diff options
author | Guillaume Ranquet <granquet@baylibre.com> | 2022-09-01 07:41:49 +0300 |
---|---|---|
committer | Dmitry Osipenko <dmitry.osipenko@collabora.com> | 2022-09-04 15:32:42 +0300 |
commit | e71a8ebbe086c3d80189726ef46bcf718cbe194d (patch) | |
tree | e5bef5486e510be563c8314c8bf59113bb6d39a1 /drivers/gpu/drm/mediatek | |
parent | 4652e95e7ea609426f9196b328abedd564be7275 (diff) | |
download | linux-e71a8ebbe086c3d80189726ef46bcf718cbe194d.tar.xz |
drm/mediatek: dp: Audio support for MT8195
This patch adds audio support to the DP driver for MT8195 with up to 8
channels.
Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
Tested-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20220901044149.16782-11-rex-bc.chen@mediatek.com
Diffstat (limited to 'drivers/gpu/drm/mediatek')
-rw-r--r-- | drivers/gpu/drm/mediatek/mtk_dp.c | 482 | ||||
-rw-r--r-- | drivers/gpu/drm/mediatek/mtk_dp_reg.h | 51 |
2 files changed, 532 insertions, 1 deletions
diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c index dd34dae417e5..dfa942ca62da 100644 --- a/drivers/gpu/drm/mediatek/mtk_dp.c +++ b/drivers/gpu/drm/mediatek/mtk_dp.c @@ -29,6 +29,7 @@ #include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/soc/mediatek/mtk_sip_svc.h> +#include <sound/hdmi-codec.h> #include <video/videomode.h> #include "mtk_dp_reg.h" @@ -47,6 +48,8 @@ #define MTK_DP_TBC_BUF_READ_START_ADDR 0x8 #define MTK_DP_TRAIN_VOLTAGE_LEVEL_RETRY 5 #define MTK_DP_TRAIN_DOWNSCALE_RETRY 10 +#define MTK_DP_VERSION 0x11 +#define MTK_DP_SDP_AUI 0x4 enum { MTK_DP_CAL_GLB_BIAS_TRIM = 0, @@ -71,9 +74,18 @@ struct mtk_dp_train_info { unsigned int channel_eq_pattern; }; +struct mtk_dp_audio_cfg { + bool detect_monitor; + int sad_count; + int sample_rate; + int word_length_bits; + int channels; +}; + struct mtk_dp_info { enum dp_pixelformat format; struct videomode vm; + struct mtk_dp_audio_cfg audio_cur_cfg; }; struct mtk_dp_efuse_fmt { @@ -111,12 +123,22 @@ struct mtk_dp { struct phy *phy; struct regmap *regs; struct timer_list debounce_timer; + + /* For audio */ + bool audio_enable; + hdmi_codec_plugged_cb plugged_cb; + struct platform_device *audio_pdev; + + struct device *codec_dev; + /* protect the plugged_cb as it's used in both bridge ops and audio */ + struct mutex update_plugged_status_lock; }; struct mtk_dp_data { int bridge_type; unsigned int smc_cmd; const struct mtk_dp_efuse_fmt *efuse_fmt; + bool audio_supported; }; static const struct mtk_dp_efuse_fmt mt8195_edp_efuse_fmt[MTK_DP_CAL_MAX] = { @@ -512,6 +534,169 @@ static void mtk_dp_pg_enable(struct mtk_dp *mtk_dp, bool enable) PGEN_PATTERN_SEL_VAL << 4, PGEN_PATTERN_SEL_MASK); } +static void mtk_dp_audio_setup_channels(struct mtk_dp *mtk_dp, + struct mtk_dp_audio_cfg *cfg) +{ + u32 channel_enable_bits; + + mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3324, + AUDIO_SOURCE_MUX_DP_ENC1_P0_DPRX, + AUDIO_SOURCE_MUX_DP_ENC1_P0_MASK); + + /* audio channel count change reset */ + mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4, + DP_ENC_DUMMY_RW_1, DP_ENC_DUMMY_RW_1); + mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3304, + AU_PRTY_REGEN_DP_ENC1_P0_MASK | + AU_CH_STS_REGEN_DP_ENC1_P0_MASK | + AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK, + AU_PRTY_REGEN_DP_ENC1_P0_MASK | + AU_CH_STS_REGEN_DP_ENC1_P0_MASK | + AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK); + + switch (cfg->channels) { + case 2: + channel_enable_bits = AUDIO_2CH_SEL_DP_ENC0_P0_MASK | + AUDIO_2CH_EN_DP_ENC0_P0_MASK; + break; + case 8: + default: + channel_enable_bits = AUDIO_8CH_SEL_DP_ENC0_P0_MASK | + AUDIO_8CH_EN_DP_ENC0_P0_MASK; + break; + } + mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3088, + channel_enable_bits | AU_EN_DP_ENC0_P0, + AUDIO_2CH_SEL_DP_ENC0_P0_MASK | + AUDIO_2CH_EN_DP_ENC0_P0_MASK | + AUDIO_8CH_SEL_DP_ENC0_P0_MASK | + AUDIO_8CH_EN_DP_ENC0_P0_MASK | + AU_EN_DP_ENC0_P0); + + /* audio channel count change reset */ + mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4, 0, DP_ENC_DUMMY_RW_1); + + /* enable audio reset */ + mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4, + DP_ENC_DUMMY_RW_1_AUDIO_RST_EN, + DP_ENC_DUMMY_RW_1_AUDIO_RST_EN); +} + +static void mtk_dp_audio_channel_status_set(struct mtk_dp *mtk_dp, + struct mtk_dp_audio_cfg *cfg) +{ + struct snd_aes_iec958 iec = { 0 }; + + switch (cfg->sample_rate) { + case 32000: + iec.status[3] = IEC958_AES3_CON_FS_32000; + break; + case 44100: + iec.status[3] = IEC958_AES3_CON_FS_44100; + break; + case 48000: + iec.status[3] = IEC958_AES3_CON_FS_48000; + break; + case 88200: + iec.status[3] = IEC958_AES3_CON_FS_88200; + break; + case 96000: + iec.status[3] = IEC958_AES3_CON_FS_96000; + break; + case 192000: + iec.status[3] = IEC958_AES3_CON_FS_192000; + break; + default: + iec.status[3] = IEC958_AES3_CON_FS_NOTID; + break; + } + + switch (cfg->word_length_bits) { + case 16: + iec.status[4] = IEC958_AES4_CON_WORDLEN_20_16; + break; + case 20: + iec.status[4] = IEC958_AES4_CON_WORDLEN_20_16 | + IEC958_AES4_CON_MAX_WORDLEN_24; + break; + case 24: + iec.status[4] = IEC958_AES4_CON_WORDLEN_24_20 | + IEC958_AES4_CON_MAX_WORDLEN_24; + break; + default: + iec.status[4] = IEC958_AES4_CON_WORDLEN_NOTID; + } + + /* IEC 60958 consumer channel status bits */ + mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_308C, + 0, CH_STATUS_0_DP_ENC0_P0_MASK); + mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3090, + iec.status[3] << 8, CH_STATUS_1_DP_ENC0_P0_MASK); + mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3094, + iec.status[4], CH_STATUS_2_DP_ENC0_P0_MASK); +} + +static void mtk_dp_audio_sdp_asp_set_channels(struct mtk_dp *mtk_dp, + int channels) +{ + mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_312C, + (min(8, channels) - 1) << 8, + ASP_HB2_DP_ENC0_P0_MASK | ASP_HB3_DP_ENC0_P0_MASK); +} + +static void mtk_dp_audio_set_divider(struct mtk_dp *mtk_dp) +{ + mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30BC, + AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_2, + AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MASK); +} + +static void mtk_dp_sdp_trigger_aui(struct mtk_dp *mtk_dp) +{ + mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280, + MTK_DP_SDP_AUI, SDP_PACKET_TYPE_DP_ENC1_P0_MASK); + mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280, + SDP_PACKET_W_DP_ENC1_P0, SDP_PACKET_W_DP_ENC1_P0); +} + +static void mtk_dp_sdp_set_data(struct mtk_dp *mtk_dp, u8 *data_bytes) +{ + mtk_dp_bulk_16bit_write(mtk_dp, MTK_DP_ENC1_P0_3200, + data_bytes, 0x10); +} + +static void mtk_dp_sdp_set_header_aui(struct mtk_dp *mtk_dp, + struct dp_sdp_header *header) +{ + u32 db_addr = MTK_DP_ENC0_P0_30D8 + (MTK_DP_SDP_AUI - 1) * 8; + + mtk_dp_bulk_16bit_write(mtk_dp, db_addr, (u8 *)header, 4); +} + +static void mtk_dp_disable_sdp_aui(struct mtk_dp *mtk_dp) +{ + /* Disable periodic send */ + mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A8 & 0xfffc, 0, + 0xff << ((MTK_DP_ENC0_P0_30A8 & 3) * 8)); +} + +static void mtk_dp_setup_sdp_aui(struct mtk_dp *mtk_dp, + struct dp_sdp *sdp) +{ + u32 shift; + + mtk_dp_sdp_set_data(mtk_dp, sdp->db); + mtk_dp_sdp_set_header_aui(mtk_dp, &sdp->sdp_header); + mtk_dp_disable_sdp_aui(mtk_dp); + + shift = (MTK_DP_ENC0_P0_30A8 & 3) * 8; + + mtk_dp_sdp_trigger_aui(mtk_dp); + /* Enable periodic sending */ + mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A8 & 0xfffc, + 0x05 << shift, 0xff << shift); +} + static void mtk_dp_aux_irq_clear(struct mtk_dp *mtk_dp) { mtk_dp_write(mtk_dp, MTK_DP_AUX_P0_3640, DP_AUX_P0_3640_VAL); @@ -1041,6 +1226,32 @@ static void mtk_dp_video_mute(struct mtk_dp *mtk_dp, bool enable) mtk_dp->data->smc_cmd, enable, res.a0, res.a1); } +static void mtk_dp_audio_mute(struct mtk_dp *mtk_dp, bool mute) +{ + u32 val[3]; + + if (mute) { + val[0] = VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0 | + VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0; + val[1] = 0; + val[2] = 0; + } else { + val[0] = 0; + val[1] = AU_EN_DP_ENC0_P0; + /* Send one every two frames */ + val[2] = 0x0F; + } + + mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3030, + val[0], + VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0 | + VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0); + mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3088, + val[1], AU_EN_DP_ENC0_P0); + mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A4, + val[2], AU_TS_CFG_DP_ENC0_P0_MASK); +} + static void mtk_dp_power_enable(struct mtk_dp *mtk_dp) { mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE, @@ -1080,6 +1291,76 @@ static void mtk_dp_initialize_priv_data(struct mtk_dp *mtk_dp) mtk_dp->info.format = DP_PIXELFORMAT_RGB; memset(&mtk_dp->info.vm, 0, sizeof(struct videomode)); + mtk_dp->audio_enable = false; +} + +static void mtk_dp_sdp_set_down_cnt_init(struct mtk_dp *mtk_dp, + u32 sram_read_start) +{ + u32 sdp_down_cnt_init = 0; + struct drm_display_mode mode; + struct videomode *vm = &mtk_dp->info.vm; + + drm_display_mode_from_videomode(vm, &mode); + + if (mode.clock > 0) + sdp_down_cnt_init = sram_read_start * + mtk_dp->train_info.link_rate * 2700 * 8 / + (mode.clock * 4); + + switch (mtk_dp->train_info.lane_count) { + case 1: + sdp_down_cnt_init = max_t(u32, sdp_down_cnt_init, 0x1A); + break; + case 2: + /* case for LowResolution && High Audio Sample Rate */ + sdp_down_cnt_init = max_t(u32, sdp_down_cnt_init, 0x10); + sdp_down_cnt_init += mode.vtotal <= 525 ? 4 : 0; + break; + case 4: + default: + sdp_down_cnt_init = max_t(u32, sdp_down_cnt_init, 6); + break; + } + + mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3040, + sdp_down_cnt_init, + SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK); +} + +static void mtk_dp_sdp_set_down_cnt_init_in_hblank(struct mtk_dp *mtk_dp) +{ + int pix_clk_mhz; + u32 dc_offset; + u32 spd_down_cnt_init = 0; + struct drm_display_mode mode; + struct videomode *vm = &mtk_dp->info.vm; + + drm_display_mode_from_videomode(vm, &mode); + + pix_clk_mhz = mtk_dp->info.format == DP_PIXELFORMAT_YUV420 ? + mode.clock / 2000 : mode.clock / 1000; + + switch (mtk_dp->train_info.lane_count) { + case 1: + spd_down_cnt_init = 0x20; + break; + case 2: + dc_offset = (mode.vtotal <= 525) ? 0x14 : 0x00; + spd_down_cnt_init = 0x18 + dc_offset; + break; + case 4: + default: + dc_offset = (mode.vtotal <= 525) ? 0x08 : 0x00; + if (pix_clk_mhz > mtk_dp->train_info.link_rate * 27) + spd_down_cnt_init = 0x8; + else + spd_down_cnt_init = 0x10 + dc_offset; + break; + } + + mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364, spd_down_cnt_init, + SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK); } static void mtk_dp_setup_tu(struct mtk_dp *mtk_dp) @@ -1091,6 +1372,8 @@ static void mtk_dp_setup_tu(struct mtk_dp *mtk_dp) MTK_DP_PIX_PER_ADDR); mtk_dp_set_sram_read_start(mtk_dp, sram_read_start); mtk_dp_setup_encoder(mtk_dp); + mtk_dp_sdp_set_down_cnt_init_in_hblank(mtk_dp); + mtk_dp_sdp_set_down_cnt_init(mtk_dp, sram_read_start); } static void mtk_dp_set_tx_out(struct mtk_dp *mtk_dp) @@ -1342,6 +1625,20 @@ static int mtk_dp_parse_capabilities(struct mtk_dp *mtk_dp) return 0; } +static bool mtk_dp_edid_parse_audio_capabilities(struct mtk_dp *mtk_dp, + struct mtk_dp_audio_cfg *cfg) +{ + if (!mtk_dp->data->audio_supported) + return false; + + if (mtk_dp->info.audio_cur_cfg.sad_count <= 0) { + drm_info(mtk_dp->drm_dev, "The SADs is NULL\n"); + return false; + } + + return true; +} + static void mtk_dp_train_change_mode(struct mtk_dp *mtk_dp) { phy_reset(mtk_dp->phy); @@ -1446,6 +1743,46 @@ static void mtk_dp_video_enable(struct mtk_dp *mtk_dp, bool enable) } } +static void mtk_dp_audio_sdp_setup(struct mtk_dp *mtk_dp, + struct mtk_dp_audio_cfg *cfg) +{ + struct dp_sdp sdp; + struct hdmi_audio_infoframe frame; + + hdmi_audio_infoframe_init(&frame); + frame.coding_type = HDMI_AUDIO_CODING_TYPE_PCM; + frame.channels = cfg->channels; + frame.sample_frequency = cfg->sample_rate; + + switch (cfg->word_length_bits) { + case 16: + frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_16; + break; + case 20: + frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_20; + break; + case 24: + default: + frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_24; + break; + } + + hdmi_audio_infoframe_pack_for_dp(&frame, &sdp, MTK_DP_VERSION); + + mtk_dp_audio_sdp_asp_set_channels(mtk_dp, cfg->channels); + mtk_dp_setup_sdp_aui(mtk_dp, &sdp); +} + +static void mtk_dp_audio_setup(struct mtk_dp *mtk_dp, + struct mtk_dp_audio_cfg *cfg) +{ + mtk_dp_audio_sdp_setup(mtk_dp, cfg); + mtk_dp_audio_channel_status_set(mtk_dp, cfg); + + mtk_dp_audio_setup_channels(mtk_dp, cfg); + mtk_dp_audio_set_divider(mtk_dp); +} + static int mtk_dp_video_config(struct mtk_dp *mtk_dp) { mtk_dp_config_mn_mode(mtk_dp); @@ -1489,6 +1826,10 @@ static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev) drm_helper_hpd_irq_event(mtk_dp->bridge.dev); if (!mtk_dp->train_info.cable_plugged_in) { + mtk_dp_disable_sdp_aui(mtk_dp); + memset(&mtk_dp->info.audio_cur_cfg, 0, + sizeof(mtk_dp->info.audio_cur_cfg)); + mtk_dp->need_debounce = false; mod_timer(&mtk_dp->debounce_timer, jiffies + msecs_to_jiffies(100) - 1); @@ -1575,6 +1916,16 @@ static int mtk_dp_dt_parse(struct mtk_dp *mtk_dp, return 0; } +static void mtk_dp_update_plugged_status(struct mtk_dp *mtk_dp) +{ + mutex_lock(&mtk_dp->update_plugged_status_lock); + if (mtk_dp->plugged_cb && mtk_dp->codec_dev) + mtk_dp->plugged_cb(mtk_dp->codec_dev, + mtk_dp->enabled & + mtk_dp->info.audio_cur_cfg.detect_monitor); + mutex_unlock(&mtk_dp->update_plugged_status_lock); +} + static enum drm_connector_status mtk_dp_bdg_detect(struct drm_bridge *bridge) { struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge); @@ -1615,7 +1966,6 @@ static enum drm_connector_status mtk_dp_bdg_detect(struct drm_bridge *bridge) DP_PWR_STATE_MASK); } } - return ret; } @@ -1625,6 +1975,8 @@ static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge, struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge); bool enabled = mtk_dp->enabled; struct edid *new_edid = NULL; + struct mtk_dp_audio_cfg *audio_caps = &mtk_dp->info.audio_cur_cfg; + struct cea_sad *sads; if (!enabled) { drm_bridge_chain_pre_enable(bridge); @@ -1650,6 +2002,11 @@ static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge, new_edid = NULL; } + if (new_edid) { + audio_caps->sad_count = drm_edid_to_sad(new_edid, &sads); + audio_caps->detect_monitor = drm_detect_monitor_audio(new_edid); + } + if (!enabled) { /* power off panel */ drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D3); @@ -1843,7 +2200,19 @@ static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge, mtk_dp_video_enable(mtk_dp, true); + mtk_dp->audio_enable = + mtk_dp_edid_parse_audio_capabilities(mtk_dp, + &mtk_dp->info.audio_cur_cfg); + if (mtk_dp->audio_enable) { + mtk_dp_audio_setup(mtk_dp, &mtk_dp->info.audio_cur_cfg); + mtk_dp_audio_mute(mtk_dp, false); + } else { + memset(&mtk_dp->info.audio_cur_cfg, 0, + sizeof(mtk_dp->info.audio_cur_cfg)); + } + mtk_dp->enabled = true; + mtk_dp_update_plugged_status(mtk_dp); return; power_off_aux: @@ -1858,7 +2227,9 @@ static void mtk_dp_bridge_atomic_disable(struct drm_bridge *bridge, struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge); mtk_dp->enabled = false; + mtk_dp_update_plugged_status(mtk_dp); mtk_dp_video_enable(mtk_dp, false); + mtk_dp_audio_mute(mtk_dp, true); if (mtk_dp->train_info.cable_plugged_in) { drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D3); @@ -2015,6 +2386,100 @@ static void mtk_dp_debounce_timer(struct timer_list *t) mtk_dp->need_debounce = true; } +/* + * HDMI audio codec callbacks + */ +static int mtk_dp_audio_hw_params(struct device *dev, void *data, + struct hdmi_codec_daifmt *daifmt, + struct hdmi_codec_params *params) +{ + struct mtk_dp *mtk_dp = dev_get_drvdata(dev); + + if (!mtk_dp->enabled) { + dev_err(mtk_dp->dev, "%s, DP is not ready!\n", __func__); + return -ENODEV; + } + + mtk_dp->info.audio_cur_cfg.channels = params->cea.channels; + mtk_dp->info.audio_cur_cfg.sample_rate = params->sample_rate; + + mtk_dp_audio_setup(mtk_dp, &mtk_dp->info.audio_cur_cfg); + + return 0; +} + +static int mtk_dp_audio_startup(struct device *dev, void *data) +{ + struct mtk_dp *mtk_dp = dev_get_drvdata(dev); + + mtk_dp_audio_mute(mtk_dp, false); + + return 0; +} + +static void mtk_dp_audio_shutdown(struct device *dev, void *data) +{ + struct mtk_dp *mtk_dp = dev_get_drvdata(dev); + + mtk_dp_audio_mute(mtk_dp, true); +} + +static int mtk_dp_audio_get_eld(struct device *dev, void *data, uint8_t *buf, + size_t len) +{ + struct mtk_dp *mtk_dp = dev_get_drvdata(dev); + + if (mtk_dp->enabled) + memcpy(buf, mtk_dp->conn->eld, len); + else + memset(buf, 0, len); + + return 0; +} + +static int mtk_dp_audio_hook_plugged_cb(struct device *dev, void *data, + hdmi_codec_plugged_cb fn, + struct device *codec_dev) +{ + struct mtk_dp *mtk_dp = data; + + mutex_lock(&mtk_dp->update_plugged_status_lock); + mtk_dp->plugged_cb = fn; + mtk_dp->codec_dev = codec_dev; + mutex_unlock(&mtk_dp->update_plugged_status_lock); + + mtk_dp_update_plugged_status(mtk_dp); + + return 0; +} + +static const struct hdmi_codec_ops mtk_dp_audio_codec_ops = { + .hw_params = mtk_dp_audio_hw_params, + .audio_startup = mtk_dp_audio_startup, + .audio_shutdown = mtk_dp_audio_shutdown, + .get_eld = mtk_dp_audio_get_eld, + .hook_plugged_cb = mtk_dp_audio_hook_plugged_cb, + .no_capture_mute = 1, +}; + +static int mtk_dp_register_audio_driver(struct device *dev) +{ + struct mtk_dp *mtk_dp = dev_get_drvdata(dev); + struct hdmi_codec_pdata codec_data = { + .ops = &mtk_dp_audio_codec_ops, + .max_i2s_channels = 8, + .i2s = 1, + .data = mtk_dp, + }; + + mtk_dp->audio_pdev = platform_device_register_data(dev, + HDMI_CODEC_DRV_NAME, + PLATFORM_DEVID_AUTO, + &codec_data, + sizeof(codec_data)); + return PTR_ERR_OR_ZERO(mtk_dp->audio_pdev); +} + static int mtk_dp_probe(struct platform_device *pdev) { struct mtk_dp *mtk_dp; @@ -2059,8 +2524,19 @@ static int mtk_dp_probe(struct platform_device *pdev) return dev_err_probe(dev, ret, "failed to request mediatek dptx irq\n"); + mutex_init(&mtk_dp->update_plugged_status_lock); + platform_set_drvdata(pdev, mtk_dp); + if (mtk_dp->data->audio_supported) { + ret = mtk_dp_register_audio_driver(dev); + if (ret) { + dev_err(dev, "Failed to register audio driver: %d\n", + ret); + return ret; + } + } + mtk_dp->phy_dev = platform_device_register_data(dev, "mediatek-dp-phy", PLATFORM_DEVID_AUTO, &mtk_dp->regs, @@ -2106,6 +2582,8 @@ static int mtk_dp_remove(struct platform_device *pdev) del_timer_sync(&mtk_dp->debounce_timer); drm_bridge_remove(&mtk_dp->bridge); platform_device_unregister(mtk_dp->phy_dev); + if (mtk_dp->audio_pdev) + platform_device_unregister(mtk_dp->audio_pdev); return 0; } @@ -2141,12 +2619,14 @@ static const struct mtk_dp_data mt8195_edp_data = { .bridge_type = DRM_MODE_CONNECTOR_eDP, .smc_cmd = MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, .efuse_fmt = mt8195_edp_efuse_fmt, + .audio_supported = false, }; static const struct mtk_dp_data mt8195_dp_data = { .bridge_type = DRM_MODE_CONNECTOR_DisplayPort, .smc_cmd = MTK_DP_SIP_ATF_VIDEO_UNMUTE, .efuse_fmt = mt8195_dp_efuse_fmt, + .audio_supported = true, }; static const struct of_device_id mtk_dp_of_match[] = { diff --git a/drivers/gpu/drm/mediatek/mtk_dp_reg.h b/drivers/gpu/drm/mediatek/mtk_dp_reg.h index 3f01ba44871f..096ad6572a5e 100644 --- a/drivers/gpu/drm/mediatek/mtk_dp_reg.h +++ b/drivers/gpu/drm/mediatek/mtk_dp_reg.h @@ -115,6 +115,8 @@ #define HSW_SEL_DP_ENC0_P0 BIT(7) #define VSP_SEL_DP_ENC0_P0 BIT(8) #define VSW_SEL_DP_ENC0_P0 BIT(9) +#define VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0 BIT(11) +#define VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0 BIT(12) #define MTK_DP_ENC0_P0_3034 0x3034 #define MTK_DP_ENC0_P0_3038 0x3038 #define VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK BIT(11) @@ -139,6 +141,38 @@ #define SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK BIT(8) #define MTK_DP_ENC0_P0_3064 0x3064 #define HDE_NUM_LAST_DP_ENC0_P0_MASK GENMASK(15, 0) +#define MTK_DP_ENC0_P0_3088 0x3088 +#define AU_EN_DP_ENC0_P0 BIT(6) +#define AUDIO_8CH_EN_DP_ENC0_P0_MASK BIT(7) +#define AUDIO_8CH_SEL_DP_ENC0_P0_MASK BIT(8) +#define AUDIO_2CH_EN_DP_ENC0_P0_MASK BIT(14) +#define AUDIO_2CH_SEL_DP_ENC0_P0_MASK BIT(15) +#define MTK_DP_ENC0_P0_308C 0x308c +#define CH_STATUS_0_DP_ENC0_P0_MASK GENMASK(15, 0) +#define MTK_DP_ENC0_P0_3090 0x3090 +#define CH_STATUS_1_DP_ENC0_P0_MASK GENMASK(15, 0) +#define MTK_DP_ENC0_P0_3094 0x3094 +#define CH_STATUS_2_DP_ENC0_P0_MASK GENMASK(7, 0) +#define MTK_DP_ENC0_P0_30A0 0x30a0 +#define DP_ENC0_30A0_MASK (BIT(7) | BIT(8) | BIT(12)) +#define MTK_DP_ENC0_P0_30A4 0x30a4 +#define AU_TS_CFG_DP_ENC0_P0_MASK GENMASK(7, 0) +#define MTK_DP_ENC0_P0_30A8 0x30a8 +#define MTK_DP_ENC0_P0_30BC 0x30bc +#define ISRC_CONT_DP_ENC0_P0 BIT(0) +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MASK GENMASK(10, 8) +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_2 (1 << 8) +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_4 (2 << 8) +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_8 (3 << 8) +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_2 (5 << 8) +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_4 (6 << 8) +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_8 (7 << 8) +#define MTK_DP_ENC0_P0_30D8 0x30d8 +#define MTK_DP_ENC0_P0_312C 0x312c +#define ASP_HB2_DP_ENC0_P0_MASK GENMASK(7, 0) +#define ASP_HB3_DP_ENC0_P0_MASK GENMASK(15, 8) +#define MTK_DP_ENC0_P0_3130 0x3130 +#define MTK_DP_ENC0_P0_3138 0x3138 #define MTK_DP_ENC0_P0_3154 0x3154 #define PGEN_HTOTAL_DP_ENC0_P0_MASK GENMASK(13, 0) #define MTK_DP_ENC0_P0_3158 0x3158 @@ -167,9 +201,23 @@ #define ISRC1_HB3_DP_ENC0_P0_MASK GENMASK(15, 8) /* offset: ENC1_OFFSET (0x3200) */ +#define MTK_DP_ENC1_P0_3200 0x3200 +#define MTK_DP_ENC1_P0_3280 0x3280 +#define SDP_PACKET_TYPE_DP_ENC1_P0_MASK GENMASK(4, 0) +#define SDP_PACKET_W_DP_ENC1_P0 BIT(5) +#define SDP_PACKET_W_DP_ENC1_P0_MASK BIT(5) +#define MTK_DP_ENC1_P0_328C 0x328c +#define VSC_DATA_RDY_VESA_DP_ENC1_P0_MASK BIT(7) #define MTK_DP_ENC1_P0_3300 0x3300 #define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_VAL 2 #define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK GENMASK(9, 8) +#define MTK_DP_ENC1_P0_3304 0x3304 +#define AU_PRTY_REGEN_DP_ENC1_P0_MASK BIT(8) +#define AU_CH_STS_REGEN_DP_ENC1_P0_MASK BIT(9) +#define AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK BIT(12) +#define MTK_DP_ENC1_P0_3324 0x3324 +#define AUDIO_SOURCE_MUX_DP_ENC1_P0_MASK GENMASK(9, 8) +#define AUDIO_SOURCE_MUX_DP_ENC1_P0_DPRX 0 #define MTK_DP_ENC1_P0_3364 0x3364 #define SDP_DOWN_CNT_IN_HBLANK_DP_ENC1_P0_VAL 0x20 #define SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK GENMASK(11, 0) @@ -186,6 +234,9 @@ VIDEO_STABLE_CNT_THRD_DP_ENC1_P0 | \ SDP_DP13_EN_DP_ENC1_P0 | \ BS2BS_MODE_DP_ENC1_P0) +#define MTK_DP_ENC1_P0_33F4 0x33f4 +#define DP_ENC_DUMMY_RW_1_AUDIO_RST_EN BIT(0) +#define DP_ENC_DUMMY_RW_1 BIT(9) /* offset: TRANS_OFFSET (0x3400) */ #define MTK_DP_TRANS_P0_3400 0x3400 |