11154ea7dSJaya Kumar /*
21154ea7dSJaya Kumar * linux/drivers/video/arcfb.c -- FB driver for Arc monochrome LCD board
31154ea7dSJaya Kumar *
41154ea7dSJaya Kumar * Copyright (C) 2005, Jaya Kumar <jayalk@intworks.biz>
51154ea7dSJaya Kumar *
61154ea7dSJaya Kumar * This file is subject to the terms and conditions of the GNU General Public
71154ea7dSJaya Kumar * License. See the file COPYING in the main directory of this archive for
81154ea7dSJaya Kumar * more details.
91154ea7dSJaya Kumar *
101154ea7dSJaya Kumar * Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven.
111154ea7dSJaya Kumar *
121154ea7dSJaya Kumar * This driver was written to be used with the Arc LCD board. Arc uses a
131154ea7dSJaya Kumar * set of KS108 chips that control individual 64x64 LCD matrices. The board
141154ea7dSJaya Kumar * can be paneled in a variety of setups such as 2x1=128x64, 4x4=256x256 and
151154ea7dSJaya Kumar * so on. The interface between the board and the host is TTL based GPIO. The
161154ea7dSJaya Kumar * GPIO requirements are 8 writable data lines and 4+n lines for control. On a
171154ea7dSJaya Kumar * GPIO-less system, the board can be tested by connecting the respective sigs
181154ea7dSJaya Kumar * up to a parallel port connector. The driver requires the IO addresses for
191154ea7dSJaya Kumar * data and control GPIO at load time. It is unable to probe for the
201154ea7dSJaya Kumar * existence of the LCD so it must be told at load time whether it should
211154ea7dSJaya Kumar * be enabled or not.
221154ea7dSJaya Kumar *
231154ea7dSJaya Kumar * Todo:
241154ea7dSJaya Kumar * - testing with 4x4
251154ea7dSJaya Kumar * - testing with interrupt hw
261154ea7dSJaya Kumar *
271154ea7dSJaya Kumar * General notes:
281154ea7dSJaya Kumar * - User must set tuhold. It's in microseconds. According to the 108 spec,
291154ea7dSJaya Kumar * the hold time is supposed to be at least 1 microsecond.
301154ea7dSJaya Kumar * - User must set num_cols=x num_rows=y, eg: x=2 means 128
311154ea7dSJaya Kumar * - User must set arcfb_enable=1 to enable it
321154ea7dSJaya Kumar * - User must set dio_addr=0xIOADDR cio_addr=0xIOADDR
331154ea7dSJaya Kumar *
341154ea7dSJaya Kumar */
351154ea7dSJaya Kumar
361154ea7dSJaya Kumar #include <linux/module.h>
371154ea7dSJaya Kumar #include <linux/kernel.h>
381154ea7dSJaya Kumar #include <linux/errno.h>
391154ea7dSJaya Kumar #include <linux/string.h>
401154ea7dSJaya Kumar #include <linux/mm.h>
411154ea7dSJaya Kumar #include <linux/vmalloc.h>
421154ea7dSJaya Kumar #include <linux/delay.h>
431154ea7dSJaya Kumar #include <linux/interrupt.h>
44e1d534acSThomas Zimmermann #include <linux/io.h>
451154ea7dSJaya Kumar #include <linux/fb.h>
461154ea7dSJaya Kumar #include <linux/init.h>
471154ea7dSJaya Kumar #include <linux/arcfb.h>
48d052d1beSRussell King #include <linux/platform_device.h>
491154ea7dSJaya Kumar
5084902b7aSKrzysztof Helt #include <linux/uaccess.h>
511154ea7dSJaya Kumar
521154ea7dSJaya Kumar #define floor8(a) (a&(~0x07))
531154ea7dSJaya Kumar #define floorXres(a,xres) (a&(~(xres - 1)))
541154ea7dSJaya Kumar #define iceil8(a) (((int)((a+7)/8))*8)
551154ea7dSJaya Kumar #define ceil64(a) (a|0x3F)
561154ea7dSJaya Kumar #define ceilXres(a,xres) (a|(xres - 1))
571154ea7dSJaya Kumar
581154ea7dSJaya Kumar /* ks108 chipset specific defines and code */
591154ea7dSJaya Kumar
601154ea7dSJaya Kumar #define KS_SET_DPY_START_LINE 0xC0
611154ea7dSJaya Kumar #define KS_SET_PAGE_NUM 0xB8
621154ea7dSJaya Kumar #define KS_SET_X 0x40
631154ea7dSJaya Kumar #define KS_CEHI 0x01
641154ea7dSJaya Kumar #define KS_CELO 0x00
651154ea7dSJaya Kumar #define KS_SEL_CMD 0x08
661154ea7dSJaya Kumar #define KS_SEL_DATA 0x00
671154ea7dSJaya Kumar #define KS_DPY_ON 0x3F
681154ea7dSJaya Kumar #define KS_DPY_OFF 0x3E
691154ea7dSJaya Kumar #define KS_INTACK 0x40
701154ea7dSJaya Kumar #define KS_CLRINT 0x02
711154ea7dSJaya Kumar
721154ea7dSJaya Kumar struct arcfb_par {
731154ea7dSJaya Kumar unsigned long dio_addr;
741154ea7dSJaya Kumar unsigned long cio_addr;
751154ea7dSJaya Kumar unsigned long c2io_addr;
761154ea7dSJaya Kumar atomic_t ref_count;
771154ea7dSJaya Kumar unsigned char cslut[9];
781154ea7dSJaya Kumar struct fb_info *info;
791154ea7dSJaya Kumar unsigned int irq;
801154ea7dSJaya Kumar spinlock_t lock;
811154ea7dSJaya Kumar };
821154ea7dSJaya Kumar
83ca9384c5SJulia Lawall static const struct fb_fix_screeninfo arcfb_fix = {
841154ea7dSJaya Kumar .id = "arcfb",
851154ea7dSJaya Kumar .type = FB_TYPE_PACKED_PIXELS,
861154ea7dSJaya Kumar .visual = FB_VISUAL_MONO01,
871154ea7dSJaya Kumar .xpanstep = 0,
881154ea7dSJaya Kumar .ypanstep = 1,
891154ea7dSJaya Kumar .ywrapstep = 0,
901154ea7dSJaya Kumar .accel = FB_ACCEL_NONE,
911154ea7dSJaya Kumar };
921154ea7dSJaya Kumar
93ca9384c5SJulia Lawall static const struct fb_var_screeninfo arcfb_var = {
941154ea7dSJaya Kumar .xres = 128,
951154ea7dSJaya Kumar .yres = 64,
961154ea7dSJaya Kumar .xres_virtual = 128,
971154ea7dSJaya Kumar .yres_virtual = 64,
981154ea7dSJaya Kumar .bits_per_pixel = 1,
991154ea7dSJaya Kumar .nonstd = 1,
1001154ea7dSJaya Kumar };
1011154ea7dSJaya Kumar
1021154ea7dSJaya Kumar static unsigned long num_cols;
1031154ea7dSJaya Kumar static unsigned long num_rows;
1041154ea7dSJaya Kumar static unsigned long dio_addr;
1051154ea7dSJaya Kumar static unsigned long cio_addr;
1061154ea7dSJaya Kumar static unsigned long c2io_addr;
1071154ea7dSJaya Kumar static unsigned long splashval;
1081154ea7dSJaya Kumar static unsigned long tuhold;
1091154ea7dSJaya Kumar static unsigned int nosplash;
1101154ea7dSJaya Kumar static unsigned int arcfb_enable;
1111154ea7dSJaya Kumar static unsigned int irq;
1121154ea7dSJaya Kumar
1131154ea7dSJaya Kumar static DECLARE_WAIT_QUEUE_HEAD(arcfb_waitq);
1141154ea7dSJaya Kumar
ks108_writeb_ctl(struct arcfb_par * par,unsigned int chipindex,unsigned char value)1151154ea7dSJaya Kumar static void ks108_writeb_ctl(struct arcfb_par *par,
1161154ea7dSJaya Kumar unsigned int chipindex, unsigned char value)
1171154ea7dSJaya Kumar {
1181154ea7dSJaya Kumar unsigned char chipselval = par->cslut[chipindex];
1191154ea7dSJaya Kumar
1201154ea7dSJaya Kumar outb(chipselval|KS_CEHI|KS_SEL_CMD, par->cio_addr);
1211154ea7dSJaya Kumar outb(value, par->dio_addr);
1221154ea7dSJaya Kumar udelay(tuhold);
1231154ea7dSJaya Kumar outb(chipselval|KS_CELO|KS_SEL_CMD, par->cio_addr);
1241154ea7dSJaya Kumar }
1251154ea7dSJaya Kumar
ks108_writeb_mainctl(struct arcfb_par * par,unsigned char value)1261154ea7dSJaya Kumar static void ks108_writeb_mainctl(struct arcfb_par *par, unsigned char value)
1271154ea7dSJaya Kumar {
1281154ea7dSJaya Kumar
1291154ea7dSJaya Kumar outb(value, par->cio_addr);
1301154ea7dSJaya Kumar udelay(tuhold);
1311154ea7dSJaya Kumar }
1321154ea7dSJaya Kumar
ks108_readb_ctl2(struct arcfb_par * par)1331154ea7dSJaya Kumar static unsigned char ks108_readb_ctl2(struct arcfb_par *par)
1341154ea7dSJaya Kumar {
1351154ea7dSJaya Kumar return inb(par->c2io_addr);
1361154ea7dSJaya Kumar }
1371154ea7dSJaya Kumar
ks108_writeb_data(struct arcfb_par * par,unsigned int chipindex,unsigned char value)1381154ea7dSJaya Kumar static void ks108_writeb_data(struct arcfb_par *par,
1391154ea7dSJaya Kumar unsigned int chipindex, unsigned char value)
1401154ea7dSJaya Kumar {
1411154ea7dSJaya Kumar unsigned char chipselval = par->cslut[chipindex];
1421154ea7dSJaya Kumar
1431154ea7dSJaya Kumar outb(chipselval|KS_CEHI|KS_SEL_DATA, par->cio_addr);
1441154ea7dSJaya Kumar outb(value, par->dio_addr);
1451154ea7dSJaya Kumar udelay(tuhold);
1461154ea7dSJaya Kumar outb(chipselval|KS_CELO|KS_SEL_DATA, par->cio_addr);
1471154ea7dSJaya Kumar }
1481154ea7dSJaya Kumar
ks108_set_start_line(struct arcfb_par * par,unsigned int chipindex,unsigned char y)1491154ea7dSJaya Kumar static void ks108_set_start_line(struct arcfb_par *par,
1501154ea7dSJaya Kumar unsigned int chipindex, unsigned char y)
1511154ea7dSJaya Kumar {
1521154ea7dSJaya Kumar ks108_writeb_ctl(par, chipindex, KS_SET_DPY_START_LINE|y);
1531154ea7dSJaya Kumar }
1541154ea7dSJaya Kumar
ks108_set_yaddr(struct arcfb_par * par,unsigned int chipindex,unsigned char y)1551154ea7dSJaya Kumar static void ks108_set_yaddr(struct arcfb_par *par,
1561154ea7dSJaya Kumar unsigned int chipindex, unsigned char y)
1571154ea7dSJaya Kumar {
1581154ea7dSJaya Kumar ks108_writeb_ctl(par, chipindex, KS_SET_PAGE_NUM|y);
1591154ea7dSJaya Kumar }
1601154ea7dSJaya Kumar
ks108_set_xaddr(struct arcfb_par * par,unsigned int chipindex,unsigned char x)1611154ea7dSJaya Kumar static void ks108_set_xaddr(struct arcfb_par *par,
1621154ea7dSJaya Kumar unsigned int chipindex, unsigned char x)
1631154ea7dSJaya Kumar {
1641154ea7dSJaya Kumar ks108_writeb_ctl(par, chipindex, KS_SET_X|x);
1651154ea7dSJaya Kumar }
1661154ea7dSJaya Kumar
ks108_clear_lcd(struct arcfb_par * par,unsigned int chipindex)1671154ea7dSJaya Kumar static void ks108_clear_lcd(struct arcfb_par *par, unsigned int chipindex)
1681154ea7dSJaya Kumar {
1691154ea7dSJaya Kumar int i,j;
1701154ea7dSJaya Kumar
1711154ea7dSJaya Kumar for (i = 0; i <= 8; i++) {
1721154ea7dSJaya Kumar ks108_set_yaddr(par, chipindex, i);
1731154ea7dSJaya Kumar ks108_set_xaddr(par, chipindex, 0);
1741154ea7dSJaya Kumar for (j = 0; j < 64; j++) {
1751154ea7dSJaya Kumar ks108_writeb_data(par, chipindex,
1761154ea7dSJaya Kumar (unsigned char) splashval);
1771154ea7dSJaya Kumar }
1781154ea7dSJaya Kumar }
1791154ea7dSJaya Kumar }
1801154ea7dSJaya Kumar
1811154ea7dSJaya Kumar /* main arcfb functions */
1821154ea7dSJaya Kumar
arcfb_open(struct fb_info * info,int user)1831154ea7dSJaya Kumar static int arcfb_open(struct fb_info *info, int user)
1841154ea7dSJaya Kumar {
1851154ea7dSJaya Kumar struct arcfb_par *par = info->par;
1861154ea7dSJaya Kumar
1871154ea7dSJaya Kumar atomic_inc(&par->ref_count);
1881154ea7dSJaya Kumar return 0;
1891154ea7dSJaya Kumar }
1901154ea7dSJaya Kumar
arcfb_release(struct fb_info * info,int user)1911154ea7dSJaya Kumar static int arcfb_release(struct fb_info *info, int user)
1921154ea7dSJaya Kumar {
1931154ea7dSJaya Kumar struct arcfb_par *par = info->par;
1941154ea7dSJaya Kumar int count = atomic_read(&par->ref_count);
1951154ea7dSJaya Kumar
1961154ea7dSJaya Kumar if (!count)
1971154ea7dSJaya Kumar return -EINVAL;
1981154ea7dSJaya Kumar atomic_dec(&par->ref_count);
1991154ea7dSJaya Kumar return 0;
2001154ea7dSJaya Kumar }
2011154ea7dSJaya Kumar
arcfb_pan_display(struct fb_var_screeninfo * var,struct fb_info * info)2021154ea7dSJaya Kumar static int arcfb_pan_display(struct fb_var_screeninfo *var,
2031154ea7dSJaya Kumar struct fb_info *info)
2041154ea7dSJaya Kumar {
2051154ea7dSJaya Kumar int i;
2061154ea7dSJaya Kumar struct arcfb_par *par = info->par;
2071154ea7dSJaya Kumar
2081154ea7dSJaya Kumar if ((var->vmode & FB_VMODE_YWRAP) && (var->yoffset < 64)
2091154ea7dSJaya Kumar && (info->var.yres <= 64)) {
2101154ea7dSJaya Kumar for (i = 0; i < num_cols; i++) {
2111154ea7dSJaya Kumar ks108_set_start_line(par, i, var->yoffset);
2121154ea7dSJaya Kumar }
2131154ea7dSJaya Kumar info->var.yoffset = var->yoffset;
2141154ea7dSJaya Kumar return 0;
2151154ea7dSJaya Kumar }
2161154ea7dSJaya Kumar
2171154ea7dSJaya Kumar return -EINVAL;
2181154ea7dSJaya Kumar }
2191154ea7dSJaya Kumar
arcfb_interrupt(int vec,void * dev_instance)2207d12e780SDavid Howells static irqreturn_t arcfb_interrupt(int vec, void *dev_instance)
2211154ea7dSJaya Kumar {
2221154ea7dSJaya Kumar struct fb_info *info = dev_instance;
2231154ea7dSJaya Kumar unsigned char ctl2status;
2241154ea7dSJaya Kumar struct arcfb_par *par = info->par;
2251154ea7dSJaya Kumar
2261154ea7dSJaya Kumar ctl2status = ks108_readb_ctl2(par);
2271154ea7dSJaya Kumar
2281154ea7dSJaya Kumar if (!(ctl2status & KS_INTACK)) /* not arc generated interrupt */
2291154ea7dSJaya Kumar return IRQ_NONE;
2301154ea7dSJaya Kumar
2311154ea7dSJaya Kumar ks108_writeb_mainctl(par, KS_CLRINT);
2321154ea7dSJaya Kumar
2331154ea7dSJaya Kumar spin_lock(&par->lock);
2341154ea7dSJaya Kumar if (waitqueue_active(&arcfb_waitq)) {
2351154ea7dSJaya Kumar wake_up(&arcfb_waitq);
2361154ea7dSJaya Kumar }
2371154ea7dSJaya Kumar spin_unlock(&par->lock);
2381154ea7dSJaya Kumar
2391154ea7dSJaya Kumar return IRQ_HANDLED;
2401154ea7dSJaya Kumar }
2411154ea7dSJaya Kumar
2421154ea7dSJaya Kumar /*
2431154ea7dSJaya Kumar * here we handle a specific page on the lcd. the complexity comes from
2441154ea7dSJaya Kumar * the fact that the fb is laidout in 8xX vertical columns. we extract
2451154ea7dSJaya Kumar * each write of 8 vertical pixels. then we shift out as we move along
2461154ea7dSJaya Kumar * X. That's what rightshift does. bitmask selects the desired input bit.
2471154ea7dSJaya Kumar */
arcfb_lcd_update_page(struct arcfb_par * par,unsigned int upper,unsigned int left,unsigned int right,unsigned int distance)2481154ea7dSJaya Kumar static void arcfb_lcd_update_page(struct arcfb_par *par, unsigned int upper,
2491154ea7dSJaya Kumar unsigned int left, unsigned int right, unsigned int distance)
2501154ea7dSJaya Kumar {
2511154ea7dSJaya Kumar unsigned char *src;
2521154ea7dSJaya Kumar unsigned int xindex, yindex, chipindex, linesize;
253604cc999SAntonino A. Daplas int i;
2541154ea7dSJaya Kumar unsigned char val;
2551154ea7dSJaya Kumar unsigned char bitmask, rightshift;
2561154ea7dSJaya Kumar
2571154ea7dSJaya Kumar xindex = left >> 6;
2581154ea7dSJaya Kumar yindex = upper >> 6;
2591154ea7dSJaya Kumar chipindex = (xindex + (yindex*num_cols));
2601154ea7dSJaya Kumar
2611154ea7dSJaya Kumar ks108_set_yaddr(par, chipindex, upper/8);
2621154ea7dSJaya Kumar
2631154ea7dSJaya Kumar linesize = par->info->var.xres/8;
2648112bafcSThomas Zimmermann src = (unsigned char *)par->info->screen_buffer + (left/8) +
265d2e8d369SAntonino A. Daplas (upper * linesize);
2661154ea7dSJaya Kumar ks108_set_xaddr(par, chipindex, left);
2671154ea7dSJaya Kumar
2681154ea7dSJaya Kumar bitmask=1;
2691154ea7dSJaya Kumar rightshift=0;
2701154ea7dSJaya Kumar while (left <= right) {
2711154ea7dSJaya Kumar val = 0;
2721154ea7dSJaya Kumar for (i = 0; i < 8; i++) {
2731154ea7dSJaya Kumar if ( i > rightshift) {
2741154ea7dSJaya Kumar val |= (*(src + (i*linesize)) & bitmask)
2751154ea7dSJaya Kumar << (i - rightshift);
2761154ea7dSJaya Kumar } else {
2771154ea7dSJaya Kumar val |= (*(src + (i*linesize)) & bitmask)
2781154ea7dSJaya Kumar >> (rightshift - i);
2791154ea7dSJaya Kumar }
2801154ea7dSJaya Kumar }
2811154ea7dSJaya Kumar ks108_writeb_data(par, chipindex, val);
2821154ea7dSJaya Kumar left++;
2831154ea7dSJaya Kumar if (bitmask == 0x80) {
2841154ea7dSJaya Kumar bitmask = 1;
2851154ea7dSJaya Kumar src++;
2861154ea7dSJaya Kumar rightshift=0;
2871154ea7dSJaya Kumar } else {
2881154ea7dSJaya Kumar bitmask <<= 1;
2891154ea7dSJaya Kumar rightshift++;
2901154ea7dSJaya Kumar }
2911154ea7dSJaya Kumar }
2921154ea7dSJaya Kumar }
2931154ea7dSJaya Kumar
2941154ea7dSJaya Kumar /*
2951154ea7dSJaya Kumar * here we handle the entire vertical page of the update. we write across
2961154ea7dSJaya Kumar * lcd chips. update_page uses the upper/left values to decide which
2971154ea7dSJaya Kumar * chip to select for the right. upper is needed for setting the page
2981154ea7dSJaya Kumar * desired for the write.
2991154ea7dSJaya Kumar */
arcfb_lcd_update_vert(struct arcfb_par * par,unsigned int top,unsigned int bottom,unsigned int left,unsigned int right)3001154ea7dSJaya Kumar static void arcfb_lcd_update_vert(struct arcfb_par *par, unsigned int top,
3011154ea7dSJaya Kumar unsigned int bottom, unsigned int left, unsigned int right)
3021154ea7dSJaya Kumar {
3031154ea7dSJaya Kumar unsigned int distance, upper, lower;
3041154ea7dSJaya Kumar
3051154ea7dSJaya Kumar distance = (bottom - top) + 1;
3061154ea7dSJaya Kumar upper = top;
3071154ea7dSJaya Kumar lower = top + 7;
3081154ea7dSJaya Kumar
3091154ea7dSJaya Kumar while (distance > 0) {
3101154ea7dSJaya Kumar distance -= 8;
3111154ea7dSJaya Kumar arcfb_lcd_update_page(par, upper, left, right, 8);
3121154ea7dSJaya Kumar upper = lower + 1;
3131154ea7dSJaya Kumar lower = upper + 7;
3141154ea7dSJaya Kumar }
3151154ea7dSJaya Kumar }
3161154ea7dSJaya Kumar
3171154ea7dSJaya Kumar /*
3181154ea7dSJaya Kumar * here we handle horizontal blocks for the update. update_vert will
3191154ea7dSJaya Kumar * handle spaning multiple pages. we break out each horizontal
3201154ea7dSJaya Kumar * block in to individual blocks no taller than 64 pixels.
3211154ea7dSJaya Kumar */
arcfb_lcd_update_horiz(struct arcfb_par * par,unsigned int left,unsigned int right,unsigned int top,unsigned int h)3221154ea7dSJaya Kumar static void arcfb_lcd_update_horiz(struct arcfb_par *par, unsigned int left,
3231154ea7dSJaya Kumar unsigned int right, unsigned int top, unsigned int h)
3241154ea7dSJaya Kumar {
3251154ea7dSJaya Kumar unsigned int distance, upper, lower;
3261154ea7dSJaya Kumar
3271154ea7dSJaya Kumar distance = h;
3281154ea7dSJaya Kumar upper = floor8(top);
3291154ea7dSJaya Kumar lower = min(upper + distance - 1, ceil64(upper));
3301154ea7dSJaya Kumar
3311154ea7dSJaya Kumar while (distance > 0) {
3321154ea7dSJaya Kumar distance -= ((lower - upper) + 1 );
3331154ea7dSJaya Kumar arcfb_lcd_update_vert(par, upper, lower, left, right);
3341154ea7dSJaya Kumar upper = lower + 1;
3351154ea7dSJaya Kumar lower = min(upper + distance - 1, ceil64(upper));
3361154ea7dSJaya Kumar }
3371154ea7dSJaya Kumar }
3381154ea7dSJaya Kumar
3391154ea7dSJaya Kumar /*
340ff0c2642SMasanari Iida * here we start the process of splitting out the fb update into
341ff0c2642SMasanari Iida * individual blocks of pixels. we end up splitting into 64x64 blocks
3421154ea7dSJaya Kumar * and finally down to 64x8 pages.
3431154ea7dSJaya Kumar */
arcfb_lcd_update(struct arcfb_par * par,unsigned int dx,unsigned int dy,unsigned int w,unsigned int h)3441154ea7dSJaya Kumar static void arcfb_lcd_update(struct arcfb_par *par, unsigned int dx,
3451154ea7dSJaya Kumar unsigned int dy, unsigned int w, unsigned int h)
3461154ea7dSJaya Kumar {
3471154ea7dSJaya Kumar unsigned int left, right, distance, y;
3481154ea7dSJaya Kumar
3491154ea7dSJaya Kumar /* align the request first */
3501154ea7dSJaya Kumar y = floor8(dy);
3511154ea7dSJaya Kumar h += dy - y;
3521154ea7dSJaya Kumar h = iceil8(h);
3531154ea7dSJaya Kumar
3541154ea7dSJaya Kumar distance = w;
3551154ea7dSJaya Kumar left = dx;
3561154ea7dSJaya Kumar right = min(left + w - 1, ceil64(left));
3571154ea7dSJaya Kumar
3581154ea7dSJaya Kumar while (distance > 0) {
3591154ea7dSJaya Kumar arcfb_lcd_update_horiz(par, left, right, y, h);
3601154ea7dSJaya Kumar distance -= ((right - left) + 1);
3611154ea7dSJaya Kumar left = right + 1;
3621154ea7dSJaya Kumar right = min(left + distance - 1, ceil64(left));
3631154ea7dSJaya Kumar }
3641154ea7dSJaya Kumar }
3651154ea7dSJaya Kumar
arcfb_ioctl(struct fb_info * info,unsigned int cmd,unsigned long arg)36667a6680dSChristoph Hellwig static int arcfb_ioctl(struct fb_info *info,
36767a6680dSChristoph Hellwig unsigned int cmd, unsigned long arg)
3681154ea7dSJaya Kumar {
3691154ea7dSJaya Kumar void __user *argp = (void __user *)arg;
3701154ea7dSJaya Kumar struct arcfb_par *par = info->par;
3711154ea7dSJaya Kumar unsigned long flags;
3721154ea7dSJaya Kumar
3731154ea7dSJaya Kumar switch (cmd) {
3741154ea7dSJaya Kumar case FBIO_WAITEVENT:
3751154ea7dSJaya Kumar {
3761154ea7dSJaya Kumar DEFINE_WAIT(wait);
3771154ea7dSJaya Kumar /* illegal to wait on arc if no irq will occur */
3781154ea7dSJaya Kumar if (!par->irq)
3791154ea7dSJaya Kumar return -EINVAL;
3801154ea7dSJaya Kumar
3811154ea7dSJaya Kumar /* wait until the Arc has generated an interrupt
3821154ea7dSJaya Kumar * which will wake us up */
3831154ea7dSJaya Kumar spin_lock_irqsave(&par->lock, flags);
3841154ea7dSJaya Kumar prepare_to_wait(&arcfb_waitq, &wait,
3851154ea7dSJaya Kumar TASK_INTERRUPTIBLE);
3861154ea7dSJaya Kumar spin_unlock_irqrestore(&par->lock, flags);
3871154ea7dSJaya Kumar schedule();
3881154ea7dSJaya Kumar finish_wait(&arcfb_waitq, &wait);
3891154ea7dSJaya Kumar }
390ad04fae0SGustavo A. R. Silva fallthrough;
39197037597SGustavo A. R. Silva
3921154ea7dSJaya Kumar case FBIO_GETCONTROL2:
3931154ea7dSJaya Kumar {
3941154ea7dSJaya Kumar unsigned char ctl2;
3951154ea7dSJaya Kumar
3961154ea7dSJaya Kumar ctl2 = ks108_readb_ctl2(info->par);
3971154ea7dSJaya Kumar if (copy_to_user(argp, &ctl2, sizeof(ctl2)))
3981154ea7dSJaya Kumar return -EFAULT;
3991154ea7dSJaya Kumar return 0;
4001154ea7dSJaya Kumar }
4011154ea7dSJaya Kumar default:
4021154ea7dSJaya Kumar return -EINVAL;
4031154ea7dSJaya Kumar }
4041154ea7dSJaya Kumar }
4051154ea7dSJaya Kumar
arcfb_damage_range(struct fb_info * info,off_t off,size_t len)40628f57d03SThomas Zimmermann static void arcfb_damage_range(struct fb_info *info, off_t off, size_t len)
4071154ea7dSJaya Kumar {
40828f57d03SThomas Zimmermann struct arcfb_par *par = info->par;
40928f57d03SThomas Zimmermann unsigned int xres = info->var.xres;
41028f57d03SThomas Zimmermann unsigned int bitppos, startpos, endpos, bitcount;
41128f57d03SThomas Zimmermann unsigned int x, y, width, height;
4121154ea7dSJaya Kumar
41328f57d03SThomas Zimmermann bitppos = off * 8;
4141154ea7dSJaya Kumar startpos = floorXres(bitppos, xres);
41528f57d03SThomas Zimmermann endpos = ceilXres((bitppos + (len * 8)), xres);
4161154ea7dSJaya Kumar bitcount = endpos - startpos;
4171154ea7dSJaya Kumar
4181154ea7dSJaya Kumar x = startpos % xres;
4191154ea7dSJaya Kumar y = startpos / xres;
42028f57d03SThomas Zimmermann width = xres;
42128f57d03SThomas Zimmermann height = bitcount / xres;
4221154ea7dSJaya Kumar
42328f57d03SThomas Zimmermann arcfb_lcd_update(par, x, y, width, height);
4241154ea7dSJaya Kumar }
4251154ea7dSJaya Kumar
arcfb_damage_area(struct fb_info * info,u32 x,u32 y,u32 width,u32 height)42628f57d03SThomas Zimmermann static void arcfb_damage_area(struct fb_info *info, u32 x, u32 y,
42728f57d03SThomas Zimmermann u32 width, u32 height)
42828f57d03SThomas Zimmermann {
42928f57d03SThomas Zimmermann struct arcfb_par *par = info->par;
43028f57d03SThomas Zimmermann
43128f57d03SThomas Zimmermann /* update the physical lcd */
43228f57d03SThomas Zimmermann arcfb_lcd_update(par, x, y, width, height);
43328f57d03SThomas Zimmermann }
43428f57d03SThomas Zimmermann
43528f57d03SThomas Zimmermann FB_GEN_DEFAULT_DEFERRED_SYSMEM_OPS(arcfb,
43628f57d03SThomas Zimmermann arcfb_damage_range,
43728f57d03SThomas Zimmermann arcfb_damage_area)
43828f57d03SThomas Zimmermann
4398a48ac33SJani Nikula static const struct fb_ops arcfb_ops = {
4401154ea7dSJaya Kumar .owner = THIS_MODULE,
4411154ea7dSJaya Kumar .fb_open = arcfb_open,
44228f57d03SThomas Zimmermann __FB_DEFAULT_DEFERRED_OPS_RDWR(arcfb),
4431154ea7dSJaya Kumar .fb_release = arcfb_release,
4441154ea7dSJaya Kumar .fb_pan_display = arcfb_pan_display,
44528f57d03SThomas Zimmermann __FB_DEFAULT_DEFERRED_OPS_DRAW(arcfb),
4461154ea7dSJaya Kumar .fb_ioctl = arcfb_ioctl,
44728f57d03SThomas Zimmermann // .fb_mmap reqires deferred I/O
4481154ea7dSJaya Kumar };
4491154ea7dSJaya Kumar
arcfb_probe(struct platform_device * dev)45048c68c4fSGreg Kroah-Hartman static int arcfb_probe(struct platform_device *dev)
4511154ea7dSJaya Kumar {
4521154ea7dSJaya Kumar struct fb_info *info;
4531154ea7dSJaya Kumar int retval = -ENOMEM;
4541154ea7dSJaya Kumar int videomemorysize;
4551154ea7dSJaya Kumar unsigned char *videomemory;
4561154ea7dSJaya Kumar struct arcfb_par *par;
4571154ea7dSJaya Kumar int i;
4581154ea7dSJaya Kumar
4591154ea7dSJaya Kumar videomemorysize = (((64*64)*num_cols)*num_rows)/8;
4601154ea7dSJaya Kumar
4611154ea7dSJaya Kumar /* We need a flat backing store for the Arc's
4621154ea7dSJaya Kumar less-flat actual paged framebuffer */
4631b86d775SJoe Perches videomemory = vzalloc(videomemorysize);
4641b86d775SJoe Perches if (!videomemory)
4651154ea7dSJaya Kumar return retval;
4661154ea7dSJaya Kumar
4671154ea7dSJaya Kumar info = framebuffer_alloc(sizeof(struct arcfb_par), &dev->dev);
4681154ea7dSJaya Kumar if (!info)
4695a6bef73SZongjie Li goto err_fb_alloc;
4701154ea7dSJaya Kumar
47130b72c0bSThomas Zimmermann info->flags |= FBINFO_VIRTFB;
4728112bafcSThomas Zimmermann info->screen_buffer = videomemory;
4731154ea7dSJaya Kumar info->fbops = &arcfb_ops;
4741154ea7dSJaya Kumar
4751154ea7dSJaya Kumar info->var = arcfb_var;
4761154ea7dSJaya Kumar info->fix = arcfb_fix;
4771154ea7dSJaya Kumar par = info->par;
4781154ea7dSJaya Kumar par->info = info;
4791154ea7dSJaya Kumar
4801154ea7dSJaya Kumar if (!dio_addr || !cio_addr || !c2io_addr) {
4811154ea7dSJaya Kumar printk(KERN_WARNING "no IO addresses supplied\n");
4825a6bef73SZongjie Li goto err_addr;
4831154ea7dSJaya Kumar }
4841154ea7dSJaya Kumar par->dio_addr = dio_addr;
4851154ea7dSJaya Kumar par->cio_addr = cio_addr;
4861154ea7dSJaya Kumar par->c2io_addr = c2io_addr;
4871154ea7dSJaya Kumar par->cslut[0] = 0x00;
4881154ea7dSJaya Kumar par->cslut[1] = 0x06;
4891154ea7dSJaya Kumar spin_lock_init(&par->lock);
4901154ea7dSJaya Kumar if (irq) {
4911154ea7dSJaya Kumar par->irq = irq;
49263a43399SThomas Gleixner if (request_irq(par->irq, &arcfb_interrupt, IRQF_SHARED,
4931154ea7dSJaya Kumar "arcfb", info)) {
4941154ea7dSJaya Kumar printk(KERN_INFO
4951154ea7dSJaya Kumar "arcfb: Failed req IRQ %d\n", par->irq);
496baaa15ffSPeter Senna Tschudin retval = -EBUSY;
4975a6bef73SZongjie Li goto err_addr;
4981154ea7dSJaya Kumar }
4991154ea7dSJaya Kumar }
500fbe3d80dSChuhong Yuan retval = register_framebuffer(info);
501fbe3d80dSChuhong Yuan if (retval < 0)
5025a6bef73SZongjie Li goto err_register_fb;
503fbe3d80dSChuhong Yuan platform_set_drvdata(dev, info);
50431b6780cSJoe Perches fb_info(info, "Arc frame buffer device, using %dK of video memory\n",
50531b6780cSJoe Perches videomemorysize >> 10);
5061154ea7dSJaya Kumar
5071154ea7dSJaya Kumar /* this inits the lcd but doesn't clear dirty pixels */
5081154ea7dSJaya Kumar for (i = 0; i < num_cols * num_rows; i++) {
5091154ea7dSJaya Kumar ks108_writeb_ctl(par, i, KS_DPY_OFF);
5101154ea7dSJaya Kumar ks108_set_start_line(par, i, 0);
5111154ea7dSJaya Kumar ks108_set_yaddr(par, i, 0);
5121154ea7dSJaya Kumar ks108_set_xaddr(par, i, 0);
5131154ea7dSJaya Kumar ks108_writeb_ctl(par, i, KS_DPY_ON);
5141154ea7dSJaya Kumar }
5151154ea7dSJaya Kumar
5161154ea7dSJaya Kumar /* if we were told to splash the screen, we just clear it */
5171154ea7dSJaya Kumar if (!nosplash) {
5181154ea7dSJaya Kumar for (i = 0; i < num_cols * num_rows; i++) {
51931b6780cSJoe Perches fb_info(info, "splashing lcd %d\n", i);
5201154ea7dSJaya Kumar ks108_set_start_line(par, i, 0);
5211154ea7dSJaya Kumar ks108_clear_lcd(par, i);
5221154ea7dSJaya Kumar }
5231154ea7dSJaya Kumar }
5241154ea7dSJaya Kumar
5251154ea7dSJaya Kumar return 0;
5265a6bef73SZongjie Li
5275a6bef73SZongjie Li err_register_fb:
5285a6bef73SZongjie Li free_irq(par->irq, info);
5295a6bef73SZongjie Li err_addr:
5301154ea7dSJaya Kumar framebuffer_release(info);
5315a6bef73SZongjie Li err_fb_alloc:
5321154ea7dSJaya Kumar vfree(videomemory);
5331154ea7dSJaya Kumar return retval;
5341154ea7dSJaya Kumar }
5351154ea7dSJaya Kumar
arcfb_remove(struct platform_device * dev)5364369e38aSUwe Kleine-König static void arcfb_remove(struct platform_device *dev)
5371154ea7dSJaya Kumar {
5383ae5eaecSRussell King struct fb_info *info = platform_get_drvdata(dev);
5391154ea7dSJaya Kumar
5401154ea7dSJaya Kumar if (info) {
5411154ea7dSJaya Kumar unregister_framebuffer(info);
542fbe3d80dSChuhong Yuan if (irq)
543fbe3d80dSChuhong Yuan free_irq(((struct arcfb_par *)(info->par))->irq, info);
5448112bafcSThomas Zimmermann vfree(info->screen_buffer);
5451154ea7dSJaya Kumar framebuffer_release(info);
5461154ea7dSJaya Kumar }
5471154ea7dSJaya Kumar }
5481154ea7dSJaya Kumar
5493ae5eaecSRussell King static struct platform_driver arcfb_driver = {
5501154ea7dSJaya Kumar .probe = arcfb_probe,
55101ecc142SUwe Kleine-König .remove = arcfb_remove,
5523ae5eaecSRussell King .driver = {
5533ae5eaecSRussell King .name = "arcfb",
5543ae5eaecSRussell King },
5551154ea7dSJaya Kumar };
5561154ea7dSJaya Kumar
5578d972a96SRussell King static struct platform_device *arcfb_device;
5581154ea7dSJaya Kumar
arcfb_init(void)5591154ea7dSJaya Kumar static int __init arcfb_init(void)
5601154ea7dSJaya Kumar {
5611154ea7dSJaya Kumar int ret;
5621154ea7dSJaya Kumar
5631154ea7dSJaya Kumar if (!arcfb_enable)
5641154ea7dSJaya Kumar return -ENXIO;
5651154ea7dSJaya Kumar
5663ae5eaecSRussell King ret = platform_driver_register(&arcfb_driver);
5671154ea7dSJaya Kumar if (!ret) {
5688d972a96SRussell King arcfb_device = platform_device_alloc("arcfb", 0);
5698d972a96SRussell King if (arcfb_device) {
5708d972a96SRussell King ret = platform_device_add(arcfb_device);
5718d972a96SRussell King } else {
5728d972a96SRussell King ret = -ENOMEM;
5738d972a96SRussell King }
5748d972a96SRussell King if (ret) {
5758d972a96SRussell King platform_device_put(arcfb_device);
5763ae5eaecSRussell King platform_driver_unregister(&arcfb_driver);
5771154ea7dSJaya Kumar }
5788d972a96SRussell King }
5791154ea7dSJaya Kumar return ret;
5801154ea7dSJaya Kumar
5811154ea7dSJaya Kumar }
5821154ea7dSJaya Kumar
arcfb_exit(void)5831154ea7dSJaya Kumar static void __exit arcfb_exit(void)
5841154ea7dSJaya Kumar {
5858d972a96SRussell King platform_device_unregister(arcfb_device);
5863ae5eaecSRussell King platform_driver_unregister(&arcfb_driver);
5871154ea7dSJaya Kumar }
5881154ea7dSJaya Kumar
5891154ea7dSJaya Kumar module_param(num_cols, ulong, 0);
5901154ea7dSJaya Kumar MODULE_PARM_DESC(num_cols, "Num horiz panels, eg: 2 = 128 bit wide");
5911154ea7dSJaya Kumar module_param(num_rows, ulong, 0);
5921154ea7dSJaya Kumar MODULE_PARM_DESC(num_rows, "Num vert panels, eg: 1 = 64 bit high");
5931154ea7dSJaya Kumar module_param(nosplash, uint, 0);
5941154ea7dSJaya Kumar MODULE_PARM_DESC(nosplash, "Disable doing the splash screen");
5951154ea7dSJaya Kumar module_param(arcfb_enable, uint, 0);
5961154ea7dSJaya Kumar MODULE_PARM_DESC(arcfb_enable, "Enable communication with Arc board");
597c729203dSDavid Howells module_param_hw(dio_addr, ulong, ioport, 0);
5981154ea7dSJaya Kumar MODULE_PARM_DESC(dio_addr, "IO address for data, eg: 0x480");
599c729203dSDavid Howells module_param_hw(cio_addr, ulong, ioport, 0);
6001154ea7dSJaya Kumar MODULE_PARM_DESC(cio_addr, "IO address for control, eg: 0x400");
601c729203dSDavid Howells module_param_hw(c2io_addr, ulong, ioport, 0);
6021154ea7dSJaya Kumar MODULE_PARM_DESC(c2io_addr, "IO address for secondary control, eg: 0x408");
6031154ea7dSJaya Kumar module_param(splashval, ulong, 0);
6041154ea7dSJaya Kumar MODULE_PARM_DESC(splashval, "Splash pattern: 0xFF is black, 0x00 is green");
6051154ea7dSJaya Kumar module_param(tuhold, ulong, 0);
6061154ea7dSJaya Kumar MODULE_PARM_DESC(tuhold, "Time to hold between strobing data to Arc board");
607c729203dSDavid Howells module_param_hw(irq, uint, irq, 0);
6081154ea7dSJaya Kumar MODULE_PARM_DESC(irq, "IRQ for the Arc board");
6091154ea7dSJaya Kumar
6101154ea7dSJaya Kumar module_init(arcfb_init);
6111154ea7dSJaya Kumar module_exit(arcfb_exit);
6121154ea7dSJaya Kumar
6131154ea7dSJaya Kumar MODULE_DESCRIPTION("fbdev driver for Arc monochrome LCD board");
6141154ea7dSJaya Kumar MODULE_AUTHOR("Jaya Kumar");
6151154ea7dSJaya Kumar MODULE_LICENSE("GPL");
6161154ea7dSJaya Kumar
617