V4L/DVB (10499): saa7146: convert saa7146 and mxb in particular to v4l2_subdev.

Modified mxb to load the i2c modules through v4l2_subdev. So no more probing.
Modified tea6415c and tea6420 to use the standard routing ops to do the
routing, rather than using private commands. Dropped the private commands
from tda9840 (they were never used except during initialization of the
module).

Added saa7146 support for VIDIOC_DBG_G_CHIP_IDENT.

Converted saa5246a and saa5249 to v4l2_subdev.

Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
Hans Verkuil 2009-02-07 11:18:05 -03:00 committed by Mauro Carvalho Chehab
parent d30e21ddcd
commit 1b8dac150a
11 changed files with 314 additions and 394 deletions

View file

@ -1,4 +1,5 @@
#include <media/saa7146_vv.h> #include <media/saa7146_vv.h>
#include <media/v4l2-chip-ident.h>
static int max_memory = 32; static int max_memory = 32;
@ -209,6 +210,7 @@ static struct v4l2_queryctrl controls[] = {
.step = 1, .step = 1,
.default_value = 128, .default_value = 128,
.type = V4L2_CTRL_TYPE_INTEGER, .type = V4L2_CTRL_TYPE_INTEGER,
.flags = V4L2_CTRL_FLAG_SLIDER,
},{ },{
.id = V4L2_CID_CONTRAST, .id = V4L2_CID_CONTRAST,
.name = "Contrast", .name = "Contrast",
@ -217,6 +219,7 @@ static struct v4l2_queryctrl controls[] = {
.step = 1, .step = 1,
.default_value = 64, .default_value = 64,
.type = V4L2_CTRL_TYPE_INTEGER, .type = V4L2_CTRL_TYPE_INTEGER,
.flags = V4L2_CTRL_FLAG_SLIDER,
},{ },{
.id = V4L2_CID_SATURATION, .id = V4L2_CID_SATURATION,
.name = "Saturation", .name = "Saturation",
@ -225,15 +228,16 @@ static struct v4l2_queryctrl controls[] = {
.step = 1, .step = 1,
.default_value = 64, .default_value = 64,
.type = V4L2_CTRL_TYPE_INTEGER, .type = V4L2_CTRL_TYPE_INTEGER,
.flags = V4L2_CTRL_FLAG_SLIDER,
},{ },{
.id = V4L2_CID_VFLIP, .id = V4L2_CID_VFLIP,
.name = "Vertical flip", .name = "Vertical Flip",
.minimum = 0, .minimum = 0,
.maximum = 1, .maximum = 1,
.type = V4L2_CTRL_TYPE_BOOLEAN, .type = V4L2_CTRL_TYPE_BOOLEAN,
},{ },{
.id = V4L2_CID_HFLIP, .id = V4L2_CID_HFLIP,
.name = "Horizontal flip", .name = "Horizontal Flip",
.minimum = 0, .minimum = 0,
.maximum = 1, .maximum = 1,
.type = V4L2_CTRL_TYPE_BOOLEAN, .type = V4L2_CTRL_TYPE_BOOLEAN,
@ -1112,6 +1116,22 @@ static int vidioc_streamoff(struct file *file, void *__fh, enum v4l2_buf_type ty
return err; return err;
} }
static int vidioc_g_chip_ident(struct file *file, void *__fh,
struct v4l2_dbg_chip_ident *chip)
{
struct saa7146_fh *fh = __fh;
struct saa7146_dev *dev = fh->dev;
chip->ident = V4L2_IDENT_NONE;
chip->revision = 0;
if (v4l2_chip_match_host(&chip->match)) {
chip->ident = V4L2_IDENT_SAA7146;
return 0;
}
return v4l2_device_call_until_err(&dev->v4l2_dev, 0,
core, g_chip_ident, chip);
}
#ifdef CONFIG_VIDEO_V4L1_COMPAT #ifdef CONFIG_VIDEO_V4L1_COMPAT
static int vidiocgmbuf(struct file *file, void *__fh, struct video_mbuf *mbuf) static int vidiocgmbuf(struct file *file, void *__fh, struct video_mbuf *mbuf)
{ {
@ -1152,6 +1172,7 @@ const struct v4l2_ioctl_ops saa7146_video_ioctl_ops = {
.vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay, .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay,
.vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay, .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay,
.vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap,
.vidioc_g_chip_ident = vidioc_g_chip_ident,
.vidioc_overlay = vidioc_overlay, .vidioc_overlay = vidioc_overlay,
.vidioc_g_fbuf = vidioc_g_fbuf, .vidioc_g_fbuf = vidioc_g_fbuf,

View file

@ -32,9 +32,14 @@
#include "mxb.h" #include "mxb.h"
#include "tea6415c.h" #include "tea6415c.h"
#include "tea6420.h" #include "tea6420.h"
#include "tda9840.h"
#define I2C_SAA7111 0x24 #define I2C_SAA5246A 0x11
#define I2C_SAA7111A 0x24
#define I2C_TDA9840 0x42
#define I2C_TEA6415C 0x43
#define I2C_TEA6420_1 0x4c
#define I2C_TEA6420_2 0x4d
#define I2C_TUNER 0x60
#define MXB_BOARD_CAN_DO_VBI(dev) (dev->revision != 0) #define MXB_BOARD_CAN_DO_VBI(dev) (dev->revision != 0)
@ -79,31 +84,29 @@ static struct {
static int video_audio_connect[MXB_INPUTS] = static int video_audio_connect[MXB_INPUTS] =
{ 0, 1, 3, 3 }; { 0, 1, 3, 3 };
/* these are the necessary input-output-pins for bringing one audio source /* These are the necessary input-output-pins for bringing one audio source
(see above) to the CD-output */ (see above) to the CD-output. Note that gain is set to 0 in this table. */
static struct tea6420_multiplex TEA6420_cd[MXB_AUDIOS+1][2] = static struct v4l2_routing TEA6420_cd[MXB_AUDIOS + 1][2] = {
{ { { 1, 1 }, { 1, 1 } }, /* Tuner */
{{1,1,0},{1,1,0}}, /* Tuner */ { { 5, 1 }, { 6, 1 } }, /* AUX 1 */
{{5,1,0},{6,1,0}}, /* AUX 1 */ { { 4, 1 }, { 6, 1 } }, /* AUX 2 */
{{4,1,0},{6,1,0}}, /* AUX 2 */ { { 3, 1 }, { 6, 1 } }, /* AUX 3 */
{{3,1,0},{6,1,0}}, /* AUX 3 */ { { 1, 1 }, { 3, 1 } }, /* Radio */
{{1,1,0},{3,1,0}}, /* Radio */ { { 1, 1 }, { 2, 1 } }, /* CD-Rom */
{{1,1,0},{2,1,0}}, /* CD-Rom */ { { 6, 1 }, { 6, 1 } } /* Mute */
{{6,1,0},{6,1,0}} /* Mute */ };
};
/* these are the necessary input-output-pins for bringing one audio source /* These are the necessary input-output-pins for bringing one audio source
(see above) to the line-output */ (see above) to the line-output. Note that gain is set to 0 in this table. */
static struct tea6420_multiplex TEA6420_line[MXB_AUDIOS+1][2] = static struct v4l2_routing TEA6420_line[MXB_AUDIOS + 1][2] = {
{ { { 2, 3 }, { 1, 2 } },
{{2,3,0},{1,2,0}}, { { 5, 3 }, { 6, 2 } },
{{5,3,0},{6,2,0}}, { { 4, 3 }, { 6, 2 } },
{{4,3,0},{6,2,0}}, { { 3, 3 }, { 6, 2 } },
{{3,3,0},{6,2,0}}, { { 2, 3 }, { 3, 2 } },
{{2,3,0},{3,2,0}}, { { 2, 3 }, { 2, 2 } },
{{2,3,0},{2,2,0}}, { { 6, 3 }, { 6, 2 } } /* Mute */
{{6,3,0},{6,2,0}} /* Mute */ };
};
#define MAXCONTROLS 1 #define MAXCONTROLS 1
static struct v4l2_queryctrl mxb_controls[] = { static struct v4l2_queryctrl mxb_controls[] = {
@ -117,12 +120,12 @@ struct mxb
struct i2c_adapter i2c_adapter; struct i2c_adapter i2c_adapter;
struct i2c_client *saa7111a; struct v4l2_subdev *saa7111a;
struct i2c_client *tda9840; struct v4l2_subdev *tda9840;
struct i2c_client *tea6415c; struct v4l2_subdev *tea6415c;
struct i2c_client *tuner; struct v4l2_subdev *tuner;
struct i2c_client *tea6420_1; struct v4l2_subdev *tea6420_1;
struct i2c_client *tea6420_2; struct v4l2_subdev *tea6420_2;
int cur_mode; /* current audio mode (mono, stereo, ...) */ int cur_mode; /* current audio mode (mono, stereo, ...) */
int cur_input; /* current input */ int cur_input; /* current input */
@ -130,84 +133,51 @@ struct mxb
struct v4l2_frequency cur_freq; /* current frequency the tuner is tuned to */ struct v4l2_frequency cur_freq; /* current frequency the tuner is tuned to */
}; };
#define saa7111a_call(mxb, o, f, args...) \
v4l2_subdev_call(mxb->saa7111a, o, f, ##args)
#define tea6420_1_call(mxb, o, f, args...) \
v4l2_subdev_call(mxb->tea6420_1, o, f, ##args)
#define tea6420_2_call(mxb, o, f, args...) \
v4l2_subdev_call(mxb->tea6420_2, o, f, ##args)
#define tda9840_call(mxb, o, f, args...) \
v4l2_subdev_call(mxb->tda9840, o, f, ##args)
#define tea6415c_call(mxb, o, f, args...) \
v4l2_subdev_call(mxb->tea6415c, o, f, ##args)
#define tuner_call(mxb, o, f, args...) \
v4l2_subdev_call(mxb->tuner, o, f, ##args)
#define call_all(dev, o, f, args...) \
v4l2_device_call_until_err(&dev->v4l2_dev, 0, o, f, ##args)
static struct saa7146_extension extension; static struct saa7146_extension extension;
static int mxb_check_clients(struct device *dev, void *data) static int mxb_probe(struct saa7146_dev *dev)
{ {
struct mxb *mxb = data; struct mxb *mxb = NULL;
struct i2c_client *client = i2c_verify_client(dev);
if (!client)
return 0;
if (I2C_ADDR_TEA6420_1 == client->addr)
mxb->tea6420_1 = client;
if (I2C_ADDR_TEA6420_2 == client->addr)
mxb->tea6420_2 = client;
if (I2C_TEA6415C_2 == client->addr)
mxb->tea6415c = client;
if (I2C_ADDR_TDA9840 == client->addr)
mxb->tda9840 = client;
if (I2C_SAA7111 == client->addr)
mxb->saa7111a = client;
if (0x60 == client->addr)
mxb->tuner = client;
return 0;
}
static int mxb_probe(struct saa7146_dev* dev)
{
struct mxb* mxb = NULL;
int result;
result = request_module("saa7115");
if (result < 0) {
printk("mxb: saa7111 i2c module not available.\n");
return -ENODEV;
}
result = request_module("tea6420");
if (result < 0) {
printk("mxb: tea6420 i2c module not available.\n");
return -ENODEV;
}
result = request_module("tea6415c");
if (result < 0) {
printk("mxb: tea6415c i2c module not available.\n");
return -ENODEV;
}
result = request_module("tda9840");
if (result < 0) {
printk("mxb: tda9840 i2c module not available.\n");
return -ENODEV;
}
result = request_module("tuner");
if (result < 0) {
printk("mxb: tuner i2c module not available.\n");
return -ENODEV;
}
mxb = kzalloc(sizeof(struct mxb), GFP_KERNEL); mxb = kzalloc(sizeof(struct mxb), GFP_KERNEL);
if( NULL == mxb ) { if (mxb == NULL) {
DEB_D(("not enough kernel memory.\n")); DEB_D(("not enough kernel memory.\n"));
return -ENOMEM; return -ENOMEM;
} }
mxb->i2c_adapter = (struct i2c_adapter) {
.class = I2C_CLASS_TV_ANALOG,
};
snprintf(mxb->i2c_adapter.name, sizeof(mxb->i2c_adapter.name), "mxb%d", mxb_num); snprintf(mxb->i2c_adapter.name, sizeof(mxb->i2c_adapter.name), "mxb%d", mxb_num);
saa7146_i2c_adapter_prepare(dev, &mxb->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480); saa7146_i2c_adapter_prepare(dev, &mxb->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
if(i2c_add_adapter(&mxb->i2c_adapter) < 0) { if (i2c_add_adapter(&mxb->i2c_adapter) < 0) {
DEB_S(("cannot register i2c-device. skipping.\n")); DEB_S(("cannot register i2c-device. skipping.\n"));
kfree(mxb); kfree(mxb);
return -EFAULT; return -EFAULT;
} }
/* loop through all i2c-devices on the bus and look who is there */ mxb->saa7111a = v4l2_i2c_new_subdev(&mxb->i2c_adapter, "saa7115", "saa7111", I2C_SAA7111A);
device_for_each_child(&mxb->i2c_adapter.dev, mxb, mxb_check_clients); mxb->tea6420_1 = v4l2_i2c_new_subdev(&mxb->i2c_adapter, "tea6420", "tea6420", I2C_TEA6420_1);
mxb->tea6420_2 = v4l2_i2c_new_subdev(&mxb->i2c_adapter, "tea6420", "tea6420", I2C_TEA6420_2);
mxb->tea6415c = v4l2_i2c_new_subdev(&mxb->i2c_adapter, "tea6415c", "tea6415c", I2C_TEA6415C);
mxb->tda9840 = v4l2_i2c_new_subdev(&mxb->i2c_adapter, "tda9840", "tda9840", I2C_TDA9840);
mxb->tuner = v4l2_i2c_new_subdev(&mxb->i2c_adapter, "tuner", "tuner", I2C_TUNER);
if (v4l2_i2c_new_subdev(&mxb->i2c_adapter, "saa5246a", "saa5246a", I2C_SAA5246A)) {
printk(KERN_INFO "mxb: found teletext decoder\n");
}
/* check if all devices are present */ /* check if all devices are present */
if (!mxb->tea6420_1 || !mxb->tea6420_2 || !mxb->tea6415c || if (!mxb->tea6420_1 || !mxb->tea6420_2 || !mxb->tea6415c ||
@ -295,47 +265,45 @@ static int mxb_init_done(struct saa7146_dev* dev)
struct v4l2_routing route; struct v4l2_routing route;
int i = 0, err = 0; int i = 0, err = 0;
struct tea6415c_multiplex vm;
/* select video mode in saa7111a */ /* select video mode in saa7111a */
mxb->saa7111a->driver->command(mxb->saa7111a, VIDIOC_S_STD, &std); saa7111a_call(mxb, tuner, s_std, std);
/* select tuner-output on saa7111a */ /* select tuner-output on saa7111a */
i = 0; i = 0;
route.input = SAA7115_COMPOSITE0; route.input = SAA7115_COMPOSITE0;
route.output = SAA7111_FMT_CCIR | SAA7111_VBI_BYPASS; route.output = SAA7111_FMT_CCIR | SAA7111_VBI_BYPASS;
mxb->saa7111a->driver->command(mxb->saa7111a, VIDIOC_INT_S_VIDEO_ROUTING, &route); saa7111a_call(mxb, video, s_routing, &route);
/* select a tuner type */ /* select a tuner type */
tun_setup.mode_mask = T_ANALOG_TV; tun_setup.mode_mask = T_ANALOG_TV;
tun_setup.addr = ADDR_UNSET; tun_setup.addr = ADDR_UNSET;
tun_setup.type = TUNER_PHILIPS_PAL; tun_setup.type = TUNER_PHILIPS_PAL;
mxb->tuner->driver->command(mxb->tuner, TUNER_SET_TYPE_ADDR, &tun_setup); tuner_call(mxb, tuner, s_type_addr, &tun_setup);
/* tune in some frequency on tuner */ /* tune in some frequency on tuner */
mxb->cur_freq.tuner = 0; mxb->cur_freq.tuner = 0;
mxb->cur_freq.type = V4L2_TUNER_ANALOG_TV; mxb->cur_freq.type = V4L2_TUNER_ANALOG_TV;
mxb->cur_freq.frequency = freq; mxb->cur_freq.frequency = freq;
mxb->tuner->driver->command(mxb->tuner, VIDIOC_S_FREQUENCY, tuner_call(mxb, tuner, s_frequency, &mxb->cur_freq);
&mxb->cur_freq);
/* set a default video standard */ /* set a default video standard */
mxb->tuner->driver->command(mxb->tuner, VIDIOC_S_STD, &std); tuner_call(mxb, tuner, s_std, std);
/* mute audio on tea6420s */ /* mute audio on tea6420s */
mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH, &TEA6420_line[6][0]); tea6420_1_call(mxb, audio, s_routing, &TEA6420_line[6][0]);
mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH, &TEA6420_line[6][1]); tea6420_2_call(mxb, audio, s_routing, &TEA6420_line[6][1]);
mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH, &TEA6420_cd[6][0]); tea6420_1_call(mxb, audio, s_routing, &TEA6420_line[6][0]);
mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH, &TEA6420_cd[6][1]); tea6420_2_call(mxb, audio, s_routing, &TEA6420_line[6][1]);
/* switch to tuner-channel on tea6415c*/ /* switch to tuner-channel on tea6415c */
vm.out = 17; route.input = 3;
vm.in = 3; route.output = 17;
mxb->tea6415c->driver->command(mxb->tea6415c, TEA6415C_SWITCH, &vm); tea6415c_call(mxb, video, s_routing, &route);
/* select tuner-output on multicable on tea6415c*/ /* select tuner-output on multicable on tea6415c */
vm.in = 3; route.input = 3;
vm.out = 13; route.output = 13;
mxb->tea6415c->driver->command(mxb->tea6415c, TEA6415C_SWITCH, &vm); tea6415c_call(mxb, video, s_routing, &route);
/* the rest for mxb */ /* the rest for mxb */
mxb->cur_input = 0; mxb->cur_input = 0;
@ -461,14 +429,14 @@ static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *vc)
mxb->cur_mute = vc->value; mxb->cur_mute = vc->value;
if (!vc->value) { if (!vc->value) {
/* switch the audio-source */ /* switch the audio-source */
mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH, tea6420_1_call(mxb, audio, s_routing,
&TEA6420_line[video_audio_connect[mxb->cur_input]][0]); &TEA6420_line[video_audio_connect[mxb->cur_input]][0]);
mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH, tea6420_2_call(mxb, audio, s_routing,
&TEA6420_line[video_audio_connect[mxb->cur_input]][1]); &TEA6420_line[video_audio_connect[mxb->cur_input]][1]);
} else { } else {
mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH, tea6420_1_call(mxb, audio, s_routing,
&TEA6420_line[6][0]); &TEA6420_line[6][0]);
mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH, tea6420_2_call(mxb, audio, s_routing,
&TEA6420_line[6][1]); &TEA6420_line[6][1]);
} }
DEB_EE(("VIDIOC_S_CTRL, V4L2_CID_AUDIO_MUTE: %d.\n", vc->value)); DEB_EE(("VIDIOC_S_CTRL, V4L2_CID_AUDIO_MUTE: %d.\n", vc->value));
@ -499,7 +467,6 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
{ {
struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
struct mxb *mxb = (struct mxb *)dev->ext_priv; struct mxb *mxb = (struct mxb *)dev->ext_priv;
struct tea6415c_multiplex vm;
struct v4l2_routing route; struct v4l2_routing route;
int i = 0; int i = 0;
@ -518,16 +485,16 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
switch (input) { switch (input) {
case TUNER: case TUNER:
i = SAA7115_COMPOSITE0; i = SAA7115_COMPOSITE0;
vm.in = 3; route.input = 3;
vm.out = 17; route.output = 17;
if (mxb->tea6415c->driver->command(mxb->tea6415c, TEA6415C_SWITCH, &vm)) { if (tea6415c_call(mxb, video, s_routing, &route)) {
printk(KERN_ERR "VIDIOC_S_INPUT: could not address tea6415c #1\n"); printk(KERN_ERR "VIDIOC_S_INPUT: could not address tea6415c #1\n");
return -EFAULT; return -EFAULT;
} }
/* connect tuner-output always to multicable */ /* connect tuner-output always to multicable */
vm.in = 3; route.input = 3;
vm.out = 13; route.output = 13;
break; break;
case AUX3_YC: case AUX3_YC:
/* nothing to be done here. aux3_yc is /* nothing to be done here. aux3_yc is
@ -541,8 +508,8 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
break; break;
case AUX1: case AUX1:
i = SAA7115_COMPOSITE0; i = SAA7115_COMPOSITE0;
vm.in = 1; route.input = 1;
vm.out = 17; route.output = 17;
break; break;
} }
@ -550,7 +517,7 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
switch (input) { switch (input) {
case TUNER: case TUNER:
case AUX1: case AUX1:
if (mxb->tea6415c->driver->command(mxb->tea6415c, TEA6415C_SWITCH, &vm)) { if (tea6415c_call(mxb, video, s_routing, &route)) {
printk(KERN_ERR "VIDIOC_S_INPUT: could not address tea6415c #3\n"); printk(KERN_ERR "VIDIOC_S_INPUT: could not address tea6415c #3\n");
return -EFAULT; return -EFAULT;
} }
@ -562,14 +529,14 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
/* switch video in saa7111a */ /* switch video in saa7111a */
route.input = i; route.input = i;
route.output = 0; route.output = 0;
if (mxb->saa7111a->driver->command(mxb->saa7111a, VIDIOC_INT_S_VIDEO_ROUTING, &route)) if (saa7111a_call(mxb, video, s_routing, &route))
printk(KERN_ERR "VIDIOC_S_INPUT: could not address saa7111a #1.\n"); printk(KERN_ERR "VIDIOC_S_INPUT: could not address saa7111a #1.\n");
/* switch the audio-source only if necessary */ /* switch the audio-source only if necessary */
if (0 == mxb->cur_mute) { if (0 == mxb->cur_mute) {
mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH, tea6420_1_call(mxb, audio, s_routing,
&TEA6420_line[video_audio_connect[input]][0]); &TEA6420_line[video_audio_connect[input]][0]);
mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH, tea6420_2_call(mxb, audio, s_routing,
&TEA6420_line[video_audio_connect[input]][1]); &TEA6420_line[video_audio_connect[input]][1]);
} }
@ -589,14 +556,12 @@ static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *t)
DEB_EE(("VIDIOC_G_TUNER: %d\n", t->index)); DEB_EE(("VIDIOC_G_TUNER: %d\n", t->index));
memset(t, 0, sizeof(*t)); memset(t, 0, sizeof(*t));
i2c_clients_command(&mxb->i2c_adapter, VIDIOC_G_TUNER, t);
strlcpy(t->name, "TV Tuner", sizeof(t->name)); strlcpy(t->name, "TV Tuner", sizeof(t->name));
t->type = V4L2_TUNER_ANALOG_TV; t->type = V4L2_TUNER_ANALOG_TV;
t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO | t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO |
V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
t->audmode = mxb->cur_mode; t->audmode = mxb->cur_mode;
return 0; return call_all(dev, tuner, g_tuner, t);
} }
static int vidioc_s_tuner(struct file *file, void *fh, struct v4l2_tuner *t) static int vidioc_s_tuner(struct file *file, void *fh, struct v4l2_tuner *t)
@ -610,8 +575,7 @@ static int vidioc_s_tuner(struct file *file, void *fh, struct v4l2_tuner *t)
} }
mxb->cur_mode = t->audmode; mxb->cur_mode = t->audmode;
i2c_clients_command(&mxb->i2c_adapter, VIDIOC_S_TUNER, t); return call_all(dev, tuner, s_tuner, t);
return 0;
} }
static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency *f) static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency *f)
@ -652,7 +616,7 @@ static int vidioc_s_frequency(struct file *file, void *fh, struct v4l2_frequency
DEB_EE(("VIDIOC_S_FREQUENCY: freq:0x%08x.\n", mxb->cur_freq.frequency)); DEB_EE(("VIDIOC_S_FREQUENCY: freq:0x%08x.\n", mxb->cur_freq.frequency));
/* tune in desired frequency */ /* tune in desired frequency */
mxb->tuner->driver->command(mxb->tuner, VIDIOC_S_FREQUENCY, &mxb->cur_freq); tuner_call(mxb, tuner, s_frequency, &mxb->cur_freq);
/* hack: changing the frequency should invalidate the vbi-counter (=> alevt) */ /* hack: changing the frequency should invalidate the vbi-counter (=> alevt) */
spin_lock(&dev->slock); spin_lock(&dev->slock);
@ -687,19 +651,15 @@ static int vidioc_s_audio(struct file *file, void *fh, struct v4l2_audio *a)
static int vidioc_g_register(struct file *file, void *fh, struct v4l2_dbg_register *reg) static int vidioc_g_register(struct file *file, void *fh, struct v4l2_dbg_register *reg)
{ {
struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
struct mxb *mxb = (struct mxb *)dev->ext_priv;
i2c_clients_command(&mxb->i2c_adapter, VIDIOC_DBG_G_REGISTER, reg); return call_all(dev, core, g_register, reg);
return 0;
} }
static int vidioc_s_register(struct file *file, void *fh, struct v4l2_dbg_register *reg) static int vidioc_s_register(struct file *file, void *fh, struct v4l2_dbg_register *reg)
{ {
struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
struct mxb *mxb = (struct mxb *)dev->ext_priv;
i2c_clients_command(&mxb->i2c_adapter, VIDIOC_DBG_S_REGISTER, reg); return call_all(dev, core, s_register, reg);
return 0;
} }
#endif #endif
@ -720,8 +680,8 @@ static long vidioc_default(struct file *file, void *fh, int cmd, void *arg)
DEB_EE(("MXB_S_AUDIO_CD: i:%d.\n", i)); DEB_EE(("MXB_S_AUDIO_CD: i:%d.\n", i));
mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH, &TEA6420_cd[i][0]); tea6420_1_call(mxb, audio, s_routing, &TEA6420_cd[i][0]);
mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH, &TEA6420_cd[i][1]); tea6420_2_call(mxb, audio, s_routing, &TEA6420_cd[i][1]);
return 0; return 0;
} }
@ -735,8 +695,8 @@ static long vidioc_default(struct file *file, void *fh, int cmd, void *arg)
} }
DEB_EE(("MXB_S_AUDIO_LINE: i:%d.\n", i)); DEB_EE(("MXB_S_AUDIO_LINE: i:%d.\n", i));
mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH, &TEA6420_line[i][0]); tea6420_1_call(mxb, audio, s_routing, &TEA6420_line[i][0]);
mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH, &TEA6420_line[i][1]); tea6420_2_call(mxb, audio, s_routing, &TEA6420_line[i][1]);
return 0; return 0;
} }
@ -791,13 +751,6 @@ static int mxb_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data
} }
} }
i2c_use_client(mxb->tea6420_1);
i2c_use_client(mxb->tea6420_2);
i2c_use_client(mxb->tea6415c);
i2c_use_client(mxb->tda9840);
i2c_use_client(mxb->saa7111a);
i2c_use_client(mxb->tuner);
printk("mxb: found Multimedia eXtension Board #%d.\n", mxb_num); printk("mxb: found Multimedia eXtension Board #%d.\n", mxb_num);
mxb_num++; mxb_num++;
@ -811,13 +764,6 @@ static int mxb_detach(struct saa7146_dev *dev)
DEB_EE(("dev:%p\n", dev)); DEB_EE(("dev:%p\n", dev));
i2c_release_client(mxb->tea6420_1);
i2c_release_client(mxb->tea6420_2);
i2c_release_client(mxb->tea6415c);
i2c_release_client(mxb->tda9840);
i2c_release_client(mxb->saa7111a);
i2c_release_client(mxb->tuner);
saa7146_unregister_device(&mxb->video_dev,dev); saa7146_unregister_device(&mxb->video_dev,dev);
if (MXB_BOARD_CAN_DO_VBI(dev)) if (MXB_BOARD_CAN_DO_VBI(dev))
saa7146_unregister_device(&mxb->vbi_dev, dev); saa7146_unregister_device(&mxb->vbi_dev, dev);
@ -834,8 +780,6 @@ static int mxb_detach(struct saa7146_dev *dev)
static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standard) static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standard)
{ {
struct mxb *mxb = (struct mxb *)dev->ext_priv; struct mxb *mxb = (struct mxb *)dev->ext_priv;
int zero = 0;
int one = 1;
if (V4L2_STD_PAL_I == standard->id) { if (V4L2_STD_PAL_I == standard->id) {
v4l2_std_id std = V4L2_STD_PAL_I; v4l2_std_id std = V4L2_STD_PAL_I;
@ -844,8 +788,8 @@ static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standa
/* set the 7146 gpio register -- I don't know what this does exactly */ /* set the 7146 gpio register -- I don't know what this does exactly */
saa7146_write(dev, GPIO_CTRL, 0x00404050); saa7146_write(dev, GPIO_CTRL, 0x00404050);
/* unset the 7111 gpio register -- I don't know what this does exactly */ /* unset the 7111 gpio register -- I don't know what this does exactly */
mxb->saa7111a->driver->command(mxb->saa7111a, VIDIOC_INT_S_GPIO, &zero); saa7111a_call(mxb, core, s_gpio, 0);
mxb->tuner->driver->command(mxb->tuner, VIDIOC_S_STD, &std); tuner_call(mxb, tuner, s_std, std);
} else { } else {
v4l2_std_id std = V4L2_STD_PAL_BG; v4l2_std_id std = V4L2_STD_PAL_BG;
@ -853,8 +797,8 @@ static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standa
/* set the 7146 gpio register -- I don't know what this does exactly */ /* set the 7146 gpio register -- I don't know what this does exactly */
saa7146_write(dev, GPIO_CTRL, 0x00404050); saa7146_write(dev, GPIO_CTRL, 0x00404050);
/* set the 7111 gpio register -- I don't know what this does exactly */ /* set the 7111 gpio register -- I don't know what this does exactly */
mxb->saa7111a->driver->command(mxb->saa7111a, VIDIOC_INT_S_GPIO, &one); saa7111a_call(mxb, core, s_gpio, 1);
mxb->tuner->driver->command(mxb->tuner, VIDIOC_S_STD, &std); tuner_call(mxb, tuner, s_std, std);
} }
return 0; return 0;
} }

View file

@ -46,10 +46,11 @@
#include <linux/smp_lock.h> #include <linux/smp_lock.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/videotext.h> #include <linux/videotext.h>
#include <linux/videodev.h> #include <linux/videodev2.h>
#include <media/v4l2-common.h> #include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-ioctl.h> #include <media/v4l2-ioctl.h>
#include <media/v4l2-i2c-drv-legacy.h> #include <media/v4l2-i2c-drv.h>
MODULE_AUTHOR("Michael Geng <linux@MichaelGeng.de>"); MODULE_AUTHOR("Michael Geng <linux@MichaelGeng.de>");
MODULE_DESCRIPTION("Philips SAA5246A, SAA5281 Teletext decoder driver"); MODULE_DESCRIPTION("Philips SAA5246A, SAA5281 Teletext decoder driver");
@ -388,13 +389,19 @@ MODULE_LICENSE("GPL");
struct saa5246a_device struct saa5246a_device
{ {
struct v4l2_subdev sd;
struct video_device *vdev;
u8 pgbuf[NUM_DAUS][VTX_VIRTUALSIZE]; u8 pgbuf[NUM_DAUS][VTX_VIRTUALSIZE];
int is_searching[NUM_DAUS]; int is_searching[NUM_DAUS];
struct i2c_client *client;
unsigned long in_use; unsigned long in_use;
struct mutex lock; struct mutex lock;
}; };
static inline struct saa5246a_device *to_dev(struct v4l2_subdev *sd)
{
return container_of(sd, struct saa5246a_device, sd);
}
static struct video_device saa_template; /* Declared near bottom */ static struct video_device saa_template; /* Declared near bottom */
/* /*
@ -403,12 +410,13 @@ static struct video_device saa_template; /* Declared near bottom */
static int i2c_sendbuf(struct saa5246a_device *t, int reg, int count, u8 *data) static int i2c_sendbuf(struct saa5246a_device *t, int reg, int count, u8 *data)
{ {
struct i2c_client *client = v4l2_get_subdevdata(&t->sd);
char buf[64]; char buf[64];
buf[0] = reg; buf[0] = reg;
memcpy(buf+1, data, count); memcpy(buf+1, data, count);
if(i2c_master_send(t->client, buf, count+1)==count+1) if (i2c_master_send(client, buf, count + 1) == count + 1)
return 0; return 0;
return -1; return -1;
} }
@ -436,7 +444,9 @@ static int i2c_senddata(struct saa5246a_device *t, ...)
*/ */
static int i2c_getdata(struct saa5246a_device *t, int count, u8 *buf) static int i2c_getdata(struct saa5246a_device *t, int count, u8 *buf)
{ {
if(i2c_master_recv(t->client, buf, count)!=count) struct i2c_client *client = v4l2_get_subdevdata(&t->sd);
if (i2c_master_recv(client, buf, count) != count)
return -1; return -1;
return 0; return 0;
} }
@ -961,9 +971,6 @@ static int saa5246a_open(struct file *file)
{ {
struct saa5246a_device *t = video_drvdata(file); struct saa5246a_device *t = video_drvdata(file);
if (t->client == NULL)
return -ENODEV;
if (test_and_set_bit(0, &t->in_use)) if (test_and_set_bit(0, &t->in_use))
return -EBUSY; return -EBUSY;
@ -1033,18 +1040,29 @@ static struct video_device saa_template =
.minor = -1, .minor = -1,
}; };
/* Addresses to scan */ static int saa5246a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
static unsigned short normal_i2c[] = { 0x22 >> 1, I2C_CLIENT_END }; {
struct i2c_client *client = v4l2_get_subdevdata(sd);
return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA5246A, 0);
}
static const struct v4l2_subdev_core_ops saa5246a_core_ops = {
.g_chip_ident = saa5246a_g_chip_ident,
};
static const struct v4l2_subdev_ops saa5246a_ops = {
.core = &saa5246a_core_ops,
};
I2C_CLIENT_INSMOD;
static int saa5246a_probe(struct i2c_client *client, static int saa5246a_probe(struct i2c_client *client,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
int pgbuf; int pgbuf;
int err; int err;
struct video_device *vd;
struct saa5246a_device *t; struct saa5246a_device *t;
struct v4l2_subdev *sd;
v4l_info(client, "chip found @ 0x%x (%s)\n", v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name); client->addr << 1, client->adapter->name);
@ -1053,40 +1071,43 @@ static int saa5246a_probe(struct i2c_client *client,
t = kzalloc(sizeof(*t), GFP_KERNEL); t = kzalloc(sizeof(*t), GFP_KERNEL);
if (t == NULL) if (t == NULL)
return -ENOMEM; return -ENOMEM;
sd = &t->sd;
v4l2_i2c_subdev_init(sd, client, &saa5246a_ops);
mutex_init(&t->lock); mutex_init(&t->lock);
/* Now create a video4linux device */ /* Now create a video4linux device */
vd = video_device_alloc(); t->vdev = video_device_alloc();
if (vd == NULL) { if (t->vdev == NULL) {
kfree(t); kfree(t);
return -ENOMEM; return -ENOMEM;
} }
i2c_set_clientdata(client, vd); memcpy(t->vdev, &saa_template, sizeof(*t->vdev));
memcpy(vd, &saa_template, sizeof(*vd));
for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) { for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) {
memset(t->pgbuf[pgbuf], ' ', sizeof(t->pgbuf[0])); memset(t->pgbuf[pgbuf], ' ', sizeof(t->pgbuf[0]));
t->is_searching[pgbuf] = false; t->is_searching[pgbuf] = false;
} }
video_set_drvdata(vd, t); video_set_drvdata(t->vdev, t);
/* Register it */ /* Register it */
err = video_register_device(vd, VFL_TYPE_VTX, -1); err = video_register_device(t->vdev, VFL_TYPE_VTX, -1);
if (err < 0) { if (err < 0) {
kfree(t); kfree(t);
video_device_release(vd); video_device_release(t->vdev);
t->vdev = NULL;
return err; return err;
} }
t->client = client;
return 0; return 0;
} }
static int saa5246a_remove(struct i2c_client *client) static int saa5246a_remove(struct i2c_client *client)
{ {
struct video_device *vd = i2c_get_clientdata(client); struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct saa5246a_device *t = to_dev(sd);
video_unregister_device(vd); video_unregister_device(t->vdev);
kfree(video_get_drvdata(vd)); v4l2_device_unregister_subdev(sd);
kfree(t);
return 0; return 0;
} }

View file

@ -50,15 +50,17 @@
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/videotext.h> #include <linux/videotext.h>
#include <linux/videodev.h> #include <linux/videodev2.h>
#include <media/v4l2-common.h> #include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-ioctl.h> #include <media/v4l2-ioctl.h>
#include <media/v4l2-i2c-drv-legacy.h> #include <media/v4l2-i2c-drv.h>
MODULE_AUTHOR("Michael Geng <linux@MichaelGeng.de>"); MODULE_AUTHOR("Michael Geng <linux@MichaelGeng.de>");
MODULE_DESCRIPTION("Philips SAA5249 Teletext decoder driver"); MODULE_DESCRIPTION("Philips SAA5249 Teletext decoder driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
#define VTX_VER_MAJ 1 #define VTX_VER_MAJ 1
#define VTX_VER_MIN 8 #define VTX_VER_MIN 8
@ -95,17 +97,23 @@ typedef struct {
struct saa5249_device struct saa5249_device
{ {
struct v4l2_subdev sd;
struct video_device *vdev;
vdau_t vdau[NUM_DAUS]; /* Data for virtual DAUs (the 5249 only has one */ vdau_t vdau[NUM_DAUS]; /* Data for virtual DAUs (the 5249 only has one */
/* real DAU, so we have to simulate some more) */ /* real DAU, so we have to simulate some more) */
int vtx_use_count; int vtx_use_count;
int is_searching[NUM_DAUS]; int is_searching[NUM_DAUS];
int disp_mode; int disp_mode;
int virtual_mode; int virtual_mode;
struct i2c_client *client;
unsigned long in_use; unsigned long in_use;
struct mutex lock; struct mutex lock;
}; };
static inline struct saa5249_device *to_dev(struct v4l2_subdev *sd)
{
return container_of(sd, struct saa5249_device, sd);
}
#define CCTWR 34 /* I²C write/read-address of vtx-chip */ #define CCTWR 34 /* I²C write/read-address of vtx-chip */
#define CCTRD 35 #define CCTRD 35
@ -147,12 +155,13 @@ static void jdelay(unsigned long delay)
static int i2c_sendbuf(struct saa5249_device *t, int reg, int count, u8 *data) static int i2c_sendbuf(struct saa5249_device *t, int reg, int count, u8 *data)
{ {
struct i2c_client *client = v4l2_get_subdevdata(&t->sd);
char buf[64]; char buf[64];
buf[0] = reg; buf[0] = reg;
memcpy(buf+1, data, count); memcpy(buf+1, data, count);
if (i2c_master_send(t->client, buf, count + 1) == count + 1) if (i2c_master_send(client, buf, count + 1) == count + 1)
return 0; return 0;
return -1; return -1;
} }
@ -180,7 +189,9 @@ static int i2c_senddata(struct saa5249_device *t, ...)
static int i2c_getdata(struct saa5249_device *t, int count, u8 *buf) static int i2c_getdata(struct saa5249_device *t, int count, u8 *buf)
{ {
if(i2c_master_recv(t->client, buf, count)!=count) struct i2c_client *client = v4l2_get_subdevdata(&t->sd);
if (i2c_master_recv(client, buf, count) != count)
return -1; return -1;
return 0; return 0;
} }
@ -497,9 +508,6 @@ static int saa5249_open(struct file *file)
struct saa5249_device *t = video_drvdata(file); struct saa5249_device *t = video_drvdata(file);
int pgbuf; int pgbuf;
if (t->client == NULL)
return -ENODEV;
if (test_and_set_bit(0, &t->in_use)) if (test_and_set_bit(0, &t->in_use))
return -EBUSY; return -EBUSY;
@ -553,18 +561,28 @@ static struct video_device saa_template =
.release = video_device_release, .release = video_device_release,
}; };
/* Addresses to scan */ static int saa5249_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
static unsigned short normal_i2c[] = { 0x22 >> 1, I2C_CLIENT_END }; {
struct i2c_client *client = v4l2_get_subdevdata(sd);
I2C_CLIENT_INSMOD; return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA5249, 0);
}
static const struct v4l2_subdev_core_ops saa5249_core_ops = {
.g_chip_ident = saa5249_g_chip_ident,
};
static const struct v4l2_subdev_ops saa5249_ops = {
.core = &saa5249_core_ops,
};
static int saa5249_probe(struct i2c_client *client, static int saa5249_probe(struct i2c_client *client,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
int pgbuf; int pgbuf;
int err; int err;
struct video_device *vd;
struct saa5249_device *t; struct saa5249_device *t;
struct v4l2_subdev *sd;
v4l_info(client, "chip found @ 0x%x (%s)\n", v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name); client->addr << 1, client->adapter->name);
@ -573,16 +591,17 @@ static int saa5249_probe(struct i2c_client *client,
t = kzalloc(sizeof(*t), GFP_KERNEL); t = kzalloc(sizeof(*t), GFP_KERNEL);
if (t == NULL) if (t == NULL)
return -ENOMEM; return -ENOMEM;
sd = &t->sd;
v4l2_i2c_subdev_init(sd, client, &saa5249_ops);
mutex_init(&t->lock); mutex_init(&t->lock);
/* Now create a video4linux device */ /* Now create a video4linux device */
vd = kmalloc(sizeof(struct video_device), GFP_KERNEL); t->vdev = video_device_alloc();
if (vd == NULL) { if (t->vdev == NULL) {
kfree(client); kfree(client);
return -ENOMEM; return -ENOMEM;
} }
i2c_set_clientdata(client, vd); memcpy(t->vdev, &saa_template, sizeof(*t->vdev));
memcpy(vd, &saa_template, sizeof(*vd));
for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) { for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) {
memset(t->vdau[pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf)); memset(t->vdau[pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf));
@ -593,26 +612,27 @@ static int saa5249_probe(struct i2c_client *client,
t->vdau[pgbuf].stopped = true; t->vdau[pgbuf].stopped = true;
t->is_searching[pgbuf] = false; t->is_searching[pgbuf] = false;
} }
video_set_drvdata(vd, t); video_set_drvdata(t->vdev, t);
/* Register it */ /* Register it */
err = video_register_device(vd, VFL_TYPE_VTX, -1); err = video_register_device(t->vdev, VFL_TYPE_VTX, -1);
if (err < 0) { if (err < 0) {
kfree(t); kfree(t);
kfree(vd); video_device_release(t->vdev);
t->vdev = NULL;
return err; return err;
} }
t->client = client;
return 0; return 0;
} }
static int saa5249_remove(struct i2c_client *client) static int saa5249_remove(struct i2c_client *client)
{ {
struct video_device *vd = i2c_get_clientdata(client); struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct saa5249_device *t = to_dev(sd);
video_unregister_device(vd); video_unregister_device(t->vdev);
kfree(video_get_drvdata(vd)); v4l2_device_unregister_subdev(sd);
kfree(vd); kfree(t);
return 0; return 0;
} }

View file

@ -30,8 +30,8 @@
#include <linux/ioctl.h> #include <linux/ioctl.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <media/v4l2-device.h> #include <media/v4l2-device.h>
#include <media/v4l2-i2c-drv-legacy.h> #include <media/v4l2-chip-ident.h>
#include "tda9840.h" #include <media/v4l2-i2c-drv.h>
MODULE_AUTHOR("Michael Hunold <michael@mihu.de>"); MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
MODULE_DESCRIPTION("tda9840 driver"); MODULE_DESCRIPTION("tda9840 driver");
@ -56,11 +56,6 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)");
#define TDA9840_SET_BOTH_R 0x16 #define TDA9840_SET_BOTH_R 0x16
#define TDA9840_SET_EXTERNAL 0x7a #define TDA9840_SET_EXTERNAL 0x7a
/* addresses to scan, found only at 0x42 (7-Bit) */
static unsigned short normal_i2c[] = { I2C_ADDR_TDA9840, I2C_CLIENT_END };
/* magic definition of all other variables and things */
I2C_CLIENT_INSMOD;
static void tda9840_write(struct v4l2_subdev *sd, u8 reg, u8 val) static void tda9840_write(struct v4l2_subdev *sd, u8 reg, u8 val)
{ {
@ -137,60 +132,17 @@ static int tda9840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t)
return 0; return 0;
} }
static long tda9840_ioctl(struct v4l2_subdev *sd, unsigned cmd, void *arg) static int tda9840_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
{ {
int byte; struct i2c_client *client = v4l2_get_subdevdata(sd);
switch (cmd) { return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TDA9840, 0);
case TDA9840_LEVEL_ADJUST:
byte = *(int *)arg;
v4l2_dbg(1, debug, sd, "TDA9840_LEVEL_ADJUST: %d\n", byte);
/* check for correct range */
if (byte > 25 || byte < -20)
return -EINVAL;
/* calculate actual value to set, see specs, page 18 */
byte /= 5;
if (0 < byte)
byte += 0x8;
else
byte = -byte;
tda9840_write(sd, LEVEL_ADJUST, byte);
break;
case TDA9840_STEREO_ADJUST:
byte = *(int *)arg;
v4l2_dbg(1, debug, sd, "TDA9840_STEREO_ADJUST: %d\n", byte);
/* check for correct range */
if (byte > 25 || byte < -24)
return -EINVAL;
/* calculate actual value to set */
byte /= 5;
if (0 < byte)
byte += 0x20;
else
byte = -byte;
tda9840_write(sd, STEREO_ADJUST, byte);
break;
default:
return -ENOIOCTLCMD;
}
return 0;
}
static int tda9840_command(struct i2c_client *client, unsigned cmd, void *arg)
{
return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
} }
/* ----------------------------------------------------------------------- */ /* ----------------------------------------------------------------------- */
static const struct v4l2_subdev_core_ops tda9840_core_ops = { static const struct v4l2_subdev_core_ops tda9840_core_ops = {
.ioctl = tda9840_ioctl, .g_chip_ident = tda9840_g_chip_ident,
}; };
static const struct v4l2_subdev_tuner_ops tda9840_tuner_ops = { static const struct v4l2_subdev_tuner_ops tda9840_tuner_ops = {
@ -209,8 +161,6 @@ static int tda9840_probe(struct i2c_client *client,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
struct v4l2_subdev *sd; struct v4l2_subdev *sd;
int result;
int byte;
/* let's see whether this adapter can support what we need */ /* let's see whether this adapter can support what we need */
if (!i2c_check_functionality(client->adapter, if (!i2c_check_functionality(client->adapter,
@ -227,15 +177,9 @@ static int tda9840_probe(struct i2c_client *client,
v4l2_i2c_subdev_init(sd, client, &tda9840_ops); v4l2_i2c_subdev_init(sd, client, &tda9840_ops);
/* set initial values for level & stereo - adjustment, mode */ /* set initial values for level & stereo - adjustment, mode */
byte = 0; tda9840_write(sd, LEVEL_ADJUST, 0);
result = tda9840_ioctl(sd, TDA9840_LEVEL_ADJUST, &byte); tda9840_write(sd, STEREO_ADJUST, 0);
result |= tda9840_ioctl(sd, TDA9840_STEREO_ADJUST, &byte);
tda9840_write(sd, SWITCH, TDA9840_SET_STEREO); tda9840_write(sd, SWITCH, TDA9840_SET_STEREO);
if (result) {
v4l2_dbg(1, debug, sd, "could not initialize tda9840\n");
kfree(sd);
return -ENODEV;
}
return 0; return 0;
} }
@ -248,12 +192,7 @@ static int tda9840_remove(struct i2c_client *client)
return 0; return 0;
} }
static int tda9840_legacy_probe(struct i2c_adapter *adapter)
{
/* Let's see whether this is a known adapter we can attach to.
Prevents conflicts with tvaudio.c. */
return adapter->id == I2C_HW_SAA7146;
}
static const struct i2c_device_id tda9840_id[] = { static const struct i2c_device_id tda9840_id[] = {
{ "tda9840", 0 }, { "tda9840", 0 },
{ } { }
@ -262,9 +201,7 @@ MODULE_DEVICE_TABLE(i2c, tda9840_id);
static struct v4l2_i2c_driver_data v4l2_i2c_data = { static struct v4l2_i2c_driver_data v4l2_i2c_data = {
.name = "tda9840", .name = "tda9840",
.command = tda9840_command,
.probe = tda9840_probe, .probe = tda9840_probe,
.remove = tda9840_remove, .remove = tda9840_remove,
.legacy_probe = tda9840_legacy_probe,
.id_table = tda9840_id, .id_table = tda9840_id,
}; };

View file

@ -1,14 +0,0 @@
#ifndef __INCLUDED_TDA9840__
#define __INCLUDED_TDA9840__
#define I2C_ADDR_TDA9840 0x42
/* values may range between +2.5 and -2.0;
the value has to be multiplied with 10 */
#define TDA9840_LEVEL_ADJUST _IOW('v',3,int)
/* values may range between +2.5 and -2.4;
the value has to be multiplied with 10 */
#define TDA9840_STEREO_ADJUST _IOW('v',4,int)
#endif

View file

@ -32,7 +32,8 @@
#include <linux/ioctl.h> #include <linux/ioctl.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <media/v4l2-device.h> #include <media/v4l2-device.h>
#include <media/v4l2-i2c-drv-legacy.h> #include <media/v4l2-chip-ident.h>
#include <media/v4l2-i2c-drv.h>
#include "tea6415c.h" #include "tea6415c.h"
MODULE_AUTHOR("Michael Hunold <michael@mihu.de>"); MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
@ -44,25 +45,22 @@ module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Debug level (0-1)"); MODULE_PARM_DESC(debug, "Debug level (0-1)");
/* addresses to scan, found only at 0x03 and/or 0x43 (7-bit) */
static unsigned short normal_i2c[] = { I2C_TEA6415C_1, I2C_TEA6415C_2, I2C_CLIENT_END };
/* magic definition of all other variables and things */ /* makes a connection between the input-pin 'i' and the output-pin 'o' */
I2C_CLIENT_INSMOD; static int tea6415c_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
/* makes a connection between the input-pin 'i' and the output-pin 'o'
for the tea6415c-client 'client' */
static int switch_matrix(struct i2c_client *client, int i, int o)
{ {
struct i2c_client *client = v4l2_get_subdevdata(sd);
u8 byte = 0; u8 byte = 0;
u32 i = route->input;
u32 o = route->output;
int ret; int ret;
v4l_dbg(1, debug, client, "i=%d, o=%d\n", i, o); v4l2_dbg(1, debug, sd, "i=%d, o=%d\n", i, o);
/* check if the pins are valid */ /* check if the pins are valid */
if (0 == ((1 == i || 3 == i || 5 == i || 6 == i || 8 == i || 10 == i || 20 == i || 11 == i) if (0 == ((1 == i || 3 == i || 5 == i || 6 == i || 8 == i || 10 == i || 20 == i || 11 == i)
&& (18 == o || 17 == o || 16 == o || 15 == o || 14 == o || 13 == o))) && (18 == o || 17 == o || 16 == o || 15 == o || 14 == o || 13 == o)))
return -1; return -EINVAL;
/* to understand this, have a look at the tea6415c-specs (p.5) */ /* to understand this, have a look at the tea6415c-specs (p.5) */
switch (o) { switch (o) {
@ -115,37 +113,33 @@ static int switch_matrix(struct i2c_client *client, int i, int o)
ret = i2c_smbus_write_byte(client, byte); ret = i2c_smbus_write_byte(client, byte);
if (ret) { if (ret) {
v4l_dbg(1, debug, client, v4l2_dbg(1, debug, sd,
"i2c_smbus_write_byte() failed, ret:%d\n", ret); "i2c_smbus_write_byte() failed, ret:%d\n", ret);
return -EIO; return -EIO;
} }
return ret; return ret;
} }
static long tea6415c_ioctl(struct v4l2_subdev *sd, unsigned cmd, void *arg) static int tea6415c_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
{ {
if (cmd == TEA6415C_SWITCH) { struct i2c_client *client = v4l2_get_subdevdata(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct tea6415c_multiplex *v = (struct tea6415c_multiplex *)arg;
return switch_matrix(client, v->in, v->out); return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TEA6415C, 0);
}
return -ENOIOCTLCMD;
}
static int tea6415c_command(struct i2c_client *client, unsigned cmd, void *arg)
{
return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
} }
/* ----------------------------------------------------------------------- */ /* ----------------------------------------------------------------------- */
static const struct v4l2_subdev_core_ops tea6415c_core_ops = { static const struct v4l2_subdev_core_ops tea6415c_core_ops = {
.ioctl = tea6415c_ioctl, .g_chip_ident = tea6415c_g_chip_ident,
};
static const struct v4l2_subdev_video_ops tea6415c_video_ops = {
.s_routing = tea6415c_s_routing,
}; };
static const struct v4l2_subdev_ops tea6415c_ops = { static const struct v4l2_subdev_ops tea6415c_ops = {
.core = &tea6415c_core_ops, .core = &tea6415c_core_ops,
.video = &tea6415c_video_ops,
}; };
/* this function is called by i2c_probe */ /* this function is called by i2c_probe */
@ -176,12 +170,6 @@ static int tea6415c_remove(struct i2c_client *client)
return 0; return 0;
} }
static int tea6415c_legacy_probe(struct i2c_adapter *adapter)
{
/* Let's see whether this is a known adapter we can attach to.
Prevents conflicts with tvaudio.c. */
return adapter->id == I2C_HW_SAA7146;
}
static const struct i2c_device_id tea6415c_id[] = { static const struct i2c_device_id tea6415c_id[] = {
{ "tea6415c", 0 }, { "tea6415c", 0 },
@ -191,9 +179,7 @@ MODULE_DEVICE_TABLE(i2c, tea6415c_id);
static struct v4l2_i2c_driver_data v4l2_i2c_data = { static struct v4l2_i2c_driver_data v4l2_i2c_data = {
.name = "tea6415c", .name = "tea6415c",
.command = tea6415c_command,
.probe = tea6415c_probe, .probe = tea6415c_probe,
.remove = tea6415c_remove, .remove = tea6415c_remove,
.legacy_probe = tea6415c_legacy_probe,
.id_table = tea6415c_id, .id_table = tea6415c_id,
}; };

View file

@ -1,10 +1,6 @@
#ifndef __INCLUDED_TEA6415C__ #ifndef __INCLUDED_TEA6415C__
#define __INCLUDED_TEA6415C__ #define __INCLUDED_TEA6415C__
/* possible i2c-addresses */
#define I2C_TEA6415C_1 0x03
#define I2C_TEA6415C_2 0x43
/* the tea6415c's design is quite brain-dead. although there are /* the tea6415c's design is quite brain-dead. although there are
8 inputs and 6 outputs, these aren't enumerated in any way. because 8 inputs and 6 outputs, these aren't enumerated in any way. because
I don't want to say "connect input pin 20 to output pin 17", I define I don't want to say "connect input pin 20 to output pin 17", I define
@ -28,12 +24,4 @@
#define TEA6415C_INPUT7 1 #define TEA6415C_INPUT7 1
#define TEA6415C_INPUT8 11 #define TEA6415C_INPUT8 11
struct tea6415c_multiplex
{
int in; /* input-pin */
int out; /* output-pin */
};
#define TEA6415C_SWITCH _IOW('v',1,struct tea6415c_multiplex)
#endif #endif

View file

@ -32,7 +32,8 @@
#include <linux/ioctl.h> #include <linux/ioctl.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <media/v4l2-device.h> #include <media/v4l2-device.h>
#include <media/v4l2-i2c-drv-legacy.h> #include <media/v4l2-chip-ident.h>
#include <media/v4l2-i2c-drv.h>
#include "tea6420.h" #include "tea6420.h"
MODULE_AUTHOR("Michael Hunold <michael@mihu.de>"); MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
@ -44,24 +45,23 @@ module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Debug level (0-1)"); MODULE_PARM_DESC(debug, "Debug level (0-1)");
/* addresses to scan, found only at 0x4c and/or 0x4d (7-Bit) */
static unsigned short normal_i2c[] = { I2C_ADDR_TEA6420_1, I2C_ADDR_TEA6420_2, I2C_CLIENT_END };
/* magic definition of all other variables and things */
I2C_CLIENT_INSMOD;
/* make a connection between the input 'i' and the output 'o' /* make a connection between the input 'i' and the output 'o'
with gain 'g' for the tea6420-client 'client' (note: i = 6 means 'mute') */ with gain 'g' (note: i = 6 means 'mute') */
static int tea6420_switch(struct i2c_client *client, int i, int o, int g) static int tea6420_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
{ {
struct i2c_client *client = v4l2_get_subdevdata(sd);
int i = route->input;
int o = route->output & 0xf;
int g = (route->output >> 4) & 0xf;
u8 byte; u8 byte;
int ret; int ret;
v4l_dbg(1, debug, client, "i=%d, o=%d, g=%d\n", i, o, g); v4l2_dbg(1, debug, sd, "i=%d, o=%d, g=%d\n", i, o, g);
/* check if the parameters are valid */ /* check if the parameters are valid */
if (i < 1 || i > 6 || o < 1 || o > 4 || g < 0 || g > 6 || g % 2 != 0) if (i < 1 || i > 6 || o < 1 || o > 4 || g < 0 || g > 6 || g % 2 != 0)
return -1; return -EINVAL;
byte = ((o - 1) << 5); byte = ((o - 1) << 5);
byte |= (i - 1); byte |= (i - 1);
@ -83,37 +83,33 @@ static int tea6420_switch(struct i2c_client *client, int i, int o, int g)
ret = i2c_smbus_write_byte(client, byte); ret = i2c_smbus_write_byte(client, byte);
if (ret) { if (ret) {
v4l_dbg(1, debug, client, v4l2_dbg(1, debug, sd,
"i2c_smbus_write_byte() failed, ret:%d\n", ret); "i2c_smbus_write_byte() failed, ret:%d\n", ret);
return -EIO; return -EIO;
} }
return 0; return 0;
} }
static long tea6420_ioctl(struct v4l2_subdev *sd, unsigned cmd, void *arg) static int tea6420_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
{ {
if (cmd == TEA6420_SWITCH) { struct i2c_client *client = v4l2_get_subdevdata(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct tea6420_multiplex *a = (struct tea6420_multiplex *)arg;
return tea6420_switch(client, a->in, a->out, a->gain); return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TEA6420, 0);
}
return -ENOIOCTLCMD;
}
static int tea6420_command(struct i2c_client *client, unsigned cmd, void *arg)
{
return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
} }
/* ----------------------------------------------------------------------- */ /* ----------------------------------------------------------------------- */
static const struct v4l2_subdev_core_ops tea6420_core_ops = { static const struct v4l2_subdev_core_ops tea6420_core_ops = {
.ioctl = tea6420_ioctl, .g_chip_ident = tea6420_g_chip_ident,
};
static const struct v4l2_subdev_audio_ops tea6420_audio_ops = {
.s_routing = tea6420_s_routing,
}; };
static const struct v4l2_subdev_ops tea6420_ops = { static const struct v4l2_subdev_ops tea6420_ops = {
.core = &tea6420_core_ops, .core = &tea6420_core_ops,
.audio = &tea6420_audio_ops,
}; };
/* this function is called by i2c_probe */ /* this function is called by i2c_probe */
@ -130,20 +126,24 @@ static int tea6420_probe(struct i2c_client *client,
v4l_info(client, "chip found @ 0x%x (%s)\n", v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name); client->addr << 1, client->adapter->name);
sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
if (sd == NULL)
return -ENOMEM;
v4l2_i2c_subdev_init(sd, client, &tea6420_ops);
/* set initial values: set "mute"-input to all outputs at gain 0 */ /* set initial values: set "mute"-input to all outputs at gain 0 */
err = 0; err = 0;
for (i = 1; i < 5; i++) { for (i = 1; i < 5; i++) {
err += tea6420_switch(client, 6, i, 0); struct v4l2_routing route;
route.input = 6;
route.output = i;
err += tea6420_s_routing(sd, &route);
} }
if (err) { if (err) {
v4l_dbg(1, debug, client, "could not initialize tea6420\n"); v4l_dbg(1, debug, client, "could not initialize tea6420\n");
return -ENODEV; return -ENODEV;
} }
sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
if (sd == NULL)
return -ENOMEM;
v4l2_i2c_subdev_init(sd, client, &tea6420_ops);
return 0; return 0;
} }
@ -156,12 +156,6 @@ static int tea6420_remove(struct i2c_client *client)
return 0; return 0;
} }
static int tea6420_legacy_probe(struct i2c_adapter *adapter)
{
/* Let's see whether this is a known adapter we can attach to.
Prevents conflicts with tvaudio.c. */
return adapter->id == I2C_HW_SAA7146;
}
static const struct i2c_device_id tea6420_id[] = { static const struct i2c_device_id tea6420_id[] = {
{ "tea6420", 0 }, { "tea6420", 0 },
@ -171,9 +165,7 @@ MODULE_DEVICE_TABLE(i2c, tea6420_id);
static struct v4l2_i2c_driver_data v4l2_i2c_data = { static struct v4l2_i2c_driver_data v4l2_i2c_data = {
.name = "tea6420", .name = "tea6420",
.command = tea6420_command,
.probe = tea6420_probe, .probe = tea6420_probe,
.remove = tea6420_remove, .remove = tea6420_remove,
.legacy_probe = tea6420_legacy_probe,
.id_table = tea6420_id, .id_table = tea6420_id,
}; };

View file

@ -1,17 +1,24 @@
#ifndef __INCLUDED_TEA6420__ #ifndef __INCLUDED_TEA6420__
#define __INCLUDED_TEA6420__ #define __INCLUDED_TEA6420__
/* possible addresses */ /* input pins */
#define I2C_ADDR_TEA6420_1 0x4c #define TEA6420_OUTPUT1 1
#define I2C_ADDR_TEA6420_2 0x4d #define TEA6420_OUTPUT2 2
#define TEA6420_OUTPUT3 3
#define TEA6420_OUTPUT4 4
struct tea6420_multiplex /* output pins */
{ #define TEA6420_INPUT1 1
int in; /* input of audio switch */ #define TEA6420_INPUT2 2
int out; /* output of audio switch */ #define TEA6420_INPUT3 3
int gain; /* gain of connection */ #define TEA6420_INPUT4 4
}; #define TEA6420_INPUT5 5
#define TEA6420_INPUT6 6
#define TEA6420_SWITCH _IOW('v',1,struct tea6420_multiplex) /* gain on the output pins, ORed with the output pin */
#define TEA6420_GAIN0 0x00
#define TEA6420_GAIN2 0x20
#define TEA6420_GAIN4 0x40
#define TEA6420_GAIN6 0x60
#endif #endif

View file

@ -63,6 +63,9 @@ enum {
V4L2_IDENT_OV7720 = 251, V4L2_IDENT_OV7720 = 251,
V4L2_IDENT_OV7725 = 252, V4L2_IDENT_OV7725 = 252,
/* module saa7146: reserved range 300-309 */
V4L2_IDENT_SAA7146 = 300,
/* Conexant MPEG encoder/decoders: reserved range 410-420 */ /* Conexant MPEG encoder/decoders: reserved range 410-420 */
V4L2_IDENT_CX23415 = 415, V4L2_IDENT_CX23415 = 415,
V4L2_IDENT_CX23416 = 416, V4L2_IDENT_CX23416 = 416,
@ -74,9 +77,21 @@ enum {
/* module tvp5150 */ /* module tvp5150 */
V4L2_IDENT_TVP5150 = 5150, V4L2_IDENT_TVP5150 = 5150,
/* module saa5246a: just ident 5246 */
V4L2_IDENT_SAA5246A = 5246,
/* module saa5249: just ident 5249 */
V4L2_IDENT_SAA5249 = 5249,
/* module cs5345: just ident 5345 */ /* module cs5345: just ident 5345 */
V4L2_IDENT_CS5345 = 5345, V4L2_IDENT_CS5345 = 5345,
/* module tea6415c: just ident 6415 */
V4L2_IDENT_TEA6415C = 6415,
/* module tea6420: just ident 6420 */
V4L2_IDENT_TEA6420 = 6420,
/* module saa6752hs: reserved range 6750-6759 */ /* module saa6752hs: reserved range 6750-6759 */
V4L2_IDENT_SAA6752HS = 6752, V4L2_IDENT_SAA6752HS = 6752,
V4L2_IDENT_SAA6752HS_AC3 = 6753, V4L2_IDENT_SAA6752HS_AC3 = 6753,
@ -87,6 +102,9 @@ enum {
/* module wm8775: just ident 8775 */ /* module wm8775: just ident 8775 */
V4L2_IDENT_WM8775 = 8775, V4L2_IDENT_WM8775 = 8775,
/* module tda9840: just ident 9840 */
V4L2_IDENT_TDA9840 = 9840,
/* module tw9910: just ident 9910 */ /* module tw9910: just ident 9910 */
V4L2_IDENT_TW9910 = 9910, V4L2_IDENT_TW9910 = 9910,