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 <[email protected]>
+//          Eduardo Valentin <[email protected]>
+//          Jarkko Nikula <[email protected]>
+//
+
+#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: signature.asc
Description: Digital signature

Reply via email to