Commit 59af3367 authored by Mike Isely's avatar Mike Isely Committed by Mauro Carvalho Chehab

V4L/DVB (11154): pvrusb2: Split i2c module handling from i2c adapter

This is the first step in the effort to move the pvrusb2 driver over
to using the v4l2-subdev framework.  This commit involves mainly
splitting apart pvrusb2-i2c-core - part of it is the driver's I2C
adapter driver and the rest is the old i2c module handling logic.  The
i2c module handling junk is moved out to pvrusb2-i2c-track and various
header references are correspondingly updated.  Yes, this patch has a
huge pile of checkpatch complaints, but I'm NOT going to fix any of
it.  Why?  First, I'm moving a large chunk of existing code and I'm
not going to spend time adjusting it to match someone's idea of coding
style.  Second, in the end I expect all that moved code to go away by
the time the rework is done so wasting time on it now to adhere to the
standard is in the end a large waste of time.
Signed-off-by: default avatarMike Isely <isely@pobox.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent c457377a
......@@ -3,6 +3,7 @@ obj-pvrusb2-debugifc-$(CONFIG_VIDEO_PVRUSB2_DEBUGIFC) := pvrusb2-debugifc.o
obj-pvrusb2-dvb-$(CONFIG_VIDEO_PVRUSB2_DVB) := pvrusb2-dvb.o
pvrusb2-objs := pvrusb2-i2c-core.o pvrusb2-i2c-cmd-v4l2.o \
pvrusb2-i2c-track.o \
pvrusb2-audio.o pvrusb2-i2c-chips-v4l2.o \
pvrusb2-encoder.o pvrusb2-video-v4l.o \
pvrusb2-eeprom.o pvrusb2-tuner.o \
......
......@@ -22,7 +22,7 @@
#ifndef __PVRUSB2_AUDIO_H
#define __PVRUSB2_AUDIO_H
#include "pvrusb2-i2c-core.h"
#include "pvrusb2-i2c-track.h"
int pvr2_i2c_msp3400_setup(struct pvr2_hdw *,struct pvr2_i2c_client *);
......
......@@ -34,7 +34,7 @@
#include "pvrusb2-i2c-core.h"
#include "pvrusb2-i2c-track.h"
int pvr2_i2c_cx2584x_v4l_setup(struct pvr2_hdw *,struct pvr2_i2c_client *);
......
......@@ -23,7 +23,7 @@
#include "pvrusb2-debugifc.h"
#include "pvrusb2-hdw.h"
#include "pvrusb2-debug.h"
#include "pvrusb2-i2c-core.h"
#include "pvrusb2-i2c-track.h"
struct debugifc_mask_item {
const char *name;
......
......@@ -29,6 +29,7 @@
#include "pvrusb2-util.h"
#include "pvrusb2-hdw.h"
#include "pvrusb2-i2c-core.h"
#include "pvrusb2-i2c-track.h"
#include "pvrusb2-tuner.h"
#include "pvrusb2-eeprom.h"
#include "pvrusb2-hdw-internal.h"
......@@ -1990,6 +1991,7 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
}
// This step MUST happen after the earlier powerup step.
pvr2_i2c_track_init(hdw);
pvr2_i2c_core_init(hdw);
if (!pvr2_hdw_dev_ok(hdw)) return;
......@@ -2501,6 +2503,7 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw)
hdw->decoder_ctrl->detach(hdw->decoder_ctrl->ctxt);
}
pvr2_i2c_core_done(hdw);
pvr2_i2c_track_done(hdw);
pvr2_hdw_remove_usb_stuff(hdw);
mutex_lock(&pvr2_unit_mtx); do {
if ((hdw->unit_number >= 0) &&
......
......@@ -19,7 +19,7 @@
*/
#include <linux/kernel.h>
#include "pvrusb2-i2c-core.h"
#include "pvrusb2-i2c-track.h"
#include "pvrusb2-hdw-internal.h"
#include "pvrusb2-debug.h"
#include "pvrusb2-i2c-cmd-v4l2.h"
......
......@@ -22,7 +22,7 @@
#ifndef __PVRUSB2_CMD_V4L2_H
#define __PVRUSB2_CMD_V4L2_H
#include "pvrusb2-i2c-core.h"
#include "pvrusb2-i2c-track.h"
extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_init;
extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_standard;
......
......@@ -18,7 +18,9 @@
*
*/
#include <linux/i2c.h>
#include "pvrusb2-i2c-core.h"
#include "pvrusb2-i2c-track.h"
#include "pvrusb2-hdw-internal.h"
#include "pvrusb2-debug.h"
#include "pvrusb2-fx2-cmd.h"
......@@ -29,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.
*/
......@@ -42,10 +43,6 @@ 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_i2c_write(struct pvr2_hdw *hdw, /* Context */
u8 i2c_addr, /* I2C address we're talking to */
u8 *data, /* Data to write */
......@@ -524,414 +521,15 @@ 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);
pvr2_i2c_track_attach_inform(client);
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);
}
pvr2_i2c_track_detach_inform(client);
return 0;
}
......@@ -1009,11 +607,6 @@ 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_add_adapter(&hdw->i2c_adap);
if (hdw->i2c_func[0x18] == i2c_24xxx_ir) {
......
......@@ -20,68 +20,13 @@
#ifndef __PVRUSB2_I2C_CORE_H
#define __PVRUSB2_I2C_CORE_H
#include <linux/list.h>
#include <linux/i2c.h>
struct pvr2_hdw;
struct pvr2_i2c_client;
struct pvr2_i2c_handler;
struct pvr2_i2c_handler_functions;
struct pvr2_i2c_op;
struct pvr2_i2c_op_functions;
struct pvr2_i2c_client {
struct i2c_client *client;
struct pvr2_i2c_handler *handler;
struct list_head list;
struct pvr2_hdw *hdw;
int detected_flag;
int recv_enable;
unsigned long pend_mask;
unsigned long ctl_mask;
void (*status_poll)(struct pvr2_i2c_client *);
};
struct pvr2_i2c_handler {
void *func_data;
const struct pvr2_i2c_handler_functions *func_table;
};
struct pvr2_i2c_handler_functions {
void (*detach)(void *);
int (*check)(void *);
void (*update)(void *);
unsigned int (*describe)(void *,char *,unsigned int);
};
struct pvr2_i2c_op {
int (*check)(struct pvr2_hdw *);
void (*update)(struct pvr2_hdw *);
const char *name;
};
void pvr2_i2c_core_init(struct pvr2_hdw *);
void pvr2_i2c_core_done(struct pvr2_hdw *);
int pvr2_i2c_client_cmd(struct pvr2_i2c_client *,unsigned int cmd,void *arg);
int pvr2_i2c_core_cmd(struct pvr2_hdw *,unsigned int cmd,void *arg);
int pvr2_i2c_core_check_stale(struct pvr2_hdw *);
void pvr2_i2c_core_sync(struct pvr2_hdw *);
void pvr2_i2c_core_status_poll(struct pvr2_hdw *);
unsigned int pvr2_i2c_report(struct pvr2_hdw *,char *buf,unsigned int maxlen);
#define PVR2_I2C_DETAIL_DEBUG 0x0001
#define PVR2_I2C_DETAIL_HANDLER 0x0002
#define PVR2_I2C_DETAIL_CTLMASK 0x0004
#define PVR2_I2C_DETAIL_ALL (\
PVR2_I2C_DETAIL_DEBUG |\
PVR2_I2C_DETAIL_HANDLER |\
PVR2_I2C_DETAIL_CTLMASK)
void pvr2_i2c_probe(struct pvr2_hdw *,struct pvr2_i2c_client *);
const struct pvr2_i2c_op *pvr2_i2c_get_op(unsigned int idx);
#endif /* __PVRUSB2_I2C_CORE_H */
#endif /* __PVRUSB2_I2C_ADAPTER_H */
/*
......
/*
*
*
* 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-track.h"
#include "pvrusb2-hdw-internal.h"
#include "pvrusb2-debug.h"
#include "pvrusb2-fx2-cmd.h"
#include "pvrusb2.h"
#define trace_i2c(...) pvr2_trace(PVR2_TRACE_I2C,__VA_ARGS__)
/*
This module implements the foundation of a rather large architecture for
tracking state in all the various V4L I2C modules. This is obsolete with
kernels later than roughly 2.6.24, but it is still present in the
standalone pvrusb2 driver to allow continued operation with older
kernel.
*/
static unsigned int pvr2_i2c_client_describe(struct pvr2_i2c_client *cp,
unsigned int detail,
char *buf,unsigned int maxlen);
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;
}
void pvr2_i2c_track_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) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"Unable to allocate tracking memory for incoming"
" i2c module; ignoring module. This is likely"
" going to be a problem.");
return;
}
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);
}
void pvr2_i2c_track_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);
}
}
void pvr2_i2c_track_init(struct pvr2_hdw *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);
}
void pvr2_i2c_track_done(struct pvr2_hdw *hdw)
{
/* Empty for now */
}
/*
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: ***
*/
/*
*
*
* 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
*
*/
#ifndef __PVRUSB2_I2C_TRACK_H
#define __PVRUSB2_I2C_TRACK_H
#include <linux/list.h>
#include <linux/i2c.h>
struct pvr2_hdw;
struct pvr2_i2c_client;
struct pvr2_i2c_handler;
struct pvr2_i2c_handler_functions;
struct pvr2_i2c_op;
struct pvr2_i2c_op_functions;
struct pvr2_i2c_client {
struct i2c_client *client;
struct pvr2_i2c_handler *handler;
struct list_head list;
struct pvr2_hdw *hdw;
int detected_flag;
int recv_enable;
unsigned long pend_mask;
unsigned long ctl_mask;
void (*status_poll)(struct pvr2_i2c_client *);
};
struct pvr2_i2c_handler {
void *func_data;
const struct pvr2_i2c_handler_functions *func_table;
};
struct pvr2_i2c_handler_functions {
void (*detach)(void *);
int (*check)(void *);
void (*update)(void *);
unsigned int (*describe)(void *,char *,unsigned int);
};
struct pvr2_i2c_op {
int (*check)(struct pvr2_hdw *);
void (*update)(struct pvr2_hdw *);
const char *name;
};
void pvr2_i2c_track_init(struct pvr2_hdw *);
void pvr2_i2c_track_done(struct pvr2_hdw *);
void pvr2_i2c_track_attach_inform(struct i2c_client *);
void pvr2_i2c_track_detach_inform(struct i2c_client *);
int pvr2_i2c_client_cmd(struct pvr2_i2c_client *,unsigned int cmd,void *arg);
int pvr2_i2c_core_cmd(struct pvr2_hdw *,unsigned int cmd,void *arg);
int pvr2_i2c_core_check_stale(struct pvr2_hdw *);
void pvr2_i2c_core_sync(struct pvr2_hdw *);
void pvr2_i2c_core_status_poll(struct pvr2_hdw *);
unsigned int pvr2_i2c_report(struct pvr2_hdw *,char *buf,unsigned int maxlen);
#define PVR2_I2C_DETAIL_DEBUG 0x0001
#define PVR2_I2C_DETAIL_HANDLER 0x0002
#define PVR2_I2C_DETAIL_CTLMASK 0x0004
#define PVR2_I2C_DETAIL_ALL (\
PVR2_I2C_DETAIL_DEBUG |\
PVR2_I2C_DETAIL_HANDLER |\
PVR2_I2C_DETAIL_CTLMASK)
void pvr2_i2c_probe(struct pvr2_hdw *,struct pvr2_i2c_client *);
const struct pvr2_i2c_op *pvr2_i2c_get_op(unsigned int idx);
#endif /* __PVRUSB2_I2C_CORE_H */
/*
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: ***
*/
......@@ -20,7 +20,7 @@
#ifndef __PVRUSB2_TUNER_H
#define __PVRUSB2_TUNER_H
#include "pvrusb2-i2c-core.h"
#include "pvrusb2-i2c-track.h"
int pvr2_i2c_tuner_setup(struct pvr2_hdw *,struct pvr2_i2c_client *);
......
......@@ -33,7 +33,7 @@
#include "pvrusb2-i2c-core.h"
#include "pvrusb2-i2c-track.h"
int pvr2_i2c_decoder_v4l_setup(struct pvr2_hdw *,struct pvr2_i2c_client *);
......
......@@ -34,7 +34,7 @@
#include "pvrusb2-i2c-core.h"
#include "pvrusb2-i2c-track.h"
int pvr2_i2c_wm8775_setup(struct pvr2_hdw *,struct pvr2_i2c_client *);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment