1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0+
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Front panel driver for Linux
4*4882a593Smuzhiyun * Copyright (C) 2000-2008, Willy Tarreau <w@1wt.eu>
5*4882a593Smuzhiyun * Copyright (C) 2016-2017 Glider bvba
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * This code drives an LCD module (/dev/lcd), and a keypad (/dev/keypad)
8*4882a593Smuzhiyun * connected to a parallel printer port.
9*4882a593Smuzhiyun *
10*4882a593Smuzhiyun * The LCD module may either be an HD44780-like 8-bit parallel LCD, or a 1-bit
11*4882a593Smuzhiyun * serial module compatible with Samsung's KS0074. The pins may be connected in
12*4882a593Smuzhiyun * any combination, everything is programmable.
13*4882a593Smuzhiyun *
14*4882a593Smuzhiyun * The keypad consists in a matrix of push buttons connecting input pins to
15*4882a593Smuzhiyun * data output pins or to the ground. The combinations have to be hard-coded
16*4882a593Smuzhiyun * in the driver, though several profiles exist and adding new ones is easy.
17*4882a593Smuzhiyun *
18*4882a593Smuzhiyun * Several profiles are provided for commonly found LCD+keypad modules on the
19*4882a593Smuzhiyun * market, such as those found in Nexcom's appliances.
20*4882a593Smuzhiyun *
21*4882a593Smuzhiyun * FIXME:
22*4882a593Smuzhiyun * - the initialization/deinitialization process is very dirty and should
23*4882a593Smuzhiyun * be rewritten. It may even be buggy.
24*4882a593Smuzhiyun *
25*4882a593Smuzhiyun * TODO:
26*4882a593Smuzhiyun * - document 24 keys keyboard (3 rows of 8 cols, 32 diodes + 2 inputs)
27*4882a593Smuzhiyun * - make the LCD a part of a virtual screen of Vx*Vy
28*4882a593Smuzhiyun * - make the inputs list smp-safe
29*4882a593Smuzhiyun * - change the keyboard to a double mapping : signals -> key_id -> values
30*4882a593Smuzhiyun * so that applications can change values without knowing signals
31*4882a593Smuzhiyun *
32*4882a593Smuzhiyun */
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun #include <linux/module.h>
37*4882a593Smuzhiyun
38*4882a593Smuzhiyun #include <linux/types.h>
39*4882a593Smuzhiyun #include <linux/errno.h>
40*4882a593Smuzhiyun #include <linux/signal.h>
41*4882a593Smuzhiyun #include <linux/sched.h>
42*4882a593Smuzhiyun #include <linux/spinlock.h>
43*4882a593Smuzhiyun #include <linux/interrupt.h>
44*4882a593Smuzhiyun #include <linux/miscdevice.h>
45*4882a593Smuzhiyun #include <linux/slab.h>
46*4882a593Smuzhiyun #include <linux/ioport.h>
47*4882a593Smuzhiyun #include <linux/fcntl.h>
48*4882a593Smuzhiyun #include <linux/init.h>
49*4882a593Smuzhiyun #include <linux/delay.h>
50*4882a593Smuzhiyun #include <linux/kernel.h>
51*4882a593Smuzhiyun #include <linux/ctype.h>
52*4882a593Smuzhiyun #include <linux/parport.h>
53*4882a593Smuzhiyun #include <linux/list.h>
54*4882a593Smuzhiyun
55*4882a593Smuzhiyun #include <linux/io.h>
56*4882a593Smuzhiyun #include <linux/uaccess.h>
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun #include "charlcd.h"
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun #define LCD_MAXBYTES 256 /* max burst write */
61*4882a593Smuzhiyun
62*4882a593Smuzhiyun #define KEYPAD_BUFFER 64
63*4882a593Smuzhiyun
64*4882a593Smuzhiyun /* poll the keyboard this every second */
65*4882a593Smuzhiyun #define INPUT_POLL_TIME (HZ / 50)
66*4882a593Smuzhiyun /* a key starts to repeat after this times INPUT_POLL_TIME */
67*4882a593Smuzhiyun #define KEYPAD_REP_START (10)
68*4882a593Smuzhiyun /* a key repeats this times INPUT_POLL_TIME */
69*4882a593Smuzhiyun #define KEYPAD_REP_DELAY (2)
70*4882a593Smuzhiyun
71*4882a593Smuzhiyun /* converts an r_str() input to an active high, bits string : 000BAOSE */
72*4882a593Smuzhiyun #define PNL_PINPUT(a) ((((unsigned char)(a)) ^ 0x7F) >> 3)
73*4882a593Smuzhiyun
74*4882a593Smuzhiyun #define PNL_PBUSY 0x80 /* inverted input, active low */
75*4882a593Smuzhiyun #define PNL_PACK 0x40 /* direct input, active low */
76*4882a593Smuzhiyun #define PNL_POUTPA 0x20 /* direct input, active high */
77*4882a593Smuzhiyun #define PNL_PSELECD 0x10 /* direct input, active high */
78*4882a593Smuzhiyun #define PNL_PERRORP 0x08 /* direct input, active low */
79*4882a593Smuzhiyun
80*4882a593Smuzhiyun #define PNL_PBIDIR 0x20 /* bi-directional ports */
81*4882a593Smuzhiyun /* high to read data in or-ed with data out */
82*4882a593Smuzhiyun #define PNL_PINTEN 0x10
83*4882a593Smuzhiyun #define PNL_PSELECP 0x08 /* inverted output, active low */
84*4882a593Smuzhiyun #define PNL_PINITP 0x04 /* direct output, active low */
85*4882a593Smuzhiyun #define PNL_PAUTOLF 0x02 /* inverted output, active low */
86*4882a593Smuzhiyun #define PNL_PSTROBE 0x01 /* inverted output */
87*4882a593Smuzhiyun
88*4882a593Smuzhiyun #define PNL_PD0 0x01
89*4882a593Smuzhiyun #define PNL_PD1 0x02
90*4882a593Smuzhiyun #define PNL_PD2 0x04
91*4882a593Smuzhiyun #define PNL_PD3 0x08
92*4882a593Smuzhiyun #define PNL_PD4 0x10
93*4882a593Smuzhiyun #define PNL_PD5 0x20
94*4882a593Smuzhiyun #define PNL_PD6 0x40
95*4882a593Smuzhiyun #define PNL_PD7 0x80
96*4882a593Smuzhiyun
97*4882a593Smuzhiyun #define PIN_NONE 0
98*4882a593Smuzhiyun #define PIN_STROBE 1
99*4882a593Smuzhiyun #define PIN_D0 2
100*4882a593Smuzhiyun #define PIN_D1 3
101*4882a593Smuzhiyun #define PIN_D2 4
102*4882a593Smuzhiyun #define PIN_D3 5
103*4882a593Smuzhiyun #define PIN_D4 6
104*4882a593Smuzhiyun #define PIN_D5 7
105*4882a593Smuzhiyun #define PIN_D6 8
106*4882a593Smuzhiyun #define PIN_D7 9
107*4882a593Smuzhiyun #define PIN_AUTOLF 14
108*4882a593Smuzhiyun #define PIN_INITP 16
109*4882a593Smuzhiyun #define PIN_SELECP 17
110*4882a593Smuzhiyun #define PIN_NOT_SET 127
111*4882a593Smuzhiyun
112*4882a593Smuzhiyun #define NOT_SET -1
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun /* macros to simplify use of the parallel port */
115*4882a593Smuzhiyun #define r_ctr(x) (parport_read_control((x)->port))
116*4882a593Smuzhiyun #define r_dtr(x) (parport_read_data((x)->port))
117*4882a593Smuzhiyun #define r_str(x) (parport_read_status((x)->port))
118*4882a593Smuzhiyun #define w_ctr(x, y) (parport_write_control((x)->port, (y)))
119*4882a593Smuzhiyun #define w_dtr(x, y) (parport_write_data((x)->port, (y)))
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun /* this defines which bits are to be used and which ones to be ignored */
122*4882a593Smuzhiyun /* logical or of the output bits involved in the scan matrix */
123*4882a593Smuzhiyun static __u8 scan_mask_o;
124*4882a593Smuzhiyun /* logical or of the input bits involved in the scan matrix */
125*4882a593Smuzhiyun static __u8 scan_mask_i;
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun enum input_type {
128*4882a593Smuzhiyun INPUT_TYPE_STD,
129*4882a593Smuzhiyun INPUT_TYPE_KBD,
130*4882a593Smuzhiyun };
131*4882a593Smuzhiyun
132*4882a593Smuzhiyun enum input_state {
133*4882a593Smuzhiyun INPUT_ST_LOW,
134*4882a593Smuzhiyun INPUT_ST_RISING,
135*4882a593Smuzhiyun INPUT_ST_HIGH,
136*4882a593Smuzhiyun INPUT_ST_FALLING,
137*4882a593Smuzhiyun };
138*4882a593Smuzhiyun
139*4882a593Smuzhiyun struct logical_input {
140*4882a593Smuzhiyun struct list_head list;
141*4882a593Smuzhiyun __u64 mask;
142*4882a593Smuzhiyun __u64 value;
143*4882a593Smuzhiyun enum input_type type;
144*4882a593Smuzhiyun enum input_state state;
145*4882a593Smuzhiyun __u8 rise_time, fall_time;
146*4882a593Smuzhiyun __u8 rise_timer, fall_timer, high_timer;
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun union {
149*4882a593Smuzhiyun struct { /* valid when type == INPUT_TYPE_STD */
150*4882a593Smuzhiyun void (*press_fct)(int);
151*4882a593Smuzhiyun void (*release_fct)(int);
152*4882a593Smuzhiyun int press_data;
153*4882a593Smuzhiyun int release_data;
154*4882a593Smuzhiyun } std;
155*4882a593Smuzhiyun struct { /* valid when type == INPUT_TYPE_KBD */
156*4882a593Smuzhiyun char press_str[sizeof(void *) + sizeof(int)] __nonstring;
157*4882a593Smuzhiyun char repeat_str[sizeof(void *) + sizeof(int)] __nonstring;
158*4882a593Smuzhiyun char release_str[sizeof(void *) + sizeof(int)] __nonstring;
159*4882a593Smuzhiyun } kbd;
160*4882a593Smuzhiyun } u;
161*4882a593Smuzhiyun };
162*4882a593Smuzhiyun
163*4882a593Smuzhiyun static LIST_HEAD(logical_inputs); /* list of all defined logical inputs */
164*4882a593Smuzhiyun
165*4882a593Smuzhiyun /* physical contacts history
166*4882a593Smuzhiyun * Physical contacts are a 45 bits string of 9 groups of 5 bits each.
167*4882a593Smuzhiyun * The 8 lower groups correspond to output bits 0 to 7, and the 9th group
168*4882a593Smuzhiyun * corresponds to the ground.
169*4882a593Smuzhiyun * Within each group, bits are stored in the same order as read on the port :
170*4882a593Smuzhiyun * BAPSE (busy=4, ack=3, paper empty=2, select=1, error=0).
171*4882a593Smuzhiyun * So, each __u64 is represented like this :
172*4882a593Smuzhiyun * 0000000000000000000BAPSEBAPSEBAPSEBAPSEBAPSEBAPSEBAPSEBAPSEBAPSE
173*4882a593Smuzhiyun * <-----unused------><gnd><d07><d06><d05><d04><d03><d02><d01><d00>
174*4882a593Smuzhiyun */
175*4882a593Smuzhiyun
176*4882a593Smuzhiyun /* what has just been read from the I/O ports */
177*4882a593Smuzhiyun static __u64 phys_read;
178*4882a593Smuzhiyun /* previous phys_read */
179*4882a593Smuzhiyun static __u64 phys_read_prev;
180*4882a593Smuzhiyun /* stabilized phys_read (phys_read|phys_read_prev) */
181*4882a593Smuzhiyun static __u64 phys_curr;
182*4882a593Smuzhiyun /* previous phys_curr */
183*4882a593Smuzhiyun static __u64 phys_prev;
184*4882a593Smuzhiyun /* 0 means that at least one logical signal needs be computed */
185*4882a593Smuzhiyun static char inputs_stable;
186*4882a593Smuzhiyun
187*4882a593Smuzhiyun /* these variables are specific to the keypad */
188*4882a593Smuzhiyun static struct {
189*4882a593Smuzhiyun bool enabled;
190*4882a593Smuzhiyun } keypad;
191*4882a593Smuzhiyun
192*4882a593Smuzhiyun static char keypad_buffer[KEYPAD_BUFFER];
193*4882a593Smuzhiyun static int keypad_buflen;
194*4882a593Smuzhiyun static int keypad_start;
195*4882a593Smuzhiyun static char keypressed;
196*4882a593Smuzhiyun static wait_queue_head_t keypad_read_wait;
197*4882a593Smuzhiyun
198*4882a593Smuzhiyun /* lcd-specific variables */
199*4882a593Smuzhiyun static struct {
200*4882a593Smuzhiyun bool enabled;
201*4882a593Smuzhiyun bool initialized;
202*4882a593Smuzhiyun
203*4882a593Smuzhiyun int charset;
204*4882a593Smuzhiyun int proto;
205*4882a593Smuzhiyun
206*4882a593Smuzhiyun /* TODO: use union here? */
207*4882a593Smuzhiyun struct {
208*4882a593Smuzhiyun int e;
209*4882a593Smuzhiyun int rs;
210*4882a593Smuzhiyun int rw;
211*4882a593Smuzhiyun int cl;
212*4882a593Smuzhiyun int da;
213*4882a593Smuzhiyun int bl;
214*4882a593Smuzhiyun } pins;
215*4882a593Smuzhiyun
216*4882a593Smuzhiyun struct charlcd *charlcd;
217*4882a593Smuzhiyun } lcd;
218*4882a593Smuzhiyun
219*4882a593Smuzhiyun /* Needed only for init */
220*4882a593Smuzhiyun static int selected_lcd_type = NOT_SET;
221*4882a593Smuzhiyun
222*4882a593Smuzhiyun /*
223*4882a593Smuzhiyun * Bit masks to convert LCD signals to parallel port outputs.
224*4882a593Smuzhiyun * _d_ are values for data port, _c_ are for control port.
225*4882a593Smuzhiyun * [0] = signal OFF, [1] = signal ON, [2] = mask
226*4882a593Smuzhiyun */
227*4882a593Smuzhiyun #define BIT_CLR 0
228*4882a593Smuzhiyun #define BIT_SET 1
229*4882a593Smuzhiyun #define BIT_MSK 2
230*4882a593Smuzhiyun #define BIT_STATES 3
231*4882a593Smuzhiyun /*
232*4882a593Smuzhiyun * one entry for each bit on the LCD
233*4882a593Smuzhiyun */
234*4882a593Smuzhiyun #define LCD_BIT_E 0
235*4882a593Smuzhiyun #define LCD_BIT_RS 1
236*4882a593Smuzhiyun #define LCD_BIT_RW 2
237*4882a593Smuzhiyun #define LCD_BIT_BL 3
238*4882a593Smuzhiyun #define LCD_BIT_CL 4
239*4882a593Smuzhiyun #define LCD_BIT_DA 5
240*4882a593Smuzhiyun #define LCD_BITS 6
241*4882a593Smuzhiyun
242*4882a593Smuzhiyun /*
243*4882a593Smuzhiyun * each bit can be either connected to a DATA or CTRL port
244*4882a593Smuzhiyun */
245*4882a593Smuzhiyun #define LCD_PORT_C 0
246*4882a593Smuzhiyun #define LCD_PORT_D 1
247*4882a593Smuzhiyun #define LCD_PORTS 2
248*4882a593Smuzhiyun
249*4882a593Smuzhiyun static unsigned char lcd_bits[LCD_PORTS][LCD_BITS][BIT_STATES];
250*4882a593Smuzhiyun
251*4882a593Smuzhiyun /*
252*4882a593Smuzhiyun * LCD protocols
253*4882a593Smuzhiyun */
254*4882a593Smuzhiyun #define LCD_PROTO_PARALLEL 0
255*4882a593Smuzhiyun #define LCD_PROTO_SERIAL 1
256*4882a593Smuzhiyun #define LCD_PROTO_TI_DA8XX_LCD 2
257*4882a593Smuzhiyun
258*4882a593Smuzhiyun /*
259*4882a593Smuzhiyun * LCD character sets
260*4882a593Smuzhiyun */
261*4882a593Smuzhiyun #define LCD_CHARSET_NORMAL 0
262*4882a593Smuzhiyun #define LCD_CHARSET_KS0074 1
263*4882a593Smuzhiyun
264*4882a593Smuzhiyun /*
265*4882a593Smuzhiyun * LCD types
266*4882a593Smuzhiyun */
267*4882a593Smuzhiyun #define LCD_TYPE_NONE 0
268*4882a593Smuzhiyun #define LCD_TYPE_CUSTOM 1
269*4882a593Smuzhiyun #define LCD_TYPE_OLD 2
270*4882a593Smuzhiyun #define LCD_TYPE_KS0074 3
271*4882a593Smuzhiyun #define LCD_TYPE_HANTRONIX 4
272*4882a593Smuzhiyun #define LCD_TYPE_NEXCOM 5
273*4882a593Smuzhiyun
274*4882a593Smuzhiyun /*
275*4882a593Smuzhiyun * keypad types
276*4882a593Smuzhiyun */
277*4882a593Smuzhiyun #define KEYPAD_TYPE_NONE 0
278*4882a593Smuzhiyun #define KEYPAD_TYPE_OLD 1
279*4882a593Smuzhiyun #define KEYPAD_TYPE_NEW 2
280*4882a593Smuzhiyun #define KEYPAD_TYPE_NEXCOM 3
281*4882a593Smuzhiyun
282*4882a593Smuzhiyun /*
283*4882a593Smuzhiyun * panel profiles
284*4882a593Smuzhiyun */
285*4882a593Smuzhiyun #define PANEL_PROFILE_CUSTOM 0
286*4882a593Smuzhiyun #define PANEL_PROFILE_OLD 1
287*4882a593Smuzhiyun #define PANEL_PROFILE_NEW 2
288*4882a593Smuzhiyun #define PANEL_PROFILE_HANTRONIX 3
289*4882a593Smuzhiyun #define PANEL_PROFILE_NEXCOM 4
290*4882a593Smuzhiyun #define PANEL_PROFILE_LARGE 5
291*4882a593Smuzhiyun
292*4882a593Smuzhiyun /*
293*4882a593Smuzhiyun * Construct custom config from the kernel's configuration
294*4882a593Smuzhiyun */
295*4882a593Smuzhiyun #define DEFAULT_PARPORT 0
296*4882a593Smuzhiyun #define DEFAULT_PROFILE PANEL_PROFILE_LARGE
297*4882a593Smuzhiyun #define DEFAULT_KEYPAD_TYPE KEYPAD_TYPE_OLD
298*4882a593Smuzhiyun #define DEFAULT_LCD_TYPE LCD_TYPE_OLD
299*4882a593Smuzhiyun #define DEFAULT_LCD_HEIGHT 2
300*4882a593Smuzhiyun #define DEFAULT_LCD_WIDTH 40
301*4882a593Smuzhiyun #define DEFAULT_LCD_BWIDTH 40
302*4882a593Smuzhiyun #define DEFAULT_LCD_HWIDTH 64
303*4882a593Smuzhiyun #define DEFAULT_LCD_CHARSET LCD_CHARSET_NORMAL
304*4882a593Smuzhiyun #define DEFAULT_LCD_PROTO LCD_PROTO_PARALLEL
305*4882a593Smuzhiyun
306*4882a593Smuzhiyun #define DEFAULT_LCD_PIN_E PIN_AUTOLF
307*4882a593Smuzhiyun #define DEFAULT_LCD_PIN_RS PIN_SELECP
308*4882a593Smuzhiyun #define DEFAULT_LCD_PIN_RW PIN_INITP
309*4882a593Smuzhiyun #define DEFAULT_LCD_PIN_SCL PIN_STROBE
310*4882a593Smuzhiyun #define DEFAULT_LCD_PIN_SDA PIN_D0
311*4882a593Smuzhiyun #define DEFAULT_LCD_PIN_BL PIN_NOT_SET
312*4882a593Smuzhiyun
313*4882a593Smuzhiyun #ifdef CONFIG_PANEL_PARPORT
314*4882a593Smuzhiyun #undef DEFAULT_PARPORT
315*4882a593Smuzhiyun #define DEFAULT_PARPORT CONFIG_PANEL_PARPORT
316*4882a593Smuzhiyun #endif
317*4882a593Smuzhiyun
318*4882a593Smuzhiyun #ifdef CONFIG_PANEL_PROFILE
319*4882a593Smuzhiyun #undef DEFAULT_PROFILE
320*4882a593Smuzhiyun #define DEFAULT_PROFILE CONFIG_PANEL_PROFILE
321*4882a593Smuzhiyun #endif
322*4882a593Smuzhiyun
323*4882a593Smuzhiyun #if DEFAULT_PROFILE == 0 /* custom */
324*4882a593Smuzhiyun #ifdef CONFIG_PANEL_KEYPAD
325*4882a593Smuzhiyun #undef DEFAULT_KEYPAD_TYPE
326*4882a593Smuzhiyun #define DEFAULT_KEYPAD_TYPE CONFIG_PANEL_KEYPAD
327*4882a593Smuzhiyun #endif
328*4882a593Smuzhiyun
329*4882a593Smuzhiyun #ifdef CONFIG_PANEL_LCD
330*4882a593Smuzhiyun #undef DEFAULT_LCD_TYPE
331*4882a593Smuzhiyun #define DEFAULT_LCD_TYPE CONFIG_PANEL_LCD
332*4882a593Smuzhiyun #endif
333*4882a593Smuzhiyun
334*4882a593Smuzhiyun #ifdef CONFIG_PANEL_LCD_HEIGHT
335*4882a593Smuzhiyun #undef DEFAULT_LCD_HEIGHT
336*4882a593Smuzhiyun #define DEFAULT_LCD_HEIGHT CONFIG_PANEL_LCD_HEIGHT
337*4882a593Smuzhiyun #endif
338*4882a593Smuzhiyun
339*4882a593Smuzhiyun #ifdef CONFIG_PANEL_LCD_WIDTH
340*4882a593Smuzhiyun #undef DEFAULT_LCD_WIDTH
341*4882a593Smuzhiyun #define DEFAULT_LCD_WIDTH CONFIG_PANEL_LCD_WIDTH
342*4882a593Smuzhiyun #endif
343*4882a593Smuzhiyun
344*4882a593Smuzhiyun #ifdef CONFIG_PANEL_LCD_BWIDTH
345*4882a593Smuzhiyun #undef DEFAULT_LCD_BWIDTH
346*4882a593Smuzhiyun #define DEFAULT_LCD_BWIDTH CONFIG_PANEL_LCD_BWIDTH
347*4882a593Smuzhiyun #endif
348*4882a593Smuzhiyun
349*4882a593Smuzhiyun #ifdef CONFIG_PANEL_LCD_HWIDTH
350*4882a593Smuzhiyun #undef DEFAULT_LCD_HWIDTH
351*4882a593Smuzhiyun #define DEFAULT_LCD_HWIDTH CONFIG_PANEL_LCD_HWIDTH
352*4882a593Smuzhiyun #endif
353*4882a593Smuzhiyun
354*4882a593Smuzhiyun #ifdef CONFIG_PANEL_LCD_CHARSET
355*4882a593Smuzhiyun #undef DEFAULT_LCD_CHARSET
356*4882a593Smuzhiyun #define DEFAULT_LCD_CHARSET CONFIG_PANEL_LCD_CHARSET
357*4882a593Smuzhiyun #endif
358*4882a593Smuzhiyun
359*4882a593Smuzhiyun #ifdef CONFIG_PANEL_LCD_PROTO
360*4882a593Smuzhiyun #undef DEFAULT_LCD_PROTO
361*4882a593Smuzhiyun #define DEFAULT_LCD_PROTO CONFIG_PANEL_LCD_PROTO
362*4882a593Smuzhiyun #endif
363*4882a593Smuzhiyun
364*4882a593Smuzhiyun #ifdef CONFIG_PANEL_LCD_PIN_E
365*4882a593Smuzhiyun #undef DEFAULT_LCD_PIN_E
366*4882a593Smuzhiyun #define DEFAULT_LCD_PIN_E CONFIG_PANEL_LCD_PIN_E
367*4882a593Smuzhiyun #endif
368*4882a593Smuzhiyun
369*4882a593Smuzhiyun #ifdef CONFIG_PANEL_LCD_PIN_RS
370*4882a593Smuzhiyun #undef DEFAULT_LCD_PIN_RS
371*4882a593Smuzhiyun #define DEFAULT_LCD_PIN_RS CONFIG_PANEL_LCD_PIN_RS
372*4882a593Smuzhiyun #endif
373*4882a593Smuzhiyun
374*4882a593Smuzhiyun #ifdef CONFIG_PANEL_LCD_PIN_RW
375*4882a593Smuzhiyun #undef DEFAULT_LCD_PIN_RW
376*4882a593Smuzhiyun #define DEFAULT_LCD_PIN_RW CONFIG_PANEL_LCD_PIN_RW
377*4882a593Smuzhiyun #endif
378*4882a593Smuzhiyun
379*4882a593Smuzhiyun #ifdef CONFIG_PANEL_LCD_PIN_SCL
380*4882a593Smuzhiyun #undef DEFAULT_LCD_PIN_SCL
381*4882a593Smuzhiyun #define DEFAULT_LCD_PIN_SCL CONFIG_PANEL_LCD_PIN_SCL
382*4882a593Smuzhiyun #endif
383*4882a593Smuzhiyun
384*4882a593Smuzhiyun #ifdef CONFIG_PANEL_LCD_PIN_SDA
385*4882a593Smuzhiyun #undef DEFAULT_LCD_PIN_SDA
386*4882a593Smuzhiyun #define DEFAULT_LCD_PIN_SDA CONFIG_PANEL_LCD_PIN_SDA
387*4882a593Smuzhiyun #endif
388*4882a593Smuzhiyun
389*4882a593Smuzhiyun #ifdef CONFIG_PANEL_LCD_PIN_BL
390*4882a593Smuzhiyun #undef DEFAULT_LCD_PIN_BL
391*4882a593Smuzhiyun #define DEFAULT_LCD_PIN_BL CONFIG_PANEL_LCD_PIN_BL
392*4882a593Smuzhiyun #endif
393*4882a593Smuzhiyun
394*4882a593Smuzhiyun #endif /* DEFAULT_PROFILE == 0 */
395*4882a593Smuzhiyun
396*4882a593Smuzhiyun /* global variables */
397*4882a593Smuzhiyun
398*4882a593Smuzhiyun /* Device single-open policy control */
399*4882a593Smuzhiyun static atomic_t keypad_available = ATOMIC_INIT(1);
400*4882a593Smuzhiyun
401*4882a593Smuzhiyun static struct pardevice *pprt;
402*4882a593Smuzhiyun
403*4882a593Smuzhiyun static int keypad_initialized;
404*4882a593Smuzhiyun
405*4882a593Smuzhiyun static DEFINE_SPINLOCK(pprt_lock);
406*4882a593Smuzhiyun static struct timer_list scan_timer;
407*4882a593Smuzhiyun
408*4882a593Smuzhiyun MODULE_DESCRIPTION("Generic parallel port LCD/Keypad driver");
409*4882a593Smuzhiyun
410*4882a593Smuzhiyun static int parport = DEFAULT_PARPORT;
411*4882a593Smuzhiyun module_param(parport, int, 0000);
412*4882a593Smuzhiyun MODULE_PARM_DESC(parport, "Parallel port index (0=lpt1, 1=lpt2, ...)");
413*4882a593Smuzhiyun
414*4882a593Smuzhiyun static int profile = DEFAULT_PROFILE;
415*4882a593Smuzhiyun module_param(profile, int, 0000);
416*4882a593Smuzhiyun MODULE_PARM_DESC(profile,
417*4882a593Smuzhiyun "1=16x2 old kp; 2=serial 16x2, new kp; 3=16x2 hantronix; "
418*4882a593Smuzhiyun "4=16x2 nexcom; default=40x2, old kp");
419*4882a593Smuzhiyun
420*4882a593Smuzhiyun static int keypad_type = NOT_SET;
421*4882a593Smuzhiyun module_param(keypad_type, int, 0000);
422*4882a593Smuzhiyun MODULE_PARM_DESC(keypad_type,
423*4882a593Smuzhiyun "Keypad type: 0=none, 1=old 6 keys, 2=new 6+1 keys, 3=nexcom 4 keys");
424*4882a593Smuzhiyun
425*4882a593Smuzhiyun static int lcd_type = NOT_SET;
426*4882a593Smuzhiyun module_param(lcd_type, int, 0000);
427*4882a593Smuzhiyun MODULE_PARM_DESC(lcd_type,
428*4882a593Smuzhiyun "LCD type: 0=none, 1=compiled-in, 2=old, 3=serial ks0074, 4=hantronix, 5=nexcom");
429*4882a593Smuzhiyun
430*4882a593Smuzhiyun static int lcd_height = NOT_SET;
431*4882a593Smuzhiyun module_param(lcd_height, int, 0000);
432*4882a593Smuzhiyun MODULE_PARM_DESC(lcd_height, "Number of lines on the LCD");
433*4882a593Smuzhiyun
434*4882a593Smuzhiyun static int lcd_width = NOT_SET;
435*4882a593Smuzhiyun module_param(lcd_width, int, 0000);
436*4882a593Smuzhiyun MODULE_PARM_DESC(lcd_width, "Number of columns on the LCD");
437*4882a593Smuzhiyun
438*4882a593Smuzhiyun static int lcd_bwidth = NOT_SET; /* internal buffer width (usually 40) */
439*4882a593Smuzhiyun module_param(lcd_bwidth, int, 0000);
440*4882a593Smuzhiyun MODULE_PARM_DESC(lcd_bwidth, "Internal LCD line width (40)");
441*4882a593Smuzhiyun
442*4882a593Smuzhiyun static int lcd_hwidth = NOT_SET; /* hardware buffer width (usually 64) */
443*4882a593Smuzhiyun module_param(lcd_hwidth, int, 0000);
444*4882a593Smuzhiyun MODULE_PARM_DESC(lcd_hwidth, "LCD line hardware address (64)");
445*4882a593Smuzhiyun
446*4882a593Smuzhiyun static int lcd_charset = NOT_SET;
447*4882a593Smuzhiyun module_param(lcd_charset, int, 0000);
448*4882a593Smuzhiyun MODULE_PARM_DESC(lcd_charset, "LCD character set: 0=standard, 1=KS0074");
449*4882a593Smuzhiyun
450*4882a593Smuzhiyun static int lcd_proto = NOT_SET;
451*4882a593Smuzhiyun module_param(lcd_proto, int, 0000);
452*4882a593Smuzhiyun MODULE_PARM_DESC(lcd_proto,
453*4882a593Smuzhiyun "LCD communication: 0=parallel (//), 1=serial, 2=TI LCD Interface");
454*4882a593Smuzhiyun
455*4882a593Smuzhiyun /*
456*4882a593Smuzhiyun * These are the parallel port pins the LCD control signals are connected to.
457*4882a593Smuzhiyun * Set this to 0 if the signal is not used. Set it to its opposite value
458*4882a593Smuzhiyun * (negative) if the signal is negated. -MAXINT is used to indicate that the
459*4882a593Smuzhiyun * pin has not been explicitly specified.
460*4882a593Smuzhiyun *
461*4882a593Smuzhiyun * WARNING! no check will be performed about collisions with keypad !
462*4882a593Smuzhiyun */
463*4882a593Smuzhiyun
464*4882a593Smuzhiyun static int lcd_e_pin = PIN_NOT_SET;
465*4882a593Smuzhiyun module_param(lcd_e_pin, int, 0000);
466*4882a593Smuzhiyun MODULE_PARM_DESC(lcd_e_pin,
467*4882a593Smuzhiyun "# of the // port pin connected to LCD 'E' signal, with polarity (-17..17)");
468*4882a593Smuzhiyun
469*4882a593Smuzhiyun static int lcd_rs_pin = PIN_NOT_SET;
470*4882a593Smuzhiyun module_param(lcd_rs_pin, int, 0000);
471*4882a593Smuzhiyun MODULE_PARM_DESC(lcd_rs_pin,
472*4882a593Smuzhiyun "# of the // port pin connected to LCD 'RS' signal, with polarity (-17..17)");
473*4882a593Smuzhiyun
474*4882a593Smuzhiyun static int lcd_rw_pin = PIN_NOT_SET;
475*4882a593Smuzhiyun module_param(lcd_rw_pin, int, 0000);
476*4882a593Smuzhiyun MODULE_PARM_DESC(lcd_rw_pin,
477*4882a593Smuzhiyun "# of the // port pin connected to LCD 'RW' signal, with polarity (-17..17)");
478*4882a593Smuzhiyun
479*4882a593Smuzhiyun static int lcd_cl_pin = PIN_NOT_SET;
480*4882a593Smuzhiyun module_param(lcd_cl_pin, int, 0000);
481*4882a593Smuzhiyun MODULE_PARM_DESC(lcd_cl_pin,
482*4882a593Smuzhiyun "# of the // port pin connected to serial LCD 'SCL' signal, with polarity (-17..17)");
483*4882a593Smuzhiyun
484*4882a593Smuzhiyun static int lcd_da_pin = PIN_NOT_SET;
485*4882a593Smuzhiyun module_param(lcd_da_pin, int, 0000);
486*4882a593Smuzhiyun MODULE_PARM_DESC(lcd_da_pin,
487*4882a593Smuzhiyun "# of the // port pin connected to serial LCD 'SDA' signal, with polarity (-17..17)");
488*4882a593Smuzhiyun
489*4882a593Smuzhiyun static int lcd_bl_pin = PIN_NOT_SET;
490*4882a593Smuzhiyun module_param(lcd_bl_pin, int, 0000);
491*4882a593Smuzhiyun MODULE_PARM_DESC(lcd_bl_pin,
492*4882a593Smuzhiyun "# of the // port pin connected to LCD backlight, with polarity (-17..17)");
493*4882a593Smuzhiyun
494*4882a593Smuzhiyun /* Deprecated module parameters - consider not using them anymore */
495*4882a593Smuzhiyun
496*4882a593Smuzhiyun static int lcd_enabled = NOT_SET;
497*4882a593Smuzhiyun module_param(lcd_enabled, int, 0000);
498*4882a593Smuzhiyun MODULE_PARM_DESC(lcd_enabled, "Deprecated option, use lcd_type instead");
499*4882a593Smuzhiyun
500*4882a593Smuzhiyun static int keypad_enabled = NOT_SET;
501*4882a593Smuzhiyun module_param(keypad_enabled, int, 0000);
502*4882a593Smuzhiyun MODULE_PARM_DESC(keypad_enabled, "Deprecated option, use keypad_type instead");
503*4882a593Smuzhiyun
504*4882a593Smuzhiyun /* for some LCD drivers (ks0074) we need a charset conversion table. */
505*4882a593Smuzhiyun static const unsigned char lcd_char_conv_ks0074[256] = {
506*4882a593Smuzhiyun /* 0|8 1|9 2|A 3|B 4|C 5|D 6|E 7|F */
507*4882a593Smuzhiyun /* 0x00 */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
508*4882a593Smuzhiyun /* 0x08 */ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
509*4882a593Smuzhiyun /* 0x10 */ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
510*4882a593Smuzhiyun /* 0x18 */ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
511*4882a593Smuzhiyun /* 0x20 */ 0x20, 0x21, 0x22, 0x23, 0xa2, 0x25, 0x26, 0x27,
512*4882a593Smuzhiyun /* 0x28 */ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
513*4882a593Smuzhiyun /* 0x30 */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
514*4882a593Smuzhiyun /* 0x38 */ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
515*4882a593Smuzhiyun /* 0x40 */ 0xa0, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
516*4882a593Smuzhiyun /* 0x48 */ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
517*4882a593Smuzhiyun /* 0x50 */ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
518*4882a593Smuzhiyun /* 0x58 */ 0x58, 0x59, 0x5a, 0xfa, 0xfb, 0xfc, 0x1d, 0xc4,
519*4882a593Smuzhiyun /* 0x60 */ 0x96, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
520*4882a593Smuzhiyun /* 0x68 */ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
521*4882a593Smuzhiyun /* 0x70 */ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
522*4882a593Smuzhiyun /* 0x78 */ 0x78, 0x79, 0x7a, 0xfd, 0xfe, 0xff, 0xce, 0x20,
523*4882a593Smuzhiyun /* 0x80 */ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
524*4882a593Smuzhiyun /* 0x88 */ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
525*4882a593Smuzhiyun /* 0x90 */ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
526*4882a593Smuzhiyun /* 0x98 */ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
527*4882a593Smuzhiyun /* 0xA0 */ 0x20, 0x40, 0xb1, 0xa1, 0x24, 0xa3, 0xfe, 0x5f,
528*4882a593Smuzhiyun /* 0xA8 */ 0x22, 0xc8, 0x61, 0x14, 0x97, 0x2d, 0xad, 0x96,
529*4882a593Smuzhiyun /* 0xB0 */ 0x80, 0x8c, 0x82, 0x83, 0x27, 0x8f, 0x86, 0xdd,
530*4882a593Smuzhiyun /* 0xB8 */ 0x2c, 0x81, 0x6f, 0x15, 0x8b, 0x8a, 0x84, 0x60,
531*4882a593Smuzhiyun /* 0xC0 */ 0xe2, 0xe2, 0xe2, 0x5b, 0x5b, 0xae, 0xbc, 0xa9,
532*4882a593Smuzhiyun /* 0xC8 */ 0xc5, 0xbf, 0xc6, 0xf1, 0xe3, 0xe3, 0xe3, 0xe3,
533*4882a593Smuzhiyun /* 0xD0 */ 0x44, 0x5d, 0xa8, 0xe4, 0xec, 0xec, 0x5c, 0x78,
534*4882a593Smuzhiyun /* 0xD8 */ 0xab, 0xa6, 0xe5, 0x5e, 0x5e, 0xe6, 0xaa, 0xbe,
535*4882a593Smuzhiyun /* 0xE0 */ 0x7f, 0xe7, 0xaf, 0x7b, 0x7b, 0xaf, 0xbd, 0xc8,
536*4882a593Smuzhiyun /* 0xE8 */ 0xa4, 0xa5, 0xc7, 0xf6, 0xa7, 0xe8, 0x69, 0x69,
537*4882a593Smuzhiyun /* 0xF0 */ 0xed, 0x7d, 0xa8, 0xe4, 0xec, 0x5c, 0x5c, 0x25,
538*4882a593Smuzhiyun /* 0xF8 */ 0xac, 0xa6, 0xea, 0xef, 0x7e, 0xeb, 0xb2, 0x79,
539*4882a593Smuzhiyun };
540*4882a593Smuzhiyun
541*4882a593Smuzhiyun static const char old_keypad_profile[][4][9] = {
542*4882a593Smuzhiyun {"S0", "Left\n", "Left\n", ""},
543*4882a593Smuzhiyun {"S1", "Down\n", "Down\n", ""},
544*4882a593Smuzhiyun {"S2", "Up\n", "Up\n", ""},
545*4882a593Smuzhiyun {"S3", "Right\n", "Right\n", ""},
546*4882a593Smuzhiyun {"S4", "Esc\n", "Esc\n", ""},
547*4882a593Smuzhiyun {"S5", "Ret\n", "Ret\n", ""},
548*4882a593Smuzhiyun {"", "", "", ""}
549*4882a593Smuzhiyun };
550*4882a593Smuzhiyun
551*4882a593Smuzhiyun /* signals, press, repeat, release */
552*4882a593Smuzhiyun static const char new_keypad_profile[][4][9] = {
553*4882a593Smuzhiyun {"S0", "Left\n", "Left\n", ""},
554*4882a593Smuzhiyun {"S1", "Down\n", "Down\n", ""},
555*4882a593Smuzhiyun {"S2", "Up\n", "Up\n", ""},
556*4882a593Smuzhiyun {"S3", "Right\n", "Right\n", ""},
557*4882a593Smuzhiyun {"S4s5", "", "Esc\n", "Esc\n"},
558*4882a593Smuzhiyun {"s4S5", "", "Ret\n", "Ret\n"},
559*4882a593Smuzhiyun {"S4S5", "Help\n", "", ""},
560*4882a593Smuzhiyun /* add new signals above this line */
561*4882a593Smuzhiyun {"", "", "", ""}
562*4882a593Smuzhiyun };
563*4882a593Smuzhiyun
564*4882a593Smuzhiyun /* signals, press, repeat, release */
565*4882a593Smuzhiyun static const char nexcom_keypad_profile[][4][9] = {
566*4882a593Smuzhiyun {"a-p-e-", "Down\n", "Down\n", ""},
567*4882a593Smuzhiyun {"a-p-E-", "Ret\n", "Ret\n", ""},
568*4882a593Smuzhiyun {"a-P-E-", "Esc\n", "Esc\n", ""},
569*4882a593Smuzhiyun {"a-P-e-", "Up\n", "Up\n", ""},
570*4882a593Smuzhiyun /* add new signals above this line */
571*4882a593Smuzhiyun {"", "", "", ""}
572*4882a593Smuzhiyun };
573*4882a593Smuzhiyun
574*4882a593Smuzhiyun static const char (*keypad_profile)[4][9] = old_keypad_profile;
575*4882a593Smuzhiyun
576*4882a593Smuzhiyun static DECLARE_BITMAP(bits, LCD_BITS);
577*4882a593Smuzhiyun
lcd_get_bits(unsigned int port,int * val)578*4882a593Smuzhiyun static void lcd_get_bits(unsigned int port, int *val)
579*4882a593Smuzhiyun {
580*4882a593Smuzhiyun unsigned int bit, state;
581*4882a593Smuzhiyun
582*4882a593Smuzhiyun for (bit = 0; bit < LCD_BITS; bit++) {
583*4882a593Smuzhiyun state = test_bit(bit, bits) ? BIT_SET : BIT_CLR;
584*4882a593Smuzhiyun *val &= lcd_bits[port][bit][BIT_MSK];
585*4882a593Smuzhiyun *val |= lcd_bits[port][bit][state];
586*4882a593Smuzhiyun }
587*4882a593Smuzhiyun }
588*4882a593Smuzhiyun
589*4882a593Smuzhiyun /* sets data port bits according to current signals values */
set_data_bits(void)590*4882a593Smuzhiyun static int set_data_bits(void)
591*4882a593Smuzhiyun {
592*4882a593Smuzhiyun int val;
593*4882a593Smuzhiyun
594*4882a593Smuzhiyun val = r_dtr(pprt);
595*4882a593Smuzhiyun lcd_get_bits(LCD_PORT_D, &val);
596*4882a593Smuzhiyun w_dtr(pprt, val);
597*4882a593Smuzhiyun return val;
598*4882a593Smuzhiyun }
599*4882a593Smuzhiyun
600*4882a593Smuzhiyun /* sets ctrl port bits according to current signals values */
set_ctrl_bits(void)601*4882a593Smuzhiyun static int set_ctrl_bits(void)
602*4882a593Smuzhiyun {
603*4882a593Smuzhiyun int val;
604*4882a593Smuzhiyun
605*4882a593Smuzhiyun val = r_ctr(pprt);
606*4882a593Smuzhiyun lcd_get_bits(LCD_PORT_C, &val);
607*4882a593Smuzhiyun w_ctr(pprt, val);
608*4882a593Smuzhiyun return val;
609*4882a593Smuzhiyun }
610*4882a593Smuzhiyun
611*4882a593Smuzhiyun /* sets ctrl & data port bits according to current signals values */
panel_set_bits(void)612*4882a593Smuzhiyun static void panel_set_bits(void)
613*4882a593Smuzhiyun {
614*4882a593Smuzhiyun set_data_bits();
615*4882a593Smuzhiyun set_ctrl_bits();
616*4882a593Smuzhiyun }
617*4882a593Smuzhiyun
618*4882a593Smuzhiyun /*
619*4882a593Smuzhiyun * Converts a parallel port pin (from -25 to 25) to data and control ports
620*4882a593Smuzhiyun * masks, and data and control port bits. The signal will be considered
621*4882a593Smuzhiyun * unconnected if it's on pin 0 or an invalid pin (<-25 or >25).
622*4882a593Smuzhiyun *
623*4882a593Smuzhiyun * Result will be used this way :
624*4882a593Smuzhiyun * out(dport, in(dport) & d_val[2] | d_val[signal_state])
625*4882a593Smuzhiyun * out(cport, in(cport) & c_val[2] | c_val[signal_state])
626*4882a593Smuzhiyun */
pin_to_bits(int pin,unsigned char * d_val,unsigned char * c_val)627*4882a593Smuzhiyun static void pin_to_bits(int pin, unsigned char *d_val, unsigned char *c_val)
628*4882a593Smuzhiyun {
629*4882a593Smuzhiyun int d_bit, c_bit, inv;
630*4882a593Smuzhiyun
631*4882a593Smuzhiyun d_val[0] = 0;
632*4882a593Smuzhiyun c_val[0] = 0;
633*4882a593Smuzhiyun d_val[1] = 0;
634*4882a593Smuzhiyun c_val[1] = 0;
635*4882a593Smuzhiyun d_val[2] = 0xFF;
636*4882a593Smuzhiyun c_val[2] = 0xFF;
637*4882a593Smuzhiyun
638*4882a593Smuzhiyun if (pin == 0)
639*4882a593Smuzhiyun return;
640*4882a593Smuzhiyun
641*4882a593Smuzhiyun inv = (pin < 0);
642*4882a593Smuzhiyun if (inv)
643*4882a593Smuzhiyun pin = -pin;
644*4882a593Smuzhiyun
645*4882a593Smuzhiyun d_bit = 0;
646*4882a593Smuzhiyun c_bit = 0;
647*4882a593Smuzhiyun
648*4882a593Smuzhiyun switch (pin) {
649*4882a593Smuzhiyun case PIN_STROBE: /* strobe, inverted */
650*4882a593Smuzhiyun c_bit = PNL_PSTROBE;
651*4882a593Smuzhiyun inv = !inv;
652*4882a593Smuzhiyun break;
653*4882a593Smuzhiyun case PIN_D0...PIN_D7: /* D0 - D7 = 2 - 9 */
654*4882a593Smuzhiyun d_bit = 1 << (pin - 2);
655*4882a593Smuzhiyun break;
656*4882a593Smuzhiyun case PIN_AUTOLF: /* autofeed, inverted */
657*4882a593Smuzhiyun c_bit = PNL_PAUTOLF;
658*4882a593Smuzhiyun inv = !inv;
659*4882a593Smuzhiyun break;
660*4882a593Smuzhiyun case PIN_INITP: /* init, direct */
661*4882a593Smuzhiyun c_bit = PNL_PINITP;
662*4882a593Smuzhiyun break;
663*4882a593Smuzhiyun case PIN_SELECP: /* select_in, inverted */
664*4882a593Smuzhiyun c_bit = PNL_PSELECP;
665*4882a593Smuzhiyun inv = !inv;
666*4882a593Smuzhiyun break;
667*4882a593Smuzhiyun default: /* unknown pin, ignore */
668*4882a593Smuzhiyun break;
669*4882a593Smuzhiyun }
670*4882a593Smuzhiyun
671*4882a593Smuzhiyun if (c_bit) {
672*4882a593Smuzhiyun c_val[2] &= ~c_bit;
673*4882a593Smuzhiyun c_val[!inv] = c_bit;
674*4882a593Smuzhiyun } else if (d_bit) {
675*4882a593Smuzhiyun d_val[2] &= ~d_bit;
676*4882a593Smuzhiyun d_val[!inv] = d_bit;
677*4882a593Smuzhiyun }
678*4882a593Smuzhiyun }
679*4882a593Smuzhiyun
680*4882a593Smuzhiyun /*
681*4882a593Smuzhiyun * send a serial byte to the LCD panel. The caller is responsible for locking
682*4882a593Smuzhiyun * if needed.
683*4882a593Smuzhiyun */
lcd_send_serial(int byte)684*4882a593Smuzhiyun static void lcd_send_serial(int byte)
685*4882a593Smuzhiyun {
686*4882a593Smuzhiyun int bit;
687*4882a593Smuzhiyun
688*4882a593Smuzhiyun /*
689*4882a593Smuzhiyun * the data bit is set on D0, and the clock on STROBE.
690*4882a593Smuzhiyun * LCD reads D0 on STROBE's rising edge.
691*4882a593Smuzhiyun */
692*4882a593Smuzhiyun for (bit = 0; bit < 8; bit++) {
693*4882a593Smuzhiyun clear_bit(LCD_BIT_CL, bits); /* CLK low */
694*4882a593Smuzhiyun panel_set_bits();
695*4882a593Smuzhiyun if (byte & 1) {
696*4882a593Smuzhiyun set_bit(LCD_BIT_DA, bits);
697*4882a593Smuzhiyun } else {
698*4882a593Smuzhiyun clear_bit(LCD_BIT_DA, bits);
699*4882a593Smuzhiyun }
700*4882a593Smuzhiyun
701*4882a593Smuzhiyun panel_set_bits();
702*4882a593Smuzhiyun udelay(2); /* maintain the data during 2 us before CLK up */
703*4882a593Smuzhiyun set_bit(LCD_BIT_CL, bits); /* CLK high */
704*4882a593Smuzhiyun panel_set_bits();
705*4882a593Smuzhiyun udelay(1); /* maintain the strobe during 1 us */
706*4882a593Smuzhiyun byte >>= 1;
707*4882a593Smuzhiyun }
708*4882a593Smuzhiyun }
709*4882a593Smuzhiyun
710*4882a593Smuzhiyun /* turn the backlight on or off */
lcd_backlight(struct charlcd * charlcd,int on)711*4882a593Smuzhiyun static void lcd_backlight(struct charlcd *charlcd, int on)
712*4882a593Smuzhiyun {
713*4882a593Smuzhiyun if (lcd.pins.bl == PIN_NONE)
714*4882a593Smuzhiyun return;
715*4882a593Smuzhiyun
716*4882a593Smuzhiyun /* The backlight is activated by setting the AUTOFEED line to +5V */
717*4882a593Smuzhiyun spin_lock_irq(&pprt_lock);
718*4882a593Smuzhiyun if (on)
719*4882a593Smuzhiyun set_bit(LCD_BIT_BL, bits);
720*4882a593Smuzhiyun else
721*4882a593Smuzhiyun clear_bit(LCD_BIT_BL, bits);
722*4882a593Smuzhiyun panel_set_bits();
723*4882a593Smuzhiyun spin_unlock_irq(&pprt_lock);
724*4882a593Smuzhiyun }
725*4882a593Smuzhiyun
726*4882a593Smuzhiyun /* send a command to the LCD panel in serial mode */
lcd_write_cmd_s(struct charlcd * charlcd,int cmd)727*4882a593Smuzhiyun static void lcd_write_cmd_s(struct charlcd *charlcd, int cmd)
728*4882a593Smuzhiyun {
729*4882a593Smuzhiyun spin_lock_irq(&pprt_lock);
730*4882a593Smuzhiyun lcd_send_serial(0x1F); /* R/W=W, RS=0 */
731*4882a593Smuzhiyun lcd_send_serial(cmd & 0x0F);
732*4882a593Smuzhiyun lcd_send_serial((cmd >> 4) & 0x0F);
733*4882a593Smuzhiyun udelay(40); /* the shortest command takes at least 40 us */
734*4882a593Smuzhiyun spin_unlock_irq(&pprt_lock);
735*4882a593Smuzhiyun }
736*4882a593Smuzhiyun
737*4882a593Smuzhiyun /* send data to the LCD panel in serial mode */
lcd_write_data_s(struct charlcd * charlcd,int data)738*4882a593Smuzhiyun static void lcd_write_data_s(struct charlcd *charlcd, int data)
739*4882a593Smuzhiyun {
740*4882a593Smuzhiyun spin_lock_irq(&pprt_lock);
741*4882a593Smuzhiyun lcd_send_serial(0x5F); /* R/W=W, RS=1 */
742*4882a593Smuzhiyun lcd_send_serial(data & 0x0F);
743*4882a593Smuzhiyun lcd_send_serial((data >> 4) & 0x0F);
744*4882a593Smuzhiyun udelay(40); /* the shortest data takes at least 40 us */
745*4882a593Smuzhiyun spin_unlock_irq(&pprt_lock);
746*4882a593Smuzhiyun }
747*4882a593Smuzhiyun
748*4882a593Smuzhiyun /* send a command to the LCD panel in 8 bits parallel mode */
lcd_write_cmd_p8(struct charlcd * charlcd,int cmd)749*4882a593Smuzhiyun static void lcd_write_cmd_p8(struct charlcd *charlcd, int cmd)
750*4882a593Smuzhiyun {
751*4882a593Smuzhiyun spin_lock_irq(&pprt_lock);
752*4882a593Smuzhiyun /* present the data to the data port */
753*4882a593Smuzhiyun w_dtr(pprt, cmd);
754*4882a593Smuzhiyun udelay(20); /* maintain the data during 20 us before the strobe */
755*4882a593Smuzhiyun
756*4882a593Smuzhiyun set_bit(LCD_BIT_E, bits);
757*4882a593Smuzhiyun clear_bit(LCD_BIT_RS, bits);
758*4882a593Smuzhiyun clear_bit(LCD_BIT_RW, bits);
759*4882a593Smuzhiyun set_ctrl_bits();
760*4882a593Smuzhiyun
761*4882a593Smuzhiyun udelay(40); /* maintain the strobe during 40 us */
762*4882a593Smuzhiyun
763*4882a593Smuzhiyun clear_bit(LCD_BIT_E, bits);
764*4882a593Smuzhiyun set_ctrl_bits();
765*4882a593Smuzhiyun
766*4882a593Smuzhiyun udelay(120); /* the shortest command takes at least 120 us */
767*4882a593Smuzhiyun spin_unlock_irq(&pprt_lock);
768*4882a593Smuzhiyun }
769*4882a593Smuzhiyun
770*4882a593Smuzhiyun /* send data to the LCD panel in 8 bits parallel mode */
lcd_write_data_p8(struct charlcd * charlcd,int data)771*4882a593Smuzhiyun static void lcd_write_data_p8(struct charlcd *charlcd, int data)
772*4882a593Smuzhiyun {
773*4882a593Smuzhiyun spin_lock_irq(&pprt_lock);
774*4882a593Smuzhiyun /* present the data to the data port */
775*4882a593Smuzhiyun w_dtr(pprt, data);
776*4882a593Smuzhiyun udelay(20); /* maintain the data during 20 us before the strobe */
777*4882a593Smuzhiyun
778*4882a593Smuzhiyun set_bit(LCD_BIT_E, bits);
779*4882a593Smuzhiyun set_bit(LCD_BIT_RS, bits);
780*4882a593Smuzhiyun clear_bit(LCD_BIT_RW, bits);
781*4882a593Smuzhiyun set_ctrl_bits();
782*4882a593Smuzhiyun
783*4882a593Smuzhiyun udelay(40); /* maintain the strobe during 40 us */
784*4882a593Smuzhiyun
785*4882a593Smuzhiyun clear_bit(LCD_BIT_E, bits);
786*4882a593Smuzhiyun set_ctrl_bits();
787*4882a593Smuzhiyun
788*4882a593Smuzhiyun udelay(45); /* the shortest data takes at least 45 us */
789*4882a593Smuzhiyun spin_unlock_irq(&pprt_lock);
790*4882a593Smuzhiyun }
791*4882a593Smuzhiyun
792*4882a593Smuzhiyun /* send a command to the TI LCD panel */
lcd_write_cmd_tilcd(struct charlcd * charlcd,int cmd)793*4882a593Smuzhiyun static void lcd_write_cmd_tilcd(struct charlcd *charlcd, int cmd)
794*4882a593Smuzhiyun {
795*4882a593Smuzhiyun spin_lock_irq(&pprt_lock);
796*4882a593Smuzhiyun /* present the data to the control port */
797*4882a593Smuzhiyun w_ctr(pprt, cmd);
798*4882a593Smuzhiyun udelay(60);
799*4882a593Smuzhiyun spin_unlock_irq(&pprt_lock);
800*4882a593Smuzhiyun }
801*4882a593Smuzhiyun
802*4882a593Smuzhiyun /* send data to the TI LCD panel */
lcd_write_data_tilcd(struct charlcd * charlcd,int data)803*4882a593Smuzhiyun static void lcd_write_data_tilcd(struct charlcd *charlcd, int data)
804*4882a593Smuzhiyun {
805*4882a593Smuzhiyun spin_lock_irq(&pprt_lock);
806*4882a593Smuzhiyun /* present the data to the data port */
807*4882a593Smuzhiyun w_dtr(pprt, data);
808*4882a593Smuzhiyun udelay(60);
809*4882a593Smuzhiyun spin_unlock_irq(&pprt_lock);
810*4882a593Smuzhiyun }
811*4882a593Smuzhiyun
812*4882a593Smuzhiyun /* fills the display with spaces and resets X/Y */
lcd_clear_fast_s(struct charlcd * charlcd)813*4882a593Smuzhiyun static void lcd_clear_fast_s(struct charlcd *charlcd)
814*4882a593Smuzhiyun {
815*4882a593Smuzhiyun int pos;
816*4882a593Smuzhiyun
817*4882a593Smuzhiyun spin_lock_irq(&pprt_lock);
818*4882a593Smuzhiyun for (pos = 0; pos < charlcd->height * charlcd->hwidth; pos++) {
819*4882a593Smuzhiyun lcd_send_serial(0x5F); /* R/W=W, RS=1 */
820*4882a593Smuzhiyun lcd_send_serial(' ' & 0x0F);
821*4882a593Smuzhiyun lcd_send_serial((' ' >> 4) & 0x0F);
822*4882a593Smuzhiyun /* the shortest data takes at least 40 us */
823*4882a593Smuzhiyun udelay(40);
824*4882a593Smuzhiyun }
825*4882a593Smuzhiyun spin_unlock_irq(&pprt_lock);
826*4882a593Smuzhiyun }
827*4882a593Smuzhiyun
828*4882a593Smuzhiyun /* fills the display with spaces and resets X/Y */
lcd_clear_fast_p8(struct charlcd * charlcd)829*4882a593Smuzhiyun static void lcd_clear_fast_p8(struct charlcd *charlcd)
830*4882a593Smuzhiyun {
831*4882a593Smuzhiyun int pos;
832*4882a593Smuzhiyun
833*4882a593Smuzhiyun spin_lock_irq(&pprt_lock);
834*4882a593Smuzhiyun for (pos = 0; pos < charlcd->height * charlcd->hwidth; pos++) {
835*4882a593Smuzhiyun /* present the data to the data port */
836*4882a593Smuzhiyun w_dtr(pprt, ' ');
837*4882a593Smuzhiyun
838*4882a593Smuzhiyun /* maintain the data during 20 us before the strobe */
839*4882a593Smuzhiyun udelay(20);
840*4882a593Smuzhiyun
841*4882a593Smuzhiyun set_bit(LCD_BIT_E, bits);
842*4882a593Smuzhiyun set_bit(LCD_BIT_RS, bits);
843*4882a593Smuzhiyun clear_bit(LCD_BIT_RW, bits);
844*4882a593Smuzhiyun set_ctrl_bits();
845*4882a593Smuzhiyun
846*4882a593Smuzhiyun /* maintain the strobe during 40 us */
847*4882a593Smuzhiyun udelay(40);
848*4882a593Smuzhiyun
849*4882a593Smuzhiyun clear_bit(LCD_BIT_E, bits);
850*4882a593Smuzhiyun set_ctrl_bits();
851*4882a593Smuzhiyun
852*4882a593Smuzhiyun /* the shortest data takes at least 45 us */
853*4882a593Smuzhiyun udelay(45);
854*4882a593Smuzhiyun }
855*4882a593Smuzhiyun spin_unlock_irq(&pprt_lock);
856*4882a593Smuzhiyun }
857*4882a593Smuzhiyun
858*4882a593Smuzhiyun /* fills the display with spaces and resets X/Y */
lcd_clear_fast_tilcd(struct charlcd * charlcd)859*4882a593Smuzhiyun static void lcd_clear_fast_tilcd(struct charlcd *charlcd)
860*4882a593Smuzhiyun {
861*4882a593Smuzhiyun int pos;
862*4882a593Smuzhiyun
863*4882a593Smuzhiyun spin_lock_irq(&pprt_lock);
864*4882a593Smuzhiyun for (pos = 0; pos < charlcd->height * charlcd->hwidth; pos++) {
865*4882a593Smuzhiyun /* present the data to the data port */
866*4882a593Smuzhiyun w_dtr(pprt, ' ');
867*4882a593Smuzhiyun udelay(60);
868*4882a593Smuzhiyun }
869*4882a593Smuzhiyun
870*4882a593Smuzhiyun spin_unlock_irq(&pprt_lock);
871*4882a593Smuzhiyun }
872*4882a593Smuzhiyun
873*4882a593Smuzhiyun static const struct charlcd_ops charlcd_serial_ops = {
874*4882a593Smuzhiyun .write_cmd = lcd_write_cmd_s,
875*4882a593Smuzhiyun .write_data = lcd_write_data_s,
876*4882a593Smuzhiyun .clear_fast = lcd_clear_fast_s,
877*4882a593Smuzhiyun .backlight = lcd_backlight,
878*4882a593Smuzhiyun };
879*4882a593Smuzhiyun
880*4882a593Smuzhiyun static const struct charlcd_ops charlcd_parallel_ops = {
881*4882a593Smuzhiyun .write_cmd = lcd_write_cmd_p8,
882*4882a593Smuzhiyun .write_data = lcd_write_data_p8,
883*4882a593Smuzhiyun .clear_fast = lcd_clear_fast_p8,
884*4882a593Smuzhiyun .backlight = lcd_backlight,
885*4882a593Smuzhiyun };
886*4882a593Smuzhiyun
887*4882a593Smuzhiyun static const struct charlcd_ops charlcd_tilcd_ops = {
888*4882a593Smuzhiyun .write_cmd = lcd_write_cmd_tilcd,
889*4882a593Smuzhiyun .write_data = lcd_write_data_tilcd,
890*4882a593Smuzhiyun .clear_fast = lcd_clear_fast_tilcd,
891*4882a593Smuzhiyun .backlight = lcd_backlight,
892*4882a593Smuzhiyun };
893*4882a593Smuzhiyun
894*4882a593Smuzhiyun /* initialize the LCD driver */
lcd_init(void)895*4882a593Smuzhiyun static void lcd_init(void)
896*4882a593Smuzhiyun {
897*4882a593Smuzhiyun struct charlcd *charlcd;
898*4882a593Smuzhiyun
899*4882a593Smuzhiyun charlcd = charlcd_alloc(0);
900*4882a593Smuzhiyun if (!charlcd)
901*4882a593Smuzhiyun return;
902*4882a593Smuzhiyun
903*4882a593Smuzhiyun /*
904*4882a593Smuzhiyun * Init lcd struct with load-time values to preserve exact
905*4882a593Smuzhiyun * current functionality (at least for now).
906*4882a593Smuzhiyun */
907*4882a593Smuzhiyun charlcd->height = lcd_height;
908*4882a593Smuzhiyun charlcd->width = lcd_width;
909*4882a593Smuzhiyun charlcd->bwidth = lcd_bwidth;
910*4882a593Smuzhiyun charlcd->hwidth = lcd_hwidth;
911*4882a593Smuzhiyun
912*4882a593Smuzhiyun switch (selected_lcd_type) {
913*4882a593Smuzhiyun case LCD_TYPE_OLD:
914*4882a593Smuzhiyun /* parallel mode, 8 bits */
915*4882a593Smuzhiyun lcd.proto = LCD_PROTO_PARALLEL;
916*4882a593Smuzhiyun lcd.charset = LCD_CHARSET_NORMAL;
917*4882a593Smuzhiyun lcd.pins.e = PIN_STROBE;
918*4882a593Smuzhiyun lcd.pins.rs = PIN_AUTOLF;
919*4882a593Smuzhiyun
920*4882a593Smuzhiyun charlcd->width = 40;
921*4882a593Smuzhiyun charlcd->bwidth = 40;
922*4882a593Smuzhiyun charlcd->hwidth = 64;
923*4882a593Smuzhiyun charlcd->height = 2;
924*4882a593Smuzhiyun break;
925*4882a593Smuzhiyun case LCD_TYPE_KS0074:
926*4882a593Smuzhiyun /* serial mode, ks0074 */
927*4882a593Smuzhiyun lcd.proto = LCD_PROTO_SERIAL;
928*4882a593Smuzhiyun lcd.charset = LCD_CHARSET_KS0074;
929*4882a593Smuzhiyun lcd.pins.bl = PIN_AUTOLF;
930*4882a593Smuzhiyun lcd.pins.cl = PIN_STROBE;
931*4882a593Smuzhiyun lcd.pins.da = PIN_D0;
932*4882a593Smuzhiyun
933*4882a593Smuzhiyun charlcd->width = 16;
934*4882a593Smuzhiyun charlcd->bwidth = 40;
935*4882a593Smuzhiyun charlcd->hwidth = 16;
936*4882a593Smuzhiyun charlcd->height = 2;
937*4882a593Smuzhiyun break;
938*4882a593Smuzhiyun case LCD_TYPE_NEXCOM:
939*4882a593Smuzhiyun /* parallel mode, 8 bits, generic */
940*4882a593Smuzhiyun lcd.proto = LCD_PROTO_PARALLEL;
941*4882a593Smuzhiyun lcd.charset = LCD_CHARSET_NORMAL;
942*4882a593Smuzhiyun lcd.pins.e = PIN_AUTOLF;
943*4882a593Smuzhiyun lcd.pins.rs = PIN_SELECP;
944*4882a593Smuzhiyun lcd.pins.rw = PIN_INITP;
945*4882a593Smuzhiyun
946*4882a593Smuzhiyun charlcd->width = 16;
947*4882a593Smuzhiyun charlcd->bwidth = 40;
948*4882a593Smuzhiyun charlcd->hwidth = 64;
949*4882a593Smuzhiyun charlcd->height = 2;
950*4882a593Smuzhiyun break;
951*4882a593Smuzhiyun case LCD_TYPE_CUSTOM:
952*4882a593Smuzhiyun /* customer-defined */
953*4882a593Smuzhiyun lcd.proto = DEFAULT_LCD_PROTO;
954*4882a593Smuzhiyun lcd.charset = DEFAULT_LCD_CHARSET;
955*4882a593Smuzhiyun /* default geometry will be set later */
956*4882a593Smuzhiyun break;
957*4882a593Smuzhiyun case LCD_TYPE_HANTRONIX:
958*4882a593Smuzhiyun /* parallel mode, 8 bits, hantronix-like */
959*4882a593Smuzhiyun default:
960*4882a593Smuzhiyun lcd.proto = LCD_PROTO_PARALLEL;
961*4882a593Smuzhiyun lcd.charset = LCD_CHARSET_NORMAL;
962*4882a593Smuzhiyun lcd.pins.e = PIN_STROBE;
963*4882a593Smuzhiyun lcd.pins.rs = PIN_SELECP;
964*4882a593Smuzhiyun
965*4882a593Smuzhiyun charlcd->width = 16;
966*4882a593Smuzhiyun charlcd->bwidth = 40;
967*4882a593Smuzhiyun charlcd->hwidth = 64;
968*4882a593Smuzhiyun charlcd->height = 2;
969*4882a593Smuzhiyun break;
970*4882a593Smuzhiyun }
971*4882a593Smuzhiyun
972*4882a593Smuzhiyun /* Overwrite with module params set on loading */
973*4882a593Smuzhiyun if (lcd_height != NOT_SET)
974*4882a593Smuzhiyun charlcd->height = lcd_height;
975*4882a593Smuzhiyun if (lcd_width != NOT_SET)
976*4882a593Smuzhiyun charlcd->width = lcd_width;
977*4882a593Smuzhiyun if (lcd_bwidth != NOT_SET)
978*4882a593Smuzhiyun charlcd->bwidth = lcd_bwidth;
979*4882a593Smuzhiyun if (lcd_hwidth != NOT_SET)
980*4882a593Smuzhiyun charlcd->hwidth = lcd_hwidth;
981*4882a593Smuzhiyun if (lcd_charset != NOT_SET)
982*4882a593Smuzhiyun lcd.charset = lcd_charset;
983*4882a593Smuzhiyun if (lcd_proto != NOT_SET)
984*4882a593Smuzhiyun lcd.proto = lcd_proto;
985*4882a593Smuzhiyun if (lcd_e_pin != PIN_NOT_SET)
986*4882a593Smuzhiyun lcd.pins.e = lcd_e_pin;
987*4882a593Smuzhiyun if (lcd_rs_pin != PIN_NOT_SET)
988*4882a593Smuzhiyun lcd.pins.rs = lcd_rs_pin;
989*4882a593Smuzhiyun if (lcd_rw_pin != PIN_NOT_SET)
990*4882a593Smuzhiyun lcd.pins.rw = lcd_rw_pin;
991*4882a593Smuzhiyun if (lcd_cl_pin != PIN_NOT_SET)
992*4882a593Smuzhiyun lcd.pins.cl = lcd_cl_pin;
993*4882a593Smuzhiyun if (lcd_da_pin != PIN_NOT_SET)
994*4882a593Smuzhiyun lcd.pins.da = lcd_da_pin;
995*4882a593Smuzhiyun if (lcd_bl_pin != PIN_NOT_SET)
996*4882a593Smuzhiyun lcd.pins.bl = lcd_bl_pin;
997*4882a593Smuzhiyun
998*4882a593Smuzhiyun /* this is used to catch wrong and default values */
999*4882a593Smuzhiyun if (charlcd->width <= 0)
1000*4882a593Smuzhiyun charlcd->width = DEFAULT_LCD_WIDTH;
1001*4882a593Smuzhiyun if (charlcd->bwidth <= 0)
1002*4882a593Smuzhiyun charlcd->bwidth = DEFAULT_LCD_BWIDTH;
1003*4882a593Smuzhiyun if (charlcd->hwidth <= 0)
1004*4882a593Smuzhiyun charlcd->hwidth = DEFAULT_LCD_HWIDTH;
1005*4882a593Smuzhiyun if (charlcd->height <= 0)
1006*4882a593Smuzhiyun charlcd->height = DEFAULT_LCD_HEIGHT;
1007*4882a593Smuzhiyun
1008*4882a593Smuzhiyun if (lcd.proto == LCD_PROTO_SERIAL) { /* SERIAL */
1009*4882a593Smuzhiyun charlcd->ops = &charlcd_serial_ops;
1010*4882a593Smuzhiyun
1011*4882a593Smuzhiyun if (lcd.pins.cl == PIN_NOT_SET)
1012*4882a593Smuzhiyun lcd.pins.cl = DEFAULT_LCD_PIN_SCL;
1013*4882a593Smuzhiyun if (lcd.pins.da == PIN_NOT_SET)
1014*4882a593Smuzhiyun lcd.pins.da = DEFAULT_LCD_PIN_SDA;
1015*4882a593Smuzhiyun
1016*4882a593Smuzhiyun } else if (lcd.proto == LCD_PROTO_PARALLEL) { /* PARALLEL */
1017*4882a593Smuzhiyun charlcd->ops = &charlcd_parallel_ops;
1018*4882a593Smuzhiyun
1019*4882a593Smuzhiyun if (lcd.pins.e == PIN_NOT_SET)
1020*4882a593Smuzhiyun lcd.pins.e = DEFAULT_LCD_PIN_E;
1021*4882a593Smuzhiyun if (lcd.pins.rs == PIN_NOT_SET)
1022*4882a593Smuzhiyun lcd.pins.rs = DEFAULT_LCD_PIN_RS;
1023*4882a593Smuzhiyun if (lcd.pins.rw == PIN_NOT_SET)
1024*4882a593Smuzhiyun lcd.pins.rw = DEFAULT_LCD_PIN_RW;
1025*4882a593Smuzhiyun } else {
1026*4882a593Smuzhiyun charlcd->ops = &charlcd_tilcd_ops;
1027*4882a593Smuzhiyun }
1028*4882a593Smuzhiyun
1029*4882a593Smuzhiyun if (lcd.pins.bl == PIN_NOT_SET)
1030*4882a593Smuzhiyun lcd.pins.bl = DEFAULT_LCD_PIN_BL;
1031*4882a593Smuzhiyun
1032*4882a593Smuzhiyun if (lcd.pins.e == PIN_NOT_SET)
1033*4882a593Smuzhiyun lcd.pins.e = PIN_NONE;
1034*4882a593Smuzhiyun if (lcd.pins.rs == PIN_NOT_SET)
1035*4882a593Smuzhiyun lcd.pins.rs = PIN_NONE;
1036*4882a593Smuzhiyun if (lcd.pins.rw == PIN_NOT_SET)
1037*4882a593Smuzhiyun lcd.pins.rw = PIN_NONE;
1038*4882a593Smuzhiyun if (lcd.pins.bl == PIN_NOT_SET)
1039*4882a593Smuzhiyun lcd.pins.bl = PIN_NONE;
1040*4882a593Smuzhiyun if (lcd.pins.cl == PIN_NOT_SET)
1041*4882a593Smuzhiyun lcd.pins.cl = PIN_NONE;
1042*4882a593Smuzhiyun if (lcd.pins.da == PIN_NOT_SET)
1043*4882a593Smuzhiyun lcd.pins.da = PIN_NONE;
1044*4882a593Smuzhiyun
1045*4882a593Smuzhiyun if (lcd.charset == NOT_SET)
1046*4882a593Smuzhiyun lcd.charset = DEFAULT_LCD_CHARSET;
1047*4882a593Smuzhiyun
1048*4882a593Smuzhiyun if (lcd.charset == LCD_CHARSET_KS0074)
1049*4882a593Smuzhiyun charlcd->char_conv = lcd_char_conv_ks0074;
1050*4882a593Smuzhiyun else
1051*4882a593Smuzhiyun charlcd->char_conv = NULL;
1052*4882a593Smuzhiyun
1053*4882a593Smuzhiyun pin_to_bits(lcd.pins.e, lcd_bits[LCD_PORT_D][LCD_BIT_E],
1054*4882a593Smuzhiyun lcd_bits[LCD_PORT_C][LCD_BIT_E]);
1055*4882a593Smuzhiyun pin_to_bits(lcd.pins.rs, lcd_bits[LCD_PORT_D][LCD_BIT_RS],
1056*4882a593Smuzhiyun lcd_bits[LCD_PORT_C][LCD_BIT_RS]);
1057*4882a593Smuzhiyun pin_to_bits(lcd.pins.rw, lcd_bits[LCD_PORT_D][LCD_BIT_RW],
1058*4882a593Smuzhiyun lcd_bits[LCD_PORT_C][LCD_BIT_RW]);
1059*4882a593Smuzhiyun pin_to_bits(lcd.pins.bl, lcd_bits[LCD_PORT_D][LCD_BIT_BL],
1060*4882a593Smuzhiyun lcd_bits[LCD_PORT_C][LCD_BIT_BL]);
1061*4882a593Smuzhiyun pin_to_bits(lcd.pins.cl, lcd_bits[LCD_PORT_D][LCD_BIT_CL],
1062*4882a593Smuzhiyun lcd_bits[LCD_PORT_C][LCD_BIT_CL]);
1063*4882a593Smuzhiyun pin_to_bits(lcd.pins.da, lcd_bits[LCD_PORT_D][LCD_BIT_DA],
1064*4882a593Smuzhiyun lcd_bits[LCD_PORT_C][LCD_BIT_DA]);
1065*4882a593Smuzhiyun
1066*4882a593Smuzhiyun lcd.charlcd = charlcd;
1067*4882a593Smuzhiyun lcd.initialized = true;
1068*4882a593Smuzhiyun }
1069*4882a593Smuzhiyun
1070*4882a593Smuzhiyun /*
1071*4882a593Smuzhiyun * These are the file operation function for user access to /dev/keypad
1072*4882a593Smuzhiyun */
1073*4882a593Smuzhiyun
keypad_read(struct file * file,char __user * buf,size_t count,loff_t * ppos)1074*4882a593Smuzhiyun static ssize_t keypad_read(struct file *file,
1075*4882a593Smuzhiyun char __user *buf, size_t count, loff_t *ppos)
1076*4882a593Smuzhiyun {
1077*4882a593Smuzhiyun unsigned i = *ppos;
1078*4882a593Smuzhiyun char __user *tmp = buf;
1079*4882a593Smuzhiyun
1080*4882a593Smuzhiyun if (keypad_buflen == 0) {
1081*4882a593Smuzhiyun if (file->f_flags & O_NONBLOCK)
1082*4882a593Smuzhiyun return -EAGAIN;
1083*4882a593Smuzhiyun
1084*4882a593Smuzhiyun if (wait_event_interruptible(keypad_read_wait,
1085*4882a593Smuzhiyun keypad_buflen != 0))
1086*4882a593Smuzhiyun return -EINTR;
1087*4882a593Smuzhiyun }
1088*4882a593Smuzhiyun
1089*4882a593Smuzhiyun for (; count-- > 0 && (keypad_buflen > 0);
1090*4882a593Smuzhiyun ++i, ++tmp, --keypad_buflen) {
1091*4882a593Smuzhiyun put_user(keypad_buffer[keypad_start], tmp);
1092*4882a593Smuzhiyun keypad_start = (keypad_start + 1) % KEYPAD_BUFFER;
1093*4882a593Smuzhiyun }
1094*4882a593Smuzhiyun *ppos = i;
1095*4882a593Smuzhiyun
1096*4882a593Smuzhiyun return tmp - buf;
1097*4882a593Smuzhiyun }
1098*4882a593Smuzhiyun
keypad_open(struct inode * inode,struct file * file)1099*4882a593Smuzhiyun static int keypad_open(struct inode *inode, struct file *file)
1100*4882a593Smuzhiyun {
1101*4882a593Smuzhiyun int ret;
1102*4882a593Smuzhiyun
1103*4882a593Smuzhiyun ret = -EBUSY;
1104*4882a593Smuzhiyun if (!atomic_dec_and_test(&keypad_available))
1105*4882a593Smuzhiyun goto fail; /* open only once at a time */
1106*4882a593Smuzhiyun
1107*4882a593Smuzhiyun ret = -EPERM;
1108*4882a593Smuzhiyun if (file->f_mode & FMODE_WRITE) /* device is read-only */
1109*4882a593Smuzhiyun goto fail;
1110*4882a593Smuzhiyun
1111*4882a593Smuzhiyun keypad_buflen = 0; /* flush the buffer on opening */
1112*4882a593Smuzhiyun return 0;
1113*4882a593Smuzhiyun fail:
1114*4882a593Smuzhiyun atomic_inc(&keypad_available);
1115*4882a593Smuzhiyun return ret;
1116*4882a593Smuzhiyun }
1117*4882a593Smuzhiyun
keypad_release(struct inode * inode,struct file * file)1118*4882a593Smuzhiyun static int keypad_release(struct inode *inode, struct file *file)
1119*4882a593Smuzhiyun {
1120*4882a593Smuzhiyun atomic_inc(&keypad_available);
1121*4882a593Smuzhiyun return 0;
1122*4882a593Smuzhiyun }
1123*4882a593Smuzhiyun
1124*4882a593Smuzhiyun static const struct file_operations keypad_fops = {
1125*4882a593Smuzhiyun .read = keypad_read, /* read */
1126*4882a593Smuzhiyun .open = keypad_open, /* open */
1127*4882a593Smuzhiyun .release = keypad_release, /* close */
1128*4882a593Smuzhiyun .llseek = default_llseek,
1129*4882a593Smuzhiyun };
1130*4882a593Smuzhiyun
1131*4882a593Smuzhiyun static struct miscdevice keypad_dev = {
1132*4882a593Smuzhiyun .minor = KEYPAD_MINOR,
1133*4882a593Smuzhiyun .name = "keypad",
1134*4882a593Smuzhiyun .fops = &keypad_fops,
1135*4882a593Smuzhiyun };
1136*4882a593Smuzhiyun
keypad_send_key(const char * string,int max_len)1137*4882a593Smuzhiyun static void keypad_send_key(const char *string, int max_len)
1138*4882a593Smuzhiyun {
1139*4882a593Smuzhiyun /* send the key to the device only if a process is attached to it. */
1140*4882a593Smuzhiyun if (!atomic_read(&keypad_available)) {
1141*4882a593Smuzhiyun while (max_len-- && keypad_buflen < KEYPAD_BUFFER && *string) {
1142*4882a593Smuzhiyun keypad_buffer[(keypad_start + keypad_buflen++) %
1143*4882a593Smuzhiyun KEYPAD_BUFFER] = *string++;
1144*4882a593Smuzhiyun }
1145*4882a593Smuzhiyun wake_up_interruptible(&keypad_read_wait);
1146*4882a593Smuzhiyun }
1147*4882a593Smuzhiyun }
1148*4882a593Smuzhiyun
1149*4882a593Smuzhiyun /* this function scans all the bits involving at least one logical signal,
1150*4882a593Smuzhiyun * and puts the results in the bitfield "phys_read" (one bit per established
1151*4882a593Smuzhiyun * contact), and sets "phys_read_prev" to "phys_read".
1152*4882a593Smuzhiyun *
1153*4882a593Smuzhiyun * Note: to debounce input signals, we will only consider as switched a signal
1154*4882a593Smuzhiyun * which is stable across 2 measures. Signals which are different between two
1155*4882a593Smuzhiyun * reads will be kept as they previously were in their logical form (phys_prev).
1156*4882a593Smuzhiyun * A signal which has just switched will have a 1 in
1157*4882a593Smuzhiyun * (phys_read ^ phys_read_prev).
1158*4882a593Smuzhiyun */
phys_scan_contacts(void)1159*4882a593Smuzhiyun static void phys_scan_contacts(void)
1160*4882a593Smuzhiyun {
1161*4882a593Smuzhiyun int bit, bitval;
1162*4882a593Smuzhiyun char oldval;
1163*4882a593Smuzhiyun char bitmask;
1164*4882a593Smuzhiyun char gndmask;
1165*4882a593Smuzhiyun
1166*4882a593Smuzhiyun phys_prev = phys_curr;
1167*4882a593Smuzhiyun phys_read_prev = phys_read;
1168*4882a593Smuzhiyun phys_read = 0; /* flush all signals */
1169*4882a593Smuzhiyun
1170*4882a593Smuzhiyun /* keep track of old value, with all outputs disabled */
1171*4882a593Smuzhiyun oldval = r_dtr(pprt) | scan_mask_o;
1172*4882a593Smuzhiyun /* activate all keyboard outputs (active low) */
1173*4882a593Smuzhiyun w_dtr(pprt, oldval & ~scan_mask_o);
1174*4882a593Smuzhiyun
1175*4882a593Smuzhiyun /* will have a 1 for each bit set to gnd */
1176*4882a593Smuzhiyun bitmask = PNL_PINPUT(r_str(pprt)) & scan_mask_i;
1177*4882a593Smuzhiyun /* disable all matrix signals */
1178*4882a593Smuzhiyun w_dtr(pprt, oldval);
1179*4882a593Smuzhiyun
1180*4882a593Smuzhiyun /* now that all outputs are cleared, the only active input bits are
1181*4882a593Smuzhiyun * directly connected to the ground
1182*4882a593Smuzhiyun */
1183*4882a593Smuzhiyun
1184*4882a593Smuzhiyun /* 1 for each grounded input */
1185*4882a593Smuzhiyun gndmask = PNL_PINPUT(r_str(pprt)) & scan_mask_i;
1186*4882a593Smuzhiyun
1187*4882a593Smuzhiyun /* grounded inputs are signals 40-44 */
1188*4882a593Smuzhiyun phys_read |= (__u64)gndmask << 40;
1189*4882a593Smuzhiyun
1190*4882a593Smuzhiyun if (bitmask != gndmask) {
1191*4882a593Smuzhiyun /*
1192*4882a593Smuzhiyun * since clearing the outputs changed some inputs, we know
1193*4882a593Smuzhiyun * that some input signals are currently tied to some outputs.
1194*4882a593Smuzhiyun * So we'll scan them.
1195*4882a593Smuzhiyun */
1196*4882a593Smuzhiyun for (bit = 0; bit < 8; bit++) {
1197*4882a593Smuzhiyun bitval = BIT(bit);
1198*4882a593Smuzhiyun
1199*4882a593Smuzhiyun if (!(scan_mask_o & bitval)) /* skip unused bits */
1200*4882a593Smuzhiyun continue;
1201*4882a593Smuzhiyun
1202*4882a593Smuzhiyun w_dtr(pprt, oldval & ~bitval); /* enable this output */
1203*4882a593Smuzhiyun bitmask = PNL_PINPUT(r_str(pprt)) & ~gndmask;
1204*4882a593Smuzhiyun phys_read |= (__u64)bitmask << (5 * bit);
1205*4882a593Smuzhiyun }
1206*4882a593Smuzhiyun w_dtr(pprt, oldval); /* disable all outputs */
1207*4882a593Smuzhiyun }
1208*4882a593Smuzhiyun /*
1209*4882a593Smuzhiyun * this is easy: use old bits when they are flapping,
1210*4882a593Smuzhiyun * use new ones when stable
1211*4882a593Smuzhiyun */
1212*4882a593Smuzhiyun phys_curr = (phys_prev & (phys_read ^ phys_read_prev)) |
1213*4882a593Smuzhiyun (phys_read & ~(phys_read ^ phys_read_prev));
1214*4882a593Smuzhiyun }
1215*4882a593Smuzhiyun
input_state_high(struct logical_input * input)1216*4882a593Smuzhiyun static inline int input_state_high(struct logical_input *input)
1217*4882a593Smuzhiyun {
1218*4882a593Smuzhiyun #if 0
1219*4882a593Smuzhiyun /* FIXME:
1220*4882a593Smuzhiyun * this is an invalid test. It tries to catch
1221*4882a593Smuzhiyun * transitions from single-key to multiple-key, but
1222*4882a593Smuzhiyun * doesn't take into account the contacts polarity.
1223*4882a593Smuzhiyun * The only solution to the problem is to parse keys
1224*4882a593Smuzhiyun * from the most complex to the simplest combinations,
1225*4882a593Smuzhiyun * and mark them as 'caught' once a combination
1226*4882a593Smuzhiyun * matches, then unmatch it for all other ones.
1227*4882a593Smuzhiyun */
1228*4882a593Smuzhiyun
1229*4882a593Smuzhiyun /* try to catch dangerous transitions cases :
1230*4882a593Smuzhiyun * someone adds a bit, so this signal was a false
1231*4882a593Smuzhiyun * positive resulting from a transition. We should
1232*4882a593Smuzhiyun * invalidate the signal immediately and not call the
1233*4882a593Smuzhiyun * release function.
1234*4882a593Smuzhiyun * eg: 0 -(press A)-> A -(press B)-> AB : don't match A's release.
1235*4882a593Smuzhiyun */
1236*4882a593Smuzhiyun if (((phys_prev & input->mask) == input->value) &&
1237*4882a593Smuzhiyun ((phys_curr & input->mask) > input->value)) {
1238*4882a593Smuzhiyun input->state = INPUT_ST_LOW; /* invalidate */
1239*4882a593Smuzhiyun return 1;
1240*4882a593Smuzhiyun }
1241*4882a593Smuzhiyun #endif
1242*4882a593Smuzhiyun
1243*4882a593Smuzhiyun if ((phys_curr & input->mask) == input->value) {
1244*4882a593Smuzhiyun if ((input->type == INPUT_TYPE_STD) &&
1245*4882a593Smuzhiyun (input->high_timer == 0)) {
1246*4882a593Smuzhiyun input->high_timer++;
1247*4882a593Smuzhiyun if (input->u.std.press_fct)
1248*4882a593Smuzhiyun input->u.std.press_fct(input->u.std.press_data);
1249*4882a593Smuzhiyun } else if (input->type == INPUT_TYPE_KBD) {
1250*4882a593Smuzhiyun /* will turn on the light */
1251*4882a593Smuzhiyun keypressed = 1;
1252*4882a593Smuzhiyun
1253*4882a593Smuzhiyun if (input->high_timer == 0) {
1254*4882a593Smuzhiyun char *press_str = input->u.kbd.press_str;
1255*4882a593Smuzhiyun
1256*4882a593Smuzhiyun if (press_str[0]) {
1257*4882a593Smuzhiyun int s = sizeof(input->u.kbd.press_str);
1258*4882a593Smuzhiyun
1259*4882a593Smuzhiyun keypad_send_key(press_str, s);
1260*4882a593Smuzhiyun }
1261*4882a593Smuzhiyun }
1262*4882a593Smuzhiyun
1263*4882a593Smuzhiyun if (input->u.kbd.repeat_str[0]) {
1264*4882a593Smuzhiyun char *repeat_str = input->u.kbd.repeat_str;
1265*4882a593Smuzhiyun
1266*4882a593Smuzhiyun if (input->high_timer >= KEYPAD_REP_START) {
1267*4882a593Smuzhiyun int s = sizeof(input->u.kbd.repeat_str);
1268*4882a593Smuzhiyun
1269*4882a593Smuzhiyun input->high_timer -= KEYPAD_REP_DELAY;
1270*4882a593Smuzhiyun keypad_send_key(repeat_str, s);
1271*4882a593Smuzhiyun }
1272*4882a593Smuzhiyun /* we will need to come back here soon */
1273*4882a593Smuzhiyun inputs_stable = 0;
1274*4882a593Smuzhiyun }
1275*4882a593Smuzhiyun
1276*4882a593Smuzhiyun if (input->high_timer < 255)
1277*4882a593Smuzhiyun input->high_timer++;
1278*4882a593Smuzhiyun }
1279*4882a593Smuzhiyun return 1;
1280*4882a593Smuzhiyun }
1281*4882a593Smuzhiyun
1282*4882a593Smuzhiyun /* else signal falling down. Let's fall through. */
1283*4882a593Smuzhiyun input->state = INPUT_ST_FALLING;
1284*4882a593Smuzhiyun input->fall_timer = 0;
1285*4882a593Smuzhiyun
1286*4882a593Smuzhiyun return 0;
1287*4882a593Smuzhiyun }
1288*4882a593Smuzhiyun
input_state_falling(struct logical_input * input)1289*4882a593Smuzhiyun static inline void input_state_falling(struct logical_input *input)
1290*4882a593Smuzhiyun {
1291*4882a593Smuzhiyun #if 0
1292*4882a593Smuzhiyun /* FIXME !!! same comment as in input_state_high */
1293*4882a593Smuzhiyun if (((phys_prev & input->mask) == input->value) &&
1294*4882a593Smuzhiyun ((phys_curr & input->mask) > input->value)) {
1295*4882a593Smuzhiyun input->state = INPUT_ST_LOW; /* invalidate */
1296*4882a593Smuzhiyun return;
1297*4882a593Smuzhiyun }
1298*4882a593Smuzhiyun #endif
1299*4882a593Smuzhiyun
1300*4882a593Smuzhiyun if ((phys_curr & input->mask) == input->value) {
1301*4882a593Smuzhiyun if (input->type == INPUT_TYPE_KBD) {
1302*4882a593Smuzhiyun /* will turn on the light */
1303*4882a593Smuzhiyun keypressed = 1;
1304*4882a593Smuzhiyun
1305*4882a593Smuzhiyun if (input->u.kbd.repeat_str[0]) {
1306*4882a593Smuzhiyun char *repeat_str = input->u.kbd.repeat_str;
1307*4882a593Smuzhiyun
1308*4882a593Smuzhiyun if (input->high_timer >= KEYPAD_REP_START) {
1309*4882a593Smuzhiyun int s = sizeof(input->u.kbd.repeat_str);
1310*4882a593Smuzhiyun
1311*4882a593Smuzhiyun input->high_timer -= KEYPAD_REP_DELAY;
1312*4882a593Smuzhiyun keypad_send_key(repeat_str, s);
1313*4882a593Smuzhiyun }
1314*4882a593Smuzhiyun /* we will need to come back here soon */
1315*4882a593Smuzhiyun inputs_stable = 0;
1316*4882a593Smuzhiyun }
1317*4882a593Smuzhiyun
1318*4882a593Smuzhiyun if (input->high_timer < 255)
1319*4882a593Smuzhiyun input->high_timer++;
1320*4882a593Smuzhiyun }
1321*4882a593Smuzhiyun input->state = INPUT_ST_HIGH;
1322*4882a593Smuzhiyun } else if (input->fall_timer >= input->fall_time) {
1323*4882a593Smuzhiyun /* call release event */
1324*4882a593Smuzhiyun if (input->type == INPUT_TYPE_STD) {
1325*4882a593Smuzhiyun void (*release_fct)(int) = input->u.std.release_fct;
1326*4882a593Smuzhiyun
1327*4882a593Smuzhiyun if (release_fct)
1328*4882a593Smuzhiyun release_fct(input->u.std.release_data);
1329*4882a593Smuzhiyun } else if (input->type == INPUT_TYPE_KBD) {
1330*4882a593Smuzhiyun char *release_str = input->u.kbd.release_str;
1331*4882a593Smuzhiyun
1332*4882a593Smuzhiyun if (release_str[0]) {
1333*4882a593Smuzhiyun int s = sizeof(input->u.kbd.release_str);
1334*4882a593Smuzhiyun
1335*4882a593Smuzhiyun keypad_send_key(release_str, s);
1336*4882a593Smuzhiyun }
1337*4882a593Smuzhiyun }
1338*4882a593Smuzhiyun
1339*4882a593Smuzhiyun input->state = INPUT_ST_LOW;
1340*4882a593Smuzhiyun } else {
1341*4882a593Smuzhiyun input->fall_timer++;
1342*4882a593Smuzhiyun inputs_stable = 0;
1343*4882a593Smuzhiyun }
1344*4882a593Smuzhiyun }
1345*4882a593Smuzhiyun
panel_process_inputs(void)1346*4882a593Smuzhiyun static void panel_process_inputs(void)
1347*4882a593Smuzhiyun {
1348*4882a593Smuzhiyun struct logical_input *input;
1349*4882a593Smuzhiyun
1350*4882a593Smuzhiyun keypressed = 0;
1351*4882a593Smuzhiyun inputs_stable = 1;
1352*4882a593Smuzhiyun list_for_each_entry(input, &logical_inputs, list) {
1353*4882a593Smuzhiyun switch (input->state) {
1354*4882a593Smuzhiyun case INPUT_ST_LOW:
1355*4882a593Smuzhiyun if ((phys_curr & input->mask) != input->value)
1356*4882a593Smuzhiyun break;
1357*4882a593Smuzhiyun /* if all needed ones were already set previously,
1358*4882a593Smuzhiyun * this means that this logical signal has been
1359*4882a593Smuzhiyun * activated by the releasing of another combined
1360*4882a593Smuzhiyun * signal, so we don't want to match.
1361*4882a593Smuzhiyun * eg: AB -(release B)-> A -(release A)-> 0 :
1362*4882a593Smuzhiyun * don't match A.
1363*4882a593Smuzhiyun */
1364*4882a593Smuzhiyun if ((phys_prev & input->mask) == input->value)
1365*4882a593Smuzhiyun break;
1366*4882a593Smuzhiyun input->rise_timer = 0;
1367*4882a593Smuzhiyun input->state = INPUT_ST_RISING;
1368*4882a593Smuzhiyun fallthrough;
1369*4882a593Smuzhiyun case INPUT_ST_RISING:
1370*4882a593Smuzhiyun if ((phys_curr & input->mask) != input->value) {
1371*4882a593Smuzhiyun input->state = INPUT_ST_LOW;
1372*4882a593Smuzhiyun break;
1373*4882a593Smuzhiyun }
1374*4882a593Smuzhiyun if (input->rise_timer < input->rise_time) {
1375*4882a593Smuzhiyun inputs_stable = 0;
1376*4882a593Smuzhiyun input->rise_timer++;
1377*4882a593Smuzhiyun break;
1378*4882a593Smuzhiyun }
1379*4882a593Smuzhiyun input->high_timer = 0;
1380*4882a593Smuzhiyun input->state = INPUT_ST_HIGH;
1381*4882a593Smuzhiyun fallthrough;
1382*4882a593Smuzhiyun case INPUT_ST_HIGH:
1383*4882a593Smuzhiyun if (input_state_high(input))
1384*4882a593Smuzhiyun break;
1385*4882a593Smuzhiyun fallthrough;
1386*4882a593Smuzhiyun case INPUT_ST_FALLING:
1387*4882a593Smuzhiyun input_state_falling(input);
1388*4882a593Smuzhiyun }
1389*4882a593Smuzhiyun }
1390*4882a593Smuzhiyun }
1391*4882a593Smuzhiyun
panel_scan_timer(struct timer_list * unused)1392*4882a593Smuzhiyun static void panel_scan_timer(struct timer_list *unused)
1393*4882a593Smuzhiyun {
1394*4882a593Smuzhiyun if (keypad.enabled && keypad_initialized) {
1395*4882a593Smuzhiyun if (spin_trylock_irq(&pprt_lock)) {
1396*4882a593Smuzhiyun phys_scan_contacts();
1397*4882a593Smuzhiyun
1398*4882a593Smuzhiyun /* no need for the parport anymore */
1399*4882a593Smuzhiyun spin_unlock_irq(&pprt_lock);
1400*4882a593Smuzhiyun }
1401*4882a593Smuzhiyun
1402*4882a593Smuzhiyun if (!inputs_stable || phys_curr != phys_prev)
1403*4882a593Smuzhiyun panel_process_inputs();
1404*4882a593Smuzhiyun }
1405*4882a593Smuzhiyun
1406*4882a593Smuzhiyun if (keypressed && lcd.enabled && lcd.initialized)
1407*4882a593Smuzhiyun charlcd_poke(lcd.charlcd);
1408*4882a593Smuzhiyun
1409*4882a593Smuzhiyun mod_timer(&scan_timer, jiffies + INPUT_POLL_TIME);
1410*4882a593Smuzhiyun }
1411*4882a593Smuzhiyun
init_scan_timer(void)1412*4882a593Smuzhiyun static void init_scan_timer(void)
1413*4882a593Smuzhiyun {
1414*4882a593Smuzhiyun if (scan_timer.function)
1415*4882a593Smuzhiyun return; /* already started */
1416*4882a593Smuzhiyun
1417*4882a593Smuzhiyun timer_setup(&scan_timer, panel_scan_timer, 0);
1418*4882a593Smuzhiyun scan_timer.expires = jiffies + INPUT_POLL_TIME;
1419*4882a593Smuzhiyun add_timer(&scan_timer);
1420*4882a593Smuzhiyun }
1421*4882a593Smuzhiyun
1422*4882a593Smuzhiyun /* converts a name of the form "({BbAaPpSsEe}{01234567-})*" to a series of bits.
1423*4882a593Smuzhiyun * if <omask> or <imask> are non-null, they will be or'ed with the bits
1424*4882a593Smuzhiyun * corresponding to out and in bits respectively.
1425*4882a593Smuzhiyun * returns 1 if ok, 0 if error (in which case, nothing is written).
1426*4882a593Smuzhiyun */
input_name2mask(const char * name,__u64 * mask,__u64 * value,u8 * imask,u8 * omask)1427*4882a593Smuzhiyun static u8 input_name2mask(const char *name, __u64 *mask, __u64 *value,
1428*4882a593Smuzhiyun u8 *imask, u8 *omask)
1429*4882a593Smuzhiyun {
1430*4882a593Smuzhiyun const char sigtab[] = "EeSsPpAaBb";
1431*4882a593Smuzhiyun u8 im, om;
1432*4882a593Smuzhiyun __u64 m, v;
1433*4882a593Smuzhiyun
1434*4882a593Smuzhiyun om = 0;
1435*4882a593Smuzhiyun im = 0;
1436*4882a593Smuzhiyun m = 0ULL;
1437*4882a593Smuzhiyun v = 0ULL;
1438*4882a593Smuzhiyun while (*name) {
1439*4882a593Smuzhiyun int in, out, bit, neg;
1440*4882a593Smuzhiyun const char *idx;
1441*4882a593Smuzhiyun
1442*4882a593Smuzhiyun idx = strchr(sigtab, *name);
1443*4882a593Smuzhiyun if (!idx)
1444*4882a593Smuzhiyun return 0; /* input name not found */
1445*4882a593Smuzhiyun
1446*4882a593Smuzhiyun in = idx - sigtab;
1447*4882a593Smuzhiyun neg = (in & 1); /* odd (lower) names are negated */
1448*4882a593Smuzhiyun in >>= 1;
1449*4882a593Smuzhiyun im |= BIT(in);
1450*4882a593Smuzhiyun
1451*4882a593Smuzhiyun name++;
1452*4882a593Smuzhiyun if (*name >= '0' && *name <= '7') {
1453*4882a593Smuzhiyun out = *name - '0';
1454*4882a593Smuzhiyun om |= BIT(out);
1455*4882a593Smuzhiyun } else if (*name == '-') {
1456*4882a593Smuzhiyun out = 8;
1457*4882a593Smuzhiyun } else {
1458*4882a593Smuzhiyun return 0; /* unknown bit name */
1459*4882a593Smuzhiyun }
1460*4882a593Smuzhiyun
1461*4882a593Smuzhiyun bit = (out * 5) + in;
1462*4882a593Smuzhiyun
1463*4882a593Smuzhiyun m |= 1ULL << bit;
1464*4882a593Smuzhiyun if (!neg)
1465*4882a593Smuzhiyun v |= 1ULL << bit;
1466*4882a593Smuzhiyun name++;
1467*4882a593Smuzhiyun }
1468*4882a593Smuzhiyun *mask = m;
1469*4882a593Smuzhiyun *value = v;
1470*4882a593Smuzhiyun if (imask)
1471*4882a593Smuzhiyun *imask |= im;
1472*4882a593Smuzhiyun if (omask)
1473*4882a593Smuzhiyun *omask |= om;
1474*4882a593Smuzhiyun return 1;
1475*4882a593Smuzhiyun }
1476*4882a593Smuzhiyun
1477*4882a593Smuzhiyun /* tries to bind a key to the signal name <name>. The key will send the
1478*4882a593Smuzhiyun * strings <press>, <repeat>, <release> for these respective events.
1479*4882a593Smuzhiyun * Returns the pointer to the new key if ok, NULL if the key could not be bound.
1480*4882a593Smuzhiyun */
panel_bind_key(const char * name,const char * press,const char * repeat,const char * release)1481*4882a593Smuzhiyun static struct logical_input *panel_bind_key(const char *name, const char *press,
1482*4882a593Smuzhiyun const char *repeat,
1483*4882a593Smuzhiyun const char *release)
1484*4882a593Smuzhiyun {
1485*4882a593Smuzhiyun struct logical_input *key;
1486*4882a593Smuzhiyun
1487*4882a593Smuzhiyun key = kzalloc(sizeof(*key), GFP_KERNEL);
1488*4882a593Smuzhiyun if (!key)
1489*4882a593Smuzhiyun return NULL;
1490*4882a593Smuzhiyun
1491*4882a593Smuzhiyun if (!input_name2mask(name, &key->mask, &key->value, &scan_mask_i,
1492*4882a593Smuzhiyun &scan_mask_o)) {
1493*4882a593Smuzhiyun kfree(key);
1494*4882a593Smuzhiyun return NULL;
1495*4882a593Smuzhiyun }
1496*4882a593Smuzhiyun
1497*4882a593Smuzhiyun key->type = INPUT_TYPE_KBD;
1498*4882a593Smuzhiyun key->state = INPUT_ST_LOW;
1499*4882a593Smuzhiyun key->rise_time = 1;
1500*4882a593Smuzhiyun key->fall_time = 1;
1501*4882a593Smuzhiyun
1502*4882a593Smuzhiyun strncpy(key->u.kbd.press_str, press, sizeof(key->u.kbd.press_str));
1503*4882a593Smuzhiyun strncpy(key->u.kbd.repeat_str, repeat, sizeof(key->u.kbd.repeat_str));
1504*4882a593Smuzhiyun strncpy(key->u.kbd.release_str, release,
1505*4882a593Smuzhiyun sizeof(key->u.kbd.release_str));
1506*4882a593Smuzhiyun list_add(&key->list, &logical_inputs);
1507*4882a593Smuzhiyun return key;
1508*4882a593Smuzhiyun }
1509*4882a593Smuzhiyun
1510*4882a593Smuzhiyun #if 0
1511*4882a593Smuzhiyun /* tries to bind a callback function to the signal name <name>. The function
1512*4882a593Smuzhiyun * <press_fct> will be called with the <press_data> arg when the signal is
1513*4882a593Smuzhiyun * activated, and so on for <release_fct>/<release_data>
1514*4882a593Smuzhiyun * Returns the pointer to the new signal if ok, NULL if the signal could not
1515*4882a593Smuzhiyun * be bound.
1516*4882a593Smuzhiyun */
1517*4882a593Smuzhiyun static struct logical_input *panel_bind_callback(char *name,
1518*4882a593Smuzhiyun void (*press_fct)(int),
1519*4882a593Smuzhiyun int press_data,
1520*4882a593Smuzhiyun void (*release_fct)(int),
1521*4882a593Smuzhiyun int release_data)
1522*4882a593Smuzhiyun {
1523*4882a593Smuzhiyun struct logical_input *callback;
1524*4882a593Smuzhiyun
1525*4882a593Smuzhiyun callback = kmalloc(sizeof(*callback), GFP_KERNEL);
1526*4882a593Smuzhiyun if (!callback)
1527*4882a593Smuzhiyun return NULL;
1528*4882a593Smuzhiyun
1529*4882a593Smuzhiyun memset(callback, 0, sizeof(struct logical_input));
1530*4882a593Smuzhiyun if (!input_name2mask(name, &callback->mask, &callback->value,
1531*4882a593Smuzhiyun &scan_mask_i, &scan_mask_o))
1532*4882a593Smuzhiyun return NULL;
1533*4882a593Smuzhiyun
1534*4882a593Smuzhiyun callback->type = INPUT_TYPE_STD;
1535*4882a593Smuzhiyun callback->state = INPUT_ST_LOW;
1536*4882a593Smuzhiyun callback->rise_time = 1;
1537*4882a593Smuzhiyun callback->fall_time = 1;
1538*4882a593Smuzhiyun callback->u.std.press_fct = press_fct;
1539*4882a593Smuzhiyun callback->u.std.press_data = press_data;
1540*4882a593Smuzhiyun callback->u.std.release_fct = release_fct;
1541*4882a593Smuzhiyun callback->u.std.release_data = release_data;
1542*4882a593Smuzhiyun list_add(&callback->list, &logical_inputs);
1543*4882a593Smuzhiyun return callback;
1544*4882a593Smuzhiyun }
1545*4882a593Smuzhiyun #endif
1546*4882a593Smuzhiyun
keypad_init(void)1547*4882a593Smuzhiyun static void keypad_init(void)
1548*4882a593Smuzhiyun {
1549*4882a593Smuzhiyun int keynum;
1550*4882a593Smuzhiyun
1551*4882a593Smuzhiyun init_waitqueue_head(&keypad_read_wait);
1552*4882a593Smuzhiyun keypad_buflen = 0; /* flushes any eventual noisy keystroke */
1553*4882a593Smuzhiyun
1554*4882a593Smuzhiyun /* Let's create all known keys */
1555*4882a593Smuzhiyun
1556*4882a593Smuzhiyun for (keynum = 0; keypad_profile[keynum][0][0]; keynum++) {
1557*4882a593Smuzhiyun panel_bind_key(keypad_profile[keynum][0],
1558*4882a593Smuzhiyun keypad_profile[keynum][1],
1559*4882a593Smuzhiyun keypad_profile[keynum][2],
1560*4882a593Smuzhiyun keypad_profile[keynum][3]);
1561*4882a593Smuzhiyun }
1562*4882a593Smuzhiyun
1563*4882a593Smuzhiyun init_scan_timer();
1564*4882a593Smuzhiyun keypad_initialized = 1;
1565*4882a593Smuzhiyun }
1566*4882a593Smuzhiyun
1567*4882a593Smuzhiyun /**************************************************/
1568*4882a593Smuzhiyun /* device initialization */
1569*4882a593Smuzhiyun /**************************************************/
1570*4882a593Smuzhiyun
panel_attach(struct parport * port)1571*4882a593Smuzhiyun static void panel_attach(struct parport *port)
1572*4882a593Smuzhiyun {
1573*4882a593Smuzhiyun struct pardev_cb panel_cb;
1574*4882a593Smuzhiyun
1575*4882a593Smuzhiyun if (port->number != parport)
1576*4882a593Smuzhiyun return;
1577*4882a593Smuzhiyun
1578*4882a593Smuzhiyun if (pprt) {
1579*4882a593Smuzhiyun pr_err("%s: port->number=%d parport=%d, already registered!\n",
1580*4882a593Smuzhiyun __func__, port->number, parport);
1581*4882a593Smuzhiyun return;
1582*4882a593Smuzhiyun }
1583*4882a593Smuzhiyun
1584*4882a593Smuzhiyun memset(&panel_cb, 0, sizeof(panel_cb));
1585*4882a593Smuzhiyun panel_cb.private = &pprt;
1586*4882a593Smuzhiyun /* panel_cb.flags = 0 should be PARPORT_DEV_EXCL? */
1587*4882a593Smuzhiyun
1588*4882a593Smuzhiyun pprt = parport_register_dev_model(port, "panel", &panel_cb, 0);
1589*4882a593Smuzhiyun if (!pprt) {
1590*4882a593Smuzhiyun pr_err("%s: port->number=%d parport=%d, parport_register_device() failed\n",
1591*4882a593Smuzhiyun __func__, port->number, parport);
1592*4882a593Smuzhiyun return;
1593*4882a593Smuzhiyun }
1594*4882a593Smuzhiyun
1595*4882a593Smuzhiyun if (parport_claim(pprt)) {
1596*4882a593Smuzhiyun pr_err("could not claim access to parport%d. Aborting.\n",
1597*4882a593Smuzhiyun parport);
1598*4882a593Smuzhiyun goto err_unreg_device;
1599*4882a593Smuzhiyun }
1600*4882a593Smuzhiyun
1601*4882a593Smuzhiyun /* must init LCD first, just in case an IRQ from the keypad is
1602*4882a593Smuzhiyun * generated at keypad init
1603*4882a593Smuzhiyun */
1604*4882a593Smuzhiyun if (lcd.enabled) {
1605*4882a593Smuzhiyun lcd_init();
1606*4882a593Smuzhiyun if (!lcd.charlcd || charlcd_register(lcd.charlcd))
1607*4882a593Smuzhiyun goto err_unreg_device;
1608*4882a593Smuzhiyun }
1609*4882a593Smuzhiyun
1610*4882a593Smuzhiyun if (keypad.enabled) {
1611*4882a593Smuzhiyun keypad_init();
1612*4882a593Smuzhiyun if (misc_register(&keypad_dev))
1613*4882a593Smuzhiyun goto err_lcd_unreg;
1614*4882a593Smuzhiyun }
1615*4882a593Smuzhiyun return;
1616*4882a593Smuzhiyun
1617*4882a593Smuzhiyun err_lcd_unreg:
1618*4882a593Smuzhiyun if (scan_timer.function)
1619*4882a593Smuzhiyun del_timer_sync(&scan_timer);
1620*4882a593Smuzhiyun if (lcd.enabled)
1621*4882a593Smuzhiyun charlcd_unregister(lcd.charlcd);
1622*4882a593Smuzhiyun err_unreg_device:
1623*4882a593Smuzhiyun charlcd_free(lcd.charlcd);
1624*4882a593Smuzhiyun lcd.charlcd = NULL;
1625*4882a593Smuzhiyun parport_unregister_device(pprt);
1626*4882a593Smuzhiyun pprt = NULL;
1627*4882a593Smuzhiyun }
1628*4882a593Smuzhiyun
panel_detach(struct parport * port)1629*4882a593Smuzhiyun static void panel_detach(struct parport *port)
1630*4882a593Smuzhiyun {
1631*4882a593Smuzhiyun if (port->number != parport)
1632*4882a593Smuzhiyun return;
1633*4882a593Smuzhiyun
1634*4882a593Smuzhiyun if (!pprt) {
1635*4882a593Smuzhiyun pr_err("%s: port->number=%d parport=%d, nothing to unregister.\n",
1636*4882a593Smuzhiyun __func__, port->number, parport);
1637*4882a593Smuzhiyun return;
1638*4882a593Smuzhiyun }
1639*4882a593Smuzhiyun if (scan_timer.function)
1640*4882a593Smuzhiyun del_timer_sync(&scan_timer);
1641*4882a593Smuzhiyun
1642*4882a593Smuzhiyun if (keypad.enabled) {
1643*4882a593Smuzhiyun misc_deregister(&keypad_dev);
1644*4882a593Smuzhiyun keypad_initialized = 0;
1645*4882a593Smuzhiyun }
1646*4882a593Smuzhiyun
1647*4882a593Smuzhiyun if (lcd.enabled) {
1648*4882a593Smuzhiyun charlcd_unregister(lcd.charlcd);
1649*4882a593Smuzhiyun lcd.initialized = false;
1650*4882a593Smuzhiyun charlcd_free(lcd.charlcd);
1651*4882a593Smuzhiyun lcd.charlcd = NULL;
1652*4882a593Smuzhiyun }
1653*4882a593Smuzhiyun
1654*4882a593Smuzhiyun /* TODO: free all input signals */
1655*4882a593Smuzhiyun parport_release(pprt);
1656*4882a593Smuzhiyun parport_unregister_device(pprt);
1657*4882a593Smuzhiyun pprt = NULL;
1658*4882a593Smuzhiyun }
1659*4882a593Smuzhiyun
1660*4882a593Smuzhiyun static struct parport_driver panel_driver = {
1661*4882a593Smuzhiyun .name = "panel",
1662*4882a593Smuzhiyun .match_port = panel_attach,
1663*4882a593Smuzhiyun .detach = panel_detach,
1664*4882a593Smuzhiyun .devmodel = true,
1665*4882a593Smuzhiyun };
1666*4882a593Smuzhiyun
1667*4882a593Smuzhiyun /* init function */
panel_init_module(void)1668*4882a593Smuzhiyun static int __init panel_init_module(void)
1669*4882a593Smuzhiyun {
1670*4882a593Smuzhiyun int selected_keypad_type = NOT_SET, err;
1671*4882a593Smuzhiyun
1672*4882a593Smuzhiyun /* take care of an eventual profile */
1673*4882a593Smuzhiyun switch (profile) {
1674*4882a593Smuzhiyun case PANEL_PROFILE_CUSTOM:
1675*4882a593Smuzhiyun /* custom profile */
1676*4882a593Smuzhiyun selected_keypad_type = DEFAULT_KEYPAD_TYPE;
1677*4882a593Smuzhiyun selected_lcd_type = DEFAULT_LCD_TYPE;
1678*4882a593Smuzhiyun break;
1679*4882a593Smuzhiyun case PANEL_PROFILE_OLD:
1680*4882a593Smuzhiyun /* 8 bits, 2*16, old keypad */
1681*4882a593Smuzhiyun selected_keypad_type = KEYPAD_TYPE_OLD;
1682*4882a593Smuzhiyun selected_lcd_type = LCD_TYPE_OLD;
1683*4882a593Smuzhiyun
1684*4882a593Smuzhiyun /* TODO: This two are a little hacky, sort it out later */
1685*4882a593Smuzhiyun if (lcd_width == NOT_SET)
1686*4882a593Smuzhiyun lcd_width = 16;
1687*4882a593Smuzhiyun if (lcd_hwidth == NOT_SET)
1688*4882a593Smuzhiyun lcd_hwidth = 16;
1689*4882a593Smuzhiyun break;
1690*4882a593Smuzhiyun case PANEL_PROFILE_NEW:
1691*4882a593Smuzhiyun /* serial, 2*16, new keypad */
1692*4882a593Smuzhiyun selected_keypad_type = KEYPAD_TYPE_NEW;
1693*4882a593Smuzhiyun selected_lcd_type = LCD_TYPE_KS0074;
1694*4882a593Smuzhiyun break;
1695*4882a593Smuzhiyun case PANEL_PROFILE_HANTRONIX:
1696*4882a593Smuzhiyun /* 8 bits, 2*16 hantronix-like, no keypad */
1697*4882a593Smuzhiyun selected_keypad_type = KEYPAD_TYPE_NONE;
1698*4882a593Smuzhiyun selected_lcd_type = LCD_TYPE_HANTRONIX;
1699*4882a593Smuzhiyun break;
1700*4882a593Smuzhiyun case PANEL_PROFILE_NEXCOM:
1701*4882a593Smuzhiyun /* generic 8 bits, 2*16, nexcom keypad, eg. Nexcom. */
1702*4882a593Smuzhiyun selected_keypad_type = KEYPAD_TYPE_NEXCOM;
1703*4882a593Smuzhiyun selected_lcd_type = LCD_TYPE_NEXCOM;
1704*4882a593Smuzhiyun break;
1705*4882a593Smuzhiyun case PANEL_PROFILE_LARGE:
1706*4882a593Smuzhiyun /* 8 bits, 2*40, old keypad */
1707*4882a593Smuzhiyun selected_keypad_type = KEYPAD_TYPE_OLD;
1708*4882a593Smuzhiyun selected_lcd_type = LCD_TYPE_OLD;
1709*4882a593Smuzhiyun break;
1710*4882a593Smuzhiyun }
1711*4882a593Smuzhiyun
1712*4882a593Smuzhiyun /*
1713*4882a593Smuzhiyun * Overwrite selection with module param values (both keypad and lcd),
1714*4882a593Smuzhiyun * where the deprecated params have lower prio.
1715*4882a593Smuzhiyun */
1716*4882a593Smuzhiyun if (keypad_enabled != NOT_SET)
1717*4882a593Smuzhiyun selected_keypad_type = keypad_enabled;
1718*4882a593Smuzhiyun if (keypad_type != NOT_SET)
1719*4882a593Smuzhiyun selected_keypad_type = keypad_type;
1720*4882a593Smuzhiyun
1721*4882a593Smuzhiyun keypad.enabled = (selected_keypad_type > 0);
1722*4882a593Smuzhiyun
1723*4882a593Smuzhiyun if (lcd_enabled != NOT_SET)
1724*4882a593Smuzhiyun selected_lcd_type = lcd_enabled;
1725*4882a593Smuzhiyun if (lcd_type != NOT_SET)
1726*4882a593Smuzhiyun selected_lcd_type = lcd_type;
1727*4882a593Smuzhiyun
1728*4882a593Smuzhiyun lcd.enabled = (selected_lcd_type > 0);
1729*4882a593Smuzhiyun
1730*4882a593Smuzhiyun if (lcd.enabled) {
1731*4882a593Smuzhiyun /*
1732*4882a593Smuzhiyun * Init lcd struct with load-time values to preserve exact
1733*4882a593Smuzhiyun * current functionality (at least for now).
1734*4882a593Smuzhiyun */
1735*4882a593Smuzhiyun lcd.charset = lcd_charset;
1736*4882a593Smuzhiyun lcd.proto = lcd_proto;
1737*4882a593Smuzhiyun lcd.pins.e = lcd_e_pin;
1738*4882a593Smuzhiyun lcd.pins.rs = lcd_rs_pin;
1739*4882a593Smuzhiyun lcd.pins.rw = lcd_rw_pin;
1740*4882a593Smuzhiyun lcd.pins.cl = lcd_cl_pin;
1741*4882a593Smuzhiyun lcd.pins.da = lcd_da_pin;
1742*4882a593Smuzhiyun lcd.pins.bl = lcd_bl_pin;
1743*4882a593Smuzhiyun }
1744*4882a593Smuzhiyun
1745*4882a593Smuzhiyun switch (selected_keypad_type) {
1746*4882a593Smuzhiyun case KEYPAD_TYPE_OLD:
1747*4882a593Smuzhiyun keypad_profile = old_keypad_profile;
1748*4882a593Smuzhiyun break;
1749*4882a593Smuzhiyun case KEYPAD_TYPE_NEW:
1750*4882a593Smuzhiyun keypad_profile = new_keypad_profile;
1751*4882a593Smuzhiyun break;
1752*4882a593Smuzhiyun case KEYPAD_TYPE_NEXCOM:
1753*4882a593Smuzhiyun keypad_profile = nexcom_keypad_profile;
1754*4882a593Smuzhiyun break;
1755*4882a593Smuzhiyun default:
1756*4882a593Smuzhiyun keypad_profile = NULL;
1757*4882a593Smuzhiyun break;
1758*4882a593Smuzhiyun }
1759*4882a593Smuzhiyun
1760*4882a593Smuzhiyun if (!lcd.enabled && !keypad.enabled) {
1761*4882a593Smuzhiyun /* no device enabled, let's exit */
1762*4882a593Smuzhiyun pr_err("panel driver disabled.\n");
1763*4882a593Smuzhiyun return -ENODEV;
1764*4882a593Smuzhiyun }
1765*4882a593Smuzhiyun
1766*4882a593Smuzhiyun err = parport_register_driver(&panel_driver);
1767*4882a593Smuzhiyun if (err) {
1768*4882a593Smuzhiyun pr_err("could not register with parport. Aborting.\n");
1769*4882a593Smuzhiyun return err;
1770*4882a593Smuzhiyun }
1771*4882a593Smuzhiyun
1772*4882a593Smuzhiyun if (pprt)
1773*4882a593Smuzhiyun pr_info("panel driver registered on parport%d (io=0x%lx).\n",
1774*4882a593Smuzhiyun parport, pprt->port->base);
1775*4882a593Smuzhiyun else
1776*4882a593Smuzhiyun pr_info("panel driver not yet registered\n");
1777*4882a593Smuzhiyun return 0;
1778*4882a593Smuzhiyun }
1779*4882a593Smuzhiyun
panel_cleanup_module(void)1780*4882a593Smuzhiyun static void __exit panel_cleanup_module(void)
1781*4882a593Smuzhiyun {
1782*4882a593Smuzhiyun parport_unregister_driver(&panel_driver);
1783*4882a593Smuzhiyun }
1784*4882a593Smuzhiyun
1785*4882a593Smuzhiyun module_init(panel_init_module);
1786*4882a593Smuzhiyun module_exit(panel_cleanup_module);
1787*4882a593Smuzhiyun MODULE_AUTHOR("Willy Tarreau");
1788*4882a593Smuzhiyun MODULE_LICENSE("GPL");
1789*4882a593Smuzhiyun
1790*4882a593Smuzhiyun /*
1791*4882a593Smuzhiyun * Local variables:
1792*4882a593Smuzhiyun * c-indent-level: 4
1793*4882a593Smuzhiyun * tab-width: 8
1794*4882a593Smuzhiyun * End:
1795*4882a593Smuzhiyun */
1796