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