Blob Blame History Raw
From 258c430456ba5f0005043762e14fc3be35983aaf Mon Sep 17 00:00:00 2001
From: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
Date: Fri, 14 Sep 2018 13:13:15 -0400
Subject: [PATCH] media: em28xx: fix handler for vidioc_s_input()
Git-commit: 258c430456ba5f0005043762e14fc3be35983aaf
Patch-mainline: v4.20-rc1
References: bsc#1051510

The a->index is not the name of the internal amux entry,
but, instead a value from zero to the maximum number
of audio inputs.

As the actual available inputs depend on each board, build
it dynamically.

This is broken for a really long time. On a quick check,
since at least commit 195a4ef627e1 ("V4L/DVB (6585): Convert
em28xx to video_ioctl2") this was not implemented right.

Fixes: 195a4ef627e1 ("V4L/DVB (6585): Convert em28xx to video_ioctl2")

Cc: stable@vger.kernel.org
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
Acked-by: Simon Schricker <sschricker@suse.de>
---
 drivers/media/usb/em28xx/em28xx-cards.c |   29 ++++++++++
 drivers/media/usb/em28xx/em28xx-video.c |   86 ++++++++++++++++++++++++++++----
 drivers/media/usb/em28xx/em28xx.h       |    5 +
 3 files changed, 110 insertions(+), 10 deletions(-)

--- a/drivers/media/usb/em28xx/em28xx-cards.c
+++ b/drivers/media/usb/em28xx/em28xx-cards.c
@@ -2928,6 +2928,9 @@ static int em28xx_hint_board(struct em28
 
 static void em28xx_card_setup(struct em28xx *dev)
 {
+	int i, j, idx;
+	bool duplicate_entry;
+
 	/*
 	 * If the device can be a webcam, seek for a sensor.
 	 * If sensor is not found, then it isn't a webcam.
@@ -3080,6 +3083,32 @@ static void em28xx_card_setup(struct em2
 	/* Allow override tuner type by a module parameter */
 	if (tuner >= 0)
 		dev->tuner_type = tuner;
+
+	/*
+	 * Dynamically generate a list of valid audio inputs for this
+	 * specific board, mapping them via enum em28xx_amux.
+	 */
+
+	idx = 0;
+	for (i = 0; i < MAX_EM28XX_INPUT; i++) {
+		if (!INPUT(i)->type)
+			continue;
+
+		/* Skip already mapped audio inputs */
+		duplicate_entry = false;
+		for (j = 0; j < idx; j++) {
+			if (INPUT(i)->amux == dev->amux_map[j]) {
+				duplicate_entry = true;
+				break;
+			}
+		}
+		if (duplicate_entry)
+			continue;
+
+		dev->amux_map[idx++] = INPUT(i)->amux;
+	}
+	for (; idx < MAX_EM28XX_INPUT; idx++)
+		dev->amux_map[idx] = EM28XX_AMUX_UNUSED;
 }
 
 void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl)
--- a/drivers/media/usb/em28xx/em28xx-video.c
+++ b/drivers/media/usb/em28xx/em28xx-video.c
@@ -1612,6 +1612,7 @@ static int vidioc_enum_input(struct file
 {
 	struct em28xx *dev = video_drvdata(file);
 	unsigned int       n;
+	int j;
 
 	n = i->index;
 	if (n >= MAX_EM28XX_INPUT)
@@ -1632,6 +1633,12 @@ static int vidioc_enum_input(struct file
 	if (dev->is_webcam)
 		i->capabilities = 0;
 
+	/* Dynamically generates an audioset bitmask */
+	i->audioset = 0;
+	for (j = 0; j < MAX_EM28XX_INPUT; j++)
+		if (dev->amux_map[j] != EM28XX_AMUX_UNUSED)
+			i->audioset |= 1 << j;
+
 	return 0;
 }
 
@@ -1657,11 +1664,24 @@ static int vidioc_s_input(struct file *f
 	return 0;
 }
 
-static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
-{
-	struct em28xx *dev = video_drvdata(file);
+static int em28xx_fill_audio_input(struct em28xx *dev,
+				   const char *s,
+				   struct v4l2_audio *a,
+				   unsigned int index)
+{
+	unsigned int idx = dev->amux_map[index];
+
+	/*
+	 * With msp3400, almost all mappings use the default (amux = 0).
+	 * The only one may use a different value is WinTV USB2, where it
+	 * can also be SCART1 input.
+	 * As it is very doubtful that we would see new boards with msp3400,
+	 * let's just reuse the existing switch.
+	 */
+	if (dev->has_msp34xx && idx != EM28XX_AMUX_UNUSED)
+		idx = EM28XX_AMUX_LINE_IN;
 
-	switch (a->index) {
+	switch (idx) {
 	case EM28XX_AMUX_VIDEO:
 		strscpy(a->name, "Television", sizeof(a->name));
 		break;
@@ -1686,31 +1706,78 @@ static int vidioc_g_audio(struct file *f
 	case EM28XX_AMUX_PCM_OUT:
 		strscpy(a->name, "PCM", sizeof(a->name));
 		break;
+	case EM28XX_AMUX_UNUSED:
 	default:
 		return -EINVAL;
 	}
-
-	a->index = dev->ctl_ainput;
+	a->index = index;
 	a->capability = V4L2_AUDCAP_STEREO;
 
+	em28xx_videodbg("%s: audio input index %d is '%s'\n",
+			s, a->index, a->name);
+
 	return 0;
 }
 
+static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a)
+{
+	struct em28xx *dev = video_drvdata(file);
+
+	if (a->index >= MAX_EM28XX_INPUT)
+		return -EINVAL;
+
+	return em28xx_fill_audio_input(dev, __func__, a, a->index);
+}
+
+static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+	struct em28xx *dev = video_drvdata(file);
+	int i;
+
+	for (i = 0; i < MAX_EM28XX_INPUT; i++)
+		if (dev->ctl_ainput == dev->amux_map[i])
+			return em28xx_fill_audio_input(dev, __func__, a, i);
+
+	/* Should never happen! */
+	return -EINVAL;
+}
+
 static int vidioc_s_audio(struct file *file, void *priv, const struct v4l2_audio *a)
 {
 	struct em28xx *dev = video_drvdata(file);
+	int idx, i;
 
 	if (a->index >= MAX_EM28XX_INPUT)
 		return -EINVAL;
-	if (0 == INPUT(a->index)->type)
+
+	idx = dev->amux_map[a->index];
+
+	if (idx == EM28XX_AMUX_UNUSED)
+		return -EINVAL;
+
+	dev->ctl_ainput = idx;
+
+	/*
+	 * FIXME: This is wrong, as different inputs at em28xx_cards
+	 * may have different audio outputs. So, the right thing
+	 * to do is to implement VIDIOC_G_AUDOUT/VIDIOC_S_AUDOUT.
+	 * With the current board definitions, this would work fine,
+	 * as, currently, all boards fit.
+	 */
+	for (i = 0; i < MAX_EM28XX_INPUT; i++)
+		if (idx == dev->amux_map[i])
+			break;
+	if (i == MAX_EM28XX_INPUT)
 		return -EINVAL;
 
-	dev->ctl_ainput = INPUT(a->index)->amux;
-	dev->ctl_aoutput = INPUT(a->index)->aout;
+	dev->ctl_aoutput = INPUT(i)->aout;
 
 	if (!dev->ctl_aoutput)
 		dev->ctl_aoutput = EM28XX_AOUT_MASTER;
 
+	em28xx_videodbg("%s: set audio input to %d\n", __func__,
+			dev->ctl_ainput);
+
 	return 0;
 }
 
@@ -2247,6 +2314,7 @@ static const struct v4l2_ioctl_ops video
 	.vidioc_try_fmt_vbi_cap     = vidioc_g_fmt_vbi_cap,
 	.vidioc_s_fmt_vbi_cap       = vidioc_g_fmt_vbi_cap,
 	.vidioc_enum_framesizes     = vidioc_enum_framesizes,
+	.vidioc_enumaudio           = vidioc_enumaudio,
 	.vidioc_g_audio             = vidioc_g_audio,
 	.vidioc_s_audio             = vidioc_s_audio,
 
--- a/drivers/media/usb/em28xx/em28xx.h
+++ b/drivers/media/usb/em28xx/em28xx.h
@@ -330,8 +330,9 @@ enum em28xx_usb_audio_type {
    address more than two different entries.
  */
 enum em28xx_amux {
+	EM28XX_AMUX_UNUSED = -1,
 	/* This is the only entry for em28xx tuner input */
-	EM28XX_AMUX_VIDEO,	/* em28xx tuner, AC97 mixer Video */
+	EM28XX_AMUX_VIDEO = 0,	/* em28xx tuner, AC97 mixer Video */
 
 	EM28XX_AMUX_LINE_IN,	/* AC97 mixer Line In */
 
@@ -652,6 +653,8 @@ struct em28xx {
 	unsigned int ctl_input;	/* selected input */
 	unsigned int ctl_ainput;/* selected audio input */
 	unsigned int ctl_aoutput;/* selected audio output */
+	enum em28xx_amux amux_map[MAX_EM28XX_INPUT];
+
 	int mute;
 	int volume;