[alsa-devel] [PATCH 2/2] Add initial support of Mitac mioa701 master volume.

Robert Jarzmik robert.jarzmik at free.fr
Thu Jan 8 19:42:26 CET 2009


Depending on the "Mio Mode" chosen, the master volume
controls the widget volume/mute levels, providing userspace
a unified vision of volume control (one control only).

The master volume is adapted to smartphones because :
 - it only handles 2 sub-controls (left and right)
 - the generic code is split apart from mio specific code
 - it abstracts to userland audiopath within the chip

Signed-off-by: Robert Jarzmik <robert.jarzmik at free.fr>
---
 sound/soc/pxa/Makefile             |    2 +-
 sound/soc/pxa/mioa701_masterctrl.c |  243 ++++++++++++++++++++++++++++++++++++
 sound/soc/pxa/mioa701_wm9713.c     |    7 +
 3 files changed, 251 insertions(+), 1 deletions(-)
 create mode 100644 sound/soc/pxa/mioa701_masterctrl.c

diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile
index 9a6d27c..f40949f 100644
--- a/sound/soc/pxa/Makefile
+++ b/sound/soc/pxa/Makefile
@@ -18,7 +18,7 @@ snd-soc-spitz-objs := spitz.o
 snd-soc-em-x270-objs := em-x270.o
 snd-soc-palm27x-objs := palm27x.o
 snd-soc-zylonite-objs := zylonite.o
-snd-soc-mioa701-objs := mioa701_wm9713.o
+snd-soc-mioa701-objs := mioa701_wm9713.o mioa701_masterctrl.o
 
 obj-$(CONFIG_SND_PXA2XX_SOC_CORGI) += snd-soc-corgi.o
 obj-$(CONFIG_SND_PXA2XX_SOC_POODLE) += snd-soc-poodle.o
diff --git a/sound/soc/pxa/mioa701_masterctrl.c b/sound/soc/pxa/mioa701_masterctrl.c
new file mode 100644
index 0000000..f95e1f7
--- /dev/null
+++ b/sound/soc/pxa/mioa701_masterctrl.c
@@ -0,0 +1,243 @@
+/*
+ * Handles the Mitac mioa701 Master Volume Control
+ *
+ * Copyright (C) 2008 Robert Jarzmik
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+#include <linux/vmalloc.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+
+#define MASTER_MAX_CTL 2
+#define MAST2CODEC(m) (((struct kctl_master_t *)m)->codec)
+#define KCTL2MAST(k) ((struct kctl_master_t *) k->private_data)
+
+struct kctl_master_t {
+	unsigned int count;
+	snd_ctl_elem_type_t type;
+	char *names[MASTER_MAX_CTL];
+	int nidx[MASTER_MAX_CTL];
+	struct snd_soc_codec *codec;
+};
+
+#define MVOL2(name1, idx1, name2, idx2) \
+{	.count = 2, .names = { name1, name2 }, .nidx = { idx1, idx2 }, \
+	.type = SNDRV_CTL_ELEM_TYPE_INTEGER, \
+}
+#define MMUTE2(name1, idx1, name2, idx2) \
+{	.count = 2, .names = { name1, name2 }, .nidx = { idx1, idx2 }, \
+	.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN, \
+}
+#define MVOL2_DEFAULT MVOL2(NULL, 0, NULL, 0)
+#define MMUTE2_DEFAULT MMUTE2(NULL, 0, NULL, 0)
+
+static struct snd_kcontrol *kctrl_byname(struct snd_soc_codec *codec, char *n)
+{
+	struct snd_ctl_elem_id rid;
+
+	memset(&rid, 0, sizeof(rid));
+	rid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	strncpy(rid.name, n, sizeof(rid.name));
+	return snd_ctl_find_id(codec->card, &rid);
+}
+
+static int master_find(struct snd_soc_codec *codec, char *name,
+		int *max, struct snd_kcontrol **kctl)
+{
+	struct snd_ctl_elem_info info;
+	int rc;
+
+	memset(&info, 0, sizeof(struct snd_ctl_elem_info));
+	*kctl = NULL;
+
+	if (name)
+		*kctl = kctrl_byname(codec, name);
+
+	if (*kctl) {
+		(*kctl)->info(*kctl, &info);
+		*max = info.value.integer.max;
+	}
+
+	rc = (*kctl != NULL);
+	return rc;
+}
+
+static int master_info(struct snd_kcontrol *kcontrol,
+		      struct snd_ctl_elem_info *uinfo)
+{
+	struct kctl_master_t *master = KCTL2MAST(kcontrol);
+
+	uinfo->type = master->type;
+	uinfo->count = master->count;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 0;
+	if (master->type & SNDRV_CTL_ELEM_TYPE_BOOLEAN)
+		uinfo->value.integer.max = 1;
+	if (master->type & SNDRV_CTL_ELEM_TYPE_INTEGER)
+		uinfo->value.integer.max = 256;
+	return 0;
+}
+
+static int master_get(struct snd_kcontrol *kcontrol,
+		      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_kcontrol *kctl;
+	struct snd_ctl_elem_value uc;
+	struct kctl_master_t *master = KCTL2MAST(kcontrol);
+	struct snd_soc_codec *codec = MAST2CODEC(master);
+	int i, ind, max, old;
+
+	for (i = 0; i < master->count; i++) {
+		max = old = 0;
+		ind = master->nidx[i];
+		if (!master_find(codec, master->names[i], &max, &kctl))
+			continue;
+		if (kctl->get(kctl, &uc) < 0)
+			continue;
+		old = uc.value.integer.value[ind];
+		if (master->type == SNDRV_CTL_ELEM_TYPE_INTEGER)
+			old = old * 256 / (max+1);
+		ucontrol->value.integer.value[i] = old;
+	}
+
+	return 0;
+}
+
+static int master_put(struct snd_kcontrol *kcontrol,
+		      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_kcontrol *kctl;
+	struct snd_ctl_elem_value uc;
+	struct kctl_master_t *master = KCTL2MAST(kcontrol);
+	struct snd_soc_codec *codec = MAST2CODEC(master);
+
+	int i, ind, max, new;
+
+	for (i = 0; i < master->count; i++) {
+		max = new = 0;
+		ind = master->nidx[i];
+		if (!master_find(codec, master->names[i], &max, &kctl))
+			continue;
+		new = ucontrol->value.integer.value[i];
+		if (master->type == SNDRV_CTL_ELEM_TYPE_INTEGER)
+			new = new * (max+1) / 256;
+
+		if (kctl->get(kctl, &uc) < 0)
+			continue;
+
+		uc.value.integer.value[ind] = new;
+		if (kctl->put(kctl, &uc) < 0)
+			continue;
+	}
+
+	return 0;
+}
+
+/*
+ * Beware : masterconf must be of same type, and have same count as the old
+ * master
+ */
+void master_change(struct snd_kcontrol *k, struct kctl_master_t *masterconf)
+{
+	if (masterconf && k)
+		masterconf->codec = MAST2CODEC(k->private_data);
+	if (k)
+		k->private_data = masterconf;
+}
+
+struct snd_kcontrol *master_init(struct snd_soc_codec *codec, char *mname,
+				 struct kctl_master_t *master)
+{
+	struct snd_kcontrol_new *kctln = NULL;
+	struct snd_kcontrol *kctl = NULL;
+	int rc;
+
+	kctln = kmalloc(sizeof(struct snd_kcontrol_new), GFP_KERNEL);
+	if (!kctln)
+		return NULL;
+
+	memset(kctln, 0, sizeof(struct snd_kcontrol_new));
+	kctln->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	kctln->name = mname;
+	kctln->info = master_info;
+	kctln->get = master_get;
+	kctln->put = master_put;
+	master->codec = codec;
+
+	kctl = snd_soc_cnew(kctln, master, NULL);
+
+	rc = snd_ctl_add(codec->card, kctl);
+	if (rc < 0) {
+		kfree(kctln);
+		kctl = NULL;
+	}
+
+	return kctl;
+}
+
+/* MIO Specific */
+static struct kctl_master_t miomastervol[] = {
+	MVOL2_DEFAULT,				/* MIO_AUDIO_OFF */
+	MVOL2("Headphone Playback Volume", 0,	/* MIO_GSM_AUDIO_HANDSET */
+	      "Out3 Playback Volume", 0),
+	MVOL2("Headphone Playback Volume", 0,	/* MIO_GSM_AUDIO_HEADSET */
+	      "Headphone Playback Volume", 1),
+	MVOL2("Speaker Playback Volume", 0,	/* MIO_GSM_AUDIO_HANDSFREE */
+	      "Speaker Playback Volume", 1),
+	MVOL2_DEFAULT,				/* MIO_GSM_AUDIO_BLUETOOTH */
+	MVOL2("Speaker Playback Volume", 0,	/* MIO_STEREO_TO_SPEAKER */
+	      "Speaker Playback Volume", 1),
+	MVOL2("Headphone Playback Volume", 0,	/* MIO_STEREO_TO_HEADPHONES */
+	      "Headphone Playback Volume", 1),
+	MVOL2_DEFAULT,				/* MIO_CAPTURE_HANDSET */
+	MVOL2_DEFAULT,				/* MIO_CAPTURE_HEADSET */
+	MVOL2_DEFAULT,				/* MIO_CAPTURE_BLUETOOTH */
+};
+
+static struct kctl_master_t miomastermute[] = {
+	MMUTE2_DEFAULT,				/* MIO_AUDIO_OFF */
+	MMUTE2("Headphone Playback Switch", 0,	/* MIO_GSM_AUDIO_HANDSET */
+	      "Out3 Playback Switch", 0),
+	MMUTE2("Headphone Playback Switch", 0,	/* MIO_GSM_AUDIO_HEADSET */
+	       "Headphone Playback Switch", 1),
+	MMUTE2("Speaker Playback Switch", 0,	/* MIO_GSM_AUDIO_HANDSFREE */
+	       "Speaker Playback Switch", 1),
+	MMUTE2_DEFAULT,				/* MIO_GSM_AUDIO_BLUETOOTH */
+	MMUTE2("Speaker Playback Switch", 0,	/* MIO_STEREO_TO_SPEAKER */
+	       "Speaker Playback Switch", 1),
+	MMUTE2("Headphone Playback Switch", 0,	/* MIO_STEREO_TO_HEADPHONES */
+	       "Headphone Playback Switch", 1),
+	MMUTE2_DEFAULT,				/* MIO_CAPTURE_HANDSET */
+	MMUTE2_DEFAULT,				/* MIO_CAPTURE_HEADSET */
+	MMUTE2_DEFAULT,				/* MIO_CAPTURE_BLUETOOTH */
+};
+
+static struct snd_kcontrol *miovol, *miomute;
+
+int mioa701_master_init(struct snd_soc_codec *codec)
+{
+	miovol =  master_init(codec, "Mio Volume", &miomastervol[0]);
+	miomute = master_init(codec, "Mio Switch", &miomastermute[0]);
+
+	return 0;
+}
+
+void mioa701_master_change(int scenario)
+{
+	master_change(miovol, &miomastervol[scenario]);
+	master_change(miomute, &miomastermute[scenario]);
+}
diff --git a/sound/soc/pxa/mioa701_wm9713.c b/sound/soc/pxa/mioa701_wm9713.c
index dbf6799..0ffc0c1 100644
--- a/sound/soc/pxa/mioa701_wm9713.c
+++ b/sound/soc/pxa/mioa701_wm9713.c
@@ -55,6 +55,8 @@
 
 static int mio_scenario = MIO_AUDIO_OFF;
 
+extern int mioa701_master_init(struct snd_soc_codec *codec);
+extern void mioa701_master_change(int scenario);
 static int phone_stream_start(struct snd_soc_codec *codec);
 static int phone_stream_stop(struct snd_soc_codec *codec);
 
@@ -320,6 +322,8 @@ static void switch_mio_mode(struct snd_soc_codec *codec, int new_scenario)
 	default:
 		break;
 	}
+
+	mioa701_master_change(mio_scenario);
 }
 
 static int set_scenario(struct snd_kcontrol *kcontrol,
@@ -463,6 +467,9 @@ static int mioa701_wm9713_init(struct snd_soc_codec *codec)
 	/* initialize mioa701 codec pins */
 	set_scenario_endpoints(codec, mio_scenario);
 
+	/* Add mio Masters */
+	mioa701_master_init(codec);
+
 	/* Prepare GPIO8 for rear speaker amplificator */
 	reg = codec->read(codec, AC97_GPIO_CFG);
 	codec->write(codec, AC97_GPIO_CFG, reg | 0x0100);
-- 
1.5.6.5



More information about the Alsa-devel mailing list