@@ -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
@@ -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"
@@ -421,6 +422,148 @@ static int mtk_hdmi_dt_parse_pdata(struct mtk_hdmi *hdmi,
return 0;
}
+/*
+ * 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)
+ 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:
+ case 44100:
+ case 48000:
+ case 88200:
+ case 96000:
+ case 176400:
+ case 192000:
+ 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 DAI format %d\n", __func__,
+ daifmt->fmt);
+ return -EINVAL;
+ }
+
+ memcpy(&hdmi_params.codec_params, params,
+ sizeof(hdmi_params.codec_params));
+
+ 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;
@@ -452,6 +595,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);
@@ -197,46 +197,30 @@ static int mtk_hdmi_aud_set_input(struct mtk_hdmi *hdmi)
static int mtk_hdmi_aud_set_src(struct mtk_hdmi *hdmi,
struct drm_display_mode *display_mode)
{
+ unsigned int sample_rate = hdmi->aud_param.codec_params.sample_rate;
mtk_hdmi_aud_on_off_hw_ncts(hdmi, false);
if (hdmi->aud_param.aud_input_type == HDMI_AUD_INPUT_I2S) {
- switch (hdmi->aud_param.aud_hdmi_fs) {
- case HDMI_AUDIO_SAMPLE_FREQUENCY_32000:
- case HDMI_AUDIO_SAMPLE_FREQUENCY_44100:
- case HDMI_AUDIO_SAMPLE_FREQUENCY_48000:
- case HDMI_AUDIO_SAMPLE_FREQUENCY_88200:
- case HDMI_AUDIO_SAMPLE_FREQUENCY_96000:
+ switch (sample_rate) {
+ case 32000:
+ case 44100:
+ case 48000:
+ case 88200:
+ case 96000:
mtk_hdmi_hw_aud_src_off(hdmi);
/* mtk_hdmi_hw_aud_src_enable(hdmi, false); */
- mtk_hdmi_hw_aud_set_mclk(
- hdmi,
- hdmi->aud_param.aud_mclk);
+ mtk_hdmi_hw_aud_set_mclk(hdmi,
+ hdmi->aud_param.aud_mclk);
mtk_hdmi_hw_aud_aclk_inv_enable(hdmi, false);
break;
default:
break;
}
} else {
- switch (hdmi->aud_param.iec_frame_fs) {
- case HDMI_IEC_32K:
- hdmi->aud_param.aud_hdmi_fs =
- HDMI_AUDIO_SAMPLE_FREQUENCY_32000;
- mtk_hdmi_hw_aud_src_off(hdmi);
- mtk_hdmi_hw_aud_set_mclk(hdmi,
- HDMI_AUD_MCLK_128FS);
- mtk_hdmi_hw_aud_aclk_inv_enable(hdmi, false);
- break;
- case HDMI_IEC_48K:
- hdmi->aud_param.aud_hdmi_fs =
- HDMI_AUDIO_SAMPLE_FREQUENCY_48000;
- mtk_hdmi_hw_aud_src_off(hdmi);
- mtk_hdmi_hw_aud_set_mclk(hdmi,
- HDMI_AUD_MCLK_128FS);
- mtk_hdmi_hw_aud_aclk_inv_enable(hdmi, false);
- break;
- case HDMI_IEC_44K:
- hdmi->aud_param.aud_hdmi_fs =
- HDMI_AUDIO_SAMPLE_FREQUENCY_44100;
+ switch (sample_rate) {
+ case 32000:
+ case 44100:
+ case 48000:
mtk_hdmi_hw_aud_src_off(hdmi);
mtk_hdmi_hw_aud_set_mclk(hdmi,
HDMI_AUD_MCLK_128FS);
@@ -246,20 +230,10 @@ static int mtk_hdmi_aud_set_src(struct mtk_hdmi *hdmi,
break;
}
}
- mtk_hdmi_hw_aud_set_ncts(hdmi, hdmi->aud_param.aud_hdmi_fs,
- display_mode->clock);
- mtk_hdmi_hw_aud_src_reenable(hdmi);
- return 0;
-}
+ mtk_hdmi_hw_aud_set_ncts(hdmi, sample_rate, display_mode->clock);
-static int mtk_hdmi_aud_set_chnl_status(struct mtk_hdmi *hdmi)
-{
- mtk_hdmi_hw_aud_set_channel_status(
- hdmi,
- hdmi->aud_param.hdmi_l_channel_state,
- hdmi->aud_param.hdmi_r_channel_state,
- hdmi->aud_param.aud_hdmi_fs);
+ mtk_hdmi_hw_aud_src_reenable(hdmi);
return 0;
}
@@ -271,7 +245,8 @@ static int mtk_hdmi_aud_output_config(struct mtk_hdmi *hdmi,
mtk_hdmi_aud_set_input(hdmi);
mtk_hdmi_aud_set_src(hdmi, display_mode);
- mtk_hdmi_aud_set_chnl_status(hdmi);
+ mtk_hdmi_hw_aud_set_channel_status(hdmi,
+ hdmi->aud_param.codec_params.iec.status);
usleep_range(50, 100);
@@ -413,15 +388,11 @@ int mtk_hdmi_output_init(struct mtk_hdmi *hdmi)
hdmi->csp = HDMI_COLORSPACE_RGB;
hdmi->output = true;
aud_param->aud_codec = HDMI_AUDIO_CODING_TYPE_PCM;
- aud_param->aud_hdmi_fs = HDMI_AUDIO_SAMPLE_FREQUENCY_48000;
aud_param->aud_sampe_size = HDMI_AUDIO_SAMPLE_SIZE_16;
aud_param->aud_input_type = HDMI_AUD_INPUT_I2S;
aud_param->aud_i2s_fmt = HDMI_I2S_MODE_I2S_24BIT;
aud_param->aud_mclk = HDMI_AUD_MCLK_128FS;
- aud_param->iec_frame_fs = HDMI_IEC_48K;
aud_param->aud_input_chan_type = HDMI_AUD_CHAN_TYPE_2_0;
- aud_param->hdmi_l_channel_state[2] = 2;
- aud_param->hdmi_r_channel_state[2] = 2;
hdmi->init = true;
return 0;
@@ -439,6 +410,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_dbg(hdmi->dev, "codec:%d, input:%d, channel:%d, fs:%d\n",
+ param->aud_codec, param->aud_input_type,
+ param->aud_input_chan_type, param->codec_params.sample_rate);
+ 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)
{
@@ -19,6 +19,7 @@
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/types.h>
+#include <sound/hdmi-codec.h>
struct clk;
struct device;
@@ -144,15 +145,12 @@ enum hdmi_aud_channel_swap_type {
struct hdmi_audio_param {
enum hdmi_audio_coding_type aud_codec;
- enum hdmi_audio_sample_frequency aud_hdmi_fs;
enum hdmi_audio_sample_size aud_sampe_size;
enum hdmi_aud_input_type aud_input_type;
enum hdmi_aud_i2s_fmt aud_i2s_fmt;
enum hdmi_aud_mclk aud_mclk;
- enum hdmi_aud_iec_frame_rate iec_frame_fs;
enum hdmi_aud_channel_type aud_input_chan_type;
- u8 hdmi_l_channel_state[6];
- u8 hdmi_r_channel_state[6];
+ struct hdmi_codec_params codec_params;
};
struct mtk_hdmi {
@@ -201,6 +199,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);
@@ -395,90 +395,18 @@ void mtk_hdmi_hw_aud_set_input_type(struct mtk_hdmi *hdmi,
}
void mtk_hdmi_hw_aud_set_channel_status(struct mtk_hdmi *hdmi,
- u8 *l_chan_status, u8 *r_chan_status,
- enum hdmi_audio_sample_frequency
- aud_hdmi_fs)
-{
- u8 l_status[5];
- u8 r_status[5];
- u8 val = 0;
-
- l_status[0] = l_chan_status[0];
- l_status[1] = l_chan_status[1];
- l_status[2] = l_chan_status[2];
- r_status[0] = r_chan_status[0];
- r_status[1] = r_chan_status[1];
- r_status[2] = r_chan_status[2];
-
- l_status[0] &= ~0x02;
- r_status[0] &= ~0x02;
-
- val = l_chan_status[3] & 0xf0;
- switch (aud_hdmi_fs) {
- case HDMI_AUDIO_SAMPLE_FREQUENCY_32000:
- val |= 0x03;
- break;
- case HDMI_AUDIO_SAMPLE_FREQUENCY_44100:
- break;
- case HDMI_AUDIO_SAMPLE_FREQUENCY_88200:
- val |= 0x08;
- break;
- case HDMI_AUDIO_SAMPLE_FREQUENCY_96000:
- val |= 0x0a;
- break;
- case HDMI_AUDIO_SAMPLE_FREQUENCY_48000:
- val |= 0x02;
- break;
- default:
- val |= 0x02;
- break;
- }
- l_status[3] = val;
- r_status[3] = val;
-
- val = l_chan_status[4];
- val |= ((~(l_status[3] & 0x0f)) << 4);
- l_status[4] = val;
- r_status[4] = val;
-
- val = l_status[0];
- mtk_hdmi_write(hdmi, GRL_I2S_C_STA0, val);
- mtk_hdmi_write(hdmi, GRL_L_STATUS_0, val);
-
- val = r_status[0];
- mtk_hdmi_write(hdmi, GRL_R_STATUS_0, val);
-
- val = l_status[1];
- mtk_hdmi_write(hdmi, GRL_I2S_C_STA1, val);
- mtk_hdmi_write(hdmi, GRL_L_STATUS_1, val);
-
- val = r_status[1];
- mtk_hdmi_write(hdmi, GRL_R_STATUS_1, val);
-
- val = l_status[2];
- mtk_hdmi_write(hdmi, GRL_I2S_C_STA2, val);
- mtk_hdmi_write(hdmi, GRL_L_STATUS_2, val);
-
- val = r_status[2];
- mtk_hdmi_write(hdmi, GRL_R_STATUS_2, val);
-
- val = l_status[3];
- mtk_hdmi_write(hdmi, GRL_I2S_C_STA3, val);
- mtk_hdmi_write(hdmi, GRL_L_STATUS_3, val);
-
- val = r_status[3];
- mtk_hdmi_write(hdmi, GRL_R_STATUS_3, val);
-
- val = l_status[4];
- mtk_hdmi_write(hdmi, GRL_I2S_C_STA4, val);
- mtk_hdmi_write(hdmi, GRL_L_STATUS_4, val);
-
- val = r_status[4];
- mtk_hdmi_write(hdmi, GRL_R_STATUS_4, val);
+ u8 *channel_status)
+{
+ int i;
- for (val = 0; val < 19; val++) {
- mtk_hdmi_write(hdmi, GRL_L_STATUS_5 + val * 4, 0);
- mtk_hdmi_write(hdmi, GRL_R_STATUS_5 + val * 4, 0);
+ for (i = 0; i < 5; i++) {
+ mtk_hdmi_write(hdmi, GRL_I2S_C_STA0 + i * 4, channel_status[i]);
+ mtk_hdmi_write(hdmi, GRL_L_STATUS_0 + i * 4, channel_status[i]);
+ mtk_hdmi_write(hdmi, GRL_R_STATUS_0 + i * 4, channel_status[i]);
+ }
+ for (; i < 24; i++) {
+ mtk_hdmi_write(hdmi, GRL_L_STATUS_0 + i * 4, 0);
+ mtk_hdmi_write(hdmi, GRL_R_STATUS_0 + i * 4, 0);
}
}
@@ -46,9 +46,7 @@ void mtk_hdmi_hw_aud_set_i2s_chan_num(struct mtk_hdmi *hdmi,
void mtk_hdmi_hw_aud_set_input_type(struct mtk_hdmi *hdmi,
enum hdmi_aud_input_type input_type);
void mtk_hdmi_hw_aud_set_channel_status(struct mtk_hdmi *hdmi,
- u8 *l_chan_status, u8 *r_chan_staus,
- enum hdmi_audio_sample_frequency
- aud_hdmi_fs);
+ u8 *channel_status);
void mtk_hdmi_hw_aud_src_enable(struct mtk_hdmi *hdmi, bool enable);
void mtk_hdmi_hw_aud_set_mclk(struct mtk_hdmi *hdmi, enum hdmi_aud_mclk mclk);
void mtk_hdmi_hw_aud_src_off(struct mtk_hdmi *hdmi);