All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 1/2] ASoC:codecs: Add a generic HDMI audio CODEC
  2014-08-31 13:02 ` Jean-Francois Moine
@ 2014-08-31 10:45   ` Jean-Francois Moine
  -1 siblings, 0 replies; 7+ messages in thread
From: Jean-Francois Moine @ 2014-08-31 10:45 UTC (permalink / raw)
  To: Mark Brown, Russell King - ARM Linux
  Cc: Andrew Jackson, alsa-devel, devicetree, dri-devel, linux-kernel

This patch adds a generic audio CODEC function to HDMI transmitters.

The CODEC is implemented as a library in a kernel module.

It handles both I2S and S/PDIF input, maintaining the audio format
and rates constraints according to the HDMI device parameters (EDID).

Audio source input switch is offered to the HDMI driver on start/stop
of audio streaming.

Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
 Documentation/devicetree/bindings/sound/hdmi2.txt |  32 ++++
 include/sound/hdmi2.h                             |  24 +++
 sound/soc/codecs/Kconfig                          |   3 +
 sound/soc/codecs/Makefile                         |   2 +
 sound/soc/codecs/hdmi2.c                          | 204 ++++++++++++++++++++++
 5 files changed, 265 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/hdmi2.txt
 create mode 100644 include/sound/hdmi2.h
 create mode 100644 sound/soc/codecs/hdmi2.c

diff --git a/Documentation/devicetree/bindings/sound/hdmi2.txt b/Documentation/devicetree/bindings/sound/hdmi2.txt
new file mode 100644
index 0000000..5776370
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/hdmi2.txt
@@ -0,0 +1,32 @@
+Device-Tree bindings for the generic HDMI2 CODEC
+
+The HDMI2 CODEC describes how the audio controller is connected to the
+HDMI transmitter.
+These definitions are included in the HDMI transmiter description.
+
+Required properties:
+
+  - audio-ports: must contain one or two HDMI transmitter dependant
+	values identifying the audio sources.
+	The source type is given by the corresponding entry in
+	the audio-port-names property.
+
+  - audio-port-names: must contain entries matching the entries in
+	the audio-ports property.
+	Each value may be "i2s" or "spdif", giving the type of
+	the associated audio port.
+
+  - #sound-dai-cells: must be set to <1> for use with the simple-card.
+	The DAI 0 is the I2S input and the DAI 1 is the S/PDIF input.
+
+Example:
+
+	hdmi: hdmi-encoder {
+		compatible = "nxp,tda998x";
+		reg = <0x70>;
+		...
+
+		audio-ports = <0x03>, <0x04>;
+		audio-port-names = "i2s", "spdif";
+		#sound-dai-cells = <1>;
+	};
diff --git a/include/sound/hdmi2.h b/include/sound/hdmi2.h
new file mode 100644
index 0000000..59e4148
--- /dev/null
+++ b/include/sound/hdmi2.h
@@ -0,0 +1,24 @@
+#ifndef SND_HDMI2_H
+#define SND_HDMI2_H
+/* hdmi2 codec data */
+struct hdmi2_codec {
+	u8 ports[2];
+	u16 source;			/* audio DAI = index to ports[] */
+#define HDMI2_I2S 0
+#define HDMI2_SPDIF 1
+
+	unsigned sample_rate;		/* current streaming values */
+	int sample_format;
+
+	u64 formats;			/* HDMI (EDID) values */
+	unsigned max_channels;
+	struct snd_pcm_hw_constraint_list rate_constraints;
+
+	void (*start)(struct hdmi2_codec *audio, int full);
+	void (*stop)(struct hdmi2_codec *audio);
+};
+
+/* hdmi device -> hdmi2 codec */
+int hdmi2_codec_register(struct device *dev);
+void hdmi2_codec_unregister(struct device *dev);
+#endif
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 8ab1547..1b8d81e 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -424,6 +424,9 @@ config SND_SOC_ES8328_SPI
 	tristate
 	select SND_SOC_ES8328
 
+config SND_SOC_HDMI2
+	tristate
+
 config SND_SOC_ISABELLE
         tristate
 
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index afba944..f59b1e6 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -53,6 +53,7 @@ snd-soc-dmic-objs := dmic.o
 snd-soc-es8328-objs := es8328.o
 snd-soc-es8328-i2c-objs := es8328-i2c.o
 snd-soc-es8328-spi-objs := es8328-spi.o
+snd-soc-hdmi2-objs := hdmi2.o
 snd-soc-isabelle-objs := isabelle.o
 snd-soc-jz4740-codec-objs := jz4740.o
 snd-soc-l3-objs := l3.o
@@ -228,6 +229,7 @@ obj-$(CONFIG_SND_SOC_DMIC)	+= snd-soc-dmic.o
 obj-$(CONFIG_SND_SOC_ES8328)	+= snd-soc-es8328.o
 obj-$(CONFIG_SND_SOC_ES8328_I2C)+= snd-soc-es8328-i2c.o
 obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o
+obj-$(CONFIG_SND_SOC_HDMI2)	+= snd-soc-hdmi2.o
 obj-$(CONFIG_SND_SOC_ISABELLE)	+= snd-soc-isabelle.o
 obj-$(CONFIG_SND_SOC_JZ4740_CODEC)	+= snd-soc-jz4740-codec.o
 obj-$(CONFIG_SND_SOC_L3)	+= snd-soc-l3.o
diff --git a/sound/soc/codecs/hdmi2.c b/sound/soc/codecs/hdmi2.c
new file mode 100644
index 0000000..8ba8ba6
--- /dev/null
+++ b/sound/soc/codecs/hdmi2.c
@@ -0,0 +1,204 @@
+/*
+ * ALSA SoC generic HDMI CODEC
+ *
+ * Copyright (C) 2014 Jean-Francois Moine
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <linux/of.h>
+#include <linux/i2c.h>
+#include <sound/hdmi2.h>
+
+#define HDMI2_FORMATS	(SNDRV_PCM_FMTBIT_S16_LE | \
+			SNDRV_PCM_FMTBIT_S20_3LE | \
+			SNDRV_PCM_FMTBIT_S24_LE | \
+			SNDRV_PCM_FMTBIT_S32_LE)
+
+static int hdmi2_startup(struct snd_pcm_substream *substream,
+			struct snd_soc_dai *dai)
+{
+	struct hdmi2_codec *audio = snd_soc_codec_get_drvdata(dai->codec);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	/* set the constraints */
+	snd_pcm_hw_constraint_list(runtime, 0,
+					SNDRV_PCM_HW_PARAM_RATE,
+					&audio->rate_constraints);
+	snd_pcm_hw_constraint_mask64(runtime,
+				SNDRV_PCM_HW_PARAM_FORMAT,
+				audio->formats);
+
+	snd_pcm_hw_constraint_minmax(runtime,
+				SNDRV_PCM_HW_PARAM_CHANNELS,
+				1, audio->max_channels);
+	return 0;
+}
+
+static int hdmi2_hw_params(struct snd_pcm_substream *substream,
+			struct snd_pcm_hw_params *params,
+			struct snd_soc_dai *dai)
+{
+	struct hdmi2_codec *audio = snd_soc_codec_get_drvdata(dai->codec);
+
+	/* if same input and same parameters, do not do a full switch */
+	if (dai->id == audio->source &&
+	    params_format(params) == audio->sample_format &&
+	    params_rate(params) == audio->sample_rate) {
+		audio->start(audio, 0);
+		return 0;
+	}
+
+	audio->source = dai->id;
+	audio->sample_format = params_format(params);
+	audio->sample_rate = params_rate(params);
+	audio->start(audio, 1);
+	return 0;
+}
+
+static void hdmi2_shutdown(struct snd_pcm_substream *substream,
+			struct snd_soc_dai *dai)
+{
+	struct hdmi2_codec *audio = snd_soc_codec_get_drvdata(dai->codec);
+
+	audio->stop(audio);
+}
+
+static const struct snd_soc_dai_ops hdmi2_ops = {
+	.startup = hdmi2_startup,
+	.hw_params = hdmi2_hw_params,
+	.shutdown = hdmi2_shutdown,
+};
+
+static struct snd_soc_dai_driver hdmi2_dai[] = {
+	{
+		.name = "i2s-hifi",
+		.id = HDMI2_I2S,
+		.playback = {
+			.stream_name	= "HDMI I2S Playback",
+			.channels_min	= 1,
+			.channels_max	= 8,
+			.rates		= SNDRV_PCM_RATE_CONTINUOUS,
+			.rate_min	= 5512,
+			.rate_max	= 192000,
+			.formats	= HDMI2_FORMATS,
+		},
+		.ops = &hdmi2_ops,
+	},
+	{
+		.name = "spdif-hifi",
+		.id = HDMI2_SPDIF,
+		.playback = {
+			.stream_name	= "HDMI SPDIF Playback",
+			.channels_min	= 1,
+			.channels_max	= 2,
+			.rates		= SNDRV_PCM_RATE_CONTINUOUS,
+			.rate_min	= 22050,
+			.rate_max	= 192000,
+			.formats	= HDMI2_FORMATS,
+		},
+		.ops = &hdmi2_ops,
+	},
+};
+
+static const struct snd_soc_dapm_widget hdmi2_widgets[] = {
+	SND_SOC_DAPM_OUTPUT("hdmi-out"),
+};
+static const struct snd_soc_dapm_route hdmi2_routes[] = {
+	{ "hdmi-out", NULL, "HDMI I2S Playback" },
+	{ "hdmi-out", NULL, "HDMI SPDIF Playback" },
+};
+
+/*
+ * The HDMI driver must set the i2c client data to the hdmi2_codec
+ */
+static int hdmi2_probe(struct snd_soc_codec *codec)
+{
+	struct i2c_client *i2c_client = to_i2c_client(codec->dev);
+	struct hdmi2_codec *audio = i2c_get_clientdata(i2c_client);
+	struct device_node *np = codec->dev->of_node;
+	int i, j, ret;
+	const char *p;
+
+	if (!audio)
+		return -ENODEV;
+	snd_soc_codec_set_drvdata(codec, audio);
+
+	if (!np)
+		return 0;
+
+	/* get the audio input ports*/
+	for (i = 0; i < 2; i++) {
+		u32 port;
+
+		ret = of_property_read_u32_index(np, "audio-ports", i, &port);
+		if (ret) {
+			if (i == 0)
+				dev_err(codec->dev,
+					"bad or missing audio-ports\n");
+			break;
+		}
+		ret = of_property_read_string_index(np, "audio-port-names",
+						i, &p);
+		if (ret) {
+			dev_err(codec->dev,
+				"missing audio-port-names[%d]\n", i);
+			break;
+		}
+		if (strcmp(p, "i2s") == 0) {
+			j = 0;
+		} else if (strcmp(p, "spdif") == 0) {
+			j = 1;
+		} else {
+			dev_err(codec->dev,
+				"bad audio-port-names '%s'\n", p);
+			break;
+		}
+		audio->ports[j] = port;
+	}
+	return 0;
+}
+
+static const struct snd_soc_codec_driver soc_codec_hdmi2 = {
+	.probe = hdmi2_probe,
+	.dapm_widgets = hdmi2_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(hdmi2_widgets),
+	.dapm_routes = hdmi2_routes,
+	.num_dapm_routes = ARRAY_SIZE(hdmi2_routes),
+};
+
+int hdmi2_codec_register(struct device *dev)
+{
+	return snd_soc_register_codec(dev,
+				&soc_codec_hdmi2,
+				hdmi2_dai, ARRAY_SIZE(hdmi2_dai));
+}
+EXPORT_SYMBOL(hdmi2_codec_register);
+
+void hdmi2_codec_unregister(struct device *dev)
+{
+	snd_soc_unregister_codec(dev);
+}
+EXPORT_SYMBOL(hdmi2_codec_unregister);
+
+/* -- module insert / remove -- */
+MODULE_AUTHOR("Jean-Francois Moine <moinejf@free.fr>");
+MODULE_DESCRIPTION("HDMI2 CODEC");
+MODULE_LICENSE("GPL");
+
+static int __init hdmi2_codec_init(void)
+{
+	return 0;
+}
+static void __exit hdmi2_codec_exit(void)
+{
+}
+
+module_init(hdmi2_codec_init);
+module_exit(hdmi2_codec_exit);
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v4 1/2] ASoC:codecs: Add a generic HDMI audio CODEC
@ 2014-08-31 10:45   ` Jean-Francois Moine
  0 siblings, 0 replies; 7+ messages in thread
From: Jean-Francois Moine @ 2014-08-31 10:45 UTC (permalink / raw)
  To: Mark Brown, Russell King - ARM Linux
  Cc: devicetree, alsa-devel, Andrew Jackson, linux-kernel, dri-devel

This patch adds a generic audio CODEC function to HDMI transmitters.

The CODEC is implemented as a library in a kernel module.

It handles both I2S and S/PDIF input, maintaining the audio format
and rates constraints according to the HDMI device parameters (EDID).

Audio source input switch is offered to the HDMI driver on start/stop
of audio streaming.

Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
 Documentation/devicetree/bindings/sound/hdmi2.txt |  32 ++++
 include/sound/hdmi2.h                             |  24 +++
 sound/soc/codecs/Kconfig                          |   3 +
 sound/soc/codecs/Makefile                         |   2 +
 sound/soc/codecs/hdmi2.c                          | 204 ++++++++++++++++++++++
 5 files changed, 265 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/hdmi2.txt
 create mode 100644 include/sound/hdmi2.h
 create mode 100644 sound/soc/codecs/hdmi2.c

diff --git a/Documentation/devicetree/bindings/sound/hdmi2.txt b/Documentation/devicetree/bindings/sound/hdmi2.txt
new file mode 100644
index 0000000..5776370
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/hdmi2.txt
@@ -0,0 +1,32 @@
+Device-Tree bindings for the generic HDMI2 CODEC
+
+The HDMI2 CODEC describes how the audio controller is connected to the
+HDMI transmitter.
+These definitions are included in the HDMI transmiter description.
+
+Required properties:
+
+  - audio-ports: must contain one or two HDMI transmitter dependant
+	values identifying the audio sources.
+	The source type is given by the corresponding entry in
+	the audio-port-names property.
+
+  - audio-port-names: must contain entries matching the entries in
+	the audio-ports property.
+	Each value may be "i2s" or "spdif", giving the type of
+	the associated audio port.
+
+  - #sound-dai-cells: must be set to <1> for use with the simple-card.
+	The DAI 0 is the I2S input and the DAI 1 is the S/PDIF input.
+
+Example:
+
+	hdmi: hdmi-encoder {
+		compatible = "nxp,tda998x";
+		reg = <0x70>;
+		...
+
+		audio-ports = <0x03>, <0x04>;
+		audio-port-names = "i2s", "spdif";
+		#sound-dai-cells = <1>;
+	};
diff --git a/include/sound/hdmi2.h b/include/sound/hdmi2.h
new file mode 100644
index 0000000..59e4148
--- /dev/null
+++ b/include/sound/hdmi2.h
@@ -0,0 +1,24 @@
+#ifndef SND_HDMI2_H
+#define SND_HDMI2_H
+/* hdmi2 codec data */
+struct hdmi2_codec {
+	u8 ports[2];
+	u16 source;			/* audio DAI = index to ports[] */
+#define HDMI2_I2S 0
+#define HDMI2_SPDIF 1
+
+	unsigned sample_rate;		/* current streaming values */
+	int sample_format;
+
+	u64 formats;			/* HDMI (EDID) values */
+	unsigned max_channels;
+	struct snd_pcm_hw_constraint_list rate_constraints;
+
+	void (*start)(struct hdmi2_codec *audio, int full);
+	void (*stop)(struct hdmi2_codec *audio);
+};
+
+/* hdmi device -> hdmi2 codec */
+int hdmi2_codec_register(struct device *dev);
+void hdmi2_codec_unregister(struct device *dev);
+#endif
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 8ab1547..1b8d81e 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -424,6 +424,9 @@ config SND_SOC_ES8328_SPI
 	tristate
 	select SND_SOC_ES8328
 
+config SND_SOC_HDMI2
+	tristate
+
 config SND_SOC_ISABELLE
         tristate
 
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index afba944..f59b1e6 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -53,6 +53,7 @@ snd-soc-dmic-objs := dmic.o
 snd-soc-es8328-objs := es8328.o
 snd-soc-es8328-i2c-objs := es8328-i2c.o
 snd-soc-es8328-spi-objs := es8328-spi.o
+snd-soc-hdmi2-objs := hdmi2.o
 snd-soc-isabelle-objs := isabelle.o
 snd-soc-jz4740-codec-objs := jz4740.o
 snd-soc-l3-objs := l3.o
@@ -228,6 +229,7 @@ obj-$(CONFIG_SND_SOC_DMIC)	+= snd-soc-dmic.o
 obj-$(CONFIG_SND_SOC_ES8328)	+= snd-soc-es8328.o
 obj-$(CONFIG_SND_SOC_ES8328_I2C)+= snd-soc-es8328-i2c.o
 obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o
+obj-$(CONFIG_SND_SOC_HDMI2)	+= snd-soc-hdmi2.o
 obj-$(CONFIG_SND_SOC_ISABELLE)	+= snd-soc-isabelle.o
 obj-$(CONFIG_SND_SOC_JZ4740_CODEC)	+= snd-soc-jz4740-codec.o
 obj-$(CONFIG_SND_SOC_L3)	+= snd-soc-l3.o
diff --git a/sound/soc/codecs/hdmi2.c b/sound/soc/codecs/hdmi2.c
new file mode 100644
index 0000000..8ba8ba6
--- /dev/null
+++ b/sound/soc/codecs/hdmi2.c
@@ -0,0 +1,204 @@
+/*
+ * ALSA SoC generic HDMI CODEC
+ *
+ * Copyright (C) 2014 Jean-Francois Moine
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <linux/of.h>
+#include <linux/i2c.h>
+#include <sound/hdmi2.h>
+
+#define HDMI2_FORMATS	(SNDRV_PCM_FMTBIT_S16_LE | \
+			SNDRV_PCM_FMTBIT_S20_3LE | \
+			SNDRV_PCM_FMTBIT_S24_LE | \
+			SNDRV_PCM_FMTBIT_S32_LE)
+
+static int hdmi2_startup(struct snd_pcm_substream *substream,
+			struct snd_soc_dai *dai)
+{
+	struct hdmi2_codec *audio = snd_soc_codec_get_drvdata(dai->codec);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	/* set the constraints */
+	snd_pcm_hw_constraint_list(runtime, 0,
+					SNDRV_PCM_HW_PARAM_RATE,
+					&audio->rate_constraints);
+	snd_pcm_hw_constraint_mask64(runtime,
+				SNDRV_PCM_HW_PARAM_FORMAT,
+				audio->formats);
+
+	snd_pcm_hw_constraint_minmax(runtime,
+				SNDRV_PCM_HW_PARAM_CHANNELS,
+				1, audio->max_channels);
+	return 0;
+}
+
+static int hdmi2_hw_params(struct snd_pcm_substream *substream,
+			struct snd_pcm_hw_params *params,
+			struct snd_soc_dai *dai)
+{
+	struct hdmi2_codec *audio = snd_soc_codec_get_drvdata(dai->codec);
+
+	/* if same input and same parameters, do not do a full switch */
+	if (dai->id == audio->source &&
+	    params_format(params) == audio->sample_format &&
+	    params_rate(params) == audio->sample_rate) {
+		audio->start(audio, 0);
+		return 0;
+	}
+
+	audio->source = dai->id;
+	audio->sample_format = params_format(params);
+	audio->sample_rate = params_rate(params);
+	audio->start(audio, 1);
+	return 0;
+}
+
+static void hdmi2_shutdown(struct snd_pcm_substream *substream,
+			struct snd_soc_dai *dai)
+{
+	struct hdmi2_codec *audio = snd_soc_codec_get_drvdata(dai->codec);
+
+	audio->stop(audio);
+}
+
+static const struct snd_soc_dai_ops hdmi2_ops = {
+	.startup = hdmi2_startup,
+	.hw_params = hdmi2_hw_params,
+	.shutdown = hdmi2_shutdown,
+};
+
+static struct snd_soc_dai_driver hdmi2_dai[] = {
+	{
+		.name = "i2s-hifi",
+		.id = HDMI2_I2S,
+		.playback = {
+			.stream_name	= "HDMI I2S Playback",
+			.channels_min	= 1,
+			.channels_max	= 8,
+			.rates		= SNDRV_PCM_RATE_CONTINUOUS,
+			.rate_min	= 5512,
+			.rate_max	= 192000,
+			.formats	= HDMI2_FORMATS,
+		},
+		.ops = &hdmi2_ops,
+	},
+	{
+		.name = "spdif-hifi",
+		.id = HDMI2_SPDIF,
+		.playback = {
+			.stream_name	= "HDMI SPDIF Playback",
+			.channels_min	= 1,
+			.channels_max	= 2,
+			.rates		= SNDRV_PCM_RATE_CONTINUOUS,
+			.rate_min	= 22050,
+			.rate_max	= 192000,
+			.formats	= HDMI2_FORMATS,
+		},
+		.ops = &hdmi2_ops,
+	},
+};
+
+static const struct snd_soc_dapm_widget hdmi2_widgets[] = {
+	SND_SOC_DAPM_OUTPUT("hdmi-out"),
+};
+static const struct snd_soc_dapm_route hdmi2_routes[] = {
+	{ "hdmi-out", NULL, "HDMI I2S Playback" },
+	{ "hdmi-out", NULL, "HDMI SPDIF Playback" },
+};
+
+/*
+ * The HDMI driver must set the i2c client data to the hdmi2_codec
+ */
+static int hdmi2_probe(struct snd_soc_codec *codec)
+{
+	struct i2c_client *i2c_client = to_i2c_client(codec->dev);
+	struct hdmi2_codec *audio = i2c_get_clientdata(i2c_client);
+	struct device_node *np = codec->dev->of_node;
+	int i, j, ret;
+	const char *p;
+
+	if (!audio)
+		return -ENODEV;
+	snd_soc_codec_set_drvdata(codec, audio);
+
+	if (!np)
+		return 0;
+
+	/* get the audio input ports*/
+	for (i = 0; i < 2; i++) {
+		u32 port;
+
+		ret = of_property_read_u32_index(np, "audio-ports", i, &port);
+		if (ret) {
+			if (i == 0)
+				dev_err(codec->dev,
+					"bad or missing audio-ports\n");
+			break;
+		}
+		ret = of_property_read_string_index(np, "audio-port-names",
+						i, &p);
+		if (ret) {
+			dev_err(codec->dev,
+				"missing audio-port-names[%d]\n", i);
+			break;
+		}
+		if (strcmp(p, "i2s") == 0) {
+			j = 0;
+		} else if (strcmp(p, "spdif") == 0) {
+			j = 1;
+		} else {
+			dev_err(codec->dev,
+				"bad audio-port-names '%s'\n", p);
+			break;
+		}
+		audio->ports[j] = port;
+	}
+	return 0;
+}
+
+static const struct snd_soc_codec_driver soc_codec_hdmi2 = {
+	.probe = hdmi2_probe,
+	.dapm_widgets = hdmi2_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(hdmi2_widgets),
+	.dapm_routes = hdmi2_routes,
+	.num_dapm_routes = ARRAY_SIZE(hdmi2_routes),
+};
+
+int hdmi2_codec_register(struct device *dev)
+{
+	return snd_soc_register_codec(dev,
+				&soc_codec_hdmi2,
+				hdmi2_dai, ARRAY_SIZE(hdmi2_dai));
+}
+EXPORT_SYMBOL(hdmi2_codec_register);
+
+void hdmi2_codec_unregister(struct device *dev)
+{
+	snd_soc_unregister_codec(dev);
+}
+EXPORT_SYMBOL(hdmi2_codec_unregister);
+
+/* -- module insert / remove -- */
+MODULE_AUTHOR("Jean-Francois Moine <moinejf@free.fr>");
+MODULE_DESCRIPTION("HDMI2 CODEC");
+MODULE_LICENSE("GPL");
+
+static int __init hdmi2_codec_init(void)
+{
+	return 0;
+}
+static void __exit hdmi2_codec_exit(void)
+{
+}
+
+module_init(hdmi2_codec_init);
+module_exit(hdmi2_codec_exit);
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v4 2/2] drm/i2c:tda998x: Use the HDMI2 audio CODEC
  2014-08-31 13:02 ` Jean-Francois Moine
@ 2014-08-31 12:35   ` Jean-Francois Moine
  -1 siblings, 0 replies; 7+ messages in thread
From: Jean-Francois Moine @ 2014-08-31 12:35 UTC (permalink / raw)
  To: Mark Brown, Russell King - ARM Linux
  Cc: Andrew Jackson, alsa-devel, devicetree, dri-devel, linux-kernel

This patch adds an audio CODEC function to the NXP TDA998x transmitter.

Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
 drivers/gpu/drm/i2c/Kconfig       |   1 +
 drivers/gpu/drm/i2c/tda998x_drv.c | 142 +++++++++++++++++++++++++++++++++++---
 2 files changed, 135 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/i2c/Kconfig b/drivers/gpu/drm/i2c/Kconfig
index 4d341db..fa79cd3 100644
--- a/drivers/gpu/drm/i2c/Kconfig
+++ b/drivers/gpu/drm/i2c/Kconfig
@@ -22,6 +22,7 @@ config DRM_I2C_SIL164
 config DRM_I2C_NXP_TDA998X
 	tristate "NXP Semiconductors TDA998X HDMI encoder"
 	default m if DRM_TILCDC
+	select SND_SOC_HDMI2
 	help
 	  Support for NXP Semiconductors TDA998X HDMI encoders.
 
diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index d476279..5f86b4d 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -27,6 +27,9 @@
 #include <drm/drm_edid.h>
 #include <drm/i2c/tda998x.h>
 
+#include <sound/pcm_params.h>
+#include <sound/hdmi2.h>
+
 #define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
 
 struct tda998x_priv {
@@ -44,9 +47,13 @@ struct tda998x_priv {
 	wait_queue_head_t wq_edid;
 	volatile int wq_edid_wait;
 	struct drm_encoder *encoder;
+
+	struct hdmi2_codec audio;
 };
 
 #define to_tda998x_priv(x)  ((struct tda998x_priv *)to_encoder_slave(x)->slave_priv)
+#define audio_priv(x) \
+		container_of(audio, struct tda998x_priv, audio)
 
 /* The TDA9988 series of devices use a paged register scheme.. to simplify
  * things we encode the page # in upper bits of the register #.  To read/
@@ -639,27 +646,42 @@ static void
 tda998x_configure_audio(struct tda998x_priv *priv,
 		struct drm_display_mode *mode, struct tda998x_encoder_params *p)
 {
-	uint8_t buf[6], clksel_aip, clksel_fs, cts_n, adiv;
+	uint8_t buf[6], clksel_aip, clksel_fs, cts_n, adiv, aclk;
 	uint32_t n;
 
 	/* Enable audio ports */
 	reg_write(priv, REG_ENA_AP, p->audio_cfg);
-	reg_write(priv, REG_ENA_ACLK, p->audio_clk_cfg);
 
 	/* Set audio input source */
-	switch (p->audio_format) {
-	case AFMT_SPDIF:
+	switch (priv->audio.source) {
+	case HDMI2_SPDIF:
 		reg_write(priv, REG_MUX_AP, MUX_AP_SELECT_SPDIF);
 		clksel_aip = AIP_CLKSEL_AIP_SPDIF;
 		clksel_fs = AIP_CLKSEL_FS_FS64SPDIF;
 		cts_n = CTS_N_M(3) | CTS_N_K(3);
+		aclk = 0;				/* no clock */
 		break;
 
-	case AFMT_I2S:
+	case HDMI2_I2S:
 		reg_write(priv, REG_MUX_AP, MUX_AP_SELECT_I2S);
 		clksel_aip = AIP_CLKSEL_AIP_I2S;
 		clksel_fs = AIP_CLKSEL_FS_ACLK;
 		cts_n = CTS_N_M(3) | CTS_N_K(3);
+		/* with I2S input, the CTS_N predivider depends on
+		 * the sample width */
+		switch (priv->audio.sample_format) {
+		case SNDRV_PCM_FORMAT_S16_LE:
+			cts_n = CTS_N_M(3) | CTS_N_K(1);
+			break;
+		case SNDRV_PCM_FORMAT_S24_LE:
+			cts_n = CTS_N_M(3) | CTS_N_K(2);
+			break;
+		default:
+		case SNDRV_PCM_FORMAT_S32_LE:
+			cts_n = CTS_N_M(3) | CTS_N_K(3);
+			break;
+		}
+		aclk = 1;				/* clock enable */
 		break;
 
 	default:
@@ -671,6 +693,7 @@ tda998x_configure_audio(struct tda998x_priv *priv,
 	reg_clear(priv, REG_AIP_CNTRL_0, AIP_CNTRL_0_LAYOUT |
 					AIP_CNTRL_0_ACR_MAN);	/* auto CTS */
 	reg_write(priv, REG_CTS_N, cts_n);
+	reg_write(priv, REG_ENA_ACLK, aclk);
 
 	/*
 	 * Audio input somehow depends on HDMI line rate which is
@@ -684,7 +707,7 @@ tda998x_configure_audio(struct tda998x_priv *priv,
 		adiv++;			/* AUDIO_DIV_SERCLK_16 */
 
 	/* S/PDIF asks for a larger divider */
-	if (p->audio_format == AFMT_SPDIF)
+	if (priv->audio.source == HDMI2_SPDIF)
 		adiv++;			/* AUDIO_DIV_SERCLK_16 or _32 */
 
 	reg_write(priv, REG_AUDIO_DIV, adiv);
@@ -693,7 +716,7 @@ tda998x_configure_audio(struct tda998x_priv *priv,
 	 * This is the approximate value of N, which happens to be
 	 * the recommended values for non-coherent clocks.
 	 */
-	n = 128 * p->audio_sample_rate / 1000;
+	n = 128 * priv->audio.sample_rate / 1000;
 
 	/* Write the CTS and N values */
 	buf[0] = 0x44;
@@ -727,6 +750,30 @@ tda998x_configure_audio(struct tda998x_priv *priv,
 	tda998x_write_aif(priv, p);
 }
 
+/* hdmi2 codec interface */
+void tda998x_audio_start(struct hdmi2_codec *audio,
+			 int full)
+{
+	struct tda998x_priv *priv = audio_priv(audio);
+	struct tda998x_encoder_params *p = &priv->params;
+
+	if (!priv->encoder->crtc)
+		return;
+	p->audio_cfg = audio->ports[audio->source];
+	if (!full) {
+		reg_write(priv, REG_ENA_AP, p->audio_cfg);
+		return;
+	}
+	tda998x_configure_audio(priv, &priv->encoder->crtc->hwmode, p);
+}
+
+void tda998x_audio_stop(struct hdmi2_codec *audio)
+{
+	struct tda998x_priv *priv = audio_priv(audio);
+
+	reg_write(priv, REG_ENA_AP, 0);
+}
+
 /* DRM encoder functions */
 
 static void tda998x_encoder_set_config(struct tda998x_priv *priv,
@@ -746,6 +793,9 @@ static void tda998x_encoder_set_config(struct tda998x_priv *priv,
 			    (p->mirr_f ? VIP_CNTRL_2_MIRR_F : 0);
 
 	priv->params = *p;
+	priv->audio.source = p->audio_format == AFMT_I2S ?
+					HDMI2_I2S : HDMI2_SPDIF;
+	priv->audio.sample_rate = p->audio_sample_rate;
 }
 
 static void tda998x_encoder_dpms(struct tda998x_priv *priv, int mode)
@@ -1128,6 +1178,64 @@ fail:
 	return NULL;
 }
 
+static void tda998x_set_audio(struct hdmi2_codec *audio,
+				struct drm_connector *connector)
+{
+	u8 *eld = connector->eld;
+	u8 *sad;
+	int sad_count;
+	unsigned eld_ver, mnl, rate_mask;
+	unsigned max_channels, fmt;
+	u64 formats;
+	struct snd_pcm_hw_constraint_list *rate_constraints =
+			&audio->rate_constraints;
+	static const u32 hdmi_rates[] = {
+		32000, 44100, 48000, 88200, 96000, 176400, 192000
+	};
+
+	/* adjust the hw params from the ELD (EDID) */
+	eld_ver = eld[0] >> 3;
+	if (eld_ver != 2 && eld_ver != 31)
+		return;
+
+	mnl = eld[4] & 0x1f;
+	if (mnl > 16)
+		return;
+
+	sad_count = eld[5] >> 4;
+	sad = eld + 20 + mnl;
+
+	/* Start from the basic audio settings */
+	max_channels = 2;
+	rate_mask = 0;
+	fmt = 0;
+	while (sad_count--) {
+		switch (sad[0] & 0x78) {
+		case 0x08: /* PCM */
+			max_channels = max(max_channels, (sad[0] & 7) + 1u);
+			rate_mask |= sad[1];
+			fmt |= sad[2] & 0x07;
+			break;
+		}
+		sad += 3;
+	}
+
+	/* set the constraints */
+	rate_constraints->list = hdmi_rates;
+	rate_constraints->count = ARRAY_SIZE(hdmi_rates);
+	rate_constraints->mask = rate_mask;
+
+	formats = 0;
+	if (fmt & 1)
+		formats |= SNDRV_PCM_FMTBIT_S16_LE;
+	if (fmt & 2)
+		formats |= SNDRV_PCM_FMTBIT_S20_3LE;
+	if (fmt & 4)
+		formats |= SNDRV_PCM_FMTBIT_S24_LE;
+	audio->formats = formats;
+	audio->max_channels = max_channels;
+}
+
 static int
 tda998x_encoder_get_modes(struct tda998x_priv *priv,
 			  struct drm_connector *connector)
@@ -1139,6 +1247,13 @@ tda998x_encoder_get_modes(struct tda998x_priv *priv,
 		drm_mode_connector_update_edid_property(connector, edid);
 		n = drm_add_edid_modes(connector, edid);
 		priv->is_hdmi_sink = drm_detect_hdmi_monitor(edid);
+
+		/* set the audio parameters from the EDID */
+		if (priv->is_hdmi_sink) {
+			drm_edid_to_eld(connector, edid);
+			tda998x_set_audio(&priv->audio, connector);
+		}
+
 		kfree(edid);
 	}
 
@@ -1167,12 +1282,14 @@ tda998x_encoder_set_property(struct drm_encoder *encoder,
 
 static void tda998x_destroy(struct tda998x_priv *priv)
 {
+	struct i2c_client *client = priv->hdmi;
+
 	/* disable all IRQs and free the IRQ handler */
 	cec_write(priv, REG_CEC_RXSHPDINTENA, 0);
 	reg_clear(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD);
 	if (priv->hdmi->irq)
 		free_irq(priv->hdmi->irq, priv);
-
+	hdmi2_codec_unregister(&client->dev);
 	i2c_unregister_device(priv->cec);
 }
 
@@ -1260,6 +1377,9 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv)
 	priv->vip_cntrl_1 = VIP_CNTRL_1_SWAP_C(0) | VIP_CNTRL_1_SWAP_D(1);
 	priv->vip_cntrl_2 = VIP_CNTRL_2_SWAP_E(4) | VIP_CNTRL_2_SWAP_F(5);
 
+	priv->params.audio_frame[1] = 1;		/* channels - 1 */
+	priv->audio.sample_rate = 48000;		/* 48kHz */
+
 	priv->current_page = 0xff;
 	priv->hdmi = client;
 	priv->cec = i2c_new_dummy(client->adapter, 0x34);
@@ -1351,6 +1471,12 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv)
 	/* enable EDID read irq: */
 	reg_set(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD);
 
+	/* register the HDMI2 audio CODEC */
+	priv->audio.start = tda998x_audio_start;
+	priv->audio.stop = tda998x_audio_stop;
+	i2c_set_clientdata(client, &priv->audio);
+	hdmi2_codec_register(&client->dev);
+
 	if (!np)
 		return 0;		/* non-DT */
 
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v4 2/2] drm/i2c:tda998x: Use the HDMI2 audio CODEC
@ 2014-08-31 12:35   ` Jean-Francois Moine
  0 siblings, 0 replies; 7+ messages in thread
From: Jean-Francois Moine @ 2014-08-31 12:35 UTC (permalink / raw)
  To: Mark Brown, Russell King - ARM Linux
  Cc: devicetree, alsa-devel, Andrew Jackson, linux-kernel, dri-devel

This patch adds an audio CODEC function to the NXP TDA998x transmitter.

Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
 drivers/gpu/drm/i2c/Kconfig       |   1 +
 drivers/gpu/drm/i2c/tda998x_drv.c | 142 +++++++++++++++++++++++++++++++++++---
 2 files changed, 135 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/i2c/Kconfig b/drivers/gpu/drm/i2c/Kconfig
index 4d341db..fa79cd3 100644
--- a/drivers/gpu/drm/i2c/Kconfig
+++ b/drivers/gpu/drm/i2c/Kconfig
@@ -22,6 +22,7 @@ config DRM_I2C_SIL164
 config DRM_I2C_NXP_TDA998X
 	tristate "NXP Semiconductors TDA998X HDMI encoder"
 	default m if DRM_TILCDC
+	select SND_SOC_HDMI2
 	help
 	  Support for NXP Semiconductors TDA998X HDMI encoders.
 
diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index d476279..5f86b4d 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -27,6 +27,9 @@
 #include <drm/drm_edid.h>
 #include <drm/i2c/tda998x.h>
 
+#include <sound/pcm_params.h>
+#include <sound/hdmi2.h>
+
 #define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
 
 struct tda998x_priv {
@@ -44,9 +47,13 @@ struct tda998x_priv {
 	wait_queue_head_t wq_edid;
 	volatile int wq_edid_wait;
 	struct drm_encoder *encoder;
+
+	struct hdmi2_codec audio;
 };
 
 #define to_tda998x_priv(x)  ((struct tda998x_priv *)to_encoder_slave(x)->slave_priv)
+#define audio_priv(x) \
+		container_of(audio, struct tda998x_priv, audio)
 
 /* The TDA9988 series of devices use a paged register scheme.. to simplify
  * things we encode the page # in upper bits of the register #.  To read/
@@ -639,27 +646,42 @@ static void
 tda998x_configure_audio(struct tda998x_priv *priv,
 		struct drm_display_mode *mode, struct tda998x_encoder_params *p)
 {
-	uint8_t buf[6], clksel_aip, clksel_fs, cts_n, adiv;
+	uint8_t buf[6], clksel_aip, clksel_fs, cts_n, adiv, aclk;
 	uint32_t n;
 
 	/* Enable audio ports */
 	reg_write(priv, REG_ENA_AP, p->audio_cfg);
-	reg_write(priv, REG_ENA_ACLK, p->audio_clk_cfg);
 
 	/* Set audio input source */
-	switch (p->audio_format) {
-	case AFMT_SPDIF:
+	switch (priv->audio.source) {
+	case HDMI2_SPDIF:
 		reg_write(priv, REG_MUX_AP, MUX_AP_SELECT_SPDIF);
 		clksel_aip = AIP_CLKSEL_AIP_SPDIF;
 		clksel_fs = AIP_CLKSEL_FS_FS64SPDIF;
 		cts_n = CTS_N_M(3) | CTS_N_K(3);
+		aclk = 0;				/* no clock */
 		break;
 
-	case AFMT_I2S:
+	case HDMI2_I2S:
 		reg_write(priv, REG_MUX_AP, MUX_AP_SELECT_I2S);
 		clksel_aip = AIP_CLKSEL_AIP_I2S;
 		clksel_fs = AIP_CLKSEL_FS_ACLK;
 		cts_n = CTS_N_M(3) | CTS_N_K(3);
+		/* with I2S input, the CTS_N predivider depends on
+		 * the sample width */
+		switch (priv->audio.sample_format) {
+		case SNDRV_PCM_FORMAT_S16_LE:
+			cts_n = CTS_N_M(3) | CTS_N_K(1);
+			break;
+		case SNDRV_PCM_FORMAT_S24_LE:
+			cts_n = CTS_N_M(3) | CTS_N_K(2);
+			break;
+		default:
+		case SNDRV_PCM_FORMAT_S32_LE:
+			cts_n = CTS_N_M(3) | CTS_N_K(3);
+			break;
+		}
+		aclk = 1;				/* clock enable */
 		break;
 
 	default:
@@ -671,6 +693,7 @@ tda998x_configure_audio(struct tda998x_priv *priv,
 	reg_clear(priv, REG_AIP_CNTRL_0, AIP_CNTRL_0_LAYOUT |
 					AIP_CNTRL_0_ACR_MAN);	/* auto CTS */
 	reg_write(priv, REG_CTS_N, cts_n);
+	reg_write(priv, REG_ENA_ACLK, aclk);
 
 	/*
 	 * Audio input somehow depends on HDMI line rate which is
@@ -684,7 +707,7 @@ tda998x_configure_audio(struct tda998x_priv *priv,
 		adiv++;			/* AUDIO_DIV_SERCLK_16 */
 
 	/* S/PDIF asks for a larger divider */
-	if (p->audio_format == AFMT_SPDIF)
+	if (priv->audio.source == HDMI2_SPDIF)
 		adiv++;			/* AUDIO_DIV_SERCLK_16 or _32 */
 
 	reg_write(priv, REG_AUDIO_DIV, adiv);
@@ -693,7 +716,7 @@ tda998x_configure_audio(struct tda998x_priv *priv,
 	 * This is the approximate value of N, which happens to be
 	 * the recommended values for non-coherent clocks.
 	 */
-	n = 128 * p->audio_sample_rate / 1000;
+	n = 128 * priv->audio.sample_rate / 1000;
 
 	/* Write the CTS and N values */
 	buf[0] = 0x44;
@@ -727,6 +750,30 @@ tda998x_configure_audio(struct tda998x_priv *priv,
 	tda998x_write_aif(priv, p);
 }
 
+/* hdmi2 codec interface */
+void tda998x_audio_start(struct hdmi2_codec *audio,
+			 int full)
+{
+	struct tda998x_priv *priv = audio_priv(audio);
+	struct tda998x_encoder_params *p = &priv->params;
+
+	if (!priv->encoder->crtc)
+		return;
+	p->audio_cfg = audio->ports[audio->source];
+	if (!full) {
+		reg_write(priv, REG_ENA_AP, p->audio_cfg);
+		return;
+	}
+	tda998x_configure_audio(priv, &priv->encoder->crtc->hwmode, p);
+}
+
+void tda998x_audio_stop(struct hdmi2_codec *audio)
+{
+	struct tda998x_priv *priv = audio_priv(audio);
+
+	reg_write(priv, REG_ENA_AP, 0);
+}
+
 /* DRM encoder functions */
 
 static void tda998x_encoder_set_config(struct tda998x_priv *priv,
@@ -746,6 +793,9 @@ static void tda998x_encoder_set_config(struct tda998x_priv *priv,
 			    (p->mirr_f ? VIP_CNTRL_2_MIRR_F : 0);
 
 	priv->params = *p;
+	priv->audio.source = p->audio_format == AFMT_I2S ?
+					HDMI2_I2S : HDMI2_SPDIF;
+	priv->audio.sample_rate = p->audio_sample_rate;
 }
 
 static void tda998x_encoder_dpms(struct tda998x_priv *priv, int mode)
@@ -1128,6 +1178,64 @@ fail:
 	return NULL;
 }
 
+static void tda998x_set_audio(struct hdmi2_codec *audio,
+				struct drm_connector *connector)
+{
+	u8 *eld = connector->eld;
+	u8 *sad;
+	int sad_count;
+	unsigned eld_ver, mnl, rate_mask;
+	unsigned max_channels, fmt;
+	u64 formats;
+	struct snd_pcm_hw_constraint_list *rate_constraints =
+			&audio->rate_constraints;
+	static const u32 hdmi_rates[] = {
+		32000, 44100, 48000, 88200, 96000, 176400, 192000
+	};
+
+	/* adjust the hw params from the ELD (EDID) */
+	eld_ver = eld[0] >> 3;
+	if (eld_ver != 2 && eld_ver != 31)
+		return;
+
+	mnl = eld[4] & 0x1f;
+	if (mnl > 16)
+		return;
+
+	sad_count = eld[5] >> 4;
+	sad = eld + 20 + mnl;
+
+	/* Start from the basic audio settings */
+	max_channels = 2;
+	rate_mask = 0;
+	fmt = 0;
+	while (sad_count--) {
+		switch (sad[0] & 0x78) {
+		case 0x08: /* PCM */
+			max_channels = max(max_channels, (sad[0] & 7) + 1u);
+			rate_mask |= sad[1];
+			fmt |= sad[2] & 0x07;
+			break;
+		}
+		sad += 3;
+	}
+
+	/* set the constraints */
+	rate_constraints->list = hdmi_rates;
+	rate_constraints->count = ARRAY_SIZE(hdmi_rates);
+	rate_constraints->mask = rate_mask;
+
+	formats = 0;
+	if (fmt & 1)
+		formats |= SNDRV_PCM_FMTBIT_S16_LE;
+	if (fmt & 2)
+		formats |= SNDRV_PCM_FMTBIT_S20_3LE;
+	if (fmt & 4)
+		formats |= SNDRV_PCM_FMTBIT_S24_LE;
+	audio->formats = formats;
+	audio->max_channels = max_channels;
+}
+
 static int
 tda998x_encoder_get_modes(struct tda998x_priv *priv,
 			  struct drm_connector *connector)
@@ -1139,6 +1247,13 @@ tda998x_encoder_get_modes(struct tda998x_priv *priv,
 		drm_mode_connector_update_edid_property(connector, edid);
 		n = drm_add_edid_modes(connector, edid);
 		priv->is_hdmi_sink = drm_detect_hdmi_monitor(edid);
+
+		/* set the audio parameters from the EDID */
+		if (priv->is_hdmi_sink) {
+			drm_edid_to_eld(connector, edid);
+			tda998x_set_audio(&priv->audio, connector);
+		}
+
 		kfree(edid);
 	}
 
@@ -1167,12 +1282,14 @@ tda998x_encoder_set_property(struct drm_encoder *encoder,
 
 static void tda998x_destroy(struct tda998x_priv *priv)
 {
+	struct i2c_client *client = priv->hdmi;
+
 	/* disable all IRQs and free the IRQ handler */
 	cec_write(priv, REG_CEC_RXSHPDINTENA, 0);
 	reg_clear(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD);
 	if (priv->hdmi->irq)
 		free_irq(priv->hdmi->irq, priv);
-
+	hdmi2_codec_unregister(&client->dev);
 	i2c_unregister_device(priv->cec);
 }
 
@@ -1260,6 +1377,9 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv)
 	priv->vip_cntrl_1 = VIP_CNTRL_1_SWAP_C(0) | VIP_CNTRL_1_SWAP_D(1);
 	priv->vip_cntrl_2 = VIP_CNTRL_2_SWAP_E(4) | VIP_CNTRL_2_SWAP_F(5);
 
+	priv->params.audio_frame[1] = 1;		/* channels - 1 */
+	priv->audio.sample_rate = 48000;		/* 48kHz */
+
 	priv->current_page = 0xff;
 	priv->hdmi = client;
 	priv->cec = i2c_new_dummy(client->adapter, 0x34);
@@ -1351,6 +1471,12 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv)
 	/* enable EDID read irq: */
 	reg_set(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD);
 
+	/* register the HDMI2 audio CODEC */
+	priv->audio.start = tda998x_audio_start;
+	priv->audio.stop = tda998x_audio_stop;
+	i2c_set_clientdata(client, &priv->audio);
+	hdmi2_codec_register(&client->dev);
+
 	if (!np)
 		return 0;		/* non-DT */
 
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v4 0/2] ASoC: tda998x: add a codec to the HDMI transmitter
@ 2014-08-31 13:02 ` Jean-Francois Moine
  0 siblings, 0 replies; 7+ messages in thread
From: Jean-Francois Moine @ 2014-08-31 13:02 UTC (permalink / raw)
  To: Mark Brown, Russell King - ARM Linux
  Cc: Andrew Jackson, alsa-devel, devicetree, dri-devel, linux-kernel

The NXP TDA998x HDMI transmitter may transmit audio to the HDMI link
from 2 different sources, I2S and S/PDIF.

This patch set adds a generic HDMI CODEC module which is used by
the TDA998x driver to update the audio constraints from the display
characteristics (EDID) and by the audio subsystem to connect the chosen
audio source to the HDMI link.

v4:
	- remove all the TDA998x specific stuff from the CODEC
	- move the EDID scan from the CODEC to the TDA998x
	- move the CODEC to sound/soc (Mark Brown)
	- update the audio_sample_rate from the EDID (Andrew Jackson)
v3: fix bad rate (Andrew Jackson)
v2: check double stream start (Mark Brown)

Jean-Francois Moine (2):
  ASoC:codecs: Add a generic HDMI audio CODEC
  drm/i2c:tda998x: Use the HDMI2  audio CODEC

 Documentation/devicetree/bindings/sound/hdmi2.txt |  32 ++++
 drivers/gpu/drm/i2c/Kconfig                       |   1 +
 drivers/gpu/drm/i2c/tda998x_drv.c                 | 142 ++++++++++++++-
 include/sound/hdmi2.h                             |  24 +++
 sound/soc/codecs/Kconfig                          |   3 +
 sound/soc/codecs/Makefile                         |   2 +
 sound/soc/codecs/hdmi2.c                          | 204 ++++++++++++++++++++++
 7 files changed, 400 insertions(+), 8 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/sound/hdmi2.txt
 create mode 100644 include/sound/hdmi2.h
 create mode 100644 sound/soc/codecs/hdmi2.c

-- 
2.1.0


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v4 0/2] ASoC: tda998x: add a codec to the HDMI transmitter
@ 2014-08-31 13:02 ` Jean-Francois Moine
  0 siblings, 0 replies; 7+ messages in thread
From: Jean-Francois Moine @ 2014-08-31 13:02 UTC (permalink / raw)
  To: Mark Brown, Russell King - ARM Linux
  Cc: devicetree, alsa-devel, Andrew Jackson, linux-kernel, dri-devel

The NXP TDA998x HDMI transmitter may transmit audio to the HDMI link
from 2 different sources, I2S and S/PDIF.

This patch set adds a generic HDMI CODEC module which is used by
the TDA998x driver to update the audio constraints from the display
characteristics (EDID) and by the audio subsystem to connect the chosen
audio source to the HDMI link.

v4:
	- remove all the TDA998x specific stuff from the CODEC
	- move the EDID scan from the CODEC to the TDA998x
	- move the CODEC to sound/soc (Mark Brown)
	- update the audio_sample_rate from the EDID (Andrew Jackson)
v3: fix bad rate (Andrew Jackson)
v2: check double stream start (Mark Brown)

Jean-Francois Moine (2):
  ASoC:codecs: Add a generic HDMI audio CODEC
  drm/i2c:tda998x: Use the HDMI2  audio CODEC

 Documentation/devicetree/bindings/sound/hdmi2.txt |  32 ++++
 drivers/gpu/drm/i2c/Kconfig                       |   1 +
 drivers/gpu/drm/i2c/tda998x_drv.c                 | 142 ++++++++++++++-
 include/sound/hdmi2.h                             |  24 +++
 sound/soc/codecs/Kconfig                          |   3 +
 sound/soc/codecs/Makefile                         |   2 +
 sound/soc/codecs/hdmi2.c                          | 204 ++++++++++++++++++++++
 7 files changed, 400 insertions(+), 8 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/sound/hdmi2.txt
 create mode 100644 include/sound/hdmi2.h
 create mode 100644 sound/soc/codecs/hdmi2.c

-- 
2.1.0

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v4 1/2] ASoC:codecs: Add a generic HDMI audio CODEC
  2014-08-31 10:45   ` Jean-Francois Moine
  (?)
@ 2014-09-01 16:36   ` Mark Brown
  -1 siblings, 0 replies; 7+ messages in thread
From: Mark Brown @ 2014-09-01 16:36 UTC (permalink / raw)
  To: Jean-Francois Moine
  Cc: Russell King - ARM Linux, Andrew Jackson, alsa-devel, devicetree,
	dri-devel, linux-kernel

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

On Sun, Aug 31, 2014 at 12:45:39PM +0200, Jean-Francois Moine wrote:

>  Documentation/devicetree/bindings/sound/hdmi2.txt |  32 ++++
>  include/sound/hdmi2.h                             |  24 +++
>  sound/soc/codecs/Kconfig                          |   3 +
>  sound/soc/codecs/Makefile                         |   2 +
>  sound/soc/codecs/hdmi2.c                          | 204 ++++++++++++++++++++++
>  5 files changed, 265 insertions(+)

This is clearly not a good name and it's not clear what the difference
between this and the existing HDMI stub CODEC is intended to be.  

> +Required properties:
> +
> +  - audio-ports: must contain one or two HDMI transmitter dependant
> +	values identifying the audio sources.
> +	The source type is given by the corresponding entry in
> +	the audio-port-names property.
> +
> +  - audio-port-names: must contain entries matching the entries in
> +	the audio-ports property.
> +	Each value may be "i2s" or "spdif", giving the type of
> +	the associated audio port.

It seems hard to see this binding as really generic - I'd expect to see
other devices which are just able to have fixed audio ports for example.

> +static int hdmi2_probe(struct snd_soc_codec *codec)
> +{
> +	struct i2c_client *i2c_client = to_i2c_client(codec->dev);
> +	struct hdmi2_codec *audio = i2c_get_clientdata(i2c_client);
> +	struct device_node *np = codec->dev->of_node;
> +	int i, j, ret;
> +	const char *p;
> +
> +	if (!audio)
> +		return -ENODEV;
> +	snd_soc_codec_set_drvdata(codec, audio);

The code also seems pretty device specific.  I think it's probably
better to leave the binding device specific for now and concentrate on
sharing inside the kernel, making any generic binding additions be about
how the devices interface rather than what's going on inside a specific
device.

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

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2014-09-01 16:37 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-08-31 13:02 [PATCH v4 0/2] ASoC: tda998x: add a codec to the HDMI transmitter Jean-Francois Moine
2014-08-31 13:02 ` Jean-Francois Moine
2014-08-31 10:45 ` [PATCH v4 1/2] ASoC:codecs: Add a generic HDMI audio CODEC Jean-Francois Moine
2014-08-31 10:45   ` Jean-Francois Moine
2014-09-01 16:36   ` Mark Brown
2014-08-31 12:35 ` [PATCH v4 2/2] drm/i2c:tda998x: Use the HDMI2 " Jean-Francois Moine
2014-08-31 12:35   ` Jean-Francois Moine

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.