Orange Pi5 kernel

Deprecated Linux kernel 5.10.110 for OrangePi 5/5B/5+ boards

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