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