Commit c0477ad9 authored by Mauro Carvalho Chehab's avatar Mauro Carvalho Chehab

V4L/DVB (3099): Fixed device controls for em28xx on WinTV USB2 devices

- Controls now come from video and audio decoder driver for msp3400 and tvp5150.
- Added audio and sound controls as provided by msp3400 and tvp5150.
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@brturbo.com.br>
parent c432a072
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include "em28xx.h" #include "em28xx.h"
#include <media/tuner.h> #include <media/tuner.h>
#include <media/v4l2-common.h>
#define DRIVER_AUTHOR "Ludovico Cavedon <cavedon@sssup.it>, " \ #define DRIVER_AUTHOR "Ludovico Cavedon <cavedon@sssup.it>, " \
"Markus Rechberger <mrechberger@gmail.com>, " \ "Markus Rechberger <mrechberger@gmail.com>, " \
...@@ -106,7 +107,31 @@ static const unsigned char saa7114_i2c_init[] = { ...@@ -106,7 +107,31 @@ static const unsigned char saa7114_i2c_init[] = {
#define TVNORMS ARRAY_SIZE(tvnorms) #define TVNORMS ARRAY_SIZE(tvnorms)
/* supported controls */ /* supported controls */
/* Common to all boards */
static struct v4l2_queryctrl em28xx_qctrl[] = { static struct v4l2_queryctrl em28xx_qctrl[] = {
{
.id = V4L2_CID_AUDIO_VOLUME,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Volume",
.minimum = 0x0,
.maximum = 0x1f,
.step = 0x1,
.default_value = 0x1f,
.flags = 0,
},{
.id = V4L2_CID_AUDIO_MUTE,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "Mute",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 1,
.flags = 0,
}
};
/* FIXME: These are specific to saa711x - should be moved to its code */
static struct v4l2_queryctrl saa711x_qctrl[] = {
{ {
.id = V4L2_CID_BRIGHTNESS, .id = V4L2_CID_BRIGHTNESS,
.type = V4L2_CTRL_TYPE_INTEGER, .type = V4L2_CTRL_TYPE_INTEGER,
...@@ -134,24 +159,6 @@ static struct v4l2_queryctrl em28xx_qctrl[] = { ...@@ -134,24 +159,6 @@ static struct v4l2_queryctrl em28xx_qctrl[] = {
.step = 0x1, .step = 0x1,
.default_value = 0x10, .default_value = 0x10,
.flags = 0, .flags = 0,
},{
.id = V4L2_CID_AUDIO_VOLUME,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Volume",
.minimum = 0x0,
.maximum = 0x1f,
.step = 0x1,
.default_value = 0x1f,
.flags = 0,
},{
.id = V4L2_CID_AUDIO_MUTE,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "Mute",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 1,
.flags = 0,
},{ },{
.id = V4L2_CID_RED_BALANCE, .id = V4L2_CID_RED_BALANCE,
.type = V4L2_CTRL_TYPE_INTEGER, .type = V4L2_CTRL_TYPE_INTEGER,
...@@ -179,7 +186,7 @@ static struct v4l2_queryctrl em28xx_qctrl[] = { ...@@ -179,7 +186,7 @@ static struct v4l2_queryctrl em28xx_qctrl[] = {
.step = 0x1, .step = 0x1,
.default_value = 0x20, .default_value = 0x20,
.flags = 0, .flags = 0,
} }
}; };
static struct usb_driver em28xx_usb_driver; static struct usb_driver em28xx_usb_driver;
...@@ -674,7 +681,6 @@ static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma) ...@@ -674,7 +681,6 @@ static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma)
*/ */
static int em28xx_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl) static int em28xx_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl)
{ {
s32 tmp;
switch (ctrl->id) { switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE: case V4L2_CID_AUDIO_MUTE:
ctrl->value = dev->mute; ctrl->value = dev->mute;
...@@ -682,6 +688,16 @@ static int em28xx_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl) ...@@ -682,6 +688,16 @@ static int em28xx_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl)
case V4L2_CID_AUDIO_VOLUME: case V4L2_CID_AUDIO_VOLUME:
ctrl->value = dev->volume; ctrl->value = dev->volume;
return 0; return 0;
default:
return -EINVAL;
}
}
/*FIXME: should be moved to saa711x */
static int saa711x_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl)
{
s32 tmp;
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS: case V4L2_CID_BRIGHTNESS:
if ((tmp = em28xx_brightness_get(dev)) < 0) if ((tmp = em28xx_brightness_get(dev)) < 0)
return -EIO; return -EIO;
...@@ -731,6 +747,15 @@ static int em28xx_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl) ...@@ -731,6 +747,15 @@ static int em28xx_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl)
case V4L2_CID_AUDIO_VOLUME: case V4L2_CID_AUDIO_VOLUME:
dev->volume = ctrl->value; dev->volume = ctrl->value;
return em28xx_audio_analog_set(dev); return em28xx_audio_analog_set(dev);
default:
return -EINVAL;
}
}
/*FIXME: should be moved to saa711x */
static int saa711x_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl)
{
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS: case V4L2_CID_BRIGHTNESS:
return em28xx_brightness_set(dev, ctrl->value); return em28xx_brightness_set(dev, ctrl->value);
case V4L2_CID_CONTRAST: case V4L2_CID_CONTRAST:
...@@ -994,14 +1019,34 @@ static int em28xx_do_ioctl(struct inode *inode, struct file *filp, ...@@ -994,14 +1019,34 @@ static int em28xx_do_ioctl(struct inode *inode, struct file *filp,
case VIDIOC_QUERYCTRL: case VIDIOC_QUERYCTRL:
{ {
struct v4l2_queryctrl *qc = arg; struct v4l2_queryctrl *qc = arg;
u8 i, n; int i, id=qc->id;
n = sizeof(em28xx_qctrl) / sizeof(em28xx_qctrl[0]);
for (i = 0; i < n; i++) memset(qc,0,sizeof(*qc));
if (qc->id && qc->id == em28xx_qctrl[i].id) { qc->id=id;
memcpy(qc, &(em28xx_qctrl[i]),
if (!dev->has_msp34xx) {
for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
if (qc->id && qc->id == em28xx_qctrl[i].id) {
memcpy(qc, &(em28xx_qctrl[i]),
sizeof(*qc));
return 0;
}
}
}
if (dev->decoder == EM28XX_TVP5150) {
em28xx_i2c_call_clients(dev,cmd,qc);
if (qc->type)
return 0;
else
return -EINVAL;
}
for (i = 0; i < ARRAY_SIZE(saa711x_qctrl); i++) {
if (qc->id && qc->id == saa711x_qctrl[i].id) {
memcpy(qc, &(saa711x_qctrl[i]),
sizeof(*qc)); sizeof(*qc));
return 0; return 0;
} }
}
return -EINVAL; return -EINVAL;
} }
...@@ -1009,29 +1054,66 @@ static int em28xx_do_ioctl(struct inode *inode, struct file *filp, ...@@ -1009,29 +1054,66 @@ static int em28xx_do_ioctl(struct inode *inode, struct file *filp,
case VIDIOC_G_CTRL: case VIDIOC_G_CTRL:
{ {
struct v4l2_control *ctrl = arg; struct v4l2_control *ctrl = arg;
int retval=-EINVAL;
if (!dev->has_msp34xx)
retval=em28xx_get_ctrl(dev, ctrl);
if (retval==-EINVAL) {
if (dev->decoder == EM28XX_TVP5150) {
em28xx_i2c_call_clients(dev,cmd,arg);
return 0;
}
return em28xx_get_ctrl(dev, ctrl); return saa711x_get_ctrl(dev, ctrl);
} else return retval;
} }
case VIDIOC_S_CTRL_OLD: /* ??? */
case VIDIOC_S_CTRL: case VIDIOC_S_CTRL:
{ {
struct v4l2_control *ctrl = arg; struct v4l2_control *ctrl = arg;
u8 i, n; u8 i;
if (!dev->has_msp34xx){
for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
if (ctrl->id == em28xx_qctrl[i].id) {
if (ctrl->value <
em28xx_qctrl[i].minimum
|| ctrl->value >
em28xx_qctrl[i].maximum)
return -ERANGE;
return em28xx_set_ctrl(dev, ctrl);
}
}
}
n = sizeof(em28xx_qctrl) / sizeof(em28xx_qctrl[0]); if (dev->decoder == EM28XX_TVP5150) {
for (i = 0; i < n; i++) em28xx_i2c_call_clients(dev,cmd,arg);
if (ctrl->id == em28xx_qctrl[i].id) { return 0;
if (ctrl->value < } else {
em28xx_qctrl[i].minimum
|| ctrl->value >
em28xx_qctrl[i].maximum)
return -ERANGE;
return em28xx_set_ctrl(dev, ctrl); if (!dev->has_msp34xx){
for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
if (ctrl->id == em28xx_qctrl[i].id) {
if (ctrl->value <
em28xx_qctrl[i].minimum
|| ctrl->value >
em28xx_qctrl[i].maximum)
return -ERANGE;
return em28xx_set_ctrl(dev, ctrl);
}
}
for (i = 0; i < ARRAY_SIZE(saa711x_qctrl); i++) {
if (ctrl->id == saa711x_qctrl[i].id) {
if (ctrl->value <
saa711x_qctrl[i].minimum
|| ctrl->value >
saa711x_qctrl[i].maximum)
return -ERANGE;
return saa711x_set_ctrl(dev, ctrl);
}
} }
}
return -EINVAL; return -EINVAL;
} }
...@@ -1850,9 +1932,12 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) ...@@ -1850,9 +1932,12 @@ static void em28xx_usb_disconnect(struct usb_interface *interface)
struct em28xx *dev = usb_get_intfdata(interface); struct em28xx *dev = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL); usb_set_intfdata(interface, NULL);
/*FIXME: IR should be disconnected */
if (!dev) if (!dev)
return; return;
down_write(&em28xx_disconnect); down_write(&em28xx_disconnect);
down(&dev->lock); down(&dev->lock);
......
...@@ -1642,6 +1642,45 @@ static void msp_any_detect_stereo(struct i2c_client *client) ...@@ -1642,6 +1642,45 @@ static void msp_any_detect_stereo(struct i2c_client *client)
} }
} }
static struct v4l2_queryctrl msp34xx_qctrl[] = {
{
.id = V4L2_CID_AUDIO_VOLUME,
.name = "Volume",
.minimum = 0,
.maximum = 65535,
.step = 65535/100,
.default_value = 58880,
.flags = 0,
.type = V4L2_CTRL_TYPE_INTEGER,
},{
.id = V4L2_CID_AUDIO_MUTE,
.name = "Mute",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 1,
.flags = 0,
.type = V4L2_CTRL_TYPE_BOOLEAN,
},{
.id = V4L2_CID_AUDIO_BASS,
.name = "Bass",
.minimum = 0,
.maximum = 65535,
.step = 65535/100,
.default_value = 32768,
.type = V4L2_CTRL_TYPE_INTEGER,
},{
.id = V4L2_CID_AUDIO_TREBLE,
.name = "Treble",
.minimum = 0,
.maximum = 65535,
.step = 65535/100,
.default_value = 32768,
.type = V4L2_CTRL_TYPE_INTEGER,
},
};
static void msp_any_set_audmode(struct i2c_client *client, int audmode) static void msp_any_set_audmode(struct i2c_client *client, int audmode)
{ {
struct msp3400c *msp = i2c_get_clientdata(client); struct msp3400c *msp = i2c_get_clientdata(client);
...@@ -1658,6 +1697,95 @@ static void msp_any_set_audmode(struct i2c_client *client, int audmode) ...@@ -1658,6 +1697,95 @@ static void msp_any_set_audmode(struct i2c_client *client, int audmode)
} }
} }
static int msp_get_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
{
struct msp3400c *msp = i2c_get_clientdata(client);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
ctrl->value = msp->muted;
return 0;
case V4L2_CID_AUDIO_BALANCE:
{
int volume = MAX(msp->left, msp->right);
ctrl->value = (32768 * MIN(msp->left, msp->right)) /
(volume ? volume : 1);
ctrl->value = (msp->left < msp->right) ?
(65535 - ctrl->value) : ctrl->value;
if (0 == volume)
ctrl->value = 32768;
return 0;
}
case V4L2_CID_AUDIO_BASS:
ctrl->value = msp->bass;
return 0;
case V4L2_CID_AUDIO_TREBLE:
ctrl->value = msp->treble;
return 0;
case V4L2_CID_AUDIO_VOLUME:
ctrl->value = MAX(msp->left, msp->right);
return 0;
default:
return -EINVAL;
}
}
static int msp_set_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
{
struct msp3400c *msp = i2c_get_clientdata(client);
int set_volume=0, balance, volume;
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
if (ctrl->value>=0 && ctrl->value<2)
msp->muted = ctrl->value;
else
return -ERANGE;
msp3400c_setvolume(client, msp->muted, msp->left, msp->right);
return 0;
case V4L2_CID_AUDIO_BALANCE:
balance=ctrl->value;
volume = MAX(msp->left, msp->right);
set_volume=1;
break;
case V4L2_CID_AUDIO_BASS:
msp->bass=ctrl->value;
msp3400c_setbass(client, msp->bass);
return 0;
case V4L2_CID_AUDIO_TREBLE:
msp->treble=ctrl->value;
msp3400c_settreble(client, msp->treble);
return 0;
case V4L2_CID_AUDIO_VOLUME:
volume = MAX(msp->left, msp->right);
balance = (32768 * MIN(msp->left, msp->right)) /
(volume ? volume : 1);
balance = (msp->left < msp->right) ?
(65535 - balance) : balance;
if (0 == volume)
balance = 32768;
volume=ctrl->value;
set_volume=1;
break;
default:
return -EINVAL;
}
if (set_volume) {
msp->left = (MIN(65536 - balance, 32768) * volume) / 32768;
msp->right = (MIN(balance, 32768) * volume) / 32768;
msp3400_dbg("volume=%d, balance=%d, left=%d, right=%d",
volume,balance,msp->left,msp->right);
msp3400c_setvolume(client, msp->muted, msp->left, msp->right);
}
return 0;
}
static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg)
{ {
...@@ -2027,6 +2155,37 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) ...@@ -2027,6 +2155,37 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg)
break; break;
} }
case VIDIOC_QUERYCTRL:
{
struct v4l2_queryctrl *qc = arg;
int i;
msp3400_dbg("VIDIOC_QUERYCTRL");
for (i = 0; i < ARRAY_SIZE(msp34xx_qctrl); i++)
if (qc->id && qc->id == msp34xx_qctrl[i].id) {
memcpy(qc, &(msp34xx_qctrl[i]),
sizeof(*qc));
return 0;
}
return -EINVAL;
}
case VIDIOC_G_CTRL:
{
struct v4l2_control *ctrl = arg;
msp3400_dbg("VIDIOC_G_CTRL\n");
return msp_get_ctrl(client, ctrl);
}
case VIDIOC_S_CTRL:
{
struct v4l2_control *ctrl = arg;
msp3400_dbg("VIDIOC_S_CTRL\n");
return msp_set_ctrl(client, ctrl);
}
default: default:
/* nothing */ /* nothing */
......
...@@ -37,24 +37,24 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)"); ...@@ -37,24 +37,24 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)");
/* supported controls */ /* supported controls */
static struct v4l2_queryctrl tvp5150_qctrl[] = { static struct v4l2_queryctrl tvp5150_qctrl[] = {
{ {
.id = V4L2_CID_BRIGHTNESS, .id = V4L2_CID_BRIGHTNESS,
.type = V4L2_CTRL_TYPE_INTEGER, .type = V4L2_CTRL_TYPE_INTEGER,
.name = "Brightness", .name = "Brightness",
.minimum = 0, .minimum = 0,
.maximum = 255, .maximum = 255,
.step = 1, .step = 1,
.default_value = 0, .default_value = 0,
.flags = 0, .flags = 0,
}, { }, {
.id = V4L2_CID_CONTRAST, .id = V4L2_CID_CONTRAST,
.type = V4L2_CTRL_TYPE_INTEGER, .type = V4L2_CTRL_TYPE_INTEGER,
.name = "Contrast", .name = "Contrast",
.minimum = 0, .minimum = 0,
.maximum = 255, .maximum = 255,
.step = 0x1, .step = 0x1,
.default_value = 0x10, .default_value = 0x10,
.flags = 0, .flags = 0,
}, { }, {
.id = V4L2_CID_SATURATION, .id = V4L2_CID_SATURATION,
.type = V4L2_CTRL_TYPE_INTEGER, .type = V4L2_CTRL_TYPE_INTEGER,
.name = "Saturation", .name = "Saturation",
...@@ -63,16 +63,16 @@ static struct v4l2_queryctrl tvp5150_qctrl[] = { ...@@ -63,16 +63,16 @@ static struct v4l2_queryctrl tvp5150_qctrl[] = {
.step = 0x1, .step = 0x1,
.default_value = 0x10, .default_value = 0x10,
.flags = 0, .flags = 0,
}, { }, {
.id = V4L2_CID_HUE, .id = V4L2_CID_HUE,
.type = V4L2_CTRL_TYPE_INTEGER, .type = V4L2_CTRL_TYPE_INTEGER,
.name = "Hue", .name = "Hue",
.minimum = -128, .minimum = -128,
.maximum = 127, .maximum = 127,
.step = 0x1, .step = 0x1,
.default_value = 0x10, .default_value = 0x10,
.flags = 0, .flags = 0,
} }
}; };
struct tvp5150 { struct tvp5150 {
...@@ -437,11 +437,24 @@ enum tvp5150_input { ...@@ -437,11 +437,24 @@ enum tvp5150_input {
static inline void tvp5150_selmux(struct i2c_client *c, static inline void tvp5150_selmux(struct i2c_client *c,
enum tvp5150_input input) enum tvp5150_input input)
{ {
int opmode=0;
struct tvp5150 *decoder = i2c_get_clientdata(c); struct tvp5150 *decoder = i2c_get_clientdata(c);
if (!decoder->enable) if (!decoder->enable)
input |= TVP5150_BLACK_SCREEN; input |= TVP5150_BLACK_SCREEN;
switch (input) {
case TVP5150_ANALOG_CH0:
case TVP5150_ANALOG_CH1:
opmode=0x30; /* TV Mode */
break;
default:
opmode=0; /* Auto Mode */
break;
}
tvp5150_write(c, TVP5150_OP_MODE_CTL, opmode);
tvp5150_write(c, TVP5150_VD_IN_SRC_SEL_1, input); tvp5150_write(c, TVP5150_VD_IN_SRC_SEL_1, input);
}; };
...@@ -498,9 +511,8 @@ static int tvp5150_get_ctrl(struct i2c_client *c, struct v4l2_control *ctrl) ...@@ -498,9 +511,8 @@ static int tvp5150_get_ctrl(struct i2c_client *c, struct v4l2_control *ctrl)
case V4L2_CID_HUE: case V4L2_CID_HUE:
ctrl->value = tvp5150_read(c, TVP5150_HUE_CTL); ctrl->value = tvp5150_read(c, TVP5150_HUE_CTL);
return 0; return 0;
default:
return -EINVAL;
} }
return -EINVAL;
} }
static int tvp5150_set_ctrl(struct i2c_client *c, struct v4l2_control *ctrl) static int tvp5150_set_ctrl(struct i2c_client *c, struct v4l2_control *ctrl)
...@@ -520,9 +532,8 @@ static int tvp5150_set_ctrl(struct i2c_client *c, struct v4l2_control *ctrl) ...@@ -520,9 +532,8 @@ static int tvp5150_set_ctrl(struct i2c_client *c, struct v4l2_control *ctrl)
case V4L2_CID_HUE: case V4L2_CID_HUE:
tvp5150_write(c, TVP5150_HUE_CTL, ctrl->value); tvp5150_write(c, TVP5150_HUE_CTL, ctrl->value);
return 0; return 0;
default:
return -EINVAL;
} }
return -EINVAL;
} }
/**************************************************************************** /****************************************************************************
...@@ -627,12 +638,11 @@ static int tvp5150_command(struct i2c_client *client, ...@@ -627,12 +638,11 @@ static int tvp5150_command(struct i2c_client *client,
case VIDIOC_QUERYCTRL: case VIDIOC_QUERYCTRL:
{ {
struct v4l2_queryctrl *qc = arg; struct v4l2_queryctrl *qc = arg;
u8 i, n; int i;
dprintk(1, KERN_DEBUG "VIDIOC_QUERYCTRL"); dprintk(1, KERN_DEBUG "VIDIOC_QUERYCTRL");
n = sizeof(tvp5150_qctrl) / sizeof(tvp5150_qctrl[0]); for (i = 0; i < ARRAY_SIZE(tvp5150_qctrl); i++)
for (i = 0; i < n; i++)
if (qc->id && qc->id == tvp5150_qctrl[i].id) { if (qc->id && qc->id == tvp5150_qctrl[i].id) {
memcpy(qc, &(tvp5150_qctrl[i]), memcpy(qc, &(tvp5150_qctrl[i]),
sizeof(*qc)); sizeof(*qc));
...@@ -648,7 +658,6 @@ static int tvp5150_command(struct i2c_client *client, ...@@ -648,7 +658,6 @@ static int tvp5150_command(struct i2c_client *client,
return tvp5150_get_ctrl(client, ctrl); return tvp5150_get_ctrl(client, ctrl);
} }
case VIDIOC_S_CTRL_OLD: /* ??? */
case VIDIOC_S_CTRL: case VIDIOC_S_CTRL:
{ {
struct v4l2_control *ctrl = arg; struct v4l2_control *ctrl = arg;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment