[RFC 4/4] ASoC: RX-51: Add DT support to sound driver
From: Sebastian Reichel
Date: Sun Oct 27 2013 - 17:25:46 EST
This patch adds device tree support to the Nokia N900 audio driver.
It also removes GPIO defines and gets them from platform data /
device tree, since some GPIO numbers may be different with DT boot.
The binding also changes a helper function in omap-mcbsp, which
is currently only used by the Nokia N900. This change is needed,
because DT instanced omap-mcbsp driver has no valid id assigned.
Last but not least the DT binding is documented.
Signed-off-by: Sebastian Reichel <sre@xxxxxxxxxx>
---
.../devicetree/bindings/sound/nokia,rx51.txt | 28 ++++
.../devicetree/bindings/vendor-prefixes.txt | 1 +
include/sound/rx51.h | 18 +++
sound/soc/omap/omap-mcbsp.c | 5 +-
sound/soc/omap/omap-mcbsp.h | 2 +-
sound/soc/omap/rx51.c | 175 ++++++++++++++++-----
6 files changed, 184 insertions(+), 45 deletions(-)
create mode 100644 Documentation/devicetree/bindings/sound/nokia,rx51.txt
create mode 100644 include/sound/rx51.h
diff --git a/Documentation/devicetree/bindings/sound/nokia,rx51.txt b/Documentation/devicetree/bindings/sound/nokia,rx51.txt
new file mode 100644
index 0000000..4f985ca
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/nokia,rx51.txt
@@ -0,0 +1,28 @@
+* Nokia N900 audio setup
+
+Required properties:
+- compatible: "nokia,rx51-audio"
+- ti,mcbsp: phandle for the McBSP node
+- ti,codec: phandle for the main TLV320AIC3X node
+- ti,codec_aux: phandle for the main TLV320AIC3X auxiliary node
+- ti,headset_amp: phandle for the TPA6130A2 node
+- tvout-selection-gpio: GPIO for tvout selection
+- jack-detection-gpio: GPIO for jack detection
+- eci-sw-gpio: GPIO for ECI SW
+- speaker-amp-gpio: GPIO for Speaker Amp
+
+Example:
+
+sound {
+ compatible = "nokia,rx51-audio";
+
+ ti,mcbsp = <&mcbsp2>;
+ ti,codec = <&tlv320aic3x>;
+ ti,codec_aux = <&tlv320aic3x_aux>;
+ ti,headset_amp = <&tpa6130a2>;
+
+ tvout-selection-gpio = <&gpio2 8 GPIO_ACTIVE_HIGH>; /* 40 */
+ jack-detection-gpio = <&gpio6 17 GPIO_ACTIVE_HIGH>; /* 177 */
+ eci-sw-gpio = <&gpio6 22 GPIO_ACTIVE_HIGH>; /* 182 */
+ speaker-amp-gpio = <&twl_gpio 7 GPIO_ACTIVE_HIGH>;
+};
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 2956800..2cc3dad 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -42,6 +42,7 @@ microchip Microchip Technology Inc.
mosaixtech Mosaix Technologies, Inc.
national National Semiconductor
nintendo Nintendo
+nokia Nokia Corporation
nvidia NVIDIA
nxp NXP Semiconductors
onnn ON Semiconductor Corp.
diff --git a/include/sound/rx51.h b/include/sound/rx51.h
new file mode 100644
index 0000000..190d745
--- /dev/null
+++ b/include/sound/rx51.h
@@ -0,0 +1,18 @@
+/*
+ * Platform data for Nokia RX-51 SoC audio
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __RX51_AUDIO_H__
+#define __RX51_AUDIO_H__
+
+struct rx51_audio_pdata {
+ int tvout_selection_gpio;
+ int jack_detection_gpio;
+ int eci_sw_gpio;
+ int speaker_amp_gpio;
+};
+
+#endif
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c
index 6c19bba..f88e31e 100644
--- a/sound/soc/omap/omap-mcbsp.c
+++ b/sound/soc/omap/omap-mcbsp.c
@@ -691,7 +691,7 @@ OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP" #port " Sidetone Channel 1 Volume", \
OMAP_MCBSP_ST_CONTROLS(2);
OMAP_MCBSP_ST_CONTROLS(3);
-int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd)
+int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd, int port_id)
{
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
@@ -701,7 +701,7 @@ int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd)
return 0;
}
- switch (mcbsp->id) {
+ switch (port_id) {
case 2: /* McBSP 2 */
return snd_soc_add_dai_controls(cpu_dai,
omap_mcbsp2_st_controls,
@@ -711,6 +711,7 @@ int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd)
omap_mcbsp3_st_controls,
ARRAY_SIZE(omap_mcbsp3_st_controls));
default:
+ dev_err(mcbsp->dev, "Port %d not supported\n", port_id);
break;
}
diff --git a/sound/soc/omap/omap-mcbsp.h b/sound/soc/omap/omap-mcbsp.h
index ba8386a..2e3369c 100644
--- a/sound/soc/omap/omap-mcbsp.h
+++ b/sound/soc/omap/omap-mcbsp.h
@@ -39,6 +39,6 @@ enum omap_mcbsp_div {
OMAP_MCBSP_CLKGDV, /* Sample rate generator divider */
};
-int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd);
+int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd, int port_id);
#endif
diff --git a/sound/soc/omap/rx51.c b/sound/soc/omap/rx51.c
index 41f26ae..aa44108 100644
--- a/sound/soc/omap/rx51.c
+++ b/sound/soc/omap/rx51.c
@@ -26,11 +26,13 @@
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
+#include <linux/of_gpio.h>
#include <linux/module.h>
#include <sound/core.h>
#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/soc.h>
+#include <sound/rx51.h>
#include <linux/platform_data/asoc-ti-mcbsp.h>
#include "../codecs/tpa6130a2.h"
@@ -38,15 +40,6 @@
#include "omap-mcbsp.h"
-#define RX51_TVOUT_SEL_GPIO 40
-#define RX51_JACK_DETECT_GPIO 177
-#define RX51_ECI_SW_GPIO 182
-/*
- * REVISIT: TWL4030 GPIO base in RX-51. Now statically defined to 192. This
- * gpio is reserved in arch/arm/mach-omap2/board-rx51-peripherals.c
- */
-#define RX51_SPEAKER_AMP_TWL_GPIO (192 + 7)
-
enum {
RX51_JACK_DISABLED,
RX51_JACK_TVOUT, /* tv-out with stereo output */
@@ -60,6 +53,8 @@ static int rx51_jack_func;
static void rx51_ext_control(struct snd_soc_dapm_context *dapm)
{
+ struct snd_soc_card *card = dapm->card;
+ struct rx51_audio_pdata *pdata = snd_soc_card_get_drvdata(card);
int hp = 0, hs = 0, tvout = 0;
switch (rx51_jack_func) {
@@ -91,7 +86,7 @@ static void rx51_ext_control(struct snd_soc_dapm_context *dapm)
else
snd_soc_dapm_disable_pin(dapm, "HS Mic");
- gpio_set_value(RX51_TVOUT_SEL_GPIO, tvout);
+ gpio_set_value(pdata->tvout_selection_gpio, tvout);
snd_soc_dapm_sync(dapm);
}
@@ -150,10 +145,12 @@ static int rx51_set_spk(struct snd_kcontrol *kcontrol,
static int rx51_spk_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
{
- if (SND_SOC_DAPM_EVENT_ON(event))
- gpio_set_value_cansleep(RX51_SPEAKER_AMP_TWL_GPIO, 1);
- else
- gpio_set_value_cansleep(RX51_SPEAKER_AMP_TWL_GPIO, 0);
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct snd_soc_card *card = dapm->card;
+ struct rx51_audio_pdata *pdata = snd_soc_card_get_drvdata(card);
+
+ gpio_set_value_cansleep(pdata->speaker_amp_gpio,
+ !!SND_SOC_DAPM_EVENT_ON(event));
return 0;
}
@@ -219,7 +216,6 @@ static struct snd_soc_jack rx51_av_jack;
static struct snd_soc_jack_gpio rx51_av_jack_gpios[] = {
{
- .gpio = RX51_JACK_DETECT_GPIO,
.name = "avdet-gpio",
.report = SND_JACK_HEADSET,
.invert = 1,
@@ -286,6 +282,9 @@ static const struct snd_kcontrol_new aic34_rx51_controlsb[] = {
static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_card *card = codec->card;
+ struct rx51_audio_pdata *pdata = snd_soc_card_get_drvdata(card);
+
struct snd_soc_dapm_context *dapm = &codec->dapm;
int err;
@@ -297,8 +296,10 @@ static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd)
/* Add RX-51 specific controls */
err = snd_soc_add_card_controls(rtd->card, aic34_rx51_controls,
ARRAY_SIZE(aic34_rx51_controls));
- if (err < 0)
+ if (err < 0) {
+ dev_err(card->dev, "Failed to add RX-51 specific controls\n");
return err;
+ }
/* Add RX-51 specific widgets */
snd_soc_dapm_new_controls(dapm, aic34_dapm_widgets,
@@ -308,25 +309,39 @@ static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd)
snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
err = tpa6130a2_add_controls(codec);
- if (err < 0)
+ if (err < 0) {
+ dev_err(card->dev, "Failed to add TPA6130A2 controls\n");
return err;
+ }
snd_soc_limit_volume(codec, "TPA6130A2 Headphone Playback Volume", 42);
- err = omap_mcbsp_st_add_controls(rtd);
- if (err < 0)
+ err = omap_mcbsp_st_add_controls(rtd, 2);
+ if (err < 0) {
+ dev_err(card->dev, "Failed to add MCBSP controls\n");
return err;
+ }
/* AV jack detection */
err = snd_soc_jack_new(codec, "AV Jack",
SND_JACK_HEADSET | SND_JACK_VIDEOOUT,
&rx51_av_jack);
- if (err)
+ if (err) {
+ dev_err(card->dev, "Failed to add AV Jack\n");
return err;
+
+ }
+
+ rx51_av_jack_gpios[0].gpio = pdata->jack_detection_gpio;
+
err = snd_soc_jack_add_gpios(&rx51_av_jack,
- ARRAY_SIZE(rx51_av_jack_gpios),
- rx51_av_jack_gpios);
+ ARRAY_SIZE(rx51_av_jack_gpios),
+ rx51_av_jack_gpios);
+ if (err) {
+ dev_err(card->dev, "Failed to add GPIOs\n");
+ return err;
+ }
- return err;
+ return 0;
}
static int rx51_aic34b_init(struct snd_soc_dapm_context *dapm)
@@ -392,37 +407,106 @@ static struct snd_soc_card rx51_sound_card = {
static int rx51_soc_probe(struct platform_device *pdev)
{
+ struct rx51_audio_pdata *pdata = pdev->dev.platform_data;
+ struct device_node *np = pdev->dev.of_node;
struct snd_soc_card *card = &rx51_sound_card;
int err;
if (!machine_is_nokia_rx51() && !of_machine_is_compatible("nokia,omap3-n900"))
return -ENODEV;
- err = gpio_request_one(RX51_TVOUT_SEL_GPIO,
+ card->dev = &pdev->dev;
+
+ if (np) {
+ struct device_node *dai_node;
+
+ pdata = devm_kzalloc(&pdev->dev,
+ sizeof(struct rx51_audio_pdata), GFP_KERNEL);
+
+ if (pdata == NULL) {
+ dev_err(card->dev, "failed to create private data\n");
+ return -ENOMEM;
+ }
+
+ pdata->tvout_selection_gpio =
+ of_get_named_gpio(np, "tvout-selection-gpio", 0);
+ pdata->jack_detection_gpio =
+ of_get_named_gpio(np, "jack-detection-gpio", 0);
+ pdata->eci_sw_gpio = of_get_named_gpio(np, "eci-sw-gpio", 0);
+ pdata->speaker_amp_gpio =
+ of_get_named_gpio(np, "speaker-amp-gpio", 0);
+
+ dai_node = of_parse_phandle(np, "ti,mcbsp", 0);
+ if (!dai_node) {
+ dev_err(&pdev->dev, "McBSP node is not provided\n");
+ return -EINVAL;
+ }
+ rx51_dai[0].cpu_dai_name = NULL;
+ rx51_dai[0].cpu_of_node = dai_node;
+
+ dai_node = of_parse_phandle(np, "ti,codec", 0);
+ if (!dai_node) {
+ dev_err(&pdev->dev, "Codec node is not provided\n");
+ return -EINVAL;
+ }
+ rx51_dai[0].codec_name = NULL;
+ rx51_dai[0].codec_of_node = dai_node;
+
+ dai_node = of_parse_phandle(np, "ti,codec_aux", 0);
+ if (!dai_node) {
+ dev_err(&pdev->dev, "Auxiliary Codec node is not provided\n");
+ return -EINVAL;
+ }
+ rx51_aux_dev[0].codec_name = NULL;
+ rx51_aux_dev[0].codec_of_node = dai_node;
+ rx51_codec_conf[0].dev_name = NULL;
+ rx51_codec_conf[0].of_node = dai_node;
+
+ if (pdata->tvout_selection_gpio == -EPROBE_DEFER ||
+ pdata->jack_detection_gpio == -EPROBE_DEFER ||
+ pdata->eci_sw_gpio == -EPROBE_DEFER ||
+ pdata->speaker_amp_gpio == -EPROBE_DEFER) {
+ devm_kfree(card->dev, pdata);
+ pdev->dev.platform_data = NULL;
+ return -EPROBE_DEFER;
+ }
+
+ pdev->dev.platform_data = pdata;
+ } else if (!pdata) {
+ dev_err(card->dev, "platform data missing");
+ err = -ENODEV;
+ goto error;
+ }
+
+ err = devm_gpio_request_one(card->dev, pdata->tvout_selection_gpio,
GPIOF_DIR_OUT | GPIOF_INIT_LOW, "tvout_sel");
- if (err)
- goto err_gpio_tvout_sel;
- err = gpio_request_one(RX51_ECI_SW_GPIO,
+ if (err) {
+ dev_err(card->dev, "could not setup tvout_sel\n");
+ goto error;
+ }
+ err = devm_gpio_request_one(card->dev, pdata->eci_sw_gpio,
GPIOF_DIR_OUT | GPIOF_INIT_HIGH, "eci_sw");
- if (err)
- goto err_gpio_eci_sw;
-
- card->dev = &pdev->dev;
+ if (err) {
+ dev_err(card->dev, "could not setup eci_sw\n");
+ goto error;
+ }
+ err = devm_gpio_request_one(card->dev, pdata->speaker_amp_gpio,
+ GPIOF_DIR_OUT | GPIOF_INIT_LOW, "speaker_en");
+ if (err) {
+ dev_err(card->dev, "could not setup speaker_en\n");
+ goto error;
+ }
+ snd_soc_card_set_drvdata(card, pdata);
err = snd_soc_register_card(card);
if (err) {
- dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", err);
- goto err_snd;
+ dev_err(card->dev, "snd_soc_register_card failed (%d)\n", err);
+ goto error;
}
return 0;
-err_snd:
+error:
card->dev = NULL;
- gpio_free(RX51_ECI_SW_GPIO);
-err_gpio_eci_sw:
- gpio_free(RX51_TVOUT_SEL_GPIO);
-err_gpio_tvout_sel:
-
return err;
}
@@ -436,16 +520,23 @@ static int rx51_soc_remove(struct platform_device *pdev)
snd_soc_unregister_card(card);
card->dev = NULL;
- gpio_free(RX51_ECI_SW_GPIO);
- gpio_free(RX51_TVOUT_SEL_GPIO);
-
return 0;
}
+#if defined(CONFIG_OF)
+static const struct of_device_id rx51_audio_of_match[] = {
+ { .compatible = "nokia,rx51-audio", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rx51_audio_of_match);
+#endif
+
+
static struct platform_driver rx51_soc_driver = {
.driver = {
.name = "rx51-audio",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(rx51_audio_of_match),
},
.probe = rx51_soc_probe,
.remove = rx51_soc_remove,
--
1.8.4.rc3
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/