Skip to contentSkip to conversation

cx23885: Return -ENXIO on slave nack

PATCH In review Andy Walls 2 participants 20 messages Jul 20, 2010 1 message not in the archive Media (V4L/DVB)

lore
Share & tools
lore

A message that is not in the archive (sent off-list or not yet received): [email protected]

Andy Walls

author
#1 lore raw

PATCH 1/17 cx23885: Return -ENXIO on slave nack

From: Jean Delvare <[email protected]>

Documentation/i2c/fault-codes says that i2c adapter drivers should return -ENXIO when no slave acks an address byte.

---
 drivers/media/video/cx23885/cx23885-i2c.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)
drivers/media/video/cx23885/cx23885-i2c.c +2 -2
index d4746e0..154c914 100644
@@ -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__);
Signature · 1 line
1.7.1.1
author
#2 lore raw

PATCH 2/17 cx23885: Check for slave nack on all transactions

In reply to a message not in the archive

From: Jean Delvare <[email protected]>

Don't just check for nacks on zero-length transactions. Check on other transactions too.

---
 drivers/media/video/cx23885/cx23885-i2c.c |    8 ++++++++
 1 files changed, 8 insertions(+), 0 deletions(-)
drivers/media/video/cx23885/cx23885-i2c.c +8 -0
index 154c914..afb8d6f 100644
@@ -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]);
Signature · 1 line
1.7.1.1
author
#3 lore raw

PATCH 3/17 cx23885: i2c_wait_done returns 0 or 1, don't check for < 0 return value

In reply to a message not in the archive

From: Jean Delvare <[email protected]>

Function i2c_wait_done() never returns negative values, so there is no point in checking for them.

---
 drivers/media/video/cx23885/cx23885-i2c.c |   15 +++------------
 1 files changed, 3 insertions(+), 12 deletions(-)
drivers/media/video/cx23885/cx23885-i2c.c +3 -12
index afb8d6f..1a39148 100644
@@ -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;
Signature · 1 line
1.7.1.1
author
#4 lore raw

PATCH 4/17 cx25840: Make cx25840 i2c register read transactions atomic

In reply to a message not in the archive

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.

---
 drivers/media/video/cx25840/cx25840-core.c |   58 +++++++++++++++++++---------
 1 files changed, 39 insertions(+), 19 deletions(-)
drivers/media/video/cx25840/cx25840-core.c +39 -19
index bb4872b..4f908fa 100644
@@ -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,
Signature · 1 line
1.7.1.1
Acked-by on v1 4/17
#5 lore raw

Re: [PATCH 04/17] cx25840: Make cx25840 i2c register read transactions atomic

Hi Andy,

Quoted 6 lines · Andy Walls

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.

Good catch.

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.

Quoted 81 lines
---
 drivers/media/video/cx25840/cx25840-core.c |   58 +++++++++++++++++++---------
 1 files changed, 39 insertions(+), 19 deletions(-)
drivers/media/video/cx25840/cx25840-core.c +39 -19
index bb4872b..4f908fa 100644
@@ -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,
Signature · 1 line
Jean Delvare
author
#6 lore raw

Re: [PATCH 04/17] cx25840: Make cx25840 i2c register read transactions atomic

Quoted 11 lines · Jean Delvare

Hi Andy,

Quoted 6 lines · Andy Walls

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.

Good catch.

Thanks.

Quoted 7 lines

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

#7 lore raw

Re: [PATCH 04/17] cx25840: Make cx25840 i2c register read transactions atomic

Quoted 52 lines · Andy Walls
Quoted 11 lines · Jean Delvare

Hi Andy,

Quoted 6 lines · Andy Walls

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.

Good catch.

Thanks.

Quoted 7 lines

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.

Quoted 4 lines

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.

Signature · 1 line
Jean Delvare
author
#8 lore raw

PATCH 5/17 cx23885: Add correct detection of the HVR-1250 model 79501

In reply to a message not in the archive

The offset in the eeprom data for the 79501 version of the HVR-1250 is at 0xc0 vs. the standard 0x80.

---
 drivers/media/video/cx23885/cx23885-cards.c |   10 ++++++++++
 1 files changed, 10 insertions(+), 0 deletions(-)
drivers/media/video/cx23885/cx23885-cards.c +10 -0
index d639186..093de84 100644
@@ -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:
Signature · 1 line
1.7.1.1
author
#9 lore raw

PATCH 6/17 cx23885: Add a VIDIOC_LOG_STATUS ioctl function for analog video devices

In reply to a message not in the archive

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.

---
 drivers/media/video/cx23885/cx23885-video.c |   16 ++++++++++++++++
 1 files changed, 16 insertions(+), 0 deletions(-)
drivers/media/video/cx23885/cx23885-video.c +16 -0
index 4e44dcd..2519455 100644
@@ -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,
Signature · 1 line
1.7.1.1
author
#10 lore raw

PATCH 7/17 v4l2_subdev: Add s_io_pin_config to v4l2_subdev_core_ops

In reply to a message not in the archive

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.

---
 include/media/v4l2-subdev.h |   23 +++++++++++++++++++++++
 1 files changed, 23 insertions(+), 0 deletions(-)
include/media/v4l2-subdev.h +23 -0
index 02c6f4d..9195ad4 100644
@@ -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);
Signature · 1 line
1.7.1.1
author
#11 lore raw

PATCH 8/17 cx25840: Add s_io_pin_config core subdev ops for the CX2388[578]

In reply to a message not in the archive

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.

---
 drivers/media/video/cx25840/cx25840-core.c |  153 ++++++++++++++++++++++++++++
 include/media/cx25840.h                    |   75 ++++++++++++++
 2 files changed, 228 insertions(+), 0 deletions(-)
drivers/media/video/cx25840/cx25840-core.c +153 -0
index 4f908fa..46a046d 100644
@@ -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,
include/media/cx25840.h +75 -0

79 lines not on this page · raw message

Signature · 1 line
1.7.1.1
author
#12 lore raw

PATCH 9/17 v4l2_subdev, cx23885: Differentiate IR carrier sense and I/O pin inversion

In reply to a message not in the archive

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

---
 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(-)
drivers/media/video/cx23885/cx23885-input.c +1 -1
index d0b1613..496d751 100644
@@ -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, &params);
drivers/media/video/cx23885/cx23888-ir.c +24 -8
index f63d378..28ca90f 100644
@@ -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)
include/media/v4l2-subdev.h +4 -1
index 9195ad4..a780cca 100644
@@ -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 */
Signature · 1 line
1.7.1.1
author
#13 lore raw

PATCH 10/17 cx23885: For CX23888 IR, configure the IO pin mux IR pins explcitly

In reply to a message not in the archive

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.

---
 drivers/media/video/cx23885/cx23885-cards.c |   31 +++++++++++++++++++++++++++
 1 files changed, 31 insertions(+), 0 deletions(-)
drivers/media/video/cx23885/cx23885-cards.c +31 -0
index 093de84..191bda0 100644
@@ -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, &params);
+		params.enable = false;
+		params.shutdown = false;
+		params.invert_level = true;
+		v4l2_subdev_call(dev->sd_ir, ir, tx_s_parameters, &params);
+		params.shutdown = true;
+		v4l2_subdev_call(dev->sd_ir, ir, tx_s_parameters, &params);
 		break;
 	case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP:
 		request_module("ir-kbd-i2c");
Signature · 1 line
1.7.1.1
author
#14 lore raw

PATCH 11/17 v4l2_subdev: Move interrupt_service_routine ptr to v4l2_subdev_core_ops

In reply to a message not in the archive

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.

---
 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(-)
drivers/media/video/cx23885/cx23885-core.c +1 -1
index 161ae73..ec8baf3 100644
@@ -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++;
drivers/media/video/cx23885/cx23888-ir.c +1 -2
index 28ca90f..51f2163 100644
@@ -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,
include/media/v4l2-subdev.h +7 -9
index a780cca..bacd525 100644
@@ -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);
Signature · 1 line
1.7.1.1
author
#15 lore raw

PATCH 12/17 cx25840: Add support for CX2388[57] A/V core integrated IR controllers

In reply to a message not in the archive

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.

---
 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
drivers/media/video/cx25840/Makefile +1 -1
index 6e8665b..2ee96d3 100644
@@ -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
 
drivers/media/video/cx25840/cx25840-core.c +128 -0

185 lines not on this page · raw message

drivers/media/video/cx25840/cx25840-core.h +28 -0
index 04393b9..8f47322 100644
@@ -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
drivers/media/video/cx25840/cx25840-ir.c new file +1262 -0

1,262 lines not on this page · raw message

Signature · 1 line
1.7.1.1
author
#16 lore raw

PATCH 13/17 cx23885: Add a v4l2_subdev group id for the CX2388[578] integrated AV core

In reply to a message not in the archive
---
 drivers/media/video/cx23885/cx23885-cards.c |    5 ++++-
 drivers/media/video/cx23885/cx23885.h       |    3 ++-
 2 files changed, 6 insertions(+), 2 deletions(-)
drivers/media/video/cx23885/cx23885-cards.c +4 -1
index 191bda0..96da11f 100644
@@ -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;
 	}
 
drivers/media/video/cx23885/cx23885.h +2 -1
index a33f2b7..460f430 100644
@@ -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)
Signature · 1 line
1.7.1.1
author
#17 lore raw

PATCH 14/17 cx23885: Add preliminary IR Rx support for the HVR-1250 and TeVii S470

In reply to a message not in the archive

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.

---
 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(-)
drivers/media/video/cx23885/cx23885-cards.c +48 -4
index 96da11f..5c11caf 100644
@@ -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, &params);
 		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);
drivers/media/video/cx23885/cx23885-core.c +18 -4
index ec8baf3..f912be2 100644
@@ -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++;
 	}
 
drivers/media/video/cx23885/cx23885-input.c +44 -2

76 lines not on this page · raw message

drivers/media/video/cx23885/cx23885-reg.h +1 -0
index c0bc9a0..a28772d 100644
@@ -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)
Signature · 1 line
1.7.1.1
author
#18 lore raw

PATCH 15/17 cx23885: Protect PCI interrupt mask manipulations with a spinlock

In reply to a message not in the archive

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.

---
 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(-)
drivers/media/video/cx23885/cx23885-cards.c +6 -11
index 5c11caf..b8ad935 100644
@@ -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;
 	}
 }
drivers/media/video/cx23885/cx23885-core.c +87 -7

143 lines not on this page · raw message

drivers/media/video/cx23885/cx23885-vbi.c +1 -1
index 708a8c7..c0b6038 100644
@@ -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 */
drivers/media/video/cx23885/cx23885-video.c +4 -3
index 2519455..da66e5f 100644
@@ -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;
drivers/media/video/cx23885/cx23885.h +5 -0
index 460f430..5bf6ed0 100644
@@ -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                                             */
Signature · 1 line
1.7.1.1
author
#19 lore raw

PATCH 16/17 cx23885: Move AV Core irq handling to a work handler

In reply to a message not in the archive

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.

---
 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
drivers/media/video/cx23885/Makefile +3 -2
index 5787ae2..e2ee95f 100644
@@ -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
 
drivers/media/video/cx23885/cx23885-av.c new file +35 -0
new file mode 100644
index 0000000..134ebdd
@@ -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 <[email protected]>
+ *
+ *  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);
+}
drivers/media/video/cx23885/cx23885-av.h new file +27 -0
new file mode 100644
index 0000000..d2915c3
@@ -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 <[email protected]>
+ *
+ *  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
drivers/media/video/cx23885/cx23885-core.c +11 -9
index fa2f4b1..4f3cd79 100644
@@ -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;
drivers/media/video/cx23885/cx23885-ir.c +20 -4
index 6ceabd4..7125247 100644
@@ -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);
 }
drivers/media/video/cx23885/cx23885.h +1 -0
index 5bf6ed0..ed94b17 100644
@@ -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;
Signature · 1 line
1.7.1.1
author
#20 lore raw

PATCH 17/17 cx23885: Require user to explicitly enable CX2388[57] IR via module param

In reply to a message not in the archive

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.

---
 drivers/media/video/cx23885/cx23885-cards.c |   21 +++++++++++++++++++--
 1 files changed, 19 insertions(+), 2 deletions(-)
drivers/media/video/cx23885/cx23885-cards.c +19 -2
index b8ad935..0a3a0ad 100644
@@ -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, &params);
 		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);
Signature · 1 line
1.7.1.1