From: Jaya Kumar I had used skeletonfb.c to start with and that had fb_get_options. I've removed that and the redundant param retrieval from arcfb as per akpm's mention of the redundancy of fb_get_options/*_setup. I also cleaned up some other duplicate code. I've listed the driver as maintained and added myself. Signed-off-by: Andrew Morton --- CREDITS | 7 + MAINTAINERS | 5 drivers/video/arcfb.c | 287 +++++++++++++++++++++++++++++--------------------- include/linux/arcfb.h | 8 + 4 files changed, 192 insertions(+), 115 deletions(-) diff -puN CREDITS~framebuffer-driver-for-arc-lcd-board-update CREDITS --- 25/CREDITS~framebuffer-driver-for-arc-lcd-board-update Mon Jun 6 14:41:00 2005 +++ 25-akpm/CREDITS Mon Jun 6 14:41:00 2005 @@ -1880,6 +1880,13 @@ S: Schlehenweg 9 S: D-91080 Uttenreuth S: Germany +N: Jaya Kumar +E: jayalk@intworks.biz +W: http://www.intworks.biz +D: Arc monochrome LCD framebuffer driver, x86 reboot fixups +S: Gurgaon, India +S: Kuala Lumpur, Malaysia + N: Gabor Kuti M: seasons@falcon.sch.bme.hu M: seasons@makosteszta.sote.hu diff -puN drivers/video/arcfb.c~framebuffer-driver-for-arc-lcd-board-update drivers/video/arcfb.c --- 25/drivers/video/arcfb.c~framebuffer-driver-for-arc-lcd-board-update Mon Jun 6 14:41:00 2005 +++ 25-akpm/drivers/video/arcfb.c Mon Jun 6 14:41:00 2005 @@ -22,15 +22,15 @@ * be enabled or not. * * Todo: - * - support for wakeup on irq for button input * - testing with 4x4 + * - testing with interrupt hw * * General notes: * - User must set tuhold. It's in microseconds. According to the 108 spec, * the hold time is supposed to be at least 1 microsecond. - * - User must set arcfb_num_cols=x arcfb_num_rows=y, eg: x=2 means 128 + * - User must set num_cols=x num_rows=y, eg: x=2 means 128 * - User must set arcfb_enable=1 to enable it - * - User must set data_io_addr=0xIOADDR ctl_io_addr=0xIOADDR + * - User must set dio_addr=0xIOADDR cio_addr=0xIOADDR * */ @@ -46,21 +46,39 @@ #include #include #include +#include #include -#define ceil64(a) (a|0x3F) #define floor8(a) (a&(~0x07)) -#define floorXres(a,info) (a&(~(xres - 1))) -#define ceilXres(a,info) (a|(xres - 1)) -#define iceil8(a) ((int)((a+7)/8)*8) +#define floorXres(a,xres) (a&(~(xres - 1))) +#define iceil8(a) (((int)((a+7)/8))*8) +#define ceil64(a) (a|0x3F) +#define ceilXres(a,xres) (a|(xres - 1)) + +/* ks108 chipset specific defines and code */ + +#define KS_SET_DPY_START_LINE 0xC0 +#define KS_SET_PAGE_NUM 0xB8 +#define KS_SET_X 0x40 +#define KS_CEHI 0x01 +#define KS_CELO 0x00 +#define KS_SEL_CMD 0x08 +#define KS_SEL_DATA 0x00 +#define KS_DPY_ON 0x3F +#define KS_DPY_OFF 0x3E +#define KS_INTACK 0x40 +#define KS_CLRINT 0x02 struct arcfb_par { - unsigned long data_io_addr; - unsigned long ctl_io_addr; + unsigned long dio_addr; + unsigned long cio_addr; + unsigned long c2io_addr; atomic_t ref_count; unsigned char cslut[9]; struct fb_info *info; + unsigned int irq; + spinlock_t lock; }; static struct fb_fix_screeninfo arcfb_fix __initdata = { @@ -82,36 +100,40 @@ static struct fb_var_screeninfo arcfb_va .nonstd = 1, }; -static unsigned long arcfb_num_cols; -static unsigned long arcfb_num_rows; -static unsigned long data_io_addr; -static unsigned long ctl_io_addr; -static unsigned long splashvalue; +static unsigned long num_cols; +static unsigned long num_rows; +static unsigned long dio_addr; +static unsigned long cio_addr; +static unsigned long c2io_addr; +static unsigned long splashval; static unsigned long tuhold; static unsigned int nosplash; static unsigned int arcfb_enable; +static unsigned int irq; -/* ks108 chipset specific defines and code */ - -#define KS_SET_DPY_START_LINE 0xC0 -#define KS_SET_PAGE_NUM 0xB8 -#define KS_SET_X 0x40 -#define KS_CEHI 0x01 -#define KS_CELO 0x00 -#define KS_SEL_CMD 0x08 -#define KS_SEL_DATA 0x00 -#define KS_DPY_ON 0x3F -#define KS_DPY_OFF 0x3E +static DECLARE_WAIT_QUEUE_HEAD(arcfb_waitq); static void ks108_writeb_ctl(struct arcfb_par *par, unsigned int chipindex, unsigned char value) { unsigned char chipselval = par->cslut[chipindex]; - outb(chipselval|KS_CEHI|KS_SEL_CMD, par->ctl_io_addr); - outb(value, par->data_io_addr); + outb(chipselval|KS_CEHI|KS_SEL_CMD, par->cio_addr); + outb(value, par->dio_addr); + udelay(tuhold); + outb(chipselval|KS_CELO|KS_SEL_CMD, par->cio_addr); +} + +static void ks108_writeb_mainctl(struct arcfb_par *par, unsigned char value) +{ + + outb(value, par->cio_addr); udelay(tuhold); - outb(chipselval|KS_CELO|KS_SEL_CMD, par->ctl_io_addr); +} + +static unsigned char ks108_readb_ctl2(struct arcfb_par *par) +{ + return inb(par->c2io_addr); } static void ks108_writeb_data(struct arcfb_par *par, @@ -119,10 +141,10 @@ static void ks108_writeb_data(struct arc { unsigned char chipselval = par->cslut[chipindex]; - outb(chipselval|KS_CEHI|KS_SEL_DATA, par->ctl_io_addr); - outb(value, par->data_io_addr); + outb(chipselval|KS_CEHI|KS_SEL_DATA, par->cio_addr); + outb(value, par->dio_addr); udelay(tuhold); - outb(chipselval|KS_CELO|KS_SEL_DATA, par->ctl_io_addr); + outb(chipselval|KS_CELO|KS_SEL_DATA, par->cio_addr); } static void ks108_set_start_line(struct arcfb_par *par, @@ -152,7 +174,7 @@ static void ks108_clear_lcd(struct arcfb ks108_set_xaddr(par, chipindex, 0); for (j = 0; j < 64; j++) { ks108_writeb_data(par, chipindex, - (unsigned char) splashvalue); + (unsigned char) splashval); } } } @@ -186,8 +208,9 @@ static int arcfb_pan_display(struct fb_v if ((var->vmode & FB_VMODE_YWRAP) && (var->yoffset < 64) && (info->var.yres <= 64)) { - for (i = 0; i < arcfb_num_cols; i++) + for (i = 0; i < num_cols; i++) { ks108_set_start_line(par, i, var->yoffset); + } info->var.yoffset = var->yoffset; return 0; } @@ -195,6 +218,29 @@ static int arcfb_pan_display(struct fb_v return -EINVAL; } +static irqreturn_t arcfb_interrupt(int vec, void *dev_instance, + struct pt_regs *regs) +{ + struct fb_info *info = dev_instance; + unsigned char ctl2status; + struct arcfb_par *par = info->par; + + ctl2status = ks108_readb_ctl2(par); + + if (!(ctl2status & KS_INTACK)) /* not arc generated interrupt */ + return IRQ_NONE; + + ks108_writeb_mainctl(par, KS_CLRINT); + + spin_lock(&par->lock); + if (waitqueue_active(&arcfb_waitq)) { + wake_up(&arcfb_waitq); + } + spin_unlock(&par->lock); + + return IRQ_HANDLED; +} + /* * here we handle a specific page on the lcd. the complexity comes from * the fact that the fb is laidout in 8xX vertical columns. we extract @@ -212,7 +258,7 @@ static void arcfb_lcd_update_page(struct xindex = left >> 6; yindex = upper >> 6; - chipindex = (xindex + (yindex*arcfb_num_cols)); + chipindex = (xindex + (yindex*num_cols)); ks108_set_yaddr(par, chipindex, upper/8); @@ -285,7 +331,7 @@ static void arcfb_lcd_update_horiz(struc lower = min(upper + distance - 1, ceil64(upper)); while (distance > 0) { - distance -= ((lower - upper) + 1); + distance -= ((lower - upper) + 1 ); arcfb_lcd_update_vert(par, upper, lower, left, right); upper = lower + 1; lower = min(upper + distance - 1, ceil64(upper)); @@ -300,13 +346,19 @@ static void arcfb_lcd_update_horiz(struc static void arcfb_lcd_update(struct arcfb_par *par, unsigned int dx, unsigned int dy, unsigned int w, unsigned int h) { - unsigned int left, right, distance; + unsigned int left, right, distance, y; + + /* align the request first */ + y = floor8(dy); + h += dy - y; + h = iceil8(h); + distance = w; left = dx; right = min(left + w - 1, ceil64(left)); while (distance > 0) { - arcfb_lcd_update_horiz(par, left, right, dy, h); + arcfb_lcd_update_horiz(par, left, right, y, h); distance -= ((right - left) + 1); left = right + 1; right = min(left + distance - 1, ceil64(left)); @@ -316,46 +368,71 @@ static void arcfb_lcd_update(struct arcf void arcfb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) { struct arcfb_par *par = info->par; - int y, h; cfb_fillrect(info, rect); - /* this aligns the write to the lcd vertical page alignment */ - y = floor8(rect->dy); - h = rect->height + (rect->dy - y); - h = iceil8(h); /* update the physical lcd */ - arcfb_lcd_update(par, rect->dx, y, rect->width, h); + arcfb_lcd_update(par, rect->dx, rect->dy, rect->width, rect->height); } void arcfb_copyarea(struct fb_info *info, const struct fb_copyarea *area) { - unsigned int y, h; struct arcfb_par *par = info->par; cfb_copyarea(info, area); - /* this aligns the write to the lcd vertical page alignment */ - y = floor8(area->dy); - h = area->height + (area->dy - y); - h = iceil8(h); /* update the physical lcd */ - arcfb_lcd_update(par, area->dx, y, area->width, h); + arcfb_lcd_update(par, area->dx, area->dy, area->width, area->height); } void arcfb_imageblit(struct fb_info *info, const struct fb_image *image) { - unsigned int y, h; struct arcfb_par *par = info->par; cfb_imageblit(info, image); - /* this aligns the write to the lcd vertical page alignment */ - y = floor8(image->dy); - h = image->height + (image->dy - y); - h = iceil8(h); /* update the physical lcd */ - arcfb_lcd_update(par, image->dx, y, image->width, h); + arcfb_lcd_update(par, image->dx, image->dy, image->width, + image->height); +} + +static int arcfb_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg, + struct fb_info *info) +{ + void __user *argp = (void __user *)arg; + struct arcfb_par *par = info->par; + unsigned long flags; + + switch (cmd) { + case FBIO_WAITEVENT: + { + DEFINE_WAIT(wait); + /* illegal to wait on arc if no irq will occur */ + if (!par->irq) + return -EINVAL; + + /* wait until the Arc has generated an interrupt + * which will wake us up */ + spin_lock_irqsave(&par->lock, flags); + prepare_to_wait(&arcfb_waitq, &wait, + TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&par->lock, flags); + schedule(); + finish_wait(&arcfb_waitq, &wait); + } + case FBIO_GETCONTROL2: + { + unsigned char ctl2; + + ctl2 = ks108_readb_ctl2(info->par); + if (copy_to_user(argp, &ctl2, sizeof(ctl2))) + return -EFAULT; + return 0; + } + default: + return -EINVAL; + } } /* @@ -377,7 +454,6 @@ static ssize_t arcfb_write(struct file * struct arcfb_par *par; unsigned int xres; - p = *ppos; inode = file->f_dentry->d_inode; fbidx = iminor(inode); @@ -418,7 +494,6 @@ static ssize_t arcfb_write(struct file * y = startpos / xres; w = xres; h = bitcount / xres; - h = iceil8(h); arcfb_lcd_update(par, x, y, w, h); if (count) @@ -426,43 +501,6 @@ static ssize_t arcfb_write(struct file * return err; } -#ifndef MODULE -static int __init arcfb_setup(char *options) -{ - char *this_opt; - - if (!options || !*options) - return 0; - - while ((this_opt = strsep(&options, ",")) != NULL) { - if (!*this_opt) - continue; - if (!strncmp(this_opt, "nosplash", 8)) - nosplash = 1; - else if (!strncmp(this_opt, "arcfb_enable", 8)) - arcfb_enable = 1; - else if (!strncmp(this_opt, "arcfb_num_cols", 14)) - arcfb_num_cols = simple_strtoul((this_opt + 14), - NULL, 0); - else if (!strncmp(this_opt, "arcfb_num_rows", 14)) - arcfb_num_rows = simple_strtoul((this_opt + 14), - NULL, 0); - else if (!strncmp(this_opt, "data_io_addr", 12)) - data_io_addr = simple_strtoul((this_opt + 12), - NULL, 0); - else if (!strncmp(this_opt, "ctl_io_addr", 11)) - ctl_io_addr = simple_strtoul((this_opt + 11), - NULL, 0); - else if (!strncmp(this_opt, "splashvalue", 11)) - splashvalue = simple_strtoul((this_opt + 11), - NULL, 0); - else if (!strncmp(this_opt, "tuhold", 6)) - tuhold = simple_strtoul((this_opt + 6), NULL, 0); - } - return 0; -} -#endif - static void arcfb_platform_release(struct device *device) { } @@ -477,6 +515,7 @@ static struct fb_ops arcfb_ops = { .fb_copyarea = arcfb_copyarea, .fb_imageblit = arcfb_imageblit, .fb_cursor = soft_cursor, + .fb_ioctl = arcfb_ioctl, }; static int __init arcfb_probe(struct device *device) @@ -489,7 +528,7 @@ static int __init arcfb_probe(struct dev struct arcfb_par *par; int i; - videomemorysize = (((64*64)*arcfb_num_cols)*arcfb_num_rows)/8; + videomemorysize = (((64*64)*num_cols)*num_rows)/8; /* We need a flat backing store for the Arc's less-flat actual paged framebuffer */ @@ -509,23 +548,37 @@ static int __init arcfb_probe(struct dev info->fix = arcfb_fix; par = info->par; par->info = info; - par->data_io_addr = data_io_addr; - par->ctl_io_addr = ctl_io_addr; + + if (!dio_addr || !cio_addr || !c2io_addr) { + printk(KERN_WARNING "no IO addresses supplied\n"); + goto err1; + } + par->dio_addr = dio_addr; + par->cio_addr = cio_addr; + par->c2io_addr = c2io_addr; par->cslut[0] = 0x00; par->cslut[1] = 0x06; info->flags = FBINFO_FLAG_DEFAULT; - + spin_lock_init(&par->lock); retval = register_framebuffer(info); if (retval < 0) goto err1; dev_set_drvdata(&dev->dev, info); - + if (irq) { + par->irq = irq; + if (request_irq(par->irq, &arcfb_interrupt, SA_SHIRQ, + "arcfb", info)) { + printk(KERN_INFO + "arcfb: Failed req IRQ %d\n", par->irq); + goto err1; + } + } printk(KERN_INFO "fb%d: Arc frame buffer device, using %dK of video memory\n", info->node, videomemorysize >> 10); /* this inits the lcd but doesn't clear dirty pixels */ - for (i = 0; i < arcfb_num_cols * arcfb_num_rows; i++) { + for (i = 0; i < num_cols * num_rows; i++) { ks108_writeb_ctl(par, i, KS_DPY_OFF); ks108_set_start_line(par, i, 0); ks108_set_yaddr(par, i, 0); @@ -535,7 +588,7 @@ static int __init arcfb_probe(struct dev /* if we were told to splash the screen, we just clear it */ if (!nosplash) { - for (i = 0; i < arcfb_num_cols * arcfb_num_rows; i++) { + for (i = 0; i < num_cols * num_rows; i++) { printk(KERN_INFO "fb%d: splashing lcd %d\n", info->node, i); ks108_set_start_line(par, i, 0); @@ -582,15 +635,6 @@ static int __init arcfb_init(void) { int ret; -#ifndef MODULE - char *option = NULL; - - if (fb_get_options("arcfb", &option)) - return -ENODEV; - - arcfb_setup(option); -#endif - if (!arcfb_enable) return -ENXIO; @@ -610,14 +654,26 @@ static void __exit arcfb_exit(void) driver_unregister(&arcfb_driver); } -module_param(arcfb_num_cols, ulong, 0); -module_param(arcfb_num_rows, ulong, 0); +module_param(num_cols, ulong, 0); +MODULE_PARM_DESC(num_cols, "Num horiz panels, eg: 2 = 128 bit wide"); +module_param(num_rows, ulong, 0); +MODULE_PARM_DESC(num_rows, "Num vert panels, eg: 1 = 64 bit high"); module_param(nosplash, uint, 0); +MODULE_PARM_DESC(nosplash, "Disable doing the splash screen"); module_param(arcfb_enable, uint, 0); -module_param(data_io_addr, ulong, 0); -module_param(ctl_io_addr, ulong, 0); -module_param(splashvalue, ulong, 0); +MODULE_PARM_DESC(arcfb_enable, "Enable communication with Arc board"); +module_param(dio_addr, ulong, 0); +MODULE_PARM_DESC(dio_addr, "IO address for data, eg: 0x480"); +module_param(cio_addr, ulong, 0); +MODULE_PARM_DESC(cio_addr, "IO address for control, eg: 0x400"); +module_param(c2io_addr, ulong, 0); +MODULE_PARM_DESC(c2io_addr, "IO address for secondary control, eg: 0x408"); +module_param(splashval, ulong, 0); +MODULE_PARM_DESC(splashval, "Splash pattern: 0xFF is black, 0x00 is green"); module_param(tuhold, ulong, 0); +MODULE_PARM_DESC(tuhold, "Time to hold between strobing data to Arc board"); +module_param(irq, uint, 0); +MODULE_PARM_DESC(irq, "IRQ for the Arc board"); module_init(arcfb_init); module_exit(arcfb_exit); @@ -625,3 +681,4 @@ module_exit(arcfb_exit); MODULE_DESCRIPTION("fbdev driver for Arc monochrome LCD board"); MODULE_AUTHOR("Jaya Kumar"); MODULE_LICENSE("GPL"); + diff -puN /dev/null include/linux/arcfb.h --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/include/linux/arcfb.h Mon Jun 6 14:41:00 2005 @@ -0,0 +1,8 @@ +#ifndef __LINUX_ARCFB_H__ +#define __LINUX_ARCFB_H__ + +#define FBIO_WAITEVENT _IO('F', 0x88) +#define FBIO_GETCONTROL2 _IOR('F', 0x89, size_t) + +#endif + diff -puN MAINTAINERS~framebuffer-driver-for-arc-lcd-board-update MAINTAINERS --- 25/MAINTAINERS~framebuffer-driver-for-arc-lcd-board-update Mon Jun 6 14:41:00 2005 +++ 25-akpm/MAINTAINERS Mon Jun 6 14:41:00 2005 @@ -265,6 +265,11 @@ P: Arnaldo Carvalho de Melo M: acme@conectiva.com.br S: Maintained +ARC FRAMEBUFFER DRIVER +P: Jaya Kumar +M: jayalk@intworks.biz +S: Maintained + ARM26 ARCHITECTURE P: Ian Molton M: spyro@f2s.com _