Skip to contentSkip to conversation

ASoC: tda998x: add a codec to the HDMI transmitter

PATCH New Jean-Francois Moine 5 participants 13 messages May 8, 2015 – Jul 28, 2015 5 messages not in the archive

Subsystems (5)
lore
Share & tools
lore
#0 lore raw

v13 cover ASoC: tda998x: add a codec to the HDMI transmitter

This patch series asks for Russell King's patch

	http://article.gmane.org/gmane.linux.ports.arm.kernel/411574
	drm/edid: add function to help find SADs

to be applayed.

v13:
- rebase on 4.2.0-rc3
- remove Russell's patches
v12:
- use Russell King's DRM ELD helper (Mark Brown)
v11:
- reduce the patch series to adding the tda998x codec only
v10:
- add the generic dt-card
- define the audio ports from a DT graph of ports (Russell King)
- reuse HDMI constants (Andrew Jackson - Jyri Sarha)
- alloc rate_constraints in codec (Jyri Sarha)
- fix bad number of channels (Jyri Sarha)
- correct codec generation from config (Russell King - Jyri Sarha)
- no module init/exit (Russell King)
v9:
- back to a TDA998x specific CODEC
- more comments
- change magic values to constants
v8:
- change some comments about the patches
v7:
- remove the change of the K predivider (Jyri Sarha)
- add S24_3LE and S32_LE as possible audio formats (Jyri Sarha)
- don't move the struct priv2 definition and use the slave encoder private data as the device private data (Russell King)
- remove the useless request_module (Russell King/Mark Brown)
- don't lock the HDMI module (Russell King)
- use platform_device_unregister to remove the codec (Russell King)
v6:
- extend the HDMI CODEC instead of using a specific CODEC
v5:
- use the TDA998x private data instead of a specific area for the CODEC interface
- the CODEC is TDA998x specific (Mark Brown)
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 (3):
drm/i2c: tda998x: Add support of a DT graph of ports drm/i2c: tda998x: Change drvdata for audio extension
ASoC: tda998x: add a codec to the HDMI transmitter

 .../devicetree/bindings/drm/i2c/tda998x.txt        |  51 ++++++
 drivers/gpu/drm/i2c/tda998x_drv.c                  | 178 +++++++++++++++++++--
 include/sound/tda998x.h                            |  23 +++
 sound/soc/codecs/Kconfig                           |   6 +
 sound/soc/codecs/Makefile                          |   2 +
 sound/soc/codecs/tda998x.c                         | 132 +++++++++++++++
 6 files changed, 381 insertions(+), 11 deletions(-)
 create mode 100644 include/sound/tda998x.h
 create mode 100644 sound/soc/codecs/tda998x.c
Signature · 1 line
2.1.4

A message that is not in the archive (sent off-list or not yet received): [email protected]

#2 lore raw

PATCH v13 1/3 drm/i2c: tda998x: Add support of a DT graph of ports

Two kinds of ports may be declared in a DT graph of ports: video and audio. This patch accepts the port value from a video port as an alternative to the video-ports property.
It also accepts audio ports in the case the transmitter is not used as a slave encoder.
The new file include/sound/tda998x.h prepares to the definition of a tda998x CODEC.

---
 .../devicetree/bindings/drm/i2c/tda998x.txt        | 51 ++++++++++++
 drivers/gpu/drm/i2c/tda998x_drv.c                  | 90 +++++++++++++++++++---
 include/sound/tda998x.h                            |  8 ++
 3 files changed, 140 insertions(+), 9 deletions(-)
 create mode 100644 include/sound/tda998x.h
Documentation/devicetree/bindings/drm/i2c/tda998x.txt +51 -0
index e9e4bce..386b6c3 100644
@@ -16,6 +16,35 @@ Optional properties:
 
   - video-ports: 24 bits value which defines how the video controller
 	output is wired to the TDA998x input - default: <0x230145>
+	This property is not used when ports are defined.
+
+Optional nodes:
+
+  - port: up to three ports.
+	The ports are defined according to [1].
+
+    Video port.
+	There may be only one video port.
+	This one must contain the following property:
+
+	- port-type: must be "rgb"
+
+	and may contain the optional property:
+
+	- reg: 24 bits value which defines how the video controller
+		output is wired to the TDA998x input (video pins)
+		When absent, the default value is <0x230145>.
+
+    Audio ports.
+	There may be one or two audio ports.
+	These ones must contain the following properties:
+
+	- port-type: must be "i2s" or "spdif"
+
+	- reg: 8 bits value which defines how the audio controller
+		output is wired to the TDA998x input (audio pins)
+
+[1] Documentation/devicetree/bindings/graph.txt
 
 Example:
 
@@ -26,4 +55,26 @@ Example:
 		interrupts = <27 2>;		/* falling edge */
 		pinctrl-0 = <&pmx_camera>;
 		pinctrl-names = "default";
+
+		port@230145 {
+			port-type = "rgb";
+			reg = <0x230145>;
+			hdmi_0: endpoint {
+				remote-endpoint = <&lcd0_0>;
+			};
+		};
+		port@3 {			/* AP1 = I2S */
+			port-type = "i2s";
+			reg = <0x03>;
+			tda998x_i2s: endpoint {
+				remote-endpoint = <&audio1_i2s>;
+			};
+		};
+		port@4 {			 /* AP2 = S/PDIF */
+			port-type = "spdif";
+			reg = <0x04>;
+			tda998x_spdif: endpoint {
+				remote-endpoint = <&audio1_spdif1>;
+			};
+		};
 	};
drivers/gpu/drm/i2c/tda998x_drv.c +81 -9

123 lines not on this page · raw message

include/sound/tda998x.h new file +8 -0
new file mode 100644
index 0000000..487a809
@@ -0,0 +1,8 @@
+#ifndef SND_TDA998X_H
+#define SND_TDA998X_H
+
+struct tda998x_audio_s {
+	u8 ports[2];			/* AP value */
+	u8 port_types[2];		/* AFMT_xxx */
+};
+#endif
Signature · 6 lines
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#3 lore raw

PATCH v13 2/3 drm/i2c: tda998x: Change drvdata for audio extension

In reply to a message not in the archive

The device drvdata is used for component bind, but points to the encoder/connector structure which is hidden from the slave encoder. For audio extension, the slave encoder private data must be accessible, so, this patch changes drvdata to the slave encoder private data and sets it in case of slave encoder use.

---
 drivers/gpu/drm/i2c/tda998x_drv.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)
drivers/gpu/drm/i2c/tda998x_drv.c +6 -2
index b0a730a..655ebb0 100644
@@ -1453,6 +1453,8 @@ static int tda998x_encoder_init(struct i2c_client *client,
 	encoder_slave->slave_priv = priv;
 	encoder_slave->slave_funcs = &tda998x_encoder_slave_funcs;
 
+	dev_set_drvdata(&client->dev, priv);
+
 	return 0;
 }
 
@@ -1580,7 +1582,7 @@ static int tda998x_bind(struct device *dev, struct device *master, void *data)
 	if (!priv)
 		return -ENOMEM;
 
-	dev_set_drvdata(dev, priv);
+	dev_set_drvdata(dev, &priv->base);
 
 	if (dev->of_node)
 		crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
@@ -1639,7 +1641,9 @@ err_encoder:
 static void tda998x_unbind(struct device *dev, struct device *master,
 			   void *data)
 {
-	struct tda998x_priv2 *priv = dev_get_drvdata(dev);
+	struct tda998x_priv *priv_s = dev_get_drvdata(dev);
+	struct tda998x_priv2 *priv =
+			container_of(priv_s, struct tda998x_priv2, base);
 
 	drm_connector_cleanup(&priv->connector);
 	drm_encoder_cleanup(&priv->encoder);
Signature · 6 lines
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#4 lore raw

PATCH v13 3/3 ASoC: tda998x: add a codec to the HDMI transmitter

In reply to a message not in the archive

The tda998x CODEC maintains the audio constraints according to the HDMI device parameters (EDID) and sets dynamically the input ports in the TDA998x I2C driver on start/stop audio streaming.

---
 drivers/gpu/drm/i2c/tda998x_drv.c |  80 +++++++++++++++++++++++
 include/sound/tda998x.h           |  15 +++++
 sound/soc/codecs/Kconfig          |   6 ++
 sound/soc/codecs/Makefile         |   2 +
 sound/soc/codecs/tda998x.c        | 132 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 235 insertions(+)
 create mode 100644 sound/soc/codecs/tda998x.c
drivers/gpu/drm/i2c/tda998x_drv.c +80 -0
index 655ebb0..90a508f 100644
@@ -758,6 +758,66 @@ tda998x_configure_audio(struct tda998x_priv *priv,
 	tda998x_write_aif(priv, p);
 }
 
+#if IS_ENABLED(CONFIG_SND_SOC_TDA998X)
+/* tda998x audio codec interface */
+
+/* return the ELD to the CODEC */
+static u8 *tda998x_get_eld(struct device *dev)
+{
+	struct tda998x_priv *priv = dev_get_drvdata(dev);
+	struct drm_encoder *encoder = priv->encoder;
+	struct drm_device *drm = encoder->dev;
+	struct drm_connector *connector;
+
+	if (!priv->is_hdmi_sink
+	 || !encoder->crtc)
+		return NULL;
+
+	list_for_each_entry(connector, &drm->mode_config.connector_list, head) {
+		if (connector->encoder == encoder)
+			return connector->eld;
+	}
+	return NULL;
+}
+
+/* switch the audio port and initialize the audio parameters for streaming */
+static int tda998x_set_audio_input(struct device *dev,
+				int port_index,
+				unsigned sample_rate)
+{
+	struct tda998x_priv *priv = dev_get_drvdata(dev);
+	struct tda998x_encoder_params *p = &priv->params;
+
+	if (!priv->encoder->crtc)
+		return -ENODEV;
+
+	/* if no port, just disable the audio port */
+	if (port_index == PORT_NONE) {
+		reg_write(priv, REG_ENA_AP, 0);
+		return 0;
+	}
+
+	/* if same audio parameters, just enable the audio port */
+	if (p->audio_cfg == priv->audio.ports[port_index] &&
+	    p->audio_sample_rate == sample_rate) {
+		reg_write(priv, REG_ENA_AP, p->audio_cfg);
+		return 0;
+	}
+
+	p->audio_format = priv->audio.port_types[port_index];
+	p->audio_clk_cfg = p->audio_format == AFMT_SPDIF ? 0 : 1;
+	p->audio_cfg = priv->audio.ports[port_index];
+	p->audio_sample_rate = sample_rate;
+	tda998x_configure_audio(priv, &priv->encoder->crtc->hwmode, p);
+	return 0;
+}
+
+static struct tda998x_ops_s tda998x_codec_ops = {
+	.get_eld = tda998x_get_eld,
+	.set_audio_input = tda998x_set_audio_input,
+};
+#endif /* SND_SOC */
+
 /* DRM encoder functions */
 
 static void tda998x_encoder_set_config(struct tda998x_priv *priv,
@@ -1123,6 +1183,12 @@ 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);
+
+#if IS_ENABLED(CONFIG_SND_SOC_TDA998X)
+	if (priv->is_hdmi_sink)
+		drm_edid_to_eld(connector, edid);
+#endif
+
 	kfree(edid);
 
 	return n;
@@ -1158,6 +1224,10 @@ static void tda998x_destroy(struct tda998x_priv *priv)
 		cancel_delayed_work_sync(&priv->dwork);
 	}
 
+#if IS_ENABLED(CONFIG_SND_SOC_TDA998X)
+	if (priv->audio.ports[0])
+		tda9998x_codec_unregister(&priv->hdmi->dev);
+#endif
 	i2c_unregister_device(priv->cec);
 }
 
@@ -1294,6 +1364,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->params.audio_sample_rate = 48000;		/* 48kHz */
+
 	priv->current_page = 0xff;
 	priv->hdmi = client;
 	/* CEC I2C address bound to TDA998x I2C addr by configuration pins */
@@ -1407,6 +1480,13 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv)
 				priv->params.audio_clk_cfg =
 					priv->params.audio_format ==
 							AFMT_SPDIF ? 0 : 1;
+
+#if IS_ENABLED(CONFIG_SND_SOC_TDA998X)
+				/* and create the audio CODEC */
+				tda9998x_codec_register(&client->dev,
+							&priv->audio,
+							&tda998x_codec_ops);
+#endif
 			}
 		} else {
 
include/sound/tda998x.h +15 -0
index 487a809..8e89db4 100644
@@ -1,8 +1,23 @@
 #ifndef SND_TDA998X_H
 #define SND_TDA998X_H
 
+/* port index for audio stream stop */
+#define PORT_NONE (-1)
+
 struct tda998x_audio_s {
 	u8 ports[2];			/* AP value */
 	u8 port_types[2];		/* AFMT_xxx */
 };
+
+struct tda998x_ops_s {
+	u8 *(*get_eld)(struct device *dev);
+	int (*set_audio_input)(struct device *dev,
+			int port_index,
+			unsigned sample_rate);
+};
+
+int tda9998x_codec_register(struct device *dev,
+			struct tda998x_audio_s *tda998x_audio_i,
+			struct tda998x_ops_s *tda998x_ops);
+void tda9998x_codec_unregister(struct device *dev);
 #endif
sound/soc/codecs/Kconfig +6 -0
index efaafce..08b73bd 100644
@@ -105,6 +105,7 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_TAS2552 if I2C
 	select SND_SOC_TAS5086 if I2C
 	select SND_SOC_TAS571X if I2C
+	select SND_SOC_TDA998X if DRM_I2C_NXP_TDA998X
 	select SND_SOC_TFA9879 if I2C
 	select SND_SOC_TLV320AIC23_I2C if I2C
 	select SND_SOC_TLV320AIC23_SPI if SPI_MASTER
@@ -622,6 +623,11 @@ config SND_SOC_TAS571X
 	tristate "Texas Instruments TAS5711/TAS5717/TAS5719 power amplifiers"
 	depends on I2C
 
+config SND_SOC_TDA998X
+	def_tristate y
+	select SND_PCM_ELD
+	depends on DRM_I2C_NXP_TDA998X
+
 config SND_SOC_TFA9879
 	tristate "NXP Semiconductors TFA9879 amplifier"
 	depends on I2C
sound/soc/codecs/Makefile +2 -0
index cf160d9..819d689 100644
@@ -108,6 +108,7 @@ snd-soc-sta529-objs := sta529.o
 snd-soc-stac9766-objs := stac9766.o
 snd-soc-tas5086-objs := tas5086.o
 snd-soc-tas571x-objs := tas571x.o
+snd-soc-tda998x-objs := tda998x.o
 snd-soc-tfa9879-objs := tfa9879.o
 snd-soc-tlv320aic23-objs := tlv320aic23.o
 snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o
@@ -292,6 +293,7 @@ obj-$(CONFIG_SND_SOC_STAC9766)	+= snd-soc-stac9766.o
 obj-$(CONFIG_SND_SOC_TAS2552)	+= snd-soc-tas2552.o
 obj-$(CONFIG_SND_SOC_TAS5086)	+= snd-soc-tas5086.o
 obj-$(CONFIG_SND_SOC_TAS571X)	+= snd-soc-tas571x.o
+obj-$(CONFIG_SND_SOC_TDA998X)	+= snd-soc-tda998x.o
 obj-$(CONFIG_SND_SOC_TFA9879)	+= snd-soc-tfa9879.o
 obj-$(CONFIG_SND_SOC_TLV320AIC23)	+= snd-soc-tlv320aic23.o
 obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C)	+= snd-soc-tlv320aic23-i2c.o
sound/soc/codecs/tda998x.c new file +132 -0

132 lines not on this page · raw message

Signature · 6 lines
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

A message that is not in the archive (sent off-list or not yet received): e036c88aa945e72b40ec965c9358dacf564e79f2.1437397272.git.moinejf-GANU6spQydw@public.gmane.org

#6 lore raw

drivers/gpu/drm/i2c/tda998x_drv.c tda998x_configure_audio(struct tda998x_priv *priv, · line 774

Quoted 4 lines · Jean-Francois Moine
drivers/gpu/drm/i2c/tda998x_drv.c +4 -0
+
+	if (!priv->is_hdmi_sink
+	 || !encoder->crtc)
+		return NULL;

That's weird indentation.

drivers/gpu/drm/i2c/tda998x_drv.c tda998x_configure_audio(struct tda998x_priv *priv, · line 779

Quoted 4 lines
drivers/gpu/drm/i2c/tda998x_drv.c +4 -0
+	list_for_each_entry(connector, &drm->mode_config.connector_list, head) {
+		if (connector->encoder == encoder)
+			return connector->eld;
+	}

What guarantees that connector->eld stays valid after it's returned?

include/sound/tda998x.h line 13

Quoted 2 lines
include/sound/tda998x.h +2 -0
+struct tda998x_ops_s {
+	u8 *(*get_eld)(struct device *dev);

Why _ops_s - what does the _s mean? If it's sound perhaps it makes sense to spell it out so people aren't guessing.

include/sound/tda998x.h line 22

Quoted 4 lines
include/sound/tda998x.h +4 -0
+int tda9998x_codec_register(struct device *dev,
+			struct tda998x_audio_s *tda998x_audio_i,
+			struct tda998x_ops_s *tda998x_ops);
+void tda9998x_codec_unregister(struct device *dev);

I'd expect these to be internal to the DRM driver.

sound/soc/codecs/Kconfig config SND_SOC_TAS571X · line 630

Quoted 5 lines
sound/soc/codecs/Kconfig +5 -0
+config SND_SOC_TDA998X
+	def_tristate y
+	select SND_PCM_ELD
+	depends on DRM_I2C_NXP_TDA998X
+

def_tristate y? Why?

sound/soc/codecs/tda998x.c line 20

Quoted 2 lines
sound/soc/codecs/tda998x.c new file +2 -0
+/* functions in tda998x_drv */
+static struct tda998x_ops_s *tda998x_ops;

I'd expect this to be stored in the driver data rather than a static global, what if a system has two HDMI outputs?

sound/soc/codecs/tda998x.c line 32

Quoted 11 lines
sound/soc/codecs/tda998x.c new file +11 -0
+static int tda998x_codec_startup(struct snd_pcm_substream *substream,
+                       struct snd_soc_dai *dai)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       u8 *eld;
+
+       eld = tda998x_ops->get_eld(dai->dev);
+       if (!eld)
+               return -ENODEV;
+       return snd_pcm_hw_constraint_eld(runtime, eld);
+}

Do we really need a device specific mechanism for fishing the ELD out of the graphics code? I'd have expected this to be more generic.

sound/soc/codecs/tda998x.c line 41

Quoted 8 lines
sound/soc/codecs/tda998x.c new file +8 -0
+/* ask the HDMI transmitter to activate the audio input port */
+static int tda998x_codec_hw_params(struct snd_pcm_substream *substream,
+				   struct snd_pcm_hw_params *params,
+				   struct snd_soc_dai *dai)
+{
+	return tda998x_ops->set_audio_input(dai->dev, dai->id,
+					params_rate(params));
+}

The set_audio_input() function doesn't appear to have anything that checks if the device is busy before enabling things, what happens if the user tries to switch between I2S and S/PDIF? It looks like only one DAI can be active at once.

sound/soc/codecs/tda998x.c line 116

Quoted 10 lines
sound/soc/codecs/tda998x.c new file +10 -0
+	for (i = 0, p_dai = dais; i < ndais ; i++, p_dai++) {
+		memcpy(p_dai, &tda998x_dai_i2s, sizeof(*p_dai));
+		p_dai->id = i;
+		if (tda998x_audio->port_types[i] == AFMT_SPDIF) {
+			p_dai->name = "spdif-hifi";
+			p_dai->playback.stream_name = "HDMI SPDIF Playback";
+			p_dai->playback.channels_max = 2;
+			p_dai->playback.rate_min = 22050;
+		}
+	}

It would be a bit clearer if the template were just a template and this copying initialised both I2S and S/PDIF specific settings.

sound/soc/codecs/tda998x.c line 122

Quoted 5 lines
sound/soc/codecs/tda998x.c new file +5 -0
+	return snd_soc_register_codec(dev,
+				&tda998x_codec_drv,
+				dais, ndais);
+}
+EXPORT_SYMBOL(tda9998x_codec_register);

ASoC is all EXPORT_SYMBOL_GPL, you shouldn't reexport functionality with plain EXPORT_SYMBOL.

A message that is not in the archive (sent off-list or not yet received): [email protected]

#8 lore raw
Quoted 8 lines · On Mon, 20 Jul 2015 19:06:06 +0100 Mark Brown…

drivers/gpu/drm/i2c/tda998x_drv.c tda998x_configure_audio(struct tda998x_priv *priv, · line 774

Quoted 4 lines · Jean-Francois Moine
drivers/gpu/drm/i2c/tda998x_drv.c +4 -0
+
+	if (!priv->is_hdmi_sink
+	 || !encoder->crtc)
+		return NULL;

That's weird indentation.

But the conditions are aligned... This sequence will be removed.

Quoted 6 lines

drivers/gpu/drm/i2c/tda998x_drv.c tda998x_configure_audio(struct tda998x_priv *priv, · line 779

Quoted 4 lines
drivers/gpu/drm/i2c/tda998x_drv.c +4 -0
+	list_for_each_entry(connector, &drm->mode_config.connector_list, head) {
+		if (connector->encoder == encoder)
+			return connector->eld;
+	}

What guarantees that connector->eld stays valid after it's returned?

You are right, the ELD content may change. I will use a pointer to a stable content.

Quoted 5 lines

include/sound/tda998x.h line 13

Quoted 2 lines
include/sound/tda998x.h +2 -0
+struct tda998x_ops_s {
+	u8 *(*get_eld)(struct device *dev);

Why _ops_s - what does the _s mean? If it's sound perhaps it makes sense to spell it out so people aren't guessing.

I will remove it.

Quoted 6 lines

include/sound/tda998x.h line 22

Quoted 4 lines
include/sound/tda998x.h +4 -0
+int tda9998x_codec_register(struct device *dev,
+			struct tda998x_audio_s *tda998x_audio_i,
+			struct tda998x_ops_s *tda998x_ops);
+void tda9998x_codec_unregister(struct device *dev);

I'd expect these to be internal to the DRM driver.

I don't see where. What is your idea?

Quoted 7 lines

sound/soc/codecs/Kconfig config SND_SOC_TAS571X · line 630

Quoted 5 lines
sound/soc/codecs/Kconfig +5 -0
+config SND_SOC_TDA998X
+	def_tristate y
+	select SND_PCM_ELD
+	depends on DRM_I2C_NXP_TDA998X
+

def_tristate y? Why?

The TDA998x CODEC is always generated when the DRM TDA998x is generated and when audio is wanted.
Its type, built-in or module, depends on the TDA998x driver type.

Quoted 5 lines

sound/soc/codecs/tda998x.c line 20

Quoted 2 lines
sound/soc/codecs/tda998x.c new file +2 -0
+/* functions in tda998x_drv */
+static struct tda998x_ops_s *tda998x_ops;

I'd expect this to be stored in the driver data rather than a static global, what if a system has two HDMI outputs?

Each TDA998x has only one HDMI output and there is only one driver.

I will put the pointer to the HDMI driver in the device private area.

Quoted 6 lines
Quoted 6 lines
sound/soc/codecs/tda998x.c new file +6 -0
+static int tda998x_codec_startup(struct snd_pcm_substream *substream,
+                       struct snd_soc_dai *dai)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       u8 *eld;
+
Quoted 8 lines

sound/soc/codecs/tda998x.c line 32

Quoted 5 lines
sound/soc/codecs/tda998x.c new file +5 -0
+       eld = tda998x_ops->get_eld(dai->dev);
+       if (!eld)
+               return -ENODEV;
+       return snd_pcm_hw_constraint_eld(runtime, eld);
+}

Do we really need a device specific mechanism for fishing the ELD out of the graphics code? I'd have expected this to be more generic.

I will put the ELD in the private data of the DRM driver.

Quoted 5 lines
Quoted 5 lines
sound/soc/codecs/tda998x.c new file +5 -0
+/* ask the HDMI transmitter to activate the audio input port */
+static int tda998x_codec_hw_params(struct snd_pcm_substream *substream,
+				   struct snd_pcm_hw_params *params,
+				   struct snd_soc_dai *dai)
+{
Quoted 8 lines

sound/soc/codecs/tda998x.c line 41

Quoted 3 lines
sound/soc/codecs/tda998x.c new file +3 -0
+	return tda998x_ops->set_audio_input(dai->dev, dai->id,
+					params_rate(params));
+}

The set_audio_input() function doesn't appear to have anything that checks if the device is busy before enabling things, what happens if the user tries to switch between I2S and S/PDIF? It looks like only one DAI can be active at once.

Right. I will check this double streaming.

Quoted 5 lines
Quoted 5 lines
sound/soc/codecs/tda998x.c new file +5 -0
+	for (i = 0, p_dai = dais; i < ndais ; i++, p_dai++) {
+		memcpy(p_dai, &tda998x_dai_i2s, sizeof(*p_dai));
+		p_dai->id = i;
+		if (tda998x_audio->port_types[i] == AFMT_SPDIF) {
+			p_dai->name = "spdif-hifi";
Quoted 8 lines

sound/soc/codecs/tda998x.c line 116

Quoted 5 lines
sound/soc/codecs/tda998x.c new file +5 -0
+			p_dai->playback.stream_name = "HDMI SPDIF Playback";
+			p_dai->playback.channels_max = 2;
+			p_dai->playback.rate_min = 22050;
+		}
+	}

It would be a bit clearer if the template were just a template and this copying initialised both I2S and S/PDIF specific settings.

OK, I will change this.

Quoted 8 lines

sound/soc/codecs/tda998x.c line 122

Quoted 5 lines
sound/soc/codecs/tda998x.c new file +5 -0
+	return snd_soc_register_codec(dev,
+				&tda998x_codec_drv,
+				dais, ndais);
+}
+EXPORT_SYMBOL(tda9998x_codec_register);

ASoC is all EXPORT_SYMBOL_GPL, you shouldn't reexport functionality with plain EXPORT_SYMBOL.

Sorry, bug of mine.

Signature · 6 lines
Ken ar c'hentañ	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#9 lore raw
Quoted 10 lines · Jean-Francois Moine
Quoted 6 lines · Mark Brown

include/sound/tda998x.h line 22

Quoted 4 lines
include/sound/tda998x.h +4 -0
+int tda9998x_codec_register(struct device *dev,
+			struct tda998x_audio_s *tda998x_audio_i,
+			struct tda998x_ops_s *tda998x_ops);
+void tda9998x_codec_unregister(struct device *dev);

I'd expect these to be internal to the DRM driver.

I don't see where. What is your idea?

What I said above - put them in the DRM driver. They're just registering a device IIRC.

Quoted 10 lines
Quoted 6 lines

sound/soc/codecs/Kconfig config SND_SOC_TAS571X · line 629

Quoted 4 lines
sound/soc/codecs/Kconfig +4 -0
+config SND_SOC_TDA998X
+	def_tristate y
+	select SND_PCM_ELD
+	depends on DRM_I2C_NXP_TDA998X

def_tristate y? Why?

The TDA998x CODEC is always generated when the DRM TDA998x is generated and when audio is wanted.
Its type, built-in or module, depends on the TDA998x driver type.

Just make this like any other driver with no default specified.

Quoted 7 lines
Quoted 5 lines

sound/soc/codecs/tda998x.c line 20

Quoted 2 lines
sound/soc/codecs/tda998x.c new file +2 -0
+/* functions in tda998x_drv */
+static struct tda998x_ops_s *tda998x_ops;

I'd expect this to be stored in the driver data rather than a static global, what if a system has two HDMI outputs?

Each TDA998x has only one HDMI output and there is only one driver.

Someone might put two chips on the board.

Quoted 15 lines
Quoted 6 lines
Quoted 6 lines
sound/soc/codecs/tda998x.c new file +6 -0
+static int tda998x_codec_startup(struct snd_pcm_substream *substream,
+                       struct snd_soc_dai *dai)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       u8 *eld;
+
Quoted 8 lines

sound/soc/codecs/tda998x.c line 32

Quoted 5 lines
sound/soc/codecs/tda998x.c new file +5 -0
+       eld = tda998x_ops->get_eld(dai->dev);
+       if (!eld)
+               return -ENODEV;
+       return snd_pcm_hw_constraint_eld(runtime, eld);
+}

Do we really need a device specific mechanism for fishing the ELD out of the graphics code? I'd have expected this to be more generic.

Quoted 1 line

I will put the ELD in the private data of the DRM driver.

That's not the issue here - the issue is that the need to get the ELD out of the video subsystem is not specific to this driver, it's pretty common and something that it seems we should factor out.

#10 lore raw
Quoted 10 lines · On Tue, 28 Jul 2015 11:24:10 +0100 Mark Brown <[email protected]> wrote:
Quoted 8 lines · Jean-Francois Moine
Quoted 6 lines · Mark Brown

include/sound/tda998x.h line 22

Quoted 4 lines
include/sound/tda998x.h +4 -0
+int tda9998x_codec_register(struct device *dev,
+			struct tda998x_audio_s *tda998x_audio_i,
+			struct tda998x_ops_s *tda998x_ops);
+void tda9998x_codec_unregister(struct device *dev);

I'd expect these to be internal to the DRM driver.

Quoted 4 lines
Quoted 1 line

I don't see where. What is your idea?

What I said above - put them in the DRM driver. They're just registering a device IIRC.

There is no CODEC device. The HDMI device supports both audio and video.

Quoted 7 lines
Quoted 6 lines
Quoted 6 lines

sound/soc/codecs/Kconfig config SND_SOC_TAS571X · line 629

Quoted 4 lines
sound/soc/codecs/Kconfig +4 -0
+config SND_SOC_TDA998X
+	def_tristate y
+	select SND_PCM_ELD
+	depends on DRM_I2C_NXP_TDA998X

def_tristate y? Why?

Quoted 5 lines
Quoted 3 lines

The TDA998x CODEC is always generated when the DRM TDA998x is generated and when audio is wanted.
Its type, built-in or module, depends on the TDA998x driver type.

Just make this like any other driver with no default specified.

If I understand correctly, you want that the user might choose to use the HDMI for video only and to have audio by some other mean. No problem.

Quoted 9 lines
Quoted 7 lines
Quoted 5 lines

sound/soc/codecs/tda998x.c line 20

Quoted 2 lines
sound/soc/codecs/tda998x.c new file +2 -0
+/* functions in tda998x_drv */
+static struct tda998x_ops_s *tda998x_ops;

I'd expect this to be stored in the driver data rather than a static global, what if a system has two HDMI outputs?

Each TDA998x has only one HDMI output and there is only one driver.

Someone might put two chips on the board.

The pointer was from the tda998x codec to the tda998x driver. There might be any number of chips on the board.

Quoted 15 lines
Quoted 14 lines
Quoted 14 lines

sound/soc/codecs/tda998x.c line 32

Quoted 11 lines
sound/soc/codecs/tda998x.c new file +11 -0
+static int tda998x_codec_startup(struct snd_pcm_substream *substream,
+                       struct snd_soc_dai *dai)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       u8 *eld;
+
+       eld = tda998x_ops->get_eld(dai->dev);
+       if (!eld)
+               return -ENODEV;
+       return snd_pcm_hw_constraint_eld(runtime, eld);
+}

Do we really need a device specific mechanism for fishing the ELD out of the graphics code? I'd have expected this to be more generic.

Quoted 5 lines
Quoted 1 line

I will put the ELD in the private data of the DRM driver.

That's not the issue here - the issue is that the need to get the ELD out of the video subsystem is not specific to this driver, it's pretty common and something that it seems we should factor out.

Yes, but how?

The EDID arrives in the DRM connector when video starts. The built ELD may be stored either in the connector itself (default), in the encoder (TDA998x device) or in some DRM/encoder/connector private data.

On the audio side, the CODEC is supported by a driver which is either a CODEC driver (as in the dummy HDMI codec) or the DRM encoder (TDA998x device).

You may note that, in DRM, there is no relation between the encoder and the connector except at video startup time, and no relation between the DRM connector and the audio CODEC (no CODEC information is returned on CODEC creation and the CODEC has no private data).

I tried many solutions to solve this problem, and the one of may latest patchset (v14) seems the simpleset: set the audio/video common part at the beginning of the TDA998x (HDMI) private data.

Signature · 6 lines
Ken ar c'hentañ	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/
_______________________________________________
Alsa-devel mailing list
[email protected]
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
#11 lore raw
Quoted 3 lines · Jean-Francois Moine

The EDID arrives in the DRM connector when video starts. The built ELD may be stored either in the connector itself (default), in the encoder (TDA998x device) or in some DRM/encoder/connector private data.

The ELD is stored in the DRM connector, and there _should_ be a system of locking which ensures that you can get at that information safely.

The ELD is only updated when the connector's get_modes() method is called, and the driver itself calls drm_edid_to_eld(). This all happens under the drm_device's struct_mutex.

So, to safely read the ELD from outside DRM, you need to take the top-level drm_device's struct_mutex. That could be fraught, as that's quite a big lock, so an alternative would be to introduce an 'eld' lock to the driver, which protects the call to drm_edid_to_eld() and the reading of the ELD.

However, that doesn't solve the problem of passing the data to an audio driver. What's needed there is a notification system which allows a video driver to broadcast HDMI events such as:

* connected
* new EDID available
* new ELD available
* disconnected

With such a system, different components driving the facilities of a HDMI connector can make decisions on what to do - which not only includes things like the audio driver, but also a driver for the CEC interface (which also needs to see the EDID to get at its "address".) This can be done in a safe manner as the HDMI video driver would have control over the generation of these messages.

The point that Mark is trying to get you to see is that this problem is not specific to TDA998x. The same problem exists for many other HDMI interfaces (except maybe Intel's i9x5/HDA stuff which has a hardware mechanism of passing the ELD data from the video driver, through the hardware to the audio driver.)

It needs solving in a driver-agnostic way, and the normal way that happens is when someone comes along, wanting to add support in that area, they get to do the hard work to propose that infrastructure.

Quoted 4 lines

You may note that, in DRM, there is no relation between the encoder and the connector except at video startup time, and no relation between the DRM connector and the audio CODEC (no CODEC information is returned on CODEC creation and the CODEC has no private data).

In the case of the TDA998x, there is a 1:1 fixed relationship between the connector and the encoder. The connector part can't be used with any other encoder, and the encoder part can't be used with any other connector. The same is true of many other HDMI implementations (such as the Synopsis Designware HDMI interface found on iMX6 and Rockchip.)

Signature · 6 lines
FTTC broadband for 0.8mile line: currently at 10.5Mbps down 400kbps up
according to speedtest.net.
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#12 lore raw
Quoted 34 lines · Russell King - ARM Linux
Quoted 3 lines · Jean-Francois Moine

The EDID arrives in the DRM connector when video starts. The built ELD may be stored either in the connector itself (default), in the encoder (TDA998x device) or in some DRM/encoder/connector private data.

The ELD is stored in the DRM connector, and there _should_ be a system of locking which ensures that you can get at that information safely.

The ELD is only updated when the connector's get_modes() method is called, and the driver itself calls drm_edid_to_eld(). This all happens under the drm_device's struct_mutex.

So, to safely read the ELD from outside DRM, you need to take the top-level drm_device's struct_mutex. That could be fraught, as that's quite a big lock, so an alternative would be to introduce an 'eld' lock to the driver, which protects the call to drm_edid_to_eld() and the reading of the ELD.

However, that doesn't solve the problem of passing the data to an audio driver. What's needed there is a notification system which allows a video driver to broadcast HDMI events such as:

* connected
* new EDID available
* new ELD available
* disconnected

With such a system, different components driving the facilities of a HDMI connector can make decisions on what to do - which not only includes things like the audio driver, but also a driver for the CEC interface (which also needs to see the EDID to get at its "address".) This can be done in a safe manner as the HDMI video driver would have control over the generation of these messages.

Quoted 5 lines

The point that Mark is trying to get you to see is that this problem is not specific to TDA998x. The same problem exists for many other HDMI interfaces (except maybe Intel's i9x5/HDA stuff which has a hardware mechanism of passing the ELD data from the video driver, through the hardware to the audio driver.)

FWIW, we're currently discussing about extending the i915/hda component binding so that the audio driver gets a notification and queries the ELD via callbacks instead of the flaky hardware access.

It'd be best if we have a common infrastructure for that, of course. But currently it's a start, just a bit step forward there.

Takashi

A message that is not in the archive (sent off-list or not yet received): [email protected]

#14 lore raw
Quoted 6 lines · Takashi Iwai

FWIW, we're currently discussing about extending the i915/hda component binding so that the audio driver gets a notification and queries the ELD via callbacks instead of the flaky hardware access.

It'd be best if we have a common infrastructure for that, of course. But currently it's a start, just a bit step forward there.

Okay, so it sounds like i915/hda also needs this.

I think in addition to what I said above, we need whatever provides the notification system to generate state notifications for new "listeners" that would otherwise be unaware of the current state.

I'm thinking at the moment that something along these lines to go into drivers/video/hdmi.c / include/linux/hdmi.h:

struct hdmi_state {
	struct mutex mutex;
	struct raw_notifier_head head;
	bool connected;
	bool edid_valid;
	u8 edid[MAX_EDID_SIZE];
};

enum {
HDMI_CONNECTED,
HDMI_DISCONNECTED,
HDMI_NEW_EDID,
};

int hdmi_register_notifier(struct notifier_block *nb)

{
	struct hdmi_state *state = ...;
	int ret;

	mutex_lock(&state->mutex);
	if (state->connected) {
		nb->notifier_call(nb, HDMI_CONNECTED, NULL);
		if (state->edid_valid)
			nb->notifier_call(nb, HDMI_NEW_EDID, state->edid);
	} else {
		nb->notifier_call(nb, HDMI_DISCONNECTED, NULL);
	}
	ret = raw_notifier_chain_register(&state->head, nb);
	mutex_unlock(&state->mutex);

	return ret;
}

int hdmi_unregister_notifier(struct notifier_block *nb)

{
	struct hdmi_state *state = ...;
	int ret;

	mutex_lock(&state->mutex);
	ret = raw_notifier_chain_unregister(&state->head, nb);
	mutex_unlock(&state->mutex);

	return ret;
}

void hdmi_event_connect(struct device *dev)

{
	struct hdmi_state *state = lookup_state_from_dev(dev);

	mutex_lock(&state->mutex);
	state->connected = true;
	raw_notifier_call_chain(&state->head, HDMI_CONNECTED, NULL);
	mutex_unlock(&state->mutex);
}

void hdmi_event_disconnect(struct device *dev)

{
	struct hdmi_state *state = lookup_state_from_dev(dev);

	mutex_lock(&state->mutex);
	state->connected = false;
	state->valid_edid = false;
	raw_notifier_call_chain(&state->head, HDMI_DISCONNECTED, NULL);
	mutex_unlock(&state->mutex);
}

int hdmi_event_new_edid(struct device *dev, const void *edid, size_t size)

{
	struct hdmi_state *state = lookup_state_from_dev(dev);

	if (size > sizeof(state->edid))
		return -EINVAL;

	mutex_lock(&state->mutex);
	state->valid_edid = true;
	memcpy(state->edid, edid, size);
	raw_notifier_call_chain(&state->head, HDMI_NEW_EDID, state->edid);
	mutex_unlock(&state->mutex);

	return 0;
}

The problems here are: how to go from a struct device to the state (iow, the implementation of lookup_state_from_dev()) and how to convey on the client side which 'state' to attach to. I'd rather not involve DT at this level: DT is not the world's only firmware system, we have to cater for x86 too here, so we need something that is firmware agnostic.

This is something I've just banged out in this email...

Signature · 6 lines
FTTC broadband for 0.8mile line: currently at 10.5Mbps down 400kbps up
according to speedtest.net.
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

A message that is not in the archive (sent off-list or not yet received): 20150728135358.GK7557-l+eeeJia6m9vn6HldHNs0ANdhmdF6hFW@public.gmane.org

#16 lore raw
Quoted 8 lines · On Tue, 28 Jul 2015 14:53:58 +0100 Russell King - ARM Linux…
Quoted 3 lines · Jean-Francois Moine

The EDID arrives in the DRM connector when video starts. The built ELD may be stored either in the connector itself (default), in the encoder (TDA998x device) or in some DRM/encoder/connector private data.

The ELD is stored in the DRM connector, and there _should_ be a system of locking which ensures that you can get at that information safely.

Quoted 8 lines

The ELD is only updated when the connector's get_modes() method is called, and the driver itself calls drm_edid_to_eld(). This all happens under the drm_device's struct_mutex.

So, to safely read the ELD from outside DRM, you need to take the top-level drm_device's struct_mutex. That could be fraught, as that's quite a big lock, so an alternative would be to introduce an 'eld' lock to the driver, which protects the call to drm_edid_to_eld() and the reading of the ELD.

I think that my solution should work:
- the CODEC uses a pointer in the driver private data to the ELD.
- when get_modes() is called, this pointer is reset to NULL. (no audio streaming is permitted).
- this function reads the EDID and this asks for hardware exchanges with the HDMI device.
- the EDID is then translated to ELD and the ELD pointer is set (audio streaming is permitted again).

Then, if audio streaming is started before get_modes() is called, the memory treatment of the ELD may be safely done during the harware exchanges.

Quoted 8 lines

However, that doesn't solve the problem of passing the data to an audio driver. What's needed there is a notification system which allows a video driver to broadcast HDMI events such as:

* connected
* new EDID available
* new ELD available
* disconnected

My patch just wants to offer basic audio to the TDA998x. Especially, the display device state is of no importance: audio streaming may continue while there is no connected device.

Quoted 7 lines

With such a system, different components driving the facilities of a HDMI connector can make decisions on what to do - which not only includes things like the audio driver, but also a driver for the CEC interface (which also needs to see the EDID to get at its "address".) This can be done in a safe manner as the HDMI video driver would have control over the generation of these messages.

Quoted 5 lines

The point that Mark is trying to get you to see is that this problem is not specific to TDA998x. The same problem exists for many other HDMI interfaces (except maybe Intel's i9x5/HDA stuff which has a hardware mechanism of passing the ELD data from the video driver, through the hardware to the audio driver.)

I know that, but I don't know enough about all the DRM and ASoC drivers to propose a global mechanism.

Quoted 3 lines

It needs solving in a driver-agnostic way, and the normal way that happens is when someone comes along, wanting to add support in that area, they get to do the hard work to propose that infrastructure.

OK. I'll stop here.

Quoted 10 lines
Quoted 4 lines

You may note that, in DRM, there is no relation between the encoder and the connector except at video startup time, and no relation between the DRM connector and the audio CODEC (no CODEC information is returned on CODEC creation and the CODEC has no private data).

In the case of the TDA998x, there is a 1:1 fixed relationship between the connector and the encoder. The connector part can't be used with any other encoder, and the encoder part can't be used with any other connector. The same is true of many other HDMI implementations (such as the Synopsis Designware HDMI interface found on iMX6 and Rockchip.)

But there is no direct link (pointer) between the encoder and the connector.

Signature · 6 lines
Ken ar c'hentañ	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#17 lore raw
Quoted 10 lines · Jean-Francois Moine
Quoted 5 lines · On Tue, 28 Jul 2015 14:53:58 +0100 Russell King - ARM Linux…

In the case of the TDA998x, there is a 1:1 fixed relationship between the connector and the encoder. The connector part can't be used with any other encoder, and the encoder part can't be used with any other connector. The same is true of many other HDMI implementations (such as the Synopsis Designware HDMI interface found on iMX6 and Rockchip.)

But there is no direct link (pointer) between the encoder and the connector.

There's no direct link from an encoder to a connector because the DRM model allows a single encoder to be associated with multiple connectors. Adding a link from the encoder to a connector breaks that ability (an encoder would then need an array of connectors.)

If we kill the old drm_slave_encoder support in TDA998x, then this would solve the problem - we can then get at the TDA998x's drm_connector easily as it then becomes part of TDA998x's private data.

However, this doesn't matter. All that we need from the DRM side of the TDA998x is to know is the EDID or ELD. That can be broadcast via a notification (using something like the code I illustrated in a follow up email) to any listeners, whether they be an audio driver or a CEC bus driver. The audio driver may not care about HDMI connect/disconnect events, but that's no reason not to include them. Some consumers of these events need to know (CEC certainly needs to know.)

Signature · 6 lines
FTTC broadband for 0.8mile line: currently at 10.5Mbps down 400kbps up
according to speedtest.net.
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html