提交 c829f267 编写于 作者: D David Härdeman 提交者: Mauro Carvalho Chehab

[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: NDavid Härdeman <david@hardeman.nu>
Signed-off-by: NMauro Carvalho Chehab <mchehab@redhat.com>
上级 67cdd42e
...@@ -19,11 +19,12 @@ ...@@ -19,11 +19,12 @@
* o DSDT dumps * o DSDT dumps
* *
* Supported features: * Supported features:
* o IR Receive
* o IR Transmit
* o Wake-On-CIR functionality * o Wake-On-CIR functionality
* *
* To do: * To do:
* o Learning * o Learning
* o IR Transmit
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
...@@ -50,6 +51,8 @@ ...@@ -50,6 +51,8 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/bitrev.h> #include <linux/bitrev.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <media/rc-core.h> #include <media/rc-core.h>
#define DRVNAME "winbond-cir" #define DRVNAME "winbond-cir"
...@@ -118,14 +121,24 @@ ...@@ -118,14 +121,24 @@
#define WBCIR_IRQ_NONE 0x00 #define WBCIR_IRQ_NONE 0x00
/* RX data bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */ /* RX data bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */
#define WBCIR_IRQ_RX 0x01 #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 */ /* Over/Under-flow bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */
#define WBCIR_IRQ_ERR 0x04 #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 */ /* Led enable/disable bit for WBCIR_REG_ECEIR_CTS */
#define WBCIR_LED_ENABLE 0x80 #define WBCIR_LED_ENABLE 0x80
/* RX data available bit for WBCIR_REG_SP3_LSR */ /* RX data available bit for WBCIR_REG_SP3_LSR */
#define WBCIR_RX_AVAIL 0x01 #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 */ /* RX disable bit for WBCIR_REG_SP3_ASCR */
#define WBCIR_RX_DISABLE 0x20 #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 */ /* Extended mode enable bit for WBCIR_REG_SP3_EXCR1 */
#define WBCIR_EXT_ENABLE 0x01 #define WBCIR_EXT_ENABLE 0x01
/* Select compare register in WBCIR_REG_WCEIR_INDEX (bits 5 & 6) */ /* Select compare register in WBCIR_REG_WCEIR_INDEX (bits 5 & 6) */
...@@ -154,6 +167,21 @@ enum wbcir_protocol { ...@@ -154,6 +167,21 @@ enum wbcir_protocol {
IR_PROTOCOL_RC6 = 0x2, 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_DONE,
WBCIR_TXSTATE_ERROR
};
/* Misc */ /* Misc */
#define WBCIR_NAME "Winbond CIR" #define WBCIR_NAME "Winbond CIR"
#define WBCIR_ID_FAMILY 0xF1 /* Family ID for the WPCD376I */ #define WBCIR_ID_FAMILY 0xF1 /* Family ID for the WPCD376I */
...@@ -166,22 +194,29 @@ enum wbcir_protocol { ...@@ -166,22 +194,29 @@ enum wbcir_protocol {
/* Per-device data */ /* Per-device data */
struct wbcir_data { struct wbcir_data {
spinlock_t spinlock; spinlock_t spinlock;
struct rc_dev *dev;
struct led_classdev led;
unsigned long wbase; /* Wake-Up Baseaddr */ unsigned long wbase; /* Wake-Up Baseaddr */
unsigned long ebase; /* Enhanced Func. Baseaddr */ unsigned long ebase; /* Enhanced Func. Baseaddr */
unsigned long sbase; /* Serial Port Baseaddr */ unsigned long sbase; /* Serial Port Baseaddr */
unsigned int irq; /* Serial Port IRQ */ unsigned int irq; /* Serial Port IRQ */
u8 irqmask;
struct rc_dev *dev; /* RX state */
enum wbcir_rxstate rxstate;
struct led_trigger *rxtrigger; struct led_trigger *rxtrigger;
struct led_trigger *txtrigger; struct ir_raw_event rxev;
struct led_classdev led;
/* RX irdata state */ /* TX state */
bool irdata_active; enum wbcir_txstate txstate;
bool irdata_error; struct led_trigger *txtrigger;
struct ir_raw_event ev; u32 txlen;
u32 txoff;
u32 *txbuf;
wait_queue_head_t txwaitq;
u8 txmask;
u32 txcarrier;
}; };
static enum wbcir_protocol protocol = IR_PROTOCOL_RC6; static enum wbcir_protocol protocol = IR_PROTOCOL_RC6;
...@@ -193,6 +228,10 @@ static int invert; /* default = 0 */ ...@@ -193,6 +228,10 @@ static int invert; /* default = 0 */
module_param(invert, bool, 0444); module_param(invert, bool, 0444);
MODULE_PARM_DESC(invert, "Invert the signal from the IR receiver"); MODULE_PARM_DESC(invert, "Invert the signal from the IR receiver");
static int txandrx; /* default = 0 */
module_param(txandrx, bool, 0444);
MODULE_PARM_DESC(invert, "Allow simultaneous TX and RX");
static unsigned int wake_sc = 0x800F040C; static unsigned int wake_sc = 0x800F040C;
module_param(wake_sc, uint, 0644); module_param(wake_sc, uint, 0644);
MODULE_PARM_DESC(wake_sc, "Scancode of the power-on IR command"); 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,6 +267,17 @@ wbcir_select_bank(struct wbcir_data *data, enum wbcir_bank bank)
outb(bank, data->sbase + WBCIR_REG_SP3_BSR); 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 static enum led_brightness
wbcir_led_brightness_get(struct led_classdev *led_cdev) wbcir_led_brightness_get(struct led_classdev *led_cdev)
{ {
...@@ -279,39 +329,15 @@ wbcir_to_rc6cells(u8 val) ...@@ -279,39 +329,15 @@ wbcir_to_rc6cells(u8 val)
* *
*****************************************************************************/ *****************************************************************************/
static irqreturn_t static void
wbcir_irq_handler(int irqno, void *cookie) wbcir_irq_rx(struct wbcir_data *data, struct pnp_dev *device)
{ {
struct pnp_dev *device = cookie;
struct wbcir_data *data = pnp_get_drvdata(device);
unsigned long flags;
u8 irdata[8]; u8 irdata[8];
u8 disable = true; bool disable = true;
u8 status; unsigned int i;
int i;
spin_lock_irqsave(&data->spinlock, flags);
wbcir_select_bank(data, WBCIR_BANK_0);
status = inb(data->sbase + WBCIR_REG_SP3_EIR);
if (!(status & (WBCIR_IRQ_RX | WBCIR_IRQ_ERR))) {
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);
}
if (!(status & WBCIR_IRQ_RX))
goto out;
if (!data->irdata_active) { if (data->rxstate == WBCIR_RXSTATE_INACTIVE) {
data->irdata_active = true; data->rxstate = WBCIR_RXSTATE_ACTIVE;
led_trigger_event(data->rxtrigger, LED_FULL); led_trigger_event(data->rxtrigger, LED_FULL);
} }
...@@ -325,28 +351,29 @@ wbcir_irq_handler(int irqno, void *cookie) ...@@ -325,28 +351,29 @@ wbcir_irq_handler(int irqno, void *cookie)
if (irdata[i] != 0xFF && irdata[i] != 0x00) if (irdata[i] != 0xFF && irdata[i] != 0x00)
disable = false; disable = false;
if (data->irdata_error) if (data->rxstate == WBCIR_RXSTATE_ERROR)
continue; continue;
pulse = irdata[i] & 0x80 ? false : true; pulse = irdata[i] & 0x80 ? false : true;
duration = (irdata[i] & 0x7F) * 10000; /* ns */ duration = (irdata[i] & 0x7F) * 10000; /* ns */
if (data->ev.pulse != pulse) { if (data->rxev.pulse != pulse) {
if (data->ev.duration != 0) { if (data->rxev.duration != 0) {
ir_raw_event_store(data->dev, &data->ev); ir_raw_event_store(data->dev, &data->rxev);
data->ev.duration = 0; data->rxev.duration = 0;
} }
data->ev.pulse = pulse; data->rxev.pulse = pulse;
} }
data->ev.duration += duration; data->rxev.duration += duration;
} }
if (disable) { if (disable) {
if (data->ev.duration != 0 && !data->irdata_error) { if (data->rxev.duration != 0 &&
ir_raw_event_store(data->dev, &data->ev); data->rxstate != WBCIR_RXSTATE_ERROR) {
data->ev.duration = 0; ir_raw_event_store(data->dev, &data->rxev);
data->rxev.duration = 0;
} }
/* Set RXINACTIVE */ /* Set RXINACTIVE */
...@@ -357,19 +384,264 @@ wbcir_irq_handler(int irqno, void *cookie) ...@@ -357,19 +384,264 @@ wbcir_irq_handler(int irqno, void *cookie)
inb(data->sbase + WBCIR_REG_SP3_RXDATA); inb(data->sbase + WBCIR_REG_SP3_RXDATA);
ir_raw_event_reset(data->dev); ir_raw_event_reset(data->dev);
data->irdata_error = false;
data->irdata_active = false;
led_trigger_event(data->rxtrigger, LED_OFF); led_trigger_event(data->rxtrigger, LED_OFF);
data->rxstate = WBCIR_RXSTATE_INACTIVE;
} }
ir_raw_event_handle(data->dev); 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;
led_trigger_event(data->txtrigger, LED_FULL);
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);
else
data->txstate = WBCIR_TXSTATE_DONE;
wbcir_set_irqmask(data, WBCIR_IRQ_RX | WBCIR_IRQ_ERR);
led_trigger_event(data->txtrigger, LED_OFF);
wake_up(&data->txwaitq);
} 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 status;
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) {
spin_unlock_irqrestore(&data->spinlock, flags);
return IRQ_NONE;
}
if (status & WBCIR_IRQ_ERR) {
/* 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)
wbcir_irq_rx(data, device);
if (status & (WBCIR_IRQ_TX_LOW | WBCIR_IRQ_TX_EMPTY))
wbcir_irq_tx(data);
out:
spin_unlock_irqrestore(&data->spinlock, flags); spin_unlock_irqrestore(&data->spinlock, flags);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
/*****************************************************************************
*
* RC-CORE INTERFACE FUNCTIONS
*
*****************************************************************************/
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;
}
spin_lock_irqsave(&data->spinlock, flags);
if (data->txstate != WBCIR_TXSTATE_INACTIVE) {
spin_unlock_irqrestore(&data->spinlock, flags);
return -EBUSY;
}
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;
}
spin_unlock_irqrestore(&data->spinlock, flags);
return 0;
}
static int
wbcir_txmask(struct rc_dev *dev, u32 mask)
{
struct wbcir_data *data = dev->priv;
unsigned long flags;
u8 val;
/* 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;
}
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;
}
spin_unlock_irqrestore(&data->spinlock, flags);
return 0;
}
static int
wbcir_tx(struct rc_dev *dev, int *buf, u32 bufsize)
{
struct wbcir_data *data = dev->priv;
u32 count;
unsigned i;
unsigned long flags;
/* bufsize has been sanity checked by the caller */
count = bufsize / sizeof(int);
/* 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);
return -EBUSY;
}
/* Convert values to multiples of 10us */
for (i = 0; i < count; i++)
buf[i] = DIV_ROUND_CLOSEST(buf[i], 10);
/* 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);
/* Wait for the TX to complete */
while (data->txstate == WBCIR_TXSTATE_ACTIVE) {
spin_unlock_irqrestore(&data->spinlock, flags);
wait_event(data->txwaitq, data->txstate != WBCIR_TXSTATE_ACTIVE);
spin_lock_irqsave(&data->spinlock, flags);
}
/* We're done */
if (data->txstate == WBCIR_TXSTATE_ERROR)
count = -EAGAIN;
data->txstate = WBCIR_TXSTATE_INACTIVE;
data->txbuf = NULL;
spin_unlock_irqrestore(&data->spinlock, flags);
return count;
}
/***************************************************************************** /*****************************************************************************
* *
...@@ -551,21 +823,18 @@ wbcir_shutdown(struct pnp_dev *device) ...@@ -551,21 +823,18 @@ wbcir_shutdown(struct pnp_dev *device)
wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x00, 0x01); 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 * ACPI will set the HW disable bit for SP3 which means that the
* output signals are left in an undefined state which may cause * output signals are left in an undefined state which may cause
* spurious interrupts which we need to ignore until the hardware * spurious interrupts which we need to ignore until the hardware
* is reinitialized. * is reinitialized.
*/ */
wbcir_set_irqmask(data, WBCIR_IRQ_NONE);
disable_irq(data->irq); disable_irq(data->irq);
/* Disable LED */
led_trigger_event(data->rxtrigger, LED_OFF);
led_trigger_event(data->txtrigger, LED_OFF);
} }
static int static int
...@@ -581,8 +850,7 @@ wbcir_init_hw(struct wbcir_data *data) ...@@ -581,8 +850,7 @@ wbcir_init_hw(struct wbcir_data *data)
u8 tmp; u8 tmp;
/* Disable interrupts */ /* Disable interrupts */
wbcir_select_bank(data, WBCIR_BANK_0); wbcir_set_irqmask(data, WBCIR_IRQ_NONE);
outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER);
/* Set PROT_SEL, RX_INV, Clear CEIR_EN (needed for the led) */ /* Set PROT_SEL, RX_INV, Clear CEIR_EN (needed for the led) */
tmp = protocol << 4; tmp = protocol << 4;
...@@ -606,10 +874,11 @@ wbcir_init_hw(struct wbcir_data *data) ...@@ -606,10 +874,11 @@ wbcir_init_hw(struct wbcir_data *data)
outb(0x00, data->ebase + WBCIR_REG_ECEIR_CCTL); 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 * set SP3_IRRX_SW to binary 01, helpfully not documented
*/ */
outb(0x10, data->ebase + WBCIR_REG_ECEIR_CTS); outb(0x10, data->ebase + WBCIR_REG_ECEIR_CTS);
data->txmask = 0x1;
/* Enable extended mode */ /* Enable extended mode */
wbcir_select_bank(data, WBCIR_BANK_2); wbcir_select_bank(data, WBCIR_BANK_2);
...@@ -657,18 +926,21 @@ wbcir_init_hw(struct wbcir_data *data) ...@@ -657,18 +926,21 @@ wbcir_init_hw(struct wbcir_data *data)
wbcir_select_bank(data, WBCIR_BANK_4); wbcir_select_bank(data, WBCIR_BANK_4);
outb(0x00, data->sbase + WBCIR_REG_SP3_IRCR1); 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); 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 */ /* Disable CRC */
wbcir_select_bank(data, WBCIR_BANK_6); wbcir_select_bank(data, WBCIR_BANK_6);
outb(0x20, data->sbase + WBCIR_REG_SP3_IRCR3); 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); wbcir_select_bank(data, WBCIR_BANK_7);
outb(0xF2, data->sbase + WBCIR_REG_SP3_IRRXDC); outb(0xF2, data->sbase + WBCIR_REG_SP3_IRRXDC);
/* Set TX modulation, 36kHz, 7us pulse width */
outb(0x69, data->sbase + WBCIR_REG_SP3_IRTXMC); outb(0x69, data->sbase + WBCIR_REG_SP3_IRTXMC);
data->txcarrier = 36000;
/* Set invert and pin direction */ /* Set invert and pin direction */
if (invert) if (invert)
...@@ -683,16 +955,23 @@ wbcir_init_hw(struct wbcir_data *data) ...@@ -683,16 +955,23 @@ wbcir_init_hw(struct wbcir_data *data)
/* Clear AUX status bits */ /* Clear AUX status bits */
outb(0xE0, data->sbase + WBCIR_REG_SP3_ASCR); outb(0xE0, data->sbase + WBCIR_REG_SP3_ASCR);
/* Clear IR decoding state */ /* Clear RX state */
data->irdata_active = false; data->rxstate = WBCIR_RXSTATE_INACTIVE;
led_trigger_event(data->rxtrigger, LED_OFF); data->rxev.duration = 0;
data->irdata_error = false;
data->ev.duration = 0;
ir_raw_event_reset(data->dev); ir_raw_event_reset(data->dev);
ir_raw_event_handle(data->dev); ir_raw_event_handle(data->dev);
/*
* Check TX state, if we did a suspend/resume cycle while TX was
* active, we will have a process waiting in txwaitq.
*/
if (data->txstate == WBCIR_TXSTATE_ACTIVE) {
data->txstate = WBCIR_TXSTATE_ERROR;
wake_up(&data->txwaitq);
}
/* Enable interrupts */ /* 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 static int
...@@ -729,6 +1008,7 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id) ...@@ -729,6 +1008,7 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id)
pnp_set_drvdata(device, data); pnp_set_drvdata(device, data);
spin_lock_init(&data->spinlock); spin_lock_init(&data->spinlock);
init_waitqueue_head(&data->txwaitq);
data->ebase = pnp_port_start(device, 0); data->ebase = pnp_port_start(device, 0);
data->wbase = pnp_port_start(device, 1); data->wbase = pnp_port_start(device, 1);
data->sbase = pnp_port_start(device, 2); 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,6 +1087,10 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id)
data->dev->input_id.vendor = PCI_VENDOR_ID_WINBOND; data->dev->input_id.vendor = PCI_VENDOR_ID_WINBOND;
data->dev->input_id.product = WBCIR_ID_FAMILY; data->dev->input_id.product = WBCIR_ID_FAMILY;
data->dev->input_id.version = WBCIR_ID_CHIP; data->dev->input_id.version = WBCIR_ID_CHIP;
data->dev->map_name = RC_MAP_RC6_MCE;
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->priv = data;
data->dev->dev.parent = &device->dev; data->dev->dev.parent = &device->dev;
...@@ -849,9 +1133,7 @@ wbcir_remove(struct pnp_dev *device) ...@@ -849,9 +1133,7 @@ wbcir_remove(struct pnp_dev *device)
struct wbcir_data *data = pnp_get_drvdata(device); struct wbcir_data *data = pnp_get_drvdata(device);
/* Disable interrupts */ /* Disable interrupts */
wbcir_select_bank(data, WBCIR_BANK_0); wbcir_set_irqmask(data, WBCIR_IRQ_NONE);
outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER);
free_irq(data->irq, device); free_irq(data->irq, device);
/* Clear status bits NEC_REP, BUFF, MSG_END, MATCH */ /* Clear status bits NEC_REP, BUFF, MSG_END, MATCH */
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册