From c0477ad9feca01bd8eff95d7482c33753d05c700 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 9 Jan 2006 15:25:14 -0200 Subject: [PATCH] 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: Mauro Carvalho Chehab --- drivers/media/video/em28xx/em28xx-video.c | 161 +++++++++++++++++----- drivers/media/video/msp3400.c | 159 +++++++++++++++++++++ drivers/media/video/tvp5150.c | 81 ++++++----- 3 files changed, 327 insertions(+), 74 deletions(-) diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index 8516ec1283..446ba3b2ac 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -32,6 +32,7 @@ #include "em28xx.h" #include +#include #define DRIVER_AUTHOR "Ludovico Cavedon , " \ "Markus Rechberger , " \ @@ -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); diff --git a/drivers/media/video/msp3400.c b/drivers/media/video/msp3400.c index fc2896bc42..8d47d78942 100644 --- a/drivers/media/video/msp3400.c +++ b/drivers/media/video/msp3400.c @@ -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 */ diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c index a60442ea4f..d62b2302af 100644 --- a/drivers/media/video/tvp5150.c +++ b/drivers/media/video/tvp5150.c @@ -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; -- 2.39.5