From 2f7c68cb55ecb7331f2381deb497c27155f32faf Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Wed, 03 Jan 2024 09:43:39 +0000 Subject: [PATCH] update kernel to 5.10.198 --- kernel/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 2919 +++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 files changed, 2,512 insertions(+), 407 deletions(-) diff --git a/kernel/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/kernel/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c index 25cf927..3253cf2 100644 --- a/kernel/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +++ b/kernel/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c @@ -1,35 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/clk.h> +#include <linux/gpio/consumer.h> #include <linux/mfd/syscon.h> #include <linux/module.h> +#include <linux/of_gpio.h> #include <linux/platform_device.h> #include <linux/phy/phy.h> #include <linux/regmap.h> #include <linux/pm_runtime.h> #include <drm/drm_of.h> -#include <drm/drmP.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_dsc.h> #include <drm/drm_edid.h> +#include <drm/drm_hdcp.h> #include <drm/bridge/dw_hdmi.h> +#include <drm/drm_edid.h> +#include <drm/drm_of.h> +#include <drm/drm_probe_helper.h> +#include <drm/drm_simple_kms_helper.h> #include <uapi/linux/videodev2.h> #include "rockchip_drm_drv.h" #include "rockchip_drm_vop.h" +#define HIWORD_UPDATE(val, mask) (val | (mask) << 16) + #define RK3228_GRF_SOC_CON2 0x0408 -#define RK3228_DDC_MASK_EN ((3 << 13) | (3 << (13 + 16))) +#define RK3228_HDMI_SDAIN_MSK BIT(14) +#define RK3228_HDMI_SCLIN_MSK BIT(13) #define RK3228_GRF_SOC_CON6 0x0418 -#define RK3228_IO_3V_DOMAIN ((7 << 4) | (7 << (4 + 16))) +#define RK3228_HDMI_HPD_VSEL BIT(6) +#define RK3228_HDMI_SDA_VSEL BIT(5) +#define RK3228_HDMI_SCL_VSEL BIT(4) #define RK3288_GRF_SOC_CON6 0x025C #define RK3288_HDMI_LCDC_SEL BIT(4) @@ -57,13 +65,85 @@ #define RK3399_GRF_SOC_CON20 0x6250 #define RK3399_HDMI_LCDC_SEL BIT(6) +#define RK3528_VO_GRF_HDMI_MASK 0x60014 +#define RK3528_HDMI_SNKDET_SEL BIT(6) +#define RK3528_HDMI_SNKDET BIT(5) +#define RK3528_HDMI_CECIN_MSK BIT(2) +#define RK3528_HDMI_SDAIN_MSK BIT(1) +#define RK3528_HDMI_SCLIN_MSK BIT(0) + +#define RK3528PMU_GRF_SOC_CON6 0x70018 +#define RK3528_HDMI_SDA5V_GRF BIT(6) +#define RK3528_HDMI_SCL5V_GRF BIT(5) +#define RK3528_HDMI_CEC5V_GRF BIT(4) +#define RK3528_HDMI_HPD5V_GRF BIT(3) + +#define RK3528_GPIO_SWPORT_DR_L 0x0000 +#define RK3528_GPIO0_A2_DR BIT(2) + #define RK3568_GRF_VO_CON1 0x0364 #define RK3568_HDMI_SDAIN_MSK BIT(15) #define RK3568_HDMI_SCLIN_MSK BIT(14) -#define HIWORD_UPDATE(val, mask) (val | (mask) << 16) -#define RK_HDMI_COLORIMETRY_BT2020 (HDMI_COLORIMETRY_EXTENDED + \ - HDMI_EXTENDED_COLORIMETRY_BT2020) +#define RK3588_GRF_SOC_CON2 0x0308 +#define RK3588_HDMI1_HPD_INT_MSK BIT(15) +#define RK3588_HDMI1_HPD_INT_CLR BIT(14) +#define RK3588_HDMI0_HPD_INT_MSK BIT(13) +#define RK3588_HDMI0_HPD_INT_CLR BIT(12) +#define RK3588_GRF_SOC_CON7 0x031c +#define RK3588_SET_HPD_PATH_MASK (0x3 << 12) +#define RK3588_GRF_SOC_STATUS1 0x0384 +#define RK3588_HDMI0_LOW_MORETHAN100MS BIT(20) +#define RK3588_HDMI0_HPD_PORT_LEVEL BIT(19) +#define RK3588_HDMI0_IHPD_PORT BIT(18) +#define RK3588_HDMI0_OHPD_INT BIT(17) +#define RK3588_HDMI0_LEVEL_INT BIT(16) +#define RK3588_HDMI0_INTR_CHANGE_CNT (0x7 << 13) +#define RK3588_HDMI1_LOW_MORETHAN100MS BIT(28) +#define RK3588_HDMI1_HPD_PORT_LEVEL BIT(27) +#define RK3588_HDMI1_IHPD_PORT BIT(26) +#define RK3588_HDMI1_OHPD_INT BIT(25) +#define RK3588_HDMI1_LEVEL_INT BIT(24) +#define RK3588_HDMI1_INTR_CHANGE_CNT (0x7 << 21) + +#define RK3588_GRF_VO1_CON1 0x0004 +#define HDCP1_P1_GPIO_IN BIT(9) +#define RK3588_GRF_VO1_CON3 0x000c +#define RK3588_COLOR_FORMAT_MASK 0xf +#define RK3588_RGB 0 +#define RK3588_YUV422 0x1 +#define RK3588_YUV444 0x2 +#define RK3588_YUV420 0x3 +#define RK3588_COMPRESSED_DATA 0xb +#define RK3588_COLOR_DEPTH_MASK (0xf << 4) +#define RK3588_8BPC 0 +#define RK3588_10BPC (0x6 << 4) +#define RK3588_CECIN_MASK BIT(8) +#define RK3588_SCLIN_MASK BIT(9) +#define RK3588_SDAIN_MASK BIT(10) +#define RK3588_MODE_MASK BIT(11) +#define RK3588_COMPRESS_MODE_MASK BIT(12) +#define RK3588_I2S_SEL_MASK BIT(13) +#define RK3588_SPDIF_SEL_MASK BIT(14) +#define RK3588_GRF_VO1_CON4 0x0010 +#define RK3588_HDMI21_MASK BIT(0) +#define RK3588_GRF_VO1_CON9 0x0024 +#define RK3588_HDMI0_GRANT_SEL BIT(10) +#define RK3588_HDMI0_GRANT_SW BIT(11) +#define RK3588_HDMI1_GRANT_SEL BIT(12) +#define RK3588_HDMI1_GRANT_SW BIT(13) +#define RK3588_GRF_VO1_CON4 0x0010 +#define RK3588_HDMI_HDCP14_MEM_EN BIT(15) +#define RK3588_GRF_VO1_CON6 0x0018 +#define RK3588_GRF_VO1_CON7 0x001c + +#define COLOR_DEPTH_10BIT BIT(31) +#define HDMI_FRL_MODE BIT(30) +#define HDMI_EARC_MODE BIT(29) +#define DATA_RATE_MASK 0xFFFFFFF + +#define HDMI20_MAX_RATE 600000 +#define HDMI_8K60_RATE 2376000 /** * struct rockchip_hdmi_chip_data - splite the grf setting of kind of chips @@ -77,72 +157,99 @@ int ddc_en_reg; u32 lcdsel_big; u32 lcdsel_lit; + bool split_mode; }; -/* HDMI output pixel format */ -enum drm_hdmi_output_type { - DRM_HDMI_OUTPUT_DEFAULT_RGB, /* default RGB */ - DRM_HDMI_OUTPUT_YCBCR444, /* YCBCR 444 */ - DRM_HDMI_OUTPUT_YCBCR422, /* YCBCR 422 */ - DRM_HDMI_OUTPUT_YCBCR420, /* YCBCR 420 */ - DRM_HDMI_OUTPUT_YCBCR_HQ, /* Highest subsampled YUV */ - DRM_HDMI_OUTPUT_YCBCR_LQ, /* Lowest subsampled YUV */ - DRM_HDMI_OUTPUT_INVALID, /* Guess what ? */ -}; - -enum dw_hdmi_rockchip_color_depth { - ROCKCHIP_HDMI_DEPTH_8, - ROCKCHIP_HDMI_DEPTH_10, - ROCKCHIP_HDMI_DEPTH_12, - ROCKCHIP_HDMI_DEPTH_16, - ROCKCHIP_HDMI_DEPTH_420_10, - ROCKCHIP_HDMI_DEPTH_420_12, - ROCKCHIP_HDMI_DEPTH_420_16 +enum hdmi_frl_rate_per_lane { + FRL_12G_PER_LANE = 12, + FRL_10G_PER_LANE = 10, + FRL_8G_PER_LANE = 8, + FRL_6G_PER_LANE = 6, + FRL_3G_PER_LANE = 3, }; struct rockchip_hdmi { struct device *dev; struct regmap *regmap; + struct regmap *vo1_regmap; + void __iomem *gpio_base; struct drm_encoder encoder; + struct drm_device *drm_dev; const struct rockchip_hdmi_chip_data *chip_data; + struct dw_hdmi_plat_data *plat_data; + struct clk *aud_clk; struct clk *phyref_clk; struct clk *grf_clk; struct clk *hclk_vio; + struct clk *hclk_vo1; struct clk *hclk_vop; + struct clk *hpd_clk; + struct clk *pclk; + struct clk *earc_clk; + struct clk *hdmitx_ref; + struct clk *link_clk; struct dw_hdmi *hdmi; + struct dw_hdmi_qp *hdmi_qp; struct phy *phy; - int max_tmdsclk; + + u32 max_tmdsclk; bool unsupported_yuv_input; bool unsupported_deep_color; bool skip_check_420_mode; - bool mode_changed; + bool hpd_wake_en; u8 force_output; u8 id; + bool hpd_stat; + bool is_hdmi_qp; unsigned long bus_format; unsigned long output_bus_format; unsigned long enc_out_encoding; + unsigned long prev_bus_format; int color_changed; + int hpd_irq; struct drm_property *color_depth_property; struct drm_property *hdmi_output_property; struct drm_property *colordepth_capacity; struct drm_property *outputmode_capacity; - struct drm_property *colorimetry_property; struct drm_property *quant_range; struct drm_property *hdr_panel_metadata_property; + struct drm_property *next_hdr_sink_data_property; struct drm_property *output_hdmi_dvi; struct drm_property *output_type_capacity; + struct drm_property *allm_capacity; + struct drm_property *allm_enable; + struct drm_property *hdcp_state_property; struct drm_property_blob *hdr_panel_blob_ptr; + struct drm_property_blob *next_hdr_data_ptr; unsigned int colordepth; unsigned int colorimetry; unsigned int hdmi_quant_range; unsigned int phy_bus_width; - enum drm_hdmi_output_type hdmi_output; + unsigned int enable_allm; + enum rk_if_color_format hdmi_output; struct rockchip_drm_sub_dev sub_dev; + + u8 max_frl_rate_per_lane; + u8 max_lanes; + u8 add_func; + u8 edid_colorimetry; + u8 hdcp_status; + struct rockchip_drm_dsc_cap dsc_cap; + struct next_hdr_sink_data next_hdr_data; + struct dw_hdmi_link_config link_cfg; + struct gpio_desc *enable_gpio; + + struct delayed_work work; + struct workqueue_struct *workqueue; + struct gpio_desc *hpd_gpiod; + struct pinctrl *p; + struct pinctrl_state *idle_state; + struct pinctrl_state *default_state; }; #define to_rockchip_hdmi(x) container_of(x, struct rockchip_hdmi, x) @@ -260,94 +367,6 @@ } }; -static const struct dw_hdmi_mpll_config rockchip_mpll_cfg_rk356x[] = { - { - 30666000, { - { 0x00b3, 0x0000 }, - { 0x2153, 0x0000 }, - { 0x40f3, 0x0000 }, - }, - }, { - 36800000, { - { 0x00b3, 0x0000 }, - { 0x2153, 0x0000 }, - { 0x40a2, 0x0001 }, - }, - }, { - 46000000, { - { 0x00b3, 0x0000 }, - { 0x2142, 0x0001 }, - { 0x40a2, 0x0001 }, - }, - }, { - 61333000, { - { 0x0072, 0x0001 }, - { 0x2142, 0x0001 }, - { 0x40a2, 0x0001 }, - }, - }, { - 73600000, { - { 0x0072, 0x0001 }, - { 0x2142, 0x0001 }, - { 0x4061, 0x0002 }, - }, - }, { - 92000000, { - { 0x0072, 0x0001 }, - { 0x2145, 0x0002 }, - { 0x4061, 0x0002 }, - }, - }, { - 122666000, { - { 0x0051, 0x0002 }, - { 0x2145, 0x0002 }, - { 0x4061, 0x0002 }, - }, - }, { - 147200000, { - { 0x0051, 0x0002 }, - { 0x2145, 0x0002 }, - { 0x4064, 0x0003 }, - }, - }, { - 184000000, { - { 0x0051, 0x0002 }, - { 0x214c, 0x0003 }, - { 0x4064, 0x0003 }, - }, - }, { - 226666000, { - { 0x0040, 0x0003 }, - { 0x214c, 0x0003 }, - { 0x4064, 0x0003 }, - }, - }, { - 272000000, { - { 0x0040, 0x0003 }, - { 0x214c, 0x0003 }, - { 0x5a64, 0x0003 }, - }, - }, { - 340000000, { - { 0x0040, 0x0002 }, - { 0x3b4c, 0x0003 }, - { 0x5a64, 0x0003 }, - }, - }, { - 600000000, { - { 0x1a40, 0x0003 }, - { 0x3b4c, 0x0003 }, - { 0x5a64, 0x0003 }, - }, - }, { - ~0UL, { - { 0x0000, 0x0000 }, - { 0x0000, 0x0000 }, - { 0x0000, 0x0000 }, - }, - } -}; - static const struct dw_hdmi_mpll_config rockchip_mpll_cfg_420[] = { { 30666000, { @@ -437,19 +456,6 @@ } }; -static const struct dw_hdmi_curr_ctrl rockchip_cur_ctr_rk356x[] = { - /* pixelclk bpp8 bpp10 bpp12 */ - { - 272000000, { 0x0000, 0x0000, 0x0000 }, - }, { - 340000000, { 0x0001, 0x0000, 0x0000 }, - }, { - 600000000, { 0x0000, 0x0000, 0x0000 }, - }, { - ~0UL, { 0x0000, 0x0000, 0x0000}, - } -}; - static struct dw_hdmi_phy_config rockchip_phy_config[] = { /*pixelclk symbol term vlev*/ { 74250000, 0x8009, 0x0004, 0x0272}, @@ -459,6 +465,744 @@ { ~0UL, 0x0000, 0x0000, 0x0000}, { ~0UL, 0x0000, 0x0000, 0x0000}, }; + +enum ROW_INDEX_BPP { + ROW_INDEX_6BPP = 0, + ROW_INDEX_8BPP, + ROW_INDEX_10BPP, + ROW_INDEX_12BPP, + ROW_INDEX_23BPP, + MAX_ROW_INDEX +}; + +enum COLUMN_INDEX_BPC { + COLUMN_INDEX_8BPC = 0, + COLUMN_INDEX_10BPC, + COLUMN_INDEX_12BPC, + COLUMN_INDEX_14BPC, + COLUMN_INDEX_16BPC, + MAX_COLUMN_INDEX +}; + +#define PPS_TABLE_LEN 8 +#define PPS_BPP_LEN 4 +#define PPS_BPC_LEN 2 + +struct pps_data { + u32 pic_width; + u32 pic_height; + u32 slice_width; + u32 slice_height; + bool convert_rgb; + u8 bpc; + u8 bpp; + u8 raw_pps[128]; +}; + +/* + * Selected Rate Control Related Parameter Recommended Values + * from DSC_v1.11 spec & C Model release: DSC_model_20161212 + */ +static struct pps_data pps_datas[PPS_TABLE_LEN] = { + { + /* 7680x4320/960X96 rgb 8bpc 12bpp */ + 7680, 4320, 960, 96, 1, 8, 192, + { + 0x12, 0x00, 0x00, 0x8d, 0x30, 0xc0, 0x10, 0xe0, + 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x05, 0xa0, + 0x01, 0x55, 0x03, 0x90, 0x00, 0x0a, 0x05, 0xc9, + 0x00, 0xa0, 0x00, 0x0f, 0x01, 0x44, 0x01, 0xaa, + 0x08, 0x00, 0x10, 0xf4, 0x03, 0x0c, 0x20, 0x00, + 0x06, 0x0b, 0x0b, 0x33, 0x0e, 0x1c, 0x2a, 0x38, + 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, + 0x7d, 0x7e, 0x00, 0x82, 0x00, 0xc0, 0x09, 0x00, + 0x09, 0x7e, 0x19, 0xbc, 0x19, 0xba, 0x19, 0xf8, + 0x1a, 0x38, 0x1a, 0x38, 0x1a, 0x76, 0x2a, 0x76, + 0x2a, 0x76, 0x2a, 0x74, 0x3a, 0xb4, 0x52, 0xf4, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, + }, + { + /* 7680x4320/960X96 rgb 8bpc 11bpp */ + 7680, 4320, 960, 96, 1, 8, 176, + { + 0x12, 0x00, 0x00, 0x8d, 0x30, 0xb0, 0x10, 0xe0, + 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x05, 0x28, + 0x01, 0x74, 0x03, 0x40, 0x00, 0x0f, 0x06, 0xe0, + 0x00, 0x2d, 0x00, 0x0f, 0x01, 0x44, 0x01, 0x33, + 0x0f, 0x00, 0x10, 0xf4, 0x03, 0x0c, 0x20, 0x00, + 0x06, 0x0b, 0x0b, 0x33, 0x0e, 0x1c, 0x2a, 0x38, + 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, + 0x7d, 0x7e, 0x00, 0x82, 0x01, 0x00, 0x09, 0x40, + 0x09, 0xbe, 0x19, 0xfc, 0x19, 0xfa, 0x19, 0xf8, + 0x1a, 0x38, 0x1a, 0x38, 0x1a, 0x76, 0x2a, 0x76, + 0x2a, 0x76, 0x2a, 0xb4, 0x3a, 0xb4, 0x52, 0xf4, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, + }, + { + /* 7680x4320/960X96 rgb 8bpc 10bpp */ + 7680, 4320, 960, 96, 1, 8, 160, + { + 0x12, 0x00, 0x00, 0x8d, 0x30, 0xa0, 0x10, 0xe0, + 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x04, 0xb0, + 0x01, 0x9a, 0x02, 0xe0, 0x00, 0x19, 0x09, 0xb0, + 0x00, 0x12, 0x00, 0x0f, 0x01, 0x44, 0x00, 0xbb, + 0x16, 0x00, 0x10, 0xec, 0x03, 0x0c, 0x20, 0x00, + 0x06, 0x0b, 0x0b, 0x33, 0x0e, 0x1c, 0x2a, 0x38, + 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, + 0x7d, 0x7e, 0x00, 0xc2, 0x01, 0x00, 0x09, 0x40, + 0x09, 0xbe, 0x19, 0xfc, 0x19, 0xfa, 0x19, 0xf8, + 0x1a, 0x38, 0x1a, 0x78, 0x1a, 0x76, 0x2a, 0xb6, + 0x2a, 0xb6, 0x2a, 0xf4, 0x3a, 0xf4, 0x5b, 0x34, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, + }, + { + /* 7680x4320/960X96 rgb 8bpc 9bpp */ + 7680, 4320, 960, 96, 1, 8, 144, + { + 0x12, 0x00, 0x00, 0x8d, 0x30, 0x90, 0x10, 0xe0, + 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x04, 0x38, + 0x01, 0xc7, 0x03, 0x16, 0x00, 0x1c, 0x08, 0xc7, + 0x00, 0x10, 0x00, 0x0f, 0x01, 0x44, 0x00, 0xaa, + 0x17, 0x00, 0x10, 0xf1, 0x03, 0x0c, 0x20, 0x00, + 0x06, 0x0b, 0x0b, 0x33, 0x0e, 0x1c, 0x2a, 0x38, + 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, + 0x7d, 0x7e, 0x00, 0xc2, 0x01, 0x00, 0x09, 0x40, + 0x09, 0xbe, 0x19, 0xfc, 0x19, 0xfa, 0x19, 0xf8, + 0x1a, 0x38, 0x1a, 0x78, 0x1a, 0x76, 0x2a, 0xb6, + 0x2a, 0xb6, 0x2a, 0xf4, 0x3a, 0xf4, 0x63, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, + }, + { + /* 7680x4320/960X96 rgb 10bpc 12bpp */ + 7680, 4320, 960, 96, 1, 10, 192, + { + 0x12, 0x00, 0x00, 0xad, 0x30, 0xc0, 0x10, 0xe0, + 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x05, 0xa0, + 0x01, 0x55, 0x03, 0x90, 0x00, 0x0a, 0x05, 0xc9, + 0x00, 0xa0, 0x00, 0x0f, 0x01, 0x44, 0x01, 0xaa, + 0x08, 0x00, 0x10, 0xf4, 0x07, 0x10, 0x20, 0x00, + 0x06, 0x0f, 0x0f, 0x33, 0x0e, 0x1c, 0x2a, 0x38, + 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, + 0x7d, 0x7e, 0x01, 0x02, 0x11, 0x80, 0x22, 0x00, + 0x22, 0x7e, 0x32, 0xbc, 0x32, 0xba, 0x3a, 0xf8, + 0x3b, 0x38, 0x3b, 0x38, 0x3b, 0x76, 0x4b, 0x76, + 0x4b, 0x76, 0x4b, 0x74, 0x5b, 0xb4, 0x73, 0xf4, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, + }, + { + /* 7680x4320/960X96 rgb 10bpc 11bpp */ + 7680, 4320, 960, 96, 1, 10, 176, + { + 0x12, 0x00, 0x00, 0xad, 0x30, 0xb0, 0x10, 0xe0, + 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x05, 0x28, + 0x01, 0x74, 0x03, 0x40, 0x00, 0x0f, 0x06, 0xe0, + 0x00, 0x2d, 0x00, 0x0f, 0x01, 0x44, 0x01, 0x33, + 0x0f, 0x00, 0x10, 0xf4, 0x07, 0x10, 0x20, 0x00, + 0x06, 0x0f, 0x0f, 0x33, 0x0e, 0x1c, 0x2a, 0x38, + 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, + 0x7d, 0x7e, 0x01, 0x42, 0x19, 0xc0, 0x2a, 0x40, + 0x2a, 0xbe, 0x3a, 0xfc, 0x3a, 0xfa, 0x3a, 0xf8, + 0x3b, 0x38, 0x3b, 0x38, 0x3b, 0x76, 0x4b, 0x76, + 0x4b, 0x76, 0x4b, 0xb4, 0x5b, 0xb4, 0x73, 0xf4, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, + }, + { + /* 7680x4320/960X96 rgb 10bpc 10bpp */ + 7680, 4320, 960, 96, 1, 10, 160, + { + 0x12, 0x00, 0x00, 0xad, 0x30, 0xa0, 0x10, 0xe0, + 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x04, 0xb0, + 0x01, 0x9a, 0x02, 0xe0, 0x00, 0x19, 0x09, 0xb0, + 0x00, 0x12, 0x00, 0x0f, 0x01, 0x44, 0x00, 0xbb, + 0x16, 0x00, 0x10, 0xec, 0x07, 0x10, 0x20, 0x00, + 0x06, 0x0f, 0x0f, 0x33, 0x0e, 0x1c, 0x2a, 0x38, + 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, + 0x7d, 0x7e, 0x01, 0xc2, 0x22, 0x00, 0x2a, 0x40, + 0x2a, 0xbe, 0x3a, 0xfc, 0x3a, 0xfa, 0x3a, 0xf8, + 0x3b, 0x38, 0x3b, 0x78, 0x3b, 0x76, 0x4b, 0xb6, + 0x4b, 0xb6, 0x4b, 0xf4, 0x63, 0xf4, 0x7c, 0x34, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, + }, + { + /* 7680x4320/960X96 rgb 10bpc 9bpp */ + 7680, 4320, 960, 96, 1, 10, 144, + { + 0x12, 0x00, 0x00, 0xad, 0x30, 0x90, 0x10, 0xe0, + 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x04, 0x38, + 0x01, 0xc7, 0x03, 0x16, 0x00, 0x1c, 0x08, 0xc7, + 0x00, 0x10, 0x00, 0x0f, 0x01, 0x44, 0x00, 0xaa, + 0x17, 0x00, 0x10, 0xf1, 0x07, 0x10, 0x20, 0x00, + 0x06, 0x0f, 0x0f, 0x33, 0x0e, 0x1c, 0x2a, 0x38, + 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, + 0x7d, 0x7e, 0x01, 0xc2, 0x22, 0x00, 0x2a, 0x40, + 0x2a, 0xbe, 0x3a, 0xfc, 0x3a, 0xfa, 0x3a, 0xf8, + 0x3b, 0x38, 0x3b, 0x78, 0x3b, 0x76, 0x4b, 0xb6, + 0x4b, 0xb6, 0x4b, 0xf4, 0x63, 0xf4, 0x84, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, + }, +}; + +static bool hdmi_bus_fmt_is_rgb(unsigned int bus_format) +{ + switch (bus_format) { + case MEDIA_BUS_FMT_RGB888_1X24: + case MEDIA_BUS_FMT_RGB101010_1X30: + case MEDIA_BUS_FMT_RGB121212_1X36: + case MEDIA_BUS_FMT_RGB161616_1X48: + return true; + + default: + return false; + } +} + +static bool hdmi_bus_fmt_is_yuv444(unsigned int bus_format) +{ + switch (bus_format) { + case MEDIA_BUS_FMT_YUV8_1X24: + case MEDIA_BUS_FMT_YUV10_1X30: + case MEDIA_BUS_FMT_YUV12_1X36: + case MEDIA_BUS_FMT_YUV16_1X48: + return true; + + default: + return false; + } +} + +static bool hdmi_bus_fmt_is_yuv422(unsigned int bus_format) +{ + switch (bus_format) { + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_UYVY10_1X20: + case MEDIA_BUS_FMT_UYVY12_1X24: + case MEDIA_BUS_FMT_YUYV8_1X16: + case MEDIA_BUS_FMT_YUYV10_1X20: + case MEDIA_BUS_FMT_YUYV12_1X24: + return true; + + default: + return false; + } +} + +static bool hdmi_bus_fmt_is_yuv420(unsigned int bus_format) +{ + switch (bus_format) { + case MEDIA_BUS_FMT_UYYVYY8_0_5X24: + case MEDIA_BUS_FMT_UYYVYY10_0_5X30: + case MEDIA_BUS_FMT_UYYVYY12_0_5X36: + case MEDIA_BUS_FMT_UYYVYY16_0_5X48: + return true; + + default: + return false; + } +} + +static int hdmi_bus_fmt_color_depth(unsigned int bus_format) +{ + switch (bus_format) { + case MEDIA_BUS_FMT_RGB888_1X24: + case MEDIA_BUS_FMT_YUV8_1X24: + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_YUYV8_1X16: + case MEDIA_BUS_FMT_UYYVYY8_0_5X24: + return 8; + + case MEDIA_BUS_FMT_RGB101010_1X30: + case MEDIA_BUS_FMT_YUV10_1X30: + case MEDIA_BUS_FMT_UYVY10_1X20: + case MEDIA_BUS_FMT_YUYV10_1X20: + case MEDIA_BUS_FMT_UYYVYY10_0_5X30: + return 10; + + case MEDIA_BUS_FMT_RGB121212_1X36: + case MEDIA_BUS_FMT_YUV12_1X36: + case MEDIA_BUS_FMT_UYVY12_1X24: + case MEDIA_BUS_FMT_YUYV12_1X24: + case MEDIA_BUS_FMT_UYYVYY12_0_5X36: + return 12; + + case MEDIA_BUS_FMT_RGB161616_1X48: + case MEDIA_BUS_FMT_YUV16_1X48: + case MEDIA_BUS_FMT_UYYVYY16_0_5X48: + return 16; + + default: + return 0; + } +} + +static int hdmi_bus_fmt_to_color_format(unsigned int bus_format) +{ + switch (bus_format) { + case MEDIA_BUS_FMT_UYYVYY8_0_5X24: + case MEDIA_BUS_FMT_UYYVYY10_0_5X30: + case MEDIA_BUS_FMT_UYYVYY12_0_5X36: + case MEDIA_BUS_FMT_UYYVYY16_0_5X48: + return RK_IF_FORMAT_YCBCR420; + + case MEDIA_BUS_FMT_YUV8_1X24: + case MEDIA_BUS_FMT_YUV10_1X30: + case MEDIA_BUS_FMT_YUV12_1X36: + case MEDIA_BUS_FMT_YUV16_1X48: + return RK_IF_FORMAT_YCBCR444; + + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_YUYV8_1X16: + case MEDIA_BUS_FMT_UYVY10_1X20: + case MEDIA_BUS_FMT_YUYV10_1X20: + case MEDIA_BUS_FMT_UYVY12_1X24: + case MEDIA_BUS_FMT_YVYU12_1X24: + return RK_IF_FORMAT_YCBCR422; + + case MEDIA_BUS_FMT_RGB888_1X24: + case MEDIA_BUS_FMT_RGB101010_1X30: + case MEDIA_BUS_FMT_RGB121212_1X36: + case MEDIA_BUS_FMT_RGB161616_1X48: + default: + return RK_IF_FORMAT_RGB; + } +} + +static unsigned int +hdmi_get_tmdsclock(struct rockchip_hdmi *hdmi, unsigned long pixelclock) +{ + unsigned int tmdsclock = pixelclock; + unsigned int depth = + hdmi_bus_fmt_color_depth(hdmi->output_bus_format); + + if (!hdmi_bus_fmt_is_yuv422(hdmi->output_bus_format)) { + switch (depth) { + case 16: + tmdsclock = pixelclock * 2; + break; + case 12: + tmdsclock = pixelclock * 3 / 2; + break; + case 10: + tmdsclock = pixelclock * 5 / 4; + break; + default: + break; + } + } + + return tmdsclock; +} + +static int rockchip_hdmi_match_by_id(struct device *dev, const void *data) +{ + struct rockchip_hdmi *hdmi = dev_get_drvdata(dev); + const unsigned int *id = data; + + return hdmi->id == *id; +} + +static struct rockchip_hdmi * +rockchip_hdmi_find_by_id(struct device_driver *drv, unsigned int id) +{ + struct device *dev; + + dev = driver_find_device(drv, NULL, &id, rockchip_hdmi_match_by_id); + if (!dev) + return NULL; + + return dev_get_drvdata(dev); +} + +static void hdmi_select_link_config(struct rockchip_hdmi *hdmi, + struct drm_crtc_state *crtc_state, + unsigned int tmdsclk) +{ + struct drm_display_mode mode = {}; + int max_lanes, max_rate_per_lane; + int max_dsc_lanes, max_dsc_rate_per_lane; + unsigned long max_frl_rate; + + drm_mode_copy(&mode, &crtc_state->mode); + if (hdmi->plat_data->split_mode) + drm_mode_convert_to_origin_mode(&mode); + + max_lanes = hdmi->max_lanes; + max_rate_per_lane = hdmi->max_frl_rate_per_lane; + max_frl_rate = max_lanes * max_rate_per_lane * 1000000; + + hdmi->link_cfg.dsc_mode = false; + hdmi->link_cfg.frl_lanes = max_lanes; + hdmi->link_cfg.rate_per_lane = max_rate_per_lane; + hdmi->link_cfg.add_func = hdmi->add_func; + + if (!max_frl_rate || (tmdsclk < HDMI20_MAX_RATE && mode.clock < HDMI20_MAX_RATE)) { + dev_info(hdmi->dev, "use tmds mode\n"); + hdmi->link_cfg.frl_mode = false; + return; + } + + hdmi->link_cfg.frl_mode = true; + + if (!hdmi->dsc_cap.v_1p2) + return; + + max_dsc_lanes = hdmi->dsc_cap.max_lanes; + max_dsc_rate_per_lane = + hdmi->dsc_cap.max_frl_rate_per_lane; + + if (mode.clock >= HDMI_8K60_RATE && + !hdmi_bus_fmt_is_yuv420(hdmi->bus_format) && + !hdmi_bus_fmt_is_yuv422(hdmi->bus_format)) { + hdmi->link_cfg.dsc_mode = true; + hdmi->link_cfg.frl_lanes = max_dsc_lanes; + hdmi->link_cfg.rate_per_lane = max_dsc_rate_per_lane; + } else { + hdmi->link_cfg.dsc_mode = false; + hdmi->link_cfg.frl_lanes = max_lanes; + hdmi->link_cfg.rate_per_lane = max_rate_per_lane; + } +} + +///////////////////////////////////////////////////////////////////////////////////// + +static int hdmi_dsc_get_slice_height(int vactive) +{ + int slice_height; + + /* + * Slice Height determination : HDMI2.1 Section 7.7.5.2 + * Select smallest slice height >=96, that results in a valid PPS and + * requires minimum padding lines required for final slice. + * + * Assumption : Vactive is even. + */ + for (slice_height = 96; slice_height <= vactive; slice_height += 2) + if (vactive % slice_height == 0) + return slice_height; + + return 0; +} + +static int hdmi_dsc_get_num_slices(struct rockchip_hdmi *hdmi, + struct drm_crtc_state *crtc_state, + int src_max_slices, int src_max_slice_width, + int hdmi_max_slices, int hdmi_throughput) +{ +/* Pixel rates in KPixels/sec */ +#define HDMI_DSC_PEAK_PIXEL_RATE 2720000 +/* + * Rates at which the source and sink are required to process pixels in each + * slice, can be two levels: either at least 340000KHz or at least 40000KHz. + */ +#define HDMI_DSC_MAX_ENC_THROUGHPUT_0 340000 +#define HDMI_DSC_MAX_ENC_THROUGHPUT_1 400000 + +/* Spec limits the slice width to 2720 pixels */ +#define MAX_HDMI_SLICE_WIDTH 2720 + int kslice_adjust; + int adjusted_clk_khz; + int min_slices; + int target_slices; + int max_throughput; /* max clock freq. in khz per slice */ + int max_slice_width; + int slice_width; + int pixel_clock = crtc_state->mode.clock; + + if (!hdmi_throughput) + return 0; + + /* + * Slice Width determination : HDMI2.1 Section 7.7.5.1 + * kslice_adjust factor for 4:2:0, and 4:2:2 formats is 0.5, where as + * for 4:4:4 is 1.0. Multiplying these factors by 10 and later + * dividing adjusted clock value by 10. + */ + if (hdmi_bus_fmt_is_yuv444(hdmi->output_bus_format) || + hdmi_bus_fmt_is_rgb(hdmi->output_bus_format)) + kslice_adjust = 10; + else + kslice_adjust = 5; + + /* + * As per spec, the rate at which the source and the sink process + * the pixels per slice are at two levels: at least 340Mhz or 400Mhz. + * This depends upon the pixel clock rate and output formats + * (kslice adjust). + * If pixel clock * kslice adjust >= 2720MHz slices can be processed + * at max 340MHz, otherwise they can be processed at max 400MHz. + */ + + adjusted_clk_khz = DIV_ROUND_UP(kslice_adjust * pixel_clock, 10); + + if (adjusted_clk_khz <= HDMI_DSC_PEAK_PIXEL_RATE) + max_throughput = HDMI_DSC_MAX_ENC_THROUGHPUT_0; + else + max_throughput = HDMI_DSC_MAX_ENC_THROUGHPUT_1; + + /* + * Taking into account the sink's capability for maximum + * clock per slice (in MHz) as read from HF-VSDB. + */ + max_throughput = min(max_throughput, hdmi_throughput * 1000); + + min_slices = DIV_ROUND_UP(adjusted_clk_khz, max_throughput); + max_slice_width = min(MAX_HDMI_SLICE_WIDTH, src_max_slice_width); + + /* + * Keep on increasing the num of slices/line, starting from min_slices + * per line till we get such a number, for which the slice_width is + * just less than max_slice_width. The slices/line selected should be + * less than or equal to the max horizontal slices that the combination + * of PCON encoder and HDMI decoder can support. + */ + do { + if (min_slices <= 1 && src_max_slices >= 1 && hdmi_max_slices >= 1) + target_slices = 1; + else if (min_slices <= 2 && src_max_slices >= 2 && hdmi_max_slices >= 2) + target_slices = 2; + else if (min_slices <= 4 && src_max_slices >= 4 && hdmi_max_slices >= 4) + target_slices = 4; + else if (min_slices <= 8 && src_max_slices >= 8 && hdmi_max_slices >= 8) + target_slices = 8; + else if (min_slices <= 12 && src_max_slices >= 12 && hdmi_max_slices >= 12) + target_slices = 12; + else if (min_slices <= 16 && src_max_slices >= 16 && hdmi_max_slices >= 16) + target_slices = 16; + else + return 0; + + slice_width = DIV_ROUND_UP(crtc_state->mode.hdisplay, target_slices); + if (slice_width > max_slice_width) + min_slices = target_slices + 1; + } while (slice_width > max_slice_width); + + return target_slices; +} + +static int hdmi_dsc_slices(struct rockchip_hdmi *hdmi, + struct drm_crtc_state *crtc_state) +{ + int hdmi_throughput = hdmi->dsc_cap.clk_per_slice; + int hdmi_max_slices = hdmi->dsc_cap.max_slices; + int rk_max_slices = 8; + int rk_max_slice_width = 2048; + + return hdmi_dsc_get_num_slices(hdmi, crtc_state, rk_max_slices, + rk_max_slice_width, + hdmi_max_slices, hdmi_throughput); +} + +static int +hdmi_dsc_get_bpp(struct rockchip_hdmi *hdmi, int src_fractional_bpp, + int slice_width, int num_slices, bool hdmi_all_bpp, + int hdmi_max_chunk_bytes) +{ + int max_dsc_bpp, min_dsc_bpp; + int target_bytes; + bool bpp_found = false; + int bpp_decrement_x16; + int bpp_target; + int bpp_target_x16; + + /* + * Get min bpp and max bpp as per Table 7.23, in HDMI2.1 spec + * Start with the max bpp and keep on decrementing with + * fractional bpp, if supported by PCON DSC encoder + * + * for each bpp we check if no of bytes can be supported by HDMI sink + */ + + /* only 9\10\12 bpp was tested */ + min_dsc_bpp = 9; + max_dsc_bpp = 12; + + /* + * Taking into account if all dsc_all_bpp supported by HDMI2.1 sink + * Section 7.7.34 : Source shall not enable compressed Video + * Transport with bpp_target settings above 12 bpp unless + * DSC_all_bpp is set to 1. + */ + if (!hdmi_all_bpp) + max_dsc_bpp = min(max_dsc_bpp, 12); + + /* + * The Sink has a limit of compressed data in bytes for a scanline, + * as described in max_chunk_bytes field in HFVSDB block of edid. + * The no. of bytes depend on the target bits per pixel that the + * source configures. So we start with the max_bpp and calculate + * the target_chunk_bytes. We keep on decrementing the target_bpp, + * till we get the target_chunk_bytes just less than what the sink's + * max_chunk_bytes, or else till we reach the min_dsc_bpp. + * + * The decrement is according to the fractional support from PCON DSC + * encoder. For fractional BPP we use bpp_target as a multiple of 16. + * + * bpp_target_x16 = bpp_target * 16 + * So we need to decrement by {1, 2, 4, 8, 16} for fractional bpps + * {1/16, 1/8, 1/4, 1/2, 1} respectively. + */ + + bpp_target = max_dsc_bpp; + + /* src does not support fractional bpp implies decrement by 16 for bppx16 */ + if (!src_fractional_bpp) + src_fractional_bpp = 1; + bpp_decrement_x16 = DIV_ROUND_UP(16, src_fractional_bpp); + bpp_target_x16 = bpp_target * 16; + + while (bpp_target_x16 > (min_dsc_bpp * 16)) { + int bpp; + + bpp = DIV_ROUND_UP(bpp_target_x16, 16); + target_bytes = DIV_ROUND_UP((num_slices * slice_width * bpp), 8); + if (target_bytes <= hdmi_max_chunk_bytes) { + bpp_found = true; + break; + } + bpp_target_x16 -= bpp_decrement_x16; + } + if (bpp_found) + return bpp_target_x16; + + return 0; +} + +static int +dw_hdmi_dsc_bpp(struct rockchip_hdmi *hdmi, + int num_slices, int slice_width) +{ + bool hdmi_all_bpp = hdmi->dsc_cap.all_bpp; + int fractional_bpp = 0; + int hdmi_max_chunk_bytes = hdmi->dsc_cap.total_chunk_kbytes * 1024; + + return hdmi_dsc_get_bpp(hdmi, fractional_bpp, slice_width, + num_slices, hdmi_all_bpp, + hdmi_max_chunk_bytes); +} + +static int dw_hdmi_qp_set_link_cfg(struct rockchip_hdmi *hdmi, + u16 pic_width, u16 pic_height, + u16 slice_width, u16 slice_height, + u16 bits_per_pixel, u8 bits_per_component) +{ + int i; + + for (i = 0; i < PPS_TABLE_LEN; i++) + if (pic_width == pps_datas[i].pic_width && + pic_height == pps_datas[i].pic_height && + slice_width == pps_datas[i].slice_width && + slice_height == pps_datas[i].slice_height && + bits_per_component == pps_datas[i].bpc && + bits_per_pixel == pps_datas[i].bpp && + hdmi_bus_fmt_is_rgb(hdmi->output_bus_format) == pps_datas[i].convert_rgb) + break; + + if (i == PPS_TABLE_LEN) { + dev_err(hdmi->dev, "can't find pps cfg!\n"); + return -EINVAL; + } + + memcpy(hdmi->link_cfg.pps_payload, pps_datas[i].raw_pps, 128); + hdmi->link_cfg.hcactive = DIV_ROUND_UP(slice_width * (bits_per_pixel / 16), 8) * + (pic_width / slice_width); + + return 0; +} + +static void dw_hdmi_qp_dsc_configure(struct rockchip_hdmi *hdmi, + struct rockchip_crtc_state *s, + struct drm_crtc_state *crtc_state) +{ + int ret; + int slice_height; + int slice_width; + int bits_per_pixel; + int slice_count; + bool hdmi_is_dsc_1_2; + unsigned int depth = hdmi_bus_fmt_color_depth(hdmi->output_bus_format); + + if (!crtc_state) + return; + + hdmi_is_dsc_1_2 = hdmi->dsc_cap.v_1p2; + + if (!hdmi_is_dsc_1_2) + return; + + slice_height = hdmi_dsc_get_slice_height(crtc_state->mode.vdisplay); + if (!slice_height) + return; + + slice_count = hdmi_dsc_slices(hdmi, crtc_state); + if (!slice_count) + return; + + slice_width = DIV_ROUND_UP(crtc_state->mode.hdisplay, slice_count); + + bits_per_pixel = dw_hdmi_dsc_bpp(hdmi, slice_count, slice_width); + if (!bits_per_pixel) + return; + + ret = dw_hdmi_qp_set_link_cfg(hdmi, crtc_state->mode.hdisplay, + crtc_state->mode.vdisplay, slice_width, + slice_height, bits_per_pixel, depth); + + if (ret) { + dev_err(hdmi->dev, "set vdsc cfg failed\n"); + return; + } + dev_info(hdmi->dev, "dsc_enable\n"); + s->dsc_enable = 1; + s->dsc_sink_cap.version_major = 1; + s->dsc_sink_cap.version_minor = 2; + s->dsc_sink_cap.slice_width = slice_width; + s->dsc_sink_cap.slice_height = slice_height; + s->dsc_sink_cap.target_bits_per_pixel_x16 = bits_per_pixel; + s->dsc_sink_cap.block_pred = 1; + s->dsc_sink_cap.native_420 = 0; + + memcpy(&s->pps, hdmi->link_cfg.pps_payload, 128); +} +///////////////////////////////////////////////////////////////////////////////////////// static int rockchip_hdmi_update_phy_table(struct rockchip_hdmi *hdmi, u32 *config, @@ -484,6 +1228,150 @@ return 0; } +static void repo_hpd_event(struct work_struct *p_work) +{ + struct rockchip_hdmi *hdmi = container_of(p_work, struct rockchip_hdmi, work.work); + bool change; + + change = drm_helper_hpd_irq_event(hdmi->drm_dev); + if (change) { + dev_dbg(hdmi->dev, "hpd stat changed:%d\n", hdmi->hpd_stat); + dw_hdmi_qp_cec_set_hpd(hdmi->hdmi_qp, hdmi->hpd_stat, change); + } +} + +static irqreturn_t rockchip_hdmi_hardirq(int irq, void *dev_id) +{ + struct rockchip_hdmi *hdmi = dev_id; + u32 intr_stat, val; + + regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &intr_stat); + + if (intr_stat) { + dev_dbg(hdmi->dev, "hpd irq %#x\n", intr_stat); + + if (!hdmi->id) + val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_MSK, + RK3588_HDMI0_HPD_INT_MSK); + else + val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_MSK, + RK3588_HDMI1_HPD_INT_MSK); + regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); + return IRQ_WAKE_THREAD; + } + + return IRQ_NONE; +} + +static irqreturn_t rockchip_hdmi_irq(int irq, void *dev_id) +{ + struct rockchip_hdmi *hdmi = dev_id; + u32 intr_stat, val; + int msecs; + bool stat; + + regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &intr_stat); + + if (!intr_stat) + return IRQ_NONE; + + if (!hdmi->id) { + val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR, + RK3588_HDMI0_HPD_INT_CLR); + if (intr_stat & RK3588_HDMI0_LEVEL_INT) + stat = true; + else + stat = false; + } else { + val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_CLR, + RK3588_HDMI1_HPD_INT_CLR); + if (intr_stat & RK3588_HDMI1_LEVEL_INT) + stat = true; + else + stat = false; + } + + regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); + + if (stat) { + hdmi->hpd_stat = true; + msecs = 150; + } else { + hdmi->hpd_stat = false; + msecs = 20; + } + mod_delayed_work(hdmi->workqueue, &hdmi->work, msecs_to_jiffies(msecs)); + + if (!hdmi->id) { + val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR, + RK3588_HDMI0_HPD_INT_CLR) | + HIWORD_UPDATE(0, RK3588_HDMI0_HPD_INT_MSK); + } else { + val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_CLR, + RK3588_HDMI1_HPD_INT_CLR) | + HIWORD_UPDATE(0, RK3588_HDMI1_HPD_INT_MSK); + } + + regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); + + return IRQ_HANDLED; +} + +static void init_hpd_work(struct rockchip_hdmi *hdmi) +{ + hdmi->workqueue = create_workqueue("hpd_queue"); + INIT_DELAYED_WORK(&hdmi->work, repo_hpd_event); +} + +static irqreturn_t rockchip_hdmi_hpd_irq_handler(int irq, void *arg) +{ + u32 val; + struct rockchip_hdmi *hdmi = arg; + + val = gpiod_get_value(hdmi->hpd_gpiod); + if (val) { + val = HIWORD_UPDATE(RK3528_HDMI_SNKDET, RK3528_HDMI_SNKDET); + if (hdmi->hdmi && hdmi->hpd_wake_en && hdmi->hpd_gpiod) + dw_hdmi_set_hpd_wake(hdmi->hdmi); + } else { + val = HIWORD_UPDATE(0, RK3528_HDMI_SNKDET); + } + regmap_write(hdmi->regmap, RK3528_VO_GRF_HDMI_MASK, val); + + return IRQ_HANDLED; +} + +static void dw_hdmi_rk3528_gpio_hpd_init(struct rockchip_hdmi *hdmi) +{ + u32 val; + + if (hdmi->hpd_gpiod) { + /* gpio0_a2's input enable is controlled by gpio output data bit */ + val = HIWORD_UPDATE(RK3528_GPIO0_A2_DR, RK3528_GPIO0_A2_DR); + writel(val, hdmi->gpio_base + RK3528_GPIO_SWPORT_DR_L); + + val = HIWORD_UPDATE(RK3528_HDMI_SNKDET_SEL | RK3528_HDMI_SDAIN_MSK | + RK3528_HDMI_SCLIN_MSK, + RK3528_HDMI_SNKDET_SEL | RK3528_HDMI_SDAIN_MSK | + RK3528_HDMI_SCLIN_MSK); + } else { + val = HIWORD_UPDATE(RK3528_HDMI_SDAIN_MSK | RK3528_HDMI_SCLIN_MSK, + RK3528_HDMI_SDAIN_MSK | RK3528_HDMI_SCLIN_MSK); + } + + regmap_write(hdmi->regmap, RK3528_VO_GRF_HDMI_MASK, val); + + val = gpiod_get_value(hdmi->hpd_gpiod); + if (val) { + val = HIWORD_UPDATE(RK3528_HDMI_SNKDET, RK3528_HDMI_SNKDET); + if (hdmi->hdmi && hdmi->hpd_wake_en && hdmi->hpd_gpiod) + dw_hdmi_set_hpd_wake(hdmi->hdmi); + } else { + val = HIWORD_UPDATE(0, RK3528_HDMI_SNKDET); + } + regmap_write(hdmi->regmap, RK3528_VO_GRF_HDMI_MASK, val); +} + static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi) { int ret, val, phy_table_size; @@ -494,6 +1382,14 @@ if (IS_ERR(hdmi->regmap)) { DRM_DEV_ERROR(hdmi->dev, "Unable to get rockchip,grf\n"); return PTR_ERR(hdmi->regmap); + } + + if (hdmi->is_hdmi_qp) { + hdmi->vo1_regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,vo1_grf"); + if (IS_ERR(hdmi->vo1_regmap)) { + DRM_DEV_ERROR(hdmi->dev, "Unable to get rockchip,vo1_grf\n"); + return PTR_ERR(hdmi->vo1_regmap); + } } hdmi->phyref_clk = devm_clk_get(hdmi->dev, "vpll"); @@ -539,20 +1435,63 @@ return PTR_ERR(hdmi->hclk_vop); } - ret = of_property_read_u32(np, "max-tmdsclk", - &hdmi->max_tmdsclk); - if (ret != -EINVAL && ret < 0) { - DRM_DEV_ERROR(hdmi->dev, "incorrect max tmdsclk\n"); - return ret; - } else if (ret == -EINVAL) { - DRM_DEV_DEBUG(hdmi->dev, - "max tmdsclk is not set, set to 594M\n"); - hdmi->max_tmdsclk = 594000; + hdmi->aud_clk = devm_clk_get_optional(hdmi->dev, "aud"); + if (IS_ERR(hdmi->aud_clk)) { + dev_err_probe(hdmi->dev, PTR_ERR(hdmi->aud_clk), + "failed to get aud_clk clock\n"); + return PTR_ERR(hdmi->aud_clk); } - hdmi->unsupported_yuv_input = - of_property_read_bool(np, "unsupported-yuv-input"); - hdmi->unsupported_deep_color = - of_property_read_bool(np, "unsupported-deep-color"); + + hdmi->hpd_clk = devm_clk_get_optional(hdmi->dev, "hpd"); + if (IS_ERR(hdmi->hpd_clk)) { + dev_err_probe(hdmi->dev, PTR_ERR(hdmi->hpd_clk), + "failed to get hpd_clk clock\n"); + return PTR_ERR(hdmi->hpd_clk); + } + + hdmi->hclk_vo1 = devm_clk_get_optional(hdmi->dev, "hclk_vo1"); + if (IS_ERR(hdmi->hclk_vo1)) { + dev_err_probe(hdmi->dev, PTR_ERR(hdmi->hclk_vo1), + "failed to get hclk_vo1 clock\n"); + return PTR_ERR(hdmi->hclk_vo1); + } + + hdmi->earc_clk = devm_clk_get_optional(hdmi->dev, "earc"); + if (IS_ERR(hdmi->earc_clk)) { + dev_err_probe(hdmi->dev, PTR_ERR(hdmi->earc_clk), + "failed to get earc_clk clock\n"); + return PTR_ERR(hdmi->earc_clk); + } + + hdmi->hdmitx_ref = devm_clk_get_optional(hdmi->dev, "hdmitx_ref"); + if (IS_ERR(hdmi->hdmitx_ref)) { + dev_err_probe(hdmi->dev, PTR_ERR(hdmi->hdmitx_ref), + "failed to get hdmitx_ref clock\n"); + return PTR_ERR(hdmi->hdmitx_ref); + } + + hdmi->pclk = devm_clk_get_optional(hdmi->dev, "pclk"); + if (IS_ERR(hdmi->pclk)) { + dev_err_probe(hdmi->dev, PTR_ERR(hdmi->pclk), + "failed to get pclk clock\n"); + return PTR_ERR(hdmi->pclk); + } + + hdmi->link_clk = devm_clk_get_optional(hdmi->dev, "link_clk"); + if (IS_ERR(hdmi->link_clk)) { + dev_err_probe(hdmi->dev, PTR_ERR(hdmi->link_clk), + "failed to get link_clk clock\n"); + return PTR_ERR(hdmi->link_clk); + } + + hdmi->enable_gpio = devm_gpiod_get_optional(hdmi->dev, "enable", + GPIOD_OUT_HIGH); + if (IS_ERR(hdmi->enable_gpio)) { + ret = PTR_ERR(hdmi->enable_gpio); + dev_err(hdmi->dev, "failed to request enable GPIO: %d\n", ret); + return ret; + } + hdmi->skip_check_420_mode = of_property_read_bool(np, "skip-check-420-mode"); @@ -578,27 +1517,85 @@ dev_dbg(hdmi->dev, "use default hdmi phy table\n"); } + hdmi->hpd_gpiod = devm_gpiod_get_optional(hdmi->dev, "hpd", GPIOD_IN); + + if (IS_ERR(hdmi->hpd_gpiod)) { + dev_err(hdmi->dev, "error getting HDP GPIO: %ld\n", + PTR_ERR(hdmi->hpd_gpiod)); + return PTR_ERR(hdmi->hpd_gpiod); + } + + if (hdmi->hpd_gpiod) { + struct resource *res; + struct platform_device *pdev = to_platform_device(hdmi->dev); + + /* gpio interrupt reflects hpd status */ + hdmi->hpd_irq = gpiod_to_irq(hdmi->hpd_gpiod); + if (hdmi->hpd_irq < 0) + return -EINVAL; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) { + DRM_DEV_ERROR(hdmi->dev, "failed to get gpio regs\n"); + return -EINVAL; + } + + hdmi->gpio_base = devm_ioremap(hdmi->dev, res->start, resource_size(res)); + if (IS_ERR(hdmi->gpio_base)) { + DRM_DEV_ERROR(hdmi->dev, "Unable to get gpio ioregmap\n"); + return PTR_ERR(hdmi->gpio_base); + } + + dw_hdmi_rk3528_gpio_hpd_init(hdmi); + ret = devm_request_threaded_irq(hdmi->dev, hdmi->hpd_irq, NULL, + rockchip_hdmi_hpd_irq_handler, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + "hdmi-hpd", hdmi); + if (ret) { + dev_err(hdmi->dev, "failed to request hpd IRQ: %d\n", ret); + return ret; + } + + hdmi->hpd_wake_en = device_property_read_bool(hdmi->dev, "hpd-wake-up"); + if (hdmi->hpd_wake_en) + enable_irq_wake(hdmi->hpd_irq); + } + + hdmi->p = devm_pinctrl_get(hdmi->dev); + if (IS_ERR(hdmi->p)) { + dev_err(hdmi->dev, "could not get pinctrl\n"); + return PTR_ERR(hdmi->p); + } + + hdmi->idle_state = pinctrl_lookup_state(hdmi->p, "idle"); + if (IS_ERR(hdmi->idle_state)) { + dev_dbg(hdmi->dev, "idle state is not defined\n"); + return 0; + } + + hdmi->default_state = pinctrl_lookup_state(hdmi->p, "default"); + if (IS_ERR(hdmi->default_state)) { + dev_err(hdmi->dev, "could not find default state\n"); + return PTR_ERR(hdmi->default_state); + } + return 0; } static enum drm_mode_status -dw_hdmi_rockchip_mode_valid(struct drm_connector *connector, +dw_hdmi_rockchip_mode_valid(struct dw_hdmi *dw_hdmi, void *data, + const struct drm_display_info *info, const struct drm_display_mode *mode) { + struct drm_connector *connector = container_of(info, struct drm_connector, display_info); struct drm_encoder *encoder = connector->encoder; enum drm_mode_status status = MODE_OK; struct drm_device *dev = connector->dev; struct rockchip_drm_private *priv = dev->dev_private; struct drm_crtc *crtc; struct rockchip_hdmi *hdmi; - - /* - * Pixel clocks we support are always < 2GHz and so fit in an - * int. We should make sure source rate does too so we don't get - * overflow when we multiply by 1000. - */ - if (mode->clock > INT_MAX / 1000) - return MODE_BAD; if (!encoder) { const struct drm_connector_helper_funcs *funcs; @@ -615,6 +1612,21 @@ return MODE_BAD; hdmi = to_rockchip_hdmi(encoder); + + if (hdmi->is_hdmi_qp) { + if (!hdmi->enable_gpio && mode->clock > 600000) + return MODE_BAD; + + return MODE_OK; + } + + /* + * Pixel clocks we support are always < 2GHz and so fit in an + * int. We should make sure source rate does too so we don't get + * overflow when we multiply by 1000. + */ + if (mode->clock > INT_MAX / 1000) + return MODE_BAD; /* * If sink max TMDS clock < 340MHz, we should check the mode pixel @@ -633,8 +1645,12 @@ return MODE_BAD; }; - if (hdmi->phy) - phy_set_bus_width(hdmi->phy, 8); + if (hdmi->phy) { + if (hdmi->is_hdmi_qp) + phy_set_bus_width(hdmi->phy, mode->clock * 10); + else + phy_set_bus_width(hdmi->phy, 8); + } /* * ensure all drm display mode can work, if someone want support more @@ -660,18 +1676,29 @@ return status; } -static const struct drm_encoder_funcs dw_hdmi_rockchip_encoder_funcs = { - .destroy = drm_encoder_cleanup, -}; - static void dw_hdmi_rockchip_encoder_disable(struct drm_encoder *encoder) { struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder); struct drm_crtc *crtc = encoder->crtc; - struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc->state); + struct rockchip_crtc_state *s; - if (!hdmi->mode_changed) - s->output_if &= ~VOP_OUTPUT_IF_HDMI0; + if (!crtc || !crtc->state) { + dev_info(hdmi->dev, "%s old crtc state is null\n", __func__); + return; + } + + s = to_rockchip_crtc_state(crtc->state); + + if (crtc->state->active_changed) { + if (hdmi->plat_data->split_mode) { + s->output_if &= ~(VOP_OUTPUT_IF_HDMI0 | VOP_OUTPUT_IF_HDMI1); + } else { + if (!hdmi->id) + s->output_if &= ~VOP_OUTPUT_IF_HDMI0; + else + s->output_if &= ~VOP_OUTPUT_IF_HDMI1; + } + } /* * when plug out hdmi it will be switch cvbs and then phy bus width * must be set as 8 @@ -688,14 +1715,23 @@ int mux; int ret; - if (WARN_ON(!crtc || !crtc->state)) + if (!crtc || !crtc->state) { + dev_info(hdmi->dev, "%s old crtc state is null\n", __func__); return; + } if (hdmi->phy) phy_set_bus_width(hdmi->phy, hdmi->phy_bus_width); clk_set_rate(hdmi->phyref_clk, crtc->state->adjusted_mode.crtc_clock * 1000); + + if (hdmi->is_hdmi_qp) { + if (hdmi->link_cfg.frl_mode) + gpiod_set_value(hdmi->enable_gpio, 0); + else + gpiod_set_value(hdmi->enable_gpio, 1); + } if (hdmi->chip_data->lcdsel_grf_reg < 0) return; @@ -735,6 +1771,205 @@ ret ? "LIT" : "BIG"); } +static int _dw_hdmi_rockchip_encoder_loader_protect(struct rockchip_hdmi *hdmi, bool on) +{ + int ret; + + if (on) { + if (hdmi->is_hdmi_qp) { + ret = clk_prepare_enable(hdmi->link_clk); + if (ret < 0) { + DRM_DEV_ERROR(hdmi->dev, "failed to enable link_clk %d\n", ret); + return ret; + } + } + + hdmi->phy->power_count++; + } else { + clk_disable_unprepare(hdmi->link_clk); + hdmi->phy->power_count--; + } + + return 0; +} + +static int dw_hdmi_rockchip_encoder_loader_protect(struct drm_encoder *encoder, bool on) +{ + struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder); + struct rockchip_hdmi *secondary; + + _dw_hdmi_rockchip_encoder_loader_protect(hdmi, on); + if (hdmi->plat_data->right) { + secondary = rockchip_hdmi_find_by_id(hdmi->dev->driver, !hdmi->id); + _dw_hdmi_rockchip_encoder_loader_protect(secondary, on); + } + + return 0; +} + +static void rk3588_set_link_mode(struct rockchip_hdmi *hdmi) +{ + int val; + bool is_hdmi0; + + if (!hdmi->id) + is_hdmi0 = true; + else + is_hdmi0 = false; + + if (!hdmi->link_cfg.frl_mode) { + val = HIWORD_UPDATE(0, RK3588_HDMI21_MASK); + if (is_hdmi0) + regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON4, val); + else + regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON7, val); + + val = HIWORD_UPDATE(0, RK3588_COMPRESS_MODE_MASK | RK3588_COLOR_FORMAT_MASK); + if (is_hdmi0) + regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val); + else + regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val); + + return; + } + + val = HIWORD_UPDATE(RK3588_HDMI21_MASK, RK3588_HDMI21_MASK); + if (is_hdmi0) + regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON4, val); + else + regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON7, val); + + if (hdmi->link_cfg.dsc_mode) { + val = HIWORD_UPDATE(RK3588_COMPRESS_MODE_MASK | RK3588_COMPRESSED_DATA, + RK3588_COMPRESS_MODE_MASK | RK3588_COLOR_FORMAT_MASK); + if (is_hdmi0) + regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val); + else + regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val); + } else { + val = HIWORD_UPDATE(0, RK3588_COMPRESS_MODE_MASK | RK3588_COLOR_FORMAT_MASK); + if (is_hdmi0) + regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val); + else + regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val); + } +} + +static void rk3588_set_color_format(struct rockchip_hdmi *hdmi, u64 bus_format, + u32 depth) +{ + u32 val = 0; + + switch (bus_format) { + case MEDIA_BUS_FMT_RGB888_1X24: + case MEDIA_BUS_FMT_RGB101010_1X30: + val = HIWORD_UPDATE(0, RK3588_COLOR_FORMAT_MASK); + break; + case MEDIA_BUS_FMT_UYYVYY8_0_5X24: + case MEDIA_BUS_FMT_UYYVYY10_0_5X30: + val = HIWORD_UPDATE(RK3588_YUV420, RK3588_COLOR_FORMAT_MASK); + break; + case MEDIA_BUS_FMT_YUV8_1X24: + case MEDIA_BUS_FMT_YUV10_1X30: + val = HIWORD_UPDATE(RK3588_YUV444, RK3588_COLOR_FORMAT_MASK); + break; + case MEDIA_BUS_FMT_YUYV10_1X20: + case MEDIA_BUS_FMT_YUYV8_1X16: + val = HIWORD_UPDATE(RK3588_YUV422, RK3588_COLOR_FORMAT_MASK); + break; + default: + dev_err(hdmi->dev, "can't set correct color format\n"); + return; + } + + if (hdmi->link_cfg.dsc_mode) + val = HIWORD_UPDATE(RK3588_COMPRESSED_DATA, RK3588_COLOR_FORMAT_MASK); + + if (depth == 8 || bus_format == MEDIA_BUS_FMT_YUYV10_1X20) + val |= HIWORD_UPDATE(RK3588_8BPC, RK3588_COLOR_DEPTH_MASK); + else + val |= HIWORD_UPDATE(RK3588_10BPC, RK3588_COLOR_DEPTH_MASK); + + if (!hdmi->id) + regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val); + else + regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val); +} + +static void rk3588_set_hdcp_status(void *data, u8 status) +{ + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + + hdmi->hdcp_status = status; +} + +static void rk3588_set_hdcp2_enable(void *data, bool enable) +{ + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + u32 val; + + if (enable) + val = HIWORD_UPDATE(HDCP1_P1_GPIO_IN, HDCP1_P1_GPIO_IN); + else + val = HIWORD_UPDATE(0, HDCP1_P1_GPIO_IN); + + regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON1, val); +} + +static void rk3588_set_grf_cfg(void *data) +{ + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + int color_depth; + + rk3588_set_link_mode(hdmi); + color_depth = hdmi_bus_fmt_color_depth(hdmi->bus_format); + rk3588_set_color_format(hdmi, hdmi->bus_format, color_depth); +} + +static u64 rk3588_get_grf_color_fmt(void *data) +{ + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + u32 val, depth; + u64 bus_format; + + if (!hdmi->id) + regmap_read(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, &val); + else + regmap_read(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, &val); + + depth = (val & RK3588_COLOR_DEPTH_MASK) >> 4; + + switch (val & RK3588_COLOR_FORMAT_MASK) { + case RK3588_YUV444: + if (!depth) + bus_format = MEDIA_BUS_FMT_YUV8_1X24; + else + bus_format = MEDIA_BUS_FMT_YUV10_1X30; + break; + case RK3588_YUV422: + bus_format = MEDIA_BUS_FMT_YUYV10_1X20; + break; + case RK3588_YUV420: + if (!depth) + bus_format = MEDIA_BUS_FMT_UYYVYY8_0_5X24; + else + bus_format = MEDIA_BUS_FMT_UYYVYY10_0_5X30; + break; + case RK3588_RGB: + if (!depth) + bus_format = MEDIA_BUS_FMT_RGB888_1X24; + else + bus_format = MEDIA_BUS_FMT_RGB101010_1X30; + break; + default: + dev_err(hdmi->dev, "can't get correct color format\n"); + bus_format = MEDIA_BUS_FMT_YUV8_1X24; + break; + } + + return bus_format; +} + static void dw_hdmi_rockchip_select_output(struct drm_connector_state *conn_state, struct drm_crtc_state *crtc_state, @@ -747,65 +1982,81 @@ unsigned int *eotf) { struct drm_display_info *info = &conn_state->connector->display_info; - struct drm_display_mode *mode = &crtc_state->mode; + struct drm_display_mode mode = {}; struct hdr_output_metadata *hdr_metadata; - u32 vic = drm_match_cea_mode(mode); - unsigned long tmdsclock, pixclock = mode->crtc_clock; + u32 vic; + unsigned long tmdsclock, pixclock; unsigned int color_depth; bool support_dc = false; - bool sink_is_hdmi = dw_hdmi_get_output_whether_hdmi(hdmi->hdmi); - int max_tmds_clock = info->max_tmds_clock; + bool sink_is_hdmi = true; + bool yuv422_out = false; + u32 max_tmds_clock = info->max_tmds_clock; int output_eotf; - *color_format = DRM_HDMI_OUTPUT_DEFAULT_RGB; + drm_mode_copy(&mode, &crtc_state->mode); + pixclock = mode.crtc_clock; + if (hdmi->plat_data->split_mode) { + drm_mode_convert_to_origin_mode(&mode); + pixclock /= 2; + } + + vic = drm_match_cea_mode(&mode); + + if (!hdmi->is_hdmi_qp) + sink_is_hdmi = dw_hdmi_get_output_whether_hdmi(hdmi->hdmi); + else + sink_is_hdmi = dw_hdmi_qp_get_output_whether_hdmi(hdmi->hdmi_qp); + + *color_format = RK_IF_FORMAT_RGB; switch (hdmi->hdmi_output) { - case DRM_HDMI_OUTPUT_YCBCR_HQ: + case RK_IF_FORMAT_YCBCR_HQ: if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444) - *color_format = DRM_HDMI_OUTPUT_YCBCR444; + *color_format = RK_IF_FORMAT_YCBCR444; else if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422) - *color_format = DRM_HDMI_OUTPUT_YCBCR422; + *color_format = RK_IF_FORMAT_YCBCR422; else if (conn_state->connector->ycbcr_420_allowed && - drm_mode_is_420(info, mode) && pixclock >= 594000) - *color_format = DRM_HDMI_OUTPUT_YCBCR420; + drm_mode_is_420(info, &mode) && + (pixclock >= 594000 && !hdmi->is_hdmi_qp)) + *color_format = RK_IF_FORMAT_YCBCR420; break; - case DRM_HDMI_OUTPUT_YCBCR_LQ: + case RK_IF_FORMAT_YCBCR_LQ: if (conn_state->connector->ycbcr_420_allowed && - drm_mode_is_420(info, mode) && pixclock >= 594000) - *color_format = DRM_HDMI_OUTPUT_YCBCR420; + drm_mode_is_420(info, &mode) && pixclock >= 594000) + *color_format = RK_IF_FORMAT_YCBCR420; else if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422) - *color_format = DRM_HDMI_OUTPUT_YCBCR422; + *color_format = RK_IF_FORMAT_YCBCR422; else if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444) - *color_format = DRM_HDMI_OUTPUT_YCBCR444; + *color_format = RK_IF_FORMAT_YCBCR444; break; - case DRM_HDMI_OUTPUT_YCBCR420: + case RK_IF_FORMAT_YCBCR420: if (conn_state->connector->ycbcr_420_allowed && - drm_mode_is_420(info, mode) && pixclock >= 594000) - *color_format = DRM_HDMI_OUTPUT_YCBCR420; + drm_mode_is_420(info, &mode) && pixclock >= 594000) + *color_format = RK_IF_FORMAT_YCBCR420; break; - case DRM_HDMI_OUTPUT_YCBCR422: + case RK_IF_FORMAT_YCBCR422: if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422) - *color_format = DRM_HDMI_OUTPUT_YCBCR422; + *color_format = RK_IF_FORMAT_YCBCR422; break; - case DRM_HDMI_OUTPUT_YCBCR444: + case RK_IF_FORMAT_YCBCR444: if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444) - *color_format = DRM_HDMI_OUTPUT_YCBCR444; + *color_format = RK_IF_FORMAT_YCBCR444; break; - case DRM_HDMI_OUTPUT_DEFAULT_RGB: + case RK_IF_FORMAT_RGB: default: break; } - if (*color_format == DRM_HDMI_OUTPUT_DEFAULT_RGB && + if (*color_format == RK_IF_FORMAT_RGB && info->edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_30) support_dc = true; - if (*color_format == DRM_HDMI_OUTPUT_YCBCR444 && + if (*color_format == RK_IF_FORMAT_YCBCR444 && info->edid_hdmi_dc_modes & (DRM_EDID_HDMI_DC_Y444 | DRM_EDID_HDMI_DC_30)) support_dc = true; - if (*color_format == DRM_HDMI_OUTPUT_YCBCR422) + if (*color_format == RK_IF_FORMAT_YCBCR422) support_dc = true; - if (*color_format == DRM_HDMI_OUTPUT_YCBCR420 && + if (*color_format == RK_IF_FORMAT_YCBCR420 && info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30) support_dc = true; @@ -814,53 +2065,66 @@ else color_depth = 8; - if (!sink_is_hdmi) { - *color_format = DRM_HDMI_OUTPUT_DEFAULT_RGB; - color_depth = 8; - } - - *eotf = TRADITIONAL_GAMMA_SDR; + *eotf = HDMI_EOTF_TRADITIONAL_GAMMA_SDR; if (conn_state->hdr_output_metadata) { hdr_metadata = (struct hdr_output_metadata *) conn_state->hdr_output_metadata->data; output_eotf = hdr_metadata->hdmi_metadata_type1.eotf; - if (output_eotf > TRADITIONAL_GAMMA_HDR && - output_eotf < FUTURE_EOTF) + if (output_eotf > HDMI_EOTF_TRADITIONAL_GAMMA_SDR && + output_eotf <= HDMI_EOTF_BT_2100_HLG) *eotf = output_eotf; } - if ((*eotf > TRADITIONAL_GAMMA_HDR && - conn_state->connector->hdr_sink_metadata.hdmi_type1.eotf & - BIT(*eotf)) || (hdmi->colorimetry == - RK_HDMI_COLORIMETRY_BT2020 && info->hdmi.colorimetry & - (BIT(6) | BIT(7)))) - *enc_out_encoding = V4L2_YCBCR_ENC_BT2020; - else if ((vic == 6) || (vic == 7) || (vic == 21) || (vic == 22) || - (vic == 2) || (vic == 3) || (vic == 17) || (vic == 18)) - *enc_out_encoding = V4L2_YCBCR_ENC_601; - else - *enc_out_encoding = V4L2_YCBCR_ENC_709; + hdmi->colorimetry = conn_state->colorspace; - if (*enc_out_encoding == V4L2_YCBCR_ENC_BT2020) { - /* BT2020 require color depth at lest 10bit */ - color_depth = 10; - /* We prefer use YCbCr422 to send 10bit */ - if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422) - *color_format = DRM_HDMI_OUTPUT_YCBCR422; + /* bt2020 sdr/hdr output */ + if ((hdmi->colorimetry >= DRM_MODE_COLORIMETRY_BT2020_CYCC) && + (hdmi->colorimetry <= DRM_MODE_COLORIMETRY_BT2020_YCC) && + hdmi->edid_colorimetry & (BIT(6) | BIT(7))) { + *enc_out_encoding = V4L2_YCBCR_ENC_BT2020; + yuv422_out = true; + /* bt709 hdr output */ + } else if ((hdmi->colorimetry <= DRM_MODE_COLORIMETRY_BT2020_CYCC) && + (hdmi->colorimetry >= DRM_MODE_COLORIMETRY_BT2020_YCC) && + (conn_state->connector->hdr_sink_metadata.hdmi_type1.eotf & BIT(*eotf) && + *eotf > HDMI_EOTF_TRADITIONAL_GAMMA_SDR)) { + *enc_out_encoding = V4L2_YCBCR_ENC_709; + yuv422_out = true; + } else if ((vic == 6) || (vic == 7) || (vic == 21) || (vic == 22) || + (vic == 2) || (vic == 3) || (vic == 17) || (vic == 18)) { + *enc_out_encoding = V4L2_YCBCR_ENC_601; + } else { + *enc_out_encoding = V4L2_YCBCR_ENC_709; } - if (mode->flags & DRM_MODE_FLAG_DBLCLK) + if ((yuv422_out || hdmi->hdmi_output == RK_IF_FORMAT_YCBCR_HQ) && color_depth == 10 && + (hdmi_bus_fmt_color_depth(hdmi->prev_bus_format) == 8 || + hdmi_bus_fmt_to_color_format(hdmi->prev_bus_format) == RK_IF_FORMAT_YCBCR422)) { + /* We prefer use YCbCr422 to send hdr 10bit */ + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422) + *color_format = RK_IF_FORMAT_YCBCR422; + } + + if (mode.flags & DRM_MODE_FLAG_DBLCLK) pixclock *= 2; - if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == + if ((mode.flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING) pixclock *= 2; - if (*color_format == DRM_HDMI_OUTPUT_YCBCR422 || color_depth == 8) + if (hdmi->is_hdmi_qp && mode.clock >= 600000) + *color_format = RK_IF_FORMAT_YCBCR420; + + if (!sink_is_hdmi) { + *color_format = RK_IF_FORMAT_RGB; + color_depth = 8; + } + + if (*color_format == RK_IF_FORMAT_YCBCR422 || color_depth == 8) tmdsclock = pixclock; else tmdsclock = pixclock * (color_depth) / 8; - if (*color_format == DRM_HDMI_OUTPUT_YCBCR420) + if (*color_format == RK_IF_FORMAT_YCBCR420) tmdsclock /= 2; /* XXX: max_tmds_clock of some sink is 0, we think it is 340MHz. */ @@ -869,67 +2133,76 @@ max_tmds_clock = min(max_tmds_clock, hdmi->max_tmdsclk); + if (hdmi->is_hdmi_qp && hdmi->link_cfg.rate_per_lane && mode.clock > 600000) + max_tmds_clock = + hdmi->link_cfg.frl_lanes * hdmi->link_cfg.rate_per_lane * 1000000; + if (tmdsclock > max_tmds_clock) { if (max_tmds_clock >= 594000) { color_depth = 8; } else if (max_tmds_clock > 340000) { - if (drm_mode_is_420(info, mode) || tmdsclock >= 594000) - *color_format = DRM_HDMI_OUTPUT_YCBCR420; + if (drm_mode_is_420(info, &mode) || tmdsclock >= 594000) + *color_format = RK_IF_FORMAT_YCBCR420; } else { color_depth = 8; - if (drm_mode_is_420(info, mode) || tmdsclock >= 594000) - *color_format = DRM_HDMI_OUTPUT_YCBCR420; + if (drm_mode_is_420(info, &mode) || tmdsclock >= 594000) + *color_format = RK_IF_FORMAT_YCBCR420; } } - if (*color_format == DRM_HDMI_OUTPUT_YCBCR420) { + if (*color_format == RK_IF_FORMAT_YCBCR420) { *output_mode = ROCKCHIP_OUT_MODE_YUV420; if (color_depth > 8) *bus_format = MEDIA_BUS_FMT_UYYVYY10_0_5X30; else *bus_format = MEDIA_BUS_FMT_UYYVYY8_0_5X24; *bus_width = color_depth / 2; - hdmi->output_bus_format = *bus_format; } else { *output_mode = ROCKCHIP_OUT_MODE_AAAA; - if (color_depth > 8) { - if (*color_format != DRM_HDMI_OUTPUT_DEFAULT_RGB) - hdmi->output_bus_format = MEDIA_BUS_FMT_YUV10_1X30; - else - hdmi->output_bus_format = MEDIA_BUS_FMT_RGB101010_1X30; - - if (!hdmi->unsupported_yuv_input) - *bus_format = hdmi->output_bus_format; + if (*color_format != RK_IF_FORMAT_RGB && + !hdmi->unsupported_yuv_input) + *bus_format = MEDIA_BUS_FMT_YUV10_1X30; else *bus_format = MEDIA_BUS_FMT_RGB101010_1X30; } else { - if (*color_format != DRM_HDMI_OUTPUT_DEFAULT_RGB) - hdmi->output_bus_format = MEDIA_BUS_FMT_YUV8_1X24; - else - hdmi->output_bus_format = MEDIA_BUS_FMT_RGB888_1X24; - - if (!hdmi->unsupported_yuv_input) - *bus_format = hdmi->output_bus_format; + if (*color_format != RK_IF_FORMAT_RGB && + !hdmi->unsupported_yuv_input) + *bus_format = MEDIA_BUS_FMT_YUV8_1X24; else *bus_format = MEDIA_BUS_FMT_RGB888_1X24; } - - if (*color_format == DRM_HDMI_OUTPUT_YCBCR422) { + if (*color_format == RK_IF_FORMAT_YCBCR422) *bus_width = 8; + else + *bus_width = color_depth; + } + hdmi->bus_format = *bus_format; + + if (*color_format == RK_IF_FORMAT_YCBCR422) { + if (hdmi->is_hdmi_qp) { + if (color_depth == 12) + hdmi->output_bus_format = MEDIA_BUS_FMT_YUYV12_1X24; + else if (color_depth == 10) + hdmi->output_bus_format = MEDIA_BUS_FMT_YUYV10_1X20; + else + hdmi->output_bus_format = MEDIA_BUS_FMT_YUYV8_1X16; + + *bus_format = hdmi->output_bus_format; + hdmi->bus_format = *bus_format; + *output_mode = ROCKCHIP_OUT_MODE_YUV422; + } else { if (color_depth == 12) hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY12_1X24; else if (color_depth == 10) hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY10_1X20; else hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY8_1X16; - } else { - *bus_width = color_depth; } + } else { + hdmi->output_bus_format = *bus_format; } - - hdmi->bus_format = *bus_format; } static bool @@ -963,49 +2236,101 @@ { struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder); - unsigned int colorformat, bus_width; + unsigned int colorformat, bus_width, tmdsclk; + struct drm_display_mode mode = {}; unsigned int output_mode; unsigned long bus_format; + int color_depth; + bool secondary = false; + + /* + * There are two hdmi but only one encoder in split mode, + * so we need to check twice. + */ +secondary: + drm_mode_copy(&mode, &crtc_state->mode); + + if (hdmi->plat_data->split_mode) + drm_mode_convert_to_origin_mode(&mode); dw_hdmi_rockchip_select_output(conn_state, crtc_state, hdmi, &colorformat, &output_mode, &bus_format, &bus_width, &hdmi->enc_out_encoding, &s->eotf); + s->bus_format = bus_format; + if (hdmi->is_hdmi_qp) { + color_depth = hdmi_bus_fmt_color_depth(bus_format); + tmdsclk = hdmi_get_tmdsclock(hdmi, crtc_state->mode.clock); + if (hdmi_bus_fmt_is_yuv420(hdmi->output_bus_format)) + tmdsclk /= 2; + hdmi_select_link_config(hdmi, crtc_state, tmdsclk); + + if (hdmi->link_cfg.frl_mode) { + /* in the current version, support max 40G frl */ + if (hdmi->link_cfg.rate_per_lane >= 10) { + hdmi->link_cfg.frl_lanes = 4; + hdmi->link_cfg.rate_per_lane = 10; + } + bus_width = hdmi->link_cfg.frl_lanes * + hdmi->link_cfg.rate_per_lane * 1000000; + /* 10 bit color depth and frl mode */ + if (color_depth == 10) + bus_width |= + COLOR_DEPTH_10BIT | HDMI_FRL_MODE; + else + bus_width |= HDMI_FRL_MODE; + } else { + bus_width = hdmi_get_tmdsclock(hdmi, mode.clock * 10); + if (hdmi_bus_fmt_is_yuv420(hdmi->output_bus_format)) + bus_width /= 2; + + if (color_depth == 10 && !hdmi_bus_fmt_is_yuv422(hdmi->output_bus_format)) + bus_width |= COLOR_DEPTH_10BIT; + } + } + hdmi->phy_bus_width = bus_width; + if (hdmi->phy) phy_set_bus_width(hdmi->phy, bus_width); s->output_type = DRM_MODE_CONNECTOR_HDMIA; s->tv_state = &conn_state->tv; - s->output_if |= VOP_OUTPUT_IF_HDMI0; + + if (hdmi->plat_data->split_mode) { + s->output_flags |= ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE; + if (hdmi->plat_data->right && hdmi->id) + s->output_flags |= ROCKCHIP_OUTPUT_DATA_SWAP; + s->output_if |= VOP_OUTPUT_IF_HDMI0 | VOP_OUTPUT_IF_HDMI1; + } else { + if (!hdmi->id) + s->output_if |= VOP_OUTPUT_IF_HDMI0; + else + s->output_if |= VOP_OUTPUT_IF_HDMI1; + } s->output_mode = output_mode; - s->bus_format = bus_format; hdmi->bus_format = s->bus_format; - - hdmi->mode_changed = crtc_state->mode_changed; if (hdmi->enc_out_encoding == V4L2_YCBCR_ENC_BT2020) s->color_space = V4L2_COLORSPACE_BT2020; - else if (colorformat == DRM_HDMI_OUTPUT_DEFAULT_RGB) + else if (colorformat == RK_IF_FORMAT_RGB) s->color_space = V4L2_COLORSPACE_DEFAULT; else if (hdmi->enc_out_encoding == V4L2_YCBCR_ENC_709) s->color_space = V4L2_COLORSPACE_REC709; else s->color_space = V4L2_COLORSPACE_SMPTE170M; + if (hdmi->plat_data->split_mode && !secondary) { + hdmi = rockchip_hdmi_find_by_id(hdmi->dev->driver, !hdmi->id); + secondary = true; + goto secondary; + } + return 0; } -static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adj) -{ - struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder); - - clk_set_rate(hdmi->phyref_clk, adj->crtc_clock * 1000); -} static unsigned long dw_hdmi_rockchip_get_input_bus_format(void *data) @@ -1063,6 +2388,14 @@ return hdmi->hdr_panel_blob_ptr; } +static void dw_hdmi_rockchip_update_color_format(struct drm_connector_state *conn_state, + void *data) +{ + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + + dw_hdmi_rockchip_check_color(conn_state, hdmi); +} + static bool dw_hdmi_rockchip_get_color_changed(void *data) { @@ -1076,6 +2409,198 @@ return ret; } +static int +dw_hdmi_rockchip_get_yuv422_format(struct drm_connector *connector, + struct edid *edid) +{ + if (!connector || !edid) + return -EINVAL; + + return rockchip_drm_get_yuv422_format(connector, edid); +} + +static int +dw_hdmi_rockchip_get_edid_dsc_info(void *data, struct edid *edid) +{ + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + + if (!edid) + return -EINVAL; + + memset(&hdmi->dsc_cap, 0, sizeof(hdmi->dsc_cap)); + hdmi->max_frl_rate_per_lane = 0; + hdmi->max_lanes = 0; + hdmi->add_func = 0; + + return rockchip_drm_parse_cea_ext(&hdmi->dsc_cap, + &hdmi->max_frl_rate_per_lane, + &hdmi->max_lanes, &hdmi->add_func, edid); +} + +static int +dw_hdmi_rockchip_get_next_hdr_data(void *data, struct edid *edid, + struct drm_connector *connector) +{ + int ret; + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + struct next_hdr_sink_data *sink_data = &hdmi->next_hdr_data; + size_t size = sizeof(*sink_data); + struct drm_property *property = hdmi->next_hdr_sink_data_property; + struct drm_property_blob *blob = hdmi->hdr_panel_blob_ptr; + + if (!edid) + return -EINVAL; + + rockchip_drm_parse_next_hdr(sink_data, edid); + + ret = drm_property_replace_global_blob(connector->dev, &blob, size, sink_data, + &connector->base, property); + + return ret; +}; + +static int dw_hdmi_rockchip_get_colorimetry(void *data, struct edid *edid) +{ + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + + return rockchip_drm_parse_colorimetry_data_block(&hdmi->edid_colorimetry, edid); +} + +static +struct dw_hdmi_link_config *dw_hdmi_rockchip_get_link_cfg(void *data) +{ + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + + return &hdmi->link_cfg; +} + +static int dw_hdmi_rockchip_get_vp_id(struct drm_crtc_state *crtc_state) +{ + struct rockchip_crtc_state *s; + + s = to_rockchip_crtc_state(crtc_state); + + return s->vp_id; +} + +static int dw_hdmi_dclk_set(void *data, bool enable, int vp_id) +{ + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + char clk_name[16]; + struct clk *dclk; + int ret; + + snprintf(clk_name, sizeof(clk_name), "dclk_vp%d", vp_id); + + dclk = devm_clk_get_optional(hdmi->dev, clk_name); + if (IS_ERR(dclk)) { + DRM_DEV_ERROR(hdmi->dev, "failed to get %s\n", clk_name); + return PTR_ERR(dclk); + } else if (!dclk) { + if (hdmi->is_hdmi_qp) { + DRM_DEV_ERROR(hdmi->dev, "failed to get %s\n", clk_name); + return -ENOENT; + } + + return 0; + } + + if (enable) { + ret = clk_prepare_enable(dclk); + if (ret < 0) + DRM_DEV_ERROR(hdmi->dev, "failed to enable dclk for video port%d - %d\n", + vp_id, ret); + } else { + clk_disable_unprepare(dclk); + } + + return 0; +} + +static int dw_hdmi_link_clk_set(void *data, bool enable) +{ + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + u64 phy_clk = hdmi->phy_bus_width; + int ret; + + if (enable) { + ret = clk_prepare_enable(hdmi->link_clk); + if (ret < 0) { + DRM_DEV_ERROR(hdmi->dev, "failed to enable link_clk %d\n", ret); + return ret; + } + + if (((phy_clk & DATA_RATE_MASK) <= 6000000) && + (phy_clk & COLOR_DEPTH_10BIT)) + phy_clk = (phy_clk & DATA_RATE_MASK) * 10 * 8; + else + phy_clk = (phy_clk & DATA_RATE_MASK) * 100; + + /* + * To be compatible with vop dclk usage scenarios, hdmi phy pll clk + * is set according to dclk rate. + * But phy pll actual frequency will varies according to the color depth. + * So we should get the actual frequency or clk_set_rate may not change + * pll frequency when 8/10 bit switch. + */ + clk_get_rate(hdmi->link_clk); + clk_set_rate(hdmi->link_clk, phy_clk); + } else { + clk_disable_unprepare(hdmi->link_clk); + } + return 0; +} + +static bool +dw_hdmi_rockchip_check_hdr_color_change(struct drm_connector_state *conn_state, + void *data) +{ + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + + if (!conn_state || !data) + return false; + + if (dw_hdmi_rockchip_check_color(conn_state, hdmi)) + return true; + + return false; +} + +static void dw_hdmi_rockchip_set_prev_bus_format(void *data, unsigned long bus_format) +{ + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + + hdmi->prev_bus_format = bus_format; +} + +static void dw_hdmi_rockchip_set_ddc_io(void *data, bool enable) +{ + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + + if (!hdmi->p || !hdmi->idle_state || !hdmi->default_state) + return; + + if (!enable) { + if (pinctrl_select_state(hdmi->p, hdmi->idle_state)) + dev_err(hdmi->dev, "could not select idle state\n"); + } else { + if (pinctrl_select_state(hdmi->p, hdmi->default_state)) + dev_err(hdmi->dev, "could not select default state\n"); + } +} + +static void dw_hdmi_rockchip_set_hdcp14_mem(void *data, bool enable) +{ + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + u32 val; + + val = HIWORD_UPDATE(enable << 15, RK3588_HDMI_HDCP14_MEM_EN); + if (!hdmi->id) + regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON4, val); + else + regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON7, val); +} + static const struct drm_prop_enum_list color_depth_enum_list[] = { { 0, "Automatic" }, /* Prefer highest color depth */ { 8, "24bit" }, @@ -1083,24 +2608,19 @@ }; static const struct drm_prop_enum_list drm_hdmi_output_enum_list[] = { - { DRM_HDMI_OUTPUT_DEFAULT_RGB, "output_rgb" }, - { DRM_HDMI_OUTPUT_YCBCR444, "output_ycbcr444" }, - { DRM_HDMI_OUTPUT_YCBCR422, "output_ycbcr422" }, - { DRM_HDMI_OUTPUT_YCBCR420, "output_ycbcr420" }, - { DRM_HDMI_OUTPUT_YCBCR_HQ, "output_ycbcr_high_subsampling" }, - { DRM_HDMI_OUTPUT_YCBCR_LQ, "output_ycbcr_low_subsampling" }, - { DRM_HDMI_OUTPUT_INVALID, "invalid_output" }, + { RK_IF_FORMAT_RGB, "rgb" }, + { RK_IF_FORMAT_YCBCR444, "ycbcr444" }, + { RK_IF_FORMAT_YCBCR422, "ycbcr422" }, + { RK_IF_FORMAT_YCBCR420, "ycbcr420" }, + { RK_IF_FORMAT_YCBCR_HQ, "ycbcr_high_subsampling" }, + { RK_IF_FORMAT_YCBCR_LQ, "ycbcr_low_subsampling" }, + { RK_IF_FORMAT_MAX, "invalid_output" }, }; static const struct drm_prop_enum_list quant_range_enum_list[] = { { HDMI_QUANTIZATION_RANGE_DEFAULT, "default" }, { HDMI_QUANTIZATION_RANGE_LIMITED, "limit" }, { HDMI_QUANTIZATION_RANGE_FULL, "full" }, -}; - -static const struct drm_prop_enum_list colorimetry_enum_list[] = { - { HDMI_COLORIMETRY_NONE, "None" }, - { RK_HDMI_COLORIMETRY_BT2020, "ITU_2020" }, }; static const struct drm_prop_enum_list output_hdmi_dvi_enum_list[] = { @@ -1114,58 +2634,76 @@ { 1, "HDMI" }, }; +static const struct drm_prop_enum_list allm_enable_list[] = { + { 0, "disable" }, + { 1, "enable" }, +}; + static void dw_hdmi_rockchip_attach_properties(struct drm_connector *connector, unsigned int color, int version, - void *data) + void *data, bool allm_en) { struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; struct drm_property *prop; struct rockchip_drm_private *private = connector->dev->dev_private; + int ret; switch (color) { case MEDIA_BUS_FMT_RGB101010_1X30: - hdmi->hdmi_output = DRM_HDMI_OUTPUT_DEFAULT_RGB; + hdmi->hdmi_output = RK_IF_FORMAT_RGB; hdmi->colordepth = 10; break; case MEDIA_BUS_FMT_YUV8_1X24: - hdmi->hdmi_output = DRM_HDMI_OUTPUT_YCBCR444; + hdmi->hdmi_output = RK_IF_FORMAT_YCBCR444; hdmi->colordepth = 8; break; case MEDIA_BUS_FMT_YUV10_1X30: - hdmi->hdmi_output = DRM_HDMI_OUTPUT_YCBCR444; + hdmi->hdmi_output = RK_IF_FORMAT_YCBCR444; hdmi->colordepth = 10; break; case MEDIA_BUS_FMT_UYVY10_1X20: - hdmi->hdmi_output = DRM_HDMI_OUTPUT_YCBCR422; + case MEDIA_BUS_FMT_YUYV10_1X20: + hdmi->hdmi_output = RK_IF_FORMAT_YCBCR422; hdmi->colordepth = 10; break; case MEDIA_BUS_FMT_UYVY8_1X16: - hdmi->hdmi_output = DRM_HDMI_OUTPUT_YCBCR422; + case MEDIA_BUS_FMT_YUYV8_1X16: + hdmi->hdmi_output = RK_IF_FORMAT_YCBCR422; hdmi->colordepth = 8; break; case MEDIA_BUS_FMT_UYYVYY8_0_5X24: - hdmi->hdmi_output = DRM_HDMI_OUTPUT_YCBCR420; + hdmi->hdmi_output = RK_IF_FORMAT_YCBCR420; hdmi->colordepth = 8; break; case MEDIA_BUS_FMT_UYYVYY10_0_5X30: - hdmi->hdmi_output = DRM_HDMI_OUTPUT_YCBCR420; + hdmi->hdmi_output = RK_IF_FORMAT_YCBCR420; hdmi->colordepth = 10; break; default: - hdmi->hdmi_output = DRM_HDMI_OUTPUT_DEFAULT_RGB; + hdmi->hdmi_output = RK_IF_FORMAT_RGB; hdmi->colordepth = 8; } hdmi->bus_format = color; + hdmi->prev_bus_format = color; - if (hdmi->hdmi_output == DRM_HDMI_OUTPUT_YCBCR422) { - if (hdmi->colordepth == 12) - hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY12_1X24; - else if (hdmi->colordepth == 10) - hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY10_1X20; - else - hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY8_1X16; + if (hdmi->hdmi_output == RK_IF_FORMAT_YCBCR422) { + if (hdmi->is_hdmi_qp) { + if (hdmi->colordepth == 12) + hdmi->output_bus_format = MEDIA_BUS_FMT_YUYV12_1X24; + else if (hdmi->colordepth == 10) + hdmi->output_bus_format = MEDIA_BUS_FMT_YUYV10_1X20; + else + hdmi->output_bus_format = MEDIA_BUS_FMT_YUYV8_1X16; + } else { + if (hdmi->colordepth == 12) + hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY12_1X24; + else if (hdmi->colordepth == 10) + hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY10_1X20; + else + hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY8_1X16; + } } else { hdmi->output_bus_format = hdmi->bus_format; } @@ -1173,7 +2711,7 @@ /* RK3368 does not support deep color mode */ if (!hdmi->color_depth_property && !hdmi->unsupported_deep_color) { prop = drm_property_create_enum(connector->dev, 0, - "hdmi_output_depth", + RK_IF_PROP_COLOR_DEPTH, color_depth_enum_list, ARRAY_SIZE(color_depth_enum_list)); if (prop) { @@ -1182,7 +2720,7 @@ } } - prop = drm_property_create_enum(connector->dev, 0, "hdmi_output_format", + prop = drm_property_create_enum(connector->dev, 0, RK_IF_PROP_COLOR_FORMAT, drm_hdmi_output_enum_list, ARRAY_SIZE(drm_hdmi_output_enum_list)); if (prop) { @@ -1190,17 +2728,8 @@ drm_object_attach_property(&connector->base, prop, 0); } - prop = drm_property_create_enum(connector->dev, 0, - "hdmi_output_colorimetry", - colorimetry_enum_list, - ARRAY_SIZE(colorimetry_enum_list)); - if (prop) { - hdmi->colorimetry_property = prop; - drm_object_attach_property(&connector->base, prop, 0); - } - prop = drm_property_create_range(connector->dev, 0, - "hdmi_color_depth_capacity", + RK_IF_PROP_COLOR_DEPTH_CAPS, 0, 0xff); if (prop) { hdmi->colordepth_capacity = prop; @@ -1208,22 +2737,12 @@ } prop = drm_property_create_range(connector->dev, 0, - "hdmi_output_mode_capacity", + RK_IF_PROP_COLOR_FORMAT_CAPS, 0, 0xf); if (prop) { hdmi->outputmode_capacity = prop; drm_object_attach_property(&connector->base, prop, 0); } - - prop = drm_property_create_enum(connector->dev, 0, - "hdmi_quant_range", - quant_range_enum_list, - ARRAY_SIZE(quant_range_enum_list)); - if (prop) { - hdmi->quant_range = prop; - drm_object_attach_property(&connector->base, prop, 0); - } - prop = drm_property_create(connector->dev, DRM_MODE_PROP_BLOB | @@ -1232,6 +2751,34 @@ if (prop) { hdmi->hdr_panel_metadata_property = prop; drm_object_attach_property(&connector->base, prop, 0); + } + + prop = drm_property_create(connector->dev, + DRM_MODE_PROP_BLOB | + DRM_MODE_PROP_IMMUTABLE, + "NEXT_HDR_SINK_DATA", 0); + if (prop) { + hdmi->next_hdr_sink_data_property = prop; + drm_object_attach_property(&connector->base, prop, 0); + } + + if (hdmi->is_hdmi_qp) { + prop = drm_property_create_bool(connector->dev, 0, "allm_capacity"); + if (prop) { + hdmi->allm_capacity = prop; + drm_object_attach_property(&connector->base, prop, + !!(hdmi->add_func & SUPPORT_HDMI_ALLM)); + } + + prop = drm_property_create_enum(connector->dev, 0, + "allm_enable", + allm_enable_list, + ARRAY_SIZE(allm_enable_list)); + if (prop) { + hdmi->allm_enable = prop; + drm_object_attach_property(&connector->base, prop, 0); + } + hdmi->enable_allm = allm_en; } prop = drm_property_create_enum(connector->dev, 0, @@ -1244,18 +2791,48 @@ } prop = drm_property_create_enum(connector->dev, 0, - "output_type_capacity", - output_type_cap_list, - ARRAY_SIZE(output_type_cap_list)); + "output_type_capacity", + output_type_cap_list, + ARRAY_SIZE(output_type_cap_list)); if (prop) { hdmi->output_type_capacity = prop; drm_object_attach_property(&connector->base, prop, 0); } + if (!hdmi->is_hdmi_qp) { + prop = drm_property_create_enum(connector->dev, 0, + "hdmi_quant_range", + quant_range_enum_list, + ARRAY_SIZE(quant_range_enum_list)); + if (prop) { + hdmi->quant_range = prop; + drm_object_attach_property(&connector->base, prop, 0); + } + } + prop = connector->dev->mode_config.hdr_output_metadata_property; - if (version >= 0x211a) + if (hdmi->is_hdmi_qp) drm_object_attach_property(&connector->base, prop, 0); - drm_object_attach_property(&connector->base, private->connector_id_prop, 0); + + if (!drm_mode_create_hdmi_colorspace_property(connector)) + drm_object_attach_property(&connector->base, + connector->colorspace_property, 0); + drm_object_attach_property(&connector->base, private->connector_id_prop, hdmi->id); + + ret = drm_connector_attach_content_protection_property(connector, true); + if (ret) { + dev_err(hdmi->dev, "failed to attach content protection: %d\n", ret); + return; + } + + prop = drm_property_create_range(connector->dev, 0, RK_IF_PROP_ENCRYPTED, + RK_IF_HDCP_ENCRYPTED_NONE, RK_IF_HDCP_ENCRYPTED_LEVEL2); + if (!prop) { + dev_err(hdmi->dev, "create hdcp encrypted prop for hdmi%d failed\n", hdmi->id); + return; + } + hdmi->hdcp_state_property = prop; + drm_object_attach_property(&connector->base, prop, RK_IF_HDCP_ENCRYPTED_NONE); } static void @@ -1294,16 +2871,16 @@ hdmi->quant_range = NULL; } - if (hdmi->colorimetry_property) { - drm_property_destroy(connector->dev, - hdmi->colorimetry_property); - hdmi->colordepth_capacity = NULL; - } - if (hdmi->hdr_panel_metadata_property) { drm_property_destroy(connector->dev, hdmi->hdr_panel_metadata_property); hdmi->hdr_panel_metadata_property = NULL; + } + + if (hdmi->next_hdr_sink_data_property) { + drm_property_destroy(connector->dev, + hdmi->next_hdr_sink_data_property); + hdmi->next_hdr_sink_data_property = NULL; } if (hdmi->output_hdmi_dvi) { @@ -1316,6 +2893,17 @@ drm_property_destroy(connector->dev, hdmi->output_type_capacity); hdmi->output_type_capacity = NULL; + } + + if (hdmi->allm_capacity) { + drm_property_destroy(connector->dev, + hdmi->allm_capacity); + hdmi->allm_capacity = NULL; + } + + if (hdmi->allm_enable) { + drm_property_destroy(connector->dev, hdmi->allm_enable); + hdmi->allm_enable = NULL; } } @@ -1353,14 +2941,16 @@ return 0; } else if (property == config->hdr_output_metadata_property) { return 0; - } else if (property == hdmi->colorimetry_property) { - hdmi->colorimetry = val; - return 0; } else if (property == hdmi->output_hdmi_dvi) { - if (hdmi->force_output != val) - hdmi->color_changed++; - hdmi->force_output = val; - dw_hdmi_set_output_type(hdmi->hdmi, val); + if (!hdmi->is_hdmi_qp) { + if (hdmi->force_output != val) + hdmi->color_changed++; + hdmi->force_output = val; + dw_hdmi_set_output_type(hdmi->hdmi, val); + } else { + hdmi->force_output = val; + dw_hdmi_qp_set_output_type(hdmi->hdmi_qp, val); + } return 0; } else if (property == hdmi->colordepth_capacity) { return 0; @@ -1368,9 +2958,22 @@ return 0; } else if (property == hdmi->output_type_capacity) { return 0; + } else if (property == hdmi->allm_capacity) { + return 0; + } else if (property == hdmi->allm_enable) { + u64 allm_enable = hdmi->enable_allm; + + hdmi->enable_allm = val; + if (allm_enable != hdmi->enable_allm) + dw_hdmi_qp_set_allm_enable(hdmi->hdmi_qp, hdmi->enable_allm); + return 0; + } else if (property == hdmi->hdcp_state_property) { + return 0; } - DRM_ERROR("failed to set rockchip hdmi connector property %s\n", property->name); + DRM_ERROR("Unknown property [PROP:%d:%s]\n", + property->base.id, property->name); + return -EINVAL; } @@ -1384,7 +2987,6 @@ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; struct drm_display_info *info = &connector->display_info; struct drm_mode_config *config = &connector->dev->mode_config; - struct rockchip_drm_private *private = connector->dev->dev_private; if (property == hdmi->color_depth_property) { *val = hdmi->colordepth; @@ -1393,32 +2995,32 @@ *val = hdmi->hdmi_output; return 0; } else if (property == hdmi->colordepth_capacity) { - *val = BIT(ROCKCHIP_HDMI_DEPTH_8); + *val = BIT(RK_IF_DEPTH_8); /* RK3368 only support 8bit */ if (hdmi->unsupported_deep_color) return 0; if (info->edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_30) - *val |= BIT(ROCKCHIP_HDMI_DEPTH_10); + *val |= BIT(RK_IF_DEPTH_10); if (info->edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_36) - *val |= BIT(ROCKCHIP_HDMI_DEPTH_12); + *val |= BIT(RK_IF_DEPTH_12); if (info->edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_48) - *val |= BIT(ROCKCHIP_HDMI_DEPTH_16); + *val |= BIT(RK_IF_DEPTH_16); if (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30) - *val |= BIT(ROCKCHIP_HDMI_DEPTH_420_10); + *val |= BIT(RK_IF_DEPTH_420_10); if (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_36) - *val |= BIT(ROCKCHIP_HDMI_DEPTH_420_12); + *val |= BIT(RK_IF_DEPTH_420_12); if (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_48) - *val |= BIT(ROCKCHIP_HDMI_DEPTH_420_16); + *val |= BIT(RK_IF_DEPTH_420_16); return 0; } else if (property == hdmi->outputmode_capacity) { - *val = BIT(DRM_HDMI_OUTPUT_DEFAULT_RGB); + *val = BIT(RK_IF_FORMAT_RGB); if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444) - *val |= BIT(DRM_HDMI_OUTPUT_YCBCR444); + *val |= BIT(RK_IF_FORMAT_YCBCR444); if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422) - *val |= BIT(DRM_HDMI_OUTPUT_YCBCR422); + *val |= BIT(RK_IF_FORMAT_YCBCR422); if (connector->ycbcr_420_allowed && info->color_formats & DRM_COLOR_FORMAT_YCRCB420) - *val |= BIT(DRM_HDMI_OUTPUT_YCBCR420); + *val |= BIT(RK_IF_FORMAT_YCBCR420); return 0; } else if (property == hdmi->quant_range) { *val = hdmi->hdmi_quant_range; @@ -1427,21 +3029,34 @@ *val = state->hdr_output_metadata ? state->hdr_output_metadata->base.id : 0; return 0; - } else if (property == hdmi->colorimetry_property) { - *val = hdmi->colorimetry; - return 0; - } else if (property == private->connector_id_prop) { - *val = hdmi->id; - return 0; } else if (property == hdmi->output_hdmi_dvi) { *val = hdmi->force_output; return 0; } else if (property == hdmi->output_type_capacity) { - *val = dw_hdmi_get_output_type_cap(hdmi->hdmi); + if (!hdmi->is_hdmi_qp) + *val = dw_hdmi_get_output_type_cap(hdmi->hdmi); + else + *val = dw_hdmi_qp_get_output_type_cap(hdmi->hdmi_qp); + return 0; + } else if (property == hdmi->allm_capacity) { + *val = !!(hdmi->add_func & SUPPORT_HDMI_ALLM); + return 0; + } else if (property == hdmi->allm_enable) { + *val = hdmi->enable_allm; + return 0; + } else if (property == hdmi->hdcp_state_property) { + if (hdmi->hdcp_status & BIT(1)) + *val = RK_IF_HDCP_ENCRYPTED_LEVEL2; + else if (hdmi->hdcp_status & BIT(0)) + *val = RK_IF_HDCP_ENCRYPTED_LEVEL1; + else + *val = RK_IF_HDCP_ENCRYPTED_NONE; return 0; } - DRM_ERROR("failed to get rockchip hdmi connector property %s\n", property->name); + DRM_ERROR("Unknown property [PROP:%d:%s]\n", + property->base.id, property->name); + return -EINVAL; } @@ -1452,6 +3067,36 @@ .get_property = dw_hdmi_rockchip_get_property, }; +static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adj) +{ + struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder); + struct drm_crtc *crtc; + struct rockchip_crtc_state *s; + + if (!encoder->crtc) + return; + crtc = encoder->crtc; + + if (!crtc->state) + return; + s = to_rockchip_crtc_state(crtc->state); + + if (!s) + return; + + if (hdmi->is_hdmi_qp) { + s->dsc_enable = 0; + if (hdmi->link_cfg.dsc_mode) + dw_hdmi_qp_dsc_configure(hdmi, s, crtc->state); + + phy_set_bus_width(hdmi->phy, hdmi->phy_bus_width); + } + + clk_set_rate(hdmi->phyref_clk, adj->crtc_clock * 1000); +} + static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_funcs = { .enable = dw_hdmi_rockchip_encoder_enable, .disable = dw_hdmi_rockchip_encoder_disable, @@ -1459,7 +3104,8 @@ .mode_set = dw_hdmi_rockchip_encoder_mode_set, }; -static void dw_hdmi_rockchip_genphy_disable(struct dw_hdmi *dw_hdmi, void *data) +static void +dw_hdmi_rockchip_genphy_disable(struct dw_hdmi *dw_hdmi, void *data) { struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; @@ -1467,13 +3113,15 @@ phy_power_off(hdmi->phy); } -static int dw_hdmi_rockchip_genphy_init(struct dw_hdmi *dw_hdmi, void *data, - struct drm_display_mode *mode) +static int +dw_hdmi_rockchip_genphy_init(struct dw_hdmi *dw_hdmi, void *data, + const struct drm_display_info *display, + const struct drm_display_mode *mode) { struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; dw_hdmi_rockchip_genphy_disable(dw_hdmi, data); - dw_hdmi_set_high_tmds_clock_ratio(dw_hdmi); + dw_hdmi_set_high_tmds_clock_ratio(dw_hdmi, display); return phy_power_on(hdmi->phy); } @@ -1483,10 +3131,17 @@ dw_hdmi_phy_setup_hpd(dw_hdmi, data); - regmap_write(hdmi->regmap, RK3228_GRF_SOC_CON2, - RK3228_DDC_MASK_EN); - regmap_write(hdmi->regmap, RK3228_GRF_SOC_CON6, - RK3228_IO_3V_DOMAIN); + regmap_write(hdmi->regmap, + RK3228_GRF_SOC_CON6, + HIWORD_UPDATE(RK3228_HDMI_HPD_VSEL | RK3228_HDMI_SDA_VSEL | + RK3228_HDMI_SCL_VSEL, + RK3228_HDMI_HPD_VSEL | RK3228_HDMI_SDA_VSEL | + RK3228_HDMI_SCL_VSEL)); + + regmap_write(hdmi->regmap, + RK3228_GRF_SOC_CON2, + HIWORD_UPDATE(RK3228_HDMI_SDAIN_MSK | RK3228_HDMI_SCLIN_MSK, + RK3228_HDMI_SDAIN_MSK | RK3228_HDMI_SCLIN_MSK)); } static enum drm_connector_status @@ -1534,6 +3189,90 @@ RK3328_HDMI_HPD_IOE)); } +static void dw_hdmi_qp_rockchip_phy_disable(struct dw_hdmi_qp *dw_hdmi, + void *data) +{ + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + + while (hdmi->phy->power_count > 0) + phy_power_off(hdmi->phy); +} + +static int dw_hdmi_qp_rockchip_genphy_init(struct dw_hdmi_qp *dw_hdmi, void *data, + struct drm_display_mode *mode) +{ + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + + dw_hdmi_qp_rockchip_phy_disable(dw_hdmi, data); + + return phy_power_on(hdmi->phy); +} + +static enum drm_connector_status +dw_hdmi_rk3588_read_hpd(struct dw_hdmi_qp *dw_hdmi, void *data) +{ + u32 val; + int ret; + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + + regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &val); + + if (!hdmi->id) { + if (val & RK3588_HDMI0_LEVEL_INT) { + hdmi->hpd_stat = true; + ret = connector_status_connected; + } else { + hdmi->hpd_stat = false; + ret = connector_status_disconnected; + } + } else { + if (val & RK3588_HDMI1_LEVEL_INT) { + hdmi->hpd_stat = true; + ret = connector_status_connected; + } else { + hdmi->hpd_stat = false; + ret = connector_status_disconnected; + } + } + + return ret; +} + +static void dw_hdmi_rk3588_setup_hpd(struct dw_hdmi_qp *dw_hdmi, void *data) +{ + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + u32 val; + + if (!hdmi->id) { + val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR, + RK3588_HDMI0_HPD_INT_CLR) | + HIWORD_UPDATE(0, RK3588_HDMI0_HPD_INT_MSK); + } else { + val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_CLR, + RK3588_HDMI1_HPD_INT_CLR) | + HIWORD_UPDATE(0, RK3588_HDMI1_HPD_INT_MSK); + } + + regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); +} + +static void dw_hdmi_rk3588_phy_set_mode(struct dw_hdmi_qp *dw_hdmi, void *data, + u32 mode_mask, bool enable) +{ + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + + if (!hdmi->phy) + return; + + /* set phy earc/frl mode */ + if (enable) + hdmi->phy_bus_width |= mode_mask; + else + hdmi->phy_bus_width &= ~mode_mask; + + phy_set_bus_width(hdmi->phy, hdmi->phy_bus_width); +} + static const struct dw_hdmi_phy_ops rk3228_hdmi_phy_ops = { .init = dw_hdmi_rockchip_genphy_init, .disable = dw_hdmi_rockchip_genphy_disable, @@ -1548,11 +3287,14 @@ static const struct dw_hdmi_plat_data rk3228_hdmi_drv_data = { .mode_valid = dw_hdmi_rockchip_mode_valid, + .mpll_cfg = rockchip_mpll_cfg, + .cur_ctr = rockchip_cur_ctr, .phy_config = rockchip_phy_config, - .phy_data = &rk3228_chip_data, - .phy_ops = &rk3228_hdmi_phy_ops, - .phy_name = "inno_dw_hdmi_phy", + .phy_data = &rk3228_chip_data, + .phy_ops = &rk3228_hdmi_phy_ops, + .phy_name = "inno_dw_hdmi_phy2", .phy_force_vendor = true, + .max_tmdsclk = 371250, .ycbcr_420_allowed = true, }; @@ -1570,6 +3312,7 @@ .phy_config = rockchip_phy_config, .phy_data = &rk3288_chip_data, .tmds_n_table = rockchip_werid_tmds_n_table, + .unsupported_yuv_input = true, .ycbcr_420_allowed = true, }; @@ -1579,6 +3322,20 @@ .read_hpd = dw_hdmi_rk3328_read_hpd, .update_hpd = dw_hdmi_phy_update_hpd, .setup_hpd = dw_hdmi_rk3328_setup_hpd, +}; + +static enum drm_connector_status +dw_hdmi_rk3528_read_hpd(struct dw_hdmi *dw_hdmi, void *data) +{ + return dw_hdmi_phy_read_hpd(dw_hdmi, data); +} + +static const struct dw_hdmi_phy_ops rk3528_hdmi_phy_ops = { + .init = dw_hdmi_rockchip_genphy_init, + .disable = dw_hdmi_rockchip_genphy_disable, + .read_hpd = dw_hdmi_rk3528_read_hpd, + .update_hpd = dw_hdmi_phy_update_hpd, + .setup_hpd = dw_hdmi_phy_setup_hpd, }; static struct rockchip_hdmi_chip_data rk3328_chip_data = { @@ -1594,6 +3351,8 @@ .phy_ops = &rk3328_hdmi_phy_ops, .phy_name = "inno_dw_hdmi_phy2", .phy_force_vendor = true, + .use_drm_infoframe = true, + .max_tmdsclk = 371250, .ycbcr_420_allowed = true, }; @@ -1608,6 +3367,8 @@ .cur_ctr = rockchip_cur_ctr, .phy_config = rockchip_phy_config, .phy_data = &rk3368_chip_data, + .unsupported_deep_color = true, + .max_tmdsclk = 340000, .ycbcr_420_allowed = true, }; @@ -1624,6 +3385,24 @@ .cur_ctr = rockchip_cur_ctr, .phy_config = rockchip_phy_config, .phy_data = &rk3399_chip_data, + .use_drm_infoframe = true, + .ycbcr_420_allowed = true, +}; + +static struct rockchip_hdmi_chip_data rk3528_chip_data = { + .lcdsel_grf_reg = -1, +}; + +static const struct dw_hdmi_plat_data rk3528_hdmi_drv_data = { + .mode_valid = dw_hdmi_rockchip_mode_valid, + .mpll_cfg = rockchip_mpll_cfg, + .cur_ctr = rockchip_cur_ctr, + .phy_config = rockchip_phy_config, + .phy_data = &rk3528_chip_data, + .phy_ops = &rk3528_hdmi_phy_ops, + .phy_name = "inno_dw_hdmi_phy2", + .phy_force_vendor = true, + .use_drm_infoframe = true, .ycbcr_420_allowed = true, }; @@ -1634,12 +3413,38 @@ static const struct dw_hdmi_plat_data rk3568_hdmi_drv_data = { .mode_valid = dw_hdmi_rockchip_mode_valid, - .mpll_cfg = rockchip_mpll_cfg_rk356x, + .mpll_cfg = rockchip_mpll_cfg, .mpll_cfg_420 = rockchip_mpll_cfg_420, - .cur_ctr = rockchip_cur_ctr_rk356x, + .cur_ctr = rockchip_cur_ctr, .phy_config = rockchip_phy_config, .phy_data = &rk3568_chip_data, .ycbcr_420_allowed = true, + .use_drm_infoframe = true, +}; + +static const struct dw_hdmi_qp_phy_ops rk3588_hdmi_phy_ops = { + .init = dw_hdmi_qp_rockchip_genphy_init, + .disable = dw_hdmi_qp_rockchip_phy_disable, + .read_hpd = dw_hdmi_rk3588_read_hpd, + .setup_hpd = dw_hdmi_rk3588_setup_hpd, + .set_mode = dw_hdmi_rk3588_phy_set_mode, +}; + +struct rockchip_hdmi_chip_data rk3588_hdmi_chip_data = { + .lcdsel_grf_reg = -1, + .ddc_en_reg = RK3588_GRF_VO1_CON3, + .split_mode = true, +}; + +static const struct dw_hdmi_plat_data rk3588_hdmi_drv_data = { + .mode_valid = dw_hdmi_rockchip_mode_valid, + .phy_data = &rk3588_hdmi_chip_data, + .qp_phy_ops = &rk3588_hdmi_phy_ops, + .phy_name = "samsung_hdptx_phy", + .phy_force_vendor = true, + .ycbcr_420_allowed = true, + .is_hdmi_qp = true, + .use_drm_infoframe = true, }; static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = { @@ -1659,8 +3464,14 @@ { .compatible = "rockchip,rk3399-dw-hdmi", .data = &rk3399_hdmi_drv_data }, + { .compatible = "rockchip,rk3528-dw-hdmi", + .data = &rk3528_hdmi_drv_data + }, { .compatible = "rockchip,rk3568-dw-hdmi", .data = &rk3568_hdmi_drv_data + }, + { .compatible = "rockchip,rk3588-dw-hdmi", + .data = &rk3588_hdmi_drv_data }, {}, }; @@ -1670,32 +3481,24 @@ void *data) { struct platform_device *pdev = to_platform_device(dev); - struct dw_hdmi_plat_data *plat_data; - const struct of_device_id *match; struct drm_device *drm = data; struct drm_encoder *encoder; struct rockchip_hdmi *hdmi; - int ret, id; + struct dw_hdmi_plat_data *plat_data; + struct rockchip_hdmi *secondary; + int ret; + u32 val; if (!pdev->dev.of_node) return -ENODEV; - hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL); + hdmi = platform_get_drvdata(pdev); if (!hdmi) return -ENOMEM; - match = of_match_node(dw_hdmi_rockchip_dt_ids, pdev->dev.of_node); - plat_data = devm_kmemdup(&pdev->dev, match->data, - sizeof(*plat_data), GFP_KERNEL); - if (!plat_data) - return -ENOMEM; + plat_data = hdmi->plat_data; + hdmi->drm_dev = drm; - id = of_alias_get_id(dev->of_node, "hdmi"); - if (id < 0) - id = 0; - hdmi->id = id; - hdmi->dev = &pdev->dev; - hdmi->chip_data = plat_data->phy_data; plat_data->phy_data = hdmi; plat_data->get_input_bus_format = dw_hdmi_rockchip_get_input_bus_format; @@ -1713,23 +3516,121 @@ dw_hdmi_rockchip_get_hdr_blob; plat_data->get_color_changed = dw_hdmi_rockchip_get_color_changed; + plat_data->get_yuv422_format = + dw_hdmi_rockchip_get_yuv422_format; + plat_data->get_edid_dsc_info = + dw_hdmi_rockchip_get_edid_dsc_info; + plat_data->get_next_hdr_data = + dw_hdmi_rockchip_get_next_hdr_data; + plat_data->get_colorimetry = + dw_hdmi_rockchip_get_colorimetry; + plat_data->get_link_cfg = dw_hdmi_rockchip_get_link_cfg; + plat_data->set_hdcp2_enable = rk3588_set_hdcp2_enable; + plat_data->set_hdcp_status = rk3588_set_hdcp_status; + plat_data->set_grf_cfg = rk3588_set_grf_cfg; + plat_data->get_grf_color_fmt = rk3588_get_grf_color_fmt; + plat_data->convert_to_split_mode = drm_mode_convert_to_split_mode; + plat_data->convert_to_origin_mode = drm_mode_convert_to_origin_mode; + plat_data->dclk_set = dw_hdmi_dclk_set; + plat_data->link_clk_set = dw_hdmi_link_clk_set; + plat_data->get_vp_id = dw_hdmi_rockchip_get_vp_id; + plat_data->update_color_format = + dw_hdmi_rockchip_update_color_format; + plat_data->check_hdr_color_change = + dw_hdmi_rockchip_check_hdr_color_change; + plat_data->set_prev_bus_format = + dw_hdmi_rockchip_set_prev_bus_format; + plat_data->set_ddc_io = + dw_hdmi_rockchip_set_ddc_io; + plat_data->set_hdcp14_mem = + dw_hdmi_rockchip_set_hdcp14_mem; plat_data->property_ops = &dw_hdmi_rockchip_property_ops; - encoder = &hdmi->encoder; + secondary = rockchip_hdmi_find_by_id(dev->driver, !hdmi->id); + /* If don't enable hdmi0 and hdmi1, we don't enable split mode */ + if (hdmi->chip_data->split_mode && secondary) { - encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); - /* - * If we failed to find the CRTC(s) which this encoder is - * supposed to be connected to, it's because the CRTC has - * not been registered yet. Defer probing, and hope that - * the required CRTC is added later. - */ - if (encoder->possible_crtcs == 0) - return -EPROBE_DEFER; + /* + * hdmi can only attach bridge and init encoder/connector in the + * last bind hdmi in split mode, or hdmi->hdmi_qp will not be initialized + * and plat_data->left/right will be null pointer. we must check if split + * mode is on and determine the sequence of hdmi bind. + */ + if (device_property_read_bool(dev, "split-mode") || + device_property_read_bool(secondary->dev, "split-mode")) { + plat_data->split_mode = true; + secondary->plat_data->split_mode = true; + if (!secondary->plat_data->first_screen) + plat_data->first_screen = true; + } + } + + if (!plat_data->first_screen) { + encoder = &hdmi->encoder; + encoder->possible_crtcs = rockchip_drm_of_find_possible_crtcs(drm, dev->of_node); + /* + * If we failed to find the CRTC(s) which this encoder is + * supposed to be connected to, it's because the CRTC has + * not been registered yet. Defer probing, and hope that + * the required CRTC is added later. + */ + if (encoder->possible_crtcs == 0) + return -EPROBE_DEFER; + + drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs); + drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS); + } + + if (!plat_data->max_tmdsclk) + hdmi->max_tmdsclk = 594000; + else + hdmi->max_tmdsclk = plat_data->max_tmdsclk; + + hdmi->is_hdmi_qp = plat_data->is_hdmi_qp; + + hdmi->unsupported_yuv_input = plat_data->unsupported_yuv_input; + hdmi->unsupported_deep_color = plat_data->unsupported_deep_color; ret = rockchip_hdmi_parse_dt(hdmi); if (ret) { DRM_DEV_ERROR(hdmi->dev, "Unable to parse OF data\n"); + return ret; + } + + ret = clk_prepare_enable(hdmi->aud_clk); + if (ret) { + dev_err(hdmi->dev, "Failed to enable HDMI aud_clk: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(hdmi->hpd_clk); + if (ret) { + dev_err(hdmi->dev, "Failed to enable HDMI hpd_clk: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(hdmi->hclk_vo1); + if (ret) { + dev_err(hdmi->dev, "Failed to enable HDMI hclk_vo1: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(hdmi->earc_clk); + if (ret) { + dev_err(hdmi->dev, "Failed to enable HDMI earc_clk: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(hdmi->hdmitx_ref); + if (ret) { + dev_err(hdmi->dev, "Failed to enable HDMI hdmitx_ref: %d\n", + ret); + return ret; + } + + ret = clk_prepare_enable(hdmi->pclk); + if (ret) { + dev_err(hdmi->dev, "Failed to enable HDMI pclk: %d\n", ret); return ret; } @@ -1739,6 +3640,39 @@ RK3568_HDMI_SCLIN_MSK, RK3568_HDMI_SDAIN_MSK | RK3568_HDMI_SCLIN_MSK)); + } + + if (hdmi->is_hdmi_qp) { + if (!hdmi->id) { + val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) | + HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) | + HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) | + HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK); + regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val); + + val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK, + RK3588_SET_HPD_PATH_MASK); + regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val); + + val = HIWORD_UPDATE(RK3588_HDMI0_GRANT_SEL, + RK3588_HDMI0_GRANT_SEL); + regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON9, val); + } else { + val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) | + HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) | + HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) | + HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK); + regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val); + + val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK, + RK3588_SET_HPD_PATH_MASK); + regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val); + + val = HIWORD_UPDATE(RK3588_HDMI1_GRANT_SEL, + RK3588_HDMI1_GRANT_SEL); + regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON9, val); + } + init_hpd_work(hdmi); } ret = clk_prepare_enable(hdmi->phyref_clk); @@ -1762,6 +3696,26 @@ return ret; } + if (hdmi->is_hdmi_qp) { + if (!hdmi->id) + val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_MSK, RK3588_HDMI0_HPD_INT_MSK); + else + val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_MSK, RK3588_HDMI1_HPD_INT_MSK); + regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); + + hdmi->hpd_irq = platform_get_irq(pdev, 4); + if (hdmi->hpd_irq < 0) + return hdmi->hpd_irq; + + ret = devm_request_threaded_irq(hdmi->dev, hdmi->hpd_irq, + rockchip_hdmi_hardirq, + rockchip_hdmi_irq, + IRQF_SHARED, "dw-hdmi-qp-hpd", + hdmi); + if (ret) + return ret; + } + hdmi->phy = devm_phy_optional_get(dev, "hdmi"); if (IS_ERR(hdmi->phy)) { hdmi->phy = devm_phy_optional_get(dev, "hdmi_phy"); @@ -1773,13 +3727,52 @@ } } - drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs); - drm_encoder_init(drm, encoder, &dw_hdmi_rockchip_encoder_funcs, - DRM_MODE_ENCODER_TMDS, NULL); + if (hdmi->is_hdmi_qp) { + hdmi->hdmi_qp = dw_hdmi_qp_bind(pdev, &hdmi->encoder, plat_data); - platform_set_drvdata(pdev, hdmi); + if (IS_ERR(hdmi->hdmi_qp)) { + ret = PTR_ERR(hdmi->hdmi_qp); + drm_encoder_cleanup(&hdmi->encoder); + } - hdmi->hdmi = dw_hdmi_bind(pdev, encoder, plat_data); + if (plat_data->bridge) { + struct drm_connector *connector = NULL; + struct list_head *connector_list = + &plat_data->bridge->dev->mode_config.connector_list; + + list_for_each_entry(connector, connector_list, head) + if (drm_connector_has_possible_encoder(connector, + &hdmi->encoder)) + break; + + hdmi->sub_dev.connector = connector; + hdmi->sub_dev.of_node = dev->of_node; + rockchip_drm_register_sub_dev(&hdmi->sub_dev); + } else if (plat_data->connector) { + hdmi->sub_dev.connector = plat_data->connector; + hdmi->sub_dev.loader_protect = dw_hdmi_rockchip_encoder_loader_protect; + if (secondary && device_property_read_bool(secondary->dev, "split-mode")) + hdmi->sub_dev.of_node = secondary->dev->of_node; + else + hdmi->sub_dev.of_node = hdmi->dev->of_node; + + rockchip_drm_register_sub_dev(&hdmi->sub_dev); + } + + if (plat_data->split_mode && secondary) { + if (device_property_read_bool(dev, "split-mode")) { + plat_data->right = secondary->hdmi_qp; + secondary->plat_data->left = hdmi->hdmi_qp; + } else { + plat_data->left = secondary->hdmi_qp; + secondary->plat_data->right = hdmi->hdmi_qp; + } + } + + return ret; + } + + hdmi->hdmi = dw_hdmi_bind(pdev, &hdmi->encoder, plat_data); /* * If dw_hdmi_bind() fails we'll never call dw_hdmi_unbind(), @@ -1787,9 +3780,15 @@ */ if (IS_ERR(hdmi->hdmi)) { ret = PTR_ERR(hdmi->hdmi); - drm_encoder_cleanup(encoder); + drm_encoder_cleanup(&hdmi->encoder); + clk_disable_unprepare(hdmi->aud_clk); clk_disable_unprepare(hdmi->phyref_clk); clk_disable_unprepare(hdmi->hclk_vop); + clk_disable_unprepare(hdmi->hpd_clk); + clk_disable_unprepare(hdmi->hclk_vo1); + clk_disable_unprepare(hdmi->earc_clk); + clk_disable_unprepare(hdmi->hdmitx_ref); + clk_disable_unprepare(hdmi->pclk); } if (plat_data->connector) { @@ -1806,11 +3805,27 @@ { struct rockchip_hdmi *hdmi = dev_get_drvdata(dev); + if (hdmi->is_hdmi_qp) { + cancel_delayed_work(&hdmi->work); + flush_workqueue(hdmi->workqueue); + destroy_workqueue(hdmi->workqueue); + } + if (hdmi->sub_dev.connector) rockchip_drm_unregister_sub_dev(&hdmi->sub_dev); - dw_hdmi_unbind(hdmi->hdmi); + + if (hdmi->is_hdmi_qp) + dw_hdmi_qp_unbind(hdmi->hdmi_qp); + else + dw_hdmi_unbind(hdmi->hdmi); + clk_disable_unprepare(hdmi->aud_clk); clk_disable_unprepare(hdmi->phyref_clk); clk_disable_unprepare(hdmi->hclk_vop); + clk_disable_unprepare(hdmi->hpd_clk); + clk_disable_unprepare(hdmi->hclk_vo1); + clk_disable_unprepare(hdmi->earc_clk); + clk_disable_unprepare(hdmi->hdmitx_ref); + clk_disable_unprepare(hdmi->pclk); } static const struct component_ops dw_hdmi_rockchip_ops = { @@ -1820,6 +3835,33 @@ static int dw_hdmi_rockchip_probe(struct platform_device *pdev) { + struct rockchip_hdmi *hdmi; + const struct of_device_id *match; + struct dw_hdmi_plat_data *plat_data; + int id; + + hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL); + if (!hdmi) + return -ENOMEM; + + id = of_alias_get_id(pdev->dev.of_node, "hdmi"); + if (id < 0) + id = 0; + + hdmi->id = id; + hdmi->dev = &pdev->dev; + + match = of_match_node(dw_hdmi_rockchip_dt_ids, pdev->dev.of_node); + plat_data = devm_kmemdup(&pdev->dev, match->data, + sizeof(*plat_data), GFP_KERNEL); + if (!plat_data) + return -ENOMEM; + + plat_data->id = hdmi->id; + hdmi->plat_data = plat_data; + hdmi->chip_data = plat_data->phy_data; + + platform_set_drvdata(pdev, hdmi); pm_runtime_enable(&pdev->dev); pm_runtime_get_sync(&pdev->dev); @@ -1828,12 +3870,25 @@ static void dw_hdmi_rockchip_shutdown(struct platform_device *pdev) { - struct rockchip_hdmi *hdmi = platform_get_drvdata(pdev); + struct rockchip_hdmi *hdmi = dev_get_drvdata(&pdev->dev); if (!hdmi) return; - dw_hdmi_suspend(&pdev->dev, hdmi->hdmi); + if (hdmi->is_hdmi_qp) { + if (hdmi->hpd_irq) + disable_irq(hdmi->hpd_irq); + cancel_delayed_work(&hdmi->work); + flush_workqueue(hdmi->workqueue); + dw_hdmi_qp_suspend(hdmi->dev, hdmi->hdmi_qp); + } else { + if (hdmi->hpd_gpiod) { + disable_irq(hdmi->hpd_irq); + if (hdmi->hpd_wake_en) + disable_irq_wake(hdmi->hpd_irq); + } + dw_hdmi_suspend(hdmi->hdmi); + } pm_runtime_put_sync(&pdev->dev); } @@ -1849,7 +3904,15 @@ { struct rockchip_hdmi *hdmi = dev_get_drvdata(dev); - dw_hdmi_suspend(dev, hdmi->hdmi); + if (hdmi->is_hdmi_qp) { + if (hdmi->hpd_irq) + disable_irq(hdmi->hpd_irq); + dw_hdmi_qp_suspend(dev, hdmi->hdmi_qp); + } else { + if (hdmi->hpd_gpiod) + disable_irq(hdmi->hpd_irq); + dw_hdmi_suspend(hdmi->hdmi); + } pm_runtime_put_sync(dev); return 0; @@ -1858,14 +3921,56 @@ static int dw_hdmi_rockchip_resume(struct device *dev) { struct rockchip_hdmi *hdmi = dev_get_drvdata(dev); + u32 val; + if (hdmi->is_hdmi_qp) { + if (!hdmi->id) { + val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) | + HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) | + HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) | + HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK); + regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val); + + val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK, + RK3588_SET_HPD_PATH_MASK); + regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val); + + val = HIWORD_UPDATE(RK3588_HDMI0_GRANT_SEL, + RK3588_HDMI0_GRANT_SEL); + regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON9, val); + } else { + val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) | + HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) | + HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) | + HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK); + regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val); + + val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK, + RK3588_SET_HPD_PATH_MASK); + regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val); + + val = HIWORD_UPDATE(RK3588_HDMI1_GRANT_SEL, + RK3588_HDMI1_GRANT_SEL); + regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON9, val); + } + + dw_hdmi_qp_resume(dev, hdmi->hdmi_qp); + if (hdmi->hpd_irq) + enable_irq(hdmi->hpd_irq); + drm_helper_hpd_irq_event(hdmi->drm_dev); + } else { + if (hdmi->hpd_gpiod) { + dw_hdmi_rk3528_gpio_hpd_init(hdmi); + enable_irq(hdmi->hpd_irq); + } + dw_hdmi_resume(hdmi->hdmi); + } pm_runtime_get_sync(dev); - dw_hdmi_resume(dev, hdmi->hdmi); - return 0; + return 0; } -static const struct dev_pm_ops dw_hdmi_pm_ops = { +static const struct dev_pm_ops dw_hdmi_rockchip_pm = { SET_SYSTEM_SLEEP_PM_OPS(dw_hdmi_rockchip_suspend, dw_hdmi_rockchip_resume) }; @@ -1876,7 +3981,7 @@ .shutdown = dw_hdmi_rockchip_shutdown, .driver = { .name = "dwhdmi-rockchip", + .pm = &dw_hdmi_rockchip_pm, .of_match_table = dw_hdmi_rockchip_dt_ids, - .pm = &dw_hdmi_pm_ops, }, }; -- Gitblit v1.6.2