[RFC PATCH 2/2] drm/mediatek: add mtk8195 hdmi display driver

CK Hu ck.hu at mediatek.com
Thu Jan 27 19:26:09 PST 2022


Hi, Guillaume:

On Mon, 2021-11-08 at 01:08 +0100, Guillaume Ranquet wrote:
> Signed-off-by: Guillaume Ranquet <granquet at baylibre.com>
> 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 <sound/hdmi-codec.h>
>  
> +//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 <linux/arm-smccc.h>
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/delay.h>
> +#include <linux/debugfs.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/of_platform.h>
> +#include <linux/of.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_gpio.h>
> +#include <linux/of_graph.h>
> +#include <linux/pm_wakeup.h>
> +#include <linux/timer.h>
> +
> +#include <drm/drm_displayid.h>
> +#include <drm/drm_edid.h>
> +#include <drm/drm_print.h>
> +#include <drm/drm_probe_helper.h>
> +#include <drm/drm_scdc_helper.h>
> +
> +#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, &params->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
> + */
> 




More information about the Linux-mediatek mailing list