[alsa-devel] [PATCH] Add initial support of Mitac mioa701 master volume.
Robert Jarzmik
robert.jarzmik at free.fr
Tue Jul 8 22:45:06 CEST 2008
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 ce171c3..53134ba 100644
--- a/sound/soc/pxa/Makefile
+++ b/sound/soc/pxa/Makefile
@@ -21,7 +21,7 @@ snd-soc-spitz-objs := spitz.o
snd-soc-amesom-tlv320-objs := amesom_tlv320.o
snd-soc-magician-objs := magician.o
snd-soc-h5000-objs := h5000.o
-snd-soc-mioa701-objs := mioa701_wm9713.o
+snd-soc-mioa701-objs := mioa701_wm9713.o mioa701_masterctrl.o
snd-soc-mainstone-wm8753-objs := mainstone_wm8753.o
snd-soc-mainstone-wm9713-objs := mainstone_wm9713.o
snd-soc-mainstone-wm9712-objs := mainstone_wm9712.o
diff --git a/sound/soc/pxa/mioa701_masterctrl.c b/sound/soc/pxa/mioa701_masterctrl.c
new file mode 100644
index 0000000..92055c8
--- /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 MAST2CARD(m) (((struct kctl_master_t *)m)->card)
+#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_card *card;
+};
+
+#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_card *card, 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(card->card, &rid);
+}
+
+static int master_find(struct snd_soc_card *card, 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(card, 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_card *card = MAST2CARD(master);
+ int i, ind, max, old;
+
+ for (i = 0; i < master->count; i++) {
+ max = old = 0;
+ ind = master->nidx[i];
+ if (!master_find(card, 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_card *card = MAST2CARD(master);
+
+ int i, ind, max, new;
+
+ for (i = 0; i < master->count; i++) {
+ max = new = 0;
+ ind = master->nidx[i];
+ if (!master_find(card, 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->card = MAST2CARD(k->private_data);
+ if (k)
+ k->private_data = masterconf;
+}
+
+struct snd_kcontrol *master_init(struct snd_soc_card *card, 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->card = card;
+
+ kctl = snd_soc_cnew(kctln, master, NULL);
+
+ rc = snd_ctl_add(card->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_card *card)
+{
+ miovol = master_init(card, "Mio Volume", &miomastervol[0]);
+ miomute = master_init(card, "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 4df42e0..f063023 100644
--- a/sound/soc/pxa/mioa701_wm9713.c
+++ b/sound/soc/pxa/mioa701_wm9713.c
@@ -53,6 +53,8 @@
static int mio_scenario = MIO_AUDIO_OFF;
+extern int mioa701_master_init(struct snd_soc_card *card);
+extern void mioa701_master_change(int scenario);
static int phone_stream_start(struct snd_soc_card *card);
static int phone_stream_stop(struct snd_soc_card *card);
@@ -318,6 +320,8 @@ static void switch_mio_mode(struct snd_soc_card *card, int new_scenario)
default:
break;
}
+
+ mioa701_master_change(mio_scenario);
}
static int set_scenario(struct snd_kcontrol *kcontrol,
@@ -483,6 +487,9 @@ static int mioa701_wm9713_init(struct snd_soc_card *card)
goto out;
}
+ /* Add mio Masters */
+ mioa701_master_init(card);
+
/* Add mioa701 specific widgets */
ret = snd_soc_dapm_new_controls(card, codec,
ARRAY_AND_SIZE(mioa701_dapm_widgets));
--
1.5.5.3
More information about the Alsa-devel
mailing list