diff options
Diffstat (limited to 'linux/drivers/media/video')
-rw-r--r-- | linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h | 8 | ||||
-rw-r--r-- | linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c | 85 |
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)); |