Commit ff2a2001 authored by Andy Walls's avatar Andy Walls Committed by Mauro Carvalho Chehab

V4L/DVB (10758): cx18: Convert I2C devices to v4l2_subdevices

This is a major perturbation to cx18 I2C device handling to convert it to the
v4l2_device/subdeivce framework.  This change breaks GPIO audio multiplexer
control for the time being.  It will be fixed in a coming change.
Signed-off-by: default avatarAndy Walls <awalls@radix.net>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent fa3e7036
......@@ -23,9 +23,7 @@
#include "cx18-driver.h"
#include "cx18-io.h"
#include "cx18-i2c.h"
#include "cx18-cards.h"
#include "cx18-audio.h"
#define CX18_AUDIO_ENABLE 0xc72014
......@@ -33,54 +31,32 @@
settings. */
int cx18_audio_set_io(struct cx18 *cx)
{
const struct cx18_card_audio_input *in;
struct v4l2_routing route;
u32 audio_input;
u32 val;
int mux_input;
int err;
/* Determine which input to use */
if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) {
audio_input = cx->card->radio_input.audio_input;
mux_input = cx->card->radio_input.muxer_input;
} else {
audio_input =
cx->card->audio_inputs[cx->audio_input].audio_input;
mux_input =
cx->card->audio_inputs[cx->audio_input].muxer_input;
}
if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags))
in = &cx->card->radio_input;
else
in = &cx->card->audio_inputs[cx->audio_input];
/* handle muxer chips */
route.input = mux_input;
route.input = in->muxer_input;
route.output = 0;
cx18_i2c_hw(cx, cx->card->hw_muxer, VIDIOC_INT_S_AUDIO_ROUTING, &route);
v4l2_subdev_call(cx->sd_extmux, audio, s_routing, &route);
route.input = audio_input;
err = cx18_i2c_hw(cx, cx->card->hw_audio_ctrl,
VIDIOC_INT_S_AUDIO_ROUTING, &route);
route.input = in->audio_input;
err = cx18_call_hw_err(cx, cx->card->hw_audio_ctrl,
audio, s_routing, &route);
if (err)
return err;
/* FIXME - this internal mux should be abstracted to a subdev */
val = cx18_read_reg(cx, CX18_AUDIO_ENABLE) & ~0x30;
val |= (audio_input > CX18_AV_AUDIO_SERIAL2) ? 0x20 :
(audio_input << 4);
val |= (in->audio_input > CX18_AV_AUDIO_SERIAL2) ? 0x20 :
(in->audio_input << 4);
cx18_write_reg_expect(cx, val | 0xb00, CX18_AUDIO_ENABLE, val, 0x30);
return 0;
}
void cx18_audio_set_route(struct cx18 *cx, struct v4l2_routing *route)
{
cx18_i2c_hw(cx, cx->card->hw_audio_ctrl,
VIDIOC_INT_S_AUDIO_ROUTING, route);
}
void cx18_audio_set_audio_clock_freq(struct cx18 *cx, u8 freq)
{
static u32 freqs[3] = { 44100, 48000, 32000 };
/* The audio clock of the digitizer must match the codec sample
rate otherwise you get some very strange effects. */
if (freq > 2)
return;
cx18_call_i2c_clients(cx, VIDIOC_INT_AUDIO_CLOCK_FREQ, &freqs[freq]);
}
......@@ -22,5 +22,3 @@
*/
int cx18_audio_set_io(struct cx18 *cx);
void cx18_audio_set_route(struct cx18 *cx, struct v4l2_routing *route);
void cx18_audio_set_audio_clock_freq(struct cx18 *cx, u8 freq);
......@@ -1209,9 +1209,10 @@ static const struct v4l2_subdev_ops cx18_av_ops = {
.video = &cx18_av_video_ops,
};
int cx18_av_probe(struct cx18 *cx, struct v4l2_subdev **sd)
int cx18_av_probe(struct cx18 *cx)
{
struct cx18_av_state *state = &cx->av_state;
struct v4l2_subdev *sd;
state->rev = cx18_av_read4(cx, CXADEC_CHIP_CTRL) & 0xffff;
state->id = ((state->rev >> 4) == CXADEC_CHIP_TYPE_MAKO)
......@@ -1224,13 +1225,13 @@ int cx18_av_probe(struct cx18 *cx, struct v4l2_subdev **sd)
state->slicer_line_delay = 0;
state->slicer_line_offset = (10 + state->slicer_line_delay - 2);
*sd = &state->sd;
v4l2_subdev_init(*sd, &cx18_av_ops);
v4l2_set_subdevdata(*sd, cx);
snprintf((*sd)->name, sizeof((*sd)->name),
sd = &state->sd;
v4l2_subdev_init(sd, &cx18_av_ops);
v4l2_set_subdevdata(sd, cx);
snprintf(sd->name, sizeof(sd->name),
"%s internal A/V decoder", cx->v4l2_dev.name);
(*sd)->grp_id = CX18_HW_CX23418;
return v4l2_device_register_subdev(&cx->v4l2_dev, *sd);
sd->grp_id = CX18_HW_418_AV;
return v4l2_device_register_subdev(&cx->v4l2_dev, sd);
}
void cx18_av_exit(struct cx18 *cx, struct v4l2_subdev *sd)
......
......@@ -342,7 +342,7 @@ int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned mask, u8 value);
int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 mask, u32 value);
void cx18_av_std_setup(struct cx18 *cx);
int cx18_av_probe(struct cx18 *cx, struct v4l2_subdev **sd);
int cx18_av_probe(struct cx18 *cx);
void cx18_av_exit(struct cx18 *cx, struct v4l2_subdev *sd);
/* ----------------------------------------------------------------------- */
......
......@@ -53,9 +53,9 @@ static const struct cx18_card cx18_card_hvr1600_esmt = {
.name = "Hauppauge HVR-1600",
.comment = "Simultaneous Digital and Analog TV capture supported\n",
.v4l2_capabilities = CX18_CAP_ENCODER,
.hw_audio_ctrl = CX18_HW_CX23418,
.hw_audio_ctrl = CX18_HW_418_AV,
.hw_muxer = CX18_HW_CS5345,
.hw_all = CX18_HW_TVEEPROM | CX18_HW_TUNER |
.hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER |
CX18_HW_CS5345 | CX18_HW_DVB,
.video_inputs = {
{ CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE7 },
......@@ -99,9 +99,9 @@ static const struct cx18_card cx18_card_hvr1600_samsung = {
.name = "Hauppauge HVR-1600 (Preproduction)",
.comment = "Simultaneous Digital and Analog TV capture supported\n",
.v4l2_capabilities = CX18_CAP_ENCODER,
.hw_audio_ctrl = CX18_HW_CX23418,
.hw_audio_ctrl = CX18_HW_418_AV,
.hw_muxer = CX18_HW_CS5345,
.hw_all = CX18_HW_TVEEPROM | CX18_HW_TUNER |
.hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER |
CX18_HW_CS5345 | CX18_HW_DVB,
.video_inputs = {
{ CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE7 },
......@@ -154,8 +154,8 @@ static const struct cx18_card cx18_card_h900 = {
.name = "Compro VideoMate H900",
.comment = "Analog TV capture supported\n",
.v4l2_capabilities = CX18_CAP_ENCODER,
.hw_audio_ctrl = CX18_HW_CX23418,
.hw_all = CX18_HW_TUNER,
.hw_audio_ctrl = CX18_HW_418_AV,
.hw_all = CX18_HW_418_AV | CX18_HW_TUNER,
.video_inputs = {
{ CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 },
{ CX18_CARD_INPUT_SVIDEO1, 1,
......@@ -201,8 +201,8 @@ static const struct cx18_card cx18_card_mpc718 = {
.name = "Yuan MPC718",
.comment = "Analog video capture works; some audio line in may not.\n",
.v4l2_capabilities = CX18_CAP_ENCODER,
.hw_audio_ctrl = CX18_HW_CX23418,
.hw_all = CX18_HW_TUNER,
.hw_audio_ctrl = CX18_HW_418_AV,
.hw_all = CX18_HW_418_AV | CX18_HW_TUNER,
.video_inputs = {
{ CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 },
{ CX18_CARD_INPUT_SVIDEO1, 1,
......@@ -251,9 +251,9 @@ static const struct cx18_card cx18_card_cnxt_raptor_pal = {
.name = "Conexant Raptor PAL/SECAM",
.comment = "Analog TV capture supported\n",
.v4l2_capabilities = CX18_CAP_ENCODER,
.hw_audio_ctrl = CX18_HW_CX23418,
.hw_muxer = CX18_HW_GPIO,
.hw_all = CX18_HW_TUNER | CX18_HW_GPIO,
.hw_audio_ctrl = CX18_HW_418_AV,
.hw_muxer = CX18_HW_GPIO_AUDIO_MUX,
.hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_AUDIO_MUX,
.video_inputs = {
{ CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 },
{ CX18_CARD_INPUT_SVIDEO1, 1,
......@@ -306,8 +306,8 @@ static const struct cx18_card cx18_card_toshiba_qosmio_dvbt = {
.comment = "Experimenters and photos needed for device to work well.\n"
"\tTo help, mail the ivtv-devel list (www.ivtvdriver.org).\n",
.v4l2_capabilities = CX18_CAP_ENCODER,
.hw_audio_ctrl = CX18_HW_CX23418,
.hw_all = CX18_HW_TUNER,
.hw_audio_ctrl = CX18_HW_418_AV,
.hw_all = CX18_HW_418_AV | CX18_HW_TUNER,
.video_inputs = {
{ CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE6 },
{ CX18_CARD_INPUT_SVIDEO1, 1,
......@@ -350,9 +350,9 @@ static const struct cx18_card cx18_card_leadtek_pvr2100 = {
.comment = "Experimenters and photos needed for device to work well.\n"
"\tTo help, mail the ivtv-devel list (www.ivtvdriver.org).\n",
.v4l2_capabilities = CX18_CAP_ENCODER,
.hw_audio_ctrl = CX18_HW_CX23418,
.hw_muxer = CX18_HW_GPIO,
.hw_all = CX18_HW_TUNER | CX18_HW_GPIO,
.hw_audio_ctrl = CX18_HW_418_AV,
.hw_muxer = CX18_HW_GPIO_AUDIO_MUX,
.hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_AUDIO_MUX,
.video_inputs = {
{ CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 },
{ CX18_CARD_INPUT_SVIDEO1, 1,
......
......@@ -22,12 +22,12 @@
*/
/* hardware flags */
#define CX18_HW_TUNER (1 << 0)
#define CX18_HW_TVEEPROM (1 << 1)
#define CX18_HW_CS5345 (1 << 2)
#define CX18_HW_GPIO (1 << 3)
#define CX18_HW_CX23418 (1 << 4)
#define CX18_HW_DVB (1 << 5)
#define CX18_HW_TUNER (1 << 0)
#define CX18_HW_TVEEPROM (1 << 1)
#define CX18_HW_CS5345 (1 << 2)
#define CX18_HW_DVB (1 << 3)
#define CX18_HW_418_AV (1 << 4)
#define CX18_HW_GPIO_AUDIO_MUX (1 << 5)
/* video inputs */
#define CX18_CARD_INPUT_VID_TUNER 1
......@@ -121,7 +121,7 @@ struct cx18_card {
char *comment;
u32 v4l2_capabilities;
u32 hw_audio_ctrl; /* hardware used for the V4L2 controls (only
1 dev allowed) */
1 dev allowed currently) */
u32 hw_muxer; /* hardware used to multiplex audio input */
u32 hw_all; /* all hardware used by the board */
struct cx18_card_video_input video_inputs[CX18_CARD_MAX_VIDEO_INPUTS];
......
......@@ -77,7 +77,7 @@ int cx18_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qctrl)
case V4L2_CID_AUDIO_BASS:
case V4L2_CID_AUDIO_TREBLE:
case V4L2_CID_AUDIO_LOUDNESS:
if (cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, VIDIOC_QUERYCTRL, qctrl))
if (v4l2_subdev_call(cx->sd_av, core, queryctrl, qctrl))
qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
return 0;
......@@ -134,7 +134,7 @@ static int cx18_s_ctrl(struct cx18 *cx, struct v4l2_control *vctrl)
case V4L2_CID_AUDIO_BASS:
case V4L2_CID_AUDIO_TREBLE:
case V4L2_CID_AUDIO_LOUDNESS:
return cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, VIDIOC_S_CTRL, vctrl);
return v4l2_subdev_call(cx->sd_av, core, s_ctrl, vctrl);
default:
CX18_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id);
......@@ -159,7 +159,8 @@ static int cx18_g_ctrl(struct cx18 *cx, struct v4l2_control *vctrl)
case V4L2_CID_AUDIO_BASS:
case V4L2_CID_AUDIO_TREBLE:
case V4L2_CID_AUDIO_LOUDNESS:
return cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, VIDIOC_G_CTRL, vctrl);
return v4l2_subdev_call(cx->sd_av, core, g_ctrl, vctrl);
default:
CX18_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id);
return -EINVAL;
......@@ -260,10 +261,12 @@ int cx18_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
return err;
}
if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
static u32 freqs[3] = { 44100, 48000, 32000 };
struct cx18_api_func_private priv;
struct cx2341x_mpeg_params p = cx->params;
int err = cx2341x_ext_ctrls(&p, atomic_read(&cx->ana_capturing),
c, VIDIOC_S_EXT_CTRLS);
unsigned int idx;
if (err)
return err;
......@@ -287,7 +290,11 @@ int cx18_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
err = cx18_setup_vbi_fmt(cx, p.stream_vbi_fmt);
cx->params = p;
cx->dualwatch_stereo_mode = p.audio_properties & 0x0300;
cx18_audio_set_audio_clock_freq(cx, p.audio_properties & 0x03);
idx = p.audio_properties & 0x03;
/* The audio clock of the digitizer must match the codec sample
rate otherwise you get some very strange effects. */
if (idx < sizeof(freqs))
cx18_call_all(cx, audio, s_clock_freq, freqs[idx]);
return err;
}
return -EINVAL;
......
......@@ -269,11 +269,16 @@ static void cx18_iounmap(struct cx18 *cx)
/* Hauppauge card? get values from tveeprom */
void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv)
{
struct i2c_client c;
u8 eedata[256];
cx->i2c_client[0].addr = 0xA0 >> 1;
tveeprom_read(&cx->i2c_client[0], eedata, sizeof(eedata));
tveeprom_hauppauge_analog(&cx->i2c_client[0], tv, eedata);
strncpy(c.name, "cx18 tveeprom tmp", sizeof(c.name));
c.name[sizeof(c.name)-1] = '\0';
c.adapter = &cx->i2c_adap[0];
c.addr = 0xA0 >> 1;
tveeprom_read(&c, eedata, sizeof(eedata));
tveeprom_hauppauge_analog(&c, tv, eedata);
}
static void cx18_process_eeprom(struct cx18 *cx)
......@@ -553,8 +558,6 @@ static int __devinit cx18_init_struct1(struct cx18 *cx)
cx->base_addr = pci_resource_start(cx->pci_dev, 0);
mutex_init(&cx->serialize_lock);
mutex_init(&cx->i2c_bus_lock[0]);
mutex_init(&cx->i2c_bus_lock[1]);
mutex_init(&cx->gpio_lock);
mutex_init(&cx->epu2apu_mb_lock);
mutex_init(&cx->epu2cpu_mb_lock);
......@@ -669,54 +672,41 @@ static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *pci_dev,
return 0;
}
#ifdef MODULE
static u32 cx18_request_module(struct cx18 *cx, u32 hw,
const char *name, u32 id)
{
if ((hw & id) == 0)
return hw;
if (request_module("%s", name) != 0) {
CX18_ERR("Failed to load module %s\n", name);
return hw & ~id;
}
CX18_DEBUG_INFO("Loaded module %s\n", name);
return hw;
}
#endif
static void cx18_load_and_init_modules(struct cx18 *cx)
static void cx18_init_subdevs(struct cx18 *cx)
{
u32 hw = cx->card->hw_all;
u32 device;
int i;
#ifdef MODULE
/* load modules */
#ifdef CONFIG_MEDIA_TUNER_MODULE
hw = cx18_request_module(cx, hw, "tuner", CX18_HW_TUNER);
#endif
#ifdef CONFIG_VIDEO_CS5345_MODULE
hw = cx18_request_module(cx, hw, "cs5345", CX18_HW_CS5345);
#endif
#endif
/* check which i2c devices are actually found */
for (i = 0; i < 32; i++) {
u32 device = 1 << i;
for (i = 0, device = 1; i < 32; i++, device <<= 1) {
if (!(device & hw))
continue;
if (device == CX18_HW_GPIO || device == CX18_HW_TVEEPROM ||
device == CX18_HW_CX23418 || device == CX18_HW_DVB) {
/* These 'devices' do not use i2c probing */
switch (device) {
case CX18_HW_GPIO_AUDIO_MUX:
case CX18_HW_DVB:
case CX18_HW_TVEEPROM:
/* These subordinate devices do not use probing */
cx->hw_flags |= device;
continue;
}
cx18_i2c_register(cx, i);
if (cx18_i2c_hw_addr(cx, device) > 0)
break;
case CX18_HW_418_AV:
/* The A/V decoder gets probed earlier to set PLLs */
/* Just note that the card uses it (i.e. has analog) */
cx->hw_flags |= device;
break;
default:
if (cx18_i2c_register(cx, i) == 0)
cx->hw_flags |= device;
break;
}
}
hw = cx->hw_flags;
if (cx->hw_flags & CX18_HW_418_AV)
cx->sd_av = cx18_find_hw(cx, CX18_HW_418_AV);
if (cx->card->hw_muxer != 0)
cx->sd_extmux = cx18_find_hw(cx, cx->card->hw_muxer);
}
static int __devinit cx18_probe(struct pci_dev *pci_dev,
......@@ -803,15 +793,17 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev,
cx->scb = (struct cx18_scb __iomem *)(cx->enc_mem + SCB_OFFSET);
cx18_init_scb(cx);
/* Initialize GPIO early so I2C device resets can be performed */
cx18_gpio_init(cx);
retval = cx18_av_probe(cx, &cx->sd_av);
/* Initialize integrated A/V decoder early to set PLLs, just in case */
retval = cx18_av_probe(cx);
if (retval) {
CX18_ERR("Could not register A/V decoder subdevice\n");
goto free_map;
}
/* Initialize the A/V decoder PLLs to sane defaults */
v4l2_subdev_call(cx->sd_av, core, init, (u32) CX18_AV_INIT_PLLS);
cx18_call_hw(cx, CX18_HW_418_AV, core, init, (u32) CX18_AV_INIT_PLLS);
/* active i2c */
CX18_DEBUG_INFO("activating i2c...\n");
......@@ -873,7 +865,7 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev,
initialization. */
cx18_init_struct2(cx);
cx18_load_and_init_modules(cx);
cx18_init_subdevs(cx);
if (cx->std & V4L2_STD_525_60) {
cx->is_60hz = 1;
......@@ -895,7 +887,7 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev,
setup.mode_mask = T_ANALOG_TV; /* matches TV tuners */
setup.tuner_callback = (setup.type == TUNER_XC2028) ?
cx18_reset_tuner_gpio : NULL;
cx18_call_i2c_clients(cx, TUNER_SET_TYPE_ADDR, &setup);
cx18_call_all(cx, tuner, s_type_addr, &setup);
if (setup.type == TUNER_XC2028) {
static struct xc2028_ctrl ctrl = {
.fname = XC2028_DEFAULT_FIRMWARE,
......@@ -905,7 +897,7 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev,
.tuner = cx->options.tuner,
.priv = &ctrl,
};
cx18_call_i2c_clients(cx, TUNER_SET_CONFIG, &cfg);
cx18_call_all(cx, tuner, s_config, &cfg);
}
}
......
......@@ -448,7 +448,8 @@ struct cx18 {
int instance;
struct pci_dev *pci_dev;
struct v4l2_device v4l2_dev;
struct v4l2_subdev *sd_av;
struct v4l2_subdev *sd_av; /* A/V decoder/digitizer sub-device */
struct v4l2_subdev *sd_extmux; /* External audio multiplexer sub-dev */
const struct cx18_card *card; /* card information */
const char *card_name; /* full name of the card */
......@@ -528,9 +529,6 @@ struct cx18 {
struct i2c_adapter i2c_adap[2];
struct i2c_algo_bit_data i2c_algo[2];
struct cx18_i2c_algo_callback_data i2c_algo_cb_data[2];
struct i2c_client i2c_client[2];
struct mutex i2c_bus_lock[2];
struct i2c_client *i2c_clients[I2C_CLIENTS_MAX];
/* gpio */
u32 gpio_dir;
......@@ -573,4 +571,22 @@ static inline int cx18_raw_vbi(const struct cx18 *cx)
return cx->vbi.in.type == V4L2_BUF_TYPE_VBI_CAPTURE;
}
/* Call the specified callback for all subdevs with a grp_id bit matching the
* mask in hw (if 0, then match them all). Ignore any errors. */
#define cx18_call_hw(cx, hw, o, f, args...) \
__v4l2_device_call_subdevs(&(cx)->v4l2_dev, \
!(hw) || (sd->grp_id & (hw)), o, f , ##args)
#define cx18_call_all(cx, o, f, args...) cx18_call_hw(cx, 0, o, f , ##args)
/* Call the specified callback for all subdevs with a grp_id bit matching the
* mask in hw (if 0, then match them all). If the callback returns an error
* other than 0 or -ENOIOCTLCMD, then return with that error code. */
#define cx18_call_hw_err(cx, hw, o, f, args...) \
__v4l2_device_call_subdevs_until_err( \
&(cx)->v4l2_dev, !(hw) || (sd->grp_id & (hw)), o, f , ##args)
#define cx18_call_all_err(cx, o, f, args...) \
cx18_call_hw_err(cx, 0, o, f , ##args)
#endif /* CX18_DRIVER_H */
......@@ -136,7 +136,7 @@ static void cx18_dualwatch(struct cx18 *cx)
new_stereo_mode = cx->params.audio_properties & stereo_mask;
memset(&vt, 0, sizeof(vt));
cx18_call_i2c_clients(cx, VIDIOC_G_TUNER, &vt);
cx18_call_all(cx, tuner, g_tuner, &vt);
if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 &&
(vt.rxsubchans & V4L2_TUNER_SUB_LANG2))
new_stereo_mode = dual;
......@@ -608,7 +608,7 @@ int cx18_v4l2_close(struct file *filp)
/* Mark that the radio is no longer in use */
clear_bit(CX18_F_I_RADIO_USER, &cx->i_flags);
/* Switch tuner to TV */
cx18_call_i2c_clients(cx, VIDIOC_S_STD, &cx->std);
cx18_call_all(cx, tuner, s_std, cx->std);
/* Select correct audio input (i.e. TV tuner or Line in) */
cx18_audio_set_io(cx);
if (atomic_read(&cx->ana_capturing) > 0) {
......@@ -671,7 +671,7 @@ static int cx18_serialized_open(struct cx18_stream *s, struct file *filp)
/* We have the radio */
cx18_mute(cx);
/* Switch tuner to radio */
cx18_call_i2c_clients(cx, AUDC_SET_RADIO, NULL);
cx18_call_all(cx, tuner, s_radio);
/* Select the correct audio input (i.e. radio tuner) */
cx18_audio_set_io(cx);
/* Done! Unmute and continue. */
......
......@@ -42,32 +42,35 @@
#define CX18_CS5345_I2C_ADDR 0x4c
/* This array should match the CX18_HW_ defines */
static const u8 hw_driverids[] = {
I2C_DRIVERID_TUNER,
I2C_DRIVERID_TVEEPROM,
I2C_DRIVERID_CS5345,
0, /* CX18_HW_GPIO dummy driver ID */
0 /* CX18_HW_CX23418 dummy driver ID */
};
/* This array should match the CX18_HW_ defines */
static const u8 hw_addrs[] = {
0,
0,
CX18_CS5345_I2C_ADDR,
0, /* CX18_HW_GPIO dummy driver ID */
0, /* CX18_HW_CX23418 dummy driver ID */
0, /* CX18_HW_TUNER */
0, /* CX18_HW_TVEEPROM */
CX18_CS5345_I2C_ADDR, /* CX18_HW_CS5345 */
0, /* CX18_HW_DVB */
0, /* CX18_HW_418_AV */
0, /* CX18_HW_GPIO_AUDIO_MUX */
};
/* This array should match the CX18_HW_ defines */
/* This might well become a card-specific array */
static const u8 hw_bus[] = {
0,
0,
0,
0, /* CX18_HW_GPIO dummy driver ID */
0, /* CX18_HW_CX23418 dummy driver ID */
1, /* CX18_HW_TUNER */
0, /* CX18_HW_TVEEPROM */
0, /* CX18_HW_CS5345 */
0, /* CX18_HW_DVB */
0, /* CX18_HW_418_AV */
0, /* CX18_HW_GPIO_AUDIO_MUX */
};
/* This array should match the CX18_HW_ defines */
static const char * const hw_modules[] = {
"tuner", /* CX18_HW_TUNER */
NULL, /* CX18_HW_TVEEPROM */
"cs5345", /* CX18_HW_CS5345 */
NULL, /* CX18_HW_DVB */
NULL, /* CX18_HW_418_AV */
NULL, /* CX18_HW_GPIO_AUDIO_MUX */
};
/* This array should match the CX18_HW_ defines */
......@@ -75,83 +78,66 @@ static const char * const hw_devicenames[] = {
"tuner",
"tveeprom",
"cs5345",
"gpio",
"cx23418",
"cx23418_DTV",
"cx23418_AV",
"gpio_audio_mux",
};
int cx18_i2c_register(struct cx18 *cx, unsigned idx)
{
struct i2c_board_info info;
struct i2c_client *c;
u8 id, bus;
int i;
CX18_DEBUG_I2C("i2c client register\n");
if (idx >= ARRAY_SIZE(hw_driverids) || hw_driverids[idx] == 0)
struct v4l2_subdev *sd;
int bus = hw_bus[idx];
struct i2c_adapter *adap = &cx->i2c_adap[bus];
const char *mod = hw_modules[idx];
const char *type = hw_devicenames[idx];
u32 hw = 1 << idx;
if (idx >= ARRAY_SIZE(hw_addrs))
return -1;
id = hw_driverids[idx];
bus = hw_bus[idx];
memset(&info, 0, sizeof(info));
strlcpy(info.type, hw_devicenames[idx], sizeof(info.type));
info.addr = hw_addrs[idx];
for (i = 0; i < I2C_CLIENTS_MAX; i++)
if (cx->i2c_clients[i] == NULL)
break;
if (i == I2C_CLIENTS_MAX) {
CX18_ERR("insufficient room for new I2C client!\n");
return -ENOMEM;
}
if (id != I2C_DRIVERID_TUNER) {
c = i2c_new_device(&cx->i2c_adap[bus], &info);
if (c->driver == NULL)
i2c_unregister_device(c);
else
cx->i2c_clients[i] = c;
return cx->i2c_clients[i] ? 0 : -ENODEV;
if (hw == CX18_HW_TUNER) {
/* special tuner group handling */
sd = v4l2_i2c_new_probed_subdev(adap, mod, type,
cx->card_i2c->radio);
if (sd != NULL)
sd->grp_id = hw;
sd = v4l2_i2c_new_probed_subdev(adap, mod, type,
cx->card_i2c->demod);
if (sd != NULL)
sd->grp_id = hw;
sd = v4l2_i2c_new_probed_subdev(adap, mod, type,
cx->card_i2c->tv);
if (sd != NULL)
sd->grp_id = hw;
return sd != NULL ? 0 : -1;
}
/* special tuner handling */
c = i2c_new_probed_device(&cx->i2c_adap[1], &info, cx->card_i2c->radio);
if (c && c->driver == NULL)
i2c_unregister_device(c);
else if (c)
cx->i2c_clients[i++] = c;
c = i2c_new_probed_device(&cx->i2c_adap[1], &info, cx->card_i2c->demod);
if (c && c->driver == NULL)
i2c_unregister_device(c);
else if (c)
cx->i2c_clients[i++] = c;
c = i2c_new_probed_device(&cx->i2c_adap[1], &info, cx->card_i2c->tv);
if (c && c->driver == NULL)
i2c_unregister_device(c);
else if (c)
cx->i2c_clients[i++] = c;
return 0;
}
/* Is it not an I2C device or one we do not wish to register? */
if (!hw_addrs[idx])
return -1;
static int attach_inform(struct i2c_client *client)
{
return 0;
/* It's an I2C device other than an analog tuner */
sd = v4l2_i2c_new_subdev(adap, mod, type, hw_addrs[idx]);
if (sd != NULL)
sd->grp_id = hw;
return sd != NULL ? 0 : -1;
}
static int detach_inform(struct i2c_client *client)
/* Find the first member of the subdev group id in hw */
struct v4l2_subdev *cx18_find_hw(struct cx18 *cx, u32 hw)
{
int i;
struct cx18 *cx = (struct cx18 *)i2c_get_adapdata(client->adapter);
struct v4l2_subdev *result = NULL;
struct v4l2_subdev *sd;
CX18_DEBUG_I2C("i2c client detach\n");
for (i = 0; i < I2C_CLIENTS_MAX; i++) {
if (cx->i2c_clients[i] == client) {
cx->i2c_clients[i] = NULL;
spin_lock(&cx->v4l2_dev.lock);
v4l2_device_for_each_subdev(sd, &cx->v4l2_dev) {
if (sd->grp_id == hw) {
result = sd;
break;
}
}
CX18_DEBUG_I2C("i2c detach [client=%s,%s]\n",
client->name, (i < I2C_CLIENTS_MAX) ? "ok" : "failed");
return 0;
spin_unlock(&cx->v4l2_dev.lock);
return result;
}
static void cx18_setscl(void *data, int state)
......@@ -204,8 +190,6 @@ static struct i2c_adapter cx18_i2c_adap_template = {
.id = I2C_HW_B_CX2341X,
.algo = NULL, /* set by i2c-algo-bit */
.algo_data = NULL, /* filled from template */
.client_register = attach_inform,
.client_unregister = detach_inform,
.owner = THIS_MODULE,
};
......@@ -221,151 +205,27 @@ static struct i2c_algo_bit_data cx18_i2c_algo_template = {
.timeout = CX18_ALGO_BIT_TIMEOUT*HZ /* jiffies */
};
static struct i2c_client cx18_i2c_client_template = {
.name = "cx18 internal",
};
int cx18_call_i2c_client(struct cx18 *cx, int addr, unsigned cmd, void *arg)
{
struct i2c_client *client;
int retval;
int i;
CX18_DEBUG_I2C("call_i2c_client addr=%02x\n", addr);
for (i = 0; i < I2C_CLIENTS_MAX; i++) {
client = cx->i2c_clients[i];
if (client == NULL || client->driver == NULL ||
client->driver->command == NULL)
continue;
if (addr == client->addr) {
retval = client->driver->command(client, cmd, arg);
return retval;
}
}
if (cmd != VIDIOC_DBG_G_CHIP_IDENT)
CX18_ERR("i2c addr 0x%02x not found for cmd 0x%x!\n",
addr, cmd);
return -ENODEV;
}
/* Find the i2c device based on the driver ID and return
its i2c address or -ENODEV if no matching device was found. */
static int cx18_i2c_id_addr(struct cx18 *cx, u32 id)
{
struct i2c_client *client;
int retval = -ENODEV;
int i;
for (i = 0; i < I2C_CLIENTS_MAX; i++) {
client = cx->i2c_clients[i];
if (client == NULL || client->driver == NULL)
continue;
if (id == client->driver->id) {
retval = client->addr;
break;
}
}
return retval;
}
/* Find the i2c device name matching the CX18_HW_ flag */
static const char *cx18_i2c_hw_name(u32 hw)
{
int i;
for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)
if (1 << i == hw)
return hw_devicenames[i];
return "unknown device";
}
/* Find the i2c device matching the CX18_HW_ flag and return
its i2c address or -ENODEV if no matching device was found. */
int cx18_i2c_hw_addr(struct cx18 *cx, u32 hw)
{
int i;
for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)
if (1 << i == hw)
return cx18_i2c_id_addr(cx, hw_driverids[i]);
return -ENODEV;
}
/* Calls i2c device based on CX18_HW_ flag. If hw == 0, then do nothing.
If hw == CX18_HW_GPIO then call the gpio handler. */
int cx18_i2c_hw(struct cx18 *cx, u32 hw, unsigned int cmd, void *arg)
{
int addr;
if (hw == 0)
return 0;
if (hw == CX18_HW_GPIO)
return cx18_gpio(cx, cmd, arg);
if (hw == CX18_HW_CX23418)
return v4l2_subdev_command(cx->sd_av, cmd, arg);
addr = cx18_i2c_hw_addr(cx, hw);
if (addr < 0) {
CX18_ERR("i2c hardware 0x%08x (%s) not found for cmd 0x%x!\n",
hw, cx18_i2c_hw_name(hw), cmd);
return addr;
}
return cx18_call_i2c_client(cx, addr, cmd, arg);
}
/* broadcast cmd for all I2C clients and for the gpio subsystem */
void cx18_call_i2c_clients(struct cx18 *cx, unsigned int cmd, void *arg)
{
if (cx->i2c_adap[0].algo == NULL || cx->i2c_adap[1].algo == NULL) {
CX18_ERR("adapter is not set\n");
return;
}
v4l2_subdev_command(cx->sd_av, cmd, arg);
i2c_clients_command(&cx->i2c_adap[0], cmd, arg);
i2c_clients_command(&cx->i2c_adap[1], cmd, arg);
if (cx->hw_flags & CX18_HW_GPIO)
cx18_gpio(cx, cmd, arg);
}
/* init + register i2c algo-bit adapter */
int init_cx18_i2c(struct cx18 *cx)
{
int i;
CX18_DEBUG_I2C("i2c init\n");
/* Sanity checks for the I2C hardware arrays. They must be the
* same size and GPIO/CX23418 must be the last entries.
*/
if (ARRAY_SIZE(hw_driverids) != ARRAY_SIZE(hw_addrs) ||
ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_addrs) ||
CX18_HW_GPIO != (1 << (ARRAY_SIZE(hw_addrs) - 2)) ||
CX18_HW_CX23418 != (1 << (ARRAY_SIZE(hw_addrs) - 1)) ||
hw_driverids[ARRAY_SIZE(hw_addrs) - 1]) {
CX18_ERR("Mismatched I2C hardware arrays\n");
return -ENODEV;
}
for (i = 0; i < 2; i++) {
memcpy(&cx->i2c_adap[i], &cx18_i2c_adap_template,
sizeof(struct i2c_adapter));
/* Setup algorithm for adapter */
memcpy(&cx->i2c_algo[i], &cx18_i2c_algo_template,
sizeof(struct i2c_algo_bit_data));
cx->i2c_algo_cb_data[i].cx = cx;
cx->i2c_algo_cb_data[i].bus_index = i;
cx->i2c_algo[i].data = &cx->i2c_algo_cb_data[i];
cx->i2c_adap[i].algo_data = &cx->i2c_algo[i];
/* Setup adapter */
memcpy(&cx->i2c_adap[i], &cx18_i2c_adap_template,
sizeof(struct i2c_adapter));
cx->i2c_adap[i].algo_data = &cx->i2c_algo[i];
sprintf(cx->i2c_adap[i].name + strlen(cx->i2c_adap[i].name),
" #%d-%d", cx->instance, i);
i2c_set_adapdata(&cx->i2c_adap[i], cx);
memcpy(&cx->i2c_client[i], &cx18_i2c_client_template,
sizeof(struct i2c_client));
sprintf(cx->i2c_client[i].name +
strlen(cx->i2c_client[i].name), "%d", i);
cx->i2c_client[i].adapter = &cx->i2c_adap[i];
i2c_set_adapdata(&cx->i2c_adap[i], &cx->v4l2_dev);
cx->i2c_adap[i].dev.parent = &cx->pci_dev->dev;
}
......
......@@ -21,11 +21,8 @@
* 02111-1307 USA
*/
int cx18_i2c_hw_addr(struct cx18 *cx, u32 hw);
int cx18_i2c_hw(struct cx18 *cx, u32 hw, unsigned int cmd, void *arg);
int cx18_call_i2c_client(struct cx18 *cx, int addr, unsigned cmd, void *arg);
void cx18_call_i2c_clients(struct cx18 *cx, unsigned int cmd, void *arg);
int cx18_i2c_register(struct cx18 *cx, unsigned idx);
struct v4l2_subdev *cx18_find_hw(struct cx18 *cx, u32 hw);
/* init + register i2c algo-bit adapter */
int init_cx18_i2c(struct cx18 *cx);
......
......@@ -372,15 +372,52 @@ static int cx18_g_chip_ident(struct file *file, void *fh,
struct v4l2_dbg_chip_ident *chip)
{
struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
int err = 0;
chip->ident = V4L2_IDENT_NONE;
chip->revision = 0;
if (v4l2_chip_match_host(&chip->match)) {
chip->ident = V4L2_IDENT_CX23418;
return 0;
switch (chip->match.type) {
case V4L2_CHIP_MATCH_HOST:
switch (chip->match.addr) {
case 0:
chip->ident = V4L2_IDENT_CX23418;
chip->revision = cx18_read_reg(cx, 0xC72028);
break;
case 1:
/*
* The A/V decoder is always present, but in the rare
* case that the card doesn't have analog, we don't
* use it. We find it w/o using the cx->sd_av pointer
*/
cx18_call_hw(cx, CX18_HW_418_AV,
core, g_chip_ident, chip);
break;
default:
/*
* Could return ident = V4L2_IDENT_UNKNOWN if we had
* other host chips at higher addresses, but we don't
*/
err = -EINVAL; /* per V4L2 spec */
break;
}
break;
case V4L2_CHIP_MATCH_I2C_DRIVER:
/* If needed, returns V4L2_IDENT_AMBIGUOUS without extra work */
cx18_call_all(cx, core, g_chip_ident, chip);
break;
case V4L2_CHIP_MATCH_I2C_ADDR:
/*
* We could return V4L2_IDENT_UNKNOWN, but we don't do the work
* to look if a chip is at the address with no driver. That's a
* dangerous thing to do with EEPROMs anyway.
*/
cx18_call_all(cx, core, g_chip_ident, chip);
break;
default:
err = -EINVAL;
break;
}
cx18_call_i2c_clients(cx, VIDIOC_DBG_G_CHIP_IDENT, chip);
return 0;
return err;
}
#ifdef CONFIG_VIDEO_ADV_DEBUG
......@@ -394,10 +431,10 @@ static int cx18_cxc(struct cx18 *cx, unsigned int cmd, void *arg)
return -EINVAL;
regs->size = 4;
if (cmd == VIDIOC_DBG_G_REGISTER)
regs->val = cx18_read_enc(cx, regs->reg);
else
if (cmd == VIDIOC_DBG_S_REGISTER)
cx18_write_enc(cx, regs->val, regs->reg);
else
regs->val = cx18_read_enc(cx, regs->reg);
return 0;
}
......@@ -408,7 +445,8 @@ static int cx18_g_register(struct file *file, void *fh,
if (v4l2_chip_match_host(&reg->match))
return cx18_cxc(cx, VIDIOC_DBG_G_REGISTER, reg);
cx18_call_i2c_clients(cx, VIDIOC_DBG_G_REGISTER, reg);
/* FIXME - errors shouldn't be ignored */
cx18_call_all(cx, core, g_register, reg);
return 0;
}
......@@ -419,7 +457,8 @@ static int cx18_s_register(struct file *file, void *fh,
if (v4l2_chip_match_host(&reg->match))
return cx18_cxc(cx, VIDIOC_DBG_S_REGISTER, reg);
cx18_call_i2c_clients(cx, VIDIOC_DBG_S_REGISTER, reg);
/* FIXME - errors shouldn't be ignored */
cx18_call_all(cx, core, s_register, reg);
return 0;
}
#endif
......@@ -598,7 +637,7 @@ static int cx18_g_frequency(struct file *file, void *fh,
if (vf->tuner != 0)
return -EINVAL;
cx18_call_i2c_clients(cx, VIDIOC_G_FREQUENCY, vf);
cx18_call_all(cx, tuner, g_frequency, vf);
return 0;
}
......@@ -617,7 +656,7 @@ int cx18_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf)
cx18_mute(cx);
CX18_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf->frequency);
cx18_call_i2c_clients(cx, VIDIOC_S_FREQUENCY, vf);
cx18_call_all(cx, tuner, s_frequency, vf);
cx18_unmute(cx);
return 0;
}
......@@ -666,7 +705,7 @@ int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std)
(unsigned long long) cx->std);
/* Tuner */
cx18_call_i2c_clients(cx, VIDIOC_S_STD, &cx->std);
cx18_call_all(cx, tuner, s_std, cx->std);
return 0;
}
......@@ -683,9 +722,7 @@ static int cx18_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
if (vt->index != 0)
return -EINVAL;
/* Setting tuner can only set audio mode */
cx18_call_i2c_clients(cx, VIDIOC_S_TUNER, vt);
cx18_call_all(cx, tuner, s_tuner, vt);
return 0;
}
......@@ -696,7 +733,7 @@ static int cx18_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
if (vt->index != 0)
return -EINVAL;
cx18_call_i2c_clients(cx, VIDIOC_G_TUNER, vt);
cx18_call_all(cx, tuner, g_tuner, vt);
if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) {
strlcpy(vt->name, "cx18 Radio Tuner", sizeof(vt->name));
......@@ -853,7 +890,7 @@ static int cx18_log_status(struct file *file, void *fh)
cx18_read_eeprom(cx, &tv);
}
cx18_call_i2c_clients(cx, VIDIOC_LOG_STATUS, NULL);
cx18_call_all(cx, core, log_status);
cx18_get_input(cx, cx->active_input, &vidin);
cx18_get_audio_input(cx, cx->audio_input, &audin);
CX18_INFO("Video Input: %s\n", vidin.name);
......@@ -894,7 +931,8 @@ static long cx18_default(struct file *file, void *fh, int cmd, void *arg)
CX18_DEBUG_IOCTL("VIDIOC_INT_S_AUDIO_ROUTING(%d, %d)\n",
route->input, route->output);
cx18_audio_set_route(cx, route);
cx18_call_hw(cx, cx->card->hw_audio_ctrl, audio, s_routing,
route);
break;
}
......@@ -922,6 +960,8 @@ long cx18_v4l2_ioctl(struct file *filp, unsigned int cmd,
mutex_lock(&cx->serialize_lock);
/* FIXME - consolidate v4l2_prio_check()'s here */
if (cx18_debug & CX18_DBGFLG_IOCTL)
vfd->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG;
res = video_ioctl2(filp, cmd, arg);
......
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