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 @@ ...@@ -23,9 +23,7 @@
#include "cx18-driver.h" #include "cx18-driver.h"
#include "cx18-io.h" #include "cx18-io.h"
#include "cx18-i2c.h"
#include "cx18-cards.h" #include "cx18-cards.h"
#include "cx18-audio.h"
#define CX18_AUDIO_ENABLE 0xc72014 #define CX18_AUDIO_ENABLE 0xc72014
...@@ -33,54 +31,32 @@ ...@@ -33,54 +31,32 @@
settings. */ settings. */
int cx18_audio_set_io(struct cx18 *cx) int cx18_audio_set_io(struct cx18 *cx)
{ {
const struct cx18_card_audio_input *in;
struct v4l2_routing route; struct v4l2_routing route;
u32 audio_input;
u32 val; u32 val;
int mux_input;
int err; int err;
/* Determine which input to use */ /* Determine which input to use */
if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) { if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags))
audio_input = cx->card->radio_input.audio_input; in = &cx->card->radio_input;
mux_input = cx->card->radio_input.muxer_input; else
} else { in = &cx->card->audio_inputs[cx->audio_input];
audio_input =
cx->card->audio_inputs[cx->audio_input].audio_input;
mux_input =
cx->card->audio_inputs[cx->audio_input].muxer_input;
}
/* handle muxer chips */ /* handle muxer chips */
route.input = mux_input; route.input = in->muxer_input;
route.output = 0; 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; route.input = in->audio_input;
err = cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, err = cx18_call_hw_err(cx, cx->card->hw_audio_ctrl,
VIDIOC_INT_S_AUDIO_ROUTING, &route); audio, s_routing, &route);
if (err) if (err)
return err; return err;
/* FIXME - this internal mux should be abstracted to a subdev */
val = cx18_read_reg(cx, CX18_AUDIO_ENABLE) & ~0x30; val = cx18_read_reg(cx, CX18_AUDIO_ENABLE) & ~0x30;
val |= (audio_input > CX18_AV_AUDIO_SERIAL2) ? 0x20 : val |= (in->audio_input > CX18_AV_AUDIO_SERIAL2) ? 0x20 :
(audio_input << 4); (in->audio_input << 4);
cx18_write_reg_expect(cx, val | 0xb00, CX18_AUDIO_ENABLE, val, 0x30); cx18_write_reg_expect(cx, val | 0xb00, CX18_AUDIO_ENABLE, val, 0x30);
return 0; 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 @@ ...@@ -22,5 +22,3 @@
*/ */
int cx18_audio_set_io(struct cx18 *cx); 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 = { ...@@ -1209,9 +1209,10 @@ static const struct v4l2_subdev_ops cx18_av_ops = {
.video = &cx18_av_video_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 cx18_av_state *state = &cx->av_state;
struct v4l2_subdev *sd;
state->rev = cx18_av_read4(cx, CXADEC_CHIP_CTRL) & 0xffff; state->rev = cx18_av_read4(cx, CXADEC_CHIP_CTRL) & 0xffff;
state->id = ((state->rev >> 4) == CXADEC_CHIP_TYPE_MAKO) state->id = ((state->rev >> 4) == CXADEC_CHIP_TYPE_MAKO)
...@@ -1224,13 +1225,13 @@ int cx18_av_probe(struct cx18 *cx, struct v4l2_subdev **sd) ...@@ -1224,13 +1225,13 @@ int cx18_av_probe(struct cx18 *cx, struct v4l2_subdev **sd)
state->slicer_line_delay = 0; state->slicer_line_delay = 0;
state->slicer_line_offset = (10 + state->slicer_line_delay - 2); state->slicer_line_offset = (10 + state->slicer_line_delay - 2);
*sd = &state->sd; sd = &state->sd;
v4l2_subdev_init(*sd, &cx18_av_ops); v4l2_subdev_init(sd, &cx18_av_ops);
v4l2_set_subdevdata(*sd, cx); v4l2_set_subdevdata(sd, cx);
snprintf((*sd)->name, sizeof((*sd)->name), snprintf(sd->name, sizeof(sd->name),
"%s internal A/V decoder", cx->v4l2_dev.name); "%s internal A/V decoder", cx->v4l2_dev.name);
(*sd)->grp_id = CX18_HW_CX23418; sd->grp_id = CX18_HW_418_AV;
return v4l2_device_register_subdev(&cx->v4l2_dev, *sd); return v4l2_device_register_subdev(&cx->v4l2_dev, sd);
} }
void cx18_av_exit(struct cx18 *cx, struct v4l2_subdev *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); ...@@ -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); int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 mask, u32 value);
void cx18_av_std_setup(struct cx18 *cx); 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); void cx18_av_exit(struct cx18 *cx, struct v4l2_subdev *sd);
/* ----------------------------------------------------------------------- */ /* ----------------------------------------------------------------------- */
......
...@@ -53,9 +53,9 @@ static const struct cx18_card cx18_card_hvr1600_esmt = { ...@@ -53,9 +53,9 @@ static const struct cx18_card cx18_card_hvr1600_esmt = {
.name = "Hauppauge HVR-1600", .name = "Hauppauge HVR-1600",
.comment = "Simultaneous Digital and Analog TV capture supported\n", .comment = "Simultaneous Digital and Analog TV capture supported\n",
.v4l2_capabilities = CX18_CAP_ENCODER, .v4l2_capabilities = CX18_CAP_ENCODER,
.hw_audio_ctrl = CX18_HW_CX23418, .hw_audio_ctrl = CX18_HW_418_AV,
.hw_muxer = CX18_HW_CS5345, .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, CX18_HW_CS5345 | CX18_HW_DVB,
.video_inputs = { .video_inputs = {
{ CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE7 }, { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE7 },
...@@ -99,9 +99,9 @@ static const struct cx18_card cx18_card_hvr1600_samsung = { ...@@ -99,9 +99,9 @@ static const struct cx18_card cx18_card_hvr1600_samsung = {
.name = "Hauppauge HVR-1600 (Preproduction)", .name = "Hauppauge HVR-1600 (Preproduction)",
.comment = "Simultaneous Digital and Analog TV capture supported\n", .comment = "Simultaneous Digital and Analog TV capture supported\n",
.v4l2_capabilities = CX18_CAP_ENCODER, .v4l2_capabilities = CX18_CAP_ENCODER,
.hw_audio_ctrl = CX18_HW_CX23418, .hw_audio_ctrl = CX18_HW_418_AV,
.hw_muxer = CX18_HW_CS5345, .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, CX18_HW_CS5345 | CX18_HW_DVB,
.video_inputs = { .video_inputs = {
{ CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE7 }, { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE7 },
...@@ -154,8 +154,8 @@ static const struct cx18_card cx18_card_h900 = { ...@@ -154,8 +154,8 @@ static const struct cx18_card cx18_card_h900 = {
.name = "Compro VideoMate H900", .name = "Compro VideoMate H900",
.comment = "Analog TV capture supported\n", .comment = "Analog TV capture supported\n",
.v4l2_capabilities = CX18_CAP_ENCODER, .v4l2_capabilities = CX18_CAP_ENCODER,
.hw_audio_ctrl = CX18_HW_CX23418, .hw_audio_ctrl = CX18_HW_418_AV,
.hw_all = CX18_HW_TUNER, .hw_all = CX18_HW_418_AV | CX18_HW_TUNER,
.video_inputs = { .video_inputs = {
{ CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 }, { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 },
{ CX18_CARD_INPUT_SVIDEO1, 1, { CX18_CARD_INPUT_SVIDEO1, 1,
...@@ -201,8 +201,8 @@ static const struct cx18_card cx18_card_mpc718 = { ...@@ -201,8 +201,8 @@ static const struct cx18_card cx18_card_mpc718 = {
.name = "Yuan MPC718", .name = "Yuan MPC718",
.comment = "Analog video capture works; some audio line in may not.\n", .comment = "Analog video capture works; some audio line in may not.\n",
.v4l2_capabilities = CX18_CAP_ENCODER, .v4l2_capabilities = CX18_CAP_ENCODER,
.hw_audio_ctrl = CX18_HW_CX23418, .hw_audio_ctrl = CX18_HW_418_AV,
.hw_all = CX18_HW_TUNER, .hw_all = CX18_HW_418_AV | CX18_HW_TUNER,
.video_inputs = { .video_inputs = {
{ CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 }, { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 },
{ CX18_CARD_INPUT_SVIDEO1, 1, { CX18_CARD_INPUT_SVIDEO1, 1,
...@@ -251,9 +251,9 @@ static const struct cx18_card cx18_card_cnxt_raptor_pal = { ...@@ -251,9 +251,9 @@ static const struct cx18_card cx18_card_cnxt_raptor_pal = {
.name = "Conexant Raptor PAL/SECAM", .name = "Conexant Raptor PAL/SECAM",
.comment = "Analog TV capture supported\n", .comment = "Analog TV capture supported\n",
.v4l2_capabilities = CX18_CAP_ENCODER, .v4l2_capabilities = CX18_CAP_ENCODER,
.hw_audio_ctrl = CX18_HW_CX23418, .hw_audio_ctrl = CX18_HW_418_AV,
.hw_muxer = CX18_HW_GPIO, .hw_muxer = CX18_HW_GPIO_AUDIO_MUX,
.hw_all = CX18_HW_TUNER | CX18_HW_GPIO, .hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_AUDIO_MUX,
.video_inputs = { .video_inputs = {
{ CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 }, { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 },
{ CX18_CARD_INPUT_SVIDEO1, 1, { CX18_CARD_INPUT_SVIDEO1, 1,
...@@ -306,8 +306,8 @@ static const struct cx18_card cx18_card_toshiba_qosmio_dvbt = { ...@@ -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" .comment = "Experimenters and photos needed for device to work well.\n"
"\tTo help, mail the ivtv-devel list (www.ivtvdriver.org).\n", "\tTo help, mail the ivtv-devel list (www.ivtvdriver.org).\n",
.v4l2_capabilities = CX18_CAP_ENCODER, .v4l2_capabilities = CX18_CAP_ENCODER,
.hw_audio_ctrl = CX18_HW_CX23418, .hw_audio_ctrl = CX18_HW_418_AV,
.hw_all = CX18_HW_TUNER, .hw_all = CX18_HW_418_AV | CX18_HW_TUNER,
.video_inputs = { .video_inputs = {
{ CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE6 }, { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE6 },
{ CX18_CARD_INPUT_SVIDEO1, 1, { CX18_CARD_INPUT_SVIDEO1, 1,
...@@ -350,9 +350,9 @@ static const struct cx18_card cx18_card_leadtek_pvr2100 = { ...@@ -350,9 +350,9 @@ static const struct cx18_card cx18_card_leadtek_pvr2100 = {
.comment = "Experimenters and photos needed for device to work well.\n" .comment = "Experimenters and photos needed for device to work well.\n"
"\tTo help, mail the ivtv-devel list (www.ivtvdriver.org).\n", "\tTo help, mail the ivtv-devel list (www.ivtvdriver.org).\n",
.v4l2_capabilities = CX18_CAP_ENCODER, .v4l2_capabilities = CX18_CAP_ENCODER,
.hw_audio_ctrl = CX18_HW_CX23418, .hw_audio_ctrl = CX18_HW_418_AV,
.hw_muxer = CX18_HW_GPIO, .hw_muxer = CX18_HW_GPIO_AUDIO_MUX,
.hw_all = CX18_HW_TUNER | CX18_HW_GPIO, .hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_AUDIO_MUX,
.video_inputs = { .video_inputs = {
{ CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 }, { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 },
{ CX18_CARD_INPUT_SVIDEO1, 1, { CX18_CARD_INPUT_SVIDEO1, 1,
......
...@@ -25,9 +25,9 @@ ...@@ -25,9 +25,9 @@
#define CX18_HW_TUNER (1 << 0) #define CX18_HW_TUNER (1 << 0)
#define CX18_HW_TVEEPROM (1 << 1) #define CX18_HW_TVEEPROM (1 << 1)
#define CX18_HW_CS5345 (1 << 2) #define CX18_HW_CS5345 (1 << 2)
#define CX18_HW_GPIO (1 << 3) #define CX18_HW_DVB (1 << 3)
#define CX18_HW_CX23418 (1 << 4) #define CX18_HW_418_AV (1 << 4)
#define CX18_HW_DVB (1 << 5) #define CX18_HW_GPIO_AUDIO_MUX (1 << 5)
/* video inputs */ /* video inputs */
#define CX18_CARD_INPUT_VID_TUNER 1 #define CX18_CARD_INPUT_VID_TUNER 1
...@@ -121,7 +121,7 @@ struct cx18_card { ...@@ -121,7 +121,7 @@ struct cx18_card {
char *comment; char *comment;
u32 v4l2_capabilities; u32 v4l2_capabilities;
u32 hw_audio_ctrl; /* hardware used for the V4L2 controls (only 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_muxer; /* hardware used to multiplex audio input */
u32 hw_all; /* all hardware used by the board */ u32 hw_all; /* all hardware used by the board */
struct cx18_card_video_input video_inputs[CX18_CARD_MAX_VIDEO_INPUTS]; 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) ...@@ -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_BASS:
case V4L2_CID_AUDIO_TREBLE: case V4L2_CID_AUDIO_TREBLE:
case V4L2_CID_AUDIO_LOUDNESS: 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; qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
return 0; return 0;
...@@ -134,7 +134,7 @@ static int cx18_s_ctrl(struct cx18 *cx, struct v4l2_control *vctrl) ...@@ -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_BASS:
case V4L2_CID_AUDIO_TREBLE: case V4L2_CID_AUDIO_TREBLE:
case V4L2_CID_AUDIO_LOUDNESS: 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: default:
CX18_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id); 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) ...@@ -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_BASS:
case V4L2_CID_AUDIO_TREBLE: case V4L2_CID_AUDIO_TREBLE:
case V4L2_CID_AUDIO_LOUDNESS: 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: default:
CX18_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id); CX18_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id);
return -EINVAL; return -EINVAL;
...@@ -260,10 +261,12 @@ int cx18_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c) ...@@ -260,10 +261,12 @@ int cx18_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
return err; return err;
} }
if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) { if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
static u32 freqs[3] = { 44100, 48000, 32000 };
struct cx18_api_func_private priv; struct cx18_api_func_private priv;
struct cx2341x_mpeg_params p = cx->params; struct cx2341x_mpeg_params p = cx->params;
int err = cx2341x_ext_ctrls(&p, atomic_read(&cx->ana_capturing), int err = cx2341x_ext_ctrls(&p, atomic_read(&cx->ana_capturing),
c, VIDIOC_S_EXT_CTRLS); c, VIDIOC_S_EXT_CTRLS);
unsigned int idx;
if (err) if (err)
return err; return err;
...@@ -287,7 +290,11 @@ int cx18_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c) ...@@ -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); err = cx18_setup_vbi_fmt(cx, p.stream_vbi_fmt);
cx->params = p; cx->params = p;
cx->dualwatch_stereo_mode = p.audio_properties & 0x0300; 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 err;
} }
return -EINVAL; return -EINVAL;
......
...@@ -269,11 +269,16 @@ static void cx18_iounmap(struct cx18 *cx) ...@@ -269,11 +269,16 @@ static void cx18_iounmap(struct cx18 *cx)
/* Hauppauge card? get values from tveeprom */ /* Hauppauge card? get values from tveeprom */
void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv) void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv)
{ {
struct i2c_client c;
u8 eedata[256]; u8 eedata[256];
cx->i2c_client[0].addr = 0xA0 >> 1; strncpy(c.name, "cx18 tveeprom tmp", sizeof(c.name));
tveeprom_read(&cx->i2c_client[0], eedata, sizeof(eedata)); c.name[sizeof(c.name)-1] = '\0';
tveeprom_hauppauge_analog(&cx->i2c_client[0], tv, eedata); 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) static void cx18_process_eeprom(struct cx18 *cx)
...@@ -553,8 +558,6 @@ static int __devinit cx18_init_struct1(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); cx->base_addr = pci_resource_start(cx->pci_dev, 0);
mutex_init(&cx->serialize_lock); 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->gpio_lock);
mutex_init(&cx->epu2apu_mb_lock); mutex_init(&cx->epu2apu_mb_lock);
mutex_init(&cx->epu2cpu_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, ...@@ -669,54 +672,41 @@ static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *pci_dev,
return 0; return 0;
} }
#ifdef MODULE static void cx18_init_subdevs(struct cx18 *cx)
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)
{ {
u32 hw = cx->card->hw_all; u32 hw = cx->card->hw_all;
u32 device;
int i; int i;
#ifdef MODULE for (i = 0, device = 1; i < 32; i++, device <<= 1) {
/* 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;
if (!(device & hw)) if (!(device & hw))
continue; continue;
if (device == CX18_HW_GPIO || device == CX18_HW_TVEEPROM ||
device == CX18_HW_CX23418 || device == CX18_HW_DVB) { switch (device) {
/* These 'devices' do not use i2c probing */ 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; cx->hw_flags |= device;
continue; break;
} case CX18_HW_418_AV:
cx18_i2c_register(cx, i); /* The A/V decoder gets probed earlier to set PLLs */
if (cx18_i2c_hw_addr(cx, device) > 0) /* 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; cx->hw_flags |= device;
break;
} }
}
if (cx->hw_flags & CX18_HW_418_AV)
cx->sd_av = cx18_find_hw(cx, CX18_HW_418_AV);
hw = cx->hw_flags; 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, static int __devinit cx18_probe(struct pci_dev *pci_dev,
...@@ -803,15 +793,17 @@ 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); cx->scb = (struct cx18_scb __iomem *)(cx->enc_mem + SCB_OFFSET);
cx18_init_scb(cx); cx18_init_scb(cx);
/* Initialize GPIO early so I2C device resets can be performed */
cx18_gpio_init(cx); 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) { if (retval) {
CX18_ERR("Could not register A/V decoder subdevice\n"); CX18_ERR("Could not register A/V decoder subdevice\n");
goto free_map; goto free_map;
} }
/* Initialize the A/V decoder PLLs to sane defaults */ /* 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 */ /* active i2c */
CX18_DEBUG_INFO("activating i2c...\n"); CX18_DEBUG_INFO("activating i2c...\n");
...@@ -873,7 +865,7 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev, ...@@ -873,7 +865,7 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev,
initialization. */ initialization. */
cx18_init_struct2(cx); cx18_init_struct2(cx);
cx18_load_and_init_modules(cx); cx18_init_subdevs(cx);
if (cx->std & V4L2_STD_525_60) { if (cx->std & V4L2_STD_525_60) {
cx->is_60hz = 1; cx->is_60hz = 1;
...@@ -895,7 +887,7 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev, ...@@ -895,7 +887,7 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev,
setup.mode_mask = T_ANALOG_TV; /* matches TV tuners */ setup.mode_mask = T_ANALOG_TV; /* matches TV tuners */
setup.tuner_callback = (setup.type == TUNER_XC2028) ? setup.tuner_callback = (setup.type == TUNER_XC2028) ?
cx18_reset_tuner_gpio : NULL; 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) { if (setup.type == TUNER_XC2028) {
static struct xc2028_ctrl ctrl = { static struct xc2028_ctrl ctrl = {
.fname = XC2028_DEFAULT_FIRMWARE, .fname = XC2028_DEFAULT_FIRMWARE,
...@@ -905,7 +897,7 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev, ...@@ -905,7 +897,7 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev,
.tuner = cx->options.tuner, .tuner = cx->options.tuner,
.priv = &ctrl, .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 { ...@@ -448,7 +448,8 @@ struct cx18 {
int instance; int instance;
struct pci_dev *pci_dev; struct pci_dev *pci_dev;
struct v4l2_device v4l2_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 struct cx18_card *card; /* card information */
const char *card_name; /* full name of the card */ const char *card_name; /* full name of the card */
...@@ -528,9 +529,6 @@ struct cx18 { ...@@ -528,9 +529,6 @@ struct cx18 {
struct i2c_adapter i2c_adap[2]; struct i2c_adapter i2c_adap[2];
struct i2c_algo_bit_data i2c_algo[2]; struct i2c_algo_bit_data i2c_algo[2];
struct cx18_i2c_algo_callback_data i2c_algo_cb_data[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 */ /* gpio */
u32 gpio_dir; u32 gpio_dir;
...@@ -573,4 +571,22 @@ static inline int cx18_raw_vbi(const struct cx18 *cx) ...@@ -573,4 +571,22 @@ static inline int cx18_raw_vbi(const struct cx18 *cx)
return cx->vbi.in.type == V4L2_BUF_TYPE_VBI_CAPTURE; 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 */ #endif /* CX18_DRIVER_H */
...@@ -136,7 +136,7 @@ static void cx18_dualwatch(struct cx18 *cx) ...@@ -136,7 +136,7 @@ static void cx18_dualwatch(struct cx18 *cx)
new_stereo_mode = cx->params.audio_properties & stereo_mask; new_stereo_mode = cx->params.audio_properties & stereo_mask;
memset(&vt, 0, sizeof(vt)); 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 && if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 &&
(vt.rxsubchans & V4L2_TUNER_SUB_LANG2)) (vt.rxsubchans & V4L2_TUNER_SUB_LANG2))
new_stereo_mode = dual; new_stereo_mode = dual;
...@@ -608,7 +608,7 @@ int cx18_v4l2_close(struct file *filp) ...@@ -608,7 +608,7 @@ int cx18_v4l2_close(struct file *filp)
/* Mark that the radio is no longer in use */ /* Mark that the radio is no longer in use */
clear_bit(CX18_F_I_RADIO_USER, &cx->i_flags); clear_bit(CX18_F_I_RADIO_USER, &cx->i_flags);
/* Switch tuner to TV */ /* 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) */ /* Select correct audio input (i.e. TV tuner or Line in) */
cx18_audio_set_io(cx); cx18_audio_set_io(cx);
if (atomic_read(&cx->ana_capturing) > 0) { if (atomic_read(&cx->ana_capturing) > 0) {
...@@ -671,7 +671,7 @@ static int cx18_serialized_open(struct cx18_stream *s, struct file *filp) ...@@ -671,7 +671,7 @@ static int cx18_serialized_open(struct cx18_stream *s, struct file *filp)
/* We have the radio */ /* We have the radio */
cx18_mute(cx); cx18_mute(cx);
/* Switch tuner to radio */ /* 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) */ /* Select the correct audio input (i.e. radio tuner) */
cx18_audio_set_io(cx); cx18_audio_set_io(cx);
/* Done! Unmute and continue. */ /* Done! Unmute and continue. */
......
...@@ -42,32 +42,35 @@ ...@@ -42,32 +42,35 @@
#define CX18_CS5345_I2C_ADDR 0x4c #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 */ /* This array should match the CX18_HW_ defines */
static const u8 hw_addrs[] = { static const u8 hw_addrs[] = {
0, 0, /* CX18_HW_TUNER */
0, 0, /* CX18_HW_TVEEPROM */
CX18_CS5345_I2C_ADDR, CX18_CS5345_I2C_ADDR, /* CX18_HW_CS5345 */
0, /* CX18_HW_GPIO dummy driver ID */ 0, /* CX18_HW_DVB */
0, /* CX18_HW_CX23418 dummy driver ID */ 0, /* CX18_HW_418_AV */
0, /* CX18_HW_GPIO_AUDIO_MUX */
}; };
/* This array should match the CX18_HW_ defines */ /* This array should match the CX18_HW_ defines */
/* This might well become a card-specific array */ /* This might well become a card-specific array */
static const u8 hw_bus[] = { static const u8 hw_bus[] = {
0, 1, /* CX18_HW_TUNER */
0, 0, /* CX18_HW_TVEEPROM */
0, 0, /* CX18_HW_CS5345 */
0, /* CX18_HW_GPIO dummy driver ID */ 0, /* CX18_HW_DVB */
0, /* CX18_HW_CX23418 dummy driver ID */ 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 */ /* This array should match the CX18_HW_ defines */
...@@ -75,83 +78,66 @@ static const char * const hw_devicenames[] = { ...@@ -75,83 +78,66 @@ static const char * const hw_devicenames[] = {
"tuner", "tuner",
"tveeprom", "tveeprom",
"cs5345", "cs5345",
"gpio", "cx23418_DTV",
"cx23418", "cx23418_AV",
"gpio_audio_mux",
}; };
int cx18_i2c_register(struct cx18 *cx, unsigned idx) int cx18_i2c_register(struct cx18 *cx, unsigned idx)
{ {
struct i2c_board_info info; struct v4l2_subdev *sd;
struct i2c_client *c; int bus = hw_bus[idx];
u8 id, bus; struct i2c_adapter *adap = &cx->i2c_adap[bus];
int i; const char *mod = hw_modules[idx];
const char *type = hw_devicenames[idx];
CX18_DEBUG_I2C("i2c client register\n"); u32 hw = 1 << idx;
if (idx >= ARRAY_SIZE(hw_driverids) || hw_driverids[idx] == 0)
if (idx >= ARRAY_SIZE(hw_addrs))
return -1; 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) { if (hw == CX18_HW_TUNER) {
CX18_ERR("insufficient room for new I2C client!\n"); /* special tuner group handling */
return -ENOMEM; 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;
} }
if (id != I2C_DRIVERID_TUNER) { /* Is it not an I2C device or one we do not wish to register? */
c = i2c_new_device(&cx->i2c_adap[bus], &info); if (!hw_addrs[idx])
if (c->driver == NULL) return -1;
i2c_unregister_device(c);
else
cx->i2c_clients[i] = c;
return cx->i2c_clients[i] ? 0 : -ENODEV;
}
/* 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;
}
static int attach_inform(struct i2c_client *client) /* It's an I2C device other than an analog tuner */
{ sd = v4l2_i2c_new_subdev(adap, mod, type, hw_addrs[idx]);
return 0; 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 v4l2_subdev *result = NULL;
struct cx18 *cx = (struct cx18 *)i2c_get_adapdata(client->adapter); struct v4l2_subdev *sd;
CX18_DEBUG_I2C("i2c client detach\n"); spin_lock(&cx->v4l2_dev.lock);
for (i = 0; i < I2C_CLIENTS_MAX; i++) { v4l2_device_for_each_subdev(sd, &cx->v4l2_dev) {
if (cx->i2c_clients[i] == client) { if (sd->grp_id == hw) {
cx->i2c_clients[i] = NULL; result = sd;
break; break;
} }
} }
CX18_DEBUG_I2C("i2c detach [client=%s,%s]\n", spin_unlock(&cx->v4l2_dev.lock);
client->name, (i < I2C_CLIENTS_MAX) ? "ok" : "failed"); return result;
return 0;
} }
static void cx18_setscl(void *data, int state) static void cx18_setscl(void *data, int state)
...@@ -204,8 +190,6 @@ static struct i2c_adapter cx18_i2c_adap_template = { ...@@ -204,8 +190,6 @@ static struct i2c_adapter cx18_i2c_adap_template = {
.id = I2C_HW_B_CX2341X, .id = I2C_HW_B_CX2341X,
.algo = NULL, /* set by i2c-algo-bit */ .algo = NULL, /* set by i2c-algo-bit */
.algo_data = NULL, /* filled from template */ .algo_data = NULL, /* filled from template */
.client_register = attach_inform,
.client_unregister = detach_inform,
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
...@@ -221,151 +205,27 @@ static struct i2c_algo_bit_data cx18_i2c_algo_template = { ...@@ -221,151 +205,27 @@ static struct i2c_algo_bit_data cx18_i2c_algo_template = {
.timeout = CX18_ALGO_BIT_TIMEOUT*HZ /* jiffies */ .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 */ /* init + register i2c algo-bit adapter */
int init_cx18_i2c(struct cx18 *cx) int init_cx18_i2c(struct cx18 *cx)
{ {
int i; int i;
CX18_DEBUG_I2C("i2c init\n"); 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++) { for (i = 0; i < 2; i++) {
memcpy(&cx->i2c_adap[i], &cx18_i2c_adap_template, /* Setup algorithm for adapter */
sizeof(struct i2c_adapter));
memcpy(&cx->i2c_algo[i], &cx18_i2c_algo_template, memcpy(&cx->i2c_algo[i], &cx18_i2c_algo_template,
sizeof(struct i2c_algo_bit_data)); sizeof(struct i2c_algo_bit_data));
cx->i2c_algo_cb_data[i].cx = cx; cx->i2c_algo_cb_data[i].cx = cx;
cx->i2c_algo_cb_data[i].bus_index = i; cx->i2c_algo_cb_data[i].bus_index = i;
cx->i2c_algo[i].data = &cx->i2c_algo_cb_data[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), sprintf(cx->i2c_adap[i].name + strlen(cx->i2c_adap[i].name),
" #%d-%d", cx->instance, i); " #%d-%d", cx->instance, i);
i2c_set_adapdata(&cx->i2c_adap[i], cx); i2c_set_adapdata(&cx->i2c_adap[i], &cx->v4l2_dev);
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];
cx->i2c_adap[i].dev.parent = &cx->pci_dev->dev; cx->i2c_adap[i].dev.parent = &cx->pci_dev->dev;
} }
......
...@@ -21,11 +21,8 @@ ...@@ -21,11 +21,8 @@
* 02111-1307 USA * 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); 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 */ /* init + register i2c algo-bit adapter */
int init_cx18_i2c(struct cx18 *cx); int init_cx18_i2c(struct cx18 *cx);
......
...@@ -372,15 +372,52 @@ static int cx18_g_chip_ident(struct file *file, void *fh, ...@@ -372,15 +372,52 @@ static int cx18_g_chip_ident(struct file *file, void *fh,
struct v4l2_dbg_chip_ident *chip) struct v4l2_dbg_chip_ident *chip)
{ {
struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
int err = 0;
chip->ident = V4L2_IDENT_NONE; chip->ident = V4L2_IDENT_NONE;
chip->revision = 0; chip->revision = 0;
if (v4l2_chip_match_host(&chip->match)) { switch (chip->match.type) {
case V4L2_CHIP_MATCH_HOST:
switch (chip->match.addr) {
case 0:
chip->ident = V4L2_IDENT_CX23418; chip->ident = V4L2_IDENT_CX23418;
return 0; 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;
} }
cx18_call_i2c_clients(cx, VIDIOC_DBG_G_CHIP_IDENT, chip); break;
return 0; 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;
}
return err;
} }
#ifdef CONFIG_VIDEO_ADV_DEBUG #ifdef CONFIG_VIDEO_ADV_DEBUG
...@@ -394,10 +431,10 @@ static int cx18_cxc(struct cx18 *cx, unsigned int cmd, void *arg) ...@@ -394,10 +431,10 @@ static int cx18_cxc(struct cx18 *cx, unsigned int cmd, void *arg)
return -EINVAL; return -EINVAL;
regs->size = 4; regs->size = 4;
if (cmd == VIDIOC_DBG_G_REGISTER) if (cmd == VIDIOC_DBG_S_REGISTER)
regs->val = cx18_read_enc(cx, regs->reg);
else
cx18_write_enc(cx, regs->val, regs->reg); cx18_write_enc(cx, regs->val, regs->reg);
else
regs->val = cx18_read_enc(cx, regs->reg);
return 0; return 0;
} }
...@@ -408,7 +445,8 @@ static int cx18_g_register(struct file *file, void *fh, ...@@ -408,7 +445,8 @@ static int cx18_g_register(struct file *file, void *fh,
if (v4l2_chip_match_host(&reg->match)) if (v4l2_chip_match_host(&reg->match))
return cx18_cxc(cx, VIDIOC_DBG_G_REGISTER, reg); 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; return 0;
} }
...@@ -419,7 +457,8 @@ static int cx18_s_register(struct file *file, void *fh, ...@@ -419,7 +457,8 @@ static int cx18_s_register(struct file *file, void *fh,
if (v4l2_chip_match_host(&reg->match)) if (v4l2_chip_match_host(&reg->match))
return cx18_cxc(cx, VIDIOC_DBG_S_REGISTER, reg); 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; return 0;
} }
#endif #endif
...@@ -598,7 +637,7 @@ static int cx18_g_frequency(struct file *file, void *fh, ...@@ -598,7 +637,7 @@ static int cx18_g_frequency(struct file *file, void *fh,
if (vf->tuner != 0) if (vf->tuner != 0)
return -EINVAL; return -EINVAL;
cx18_call_i2c_clients(cx, VIDIOC_G_FREQUENCY, vf); cx18_call_all(cx, tuner, g_frequency, vf);
return 0; return 0;
} }
...@@ -617,7 +656,7 @@ int cx18_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf) ...@@ -617,7 +656,7 @@ int cx18_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf)
cx18_mute(cx); cx18_mute(cx);
CX18_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf->frequency); 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); cx18_unmute(cx);
return 0; return 0;
} }
...@@ -666,7 +705,7 @@ int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std) ...@@ -666,7 +705,7 @@ int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std)
(unsigned long long) cx->std); (unsigned long long) cx->std);
/* Tuner */ /* Tuner */
cx18_call_i2c_clients(cx, VIDIOC_S_STD, &cx->std); cx18_call_all(cx, tuner, s_std, cx->std);
return 0; return 0;
} }
...@@ -683,9 +722,7 @@ static int cx18_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) ...@@ -683,9 +722,7 @@ static int cx18_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
if (vt->index != 0) if (vt->index != 0)
return -EINVAL; return -EINVAL;
/* Setting tuner can only set audio mode */ cx18_call_all(cx, tuner, s_tuner, vt);
cx18_call_i2c_clients(cx, VIDIOC_S_TUNER, vt);
return 0; return 0;
} }
...@@ -696,7 +733,7 @@ static int cx18_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) ...@@ -696,7 +733,7 @@ static int cx18_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
if (vt->index != 0) if (vt->index != 0)
return -EINVAL; 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)) { if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) {
strlcpy(vt->name, "cx18 Radio Tuner", sizeof(vt->name)); strlcpy(vt->name, "cx18 Radio Tuner", sizeof(vt->name));
...@@ -853,7 +890,7 @@ static int cx18_log_status(struct file *file, void *fh) ...@@ -853,7 +890,7 @@ static int cx18_log_status(struct file *file, void *fh)
cx18_read_eeprom(cx, &tv); 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_input(cx, cx->active_input, &vidin);
cx18_get_audio_input(cx, cx->audio_input, &audin); cx18_get_audio_input(cx, cx->audio_input, &audin);
CX18_INFO("Video Input: %s\n", vidin.name); 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) ...@@ -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", CX18_DEBUG_IOCTL("VIDIOC_INT_S_AUDIO_ROUTING(%d, %d)\n",
route->input, route->output); route->input, route->output);
cx18_audio_set_route(cx, route); cx18_call_hw(cx, cx->card->hw_audio_ctrl, audio, s_routing,
route);
break; break;
} }
...@@ -922,6 +960,8 @@ long cx18_v4l2_ioctl(struct file *filp, unsigned int cmd, ...@@ -922,6 +960,8 @@ long cx18_v4l2_ioctl(struct file *filp, unsigned int cmd,
mutex_lock(&cx->serialize_lock); mutex_lock(&cx->serialize_lock);
/* FIXME - consolidate v4l2_prio_check()'s here */
if (cx18_debug & CX18_DBGFLG_IOCTL) if (cx18_debug & CX18_DBGFLG_IOCTL)
vfd->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; vfd->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG;
res = video_ioctl2(filp, cmd, 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