diff options
Diffstat (limited to 'linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c')
-rw-r--r-- | linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c | 482 |
1 files changed, 56 insertions, 426 deletions
diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index d95abcc55..dbc0551f0 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -18,6 +18,7 @@ * */ +#include <linux/i2c.h> #include "pvrusb2-i2c-core.h" #include "pvrusb2-hdw-internal.h" #include "pvrusb2-debug.h" @@ -30,8 +31,7 @@ /* 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. + device. */ @@ -43,9 +43,17 @@ static int ir_mode[PVR_NUM] = { [0 ... PVR_NUM-1] = 1 }; module_param_array(ir_mode, int, NULL, 0444); MODULE_PARM_DESC(ir_mode,"specify: 0=disable IR reception, 1=normal IR"); -static unsigned int pvr2_i2c_client_describe(struct pvr2_i2c_client *cp, - unsigned int detail, - char *buf,unsigned int maxlen); +static int pvr2_disable_ir_video; +module_param_named(disable_autoload_ir_video, pvr2_disable_ir_video, + int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(disable_autoload_ir_video, + "1=do not try to autoload ir_video IR receiver"); + +/* Mapping of IR schemes to known I2C addresses - if any */ +static const unsigned char ir_video_addresses[] = { + [PVR2_IR_SCHEME_29XXX] = 0x18, + [PVR2_IR_SCHEME_24XXX] = 0x18, +}; static int pvr2_i2c_write(struct pvr2_hdw *hdw, /* Context */ u8 i2c_addr, /* I2C address we're talking to */ @@ -599,417 +607,6 @@ static u32 pvr2_i2c_functionality(struct i2c_adapter *adap) return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C; } -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 pvr2_i2c_client *cp, *ncp; - int stat = -EINVAL; - - if (!hdw) return stat; - - mutex_lock(&hdw->i2c_list_lock); - list_for_each_entry_safe(cp, ncp, &hdw->i2c_clients, 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_status_poll(struct pvr2_hdw *hdw) -{ - struct pvr2_i2c_client *cp; - mutex_lock(&hdw->i2c_list_lock); do { - struct v4l2_tuner *vtp = &hdw->tuner_signal_info; - memset(vtp,0,sizeof(*vtp)); - list_for_each_entry(cp, &hdw->i2c_clients, list) { - if (!cp->detected_flag) continue; - if (!cp->status_poll) continue; - cp->status_poll(cp); - } - hdw->tuner_signal_stale = 0; - pvr2_trace(PVR2_TRACE_CHIPS,"i2c status poll" - " type=%u strength=%u audio=0x%x cap=0x%x" - " low=%u hi=%u", - vtp->type, - vtp->signal,vtp->rxsubchans,vtp->capability, - vtp->rangelow,vtp->rangehigh); - } while (0); mutex_unlock(&hdw->i2c_list_lock); -} - - -/* Issue various I2C operations to bring chip-level drivers into sync with - state stored in this driver. */ -void pvr2_i2c_core_sync(struct pvr2_hdw *hdw) -{ - unsigned long msk; - unsigned int idx; - struct pvr2_i2c_client *cp, *ncp; - - 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_entry(cp, &hdw->i2c_clients, 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_entry(cp, &hdw->i2c_clients, 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_entry_safe(cp, ncp, &hdw->i2c_clients, - 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_entry(cp, &hdw->i2c_clients, - 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 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 && opf->check)) continue; - if (opf->check(hdw)) { - sm |= msk; - } - } - if (sm) pt |= PVR2_I2C_PEND_STALE; - - list_for_each_entry(cp, &hdw->i2c_clients, list) - if (handler_check(cp)) - 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; - - 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 pvr2_i2c_client *cp; - ccnt = 0; - mutex_lock(&hdw->i2c_list_lock); do { - list_for_each_entry(cp, &hdw->i2c_clients, 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 = kzalloc(sizeof(*cp),GFP_KERNEL); - trace_i2c("i2c_attach [client=%s @ 0x%x ctxt=%p]", - client->name, - client->addr,cp); - if (!cp) return -ENOMEM; - cp->hdw = hdw; - INIT_LIST_HEAD(&cp->list); - cp->client = client; - mutex_lock(&hdw->i2c_list_lock); do { - hdw->cropcap_stale = !0; - 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) queue_work(hdw->workqueue,&hdw->worki2csync); - 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, *ncp; - unsigned long amask = 0; - int foundfl = 0; - mutex_lock(&hdw->i2c_list_lock); do { - hdw->cropcap_stale = !0; - list_for_each_entry_safe(cp, ncp, &hdw->i2c_clients, 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, .functionality = pvr2_i2c_functionality, @@ -1020,10 +617,10 @@ static struct i2c_algorithm pvr2_i2c_algo_template = { static struct i2c_adapter pvr2_i2c_adap_template = { .owner = THIS_MODULE, - .class = I2C_CLASS_TV_ANALOG, + .class = 0, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) .id = I2C_HW_B_BT848, - .client_register = pvr2_i2c_attach_inform, - .client_unregister = pvr2_i2c_detach_inform, +#endif }; @@ -1054,6 +651,33 @@ static void do_i2c_scan(struct pvr2_hdw *hdw) printk(KERN_INFO "%s: i2c scan done.\n", hdw->name); } +static void pvr2_i2c_register_ir(struct pvr2_hdw *hdw) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) + struct i2c_board_info info; + unsigned char addr = 0; + if (pvr2_disable_ir_video) { + pvr2_trace(PVR2_TRACE_INFO, + "Automatic binding of ir_video has been disabled."); + return; + } + if (hdw->ir_scheme_active < ARRAY_SIZE(ir_video_addresses)) { + addr = ir_video_addresses[hdw->ir_scheme_active]; + } + if (!addr) { + /* The device either doesn't support I2C-based IR or we + don't know (yet) how to operate IR on the device. */ + return; + } + pvr2_trace(PVR2_TRACE_INFO, + "Binding ir_video to i2c address 0x%02x.", addr); + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "ir_video", I2C_NAME_SIZE); + info.addr = addr; + i2c_new_device(&hdw->i2c_adap, &info); +#endif +} + void pvr2_i2c_core_init(struct pvr2_hdw *hdw) { unsigned int idx; @@ -1069,7 +693,9 @@ void pvr2_i2c_core_init(struct pvr2_hdw *hdw) printk(KERN_INFO "%s: IR disabled\n",hdw->name); hdw->i2c_func[0x18] = i2c_black_hole; } else if (ir_mode[hdw->unit_number] == 1) { - if (hdw->hdw_desc->ir_scheme == PVR2_IR_SCHEME_24XXX) { + if (hdw->ir_scheme_active == PVR2_IR_SCHEME_24XXX) { + /* Set up translation so that our IR looks like a + 29xxx device */ hdw->i2c_func[0x18] = i2c_24xxx_ir; } } @@ -1087,24 +713,28 @@ void pvr2_i2c_core_init(struct pvr2_hdw *hdw) hdw->i2c_adap.dev.parent = &hdw->usb_dev->dev; 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_set_adapdata(&hdw->i2c_adap, &hdw->v4l2_dev); i2c_add_adapter(&hdw->i2c_adap); if (hdw->i2c_func[0x18] == i2c_24xxx_ir) { /* Probe for a different type of IR receiver on this - device. If present, disable the emulated IR receiver. */ + device. This is really the only way to differentiate + older 24xxx devices from 24xxx variants that include an + IR blaster. If the IR blaster is present, the IR + receiver is part of that chip and thus we must disable + the emulated IR receiver. */ if (do_i2c_probe(hdw, 0x71)) { pvr2_trace(PVR2_TRACE_INFO, "Device has newer IR hardware;" " disabling unneeded virtual IR device"); hdw->i2c_func[0x18] = NULL; + /* Remember that this is a different device... */ + hdw->ir_scheme_active = PVR2_IR_SCHEME_24XXX_MCE; } } if (i2c_scan) do_i2c_scan(hdw); + + pvr2_i2c_register_ir(hdw); } void pvr2_i2c_core_done(struct pvr2_hdw *hdw) |