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 @@
#include "em28xx.h"
#include <media/tuner.h>
#include <media/v4l2-common.h>
#define DRIVER_AUTHOR "Ludovico Cavedon <cavedon@sssup.it>, " \
"Markus Rechberger <mrechberger@gmail.com>, " \
......@@ -106,7 +107,31 @@ static const unsigned char saa7114_i2c_init[] = {
#define TVNORMS ARRAY_SIZE(tvnorms)
/* supported controls */
/* Common to all boards */
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,
.type = V4L2_CTRL_TYPE_INTEGER,
......@@ -134,24 +159,6 @@ static struct v4l2_queryctrl em28xx_qctrl[] = {
.step = 0x1,
.default_value = 0x10,
.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,
.type = V4L2_CTRL_TYPE_INTEGER,
......@@ -179,7 +186,7 @@ static struct v4l2_queryctrl em28xx_qctrl[] = {
.step = 0x1,
.default_value = 0x20,
.flags = 0,
}
}
};
static struct usb_driver em28xx_usb_driver;
......@@ -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)
{
s32 tmp;
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
ctrl->value = dev->mute;
......@@ -682,6 +688,16 @@ static int em28xx_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl)
case V4L2_CID_AUDIO_VOLUME:
ctrl->value = dev->volume;
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:
if ((tmp = em28xx_brightness_get(dev)) < 0)
return -EIO;
......@@ -731,6 +747,15 @@ static int em28xx_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl)
case V4L2_CID_AUDIO_VOLUME:
dev->volume = ctrl->value;
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:
return em28xx_brightness_set(dev, ctrl->value);
case V4L2_CID_CONTRAST:
......@@ -994,14 +1019,34 @@ static int em28xx_do_ioctl(struct inode *inode, struct file *filp,
case VIDIOC_QUERYCTRL:
{
struct v4l2_queryctrl *qc = arg;
u8 i, n;
n = sizeof(em28xx_qctrl) / sizeof(em28xx_qctrl[0]);
for (i = 0; i < n; i++)
if (qc->id && qc->id == em28xx_qctrl[i].id) {
memcpy(qc, &(em28xx_qctrl[i]),
int i, id=qc->id;
memset(qc,0,sizeof(*qc));
qc->id=id;
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));
return 0;
}
}
return -EINVAL;
}
......@@ -1009,29 +1054,66 @@ static int em28xx_do_ioctl(struct inode *inode, struct file *filp,
case VIDIOC_G_CTRL:
{
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:
{
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]);
for (i = 0; i < n; i++)
if (ctrl->id == em28xx_qctrl[i].id) {
if (ctrl->value <
em28xx_qctrl[i].minimum
|| ctrl->value >
em28xx_qctrl[i].maximum)
return -ERANGE;
if (dev->decoder == EM28XX_TVP5150) {
em28xx_i2c_call_clients(dev,cmd,arg);
return 0;
} else {
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;
}
......@@ -1850,9 +1932,12 @@ static void em28xx_usb_disconnect(struct usb_interface *interface)
struct em28xx *dev = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL);
/*FIXME: IR should be disconnected */
if (!dev)
return;
down_write(&em28xx_disconnect);
down(&dev->lock);
......
......@@ -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)
{
struct msp3400c *msp = i2c_get_clientdata(client);
......@@ -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)
{
......@@ -2027,6 +2155,37 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg)
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:
/* nothing */
......
......@@ -37,24 +37,24 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)");
/* supported controls */
static struct v4l2_queryctrl tvp5150_qctrl[] = {
{
.id = V4L2_CID_BRIGHTNESS,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Brightness",
.minimum = 0,
.maximum = 255,
.step = 1,
.default_value = 0,
.flags = 0,
}, {
.id = V4L2_CID_CONTRAST,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Contrast",
.minimum = 0,
.maximum = 255,
.step = 0x1,
.default_value = 0x10,
.flags = 0,
}, {
.id = V4L2_CID_BRIGHTNESS,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Brightness",
.minimum = 0,
.maximum = 255,
.step = 1,
.default_value = 0,
.flags = 0,
}, {
.id = V4L2_CID_CONTRAST,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Contrast",
.minimum = 0,
.maximum = 255,
.step = 0x1,
.default_value = 0x10,
.flags = 0,
}, {
.id = V4L2_CID_SATURATION,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Saturation",
......@@ -63,16 +63,16 @@ static struct v4l2_queryctrl tvp5150_qctrl[] = {
.step = 0x1,
.default_value = 0x10,
.flags = 0,
}, {
.id = V4L2_CID_HUE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Hue",
.minimum = -128,
.maximum = 127,
.step = 0x1,
.default_value = 0x10,
.flags = 0,
}
}, {
.id = V4L2_CID_HUE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Hue",
.minimum = -128,
.maximum = 127,
.step = 0x1,
.default_value = 0x10,
.flags = 0,
}
};
struct tvp5150 {
......@@ -437,11 +437,24 @@ enum tvp5150_input {
static inline void tvp5150_selmux(struct i2c_client *c,
enum tvp5150_input input)
{
int opmode=0;
struct tvp5150 *decoder = i2c_get_clientdata(c);
if (!decoder->enable)
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);
};
......@@ -498,9 +511,8 @@ static int tvp5150_get_ctrl(struct i2c_client *c, struct v4l2_control *ctrl)
case V4L2_CID_HUE:
ctrl->value = tvp5150_read(c, TVP5150_HUE_CTL);
return 0;
default:
return -EINVAL;
}
return -EINVAL;
}
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:
tvp5150_write(c, TVP5150_HUE_CTL, ctrl->value);
return 0;
default:
return -EINVAL;
}
return -EINVAL;
}
/****************************************************************************
......@@ -627,12 +638,11 @@ static int tvp5150_command(struct i2c_client *client,
case VIDIOC_QUERYCTRL:
{
struct v4l2_queryctrl *qc = arg;
u8 i, n;
int i;
dprintk(1, KERN_DEBUG "VIDIOC_QUERYCTRL");
n = sizeof(tvp5150_qctrl) / sizeof(tvp5150_qctrl[0]);
for (i = 0; i < n; i++)
for (i = 0; i < ARRAY_SIZE(tvp5150_qctrl); i++)
if (qc->id && qc->id == tvp5150_qctrl[i].id) {
memcpy(qc, &(tvp5150_qctrl[i]),
sizeof(*qc));
......@@ -648,7 +658,6 @@ static int tvp5150_command(struct i2c_client *client,
return tvp5150_get_ctrl(client, ctrl);
}
case VIDIOC_S_CTRL_OLD: /* ??? */
case VIDIOC_S_CTRL:
{
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