[alsa-devel] [PATCH] Add initial support of Mitac mioa701 device SoC.

Robert Jarzmik robert.jarzmik at free.fr
Tue Jul 8 22:45:05 CEST 2008


This machine driver enables sound functions on Mitac mio
a701 smartphone. Build upon ASoC v2, it handles :
 - rear speaker
 - front speaker
 - microphone
 - GSM

A global "Mio Mode" switch is provided to cope with audio
path setup. It ensures balance on audio chip line, which if
not respected can produce a lot of heat and even fry the
battery behind the wm9713 and the speaker amplificator.

It doesn't cope with :
 - headset jack (will be integrade after jack support has
   hit ASoC v2)
 - master volume control (depending on current Mio Mode,
   will be submitted in a second patch)

Signed-off-by: Robert Jarzmik <robert.jarzmik at free.fr>
---
 sound/soc/pxa/Kconfig          |    9 +
 sound/soc/pxa/Makefile         |    2 +
 sound/soc/pxa/mioa701_wm9713.c |  639 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 650 insertions(+), 0 deletions(-)
 create mode 100644 sound/soc/pxa/mioa701_wm9713.c

diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig
index 7530227..ca60dec 100644
--- a/sound/soc/pxa/Kconfig
+++ b/sound/soc/pxa/Kconfig
@@ -139,3 +139,12 @@ config SND_PXA2XX_SOC_E800
 	help
 	  Say Y if you want to add support for SoC audio on the
 	  Toshiba e800 PDA
+
+config SND_PXA2XX_SOC_MIOA701
+        tristate "SoC Audio support for MIO A701"
+        depends on SND_PXA2XX_SOC
+        select SND_PXA2XX_SOC_AC97
+        select SND_SOC_WM9713
+        help
+          Say Y if you want to add support for SoC audio on the
+          MIO A701.
diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile
index fd45a09..ce171c3 100644
--- a/sound/soc/pxa/Makefile
+++ b/sound/soc/pxa/Makefile
@@ -21,6 +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-mainstone-wm8753-objs := mainstone_wm8753.o
 snd-soc-mainstone-wm9713-objs := mainstone_wm9713.o
 snd-soc-mainstone-wm9712-objs := mainstone_wm9712.o
@@ -34,6 +35,7 @@ obj-$(CONFIG_SND_PXA2XX_SOC_SPITZ) += snd-soc-spitz.o
 obj-$(CONFIG_SND_PXA2XX_SOC_AMESOM_TLV320) += snd-soc-amesom-tlv320.o
 obj-$(CONFIG_SND_PXA2XX_SOC_MAGICIAN) += snd-soc-magician.o
 obj-$(CONFIG_SND_PXA2XX_SOC_H5000) += snd-soc-h5000.o
+obj-$(CONFIG_SND_PXA2XX_SOC_MIOA701) += snd-soc-mioa701.o
 obj-$(CONFIG_SND_PXA2XX_SOC_MAINSTONE_WM8753) += snd-soc-mainstone-wm8753.o
 obj-$(CONFIG_SND_PXA2XX_SOC_MAINSTONE_WM9713) += snd-soc-mainstone-wm9713.o
 obj-$(CONFIG_SND_PXA2XX_SOC_MAINSTONE_WM9712) += snd-soc-mainstone-wm9712.o
diff --git a/sound/soc/pxa/mioa701_wm9713.c b/sound/soc/pxa/mioa701_wm9713.c
new file mode 100644
index 0000000..4df42e0
--- /dev/null
+++ b/sound/soc/pxa/mioa701_wm9713.c
@@ -0,0 +1,639 @@
+/*
+ * Handles the Mitac mioa701 SoC system
+ *
+ * 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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/ac97_codec.h>
+
+#include <asm/arch/audio.h>
+
+#include "pxa2xx-pcm.h"
+#include "../codecs/wm9713.h"
+
+#define ARRAY_AND_SIZE(x)	(x), ARRAY_SIZE(x)
+
+#define AC97_GPIO_PULL		0x58
+
+/* define the scenarios */
+#define MIO_AUDIO_OFF			0
+#define MIO_GSM_AUDIO_HANDSET		1
+#define MIO_GSM_AUDIO_HEADSET		2
+#define MIO_GSM_AUDIO_HANDSFREE		3
+#define MIO_GSM_AUDIO_BLUETOOTH		4
+#define MIO_STEREO_TO_SPEAKER		5
+#define MIO_STEREO_TO_HEADPHONES	6
+#define MIO_CAPTURE_HANDSET		7
+#define MIO_CAPTURE_HEADSET		8
+#define MIO_CAPTURE_BLUETOOTH		9
+
+static int mio_scenario = MIO_AUDIO_OFF;
+
+static int phone_stream_start(struct snd_soc_card *card);
+static int phone_stream_stop(struct snd_soc_card *card);
+
+struct mio_mixes_t {
+	char *mixname;
+	int  val;
+};
+
+static const struct mio_mixes_t mixes_reset_all[] = {
+	/* left HP mixer */
+	{"Left HP Mixer PC Beep Playback Switch", 0},
+	{"Left HP Mixer Voice Playback Switch",	  0},
+	{"Left HP Mixer Aux Playback Switch",	  0},
+	{"Left HP Mixer Bypass Playback Switch",  0},
+	{"Left HP Mixer PCM Playback Switch",	  0},
+	{"Left HP Mixer MonoIn Playback Switch",  0},
+	{"Left HP Mixer Capture Headphone Mux",	  0},
+
+	/* right HP mixer */
+	{"Right HP Mixer PC Beep Playback Switch", 0},
+	{"Right HP Mixer Voice Playback Switch",   0},
+	{"Right HP Mixer Aux Playback Switch",	   0},
+	{"Right HP Mixer Bypass Playback Switch",  0},
+	{"Right HP Mixer PCM Playback Switch",	   0},
+	{"Right HP Mixer MonoIn Playback Switch",  0},
+	{"Right HP Mixer Capture Headphone Mux",   0},
+
+	/* speaker mixer */
+	{"Speaker Mixer PC Beep Playback Switch", 0},
+	{"Speaker Mixer Voice Playback Switch",	  0},
+	{"Speaker Mixer Aux Playback Switch",	  0},
+	{"Speaker Mixer Bypass Playback Switch",  0},
+	{"Speaker Mixer PCM Playback Switch",	  0},
+	{"Speaker Mixer MonoIn Playback Switch",  0},
+	{"Speaker Mixer Mic 1 Sidetone Switch",	  0},
+
+	/* mono mixer */
+	{"Mono Mixer PC Beep Playback Switch", 0},
+	{"Mono Mixer Voice Playback Switch",   0},
+	{"Mono Mixer Aux Playback Switch",     0},
+	{"Mono Mixer Bypass Playback Switch",  0},
+	{"Mono Mixer PCM Playback Switch",     0},
+	{"Mono Mixer Capture Mono Mux",	       0},
+	{"Mono Mixer MonoIn Playback Switch",  0},
+	{"Mono Mixer Mic 1 Sidetone Switch",   0},
+	{"Mono Playback Switch",	       0},
+
+	/* headphone muxers */
+	{"Left Headphone Out Mux", 0},
+	{"Right Headphone Out Mux", 0},
+
+	/* speaker muxer */
+	{"Left Speaker Out Mux", 0},
+	{"Right Speaker Out Mux", 0},
+
+	/* Out3 muxer */
+	{ "Out 3 Mux", 0},
+
+	{ NULL, 0 }
+};
+
+static const struct mio_mixes_t mixes_gsm_call_headset[] = {
+	/*
+	 * GSM Out to Headset HPL Path
+	 *   => PCBeep -> Headphone Mixer, Headphone Mixer -> HPL
+	 */
+	{ "Left HP Mixer PC Beep Playback Switch", 1 },
+	{ "Left Headphone Out Mux", 2 },
+	/*
+	 * GSM Out to Headset HPR Path
+	 *   => MonoIn -> Headphone Mixer, Headphone Mixer -> HPR
+	 */
+	{ "Right HP Mixer MonoIn Playback Switch" , 1 },
+	{ "Right Headphone Out Mux", 2 },
+	/*
+	 * LineL to GSM In
+	 * LineL -> MonoMixer, MonoMixer -> Mono, Unmute Mono Mixer
+	 */
+	{ "Mono Mixer Bypass Playback Switch",	1},
+	{ "Mono Out Mux", 2 },
+	{ "Mono Playback Switch", 1},
+	{ NULL, 0 }
+};
+
+static const struct mio_mixes_t mixes_gsm_call_handset[] = {
+	/*
+	 * GSM Out to Front Speaker HPL Path
+	 *   => PCBeep -> Headphone Mixer, Headphone Mixer -> HPL
+	 */
+	{ "Left HP Mixer PC Beep Playback Switch", 1 },
+	{ "Left Headphone Out Mux", 2 },
+	/*
+	 * GSM Out to Front Speaker Out3 Path
+	 *   => MonoIn -> Speaker Mixer, Speaker Mixer -> Inv1, Inv1 -> Out3
+	 */
+	{ "Speaker Mixer MonoIn Playback Switch" , 1 },
+	{ "DAC Inv Mux 1", 2 },
+	{ "Out 3 Mux", 2 },
+	/*
+	 * MIC1 to GSM In
+	 *   => MIC1 -> MICA, MICA -> Mono Mixer, Mono Mixer -> MONO,
+	 *      UnMute Mono Mixer
+	 */
+	{ "Mic A Source", 0 },
+	{ "Mono Mixer Mic 1 Sidetone Switch", 1 },
+	{ "Mono Out Mux", 2 },
+	{ "Mono Playback Switch", 1},
+	{ NULL, 0 }
+};
+
+static const struct mio_mixes_t mixes_gsm_call_handsfree[] = {
+	/*
+	 * GSM Out to Rear Speaker SPKL Path
+	 *   => PCBeep -> Speaker Mixer, Speaker Mixer -> Inv1, Inv1 -> SPKL
+	 */
+	{ "Speaker Mixer PC Beep Playback Switch", 1 },
+	{ "DAC Inv Mux 1", 2 },
+	{ "Left Speaker Out Mux", 4 },
+	/*
+	 * GSM Out to Rear Speaker SPKR Path
+	 *   => MonoIn -> Speaker Mixer, Speaker Mixer -> SPKR
+	 */
+	{ "Speaker Mixer MonoIn Playback Switch" , 1 },
+	{ "Right Speaker Out Mux", 3 },
+	/*
+	 * MIC1 to GSM In
+	 *   => MIC1 -> MICA, MICA -> Mono Mixer, Mono Mixer -> MONO,
+	 *      Unmute Mono Mixer
+	 */
+	{ "Mic A Source", 0 },
+	{ "Mono Mixer Mic 1 Sidetone Switch", 1 },
+	{ "Mono Out Mux", 2 },
+	{ "Mono Playback Switch", 1},
+	{ NULL, 0 }
+};
+
+static const struct mio_mixes_t mixes_stereo_to_rearspeaker[] = {
+	/*
+	 * PCM to Rear Speakers
+	 *   => PCM -> Speaker Mixer, Speaker Mixer -> Inv1, Inv1 -> SPKL,
+	 *      Speaker Mixer -> SPKR
+	 */
+	{ "Speaker Mixer PCM Playback Switch", 1},
+	{ "DAC Inv Mux 1", 2 },
+	{ "Left Speaker Out Mux", 4 },
+	{ "Right Speaker Out Mux", 3 },
+	{ NULL, 0 }
+};
+
+struct snd_kcontrol *mioa701_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);
+}
+
+void setup_muxers(struct snd_soc_card *card, const struct mio_mixes_t mixes[])
+{
+	int pos = 0;
+	struct snd_kcontrol *kctl;
+	struct snd_ctl_elem_value ucontrol;
+	char mname[44];
+
+	while (mixes[pos].mixname) {
+		memset(mname, 0, 44);
+		strncpy(mname, mixes[pos].mixname, 43);
+		kctl = mioa701_kctrl_byname(card, mname);
+		memset(&ucontrol, 0, sizeof(ucontrol));
+		if (kctl) {
+			kctl->get(kctl, &ucontrol);
+			ucontrol.value.enumerated.item[0] = mixes[pos].val;
+			kctl->put(kctl, &ucontrol);
+		}
+		pos++;
+	}
+}
+
+#define NB_ENDP ARRAY_SIZE(endpn)
+static int set_scenario_endpoints(struct snd_soc_card *card, int scenario)
+{
+	static char *endpn[] = { "Front Speaker", "Rear Speaker",
+				 "GSM Line Out", "GSM Line In",
+				 "Headset Mic", "Front Mic", "Headset" };
+	static const int typ_endps[][NB_ENDP] = {
+		{ 0, 0, 0, 0, 0, 0, 0 }, /* MIO_AUDIO_OFF		*/
+		{ 1, 0, 1, 1, 0, 1, 0 }, /* MIO_GSM_AUDIO_HANDSET	*/
+		{ 0, 0, 1, 1, 1, 0, 1 }, /* MIO_GSM_AUDIO_HEADSET	*/
+		{ 0, 1, 1, 1, 0, 1, 0 }, /* MIO_GSM_AUDIO_HANDSFREE*/
+		{ 0, 0, 1, 1, 0, 0, 0 }, /* MIO_GSM_AUDIO_BLUETOOTH*/
+		{ 0, 1, 0, 0, 0, 0, 0 }, /* MIO_STEREO_TO_SPEAKER	*/
+		{ 0, 0, 0, 0, 0, 0, 1 }, /* MIO_STEREO_TO_HEADPHONES	*/
+		{ 0, 0, 0, 0, 0, 1, 0 }, /* MIO_CAPTURE_HANDSET		*/
+		{ 0, 0, 0, 0, 1, 0, 0 }, /* MIO_CAPTURE_HEADSET		*/
+		{ 0, 0, 0, 0, 0, 0, 0 }, /* MIO_CAPTURE_BLUETOOTH	*/
+	};
+	const int *endps = typ_endps[scenario];
+	int i;
+
+	for (i = 0; i < NB_ENDP; i++)
+		if (endps[i])
+			snd_soc_dapm_enable_pin(card, endpn[i]);
+		else
+			snd_soc_dapm_disable_pin(card, endpn[i]);
+	snd_soc_dapm_sync(card);
+
+	return 0;
+}
+
+static int get_scenario(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = mio_scenario;
+	return 0;
+}
+
+static int isPhoneMode(int scenario)
+{
+	int onPhone = 0;
+
+	switch (scenario) {
+	case MIO_GSM_AUDIO_HANDSET:
+	case MIO_GSM_AUDIO_HEADSET:
+	case MIO_GSM_AUDIO_BLUETOOTH:
+	case MIO_GSM_AUDIO_HANDSFREE:
+		onPhone = 1;
+	}
+
+	return onPhone;
+}
+
+static void switch_mio_mode(struct snd_soc_card *card, int new_scenario)
+{
+	int wasPhone = 0, willPhone = 0;
+
+	wasPhone  = isPhoneMode(mio_scenario);
+	willPhone = isPhoneMode(new_scenario);
+
+	mio_scenario = new_scenario;
+	set_scenario_endpoints(card, mio_scenario);
+
+	if (!wasPhone && willPhone)
+		phone_stream_start(card);
+	if (wasPhone && !willPhone)
+		phone_stream_stop(card);
+
+	setup_muxers(card, mixes_reset_all);
+	switch (mio_scenario) {
+	case MIO_STEREO_TO_SPEAKER:
+		setup_muxers(card, mixes_stereo_to_rearspeaker);
+		break;
+	case MIO_GSM_AUDIO_HANDSET:
+		setup_muxers(card, mixes_gsm_call_handset);
+		break;
+	case MIO_GSM_AUDIO_HANDSFREE:
+		setup_muxers(card, mixes_gsm_call_handsfree);
+		break;
+	case MIO_GSM_AUDIO_HEADSET:
+		setup_muxers(card, mixes_gsm_call_headset);
+		break;
+	default:
+		break;
+	}
+}
+
+static int set_scenario(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+
+	if (mio_scenario == ucontrol->value.integer.value[0])
+		return 0;
+
+	switch_mio_mode(card, ucontrol->value.integer.value[0]);
+	return 1;
+}
+
+static int phone_stream_start(struct snd_soc_card *card)
+{
+	snd_soc_dapm_stream_event(card, "AC97 HiFi",
+				  SND_SOC_DAPM_STREAM_START);
+	return 0;
+}
+
+static int phone_stream_stop(struct snd_soc_card *card)
+{
+	snd_soc_dapm_stream_event(card, "AC97 HiFi",
+				  SND_SOC_DAPM_STREAM_STOP);
+	return 0;
+}
+
+/* Use GPIO8 for rear speaker amplificator */
+static int rear_amp_power(struct snd_soc_codec *codec, int power)
+{
+	unsigned short reg;
+
+	if (power) {
+		reg = snd_soc_read(codec, AC97_GPIO_CFG);
+		snd_soc_write(codec, AC97_GPIO_CFG, reg | 0x0100);
+		reg = snd_soc_read(codec, AC97_GPIO_PULL);
+		snd_soc_write(codec, AC97_GPIO_PULL, reg | (1<<15));
+	} else {
+		reg = snd_soc_read(codec, AC97_GPIO_CFG);
+		snd_soc_write(codec, AC97_GPIO_CFG, reg & ~0x0100);
+		reg = snd_soc_read(codec, AC97_GPIO_PULL);
+		snd_soc_write(codec, AC97_GPIO_PULL, reg & ~(1<<15));
+	}
+
+	return 0;
+}
+
+static int rear_amp_event(struct snd_soc_dapm_widget *widget,
+			  struct snd_kcontrol *kctl, int event)
+{
+	struct snd_soc_codec *codec = widget->codec;
+	int rc;
+
+	if (SND_SOC_DAPM_EVENT_ON(event))
+		rc = rear_amp_power(codec, 1);
+	else
+		rc = rear_amp_power(codec, 0);
+
+	return rc;
+}
+
+static const char *mio_scenarios[] = {
+	"Off",
+	"GSM Handset",
+	"GSM Headset",
+	"GSM Handsfree",
+	"GSM Bluetooth",
+	"PCM Speaker",
+	"Headphones",
+	"Capture Handset",
+	"Capture Headset",
+	"Capture Bluetooth"
+};
+static const struct soc_enum mio_scenario_enum[] = {
+	SOC_ENUM_SINGLE_EXT(10, mio_scenarios),
+};
+
+static const struct snd_kcontrol_new mioa701_controls[] = {
+	SOC_ENUM_EXT("Mio Mode", mio_scenario_enum[0],
+		     get_scenario, set_scenario),
+};
+
+/* mioa701 machine dapm widgets */
+static const struct snd_soc_dapm_widget mioa701_dapm_widgets[] = {
+	SND_SOC_DAPM_SPK("Front Speaker", NULL),
+	SND_SOC_DAPM_SPK("Rear Speaker", rear_amp_event),
+	SND_SOC_DAPM_MIC("Headset", NULL),
+	SND_SOC_DAPM_LINE("GSM Line Out", NULL),
+	SND_SOC_DAPM_LINE("GSM Line In", NULL),
+	SND_SOC_DAPM_MIC("Headset Mic", NULL),
+	SND_SOC_DAPM_MIC("Front Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+	/* Call Mic */
+	{"Mic Bias", NULL, "Front Mic"},
+	{"MIC1", NULL, "Mic Bias"},
+
+	/* Headset Mic */
+	{"LINEL", NULL, "Headset Mic"},
+	{"LINER", NULL, "Headset Mic"},
+
+	/* GSM Module */
+	{"MONOIN", NULL, "GSM Line Out"},
+	{"PCBEEP", NULL, "GSM Line Out"},
+	{"GSM Line In", NULL, "MONO"},
+
+	/* headphone connected to HPL, HPR */
+	{"Headset", NULL, "HPL"},
+	{"Headset", NULL, "HPR"},
+
+	/* front speaker connected to HPL, OUT3 */
+	{"Front Speaker", NULL, "HPL"},
+	{"Front Speaker", NULL, "OUT3"},
+
+	/* rear speaker connected to SPKL, SPKR */
+	{"Rear Speaker", NULL, "SPKL"},
+	{"Rear Speaker", NULL, "SPKR"},
+};
+
+static int mioa701_wm9713_init(struct snd_soc_card *card)
+{
+	struct snd_soc_codec *codec;
+	struct snd_ac97_bus_ops *ac97_ops;
+	int i, ret;
+	unsigned short reg;
+
+	codec = snd_soc_card_get_codec(card, wm9713_codec_id, 0);
+	if (codec == NULL)
+		return -ENODEV;
+
+	ac97_ops = snd_soc_card_get_ac97_ops(card, pxa_ac97_hifi_dai_id);
+	if (!ac97_ops) {
+		printk(KERN_ERR "Unable to obtain AC97 operations\n");
+		return -ENODEV;
+	}
+
+	/* register with AC97 bus for ad-hoc driver access */
+	ret = snd_soc_new_ac97_codec(codec, ac97_ops, card->card, 0, 0);
+	if (ret < 0)
+		return ret;
+
+	/* do a cold reset for the controller and then try
+	 * a warm reset followed by an optional cold reset for codec */
+	ac97_ops->reset(codec->ac97);
+	ac97_ops->warm_reset(codec->ac97);
+	if (ac97_ops->read(codec->ac97, AC97_VENDOR_ID1) == 0) {
+		printk(KERN_ERR "AC97 link error\n");
+		return ret;
+	}
+
+	snd_soc_card_init_codec(codec, card);
+
+	/* initialize mioa701 codec pins */
+	set_scenario_endpoints(card, mio_scenario);
+
+	/* Add mioa701 specific controls */
+	for (i = 0; i < ARRAY_SIZE(mioa701_controls); i++) {
+		ret = snd_ctl_add(card->card, snd_soc_cnew(&mioa701_controls[i],
+							   card, NULL));
+		if (ret)
+			goto out;
+	}
+
+	/* Add mioa701 specific widgets */
+	ret = snd_soc_dapm_new_controls(card, codec,
+					ARRAY_AND_SIZE(mioa701_dapm_widgets));
+	if (ret)
+		goto out;
+
+	/* Set up mioa701 specific audio path audio_mapnects */
+	ret = snd_soc_dapm_add_routes(card, ARRAY_AND_SIZE(audio_map));
+	if (ret)
+		goto out;
+
+	/* Prepare MIC input */
+	reg = snd_soc_read(codec, AC97_3D_CONTROL);
+	snd_soc_write(codec, AC97_3D_CONTROL, reg | 0xc000);
+
+	snd_soc_dapm_sync(card);
+
+	return 0;
+out:
+	return ret;
+}
+
+static void mioa701_wm9713_exit(struct snd_soc_card *card)
+{
+}
+
+#ifdef CONFIG_PM
+static int mioa701_wm9713_suspend(struct platform_device *pdev,
+				  pm_message_t state)
+{
+	struct snd_soc_card *card = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec;
+
+	codec = snd_soc_card_get_codec(card, wm9713_codec_id, 0);
+	if (codec == NULL)
+		return -ENODEV;
+
+	rear_amp_power(codec, 0);
+	return 0; /* snd_soc_card_suspend_pcms(card, state) doesn't work */
+}
+
+static int mioa701_wm9713_resume(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec;
+
+	codec = snd_soc_card_get_codec(card, wm9713_codec_id, 0);
+	if (codec == NULL)
+		return -ENODEV;
+
+	rear_amp_power(codec, 0);
+	return 0; /* snd_soc_card_resume_pcms(card) doesn't work */
+}
+
+#else
+#define mioa701_wm9713_suspend NULL
+#define mioa701_wm9713_resume  NULL
+#endif
+
+static struct snd_soc_ops mioa701_hifi_ops = {
+};
+
+static struct snd_soc_ops mioa701_aux_ops = {
+};
+
+static struct snd_soc_pcm_config pcm_configs[] = {
+	{
+		.name		= "Aux",
+		.codec		= wm9713_codec_id,
+		.codec_dai	= wm9713_codec_aux_dai_id,
+		.platform	= pxa_platform_id,
+		.cpu_dai	= pxa_ac97_aux_dai_id,
+		.playback	= 1,
+		.ops		= &mioa701_aux_ops,
+	},
+	{
+		.name		= "HiFi",
+		.codec		= wm9713_codec_id,
+		.codec_dai	= wm9713_codec_hifi_dai_id,
+		.platform	= pxa_platform_id,
+		.cpu_dai	= pxa_ac97_hifi_dai_id,
+		.playback	= 1,
+		.capture	= 1,
+		.ops		= &mioa701_hifi_ops,
+	},
+};
+
+static int mioa701_wm9713_probe(struct platform_device *pdev)
+{
+	struct snd_soc_card *card;
+	int ret;
+
+	card = snd_soc_card_create("mioa701", &pdev->dev,
+				   SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (card == NULL)
+		return -ENOMEM;
+
+	card->longname = "WM9713";
+	card->init = mioa701_wm9713_init;
+	card->exit = mioa701_wm9713_exit;
+	card->private_data = pdev;
+	platform_set_drvdata(pdev, card);
+
+	ret = snd_soc_card_create_pcms(card, ARRAY_AND_SIZE(pcm_configs));
+	if (ret < 0)
+		goto errpcms;
+
+	ret = snd_soc_card_register(card);
+	if (ret < 0)
+		goto errcard;
+	return ret;
+
+errpcms:
+	snd_soc_card_free(card);
+errcard:
+	return ret;
+}
+
+static int __devexit mioa701_wm9713_remove(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+	snd_soc_card_free(card);
+	return 0;
+}
+
+static struct platform_driver mioa701_wm9713_driver = {
+	.probe		= mioa701_wm9713_probe,
+	.remove		= __devexit_p(mioa701_wm9713_remove),
+	.suspend	= mioa701_wm9713_suspend,
+	.resume		= mioa701_wm9713_resume,
+	.driver		= {
+		.name		= "mioa701-wm9713",
+		.owner		= THIS_MODULE,
+	},
+};
+
+static int __init mioa701_asoc_init(void)
+{
+	return platform_driver_register(&mioa701_wm9713_driver);
+}
+
+static void __exit mioa701_asoc_exit(void)
+{
+	platform_driver_unregister(&mioa701_wm9713_driver);
+}
+
+module_init(mioa701_asoc_init);
+module_exit(mioa701_asoc_exit);
+
+/* Module information */
+MODULE_AUTHOR("Robert Jarzmik (rjarzmik at free.fr)");
+MODULE_DESCRIPTION("ALSA SoC WM9713 MIO A701");
+MODULE_LICENSE("GPL");
-- 
1.5.5.3



More information about the Alsa-devel mailing list