11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 28f83f268SJie Qiu /* 38f83f268SJie Qiu * Copyright (c) 2014 MediaTek Inc. 48f83f268SJie Qiu * Author: Jie Qiu <jie.qiu@mediatek.com> 58f83f268SJie Qiu */ 69aef5867SSam Ravnborg 756ba355dSJie Qiu #include <linux/arm-smccc.h> 88f83f268SJie Qiu #include <linux/clk.h> 98f83f268SJie Qiu #include <linux/delay.h> 108f83f268SJie Qiu #include <linux/hdmi.h> 118f83f268SJie Qiu #include <linux/i2c.h> 128f83f268SJie Qiu #include <linux/io.h> 138f83f268SJie Qiu #include <linux/kernel.h> 148f83f268SJie Qiu #include <linux/mfd/syscon.h> 15a481bf2fSCK Hu #include <linux/module.h> 16f07980d4STzung-Bi Shih #include <linux/mutex.h> 178f83f268SJie Qiu #include <linux/of_platform.h> 188f83f268SJie Qiu #include <linux/of.h> 198f83f268SJie Qiu #include <linux/of_gpio.h> 208f83f268SJie Qiu #include <linux/of_graph.h> 218f83f268SJie Qiu #include <linux/phy/phy.h> 228f83f268SJie Qiu #include <linux/platform_device.h> 238f83f268SJie Qiu #include <linux/regmap.h> 249aef5867SSam Ravnborg 258f83f268SJie Qiu #include <sound/hdmi-codec.h> 269aef5867SSam Ravnborg 279aef5867SSam Ravnborg #include <drm/drm_atomic_helper.h> 28ee68c743SBoris Brezillon #include <drm/drm_bridge.h> 299aef5867SSam Ravnborg #include <drm/drm_crtc.h> 309aef5867SSam Ravnborg #include <drm/drm_edid.h> 319aef5867SSam Ravnborg #include <drm/drm_print.h> 329aef5867SSam Ravnborg #include <drm/drm_probe_helper.h> 339aef5867SSam Ravnborg 348f83f268SJie Qiu #include "mtk_cec.h" 358f83f268SJie Qiu #include "mtk_hdmi.h" 368f83f268SJie Qiu #include "mtk_hdmi_regs.h" 378f83f268SJie Qiu 388f83f268SJie Qiu #define NCTS_BYTES 7 398f83f268SJie Qiu 408f83f268SJie Qiu enum mtk_hdmi_clk_id { 418f83f268SJie Qiu MTK_HDMI_CLK_HDMI_PIXEL, 428f83f268SJie Qiu MTK_HDMI_CLK_HDMI_PLL, 438f83f268SJie Qiu MTK_HDMI_CLK_AUD_BCLK, 448f83f268SJie Qiu MTK_HDMI_CLK_AUD_SPDIF, 458f83f268SJie Qiu MTK_HDMI_CLK_COUNT 468f83f268SJie Qiu }; 478f83f268SJie Qiu 488f83f268SJie Qiu enum hdmi_aud_input_type { 498f83f268SJie Qiu HDMI_AUD_INPUT_I2S = 0, 508f83f268SJie Qiu HDMI_AUD_INPUT_SPDIF, 518f83f268SJie Qiu }; 528f83f268SJie Qiu 538f83f268SJie Qiu enum hdmi_aud_i2s_fmt { 548f83f268SJie Qiu HDMI_I2S_MODE_RJT_24BIT = 0, 558f83f268SJie Qiu HDMI_I2S_MODE_RJT_16BIT, 568f83f268SJie Qiu HDMI_I2S_MODE_LJT_24BIT, 578f83f268SJie Qiu HDMI_I2S_MODE_LJT_16BIT, 588f83f268SJie Qiu HDMI_I2S_MODE_I2S_24BIT, 598f83f268SJie Qiu HDMI_I2S_MODE_I2S_16BIT 608f83f268SJie Qiu }; 618f83f268SJie Qiu 628f83f268SJie Qiu enum hdmi_aud_mclk { 638f83f268SJie Qiu HDMI_AUD_MCLK_128FS, 648f83f268SJie Qiu HDMI_AUD_MCLK_192FS, 658f83f268SJie Qiu HDMI_AUD_MCLK_256FS, 668f83f268SJie Qiu HDMI_AUD_MCLK_384FS, 678f83f268SJie Qiu HDMI_AUD_MCLK_512FS, 688f83f268SJie Qiu HDMI_AUD_MCLK_768FS, 698f83f268SJie Qiu HDMI_AUD_MCLK_1152FS, 708f83f268SJie Qiu }; 718f83f268SJie Qiu 728f83f268SJie Qiu enum hdmi_aud_channel_type { 738f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_1_0 = 0, 748f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_1_1, 758f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_2_0, 768f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_2_1, 778f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_3_0, 788f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_3_1, 798f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_4_0, 808f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_4_1, 818f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_5_0, 828f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_5_1, 838f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_6_0, 848f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_6_1, 858f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_0, 868f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_1, 878f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_3_0_LRS, 888f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_3_1_LRS, 898f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_4_0_CLRS, 908f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_4_1_CLRS, 918f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_6_1_CS, 928f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_6_1_CH, 938f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_6_1_OH, 948f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_6_1_CHR, 958f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_1_LH_RH, 968f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_1_LSR_RSR, 978f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_1_LC_RC, 988f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_1_LW_RW, 998f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_1_LSD_RSD, 1008f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_1_LSS_RSS, 1018f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_1_LHS_RHS, 1028f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_1_CS_CH, 1038f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_1_CS_OH, 1048f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_1_CS_CHR, 1058f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_1_CH_OH, 1068f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_1_CH_CHR, 1078f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_1_OH_CHR, 1088f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_1_LSS_RSS_LSR_RSR, 1098f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_6_0_CS, 1108f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_6_0_CH, 1118f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_6_0_OH, 1128f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_6_0_CHR, 1138f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_0_LH_RH, 1148f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_0_LSR_RSR, 1158f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_0_LC_RC, 1168f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_0_LW_RW, 1178f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_0_LSD_RSD, 1188f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_0_LSS_RSS, 1198f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_0_LHS_RHS, 1208f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_0_CS_CH, 1218f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_0_CS_OH, 1228f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_0_CS_CHR, 1238f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_0_CH_OH, 1248f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_0_CH_CHR, 1258f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_0_OH_CHR, 1268f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_0_LSS_RSS_LSR_RSR, 1278f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_8_0_LH_RH_CS, 1288f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_UNKNOWN = 0xFF 1298f83f268SJie Qiu }; 1308f83f268SJie Qiu 1318f83f268SJie Qiu enum hdmi_aud_channel_swap_type { 1328f83f268SJie Qiu HDMI_AUD_SWAP_LR, 1338f83f268SJie Qiu HDMI_AUD_SWAP_LFE_CC, 1348f83f268SJie Qiu HDMI_AUD_SWAP_LSRS, 1358f83f268SJie Qiu HDMI_AUD_SWAP_RLS_RRS, 1368f83f268SJie Qiu HDMI_AUD_SWAP_LR_STATUS, 1378f83f268SJie Qiu }; 1388f83f268SJie Qiu 1398f83f268SJie Qiu struct hdmi_audio_param { 1408f83f268SJie Qiu enum hdmi_audio_coding_type aud_codec; 1418f83f268SJie Qiu enum hdmi_audio_sample_size aud_sampe_size; 1428f83f268SJie Qiu enum hdmi_aud_input_type aud_input_type; 1438f83f268SJie Qiu enum hdmi_aud_i2s_fmt aud_i2s_fmt; 1448f83f268SJie Qiu enum hdmi_aud_mclk aud_mclk; 1458f83f268SJie Qiu enum hdmi_aud_channel_type aud_input_chan_type; 1468f83f268SJie Qiu struct hdmi_codec_params codec_params; 1478f83f268SJie Qiu }; 1488f83f268SJie Qiu 14996f51a4dSCK Hu struct mtk_hdmi_conf { 15096f51a4dSCK Hu bool tz_disabled; 15141ca9caaSNeil Armstrong bool cea_modes_only; 152c91026a9SNeil Armstrong unsigned long max_mode_clock; 15396f51a4dSCK Hu }; 15496f51a4dSCK Hu 1558f83f268SJie Qiu struct mtk_hdmi { 1568f83f268SJie Qiu struct drm_bridge bridge; 1573bb80f24SLaurent Pinchart struct drm_bridge *next_bridge; 1582e477391SDafna Hirschfeld struct drm_connector *curr_conn;/* current connector (only valid when 'enabled') */ 1598f83f268SJie Qiu struct device *dev; 16096f51a4dSCK Hu const struct mtk_hdmi_conf *conf; 1618f83f268SJie Qiu struct phy *phy; 1628f83f268SJie Qiu struct device *cec_dev; 1638f83f268SJie Qiu struct i2c_adapter *ddc_adpt; 1648f83f268SJie Qiu struct clk *clk[MTK_HDMI_CLK_COUNT]; 1658f83f268SJie Qiu struct drm_display_mode mode; 1668f83f268SJie Qiu bool dvi_mode; 1678f83f268SJie Qiu u32 min_clock; 1688f83f268SJie Qiu u32 max_clock; 1698f83f268SJie Qiu u32 max_hdisplay; 1708f83f268SJie Qiu u32 max_vdisplay; 1718f83f268SJie Qiu u32 ibias; 1728f83f268SJie Qiu u32 ibias_up; 1738f83f268SJie Qiu struct regmap *sys_regmap; 1748f83f268SJie Qiu unsigned int sys_offset; 1758f83f268SJie Qiu void __iomem *regs; 1768f83f268SJie Qiu enum hdmi_colorspace csp; 1778f83f268SJie Qiu struct hdmi_audio_param aud_param; 1788f83f268SJie Qiu bool audio_enable; 1798f83f268SJie Qiu bool powered; 1808f83f268SJie Qiu bool enabled; 1815d3c6447STzung-Bi Shih hdmi_codec_plugged_cb plugged_cb; 1825d3c6447STzung-Bi Shih struct device *codec_dev; 183f07980d4STzung-Bi Shih struct mutex update_plugged_status_lock; 1848f83f268SJie Qiu }; 1858f83f268SJie Qiu 1868f83f268SJie Qiu static inline struct mtk_hdmi *hdmi_ctx_from_bridge(struct drm_bridge *b) 1878f83f268SJie Qiu { 1888f83f268SJie Qiu return container_of(b, struct mtk_hdmi, bridge); 1898f83f268SJie Qiu } 1908f83f268SJie Qiu 1918f83f268SJie Qiu static u32 mtk_hdmi_read(struct mtk_hdmi *hdmi, u32 offset) 1928f83f268SJie Qiu { 1938f83f268SJie Qiu return readl(hdmi->regs + offset); 1948f83f268SJie Qiu } 1958f83f268SJie Qiu 1968f83f268SJie Qiu static void mtk_hdmi_write(struct mtk_hdmi *hdmi, u32 offset, u32 val) 1978f83f268SJie Qiu { 1988f83f268SJie Qiu writel(val, hdmi->regs + offset); 1998f83f268SJie Qiu } 2008f83f268SJie Qiu 2018f83f268SJie Qiu static void mtk_hdmi_clear_bits(struct mtk_hdmi *hdmi, u32 offset, u32 bits) 2028f83f268SJie Qiu { 2038f83f268SJie Qiu void __iomem *reg = hdmi->regs + offset; 2048f83f268SJie Qiu u32 tmp; 2058f83f268SJie Qiu 2068f83f268SJie Qiu tmp = readl(reg); 2078f83f268SJie Qiu tmp &= ~bits; 2088f83f268SJie Qiu writel(tmp, reg); 2098f83f268SJie Qiu } 2108f83f268SJie Qiu 2118f83f268SJie Qiu static void mtk_hdmi_set_bits(struct mtk_hdmi *hdmi, u32 offset, u32 bits) 2128f83f268SJie Qiu { 2138f83f268SJie Qiu void __iomem *reg = hdmi->regs + offset; 2148f83f268SJie Qiu u32 tmp; 2158f83f268SJie Qiu 2168f83f268SJie Qiu tmp = readl(reg); 2178f83f268SJie Qiu tmp |= bits; 2188f83f268SJie Qiu writel(tmp, reg); 2198f83f268SJie Qiu } 2208f83f268SJie Qiu 2218f83f268SJie Qiu static void mtk_hdmi_mask(struct mtk_hdmi *hdmi, u32 offset, u32 val, u32 mask) 2228f83f268SJie Qiu { 2238f83f268SJie Qiu void __iomem *reg = hdmi->regs + offset; 2248f83f268SJie Qiu u32 tmp; 2258f83f268SJie Qiu 2268f83f268SJie Qiu tmp = readl(reg); 2278f83f268SJie Qiu tmp = (tmp & ~mask) | (val & mask); 2288f83f268SJie Qiu writel(tmp, reg); 2298f83f268SJie Qiu } 2308f83f268SJie Qiu 2318f83f268SJie Qiu static void mtk_hdmi_hw_vid_black(struct mtk_hdmi *hdmi, bool black) 2328f83f268SJie Qiu { 2338f83f268SJie Qiu mtk_hdmi_mask(hdmi, VIDEO_CFG_4, black ? GEN_RGB : NORMAL_PATH, 2348f83f268SJie Qiu VIDEO_SOURCE_SEL); 2358f83f268SJie Qiu } 2368f83f268SJie Qiu 2378f83f268SJie Qiu static void mtk_hdmi_hw_make_reg_writable(struct mtk_hdmi *hdmi, bool enable) 2388f83f268SJie Qiu { 23956ba355dSJie Qiu struct arm_smccc_res res; 24056ba355dSJie Qiu 24156ba355dSJie Qiu /* 24256ba355dSJie Qiu * MT8173 HDMI hardware has an output control bit to enable/disable HDMI 24356ba355dSJie Qiu * output. This bit can only be controlled in ARM supervisor mode. 24456ba355dSJie Qiu * The ARM trusted firmware provides an API for the HDMI driver to set 24556ba355dSJie Qiu * this control bit to enable HDMI output in supervisor mode. 24656ba355dSJie Qiu */ 24796f51a4dSCK Hu if (hdmi->conf && hdmi->conf->tz_disabled) 2480fc721b2Schunhui dai regmap_update_bits(hdmi->sys_regmap, 2490fc721b2Schunhui dai hdmi->sys_offset + HDMI_SYS_CFG20, 2500fc721b2Schunhui dai 0x80008005, enable ? 0x80000005 : 0x8000); 2510fc721b2Schunhui dai else 2520fc721b2Schunhui dai arm_smccc_smc(MTK_SIP_SET_AUTHORIZED_SECURE_REG, 0x14000904, 2530fc721b2Schunhui dai 0x80000000, 0, 0, 0, 0, 0, &res); 25456ba355dSJie Qiu 2558f83f268SJie Qiu regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG20, 2568f83f268SJie Qiu HDMI_PCLK_FREE_RUN, enable ? HDMI_PCLK_FREE_RUN : 0); 2578f83f268SJie Qiu regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG1C, 2588f83f268SJie Qiu HDMI_ON | ANLG_ON, enable ? (HDMI_ON | ANLG_ON) : 0); 2598f83f268SJie Qiu } 2608f83f268SJie Qiu 2618f83f268SJie Qiu static void mtk_hdmi_hw_1p4_version_enable(struct mtk_hdmi *hdmi, bool enable) 2628f83f268SJie Qiu { 2638f83f268SJie Qiu regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG20, 2648f83f268SJie Qiu HDMI2P0_EN, enable ? 0 : HDMI2P0_EN); 2658f83f268SJie Qiu } 2668f83f268SJie Qiu 2678f83f268SJie Qiu static void mtk_hdmi_hw_aud_mute(struct mtk_hdmi *hdmi) 2688f83f268SJie Qiu { 2698f83f268SJie Qiu mtk_hdmi_set_bits(hdmi, GRL_AUDIO_CFG, AUDIO_ZERO); 2708f83f268SJie Qiu } 2718f83f268SJie Qiu 2728f83f268SJie Qiu static void mtk_hdmi_hw_aud_unmute(struct mtk_hdmi *hdmi) 2738f83f268SJie Qiu { 2748f83f268SJie Qiu mtk_hdmi_clear_bits(hdmi, GRL_AUDIO_CFG, AUDIO_ZERO); 2758f83f268SJie Qiu } 2768f83f268SJie Qiu 2778f83f268SJie Qiu static void mtk_hdmi_hw_reset(struct mtk_hdmi *hdmi) 2788f83f268SJie Qiu { 2798f83f268SJie Qiu regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG1C, 2808f83f268SJie Qiu HDMI_RST, HDMI_RST); 2818f83f268SJie Qiu regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG1C, 2828f83f268SJie Qiu HDMI_RST, 0); 2838f83f268SJie Qiu mtk_hdmi_clear_bits(hdmi, GRL_CFG3, CFG3_CONTROL_PACKET_DELAY); 2848f83f268SJie Qiu regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG1C, 2858f83f268SJie Qiu ANLG_ON, ANLG_ON); 2868f83f268SJie Qiu } 2878f83f268SJie Qiu 2888f83f268SJie Qiu static void mtk_hdmi_hw_enable_notice(struct mtk_hdmi *hdmi, bool enable_notice) 2898f83f268SJie Qiu { 2908f83f268SJie Qiu mtk_hdmi_mask(hdmi, GRL_CFG2, enable_notice ? CFG2_NOTICE_EN : 0, 2918f83f268SJie Qiu CFG2_NOTICE_EN); 2928f83f268SJie Qiu } 2938f83f268SJie Qiu 2948f83f268SJie Qiu static void mtk_hdmi_hw_write_int_mask(struct mtk_hdmi *hdmi, u32 int_mask) 2958f83f268SJie Qiu { 2968f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_INT_MASK, int_mask); 2978f83f268SJie Qiu } 2988f83f268SJie Qiu 2998f83f268SJie Qiu static void mtk_hdmi_hw_enable_dvi_mode(struct mtk_hdmi *hdmi, bool enable) 3008f83f268SJie Qiu { 3018f83f268SJie Qiu mtk_hdmi_mask(hdmi, GRL_CFG1, enable ? CFG1_DVI : 0, CFG1_DVI); 3028f83f268SJie Qiu } 3038f83f268SJie Qiu 3048f83f268SJie Qiu static void mtk_hdmi_hw_send_info_frame(struct mtk_hdmi *hdmi, u8 *buffer, 3058f83f268SJie Qiu u8 len) 3068f83f268SJie Qiu { 3078f83f268SJie Qiu u32 ctrl_reg = GRL_CTRL; 3088f83f268SJie Qiu int i; 3098f83f268SJie Qiu u8 *frame_data; 3108f83f268SJie Qiu enum hdmi_infoframe_type frame_type; 3118f83f268SJie Qiu u8 frame_ver; 3128f83f268SJie Qiu u8 frame_len; 3138f83f268SJie Qiu u8 checksum; 3148f83f268SJie Qiu int ctrl_frame_en = 0; 3158f83f268SJie Qiu 3163cda3d31SBernard Zhao frame_type = *buffer++; 3173cda3d31SBernard Zhao frame_ver = *buffer++; 3183cda3d31SBernard Zhao frame_len = *buffer++; 3193cda3d31SBernard Zhao checksum = *buffer++; 3208f83f268SJie Qiu frame_data = buffer; 3218f83f268SJie Qiu 3228f83f268SJie Qiu dev_dbg(hdmi->dev, 3238f83f268SJie Qiu "frame_type:0x%x,frame_ver:0x%x,frame_len:0x%x,checksum:0x%x\n", 3248f83f268SJie Qiu frame_type, frame_ver, frame_len, checksum); 3258f83f268SJie Qiu 3268f83f268SJie Qiu switch (frame_type) { 3278f83f268SJie Qiu case HDMI_INFOFRAME_TYPE_AVI: 3288f83f268SJie Qiu ctrl_frame_en = CTRL_AVI_EN; 3298f83f268SJie Qiu ctrl_reg = GRL_CTRL; 3308f83f268SJie Qiu break; 3318f83f268SJie Qiu case HDMI_INFOFRAME_TYPE_SPD: 3328f83f268SJie Qiu ctrl_frame_en = CTRL_SPD_EN; 3338f83f268SJie Qiu ctrl_reg = GRL_CTRL; 3348f83f268SJie Qiu break; 3358f83f268SJie Qiu case HDMI_INFOFRAME_TYPE_AUDIO: 3368f83f268SJie Qiu ctrl_frame_en = CTRL_AUDIO_EN; 3378f83f268SJie Qiu ctrl_reg = GRL_CTRL; 3388f83f268SJie Qiu break; 3398f83f268SJie Qiu case HDMI_INFOFRAME_TYPE_VENDOR: 3408f83f268SJie Qiu ctrl_frame_en = VS_EN; 3418f83f268SJie Qiu ctrl_reg = GRL_ACP_ISRC_CTRL; 3428f83f268SJie Qiu break; 3431f1635b1SSean Paul default: 3441f1635b1SSean Paul dev_err(hdmi->dev, "Unknown infoframe type %d\n", frame_type); 3451f1635b1SSean Paul return; 3468f83f268SJie Qiu } 3478f83f268SJie Qiu mtk_hdmi_clear_bits(hdmi, ctrl_reg, ctrl_frame_en); 3488f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_INFOFRM_TYPE, frame_type); 3498f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_INFOFRM_VER, frame_ver); 3508f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_INFOFRM_LNG, frame_len); 3518f83f268SJie Qiu 3528f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_IFM_PORT, checksum); 3538f83f268SJie Qiu for (i = 0; i < frame_len; i++) 3548f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_IFM_PORT, frame_data[i]); 3558f83f268SJie Qiu 3568f83f268SJie Qiu mtk_hdmi_set_bits(hdmi, ctrl_reg, ctrl_frame_en); 3578f83f268SJie Qiu } 3588f83f268SJie Qiu 3598f83f268SJie Qiu static void mtk_hdmi_hw_send_aud_packet(struct mtk_hdmi *hdmi, bool enable) 3608f83f268SJie Qiu { 3618f83f268SJie Qiu mtk_hdmi_mask(hdmi, GRL_SHIFT_R2, enable ? 0 : AUDIO_PACKET_OFF, 3628f83f268SJie Qiu AUDIO_PACKET_OFF); 3638f83f268SJie Qiu } 3648f83f268SJie Qiu 3658f83f268SJie Qiu static void mtk_hdmi_hw_config_sys(struct mtk_hdmi *hdmi) 3668f83f268SJie Qiu { 3678f83f268SJie Qiu regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG20, 3688f83f268SJie Qiu HDMI_OUT_FIFO_EN | MHL_MODE_ON, 0); 3698f83f268SJie Qiu usleep_range(2000, 4000); 3708f83f268SJie Qiu regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG20, 3718f83f268SJie Qiu HDMI_OUT_FIFO_EN | MHL_MODE_ON, HDMI_OUT_FIFO_EN); 3728f83f268SJie Qiu } 3738f83f268SJie Qiu 3748f83f268SJie Qiu static void mtk_hdmi_hw_set_deep_color_mode(struct mtk_hdmi *hdmi) 3758f83f268SJie Qiu { 3768f83f268SJie Qiu regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG20, 3778f83f268SJie Qiu DEEP_COLOR_MODE_MASK | DEEP_COLOR_EN, 3788f83f268SJie Qiu COLOR_8BIT_MODE); 3798f83f268SJie Qiu } 3808f83f268SJie Qiu 3818f83f268SJie Qiu static void mtk_hdmi_hw_send_av_mute(struct mtk_hdmi *hdmi) 3828f83f268SJie Qiu { 3838f83f268SJie Qiu mtk_hdmi_clear_bits(hdmi, GRL_CFG4, CTRL_AVMUTE); 3848f83f268SJie Qiu usleep_range(2000, 4000); 3858f83f268SJie Qiu mtk_hdmi_set_bits(hdmi, GRL_CFG4, CTRL_AVMUTE); 3868f83f268SJie Qiu } 3878f83f268SJie Qiu 3888f83f268SJie Qiu static void mtk_hdmi_hw_send_av_unmute(struct mtk_hdmi *hdmi) 3898f83f268SJie Qiu { 3908f83f268SJie Qiu mtk_hdmi_mask(hdmi, GRL_CFG4, CFG4_AV_UNMUTE_EN, 3918f83f268SJie Qiu CFG4_AV_UNMUTE_EN | CFG4_AV_UNMUTE_SET); 3928f83f268SJie Qiu usleep_range(2000, 4000); 3938f83f268SJie Qiu mtk_hdmi_mask(hdmi, GRL_CFG4, CFG4_AV_UNMUTE_SET, 3948f83f268SJie Qiu CFG4_AV_UNMUTE_EN | CFG4_AV_UNMUTE_SET); 3958f83f268SJie Qiu } 3968f83f268SJie Qiu 3978f83f268SJie Qiu static void mtk_hdmi_hw_ncts_enable(struct mtk_hdmi *hdmi, bool on) 3988f83f268SJie Qiu { 3998f83f268SJie Qiu mtk_hdmi_mask(hdmi, GRL_CTS_CTRL, on ? 0 : CTS_CTRL_SOFT, 4008f83f268SJie Qiu CTS_CTRL_SOFT); 4018f83f268SJie Qiu } 4028f83f268SJie Qiu 4038f83f268SJie Qiu static void mtk_hdmi_hw_ncts_auto_write_enable(struct mtk_hdmi *hdmi, 4048f83f268SJie Qiu bool enable) 4058f83f268SJie Qiu { 4068f83f268SJie Qiu mtk_hdmi_mask(hdmi, GRL_CTS_CTRL, enable ? NCTS_WRI_ANYTIME : 0, 4078f83f268SJie Qiu NCTS_WRI_ANYTIME); 4088f83f268SJie Qiu } 4098f83f268SJie Qiu 4108f83f268SJie Qiu static void mtk_hdmi_hw_msic_setting(struct mtk_hdmi *hdmi, 4118f83f268SJie Qiu struct drm_display_mode *mode) 4128f83f268SJie Qiu { 4138f83f268SJie Qiu mtk_hdmi_clear_bits(hdmi, GRL_CFG4, CFG4_MHL_MODE); 4148f83f268SJie Qiu 4158f83f268SJie Qiu if (mode->flags & DRM_MODE_FLAG_INTERLACE && 4168f83f268SJie Qiu mode->clock == 74250 && 4178f83f268SJie Qiu mode->vdisplay == 1080) 4188f83f268SJie Qiu mtk_hdmi_clear_bits(hdmi, GRL_CFG2, CFG2_MHL_DE_SEL); 4198f83f268SJie Qiu else 4208f83f268SJie Qiu mtk_hdmi_set_bits(hdmi, GRL_CFG2, CFG2_MHL_DE_SEL); 4218f83f268SJie Qiu } 4228f83f268SJie Qiu 4238f83f268SJie Qiu static void mtk_hdmi_hw_aud_set_channel_swap(struct mtk_hdmi *hdmi, 4248f83f268SJie Qiu enum hdmi_aud_channel_swap_type swap) 4258f83f268SJie Qiu { 4268f83f268SJie Qiu u8 swap_bit; 4278f83f268SJie Qiu 4288f83f268SJie Qiu switch (swap) { 4298f83f268SJie Qiu case HDMI_AUD_SWAP_LR: 4308f83f268SJie Qiu swap_bit = LR_SWAP; 4318f83f268SJie Qiu break; 4328f83f268SJie Qiu case HDMI_AUD_SWAP_LFE_CC: 4338f83f268SJie Qiu swap_bit = LFE_CC_SWAP; 4348f83f268SJie Qiu break; 4358f83f268SJie Qiu case HDMI_AUD_SWAP_LSRS: 4368f83f268SJie Qiu swap_bit = LSRS_SWAP; 4378f83f268SJie Qiu break; 4388f83f268SJie Qiu case HDMI_AUD_SWAP_RLS_RRS: 4398f83f268SJie Qiu swap_bit = RLS_RRS_SWAP; 4408f83f268SJie Qiu break; 4418f83f268SJie Qiu case HDMI_AUD_SWAP_LR_STATUS: 4428f83f268SJie Qiu swap_bit = LR_STATUS_SWAP; 4438f83f268SJie Qiu break; 4448f83f268SJie Qiu default: 4458f83f268SJie Qiu swap_bit = LFE_CC_SWAP; 4468f83f268SJie Qiu break; 4478f83f268SJie Qiu } 4488f83f268SJie Qiu mtk_hdmi_mask(hdmi, GRL_CH_SWAP, swap_bit, 0xff); 4498f83f268SJie Qiu } 4508f83f268SJie Qiu 4518f83f268SJie Qiu static void mtk_hdmi_hw_aud_set_bit_num(struct mtk_hdmi *hdmi, 4528f83f268SJie Qiu enum hdmi_audio_sample_size bit_num) 4538f83f268SJie Qiu { 4548f83f268SJie Qiu u32 val; 4558f83f268SJie Qiu 4568f83f268SJie Qiu switch (bit_num) { 4578f83f268SJie Qiu case HDMI_AUDIO_SAMPLE_SIZE_16: 4588f83f268SJie Qiu val = AOUT_16BIT; 4598f83f268SJie Qiu break; 4608f83f268SJie Qiu case HDMI_AUDIO_SAMPLE_SIZE_20: 4618f83f268SJie Qiu val = AOUT_20BIT; 4628f83f268SJie Qiu break; 4638f83f268SJie Qiu case HDMI_AUDIO_SAMPLE_SIZE_24: 4648f83f268SJie Qiu case HDMI_AUDIO_SAMPLE_SIZE_STREAM: 4658f83f268SJie Qiu val = AOUT_24BIT; 4668f83f268SJie Qiu break; 4678f83f268SJie Qiu } 4688f83f268SJie Qiu 4698f83f268SJie Qiu mtk_hdmi_mask(hdmi, GRL_AOUT_CFG, val, AOUT_BNUM_SEL_MASK); 4708f83f268SJie Qiu } 4718f83f268SJie Qiu 4728f83f268SJie Qiu static void mtk_hdmi_hw_aud_set_i2s_fmt(struct mtk_hdmi *hdmi, 4738f83f268SJie Qiu enum hdmi_aud_i2s_fmt i2s_fmt) 4748f83f268SJie Qiu { 4758f83f268SJie Qiu u32 val; 4768f83f268SJie Qiu 4778f83f268SJie Qiu val = mtk_hdmi_read(hdmi, GRL_CFG0); 4788f83f268SJie Qiu val &= ~(CFG0_W_LENGTH_MASK | CFG0_I2S_MODE_MASK); 4798f83f268SJie Qiu 4808f83f268SJie Qiu switch (i2s_fmt) { 4818f83f268SJie Qiu case HDMI_I2S_MODE_RJT_24BIT: 4828f83f268SJie Qiu val |= CFG0_I2S_MODE_RTJ | CFG0_W_LENGTH_24BIT; 4838f83f268SJie Qiu break; 4848f83f268SJie Qiu case HDMI_I2S_MODE_RJT_16BIT: 4858f83f268SJie Qiu val |= CFG0_I2S_MODE_RTJ | CFG0_W_LENGTH_16BIT; 4868f83f268SJie Qiu break; 4878f83f268SJie Qiu case HDMI_I2S_MODE_LJT_24BIT: 4888f83f268SJie Qiu default: 4898f83f268SJie Qiu val |= CFG0_I2S_MODE_LTJ | CFG0_W_LENGTH_24BIT; 4908f83f268SJie Qiu break; 4918f83f268SJie Qiu case HDMI_I2S_MODE_LJT_16BIT: 4928f83f268SJie Qiu val |= CFG0_I2S_MODE_LTJ | CFG0_W_LENGTH_16BIT; 4938f83f268SJie Qiu break; 4948f83f268SJie Qiu case HDMI_I2S_MODE_I2S_24BIT: 4958f83f268SJie Qiu val |= CFG0_I2S_MODE_I2S | CFG0_W_LENGTH_24BIT; 4968f83f268SJie Qiu break; 4978f83f268SJie Qiu case HDMI_I2S_MODE_I2S_16BIT: 4988f83f268SJie Qiu val |= CFG0_I2S_MODE_I2S | CFG0_W_LENGTH_16BIT; 4998f83f268SJie Qiu break; 5008f83f268SJie Qiu } 5018f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_CFG0, val); 5028f83f268SJie Qiu } 5038f83f268SJie Qiu 5048f83f268SJie Qiu static void mtk_hdmi_hw_audio_config(struct mtk_hdmi *hdmi, bool dst) 5058f83f268SJie Qiu { 5068f83f268SJie Qiu const u8 mask = HIGH_BIT_RATE | DST_NORMAL_DOUBLE | SACD_DST | DSD_SEL; 5078f83f268SJie Qiu u8 val; 5088f83f268SJie Qiu 5098f83f268SJie Qiu /* Disable high bitrate, set DST packet normal/double */ 5108f83f268SJie Qiu mtk_hdmi_clear_bits(hdmi, GRL_AOUT_CFG, HIGH_BIT_RATE_PACKET_ALIGN); 5118f83f268SJie Qiu 5128f83f268SJie Qiu if (dst) 5138f83f268SJie Qiu val = DST_NORMAL_DOUBLE | SACD_DST; 5148f83f268SJie Qiu else 5158f83f268SJie Qiu val = 0; 5168f83f268SJie Qiu 5178f83f268SJie Qiu mtk_hdmi_mask(hdmi, GRL_AUDIO_CFG, val, mask); 5188f83f268SJie Qiu } 5198f83f268SJie Qiu 5208f83f268SJie Qiu static void mtk_hdmi_hw_aud_set_i2s_chan_num(struct mtk_hdmi *hdmi, 5218f83f268SJie Qiu enum hdmi_aud_channel_type channel_type, 5228f83f268SJie Qiu u8 channel_count) 5238f83f268SJie Qiu { 5248f83f268SJie Qiu unsigned int ch_switch; 5258f83f268SJie Qiu u8 i2s_uv; 5268f83f268SJie Qiu 5278f83f268SJie Qiu ch_switch = CH_SWITCH(7, 7) | CH_SWITCH(6, 6) | 5288f83f268SJie Qiu CH_SWITCH(5, 5) | CH_SWITCH(4, 4) | 5298f83f268SJie Qiu CH_SWITCH(3, 3) | CH_SWITCH(1, 2) | 5308f83f268SJie Qiu CH_SWITCH(2, 1) | CH_SWITCH(0, 0); 5318f83f268SJie Qiu 5328f83f268SJie Qiu if (channel_count == 2) { 5338f83f268SJie Qiu i2s_uv = I2S_UV_CH_EN(0); 5348f83f268SJie Qiu } else if (channel_count == 3 || channel_count == 4) { 5358f83f268SJie Qiu if (channel_count == 4 && 5368f83f268SJie Qiu (channel_type == HDMI_AUD_CHAN_TYPE_3_0_LRS || 5378f83f268SJie Qiu channel_type == HDMI_AUD_CHAN_TYPE_4_0)) 5388f83f268SJie Qiu i2s_uv = I2S_UV_CH_EN(2) | I2S_UV_CH_EN(0); 5398f83f268SJie Qiu else 5408f83f268SJie Qiu i2s_uv = I2S_UV_CH_EN(3) | I2S_UV_CH_EN(2); 5418f83f268SJie Qiu } else if (channel_count == 6 || channel_count == 5) { 5428f83f268SJie Qiu if (channel_count == 6 && 5438f83f268SJie Qiu channel_type != HDMI_AUD_CHAN_TYPE_5_1 && 5448f83f268SJie Qiu channel_type != HDMI_AUD_CHAN_TYPE_4_1_CLRS) { 5458f83f268SJie Qiu i2s_uv = I2S_UV_CH_EN(3) | I2S_UV_CH_EN(2) | 5468f83f268SJie Qiu I2S_UV_CH_EN(1) | I2S_UV_CH_EN(0); 5478f83f268SJie Qiu } else { 5488f83f268SJie Qiu i2s_uv = I2S_UV_CH_EN(2) | I2S_UV_CH_EN(1) | 5498f83f268SJie Qiu I2S_UV_CH_EN(0); 5508f83f268SJie Qiu } 5518f83f268SJie Qiu } else if (channel_count == 8 || channel_count == 7) { 5528f83f268SJie Qiu i2s_uv = I2S_UV_CH_EN(3) | I2S_UV_CH_EN(2) | 5538f83f268SJie Qiu I2S_UV_CH_EN(1) | I2S_UV_CH_EN(0); 5548f83f268SJie Qiu } else { 5558f83f268SJie Qiu i2s_uv = I2S_UV_CH_EN(0); 5568f83f268SJie Qiu } 5578f83f268SJie Qiu 5588f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_CH_SW0, ch_switch & 0xff); 5598f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_CH_SW1, (ch_switch >> 8) & 0xff); 5608f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_CH_SW2, (ch_switch >> 16) & 0xff); 5618f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_I2S_UV, i2s_uv); 5628f83f268SJie Qiu } 5638f83f268SJie Qiu 5648f83f268SJie Qiu static void mtk_hdmi_hw_aud_set_input_type(struct mtk_hdmi *hdmi, 5658f83f268SJie Qiu enum hdmi_aud_input_type input_type) 5668f83f268SJie Qiu { 5678f83f268SJie Qiu u32 val; 5688f83f268SJie Qiu 5698f83f268SJie Qiu val = mtk_hdmi_read(hdmi, GRL_CFG1); 5708f83f268SJie Qiu if (input_type == HDMI_AUD_INPUT_I2S && 5718f83f268SJie Qiu (val & CFG1_SPDIF) == CFG1_SPDIF) { 5728f83f268SJie Qiu val &= ~CFG1_SPDIF; 5738f83f268SJie Qiu } else if (input_type == HDMI_AUD_INPUT_SPDIF && 5748f83f268SJie Qiu (val & CFG1_SPDIF) == 0) { 5758f83f268SJie Qiu val |= CFG1_SPDIF; 5768f83f268SJie Qiu } 5778f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_CFG1, val); 5788f83f268SJie Qiu } 5798f83f268SJie Qiu 5808f83f268SJie Qiu static void mtk_hdmi_hw_aud_set_channel_status(struct mtk_hdmi *hdmi, 5818f83f268SJie Qiu u8 *channel_status) 5828f83f268SJie Qiu { 5838f83f268SJie Qiu int i; 5848f83f268SJie Qiu 5858f83f268SJie Qiu for (i = 0; i < 5; i++) { 5868f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_I2S_C_STA0 + i * 4, channel_status[i]); 5878f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_L_STATUS_0 + i * 4, channel_status[i]); 5888f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_R_STATUS_0 + i * 4, channel_status[i]); 5898f83f268SJie Qiu } 5908f83f268SJie Qiu for (; i < 24; i++) { 5918f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_L_STATUS_0 + i * 4, 0); 5928f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_R_STATUS_0 + i * 4, 0); 5938f83f268SJie Qiu } 5948f83f268SJie Qiu } 5958f83f268SJie Qiu 5968f83f268SJie Qiu static void mtk_hdmi_hw_aud_src_reenable(struct mtk_hdmi *hdmi) 5978f83f268SJie Qiu { 5988f83f268SJie Qiu u32 val; 5998f83f268SJie Qiu 6008f83f268SJie Qiu val = mtk_hdmi_read(hdmi, GRL_MIX_CTRL); 6018f83f268SJie Qiu if (val & MIX_CTRL_SRC_EN) { 6028f83f268SJie Qiu val &= ~MIX_CTRL_SRC_EN; 6038f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_MIX_CTRL, val); 6048f83f268SJie Qiu usleep_range(255, 512); 6058f83f268SJie Qiu val |= MIX_CTRL_SRC_EN; 6068f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_MIX_CTRL, val); 6078f83f268SJie Qiu } 6088f83f268SJie Qiu } 6098f83f268SJie Qiu 6108f83f268SJie Qiu static void mtk_hdmi_hw_aud_src_disable(struct mtk_hdmi *hdmi) 6118f83f268SJie Qiu { 6128f83f268SJie Qiu u32 val; 6138f83f268SJie Qiu 6148f83f268SJie Qiu val = mtk_hdmi_read(hdmi, GRL_MIX_CTRL); 6158f83f268SJie Qiu val &= ~MIX_CTRL_SRC_EN; 6168f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_MIX_CTRL, val); 6178f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_SHIFT_L1, 0x00); 6188f83f268SJie Qiu } 6198f83f268SJie Qiu 6208f83f268SJie Qiu static void mtk_hdmi_hw_aud_set_mclk(struct mtk_hdmi *hdmi, 6218f83f268SJie Qiu enum hdmi_aud_mclk mclk) 6228f83f268SJie Qiu { 6238f83f268SJie Qiu u32 val; 6248f83f268SJie Qiu 6258f83f268SJie Qiu val = mtk_hdmi_read(hdmi, GRL_CFG5); 6268f83f268SJie Qiu val &= CFG5_CD_RATIO_MASK; 6278f83f268SJie Qiu 6288f83f268SJie Qiu switch (mclk) { 6298f83f268SJie Qiu case HDMI_AUD_MCLK_128FS: 6308f83f268SJie Qiu val |= CFG5_FS128; 6318f83f268SJie Qiu break; 6328f83f268SJie Qiu case HDMI_AUD_MCLK_256FS: 6338f83f268SJie Qiu val |= CFG5_FS256; 6348f83f268SJie Qiu break; 6358f83f268SJie Qiu case HDMI_AUD_MCLK_384FS: 6368f83f268SJie Qiu val |= CFG5_FS384; 6378f83f268SJie Qiu break; 6388f83f268SJie Qiu case HDMI_AUD_MCLK_512FS: 6398f83f268SJie Qiu val |= CFG5_FS512; 6408f83f268SJie Qiu break; 6418f83f268SJie Qiu case HDMI_AUD_MCLK_768FS: 6428f83f268SJie Qiu val |= CFG5_FS768; 6438f83f268SJie Qiu break; 6448f83f268SJie Qiu default: 6458f83f268SJie Qiu val |= CFG5_FS256; 6468f83f268SJie Qiu break; 6478f83f268SJie Qiu } 6488f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_CFG5, val); 6498f83f268SJie Qiu } 6508f83f268SJie Qiu 6518f83f268SJie Qiu struct hdmi_acr_n { 6528f83f268SJie Qiu unsigned int clock; 6538f83f268SJie Qiu unsigned int n[3]; 6548f83f268SJie Qiu }; 6558f83f268SJie Qiu 6568f83f268SJie Qiu /* Recommended N values from HDMI specification, tables 7-1 to 7-3 */ 6578f83f268SJie Qiu static const struct hdmi_acr_n hdmi_rec_n_table[] = { 6588f83f268SJie Qiu /* Clock, N: 32kHz 44.1kHz 48kHz */ 6598f83f268SJie Qiu { 25175, { 4576, 7007, 6864 } }, 6608f83f268SJie Qiu { 74176, { 11648, 17836, 11648 } }, 6618f83f268SJie Qiu { 148352, { 11648, 8918, 5824 } }, 6628f83f268SJie Qiu { 296703, { 5824, 4459, 5824 } }, 6638f83f268SJie Qiu { 297000, { 3072, 4704, 5120 } }, 6648f83f268SJie Qiu { 0, { 4096, 6272, 6144 } }, /* all other TMDS clocks */ 6658f83f268SJie Qiu }; 6668f83f268SJie Qiu 6678f83f268SJie Qiu /** 6688f83f268SJie Qiu * hdmi_recommended_n() - Return N value recommended by HDMI specification 6698f83f268SJie Qiu * @freq: audio sample rate in Hz 6708f83f268SJie Qiu * @clock: rounded TMDS clock in kHz 6718f83f268SJie Qiu */ 6728f83f268SJie Qiu static unsigned int hdmi_recommended_n(unsigned int freq, unsigned int clock) 6738f83f268SJie Qiu { 6748f83f268SJie Qiu const struct hdmi_acr_n *recommended; 6758f83f268SJie Qiu unsigned int i; 6768f83f268SJie Qiu 6778f83f268SJie Qiu for (i = 0; i < ARRAY_SIZE(hdmi_rec_n_table) - 1; i++) { 6788f83f268SJie Qiu if (clock == hdmi_rec_n_table[i].clock) 6798f83f268SJie Qiu break; 6808f83f268SJie Qiu } 6818f83f268SJie Qiu recommended = hdmi_rec_n_table + i; 6828f83f268SJie Qiu 6838f83f268SJie Qiu switch (freq) { 6848f83f268SJie Qiu case 32000: 6858f83f268SJie Qiu return recommended->n[0]; 6868f83f268SJie Qiu case 44100: 6878f83f268SJie Qiu return recommended->n[1]; 6888f83f268SJie Qiu case 48000: 6898f83f268SJie Qiu return recommended->n[2]; 6908f83f268SJie Qiu case 88200: 6918f83f268SJie Qiu return recommended->n[1] * 2; 6928f83f268SJie Qiu case 96000: 6938f83f268SJie Qiu return recommended->n[2] * 2; 6948f83f268SJie Qiu case 176400: 6958f83f268SJie Qiu return recommended->n[1] * 4; 6968f83f268SJie Qiu case 192000: 6978f83f268SJie Qiu return recommended->n[2] * 4; 6988f83f268SJie Qiu default: 6998f83f268SJie Qiu return (128 * freq) / 1000; 7008f83f268SJie Qiu } 7018f83f268SJie Qiu } 7028f83f268SJie Qiu 7038f83f268SJie Qiu static unsigned int hdmi_mode_clock_to_hz(unsigned int clock) 7048f83f268SJie Qiu { 7058f83f268SJie Qiu switch (clock) { 7068f83f268SJie Qiu case 25175: 7078f83f268SJie Qiu return 25174825; /* 25.2/1.001 MHz */ 7088f83f268SJie Qiu case 74176: 7098f83f268SJie Qiu return 74175824; /* 74.25/1.001 MHz */ 7108f83f268SJie Qiu case 148352: 7118f83f268SJie Qiu return 148351648; /* 148.5/1.001 MHz */ 7128f83f268SJie Qiu case 296703: 7138f83f268SJie Qiu return 296703297; /* 297/1.001 MHz */ 7148f83f268SJie Qiu default: 7158f83f268SJie Qiu return clock * 1000; 7168f83f268SJie Qiu } 7178f83f268SJie Qiu } 7188f83f268SJie Qiu 7198f83f268SJie Qiu static unsigned int hdmi_expected_cts(unsigned int audio_sample_rate, 7208f83f268SJie Qiu unsigned int tmds_clock, unsigned int n) 7218f83f268SJie Qiu { 7228f83f268SJie Qiu return DIV_ROUND_CLOSEST_ULL((u64)hdmi_mode_clock_to_hz(tmds_clock) * n, 7238f83f268SJie Qiu 128 * audio_sample_rate); 7248f83f268SJie Qiu } 7258f83f268SJie Qiu 7268f83f268SJie Qiu static void do_hdmi_hw_aud_set_ncts(struct mtk_hdmi *hdmi, unsigned int n, 7278f83f268SJie Qiu unsigned int cts) 7288f83f268SJie Qiu { 7298f83f268SJie Qiu unsigned char val[NCTS_BYTES]; 7308f83f268SJie Qiu int i; 7318f83f268SJie Qiu 7328f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_NCTS, 0); 7338f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_NCTS, 0); 7348f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_NCTS, 0); 7358f83f268SJie Qiu memset(val, 0, sizeof(val)); 7368f83f268SJie Qiu 7378f83f268SJie Qiu val[0] = (cts >> 24) & 0xff; 7388f83f268SJie Qiu val[1] = (cts >> 16) & 0xff; 7398f83f268SJie Qiu val[2] = (cts >> 8) & 0xff; 7408f83f268SJie Qiu val[3] = cts & 0xff; 7418f83f268SJie Qiu 7428f83f268SJie Qiu val[4] = (n >> 16) & 0xff; 7438f83f268SJie Qiu val[5] = (n >> 8) & 0xff; 7448f83f268SJie Qiu val[6] = n & 0xff; 7458f83f268SJie Qiu 7468f83f268SJie Qiu for (i = 0; i < NCTS_BYTES; i++) 7478f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_NCTS, val[i]); 7488f83f268SJie Qiu } 7498f83f268SJie Qiu 7508f83f268SJie Qiu static void mtk_hdmi_hw_aud_set_ncts(struct mtk_hdmi *hdmi, 7518f83f268SJie Qiu unsigned int sample_rate, 7528f83f268SJie Qiu unsigned int clock) 7538f83f268SJie Qiu { 7548f83f268SJie Qiu unsigned int n, cts; 7558f83f268SJie Qiu 7568f83f268SJie Qiu n = hdmi_recommended_n(sample_rate, clock); 7578f83f268SJie Qiu cts = hdmi_expected_cts(sample_rate, clock, n); 7588f83f268SJie Qiu 7598f83f268SJie Qiu dev_dbg(hdmi->dev, "%s: sample_rate=%u, clock=%d, cts=%u, n=%u\n", 7608f83f268SJie Qiu __func__, sample_rate, clock, n, cts); 7618f83f268SJie Qiu 7628f83f268SJie Qiu mtk_hdmi_mask(hdmi, DUMMY_304, AUDIO_I2S_NCTS_SEL_64, 7638f83f268SJie Qiu AUDIO_I2S_NCTS_SEL); 7648f83f268SJie Qiu do_hdmi_hw_aud_set_ncts(hdmi, n, cts); 7658f83f268SJie Qiu } 7668f83f268SJie Qiu 7678f83f268SJie Qiu static u8 mtk_hdmi_aud_get_chnl_count(enum hdmi_aud_channel_type channel_type) 7688f83f268SJie Qiu { 7698f83f268SJie Qiu switch (channel_type) { 7708f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_1_0: 7718f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_1_1: 7728f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_2_0: 7738f83f268SJie Qiu return 2; 7748f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_2_1: 7758f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_3_0: 7768f83f268SJie Qiu return 3; 7778f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_3_1: 7788f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_4_0: 7798f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_3_0_LRS: 7808f83f268SJie Qiu return 4; 7818f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_4_1: 7828f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_5_0: 7838f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_3_1_LRS: 7848f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_4_0_CLRS: 7858f83f268SJie Qiu return 5; 7868f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_5_1: 7878f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_6_0: 7888f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_4_1_CLRS: 7898f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_6_0_CS: 7908f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_6_0_CH: 7918f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_6_0_OH: 7928f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_6_0_CHR: 7938f83f268SJie Qiu return 6; 7948f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_6_1: 7958f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_6_1_CS: 7968f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_6_1_CH: 7978f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_6_1_OH: 7988f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_6_1_CHR: 7998f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0: 8008f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_LH_RH: 8018f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_LSR_RSR: 8028f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_LC_RC: 8038f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_LW_RW: 8048f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_LSD_RSD: 8058f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_LSS_RSS: 8068f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_LHS_RHS: 8078f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_CS_CH: 8088f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_CS_OH: 8098f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_CS_CHR: 8108f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_CH_OH: 8118f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_CH_CHR: 8128f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_OH_CHR: 8138f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_LSS_RSS_LSR_RSR: 8148f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_8_0_LH_RH_CS: 8158f83f268SJie Qiu return 7; 8168f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1: 8178f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_LH_RH: 8188f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_LSR_RSR: 8198f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_LC_RC: 8208f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_LW_RW: 8218f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_LSD_RSD: 8228f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_LSS_RSS: 8238f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_LHS_RHS: 8248f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_CS_CH: 8258f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_CS_OH: 8268f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_CS_CHR: 8278f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_CH_OH: 8288f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_CH_CHR: 8298f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_OH_CHR: 8308f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_LSS_RSS_LSR_RSR: 8318f83f268SJie Qiu return 8; 8328f83f268SJie Qiu default: 8338f83f268SJie Qiu return 2; 8348f83f268SJie Qiu } 8358f83f268SJie Qiu } 8368f83f268SJie Qiu 8378f83f268SJie Qiu static int mtk_hdmi_video_change_vpll(struct mtk_hdmi *hdmi, u32 clock) 8388f83f268SJie Qiu { 8398f83f268SJie Qiu unsigned long rate; 8408f83f268SJie Qiu int ret; 8418f83f268SJie Qiu 8428f83f268SJie Qiu /* The DPI driver already should have set TVDPLL to the correct rate */ 8438f83f268SJie Qiu ret = clk_set_rate(hdmi->clk[MTK_HDMI_CLK_HDMI_PLL], clock); 8448f83f268SJie Qiu if (ret) { 8458f83f268SJie Qiu dev_err(hdmi->dev, "Failed to set PLL to %u Hz: %d\n", clock, 8468f83f268SJie Qiu ret); 8478f83f268SJie Qiu return ret; 8488f83f268SJie Qiu } 8498f83f268SJie Qiu 8508f83f268SJie Qiu rate = clk_get_rate(hdmi->clk[MTK_HDMI_CLK_HDMI_PLL]); 8518f83f268SJie Qiu 8528f83f268SJie Qiu if (DIV_ROUND_CLOSEST(rate, 1000) != DIV_ROUND_CLOSEST(clock, 1000)) 8538f83f268SJie Qiu dev_warn(hdmi->dev, "Want PLL %u Hz, got %lu Hz\n", clock, 8548f83f268SJie Qiu rate); 8558f83f268SJie Qiu else 8568f83f268SJie Qiu dev_dbg(hdmi->dev, "Want PLL %u Hz, got %lu Hz\n", clock, rate); 8578f83f268SJie Qiu 8588f83f268SJie Qiu mtk_hdmi_hw_config_sys(hdmi); 8598f83f268SJie Qiu mtk_hdmi_hw_set_deep_color_mode(hdmi); 8608f83f268SJie Qiu return 0; 8618f83f268SJie Qiu } 8628f83f268SJie Qiu 8638f83f268SJie Qiu static void mtk_hdmi_video_set_display_mode(struct mtk_hdmi *hdmi, 8648f83f268SJie Qiu struct drm_display_mode *mode) 8658f83f268SJie Qiu { 8668f83f268SJie Qiu mtk_hdmi_hw_reset(hdmi); 8678f83f268SJie Qiu mtk_hdmi_hw_enable_notice(hdmi, true); 8688f83f268SJie Qiu mtk_hdmi_hw_write_int_mask(hdmi, 0xff); 8698f83f268SJie Qiu mtk_hdmi_hw_enable_dvi_mode(hdmi, hdmi->dvi_mode); 8708f83f268SJie Qiu mtk_hdmi_hw_ncts_auto_write_enable(hdmi, true); 8718f83f268SJie Qiu 8728f83f268SJie Qiu mtk_hdmi_hw_msic_setting(hdmi, mode); 8738f83f268SJie Qiu } 8748f83f268SJie Qiu 8758f83f268SJie Qiu 876a812ca19SBernard Zhao static void mtk_hdmi_aud_set_input(struct mtk_hdmi *hdmi) 8778f83f268SJie Qiu { 8788f83f268SJie Qiu enum hdmi_aud_channel_type chan_type; 8798f83f268SJie Qiu u8 chan_count; 8808f83f268SJie Qiu bool dst; 8818f83f268SJie Qiu 8828f83f268SJie Qiu mtk_hdmi_hw_aud_set_channel_swap(hdmi, HDMI_AUD_SWAP_LFE_CC); 8838f83f268SJie Qiu mtk_hdmi_set_bits(hdmi, GRL_MIX_CTRL, MIX_CTRL_FLAT); 8848f83f268SJie Qiu 8858f83f268SJie Qiu if (hdmi->aud_param.aud_input_type == HDMI_AUD_INPUT_SPDIF && 8868f83f268SJie Qiu hdmi->aud_param.aud_codec == HDMI_AUDIO_CODING_TYPE_DST) { 8878f83f268SJie Qiu mtk_hdmi_hw_aud_set_bit_num(hdmi, HDMI_AUDIO_SAMPLE_SIZE_24); 8888f83f268SJie Qiu } else if (hdmi->aud_param.aud_i2s_fmt == HDMI_I2S_MODE_LJT_24BIT) { 8898f83f268SJie Qiu hdmi->aud_param.aud_i2s_fmt = HDMI_I2S_MODE_LJT_16BIT; 8908f83f268SJie Qiu } 8918f83f268SJie Qiu 8928f83f268SJie Qiu mtk_hdmi_hw_aud_set_i2s_fmt(hdmi, hdmi->aud_param.aud_i2s_fmt); 8938f83f268SJie Qiu mtk_hdmi_hw_aud_set_bit_num(hdmi, HDMI_AUDIO_SAMPLE_SIZE_24); 8948f83f268SJie Qiu 8958f83f268SJie Qiu dst = ((hdmi->aud_param.aud_input_type == HDMI_AUD_INPUT_SPDIF) && 8968f83f268SJie Qiu (hdmi->aud_param.aud_codec == HDMI_AUDIO_CODING_TYPE_DST)); 8978f83f268SJie Qiu mtk_hdmi_hw_audio_config(hdmi, dst); 8988f83f268SJie Qiu 8998f83f268SJie Qiu if (hdmi->aud_param.aud_input_type == HDMI_AUD_INPUT_SPDIF) 9008f83f268SJie Qiu chan_type = HDMI_AUD_CHAN_TYPE_2_0; 9018f83f268SJie Qiu else 9028f83f268SJie Qiu chan_type = hdmi->aud_param.aud_input_chan_type; 9038f83f268SJie Qiu chan_count = mtk_hdmi_aud_get_chnl_count(chan_type); 9048f83f268SJie Qiu mtk_hdmi_hw_aud_set_i2s_chan_num(hdmi, chan_type, chan_count); 9058f83f268SJie Qiu mtk_hdmi_hw_aud_set_input_type(hdmi, hdmi->aud_param.aud_input_type); 9068f83f268SJie Qiu } 9078f83f268SJie Qiu 9088f83f268SJie Qiu static int mtk_hdmi_aud_set_src(struct mtk_hdmi *hdmi, 9098f83f268SJie Qiu struct drm_display_mode *display_mode) 9108f83f268SJie Qiu { 9118f83f268SJie Qiu unsigned int sample_rate = hdmi->aud_param.codec_params.sample_rate; 9128f83f268SJie Qiu 913a812ca19SBernard Zhao mtk_hdmi_hw_ncts_enable(hdmi, false); 9148f83f268SJie Qiu mtk_hdmi_hw_aud_src_disable(hdmi); 9158f83f268SJie Qiu mtk_hdmi_clear_bits(hdmi, GRL_CFG2, CFG2_ACLK_INV); 9168f83f268SJie Qiu 9178f83f268SJie Qiu if (hdmi->aud_param.aud_input_type == HDMI_AUD_INPUT_I2S) { 9188f83f268SJie Qiu switch (sample_rate) { 9198f83f268SJie Qiu case 32000: 9208f83f268SJie Qiu case 44100: 9218f83f268SJie Qiu case 48000: 9228f83f268SJie Qiu case 88200: 9238f83f268SJie Qiu case 96000: 9248f83f268SJie Qiu break; 9258f83f268SJie Qiu default: 9268f83f268SJie Qiu return -EINVAL; 9278f83f268SJie Qiu } 9288f83f268SJie Qiu mtk_hdmi_hw_aud_set_mclk(hdmi, hdmi->aud_param.aud_mclk); 9298f83f268SJie Qiu } else { 9308f83f268SJie Qiu switch (sample_rate) { 9318f83f268SJie Qiu case 32000: 9328f83f268SJie Qiu case 44100: 9338f83f268SJie Qiu case 48000: 9348f83f268SJie Qiu break; 9358f83f268SJie Qiu default: 9368f83f268SJie Qiu return -EINVAL; 9378f83f268SJie Qiu } 9388f83f268SJie Qiu mtk_hdmi_hw_aud_set_mclk(hdmi, HDMI_AUD_MCLK_128FS); 9398f83f268SJie Qiu } 9408f83f268SJie Qiu 9418f83f268SJie Qiu mtk_hdmi_hw_aud_set_ncts(hdmi, sample_rate, display_mode->clock); 9428f83f268SJie Qiu 9438f83f268SJie Qiu mtk_hdmi_hw_aud_src_reenable(hdmi); 9448f83f268SJie Qiu return 0; 9458f83f268SJie Qiu } 9468f83f268SJie Qiu 9478f83f268SJie Qiu static int mtk_hdmi_aud_output_config(struct mtk_hdmi *hdmi, 9488f83f268SJie Qiu struct drm_display_mode *display_mode) 9498f83f268SJie Qiu { 9508f83f268SJie Qiu mtk_hdmi_hw_aud_mute(hdmi); 951a812ca19SBernard Zhao mtk_hdmi_hw_send_aud_packet(hdmi, false); 9528f83f268SJie Qiu 9538f83f268SJie Qiu mtk_hdmi_aud_set_input(hdmi); 9548f83f268SJie Qiu mtk_hdmi_aud_set_src(hdmi, display_mode); 9558f83f268SJie Qiu mtk_hdmi_hw_aud_set_channel_status(hdmi, 9568f83f268SJie Qiu hdmi->aud_param.codec_params.iec.status); 9578f83f268SJie Qiu 9588f83f268SJie Qiu usleep_range(50, 100); 9598f83f268SJie Qiu 960a812ca19SBernard Zhao mtk_hdmi_hw_ncts_enable(hdmi, true); 961a812ca19SBernard Zhao mtk_hdmi_hw_send_aud_packet(hdmi, true); 9628f83f268SJie Qiu mtk_hdmi_hw_aud_unmute(hdmi); 9638f83f268SJie Qiu return 0; 9648f83f268SJie Qiu } 9658f83f268SJie Qiu 9668f83f268SJie Qiu static int mtk_hdmi_setup_avi_infoframe(struct mtk_hdmi *hdmi, 9678f83f268SJie Qiu struct drm_display_mode *mode) 9688f83f268SJie Qiu { 9698f83f268SJie Qiu struct hdmi_avi_infoframe frame; 9703852489cSBernard Zhao u8 buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AVI_INFOFRAME_SIZE]; 9718f83f268SJie Qiu ssize_t err; 9728f83f268SJie Qiu 97313d0add3SVille Syrjälä err = drm_hdmi_avi_infoframe_from_display_mode(&frame, 9742e477391SDafna Hirschfeld hdmi->curr_conn, mode); 9758f83f268SJie Qiu if (err < 0) { 9768f83f268SJie Qiu dev_err(hdmi->dev, 9778f83f268SJie Qiu "Failed to get AVI infoframe from mode: %zd\n", err); 9788f83f268SJie Qiu return err; 9798f83f268SJie Qiu } 9808f83f268SJie Qiu 9818f83f268SJie Qiu err = hdmi_avi_infoframe_pack(&frame, buffer, sizeof(buffer)); 9828f83f268SJie Qiu if (err < 0) { 9838f83f268SJie Qiu dev_err(hdmi->dev, "Failed to pack AVI infoframe: %zd\n", err); 9848f83f268SJie Qiu return err; 9858f83f268SJie Qiu } 9868f83f268SJie Qiu 9878f83f268SJie Qiu mtk_hdmi_hw_send_info_frame(hdmi, buffer, sizeof(buffer)); 9888f83f268SJie Qiu return 0; 9898f83f268SJie Qiu } 9908f83f268SJie Qiu 9918f83f268SJie Qiu static int mtk_hdmi_setup_spd_infoframe(struct mtk_hdmi *hdmi, 9928f83f268SJie Qiu const char *vendor, 9938f83f268SJie Qiu const char *product) 9948f83f268SJie Qiu { 9958f83f268SJie Qiu struct hdmi_spd_infoframe frame; 9963852489cSBernard Zhao u8 buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_SPD_INFOFRAME_SIZE]; 9978f83f268SJie Qiu ssize_t err; 9988f83f268SJie Qiu 9998f83f268SJie Qiu err = hdmi_spd_infoframe_init(&frame, vendor, product); 10008f83f268SJie Qiu if (err < 0) { 10018f83f268SJie Qiu dev_err(hdmi->dev, "Failed to initialize SPD infoframe: %zd\n", 10028f83f268SJie Qiu err); 10038f83f268SJie Qiu return err; 10048f83f268SJie Qiu } 10058f83f268SJie Qiu 10068f83f268SJie Qiu err = hdmi_spd_infoframe_pack(&frame, buffer, sizeof(buffer)); 10078f83f268SJie Qiu if (err < 0) { 10088f83f268SJie Qiu dev_err(hdmi->dev, "Failed to pack SDP infoframe: %zd\n", err); 10098f83f268SJie Qiu return err; 10108f83f268SJie Qiu } 10118f83f268SJie Qiu 10128f83f268SJie Qiu mtk_hdmi_hw_send_info_frame(hdmi, buffer, sizeof(buffer)); 10138f83f268SJie Qiu return 0; 10148f83f268SJie Qiu } 10158f83f268SJie Qiu 10168f83f268SJie Qiu static int mtk_hdmi_setup_audio_infoframe(struct mtk_hdmi *hdmi) 10178f83f268SJie Qiu { 10188f83f268SJie Qiu struct hdmi_audio_infoframe frame; 10193852489cSBernard Zhao u8 buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AUDIO_INFOFRAME_SIZE]; 10208f83f268SJie Qiu ssize_t err; 10218f83f268SJie Qiu 10228f83f268SJie Qiu err = hdmi_audio_infoframe_init(&frame); 10238f83f268SJie Qiu if (err < 0) { 10248f83f268SJie Qiu dev_err(hdmi->dev, "Failed to setup audio infoframe: %zd\n", 10258f83f268SJie Qiu err); 10268f83f268SJie Qiu return err; 10278f83f268SJie Qiu } 10288f83f268SJie Qiu 10298f83f268SJie Qiu frame.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM; 10308f83f268SJie Qiu frame.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM; 10318f83f268SJie Qiu frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM; 10328f83f268SJie Qiu frame.channels = mtk_hdmi_aud_get_chnl_count( 10338f83f268SJie Qiu hdmi->aud_param.aud_input_chan_type); 10348f83f268SJie Qiu 10358f83f268SJie Qiu err = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer)); 10368f83f268SJie Qiu if (err < 0) { 10378f83f268SJie Qiu dev_err(hdmi->dev, "Failed to pack audio infoframe: %zd\n", 10388f83f268SJie Qiu err); 10398f83f268SJie Qiu return err; 10408f83f268SJie Qiu } 10418f83f268SJie Qiu 10428f83f268SJie Qiu mtk_hdmi_hw_send_info_frame(hdmi, buffer, sizeof(buffer)); 10438f83f268SJie Qiu return 0; 10448f83f268SJie Qiu } 10458f83f268SJie Qiu 10468f83f268SJie Qiu static int mtk_hdmi_setup_vendor_specific_infoframe(struct mtk_hdmi *hdmi, 10478f83f268SJie Qiu struct drm_display_mode *mode) 10488f83f268SJie Qiu { 10498f83f268SJie Qiu struct hdmi_vendor_infoframe frame; 10508f83f268SJie Qiu u8 buffer[10]; 10518f83f268SJie Qiu ssize_t err; 10528f83f268SJie Qiu 1053f1781e9bSVille Syrjälä err = drm_hdmi_vendor_infoframe_from_display_mode(&frame, 10542e477391SDafna Hirschfeld hdmi->curr_conn, mode); 10558f83f268SJie Qiu if (err) { 10568f83f268SJie Qiu dev_err(hdmi->dev, 10578f83f268SJie Qiu "Failed to get vendor infoframe from mode: %zd\n", err); 10588f83f268SJie Qiu return err; 10598f83f268SJie Qiu } 10608f83f268SJie Qiu 10618f83f268SJie Qiu err = hdmi_vendor_infoframe_pack(&frame, buffer, sizeof(buffer)); 1062014580ffSYYS if (err < 0) { 10638f83f268SJie Qiu dev_err(hdmi->dev, "Failed to pack vendor infoframe: %zd\n", 10648f83f268SJie Qiu err); 10658f83f268SJie Qiu return err; 10668f83f268SJie Qiu } 10678f83f268SJie Qiu 10688f83f268SJie Qiu mtk_hdmi_hw_send_info_frame(hdmi, buffer, sizeof(buffer)); 10698f83f268SJie Qiu return 0; 10708f83f268SJie Qiu } 10718f83f268SJie Qiu 10728f83f268SJie Qiu static int mtk_hdmi_output_init(struct mtk_hdmi *hdmi) 10738f83f268SJie Qiu { 10748f83f268SJie Qiu struct hdmi_audio_param *aud_param = &hdmi->aud_param; 10758f83f268SJie Qiu 10768f83f268SJie Qiu hdmi->csp = HDMI_COLORSPACE_RGB; 10778f83f268SJie Qiu aud_param->aud_codec = HDMI_AUDIO_CODING_TYPE_PCM; 10788f83f268SJie Qiu aud_param->aud_sampe_size = HDMI_AUDIO_SAMPLE_SIZE_16; 10798f83f268SJie Qiu aud_param->aud_input_type = HDMI_AUD_INPUT_I2S; 10808f83f268SJie Qiu aud_param->aud_i2s_fmt = HDMI_I2S_MODE_I2S_24BIT; 10818f83f268SJie Qiu aud_param->aud_mclk = HDMI_AUD_MCLK_128FS; 10828f83f268SJie Qiu aud_param->aud_input_chan_type = HDMI_AUD_CHAN_TYPE_2_0; 10838f83f268SJie Qiu 10848f83f268SJie Qiu return 0; 10858f83f268SJie Qiu } 10868f83f268SJie Qiu 1087188af070SBaoyou Xie static void mtk_hdmi_audio_enable(struct mtk_hdmi *hdmi) 10888f83f268SJie Qiu { 1089a812ca19SBernard Zhao mtk_hdmi_hw_send_aud_packet(hdmi, true); 10908f83f268SJie Qiu hdmi->audio_enable = true; 10918f83f268SJie Qiu } 10928f83f268SJie Qiu 1093188af070SBaoyou Xie static void mtk_hdmi_audio_disable(struct mtk_hdmi *hdmi) 10948f83f268SJie Qiu { 1095a812ca19SBernard Zhao mtk_hdmi_hw_send_aud_packet(hdmi, false); 10968f83f268SJie Qiu hdmi->audio_enable = false; 10978f83f268SJie Qiu } 10988f83f268SJie Qiu 1099188af070SBaoyou Xie static int mtk_hdmi_audio_set_param(struct mtk_hdmi *hdmi, 11008f83f268SJie Qiu struct hdmi_audio_param *param) 11018f83f268SJie Qiu { 11028f83f268SJie Qiu if (!hdmi->audio_enable) { 11038f83f268SJie Qiu dev_err(hdmi->dev, "hdmi audio is in disable state!\n"); 11048f83f268SJie Qiu return -EINVAL; 11058f83f268SJie Qiu } 11068f83f268SJie Qiu dev_dbg(hdmi->dev, "codec:%d, input:%d, channel:%d, fs:%d\n", 11078f83f268SJie Qiu param->aud_codec, param->aud_input_type, 11088f83f268SJie Qiu param->aud_input_chan_type, param->codec_params.sample_rate); 11098f83f268SJie Qiu memcpy(&hdmi->aud_param, param, sizeof(*param)); 11108f83f268SJie Qiu return mtk_hdmi_aud_output_config(hdmi, &hdmi->mode); 11118f83f268SJie Qiu } 11128f83f268SJie Qiu 11138f83f268SJie Qiu static int mtk_hdmi_output_set_display_mode(struct mtk_hdmi *hdmi, 11148f83f268SJie Qiu struct drm_display_mode *mode) 11158f83f268SJie Qiu { 11168f83f268SJie Qiu int ret; 11178f83f268SJie Qiu 11188f83f268SJie Qiu mtk_hdmi_hw_vid_black(hdmi, true); 11198f83f268SJie Qiu mtk_hdmi_hw_aud_mute(hdmi); 11208f83f268SJie Qiu mtk_hdmi_hw_send_av_mute(hdmi); 11218f83f268SJie Qiu phy_power_off(hdmi->phy); 11228f83f268SJie Qiu 11238f83f268SJie Qiu ret = mtk_hdmi_video_change_vpll(hdmi, 11248f83f268SJie Qiu mode->clock * 1000); 11258f83f268SJie Qiu if (ret) { 11268f83f268SJie Qiu dev_err(hdmi->dev, "Failed to set vpll: %d\n", ret); 11278f83f268SJie Qiu return ret; 11288f83f268SJie Qiu } 11298f83f268SJie Qiu mtk_hdmi_video_set_display_mode(hdmi, mode); 11308f83f268SJie Qiu 11318f83f268SJie Qiu phy_power_on(hdmi->phy); 11328f83f268SJie Qiu mtk_hdmi_aud_output_config(hdmi, mode); 11338f83f268SJie Qiu 11348f83f268SJie Qiu mtk_hdmi_hw_vid_black(hdmi, false); 11358f83f268SJie Qiu mtk_hdmi_hw_aud_unmute(hdmi); 11368f83f268SJie Qiu mtk_hdmi_hw_send_av_unmute(hdmi); 11378f83f268SJie Qiu 11388f83f268SJie Qiu return 0; 11398f83f268SJie Qiu } 11408f83f268SJie Qiu 11418f83f268SJie Qiu static const char * const mtk_hdmi_clk_names[MTK_HDMI_CLK_COUNT] = { 11428f83f268SJie Qiu [MTK_HDMI_CLK_HDMI_PIXEL] = "pixel", 11438f83f268SJie Qiu [MTK_HDMI_CLK_HDMI_PLL] = "pll", 11448f83f268SJie Qiu [MTK_HDMI_CLK_AUD_BCLK] = "bclk", 11458f83f268SJie Qiu [MTK_HDMI_CLK_AUD_SPDIF] = "spdif", 11468f83f268SJie Qiu }; 11478f83f268SJie Qiu 11488f83f268SJie Qiu static int mtk_hdmi_get_all_clk(struct mtk_hdmi *hdmi, 11498f83f268SJie Qiu struct device_node *np) 11508f83f268SJie Qiu { 11518f83f268SJie Qiu int i; 11528f83f268SJie Qiu 11538f83f268SJie Qiu for (i = 0; i < ARRAY_SIZE(mtk_hdmi_clk_names); i++) { 11548f83f268SJie Qiu hdmi->clk[i] = of_clk_get_by_name(np, 11558f83f268SJie Qiu mtk_hdmi_clk_names[i]); 11568f83f268SJie Qiu if (IS_ERR(hdmi->clk[i])) 11578f83f268SJie Qiu return PTR_ERR(hdmi->clk[i]); 11588f83f268SJie Qiu } 11598f83f268SJie Qiu return 0; 11608f83f268SJie Qiu } 11618f83f268SJie Qiu 11628f83f268SJie Qiu static int mtk_hdmi_clk_enable_audio(struct mtk_hdmi *hdmi) 11638f83f268SJie Qiu { 11648f83f268SJie Qiu int ret; 11658f83f268SJie Qiu 11668f83f268SJie Qiu ret = clk_prepare_enable(hdmi->clk[MTK_HDMI_CLK_AUD_BCLK]); 11678f83f268SJie Qiu if (ret) 11688f83f268SJie Qiu return ret; 11698f83f268SJie Qiu 11708f83f268SJie Qiu ret = clk_prepare_enable(hdmi->clk[MTK_HDMI_CLK_AUD_SPDIF]); 11718f83f268SJie Qiu if (ret) 11728f83f268SJie Qiu goto err; 11738f83f268SJie Qiu 11748f83f268SJie Qiu return 0; 11758f83f268SJie Qiu err: 11768f83f268SJie Qiu clk_disable_unprepare(hdmi->clk[MTK_HDMI_CLK_AUD_BCLK]); 11778f83f268SJie Qiu return ret; 11788f83f268SJie Qiu } 11798f83f268SJie Qiu 11808f83f268SJie Qiu static void mtk_hdmi_clk_disable_audio(struct mtk_hdmi *hdmi) 11818f83f268SJie Qiu { 11828f83f268SJie Qiu clk_disable_unprepare(hdmi->clk[MTK_HDMI_CLK_AUD_BCLK]); 11838f83f268SJie Qiu clk_disable_unprepare(hdmi->clk[MTK_HDMI_CLK_AUD_SPDIF]); 11848f83f268SJie Qiu } 11858f83f268SJie Qiu 11865d3c6447STzung-Bi Shih static enum drm_connector_status 11875d3c6447STzung-Bi Shih mtk_hdmi_update_plugged_status(struct mtk_hdmi *hdmi) 11885d3c6447STzung-Bi Shih { 1189f07980d4STzung-Bi Shih bool connected; 11905d3c6447STzung-Bi Shih 1191f07980d4STzung-Bi Shih mutex_lock(&hdmi->update_plugged_status_lock); 1192f07980d4STzung-Bi Shih connected = mtk_cec_hpd_high(hdmi->cec_dev); 11935d3c6447STzung-Bi Shih if (hdmi->plugged_cb && hdmi->codec_dev) 11945d3c6447STzung-Bi Shih hdmi->plugged_cb(hdmi->codec_dev, connected); 1195f07980d4STzung-Bi Shih mutex_unlock(&hdmi->update_plugged_status_lock); 11965d3c6447STzung-Bi Shih 11975d3c6447STzung-Bi Shih return connected ? 11985d3c6447STzung-Bi Shih connector_status_connected : connector_status_disconnected; 11995d3c6447STzung-Bi Shih } 12005d3c6447STzung-Bi Shih 12012e477391SDafna Hirschfeld static enum drm_connector_status mtk_hdmi_detect(struct mtk_hdmi *hdmi) 12028f83f268SJie Qiu { 12035d3c6447STzung-Bi Shih return mtk_hdmi_update_plugged_status(hdmi); 12048f83f268SJie Qiu } 12058f83f268SJie Qiu 12062e477391SDafna Hirschfeld static int mtk_hdmi_bridge_mode_valid(struct drm_bridge *bridge, 12072e477391SDafna Hirschfeld const struct drm_display_info *info, 12082e477391SDafna Hirschfeld const struct drm_display_mode *mode) 12098f83f268SJie Qiu { 12102e477391SDafna Hirschfeld struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge); 1211fadf872dSBoris Brezillon struct drm_bridge *next_bridge; 12128f83f268SJie Qiu 12138f83f268SJie Qiu dev_dbg(hdmi->dev, "xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n", 12140425662fSVille Syrjälä mode->hdisplay, mode->vdisplay, drm_mode_vrefresh(mode), 12158f83f268SJie Qiu !!(mode->flags & DRM_MODE_FLAG_INTERLACE), mode->clock * 1000); 12168f83f268SJie Qiu 1217fadf872dSBoris Brezillon next_bridge = drm_bridge_get_next_bridge(&hdmi->bridge); 1218fadf872dSBoris Brezillon if (next_bridge) { 12198f83f268SJie Qiu struct drm_display_mode adjusted_mode; 12208f83f268SJie Qiu 12218f83f268SJie Qiu drm_mode_copy(&adjusted_mode, mode); 1222fadf872dSBoris Brezillon if (!drm_bridge_chain_mode_fixup(next_bridge, mode, 12238f83f268SJie Qiu &adjusted_mode)) 12248f83f268SJie Qiu return MODE_BAD; 12258f83f268SJie Qiu } 12268f83f268SJie Qiu 1227*3b8e19a0SAngeloGioacchino Del Regno if (hdmi->conf) { 122841ca9caaSNeil Armstrong if (hdmi->conf->cea_modes_only && !drm_match_cea_mode(mode)) 122941ca9caaSNeil Armstrong return MODE_BAD; 123041ca9caaSNeil Armstrong 1231c91026a9SNeil Armstrong if (hdmi->conf->max_mode_clock && 1232c91026a9SNeil Armstrong mode->clock > hdmi->conf->max_mode_clock) 1233c91026a9SNeil Armstrong return MODE_CLOCK_HIGH; 1234*3b8e19a0SAngeloGioacchino Del Regno } 1235c91026a9SNeil Armstrong 12368f83f268SJie Qiu if (mode->clock < 27000) 12378f83f268SJie Qiu return MODE_CLOCK_LOW; 12388f83f268SJie Qiu if (mode->clock > 297000) 12398f83f268SJie Qiu return MODE_CLOCK_HIGH; 12408f83f268SJie Qiu 12418f83f268SJie Qiu return drm_mode_validate_size(mode, 0x1fff, 0x1fff); 12428f83f268SJie Qiu } 12438f83f268SJie Qiu 12448f83f268SJie Qiu static void mtk_hdmi_hpd_event(bool hpd, struct device *dev) 12458f83f268SJie Qiu { 12468f83f268SJie Qiu struct mtk_hdmi *hdmi = dev_get_drvdata(dev); 12478f83f268SJie Qiu 12482e477391SDafna Hirschfeld if (hdmi && hdmi->bridge.encoder && hdmi->bridge.encoder->dev) { 12492e477391SDafna Hirschfeld static enum drm_connector_status status; 12502e477391SDafna Hirschfeld 12512e477391SDafna Hirschfeld status = mtk_hdmi_detect(hdmi); 12528f83f268SJie Qiu drm_helper_hpd_irq_event(hdmi->bridge.encoder->dev); 12532e477391SDafna Hirschfeld drm_bridge_hpd_notify(&hdmi->bridge, status); 12542e477391SDafna Hirschfeld } 12558f83f268SJie Qiu } 12568f83f268SJie Qiu 12578f83f268SJie Qiu /* 12588f83f268SJie Qiu * Bridge callbacks 12598f83f268SJie Qiu */ 12608f83f268SJie Qiu 12612e477391SDafna Hirschfeld static enum drm_connector_status mtk_hdmi_bridge_detect(struct drm_bridge *bridge) 12622e477391SDafna Hirschfeld { 12632e477391SDafna Hirschfeld struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge); 12642e477391SDafna Hirschfeld 12652e477391SDafna Hirschfeld return mtk_hdmi_detect(hdmi); 12662e477391SDafna Hirschfeld } 12672e477391SDafna Hirschfeld 12682e477391SDafna Hirschfeld static struct edid *mtk_hdmi_bridge_get_edid(struct drm_bridge *bridge, 12692e477391SDafna Hirschfeld struct drm_connector *connector) 12702e477391SDafna Hirschfeld { 12712e477391SDafna Hirschfeld struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge); 12722e477391SDafna Hirschfeld struct edid *edid; 12732e477391SDafna Hirschfeld 12742e477391SDafna Hirschfeld if (!hdmi->ddc_adpt) 12752e477391SDafna Hirschfeld return NULL; 12762e477391SDafna Hirschfeld edid = drm_get_edid(connector, hdmi->ddc_adpt); 12772e477391SDafna Hirschfeld if (!edid) 12782e477391SDafna Hirschfeld return NULL; 12792e477391SDafna Hirschfeld hdmi->dvi_mode = !drm_detect_monitor_audio(edid); 12802e477391SDafna Hirschfeld return edid; 12812e477391SDafna Hirschfeld } 12822e477391SDafna Hirschfeld 1283a25b988fSLaurent Pinchart static int mtk_hdmi_bridge_attach(struct drm_bridge *bridge, 1284a25b988fSLaurent Pinchart enum drm_bridge_attach_flags flags) 12858f83f268SJie Qiu { 12868f83f268SJie Qiu struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge); 12878f83f268SJie Qiu int ret; 12888f83f268SJie Qiu 12892e477391SDafna Hirschfeld if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) { 12902e477391SDafna Hirschfeld DRM_ERROR("%s: The flag DRM_BRIDGE_ATTACH_NO_CONNECTOR must be supplied\n", 12912e477391SDafna Hirschfeld __func__); 1292a25b988fSLaurent Pinchart return -EINVAL; 1293a25b988fSLaurent Pinchart } 1294a25b988fSLaurent Pinchart 12953bb80f24SLaurent Pinchart if (hdmi->next_bridge) { 12963bb80f24SLaurent Pinchart ret = drm_bridge_attach(bridge->encoder, hdmi->next_bridge, 1297a25b988fSLaurent Pinchart bridge, flags); 1298fb8d617fSLaurent Pinchart if (ret) 12998f83f268SJie Qiu return ret; 13008f83f268SJie Qiu } 13018f83f268SJie Qiu 13028f83f268SJie Qiu mtk_cec_set_hpd_event(hdmi->cec_dev, mtk_hdmi_hpd_event, hdmi->dev); 13038f83f268SJie Qiu 13048f83f268SJie Qiu return 0; 13058f83f268SJie Qiu } 13068f83f268SJie Qiu 13078f83f268SJie Qiu static bool mtk_hdmi_bridge_mode_fixup(struct drm_bridge *bridge, 13088f83f268SJie Qiu const struct drm_display_mode *mode, 13098f83f268SJie Qiu struct drm_display_mode *adjusted_mode) 13108f83f268SJie Qiu { 13118f83f268SJie Qiu return true; 13128f83f268SJie Qiu } 13138f83f268SJie Qiu 1314053d231fSDafna Hirschfeld static void mtk_hdmi_bridge_atomic_disable(struct drm_bridge *bridge, 1315053d231fSDafna Hirschfeld struct drm_bridge_state *old_bridge_state) 13168f83f268SJie Qiu { 13178f83f268SJie Qiu struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge); 13188f83f268SJie Qiu 13198f83f268SJie Qiu if (!hdmi->enabled) 13208f83f268SJie Qiu return; 13218f83f268SJie Qiu 13228f83f268SJie Qiu phy_power_off(hdmi->phy); 13238f83f268SJie Qiu clk_disable_unprepare(hdmi->clk[MTK_HDMI_CLK_HDMI_PIXEL]); 13248f83f268SJie Qiu clk_disable_unprepare(hdmi->clk[MTK_HDMI_CLK_HDMI_PLL]); 13258f83f268SJie Qiu 13262e477391SDafna Hirschfeld hdmi->curr_conn = NULL; 13272e477391SDafna Hirschfeld 13288f83f268SJie Qiu hdmi->enabled = false; 13298f83f268SJie Qiu } 13308f83f268SJie Qiu 1331053d231fSDafna Hirschfeld static void mtk_hdmi_bridge_atomic_post_disable(struct drm_bridge *bridge, 1332053d231fSDafna Hirschfeld struct drm_bridge_state *old_state) 13338f83f268SJie Qiu { 13348f83f268SJie Qiu struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge); 13358f83f268SJie Qiu 13368f83f268SJie Qiu if (!hdmi->powered) 13378f83f268SJie Qiu return; 13388f83f268SJie Qiu 13398f83f268SJie Qiu mtk_hdmi_hw_1p4_version_enable(hdmi, true); 13408f83f268SJie Qiu mtk_hdmi_hw_make_reg_writable(hdmi, false); 13418f83f268SJie Qiu 13428f83f268SJie Qiu hdmi->powered = false; 13438f83f268SJie Qiu } 13448f83f268SJie Qiu 13458f83f268SJie Qiu static void mtk_hdmi_bridge_mode_set(struct drm_bridge *bridge, 134663f8f3baSLaurent Pinchart const struct drm_display_mode *mode, 134763f8f3baSLaurent Pinchart const struct drm_display_mode *adjusted_mode) 13488f83f268SJie Qiu { 13498f83f268SJie Qiu struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge); 13508f83f268SJie Qiu 13518f83f268SJie Qiu dev_dbg(hdmi->dev, "cur info: name:%s, hdisplay:%d\n", 13528f83f268SJie Qiu adjusted_mode->name, adjusted_mode->hdisplay); 13538f83f268SJie Qiu dev_dbg(hdmi->dev, "hsync_start:%d,hsync_end:%d, htotal:%d", 13548f83f268SJie Qiu adjusted_mode->hsync_start, adjusted_mode->hsync_end, 13558f83f268SJie Qiu adjusted_mode->htotal); 13568f83f268SJie Qiu dev_dbg(hdmi->dev, "hskew:%d, vdisplay:%d\n", 13578f83f268SJie Qiu adjusted_mode->hskew, adjusted_mode->vdisplay); 13588f83f268SJie Qiu dev_dbg(hdmi->dev, "vsync_start:%d, vsync_end:%d, vtotal:%d", 13598f83f268SJie Qiu adjusted_mode->vsync_start, adjusted_mode->vsync_end, 13608f83f268SJie Qiu adjusted_mode->vtotal); 13618f83f268SJie Qiu dev_dbg(hdmi->dev, "vscan:%d, flag:%d\n", 13628f83f268SJie Qiu adjusted_mode->vscan, adjusted_mode->flags); 13638f83f268SJie Qiu 13648f83f268SJie Qiu drm_mode_copy(&hdmi->mode, adjusted_mode); 13658f83f268SJie Qiu } 13668f83f268SJie Qiu 1367053d231fSDafna Hirschfeld static void mtk_hdmi_bridge_atomic_pre_enable(struct drm_bridge *bridge, 1368053d231fSDafna Hirschfeld struct drm_bridge_state *old_state) 13698f83f268SJie Qiu { 13708f83f268SJie Qiu struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge); 13718f83f268SJie Qiu 13728f83f268SJie Qiu mtk_hdmi_hw_make_reg_writable(hdmi, true); 13738f83f268SJie Qiu mtk_hdmi_hw_1p4_version_enable(hdmi, true); 13748f83f268SJie Qiu 13758f83f268SJie Qiu hdmi->powered = true; 13768f83f268SJie Qiu } 13778f83f268SJie Qiu 1378d542b7c4SJunzhi Zhao static void mtk_hdmi_send_infoframe(struct mtk_hdmi *hdmi, 1379d542b7c4SJunzhi Zhao struct drm_display_mode *mode) 1380d542b7c4SJunzhi Zhao { 1381d542b7c4SJunzhi Zhao mtk_hdmi_setup_audio_infoframe(hdmi); 1382d542b7c4SJunzhi Zhao mtk_hdmi_setup_avi_infoframe(hdmi, mode); 1383d542b7c4SJunzhi Zhao mtk_hdmi_setup_spd_infoframe(hdmi, "mediatek", "On-chip HDMI"); 1384d542b7c4SJunzhi Zhao if (mode->flags & DRM_MODE_FLAG_3D_MASK) 1385d542b7c4SJunzhi Zhao mtk_hdmi_setup_vendor_specific_infoframe(hdmi, mode); 1386d542b7c4SJunzhi Zhao } 1387d542b7c4SJunzhi Zhao 1388053d231fSDafna Hirschfeld static void mtk_hdmi_bridge_atomic_enable(struct drm_bridge *bridge, 1389053d231fSDafna Hirschfeld struct drm_bridge_state *old_state) 13908f83f268SJie Qiu { 13912e477391SDafna Hirschfeld struct drm_atomic_state *state = old_state->base.state; 13928f83f268SJie Qiu struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge); 13938f83f268SJie Qiu 13942e477391SDafna Hirschfeld /* Retrieve the connector through the atomic state. */ 13952e477391SDafna Hirschfeld hdmi->curr_conn = drm_atomic_get_new_connector_for_encoder(state, 13962e477391SDafna Hirschfeld bridge->encoder); 13972e477391SDafna Hirschfeld 13988f83f268SJie Qiu mtk_hdmi_output_set_display_mode(hdmi, &hdmi->mode); 13998f83f268SJie Qiu clk_prepare_enable(hdmi->clk[MTK_HDMI_CLK_HDMI_PLL]); 14008f83f268SJie Qiu clk_prepare_enable(hdmi->clk[MTK_HDMI_CLK_HDMI_PIXEL]); 14018f83f268SJie Qiu phy_power_on(hdmi->phy); 1402d542b7c4SJunzhi Zhao mtk_hdmi_send_infoframe(hdmi, &hdmi->mode); 14038f83f268SJie Qiu 14048f83f268SJie Qiu hdmi->enabled = true; 14058f83f268SJie Qiu } 14068f83f268SJie Qiu 14078f83f268SJie Qiu static const struct drm_bridge_funcs mtk_hdmi_bridge_funcs = { 14082e477391SDafna Hirschfeld .mode_valid = mtk_hdmi_bridge_mode_valid, 1409053d231fSDafna Hirschfeld .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, 1410053d231fSDafna Hirschfeld .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, 1411053d231fSDafna Hirschfeld .atomic_reset = drm_atomic_helper_bridge_reset, 14128f83f268SJie Qiu .attach = mtk_hdmi_bridge_attach, 14138f83f268SJie Qiu .mode_fixup = mtk_hdmi_bridge_mode_fixup, 1414053d231fSDafna Hirschfeld .atomic_disable = mtk_hdmi_bridge_atomic_disable, 1415053d231fSDafna Hirschfeld .atomic_post_disable = mtk_hdmi_bridge_atomic_post_disable, 14168f83f268SJie Qiu .mode_set = mtk_hdmi_bridge_mode_set, 1417053d231fSDafna Hirschfeld .atomic_pre_enable = mtk_hdmi_bridge_atomic_pre_enable, 1418053d231fSDafna Hirschfeld .atomic_enable = mtk_hdmi_bridge_atomic_enable, 14192e477391SDafna Hirschfeld .detect = mtk_hdmi_bridge_detect, 14202e477391SDafna Hirschfeld .get_edid = mtk_hdmi_bridge_get_edid, 14218f83f268SJie Qiu }; 14228f83f268SJie Qiu 14238f83f268SJie Qiu static int mtk_hdmi_dt_parse_pdata(struct mtk_hdmi *hdmi, 14248f83f268SJie Qiu struct platform_device *pdev) 14258f83f268SJie Qiu { 14268f83f268SJie Qiu struct device *dev = &pdev->dev; 14278f83f268SJie Qiu struct device_node *np = dev->of_node; 142886418f90SRob Herring struct device_node *cec_np, *remote, *i2c_np; 14298f83f268SJie Qiu struct platform_device *cec_pdev; 14308f83f268SJie Qiu struct regmap *regmap; 14318f83f268SJie Qiu struct resource *mem; 14328f83f268SJie Qiu int ret; 14338f83f268SJie Qiu 14348f83f268SJie Qiu ret = mtk_hdmi_get_all_clk(hdmi, np); 14358f83f268SJie Qiu if (ret) { 1436af19d645SMatthias Brugger if (ret != -EPROBE_DEFER) 14378f83f268SJie Qiu dev_err(dev, "Failed to get clocks: %d\n", ret); 1438af19d645SMatthias Brugger 14398f83f268SJie Qiu return ret; 14408f83f268SJie Qiu } 14418f83f268SJie Qiu 14428f83f268SJie Qiu /* The CEC module handles HDMI hotplug detection */ 1443ceff2f4dSJohan Hovold cec_np = of_get_compatible_child(np->parent, "mediatek,mt8173-cec"); 14448f83f268SJie Qiu if (!cec_np) { 14458f83f268SJie Qiu dev_err(dev, "Failed to find CEC node\n"); 14468f83f268SJie Qiu return -EINVAL; 14478f83f268SJie Qiu } 14488f83f268SJie Qiu 14498f83f268SJie Qiu cec_pdev = of_find_device_by_node(cec_np); 14508f83f268SJie Qiu if (!cec_pdev) { 14514bf99144SRob Herring dev_err(hdmi->dev, "Waiting for CEC device %pOF\n", 14524bf99144SRob Herring cec_np); 1453ceff2f4dSJohan Hovold of_node_put(cec_np); 14548f83f268SJie Qiu return -EPROBE_DEFER; 14558f83f268SJie Qiu } 1456ceff2f4dSJohan Hovold of_node_put(cec_np); 14578f83f268SJie Qiu hdmi->cec_dev = &cec_pdev->dev; 14588f83f268SJie Qiu 14598f83f268SJie Qiu /* 14608f83f268SJie Qiu * The mediatek,syscon-hdmi property contains a phandle link to the 14618f83f268SJie Qiu * MMSYS_CONFIG device and the register offset of the HDMI_SYS_CFG 14628f83f268SJie Qiu * registers it contains. 14638f83f268SJie Qiu */ 14648f83f268SJie Qiu regmap = syscon_regmap_lookup_by_phandle(np, "mediatek,syscon-hdmi"); 14658f83f268SJie Qiu ret = of_property_read_u32_index(np, "mediatek,syscon-hdmi", 1, 14668f83f268SJie Qiu &hdmi->sys_offset); 14678f83f268SJie Qiu if (IS_ERR(regmap)) 14688f83f268SJie Qiu ret = PTR_ERR(regmap); 14698f83f268SJie Qiu if (ret) { 14708f83f268SJie Qiu dev_err(dev, 14718f83f268SJie Qiu "Failed to get system configuration registers: %d\n", 14728f83f268SJie Qiu ret); 14730680a622SYu Kuai goto put_device; 14748f83f268SJie Qiu } 14758f83f268SJie Qiu hdmi->sys_regmap = regmap; 14768f83f268SJie Qiu 14778f83f268SJie Qiu mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 14788f83f268SJie Qiu hdmi->regs = devm_ioremap_resource(dev, mem); 14790680a622SYu Kuai if (IS_ERR(hdmi->regs)) { 14800680a622SYu Kuai ret = PTR_ERR(hdmi->regs); 14810680a622SYu Kuai goto put_device; 14820680a622SYu Kuai } 14838f83f268SJie Qiu 148486418f90SRob Herring remote = of_graph_get_remote_node(np, 1, 0); 14850680a622SYu Kuai if (!remote) { 14860680a622SYu Kuai ret = -EINVAL; 14870680a622SYu Kuai goto put_device; 14880680a622SYu Kuai } 14898f83f268SJie Qiu 14908f83f268SJie Qiu if (!of_device_is_compatible(remote, "hdmi-connector")) { 14913bb80f24SLaurent Pinchart hdmi->next_bridge = of_drm_find_bridge(remote); 14923bb80f24SLaurent Pinchart if (!hdmi->next_bridge) { 14938f83f268SJie Qiu dev_err(dev, "Waiting for external bridge\n"); 14948f83f268SJie Qiu of_node_put(remote); 14950680a622SYu Kuai ret = -EPROBE_DEFER; 14960680a622SYu Kuai goto put_device; 14978f83f268SJie Qiu } 14988f83f268SJie Qiu } 14998f83f268SJie Qiu 15008f83f268SJie Qiu i2c_np = of_parse_phandle(remote, "ddc-i2c-bus", 0); 15018f83f268SJie Qiu if (!i2c_np) { 15024bf99144SRob Herring dev_err(dev, "Failed to find ddc-i2c-bus node in %pOF\n", 15034bf99144SRob Herring remote); 15048f83f268SJie Qiu of_node_put(remote); 15050680a622SYu Kuai ret = -EINVAL; 15060680a622SYu Kuai goto put_device; 15078f83f268SJie Qiu } 15088f83f268SJie Qiu of_node_put(remote); 15098f83f268SJie Qiu 15108f83f268SJie Qiu hdmi->ddc_adpt = of_find_i2c_adapter_by_node(i2c_np); 15112ae2c331SWen Yang of_node_put(i2c_np); 15128f83f268SJie Qiu if (!hdmi->ddc_adpt) { 15138f83f268SJie Qiu dev_err(dev, "Failed to get ddc i2c adapter by node\n"); 15140680a622SYu Kuai ret = -EINVAL; 15150680a622SYu Kuai goto put_device; 15168f83f268SJie Qiu } 15178f83f268SJie Qiu 15188f83f268SJie Qiu return 0; 15190680a622SYu Kuai put_device: 15200680a622SYu Kuai put_device(hdmi->cec_dev); 15210680a622SYu Kuai return ret; 15228f83f268SJie Qiu } 15238f83f268SJie Qiu 15248f83f268SJie Qiu /* 15258f83f268SJie Qiu * HDMI audio codec callbacks 15268f83f268SJie Qiu */ 15278f83f268SJie Qiu 15285dd0775eSDave Airlie static int mtk_hdmi_audio_hw_params(struct device *dev, void *data, 15298f83f268SJie Qiu struct hdmi_codec_daifmt *daifmt, 15308f83f268SJie Qiu struct hdmi_codec_params *params) 15318f83f268SJie Qiu { 15328f83f268SJie Qiu struct mtk_hdmi *hdmi = dev_get_drvdata(dev); 15338f83f268SJie Qiu struct hdmi_audio_param hdmi_params; 15348f83f268SJie Qiu unsigned int chan = params->cea.channels; 15358f83f268SJie Qiu 15368f83f268SJie Qiu dev_dbg(hdmi->dev, "%s: %u Hz, %d bit, %d channels\n", __func__, 15378f83f268SJie Qiu params->sample_rate, params->sample_width, chan); 15388f83f268SJie Qiu 15398f83f268SJie Qiu if (!hdmi->bridge.encoder) 15408f83f268SJie Qiu return -ENODEV; 15418f83f268SJie Qiu 15428f83f268SJie Qiu switch (chan) { 15438f83f268SJie Qiu case 2: 15448f83f268SJie Qiu hdmi_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_2_0; 15458f83f268SJie Qiu break; 15468f83f268SJie Qiu case 4: 15478f83f268SJie Qiu hdmi_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_4_0; 15488f83f268SJie Qiu break; 15498f83f268SJie Qiu case 6: 15508f83f268SJie Qiu hdmi_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_5_1; 15518f83f268SJie Qiu break; 15528f83f268SJie Qiu case 8: 15538f83f268SJie Qiu hdmi_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_7_1; 15548f83f268SJie Qiu break; 15558f83f268SJie Qiu default: 15568f83f268SJie Qiu dev_err(hdmi->dev, "channel[%d] not supported!\n", chan); 15578f83f268SJie Qiu return -EINVAL; 15588f83f268SJie Qiu } 15598f83f268SJie Qiu 15608f83f268SJie Qiu switch (params->sample_rate) { 15618f83f268SJie Qiu case 32000: 15628f83f268SJie Qiu case 44100: 15638f83f268SJie Qiu case 48000: 15648f83f268SJie Qiu case 88200: 15658f83f268SJie Qiu case 96000: 15668f83f268SJie Qiu case 176400: 15678f83f268SJie Qiu case 192000: 15688f83f268SJie Qiu break; 15698f83f268SJie Qiu default: 15708f83f268SJie Qiu dev_err(hdmi->dev, "rate[%d] not supported!\n", 15718f83f268SJie Qiu params->sample_rate); 15728f83f268SJie Qiu return -EINVAL; 15738f83f268SJie Qiu } 15748f83f268SJie Qiu 15758f83f268SJie Qiu switch (daifmt->fmt) { 15768f83f268SJie Qiu case HDMI_I2S: 15778f83f268SJie Qiu hdmi_params.aud_codec = HDMI_AUDIO_CODING_TYPE_PCM; 15788f83f268SJie Qiu hdmi_params.aud_sampe_size = HDMI_AUDIO_SAMPLE_SIZE_16; 15798f83f268SJie Qiu hdmi_params.aud_input_type = HDMI_AUD_INPUT_I2S; 15808f83f268SJie Qiu hdmi_params.aud_i2s_fmt = HDMI_I2S_MODE_I2S_24BIT; 15818f83f268SJie Qiu hdmi_params.aud_mclk = HDMI_AUD_MCLK_128FS; 15828f83f268SJie Qiu break; 1583d1ef028dSchunhui dai case HDMI_SPDIF: 1584d1ef028dSchunhui dai hdmi_params.aud_codec = HDMI_AUDIO_CODING_TYPE_PCM; 1585d1ef028dSchunhui dai hdmi_params.aud_sampe_size = HDMI_AUDIO_SAMPLE_SIZE_16; 1586d1ef028dSchunhui dai hdmi_params.aud_input_type = HDMI_AUD_INPUT_SPDIF; 1587d1ef028dSchunhui dai break; 15888f83f268SJie Qiu default: 15898f83f268SJie Qiu dev_err(hdmi->dev, "%s: Invalid DAI format %d\n", __func__, 15908f83f268SJie Qiu daifmt->fmt); 15918f83f268SJie Qiu return -EINVAL; 15928f83f268SJie Qiu } 15938f83f268SJie Qiu 15948f83f268SJie Qiu memcpy(&hdmi_params.codec_params, params, 15958f83f268SJie Qiu sizeof(hdmi_params.codec_params)); 15968f83f268SJie Qiu 15978f83f268SJie Qiu mtk_hdmi_audio_set_param(hdmi, &hdmi_params); 15988f83f268SJie Qiu 15998f83f268SJie Qiu return 0; 16008f83f268SJie Qiu } 16018f83f268SJie Qiu 16025dd0775eSDave Airlie static int mtk_hdmi_audio_startup(struct device *dev, void *data) 16038f83f268SJie Qiu { 16048f83f268SJie Qiu struct mtk_hdmi *hdmi = dev_get_drvdata(dev); 16058f83f268SJie Qiu 16068f83f268SJie Qiu mtk_hdmi_audio_enable(hdmi); 16078f83f268SJie Qiu 16088f83f268SJie Qiu return 0; 16098f83f268SJie Qiu } 16108f83f268SJie Qiu 16115dd0775eSDave Airlie static void mtk_hdmi_audio_shutdown(struct device *dev, void *data) 16128f83f268SJie Qiu { 16138f83f268SJie Qiu struct mtk_hdmi *hdmi = dev_get_drvdata(dev); 16148f83f268SJie Qiu 16158f83f268SJie Qiu mtk_hdmi_audio_disable(hdmi); 16168f83f268SJie Qiu } 16178f83f268SJie Qiu 1618188af070SBaoyou Xie static int 1619d789710fSKuninori Morimoto mtk_hdmi_audio_mute(struct device *dev, void *data, 1620d789710fSKuninori Morimoto bool enable, int direction) 16218f83f268SJie Qiu { 16228f83f268SJie Qiu struct mtk_hdmi *hdmi = dev_get_drvdata(dev); 16238f83f268SJie Qiu 16248f83f268SJie Qiu if (enable) 16258f83f268SJie Qiu mtk_hdmi_hw_aud_mute(hdmi); 16268f83f268SJie Qiu else 16278f83f268SJie Qiu mtk_hdmi_hw_aud_unmute(hdmi); 16288f83f268SJie Qiu 16298f83f268SJie Qiu return 0; 16308f83f268SJie Qiu } 16318f83f268SJie Qiu 16325dd0775eSDave Airlie static int mtk_hdmi_audio_get_eld(struct device *dev, void *data, uint8_t *buf, size_t len) 16338f83f268SJie Qiu { 16348f83f268SJie Qiu struct mtk_hdmi *hdmi = dev_get_drvdata(dev); 16358f83f268SJie Qiu 16362e477391SDafna Hirschfeld if (hdmi->enabled) 16372e477391SDafna Hirschfeld memcpy(buf, hdmi->curr_conn->eld, min(sizeof(hdmi->curr_conn->eld), len)); 16382e477391SDafna Hirschfeld else 16392e477391SDafna Hirschfeld memset(buf, 0, len); 16408f83f268SJie Qiu return 0; 16418f83f268SJie Qiu } 16428f83f268SJie Qiu 16435d3c6447STzung-Bi Shih static int mtk_hdmi_audio_hook_plugged_cb(struct device *dev, void *data, 16445d3c6447STzung-Bi Shih hdmi_codec_plugged_cb fn, 16455d3c6447STzung-Bi Shih struct device *codec_dev) 16465d3c6447STzung-Bi Shih { 16475d3c6447STzung-Bi Shih struct mtk_hdmi *hdmi = data; 16485d3c6447STzung-Bi Shih 1649f07980d4STzung-Bi Shih mutex_lock(&hdmi->update_plugged_status_lock); 16505d3c6447STzung-Bi Shih hdmi->plugged_cb = fn; 16515d3c6447STzung-Bi Shih hdmi->codec_dev = codec_dev; 1652f07980d4STzung-Bi Shih mutex_unlock(&hdmi->update_plugged_status_lock); 1653f07980d4STzung-Bi Shih 16545d3c6447STzung-Bi Shih mtk_hdmi_update_plugged_status(hdmi); 16555d3c6447STzung-Bi Shih 16565d3c6447STzung-Bi Shih return 0; 16575d3c6447STzung-Bi Shih } 16585d3c6447STzung-Bi Shih 16598f83f268SJie Qiu static const struct hdmi_codec_ops mtk_hdmi_audio_codec_ops = { 16608f83f268SJie Qiu .hw_params = mtk_hdmi_audio_hw_params, 16618f83f268SJie Qiu .audio_startup = mtk_hdmi_audio_startup, 16628f83f268SJie Qiu .audio_shutdown = mtk_hdmi_audio_shutdown, 1663d789710fSKuninori Morimoto .mute_stream = mtk_hdmi_audio_mute, 16648f83f268SJie Qiu .get_eld = mtk_hdmi_audio_get_eld, 16655d3c6447STzung-Bi Shih .hook_plugged_cb = mtk_hdmi_audio_hook_plugged_cb, 1666d789710fSKuninori Morimoto .no_capture_mute = 1, 16678f83f268SJie Qiu }; 16688f83f268SJie Qiu 1669f9eb06cdSTzung-Bi Shih static int mtk_hdmi_register_audio_driver(struct device *dev) 16708f83f268SJie Qiu { 16715d3c6447STzung-Bi Shih struct mtk_hdmi *hdmi = dev_get_drvdata(dev); 16728f83f268SJie Qiu struct hdmi_codec_pdata codec_data = { 16738f83f268SJie Qiu .ops = &mtk_hdmi_audio_codec_ops, 16748f83f268SJie Qiu .max_i2s_channels = 2, 16758f83f268SJie Qiu .i2s = 1, 16765d3c6447STzung-Bi Shih .data = hdmi, 16778f83f268SJie Qiu }; 16788f83f268SJie Qiu struct platform_device *pdev; 16798f83f268SJie Qiu 16808f83f268SJie Qiu pdev = platform_device_register_data(dev, HDMI_CODEC_DRV_NAME, 16818f83f268SJie Qiu PLATFORM_DEVID_AUTO, &codec_data, 16828f83f268SJie Qiu sizeof(codec_data)); 16838f83f268SJie Qiu if (IS_ERR(pdev)) 1684f9eb06cdSTzung-Bi Shih return PTR_ERR(pdev); 16858f83f268SJie Qiu 16868f83f268SJie Qiu DRM_INFO("%s driver bound to HDMI\n", HDMI_CODEC_DRV_NAME); 1687f9eb06cdSTzung-Bi Shih return 0; 16888f83f268SJie Qiu } 16898f83f268SJie Qiu 16908f83f268SJie Qiu static int mtk_drm_hdmi_probe(struct platform_device *pdev) 16918f83f268SJie Qiu { 16928f83f268SJie Qiu struct mtk_hdmi *hdmi; 16938f83f268SJie Qiu struct device *dev = &pdev->dev; 16948f83f268SJie Qiu int ret; 16958f83f268SJie Qiu 16968f83f268SJie Qiu hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); 16978f83f268SJie Qiu if (!hdmi) 16988f83f268SJie Qiu return -ENOMEM; 16998f83f268SJie Qiu 17008f83f268SJie Qiu hdmi->dev = dev; 170196f51a4dSCK Hu hdmi->conf = of_device_get_match_data(dev); 17028f83f268SJie Qiu 17038f83f268SJie Qiu ret = mtk_hdmi_dt_parse_pdata(hdmi, pdev); 17048f83f268SJie Qiu if (ret) 17058f83f268SJie Qiu return ret; 17068f83f268SJie Qiu 17078f83f268SJie Qiu hdmi->phy = devm_phy_get(dev, "hdmi"); 17088f83f268SJie Qiu if (IS_ERR(hdmi->phy)) { 17098f83f268SJie Qiu ret = PTR_ERR(hdmi->phy); 17108f83f268SJie Qiu dev_err(dev, "Failed to get HDMI PHY: %d\n", ret); 17118f83f268SJie Qiu return ret; 17128f83f268SJie Qiu } 17138f83f268SJie Qiu 1714f07980d4STzung-Bi Shih mutex_init(&hdmi->update_plugged_status_lock); 17158f83f268SJie Qiu platform_set_drvdata(pdev, hdmi); 17168f83f268SJie Qiu 17178f83f268SJie Qiu ret = mtk_hdmi_output_init(hdmi); 17188f83f268SJie Qiu if (ret) { 17198f83f268SJie Qiu dev_err(dev, "Failed to initialize hdmi output\n"); 17208f83f268SJie Qiu return ret; 17218f83f268SJie Qiu } 17228f83f268SJie Qiu 1723f9eb06cdSTzung-Bi Shih ret = mtk_hdmi_register_audio_driver(dev); 1724f9eb06cdSTzung-Bi Shih if (ret) { 1725f9eb06cdSTzung-Bi Shih dev_err(dev, "Failed to register audio driver: %d\n", ret); 1726f9eb06cdSTzung-Bi Shih return ret; 1727f9eb06cdSTzung-Bi Shih } 17288f83f268SJie Qiu 17298f83f268SJie Qiu hdmi->bridge.funcs = &mtk_hdmi_bridge_funcs; 17308f83f268SJie Qiu hdmi->bridge.of_node = pdev->dev.of_node; 17312e477391SDafna Hirschfeld hdmi->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID 17322e477391SDafna Hirschfeld | DRM_BRIDGE_OP_HPD; 17332e477391SDafna Hirschfeld hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA; 1734e6910091SInki Dae drm_bridge_add(&hdmi->bridge); 17358f83f268SJie Qiu 17368f83f268SJie Qiu ret = mtk_hdmi_clk_enable_audio(hdmi); 17378f83f268SJie Qiu if (ret) { 17388f83f268SJie Qiu dev_err(dev, "Failed to enable audio clocks: %d\n", ret); 17398f83f268SJie Qiu goto err_bridge_remove; 17408f83f268SJie Qiu } 17418f83f268SJie Qiu 17428f83f268SJie Qiu return 0; 17438f83f268SJie Qiu 17448f83f268SJie Qiu err_bridge_remove: 17458f83f268SJie Qiu drm_bridge_remove(&hdmi->bridge); 17468f83f268SJie Qiu return ret; 17478f83f268SJie Qiu } 17488f83f268SJie Qiu 17498f83f268SJie Qiu static int mtk_drm_hdmi_remove(struct platform_device *pdev) 17508f83f268SJie Qiu { 17518f83f268SJie Qiu struct mtk_hdmi *hdmi = platform_get_drvdata(pdev); 17528f83f268SJie Qiu 17538f83f268SJie Qiu drm_bridge_remove(&hdmi->bridge); 17548f83f268SJie Qiu mtk_hdmi_clk_disable_audio(hdmi); 17558f83f268SJie Qiu return 0; 17568f83f268SJie Qiu } 17578f83f268SJie Qiu 17588f83f268SJie Qiu #ifdef CONFIG_PM_SLEEP 17598f83f268SJie Qiu static int mtk_hdmi_suspend(struct device *dev) 17608f83f268SJie Qiu { 17618f83f268SJie Qiu struct mtk_hdmi *hdmi = dev_get_drvdata(dev); 17628f83f268SJie Qiu 17638f83f268SJie Qiu mtk_hdmi_clk_disable_audio(hdmi); 17645ab546f5SEnric Balletbo i Serra 17658f83f268SJie Qiu return 0; 17668f83f268SJie Qiu } 17678f83f268SJie Qiu 17688f83f268SJie Qiu static int mtk_hdmi_resume(struct device *dev) 17698f83f268SJie Qiu { 17708f83f268SJie Qiu struct mtk_hdmi *hdmi = dev_get_drvdata(dev); 17718f83f268SJie Qiu int ret = 0; 17728f83f268SJie Qiu 17738f83f268SJie Qiu ret = mtk_hdmi_clk_enable_audio(hdmi); 17748f83f268SJie Qiu if (ret) { 17758f83f268SJie Qiu dev_err(dev, "hdmi resume failed!\n"); 17768f83f268SJie Qiu return ret; 17778f83f268SJie Qiu } 17788f83f268SJie Qiu 17798f83f268SJie Qiu return 0; 17808f83f268SJie Qiu } 17818f83f268SJie Qiu #endif 17828f83f268SJie Qiu static SIMPLE_DEV_PM_OPS(mtk_hdmi_pm_ops, 17838f83f268SJie Qiu mtk_hdmi_suspend, mtk_hdmi_resume); 17848f83f268SJie Qiu 178596f51a4dSCK Hu static const struct mtk_hdmi_conf mtk_hdmi_conf_mt2701 = { 178696f51a4dSCK Hu .tz_disabled = true, 178796f51a4dSCK Hu }; 178896f51a4dSCK Hu 1789511cf7d1SNeil Armstrong static const struct mtk_hdmi_conf mtk_hdmi_conf_mt8167 = { 1790511cf7d1SNeil Armstrong .max_mode_clock = 148500, 1791511cf7d1SNeil Armstrong .cea_modes_only = true, 1792511cf7d1SNeil Armstrong }; 1793511cf7d1SNeil Armstrong 17948f83f268SJie Qiu static const struct of_device_id mtk_drm_hdmi_of_ids[] = { 179596f51a4dSCK Hu { .compatible = "mediatek,mt2701-hdmi", 179696f51a4dSCK Hu .data = &mtk_hdmi_conf_mt2701, 179796f51a4dSCK Hu }, 1798511cf7d1SNeil Armstrong { .compatible = "mediatek,mt8167-hdmi", 1799511cf7d1SNeil Armstrong .data = &mtk_hdmi_conf_mt8167, 1800511cf7d1SNeil Armstrong }, 180196f51a4dSCK Hu { .compatible = "mediatek,mt8173-hdmi", 180296f51a4dSCK Hu }, 18038f83f268SJie Qiu {} 18048f83f268SJie Qiu }; 1805fdcbe17cSBoris Brezillon MODULE_DEVICE_TABLE(of, mtk_drm_hdmi_of_ids); 18068f83f268SJie Qiu 18078f83f268SJie Qiu static struct platform_driver mtk_hdmi_driver = { 18088f83f268SJie Qiu .probe = mtk_drm_hdmi_probe, 18098f83f268SJie Qiu .remove = mtk_drm_hdmi_remove, 18108f83f268SJie Qiu .driver = { 18118f83f268SJie Qiu .name = "mediatek-drm-hdmi", 18128f83f268SJie Qiu .of_match_table = mtk_drm_hdmi_of_ids, 18138f83f268SJie Qiu .pm = &mtk_hdmi_pm_ops, 18148f83f268SJie Qiu }, 18158f83f268SJie Qiu }; 18168f83f268SJie Qiu 18178f83f268SJie Qiu static struct platform_driver * const mtk_hdmi_drivers[] = { 18188f83f268SJie Qiu &mtk_hdmi_ddc_driver, 18198f83f268SJie Qiu &mtk_cec_driver, 18208f83f268SJie Qiu &mtk_hdmi_driver, 18218f83f268SJie Qiu }; 18228f83f268SJie Qiu 18238f83f268SJie Qiu static int __init mtk_hdmitx_init(void) 18248f83f268SJie Qiu { 1825446b8c54SPhilipp Zabel return platform_register_drivers(mtk_hdmi_drivers, 1826446b8c54SPhilipp Zabel ARRAY_SIZE(mtk_hdmi_drivers)); 18278f83f268SJie Qiu } 18288f83f268SJie Qiu 18298f83f268SJie Qiu static void __exit mtk_hdmitx_exit(void) 18308f83f268SJie Qiu { 1831446b8c54SPhilipp Zabel platform_unregister_drivers(mtk_hdmi_drivers, 1832446b8c54SPhilipp Zabel ARRAY_SIZE(mtk_hdmi_drivers)); 18338f83f268SJie Qiu } 18348f83f268SJie Qiu 18358f83f268SJie Qiu module_init(mtk_hdmitx_init); 18368f83f268SJie Qiu module_exit(mtk_hdmitx_exit); 18378f83f268SJie Qiu 18388f83f268SJie Qiu MODULE_AUTHOR("Jie Qiu <jie.qiu@mediatek.com>"); 18398f83f268SJie Qiu MODULE_DESCRIPTION("MediaTek HDMI Driver"); 18408f83f268SJie Qiu MODULE_LICENSE("GPL v2"); 1841