diff options
Diffstat (limited to 'drivers/media/video/ir-kbd-gpio.c')
-rw-r--r-- | drivers/media/video/ir-kbd-gpio.c | 444 |
1 files changed, 444 insertions, 0 deletions
diff --git a/drivers/media/video/ir-kbd-gpio.c b/drivers/media/video/ir-kbd-gpio.c new file mode 100644 index 000000000000..ab6620de4b3b --- /dev/null +++ b/drivers/media/video/ir-kbd-gpio.c | |||
@@ -0,0 +1,444 @@ | |||
1 | /* | ||
2 | * $Id: ir-kbd-gpio.c,v 1.12 2005/02/22 12:28:40 kraxel Exp $ | ||
3 | * | ||
4 | * Copyright (c) 2003 Gerd Knorr | ||
5 | * Copyright (c) 2003 Pavel Machek | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | */ | ||
21 | |||
22 | #include <linux/module.h> | ||
23 | #include <linux/moduleparam.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/delay.h> | ||
26 | #include <linux/interrupt.h> | ||
27 | #include <linux/input.h> | ||
28 | #include <linux/pci.h> | ||
29 | |||
30 | #include <media/ir-common.h> | ||
31 | |||
32 | #include "bttv.h" | ||
33 | |||
34 | /* ---------------------------------------------------------------------- */ | ||
35 | |||
36 | static IR_KEYTAB_TYPE ir_codes_avermedia[IR_KEYTAB_SIZE] = { | ||
37 | [ 34 ] = KEY_KP0, | ||
38 | [ 40 ] = KEY_KP1, | ||
39 | [ 24 ] = KEY_KP2, | ||
40 | [ 56 ] = KEY_KP3, | ||
41 | [ 36 ] = KEY_KP4, | ||
42 | [ 20 ] = KEY_KP5, | ||
43 | [ 52 ] = KEY_KP6, | ||
44 | [ 44 ] = KEY_KP7, | ||
45 | [ 28 ] = KEY_KP8, | ||
46 | [ 60 ] = KEY_KP9, | ||
47 | |||
48 | [ 48 ] = KEY_EJECTCD, // Unmarked on my controller | ||
49 | [ 0 ] = KEY_POWER, | ||
50 | [ 18 ] = BTN_LEFT, // DISPLAY/L | ||
51 | [ 50 ] = BTN_RIGHT, // LOOP/R | ||
52 | [ 10 ] = KEY_MUTE, | ||
53 | [ 38 ] = KEY_RECORD, | ||
54 | [ 22 ] = KEY_PAUSE, | ||
55 | [ 54 ] = KEY_STOP, | ||
56 | [ 30 ] = KEY_VOLUMEDOWN, | ||
57 | [ 62 ] = KEY_VOLUMEUP, | ||
58 | |||
59 | [ 32 ] = KEY_TUNER, // TV/FM | ||
60 | [ 16 ] = KEY_CD, | ||
61 | [ 8 ] = KEY_VIDEO, | ||
62 | [ 4 ] = KEY_AUDIO, | ||
63 | [ 12 ] = KEY_ZOOM, // full screen | ||
64 | [ 2 ] = KEY_INFO, // preview | ||
65 | [ 42 ] = KEY_SEARCH, // autoscan | ||
66 | [ 26 ] = KEY_STOP, // freeze | ||
67 | [ 58 ] = KEY_RECORD, // capture | ||
68 | [ 6 ] = KEY_PLAY, // unmarked | ||
69 | [ 46 ] = KEY_RED, // unmarked | ||
70 | [ 14 ] = KEY_GREEN, // unmarked | ||
71 | |||
72 | [ 33 ] = KEY_YELLOW, // unmarked | ||
73 | [ 17 ] = KEY_CHANNELDOWN, | ||
74 | [ 49 ] = KEY_CHANNELUP, | ||
75 | [ 1 ] = KEY_BLUE, // unmarked | ||
76 | }; | ||
77 | |||
78 | /* Matt Jesson <dvb@jesson.eclipse.co.uk */ | ||
79 | static IR_KEYTAB_TYPE ir_codes_avermedia_dvbt[IR_KEYTAB_SIZE] = { | ||
80 | [ 0x28 ] = KEY_KP0, //'0' / 'enter' | ||
81 | [ 0x22 ] = KEY_KP1, //'1' | ||
82 | [ 0x12 ] = KEY_KP2, //'2' / 'up arrow' | ||
83 | [ 0x32 ] = KEY_KP3, //'3' | ||
84 | [ 0x24 ] = KEY_KP4, //'4' / 'left arrow' | ||
85 | [ 0x14 ] = KEY_KP5, //'5' | ||
86 | [ 0x34 ] = KEY_KP6, //'6' / 'right arrow' | ||
87 | [ 0x26 ] = KEY_KP7, //'7' | ||
88 | [ 0x16 ] = KEY_KP8, //'8' / 'down arrow' | ||
89 | [ 0x36 ] = KEY_KP9, //'9' | ||
90 | |||
91 | [ 0x20 ] = KEY_LIST, // 'source' | ||
92 | [ 0x10 ] = KEY_TEXT, // 'teletext' | ||
93 | [ 0x00 ] = KEY_POWER, // 'power' | ||
94 | [ 0x04 ] = KEY_AUDIO, // 'audio' | ||
95 | [ 0x06 ] = KEY_ZOOM, // 'full screen' | ||
96 | [ 0x18 ] = KEY_VIDEO, // 'display' | ||
97 | [ 0x38 ] = KEY_SEARCH, // 'loop' | ||
98 | [ 0x08 ] = KEY_INFO, // 'preview' | ||
99 | [ 0x2a ] = KEY_REWIND, // 'backward <<' | ||
100 | [ 0x1a ] = KEY_FASTFORWARD, // 'forward >>' | ||
101 | [ 0x3a ] = KEY_RECORD, // 'capture' | ||
102 | [ 0x0a ] = KEY_MUTE, // 'mute' | ||
103 | [ 0x2c ] = KEY_RECORD, // 'record' | ||
104 | [ 0x1c ] = KEY_PAUSE, // 'pause' | ||
105 | [ 0x3c ] = KEY_STOP, // 'stop' | ||
106 | [ 0x0c ] = KEY_PLAY, // 'play' | ||
107 | [ 0x2e ] = KEY_RED, // 'red' | ||
108 | [ 0x01 ] = KEY_BLUE, // 'blue' / 'cancel' | ||
109 | [ 0x0e ] = KEY_YELLOW, // 'yellow' / 'ok' | ||
110 | [ 0x21 ] = KEY_GREEN, // 'green' | ||
111 | [ 0x11 ] = KEY_CHANNELDOWN, // 'channel -' | ||
112 | [ 0x31 ] = KEY_CHANNELUP, // 'channel +' | ||
113 | [ 0x1e ] = KEY_VOLUMEDOWN, // 'volume -' | ||
114 | [ 0x3e ] = KEY_VOLUMEUP, // 'volume +' | ||
115 | }; | ||
116 | |||
117 | static IR_KEYTAB_TYPE ir_codes_pixelview[IR_KEYTAB_SIZE] = { | ||
118 | [ 2 ] = KEY_KP0, | ||
119 | [ 1 ] = KEY_KP1, | ||
120 | [ 11 ] = KEY_KP2, | ||
121 | [ 27 ] = KEY_KP3, | ||
122 | [ 5 ] = KEY_KP4, | ||
123 | [ 9 ] = KEY_KP5, | ||
124 | [ 21 ] = KEY_KP6, | ||
125 | [ 6 ] = KEY_KP7, | ||
126 | [ 10 ] = KEY_KP8, | ||
127 | [ 18 ] = KEY_KP9, | ||
128 | |||
129 | [ 3 ] = KEY_TUNER, // TV/FM | ||
130 | [ 7 ] = KEY_SEARCH, // scan | ||
131 | [ 28 ] = KEY_ZOOM, // full screen | ||
132 | [ 30 ] = KEY_POWER, | ||
133 | [ 23 ] = KEY_VOLUMEDOWN, | ||
134 | [ 31 ] = KEY_VOLUMEUP, | ||
135 | [ 20 ] = KEY_CHANNELDOWN, | ||
136 | [ 22 ] = KEY_CHANNELUP, | ||
137 | [ 24 ] = KEY_MUTE, | ||
138 | |||
139 | [ 0 ] = KEY_LIST, // source | ||
140 | [ 19 ] = KEY_INFO, // loop | ||
141 | [ 16 ] = KEY_LAST, // +100 | ||
142 | [ 13 ] = KEY_CLEAR, // reset | ||
143 | [ 12 ] = BTN_RIGHT, // fun++ | ||
144 | [ 4 ] = BTN_LEFT, // fun-- | ||
145 | [ 14 ] = KEY_GOTO, // function | ||
146 | [ 15 ] = KEY_STOP, // freeze | ||
147 | }; | ||
148 | |||
149 | /* Attila Kondoros <attila.kondoros@chello.hu> */ | ||
150 | static IR_KEYTAB_TYPE ir_codes_apac_viewcomp[IR_KEYTAB_SIZE] = { | ||
151 | |||
152 | [ 1 ] = KEY_KP1, | ||
153 | [ 2 ] = KEY_KP2, | ||
154 | [ 3 ] = KEY_KP3, | ||
155 | [ 4 ] = KEY_KP4, | ||
156 | [ 5 ] = KEY_KP5, | ||
157 | [ 6 ] = KEY_KP6, | ||
158 | [ 7 ] = KEY_KP7, | ||
159 | [ 8 ] = KEY_KP8, | ||
160 | [ 9 ] = KEY_KP9, | ||
161 | [ 0 ] = KEY_KP0, | ||
162 | [ 23 ] = KEY_LAST, // +100 | ||
163 | [ 10 ] = KEY_LIST, // recall | ||
164 | |||
165 | |||
166 | [ 28 ] = KEY_TUNER, // TV/FM | ||
167 | [ 21 ] = KEY_SEARCH, // scan | ||
168 | [ 18 ] = KEY_POWER, // power | ||
169 | [ 31 ] = KEY_VOLUMEDOWN, // vol up | ||
170 | [ 27 ] = KEY_VOLUMEUP, // vol down | ||
171 | [ 30 ] = KEY_CHANNELDOWN, // chn up | ||
172 | [ 26 ] = KEY_CHANNELUP, // chn down | ||
173 | |||
174 | [ 17 ] = KEY_VIDEO, // video | ||
175 | [ 15 ] = KEY_ZOOM, // full screen | ||
176 | [ 19 ] = KEY_MUTE, // mute/unmute | ||
177 | [ 16 ] = KEY_TEXT, // min | ||
178 | |||
179 | [ 13 ] = KEY_STOP, // freeze | ||
180 | [ 14 ] = KEY_RECORD, // record | ||
181 | [ 29 ] = KEY_PLAYPAUSE, // stop | ||
182 | [ 25 ] = KEY_PLAY, // play | ||
183 | |||
184 | [ 22 ] = KEY_GOTO, // osd | ||
185 | [ 20 ] = KEY_REFRESH, // default | ||
186 | [ 12 ] = KEY_KPPLUS, // fine tune >>>> | ||
187 | [ 24 ] = KEY_KPMINUS // fine tune <<<< | ||
188 | }; | ||
189 | |||
190 | /* ---------------------------------------------------------------------- */ | ||
191 | |||
192 | struct IR { | ||
193 | struct bttv_sub_device *sub; | ||
194 | struct input_dev input; | ||
195 | struct ir_input_state ir; | ||
196 | char name[32]; | ||
197 | char phys[32]; | ||
198 | u32 mask_keycode; | ||
199 | u32 mask_keydown; | ||
200 | u32 mask_keyup; | ||
201 | |||
202 | int polling; | ||
203 | u32 last_gpio; | ||
204 | struct work_struct work; | ||
205 | struct timer_list timer; | ||
206 | }; | ||
207 | |||
208 | static int debug; | ||
209 | module_param(debug, int, 0644); /* debug level (0,1,2) */ | ||
210 | |||
211 | #define DEVNAME "ir-kbd-gpio" | ||
212 | #define dprintk(fmt, arg...) if (debug) \ | ||
213 | printk(KERN_DEBUG DEVNAME ": " fmt , ## arg) | ||
214 | |||
215 | static void ir_irq(struct bttv_sub_device *sub); | ||
216 | static int ir_probe(struct device *dev); | ||
217 | static int ir_remove(struct device *dev); | ||
218 | |||
219 | static struct bttv_sub_driver driver = { | ||
220 | .drv = { | ||
221 | .name = DEVNAME, | ||
222 | .probe = ir_probe, | ||
223 | .remove = ir_remove, | ||
224 | }, | ||
225 | .gpio_irq = ir_irq, | ||
226 | }; | ||
227 | |||
228 | /* ---------------------------------------------------------------------- */ | ||
229 | |||
230 | static void ir_handle_key(struct IR *ir) | ||
231 | { | ||
232 | u32 gpio,data; | ||
233 | |||
234 | /* read gpio value */ | ||
235 | gpio = bttv_gpio_read(ir->sub->core); | ||
236 | if (ir->polling) { | ||
237 | if (ir->last_gpio == gpio) | ||
238 | return; | ||
239 | ir->last_gpio = gpio; | ||
240 | } | ||
241 | |||
242 | /* extract data */ | ||
243 | data = ir_extract_bits(gpio, ir->mask_keycode); | ||
244 | dprintk(DEVNAME ": irq gpio=0x%x code=%d | %s%s%s\n", | ||
245 | gpio, data, | ||
246 | ir->polling ? "poll" : "irq", | ||
247 | (gpio & ir->mask_keydown) ? " down" : "", | ||
248 | (gpio & ir->mask_keyup) ? " up" : ""); | ||
249 | |||
250 | if (ir->mask_keydown) { | ||
251 | /* bit set on keydown */ | ||
252 | if (gpio & ir->mask_keydown) { | ||
253 | ir_input_keydown(&ir->input,&ir->ir,data,data); | ||
254 | } else { | ||
255 | ir_input_nokey(&ir->input,&ir->ir); | ||
256 | } | ||
257 | |||
258 | } else if (ir->mask_keyup) { | ||
259 | /* bit cleared on keydown */ | ||
260 | if (0 == (gpio & ir->mask_keyup)) { | ||
261 | ir_input_keydown(&ir->input,&ir->ir,data,data); | ||
262 | } else { | ||
263 | ir_input_nokey(&ir->input,&ir->ir); | ||
264 | } | ||
265 | |||
266 | } else { | ||
267 | /* can't disturgissh keydown/up :-/ */ | ||
268 | ir_input_keydown(&ir->input,&ir->ir,data,data); | ||
269 | ir_input_nokey(&ir->input,&ir->ir); | ||
270 | } | ||
271 | } | ||
272 | |||
273 | static void ir_irq(struct bttv_sub_device *sub) | ||
274 | { | ||
275 | struct IR *ir = dev_get_drvdata(&sub->dev); | ||
276 | |||
277 | if (!ir->polling) | ||
278 | ir_handle_key(ir); | ||
279 | } | ||
280 | |||
281 | static void ir_timer(unsigned long data) | ||
282 | { | ||
283 | struct IR *ir = (struct IR*)data; | ||
284 | |||
285 | schedule_work(&ir->work); | ||
286 | } | ||
287 | |||
288 | static void ir_work(void *data) | ||
289 | { | ||
290 | struct IR *ir = data; | ||
291 | unsigned long timeout; | ||
292 | |||
293 | ir_handle_key(ir); | ||
294 | timeout = jiffies + (ir->polling * HZ / 1000); | ||
295 | mod_timer(&ir->timer, timeout); | ||
296 | } | ||
297 | |||
298 | /* ---------------------------------------------------------------------- */ | ||
299 | |||
300 | static int ir_probe(struct device *dev) | ||
301 | { | ||
302 | struct bttv_sub_device *sub = to_bttv_sub_dev(dev); | ||
303 | struct IR *ir; | ||
304 | IR_KEYTAB_TYPE *ir_codes = NULL; | ||
305 | int ir_type = IR_TYPE_OTHER; | ||
306 | |||
307 | ir = kmalloc(sizeof(*ir),GFP_KERNEL); | ||
308 | if (NULL == ir) | ||
309 | return -ENOMEM; | ||
310 | memset(ir,0,sizeof(*ir)); | ||
311 | |||
312 | /* detect & configure */ | ||
313 | switch (sub->core->type) { | ||
314 | case BTTV_AVERMEDIA: | ||
315 | case BTTV_AVPHONE98: | ||
316 | case BTTV_AVERMEDIA98: | ||
317 | ir_codes = ir_codes_avermedia; | ||
318 | ir->mask_keycode = 0xf88000; | ||
319 | ir->mask_keydown = 0x010000; | ||
320 | ir->polling = 50; // ms | ||
321 | break; | ||
322 | |||
323 | case BTTV_AVDVBT_761: | ||
324 | case BTTV_AVDVBT_771: | ||
325 | ir_codes = ir_codes_avermedia_dvbt; | ||
326 | ir->mask_keycode = 0x0f00c0; | ||
327 | ir->mask_keydown = 0x000020; | ||
328 | ir->polling = 50; // ms | ||
329 | break; | ||
330 | |||
331 | case BTTV_PXELVWPLTVPAK: | ||
332 | ir_codes = ir_codes_pixelview; | ||
333 | ir->mask_keycode = 0x003e00; | ||
334 | ir->mask_keyup = 0x010000; | ||
335 | ir->polling = 50; // ms | ||
336 | break; | ||
337 | case BTTV_PV_BT878P_9B: | ||
338 | case BTTV_PV_BT878P_PLUS: | ||
339 | ir_codes = ir_codes_pixelview; | ||
340 | ir->mask_keycode = 0x001f00; | ||
341 | ir->mask_keyup = 0x008000; | ||
342 | ir->polling = 50; // ms | ||
343 | break; | ||
344 | |||
345 | case BTTV_WINFAST2000: | ||
346 | ir_codes = ir_codes_winfast; | ||
347 | ir->mask_keycode = 0x1f8; | ||
348 | break; | ||
349 | case BTTV_MAGICTVIEW061: | ||
350 | case BTTV_MAGICTVIEW063: | ||
351 | ir_codes = ir_codes_winfast; | ||
352 | ir->mask_keycode = 0x0008e000; | ||
353 | ir->mask_keydown = 0x00200000; | ||
354 | break; | ||
355 | case BTTV_APAC_VIEWCOMP: | ||
356 | ir_codes = ir_codes_apac_viewcomp; | ||
357 | ir->mask_keycode = 0x001f00; | ||
358 | ir->mask_keyup = 0x008000; | ||
359 | ir->polling = 50; // ms | ||
360 | break; | ||
361 | } | ||
362 | if (NULL == ir_codes) { | ||
363 | kfree(ir); | ||
364 | return -ENODEV; | ||
365 | } | ||
366 | |||
367 | /* init hardware-specific stuff */ | ||
368 | bttv_gpio_inout(sub->core, ir->mask_keycode | ir->mask_keydown, 0); | ||
369 | ir->sub = sub; | ||
370 | |||
371 | /* init input device */ | ||
372 | snprintf(ir->name, sizeof(ir->name), "bttv IR (card=%d)", | ||
373 | sub->core->type); | ||
374 | snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", | ||
375 | pci_name(sub->core->pci)); | ||
376 | |||
377 | ir_input_init(&ir->input, &ir->ir, ir_type, ir_codes); | ||
378 | ir->input.name = ir->name; | ||
379 | ir->input.phys = ir->phys; | ||
380 | ir->input.id.bustype = BUS_PCI; | ||
381 | ir->input.id.version = 1; | ||
382 | if (sub->core->pci->subsystem_vendor) { | ||
383 | ir->input.id.vendor = sub->core->pci->subsystem_vendor; | ||
384 | ir->input.id.product = sub->core->pci->subsystem_device; | ||
385 | } else { | ||
386 | ir->input.id.vendor = sub->core->pci->vendor; | ||
387 | ir->input.id.product = sub->core->pci->device; | ||
388 | } | ||
389 | |||
390 | if (ir->polling) { | ||
391 | INIT_WORK(&ir->work, ir_work, ir); | ||
392 | init_timer(&ir->timer); | ||
393 | ir->timer.function = ir_timer; | ||
394 | ir->timer.data = (unsigned long)ir; | ||
395 | schedule_work(&ir->work); | ||
396 | } | ||
397 | |||
398 | /* all done */ | ||
399 | dev_set_drvdata(dev,ir); | ||
400 | input_register_device(&ir->input); | ||
401 | printk(DEVNAME ": %s detected at %s\n",ir->input.name,ir->input.phys); | ||
402 | |||
403 | return 0; | ||
404 | } | ||
405 | |||
406 | static int ir_remove(struct device *dev) | ||
407 | { | ||
408 | struct IR *ir = dev_get_drvdata(dev); | ||
409 | |||
410 | if (ir->polling) { | ||
411 | del_timer(&ir->timer); | ||
412 | flush_scheduled_work(); | ||
413 | } | ||
414 | |||
415 | input_unregister_device(&ir->input); | ||
416 | kfree(ir); | ||
417 | return 0; | ||
418 | } | ||
419 | |||
420 | /* ---------------------------------------------------------------------- */ | ||
421 | |||
422 | MODULE_AUTHOR("Gerd Knorr, Pavel Machek"); | ||
423 | MODULE_DESCRIPTION("input driver for bt8x8 gpio IR remote controls"); | ||
424 | MODULE_LICENSE("GPL"); | ||
425 | |||
426 | static int ir_init(void) | ||
427 | { | ||
428 | return bttv_sub_register(&driver, "remote"); | ||
429 | } | ||
430 | |||
431 | static void ir_fini(void) | ||
432 | { | ||
433 | bttv_sub_unregister(&driver); | ||
434 | } | ||
435 | |||
436 | module_init(ir_init); | ||
437 | module_exit(ir_fini); | ||
438 | |||
439 | |||
440 | /* | ||
441 | * Local variables: | ||
442 | * c-basic-offset: 8 | ||
443 | * End: | ||
444 | */ | ||