[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