All of lore.kernel.org
 help / color / mirror / Atom feed
From: Sylwester Nawrocki <s.nawrocki@samsung.com>
To: linux-samsung-soc@vger.kernel.org, linux-clk@vger.kernel.org,
	dri-devel@lists.freedesktop.org, alsa-devel@alsa-project.org,
	devicetree@vger.kernel.org
Cc: javier@osg.samsung.com, b.zolnierkie@samsung.com,
	sw0312.kim@samsung.com, krzk@kernel.org, cw00.choi@samsung.com,
	broonie@kernel.org, Sylwester Nawrocki <s.nawrocki@samsung.com>,
	robh+dt@kernel.org
Subject: [PATCH RFC 4/7] drm: exynos: Add driver for HDMI audio interface
Date: Fri, 21 Apr 2017 19:19:48 +0200	[thread overview]
Message-ID: <1492795191-31298-5-git-send-email-s.nawrocki@samsung.com> (raw)
In-Reply-To: <1492795191-31298-1-git-send-email-s.nawrocki@samsung.com>

The hdmi-codec interface added in this patch is required to properly
support HDMI audio. Currently the audio part of the SoC internal
HDMI transmitter is configured with fixed values, which makes HDMI
audio working by chance, only on boards equipped with external audio
codec connected in parallel with the HDMI audio transmitter I2S input
interface.

Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
---
 drivers/gpu/drm/exynos/Kconfig       |   1 +
 drivers/gpu/drm/exynos/exynos_hdmi.c | 220 +++++++++++++++++++++++++++++------
 2 files changed, 188 insertions(+), 33 deletions(-)

diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig
index 1d18534..a6edbb6 100644
--- a/drivers/gpu/drm/exynos/Kconfig
+++ b/drivers/gpu/drm/exynos/Kconfig
@@ -3,6 +3,7 @@ config DRM_EXYNOS
 	depends on OF && DRM && (ARCH_S3C64XX || ARCH_EXYNOS || ARCH_MULTIPLATFORM)
 	select DRM_KMS_HELPER
 	select VIDEOMODE_HELPERS
+	select SND_SOC_HDMI_CODEC if SND_SOC
 	help
 	  Choose this option if you have a Samsung SoC EXYNOS chipset.
 	  If M is selected the module will be called exynosdrm.
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c
index 88ccc04..be18023 100644
--- a/drivers/gpu/drm/exynos/exynos_hdmi.c
+++ b/drivers/gpu/drm/exynos/exynos_hdmi.c
@@ -40,7 +40,7 @@
 #include <linux/component.h>
 #include <linux/mfd/syscon.h>
 #include <linux/regmap.h>
-
+#include <sound/hdmi-codec.h>
 #include <drm/exynos_drm.h>
 
 #include "exynos_drm_drv.h"
@@ -110,13 +110,23 @@ struct hdmi_driver_data {
 	struct string_array_spec clk_muxes;
 };
 
+struct hdmi_audio {
+	struct platform_device *pdev;
+	struct hdmi_audio_infoframe infoframe;
+	unsigned int sample_rate;
+	unsigned int sample_width;
+	u8 enable;
+};
+
 struct hdmi_context {
 	struct drm_encoder		encoder;
 	struct device			*dev;
 	struct drm_device		*drm_dev;
 	struct drm_connector		connector;
+	struct hdmi_audio		audio;
 	bool				powered;
 	bool				dvi_mode;
+	struct mutex			mutex;
 	struct delayed_work		hotplug_work;
 	struct drm_display_mode		current_mode;
 	const struct hdmi_driver_data	*drv_data;
@@ -766,6 +776,22 @@ static int hdmi_clk_set_parents(struct hdmi_context *hdata, bool to_phy)
 	return ret;
 }
 
+static int hdmi_audio_infoframe_apply(struct hdmi_context *hdata)
+{
+	struct hdmi_audio_infoframe *infoframe = &hdata->audio.infoframe;
+	u8 buf[HDMI_INFOFRAME_SIZE(AUDIO)];
+	int len;
+
+	len = hdmi_audio_infoframe_pack(infoframe, buf, sizeof(buf));
+	if (len < 0)
+		return len;
+
+	hdmi_reg_writeb(hdata, HDMI_AUI_CON, HDMI_AUI_CON_EVERY_VSYNC);
+	hdmi_reg_write_buf(hdata, HDMI_AUI_HEADER0, buf, len);
+
+	return 0;
+}
+
 static void hdmi_reg_infoframes(struct hdmi_context *hdata)
 {
 	union hdmi_infoframe frm;
@@ -803,15 +829,7 @@ static void hdmi_reg_infoframes(struct hdmi_context *hdata)
 		hdmi_reg_write_buf(hdata, HDMI_VSI_DATA(0), buf + 3, ret - 3);
 	}
 
-	ret = hdmi_audio_infoframe_init(&frm.audio);
-	if (!ret) {
-		frm.audio.channels = 2;
-		ret = hdmi_audio_infoframe_pack(&frm.audio, buf, sizeof(buf));
-	}
-	if (ret > 0) {
-		hdmi_reg_writeb(hdata, HDMI_AUI_CON, HDMI_AUI_CON_EVERY_VSYNC);
-		hdmi_reg_write_buf(hdata, HDMI_AUI_HEADER0, buf, ret);
-	}
+	hdmi_audio_infoframe_apply(hdata);
 }
 
 static enum drm_connector_status hdmi_detect(struct drm_connector *connector,
@@ -993,23 +1011,18 @@ static void hdmi_reg_acr(struct hdmi_context *hdata, u32 freq)
 	hdmi_reg_writeb(hdata, HDMI_ACR_CON, 4);
 }
 
-static void hdmi_audio_init(struct hdmi_context *hdata)
+static void hdmi_audio_config(struct hdmi_context *hdata)
 {
-	u32 sample_rate, bits_per_sample;
-	u32 data_num, bit_ch, sample_frq;
-	u32 val;
+	u32 data_num, sample_freq, val;
+	u32 bit_ch = 1;
 
-	sample_rate = 44100;
-	bits_per_sample = 16;
 
-	switch (bits_per_sample) {
+	switch (hdata->audio.sample_width) {
 	case 20:
 		data_num = 2;
-		bit_ch = 1;
 		break;
 	case 24:
 		data_num = 3;
-		bit_ch = 1;
 		break;
 	default:
 		data_num = 1;
@@ -1017,7 +1030,7 @@ static void hdmi_audio_init(struct hdmi_context *hdata)
 		break;
 	}
 
-	hdmi_reg_acr(hdata, sample_rate);
+	hdmi_reg_acr(hdata, hdata->audio.sample_rate);
 
 	hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CON, HDMI_I2S_IN_DISABLE
 				| HDMI_I2S_AUD_I2S | HDMI_I2S_CUV_I2S_ENABLE
@@ -1028,10 +1041,21 @@ static void hdmi_audio_init(struct hdmi_context *hdata)
 
 	hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CUV, HDMI_I2S_CUV_RL_EN);
 
-	sample_frq = (sample_rate == 44100) ? 0 :
-			(sample_rate == 48000) ? 2 :
-			(sample_rate == 32000) ? 3 :
-			(sample_rate == 96000) ? 0xa : 0x0;
+	switch(hdata->audio.sample_rate) {
+	case 32000:
+		sample_freq = 0x3;
+		break;
+	case 48000:
+		sample_freq = 0x2;
+		break;
+	case 96000:
+		sample_freq = 0xa;
+		break;
+	case 44100:
+	default:
+		sample_freq = 0;
+		break;
+	}
 
 	hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_DIS);
 	hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_EN);
@@ -1065,7 +1089,7 @@ static void hdmi_audio_init(struct hdmi_context *hdata)
 	hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_1, HDMI_I2S_CD_PLAYER);
 	hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_2, HDMI_I2S_SET_SOURCE_NUM(0));
 	hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_3, HDMI_I2S_CLK_ACCUR_LEVEL_2
-			| HDMI_I2S_SET_SMP_FREQ(sample_frq));
+			| HDMI_I2S_SET_SMP_FREQ(sample_freq));
 	hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_4,
 			HDMI_I2S_ORG_SMP_FREQ_44_1
 			| HDMI_I2S_WORD_LEN_MAX24_24BITS
@@ -1074,13 +1098,15 @@ static void hdmi_audio_init(struct hdmi_context *hdata)
 	hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_CON, HDMI_I2S_CH_STATUS_RELOAD);
 }
 
-static void hdmi_audio_control(struct hdmi_context *hdata, bool onoff)
+static void hdmi_audio_control(struct hdmi_context *hdata)
 {
+	bool enable = hdata->audio.enable;
+
 	if (hdata->dvi_mode)
 		return;
 
-	hdmi_reg_writeb(hdata, HDMI_AUI_CON, onoff ? 2 : 0);
-	hdmi_reg_writemask(hdata, HDMI_CON_0, onoff ?
+	hdmi_reg_writeb(hdata, HDMI_AUI_CON, enable ? 2 : 0);
+	hdmi_reg_writemask(hdata, HDMI_CON_0, enable ?
 			HDMI_ASP_EN : HDMI_ASP_DIS, HDMI_ASP_MASK);
 }
 
@@ -1400,9 +1426,9 @@ static void hdmi_conf_apply(struct hdmi_context *hdata)
 {
 	hdmi_start(hdata, false);
 	hdmi_conf_init(hdata);
-	hdmi_audio_init(hdata);
+	hdmi_audio_config(hdata);
 	hdmi_mode_apply(hdata);
-	hdmi_audio_control(hdata, true);
+	hdmi_audio_control(hdata);
 }
 
 static void hdmi_mode_set(struct drm_encoder *encoder,
@@ -1476,8 +1502,12 @@ static void hdmi_enable(struct drm_encoder *encoder)
 {
 	struct hdmi_context *hdata = encoder_to_hdmi(encoder);
 
+	mutex_lock(&hdata->mutex);
+
 	hdmiphy_enable(hdata);
 	hdmi_conf_apply(hdata);
+
+	mutex_unlock(&hdata->mutex);
 }
 
 static void hdmi_disable(struct drm_encoder *encoder)
@@ -1486,6 +1516,8 @@ static void hdmi_disable(struct drm_encoder *encoder)
 	struct drm_crtc *crtc = encoder->crtc;
 	const struct drm_crtc_helper_funcs *funcs = NULL;
 
+	mutex_lock(&hdata->mutex);
+
 	if (!hdata->powered)
 		return;
 
@@ -1506,6 +1538,8 @@ static void hdmi_disable(struct drm_encoder *encoder)
 	cancel_delayed_work(&hdata->hotplug_work);
 
 	hdmiphy_disable(hdata);
+
+	mutex_unlock(&hdata->mutex);
 }
 
 static const struct drm_encoder_helper_funcs exynos_hdmi_encoder_helper_funcs = {
@@ -1519,6 +1553,109 @@ static void hdmi_disable(struct drm_encoder *encoder)
 	.destroy = drm_encoder_cleanup,
 };
 
+static void hdmi_audio_shutdown(struct device *dev, void *data)
+{
+	struct hdmi_context *hdata = dev_get_drvdata(dev);
+
+	mutex_lock(&hdata->mutex);
+
+	hdata->audio.enable = false;
+
+	if (hdata->powered)
+		hdmi_audio_control(hdata);
+
+	mutex_unlock(&hdata->mutex);
+}
+
+static int hdmi_audio_hw_params(struct device *dev, void *data,
+				struct hdmi_codec_daifmt *daifmt,
+				struct hdmi_codec_params *params)
+{
+	struct hdmi_context *hdata = dev_get_drvdata(dev);
+
+	if (daifmt->fmt != HDMI_I2S || 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;
+	}
+
+	mutex_lock(&hdata->mutex);
+
+	hdata->audio.sample_rate = params->sample_rate;
+	hdata->audio.sample_width = params->sample_width;
+	hdata->audio.infoframe = params->cea;
+
+	if (hdata->powered) {
+		hdmi_audio_config(hdata);
+		hdmi_audio_infoframe_apply(hdata);
+	}
+
+	mutex_unlock(&hdata->mutex);
+
+	return 0;
+}
+
+static int hdmi_audio_digital_mute(struct device *dev, void *data, bool mute)
+{
+	struct hdmi_context *hdata = dev_get_drvdata(dev);
+
+	mutex_lock(&hdata->mutex);
+
+	hdata->audio.enable = !mute;
+
+	if (hdata->powered)
+		hdmi_audio_control(hdata);
+
+	mutex_unlock(&hdata->mutex);
+
+	return 0;
+}
+
+static int hdmi_audio_get_eld(struct device *dev, void *data, uint8_t *buf,
+			      size_t len)
+{
+	struct hdmi_context *hdata = dev_get_drvdata(dev);
+	struct drm_connector *connector = &hdata->connector;
+
+	memcpy(buf, connector->eld, min(sizeof(connector->eld), len));
+
+	return 0;
+}
+
+static const struct hdmi_codec_ops audio_codec_ops = {
+	.hw_params = hdmi_audio_hw_params,
+	.audio_shutdown = hdmi_audio_shutdown,
+	.digital_mute = hdmi_audio_digital_mute,
+	.get_eld = hdmi_audio_get_eld,
+};
+
+static int hdmi_register_audio_device(struct hdmi_context *hdata)
+{
+	struct hdmi_codec_pdata codec_data = {
+		.ops = &audio_codec_ops,
+		.max_i2s_channels = 6,
+		.i2s = 1,
+	};
+
+	hdata->audio.pdev = platform_device_register_data(
+		hdata->dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO,
+		&codec_data, sizeof(codec_data));
+
+	if (IS_ERR(hdata->audio.pdev))
+		return PTR_ERR(hdata->audio.pdev);
+
+	return 0;
+}
+
+static void hdmi_unregister_audio_device(struct hdmi_context *hdata)
+{
+	platform_device_unregister(hdata->audio.pdev);
+}
+
 static void hdmi_hotplug_work_func(struct work_struct *work)
 {
 	struct hdmi_context *hdata;
@@ -1703,6 +1840,7 @@ static int hdmi_bind(struct device *dev, struct device *master, void *data)
 	struct drm_device *drm_dev = data;
 	struct hdmi_context *hdata = dev_get_drvdata(dev);
 	struct drm_encoder *encoder = &hdata->encoder;
+	struct hdmi_audio_infoframe *audio_infoframe = &hdata->audio.infoframe;
 	int ret, pipe;
 
 	hdata->drm_dev = drm_dev;
@@ -1720,6 +1858,12 @@ static int hdmi_bind(struct device *dev, struct device *master, void *data)
 
 	DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
 
+	hdmi_audio_infoframe_init(audio_infoframe);
+	audio_infoframe->coding_type = HDMI_AUDIO_CODING_TYPE_STREAM;
+	audio_infoframe->sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM;
+	audio_infoframe->sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM;
+	audio_infoframe->channels = 2;
+
 	drm_encoder_init(drm_dev, encoder, &exynos_hdmi_encoder_funcs,
 			 DRM_MODE_ENCODER_TMDS, NULL);
 
@@ -1822,11 +1966,11 @@ static int hdmi_probe(struct platform_device *pdev)
 		return -ENOMEM;
 
 	hdata->drv_data = of_device_get_match_data(dev);
-
 	platform_set_drvdata(pdev, hdata);
-
 	hdata->dev = dev;
 
+	mutex_init(&hdata->mutex);
+
 	ret = hdmi_resources_init(hdata);
 	if (ret) {
 		if (ret != -EPROBE_DEFER)
@@ -1880,12 +2024,19 @@ static int hdmi_probe(struct platform_device *pdev)
 
 	pm_runtime_enable(dev);
 
-	ret = component_add(&pdev->dev, &hdmi_component_ops);
+	ret = hdmi_register_audio_device(hdata);
 	if (ret)
 		goto err_disable_pm_runtime;
 
+	ret = component_add(&pdev->dev, &hdmi_component_ops);
+	if (ret)
+		goto err_unregister_audio;
+
 	return ret;
 
+err_unregister_audio:
+	hdmi_unregister_audio_device(hdata);
+
 err_disable_pm_runtime:
 	pm_runtime_disable(dev);
 
@@ -1906,6 +2057,7 @@ static int hdmi_remove(struct platform_device *pdev)
 
 	cancel_delayed_work_sync(&hdata->hotplug_work);
 
+	hdmi_unregister_audio_device(hdata);
 	component_del(&pdev->dev, &hdmi_component_ops);
 
 	pm_runtime_disable(&pdev->dev);
@@ -1921,6 +2073,8 @@ static int hdmi_remove(struct platform_device *pdev)
 
 	put_device(&hdata->ddc_adpt->dev);
 
+	mutex_destroy(&hdata->mutex);
+
 	return 0;
 }
 
-- 
1.9.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

WARNING: multiple messages have this Message-ID (diff)
From: Sylwester Nawrocki <s.nawrocki@samsung.com>
To: linux-samsung-soc@vger.kernel.org, linux-clk@vger.kernel.org,
	dri-devel@lists.freedesktop.org, alsa-devel@alsa-project.org,
	devicetree@vger.kernel.org
Cc: inki.dae@samsung.com, sw0312.kim@samsung.com,
	cw00.choi@samsung.com, javier@osg.samsung.com, krzk@kernel.org,
	jy0922.shim@samsung.com, broonie@kernel.org, robh+dt@kernel.org,
	b.zolnierkie@samsung.com,
	Sylwester Nawrocki <s.nawrocki@samsung.com>
Subject: [PATCH RFC 4/7] drm: exynos: Add driver for HDMI audio interface
Date: Fri, 21 Apr 2017 19:19:48 +0200	[thread overview]
Message-ID: <1492795191-31298-5-git-send-email-s.nawrocki@samsung.com> (raw)
In-Reply-To: <1492795191-31298-1-git-send-email-s.nawrocki@samsung.com>

The hdmi-codec interface added in this patch is required to properly
support HDMI audio. Currently the audio part of the SoC internal
HDMI transmitter is configured with fixed values, which makes HDMI
audio working by chance, only on boards equipped with external audio
codec connected in parallel with the HDMI audio transmitter I2S input
interface.

Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
---
 drivers/gpu/drm/exynos/Kconfig       |   1 +
 drivers/gpu/drm/exynos/exynos_hdmi.c | 220 +++++++++++++++++++++++++++++------
 2 files changed, 188 insertions(+), 33 deletions(-)

diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig
index 1d18534..a6edbb6 100644
--- a/drivers/gpu/drm/exynos/Kconfig
+++ b/drivers/gpu/drm/exynos/Kconfig
@@ -3,6 +3,7 @@ config DRM_EXYNOS
 	depends on OF && DRM && (ARCH_S3C64XX || ARCH_EXYNOS || ARCH_MULTIPLATFORM)
 	select DRM_KMS_HELPER
 	select VIDEOMODE_HELPERS
+	select SND_SOC_HDMI_CODEC if SND_SOC
 	help
 	  Choose this option if you have a Samsung SoC EXYNOS chipset.
 	  If M is selected the module will be called exynosdrm.
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c
index 88ccc04..be18023 100644
--- a/drivers/gpu/drm/exynos/exynos_hdmi.c
+++ b/drivers/gpu/drm/exynos/exynos_hdmi.c
@@ -40,7 +40,7 @@
 #include <linux/component.h>
 #include <linux/mfd/syscon.h>
 #include <linux/regmap.h>
-
+#include <sound/hdmi-codec.h>
 #include <drm/exynos_drm.h>
 
 #include "exynos_drm_drv.h"
@@ -110,13 +110,23 @@ struct hdmi_driver_data {
 	struct string_array_spec clk_muxes;
 };
 
+struct hdmi_audio {
+	struct platform_device *pdev;
+	struct hdmi_audio_infoframe infoframe;
+	unsigned int sample_rate;
+	unsigned int sample_width;
+	u8 enable;
+};
+
 struct hdmi_context {
 	struct drm_encoder		encoder;
 	struct device			*dev;
 	struct drm_device		*drm_dev;
 	struct drm_connector		connector;
+	struct hdmi_audio		audio;
 	bool				powered;
 	bool				dvi_mode;
+	struct mutex			mutex;
 	struct delayed_work		hotplug_work;
 	struct drm_display_mode		current_mode;
 	const struct hdmi_driver_data	*drv_data;
@@ -766,6 +776,22 @@ static int hdmi_clk_set_parents(struct hdmi_context *hdata, bool to_phy)
 	return ret;
 }
 
+static int hdmi_audio_infoframe_apply(struct hdmi_context *hdata)
+{
+	struct hdmi_audio_infoframe *infoframe = &hdata->audio.infoframe;
+	u8 buf[HDMI_INFOFRAME_SIZE(AUDIO)];
+	int len;
+
+	len = hdmi_audio_infoframe_pack(infoframe, buf, sizeof(buf));
+	if (len < 0)
+		return len;
+
+	hdmi_reg_writeb(hdata, HDMI_AUI_CON, HDMI_AUI_CON_EVERY_VSYNC);
+	hdmi_reg_write_buf(hdata, HDMI_AUI_HEADER0, buf, len);
+
+	return 0;
+}
+
 static void hdmi_reg_infoframes(struct hdmi_context *hdata)
 {
 	union hdmi_infoframe frm;
@@ -803,15 +829,7 @@ static void hdmi_reg_infoframes(struct hdmi_context *hdata)
 		hdmi_reg_write_buf(hdata, HDMI_VSI_DATA(0), buf + 3, ret - 3);
 	}
 
-	ret = hdmi_audio_infoframe_init(&frm.audio);
-	if (!ret) {
-		frm.audio.channels = 2;
-		ret = hdmi_audio_infoframe_pack(&frm.audio, buf, sizeof(buf));
-	}
-	if (ret > 0) {
-		hdmi_reg_writeb(hdata, HDMI_AUI_CON, HDMI_AUI_CON_EVERY_VSYNC);
-		hdmi_reg_write_buf(hdata, HDMI_AUI_HEADER0, buf, ret);
-	}
+	hdmi_audio_infoframe_apply(hdata);
 }
 
 static enum drm_connector_status hdmi_detect(struct drm_connector *connector,
@@ -993,23 +1011,18 @@ static void hdmi_reg_acr(struct hdmi_context *hdata, u32 freq)
 	hdmi_reg_writeb(hdata, HDMI_ACR_CON, 4);
 }
 
-static void hdmi_audio_init(struct hdmi_context *hdata)
+static void hdmi_audio_config(struct hdmi_context *hdata)
 {
-	u32 sample_rate, bits_per_sample;
-	u32 data_num, bit_ch, sample_frq;
-	u32 val;
+	u32 data_num, sample_freq, val;
+	u32 bit_ch = 1;
 
-	sample_rate = 44100;
-	bits_per_sample = 16;
 
-	switch (bits_per_sample) {
+	switch (hdata->audio.sample_width) {
 	case 20:
 		data_num = 2;
-		bit_ch = 1;
 		break;
 	case 24:
 		data_num = 3;
-		bit_ch = 1;
 		break;
 	default:
 		data_num = 1;
@@ -1017,7 +1030,7 @@ static void hdmi_audio_init(struct hdmi_context *hdata)
 		break;
 	}
 
-	hdmi_reg_acr(hdata, sample_rate);
+	hdmi_reg_acr(hdata, hdata->audio.sample_rate);
 
 	hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CON, HDMI_I2S_IN_DISABLE
 				| HDMI_I2S_AUD_I2S | HDMI_I2S_CUV_I2S_ENABLE
@@ -1028,10 +1041,21 @@ static void hdmi_audio_init(struct hdmi_context *hdata)
 
 	hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CUV, HDMI_I2S_CUV_RL_EN);
 
-	sample_frq = (sample_rate == 44100) ? 0 :
-			(sample_rate == 48000) ? 2 :
-			(sample_rate == 32000) ? 3 :
-			(sample_rate == 96000) ? 0xa : 0x0;
+	switch(hdata->audio.sample_rate) {
+	case 32000:
+		sample_freq = 0x3;
+		break;
+	case 48000:
+		sample_freq = 0x2;
+		break;
+	case 96000:
+		sample_freq = 0xa;
+		break;
+	case 44100:
+	default:
+		sample_freq = 0;
+		break;
+	}
 
 	hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_DIS);
 	hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_EN);
@@ -1065,7 +1089,7 @@ static void hdmi_audio_init(struct hdmi_context *hdata)
 	hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_1, HDMI_I2S_CD_PLAYER);
 	hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_2, HDMI_I2S_SET_SOURCE_NUM(0));
 	hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_3, HDMI_I2S_CLK_ACCUR_LEVEL_2
-			| HDMI_I2S_SET_SMP_FREQ(sample_frq));
+			| HDMI_I2S_SET_SMP_FREQ(sample_freq));
 	hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_4,
 			HDMI_I2S_ORG_SMP_FREQ_44_1
 			| HDMI_I2S_WORD_LEN_MAX24_24BITS
@@ -1074,13 +1098,15 @@ static void hdmi_audio_init(struct hdmi_context *hdata)
 	hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_CON, HDMI_I2S_CH_STATUS_RELOAD);
 }
 
-static void hdmi_audio_control(struct hdmi_context *hdata, bool onoff)
+static void hdmi_audio_control(struct hdmi_context *hdata)
 {
+	bool enable = hdata->audio.enable;
+
 	if (hdata->dvi_mode)
 		return;
 
-	hdmi_reg_writeb(hdata, HDMI_AUI_CON, onoff ? 2 : 0);
-	hdmi_reg_writemask(hdata, HDMI_CON_0, onoff ?
+	hdmi_reg_writeb(hdata, HDMI_AUI_CON, enable ? 2 : 0);
+	hdmi_reg_writemask(hdata, HDMI_CON_0, enable ?
 			HDMI_ASP_EN : HDMI_ASP_DIS, HDMI_ASP_MASK);
 }
 
@@ -1400,9 +1426,9 @@ static void hdmi_conf_apply(struct hdmi_context *hdata)
 {
 	hdmi_start(hdata, false);
 	hdmi_conf_init(hdata);
-	hdmi_audio_init(hdata);
+	hdmi_audio_config(hdata);
 	hdmi_mode_apply(hdata);
-	hdmi_audio_control(hdata, true);
+	hdmi_audio_control(hdata);
 }
 
 static void hdmi_mode_set(struct drm_encoder *encoder,
@@ -1476,8 +1502,12 @@ static void hdmi_enable(struct drm_encoder *encoder)
 {
 	struct hdmi_context *hdata = encoder_to_hdmi(encoder);
 
+	mutex_lock(&hdata->mutex);
+
 	hdmiphy_enable(hdata);
 	hdmi_conf_apply(hdata);
+
+	mutex_unlock(&hdata->mutex);
 }
 
 static void hdmi_disable(struct drm_encoder *encoder)
@@ -1486,6 +1516,8 @@ static void hdmi_disable(struct drm_encoder *encoder)
 	struct drm_crtc *crtc = encoder->crtc;
 	const struct drm_crtc_helper_funcs *funcs = NULL;
 
+	mutex_lock(&hdata->mutex);
+
 	if (!hdata->powered)
 		return;
 
@@ -1506,6 +1538,8 @@ static void hdmi_disable(struct drm_encoder *encoder)
 	cancel_delayed_work(&hdata->hotplug_work);
 
 	hdmiphy_disable(hdata);
+
+	mutex_unlock(&hdata->mutex);
 }
 
 static const struct drm_encoder_helper_funcs exynos_hdmi_encoder_helper_funcs = {
@@ -1519,6 +1553,109 @@ static void hdmi_disable(struct drm_encoder *encoder)
 	.destroy = drm_encoder_cleanup,
 };
 
+static void hdmi_audio_shutdown(struct device *dev, void *data)
+{
+	struct hdmi_context *hdata = dev_get_drvdata(dev);
+
+	mutex_lock(&hdata->mutex);
+
+	hdata->audio.enable = false;
+
+	if (hdata->powered)
+		hdmi_audio_control(hdata);
+
+	mutex_unlock(&hdata->mutex);
+}
+
+static int hdmi_audio_hw_params(struct device *dev, void *data,
+				struct hdmi_codec_daifmt *daifmt,
+				struct hdmi_codec_params *params)
+{
+	struct hdmi_context *hdata = dev_get_drvdata(dev);
+
+	if (daifmt->fmt != HDMI_I2S || 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;
+	}
+
+	mutex_lock(&hdata->mutex);
+
+	hdata->audio.sample_rate = params->sample_rate;
+	hdata->audio.sample_width = params->sample_width;
+	hdata->audio.infoframe = params->cea;
+
+	if (hdata->powered) {
+		hdmi_audio_config(hdata);
+		hdmi_audio_infoframe_apply(hdata);
+	}
+
+	mutex_unlock(&hdata->mutex);
+
+	return 0;
+}
+
+static int hdmi_audio_digital_mute(struct device *dev, void *data, bool mute)
+{
+	struct hdmi_context *hdata = dev_get_drvdata(dev);
+
+	mutex_lock(&hdata->mutex);
+
+	hdata->audio.enable = !mute;
+
+	if (hdata->powered)
+		hdmi_audio_control(hdata);
+
+	mutex_unlock(&hdata->mutex);
+
+	return 0;
+}
+
+static int hdmi_audio_get_eld(struct device *dev, void *data, uint8_t *buf,
+			      size_t len)
+{
+	struct hdmi_context *hdata = dev_get_drvdata(dev);
+	struct drm_connector *connector = &hdata->connector;
+
+	memcpy(buf, connector->eld, min(sizeof(connector->eld), len));
+
+	return 0;
+}
+
+static const struct hdmi_codec_ops audio_codec_ops = {
+	.hw_params = hdmi_audio_hw_params,
+	.audio_shutdown = hdmi_audio_shutdown,
+	.digital_mute = hdmi_audio_digital_mute,
+	.get_eld = hdmi_audio_get_eld,
+};
+
+static int hdmi_register_audio_device(struct hdmi_context *hdata)
+{
+	struct hdmi_codec_pdata codec_data = {
+		.ops = &audio_codec_ops,
+		.max_i2s_channels = 6,
+		.i2s = 1,
+	};
+
+	hdata->audio.pdev = platform_device_register_data(
+		hdata->dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO,
+		&codec_data, sizeof(codec_data));
+
+	if (IS_ERR(hdata->audio.pdev))
+		return PTR_ERR(hdata->audio.pdev);
+
+	return 0;
+}
+
+static void hdmi_unregister_audio_device(struct hdmi_context *hdata)
+{
+	platform_device_unregister(hdata->audio.pdev);
+}
+
 static void hdmi_hotplug_work_func(struct work_struct *work)
 {
 	struct hdmi_context *hdata;
@@ -1703,6 +1840,7 @@ static int hdmi_bind(struct device *dev, struct device *master, void *data)
 	struct drm_device *drm_dev = data;
 	struct hdmi_context *hdata = dev_get_drvdata(dev);
 	struct drm_encoder *encoder = &hdata->encoder;
+	struct hdmi_audio_infoframe *audio_infoframe = &hdata->audio.infoframe;
 	int ret, pipe;
 
 	hdata->drm_dev = drm_dev;
@@ -1720,6 +1858,12 @@ static int hdmi_bind(struct device *dev, struct device *master, void *data)
 
 	DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
 
+	hdmi_audio_infoframe_init(audio_infoframe);
+	audio_infoframe->coding_type = HDMI_AUDIO_CODING_TYPE_STREAM;
+	audio_infoframe->sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM;
+	audio_infoframe->sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM;
+	audio_infoframe->channels = 2;
+
 	drm_encoder_init(drm_dev, encoder, &exynos_hdmi_encoder_funcs,
 			 DRM_MODE_ENCODER_TMDS, NULL);
 
@@ -1822,11 +1966,11 @@ static int hdmi_probe(struct platform_device *pdev)
 		return -ENOMEM;
 
 	hdata->drv_data = of_device_get_match_data(dev);
-
 	platform_set_drvdata(pdev, hdata);
-
 	hdata->dev = dev;
 
+	mutex_init(&hdata->mutex);
+
 	ret = hdmi_resources_init(hdata);
 	if (ret) {
 		if (ret != -EPROBE_DEFER)
@@ -1880,12 +2024,19 @@ static int hdmi_probe(struct platform_device *pdev)
 
 	pm_runtime_enable(dev);
 
-	ret = component_add(&pdev->dev, &hdmi_component_ops);
+	ret = hdmi_register_audio_device(hdata);
 	if (ret)
 		goto err_disable_pm_runtime;
 
+	ret = component_add(&pdev->dev, &hdmi_component_ops);
+	if (ret)
+		goto err_unregister_audio;
+
 	return ret;
 
+err_unregister_audio:
+	hdmi_unregister_audio_device(hdata);
+
 err_disable_pm_runtime:
 	pm_runtime_disable(dev);
 
@@ -1906,6 +2057,7 @@ static int hdmi_remove(struct platform_device *pdev)
 
 	cancel_delayed_work_sync(&hdata->hotplug_work);
 
+	hdmi_unregister_audio_device(hdata);
 	component_del(&pdev->dev, &hdmi_component_ops);
 
 	pm_runtime_disable(&pdev->dev);
@@ -1921,6 +2073,8 @@ static int hdmi_remove(struct platform_device *pdev)
 
 	put_device(&hdata->ddc_adpt->dev);
 
+	mutex_destroy(&hdata->mutex);
+
 	return 0;
 }
 
-- 
1.9.1


  parent reply	other threads:[~2017-04-21 17:19 UTC|newest]

Thread overview: 46+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <CGME20170421172007epcas1p25dba753df34c309e6b00ed08ae930043@epcas1p2.samsung.com>
2017-04-21 17:19 ` [PATCH RFC 0/7] HDMI audio support for Exynos Odroid boards Sylwester Nawrocki
2017-04-21 17:19   ` Sylwester Nawrocki
     [not found]   ` <CGME20170421172016epcas5p342c16e1219c1205a44a84eaa770ad5ac@epcas5p3.samsung.com>
2017-04-21 17:19     ` [PATCH RFC 1/7] clk: samsung: Add enable/disable operation for PLL36XX clocks Sylwester Nawrocki
2017-04-21 17:19       ` Sylwester Nawrocki
2017-04-22  2:51       ` Stephen Boyd
2017-04-24 11:12         ` Sylwester Nawrocki
2017-04-24 11:12           ` Sylwester Nawrocki
2017-04-22 15:22       ` Krzysztof Kozlowski
2017-04-24 11:12         ` Sylwester Nawrocki
2017-04-24 11:12           ` Sylwester Nawrocki
2017-04-24 11:18           ` Krzysztof Kozlowski
2017-04-24 11:18             ` Krzysztof Kozlowski
2017-04-24 11:35             ` Sylwester Nawrocki
2017-04-24 11:50               ` Krzysztof Kozlowski
     [not found]   ` <CGME20170421172023epcas1p3dd7397c8c61daa548ffac88a76e53113@epcas1p3.samsung.com>
2017-04-21 17:19     ` [PATCH RFC 2/7] clk: samsung: Add definitions of some audio related clocks Sylwester Nawrocki
2017-04-21 17:19       ` Sylwester Nawrocki
2017-04-22 15:27       ` Krzysztof Kozlowski
2017-06-08 10:00         ` Sylwester Nawrocki
     [not found]   ` <CGME20170421172029epcas5p1b32ed135c5e0f1b86fbcc54279126349@epcas5p1.samsung.com>
2017-04-21 17:19     ` [PATCH RFC 3/7] clk: samsung: exynos542x: Add EPLL rate table Sylwester Nawrocki
2017-04-21 17:19       ` Sylwester Nawrocki
2017-04-22 15:28       ` Krzysztof Kozlowski
     [not found]   ` <CGME20170421172034epcas5p1dafc1794a7649ec30053f7f62d1831e5@epcas5p1.samsung.com>
2017-04-21 17:19     ` Sylwester Nawrocki [this message]
2017-04-21 17:19       ` [PATCH RFC 4/7] drm: exynos: Add driver for HDMI audio interface Sylwester Nawrocki
2017-04-22 15:31       ` Krzysztof Kozlowski
     [not found]   ` <CGME20170421172040epcas5p2ee7191a899c52ea3f077837dc2e865ca@epcas5p2.samsung.com>
2017-04-21 17:19     ` [PATCH RFC 5/7] ASoC: Add Odroid sound DT bindings documentation Sylwester Nawrocki
2017-04-21 17:19       ` Sylwester Nawrocki
2017-04-21 17:28       ` Applied "ASoC: Add Odroid sound DT bindings documentation" to the asoc tree Mark Brown
2017-04-21 17:28         ` Mark Brown
2017-04-21 17:31         ` Krzysztof Kozlowski
2017-04-21 17:31           ` Krzysztof Kozlowski
2017-04-21 17:58           ` Mark Brown
2017-04-21 17:58             ` Mark Brown
2017-04-21 18:01             ` Krzysztof Kozlowski
2017-04-21 18:01               ` Krzysztof Kozlowski
2017-04-21 18:07             ` Krzysztof Kozlowski
2017-04-21 18:07               ` Krzysztof Kozlowski
2017-04-24  9:57               ` Mark Brown
2017-04-28 17:03       ` [PATCH RFC 5/7] ASoC: Add Odroid sound DT bindings documentation Rob Herring
2017-06-09 16:53         ` Sylwester Nawrocki
     [not found]   ` <CGME20170421172046epcas1p32778006ff0ddc30083d49b31497b5b5b@epcas1p3.samsung.com>
2017-04-21 17:19     ` [PATCH RFC 6/7] ASoC: samsung: Add Odroid ASoC machine driver Sylwester Nawrocki
2017-04-21 17:19       ` Sylwester Nawrocki
2017-04-21 17:28       ` Applied "ASoC: samsung: Add Odroid ASoC machine driver" to the asoc tree Mark Brown
2017-04-21 17:28         ` Mark Brown
     [not found]   ` <CGME20170421172053epcas1p26cbba167969b1bffb48e3b7e6f5c3604@epcas1p2.samsung.com>
2017-04-21 17:19     ` [PATCH RFC 7/7] ARM: dts: samsung: Switch to dedicated Odroid sound card binding Sylwester Nawrocki
2017-04-21 17:19       ` Sylwester Nawrocki
2017-04-21 18:43       ` Krzysztof Kozlowski

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=1492795191-31298-5-git-send-email-s.nawrocki@samsung.com \
    --to=s.nawrocki@samsung.com \
    --cc=alsa-devel@alsa-project.org \
    --cc=b.zolnierkie@samsung.com \
    --cc=broonie@kernel.org \
    --cc=cw00.choi@samsung.com \
    --cc=devicetree@vger.kernel.org \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=javier@osg.samsung.com \
    --cc=krzk@kernel.org \
    --cc=linux-clk@vger.kernel.org \
    --cc=linux-samsung-soc@vger.kernel.org \
    --cc=robh+dt@kernel.org \
    --cc=sw0312.kim@samsung.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.