Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5320918b authored by Dave Airlie's avatar Dave Airlie Committed by Dave Airlie
Browse files

drm/udl: initial UDL driver (v4)



This is an initial drm/kms driver for the displaylink devices.

Supports fb_defio,
supports KMS dumb interface
supports 24bpp via conversion to 16bpp, hw can do this better.
supports hot unplug using new drm core features.

On an unplug, it disables connector polling, unplugs connectors
from sysfs, unplugs fbdev layer (using Kay's API), drops all the
USB device URBs, and call the drm core to unplug the device.

This driver is based in large parts on udlfb.c so I've licensed
it under GPLv2.

Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
parent 2c07a21d
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -165,3 +165,4 @@ source "drivers/gpu/drm/vmwgfx/Kconfig"


source "drivers/gpu/drm/gma500/Kconfig"
source "drivers/gpu/drm/gma500/Kconfig"


source "drivers/gpu/drm/udl/Kconfig"
+1 −0
Original line number Original line Diff line number Diff line
@@ -37,4 +37,5 @@ obj-$(CONFIG_DRM_VIA) +=via/
obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
obj-$(CONFIG_DRM_EXYNOS) +=exynos/
obj-$(CONFIG_DRM_EXYNOS) +=exynos/
obj-$(CONFIG_DRM_GMA500) += gma500/
obj-$(CONFIG_DRM_GMA500) += gma500/
obj-$(CONFIG_DRM_UDL) += udl/
obj-y			+= i2c/
obj-y			+= i2c/
+12 −0
Original line number Original line Diff line number Diff line
config DRM_UDL
	tristate "DisplayLink"
	depends on DRM && EXPERIMENTAL
	select USB
	select FB_SYS_FILLRECT
	select FB_SYS_COPYAREA
	select FB_SYS_IMAGEBLIT
	select FB_DEFERRED_IO
	select DRM_KMS_HELPER
	help
	  This is a KMS driver for the USB displaylink video adapters.
          Say M/Y to add support for these devices via drm/kms interfaces.
+6 −0
Original line number Original line Diff line number Diff line

ccflags-y := -Iinclude/drm

udl-y := udl_drv.o udl_modeset.o udl_connector.o udl_encoder.o udl_main.o udl_fb.o udl_transfer.o udl_gem.o

obj-$(CONFIG_DRM_UDL) := udl.o
+141 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2012 Red Hat
 * based in parts on udlfb.c:
 * Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it>
 * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com>
 * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com>
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License v2. See the file COPYING in the main directory of this archive for
 * more details.
 */

#include "drmP.h"
#include "drm_crtc.h"
#include "drm_edid.h"
#include "drm_crtc_helper.h"
#include "udl_drv.h"

/* dummy connector to just get EDID,
   all UDL appear to have a DVI-D */

static u8 *udl_get_edid(struct udl_device *udl)
{
	u8 *block;
	char rbuf[3];
	int ret, i;

	block = kmalloc(EDID_LENGTH, GFP_KERNEL);
	if (block == NULL)
		return NULL;

	for (i = 0; i < EDID_LENGTH; i++) {
		ret = usb_control_msg(udl->ddev->usbdev,
				      usb_rcvctrlpipe(udl->ddev->usbdev, 0), (0x02),
				      (0x80 | (0x02 << 5)), i << 8, 0xA1, rbuf, 2,
				      HZ);
		if (ret < 1) {
			DRM_ERROR("Read EDID byte %d failed err %x\n", i, ret);
			i--;
			goto error;
		}
		block[i] = rbuf[1];
	}

	return block;

error:
	kfree(block);
	return NULL;
}

static int udl_get_modes(struct drm_connector *connector)
{
	struct udl_device *udl = connector->dev->dev_private;
	struct edid *edid;
	int ret;

	edid = (struct edid *)udl_get_edid(udl);

	connector->display_info.raw_edid = (char *)edid;

	drm_mode_connector_update_edid_property(connector, edid);
	ret = drm_add_edid_modes(connector, edid);
	connector->display_info.raw_edid = NULL;
	kfree(edid);
	return ret;
}

static int udl_mode_valid(struct drm_connector *connector,
			  struct drm_display_mode *mode)
{
	return 0;
}

static enum drm_connector_status
udl_detect(struct drm_connector *connector, bool force)
{
	if (drm_device_is_unplugged(connector->dev))
		return connector_status_disconnected;
	return connector_status_connected;
}

struct drm_encoder *udl_best_single_encoder(struct drm_connector *connector)
{
	int enc_id = connector->encoder_ids[0];
	struct drm_mode_object *obj;
	struct drm_encoder *encoder;

	obj = drm_mode_object_find(connector->dev, enc_id, DRM_MODE_OBJECT_ENCODER);
	if (!obj)
		return NULL;
	encoder = obj_to_encoder(obj);
	return encoder;
}

int udl_connector_set_property(struct drm_connector *connector, struct drm_property *property,
			       uint64_t val)
{
	return 0;
}

static void udl_connector_destroy(struct drm_connector *connector)
{
	drm_sysfs_connector_remove(connector);
	drm_connector_cleanup(connector);
	kfree(connector);
}

struct drm_connector_helper_funcs udl_connector_helper_funcs = {
	.get_modes = udl_get_modes,
	.mode_valid = udl_mode_valid,
	.best_encoder = udl_best_single_encoder,
};

struct drm_connector_funcs udl_connector_funcs = {
	.dpms = drm_helper_connector_dpms,
	.detect = udl_detect,
	.fill_modes = drm_helper_probe_single_connector_modes,
	.destroy = udl_connector_destroy,
	.set_property = udl_connector_set_property,
};

int udl_connector_init(struct drm_device *dev, struct drm_encoder *encoder)
{
	struct drm_connector *connector;

	connector = kzalloc(sizeof(struct drm_connector), GFP_KERNEL);
	if (!connector)
		return -ENOMEM;

	drm_connector_init(dev, connector, &udl_connector_funcs, DRM_MODE_CONNECTOR_DVII);
	drm_connector_helper_add(connector, &udl_connector_helper_funcs);

	drm_sysfs_connector_add(connector);
	drm_mode_connector_attach_encoder(connector, encoder);

	drm_connector_attach_property(connector,
				      dev->mode_config.dirty_info_property,
				      1);
	return 0;
}
Loading