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> 17fcd70cd3SDaniel Vetter #include <drm/drm_probe_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; 344*1f1635b1SSean Paul default: 345*1f1635b1SSean Paul dev_err(hdmi->dev, "Unknown infoframe type %d\n", frame_type); 346*1f1635b1SSean Paul return; 3478f83f268SJie Qiu } 3488f83f268SJie Qiu mtk_hdmi_clear_bits(hdmi, ctrl_reg, ctrl_frame_en); 3498f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_INFOFRM_TYPE, frame_type); 3508f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_INFOFRM_VER, frame_ver); 3518f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_INFOFRM_LNG, frame_len); 3528f83f268SJie Qiu 3538f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_IFM_PORT, checksum); 3548f83f268SJie Qiu for (i = 0; i < frame_len; i++) 3558f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_IFM_PORT, frame_data[i]); 3568f83f268SJie Qiu 3578f83f268SJie Qiu mtk_hdmi_set_bits(hdmi, ctrl_reg, ctrl_frame_en); 3588f83f268SJie Qiu } 3598f83f268SJie Qiu 3608f83f268SJie Qiu static void mtk_hdmi_hw_send_aud_packet(struct mtk_hdmi *hdmi, bool enable) 3618f83f268SJie Qiu { 3628f83f268SJie Qiu mtk_hdmi_mask(hdmi, GRL_SHIFT_R2, enable ? 0 : AUDIO_PACKET_OFF, 3638f83f268SJie Qiu AUDIO_PACKET_OFF); 3648f83f268SJie Qiu } 3658f83f268SJie Qiu 3668f83f268SJie Qiu static void mtk_hdmi_hw_config_sys(struct mtk_hdmi *hdmi) 3678f83f268SJie Qiu { 3688f83f268SJie Qiu regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG20, 3698f83f268SJie Qiu HDMI_OUT_FIFO_EN | MHL_MODE_ON, 0); 3708f83f268SJie Qiu usleep_range(2000, 4000); 3718f83f268SJie Qiu regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG20, 3728f83f268SJie Qiu HDMI_OUT_FIFO_EN | MHL_MODE_ON, HDMI_OUT_FIFO_EN); 3738f83f268SJie Qiu } 3748f83f268SJie Qiu 3758f83f268SJie Qiu static void mtk_hdmi_hw_set_deep_color_mode(struct mtk_hdmi *hdmi) 3768f83f268SJie Qiu { 3778f83f268SJie Qiu regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG20, 3788f83f268SJie Qiu DEEP_COLOR_MODE_MASK | DEEP_COLOR_EN, 3798f83f268SJie Qiu COLOR_8BIT_MODE); 3808f83f268SJie Qiu } 3818f83f268SJie Qiu 3828f83f268SJie Qiu static void mtk_hdmi_hw_send_av_mute(struct mtk_hdmi *hdmi) 3838f83f268SJie Qiu { 3848f83f268SJie Qiu mtk_hdmi_clear_bits(hdmi, GRL_CFG4, CTRL_AVMUTE); 3858f83f268SJie Qiu usleep_range(2000, 4000); 3868f83f268SJie Qiu mtk_hdmi_set_bits(hdmi, GRL_CFG4, CTRL_AVMUTE); 3878f83f268SJie Qiu } 3888f83f268SJie Qiu 3898f83f268SJie Qiu static void mtk_hdmi_hw_send_av_unmute(struct mtk_hdmi *hdmi) 3908f83f268SJie Qiu { 3918f83f268SJie Qiu mtk_hdmi_mask(hdmi, GRL_CFG4, CFG4_AV_UNMUTE_EN, 3928f83f268SJie Qiu CFG4_AV_UNMUTE_EN | CFG4_AV_UNMUTE_SET); 3938f83f268SJie Qiu usleep_range(2000, 4000); 3948f83f268SJie Qiu mtk_hdmi_mask(hdmi, GRL_CFG4, CFG4_AV_UNMUTE_SET, 3958f83f268SJie Qiu CFG4_AV_UNMUTE_EN | CFG4_AV_UNMUTE_SET); 3968f83f268SJie Qiu } 3978f83f268SJie Qiu 3988f83f268SJie Qiu static void mtk_hdmi_hw_ncts_enable(struct mtk_hdmi *hdmi, bool on) 3998f83f268SJie Qiu { 4008f83f268SJie Qiu mtk_hdmi_mask(hdmi, GRL_CTS_CTRL, on ? 0 : CTS_CTRL_SOFT, 4018f83f268SJie Qiu CTS_CTRL_SOFT); 4028f83f268SJie Qiu } 4038f83f268SJie Qiu 4048f83f268SJie Qiu static void mtk_hdmi_hw_ncts_auto_write_enable(struct mtk_hdmi *hdmi, 4058f83f268SJie Qiu bool enable) 4068f83f268SJie Qiu { 4078f83f268SJie Qiu mtk_hdmi_mask(hdmi, GRL_CTS_CTRL, enable ? NCTS_WRI_ANYTIME : 0, 4088f83f268SJie Qiu NCTS_WRI_ANYTIME); 4098f83f268SJie Qiu } 4108f83f268SJie Qiu 4118f83f268SJie Qiu static void mtk_hdmi_hw_msic_setting(struct mtk_hdmi *hdmi, 4128f83f268SJie Qiu struct drm_display_mode *mode) 4138f83f268SJie Qiu { 4148f83f268SJie Qiu mtk_hdmi_clear_bits(hdmi, GRL_CFG4, CFG4_MHL_MODE); 4158f83f268SJie Qiu 4168f83f268SJie Qiu if (mode->flags & DRM_MODE_FLAG_INTERLACE && 4178f83f268SJie Qiu mode->clock == 74250 && 4188f83f268SJie Qiu mode->vdisplay == 1080) 4198f83f268SJie Qiu mtk_hdmi_clear_bits(hdmi, GRL_CFG2, CFG2_MHL_DE_SEL); 4208f83f268SJie Qiu else 4218f83f268SJie Qiu mtk_hdmi_set_bits(hdmi, GRL_CFG2, CFG2_MHL_DE_SEL); 4228f83f268SJie Qiu } 4238f83f268SJie Qiu 4248f83f268SJie Qiu static void mtk_hdmi_hw_aud_set_channel_swap(struct mtk_hdmi *hdmi, 4258f83f268SJie Qiu enum hdmi_aud_channel_swap_type swap) 4268f83f268SJie Qiu { 4278f83f268SJie Qiu u8 swap_bit; 4288f83f268SJie Qiu 4298f83f268SJie Qiu switch (swap) { 4308f83f268SJie Qiu case HDMI_AUD_SWAP_LR: 4318f83f268SJie Qiu swap_bit = LR_SWAP; 4328f83f268SJie Qiu break; 4338f83f268SJie Qiu case HDMI_AUD_SWAP_LFE_CC: 4348f83f268SJie Qiu swap_bit = LFE_CC_SWAP; 4358f83f268SJie Qiu break; 4368f83f268SJie Qiu case HDMI_AUD_SWAP_LSRS: 4378f83f268SJie Qiu swap_bit = LSRS_SWAP; 4388f83f268SJie Qiu break; 4398f83f268SJie Qiu case HDMI_AUD_SWAP_RLS_RRS: 4408f83f268SJie Qiu swap_bit = RLS_RRS_SWAP; 4418f83f268SJie Qiu break; 4428f83f268SJie Qiu case HDMI_AUD_SWAP_LR_STATUS: 4438f83f268SJie Qiu swap_bit = LR_STATUS_SWAP; 4448f83f268SJie Qiu break; 4458f83f268SJie Qiu default: 4468f83f268SJie Qiu swap_bit = LFE_CC_SWAP; 4478f83f268SJie Qiu break; 4488f83f268SJie Qiu } 4498f83f268SJie Qiu mtk_hdmi_mask(hdmi, GRL_CH_SWAP, swap_bit, 0xff); 4508f83f268SJie Qiu } 4518f83f268SJie Qiu 4528f83f268SJie Qiu static void mtk_hdmi_hw_aud_set_bit_num(struct mtk_hdmi *hdmi, 4538f83f268SJie Qiu enum hdmi_audio_sample_size bit_num) 4548f83f268SJie Qiu { 4558f83f268SJie Qiu u32 val; 4568f83f268SJie Qiu 4578f83f268SJie Qiu switch (bit_num) { 4588f83f268SJie Qiu case HDMI_AUDIO_SAMPLE_SIZE_16: 4598f83f268SJie Qiu val = AOUT_16BIT; 4608f83f268SJie Qiu break; 4618f83f268SJie Qiu case HDMI_AUDIO_SAMPLE_SIZE_20: 4628f83f268SJie Qiu val = AOUT_20BIT; 4638f83f268SJie Qiu break; 4648f83f268SJie Qiu case HDMI_AUDIO_SAMPLE_SIZE_24: 4658f83f268SJie Qiu case HDMI_AUDIO_SAMPLE_SIZE_STREAM: 4668f83f268SJie Qiu val = AOUT_24BIT; 4678f83f268SJie Qiu break; 4688f83f268SJie Qiu } 4698f83f268SJie Qiu 4708f83f268SJie Qiu mtk_hdmi_mask(hdmi, GRL_AOUT_CFG, val, AOUT_BNUM_SEL_MASK); 4718f83f268SJie Qiu } 4728f83f268SJie Qiu 4738f83f268SJie Qiu static void mtk_hdmi_hw_aud_set_i2s_fmt(struct mtk_hdmi *hdmi, 4748f83f268SJie Qiu enum hdmi_aud_i2s_fmt i2s_fmt) 4758f83f268SJie Qiu { 4768f83f268SJie Qiu u32 val; 4778f83f268SJie Qiu 4788f83f268SJie Qiu val = mtk_hdmi_read(hdmi, GRL_CFG0); 4798f83f268SJie Qiu val &= ~(CFG0_W_LENGTH_MASK | CFG0_I2S_MODE_MASK); 4808f83f268SJie Qiu 4818f83f268SJie Qiu switch (i2s_fmt) { 4828f83f268SJie Qiu case HDMI_I2S_MODE_RJT_24BIT: 4838f83f268SJie Qiu val |= CFG0_I2S_MODE_RTJ | CFG0_W_LENGTH_24BIT; 4848f83f268SJie Qiu break; 4858f83f268SJie Qiu case HDMI_I2S_MODE_RJT_16BIT: 4868f83f268SJie Qiu val |= CFG0_I2S_MODE_RTJ | CFG0_W_LENGTH_16BIT; 4878f83f268SJie Qiu break; 4888f83f268SJie Qiu case HDMI_I2S_MODE_LJT_24BIT: 4898f83f268SJie Qiu default: 4908f83f268SJie Qiu val |= CFG0_I2S_MODE_LTJ | CFG0_W_LENGTH_24BIT; 4918f83f268SJie Qiu break; 4928f83f268SJie Qiu case HDMI_I2S_MODE_LJT_16BIT: 4938f83f268SJie Qiu val |= CFG0_I2S_MODE_LTJ | CFG0_W_LENGTH_16BIT; 4948f83f268SJie Qiu break; 4958f83f268SJie Qiu case HDMI_I2S_MODE_I2S_24BIT: 4968f83f268SJie Qiu val |= CFG0_I2S_MODE_I2S | CFG0_W_LENGTH_24BIT; 4978f83f268SJie Qiu break; 4988f83f268SJie Qiu case HDMI_I2S_MODE_I2S_16BIT: 4998f83f268SJie Qiu val |= CFG0_I2S_MODE_I2S | CFG0_W_LENGTH_16BIT; 5008f83f268SJie Qiu break; 5018f83f268SJie Qiu } 5028f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_CFG0, val); 5038f83f268SJie Qiu } 5048f83f268SJie Qiu 5058f83f268SJie Qiu static void mtk_hdmi_hw_audio_config(struct mtk_hdmi *hdmi, bool dst) 5068f83f268SJie Qiu { 5078f83f268SJie Qiu const u8 mask = HIGH_BIT_RATE | DST_NORMAL_DOUBLE | SACD_DST | DSD_SEL; 5088f83f268SJie Qiu u8 val; 5098f83f268SJie Qiu 5108f83f268SJie Qiu /* Disable high bitrate, set DST packet normal/double */ 5118f83f268SJie Qiu mtk_hdmi_clear_bits(hdmi, GRL_AOUT_CFG, HIGH_BIT_RATE_PACKET_ALIGN); 5128f83f268SJie Qiu 5138f83f268SJie Qiu if (dst) 5148f83f268SJie Qiu val = DST_NORMAL_DOUBLE | SACD_DST; 5158f83f268SJie Qiu else 5168f83f268SJie Qiu val = 0; 5178f83f268SJie Qiu 5188f83f268SJie Qiu mtk_hdmi_mask(hdmi, GRL_AUDIO_CFG, val, mask); 5198f83f268SJie Qiu } 5208f83f268SJie Qiu 5218f83f268SJie Qiu static void mtk_hdmi_hw_aud_set_i2s_chan_num(struct mtk_hdmi *hdmi, 5228f83f268SJie Qiu enum hdmi_aud_channel_type channel_type, 5238f83f268SJie Qiu u8 channel_count) 5248f83f268SJie Qiu { 5258f83f268SJie Qiu unsigned int ch_switch; 5268f83f268SJie Qiu u8 i2s_uv; 5278f83f268SJie Qiu 5288f83f268SJie Qiu ch_switch = CH_SWITCH(7, 7) | CH_SWITCH(6, 6) | 5298f83f268SJie Qiu CH_SWITCH(5, 5) | CH_SWITCH(4, 4) | 5308f83f268SJie Qiu CH_SWITCH(3, 3) | CH_SWITCH(1, 2) | 5318f83f268SJie Qiu CH_SWITCH(2, 1) | CH_SWITCH(0, 0); 5328f83f268SJie Qiu 5338f83f268SJie Qiu if (channel_count == 2) { 5348f83f268SJie Qiu i2s_uv = I2S_UV_CH_EN(0); 5358f83f268SJie Qiu } else if (channel_count == 3 || channel_count == 4) { 5368f83f268SJie Qiu if (channel_count == 4 && 5378f83f268SJie Qiu (channel_type == HDMI_AUD_CHAN_TYPE_3_0_LRS || 5388f83f268SJie Qiu channel_type == HDMI_AUD_CHAN_TYPE_4_0)) 5398f83f268SJie Qiu i2s_uv = I2S_UV_CH_EN(2) | I2S_UV_CH_EN(0); 5408f83f268SJie Qiu else 5418f83f268SJie Qiu i2s_uv = I2S_UV_CH_EN(3) | I2S_UV_CH_EN(2); 5428f83f268SJie Qiu } else if (channel_count == 6 || channel_count == 5) { 5438f83f268SJie Qiu if (channel_count == 6 && 5448f83f268SJie Qiu channel_type != HDMI_AUD_CHAN_TYPE_5_1 && 5458f83f268SJie Qiu channel_type != HDMI_AUD_CHAN_TYPE_4_1_CLRS) { 5468f83f268SJie Qiu i2s_uv = I2S_UV_CH_EN(3) | I2S_UV_CH_EN(2) | 5478f83f268SJie Qiu I2S_UV_CH_EN(1) | I2S_UV_CH_EN(0); 5488f83f268SJie Qiu } else { 5498f83f268SJie Qiu i2s_uv = I2S_UV_CH_EN(2) | I2S_UV_CH_EN(1) | 5508f83f268SJie Qiu I2S_UV_CH_EN(0); 5518f83f268SJie Qiu } 5528f83f268SJie Qiu } else if (channel_count == 8 || channel_count == 7) { 5538f83f268SJie Qiu i2s_uv = I2S_UV_CH_EN(3) | I2S_UV_CH_EN(2) | 5548f83f268SJie Qiu I2S_UV_CH_EN(1) | I2S_UV_CH_EN(0); 5558f83f268SJie Qiu } else { 5568f83f268SJie Qiu i2s_uv = I2S_UV_CH_EN(0); 5578f83f268SJie Qiu } 5588f83f268SJie Qiu 5598f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_CH_SW0, ch_switch & 0xff); 5608f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_CH_SW1, (ch_switch >> 8) & 0xff); 5618f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_CH_SW2, (ch_switch >> 16) & 0xff); 5628f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_I2S_UV, i2s_uv); 5638f83f268SJie Qiu } 5648f83f268SJie Qiu 5658f83f268SJie Qiu static void mtk_hdmi_hw_aud_set_input_type(struct mtk_hdmi *hdmi, 5668f83f268SJie Qiu enum hdmi_aud_input_type input_type) 5678f83f268SJie Qiu { 5688f83f268SJie Qiu u32 val; 5698f83f268SJie Qiu 5708f83f268SJie Qiu val = mtk_hdmi_read(hdmi, GRL_CFG1); 5718f83f268SJie Qiu if (input_type == HDMI_AUD_INPUT_I2S && 5728f83f268SJie Qiu (val & CFG1_SPDIF) == CFG1_SPDIF) { 5738f83f268SJie Qiu val &= ~CFG1_SPDIF; 5748f83f268SJie Qiu } else if (input_type == HDMI_AUD_INPUT_SPDIF && 5758f83f268SJie Qiu (val & CFG1_SPDIF) == 0) { 5768f83f268SJie Qiu val |= CFG1_SPDIF; 5778f83f268SJie Qiu } 5788f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_CFG1, val); 5798f83f268SJie Qiu } 5808f83f268SJie Qiu 5818f83f268SJie Qiu static void mtk_hdmi_hw_aud_set_channel_status(struct mtk_hdmi *hdmi, 5828f83f268SJie Qiu u8 *channel_status) 5838f83f268SJie Qiu { 5848f83f268SJie Qiu int i; 5858f83f268SJie Qiu 5868f83f268SJie Qiu for (i = 0; i < 5; i++) { 5878f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_I2S_C_STA0 + i * 4, channel_status[i]); 5888f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_L_STATUS_0 + i * 4, channel_status[i]); 5898f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_R_STATUS_0 + i * 4, channel_status[i]); 5908f83f268SJie Qiu } 5918f83f268SJie Qiu for (; i < 24; i++) { 5928f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_L_STATUS_0 + i * 4, 0); 5938f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_R_STATUS_0 + i * 4, 0); 5948f83f268SJie Qiu } 5958f83f268SJie Qiu } 5968f83f268SJie Qiu 5978f83f268SJie Qiu static void mtk_hdmi_hw_aud_src_reenable(struct mtk_hdmi *hdmi) 5988f83f268SJie Qiu { 5998f83f268SJie Qiu u32 val; 6008f83f268SJie Qiu 6018f83f268SJie Qiu val = mtk_hdmi_read(hdmi, GRL_MIX_CTRL); 6028f83f268SJie Qiu if (val & MIX_CTRL_SRC_EN) { 6038f83f268SJie Qiu val &= ~MIX_CTRL_SRC_EN; 6048f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_MIX_CTRL, val); 6058f83f268SJie Qiu usleep_range(255, 512); 6068f83f268SJie Qiu val |= MIX_CTRL_SRC_EN; 6078f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_MIX_CTRL, val); 6088f83f268SJie Qiu } 6098f83f268SJie Qiu } 6108f83f268SJie Qiu 6118f83f268SJie Qiu static void mtk_hdmi_hw_aud_src_disable(struct mtk_hdmi *hdmi) 6128f83f268SJie Qiu { 6138f83f268SJie Qiu u32 val; 6148f83f268SJie Qiu 6158f83f268SJie Qiu val = mtk_hdmi_read(hdmi, GRL_MIX_CTRL); 6168f83f268SJie Qiu val &= ~MIX_CTRL_SRC_EN; 6178f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_MIX_CTRL, val); 6188f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_SHIFT_L1, 0x00); 6198f83f268SJie Qiu } 6208f83f268SJie Qiu 6218f83f268SJie Qiu static void mtk_hdmi_hw_aud_set_mclk(struct mtk_hdmi *hdmi, 6228f83f268SJie Qiu enum hdmi_aud_mclk mclk) 6238f83f268SJie Qiu { 6248f83f268SJie Qiu u32 val; 6258f83f268SJie Qiu 6268f83f268SJie Qiu val = mtk_hdmi_read(hdmi, GRL_CFG5); 6278f83f268SJie Qiu val &= CFG5_CD_RATIO_MASK; 6288f83f268SJie Qiu 6298f83f268SJie Qiu switch (mclk) { 6308f83f268SJie Qiu case HDMI_AUD_MCLK_128FS: 6318f83f268SJie Qiu val |= CFG5_FS128; 6328f83f268SJie Qiu break; 6338f83f268SJie Qiu case HDMI_AUD_MCLK_256FS: 6348f83f268SJie Qiu val |= CFG5_FS256; 6358f83f268SJie Qiu break; 6368f83f268SJie Qiu case HDMI_AUD_MCLK_384FS: 6378f83f268SJie Qiu val |= CFG5_FS384; 6388f83f268SJie Qiu break; 6398f83f268SJie Qiu case HDMI_AUD_MCLK_512FS: 6408f83f268SJie Qiu val |= CFG5_FS512; 6418f83f268SJie Qiu break; 6428f83f268SJie Qiu case HDMI_AUD_MCLK_768FS: 6438f83f268SJie Qiu val |= CFG5_FS768; 6448f83f268SJie Qiu break; 6458f83f268SJie Qiu default: 6468f83f268SJie Qiu val |= CFG5_FS256; 6478f83f268SJie Qiu break; 6488f83f268SJie Qiu } 6498f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_CFG5, val); 6508f83f268SJie Qiu } 6518f83f268SJie Qiu 6528f83f268SJie Qiu struct hdmi_acr_n { 6538f83f268SJie Qiu unsigned int clock; 6548f83f268SJie Qiu unsigned int n[3]; 6558f83f268SJie Qiu }; 6568f83f268SJie Qiu 6578f83f268SJie Qiu /* Recommended N values from HDMI specification, tables 7-1 to 7-3 */ 6588f83f268SJie Qiu static const struct hdmi_acr_n hdmi_rec_n_table[] = { 6598f83f268SJie Qiu /* Clock, N: 32kHz 44.1kHz 48kHz */ 6608f83f268SJie Qiu { 25175, { 4576, 7007, 6864 } }, 6618f83f268SJie Qiu { 74176, { 11648, 17836, 11648 } }, 6628f83f268SJie Qiu { 148352, { 11648, 8918, 5824 } }, 6638f83f268SJie Qiu { 296703, { 5824, 4459, 5824 } }, 6648f83f268SJie Qiu { 297000, { 3072, 4704, 5120 } }, 6658f83f268SJie Qiu { 0, { 4096, 6272, 6144 } }, /* all other TMDS clocks */ 6668f83f268SJie Qiu }; 6678f83f268SJie Qiu 6688f83f268SJie Qiu /** 6698f83f268SJie Qiu * hdmi_recommended_n() - Return N value recommended by HDMI specification 6708f83f268SJie Qiu * @freq: audio sample rate in Hz 6718f83f268SJie Qiu * @clock: rounded TMDS clock in kHz 6728f83f268SJie Qiu */ 6738f83f268SJie Qiu static unsigned int hdmi_recommended_n(unsigned int freq, unsigned int clock) 6748f83f268SJie Qiu { 6758f83f268SJie Qiu const struct hdmi_acr_n *recommended; 6768f83f268SJie Qiu unsigned int i; 6778f83f268SJie Qiu 6788f83f268SJie Qiu for (i = 0; i < ARRAY_SIZE(hdmi_rec_n_table) - 1; i++) { 6798f83f268SJie Qiu if (clock == hdmi_rec_n_table[i].clock) 6808f83f268SJie Qiu break; 6818f83f268SJie Qiu } 6828f83f268SJie Qiu recommended = hdmi_rec_n_table + i; 6838f83f268SJie Qiu 6848f83f268SJie Qiu switch (freq) { 6858f83f268SJie Qiu case 32000: 6868f83f268SJie Qiu return recommended->n[0]; 6878f83f268SJie Qiu case 44100: 6888f83f268SJie Qiu return recommended->n[1]; 6898f83f268SJie Qiu case 48000: 6908f83f268SJie Qiu return recommended->n[2]; 6918f83f268SJie Qiu case 88200: 6928f83f268SJie Qiu return recommended->n[1] * 2; 6938f83f268SJie Qiu case 96000: 6948f83f268SJie Qiu return recommended->n[2] * 2; 6958f83f268SJie Qiu case 176400: 6968f83f268SJie Qiu return recommended->n[1] * 4; 6978f83f268SJie Qiu case 192000: 6988f83f268SJie Qiu return recommended->n[2] * 4; 6998f83f268SJie Qiu default: 7008f83f268SJie Qiu return (128 * freq) / 1000; 7018f83f268SJie Qiu } 7028f83f268SJie Qiu } 7038f83f268SJie Qiu 7048f83f268SJie Qiu static unsigned int hdmi_mode_clock_to_hz(unsigned int clock) 7058f83f268SJie Qiu { 7068f83f268SJie Qiu switch (clock) { 7078f83f268SJie Qiu case 25175: 7088f83f268SJie Qiu return 25174825; /* 25.2/1.001 MHz */ 7098f83f268SJie Qiu case 74176: 7108f83f268SJie Qiu return 74175824; /* 74.25/1.001 MHz */ 7118f83f268SJie Qiu case 148352: 7128f83f268SJie Qiu return 148351648; /* 148.5/1.001 MHz */ 7138f83f268SJie Qiu case 296703: 7148f83f268SJie Qiu return 296703297; /* 297/1.001 MHz */ 7158f83f268SJie Qiu default: 7168f83f268SJie Qiu return clock * 1000; 7178f83f268SJie Qiu } 7188f83f268SJie Qiu } 7198f83f268SJie Qiu 7208f83f268SJie Qiu static unsigned int hdmi_expected_cts(unsigned int audio_sample_rate, 7218f83f268SJie Qiu unsigned int tmds_clock, unsigned int n) 7228f83f268SJie Qiu { 7238f83f268SJie Qiu return DIV_ROUND_CLOSEST_ULL((u64)hdmi_mode_clock_to_hz(tmds_clock) * n, 7248f83f268SJie Qiu 128 * audio_sample_rate); 7258f83f268SJie Qiu } 7268f83f268SJie Qiu 7278f83f268SJie Qiu static void do_hdmi_hw_aud_set_ncts(struct mtk_hdmi *hdmi, unsigned int n, 7288f83f268SJie Qiu unsigned int cts) 7298f83f268SJie Qiu { 7308f83f268SJie Qiu unsigned char val[NCTS_BYTES]; 7318f83f268SJie Qiu int i; 7328f83f268SJie Qiu 7338f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_NCTS, 0); 7348f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_NCTS, 0); 7358f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_NCTS, 0); 7368f83f268SJie Qiu memset(val, 0, sizeof(val)); 7378f83f268SJie Qiu 7388f83f268SJie Qiu val[0] = (cts >> 24) & 0xff; 7398f83f268SJie Qiu val[1] = (cts >> 16) & 0xff; 7408f83f268SJie Qiu val[2] = (cts >> 8) & 0xff; 7418f83f268SJie Qiu val[3] = cts & 0xff; 7428f83f268SJie Qiu 7438f83f268SJie Qiu val[4] = (n >> 16) & 0xff; 7448f83f268SJie Qiu val[5] = (n >> 8) & 0xff; 7458f83f268SJie Qiu val[6] = n & 0xff; 7468f83f268SJie Qiu 7478f83f268SJie Qiu for (i = 0; i < NCTS_BYTES; i++) 7488f83f268SJie Qiu mtk_hdmi_write(hdmi, GRL_NCTS, val[i]); 7498f83f268SJie Qiu } 7508f83f268SJie Qiu 7518f83f268SJie Qiu static void mtk_hdmi_hw_aud_set_ncts(struct mtk_hdmi *hdmi, 7528f83f268SJie Qiu unsigned int sample_rate, 7538f83f268SJie Qiu unsigned int clock) 7548f83f268SJie Qiu { 7558f83f268SJie Qiu unsigned int n, cts; 7568f83f268SJie Qiu 7578f83f268SJie Qiu n = hdmi_recommended_n(sample_rate, clock); 7588f83f268SJie Qiu cts = hdmi_expected_cts(sample_rate, clock, n); 7598f83f268SJie Qiu 7608f83f268SJie Qiu dev_dbg(hdmi->dev, "%s: sample_rate=%u, clock=%d, cts=%u, n=%u\n", 7618f83f268SJie Qiu __func__, sample_rate, clock, n, cts); 7628f83f268SJie Qiu 7638f83f268SJie Qiu mtk_hdmi_mask(hdmi, DUMMY_304, AUDIO_I2S_NCTS_SEL_64, 7648f83f268SJie Qiu AUDIO_I2S_NCTS_SEL); 7658f83f268SJie Qiu do_hdmi_hw_aud_set_ncts(hdmi, n, cts); 7668f83f268SJie Qiu } 7678f83f268SJie Qiu 7688f83f268SJie Qiu static u8 mtk_hdmi_aud_get_chnl_count(enum hdmi_aud_channel_type channel_type) 7698f83f268SJie Qiu { 7708f83f268SJie Qiu switch (channel_type) { 7718f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_1_0: 7728f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_1_1: 7738f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_2_0: 7748f83f268SJie Qiu return 2; 7758f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_2_1: 7768f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_3_0: 7778f83f268SJie Qiu return 3; 7788f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_3_1: 7798f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_4_0: 7808f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_3_0_LRS: 7818f83f268SJie Qiu return 4; 7828f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_4_1: 7838f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_5_0: 7848f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_3_1_LRS: 7858f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_4_0_CLRS: 7868f83f268SJie Qiu return 5; 7878f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_5_1: 7888f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_6_0: 7898f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_4_1_CLRS: 7908f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_6_0_CS: 7918f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_6_0_CH: 7928f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_6_0_OH: 7938f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_6_0_CHR: 7948f83f268SJie Qiu return 6; 7958f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_6_1: 7968f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_6_1_CS: 7978f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_6_1_CH: 7988f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_6_1_OH: 7998f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_6_1_CHR: 8008f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0: 8018f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_LH_RH: 8028f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_LSR_RSR: 8038f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_LC_RC: 8048f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_LW_RW: 8058f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_LSD_RSD: 8068f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_LSS_RSS: 8078f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_LHS_RHS: 8088f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_CS_CH: 8098f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_CS_OH: 8108f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_CS_CHR: 8118f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_CH_OH: 8128f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_CH_CHR: 8138f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_OH_CHR: 8148f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_0_LSS_RSS_LSR_RSR: 8158f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_8_0_LH_RH_CS: 8168f83f268SJie Qiu return 7; 8178f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1: 8188f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_LH_RH: 8198f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_LSR_RSR: 8208f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_LC_RC: 8218f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_LW_RW: 8228f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_LSD_RSD: 8238f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_LSS_RSS: 8248f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_LHS_RHS: 8258f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_CS_CH: 8268f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_CS_OH: 8278f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_CS_CHR: 8288f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_CH_OH: 8298f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_CH_CHR: 8308f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_OH_CHR: 8318f83f268SJie Qiu case HDMI_AUD_CHAN_TYPE_7_1_LSS_RSS_LSR_RSR: 8328f83f268SJie Qiu return 8; 8338f83f268SJie Qiu default: 8348f83f268SJie Qiu return 2; 8358f83f268SJie Qiu } 8368f83f268SJie Qiu } 8378f83f268SJie Qiu 8388f83f268SJie Qiu static int mtk_hdmi_video_change_vpll(struct mtk_hdmi *hdmi, u32 clock) 8398f83f268SJie Qiu { 8408f83f268SJie Qiu unsigned long rate; 8418f83f268SJie Qiu int ret; 8428f83f268SJie Qiu 8438f83f268SJie Qiu /* The DPI driver already should have set TVDPLL to the correct rate */ 8448f83f268SJie Qiu ret = clk_set_rate(hdmi->clk[MTK_HDMI_CLK_HDMI_PLL], clock); 8458f83f268SJie Qiu if (ret) { 8468f83f268SJie Qiu dev_err(hdmi->dev, "Failed to set PLL to %u Hz: %d\n", clock, 8478f83f268SJie Qiu ret); 8488f83f268SJie Qiu return ret; 8498f83f268SJie Qiu } 8508f83f268SJie Qiu 8518f83f268SJie Qiu rate = clk_get_rate(hdmi->clk[MTK_HDMI_CLK_HDMI_PLL]); 8528f83f268SJie Qiu 8538f83f268SJie Qiu if (DIV_ROUND_CLOSEST(rate, 1000) != DIV_ROUND_CLOSEST(clock, 1000)) 8548f83f268SJie Qiu dev_warn(hdmi->dev, "Want PLL %u Hz, got %lu Hz\n", clock, 8558f83f268SJie Qiu rate); 8568f83f268SJie Qiu else 8578f83f268SJie Qiu dev_dbg(hdmi->dev, "Want PLL %u Hz, got %lu Hz\n", clock, rate); 8588f83f268SJie Qiu 8598f83f268SJie Qiu mtk_hdmi_hw_config_sys(hdmi); 8608f83f268SJie Qiu mtk_hdmi_hw_set_deep_color_mode(hdmi); 8618f83f268SJie Qiu return 0; 8628f83f268SJie Qiu } 8638f83f268SJie Qiu 8648f83f268SJie Qiu static void mtk_hdmi_video_set_display_mode(struct mtk_hdmi *hdmi, 8658f83f268SJie Qiu struct drm_display_mode *mode) 8668f83f268SJie Qiu { 8678f83f268SJie Qiu mtk_hdmi_hw_reset(hdmi); 8688f83f268SJie Qiu mtk_hdmi_hw_enable_notice(hdmi, true); 8698f83f268SJie Qiu mtk_hdmi_hw_write_int_mask(hdmi, 0xff); 8708f83f268SJie Qiu mtk_hdmi_hw_enable_dvi_mode(hdmi, hdmi->dvi_mode); 8718f83f268SJie Qiu mtk_hdmi_hw_ncts_auto_write_enable(hdmi, true); 8728f83f268SJie Qiu 8738f83f268SJie Qiu mtk_hdmi_hw_msic_setting(hdmi, mode); 8748f83f268SJie Qiu } 8758f83f268SJie Qiu 8768f83f268SJie Qiu static int mtk_hdmi_aud_enable_packet(struct mtk_hdmi *hdmi, bool enable) 8778f83f268SJie Qiu { 8788f83f268SJie Qiu mtk_hdmi_hw_send_aud_packet(hdmi, enable); 8798f83f268SJie Qiu return 0; 8808f83f268SJie Qiu } 8818f83f268SJie Qiu 8828f83f268SJie Qiu static int mtk_hdmi_aud_on_off_hw_ncts(struct mtk_hdmi *hdmi, bool on) 8838f83f268SJie Qiu { 8848f83f268SJie Qiu mtk_hdmi_hw_ncts_enable(hdmi, on); 8858f83f268SJie Qiu return 0; 8868f83f268SJie Qiu } 8878f83f268SJie Qiu 8888f83f268SJie Qiu static int mtk_hdmi_aud_set_input(struct mtk_hdmi *hdmi) 8898f83f268SJie Qiu { 8908f83f268SJie Qiu enum hdmi_aud_channel_type chan_type; 8918f83f268SJie Qiu u8 chan_count; 8928f83f268SJie Qiu bool dst; 8938f83f268SJie Qiu 8948f83f268SJie Qiu mtk_hdmi_hw_aud_set_channel_swap(hdmi, HDMI_AUD_SWAP_LFE_CC); 8958f83f268SJie Qiu mtk_hdmi_set_bits(hdmi, GRL_MIX_CTRL, MIX_CTRL_FLAT); 8968f83f268SJie Qiu 8978f83f268SJie Qiu if (hdmi->aud_param.aud_input_type == HDMI_AUD_INPUT_SPDIF && 8988f83f268SJie Qiu hdmi->aud_param.aud_codec == HDMI_AUDIO_CODING_TYPE_DST) { 8998f83f268SJie Qiu mtk_hdmi_hw_aud_set_bit_num(hdmi, HDMI_AUDIO_SAMPLE_SIZE_24); 9008f83f268SJie Qiu } else if (hdmi->aud_param.aud_i2s_fmt == HDMI_I2S_MODE_LJT_24BIT) { 9018f83f268SJie Qiu hdmi->aud_param.aud_i2s_fmt = HDMI_I2S_MODE_LJT_16BIT; 9028f83f268SJie Qiu } 9038f83f268SJie Qiu 9048f83f268SJie Qiu mtk_hdmi_hw_aud_set_i2s_fmt(hdmi, hdmi->aud_param.aud_i2s_fmt); 9058f83f268SJie Qiu mtk_hdmi_hw_aud_set_bit_num(hdmi, HDMI_AUDIO_SAMPLE_SIZE_24); 9068f83f268SJie Qiu 9078f83f268SJie Qiu dst = ((hdmi->aud_param.aud_input_type == HDMI_AUD_INPUT_SPDIF) && 9088f83f268SJie Qiu (hdmi->aud_param.aud_codec == HDMI_AUDIO_CODING_TYPE_DST)); 9098f83f268SJie Qiu mtk_hdmi_hw_audio_config(hdmi, dst); 9108f83f268SJie Qiu 9118f83f268SJie Qiu if (hdmi->aud_param.aud_input_type == HDMI_AUD_INPUT_SPDIF) 9128f83f268SJie Qiu chan_type = HDMI_AUD_CHAN_TYPE_2_0; 9138f83f268SJie Qiu else 9148f83f268SJie Qiu chan_type = hdmi->aud_param.aud_input_chan_type; 9158f83f268SJie Qiu chan_count = mtk_hdmi_aud_get_chnl_count(chan_type); 9168f83f268SJie Qiu mtk_hdmi_hw_aud_set_i2s_chan_num(hdmi, chan_type, chan_count); 9178f83f268SJie Qiu mtk_hdmi_hw_aud_set_input_type(hdmi, hdmi->aud_param.aud_input_type); 9188f83f268SJie Qiu 9198f83f268SJie Qiu return 0; 9208f83f268SJie Qiu } 9218f83f268SJie Qiu 9228f83f268SJie Qiu static int mtk_hdmi_aud_set_src(struct mtk_hdmi *hdmi, 9238f83f268SJie Qiu struct drm_display_mode *display_mode) 9248f83f268SJie Qiu { 9258f83f268SJie Qiu unsigned int sample_rate = hdmi->aud_param.codec_params.sample_rate; 9268f83f268SJie Qiu 9278f83f268SJie Qiu mtk_hdmi_aud_on_off_hw_ncts(hdmi, false); 9288f83f268SJie Qiu mtk_hdmi_hw_aud_src_disable(hdmi); 9298f83f268SJie Qiu mtk_hdmi_clear_bits(hdmi, GRL_CFG2, CFG2_ACLK_INV); 9308f83f268SJie Qiu 9318f83f268SJie Qiu if (hdmi->aud_param.aud_input_type == HDMI_AUD_INPUT_I2S) { 9328f83f268SJie Qiu switch (sample_rate) { 9338f83f268SJie Qiu case 32000: 9348f83f268SJie Qiu case 44100: 9358f83f268SJie Qiu case 48000: 9368f83f268SJie Qiu case 88200: 9378f83f268SJie Qiu case 96000: 9388f83f268SJie Qiu break; 9398f83f268SJie Qiu default: 9408f83f268SJie Qiu return -EINVAL; 9418f83f268SJie Qiu } 9428f83f268SJie Qiu mtk_hdmi_hw_aud_set_mclk(hdmi, hdmi->aud_param.aud_mclk); 9438f83f268SJie Qiu } else { 9448f83f268SJie Qiu switch (sample_rate) { 9458f83f268SJie Qiu case 32000: 9468f83f268SJie Qiu case 44100: 9478f83f268SJie Qiu case 48000: 9488f83f268SJie Qiu break; 9498f83f268SJie Qiu default: 9508f83f268SJie Qiu return -EINVAL; 9518f83f268SJie Qiu } 9528f83f268SJie Qiu mtk_hdmi_hw_aud_set_mclk(hdmi, HDMI_AUD_MCLK_128FS); 9538f83f268SJie Qiu } 9548f83f268SJie Qiu 9558f83f268SJie Qiu mtk_hdmi_hw_aud_set_ncts(hdmi, sample_rate, display_mode->clock); 9568f83f268SJie Qiu 9578f83f268SJie Qiu mtk_hdmi_hw_aud_src_reenable(hdmi); 9588f83f268SJie Qiu return 0; 9598f83f268SJie Qiu } 9608f83f268SJie Qiu 9618f83f268SJie Qiu static int mtk_hdmi_aud_output_config(struct mtk_hdmi *hdmi, 9628f83f268SJie Qiu struct drm_display_mode *display_mode) 9638f83f268SJie Qiu { 9648f83f268SJie Qiu mtk_hdmi_hw_aud_mute(hdmi); 9658f83f268SJie Qiu mtk_hdmi_aud_enable_packet(hdmi, false); 9668f83f268SJie Qiu 9678f83f268SJie Qiu mtk_hdmi_aud_set_input(hdmi); 9688f83f268SJie Qiu mtk_hdmi_aud_set_src(hdmi, display_mode); 9698f83f268SJie Qiu mtk_hdmi_hw_aud_set_channel_status(hdmi, 9708f83f268SJie Qiu hdmi->aud_param.codec_params.iec.status); 9718f83f268SJie Qiu 9728f83f268SJie Qiu usleep_range(50, 100); 9738f83f268SJie Qiu 9748f83f268SJie Qiu mtk_hdmi_aud_on_off_hw_ncts(hdmi, true); 9758f83f268SJie Qiu mtk_hdmi_aud_enable_packet(hdmi, true); 9768f83f268SJie Qiu mtk_hdmi_hw_aud_unmute(hdmi); 9778f83f268SJie Qiu return 0; 9788f83f268SJie Qiu } 9798f83f268SJie Qiu 9808f83f268SJie Qiu static int mtk_hdmi_setup_avi_infoframe(struct mtk_hdmi *hdmi, 9818f83f268SJie Qiu struct drm_display_mode *mode) 9828f83f268SJie Qiu { 9838f83f268SJie Qiu struct hdmi_avi_infoframe frame; 9848f83f268SJie Qiu u8 buffer[17]; 9858f83f268SJie Qiu ssize_t err; 9868f83f268SJie Qiu 98713d0add3SVille Syrjälä err = drm_hdmi_avi_infoframe_from_display_mode(&frame, 98813d0add3SVille Syrjälä &hdmi->conn, mode); 9898f83f268SJie Qiu if (err < 0) { 9908f83f268SJie Qiu dev_err(hdmi->dev, 9918f83f268SJie Qiu "Failed to get AVI infoframe from mode: %zd\n", err); 9928f83f268SJie Qiu return err; 9938f83f268SJie Qiu } 9948f83f268SJie Qiu 9958f83f268SJie Qiu err = hdmi_avi_infoframe_pack(&frame, buffer, sizeof(buffer)); 9968f83f268SJie Qiu if (err < 0) { 9978f83f268SJie Qiu dev_err(hdmi->dev, "Failed to pack AVI infoframe: %zd\n", err); 9988f83f268SJie Qiu return err; 9998f83f268SJie Qiu } 10008f83f268SJie Qiu 10018f83f268SJie Qiu mtk_hdmi_hw_send_info_frame(hdmi, buffer, sizeof(buffer)); 10028f83f268SJie Qiu return 0; 10038f83f268SJie Qiu } 10048f83f268SJie Qiu 10058f83f268SJie Qiu static int mtk_hdmi_setup_spd_infoframe(struct mtk_hdmi *hdmi, 10068f83f268SJie Qiu const char *vendor, 10078f83f268SJie Qiu const char *product) 10088f83f268SJie Qiu { 10098f83f268SJie Qiu struct hdmi_spd_infoframe frame; 10108f83f268SJie Qiu u8 buffer[29]; 10118f83f268SJie Qiu ssize_t err; 10128f83f268SJie Qiu 10138f83f268SJie Qiu err = hdmi_spd_infoframe_init(&frame, vendor, product); 10148f83f268SJie Qiu if (err < 0) { 10158f83f268SJie Qiu dev_err(hdmi->dev, "Failed to initialize SPD infoframe: %zd\n", 10168f83f268SJie Qiu err); 10178f83f268SJie Qiu return err; 10188f83f268SJie Qiu } 10198f83f268SJie Qiu 10208f83f268SJie Qiu err = hdmi_spd_infoframe_pack(&frame, buffer, sizeof(buffer)); 10218f83f268SJie Qiu if (err < 0) { 10228f83f268SJie Qiu dev_err(hdmi->dev, "Failed to pack SDP infoframe: %zd\n", err); 10238f83f268SJie Qiu return err; 10248f83f268SJie Qiu } 10258f83f268SJie Qiu 10268f83f268SJie Qiu mtk_hdmi_hw_send_info_frame(hdmi, buffer, sizeof(buffer)); 10278f83f268SJie Qiu return 0; 10288f83f268SJie Qiu } 10298f83f268SJie Qiu 10308f83f268SJie Qiu static int mtk_hdmi_setup_audio_infoframe(struct mtk_hdmi *hdmi) 10318f83f268SJie Qiu { 10328f83f268SJie Qiu struct hdmi_audio_infoframe frame; 10338f83f268SJie Qiu u8 buffer[14]; 10348f83f268SJie Qiu ssize_t err; 10358f83f268SJie Qiu 10368f83f268SJie Qiu err = hdmi_audio_infoframe_init(&frame); 10378f83f268SJie Qiu if (err < 0) { 10388f83f268SJie Qiu dev_err(hdmi->dev, "Failed to setup audio infoframe: %zd\n", 10398f83f268SJie Qiu err); 10408f83f268SJie Qiu return err; 10418f83f268SJie Qiu } 10428f83f268SJie Qiu 10438f83f268SJie Qiu frame.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM; 10448f83f268SJie Qiu frame.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM; 10458f83f268SJie Qiu frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM; 10468f83f268SJie Qiu frame.channels = mtk_hdmi_aud_get_chnl_count( 10478f83f268SJie Qiu hdmi->aud_param.aud_input_chan_type); 10488f83f268SJie Qiu 10498f83f268SJie Qiu err = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer)); 10508f83f268SJie Qiu if (err < 0) { 10518f83f268SJie Qiu dev_err(hdmi->dev, "Failed to pack audio infoframe: %zd\n", 10528f83f268SJie Qiu err); 10538f83f268SJie Qiu return err; 10548f83f268SJie Qiu } 10558f83f268SJie Qiu 10568f83f268SJie Qiu mtk_hdmi_hw_send_info_frame(hdmi, buffer, sizeof(buffer)); 10578f83f268SJie Qiu return 0; 10588f83f268SJie Qiu } 10598f83f268SJie Qiu 10608f83f268SJie Qiu static int mtk_hdmi_setup_vendor_specific_infoframe(struct mtk_hdmi *hdmi, 10618f83f268SJie Qiu struct drm_display_mode *mode) 10628f83f268SJie Qiu { 10638f83f268SJie Qiu struct hdmi_vendor_infoframe frame; 10648f83f268SJie Qiu u8 buffer[10]; 10658f83f268SJie Qiu ssize_t err; 10668f83f268SJie Qiu 1067f1781e9bSVille Syrjälä err = drm_hdmi_vendor_infoframe_from_display_mode(&frame, 1068f1781e9bSVille Syrjälä &hdmi->conn, mode); 10698f83f268SJie Qiu if (err) { 10708f83f268SJie Qiu dev_err(hdmi->dev, 10718f83f268SJie Qiu "Failed to get vendor infoframe from mode: %zd\n", err); 10728f83f268SJie Qiu return err; 10738f83f268SJie Qiu } 10748f83f268SJie Qiu 10758f83f268SJie Qiu err = hdmi_vendor_infoframe_pack(&frame, buffer, sizeof(buffer)); 1076014580ffSYYS if (err < 0) { 10778f83f268SJie Qiu dev_err(hdmi->dev, "Failed to pack vendor infoframe: %zd\n", 10788f83f268SJie Qiu err); 10798f83f268SJie Qiu return err; 10808f83f268SJie Qiu } 10818f83f268SJie Qiu 10828f83f268SJie Qiu mtk_hdmi_hw_send_info_frame(hdmi, buffer, sizeof(buffer)); 10838f83f268SJie Qiu return 0; 10848f83f268SJie Qiu } 10858f83f268SJie Qiu 10868f83f268SJie Qiu static int mtk_hdmi_output_init(struct mtk_hdmi *hdmi) 10878f83f268SJie Qiu { 10888f83f268SJie Qiu struct hdmi_audio_param *aud_param = &hdmi->aud_param; 10898f83f268SJie Qiu 10908f83f268SJie Qiu hdmi->csp = HDMI_COLORSPACE_RGB; 10918f83f268SJie Qiu aud_param->aud_codec = HDMI_AUDIO_CODING_TYPE_PCM; 10928f83f268SJie Qiu aud_param->aud_sampe_size = HDMI_AUDIO_SAMPLE_SIZE_16; 10938f83f268SJie Qiu aud_param->aud_input_type = HDMI_AUD_INPUT_I2S; 10948f83f268SJie Qiu aud_param->aud_i2s_fmt = HDMI_I2S_MODE_I2S_24BIT; 10958f83f268SJie Qiu aud_param->aud_mclk = HDMI_AUD_MCLK_128FS; 10968f83f268SJie Qiu aud_param->aud_input_chan_type = HDMI_AUD_CHAN_TYPE_2_0; 10978f83f268SJie Qiu 10988f83f268SJie Qiu return 0; 10998f83f268SJie Qiu } 11008f83f268SJie Qiu 1101188af070SBaoyou Xie static void mtk_hdmi_audio_enable(struct mtk_hdmi *hdmi) 11028f83f268SJie Qiu { 11038f83f268SJie Qiu mtk_hdmi_aud_enable_packet(hdmi, true); 11048f83f268SJie Qiu hdmi->audio_enable = true; 11058f83f268SJie Qiu } 11068f83f268SJie Qiu 1107188af070SBaoyou Xie static void mtk_hdmi_audio_disable(struct mtk_hdmi *hdmi) 11088f83f268SJie Qiu { 11098f83f268SJie Qiu mtk_hdmi_aud_enable_packet(hdmi, false); 11108f83f268SJie Qiu hdmi->audio_enable = false; 11118f83f268SJie Qiu } 11128f83f268SJie Qiu 1113188af070SBaoyou Xie static int mtk_hdmi_audio_set_param(struct mtk_hdmi *hdmi, 11148f83f268SJie Qiu struct hdmi_audio_param *param) 11158f83f268SJie Qiu { 11168f83f268SJie Qiu if (!hdmi->audio_enable) { 11178f83f268SJie Qiu dev_err(hdmi->dev, "hdmi audio is in disable state!\n"); 11188f83f268SJie Qiu return -EINVAL; 11198f83f268SJie Qiu } 11208f83f268SJie Qiu dev_dbg(hdmi->dev, "codec:%d, input:%d, channel:%d, fs:%d\n", 11218f83f268SJie Qiu param->aud_codec, param->aud_input_type, 11228f83f268SJie Qiu param->aud_input_chan_type, param->codec_params.sample_rate); 11238f83f268SJie Qiu memcpy(&hdmi->aud_param, param, sizeof(*param)); 11248f83f268SJie Qiu return mtk_hdmi_aud_output_config(hdmi, &hdmi->mode); 11258f83f268SJie Qiu } 11268f83f268SJie Qiu 11278f83f268SJie Qiu static int mtk_hdmi_output_set_display_mode(struct mtk_hdmi *hdmi, 11288f83f268SJie Qiu struct drm_display_mode *mode) 11298f83f268SJie Qiu { 11308f83f268SJie Qiu int ret; 11318f83f268SJie Qiu 11328f83f268SJie Qiu mtk_hdmi_hw_vid_black(hdmi, true); 11338f83f268SJie Qiu mtk_hdmi_hw_aud_mute(hdmi); 11348f83f268SJie Qiu mtk_hdmi_hw_send_av_mute(hdmi); 11358f83f268SJie Qiu phy_power_off(hdmi->phy); 11368f83f268SJie Qiu 11378f83f268SJie Qiu ret = mtk_hdmi_video_change_vpll(hdmi, 11388f83f268SJie Qiu mode->clock * 1000); 11398f83f268SJie Qiu if (ret) { 11408f83f268SJie Qiu dev_err(hdmi->dev, "Failed to set vpll: %d\n", ret); 11418f83f268SJie Qiu return ret; 11428f83f268SJie Qiu } 11438f83f268SJie Qiu mtk_hdmi_video_set_display_mode(hdmi, mode); 11448f83f268SJie Qiu 11458f83f268SJie Qiu phy_power_on(hdmi->phy); 11468f83f268SJie Qiu mtk_hdmi_aud_output_config(hdmi, mode); 11478f83f268SJie Qiu 11488f83f268SJie Qiu mtk_hdmi_hw_vid_black(hdmi, false); 11498f83f268SJie Qiu mtk_hdmi_hw_aud_unmute(hdmi); 11508f83f268SJie Qiu mtk_hdmi_hw_send_av_unmute(hdmi); 11518f83f268SJie Qiu 11528f83f268SJie Qiu return 0; 11538f83f268SJie Qiu } 11548f83f268SJie Qiu 11558f83f268SJie Qiu static const char * const mtk_hdmi_clk_names[MTK_HDMI_CLK_COUNT] = { 11568f83f268SJie Qiu [MTK_HDMI_CLK_HDMI_PIXEL] = "pixel", 11578f83f268SJie Qiu [MTK_HDMI_CLK_HDMI_PLL] = "pll", 11588f83f268SJie Qiu [MTK_HDMI_CLK_AUD_BCLK] = "bclk", 11598f83f268SJie Qiu [MTK_HDMI_CLK_AUD_SPDIF] = "spdif", 11608f83f268SJie Qiu }; 11618f83f268SJie Qiu 11628f83f268SJie Qiu static int mtk_hdmi_get_all_clk(struct mtk_hdmi *hdmi, 11638f83f268SJie Qiu struct device_node *np) 11648f83f268SJie Qiu { 11658f83f268SJie Qiu int i; 11668f83f268SJie Qiu 11678f83f268SJie Qiu for (i = 0; i < ARRAY_SIZE(mtk_hdmi_clk_names); i++) { 11688f83f268SJie Qiu hdmi->clk[i] = of_clk_get_by_name(np, 11698f83f268SJie Qiu mtk_hdmi_clk_names[i]); 11708f83f268SJie Qiu if (IS_ERR(hdmi->clk[i])) 11718f83f268SJie Qiu return PTR_ERR(hdmi->clk[i]); 11728f83f268SJie Qiu } 11738f83f268SJie Qiu return 0; 11748f83f268SJie Qiu } 11758f83f268SJie Qiu 11768f83f268SJie Qiu static int mtk_hdmi_clk_enable_audio(struct mtk_hdmi *hdmi) 11778f83f268SJie Qiu { 11788f83f268SJie Qiu int ret; 11798f83f268SJie Qiu 11808f83f268SJie Qiu ret = clk_prepare_enable(hdmi->clk[MTK_HDMI_CLK_AUD_BCLK]); 11818f83f268SJie Qiu if (ret) 11828f83f268SJie Qiu return ret; 11838f83f268SJie Qiu 11848f83f268SJie Qiu ret = clk_prepare_enable(hdmi->clk[MTK_HDMI_CLK_AUD_SPDIF]); 11858f83f268SJie Qiu if (ret) 11868f83f268SJie Qiu goto err; 11878f83f268SJie Qiu 11888f83f268SJie Qiu return 0; 11898f83f268SJie Qiu err: 11908f83f268SJie Qiu clk_disable_unprepare(hdmi->clk[MTK_HDMI_CLK_AUD_BCLK]); 11918f83f268SJie Qiu return ret; 11928f83f268SJie Qiu } 11938f83f268SJie Qiu 11948f83f268SJie Qiu static void mtk_hdmi_clk_disable_audio(struct mtk_hdmi *hdmi) 11958f83f268SJie Qiu { 11968f83f268SJie Qiu clk_disable_unprepare(hdmi->clk[MTK_HDMI_CLK_AUD_BCLK]); 11978f83f268SJie Qiu clk_disable_unprepare(hdmi->clk[MTK_HDMI_CLK_AUD_SPDIF]); 11988f83f268SJie Qiu } 11998f83f268SJie Qiu 12008f83f268SJie Qiu static enum drm_connector_status hdmi_conn_detect(struct drm_connector *conn, 12018f83f268SJie Qiu bool force) 12028f83f268SJie Qiu { 12038f83f268SJie Qiu struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn); 12048f83f268SJie Qiu 12058f83f268SJie Qiu return mtk_cec_hpd_high(hdmi->cec_dev) ? 12068f83f268SJie Qiu connector_status_connected : connector_status_disconnected; 12078f83f268SJie Qiu } 12088f83f268SJie Qiu 12098f83f268SJie Qiu static void hdmi_conn_destroy(struct drm_connector *conn) 12108f83f268SJie Qiu { 12118f83f268SJie Qiu struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn); 12128f83f268SJie Qiu 12138f83f268SJie Qiu mtk_cec_set_hpd_event(hdmi->cec_dev, NULL, NULL); 12148f83f268SJie Qiu 12158f83f268SJie Qiu drm_connector_cleanup(conn); 12168f83f268SJie Qiu } 12178f83f268SJie Qiu 12188f83f268SJie Qiu static int mtk_hdmi_conn_get_modes(struct drm_connector *conn) 12198f83f268SJie Qiu { 12208f83f268SJie Qiu struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn); 12218f83f268SJie Qiu struct edid *edid; 12228f83f268SJie Qiu int ret; 12238f83f268SJie Qiu 12248f83f268SJie Qiu if (!hdmi->ddc_adpt) 12258f83f268SJie Qiu return -ENODEV; 12268f83f268SJie Qiu 12278f83f268SJie Qiu edid = drm_get_edid(conn, hdmi->ddc_adpt); 12288f83f268SJie Qiu if (!edid) 12298f83f268SJie Qiu return -ENODEV; 12308f83f268SJie Qiu 12318f83f268SJie Qiu hdmi->dvi_mode = !drm_detect_monitor_audio(edid); 12328f83f268SJie Qiu 1233c555f023SDaniel Vetter drm_connector_update_edid_property(conn, edid); 12348f83f268SJie Qiu 12358f83f268SJie Qiu ret = drm_add_edid_modes(conn, edid); 12368f83f268SJie Qiu kfree(edid); 12378f83f268SJie Qiu return ret; 12388f83f268SJie Qiu } 12398f83f268SJie Qiu 12408f83f268SJie Qiu static int mtk_hdmi_conn_mode_valid(struct drm_connector *conn, 12418f83f268SJie Qiu struct drm_display_mode *mode) 12428f83f268SJie Qiu { 12438f83f268SJie Qiu struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn); 12448f83f268SJie Qiu 12458f83f268SJie Qiu dev_dbg(hdmi->dev, "xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n", 12468f83f268SJie Qiu mode->hdisplay, mode->vdisplay, mode->vrefresh, 12478f83f268SJie Qiu !!(mode->flags & DRM_MODE_FLAG_INTERLACE), mode->clock * 1000); 12488f83f268SJie Qiu 12498f83f268SJie Qiu if (hdmi->bridge.next) { 12508f83f268SJie Qiu struct drm_display_mode adjusted_mode; 12518f83f268SJie Qiu 12528f83f268SJie Qiu drm_mode_copy(&adjusted_mode, mode); 12538f83f268SJie Qiu if (!drm_bridge_mode_fixup(hdmi->bridge.next, mode, 12548f83f268SJie Qiu &adjusted_mode)) 12558f83f268SJie Qiu return MODE_BAD; 12568f83f268SJie Qiu } 12578f83f268SJie Qiu 12588f83f268SJie Qiu if (mode->clock < 27000) 12598f83f268SJie Qiu return MODE_CLOCK_LOW; 12608f83f268SJie Qiu if (mode->clock > 297000) 12618f83f268SJie Qiu return MODE_CLOCK_HIGH; 12628f83f268SJie Qiu 12638f83f268SJie Qiu return drm_mode_validate_size(mode, 0x1fff, 0x1fff); 12648f83f268SJie Qiu } 12658f83f268SJie Qiu 12668f83f268SJie Qiu static struct drm_encoder *mtk_hdmi_conn_best_enc(struct drm_connector *conn) 12678f83f268SJie Qiu { 12688f83f268SJie Qiu struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn); 12698f83f268SJie Qiu 12708f83f268SJie Qiu return hdmi->bridge.encoder; 12718f83f268SJie Qiu } 12728f83f268SJie Qiu 12738f83f268SJie Qiu static const struct drm_connector_funcs mtk_hdmi_connector_funcs = { 12748f83f268SJie Qiu .detect = hdmi_conn_detect, 12758f83f268SJie Qiu .fill_modes = drm_helper_probe_single_connector_modes, 12768f83f268SJie Qiu .destroy = hdmi_conn_destroy, 12778f83f268SJie Qiu .reset = drm_atomic_helper_connector_reset, 12788f83f268SJie Qiu .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 12798f83f268SJie Qiu .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 12808f83f268SJie Qiu }; 12818f83f268SJie Qiu 12828f83f268SJie Qiu static const struct drm_connector_helper_funcs 12838f83f268SJie Qiu mtk_hdmi_connector_helper_funcs = { 12848f83f268SJie Qiu .get_modes = mtk_hdmi_conn_get_modes, 12858f83f268SJie Qiu .mode_valid = mtk_hdmi_conn_mode_valid, 12868f83f268SJie Qiu .best_encoder = mtk_hdmi_conn_best_enc, 12878f83f268SJie Qiu }; 12888f83f268SJie Qiu 12898f83f268SJie Qiu static void mtk_hdmi_hpd_event(bool hpd, struct device *dev) 12908f83f268SJie Qiu { 12918f83f268SJie Qiu struct mtk_hdmi *hdmi = dev_get_drvdata(dev); 12928f83f268SJie Qiu 12938f83f268SJie Qiu if (hdmi && hdmi->bridge.encoder && hdmi->bridge.encoder->dev) 12948f83f268SJie Qiu drm_helper_hpd_irq_event(hdmi->bridge.encoder->dev); 12958f83f268SJie Qiu } 12968f83f268SJie Qiu 12978f83f268SJie Qiu /* 12988f83f268SJie Qiu * Bridge callbacks 12998f83f268SJie Qiu */ 13008f83f268SJie Qiu 13018f83f268SJie Qiu static int mtk_hdmi_bridge_attach(struct drm_bridge *bridge) 13028f83f268SJie Qiu { 13038f83f268SJie Qiu struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge); 13048f83f268SJie Qiu int ret; 13058f83f268SJie Qiu 13068f83f268SJie Qiu ret = drm_connector_init(bridge->encoder->dev, &hdmi->conn, 13078f83f268SJie Qiu &mtk_hdmi_connector_funcs, 13088f83f268SJie Qiu DRM_MODE_CONNECTOR_HDMIA); 13098f83f268SJie Qiu if (ret) { 13108f83f268SJie Qiu dev_err(hdmi->dev, "Failed to initialize connector: %d\n", ret); 13118f83f268SJie Qiu return ret; 13128f83f268SJie Qiu } 13138f83f268SJie Qiu drm_connector_helper_add(&hdmi->conn, &mtk_hdmi_connector_helper_funcs); 13148f83f268SJie Qiu 13158f83f268SJie Qiu hdmi->conn.polled = DRM_CONNECTOR_POLL_HPD; 13168f83f268SJie Qiu hdmi->conn.interlace_allowed = true; 13178f83f268SJie Qiu hdmi->conn.doublescan_allowed = false; 13188f83f268SJie Qiu 1319cde4c44dSDaniel Vetter ret = drm_connector_attach_encoder(&hdmi->conn, 13208f83f268SJie Qiu bridge->encoder); 13218f83f268SJie Qiu if (ret) { 13228f83f268SJie Qiu dev_err(hdmi->dev, 13238f83f268SJie Qiu "Failed to attach connector to encoder: %d\n", ret); 13248f83f268SJie Qiu return ret; 13258f83f268SJie Qiu } 13268f83f268SJie Qiu 13273bb80f24SLaurent Pinchart if (hdmi->next_bridge) { 13283bb80f24SLaurent Pinchart ret = drm_bridge_attach(bridge->encoder, hdmi->next_bridge, 13293bb80f24SLaurent Pinchart bridge); 13308f83f268SJie Qiu if (ret) { 13318f83f268SJie Qiu dev_err(hdmi->dev, 13328f83f268SJie Qiu "Failed to attach external bridge: %d\n", ret); 13338f83f268SJie Qiu return ret; 13348f83f268SJie Qiu } 13358f83f268SJie Qiu } 13368f83f268SJie Qiu 13378f83f268SJie Qiu mtk_cec_set_hpd_event(hdmi->cec_dev, mtk_hdmi_hpd_event, hdmi->dev); 13388f83f268SJie Qiu 13398f83f268SJie Qiu return 0; 13408f83f268SJie Qiu } 13418f83f268SJie Qiu 13428f83f268SJie Qiu static bool mtk_hdmi_bridge_mode_fixup(struct drm_bridge *bridge, 13438f83f268SJie Qiu const struct drm_display_mode *mode, 13448f83f268SJie Qiu struct drm_display_mode *adjusted_mode) 13458f83f268SJie Qiu { 13468f83f268SJie Qiu return true; 13478f83f268SJie Qiu } 13488f83f268SJie Qiu 13498f83f268SJie Qiu static void mtk_hdmi_bridge_disable(struct drm_bridge *bridge) 13508f83f268SJie Qiu { 13518f83f268SJie Qiu struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge); 13528f83f268SJie Qiu 13538f83f268SJie Qiu if (!hdmi->enabled) 13548f83f268SJie Qiu return; 13558f83f268SJie Qiu 13568f83f268SJie Qiu phy_power_off(hdmi->phy); 13578f83f268SJie Qiu clk_disable_unprepare(hdmi->clk[MTK_HDMI_CLK_HDMI_PIXEL]); 13588f83f268SJie Qiu clk_disable_unprepare(hdmi->clk[MTK_HDMI_CLK_HDMI_PLL]); 13598f83f268SJie Qiu 13608f83f268SJie Qiu hdmi->enabled = false; 13618f83f268SJie Qiu } 13628f83f268SJie Qiu 13638f83f268SJie Qiu static void mtk_hdmi_bridge_post_disable(struct drm_bridge *bridge) 13648f83f268SJie Qiu { 13658f83f268SJie Qiu struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge); 13668f83f268SJie Qiu 13678f83f268SJie Qiu if (!hdmi->powered) 13688f83f268SJie Qiu return; 13698f83f268SJie Qiu 13708f83f268SJie Qiu mtk_hdmi_hw_1p4_version_enable(hdmi, true); 13718f83f268SJie Qiu mtk_hdmi_hw_make_reg_writable(hdmi, false); 13728f83f268SJie Qiu 13738f83f268SJie Qiu hdmi->powered = false; 13748f83f268SJie Qiu } 13758f83f268SJie Qiu 13768f83f268SJie Qiu static void mtk_hdmi_bridge_mode_set(struct drm_bridge *bridge, 137763f8f3baSLaurent Pinchart const struct drm_display_mode *mode, 137863f8f3baSLaurent Pinchart const struct drm_display_mode *adjusted_mode) 13798f83f268SJie Qiu { 13808f83f268SJie Qiu struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge); 13818f83f268SJie Qiu 13828f83f268SJie Qiu dev_dbg(hdmi->dev, "cur info: name:%s, hdisplay:%d\n", 13838f83f268SJie Qiu adjusted_mode->name, adjusted_mode->hdisplay); 13848f83f268SJie Qiu dev_dbg(hdmi->dev, "hsync_start:%d,hsync_end:%d, htotal:%d", 13858f83f268SJie Qiu adjusted_mode->hsync_start, adjusted_mode->hsync_end, 13868f83f268SJie Qiu adjusted_mode->htotal); 13878f83f268SJie Qiu dev_dbg(hdmi->dev, "hskew:%d, vdisplay:%d\n", 13888f83f268SJie Qiu adjusted_mode->hskew, adjusted_mode->vdisplay); 13898f83f268SJie Qiu dev_dbg(hdmi->dev, "vsync_start:%d, vsync_end:%d, vtotal:%d", 13908f83f268SJie Qiu adjusted_mode->vsync_start, adjusted_mode->vsync_end, 13918f83f268SJie Qiu adjusted_mode->vtotal); 13928f83f268SJie Qiu dev_dbg(hdmi->dev, "vscan:%d, flag:%d\n", 13938f83f268SJie Qiu adjusted_mode->vscan, adjusted_mode->flags); 13948f83f268SJie Qiu 13958f83f268SJie Qiu drm_mode_copy(&hdmi->mode, adjusted_mode); 13968f83f268SJie Qiu } 13978f83f268SJie Qiu 13988f83f268SJie Qiu static void mtk_hdmi_bridge_pre_enable(struct drm_bridge *bridge) 13998f83f268SJie Qiu { 14008f83f268SJie Qiu struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge); 14018f83f268SJie Qiu 14028f83f268SJie Qiu mtk_hdmi_hw_make_reg_writable(hdmi, true); 14038f83f268SJie Qiu mtk_hdmi_hw_1p4_version_enable(hdmi, true); 14048f83f268SJie Qiu 14058f83f268SJie Qiu hdmi->powered = true; 14068f83f268SJie Qiu } 14078f83f268SJie Qiu 1408d542b7c4SJunzhi Zhao static void mtk_hdmi_send_infoframe(struct mtk_hdmi *hdmi, 1409d542b7c4SJunzhi Zhao struct drm_display_mode *mode) 1410d542b7c4SJunzhi Zhao { 1411d542b7c4SJunzhi Zhao mtk_hdmi_setup_audio_infoframe(hdmi); 1412d542b7c4SJunzhi Zhao mtk_hdmi_setup_avi_infoframe(hdmi, mode); 1413d542b7c4SJunzhi Zhao mtk_hdmi_setup_spd_infoframe(hdmi, "mediatek", "On-chip HDMI"); 1414d542b7c4SJunzhi Zhao if (mode->flags & DRM_MODE_FLAG_3D_MASK) 1415d542b7c4SJunzhi Zhao mtk_hdmi_setup_vendor_specific_infoframe(hdmi, mode); 1416d542b7c4SJunzhi Zhao } 1417d542b7c4SJunzhi Zhao 14188f83f268SJie Qiu static void mtk_hdmi_bridge_enable(struct drm_bridge *bridge) 14198f83f268SJie Qiu { 14208f83f268SJie Qiu struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge); 14218f83f268SJie Qiu 14228f83f268SJie Qiu mtk_hdmi_output_set_display_mode(hdmi, &hdmi->mode); 14238f83f268SJie Qiu clk_prepare_enable(hdmi->clk[MTK_HDMI_CLK_HDMI_PLL]); 14248f83f268SJie Qiu clk_prepare_enable(hdmi->clk[MTK_HDMI_CLK_HDMI_PIXEL]); 14258f83f268SJie Qiu phy_power_on(hdmi->phy); 1426d542b7c4SJunzhi Zhao mtk_hdmi_send_infoframe(hdmi, &hdmi->mode); 14278f83f268SJie Qiu 14288f83f268SJie Qiu hdmi->enabled = true; 14298f83f268SJie Qiu } 14308f83f268SJie Qiu 14318f83f268SJie Qiu static const struct drm_bridge_funcs mtk_hdmi_bridge_funcs = { 14328f83f268SJie Qiu .attach = mtk_hdmi_bridge_attach, 14338f83f268SJie Qiu .mode_fixup = mtk_hdmi_bridge_mode_fixup, 14348f83f268SJie Qiu .disable = mtk_hdmi_bridge_disable, 14358f83f268SJie Qiu .post_disable = mtk_hdmi_bridge_post_disable, 14368f83f268SJie Qiu .mode_set = mtk_hdmi_bridge_mode_set, 14378f83f268SJie Qiu .pre_enable = mtk_hdmi_bridge_pre_enable, 14388f83f268SJie Qiu .enable = mtk_hdmi_bridge_enable, 14398f83f268SJie Qiu }; 14408f83f268SJie Qiu 14418f83f268SJie Qiu static int mtk_hdmi_dt_parse_pdata(struct mtk_hdmi *hdmi, 14428f83f268SJie Qiu struct platform_device *pdev) 14438f83f268SJie Qiu { 14448f83f268SJie Qiu struct device *dev = &pdev->dev; 14458f83f268SJie Qiu struct device_node *np = dev->of_node; 144686418f90SRob Herring struct device_node *cec_np, *remote, *i2c_np; 14478f83f268SJie Qiu struct platform_device *cec_pdev; 14488f83f268SJie Qiu struct regmap *regmap; 14498f83f268SJie Qiu struct resource *mem; 14508f83f268SJie Qiu int ret; 14518f83f268SJie Qiu 14528f83f268SJie Qiu ret = mtk_hdmi_get_all_clk(hdmi, np); 14538f83f268SJie Qiu if (ret) { 14548f83f268SJie Qiu dev_err(dev, "Failed to get clocks: %d\n", ret); 14558f83f268SJie Qiu return ret; 14568f83f268SJie Qiu } 14578f83f268SJie Qiu 14588f83f268SJie Qiu /* The CEC module handles HDMI hotplug detection */ 1459ceff2f4dSJohan Hovold cec_np = of_get_compatible_child(np->parent, "mediatek,mt8173-cec"); 14608f83f268SJie Qiu if (!cec_np) { 14618f83f268SJie Qiu dev_err(dev, "Failed to find CEC node\n"); 14628f83f268SJie Qiu return -EINVAL; 14638f83f268SJie Qiu } 14648f83f268SJie Qiu 14658f83f268SJie Qiu cec_pdev = of_find_device_by_node(cec_np); 14668f83f268SJie Qiu if (!cec_pdev) { 14674bf99144SRob Herring dev_err(hdmi->dev, "Waiting for CEC device %pOF\n", 14684bf99144SRob Herring cec_np); 1469ceff2f4dSJohan Hovold of_node_put(cec_np); 14708f83f268SJie Qiu return -EPROBE_DEFER; 14718f83f268SJie Qiu } 1472ceff2f4dSJohan Hovold of_node_put(cec_np); 14738f83f268SJie Qiu hdmi->cec_dev = &cec_pdev->dev; 14748f83f268SJie Qiu 14758f83f268SJie Qiu /* 14768f83f268SJie Qiu * The mediatek,syscon-hdmi property contains a phandle link to the 14778f83f268SJie Qiu * MMSYS_CONFIG device and the register offset of the HDMI_SYS_CFG 14788f83f268SJie Qiu * registers it contains. 14798f83f268SJie Qiu */ 14808f83f268SJie Qiu regmap = syscon_regmap_lookup_by_phandle(np, "mediatek,syscon-hdmi"); 14818f83f268SJie Qiu ret = of_property_read_u32_index(np, "mediatek,syscon-hdmi", 1, 14828f83f268SJie Qiu &hdmi->sys_offset); 14838f83f268SJie Qiu if (IS_ERR(regmap)) 14848f83f268SJie Qiu ret = PTR_ERR(regmap); 14858f83f268SJie Qiu if (ret) { 14868f83f268SJie Qiu dev_err(dev, 14878f83f268SJie Qiu "Failed to get system configuration registers: %d\n", 14888f83f268SJie Qiu ret); 14898f83f268SJie Qiu return ret; 14908f83f268SJie Qiu } 14918f83f268SJie Qiu hdmi->sys_regmap = regmap; 14928f83f268SJie Qiu 14938f83f268SJie Qiu mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 14948f83f268SJie Qiu hdmi->regs = devm_ioremap_resource(dev, mem); 14958f83f268SJie Qiu if (IS_ERR(hdmi->regs)) 14968f83f268SJie Qiu return PTR_ERR(hdmi->regs); 14978f83f268SJie Qiu 149886418f90SRob Herring remote = of_graph_get_remote_node(np, 1, 0); 149986418f90SRob Herring if (!remote) 15008f83f268SJie Qiu return -EINVAL; 15018f83f268SJie Qiu 15028f83f268SJie Qiu if (!of_device_is_compatible(remote, "hdmi-connector")) { 15033bb80f24SLaurent Pinchart hdmi->next_bridge = of_drm_find_bridge(remote); 15043bb80f24SLaurent Pinchart if (!hdmi->next_bridge) { 15058f83f268SJie Qiu dev_err(dev, "Waiting for external bridge\n"); 15068f83f268SJie Qiu of_node_put(remote); 15078f83f268SJie Qiu return -EPROBE_DEFER; 15088f83f268SJie Qiu } 15098f83f268SJie Qiu } 15108f83f268SJie Qiu 15118f83f268SJie Qiu i2c_np = of_parse_phandle(remote, "ddc-i2c-bus", 0); 15128f83f268SJie Qiu if (!i2c_np) { 15134bf99144SRob Herring dev_err(dev, "Failed to find ddc-i2c-bus node in %pOF\n", 15144bf99144SRob Herring remote); 15158f83f268SJie Qiu of_node_put(remote); 15168f83f268SJie Qiu return -EINVAL; 15178f83f268SJie Qiu } 15188f83f268SJie Qiu of_node_put(remote); 15198f83f268SJie Qiu 15208f83f268SJie Qiu hdmi->ddc_adpt = of_find_i2c_adapter_by_node(i2c_np); 15212ae2c331SWen Yang of_node_put(i2c_np); 15228f83f268SJie Qiu if (!hdmi->ddc_adpt) { 15238f83f268SJie Qiu dev_err(dev, "Failed to get ddc i2c adapter by node\n"); 15248f83f268SJie Qiu return -EINVAL; 15258f83f268SJie Qiu } 15268f83f268SJie Qiu 15278f83f268SJie Qiu return 0; 15288f83f268SJie Qiu } 15298f83f268SJie Qiu 15308f83f268SJie Qiu /* 15318f83f268SJie Qiu * HDMI audio codec callbacks 15328f83f268SJie Qiu */ 15338f83f268SJie Qiu 15345dd0775eSDave Airlie static int mtk_hdmi_audio_hw_params(struct device *dev, void *data, 15358f83f268SJie Qiu struct hdmi_codec_daifmt *daifmt, 15368f83f268SJie Qiu struct hdmi_codec_params *params) 15378f83f268SJie Qiu { 15388f83f268SJie Qiu struct mtk_hdmi *hdmi = dev_get_drvdata(dev); 15398f83f268SJie Qiu struct hdmi_audio_param hdmi_params; 15408f83f268SJie Qiu unsigned int chan = params->cea.channels; 15418f83f268SJie Qiu 15428f83f268SJie Qiu dev_dbg(hdmi->dev, "%s: %u Hz, %d bit, %d channels\n", __func__, 15438f83f268SJie Qiu params->sample_rate, params->sample_width, chan); 15448f83f268SJie Qiu 15458f83f268SJie Qiu if (!hdmi->bridge.encoder) 15468f83f268SJie Qiu return -ENODEV; 15478f83f268SJie Qiu 15488f83f268SJie Qiu switch (chan) { 15498f83f268SJie Qiu case 2: 15508f83f268SJie Qiu hdmi_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_2_0; 15518f83f268SJie Qiu break; 15528f83f268SJie Qiu case 4: 15538f83f268SJie Qiu hdmi_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_4_0; 15548f83f268SJie Qiu break; 15558f83f268SJie Qiu case 6: 15568f83f268SJie Qiu hdmi_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_5_1; 15578f83f268SJie Qiu break; 15588f83f268SJie Qiu case 8: 15598f83f268SJie Qiu hdmi_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_7_1; 15608f83f268SJie Qiu break; 15618f83f268SJie Qiu default: 15628f83f268SJie Qiu dev_err(hdmi->dev, "channel[%d] not supported!\n", chan); 15638f83f268SJie Qiu return -EINVAL; 15648f83f268SJie Qiu } 15658f83f268SJie Qiu 15668f83f268SJie Qiu switch (params->sample_rate) { 15678f83f268SJie Qiu case 32000: 15688f83f268SJie Qiu case 44100: 15698f83f268SJie Qiu case 48000: 15708f83f268SJie Qiu case 88200: 15718f83f268SJie Qiu case 96000: 15728f83f268SJie Qiu case 176400: 15738f83f268SJie Qiu case 192000: 15748f83f268SJie Qiu break; 15758f83f268SJie Qiu default: 15768f83f268SJie Qiu dev_err(hdmi->dev, "rate[%d] not supported!\n", 15778f83f268SJie Qiu params->sample_rate); 15788f83f268SJie Qiu return -EINVAL; 15798f83f268SJie Qiu } 15808f83f268SJie Qiu 15818f83f268SJie Qiu switch (daifmt->fmt) { 15828f83f268SJie Qiu case HDMI_I2S: 15838f83f268SJie Qiu hdmi_params.aud_codec = HDMI_AUDIO_CODING_TYPE_PCM; 15848f83f268SJie Qiu hdmi_params.aud_sampe_size = HDMI_AUDIO_SAMPLE_SIZE_16; 15858f83f268SJie Qiu hdmi_params.aud_input_type = HDMI_AUD_INPUT_I2S; 15868f83f268SJie Qiu hdmi_params.aud_i2s_fmt = HDMI_I2S_MODE_I2S_24BIT; 15878f83f268SJie Qiu hdmi_params.aud_mclk = HDMI_AUD_MCLK_128FS; 15888f83f268SJie Qiu break; 1589d1ef028dSchunhui dai case HDMI_SPDIF: 1590d1ef028dSchunhui dai hdmi_params.aud_codec = HDMI_AUDIO_CODING_TYPE_PCM; 1591d1ef028dSchunhui dai hdmi_params.aud_sampe_size = HDMI_AUDIO_SAMPLE_SIZE_16; 1592d1ef028dSchunhui dai hdmi_params.aud_input_type = HDMI_AUD_INPUT_SPDIF; 1593d1ef028dSchunhui dai break; 15948f83f268SJie Qiu default: 15958f83f268SJie Qiu dev_err(hdmi->dev, "%s: Invalid DAI format %d\n", __func__, 15968f83f268SJie Qiu daifmt->fmt); 15978f83f268SJie Qiu return -EINVAL; 15988f83f268SJie Qiu } 15998f83f268SJie Qiu 16008f83f268SJie Qiu memcpy(&hdmi_params.codec_params, params, 16018f83f268SJie Qiu sizeof(hdmi_params.codec_params)); 16028f83f268SJie Qiu 16038f83f268SJie Qiu mtk_hdmi_audio_set_param(hdmi, &hdmi_params); 16048f83f268SJie Qiu 16058f83f268SJie Qiu return 0; 16068f83f268SJie Qiu } 16078f83f268SJie Qiu 16085dd0775eSDave Airlie static int mtk_hdmi_audio_startup(struct device *dev, void *data) 16098f83f268SJie Qiu { 16108f83f268SJie Qiu struct mtk_hdmi *hdmi = dev_get_drvdata(dev); 16118f83f268SJie Qiu 16128f83f268SJie Qiu dev_dbg(dev, "%s\n", __func__); 16138f83f268SJie Qiu 16148f83f268SJie Qiu mtk_hdmi_audio_enable(hdmi); 16158f83f268SJie Qiu 16168f83f268SJie Qiu return 0; 16178f83f268SJie Qiu } 16188f83f268SJie Qiu 16195dd0775eSDave Airlie static void mtk_hdmi_audio_shutdown(struct device *dev, void *data) 16208f83f268SJie Qiu { 16218f83f268SJie Qiu struct mtk_hdmi *hdmi = dev_get_drvdata(dev); 16228f83f268SJie Qiu 16238f83f268SJie Qiu dev_dbg(dev, "%s\n", __func__); 16248f83f268SJie Qiu 16258f83f268SJie Qiu mtk_hdmi_audio_disable(hdmi); 16268f83f268SJie Qiu } 16278f83f268SJie Qiu 1628188af070SBaoyou Xie static int 1629188af070SBaoyou Xie mtk_hdmi_audio_digital_mute(struct device *dev, void *data, bool enable) 16308f83f268SJie Qiu { 16318f83f268SJie Qiu struct mtk_hdmi *hdmi = dev_get_drvdata(dev); 16328f83f268SJie Qiu 16338f83f268SJie Qiu dev_dbg(dev, "%s(%d)\n", __func__, enable); 16348f83f268SJie Qiu 16358f83f268SJie Qiu if (enable) 16368f83f268SJie Qiu mtk_hdmi_hw_aud_mute(hdmi); 16378f83f268SJie Qiu else 16388f83f268SJie Qiu mtk_hdmi_hw_aud_unmute(hdmi); 16398f83f268SJie Qiu 16408f83f268SJie Qiu return 0; 16418f83f268SJie Qiu } 16428f83f268SJie Qiu 16435dd0775eSDave Airlie static int mtk_hdmi_audio_get_eld(struct device *dev, void *data, uint8_t *buf, size_t len) 16448f83f268SJie Qiu { 16458f83f268SJie Qiu struct mtk_hdmi *hdmi = dev_get_drvdata(dev); 16468f83f268SJie Qiu 16478f83f268SJie Qiu dev_dbg(dev, "%s\n", __func__); 16488f83f268SJie Qiu 16498f83f268SJie Qiu memcpy(buf, hdmi->conn.eld, min(sizeof(hdmi->conn.eld), len)); 16508f83f268SJie Qiu 16518f83f268SJie Qiu return 0; 16528f83f268SJie Qiu } 16538f83f268SJie Qiu 16548f83f268SJie Qiu static const struct hdmi_codec_ops mtk_hdmi_audio_codec_ops = { 16558f83f268SJie Qiu .hw_params = mtk_hdmi_audio_hw_params, 16568f83f268SJie Qiu .audio_startup = mtk_hdmi_audio_startup, 16578f83f268SJie Qiu .audio_shutdown = mtk_hdmi_audio_shutdown, 16588f83f268SJie Qiu .digital_mute = mtk_hdmi_audio_digital_mute, 16598f83f268SJie Qiu .get_eld = mtk_hdmi_audio_get_eld, 16608f83f268SJie Qiu }; 16618f83f268SJie Qiu 16628f83f268SJie Qiu static void mtk_hdmi_register_audio_driver(struct device *dev) 16638f83f268SJie Qiu { 16648f83f268SJie Qiu struct hdmi_codec_pdata codec_data = { 16658f83f268SJie Qiu .ops = &mtk_hdmi_audio_codec_ops, 16668f83f268SJie Qiu .max_i2s_channels = 2, 16678f83f268SJie Qiu .i2s = 1, 16688f83f268SJie Qiu }; 16698f83f268SJie Qiu struct platform_device *pdev; 16708f83f268SJie Qiu 16718f83f268SJie Qiu pdev = platform_device_register_data(dev, HDMI_CODEC_DRV_NAME, 16728f83f268SJie Qiu PLATFORM_DEVID_AUTO, &codec_data, 16738f83f268SJie Qiu sizeof(codec_data)); 16748f83f268SJie Qiu if (IS_ERR(pdev)) 16758f83f268SJie Qiu return; 16768f83f268SJie Qiu 16778f83f268SJie Qiu DRM_INFO("%s driver bound to HDMI\n", HDMI_CODEC_DRV_NAME); 16788f83f268SJie Qiu } 16798f83f268SJie Qiu 16808f83f268SJie Qiu static int mtk_drm_hdmi_probe(struct platform_device *pdev) 16818f83f268SJie Qiu { 16828f83f268SJie Qiu struct mtk_hdmi *hdmi; 16838f83f268SJie Qiu struct device *dev = &pdev->dev; 16848f83f268SJie Qiu int ret; 16858f83f268SJie Qiu 16868f83f268SJie Qiu hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); 16878f83f268SJie Qiu if (!hdmi) 16888f83f268SJie Qiu return -ENOMEM; 16898f83f268SJie Qiu 16908f83f268SJie Qiu hdmi->dev = dev; 16918f83f268SJie Qiu 16928f83f268SJie Qiu ret = mtk_hdmi_dt_parse_pdata(hdmi, pdev); 16938f83f268SJie Qiu if (ret) 16948f83f268SJie Qiu return ret; 16958f83f268SJie Qiu 16968f83f268SJie Qiu hdmi->phy = devm_phy_get(dev, "hdmi"); 16978f83f268SJie Qiu if (IS_ERR(hdmi->phy)) { 16988f83f268SJie Qiu ret = PTR_ERR(hdmi->phy); 16998f83f268SJie Qiu dev_err(dev, "Failed to get HDMI PHY: %d\n", ret); 17008f83f268SJie Qiu return ret; 17018f83f268SJie Qiu } 17028f83f268SJie Qiu 17038f83f268SJie Qiu platform_set_drvdata(pdev, hdmi); 17048f83f268SJie Qiu 17058f83f268SJie Qiu ret = mtk_hdmi_output_init(hdmi); 17068f83f268SJie Qiu if (ret) { 17078f83f268SJie Qiu dev_err(dev, "Failed to initialize hdmi output\n"); 17088f83f268SJie Qiu return ret; 17098f83f268SJie Qiu } 17108f83f268SJie Qiu 17118f83f268SJie Qiu mtk_hdmi_register_audio_driver(dev); 17128f83f268SJie Qiu 17138f83f268SJie Qiu hdmi->bridge.funcs = &mtk_hdmi_bridge_funcs; 17148f83f268SJie Qiu hdmi->bridge.of_node = pdev->dev.of_node; 1715e6910091SInki Dae drm_bridge_add(&hdmi->bridge); 17168f83f268SJie Qiu 17178f83f268SJie Qiu ret = mtk_hdmi_clk_enable_audio(hdmi); 17188f83f268SJie Qiu if (ret) { 17198f83f268SJie Qiu dev_err(dev, "Failed to enable audio clocks: %d\n", ret); 17208f83f268SJie Qiu goto err_bridge_remove; 17218f83f268SJie Qiu } 17228f83f268SJie Qiu 17238f83f268SJie Qiu dev_dbg(dev, "mediatek hdmi probe success\n"); 17248f83f268SJie Qiu return 0; 17258f83f268SJie Qiu 17268f83f268SJie Qiu err_bridge_remove: 17278f83f268SJie Qiu drm_bridge_remove(&hdmi->bridge); 17288f83f268SJie Qiu return ret; 17298f83f268SJie Qiu } 17308f83f268SJie Qiu 17318f83f268SJie Qiu static int mtk_drm_hdmi_remove(struct platform_device *pdev) 17328f83f268SJie Qiu { 17338f83f268SJie Qiu struct mtk_hdmi *hdmi = platform_get_drvdata(pdev); 17348f83f268SJie Qiu 17358f83f268SJie Qiu drm_bridge_remove(&hdmi->bridge); 17368f83f268SJie Qiu mtk_hdmi_clk_disable_audio(hdmi); 17378f83f268SJie Qiu return 0; 17388f83f268SJie Qiu } 17398f83f268SJie Qiu 17408f83f268SJie Qiu #ifdef CONFIG_PM_SLEEP 17418f83f268SJie Qiu static int mtk_hdmi_suspend(struct device *dev) 17428f83f268SJie Qiu { 17438f83f268SJie Qiu struct mtk_hdmi *hdmi = dev_get_drvdata(dev); 17448f83f268SJie Qiu 17458f83f268SJie Qiu mtk_hdmi_clk_disable_audio(hdmi); 17468f83f268SJie Qiu dev_dbg(dev, "hdmi suspend success!\n"); 17478f83f268SJie Qiu return 0; 17488f83f268SJie Qiu } 17498f83f268SJie Qiu 17508f83f268SJie Qiu static int mtk_hdmi_resume(struct device *dev) 17518f83f268SJie Qiu { 17528f83f268SJie Qiu struct mtk_hdmi *hdmi = dev_get_drvdata(dev); 17538f83f268SJie Qiu int ret = 0; 17548f83f268SJie Qiu 17558f83f268SJie Qiu ret = mtk_hdmi_clk_enable_audio(hdmi); 17568f83f268SJie Qiu if (ret) { 17578f83f268SJie Qiu dev_err(dev, "hdmi resume failed!\n"); 17588f83f268SJie Qiu return ret; 17598f83f268SJie Qiu } 17608f83f268SJie Qiu 17618f83f268SJie Qiu dev_dbg(dev, "hdmi resume success!\n"); 17628f83f268SJie Qiu return 0; 17638f83f268SJie Qiu } 17648f83f268SJie Qiu #endif 17658f83f268SJie Qiu static SIMPLE_DEV_PM_OPS(mtk_hdmi_pm_ops, 17668f83f268SJie Qiu mtk_hdmi_suspend, mtk_hdmi_resume); 17678f83f268SJie Qiu 17688f83f268SJie Qiu static const struct of_device_id mtk_drm_hdmi_of_ids[] = { 17698f83f268SJie Qiu { .compatible = "mediatek,mt8173-hdmi", }, 17708f83f268SJie Qiu {} 17718f83f268SJie Qiu }; 17728f83f268SJie Qiu 17738f83f268SJie Qiu static struct platform_driver mtk_hdmi_driver = { 17748f83f268SJie Qiu .probe = mtk_drm_hdmi_probe, 17758f83f268SJie Qiu .remove = mtk_drm_hdmi_remove, 17768f83f268SJie Qiu .driver = { 17778f83f268SJie Qiu .name = "mediatek-drm-hdmi", 17788f83f268SJie Qiu .of_match_table = mtk_drm_hdmi_of_ids, 17798f83f268SJie Qiu .pm = &mtk_hdmi_pm_ops, 17808f83f268SJie Qiu }, 17818f83f268SJie Qiu }; 17828f83f268SJie Qiu 17838f83f268SJie Qiu static struct platform_driver * const mtk_hdmi_drivers[] = { 17848f83f268SJie Qiu &mtk_hdmi_phy_driver, 17858f83f268SJie Qiu &mtk_hdmi_ddc_driver, 17868f83f268SJie Qiu &mtk_cec_driver, 17878f83f268SJie Qiu &mtk_hdmi_driver, 17888f83f268SJie Qiu }; 17898f83f268SJie Qiu 17908f83f268SJie Qiu static int __init mtk_hdmitx_init(void) 17918f83f268SJie Qiu { 1792446b8c54SPhilipp Zabel return platform_register_drivers(mtk_hdmi_drivers, 1793446b8c54SPhilipp Zabel ARRAY_SIZE(mtk_hdmi_drivers)); 17948f83f268SJie Qiu } 17958f83f268SJie Qiu 17968f83f268SJie Qiu static void __exit mtk_hdmitx_exit(void) 17978f83f268SJie Qiu { 1798446b8c54SPhilipp Zabel platform_unregister_drivers(mtk_hdmi_drivers, 1799446b8c54SPhilipp Zabel ARRAY_SIZE(mtk_hdmi_drivers)); 18008f83f268SJie Qiu } 18018f83f268SJie Qiu 18028f83f268SJie Qiu module_init(mtk_hdmitx_init); 18038f83f268SJie Qiu module_exit(mtk_hdmitx_exit); 18048f83f268SJie Qiu 18058f83f268SJie Qiu MODULE_AUTHOR("Jie Qiu <jie.qiu@mediatek.com>"); 18068f83f268SJie Qiu MODULE_DESCRIPTION("MediaTek HDMI Driver"); 18078f83f268SJie Qiu MODULE_LICENSE("GPL v2"); 1808