diff options
author | Sean Young <sean@mess.org> | 2012-10-24 16:22:42 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2012-11-21 14:06:17 -0500 |
commit | 37b0b4e90d799cddfdd5d0a90ed470933f7e9859 (patch) | |
tree | 4c0ed67b3347459203511887f1ba2c83809b9e70 /drivers/media/rc/winbond-cir.c | |
parent | c496e716e0a7e3c486b99edbab5a6db7649824b1 (diff) |
[media] winbond-cir: add carrier detection
The winbond hardware has a counter for leading edges, which increases as
they are received. As we read raw IR from a fifo in an interrupt handler,
we cannot correlate them to specific IR pulses so we simply count all
pulses and edges until we go idle and disable the receiver.
Signed-off-by: Sean Young <sean@mess.org>
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 | 80 |
1 files changed, 75 insertions, 5 deletions
diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c index f040bbfd2100..7f3c476dde05 100644 --- a/drivers/media/rc/winbond-cir.c +++ b/drivers/media/rc/winbond-cir.c | |||
@@ -7,6 +7,7 @@ | |||
7 | * with minor modifications. | 7 | * with minor modifications. |
8 | * | 8 | * |
9 | * Original Author: David Härdeman <david@hardeman.nu> | 9 | * Original Author: David Härdeman <david@hardeman.nu> |
10 | * Copyright (C) 2012 Sean Young <sean@mess.org> | ||
10 | * Copyright (C) 2009 - 2011 David Härdeman <david@hardeman.nu> | 11 | * Copyright (C) 2009 - 2011 David Härdeman <david@hardeman.nu> |
11 | * | 12 | * |
12 | * Dedicated to my daughter Matilda, without whose loving attention this | 13 | * Dedicated to my daughter Matilda, without whose loving attention this |
@@ -22,9 +23,7 @@ | |||
22 | * o IR Receive | 23 | * o IR Receive |
23 | * o IR Transmit | 24 | * o IR Transmit |
24 | * o Wake-On-CIR functionality | 25 | * o Wake-On-CIR functionality |
25 | * | 26 | * o Carrier detection |
26 | * To do: | ||
27 | * o Learning | ||
28 | * | 27 | * |
29 | * This program is free software; you can redistribute it and/or modify | 28 | * This program is free software; you can redistribute it and/or modify |
30 | * it under the terms of the GNU General Public License as published by | 29 | * it under the terms of the GNU General Public License as published by |
@@ -149,6 +148,12 @@ | |||
149 | #define WBCIR_REGSEL_MASK 0x20 | 148 | #define WBCIR_REGSEL_MASK 0x20 |
150 | /* Starting address of selected register in WBCIR_REG_WCEIR_INDEX */ | 149 | /* Starting address of selected register in WBCIR_REG_WCEIR_INDEX */ |
151 | #define WBCIR_REG_ADDR0 0x00 | 150 | #define WBCIR_REG_ADDR0 0x00 |
151 | /* Enable carrier counter */ | ||
152 | #define WBCIR_CNTR_EN 0x01 | ||
153 | /* Reset carrier counter */ | ||
154 | #define WBCIR_CNTR_R 0x02 | ||
155 | /* Invert TX */ | ||
156 | #define WBCIR_IRTX_INV 0x04 | ||
152 | 157 | ||
153 | /* Valid banks for the SP3 UART */ | 158 | /* Valid banks for the SP3 UART */ |
154 | enum wbcir_bank { | 159 | enum wbcir_bank { |
@@ -207,6 +212,8 @@ struct wbcir_data { | |||
207 | /* RX state */ | 212 | /* RX state */ |
208 | enum wbcir_rxstate rxstate; | 213 | enum wbcir_rxstate rxstate; |
209 | struct led_trigger *rxtrigger; | 214 | struct led_trigger *rxtrigger; |
215 | int carrier_report_enabled; | ||
216 | u32 pulse_duration; | ||
210 | 217 | ||
211 | /* TX state */ | 218 | /* TX state */ |
212 | enum wbcir_txstate txstate; | 219 | enum wbcir_txstate txstate; |
@@ -329,6 +336,30 @@ wbcir_to_rc6cells(u8 val) | |||
329 | *****************************************************************************/ | 336 | *****************************************************************************/ |
330 | 337 | ||
331 | static void | 338 | static void |
339 | wbcir_carrier_report(struct wbcir_data *data) | ||
340 | { | ||
341 | unsigned counter = inb(data->ebase + WBCIR_REG_ECEIR_CNT_LO) | | ||
342 | inb(data->ebase + WBCIR_REG_ECEIR_CNT_HI) << 8; | ||
343 | |||
344 | if (counter > 0 && counter < 0xffff) { | ||
345 | DEFINE_IR_RAW_EVENT(ev); | ||
346 | |||
347 | ev.carrier_report = 1; | ||
348 | ev.carrier = DIV_ROUND_CLOSEST(counter * 1000000u, | ||
349 | data->pulse_duration); | ||
350 | |||
351 | ir_raw_event_store(data->dev, &ev); | ||
352 | } | ||
353 | |||
354 | /* reset and restart the counter */ | ||
355 | data->pulse_duration = 0; | ||
356 | wbcir_set_bits(data->ebase + WBCIR_REG_ECEIR_CCTL, WBCIR_CNTR_R, | ||
357 | WBCIR_CNTR_EN | WBCIR_CNTR_R); | ||
358 | wbcir_set_bits(data->ebase + WBCIR_REG_ECEIR_CCTL, WBCIR_CNTR_EN, | ||
359 | WBCIR_CNTR_EN | WBCIR_CNTR_R); | ||
360 | } | ||
361 | |||
362 | static void | ||
332 | wbcir_idle_rx(struct rc_dev *dev, bool idle) | 363 | wbcir_idle_rx(struct rc_dev *dev, bool idle) |
333 | { | 364 | { |
334 | struct wbcir_data *data = dev->priv; | 365 | struct wbcir_data *data = dev->priv; |
@@ -341,6 +372,10 @@ wbcir_idle_rx(struct rc_dev *dev, bool idle) | |||
341 | if (idle && data->rxstate != WBCIR_RXSTATE_INACTIVE) { | 372 | if (idle && data->rxstate != WBCIR_RXSTATE_INACTIVE) { |
342 | data->rxstate = WBCIR_RXSTATE_INACTIVE; | 373 | data->rxstate = WBCIR_RXSTATE_INACTIVE; |
343 | led_trigger_event(data->rxtrigger, LED_OFF); | 374 | led_trigger_event(data->rxtrigger, LED_OFF); |
375 | |||
376 | if (data->carrier_report_enabled) | ||
377 | wbcir_carrier_report(data); | ||
378 | |||
344 | /* Tell hardware to go idle by setting RXINACTIVE */ | 379 | /* Tell hardware to go idle by setting RXINACTIVE */ |
345 | outb(WBCIR_RX_DISABLE, data->sbase + WBCIR_REG_SP3_ASCR); | 380 | outb(WBCIR_RX_DISABLE, data->sbase + WBCIR_REG_SP3_ASCR); |
346 | } | 381 | } |
@@ -351,14 +386,21 @@ wbcir_irq_rx(struct wbcir_data *data, struct pnp_dev *device) | |||
351 | { | 386 | { |
352 | u8 irdata; | 387 | u8 irdata; |
353 | DEFINE_IR_RAW_EVENT(rawir); | 388 | DEFINE_IR_RAW_EVENT(rawir); |
389 | unsigned duration; | ||
354 | 390 | ||
355 | /* Since RXHDLEV is set, at least 8 bytes are in the FIFO */ | 391 | /* Since RXHDLEV is set, at least 8 bytes are in the FIFO */ |
356 | while (inb(data->sbase + WBCIR_REG_SP3_LSR) & WBCIR_RX_AVAIL) { | 392 | while (inb(data->sbase + WBCIR_REG_SP3_LSR) & WBCIR_RX_AVAIL) { |
357 | irdata = inb(data->sbase + WBCIR_REG_SP3_RXDATA); | 393 | irdata = inb(data->sbase + WBCIR_REG_SP3_RXDATA); |
358 | if (data->rxstate == WBCIR_RXSTATE_ERROR) | 394 | if (data->rxstate == WBCIR_RXSTATE_ERROR) |
359 | continue; | 395 | continue; |
396 | |||
397 | duration = ((irdata & 0x7F) + 1) * 2; | ||
360 | rawir.pulse = irdata & 0x80 ? false : true; | 398 | rawir.pulse = irdata & 0x80 ? false : true; |
361 | rawir.duration = US_TO_NS(((irdata & 0x7F) + 1) * 2); | 399 | rawir.duration = US_TO_NS(duration); |
400 | |||
401 | if (rawir.pulse) | ||
402 | data->pulse_duration += duration; | ||
403 | |||
362 | ir_raw_event_store_with_filter(data->dev, &rawir); | 404 | ir_raw_event_store_with_filter(data->dev, &rawir); |
363 | } | 405 | } |
364 | 406 | ||
@@ -488,6 +530,33 @@ wbcir_irq_handler(int irqno, void *cookie) | |||
488 | *****************************************************************************/ | 530 | *****************************************************************************/ |
489 | 531 | ||
490 | static int | 532 | static int |
533 | wbcir_set_carrier_report(struct rc_dev *dev, int enable) | ||
534 | { | ||
535 | struct wbcir_data *data = dev->priv; | ||
536 | unsigned long flags; | ||
537 | |||
538 | spin_lock_irqsave(&data->spinlock, flags); | ||
539 | |||
540 | if (data->carrier_report_enabled == enable) { | ||
541 | spin_unlock_irqrestore(&data->spinlock, flags); | ||
542 | return 0; | ||
543 | } | ||
544 | |||
545 | data->pulse_duration = 0; | ||
546 | wbcir_set_bits(data->ebase + WBCIR_REG_ECEIR_CCTL, WBCIR_CNTR_R, | ||
547 | WBCIR_CNTR_EN | WBCIR_CNTR_R); | ||
548 | |||
549 | if (enable && data->dev->idle) | ||
550 | wbcir_set_bits(data->ebase + WBCIR_REG_ECEIR_CCTL, | ||
551 | WBCIR_CNTR_EN, WBCIR_CNTR_EN | WBCIR_CNTR_R); | ||
552 | |||
553 | data->carrier_report_enabled = enable; | ||
554 | spin_unlock_irqrestore(&data->spinlock, flags); | ||
555 | |||
556 | return 0; | ||
557 | } | ||
558 | |||
559 | static int | ||
491 | wbcir_txcarrier(struct rc_dev *dev, u32 carrier) | 560 | wbcir_txcarrier(struct rc_dev *dev, u32 carrier) |
492 | { | 561 | { |
493 | struct wbcir_data *data = dev->priv; | 562 | struct wbcir_data *data = dev->priv; |
@@ -833,7 +902,7 @@ wbcir_init_hw(struct wbcir_data *data) | |||
833 | 902 | ||
834 | /* Set IRTX_INV */ | 903 | /* Set IRTX_INV */ |
835 | if (invert) | 904 | if (invert) |
836 | outb(0x04, data->ebase + WBCIR_REG_ECEIR_CCTL); | 905 | outb(WBCIR_IRTX_INV, data->ebase + WBCIR_REG_ECEIR_CCTL); |
837 | else | 906 | else |
838 | outb(0x00, data->ebase + WBCIR_REG_ECEIR_CCTL); | 907 | outb(0x00, data->ebase + WBCIR_REG_ECEIR_CCTL); |
839 | 908 | ||
@@ -1014,6 +1083,7 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id) | |||
1014 | data->dev->input_id.version = WBCIR_ID_CHIP; | 1083 | data->dev->input_id.version = WBCIR_ID_CHIP; |
1015 | data->dev->map_name = RC_MAP_RC6_MCE; | 1084 | data->dev->map_name = RC_MAP_RC6_MCE; |
1016 | data->dev->s_idle = wbcir_idle_rx; | 1085 | data->dev->s_idle = wbcir_idle_rx; |
1086 | data->dev->s_carrier_report = wbcir_set_carrier_report; | ||
1017 | data->dev->s_tx_mask = wbcir_txmask; | 1087 | data->dev->s_tx_mask = wbcir_txmask; |
1018 | data->dev->s_tx_carrier = wbcir_txcarrier; | 1088 | data->dev->s_tx_carrier = wbcir_txcarrier; |
1019 | data->dev->tx_ir = wbcir_tx; | 1089 | data->dev->tx_ir = wbcir_tx; |