@@ -17,6 +17,8 @@
#include <drm/drm_crtc_helper.h>
#include <drm/drm_edid.h>
+#include <sound/hdmi_drm.h>
+
#include "sti_hdmi.h"
#include "sti_hdmi_tx3g4c28phy.h"
#include "sti_hdmi_tx3g0c55phy.h"
@@ -34,6 +36,8 @@
#define HDMI_DFLT_CHL0_DAT 0x0110
#define HDMI_DFLT_CHL1_DAT 0x0114
#define HDMI_DFLT_CHL2_DAT 0x0118
+#define HDMI_AUDIO_CFG 0x0200
+#define HDMI_SPDIF_FIFO_STATUS 0x0204
#define HDMI_SW_DI_1_HEAD_WORD 0x0210
#define HDMI_SW_DI_1_PKT_WORD0 0x0214
#define HDMI_SW_DI_1_PKT_WORD1 0x0218
@@ -43,6 +47,9 @@
#define HDMI_SW_DI_1_PKT_WORD5 0x0228
#define HDMI_SW_DI_1_PKT_WORD6 0x022C
#define HDMI_SW_DI_CFG 0x0230
+#define HDMI_SAMPLE_FLAT_MASK 0x0244
+#define HDMI_AUDN 0x0400
+#define HDMI_AUD_CTS 0x0404
#define HDMI_SW_DI_2_HEAD_WORD 0x0600
#define HDMI_SW_DI_2_PKT_WORD0 0x0604
#define HDMI_SW_DI_2_PKT_WORD1 0x0608
@@ -52,6 +59,7 @@
#define HDMI_SW_DI_2_PKT_WORD5 0x0618
#define HDMI_SW_DI_2_PKT_WORD6 0x061C
+
#define HDMI_IFRAME_SLOT_AVI 1
#define HDMI_IFRAME_SLOT_AUDIO 2
@@ -109,6 +117,27 @@
#define HDMI_STA_SW_RST BIT(1)
+#define HDMI_AUD_CFG_8CH BIT(0)
+#define HDMI_AUD_CFG_SPDIF_DIV_2 BIT(1)
+#define HDMI_AUD_CFG_SPDIF_DIV_3 BIT(2)
+#define HDMI_AUD_CFG_SPDIF_CLK_DIV_4 (BIT(1) | BIT(1))
+#define HDMI_AUD_CFG_CTS_CLK_128FS BIT(17)
+#define HDMI_AUD_CFG_CH12_VALID BIT(28)
+#define HDMI_AUD_CFG_CH34_VALID BIT(29)
+#define HDMI_AUD_CFG_CH56_VALID BIT(30)
+#define HDMI_AUD_CFG_CH78_VALID BIT(31)
+
+/* sample flat mask */
+#define HDMI_SAMPLE_FLAT_NO 0
+#define HDMI_SAMPLE_FLAT_SP0 BIT(0)
+#define HDMI_SAMPLE_FLAT_SP1 BIT(1)
+#define HDMI_SAMPLE_FLAT_SP2 BIT(2)
+#define HDMI_SAMPLE_FLAT_SP3 BIT(3)
+#define HDMI_SAMPLE_FLAT_ALL (HDMI_SAMPLE_FLAT_SP0 \
+ | HDMI_SAMPLE_FLAT_SP1 \
+ | HDMI_SAMPLE_FLAT_SP2 \
+ | HDMI_SAMPLE_FLAT_SP3)
+
#define HDMI_INFOFRAME_HEADER_TYPE(x) (((x) & 0xff) << 0)
#define HDMI_INFOFRAME_HEADER_VERSION(x) (((x) & 0xff) << 8)
#define HDMI_INFOFRAME_HEADER_LEN(x) (((x) & 0x0f) << 16)
@@ -380,19 +409,13 @@ static int hdmi_avi_infoframe_config(struct sti_hdmi *hdmi)
*/
static int hdmi_audio_infoframe_config(struct sti_hdmi *hdmi)
{
- struct hdmi_audio_infoframe infofame;
+ struct hdmi_audio_infoframe *infoframe;
u8 buffer[HDMI_INFOFRAME_SIZE(AUDIO)];
int ret;
- ret = hdmi_audio_infoframe_init(&infofame);
- if (ret < 0) {
- DRM_ERROR("failed to setup audio infoframe: %d\n", ret);
- return ret;
- }
-
- infofame.channels = 2;
+ infoframe = &hdmi->audio.infoframe;
- ret = hdmi_audio_infoframe_pack(&infofame, buffer, sizeof(buffer));
+ ret = hdmi_audio_infoframe_pack(infoframe, buffer, sizeof(buffer));
if (ret < 0) {
DRM_ERROR("failed to pack audio infoframe: %d\n", ret);
return ret;
@@ -404,6 +427,56 @@ static int hdmi_audio_infoframe_config(struct sti_hdmi *hdmi)
}
/**
+ * set audio frame rate
+ *
+ * @hdmi: pointer on the hdmi internal structure
+ *
+ */
+static int hdmi_audio_set_infoframe(struct sti_hdmi *hdmi,
+ struct hdmi_audio_infoframe *info)
+{
+ struct hdmi_audio_n_cts n_cts;
+ int ret, audio_cfg;
+
+ hdmi->audio.infoframe = *info;
+
+ if (!hdmi->enabled)
+ return 0;
+
+ /* update HDMI registers according to configuration */
+ audio_cfg = HDMI_AUD_CFG_SPDIF_DIV_3 | HDMI_AUD_CFG_CTS_CLK_128FS;
+
+ switch (info->channels) {
+ case 8:
+ audio_cfg |= HDMI_AUD_CFG_CH78_VALID;
+ case 6:
+ audio_cfg |= HDMI_AUD_CFG_CH56_VALID;
+ case 4:
+ audio_cfg |= HDMI_AUD_CFG_CH34_VALID | HDMI_AUD_CFG_8CH;
+ case 2:
+ audio_cfg = HDMI_AUD_CFG_CH12_VALID;
+ break;
+ default:
+ DRM_ERROR("ERROR: Unsupported number of channels (%d)!\n",
+ info->channels);
+ return -EINVAL;
+ }
+
+ hdmi_write(hdmi, audio_cfg, HDMI_AUDIO_CFG);
+
+ /* update N parameter */
+ ret = hdmi_audio_compute_n_cts(info->sample_frequency,
+ hdmi->mode.clock * 1000, &n_cts);
+
+ DRM_DEBUG_DRIVER("n= %d, cts = %d\n", n_cts.n, n_cts.cts);
+
+ /* clear interrupt status */
+ hdmi_write(hdmi, n_cts.n, HDMI_AUDN);
+
+ return 0;
+}
+
+/**
* Software reset of the hdmi subsystem
*
* @hdmi: pointer on the hdmi internal structure
@@ -462,7 +535,6 @@ static void sti_hdmi_disable(struct drm_bridge *bridge)
/* Disable HDMI */
val &= ~HDMI_CFG_DEVICE_EN;
hdmi_write(hdmi, val, HDMI_CFG);
-
hdmi_write(hdmi, 0xffffffff, HDMI_INT_CLR);
/* Stop the phy */
@@ -520,8 +592,9 @@ static void sti_hdmi_pre_enable(struct drm_bridge *bridge)
DRM_ERROR("Unable to configure AVI infoframe\n");
/* Program AUDIO infoframe */
- if (hdmi_audio_infoframe_config(hdmi))
- DRM_ERROR("Unable to configure AUDIO infoframe\n");
+ if (hdmi->audio.enabled)
+ if (hdmi_audio_infoframe_config(hdmi))
+ DRM_ERROR("Unable to configure AUDIO infoframe\n");
/* Sw reset */
hdmi_swreset(hdmi);
@@ -567,6 +640,43 @@ static const struct drm_bridge_funcs sti_hdmi_bridge_funcs = {
.mode_set = sti_hdmi_set_mode,
};
+void sti_hdmi_audio_disable(struct drm_bridge *bridge)
+{
+ struct sti_hdmi *hdmi = bridge->driver_private;
+
+ DRM_ERROR("enter %s\n", __func__);
+ /* mute */
+ hdmi_write(hdmi, HDMI_SAMPLE_FLAT_ALL, HDMI_SAMPLE_FLAT_MASK);
+}
+
+int sti_hdmi_audio_set_mode(struct drm_bridge *bridge,
+ struct hdmi_audio_mode *mode)
+{
+ DRM_ERROR("enter %s\n", __func__);
+ return 0;
+}
+
+void sti_hdmi_audio_pre_enable(struct drm_bridge *bridge)
+{
+ DRM_ERROR("enter %s\n", __func__);
+}
+
+void sti_hdmi_audio_bridge_enable(struct drm_bridge *bridge)
+{
+ struct sti_hdmi *hdmi = bridge->driver_private;
+
+ DRM_ERROR("enter %s\n", __func__);
+ /* unmute */
+ hdmi_write(hdmi, HDMI_SAMPLE_FLAT_NO, HDMI_SAMPLE_FLAT_MASK);
+}
+
+static const struct drm_audio_bridge_funcs sti_hdmi_audio_bridge_funcs = {
+ .pre_enable = sti_hdmi_audio_pre_enable,
+ .enable = sti_hdmi_audio_bridge_enable,
+ .disable = sti_hdmi_audio_disable,
+ .mode_set = sti_hdmi_audio_set_mode,
+};
+
static int sti_hdmi_connector_get_modes(struct drm_connector *connector)
{
struct sti_hdmi_connector *hdmi_connector
@@ -658,6 +768,7 @@ static void sti_hdmi_connector_destroy(struct drm_connector *connector)
struct sti_hdmi_connector *hdmi_connector
= to_sti_hdmi_connector(connector);
+ drm_bridge_remove(connector->encoder->bridge);
drm_connector_unregister(connector);
drm_connector_cleanup(connector);
kfree(hdmi_connector);
@@ -715,8 +826,14 @@ static int sti_hdmi_bind(struct device *dev, struct device *master, void *data)
bridge->driver_private = hdmi;
bridge->funcs = &sti_hdmi_bridge_funcs;
+ bridge->audio_funcs = &sti_hdmi_audio_bridge_funcs;
drm_bridge_attach(drm_dev, bridge);
+ bridge->of_node = dev->of_node;
+ err = drm_bridge_add(bridge);
+ if (err)
+ goto err_adapt;
+
encoder->bridge = bridge;
connector->encoder = encoder;
@@ -748,6 +865,7 @@ err_sysfs:
drm_connector_unregister(drm_connector);
err_connector:
drm_connector_cleanup(drm_connector);
+ drm_bridge_remove(bridge);
err_adapt:
put_device(&hdmi->ddc_adapt->dev);
return -EINVAL;
@@ -877,6 +995,10 @@ static int sti_hdmi_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, hdmi);
+ /* Register audio driver */
+ if (hdmi_drm_codec_register(dev))
+ DRM_INFO("Failed to register HDMI audio driver\n");
+
return component_add(&pdev->dev, &sti_hdmi_ops);
}
@@ -24,6 +24,7 @@ struct hdmi_phy_ops {
void (*stop)(struct sti_hdmi *hdmi);
};
+
/**
* STI hdmi structure
*
@@ -36,6 +37,7 @@ struct hdmi_phy_ops {
* @clk_tmds: hdmi tmds clock
* @clk_phy: hdmi phy clock
* @clk_audio: hdmi audio clock
+ * @audio: hdmi audio state
* @irq: hdmi interrupt number
* @irq_status: interrupt status register
* @phy_ops: phy start/stop operations
@@ -55,6 +57,7 @@ struct sti_hdmi {
struct clk *clk_tmds;
struct clk *clk_phy;
struct clk *clk_audio;
+ struct hdmi_audio_mode audio;
int irq;
u32 irq_status;
struct hdmi_phy_ops *phy_ops;
Add management of the audio bridge Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com> --- drivers/gpu/drm/sti/sti_hdmi.c | 146 +++++++++++++++++++++++++++++++++++++---- drivers/gpu/drm/sti/sti_hdmi.h | 3 + 2 files changed, 137 insertions(+), 12 deletions(-)