From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id F321DC433F5 for ; Fri, 28 Jan 2022 03:26:17 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S242172AbiA1D0R (ORCPT ); Thu, 27 Jan 2022 22:26:17 -0500 Received: from mailgw01.mediatek.com ([60.244.123.138]:36734 "EHLO mailgw01.mediatek.com" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S237063AbiA1D0P (ORCPT ); Thu, 27 Jan 2022 22:26:15 -0500 X-UUID: c11841c0fa7640b18ade6e4129ddcbcb-20220128 X-UUID: c11841c0fa7640b18ade6e4129ddcbcb-20220128 Received: from mtkexhb02.mediatek.inc [(172.21.101.103)] by mailgw01.mediatek.com (envelope-from ) (Generic MTA with TLSv1.2 ECDHE-RSA-AES256-SHA384 256/256) with ESMTP id 577657033; Fri, 28 Jan 2022 11:26:11 +0800 Received: from mtkcas10.mediatek.inc (172.21.101.39) by mtkmbs07n1.mediatek.inc (172.21.101.16) with Microsoft SMTP Server (TLS) id 15.0.1497.2; Fri, 28 Jan 2022 11:26:09 +0800 Received: from mtksdccf07 (172.21.84.99) by mtkcas10.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1497.2 via Frontend Transport; Fri, 28 Jan 2022 11:26:09 +0800 Message-ID: <55d7459dd403b55594e707680b014a8a4c84e344.camel@mediatek.com> Subject: Re: [RFC PATCH 2/2] drm/mediatek: add mtk8195 hdmi display driver From: CK Hu To: Guillaume Ranquet , Chun-Kuang Hu , Philipp Zabel , "David Airlie" , Daniel Vetter , "Matthias Brugger" CC: , , , Date: Fri, 28 Jan 2022 11:26:09 +0800 In-Reply-To: <20211108000847.14320-3-granquet@baylibre.com> References: <20211108000847.14320-1-granquet@baylibre.com> <20211108000847.14320-3-granquet@baylibre.com> Content-Type: text/plain; charset="UTF-8" X-Mailer: Evolution 3.28.5-0ubuntu0.18.04.2 MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-MTK: N Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Hi, Guillaume: On Mon, 2021-11-08 at 01:08 +0100, Guillaume Ranquet wrote: > Signed-off-by: Guillaume Ranquet > Change-Id: I6399dec26cfe56a338c2ca96989cb7cbd14e292b > --- > drivers/gpu/drm/mediatek/Kconfig | 9 + > drivers/gpu/drm/mediatek/Makefile | 2 + > drivers/gpu/drm/mediatek/mtk_hdmi_common.c | 219 +- > drivers/gpu/drm/mediatek/mtk_hdmi_common.h | 24 +- > drivers/gpu/drm/mediatek/mtk_mt8195_hdmi.c | 1835 > +++++++++++++++++ > drivers/gpu/drm/mediatek/mtk_mt8195_hdmi.h | 26 + > .../gpu/drm/mediatek/mtk_mt8195_hdmi_ddc.c | 530 +++++ > .../gpu/drm/mediatek/mtk_mt8195_hdmi_ddc.h | 20 + > .../gpu/drm/mediatek/mtk_mt8195_hdmi_regs.h | 329 +++ > 9 files changed, 2932 insertions(+), 62 deletions(-) > create mode 100644 drivers/gpu/drm/mediatek/mtk_mt8195_hdmi.c > create mode 100644 drivers/gpu/drm/mediatek/mtk_mt8195_hdmi.h > create mode 100644 drivers/gpu/drm/mediatek/mtk_mt8195_hdmi_ddc.c > create mode 100644 drivers/gpu/drm/mediatek/mtk_mt8195_hdmi_ddc.h > create mode 100644 drivers/gpu/drm/mediatek/mtk_mt8195_hdmi_regs.h > > [snip] > > diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_common.h > b/drivers/gpu/drm/mediatek/mtk_hdmi_common.h > index f60b5b8bf99e..a58946d3848b 100644 > --- a/drivers/gpu/drm/mediatek/mtk_hdmi_common.h > +++ b/drivers/gpu/drm/mediatek/mtk_hdmi_common.h > @@ -25,12 +25,17 @@ > > #include > > +//TODO: see what can be done here... :-/ > +#include "mtk_mt8195_hdmi_ddc.h" > +#include "mtk_mt8195_hdmi.h" > #include "mtk_cec.h" > #include "mtk_hdmi.h" > > struct mtk_hdmi_conf { > bool tz_disabled; > bool cea_modes_only; > + bool is_mt8195; > + bool low_power; > unsigned long max_mode_clock; > }; > > @@ -127,6 +132,17 @@ enum hdmi_aud_channel_swap_type { > HDMI_AUD_SWAP_LR_STATUS, > }; > > +enum mtk_hdmi_clk_id_mt8195 { > + MTK_MT8195_HDMI_CLK_UNIVPLL_D6D4, > + MTK_MT8195_HDMI_CLK_MSDCPLL_D2, > + MTK_MT8195_HDMI_CLK_HDMI_APB_SEL, > + MTK_MT8195_HDMI_UNIVPLL_D4D8, > + MTK_MT8195_HDIM_HDCP_SEL, > + MTK_MT8195_HDMI_HDCP_24M_SEL, > + MTK_MT8195_HDMI_VPP_SPLIT_HDMI, > + MTK_MT8195_HDMI_CLK_COUNT, > +}; > + > enum mtk_hdmi_clk_id_mt8183 { > MTK_MT8183_HDMI_CLK_HDMI_PIXEL, > MTK_MT8183_HDMI_CLK_HDMI_PLL, > @@ -136,6 +152,7 @@ enum mtk_hdmi_clk_id_mt8183 { > }; > > extern const char *const > mtk_hdmi_clk_names_mt8183[MTK_MT8183_HDMI_CLK_COUNT]; > +extern const char *const > mtk_hdmi_clk_names_mt8195[MTK_MT8195_HDMI_CLK_COUNT]; > > enum hdmi_hpd_state { > HDMI_PLUG_OUT = 0, > @@ -160,7 +177,8 @@ struct mtk_hdmi { > const struct mtk_hdmi_conf *conf; > struct phy *phy; > struct i2c_adapter *ddc_adpt; > - struct clk *clk[MTK_MT8183_HDMI_CLK_COUNT]; > + //TODO: array size is max(MTK_MT8183_HDMI_CLK_COUNT, > MTK_MT8195_HDMI_CLK_COUNT)... dynamically allocate instead? > + struct clk *clk[MTK_MT8195_HDMI_CLK_COUNT]; > struct drm_display_mode mode; > bool dvi_mode; > struct regmap *sys_regmap; > @@ -228,9 +246,13 @@ int mtk_drm_hdmi_probe(struct platform_device > *pdev); > int mtk_drm_hdmi_remove(struct platform_device *pdev); > > //TODO: do better than this? function pointers? > +extern const struct drm_bridge_funcs mtk_mt8195_hdmi_bridge_funcs; > extern const struct drm_bridge_funcs mtk_mt8183_hdmi_bridge_funcs; > void mtk_hdmi_output_init_mt8183(struct mtk_hdmi *hdmi); > +void mtk_hdmi_output_init_mt8195(struct mtk_hdmi *hdmi); > +void mtk_hdmi_clk_disable_mt8195(struct mtk_hdmi *hdmi); > void mtk_hdmi_clk_disable_mt8183(struct mtk_hdmi *hdmi); > +void set_hdmi_codec_pdata_mt8195(struct hdmi_codec_pdata > *codec_data); > void set_hdmi_codec_pdata_mt8183(struct hdmi_codec_pdata > *codec_data); > > #endif //_MTK_HDMI_COMMON_H > diff --git a/drivers/gpu/drm/mediatek/mtk_mt8195_hdmi.c > b/drivers/gpu/drm/mediatek/mtk_mt8195_hdmi.c > new file mode 100644 > index 000000000000..3fa928b79ed2 > --- /dev/null > +++ b/drivers/gpu/drm/mediatek/mtk_mt8195_hdmi.c > @@ -0,0 +1,1835 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (c) 2021 MediaTek Inc. > + * Copyright (c) 2021 BayLibre, SAS > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > +#include > +#include > +#include > + > +#include "mtk_drm_crtc.h" > +#include "mtk_mt8195_hdmi.h" > +#include "mtk_hdmi_common.h" > +#include "mtk_mt8195_hdmi_ddc.h" > +#include "mtk_mt8195_hdmi_regs.h" > + > +#define RGB444_8bit BIT(0) > +#define RGB444_10bit BIT(1) > +#define RGB444_12bit BIT(2) > +#define RGB444_16bit BIT(3) > + > +#define YCBCR444_8bit BIT(4) > +#define YCBCR444_10bit BIT(5) > +#define YCBCR444_12bit BIT(6) > +#define YCBCR444_16bit BIT(7) > + > +#define YCBCR422_8bit_NO_SUPPORT BIT(8) > +#define YCBCR422_10bit_NO_SUPPORT BIT(9) > +#define YCBCR422_12bit BIT(10) > +#define YCBCR422_16bit_NO_SUPPORT BIT(11) > + > +#define YCBCR420_8bit BIT(12) > +#define YCBCR420_10bit BIT(13) > +#define YCBCR420_12bit BIT(14) > +#define YCBCR420_16bit BIT(15) > + > +#define BYTES_TO_UINT32(msb, b1, b2, > lsb) \ > + (((msb & 0xff) << 24) + ((b1 & 0xff) << 16) + ((b2 & 0xff) << > 8) + \ > + ((lsb & 0xff))) > + > +static inline struct mtk_hdmi *hdmi_ctx_from_conn(struct > drm_connector *c) > +{ > + return container_of(c, struct mtk_hdmi, conn); > +} > + > +static inline void mtk_hdmi_clr_all_int_status(struct mtk_hdmi > *hdmi) > +{ > + /*clear all tx irq*/ > + mtk_hdmi_write(hdmi, TOP_INT_CLR00, 0xffffffff); > + mtk_hdmi_write(hdmi, TOP_INT_CLR00, 0x00000000); > + mtk_hdmi_write(hdmi, TOP_INT_CLR01, 0xffffffff); > + mtk_hdmi_write(hdmi, TOP_INT_CLR01, 0x00000000); > +} > + > +static inline void mtk_hdmi_disable_all_int(struct mtk_hdmi *hdmi) > +{ > + /*disable all tx irq*/ > + mtk_hdmi_write(hdmi, TOP_INT_MASK00, 0x00000000); > + mtk_hdmi_write(hdmi, TOP_INT_MASK01, 0x00000000); > +} > + > +static inline void mtk_hdmi_en_hdcp_reauth_int(struct mtk_hdmi > *hdmi, > + bool enable) > +{ > + if (enable) > + mtk_hdmi_mask(hdmi, TOP_INT_MASK00, > + HDCP2X_RX_REAUTH_REQ_DDCM_INT_UNMASK, > + HDCP2X_RX_REAUTH_REQ_DDCM_INT_UNMASK); > + else > + mtk_hdmi_mask(hdmi, TOP_INT_MASK00, > + HDCP2X_RX_REAUTH_REQ_DDCM_INT_MASK, > + HDCP2X_RX_REAUTH_REQ_DDCM_INT_UNMASK); > +} > + > +static inline void mtk_hdmi_enable_hpd_pord_irq(struct mtk_hdmi > *hdmi, > + bool enable) > +{ > + if (enable) > + mtk_hdmi_mask(hdmi, TOP_INT_MASK00, 0x0000000f, > 0x0000000f); > + else > + mtk_hdmi_mask(hdmi, TOP_INT_MASK00, 0x00000000, > 0x0000000f); > +} > + > +static inline void mtk_hdmi_clr_htplg_pord_irq(struct mtk_hdmi > *hdmi) > +{ > + mtk_hdmi_mask(hdmi, TOP_INT_CLR00, 0x0000000f, 0x0000000f); > + mtk_hdmi_mask(hdmi, TOP_INT_CLR00, 0x00000000, 0x0000000f); > +} > + > +static inline void mtk_hdmi_set_sw_hpd(struct mtk_hdmi *hdmi, bool > high) > +{ > + if (high) > + mtk_hdmi_mask(hdmi, HDMITX_CONFIG, 0x1 << > HDMITX_SW_HPD_SHIFT, > + HDMITX_SW_HPD); > + else > + mtk_hdmi_mask(hdmi, HDMITX_CONFIG, 0x0 << > HDMITX_SW_HPD_SHIFT, > + HDMITX_SW_HPD); > +} > + > +static inline void mtk_hdmi_force_hdcp_hpd(struct mtk_hdmi *hdmi) > +{ > + /* force HDCP HPD to 1*/ > + mtk_hdmi_mask(hdmi, HDCP2X_CTRL_0, HDCP2X_HPD_OVR, > HDCP2X_HPD_OVR); > + mtk_hdmi_mask(hdmi, HDCP2X_CTRL_0, HDCP2X_HPD_SW, > HDCP2X_HPD_SW); > +} > + > +static void mtk_hdmi_disable_hdcp_encrypt(struct mtk_hdmi *hdmi) > +{ > + mtk_hdmi_mask(hdmi, HDCP2X_CTRL_0, 0x0 << > HDCP2X_ENCRYPT_EN_SHIFT, > + HDCP2X_ENCRYPT_EN); > + mtk_hdmi_mask(hdmi, HDCP1X_CTRL, 0x0 << HDCP1X_ENC_EN_SHIFT, > + HDCP1X_ENC_EN); > +} > + > +static void mtk_hdmi_yuv420_downsample(struct mtk_hdmi *hdmi, bool > enable) > +{ > + if (enable) { > + mtk_hdmi_mask(hdmi, HDMITX_CONFIG, > + HDMI_YUV420_MODE | HDMITX_SW_HPD, > + HDMI_YUV420_MODE | HDMITX_SW_HPD); > + mtk_hdmi_mask(hdmi, VID_DOWNSAMPLE_CONFIG, > + C444_C422_CONFIG_ENABLE, > C444_C422_CONFIG_ENABLE); > + mtk_hdmi_mask(hdmi, VID_DOWNSAMPLE_CONFIG, > + C422_C420_CONFIG_ENABLE, > C422_C420_CONFIG_ENABLE); > + mtk_hdmi_mask(hdmi, VID_DOWNSAMPLE_CONFIG, 0, > + C422_C420_CONFIG_BYPASS); > + mtk_hdmi_mask(hdmi, VID_DOWNSAMPLE_CONFIG, > + C422_C420_CONFIG_OUT_CB_OR_CR, > + C422_C420_CONFIG_OUT_CB_OR_CR); > + mtk_hdmi_mask(hdmi, VID_OUT_FORMAT, > + OUTPUT_FORMAT_DEMUX_420_ENABLE, > + OUTPUT_FORMAT_DEMUX_420_ENABLE); > + } else { > + mtk_hdmi_mask(hdmi, HDMITX_CONFIG, 0 | HDMITX_SW_HPD, > + HDMI_YUV420_MODE | HDMITX_SW_HPD); > + mtk_hdmi_mask(hdmi, VID_DOWNSAMPLE_CONFIG, 0, > + C444_C422_CONFIG_ENABLE); > + mtk_hdmi_mask(hdmi, VID_DOWNSAMPLE_CONFIG, 0, > + C422_C420_CONFIG_ENABLE); > + mtk_hdmi_mask(hdmi, VID_DOWNSAMPLE_CONFIG, > + C422_C420_CONFIG_BYPASS, > C422_C420_CONFIG_BYPASS); > + mtk_hdmi_mask(hdmi, VID_DOWNSAMPLE_CONFIG, 0, > + C422_C420_CONFIG_OUT_CB_OR_CR); > + mtk_hdmi_mask(hdmi, VID_OUT_FORMAT, 0, > + OUTPUT_FORMAT_DEMUX_420_ENABLE); > + } > +} > + > +static bool mtk_hdmi_tmds_over_340M(struct mtk_hdmi *hdmi) > +{ > + unsigned long pixel_clk, tmds_clk; > + > + pixel_clk = hdmi->mode.clock * 1000; > + > + /* TMDS clk frequency */ > + if (hdmi->color_depth == HDMI_8_BIT) > + tmds_clk = pixel_clk; > + else if (hdmi->color_depth == HDMI_10_BIT) > + tmds_clk = pixel_clk * 5 / 4; > + else if (hdmi->color_depth == HDMI_12_BIT) > + tmds_clk = pixel_clk * 3 / 2; > + else if (hdmi->color_depth == HDMI_16_BIT) > + tmds_clk = pixel_clk * 2; > + else > + return -EINVAL; > + > + if (tmds_clk >= 340000000 && hdmi->csp != > HDMI_COLORSPACE_YUV420) > + return true; > + > + return false; > +} > + > +static inline void mtk_hdmi_enable_scrambling(struct mtk_hdmi *hdmi, > + bool enable) > +{ > + usleep_range(100, 150); > + > + if (enable) > + mtk_hdmi_mask(hdmi, TOP_CFG00, SCR_ON | HDMI2_ON, > + SCR_ON | HDMI2_ON); > + else > + mtk_hdmi_mask(hdmi, TOP_CFG00, SCR_OFF | HDMI2_OFF, > + SCR_ON | HDMI2_ON); > +} > + > +static void mtk_hdmi_hw_vid_black(struct mtk_hdmi *hdmi, bool black) > +{ > + if (black) > + mtk_hdmi_mask(hdmi, TOP_VMUTE_CFG1, REG_VMUTE_EN, > REG_VMUTE_EN); > + else > + mtk_hdmi_mask(hdmi, TOP_VMUTE_CFG1, 0, REG_VMUTE_EN); > +} > + > +static void mtk_hdmi_hw_aud_mute(struct mtk_hdmi *hdmi) > +{ > + if (mtk_hdmi_read(hdmi, AIP_CTRL) & DSD_EN) > + mtk_hdmi_mask(hdmi, AIP_TXCTRL, > + DSD_MUTE_DATA | AUD_MUTE_FIFO_EN, > + DSD_MUTE_DATA | AUD_MUTE_FIFO_EN); > + else > + mtk_hdmi_mask(hdmi, AIP_TXCTRL, AUD_MUTE_FIFO_EN, > + AUD_MUTE_FIFO_EN); > +} > + > +static void mtk_hdmi_hw_aud_unmute(struct mtk_hdmi *hdmi) > +{ > + mtk_hdmi_mask(hdmi, AIP_TXCTRL, AUD_MUTE_DIS, > AUD_MUTE_FIFO_EN); > +} > + > +static void mtk_hdmi_hw_reset(struct mtk_hdmi *hdmi) > +{ > + mtk_hdmi_mask(hdmi, HDMITX_CONFIG, 0x0 << HDMITX_SW_RSTB_SHIFT, > + HDMITX_SW_RSTB); > + usleep_range(1, 5); > + mtk_hdmi_mask(hdmi, HDMITX_CONFIG, 0x1 << HDMITX_SW_RSTB_SHIFT, > + HDMITX_SW_RSTB); > +} > + > +static void mtk_hdmi_enable_hdmi_mode(struct mtk_hdmi *hdmi, bool > enable) > +{ > + if (enable) > + mtk_hdmi_mask(hdmi, TOP_CFG00, HDMI_MODE_HDMI, > HDMI_MODE_HDMI); > + else > + mtk_hdmi_mask(hdmi, TOP_CFG00, HDMI_MODE_DVI, > HDMI_MODE_HDMI); > +} > + > +static bool mtk_hdmi_sink_is_hdmi_device(struct mtk_hdmi *hdmi) > +{ > + if (hdmi->dvi_mode) > + return false; > + else > + return true; > +} > + > +static void mtk_hdmi_set_deep_color(struct mtk_hdmi *hdmi, bool > is_hdmi_sink) > +{ > + unsigned int deep_color = 0; > + > + /* ycbcr422 12bit no deep color */ > + if (hdmi->csp == HDMI_COLORSPACE_YUV422) { > + deep_color = DEEPCOLOR_MODE_8BIT; > + } else { > + switch (hdmi->color_depth) { > + case HDMI_8_BIT: > + deep_color = DEEPCOLOR_MODE_8BIT; > + break; > + case HDMI_10_BIT: > + deep_color = DEEPCOLOR_MODE_10BIT; > + break; > + case HDMI_12_BIT: > + deep_color = DEEPCOLOR_MODE_12BIT; > + break; > + case HDMI_16_BIT: > + deep_color = DEEPCOLOR_MODE_16BIT; > + break; > + default: > + WARN(1, "Unssupported color depth %d\n", > + hdmi->color_depth); > + } > + } > + > + mtk_hdmi_mask(hdmi, TOP_CFG00, deep_color, > DEEPCOLOR_MODE_MASKBIT); > + > + /* GCP */ > + mtk_hdmi_mask(hdmi, TOP_CFG00, 0, DEEPCOLOR_PAT_EN); > + if (is_hdmi_sink && deep_color != DEEPCOLOR_MODE_8BIT) > + mtk_hdmi_mask(hdmi, TOP_MISC_CTLR, DEEP_COLOR_ADD, > + DEEP_COLOR_ADD); > + else > + mtk_hdmi_mask(hdmi, TOP_MISC_CTLR, 0, DEEP_COLOR_ADD); > +} > + > +static void mtk_hdmi_hw_audio_infoframe(struct mtk_hdmi *hdmi, u8 > *buffer, > + u8 len) > +{ > + enum hdmi_infoframe_type frame_type; > + u8 frame_ver; > + u8 frame_len; > + u8 checksum; > + > + frame_type = buffer[0]; > + frame_ver = buffer[1]; > + frame_len = buffer[2]; > + checksum = buffer[3]; > + > + mtk_hdmi_mask(hdmi, TOP_INFO_EN, AUD_DIS_WR | AUD_DIS, > + AUD_EN_WR | AUD_EN); > + mtk_hdmi_mask(hdmi, TOP_INFO_RPT, AUD_RPT_DIS, AUD_RPT_EN); > + > + mtk_hdmi_write(hdmi, TOP_AIF_HEADER, > + BYTES_TO_UINT32(0, frame_len, frame_ver, > frame_type)); > + mtk_hdmi_write(hdmi, TOP_AIF_PKT00, > + BYTES_TO_UINT32(buffer[6], buffer[5], buffer[4], > + buffer[3])); > + mtk_hdmi_write(hdmi, TOP_AIF_PKT01, > + BYTES_TO_UINT32(0, 0, buffer[8], buffer[7])); > + mtk_hdmi_write(hdmi, TOP_AIF_PKT02, 0); > + mtk_hdmi_write(hdmi, TOP_AIF_PKT03, 0); > + mtk_hdmi_mask(hdmi, TOP_INFO_RPT, AUD_RPT_EN, AUD_RPT_EN); > + mtk_hdmi_mask(hdmi, TOP_INFO_EN, AUD_EN_WR | AUD_EN, > + AUD_EN_WR | AUD_EN); > +} > + > +static void mtk_hdmi_hw_avi_infoframe(struct mtk_hdmi *hdmi, u8 > *buffer, u8 len) > +{ > + mtk_hdmi_mask(hdmi, TOP_INFO_EN, AVI_DIS_WR | AVI_DIS, > + AVI_EN_WR | AVI_EN); > + mtk_hdmi_mask(hdmi, TOP_INFO_RPT, AVI_RPT_DIS, AVI_RPT_EN); > + > + mtk_hdmi_write(hdmi, TOP_AVI_HEADER, > + BYTES_TO_UINT32(0, buffer[2], buffer[1], > buffer[0])); > + > + mtk_hdmi_write(hdmi, TOP_AVI_PKT00, > + BYTES_TO_UINT32(buffer[6], buffer[5], buffer[4], > + buffer[3])); > + > + mtk_hdmi_write(hdmi, TOP_AVI_PKT01, > + BYTES_TO_UINT32(0, buffer[9], buffer[8], > buffer[7])); > + > + mtk_hdmi_write(hdmi, TOP_AVI_PKT02, > + BYTES_TO_UINT32(buffer[13], buffer[12], > buffer[11], > + buffer[10])); > + > + mtk_hdmi_write(hdmi, TOP_AVI_PKT03, > + BYTES_TO_UINT32(0, buffer[16], buffer[15], > buffer[14])); > + > + mtk_hdmi_write(hdmi, TOP_AVI_PKT04, 0); > + mtk_hdmi_write(hdmi, TOP_AVI_PKT05, 0); > + > + mtk_hdmi_mask(hdmi, TOP_INFO_RPT, AVI_RPT_EN, AVI_RPT_EN); > + mtk_hdmi_mask(hdmi, TOP_INFO_EN, AVI_EN_WR | AVI_EN, > + AVI_EN_WR | AVI_EN); > +} > + > +static void mtk_hdmi_hw_spd_infoframe(struct mtk_hdmi *hdmi, u8 > *buffer, u8 len) > +{ > + mtk_hdmi_mask(hdmi, TOP_INFO_EN, SPD_DIS_WR | SPD_DIS, > + SPD_EN_WR | SPD_EN); > + mtk_hdmi_mask(hdmi, TOP_INFO_RPT, SPD_RPT_DIS, SPD_RPT_EN); > + > + mtk_hdmi_write(hdmi, TOP_SPDIF_HEADER, > + BYTES_TO_UINT32(0, buffer[2], buffer[1], > buffer[0])); > + > + mtk_hdmi_write(hdmi, TOP_SPDIF_PKT00, > + BYTES_TO_UINT32(buffer[6], buffer[5], buffer[4], > + buffer[3])); > + > + mtk_hdmi_write(hdmi, TOP_SPDIF_PKT01, > + BYTES_TO_UINT32(0, buffer[9], buffer[8], > buffer[7])); > + > + mtk_hdmi_write(hdmi, TOP_SPDIF_PKT02, > + BYTES_TO_UINT32(buffer[13], buffer[12], > buffer[11], > + buffer[10])); > + > + mtk_hdmi_write(hdmi, TOP_SPDIF_PKT03, > + BYTES_TO_UINT32(0, buffer[16], buffer[15], > buffer[14])); > + > + mtk_hdmi_write(hdmi, TOP_SPDIF_PKT04, > + BYTES_TO_UINT32(buffer[20], buffer[19], > buffer[18], > + buffer[17])); > + > + mtk_hdmi_write(hdmi, TOP_SPDIF_PKT05, > + BYTES_TO_UINT32(0, buffer[23], buffer[22], > buffer[21])); > + > + mtk_hdmi_write(hdmi, TOP_SPDIF_PKT06, > + BYTES_TO_UINT32(buffer[27], buffer[26], > buffer[25], > + buffer[24])); > + > + mtk_hdmi_write(hdmi, TOP_SPDIF_PKT07, > + BYTES_TO_UINT32(0, buffer[30], buffer[29], > buffer[28])); > + > + mtk_hdmi_mask(hdmi, TOP_INFO_RPT, SPD_RPT_EN, SPD_RPT_EN); > + mtk_hdmi_mask(hdmi, TOP_INFO_EN, SPD_EN_WR | SPD_EN, > + SPD_EN_WR | SPD_EN); > +} > + > +static int mtk_hdmi_setup_audio_infoframe(struct mtk_hdmi *hdmi) > +{ > + struct hdmi_codec_params *params = &hdmi- > >aud_param.codec_params; > + struct hdmi_audio_infoframe frame; > + u8 buffer[14]; > + ssize_t err; > + > + memcpy(&frame, ¶ms->cea, sizeof(struct > hdmi_audio_infoframe)); > + > + err = hdmi_audio_infoframe_pack(&frame, buffer, > sizeof(buffer)); > + if (err < 0) > + return err; > + > + mtk_hdmi_hw_audio_infoframe(hdmi, buffer, sizeof(buffer)); > + return 0; > +} > + > +static void mtk_hdmi_hw_send_aud_packet(struct mtk_hdmi *hdmi, bool > enable) > +{ > + if (enable) > + mtk_hdmi_mask(hdmi, AIP_TXCTRL, 0, AUD_PACKET_DROP); > + else > + mtk_hdmi_mask(hdmi, AIP_TXCTRL, AUD_PACKET_DROP, > + AUD_PACKET_DROP); > +} > + > +static inline void mtk_hdmi_hw_send_av_mute(struct mtk_hdmi *hdmi) > +{ > + /*GCP packet */ > + mtk_hdmi_mask(hdmi, TOP_CFG01, 0, CP_CLR_MUTE_EN); > + mtk_hdmi_mask(hdmi, TOP_CFG01, 0, CP_SET_MUTE_EN); > + mtk_hdmi_mask(hdmi, TOP_INFO_RPT, 0, CP_RPT_EN); > + mtk_hdmi_mask(hdmi, TOP_INFO_EN, 0, CP_EN | CP_EN_WR); > + > + mtk_hdmi_mask(hdmi, TOP_CFG01, 0, CP_CLR_MUTE_EN); > + mtk_hdmi_mask(hdmi, TOP_CFG01, CP_SET_MUTE_EN, CP_SET_MUTE_EN); > + mtk_hdmi_mask(hdmi, TOP_INFO_RPT, CP_RPT_EN, CP_RPT_EN); > + mtk_hdmi_mask(hdmi, TOP_INFO_EN, CP_EN | CP_EN_WR, CP_EN | > CP_EN_WR); > +} > + > +static inline void mtk_hdmi_hw_send_av_unmute(struct mtk_hdmi *hdmi) > +{ > + /*GCP packet */ > + mtk_hdmi_mask(hdmi, TOP_CFG01, 0, CP_CLR_MUTE_EN); > + mtk_hdmi_mask(hdmi, TOP_CFG01, 0, CP_SET_MUTE_EN); > + mtk_hdmi_mask(hdmi, TOP_INFO_RPT, 0, CP_RPT_EN); > + mtk_hdmi_mask(hdmi, TOP_INFO_EN, 0, CP_EN | CP_EN_WR); > + > + mtk_hdmi_mask(hdmi, TOP_CFG01, CP_CLR_MUTE_EN, CP_CLR_MUTE_EN); > + mtk_hdmi_mask(hdmi, TOP_CFG01, 0, CP_SET_MUTE_DIS); > + mtk_hdmi_mask(hdmi, TOP_INFO_RPT, CP_RPT_EN, CP_RPT_EN); > + mtk_hdmi_mask(hdmi, TOP_INFO_EN, CP_EN | CP_EN_WR, CP_EN | > CP_EN_WR); > +} > + > +static void mtk_hdmi_hw_ncts_enable(struct mtk_hdmi *hdmi, bool > enable) > +{ > + unsigned int data; > + > + data = mtk_hdmi_read(hdmi, AIP_CTRL); > + > + if (enable) > + data |= CTS_SW_SEL; > + else > + data &= ~CTS_SW_SEL; > + > + mtk_hdmi_write(hdmi, AIP_CTRL, data); > +} > + > +static void mtk_hdmi_hw_aud_set_channel_status(struct mtk_hdmi > *hdmi, > + u8 *channel_status) > +{ > + /* actually, only the first 5 or 7 bytes of Channel Status > + * contain useful information > + */ > + mtk_hdmi_write(hdmi, AIP_I2S_CHST0, > + BYTES_TO_UINT32(channel_status[3], > channel_status[2], > + channel_status[1], > channel_status[0])); > + mtk_hdmi_write(hdmi, AIP_I2S_CHST1, > + BYTES_TO_UINT32(0, channel_status[6], > channel_status[5], > + channel_status[4])); > +} > + > +struct hdmi_acr_n { > + unsigned int clock; > + unsigned int n[3]; > +}; > + > +/* Recommended N values from HDMI specification, tables 7-1 to 7-3 > */ > +static const struct hdmi_acr_n hdmi_rec_n_table[] = { > + /* Clock, N: 32kHz 44.1kHz 48kHz */ > + { 25175, { 4576, 7007, 6864 } }, > + { 74176, { 11648, 17836, 11648 } }, > + { 148352, { 11648, 8918, 5824 } }, > + { 296703, { 5824, 4459, 5824 } }, > + { 297000, { 3072, 4704, 5120 } }, > + { 0, { 4096, 6272, 6144 } }, /* all other TMDS clocks */ > +}; > + > +/** > + * hdmi_recommended_n() - Return N value recommended by HDMI > specification > + * @freq: audio sample rate in Hz > + * @clock: rounded TMDS clock in kHz > + */ > +static int hdmi_recommended_n(unsigned int freq, unsigned int clock) > +{ > + const struct hdmi_acr_n *recommended; > + unsigned int i; > + > + for (i = 0; i < ARRAY_SIZE(hdmi_rec_n_table) - 1; i++) { > + if (clock == hdmi_rec_n_table[i].clock) > + break; > + } > + > + if (i == ARRAY_SIZE(hdmi_rec_n_table)) > + return -EINVAL; > + > + recommended = hdmi_rec_n_table + i; > + > + switch (freq) { > + case 32000: > + return recommended->n[0]; > + case 44100: > + return recommended->n[1]; > + case 48000: > + return recommended->n[2]; > + case 88200: > + return recommended->n[1] * 2; > + case 96000: > + return recommended->n[2] * 2; > + case 176400: > + return recommended->n[1] * 4; > + case 192000: > + return recommended->n[2] * 4; > + default: > + return (128 * freq) / 1000; > + } > +} > + > +static unsigned int hdmi_mode_clock_to_hz(unsigned int clock) > +{ > + switch (clock) { > + case 25175: > + return 25174825; /* 25.2/1.001 MHz */ > + case 74176: > + return 74175824; /* 74.25/1.001 MHz */ > + case 148352: > + return 148351648; /* 148.5/1.001 MHz */ > + case 296703: > + return 296703297; /* 297/1.001 MHz */ > + default: > + return clock * 1000; > + } > +} > + > +static unsigned int hdmi_expected_cts(unsigned int > audio_sample_rate, > + unsigned int tmds_clock, unsigned > int n) > +{ > + return > DIV_ROUND_CLOSEST_ULL((u64)hdmi_mode_clock_to_hz(tmds_clock) * n, > + 128 * audio_sample_rate); > +} > + > +static void mtk_hdmi_hw_aud_set_ncts(struct mtk_hdmi *hdmi, > + unsigned int sample_rate, > + unsigned int clock) > +{ > + unsigned int ncts; > + int n; > + > + n = hdmi_recommended_n(sample_rate, clock); > + > + if (n == -EINVAL) { > + DRM_ERROR("Invalid sample rate: %u\n", sample_rate); > + return; > + } > + > + ncts = hdmi_expected_cts(sample_rate, clock, n); > + mtk_hdmi_write(hdmi, AIP_N_VAL, n); > + mtk_hdmi_write(hdmi, AIP_CTS_SVAL, ncts); > +} > + > +static void mtk_hdmi_aud_enable_packet(struct mtk_hdmi *hdmi, bool > enable) > +{ > + mtk_hdmi_hw_send_aud_packet(hdmi, enable); > +} > + > +static void mtk_hdmi_aud_on_off_hw_ncts(struct mtk_hdmi *hdmi, bool > on) > +{ > + mtk_hdmi_hw_ncts_enable(hdmi, on); > +} > + > +static void mtk_hdmi_audio_dsd_config(struct mtk_hdmi *hdmi, > + unsigned char chnum, bool > dsd_bypass) > +{ > + mtk_hdmi_mask(hdmi, AIP_CTRL, DSD_EN, SPDIF_EN | DSD_EN | > HBRA_ON); > + mtk_hdmi_mask(hdmi, AIP_TXCTRL, DSD_MUTE_DATA, DSD_MUTE_DATA); > + if (dsd_bypass) > + mtk_hdmi_write(hdmi, TOP_AUD_MAP, 0x75316420); > + else > + mtk_hdmi_write(hdmi, TOP_AUD_MAP, 0x04230150); > + > + mtk_hdmi_mask(hdmi, AIP_SPDIF_CTRL, 0, I2S2DSD_EN); > +} > + > +static inline void mtk_hdmi_hw_i2s_fifo_map(struct mtk_hdmi *hdmi, > + unsigned int fifo_mapping) > +{ > + mtk_hdmi_mask(hdmi, AIP_I2S_CTRL, fifo_mapping, > + FIFO3_MAP | FIFO2_MAP | FIFO1_MAP | FIFO0_MAP); > +} > + > +static inline void mtk_hdmi_hw_i2s_ch_number(struct mtk_hdmi *hdmi, > + unsigned int chnum) > +{ > + mtk_hdmi_mask(hdmi, AIP_CTRL, chnum << I2S_EN_SHIFT, I2S_EN); > +} > + > +static void mtk_hdmi_hw_i2s_ch_mapping(struct mtk_hdmi *hdmi, > + unsigned char chnum, > + unsigned char mapping) > +{ > + unsigned int bdata; > + > + switch (chnum) { > + case 2: > + bdata = 0x1; > + break; > + case 3: > + bdata = 0x3; > + break; > + case 6: > + if (mapping == 0x0E) { > + bdata = 0xf; > + break; > + } > + fallthrough; > + case 5: > + bdata = 0x7; > + break; > + case 7: > + case 8: > + bdata = 0xf; > + break; > + default: > + bdata = 0x1; > + } > + > + mtk_hdmi_hw_i2s_fifo_map(hdmi, (MAP_SD3 << 6) | (MAP_SD2 << 4) > | > + (MAP_SD1 << 2) | > (MAP_SD0 << 0)); > + mtk_hdmi_hw_i2s_ch_number(hdmi, bdata); > + > + if (chnum == 2) > + mtk_hdmi_mask(hdmi, AIP_TXCTRL, LAYOUT0, LAYOUT1); > + else > + mtk_hdmi_mask(hdmi, AIP_TXCTRL, LAYOUT1, LAYOUT1); > +} > + > +static void mtk_hdmi_i2s_data_fmt(struct mtk_hdmi *hdmi, unsigned > char fmt) > +{ > + unsigned int u4Data; > + > + u4Data = mtk_hdmi_read(hdmi, AIP_I2S_CTRL); > + u4Data &= ~(WS_HIGH | I2S_1ST_BIT_NOSHIFT | JUSTIFY_RIGHT); > + > + switch (fmt) { > + case HDMI_I2S_MODE_RJT_24BIT: > + case HDMI_I2S_MODE_RJT_16BIT: > + u4Data |= (WS_HIGH | I2S_1ST_BIT_NOSHIFT | > JUSTIFY_RIGHT); > + u4Data |= (WS_HIGH | I2S_1ST_BIT_NOSHIFT | > JUSTIFY_RIGHT); > + break; > + > + case HDMI_I2S_MODE_LJT_24BIT: > + case HDMI_I2S_MODE_LJT_16BIT: > + u4Data |= (WS_HIGH | I2S_1ST_BIT_NOSHIFT); > + u4Data |= (WS_HIGH | I2S_1ST_BIT_NOSHIFT); > + break; > + > + case HDMI_I2S_MODE_I2S_24BIT: > + case HDMI_I2S_MODE_I2S_16BIT: > + default: > + break; > + } > + mtk_hdmi_write(hdmi, AIP_I2S_CTRL, u4Data); > +} > + > +static inline void mtk_hdmi_i2s_sck_edge(struct mtk_hdmi *hdmi, > + unsigned int edge) > +{ > + mtk_hdmi_mask(hdmi, AIP_I2S_CTRL, edge, SCK_EDGE_RISE); > +} > + > +static inline void mtk_hdmi_i2s_cbit_order(struct mtk_hdmi *hdmi, > + unsigned int cbit) > +{ > + mtk_hdmi_mask(hdmi, AIP_I2S_CTRL, cbit, CBIT_ORDER_SAME); > +} > + > +static inline void mtk_hdmi_i2s_vbit(struct mtk_hdmi *hdmi, unsigned > int vbit) > +{ > + mtk_hdmi_mask(hdmi, AIP_I2S_CTRL, vbit, VBIT_COM); > +} > + > +static inline void mtk_hdmi_i2s_data_direction(struct mtk_hdmi > *hdmi, > + unsigned int data_dir) > +{ > + mtk_hdmi_mask(hdmi, AIP_I2S_CTRL, data_dir, DATA_DIR_LSB); > +} > + > +static inline void mtk_hdmi_hw_audio_type(struct mtk_hdmi *hdmi, > + unsigned int spdif_i2s) > +{ > + mtk_hdmi_mask(hdmi, AIP_CTRL, spdif_i2s << SPDIF_EN_SHIFT, > SPDIF_EN); > +} > + > +static unsigned char mtk_hdmi_get_i2s_ch_mapping(struct mtk_hdmi > *hdmi, > + unsigned char > channel_type) > +{ > + unsigned char FC, LFE, RR, RL, RRC, RLC, RC; > + unsigned char ch_number = 0; > + unsigned char channelmap = 0x00; > + > + switch (channel_type) { > + case HDMI_AUD_CHAN_TYPE_1_0: > + case HDMI_AUD_CHAN_TYPE_2_0: > + LFE = 0; > + ch_number = 2; > + break; > + > + case HDMI_AUD_CHAN_TYPE_1_1: > + case HDMI_AUD_CHAN_TYPE_2_1: > + LFE = 1; > + ch_number = 3; > + break; > + > + case HDMI_AUD_CHAN_TYPE_3_0: > + FC = 1; > + LFE = 0; > + ch_number = 3; > + break; > + > + case HDMI_AUD_CHAN_TYPE_3_0_LRS: > + RR = 1; > + RL = 1; > + LFE = 0; > + ch_number = 4; > + break; > + > + case HDMI_AUD_CHAN_TYPE_3_1_LRS: > + FC = 0; > + LFE = 1; > + RR = 1; > + RL = 1; > + ch_number = 5; > + break; > + > + case HDMI_AUD_CHAN_TYPE_4_0_CLRS: > + FC = 1; > + LFE = 0; > + RR = 1; > + RL = 1; > + ch_number = 5; > + break; > + > + case HDMI_AUD_CHAN_TYPE_4_1_CLRS: > + FC = 1; > + LFE = 1; > + RR = 1; > + RL = 1; > + ch_number = 6; > + break; > + > + case HDMI_AUD_CHAN_TYPE_3_1: > + FC = 1; > + LFE = 1; > + ch_number = 4; > + break; > + > + case HDMI_AUD_CHAN_TYPE_4_0: > + RR = 1; > + RL = 1; > + LFE = 0; > + ch_number = 4; > + break; > + > + case HDMI_AUD_CHAN_TYPE_4_1: > + RR = 1; > + RL = 1; > + LFE = 1; > + ch_number = 5; > + break; > + > + case HDMI_AUD_CHAN_TYPE_5_0: > + FC = 1; > + LFE = 0; > + RR = 1; > + RL = 1; > + ch_number = 5; > + break; > + > + case HDMI_AUD_CHAN_TYPE_5_1: > + FC = 1; > + LFE = 1; > + RR = 1; > + RL = 1; > + ch_number = 6; > + break; > + > + case HDMI_AUD_CHAN_TYPE_6_0: > + case HDMI_AUD_CHAN_TYPE_6_0_CS: > + case HDMI_AUD_CHAN_TYPE_6_0_CH: > + case HDMI_AUD_CHAN_TYPE_6_0_OH: > + case HDMI_AUD_CHAN_TYPE_6_0_CHR: > + FC = 1; > + LFE = 0; > + RR = 1; > + RL = 1; > + RC = 1; > + ch_number = 6; > + break; > + > + case HDMI_AUD_CHAN_TYPE_6_1: > + case HDMI_AUD_CHAN_TYPE_6_1_CS: > + case HDMI_AUD_CHAN_TYPE_6_1_CH: > + case HDMI_AUD_CHAN_TYPE_6_1_OH: > + case HDMI_AUD_CHAN_TYPE_6_1_CHR: > + FC = 1; > + LFE = 1; > + RR = 1; > + RL = 1; > + RC = 1; > + ch_number = 7; > + break; > + > + case HDMI_AUD_CHAN_TYPE_7_0: > + case HDMI_AUD_CHAN_TYPE_7_0_LH_RH: > + case HDMI_AUD_CHAN_TYPE_7_0_LSR_RSR: > + case HDMI_AUD_CHAN_TYPE_7_0_LC_RC: > + case HDMI_AUD_CHAN_TYPE_7_0_LW_RW: > + case HDMI_AUD_CHAN_TYPE_7_0_LSD_RSD: > + case HDMI_AUD_CHAN_TYPE_7_0_LSS_RSS: > + case HDMI_AUD_CHAN_TYPE_7_0_LHS_RHS: > + case HDMI_AUD_CHAN_TYPE_7_0_CS_CH: > + case HDMI_AUD_CHAN_TYPE_7_0_CS_OH: > + case HDMI_AUD_CHAN_TYPE_7_0_CS_CHR: > + case HDMI_AUD_CHAN_TYPE_7_0_CH_OH: > + case HDMI_AUD_CHAN_TYPE_7_0_CH_CHR: > + case HDMI_AUD_CHAN_TYPE_7_0_OH_CHR: > + case HDMI_AUD_CHAN_TYPE_7_0_LSS_RSS_LSR_RSR: > + case HDMI_AUD_CHAN_TYPE_8_0_LH_RH_CS: > + FC = 1; > + LFE = 0; > + RR = 1; > + RL = 1; > + RRC = 1; > + RLC = 1; > + ch_number = 7; > + break; > + > + case HDMI_AUD_CHAN_TYPE_7_1: > + case HDMI_AUD_CHAN_TYPE_7_1_LH_RH: > + case HDMI_AUD_CHAN_TYPE_7_1_LSR_RSR: > + case HDMI_AUD_CHAN_TYPE_7_1_LC_RC: > + case HDMI_AUD_CHAN_TYPE_7_1_LW_RW: > + case HDMI_AUD_CHAN_TYPE_7_1_LSD_RSD: > + case HDMI_AUD_CHAN_TYPE_7_1_LSS_RSS: > + case HDMI_AUD_CHAN_TYPE_7_1_LHS_RHS: > + case HDMI_AUD_CHAN_TYPE_7_1_CS_CH: > + case HDMI_AUD_CHAN_TYPE_7_1_CS_OH: > + case HDMI_AUD_CHAN_TYPE_7_1_CS_CHR: > + case HDMI_AUD_CHAN_TYPE_7_1_CH_OH: > + case HDMI_AUD_CHAN_TYPE_7_1_CH_CHR: > + case HDMI_AUD_CHAN_TYPE_7_1_OH_CHR: > + case HDMI_AUD_CHAN_TYPE_7_1_LSS_RSS_LSR_RSR: > + FC = 1; > + LFE = 1; > + RR = 1; > + RL = 1; > + RRC = 1; > + RLC = 1; > + ch_number = 8; > + break; > + > + default: > + ch_number = 2; > + break; > + } > + > + switch (ch_number) { > + case 8: > + break; > + > + case 7: > + break; > + > + case 6: > + if (FC == 1 && RR == 1 && RL == 1 && RC == 1 && LFE == > 0) { > + /* 6.0 */ > + channelmap = 0x0E; > + } else if (FC == 1 && RR == 1 && RL == 1 && RC == 0 && > + LFE == 1) { > + /* 5.1 */ > + channelmap = 0x0B; > + } > + break; > + > + case 5: > + break; > + > + case 4: > + if (RR == 1 && RL == 1 && LFE == 0) > + channelmap = 0x08; > + else if (FC == 1 && LFE == 1) > + channelmap = 0x03; > + break; > + > + case 3: > + if (FC == 1) > + channelmap = 0x02; > + else if (LFE == 1) > + channelmap = 0x01; > + break; > + > + case 2: > + channelmap = 0x00; > + break; > + > + default: > + break; > + } > + > + return channelmap; > +} > + > +static inline void mtk_hdmi_hw_i2s_ch_swap(struct mtk_hdmi *hdmi, > + unsigned char swapbit) > +{ > + mtk_hdmi_mask(hdmi, AIP_SPDIF_CTRL, swapbit << 20, 0x0F << 20); > +} > + > +static void mtk_hdmi_hbr_config(struct mtk_hdmi *hdmi, bool > dsd_bypass) > +{ > + if (dsd_bypass) { > + mtk_hdmi_mask(hdmi, AIP_CTRL, HBRA_ON, > + SPDIF_EN | DSD_EN | HBRA_ON); > + mtk_hdmi_mask(hdmi, AIP_CTRL, I2S_EN, I2S_EN); > + } else { > + mtk_hdmi_mask(hdmi, AIP_CTRL, SPDIF_EN, > + SPDIF_EN | DSD_EN | HBRA_ON); > + mtk_hdmi_mask(hdmi, AIP_CTRL, SPDIF_INTERNAL_MODULE, > + SPDIF_INTERNAL_MODULE); > + mtk_hdmi_mask(hdmi, AIP_CTRL, HBR_FROM_SPDIF, > HBR_FROM_SPDIF); > + mtk_hdmi_mask(hdmi, AIP_CTRL, CTS_CAL_N4, CTS_CAL_N4); > + } > +} > + > +static inline void mtk_hdmi_hw_spdif_config(struct mtk_hdmi *hdmi) > +{ > + mtk_hdmi_mask(hdmi, AIP_SPDIF_CTRL, WR_1UI_UNLOCK, > WR_1UI_LOCK); > + mtk_hdmi_mask(hdmi, AIP_SPDIF_CTRL, FS_UNOVERRIDE, > FS_OVERRIDE_WRITE); > + mtk_hdmi_mask(hdmi, AIP_SPDIF_CTRL, WR_2UI_UNLOCK, > WR_2UI_LOCK); > + mtk_hdmi_mask(hdmi, AIP_SPDIF_CTRL, 0x4 << MAX_1UI_WRITE_SHIFT, > + MAX_1UI_WRITE); > + mtk_hdmi_mask(hdmi, AIP_SPDIF_CTRL, 0x9 << MAX_2UI_WRITE_SHIFT, > + MAX_2UI_WRITE); > + mtk_hdmi_mask(hdmi, AIP_SPDIF_CTRL, 0x4 << > AUD_ERR_THRESH_SHIFT, > + AUD_ERR_THRESH); > + mtk_hdmi_mask(hdmi, AIP_SPDIF_CTRL, I2S2DSD_EN, I2S2DSD_EN); > +} > + > +static void mtk_hdmi_aud_set_input(struct mtk_hdmi *hdmi) > +{ > + unsigned char ChMapping; > + > + mtk_hdmi_write(hdmi, TOP_AUD_MAP, > + C_SD7 + C_SD6 + C_SD5 + C_SD4 + C_SD3 + C_SD2 + > C_SD1 + > + C_SD0); > + mtk_hdmi_mask(hdmi, AIP_SPDIF_CTRL, 0, 0x0F << 20); > + mtk_hdmi_mask(hdmi, AIP_CTRL, 0, > + SPDIF_EN | DSD_EN | HBRA_ON | CTS_CAL_N4 | > + HBR_FROM_SPDIF | SPDIF_INTERNAL_MODULE); > + mtk_hdmi_mask(hdmi, AIP_TXCTRL, 0, DSD_MUTE_DATA | LAYOUT1); > + > + if (hdmi->aud_param.aud_input_type == HDMI_AUD_INPUT_I2S) { > + if (hdmi->aud_param.aud_codec == > HDMI_AUDIO_CODING_TYPE_DSD) { > + mtk_hdmi_audio_dsd_config( > + hdmi, hdmi- > >aud_param.codec_params.channels, > + 0); > + mtk_hdmi_hw_i2s_ch_mapping( > + hdmi, hdmi- > >aud_param.codec_params.channels, > + 1); > + } else { > + mtk_hdmi_i2s_data_fmt(hdmi, > + hdmi- > >aud_param.aud_i2s_fmt); > + mtk_hdmi_i2s_sck_edge(hdmi, SCK_EDGE_RISE); > + mtk_hdmi_i2s_cbit_order(hdmi, CBIT_ORDER_SAME); > + mtk_hdmi_i2s_vbit(hdmi, VBIT_PCM); > + mtk_hdmi_i2s_data_direction(hdmi, > DATA_DIR_MSB); > + mtk_hdmi_hw_audio_type(hdmi, > HDMI_AUD_INPUT_I2S); > + ChMapping = mtk_hdmi_get_i2s_ch_mapping( > + hdmi, hdmi- > >aud_param.aud_input_chan_type); > + mtk_hdmi_hw_i2s_ch_mapping( > + hdmi, hdmi- > >aud_param.codec_params.channels, > + ChMapping); > + mtk_hdmi_hw_i2s_ch_swap(hdmi, LFE_CC_SWAP); > + } > + } else { > + if (hdmi->aud_param.aud_input_type == > HDMI_AUD_INPUT_SPDIF && > + (hdmi->aud_param.aud_codec == > + HDMI_AUDIO_CODING_TYPE_DTS_HD || > + hdmi->aud_param.aud_codec == > + HDMI_AUDIO_CODING_TYPE_MLP) && > + hdmi->aud_param.codec_params.sample_rate == 768000) > { > + mtk_hdmi_hbr_config(hdmi, false); > + } else { > + mtk_hdmi_hw_spdif_config(hdmi); > + mtk_hdmi_hw_i2s_ch_mapping(hdmi, 2, 0); > + } > + } > +} > + > +static void mtk_hdmi_aud_set_sw_ncts(struct mtk_hdmi *hdmi, > + struct drm_display_mode > *display_mode) > +{ > + unsigned int sample_rate = hdmi- > >aud_param.codec_params.sample_rate; > + > + mtk_hdmi_aud_on_off_hw_ncts(hdmi, false); > + > + mtk_hdmi_hw_aud_set_ncts(hdmi, sample_rate, display_mode- > >clock); > +} > + > +static inline void mtk_hdmi_hw_audio_input_enable(struct mtk_hdmi > *hdmi, > + unsigned int enable) > +{ > + if (enable) > + mtk_hdmi_mask(hdmi, AIP_CTRL, AUD_IN_EN, AUD_IN_EN); > + else > + mtk_hdmi_mask(hdmi, AIP_CTRL, 0x0 << AUD_IN_EN_SHIFT, > + AUD_IN_EN); > +} > + > +static void mtk_hdmi_aip_ctrl_init(struct mtk_hdmi *hdmi) > +{ > + mtk_hdmi_mask(hdmi, AIP_CTRL, > + AUD_SEL_OWRT | NO_MCLK_CTSGEN_SEL | CTS_REQ_EN, > + AUD_SEL_OWRT | NO_MCLK_CTSGEN_SEL | MCLK_EN | > CTS_REQ_EN); > + mtk_hdmi_mask(hdmi, AIP_TPI_CTRL, TPI_AUDIO_LOOKUP_DIS, > + TPI_AUDIO_LOOKUP_EN); > +} > + > +static void mtk_hdmi_audio_reset(struct mtk_hdmi *hdmi, bool rst) > +{ > + if (rst) > + mtk_hdmi_mask(hdmi, AIP_TXCTRL, > + RST4AUDIO | RST4AUDIO_FIFO | > RST4AUDIO_ACR, > + RST4AUDIO | RST4AUDIO_FIFO | > RST4AUDIO_ACR); > + else > + mtk_hdmi_mask(hdmi, AIP_TXCTRL, 0, > + RST4AUDIO | RST4AUDIO_FIFO | > RST4AUDIO_ACR); > +} > + > +static void mtk_hdmi_aud_output_config(struct mtk_hdmi *hdmi, > + struct drm_display_mode > *display_mode) > +{ > + mtk_hdmi_hw_aud_mute(hdmi); > + mtk_hdmi_aud_enable_packet(hdmi, false); > + mtk_hdmi_audio_reset(hdmi, true); > + mtk_hdmi_aip_ctrl_init(hdmi); > + > + mtk_hdmi_aud_set_input(hdmi); > + > + mtk_hdmi_hw_aud_set_channel_status( > + hdmi, hdmi->aud_param.codec_params.iec.status); > + > + mtk_hdmi_setup_audio_infoframe(hdmi); > + > + mtk_hdmi_hw_audio_input_enable(hdmi, true); > + > + mtk_hdmi_audio_reset(hdmi, false); > + > + mtk_hdmi_aud_set_sw_ncts(hdmi, display_mode); > + > + usleep_range(25, 50); > + mtk_hdmi_aud_on_off_hw_ncts(hdmi, true); > + > + mtk_hdmi_aud_enable_packet(hdmi, true); > + mtk_hdmi_hw_aud_unmute(hdmi); > +} > + > +void mtk_hdmi_output_init_mt8195(struct mtk_hdmi *hdmi) > +{ > + struct hdmi_audio_param *aud_param = &hdmi->aud_param; > + > + aud_param->aud_codec = HDMI_AUDIO_CODING_TYPE_PCM; > + aud_param->aud_sampe_size = HDMI_AUDIO_SAMPLE_SIZE_16; > + aud_param->aud_input_type = HDMI_AUD_INPUT_I2S; > + aud_param->aud_i2s_fmt = HDMI_I2S_MODE_I2S_24BIT; > + aud_param->aud_mclk = HDMI_AUD_MCLK_128FS; > + aud_param->aud_input_chan_type = HDMI_AUD_CHAN_TYPE_2_0; > + > + hdmi->hpd = HDMI_PLUG_OUT; > + hdmi->set_csp_depth = RGB444_8bit; > + hdmi->csp = HDMI_COLORSPACE_RGB; > + hdmi->color_depth = HDMI_8_BIT; > + hdmi->colorimtery = HDMI_COLORIMETRY_NONE; > + hdmi->extended_colorimetry = > HDMI_EXTENDED_COLORIMETRY_RESERVED; > + hdmi->quantization_range = HDMI_QUANTIZATION_RANGE_DEFAULT; > + hdmi->ycc_quantization_range = > HDMI_YCC_QUANTIZATION_RANGE_LIMITED; > +} > + > +static void mtk_hdmi_reset_colorspace_setting(struct mtk_hdmi *hdmi) > +{ > + hdmi->set_csp_depth = RGB444_8bit; > + hdmi->csp = HDMI_COLORSPACE_RGB; > + hdmi->color_depth = HDMI_8_BIT; > + hdmi->colorimtery = HDMI_COLORIMETRY_NONE; > + hdmi->extended_colorimetry = > HDMI_EXTENDED_COLORIMETRY_RESERVED; > + hdmi->quantization_range = HDMI_QUANTIZATION_RANGE_DEFAULT; > + hdmi->ycc_quantization_range = > HDMI_YCC_QUANTIZATION_RANGE_LIMITED; > +} > + > +static void mtk_hdmi_audio_enable(struct mtk_hdmi *hdmi) > +{ > + mtk_hdmi_aud_enable_packet(hdmi, true); > + hdmi->audio_enable = true; > +} > + > +static void mtk_hdmi_audio_disable(struct mtk_hdmi *hdmi) > +{ > + mtk_hdmi_aud_enable_packet(hdmi, false); > + hdmi->audio_enable = false; > +} > + > +static void mtk_hdmi_audio_set_param(struct mtk_hdmi *hdmi, > + struct hdmi_audio_param *param) > +{ > + if (!hdmi->audio_enable) > + return; > + > + memcpy(&hdmi->aud_param, param, sizeof(*param)); > + mtk_hdmi_aud_output_config(hdmi, &hdmi->mode); > +} > + > +static void mtk_hdmi_change_video_resolution(struct mtk_hdmi *hdmi) > +{ > + bool is_over_340M = false; > + bool is_hdmi_sink = false; > + > + mtk_hdmi_hw_reset(hdmi); > + mtk_hdmi_set_sw_hpd(hdmi, true); > + usleep_range(2, 5); > + > + mtk_hdmi_write(hdmi, HDCP_TOP_CTRL, 0x0); > + mtk_hdmi_en_hdcp_reauth_int(hdmi, true); > + mtk_hdmi_enable_hpd_pord_irq(hdmi, true); > + mtk_hdmi_force_hdcp_hpd(hdmi); > + > + is_hdmi_sink = mtk_hdmi_sink_is_hdmi_device(hdmi); > + mtk_hdmi_set_deep_color(hdmi, is_hdmi_sink); > + mtk_hdmi_enable_hdmi_mode(hdmi, is_hdmi_sink); > + > + usleep_range(5, 10); > + mtk_hdmi_hw_vid_black(hdmi, true); > + mtk_hdmi_hw_aud_mute(hdmi); > + mtk_hdmi_hw_send_av_unmute(hdmi); > + > + mtk_hdmi_mask(hdmi, TOP_CFG01, NULL_PKT_VSYNC_HIGH_EN, > + NULL_PKT_VSYNC_HIGH_EN | NULL_PKT_EN); > + > + is_over_340M = mtk_hdmi_tmds_over_340M(hdmi); > + mtk_hdmi_enable_scrambling(hdmi, is_over_340M); > + > + if (hdmi->csp == HDMI_COLORSPACE_YUV420) > + mtk_hdmi_yuv420_downsample(hdmi, true); > + else > + mtk_hdmi_yuv420_downsample(hdmi, false); > +} > + > +static void mtk_hdmi_output_set_display_mode(struct mtk_hdmi *hdmi, > + struct drm_display_mode > *mode) > +{ Try to implement this function like the one in mt8173 hdmi driver. That means the callee function and the callee function of callee function. So I would compare the difference of mt8173 and mt8195. > + mtk_hdmi_change_video_resolution(hdmi); > + mtk_hdmi_aud_output_config(hdmi, mode); > +} > + > +static void mtk_hdmi_clk_enable(struct mtk_hdmi *hdmi) > +{ > + int i; > + > + clk_set_parent(hdmi->clk[MTK_MT8195_HDIM_HDCP_SEL], > + hdmi->clk[MTK_MT8195_HDMI_UNIVPLL_D4D8]); > + > + for (i = 0; i < ARRAY_SIZE(mtk_hdmi_clk_names_mt8195); i++) { > + if (i == MTK_MT8195_HDMI_UNIVPLL_D4D8 || > + i == MTK_MT8195_HDMI_CLK_UNIVPLL_D6D4 || > + i == MTK_MT8195_HDMI_CLK_MSDCPLL_D2 || > + i == MTK_MT8195_HDMI_CLK_HDMI_APB_SEL) > + continue; > + else > + clk_prepare_enable(hdmi->clk[i]); > + } > +} > + > +void mtk_hdmi_clk_disable_mt8195(struct mtk_hdmi *hdmi) > +{ > + int i; > + > + for (i = 0; i < ARRAY_SIZE(mtk_hdmi_clk_names_mt8195); i++) { > + if (i == MTK_MT8195_HDMI_UNIVPLL_D4D8 || > + i == MTK_MT8195_HDMI_CLK_UNIVPLL_D6D4 || > + i == MTK_MT8195_HDMI_CLK_MSDCPLL_D2 || > + i == MTK_MT8195_HDMI_CLK_HDMI_APB_SEL) > + continue; > + else > + clk_disable_unprepare(hdmi->clk[i]); > + } > +} > + > +static void mtk_hdmi_hpd_event(enum hdmi_hpd_state hpd, struct > device *dev) > +{ > + struct mtk_hdmi *hdmi = dev_get_drvdata(dev); > + > + if (hdmi && hdmi->bridge.encoder && hdmi->bridge.encoder->dev) > + drm_helper_hpd_irq_event(hdmi->bridge.encoder->dev); > +} > + > +static enum hdmi_hpd_state mtk_hdmi_hpd_pord_status(struct mtk_hdmi > *hdmi) > +{ > + unsigned int hpd_status; > + > + hpd_status = mtk_hdmi_read(hdmi, HPD_DDC_STATUS); > + if ((hpd_status & (HPD_PIN_STA | PORD_PIN_STA)) == > + (HPD_PIN_STA | PORD_PIN_STA)) > + return HDMI_PLUG_IN_AND_SINK_POWER_ON; > + else if ((hpd_status & (HPD_PIN_STA | PORD_PIN_STA)) == > PORD_PIN_STA) > + return HDMI_PLUG_IN_ONLY; > + else > + return HDMI_PLUG_OUT; > +} > + > +static irqreturn_t mtk_hdmi_isr(int irq, void *arg) > +{ > + struct mtk_hdmi *hdmi = arg; > + unsigned int int_status; > + int ret = IRQ_HANDLED; > + > + int_status = mtk_hdmi_read(hdmi, TOP_INT_STA00); > + > + /* handle hpd interrupt */ > + if (int_status & (PORD_F_INT_STA | PORD_R_INT_STA | > HTPLG_F_INT_STA | > + HTPLG_R_INT_STA)) { > + mtk_hdmi_enable_hpd_pord_irq(hdmi, false); > + mtk_hdmi_clr_htplg_pord_irq(hdmi); > + ret = IRQ_WAKE_THREAD; > + } > + > + /*clear all tx irq*/ > + mtk_hdmi_clr_all_int_status(hdmi); > + > + return ret; > +} > + > +static irqreturn_t mtk_hdmi_hpd_work_handle(int irq, void *arg) > +{ > + struct mtk_hdmi *hdmi = arg; > + enum hdmi_hpd_state hpd; > + > + hpd = mtk_hdmi_hpd_pord_status(hdmi); > + if (hpd != hdmi->hpd) { > + hdmi->hpd = hpd; > + mtk_hdmi_hpd_event(hpd, hdmi->dev); > + } > + > + mtk_hdmi_enable_hpd_pord_irq(hdmi, true); > + return IRQ_HANDLED; > +} > + > +static enum drm_connector_status hdmi_conn_detect(struct > drm_connector *conn, > + bool force) > +{ > + struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn); > + > + if (hdmi->hpd != HDMI_PLUG_IN_AND_SINK_POWER_ON && > + hdmi->hpd != HDMI_PLUG_IN_ONLY) { > + hdmi->support_csp_depth = RGB444_8bit; > + hdmi->set_csp_depth = RGB444_8bit; > + hdmi->csp = HDMI_COLORSPACE_RGB; > + hdmi->color_depth = HDMI_8_BIT; > + hdmi->colorimtery = HDMI_COLORIMETRY_NONE; > + hdmi->extended_colorimetry = > HDMI_EXTENDED_COLORIMETRY_RESERVED; > + hdmi->quantization_range = > HDMI_QUANTIZATION_RANGE_DEFAULT; > + hdmi->ycc_quantization_range = > + HDMI_YCC_QUANTIZATION_RANGE_LIMITED; > + } > + > + return (hdmi->hpd != HDMI_PLUG_OUT) ? > connector_status_connected : > + connector_status_di > sconnected; > +} > + > +static void hdmi_conn_destroy(struct drm_connector *conn) > +{ > + drm_connector_cleanup(conn); > +} > + > +static int hdmi_conn_atomic_set_property(struct drm_connector *conn, > + struct drm_connector_state > *state, > + struct drm_property *property, > + uint64_t val) > +{ > + struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn); > + int ret = 0; > + > + spin_lock(&hdmi->property_lock); > + > + if (property == hdmi->csp_depth_prop) { > + if (val & hdmi->support_csp_depth) > + hdmi->set_csp_depth = val; > + else > + ret = -EINVAL; > + } else { > + ret = -EINVAL; > + } > + > + spin_unlock(&hdmi->property_lock); > + return ret; > +} > + > +static int > +hdmi_conn_atomic_get_property(struct drm_connector *conn, > + const struct drm_connector_state *state, > + struct drm_property *property, uint64_t > *val) > +{ > + int ret = -EINVAL; > + struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn); > + > + spin_lock(&hdmi->property_lock); > + > + if (property == hdmi->csp_depth_prop) { > + *val = hdmi->support_csp_depth; > + ret = 0; > + } > + > + spin_unlock(&hdmi->property_lock); > + return ret; > +} > + > +static int mtk_hdmi_enable_disable(struct mtk_hdmi *hdmi, bool > enable) > +{ > + int ret; > + > + if (enable && !hdmi->hdmi_enabled) { > + if (!hdmi->power_clk_enabled) { > + /* power domain on */ > + ret = pm_runtime_get_sync(hdmi->dev); > + > + /* clk on */ > + mtk_hdmi_clk_enable(hdmi); > + hdmi->power_clk_enabled = true; > + } > + > + if (!hdmi->irq_registered) { > + /* disable all tx interrupts */ > + mtk_hdmi_disable_all_int(hdmi); > + /* request irq */ > + hdmi->hdmi_irq = > + irq_of_parse_and_map(hdmi->dev- > >of_node, 0); > + ret = request_threaded_irq(hdmi->hdmi_irq, > mtk_hdmi_isr, > + mtk_hdmi_hpd_work_ha > ndle, > + IRQF_TRIGGER_HIGH, > "hdmiirq", > + hdmi); > + hdmi->irq_registered = true; > + /* enable hpd interrupt */ > + mtk_hdmi_set_sw_hpd(hdmi, true); > + mtk_hdmi_enable_hpd_pord_irq(hdmi, true); > + } > + > + } else if (!enable && hdmi->hdmi_enabled) { > + if (hdmi->irq_registered) { > + /* free irq */ > + free_irq(hdmi->hdmi_irq, NULL); > + hdmi->irq_registered = false; > + } > + > + if (hdmi->power_clk_enabled) { > + /* clk disable */ > + mtk_hdmi_clk_disable_mt8195(hdmi); > + /* power domain off */ > + ret = pm_runtime_put_sync(hdmi->dev); > + hdmi->power_clk_enabled = false; > + } > + } > + > + hdmi->hdmi_enabled = enable; > + > + return 0; > +} > + > +static const struct drm_prop_enum_list csp_depth_props[] = { > + { __builtin_ffs(RGB444_8bit), "RGB444_8bit" }, > + { __builtin_ffs(RGB444_10bit), "RGB444_10bit" }, > + { __builtin_ffs(RGB444_12bit), "RGB444_10bit" }, > + { __builtin_ffs(RGB444_16bit), "RGB444_16bit" }, > + { __builtin_ffs(YCBCR444_8bit), "YCBCR444_8bit" }, > + { __builtin_ffs(YCBCR444_10bit), "YCBCR444_10bit" }, > + { __builtin_ffs(YCBCR444_12bit), "YCBCR444_12bit" }, > + { __builtin_ffs(YCBCR444_16bit), "YCBCR444_16bit" }, > + { __builtin_ffs(YCBCR422_8bit_NO_SUPPORT), > "YCBCR422_8bit_NO_SUPPORT" }, > + { __builtin_ffs(YCBCR422_10bit_NO_SUPPORT), > + "YCBCR422_10bit_NO_SUPPORT" }, > + { __builtin_ffs(YCBCR422_12bit), "YCBCR422_12bit" }, > + { __builtin_ffs(YCBCR422_16bit_NO_SUPPORT), > + "YCBCR422_16bit_NO_SUPPORT" }, > + { __builtin_ffs(YCBCR420_8bit), "YCBCR420_8bit" }, > + { __builtin_ffs(YCBCR420_10bit), "YCBCR420_10bit" }, > + { __builtin_ffs(YCBCR420_12bit), "YCBCR420_12bit" }, > + { __builtin_ffs(YCBCR420_16bit), "YCBCR420_16bit" }, > +}; > + > +static void mtk_hdmi_connetor_init_property(struct drm_device > *drm_dev, > + struct drm_connector *conn) > +{ > + struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn); > + struct drm_property *prop; > + /* ycbcr422 cannot support 8,10,16bit */ > + unsigned int supported_csp_depth_mask = > + RGB444_8bit | RGB444_10bit | RGB444_12bit | > RGB444_16bit | > + YCBCR444_8bit | YCBCR444_10bit | YCBCR444_12bit | > + YCBCR444_16bit | YCBCR422_12bit | YCBCR420_8bit | > + YCBCR420_10bit | YCBCR420_12bit | YCBCR420_16bit; > + > + spin_lock_init(&hdmi->property_lock); > + > + /* create colorspace_depth bitmask property */ > + prop = drm_property_create_bitmask( > + conn->dev, 0, "hdmi_colorspace_depth", csp_depth_props, > + ARRAY_SIZE(csp_depth_props), supported_csp_depth_mask); > + > + if (!prop) > + return; > + > + hdmi->csp_depth_prop = prop; > + drm_object_attach_property(&conn->base, prop, 0); > + > + /* create mtk_hdmi_blob property, include EDID parser info, > + * such as max_tmds_clock_rate, max_tmds_character_rate, > support dolby vision > + */ > + prop = drm_property_create(conn->dev, > + DRM_MODE_PROP_BLOB | > DRM_MODE_PROP_IMMUTABLE, > + "HDMI_INFO", 0); > + if (!prop) > + return; > + > + drm_object_attach_property(&conn->base, prop, 0); > +} > + > +static void mtk_hdmi_convert_colorspace_depth(struct mtk_hdmi *hdmi) > +{ > + switch (hdmi->set_csp_depth) { > + case RGB444_8bit: > + hdmi->csp = HDMI_COLORSPACE_RGB; > + hdmi->color_depth = HDMI_8_BIT; > + break; > + case RGB444_10bit: > + hdmi->csp = HDMI_COLORSPACE_RGB; > + hdmi->color_depth = HDMI_10_BIT; > + break; > + case RGB444_12bit: > + hdmi->csp = HDMI_COLORSPACE_RGB; > + hdmi->color_depth = HDMI_12_BIT; > + break; > + case RGB444_16bit: > + hdmi->csp = HDMI_COLORSPACE_RGB; > + hdmi->color_depth = HDMI_16_BIT; > + break; > + case YCBCR444_8bit: > + hdmi->csp = HDMI_COLORSPACE_YUV444; > + hdmi->color_depth = HDMI_8_BIT; > + break; > + case YCBCR444_10bit: > + hdmi->csp = HDMI_COLORSPACE_YUV444; > + hdmi->color_depth = HDMI_10_BIT; > + break; > + case YCBCR444_12bit: > + hdmi->csp = HDMI_COLORSPACE_YUV444; > + hdmi->color_depth = HDMI_12_BIT; > + break; > + case YCBCR444_16bit: > + hdmi->csp = HDMI_COLORSPACE_YUV444; > + hdmi->color_depth = HDMI_16_BIT; > + break; > + case YCBCR422_12bit: > + hdmi->csp = HDMI_COLORSPACE_YUV422; > + hdmi->color_depth = HDMI_12_BIT; > + break; > + case YCBCR420_8bit: > + hdmi->csp = HDMI_COLORSPACE_YUV420; > + hdmi->color_depth = HDMI_8_BIT; > + break; > + case YCBCR420_10bit: > + hdmi->csp = HDMI_COLORSPACE_YUV420; > + hdmi->color_depth = HDMI_10_BIT; > + break; > + case YCBCR420_12bit: > + hdmi->csp = HDMI_COLORSPACE_YUV420; > + hdmi->color_depth = HDMI_12_BIT; > + break; > + case YCBCR420_16bit: > + hdmi->csp = HDMI_COLORSPACE_YUV420; > + hdmi->color_depth = HDMI_16_BIT; > + break; > + default: > + > + hdmi->csp = HDMI_COLORSPACE_RGB; > + hdmi->color_depth = HDMI_8_BIT; > + } > +} > + > +static int mtk_hdmi_conn_get_modes(struct drm_connector *conn) > +{ > + struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn); > + struct edid *edid; > + int ret; > + > + if (!hdmi->ddc_adpt) > + return -ENODEV; > + > + edid = drm_get_edid(conn, hdmi->ddc_adpt); > + if (!edid) > + return -ENODEV; > + > + hdmi->dvi_mode = !drm_detect_hdmi_monitor(edid); > + > + drm_connector_update_edid_property(conn, edid); > + > + ret = drm_add_edid_modes(conn, edid); > + > + kfree(edid); > + > + return ret; > +} > + > +static int mtk_hdmi_conn_mode_valid(struct drm_connector *conn, > + struct drm_display_mode *mode) > +{ > + if (mode->clock < 27000) > + return MODE_CLOCK_LOW; > + if (mode->clock > 594000) > + return MODE_CLOCK_HIGH; > + > + return drm_mode_validate_size(mode, 0x1fff, 0x1fff); > +} > + > +static struct drm_encoder *mtk_hdmi_conn_best_enc(struct > drm_connector *conn) > +{ > + struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn); > + > + return hdmi->bridge.encoder; > +} > + > +static const struct drm_connector_funcs mtk_hdmi_connector_funcs = { > + .detect = hdmi_conn_detect, > + .fill_modes = drm_helper_probe_single_connector_modes, > + .destroy = hdmi_conn_destroy, > + .reset = drm_atomic_helper_connector_reset, > + .atomic_duplicate_state = > drm_atomic_helper_connector_duplicate_state, > + .atomic_destroy_state = > drm_atomic_helper_connector_destroy_state, > + .atomic_set_property = hdmi_conn_atomic_set_property, > + .atomic_get_property = hdmi_conn_atomic_get_property, > +}; > + > +static const struct drm_connector_helper_funcs > mtk_hdmi_connector_helper_funcs = { > + .get_modes = mtk_hdmi_conn_get_modes, > + .mode_valid = mtk_hdmi_conn_mode_valid, > + .best_encoder = mtk_hdmi_conn_best_enc, > +}; > + > +/* > + * Bridge callbacks > + */ > + > +static int mtk_hdmi_bridge_attach(struct drm_bridge *bridge, > + enum drm_bridge_attach_flags flags) > +{ This implementation like the old style in v4.8 [1], but I think you should implement this as the new style [2]. [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/gpu/drm/mediatek/mtk_hdmi.c?h=v4.8-rc1#n1297 [2] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/gpu/drm/mediatek/mtk_hdmi.c?h=v5.17-rc1#n1283 > + struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge); > + int ret; > + > + ret = drm_connector_init(bridge->encoder->dev, &hdmi->conn, > + &mtk_hdmi_connector_funcs, > + DRM_MODE_CONNECTOR_HDMIA); > + if (ret) > + return ret; > + > + drm_connector_helper_add(&hdmi->conn, > &mtk_hdmi_connector_helper_funcs); > + > + hdmi->conn.polled = DRM_CONNECTOR_POLL_HPD; > + hdmi->conn.interlace_allowed = false; > + hdmi->conn.doublescan_allowed = false; > + hdmi->conn.ycbcr_420_allowed = true; > + > + ret = drm_connector_attach_encoder(&hdmi->conn, bridge- > >encoder); > + if (ret) > + return ret; > + > + mtk_hdmi_connetor_init_property(bridge->dev, &hdmi->conn); > + > + pm_runtime_enable(hdmi->dev); > + mtk_hdmi_enable_disable(hdmi, true); > + > + return 0; > +} > + > +static void mtk_hdmi_bridge_disable(struct drm_bridge *bridge) > +{ > + struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge); > + > + if (!hdmi->enabled) > + return; > + > + mtk_hdmi_hw_send_av_mute(hdmi); > + usleep_range(50000, 50050); > + mtk_hdmi_hw_vid_black(hdmi, true); > + mtk_hdmi_hw_aud_mute(hdmi); > + mtk_hdmi_disable_hdcp_encrypt(hdmi); > + usleep_range(50000, 50050); > + > + hdmi->enabled = false; > +} > + > +static void mtk_hdmi_handle_plugged_change(struct mtk_hdmi *hdmi, > bool plugged) > +{ > + if (hdmi->plugged_cb && hdmi->codec_dev) > + hdmi->plugged_cb(hdmi->codec_dev, plugged); > +} > + > +static void mtk_hdmi_bridge_post_disable(struct drm_bridge *bridge) > +{ > + struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge); > + > + if (!hdmi->powered) > + return; > + > + phy_power_off(hdmi->phy); > + > + hdmi->powered = false; > + > + mtk_hdmi_reset_colorspace_setting(hdmi); > + > + /* signal the disconnect event to audio codec */ > + mtk_hdmi_handle_plugged_change(hdmi, false); > +} > + > +static void mtk_hdmi_bridge_pre_enable(struct drm_bridge *bridge) > +{ > + struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge); > + u8 buffer_spd[HDMI_INFOFRAME_HEADER_SIZE + > HDMI_SPD_INFOFRAME_SIZE]; > + u8 buffer_avi[HDMI_INFOFRAME_HEADER_SIZE + > HDMI_AVI_INFOFRAME_SIZE]; > + union phy_configure_opts opts = { > + .dp = { .link_rate = hdmi->mode.clock * 1000 } > + }; > + > + mtk_hdmi_convert_colorspace_depth(hdmi); > + mtk_hdmi_output_set_display_mode(hdmi, &hdmi->mode); > + phy_configure(hdmi->phy, &opts); In mt8173 hdmi driver, it does not call phy_configure(), please add comment for why do this. > + phy_power_on(hdmi->phy); > + mtk_hdmi_send_infoframe(hdmi, buffer_spd, sizeof(buffer_spd), > buffer_avi, sizeof(buffer_avi), &hdmi->mode); In mt8173 hdmi driver, send infoframe in mtk_hdmi_bridge_atomic_enable(), try to move to that function. If can not move, add comment for why can not move. > + mtk_hdmi_hw_spd_infoframe(hdmi, buffer_spd, > sizeof(buffer_avi)); > + mtk_hdmi_hw_avi_infoframe(hdmi, buffer_avi, > sizeof(buffer_spd)); > + > + hdmi->powered = true; > +} > + > +static void mtk_hdmi_bridge_enable(struct drm_bridge *bridge) > +{ > + struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge); > + > + phy_power_on(hdmi->phy); > + mtk_hdmi_hw_vid_black(hdmi, false); > + mtk_hdmi_hw_aud_unmute(hdmi); > + > + /* signal the connect event to audio codec */ > + mtk_hdmi_handle_plugged_change(hdmi, true); clk_prepare_enable() here. > + > + hdmi->enabled = true; > +} > + > +const struct drm_bridge_funcs mtk_mt8195_hdmi_bridge_funcs = { > + .attach = mtk_hdmi_bridge_attach, > + .mode_fixup = mtk_hdmi_bridge_mode_fixup, > + .disable = mtk_hdmi_bridge_disable, atomic_disable > + .post_disable = mtk_hdmi_bridge_post_disable, atomic_post_disable > + .mode_set = mtk_hdmi_bridge_mode_set, > + .pre_enable = mtk_hdmi_bridge_pre_enable, atomic_pre_enable > + .enable = mtk_hdmi_bridge_enable, atomic_enable Regards, CK > + .get_edid = mtk_hdmi_bridge_get_edid, > +}; > + > +static void mtk_hdmi_set_plugged_cb(struct mtk_hdmi *hdmi, > + hdmi_codec_plugged_cb fn, > + struct device *codec_dev) > +{ > + bool plugged; > + > + hdmi->plugged_cb = fn; > + hdmi->codec_dev = codec_dev; > + plugged = (hdmi->hpd == HDMI_PLUG_IN_AND_SINK_POWER_ON) ? true > : false; > + mtk_hdmi_handle_plugged_change(hdmi, plugged); > +} > + > +/* > + * HDMI audio codec callbacks > + */ >