[Libdlo] [PATCH 2.6.32-rc7 1/1] udlfb: add dynamic modeset support
Bernie Thompson
bernie at plugable.com
Sun Nov 15 19:48:01 PST 2009
This is the udlfb 0.4.0 full kernel patch,
with the equivalent functionality to the standalone release.
There are some additional changes vs 0.4.0 to conform to kernel style (checkpatch.pl),
and the copy of drm_edid.h is not included, as this targets only the latest kernel.
Looked at separating out the ref counting/synchronization into its own patch, but it's
been tested for several months in this form, and is due for other rework soon (which
of course would then be done as its own patch from the start).
The patch below is also available at http://git.plugable.com/dynamicmode.patch
Feedback welcome if there's anything amiss.
--
Add dynamic modeset support
udlfb uses EDID to find the monitor’s preferred mode
udlfb no longer has fixed mode tables – it’s able to set any mode
dynamically, from the standard VESA timing characteristics of the monitor.
Probe and disconnect also now have better synchronization.
Code ported from displaylink-mod 0.3 branch of Roberto De Ioris, with
changes to minimize diffs and clean for checkpatch.pl style
Signed-off-by: Bernie Thompson <bernie at plugable.com>
---
udlfb.c | 818 +++++++++++++++++++++++++++++++++++++++++++++++-----------------
udlfb.h | 238 ++++--------------
2 files changed, 662 insertions(+), 394 deletions(-)
--
diff -uprN -X linux-2.6.32-rc7/Documentation/dontdiff linux-2.6.32-rc7/drivers/staging/udlfb/udlfb.c linux-2.6/drivers/staging/udlfb/udlfb.c
--- linux-2.6.32-rc7/drivers/staging/udlfb/udlfb.c 2009-11-12 16:46:07.000000000 -0800
+++ linux-2.6/drivers/staging/udlfb/udlfb.c 2009-11-15 12:02:36.000000000 -0800
@@ -1,12 +1,14 @@
/*****************************************************************************
* DLFB Kernel Driver *
- * Version 0.2 (udlfb) *
+ * Version 0.4 (udlfb) *
* (C) 2009 Roberto De Ioris <roberto at unbit.it> *
* *
* This file is licensed under the GPLv2. See COPYING in the package. *
* Based on the amazing work of Florian Echtler and libdlo 0.1 *
* *
* *
+ * 11.11.09 release 0.4 merge 0.3 work back into udlfb for kernel tree *
+ * 24.06.09 release 0.3 as displylink-mod (resolution manager, new ioctls) *
* 10.06.09 release 0.2.3 (edid ioctl, fallback for unsupported modes) *
* 05.06.09 release 0.2.2 (real screen blanking, rle compression, double buffer) *
* 31.05.09 release 0.2 *
@@ -22,10 +24,19 @@
#include <linux/fb.h>
#include <linux/mutex.h>
#include <linux/vmalloc.h>
+#include <linux/version.h>
+
+/* Many users compile this as a loadable module on older kernels.Keep for now */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
+#include <drm/drm_edid.h>
+#else
+#include "drm_edid.h"
+#endif
+
#include "udlfb.h"
-#define DRIVER_VERSION "DLFB 0.2"
+#define DRIVER_VERSION "DLFB 0.4"
/* memory functions taken from vfb */
@@ -99,6 +110,12 @@ static int dlfb_mmap(struct fb_info *inf
}
/* ioctl structure */
+
+struct dlores {
+ int w, h;
+ int freq;
+};
+
struct dloarea {
int x, y;
int w, h;
@@ -164,6 +181,9 @@ image_blit(struct dlfb_data *dev_info, i
char *bufptr;
+ if (dev_info->udev == NULL)
+ return 0;
+
if (x + width > dev_info->info->var.xres)
return -EINVAL;
@@ -276,7 +296,7 @@ image_blit(struct dlfb_data *dev_info, i
}
if (bufptr > dev_info->buf) {
- ret = dlfb_bulk_msg(dev_info, bufptr - dev_info->buf);
+ dlfb_bulk_msg(dev_info, bufptr - dev_info->buf);
}
mutex_unlock(&dev_info->bulk_mutex);
@@ -371,111 +391,6 @@ draw_rect(struct dlfb_data *dev_info, in
return 1;
}
-static void swapfb(struct dlfb_data *dev_info)
-{
-
- int tmpbase;
- char *bufptr;
-
- mutex_lock(&dev_info->bulk_mutex);
-
- tmpbase = dev_info->base16;
-
- dev_info->base16 = dev_info->base16d;
- dev_info->base16d = tmpbase;
-
- bufptr = dev_info->buf;
-
- bufptr = dlfb_set_register(bufptr, 0xFF, 0x00);
-
- // set addresses
- bufptr =
- dlfb_set_register(bufptr, 0x20, (char)(dev_info->base16 >> 16));
- bufptr = dlfb_set_register(bufptr, 0x21, (char)(dev_info->base16 >> 8));
- bufptr = dlfb_set_register(bufptr, 0x22, (char)(dev_info->base16));
-
- bufptr = dlfb_set_register(bufptr, 0xFF, 0x00);
-
- dlfb_bulk_msg(dev_info, bufptr - dev_info->buf);
-
- mutex_unlock(&dev_info->bulk_mutex);
-}
-
-static int copyfb(struct dlfb_data *dev_info)
-{
- int base;
- int source;
- int rem;
- int i, ret;
-
- char *bufptr;
-
- base = dev_info->base16d;
-
- mutex_lock(&dev_info->bulk_mutex);
-
- source = dev_info->base16;
-
- bufptr = dev_info->buf;
-
- for (i = 0; i < dev_info->info->var.yres; i++) {
-
- if (dev_info->bufend - bufptr < BUF_HIGH_WATER_MARK) {
- ret = dlfb_bulk_msg(dev_info, bufptr - dev_info->buf);
- bufptr = dev_info->buf;
- }
-
- rem = dev_info->info->var.xres;
-
- while (rem) {
-
- if (dev_info->bufend - bufptr < BUF_HIGH_WATER_MARK) {
- ret =
- dlfb_bulk_msg(dev_info,
- bufptr - dev_info->buf);
- bufptr = dev_info->buf;
-
- }
-
- *bufptr++ = 0xAF;
- *bufptr++ = 0x6A;
-
- *bufptr++ = (char)(base >> 16);
- *bufptr++ = (char)(base >> 8);
- *bufptr++ = (char)(base);
-
- if (rem > 255) {
- *bufptr++ = 255;
- *bufptr++ = (char)(source >> 16);
- *bufptr++ = (char)(source >> 8);
- *bufptr++ = (char)(source);
-
- rem -= 255;
- base += 255 * 2;
- source += 255 * 2;
-
- } else {
- *bufptr++ = rem;
- *bufptr++ = (char)(source >> 16);
- *bufptr++ = (char)(source >> 8);
- *bufptr++ = (char)(source);
-
- base += rem * 2;
- source += rem * 2;
- rem = 0;
- }
- }
- }
-
- if (bufptr > dev_info->buf)
- ret = dlfb_bulk_msg(dev_info, bufptr - dev_info->buf);
-
- mutex_unlock(&dev_info->bulk_mutex);
-
- return 1;
-
-}
-
static int
copyarea(struct dlfb_data *dev_info, int dx, int dy, int sx, int sy,
int width, int height)
@@ -568,23 +483,42 @@ static void dlfb_copyarea(struct fb_info
struct dlfb_data *dev = info->par;
+ mutex_lock(&dev->fb_mutex);
+
+ dev = info->par;
+
+ if (dev->udev == NULL)
+ return;
+
copyarea(dev, area->dx, area->dy, area->sx, area->sy, area->width,
area->height);
+ mutex_unlock(&dev->fb_mutex);
+
/* printk("COPY AREA %d %d %d %d %d %d !!!\n", area->dx, area->dy, area->sx, area->sy, area->width, area->height); */
}
static void dlfb_imageblit(struct fb_info *info, const struct fb_image *image)
{
-
- int ret;
struct dlfb_data *dev = info->par;
+
+ mutex_lock(&dev->fb_mutex);
+
+ dev = info->par;
+
+ if (dev->udev == NULL)
+ return;
+
/* printk("IMAGE BLIT (1) %d %d %d %d DEPTH %d {%p}!!!\n", image->dx, image->dy, image->width, image->height, image->depth, dev->udev); */
+
cfb_imageblit(info, image);
- ret =
- image_blit(dev, image->dx, image->dy, image->width, image->height,
+
+ image_blit(dev, image->dx, image->dy, image->width, image->height,
info->screen_base);
+
+ mutex_unlock(&dev->fb_mutex);
+
/* printk("IMAGE BLIT (2) %d %d %d %d DEPTH %d {%p} %d!!!\n", image->dx, image->dy, image->width, image->height, image->depth, dev->udev, ret); */
}
@@ -595,11 +529,21 @@ static void dlfb_fillrect(struct fb_info
unsigned char red, green, blue;
struct dlfb_data *dev = info->par;
+ mutex_lock(&dev->fb_mutex);
+
+ dev = info->par;
+
+ if (dev->udev == NULL)
+ return;
+
memcpy(&red, ®ion->color, 1);
memcpy(&green, ®ion->color + 1, 1);
memcpy(&blue, ®ion->color + 2, 1);
draw_rect(dev, region->dx, region->dy, region->width, region->height,
red, green, blue);
+
+ mutex_unlock(&dev->fb_mutex);
+
/* printk("FILL RECT %d %d !!!\n", region->dx, region->dy); */
}
@@ -609,6 +553,12 @@ static int dlfb_ioctl(struct fb_info *in
struct dlfb_data *dev_info = info->par;
struct dloarea *area = NULL;
+ struct dlores *res = NULL;
+ char *name;
+
+ if (dev_info->udev == NULL) {
+ return -EINVAL;
+ }
if (cmd == 0xAD) {
char *edid = (char *)arg;
@@ -639,12 +589,6 @@ static int dlfb_ioctl(struct fb_info *in
if (cmd == 0xAA) {
image_blit(dev_info, area->x, area->y, area->w, area->h,
info->screen_base);
- }
- if (cmd == 0xAC) {
- copyfb(dev_info);
- image_blit(dev_info, area->x, area->y, area->w, area->h,
- info->screen_base);
- swapfb(dev_info);
} else if (cmd == 0xAB) {
if (area->x2 < 0)
@@ -656,6 +600,19 @@ static int dlfb_ioctl(struct fb_info *in
copyarea(dev_info,
area->x2, area->y2, area->x, area->y, area->w,
area->h);
+ } else if (cmd == 0xAE) {
+ res = (struct dlores *) arg;
+ dlfb_set_video_mode(dev_info, 0, res->w, res->h, res->freq);
+
+ } else if (cmd == 0xAF) {
+ name = (char *) arg;
+ if (copy_to_user(name, dev_info->name, 64))
+ return -EFAULT;
+ return 0;
+ } else if (cmd == 0xB0) {
+ name = (char *) arg;
+ if (copy_to_user(name, "displaylink", 11))
+ return -EFAULT;
}
return 0;
}
@@ -691,8 +648,30 @@ dlfb_setcolreg(unsigned regno, unsigned
static int dlfb_release(struct fb_info *info, int user)
{
struct dlfb_data *dev_info = info->par;
+
+ BUG_ON(dev_info == NULL);
+
+ /* fbcon control */
+ if (user == 0)
+ return 0;
+
+ mutex_lock(&dev_info->fb_mutex);
+
+ atomic_dec(&dev_info->fb_count);
+
+
+ if (atomic_read(&dev_info->fb_count) == 0 && dev_info->udev == NULL) {
+ dlfb_destroy_framebuffer(dev_info);
+ mutex_unlock(&dev_info->fb_mutex);
+ kfree(dev_info);
+ return 0 ;
+ }
+
image_blit(dev_info, 0, 0, info->var.xres, info->var.yres,
info->screen_base);
+
+ mutex_unlock(&dev_info->fb_mutex);
+
return 0;
}
@@ -714,6 +693,111 @@ static int dlfb_blank(int blank_mode, st
return 0;
}
+
+static int dlfb_open(struct fb_info *info, int user)
+{
+
+ struct dlfb_data *dev = info->par;
+
+ BUG_ON(dev == NULL);
+
+ /* fbcon can survive disconnection, no refcount needed */
+ if (user == 0)
+ return 0;
+
+ mutex_lock(&dev->fb_mutex);
+
+ printk(KERN_INFO "application %s %d is opening displaylink device\n",
+ current->comm, user);
+
+ if (dev->udev == NULL) {
+ mutex_unlock(&dev->fb_mutex);
+ return -1;
+ }
+
+ atomic_inc(&dev->fb_count);
+
+ mutex_unlock(&dev->fb_mutex);
+
+ return 0;
+
+}
+
+
+static int dlfb_setpar(struct fb_info *info)
+{
+ struct dlfb_data *dev = info->par;
+
+ BUG_ON(dev == NULL);
+
+ if (dev->udev == NULL)
+ return -EINVAL;
+
+ printk(KERN_INFO "setting hardware to %d %d\n",
+ info->var.xres, info->var.yres);
+
+ dlfb_set_video_mode(dev, 0, info->var.xres, info->var.yres, 0);
+
+ info->fix.line_length = dev->line_length;
+
+ return 0;
+}
+
+
+
+static int dlfb_checkvar(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ struct dlfb_data *dev = info->par;
+ struct edid *edid ;
+ struct detailed_timing *best_edid ;
+ struct std_timing *std_edid ;
+
+ int i ;
+
+ BUG_ON(dev == NULL);
+
+ if (dev->udev == NULL)
+ return -EINVAL;
+
+ edid = (struct edid *) dev->edid ;
+
+ printk(KERN_INFO "checking for resolution %d %d\n",
+ var->xres, var->yres);
+
+ for (i = 0; i < 4; i++) {
+ best_edid = (struct detailed_timing *)
+ &edid->detailed_timings[i];
+ if (EDID_GET_WIDTH(best_edid) == 0)
+ break;
+ printk(KERN_INFO "edid %dX%d\n", EDID_GET_WIDTH(best_edid),
+ EDID_GET_HEIGHT(best_edid));
+ if (EDID_GET_WIDTH(best_edid) == var->xres &&
+ EDID_GET_HEIGHT(best_edid) == var->yres) {
+ printk(KERN_INFO
+ "Found valid resolution\n");
+ return 0;
+ }
+ }
+
+ for (i = 0; i < 8; i++) {
+ std_edid = (struct std_timing *) &edid->standard_timings[i];
+ if ((std_edid->hsize*8) + 248 < 320)
+ break;
+ printk(KERN_INFO "edid (std) %d %d %d %d\n",
+ (std_edid->hsize*8)+248,
+ (((std_edid->hsize*8)+248)/4)*3,
+ std_edid->vfreq+60,
+ std_edid->aspect_ratio);
+ if ((std_edid->hsize*8)+248 == var->xres &&
+ (((std_edid->hsize*8)+248)/4)*3 == var->yres) {
+ printk(KERN_INFO "Found valid resolution\n");
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
static struct fb_ops dlfb_ops = {
.fb_setcolreg = dlfb_setcolreg,
.fb_fillrect = dlfb_fillrect,
@@ -723,103 +807,407 @@ static struct fb_ops dlfb_ops = {
.fb_ioctl = dlfb_ioctl,
.fb_release = dlfb_release,
.fb_blank = dlfb_blank,
+ .fb_open = dlfb_open,
+ .fb_check_var = dlfb_checkvar,
+ .fb_set_par = dlfb_setpar,
};
static int
dlfb_probe(struct usb_interface *interface, const struct usb_device_id *id)
{
- struct dlfb_data *dev_info;
- struct fb_info *info;
+ struct dlfb_data *dev;
int ret;
- char rbuf[4];
+ int mode = 0;
- dev_info = kzalloc(sizeof(*dev_info), GFP_KERNEL);
- if (dev_info == NULL) {
- printk("cannot allocate dev_info structure.\n");
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (dev == NULL) {
+ printk(KERN_ERR "cannot allocate dlfb_data structure.\n");
return -ENOMEM;
}
- mutex_init(&dev_info->bulk_mutex);
+ mutex_init(&dev->bulk_mutex);
+ mutex_init(&dev->fb_mutex);
- dev_info->udev = usb_get_dev(interface_to_usbdev(interface));
- dev_info->interface = interface;
+ dev->udev = usb_get_dev(interface_to_usbdev(interface));
+ dev->interface = interface;
- printk("DisplayLink device attached\n");
+ printk(KERN_INFO "DisplayLink device attached\n");
/* add framebuffer info to usb interface */
- usb_set_intfdata(interface, dev_info);
+ usb_set_intfdata(interface, dev);
- dev_info->buf = kmalloc(BUF_SIZE, GFP_KERNEL);
- /* usb_buffer_alloc(dev_info->udev, BUF_SIZE , GFP_KERNEL, &dev_info->tx_urb->transfer_dma); */
+ dev->buf = kmalloc(BUF_SIZE, GFP_KERNEL);
- if (dev_info->buf == NULL) {
+ if (dev->buf == NULL) {
printk("unable to allocate memory for dlfb commands\n");
goto out;
}
- dev_info->bufend = dev_info->buf + BUF_SIZE;
+ dev->bufend = dev->buf + BUF_SIZE;
- dev_info->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
- usb_fill_bulk_urb(dev_info->tx_urb, dev_info->udev,
- usb_sndbulkpipe(dev_info->udev, 1), dev_info->buf, 0,
- dlfb_bulk_callback, dev_info);
-
- ret =
- usb_control_msg(dev_info->udev, usb_rcvctrlpipe(dev_info->udev, 0),
- (0x06), (0x80 | (0x02 << 5)), 0, 0, rbuf, 4, 0);
- printk("ret control msg 0: %d %x%x%x%x\n", ret, rbuf[0], rbuf[1],
- rbuf[2], rbuf[3]);
+ dev->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
+ usb_fill_bulk_urb(dev->tx_urb, dev->udev,
+ usb_sndbulkpipe(dev->udev, 1), dev->buf, 0,
+ dlfb_bulk_callback, dev);
- dlfb_edid(dev_info);
+ if (strlen(dev->udev->product) > 63)
+ memcpy(dev->name, dev->udev->product, 63);
+ else
+ memcpy(dev->name, dev->udev->product,
+ strlen(dev->udev->product));
- info = framebuffer_alloc(sizeof(u32) * 256, &dev_info->udev->dev);
+ dlfb_edid(dev);
+ ret = dlfb_setup(dev);
- if (!info) {
- printk("non posso allocare il framebuffer displaylink");
+
+ dlfb_set_video_mode(dev, 0, 0, 0, 0);
+
+ dev->backing_buffer = kzalloc(dev->screen_size, GFP_KERNEL);
+
+ if (!dev->backing_buffer) {
+ printk(KERN_ERR "non posso allocare il backing buffer\n");
goto out;
}
- fb_parse_edid(dev_info->edid, &info->var);
+ ret = dlfb_activate_framebuffer(dev, mode);
- printk("EDID XRES %d YRES %d\n", info->var.xres, info->var.yres);
+ if (ret != 0) {
+ printk(KERN_ERR "unable to allocate framebuffer\n");
+ goto out;
+ }
- if (dlfb_set_video_mode(dev_info, info->var.xres, info->var.yres) != 0) {
- info->var.xres = 1280;
- info->var.yres = 1024;
- if (dlfb_set_video_mode
- (dev_info, info->var.xres, info->var.yres) != 0) {
- goto out;
- }
+ /* put the green screen */
+ draw_rect(dev, 0, 0, dev->info->var.xres,
+ dev->info->var.yres, 0x30, 0xff, 0x30);
+
+ return 0;
+
+ out:
+ usb_set_intfdata(interface, NULL);
+ usb_put_dev(dev->udev);
+ kfree(dev);
+ return -ENOMEM;
+
+}
+
+static uint16_t lfsr16(uint16_t v)
+{
+ uint32_t _v = 0xFFFF;
+
+ v = cpu_to_le16(v);
+
+ while (v--) {
+ _v = ((_v << 1) |
+ (((_v >> 15) ^ (_v >> 4) ^ (_v >> 2) ^ (_v >> 1))
+ & 1)) & 0xFFFF;
}
+ return (uint16_t) _v;
+}
- printk("found valid mode...%d\n", info->var.pixclock);
- info->pseudo_palette = info->par;
- info->par = dev_info;
- dev_info->info = info;
+/* displaylink functions */
+void dlfb_bulk_callback(struct urb *urb)
+{
+
+ struct dlfb_data *dev = urb->context;
+ complete(&dev->done);
+
+}
+
+void dlfb_edid(struct dlfb_data *dev)
+{
+ int i;
+ int ret;
+ char rbuf[2];
+
+ for (i = 0; i < 128; i++) {
+ ret = usb_control_msg(dev->udev,
+ usb_rcvctrlpipe(dev->udev, 0), (0x02),
+ (0x80 | (0x02 << 5)), i << 8, 0xA1, rbuf, 2, 0);
+ dev->edid[i] = rbuf[1];
+ }
+}
+
+void dlfb_get_best_edid(struct dlfb_data *dev)
+{
+ return;
+}
+
+
+int dlfb_bulk_msg(struct dlfb_data *dev, int len)
+{
+
+ int ret;
+
+ init_completion(&dev->done);
+
+ dev->tx_urb->actual_length = 0;
+ dev->tx_urb->transfer_buffer_length = len;
+
+ ret = usb_submit_urb(dev->tx_urb, GFP_KERNEL);
+ if (!wait_for_completion_timeout(&dev->done, 1000)) {
+ usb_kill_urb(dev->tx_urb);
+ printk(KERN_WARNING "usb timeout !!!\n");
+ }
+
+ return dev->tx_urb->actual_length;
+
+}
+
+static char *dlfb_set_register_16(char *bufptr, uint8_t reg, uint16_t val)
+{
+
+ bufptr = dlfb_set_register(bufptr, reg, val >> 8);
+ bufptr = dlfb_set_register(bufptr, reg+1, val & 0xFF);
+
+ return bufptr;
+}
+
+static char *dlfb_set_register_le16(char *bufptr, uint8_t reg, uint16_t val)
+{
+
+ bufptr = dlfb_set_register(bufptr, reg, val & 0xFF);
+ bufptr = dlfb_set_register(bufptr, reg+1, val >> 8);
+
+ return bufptr;
+}
+
+char *dlfb_edid_to_reg(struct detailed_timing *edid, char *bufptr,
+ int width, int height, int freq)
+{
+ uint16_t edid_w ;
+ uint16_t edid_h ;
+ uint16_t edid_x_ds ;
+ uint16_t edid_x_de ;
+ uint16_t edid_y_ds ;
+ uint16_t edid_y_de ;
+ uint16_t edid_x_ec ;
+ uint16_t edid_h_se ;
+ uint16_t edid_y_ec ;
+ uint16_t edid_v_se ;
+ uint16_t edid_pclock ;
+
+ edid_w = EDID_GET_WIDTH(edid) ;
+ if (width != 0)
+ edid_w = width;
+
+ edid_h = EDID_GET_HEIGHT(edid) ;
+ if (height != 0)
+ edid_h = height;
+
+ /* display x start/end */
+ edid_x_ds = (EDID_GET_HBLANK(edid) - EDID_GET_HSYNC(edid)) ;
+ edid_x_de = (edid_x_ds + edid_w) ;
+
+ /* display y start/end */
+ edid_y_ds = (EDID_GET_VBLANK(edid) - EDID_GET_VSYNC(edid));
+ edid_y_de = (edid_y_ds + edid_h);
+
+ /* x end count */
+ edid_x_ec = (edid_w + EDID_GET_HBLANK(edid) - 1);
+ edid_h_se = (EDID_GET_HPULSE(edid) + 1) ;
+
+ /* y end count */
+ edid_y_ec = (edid_h + EDID_GET_VBLANK(edid)) ;
+ edid_v_se = (EDID_GET_VPULSE(edid)) ;
+
+ /* pixel clock */
+ edid_pclock = edid->pixel_clock*2;
+
+ bufptr = dlfb_set_register_16(bufptr, 0x01, lfsr16(edid_x_ds)) ;
+ bufptr = dlfb_set_register_16(bufptr, 0x03, lfsr16(edid_x_de)) ;
+ bufptr = dlfb_set_register_16(bufptr, 0x05, lfsr16(edid_y_ds)) ;
+ bufptr = dlfb_set_register_16(bufptr, 0x07, lfsr16(edid_y_de)) ;
+ bufptr = dlfb_set_register_16(bufptr, 0x09, lfsr16(edid_x_ec)) ;
+ bufptr = dlfb_set_register_16(bufptr, 0x0B, lfsr16(1)) ;
+ bufptr = dlfb_set_register_16(bufptr, 0x0D, lfsr16(edid_h_se)) ;
+ bufptr = dlfb_set_register_16(bufptr, 0x0F, edid_w) ;
+ bufptr = dlfb_set_register_16(bufptr, 0x11, lfsr16(edid_y_ec)) ;
+ bufptr = dlfb_set_register_16(bufptr, 0x13, lfsr16(0)) ;
+ bufptr = dlfb_set_register_16(bufptr, 0x15, lfsr16(edid_v_se)) ;
+ bufptr = dlfb_set_register_16(bufptr, 0x17, edid_h) ;
+ bufptr = dlfb_set_register_le16(bufptr, 0x1B, edid_pclock) ;
+
+ return bufptr;
+}
+
+char *dlfb_set_register(char *bufptr, uint8_t reg, uint8_t val)
+{
+
+ *bufptr++ = 0xAF;
+ *bufptr++ = 0x20;
+ *bufptr++ = reg;
+ *bufptr++ = val;
+
+ return bufptr;
+
+}
+
+int dlfb_set_video_mode(struct dlfb_data *dev, int mode,
+ int width, int height, int freq)
+{
+
+ char *bufptr;
+ int ret;
+
+ struct edid *edid = (struct edid *) dev->edid;
+ struct detailed_timing *best_edid = &edid->detailed_timings[mode];
+
+ if (dev->udev == NULL)
+ return 0;
+
+ dev->base16 = 0;
+ dev->base8 = dev->screen_size;
+
+ bufptr = dev->buf;
+
+ mutex_lock(&dev->bulk_mutex);
+
+ /* set registers */
+ bufptr = dlfb_set_register(bufptr, 0xFF, 0x00);
+
+ /* set color depth */
+ bufptr = dlfb_set_register(bufptr, 0x00, 0x00);
+
+ /* set addresses */
+ bufptr =
+ dlfb_set_register(bufptr, 0x20,
+ (char)(dev->base16 >> 16));
+ bufptr =
+ dlfb_set_register(bufptr, 0x21,
+ (char)(dev->base16 >> 8));
+ bufptr =
+ dlfb_set_register(bufptr, 0x22,
+ (char)(dev->base16));
+
+ bufptr =
+ dlfb_set_register(bufptr, 0x26,
+ (char)(dev->base8 >> 16));
+
+ bufptr =
+ dlfb_set_register(bufptr, 0x27,
+ (char)(dev->base8 >> 8));
+
+ bufptr =
+ dlfb_set_register(bufptr, 0x28,
+ (char)(dev->base8));
+
+ if (width != 0)
+ printk(KERN_INFO "displaylink setting resolution to %dx%d\n",
+ width, height);
+
+ bufptr = dlfb_edid_to_reg(best_edid, bufptr, width, height, freq);
+
+ /* blank */
+ bufptr = dlfb_set_register(bufptr, 0x1F, 0x00);
+
+ /* end registers */
+ bufptr = dlfb_set_register(bufptr, 0xFF, 0xFF);
+
+ ret = dlfb_bulk_msg(dev, bufptr - dev->buf);
+ printk(KERN_INFO "set video mode bulk message: %d %d\n", ret,
+ bufptr - dev->buf);
+
+ ret = dlfb_bulk_msg(dev, 0);
+ printk(KERN_INFO "displaylink register flush: %d\n", ret);
+
+ if (width == 0)
+ dev->line_length = EDID_GET_WIDTH(best_edid) * (FB_BPP/8) ;
+ else
+ dev->line_length = width * (FB_BPP/8) ;
+
+ mutex_unlock(&dev->bulk_mutex);
+
+ return 0;
+
+}
+
+int dlfb_setup(struct dlfb_data *dev)
+{
+
+ int ret;
+ unsigned char buf[4];
+
+ struct edid *edid = (struct edid *) dev->edid;
+
+ struct detailed_timing *best_edid = &edid->detailed_timings[0] ;
+
+ ret = usb_control_msg(dev->udev,
+ usb_rcvctrlpipe(dev->udev, 0), (0x02),
+ (0x80 | (0x02 << 5)), 0, 0, buf, 4, 5000);
+
+ if (ret != 4)
+ return -1 ;
+
+ switch ((buf[3] >> 4) & 0xF) {
+ case DL_CHIP_TYPE_BASE:
+ strcpy(dev->chiptype, "base");
+ break;
+ case DL_CHIP_TYPE_ALEX:
+ strcpy(dev->chiptype, "alex");
+ break;
+ case DL_CHIP_TYPE_OLLIE:
+ strcpy(dev->chiptype, "ollie");
+ break;
+ default:
+ if (buf[3] == DL_CHIP_TYPE_OLLIE)
+ strcpy(dev->chiptype, "ollie");
+ else
+ strcpy(dev->chiptype, "unknown");
+ }
+
+ printk(KERN_INFO "found DisplayLink Chip %s\n", dev->chiptype);
+
+ memcpy(dev->buf, STD_CHANNEL, 16);
+ ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
+ 0x12, (0x02 << 5), 0, 0,
+ dev->buf, 16, 0);
+
+ dev->line_length = EDID_GET_WIDTH(best_edid) * (FB_BPP / 8);
+
+ printk(KERN_INFO "displaylink monitor info: W(%d) H(%d) clock(%d)\n",
+ EDID_GET_WIDTH(best_edid),
+ EDID_GET_HEIGHT(best_edid),
+ best_edid->pixel_clock
+ );
+
+ dev->screen_size = EDID_GET_WIDTH(best_edid) *
+ EDID_GET_HEIGHT(best_edid) * (FB_BPP / 8);
+
+ return 0;
+}
+
+int dlfb_activate_framebuffer(struct dlfb_data *dev, int mode)
+{
+
+ struct fb_info *info;
+
+ info = framebuffer_alloc(sizeof(u32) * 256, &dev->udev->dev);
+
+ if (!info) {
+ printk(KERN_ERR "unable to allocate fb_info struct");
+ return -ENOMEM;
+ }
+
+ dev->info = info;
+
+ info->pseudo_palette = info->par;
+ info->par = dev;
info->flags =
FBINFO_DEFAULT | FBINFO_READS_FAST | FBINFO_HWACCEL_IMAGEBLIT |
FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT;
info->fbops = &dlfb_ops;
- info->screen_base = rvmalloc(dev_info->screen_size);
+ info->screen_base = rvmalloc(dev->screen_size);
if (info->screen_base == NULL) {
- printk
- ("cannot allocate framebuffer virtual memory of %d bytes\n",
- dev_info->screen_size);
+ printk(KERN_ERR "cannot allocate framebuffer virtual memory"\
+ " of %d bytes\n", dev->screen_size);
goto out0;
}
- printk("screen base allocated !!!\n");
-
- dev_info->backing_buffer = kzalloc(dev_info->screen_size, GFP_KERNEL);
-
- if (!dev_info->backing_buffer)
- printk("non posso allocare il backing buffer\n");
-
- /* info->var = dev_info->si; */
+ fb_parse_edid(dev->edid, &info->var);
info->var.bits_per_pixel = 16;
info->var.activate = FB_ACTIVATE_TEST;
@@ -837,70 +1225,80 @@ dlfb_probe(struct usb_interface *interfa
info->var.blue.length = 5;
info->var.blue.msb_right = 0;
- /* info->var.pixclock = (10000000 / FB_W * 1000 / FB_H)/2 ; */
-
info->fix.smem_start = (unsigned long)info->screen_base;
- info->fix.smem_len = PAGE_ALIGN(dev_info->screen_size);
- if (strlen(dev_info->udev->product) > 15) {
- memcpy(info->fix.id, dev_info->udev->product, 15);
- } else {
- memcpy(info->fix.id, dev_info->udev->product,
- strlen(dev_info->udev->product));
- }
+ info->fix.smem_len = PAGE_ALIGN(dev->screen_size);
+
+ if (strlen(dev->udev->product) > 15)
+ memcpy(info->fix.id, dev->udev->product, 15);
+ else
+ memcpy(info->fix.id, dev->udev->product,
+ strlen(dev->udev->product));
info->fix.type = FB_TYPE_PACKED_PIXELS;
info->fix.visual = FB_VISUAL_TRUECOLOR;
info->fix.accel = info->flags;
- info->fix.line_length = dev_info->line_length;
+ info->fix.line_length = dev->line_length;
if (fb_alloc_cmap(&info->cmap, 256, 0) < 0)
goto out1;
- printk("colormap allocated\n");
if (register_framebuffer(info) < 0)
goto out2;
- draw_rect(dev_info, 0, 0, dev_info->info->var.xres,
- dev_info->info->var.yres, 0x30, 0xff, 0x30);
-
return 0;
-out2:
+ out2:
fb_dealloc_cmap(&info->cmap);
-out1:
- rvfree(info->screen_base, dev_info->screen_size);
-out0:
+ out1:
+ rvfree(info->screen_base, dev->screen_size);
+ out0:
framebuffer_release(info);
-out:
- usb_set_intfdata(interface, NULL);
- usb_put_dev(dev_info->udev);
- kfree(dev_info);
- return -ENOMEM;
+
+ return -1 ;
}
+void dlfb_destroy_framebuffer(struct dlfb_data *dev)
+{
+ unregister_framebuffer(dev->info);
+ fb_dealloc_cmap(&dev->info->cmap);
+ rvfree(dev->info->screen_base, dev->screen_size);
+ framebuffer_release(dev->info);
+}
+
+
+
static void dlfb_disconnect(struct usb_interface *interface)
{
- struct dlfb_data *dev_info = usb_get_intfdata(interface);
+ struct dlfb_data *dev = usb_get_intfdata(interface);
+ struct dlfb_orphaned_dev *odev;
- mutex_unlock(&dev_info->bulk_mutex);
+ mutex_unlock(&dev->bulk_mutex);
- usb_kill_urb(dev_info->tx_urb);
- usb_free_urb(dev_info->tx_urb);
+ usb_kill_urb(dev->tx_urb);
+ usb_free_urb(dev->tx_urb);
usb_set_intfdata(interface, NULL);
- usb_put_dev(dev_info->udev);
+ usb_put_dev(dev->udev);
- if (dev_info->info) {
- unregister_framebuffer(dev_info->info);
- fb_dealloc_cmap(&dev_info->info->cmap);
- rvfree(dev_info->info->screen_base, dev_info->screen_size);
- kfree(dev_info->backing_buffer);
- framebuffer_release(dev_info->info);
+ mutex_lock(&dev->fb_mutex);
+ if (atomic_read(&dev->fb_count) == 0) {
+ dlfb_destroy_framebuffer(dev);
+ } else {
+ odev = kzalloc(sizeof(*odev), GFP_KERNEL);
+ atomic_set(&odev->fb_count, atomic_read(&dev->fb_count));
+ odev->udev = NULL;
+ mutex_init(&odev->fb_mutex);
+ odev->info = dev->info;
+ odev->info->par = odev;
+ odev->screen_size = dev->screen_size;
+ odev->line_length = dev->line_length;
+ printk(KERN_INFO "%d clients are still connected to this"\
+ " framebuffer device\n", atomic_read(&odev->fb_count));
}
- kfree(dev_info);
+ mutex_unlock(&dev->fb_mutex);
- printk("DisplayLink device disconnected\n");
+ kfree(dev);
}
static struct usb_driver dlfb_driver = {
@@ -914,14 +1312,10 @@ static int __init dlfb_init(void)
{
int res;
- dlfb_init_modes();
-
res = usb_register(&dlfb_driver);
if (res)
err("usb_register failed. Error number %d", res);
- printk("VMODES initialized\n");
-
return res;
}
diff -uprN -X linux-2.6.32-rc7/Documentation/dontdiff linux-2.6.32-rc7/drivers/staging/udlfb/udlfb.h linux-2.6/drivers/staging/udlfb/udlfb.h
--- linux-2.6.32-rc7/drivers/staging/udlfb/udlfb.h 2009-11-12 16:46:07.000000000 -0800
+++ linux-2.6/drivers/staging/udlfb/udlfb.h 2009-11-14 16:44:05.000000000 -0800
@@ -1,18 +1,53 @@
#ifndef UDLFB_H
#define UDLFB_H
-#define MAX_VMODES 4
#define FB_BPP 16
#define STD_CHANNEL "\x57\xCD\xDC\xA7\x1C\x88\x5E\x15" \
"\x60\xFE\xC6\x97\x16\x3D\x47\xF2"
+#define DL_CHIP_TYPE_BASE 0xB
+#define DL_CHIP_TYPE_ALEX 0xF
+#define DL_CHIP_TYPE_OLLIE 0xF1
+
+
+#define EDID_GET_WIDTH(edid) \
+ ((((uint16_t) edid->data.pixel_data.hactive_hi) << 8) \
+ | (uint16_t) edid->data.pixel_data.hactive_lo)
+#define EDID_GET_HEIGHT(edid) \
+ ((((uint16_t) edid->data.pixel_data.vactive_hi) << 8) \
+ | (uint16_t) edid->data.pixel_data.vactive_lo)
+#define EDID_GET_HBLANK(edid) \
+ ((((uint16_t) edid->data.pixel_data.hblank_hi) << 8) \
+ | (uint16_t) edid->data.pixel_data.hblank_lo)
+#define EDID_GET_VBLANK(edid) \
+ ((((uint16_t) edid->data.pixel_data.vblank_hi) << 8) \
+ | (uint16_t) edid->data.pixel_data.vblank_lo)
+#define EDID_GET_HSYNC(edid) \
+ ((((uint16_t) edid->data.pixel_data.hsync_offset_hi) << 8) \
+ | (uint16_t) edid->data.pixel_data.hsync_offset_lo)
+#define EDID_GET_VSYNC(edid) \
+ ((((uint16_t) edid->data.pixel_data.vsync_offset_hi) << 8) \
+ | (uint16_t) edid->data.pixel_data.vsync_offset_lo)
+#define EDID_GET_HPULSE(edid) \
+ ((((uint16_t) edid->data.pixel_data.hsync_pulse_width_hi) << 8) \
+ | (uint16_t) edid->data.pixel_data.hsync_pulse_width_lo)
+#define EDID_GET_VPULSE(edid) \
+ ((((uint16_t) edid->data.pixel_data.vsync_pulse_width_hi) << 8) \
+ | (uint16_t) edid->data.pixel_data.vsync_pulse_width_lo)
+
/* as libdlo */
#define BUF_HIGH_WATER_MARK 1024
#define BUF_SIZE (64*1024)
struct dlfb_data {
+ /* must match "orphaned" struct below */
+ atomic_t fb_count;
struct usb_device *udev;
+ struct mutex fb_mutex;
+ int screen_size;
+ int line_length;
+ /* menbers above must match "orphaned" struct below */
struct usb_interface *interface;
struct urb *tx_urb, *ctrl_urb;
struct usb_ctrlrequest dr;
@@ -22,8 +57,8 @@ struct dlfb_data {
char *backing_buffer;
struct mutex bulk_mutex;
char edid[128];
- int screen_size;
- int line_length;
+ char chiptype[8];
+ char name[64];
struct completion done;
int base16;
int base16d;
@@ -31,6 +66,15 @@ struct dlfb_data {
int base8d;
};
+struct dlfb_orphaned_dev {
+ atomic_t fb_count;
+ struct usb_device *udev;
+ struct mutex fb_mutex;
+ struct fb_info *info;
+ int screen_size;
+ int line_length;
+};
+
struct dlfb_video_mode {
uint8_t col;
uint32_t hclock;
@@ -42,184 +86,14 @@ struct dlfb_video_mode {
uint8_t unknown3[4];
} __attribute__ ((__packed__));
-static struct dlfb_video_mode dlfb_video_modes[MAX_VMODES];
-
-static void dlfb_bulk_callback(struct urb *urb)
-{
- struct dlfb_data *dev_info = urb->context;
- complete(&dev_info->done);
-}
-
-static void dlfb_edid(struct dlfb_data *dev_info)
-{
- int i;
- int ret;
- char rbuf[2];
-
- for (i = 0; i < 128; i++) {
- ret =
- usb_control_msg(dev_info->udev,
- usb_rcvctrlpipe(dev_info->udev, 0), (0x02),
- (0x80 | (0x02 << 5)), i << 8, 0xA1, rbuf, 2,
- 0);
- /*printk("ret control msg edid %d: %d [%d]\n",i, ret, rbuf[1]); */
- dev_info->edid[i] = rbuf[1];
- }
-
-}
-
-static int dlfb_bulk_msg(struct dlfb_data *dev_info, int len)
-{
- int ret;
-
- init_completion(&dev_info->done);
-
- dev_info->tx_urb->actual_length = 0;
- dev_info->tx_urb->transfer_buffer_length = len;
-
- ret = usb_submit_urb(dev_info->tx_urb, GFP_KERNEL);
- if (!wait_for_completion_timeout(&dev_info->done, 1000)) {
- usb_kill_urb(dev_info->tx_urb);
- printk("usb timeout !!!\n");
- }
-
- return dev_info->tx_urb->actual_length;
-}
-
-static void dlfb_init_modes(void)
-{
- dlfb_video_modes[0].col = 0;
- memcpy(&dlfb_video_modes[0].hclock, "\x20\x3C\x7A\xC9", 4);
- memcpy(&dlfb_video_modes[0].vclock, "\xF2\x6C\x48\xF9", 4);
- memcpy(&dlfb_video_modes[0].unknown1, "\x70\x53\xFF\xFF\x21\x27", 6);
- dlfb_video_modes[0].xres = 800;
- memcpy(&dlfb_video_modes[0].unknown2, "\x91\xF3\xFF\xFF\xFF\xF9", 6);
- dlfb_video_modes[0].yres = 480;
- memcpy(&dlfb_video_modes[0].unknown3, "\x01\x02\xC8\x19", 4);
-
- dlfb_video_modes[1].col = 0;
- memcpy(&dlfb_video_modes[1].hclock, "\x36\x18\xD5\x10", 4);
- memcpy(&dlfb_video_modes[1].vclock, "\x60\xA9\x7B\x33", 4);
- memcpy(&dlfb_video_modes[1].unknown1, "\xA1\x2B\x27\x32\xFF\xFF", 6);
- dlfb_video_modes[1].xres = 1024;
- memcpy(&dlfb_video_modes[1].unknown2, "\xD9\x9A\xFF\xCA\xFF\xFF", 6);
- dlfb_video_modes[1].yres = 768;
- memcpy(&dlfb_video_modes[1].unknown3, "\x04\x03\xC8\x32", 4);
-
- dlfb_video_modes[2].col = 0;
- memcpy(&dlfb_video_modes[2].hclock, "\x98\xF8\x0D\x57", 4);
- memcpy(&dlfb_video_modes[2].vclock, "\x2A\x55\x4D\x54", 4);
- memcpy(&dlfb_video_modes[2].unknown1, "\xCA\x0D\xFF\xFF\x94\x43", 6);
- dlfb_video_modes[2].xres = 1280;
- memcpy(&dlfb_video_modes[2].unknown2, "\x9A\xA8\xFF\xFF\xFF\xF9", 6);
- dlfb_video_modes[2].yres = 1024;
- memcpy(&dlfb_video_modes[2].unknown3, "\x04\x02\x60\x54", 4);
-
- dlfb_video_modes[3].col = 0;
- memcpy(&dlfb_video_modes[3].hclock, "\x42\x24\x38\x36", 4);
- memcpy(&dlfb_video_modes[3].vclock, "\xC1\x52\xD9\x29", 4);
- memcpy(&dlfb_video_modes[3].unknown1, "\xEA\xB8\x32\x60\xFF\xFF", 6);
- dlfb_video_modes[3].xres = 1400;
- memcpy(&dlfb_video_modes[3].unknown2, "\xC9\x4E\xFF\xFF\xFF\xF2", 6);
- dlfb_video_modes[3].yres = 1050;
- memcpy(&dlfb_video_modes[3].unknown3, "\x04\x02\x1E\x5F", 4);
-}
-
-static char *dlfb_set_register(char *bufptr, uint8_t reg, uint8_t val)
-{
- *bufptr++ = 0xAF;
- *bufptr++ = 0x20;
- *bufptr++ = reg;
- *bufptr++ = val;
-
- return bufptr;
-}
-
-static int dlfb_set_video_mode(struct dlfb_data *dev_info, int width, int height)
-{
- int i, ret;
- unsigned char j;
- char *bufptr = dev_info->buf;
- uint8_t *vdata;
-
- for (i = 0; i < MAX_VMODES; i++) {
- printk("INIT VIDEO %d %d %d\n", i, dlfb_video_modes[i].xres,
- dlfb_video_modes[i].yres);
- if (dlfb_video_modes[i].xres == width
- && dlfb_video_modes[i].yres == height) {
-
- dev_info->base16 = 0;
- dev_info->base16d = width * height * (FB_BPP / 8);
-
- //dev_info->base8 = width * height * (FB_BPP / 8);
-
- dev_info->base8 = dev_info->base16;
- dev_info->base8d = dev_info->base16d;
-
- /* set encryption key (null) */
- memcpy(dev_info->buf, STD_CHANNEL, 16);
- ret =
- usb_control_msg(dev_info->udev,
- usb_sndctrlpipe(dev_info->udev, 0),
- 0x12, (0x02 << 5), 0, 0,
- dev_info->buf, 16, 0);
- printk("ret control msg 1 (STD_CHANNEL): %d\n", ret);
-
- /* set registers */
- bufptr = dlfb_set_register(bufptr, 0xFF, 0x00);
-
- /* set color depth */
- bufptr = dlfb_set_register(bufptr, 0x00, 0x00);
-
- /* set addresses */
- bufptr =
- dlfb_set_register(bufptr, 0x20,
- (char)(dev_info->base16 >> 16));
- bufptr =
- dlfb_set_register(bufptr, 0x21,
- (char)(dev_info->base16 >> 8));
- bufptr =
- dlfb_set_register(bufptr, 0x22,
- (char)(dev_info->base16));
-
- bufptr =
- dlfb_set_register(bufptr, 0x26,
- (char)(dev_info->base8 >> 16));
- bufptr =
- dlfb_set_register(bufptr, 0x27,
- (char)(dev_info->base8 >> 8));
- bufptr =
- dlfb_set_register(bufptr, 0x28,
- (char)(dev_info->base8));
-
- /* set video mode */
- vdata = (uint8_t *)&dlfb_video_modes[i];
- for (j = 0; j < 29; j++)
- bufptr = dlfb_set_register(bufptr, j, vdata[j]);
-
- /* blank */
- bufptr = dlfb_set_register(bufptr, 0x1F, 0x00);
-
- /* end registers */
- bufptr = dlfb_set_register(bufptr, 0xFF, 0xFF);
-
- /* send */
- ret = dlfb_bulk_msg(dev_info, bufptr - dev_info->buf);
- printk("ret bulk 2: %d %td\n", ret,
- bufptr - dev_info->buf);
-
- /* flush */
- ret = dlfb_bulk_msg(dev_info, 0);
- printk("ret bulk 3: %d\n", ret);
-
- dev_info->screen_size = width * height * (FB_BPP / 8);
- dev_info->line_length = width * (FB_BPP / 8);
-
- return 0;
- }
- }
-
- return -1;
-}
+char *dlfb_set_register(char *bufptr, uint8_t reg, uint8_t val);
+int dlfb_bulk_msg(struct dlfb_data *dev, int len);
+void dlfb_destroy_framebuffer(struct dlfb_data *dev);
+void dlfb_edid(struct dlfb_data *dev);
+int dlfb_set_video_mode(struct dlfb_data *dev, int mode, int width,
+ int height, int freq);
+void dlfb_bulk_callback(struct urb *urb);
+int dlfb_setup(struct dlfb_data *dev);
+int dlfb_activate_framebuffer(struct dlfb_data *dev, int mode);
#endif
More information about the Libdlo
mailing list