diff options
author | David Härdeman <david@hardeman.nu> | 2011-04-28 11:13:22 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2011-05-20 08:29:51 -0400 |
commit | c829f2672f312f2c19dc021b233a45d3dd850a4f (patch) | |
tree | 3d74712f745ea3061c947bc9f00d4f9eef42be6b /drivers/media/rc | |
parent | 67cdd42e0630eff3673808aa57007501353c73b6 (diff) |
[media] rc-core: add TX support to the winbond-cir driver
This patch adds preliminary IR TX capabilities to the
winbond-cir driver.
Signed-off-by: David Härdeman <david@hardeman.nu>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/rc')
-rw-r--r-- | drivers/media/rc/winbond-cir.c | 432 |
1 files changed, 357 insertions, 75 deletions
diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c index c7686ca81a6b..c42a8ed36c92 100644 --- a/drivers/media/rc/winbond-cir.c +++ b/drivers/media/rc/winbond-cir.c | |||
@@ -19,11 +19,12 @@ | |||
19 | * o DSDT dumps | 19 | * o DSDT dumps |
20 | * | 20 | * |
21 | * Supported features: | 21 | * Supported features: |
22 | * o IR Receive | ||
23 | * o IR Transmit | ||
22 | * o Wake-On-CIR functionality | 24 | * o Wake-On-CIR functionality |
23 | * | 25 | * |
24 | * To do: | 26 | * To do: |
25 | * o Learning | 27 | * o Learning |
26 | * o IR Transmit | ||
27 | * | 28 | * |
28 | * This program is free software; you can redistribute it and/or modify | 29 | * 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 | * it under the terms of the GNU General Public License as published by |
@@ -50,6 +51,8 @@ | |||
50 | #include <linux/io.h> | 51 | #include <linux/io.h> |
51 | #include <linux/bitrev.h> | 52 | #include <linux/bitrev.h> |
52 | #include <linux/slab.h> | 53 | #include <linux/slab.h> |
54 | #include <linux/wait.h> | ||
55 | #include <linux/sched.h> | ||
53 | #include <media/rc-core.h> | 56 | #include <media/rc-core.h> |
54 | 57 | ||
55 | #define DRVNAME "winbond-cir" | 58 | #define DRVNAME "winbond-cir" |
@@ -118,14 +121,24 @@ | |||
118 | #define WBCIR_IRQ_NONE 0x00 | 121 | #define WBCIR_IRQ_NONE 0x00 |
119 | /* RX data bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */ | 122 | /* RX data bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */ |
120 | #define WBCIR_IRQ_RX 0x01 | 123 | #define WBCIR_IRQ_RX 0x01 |
124 | /* TX data low bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */ | ||
125 | #define WBCIR_IRQ_TX_LOW 0x02 | ||
121 | /* Over/Under-flow bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */ | 126 | /* Over/Under-flow bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */ |
122 | #define WBCIR_IRQ_ERR 0x04 | 127 | #define WBCIR_IRQ_ERR 0x04 |
128 | /* TX data empty bit for WBCEIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */ | ||
129 | #define WBCIR_IRQ_TX_EMPTY 0x20 | ||
123 | /* Led enable/disable bit for WBCIR_REG_ECEIR_CTS */ | 130 | /* Led enable/disable bit for WBCIR_REG_ECEIR_CTS */ |
124 | #define WBCIR_LED_ENABLE 0x80 | 131 | #define WBCIR_LED_ENABLE 0x80 |
125 | /* RX data available bit for WBCIR_REG_SP3_LSR */ | 132 | /* RX data available bit for WBCIR_REG_SP3_LSR */ |
126 | #define WBCIR_RX_AVAIL 0x01 | 133 | #define WBCIR_RX_AVAIL 0x01 |
134 | /* RX data overrun error bit for WBCIR_REG_SP3_LSR */ | ||
135 | #define WBCIR_RX_OVERRUN 0x02 | ||
136 | /* TX End-Of-Transmission bit for WBCIR_REG_SP3_ASCR */ | ||
137 | #define WBCIR_TX_EOT 0x04 | ||
127 | /* RX disable bit for WBCIR_REG_SP3_ASCR */ | 138 | /* RX disable bit for WBCIR_REG_SP3_ASCR */ |
128 | #define WBCIR_RX_DISABLE 0x20 | 139 | #define WBCIR_RX_DISABLE 0x20 |
140 | /* TX data underrun error bit for WBCIR_REG_SP3_ASCR */ | ||
141 | #define WBCIR_TX_UNDERRUN 0x40 | ||
129 | /* Extended mode enable bit for WBCIR_REG_SP3_EXCR1 */ | 142 | /* Extended mode enable bit for WBCIR_REG_SP3_EXCR1 */ |
130 | #define WBCIR_EXT_ENABLE 0x01 | 143 | #define WBCIR_EXT_ENABLE 0x01 |
131 | /* Select compare register in WBCIR_REG_WCEIR_INDEX (bits 5 & 6) */ | 144 | /* Select compare register in WBCIR_REG_WCEIR_INDEX (bits 5 & 6) */ |
@@ -154,6 +167,21 @@ enum wbcir_protocol { | |||
154 | IR_PROTOCOL_RC6 = 0x2, | 167 | IR_PROTOCOL_RC6 = 0x2, |
155 | }; | 168 | }; |
156 | 169 | ||
170 | /* Possible states for IR reception */ | ||
171 | enum wbcir_rxstate { | ||
172 | WBCIR_RXSTATE_INACTIVE = 0, | ||
173 | WBCIR_RXSTATE_ACTIVE, | ||
174 | WBCIR_RXSTATE_ERROR | ||
175 | }; | ||
176 | |||
177 | /* Possible states for IR transmission */ | ||
178 | enum wbcir_txstate { | ||
179 | WBCIR_TXSTATE_INACTIVE = 0, | ||
180 | WBCIR_TXSTATE_ACTIVE, | ||
181 | WBCIR_TXSTATE_DONE, | ||
182 | WBCIR_TXSTATE_ERROR | ||
183 | }; | ||
184 | |||
157 | /* Misc */ | 185 | /* Misc */ |
158 | #define WBCIR_NAME "Winbond CIR" | 186 | #define WBCIR_NAME "Winbond CIR" |
159 | #define WBCIR_ID_FAMILY 0xF1 /* Family ID for the WPCD376I */ | 187 | #define WBCIR_ID_FAMILY 0xF1 /* Family ID for the WPCD376I */ |
@@ -166,22 +194,29 @@ enum wbcir_protocol { | |||
166 | /* Per-device data */ | 194 | /* Per-device data */ |
167 | struct wbcir_data { | 195 | struct wbcir_data { |
168 | spinlock_t spinlock; | 196 | spinlock_t spinlock; |
197 | struct rc_dev *dev; | ||
198 | struct led_classdev led; | ||
169 | 199 | ||
170 | unsigned long wbase; /* Wake-Up Baseaddr */ | 200 | unsigned long wbase; /* Wake-Up Baseaddr */ |
171 | unsigned long ebase; /* Enhanced Func. Baseaddr */ | 201 | unsigned long ebase; /* Enhanced Func. Baseaddr */ |
172 | unsigned long sbase; /* Serial Port Baseaddr */ | 202 | unsigned long sbase; /* Serial Port Baseaddr */ |
173 | unsigned int irq; /* Serial Port IRQ */ | 203 | unsigned int irq; /* Serial Port IRQ */ |
204 | u8 irqmask; | ||
174 | 205 | ||
175 | struct rc_dev *dev; | 206 | /* RX state */ |
176 | 207 | enum wbcir_rxstate rxstate; | |
177 | struct led_trigger *rxtrigger; | 208 | struct led_trigger *rxtrigger; |
178 | struct led_trigger *txtrigger; | 209 | struct ir_raw_event rxev; |
179 | struct led_classdev led; | ||
180 | 210 | ||
181 | /* RX irdata state */ | 211 | /* TX state */ |
182 | bool irdata_active; | 212 | enum wbcir_txstate txstate; |
183 | bool irdata_error; | 213 | struct led_trigger *txtrigger; |
184 | struct ir_raw_event ev; | 214 | u32 txlen; |
215 | u32 txoff; | ||
216 | u32 *txbuf; | ||
217 | wait_queue_head_t txwaitq; | ||
218 | u8 txmask; | ||
219 | u32 txcarrier; | ||
185 | }; | 220 | }; |
186 | 221 | ||
187 | static enum wbcir_protocol protocol = IR_PROTOCOL_RC6; | 222 | static enum wbcir_protocol protocol = IR_PROTOCOL_RC6; |
@@ -193,6 +228,10 @@ static int invert; /* default = 0 */ | |||
193 | module_param(invert, bool, 0444); | 228 | module_param(invert, bool, 0444); |
194 | MODULE_PARM_DESC(invert, "Invert the signal from the IR receiver"); | 229 | MODULE_PARM_DESC(invert, "Invert the signal from the IR receiver"); |
195 | 230 | ||
231 | static int txandrx; /* default = 0 */ | ||
232 | module_param(txandrx, bool, 0444); | ||
233 | MODULE_PARM_DESC(invert, "Allow simultaneous TX and RX"); | ||
234 | |||
196 | static unsigned int wake_sc = 0x800F040C; | 235 | static unsigned int wake_sc = 0x800F040C; |
197 | module_param(wake_sc, uint, 0644); | 236 | module_param(wake_sc, uint, 0644); |
198 | MODULE_PARM_DESC(wake_sc, "Scancode of the power-on IR command"); | 237 | MODULE_PARM_DESC(wake_sc, "Scancode of the power-on IR command"); |
@@ -228,6 +267,17 @@ wbcir_select_bank(struct wbcir_data *data, enum wbcir_bank bank) | |||
228 | outb(bank, data->sbase + WBCIR_REG_SP3_BSR); | 267 | outb(bank, data->sbase + WBCIR_REG_SP3_BSR); |
229 | } | 268 | } |
230 | 269 | ||
270 | static inline void | ||
271 | wbcir_set_irqmask(struct wbcir_data *data, u8 irqmask) | ||
272 | { | ||
273 | if (data->irqmask == irqmask) | ||
274 | return; | ||
275 | |||
276 | wbcir_select_bank(data, WBCIR_BANK_0); | ||
277 | outb(irqmask, data->sbase + WBCIR_REG_SP3_IER); | ||
278 | data->irqmask = irqmask; | ||
279 | } | ||
280 | |||
231 | static enum led_brightness | 281 | static enum led_brightness |
232 | wbcir_led_brightness_get(struct led_classdev *led_cdev) | 282 | wbcir_led_brightness_get(struct led_classdev *led_cdev) |
233 | { | 283 | { |
@@ -279,39 +329,15 @@ wbcir_to_rc6cells(u8 val) | |||
279 | * | 329 | * |
280 | *****************************************************************************/ | 330 | *****************************************************************************/ |
281 | 331 | ||
282 | static irqreturn_t | 332 | static void |
283 | wbcir_irq_handler(int irqno, void *cookie) | 333 | wbcir_irq_rx(struct wbcir_data *data, struct pnp_dev *device) |
284 | { | 334 | { |
285 | struct pnp_dev *device = cookie; | ||
286 | struct wbcir_data *data = pnp_get_drvdata(device); | ||
287 | unsigned long flags; | ||
288 | u8 irdata[8]; | 335 | u8 irdata[8]; |
289 | u8 disable = true; | 336 | bool disable = true; |
290 | u8 status; | 337 | unsigned int i; |
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 | 338 | ||
313 | if (!data->irdata_active) { | 339 | if (data->rxstate == WBCIR_RXSTATE_INACTIVE) { |
314 | data->irdata_active = true; | 340 | data->rxstate = WBCIR_RXSTATE_ACTIVE; |
315 | led_trigger_event(data->rxtrigger, LED_FULL); | 341 | led_trigger_event(data->rxtrigger, LED_FULL); |
316 | } | 342 | } |
317 | 343 | ||
@@ -325,28 +351,29 @@ wbcir_irq_handler(int irqno, void *cookie) | |||
325 | if (irdata[i] != 0xFF && irdata[i] != 0x00) | 351 | if (irdata[i] != 0xFF && irdata[i] != 0x00) |
326 | disable = false; | 352 | disable = false; |
327 | 353 | ||
328 | if (data->irdata_error) | 354 | if (data->rxstate == WBCIR_RXSTATE_ERROR) |
329 | continue; | 355 | continue; |
330 | 356 | ||
331 | pulse = irdata[i] & 0x80 ? false : true; | 357 | pulse = irdata[i] & 0x80 ? false : true; |
332 | duration = (irdata[i] & 0x7F) * 10000; /* ns */ | 358 | duration = (irdata[i] & 0x7F) * 10000; /* ns */ |
333 | 359 | ||
334 | if (data->ev.pulse != pulse) { | 360 | if (data->rxev.pulse != pulse) { |
335 | if (data->ev.duration != 0) { | 361 | if (data->rxev.duration != 0) { |
336 | ir_raw_event_store(data->dev, &data->ev); | 362 | ir_raw_event_store(data->dev, &data->rxev); |
337 | data->ev.duration = 0; | 363 | data->rxev.duration = 0; |
338 | } | 364 | } |
339 | 365 | ||
340 | data->ev.pulse = pulse; | 366 | data->rxev.pulse = pulse; |
341 | } | 367 | } |
342 | 368 | ||
343 | data->ev.duration += duration; | 369 | data->rxev.duration += duration; |
344 | } | 370 | } |
345 | 371 | ||
346 | if (disable) { | 372 | if (disable) { |
347 | if (data->ev.duration != 0 && !data->irdata_error) { | 373 | if (data->rxev.duration != 0 && |
348 | ir_raw_event_store(data->dev, &data->ev); | 374 | data->rxstate != WBCIR_RXSTATE_ERROR) { |
349 | data->ev.duration = 0; | 375 | ir_raw_event_store(data->dev, &data->rxev); |
376 | data->rxev.duration = 0; | ||
350 | } | 377 | } |
351 | 378 | ||
352 | /* Set RXINACTIVE */ | 379 | /* Set RXINACTIVE */ |
@@ -357,19 +384,264 @@ wbcir_irq_handler(int irqno, void *cookie) | |||
357 | inb(data->sbase + WBCIR_REG_SP3_RXDATA); | 384 | inb(data->sbase + WBCIR_REG_SP3_RXDATA); |
358 | 385 | ||
359 | ir_raw_event_reset(data->dev); | 386 | ir_raw_event_reset(data->dev); |
360 | data->irdata_error = false; | ||
361 | data->irdata_active = false; | ||
362 | led_trigger_event(data->rxtrigger, LED_OFF); | 387 | led_trigger_event(data->rxtrigger, LED_OFF); |
388 | data->rxstate = WBCIR_RXSTATE_INACTIVE; | ||
363 | } | 389 | } |
364 | 390 | ||
365 | ir_raw_event_handle(data->dev); | 391 | ir_raw_event_handle(data->dev); |
392 | } | ||
393 | |||
394 | static void | ||
395 | wbcir_irq_tx(struct wbcir_data *data) | ||
396 | { | ||
397 | unsigned int space; | ||
398 | unsigned int used; | ||
399 | u8 bytes[16]; | ||
400 | u8 byte; | ||
401 | |||
402 | if (!data->txbuf) | ||
403 | return; | ||
404 | |||
405 | switch (data->txstate) { | ||
406 | case WBCIR_TXSTATE_INACTIVE: | ||
407 | /* TX FIFO empty */ | ||
408 | space = 16; | ||
409 | led_trigger_event(data->txtrigger, LED_FULL); | ||
410 | break; | ||
411 | case WBCIR_TXSTATE_ACTIVE: | ||
412 | /* TX FIFO low (3 bytes or less) */ | ||
413 | space = 13; | ||
414 | break; | ||
415 | case WBCIR_TXSTATE_ERROR: | ||
416 | space = 0; | ||
417 | break; | ||
418 | default: | ||
419 | return; | ||
420 | } | ||
421 | |||
422 | /* | ||
423 | * TX data is run-length coded in bytes: YXXXXXXX | ||
424 | * Y = space (1) or pulse (0) | ||
425 | * X = duration, encoded as (X + 1) * 10us (i.e 10 to 1280 us) | ||
426 | */ | ||
427 | for (used = 0; used < space && data->txoff != data->txlen; used++) { | ||
428 | if (data->txbuf[data->txoff] == 0) { | ||
429 | data->txoff++; | ||
430 | continue; | ||
431 | } | ||
432 | byte = min((u32)0x80, data->txbuf[data->txoff]); | ||
433 | data->txbuf[data->txoff] -= byte; | ||
434 | byte--; | ||
435 | byte |= (data->txoff % 2 ? 0x80 : 0x00); /* pulse/space */ | ||
436 | bytes[used] = byte; | ||
437 | } | ||
438 | |||
439 | while (data->txbuf[data->txoff] == 0 && data->txoff != data->txlen) | ||
440 | data->txoff++; | ||
441 | |||
442 | if (used == 0) { | ||
443 | /* Finished */ | ||
444 | if (data->txstate == WBCIR_TXSTATE_ERROR) | ||
445 | /* Clear TX underrun bit */ | ||
446 | outb(WBCIR_TX_UNDERRUN, data->sbase + WBCIR_REG_SP3_ASCR); | ||
447 | else | ||
448 | data->txstate = WBCIR_TXSTATE_DONE; | ||
449 | wbcir_set_irqmask(data, WBCIR_IRQ_RX | WBCIR_IRQ_ERR); | ||
450 | led_trigger_event(data->txtrigger, LED_OFF); | ||
451 | wake_up(&data->txwaitq); | ||
452 | } else if (data->txoff == data->txlen) { | ||
453 | /* At the end of transmission, tell the hw before last byte */ | ||
454 | outsb(data->sbase + WBCIR_REG_SP3_TXDATA, bytes, used - 1); | ||
455 | outb(WBCIR_TX_EOT, data->sbase + WBCIR_REG_SP3_ASCR); | ||
456 | outb(bytes[used - 1], data->sbase + WBCIR_REG_SP3_TXDATA); | ||
457 | wbcir_set_irqmask(data, WBCIR_IRQ_RX | WBCIR_IRQ_ERR | | ||
458 | WBCIR_IRQ_TX_EMPTY); | ||
459 | } else { | ||
460 | /* More data to follow... */ | ||
461 | outsb(data->sbase + WBCIR_REG_SP3_RXDATA, bytes, used); | ||
462 | if (data->txstate == WBCIR_TXSTATE_INACTIVE) { | ||
463 | wbcir_set_irqmask(data, WBCIR_IRQ_RX | WBCIR_IRQ_ERR | | ||
464 | WBCIR_IRQ_TX_LOW); | ||
465 | data->txstate = WBCIR_TXSTATE_ACTIVE; | ||
466 | } | ||
467 | } | ||
468 | } | ||
469 | |||
470 | static irqreturn_t | ||
471 | wbcir_irq_handler(int irqno, void *cookie) | ||
472 | { | ||
473 | struct pnp_dev *device = cookie; | ||
474 | struct wbcir_data *data = pnp_get_drvdata(device); | ||
475 | unsigned long flags; | ||
476 | u8 status; | ||
477 | |||
478 | spin_lock_irqsave(&data->spinlock, flags); | ||
479 | wbcir_select_bank(data, WBCIR_BANK_0); | ||
480 | status = inb(data->sbase + WBCIR_REG_SP3_EIR); | ||
481 | status &= data->irqmask; | ||
482 | |||
483 | if (!status) { | ||
484 | spin_unlock_irqrestore(&data->spinlock, flags); | ||
485 | return IRQ_NONE; | ||
486 | } | ||
487 | |||
488 | if (status & WBCIR_IRQ_ERR) { | ||
489 | /* RX overflow? (read clears bit) */ | ||
490 | if (inb(data->sbase + WBCIR_REG_SP3_LSR) & WBCIR_RX_OVERRUN) { | ||
491 | data->rxstate = WBCIR_RXSTATE_ERROR; | ||
492 | ir_raw_event_reset(data->dev); | ||
493 | } | ||
494 | |||
495 | /* TX underflow? */ | ||
496 | if (inb(data->sbase + WBCIR_REG_SP3_ASCR) & WBCIR_TX_UNDERRUN) | ||
497 | data->txstate = WBCIR_TXSTATE_ERROR; | ||
498 | } | ||
499 | |||
500 | if (status & WBCIR_IRQ_RX) | ||
501 | wbcir_irq_rx(data, device); | ||
502 | |||
503 | if (status & (WBCIR_IRQ_TX_LOW | WBCIR_IRQ_TX_EMPTY)) | ||
504 | wbcir_irq_tx(data); | ||
366 | 505 | ||
367 | out: | ||
368 | spin_unlock_irqrestore(&data->spinlock, flags); | 506 | spin_unlock_irqrestore(&data->spinlock, flags); |
369 | return IRQ_HANDLED; | 507 | return IRQ_HANDLED; |
370 | } | 508 | } |
371 | 509 | ||
510 | /***************************************************************************** | ||
511 | * | ||
512 | * RC-CORE INTERFACE FUNCTIONS | ||
513 | * | ||
514 | *****************************************************************************/ | ||
515 | |||
516 | static int | ||
517 | wbcir_txcarrier(struct rc_dev *dev, u32 carrier) | ||
518 | { | ||
519 | struct wbcir_data *data = dev->priv; | ||
520 | unsigned long flags; | ||
521 | u8 val; | ||
522 | u32 freq; | ||
523 | |||
524 | freq = DIV_ROUND_CLOSEST(carrier, 1000); | ||
525 | if (freq < 30 || freq > 60) | ||
526 | return -EINVAL; | ||
527 | |||
528 | switch (freq) { | ||
529 | case 58: | ||
530 | case 59: | ||
531 | case 60: | ||
532 | val = freq - 58; | ||
533 | freq *= 1000; | ||
534 | break; | ||
535 | case 57: | ||
536 | val = freq - 27; | ||
537 | freq = 56900; | ||
538 | break; | ||
539 | default: | ||
540 | val = freq - 27; | ||
541 | freq *= 1000; | ||
542 | break; | ||
543 | } | ||
544 | |||
545 | spin_lock_irqsave(&data->spinlock, flags); | ||
546 | if (data->txstate != WBCIR_TXSTATE_INACTIVE) { | ||
547 | spin_unlock_irqrestore(&data->spinlock, flags); | ||
548 | return -EBUSY; | ||
549 | } | ||
550 | |||
551 | if (data->txcarrier != freq) { | ||
552 | wbcir_select_bank(data, WBCIR_BANK_7); | ||
553 | wbcir_set_bits(data->sbase + WBCIR_REG_SP3_IRTXMC, val, 0x1F); | ||
554 | data->txcarrier = freq; | ||
555 | } | ||
556 | |||
557 | spin_unlock_irqrestore(&data->spinlock, flags); | ||
558 | return 0; | ||
559 | } | ||
560 | |||
561 | static int | ||
562 | wbcir_txmask(struct rc_dev *dev, u32 mask) | ||
563 | { | ||
564 | struct wbcir_data *data = dev->priv; | ||
565 | unsigned long flags; | ||
566 | u8 val; | ||
567 | |||
568 | /* Four outputs, only one output can be enabled at a time */ | ||
569 | switch (mask) { | ||
570 | case 0x1: | ||
571 | val = 0x0; | ||
572 | break; | ||
573 | case 0x2: | ||
574 | val = 0x1; | ||
575 | break; | ||
576 | case 0x4: | ||
577 | val = 0x2; | ||
578 | break; | ||
579 | case 0x8: | ||
580 | val = 0x3; | ||
581 | break; | ||
582 | default: | ||
583 | return -EINVAL; | ||
584 | } | ||
585 | |||
586 | spin_lock_irqsave(&data->spinlock, flags); | ||
587 | if (data->txstate != WBCIR_TXSTATE_INACTIVE) { | ||
588 | spin_unlock_irqrestore(&data->spinlock, flags); | ||
589 | return -EBUSY; | ||
590 | } | ||
591 | |||
592 | if (data->txmask != mask) { | ||
593 | wbcir_set_bits(data->ebase + WBCIR_REG_ECEIR_CTS, val, 0x0c); | ||
594 | data->txmask = mask; | ||
595 | } | ||
596 | |||
597 | spin_unlock_irqrestore(&data->spinlock, flags); | ||
598 | return 0; | ||
599 | } | ||
600 | |||
601 | static int | ||
602 | wbcir_tx(struct rc_dev *dev, int *buf, u32 bufsize) | ||
603 | { | ||
604 | struct wbcir_data *data = dev->priv; | ||
605 | u32 count; | ||
606 | unsigned i; | ||
607 | unsigned long flags; | ||
608 | |||
609 | /* bufsize has been sanity checked by the caller */ | ||
610 | count = bufsize / sizeof(int); | ||
611 | |||
612 | /* Not sure if this is possible, but better safe than sorry */ | ||
613 | spin_lock_irqsave(&data->spinlock, flags); | ||
614 | if (data->txstate != WBCIR_TXSTATE_INACTIVE) { | ||
615 | spin_unlock_irqrestore(&data->spinlock, flags); | ||
616 | return -EBUSY; | ||
617 | } | ||
618 | |||
619 | /* Convert values to multiples of 10us */ | ||
620 | for (i = 0; i < count; i++) | ||
621 | buf[i] = DIV_ROUND_CLOSEST(buf[i], 10); | ||
622 | |||
623 | /* Fill the TX fifo once, the irq handler will do the rest */ | ||
624 | data->txbuf = buf; | ||
625 | data->txlen = count; | ||
626 | data->txoff = 0; | ||
627 | wbcir_irq_tx(data); | ||
628 | |||
629 | /* Wait for the TX to complete */ | ||
630 | while (data->txstate == WBCIR_TXSTATE_ACTIVE) { | ||
631 | spin_unlock_irqrestore(&data->spinlock, flags); | ||
632 | wait_event(data->txwaitq, data->txstate != WBCIR_TXSTATE_ACTIVE); | ||
633 | spin_lock_irqsave(&data->spinlock, flags); | ||
634 | } | ||
635 | |||
636 | /* We're done */ | ||
637 | if (data->txstate == WBCIR_TXSTATE_ERROR) | ||
638 | count = -EAGAIN; | ||
639 | data->txstate = WBCIR_TXSTATE_INACTIVE; | ||
640 | data->txbuf = NULL; | ||
641 | spin_unlock_irqrestore(&data->spinlock, flags); | ||
372 | 642 | ||
643 | return count; | ||
644 | } | ||
373 | 645 | ||
374 | /***************************************************************************** | 646 | /***************************************************************************** |
375 | * | 647 | * |
@@ -551,21 +823,18 @@ finish: | |||
551 | wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x00, 0x01); | 823 | wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x00, 0x01); |
552 | } | 824 | } |
553 | 825 | ||
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 | /* | 826 | /* |
563 | * ACPI will set the HW disable bit for SP3 which means that the | 827 | * 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 | 828 | * output signals are left in an undefined state which may cause |
565 | * spurious interrupts which we need to ignore until the hardware | 829 | * spurious interrupts which we need to ignore until the hardware |
566 | * is reinitialized. | 830 | * is reinitialized. |
567 | */ | 831 | */ |
832 | wbcir_set_irqmask(data, WBCIR_IRQ_NONE); | ||
568 | disable_irq(data->irq); | 833 | disable_irq(data->irq); |
834 | |||
835 | /* Disable LED */ | ||
836 | led_trigger_event(data->rxtrigger, LED_OFF); | ||
837 | led_trigger_event(data->txtrigger, LED_OFF); | ||
569 | } | 838 | } |
570 | 839 | ||
571 | static int | 840 | static int |
@@ -581,8 +850,7 @@ wbcir_init_hw(struct wbcir_data *data) | |||
581 | u8 tmp; | 850 | u8 tmp; |
582 | 851 | ||
583 | /* Disable interrupts */ | 852 | /* Disable interrupts */ |
584 | wbcir_select_bank(data, WBCIR_BANK_0); | 853 | wbcir_set_irqmask(data, WBCIR_IRQ_NONE); |
585 | outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER); | ||
586 | 854 | ||
587 | /* Set PROT_SEL, RX_INV, Clear CEIR_EN (needed for the led) */ | 855 | /* Set PROT_SEL, RX_INV, Clear CEIR_EN (needed for the led) */ |
588 | tmp = protocol << 4; | 856 | tmp = protocol << 4; |
@@ -606,10 +874,11 @@ wbcir_init_hw(struct wbcir_data *data) | |||
606 | outb(0x00, data->ebase + WBCIR_REG_ECEIR_CCTL); | 874 | outb(0x00, data->ebase + WBCIR_REG_ECEIR_CCTL); |
607 | 875 | ||
608 | /* | 876 | /* |
609 | * Clear IR LED, set SP3 clock to 24Mhz | 877 | * Clear IR LED, set SP3 clock to 24Mhz, set TX mask to IRTX1, |
610 | * set SP3_IRRX_SW to binary 01, helpfully not documented | 878 | * set SP3_IRRX_SW to binary 01, helpfully not documented |
611 | */ | 879 | */ |
612 | outb(0x10, data->ebase + WBCIR_REG_ECEIR_CTS); | 880 | outb(0x10, data->ebase + WBCIR_REG_ECEIR_CTS); |
881 | data->txmask = 0x1; | ||
613 | 882 | ||
614 | /* Enable extended mode */ | 883 | /* Enable extended mode */ |
615 | wbcir_select_bank(data, WBCIR_BANK_2); | 884 | wbcir_select_bank(data, WBCIR_BANK_2); |
@@ -657,18 +926,21 @@ wbcir_init_hw(struct wbcir_data *data) | |||
657 | wbcir_select_bank(data, WBCIR_BANK_4); | 926 | wbcir_select_bank(data, WBCIR_BANK_4); |
658 | outb(0x00, data->sbase + WBCIR_REG_SP3_IRCR1); | 927 | outb(0x00, data->sbase + WBCIR_REG_SP3_IRCR1); |
659 | 928 | ||
660 | /* Enable MSR interrupt, Clear AUX_IRX */ | 929 | /* Disable MSR interrupt, clear AUX_IRX, mask RX during TX? */ |
661 | wbcir_select_bank(data, WBCIR_BANK_5); | 930 | wbcir_select_bank(data, WBCIR_BANK_5); |
662 | outb(0x00, data->sbase + WBCIR_REG_SP3_IRCR2); | 931 | outb(txandrx ? 0x03 : 0x02, data->sbase + WBCIR_REG_SP3_IRCR2); |
663 | 932 | ||
664 | /* Disable CRC */ | 933 | /* Disable CRC */ |
665 | wbcir_select_bank(data, WBCIR_BANK_6); | 934 | wbcir_select_bank(data, WBCIR_BANK_6); |
666 | outb(0x20, data->sbase + WBCIR_REG_SP3_IRCR3); | 935 | outb(0x20, data->sbase + WBCIR_REG_SP3_IRCR3); |
667 | 936 | ||
668 | /* Set RX/TX (de)modulation freq, not really used */ | 937 | /* Set RX demodulation freq, not really used */ |
669 | wbcir_select_bank(data, WBCIR_BANK_7); | 938 | wbcir_select_bank(data, WBCIR_BANK_7); |
670 | outb(0xF2, data->sbase + WBCIR_REG_SP3_IRRXDC); | 939 | outb(0xF2, data->sbase + WBCIR_REG_SP3_IRRXDC); |
940 | |||
941 | /* Set TX modulation, 36kHz, 7us pulse width */ | ||
671 | outb(0x69, data->sbase + WBCIR_REG_SP3_IRTXMC); | 942 | outb(0x69, data->sbase + WBCIR_REG_SP3_IRTXMC); |
943 | data->txcarrier = 36000; | ||
672 | 944 | ||
673 | /* Set invert and pin direction */ | 945 | /* Set invert and pin direction */ |
674 | if (invert) | 946 | if (invert) |
@@ -683,16 +955,23 @@ wbcir_init_hw(struct wbcir_data *data) | |||
683 | /* Clear AUX status bits */ | 955 | /* Clear AUX status bits */ |
684 | outb(0xE0, data->sbase + WBCIR_REG_SP3_ASCR); | 956 | outb(0xE0, data->sbase + WBCIR_REG_SP3_ASCR); |
685 | 957 | ||
686 | /* Clear IR decoding state */ | 958 | /* Clear RX state */ |
687 | data->irdata_active = false; | 959 | data->rxstate = WBCIR_RXSTATE_INACTIVE; |
688 | led_trigger_event(data->rxtrigger, LED_OFF); | 960 | data->rxev.duration = 0; |
689 | data->irdata_error = false; | ||
690 | data->ev.duration = 0; | ||
691 | ir_raw_event_reset(data->dev); | 961 | ir_raw_event_reset(data->dev); |
692 | ir_raw_event_handle(data->dev); | 962 | ir_raw_event_handle(data->dev); |
693 | 963 | ||
964 | /* | ||
965 | * Check TX state, if we did a suspend/resume cycle while TX was | ||
966 | * active, we will have a process waiting in txwaitq. | ||
967 | */ | ||
968 | if (data->txstate == WBCIR_TXSTATE_ACTIVE) { | ||
969 | data->txstate = WBCIR_TXSTATE_ERROR; | ||
970 | wake_up(&data->txwaitq); | ||
971 | } | ||
972 | |||
694 | /* Enable interrupts */ | 973 | /* Enable interrupts */ |
695 | outb(WBCIR_IRQ_RX | WBCIR_IRQ_ERR, data->sbase + WBCIR_REG_SP3_IER); | 974 | wbcir_set_irqmask(data, WBCIR_IRQ_RX | WBCIR_IRQ_ERR); |
696 | } | 975 | } |
697 | 976 | ||
698 | static int | 977 | static int |
@@ -729,6 +1008,7 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id) | |||
729 | pnp_set_drvdata(device, data); | 1008 | pnp_set_drvdata(device, data); |
730 | 1009 | ||
731 | spin_lock_init(&data->spinlock); | 1010 | spin_lock_init(&data->spinlock); |
1011 | init_waitqueue_head(&data->txwaitq); | ||
732 | data->ebase = pnp_port_start(device, 0); | 1012 | data->ebase = pnp_port_start(device, 0); |
733 | data->wbase = pnp_port_start(device, 1); | 1013 | data->wbase = pnp_port_start(device, 1); |
734 | data->sbase = pnp_port_start(device, 2); | 1014 | data->sbase = pnp_port_start(device, 2); |
@@ -807,6 +1087,10 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id) | |||
807 | data->dev->input_id.vendor = PCI_VENDOR_ID_WINBOND; | 1087 | data->dev->input_id.vendor = PCI_VENDOR_ID_WINBOND; |
808 | data->dev->input_id.product = WBCIR_ID_FAMILY; | 1088 | data->dev->input_id.product = WBCIR_ID_FAMILY; |
809 | data->dev->input_id.version = WBCIR_ID_CHIP; | 1089 | data->dev->input_id.version = WBCIR_ID_CHIP; |
1090 | data->dev->map_name = RC_MAP_RC6_MCE; | ||
1091 | data->dev->s_tx_mask = wbcir_txmask; | ||
1092 | data->dev->s_tx_carrier = wbcir_txcarrier; | ||
1093 | data->dev->tx_ir = wbcir_tx; | ||
810 | data->dev->priv = data; | 1094 | data->dev->priv = data; |
811 | data->dev->dev.parent = &device->dev; | 1095 | data->dev->dev.parent = &device->dev; |
812 | 1096 | ||
@@ -849,9 +1133,7 @@ wbcir_remove(struct pnp_dev *device) | |||
849 | struct wbcir_data *data = pnp_get_drvdata(device); | 1133 | struct wbcir_data *data = pnp_get_drvdata(device); |
850 | 1134 | ||
851 | /* Disable interrupts */ | 1135 | /* Disable interrupts */ |
852 | wbcir_select_bank(data, WBCIR_BANK_0); | 1136 | wbcir_set_irqmask(data, WBCIR_IRQ_NONE); |
853 | outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER); | ||
854 | |||
855 | free_irq(data->irq, device); | 1137 | free_irq(data->irq, device); |
856 | 1138 | ||
857 | /* Clear status bits NEC_REP, BUFF, MSG_END, MATCH */ | 1139 | /* Clear status bits NEC_REP, BUFF, MSG_END, MATCH */ |