diff mbox

[4/4] ASoC: tda998x: adjust the audio hw parameters from EDID

Message ID d99e90932aad1a644fa32ff22fd2cb56b830341b.1390813481.git.moinejf@free.fr (mailing list archive)
State New, archived
Headers show

Commit Message

Jean-Francois Moine Jan. 27, 2014, 8:48 a.m. UTC
The supported audio parameters are described in the EDID which is
received by the HDMI transmitter from the connected screen.
Use these ones to adjust the audio stream parameters.

Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
 drivers/gpu/drm/i2c/tda998x_drv.c | 15 ++++++++
 include/drm/i2c/tda998x.h         |  1 +
 sound/soc/codecs/tda998x.c        | 79 ++++++++++++++++++++++++++++++++++++++-
 3 files changed, 94 insertions(+), 1 deletion(-)

Comments

Mark Brown Jan. 27, 2014, 8:44 p.m. UTC | #1
On Mon, Jan 27, 2014 at 09:48:54AM +0100, Jean-Francois Moine wrote:

> +		eld_ver = eld[0] >> 3;
> +		if (eld_ver != 2 && eld_ver != 31)
> +			return 0;
> +
> +		mnl = eld[4] & 0x1f;
> +		if (mnl > 16)
> +			return 0;
> +
> +		sad_count = eld[5] >> 4;
> +		sad = eld + 20 + mnl;

Can this parsing code be factored out - it (or large parts of it) should
be usable by other HDMI devices shouldn't it?
Russell King - ARM Linux Jan. 27, 2014, 8:49 p.m. UTC | #2
On Mon, Jan 27, 2014 at 08:44:41PM +0000, Mark Brown wrote:
> On Mon, Jan 27, 2014 at 09:48:54AM +0100, Jean-Francois Moine wrote:
> 
> > +		eld_ver = eld[0] >> 3;
> > +		if (eld_ver != 2 && eld_ver != 31)
> > +			return 0;
> > +
> > +		mnl = eld[4] & 0x1f;
> > +		if (mnl > 16)
> > +			return 0;
> > +
> > +		sad_count = eld[5] >> 4;
> > +		sad = eld + 20 + mnl;
> 
> Can this parsing code be factored out - it (or large parts of it) should
> be usable by other HDMI devices shouldn't it?

Yes, preferably as a generic ALSA helper rather than an ASoC helper -
I don't see any need for this to be ASoC specific (I have a pure ALSA
driver which has very similar code in it.)
Mark Brown Jan. 27, 2014, 8:54 p.m. UTC | #3
On Mon, Jan 27, 2014 at 08:49:15PM +0000, Russell King - ARM Linux wrote:
> On Mon, Jan 27, 2014 at 08:44:41PM +0000, Mark Brown wrote:

> > Can this parsing code be factored out - it (or large parts of it) should
> > be usable by other HDMI devices shouldn't it?

> Yes, preferably as a generic ALSA helper rather than an ASoC helper -
> I don't see any need for this to be ASoC specific (I have a pure ALSA
> driver which has very similar code in it.)

Indeed, definitely ALSA generic - ideally we could factor a lot of the
integration with the video side out.
Takashi Iwai Jan. 28, 2014, 9:23 a.m. UTC | #4
At Mon, 27 Jan 2014 20:54:37 +0000,
Mark Brown wrote:
> 
> On Mon, Jan 27, 2014 at 08:49:15PM +0000, Russell King - ARM Linux wrote:
> > On Mon, Jan 27, 2014 at 08:44:41PM +0000, Mark Brown wrote:
> 
> > > Can this parsing code be factored out - it (or large parts of it) should
> > > be usable by other HDMI devices shouldn't it?
> 
> > Yes, preferably as a generic ALSA helper rather than an ASoC helper -
> > I don't see any need for this to be ASoC specific (I have a pure ALSA
> > driver which has very similar code in it.)
> 
> Indeed, definitely ALSA generic - ideally we could factor a lot of the
> integration with the video side out.

Yes, indeed.

OTOH, as discussed recently, we're heading to move from ELD parsing to
more direct communication between video and audio drivers for
HD-audio.  ELD will be still provided to user-space, but not evaluated
any longer in the new scenario.


Takashi
Mark Brown Jan. 28, 2014, 11 a.m. UTC | #5
On Tue, Jan 28, 2014 at 10:23:57AM +0100, Takashi Iwai wrote:
> Mark Brown wrote:
> > On Mon, Jan 27, 2014 at 08:49:15PM +0000, Russell King - ARM Linux wrote:

> > > Yes, preferably as a generic ALSA helper rather than an ASoC helper -
> > > I don't see any need for this to be ASoC specific (I have a pure ALSA
> > > driver which has very similar code in it.)

> > Indeed, definitely ALSA generic - ideally we could factor a lot of the
> > integration with the video side out.

> Yes, indeed.

> OTOH, as discussed recently, we're heading to move from ELD parsing to
> more direct communication between video and audio drivers for
> HD-audio.  ELD will be still provided to user-space, but not evaluated
> any longer in the new scenario.

That sort of refactoring being one of the best reasons to keep things
out of individual drivers!  Having said all this I don't know if it's
worth blocking Jean-Francois' work on that, it's an improvement in
itself.  Splitting the code out a bit would be good to help prepare but
having the full refactoring done might be too much of a blocker.
Takashi Iwai Jan. 28, 2014, 11:12 a.m. UTC | #6
At Tue, 28 Jan 2014 11:00:51 +0000,
Mark Brown wrote:
> 
> On Tue, Jan 28, 2014 at 10:23:57AM +0100, Takashi Iwai wrote:
> > Mark Brown wrote:
> > > On Mon, Jan 27, 2014 at 08:49:15PM +0000, Russell King - ARM Linux wrote:
> 
> > > > Yes, preferably as a generic ALSA helper rather than an ASoC helper -
> > > > I don't see any need for this to be ASoC specific (I have a pure ALSA
> > > > driver which has very similar code in it.)
> 
> > > Indeed, definitely ALSA generic - ideally we could factor a lot of the
> > > integration with the video side out.
> 
> > Yes, indeed.
> 
> > OTOH, as discussed recently, we're heading to move from ELD parsing to
> > more direct communication between video and audio drivers for
> > HD-audio.  ELD will be still provided to user-space, but not evaluated
> > any longer in the new scenario.
> 
> That sort of refactoring being one of the best reasons to keep things
> out of individual drivers!  Having said all this I don't know if it's
> worth blocking Jean-Francois' work on that, it's an improvement in
> itself.  Splitting the code out a bit would be good to help prepare but
> having the full refactoring done might be too much of a blocker.

I just rather wanted to point out the general direction for the
further development, didn't mean NAK.  Jean-Francois' patch itself
looks simple enough, so I see no problem to take it for now as is.


Takashi
diff mbox

Patch

diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index 186c751..3c8b4d7 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -45,6 +45,8 @@  struct tda998x_priv {
 	wait_queue_head_t wq_edid;
 	volatile int wq_edid_wait;
 	struct drm_encoder *encoder;
+
+	u8 *eld;
 };
 
 #define to_tda998x_priv(x)  ((struct tda998x_priv *)to_encoder_slave(x)->slave_priv)
@@ -731,6 +733,14 @@  tda998x_configure_audio(struct tda998x_priv *priv,
 }
 
 /* tda998x codec interface */
+u8 *tda998x_audio_get_eld(struct i2c_client *client)
+{
+	struct tda998x_priv *priv = i2c_get_clientdata(client);
+
+	return priv->eld;
+}
+EXPORT_SYMBOL_GPL(tda998x_audio_get_eld);
+
 void tda998x_audio_update(struct i2c_client *client,
 			int format,
 			int port)
@@ -1186,6 +1196,11 @@  tda998x_encoder_get_modes(struct drm_encoder *encoder,
 		drm_mode_connector_update_edid_property(connector, edid);
 		n = drm_add_edid_modes(connector, edid);
 		priv->is_hdmi_sink = drm_detect_hdmi_monitor(edid);
+
+		/* keep the EDID as ELD for the audio subsystem */
+		drm_edid_to_eld(connector, edid);
+		priv->eld = connector->eld;
+
 		kfree(edid);
 	}
 
diff --git a/include/drm/i2c/tda998x.h b/include/drm/i2c/tda998x.h
index 0459931..64aaaa8 100644
--- a/include/drm/i2c/tda998x.h
+++ b/include/drm/i2c/tda998x.h
@@ -26,6 +26,7 @@  struct tda998x_encoder_params {
 	unsigned audio_sample_rate;
 };
 
+u8 *tda998x_audio_get_eld(struct i2c_client *client);
 void tda998x_audio_update(struct i2c_client *client,
 			int format,
 			int port);
diff --git a/sound/soc/codecs/tda998x.c b/sound/soc/codecs/tda998x.c
index d724f7d..500ac61 100644
--- a/sound/soc/codecs/tda998x.c
+++ b/sound/soc/codecs/tda998x.c
@@ -91,10 +91,79 @@  static int tda_startup(struct snd_pcm_substream *substream,
 			struct snd_soc_dai *dai)
 {
 	struct tda_priv *priv = snd_soc_codec_get_drvdata(dai->codec);
+	u8 *eld = NULL;
+	static unsigned rates_mask[] = {
+		SNDRV_PCM_RATE_32000,
+		SNDRV_PCM_RATE_44100,
+		SNDRV_PCM_RATE_48000,
+		SNDRV_PCM_RATE_88200,
+		SNDRV_PCM_RATE_96000,
+		SNDRV_PCM_RATE_176400,
+		SNDRV_PCM_RATE_192000,
+	};
 
 	/* memorize the used DAI */
 	priv->dai_id = dai->id;
 
+	/* get the ELD from the tda998x driver */
+	if (!priv->i2c_client)
+		tda_get_encoder(priv);
+	if (priv->i2c_client)
+		eld = tda998x_audio_get_eld(priv->i2c_client);
+
+	/* adjust the hw params from the ELD (EDID) */
+	if (eld) {
+		struct snd_soc_dai_driver *dai_drv = dai->driver;
+		struct snd_soc_pcm_stream *stream = &dai_drv->playback;
+		u8 *sad;
+		int sad_count;
+		unsigned eld_ver, mnl, rates, rate_mask, i;
+		unsigned max_channels, fmt;
+		u64 formats;
+
+		eld_ver = eld[0] >> 3;
+		if (eld_ver != 2 && eld_ver != 31)
+			return 0;
+
+		mnl = eld[4] & 0x1f;
+		if (mnl > 16)
+			return 0;
+
+		sad_count = eld[5] >> 4;
+		sad = eld + 20 + mnl;
+
+		/* Start from the basic audio settings */
+		max_channels = 2;
+		rates = 0;
+		fmt = 0;
+		while (sad_count--) {
+			switch (sad[0] & 0x78) {
+			case 0x08: /* PCM */
+				max_channels = max(max_channels, (sad[0] & 7) + 1u);
+				rates |= sad[1];
+				fmt |= sad[2] & 0x07;
+				break;
+			}
+			sad += 3;
+		}
+
+		for (rate_mask = i = 0; i < ARRAY_SIZE(rates_mask); i++)
+			if (rates & 1 << i)
+				rate_mask |= rates_mask[i];
+		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;
+
+		/* change the snd_soc_pcm_stream values of the driver */
+		stream->rates = rate_mask;
+		stream->channels_max = max_channels;
+		stream->formats = formats;
+	}
+
 	/* start the TDA998x audio */
 	return tda_start_stop(priv, 1);
 }
@@ -193,9 +262,17 @@  static const struct snd_soc_codec_driver soc_codec_tda998x = {
 
 static int tda998x_dev_probe(struct platform_device *pdev)
 {
+	struct snd_soc_dai_driver *dai_drv;
+
+	/* copy the DAI driver to a writable area */
+	dai_drv = devm_kzalloc(&pdev->dev, sizeof(tda998x_dai), GFP_KERNEL);
+	if (!dai_drv)
+		return -ENOMEM;
+	memcpy(dai_drv, tda998x_dai, sizeof(tda998x_dai));
+
 	return snd_soc_register_codec(&pdev->dev,
 				&soc_codec_tda998x,
-				tda998x_dai, ARRAY_SIZE(tda998x_dai));
+				dai_drv, ARRAY_SIZE(tda998x_dai));
 }
 
 static int tda998x_dev_remove(struct platform_device *pdev)