xref: /linux/drivers/video/fbdev/arcfb.c (revision c771600c6af14749609b49565ffb4cac2959710d)
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