V4L/DVB (4264): Cx88-blackbird: implement VIDIOC_QUERYCTRL and VIDIOC_QUERYMENU

This patch implements the newer v4l2 control features to make the
standard user controls and mpeg encoder controls of cx88-blackbird
video encoder boards available to userspace.

Signed-off-by: Michael Krufky <mkrufky@linuxtv.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c
index 4ff8158..349632b 100644
--- a/drivers/media/video/cx88/cx88-blackbird.c
+++ b/drivers/media/video/cx88/cx88-blackbird.c
@@ -686,6 +686,39 @@
 
 /* ------------------------------------------------------------------ */
 
+static const u32 *ctrl_classes[] = {
+	cx88_user_ctrls,
+	cx2341x_mpeg_ctrls,
+	NULL
+};
+
+static int blackbird_queryctrl(struct cx8802_dev *dev, struct v4l2_queryctrl *qctrl)
+{
+	qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
+	if (qctrl->id == 0)
+		return -EINVAL;
+
+	/* Standard V4L2 controls */
+	if (cx8800_ctrl_query(qctrl) == 0)
+		return 0;
+
+	/* MPEG V4L2 controls */
+	if (cx2341x_ctrl_query(&dev->params, qctrl))
+		qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
+	return 0;
+}
+
+static int blackbird_querymenu(struct cx8802_dev *dev, struct v4l2_querymenu *qmenu)
+{
+	struct v4l2_queryctrl qctrl;
+
+	qctrl.id = qmenu->id;
+	blackbird_queryctrl(dev, &qctrl);
+	return v4l2_ctrl_query_menu(qmenu, &qctrl, cx2341x_ctrl_get_menu(qmenu->id));
+}
+
+/* ------------------------------------------------------------------ */
+
 static int mpeg_do_ioctl(struct inode *inode, struct file *file,
 			 unsigned int cmd, void *arg)
 {
@@ -866,6 +899,16 @@
 		       core->name);
 		return 0;
 	}
+	case VIDIOC_QUERYMENU:
+		return blackbird_querymenu(dev, arg);
+	case VIDIOC_QUERYCTRL:
+	{
+		struct v4l2_queryctrl *c = arg;
+
+		if (blackbird_queryctrl(dev, c) == 0)
+			return 0;
+		return cx88_do_ioctl(inode, file, 0, dev->core, cmd, arg, mpeg_do_ioctl);
+	}
 
 	default:
 		return cx88_do_ioctl(inode, file, 0, dev->core, cmd, arg, mpeg_do_ioctl);
diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c
index 8d5cf47..fe147e3 100644
--- a/drivers/media/video/cx88/cx88-video.c
+++ b/drivers/media/video/cx88/cx88-video.c
@@ -327,6 +327,51 @@
 };
 static const int CX8800_CTLS = ARRAY_SIZE(cx8800_ctls);
 
+const u32 cx88_user_ctrls[] = {
+	V4L2_CID_USER_CLASS,
+	V4L2_CID_BRIGHTNESS,
+	V4L2_CID_CONTRAST,
+	V4L2_CID_SATURATION,
+	V4L2_CID_HUE,
+	V4L2_CID_AUDIO_VOLUME,
+	V4L2_CID_AUDIO_BALANCE,
+	V4L2_CID_AUDIO_MUTE,
+	0
+};
+EXPORT_SYMBOL(cx88_user_ctrls);
+
+static const u32 *ctrl_classes[] = {
+	cx88_user_ctrls,
+	NULL
+};
+
+int cx8800_ctrl_query(struct v4l2_queryctrl *qctrl)
+{
+	int i;
+
+	if (qctrl->id < V4L2_CID_BASE ||
+	    qctrl->id >= V4L2_CID_LASTP1)
+		return -EINVAL;
+	for (i = 0; i < CX8800_CTLS; i++)
+		if (cx8800_ctls[i].v.id == qctrl->id)
+			break;
+	if (i == CX8800_CTLS) {
+		*qctrl = no_ctl;
+		return 0;
+	}
+	*qctrl = cx8800_ctls[i].v;
+	return 0;
+}
+EXPORT_SYMBOL(cx8800_ctrl_query);
+
+static int cx88_queryctrl(struct v4l2_queryctrl *qctrl)
+{
+	qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
+	if (qctrl->id == 0)
+		return -EINVAL;
+	return cx8800_ctrl_query(qctrl);
+}
+
 /* ------------------------------------------------------------------- */
 /* resource management                                                 */
 
@@ -1362,20 +1407,8 @@
 	case VIDIOC_QUERYCTRL:
 	{
 		struct v4l2_queryctrl *c = arg;
-		int i;
 
-		if (c->id <  V4L2_CID_BASE ||
-		    c->id >= V4L2_CID_LASTP1)
-			return -EINVAL;
-		for (i = 0; i < CX8800_CTLS; i++)
-			if (cx8800_ctls[i].v.id == c->id)
-				break;
-		if (i == CX8800_CTLS) {
-			*c = no_ctl;
-			return 0;
-		}
-		*c = cx8800_ctls[i].v;
-		return 0;
+		return cx88_queryctrl(c);
 	}
 	case VIDIOC_G_CTRL:
 		return get_control(core,arg);
diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h
index 9a9a0fc..336e823 100644
--- a/drivers/media/video/cx88/cx88.h
+++ b/drivers/media/video/cx88/cx88.h
@@ -590,6 +590,8 @@
 extern int cx88_do_ioctl(struct inode *inode, struct file *file, int radio,
 				struct cx88_core *core, unsigned int cmd,
 				void *arg, v4l2_kioctl driver_ioctl);
+extern const u32 cx88_user_ctrls[];
+extern int cx8800_ctrl_query(struct v4l2_queryctrl *qctrl);
 
 /*
  * Local variables: