* [PATCH 01/17] cx23885: Return -ENXIO on slave nack
[not found] <cover.1279586511.git.awalls@md.metrocast.net>
@ 2010-07-20 1:08 ` Andy Walls
2010-07-20 1:09 ` [PATCH 02/17] cx23885: Check for slave nack on all transactions Andy Walls
` (15 subsequent siblings)
16 siblings, 0 replies; 20+ messages in thread
From: Andy Walls @ 2010-07-20 1:08 UTC (permalink / raw)
To: linux-media; +Cc: Kenney Phillisjr, Steven Toth, Jean Delvare
From: Jean Delvare <khali@linux-fr.org>
Documentation/i2c/fault-codes says that i2c adapter drivers should
return -ENXIO when no slave acks an address byte.
Signed-off-by: Jean Delvare <khali@linux-fr.org>
Signed-off-by: Andy Walls <awalls@md.metrocast.net>
---
drivers/media/video/cx23885/cx23885-i2c.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/media/video/cx23885/cx23885-i2c.c b/drivers/media/video/cx23885/cx23885-i2c.c
index d4746e0..154c914 100644
--- a/drivers/media/video/cx23885/cx23885-i2c.c
+++ b/drivers/media/video/cx23885/cx23885-i2c.c
@@ -99,7 +99,7 @@ static int i2c_sendbytes(struct i2c_adapter *i2c_adap,
if (!i2c_wait_done(i2c_adap))
return -EIO;
if (!i2c_slave_did_ack(i2c_adap))
- return -EIO;
+ return -ENXIO;
dprintk(1, "%s() returns 0\n", __func__);
return 0;
@@ -185,7 +185,7 @@ static int i2c_readbytes(struct i2c_adapter *i2c_adap,
if (!i2c_wait_done(i2c_adap))
return -EIO;
if (!i2c_slave_did_ack(i2c_adap))
- return -EIO;
+ return -ENXIO;
dprintk(1, "%s() returns 0\n", __func__);
--
1.7.1.1
^ permalink raw reply related [flat|nested] 20+ messages in thread* [PATCH 02/17] cx23885: Check for slave nack on all transactions
[not found] <cover.1279586511.git.awalls@md.metrocast.net>
2010-07-20 1:08 ` [PATCH 01/17] cx23885: Return -ENXIO on slave nack Andy Walls
@ 2010-07-20 1:09 ` Andy Walls
2010-07-20 1:10 ` [PATCH 03/17] cx23885: i2c_wait_done returns 0 or 1, don't check for < 0 return value Andy Walls
` (14 subsequent siblings)
16 siblings, 0 replies; 20+ messages in thread
From: Andy Walls @ 2010-07-20 1:09 UTC (permalink / raw)
To: linux-media; +Cc: Kenney Phillisjr, Steven Toth, Jean Delvare
From: Jean Delvare <khali@linux-fr.org>
Don't just check for nacks on zero-length transactions. Check on
other transactions too.
Signed-off-by: Jean Delvare <khali@linux-fr.org>
Signed-off-by: Andy Walls <awalls@md.metrocast.net>
---
drivers/media/video/cx23885/cx23885-i2c.c | 8 ++++++++
1 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/drivers/media/video/cx23885/cx23885-i2c.c b/drivers/media/video/cx23885/cx23885-i2c.c
index 154c914..afb8d6f 100644
--- a/drivers/media/video/cx23885/cx23885-i2c.c
+++ b/drivers/media/video/cx23885/cx23885-i2c.c
@@ -125,6 +125,10 @@ static int i2c_sendbytes(struct i2c_adapter *i2c_adap,
goto err;
if (retval == 0)
goto eio;
+ if (!i2c_slave_did_ack(i2c_adap)) {
+ retval = -ENXIO;
+ goto err;
+ }
if (i2c_debug) {
printk(" <W %02x %02x", msg->addr << 1, msg->buf[0]);
if (!(ctrl & I2C_NOSTOP))
@@ -214,6 +218,10 @@ static int i2c_readbytes(struct i2c_adapter *i2c_adap,
goto err;
if (retval == 0)
goto eio;
+ if (cnt == 0 && !i2c_slave_did_ack(i2c_adap)) {
+ retval = -ENXIO;
+ goto err;
+ }
msg->buf[cnt] = cx_read(bus->reg_rdata) & 0xff;
if (i2c_debug) {
dprintk(1, " %02x", msg->buf[cnt]);
--
1.7.1.1
^ permalink raw reply related [flat|nested] 20+ messages in thread* [PATCH 03/17] cx23885: i2c_wait_done returns 0 or 1, don't check for < 0 return value
[not found] <cover.1279586511.git.awalls@md.metrocast.net>
2010-07-20 1:08 ` [PATCH 01/17] cx23885: Return -ENXIO on slave nack Andy Walls
2010-07-20 1:09 ` [PATCH 02/17] cx23885: Check for slave nack on all transactions Andy Walls
@ 2010-07-20 1:10 ` Andy Walls
2010-07-20 1:11 ` [PATCH 04/17] cx25840: Make cx25840 i2c register read transactions atomic Andy Walls
` (13 subsequent siblings)
16 siblings, 0 replies; 20+ messages in thread
From: Andy Walls @ 2010-07-20 1:10 UTC (permalink / raw)
To: linux-media; +Cc: Kenney Phillisjr, Steven Toth, Jean Delvare
From: Jean Delvare <khali@linux-fr.org>
Function i2c_wait_done() never returns negative values, so there is no
point in checking for them.
Signed-off-by: Jean Delvare <khali@linux-fr.org>
Signed-off-by: Andy Walls <awalls@md.metrocast.net>
---
drivers/media/video/cx23885/cx23885-i2c.c | 15 +++------------
1 files changed, 3 insertions(+), 12 deletions(-)
diff --git a/drivers/media/video/cx23885/cx23885-i2c.c b/drivers/media/video/cx23885/cx23885-i2c.c
index afb8d6f..1a39148 100644
--- a/drivers/media/video/cx23885/cx23885-i2c.c
+++ b/drivers/media/video/cx23885/cx23885-i2c.c
@@ -120,10 +120,7 @@ static int i2c_sendbytes(struct i2c_adapter *i2c_adap,
cx_write(bus->reg_wdata, wdata);
cx_write(bus->reg_ctrl, ctrl);
- retval = i2c_wait_done(i2c_adap);
- if (retval < 0)
- goto err;
- if (retval == 0)
+ if (!i2c_wait_done(i2c_adap))
goto eio;
if (!i2c_slave_did_ack(i2c_adap)) {
retval = -ENXIO;
@@ -149,10 +146,7 @@ static int i2c_sendbytes(struct i2c_adapter *i2c_adap,
cx_write(bus->reg_wdata, wdata);
cx_write(bus->reg_ctrl, ctrl);
- retval = i2c_wait_done(i2c_adap);
- if (retval < 0)
- goto err;
- if (retval == 0)
+ if (!i2c_wait_done(i2c_adap))
goto eio;
if (i2c_debug) {
dprintk(1, " %02x", msg->buf[cnt]);
@@ -213,10 +207,7 @@ static int i2c_readbytes(struct i2c_adapter *i2c_adap,
cx_write(bus->reg_addr, msg->addr << 25);
cx_write(bus->reg_ctrl, ctrl);
- retval = i2c_wait_done(i2c_adap);
- if (retval < 0)
- goto err;
- if (retval == 0)
+ if (!i2c_wait_done(i2c_adap))
goto eio;
if (cnt == 0 && !i2c_slave_did_ack(i2c_adap)) {
retval = -ENXIO;
--
1.7.1.1
^ permalink raw reply related [flat|nested] 20+ messages in thread* [PATCH 04/17] cx25840: Make cx25840 i2c register read transactions atomic
[not found] <cover.1279586511.git.awalls@md.metrocast.net>
` (2 preceding siblings ...)
2010-07-20 1:10 ` [PATCH 03/17] cx23885: i2c_wait_done returns 0 or 1, don't check for < 0 return value Andy Walls
@ 2010-07-20 1:11 ` Andy Walls
2010-07-20 6:42 ` Jean Delvare
2010-07-20 1:12 ` [PATCH 05/17] cx23885: Add correct detection of the HVR-1250 model 79501 Andy Walls
` (12 subsequent siblings)
16 siblings, 1 reply; 20+ messages in thread
From: Andy Walls @ 2010-07-20 1:11 UTC (permalink / raw)
To: linux-media; +Cc: Mike Isely, Kenney Phillisjr, Hans Verkuil, Jean Delvare
There was a small window between writing the cx25840 register
address over the i2c bus and reading the register contents back from the
cx25840 device that the i2c adapter lock was released. This change ensures the
adapter lock is not released until the register read is done.
Signed-off-by: Andy Walls <awalls@md.metrocast.net>
---
drivers/media/video/cx25840/cx25840-core.c | 58 +++++++++++++++++++---------
1 files changed, 39 insertions(+), 19 deletions(-)
diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c
index bb4872b..4f908fa 100644
--- a/drivers/media/video/cx25840/cx25840-core.c
+++ b/drivers/media/video/cx25840/cx25840-core.c
@@ -80,33 +80,53 @@ int cx25840_write4(struct i2c_client *client, u16 addr, u32 value)
u8 cx25840_read(struct i2c_client * client, u16 addr)
{
- u8 buffer[2];
- buffer[0] = addr >> 8;
- buffer[1] = addr & 0xff;
-
- if (i2c_master_send(client, buffer, 2) < 2)
- return 0;
-
- if (i2c_master_recv(client, buffer, 1) < 1)
+ struct i2c_msg msgs[2];
+ u8 tx_buf[2], rx_buf[1];
+
+ /* Write register address */
+ tx_buf[0] = addr >> 8;
+ tx_buf[1] = addr & 0xff;
+ msgs[0].addr = client->addr;
+ msgs[0].flags = 0;
+ msgs[0].len = 2;
+ msgs[0].buf = (char *) tx_buf;
+
+ /* Read data from register */
+ msgs[1].addr = client->addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = 1;
+ msgs[1].buf = (char *) rx_buf;
+
+ if (i2c_transfer(client->adapter, msgs, 2) < 2)
return 0;
- return buffer[0];
+ return rx_buf[0];
}
u32 cx25840_read4(struct i2c_client * client, u16 addr)
{
- u8 buffer[4];
- buffer[0] = addr >> 8;
- buffer[1] = addr & 0xff;
-
- if (i2c_master_send(client, buffer, 2) < 2)
- return 0;
-
- if (i2c_master_recv(client, buffer, 4) < 4)
+ struct i2c_msg msgs[2];
+ u8 tx_buf[2], rx_buf[4];
+
+ /* Write register address */
+ tx_buf[0] = addr >> 8;
+ tx_buf[1] = addr & 0xff;
+ msgs[0].addr = client->addr;
+ msgs[0].flags = 0;
+ msgs[0].len = 2;
+ msgs[0].buf = (char *) tx_buf;
+
+ /* Read data from registers */
+ msgs[1].addr = client->addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = 4;
+ msgs[1].buf = (char *) rx_buf;
+
+ if (i2c_transfer(client->adapter, msgs, 2) < 2)
return 0;
- return (buffer[3] << 24) | (buffer[2] << 16) |
- (buffer[1] << 8) | buffer[0];
+ return (rx_buf[3] << 24) | (rx_buf[2] << 16) | (rx_buf[1] << 8) |
+ rx_buf[0];
}
int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned and_mask,
--
1.7.1.1
^ permalink raw reply related [flat|nested] 20+ messages in thread* Re: [PATCH 04/17] cx25840: Make cx25840 i2c register read transactions atomic
2010-07-20 1:11 ` [PATCH 04/17] cx25840: Make cx25840 i2c register read transactions atomic Andy Walls
@ 2010-07-20 6:42 ` Jean Delvare
2010-07-20 12:35 ` Andy Walls
0 siblings, 1 reply; 20+ messages in thread
From: Jean Delvare @ 2010-07-20 6:42 UTC (permalink / raw)
To: Andy Walls; +Cc: linux-media, Mike Isely, Kenney Phillisjr, Hans Verkuil
Hi Andy,
On Mon, 19 Jul 2010 21:11:46 -0400, Andy Walls wrote:
> There was a small window between writing the cx25840 register
> address over the i2c bus and reading the register contents back from the
> cx25840 device that the i2c adapter lock was released. This change ensures the
> adapter lock is not released until the register read is done.
>
> Signed-off-by: Andy Walls <awalls@md.metrocast.net>
Good catch.
Acked-by: Jean Delvare <khali@linux-fr.org>
Note that cx25840_and_or() still has a (smaller and less dangerous)
race window. If several calls to cx25840_and_or() happen in parallel on
the same register, some of the changes may be lost. I don't know if
this can be a problem in practice though. If it is, then additional
locking around cx25840_and_or() would be needed.
> ---
> drivers/media/video/cx25840/cx25840-core.c | 58 +++++++++++++++++++---------
> 1 files changed, 39 insertions(+), 19 deletions(-)
>
> diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c
> index bb4872b..4f908fa 100644
> --- a/drivers/media/video/cx25840/cx25840-core.c
> +++ b/drivers/media/video/cx25840/cx25840-core.c
> @@ -80,33 +80,53 @@ int cx25840_write4(struct i2c_client *client, u16 addr, u32 value)
>
> u8 cx25840_read(struct i2c_client * client, u16 addr)
> {
> - u8 buffer[2];
> - buffer[0] = addr >> 8;
> - buffer[1] = addr & 0xff;
> -
> - if (i2c_master_send(client, buffer, 2) < 2)
> - return 0;
> -
> - if (i2c_master_recv(client, buffer, 1) < 1)
> + struct i2c_msg msgs[2];
> + u8 tx_buf[2], rx_buf[1];
> +
> + /* Write register address */
> + tx_buf[0] = addr >> 8;
> + tx_buf[1] = addr & 0xff;
> + msgs[0].addr = client->addr;
> + msgs[0].flags = 0;
> + msgs[0].len = 2;
> + msgs[0].buf = (char *) tx_buf;
> +
> + /* Read data from register */
> + msgs[1].addr = client->addr;
> + msgs[1].flags = I2C_M_RD;
> + msgs[1].len = 1;
> + msgs[1].buf = (char *) rx_buf;
> +
> + if (i2c_transfer(client->adapter, msgs, 2) < 2)
> return 0;
>
> - return buffer[0];
> + return rx_buf[0];
> }
>
> u32 cx25840_read4(struct i2c_client * client, u16 addr)
> {
> - u8 buffer[4];
> - buffer[0] = addr >> 8;
> - buffer[1] = addr & 0xff;
> -
> - if (i2c_master_send(client, buffer, 2) < 2)
> - return 0;
> -
> - if (i2c_master_recv(client, buffer, 4) < 4)
> + struct i2c_msg msgs[2];
> + u8 tx_buf[2], rx_buf[4];
> +
> + /* Write register address */
> + tx_buf[0] = addr >> 8;
> + tx_buf[1] = addr & 0xff;
> + msgs[0].addr = client->addr;
> + msgs[0].flags = 0;
> + msgs[0].len = 2;
> + msgs[0].buf = (char *) tx_buf;
> +
> + /* Read data from registers */
> + msgs[1].addr = client->addr;
> + msgs[1].flags = I2C_M_RD;
> + msgs[1].len = 4;
> + msgs[1].buf = (char *) rx_buf;
> +
> + if (i2c_transfer(client->adapter, msgs, 2) < 2)
> return 0;
>
> - return (buffer[3] << 24) | (buffer[2] << 16) |
> - (buffer[1] << 8) | buffer[0];
> + return (rx_buf[3] << 24) | (rx_buf[2] << 16) | (rx_buf[1] << 8) |
> + rx_buf[0];
> }
>
> int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned and_mask,
--
Jean Delvare
^ permalink raw reply [flat|nested] 20+ messages in thread* Re: [PATCH 04/17] cx25840: Make cx25840 i2c register read transactions atomic
2010-07-20 6:42 ` Jean Delvare
@ 2010-07-20 12:35 ` Andy Walls
2010-07-20 12:46 ` Jean Delvare
0 siblings, 1 reply; 20+ messages in thread
From: Andy Walls @ 2010-07-20 12:35 UTC (permalink / raw)
To: Jean Delvare; +Cc: linux-media, Mike Isely, Kenney Phillisjr, Hans Verkuil
On Tue, 2010-07-20 at 08:42 +0200, Jean Delvare wrote:
> Hi Andy,
>
> On Mon, 19 Jul 2010 21:11:46 -0400, Andy Walls wrote:
> > There was a small window between writing the cx25840 register
> > address over the i2c bus and reading the register contents back from the
> > cx25840 device that the i2c adapter lock was released. This change ensures the
> > adapter lock is not released until the register read is done.
> >
> > Signed-off-by: Andy Walls <awalls@md.metrocast.net>
>
> Good catch.
Thanks.
> Acked-by: Jean Delvare <khali@linux-fr.org>
>
> Note that cx25840_and_or() still has a (smaller and less dangerous)
> race window. If several calls to cx25840_and_or() happen in parallel on
> the same register, some of the changes may be lost. I don't know if
> this can be a problem in practice though. If it is, then additional
> locking around cx25840_and_or() would be needed.
Ah, thank you for pointing that out.
So, please bear with me while I think out loud:
1. There are many explicit cases of read-modify-write on a register in
the cx25840 module, this is not the only one.
2. The bridge driver historically has always serialized access to the
cx25840 module so races have never previously been an issue.
3. I have added a work handler in the cx23885 module that calls the
cx25840 module's interrupt handler. Calls by the work handler are
serialized with respect to themselves, but not with respect to
serialized calls in #2.
4. IIRC, registers written to by the cx25840 interrupt handler are never
written to by the other cx25840 module functions; and vice-versa. I
will have to audit the cx25840 module code to be sure.
5. There is always a race on r-m-w on *some* registers in the
0x800-0x9ff range with the audio microcontroller that is built into the
chip. The only way to provide locking for those is to halt the
microcontroller. I've just looked at Patch 12/17 again. The interrupt
handler only reads the CX23885_AUD_MC_INT_MASK_REG, which is used by the
audio micrcontroller. The interrupt handler does do a r-m-w on the
CX25840_AUD_INT_STAT_REG, but that register is not used by the
microcontroller.
So, I think I'm OK with just not dropping the i2c adapter lock in a
cx25840 register read transaction until it is complete.
Thanks for making me think that one through. :)
Regards,
Andy
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 04/17] cx25840: Make cx25840 i2c register read transactions atomic
2010-07-20 12:35 ` Andy Walls
@ 2010-07-20 12:46 ` Jean Delvare
0 siblings, 0 replies; 20+ messages in thread
From: Jean Delvare @ 2010-07-20 12:46 UTC (permalink / raw)
To: Andy Walls; +Cc: linux-media, Mike Isely, Kenney Phillisjr, Hans Verkuil
On Tue, 20 Jul 2010 08:35:26 -0400, Andy Walls wrote:
> On Tue, 2010-07-20 at 08:42 +0200, Jean Delvare wrote:
> > Hi Andy,
> >
> > On Mon, 19 Jul 2010 21:11:46 -0400, Andy Walls wrote:
> > > There was a small window between writing the cx25840 register
> > > address over the i2c bus and reading the register contents back from the
> > > cx25840 device that the i2c adapter lock was released. This change ensures the
> > > adapter lock is not released until the register read is done.
> > >
> > > Signed-off-by: Andy Walls <awalls@md.metrocast.net>
> >
> > Good catch.
>
> Thanks.
>
> > Acked-by: Jean Delvare <khali@linux-fr.org>
> >
> > Note that cx25840_and_or() still has a (smaller and less dangerous)
> > race window. If several calls to cx25840_and_or() happen in parallel on
> > the same register, some of the changes may be lost. I don't know if
> > this can be a problem in practice though. If it is, then additional
> > locking around cx25840_and_or() would be needed.
>
> Ah, thank you for pointing that out.
>
> So, please bear with me while I think out loud:
>
> 1. There are many explicit cases of read-modify-write on a register in
> the cx25840 module, this is not the only one.
>
> 2. The bridge driver historically has always serialized access to the
> cx25840 module so races have never previously been an issue.
>
> 3. I have added a work handler in the cx23885 module that calls the
> cx25840 module's interrupt handler. Calls by the work handler are
> serialized with respect to themselves, but not with respect to
> serialized calls in #2.
>
> 4. IIRC, registers written to by the cx25840 interrupt handler are never
> written to by the other cx25840 module functions; and vice-versa. I
> will have to audit the cx25840 module code to be sure.
>
> 5. There is always a race on r-m-w on *some* registers in the
> 0x800-0x9ff range with the audio microcontroller that is built into the
> chip. The only way to provide locking for those is to halt the
> microcontroller. I've just looked at Patch 12/17 again. The interrupt
> handler only reads the CX23885_AUD_MC_INT_MASK_REG, which is used by the
> audio micrcontroller. The interrupt handler does do a r-m-w on the
> CX25840_AUD_INT_STAT_REG, but that register is not used by the
> microcontroller.
>
>
> So, I think I'm OK with just not dropping the i2c adapter lock in a
> cx25840 register read transaction until it is complete.
>
> Thanks for making me think that one through. :)
Looks like you're OK then, but you may want to document this somewhere
for future contributors to these drivers.
--
Jean Delvare
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 05/17] cx23885: Add correct detection of the HVR-1250 model 79501
[not found] <cover.1279586511.git.awalls@md.metrocast.net>
` (3 preceding siblings ...)
2010-07-20 1:11 ` [PATCH 04/17] cx25840: Make cx25840 i2c register read transactions atomic Andy Walls
@ 2010-07-20 1:12 ` Andy Walls
2010-07-20 1:12 ` [PATCH 06/17] cx23885: Add a VIDIOC_LOG_STATUS ioctl function for analog video devices Andy Walls
` (11 subsequent siblings)
16 siblings, 0 replies; 20+ messages in thread
From: Andy Walls @ 2010-07-20 1:12 UTC (permalink / raw)
To: linux-media; +Cc: Kenney Phillisjr, Steven Toth
The offset in the eeprom data for the 79501 version of the HVR-1250 is at 0xc0
vs. the standard 0x80.
Signed-off-by: Andy Walls <awalls@md.metrocast.net>
---
drivers/media/video/cx23885/cx23885-cards.c | 10 ++++++++++
1 files changed, 10 insertions(+), 0 deletions(-)
diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c
index d639186..093de84 100644
--- a/drivers/media/video/cx23885/cx23885-cards.c
+++ b/drivers/media/video/cx23885/cx23885-cards.c
@@ -586,6 +586,9 @@ static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data)
case 79101:
/* WinTV-HVR1250 (PCIe, Retail, IR, half height,
ATSC and Basic analog */
+ case 79501:
+ /* WinTV-HVR1250 (PCIe, No IR, half height,
+ ATSC [at least] and Basic analog) */
case 79561:
/* WinTV-HVR1250 (PCIe, OEM, No IR, half height,
ATSC and Basic analog */
@@ -988,6 +991,13 @@ void cx23885_card_setup(struct cx23885_dev *dev)
switch (dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ if (dev->i2c_bus[0].i2c_rc == 0) {
+ if (eeprom[0x80] != 0x84)
+ hauppauge_eeprom(dev, eeprom+0xc0);
+ else
+ hauppauge_eeprom(dev, eeprom+0x80);
+ }
+ break;
case CX23885_BOARD_HAUPPAUGE_HVR1500:
case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
case CX23885_BOARD_HAUPPAUGE_HVR1400:
--
1.7.1.1
^ permalink raw reply related [flat|nested] 20+ messages in thread* [PATCH 06/17] cx23885: Add a VIDIOC_LOG_STATUS ioctl function for analog video devices
[not found] <cover.1279586511.git.awalls@md.metrocast.net>
` (4 preceding siblings ...)
2010-07-20 1:12 ` [PATCH 05/17] cx23885: Add correct detection of the HVR-1250 model 79501 Andy Walls
@ 2010-07-20 1:12 ` Andy Walls
2010-07-20 1:16 ` [PATCH 07/17] v4l2_subdev: Add s_io_pin_config to v4l2_subdev_core_ops Andy Walls
` (10 subsequent siblings)
16 siblings, 0 replies; 20+ messages in thread
From: Andy Walls @ 2010-07-20 1:12 UTC (permalink / raw)
To: linux-media; +Cc: Kenney Phillisjr, Hans Verkuil, Steven Toth
Add a simple log_status function for raw analog video capture device nodes,
to provide insight into the state of the CX2388[578] A/V decoder core.
Signed-off-by: Andy Walls <awalls@md.metrocast.net>
---
drivers/media/video/cx23885/cx23885-video.c | 16 ++++++++++++++++
1 files changed, 16 insertions(+), 0 deletions(-)
diff --git a/drivers/media/video/cx23885/cx23885-video.c b/drivers/media/video/cx23885/cx23885-video.c
index 4e44dcd..2519455 100644
--- a/drivers/media/video/cx23885/cx23885-video.c
+++ b/drivers/media/video/cx23885/cx23885-video.c
@@ -1205,6 +1205,21 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
return 0;
}
+static int vidioc_log_status(struct file *file, void *priv)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+
+ printk(KERN_INFO
+ "%s/0: ============ START LOG STATUS ============\n",
+ dev->name);
+ call_all(dev, core, log_status);
+ printk(KERN_INFO
+ "%s/0: ============= END LOG STATUS =============\n",
+ dev->name);
+ return 0;
+}
+
static int vidioc_queryctrl(struct file *file, void *priv,
struct v4l2_queryctrl *qctrl)
{
@@ -1410,6 +1425,7 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
+ .vidioc_log_status = vidioc_log_status,
.vidioc_queryctrl = vidioc_queryctrl,
.vidioc_g_ctrl = vidioc_g_ctrl,
.vidioc_s_ctrl = vidioc_s_ctrl,
--
1.7.1.1
^ permalink raw reply related [flat|nested] 20+ messages in thread* [PATCH 07/17] v4l2_subdev: Add s_io_pin_config to v4l2_subdev_core_ops
[not found] <cover.1279586511.git.awalls@md.metrocast.net>
` (5 preceding siblings ...)
2010-07-20 1:12 ` [PATCH 06/17] cx23885: Add a VIDIOC_LOG_STATUS ioctl function for analog video devices Andy Walls
@ 2010-07-20 1:16 ` Andy Walls
2010-07-20 1:18 ` [PATCH 08/17] cx25840: Add s_io_pin_config core subdev ops for the CX2388[578] Andy Walls
` (9 subsequent siblings)
16 siblings, 0 replies; 20+ messages in thread
From: Andy Walls @ 2010-07-20 1:16 UTC (permalink / raw)
To: linux-media
Cc: Mike Isely, Kenney Phillisjr, Jarod Wilson, Hans Verkuil,
Steven Toth, Igor M.Liplianin
Add a method to v4l2_sudev_core_ops to allow bridge drivers to
manage what signal pads/functions are routed out to multiplexed IO pins on a
pin by pin basis. The interface also allows specifying initial output settings
for pins and disabling an IO pin altogether.
Signed-off-by: Andy Walls <awalls@md.metrocast.net>
---
include/media/v4l2-subdev.h | 23 +++++++++++++++++++++++
1 files changed, 23 insertions(+), 0 deletions(-)
diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
index 02c6f4d..9195ad4 100644
--- a/include/media/v4l2-subdev.h
+++ b/include/media/v4l2-subdev.h
@@ -90,10 +90,31 @@ struct v4l2_decode_vbi_line {
not yet implemented) since ops provide proper type-checking.
*/
+/* Subdevice external IO pin configuration */
+#define V4L2_SUBDEV_IO_PIN_DISABLE (1 << 0) /* ENABLE assumed */
+#define V4L2_SUBDEV_IO_PIN_OUTPUT (1 << 1)
+#define V4L2_SUBDEV_IO_PIN_INPUT (1 << 2)
+#define V4L2_SUBDEV_IO_PIN_SET_VALUE (1 << 3) /* Set output value */
+#define V4L2_SUBDEV_IO_PIN_ACTIVE_LOW (1 << 4) /* ACTIVE HIGH assumed */
+
+struct v4l2_subdev_io_pin_config {
+ u32 flags; /* V4L2_SUBDEV_IO_PIN_* flags for this pin's config */
+ u8 pin; /* Chip external IO pin to configure */
+ u8 function; /* Internal signal pad/function to route to IO pin */
+ u8 value; /* Initial value for pin - e.g. GPIO output value */
+ u8 strength; /* Pin drive strength */
+};
+
/* s_config: if set, then it is always called by the v4l2_i2c_new_subdev*
functions after the v4l2_subdev was registered. It is used to pass
platform data to the subdev which can be used during initialization.
+ s_io_pin_config: configure one or more chip I/O pins for chips that
+ multiplex different internal signal pads out to IO pins. This function
+ takes a pointer to an array of 'n' pin configuration entries, one for
+ each pin being configured. This function could be called at times
+ other than just subdevice initialization.
+
init: initialize the sensor registors to some sort of reasonable default
values. Do not use for new drivers and should be removed in existing
drivers.
@@ -115,6 +136,8 @@ struct v4l2_subdev_core_ops {
int (*g_chip_ident)(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip);
int (*log_status)(struct v4l2_subdev *sd);
int (*s_config)(struct v4l2_subdev *sd, int irq, void *platform_data);
+ int (*s_io_pin_config)(struct v4l2_subdev *sd, size_t n,
+ struct v4l2_subdev_io_pin_config *pincfg);
int (*init)(struct v4l2_subdev *sd, u32 val);
int (*load_fw)(struct v4l2_subdev *sd);
int (*reset)(struct v4l2_subdev *sd, u32 val);
--
1.7.1.1
^ permalink raw reply related [flat|nested] 20+ messages in thread* [PATCH 08/17] cx25840: Add s_io_pin_config core subdev ops for the CX2388[578]
[not found] <cover.1279586511.git.awalls@md.metrocast.net>
` (6 preceding siblings ...)
2010-07-20 1:16 ` [PATCH 07/17] v4l2_subdev: Add s_io_pin_config to v4l2_subdev_core_ops Andy Walls
@ 2010-07-20 1:18 ` Andy Walls
2010-07-20 1:18 ` [PATCH 09/17] v4l2_subdev, cx23885: Differentiate IR carrier sense and I/O pin inversion Andy Walls
` (8 subsequent siblings)
16 siblings, 0 replies; 20+ messages in thread
From: Andy Walls @ 2010-07-20 1:18 UTC (permalink / raw)
To: linux-media
Cc: Mike Isely, Kenney Phillisjr, Jarod Wilson, Hans Verkuil,
Steven Toth, Igor M.Liplianin
Add s_io_pin_config core subdev op for the CX2388[578] AV cores.
This is complete for IR_RX, IR_TX, GPIOs 16,19-23, and IRQ_N.
It likely needs work for the I2S signal direction.
Signed-off-by: Andy Walls <awalls@md.metrocast.net>
---
drivers/media/video/cx25840/cx25840-core.c | 153 ++++++++++++++++++++++++++++
include/media/cx25840.h | 75 ++++++++++++++
2 files changed, 228 insertions(+), 0 deletions(-)
diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c
index 4f908fa..46a046d 100644
--- a/drivers/media/video/cx25840/cx25840-core.c
+++ b/drivers/media/video/cx25840/cx25840-core.c
@@ -144,6 +144,158 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
/* ----------------------------------------------------------------------- */
+static int cx23885_s_io_pin_config(struct v4l2_subdev *sd, size_t n,
+ struct v4l2_subdev_io_pin_config *p)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int i;
+ u32 pin_ctrl;
+ u8 gpio_oe, gpio_data, strength;
+
+ pin_ctrl = cx25840_read4(client, 0x120);
+ gpio_oe = cx25840_read(client, 0x160);
+ gpio_data = cx25840_read(client, 0x164);
+
+ for (i = 0; i < n; i++) {
+ strength = p[i].strength;
+ if (strength > CX25840_PIN_DRIVE_FAST)
+ strength = CX25840_PIN_DRIVE_FAST;
+
+ switch (p[i].pin) {
+ case CX23885_PIN_IRQ_N_GPIO16:
+ if (p[i].function != CX23885_PAD_IRQ_N) {
+ /* GPIO16 */
+ pin_ctrl &= ~(0x1 << 25);
+ } else {
+ /* IRQ_N */
+ if (p[i].flags &
+ (V4L2_SUBDEV_IO_PIN_DISABLE |
+ V4L2_SUBDEV_IO_PIN_INPUT)) {
+ pin_ctrl &= ~(0x1 << 25);
+ } else {
+ pin_ctrl |= (0x1 << 25);
+ }
+ if (p[i].flags &
+ V4L2_SUBDEV_IO_PIN_ACTIVE_LOW) {
+ pin_ctrl &= ~(0x1 << 24);
+ } else {
+ pin_ctrl |= (0x1 << 24);
+ }
+ }
+ break;
+ case CX23885_PIN_IR_RX_GPIO19:
+ if (p[i].function != CX23885_PAD_GPIO19) {
+ /* IR_RX */
+ gpio_oe |= (0x1 << 0);
+ pin_ctrl &= ~(0x3 << 18);
+ pin_ctrl |= (strength << 18);
+ } else {
+ /* GPIO19 */
+ gpio_oe &= ~(0x1 << 0);
+ if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) {
+ gpio_data &= ~(0x1 << 0);
+ gpio_data |= ((p[i].value & 0x1) << 0);
+ }
+ pin_ctrl &= ~(0x3 << 12);
+ pin_ctrl |= (strength << 12);
+ }
+ break;
+ case CX23885_PIN_IR_TX_GPIO20:
+ if (p[i].function != CX23885_PAD_GPIO20) {
+ /* IR_TX */
+ gpio_oe |= (0x1 << 1);
+ if (p[i].flags & V4L2_SUBDEV_IO_PIN_DISABLE)
+ pin_ctrl &= ~(0x1 << 10);
+ else
+ pin_ctrl |= (0x1 << 10);
+ pin_ctrl &= ~(0x3 << 18);
+ pin_ctrl |= (strength << 18);
+ } else {
+ /* GPIO20 */
+ gpio_oe &= ~(0x1 << 1);
+ if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) {
+ gpio_data &= ~(0x1 << 1);
+ gpio_data |= ((p[i].value & 0x1) << 1);
+ }
+ pin_ctrl &= ~(0x3 << 12);
+ pin_ctrl |= (strength << 12);
+ }
+ break;
+ case CX23885_PIN_I2S_SDAT_GPIO21:
+ if (p[i].function != CX23885_PAD_GPIO21) {
+ /* I2S_SDAT */
+ /* TODO: Input or Output config */
+ gpio_oe |= (0x1 << 2);
+ pin_ctrl &= ~(0x3 << 22);
+ pin_ctrl |= (strength << 22);
+ } else {
+ /* GPIO21 */
+ gpio_oe &= ~(0x1 << 2);
+ if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) {
+ gpio_data &= ~(0x1 << 2);
+ gpio_data |= ((p[i].value & 0x1) << 2);
+ }
+ pin_ctrl &= ~(0x3 << 12);
+ pin_ctrl |= (strength << 12);
+ }
+ break;
+ case CX23885_PIN_I2S_WCLK_GPIO22:
+ if (p[i].function != CX23885_PAD_GPIO22) {
+ /* I2S_WCLK */
+ /* TODO: Input or Output config */
+ gpio_oe |= (0x1 << 3);
+ pin_ctrl &= ~(0x3 << 22);
+ pin_ctrl |= (strength << 22);
+ } else {
+ /* GPIO22 */
+ gpio_oe &= ~(0x1 << 3);
+ if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) {
+ gpio_data &= ~(0x1 << 3);
+ gpio_data |= ((p[i].value & 0x1) << 3);
+ }
+ pin_ctrl &= ~(0x3 << 12);
+ pin_ctrl |= (strength << 12);
+ }
+ break;
+ case CX23885_PIN_I2S_BCLK_GPIO23:
+ if (p[i].function != CX23885_PAD_GPIO23) {
+ /* I2S_BCLK */
+ /* TODO: Input or Output config */
+ gpio_oe |= (0x1 << 4);
+ pin_ctrl &= ~(0x3 << 22);
+ pin_ctrl |= (strength << 22);
+ } else {
+ /* GPIO23 */
+ gpio_oe &= ~(0x1 << 4);
+ if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) {
+ gpio_data &= ~(0x1 << 4);
+ gpio_data |= ((p[i].value & 0x1) << 4);
+ }
+ pin_ctrl &= ~(0x3 << 12);
+ pin_ctrl |= (strength << 12);
+ }
+ break;
+ }
+ }
+
+ cx25840_write(client, 0x164, gpio_data);
+ cx25840_write(client, 0x160, gpio_oe);
+ cx25840_write4(client, 0x120, pin_ctrl);
+ return 0;
+}
+
+static int common_s_io_pin_config(struct v4l2_subdev *sd, size_t n,
+ struct v4l2_subdev_io_pin_config *pincfg)
+{
+ struct cx25840_state *state = to_state(sd);
+
+ if (is_cx2388x(state))
+ return cx23885_s_io_pin_config(sd, n, pincfg);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
static void init_dll1(struct i2c_client *client)
{
/* This is the Hauppauge sequence used to
@@ -1610,6 +1762,7 @@ static const struct v4l2_subdev_core_ops cx25840_core_ops = {
.s_std = cx25840_s_std,
.reset = cx25840_reset,
.load_fw = cx25840_load_fw,
+ .s_io_pin_config = common_s_io_pin_config,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = cx25840_g_register,
.s_register = cx25840_s_register,
diff --git a/include/media/cx25840.h b/include/media/cx25840.h
index 0b0cb17..1bba39e 100644
--- a/include/media/cx25840.h
+++ b/include/media/cx25840.h
@@ -97,4 +97,79 @@ enum cx25840_audio_input {
CX25840_AUDIO8,
};
+enum cx25840_io_pin {
+ CX25840_PIN_DVALID_PRGM0 = 0,
+ CX25840_PIN_FIELD_PRGM1,
+ CX25840_PIN_HRESET_PRGM2,
+ CX25840_PIN_VRESET_HCTL_PRGM3,
+ CX25840_PIN_IRQ_N_PRGM4,
+ CX25840_PIN_IR_TX_PRGM6,
+ CX25840_PIN_IR_RX_PRGM5,
+ CX25840_PIN_GPIO0_PRGM8,
+ CX25840_PIN_GPIO1_PRGM9,
+ CX25840_PIN_SA_SDIN, /* Alternate GP Input only */
+ CX25840_PIN_SA_SDOUT, /* Alternate GP Input only */
+ CX25840_PIN_PLL_CLK_PRGM7,
+ CX25840_PIN_CHIP_SEL_VIPCLK, /* Output only */
+};
+
+enum cx25840_io_pad {
+ /* Output pads */
+ CX25840_PAD_DEFAULT = 0,
+ CX25840_PAD_ACTIVE,
+ CX25840_PAD_VACTIVE,
+ CX25840_PAD_CBFLAG,
+ CX25840_PAD_VID_DATA_EXT0,
+ CX25840_PAD_VID_DATA_EXT1,
+ CX25840_PAD_GPO0,
+ CX25840_PAD_GPO1,
+ CX25840_PAD_GPO2,
+ CX25840_PAD_GPO3,
+ CX25840_PAD_IRQ_N,
+ CX25840_PAD_AC_SYNC,
+ CX25840_PAD_AC_SDOUT,
+ CX25840_PAD_PLL_CLK,
+ CX25840_PAD_VRESET,
+ CX25840_PAD_RESERVED,
+ /* Pads for PLL_CLK output only */
+ CX25840_PAD_XTI_X5_DLL,
+ CX25840_PAD_AUX_PLL,
+ CX25840_PAD_VID_PLL,
+ CX25840_PAD_XTI,
+ /* Input Pads */
+ CX25840_PAD_GPI0,
+ CX25840_PAD_GPI1,
+ CX25840_PAD_GPI2,
+ CX25840_PAD_GPI3,
+};
+
+enum cx25840_io_pin_strength {
+ CX25840_PIN_DRIVE_MEDIUM = 0,
+ CX25840_PIN_DRIVE_SLOW,
+ CX25840_PIN_DRIVE_FAST,
+};
+
+enum cx23885_io_pin {
+ CX23885_PIN_IR_RX_GPIO19,
+ CX23885_PIN_IR_TX_GPIO20,
+ CX23885_PIN_I2S_SDAT_GPIO21,
+ CX23885_PIN_I2S_WCLK_GPIO22,
+ CX23885_PIN_I2S_BCLK_GPIO23,
+ CX23885_PIN_IRQ_N_GPIO16,
+};
+
+enum cx23885_io_pad {
+ CX23885_PAD_IR_RX,
+ CX23885_PAD_GPIO19,
+ CX23885_PAD_IR_TX,
+ CX23885_PAD_GPIO20,
+ CX23885_PAD_I2S_SDAT,
+ CX23885_PAD_GPIO21,
+ CX23885_PAD_I2S_WCLK,
+ CX23885_PAD_GPIO22,
+ CX23885_PAD_I2S_BCLK,
+ CX23885_PAD_GPIO23,
+ CX23885_PAD_IRQ_N,
+ CX23885_PAD_GPIO16,
+};
#endif
--
1.7.1.1
^ permalink raw reply related [flat|nested] 20+ messages in thread* [PATCH 09/17] v4l2_subdev, cx23885: Differentiate IR carrier sense and I/O pin inversion
[not found] <cover.1279586511.git.awalls@md.metrocast.net>
` (7 preceding siblings ...)
2010-07-20 1:18 ` [PATCH 08/17] cx25840: Add s_io_pin_config core subdev ops for the CX2388[578] Andy Walls
@ 2010-07-20 1:18 ` Andy Walls
2010-07-20 1:19 ` [PATCH 10/17] cx23885: For CX23888 IR, configure the IO pin mux IR pins explcitly Andy Walls
` (7 subsequent siblings)
16 siblings, 0 replies; 20+ messages in thread
From: Andy Walls @ 2010-07-20 1:18 UTC (permalink / raw)
To: linux-media
Cc: Kenney Phillisjr, Jarod Wilson, Hans Verkuil, Steven Toth,
Igor M.Liplianin
There is a distinction on IR Tx for the CX2388[578] chips of carrier
sense inversion (space is a carrier burst and mark is no burst) and I/O
pin level inversion (0 is high output level, 1 is low output level).
Allow the caller to set these parameters distinctly as v4l2_subdevice
IR parameters. This permits the IR device to be configured and enabled
without the IR Tx LED being on during idle/space time due to an external
hardware level inversion
Signed-off-by: Andy Walls <awalls@md.metrocast.net>
---
drivers/media/video/cx23885/cx23885-input.c | 2 +-
drivers/media/video/cx23885/cx23888-ir.c | 32 ++++++++++++++++++++------
include/media/v4l2-subdev.h | 5 +++-
3 files changed, 29 insertions(+), 10 deletions(-)
diff --git a/drivers/media/video/cx23885/cx23885-input.c b/drivers/media/video/cx23885/cx23885-input.c
index d0b1613..496d751 100644
--- a/drivers/media/video/cx23885/cx23885-input.c
+++ b/drivers/media/video/cx23885/cx23885-input.c
@@ -170,7 +170,7 @@ static int cx23885_input_ir_start(struct cx23885_dev *dev)
* mark is received as low logic level;
* falling edges are detected as rising edges; etc.
*/
- params.invert = true;
+ params.invert_level = true;
break;
}
v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, ¶ms);
diff --git a/drivers/media/video/cx23885/cx23888-ir.c b/drivers/media/video/cx23885/cx23888-ir.c
index f63d378..28ca90f 100644
--- a/drivers/media/video/cx23885/cx23888-ir.c
+++ b/drivers/media/video/cx23885/cx23888-ir.c
@@ -60,6 +60,8 @@ MODULE_PARM_DESC(ir_888_debug, "enable debug messages [CX23888 IR controller]");
#define CNTRL_CPL 0x00001000
#define CNTRL_LBM 0x00002000
#define CNTRL_R 0x00004000
+/* CX23888 specific control flag */
+#define CNTRL_IVO 0x00008000
#define CX23888_IR_TXCLK_REG 0x170004
#define TXCLK_TCD 0x0000FFFF
@@ -423,6 +425,13 @@ static inline void control_tx_polarity_invert(struct cx23885_dev *dev,
invert ? CNTRL_CPL : 0);
}
+static inline void control_tx_level_invert(struct cx23885_dev *dev,
+ bool invert)
+{
+ cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_IVO,
+ invert ? CNTRL_IVO : 0);
+}
+
/*
* IR Rx & Tx Clock Register helpers
*/
@@ -782,8 +791,8 @@ static int cx23888_ir_rx_s_parameters(struct v4l2_subdev *sd,
control_rx_s_edge_detection(dev, CNTRL_EDG_BOTH);
- o->invert = p->invert;
- atomic_set(&state->rx_invert, p->invert);
+ o->invert_level = p->invert_level;
+ atomic_set(&state->rx_invert, p->invert_level);
o->interrupt_enable = p->interrupt_enable;
o->enable = p->enable;
@@ -894,8 +903,11 @@ static int cx23888_ir_tx_s_parameters(struct v4l2_subdev *sd,
/* FIXME - make this dependent on resolution for better performance */
control_tx_irq_watermark(dev, TX_FIFO_HALF_EMPTY);
- control_tx_polarity_invert(dev, p->invert);
- o->invert = p->invert;
+ control_tx_polarity_invert(dev, p->invert_carrier_sense);
+ o->invert_carrier_sense = p->invert_carrier_sense;
+
+ control_tx_level_invert(dev, p->invert_level);
+ o->invert_level = p->invert_level;
o->interrupt_enable = p->interrupt_enable;
o->enable = p->enable;
@@ -1025,8 +1037,11 @@ static int cx23888_ir_log_status(struct v4l2_subdev *sd)
cntrl & CNTRL_TFE ? "enabled" : "disabled");
v4l2_info(sd, "\tFIFO interrupt watermark: %s\n",
cntrl & CNTRL_TIC ? "not empty" : "half full or less");
- v4l2_info(sd, "\tSignal polarity: %s\n",
- cntrl & CNTRL_CPL ? "0:mark 1:space" : "0:space 1:mark");
+ v4l2_info(sd, "\tOutput pin level inversion %s\n",
+ cntrl & CNTRL_IVO ? "yes" : "no");
+ v4l2_info(sd, "\tCarrier polarity: %s\n",
+ cntrl & CNTRL_CPL ? "space:burst mark:noburst"
+ : "space:noburst mark:burst");
if (cntrl & CNTRL_MOD) {
v4l2_info(sd, "\tCarrier (16 clocks): %u Hz\n",
clock_divider_to_carrier_freq(txclk));
@@ -1146,7 +1161,7 @@ static const struct v4l2_subdev_ir_parameters default_rx_params = {
.noise_filter_min_width = 333333, /* ns */
.carrier_range_lower = 35000,
.carrier_range_upper = 37000,
- .invert = false,
+ .invert_level = false,
};
static const struct v4l2_subdev_ir_parameters default_tx_params = {
@@ -1160,7 +1175,8 @@ static const struct v4l2_subdev_ir_parameters default_tx_params = {
.modulation = true,
.carrier_freq = 36000, /* 36 kHz - RC-5 carrier */
.duty_cycle = 25, /* 25 % - RC-5 carrier */
- .invert = false,
+ .invert_level = false,
+ .invert_carrier_sense = false,
};
int cx23888_ir_probe(struct cx23885_dev *dev)
diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
index 9195ad4..a780cca 100644
--- a/include/media/v4l2-subdev.h
+++ b/include/media/v4l2-subdev.h
@@ -379,7 +379,10 @@ struct v4l2_subdev_ir_parameters {
u32 max_pulse_width; /* ns, valid only for baseband signal */
unsigned int carrier_freq; /* Hz, valid only for modulated signal*/
unsigned int duty_cycle; /* percent, valid only for modulated signal*/
- bool invert; /* logically invert sense of mark/space */
+ bool invert_level; /* invert signal level */
+
+ /* Tx only */
+ bool invert_carrier_sense; /* Send 0/space as a carrier burst */
/* Rx only */
u32 noise_filter_min_width; /* ns, min time of a valid pulse */
--
1.7.1.1
^ permalink raw reply related [flat|nested] 20+ messages in thread* [PATCH 10/17] cx23885: For CX23888 IR, configure the IO pin mux IR pins explcitly
[not found] <cover.1279586511.git.awalls@md.metrocast.net>
` (8 preceding siblings ...)
2010-07-20 1:18 ` [PATCH 09/17] v4l2_subdev, cx23885: Differentiate IR carrier sense and I/O pin inversion Andy Walls
@ 2010-07-20 1:19 ` Andy Walls
2010-07-20 1:20 ` [PATCH 11/17] v4l2_subdev: Move interrupt_service_routine ptr to v4l2_subdev_core_ops Andy Walls
` (6 subsequent siblings)
16 siblings, 0 replies; 20+ messages in thread
From: Andy Walls @ 2010-07-20 1:19 UTC (permalink / raw)
To: linux-media; +Cc: Kenney Phillisjr, Jarod Wilson, Steven Toth
Explicitly configure the IR Tx and IR Rx pins to be driven by the
IR Rx and Tx pads from the AV core for CX23888 IR.
For the HVR-1850 and HVR-1290 configure the IR Tx level inversion,
so the Tx LED is off when idle.
Signed-off-by: Andy Walls <awalls@md.metrocast.net>
---
drivers/media/video/cx23885/cx23885-cards.c | 31 +++++++++++++++++++++++++++
1 files changed, 31 insertions(+), 0 deletions(-)
diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c
index 093de84..191bda0 100644
--- a/drivers/media/video/cx23885/cx23885-cards.c
+++ b/drivers/media/video/cx23885/cx23885-cards.c
@@ -922,6 +922,24 @@ void cx23885_gpio_setup(struct cx23885_dev *dev)
int cx23885_ir_init(struct cx23885_dev *dev)
{
+ static struct v4l2_subdev_io_pin_config ir_pin_cfg[] = {
+ {
+ .flags = V4L2_SUBDEV_IO_PIN_INPUT,
+ .pin = CX23885_PIN_IR_RX_GPIO19,
+ .function = CX23885_PAD_IR_RX,
+ .value = 0,
+ .strength = CX25840_PIN_DRIVE_MEDIUM,
+ }, {
+ .flags = V4L2_SUBDEV_IO_PIN_OUTPUT,
+ .pin = CX23885_PIN_IR_TX_GPIO20,
+ .function = CX23885_PAD_IR_TX,
+ .value = 0,
+ .strength = CX25840_PIN_DRIVE_MEDIUM,
+ }
+ };
+ const size_t ir_pin_cfg_count = ARRAY_SIZE(ir_pin_cfg);
+
+ struct v4l2_subdev_ir_parameters params;
int ret = 0;
switch (dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1250:
@@ -942,7 +960,20 @@ int cx23885_ir_init(struct cx23885_dev *dev)
if (ret)
break;
dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_888_IR);
+ v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
+ ir_pin_cfg_count, ir_pin_cfg);
dev->pci_irqmask |= PCI_MSK_IR;
+ /*
+ * For these boards we need to invert the Tx output via the
+ * IR controller to have the LED off while idle
+ */
+ v4l2_subdev_call(dev->sd_ir, ir, tx_g_parameters, ¶ms);
+ params.enable = false;
+ params.shutdown = false;
+ params.invert_level = true;
+ v4l2_subdev_call(dev->sd_ir, ir, tx_s_parameters, ¶ms);
+ params.shutdown = true;
+ v4l2_subdev_call(dev->sd_ir, ir, tx_s_parameters, ¶ms);
break;
case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP:
request_module("ir-kbd-i2c");
--
1.7.1.1
^ permalink raw reply related [flat|nested] 20+ messages in thread* [PATCH 11/17] v4l2_subdev: Move interrupt_service_routine ptr to v4l2_subdev_core_ops
[not found] <cover.1279586511.git.awalls@md.metrocast.net>
` (9 preceding siblings ...)
2010-07-20 1:19 ` [PATCH 10/17] cx23885: For CX23888 IR, configure the IO pin mux IR pins explcitly Andy Walls
@ 2010-07-20 1:20 ` Andy Walls
2010-07-20 1:22 ` [PATCH 12/17] cx25840: Add support for CX2388[57] A/V core integrated IR controllers Andy Walls
` (5 subsequent siblings)
16 siblings, 0 replies; 20+ messages in thread
From: Andy Walls @ 2010-07-20 1:20 UTC (permalink / raw)
To: linux-media; +Cc: Kenney Phillisjr, Jarod Wilson, Hans Verkuil, Steven Toth
The CX2584x and related cores are multifunction subdevices with a number
of internal blocks that act as interrupt sources. Move the v4L2_subdev
interrupt_service_routine callback from v4l_subdev_ir_ops to
v4l2_subdev_core_ops, as the video and audio blocks of a CX2584x and
related cores can generate interrupts along with the IR block. This
change also makes sense for other subdev's that generate interrupts and
do not have an IR block.
Signed-off-by: Andy Walls <awalls@md.metrocast.net>
---
drivers/media/video/cx23885/cx23885-core.c | 2 +-
drivers/media/video/cx23885/cx23888-ir.c | 3 +--
include/media/v4l2-subdev.h | 16 +++++++---------
3 files changed, 9 insertions(+), 12 deletions(-)
diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c
index 161ae73..ec8baf3 100644
--- a/drivers/media/video/cx23885/cx23885-core.c
+++ b/drivers/media/video/cx23885/cx23885-core.c
@@ -1765,7 +1765,7 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
handled += cx23885_video_irq(dev, vida_status);
if (pci_status & PCI_MSK_IR) {
- v4l2_subdev_call(dev->sd_ir, ir, interrupt_service_routine,
+ v4l2_subdev_call(dev->sd_ir, core, interrupt_service_routine,
pci_status, &ir_handled);
if (ir_handled)
handled++;
diff --git a/drivers/media/video/cx23885/cx23888-ir.c b/drivers/media/video/cx23885/cx23888-ir.c
index 28ca90f..51f2163 100644
--- a/drivers/media/video/cx23885/cx23888-ir.c
+++ b/drivers/media/video/cx23885/cx23888-ir.c
@@ -1126,11 +1126,10 @@ static const struct v4l2_subdev_core_ops cx23888_ir_core_ops = {
.g_register = cx23888_ir_g_register,
.s_register = cx23888_ir_s_register,
#endif
+ .interrupt_service_routine = cx23888_ir_irq_handler,
};
static const struct v4l2_subdev_ir_ops cx23888_ir_ir_ops = {
- .interrupt_service_routine = cx23888_ir_irq_handler,
-
.rx_read = cx23888_ir_rx_read,
.rx_g_parameters = cx23888_ir_rx_g_parameters,
.rx_s_parameters = cx23888_ir_rx_s_parameters,
diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
index a780cca..bacd525 100644
--- a/include/media/v4l2-subdev.h
+++ b/include/media/v4l2-subdev.h
@@ -131,6 +131,11 @@ struct v4l2_subdev_io_pin_config {
s_power: puts subdevice in power saving mode (on == 0) or normal operation
mode (on == 1).
+
+ interrupt_service_routine: Called by the bridge chip's interrupt service
+ handler, when an interrupt status has be raised due to this subdev,
+ so that this subdev can handle the details. It may schedule work to be
+ performed later. It must not sleep. *Called from an IRQ context*.
*/
struct v4l2_subdev_core_ops {
int (*g_chip_ident)(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip);
@@ -156,6 +161,8 @@ struct v4l2_subdev_core_ops {
int (*s_register)(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg);
#endif
int (*s_power)(struct v4l2_subdev *sd, int on);
+ int (*interrupt_service_routine)(struct v4l2_subdev *sd,
+ u32 status, bool *handled);
};
/* s_mode: switch the tuner to a specific tuner mode. Replacement of s_radio.
@@ -330,11 +337,6 @@ struct v4l2_subdev_sensor_ops {
};
/*
- interrupt_service_routine: Called by the bridge chip's interrupt service
- handler, when an IR interrupt status has be raised due to this subdev,
- so that this subdev can handle the details. It may schedule work to be
- performed later. It must not sleep. *Called from an IRQ context*.
-
[rt]x_g_parameters: Get the current operating parameters and state of the
the IR receiver or transmitter.
@@ -392,10 +394,6 @@ struct v4l2_subdev_ir_parameters {
};
struct v4l2_subdev_ir_ops {
- /* Common to receiver and transmitter */
- int (*interrupt_service_routine)(struct v4l2_subdev *sd,
- u32 status, bool *handled);
-
/* Receiver */
int (*rx_read)(struct v4l2_subdev *sd, u8 *buf, size_t count,
ssize_t *num);
--
1.7.1.1
^ permalink raw reply related [flat|nested] 20+ messages in thread* [PATCH 12/17] cx25840: Add support for CX2388[57] A/V core integrated IR controllers
[not found] <cover.1279586511.git.awalls@md.metrocast.net>
` (10 preceding siblings ...)
2010-07-20 1:20 ` [PATCH 11/17] v4l2_subdev: Move interrupt_service_routine ptr to v4l2_subdev_core_ops Andy Walls
@ 2010-07-20 1:22 ` Andy Walls
2010-07-20 1:22 ` [PATCH 13/17] cx23885: Add a v4l2_subdev group id for the CX2388[578] integrated AV core Andy Walls
` (4 subsequent siblings)
16 siblings, 0 replies; 20+ messages in thread
From: Andy Walls @ 2010-07-20 1:22 UTC (permalink / raw)
To: linux-media
Cc: Mike Isely, Kenney Phillisjr, Jarod Wilson, Hans Verkuil,
Steven Toth, Igor M.Liplianin
This patch is primarily a port of the integrated IR controller code in
cx23885/cx23888-ir.c. Right now, only the CX2388[57] AV core will
really try to set up IR. This IR support, by design, still requires the
bridge driver to do final IO pin mux configuration and setup of the IR
controller parameters.
For the CX2388[578] chips, enabling the AV Core for IR also starts
sending Audio and Video interrupts to the bridge driver. For
CX2388[578] chips audio and video interrupts are ignored and
acknowledged when they happen.
IR interrupt handling and status logging is exluded for the CX23888
which does not have an IR controller on the AV core.
Note that experimentation reveals that the IR irq enables on the
CX23885 have an inverted logic sense. The CX23887 likely suffers from
the same quirk. For these chips, those irq enable bits are handled
as interrupt disables.
Signed-off-by: Andy Walls <awalls@md.metrocast.net>
---
drivers/media/video/cx25840/Makefile | 2 +-
drivers/media/video/cx25840/cx25840-core.c | 128 +++
drivers/media/video/cx25840/cx25840-core.h | 28 +
drivers/media/video/cx25840/cx25840-ir.c | 1262 ++++++++++++++++++++++++++++
4 files changed, 1419 insertions(+), 1 deletions(-)
create mode 100644 drivers/media/video/cx25840/cx25840-ir.c
diff --git a/drivers/media/video/cx25840/Makefile b/drivers/media/video/cx25840/Makefile
index 6e8665b..2ee96d3 100644
--- a/drivers/media/video/cx25840/Makefile
+++ b/drivers/media/video/cx25840/Makefile
@@ -1,5 +1,5 @@
cx25840-objs := cx25840-core.o cx25840-audio.o cx25840-firmware.o \
- cx25840-vbi.o
+ cx25840-vbi.o cx25840-ir.o
obj-$(CONFIG_VIDEO_CX25840) += cx25840.o
diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c
index 46a046d..9fab0b1 100644
--- a/drivers/media/video/cx25840/cx25840-core.c
+++ b/drivers/media/video/cx25840/cx25840-core.c
@@ -15,6 +15,9 @@
*
* CX23885 support by Steven Toth <stoth@linuxtv.org>.
*
+ * CX2388[578] IRQ handling, IO Pin mux configuration and other small fixes are
+ * Copyright (C) 2010 Andy Walls <awalls@md.metrocast.net>
+ *
* 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 the Free Software Foundation; either version 2
@@ -48,6 +51,28 @@ MODULE_DESCRIPTION("Conexant CX25840 audio/video decoder driver");
MODULE_AUTHOR("Ulf Eklund, Chris Kennedy, Hans Verkuil, Tyler Trafford");
MODULE_LICENSE("GPL");
+#define CX25840_VID_INT_STAT_REG 0x410
+#define CX25840_VID_INT_STAT_BITS 0x0000ffff
+#define CX25840_VID_INT_MASK_BITS 0xffff0000
+#define CX25840_VID_INT_MASK_SHFT 16
+#define CX25840_VID_INT_MASK_REG 0x412
+
+#define CX23885_AUD_MC_INT_MASK_REG 0x80c
+#define CX23885_AUD_MC_INT_STAT_BITS 0xffff0000
+#define CX23885_AUD_MC_INT_CTRL_BITS 0x0000ffff
+#define CX23885_AUD_MC_INT_STAT_SHFT 16
+
+#define CX25840_AUD_INT_CTRL_REG 0x812
+#define CX25840_AUD_INT_STAT_REG 0x813
+
+#define CX23885_PIN_CTRL_IRQ_REG 0x123
+#define CX23885_PIN_CTRL_IRQ_IR_STAT 0x40
+#define CX23885_PIN_CTRL_IRQ_AUD_STAT 0x20
+#define CX23885_PIN_CTRL_IRQ_VID_STAT 0x10
+
+#define CX25840_IR_STATS_REG 0x210
+#define CX25840_IR_IRQEN_REG 0x214
+
static int cx25840_debug;
module_param_named(debug,cx25840_debug, int, 0644);
@@ -137,6 +162,14 @@ int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned and_mask,
or_value);
}
+int cx25840_and_or4(struct i2c_client *client, u16 addr, u32 and_mask,
+ u32 or_value)
+{
+ return cx25840_write4(client, addr,
+ (cx25840_read4(client, addr) & and_mask) |
+ or_value);
+}
+
/* ----------------------------------------------------------------------- */
static int set_input(struct i2c_client *client, enum cx25840_video_input vid_input,
@@ -592,6 +625,13 @@ static void cx23885_initialize(struct i2c_client *client)
/* start microcontroller */
cx25840_and_or(client, 0x803, ~0x10, 0x10);
+
+ /* Disable and clear video interrupts - we don't use them */
+ cx25840_write4(client, CX25840_VID_INT_STAT_REG, 0xffffffff);
+
+ /* Disable and clear audio interrupts - we don't use them */
+ cx25840_write(client, CX25840_AUD_INT_CTRL_REG, 0xff);
+ cx25840_write(client, CX25840_AUD_INT_STAT_REG, 0xff);
}
/* ----------------------------------------------------------------------- */
@@ -1748,9 +1788,93 @@ static int cx25840_log_status(struct v4l2_subdev *sd)
log_video_status(client);
if (!is_cx2583x(state))
log_audio_status(client);
+ cx25840_ir_log_status(sd);
return 0;
}
+static int cx23885_irq_handler(struct v4l2_subdev *sd, u32 status,
+ bool *handled)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
+ u8 irq_stat, aud_stat, aud_en, ir_stat, ir_en;
+ u32 vid_stat, aud_mc_stat;
+ bool block_handled;
+ int ret = 0;
+
+ irq_stat = cx25840_read(c, CX23885_PIN_CTRL_IRQ_REG);
+ v4l_dbg(2, cx25840_debug, c, "AV Core IRQ status (entry): %s %s %s\n",
+ irq_stat & CX23885_PIN_CTRL_IRQ_IR_STAT ? "ir" : " ",
+ irq_stat & CX23885_PIN_CTRL_IRQ_AUD_STAT ? "aud" : " ",
+ irq_stat & CX23885_PIN_CTRL_IRQ_VID_STAT ? "vid" : " ");
+
+ if ((is_cx23885(state) || is_cx23887(state))) {
+ ir_stat = cx25840_read(c, CX25840_IR_STATS_REG);
+ ir_en = cx25840_read(c, CX25840_IR_IRQEN_REG);
+ v4l_dbg(2, cx25840_debug, c,
+ "AV Core ir IRQ status: %#04x disables: %#04x\n",
+ ir_stat, ir_en);
+ if (irq_stat & CX23885_PIN_CTRL_IRQ_IR_STAT) {
+ block_handled = false;
+ ret = cx25840_ir_irq_handler(sd,
+ status, &block_handled);
+ if (block_handled)
+ *handled = true;
+ }
+ }
+
+ aud_stat = cx25840_read(c, CX25840_AUD_INT_STAT_REG);
+ aud_en = cx25840_read(c, CX25840_AUD_INT_CTRL_REG);
+ v4l_dbg(2, cx25840_debug, c,
+ "AV Core audio IRQ status: %#04x disables: %#04x\n",
+ aud_stat, aud_en);
+ aud_mc_stat = cx25840_read4(c, CX23885_AUD_MC_INT_MASK_REG);
+ v4l_dbg(2, cx25840_debug, c,
+ "AV Core audio MC IRQ status: %#06x enables: %#06x\n",
+ aud_mc_stat >> CX23885_AUD_MC_INT_STAT_SHFT,
+ aud_mc_stat & CX23885_AUD_MC_INT_CTRL_BITS);
+ if (irq_stat & CX23885_PIN_CTRL_IRQ_AUD_STAT) {
+ if (aud_stat) {
+ cx25840_write(c, CX25840_AUD_INT_STAT_REG, aud_stat);
+ *handled = true;
+ }
+ }
+
+ vid_stat = cx25840_read4(c, CX25840_VID_INT_STAT_REG);
+ v4l_dbg(2, cx25840_debug, c,
+ "AV Core video IRQ status: %#06x disables: %#06x\n",
+ vid_stat & CX25840_VID_INT_STAT_BITS,
+ vid_stat >> CX25840_VID_INT_MASK_SHFT);
+ if (irq_stat & CX23885_PIN_CTRL_IRQ_VID_STAT) {
+ if (vid_stat & CX25840_VID_INT_STAT_BITS) {
+ cx25840_write4(c, CX25840_VID_INT_STAT_REG, vid_stat);
+ *handled = true;
+ }
+ }
+
+ irq_stat = cx25840_read(c, CX23885_PIN_CTRL_IRQ_REG);
+ v4l_dbg(2, cx25840_debug, c, "AV Core IRQ status (exit): %s %s %s\n",
+ irq_stat & CX23885_PIN_CTRL_IRQ_IR_STAT ? "ir" : " ",
+ irq_stat & CX23885_PIN_CTRL_IRQ_AUD_STAT ? "aud" : " ",
+ irq_stat & CX23885_PIN_CTRL_IRQ_VID_STAT ? "vid" : " ");
+
+ return ret;
+}
+
+static int cx25840_irq_handler(struct v4l2_subdev *sd, u32 status,
+ bool *handled)
+{
+ struct cx25840_state *state = to_state(sd);
+
+ *handled = false;
+
+ /* Only support the CX2388[578] AV Core for now */
+ if (is_cx2388x(state))
+ return cx23885_irq_handler(sd, status, handled);
+
+ return -ENODEV;
+}
+
/* ----------------------------------------------------------------------- */
static const struct v4l2_subdev_core_ops cx25840_core_ops = {
@@ -1767,6 +1891,7 @@ static const struct v4l2_subdev_core_ops cx25840_core_ops = {
.g_register = cx25840_g_register,
.s_register = cx25840_s_register,
#endif
+ .interrupt_service_routine = cx25840_irq_handler,
};
static const struct v4l2_subdev_tuner_ops cx25840_tuner_ops = {
@@ -1801,6 +1926,7 @@ static const struct v4l2_subdev_ops cx25840_ops = {
.audio = &cx25840_audio_ops,
.video = &cx25840_video_ops,
.vbi = &cx25840_vbi_ops,
+ .ir = &cx25840_ir_ops,
};
/* ----------------------------------------------------------------------- */
@@ -1942,6 +2068,7 @@ static int cx25840_probe(struct i2c_client *client,
state->id = id;
state->rev = device_id;
+ cx25840_ir_probe(sd);
return 0;
}
@@ -1949,6 +2076,7 @@ static int cx25840_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ cx25840_ir_remove(sd);
v4l2_device_unregister_subdev(sd);
kfree(to_state(sd));
return 0;
diff --git a/drivers/media/video/cx25840/cx25840-core.h b/drivers/media/video/cx25840/cx25840-core.h
index 04393b9..8f47322 100644
--- a/drivers/media/video/cx25840/cx25840-core.h
+++ b/drivers/media/video/cx25840/cx25840-core.h
@@ -34,6 +34,8 @@
providing this information. */
#define CX25840_CID_ENABLE_PVR150_WORKAROUND (V4L2_CID_PRIVATE_BASE+0)
+struct cx25840_ir_state;
+
struct cx25840_state {
struct i2c_client *c;
struct v4l2_subdev sd;
@@ -52,6 +54,7 @@ struct cx25840_state {
int is_initialized;
wait_queue_head_t fw_wait; /* wake up when the fw load is finished */
struct work_struct fw_work; /* work entry for fw load */
+ struct cx25840_ir_state *ir_state;
};
static inline struct cx25840_state *to_state(struct v4l2_subdev *sd)
@@ -77,6 +80,21 @@ static inline bool is_cx2388x(struct cx25840_state *state)
state->id == V4L2_IDENT_CX23888_AV;
}
+static inline bool is_cx23885(struct cx25840_state *state)
+{
+ return state->id == V4L2_IDENT_CX23885_AV;
+}
+
+static inline bool is_cx23887(struct cx25840_state *state)
+{
+ return state->id == V4L2_IDENT_CX23887_AV;
+}
+
+static inline bool is_cx23888(struct cx25840_state *state)
+{
+ return state->id == V4L2_IDENT_CX23888_AV;
+}
+
/* ----------------------------------------------------------------------- */
/* cx25850-core.c */
int cx25840_write(struct i2c_client *client, u16 addr, u8 value);
@@ -84,6 +102,8 @@ int cx25840_write4(struct i2c_client *client, u16 addr, u32 value);
u8 cx25840_read(struct i2c_client *client, u16 addr);
u32 cx25840_read4(struct i2c_client *client, u16 addr);
int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned mask, u8 value);
+int cx25840_and_or4(struct i2c_client *client, u16 addr, u32 and_mask,
+ u32 or_value);
void cx25840_std_setup(struct i2c_client *client);
/* ----------------------------------------------------------------------- */
@@ -104,4 +124,12 @@ int cx25840_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *
int cx25840_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt);
int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi);
+/* ----------------------------------------------------------------------- */
+/* cx25850-ir.c */
+extern const struct v4l2_subdev_ir_ops cx25840_ir_ops;
+int cx25840_ir_log_status(struct v4l2_subdev *sd);
+int cx25840_ir_irq_handler(struct v4l2_subdev *sd, u32 status, bool *handled);
+int cx25840_ir_probe(struct v4l2_subdev *sd);
+int cx25840_ir_remove(struct v4l2_subdev *sd);
+
#endif
diff --git a/drivers/media/video/cx25840/cx25840-ir.c b/drivers/media/video/cx25840/cx25840-ir.c
new file mode 100644
index 0000000..308e87e
--- /dev/null
+++ b/drivers/media/video/cx25840/cx25840-ir.c
@@ -0,0 +1,1262 @@
+/*
+ * Driver for the Conexant CX2584x Audio/Video decoder chip and related cores
+ *
+ * Integrated Consumer Infrared Controller
+ *
+ * Copyright (C) 2010 Andy Walls <awalls@md.metrocast.net>
+ *
+ * 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 the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include <linux/slab.h>
+#include <linux/kfifo.h>
+#include <media/cx25840.h>
+
+#include "cx25840-core.h"
+
+static unsigned int ir_debug;
+module_param(ir_debug, int, 0644);
+MODULE_PARM_DESC(ir_debug, "enable integrated IR debug messages");
+
+#define CX25840_IR_REG_BASE 0x200
+
+#define CX25840_IR_CNTRL_REG 0x200
+#define CNTRL_WIN_3_3 0x00000000
+#define CNTRL_WIN_4_3 0x00000001
+#define CNTRL_WIN_3_4 0x00000002
+#define CNTRL_WIN_4_4 0x00000003
+#define CNTRL_WIN 0x00000003
+#define CNTRL_EDG_NONE 0x00000000
+#define CNTRL_EDG_FALL 0x00000004
+#define CNTRL_EDG_RISE 0x00000008
+#define CNTRL_EDG_BOTH 0x0000000C
+#define CNTRL_EDG 0x0000000C
+#define CNTRL_DMD 0x00000010
+#define CNTRL_MOD 0x00000020
+#define CNTRL_RFE 0x00000040
+#define CNTRL_TFE 0x00000080
+#define CNTRL_RXE 0x00000100
+#define CNTRL_TXE 0x00000200
+#define CNTRL_RIC 0x00000400
+#define CNTRL_TIC 0x00000800
+#define CNTRL_CPL 0x00001000
+#define CNTRL_LBM 0x00002000
+#define CNTRL_R 0x00004000
+
+#define CX25840_IR_TXCLK_REG 0x204
+#define TXCLK_TCD 0x0000FFFF
+
+#define CX25840_IR_RXCLK_REG 0x208
+#define RXCLK_RCD 0x0000FFFF
+
+#define CX25840_IR_CDUTY_REG 0x20C
+#define CDUTY_CDC 0x0000000F
+
+#define CX25840_IR_STATS_REG 0x210
+#define STATS_RTO 0x00000001
+#define STATS_ROR 0x00000002
+#define STATS_RBY 0x00000004
+#define STATS_TBY 0x00000008
+#define STATS_RSR 0x00000010
+#define STATS_TSR 0x00000020
+
+#define CX25840_IR_IRQEN_REG 0x214
+#define IRQEN_RTE 0x00000001
+#define IRQEN_ROE 0x00000002
+#define IRQEN_RSE 0x00000010
+#define IRQEN_TSE 0x00000020
+#define IRQEN_MSK 0x00000033
+
+#define CX25840_IR_FILTR_REG 0x218
+#define FILTR_LPF 0x0000FFFF
+
+#define CX25840_IR_FIFO_REG 0x23C
+#define FIFO_RXTX 0x0000FFFF
+#define FIFO_RXTX_LVL 0x00010000
+#define FIFO_RXTX_RTO 0x0001FFFF
+#define FIFO_RX_NDV 0x00020000
+#define FIFO_RX_DEPTH 8
+#define FIFO_TX_DEPTH 8
+
+#define CX25840_VIDCLK_FREQ 108000000 /* 108 MHz, BT.656 */
+#define CX25840_IR_REFCLK_FREQ (CX25840_VIDCLK_FREQ / 2)
+
+#define CX25840_IR_RX_KFIFO_SIZE (512 * sizeof(u32))
+#define CX25840_IR_TX_KFIFO_SIZE (512 * sizeof(u32))
+
+struct cx25840_ir_state {
+ struct i2c_client *c;
+
+ struct v4l2_subdev_ir_parameters rx_params;
+ struct mutex rx_params_lock; /* protects Rx parameter settings cache */
+ atomic_t rxclk_divider;
+ atomic_t rx_invert;
+
+ struct kfifo rx_kfifo;
+ spinlock_t rx_kfifo_lock; /* protect Rx data kfifo */
+
+ struct v4l2_subdev_ir_parameters tx_params;
+ struct mutex tx_params_lock; /* protects Tx parameter settings cache */
+ atomic_t txclk_divider;
+};
+
+static inline struct cx25840_ir_state *to_ir_state(struct v4l2_subdev *sd)
+{
+ struct cx25840_state *state = to_state(sd);
+ return state ? state->ir_state : NULL;
+}
+
+
+/*
+ * Rx and Tx Clock Divider register computations
+ *
+ * Note the largest clock divider value of 0xffff corresponds to:
+ * (0xffff + 1) * 1000 / 108/2 MHz = 1,213,629.629... ns
+ * which fits in 21 bits, so we'll use unsigned int for time arguments.
+ */
+static inline u16 count_to_clock_divider(unsigned int d)
+{
+ if (d > RXCLK_RCD + 1)
+ d = RXCLK_RCD;
+ else if (d < 2)
+ d = 1;
+ else
+ d--;
+ return (u16) d;
+}
+
+static inline u16 ns_to_clock_divider(unsigned int ns)
+{
+ return count_to_clock_divider(
+ DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ / 1000000 * ns, 1000));
+}
+
+static inline unsigned int clock_divider_to_ns(unsigned int divider)
+{
+ /* Period of the Rx or Tx clock in ns */
+ return DIV_ROUND_CLOSEST((divider + 1) * 1000,
+ CX25840_IR_REFCLK_FREQ / 1000000);
+}
+
+static inline u16 carrier_freq_to_clock_divider(unsigned int freq)
+{
+ return count_to_clock_divider(
+ DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, freq * 16));
+}
+
+static inline unsigned int clock_divider_to_carrier_freq(unsigned int divider)
+{
+ return DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, (divider + 1) * 16);
+}
+
+static inline u16 freq_to_clock_divider(unsigned int freq,
+ unsigned int rollovers)
+{
+ return count_to_clock_divider(
+ DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, freq * rollovers));
+}
+
+static inline unsigned int clock_divider_to_freq(unsigned int divider,
+ unsigned int rollovers)
+{
+ return DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ,
+ (divider + 1) * rollovers);
+}
+
+/*
+ * Low Pass Filter register calculations
+ *
+ * Note the largest count value of 0xffff corresponds to:
+ * 0xffff * 1000 / 108/2 MHz = 1,213,611.11... ns
+ * which fits in 21 bits, so we'll use unsigned int for time arguments.
+ */
+static inline u16 count_to_lpf_count(unsigned int d)
+{
+ if (d > FILTR_LPF)
+ d = FILTR_LPF;
+ else if (d < 4)
+ d = 0;
+ return (u16) d;
+}
+
+static inline u16 ns_to_lpf_count(unsigned int ns)
+{
+ return count_to_lpf_count(
+ DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ / 1000000 * ns, 1000));
+}
+
+static inline unsigned int lpf_count_to_ns(unsigned int count)
+{
+ /* Duration of the Low Pass Filter rejection window in ns */
+ return DIV_ROUND_CLOSEST(count * 1000,
+ CX25840_IR_REFCLK_FREQ / 1000000);
+}
+
+static inline unsigned int lpf_count_to_us(unsigned int count)
+{
+ /* Duration of the Low Pass Filter rejection window in us */
+ return DIV_ROUND_CLOSEST(count, CX25840_IR_REFCLK_FREQ / 1000000);
+}
+
+/*
+ * FIFO register pulse width count compuations
+ */
+static u32 clock_divider_to_resolution(u16 divider)
+{
+ /*
+ * Resolution is the duration of 1 tick of the readable portion of
+ * of the pulse width counter as read from the FIFO. The two lsb's are
+ * not readable, hence the << 2. This function returns ns.
+ */
+ return DIV_ROUND_CLOSEST((1 << 2) * ((u32) divider + 1) * 1000,
+ CX25840_IR_REFCLK_FREQ / 1000000);
+}
+
+static u64 pulse_width_count_to_ns(u16 count, u16 divider)
+{
+ u64 n;
+ u32 rem;
+
+ /*
+ * The 2 lsb's of the pulse width timer count are not readable, hence
+ * the (count << 2) | 0x3
+ */
+ n = (((u64) count << 2) | 0x3) * (divider + 1) * 1000; /* millicycles */
+ rem = do_div(n, CX25840_IR_REFCLK_FREQ / 1000000); /* / MHz => ns */
+ if (rem >= CX25840_IR_REFCLK_FREQ / 1000000 / 2)
+ n++;
+ return n;
+}
+
+#if 0
+/* Keep as we will need this for Transmit functionality */
+static u16 ns_to_pulse_width_count(u32 ns, u16 divider)
+{
+ u64 n;
+ u32 d;
+ u32 rem;
+
+ /*
+ * The 2 lsb's of the pulse width timer count are not accessable, hence
+ * the (1 << 2)
+ */
+ n = ((u64) ns) * CX25840_IR_REFCLK_FREQ / 1000000; /* millicycles */
+ d = (1 << 2) * ((u32) divider + 1) * 1000; /* millicycles/count */
+ rem = do_div(n, d);
+ if (rem >= d / 2)
+ n++;
+
+ if (n > FIFO_RXTX)
+ n = FIFO_RXTX;
+ else if (n == 0)
+ n = 1;
+ return (u16) n;
+}
+
+#endif
+static unsigned int pulse_width_count_to_us(u16 count, u16 divider)
+{
+ u64 n;
+ u32 rem;
+
+ /*
+ * The 2 lsb's of the pulse width timer count are not readable, hence
+ * the (count << 2) | 0x3
+ */
+ n = (((u64) count << 2) | 0x3) * (divider + 1); /* cycles */
+ rem = do_div(n, CX25840_IR_REFCLK_FREQ / 1000000); /* / MHz => us */
+ if (rem >= CX25840_IR_REFCLK_FREQ / 1000000 / 2)
+ n++;
+ return (unsigned int) n;
+}
+
+/*
+ * Pulse Clocks computations: Combined Pulse Width Count & Rx Clock Counts
+ *
+ * The total pulse clock count is an 18 bit pulse width timer count as the most
+ * significant part and (up to) 16 bit clock divider count as a modulus.
+ * When the Rx clock divider ticks down to 0, it increments the 18 bit pulse
+ * width timer count's least significant bit.
+ */
+static u64 ns_to_pulse_clocks(u32 ns)
+{
+ u64 clocks;
+ u32 rem;
+ clocks = CX25840_IR_REFCLK_FREQ / 1000000 * (u64) ns; /* millicycles */
+ rem = do_div(clocks, 1000); /* /1000 = cycles */
+ if (rem >= 1000 / 2)
+ clocks++;
+ return clocks;
+}
+
+static u16 pulse_clocks_to_clock_divider(u64 count)
+{
+ u32 rem;
+
+ rem = do_div(count, (FIFO_RXTX << 2) | 0x3);
+
+ /* net result needs to be rounded down and decremented by 1 */
+ if (count > RXCLK_RCD + 1)
+ count = RXCLK_RCD;
+ else if (count < 2)
+ count = 1;
+ else
+ count--;
+ return (u16) count;
+}
+
+/*
+ * IR Control Register helpers
+ */
+enum tx_fifo_watermark {
+ TX_FIFO_HALF_EMPTY = 0,
+ TX_FIFO_EMPTY = CNTRL_TIC,
+};
+
+enum rx_fifo_watermark {
+ RX_FIFO_HALF_FULL = 0,
+ RX_FIFO_NOT_EMPTY = CNTRL_RIC,
+};
+
+static inline void control_tx_irq_watermark(struct i2c_client *c,
+ enum tx_fifo_watermark level)
+{
+ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_TIC, level);
+}
+
+static inline void control_rx_irq_watermark(struct i2c_client *c,
+ enum rx_fifo_watermark level)
+{
+ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_RIC, level);
+}
+
+static inline void control_tx_enable(struct i2c_client *c, bool enable)
+{
+ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~(CNTRL_TXE | CNTRL_TFE),
+ enable ? (CNTRL_TXE | CNTRL_TFE) : 0);
+}
+
+static inline void control_rx_enable(struct i2c_client *c, bool enable)
+{
+ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~(CNTRL_RXE | CNTRL_RFE),
+ enable ? (CNTRL_RXE | CNTRL_RFE) : 0);
+}
+
+static inline void control_tx_modulation_enable(struct i2c_client *c,
+ bool enable)
+{
+ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_MOD,
+ enable ? CNTRL_MOD : 0);
+}
+
+static inline void control_rx_demodulation_enable(struct i2c_client *c,
+ bool enable)
+{
+ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_DMD,
+ enable ? CNTRL_DMD : 0);
+}
+
+static inline void control_rx_s_edge_detection(struct i2c_client *c,
+ u32 edge_types)
+{
+ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_EDG_BOTH,
+ edge_types & CNTRL_EDG_BOTH);
+}
+
+static void control_rx_s_carrier_window(struct i2c_client *c,
+ unsigned int carrier,
+ unsigned int *carrier_range_low,
+ unsigned int *carrier_range_high)
+{
+ u32 v;
+ unsigned int c16 = carrier * 16;
+
+ if (*carrier_range_low < DIV_ROUND_CLOSEST(c16, 16 + 3)) {
+ v = CNTRL_WIN_3_4;
+ *carrier_range_low = DIV_ROUND_CLOSEST(c16, 16 + 4);
+ } else {
+ v = CNTRL_WIN_3_3;
+ *carrier_range_low = DIV_ROUND_CLOSEST(c16, 16 + 3);
+ }
+
+ if (*carrier_range_high > DIV_ROUND_CLOSEST(c16, 16 - 3)) {
+ v |= CNTRL_WIN_4_3;
+ *carrier_range_high = DIV_ROUND_CLOSEST(c16, 16 - 4);
+ } else {
+ v |= CNTRL_WIN_3_3;
+ *carrier_range_high = DIV_ROUND_CLOSEST(c16, 16 - 3);
+ }
+ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_WIN, v);
+}
+
+static inline void control_tx_polarity_invert(struct i2c_client *c,
+ bool invert)
+{
+ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_CPL,
+ invert ? CNTRL_CPL : 0);
+}
+
+/*
+ * IR Rx & Tx Clock Register helpers
+ */
+static unsigned int txclk_tx_s_carrier(struct i2c_client *c,
+ unsigned int freq,
+ u16 *divider)
+{
+ *divider = carrier_freq_to_clock_divider(freq);
+ cx25840_write4(c, CX25840_IR_TXCLK_REG, *divider);
+ return clock_divider_to_carrier_freq(*divider);
+}
+
+static unsigned int rxclk_rx_s_carrier(struct i2c_client *c,
+ unsigned int freq,
+ u16 *divider)
+{
+ *divider = carrier_freq_to_clock_divider(freq);
+ cx25840_write4(c, CX25840_IR_RXCLK_REG, *divider);
+ return clock_divider_to_carrier_freq(*divider);
+}
+
+static u32 txclk_tx_s_max_pulse_width(struct i2c_client *c, u32 ns,
+ u16 *divider)
+{
+ u64 pulse_clocks;
+
+ if (ns > V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS)
+ ns = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS;
+ pulse_clocks = ns_to_pulse_clocks(ns);
+ *divider = pulse_clocks_to_clock_divider(pulse_clocks);
+ cx25840_write4(c, CX25840_IR_TXCLK_REG, *divider);
+ return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider);
+}
+
+static u32 rxclk_rx_s_max_pulse_width(struct i2c_client *c, u32 ns,
+ u16 *divider)
+{
+ u64 pulse_clocks;
+
+ if (ns > V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS)
+ ns = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS;
+ pulse_clocks = ns_to_pulse_clocks(ns);
+ *divider = pulse_clocks_to_clock_divider(pulse_clocks);
+ cx25840_write4(c, CX25840_IR_RXCLK_REG, *divider);
+ return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider);
+}
+
+/*
+ * IR Tx Carrier Duty Cycle register helpers
+ */
+static unsigned int cduty_tx_s_duty_cycle(struct i2c_client *c,
+ unsigned int duty_cycle)
+{
+ u32 n;
+ n = DIV_ROUND_CLOSEST(duty_cycle * 100, 625); /* 16ths of 100% */
+ if (n != 0)
+ n--;
+ if (n > 15)
+ n = 15;
+ cx25840_write4(c, CX25840_IR_CDUTY_REG, n);
+ return DIV_ROUND_CLOSEST((n + 1) * 100, 16);
+}
+
+/*
+ * IR Filter Register helpers
+ */
+static u32 filter_rx_s_min_width(struct i2c_client *c, u32 min_width_ns)
+{
+ u32 count = ns_to_lpf_count(min_width_ns);
+ cx25840_write4(c, CX25840_IR_FILTR_REG, count);
+ return lpf_count_to_ns(count);
+}
+
+/*
+ * IR IRQ Enable Register helpers
+ */
+static inline void irqenable_rx(struct v4l2_subdev *sd, u32 mask)
+{
+ struct cx25840_state *state = to_state(sd);
+
+ if (is_cx23885(state) || is_cx23887(state))
+ mask ^= IRQEN_MSK;
+ mask &= (IRQEN_RTE | IRQEN_ROE | IRQEN_RSE);
+ cx25840_and_or4(state->c, CX25840_IR_IRQEN_REG,
+ ~(IRQEN_RTE | IRQEN_ROE | IRQEN_RSE), mask);
+}
+
+static inline void irqenable_tx(struct v4l2_subdev *sd, u32 mask)
+{
+ struct cx25840_state *state = to_state(sd);
+
+ if (is_cx23885(state) || is_cx23887(state))
+ mask ^= IRQEN_MSK;
+ mask &= IRQEN_TSE;
+ cx25840_and_or4(state->c, CX25840_IR_IRQEN_REG, ~IRQEN_TSE, mask);
+}
+
+/*
+ * V4L2 Subdevice IR Ops
+ */
+int cx25840_ir_irq_handler(struct v4l2_subdev *sd, u32 status, bool *handled)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct cx25840_ir_state *ir_state = to_ir_state(sd);
+ struct i2c_client *c = NULL;
+ unsigned long flags;
+
+ u32 rx_data[FIFO_RX_DEPTH];
+ int i, j, k;
+ u32 events, v;
+ int tsr, rsr, rto, ror, tse, rse, rte, roe, kror;
+ u32 cntrl, irqen, stats;
+
+ *handled = false;
+ if (ir_state == NULL)
+ return -ENODEV;
+
+ c = ir_state->c;
+
+ /* Only support the IR controller for the CX2388[57] AV Core for now */
+ if (!(is_cx23885(state) || is_cx23887(state)))
+ return -ENODEV;
+
+ cntrl = cx25840_read4(c, CX25840_IR_CNTRL_REG);
+ irqen = cx25840_read4(c, CX25840_IR_IRQEN_REG);
+ if (is_cx23885(state) || is_cx23887(state))
+ irqen ^= IRQEN_MSK;
+ stats = cx25840_read4(c, CX25840_IR_STATS_REG);
+
+ tsr = stats & STATS_TSR; /* Tx FIFO Service Request */
+ rsr = stats & STATS_RSR; /* Rx FIFO Service Request */
+ rto = stats & STATS_RTO; /* Rx Pulse Width Timer Time Out */
+ ror = stats & STATS_ROR; /* Rx FIFO Over Run */
+
+ tse = irqen & IRQEN_TSE; /* Tx FIFO Service Request IRQ Enable */
+ rse = irqen & IRQEN_RSE; /* Rx FIFO Service Reuqest IRQ Enable */
+ rte = irqen & IRQEN_RTE; /* Rx Pulse Width Timer Time Out IRQ Enable */
+ roe = irqen & IRQEN_ROE; /* Rx FIFO Over Run IRQ Enable */
+
+ v4l2_dbg(2, ir_debug, sd, "IR IRQ Status: %s %s %s %s %s %s\n",
+ tsr ? "tsr" : " ", rsr ? "rsr" : " ",
+ rto ? "rto" : " ", ror ? "ror" : " ",
+ stats & STATS_TBY ? "tby" : " ",
+ stats & STATS_RBY ? "rby" : " ");
+
+ v4l2_dbg(2, ir_debug, sd, "IR IRQ Enables: %s %s %s %s\n",
+ tse ? "tse" : " ", rse ? "rse" : " ",
+ rte ? "rte" : " ", roe ? "roe" : " ");
+
+ /*
+ * Transmitter interrupt service
+ */
+ if (tse && tsr) {
+ /*
+ * TODO:
+ * Check the watermark threshold setting
+ * Pull FIFO_TX_DEPTH or FIFO_TX_DEPTH/2 entries from tx_kfifo
+ * Push the data to the hardware FIFO.
+ * If there was nothing more to send in the tx_kfifo, disable
+ * the TSR IRQ and notify the v4l2_device.
+ * If there was something in the tx_kfifo, check the tx_kfifo
+ * level and notify the v4l2_device, if it is low.
+ */
+ /* For now, inhibit TSR interrupt until Tx is implemented */
+ irqenable_tx(sd, 0);
+ events = V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ;
+ v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_TX_NOTIFY, &events);
+ *handled = true;
+ }
+
+ /*
+ * Receiver interrupt service
+ */
+ kror = 0;
+ if ((rse && rsr) || (rte && rto)) {
+ /*
+ * Receive data on RSR to clear the STATS_RSR.
+ * Receive data on RTO, since we may not have yet hit the RSR
+ * watermark when we receive the RTO.
+ */
+ for (i = 0, v = FIFO_RX_NDV;
+ (v & FIFO_RX_NDV) && !kror; i = 0) {
+ for (j = 0;
+ (v & FIFO_RX_NDV) && j < FIFO_RX_DEPTH; j++) {
+ v = cx25840_read4(c, CX25840_IR_FIFO_REG);
+ rx_data[i++] = v & ~FIFO_RX_NDV;
+ }
+ if (i == 0)
+ break;
+ j = i * sizeof(u32);
+ k = kfifo_in_locked(&ir_state->rx_kfifo,
+ (unsigned char *) rx_data, j,
+ &ir_state->rx_kfifo_lock);
+ if (k != j)
+ kror++; /* rx_kfifo over run */
+ }
+ *handled = true;
+ }
+
+ events = 0;
+ v = 0;
+ if (kror) {
+ events |= V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN;
+ v4l2_err(sd, "IR receiver software FIFO overrun\n");
+ }
+ if (roe && ror) {
+ /*
+ * The RX FIFO Enable (CNTRL_RFE) must be toggled to clear
+ * the Rx FIFO Over Run status (STATS_ROR)
+ */
+ v |= CNTRL_RFE;
+ events |= V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN;
+ v4l2_err(sd, "IR receiver hardware FIFO overrun\n");
+ }
+ if (rte && rto) {
+ /*
+ * The IR Receiver Enable (CNTRL_RXE) must be toggled to clear
+ * the Rx Pulse Width Timer Time Out (STATS_RTO)
+ */
+ v |= CNTRL_RXE;
+ events |= V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED;
+ }
+ if (v) {
+ /* Clear STATS_ROR & STATS_RTO as needed by reseting hardware */
+ cx25840_write4(c, CX25840_IR_CNTRL_REG, cntrl & ~v);
+ cx25840_write4(c, CX25840_IR_CNTRL_REG, cntrl);
+ *handled = true;
+ }
+ spin_lock_irqsave(&ir_state->rx_kfifo_lock, flags);
+ if (kfifo_len(&ir_state->rx_kfifo) >= CX25840_IR_RX_KFIFO_SIZE / 2)
+ events |= V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ;
+ spin_unlock_irqrestore(&ir_state->rx_kfifo_lock, flags);
+
+ if (events)
+ v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_RX_NOTIFY, &events);
+ return 0;
+}
+
+/* Receiver */
+static int cx25840_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count,
+ ssize_t *num)
+{
+ struct cx25840_ir_state *ir_state = to_ir_state(sd);
+ bool invert;
+ u16 divider;
+ unsigned int i, n;
+ u32 *p;
+ u32 u, v;
+
+ if (ir_state == NULL)
+ return -ENODEV;
+
+ invert = (bool) atomic_read(&ir_state->rx_invert);
+ divider = (u16) atomic_read(&ir_state->rxclk_divider);
+
+ n = count / sizeof(u32) * sizeof(u32);
+ if (n == 0) {
+ *num = 0;
+ return 0;
+ }
+
+ n = kfifo_out_locked(&ir_state->rx_kfifo, buf, n,
+ &ir_state->rx_kfifo_lock);
+
+ n /= sizeof(u32);
+ *num = n * sizeof(u32);
+
+ for (p = (u32 *) buf, i = 0; i < n; p++, i++) {
+ if ((*p & FIFO_RXTX_RTO) == FIFO_RXTX_RTO) {
+ *p = V4L2_SUBDEV_IR_PULSE_RX_SEQ_END;
+ v4l2_dbg(2, ir_debug, sd, "rx read: end of rx\n");
+ continue;
+ }
+
+ u = (*p & FIFO_RXTX_LVL) ? V4L2_SUBDEV_IR_PULSE_LEVEL_MASK : 0;
+ if (invert)
+ u = u ? 0 : V4L2_SUBDEV_IR_PULSE_LEVEL_MASK;
+
+ v = (u32) pulse_width_count_to_ns((u16) (*p & FIFO_RXTX),
+ divider);
+ if (v >= V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS)
+ v = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS - 1;
+
+ *p = u | v;
+
+ v4l2_dbg(2, ir_debug, sd, "rx read: %10u ns %s\n",
+ v, u ? "mark" : "space");
+ }
+ return 0;
+}
+
+static int cx25840_ir_rx_g_parameters(struct v4l2_subdev *sd,
+ struct v4l2_subdev_ir_parameters *p)
+{
+ struct cx25840_ir_state *ir_state = to_ir_state(sd);
+
+ if (ir_state == NULL)
+ return -ENODEV;
+
+ mutex_lock(&ir_state->rx_params_lock);
+ memcpy(p, &ir_state->rx_params,
+ sizeof(struct v4l2_subdev_ir_parameters));
+ mutex_unlock(&ir_state->rx_params_lock);
+ return 0;
+}
+
+static int cx25840_ir_rx_shutdown(struct v4l2_subdev *sd)
+{
+ struct cx25840_ir_state *ir_state = to_ir_state(sd);
+ struct i2c_client *c;
+
+ if (ir_state == NULL)
+ return -ENODEV;
+
+ c = ir_state->c;
+ mutex_lock(&ir_state->rx_params_lock);
+
+ /* Disable or slow down all IR Rx circuits and counters */
+ irqenable_rx(sd, 0);
+ control_rx_enable(c, false);
+ control_rx_demodulation_enable(c, false);
+ control_rx_s_edge_detection(c, CNTRL_EDG_NONE);
+ filter_rx_s_min_width(c, 0);
+ cx25840_write4(c, CX25840_IR_RXCLK_REG, RXCLK_RCD);
+
+ ir_state->rx_params.shutdown = true;
+
+ mutex_unlock(&ir_state->rx_params_lock);
+ return 0;
+}
+
+static int cx25840_ir_rx_s_parameters(struct v4l2_subdev *sd,
+ struct v4l2_subdev_ir_parameters *p)
+{
+ struct cx25840_ir_state *ir_state = to_ir_state(sd);
+ struct i2c_client *c;
+ struct v4l2_subdev_ir_parameters *o;
+ u16 rxclk_divider;
+
+ if (ir_state == NULL)
+ return -ENODEV;
+
+ if (p->shutdown)
+ return cx25840_ir_rx_shutdown(sd);
+
+ if (p->mode != V4L2_SUBDEV_IR_MODE_PULSE_WIDTH)
+ return -ENOSYS;
+
+ c = ir_state->c;
+ o = &ir_state->rx_params;
+
+ mutex_lock(&ir_state->rx_params_lock);
+
+ o->shutdown = p->shutdown;
+
+ p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
+ o->mode = p->mode;
+
+ p->bytes_per_data_element = sizeof(u32);
+ o->bytes_per_data_element = p->bytes_per_data_element;
+
+ /* Before we tweak the hardware, we have to disable the receiver */
+ irqenable_rx(sd, 0);
+ control_rx_enable(c, false);
+
+ control_rx_demodulation_enable(c, p->modulation);
+ o->modulation = p->modulation;
+
+ if (p->modulation) {
+ p->carrier_freq = rxclk_rx_s_carrier(c, p->carrier_freq,
+ &rxclk_divider);
+
+ o->carrier_freq = p->carrier_freq;
+
+ p->duty_cycle = 50;
+ o->duty_cycle = p->duty_cycle;
+
+ control_rx_s_carrier_window(c, p->carrier_freq,
+ &p->carrier_range_lower,
+ &p->carrier_range_upper);
+ o->carrier_range_lower = p->carrier_range_lower;
+ o->carrier_range_upper = p->carrier_range_upper;
+ } else {
+ p->max_pulse_width =
+ rxclk_rx_s_max_pulse_width(c, p->max_pulse_width,
+ &rxclk_divider);
+ o->max_pulse_width = p->max_pulse_width;
+ }
+ atomic_set(&ir_state->rxclk_divider, rxclk_divider);
+
+ p->noise_filter_min_width =
+ filter_rx_s_min_width(c, p->noise_filter_min_width);
+ o->noise_filter_min_width = p->noise_filter_min_width;
+
+ p->resolution = clock_divider_to_resolution(rxclk_divider);
+ o->resolution = p->resolution;
+
+ /* FIXME - make this dependent on resolution for better performance */
+ control_rx_irq_watermark(c, RX_FIFO_HALF_FULL);
+
+ control_rx_s_edge_detection(c, CNTRL_EDG_BOTH);
+
+ o->invert_level = p->invert_level;
+ atomic_set(&ir_state->rx_invert, p->invert_level);
+
+ o->interrupt_enable = p->interrupt_enable;
+ o->enable = p->enable;
+ if (p->enable) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&ir_state->rx_kfifo_lock, flags);
+ kfifo_reset(&ir_state->rx_kfifo);
+ spin_unlock_irqrestore(&ir_state->rx_kfifo_lock, flags);
+ if (p->interrupt_enable)
+ irqenable_rx(sd, IRQEN_RSE | IRQEN_RTE | IRQEN_ROE);
+ control_rx_enable(c, p->enable);
+ }
+
+ mutex_unlock(&ir_state->rx_params_lock);
+ return 0;
+}
+
+/* Transmitter */
+static int cx25840_ir_tx_write(struct v4l2_subdev *sd, u8 *buf, size_t count,
+ ssize_t *num)
+{
+ struct cx25840_ir_state *ir_state = to_ir_state(sd);
+ struct i2c_client *c;
+
+ if (ir_state == NULL)
+ return -ENODEV;
+
+ c = ir_state->c;
+#if 0
+ /*
+ * FIXME - the code below is an incomplete and untested sketch of what
+ * may need to be done. The critical part is to get 4 (or 8) pulses
+ * from the tx_kfifo, or converted from ns to the proper units from the
+ * input, and push them off to the hardware Tx FIFO right away, if the
+ * HW TX fifo needs service. The rest can be pushed to the tx_kfifo in
+ * a less critical timeframe. Also watch out for overruning the
+ * tx_kfifo - don't let it happen and let the caller know not all his
+ * pulses were written.
+ */
+ u32 *ns_pulse = (u32 *) buf;
+ unsigned int n;
+ u32 fifo_pulse[FIFO_TX_DEPTH];
+ u32 mark;
+
+ /* Compute how much we can fit in the tx kfifo */
+ n = CX25840_IR_TX_KFIFO_SIZE - kfifo_len(ir_state->tx_kfifo);
+ n = min(n, (unsigned int) count);
+ n /= sizeof(u32);
+
+ /* FIXME - turn on Tx Fifo service interrupt
+ * check hardware fifo level, and other stuff
+ */
+ for (i = 0; i < n; ) {
+ for (j = 0; j < FIFO_TX_DEPTH / 2 && i < n; j++) {
+ mark = ns_pulse[i] & V4L2_SUBDEV_IR_PULSE_LEVEL_MASK;
+ fifo_pulse[j] = ns_to_pulse_width_count(
+ ns_pulse[i] &
+ ~V4L2_SUBDEV_IR_PULSE_LEVEL_MASK,
+ ir_state->txclk_divider);
+ if (mark)
+ fifo_pulse[j] &= FIFO_RXTX_LVL;
+ i++;
+ }
+ kfifo_put(ir_state->tx_kfifo, (u8 *) fifo_pulse,
+ j * sizeof(u32));
+ }
+ *num = n * sizeof(u32);
+#else
+ /* For now enable the Tx FIFO Service interrupt & pretend we did work */
+ irqenable_tx(sd, IRQEN_TSE);
+ *num = count;
+#endif
+ return 0;
+}
+
+static int cx25840_ir_tx_g_parameters(struct v4l2_subdev *sd,
+ struct v4l2_subdev_ir_parameters *p)
+{
+ struct cx25840_ir_state *ir_state = to_ir_state(sd);
+
+ if (ir_state == NULL)
+ return -ENODEV;
+
+ mutex_lock(&ir_state->tx_params_lock);
+ memcpy(p, &ir_state->tx_params,
+ sizeof(struct v4l2_subdev_ir_parameters));
+ mutex_unlock(&ir_state->tx_params_lock);
+ return 0;
+}
+
+static int cx25840_ir_tx_shutdown(struct v4l2_subdev *sd)
+{
+ struct cx25840_ir_state *ir_state = to_ir_state(sd);
+ struct i2c_client *c;
+
+ if (ir_state == NULL)
+ return -ENODEV;
+
+ c = ir_state->c;
+ mutex_lock(&ir_state->tx_params_lock);
+
+ /* Disable or slow down all IR Tx circuits and counters */
+ irqenable_tx(sd, 0);
+ control_tx_enable(c, false);
+ control_tx_modulation_enable(c, false);
+ cx25840_write4(c, CX25840_IR_TXCLK_REG, TXCLK_TCD);
+
+ ir_state->tx_params.shutdown = true;
+
+ mutex_unlock(&ir_state->tx_params_lock);
+ return 0;
+}
+
+static int cx25840_ir_tx_s_parameters(struct v4l2_subdev *sd,
+ struct v4l2_subdev_ir_parameters *p)
+{
+ struct cx25840_ir_state *ir_state = to_ir_state(sd);
+ struct i2c_client *c;
+ struct v4l2_subdev_ir_parameters *o;
+ u16 txclk_divider;
+
+ if (ir_state == NULL)
+ return -ENODEV;
+
+ if (p->shutdown)
+ return cx25840_ir_tx_shutdown(sd);
+
+ if (p->mode != V4L2_SUBDEV_IR_MODE_PULSE_WIDTH)
+ return -ENOSYS;
+
+ c = ir_state->c;
+ o = &ir_state->tx_params;
+ mutex_lock(&ir_state->tx_params_lock);
+
+ o->shutdown = p->shutdown;
+
+ p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
+ o->mode = p->mode;
+
+ p->bytes_per_data_element = sizeof(u32);
+ o->bytes_per_data_element = p->bytes_per_data_element;
+
+ /* Before we tweak the hardware, we have to disable the transmitter */
+ irqenable_tx(sd, 0);
+ control_tx_enable(c, false);
+
+ control_tx_modulation_enable(c, p->modulation);
+ o->modulation = p->modulation;
+
+ if (p->modulation) {
+ p->carrier_freq = txclk_tx_s_carrier(c, p->carrier_freq,
+ &txclk_divider);
+ o->carrier_freq = p->carrier_freq;
+
+ p->duty_cycle = cduty_tx_s_duty_cycle(c, p->duty_cycle);
+ o->duty_cycle = p->duty_cycle;
+ } else {
+ p->max_pulse_width =
+ txclk_tx_s_max_pulse_width(c, p->max_pulse_width,
+ &txclk_divider);
+ o->max_pulse_width = p->max_pulse_width;
+ }
+ atomic_set(&ir_state->txclk_divider, txclk_divider);
+
+ p->resolution = clock_divider_to_resolution(txclk_divider);
+ o->resolution = p->resolution;
+
+ /* FIXME - make this dependent on resolution for better performance */
+ control_tx_irq_watermark(c, TX_FIFO_HALF_EMPTY);
+
+ control_tx_polarity_invert(c, p->invert_carrier_sense);
+ o->invert_carrier_sense = p->invert_carrier_sense;
+
+ /*
+ * FIXME: we don't have hardware help for IO pin level inversion
+ * here like we have on the CX23888.
+ * Act on this with some mix of logical inversion of data levels,
+ * carrier polarity, and carrier duty cycle.
+ */
+ o->invert_level = p->invert_level;
+
+ o->interrupt_enable = p->interrupt_enable;
+ o->enable = p->enable;
+ if (p->enable) {
+ /* reset tx_fifo here */
+ if (p->interrupt_enable)
+ irqenable_tx(sd, IRQEN_TSE);
+ control_tx_enable(c, p->enable);
+ }
+
+ mutex_unlock(&ir_state->tx_params_lock);
+ return 0;
+}
+
+
+/*
+ * V4L2 Subdevice Core Ops support
+ */
+int cx25840_ir_log_status(struct v4l2_subdev *sd)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *c = state->c;
+ char *s;
+ int i, j;
+ u32 cntrl, txclk, rxclk, cduty, stats, irqen, filtr;
+
+ /* The CX23888 chip doesn't have an IR controller on the A/V core */
+ if (is_cx23888(state))
+ return 0;
+
+ cntrl = cx25840_read4(c, CX25840_IR_CNTRL_REG);
+ txclk = cx25840_read4(c, CX25840_IR_TXCLK_REG) & TXCLK_TCD;
+ rxclk = cx25840_read4(c, CX25840_IR_RXCLK_REG) & RXCLK_RCD;
+ cduty = cx25840_read4(c, CX25840_IR_CDUTY_REG) & CDUTY_CDC;
+ stats = cx25840_read4(c, CX25840_IR_STATS_REG);
+ irqen = cx25840_read4(c, CX25840_IR_IRQEN_REG);
+ if (is_cx23885(state) || is_cx23887(state))
+ irqen ^= IRQEN_MSK;
+ filtr = cx25840_read4(c, CX25840_IR_FILTR_REG) & FILTR_LPF;
+
+ v4l2_info(sd, "IR Receiver:\n");
+ v4l2_info(sd, "\tEnabled: %s\n",
+ cntrl & CNTRL_RXE ? "yes" : "no");
+ v4l2_info(sd, "\tDemodulation from a carrier: %s\n",
+ cntrl & CNTRL_DMD ? "enabled" : "disabled");
+ v4l2_info(sd, "\tFIFO: %s\n",
+ cntrl & CNTRL_RFE ? "enabled" : "disabled");
+ switch (cntrl & CNTRL_EDG) {
+ case CNTRL_EDG_NONE:
+ s = "disabled";
+ break;
+ case CNTRL_EDG_FALL:
+ s = "falling edge";
+ break;
+ case CNTRL_EDG_RISE:
+ s = "rising edge";
+ break;
+ case CNTRL_EDG_BOTH:
+ s = "rising & falling edges";
+ break;
+ default:
+ s = "??? edge";
+ break;
+ }
+ v4l2_info(sd, "\tPulse timers' start/stop trigger: %s\n", s);
+ v4l2_info(sd, "\tFIFO data on pulse timer overflow: %s\n",
+ cntrl & CNTRL_R ? "not loaded" : "overflow marker");
+ v4l2_info(sd, "\tFIFO interrupt watermark: %s\n",
+ cntrl & CNTRL_RIC ? "not empty" : "half full or greater");
+ v4l2_info(sd, "\tLoopback mode: %s\n",
+ cntrl & CNTRL_LBM ? "loopback active" : "normal receive");
+ if (cntrl & CNTRL_DMD) {
+ v4l2_info(sd, "\tExpected carrier (16 clocks): %u Hz\n",
+ clock_divider_to_carrier_freq(rxclk));
+ switch (cntrl & CNTRL_WIN) {
+ case CNTRL_WIN_3_3:
+ i = 3;
+ j = 3;
+ break;
+ case CNTRL_WIN_4_3:
+ i = 4;
+ j = 3;
+ break;
+ case CNTRL_WIN_3_4:
+ i = 3;
+ j = 4;
+ break;
+ case CNTRL_WIN_4_4:
+ i = 4;
+ j = 4;
+ break;
+ default:
+ i = 0;
+ j = 0;
+ break;
+ }
+ v4l2_info(sd, "\tNext carrier edge window: 16 clocks "
+ "-%1d/+%1d, %u to %u Hz\n", i, j,
+ clock_divider_to_freq(rxclk, 16 + j),
+ clock_divider_to_freq(rxclk, 16 - i));
+ } else {
+ v4l2_info(sd, "\tMax measurable pulse width: %u us, "
+ "%llu ns\n",
+ pulse_width_count_to_us(FIFO_RXTX, rxclk),
+ pulse_width_count_to_ns(FIFO_RXTX, rxclk));
+ }
+ v4l2_info(sd, "\tLow pass filter: %s\n",
+ filtr ? "enabled" : "disabled");
+ if (filtr)
+ v4l2_info(sd, "\tMin acceptable pulse width (LPF): %u us, "
+ "%u ns\n",
+ lpf_count_to_us(filtr),
+ lpf_count_to_ns(filtr));
+ v4l2_info(sd, "\tPulse width timer timed-out: %s\n",
+ stats & STATS_RTO ? "yes" : "no");
+ v4l2_info(sd, "\tPulse width timer time-out intr: %s\n",
+ irqen & IRQEN_RTE ? "enabled" : "disabled");
+ v4l2_info(sd, "\tFIFO overrun: %s\n",
+ stats & STATS_ROR ? "yes" : "no");
+ v4l2_info(sd, "\tFIFO overrun interrupt: %s\n",
+ irqen & IRQEN_ROE ? "enabled" : "disabled");
+ v4l2_info(sd, "\tBusy: %s\n",
+ stats & STATS_RBY ? "yes" : "no");
+ v4l2_info(sd, "\tFIFO service requested: %s\n",
+ stats & STATS_RSR ? "yes" : "no");
+ v4l2_info(sd, "\tFIFO service request interrupt: %s\n",
+ irqen & IRQEN_RSE ? "enabled" : "disabled");
+
+ v4l2_info(sd, "IR Transmitter:\n");
+ v4l2_info(sd, "\tEnabled: %s\n",
+ cntrl & CNTRL_TXE ? "yes" : "no");
+ v4l2_info(sd, "\tModulation onto a carrier: %s\n",
+ cntrl & CNTRL_MOD ? "enabled" : "disabled");
+ v4l2_info(sd, "\tFIFO: %s\n",
+ cntrl & CNTRL_TFE ? "enabled" : "disabled");
+ v4l2_info(sd, "\tFIFO interrupt watermark: %s\n",
+ cntrl & CNTRL_TIC ? "not empty" : "half full or less");
+ v4l2_info(sd, "\tCarrier polarity: %s\n",
+ cntrl & CNTRL_CPL ? "space:burst mark:noburst"
+ : "space:noburst mark:burst");
+ if (cntrl & CNTRL_MOD) {
+ v4l2_info(sd, "\tCarrier (16 clocks): %u Hz\n",
+ clock_divider_to_carrier_freq(txclk));
+ v4l2_info(sd, "\tCarrier duty cycle: %2u/16\n",
+ cduty + 1);
+ } else {
+ v4l2_info(sd, "\tMax pulse width: %u us, "
+ "%llu ns\n",
+ pulse_width_count_to_us(FIFO_RXTX, txclk),
+ pulse_width_count_to_ns(FIFO_RXTX, txclk));
+ }
+ v4l2_info(sd, "\tBusy: %s\n",
+ stats & STATS_TBY ? "yes" : "no");
+ v4l2_info(sd, "\tFIFO service requested: %s\n",
+ stats & STATS_TSR ? "yes" : "no");
+ v4l2_info(sd, "\tFIFO service request interrupt: %s\n",
+ irqen & IRQEN_TSE ? "enabled" : "disabled");
+
+ return 0;
+}
+
+
+const struct v4l2_subdev_ir_ops cx25840_ir_ops = {
+ .rx_read = cx25840_ir_rx_read,
+ .rx_g_parameters = cx25840_ir_rx_g_parameters,
+ .rx_s_parameters = cx25840_ir_rx_s_parameters,
+
+ .tx_write = cx25840_ir_tx_write,
+ .tx_g_parameters = cx25840_ir_tx_g_parameters,
+ .tx_s_parameters = cx25840_ir_tx_s_parameters,
+};
+
+
+static const struct v4l2_subdev_ir_parameters default_rx_params = {
+ .bytes_per_data_element = sizeof(u32),
+ .mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH,
+
+ .enable = false,
+ .interrupt_enable = false,
+ .shutdown = true,
+
+ .modulation = true,
+ .carrier_freq = 36000, /* 36 kHz - RC-5, and RC-6 carrier */
+
+ /* RC-5: 666,667 ns = 1/36 kHz * 32 cycles * 1 mark * 0.75 */
+ /* RC-6: 333,333 ns = 1/36 kHz * 16 cycles * 1 mark * 0.75 */
+ .noise_filter_min_width = 333333, /* ns */
+ .carrier_range_lower = 35000,
+ .carrier_range_upper = 37000,
+ .invert_level = false,
+};
+
+static const struct v4l2_subdev_ir_parameters default_tx_params = {
+ .bytes_per_data_element = sizeof(u32),
+ .mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH,
+
+ .enable = false,
+ .interrupt_enable = false,
+ .shutdown = true,
+
+ .modulation = true,
+ .carrier_freq = 36000, /* 36 kHz - RC-5 carrier */
+ .duty_cycle = 25, /* 25 % - RC-5 carrier */
+ .invert_level = false,
+ .invert_carrier_sense = false,
+};
+
+int cx25840_ir_probe(struct v4l2_subdev *sd)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct cx25840_ir_state *ir_state;
+ struct v4l2_subdev_ir_parameters default_params;
+
+ /* Only init the IR controller for the CX2388[57] AV Core for now */
+ if (!(is_cx23885(state) || is_cx23887(state)))
+ return 0;
+
+ ir_state = kzalloc(sizeof(struct cx25840_ir_state), GFP_KERNEL);
+ if (ir_state == NULL)
+ return -ENOMEM;
+
+ spin_lock_init(&ir_state->rx_kfifo_lock);
+ if (kfifo_alloc(&ir_state->rx_kfifo,
+ CX25840_IR_RX_KFIFO_SIZE, GFP_KERNEL)) {
+ kfree(ir_state);
+ return -ENOMEM;
+ }
+
+ ir_state->c = state->c;
+ state->ir_state = ir_state;
+
+ /* Ensure no interrupts arrive yet */
+ if (is_cx23885(state) || is_cx23887(state))
+ cx25840_write4(ir_state->c, CX25840_IR_IRQEN_REG, IRQEN_MSK);
+ else
+ cx25840_write4(ir_state->c, CX25840_IR_IRQEN_REG, 0);
+
+ mutex_init(&ir_state->rx_params_lock);
+ memcpy(&default_params, &default_rx_params,
+ sizeof(struct v4l2_subdev_ir_parameters));
+ v4l2_subdev_call(sd, ir, rx_s_parameters, &default_params);
+
+ mutex_init(&ir_state->tx_params_lock);
+ memcpy(&default_params, &default_tx_params,
+ sizeof(struct v4l2_subdev_ir_parameters));
+ v4l2_subdev_call(sd, ir, tx_s_parameters, &default_params);
+
+ return 0;
+}
+
+int cx25840_ir_remove(struct v4l2_subdev *sd)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct cx25840_ir_state *ir_state = to_ir_state(sd);
+
+ if (ir_state == NULL)
+ return -ENODEV;
+
+ cx25840_ir_rx_shutdown(sd);
+ cx25840_ir_tx_shutdown(sd);
+
+ kfifo_free(&ir_state->rx_kfifo);
+ kfree(ir_state);
+ state->ir_state = NULL;
+ return 0;
+}
--
1.7.1.1
^ permalink raw reply related [flat|nested] 20+ messages in thread* [PATCH 13/17] cx23885: Add a v4l2_subdev group id for the CX2388[578] integrated AV core
[not found] <cover.1279586511.git.awalls@md.metrocast.net>
` (11 preceding siblings ...)
2010-07-20 1:22 ` [PATCH 12/17] cx25840: Add support for CX2388[57] A/V core integrated IR controllers Andy Walls
@ 2010-07-20 1:22 ` Andy Walls
2010-07-20 1:23 ` [PATCH 14/17] cx23885: Add preliminary IR Rx support for the HVR-1250 and TeVii S470 Andy Walls
` (3 subsequent siblings)
16 siblings, 0 replies; 20+ messages in thread
From: Andy Walls @ 2010-07-20 1:22 UTC (permalink / raw)
To: linux-media; +Cc: Kenney Phillisjr, Jarod Wilson, Steven Toth, Igor M.Liplianin
Signed-off-by: Andy Walls <awalls@md.metrocast.net>
---
drivers/media/video/cx23885/cx23885-cards.c | 5 ++++-
drivers/media/video/cx23885/cx23885.h | 3 ++-
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c
index 191bda0..96da11f 100644
--- a/drivers/media/video/cx23885/cx23885-cards.c
+++ b/drivers/media/video/cx23885/cx23885-cards.c
@@ -1152,7 +1152,10 @@ void cx23885_card_setup(struct cx23885_dev *dev)
dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev,
&dev->i2c_bus[2].i2c_adap,
"cx25840", "cx25840", 0x88 >> 1, NULL);
- v4l2_subdev_call(dev->sd_cx25840, core, load_fw);
+ if (dev->sd_cx25840) {
+ dev->sd_cx25840->grp_id = CX23885_HW_AV_CORE;
+ v4l2_subdev_call(dev->sd_cx25840, core, load_fw);
+ }
break;
}
diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h
index a33f2b7..460f430 100644
--- a/drivers/media/video/cx23885/cx23885.h
+++ b/drivers/media/video/cx23885/cx23885.h
@@ -403,7 +403,8 @@ static inline struct cx23885_dev *to_cx23885(struct v4l2_device *v4l2_dev)
#define call_all(dev, o, f, args...) \
v4l2_device_call_all(&dev->v4l2_dev, 0, o, f, ##args)
-#define CX23885_HW_888_IR (1 << 0)
+#define CX23885_HW_888_IR (1 << 0)
+#define CX23885_HW_AV_CORE (1 << 1)
#define call_hw(dev, grpid, o, f, args...) \
v4l2_device_call_all(&dev->v4l2_dev, grpid, o, f, ##args)
--
1.7.1.1
^ permalink raw reply related [flat|nested] 20+ messages in thread* [PATCH 14/17] cx23885: Add preliminary IR Rx support for the HVR-1250 and TeVii S470
[not found] <cover.1279586511.git.awalls@md.metrocast.net>
` (12 preceding siblings ...)
2010-07-20 1:22 ` [PATCH 13/17] cx23885: Add a v4l2_subdev group id for the CX2388[578] integrated AV core Andy Walls
@ 2010-07-20 1:23 ` Andy Walls
2010-07-20 1:25 ` [PATCH 15/17] cx23885: Protect PCI interrupt mask manipulations with a spinlock Andy Walls
` (2 subsequent siblings)
16 siblings, 0 replies; 20+ messages in thread
From: Andy Walls @ 2010-07-20 1:23 UTC (permalink / raw)
To: linux-media; +Cc: Kenney Phillisjr, Jarod Wilson, Steven Toth, Igor M.Liplianin
Add initial IR Rx support using the intergrated IR controller in the
A/V core of the CX23885 bridge chip.
This initial support is flawed in that I2C transactions should not
be performed in a hard irq context. That will be fixed in a
follow on patch.
The TeVii S470 support is reported to generate perptual interrupts
that renders a user' system nearly unusable. The TeVii S470 IR
will be disabled by default in a follow on patch.
Signed-off-by: Andy Walls <awalls@md.metrocast.net>
---
drivers/media/video/cx23885/cx23885-cards.c | 52 ++++++++++++++++++++++++--
drivers/media/video/cx23885/cx23885-core.c | 22 +++++++++--
drivers/media/video/cx23885/cx23885-input.c | 46 ++++++++++++++++++++++-
drivers/media/video/cx23885/cx23885-reg.h | 1 +
4 files changed, 111 insertions(+), 10 deletions(-)
diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c
index 96da11f..5c11caf 100644
--- a/drivers/media/video/cx23885/cx23885-cards.c
+++ b/drivers/media/video/cx23885/cx23885-cards.c
@@ -922,7 +922,7 @@ void cx23885_gpio_setup(struct cx23885_dev *dev)
int cx23885_ir_init(struct cx23885_dev *dev)
{
- static struct v4l2_subdev_io_pin_config ir_pin_cfg[] = {
+ static struct v4l2_subdev_io_pin_config ir_rxtx_pin_cfg[] = {
{
.flags = V4L2_SUBDEV_IO_PIN_INPUT,
.pin = CX23885_PIN_IR_RX_GPIO19,
@@ -937,12 +937,22 @@ int cx23885_ir_init(struct cx23885_dev *dev)
.strength = CX25840_PIN_DRIVE_MEDIUM,
}
};
- const size_t ir_pin_cfg_count = ARRAY_SIZE(ir_pin_cfg);
+ const size_t ir_rxtx_pin_cfg_count = ARRAY_SIZE(ir_rxtx_pin_cfg);
+
+ static struct v4l2_subdev_io_pin_config ir_rx_pin_cfg[] = {
+ {
+ .flags = V4L2_SUBDEV_IO_PIN_INPUT,
+ .pin = CX23885_PIN_IR_RX_GPIO19,
+ .function = CX23885_PAD_IR_RX,
+ .value = 0,
+ .strength = CX25840_PIN_DRIVE_MEDIUM,
+ }
+ };
+ const size_t ir_rx_pin_cfg_count = ARRAY_SIZE(ir_rx_pin_cfg);
struct v4l2_subdev_ir_parameters params;
int ret = 0;
switch (dev->board) {
- case CX23885_BOARD_HAUPPAUGE_HVR1250:
case CX23885_BOARD_HAUPPAUGE_HVR1500:
case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
case CX23885_BOARD_HAUPPAUGE_HVR1800:
@@ -961,7 +971,7 @@ int cx23885_ir_init(struct cx23885_dev *dev)
break;
dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_888_IR);
v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
- ir_pin_cfg_count, ir_pin_cfg);
+ ir_rxtx_pin_cfg_count, ir_rxtx_pin_cfg);
dev->pci_irqmask |= PCI_MSK_IR;
/*
* For these boards we need to invert the Tx output via the
@@ -975,6 +985,26 @@ int cx23885_ir_init(struct cx23885_dev *dev)
params.shutdown = true;
v4l2_subdev_call(dev->sd_ir, ir, tx_s_parameters, ¶ms);
break;
+ case CX23885_BOARD_TEVII_S470:
+ dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_AV_CORE);
+ if (dev->sd_ir == NULL) {
+ ret = -ENODEV;
+ break;
+ }
+ v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
+ ir_rx_pin_cfg_count, ir_rx_pin_cfg);
+ dev->pci_irqmask |= PCI_MSK_AV_CORE;
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_AV_CORE);
+ if (dev->sd_ir == NULL) {
+ ret = -ENODEV;
+ break;
+ }
+ v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
+ ir_rxtx_pin_cfg_count, ir_rxtx_pin_cfg);
+ dev->pci_irqmask |= PCI_MSK_AV_CORE;
+ break;
case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP:
request_module("ir-kbd-i2c");
break;
@@ -993,6 +1023,13 @@ void cx23885_ir_fini(struct cx23885_dev *dev)
cx23888_ir_remove(dev);
dev->sd_ir = NULL;
break;
+ case CX23885_BOARD_TEVII_S470:
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ dev->pci_irqmask &= ~PCI_MSK_AV_CORE;
+ cx_clear(PCI_INT_MSK, PCI_MSK_AV_CORE);
+ /* sd_ir is a duplicate pointer to the AV Core, just clear it */
+ dev->sd_ir = NULL;
+ break;
}
}
@@ -1004,6 +1041,11 @@ void cx23885_ir_pci_int_enable(struct cx23885_dev *dev)
if (dev->sd_ir && (dev->pci_irqmask & PCI_MSK_IR))
cx_set(PCI_INT_MSK, PCI_MSK_IR);
break;
+ case CX23885_BOARD_TEVII_S470:
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ if (dev->sd_ir && (dev->pci_irqmask & PCI_MSK_AV_CORE))
+ cx_set(PCI_INT_MSK, PCI_MSK_AV_CORE);
+ break;
}
}
@@ -1149,6 +1191,8 @@ void cx23885_card_setup(struct cx23885_dev *dev)
case CX23885_BOARD_MAGICPRO_PROHDTVE2:
case CX23885_BOARD_HAUPPAUGE_HVR1290:
case CX23885_BOARD_LEADTEK_WINFAST_PXTV1200:
+ case CX23885_BOARD_TEVII_S470:
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev,
&dev->i2c_bus[2].i2c_adap,
"cx25840", "cx25840", 0x88 >> 1, NULL);
diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c
index ec8baf3..f912be2 100644
--- a/drivers/media/video/cx23885/cx23885-core.c
+++ b/drivers/media/video/cx23885/cx23885-core.c
@@ -1650,7 +1650,7 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
u32 ts1_status, ts1_mask;
u32 ts2_status, ts2_mask;
int vida_count = 0, ts1_count = 0, ts2_count = 0, handled = 0;
- bool ir_handled = false;
+ bool subdev_handled;
pci_status = cx_read(PCI_INT_STAT);
pci_mask = cx_read(PCI_INT_MSK);
@@ -1681,7 +1681,7 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
PCI_MSK_VID_C | PCI_MSK_VID_B | PCI_MSK_VID_A |
PCI_MSK_AUD_INT | PCI_MSK_AUD_EXT |
PCI_MSK_GPIO0 | PCI_MSK_GPIO1 |
- PCI_MSK_IR)) {
+ PCI_MSK_AV_CORE | PCI_MSK_IR)) {
if (pci_status & PCI_MSK_RISC_RD)
dprintk(7, " (PCI_MSK_RISC_RD 0x%08x)\n",
@@ -1731,6 +1731,10 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
dprintk(7, " (PCI_MSK_GPIO1 0x%08x)\n",
PCI_MSK_GPIO1);
+ if (pci_status & PCI_MSK_AV_CORE)
+ dprintk(7, " (PCI_MSK_AV_CORE 0x%08x)\n",
+ PCI_MSK_AV_CORE);
+
if (pci_status & PCI_MSK_IR)
dprintk(7, " (PCI_MSK_IR 0x%08x)\n",
PCI_MSK_IR);
@@ -1765,9 +1769,19 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
handled += cx23885_video_irq(dev, vida_status);
if (pci_status & PCI_MSK_IR) {
+ subdev_handled = false;
v4l2_subdev_call(dev->sd_ir, core, interrupt_service_routine,
- pci_status, &ir_handled);
- if (ir_handled)
+ pci_status, &subdev_handled);
+ if (subdev_handled)
+ handled++;
+ }
+
+ if (pci_status & PCI_MSK_AV_CORE) {
+ subdev_handled = false;
+ v4l2_subdev_call(dev->sd_cx25840,
+ core, interrupt_service_routine,
+ pci_status, &subdev_handled);
+ if (subdev_handled)
handled++;
}
diff --git a/drivers/media/video/cx23885/cx23885-input.c b/drivers/media/video/cx23885/cx23885-input.c
index 496d751..3f924e2 100644
--- a/drivers/media/video/cx23885/cx23885-input.c
+++ b/drivers/media/video/cx23885/cx23885-input.c
@@ -99,8 +99,10 @@ void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events)
switch (dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1850:
case CX23885_BOARD_HAUPPAUGE_HVR1290:
+ case CX23885_BOARD_TEVII_S470:
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
/*
- * The only board we handle right now. However other boards
+ * The only boards we handle right now. However other boards
* using the CX2388x integrated IR controller should be similar
*/
break;
@@ -148,6 +150,7 @@ static int cx23885_input_ir_start(struct cx23885_dev *dev)
switch (dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1850:
case CX23885_BOARD_HAUPPAUGE_HVR1290:
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
/*
* The IR controller on this board only returns pulse widths.
* Any other mode setting will fail to set up the device.
@@ -172,6 +175,37 @@ static int cx23885_input_ir_start(struct cx23885_dev *dev)
*/
params.invert_level = true;
break;
+ case CX23885_BOARD_TEVII_S470:
+ /*
+ * The IR controller on this board only returns pulse widths.
+ * Any other mode setting will fail to set up the device.
+ */
+ params.mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
+ params.enable = true;
+ params.interrupt_enable = true;
+ params.shutdown = false;
+
+ /* Setup for a standard NEC protocol */
+ params.carrier_freq = 37917; /* Hz, 455 kHz/12 for NEC */
+ params.carrier_range_lower = 33000; /* Hz */
+ params.carrier_range_upper = 43000; /* Hz */
+ params.duty_cycle = 33; /* percent, 33 percent for NEC */
+
+ /*
+ * NEC max pulse width: (64/3)/(455 kHz/12) * 16 nec_units
+ * (64/3)/(455 kHz/12) * 16 nec_units * 1.375 = 12378022 ns
+ */
+ params.max_pulse_width = 12378022; /* ns */
+
+ /*
+ * NEC noise filter min width: (64/3)/(455 kHz/12) * 1 nec_unit
+ * (64/3)/(455 kHz/12) * 1 nec_units * 0.625 = 351648 ns
+ */
+ params.noise_filter_min_width = 351648; /* ns */
+
+ params.modulation = false;
+ params.invert_level = true;
+ break;
}
v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, ¶ms);
return 0;
@@ -244,12 +278,20 @@ int cx23885_input_init(struct cx23885_dev *dev)
switch (dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1850:
case CX23885_BOARD_HAUPPAUGE_HVR1290:
- /* Integrated CX23888 IR controller */
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ /* Integrated CX2388[58] IR controller */
driver_type = RC_DRIVER_IR_RAW;
allowed_protos = IR_TYPE_ALL;
/* The grey Hauppauge RC-5 remote */
rc_map = RC_MAP_RC5_HAUPPAUGE_NEW;
break;
+ case CX23885_BOARD_TEVII_S470:
+ /* Integrated CX23885 IR controller */
+ driver_type = RC_DRIVER_IR_RAW;
+ allowed_protos = IR_TYPE_ALL;
+ /* A guess at the remote */
+ rc_map = RC_MAP_TEVII_NEC;
+ break;
default:
return -ENODEV;
}
diff --git a/drivers/media/video/cx23885/cx23885-reg.h b/drivers/media/video/cx23885/cx23885-reg.h
index c0bc9a0..a28772d 100644
--- a/drivers/media/video/cx23885/cx23885-reg.h
+++ b/drivers/media/video/cx23885/cx23885-reg.h
@@ -213,6 +213,7 @@ Channel manager Data Structure entry = 20 DWORD
#define DEV_CNTRL2 0x00040000
#define PCI_MSK_IR (1 << 28)
+#define PCI_MSK_AV_CORE (1 << 27)
#define PCI_MSK_GPIO1 (1 << 24)
#define PCI_MSK_GPIO0 (1 << 23)
#define PCI_MSK_APB_DMA (1 << 12)
--
1.7.1.1
^ permalink raw reply related [flat|nested] 20+ messages in thread* [PATCH 15/17] cx23885: Protect PCI interrupt mask manipulations with a spinlock
[not found] <cover.1279586511.git.awalls@md.metrocast.net>
` (13 preceding siblings ...)
2010-07-20 1:23 ` [PATCH 14/17] cx23885: Add preliminary IR Rx support for the HVR-1250 and TeVii S470 Andy Walls
@ 2010-07-20 1:25 ` Andy Walls
2010-07-20 1:25 ` [PATCH 16/17] cx23885: Move AV Core irq handling to a work handler Andy Walls
2010-07-20 1:26 ` [PATCH 17/17] cx23885: Require user to explicitly enable CX2388[57] IR via module param Andy Walls
16 siblings, 0 replies; 20+ messages in thread
From: Andy Walls @ 2010-07-20 1:25 UTC (permalink / raw)
To: linux-media; +Cc: Kenney Phillisjr, Steven Toth, Igor M.Liplianin
This patch encapsulates access to the PCI_INT_MSK register and
dev->pci_irqmask variable and protects them with a spinlock.
This is needed because both the hard IRQ handler and a workhandler
will need to manipulate the mask to disable the AV_CORE interrupt.
Signed-off-by: Andy Walls <awalls@md.metrocast.net>
---
drivers/media/video/cx23885/cx23885-cards.c | 17 ++---
drivers/media/video/cx23885/cx23885-core.c | 94 +++++++++++++++++++++++++--
drivers/media/video/cx23885/cx23885-vbi.c | 2 +-
drivers/media/video/cx23885/cx23885-video.c | 7 +-
drivers/media/video/cx23885/cx23885.h | 5 ++
5 files changed, 103 insertions(+), 22 deletions(-)
diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c
index 5c11caf..b8ad935 100644
--- a/drivers/media/video/cx23885/cx23885-cards.c
+++ b/drivers/media/video/cx23885/cx23885-cards.c
@@ -972,7 +972,6 @@ int cx23885_ir_init(struct cx23885_dev *dev)
dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_888_IR);
v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
ir_rxtx_pin_cfg_count, ir_rxtx_pin_cfg);
- dev->pci_irqmask |= PCI_MSK_IR;
/*
* For these boards we need to invert the Tx output via the
* IR controller to have the LED off while idle
@@ -993,7 +992,6 @@ int cx23885_ir_init(struct cx23885_dev *dev)
}
v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
ir_rx_pin_cfg_count, ir_rx_pin_cfg);
- dev->pci_irqmask |= PCI_MSK_AV_CORE;
break;
case CX23885_BOARD_HAUPPAUGE_HVR1250:
dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_AV_CORE);
@@ -1003,7 +1001,6 @@ int cx23885_ir_init(struct cx23885_dev *dev)
}
v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
ir_rxtx_pin_cfg_count, ir_rxtx_pin_cfg);
- dev->pci_irqmask |= PCI_MSK_AV_CORE;
break;
case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP:
request_module("ir-kbd-i2c");
@@ -1018,15 +1015,13 @@ void cx23885_ir_fini(struct cx23885_dev *dev)
switch (dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1850:
case CX23885_BOARD_HAUPPAUGE_HVR1290:
- dev->pci_irqmask &= ~PCI_MSK_IR;
- cx_clear(PCI_INT_MSK, PCI_MSK_IR);
+ cx23885_irq_remove(dev, PCI_MSK_IR);
cx23888_ir_remove(dev);
dev->sd_ir = NULL;
break;
case CX23885_BOARD_TEVII_S470:
case CX23885_BOARD_HAUPPAUGE_HVR1250:
- dev->pci_irqmask &= ~PCI_MSK_AV_CORE;
- cx_clear(PCI_INT_MSK, PCI_MSK_AV_CORE);
+ cx23885_irq_remove(dev, PCI_MSK_AV_CORE);
/* sd_ir is a duplicate pointer to the AV Core, just clear it */
dev->sd_ir = NULL;
break;
@@ -1038,13 +1033,13 @@ void cx23885_ir_pci_int_enable(struct cx23885_dev *dev)
switch (dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1850:
case CX23885_BOARD_HAUPPAUGE_HVR1290:
- if (dev->sd_ir && (dev->pci_irqmask & PCI_MSK_IR))
- cx_set(PCI_INT_MSK, PCI_MSK_IR);
+ if (dev->sd_ir)
+ cx23885_irq_add_enable(dev, PCI_MSK_IR);
break;
case CX23885_BOARD_TEVII_S470:
case CX23885_BOARD_HAUPPAUGE_HVR1250:
- if (dev->sd_ir && (dev->pci_irqmask & PCI_MSK_AV_CORE))
- cx_set(PCI_INT_MSK, PCI_MSK_AV_CORE);
+ if (dev->sd_ir)
+ cx23885_irq_add_enable(dev, PCI_MSK_AV_CORE);
break;
}
}
diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c
index f912be2..fa2f4b1 100644
--- a/drivers/media/video/cx23885/cx23885-core.c
+++ b/drivers/media/video/cx23885/cx23885-core.c
@@ -299,6 +299,83 @@ static struct sram_channel cx23887_sram_channels[] = {
},
};
+void cx23885_irq_add(struct cx23885_dev *dev, u32 mask)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+ dev->pci_irqmask |= mask;
+
+ spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+}
+
+void cx23885_irq_add_enable(struct cx23885_dev *dev, u32 mask)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+ dev->pci_irqmask |= mask;
+ cx_set(PCI_INT_MSK, mask);
+
+ spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+}
+
+void cx23885_irq_enable(struct cx23885_dev *dev, u32 mask)
+{
+ u32 v;
+ unsigned long flags;
+ spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+ v = mask & dev->pci_irqmask;
+ if (v)
+ cx_set(PCI_INT_MSK, v);
+
+ spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+}
+
+static inline void cx23885_irq_enable_all(struct cx23885_dev *dev)
+{
+ cx23885_irq_enable(dev, 0xffffffff);
+}
+
+void cx23885_irq_disable(struct cx23885_dev *dev, u32 mask)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+ cx_clear(PCI_INT_MSK, mask);
+
+ spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+}
+
+static inline void cx23885_irq_disable_all(struct cx23885_dev *dev)
+{
+ cx23885_irq_disable(dev, 0xffffffff);
+}
+
+void cx23885_irq_remove(struct cx23885_dev *dev, u32 mask)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+ dev->pci_irqmask &= ~mask;
+ cx_clear(PCI_INT_MSK, mask);
+
+ spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+}
+
+static u32 cx23885_irq_get_mask(struct cx23885_dev *dev)
+{
+ u32 v;
+ unsigned long flags;
+ spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+ v = cx_read(PCI_INT_MSK);
+
+ spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+ return v;
+}
+
static int cx23885_risc_decode(u32 risc)
{
static char *instr[16] = {
@@ -548,7 +625,7 @@ static void cx23885_shutdown(struct cx23885_dev *dev)
cx_write(UART_CTL, 0);
/* Disable Interrupts */
- cx_write(PCI_INT_MSK, 0);
+ cx23885_irq_disable_all(dev);
cx_write(VID_A_INT_MSK, 0);
cx_write(VID_B_INT_MSK, 0);
cx_write(VID_C_INT_MSK, 0);
@@ -774,6 +851,8 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
{
int i;
+ spin_lock_init(&dev->pci_irqmask_lock);
+
mutex_init(&dev->lock);
mutex_init(&dev->gpio_lock);
@@ -820,9 +899,9 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
dev->pci_bus = dev->pci->bus->number;
dev->pci_slot = PCI_SLOT(dev->pci->devfn);
- dev->pci_irqmask = 0x001f00;
+ cx23885_irq_add(dev, 0x001f00);
if (cx23885_boards[dev->board].cimax > 0)
- dev->pci_irqmask |= 0x01800000; /* for CiMaxes */
+ cx23885_irq_add(dev, 0x01800000); /* for CiMaxes */
/* External Master 1 Bus */
dev->i2c_bus[0].nr = 0;
@@ -1156,7 +1235,7 @@ static void cx23885_tsport_reg_dump(struct cx23885_tsport *port)
dprintk(1, "%s() DEV_CNTRL2 0x%08X\n", __func__,
cx_read(DEV_CNTRL2));
dprintk(1, "%s() PCI_INT_MSK 0x%08X\n", __func__,
- cx_read(PCI_INT_MSK));
+ cx23885_irq_get_mask(dev));
dprintk(1, "%s() AUD_INT_INT_MSK 0x%08X\n", __func__,
cx_read(AUDIO_INT_INT_MSK));
dprintk(1, "%s() AUD_INT_DMA_CTL 0x%08X\n", __func__,
@@ -1292,7 +1371,8 @@ static int cx23885_start_dma(struct cx23885_tsport *port,
dprintk(1, "%s() enabling TS int's and DMA\n", __func__);
cx_set(port->reg_ts_int_msk, port->ts_int_msk_val);
cx_set(port->reg_dma_ctl, port->dma_ctl_val);
- cx_set(PCI_INT_MSK, dev->pci_irqmask | port->pci_irqmask);
+ cx23885_irq_add(dev, port->pci_irqmask);
+ cx23885_irq_enable_all(dev);
break;
default:
BUG();
@@ -1653,7 +1733,7 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
bool subdev_handled;
pci_status = cx_read(PCI_INT_STAT);
- pci_mask = cx_read(PCI_INT_MSK);
+ pci_mask = cx23885_irq_get_mask(dev);
vida_status = cx_read(VID_A_INT_STAT);
vida_mask = cx_read(VID_A_INT_MSK);
ts1_status = cx_read(VID_B_INT_STAT);
@@ -1977,7 +2057,7 @@ static int __devinit cx23885_initdev(struct pci_dev *pci_dev,
switch (dev->board) {
case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
- cx_set(PCI_INT_MSK, 0x01800000); /* for NetUP */
+ cx23885_irq_add_enable(dev, 0x01800000); /* for NetUP */
break;
}
diff --git a/drivers/media/video/cx23885/cx23885-vbi.c b/drivers/media/video/cx23885/cx23885-vbi.c
index 708a8c7..c0b6038 100644
--- a/drivers/media/video/cx23885/cx23885-vbi.c
+++ b/drivers/media/video/cx23885/cx23885-vbi.c
@@ -74,7 +74,7 @@ static int cx23885_start_vbi_dma(struct cx23885_dev *dev,
q->count = 1;
/* enable irqs */
- cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | 0x01);
+ cx23885_irq_add_enable(dev, 0x01);
cx_set(VID_A_INT_MSK, 0x000022);
/* start dma */
diff --git a/drivers/media/video/cx23885/cx23885-video.c b/drivers/media/video/cx23885/cx23885-video.c
index 2519455..da66e5f 100644
--- a/drivers/media/video/cx23885/cx23885-video.c
+++ b/drivers/media/video/cx23885/cx23885-video.c
@@ -441,7 +441,7 @@ static int cx23885_start_video_dma(struct cx23885_dev *dev,
q->count = 1;
/* enable irq */
- cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | 0x01);
+ cx23885_irq_add_enable(dev, 0x01);
cx_set(VID_A_INT_MSK, 0x000011);
/* start dma */
@@ -1465,7 +1465,7 @@ static const struct v4l2_file_operations radio_fops = {
void cx23885_video_unregister(struct cx23885_dev *dev)
{
dprintk(1, "%s()\n", __func__);
- cx_clear(PCI_INT_MSK, 1);
+ cx23885_irq_remove(dev, 0x01);
if (dev->video_dev) {
if (video_is_registered(dev->video_dev))
@@ -1502,7 +1502,8 @@ int cx23885_video_register(struct cx23885_dev *dev)
VID_A_DMA_CTL, 0x11, 0x00);
/* Don't enable VBI yet */
- cx_set(PCI_INT_MSK, 1);
+
+ cx23885_irq_add_enable(dev, 0x01);
if (TUNER_ABSENT != dev->tuner_type) {
struct v4l2_subdev *sd = NULL;
diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h
index 460f430..5bf6ed0 100644
--- a/drivers/media/video/cx23885/cx23885.h
+++ b/drivers/media/video/cx23885/cx23885.h
@@ -325,6 +325,7 @@ struct cx23885_dev {
u32 __iomem *lmmio;
u8 __iomem *bmmio;
int pci_irqmask;
+ spinlock_t pci_irqmask_lock; /* protects mask reg too */
int hwrevision;
/* This valud is board specific and is used to configure the
@@ -485,6 +486,10 @@ extern u32 cx23885_gpio_get(struct cx23885_dev *dev, u32 mask);
extern void cx23885_gpio_enable(struct cx23885_dev *dev, u32 mask,
int asoutput);
+extern void cx23885_irq_add_enable(struct cx23885_dev *dev, u32 mask);
+extern void cx23885_irq_enable(struct cx23885_dev *dev, u32 mask);
+extern void cx23885_irq_disable(struct cx23885_dev *dev, u32 mask);
+extern void cx23885_irq_remove(struct cx23885_dev *dev, u32 mask);
/* ----------------------------------------------------------- */
/* cx23885-cards.c */
--
1.7.1.1
^ permalink raw reply related [flat|nested] 20+ messages in thread* [PATCH 16/17] cx23885: Move AV Core irq handling to a work handler
[not found] <cover.1279586511.git.awalls@md.metrocast.net>
` (14 preceding siblings ...)
2010-07-20 1:25 ` [PATCH 15/17] cx23885: Protect PCI interrupt mask manipulations with a spinlock Andy Walls
@ 2010-07-20 1:25 ` Andy Walls
2010-07-20 1:26 ` [PATCH 17/17] cx23885: Require user to explicitly enable CX2388[57] IR via module param Andy Walls
16 siblings, 0 replies; 20+ messages in thread
From: Andy Walls @ 2010-07-20 1:25 UTC (permalink / raw)
To: linux-media; +Cc: Kenney Phillisjr, Jarod Wilson, Steven Toth, Igor M.Liplianin
Interrupts from the AV Core are best handled by a workqueue handler
since many I2C transactions are required to service the AV Core
interrupt. The AV_CORE PCI interrupt is disabled by the IRQ handler
and reenabled when the work handler is finished.
Signed-off-by: Andy Walls <awalls@md.metrocast.net>
---
drivers/media/video/cx23885/Makefile | 5 ++-
drivers/media/video/cx23885/cx23885-av.c | 35 ++++++++++++++++++++++++++++
drivers/media/video/cx23885/cx23885-av.h | 27 +++++++++++++++++++++
drivers/media/video/cx23885/cx23885-core.c | 20 ++++++++-------
drivers/media/video/cx23885/cx23885-ir.c | 24 ++++++++++++++++---
drivers/media/video/cx23885/cx23885.h | 1 +
6 files changed, 97 insertions(+), 15 deletions(-)
create mode 100644 drivers/media/video/cx23885/cx23885-av.c
create mode 100644 drivers/media/video/cx23885/cx23885-av.h
diff --git a/drivers/media/video/cx23885/Makefile b/drivers/media/video/cx23885/Makefile
index 5787ae2..e2ee95f 100644
--- a/drivers/media/video/cx23885/Makefile
+++ b/drivers/media/video/cx23885/Makefile
@@ -1,7 +1,8 @@
cx23885-objs := cx23885-cards.o cx23885-video.o cx23885-vbi.o \
cx23885-core.o cx23885-i2c.o cx23885-dvb.o cx23885-417.o \
- cx23885-ioctl.o cx23885-ir.o cx23885-input.o cx23888-ir.o \
- netup-init.o cimax2.o netup-eeprom.o cx23885-f300.o
+ cx23885-ioctl.o cx23885-ir.o cx23885-av.o cx23885-input.o \
+ cx23888-ir.o netup-init.o cimax2.o netup-eeprom.o \
+ cx23885-f300.o
obj-$(CONFIG_VIDEO_CX23885) += cx23885.o
diff --git a/drivers/media/video/cx23885/cx23885-av.c b/drivers/media/video/cx23885/cx23885-av.c
new file mode 100644
index 0000000..134ebdd
--- /dev/null
+++ b/drivers/media/video/cx23885/cx23885-av.c
@@ -0,0 +1,35 @@
+/*
+ * Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ * AV device support routines - non-input, non-vl42_subdev routines
+ *
+ * Copyright (C) 2010 Andy Walls <awalls@md.metrocast.net>
+ *
+ * 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 the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include "cx23885.h"
+
+void cx23885_av_work_handler(struct work_struct *work)
+{
+ struct cx23885_dev *dev =
+ container_of(work, struct cx23885_dev, cx25840_work);
+ bool handled;
+
+ v4l2_subdev_call(dev->sd_cx25840, core, interrupt_service_routine,
+ PCI_MSK_AV_CORE, &handled);
+ cx23885_irq_enable(dev, PCI_MSK_AV_CORE);
+}
diff --git a/drivers/media/video/cx23885/cx23885-av.h b/drivers/media/video/cx23885/cx23885-av.h
new file mode 100644
index 0000000..d2915c3
--- /dev/null
+++ b/drivers/media/video/cx23885/cx23885-av.h
@@ -0,0 +1,27 @@
+/*
+ * Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ * AV device support routines - non-input, non-vl42_subdev routines
+ *
+ * Copyright (C) 2010 Andy Walls <awalls@md.metrocast.net>
+ *
+ * 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 the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef _CX23885_AV_H_
+#define _CX23885_AV_H_
+void cx23885_av_work_handler(struct work_struct *work);
+#endif
diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c
index fa2f4b1..4f3cd79 100644
--- a/drivers/media/video/cx23885/cx23885-core.c
+++ b/drivers/media/video/cx23885/cx23885-core.c
@@ -34,6 +34,7 @@
#include "cimax2.h"
#include "cx23888-ir.h"
#include "cx23885-ir.h"
+#include "cx23885-av.h"
#include "cx23885-input.h"
MODULE_DESCRIPTION("Driver for cx23885 based TV cards");
@@ -1856,13 +1857,13 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
handled++;
}
- if (pci_status & PCI_MSK_AV_CORE) {
- subdev_handled = false;
- v4l2_subdev_call(dev->sd_cx25840,
- core, interrupt_service_routine,
- pci_status, &subdev_handled);
- if (subdev_handled)
- handled++;
+ if ((pci_status & pci_mask) & PCI_MSK_AV_CORE) {
+ cx23885_irq_disable(dev, PCI_MSK_AV_CORE);
+ if (!schedule_work(&dev->cx25840_work))
+ printk(KERN_ERR "%s: failed to set up deferred work for"
+ " AV Core/IR interrupt. Interrupt is disabled"
+ " and won't be re-enabled\n", dev->name);
+ handled++;
}
if (handled)
@@ -1882,11 +1883,11 @@ static void cx23885_v4l2_dev_notify(struct v4l2_subdev *sd,
dev = to_cx23885(sd->v4l2_dev);
switch (notification) {
- case V4L2_SUBDEV_IR_RX_NOTIFY: /* Called in an IRQ context */
+ case V4L2_SUBDEV_IR_RX_NOTIFY: /* Possibly called in an IRQ context */
if (sd == dev->sd_ir)
cx23885_ir_rx_v4l2_dev_notify(sd, *(u32 *)arg);
break;
- case V4L2_SUBDEV_IR_TX_NOTIFY: /* Called in an IRQ context */
+ case V4L2_SUBDEV_IR_TX_NOTIFY: /* Possibly called in an IRQ context */
if (sd == dev->sd_ir)
cx23885_ir_tx_v4l2_dev_notify(sd, *(u32 *)arg);
break;
@@ -1895,6 +1896,7 @@ static void cx23885_v4l2_dev_notify(struct v4l2_subdev *sd,
static void cx23885_v4l2_dev_notify_init(struct cx23885_dev *dev)
{
+ INIT_WORK(&dev->cx25840_work, cx23885_av_work_handler);
INIT_WORK(&dev->ir_rx_work, cx23885_ir_rx_work_handler);
INIT_WORK(&dev->ir_tx_work, cx23885_ir_tx_work_handler);
dev->v4l2_dev.notify = cx23885_v4l2_dev_notify;
diff --git a/drivers/media/video/cx23885/cx23885-ir.c b/drivers/media/video/cx23885/cx23885-ir.c
index 6ceabd4..7125247 100644
--- a/drivers/media/video/cx23885/cx23885-ir.c
+++ b/drivers/media/video/cx23885/cx23885-ir.c
@@ -72,7 +72,7 @@ void cx23885_ir_tx_work_handler(struct work_struct *work)
}
-/* Called in an IRQ context */
+/* Possibly called in an IRQ context */
void cx23885_ir_rx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events)
{
struct cx23885_dev *dev = to_cx23885(sd->v4l2_dev);
@@ -86,10 +86,18 @@ void cx23885_ir_rx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events)
set_bit(CX23885_IR_RX_HW_FIFO_OVERRUN, notifications);
if (events & V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN)
set_bit(CX23885_IR_RX_SW_FIFO_OVERRUN, notifications);
- schedule_work(&dev->ir_rx_work);
+
+ /*
+ * For the integrated AV core, we are already in a workqueue context.
+ * For the CX23888 integrated IR, we are in an interrupt context.
+ */
+ if (sd == dev->sd_cx25840)
+ cx23885_ir_rx_work_handler(&dev->ir_rx_work);
+ else
+ schedule_work(&dev->ir_rx_work);
}
-/* Called in an IRQ context */
+/* Possibly called in an IRQ context */
void cx23885_ir_tx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events)
{
struct cx23885_dev *dev = to_cx23885(sd->v4l2_dev);
@@ -97,5 +105,13 @@ void cx23885_ir_tx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events)
if (events & V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ)
set_bit(CX23885_IR_TX_FIFO_SERVICE_REQ, notifications);
- schedule_work(&dev->ir_tx_work);
+
+ /*
+ * For the integrated AV core, we are already in a workqueue context.
+ * For the CX23888 integrated IR, we are in an interrupt context.
+ */
+ if (sd == dev->sd_cx25840)
+ cx23885_ir_tx_work_handler(&dev->ir_tx_work);
+ else
+ schedule_work(&dev->ir_tx_work);
}
diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h
index 5bf6ed0..ed94b17 100644
--- a/drivers/media/video/cx23885/cx23885.h
+++ b/drivers/media/video/cx23885/cx23885.h
@@ -366,6 +366,7 @@ struct cx23885_dev {
unsigned char radio_addr;
unsigned int has_radio;
struct v4l2_subdev *sd_cx25840;
+ struct work_struct cx25840_work;
/* Infrared */
struct v4l2_subdev *sd_ir;
--
1.7.1.1
^ permalink raw reply related [flat|nested] 20+ messages in thread* [PATCH 17/17] cx23885: Require user to explicitly enable CX2388[57] IR via module param
[not found] <cover.1279586511.git.awalls@md.metrocast.net>
` (15 preceding siblings ...)
2010-07-20 1:25 ` [PATCH 16/17] cx23885: Move AV Core irq handling to a work handler Andy Walls
@ 2010-07-20 1:26 ` Andy Walls
16 siblings, 0 replies; 20+ messages in thread
From: Andy Walls @ 2010-07-20 1:26 UTC (permalink / raw)
To: linux-media
Cc: Kenney Phillisjr, Jarod Wilson, Steven Toth, Igor M.Liplianin,
Mauro Carvalho Chehab
The CX23885 IR controller was reported to cause an interrupt storm
on a TeVii S470 card, but was reported fine on an HVR-1250. Keep
integrated IR disabled by default on CX2388[57] based cards to avoid
a bad user experience in the general case.
Signed-off-by: Andy Walls <awalls@md.metrocast.net>
---
drivers/media/video/cx23885/cx23885-cards.c | 21 +++++++++++++++++++--
1 files changed, 19 insertions(+), 2 deletions(-)
diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c
index b8ad935..0a3a0ad 100644
--- a/drivers/media/video/cx23885/cx23885-cards.c
+++ b/drivers/media/video/cx23885/cx23885-cards.c
@@ -30,6 +30,16 @@
#include "netup-init.h"
#include "cx23888-ir.h"
+static unsigned int enable_885_ir;
+module_param(enable_885_ir, int, 0644);
+MODULE_PARM_DESC(enable_885_ir,
+ "Enable integrated IR controller for supported\n"
+ "\t\t CX2388[57] boards that are wired for it:\n"
+ "\t\t\tHVR-1250 (reported safe)\n"
+ "\t\t\tTeVii S470 (reported unsafe)\n"
+ "\t\t This can cause an interrupt storm with some cards.\n"
+ "\t\t Default: 0 [Disabled]");
+
/* ------------------------------------------------------------------ */
/* board config info */
@@ -985,6 +995,8 @@ int cx23885_ir_init(struct cx23885_dev *dev)
v4l2_subdev_call(dev->sd_ir, ir, tx_s_parameters, ¶ms);
break;
case CX23885_BOARD_TEVII_S470:
+ if (!enable_885_ir)
+ break;
dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_AV_CORE);
if (dev->sd_ir == NULL) {
ret = -ENODEV;
@@ -994,6 +1006,8 @@ int cx23885_ir_init(struct cx23885_dev *dev)
ir_rx_pin_cfg_count, ir_rx_pin_cfg);
break;
case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ if (!enable_885_ir)
+ break;
dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_AV_CORE);
if (dev->sd_ir == NULL) {
ret = -ENODEV;
@@ -1174,6 +1188,11 @@ void cx23885_card_setup(struct cx23885_dev *dev)
* loaded, ensure this happens.
*/
switch (dev->board) {
+ case CX23885_BOARD_TEVII_S470:
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ /* Currently only enabled for the integrated IR controller */
+ if (!enable_885_ir)
+ break;
case CX23885_BOARD_HAUPPAUGE_HVR1800:
case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
case CX23885_BOARD_HAUPPAUGE_HVR1700:
@@ -1186,8 +1205,6 @@ void cx23885_card_setup(struct cx23885_dev *dev)
case CX23885_BOARD_MAGICPRO_PROHDTVE2:
case CX23885_BOARD_HAUPPAUGE_HVR1290:
case CX23885_BOARD_LEADTEK_WINFAST_PXTV1200:
- case CX23885_BOARD_TEVII_S470:
- case CX23885_BOARD_HAUPPAUGE_HVR1250:
dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev,
&dev->i2c_bus[2].i2c_adap,
"cx25840", "cx25840", 0x88 >> 1, NULL);
--
1.7.1.1
^ permalink raw reply related [flat|nested] 20+ messages in thread