diff mbox

[V2,2/2] ASoC: Davinci: machine: Add device tree binding

Message ID 1357293277-25543-3-git-send-email-gururaja.hebbar@ti.com (mailing list archive)
State New, archived
Headers show

Commit Message

Hebbar, Gururaja Jan. 4, 2013, 9:54 a.m. UTC
From: "Hebbar, Gururaja" <gururaja.hebbar@ti.com>

Device tree support for Davinci Machine driver

When the board boots with device tree, the driver will receive card,
codec, dai interface details (like the card name, DAPM routing map,
phandle for the audio components described in the dts file, codec mclk
speed).
The card will be set up based on this information.
Since the routing is provided via DT we can mark the card fully routed
so core can take care of disconnecting the unused pins.

Signed-off-by: Hebbar, Gururaja <gururaja.hebbar@ti.com>
---
Changes in V2
	 - Remove reference to Linux & software details from DT binding

:000000 100644 0000000... 25f7180... A	Documentation/devicetree/bindings/sound/davinci-evm-audio.txt
:100644 100644 d55e647... 91a32e5... M	sound/soc/davinci/davinci-evm.c
 .../bindings/sound/davinci-evm-audio.txt           |   53 ++++++
 sound/soc/davinci/davinci-evm.c                    |  179 +++++++++++++++++---
 2 files changed, 212 insertions(+), 20 deletions(-)

Comments

Mark Brown Jan. 4, 2013, 12:26 p.m. UTC | #1
On Fri, Jan 04, 2013 at 03:24:37PM +0530, Hebbar Gururaja wrote:

> +		"MIC3L",                "Mic Bias 2V",
> +		"MIC3R",                "Mic Bias 2V",
> +		"Mic Bias 2V",          "Mic Jack",

The CODEC driver biases should be changed over to be supplies, this
makes the above much more natural - the routing there is a hack for
older versions of ASoc.  Otherwise this looks fine.
Hebbar, Gururaja Jan. 7, 2013, 9:20 a.m. UTC | #2
On Fri, Jan 04, 2013 at 17:56:12, Mark Brown wrote:
> On Fri, Jan 04, 2013 at 03:24:37PM +0530, Hebbar Gururaja wrote:
> 
> > +		"MIC3L",                "Mic Bias 2V",
> > +		"MIC3R",                "Mic Bias 2V",
> > +		"Mic Bias 2V",          "Mic Jack",
> 
> The CODEC driver biases should be changed over to be supplies, this
> makes the above much more natural - the routing there is a hack for
> older versions of ASoc.  

I will be looking into this now. To speed up the things, do you have any
reference link/implementation which  I can refer to?

I looked at the OMAP implementation but they are following the same (meaning both
DT & non-DT use similar audio map for routing).


> Otherwise this looks fine.

Thanks for the review.

> 


Regards, 
Gururaja
Mark Brown Jan. 7, 2013, 2:27 p.m. UTC | #3
On Mon, Jan 07, 2013 at 09:20:16AM +0000, Hebbar, Gururaja wrote:

> I will be looking into this now. To speed up the things, do you have any
> reference link/implementation which  I can refer to?

Any modern CODEC driver.

> I looked at the OMAP implementation but they are following the same (meaning both
> DT & non-DT use similar audio map for routing).

You should use the same setup for DT and non-DT.
Hebbar, Gururaja Jan. 23, 2013, 12:39 p.m. UTC | #4
On Fri, Jan 04, 2013 at 17:56:12, Mark Brown wrote:
> On Fri, Jan 04, 2013 at 03:24:37PM +0530, Hebbar Gururaja wrote:
> 
> > +		"MIC3L",                "Mic Bias 2V",
> > +		"MIC3R",                "Mic Bias 2V",
> > +		"Mic Bias 2V",          "Mic Jack",
> 
> The CODEC driver biases should be changed over to be supplies, this
> makes the above much more natural - the routing there is a hack for
> older versions of ASoc.  Otherwise this looks fine.


ON TLV320AIC3x Codec, MIC Bias power on/off share the same register bits
with Bias voltage output.

Page 0 / Register 25: MICBIAS Control Register

BIT	READ/WRITE	RESET VALUE	DESCRIPTION
D7–D6	R/W 			00 	MICBIAS Level Control
					00: MICBIAS output is powered down
					01: MICBIAS output is powered to 2.0V
					10: MICBIAS output is powered to 2.5V
					11: MICBIAS output is connected to AVDD


Because of this, I find it difficult to use SND_SOC_DAPM_SUPPLY macro.

I found a similar implementation (MIC BIAS enable + Bias voltage selection)
in 2 other platform (WM8900 & SGTL5000).

WM8900 --> Different registers for MIC BIAS enable & Bias voltage selection.
However both are implemented using different macros

static const char *mic_bias_level_txt[] = { "0.9*AVDD", "0.65*AVDD" };

static const struct soc_enum mic_bias_level =
SOC_ENUM_SINGLE(WM8900_REG_INCTL, 8, 2, mic_bias_level_txt);

...
static const struct snd_kcontrol_new wm8900_snd_controls[] = {
SOC_ENUM("Mic Bias Level", mic_bias_level),
...

SND_SOC_DAPM_SUPPLY("Mic Bias", WM8900_REG_POWER1, 4, 0, NULL, 0),



SGTL5000 --> Single register for MIC BIAS enable & output impedance of MIC
Bias. The driver uses SND_SOC_DAPM_POST_PMU & SND_SOC_DAPM_PRE_PMD macro to
handle the MIC Bias enable & disable event.


static int mic_bias_event(struct snd_soc_dapm_widget *w,
        struct snd_kcontrol *kcontrol, int event)
{
        switch (event) {
        case SND_SOC_DAPM_POST_PMU:
	...

static const struct snd_soc_dapm_widget sgtl5000_dapm_widgets[] = {
	...
        SND_SOC_DAPM_SUPPLY("Mic Bias", SGTL5000_CHIP_MIC_CTRL, 8, 0,
                            mic_bias_event,
                            SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),


Since TLV320AIC3x Codec uses single register bit-set to indicate BIA state
& Voltage level, I am not able to able to change existing code to
SND_SOC_DAPM_SUPPLY macro.


Could you please show some pointers on how to handle/implement
SND_SOC_DAPM_SUPPLY in above scenario?


Thanks & Regards, 
Gururaja
Mark Brown Jan. 23, 2013, 3:48 p.m. UTC | #5
On Wed, Jan 23, 2013 at 12:39:35PM +0000, Hebbar, Gururaja wrote:
> On Fri, Jan 04, 2013 at 17:56:12, Mark Brown wrote:

> > The CODEC driver biases should be changed over to be supplies, this
> > makes the above much more natural - the routing there is a hack for
> > older versions of ASoc.  Otherwise this looks fine.

> ON TLV320AIC3x Codec, MIC Bias power on/off share the same register bits
> with Bias voltage output.

So you need to use an event to write the actual enable bit.

> SGTL5000 --> Single register for MIC BIAS enable & output impedance of MIC
> Bias. The driver uses SND_SOC_DAPM_POST_PMU & SND_SOC_DAPM_PRE_PMD macro to
> handle the MIC Bias enable & disable event.

This is the way these things should be handled.
diff mbox

Patch

diff --git a/Documentation/devicetree/bindings/sound/davinci-evm-audio.txt b/Documentation/devicetree/bindings/sound/davinci-evm-audio.txt
new file mode 100644
index 0000000..25f7180
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/davinci-evm-audio.txt
@@ -0,0 +1,53 @@ 
+* Texas Instruments SoC audio setups with TLV320AIC3X Codec
+
+Required properties:
+- compatible :
+	"ti,dm365-voice-codec-audio"	: for DM365 platforms with Voice Codec
+	"ti,da830-evm-audio"		: for DM365/DA8xx/OMAPL1x/AM33xx
+
+- ti,model : The user-visible name of this sound complex.
+- ti,audio-codec : The phandle of the TLV320AIC3x audio codec
+- ti,mcasp-controller : The phandle of the McASP controller
+- ti,codec-clock-rate : The Codec Clock rate (in Hz) applied to the Codec
+- ti,audio-routing : A list of the connections between audio components.
+  Each entry is a pair of strings, the first being the connection's sink,
+  the second being the connection's source. Valid names for sources and
+  sinks are the codec's pins, and the jacks on the board:
+
+  Codec pins:
+
+  * MIC3L
+  * MIC3R
+  * LINE1L
+  * LINE2L
+  * LINE1R
+  * LINE2R
+
+  Board connectors:
+
+  * Headphone Jack
+  * Line Out
+  * Mic Jack
+
+
+Example:
+
+sound {
+	compatible = "ti,da830-evm-audio";
+	ti,model = "DA830 EVM";
+	ti,audio-codec = <&tlv320aic3x>;
+	ti,mcasp-controller = <&mcasp1>;
+	ti,codec-clock-rate = <12000000>;
+	ti,audio-routing =
+		"Headphone Jack",       "HPLOUT",
+		"Headphone Jack",       "HPROUT",
+		"Line Out",             "LLOUT",
+		"Line Out",             "RLOUT",
+		"MIC3L",                "Mic Bias 2V",
+		"MIC3R",                "Mic Bias 2V",
+		"Mic Bias 2V",          "Mic Jack",
+		"LINE1L",               "Line In",
+		"LINE2L",               "Line In",
+		"LINE1R",               "Line In",
+		"LINE2R",               "Line In";
+};
diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c
index d55e647..91a32e5 100644
--- a/sound/soc/davinci/davinci-evm.c
+++ b/sound/soc/davinci/davinci-evm.c
@@ -15,6 +15,7 @@ 
 #include <linux/interrupt.h>
 #include <linux/platform_device.h>
 #include <linux/i2c.h>
+#include <linux/of_platform.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/soc.h>
@@ -34,27 +35,38 @@  static int evm_hw_params(struct snd_pcm_substream *substream,
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_dai *codec_dai = rtd->codec_dai;
 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_card *soc_card = codec->card;
+	struct device_node *np = soc_card->dev->of_node;
 	int ret = 0;
 	unsigned sysclk;
 
-	/* ASP1 on DM355 EVM is clocked by an external oscillator */
-	if (machine_is_davinci_dm355_evm() || machine_is_davinci_dm6467_evm() ||
-	    machine_is_davinci_dm365_evm())
-		sysclk = 27000000;
-
-	/* ASP0 in DM6446 EVM is clocked by U55, as configured by
-	 * board-dm644x-evm.c using GPIOs from U18.  There are six
-	 * options; here we "know" we use a 48 KHz sample rate.
-	 */
-	else if (machine_is_davinci_evm())
-		sysclk = 12288000;
-
-	else if (machine_is_davinci_da830_evm() ||
-				machine_is_davinci_da850_evm())
-		sysclk = 24576000;
-
-	else
-		return -EINVAL;
+	if (np) {
+		ret = of_property_read_u32(np, "ti,codec-clock-rate", &sysclk);
+		if (ret < 0)
+			return ret;
+	} else {
+		/* ASP1 on DM355 EVM is clocked by an external oscillator */
+		if (machine_is_davinci_dm355_evm() ||
+			machine_is_davinci_dm6467_evm() ||
+			machine_is_davinci_dm365_evm())
+			sysclk = 27000000;
+
+		/*
+		 * ASP0 in DM6446 EVM is clocked by U55, as configured by
+		 * board-dm644x-evm.c using GPIOs from U18.  There are six
+		 * options; here we "know" we use a 48 KHz sample rate.
+		 */
+		else if (machine_is_davinci_evm())
+			sysclk = 12288000;
+
+		else if (machine_is_davinci_da830_evm() ||
+					machine_is_davinci_da850_evm())
+			sysclk = 24576000;
+
+		else
+			return -EINVAL;
+	}
 
 	/* set codec DAI configuration */
 	ret = snd_soc_dai_set_fmt(codec_dai, AUDIO_FORMAT);
@@ -132,13 +144,22 @@  static int evm_aic3x_init(struct snd_soc_pcm_runtime *rtd)
 {
 	struct snd_soc_codec *codec = rtd->codec;
 	struct snd_soc_dapm_context *dapm = &codec->dapm;
+	struct device_node *np = codec->card->dev->of_node;
+	int ret;
 
 	/* Add davinci-evm specific widgets */
 	snd_soc_dapm_new_controls(dapm, aic3x_dapm_widgets,
 				  ARRAY_SIZE(aic3x_dapm_widgets));
 
-	/* Set up davinci-evm specific audio path audio_map */
-	snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
+	if (np) {
+		ret = snd_soc_of_parse_audio_routing(codec->card,
+							"ti,audio-routing");
+		if (ret)
+			return ret;
+	} else {
+		/* Set up davinci-evm specific audio path audio_map */
+		snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
+	}
 
 	/* not connected */
 	snd_soc_dapm_disable_pin(dapm, "MONO_LOUT");
@@ -287,6 +308,108 @@  static struct snd_soc_card da850_snd_soc_card = {
 	.num_links = 1,
 };
 
+#if defined(CONFIG_OF)
+
+enum {
+	MACHINE_VERSION_1 = 0,	/* DM365 with Voice Codec */
+	MACHINE_VERSION_2,	/* DM365/DA8xx/OMAPL1x/AM33xx */
+};
+
+static const struct of_device_id davinci_evm_dt_ids[] = {
+	{
+		.compatible = "ti,dm365-voice-codec-audio",
+		.data = (void *)MACHINE_VERSION_1,
+	},
+	{
+		.compatible = "ti,da830-evm-audio",
+		.data = (void *)MACHINE_VERSION_2,
+	},
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, davinci_mcasp_dt_ids);
+
+/*
+ * This struct is just used as place holder. It will be filled with
+ * data from dt node
+ */
+static struct snd_soc_dai_link evm_dai = {
+	.name		= "TLV320AIC3X",
+	.stream_name	= "AIC3X",
+	.codec_dai_name	= "tlv320aic3x-hifi",
+};
+
+/* davinci evm audio machine driver */
+static struct snd_soc_card evm_soc_card = {
+	.owner = THIS_MODULE,
+	.dai_link = &evm_dai,
+	.num_links = 1,
+};
+
+static int davinci_evm_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	const struct of_device_id *match =
+		of_match_device(of_match_ptr(davinci_evm_dt_ids), &pdev->dev);
+	u32 machine_ver;
+	int ret = 0;
+
+	machine_ver = (u32)match->data;
+	switch (machine_ver) {
+	case MACHINE_VERSION_1:
+		evm_dai.name		= "Voice Codec - CQ93VC";
+		evm_dai.stream_name	= "CQ93";
+		evm_dai.codec_dai_name	= "cq93vc-hifi";
+		break;
+
+	case MACHINE_VERSION_2:
+		evm_dai.ops = &evm_ops;
+		evm_dai.init = evm_aic3x_init;
+		break;
+	}
+
+	evm_dai.codec_of_node = of_parse_phandle(np, "ti,audio-codec", 0);
+	if (!evm_dai.codec_of_node)
+		return -EINVAL;
+
+	evm_dai.cpu_of_node = of_parse_phandle(np,
+						"ti,mcasp-controller", 0);
+	if (!evm_dai.cpu_of_node)
+		return -EINVAL;
+
+	evm_dai.platform_of_node = evm_dai.cpu_of_node;
+
+	evm_soc_card.dev = &pdev->dev;
+	ret = snd_soc_of_parse_card_name(&evm_soc_card, "ti,model");
+	if (ret)
+		return ret;
+
+	ret = snd_soc_register_card(&evm_soc_card);
+	if (ret)
+		dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
+
+	return ret;
+}
+
+static int __devexit davinci_evm_remove(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+	snd_soc_unregister_card(card);
+
+	return 0;
+}
+
+static struct platform_driver davinci_evm_driver = {
+	.probe		= davinci_evm_probe,
+	.remove		= __devexit_p(davinci_evm_remove),
+	.driver		= {
+		.name	= "davinci_evm",
+		.owner	= THIS_MODULE,
+		.of_match_table = of_match_ptr(davinci_evm_dt_ids),
+	},
+};
+#endif
+
 static struct platform_device *evm_snd_device;
 
 static int __init evm_init(void)
@@ -295,6 +418,15 @@  static int __init evm_init(void)
 	int index;
 	int ret;
 
+#if defined(CONFIG_OF)
+	/*
+	 * If dtb is there, the devices will be created dynamically.
+	 * Only register platfrom driver structure.
+	 */
+	if (of_have_populated_dt())
+		return platform_driver_register(&davinci_evm_driver);
+#endif
+
 	if (machine_is_davinci_evm()) {
 		evm_snd_dev_data = &dm6446_snd_soc_card_evm;
 		index = 0;
@@ -330,6 +462,13 @@  static int __init evm_init(void)
 
 static void __exit evm_exit(void)
 {
+#if defined(CONFIG_OF)
+	if (of_have_populated_dt()) {
+		platform_driver_unregister(&davinci_evm_driver);
+		return;
+	}
+#endif
+
 	platform_device_unregister(evm_snd_device);
 }