All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jyri Sarha <jsarha@ti.com>
To: dri-devel@lists.freedesktop.org, airlied@linux.ie,
	linux-omap@vger.kernel.org, devicetree@vger.kernel.org,
	bcousson@baylibre.com, alsa-devel@alsa-project.org
Cc: peter.ujfalusi@ti.com, moinejf@free.fr, tony@atomide.com,
	broonie@kernel.org, Jyri Sarha <jsarha@ti.com>,
	liam.r.girdwood@linux.intel.com, tomi.valkeinen@ti.com,
	rmk+kernel@arm.linux.org.uk
Subject: [PATCH RFC v4 7/8] drm/i2c: tda998x: Register ASoC HDMI codec for audio functionality
Date: Fri, 18 Sep 2015 14:06:44 +0300	[thread overview]
Message-ID: <6178083f43c786adcd061abeb4a5265d145df0fd.1442572860.git.jsarha@ti.com> (raw)
In-Reply-To: <cover.1442572860.git.jsarha@ti.com>

Register ASoC HDMI codec for audio functionality. This is an initial
ASoC audio implementation for tda998x driver and it does not use all
the features provided by hdmi-codec.

HDMI audio info-frame and audio stream header is generated by the ASoC
HDMI codec. The codec also applies constraints for available
sample-rates.

Implementation of audio_startup for hdmi_codec_ops would enable
tda998x driver to abort ongoing playback if the HDMI cable is
unplugged or re-plugged to a device without audio capability.

Signed-off-by: Jyri Sarha <jsarha@ti.com>
---
 drivers/gpu/drm/i2c/Kconfig       |   1 +
 drivers/gpu/drm/i2c/tda998x_drv.c | 181 +++++++++++++++++++++++++++++++-------
 include/drm/i2c/tda998x.h         |   1 +
 3 files changed, 150 insertions(+), 33 deletions(-)

diff --git a/drivers/gpu/drm/i2c/Kconfig b/drivers/gpu/drm/i2c/Kconfig
index 22c7ed6..088f278 100644
--- a/drivers/gpu/drm/i2c/Kconfig
+++ b/drivers/gpu/drm/i2c/Kconfig
@@ -28,6 +28,7 @@ config DRM_I2C_SIL164
 config DRM_I2C_NXP_TDA998X
 	tristate "NXP Semiconductors TDA998X HDMI encoder"
 	default m if DRM_TILCDC
+	select SND_SOC_HDMI_CODEC if SND_SOC
 	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 2fc6399..1a9bbf2 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -20,6 +20,7 @@
 #include <linux/module.h>
 #include <linux/irq.h>
 #include <sound/asoundef.h>
+#include <sound/hdmi-codec.h>
 
 #include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
@@ -30,9 +31,9 @@
 
 #define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
 
-struct tda998x_audio {
-	u8 ports[2];			/* AP value */
-	u8 port_types[2];		/* AFMT_xxx */
+struct tda998x_audio_port {
+	u8 format;		/* AFMT_xxx */
+	u8 config;		/* AP value */
 };
 
 struct tda998x_priv {
@@ -49,11 +50,13 @@ struct tda998x_priv {
 	u8 vip_cntrl_2;
 	struct tda998x_audio_params audio_params;
 
+	struct platform_device *audio_pdev;
+
 	wait_queue_head_t wq_edid;
 	volatile int wq_edid_wait;
 	struct drm_encoder *encoder;
 
-	struct tda998x_audio audio;
+	struct tda998x_audio_port audio_port[2];
 };
 
 #define to_tda998x_priv(x)  ((struct tda998x_priv *)to_encoder_slave(x)->slave_priv)
@@ -701,7 +704,7 @@ tda998x_configure_audio(struct tda998x_priv *priv,
 		break;
 
 	default:
-		BUG();
+		dev_err(&priv->hdmi->dev, "Unsupported I2S format\n");
 		return -EINVAL;
 	}
 
@@ -1028,7 +1031,7 @@ tda998x_encoder_mode_set(struct tda998x_priv *priv,
 
 		tda998x_write_avi(priv, adjusted_mode);
 
-		if (priv->audio_params.config) {
+		if (priv->audio_params.format != AFMT_UNUSED) {
 			tda998x_configure_audio(priv,
 						&priv->audio_params,
 						adjusted_mode->clock);
@@ -1124,6 +1127,8 @@ 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);
+	drm_edid_to_eld(connector, edid);
+
 	kfree(edid);
 
 	return n;
@@ -1160,6 +1165,9 @@ static void tda998x_destroy(struct tda998x_priv *priv)
 	}
 
 	i2c_unregister_device(priv->cec);
+
+	if (priv->audio_pdev)
+		platform_device_unregister(priv->audio_pdev);
 }
 
 /* Slave encoder support */
@@ -1234,6 +1242,133 @@ static struct drm_encoder_slave_funcs tda998x_encoder_slave_funcs = {
 	.set_property = tda998x_encoder_set_property,
 };
 
+static int tda998x_audio_hw_params(struct device *dev,
+				   struct hdmi_codec_daifmt *daifmt,
+				   struct hdmi_codec_params *params)
+{
+	struct tda998x_priv *priv = dev_get_drvdata(dev);
+	int i, ret;
+	struct tda998x_audio_params audio = {
+		.sample_width = params->sample_width,
+		.sample_rate = params->sample_rate,
+		.cea = params->cea,
+	};
+
+	if (!priv->encoder->crtc)
+		return -ENODEV;
+
+	memcpy(audio.status, params->iec.status,
+	       min(sizeof(audio.status), sizeof(params->iec.status)));
+
+	switch (daifmt->fmt) {
+	case HDMI_I2S:
+		if (daifmt->bit_clk_inv || daifmt->frame_clk_inv ||
+		    daifmt->bit_clk_master || daifmt->frame_clk_master) {
+			dev_err(dev, "%s: Bad flags %d %d %d %d\n", __func__,
+				daifmt->bit_clk_inv, daifmt->frame_clk_inv,
+				daifmt->bit_clk_master,
+				daifmt->frame_clk_master);
+			return -EINVAL;
+		}
+		for (i = 0; i < ARRAY_SIZE(priv->audio_port); i++)
+			if (priv->audio_port[i].format == AFMT_I2S)
+				audio.config = priv->audio_port[i].config;
+		audio.format = AFMT_I2S;
+		break;
+	case HDMI_SPDIF:
+		for (i = 0; i < ARRAY_SIZE(priv->audio_port); i++)
+			if (priv->audio_port[i].format == AFMT_SPDIF)
+				audio.config = priv->audio_port[i].config;
+		audio.format = AFMT_SPDIF;
+		break;
+	default:
+		dev_err(dev, "%s: Invalid format %d\n", __func__, daifmt->fmt);
+		return -EINVAL;
+	}
+
+	if (audio.config == 0) {
+		dev_err(dev, "%s: No audio configutation found\n", __func__);
+		return -EINVAL;
+	}
+
+	ret = tda998x_configure_audio(priv,
+				      &audio,
+				      priv->encoder->crtc->hwmode.clock);
+
+	return ret;
+}
+
+static void tda998x_audio_shutdown(struct device *dev)
+{
+	struct tda998x_priv *priv = dev_get_drvdata(dev);
+
+	reg_write(priv, REG_ENA_AP, 0);
+}
+
+int tda998x_audio_digital_mute(struct device *dev, bool enable)
+{
+	struct tda998x_priv *priv = dev_get_drvdata(dev);
+
+	tda998x_audio_mute(priv, enable);
+
+	return 0;
+}
+
+static int tda998x_audio_get_eld(struct device *dev, uint8_t *buf, size_t len)
+{
+	struct tda998x_priv *priv = dev_get_drvdata(dev);
+	struct drm_mode_config *config = &priv->encoder->dev->mode_config;
+	struct drm_connector *connector;
+	int ret = -ENODEV;
+
+	mutex_lock(&config->mutex);
+	list_for_each_entry(connector, &config->connector_list, head) {
+		if (priv->encoder == connector->encoder) {
+			memcpy(buf, connector->eld,
+			       min(sizeof(connector->eld), len));
+			ret = 0;
+		}
+	}
+	mutex_unlock(&config->mutex);
+
+	return ret;
+}
+
+static const struct hdmi_codec_ops audio_codec_ops = {
+	.hw_params = tda998x_audio_hw_params,
+	.audio_shutdown = tda998x_audio_shutdown,
+	.digital_mute = tda998x_audio_digital_mute,
+	.get_eld = tda998x_audio_get_eld,
+};
+
+static int tda998x_audio_codec_init(struct tda998x_priv *priv,
+				    struct device *dev)
+{
+	struct hdmi_codec_pdata codec_data = {
+		.ops = &audio_codec_ops,
+		.max_i2s_channels = 2,
+	};
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(priv->audio_port); i++) {
+		if (priv->audio_port[i].format == AFMT_I2S &&
+		    priv->audio_port[i].config != 0)
+			codec_data.i2s = 1;
+		if (priv->audio_port[i].format == AFMT_SPDIF &&
+		    priv->audio_port[i].config != 0)
+			codec_data.spdif = 1;
+	}
+
+	priv->audio_pdev = platform_device_register_data(
+		dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO,
+		&codec_data, sizeof(codec_data));
+
+	if (IS_ERR(priv->audio_pdev))
+		return PTR_ERR(priv->audio_pdev);
+
+	return 0;
+}
+
 /* I2C driver functions */
 
 static int tda998x_parse_ports(struct tda998x_priv *priv,
@@ -1274,12 +1409,12 @@ static int tda998x_parse_ports(struct tda998x_priv *priv,
 			afmt = AFMT_SPDIF;
 		else
 			continue;
-		if (audio_index >= ARRAY_SIZE(priv->audio.ports)) {
+		if (audio_index >= ARRAY_SIZE(priv->audio_port)) {
 			dev_err(&priv->hdmi->dev, "too many audio ports\n");
 			break;
 		}
-		priv->audio.ports[audio_index] = reg;
-		priv->audio.port_types[audio_index] = afmt;
+		priv->audio_port[audio_index].format = afmt;
+		priv->audio_port[audio_index].config = reg;
 		audio_index++;
 	}
 	return rgb_initialized;
@@ -1412,30 +1547,8 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv)
 				priv->vip_cntrl_2 = video;
 			}
 		}
-		if (priv->audio.ports[0]) {
-			struct tda998x_audio_params params = {
-				.config = priv->audio.ports[0],
-				.format = priv->audio.port_types[0],
-				.sample_width = 24,
-				.sample_rate = 44100,
-				.cea = {
-					.channels = 2,
-					.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM,
-					.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM,
-					.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM,
-				},
-				.status = {
-					IEC958_AES0_CON_NOT_COPYRIGHT,
-					IEC958_AES1_CON_GENERAL,
-					IEC958_AES2_CON_SOURCE_UNSPEC |
-					IEC958_AES2_CON_CHANNEL_UNSPEC,
-					IEC958_AES3_CON_CLOCK_1000PPM |
-					IEC958_AES3_CON_FS_NOTID,
-				},
-			};
-
-			priv->audio_params = params;
-		}
+		if (priv->audio_port[0].format != AFMT_UNUSED)
+			tda998x_audio_codec_init(priv, &client->dev);
 	}
 
 	return 0;
@@ -1468,6 +1581,8 @@ static int tda998x_encoder_init(struct i2c_client *client,
 		return ret;
 	}
 
+	dev_set_drvdata(&client->dev, priv);
+
 	encoder_slave->slave_priv = priv;
 	encoder_slave->slave_funcs = &tda998x_encoder_slave_funcs;
 
diff --git a/include/drm/i2c/tda998x.h b/include/drm/i2c/tda998x.h
index dfe7829..5a0cabb 100644
--- a/include/drm/i2c/tda998x.h
+++ b/include/drm/i2c/tda998x.h
@@ -4,6 +4,7 @@
 struct tda998x_audio_params {
 	u8 config;
 	enum {
+		AFMT_UNUSED = 0,
 		AFMT_SPDIF,
 		AFMT_I2S
 	} format;
-- 
1.9.1

  parent reply	other threads:[~2015-09-18 11:06 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-09-18 11:06 [PATCH RFC v4 0/8] Implement generic ASoC HDMI codec and use it in tda998x Jyri Sarha
2015-09-18 11:06 ` [PATCH RFC v4 3/8] ASoC: hdmi-codec: Add hdmi-codec for external HDMI-encoders Jyri Sarha
     [not found]   ` <6fe9ab5dff972e6f228259d6818f3f481a11577d.1442572860.git.jsarha-l0cyMroinI0@public.gmane.org>
2015-09-19 17:54     ` Mark Brown
2015-09-21  9:31       ` Russell King - ARM Linux
2015-09-21 13:41         ` Jyri Sarha
2015-09-21 17:18           ` Mark Brown
     [not found] ` <cover.1442572860.git.jsarha-l0cyMroinI0@public.gmane.org>
2015-09-18 11:06   ` [PATCH RFC v4 1/8] ASoC: hdmi: Remove obsolete dummy HDMI codec Jyri Sarha
2015-09-18 11:06   ` [PATCH RFC v4 2/8] ALSA: pcm: add IEC958 channel status helper for hw_params Jyri Sarha
     [not found]     ` <a7b69fb075b0ae993846cfb65abb20ecab5c9805.1442572860.git.jsarha-l0cyMroinI0@public.gmane.org>
2015-09-19 17:46       ` Mark Brown
2015-09-21  9:37     ` Russell King - ARM Linux
2015-09-21 14:39       ` Jyri Sarha
2015-09-21 15:08         ` Clemens Ladisch
2015-09-21 17:25           ` Jyri Sarha
2015-09-18 11:06   ` [PATCH RFC v4 4/8] drm/i2c: tda998x: Add support of a DT graph of ports Jyri Sarha
     [not found]     ` <e9ebb04f781fef7cff99190d301fb64805b97d39.1442572860.git.jsarha-l0cyMroinI0@public.gmane.org>
2015-09-21 15:19       ` Rob Herring
2015-09-24 10:36         ` Jean-Francois Moine
2015-09-24 16:29           ` Rob Herring
2015-09-18 11:06   ` [PATCH RFC v4 6/8] drm/i2c: tda998x: Improve tda998x_configure_audio() audio related pdata Jyri Sarha
2015-09-18 11:06 ` [PATCH RFC v4 5/8] drm/i2c: tda998x: Remove include/sound/tda998x.h and fix graph parsing Jyri Sarha
2015-09-18 11:06 ` Jyri Sarha [this message]
2015-09-18 11:06 ` [PATCH RFC v4 8/8] ARM: dts: am335x-boneblack: Add HDMI audio support Jyri Sarha

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=6178083f43c786adcd061abeb4a5265d145df0fd.1442572860.git.jsarha@ti.com \
    --to=jsarha@ti.com \
    --cc=airlied@linux.ie \
    --cc=alsa-devel@alsa-project.org \
    --cc=bcousson@baylibre.com \
    --cc=broonie@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=liam.r.girdwood@linux.intel.com \
    --cc=linux-omap@vger.kernel.org \
    --cc=moinejf@free.fr \
    --cc=peter.ujfalusi@ti.com \
    --cc=rmk+kernel@arm.linux.org.uk \
    --cc=tomi.valkeinen@ti.com \
    --cc=tony@atomide.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.