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 <mchehab@brturbo.com.br>
diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c
index 8516ec1..446ba3b 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 <media/tuner.h>
+#include <media/v4l2-common.h>
 
 #define DRIVER_AUTHOR "Ludovico Cavedon <cavedon@sssup.it>, " \
 		      "Markus Rechberger <mrechberger@gmail.com>, " \
@@ -106,8 +107,32 @@
 #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,
 		.name = "Brightness",
@@ -135,24 +160,6 @@
 		.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,
 		.name = "Red chroma balance",
@@ -179,7 +186,7 @@
 		.step = 0x1,
 		.default_value = 0x20,
 		.flags = 0,
-	 }
+	}
 };
 
 static struct usb_driver em28xx_usb_driver;
@@ -674,7 +681,6 @@
  */
 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 @@
 	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 @@
 	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 @@
 	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 @@
 	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;
 
-
-			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;
-
-					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);
+					}
 				}
+			}
+
+			if (dev->decoder == EM28XX_TVP5150) {
+				em28xx_i2c_call_clients(dev,cmd,arg);
+				return 0;
+			} else {
+
+			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 @@
 	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 fc2896b..8d47d78 100644
--- a/drivers/media/video/msp3400.c
+++ b/drivers/media/video/msp3400.c
@@ -1642,6 +1642,45 @@
 	}
 }
 
+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 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 @@
 
 		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 a60442e..d62b230 100644
--- a/drivers/media/video/tvp5150.c
+++ b/drivers/media/video/tvp5150.c
@@ -37,24 +37,24 @@
 /* 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 @@
 		 .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 @@
 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 @@
 	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 @@
 	case V4L2_CID_HUE:
 		tvp5150_write(c, TVP5150_HUE_CTL, ctrl->value);
 		return 0;
-	default:
-		return -EINVAL;
 	}
+	return -EINVAL;
 }
 
 /****************************************************************************
@@ -627,12 +638,11 @@
 	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 @@
 
 			return tvp5150_get_ctrl(client, ctrl);
 		}
-	case VIDIOC_S_CTRL_OLD:	/* ??? */
 	case VIDIOC_S_CTRL:
 		{
 			struct v4l2_control *ctrl = arg;