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> 15f07980d4STzung-Bi Shih #include <linux/mutex.h> 168f83f268SJie Qiu #include <linux/of_platform.h> 178f83f268SJie Qiu #include <linux/of.h> 188f83f268SJie Qiu #include <linux/of_gpio.h> 198f83f268SJie Qiu #include <linux/of_graph.h> 208f83f268SJie Qiu #include <linux/phy/phy.h> 218f83f268SJie Qiu #include <linux/platform_device.h> 228f83f268SJie Qiu #include <linux/regmap.h> 239aef5867SSam Ravnborg 248f83f268SJie Qiu #include <sound/hdmi-codec.h> 259aef5867SSam Ravnborg 269aef5867SSam Ravnborg #include <drm/drm_atomic_helper.h> 27ee68c743SBoris Brezillon #include <drm/drm_bridge.h> 289aef5867SSam Ravnborg #include <drm/drm_crtc.h> 299aef5867SSam Ravnborg #include <drm/drm_edid.h> 309aef5867SSam Ravnborg #include <drm/drm_print.h> 319aef5867SSam Ravnborg #include <drm/drm_probe_helper.h> 329aef5867SSam Ravnborg 338f83f268SJie Qiu #include "mtk_cec.h" 348f83f268SJie Qiu #include "mtk_hdmi.h" 358f83f268SJie Qiu #include "mtk_hdmi_regs.h" 368f83f268SJie Qiu 378f83f268SJie Qiu #define NCTS_BYTES 7 388f83f268SJie Qiu 398f83f268SJie Qiu enum mtk_hdmi_clk_id { 408f83f268SJie Qiu MTK_HDMI_CLK_HDMI_PIXEL, 418f83f268SJie Qiu MTK_HDMI_CLK_HDMI_PLL, 428f83f268SJie Qiu MTK_HDMI_CLK_AUD_BCLK, 438f83f268SJie Qiu MTK_HDMI_CLK_AUD_SPDIF, 448f83f268SJie Qiu MTK_HDMI_CLK_COUNT 458f83f268SJie Qiu }; 468f83f268SJie Qiu 478f83f268SJie Qiu enum hdmi_aud_input_type { 488f83f268SJie Qiu HDMI_AUD_INPUT_I2S = 0, 498f83f268SJie Qiu HDMI_AUD_INPUT_SPDIF, 508f83f268SJie Qiu }; 518f83f268SJie Qiu 528f83f268SJie Qiu enum hdmi_aud_i2s_fmt { 538f83f268SJie Qiu HDMI_I2S_MODE_RJT_24BIT = 0, 548f83f268SJie Qiu HDMI_I2S_MODE_RJT_16BIT, 558f83f268SJie Qiu HDMI_I2S_MODE_LJT_24BIT, 568f83f268SJie Qiu HDMI_I2S_MODE_LJT_16BIT, 578f83f268SJie Qiu HDMI_I2S_MODE_I2S_24BIT, 588f83f268SJie Qiu HDMI_I2S_MODE_I2S_16BIT 598f83f268SJie Qiu }; 608f83f268SJie Qiu 618f83f268SJie Qiu enum hdmi_aud_mclk { 628f83f268SJie Qiu HDMI_AUD_MCLK_128FS, 638f83f268SJie Qiu HDMI_AUD_MCLK_192FS, 648f83f268SJie Qiu HDMI_AUD_MCLK_256FS, 658f83f268SJie Qiu HDMI_AUD_MCLK_384FS, 668f83f268SJie Qiu HDMI_AUD_MCLK_512FS, 678f83f268SJie Qiu HDMI_AUD_MCLK_768FS, 688f83f268SJie Qiu HDMI_AUD_MCLK_1152FS, 698f83f268SJie Qiu }; 708f83f268SJie Qiu 718f83f268SJie Qiu enum hdmi_aud_channel_type { 728f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_1_0 = 0, 738f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_1_1, 748f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_2_0, 758f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_2_1, 768f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_3_0, 778f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_3_1, 788f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_4_0, 798f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_4_1, 808f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_5_0, 818f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_5_1, 828f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_6_0, 838f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_6_1, 848f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_0, 858f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_1, 868f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_3_0_LRS, 878f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_3_1_LRS, 888f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_4_0_CLRS, 898f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_4_1_CLRS, 908f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_6_1_CS, 918f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_6_1_CH, 928f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_6_1_OH, 938f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_6_1_CHR, 948f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_1_LH_RH, 958f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_1_LSR_RSR, 968f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_1_LC_RC, 978f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_1_LW_RW, 988f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_1_LSD_RSD, 998f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_1_LSS_RSS, 1008f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_1_LHS_RHS, 1018f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_1_CS_CH, 1028f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_1_CS_OH, 1038f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_1_CS_CHR, 1048f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_1_CH_OH, 1058f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_1_CH_CHR, 1068f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_1_OH_CHR, 1078f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_1_LSS_RSS_LSR_RSR, 1088f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_6_0_CS, 1098f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_6_0_CH, 1108f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_6_0_OH, 1118f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_6_0_CHR, 1128f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_0_LH_RH, 1138f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_0_LSR_RSR, 1148f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_0_LC_RC, 1158f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_0_LW_RW, 1168f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_0_LSD_RSD, 1178f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_0_LSS_RSS, 1188f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_0_LHS_RHS, 1198f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_0_CS_CH, 1208f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_0_CS_OH, 1218f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_0_CS_CHR, 1228f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_0_CH_OH, 1238f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_0_CH_CHR, 1248f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_0_OH_CHR, 1258f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_7_0_LSS_RSS_LSR_RSR, 1268f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_8_0_LH_RH_CS, 1278f83f268SJie Qiu HDMI_AUD_CHAN_TYPE_UNKNOWN = 0xFF 1288f83f268SJie Qiu }; 1298f83f268SJie Qiu 1308f83f268SJie Qiu enum hdmi_aud_channel_swap_type { 1318f83f268SJie Qiu HDMI_AUD_SWAP_LR, 1328f83f268SJie Qiu HDMI_AUD_SWAP_LFE_CC, 1338f83f268SJie Qiu HDMI_AUD_SWAP_LSRS, 1348f83f268SJie Qiu HDMI_AUD_SWAP_RLS_RRS, 1358f83f268SJie Qiu HDMI_AUD_SWAP_LR_STATUS, 1368f83f268SJie Qiu }; 1378f83f268SJie Qiu 1388f83f268SJie Qiu struct hdmi_audio_param { 1398f83f268SJie Qiu enum hdmi_audio_coding_type aud_codec; 1408f83f268SJie Qiu enum hdmi_audio_sample_size aud_sampe_size; 1418f83f268SJie Qiu enum hdmi_aud_input_type aud_input_type; 1428f83f268SJie Qiu enum hdmi_aud_i2s_fmt aud_i2s_fmt; 1438f83f268SJie Qiu enum hdmi_aud_mclk aud_mclk; 1448f83f268SJie Qiu enum hdmi_aud_channel_type aud_input_chan_type; 1458f83f268SJie Qiu struct hdmi_codec_params codec_params; 1468f83f268SJie Qiu }; 1478f83f268SJie Qiu 1488f83f268SJie Qiu struct mtk_hdmi { 1498f83f268SJie Qiu struct drm_bridge bridge; 1503bb80f24SLaurent Pinchart struct drm_bridge *next_bridge; 1518f83f268SJie Qiu struct drm_connector conn; 1528f83f268SJie Qiu struct device *dev; 1538f83f268SJie Qiu struct phy *phy; 1548f83f268SJie Qiu struct device *cec_dev; 1558f83f268SJie Qiu struct i2c_adapter *ddc_adpt; 1568f83f268SJie Qiu struct clk *clk[MTK_HDMI_CLK_COUNT]; 1578f83f268SJie Qiu struct drm_display_mode mode; 1588f83f268SJie Qiu bool dvi_mode; 1598f83f268SJie Qiu u32 min_clock; 1608f83f268SJie Qiu u32 max_clock; 1618f83f268SJie Qiu u32 max_hdisplay; 1628f83f268SJie Qiu u32 max_vdisplay; 1638f83f268SJie Qiu u32 ibias; 1648f83f268SJie Qiu u32 ibias_up; 1658f83f268SJie Qiu struct regmap *sys_regmap; 1668f83f268SJie Qiu unsigned int sys_offset; 1678f83f268SJie Qiu void __iomem *regs; 1688f83f268SJie Qiu enum hdmi_colorspace csp; 1698f83f268SJie Qiu struct hdmi_audio_param aud_param; 1708f83f268SJie Qiu bool audio_enable; 1718f83f268SJie Qiu bool powered; 1728f83f268SJie Qiu bool enabled; 1735d3c6447STzung-Bi Shih hdmi_codec_plugged_cb plugged_cb; 1745d3c6447STzung-Bi Shih struct device *codec_dev; 175f07980d4STzung-Bi Shih struct mutex update_plugged_status_lock; 1768f83f268SJie Qiu }; 1778f83f268SJie Qiu 1788f83f268SJie Qiu static inline struct mtk_hdmi *hdmi_ctx_from_bridge(struct drm_bridge *b) 1798f83f268SJie Qiu { 1808f83f268SJie Qiu return container_of(b, struct mtk_hdmi, bridge); 1818f83f268SJie Qiu } 1828f83f268SJie Qiu 1838f83f268SJie Qiu static inline struct mtk_hdmi *hdmi_ctx_from_conn(struct drm_connector *c) 1848f83f268SJie Qiu { 1858f83f268SJie Qiu return container_of(c, struct mtk_hdmi, conn); 1868f83f268SJie Qiu } 1878f83f268SJie Qiu 1888f83f268SJie Qiu static u32 mtk_hdmi_read(struct mtk_hdmi *hdmi, u32 offset) 1898f83f268SJie Qiu { 1908f83f268SJie Qiu return readl(hdmi->regs + offset); 1918f83f268SJie Qiu } 1928f83f268SJie Qiu 1938f83f268SJie Qiu static void mtk_hdmi_write(struct mtk_hdmi *hdmi, u32 offset, u32 val) 1948f83f268SJie Qiu { 1958f83f268SJie Qiu writel(val, hdmi->regs + offset); 1968f83f268SJie Qiu } 1978f83f268SJie Qiu 1988f83f268SJie Qiu static void mtk_hdmi_clear_bits(struct mtk_hdmi *hdmi, u32 offset, u32 bits) 1998f83f268SJie Qiu { 2008f83f268SJie Qiu void __iomem *reg = hdmi->regs + offset; 2018f83f268SJie Qiu u32 tmp; 2028f83f268SJie Qiu 2038f83f268SJie Qiu tmp = readl(reg); 2048f83f268SJie Qiu tmp &= ~bits; 2058f83f268SJie Qiu writel(tmp, reg); 2068f83f268SJie Qiu } 2078f83f268SJie Qiu 2088f83f268SJie Qiu static void mtk_hdmi_set_bits(struct mtk_hdmi *hdmi, u32 offset, u32 bits) 2098f83f268SJie Qiu { 2108f83f268SJie Qiu void __iomem *reg = hdmi->regs + offset; 2118f83f268SJie Qiu u32 tmp; 2128f83f268SJie Qiu 2138f83f268SJie Qiu tmp = readl(reg); 2148f83f268SJie Qiu tmp |= bits; 2158f83f268SJie Qiu writel(tmp, reg); 2168f83f268SJie Qiu } 2178f83f268SJie Qiu 2188f83f268SJie Qiu static void mtk_hdmi_mask(struct mtk_hdmi *hdmi, u32 offset, u32 val, u32 mask) 2198f83f268SJie Qiu { 2208f83f268SJie Qiu void __iomem *reg = hdmi->regs + offset; 2218f83f268SJie Qiu u32 tmp; 2228f83f268SJie Qiu 2238f83f268SJie Qiu tmp = readl(reg); 2248f83f268SJie Qiu tmp = (tmp & ~mask) | (val & mask); 2258f83f268SJie Qiu writel(tmp, reg); 2268f83f268SJie Qiu } 2278f83f268SJie Qiu 2288f83f268SJie Qiu static void mtk_hdmi_hw_vid_black(struct mtk_hdmi *hdmi, bool black) 2298f83f268SJie Qiu { 2308f83f268SJie Qiu mtk_hdmi_mask(hdmi, VIDEO_CFG_4, black ? GEN_RGB : NORMAL_PATH, 2318f83f268SJie Qiu VIDEO_SOURCE_SEL); 2328f83f268SJie Qiu } 2338f83f268SJie Qiu 2348f83f268SJie Qiu static void mtk_hdmi_hw_make_reg_writable(struct mtk_hdmi *hdmi, bool enable) 2358f83f268SJie Qiu { 23656ba355dSJie Qiu struct arm_smccc_res res; 237be28b650Schunhui dai struct mtk_hdmi_phy *hdmi_phy = phy_get_drvdata(hdmi->phy); 23856ba355dSJie Qiu 23956ba355dSJie Qiu /* 24056ba355dSJie Qiu * MT8173 HDMI hardware has an output control bit to enable/disable HDMI 24156ba355dSJie Qiu * output. This bit can only be controlled in ARM supervisor mode. 24256ba355dSJie Qiu * The ARM trusted firmware provides an API for the HDMI driver to set 24356ba355dSJie Qiu * this control bit to enable HDMI output in supervisor mode. 24456ba355dSJie Qiu */ 2450fc721b2Schunhui dai if (hdmi_phy->conf && hdmi_phy->conf->tz_disabled) 2460fc721b2Schunhui dai regmap_update_bits(hdmi->sys_regmap, 2470fc721b2Schunhui dai hdmi->sys_offset + HDMI_SYS_CFG20, 2480fc721b2Schunhui dai 0x80008005, enable ? 0x80000005 : 0x8000); 2490fc721b2Schunhui dai else 2500fc721b2Schunhui dai arm_smccc_smc(MTK_SIP_SET_AUTHORIZED_SECURE_REG, 0x14000904, 2510fc721b2Schunhui dai 0x80000000, 0, 0, 0, 0, 0, &res); 25256ba355dSJie Qiu 2538f83f268SJie Qiu regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG20, 2548f83f268SJie Qiu HDMI_PCLK_FREE_RUN, enable ? HDMI_PCLK_FREE_RUN : 0); 2558f83f268SJie Qiu regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG1C, 2568f83f268SJie Qiu HDMI_ON | ANLG_ON, enable ? (HDMI_ON | ANLG_ON) : 0); 2578f83f268SJie Qiu } 2588f83f268SJie Qiu 2598f83f268SJie Qiu static void mtk_hdmi_hw_1p4_version_enable(struct mtk_hdmi *hdmi, bool enable) 2608f83f268SJie Qiu { 2618f83f268SJie Qiu regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG20, 2628f83f268SJie Qiu HDMI2P0_EN, enable ? 0 : HDMI2P0_EN); 2638f83f268SJie Qiu } 2648f83f268SJie Qiu 2658f83f268SJie Qiu static void mtk_hdmi_hw_aud_mute(struct mtk_hdmi *hdmi) 2668f83f268SJie Qiu { 2678f83f268SJie Qiu mtk_hdmi_set_bits(hdmi, GRL_AUDIO_CFG, AUDIO_ZERO); 2688f83f268SJie Qiu } 2698f83f268SJie Qiu 2708f83f268SJie Qiu static void mtk_hdmi_hw_aud_unmute(struct mtk_hdmi *hdmi) 2718f83f268SJie Qiu { 2728f83f268SJie Qiu mtk_hdmi_clear_bits(hdmi, GRL_AUDIO_CFG, AUDIO_ZERO); 2738f83f268SJie Qiu } 2748f83f268SJie Qiu 2758f83f268SJie Qiu static void mtk_hdmi_hw_reset(struct mtk_hdmi *hdmi) 2768f83f268SJie Qiu { 2778f83f268SJie Qiu regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG1C, 2788f83f268SJie Qiu HDMI_RST, HDMI_RST); 2798f83f268SJie Qiu regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG1C, 2808f83f268SJie Qiu HDMI_RST, 0); 2818f83f268SJie Qiu mtk_hdmi_clear_bits(hdmi, GRL_CFG3, CFG3_CONTROL_PACKET_DELAY); 2828f83f268SJie Qiu regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG1C, 2838f83f268SJie Qiu ANLG_ON, ANLG_ON); 2848f83f268SJie Qiu } 2858f83f268SJie Qiu 2868f83f268SJie Qiu static void mtk_hdmi_hw_enable_notice(struct mtk_hdmi *hdmi, bool enable_notice) 2878f83f268SJie Qiu { 2888f83f268SJie Qiu mtk_hdmi_mask(hdmi, GRL_CFG2, enable_notice ? CFG2_NOTICE_EN : 0, 2898f83f268SJie Qiu CFG2_NOTICE_EN); 2908f83f268SJie Qiu } 2918f83f268SJie Qiu 2928f83f268SJie Qiu static void mtk_hdmi_hw_write_int_mask(struct mtk_hdmi *hdmi, u32 int_mask) 2938f83f268SJie Qiu { 2948f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_INT_MASK, int_mask); 2958f83f268SJie Qiu } 2968f83f268SJie Qiu 2978f83f268SJie Qiu static void mtk_hdmi_hw_enable_dvi_mode(struct mtk_hdmi *hdmi, bool enable) 2988f83f268SJie Qiu { 2998f83f268SJie Qiu mtk_hdmi_mask(hdmi, GRL_CFG1, enable ? CFG1_DVI : 0, CFG1_DVI); 3008f83f268SJie Qiu } 3018f83f268SJie Qiu 3028f83f268SJie Qiu static void mtk_hdmi_hw_send_info_frame(struct mtk_hdmi *hdmi, u8 *buffer, 3038f83f268SJie Qiu u8 len) 3048f83f268SJie Qiu { 3058f83f268SJie Qiu u32 ctrl_reg = GRL_CTRL; 3068f83f268SJie Qiu int i; 3078f83f268SJie Qiu u8 *frame_data; 3088f83f268SJie Qiu enum hdmi_infoframe_type frame_type; 3098f83f268SJie Qiu u8 frame_ver; 3108f83f268SJie Qiu u8 frame_len; 3118f83f268SJie Qiu u8 checksum; 3128f83f268SJie Qiu int ctrl_frame_en = 0; 3138f83f268SJie Qiu 3143cda3d31SBernard Zhao frame_type = *buffer++; 3153cda3d31SBernard Zhao frame_ver = *buffer++; 3163cda3d31SBernard Zhao frame_len = *buffer++; 3173cda3d31SBernard Zhao checksum = *buffer++; 3188f83f268SJie Qiu frame_data = buffer; 3198f83f268SJie Qiu 3208f83f268SJie Qiu dev_dbg(hdmi->dev, 3218f83f268SJie Qiu "frame_type:0x%x,frame_ver:0x%x,frame_len:0x%x,checksum:0x%x\n", 3228f83f268SJie Qiu frame_type, frame_ver, frame_len, checksum); 3238f83f268SJie Qiu 3248f83f268SJie Qiu switch (frame_type) { 3258f83f268SJie Qiu case HDMI_INFOFRAME_TYPE_AVI: 3268f83f268SJie Qiu ctrl_frame_en = CTRL_AVI_EN; 3278f83f268SJie Qiu ctrl_reg = GRL_CTRL; 3288f83f268SJie Qiu break; 3298f83f268SJie Qiu case HDMI_INFOFRAME_TYPE_SPD: 3308f83f268SJie Qiu ctrl_frame_en = CTRL_SPD_EN; 3318f83f268SJie Qiu ctrl_reg = GRL_CTRL; 3328f83f268SJie Qiu break; 3338f83f268SJie Qiu case HDMI_INFOFRAME_TYPE_AUDIO: 3348f83f268SJie Qiu ctrl_frame_en = CTRL_AUDIO_EN; 3358f83f268SJie Qiu ctrl_reg = GRL_CTRL; 3368f83f268SJie Qiu break; 3378f83f268SJie Qiu case HDMI_INFOFRAME_TYPE_VENDOR: 3388f83f268SJie Qiu ctrl_frame_en = VS_EN; 3398f83f268SJie Qiu ctrl_reg = GRL_ACP_ISRC_CTRL; 3408f83f268SJie Qiu break; 3411f1635b1SSean Paul default: 3421f1635b1SSean Paul dev_err(hdmi->dev, "Unknown infoframe type %d\n", frame_type); 3431f1635b1SSean Paul return; 3448f83f268SJie Qiu } 3458f83f268SJie Qiu mtk_hdmi_clear_bits(hdmi, ctrl_reg, ctrl_frame_en); 3468f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_INFOFRM_TYPE, frame_type); 3478f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_INFOFRM_VER, frame_ver); 3488f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_INFOFRM_LNG, frame_len); 3498f83f268SJie Qiu 3508f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_IFM_PORT, checksum); 3518f83f268SJie Qiu for (i = 0; i < frame_len; i++) 3528f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_IFM_PORT, frame_data[i]); 3538f83f268SJie Qiu 3548f83f268SJie Qiu mtk_hdmi_set_bits(hdmi, ctrl_reg, ctrl_frame_en); 3558f83f268SJie Qiu } 3568f83f268SJie Qiu 3578f83f268SJie Qiu static void mtk_hdmi_hw_send_aud_packet(struct mtk_hdmi *hdmi, bool enable) 3588f83f268SJie Qiu { 3598f83f268SJie Qiu mtk_hdmi_mask(hdmi, GRL_SHIFT_R2, enable ? 0 : AUDIO_PACKET_OFF, 3608f83f268SJie Qiu AUDIO_PACKET_OFF); 3618f83f268SJie Qiu } 3628f83f268SJie Qiu 3638f83f268SJie Qiu static void mtk_hdmi_hw_config_sys(struct mtk_hdmi *hdmi) 3648f83f268SJie Qiu { 3658f83f268SJie Qiu regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG20, 3668f83f268SJie Qiu HDMI_OUT_FIFO_EN | MHL_MODE_ON, 0); 3678f83f268SJie Qiu usleep_range(2000, 4000); 3688f83f268SJie Qiu regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG20, 3698f83f268SJie Qiu HDMI_OUT_FIFO_EN | MHL_MODE_ON, HDMI_OUT_FIFO_EN); 3708f83f268SJie Qiu } 3718f83f268SJie Qiu 3728f83f268SJie Qiu static void mtk_hdmi_hw_set_deep_color_mode(struct mtk_hdmi *hdmi) 3738f83f268SJie Qiu { 3748f83f268SJie Qiu regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG20, 3758f83f268SJie Qiu DEEP_COLOR_MODE_MASK | DEEP_COLOR_EN, 3768f83f268SJie Qiu COLOR_8BIT_MODE); 3778f83f268SJie Qiu } 3788f83f268SJie Qiu 3798f83f268SJie Qiu static void mtk_hdmi_hw_send_av_mute(struct mtk_hdmi *hdmi) 3808f83f268SJie Qiu { 3818f83f268SJie Qiu mtk_hdmi_clear_bits(hdmi, GRL_CFG4, CTRL_AVMUTE); 3828f83f268SJie Qiu usleep_range(2000, 4000); 3838f83f268SJie Qiu mtk_hdmi_set_bits(hdmi, GRL_CFG4, CTRL_AVMUTE); 3848f83f268SJie Qiu } 3858f83f268SJie Qiu 3868f83f268SJie Qiu static void mtk_hdmi_hw_send_av_unmute(struct mtk_hdmi *hdmi) 3878f83f268SJie Qiu { 3888f83f268SJie Qiu mtk_hdmi_mask(hdmi, GRL_CFG4, CFG4_AV_UNMUTE_EN, 3898f83f268SJie Qiu CFG4_AV_UNMUTE_EN | CFG4_AV_UNMUTE_SET); 3908f83f268SJie Qiu usleep_range(2000, 4000); 3918f83f268SJie Qiu mtk_hdmi_mask(hdmi, GRL_CFG4, CFG4_AV_UNMUTE_SET, 3928f83f268SJie Qiu CFG4_AV_UNMUTE_EN | CFG4_AV_UNMUTE_SET); 3938f83f268SJie Qiu } 3948f83f268SJie Qiu 3958f83f268SJie Qiu static void mtk_hdmi_hw_ncts_enable(struct mtk_hdmi *hdmi, bool on) 3968f83f268SJie Qiu { 3978f83f268SJie Qiu mtk_hdmi_mask(hdmi, GRL_CTS_CTRL, on ? 0 : CTS_CTRL_SOFT, 3988f83f268SJie Qiu CTS_CTRL_SOFT); 3998f83f268SJie Qiu } 4008f83f268SJie Qiu 4018f83f268SJie Qiu static void mtk_hdmi_hw_ncts_auto_write_enable(struct mtk_hdmi *hdmi, 4028f83f268SJie Qiu bool enable) 4038f83f268SJie Qiu { 4048f83f268SJie Qiu mtk_hdmi_mask(hdmi, GRL_CTS_CTRL, enable ? NCTS_WRI_ANYTIME : 0, 4058f83f268SJie Qiu NCTS_WRI_ANYTIME); 4068f83f268SJie Qiu } 4078f83f268SJie Qiu 4088f83f268SJie Qiu static void mtk_hdmi_hw_msic_setting(struct mtk_hdmi *hdmi, 4098f83f268SJie Qiu struct drm_display_mode *mode) 4108f83f268SJie Qiu { 4118f83f268SJie Qiu mtk_hdmi_clear_bits(hdmi, GRL_CFG4, CFG4_MHL_MODE); 4128f83f268SJie Qiu 4138f83f268SJie Qiu if (mode->flags & DRM_MODE_FLAG_INTERLACE && 4148f83f268SJie Qiu mode->clock == 74250 && 4158f83f268SJie Qiu mode->vdisplay == 1080) 4168f83f268SJie Qiu mtk_hdmi_clear_bits(hdmi, GRL_CFG2, CFG2_MHL_DE_SEL); 4178f83f268SJie Qiu else 4188f83f268SJie Qiu mtk_hdmi_set_bits(hdmi, GRL_CFG2, CFG2_MHL_DE_SEL); 4198f83f268SJie Qiu } 4208f83f268SJie Qiu 4218f83f268SJie Qiu static void mtk_hdmi_hw_aud_set_channel_swap(struct mtk_hdmi *hdmi, 4228f83f268SJie Qiu enum hdmi_aud_channel_swap_type swap) 4238f83f268SJie Qiu { 4248f83f268SJie Qiu u8 swap_bit; 4258f83f268SJie Qiu 4268f83f268SJie Qiu switch (swap) { 4278f83f268SJie Qiu case HDMI_AUD_SWAP_LR: 4288f83f268SJie Qiu swap_bit = LR_SWAP; 4298f83f268SJie Qiu break; 4308f83f268SJie Qiu case HDMI_AUD_SWAP_LFE_CC: 4318f83f268SJie Qiu swap_bit = LFE_CC_SWAP; 4328f83f268SJie Qiu break; 4338f83f268SJie Qiu case HDMI_AUD_SWAP_LSRS: 4348f83f268SJie Qiu swap_bit = LSRS_SWAP; 4358f83f268SJie Qiu break; 4368f83f268SJie Qiu case HDMI_AUD_SWAP_RLS_RRS: 4378f83f268SJie Qiu swap_bit = RLS_RRS_SWAP; 4388f83f268SJie Qiu break; 4398f83f268SJie Qiu case HDMI_AUD_SWAP_LR_STATUS: 4408f83f268SJie Qiu swap_bit = LR_STATUS_SWAP; 4418f83f268SJie Qiu break; 4428f83f268SJie Qiu default: 4438f83f268SJie Qiu swap_bit = LFE_CC_SWAP; 4448f83f268SJie Qiu break; 4458f83f268SJie Qiu } 4468f83f268SJie Qiu mtk_hdmi_mask(hdmi, GRL_CH_SWAP, swap_bit, 0xff); 4478f83f268SJie Qiu } 4488f83f268SJie Qiu 4498f83f268SJie Qiu static void mtk_hdmi_hw_aud_set_bit_num(struct mtk_hdmi *hdmi, 4508f83f268SJie Qiu enum hdmi_audio_sample_size bit_num) 4518f83f268SJie Qiu { 4528f83f268SJie Qiu u32 val; 4538f83f268SJie Qiu 4548f83f268SJie Qiu switch (bit_num) { 4558f83f268SJie Qiu case HDMI_AUDIO_SAMPLE_SIZE_16: 4568f83f268SJie Qiu val = AOUT_16BIT; 4578f83f268SJie Qiu break; 4588f83f268SJie Qiu case HDMI_AUDIO_SAMPLE_SIZE_20: 4598f83f268SJie Qiu val = AOUT_20BIT; 4608f83f268SJie Qiu break; 4618f83f268SJie Qiu case HDMI_AUDIO_SAMPLE_SIZE_24: 4628f83f268SJie Qiu case HDMI_AUDIO_SAMPLE_SIZE_STREAM: 4638f83f268SJie Qiu val = AOUT_24BIT; 4648f83f268SJie Qiu break; 4658f83f268SJie Qiu } 4668f83f268SJie Qiu 4678f83f268SJie Qiu mtk_hdmi_mask(hdmi, GRL_AOUT_CFG, val, AOUT_BNUM_SEL_MASK); 4688f83f268SJie Qiu } 4698f83f268SJie Qiu 4708f83f268SJie Qiu static void mtk_hdmi_hw_aud_set_i2s_fmt(struct mtk_hdmi *hdmi, 4718f83f268SJie Qiu enum hdmi_aud_i2s_fmt i2s_fmt) 4728f83f268SJie Qiu { 4738f83f268SJie Qiu u32 val; 4748f83f268SJie Qiu 4758f83f268SJie Qiu val = mtk_hdmi_read(hdmi, GRL_CFG0); 4768f83f268SJie Qiu val &= ~(CFG0_W_LENGTH_MASK | CFG0_I2S_MODE_MASK); 4778f83f268SJie Qiu 4788f83f268SJie Qiu switch (i2s_fmt) { 4798f83f268SJie Qiu case HDMI_I2S_MODE_RJT_24BIT: 4808f83f268SJie Qiu val |= CFG0_I2S_MODE_RTJ | CFG0_W_LENGTH_24BIT; 4818f83f268SJie Qiu break; 4828f83f268SJie Qiu case HDMI_I2S_MODE_RJT_16BIT: 4838f83f268SJie Qiu val |= CFG0_I2S_MODE_RTJ | CFG0_W_LENGTH_16BIT; 4848f83f268SJie Qiu break; 4858f83f268SJie Qiu case HDMI_I2S_MODE_LJT_24BIT: 4868f83f268SJie Qiu default: 4878f83f268SJie Qiu val |= CFG0_I2S_MODE_LTJ | CFG0_W_LENGTH_24BIT; 4888f83f268SJie Qiu break; 4898f83f268SJie Qiu case HDMI_I2S_MODE_LJT_16BIT: 4908f83f268SJie Qiu val |= CFG0_I2S_MODE_LTJ | CFG0_W_LENGTH_16BIT; 4918f83f268SJie Qiu break; 4928f83f268SJie Qiu case HDMI_I2S_MODE_I2S_24BIT: 4938f83f268SJie Qiu val |= CFG0_I2S_MODE_I2S | CFG0_W_LENGTH_24BIT; 4948f83f268SJie Qiu break; 4958f83f268SJie Qiu case HDMI_I2S_MODE_I2S_16BIT: 4968f83f268SJie Qiu val |= CFG0_I2S_MODE_I2S | CFG0_W_LENGTH_16BIT; 4978f83f268SJie Qiu break; 4988f83f268SJie Qiu } 4998f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_CFG0, val); 5008f83f268SJie Qiu } 5018f83f268SJie Qiu 5028f83f268SJie Qiu static void mtk_hdmi_hw_audio_config(struct mtk_hdmi *hdmi, bool dst) 5038f83f268SJie Qiu { 5048f83f268SJie Qiu const u8 mask = HIGH_BIT_RATE | DST_NORMAL_DOUBLE | SACD_DST | DSD_SEL; 5058f83f268SJie Qiu u8 val; 5068f83f268SJie Qiu 5078f83f268SJie Qiu /* Disable high bitrate, set DST packet normal/double */ 5088f83f268SJie Qiu mtk_hdmi_clear_bits(hdmi, GRL_AOUT_CFG, HIGH_BIT_RATE_PACKET_ALIGN); 5098f83f268SJie Qiu 5108f83f268SJie Qiu if (dst) 5118f83f268SJie Qiu val = DST_NORMAL_DOUBLE | SACD_DST; 5128f83f268SJie Qiu else 5138f83f268SJie Qiu val = 0; 5148f83f268SJie Qiu 5158f83f268SJie Qiu mtk_hdmi_mask(hdmi, GRL_AUDIO_CFG, val, mask); 5168f83f268SJie Qiu } 5178f83f268SJie Qiu 5188f83f268SJie Qiu static void mtk_hdmi_hw_aud_set_i2s_chan_num(struct mtk_hdmi *hdmi, 5198f83f268SJie Qiu enum hdmi_aud_channel_type channel_type, 5208f83f268SJie Qiu u8 channel_count) 5218f83f268SJie Qiu { 5228f83f268SJie Qiu unsigned int ch_switch; 5238f83f268SJie Qiu u8 i2s_uv; 5248f83f268SJie Qiu 5258f83f268SJie Qiu ch_switch = CH_SWITCH(7, 7) | CH_SWITCH(6, 6) | 5268f83f268SJie Qiu CH_SWITCH(5, 5) | CH_SWITCH(4, 4) | 5278f83f268SJie Qiu CH_SWITCH(3, 3) | CH_SWITCH(1, 2) | 5288f83f268SJie Qiu CH_SWITCH(2, 1) | CH_SWITCH(0, 0); 5298f83f268SJie Qiu 5308f83f268SJie Qiu if (channel_count == 2) { 5318f83f268SJie Qiu i2s_uv = I2S_UV_CH_EN(0); 5328f83f268SJie Qiu } else if (channel_count == 3 || channel_count == 4) { 5338f83f268SJie Qiu if (channel_count == 4 && 5348f83f268SJie Qiu (channel_type == HDMI_AUD_CHAN_TYPE_3_0_LRS || 5358f83f268SJie Qiu channel_type == HDMI_AUD_CHAN_TYPE_4_0)) 5368f83f268SJie Qiu i2s_uv = I2S_UV_CH_EN(2) | I2S_UV_CH_EN(0); 5378f83f268SJie Qiu else 5388f83f268SJie Qiu i2s_uv = I2S_UV_CH_EN(3) | I2S_UV_CH_EN(2); 5398f83f268SJie Qiu } else if (channel_count == 6 || channel_count == 5) { 5408f83f268SJie Qiu if (channel_count == 6 && 5418f83f268SJie Qiu channel_type != HDMI_AUD_CHAN_TYPE_5_1 && 5428f83f268SJie Qiu channel_type != HDMI_AUD_CHAN_TYPE_4_1_CLRS) { 5438f83f268SJie Qiu i2s_uv = I2S_UV_CH_EN(3) | I2S_UV_CH_EN(2) | 5448f83f268SJie Qiu I2S_UV_CH_EN(1) | I2S_UV_CH_EN(0); 5458f83f268SJie Qiu } else { 5468f83f268SJie Qiu i2s_uv = I2S_UV_CH_EN(2) | I2S_UV_CH_EN(1) | 5478f83f268SJie Qiu I2S_UV_CH_EN(0); 5488f83f268SJie Qiu } 5498f83f268SJie Qiu } else if (channel_count == 8 || channel_count == 7) { 5508f83f268SJie Qiu i2s_uv = I2S_UV_CH_EN(3) | I2S_UV_CH_EN(2) | 5518f83f268SJie Qiu I2S_UV_CH_EN(1) | I2S_UV_CH_EN(0); 5528f83f268SJie Qiu } else { 5538f83f268SJie Qiu i2s_uv = I2S_UV_CH_EN(0); 5548f83f268SJie Qiu } 5558f83f268SJie Qiu 5568f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_CH_SW0, ch_switch & 0xff); 5578f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_CH_SW1, (ch_switch >> 8) & 0xff); 5588f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_CH_SW2, (ch_switch >> 16) & 0xff); 5598f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_I2S_UV, i2s_uv); 5608f83f268SJie Qiu } 5618f83f268SJie Qiu 5628f83f268SJie Qiu static void mtk_hdmi_hw_aud_set_input_type(struct mtk_hdmi *hdmi, 5638f83f268SJie Qiu enum hdmi_aud_input_type input_type) 5648f83f268SJie Qiu { 5658f83f268SJie Qiu u32 val; 5668f83f268SJie Qiu 5678f83f268SJie Qiu val = mtk_hdmi_read(hdmi, GRL_CFG1); 5688f83f268SJie Qiu if (input_type == HDMI_AUD_INPUT_I2S && 5698f83f268SJie Qiu (val & CFG1_SPDIF) == CFG1_SPDIF) { 5708f83f268SJie Qiu val &= ~CFG1_SPDIF; 5718f83f268SJie Qiu } else if (input_type == HDMI_AUD_INPUT_SPDIF && 5728f83f268SJie Qiu (val & CFG1_SPDIF) == 0) { 5738f83f268SJie Qiu val |= CFG1_SPDIF; 5748f83f268SJie Qiu } 5758f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_CFG1, val); 5768f83f268SJie Qiu } 5778f83f268SJie Qiu 5788f83f268SJie Qiu static void mtk_hdmi_hw_aud_set_channel_status(struct mtk_hdmi *hdmi, 5798f83f268SJie Qiu u8 *channel_status) 5808f83f268SJie Qiu { 5818f83f268SJie Qiu int i; 5828f83f268SJie Qiu 5838f83f268SJie Qiu for (i = 0; i < 5; i++) { 5848f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_I2S_C_STA0 + i * 4, channel_status[i]); 5858f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_L_STATUS_0 + i * 4, channel_status[i]); 5868f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_R_STATUS_0 + i * 4, channel_status[i]); 5878f83f268SJie Qiu } 5888f83f268SJie Qiu for (; i < 24; i++) { 5898f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_L_STATUS_0 + i * 4, 0); 5908f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_R_STATUS_0 + i * 4, 0); 5918f83f268SJie Qiu } 5928f83f268SJie Qiu } 5938f83f268SJie Qiu 5948f83f268SJie Qiu static void mtk_hdmi_hw_aud_src_reenable(struct mtk_hdmi *hdmi) 5958f83f268SJie Qiu { 5968f83f268SJie Qiu u32 val; 5978f83f268SJie Qiu 5988f83f268SJie Qiu val = mtk_hdmi_read(hdmi, GRL_MIX_CTRL); 5998f83f268SJie Qiu if (val & MIX_CTRL_SRC_EN) { 6008f83f268SJie Qiu val &= ~MIX_CTRL_SRC_EN; 6018f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_MIX_CTRL, val); 6028f83f268SJie Qiu usleep_range(255, 512); 6038f83f268SJie Qiu val |= MIX_CTRL_SRC_EN; 6048f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_MIX_CTRL, val); 6058f83f268SJie Qiu } 6068f83f268SJie Qiu } 6078f83f268SJie Qiu 6088f83f268SJie Qiu static void mtk_hdmi_hw_aud_src_disable(struct mtk_hdmi *hdmi) 6098f83f268SJie Qiu { 6108f83f268SJie Qiu u32 val; 6118f83f268SJie Qiu 6128f83f268SJie Qiu val = mtk_hdmi_read(hdmi, GRL_MIX_CTRL); 6138f83f268SJie Qiu val &= ~MIX_CTRL_SRC_EN; 6148f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_MIX_CTRL, val); 6158f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_SHIFT_L1, 0x00); 6168f83f268SJie Qiu } 6178f83f268SJie Qiu 6188f83f268SJie Qiu static void mtk_hdmi_hw_aud_set_mclk(struct mtk_hdmi *hdmi, 6198f83f268SJie Qiu enum hdmi_aud_mclk mclk) 6208f83f268SJie Qiu { 6218f83f268SJie Qiu u32 val; 6228f83f268SJie Qiu 6238f83f268SJie Qiu val = mtk_hdmi_read(hdmi, GRL_CFG5); 6248f83f268SJie Qiu val &= CFG5_CD_RATIO_MASK; 6258f83f268SJie Qiu 6268f83f268SJie Qiu switch (mclk) { 6278f83f268SJie Qiu case HDMI_AUD_MCLK_128FS: 6288f83f268SJie Qiu val |= CFG5_FS128; 6298f83f268SJie Qiu break; 6308f83f268SJie Qiu case HDMI_AUD_MCLK_256FS: 6318f83f268SJie Qiu val |= CFG5_FS256; 6328f83f268SJie Qiu break; 6338f83f268SJie Qiu case HDMI_AUD_MCLK_384FS: 6348f83f268SJie Qiu val |= CFG5_FS384; 6358f83f268SJie Qiu break; 6368f83f268SJie Qiu case HDMI_AUD_MCLK_512FS: 6378f83f268SJie Qiu val |= CFG5_FS512; 6388f83f268SJie Qiu break; 6398f83f268SJie Qiu case HDMI_AUD_MCLK_768FS: 6408f83f268SJie Qiu val |= CFG5_FS768; 6418f83f268SJie Qiu break; 6428f83f268SJie Qiu default: 6438f83f268SJie Qiu val |= CFG5_FS256; 6448f83f268SJie Qiu break; 6458f83f268SJie Qiu } 6468f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_CFG5, val); 6478f83f268SJie Qiu } 6488f83f268SJie Qiu 6498f83f268SJie Qiu struct hdmi_acr_n { 6508f83f268SJie Qiu unsigned int clock; 6518f83f268SJie Qiu unsigned int n[3]; 6528f83f268SJie Qiu }; 6538f83f268SJie Qiu 6548f83f268SJie Qiu /* Recommended N values from HDMI specification, tables 7-1 to 7-3 */ 6558f83f268SJie Qiu static const struct hdmi_acr_n hdmi_rec_n_table[] = { 6568f83f268SJie Qiu /* Clock, N: 32kHz 44.1kHz 48kHz */ 6578f83f268SJie Qiu { 25175, { 4576, 7007, 6864 } }, 6588f83f268SJie Qiu { 74176, { 11648, 17836, 11648 } }, 6598f83f268SJie Qiu { 148352, { 11648, 8918, 5824 } }, 6608f83f268SJie Qiu { 296703, { 5824, 4459, 5824 } }, 6618f83f268SJie Qiu { 297000, { 3072, 4704, 5120 } }, 6628f83f268SJie Qiu { 0, { 4096, 6272, 6144 } }, /* all other TMDS clocks */ 6638f83f268SJie Qiu }; 6648f83f268SJie Qiu 6658f83f268SJie Qiu /** 6668f83f268SJie Qiu * hdmi_recommended_n() - Return N value recommended by HDMI specification 6678f83f268SJie Qiu * @freq: audio sample rate in Hz 6688f83f268SJie Qiu * @clock: rounded TMDS clock in kHz 6698f83f268SJie Qiu */ 6708f83f268SJie Qiu static unsigned int hdmi_recommended_n(unsigned int freq, unsigned int clock) 6718f83f268SJie Qiu { 6728f83f268SJie Qiu const struct hdmi_acr_n *recommended; 6738f83f268SJie Qiu unsigned int i; 6748f83f268SJie Qiu 6758f83f268SJie Qiu for (i = 0; i < ARRAY_SIZE(hdmi_rec_n_table) - 1; i++) { 6768f83f268SJie Qiu if (clock == hdmi_rec_n_table[i].clock) 6778f83f268SJie Qiu break; 6788f83f268SJie Qiu } 6798f83f268SJie Qiu recommended = hdmi_rec_n_table + i; 6808f83f268SJie Qiu 6818f83f268SJie Qiu switch (freq) { 6828f83f268SJie Qiu case 32000: 6838f83f268SJie Qiu return recommended->n[0]; 6848f83f268SJie Qiu case 44100: 6858f83f268SJie Qiu return recommended->n[1]; 6868f83f268SJie Qiu case 48000: 6878f83f268SJie Qiu return recommended->n[2]; 6888f83f268SJie Qiu case 88200: 6898f83f268SJie Qiu return recommended->n[1] * 2; 6908f83f268SJie Qiu case 96000: 6918f83f268SJie Qiu return recommended->n[2] * 2; 6928f83f268SJie Qiu case 176400: 6938f83f268SJie Qiu return recommended->n[1] * 4; 6948f83f268SJie Qiu case 192000: 6958f83f268SJie Qiu return recommended->n[2] * 4; 6968f83f268SJie Qiu default: 6978f83f268SJie Qiu return (128 * freq) / 1000; 6988f83f268SJie Qiu } 6998f83f268SJie Qiu } 7008f83f268SJie Qiu 7018f83f268SJie Qiu static unsigned int hdmi_mode_clock_to_hz(unsigned int clock) 7028f83f268SJie Qiu { 7038f83f268SJie Qiu switch (clock) { 7048f83f268SJie Qiu case 25175: 7058f83f268SJie Qiu return 25174825; /* 25.2/1.001 MHz */ 7068f83f268SJie Qiu case 74176: 7078f83f268SJie Qiu return 74175824; /* 74.25/1.001 MHz */ 7088f83f268SJie Qiu case 148352: 7098f83f268SJie Qiu return 148351648; /* 148.5/1.001 MHz */ 7108f83f268SJie Qiu case 296703: 7118f83f268SJie Qiu return 296703297; /* 297/1.001 MHz */ 7128f83f268SJie Qiu default: 7138f83f268SJie Qiu return clock * 1000; 7148f83f268SJie Qiu } 7158f83f268SJie Qiu } 7168f83f268SJie Qiu 7178f83f268SJie Qiu static unsigned int hdmi_expected_cts(unsigned int audio_sample_rate, 7188f83f268SJie Qiu unsigned int tmds_clock, unsigned int n) 7198f83f268SJie Qiu { 7208f83f268SJie Qiu return DIV_ROUND_CLOSEST_ULL((u64)hdmi_mode_clock_to_hz(tmds_clock) * n, 7218f83f268SJie Qiu 128 * audio_sample_rate); 7228f83f268SJie Qiu } 7238f83f268SJie Qiu 7248f83f268SJie Qiu static void do_hdmi_hw_aud_set_ncts(struct mtk_hdmi *hdmi, unsigned int n, 7258f83f268SJie Qiu unsigned int cts) 7268f83f268SJie Qiu { 7278f83f268SJie Qiu unsigned char val[NCTS_BYTES]; 7288f83f268SJie Qiu int i; 7298f83f268SJie Qiu 7308f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_NCTS, 0); 7318f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_NCTS, 0); 7328f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_NCTS, 0); 7338f83f268SJie Qiu memset(val, 0, sizeof(val)); 7348f83f268SJie Qiu 7358f83f268SJie Qiu val[0] = (cts >> 24) & 0xff; 7368f83f268SJie Qiu val[1] = (cts >> 16) & 0xff; 7378f83f268SJie Qiu val[2] = (cts >> 8) & 0xff; 7388f83f268SJie Qiu val[3] = cts & 0xff; 7398f83f268SJie Qiu 7408f83f268SJie Qiu val[4] = (n >> 16) & 0xff; 7418f83f268SJie Qiu val[5] = (n >> 8) & 0xff; 7428f83f268SJie Qiu val[6] = n & 0xff; 7438f83f268SJie Qiu 7448f83f268SJie Qiu for (i = 0; i < NCTS_BYTES; i++) 7458f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_NCTS, val[i]); 7468f83f268SJie Qiu } 7478f83f268SJie Qiu 7488f83f268SJie Qiu static void mtk_hdmi_hw_aud_set_ncts(struct mtk_hdmi *hdmi, 7498f83f268SJie Qiu unsigned int sample_rate, 7508f83f268SJie Qiu unsigned int clock) 7518f83f268SJie Qiu { 7528f83f268SJie Qiu unsigned int n, cts; 7538f83f268SJie Qiu 7548f83f268SJie Qiu n = hdmi_recommended_n(sample_rate, clock); 7558f83f268SJie Qiu cts = hdmi_expected_cts(sample_rate, clock, n); 7568f83f268SJie Qiu 7578f83f268SJie Qiu dev_dbg(hdmi->dev, "%s: sample_rate=%u, clock=%d, cts=%u, n=%u\n", 7588f83f268SJie Qiu __func__, sample_rate, clock, n, cts); 7598f83f268SJie Qiu 7608f83f268SJie Qiu mtk_hdmi_mask(hdmi, DUMMY_304, AUDIO_I2S_NCTS_SEL_64, 7618f83f268SJie Qiu AUDIO_I2S_NCTS_SEL); 7628f83f268SJie Qiu do_hdmi_hw_aud_set_ncts(hdmi, n, cts); 7638f83f268SJie Qiu } 7648f83f268SJie Qiu 7658f83f268SJie Qiu static u8 mtk_hdmi_aud_get_chnl_count(enum hdmi_aud_channel_type channel_type) 7668f83f268SJie Qiu { 7678f83f268SJie Qiu switch (channel_type) { 7688f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_1_0: 7698f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_1_1: 7708f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_2_0: 7718f83f268SJie Qiu return 2; 7728f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_2_1: 7738f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_3_0: 7748f83f268SJie Qiu return 3; 7758f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_3_1: 7768f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_4_0: 7778f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_3_0_LRS: 7788f83f268SJie Qiu return 4; 7798f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_4_1: 7808f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_5_0: 7818f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_3_1_LRS: 7828f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_4_0_CLRS: 7838f83f268SJie Qiu return 5; 7848f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_5_1: 7858f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_6_0: 7868f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_4_1_CLRS: 7878f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_6_0_CS: 7888f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_6_0_CH: 7898f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_6_0_OH: 7908f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_6_0_CHR: 7918f83f268SJie Qiu return 6; 7928f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_6_1: 7938f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_6_1_CS: 7948f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_6_1_CH: 7958f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_6_1_OH: 7968f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_6_1_CHR: 7978f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0: 7988f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_LH_RH: 7998f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_LSR_RSR: 8008f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_LC_RC: 8018f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_LW_RW: 8028f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_LSD_RSD: 8038f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_LSS_RSS: 8048f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_LHS_RHS: 8058f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_CS_CH: 8068f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_CS_OH: 8078f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_CS_CHR: 8088f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_CH_OH: 8098f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_CH_CHR: 8108f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_OH_CHR: 8118f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_LSS_RSS_LSR_RSR: 8128f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_8_0_LH_RH_CS: 8138f83f268SJie Qiu return 7; 8148f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1: 8158f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_LH_RH: 8168f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_LSR_RSR: 8178f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_LC_RC: 8188f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_LW_RW: 8198f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_LSD_RSD: 8208f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_LSS_RSS: 8218f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_LHS_RHS: 8228f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_CS_CH: 8238f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_CS_OH: 8248f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_CS_CHR: 8258f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_CH_OH: 8268f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_CH_CHR: 8278f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_OH_CHR: 8288f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_LSS_RSS_LSR_RSR: 8298f83f268SJie Qiu return 8; 8308f83f268SJie Qiu default: 8318f83f268SJie Qiu return 2; 8328f83f268SJie Qiu } 8338f83f268SJie Qiu } 8348f83f268SJie Qiu 8358f83f268SJie Qiu static int mtk_hdmi_video_change_vpll(struct mtk_hdmi *hdmi, u32 clock) 8368f83f268SJie Qiu { 8378f83f268SJie Qiu unsigned long rate; 8388f83f268SJie Qiu int ret; 8398f83f268SJie Qiu 8408f83f268SJie Qiu /* The DPI driver already should have set TVDPLL to the correct rate */ 8418f83f268SJie Qiu ret = clk_set_rate(hdmi->clk[MTK_HDMI_CLK_HDMI_PLL], clock); 8428f83f268SJie Qiu if (ret) { 8438f83f268SJie Qiu dev_err(hdmi->dev, "Failed to set PLL to %u Hz: %d\n", clock, 8448f83f268SJie Qiu ret); 8458f83f268SJie Qiu return ret; 8468f83f268SJie Qiu } 8478f83f268SJie Qiu 8488f83f268SJie Qiu rate = clk_get_rate(hdmi->clk[MTK_HDMI_CLK_HDMI_PLL]); 8498f83f268SJie Qiu 8508f83f268SJie Qiu if (DIV_ROUND_CLOSEST(rate, 1000) != DIV_ROUND_CLOSEST(clock, 1000)) 8518f83f268SJie Qiu dev_warn(hdmi->dev, "Want PLL %u Hz, got %lu Hz\n", clock, 8528f83f268SJie Qiu rate); 8538f83f268SJie Qiu else 8548f83f268SJie Qiu dev_dbg(hdmi->dev, "Want PLL %u Hz, got %lu Hz\n", clock, rate); 8558f83f268SJie Qiu 8568f83f268SJie Qiu mtk_hdmi_hw_config_sys(hdmi); 8578f83f268SJie Qiu mtk_hdmi_hw_set_deep_color_mode(hdmi); 8588f83f268SJie Qiu return 0; 8598f83f268SJie Qiu } 8608f83f268SJie Qiu 8618f83f268SJie Qiu static void mtk_hdmi_video_set_display_mode(struct mtk_hdmi *hdmi, 8628f83f268SJie Qiu struct drm_display_mode *mode) 8638f83f268SJie Qiu { 8648f83f268SJie Qiu mtk_hdmi_hw_reset(hdmi); 8658f83f268SJie Qiu mtk_hdmi_hw_enable_notice(hdmi, true); 8668f83f268SJie Qiu mtk_hdmi_hw_write_int_mask(hdmi, 0xff); 8678f83f268SJie Qiu mtk_hdmi_hw_enable_dvi_mode(hdmi, hdmi->dvi_mode); 8688f83f268SJie Qiu mtk_hdmi_hw_ncts_auto_write_enable(hdmi, true); 8698f83f268SJie Qiu 8708f83f268SJie Qiu mtk_hdmi_hw_msic_setting(hdmi, mode); 8718f83f268SJie Qiu } 8728f83f268SJie Qiu 8738f83f268SJie Qiu static int mtk_hdmi_aud_enable_packet(struct mtk_hdmi *hdmi, bool enable) 8748f83f268SJie Qiu { 8758f83f268SJie Qiu mtk_hdmi_hw_send_aud_packet(hdmi, enable); 8768f83f268SJie Qiu return 0; 8778f83f268SJie Qiu } 8788f83f268SJie Qiu 8798f83f268SJie Qiu static int mtk_hdmi_aud_on_off_hw_ncts(struct mtk_hdmi *hdmi, bool on) 8808f83f268SJie Qiu { 8818f83f268SJie Qiu mtk_hdmi_hw_ncts_enable(hdmi, on); 8828f83f268SJie Qiu return 0; 8838f83f268SJie Qiu } 8848f83f268SJie Qiu 8858f83f268SJie Qiu static int mtk_hdmi_aud_set_input(struct mtk_hdmi *hdmi) 8868f83f268SJie Qiu { 8878f83f268SJie Qiu enum hdmi_aud_channel_type chan_type; 8888f83f268SJie Qiu u8 chan_count; 8898f83f268SJie Qiu bool dst; 8908f83f268SJie Qiu 8918f83f268SJie Qiu mtk_hdmi_hw_aud_set_channel_swap(hdmi, HDMI_AUD_SWAP_LFE_CC); 8928f83f268SJie Qiu mtk_hdmi_set_bits(hdmi, GRL_MIX_CTRL, MIX_CTRL_FLAT); 8938f83f268SJie Qiu 8948f83f268SJie Qiu if (hdmi->aud_param.aud_input_type == HDMI_AUD_INPUT_SPDIF && 8958f83f268SJie Qiu hdmi->aud_param.aud_codec == HDMI_AUDIO_CODING_TYPE_DST) { 8968f83f268SJie Qiu mtk_hdmi_hw_aud_set_bit_num(hdmi, HDMI_AUDIO_SAMPLE_SIZE_24); 8978f83f268SJie Qiu } else if (hdmi->aud_param.aud_i2s_fmt == HDMI_I2S_MODE_LJT_24BIT) { 8988f83f268SJie Qiu hdmi->aud_param.aud_i2s_fmt = HDMI_I2S_MODE_LJT_16BIT; 8998f83f268SJie Qiu } 9008f83f268SJie Qiu 9018f83f268SJie Qiu mtk_hdmi_hw_aud_set_i2s_fmt(hdmi, hdmi->aud_param.aud_i2s_fmt); 9028f83f268SJie Qiu mtk_hdmi_hw_aud_set_bit_num(hdmi, HDMI_AUDIO_SAMPLE_SIZE_24); 9038f83f268SJie Qiu 9048f83f268SJie Qiu dst = ((hdmi->aud_param.aud_input_type == HDMI_AUD_INPUT_SPDIF) && 9058f83f268SJie Qiu (hdmi->aud_param.aud_codec == HDMI_AUDIO_CODING_TYPE_DST)); 9068f83f268SJie Qiu mtk_hdmi_hw_audio_config(hdmi, dst); 9078f83f268SJie Qiu 9088f83f268SJie Qiu if (hdmi->aud_param.aud_input_type == HDMI_AUD_INPUT_SPDIF) 9098f83f268SJie Qiu chan_type = HDMI_AUD_CHAN_TYPE_2_0; 9108f83f268SJie Qiu else 9118f83f268SJie Qiu chan_type = hdmi->aud_param.aud_input_chan_type; 9128f83f268SJie Qiu chan_count = mtk_hdmi_aud_get_chnl_count(chan_type); 9138f83f268SJie Qiu mtk_hdmi_hw_aud_set_i2s_chan_num(hdmi, chan_type, chan_count); 9148f83f268SJie Qiu mtk_hdmi_hw_aud_set_input_type(hdmi, hdmi->aud_param.aud_input_type); 9158f83f268SJie Qiu 9168f83f268SJie Qiu return 0; 9178f83f268SJie Qiu } 9188f83f268SJie Qiu 9198f83f268SJie Qiu static int mtk_hdmi_aud_set_src(struct mtk_hdmi *hdmi, 9208f83f268SJie Qiu struct drm_display_mode *display_mode) 9218f83f268SJie Qiu { 9228f83f268SJie Qiu unsigned int sample_rate = hdmi->aud_param.codec_params.sample_rate; 9238f83f268SJie Qiu 9248f83f268SJie Qiu mtk_hdmi_aud_on_off_hw_ncts(hdmi, false); 9258f83f268SJie Qiu mtk_hdmi_hw_aud_src_disable(hdmi); 9268f83f268SJie Qiu mtk_hdmi_clear_bits(hdmi, GRL_CFG2, CFG2_ACLK_INV); 9278f83f268SJie Qiu 9288f83f268SJie Qiu if (hdmi->aud_param.aud_input_type == HDMI_AUD_INPUT_I2S) { 9298f83f268SJie Qiu switch (sample_rate) { 9308f83f268SJie Qiu case 32000: 9318f83f268SJie Qiu case 44100: 9328f83f268SJie Qiu case 48000: 9338f83f268SJie Qiu case 88200: 9348f83f268SJie Qiu case 96000: 9358f83f268SJie Qiu break; 9368f83f268SJie Qiu default: 9378f83f268SJie Qiu return -EINVAL; 9388f83f268SJie Qiu } 9398f83f268SJie Qiu mtk_hdmi_hw_aud_set_mclk(hdmi, hdmi->aud_param.aud_mclk); 9408f83f268SJie Qiu } else { 9418f83f268SJie Qiu switch (sample_rate) { 9428f83f268SJie Qiu case 32000: 9438f83f268SJie Qiu case 44100: 9448f83f268SJie Qiu case 48000: 9458f83f268SJie Qiu break; 9468f83f268SJie Qiu default: 9478f83f268SJie Qiu return -EINVAL; 9488f83f268SJie Qiu } 9498f83f268SJie Qiu mtk_hdmi_hw_aud_set_mclk(hdmi, HDMI_AUD_MCLK_128FS); 9508f83f268SJie Qiu } 9518f83f268SJie Qiu 9528f83f268SJie Qiu mtk_hdmi_hw_aud_set_ncts(hdmi, sample_rate, display_mode->clock); 9538f83f268SJie Qiu 9548f83f268SJie Qiu mtk_hdmi_hw_aud_src_reenable(hdmi); 9558f83f268SJie Qiu return 0; 9568f83f268SJie Qiu } 9578f83f268SJie Qiu 9588f83f268SJie Qiu static int mtk_hdmi_aud_output_config(struct mtk_hdmi *hdmi, 9598f83f268SJie Qiu struct drm_display_mode *display_mode) 9608f83f268SJie Qiu { 9618f83f268SJie Qiu mtk_hdmi_hw_aud_mute(hdmi); 9628f83f268SJie Qiu mtk_hdmi_aud_enable_packet(hdmi, false); 9638f83f268SJie Qiu 9648f83f268SJie Qiu mtk_hdmi_aud_set_input(hdmi); 9658f83f268SJie Qiu mtk_hdmi_aud_set_src(hdmi, display_mode); 9668f83f268SJie Qiu mtk_hdmi_hw_aud_set_channel_status(hdmi, 9678f83f268SJie Qiu hdmi->aud_param.codec_params.iec.status); 9688f83f268SJie Qiu 9698f83f268SJie Qiu usleep_range(50, 100); 9708f83f268SJie Qiu 9718f83f268SJie Qiu mtk_hdmi_aud_on_off_hw_ncts(hdmi, true); 9728f83f268SJie Qiu mtk_hdmi_aud_enable_packet(hdmi, true); 9738f83f268SJie Qiu mtk_hdmi_hw_aud_unmute(hdmi); 9748f83f268SJie Qiu return 0; 9758f83f268SJie Qiu } 9768f83f268SJie Qiu 9778f83f268SJie Qiu static int mtk_hdmi_setup_avi_infoframe(struct mtk_hdmi *hdmi, 9788f83f268SJie Qiu struct drm_display_mode *mode) 9798f83f268SJie Qiu { 9808f83f268SJie Qiu struct hdmi_avi_infoframe frame; 9813852489cSBernard Zhao u8 buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AVI_INFOFRAME_SIZE]; 9828f83f268SJie Qiu ssize_t err; 9838f83f268SJie Qiu 98413d0add3SVille Syrjälä err = drm_hdmi_avi_infoframe_from_display_mode(&frame, 98513d0add3SVille Syrjälä &hdmi->conn, mode); 9868f83f268SJie Qiu if (err < 0) { 9878f83f268SJie Qiu dev_err(hdmi->dev, 9888f83f268SJie Qiu "Failed to get AVI infoframe from mode: %zd\n", err); 9898f83f268SJie Qiu return err; 9908f83f268SJie Qiu } 9918f83f268SJie Qiu 9928f83f268SJie Qiu err = hdmi_avi_infoframe_pack(&frame, buffer, sizeof(buffer)); 9938f83f268SJie Qiu if (err < 0) { 9948f83f268SJie Qiu dev_err(hdmi->dev, "Failed to pack AVI infoframe: %zd\n", err); 9958f83f268SJie Qiu return err; 9968f83f268SJie Qiu } 9978f83f268SJie Qiu 9988f83f268SJie Qiu mtk_hdmi_hw_send_info_frame(hdmi, buffer, sizeof(buffer)); 9998f83f268SJie Qiu return 0; 10008f83f268SJie Qiu } 10018f83f268SJie Qiu 10028f83f268SJie Qiu static int mtk_hdmi_setup_spd_infoframe(struct mtk_hdmi *hdmi, 10038f83f268SJie Qiu const char *vendor, 10048f83f268SJie Qiu const char *product) 10058f83f268SJie Qiu { 10068f83f268SJie Qiu struct hdmi_spd_infoframe frame; 10073852489cSBernard Zhao u8 buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_SPD_INFOFRAME_SIZE]; 10088f83f268SJie Qiu ssize_t err; 10098f83f268SJie Qiu 10108f83f268SJie Qiu err = hdmi_spd_infoframe_init(&frame, vendor, product); 10118f83f268SJie Qiu if (err < 0) { 10128f83f268SJie Qiu dev_err(hdmi->dev, "Failed to initialize SPD infoframe: %zd\n", 10138f83f268SJie Qiu err); 10148f83f268SJie Qiu return err; 10158f83f268SJie Qiu } 10168f83f268SJie Qiu 10178f83f268SJie Qiu err = hdmi_spd_infoframe_pack(&frame, buffer, sizeof(buffer)); 10188f83f268SJie Qiu if (err < 0) { 10198f83f268SJie Qiu dev_err(hdmi->dev, "Failed to pack SDP infoframe: %zd\n", err); 10208f83f268SJie Qiu return err; 10218f83f268SJie Qiu } 10228f83f268SJie Qiu 10238f83f268SJie Qiu mtk_hdmi_hw_send_info_frame(hdmi, buffer, sizeof(buffer)); 10248f83f268SJie Qiu return 0; 10258f83f268SJie Qiu } 10268f83f268SJie Qiu 10278f83f268SJie Qiu static int mtk_hdmi_setup_audio_infoframe(struct mtk_hdmi *hdmi) 10288f83f268SJie Qiu { 10298f83f268SJie Qiu struct hdmi_audio_infoframe frame; 10303852489cSBernard Zhao u8 buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AUDIO_INFOFRAME_SIZE]; 10318f83f268SJie Qiu ssize_t err; 10328f83f268SJie Qiu 10338f83f268SJie Qiu err = hdmi_audio_infoframe_init(&frame); 10348f83f268SJie Qiu if (err < 0) { 10358f83f268SJie Qiu dev_err(hdmi->dev, "Failed to setup audio infoframe: %zd\n", 10368f83f268SJie Qiu err); 10378f83f268SJie Qiu return err; 10388f83f268SJie Qiu } 10398f83f268SJie Qiu 10408f83f268SJie Qiu frame.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM; 10418f83f268SJie Qiu frame.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM; 10428f83f268SJie Qiu frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM; 10438f83f268SJie Qiu frame.channels = mtk_hdmi_aud_get_chnl_count( 10448f83f268SJie Qiu hdmi->aud_param.aud_input_chan_type); 10458f83f268SJie Qiu 10468f83f268SJie Qiu err = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer)); 10478f83f268SJie Qiu if (err < 0) { 10488f83f268SJie Qiu dev_err(hdmi->dev, "Failed to pack audio infoframe: %zd\n", 10498f83f268SJie Qiu err); 10508f83f268SJie Qiu return err; 10518f83f268SJie Qiu } 10528f83f268SJie Qiu 10538f83f268SJie Qiu mtk_hdmi_hw_send_info_frame(hdmi, buffer, sizeof(buffer)); 10548f83f268SJie Qiu return 0; 10558f83f268SJie Qiu } 10568f83f268SJie Qiu 10578f83f268SJie Qiu static int mtk_hdmi_setup_vendor_specific_infoframe(struct mtk_hdmi *hdmi, 10588f83f268SJie Qiu struct drm_display_mode *mode) 10598f83f268SJie Qiu { 10608f83f268SJie Qiu struct hdmi_vendor_infoframe frame; 10618f83f268SJie Qiu u8 buffer[10]; 10628f83f268SJie Qiu ssize_t err; 10638f83f268SJie Qiu 1064f1781e9bSVille Syrjälä err = drm_hdmi_vendor_infoframe_from_display_mode(&frame, 1065f1781e9bSVille Syrjälä &hdmi->conn, mode); 10668f83f268SJie Qiu if (err) { 10678f83f268SJie Qiu dev_err(hdmi->dev, 10688f83f268SJie Qiu "Failed to get vendor infoframe from mode: %zd\n", err); 10698f83f268SJie Qiu return err; 10708f83f268SJie Qiu } 10718f83f268SJie Qiu 10728f83f268SJie Qiu err = hdmi_vendor_infoframe_pack(&frame, buffer, sizeof(buffer)); 1073014580ffSYYS if (err < 0) { 10748f83f268SJie Qiu dev_err(hdmi->dev, "Failed to pack vendor infoframe: %zd\n", 10758f83f268SJie Qiu err); 10768f83f268SJie Qiu return err; 10778f83f268SJie Qiu } 10788f83f268SJie Qiu 10798f83f268SJie Qiu mtk_hdmi_hw_send_info_frame(hdmi, buffer, sizeof(buffer)); 10808f83f268SJie Qiu return 0; 10818f83f268SJie Qiu } 10828f83f268SJie Qiu 10838f83f268SJie Qiu static int mtk_hdmi_output_init(struct mtk_hdmi *hdmi) 10848f83f268SJie Qiu { 10858f83f268SJie Qiu struct hdmi_audio_param *aud_param = &hdmi->aud_param; 10868f83f268SJie Qiu 10878f83f268SJie Qiu hdmi->csp = HDMI_COLORSPACE_RGB; 10888f83f268SJie Qiu aud_param->aud_codec = HDMI_AUDIO_CODING_TYPE_PCM; 10898f83f268SJie Qiu aud_param->aud_sampe_size = HDMI_AUDIO_SAMPLE_SIZE_16; 10908f83f268SJie Qiu aud_param->aud_input_type = HDMI_AUD_INPUT_I2S; 10918f83f268SJie Qiu aud_param->aud_i2s_fmt = HDMI_I2S_MODE_I2S_24BIT; 10928f83f268SJie Qiu aud_param->aud_mclk = HDMI_AUD_MCLK_128FS; 10938f83f268SJie Qiu aud_param->aud_input_chan_type = HDMI_AUD_CHAN_TYPE_2_0; 10948f83f268SJie Qiu 10958f83f268SJie Qiu return 0; 10968f83f268SJie Qiu } 10978f83f268SJie Qiu 1098188af070SBaoyou Xie static void mtk_hdmi_audio_enable(struct mtk_hdmi *hdmi) 10998f83f268SJie Qiu { 11008f83f268SJie Qiu mtk_hdmi_aud_enable_packet(hdmi, true); 11018f83f268SJie Qiu hdmi->audio_enable = true; 11028f83f268SJie Qiu } 11038f83f268SJie Qiu 1104188af070SBaoyou Xie static void mtk_hdmi_audio_disable(struct mtk_hdmi *hdmi) 11058f83f268SJie Qiu { 11068f83f268SJie Qiu mtk_hdmi_aud_enable_packet(hdmi, false); 11078f83f268SJie Qiu hdmi->audio_enable = false; 11088f83f268SJie Qiu } 11098f83f268SJie Qiu 1110188af070SBaoyou Xie static int mtk_hdmi_audio_set_param(struct mtk_hdmi *hdmi, 11118f83f268SJie Qiu struct hdmi_audio_param *param) 11128f83f268SJie Qiu { 11138f83f268SJie Qiu if (!hdmi->audio_enable) { 11148f83f268SJie Qiu dev_err(hdmi->dev, "hdmi audio is in disable state!\n"); 11158f83f268SJie Qiu return -EINVAL; 11168f83f268SJie Qiu } 11178f83f268SJie Qiu dev_dbg(hdmi->dev, "codec:%d, input:%d, channel:%d, fs:%d\n", 11188f83f268SJie Qiu param->aud_codec, param->aud_input_type, 11198f83f268SJie Qiu param->aud_input_chan_type, param->codec_params.sample_rate); 11208f83f268SJie Qiu memcpy(&hdmi->aud_param, param, sizeof(*param)); 11218f83f268SJie Qiu return mtk_hdmi_aud_output_config(hdmi, &hdmi->mode); 11228f83f268SJie Qiu } 11238f83f268SJie Qiu 11248f83f268SJie Qiu static int mtk_hdmi_output_set_display_mode(struct mtk_hdmi *hdmi, 11258f83f268SJie Qiu struct drm_display_mode *mode) 11268f83f268SJie Qiu { 11278f83f268SJie Qiu int ret; 11288f83f268SJie Qiu 11298f83f268SJie Qiu mtk_hdmi_hw_vid_black(hdmi, true); 11308f83f268SJie Qiu mtk_hdmi_hw_aud_mute(hdmi); 11318f83f268SJie Qiu mtk_hdmi_hw_send_av_mute(hdmi); 11328f83f268SJie Qiu phy_power_off(hdmi->phy); 11338f83f268SJie Qiu 11348f83f268SJie Qiu ret = mtk_hdmi_video_change_vpll(hdmi, 11358f83f268SJie Qiu mode->clock * 1000); 11368f83f268SJie Qiu if (ret) { 11378f83f268SJie Qiu dev_err(hdmi->dev, "Failed to set vpll: %d\n", ret); 11388f83f268SJie Qiu return ret; 11398f83f268SJie Qiu } 11408f83f268SJie Qiu mtk_hdmi_video_set_display_mode(hdmi, mode); 11418f83f268SJie Qiu 11428f83f268SJie Qiu phy_power_on(hdmi->phy); 11438f83f268SJie Qiu mtk_hdmi_aud_output_config(hdmi, mode); 11448f83f268SJie Qiu 11458f83f268SJie Qiu mtk_hdmi_hw_vid_black(hdmi, false); 11468f83f268SJie Qiu mtk_hdmi_hw_aud_unmute(hdmi); 11478f83f268SJie Qiu mtk_hdmi_hw_send_av_unmute(hdmi); 11488f83f268SJie Qiu 11498f83f268SJie Qiu return 0; 11508f83f268SJie Qiu } 11518f83f268SJie Qiu 11528f83f268SJie Qiu static const char * const mtk_hdmi_clk_names[MTK_HDMI_CLK_COUNT] = { 11538f83f268SJie Qiu [MTK_HDMI_CLK_HDMI_PIXEL] = "pixel", 11548f83f268SJie Qiu [MTK_HDMI_CLK_HDMI_PLL] = "pll", 11558f83f268SJie Qiu [MTK_HDMI_CLK_AUD_BCLK] = "bclk", 11568f83f268SJie Qiu [MTK_HDMI_CLK_AUD_SPDIF] = "spdif", 11578f83f268SJie Qiu }; 11588f83f268SJie Qiu 11598f83f268SJie Qiu static int mtk_hdmi_get_all_clk(struct mtk_hdmi *hdmi, 11608f83f268SJie Qiu struct device_node *np) 11618f83f268SJie Qiu { 11628f83f268SJie Qiu int i; 11638f83f268SJie Qiu 11648f83f268SJie Qiu for (i = 0; i < ARRAY_SIZE(mtk_hdmi_clk_names); i++) { 11658f83f268SJie Qiu hdmi->clk[i] = of_clk_get_by_name(np, 11668f83f268SJie Qiu mtk_hdmi_clk_names[i]); 11678f83f268SJie Qiu if (IS_ERR(hdmi->clk[i])) 11688f83f268SJie Qiu return PTR_ERR(hdmi->clk[i]); 11698f83f268SJie Qiu } 11708f83f268SJie Qiu return 0; 11718f83f268SJie Qiu } 11728f83f268SJie Qiu 11738f83f268SJie Qiu static int mtk_hdmi_clk_enable_audio(struct mtk_hdmi *hdmi) 11748f83f268SJie Qiu { 11758f83f268SJie Qiu int ret; 11768f83f268SJie Qiu 11778f83f268SJie Qiu ret = clk_prepare_enable(hdmi->clk[MTK_HDMI_CLK_AUD_BCLK]); 11788f83f268SJie Qiu if (ret) 11798f83f268SJie Qiu return ret; 11808f83f268SJie Qiu 11818f83f268SJie Qiu ret = clk_prepare_enable(hdmi->clk[MTK_HDMI_CLK_AUD_SPDIF]); 11828f83f268SJie Qiu if (ret) 11838f83f268SJie Qiu goto err; 11848f83f268SJie Qiu 11858f83f268SJie Qiu return 0; 11868f83f268SJie Qiu err: 11878f83f268SJie Qiu clk_disable_unprepare(hdmi->clk[MTK_HDMI_CLK_AUD_BCLK]); 11888f83f268SJie Qiu return ret; 11898f83f268SJie Qiu } 11908f83f268SJie Qiu 11918f83f268SJie Qiu static void mtk_hdmi_clk_disable_audio(struct mtk_hdmi *hdmi) 11928f83f268SJie Qiu { 11938f83f268SJie Qiu clk_disable_unprepare(hdmi->clk[MTK_HDMI_CLK_AUD_BCLK]); 11948f83f268SJie Qiu clk_disable_unprepare(hdmi->clk[MTK_HDMI_CLK_AUD_SPDIF]); 11958f83f268SJie Qiu } 11968f83f268SJie Qiu 11975d3c6447STzung-Bi Shih static enum drm_connector_status 11985d3c6447STzung-Bi Shih mtk_hdmi_update_plugged_status(struct mtk_hdmi *hdmi) 11995d3c6447STzung-Bi Shih { 1200f07980d4STzung-Bi Shih bool connected; 12015d3c6447STzung-Bi Shih 1202f07980d4STzung-Bi Shih mutex_lock(&hdmi->update_plugged_status_lock); 1203f07980d4STzung-Bi Shih connected = mtk_cec_hpd_high(hdmi->cec_dev); 12045d3c6447STzung-Bi Shih if (hdmi->plugged_cb && hdmi->codec_dev) 12055d3c6447STzung-Bi Shih hdmi->plugged_cb(hdmi->codec_dev, connected); 1206f07980d4STzung-Bi Shih mutex_unlock(&hdmi->update_plugged_status_lock); 12075d3c6447STzung-Bi Shih 12085d3c6447STzung-Bi Shih return connected ? 12095d3c6447STzung-Bi Shih connector_status_connected : connector_status_disconnected; 12105d3c6447STzung-Bi Shih } 12115d3c6447STzung-Bi Shih 12128f83f268SJie Qiu static enum drm_connector_status hdmi_conn_detect(struct drm_connector *conn, 12138f83f268SJie Qiu bool force) 12148f83f268SJie Qiu { 12158f83f268SJie Qiu struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn); 12165d3c6447STzung-Bi Shih return mtk_hdmi_update_plugged_status(hdmi); 12178f83f268SJie Qiu } 12188f83f268SJie Qiu 12198f83f268SJie Qiu static void hdmi_conn_destroy(struct drm_connector *conn) 12208f83f268SJie Qiu { 12218f83f268SJie Qiu struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn); 12228f83f268SJie Qiu 12238f83f268SJie Qiu mtk_cec_set_hpd_event(hdmi->cec_dev, NULL, NULL); 12248f83f268SJie Qiu 12258f83f268SJie Qiu drm_connector_cleanup(conn); 12268f83f268SJie Qiu } 12278f83f268SJie Qiu 12288f83f268SJie Qiu static int mtk_hdmi_conn_get_modes(struct drm_connector *conn) 12298f83f268SJie Qiu { 12308f83f268SJie Qiu struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn); 12318f83f268SJie Qiu struct edid *edid; 12328f83f268SJie Qiu int ret; 12338f83f268SJie Qiu 12348f83f268SJie Qiu if (!hdmi->ddc_adpt) 12358f83f268SJie Qiu return -ENODEV; 12368f83f268SJie Qiu 12378f83f268SJie Qiu edid = drm_get_edid(conn, hdmi->ddc_adpt); 12388f83f268SJie Qiu if (!edid) 12398f83f268SJie Qiu return -ENODEV; 12408f83f268SJie Qiu 12418f83f268SJie Qiu hdmi->dvi_mode = !drm_detect_monitor_audio(edid); 12428f83f268SJie Qiu 1243c555f023SDaniel Vetter drm_connector_update_edid_property(conn, edid); 12448f83f268SJie Qiu 12458f83f268SJie Qiu ret = drm_add_edid_modes(conn, edid); 12468f83f268SJie Qiu kfree(edid); 12478f83f268SJie Qiu return ret; 12488f83f268SJie Qiu } 12498f83f268SJie Qiu 12508f83f268SJie Qiu static int mtk_hdmi_conn_mode_valid(struct drm_connector *conn, 12518f83f268SJie Qiu struct drm_display_mode *mode) 12528f83f268SJie Qiu { 12538f83f268SJie Qiu struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn); 1254fadf872dSBoris Brezillon struct drm_bridge *next_bridge; 12558f83f268SJie Qiu 12568f83f268SJie Qiu dev_dbg(hdmi->dev, "xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n", 12578f83f268SJie Qiu mode->hdisplay, mode->vdisplay, mode->vrefresh, 12588f83f268SJie Qiu !!(mode->flags & DRM_MODE_FLAG_INTERLACE), mode->clock * 1000); 12598f83f268SJie Qiu 1260fadf872dSBoris Brezillon next_bridge = drm_bridge_get_next_bridge(&hdmi->bridge); 1261fadf872dSBoris Brezillon if (next_bridge) { 12628f83f268SJie Qiu struct drm_display_mode adjusted_mode; 12638f83f268SJie Qiu 12648f83f268SJie Qiu drm_mode_copy(&adjusted_mode, mode); 1265fadf872dSBoris Brezillon if (!drm_bridge_chain_mode_fixup(next_bridge, mode, 12668f83f268SJie Qiu &adjusted_mode)) 12678f83f268SJie Qiu return MODE_BAD; 12688f83f268SJie Qiu } 12698f83f268SJie Qiu 12708f83f268SJie Qiu if (mode->clock < 27000) 12718f83f268SJie Qiu return MODE_CLOCK_LOW; 12728f83f268SJie Qiu if (mode->clock > 297000) 12738f83f268SJie Qiu return MODE_CLOCK_HIGH; 12748f83f268SJie Qiu 12758f83f268SJie Qiu return drm_mode_validate_size(mode, 0x1fff, 0x1fff); 12768f83f268SJie Qiu } 12778f83f268SJie Qiu 12788f83f268SJie Qiu static struct drm_encoder *mtk_hdmi_conn_best_enc(struct drm_connector *conn) 12798f83f268SJie Qiu { 12808f83f268SJie Qiu struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn); 12818f83f268SJie Qiu 12828f83f268SJie Qiu return hdmi->bridge.encoder; 12838f83f268SJie Qiu } 12848f83f268SJie Qiu 12858f83f268SJie Qiu static const struct drm_connector_funcs mtk_hdmi_connector_funcs = { 12868f83f268SJie Qiu .detect = hdmi_conn_detect, 12878f83f268SJie Qiu .fill_modes = drm_helper_probe_single_connector_modes, 12888f83f268SJie Qiu .destroy = hdmi_conn_destroy, 12898f83f268SJie Qiu .reset = drm_atomic_helper_connector_reset, 12908f83f268SJie Qiu .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 12918f83f268SJie Qiu .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 12928f83f268SJie Qiu }; 12938f83f268SJie Qiu 12948f83f268SJie Qiu static const struct drm_connector_helper_funcs 12958f83f268SJie Qiu mtk_hdmi_connector_helper_funcs = { 12968f83f268SJie Qiu .get_modes = mtk_hdmi_conn_get_modes, 12978f83f268SJie Qiu .mode_valid = mtk_hdmi_conn_mode_valid, 12988f83f268SJie Qiu .best_encoder = mtk_hdmi_conn_best_enc, 12998f83f268SJie Qiu }; 13008f83f268SJie Qiu 13018f83f268SJie Qiu static void mtk_hdmi_hpd_event(bool hpd, struct device *dev) 13028f83f268SJie Qiu { 13038f83f268SJie Qiu struct mtk_hdmi *hdmi = dev_get_drvdata(dev); 13048f83f268SJie Qiu 13058f83f268SJie Qiu if (hdmi && hdmi->bridge.encoder && hdmi->bridge.encoder->dev) 13068f83f268SJie Qiu drm_helper_hpd_irq_event(hdmi->bridge.encoder->dev); 13078f83f268SJie Qiu } 13088f83f268SJie Qiu 13098f83f268SJie Qiu /* 13108f83f268SJie Qiu * Bridge callbacks 13118f83f268SJie Qiu */ 13128f83f268SJie Qiu 1313a25b988fSLaurent Pinchart static int mtk_hdmi_bridge_attach(struct drm_bridge *bridge, 1314a25b988fSLaurent Pinchart enum drm_bridge_attach_flags flags) 13158f83f268SJie Qiu { 13168f83f268SJie Qiu struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge); 13178f83f268SJie Qiu int ret; 13188f83f268SJie Qiu 1319a25b988fSLaurent Pinchart if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { 1320a25b988fSLaurent Pinchart DRM_ERROR("Fix bridge driver to make connector optional!"); 1321a25b988fSLaurent Pinchart return -EINVAL; 1322a25b988fSLaurent Pinchart } 1323a25b988fSLaurent Pinchart 13243577dc09SAndrzej Pietrasiewicz ret = drm_connector_init_with_ddc(bridge->encoder->dev, &hdmi->conn, 13258f83f268SJie Qiu &mtk_hdmi_connector_funcs, 13263577dc09SAndrzej Pietrasiewicz DRM_MODE_CONNECTOR_HDMIA, 13273577dc09SAndrzej Pietrasiewicz hdmi->ddc_adpt); 13288f83f268SJie Qiu if (ret) { 13298f83f268SJie Qiu dev_err(hdmi->dev, "Failed to initialize connector: %d\n", ret); 13308f83f268SJie Qiu return ret; 13318f83f268SJie Qiu } 13328f83f268SJie Qiu drm_connector_helper_add(&hdmi->conn, &mtk_hdmi_connector_helper_funcs); 13338f83f268SJie Qiu 13348f83f268SJie Qiu hdmi->conn.polled = DRM_CONNECTOR_POLL_HPD; 13358f83f268SJie Qiu hdmi->conn.interlace_allowed = true; 13368f83f268SJie Qiu hdmi->conn.doublescan_allowed = false; 13378f83f268SJie Qiu 1338cde4c44dSDaniel Vetter ret = drm_connector_attach_encoder(&hdmi->conn, 13398f83f268SJie Qiu bridge->encoder); 13408f83f268SJie Qiu if (ret) { 13418f83f268SJie Qiu dev_err(hdmi->dev, 13428f83f268SJie Qiu "Failed to attach connector to encoder: %d\n", ret); 13438f83f268SJie Qiu return ret; 13448f83f268SJie Qiu } 13458f83f268SJie Qiu 13463bb80f24SLaurent Pinchart if (hdmi->next_bridge) { 13473bb80f24SLaurent Pinchart ret = drm_bridge_attach(bridge->encoder, hdmi->next_bridge, 1348a25b988fSLaurent Pinchart bridge, flags); 13498f83f268SJie Qiu if (ret) { 13508f83f268SJie Qiu dev_err(hdmi->dev, 13518f83f268SJie Qiu "Failed to attach external bridge: %d\n", ret); 13528f83f268SJie Qiu return ret; 13538f83f268SJie Qiu } 13548f83f268SJie Qiu } 13558f83f268SJie Qiu 13568f83f268SJie Qiu mtk_cec_set_hpd_event(hdmi->cec_dev, mtk_hdmi_hpd_event, hdmi->dev); 13578f83f268SJie Qiu 13588f83f268SJie Qiu return 0; 13598f83f268SJie Qiu } 13608f83f268SJie Qiu 13618f83f268SJie Qiu static bool mtk_hdmi_bridge_mode_fixup(struct drm_bridge *bridge, 13628f83f268SJie Qiu const struct drm_display_mode *mode, 13638f83f268SJie Qiu struct drm_display_mode *adjusted_mode) 13648f83f268SJie Qiu { 13658f83f268SJie Qiu return true; 13668f83f268SJie Qiu } 13678f83f268SJie Qiu 13688f83f268SJie Qiu static void mtk_hdmi_bridge_disable(struct drm_bridge *bridge) 13698f83f268SJie Qiu { 13708f83f268SJie Qiu struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge); 13718f83f268SJie Qiu 13728f83f268SJie Qiu if (!hdmi->enabled) 13738f83f268SJie Qiu return; 13748f83f268SJie Qiu 13758f83f268SJie Qiu phy_power_off(hdmi->phy); 13768f83f268SJie Qiu clk_disable_unprepare(hdmi->clk[MTK_HDMI_CLK_HDMI_PIXEL]); 13778f83f268SJie Qiu clk_disable_unprepare(hdmi->clk[MTK_HDMI_CLK_HDMI_PLL]); 13788f83f268SJie Qiu 13798f83f268SJie Qiu hdmi->enabled = false; 13808f83f268SJie Qiu } 13818f83f268SJie Qiu 13828f83f268SJie Qiu static void mtk_hdmi_bridge_post_disable(struct drm_bridge *bridge) 13838f83f268SJie Qiu { 13848f83f268SJie Qiu struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge); 13858f83f268SJie Qiu 13868f83f268SJie Qiu if (!hdmi->powered) 13878f83f268SJie Qiu return; 13888f83f268SJie Qiu 13898f83f268SJie Qiu mtk_hdmi_hw_1p4_version_enable(hdmi, true); 13908f83f268SJie Qiu mtk_hdmi_hw_make_reg_writable(hdmi, false); 13918f83f268SJie Qiu 13928f83f268SJie Qiu hdmi->powered = false; 13938f83f268SJie Qiu } 13948f83f268SJie Qiu 13958f83f268SJie Qiu static void mtk_hdmi_bridge_mode_set(struct drm_bridge *bridge, 139663f8f3baSLaurent Pinchart const struct drm_display_mode *mode, 139763f8f3baSLaurent Pinchart const struct drm_display_mode *adjusted_mode) 13988f83f268SJie Qiu { 13998f83f268SJie Qiu struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge); 14008f83f268SJie Qiu 14018f83f268SJie Qiu dev_dbg(hdmi->dev, "cur info: name:%s, hdisplay:%d\n", 14028f83f268SJie Qiu adjusted_mode->name, adjusted_mode->hdisplay); 14038f83f268SJie Qiu dev_dbg(hdmi->dev, "hsync_start:%d,hsync_end:%d, htotal:%d", 14048f83f268SJie Qiu adjusted_mode->hsync_start, adjusted_mode->hsync_end, 14058f83f268SJie Qiu adjusted_mode->htotal); 14068f83f268SJie Qiu dev_dbg(hdmi->dev, "hskew:%d, vdisplay:%d\n", 14078f83f268SJie Qiu adjusted_mode->hskew, adjusted_mode->vdisplay); 14088f83f268SJie Qiu dev_dbg(hdmi->dev, "vsync_start:%d, vsync_end:%d, vtotal:%d", 14098f83f268SJie Qiu adjusted_mode->vsync_start, adjusted_mode->vsync_end, 14108f83f268SJie Qiu adjusted_mode->vtotal); 14118f83f268SJie Qiu dev_dbg(hdmi->dev, "vscan:%d, flag:%d\n", 14128f83f268SJie Qiu adjusted_mode->vscan, adjusted_mode->flags); 14138f83f268SJie Qiu 14148f83f268SJie Qiu drm_mode_copy(&hdmi->mode, adjusted_mode); 14158f83f268SJie Qiu } 14168f83f268SJie Qiu 14178f83f268SJie Qiu static void mtk_hdmi_bridge_pre_enable(struct drm_bridge *bridge) 14188f83f268SJie Qiu { 14198f83f268SJie Qiu struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge); 14208f83f268SJie Qiu 14218f83f268SJie Qiu mtk_hdmi_hw_make_reg_writable(hdmi, true); 14228f83f268SJie Qiu mtk_hdmi_hw_1p4_version_enable(hdmi, true); 14238f83f268SJie Qiu 14248f83f268SJie Qiu hdmi->powered = true; 14258f83f268SJie Qiu } 14268f83f268SJie Qiu 1427d542b7c4SJunzhi Zhao static void mtk_hdmi_send_infoframe(struct mtk_hdmi *hdmi, 1428d542b7c4SJunzhi Zhao struct drm_display_mode *mode) 1429d542b7c4SJunzhi Zhao { 1430d542b7c4SJunzhi Zhao mtk_hdmi_setup_audio_infoframe(hdmi); 1431d542b7c4SJunzhi Zhao mtk_hdmi_setup_avi_infoframe(hdmi, mode); 1432d542b7c4SJunzhi Zhao mtk_hdmi_setup_spd_infoframe(hdmi, "mediatek", "On-chip HDMI"); 1433d542b7c4SJunzhi Zhao if (mode->flags & DRM_MODE_FLAG_3D_MASK) 1434d542b7c4SJunzhi Zhao mtk_hdmi_setup_vendor_specific_infoframe(hdmi, mode); 1435d542b7c4SJunzhi Zhao } 1436d542b7c4SJunzhi Zhao 14378f83f268SJie Qiu static void mtk_hdmi_bridge_enable(struct drm_bridge *bridge) 14388f83f268SJie Qiu { 14398f83f268SJie Qiu struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge); 14408f83f268SJie Qiu 14418f83f268SJie Qiu mtk_hdmi_output_set_display_mode(hdmi, &hdmi->mode); 14428f83f268SJie Qiu clk_prepare_enable(hdmi->clk[MTK_HDMI_CLK_HDMI_PLL]); 14438f83f268SJie Qiu clk_prepare_enable(hdmi->clk[MTK_HDMI_CLK_HDMI_PIXEL]); 14448f83f268SJie Qiu phy_power_on(hdmi->phy); 1445d542b7c4SJunzhi Zhao mtk_hdmi_send_infoframe(hdmi, &hdmi->mode); 14468f83f268SJie Qiu 14478f83f268SJie Qiu hdmi->enabled = true; 14488f83f268SJie Qiu } 14498f83f268SJie Qiu 14508f83f268SJie Qiu static const struct drm_bridge_funcs mtk_hdmi_bridge_funcs = { 14518f83f268SJie Qiu .attach = mtk_hdmi_bridge_attach, 14528f83f268SJie Qiu .mode_fixup = mtk_hdmi_bridge_mode_fixup, 14538f83f268SJie Qiu .disable = mtk_hdmi_bridge_disable, 14548f83f268SJie Qiu .post_disable = mtk_hdmi_bridge_post_disable, 14558f83f268SJie Qiu .mode_set = mtk_hdmi_bridge_mode_set, 14568f83f268SJie Qiu .pre_enable = mtk_hdmi_bridge_pre_enable, 14578f83f268SJie Qiu .enable = mtk_hdmi_bridge_enable, 14588f83f268SJie Qiu }; 14598f83f268SJie Qiu 14608f83f268SJie Qiu static int mtk_hdmi_dt_parse_pdata(struct mtk_hdmi *hdmi, 14618f83f268SJie Qiu struct platform_device *pdev) 14628f83f268SJie Qiu { 14638f83f268SJie Qiu struct device *dev = &pdev->dev; 14648f83f268SJie Qiu struct device_node *np = dev->of_node; 146586418f90SRob Herring struct device_node *cec_np, *remote, *i2c_np; 14668f83f268SJie Qiu struct platform_device *cec_pdev; 14678f83f268SJie Qiu struct regmap *regmap; 14688f83f268SJie Qiu struct resource *mem; 14698f83f268SJie Qiu int ret; 14708f83f268SJie Qiu 14718f83f268SJie Qiu ret = mtk_hdmi_get_all_clk(hdmi, np); 14728f83f268SJie Qiu if (ret) { 1473af19d645SMatthias Brugger if (ret != -EPROBE_DEFER) 14748f83f268SJie Qiu dev_err(dev, "Failed to get clocks: %d\n", ret); 1475af19d645SMatthias Brugger 14768f83f268SJie Qiu return ret; 14778f83f268SJie Qiu } 14788f83f268SJie Qiu 14798f83f268SJie Qiu /* The CEC module handles HDMI hotplug detection */ 1480ceff2f4dSJohan Hovold cec_np = of_get_compatible_child(np->parent, "mediatek,mt8173-cec"); 14818f83f268SJie Qiu if (!cec_np) { 14828f83f268SJie Qiu dev_err(dev, "Failed to find CEC node\n"); 14838f83f268SJie Qiu return -EINVAL; 14848f83f268SJie Qiu } 14858f83f268SJie Qiu 14868f83f268SJie Qiu cec_pdev = of_find_device_by_node(cec_np); 14878f83f268SJie Qiu if (!cec_pdev) { 14884bf99144SRob Herring dev_err(hdmi->dev, "Waiting for CEC device %pOF\n", 14894bf99144SRob Herring cec_np); 1490ceff2f4dSJohan Hovold of_node_put(cec_np); 14918f83f268SJie Qiu return -EPROBE_DEFER; 14928f83f268SJie Qiu } 1493ceff2f4dSJohan Hovold of_node_put(cec_np); 14948f83f268SJie Qiu hdmi->cec_dev = &cec_pdev->dev; 14958f83f268SJie Qiu 14968f83f268SJie Qiu /* 14978f83f268SJie Qiu * The mediatek,syscon-hdmi property contains a phandle link to the 14988f83f268SJie Qiu * MMSYS_CONFIG device and the register offset of the HDMI_SYS_CFG 14998f83f268SJie Qiu * registers it contains. 15008f83f268SJie Qiu */ 15018f83f268SJie Qiu regmap = syscon_regmap_lookup_by_phandle(np, "mediatek,syscon-hdmi"); 15028f83f268SJie Qiu ret = of_property_read_u32_index(np, "mediatek,syscon-hdmi", 1, 15038f83f268SJie Qiu &hdmi->sys_offset); 15048f83f268SJie Qiu if (IS_ERR(regmap)) 15058f83f268SJie Qiu ret = PTR_ERR(regmap); 15068f83f268SJie Qiu if (ret) { 15078f83f268SJie Qiu dev_err(dev, 15088f83f268SJie Qiu "Failed to get system configuration registers: %d\n", 15098f83f268SJie Qiu ret); 15108f83f268SJie Qiu return ret; 15118f83f268SJie Qiu } 15128f83f268SJie Qiu hdmi->sys_regmap = regmap; 15138f83f268SJie Qiu 15148f83f268SJie Qiu mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 15158f83f268SJie Qiu hdmi->regs = devm_ioremap_resource(dev, mem); 15168f83f268SJie Qiu if (IS_ERR(hdmi->regs)) 15178f83f268SJie Qiu return PTR_ERR(hdmi->regs); 15188f83f268SJie Qiu 151986418f90SRob Herring remote = of_graph_get_remote_node(np, 1, 0); 152086418f90SRob Herring if (!remote) 15218f83f268SJie Qiu return -EINVAL; 15228f83f268SJie Qiu 15238f83f268SJie Qiu if (!of_device_is_compatible(remote, "hdmi-connector")) { 15243bb80f24SLaurent Pinchart hdmi->next_bridge = of_drm_find_bridge(remote); 15253bb80f24SLaurent Pinchart if (!hdmi->next_bridge) { 15268f83f268SJie Qiu dev_err(dev, "Waiting for external bridge\n"); 15278f83f268SJie Qiu of_node_put(remote); 15288f83f268SJie Qiu return -EPROBE_DEFER; 15298f83f268SJie Qiu } 15308f83f268SJie Qiu } 15318f83f268SJie Qiu 15328f83f268SJie Qiu i2c_np = of_parse_phandle(remote, "ddc-i2c-bus", 0); 15338f83f268SJie Qiu if (!i2c_np) { 15344bf99144SRob Herring dev_err(dev, "Failed to find ddc-i2c-bus node in %pOF\n", 15354bf99144SRob Herring remote); 15368f83f268SJie Qiu of_node_put(remote); 15378f83f268SJie Qiu return -EINVAL; 15388f83f268SJie Qiu } 15398f83f268SJie Qiu of_node_put(remote); 15408f83f268SJie Qiu 15418f83f268SJie Qiu hdmi->ddc_adpt = of_find_i2c_adapter_by_node(i2c_np); 15422ae2c331SWen Yang of_node_put(i2c_np); 15438f83f268SJie Qiu if (!hdmi->ddc_adpt) { 15448f83f268SJie Qiu dev_err(dev, "Failed to get ddc i2c adapter by node\n"); 15458f83f268SJie Qiu return -EINVAL; 15468f83f268SJie Qiu } 15478f83f268SJie Qiu 15488f83f268SJie Qiu return 0; 15498f83f268SJie Qiu } 15508f83f268SJie Qiu 15518f83f268SJie Qiu /* 15528f83f268SJie Qiu * HDMI audio codec callbacks 15538f83f268SJie Qiu */ 15548f83f268SJie Qiu 15555dd0775eSDave Airlie static int mtk_hdmi_audio_hw_params(struct device *dev, void *data, 15568f83f268SJie Qiu struct hdmi_codec_daifmt *daifmt, 15578f83f268SJie Qiu struct hdmi_codec_params *params) 15588f83f268SJie Qiu { 15598f83f268SJie Qiu struct mtk_hdmi *hdmi = dev_get_drvdata(dev); 15608f83f268SJie Qiu struct hdmi_audio_param hdmi_params; 15618f83f268SJie Qiu unsigned int chan = params->cea.channels; 15628f83f268SJie Qiu 15638f83f268SJie Qiu dev_dbg(hdmi->dev, "%s: %u Hz, %d bit, %d channels\n", __func__, 15648f83f268SJie Qiu params->sample_rate, params->sample_width, chan); 15658f83f268SJie Qiu 15668f83f268SJie Qiu if (!hdmi->bridge.encoder) 15678f83f268SJie Qiu return -ENODEV; 15688f83f268SJie Qiu 15698f83f268SJie Qiu switch (chan) { 15708f83f268SJie Qiu case 2: 15718f83f268SJie Qiu hdmi_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_2_0; 15728f83f268SJie Qiu break; 15738f83f268SJie Qiu case 4: 15748f83f268SJie Qiu hdmi_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_4_0; 15758f83f268SJie Qiu break; 15768f83f268SJie Qiu case 6: 15778f83f268SJie Qiu hdmi_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_5_1; 15788f83f268SJie Qiu break; 15798f83f268SJie Qiu case 8: 15808f83f268SJie Qiu hdmi_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_7_1; 15818f83f268SJie Qiu break; 15828f83f268SJie Qiu default: 15838f83f268SJie Qiu dev_err(hdmi->dev, "channel[%d] not supported!\n", chan); 15848f83f268SJie Qiu return -EINVAL; 15858f83f268SJie Qiu } 15868f83f268SJie Qiu 15878f83f268SJie Qiu switch (params->sample_rate) { 15888f83f268SJie Qiu case 32000: 15898f83f268SJie Qiu case 44100: 15908f83f268SJie Qiu case 48000: 15918f83f268SJie Qiu case 88200: 15928f83f268SJie Qiu case 96000: 15938f83f268SJie Qiu case 176400: 15948f83f268SJie Qiu case 192000: 15958f83f268SJie Qiu break; 15968f83f268SJie Qiu default: 15978f83f268SJie Qiu dev_err(hdmi->dev, "rate[%d] not supported!\n", 15988f83f268SJie Qiu params->sample_rate); 15998f83f268SJie Qiu return -EINVAL; 16008f83f268SJie Qiu } 16018f83f268SJie Qiu 16028f83f268SJie Qiu switch (daifmt->fmt) { 16038f83f268SJie Qiu case HDMI_I2S: 16048f83f268SJie Qiu hdmi_params.aud_codec = HDMI_AUDIO_CODING_TYPE_PCM; 16058f83f268SJie Qiu hdmi_params.aud_sampe_size = HDMI_AUDIO_SAMPLE_SIZE_16; 16068f83f268SJie Qiu hdmi_params.aud_input_type = HDMI_AUD_INPUT_I2S; 16078f83f268SJie Qiu hdmi_params.aud_i2s_fmt = HDMI_I2S_MODE_I2S_24BIT; 16088f83f268SJie Qiu hdmi_params.aud_mclk = HDMI_AUD_MCLK_128FS; 16098f83f268SJie Qiu break; 1610d1ef028dSchunhui dai case HDMI_SPDIF: 1611d1ef028dSchunhui dai hdmi_params.aud_codec = HDMI_AUDIO_CODING_TYPE_PCM; 1612d1ef028dSchunhui dai hdmi_params.aud_sampe_size = HDMI_AUDIO_SAMPLE_SIZE_16; 1613d1ef028dSchunhui dai hdmi_params.aud_input_type = HDMI_AUD_INPUT_SPDIF; 1614d1ef028dSchunhui dai break; 16158f83f268SJie Qiu default: 16168f83f268SJie Qiu dev_err(hdmi->dev, "%s: Invalid DAI format %d\n", __func__, 16178f83f268SJie Qiu daifmt->fmt); 16188f83f268SJie Qiu return -EINVAL; 16198f83f268SJie Qiu } 16208f83f268SJie Qiu 16218f83f268SJie Qiu memcpy(&hdmi_params.codec_params, params, 16228f83f268SJie Qiu sizeof(hdmi_params.codec_params)); 16238f83f268SJie Qiu 16248f83f268SJie Qiu mtk_hdmi_audio_set_param(hdmi, &hdmi_params); 16258f83f268SJie Qiu 16268f83f268SJie Qiu return 0; 16278f83f268SJie Qiu } 16288f83f268SJie Qiu 16295dd0775eSDave Airlie static int mtk_hdmi_audio_startup(struct device *dev, void *data) 16308f83f268SJie Qiu { 16318f83f268SJie Qiu struct mtk_hdmi *hdmi = dev_get_drvdata(dev); 16328f83f268SJie Qiu 16338f83f268SJie Qiu dev_dbg(dev, "%s\n", __func__); 16348f83f268SJie Qiu 16358f83f268SJie Qiu mtk_hdmi_audio_enable(hdmi); 16368f83f268SJie Qiu 16378f83f268SJie Qiu return 0; 16388f83f268SJie Qiu } 16398f83f268SJie Qiu 16405dd0775eSDave Airlie static void mtk_hdmi_audio_shutdown(struct device *dev, void *data) 16418f83f268SJie Qiu { 16428f83f268SJie Qiu struct mtk_hdmi *hdmi = dev_get_drvdata(dev); 16438f83f268SJie Qiu 16448f83f268SJie Qiu dev_dbg(dev, "%s\n", __func__); 16458f83f268SJie Qiu 16468f83f268SJie Qiu mtk_hdmi_audio_disable(hdmi); 16478f83f268SJie Qiu } 16488f83f268SJie Qiu 1649188af070SBaoyou Xie static int 1650*d789710fSKuninori Morimoto mtk_hdmi_audio_mute(struct device *dev, void *data, 1651*d789710fSKuninori Morimoto bool enable, int direction) 16528f83f268SJie Qiu { 16538f83f268SJie Qiu struct mtk_hdmi *hdmi = dev_get_drvdata(dev); 16548f83f268SJie Qiu 16558f83f268SJie Qiu dev_dbg(dev, "%s(%d)\n", __func__, enable); 16568f83f268SJie Qiu 16578f83f268SJie Qiu if (enable) 16588f83f268SJie Qiu mtk_hdmi_hw_aud_mute(hdmi); 16598f83f268SJie Qiu else 16608f83f268SJie Qiu mtk_hdmi_hw_aud_unmute(hdmi); 16618f83f268SJie Qiu 16628f83f268SJie Qiu return 0; 16638f83f268SJie Qiu } 16648f83f268SJie Qiu 16655dd0775eSDave Airlie static int mtk_hdmi_audio_get_eld(struct device *dev, void *data, uint8_t *buf, size_t len) 16668f83f268SJie Qiu { 16678f83f268SJie Qiu struct mtk_hdmi *hdmi = dev_get_drvdata(dev); 16688f83f268SJie Qiu 16698f83f268SJie Qiu dev_dbg(dev, "%s\n", __func__); 16708f83f268SJie Qiu 16718f83f268SJie Qiu memcpy(buf, hdmi->conn.eld, min(sizeof(hdmi->conn.eld), len)); 16728f83f268SJie Qiu 16738f83f268SJie Qiu return 0; 16748f83f268SJie Qiu } 16758f83f268SJie Qiu 16765d3c6447STzung-Bi Shih static int mtk_hdmi_audio_hook_plugged_cb(struct device *dev, void *data, 16775d3c6447STzung-Bi Shih hdmi_codec_plugged_cb fn, 16785d3c6447STzung-Bi Shih struct device *codec_dev) 16795d3c6447STzung-Bi Shih { 16805d3c6447STzung-Bi Shih struct mtk_hdmi *hdmi = data; 16815d3c6447STzung-Bi Shih 1682f07980d4STzung-Bi Shih mutex_lock(&hdmi->update_plugged_status_lock); 16835d3c6447STzung-Bi Shih hdmi->plugged_cb = fn; 16845d3c6447STzung-Bi Shih hdmi->codec_dev = codec_dev; 1685f07980d4STzung-Bi Shih mutex_unlock(&hdmi->update_plugged_status_lock); 1686f07980d4STzung-Bi Shih 16875d3c6447STzung-Bi Shih mtk_hdmi_update_plugged_status(hdmi); 16885d3c6447STzung-Bi Shih 16895d3c6447STzung-Bi Shih return 0; 16905d3c6447STzung-Bi Shih } 16915d3c6447STzung-Bi Shih 16928f83f268SJie Qiu static const struct hdmi_codec_ops mtk_hdmi_audio_codec_ops = { 16938f83f268SJie Qiu .hw_params = mtk_hdmi_audio_hw_params, 16948f83f268SJie Qiu .audio_startup = mtk_hdmi_audio_startup, 16958f83f268SJie Qiu .audio_shutdown = mtk_hdmi_audio_shutdown, 1696*d789710fSKuninori Morimoto .mute_stream = mtk_hdmi_audio_mute, 16978f83f268SJie Qiu .get_eld = mtk_hdmi_audio_get_eld, 16985d3c6447STzung-Bi Shih .hook_plugged_cb = mtk_hdmi_audio_hook_plugged_cb, 1699*d789710fSKuninori Morimoto .no_capture_mute = 1, 17008f83f268SJie Qiu }; 17018f83f268SJie Qiu 1702f9eb06cdSTzung-Bi Shih static int mtk_hdmi_register_audio_driver(struct device *dev) 17038f83f268SJie Qiu { 17045d3c6447STzung-Bi Shih struct mtk_hdmi *hdmi = dev_get_drvdata(dev); 17058f83f268SJie Qiu struct hdmi_codec_pdata codec_data = { 17068f83f268SJie Qiu .ops = &mtk_hdmi_audio_codec_ops, 17078f83f268SJie Qiu .max_i2s_channels = 2, 17088f83f268SJie Qiu .i2s = 1, 17095d3c6447STzung-Bi Shih .data = hdmi, 17108f83f268SJie Qiu }; 17118f83f268SJie Qiu struct platform_device *pdev; 17128f83f268SJie Qiu 17138f83f268SJie Qiu pdev = platform_device_register_data(dev, HDMI_CODEC_DRV_NAME, 17148f83f268SJie Qiu PLATFORM_DEVID_AUTO, &codec_data, 17158f83f268SJie Qiu sizeof(codec_data)); 17168f83f268SJie Qiu if (IS_ERR(pdev)) 1717f9eb06cdSTzung-Bi Shih return PTR_ERR(pdev); 17188f83f268SJie Qiu 17198f83f268SJie Qiu DRM_INFO("%s driver bound to HDMI\n", HDMI_CODEC_DRV_NAME); 1720f9eb06cdSTzung-Bi Shih return 0; 17218f83f268SJie Qiu } 17228f83f268SJie Qiu 17238f83f268SJie Qiu static int mtk_drm_hdmi_probe(struct platform_device *pdev) 17248f83f268SJie Qiu { 17258f83f268SJie Qiu struct mtk_hdmi *hdmi; 17268f83f268SJie Qiu struct device *dev = &pdev->dev; 17278f83f268SJie Qiu int ret; 17288f83f268SJie Qiu 17298f83f268SJie Qiu hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); 17308f83f268SJie Qiu if (!hdmi) 17318f83f268SJie Qiu return -ENOMEM; 17328f83f268SJie Qiu 17338f83f268SJie Qiu hdmi->dev = dev; 17348f83f268SJie Qiu 17358f83f268SJie Qiu ret = mtk_hdmi_dt_parse_pdata(hdmi, pdev); 17368f83f268SJie Qiu if (ret) 17378f83f268SJie Qiu return ret; 17388f83f268SJie Qiu 17398f83f268SJie Qiu hdmi->phy = devm_phy_get(dev, "hdmi"); 17408f83f268SJie Qiu if (IS_ERR(hdmi->phy)) { 17418f83f268SJie Qiu ret = PTR_ERR(hdmi->phy); 17428f83f268SJie Qiu dev_err(dev, "Failed to get HDMI PHY: %d\n", ret); 17438f83f268SJie Qiu return ret; 17448f83f268SJie Qiu } 17458f83f268SJie Qiu 1746f07980d4STzung-Bi Shih mutex_init(&hdmi->update_plugged_status_lock); 17478f83f268SJie Qiu platform_set_drvdata(pdev, hdmi); 17488f83f268SJie Qiu 17498f83f268SJie Qiu ret = mtk_hdmi_output_init(hdmi); 17508f83f268SJie Qiu if (ret) { 17518f83f268SJie Qiu dev_err(dev, "Failed to initialize hdmi output\n"); 17528f83f268SJie Qiu return ret; 17538f83f268SJie Qiu } 17548f83f268SJie Qiu 1755f9eb06cdSTzung-Bi Shih ret = mtk_hdmi_register_audio_driver(dev); 1756f9eb06cdSTzung-Bi Shih if (ret) { 1757f9eb06cdSTzung-Bi Shih dev_err(dev, "Failed to register audio driver: %d\n", ret); 1758f9eb06cdSTzung-Bi Shih return ret; 1759f9eb06cdSTzung-Bi Shih } 17608f83f268SJie Qiu 17618f83f268SJie Qiu hdmi->bridge.funcs = &mtk_hdmi_bridge_funcs; 17628f83f268SJie Qiu hdmi->bridge.of_node = pdev->dev.of_node; 1763e6910091SInki Dae drm_bridge_add(&hdmi->bridge); 17648f83f268SJie Qiu 17658f83f268SJie Qiu ret = mtk_hdmi_clk_enable_audio(hdmi); 17668f83f268SJie Qiu if (ret) { 17678f83f268SJie Qiu dev_err(dev, "Failed to enable audio clocks: %d\n", ret); 17688f83f268SJie Qiu goto err_bridge_remove; 17698f83f268SJie Qiu } 17708f83f268SJie Qiu 17718f83f268SJie Qiu dev_dbg(dev, "mediatek hdmi probe success\n"); 17728f83f268SJie Qiu return 0; 17738f83f268SJie Qiu 17748f83f268SJie Qiu err_bridge_remove: 17758f83f268SJie Qiu drm_bridge_remove(&hdmi->bridge); 17768f83f268SJie Qiu return ret; 17778f83f268SJie Qiu } 17788f83f268SJie Qiu 17798f83f268SJie Qiu static int mtk_drm_hdmi_remove(struct platform_device *pdev) 17808f83f268SJie Qiu { 17818f83f268SJie Qiu struct mtk_hdmi *hdmi = platform_get_drvdata(pdev); 17828f83f268SJie Qiu 17838f83f268SJie Qiu drm_bridge_remove(&hdmi->bridge); 17848f83f268SJie Qiu mtk_hdmi_clk_disable_audio(hdmi); 17858f83f268SJie Qiu return 0; 17868f83f268SJie Qiu } 17878f83f268SJie Qiu 17888f83f268SJie Qiu #ifdef CONFIG_PM_SLEEP 17898f83f268SJie Qiu static int mtk_hdmi_suspend(struct device *dev) 17908f83f268SJie Qiu { 17918f83f268SJie Qiu struct mtk_hdmi *hdmi = dev_get_drvdata(dev); 17928f83f268SJie Qiu 17938f83f268SJie Qiu mtk_hdmi_clk_disable_audio(hdmi); 17948f83f268SJie Qiu dev_dbg(dev, "hdmi suspend success!\n"); 17958f83f268SJie Qiu return 0; 17968f83f268SJie Qiu } 17978f83f268SJie Qiu 17988f83f268SJie Qiu static int mtk_hdmi_resume(struct device *dev) 17998f83f268SJie Qiu { 18008f83f268SJie Qiu struct mtk_hdmi *hdmi = dev_get_drvdata(dev); 18018f83f268SJie Qiu int ret = 0; 18028f83f268SJie Qiu 18038f83f268SJie Qiu ret = mtk_hdmi_clk_enable_audio(hdmi); 18048f83f268SJie Qiu if (ret) { 18058f83f268SJie Qiu dev_err(dev, "hdmi resume failed!\n"); 18068f83f268SJie Qiu return ret; 18078f83f268SJie Qiu } 18088f83f268SJie Qiu 18098f83f268SJie Qiu dev_dbg(dev, "hdmi resume success!\n"); 18108f83f268SJie Qiu return 0; 18118f83f268SJie Qiu } 18128f83f268SJie Qiu #endif 18138f83f268SJie Qiu static SIMPLE_DEV_PM_OPS(mtk_hdmi_pm_ops, 18148f83f268SJie Qiu mtk_hdmi_suspend, mtk_hdmi_resume); 18158f83f268SJie Qiu 18168f83f268SJie Qiu static const struct of_device_id mtk_drm_hdmi_of_ids[] = { 18178f83f268SJie Qiu { .compatible = "mediatek,mt8173-hdmi", }, 18188f83f268SJie Qiu {} 18198f83f268SJie Qiu }; 18208f83f268SJie Qiu 18218f83f268SJie Qiu static struct platform_driver mtk_hdmi_driver = { 18228f83f268SJie Qiu .probe = mtk_drm_hdmi_probe, 18238f83f268SJie Qiu .remove = mtk_drm_hdmi_remove, 18248f83f268SJie Qiu .driver = { 18258f83f268SJie Qiu .name = "mediatek-drm-hdmi", 18268f83f268SJie Qiu .of_match_table = mtk_drm_hdmi_of_ids, 18278f83f268SJie Qiu .pm = &mtk_hdmi_pm_ops, 18288f83f268SJie Qiu }, 18298f83f268SJie Qiu }; 18308f83f268SJie Qiu 18318f83f268SJie Qiu static struct platform_driver * const mtk_hdmi_drivers[] = { 18328f83f268SJie Qiu &mtk_hdmi_phy_driver, 18338f83f268SJie Qiu &mtk_hdmi_ddc_driver, 18348f83f268SJie Qiu &mtk_cec_driver, 18358f83f268SJie Qiu &mtk_hdmi_driver, 18368f83f268SJie Qiu }; 18378f83f268SJie Qiu 18388f83f268SJie Qiu static int __init mtk_hdmitx_init(void) 18398f83f268SJie Qiu { 1840446b8c54SPhilipp Zabel return platform_register_drivers(mtk_hdmi_drivers, 1841446b8c54SPhilipp Zabel ARRAY_SIZE(mtk_hdmi_drivers)); 18428f83f268SJie Qiu } 18438f83f268SJie Qiu 18448f83f268SJie Qiu static void __exit mtk_hdmitx_exit(void) 18458f83f268SJie Qiu { 1846446b8c54SPhilipp Zabel platform_unregister_drivers(mtk_hdmi_drivers, 1847446b8c54SPhilipp Zabel ARRAY_SIZE(mtk_hdmi_drivers)); 18488f83f268SJie Qiu } 18498f83f268SJie Qiu 18508f83f268SJie Qiu module_init(mtk_hdmitx_init); 18518f83f268SJie Qiu module_exit(mtk_hdmitx_exit); 18528f83f268SJie Qiu 18538f83f268SJie Qiu MODULE_AUTHOR("Jie Qiu <jie.qiu@mediatek.com>"); 18548f83f268SJie Qiu MODULE_DESCRIPTION("MediaTek HDMI Driver"); 18558f83f268SJie Qiu MODULE_LICENSE("GPL v2"); 1856