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; 236be28b650Schunhui dai struct mtk_hdmi_phy *hdmi_phy = phy_get_drvdata(hdmi->phy); 23756ba355dSJie Qiu 23856ba355dSJie Qiu /* 23956ba355dSJie Qiu * MT8173 HDMI hardware has an output control bit to enable/disable HDMI 24056ba355dSJie Qiu * output. This bit can only be controlled in ARM supervisor mode. 24156ba355dSJie Qiu * The ARM trusted firmware provides an API for the HDMI driver to set 24256ba355dSJie Qiu * this control bit to enable HDMI output in supervisor mode. 24356ba355dSJie Qiu */ 2440fc721b2Schunhui dai if (hdmi_phy->conf && hdmi_phy->conf->tz_disabled) 2450fc721b2Schunhui dai regmap_update_bits(hdmi->sys_regmap, 2460fc721b2Schunhui dai hdmi->sys_offset + HDMI_SYS_CFG20, 2470fc721b2Schunhui dai 0x80008005, enable ? 0x80000005 : 0x8000); 2480fc721b2Schunhui dai else 2490fc721b2Schunhui dai arm_smccc_smc(MTK_SIP_SET_AUTHORIZED_SECURE_REG, 0x14000904, 2500fc721b2Schunhui dai 0x80000000, 0, 0, 0, 0, 0, &res); 25156ba355dSJie Qiu 2528f83f268SJie Qiu regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG20, 2538f83f268SJie Qiu HDMI_PCLK_FREE_RUN, enable ? HDMI_PCLK_FREE_RUN : 0); 2548f83f268SJie Qiu regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG1C, 2558f83f268SJie Qiu HDMI_ON | ANLG_ON, enable ? (HDMI_ON | ANLG_ON) : 0); 2568f83f268SJie Qiu } 2578f83f268SJie Qiu 2588f83f268SJie Qiu static void mtk_hdmi_hw_1p4_version_enable(struct mtk_hdmi *hdmi, bool enable) 2598f83f268SJie Qiu { 2608f83f268SJie Qiu regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG20, 2618f83f268SJie Qiu HDMI2P0_EN, enable ? 0 : HDMI2P0_EN); 2628f83f268SJie Qiu } 2638f83f268SJie Qiu 2648f83f268SJie Qiu static void mtk_hdmi_hw_aud_mute(struct mtk_hdmi *hdmi) 2658f83f268SJie Qiu { 2668f83f268SJie Qiu mtk_hdmi_set_bits(hdmi, GRL_AUDIO_CFG, AUDIO_ZERO); 2678f83f268SJie Qiu } 2688f83f268SJie Qiu 2698f83f268SJie Qiu static void mtk_hdmi_hw_aud_unmute(struct mtk_hdmi *hdmi) 2708f83f268SJie Qiu { 2718f83f268SJie Qiu mtk_hdmi_clear_bits(hdmi, GRL_AUDIO_CFG, AUDIO_ZERO); 2728f83f268SJie Qiu } 2738f83f268SJie Qiu 2748f83f268SJie Qiu static void mtk_hdmi_hw_reset(struct mtk_hdmi *hdmi) 2758f83f268SJie Qiu { 2768f83f268SJie Qiu regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG1C, 2778f83f268SJie Qiu HDMI_RST, HDMI_RST); 2788f83f268SJie Qiu regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG1C, 2798f83f268SJie Qiu HDMI_RST, 0); 2808f83f268SJie Qiu mtk_hdmi_clear_bits(hdmi, GRL_CFG3, CFG3_CONTROL_PACKET_DELAY); 2818f83f268SJie Qiu regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG1C, 2828f83f268SJie Qiu ANLG_ON, ANLG_ON); 2838f83f268SJie Qiu } 2848f83f268SJie Qiu 2858f83f268SJie Qiu static void mtk_hdmi_hw_enable_notice(struct mtk_hdmi *hdmi, bool enable_notice) 2868f83f268SJie Qiu { 2878f83f268SJie Qiu mtk_hdmi_mask(hdmi, GRL_CFG2, enable_notice ? CFG2_NOTICE_EN : 0, 2888f83f268SJie Qiu CFG2_NOTICE_EN); 2898f83f268SJie Qiu } 2908f83f268SJie Qiu 2918f83f268SJie Qiu static void mtk_hdmi_hw_write_int_mask(struct mtk_hdmi *hdmi, u32 int_mask) 2928f83f268SJie Qiu { 2938f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_INT_MASK, int_mask); 2948f83f268SJie Qiu } 2958f83f268SJie Qiu 2968f83f268SJie Qiu static void mtk_hdmi_hw_enable_dvi_mode(struct mtk_hdmi *hdmi, bool enable) 2978f83f268SJie Qiu { 2988f83f268SJie Qiu mtk_hdmi_mask(hdmi, GRL_CFG1, enable ? CFG1_DVI : 0, CFG1_DVI); 2998f83f268SJie Qiu } 3008f83f268SJie Qiu 3018f83f268SJie Qiu static void mtk_hdmi_hw_send_info_frame(struct mtk_hdmi *hdmi, u8 *buffer, 3028f83f268SJie Qiu u8 len) 3038f83f268SJie Qiu { 3048f83f268SJie Qiu u32 ctrl_reg = GRL_CTRL; 3058f83f268SJie Qiu int i; 3068f83f268SJie Qiu u8 *frame_data; 3078f83f268SJie Qiu enum hdmi_infoframe_type frame_type; 3088f83f268SJie Qiu u8 frame_ver; 3098f83f268SJie Qiu u8 frame_len; 3108f83f268SJie Qiu u8 checksum; 3118f83f268SJie Qiu int ctrl_frame_en = 0; 3128f83f268SJie Qiu 3138f83f268SJie Qiu frame_type = *buffer; 3148f83f268SJie Qiu buffer += 1; 3158f83f268SJie Qiu frame_ver = *buffer; 3168f83f268SJie Qiu buffer += 1; 3178f83f268SJie Qiu frame_len = *buffer; 3188f83f268SJie Qiu buffer += 1; 3198f83f268SJie Qiu checksum = *buffer; 3208f83f268SJie Qiu buffer += 1; 3218f83f268SJie Qiu frame_data = buffer; 3228f83f268SJie Qiu 3238f83f268SJie Qiu dev_dbg(hdmi->dev, 3248f83f268SJie Qiu "frame_type:0x%x,frame_ver:0x%x,frame_len:0x%x,checksum:0x%x\n", 3258f83f268SJie Qiu frame_type, frame_ver, frame_len, checksum); 3268f83f268SJie Qiu 3278f83f268SJie Qiu switch (frame_type) { 3288f83f268SJie Qiu case HDMI_INFOFRAME_TYPE_AVI: 3298f83f268SJie Qiu ctrl_frame_en = CTRL_AVI_EN; 3308f83f268SJie Qiu ctrl_reg = GRL_CTRL; 3318f83f268SJie Qiu break; 3328f83f268SJie Qiu case HDMI_INFOFRAME_TYPE_SPD: 3338f83f268SJie Qiu ctrl_frame_en = CTRL_SPD_EN; 3348f83f268SJie Qiu ctrl_reg = GRL_CTRL; 3358f83f268SJie Qiu break; 3368f83f268SJie Qiu case HDMI_INFOFRAME_TYPE_AUDIO: 3378f83f268SJie Qiu ctrl_frame_en = CTRL_AUDIO_EN; 3388f83f268SJie Qiu ctrl_reg = GRL_CTRL; 3398f83f268SJie Qiu break; 3408f83f268SJie Qiu case HDMI_INFOFRAME_TYPE_VENDOR: 3418f83f268SJie Qiu ctrl_frame_en = VS_EN; 3428f83f268SJie Qiu ctrl_reg = GRL_ACP_ISRC_CTRL; 3438f83f268SJie Qiu break; 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; 9818f83f268SJie Qiu u8 buffer[17]; 9828f83f268SJie Qiu ssize_t err; 9838f83f268SJie Qiu 984*13d0add3SVille Syrjälä err = drm_hdmi_avi_infoframe_from_display_mode(&frame, 985*13d0add3SVille 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; 10078f83f268SJie Qiu u8 buffer[29]; 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; 10308f83f268SJie Qiu u8 buffer[14]; 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 11978f83f268SJie Qiu static enum drm_connector_status hdmi_conn_detect(struct drm_connector *conn, 11988f83f268SJie Qiu bool force) 11998f83f268SJie Qiu { 12008f83f268SJie Qiu struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn); 12018f83f268SJie Qiu 12028f83f268SJie Qiu return mtk_cec_hpd_high(hdmi->cec_dev) ? 12038f83f268SJie Qiu connector_status_connected : connector_status_disconnected; 12048f83f268SJie Qiu } 12058f83f268SJie Qiu 12068f83f268SJie Qiu static void hdmi_conn_destroy(struct drm_connector *conn) 12078f83f268SJie Qiu { 12088f83f268SJie Qiu struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn); 12098f83f268SJie Qiu 12108f83f268SJie Qiu mtk_cec_set_hpd_event(hdmi->cec_dev, NULL, NULL); 12118f83f268SJie Qiu 12128f83f268SJie Qiu drm_connector_cleanup(conn); 12138f83f268SJie Qiu } 12148f83f268SJie Qiu 12158f83f268SJie Qiu static int mtk_hdmi_conn_get_modes(struct drm_connector *conn) 12168f83f268SJie Qiu { 12178f83f268SJie Qiu struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn); 12188f83f268SJie Qiu struct edid *edid; 12198f83f268SJie Qiu int ret; 12208f83f268SJie Qiu 12218f83f268SJie Qiu if (!hdmi->ddc_adpt) 12228f83f268SJie Qiu return -ENODEV; 12238f83f268SJie Qiu 12248f83f268SJie Qiu edid = drm_get_edid(conn, hdmi->ddc_adpt); 12258f83f268SJie Qiu if (!edid) 12268f83f268SJie Qiu return -ENODEV; 12278f83f268SJie Qiu 12288f83f268SJie Qiu hdmi->dvi_mode = !drm_detect_monitor_audio(edid); 12298f83f268SJie Qiu 1230c555f023SDaniel Vetter drm_connector_update_edid_property(conn, edid); 12318f83f268SJie Qiu 12328f83f268SJie Qiu ret = drm_add_edid_modes(conn, edid); 12338f83f268SJie Qiu kfree(edid); 12348f83f268SJie Qiu return ret; 12358f83f268SJie Qiu } 12368f83f268SJie Qiu 12378f83f268SJie Qiu static int mtk_hdmi_conn_mode_valid(struct drm_connector *conn, 12388f83f268SJie Qiu struct drm_display_mode *mode) 12398f83f268SJie Qiu { 12408f83f268SJie Qiu struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn); 12418f83f268SJie Qiu 12428f83f268SJie Qiu dev_dbg(hdmi->dev, "xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n", 12438f83f268SJie Qiu mode->hdisplay, mode->vdisplay, mode->vrefresh, 12448f83f268SJie Qiu !!(mode->flags & DRM_MODE_FLAG_INTERLACE), mode->clock * 1000); 12458f83f268SJie Qiu 12468f83f268SJie Qiu if (hdmi->bridge.next) { 12478f83f268SJie Qiu struct drm_display_mode adjusted_mode; 12488f83f268SJie Qiu 12498f83f268SJie Qiu drm_mode_copy(&adjusted_mode, mode); 12508f83f268SJie Qiu if (!drm_bridge_mode_fixup(hdmi->bridge.next, mode, 12518f83f268SJie Qiu &adjusted_mode)) 12528f83f268SJie Qiu return MODE_BAD; 12538f83f268SJie Qiu } 12548f83f268SJie Qiu 12558f83f268SJie Qiu if (mode->clock < 27000) 12568f83f268SJie Qiu return MODE_CLOCK_LOW; 12578f83f268SJie Qiu if (mode->clock > 297000) 12588f83f268SJie Qiu return MODE_CLOCK_HIGH; 12598f83f268SJie Qiu 12608f83f268SJie Qiu return drm_mode_validate_size(mode, 0x1fff, 0x1fff); 12618f83f268SJie Qiu } 12628f83f268SJie Qiu 12638f83f268SJie Qiu static struct drm_encoder *mtk_hdmi_conn_best_enc(struct drm_connector *conn) 12648f83f268SJie Qiu { 12658f83f268SJie Qiu struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn); 12668f83f268SJie Qiu 12678f83f268SJie Qiu return hdmi->bridge.encoder; 12688f83f268SJie Qiu } 12698f83f268SJie Qiu 12708f83f268SJie Qiu static const struct drm_connector_funcs mtk_hdmi_connector_funcs = { 12718f83f268SJie Qiu .detect = hdmi_conn_detect, 12728f83f268SJie Qiu .fill_modes = drm_helper_probe_single_connector_modes, 12738f83f268SJie Qiu .destroy = hdmi_conn_destroy, 12748f83f268SJie Qiu .reset = drm_atomic_helper_connector_reset, 12758f83f268SJie Qiu .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 12768f83f268SJie Qiu .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 12778f83f268SJie Qiu }; 12788f83f268SJie Qiu 12798f83f268SJie Qiu static const struct drm_connector_helper_funcs 12808f83f268SJie Qiu mtk_hdmi_connector_helper_funcs = { 12818f83f268SJie Qiu .get_modes = mtk_hdmi_conn_get_modes, 12828f83f268SJie Qiu .mode_valid = mtk_hdmi_conn_mode_valid, 12838f83f268SJie Qiu .best_encoder = mtk_hdmi_conn_best_enc, 12848f83f268SJie Qiu }; 12858f83f268SJie Qiu 12868f83f268SJie Qiu static void mtk_hdmi_hpd_event(bool hpd, struct device *dev) 12878f83f268SJie Qiu { 12888f83f268SJie Qiu struct mtk_hdmi *hdmi = dev_get_drvdata(dev); 12898f83f268SJie Qiu 12908f83f268SJie Qiu if (hdmi && hdmi->bridge.encoder && hdmi->bridge.encoder->dev) 12918f83f268SJie Qiu drm_helper_hpd_irq_event(hdmi->bridge.encoder->dev); 12928f83f268SJie Qiu } 12938f83f268SJie Qiu 12948f83f268SJie Qiu /* 12958f83f268SJie Qiu * Bridge callbacks 12968f83f268SJie Qiu */ 12978f83f268SJie Qiu 12988f83f268SJie Qiu static int mtk_hdmi_bridge_attach(struct drm_bridge *bridge) 12998f83f268SJie Qiu { 13008f83f268SJie Qiu struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge); 13018f83f268SJie Qiu int ret; 13028f83f268SJie Qiu 13038f83f268SJie Qiu ret = drm_connector_init(bridge->encoder->dev, &hdmi->conn, 13048f83f268SJie Qiu &mtk_hdmi_connector_funcs, 13058f83f268SJie Qiu DRM_MODE_CONNECTOR_HDMIA); 13068f83f268SJie Qiu if (ret) { 13078f83f268SJie Qiu dev_err(hdmi->dev, "Failed to initialize connector: %d\n", ret); 13088f83f268SJie Qiu return ret; 13098f83f268SJie Qiu } 13108f83f268SJie Qiu drm_connector_helper_add(&hdmi->conn, &mtk_hdmi_connector_helper_funcs); 13118f83f268SJie Qiu 13128f83f268SJie Qiu hdmi->conn.polled = DRM_CONNECTOR_POLL_HPD; 13138f83f268SJie Qiu hdmi->conn.interlace_allowed = true; 13148f83f268SJie Qiu hdmi->conn.doublescan_allowed = false; 13158f83f268SJie Qiu 1316cde4c44dSDaniel Vetter ret = drm_connector_attach_encoder(&hdmi->conn, 13178f83f268SJie Qiu bridge->encoder); 13188f83f268SJie Qiu if (ret) { 13198f83f268SJie Qiu dev_err(hdmi->dev, 13208f83f268SJie Qiu "Failed to attach connector to encoder: %d\n", ret); 13218f83f268SJie Qiu return ret; 13228f83f268SJie Qiu } 13238f83f268SJie Qiu 13243bb80f24SLaurent Pinchart if (hdmi->next_bridge) { 13253bb80f24SLaurent Pinchart ret = drm_bridge_attach(bridge->encoder, hdmi->next_bridge, 13263bb80f24SLaurent Pinchart bridge); 13278f83f268SJie Qiu if (ret) { 13288f83f268SJie Qiu dev_err(hdmi->dev, 13298f83f268SJie Qiu "Failed to attach external bridge: %d\n", ret); 13308f83f268SJie Qiu return ret; 13318f83f268SJie Qiu } 13328f83f268SJie Qiu } 13338f83f268SJie Qiu 13348f83f268SJie Qiu mtk_cec_set_hpd_event(hdmi->cec_dev, mtk_hdmi_hpd_event, hdmi->dev); 13358f83f268SJie Qiu 13368f83f268SJie Qiu return 0; 13378f83f268SJie Qiu } 13388f83f268SJie Qiu 13398f83f268SJie Qiu static bool mtk_hdmi_bridge_mode_fixup(struct drm_bridge *bridge, 13408f83f268SJie Qiu const struct drm_display_mode *mode, 13418f83f268SJie Qiu struct drm_display_mode *adjusted_mode) 13428f83f268SJie Qiu { 13438f83f268SJie Qiu return true; 13448f83f268SJie Qiu } 13458f83f268SJie Qiu 13468f83f268SJie Qiu static void mtk_hdmi_bridge_disable(struct drm_bridge *bridge) 13478f83f268SJie Qiu { 13488f83f268SJie Qiu struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge); 13498f83f268SJie Qiu 13508f83f268SJie Qiu if (!hdmi->enabled) 13518f83f268SJie Qiu return; 13528f83f268SJie Qiu 13538f83f268SJie Qiu phy_power_off(hdmi->phy); 13548f83f268SJie Qiu clk_disable_unprepare(hdmi->clk[MTK_HDMI_CLK_HDMI_PIXEL]); 13558f83f268SJie Qiu clk_disable_unprepare(hdmi->clk[MTK_HDMI_CLK_HDMI_PLL]); 13568f83f268SJie Qiu 13578f83f268SJie Qiu hdmi->enabled = false; 13588f83f268SJie Qiu } 13598f83f268SJie Qiu 13608f83f268SJie Qiu static void mtk_hdmi_bridge_post_disable(struct drm_bridge *bridge) 13618f83f268SJie Qiu { 13628f83f268SJie Qiu struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge); 13638f83f268SJie Qiu 13648f83f268SJie Qiu if (!hdmi->powered) 13658f83f268SJie Qiu return; 13668f83f268SJie Qiu 13678f83f268SJie Qiu mtk_hdmi_hw_1p4_version_enable(hdmi, true); 13688f83f268SJie Qiu mtk_hdmi_hw_make_reg_writable(hdmi, false); 13698f83f268SJie Qiu 13708f83f268SJie Qiu hdmi->powered = false; 13718f83f268SJie Qiu } 13728f83f268SJie Qiu 13738f83f268SJie Qiu static void mtk_hdmi_bridge_mode_set(struct drm_bridge *bridge, 13748f83f268SJie Qiu struct drm_display_mode *mode, 13758f83f268SJie Qiu struct drm_display_mode *adjusted_mode) 13768f83f268SJie Qiu { 13778f83f268SJie Qiu struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge); 13788f83f268SJie Qiu 13798f83f268SJie Qiu dev_dbg(hdmi->dev, "cur info: name:%s, hdisplay:%d\n", 13808f83f268SJie Qiu adjusted_mode->name, adjusted_mode->hdisplay); 13818f83f268SJie Qiu dev_dbg(hdmi->dev, "hsync_start:%d,hsync_end:%d, htotal:%d", 13828f83f268SJie Qiu adjusted_mode->hsync_start, adjusted_mode->hsync_end, 13838f83f268SJie Qiu adjusted_mode->htotal); 13848f83f268SJie Qiu dev_dbg(hdmi->dev, "hskew:%d, vdisplay:%d\n", 13858f83f268SJie Qiu adjusted_mode->hskew, adjusted_mode->vdisplay); 13868f83f268SJie Qiu dev_dbg(hdmi->dev, "vsync_start:%d, vsync_end:%d, vtotal:%d", 13878f83f268SJie Qiu adjusted_mode->vsync_start, adjusted_mode->vsync_end, 13888f83f268SJie Qiu adjusted_mode->vtotal); 13898f83f268SJie Qiu dev_dbg(hdmi->dev, "vscan:%d, flag:%d\n", 13908f83f268SJie Qiu adjusted_mode->vscan, adjusted_mode->flags); 13918f83f268SJie Qiu 13928f83f268SJie Qiu drm_mode_copy(&hdmi->mode, adjusted_mode); 13938f83f268SJie Qiu } 13948f83f268SJie Qiu 13958f83f268SJie Qiu static void mtk_hdmi_bridge_pre_enable(struct drm_bridge *bridge) 13968f83f268SJie Qiu { 13978f83f268SJie Qiu struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge); 13988f83f268SJie Qiu 13998f83f268SJie Qiu mtk_hdmi_hw_make_reg_writable(hdmi, true); 14008f83f268SJie Qiu mtk_hdmi_hw_1p4_version_enable(hdmi, true); 14018f83f268SJie Qiu 14028f83f268SJie Qiu hdmi->powered = true; 14038f83f268SJie Qiu } 14048f83f268SJie Qiu 1405d542b7c4SJunzhi Zhao static void mtk_hdmi_send_infoframe(struct mtk_hdmi *hdmi, 1406d542b7c4SJunzhi Zhao struct drm_display_mode *mode) 1407d542b7c4SJunzhi Zhao { 1408d542b7c4SJunzhi Zhao mtk_hdmi_setup_audio_infoframe(hdmi); 1409d542b7c4SJunzhi Zhao mtk_hdmi_setup_avi_infoframe(hdmi, mode); 1410d542b7c4SJunzhi Zhao mtk_hdmi_setup_spd_infoframe(hdmi, "mediatek", "On-chip HDMI"); 1411d542b7c4SJunzhi Zhao if (mode->flags & DRM_MODE_FLAG_3D_MASK) 1412d542b7c4SJunzhi Zhao mtk_hdmi_setup_vendor_specific_infoframe(hdmi, mode); 1413d542b7c4SJunzhi Zhao } 1414d542b7c4SJunzhi Zhao 14158f83f268SJie Qiu static void mtk_hdmi_bridge_enable(struct drm_bridge *bridge) 14168f83f268SJie Qiu { 14178f83f268SJie Qiu struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge); 14188f83f268SJie Qiu 14198f83f268SJie Qiu mtk_hdmi_output_set_display_mode(hdmi, &hdmi->mode); 14208f83f268SJie Qiu clk_prepare_enable(hdmi->clk[MTK_HDMI_CLK_HDMI_PLL]); 14218f83f268SJie Qiu clk_prepare_enable(hdmi->clk[MTK_HDMI_CLK_HDMI_PIXEL]); 14228f83f268SJie Qiu phy_power_on(hdmi->phy); 1423d542b7c4SJunzhi Zhao mtk_hdmi_send_infoframe(hdmi, &hdmi->mode); 14248f83f268SJie Qiu 14258f83f268SJie Qiu hdmi->enabled = true; 14268f83f268SJie Qiu } 14278f83f268SJie Qiu 14288f83f268SJie Qiu static const struct drm_bridge_funcs mtk_hdmi_bridge_funcs = { 14298f83f268SJie Qiu .attach = mtk_hdmi_bridge_attach, 14308f83f268SJie Qiu .mode_fixup = mtk_hdmi_bridge_mode_fixup, 14318f83f268SJie Qiu .disable = mtk_hdmi_bridge_disable, 14328f83f268SJie Qiu .post_disable = mtk_hdmi_bridge_post_disable, 14338f83f268SJie Qiu .mode_set = mtk_hdmi_bridge_mode_set, 14348f83f268SJie Qiu .pre_enable = mtk_hdmi_bridge_pre_enable, 14358f83f268SJie Qiu .enable = mtk_hdmi_bridge_enable, 14368f83f268SJie Qiu }; 14378f83f268SJie Qiu 14388f83f268SJie Qiu static int mtk_hdmi_dt_parse_pdata(struct mtk_hdmi *hdmi, 14398f83f268SJie Qiu struct platform_device *pdev) 14408f83f268SJie Qiu { 14418f83f268SJie Qiu struct device *dev = &pdev->dev; 14428f83f268SJie Qiu struct device_node *np = dev->of_node; 144386418f90SRob Herring struct device_node *cec_np, *remote, *i2c_np; 14448f83f268SJie Qiu struct platform_device *cec_pdev; 14458f83f268SJie Qiu struct regmap *regmap; 14468f83f268SJie Qiu struct resource *mem; 14478f83f268SJie Qiu int ret; 14488f83f268SJie Qiu 14498f83f268SJie Qiu ret = mtk_hdmi_get_all_clk(hdmi, np); 14508f83f268SJie Qiu if (ret) { 14518f83f268SJie Qiu dev_err(dev, "Failed to get clocks: %d\n", ret); 14528f83f268SJie Qiu return ret; 14538f83f268SJie Qiu } 14548f83f268SJie Qiu 14558f83f268SJie Qiu /* The CEC module handles HDMI hotplug detection */ 1456ceff2f4dSJohan Hovold cec_np = of_get_compatible_child(np->parent, "mediatek,mt8173-cec"); 14578f83f268SJie Qiu if (!cec_np) { 14588f83f268SJie Qiu dev_err(dev, "Failed to find CEC node\n"); 14598f83f268SJie Qiu return -EINVAL; 14608f83f268SJie Qiu } 14618f83f268SJie Qiu 14628f83f268SJie Qiu cec_pdev = of_find_device_by_node(cec_np); 14638f83f268SJie Qiu if (!cec_pdev) { 14644bf99144SRob Herring dev_err(hdmi->dev, "Waiting for CEC device %pOF\n", 14654bf99144SRob Herring cec_np); 1466ceff2f4dSJohan Hovold of_node_put(cec_np); 14678f83f268SJie Qiu return -EPROBE_DEFER; 14688f83f268SJie Qiu } 1469ceff2f4dSJohan Hovold of_node_put(cec_np); 14708f83f268SJie Qiu hdmi->cec_dev = &cec_pdev->dev; 14718f83f268SJie Qiu 14728f83f268SJie Qiu /* 14738f83f268SJie Qiu * The mediatek,syscon-hdmi property contains a phandle link to the 14748f83f268SJie Qiu * MMSYS_CONFIG device and the register offset of the HDMI_SYS_CFG 14758f83f268SJie Qiu * registers it contains. 14768f83f268SJie Qiu */ 14778f83f268SJie Qiu regmap = syscon_regmap_lookup_by_phandle(np, "mediatek,syscon-hdmi"); 14788f83f268SJie Qiu ret = of_property_read_u32_index(np, "mediatek,syscon-hdmi", 1, 14798f83f268SJie Qiu &hdmi->sys_offset); 14808f83f268SJie Qiu if (IS_ERR(regmap)) 14818f83f268SJie Qiu ret = PTR_ERR(regmap); 14828f83f268SJie Qiu if (ret) { 14838f83f268SJie Qiu ret = PTR_ERR(regmap); 14848f83f268SJie Qiu dev_err(dev, 14858f83f268SJie Qiu "Failed to get system configuration registers: %d\n", 14868f83f268SJie Qiu ret); 14878f83f268SJie Qiu return ret; 14888f83f268SJie Qiu } 14898f83f268SJie Qiu hdmi->sys_regmap = regmap; 14908f83f268SJie Qiu 14918f83f268SJie Qiu mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 14928f83f268SJie Qiu hdmi->regs = devm_ioremap_resource(dev, mem); 14938f83f268SJie Qiu if (IS_ERR(hdmi->regs)) 14948f83f268SJie Qiu return PTR_ERR(hdmi->regs); 14958f83f268SJie Qiu 149686418f90SRob Herring remote = of_graph_get_remote_node(np, 1, 0); 149786418f90SRob Herring if (!remote) 14988f83f268SJie Qiu return -EINVAL; 14998f83f268SJie Qiu 15008f83f268SJie Qiu if (!of_device_is_compatible(remote, "hdmi-connector")) { 15013bb80f24SLaurent Pinchart hdmi->next_bridge = of_drm_find_bridge(remote); 15023bb80f24SLaurent Pinchart if (!hdmi->next_bridge) { 15038f83f268SJie Qiu dev_err(dev, "Waiting for external bridge\n"); 15048f83f268SJie Qiu of_node_put(remote); 15058f83f268SJie Qiu return -EPROBE_DEFER; 15068f83f268SJie Qiu } 15078f83f268SJie Qiu } 15088f83f268SJie Qiu 15098f83f268SJie Qiu i2c_np = of_parse_phandle(remote, "ddc-i2c-bus", 0); 15108f83f268SJie Qiu if (!i2c_np) { 15114bf99144SRob Herring dev_err(dev, "Failed to find ddc-i2c-bus node in %pOF\n", 15124bf99144SRob Herring remote); 15138f83f268SJie Qiu of_node_put(remote); 15148f83f268SJie Qiu return -EINVAL; 15158f83f268SJie Qiu } 15168f83f268SJie Qiu of_node_put(remote); 15178f83f268SJie Qiu 15188f83f268SJie Qiu hdmi->ddc_adpt = of_find_i2c_adapter_by_node(i2c_np); 15198f83f268SJie Qiu if (!hdmi->ddc_adpt) { 15208f83f268SJie Qiu dev_err(dev, "Failed to get ddc i2c adapter by node\n"); 15218f83f268SJie Qiu return -EINVAL; 15228f83f268SJie Qiu } 15238f83f268SJie Qiu 15248f83f268SJie Qiu return 0; 15258f83f268SJie Qiu } 15268f83f268SJie Qiu 15278f83f268SJie Qiu /* 15288f83f268SJie Qiu * HDMI audio codec callbacks 15298f83f268SJie Qiu */ 15308f83f268SJie Qiu 15315dd0775eSDave Airlie static int mtk_hdmi_audio_hw_params(struct device *dev, void *data, 15328f83f268SJie Qiu struct hdmi_codec_daifmt *daifmt, 15338f83f268SJie Qiu struct hdmi_codec_params *params) 15348f83f268SJie Qiu { 15358f83f268SJie Qiu struct mtk_hdmi *hdmi = dev_get_drvdata(dev); 15368f83f268SJie Qiu struct hdmi_audio_param hdmi_params; 15378f83f268SJie Qiu unsigned int chan = params->cea.channels; 15388f83f268SJie Qiu 15398f83f268SJie Qiu dev_dbg(hdmi->dev, "%s: %u Hz, %d bit, %d channels\n", __func__, 15408f83f268SJie Qiu params->sample_rate, params->sample_width, chan); 15418f83f268SJie Qiu 15428f83f268SJie Qiu if (!hdmi->bridge.encoder) 15438f83f268SJie Qiu return -ENODEV; 15448f83f268SJie Qiu 15458f83f268SJie Qiu switch (chan) { 15468f83f268SJie Qiu case 2: 15478f83f268SJie Qiu hdmi_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_2_0; 15488f83f268SJie Qiu break; 15498f83f268SJie Qiu case 4: 15508f83f268SJie Qiu hdmi_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_4_0; 15518f83f268SJie Qiu break; 15528f83f268SJie Qiu case 6: 15538f83f268SJie Qiu hdmi_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_5_1; 15548f83f268SJie Qiu break; 15558f83f268SJie Qiu case 8: 15568f83f268SJie Qiu hdmi_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_7_1; 15578f83f268SJie Qiu break; 15588f83f268SJie Qiu default: 15598f83f268SJie Qiu dev_err(hdmi->dev, "channel[%d] not supported!\n", chan); 15608f83f268SJie Qiu return -EINVAL; 15618f83f268SJie Qiu } 15628f83f268SJie Qiu 15638f83f268SJie Qiu switch (params->sample_rate) { 15648f83f268SJie Qiu case 32000: 15658f83f268SJie Qiu case 44100: 15668f83f268SJie Qiu case 48000: 15678f83f268SJie Qiu case 88200: 15688f83f268SJie Qiu case 96000: 15698f83f268SJie Qiu case 176400: 15708f83f268SJie Qiu case 192000: 15718f83f268SJie Qiu break; 15728f83f268SJie Qiu default: 15738f83f268SJie Qiu dev_err(hdmi->dev, "rate[%d] not supported!\n", 15748f83f268SJie Qiu params->sample_rate); 15758f83f268SJie Qiu return -EINVAL; 15768f83f268SJie Qiu } 15778f83f268SJie Qiu 15788f83f268SJie Qiu switch (daifmt->fmt) { 15798f83f268SJie Qiu case HDMI_I2S: 15808f83f268SJie Qiu hdmi_params.aud_codec = HDMI_AUDIO_CODING_TYPE_PCM; 15818f83f268SJie Qiu hdmi_params.aud_sampe_size = HDMI_AUDIO_SAMPLE_SIZE_16; 15828f83f268SJie Qiu hdmi_params.aud_input_type = HDMI_AUD_INPUT_I2S; 15838f83f268SJie Qiu hdmi_params.aud_i2s_fmt = HDMI_I2S_MODE_I2S_24BIT; 15848f83f268SJie Qiu hdmi_params.aud_mclk = HDMI_AUD_MCLK_128FS; 15858f83f268SJie Qiu break; 1586d1ef028dSchunhui dai case HDMI_SPDIF: 1587d1ef028dSchunhui dai hdmi_params.aud_codec = HDMI_AUDIO_CODING_TYPE_PCM; 1588d1ef028dSchunhui dai hdmi_params.aud_sampe_size = HDMI_AUDIO_SAMPLE_SIZE_16; 1589d1ef028dSchunhui dai hdmi_params.aud_input_type = HDMI_AUD_INPUT_SPDIF; 1590d1ef028dSchunhui dai break; 15918f83f268SJie Qiu default: 15928f83f268SJie Qiu dev_err(hdmi->dev, "%s: Invalid DAI format %d\n", __func__, 15938f83f268SJie Qiu daifmt->fmt); 15948f83f268SJie Qiu return -EINVAL; 15958f83f268SJie Qiu } 15968f83f268SJie Qiu 15978f83f268SJie Qiu memcpy(&hdmi_params.codec_params, params, 15988f83f268SJie Qiu sizeof(hdmi_params.codec_params)); 15998f83f268SJie Qiu 16008f83f268SJie Qiu mtk_hdmi_audio_set_param(hdmi, &hdmi_params); 16018f83f268SJie Qiu 16028f83f268SJie Qiu return 0; 16038f83f268SJie Qiu } 16048f83f268SJie Qiu 16055dd0775eSDave Airlie static int mtk_hdmi_audio_startup(struct device *dev, void *data) 16068f83f268SJie Qiu { 16078f83f268SJie Qiu struct mtk_hdmi *hdmi = dev_get_drvdata(dev); 16088f83f268SJie Qiu 16098f83f268SJie Qiu dev_dbg(dev, "%s\n", __func__); 16108f83f268SJie Qiu 16118f83f268SJie Qiu mtk_hdmi_audio_enable(hdmi); 16128f83f268SJie Qiu 16138f83f268SJie Qiu return 0; 16148f83f268SJie Qiu } 16158f83f268SJie Qiu 16165dd0775eSDave Airlie static void mtk_hdmi_audio_shutdown(struct device *dev, void *data) 16178f83f268SJie Qiu { 16188f83f268SJie Qiu struct mtk_hdmi *hdmi = dev_get_drvdata(dev); 16198f83f268SJie Qiu 16208f83f268SJie Qiu dev_dbg(dev, "%s\n", __func__); 16218f83f268SJie Qiu 16228f83f268SJie Qiu mtk_hdmi_audio_disable(hdmi); 16238f83f268SJie Qiu } 16248f83f268SJie Qiu 1625188af070SBaoyou Xie static int 1626188af070SBaoyou Xie mtk_hdmi_audio_digital_mute(struct device *dev, void *data, bool enable) 16278f83f268SJie Qiu { 16288f83f268SJie Qiu struct mtk_hdmi *hdmi = dev_get_drvdata(dev); 16298f83f268SJie Qiu 16308f83f268SJie Qiu dev_dbg(dev, "%s(%d)\n", __func__, enable); 16318f83f268SJie Qiu 16328f83f268SJie Qiu if (enable) 16338f83f268SJie Qiu mtk_hdmi_hw_aud_mute(hdmi); 16348f83f268SJie Qiu else 16358f83f268SJie Qiu mtk_hdmi_hw_aud_unmute(hdmi); 16368f83f268SJie Qiu 16378f83f268SJie Qiu return 0; 16388f83f268SJie Qiu } 16398f83f268SJie Qiu 16405dd0775eSDave Airlie static int mtk_hdmi_audio_get_eld(struct device *dev, void *data, uint8_t *buf, size_t len) 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 memcpy(buf, hdmi->conn.eld, min(sizeof(hdmi->conn.eld), len)); 16478f83f268SJie Qiu 16488f83f268SJie Qiu return 0; 16498f83f268SJie Qiu } 16508f83f268SJie Qiu 16518f83f268SJie Qiu static const struct hdmi_codec_ops mtk_hdmi_audio_codec_ops = { 16528f83f268SJie Qiu .hw_params = mtk_hdmi_audio_hw_params, 16538f83f268SJie Qiu .audio_startup = mtk_hdmi_audio_startup, 16548f83f268SJie Qiu .audio_shutdown = mtk_hdmi_audio_shutdown, 16558f83f268SJie Qiu .digital_mute = mtk_hdmi_audio_digital_mute, 16568f83f268SJie Qiu .get_eld = mtk_hdmi_audio_get_eld, 16578f83f268SJie Qiu }; 16588f83f268SJie Qiu 16598f83f268SJie Qiu static void mtk_hdmi_register_audio_driver(struct device *dev) 16608f83f268SJie Qiu { 16618f83f268SJie Qiu struct hdmi_codec_pdata codec_data = { 16628f83f268SJie Qiu .ops = &mtk_hdmi_audio_codec_ops, 16638f83f268SJie Qiu .max_i2s_channels = 2, 16648f83f268SJie Qiu .i2s = 1, 16658f83f268SJie Qiu }; 16668f83f268SJie Qiu struct platform_device *pdev; 16678f83f268SJie Qiu 16688f83f268SJie Qiu pdev = platform_device_register_data(dev, HDMI_CODEC_DRV_NAME, 16698f83f268SJie Qiu PLATFORM_DEVID_AUTO, &codec_data, 16708f83f268SJie Qiu sizeof(codec_data)); 16718f83f268SJie Qiu if (IS_ERR(pdev)) 16728f83f268SJie Qiu return; 16738f83f268SJie Qiu 16748f83f268SJie Qiu DRM_INFO("%s driver bound to HDMI\n", HDMI_CODEC_DRV_NAME); 16758f83f268SJie Qiu } 16768f83f268SJie Qiu 16778f83f268SJie Qiu static int mtk_drm_hdmi_probe(struct platform_device *pdev) 16788f83f268SJie Qiu { 16798f83f268SJie Qiu struct mtk_hdmi *hdmi; 16808f83f268SJie Qiu struct device *dev = &pdev->dev; 16818f83f268SJie Qiu int ret; 16828f83f268SJie Qiu 16838f83f268SJie Qiu hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); 16848f83f268SJie Qiu if (!hdmi) 16858f83f268SJie Qiu return -ENOMEM; 16868f83f268SJie Qiu 16878f83f268SJie Qiu hdmi->dev = dev; 16888f83f268SJie Qiu 16898f83f268SJie Qiu ret = mtk_hdmi_dt_parse_pdata(hdmi, pdev); 16908f83f268SJie Qiu if (ret) 16918f83f268SJie Qiu return ret; 16928f83f268SJie Qiu 16938f83f268SJie Qiu hdmi->phy = devm_phy_get(dev, "hdmi"); 16948f83f268SJie Qiu if (IS_ERR(hdmi->phy)) { 16958f83f268SJie Qiu ret = PTR_ERR(hdmi->phy); 16968f83f268SJie Qiu dev_err(dev, "Failed to get HDMI PHY: %d\n", ret); 16978f83f268SJie Qiu return ret; 16988f83f268SJie Qiu } 16998f83f268SJie Qiu 17008f83f268SJie Qiu platform_set_drvdata(pdev, hdmi); 17018f83f268SJie Qiu 17028f83f268SJie Qiu ret = mtk_hdmi_output_init(hdmi); 17038f83f268SJie Qiu if (ret) { 17048f83f268SJie Qiu dev_err(dev, "Failed to initialize hdmi output\n"); 17058f83f268SJie Qiu return ret; 17068f83f268SJie Qiu } 17078f83f268SJie Qiu 17088f83f268SJie Qiu mtk_hdmi_register_audio_driver(dev); 17098f83f268SJie Qiu 17108f83f268SJie Qiu hdmi->bridge.funcs = &mtk_hdmi_bridge_funcs; 17118f83f268SJie Qiu hdmi->bridge.of_node = pdev->dev.of_node; 1712e6910091SInki Dae drm_bridge_add(&hdmi->bridge); 17138f83f268SJie Qiu 17148f83f268SJie Qiu ret = mtk_hdmi_clk_enable_audio(hdmi); 17158f83f268SJie Qiu if (ret) { 17168f83f268SJie Qiu dev_err(dev, "Failed to enable audio clocks: %d\n", ret); 17178f83f268SJie Qiu goto err_bridge_remove; 17188f83f268SJie Qiu } 17198f83f268SJie Qiu 17208f83f268SJie Qiu dev_dbg(dev, "mediatek hdmi probe success\n"); 17218f83f268SJie Qiu return 0; 17228f83f268SJie Qiu 17238f83f268SJie Qiu err_bridge_remove: 17248f83f268SJie Qiu drm_bridge_remove(&hdmi->bridge); 17258f83f268SJie Qiu return ret; 17268f83f268SJie Qiu } 17278f83f268SJie Qiu 17288f83f268SJie Qiu static int mtk_drm_hdmi_remove(struct platform_device *pdev) 17298f83f268SJie Qiu { 17308f83f268SJie Qiu struct mtk_hdmi *hdmi = platform_get_drvdata(pdev); 17318f83f268SJie Qiu 17328f83f268SJie Qiu drm_bridge_remove(&hdmi->bridge); 17338f83f268SJie Qiu mtk_hdmi_clk_disable_audio(hdmi); 17348f83f268SJie Qiu return 0; 17358f83f268SJie Qiu } 17368f83f268SJie Qiu 17378f83f268SJie Qiu #ifdef CONFIG_PM_SLEEP 17388f83f268SJie Qiu static int mtk_hdmi_suspend(struct device *dev) 17398f83f268SJie Qiu { 17408f83f268SJie Qiu struct mtk_hdmi *hdmi = dev_get_drvdata(dev); 17418f83f268SJie Qiu 17428f83f268SJie Qiu mtk_hdmi_clk_disable_audio(hdmi); 17438f83f268SJie Qiu dev_dbg(dev, "hdmi suspend success!\n"); 17448f83f268SJie Qiu return 0; 17458f83f268SJie Qiu } 17468f83f268SJie Qiu 17478f83f268SJie Qiu static int mtk_hdmi_resume(struct device *dev) 17488f83f268SJie Qiu { 17498f83f268SJie Qiu struct mtk_hdmi *hdmi = dev_get_drvdata(dev); 17508f83f268SJie Qiu int ret = 0; 17518f83f268SJie Qiu 17528f83f268SJie Qiu ret = mtk_hdmi_clk_enable_audio(hdmi); 17538f83f268SJie Qiu if (ret) { 17548f83f268SJie Qiu dev_err(dev, "hdmi resume failed!\n"); 17558f83f268SJie Qiu return ret; 17568f83f268SJie Qiu } 17578f83f268SJie Qiu 17588f83f268SJie Qiu dev_dbg(dev, "hdmi resume success!\n"); 17598f83f268SJie Qiu return 0; 17608f83f268SJie Qiu } 17618f83f268SJie Qiu #endif 17628f83f268SJie Qiu static SIMPLE_DEV_PM_OPS(mtk_hdmi_pm_ops, 17638f83f268SJie Qiu mtk_hdmi_suspend, mtk_hdmi_resume); 17648f83f268SJie Qiu 17658f83f268SJie Qiu static const struct of_device_id mtk_drm_hdmi_of_ids[] = { 17668f83f268SJie Qiu { .compatible = "mediatek,mt8173-hdmi", }, 17678f83f268SJie Qiu {} 17688f83f268SJie Qiu }; 17698f83f268SJie Qiu 17708f83f268SJie Qiu static struct platform_driver mtk_hdmi_driver = { 17718f83f268SJie Qiu .probe = mtk_drm_hdmi_probe, 17728f83f268SJie Qiu .remove = mtk_drm_hdmi_remove, 17738f83f268SJie Qiu .driver = { 17748f83f268SJie Qiu .name = "mediatek-drm-hdmi", 17758f83f268SJie Qiu .of_match_table = mtk_drm_hdmi_of_ids, 17768f83f268SJie Qiu .pm = &mtk_hdmi_pm_ops, 17778f83f268SJie Qiu }, 17788f83f268SJie Qiu }; 17798f83f268SJie Qiu 17808f83f268SJie Qiu static struct platform_driver * const mtk_hdmi_drivers[] = { 17818f83f268SJie Qiu &mtk_hdmi_phy_driver, 17828f83f268SJie Qiu &mtk_hdmi_ddc_driver, 17838f83f268SJie Qiu &mtk_cec_driver, 17848f83f268SJie Qiu &mtk_hdmi_driver, 17858f83f268SJie Qiu }; 17868f83f268SJie Qiu 17878f83f268SJie Qiu static int __init mtk_hdmitx_init(void) 17888f83f268SJie Qiu { 1789446b8c54SPhilipp Zabel return platform_register_drivers(mtk_hdmi_drivers, 1790446b8c54SPhilipp Zabel ARRAY_SIZE(mtk_hdmi_drivers)); 17918f83f268SJie Qiu } 17928f83f268SJie Qiu 17938f83f268SJie Qiu static void __exit mtk_hdmitx_exit(void) 17948f83f268SJie Qiu { 1795446b8c54SPhilipp Zabel platform_unregister_drivers(mtk_hdmi_drivers, 1796446b8c54SPhilipp Zabel ARRAY_SIZE(mtk_hdmi_drivers)); 17978f83f268SJie Qiu } 17988f83f268SJie Qiu 17998f83f268SJie Qiu module_init(mtk_hdmitx_init); 18008f83f268SJie Qiu module_exit(mtk_hdmitx_exit); 18018f83f268SJie Qiu 18028f83f268SJie Qiu MODULE_AUTHOR("Jie Qiu <jie.qiu@mediatek.com>"); 18038f83f268SJie Qiu MODULE_DESCRIPTION("MediaTek HDMI Driver"); 18048f83f268SJie Qiu MODULE_LICENSE("GPL v2"); 1805