123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935 |
- /*
- *
- * $Id$
- *
- * Copyright (C) 2005 Mike Isely <isely@pobox.com>
- *
- * 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
- *
- * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
- #include "pvrusb2-i2c-core.h"
- #include "pvrusb2-hdw-internal.h"
- #include "pvrusb2-debug.h"
- #define trace_i2c(...) pvr2_trace(PVR2_TRACE_I2C,__VA_ARGS__)
- /*
- This module attempts to implement a compliant I2C adapter for the pvrusb2
- device. By doing this we can then make use of existing functionality in
- V4L (e.g. tuner.c) rather than rolling our own.
- */
- static unsigned int i2c_scan = 0;
- module_param(i2c_scan, int, S_IRUGO|S_IWUSR);
- MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time");
- static unsigned int pvr2_i2c_client_describe(struct pvr2_i2c_client *cp,
- unsigned int detail,
- char *buf,unsigned int maxlen);
- static int pvr2_i2c_write(struct pvr2_hdw *hdw, /* Context */
- u8 i2c_addr, /* I2C address we're talking to */
- u8 *data, /* Data to write */
- u16 length) /* Size of data to write */
- {
- /* Return value - default 0 means success */
- int ret;
- if (!data) length = 0;
- if (length > (sizeof(hdw->cmd_buffer) - 3)) {
- pvr2_trace(PVR2_TRACE_ERROR_LEGS,
- "Killing an I2C write to %u that is too large"
- " (desired=%u limit=%u)",
- i2c_addr,
- length,(unsigned int)(sizeof(hdw->cmd_buffer) - 3));
- return -ENOTSUPP;
- }
- LOCK_TAKE(hdw->ctl_lock);
- /* Clear the command buffer (likely to be paranoia) */
- memset(hdw->cmd_buffer, 0, sizeof(hdw->cmd_buffer));
- /* Set up command buffer for an I2C write */
- hdw->cmd_buffer[0] = 0x08; /* write prefix */
- hdw->cmd_buffer[1] = i2c_addr; /* i2c addr of chip */
- hdw->cmd_buffer[2] = length; /* length of what follows */
- if (length) memcpy(hdw->cmd_buffer + 3, data, length);
- /* Do the operation */
- ret = pvr2_send_request(hdw,
- hdw->cmd_buffer,
- length + 3,
- hdw->cmd_buffer,
- 1);
- if (!ret) {
- if (hdw->cmd_buffer[0] != 8) {
- ret = -EIO;
- if (hdw->cmd_buffer[0] != 7) {
- trace_i2c("unexpected status"
- " from i2_write[%d]: %d",
- i2c_addr,hdw->cmd_buffer[0]);
- }
- }
- }
- LOCK_GIVE(hdw->ctl_lock);
- return ret;
- }
- static int pvr2_i2c_read(struct pvr2_hdw *hdw, /* Context */
- u8 i2c_addr, /* I2C address we're talking to */
- u8 *data, /* Data to write */
- u16 dlen, /* Size of data to write */
- u8 *res, /* Where to put data we read */
- u16 rlen) /* Amount of data to read */
- {
- /* Return value - default 0 means success */
- int ret;
- if (!data) dlen = 0;
- if (dlen > (sizeof(hdw->cmd_buffer) - 4)) {
- pvr2_trace(PVR2_TRACE_ERROR_LEGS,
- "Killing an I2C read to %u that has wlen too large"
- " (desired=%u limit=%u)",
- i2c_addr,
- dlen,(unsigned int)(sizeof(hdw->cmd_buffer) - 4));
- return -ENOTSUPP;
- }
- if (res && (rlen > (sizeof(hdw->cmd_buffer) - 1))) {
- pvr2_trace(PVR2_TRACE_ERROR_LEGS,
- "Killing an I2C read to %u that has rlen too large"
- " (desired=%u limit=%u)",
- i2c_addr,
- rlen,(unsigned int)(sizeof(hdw->cmd_buffer) - 1));
- return -ENOTSUPP;
- }
- LOCK_TAKE(hdw->ctl_lock);
- /* Clear the command buffer (likely to be paranoia) */
- memset(hdw->cmd_buffer, 0, sizeof(hdw->cmd_buffer));
- /* Set up command buffer for an I2C write followed by a read */
- hdw->cmd_buffer[0] = 0x09; /* read prefix */
- hdw->cmd_buffer[1] = dlen; /* arg length */
- hdw->cmd_buffer[2] = rlen; /* answer length. Device will send one
- more byte (status). */
- hdw->cmd_buffer[3] = i2c_addr; /* i2c addr of chip */
- if (dlen) memcpy(hdw->cmd_buffer + 4, data, dlen);
- /* Do the operation */
- ret = pvr2_send_request(hdw,
- hdw->cmd_buffer,
- 4 + dlen,
- hdw->cmd_buffer,
- rlen + 1);
- if (!ret) {
- if (hdw->cmd_buffer[0] != 8) {
- ret = -EIO;
- if (hdw->cmd_buffer[0] != 7) {
- trace_i2c("unexpected status"
- " from i2_read[%d]: %d",
- i2c_addr,hdw->cmd_buffer[0]);
- }
- }
- }
- /* Copy back the result */
- if (res && rlen) {
- if (ret) {
- /* Error, just blank out the return buffer */
- memset(res, 0, rlen);
- } else {
- memcpy(res, hdw->cmd_buffer + 1, rlen);
- }
- }
- LOCK_GIVE(hdw->ctl_lock);
- return ret;
- }
- /* This is the common low level entry point for doing I2C operations to the
- hardware. */
- static 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 special entry point that is entered if an I2C operation is
- attempted to a cx25840 chip on model 24xxx hardware. This chip can
- sometimes wedge itself. Worse still, when this happens msp3400 can
- falsely detect this part and then the system gets hosed up after msp3400
- gets confused and dies. What we want to do here is try to keep msp3400
- away and also try to notice if the chip is wedged and send a warning to
- the system log. */
- static int i2c_hack_cx25840(struct pvr2_hdw *hdw,
- u8 i2c_addr,u8 *wdata,u16 wlen,u8 *rdata,u16 rlen)
- {
- int ret;
- unsigned int subaddr;
- u8 wbuf[2];
- int state = hdw->i2c_cx25840_hack_state;
- if (!(rlen || wlen)) {
- // Probe attempt - always just succeed and don't bother the
- // hardware (this helps to make the state machine further
- // down somewhat easier).
- return 0;
- }
- if (state == 3) {
- return pvr2_i2c_basic_op(hdw,i2c_addr,wdata,wlen,rdata,rlen);
- }
- /* We're looking for the exact pattern where the revision register
- is being read. The cx25840 module will always look at the
- revision register first. Any other pattern of access therefore
- has to be a probe attempt from somebody else so we'll reject it.
- Normally we could just let each client just probe the part
- anyway, but when the cx25840 is wedged, msp3400 will get a false
- positive and that just screws things up... */
- if (wlen == 0) {
- switch (state) {
- case 1: subaddr = 0x0100; break;
- case 2: subaddr = 0x0101; break;
- default: goto fail;
- }
- } else if (wlen == 2) {
- subaddr = (wdata[0] << 8) | wdata[1];
- switch (subaddr) {
- case 0x0100: state = 1; break;
- case 0x0101: state = 2; break;
- default: goto fail;
- }
- } else {
- goto fail;
- }
- if (!rlen) goto success;
- state = 0;
- if (rlen != 1) goto fail;
- /* If we get to here then we have a legitimate read for one of the
- two revision bytes, so pass it through. */
- wbuf[0] = subaddr >> 8;
- wbuf[1] = subaddr;
- ret = pvr2_i2c_basic_op(hdw,i2c_addr,wbuf,2,rdata,rlen);
- if ((ret != 0) || (*rdata == 0x04) || (*rdata == 0x0a)) {
- pvr2_trace(PVR2_TRACE_ERROR_LEGS,
- "WARNING: Detected a wedged cx25840 chip;"
- " the device will not work.");
- pvr2_trace(PVR2_TRACE_ERROR_LEGS,
- "WARNING: Try power cycling the pvrusb2 device.");
- pvr2_trace(PVR2_TRACE_ERROR_LEGS,
- "WARNING: Disabling further access to the device"
- " to prevent other foul-ups.");
- // This blocks all further communication with the part.
- hdw->i2c_func[0x44] = NULL;
- pvr2_hdw_render_useless(hdw);
- goto fail;
- }
- /* Success! */
- pvr2_trace(PVR2_TRACE_CHIPS,"cx25840 appears to be OK.");
- state = 3;
- success:
- hdw->i2c_cx25840_hack_state = state;
- return 0;
- fail:
- hdw->i2c_cx25840_hack_state = state;
- return -EIO;
- }
- /* 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,
- struct i2c_msg msgs[],
- int num)
- {
- int ret = -ENOTSUPP;
- pvr2_i2c_func funcp = NULL;
- 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) {
- /* Simple read */
- u16 tcnt,bcnt,offs;
- if (!msgs[0].len) {
- /* Length == 0 read. This is a probe. */
- if (funcp(hdw,msgs[0].addr,NULL,0,NULL,0)) {
- ret = -EIO;
- goto done;
- }
- ret = 1;
- goto done;
- }
- /* If the read is short enough we'll do the whole
- thing atomically. Otherwise we have no choice
- but to break apart the reads. */
- tcnt = msgs[0].len;
- offs = 0;
- while (tcnt) {
- bcnt = tcnt;
- if (bcnt > sizeof(hdw->cmd_buffer)-1) {
- bcnt = sizeof(hdw->cmd_buffer)-1;
- }
- if (funcp(hdw,msgs[0].addr,NULL,0,
- msgs[0].buf+offs,bcnt)) {
- ret = -EIO;
- goto done;
- }
- offs += bcnt;
- tcnt -= bcnt;
- }
- ret = 1;
- goto done;
- } else {
- /* Simple write */
- ret = 1;
- if (funcp(hdw,msgs[0].addr,
- msgs[0].buf,msgs[0].len,NULL,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;
- /* Write followed by atomic read. If the read
- portion is short enough we'll do the whole thing
- atomically. Otherwise we have no choice but to
- break apart the reads. */
- tcnt = msgs[1].len;
- wcnt = msgs[0].len;
- offs = 0;
- while (tcnt || wcnt) {
- bcnt = tcnt;
- if (bcnt > sizeof(hdw->cmd_buffer)-1) {
- bcnt = sizeof(hdw->cmd_buffer)-1;
- }
- if (funcp(hdw,msgs[0].addr,
- msgs[0].buf,wcnt,
- msgs[1].buf+offs,bcnt)) {
- ret = -EIO;
- goto done;
- }
- offs += bcnt;
- tcnt -= bcnt;
- wcnt = 0;
- }
- ret = 2;
- goto done;
- } else {
- trace_i2c("i2c refusing complex transfer"
- " read0=%d read1=%d",
- (msgs[0].flags & I2C_M_RD),
- (msgs[1].flags & I2C_M_RD));
- }
- } else {
- trace_i2c("i2c refusing %d phase transfer",num);
- }
- done:
- if (pvrusb2_debug & PVR2_TRACE_I2C_TRAF) {
- unsigned int idx,offs,cnt;
- for (idx = 0; idx < num; idx++) {
- cnt = msgs[idx].len;
- printk(KERN_INFO
- "pvrusb2 i2c xfer %u/%u:"
- " addr=0x%x len=%d %s%s",
- idx+1,num,
- msgs[idx].addr,
- cnt,
- (msgs[idx].flags & I2C_M_RD ?
- "read" : "write"),
- (msgs[idx].flags & I2C_M_NOSTART ?
- " nostart" : ""));
- if ((ret > 0) || !(msgs[idx].flags & I2C_M_RD)) {
- if (cnt > 8) cnt = 8;
- printk(" [");
- for (offs = 0; offs < (cnt>8?8:cnt); offs++) {
- if (offs) printk(" ");
- printk("%02x",msgs[idx].buf[offs]);
- }
- if (offs < cnt) printk(" ...");
- printk("]");
- }
- if (idx+1 == num) {
- printk(" result=%d",ret);
- }
- printk("\n");
- }
- if (!num) {
- printk(KERN_INFO
- "pvrusb2 i2c xfer null transfer result=%d\n",
- ret);
- }
- }
- return ret;
- }
- static int pvr2_i2c_control(struct i2c_adapter *adapter,
- unsigned int cmd, unsigned long arg)
- {
- return 0;
- }
- static u32 pvr2_i2c_functionality(struct i2c_adapter *adap)
- {
- return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE_DATA;
- }
- static int pvr2_i2c_core_singleton(struct i2c_client *cp,
- unsigned int cmd,void *arg)
- {
- int stat;
- if (!cp) return -EINVAL;
- if (!(cp->driver)) return -EINVAL;
- if (!(cp->driver->command)) return -EINVAL;
- if (!try_module_get(cp->driver->driver.owner)) return -EAGAIN;
- stat = cp->driver->command(cp,cmd,arg);
- module_put(cp->driver->driver.owner);
- return stat;
- }
- int pvr2_i2c_client_cmd(struct pvr2_i2c_client *cp,unsigned int cmd,void *arg)
- {
- int stat;
- if (pvrusb2_debug & PVR2_TRACE_I2C_CMD) {
- char buf[100];
- unsigned int cnt;
- cnt = pvr2_i2c_client_describe(cp,PVR2_I2C_DETAIL_DEBUG,
- buf,sizeof(buf));
- pvr2_trace(PVR2_TRACE_I2C_CMD,
- "i2c COMMAND (code=%u 0x%x) to %.*s",
- cmd,cmd,cnt,buf);
- }
- stat = pvr2_i2c_core_singleton(cp->client,cmd,arg);
- if (pvrusb2_debug & PVR2_TRACE_I2C_CMD) {
- char buf[100];
- unsigned int cnt;
- cnt = pvr2_i2c_client_describe(cp,PVR2_I2C_DETAIL_DEBUG,
- buf,sizeof(buf));
- pvr2_trace(PVR2_TRACE_I2C_CMD,
- "i2c COMMAND to %.*s (ret=%d)",cnt,buf,stat);
- }
- return stat;
- }
- int pvr2_i2c_core_cmd(struct pvr2_hdw *hdw,unsigned int cmd,void *arg)
- {
- struct list_head *item,*nc;
- struct pvr2_i2c_client *cp;
- int stat = -EINVAL;
- if (!hdw) return stat;
- mutex_lock(&hdw->i2c_list_lock);
- list_for_each_safe(item,nc,&hdw->i2c_clients) {
- cp = list_entry(item,struct pvr2_i2c_client,list);
- if (!cp->recv_enable) continue;
- mutex_unlock(&hdw->i2c_list_lock);
- stat = pvr2_i2c_client_cmd(cp,cmd,arg);
- mutex_lock(&hdw->i2c_list_lock);
- }
- mutex_unlock(&hdw->i2c_list_lock);
- return stat;
- }
- static int handler_check(struct pvr2_i2c_client *cp)
- {
- struct pvr2_i2c_handler *hp = cp->handler;
- if (!hp) return 0;
- if (!hp->func_table->check) return 0;
- return hp->func_table->check(hp->func_data) != 0;
- }
- #define BUFSIZE 500
- void pvr2_i2c_core_sync(struct pvr2_hdw *hdw)
- {
- unsigned long msk;
- unsigned int idx;
- struct list_head *item,*nc;
- struct pvr2_i2c_client *cp;
- if (!hdw->i2c_linked) return;
- if (!(hdw->i2c_pend_types & PVR2_I2C_PEND_ALL)) {
- return;
- }
- mutex_lock(&hdw->i2c_list_lock); do {
- pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: core_sync BEGIN");
- if (hdw->i2c_pend_types & PVR2_I2C_PEND_DETECT) {
- /* One or more I2C clients have attached since we
- last synced. So scan the list and identify the
- new clients. */
- char *buf;
- unsigned int cnt;
- unsigned long amask = 0;
- buf = kmalloc(BUFSIZE,GFP_KERNEL);
- pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: PEND_DETECT");
- hdw->i2c_pend_types &= ~PVR2_I2C_PEND_DETECT;
- list_for_each(item,&hdw->i2c_clients) {
- cp = list_entry(item,struct pvr2_i2c_client,
- list);
- if (!cp->detected_flag) {
- cp->ctl_mask = 0;
- pvr2_i2c_probe(hdw,cp);
- cp->detected_flag = !0;
- msk = cp->ctl_mask;
- cnt = 0;
- if (buf) {
- cnt = pvr2_i2c_client_describe(
- cp,
- PVR2_I2C_DETAIL_ALL,
- buf,BUFSIZE);
- }
- trace_i2c("Probed: %.*s",cnt,buf);
- if (handler_check(cp)) {
- hdw->i2c_pend_types |=
- PVR2_I2C_PEND_CLIENT;
- }
- cp->pend_mask = msk;
- hdw->i2c_pend_mask |= msk;
- hdw->i2c_pend_types |=
- PVR2_I2C_PEND_REFRESH;
- }
- amask |= cp->ctl_mask;
- }
- hdw->i2c_active_mask = amask;
- if (buf) kfree(buf);
- }
- if (hdw->i2c_pend_types & PVR2_I2C_PEND_STALE) {
- /* Need to do one or more global updates. Arrange
- for this to happen. */
- unsigned long m2;
- pvr2_trace(PVR2_TRACE_I2C_CORE,
- "i2c: PEND_STALE (0x%lx)",
- hdw->i2c_stale_mask);
- hdw->i2c_pend_types &= ~PVR2_I2C_PEND_STALE;
- list_for_each(item,&hdw->i2c_clients) {
- cp = list_entry(item,struct pvr2_i2c_client,
- list);
- m2 = hdw->i2c_stale_mask;
- m2 &= cp->ctl_mask;
- m2 &= ~cp->pend_mask;
- if (m2) {
- pvr2_trace(PVR2_TRACE_I2C_CORE,
- "i2c: cp=%p setting 0x%lx",
- cp,m2);
- cp->pend_mask |= m2;
- }
- }
- hdw->i2c_pend_mask |= hdw->i2c_stale_mask;
- hdw->i2c_stale_mask = 0;
- hdw->i2c_pend_types |= PVR2_I2C_PEND_REFRESH;
- }
- if (hdw->i2c_pend_types & PVR2_I2C_PEND_CLIENT) {
- /* One or more client handlers are asking for an
- update. Run through the list of known clients
- and update each one. */
- pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: PEND_CLIENT");
- hdw->i2c_pend_types &= ~PVR2_I2C_PEND_CLIENT;
- list_for_each_safe(item,nc,&hdw->i2c_clients) {
- cp = list_entry(item,struct pvr2_i2c_client,
- list);
- if (!cp->handler) continue;
- if (!cp->handler->func_table->update) continue;
- pvr2_trace(PVR2_TRACE_I2C_CORE,
- "i2c: cp=%p update",cp);
- mutex_unlock(&hdw->i2c_list_lock);
- cp->handler->func_table->update(
- cp->handler->func_data);
- mutex_lock(&hdw->i2c_list_lock);
- /* If client's update function set some
- additional pending bits, account for that
- here. */
- if (cp->pend_mask & ~hdw->i2c_pend_mask) {
- hdw->i2c_pend_mask |= cp->pend_mask;
- hdw->i2c_pend_types |=
- PVR2_I2C_PEND_REFRESH;
- }
- }
- }
- if (hdw->i2c_pend_types & PVR2_I2C_PEND_REFRESH) {
- const struct pvr2_i2c_op *opf;
- unsigned long pm;
- /* Some actual updates are pending. Walk through
- each update type and perform it. */
- pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: PEND_REFRESH"
- " (0x%lx)",hdw->i2c_pend_mask);
- hdw->i2c_pend_types &= ~PVR2_I2C_PEND_REFRESH;
- pm = hdw->i2c_pend_mask;
- hdw->i2c_pend_mask = 0;
- for (idx = 0, msk = 1; pm; idx++, msk <<= 1) {
- if (!(pm & msk)) continue;
- pm &= ~msk;
- list_for_each(item,&hdw->i2c_clients) {
- cp = list_entry(item,
- struct pvr2_i2c_client,
- list);
- if (cp->pend_mask & msk) {
- cp->pend_mask &= ~msk;
- cp->recv_enable = !0;
- } else {
- cp->recv_enable = 0;
- }
- }
- opf = pvr2_i2c_get_op(idx);
- if (!opf) continue;
- mutex_unlock(&hdw->i2c_list_lock);
- opf->update(hdw);
- mutex_lock(&hdw->i2c_list_lock);
- }
- }
- pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: core_sync END");
- } while (0); mutex_unlock(&hdw->i2c_list_lock);
- }
- int pvr2_i2c_core_check_stale(struct pvr2_hdw *hdw)
- {
- unsigned long msk,sm,pm;
- unsigned int idx;
- const struct pvr2_i2c_op *opf;
- struct list_head *item;
- struct pvr2_i2c_client *cp;
- unsigned int pt = 0;
- pvr2_trace(PVR2_TRACE_I2C_CORE,"pvr2_i2c_core_check_stale BEGIN");
- pm = hdw->i2c_active_mask;
- sm = 0;
- for (idx = 0, msk = 1; pm; idx++, msk <<= 1) {
- if (!(msk & pm)) continue;
- pm &= ~msk;
- opf = pvr2_i2c_get_op(idx);
- if (!opf) continue;
- if (opf->check(hdw)) {
- sm |= msk;
- }
- }
- if (sm) pt |= PVR2_I2C_PEND_STALE;
- list_for_each(item,&hdw->i2c_clients) {
- cp = list_entry(item,struct pvr2_i2c_client,list);
- if (!handler_check(cp)) continue;
- pt |= PVR2_I2C_PEND_CLIENT;
- }
- if (pt) {
- mutex_lock(&hdw->i2c_list_lock); do {
- hdw->i2c_pend_types |= pt;
- hdw->i2c_stale_mask |= sm;
- hdw->i2c_pend_mask |= hdw->i2c_stale_mask;
- } while (0); mutex_unlock(&hdw->i2c_list_lock);
- }
- pvr2_trace(PVR2_TRACE_I2C_CORE,
- "i2c: types=0x%x stale=0x%lx pend=0x%lx",
- hdw->i2c_pend_types,
- hdw->i2c_stale_mask,
- hdw->i2c_pend_mask);
- pvr2_trace(PVR2_TRACE_I2C_CORE,"pvr2_i2c_core_check_stale END");
- return (hdw->i2c_pend_types & PVR2_I2C_PEND_ALL) != 0;
- }
- static unsigned int pvr2_i2c_client_describe(struct pvr2_i2c_client *cp,
- unsigned int detail,
- char *buf,unsigned int maxlen)
- {
- unsigned int ccnt,bcnt;
- int spcfl = 0;
- const struct pvr2_i2c_op *opf;
- ccnt = 0;
- if (detail & PVR2_I2C_DETAIL_DEBUG) {
- bcnt = scnprintf(buf,maxlen,
- "ctxt=%p ctl_mask=0x%lx",
- cp,cp->ctl_mask);
- ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
- spcfl = !0;
- }
- bcnt = scnprintf(buf,maxlen,
- "%s%s @ 0x%x",
- (spcfl ? " " : ""),
- cp->client->name,
- cp->client->addr);
- ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
- if ((detail & PVR2_I2C_DETAIL_HANDLER) &&
- cp->handler && cp->handler->func_table->describe) {
- bcnt = scnprintf(buf,maxlen," (");
- ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
- bcnt = cp->handler->func_table->describe(
- cp->handler->func_data,buf,maxlen);
- ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
- bcnt = scnprintf(buf,maxlen,")");
- ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
- }
- if ((detail & PVR2_I2C_DETAIL_CTLMASK) && cp->ctl_mask) {
- unsigned int idx;
- unsigned long msk,sm;
- int spcfl;
- bcnt = scnprintf(buf,maxlen," [");
- ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
- sm = 0;
- spcfl = 0;
- for (idx = 0, msk = 1; msk; idx++, msk <<= 1) {
- if (!(cp->ctl_mask & msk)) continue;
- opf = pvr2_i2c_get_op(idx);
- if (opf) {
- bcnt = scnprintf(buf,maxlen,"%s%s",
- spcfl ? " " : "",
- opf->name);
- ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
- spcfl = !0;
- } else {
- sm |= msk;
- }
- }
- if (sm) {
- bcnt = scnprintf(buf,maxlen,"%s%lx",
- idx != 0 ? " " : "",sm);
- ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
- }
- bcnt = scnprintf(buf,maxlen,"]");
- ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
- }
- return ccnt;
- }
- unsigned int pvr2_i2c_report(struct pvr2_hdw *hdw,
- char *buf,unsigned int maxlen)
- {
- unsigned int ccnt,bcnt;
- struct list_head *item;
- struct pvr2_i2c_client *cp;
- ccnt = 0;
- mutex_lock(&hdw->i2c_list_lock); do {
- list_for_each(item,&hdw->i2c_clients) {
- cp = list_entry(item,struct pvr2_i2c_client,list);
- bcnt = pvr2_i2c_client_describe(
- cp,
- (PVR2_I2C_DETAIL_HANDLER|
- PVR2_I2C_DETAIL_CTLMASK),
- buf,maxlen);
- ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
- bcnt = scnprintf(buf,maxlen,"\n");
- ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
- }
- } while (0); mutex_unlock(&hdw->i2c_list_lock);
- return ccnt;
- }
- static int pvr2_i2c_attach_inform(struct i2c_client *client)
- {
- struct pvr2_hdw *hdw = (struct pvr2_hdw *)(client->adapter->algo_data);
- struct pvr2_i2c_client *cp;
- int fl = !(hdw->i2c_pend_types & PVR2_I2C_PEND_ALL);
- cp = kmalloc(sizeof(*cp),GFP_KERNEL);
- trace_i2c("i2c_attach [client=%s @ 0x%x ctxt=%p]",
- client->name,
- client->addr,cp);
- if (!cp) return -ENOMEM;
- memset(cp,0,sizeof(*cp));
- INIT_LIST_HEAD(&cp->list);
- cp->client = client;
- mutex_lock(&hdw->i2c_list_lock); do {
- list_add_tail(&cp->list,&hdw->i2c_clients);
- hdw->i2c_pend_types |= PVR2_I2C_PEND_DETECT;
- } while (0); mutex_unlock(&hdw->i2c_list_lock);
- if (fl) pvr2_hdw_poll_trigger_unlocked(hdw);
- return 0;
- }
- static int pvr2_i2c_detach_inform(struct i2c_client *client)
- {
- struct pvr2_hdw *hdw = (struct pvr2_hdw *)(client->adapter->algo_data);
- struct pvr2_i2c_client *cp;
- struct list_head *item,*nc;
- unsigned long amask = 0;
- int foundfl = 0;
- mutex_lock(&hdw->i2c_list_lock); do {
- list_for_each_safe(item,nc,&hdw->i2c_clients) {
- cp = list_entry(item,struct pvr2_i2c_client,list);
- if (cp->client == client) {
- trace_i2c("pvr2_i2c_detach"
- " [client=%s @ 0x%x ctxt=%p]",
- client->name,
- client->addr,cp);
- if (cp->handler &&
- cp->handler->func_table->detach) {
- cp->handler->func_table->detach(
- cp->handler->func_data);
- }
- list_del(&cp->list);
- kfree(cp);
- foundfl = !0;
- continue;
- }
- amask |= cp->ctl_mask;
- }
- hdw->i2c_active_mask = amask;
- } while (0); mutex_unlock(&hdw->i2c_list_lock);
- if (!foundfl) {
- trace_i2c("pvr2_i2c_detach [client=%s @ 0x%x ctxt=<unknown>]",
- client->name,
- client->addr);
- }
- return 0;
- }
- static struct i2c_algorithm pvr2_i2c_algo_template = {
- .master_xfer = pvr2_i2c_xfer,
- .algo_control = pvr2_i2c_control,
- .functionality = pvr2_i2c_functionality,
- };
- static struct i2c_adapter pvr2_i2c_adap_template = {
- .owner = THIS_MODULE,
- .class = I2C_CLASS_TV_ANALOG,
- .id = I2C_HW_B_BT848,
- .client_register = pvr2_i2c_attach_inform,
- .client_unregister = pvr2_i2c_detach_inform,
- };
- static void do_i2c_scan(struct pvr2_hdw *hdw)
- {
- struct i2c_msg msg[1];
- int i,rc;
- msg[0].addr = 0;
- msg[0].flags = I2C_M_RD;
- msg[0].len = 0;
- msg[0].buf = NULL;
- printk("%s: i2c scan beginning\n",hdw->name);
- for (i = 0; i < 128; i++) {
- msg[0].addr = i;
- rc = i2c_transfer(&hdw->i2c_adap,msg,
- sizeof(msg)/sizeof(msg[0]));
- if (rc != 1) continue;
- printk("%s: i2c scan: found device @ 0x%x\n",hdw->name,i);
- }
- printk("%s: i2c scan done.\n",hdw->name);
- }
- 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;
- }
- // 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;
- hdw->i2c_func[0x44] = i2c_hack_cx25840;
- }
- // 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));
- hdw->i2c_adap.algo = &hdw->i2c_algo;
- hdw->i2c_adap.algo_data = hdw;
- hdw->i2c_pend_mask = 0;
- hdw->i2c_stale_mask = 0;
- hdw->i2c_active_mask = 0;
- INIT_LIST_HEAD(&hdw->i2c_clients);
- mutex_init(&hdw->i2c_list_lock);
- hdw->i2c_linked = !0;
- i2c_add_adapter(&hdw->i2c_adap);
- if (i2c_scan) do_i2c_scan(hdw);
- }
- void pvr2_i2c_core_done(struct pvr2_hdw *hdw)
- {
- if (hdw->i2c_linked) {
- i2c_del_adapter(&hdw->i2c_adap);
- hdw->i2c_linked = 0;
- }
- }
- /*
- Stuff for Emacs to see, in order to encourage consistent editing style:
- *** Local Variables: ***
- *** mode: c ***
- *** fill-column: 75 ***
- *** tab-width: 8 ***
- *** c-basic-offset: 8 ***
- *** End: ***
- */
|