brintos

brintos / linux-shallow public Read only

0
0
Text · 15.4 KiB · b807cf0 Raw
617 lines · c
1/*2 * linux/drivers/video/arcfb.c -- FB driver for Arc monochrome LCD board3 *4 * Copyright (C) 2005, Jaya Kumar <jayalk@intworks.biz>5 *6 * This file is subject to the terms and conditions of the GNU General Public7 * License. See the file COPYING in the main directory of this archive for8 * more details.9 *10 * Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven.11 *12 * This driver was written to be used with the Arc LCD board. Arc uses a13 * set of KS108 chips that control individual 64x64 LCD matrices. The board14 * can be paneled in a variety of setups such as 2x1=128x64, 4x4=256x256 and15 * so on. The interface between the board and the host is TTL based GPIO. The16 * GPIO requirements are 8 writable data lines and 4+n lines for control. On a17 * GPIO-less system, the board can be tested by connecting the respective sigs18 * up to a parallel port connector. The driver requires the IO addresses for19 * data and control GPIO at load time. It is unable to probe for the20 * existence of the LCD so it must be told at load time whether it should21 * be enabled or not.22 *23 * Todo:24 * - testing with 4x425 * - testing with interrupt hw26 *27 * General notes:28 * - User must set tuhold. It's in microseconds. According to the 108 spec,29 *   the hold time is supposed to be at least 1 microsecond.30 * - User must set num_cols=x num_rows=y, eg: x=2 means 12831 * - User must set arcfb_enable=1 to enable it32 * - User must set dio_addr=0xIOADDR cio_addr=0xIOADDR33 *34 */35 36#include <linux/module.h>37#include <linux/kernel.h>38#include <linux/errno.h>39#include <linux/string.h>40#include <linux/mm.h>41#include <linux/vmalloc.h>42#include <linux/delay.h>43#include <linux/interrupt.h>44#include <linux/io.h>45#include <linux/fb.h>46#include <linux/init.h>47#include <linux/arcfb.h>48#include <linux/platform_device.h>49 50#include <linux/uaccess.h>51 52#define floor8(a) (a&(~0x07))53#define floorXres(a,xres) (a&(~(xres - 1)))54#define iceil8(a) (((int)((a+7)/8))*8)55#define ceil64(a) (a|0x3F)56#define ceilXres(a,xres) (a|(xres - 1))57 58/* ks108 chipset specific defines and code */59 60#define KS_SET_DPY_START_LINE 	0xC061#define KS_SET_PAGE_NUM 	0xB862#define KS_SET_X 		0x4063#define KS_CEHI 		0x0164#define KS_CELO 		0x0065#define KS_SEL_CMD 		0x0866#define KS_SEL_DATA 		0x0067#define KS_DPY_ON 		0x3F68#define KS_DPY_OFF 		0x3E69#define KS_INTACK 		0x4070#define KS_CLRINT		0x0271 72struct arcfb_par {73	unsigned long dio_addr;74	unsigned long cio_addr;75	unsigned long c2io_addr;76	atomic_t ref_count;77	unsigned char cslut[9];78	struct fb_info *info;79	unsigned int irq;80	spinlock_t lock;81};82 83static const struct fb_fix_screeninfo arcfb_fix = {84	.id =		"arcfb",85	.type =		FB_TYPE_PACKED_PIXELS,86	.visual =	FB_VISUAL_MONO01,87	.xpanstep =	0,88	.ypanstep =	1,89	.ywrapstep =	0,90	.accel =	FB_ACCEL_NONE,91};92 93static const struct fb_var_screeninfo arcfb_var = {94	.xres		= 128,95	.yres		= 64,96	.xres_virtual	= 128,97	.yres_virtual	= 64,98	.bits_per_pixel	= 1,99	.nonstd		= 1,100};101 102static unsigned long num_cols;103static unsigned long num_rows;104static unsigned long dio_addr;105static unsigned long cio_addr;106static unsigned long c2io_addr;107static unsigned long splashval;108static unsigned long tuhold;109static unsigned int nosplash;110static unsigned int arcfb_enable;111static unsigned int irq;112 113static DECLARE_WAIT_QUEUE_HEAD(arcfb_waitq);114 115static void ks108_writeb_ctl(struct arcfb_par *par,116				unsigned int chipindex, unsigned char value)117{118	unsigned char chipselval = par->cslut[chipindex];119 120	outb(chipselval|KS_CEHI|KS_SEL_CMD, par->cio_addr);121	outb(value, par->dio_addr);122	udelay(tuhold);123	outb(chipselval|KS_CELO|KS_SEL_CMD, par->cio_addr);124}125 126static void ks108_writeb_mainctl(struct arcfb_par *par, unsigned char value)127{128 129	outb(value, par->cio_addr);130	udelay(tuhold);131}132 133static unsigned char ks108_readb_ctl2(struct arcfb_par *par)134{135	return inb(par->c2io_addr);136}137 138static void ks108_writeb_data(struct arcfb_par *par,139				unsigned int chipindex, unsigned char value)140{141	unsigned char chipselval = par->cslut[chipindex];142 143	outb(chipselval|KS_CEHI|KS_SEL_DATA, par->cio_addr);144	outb(value, par->dio_addr);145	udelay(tuhold);146	outb(chipselval|KS_CELO|KS_SEL_DATA, par->cio_addr);147}148 149static void ks108_set_start_line(struct arcfb_par *par,150				unsigned int chipindex, unsigned char y)151{152	ks108_writeb_ctl(par, chipindex, KS_SET_DPY_START_LINE|y);153}154 155static void ks108_set_yaddr(struct arcfb_par *par,156				unsigned int chipindex, unsigned char y)157{158	ks108_writeb_ctl(par, chipindex, KS_SET_PAGE_NUM|y);159}160 161static void ks108_set_xaddr(struct arcfb_par *par,162				unsigned int chipindex, unsigned char x)163{164	ks108_writeb_ctl(par, chipindex, KS_SET_X|x);165}166 167static void ks108_clear_lcd(struct arcfb_par *par, unsigned int chipindex)168{169	int i,j;170 171	for (i = 0; i <= 8; i++) {172		ks108_set_yaddr(par, chipindex, i);173		ks108_set_xaddr(par, chipindex, 0);174		for (j = 0; j < 64; j++) {175			ks108_writeb_data(par, chipindex,176				(unsigned char) splashval);177		}178	}179}180 181/* main arcfb functions */182 183static int arcfb_open(struct fb_info *info, int user)184{185	struct arcfb_par *par = info->par;186 187	atomic_inc(&par->ref_count);188	return 0;189}190 191static int arcfb_release(struct fb_info *info, int user)192{193	struct arcfb_par *par = info->par;194	int count = atomic_read(&par->ref_count);195 196	if (!count)197		return -EINVAL;198	atomic_dec(&par->ref_count);199	return 0;200}201 202static int arcfb_pan_display(struct fb_var_screeninfo *var,203				struct fb_info *info)204{205	int i;206	struct arcfb_par *par = info->par;207 208	if ((var->vmode & FB_VMODE_YWRAP) && (var->yoffset < 64)209		&& (info->var.yres <= 64)) {210		for (i = 0; i < num_cols; i++) {211			ks108_set_start_line(par, i, var->yoffset);212		}213		info->var.yoffset = var->yoffset;214		return 0;215	}216 217	return -EINVAL;218}219 220static irqreturn_t arcfb_interrupt(int vec, void *dev_instance)221{222	struct fb_info *info = dev_instance;223	unsigned char ctl2status;224	struct arcfb_par *par = info->par;225 226	ctl2status = ks108_readb_ctl2(par);227 228	if (!(ctl2status & KS_INTACK)) /* not arc generated interrupt */229		return IRQ_NONE;230 231	ks108_writeb_mainctl(par, KS_CLRINT);232 233	spin_lock(&par->lock);234        if (waitqueue_active(&arcfb_waitq)) {235                wake_up(&arcfb_waitq);236        }237	spin_unlock(&par->lock);238 239	return IRQ_HANDLED;240}241 242/*243 * here we handle a specific page on the lcd. the complexity comes from244 * the fact that the fb is laidout in 8xX vertical columns. we extract245 * each write of 8 vertical pixels. then we shift out as we move along246 * X. That's what rightshift does. bitmask selects the desired input bit.247 */248static void arcfb_lcd_update_page(struct arcfb_par *par, unsigned int upper,249		unsigned int left, unsigned int right, unsigned int distance)250{251	unsigned char *src;252	unsigned int xindex, yindex, chipindex, linesize;253	int i;254	unsigned char val;255	unsigned char bitmask, rightshift;256 257	xindex = left >> 6;258	yindex = upper >> 6;259	chipindex = (xindex + (yindex*num_cols));260 261	ks108_set_yaddr(par, chipindex, upper/8);262 263	linesize = par->info->var.xres/8;264	src = (unsigned char *)par->info->screen_buffer + (left/8) +265		(upper * linesize);266	ks108_set_xaddr(par, chipindex, left);267 268	bitmask=1;269	rightshift=0;270	while (left <= right) {271		val = 0;272		for (i = 0; i < 8; i++) {273			if ( i > rightshift) {274				val |= (*(src + (i*linesize)) & bitmask)275						<< (i - rightshift);276			} else {277				val |= (*(src + (i*linesize)) & bitmask)278						 >> (rightshift - i);279			}280		}281		ks108_writeb_data(par, chipindex, val);282		left++;283		if (bitmask == 0x80) {284			bitmask = 1;285			src++;286			rightshift=0;287		} else {288			bitmask <<= 1;289			rightshift++;290		}291	}292}293 294/*295 * here we handle the entire vertical page of the update. we write across296 * lcd chips. update_page uses the upper/left values to decide which297 * chip to select for the right. upper is needed for setting the page298 * desired for the write.299 */300static void arcfb_lcd_update_vert(struct arcfb_par *par, unsigned int top,301		unsigned int bottom, unsigned int left, unsigned int right)302{303	unsigned int distance, upper, lower;304 305	distance = (bottom - top) + 1;306	upper = top;307	lower = top + 7;308 309	while (distance > 0) {310		distance -= 8;311		arcfb_lcd_update_page(par, upper, left, right, 8);312		upper = lower + 1;313		lower = upper + 7;314	}315}316 317/*318 * here we handle horizontal blocks for the update. update_vert will319 * handle spaning multiple pages. we break out each horizontal320 * block in to individual blocks no taller than 64 pixels.321 */322static void arcfb_lcd_update_horiz(struct arcfb_par *par, unsigned int left,323			unsigned int right, unsigned int top, unsigned int h)324{325	unsigned int distance, upper, lower;326 327	distance = h;328	upper = floor8(top);329	lower = min(upper + distance - 1, ceil64(upper));330 331	while (distance > 0) {332		distance -= ((lower - upper) + 1 );333		arcfb_lcd_update_vert(par, upper, lower, left, right);334		upper = lower + 1;335		lower = min(upper + distance - 1, ceil64(upper));336	}337}338 339/*340 * here we start the process of splitting out the fb update into341 * individual blocks of pixels. we end up splitting into 64x64 blocks342 * and finally down to 64x8 pages.343 */344static void arcfb_lcd_update(struct arcfb_par *par, unsigned int dx,345			unsigned int dy, unsigned int w, unsigned int h)346{347	unsigned int left, right, distance, y;348 349	/* align the request first */350	y = floor8(dy);351	h += dy - y;352	h = iceil8(h);353 354	distance = w;355	left = dx;356	right = min(left + w - 1, ceil64(left));357 358	while (distance > 0) {359		arcfb_lcd_update_horiz(par, left, right, y, h);360		distance -= ((right - left) + 1);361		left = right + 1;362		right = min(left + distance - 1, ceil64(left));363	}364}365 366static int arcfb_ioctl(struct fb_info *info,367			  unsigned int cmd, unsigned long arg)368{369	void __user *argp = (void __user *)arg;370	struct arcfb_par *par = info->par;371	unsigned long flags;372 373	switch (cmd) {374		case FBIO_WAITEVENT:375		{376			DEFINE_WAIT(wait);377			/* illegal to wait on arc if no irq will occur */378			if (!par->irq)379				return -EINVAL;380 381			/* wait until the Arc has generated an interrupt382			 * which will wake us up */383			spin_lock_irqsave(&par->lock, flags);384			prepare_to_wait(&arcfb_waitq, &wait,385					TASK_INTERRUPTIBLE);386			spin_unlock_irqrestore(&par->lock, flags);387			schedule();388			finish_wait(&arcfb_waitq, &wait);389		}390		fallthrough;391 392		case FBIO_GETCONTROL2:393		{394			unsigned char ctl2;395 396			ctl2 = ks108_readb_ctl2(info->par);397			if (copy_to_user(argp, &ctl2, sizeof(ctl2)))398				return -EFAULT;399			return 0;400		}401		default:402			return -EINVAL;403	}404}405 406static void arcfb_damage_range(struct fb_info *info, off_t off, size_t len)407{408	struct arcfb_par *par = info->par;409	unsigned int xres = info->var.xres;410	unsigned int bitppos, startpos, endpos, bitcount;411	unsigned int x, y, width, height;412 413	bitppos = off * 8;414	startpos = floorXres(bitppos, xres);415	endpos = ceilXres((bitppos + (len * 8)), xres);416	bitcount = endpos - startpos;417 418	x = startpos % xres;419	y = startpos / xres;420	width = xres;421	height = bitcount / xres;422 423	arcfb_lcd_update(par, x, y, width, height);424}425 426static void arcfb_damage_area(struct fb_info *info, u32 x, u32 y,427			      u32 width, u32 height)428{429	struct arcfb_par *par = info->par;430 431	/* update the physical lcd */432	arcfb_lcd_update(par, x, y, width, height);433}434 435FB_GEN_DEFAULT_DEFERRED_SYSMEM_OPS(arcfb,436				   arcfb_damage_range,437				   arcfb_damage_area)438 439static const struct fb_ops arcfb_ops = {440	.owner		= THIS_MODULE,441	.fb_open	= arcfb_open,442	__FB_DEFAULT_DEFERRED_OPS_RDWR(arcfb),443	.fb_release	= arcfb_release,444	.fb_pan_display	= arcfb_pan_display,445	__FB_DEFAULT_DEFERRED_OPS_DRAW(arcfb),446	.fb_ioctl 	= arcfb_ioctl,447	// .fb_mmap reqires deferred I/O448};449 450static int arcfb_probe(struct platform_device *dev)451{452	struct fb_info *info;453	int retval = -ENOMEM;454	int videomemorysize;455	unsigned char *videomemory;456	struct arcfb_par *par;457	int i;458 459	videomemorysize = (((64*64)*num_cols)*num_rows)/8;460 461	/* We need a flat backing store for the Arc's462	   less-flat actual paged framebuffer */463	videomemory = vzalloc(videomemorysize);464	if (!videomemory)465		return retval;466 467	info = framebuffer_alloc(sizeof(struct arcfb_par), &dev->dev);468	if (!info)469		goto err_fb_alloc;470 471	info->flags |= FBINFO_VIRTFB;472	info->screen_buffer = videomemory;473	info->fbops = &arcfb_ops;474 475	info->var = arcfb_var;476	info->fix = arcfb_fix;477	par = info->par;478	par->info = info;479 480	if (!dio_addr || !cio_addr || !c2io_addr) {481		printk(KERN_WARNING "no IO addresses supplied\n");482		goto err_addr;483	}484	par->dio_addr = dio_addr;485	par->cio_addr = cio_addr;486	par->c2io_addr = c2io_addr;487	par->cslut[0] = 0x00;488	par->cslut[1] = 0x06;489	spin_lock_init(&par->lock);490	if (irq) {491		par->irq = irq;492		if (request_irq(par->irq, &arcfb_interrupt, IRQF_SHARED,493				"arcfb", info)) {494			printk(KERN_INFO495				"arcfb: Failed req IRQ %d\n", par->irq);496			retval = -EBUSY;497			goto err_addr;498		}499	}500	retval = register_framebuffer(info);501	if (retval < 0)502		goto err_register_fb;503	platform_set_drvdata(dev, info);504	fb_info(info, "Arc frame buffer device, using %dK of video memory\n",505		videomemorysize >> 10);506 507	/* this inits the lcd but doesn't clear dirty pixels */508	for (i = 0; i < num_cols * num_rows; i++) {509		ks108_writeb_ctl(par, i, KS_DPY_OFF);510		ks108_set_start_line(par, i, 0);511		ks108_set_yaddr(par, i, 0);512		ks108_set_xaddr(par, i, 0);513		ks108_writeb_ctl(par, i, KS_DPY_ON);514	}515 516	/* if we were told to splash the screen, we just clear it */517	if (!nosplash) {518		for (i = 0; i < num_cols * num_rows; i++) {519			fb_info(info, "splashing lcd %d\n", i);520			ks108_set_start_line(par, i, 0);521			ks108_clear_lcd(par, i);522		}523	}524 525	return 0;526 527err_register_fb:528	free_irq(par->irq, info);529err_addr:530	framebuffer_release(info);531err_fb_alloc:532	vfree(videomemory);533	return retval;534}535 536static void arcfb_remove(struct platform_device *dev)537{538	struct fb_info *info = platform_get_drvdata(dev);539 540	if (info) {541		unregister_framebuffer(info);542		if (irq)543			free_irq(((struct arcfb_par *)(info->par))->irq, info);544		vfree(info->screen_buffer);545		framebuffer_release(info);546	}547}548 549static struct platform_driver arcfb_driver = {550	.probe	= arcfb_probe,551	.remove	= arcfb_remove,552	.driver	= {553		.name	= "arcfb",554	},555};556 557static struct platform_device *arcfb_device;558 559static int __init arcfb_init(void)560{561	int ret;562 563	if (!arcfb_enable)564		return -ENXIO;565 566	ret = platform_driver_register(&arcfb_driver);567	if (!ret) {568		arcfb_device = platform_device_alloc("arcfb", 0);569		if (arcfb_device) {570			ret = platform_device_add(arcfb_device);571		} else {572			ret = -ENOMEM;573		}574		if (ret) {575			platform_device_put(arcfb_device);576			platform_driver_unregister(&arcfb_driver);577		}578	}579	return ret;580 581}582 583static void __exit arcfb_exit(void)584{585	platform_device_unregister(arcfb_device);586	platform_driver_unregister(&arcfb_driver);587}588 589module_param(num_cols, ulong, 0);590MODULE_PARM_DESC(num_cols, "Num horiz panels, eg: 2 = 128 bit wide");591module_param(num_rows, ulong, 0);592MODULE_PARM_DESC(num_rows, "Num vert panels, eg: 1 = 64 bit high");593module_param(nosplash, uint, 0);594MODULE_PARM_DESC(nosplash, "Disable doing the splash screen");595module_param(arcfb_enable, uint, 0);596MODULE_PARM_DESC(arcfb_enable, "Enable communication with Arc board");597module_param_hw(dio_addr, ulong, ioport, 0);598MODULE_PARM_DESC(dio_addr, "IO address for data, eg: 0x480");599module_param_hw(cio_addr, ulong, ioport, 0);600MODULE_PARM_DESC(cio_addr, "IO address for control, eg: 0x400");601module_param_hw(c2io_addr, ulong, ioport, 0);602MODULE_PARM_DESC(c2io_addr, "IO address for secondary control, eg: 0x408");603module_param(splashval, ulong, 0);604MODULE_PARM_DESC(splashval, "Splash pattern: 0xFF is black, 0x00 is green");605module_param(tuhold, ulong, 0);606MODULE_PARM_DESC(tuhold, "Time to hold between strobing data to Arc board");607module_param_hw(irq, uint, irq, 0);608MODULE_PARM_DESC(irq, "IRQ for the Arc board");609 610module_init(arcfb_init);611module_exit(arcfb_exit);612 613MODULE_DESCRIPTION("fbdev driver for Arc monochrome LCD board");614MODULE_AUTHOR("Jaya Kumar");615MODULE_LICENSE("GPL");616 617