Skip to content

Commit

Permalink
drm/udl: initial UDL driver (v4)
Browse files Browse the repository at this point in the history
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: Dave Airlie <airlied@redhat.com>
  • Loading branch information
airlied committed Mar 15, 2012
1 parent 2c07a21 commit 5320918
Show file tree
Hide file tree
Showing 13 changed files with 2,324 additions and 0 deletions.
1 change: 1 addition & 0 deletions drivers/gpu/drm/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -165,3 +165,4 @@ source "drivers/gpu/drm/vmwgfx/Kconfig"

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

source "drivers/gpu/drm/udl/Kconfig"
1 change: 1 addition & 0 deletions drivers/gpu/drm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,5 @@ obj-$(CONFIG_DRM_VIA) +=via/
obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
obj-$(CONFIG_DRM_EXYNOS) +=exynos/
obj-$(CONFIG_DRM_GMA500) += gma500/
obj-$(CONFIG_DRM_UDL) += udl/
obj-y += i2c/
12 changes: 12 additions & 0 deletions drivers/gpu/drm/udl/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
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 changes: 6 additions & 0 deletions drivers/gpu/drm/udl/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

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 changes: 141 additions & 0 deletions drivers/gpu/drm/udl/udl_connector.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/*
* 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;
}
99 changes: 99 additions & 0 deletions drivers/gpu/drm/udl/udl_drv.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* Copyright (C) 2012 Red Hat
*
* 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 <linux/module.h>
#include "drm_usb.h"
#include "drm_crtc_helper.h"
#include "udl_drv.h"

static struct drm_driver driver;

static struct usb_device_id id_table[] = {
{.idVendor = 0x17e9, .match_flags = USB_DEVICE_ID_MATCH_VENDOR,},
{},
};
MODULE_DEVICE_TABLE(usb, id_table);

MODULE_LICENSE("GPL");

static int udl_usb_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
return drm_get_usb_dev(interface, id, &driver);
}

static void udl_usb_disconnect(struct usb_interface *interface)
{
struct drm_device *dev = usb_get_intfdata(interface);

drm_kms_helper_poll_disable(dev);
drm_connector_unplug_all(dev);
udl_fbdev_unplug(dev);
udl_drop_usb(dev);
drm_unplug_dev(dev);
}

static struct vm_operations_struct udl_gem_vm_ops = {
.fault = udl_gem_fault,
.open = drm_gem_vm_open,
.close = drm_gem_vm_close,
};

static const struct file_operations udl_driver_fops = {
.owner = THIS_MODULE,
.open = drm_open,
.mmap = drm_gem_mmap,
.poll = drm_poll,
.read = drm_read,
.unlocked_ioctl = drm_ioctl,
.release = drm_release,
.fasync = drm_fasync,
.llseek = noop_llseek,
};

static struct drm_driver driver = {
.driver_features = DRIVER_MODESET | DRIVER_GEM,
.load = udl_driver_load,
.unload = udl_driver_unload,

/* gem hooks */
.gem_init_object = udl_gem_init_object,
.gem_free_object = udl_gem_free_object,
.gem_vm_ops = &udl_gem_vm_ops,

.dumb_create = udl_dumb_create,
.dumb_map_offset = udl_gem_mmap,
.dumb_destroy = udl_dumb_destroy,
.fops = &udl_driver_fops,
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
.date = DRIVER_DATE,
.major = DRIVER_MAJOR,
.minor = DRIVER_MINOR,
.patchlevel = DRIVER_PATCHLEVEL,
};

static struct usb_driver udl_driver = {
.name = "udl",
.probe = udl_usb_probe,
.disconnect = udl_usb_disconnect,
.id_table = id_table,
};

static int __init udl_init(void)
{
return drm_usb_init(&driver, &udl_driver);
}

static void __exit udl_exit(void)
{
drm_usb_exit(&driver, &udl_driver);
}

module_init(udl_init);
module_exit(udl_exit);
Loading

0 comments on commit 5320918

Please sign in to comment.