diff options
-rw-r--r-- | linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c | 141 |
1 files changed, 137 insertions, 4 deletions
diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index ebfe184ab..760cb5c7f 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -200,6 +200,139 @@ static int pvr2_i2c_basic_op(struct pvr2_hdw *hdw, } } +#if 0 +/* This is special code for spying on IR transactions for 29xxx devices. + We use this to reverse-engineer the IR protocol in order to simulate it + correctly for 24xxx devices. */ +static int i2c_29xxx_ir(struct pvr2_hdw *hdw, + u8 i2c_addr,u8 *wdata,u16 wlen,u8 *rdata,u16 rlen) +{ + char buf[200]; + unsigned int c1,c2; + unsigned int idx,stat; + if (!(rlen || wlen)) { + /* This is a probe attempt. Just let it succeed. */ + pvr2_trace(PVR2_TRACE_DEBUG,"IR: probe"); + return 0; + } + stat = pvr2_i2c_basic_op(hdw,i2c_addr,wdata,wlen,rdata,rlen); + if ((wlen == 0) && (rlen == 3) && (stat == 0) && + (rdata[0] == 0) && (rdata[1] == 0) && (rdata[2] == 0xc1)) { + return stat; + } + c1 = 0; + c2 = scnprintf(buf+c1,sizeof(buf)-c1, + "IR: stat=%d",stat); + c1 += c2; + if (wlen) { + c2 = scnprintf(buf+c1,sizeof(buf)-c1, + " write ["); + c1 += c2; + for (idx = 0; idx < wlen; idx++) { + c2 = scnprintf(buf+c1,sizeof(buf)-c1, + "%s%02x",idx == 0 ? "" : " ", + wdata[idx]); + c1 += c2; + } + c2 = scnprintf(buf+c1,sizeof(buf)-c1, + "]"); + c1 += c2; + } + if (rlen && (stat == 0)) { + c2 = scnprintf(buf+c1,sizeof(buf)-c1, + " read ["); + c1 += c2; + for (idx = 0; idx < rlen; idx++) { + c2 = scnprintf(buf+c1,sizeof(buf)-c1, + "%s%02x",idx == 0 ? "" : " ", + rdata[idx]); + c1 += c2; + } + c2 = scnprintf(buf+c1,sizeof(buf)-c1, + "]"); + c1 += c2; + } else if (rlen) { + c2 = scnprintf(buf+c1,sizeof(buf)-c1, + " read cnt=%u",rlen); + c1 += c2; + } + pvr2_trace(PVR2_TRACE_DEBUG,"%.*s",c1,buf); + return stat; +} +#endif + +/* This is a special entry point for cases of I2C transaction attempts to + the IR receiver. The implementation here simulates the IR receiver by + issuing a command to the FX2 firmware and using that response to return + what the real I2C receiver would have returned. We use this for 24xxx + devices, where the IR receiver chip has been removed and replaced with + FX2 related logic. */ +static int i2c_24xxx_ir(struct pvr2_hdw *hdw, + u8 i2c_addr,u8 *wdata,u16 wlen,u8 *rdata,u16 rlen) +{ + u8 dat[4]; + unsigned int stat; + + if (!(rlen || wlen)) { + /* This is a probe attempt. Just let it succeed. */ + return 0; + } + + /* We don't understand this kind of transaction */ + if ((wlen != 0) || (rlen == 0)) return -EIO; + + if (rlen < 3) { + /* Mike Isely <isely@pobox.com> Appears to be a probe + attempt from lirc. Just fill in zeroes and return. If + we try instead to do the full transaction here, then bad + things seem to happen within the lirc driver module + (version 0.8.0-7 sources from Debian, when run under + vanilla 2.6.17.6 kernel) - and I don't have the patience + to chase it down. */ + if (rlen > 0) rdata[0] = 0; + if (rlen > 1) rdata[1] = 0; + return 0; + } + + /* Issue a command to the FX2 to read the IR receiver. */ + LOCK_TAKE(hdw->ctl_lock); do { + hdw->cmd_buffer[0] = 0xec; + stat = pvr2_send_request(hdw, + hdw->cmd_buffer,1, + hdw->cmd_buffer,4); + dat[0] = hdw->cmd_buffer[0]; + dat[1] = hdw->cmd_buffer[1]; + dat[2] = hdw->cmd_buffer[2]; + dat[3] = hdw->cmd_buffer[3]; + } while (0); LOCK_GIVE(hdw->ctl_lock); + + /* Give up if that operation failed. */ + if (stat != 0) return stat; + + /* Mangle the results into something that looks like the real IR + receiver. */ + rdata[2] = 0xc1; + if (dat[0] != 1) { + /* No code received. */ + rdata[0] = 0; + rdata[1] = 0; + } else { + u16 val; + /* Mash the FX2 firmware-provided IR code into something + that the normal i2c chip-level driver expects. */ + val = dat[1]; + val <<= 8; + val |= dat[2]; + val >>= 1; + val &= ~0x0003; + val |= 0x8000; + rdata[0] = (val >> 8) & 0xffu; + rdata[1] = val & 0xffu; + } + + return 0; +} + /* 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 @@ -913,17 +1046,17 @@ 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. + /* 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; } - // If however we're dealing with new hardware, insert some hacks in - // the I2C transfer stack to let things work better. + /* However, deal with various special cases for 24xxx hardware. */ if (hdw->hdw_type == PVR2_HDW_TYPE_24XXX) { hdw->i2c_func[0x1b] = i2c_hack_wm8775; hdw->i2c_func[0x44] = i2c_hack_cx25840; + hdw->i2c_func[0x18] = i2c_24xxx_ir; } // Configure the adapter and set up everything else related to it. |