All of lore.kernel.org
 help / color / mirror / Atom feed
From: Pavel Machek <pavel@ucw.cz>
To: perex@perex.cz, tiwai@suse.com, lgirdwood@gmail.com,
	broonie@kernel.org, peter.ujfalusi@ti.com,
	jarkko.nikula@bitmer.com, bhumirks@gmail.com,
	alsa-devel@alsa-project.org, pali.rohar@gmail.com,
	sre@kernel.org, "kernel list" <linux-kernel@vger.kernel.org>,
	linux-arm-kernel <linux-arm-kernel@lists.infradead.org>,
	linux-omap@vger.kernel.org, tony@atomide.com, khilman@kernel.org,
	aaro.koskinen@iki.fi, ivo.g.dimitrov.75@gmail.com,
	patrikbachan@gmail.com, serge@hallyn.com, abcloriens@gmail.com,
	clayton@craftyguy.net, martijn@brixit.nl,
	sakari.ailus@linux.intel.com,
	"Filip Matijević" <filip.matijevic.pz@gmail.com>
Subject: [rfc] Sound support for n9
Date: Fri, 12 Jan 2018 16:00:44 +0100	[thread overview]
Message-ID: <20180112150044.GA31446@amd> (raw)


[-- Attachment #1.1: Type: text/plain, Size: 27000 bytes --]

Hi!

Binding documentation is pending, and code will need to be updated to
match it.

I guess burst_bclkdiv should be handled as int, not as u8?

SPDX is modern these days.

Anything else that needs to be fixed?

Best regards,
								Pavel


diff --git a/include/sound/n9.h b/include/sound/n9.h
new file mode 100644
index 0000000..b14ddf0
--- /dev/null
+++ b/include/sound/n9.h
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (C) 2009 Nokia
+
+#ifndef _N9_H_
+#define _N9_H_
+
+struct dfl61audio_hsmic_event {
+	void *private;
+	void (*event)(void *priv, bool on);
+};
+
+void dfl61_jack_report(int status);
+int dfl61_request_hsmicbias(bool enable);
+void dfl61_register_hsmic_event_cb(struct dfl61audio_hsmic_event *event);
+int dfl61_request_hp_enable(bool enable);
+#endif
diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c
index 5b94a15..3823bcc 100644
--- a/sound/soc/codecs/tlv320dac33.c
+++ b/sound/soc/codecs/tlv320dac33.c
@@ -30,6 +32,7 @@
 #include <linux/interrupt.h>
 #include <linux/gpio.h>
 #include <linux/regulator/consumer.h>
+#include <linux/of_gpio.h>
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -1484,16 +1487,11 @@ static struct snd_soc_dai_driver dac33_dai = {
 static int dac33_i2c_probe(struct i2c_client *client,
 			   const struct i2c_device_id *id)
 {
-	struct tlv320dac33_platform_data *pdata;
+	struct tlv320dac33_platform_data *pdata = client->dev.platform_data;
 	struct tlv320dac33_priv *dac33;
+	struct device_node *np = client->dev.of_node;
 	int ret, i;
 
-	if (client->dev.platform_data == NULL) {
-		dev_err(&client->dev, "Platform data not set\n");
-		return -ENODEV;
-	}
-	pdata = client->dev.platform_data;
-
 	dac33 = devm_kzalloc(&client->dev, sizeof(struct tlv320dac33_priv),
 			     GFP_KERNEL);
 	if (dac33 == NULL)
@@ -1505,10 +1503,26 @@ static int dac33_i2c_probe(struct i2c_client *client,
 
 	i2c_set_clientdata(client, dac33);
 
-	dac33->power_gpio = pdata->power_gpio;
-	dac33->burst_bclkdiv = pdata->burst_bclkdiv;
-	dac33->keep_bclk = pdata->keep_bclk;
-	dac33->mode1_latency = pdata->mode1_latency;
+	if (pdata) {
+		dac33->power_gpio = pdata->power_gpio;
+		dac33->burst_bclkdiv = pdata->burst_bclkdiv;
+		dac33->keep_bclk = pdata->keep_bclk;
+		dac33->mode1_latency = pdata->mode1_latency;
+	} else if (np) {
+		ret = of_get_named_gpio(np, "power-gpio", 0);
+		if (ret >= 0)
+			dac33->power_gpio = ret;
+		else
+			dac33->power_gpio = -1;
+
+		if (of_property_read_bool(np, "keep-bclk"))
+			dac33->keep_bclk = true;
+
+		of_property_read_u8(np, "burst-bclkdiv", &dac33->burst_bclkdiv);
+	} else {
+		dev_err(&client->dev, "Platform data not set\n");
+		return -ENODEV;
+	}
 	if (!dac33->mode1_latency)
 		dac33->mode1_latency = 10000; /* 10ms */
 	dac33->irq = client->irq;
@@ -1574,9 +1588,16 @@ static const struct i2c_device_id tlv320dac33_i2c_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, tlv320dac33_i2c_id);
 
+static const struct of_device_id tlv320dac33_of_match[] = {
+	{ .compatible = "ti,tlv320dac33", },
+	{},
+};
+MODULE_DEVICE_TABLE(i2c, tlv320dac33_of_match);
+
 static struct i2c_driver tlv320dac33_i2c_driver = {
 	.driver = {
 		.name = "tlv320dac33-codec",
+		.of_match_table = of_match_ptr(tlv320dac33_of_match),
 	},
 	.probe		= dac33_i2c_probe,
 	.remove		= dac33_i2c_remove,
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig
index f5451c7..2772414 100644
--- a/sound/soc/omap/Kconfig
+++ b/sound/soc/omap/Kconfig
@@ -47,6 +47,18 @@ config SND_OMAP_SOC_RX51
 	  Say Y if you want to add support for SoC audio on Nokia N900
 	  cellphone.
 
+config SND_OMAP_SOC_N9
+	tristate "SoC Audio support for Nokia N9/N950"
+	depends on SND_OMAP_SOC && ARM && I2C
+	select SND_OMAP_SOC_MCBSP
+	select SND_SOC_TWL4030
+	select SND_SOC_TLV320DAC33
+	select SND_SOC_TPA6130A2
+	select SND_SOC_WL1273 if MFD_WL1273_CORE
+	depends on GPIOLIB
+	help
+	  Say Y if you want to add support for SoC audio on Nokia N9/N950.
+
 config SND_OMAP_SOC_AMS_DELTA
 	tristate "SoC Audio support for Amstrad E3 (Delta) videophone"
 	depends on SND_OMAP_SOC && MACH_AMS_DELTA && TTY
diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile
index a6785dc..07efd21 100644
--- a/sound/soc/omap/Makefile
+++ b/sound/soc/omap/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_SND_OMAP_SOC_HDMI_AUDIO) += snd-soc-omap-hdmi-audio.o
 # OMAP Machine Support
 snd-soc-n810-objs := n810.o
 snd-soc-rx51-objs := rx51.o
+snd-soc-n9-objs := n9.o
 snd-soc-ams-delta-objs := ams-delta.o
 snd-soc-osk5912-objs := osk5912.o
 snd-soc-am3517evm-objs := am3517evm.o
@@ -24,6 +25,7 @@ snd-soc-omap3pandora-objs := omap3pandora.o
 
 obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o
 obj-$(CONFIG_SND_OMAP_SOC_RX51) += snd-soc-rx51.o
+obj-$(CONFIG_SND_OMAP_SOC_N9) += snd-soc-n9.o
 obj-$(CONFIG_SND_OMAP_SOC_AMS_DELTA) += snd-soc-ams-delta.o
 obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o
 obj-$(CONFIG_SND_OMAP_SOC_AM3517EVM) += snd-soc-am3517evm.o
diff --git a/sound/soc/omap/n9.c b/sound/soc/omap/n9.c
new file mode 100644
index 0000000..db61463
--- /dev/null
+++ b/sound/soc/omap/n9.c
@@ -0,0 +1,748 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// n9.c  --  SoC audio for Nokia N9/N950
+//
+// Copyright (C) 2008 - 2009 Nokia Corporation
+//
+// Contact: Peter Ujfalusi <peter.ujfalusi@ti.com>
+//          Eduardo Valentin <eduardo.valentin@nokia.com>
+//          Jarkko Nikula <jarkko.nikula@bitmer.com>
+//
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <linux/platform_data/asoc-ti-mcbsp.h>
+#include <linux/mfd/twl4030-audio.h>
+#include "../codecs/tlv320dac33.h"
+#include "../codecs/tpa6130a2.h"
+#include "../codecs/wl1273.h"
+
+#include <asm/mach-types.h>
+
+#include <sound/n9.h>
+#include "omap-mcbsp.h"
+#include "mcbsp.h"
+
+#define JACK_REPORT_MASK	(SND_JACK_MECHANICAL | SND_JACK_AVOUT | \
+							 SND_JACK_HEADSET)
+
+struct dfl61wl1273_audio_pdata {
+	struct gpio_desc *power_gpio;
+};
+
+struct dfl61twl_audio_pdata {
+	struct gpio_desc *speaker_amp_gpio;
+};
+
+static int dfl61dac33_interconnect_enable(int);
+static struct snd_soc_card dfl61dac33_sound_card;
+static struct snd_soc_card dfl61twl_sound_card;
+static struct snd_soc_jack dfl61_jack;
+static struct dfl61audio_hsmic_event *hsmic_event;
+
+static struct snd_soc_component *find_component(struct snd_soc_card *card) {
+	struct snd_soc_component *component;
+
+	if (list_empty(&card->component_dev_list)) {
+		pr_err("Can't find codec for %s\n", card->name);
+		return NULL;
+	}
+
+	component = list_entry(card->component_dev_list.next,
+			       struct snd_soc_component, card_list);
+
+	return component;
+}
+
+/* TWL4030 */
+void dfl61_jack_report(int status)
+{
+	if (dfl61_jack.card)
+		snd_soc_jack_report(&dfl61_jack, status, JACK_REPORT_MASK);
+	else
+		pr_err("twl4030: Cannot report jack status");
+}
+EXPORT_SYMBOL_GPL(dfl61_jack_report);
+
+int dfl61_request_hsmicbias(bool enable)
+{
+	struct snd_soc_component *component;
+	struct snd_soc_dapm_context *dapm;
+	bool lock = false;
+	int ret;
+
+	if (!dfl61twl_sound_card.instantiated) {
+		pr_warn("twl4030: sound card not instantiated yet");
+		return -EPROBE_DEFER;
+	}
+
+	component = find_component(&dfl61twl_sound_card);
+	if (!component)
+		return -ENODEV;
+
+	dapm = snd_soc_component_get_dapm(component);
+	if (!dapm) {
+		pr_err("twl4030: Cannot set hsmicbias yet");
+		return -ENODEV;
+	}
+
+	mutex_lock(&dfl61twl_sound_card.dapm_mutex);
+	lock = true;
+
+	if (enable)
+		snd_soc_dapm_force_enable_pin_unlocked(dapm, "Headset Mic Bias");
+	else
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Mic Bias");
+
+	ret = snd_soc_dapm_sync_unlocked(dapm);
+
+	if (lock)
+		mutex_unlock(&dfl61twl_sound_card.dapm_mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL(dfl61_request_hsmicbias);
+
+void dfl61_register_hsmic_event_cb(struct dfl61audio_hsmic_event *event)
+{
+	hsmic_event = event;
+}
+EXPORT_SYMBOL(dfl61_register_hsmic_event_cb);
+
+static int dfl61twl_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	unsigned int fmt;
+	int r;
+
+	switch (params_channels(params)) {
+	case 2: /* Stereo I2S mode */
+		fmt =	SND_SOC_DAIFMT_I2S |
+			SND_SOC_DAIFMT_NB_NF |
+			SND_SOC_DAIFMT_CBM_CFM;
+	case 4: /* Four channel TDM mode */
+		fmt =	SND_SOC_DAIFMT_DSP_A |
+			SND_SOC_DAIFMT_IB_NF |
+			SND_SOC_DAIFMT_CBM_CFM;
+		break;
+	default:
+		return -EINVAL;
+	}
+	/* Set codec DAI configuration */
+	r = snd_soc_dai_set_fmt(rtd->codec_dai, fmt);
+	if (r < 0) {
+		pr_err("Can't set codec DAI configuration for twl4030: %d\n", r);
+		return r;
+	}
+
+	/* Set cpu DAI configuration */
+	r = snd_soc_dai_set_fmt(rtd->cpu_dai, fmt);
+	if (r < 0) {
+		pr_err("Can't set cpu DAI configuration for twl4030: %d\n", r);
+		return r;
+	}
+
+	return 0;
+}
+
+static int dfl61twl_spk_event(struct snd_soc_dapm_widget *w,
+			  struct snd_kcontrol *k, int event)
+{
+	struct snd_soc_dapm_context *dapm = w->dapm;
+	struct snd_soc_card *card = dapm->card;
+	struct dfl61twl_audio_pdata *pdata = snd_soc_card_get_drvdata(card);
+
+	gpiod_set_raw_value_cansleep(pdata->speaker_amp_gpio,
+				     !!SND_SOC_DAPM_EVENT_ON(event));
+
+	return 0;
+}
+
+static int dfl61twl_tlv320dac33_event(struct snd_soc_dapm_widget *w,
+			  struct snd_kcontrol *k, int event)
+{
+	int r;
+
+	if (SND_SOC_DAPM_EVENT_ON(event))
+		r = dfl61dac33_interconnect_enable(1);
+	else
+		r = dfl61dac33_interconnect_enable(0);
+
+	return r;
+}
+
+static int dfl61twl_hsmic_event(struct snd_soc_dapm_widget *w,
+				struct snd_kcontrol *k, int event)
+{
+	if (!hsmic_event || !hsmic_event->event)
+		return 0;
+
+	if (SND_SOC_DAPM_EVENT_ON(event))
+		hsmic_event->event(hsmic_event->private, 1);
+	else
+		hsmic_event->event(hsmic_event->private, 0);
+
+	return 0;
+}
+
+/* DAPM widgets and routing for TWL4030 */
+static const struct snd_soc_dapm_widget dfl61twl_dapm_widgets[] = {
+	SND_SOC_DAPM_SPK("Ext Spk", dfl61twl_spk_event),
+	SND_SOC_DAPM_SPK("Earpiece", NULL),
+	SND_SOC_DAPM_SPK("HAC", NULL),
+	SND_SOC_DAPM_SPK("Vibra", NULL),
+	SND_SOC_DAPM_SPK("DAC33 interconnect", dfl61twl_tlv320dac33_event),
+
+	SND_SOC_DAPM_MIC("Digital Mic", NULL),
+	SND_SOC_DAPM_MIC("Headset Mic", dfl61twl_hsmic_event),
+
+	SND_SOC_DAPM_LINE("FMRX Left Line-in", NULL),
+	SND_SOC_DAPM_LINE("FMRX Right Line-in", NULL),
+};
+
+static const struct snd_soc_dapm_route dfl61twl_audio_map[] = {
+	{"Ext Spk", NULL, "PREDRIVER"},
+	{"Earpiece", NULL, "EARPIECE"},
+	{"HAC", NULL, "HFL"},
+	{"Vibra", NULL, "VIBRA"},
+	{"DAC33 interconnect", NULL, "PREDRIVEL"},
+
+	{"DIGIMIC0", NULL, "Digital Mic"},
+	{"Digital Mic", NULL, "Mic Bias 1"},
+
+	{"HSMIC", NULL, "Headset Mic"},
+	{"Headset Mic", NULL, "Headset Mic Bias"},
+
+	{"AUXL", NULL, "FMRX Left Line-in"},
+	{"AUXR", NULL, "FMRX Right Line-in"},
+};
+
+/* Pre DAC routings for the twl4030 codec */
+static const char *twl4030_predacl1_texts[] = {
+	"SDRL1", "SDRM1", "SDRL2", "SDRM2",
+};
+static const char *twl4030_predacr1_texts[] = {
+	"SDRR1", "SDRM1", "SDRR2", "SDRM2"
+};
+static const char *twl4030_predacl2_texts[] = {"SDRL2", "SDRM2"};
+static const char *twl4030_predacr2_texts[] = {"SDRR2", "SDRM2"};
+
+static const struct soc_enum twl4030_predacl1_enum =
+	SOC_ENUM_SINGLE(TWL4030_REG_RX_PATH_SEL, 2,
+			ARRAY_SIZE(twl4030_predacl1_texts),
+			twl4030_predacl1_texts);
+
+static const struct soc_enum twl4030_predacr1_enum =
+	SOC_ENUM_SINGLE(TWL4030_REG_RX_PATH_SEL, 0,
+			ARRAY_SIZE(twl4030_predacr1_texts),
+			twl4030_predacr1_texts);
+
+static const struct soc_enum twl4030_predacl2_enum =
+	SOC_ENUM_SINGLE(TWL4030_REG_RX_PATH_SEL, 5,
+			ARRAY_SIZE(twl4030_predacl2_texts),
+			twl4030_predacl2_texts);
+
+static const struct soc_enum twl4030_predacr2_enum =
+	SOC_ENUM_SINGLE(TWL4030_REG_RX_PATH_SEL, 4,
+			ARRAY_SIZE(twl4030_predacr2_texts),
+			twl4030_predacr2_texts);
+
+static const struct snd_kcontrol_new dfl61twl_controls[] = {
+	/* Mux controls before the DACs */
+	SOC_ENUM("DACL1 Playback Mux", twl4030_predacl1_enum),
+	SOC_ENUM("DACR1 Playback Mux", twl4030_predacr1_enum),
+	SOC_ENUM("DACL2 Playback Mux", twl4030_predacl2_enum),
+	SOC_ENUM("DACR2 Playback Mux", twl4030_predacr2_enum),
+};
+
+static int dfl61twl_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+	int r;
+
+	/* Create jack for accessory reporting */
+	r = snd_soc_card_jack_new(&dfl61twl_sound_card, "Jack",
+				JACK_REPORT_MASK , &dfl61_jack, NULL, 0);
+	if (r) {
+		pr_err("Failed to add Jack\n");
+		return r;
+	}
+
+	snd_soc_add_codec_controls(rtd->codec, dfl61twl_controls,
+				ARRAY_SIZE(dfl61twl_controls));
+
+	if (omap_mcbsp_st_add_controls(rtd, 3))
+		dev_dbg(rtd->codec->dev, "Unable to set Sidetone for McBSP3\n");
+
+	mcbsp->dma_op_mode = MCBSP_DMA_MODE_THRESHOLD;
+
+	return 0;
+}
+
+static struct snd_soc_ops dfl61twl_ops = {
+	.hw_params = dfl61twl_hw_params,
+};
+
+/* TLV320DAC33 */
+static int dfl61dac33_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	int r;
+
+	/* Set codec DAI configuration */
+	r = snd_soc_dai_set_fmt(rtd->codec_dai,
+				SND_SOC_DAIFMT_LEFT_J |
+				SND_SOC_DAIFMT_NB_NF |
+				SND_SOC_DAIFMT_CBM_CFM);
+	if (r < 0) {
+		pr_err("Can't set codec DAI configuration for tlv320dac33: %d\n", r);
+		return r;
+	}
+
+	/* Set cpu DAI configuration */
+	r = snd_soc_dai_set_fmt(rtd->cpu_dai,
+				SND_SOC_DAIFMT_LEFT_J |
+				SND_SOC_DAIFMT_NB_NF |
+				SND_SOC_DAIFMT_CBM_CFM);
+	if (r < 0) {
+		pr_err("Can't set cpu DAI configuration for tlv320dac33: %d\n", r);
+		return r;
+	}
+
+	/* Set the codec system clock for DAC and ADC */
+	r = snd_soc_dai_set_sysclk(rtd->codec_dai, TLV320DAC33_SLEEPCLK, 32768,
+					    SND_SOC_CLOCK_IN);
+	if (r < 0) {
+		pr_err("Can't set codec system clock for tlv320dac33\n");
+		return r;
+	}
+
+	return 0;
+}
+
+static int dfl61dac33_interconnect_enable(int enable)
+{
+	struct snd_soc_component *component =
+		find_component(&dfl61dac33_sound_card);
+	struct snd_soc_dapm_context *dapm;
+	bool lock = false;
+
+	if (!component)
+		return -ENODEV;
+
+	dapm = snd_soc_component_get_dapm(component);
+
+	mutex_lock(&dapm->card->dapm_mutex);
+		lock = true;
+
+	if (enable)
+		snd_soc_dapm_enable_pin_unlocked(dapm, "twl4030 interconnect");
+	else
+		snd_soc_dapm_disable_pin_unlocked(dapm, "twl4030 interconnect");
+
+	snd_soc_dapm_sync_unlocked(dapm);
+
+	if (lock)
+		mutex_unlock(&dapm->card->dapm_mutex);
+
+	return 0;
+}
+
+static void dfl61dac33_hp_enable(struct snd_soc_component *component, int enable)
+{
+	struct snd_soc_dapm_context *dapm =
+		snd_soc_component_get_dapm(component);
+
+	snd_soc_dapm_mutex_lock(dapm);
+
+	if (enable) {
+		snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone");
+	} else {
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone");
+	}
+
+	snd_soc_dapm_sync_unlocked(dapm);
+
+	snd_soc_dapm_mutex_unlock(dapm);
+}
+
+int dfl61_request_hp_enable(bool enable)
+{
+	struct snd_soc_component *component =
+		find_component(&dfl61dac33_sound_card);
+
+	if (!component) {
+		pr_err("dfl61-request_hp_enable");
+		return -ENODEV;
+	}
+
+	dfl61dac33_hp_enable(component, enable);
+
+	return 0;
+}
+EXPORT_SYMBOL(dfl61_request_hp_enable);
+
+static const struct snd_kcontrol_new dfl61dac33_controls[] = {
+	SOC_DAPM_PIN_SWITCH("Headphone"),
+};
+
+static const struct snd_soc_dapm_widget dfl61dac33_dapm_widgets[] = {
+	/* Outputs */
+	SND_SOC_DAPM_LINE("FMTX_L Line Out", NULL),
+	SND_SOC_DAPM_LINE("FMTX_R Line Out", NULL),
+	SND_SOC_DAPM_HP("Headphone", NULL),
+	/* Inputs */
+	SND_SOC_DAPM_LINE("twl4030 interconnect", NULL),
+};
+
+static const struct snd_soc_dapm_route dfl61dac33_audio_map[] = {
+	{"Headphone", NULL, "TPA6140A2 HPLEFT"},
+	{"Headphone", NULL, "TPA6140A2 HPRIGHT"},
+	{"TPA6140A2 HPLEFT", NULL, "LEFT_LO"},
+	{"TPA6140A2 HPRIGHT", NULL, "RIGHT_LO"},
+
+	{"FMTX_L Line Out", NULL, "LEFT_LO"},
+	{"FMTX_R Line Out", NULL, "RIGHT_LO"},
+
+	{"LINER", NULL, "twl4030 interconnect"},
+	{"LINEL", NULL, "twl4030 interconnect"},
+};
+
+static int dfl61dac33_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(rtd->codec);
+
+	snd_soc_limit_volume(rtd->card, "TPA6140A2 Headphone Playback Volume", 21);
+
+	snd_soc_dapm_disable_pin(dapm, "twl4030 interconnect");
+
+	if (omap_mcbsp_st_add_controls(rtd, 2))
+		dev_dbg(rtd->codec->dev, "Unable to set Sidetone for McBSP2\n");
+
+	mcbsp->dma_op_mode = MCBSP_DMA_MODE_THRESHOLD;
+
+	return 0;
+}
+
+static struct snd_soc_ops dfl61dac33_ops = {
+	.hw_params = dfl61dac33_hw_params,
+};
+
+/* WL1273 */
+static int dfl61wl1273_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+	mcbsp->dma_op_mode = MCBSP_DMA_MODE_THRESHOLD;
+
+	return 0;
+}
+
+static int dfl61wl1273_startup(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	unsigned int fmt;
+	int r;
+
+	r = wl1273_get_format(rtd->codec, &fmt);
+	if (r < 0) {
+		pr_err("Can't get fmt for wl1273: %d\n", r);
+		return r;
+	}
+
+	/* Set cpu DAI configuration */
+	r = snd_soc_dai_set_fmt(rtd->cpu_dai, fmt);
+	if (r < 0) {
+		pr_err("Can't set cpu DAI configuration for wl1273: %d\n", r);
+		return r;
+	}
+	return 0;
+}
+
+static struct snd_soc_ops dfl61wl1273_ops = {
+	.startup = dfl61wl1273_startup,
+};
+
+static int dfl61wl1273_card_probe(struct snd_soc_card *card)
+{
+	struct dfl61wl1273_audio_pdata *pdata = snd_soc_card_get_drvdata(card);
+
+	gpiod_set_value(pdata->power_gpio, 1);
+	return 0;
+}
+
+
+static int dfl61wl1273_card_remove(struct snd_soc_card *card)
+{
+	struct dfl61wl1273_audio_pdata *pdata = snd_soc_card_get_drvdata(card);
+
+	gpiod_set_value(pdata->power_gpio, 0);
+	return 0;
+}
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link dfl61twl_dai[] = {
+	{
+		.name = "TWL4030",
+		.stream_name = "TWL4030",
+		.cpu_dai_name = "omap-mcbsp.3",
+		.codec_dai_name = "twl4030-hifi",
+		.platform_name = "omap-pcm-audio",
+		.codec_name = "twl4030-codec",
+		.dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF |
+					SND_SOC_DAIFMT_CBM_CFM,
+		.init = dfl61twl_init,
+		.ops = &dfl61twl_ops,
+	},
+};
+
+static struct snd_soc_dai_link dfl61dac33_dai[] = {
+	{
+		.name = "TLV320DAC33",
+		.stream_name = "DAC33",
+		.cpu_dai_name = "omap-mcbsp.2",
+		.codec_dai_name = "tlv320dac33-hifi",
+		.platform_name = "omap-pcm-audio",
+		.codec_name = "tlv320dac33-codec.1-0019",
+		.init = dfl61dac33_init,
+		.ops = &dfl61dac33_ops,
+	},
+};
+
+static struct snd_soc_aux_dev dfl61dac33_aux_dev[] = {
+	{
+		.name = "TPA6140A2",
+		.codec_name = "tpa6130a2.1-0060",
+	},
+};
+
+static struct snd_soc_codec_conf dfl61dac33_codec_conf[] = {
+	{
+		.dev_name = "tpa6130a2.2-0060",
+		.name_prefix = "TPA6140A2",
+	},
+};
+
+static struct snd_soc_dai_link dfl61wl1273_dai[] = {
+	{
+		.name = "BT/FM PCM",
+		.stream_name = "BT/FM Stream",
+		.cpu_dai_name = "omap-mcbsp.4",
+		.codec_dai_name = "wl1273-fm",
+		.platform_name = "omap-pcm-audio",
+		.codec_name = "wl1273-codec",
+		.init = dfl61wl1273_init,
+		.ops = &dfl61wl1273_ops,
+	},
+};
+
+/* Audio cards */
+static struct snd_soc_card dfl61twl_sound_card = {
+	.name = "dfl61-twl4030",
+	.owner = THIS_MODULE,
+	.dai_link = dfl61twl_dai,
+	.num_links = ARRAY_SIZE(dfl61twl_dai),
+	.fully_routed = true,
+	.dapm_widgets = dfl61twl_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(dfl61twl_dapm_widgets),
+	.dapm_routes = dfl61twl_audio_map,
+	.num_dapm_routes = ARRAY_SIZE(dfl61twl_audio_map),
+};
+
+static struct snd_soc_card dfl61dac33_sound_card = {
+	.name = "dfl61-dac33",
+	.owner = THIS_MODULE,
+	.dai_link = dfl61dac33_dai,
+	.num_links = ARRAY_SIZE(dfl61dac33_dai),
+	.aux_dev = dfl61dac33_aux_dev,
+	.num_aux_devs = ARRAY_SIZE(dfl61dac33_aux_dev),
+	.codec_conf = dfl61dac33_codec_conf,
+	.num_configs = ARRAY_SIZE(dfl61dac33_codec_conf),
+	.fully_routed = true,
+	.controls = dfl61dac33_controls,
+	.num_controls = ARRAY_SIZE(dfl61dac33_controls),
+	.dapm_widgets = dfl61dac33_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(dfl61dac33_dapm_widgets),
+	.dapm_routes = dfl61dac33_audio_map,
+	.num_dapm_routes = ARRAY_SIZE(dfl61dac33_audio_map),
+};
+
+static struct snd_soc_card dfl61wl1273_sound_card = {
+	.name = "dfl61-wl1273",
+	.owner = THIS_MODULE,
+	.probe = dfl61wl1273_card_probe,
+	.remove = dfl61wl1273_card_remove,
+	.dai_link = dfl61wl1273_dai,
+	.num_links = ARRAY_SIZE(dfl61wl1273_dai),
+	.fully_routed = true,
+};
+
+static int n9_soc_probe(struct platform_device *pdev)
+{
+	struct dfl61twl_audio_pdata *pdata_twl;
+	struct dfl61wl1273_audio_pdata *pdata_wl1273;
+	struct device_node *np = pdev->dev.of_node;
+	struct snd_soc_card *card_twl = &dfl61twl_sound_card;
+	struct snd_soc_card *card_dac33 = &dfl61dac33_sound_card;
+	struct snd_soc_card *card_wl1273 = &dfl61wl1273_sound_card;
+	int err;
+
+	if (!(machine_is_nokia_rm696() || machine_is_nokia_rm680())
+		&& !(of_machine_is_compatible("nokia,omap3-n9")
+		|| of_machine_is_compatible("nokia,omap3-n950")))
+		return -ENODEV;
+
+	card_twl->dev = &pdev->dev;
+	card_dac33->dev = &pdev->dev;
+	card_wl1273->dev = &pdev->dev;
+
+	if (np) {
+		struct device_node *dai_node;
+
+		dai_node = of_parse_phandle(np, "nokia,twl4030-cpu-dai", 0);
+		if (!dai_node) {
+			dev_err(&pdev->dev, "McBSP node for TWL4030 is not provided\n");
+			return -EINVAL;
+		}
+		dfl61twl_dai[0].cpu_dai_name = NULL;
+		dfl61twl_dai[0].platform_name = NULL;
+		dfl61twl_dai[0].cpu_of_node = dai_node;
+		dfl61twl_dai[0].platform_of_node = dai_node;
+
+		dai_node = of_parse_phandle(np, "nokia,tlv320dac33-cpu-dai", 0);
+		if (!dai_node) {
+			dev_err(&pdev->dev, "McBSP node for TLV320DAC33 is not provided\n");
+			return -EINVAL;
+		}
+		dfl61dac33_dai[0].cpu_dai_name = NULL;
+		dfl61dac33_dai[0].platform_name = NULL;
+		dfl61dac33_dai[0].cpu_of_node = dai_node;
+		dfl61dac33_dai[0].platform_of_node = dai_node;
+
+		dai_node = of_parse_phandle(np, "nokia,wl1273-cpu-dai", 0);
+		if (!dai_node) {
+			dev_err(&pdev->dev, "McBSP node for WL1273 is not provided\n");
+			return -EINVAL;
+		}
+		dfl61wl1273_dai[0].cpu_dai_name = NULL;
+		dfl61wl1273_dai[0].platform_name = NULL;
+		dfl61wl1273_dai[0].cpu_of_node = dai_node;
+		dfl61wl1273_dai[0].platform_of_node = dai_node;
+
+		dai_node = of_parse_phandle(np, "nokia,twl4030-codec", 0);
+		if (!dai_node) {
+			dev_err(&pdev->dev, "TWL4030 codec node is not provided\n");
+			return -EINVAL;
+		}
+		dfl61twl_dai[0].codec_name = NULL;
+		dfl61twl_dai[0].codec_of_node = dai_node;
+
+		dai_node = of_parse_phandle(np, "nokia,tlv320dac33-codec", 0);
+		if (!dai_node) {
+			dev_err(&pdev->dev, "TLV320DAC33 codec node is not provided\n");
+			return -EINVAL;
+		}
+		dfl61dac33_dai[0].codec_name = NULL;
+		dfl61dac33_dai[0].codec_of_node = dai_node;
+
+		dai_node = of_parse_phandle(np, "nokia,headphone-amplifier", 0);
+		if (!dai_node) {
+			dev_err(&pdev->dev, "Headphone amplifier node is not provided\n");
+			return -EINVAL;
+		}
+		dfl61dac33_aux_dev[0].codec_name = NULL;
+		dfl61dac33_aux_dev[0].codec_of_node = dai_node;
+		dfl61dac33_codec_conf[0].dev_name = NULL;
+		dfl61dac33_codec_conf[0].of_node = dai_node;
+
+		dai_node = of_parse_phandle(np, "nokia,wl1273-codec", 0);
+		if (!dai_node) {
+			dev_err(&pdev->dev, "WL1273 codec node is not provided\n");
+			return -EINVAL;
+		}
+		dfl61wl1273_dai[0].codec_name = NULL;
+		dfl61wl1273_dai[0].codec_of_node = dai_node;
+	}
+
+	pdata_twl = devm_kzalloc(&pdev->dev, sizeof(*pdata_twl), GFP_KERNEL);
+	if (pdata_twl == NULL) {
+		dev_err(card_twl->dev, "failed to create private data for twl4030\n");
+		return -ENOMEM;
+	}
+	snd_soc_card_set_drvdata(card_twl, pdata_twl);
+
+	pdata_twl->speaker_amp_gpio = devm_gpiod_get(card_twl->dev,
+						     "speaker-amplifier",
+						     GPIOD_OUT_LOW);
+	if (IS_ERR(pdata_twl->speaker_amp_gpio)) {
+		dev_err(card_twl->dev, "could not get speaker enable gpio\n");
+		return PTR_ERR(pdata_twl->speaker_amp_gpio);
+	}
+
+	pdata_wl1273 = devm_kzalloc(&pdev->dev, sizeof(*pdata_wl1273), GFP_KERNEL);
+	if (pdata_wl1273 == NULL) {
+		dev_err(card_wl1273->dev, "failed to create private data for wl1273\n");
+		return -ENOMEM;
+	}
+	snd_soc_card_set_drvdata(card_wl1273, pdata_wl1273);
+
+	pdata_wl1273->power_gpio = devm_gpiod_get(card_wl1273->dev,
+						  "wl1273-power",
+						  GPIOD_OUT_LOW);
+	if (IS_ERR(pdata_wl1273->power_gpio)) {
+		dev_err(card_wl1273->dev, "could not get wl1273 enable gpio\n");
+		return PTR_ERR(pdata_wl1273->power_gpio);
+	}
+
+	err = devm_snd_soc_register_card(&pdev->dev, card_twl);
+	if (err < 0) {
+		dev_err(card_twl->dev, "failed to register twl4030 card: %d\n", err);
+		return err;
+	}
+
+	err = devm_snd_soc_register_card(&pdev->dev, card_dac33);
+	if (err < 0) {
+		dev_err(card_dac33->dev, "failed to register tlv320dac33 card: %d\n", err);
+		return err;
+	}
+
+
+
+	err = devm_snd_soc_register_card(&pdev->dev, card_wl1273);
+	if (err < 0) {
+		dev_err(card_wl1273->dev, "failed to register wl1273 card\n");
+		return err;
+	}
+
+	return 0;
+}
+
+static const struct of_device_id n9_audio_of_match[] = {
+	{ .compatible = "nokia,n9-audio", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, n9_audio_of_match);
+
+static struct platform_driver n9_soc_driver = {
+	.driver = {
+		.name = "n9-audio",
+		.of_match_table = of_match_ptr(n9_audio_of_match),
+	},
+	.probe = n9_soc_probe,
+};
+
+module_platform_driver(n9_soc_driver);
+
+MODULE_AUTHOR("Nokia Corporation");
+MODULE_DESCRIPTION("ALSA SoC Nokia N9/N950");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:n9-audio");

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



WARNING: multiple messages have this Message-ID (diff)
From: pavel@ucw.cz (Pavel Machek)
To: linux-arm-kernel@lists.infradead.org
Subject: [rfc] Sound support for n9
Date: Fri, 12 Jan 2018 16:00:44 +0100	[thread overview]
Message-ID: <20180112150044.GA31446@amd> (raw)

Hi!

Binding documentation is pending, and code will need to be updated to
match it.

I guess burst_bclkdiv should be handled as int, not as u8?

SPDX is modern these days.

Anything else that needs to be fixed?

Best regards,
								Pavel


diff --git a/include/sound/n9.h b/include/sound/n9.h
new file mode 100644
index 0000000..b14ddf0
--- /dev/null
+++ b/include/sound/n9.h
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (C) 2009 Nokia
+
+#ifndef _N9_H_
+#define _N9_H_
+
+struct dfl61audio_hsmic_event {
+	void *private;
+	void (*event)(void *priv, bool on);
+};
+
+void dfl61_jack_report(int status);
+int dfl61_request_hsmicbias(bool enable);
+void dfl61_register_hsmic_event_cb(struct dfl61audio_hsmic_event *event);
+int dfl61_request_hp_enable(bool enable);
+#endif
diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c
index 5b94a15..3823bcc 100644
--- a/sound/soc/codecs/tlv320dac33.c
+++ b/sound/soc/codecs/tlv320dac33.c
@@ -30,6 +32,7 @@
 #include <linux/interrupt.h>
 #include <linux/gpio.h>
 #include <linux/regulator/consumer.h>
+#include <linux/of_gpio.h>
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -1484,16 +1487,11 @@ static struct snd_soc_dai_driver dac33_dai = {
 static int dac33_i2c_probe(struct i2c_client *client,
 			   const struct i2c_device_id *id)
 {
-	struct tlv320dac33_platform_data *pdata;
+	struct tlv320dac33_platform_data *pdata = client->dev.platform_data;
 	struct tlv320dac33_priv *dac33;
+	struct device_node *np = client->dev.of_node;
 	int ret, i;
 
-	if (client->dev.platform_data == NULL) {
-		dev_err(&client->dev, "Platform data not set\n");
-		return -ENODEV;
-	}
-	pdata = client->dev.platform_data;
-
 	dac33 = devm_kzalloc(&client->dev, sizeof(struct tlv320dac33_priv),
 			     GFP_KERNEL);
 	if (dac33 == NULL)
@@ -1505,10 +1503,26 @@ static int dac33_i2c_probe(struct i2c_client *client,
 
 	i2c_set_clientdata(client, dac33);
 
-	dac33->power_gpio = pdata->power_gpio;
-	dac33->burst_bclkdiv = pdata->burst_bclkdiv;
-	dac33->keep_bclk = pdata->keep_bclk;
-	dac33->mode1_latency = pdata->mode1_latency;
+	if (pdata) {
+		dac33->power_gpio = pdata->power_gpio;
+		dac33->burst_bclkdiv = pdata->burst_bclkdiv;
+		dac33->keep_bclk = pdata->keep_bclk;
+		dac33->mode1_latency = pdata->mode1_latency;
+	} else if (np) {
+		ret = of_get_named_gpio(np, "power-gpio", 0);
+		if (ret >= 0)
+			dac33->power_gpio = ret;
+		else
+			dac33->power_gpio = -1;
+
+		if (of_property_read_bool(np, "keep-bclk"))
+			dac33->keep_bclk = true;
+
+		of_property_read_u8(np, "burst-bclkdiv", &dac33->burst_bclkdiv);
+	} else {
+		dev_err(&client->dev, "Platform data not set\n");
+		return -ENODEV;
+	}
 	if (!dac33->mode1_latency)
 		dac33->mode1_latency = 10000; /* 10ms */
 	dac33->irq = client->irq;
@@ -1574,9 +1588,16 @@ static const struct i2c_device_id tlv320dac33_i2c_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, tlv320dac33_i2c_id);
 
+static const struct of_device_id tlv320dac33_of_match[] = {
+	{ .compatible = "ti,tlv320dac33", },
+	{},
+};
+MODULE_DEVICE_TABLE(i2c, tlv320dac33_of_match);
+
 static struct i2c_driver tlv320dac33_i2c_driver = {
 	.driver = {
 		.name = "tlv320dac33-codec",
+		.of_match_table = of_match_ptr(tlv320dac33_of_match),
 	},
 	.probe		= dac33_i2c_probe,
 	.remove		= dac33_i2c_remove,
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig
index f5451c7..2772414 100644
--- a/sound/soc/omap/Kconfig
+++ b/sound/soc/omap/Kconfig
@@ -47,6 +47,18 @@ config SND_OMAP_SOC_RX51
 	  Say Y if you want to add support for SoC audio on Nokia N900
 	  cellphone.
 
+config SND_OMAP_SOC_N9
+	tristate "SoC Audio support for Nokia N9/N950"
+	depends on SND_OMAP_SOC && ARM && I2C
+	select SND_OMAP_SOC_MCBSP
+	select SND_SOC_TWL4030
+	select SND_SOC_TLV320DAC33
+	select SND_SOC_TPA6130A2
+	select SND_SOC_WL1273 if MFD_WL1273_CORE
+	depends on GPIOLIB
+	help
+	  Say Y if you want to add support for SoC audio on Nokia N9/N950.
+
 config SND_OMAP_SOC_AMS_DELTA
 	tristate "SoC Audio support for Amstrad E3 (Delta) videophone"
 	depends on SND_OMAP_SOC && MACH_AMS_DELTA && TTY
diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile
index a6785dc..07efd21 100644
--- a/sound/soc/omap/Makefile
+++ b/sound/soc/omap/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_SND_OMAP_SOC_HDMI_AUDIO) += snd-soc-omap-hdmi-audio.o
 # OMAP Machine Support
 snd-soc-n810-objs := n810.o
 snd-soc-rx51-objs := rx51.o
+snd-soc-n9-objs := n9.o
 snd-soc-ams-delta-objs := ams-delta.o
 snd-soc-osk5912-objs := osk5912.o
 snd-soc-am3517evm-objs := am3517evm.o
@@ -24,6 +25,7 @@ snd-soc-omap3pandora-objs := omap3pandora.o
 
 obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o
 obj-$(CONFIG_SND_OMAP_SOC_RX51) += snd-soc-rx51.o
+obj-$(CONFIG_SND_OMAP_SOC_N9) += snd-soc-n9.o
 obj-$(CONFIG_SND_OMAP_SOC_AMS_DELTA) += snd-soc-ams-delta.o
 obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o
 obj-$(CONFIG_SND_OMAP_SOC_AM3517EVM) += snd-soc-am3517evm.o
diff --git a/sound/soc/omap/n9.c b/sound/soc/omap/n9.c
new file mode 100644
index 0000000..db61463
--- /dev/null
+++ b/sound/soc/omap/n9.c
@@ -0,0 +1,748 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// n9.c  --  SoC audio for Nokia N9/N950
+//
+// Copyright (C) 2008 - 2009 Nokia Corporation
+//
+// Contact: Peter Ujfalusi <peter.ujfalusi@ti.com>
+//          Eduardo Valentin <eduardo.valentin@nokia.com>
+//          Jarkko Nikula <jarkko.nikula@bitmer.com>
+//
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <linux/platform_data/asoc-ti-mcbsp.h>
+#include <linux/mfd/twl4030-audio.h>
+#include "../codecs/tlv320dac33.h"
+#include "../codecs/tpa6130a2.h"
+#include "../codecs/wl1273.h"
+
+#include <asm/mach-types.h>
+
+#include <sound/n9.h>
+#include "omap-mcbsp.h"
+#include "mcbsp.h"
+
+#define JACK_REPORT_MASK	(SND_JACK_MECHANICAL | SND_JACK_AVOUT | \
+							 SND_JACK_HEADSET)
+
+struct dfl61wl1273_audio_pdata {
+	struct gpio_desc *power_gpio;
+};
+
+struct dfl61twl_audio_pdata {
+	struct gpio_desc *speaker_amp_gpio;
+};
+
+static int dfl61dac33_interconnect_enable(int);
+static struct snd_soc_card dfl61dac33_sound_card;
+static struct snd_soc_card dfl61twl_sound_card;
+static struct snd_soc_jack dfl61_jack;
+static struct dfl61audio_hsmic_event *hsmic_event;
+
+static struct snd_soc_component *find_component(struct snd_soc_card *card) {
+	struct snd_soc_component *component;
+
+	if (list_empty(&card->component_dev_list)) {
+		pr_err("Can't find codec for %s\n", card->name);
+		return NULL;
+	}
+
+	component = list_entry(card->component_dev_list.next,
+			       struct snd_soc_component, card_list);
+
+	return component;
+}
+
+/* TWL4030 */
+void dfl61_jack_report(int status)
+{
+	if (dfl61_jack.card)
+		snd_soc_jack_report(&dfl61_jack, status, JACK_REPORT_MASK);
+	else
+		pr_err("twl4030: Cannot report jack status");
+}
+EXPORT_SYMBOL_GPL(dfl61_jack_report);
+
+int dfl61_request_hsmicbias(bool enable)
+{
+	struct snd_soc_component *component;
+	struct snd_soc_dapm_context *dapm;
+	bool lock = false;
+	int ret;
+
+	if (!dfl61twl_sound_card.instantiated) {
+		pr_warn("twl4030: sound card not instantiated yet");
+		return -EPROBE_DEFER;
+	}
+
+	component = find_component(&dfl61twl_sound_card);
+	if (!component)
+		return -ENODEV;
+
+	dapm = snd_soc_component_get_dapm(component);
+	if (!dapm) {
+		pr_err("twl4030: Cannot set hsmicbias yet");
+		return -ENODEV;
+	}
+
+	mutex_lock(&dfl61twl_sound_card.dapm_mutex);
+	lock = true;
+
+	if (enable)
+		snd_soc_dapm_force_enable_pin_unlocked(dapm, "Headset Mic Bias");
+	else
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Mic Bias");
+
+	ret = snd_soc_dapm_sync_unlocked(dapm);
+
+	if (lock)
+		mutex_unlock(&dfl61twl_sound_card.dapm_mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL(dfl61_request_hsmicbias);
+
+void dfl61_register_hsmic_event_cb(struct dfl61audio_hsmic_event *event)
+{
+	hsmic_event = event;
+}
+EXPORT_SYMBOL(dfl61_register_hsmic_event_cb);
+
+static int dfl61twl_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	unsigned int fmt;
+	int r;
+
+	switch (params_channels(params)) {
+	case 2: /* Stereo I2S mode */
+		fmt =	SND_SOC_DAIFMT_I2S |
+			SND_SOC_DAIFMT_NB_NF |
+			SND_SOC_DAIFMT_CBM_CFM;
+	case 4: /* Four channel TDM mode */
+		fmt =	SND_SOC_DAIFMT_DSP_A |
+			SND_SOC_DAIFMT_IB_NF |
+			SND_SOC_DAIFMT_CBM_CFM;
+		break;
+	default:
+		return -EINVAL;
+	}
+	/* Set codec DAI configuration */
+	r = snd_soc_dai_set_fmt(rtd->codec_dai, fmt);
+	if (r < 0) {
+		pr_err("Can't set codec DAI configuration for twl4030: %d\n", r);
+		return r;
+	}
+
+	/* Set cpu DAI configuration */
+	r = snd_soc_dai_set_fmt(rtd->cpu_dai, fmt);
+	if (r < 0) {
+		pr_err("Can't set cpu DAI configuration for twl4030: %d\n", r);
+		return r;
+	}
+
+	return 0;
+}
+
+static int dfl61twl_spk_event(struct snd_soc_dapm_widget *w,
+			  struct snd_kcontrol *k, int event)
+{
+	struct snd_soc_dapm_context *dapm = w->dapm;
+	struct snd_soc_card *card = dapm->card;
+	struct dfl61twl_audio_pdata *pdata = snd_soc_card_get_drvdata(card);
+
+	gpiod_set_raw_value_cansleep(pdata->speaker_amp_gpio,
+				     !!SND_SOC_DAPM_EVENT_ON(event));
+
+	return 0;
+}
+
+static int dfl61twl_tlv320dac33_event(struct snd_soc_dapm_widget *w,
+			  struct snd_kcontrol *k, int event)
+{
+	int r;
+
+	if (SND_SOC_DAPM_EVENT_ON(event))
+		r = dfl61dac33_interconnect_enable(1);
+	else
+		r = dfl61dac33_interconnect_enable(0);
+
+	return r;
+}
+
+static int dfl61twl_hsmic_event(struct snd_soc_dapm_widget *w,
+				struct snd_kcontrol *k, int event)
+{
+	if (!hsmic_event || !hsmic_event->event)
+		return 0;
+
+	if (SND_SOC_DAPM_EVENT_ON(event))
+		hsmic_event->event(hsmic_event->private, 1);
+	else
+		hsmic_event->event(hsmic_event->private, 0);
+
+	return 0;
+}
+
+/* DAPM widgets and routing for TWL4030 */
+static const struct snd_soc_dapm_widget dfl61twl_dapm_widgets[] = {
+	SND_SOC_DAPM_SPK("Ext Spk", dfl61twl_spk_event),
+	SND_SOC_DAPM_SPK("Earpiece", NULL),
+	SND_SOC_DAPM_SPK("HAC", NULL),
+	SND_SOC_DAPM_SPK("Vibra", NULL),
+	SND_SOC_DAPM_SPK("DAC33 interconnect", dfl61twl_tlv320dac33_event),
+
+	SND_SOC_DAPM_MIC("Digital Mic", NULL),
+	SND_SOC_DAPM_MIC("Headset Mic", dfl61twl_hsmic_event),
+
+	SND_SOC_DAPM_LINE("FMRX Left Line-in", NULL),
+	SND_SOC_DAPM_LINE("FMRX Right Line-in", NULL),
+};
+
+static const struct snd_soc_dapm_route dfl61twl_audio_map[] = {
+	{"Ext Spk", NULL, "PREDRIVER"},
+	{"Earpiece", NULL, "EARPIECE"},
+	{"HAC", NULL, "HFL"},
+	{"Vibra", NULL, "VIBRA"},
+	{"DAC33 interconnect", NULL, "PREDRIVEL"},
+
+	{"DIGIMIC0", NULL, "Digital Mic"},
+	{"Digital Mic", NULL, "Mic Bias 1"},
+
+	{"HSMIC", NULL, "Headset Mic"},
+	{"Headset Mic", NULL, "Headset Mic Bias"},
+
+	{"AUXL", NULL, "FMRX Left Line-in"},
+	{"AUXR", NULL, "FMRX Right Line-in"},
+};
+
+/* Pre DAC routings for the twl4030 codec */
+static const char *twl4030_predacl1_texts[] = {
+	"SDRL1", "SDRM1", "SDRL2", "SDRM2",
+};
+static const char *twl4030_predacr1_texts[] = {
+	"SDRR1", "SDRM1", "SDRR2", "SDRM2"
+};
+static const char *twl4030_predacl2_texts[] = {"SDRL2", "SDRM2"};
+static const char *twl4030_predacr2_texts[] = {"SDRR2", "SDRM2"};
+
+static const struct soc_enum twl4030_predacl1_enum =
+	SOC_ENUM_SINGLE(TWL4030_REG_RX_PATH_SEL, 2,
+			ARRAY_SIZE(twl4030_predacl1_texts),
+			twl4030_predacl1_texts);
+
+static const struct soc_enum twl4030_predacr1_enum =
+	SOC_ENUM_SINGLE(TWL4030_REG_RX_PATH_SEL, 0,
+			ARRAY_SIZE(twl4030_predacr1_texts),
+			twl4030_predacr1_texts);
+
+static const struct soc_enum twl4030_predacl2_enum =
+	SOC_ENUM_SINGLE(TWL4030_REG_RX_PATH_SEL, 5,
+			ARRAY_SIZE(twl4030_predacl2_texts),
+			twl4030_predacl2_texts);
+
+static const struct soc_enum twl4030_predacr2_enum =
+	SOC_ENUM_SINGLE(TWL4030_REG_RX_PATH_SEL, 4,
+			ARRAY_SIZE(twl4030_predacr2_texts),
+			twl4030_predacr2_texts);
+
+static const struct snd_kcontrol_new dfl61twl_controls[] = {
+	/* Mux controls before the DACs */
+	SOC_ENUM("DACL1 Playback Mux", twl4030_predacl1_enum),
+	SOC_ENUM("DACR1 Playback Mux", twl4030_predacr1_enum),
+	SOC_ENUM("DACL2 Playback Mux", twl4030_predacl2_enum),
+	SOC_ENUM("DACR2 Playback Mux", twl4030_predacr2_enum),
+};
+
+static int dfl61twl_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+	int r;
+
+	/* Create jack for accessory reporting */
+	r = snd_soc_card_jack_new(&dfl61twl_sound_card, "Jack",
+				JACK_REPORT_MASK , &dfl61_jack, NULL, 0);
+	if (r) {
+		pr_err("Failed to add Jack\n");
+		return r;
+	}
+
+	snd_soc_add_codec_controls(rtd->codec, dfl61twl_controls,
+				ARRAY_SIZE(dfl61twl_controls));
+
+	if (omap_mcbsp_st_add_controls(rtd, 3))
+		dev_dbg(rtd->codec->dev, "Unable to set Sidetone for McBSP3\n");
+
+	mcbsp->dma_op_mode = MCBSP_DMA_MODE_THRESHOLD;
+
+	return 0;
+}
+
+static struct snd_soc_ops dfl61twl_ops = {
+	.hw_params = dfl61twl_hw_params,
+};
+
+/* TLV320DAC33 */
+static int dfl61dac33_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	int r;
+
+	/* Set codec DAI configuration */
+	r = snd_soc_dai_set_fmt(rtd->codec_dai,
+				SND_SOC_DAIFMT_LEFT_J |
+				SND_SOC_DAIFMT_NB_NF |
+				SND_SOC_DAIFMT_CBM_CFM);
+	if (r < 0) {
+		pr_err("Can't set codec DAI configuration for tlv320dac33: %d\n", r);
+		return r;
+	}
+
+	/* Set cpu DAI configuration */
+	r = snd_soc_dai_set_fmt(rtd->cpu_dai,
+				SND_SOC_DAIFMT_LEFT_J |
+				SND_SOC_DAIFMT_NB_NF |
+				SND_SOC_DAIFMT_CBM_CFM);
+	if (r < 0) {
+		pr_err("Can't set cpu DAI configuration for tlv320dac33: %d\n", r);
+		return r;
+	}
+
+	/* Set the codec system clock for DAC and ADC */
+	r = snd_soc_dai_set_sysclk(rtd->codec_dai, TLV320DAC33_SLEEPCLK, 32768,
+					    SND_SOC_CLOCK_IN);
+	if (r < 0) {
+		pr_err("Can't set codec system clock for tlv320dac33\n");
+		return r;
+	}
+
+	return 0;
+}
+
+static int dfl61dac33_interconnect_enable(int enable)
+{
+	struct snd_soc_component *component =
+		find_component(&dfl61dac33_sound_card);
+	struct snd_soc_dapm_context *dapm;
+	bool lock = false;
+
+	if (!component)
+		return -ENODEV;
+
+	dapm = snd_soc_component_get_dapm(component);
+
+	mutex_lock(&dapm->card->dapm_mutex);
+		lock = true;
+
+	if (enable)
+		snd_soc_dapm_enable_pin_unlocked(dapm, "twl4030 interconnect");
+	else
+		snd_soc_dapm_disable_pin_unlocked(dapm, "twl4030 interconnect");
+
+	snd_soc_dapm_sync_unlocked(dapm);
+
+	if (lock)
+		mutex_unlock(&dapm->card->dapm_mutex);
+
+	return 0;
+}
+
+static void dfl61dac33_hp_enable(struct snd_soc_component *component, int enable)
+{
+	struct snd_soc_dapm_context *dapm =
+		snd_soc_component_get_dapm(component);
+
+	snd_soc_dapm_mutex_lock(dapm);
+
+	if (enable) {
+		snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone");
+	} else {
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone");
+	}
+
+	snd_soc_dapm_sync_unlocked(dapm);
+
+	snd_soc_dapm_mutex_unlock(dapm);
+}
+
+int dfl61_request_hp_enable(bool enable)
+{
+	struct snd_soc_component *component =
+		find_component(&dfl61dac33_sound_card);
+
+	if (!component) {
+		pr_err("dfl61-request_hp_enable");
+		return -ENODEV;
+	}
+
+	dfl61dac33_hp_enable(component, enable);
+
+	return 0;
+}
+EXPORT_SYMBOL(dfl61_request_hp_enable);
+
+static const struct snd_kcontrol_new dfl61dac33_controls[] = {
+	SOC_DAPM_PIN_SWITCH("Headphone"),
+};
+
+static const struct snd_soc_dapm_widget dfl61dac33_dapm_widgets[] = {
+	/* Outputs */
+	SND_SOC_DAPM_LINE("FMTX_L Line Out", NULL),
+	SND_SOC_DAPM_LINE("FMTX_R Line Out", NULL),
+	SND_SOC_DAPM_HP("Headphone", NULL),
+	/* Inputs */
+	SND_SOC_DAPM_LINE("twl4030 interconnect", NULL),
+};
+
+static const struct snd_soc_dapm_route dfl61dac33_audio_map[] = {
+	{"Headphone", NULL, "TPA6140A2 HPLEFT"},
+	{"Headphone", NULL, "TPA6140A2 HPRIGHT"},
+	{"TPA6140A2 HPLEFT", NULL, "LEFT_LO"},
+	{"TPA6140A2 HPRIGHT", NULL, "RIGHT_LO"},
+
+	{"FMTX_L Line Out", NULL, "LEFT_LO"},
+	{"FMTX_R Line Out", NULL, "RIGHT_LO"},
+
+	{"LINER", NULL, "twl4030 interconnect"},
+	{"LINEL", NULL, "twl4030 interconnect"},
+};
+
+static int dfl61dac33_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(rtd->codec);
+
+	snd_soc_limit_volume(rtd->card, "TPA6140A2 Headphone Playback Volume", 21);
+
+	snd_soc_dapm_disable_pin(dapm, "twl4030 interconnect");
+
+	if (omap_mcbsp_st_add_controls(rtd, 2))
+		dev_dbg(rtd->codec->dev, "Unable to set Sidetone for McBSP2\n");
+
+	mcbsp->dma_op_mode = MCBSP_DMA_MODE_THRESHOLD;
+
+	return 0;
+}
+
+static struct snd_soc_ops dfl61dac33_ops = {
+	.hw_params = dfl61dac33_hw_params,
+};
+
+/* WL1273 */
+static int dfl61wl1273_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+	mcbsp->dma_op_mode = MCBSP_DMA_MODE_THRESHOLD;
+
+	return 0;
+}
+
+static int dfl61wl1273_startup(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	unsigned int fmt;
+	int r;
+
+	r = wl1273_get_format(rtd->codec, &fmt);
+	if (r < 0) {
+		pr_err("Can't get fmt for wl1273: %d\n", r);
+		return r;
+	}
+
+	/* Set cpu DAI configuration */
+	r = snd_soc_dai_set_fmt(rtd->cpu_dai, fmt);
+	if (r < 0) {
+		pr_err("Can't set cpu DAI configuration for wl1273: %d\n", r);
+		return r;
+	}
+	return 0;
+}
+
+static struct snd_soc_ops dfl61wl1273_ops = {
+	.startup = dfl61wl1273_startup,
+};
+
+static int dfl61wl1273_card_probe(struct snd_soc_card *card)
+{
+	struct dfl61wl1273_audio_pdata *pdata = snd_soc_card_get_drvdata(card);
+
+	gpiod_set_value(pdata->power_gpio, 1);
+	return 0;
+}
+
+
+static int dfl61wl1273_card_remove(struct snd_soc_card *card)
+{
+	struct dfl61wl1273_audio_pdata *pdata = snd_soc_card_get_drvdata(card);
+
+	gpiod_set_value(pdata->power_gpio, 0);
+	return 0;
+}
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link dfl61twl_dai[] = {
+	{
+		.name = "TWL4030",
+		.stream_name = "TWL4030",
+		.cpu_dai_name = "omap-mcbsp.3",
+		.codec_dai_name = "twl4030-hifi",
+		.platform_name = "omap-pcm-audio",
+		.codec_name = "twl4030-codec",
+		.dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF |
+					SND_SOC_DAIFMT_CBM_CFM,
+		.init = dfl61twl_init,
+		.ops = &dfl61twl_ops,
+	},
+};
+
+static struct snd_soc_dai_link dfl61dac33_dai[] = {
+	{
+		.name = "TLV320DAC33",
+		.stream_name = "DAC33",
+		.cpu_dai_name = "omap-mcbsp.2",
+		.codec_dai_name = "tlv320dac33-hifi",
+		.platform_name = "omap-pcm-audio",
+		.codec_name = "tlv320dac33-codec.1-0019",
+		.init = dfl61dac33_init,
+		.ops = &dfl61dac33_ops,
+	},
+};
+
+static struct snd_soc_aux_dev dfl61dac33_aux_dev[] = {
+	{
+		.name = "TPA6140A2",
+		.codec_name = "tpa6130a2.1-0060",
+	},
+};
+
+static struct snd_soc_codec_conf dfl61dac33_codec_conf[] = {
+	{
+		.dev_name = "tpa6130a2.2-0060",
+		.name_prefix = "TPA6140A2",
+	},
+};
+
+static struct snd_soc_dai_link dfl61wl1273_dai[] = {
+	{
+		.name = "BT/FM PCM",
+		.stream_name = "BT/FM Stream",
+		.cpu_dai_name = "omap-mcbsp.4",
+		.codec_dai_name = "wl1273-fm",
+		.platform_name = "omap-pcm-audio",
+		.codec_name = "wl1273-codec",
+		.init = dfl61wl1273_init,
+		.ops = &dfl61wl1273_ops,
+	},
+};
+
+/* Audio cards */
+static struct snd_soc_card dfl61twl_sound_card = {
+	.name = "dfl61-twl4030",
+	.owner = THIS_MODULE,
+	.dai_link = dfl61twl_dai,
+	.num_links = ARRAY_SIZE(dfl61twl_dai),
+	.fully_routed = true,
+	.dapm_widgets = dfl61twl_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(dfl61twl_dapm_widgets),
+	.dapm_routes = dfl61twl_audio_map,
+	.num_dapm_routes = ARRAY_SIZE(dfl61twl_audio_map),
+};
+
+static struct snd_soc_card dfl61dac33_sound_card = {
+	.name = "dfl61-dac33",
+	.owner = THIS_MODULE,
+	.dai_link = dfl61dac33_dai,
+	.num_links = ARRAY_SIZE(dfl61dac33_dai),
+	.aux_dev = dfl61dac33_aux_dev,
+	.num_aux_devs = ARRAY_SIZE(dfl61dac33_aux_dev),
+	.codec_conf = dfl61dac33_codec_conf,
+	.num_configs = ARRAY_SIZE(dfl61dac33_codec_conf),
+	.fully_routed = true,
+	.controls = dfl61dac33_controls,
+	.num_controls = ARRAY_SIZE(dfl61dac33_controls),
+	.dapm_widgets = dfl61dac33_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(dfl61dac33_dapm_widgets),
+	.dapm_routes = dfl61dac33_audio_map,
+	.num_dapm_routes = ARRAY_SIZE(dfl61dac33_audio_map),
+};
+
+static struct snd_soc_card dfl61wl1273_sound_card = {
+	.name = "dfl61-wl1273",
+	.owner = THIS_MODULE,
+	.probe = dfl61wl1273_card_probe,
+	.remove = dfl61wl1273_card_remove,
+	.dai_link = dfl61wl1273_dai,
+	.num_links = ARRAY_SIZE(dfl61wl1273_dai),
+	.fully_routed = true,
+};
+
+static int n9_soc_probe(struct platform_device *pdev)
+{
+	struct dfl61twl_audio_pdata *pdata_twl;
+	struct dfl61wl1273_audio_pdata *pdata_wl1273;
+	struct device_node *np = pdev->dev.of_node;
+	struct snd_soc_card *card_twl = &dfl61twl_sound_card;
+	struct snd_soc_card *card_dac33 = &dfl61dac33_sound_card;
+	struct snd_soc_card *card_wl1273 = &dfl61wl1273_sound_card;
+	int err;
+
+	if (!(machine_is_nokia_rm696() || machine_is_nokia_rm680())
+		&& !(of_machine_is_compatible("nokia,omap3-n9")
+		|| of_machine_is_compatible("nokia,omap3-n950")))
+		return -ENODEV;
+
+	card_twl->dev = &pdev->dev;
+	card_dac33->dev = &pdev->dev;
+	card_wl1273->dev = &pdev->dev;
+
+	if (np) {
+		struct device_node *dai_node;
+
+		dai_node = of_parse_phandle(np, "nokia,twl4030-cpu-dai", 0);
+		if (!dai_node) {
+			dev_err(&pdev->dev, "McBSP node for TWL4030 is not provided\n");
+			return -EINVAL;
+		}
+		dfl61twl_dai[0].cpu_dai_name = NULL;
+		dfl61twl_dai[0].platform_name = NULL;
+		dfl61twl_dai[0].cpu_of_node = dai_node;
+		dfl61twl_dai[0].platform_of_node = dai_node;
+
+		dai_node = of_parse_phandle(np, "nokia,tlv320dac33-cpu-dai", 0);
+		if (!dai_node) {
+			dev_err(&pdev->dev, "McBSP node for TLV320DAC33 is not provided\n");
+			return -EINVAL;
+		}
+		dfl61dac33_dai[0].cpu_dai_name = NULL;
+		dfl61dac33_dai[0].platform_name = NULL;
+		dfl61dac33_dai[0].cpu_of_node = dai_node;
+		dfl61dac33_dai[0].platform_of_node = dai_node;
+
+		dai_node = of_parse_phandle(np, "nokia,wl1273-cpu-dai", 0);
+		if (!dai_node) {
+			dev_err(&pdev->dev, "McBSP node for WL1273 is not provided\n");
+			return -EINVAL;
+		}
+		dfl61wl1273_dai[0].cpu_dai_name = NULL;
+		dfl61wl1273_dai[0].platform_name = NULL;
+		dfl61wl1273_dai[0].cpu_of_node = dai_node;
+		dfl61wl1273_dai[0].platform_of_node = dai_node;
+
+		dai_node = of_parse_phandle(np, "nokia,twl4030-codec", 0);
+		if (!dai_node) {
+			dev_err(&pdev->dev, "TWL4030 codec node is not provided\n");
+			return -EINVAL;
+		}
+		dfl61twl_dai[0].codec_name = NULL;
+		dfl61twl_dai[0].codec_of_node = dai_node;
+
+		dai_node = of_parse_phandle(np, "nokia,tlv320dac33-codec", 0);
+		if (!dai_node) {
+			dev_err(&pdev->dev, "TLV320DAC33 codec node is not provided\n");
+			return -EINVAL;
+		}
+		dfl61dac33_dai[0].codec_name = NULL;
+		dfl61dac33_dai[0].codec_of_node = dai_node;
+
+		dai_node = of_parse_phandle(np, "nokia,headphone-amplifier", 0);
+		if (!dai_node) {
+			dev_err(&pdev->dev, "Headphone amplifier node is not provided\n");
+			return -EINVAL;
+		}
+		dfl61dac33_aux_dev[0].codec_name = NULL;
+		dfl61dac33_aux_dev[0].codec_of_node = dai_node;
+		dfl61dac33_codec_conf[0].dev_name = NULL;
+		dfl61dac33_codec_conf[0].of_node = dai_node;
+
+		dai_node = of_parse_phandle(np, "nokia,wl1273-codec", 0);
+		if (!dai_node) {
+			dev_err(&pdev->dev, "WL1273 codec node is not provided\n");
+			return -EINVAL;
+		}
+		dfl61wl1273_dai[0].codec_name = NULL;
+		dfl61wl1273_dai[0].codec_of_node = dai_node;
+	}
+
+	pdata_twl = devm_kzalloc(&pdev->dev, sizeof(*pdata_twl), GFP_KERNEL);
+	if (pdata_twl == NULL) {
+		dev_err(card_twl->dev, "failed to create private data for twl4030\n");
+		return -ENOMEM;
+	}
+	snd_soc_card_set_drvdata(card_twl, pdata_twl);
+
+	pdata_twl->speaker_amp_gpio = devm_gpiod_get(card_twl->dev,
+						     "speaker-amplifier",
+						     GPIOD_OUT_LOW);
+	if (IS_ERR(pdata_twl->speaker_amp_gpio)) {
+		dev_err(card_twl->dev, "could not get speaker enable gpio\n");
+		return PTR_ERR(pdata_twl->speaker_amp_gpio);
+	}
+
+	pdata_wl1273 = devm_kzalloc(&pdev->dev, sizeof(*pdata_wl1273), GFP_KERNEL);
+	if (pdata_wl1273 == NULL) {
+		dev_err(card_wl1273->dev, "failed to create private data for wl1273\n");
+		return -ENOMEM;
+	}
+	snd_soc_card_set_drvdata(card_wl1273, pdata_wl1273);
+
+	pdata_wl1273->power_gpio = devm_gpiod_get(card_wl1273->dev,
+						  "wl1273-power",
+						  GPIOD_OUT_LOW);
+	if (IS_ERR(pdata_wl1273->power_gpio)) {
+		dev_err(card_wl1273->dev, "could not get wl1273 enable gpio\n");
+		return PTR_ERR(pdata_wl1273->power_gpio);
+	}
+
+	err = devm_snd_soc_register_card(&pdev->dev, card_twl);
+	if (err < 0) {
+		dev_err(card_twl->dev, "failed to register twl4030 card: %d\n", err);
+		return err;
+	}
+
+	err = devm_snd_soc_register_card(&pdev->dev, card_dac33);
+	if (err < 0) {
+		dev_err(card_dac33->dev, "failed to register tlv320dac33 card: %d\n", err);
+		return err;
+	}
+
+
+
+	err = devm_snd_soc_register_card(&pdev->dev, card_wl1273);
+	if (err < 0) {
+		dev_err(card_wl1273->dev, "failed to register wl1273 card\n");
+		return err;
+	}
+
+	return 0;
+}
+
+static const struct of_device_id n9_audio_of_match[] = {
+	{ .compatible = "nokia,n9-audio", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, n9_audio_of_match);
+
+static struct platform_driver n9_soc_driver = {
+	.driver = {
+		.name = "n9-audio",
+		.of_match_table = of_match_ptr(n9_audio_of_match),
+	},
+	.probe = n9_soc_probe,
+};
+
+module_platform_driver(n9_soc_driver);
+
+MODULE_AUTHOR("Nokia Corporation");
+MODULE_DESCRIPTION("ALSA SoC Nokia N9/N950");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:n9-audio");

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 181 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20180112/d4ae8582/attachment-0001.sig>

WARNING: multiple messages have this Message-ID (diff)
From: Pavel Machek <pavel@ucw.cz>
To: perex@perex.cz, tiwai@suse.com, lgirdwood@gmail.com,
	broonie@kernel.org, peter.ujfalusi@ti.com,
	jarkko.nikula@bitmer.com, bhumirks@gmail.com,
	alsa-devel@alsa-project.org, pali.rohar@gmail.com,
	sre@kernel.org, "kernel list" <linux-kernel@vger.kernel.org>,
	linux-arm-kernel <linux-arm-kernel@lists.infradead.org>,
	linux-omap@vger.kernel.org, tony@atomide.com, khilman@kernel.org,
	aaro.koskinen@iki.fi, ivo.g.dimitrov.75@gmail.com,
	patrikbachan@gmail.com, serge@hallyn.com, abcloriens@gmail.com,
	clayton@craftyguy.net, martijn@brixit.nl,
	sakari.ailus@linux.intel.com,
	"Filip Matijević" <filip.matijevic.pz@gmail.com>
Subject: [rfc] Sound support for n9
Date: Fri, 12 Jan 2018 16:00:44 +0100	[thread overview]
Message-ID: <20180112150044.GA31446@amd> (raw)

[-- Attachment #1: Type: text/plain, Size: 27000 bytes --]

Hi!

Binding documentation is pending, and code will need to be updated to
match it.

I guess burst_bclkdiv should be handled as int, not as u8?

SPDX is modern these days.

Anything else that needs to be fixed?

Best regards,
								Pavel


diff --git a/include/sound/n9.h b/include/sound/n9.h
new file mode 100644
index 0000000..b14ddf0
--- /dev/null
+++ b/include/sound/n9.h
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (C) 2009 Nokia
+
+#ifndef _N9_H_
+#define _N9_H_
+
+struct dfl61audio_hsmic_event {
+	void *private;
+	void (*event)(void *priv, bool on);
+};
+
+void dfl61_jack_report(int status);
+int dfl61_request_hsmicbias(bool enable);
+void dfl61_register_hsmic_event_cb(struct dfl61audio_hsmic_event *event);
+int dfl61_request_hp_enable(bool enable);
+#endif
diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c
index 5b94a15..3823bcc 100644
--- a/sound/soc/codecs/tlv320dac33.c
+++ b/sound/soc/codecs/tlv320dac33.c
@@ -30,6 +32,7 @@
 #include <linux/interrupt.h>
 #include <linux/gpio.h>
 #include <linux/regulator/consumer.h>
+#include <linux/of_gpio.h>
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -1484,16 +1487,11 @@ static struct snd_soc_dai_driver dac33_dai = {
 static int dac33_i2c_probe(struct i2c_client *client,
 			   const struct i2c_device_id *id)
 {
-	struct tlv320dac33_platform_data *pdata;
+	struct tlv320dac33_platform_data *pdata = client->dev.platform_data;
 	struct tlv320dac33_priv *dac33;
+	struct device_node *np = client->dev.of_node;
 	int ret, i;
 
-	if (client->dev.platform_data == NULL) {
-		dev_err(&client->dev, "Platform data not set\n");
-		return -ENODEV;
-	}
-	pdata = client->dev.platform_data;
-
 	dac33 = devm_kzalloc(&client->dev, sizeof(struct tlv320dac33_priv),
 			     GFP_KERNEL);
 	if (dac33 == NULL)
@@ -1505,10 +1503,26 @@ static int dac33_i2c_probe(struct i2c_client *client,
 
 	i2c_set_clientdata(client, dac33);
 
-	dac33->power_gpio = pdata->power_gpio;
-	dac33->burst_bclkdiv = pdata->burst_bclkdiv;
-	dac33->keep_bclk = pdata->keep_bclk;
-	dac33->mode1_latency = pdata->mode1_latency;
+	if (pdata) {
+		dac33->power_gpio = pdata->power_gpio;
+		dac33->burst_bclkdiv = pdata->burst_bclkdiv;
+		dac33->keep_bclk = pdata->keep_bclk;
+		dac33->mode1_latency = pdata->mode1_latency;
+	} else if (np) {
+		ret = of_get_named_gpio(np, "power-gpio", 0);
+		if (ret >= 0)
+			dac33->power_gpio = ret;
+		else
+			dac33->power_gpio = -1;
+
+		if (of_property_read_bool(np, "keep-bclk"))
+			dac33->keep_bclk = true;
+
+		of_property_read_u8(np, "burst-bclkdiv", &dac33->burst_bclkdiv);
+	} else {
+		dev_err(&client->dev, "Platform data not set\n");
+		return -ENODEV;
+	}
 	if (!dac33->mode1_latency)
 		dac33->mode1_latency = 10000; /* 10ms */
 	dac33->irq = client->irq;
@@ -1574,9 +1588,16 @@ static const struct i2c_device_id tlv320dac33_i2c_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, tlv320dac33_i2c_id);
 
+static const struct of_device_id tlv320dac33_of_match[] = {
+	{ .compatible = "ti,tlv320dac33", },
+	{},
+};
+MODULE_DEVICE_TABLE(i2c, tlv320dac33_of_match);
+
 static struct i2c_driver tlv320dac33_i2c_driver = {
 	.driver = {
 		.name = "tlv320dac33-codec",
+		.of_match_table = of_match_ptr(tlv320dac33_of_match),
 	},
 	.probe		= dac33_i2c_probe,
 	.remove		= dac33_i2c_remove,
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig
index f5451c7..2772414 100644
--- a/sound/soc/omap/Kconfig
+++ b/sound/soc/omap/Kconfig
@@ -47,6 +47,18 @@ config SND_OMAP_SOC_RX51
 	  Say Y if you want to add support for SoC audio on Nokia N900
 	  cellphone.
 
+config SND_OMAP_SOC_N9
+	tristate "SoC Audio support for Nokia N9/N950"
+	depends on SND_OMAP_SOC && ARM && I2C
+	select SND_OMAP_SOC_MCBSP
+	select SND_SOC_TWL4030
+	select SND_SOC_TLV320DAC33
+	select SND_SOC_TPA6130A2
+	select SND_SOC_WL1273 if MFD_WL1273_CORE
+	depends on GPIOLIB
+	help
+	  Say Y if you want to add support for SoC audio on Nokia N9/N950.
+
 config SND_OMAP_SOC_AMS_DELTA
 	tristate "SoC Audio support for Amstrad E3 (Delta) videophone"
 	depends on SND_OMAP_SOC && MACH_AMS_DELTA && TTY
diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile
index a6785dc..07efd21 100644
--- a/sound/soc/omap/Makefile
+++ b/sound/soc/omap/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_SND_OMAP_SOC_HDMI_AUDIO) += snd-soc-omap-hdmi-audio.o
 # OMAP Machine Support
 snd-soc-n810-objs := n810.o
 snd-soc-rx51-objs := rx51.o
+snd-soc-n9-objs := n9.o
 snd-soc-ams-delta-objs := ams-delta.o
 snd-soc-osk5912-objs := osk5912.o
 snd-soc-am3517evm-objs := am3517evm.o
@@ -24,6 +25,7 @@ snd-soc-omap3pandora-objs := omap3pandora.o
 
 obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o
 obj-$(CONFIG_SND_OMAP_SOC_RX51) += snd-soc-rx51.o
+obj-$(CONFIG_SND_OMAP_SOC_N9) += snd-soc-n9.o
 obj-$(CONFIG_SND_OMAP_SOC_AMS_DELTA) += snd-soc-ams-delta.o
 obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o
 obj-$(CONFIG_SND_OMAP_SOC_AM3517EVM) += snd-soc-am3517evm.o
diff --git a/sound/soc/omap/n9.c b/sound/soc/omap/n9.c
new file mode 100644
index 0000000..db61463
--- /dev/null
+++ b/sound/soc/omap/n9.c
@@ -0,0 +1,748 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// n9.c  --  SoC audio for Nokia N9/N950
+//
+// Copyright (C) 2008 - 2009 Nokia Corporation
+//
+// Contact: Peter Ujfalusi <peter.ujfalusi@ti.com>
+//          Eduardo Valentin <eduardo.valentin@nokia.com>
+//          Jarkko Nikula <jarkko.nikula@bitmer.com>
+//
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <linux/platform_data/asoc-ti-mcbsp.h>
+#include <linux/mfd/twl4030-audio.h>
+#include "../codecs/tlv320dac33.h"
+#include "../codecs/tpa6130a2.h"
+#include "../codecs/wl1273.h"
+
+#include <asm/mach-types.h>
+
+#include <sound/n9.h>
+#include "omap-mcbsp.h"
+#include "mcbsp.h"
+
+#define JACK_REPORT_MASK	(SND_JACK_MECHANICAL | SND_JACK_AVOUT | \
+							 SND_JACK_HEADSET)
+
+struct dfl61wl1273_audio_pdata {
+	struct gpio_desc *power_gpio;
+};
+
+struct dfl61twl_audio_pdata {
+	struct gpio_desc *speaker_amp_gpio;
+};
+
+static int dfl61dac33_interconnect_enable(int);
+static struct snd_soc_card dfl61dac33_sound_card;
+static struct snd_soc_card dfl61twl_sound_card;
+static struct snd_soc_jack dfl61_jack;
+static struct dfl61audio_hsmic_event *hsmic_event;
+
+static struct snd_soc_component *find_component(struct snd_soc_card *card) {
+	struct snd_soc_component *component;
+
+	if (list_empty(&card->component_dev_list)) {
+		pr_err("Can't find codec for %s\n", card->name);
+		return NULL;
+	}
+
+	component = list_entry(card->component_dev_list.next,
+			       struct snd_soc_component, card_list);
+
+	return component;
+}
+
+/* TWL4030 */
+void dfl61_jack_report(int status)
+{
+	if (dfl61_jack.card)
+		snd_soc_jack_report(&dfl61_jack, status, JACK_REPORT_MASK);
+	else
+		pr_err("twl4030: Cannot report jack status");
+}
+EXPORT_SYMBOL_GPL(dfl61_jack_report);
+
+int dfl61_request_hsmicbias(bool enable)
+{
+	struct snd_soc_component *component;
+	struct snd_soc_dapm_context *dapm;
+	bool lock = false;
+	int ret;
+
+	if (!dfl61twl_sound_card.instantiated) {
+		pr_warn("twl4030: sound card not instantiated yet");
+		return -EPROBE_DEFER;
+	}
+
+	component = find_component(&dfl61twl_sound_card);
+	if (!component)
+		return -ENODEV;
+
+	dapm = snd_soc_component_get_dapm(component);
+	if (!dapm) {
+		pr_err("twl4030: Cannot set hsmicbias yet");
+		return -ENODEV;
+	}
+
+	mutex_lock(&dfl61twl_sound_card.dapm_mutex);
+	lock = true;
+
+	if (enable)
+		snd_soc_dapm_force_enable_pin_unlocked(dapm, "Headset Mic Bias");
+	else
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Mic Bias");
+
+	ret = snd_soc_dapm_sync_unlocked(dapm);
+
+	if (lock)
+		mutex_unlock(&dfl61twl_sound_card.dapm_mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL(dfl61_request_hsmicbias);
+
+void dfl61_register_hsmic_event_cb(struct dfl61audio_hsmic_event *event)
+{
+	hsmic_event = event;
+}
+EXPORT_SYMBOL(dfl61_register_hsmic_event_cb);
+
+static int dfl61twl_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	unsigned int fmt;
+	int r;
+
+	switch (params_channels(params)) {
+	case 2: /* Stereo I2S mode */
+		fmt =	SND_SOC_DAIFMT_I2S |
+			SND_SOC_DAIFMT_NB_NF |
+			SND_SOC_DAIFMT_CBM_CFM;
+	case 4: /* Four channel TDM mode */
+		fmt =	SND_SOC_DAIFMT_DSP_A |
+			SND_SOC_DAIFMT_IB_NF |
+			SND_SOC_DAIFMT_CBM_CFM;
+		break;
+	default:
+		return -EINVAL;
+	}
+	/* Set codec DAI configuration */
+	r = snd_soc_dai_set_fmt(rtd->codec_dai, fmt);
+	if (r < 0) {
+		pr_err("Can't set codec DAI configuration for twl4030: %d\n", r);
+		return r;
+	}
+
+	/* Set cpu DAI configuration */
+	r = snd_soc_dai_set_fmt(rtd->cpu_dai, fmt);
+	if (r < 0) {
+		pr_err("Can't set cpu DAI configuration for twl4030: %d\n", r);
+		return r;
+	}
+
+	return 0;
+}
+
+static int dfl61twl_spk_event(struct snd_soc_dapm_widget *w,
+			  struct snd_kcontrol *k, int event)
+{
+	struct snd_soc_dapm_context *dapm = w->dapm;
+	struct snd_soc_card *card = dapm->card;
+	struct dfl61twl_audio_pdata *pdata = snd_soc_card_get_drvdata(card);
+
+	gpiod_set_raw_value_cansleep(pdata->speaker_amp_gpio,
+				     !!SND_SOC_DAPM_EVENT_ON(event));
+
+	return 0;
+}
+
+static int dfl61twl_tlv320dac33_event(struct snd_soc_dapm_widget *w,
+			  struct snd_kcontrol *k, int event)
+{
+	int r;
+
+	if (SND_SOC_DAPM_EVENT_ON(event))
+		r = dfl61dac33_interconnect_enable(1);
+	else
+		r = dfl61dac33_interconnect_enable(0);
+
+	return r;
+}
+
+static int dfl61twl_hsmic_event(struct snd_soc_dapm_widget *w,
+				struct snd_kcontrol *k, int event)
+{
+	if (!hsmic_event || !hsmic_event->event)
+		return 0;
+
+	if (SND_SOC_DAPM_EVENT_ON(event))
+		hsmic_event->event(hsmic_event->private, 1);
+	else
+		hsmic_event->event(hsmic_event->private, 0);
+
+	return 0;
+}
+
+/* DAPM widgets and routing for TWL4030 */
+static const struct snd_soc_dapm_widget dfl61twl_dapm_widgets[] = {
+	SND_SOC_DAPM_SPK("Ext Spk", dfl61twl_spk_event),
+	SND_SOC_DAPM_SPK("Earpiece", NULL),
+	SND_SOC_DAPM_SPK("HAC", NULL),
+	SND_SOC_DAPM_SPK("Vibra", NULL),
+	SND_SOC_DAPM_SPK("DAC33 interconnect", dfl61twl_tlv320dac33_event),
+
+	SND_SOC_DAPM_MIC("Digital Mic", NULL),
+	SND_SOC_DAPM_MIC("Headset Mic", dfl61twl_hsmic_event),
+
+	SND_SOC_DAPM_LINE("FMRX Left Line-in", NULL),
+	SND_SOC_DAPM_LINE("FMRX Right Line-in", NULL),
+};
+
+static const struct snd_soc_dapm_route dfl61twl_audio_map[] = {
+	{"Ext Spk", NULL, "PREDRIVER"},
+	{"Earpiece", NULL, "EARPIECE"},
+	{"HAC", NULL, "HFL"},
+	{"Vibra", NULL, "VIBRA"},
+	{"DAC33 interconnect", NULL, "PREDRIVEL"},
+
+	{"DIGIMIC0", NULL, "Digital Mic"},
+	{"Digital Mic", NULL, "Mic Bias 1"},
+
+	{"HSMIC", NULL, "Headset Mic"},
+	{"Headset Mic", NULL, "Headset Mic Bias"},
+
+	{"AUXL", NULL, "FMRX Left Line-in"},
+	{"AUXR", NULL, "FMRX Right Line-in"},
+};
+
+/* Pre DAC routings for the twl4030 codec */
+static const char *twl4030_predacl1_texts[] = {
+	"SDRL1", "SDRM1", "SDRL2", "SDRM2",
+};
+static const char *twl4030_predacr1_texts[] = {
+	"SDRR1", "SDRM1", "SDRR2", "SDRM2"
+};
+static const char *twl4030_predacl2_texts[] = {"SDRL2", "SDRM2"};
+static const char *twl4030_predacr2_texts[] = {"SDRR2", "SDRM2"};
+
+static const struct soc_enum twl4030_predacl1_enum =
+	SOC_ENUM_SINGLE(TWL4030_REG_RX_PATH_SEL, 2,
+			ARRAY_SIZE(twl4030_predacl1_texts),
+			twl4030_predacl1_texts);
+
+static const struct soc_enum twl4030_predacr1_enum =
+	SOC_ENUM_SINGLE(TWL4030_REG_RX_PATH_SEL, 0,
+			ARRAY_SIZE(twl4030_predacr1_texts),
+			twl4030_predacr1_texts);
+
+static const struct soc_enum twl4030_predacl2_enum =
+	SOC_ENUM_SINGLE(TWL4030_REG_RX_PATH_SEL, 5,
+			ARRAY_SIZE(twl4030_predacl2_texts),
+			twl4030_predacl2_texts);
+
+static const struct soc_enum twl4030_predacr2_enum =
+	SOC_ENUM_SINGLE(TWL4030_REG_RX_PATH_SEL, 4,
+			ARRAY_SIZE(twl4030_predacr2_texts),
+			twl4030_predacr2_texts);
+
+static const struct snd_kcontrol_new dfl61twl_controls[] = {
+	/* Mux controls before the DACs */
+	SOC_ENUM("DACL1 Playback Mux", twl4030_predacl1_enum),
+	SOC_ENUM("DACR1 Playback Mux", twl4030_predacr1_enum),
+	SOC_ENUM("DACL2 Playback Mux", twl4030_predacl2_enum),
+	SOC_ENUM("DACR2 Playback Mux", twl4030_predacr2_enum),
+};
+
+static int dfl61twl_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+	int r;
+
+	/* Create jack for accessory reporting */
+	r = snd_soc_card_jack_new(&dfl61twl_sound_card, "Jack",
+				JACK_REPORT_MASK , &dfl61_jack, NULL, 0);
+	if (r) {
+		pr_err("Failed to add Jack\n");
+		return r;
+	}
+
+	snd_soc_add_codec_controls(rtd->codec, dfl61twl_controls,
+				ARRAY_SIZE(dfl61twl_controls));
+
+	if (omap_mcbsp_st_add_controls(rtd, 3))
+		dev_dbg(rtd->codec->dev, "Unable to set Sidetone for McBSP3\n");
+
+	mcbsp->dma_op_mode = MCBSP_DMA_MODE_THRESHOLD;
+
+	return 0;
+}
+
+static struct snd_soc_ops dfl61twl_ops = {
+	.hw_params = dfl61twl_hw_params,
+};
+
+/* TLV320DAC33 */
+static int dfl61dac33_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	int r;
+
+	/* Set codec DAI configuration */
+	r = snd_soc_dai_set_fmt(rtd->codec_dai,
+				SND_SOC_DAIFMT_LEFT_J |
+				SND_SOC_DAIFMT_NB_NF |
+				SND_SOC_DAIFMT_CBM_CFM);
+	if (r < 0) {
+		pr_err("Can't set codec DAI configuration for tlv320dac33: %d\n", r);
+		return r;
+	}
+
+	/* Set cpu DAI configuration */
+	r = snd_soc_dai_set_fmt(rtd->cpu_dai,
+				SND_SOC_DAIFMT_LEFT_J |
+				SND_SOC_DAIFMT_NB_NF |
+				SND_SOC_DAIFMT_CBM_CFM);
+	if (r < 0) {
+		pr_err("Can't set cpu DAI configuration for tlv320dac33: %d\n", r);
+		return r;
+	}
+
+	/* Set the codec system clock for DAC and ADC */
+	r = snd_soc_dai_set_sysclk(rtd->codec_dai, TLV320DAC33_SLEEPCLK, 32768,
+					    SND_SOC_CLOCK_IN);
+	if (r < 0) {
+		pr_err("Can't set codec system clock for tlv320dac33\n");
+		return r;
+	}
+
+	return 0;
+}
+
+static int dfl61dac33_interconnect_enable(int enable)
+{
+	struct snd_soc_component *component =
+		find_component(&dfl61dac33_sound_card);
+	struct snd_soc_dapm_context *dapm;
+	bool lock = false;
+
+	if (!component)
+		return -ENODEV;
+
+	dapm = snd_soc_component_get_dapm(component);
+
+	mutex_lock(&dapm->card->dapm_mutex);
+		lock = true;
+
+	if (enable)
+		snd_soc_dapm_enable_pin_unlocked(dapm, "twl4030 interconnect");
+	else
+		snd_soc_dapm_disable_pin_unlocked(dapm, "twl4030 interconnect");
+
+	snd_soc_dapm_sync_unlocked(dapm);
+
+	if (lock)
+		mutex_unlock(&dapm->card->dapm_mutex);
+
+	return 0;
+}
+
+static void dfl61dac33_hp_enable(struct snd_soc_component *component, int enable)
+{
+	struct snd_soc_dapm_context *dapm =
+		snd_soc_component_get_dapm(component);
+
+	snd_soc_dapm_mutex_lock(dapm);
+
+	if (enable) {
+		snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone");
+	} else {
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone");
+	}
+
+	snd_soc_dapm_sync_unlocked(dapm);
+
+	snd_soc_dapm_mutex_unlock(dapm);
+}
+
+int dfl61_request_hp_enable(bool enable)
+{
+	struct snd_soc_component *component =
+		find_component(&dfl61dac33_sound_card);
+
+	if (!component) {
+		pr_err("dfl61-request_hp_enable");
+		return -ENODEV;
+	}
+
+	dfl61dac33_hp_enable(component, enable);
+
+	return 0;
+}
+EXPORT_SYMBOL(dfl61_request_hp_enable);
+
+static const struct snd_kcontrol_new dfl61dac33_controls[] = {
+	SOC_DAPM_PIN_SWITCH("Headphone"),
+};
+
+static const struct snd_soc_dapm_widget dfl61dac33_dapm_widgets[] = {
+	/* Outputs */
+	SND_SOC_DAPM_LINE("FMTX_L Line Out", NULL),
+	SND_SOC_DAPM_LINE("FMTX_R Line Out", NULL),
+	SND_SOC_DAPM_HP("Headphone", NULL),
+	/* Inputs */
+	SND_SOC_DAPM_LINE("twl4030 interconnect", NULL),
+};
+
+static const struct snd_soc_dapm_route dfl61dac33_audio_map[] = {
+	{"Headphone", NULL, "TPA6140A2 HPLEFT"},
+	{"Headphone", NULL, "TPA6140A2 HPRIGHT"},
+	{"TPA6140A2 HPLEFT", NULL, "LEFT_LO"},
+	{"TPA6140A2 HPRIGHT", NULL, "RIGHT_LO"},
+
+	{"FMTX_L Line Out", NULL, "LEFT_LO"},
+	{"FMTX_R Line Out", NULL, "RIGHT_LO"},
+
+	{"LINER", NULL, "twl4030 interconnect"},
+	{"LINEL", NULL, "twl4030 interconnect"},
+};
+
+static int dfl61dac33_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(rtd->codec);
+
+	snd_soc_limit_volume(rtd->card, "TPA6140A2 Headphone Playback Volume", 21);
+
+	snd_soc_dapm_disable_pin(dapm, "twl4030 interconnect");
+
+	if (omap_mcbsp_st_add_controls(rtd, 2))
+		dev_dbg(rtd->codec->dev, "Unable to set Sidetone for McBSP2\n");
+
+	mcbsp->dma_op_mode = MCBSP_DMA_MODE_THRESHOLD;
+
+	return 0;
+}
+
+static struct snd_soc_ops dfl61dac33_ops = {
+	.hw_params = dfl61dac33_hw_params,
+};
+
+/* WL1273 */
+static int dfl61wl1273_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+	mcbsp->dma_op_mode = MCBSP_DMA_MODE_THRESHOLD;
+
+	return 0;
+}
+
+static int dfl61wl1273_startup(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	unsigned int fmt;
+	int r;
+
+	r = wl1273_get_format(rtd->codec, &fmt);
+	if (r < 0) {
+		pr_err("Can't get fmt for wl1273: %d\n", r);
+		return r;
+	}
+
+	/* Set cpu DAI configuration */
+	r = snd_soc_dai_set_fmt(rtd->cpu_dai, fmt);
+	if (r < 0) {
+		pr_err("Can't set cpu DAI configuration for wl1273: %d\n", r);
+		return r;
+	}
+	return 0;
+}
+
+static struct snd_soc_ops dfl61wl1273_ops = {
+	.startup = dfl61wl1273_startup,
+};
+
+static int dfl61wl1273_card_probe(struct snd_soc_card *card)
+{
+	struct dfl61wl1273_audio_pdata *pdata = snd_soc_card_get_drvdata(card);
+
+	gpiod_set_value(pdata->power_gpio, 1);
+	return 0;
+}
+
+
+static int dfl61wl1273_card_remove(struct snd_soc_card *card)
+{
+	struct dfl61wl1273_audio_pdata *pdata = snd_soc_card_get_drvdata(card);
+
+	gpiod_set_value(pdata->power_gpio, 0);
+	return 0;
+}
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link dfl61twl_dai[] = {
+	{
+		.name = "TWL4030",
+		.stream_name = "TWL4030",
+		.cpu_dai_name = "omap-mcbsp.3",
+		.codec_dai_name = "twl4030-hifi",
+		.platform_name = "omap-pcm-audio",
+		.codec_name = "twl4030-codec",
+		.dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF |
+					SND_SOC_DAIFMT_CBM_CFM,
+		.init = dfl61twl_init,
+		.ops = &dfl61twl_ops,
+	},
+};
+
+static struct snd_soc_dai_link dfl61dac33_dai[] = {
+	{
+		.name = "TLV320DAC33",
+		.stream_name = "DAC33",
+		.cpu_dai_name = "omap-mcbsp.2",
+		.codec_dai_name = "tlv320dac33-hifi",
+		.platform_name = "omap-pcm-audio",
+		.codec_name = "tlv320dac33-codec.1-0019",
+		.init = dfl61dac33_init,
+		.ops = &dfl61dac33_ops,
+	},
+};
+
+static struct snd_soc_aux_dev dfl61dac33_aux_dev[] = {
+	{
+		.name = "TPA6140A2",
+		.codec_name = "tpa6130a2.1-0060",
+	},
+};
+
+static struct snd_soc_codec_conf dfl61dac33_codec_conf[] = {
+	{
+		.dev_name = "tpa6130a2.2-0060",
+		.name_prefix = "TPA6140A2",
+	},
+};
+
+static struct snd_soc_dai_link dfl61wl1273_dai[] = {
+	{
+		.name = "BT/FM PCM",
+		.stream_name = "BT/FM Stream",
+		.cpu_dai_name = "omap-mcbsp.4",
+		.codec_dai_name = "wl1273-fm",
+		.platform_name = "omap-pcm-audio",
+		.codec_name = "wl1273-codec",
+		.init = dfl61wl1273_init,
+		.ops = &dfl61wl1273_ops,
+	},
+};
+
+/* Audio cards */
+static struct snd_soc_card dfl61twl_sound_card = {
+	.name = "dfl61-twl4030",
+	.owner = THIS_MODULE,
+	.dai_link = dfl61twl_dai,
+	.num_links = ARRAY_SIZE(dfl61twl_dai),
+	.fully_routed = true,
+	.dapm_widgets = dfl61twl_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(dfl61twl_dapm_widgets),
+	.dapm_routes = dfl61twl_audio_map,
+	.num_dapm_routes = ARRAY_SIZE(dfl61twl_audio_map),
+};
+
+static struct snd_soc_card dfl61dac33_sound_card = {
+	.name = "dfl61-dac33",
+	.owner = THIS_MODULE,
+	.dai_link = dfl61dac33_dai,
+	.num_links = ARRAY_SIZE(dfl61dac33_dai),
+	.aux_dev = dfl61dac33_aux_dev,
+	.num_aux_devs = ARRAY_SIZE(dfl61dac33_aux_dev),
+	.codec_conf = dfl61dac33_codec_conf,
+	.num_configs = ARRAY_SIZE(dfl61dac33_codec_conf),
+	.fully_routed = true,
+	.controls = dfl61dac33_controls,
+	.num_controls = ARRAY_SIZE(dfl61dac33_controls),
+	.dapm_widgets = dfl61dac33_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(dfl61dac33_dapm_widgets),
+	.dapm_routes = dfl61dac33_audio_map,
+	.num_dapm_routes = ARRAY_SIZE(dfl61dac33_audio_map),
+};
+
+static struct snd_soc_card dfl61wl1273_sound_card = {
+	.name = "dfl61-wl1273",
+	.owner = THIS_MODULE,
+	.probe = dfl61wl1273_card_probe,
+	.remove = dfl61wl1273_card_remove,
+	.dai_link = dfl61wl1273_dai,
+	.num_links = ARRAY_SIZE(dfl61wl1273_dai),
+	.fully_routed = true,
+};
+
+static int n9_soc_probe(struct platform_device *pdev)
+{
+	struct dfl61twl_audio_pdata *pdata_twl;
+	struct dfl61wl1273_audio_pdata *pdata_wl1273;
+	struct device_node *np = pdev->dev.of_node;
+	struct snd_soc_card *card_twl = &dfl61twl_sound_card;
+	struct snd_soc_card *card_dac33 = &dfl61dac33_sound_card;
+	struct snd_soc_card *card_wl1273 = &dfl61wl1273_sound_card;
+	int err;
+
+	if (!(machine_is_nokia_rm696() || machine_is_nokia_rm680())
+		&& !(of_machine_is_compatible("nokia,omap3-n9")
+		|| of_machine_is_compatible("nokia,omap3-n950")))
+		return -ENODEV;
+
+	card_twl->dev = &pdev->dev;
+	card_dac33->dev = &pdev->dev;
+	card_wl1273->dev = &pdev->dev;
+
+	if (np) {
+		struct device_node *dai_node;
+
+		dai_node = of_parse_phandle(np, "nokia,twl4030-cpu-dai", 0);
+		if (!dai_node) {
+			dev_err(&pdev->dev, "McBSP node for TWL4030 is not provided\n");
+			return -EINVAL;
+		}
+		dfl61twl_dai[0].cpu_dai_name = NULL;
+		dfl61twl_dai[0].platform_name = NULL;
+		dfl61twl_dai[0].cpu_of_node = dai_node;
+		dfl61twl_dai[0].platform_of_node = dai_node;
+
+		dai_node = of_parse_phandle(np, "nokia,tlv320dac33-cpu-dai", 0);
+		if (!dai_node) {
+			dev_err(&pdev->dev, "McBSP node for TLV320DAC33 is not provided\n");
+			return -EINVAL;
+		}
+		dfl61dac33_dai[0].cpu_dai_name = NULL;
+		dfl61dac33_dai[0].platform_name = NULL;
+		dfl61dac33_dai[0].cpu_of_node = dai_node;
+		dfl61dac33_dai[0].platform_of_node = dai_node;
+
+		dai_node = of_parse_phandle(np, "nokia,wl1273-cpu-dai", 0);
+		if (!dai_node) {
+			dev_err(&pdev->dev, "McBSP node for WL1273 is not provided\n");
+			return -EINVAL;
+		}
+		dfl61wl1273_dai[0].cpu_dai_name = NULL;
+		dfl61wl1273_dai[0].platform_name = NULL;
+		dfl61wl1273_dai[0].cpu_of_node = dai_node;
+		dfl61wl1273_dai[0].platform_of_node = dai_node;
+
+		dai_node = of_parse_phandle(np, "nokia,twl4030-codec", 0);
+		if (!dai_node) {
+			dev_err(&pdev->dev, "TWL4030 codec node is not provided\n");
+			return -EINVAL;
+		}
+		dfl61twl_dai[0].codec_name = NULL;
+		dfl61twl_dai[0].codec_of_node = dai_node;
+
+		dai_node = of_parse_phandle(np, "nokia,tlv320dac33-codec", 0);
+		if (!dai_node) {
+			dev_err(&pdev->dev, "TLV320DAC33 codec node is not provided\n");
+			return -EINVAL;
+		}
+		dfl61dac33_dai[0].codec_name = NULL;
+		dfl61dac33_dai[0].codec_of_node = dai_node;
+
+		dai_node = of_parse_phandle(np, "nokia,headphone-amplifier", 0);
+		if (!dai_node) {
+			dev_err(&pdev->dev, "Headphone amplifier node is not provided\n");
+			return -EINVAL;
+		}
+		dfl61dac33_aux_dev[0].codec_name = NULL;
+		dfl61dac33_aux_dev[0].codec_of_node = dai_node;
+		dfl61dac33_codec_conf[0].dev_name = NULL;
+		dfl61dac33_codec_conf[0].of_node = dai_node;
+
+		dai_node = of_parse_phandle(np, "nokia,wl1273-codec", 0);
+		if (!dai_node) {
+			dev_err(&pdev->dev, "WL1273 codec node is not provided\n");
+			return -EINVAL;
+		}
+		dfl61wl1273_dai[0].codec_name = NULL;
+		dfl61wl1273_dai[0].codec_of_node = dai_node;
+	}
+
+	pdata_twl = devm_kzalloc(&pdev->dev, sizeof(*pdata_twl), GFP_KERNEL);
+	if (pdata_twl == NULL) {
+		dev_err(card_twl->dev, "failed to create private data for twl4030\n");
+		return -ENOMEM;
+	}
+	snd_soc_card_set_drvdata(card_twl, pdata_twl);
+
+	pdata_twl->speaker_amp_gpio = devm_gpiod_get(card_twl->dev,
+						     "speaker-amplifier",
+						     GPIOD_OUT_LOW);
+	if (IS_ERR(pdata_twl->speaker_amp_gpio)) {
+		dev_err(card_twl->dev, "could not get speaker enable gpio\n");
+		return PTR_ERR(pdata_twl->speaker_amp_gpio);
+	}
+
+	pdata_wl1273 = devm_kzalloc(&pdev->dev, sizeof(*pdata_wl1273), GFP_KERNEL);
+	if (pdata_wl1273 == NULL) {
+		dev_err(card_wl1273->dev, "failed to create private data for wl1273\n");
+		return -ENOMEM;
+	}
+	snd_soc_card_set_drvdata(card_wl1273, pdata_wl1273);
+
+	pdata_wl1273->power_gpio = devm_gpiod_get(card_wl1273->dev,
+						  "wl1273-power",
+						  GPIOD_OUT_LOW);
+	if (IS_ERR(pdata_wl1273->power_gpio)) {
+		dev_err(card_wl1273->dev, "could not get wl1273 enable gpio\n");
+		return PTR_ERR(pdata_wl1273->power_gpio);
+	}
+
+	err = devm_snd_soc_register_card(&pdev->dev, card_twl);
+	if (err < 0) {
+		dev_err(card_twl->dev, "failed to register twl4030 card: %d\n", err);
+		return err;
+	}
+
+	err = devm_snd_soc_register_card(&pdev->dev, card_dac33);
+	if (err < 0) {
+		dev_err(card_dac33->dev, "failed to register tlv320dac33 card: %d\n", err);
+		return err;
+	}
+
+
+
+	err = devm_snd_soc_register_card(&pdev->dev, card_wl1273);
+	if (err < 0) {
+		dev_err(card_wl1273->dev, "failed to register wl1273 card\n");
+		return err;
+	}
+
+	return 0;
+}
+
+static const struct of_device_id n9_audio_of_match[] = {
+	{ .compatible = "nokia,n9-audio", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, n9_audio_of_match);
+
+static struct platform_driver n9_soc_driver = {
+	.driver = {
+		.name = "n9-audio",
+		.of_match_table = of_match_ptr(n9_audio_of_match),
+	},
+	.probe = n9_soc_probe,
+};
+
+module_platform_driver(n9_soc_driver);
+
+MODULE_AUTHOR("Nokia Corporation");
+MODULE_DESCRIPTION("ALSA SoC Nokia N9/N950");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:n9-audio");

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]

             reply	other threads:[~2018-01-12 15:00 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-01-12 15:00 Pavel Machek [this message]
2018-01-12 15:00 ` [rfc] Sound support for n9 Pavel Machek
2018-01-12 15:00 ` Pavel Machek

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20180112150044.GA31446@amd \
    --to=pavel@ucw.cz \
    --cc=aaro.koskinen@iki.fi \
    --cc=abcloriens@gmail.com \
    --cc=alsa-devel@alsa-project.org \
    --cc=bhumirks@gmail.com \
    --cc=broonie@kernel.org \
    --cc=clayton@craftyguy.net \
    --cc=filip.matijevic.pz@gmail.com \
    --cc=ivo.g.dimitrov.75@gmail.com \
    --cc=jarkko.nikula@bitmer.com \
    --cc=khilman@kernel.org \
    --cc=lgirdwood@gmail.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-omap@vger.kernel.org \
    --cc=martijn@brixit.nl \
    --cc=pali.rohar@gmail.com \
    --cc=patrikbachan@gmail.com \
    --cc=perex@perex.cz \
    --cc=peter.ujfalusi@ti.com \
    --cc=sakari.ailus@linux.intel.com \
    --cc=serge@hallyn.com \
    --cc=sre@kernel.org \
    --cc=tiwai@suse.com \
    --cc=tony@atomide.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.