diff options
author | David Härdeman <david@hardeman.nu> | 2010-10-29 15:08:28 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2010-12-29 05:16:38 -0500 |
commit | 5b2e303f6df1e0b1a903950c5d613a20c8c71a37 (patch) | |
tree | 2fdd9dfedac05cc473e282e7d20331863b58b6f8 /drivers/media/rc/winbond-cir.c | |
parent | d8b4b5822f51e2142b731b42c81e3f03eec475b2 (diff) |
[media] rc-core: convert winbond-cir
Move winbond-cir from drivers/input/misc/ into drivers/media/rc/
and convert it to use rc-core.
Signed-off-by: David Härdeman <david@hardeman.nu>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/rc/winbond-cir.c')
-rw-r--r-- | drivers/media/rc/winbond-cir.c | 932 |
1 files changed, 932 insertions, 0 deletions
diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c new file mode 100644 index 000000000000..0ee16ec23bbf --- /dev/null +++ b/drivers/media/rc/winbond-cir.c | |||
@@ -0,0 +1,932 @@ | |||
1 | /* | ||
2 | * winbond-cir.c - Driver for the Consumer IR functionality of Winbond | ||
3 | * SuperI/O chips. | ||
4 | * | ||
5 | * Currently supports the Winbond WPCD376i chip (PNP id WEC1022), but | ||
6 | * could probably support others (Winbond WEC102X, NatSemi, etc) | ||
7 | * with minor modifications. | ||
8 | * | ||
9 | * Original Author: David Härdeman <david@hardeman.nu> | ||
10 | * Copyright (C) 2009 - 2010 David Härdeman <david@hardeman.nu> | ||
11 | * | ||
12 | * Dedicated to my daughter Matilda, without whose loving attention this | ||
13 | * driver would have been finished in half the time and with a fraction | ||
14 | * of the bugs. | ||
15 | * | ||
16 | * Written using: | ||
17 | * o Winbond WPCD376I datasheet helpfully provided by Jesse Barnes at Intel | ||
18 | * o NatSemi PC87338/PC97338 datasheet (for the serial port stuff) | ||
19 | * o DSDT dumps | ||
20 | * | ||
21 | * Supported features: | ||
22 | * o Wake-On-CIR functionality | ||
23 | * | ||
24 | * To do: | ||
25 | * o Learning | ||
26 | * o IR Transmit | ||
27 | * | ||
28 | * This program is free software; you can redistribute it and/or modify | ||
29 | * it under the terms of the GNU General Public License as published by | ||
30 | * the Free Software Foundation; either version 2 of the License, or | ||
31 | * (at your option) any later version. | ||
32 | * | ||
33 | * This program is distributed in the hope that it will be useful, | ||
34 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
35 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
36 | * GNU General Public License for more details. | ||
37 | * | ||
38 | * You should have received a copy of the GNU General Public License | ||
39 | * along with this program; if not, write to the Free Software | ||
40 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
41 | */ | ||
42 | |||
43 | #include <linux/module.h> | ||
44 | #include <linux/pnp.h> | ||
45 | #include <linux/interrupt.h> | ||
46 | #include <linux/timer.h> | ||
47 | #include <linux/leds.h> | ||
48 | #include <linux/spinlock.h> | ||
49 | #include <linux/pci_ids.h> | ||
50 | #include <linux/io.h> | ||
51 | #include <linux/bitrev.h> | ||
52 | #include <linux/slab.h> | ||
53 | #include <media/ir-core.h> | ||
54 | |||
55 | #define DRVNAME "winbond-cir" | ||
56 | |||
57 | /* CEIR Wake-Up Registers, relative to data->wbase */ | ||
58 | #define WBCIR_REG_WCEIR_CTL 0x03 /* CEIR Receiver Control */ | ||
59 | #define WBCIR_REG_WCEIR_STS 0x04 /* CEIR Receiver Status */ | ||
60 | #define WBCIR_REG_WCEIR_EV_EN 0x05 /* CEIR Receiver Event Enable */ | ||
61 | #define WBCIR_REG_WCEIR_CNTL 0x06 /* CEIR Receiver Counter Low */ | ||
62 | #define WBCIR_REG_WCEIR_CNTH 0x07 /* CEIR Receiver Counter High */ | ||
63 | #define WBCIR_REG_WCEIR_INDEX 0x08 /* CEIR Receiver Index */ | ||
64 | #define WBCIR_REG_WCEIR_DATA 0x09 /* CEIR Receiver Data */ | ||
65 | #define WBCIR_REG_WCEIR_CSL 0x0A /* CEIR Re. Compare Strlen */ | ||
66 | #define WBCIR_REG_WCEIR_CFG1 0x0B /* CEIR Re. Configuration 1 */ | ||
67 | #define WBCIR_REG_WCEIR_CFG2 0x0C /* CEIR Re. Configuration 2 */ | ||
68 | |||
69 | /* CEIR Enhanced Functionality Registers, relative to data->ebase */ | ||
70 | #define WBCIR_REG_ECEIR_CTS 0x00 /* Enhanced IR Control Status */ | ||
71 | #define WBCIR_REG_ECEIR_CCTL 0x01 /* Infrared Counter Control */ | ||
72 | #define WBCIR_REG_ECEIR_CNT_LO 0x02 /* Infrared Counter LSB */ | ||
73 | #define WBCIR_REG_ECEIR_CNT_HI 0x03 /* Infrared Counter MSB */ | ||
74 | #define WBCIR_REG_ECEIR_IREM 0x04 /* Infrared Emitter Status */ | ||
75 | |||
76 | /* SP3 Banked Registers, relative to data->sbase */ | ||
77 | #define WBCIR_REG_SP3_BSR 0x03 /* Bank Select, all banks */ | ||
78 | /* Bank 0 */ | ||
79 | #define WBCIR_REG_SP3_RXDATA 0x00 /* FIFO RX data (r) */ | ||
80 | #define WBCIR_REG_SP3_TXDATA 0x00 /* FIFO TX data (w) */ | ||
81 | #define WBCIR_REG_SP3_IER 0x01 /* Interrupt Enable */ | ||
82 | #define WBCIR_REG_SP3_EIR 0x02 /* Event Identification (r) */ | ||
83 | #define WBCIR_REG_SP3_FCR 0x02 /* FIFO Control (w) */ | ||
84 | #define WBCIR_REG_SP3_MCR 0x04 /* Mode Control */ | ||
85 | #define WBCIR_REG_SP3_LSR 0x05 /* Link Status */ | ||
86 | #define WBCIR_REG_SP3_MSR 0x06 /* Modem Status */ | ||
87 | #define WBCIR_REG_SP3_ASCR 0x07 /* Aux Status and Control */ | ||
88 | /* Bank 2 */ | ||
89 | #define WBCIR_REG_SP3_BGDL 0x00 /* Baud Divisor LSB */ | ||
90 | #define WBCIR_REG_SP3_BGDH 0x01 /* Baud Divisor MSB */ | ||
91 | #define WBCIR_REG_SP3_EXCR1 0x02 /* Extended Control 1 */ | ||
92 | #define WBCIR_REG_SP3_EXCR2 0x04 /* Extended Control 2 */ | ||
93 | #define WBCIR_REG_SP3_TXFLV 0x06 /* TX FIFO Level */ | ||
94 | #define WBCIR_REG_SP3_RXFLV 0x07 /* RX FIFO Level */ | ||
95 | /* Bank 3 */ | ||
96 | #define WBCIR_REG_SP3_MRID 0x00 /* Module Identification */ | ||
97 | #define WBCIR_REG_SP3_SH_LCR 0x01 /* LCR Shadow */ | ||
98 | #define WBCIR_REG_SP3_SH_FCR 0x02 /* FCR Shadow */ | ||
99 | /* Bank 4 */ | ||
100 | #define WBCIR_REG_SP3_IRCR1 0x02 /* Infrared Control 1 */ | ||
101 | /* Bank 5 */ | ||
102 | #define WBCIR_REG_SP3_IRCR2 0x04 /* Infrared Control 2 */ | ||
103 | /* Bank 6 */ | ||
104 | #define WBCIR_REG_SP3_IRCR3 0x00 /* Infrared Control 3 */ | ||
105 | #define WBCIR_REG_SP3_SIR_PW 0x02 /* SIR Pulse Width */ | ||
106 | /* Bank 7 */ | ||
107 | #define WBCIR_REG_SP3_IRRXDC 0x00 /* IR RX Demod Control */ | ||
108 | #define WBCIR_REG_SP3_IRTXMC 0x01 /* IR TX Mod Control */ | ||
109 | #define WBCIR_REG_SP3_RCCFG 0x02 /* CEIR Config */ | ||
110 | #define WBCIR_REG_SP3_IRCFG1 0x04 /* Infrared Config 1 */ | ||
111 | #define WBCIR_REG_SP3_IRCFG4 0x07 /* Infrared Config 4 */ | ||
112 | |||
113 | /* | ||
114 | * Magic values follow | ||
115 | */ | ||
116 | |||
117 | /* No interrupts for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */ | ||
118 | #define WBCIR_IRQ_NONE 0x00 | ||
119 | /* RX data bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */ | ||
120 | #define WBCIR_IRQ_RX 0x01 | ||
121 | /* Over/Under-flow bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */ | ||
122 | #define WBCIR_IRQ_ERR 0x04 | ||
123 | /* Led enable/disable bit for WBCIR_REG_ECEIR_CTS */ | ||
124 | #define WBCIR_LED_ENABLE 0x80 | ||
125 | /* RX data available bit for WBCIR_REG_SP3_LSR */ | ||
126 | #define WBCIR_RX_AVAIL 0x01 | ||
127 | /* RX disable bit for WBCIR_REG_SP3_ASCR */ | ||
128 | #define WBCIR_RX_DISABLE 0x20 | ||
129 | /* Extended mode enable bit for WBCIR_REG_SP3_EXCR1 */ | ||
130 | #define WBCIR_EXT_ENABLE 0x01 | ||
131 | /* Select compare register in WBCIR_REG_WCEIR_INDEX (bits 5 & 6) */ | ||
132 | #define WBCIR_REGSEL_COMPARE 0x10 | ||
133 | /* Select mask register in WBCIR_REG_WCEIR_INDEX (bits 5 & 6) */ | ||
134 | #define WBCIR_REGSEL_MASK 0x20 | ||
135 | /* Starting address of selected register in WBCIR_REG_WCEIR_INDEX */ | ||
136 | #define WBCIR_REG_ADDR0 0x00 | ||
137 | |||
138 | /* Valid banks for the SP3 UART */ | ||
139 | enum wbcir_bank { | ||
140 | WBCIR_BANK_0 = 0x00, | ||
141 | WBCIR_BANK_1 = 0x80, | ||
142 | WBCIR_BANK_2 = 0xE0, | ||
143 | WBCIR_BANK_3 = 0xE4, | ||
144 | WBCIR_BANK_4 = 0xE8, | ||
145 | WBCIR_BANK_5 = 0xEC, | ||
146 | WBCIR_BANK_6 = 0xF0, | ||
147 | WBCIR_BANK_7 = 0xF4, | ||
148 | }; | ||
149 | |||
150 | /* Supported power-on IR Protocols */ | ||
151 | enum wbcir_protocol { | ||
152 | IR_PROTOCOL_RC5 = 0x0, | ||
153 | IR_PROTOCOL_NEC = 0x1, | ||
154 | IR_PROTOCOL_RC6 = 0x2, | ||
155 | }; | ||
156 | |||
157 | /* Misc */ | ||
158 | #define WBCIR_NAME "Winbond CIR" | ||
159 | #define WBCIR_ID_FAMILY 0xF1 /* Family ID for the WPCD376I */ | ||
160 | #define WBCIR_ID_CHIP 0x04 /* Chip ID for the WPCD376I */ | ||
161 | #define INVALID_SCANCODE 0x7FFFFFFF /* Invalid with all protos */ | ||
162 | #define WAKEUP_IOMEM_LEN 0x10 /* Wake-Up I/O Reg Len */ | ||
163 | #define EHFUNC_IOMEM_LEN 0x10 /* Enhanced Func I/O Reg Len */ | ||
164 | #define SP_IOMEM_LEN 0x08 /* Serial Port 3 (IR) Reg Len */ | ||
165 | |||
166 | /* Per-device data */ | ||
167 | struct wbcir_data { | ||
168 | spinlock_t spinlock; | ||
169 | |||
170 | unsigned long wbase; /* Wake-Up Baseaddr */ | ||
171 | unsigned long ebase; /* Enhanced Func. Baseaddr */ | ||
172 | unsigned long sbase; /* Serial Port Baseaddr */ | ||
173 | unsigned int irq; /* Serial Port IRQ */ | ||
174 | |||
175 | struct rc_dev *dev; | ||
176 | |||
177 | struct led_trigger *rxtrigger; | ||
178 | struct led_trigger *txtrigger; | ||
179 | struct led_classdev led; | ||
180 | |||
181 | /* RX irdata state */ | ||
182 | bool irdata_active; | ||
183 | bool irdata_error; | ||
184 | struct ir_raw_event ev; | ||
185 | }; | ||
186 | |||
187 | static enum wbcir_protocol protocol = IR_PROTOCOL_RC6; | ||
188 | module_param(protocol, uint, 0444); | ||
189 | MODULE_PARM_DESC(protocol, "IR protocol to use for the power-on command " | ||
190 | "(0 = RC5, 1 = NEC, 2 = RC6A, default)"); | ||
191 | |||
192 | static int invert; /* default = 0 */ | ||
193 | module_param(invert, bool, 0444); | ||
194 | MODULE_PARM_DESC(invert, "Invert the signal from the IR receiver"); | ||
195 | |||
196 | static unsigned int wake_sc = 0x800F040C; | ||
197 | module_param(wake_sc, uint, 0644); | ||
198 | MODULE_PARM_DESC(wake_sc, "Scancode of the power-on IR command"); | ||
199 | |||
200 | static unsigned int wake_rc6mode = 6; | ||
201 | module_param(wake_rc6mode, uint, 0644); | ||
202 | MODULE_PARM_DESC(wake_rc6mode, "RC6 mode for the power-on command " | ||
203 | "(0 = 0, 6 = 6A, default)"); | ||
204 | |||
205 | |||
206 | |||
207 | /***************************************************************************** | ||
208 | * | ||
209 | * UTILITY FUNCTIONS | ||
210 | * | ||
211 | *****************************************************************************/ | ||
212 | |||
213 | /* Caller needs to hold wbcir_lock */ | ||
214 | static void | ||
215 | wbcir_set_bits(unsigned long addr, u8 bits, u8 mask) | ||
216 | { | ||
217 | u8 val; | ||
218 | |||
219 | val = inb(addr); | ||
220 | val = ((val & ~mask) | (bits & mask)); | ||
221 | outb(val, addr); | ||
222 | } | ||
223 | |||
224 | /* Selects the register bank for the serial port */ | ||
225 | static inline void | ||
226 | wbcir_select_bank(struct wbcir_data *data, enum wbcir_bank bank) | ||
227 | { | ||
228 | outb(bank, data->sbase + WBCIR_REG_SP3_BSR); | ||
229 | } | ||
230 | |||
231 | static enum led_brightness | ||
232 | wbcir_led_brightness_get(struct led_classdev *led_cdev) | ||
233 | { | ||
234 | struct wbcir_data *data = container_of(led_cdev, | ||
235 | struct wbcir_data, | ||
236 | led); | ||
237 | |||
238 | if (inb(data->ebase + WBCIR_REG_ECEIR_CTS) & WBCIR_LED_ENABLE) | ||
239 | return LED_FULL; | ||
240 | else | ||
241 | return LED_OFF; | ||
242 | } | ||
243 | |||
244 | static void | ||
245 | wbcir_led_brightness_set(struct led_classdev *led_cdev, | ||
246 | enum led_brightness brightness) | ||
247 | { | ||
248 | struct wbcir_data *data = container_of(led_cdev, | ||
249 | struct wbcir_data, | ||
250 | led); | ||
251 | |||
252 | wbcir_set_bits(data->ebase + WBCIR_REG_ECEIR_CTS, | ||
253 | brightness == LED_OFF ? 0x00 : WBCIR_LED_ENABLE, | ||
254 | WBCIR_LED_ENABLE); | ||
255 | } | ||
256 | |||
257 | /* Manchester encodes bits to RC6 message cells (see wbcir_shutdown) */ | ||
258 | static u8 | ||
259 | wbcir_to_rc6cells(u8 val) | ||
260 | { | ||
261 | u8 coded = 0x00; | ||
262 | int i; | ||
263 | |||
264 | val &= 0x0F; | ||
265 | for (i = 0; i < 4; i++) { | ||
266 | if (val & 0x01) | ||
267 | coded |= 0x02 << (i * 2); | ||
268 | else | ||
269 | coded |= 0x01 << (i * 2); | ||
270 | val >>= 1; | ||
271 | } | ||
272 | |||
273 | return coded; | ||
274 | } | ||
275 | |||
276 | /***************************************************************************** | ||
277 | * | ||
278 | * INTERRUPT FUNCTIONS | ||
279 | * | ||
280 | *****************************************************************************/ | ||
281 | |||
282 | static irqreturn_t | ||
283 | wbcir_irq_handler(int irqno, void *cookie) | ||
284 | { | ||
285 | struct pnp_dev *device = cookie; | ||
286 | struct wbcir_data *data = pnp_get_drvdata(device); | ||
287 | unsigned long flags; | ||
288 | u8 irdata[8]; | ||
289 | u8 disable = true; | ||
290 | u8 status; | ||
291 | int i; | ||
292 | |||
293 | spin_lock_irqsave(&data->spinlock, flags); | ||
294 | |||
295 | wbcir_select_bank(data, WBCIR_BANK_0); | ||
296 | |||
297 | status = inb(data->sbase + WBCIR_REG_SP3_EIR); | ||
298 | |||
299 | if (!(status & (WBCIR_IRQ_RX | WBCIR_IRQ_ERR))) { | ||
300 | spin_unlock_irqrestore(&data->spinlock, flags); | ||
301 | return IRQ_NONE; | ||
302 | } | ||
303 | |||
304 | /* Check for e.g. buffer overflow */ | ||
305 | if (status & WBCIR_IRQ_ERR) { | ||
306 | data->irdata_error = true; | ||
307 | ir_raw_event_reset(data->dev); | ||
308 | } | ||
309 | |||
310 | if (!(status & WBCIR_IRQ_RX)) | ||
311 | goto out; | ||
312 | |||
313 | if (!data->irdata_active) { | ||
314 | data->irdata_active = true; | ||
315 | led_trigger_event(data->rxtrigger, LED_FULL); | ||
316 | } | ||
317 | |||
318 | /* Since RXHDLEV is set, at least 8 bytes are in the FIFO */ | ||
319 | insb(data->sbase + WBCIR_REG_SP3_RXDATA, &irdata[0], 8); | ||
320 | |||
321 | for (i = 0; i < 8; i++) { | ||
322 | u8 pulse; | ||
323 | u32 duration; | ||
324 | |||
325 | if (irdata[i] != 0xFF && irdata[i] != 0x00) | ||
326 | disable = false; | ||
327 | |||
328 | if (data->irdata_error) | ||
329 | continue; | ||
330 | |||
331 | pulse = irdata[i] & 0x80 ? false : true; | ||
332 | duration = (irdata[i] & 0x7F) * 10000; /* ns */ | ||
333 | |||
334 | if (data->ev.pulse != pulse) { | ||
335 | if (data->ev.duration != 0) { | ||
336 | ir_raw_event_store(data->dev, &data->ev); | ||
337 | data->ev.duration = 0; | ||
338 | } | ||
339 | |||
340 | data->ev.pulse = pulse; | ||
341 | } | ||
342 | |||
343 | data->ev.duration += duration; | ||
344 | } | ||
345 | |||
346 | if (disable) { | ||
347 | if (data->ev.duration != 0 && !data->irdata_error) { | ||
348 | ir_raw_event_store(data->dev, &data->ev); | ||
349 | data->ev.duration = 0; | ||
350 | } | ||
351 | |||
352 | /* Set RXINACTIVE */ | ||
353 | outb(WBCIR_RX_DISABLE, data->sbase + WBCIR_REG_SP3_ASCR); | ||
354 | |||
355 | /* Drain the FIFO */ | ||
356 | while (inb(data->sbase + WBCIR_REG_SP3_LSR) & WBCIR_RX_AVAIL) | ||
357 | inb(data->sbase + WBCIR_REG_SP3_RXDATA); | ||
358 | |||
359 | ir_raw_event_reset(data->dev); | ||
360 | data->irdata_error = false; | ||
361 | data->irdata_active = false; | ||
362 | led_trigger_event(data->rxtrigger, LED_OFF); | ||
363 | } | ||
364 | |||
365 | ir_raw_event_handle(data->dev); | ||
366 | |||
367 | out: | ||
368 | spin_unlock_irqrestore(&data->spinlock, flags); | ||
369 | return IRQ_HANDLED; | ||
370 | } | ||
371 | |||
372 | |||
373 | |||
374 | /***************************************************************************** | ||
375 | * | ||
376 | * SETUP/INIT/SUSPEND/RESUME FUNCTIONS | ||
377 | * | ||
378 | *****************************************************************************/ | ||
379 | |||
380 | static void | ||
381 | wbcir_shutdown(struct pnp_dev *device) | ||
382 | { | ||
383 | struct device *dev = &device->dev; | ||
384 | struct wbcir_data *data = pnp_get_drvdata(device); | ||
385 | int do_wake = 1; | ||
386 | u8 match[11]; | ||
387 | u8 mask[11]; | ||
388 | u8 rc6_csl = 0; | ||
389 | int i; | ||
390 | |||
391 | memset(match, 0, sizeof(match)); | ||
392 | memset(mask, 0, sizeof(mask)); | ||
393 | |||
394 | if (wake_sc == INVALID_SCANCODE || !device_may_wakeup(dev)) { | ||
395 | do_wake = 0; | ||
396 | goto finish; | ||
397 | } | ||
398 | |||
399 | switch (protocol) { | ||
400 | case IR_PROTOCOL_RC5: | ||
401 | if (wake_sc > 0xFFF) { | ||
402 | do_wake = 0; | ||
403 | dev_err(dev, "RC5 - Invalid wake scancode\n"); | ||
404 | break; | ||
405 | } | ||
406 | |||
407 | /* Mask = 13 bits, ex toggle */ | ||
408 | mask[0] = 0xFF; | ||
409 | mask[1] = 0x17; | ||
410 | |||
411 | match[0] = (wake_sc & 0x003F); /* 6 command bits */ | ||
412 | match[0] |= (wake_sc & 0x0180) >> 1; /* 2 address bits */ | ||
413 | match[1] = (wake_sc & 0x0E00) >> 9; /* 3 address bits */ | ||
414 | if (!(wake_sc & 0x0040)) /* 2nd start bit */ | ||
415 | match[1] |= 0x10; | ||
416 | |||
417 | break; | ||
418 | |||
419 | case IR_PROTOCOL_NEC: | ||
420 | if (wake_sc > 0xFFFFFF) { | ||
421 | do_wake = 0; | ||
422 | dev_err(dev, "NEC - Invalid wake scancode\n"); | ||
423 | break; | ||
424 | } | ||
425 | |||
426 | mask[0] = mask[1] = mask[2] = mask[3] = 0xFF; | ||
427 | |||
428 | match[1] = bitrev8((wake_sc & 0xFF)); | ||
429 | match[0] = ~match[1]; | ||
430 | |||
431 | match[3] = bitrev8((wake_sc & 0xFF00) >> 8); | ||
432 | if (wake_sc > 0xFFFF) | ||
433 | match[2] = bitrev8((wake_sc & 0xFF0000) >> 16); | ||
434 | else | ||
435 | match[2] = ~match[3]; | ||
436 | |||
437 | break; | ||
438 | |||
439 | case IR_PROTOCOL_RC6: | ||
440 | |||
441 | if (wake_rc6mode == 0) { | ||
442 | if (wake_sc > 0xFFFF) { | ||
443 | do_wake = 0; | ||
444 | dev_err(dev, "RC6 - Invalid wake scancode\n"); | ||
445 | break; | ||
446 | } | ||
447 | |||
448 | /* Command */ | ||
449 | match[0] = wbcir_to_rc6cells(wake_sc >> 0); | ||
450 | mask[0] = 0xFF; | ||
451 | match[1] = wbcir_to_rc6cells(wake_sc >> 4); | ||
452 | mask[1] = 0xFF; | ||
453 | |||
454 | /* Address */ | ||
455 | match[2] = wbcir_to_rc6cells(wake_sc >> 8); | ||
456 | mask[2] = 0xFF; | ||
457 | match[3] = wbcir_to_rc6cells(wake_sc >> 12); | ||
458 | mask[3] = 0xFF; | ||
459 | |||
460 | /* Header */ | ||
461 | match[4] = 0x50; /* mode1 = mode0 = 0, ignore toggle */ | ||
462 | mask[4] = 0xF0; | ||
463 | match[5] = 0x09; /* start bit = 1, mode2 = 0 */ | ||
464 | mask[5] = 0x0F; | ||
465 | |||
466 | rc6_csl = 44; | ||
467 | |||
468 | } else if (wake_rc6mode == 6) { | ||
469 | i = 0; | ||
470 | |||
471 | /* Command */ | ||
472 | match[i] = wbcir_to_rc6cells(wake_sc >> 0); | ||
473 | mask[i++] = 0xFF; | ||
474 | match[i] = wbcir_to_rc6cells(wake_sc >> 4); | ||
475 | mask[i++] = 0xFF; | ||
476 | |||
477 | /* Address + Toggle */ | ||
478 | match[i] = wbcir_to_rc6cells(wake_sc >> 8); | ||
479 | mask[i++] = 0xFF; | ||
480 | match[i] = wbcir_to_rc6cells(wake_sc >> 12); | ||
481 | mask[i++] = 0x3F; | ||
482 | |||
483 | /* Customer bits 7 - 0 */ | ||
484 | match[i] = wbcir_to_rc6cells(wake_sc >> 16); | ||
485 | mask[i++] = 0xFF; | ||
486 | match[i] = wbcir_to_rc6cells(wake_sc >> 20); | ||
487 | mask[i++] = 0xFF; | ||
488 | |||
489 | if (wake_sc & 0x80000000) { | ||
490 | /* Customer range bit and bits 15 - 8 */ | ||
491 | match[i] = wbcir_to_rc6cells(wake_sc >> 24); | ||
492 | mask[i++] = 0xFF; | ||
493 | match[i] = wbcir_to_rc6cells(wake_sc >> 28); | ||
494 | mask[i++] = 0xFF; | ||
495 | rc6_csl = 76; | ||
496 | } else if (wake_sc <= 0x007FFFFF) { | ||
497 | rc6_csl = 60; | ||
498 | } else { | ||
499 | do_wake = 0; | ||
500 | dev_err(dev, "RC6 - Invalid wake scancode\n"); | ||
501 | break; | ||
502 | } | ||
503 | |||
504 | /* Header */ | ||
505 | match[i] = 0x93; /* mode1 = mode0 = 1, submode = 0 */ | ||
506 | mask[i++] = 0xFF; | ||
507 | match[i] = 0x0A; /* start bit = 1, mode2 = 1 */ | ||
508 | mask[i++] = 0x0F; | ||
509 | |||
510 | } else { | ||
511 | do_wake = 0; | ||
512 | dev_err(dev, "RC6 - Invalid wake mode\n"); | ||
513 | } | ||
514 | |||
515 | break; | ||
516 | |||
517 | default: | ||
518 | do_wake = 0; | ||
519 | break; | ||
520 | } | ||
521 | |||
522 | finish: | ||
523 | if (do_wake) { | ||
524 | /* Set compare and compare mask */ | ||
525 | wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_INDEX, | ||
526 | WBCIR_REGSEL_COMPARE | WBCIR_REG_ADDR0, | ||
527 | 0x3F); | ||
528 | outsb(data->wbase + WBCIR_REG_WCEIR_DATA, match, 11); | ||
529 | wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_INDEX, | ||
530 | WBCIR_REGSEL_MASK | WBCIR_REG_ADDR0, | ||
531 | 0x3F); | ||
532 | outsb(data->wbase + WBCIR_REG_WCEIR_DATA, mask, 11); | ||
533 | |||
534 | /* RC6 Compare String Len */ | ||
535 | outb(rc6_csl, data->wbase + WBCIR_REG_WCEIR_CSL); | ||
536 | |||
537 | /* Clear status bits NEC_REP, BUFF, MSG_END, MATCH */ | ||
538 | wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_STS, 0x17, 0x17); | ||
539 | |||
540 | /* Clear BUFF_EN, Clear END_EN, Set MATCH_EN */ | ||
541 | wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x01, 0x07); | ||
542 | |||
543 | /* Set CEIR_EN */ | ||
544 | wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x01, 0x01); | ||
545 | |||
546 | } else { | ||
547 | /* Clear BUFF_EN, Clear END_EN, Clear MATCH_EN */ | ||
548 | wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07); | ||
549 | |||
550 | /* Clear CEIR_EN */ | ||
551 | wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x00, 0x01); | ||
552 | } | ||
553 | |||
554 | /* Disable interrupts */ | ||
555 | wbcir_select_bank(data, WBCIR_BANK_0); | ||
556 | outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER); | ||
557 | |||
558 | /* Disable LED */ | ||
559 | data->irdata_active = false; | ||
560 | led_trigger_event(data->rxtrigger, LED_OFF); | ||
561 | |||
562 | /* | ||
563 | * ACPI will set the HW disable bit for SP3 which means that the | ||
564 | * output signals are left in an undefined state which may cause | ||
565 | * spurious interrupts which we need to ignore until the hardware | ||
566 | * is reinitialized. | ||
567 | */ | ||
568 | disable_irq(data->irq); | ||
569 | } | ||
570 | |||
571 | static int | ||
572 | wbcir_suspend(struct pnp_dev *device, pm_message_t state) | ||
573 | { | ||
574 | wbcir_shutdown(device); | ||
575 | return 0; | ||
576 | } | ||
577 | |||
578 | static void | ||
579 | wbcir_init_hw(struct wbcir_data *data) | ||
580 | { | ||
581 | u8 tmp; | ||
582 | |||
583 | /* Disable interrupts */ | ||
584 | wbcir_select_bank(data, WBCIR_BANK_0); | ||
585 | outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER); | ||
586 | |||
587 | /* Set PROT_SEL, RX_INV, Clear CEIR_EN (needed for the led) */ | ||
588 | tmp = protocol << 4; | ||
589 | if (invert) | ||
590 | tmp |= 0x08; | ||
591 | outb(tmp, data->wbase + WBCIR_REG_WCEIR_CTL); | ||
592 | |||
593 | /* Clear status bits NEC_REP, BUFF, MSG_END, MATCH */ | ||
594 | wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_STS, 0x17, 0x17); | ||
595 | |||
596 | /* Clear BUFF_EN, Clear END_EN, Clear MATCH_EN */ | ||
597 | wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07); | ||
598 | |||
599 | /* Set RC5 cell time to correspond to 36 kHz */ | ||
600 | wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CFG1, 0x4A, 0x7F); | ||
601 | |||
602 | /* Set IRTX_INV */ | ||
603 | if (invert) | ||
604 | outb(0x04, data->ebase + WBCIR_REG_ECEIR_CCTL); | ||
605 | else | ||
606 | outb(0x00, data->ebase + WBCIR_REG_ECEIR_CCTL); | ||
607 | |||
608 | /* | ||
609 | * Clear IR LED, set SP3 clock to 24Mhz | ||
610 | * set SP3_IRRX_SW to binary 01, helpfully not documented | ||
611 | */ | ||
612 | outb(0x10, data->ebase + WBCIR_REG_ECEIR_CTS); | ||
613 | |||
614 | /* Enable extended mode */ | ||
615 | wbcir_select_bank(data, WBCIR_BANK_2); | ||
616 | outb(WBCIR_EXT_ENABLE, data->sbase + WBCIR_REG_SP3_EXCR1); | ||
617 | |||
618 | /* | ||
619 | * Configure baud generator, IR data will be sampled at | ||
620 | * a bitrate of: (24Mhz * prescaler) / (divisor * 16). | ||
621 | * | ||
622 | * The ECIR registers include a flag to change the | ||
623 | * 24Mhz clock freq to 48Mhz. | ||
624 | * | ||
625 | * It's not documented in the specs, but fifo levels | ||
626 | * other than 16 seems to be unsupported. | ||
627 | */ | ||
628 | |||
629 | /* prescaler 1.0, tx/rx fifo lvl 16 */ | ||
630 | outb(0x30, data->sbase + WBCIR_REG_SP3_EXCR2); | ||
631 | |||
632 | /* Set baud divisor to generate one byte per bit/cell */ | ||
633 | switch (protocol) { | ||
634 | case IR_PROTOCOL_RC5: | ||
635 | outb(0xA7, data->sbase + WBCIR_REG_SP3_BGDL); | ||
636 | break; | ||
637 | case IR_PROTOCOL_RC6: | ||
638 | outb(0x53, data->sbase + WBCIR_REG_SP3_BGDL); | ||
639 | break; | ||
640 | case IR_PROTOCOL_NEC: | ||
641 | outb(0x69, data->sbase + WBCIR_REG_SP3_BGDL); | ||
642 | break; | ||
643 | } | ||
644 | outb(0x00, data->sbase + WBCIR_REG_SP3_BGDH); | ||
645 | |||
646 | /* Set CEIR mode */ | ||
647 | wbcir_select_bank(data, WBCIR_BANK_0); | ||
648 | outb(0xC0, data->sbase + WBCIR_REG_SP3_MCR); | ||
649 | inb(data->sbase + WBCIR_REG_SP3_LSR); /* Clear LSR */ | ||
650 | inb(data->sbase + WBCIR_REG_SP3_MSR); /* Clear MSR */ | ||
651 | |||
652 | /* Disable RX demod, run-length encoding/decoding, set freq span */ | ||
653 | wbcir_select_bank(data, WBCIR_BANK_7); | ||
654 | outb(0x10, data->sbase + WBCIR_REG_SP3_RCCFG); | ||
655 | |||
656 | /* Disable timer */ | ||
657 | wbcir_select_bank(data, WBCIR_BANK_4); | ||
658 | outb(0x00, data->sbase + WBCIR_REG_SP3_IRCR1); | ||
659 | |||
660 | /* Enable MSR interrupt, Clear AUX_IRX */ | ||
661 | wbcir_select_bank(data, WBCIR_BANK_5); | ||
662 | outb(0x00, data->sbase + WBCIR_REG_SP3_IRCR2); | ||
663 | |||
664 | /* Disable CRC */ | ||
665 | wbcir_select_bank(data, WBCIR_BANK_6); | ||
666 | outb(0x20, data->sbase + WBCIR_REG_SP3_IRCR3); | ||
667 | |||
668 | /* Set RX/TX (de)modulation freq, not really used */ | ||
669 | wbcir_select_bank(data, WBCIR_BANK_7); | ||
670 | outb(0xF2, data->sbase + WBCIR_REG_SP3_IRRXDC); | ||
671 | outb(0x69, data->sbase + WBCIR_REG_SP3_IRTXMC); | ||
672 | |||
673 | /* Set invert and pin direction */ | ||
674 | if (invert) | ||
675 | outb(0x10, data->sbase + WBCIR_REG_SP3_IRCFG4); | ||
676 | else | ||
677 | outb(0x00, data->sbase + WBCIR_REG_SP3_IRCFG4); | ||
678 | |||
679 | /* Set FIFO thresholds (RX = 8, TX = 3), reset RX/TX */ | ||
680 | wbcir_select_bank(data, WBCIR_BANK_0); | ||
681 | outb(0x97, data->sbase + WBCIR_REG_SP3_FCR); | ||
682 | |||
683 | /* Clear AUX status bits */ | ||
684 | outb(0xE0, data->sbase + WBCIR_REG_SP3_ASCR); | ||
685 | |||
686 | /* Clear IR decoding state */ | ||
687 | data->irdata_active = false; | ||
688 | led_trigger_event(data->rxtrigger, LED_OFF); | ||
689 | data->irdata_error = false; | ||
690 | data->ev.duration = 0; | ||
691 | ir_raw_event_reset(data->dev); | ||
692 | ir_raw_event_handle(data->dev); | ||
693 | |||
694 | /* Enable interrupts */ | ||
695 | outb(WBCIR_IRQ_RX | WBCIR_IRQ_ERR, data->sbase + WBCIR_REG_SP3_IER); | ||
696 | } | ||
697 | |||
698 | static int | ||
699 | wbcir_resume(struct pnp_dev *device) | ||
700 | { | ||
701 | struct wbcir_data *data = pnp_get_drvdata(device); | ||
702 | |||
703 | wbcir_init_hw(data); | ||
704 | enable_irq(data->irq); | ||
705 | |||
706 | return 0; | ||
707 | } | ||
708 | |||
709 | static int __devinit | ||
710 | wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id) | ||
711 | { | ||
712 | struct device *dev = &device->dev; | ||
713 | struct wbcir_data *data; | ||
714 | int err; | ||
715 | |||
716 | if (!(pnp_port_len(device, 0) == EHFUNC_IOMEM_LEN && | ||
717 | pnp_port_len(device, 1) == WAKEUP_IOMEM_LEN && | ||
718 | pnp_port_len(device, 2) == SP_IOMEM_LEN)) { | ||
719 | dev_err(dev, "Invalid resources\n"); | ||
720 | return -ENODEV; | ||
721 | } | ||
722 | |||
723 | data = kzalloc(sizeof(*data), GFP_KERNEL); | ||
724 | if (!data) { | ||
725 | err = -ENOMEM; | ||
726 | goto exit; | ||
727 | } | ||
728 | |||
729 | pnp_set_drvdata(device, data); | ||
730 | |||
731 | spin_lock_init(&data->spinlock); | ||
732 | data->ebase = pnp_port_start(device, 0); | ||
733 | data->wbase = pnp_port_start(device, 1); | ||
734 | data->sbase = pnp_port_start(device, 2); | ||
735 | data->irq = pnp_irq(device, 0); | ||
736 | |||
737 | if (data->wbase == 0 || data->ebase == 0 || | ||
738 | data->sbase == 0 || data->irq == 0) { | ||
739 | err = -ENODEV; | ||
740 | dev_err(dev, "Invalid resources\n"); | ||
741 | goto exit_free_data; | ||
742 | } | ||
743 | |||
744 | dev_dbg(&device->dev, "Found device " | ||
745 | "(w: 0x%lX, e: 0x%lX, s: 0x%lX, i: %u)\n", | ||
746 | data->wbase, data->ebase, data->sbase, data->irq); | ||
747 | |||
748 | if (!request_region(data->wbase, WAKEUP_IOMEM_LEN, DRVNAME)) { | ||
749 | dev_err(dev, "Region 0x%lx-0x%lx already in use!\n", | ||
750 | data->wbase, data->wbase + WAKEUP_IOMEM_LEN - 1); | ||
751 | err = -EBUSY; | ||
752 | goto exit_free_data; | ||
753 | } | ||
754 | |||
755 | if (!request_region(data->ebase, EHFUNC_IOMEM_LEN, DRVNAME)) { | ||
756 | dev_err(dev, "Region 0x%lx-0x%lx already in use!\n", | ||
757 | data->ebase, data->ebase + EHFUNC_IOMEM_LEN - 1); | ||
758 | err = -EBUSY; | ||
759 | goto exit_release_wbase; | ||
760 | } | ||
761 | |||
762 | if (!request_region(data->sbase, SP_IOMEM_LEN, DRVNAME)) { | ||
763 | dev_err(dev, "Region 0x%lx-0x%lx already in use!\n", | ||
764 | data->sbase, data->sbase + SP_IOMEM_LEN - 1); | ||
765 | err = -EBUSY; | ||
766 | goto exit_release_ebase; | ||
767 | } | ||
768 | |||
769 | err = request_irq(data->irq, wbcir_irq_handler, | ||
770 | IRQF_DISABLED, DRVNAME, device); | ||
771 | if (err) { | ||
772 | dev_err(dev, "Failed to claim IRQ %u\n", data->irq); | ||
773 | err = -EBUSY; | ||
774 | goto exit_release_sbase; | ||
775 | } | ||
776 | |||
777 | led_trigger_register_simple("cir-tx", &data->txtrigger); | ||
778 | if (!data->txtrigger) { | ||
779 | err = -ENOMEM; | ||
780 | goto exit_free_irq; | ||
781 | } | ||
782 | |||
783 | led_trigger_register_simple("cir-rx", &data->rxtrigger); | ||
784 | if (!data->rxtrigger) { | ||
785 | err = -ENOMEM; | ||
786 | goto exit_unregister_txtrigger; | ||
787 | } | ||
788 | |||
789 | data->led.name = "cir::activity"; | ||
790 | data->led.default_trigger = "cir-rx"; | ||
791 | data->led.brightness_set = wbcir_led_brightness_set; | ||
792 | data->led.brightness_get = wbcir_led_brightness_get; | ||
793 | err = led_classdev_register(&device->dev, &data->led); | ||
794 | if (err) | ||
795 | goto exit_unregister_rxtrigger; | ||
796 | |||
797 | data->dev = rc_allocate_device(); | ||
798 | if (!data->dev) { | ||
799 | err = -ENOMEM; | ||
800 | goto exit_unregister_led; | ||
801 | } | ||
802 | |||
803 | data->dev->driver_name = WBCIR_NAME; | ||
804 | data->dev->input_name = WBCIR_NAME; | ||
805 | data->dev->input_phys = "wbcir/cir0"; | ||
806 | data->dev->input_id.bustype = BUS_HOST; | ||
807 | data->dev->input_id.vendor = PCI_VENDOR_ID_WINBOND; | ||
808 | data->dev->input_id.product = WBCIR_ID_FAMILY; | ||
809 | data->dev->input_id.version = WBCIR_ID_CHIP; | ||
810 | data->dev->priv = data; | ||
811 | data->dev->dev.parent = &device->dev; | ||
812 | |||
813 | err = rc_register_device(data->dev); | ||
814 | if (err) | ||
815 | goto exit_free_rc; | ||
816 | |||
817 | device_init_wakeup(&device->dev, 1); | ||
818 | |||
819 | wbcir_init_hw(data); | ||
820 | |||
821 | return 0; | ||
822 | |||
823 | exit_free_rc: | ||
824 | rc_free_device(data->dev); | ||
825 | exit_unregister_led: | ||
826 | led_classdev_unregister(&data->led); | ||
827 | exit_unregister_rxtrigger: | ||
828 | led_trigger_unregister_simple(data->rxtrigger); | ||
829 | exit_unregister_txtrigger: | ||
830 | led_trigger_unregister_simple(data->txtrigger); | ||
831 | exit_free_irq: | ||
832 | free_irq(data->irq, device); | ||
833 | exit_release_sbase: | ||
834 | release_region(data->sbase, SP_IOMEM_LEN); | ||
835 | exit_release_ebase: | ||
836 | release_region(data->ebase, EHFUNC_IOMEM_LEN); | ||
837 | exit_release_wbase: | ||
838 | release_region(data->wbase, WAKEUP_IOMEM_LEN); | ||
839 | exit_free_data: | ||
840 | kfree(data); | ||
841 | pnp_set_drvdata(device, NULL); | ||
842 | exit: | ||
843 | return err; | ||
844 | } | ||
845 | |||
846 | static void __devexit | ||
847 | wbcir_remove(struct pnp_dev *device) | ||
848 | { | ||
849 | struct wbcir_data *data = pnp_get_drvdata(device); | ||
850 | |||
851 | /* Disable interrupts */ | ||
852 | wbcir_select_bank(data, WBCIR_BANK_0); | ||
853 | outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER); | ||
854 | |||
855 | free_irq(data->irq, device); | ||
856 | |||
857 | /* Clear status bits NEC_REP, BUFF, MSG_END, MATCH */ | ||
858 | wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_STS, 0x17, 0x17); | ||
859 | |||
860 | /* Clear CEIR_EN */ | ||
861 | wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x00, 0x01); | ||
862 | |||
863 | /* Clear BUFF_EN, END_EN, MATCH_EN */ | ||
864 | wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07); | ||
865 | |||
866 | rc_unregister_device(data->dev); | ||
867 | |||
868 | led_trigger_unregister_simple(data->rxtrigger); | ||
869 | led_trigger_unregister_simple(data->txtrigger); | ||
870 | led_classdev_unregister(&data->led); | ||
871 | |||
872 | /* This is ok since &data->led isn't actually used */ | ||
873 | wbcir_led_brightness_set(&data->led, LED_OFF); | ||
874 | |||
875 | release_region(data->wbase, WAKEUP_IOMEM_LEN); | ||
876 | release_region(data->ebase, EHFUNC_IOMEM_LEN); | ||
877 | release_region(data->sbase, SP_IOMEM_LEN); | ||
878 | |||
879 | kfree(data); | ||
880 | |||
881 | pnp_set_drvdata(device, NULL); | ||
882 | } | ||
883 | |||
884 | static const struct pnp_device_id wbcir_ids[] = { | ||
885 | { "WEC1022", 0 }, | ||
886 | { "", 0 } | ||
887 | }; | ||
888 | MODULE_DEVICE_TABLE(pnp, wbcir_ids); | ||
889 | |||
890 | static struct pnp_driver wbcir_driver = { | ||
891 | .name = WBCIR_NAME, | ||
892 | .id_table = wbcir_ids, | ||
893 | .probe = wbcir_probe, | ||
894 | .remove = __devexit_p(wbcir_remove), | ||
895 | .suspend = wbcir_suspend, | ||
896 | .resume = wbcir_resume, | ||
897 | .shutdown = wbcir_shutdown | ||
898 | }; | ||
899 | |||
900 | static int __init | ||
901 | wbcir_init(void) | ||
902 | { | ||
903 | int ret; | ||
904 | |||
905 | switch (protocol) { | ||
906 | case IR_PROTOCOL_RC5: | ||
907 | case IR_PROTOCOL_NEC: | ||
908 | case IR_PROTOCOL_RC6: | ||
909 | break; | ||
910 | default: | ||
911 | printk(KERN_ERR DRVNAME ": Invalid power-on protocol\n"); | ||
912 | } | ||
913 | |||
914 | ret = pnp_register_driver(&wbcir_driver); | ||
915 | if (ret) | ||
916 | printk(KERN_ERR DRVNAME ": Unable to register driver\n"); | ||
917 | |||
918 | return ret; | ||
919 | } | ||
920 | |||
921 | static void __exit | ||
922 | wbcir_exit(void) | ||
923 | { | ||
924 | pnp_unregister_driver(&wbcir_driver); | ||
925 | } | ||
926 | |||
927 | module_init(wbcir_init); | ||
928 | module_exit(wbcir_exit); | ||
929 | |||
930 | MODULE_AUTHOR("David Härdeman <david@hardeman.nu>"); | ||
931 | MODULE_DESCRIPTION("Winbond SuperI/O Consumer IR Driver"); | ||
932 | MODULE_LICENSE("GPL"); | ||