diff options
Diffstat (limited to 'drivers/media/rc/winbond-cir.c')
| -rw-r--r-- | drivers/media/rc/winbond-cir.c | 646 |
1 files changed, 471 insertions, 175 deletions
diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c index 186de552200..a8b981f5ce2 100644 --- a/drivers/media/rc/winbond-cir.c +++ b/drivers/media/rc/winbond-cir.c @@ -6,8 +6,9 @@ * could probably support others (Winbond WEC102X, NatSemi, etc) * with minor modifications. * - * Original Author: David Härdeman <david@hardeman.nu> - * Copyright (C) 2009 - 2010 David Härdeman <david@hardeman.nu> + * Original Author: David Härdeman <david@hardeman.nu> + * Copyright (C) 2012 Sean Young <sean@mess.org> + * Copyright (C) 2009 - 2011 David Härdeman <david@hardeman.nu> * * Dedicated to my daughter Matilda, without whose loving attention this * driver would have been finished in half the time and with a fraction @@ -19,11 +20,10 @@ * o DSDT dumps * * Supported features: - * o Wake-On-CIR functionality - * - * To do: - * o Learning + * o IR Receive * o IR Transmit + * o Wake-On-CIR functionality + * o Carrier detection * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -40,6 +40,8 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/pnp.h> #include <linux/interrupt.h> @@ -50,6 +52,8 @@ #include <linux/io.h> #include <linux/bitrev.h> #include <linux/slab.h> +#include <linux/wait.h> +#include <linux/sched.h> #include <media/rc-core.h> #define DRVNAME "winbond-cir" @@ -118,14 +122,24 @@ #define WBCIR_IRQ_NONE 0x00 /* RX data bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */ #define WBCIR_IRQ_RX 0x01 +/* TX data low bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */ +#define WBCIR_IRQ_TX_LOW 0x02 /* Over/Under-flow bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */ #define WBCIR_IRQ_ERR 0x04 +/* TX data empty bit for WBCEIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */ +#define WBCIR_IRQ_TX_EMPTY 0x20 /* Led enable/disable bit for WBCIR_REG_ECEIR_CTS */ #define WBCIR_LED_ENABLE 0x80 /* RX data available bit for WBCIR_REG_SP3_LSR */ #define WBCIR_RX_AVAIL 0x01 +/* RX data overrun error bit for WBCIR_REG_SP3_LSR */ +#define WBCIR_RX_OVERRUN 0x02 +/* TX End-Of-Transmission bit for WBCIR_REG_SP3_ASCR */ +#define WBCIR_TX_EOT 0x04 /* RX disable bit for WBCIR_REG_SP3_ASCR */ #define WBCIR_RX_DISABLE 0x20 +/* TX data underrun error bit for WBCIR_REG_SP3_ASCR */ +#define WBCIR_TX_UNDERRUN 0x40 /* Extended mode enable bit for WBCIR_REG_SP3_EXCR1 */ #define WBCIR_EXT_ENABLE 0x01 /* Select compare register in WBCIR_REG_WCEIR_INDEX (bits 5 & 6) */ @@ -134,6 +148,14 @@ #define WBCIR_REGSEL_MASK 0x20 /* Starting address of selected register in WBCIR_REG_WCEIR_INDEX */ #define WBCIR_REG_ADDR0 0x00 +/* Enable carrier counter */ +#define WBCIR_CNTR_EN 0x01 +/* Reset carrier counter */ +#define WBCIR_CNTR_R 0x02 +/* Invert TX */ +#define WBCIR_IRTX_INV 0x04 +/* Receiver oversampling */ +#define WBCIR_RX_T_OV 0x40 /* Valid banks for the SP3 UART */ enum wbcir_bank { @@ -154,6 +176,20 @@ enum wbcir_protocol { IR_PROTOCOL_RC6 = 0x2, }; +/* Possible states for IR reception */ +enum wbcir_rxstate { + WBCIR_RXSTATE_INACTIVE = 0, + WBCIR_RXSTATE_ACTIVE, + WBCIR_RXSTATE_ERROR +}; + +/* Possible states for IR transmission */ +enum wbcir_txstate { + WBCIR_TXSTATE_INACTIVE = 0, + WBCIR_TXSTATE_ACTIVE, + WBCIR_TXSTATE_ERROR +}; + /* Misc */ #define WBCIR_NAME "Winbond CIR" #define WBCIR_ID_FAMILY 0xF1 /* Family ID for the WPCD376I */ @@ -166,22 +202,27 @@ enum wbcir_protocol { /* Per-device data */ struct wbcir_data { spinlock_t spinlock; + struct rc_dev *dev; + struct led_classdev led; unsigned long wbase; /* Wake-Up Baseaddr */ unsigned long ebase; /* Enhanced Func. Baseaddr */ unsigned long sbase; /* Serial Port Baseaddr */ unsigned int irq; /* Serial Port IRQ */ - - struct rc_dev *dev; - - struct led_trigger *rxtrigger; - struct led_trigger *txtrigger; - struct led_classdev led; - - /* RX irdata state */ - bool irdata_active; - bool irdata_error; - struct ir_raw_event ev; + u8 irqmask; + + /* RX state */ + enum wbcir_rxstate rxstate; + int carrier_report_enabled; + u32 pulse_duration; + + /* TX state */ + enum wbcir_txstate txstate; + u32 txlen; + u32 txoff; + u32 *txbuf; + u8 txmask; + u32 txcarrier; }; static enum wbcir_protocol protocol = IR_PROTOCOL_RC6; @@ -189,10 +230,14 @@ module_param(protocol, uint, 0444); MODULE_PARM_DESC(protocol, "IR protocol to use for the power-on command " "(0 = RC5, 1 = NEC, 2 = RC6A, default)"); -static int invert; /* default = 0 */ +static bool invert; /* default = 0 */ module_param(invert, bool, 0444); MODULE_PARM_DESC(invert, "Invert the signal from the IR receiver"); +static bool txandrx; /* default = 0 */ +module_param(txandrx, bool, 0444); +MODULE_PARM_DESC(txandrx, "Allow simultaneous TX and RX"); + static unsigned int wake_sc = 0x800F040C; module_param(wake_sc, uint, 0644); MODULE_PARM_DESC(wake_sc, "Scancode of the power-on IR command"); @@ -228,6 +273,17 @@ wbcir_select_bank(struct wbcir_data *data, enum wbcir_bank bank) outb(bank, data->sbase + WBCIR_REG_SP3_BSR); } +static inline void +wbcir_set_irqmask(struct wbcir_data *data, u8 irqmask) +{ + if (data->irqmask == irqmask) + return; + + wbcir_select_bank(data, WBCIR_BANK_0); + outb(irqmask, data->sbase + WBCIR_REG_SP3_IER); + data->irqmask = irqmask; +} + static enum led_brightness wbcir_led_brightness_get(struct led_classdev *led_cdev) { @@ -279,97 +335,353 @@ wbcir_to_rc6cells(u8 val) * *****************************************************************************/ +static void +wbcir_carrier_report(struct wbcir_data *data) +{ + unsigned counter = inb(data->ebase + WBCIR_REG_ECEIR_CNT_LO) | + inb(data->ebase + WBCIR_REG_ECEIR_CNT_HI) << 8; + + if (counter > 0 && counter < 0xffff) { + DEFINE_IR_RAW_EVENT(ev); + + ev.carrier_report = 1; + ev.carrier = DIV_ROUND_CLOSEST(counter * 1000000u, + data->pulse_duration); + + ir_raw_event_store(data->dev, &ev); + } + + /* reset and restart the counter */ + data->pulse_duration = 0; + wbcir_set_bits(data->ebase + WBCIR_REG_ECEIR_CCTL, WBCIR_CNTR_R, + WBCIR_CNTR_EN | WBCIR_CNTR_R); + wbcir_set_bits(data->ebase + WBCIR_REG_ECEIR_CCTL, WBCIR_CNTR_EN, + WBCIR_CNTR_EN | WBCIR_CNTR_R); +} + +static void +wbcir_idle_rx(struct rc_dev *dev, bool idle) +{ + struct wbcir_data *data = dev->priv; + + if (!idle && data->rxstate == WBCIR_RXSTATE_INACTIVE) + data->rxstate = WBCIR_RXSTATE_ACTIVE; + + if (idle && data->rxstate != WBCIR_RXSTATE_INACTIVE) { + data->rxstate = WBCIR_RXSTATE_INACTIVE; + + if (data->carrier_report_enabled) + wbcir_carrier_report(data); + + /* Tell hardware to go idle by setting RXINACTIVE */ + outb(WBCIR_RX_DISABLE, data->sbase + WBCIR_REG_SP3_ASCR); + } +} + +static void +wbcir_irq_rx(struct wbcir_data *data, struct pnp_dev *device) +{ + u8 irdata; + DEFINE_IR_RAW_EVENT(rawir); + unsigned duration; + + /* Since RXHDLEV is set, at least 8 bytes are in the FIFO */ + while (inb(data->sbase + WBCIR_REG_SP3_LSR) & WBCIR_RX_AVAIL) { + irdata = inb(data->sbase + WBCIR_REG_SP3_RXDATA); + if (data->rxstate == WBCIR_RXSTATE_ERROR) + continue; + + duration = ((irdata & 0x7F) + 1) * + (data->carrier_report_enabled ? 2 : 10); + rawir.pulse = irdata & 0x80 ? false : true; + rawir.duration = US_TO_NS(duration); + + if (rawir.pulse) + data->pulse_duration += duration; + + ir_raw_event_store_with_filter(data->dev, &rawir); + } + + ir_raw_event_handle(data->dev); +} + +static void +wbcir_irq_tx(struct wbcir_data *data) +{ + unsigned int space; + unsigned int used; + u8 bytes[16]; + u8 byte; + + if (!data->txbuf) + return; + + switch (data->txstate) { + case WBCIR_TXSTATE_INACTIVE: + /* TX FIFO empty */ + space = 16; + break; + case WBCIR_TXSTATE_ACTIVE: + /* TX FIFO low (3 bytes or less) */ + space = 13; + break; + case WBCIR_TXSTATE_ERROR: + space = 0; + break; + default: + return; + } + + /* + * TX data is run-length coded in bytes: YXXXXXXX + * Y = space (1) or pulse (0) + * X = duration, encoded as (X + 1) * 10us (i.e 10 to 1280 us) + */ + for (used = 0; used < space && data->txoff != data->txlen; used++) { + if (data->txbuf[data->txoff] == 0) { + data->txoff++; + continue; + } + byte = min((u32)0x80, data->txbuf[data->txoff]); + data->txbuf[data->txoff] -= byte; + byte--; + byte |= (data->txoff % 2 ? 0x80 : 0x00); /* pulse/space */ + bytes[used] = byte; + } + + while (data->txbuf[data->txoff] == 0 && data->txoff != data->txlen) + data->txoff++; + + if (used == 0) { + /* Finished */ + if (data->txstate == WBCIR_TXSTATE_ERROR) + /* Clear TX underrun bit */ + outb(WBCIR_TX_UNDERRUN, data->sbase + WBCIR_REG_SP3_ASCR); + wbcir_set_irqmask(data, WBCIR_IRQ_RX | WBCIR_IRQ_ERR); + kfree(data->txbuf); + data->txbuf = NULL; + data->txstate = WBCIR_TXSTATE_INACTIVE; + } else if (data->txoff == data->txlen) { + /* At the end of transmission, tell the hw before last byte */ + outsb(data->sbase + WBCIR_REG_SP3_TXDATA, bytes, used - 1); + outb(WBCIR_TX_EOT, data->sbase + WBCIR_REG_SP3_ASCR); + outb(bytes[used - 1], data->sbase + WBCIR_REG_SP3_TXDATA); + wbcir_set_irqmask(data, WBCIR_IRQ_RX | WBCIR_IRQ_ERR | + WBCIR_IRQ_TX_EMPTY); + } else { + /* More data to follow... */ + outsb(data->sbase + WBCIR_REG_SP3_RXDATA, bytes, used); + if (data->txstate == WBCIR_TXSTATE_INACTIVE) { + wbcir_set_irqmask(data, WBCIR_IRQ_RX | WBCIR_IRQ_ERR | + WBCIR_IRQ_TX_LOW); + data->txstate = WBCIR_TXSTATE_ACTIVE; + } + } +} + static irqreturn_t wbcir_irq_handler(int irqno, void *cookie) { struct pnp_dev *device = cookie; struct wbcir_data *data = pnp_get_drvdata(device); unsigned long flags; - u8 irdata[8]; - u8 disable = true; u8 status; - int i; spin_lock_irqsave(&data->spinlock, flags); - wbcir_select_bank(data, WBCIR_BANK_0); - status = inb(data->sbase + WBCIR_REG_SP3_EIR); + status &= data->irqmask; - if (!(status & (WBCIR_IRQ_RX | WBCIR_IRQ_ERR))) { + if (!status) { spin_unlock_irqrestore(&data->spinlock, flags); return IRQ_NONE; } - /* Check for e.g. buffer overflow */ if (status & WBCIR_IRQ_ERR) { - data->irdata_error = true; - ir_raw_event_reset(data->dev); + /* RX overflow? (read clears bit) */ + if (inb(data->sbase + WBCIR_REG_SP3_LSR) & WBCIR_RX_OVERRUN) { + data->rxstate = WBCIR_RXSTATE_ERROR; + ir_raw_event_reset(data->dev); + } + + /* TX underflow? */ + if (inb(data->sbase + WBCIR_REG_SP3_ASCR) & WBCIR_TX_UNDERRUN) + data->txstate = WBCIR_TXSTATE_ERROR; } - if (!(status & WBCIR_IRQ_RX)) - goto out; + if (status & WBCIR_IRQ_RX) + wbcir_irq_rx(data, device); + + if (status & (WBCIR_IRQ_TX_LOW | WBCIR_IRQ_TX_EMPTY)) + wbcir_irq_tx(data); + + spin_unlock_irqrestore(&data->spinlock, flags); + return IRQ_HANDLED; +} + +/***************************************************************************** + * + * RC-CORE INTERFACE FUNCTIONS + * + *****************************************************************************/ + +static int +wbcir_set_carrier_report(struct rc_dev *dev, int enable) +{ + struct wbcir_data *data = dev->priv; + unsigned long flags; + + spin_lock_irqsave(&data->spinlock, flags); - if (!data->irdata_active) { - data->irdata_active = true; - led_trigger_event(data->rxtrigger, LED_FULL); + if (data->carrier_report_enabled == enable) { + spin_unlock_irqrestore(&data->spinlock, flags); + return 0; } - /* Since RXHDLEV is set, at least 8 bytes are in the FIFO */ - insb(data->sbase + WBCIR_REG_SP3_RXDATA, &irdata[0], 8); + data->pulse_duration = 0; + wbcir_set_bits(data->ebase + WBCIR_REG_ECEIR_CCTL, WBCIR_CNTR_R, + WBCIR_CNTR_EN | WBCIR_CNTR_R); - for (i = 0; i < 8; i++) { - u8 pulse; - u32 duration; + if (enable && data->dev->idle) + wbcir_set_bits(data->ebase + WBCIR_REG_ECEIR_CCTL, + WBCIR_CNTR_EN, WBCIR_CNTR_EN | WBCIR_CNTR_R); - if (irdata[i] != 0xFF && irdata[i] != 0x00) - disable = false; + /* Set a higher sampling resolution if carrier reports are enabled */ + wbcir_select_bank(data, WBCIR_BANK_2); + data->dev->rx_resolution = US_TO_NS(enable ? 2 : 10); + outb(enable ? 0x03 : 0x0f, data->sbase + WBCIR_REG_SP3_BGDL); + outb(0x00, data->sbase + WBCIR_REG_SP3_BGDH); - if (data->irdata_error) - continue; + /* Enable oversampling if carrier reports are enabled */ + wbcir_select_bank(data, WBCIR_BANK_7); + wbcir_set_bits(data->sbase + WBCIR_REG_SP3_RCCFG, + enable ? WBCIR_RX_T_OV : 0, WBCIR_RX_T_OV); - pulse = irdata[i] & 0x80 ? false : true; - duration = (irdata[i] & 0x7F) * 10000; /* ns */ + data->carrier_report_enabled = enable; + spin_unlock_irqrestore(&data->spinlock, flags); - if (data->ev.pulse != pulse) { - if (data->ev.duration != 0) { - ir_raw_event_store(data->dev, &data->ev); - data->ev.duration = 0; - } + return 0; +} - data->ev.pulse = pulse; - } +static int +wbcir_txcarrier(struct rc_dev *dev, u32 carrier) +{ + struct wbcir_data *data = dev->priv; + unsigned long flags; + u8 val; + u32 freq; + + freq = DIV_ROUND_CLOSEST(carrier, 1000); + if (freq < 30 || freq > 60) + return -EINVAL; + + switch (freq) { + case 58: + case 59: + case 60: + val = freq - 58; + freq *= 1000; + break; + case 57: + val = freq - 27; + freq = 56900; + break; + default: + val = freq - 27; + freq *= 1000; + break; + } - data->ev.duration += duration; + spin_lock_irqsave(&data->spinlock, flags); + if (data->txstate != WBCIR_TXSTATE_INACTIVE) { + spin_unlock_irqrestore(&data->spinlock, flags); + return -EBUSY; } - if (disable) { - if (data->ev.duration != 0 && !data->irdata_error) { - ir_raw_event_store(data->dev, &data->ev); - data->ev.duration = 0; - } + if (data->txcarrier != freq) { + wbcir_select_bank(data, WBCIR_BANK_7); + wbcir_set_bits(data->sbase + WBCIR_REG_SP3_IRTXMC, val, 0x1F); + data->txcarrier = freq; + } - /* Set RXINACTIVE */ - outb(WBCIR_RX_DISABLE, data->sbase + WBCIR_REG_SP3_ASCR); + spin_unlock_irqrestore(&data->spinlock, flags); + return 0; +} - /* Drain the FIFO */ - while (inb(data->sbase + WBCIR_REG_SP3_LSR) & WBCIR_RX_AVAIL) - inb(data->sbase + WBCIR_REG_SP3_RXDATA); +static int +wbcir_txmask(struct rc_dev *dev, u32 mask) +{ + struct wbcir_data *data = dev->priv; + unsigned long flags; + u8 val; - ir_raw_event_reset(data->dev); - data->irdata_error = false; - data->irdata_active = false; - led_trigger_event(data->rxtrigger, LED_OFF); + /* Four outputs, only one output can be enabled at a time */ + switch (mask) { + case 0x1: + val = 0x0; + break; + case 0x2: + val = 0x1; + break; + case 0x4: + val = 0x2; + break; + case 0x8: + val = 0x3; + break; + default: + return -EINVAL; } - ir_raw_event_handle(data->dev); + spin_lock_irqsave(&data->spinlock, flags); + if (data->txstate != WBCIR_TXSTATE_INACTIVE) { + spin_unlock_irqrestore(&data->spinlock, flags); + return -EBUSY; + } + + if (data->txmask != mask) { + wbcir_set_bits(data->ebase + WBCIR_REG_ECEIR_CTS, val, 0x0c); + data->txmask = mask; + } -out: spin_unlock_irqrestore(&data->spinlock, flags); - return IRQ_HANDLED; + return 0; } +static int +wbcir_tx(struct rc_dev *dev, unsigned *b, unsigned count) +{ + struct wbcir_data *data = dev->priv; + unsigned *buf; + unsigned i; + unsigned long flags; + + buf = kmalloc(count * sizeof(*b), GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* Convert values to multiples of 10us */ + for (i = 0; i < count; i++) + buf[i] = DIV_ROUND_CLOSEST(b[i], 10); + /* Not sure if this is possible, but better safe than sorry */ + spin_lock_irqsave(&data->spinlock, flags); + if (data->txstate != WBCIR_TXSTATE_INACTIVE) { + spin_unlock_irqrestore(&data->spinlock, flags); + kfree(buf); + return -EBUSY; + } + + /* Fill the TX fifo once, the irq handler will do the rest */ + data->txbuf = buf; + data->txlen = count; + data->txoff = 0; + wbcir_irq_tx(data); + + /* We're done */ + spin_unlock_irqrestore(&data->spinlock, flags); + return count; +} /***************************************************************************** * @@ -382,7 +694,7 @@ wbcir_shutdown(struct pnp_dev *device) { struct device *dev = &device->dev; struct wbcir_data *data = pnp_get_drvdata(device); - int do_wake = 1; + bool do_wake = true; u8 match[11]; u8 mask[11]; u8 rc6_csl = 0; @@ -392,14 +704,14 @@ wbcir_shutdown(struct pnp_dev *device) memset(mask, 0, sizeof(mask)); if (wake_sc == INVALID_SCANCODE || !device_may_wakeup(dev)) { - do_wake = 0; + do_wake = false; goto finish; } switch (protocol) { case IR_PROTOCOL_RC5: if (wake_sc > 0xFFF) { - do_wake = 0; + do_wake = false; dev_err(dev, "RC5 - Invalid wake scancode\n"); break; } @@ -418,7 +730,7 @@ wbcir_shutdown(struct pnp_dev *device) case IR_PROTOCOL_NEC: if (wake_sc > 0xFFFFFF) { - do_wake = 0; + do_wake = false; dev_err(dev, "NEC - Invalid wake scancode\n"); break; } @@ -440,7 +752,7 @@ wbcir_shutdown(struct pnp_dev *device) if (wake_rc6mode == 0) { if (wake_sc > 0xFFFF) { - do_wake = 0; + do_wake = false; dev_err(dev, "RC6 - Invalid wake scancode\n"); break; } @@ -496,7 +808,7 @@ wbcir_shutdown(struct pnp_dev *device) } else if (wake_sc <= 0x007FFFFF) { rc6_csl = 60; } else { - do_wake = 0; + do_wake = false; dev_err(dev, "RC6 - Invalid wake scancode\n"); break; } @@ -508,14 +820,14 @@ wbcir_shutdown(struct pnp_dev *device) mask[i++] = 0x0F; } else { - do_wake = 0; + do_wake = false; dev_err(dev, "RC6 - Invalid wake mode\n"); } break; default: - do_wake = 0; + do_wake = false; break; } @@ -551,26 +863,21 @@ finish: wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x00, 0x01); } - /* Disable interrupts */ - wbcir_select_bank(data, WBCIR_BANK_0); - outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER); - - /* Disable LED */ - data->irdata_active = false; - led_trigger_event(data->rxtrigger, LED_OFF); - /* * ACPI will set the HW disable bit for SP3 which means that the * output signals are left in an undefined state which may cause * spurious interrupts which we need to ignore until the hardware * is reinitialized. */ + wbcir_set_irqmask(data, WBCIR_IRQ_NONE); disable_irq(data->irq); } static int wbcir_suspend(struct pnp_dev *device, pm_message_t state) { + struct wbcir_data *data = pnp_get_drvdata(device); + led_classdev_suspend(&data->led); wbcir_shutdown(device); return 0; } @@ -581,8 +888,7 @@ wbcir_init_hw(struct wbcir_data *data) u8 tmp; /* Disable interrupts */ - wbcir_select_bank(data, WBCIR_BANK_0); - outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER); + wbcir_set_irqmask(data, WBCIR_IRQ_NONE); /* Set PROT_SEL, RX_INV, Clear CEIR_EN (needed for the led) */ tmp = protocol << 4; @@ -601,15 +907,16 @@ wbcir_init_hw(struct wbcir_data *data) /* Set IRTX_INV */ if (invert) - outb(0x04, data->ebase + WBCIR_REG_ECEIR_CCTL); + outb(WBCIR_IRTX_INV, data->ebase + WBCIR_REG_ECEIR_CCTL); else outb(0x00, data->ebase + WBCIR_REG_ECEIR_CCTL); /* - * Clear IR LED, set SP3 clock to 24Mhz + * Clear IR LED, set SP3 clock to 24Mhz, set TX mask to IRTX1, * set SP3_IRRX_SW to binary 01, helpfully not documented */ outb(0x10, data->ebase + WBCIR_REG_ECEIR_CTS); + data->txmask = 0x1; /* Enable extended mode */ wbcir_select_bank(data, WBCIR_BANK_2); @@ -629,18 +936,8 @@ wbcir_init_hw(struct wbcir_data *data) /* prescaler 1.0, tx/rx fifo lvl 16 */ outb(0x30, data->sbase + WBCIR_REG_SP3_EXCR2); - /* Set baud divisor to generate one byte per bit/cell */ - switch (protocol) { - case IR_PROTOCOL_RC5: - outb(0xA7, data->sbase + WBCIR_REG_SP3_BGDL); - break; - case IR_PROTOCOL_RC6: - outb(0x53, data->sbase + WBCIR_REG_SP3_BGDL); - break; - case IR_PROTOCOL_NEC: - outb(0x69, data->sbase + WBCIR_REG_SP3_BGDL); - break; - } + /* Set baud divisor to sample every 10 us */ + outb(0x0f, data->sbase + WBCIR_REG_SP3_BGDL); outb(0x00, data->sbase + WBCIR_REG_SP3_BGDH); /* Set CEIR mode */ @@ -649,26 +946,29 @@ wbcir_init_hw(struct wbcir_data *data) inb(data->sbase + WBCIR_REG_SP3_LSR); /* Clear LSR */ inb(data->sbase + WBCIR_REG_SP3_MSR); /* Clear MSR */ - /* Disable RX demod, run-length encoding/decoding, set freq span */ + /* Disable RX demod, enable run-length enc/dec, set freq span */ wbcir_select_bank(data, WBCIR_BANK_7); - outb(0x10, data->sbase + WBCIR_REG_SP3_RCCFG); + outb(0x90, data->sbase + WBCIR_REG_SP3_RCCFG); /* Disable timer */ wbcir_select_bank(data, WBCIR_BANK_4); outb(0x00, data->sbase + WBCIR_REG_SP3_IRCR1); - /* Enable MSR interrupt, Clear AUX_IRX */ + /* Disable MSR interrupt, clear AUX_IRX, mask RX during TX? */ wbcir_select_bank(data, WBCIR_BANK_5); - outb(0x00, data->sbase + WBCIR_REG_SP3_IRCR2); + outb(txandrx ? 0x03 : 0x02, data->sbase + WBCIR_REG_SP3_IRCR2); /* Disable CRC */ wbcir_select_bank(data, WBCIR_BANK_6); outb(0x20, data->sbase + WBCIR_REG_SP3_IRCR3); - /* Set RX/TX (de)modulation freq, not really used */ + /* Set RX demodulation freq, not really used */ wbcir_select_bank(data, WBCIR_BANK_7); outb(0xF2, data->sbase + WBCIR_REG_SP3_IRRXDC); + + /* Set TX modulation, 36kHz, 7us pulse width */ outb(0x69, data->sbase + WBCIR_REG_SP3_IRTXMC); + data->txcarrier = 36000; /* Set invert and pin direction */ if (invert) @@ -683,16 +983,20 @@ wbcir_init_hw(struct wbcir_data *data) /* Clear AUX status bits */ outb(0xE0, data->sbase + WBCIR_REG_SP3_ASCR); - /* Clear IR decoding state */ - data->irdata_active = false; - led_trigger_event(data->rxtrigger, LED_OFF); - data->irdata_error = false; - data->ev.duration = 0; + /* Clear RX state */ + data->rxstate = WBCIR_RXSTATE_INACTIVE; ir_raw_event_reset(data->dev); - ir_raw_event_handle(data->dev); + ir_raw_event_set_idle(data->dev, true); + + /* Clear TX state */ + if (data->txstate == WBCIR_TXSTATE_ACTIVE) { + kfree(data->txbuf); + data->txbuf = NULL; + data->txstate = WBCIR_TXSTATE_INACTIVE; + } /* Enable interrupts */ - outb(WBCIR_IRQ_RX | WBCIR_IRQ_ERR, data->sbase + WBCIR_REG_SP3_IER); + wbcir_set_irqmask(data, WBCIR_IRQ_RX | WBCIR_IRQ_ERR); } static int @@ -702,11 +1006,12 @@ wbcir_resume(struct pnp_dev *device) wbcir_init_hw(data); enable_irq(data->irq); + led_classdev_resume(&data->led); return 0; } -static int __devinit +static int wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id) { struct device *dev = &device->dev; @@ -745,11 +1050,49 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id) "(w: 0x%lX, e: 0x%lX, s: 0x%lX, i: %u)\n", data->wbase, data->ebase, data->sbase, data->irq); + data->led.name = "cir::activity"; + data->led.default_trigger = "rc-feedback"; + data->led.brightness_set = wbcir_led_brightness_set; + data->led.brightness_get = wbcir_led_brightness_get; + err = led_classdev_register(&device->dev, &data->led); + if (err) + goto exit_free_data; + + data->dev = rc_allocate_device(); + if (!data->dev) { + err = -ENOMEM; + goto exit_unregister_led; + } + + data->dev->driver_type = RC_DRIVER_IR_RAW; + data->dev->driver_name = DRVNAME; + data->dev->input_name = WBCIR_NAME; + data->dev->input_phys = "wbcir/cir0"; + data->dev->input_id.bustype = BUS_HOST; + data->dev->input_id.vendor = PCI_VENDOR_ID_WINBOND; + data->dev->input_id.product = WBCIR_ID_FAMILY; + data->dev->input_id.version = WBCIR_ID_CHIP; + data->dev->map_name = RC_MAP_RC6_MCE; + data->dev->s_idle = wbcir_idle_rx; + data->dev->s_carrier_report = wbcir_set_carrier_report; + data->dev->s_tx_mask = wbcir_txmask; + data->dev->s_tx_carrier = wbcir_txcarrier; + data->dev->tx_ir = wbcir_tx; + data->dev->priv = data; + data->dev->dev.parent = &device->dev; + data->dev->timeout = MS_TO_NS(100); + data->dev->rx_resolution = US_TO_NS(2); + rc_set_allowed_protocols(data->dev, RC_BIT_ALL); + + err = rc_register_device(data->dev); + if (err) + goto exit_free_rc; + if (!request_region(data->wbase, WAKEUP_IOMEM_LEN, DRVNAME)) { dev_err(dev, "Region 0x%lx-0x%lx already in use!\n", data->wbase, data->wbase + WAKEUP_IOMEM_LEN - 1); err = -EBUSY; - goto exit_free_data; + goto exit_unregister_device; } if (!request_region(data->ebase, EHFUNC_IOMEM_LEN, DRVNAME)) { @@ -767,75 +1110,32 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id) } err = request_irq(data->irq, wbcir_irq_handler, - IRQF_DISABLED, DRVNAME, device); + 0, DRVNAME, device); if (err) { dev_err(dev, "Failed to claim IRQ %u\n", data->irq); err = -EBUSY; goto exit_release_sbase; } - led_trigger_register_simple("cir-tx", &data->txtrigger); - if (!data->txtrigger) { - err = -ENOMEM; - goto exit_free_irq; - } - - led_trigger_register_simple("cir-rx", &data->rxtrigger); - if (!data->rxtrigger) { - err = -ENOMEM; - goto exit_unregister_txtrigger; - } - - data->led.name = "cir::activity"; - data->led.default_trigger = "cir-rx"; - data->led.brightness_set = wbcir_led_brightness_set; - data->led.brightness_get = wbcir_led_brightness_get; - err = led_classdev_register(&device->dev, &data->led); - if (err) - goto exit_unregister_rxtrigger; - - data->dev = rc_allocate_device(); - if (!data->dev) { - err = -ENOMEM; - goto exit_unregister_led; - } - - data->dev->driver_name = WBCIR_NAME; - data->dev->input_name = WBCIR_NAME; - data->dev->input_phys = "wbcir/cir0"; - data->dev->input_id.bustype = BUS_HOST; - data->dev->input_id.vendor = PCI_VENDOR_ID_WINBOND; - data->dev->input_id.product = WBCIR_ID_FAMILY; - data->dev->input_id.version = WBCIR_ID_CHIP; - data->dev->priv = data; - data->dev->dev.parent = &device->dev; - - err = rc_register_device(data->dev); - if (err) - goto exit_free_rc; - device_init_wakeup(&device->dev, 1); wbcir_init_hw(data); return 0; -exit_free_rc: - rc_free_device(data->dev); -exit_unregister_led: - led_classdev_unregister(&data->led); -exit_unregister_rxtrigger: - led_trigger_unregister_simple(data->rxtrigger); -exit_unregister_txtrigger: - led_trigger_unregister_simple(data->txtrigger); -exit_free_irq: - free_irq(data->irq, device); exit_release_sbase: release_region(data->sbase, SP_IOMEM_LEN); exit_release_ebase: release_region(data->ebase, EHFUNC_IOMEM_LEN); exit_release_wbase: release_region(data->wbase, WAKEUP_IOMEM_LEN); +exit_unregister_device: + rc_unregister_device(data->dev); + data->dev = NULL; +exit_free_rc: + rc_free_device(data->dev); +exit_unregister_led: + led_classdev_unregister(&data->led); exit_free_data: kfree(data); pnp_set_drvdata(device, NULL); @@ -843,15 +1143,13 @@ exit: return err; } -static void __devexit +static void wbcir_remove(struct pnp_dev *device) { struct wbcir_data *data = pnp_get_drvdata(device); /* Disable interrupts */ - wbcir_select_bank(data, WBCIR_BANK_0); - outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER); - + wbcir_set_irqmask(data, WBCIR_IRQ_NONE); free_irq(data->irq, device); /* Clear status bits NEC_REP, BUFF, MSG_END, MATCH */ @@ -865,8 +1163,6 @@ wbcir_remove(struct pnp_dev *device) rc_unregister_device(data->dev); - led_trigger_unregister_simple(data->rxtrigger); - led_trigger_unregister_simple(data->txtrigger); led_classdev_unregister(&data->led); /* This is ok since &data->led isn't actually used */ @@ -891,7 +1187,7 @@ static struct pnp_driver wbcir_driver = { .name = WBCIR_NAME, .id_table = wbcir_ids, .probe = wbcir_probe, - .remove = __devexit_p(wbcir_remove), + .remove = wbcir_remove, .suspend = wbcir_suspend, .resume = wbcir_resume, .shutdown = wbcir_shutdown @@ -908,12 +1204,12 @@ wbcir_init(void) case IR_PROTOCOL_RC6: break; default: - printk(KERN_ERR DRVNAME ": Invalid power-on protocol\n"); + pr_err("Invalid power-on protocol\n"); } ret = pnp_register_driver(&wbcir_driver); if (ret) - printk(KERN_ERR DRVNAME ": Unable to register driver\n"); + pr_err("Unable to register driver\n"); return ret; } @@ -927,6 +1223,6 @@ wbcir_exit(void) module_init(wbcir_init); module_exit(wbcir_exit); -MODULE_AUTHOR("David Härdeman <david@hardeman.nu>"); +MODULE_AUTHOR("David Härdeman <david@hardeman.nu>"); MODULE_DESCRIPTION("Winbond SuperI/O Consumer IR Driver"); MODULE_LICENSE("GPL"); |
