mirror of https://gitee.com/openkylin/linux.git
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: Andy Walls <awalls@radix.net> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
parent
fa3e70360c
commit
ff2a200180
|
@ -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;
|
||||
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;
|
||||
}
|
||||
cx18_i2c_register(cx, i);
|
||||
if (cx18_i2c_hw_addr(cx, device) > 0)
|
||||
cx->hw_flags |= device;
|
||||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
|
||||
CX18_DEBUG_I2C("i2c client register\n");
|
||||
if (idx >= ARRAY_SIZE(hw_driverids) || hw_driverids[idx] == 0)
|
||||
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 (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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
/* Is it not an I2C device or one we do not wish to register? */
|
||||
if (!hw_addrs[idx])
|
||||
return -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;
|
||||
/* 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 attach_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)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
struct v4l2_subdev *result = NULL;
|
||||
struct v4l2_subdev *sd;
|
||||
|
||||
static int detach_inform(struct i2c_client *client)
|
||||
{
|
||||
int i;
|
||||
struct cx18 *cx = (struct cx18 *)i2c_get_adapdata(client->adapter);
|
||||
|
||||
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(®->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(®->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);
|
||||
|
|
Loading…
Reference in New Issue