summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Isely <isely@pobox.com>2006-04-20 00:12:08 -0500
committerMike Isely <isely@pobox.com>2006-04-20 00:12:08 -0500
commitf3bab8e1b3ea52801b5b256bf203d6e2c946033d (patch)
treef523da77be72a7105e4f90945026455c568ccbee
parentf4549adf17ab1879858c7b8655dfe7ef023e404e (diff)
downloadmediapointer-dvb-s2-f3bab8e1b3ea52801b5b256bf203d6e2c946033d.tar.gz
mediapointer-dvb-s2-f3bab8e1b3ea52801b5b256bf203d6e2c946033d.tar.bz2
Implement I2C transaction filtering in pvrusb2
From: Mike Isely <isely@pobox.com> Implement a rudimentary filter mechanism in the I2C adapter within the pvrusb2 driver. It is possible now to interpose a filtering function on a per-target basis. This function can intercept the requested transaction and perform various manipulations on behalf of the client. With this mechanism is also a filter inserted for wm8775 that causes all probe attempts to the chip to always succeed (just return success on probe attempts without touching the hardware). This neatly solves the problem in the PVR USB2 hardware where we can't seem able to probe the chip at all. Signed-off-by: Mike Isely <isely@pobox.com>
-rw-r--r--linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h8
-rw-r--r--linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c85
2 files changed, 83 insertions, 10 deletions
diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
index ee0885ef1..ec6c35b8d 100644
--- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
+++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
@@ -191,6 +191,9 @@ struct pvr2_decoder_ctrl {
#define PVR2_HDW_TYPE_24XXX 1
#endif
+typedef int (*pvr2_i2c_func)(struct pvr2_hdw *,u8,u8 *,u16,u8 *, u16);
+#define PVR2_I2C_FUNC_CNT 128
+
/* This structure contains all state data directly needed to
manipulate the hardware (as opposed to complying with a kernel
interface) */
@@ -221,6 +224,7 @@ struct pvr2_hdw {
/* I2C stuff */
struct i2c_adapter i2c_adap;
struct i2c_algorithm i2c_algo;
+ pvr2_i2c_func i2c_func[PVR2_I2C_FUNC_CNT];
int i2c_linked;
unsigned int i2c_pend_types; /* Which types of update are needed */
unsigned long i2c_pend_mask; /* Change bits we need to scan */
@@ -365,6 +369,10 @@ void pvr2_hdw_subsys_stream_bit_chg_no_lock(struct pvr2_hdw *hdw,
void pvr2_hdw_internal_find_stdenum(struct pvr2_hdw *hdw);
void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw);
+int pvr2_i2c_basic_op(struct pvr2_hdw *,u8 i2c_addr,
+ u8 *wdata,u16 wlen,
+ u8 *rdata,u16 rlen);
+
#endif /* __PVRUSB2_HDW_INTERNAL_H */
/*
diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
index 9a96273f2..e1d3803b3 100644
--- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
+++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
@@ -164,6 +164,38 @@ static int pvr2_i2c_read(struct pvr2_hdw *hdw, /* Context */
return ret;
}
+/* This is the common low level entry point for doing I2C operations to the
+ hardware. */
+int pvr2_i2c_basic_op(struct pvr2_hdw *hdw,
+ u8 i2c_addr,
+ u8 *wdata,
+ u16 wlen,
+ u8 *rdata,
+ u16 rlen)
+{
+ if (!rdata) rlen = 0;
+ if (!wdata) wlen = 0;
+ if (rlen || !wlen) {
+ return pvr2_i2c_read(hdw,i2c_addr,wdata,wlen,rdata,rlen);
+ } else {
+ return pvr2_i2c_write(hdw,i2c_addr,wdata,wlen);
+ }
+}
+
+/* This is a special entry point that is entered if an I2C operation is
+ attempted to a wm8775 chip on model 24xxx hardware. Autodetect of this
+ part doesn't work, but we know it is really there. So let's look for
+ the autodetect attempt and just return success if we see that. */
+static int i2c_hack_wm8775(struct pvr2_hdw *hdw,
+ u8 i2c_addr,u8 *wdata,u16 wlen,u8 *rdata,u16 rlen)
+{
+ if (!(rlen || wlen)) {
+ // This is a probe attempt. Just let it succeed.
+ return 0;
+ }
+ return pvr2_i2c_basic_op(hdw,i2c_addr,wdata,wlen,rdata,rlen);
+}
+
/* This is a very, very limited I2C adapter implementation. We can only
support what we actually know will work on the device... */
static int pvr2_i2c_xfer(struct i2c_adapter *i2c_adap,
@@ -171,12 +203,24 @@ static int pvr2_i2c_xfer(struct i2c_adapter *i2c_adap,
int num)
{
int ret = -ENOTSUPP;
+ pvr2_i2c_func funcp = 0;
struct pvr2_hdw *hdw = (struct pvr2_hdw *)(i2c_adap->algo_data);
+ if (!num) {
+ ret = -EINVAL;
+ goto done;
+ }
if ((msgs[0].flags & I2C_M_NOSTART)) {
trace_i2c("i2c refusing I2C_M_NOSTART");
goto done;
}
+ if (msgs[0].addr < PVR2_I2C_FUNC_CNT) {
+ funcp = hdw->i2c_func[msgs[0].addr];
+ }
+ if (!funcp) {
+ ret = -EIO;
+ goto done;
+ }
if (num == 1) {
if (msgs[0].flags & I2C_M_RD) {
@@ -184,8 +228,7 @@ static int pvr2_i2c_xfer(struct i2c_adapter *i2c_adap,
u16 tcnt,bcnt,offs;
if (!msgs[0].len) {
/* Length == 0 read. This is a probe. */
- if (pvr2_i2c_read(hdw,msgs[0].addr,
- 0,0,0,0)) {
+ if (funcp(hdw,msgs[0].addr,0,0,0,0)) {
ret = -EIO;
goto done;
}
@@ -202,9 +245,8 @@ static int pvr2_i2c_xfer(struct i2c_adapter *i2c_adap,
if (bcnt > sizeof(hdw->cmd_buffer)-1) {
bcnt = sizeof(hdw->cmd_buffer)-1;
}
- if (pvr2_i2c_read(hdw,msgs[0].addr,
- 0,0,
- msgs[0].buf+offs,bcnt)) {
+ if (funcp(hdw,msgs[0].addr,0,0,
+ msgs[0].buf+offs,bcnt)) {
ret = -EIO;
goto done;
}
@@ -216,13 +258,19 @@ static int pvr2_i2c_xfer(struct i2c_adapter *i2c_adap,
} else {
/* Simple write */
ret = 1;
- if (pvr2_i2c_write(hdw,msgs[0].addr,
- msgs[0].buf,msgs[0].len)) {
+ if (funcp(hdw,msgs[0].addr,
+ msgs[0].buf,msgs[0].len,0,0)) {
ret = -EIO;
}
goto done;
}
} else if (num == 2) {
+ if (msgs[0].addr != msgs[1].addr) {
+ trace_i2c("i2c refusing 2 phase transfer with"
+ " conflicting target addresses");
+ ret = -ENOTSUPP;
+ goto done;
+ }
if ((!((msgs[0].flags & I2C_M_RD))) &&
(msgs[1].flags & I2C_M_RD)) {
u16 tcnt,bcnt,wcnt,offs;
@@ -238,9 +286,9 @@ static int pvr2_i2c_xfer(struct i2c_adapter *i2c_adap,
if (bcnt > sizeof(hdw->cmd_buffer)-1) {
bcnt = sizeof(hdw->cmd_buffer)-1;
}
- if (pvr2_i2c_read(hdw,msgs[0].addr,
- msgs[0].buf,wcnt,
- msgs[1].buf+offs,bcnt)) {
+ if (funcp(hdw,msgs[0].addr,
+ msgs[0].buf,wcnt,
+ msgs[1].buf+offs,bcnt)) {
ret = -EIO;
goto done;
}
@@ -757,6 +805,23 @@ static void do_i2c_scan(struct pvr2_hdw *hdw)
void pvr2_i2c_core_init(struct pvr2_hdw *hdw)
{
+ unsigned int idx;
+
+ // The default action for all possible I2C addresses is just to do
+ // the transfer normally.
+ for (idx = 0; idx < PVR2_I2C_FUNC_CNT; idx++) {
+ hdw->i2c_func[idx] = pvr2_i2c_basic_op;
+ }
+
+#ifdef CONFIG_VIDEO_PVRUSB2_24XXX
+ // If however we're dealing with new hardware, insert some hacks in
+ // the I2C transfer stack to let things work better.
+ if (hdw->hdw_type == PVR2_HDW_TYPE_24XXX) {
+ hdw->i2c_func[0x1b] = i2c_hack_wm8775;
+ }
+#endif
+
+ // Configure the adapter and set up everything else related to it.
memcpy(&hdw->i2c_adap,&pvr2_i2c_adap_template,sizeof(hdw->i2c_adap));
memcpy(&hdw->i2c_algo,&pvr2_i2c_algo_template,sizeof(hdw->i2c_algo));
strlcpy(hdw->i2c_adap.name,hdw->name,sizeof(hdw->i2c_adap.name));