alsa-devel.alsa-project.org archive mirror
 help / color / mirror / Atom feed
From: Philipp Zabel <p.zabel@pengutronix.de>
To: alsa-devel@alsa-project.org
Cc: Jean-Francois Moine <moinejf@free.fr>,
	Koro Chen <koro.chen@mediatek.com>,
	Lars-Peter Clausen <lars@metafoo.de>,
	Russell King - ARM Linux <linux@arm.linux.org.uk>,
	Philipp Zabel <p.zabel@pengutronix.de>,
	Arnaud Pouliquen <arnaud.pouliquen@st.com>,
	Liam Girdwood <lgirdwood@gmail.com>, Jyri Sarha <jsarha@ti.com>,
	Cawa Cheng <cawa.cheng@mediatek.com>,
	Mark Brown <broonie@kernel.org>,
	linux-mediatek@lists.infradead.org,
	Daniel Kurtz <djkurtz@chromium.org>,
	kernel@pengutronix.de, Matthias Brugger <matthias.bgg@gmail.com>
Subject: [RFC v2 1/6] drm/mediatek: hdmi: Add audio interface to the hdmi-codec driver
Date: Mon,  4 Jan 2016 20:09:06 +0100	[thread overview]
Message-ID: <1451934551-21333-2-git-send-email-p.zabel@pengutronix.de> (raw)
In-Reply-To: <1451934551-21333-1-git-send-email-p.zabel@pengutronix.de>

Add the interface needed by Jyri's generic hdmi-codec driver [1] to start
or stop audio playback and to retrieve ELD (EDID like data) to limit the
supported audio formats to the HDMI sink capabilities.

[1] https://patchwork.kernel.org/patch/7215271/ ("ASoC: hdmi-codec: Add
    hdmi-codec for external HDMI-encoders")

Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
---
 drivers/gpu/drm/mediatek/Kconfig            |   1 +
 drivers/gpu/drm/mediatek/mtk_drm_hdmi_drv.c | 189 ++++++++++++++++++++++++++++
 drivers/gpu/drm/mediatek/mtk_hdmi.c         |  26 ++++
 drivers/gpu/drm/mediatek/mtk_hdmi.h         |   4 +
 4 files changed, 220 insertions(+)

diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
index 829ab66..93b7ca9 100644
--- a/drivers/gpu/drm/mediatek/Kconfig
+++ b/drivers/gpu/drm/mediatek/Kconfig
@@ -17,6 +17,7 @@ config DRM_MEDIATEK
 config DRM_MEDIATEK_HDMI
 	tristate "DRM HDMI Support for Mediatek SoCs"
 	depends on DRM_MEDIATEK
+	select SND_SOC_HDMI_CODEC if SND_SOC
 	select GENERIC_PHY
 	help
 	  DRM/KMS HDMI driver for Mediatek SoCs
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_hdmi_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_hdmi_drv.c
index 5c1ec96..1a07ef3 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_hdmi_drv.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_hdmi_drv.c
@@ -26,6 +26,7 @@
 #include <linux/of_graph.h>
 #include <linux/phy/phy.h>
 #include <linux/platform_device.h>
+#include <sound/hdmi-codec.h>
 #include "mtk_cec.h"
 #include "mtk_hdmi.h"
 #include "mtk_hdmi_hw.h"
@@ -437,6 +438,192 @@ static irqreturn_t hdmi_flt_n_5v_irq_thread(int irq, void *arg)
 	return IRQ_HANDLED;
 }
 
+/*
+ * HDMI audio codec callbacks
+ */
+
+static int mtk_hdmi_audio_hw_params(struct device *dev,
+				    struct hdmi_codec_daifmt *daifmt,
+				    struct hdmi_codec_params *params)
+{
+	struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
+	struct hdmi_audio_param hdmi_params;
+	unsigned int chan = params->cea.channels;
+
+	dev_dbg(hdmi->dev, "%s: %u Hz, %d bit, %d channels\n", __func__,
+		params->sample_rate, params->sample_width, chan);
+
+	if (!hdmi->bridge.encoder || !hdmi->bridge.encoder->crtc)
+		return -ENODEV;
+
+	switch (chan) {
+	case 2:
+		hdmi_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_2_0;
+		break;
+	case 4:
+		hdmi_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_4_0;
+		break;
+	case 6:
+		hdmi_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_5_1;
+		break;
+	case 8:
+		hdmi_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_7_1;
+		break;
+	default:
+		dev_err(hdmi->dev, "channel[%d] not supported!\n", chan);
+		return -EINVAL;
+	}
+
+	switch (params->sample_rate) {
+	case 32000:
+		hdmi_params.aud_hdmi_fs = HDMI_AUDIO_SAMPLE_FREQUENCY_32000;
+		hdmi_params.iec_frame_fs = HDMI_IEC_32K;
+		/* channel status byte 3: fs and clock accuracy */
+		hdmi_params.hdmi_l_channel_state[3] = 0x3;
+		break;
+	case 44100:
+		hdmi_params.aud_hdmi_fs = HDMI_AUDIO_SAMPLE_FREQUENCY_44100;
+		hdmi_params.iec_frame_fs = HDMI_IEC_44K;
+		/* channel status byte 3: fs and clock accuracy */
+		hdmi_params.hdmi_l_channel_state[3] = 0;
+		break;
+	case 48000:
+		hdmi_params.aud_hdmi_fs = HDMI_AUDIO_SAMPLE_FREQUENCY_48000;
+		hdmi_params.iec_frame_fs = HDMI_IEC_48K;
+		/* channel status byte 3: fs and clock accuracy */
+		hdmi_params.hdmi_l_channel_state[3] = 0x2;
+		break;
+	case 88200:
+		hdmi_params.aud_hdmi_fs = HDMI_AUDIO_SAMPLE_FREQUENCY_88200;
+		hdmi_params.iec_frame_fs = HDMI_IEC_88K;
+		/* channel status byte 3: fs and clock accuracy */
+		hdmi_params.hdmi_l_channel_state[3] = 0x8;
+		break;
+	case 96000:
+		hdmi_params.aud_hdmi_fs = HDMI_AUDIO_SAMPLE_FREQUENCY_96000;
+		hdmi_params.iec_frame_fs = HDMI_IEC_96K;
+		/* channel status byte 3: fs and clock accuracy */
+		hdmi_params.hdmi_l_channel_state[3] = 0xa;
+		break;
+	case 176400:
+		hdmi_params.aud_hdmi_fs = HDMI_AUDIO_SAMPLE_FREQUENCY_176400;
+		hdmi_params.iec_frame_fs = HDMI_IEC_176K;
+		/* channel status byte 3: fs and clock accuracy */
+		hdmi_params.hdmi_l_channel_state[3] = 0xc;
+		break;
+	case 192000:
+		hdmi_params.aud_hdmi_fs = HDMI_AUDIO_SAMPLE_FREQUENCY_192000;
+		hdmi_params.iec_frame_fs = HDMI_IEC_192K;
+		/* channel status byte 3: fs and clock accuracy */
+		hdmi_params.hdmi_l_channel_state[3] = 0xe;
+		break;
+	default:
+		dev_err(hdmi->dev, "rate[%d] not supported!\n",
+			params->sample_rate);
+		return -EINVAL;
+	}
+
+	switch (daifmt->fmt) {
+	case HDMI_I2S:
+		hdmi_params.aud_codec = HDMI_AUDIO_CODING_TYPE_PCM;
+		hdmi_params.aud_sampe_size = HDMI_AUDIO_SAMPLE_SIZE_16;
+		hdmi_params.aud_input_type = HDMI_AUD_INPUT_I2S;
+		hdmi_params.aud_i2s_fmt = HDMI_I2S_MODE_I2S_24BIT;
+		hdmi_params.aud_mclk = HDMI_AUD_MCLK_128FS;
+		break;
+	default:
+		dev_err(hdmi->dev, "%s: Invalid format %d\n", __func__,
+			daifmt->fmt);
+		return -EINVAL;
+	}
+
+	/* channel status */
+	/* byte 0: no copyright is asserted, mode 0 */
+	hdmi_params.hdmi_l_channel_state[0] = 1 << 2;
+	/* byte 1: category code */
+	hdmi_params.hdmi_l_channel_state[1] = 0;
+	/* byte 2: source/channel number don't take into account */
+	hdmi_params.hdmi_l_channel_state[2] = 0;
+	/* byte 4: word length 16bits */
+	hdmi_params.hdmi_l_channel_state[4] = 0x2;
+	memcpy(hdmi_params.hdmi_r_channel_state,
+	       hdmi_params.hdmi_l_channel_state,
+	       sizeof(hdmi_params.hdmi_l_channel_state));
+
+	mtk_hdmi_audio_set_param(hdmi, &hdmi_params);
+
+	return 0;
+}
+
+static int mtk_hdmi_audio_startup(struct device *dev,
+				  void (*abort_cb)(struct device *dev))
+{
+	struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
+
+	dev_dbg(dev, "%s\n", __func__);
+
+	mtk_hdmi_audio_enable(hdmi);
+
+	return 0;
+}
+
+static void mtk_hdmi_audio_shutdown(struct device *dev)
+{
+	struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
+
+	dev_dbg(dev, "%s\n", __func__);
+
+	mtk_hdmi_audio_disable(hdmi);
+}
+
+int mtk_hdmi_audio_digital_mute(struct device *dev, bool enable)
+{
+	struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
+
+	dev_dbg(dev, "%s(%d)\n", __func__, enable);
+
+	mtk_hdmi_hw_aud_mute(hdmi, enable);
+
+	return 0;
+}
+
+static int mtk_hdmi_audio_get_eld(struct device *dev, uint8_t *buf, size_t len)
+{
+	struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
+
+	dev_dbg(dev, "%s\n", __func__);
+
+	memcpy(buf, hdmi->conn.eld, min(sizeof(hdmi->conn.eld), len));
+
+	return 0;
+}
+
+static const struct hdmi_codec_ops mtk_hdmi_audio_codec_ops = {
+	.hw_params = mtk_hdmi_audio_hw_params,
+	.audio_startup = mtk_hdmi_audio_startup,
+	.audio_shutdown = mtk_hdmi_audio_shutdown,
+	.digital_mute = mtk_hdmi_audio_digital_mute,
+	.get_eld = mtk_hdmi_audio_get_eld,
+};
+
+static void mtk_hdmi_register_audio_driver(struct device *dev)
+{
+	struct hdmi_codec_pdata codec_data = {
+		.ops = &mtk_hdmi_audio_codec_ops,
+		.max_i2s_channels = 2,
+		.i2s = 1,
+	};
+	struct platform_device *pdev;
+
+	pdev = platform_device_register_data(dev, HDMI_CODEC_DRV_NAME,
+					     PLATFORM_DEVID_AUTO, &codec_data,
+					     sizeof(codec_data));
+	if (IS_ERR(pdev))
+		return;
+
+	DRM_INFO("%s driver bound to HDMI\n", HDMI_CODEC_DRV_NAME);
+}
+
 static int mtk_drm_hdmi_probe(struct platform_device *pdev)
 {
 	struct mtk_hdmi *hdmi;
@@ -485,6 +672,8 @@ static int mtk_drm_hdmi_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+	mtk_hdmi_register_audio_driver(dev);
+
 	hdmi->bridge.funcs = &mtk_hdmi_bridge_funcs;
 	hdmi->bridge.of_node = pdev->dev.of_node;
 	ret = drm_bridge_add(&hdmi->bridge);
diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c
index 342beab..535a4d7 100644
--- a/drivers/gpu/drm/mediatek/mtk_hdmi.c
+++ b/drivers/gpu/drm/mediatek/mtk_hdmi.c
@@ -441,6 +441,32 @@ void mtk_hdmi_power_off(struct mtk_hdmi *hdmi)
 	mtk_hdmi_hw_make_reg_writable(hdmi, false);
 }
 
+void mtk_hdmi_audio_enable(struct mtk_hdmi *hdmi)
+{
+	mtk_hdmi_aud_enable_packet(hdmi, true);
+	hdmi->audio_enable = true;
+}
+
+void mtk_hdmi_audio_disable(struct mtk_hdmi *hdmi)
+{
+	mtk_hdmi_aud_enable_packet(hdmi, false);
+	hdmi->audio_enable = false;
+}
+
+int mtk_hdmi_audio_set_param(struct mtk_hdmi *hdmi,
+			     struct hdmi_audio_param *param)
+{
+	if (!hdmi->audio_enable) {
+		dev_err(hdmi->dev, "hdmi audio is in disable state!\n");
+		return -EINVAL;
+	}
+	dev_info(hdmi->dev, "codec:%d, input:%d, channel:%d, fs:%d\n",
+		 param->aud_codec, param->aud_input_type,
+		 param->aud_input_chan_type, param->aud_hdmi_fs);
+	memcpy(&hdmi->aud_param, param, sizeof(*param));
+	return mtk_hdmi_aud_output_config(hdmi, &hdmi->mode);
+}
+
 int mtk_hdmi_output_set_display_mode(struct mtk_hdmi *hdmi,
 				     struct drm_display_mode *mode)
 {
diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.h b/drivers/gpu/drm/mediatek/mtk_hdmi.h
index c072bcc..12e5614 100644
--- a/drivers/gpu/drm/mediatek/mtk_hdmi.h
+++ b/drivers/gpu/drm/mediatek/mtk_hdmi.h
@@ -211,6 +211,10 @@ int mtk_hdmi_output_set_display_mode(struct mtk_hdmi *hdmi,
 				     struct drm_display_mode *mode);
 void mtk_hdmi_power_on(struct mtk_hdmi *hdmi);
 void mtk_hdmi_power_off(struct mtk_hdmi *hdmi);
+void mtk_hdmi_audio_enable(struct mtk_hdmi *hctx);
+void mtk_hdmi_audio_disable(struct mtk_hdmi *hctx);
+int mtk_hdmi_audio_set_param(struct mtk_hdmi *hctx,
+			     struct hdmi_audio_param *param);
 #if defined(CONFIG_DEBUG_FS)
 int mtk_drm_hdmi_debugfs_init(struct mtk_hdmi *hdmi);
 void mtk_drm_hdmi_debugfs_exit(struct mtk_hdmi *hdmi);
-- 
2.6.2

  reply	other threads:[~2016-01-04 19:09 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-01-04 19:09 [RFC v2 0/6] ASoC: Add mediatek HDMI codec support Philipp Zabel
2016-01-04 19:09 ` Philipp Zabel [this message]
2016-01-04 22:29   ` [RFC v2 1/6] drm/mediatek: hdmi: Add audio interface to the hdmi-codec driver Russell King - ARM Linux
2016-01-05 14:56     ` Philipp Zabel
2016-01-04 19:11 ` [RFC v2 0/6] ASoC: Add mediatek HDMI codec support Russell King - ARM Linux
2016-01-04 19:18 ` [RFC v2 4/6] video: rmk's HDMI notification prototype Philipp Zabel
2016-01-04 19:18 ` [RFC v2 5/6] drm/mediatek: hdmi: issue notifications Philipp Zabel
     [not found] ` <1451934551-21333-1-git-send-email-p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
2016-01-04 19:09   ` [RFC v2 2/6] ASoC: mediatek: address dai link array entries by enum Philipp Zabel
2016-01-04 19:15     ` [RFC v2 3/6] ASoC: mediatek: Add HDMI dai-links in the machine driver Philipp Zabel
2016-01-05 12:46       ` Mark Brown
2016-01-07 10:06         ` Philipp Zabel
2016-03-05 12:24     ` Applied "ASoC: mediatek: address dai link array entries by enum" to the asoc tree Mark Brown
2016-01-04 19:19   ` [RFC v2 6/6] ASoC: hdmi-codec: Use HDMI notifications to add jack support Philipp Zabel
2016-01-07 17:09     ` Jyri Sarha
2016-01-08  8:43       ` Philipp Zabel
2016-01-08  9:57         ` Jyri Sarha
2016-01-08 10:46           ` Russell King - ARM Linux
2016-01-08 10:52             ` Takashi Iwai
2016-01-08 12:55             ` Mark Brown

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=1451934551-21333-2-git-send-email-p.zabel@pengutronix.de \
    --to=p.zabel@pengutronix.de \
    --cc=alsa-devel@alsa-project.org \
    --cc=arnaud.pouliquen@st.com \
    --cc=broonie@kernel.org \
    --cc=cawa.cheng@mediatek.com \
    --cc=djkurtz@chromium.org \
    --cc=jsarha@ti.com \
    --cc=kernel@pengutronix.de \
    --cc=koro.chen@mediatek.com \
    --cc=lars@metafoo.de \
    --cc=lgirdwood@gmail.com \
    --cc=linux-mediatek@lists.infradead.org \
    --cc=linux@arm.linux.org.uk \
    --cc=matthias.bgg@gmail.com \
    --cc=moinejf@free.fr \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).