1382 lines
40 KiB
C
1382 lines
40 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2021 Fuzhou Rockchip Electronics Co., Ltd
|
|
* Author: Algea Cao <algea.cao@rock-chips.com>
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <boot_rkimg.h>
|
|
#include <clk.h>
|
|
#include <fdtdec.h>
|
|
#include <regmap.h>
|
|
#include <syscon.h>
|
|
#include <asm/arch-rockchip/clock.h>
|
|
#include <asm/io.h>
|
|
#include <asm-generic/gpio.h>
|
|
#include <dm/of_access.h>
|
|
#include <dm/device.h>
|
|
#include <linux/dw_hdmi.h>
|
|
#include <linux/hdmi.h>
|
|
#include <linux/media-bus-format.h>
|
|
#include "rockchip_display.h"
|
|
#include "rockchip_crtc.h"
|
|
#include "rockchip_connector.h"
|
|
#include "rockchip_phy.h"
|
|
#include "dw_hdmi_qp.h"
|
|
#include "rockchip_dw_hdmi_qp.h"
|
|
|
|
#define HIWORD_UPDATE(val, mask) ((val) | (mask) << 16)
|
|
|
|
#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_CON3 0x000c
|
|
#define RK3588_COLOR_FORMAT_MASK 0xf
|
|
#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_CON6 0x0018
|
|
#define RK3588_GRF_VO1_CON7 0x001c
|
|
|
|
#define PPS_TABLE_LEN 8
|
|
|
|
#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
|
|
|
|
enum device_type {
|
|
RK3588_HDMI
|
|
};
|
|
|
|
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];
|
|
};
|
|
|
|
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 rockchip_connector connector;
|
|
int id;
|
|
struct udevice *dev;
|
|
struct regmap *regmap;
|
|
struct regmap *vo1_regmap;
|
|
void *grf;
|
|
void *vo1_grf;
|
|
|
|
unsigned long bus_format;
|
|
unsigned long output_bus_format;
|
|
unsigned long enc_out_encoding;
|
|
|
|
u8 max_frl_rate_per_lane;
|
|
u8 max_lanes;
|
|
bool allm_en;
|
|
u32 bus_width;
|
|
struct drm_hdmi_dsc_cap dsc_cap;
|
|
struct dw_hdmi_link_config link_cfg;
|
|
|
|
struct clk link_clk;
|
|
|
|
struct gpio_desc enable_gpio;
|
|
};
|
|
|
|
/*
|
|
* 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 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 void hdmi_select_link_config(struct rockchip_hdmi *hdmi,
|
|
struct drm_display_mode *mode,
|
|
unsigned int tmdsclk)
|
|
{
|
|
int max_lanes, max_rate_per_lane;
|
|
int max_dsc_lanes, max_dsc_rate_per_lane;
|
|
unsigned long max_frl_rate;
|
|
|
|
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.allm_en = hdmi->allm_en;
|
|
|
|
if (!max_frl_rate ||
|
|
(tmdsclk < HDMI20_MAX_RATE && mode->clock < HDMI20_MAX_RATE)) {
|
|
printf("%s use tmds mode\n", __func__);
|
|
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_display_mode *mode,
|
|
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 = 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(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_display_mode *mode)
|
|
{
|
|
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, mode, 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 drm_display_mode *mode)
|
|
{
|
|
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);
|
|
|
|
hdmi_is_dsc_1_2 = hdmi->dsc_cap.v_1p2;
|
|
|
|
if (!hdmi_is_dsc_1_2)
|
|
return;
|
|
|
|
slice_height = hdmi_dsc_get_slice_height(mode->vdisplay);
|
|
if (!slice_height)
|
|
return;
|
|
|
|
slice_count = hdmi_dsc_slices(hdmi, mode);
|
|
if (!slice_count)
|
|
return;
|
|
|
|
slice_width = DIV_ROUND_UP(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, mode->hdisplay,
|
|
mode->vdisplay, slice_width,
|
|
slice_height, bits_per_pixel, depth);
|
|
|
|
if (ret) {
|
|
dev_err(hdmi->dev, "set vdsc cfg failed\n");
|
|
return;
|
|
}
|
|
printf("dsc_enable\n");
|
|
}
|
|
|
|
static unsigned int drm_rk_select_color(struct hdmi_edid_data *edid_data,
|
|
struct base_screen_info *screen_info,
|
|
enum dw_hdmi_devtype dev_type,
|
|
bool output_bus_format_rgb)
|
|
{
|
|
struct drm_display_info *info = &edid_data->display_info;
|
|
struct drm_display_mode *mode = edid_data->preferred_mode;
|
|
struct drm_hdmi_info *hdmi_info = &edid_data->display_info.hdmi;
|
|
int max_tmds_clock = info->max_tmds_clock;
|
|
bool support_dc = false;
|
|
bool mode_420 = drm_mode_is_420(info, mode);
|
|
unsigned int color_depth = 8;
|
|
unsigned int base_color = DRM_HDMI_OUTPUT_YCBCR444;
|
|
unsigned int color_format = DRM_HDMI_OUTPUT_DEFAULT_RGB;
|
|
unsigned long tmdsclock, pixclock = mode->clock;
|
|
|
|
if (screen_info)
|
|
base_color = screen_info->format;
|
|
|
|
switch (base_color) {
|
|
case DRM_HDMI_OUTPUT_YCBCR_HQ:
|
|
if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
|
|
color_format = DRM_HDMI_OUTPUT_YCBCR444;
|
|
else if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
|
|
color_format = DRM_HDMI_OUTPUT_YCBCR422;
|
|
else if (mode_420 && pixclock >= 340000)
|
|
color_format = DRM_HDMI_OUTPUT_YCBCR420;
|
|
break;
|
|
case DRM_HDMI_OUTPUT_YCBCR_LQ:
|
|
if (mode_420 && pixclock >= 340000)
|
|
color_format = DRM_HDMI_OUTPUT_YCBCR420;
|
|
else if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
|
|
color_format = DRM_HDMI_OUTPUT_YCBCR422;
|
|
else if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
|
|
color_format = DRM_HDMI_OUTPUT_YCBCR444;
|
|
break;
|
|
case DRM_HDMI_OUTPUT_YCBCR420:
|
|
if (mode_420 && pixclock >= 340000)
|
|
color_format = DRM_HDMI_OUTPUT_YCBCR420;
|
|
break;
|
|
case DRM_HDMI_OUTPUT_YCBCR422:
|
|
if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
|
|
color_format = DRM_HDMI_OUTPUT_YCBCR422;
|
|
break;
|
|
case DRM_HDMI_OUTPUT_YCBCR444:
|
|
if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
|
|
color_format = DRM_HDMI_OUTPUT_YCBCR444;
|
|
break;
|
|
case DRM_HDMI_OUTPUT_DEFAULT_RGB:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (output_bus_format_rgb)
|
|
color_format = DRM_HDMI_OUTPUT_DEFAULT_RGB;
|
|
|
|
if (color_format == DRM_HDMI_OUTPUT_DEFAULT_RGB &&
|
|
info->edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_30)
|
|
support_dc = true;
|
|
if (color_format == DRM_HDMI_OUTPUT_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)
|
|
support_dc = true;
|
|
if (color_format == DRM_HDMI_OUTPUT_YCBCR420 &&
|
|
info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30)
|
|
support_dc = true;
|
|
|
|
if (mode->flags & DRM_MODE_FLAG_DBLCLK)
|
|
pixclock *= 2;
|
|
|
|
if (screen_info && screen_info->depth == 10)
|
|
color_depth = screen_info->depth;
|
|
|
|
if (mode->clock >= 600000)
|
|
color_format = DRM_HDMI_OUTPUT_YCBCR420;
|
|
|
|
if (color_format == DRM_HDMI_OUTPUT_YCBCR422 || color_depth == 8)
|
|
tmdsclock = pixclock;
|
|
else
|
|
tmdsclock = pixclock * color_depth / 8;
|
|
|
|
if (color_format == DRM_HDMI_OUTPUT_YCBCR420)
|
|
tmdsclock /= 2;
|
|
|
|
if (!max_tmds_clock)
|
|
max_tmds_clock = 340000;
|
|
|
|
if (hdmi_info->max_frl_rate_per_lane && mode->clock > 600000)
|
|
max_tmds_clock =
|
|
hdmi_info->max_lanes * hdmi_info->max_frl_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;
|
|
} else {
|
|
color_depth = 8;
|
|
if (drm_mode_is_420(info, mode) || tmdsclock >= 594000)
|
|
color_format = DRM_HDMI_OUTPUT_YCBCR420;
|
|
}
|
|
}
|
|
|
|
printf("color_format:%x\n", color_format);
|
|
if (color_depth > 8 && support_dc) {
|
|
switch (color_format) {
|
|
case DRM_HDMI_OUTPUT_YCBCR444:
|
|
return MEDIA_BUS_FMT_YUV10_1X30;
|
|
case DRM_HDMI_OUTPUT_YCBCR422:
|
|
return MEDIA_BUS_FMT_YUYV10_1X20;
|
|
case DRM_HDMI_OUTPUT_YCBCR420:
|
|
return MEDIA_BUS_FMT_UYYVYY10_0_5X30;
|
|
default:
|
|
return MEDIA_BUS_FMT_RGB101010_1X30;
|
|
}
|
|
} else {
|
|
switch (color_format) {
|
|
case DRM_HDMI_OUTPUT_YCBCR444:
|
|
return MEDIA_BUS_FMT_YUV8_1X24;
|
|
case DRM_HDMI_OUTPUT_YCBCR422:
|
|
return MEDIA_BUS_FMT_YUYV8_1X16;
|
|
case DRM_HDMI_OUTPUT_YCBCR420:
|
|
return MEDIA_BUS_FMT_UYYVYY8_0_5X24;
|
|
default:
|
|
return MEDIA_BUS_FMT_RGB888_1X24;
|
|
}
|
|
}
|
|
}
|
|
|
|
#define SUPPORT_HDMI_ALLM BIT(1)
|
|
|
|
void dw_hdmi_qp_selete_output(struct hdmi_edid_data *edid_data,
|
|
struct rockchip_connector *conn,
|
|
unsigned int *bus_format,
|
|
struct overscan *overscan,
|
|
enum dw_hdmi_devtype dev_type,
|
|
bool output_bus_format_rgb,
|
|
void *data, struct display_state *state)
|
|
{
|
|
struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
|
struct drm_hdmi_info *hdmi_info = &edid_data->display_info.hdmi;
|
|
int ret, i, screen_size;
|
|
struct base_disp_info base_parameter;
|
|
struct base2_disp_info *base2_parameter = state->conn_state.disp_info;
|
|
const struct base_overscan *scan;
|
|
struct base_screen_info *screen_info = NULL;
|
|
struct base2_screen_info *screen_info2 = NULL;
|
|
int max_scan = 100;
|
|
int min_scan = 51;
|
|
int offset = 0;
|
|
int color_depth;
|
|
unsigned int tmdsclk;
|
|
unsigned int pixel_clk;
|
|
bool found = false;
|
|
struct blk_desc *dev_desc;
|
|
disk_partition_t part_info;
|
|
char baseparameter_buf[8 * RK_BLK_SIZE] __aligned(ARCH_DMA_MINALIGN);
|
|
|
|
overscan->left_margin = max_scan;
|
|
overscan->right_margin = max_scan;
|
|
overscan->top_margin = max_scan;
|
|
overscan->bottom_margin = max_scan;
|
|
|
|
hdmi->max_frl_rate_per_lane = hdmi_info->max_frl_rate_per_lane;
|
|
hdmi->max_lanes = hdmi_info->max_lanes;
|
|
memcpy(&hdmi->dsc_cap, &hdmi_info->dsc_cap, sizeof(struct drm_hdmi_dsc_cap));
|
|
|
|
if (output_bus_format_rgb)
|
|
*bus_format = MEDIA_BUS_FMT_RGB888_1X24;
|
|
else
|
|
*bus_format = MEDIA_BUS_FMT_YUV8_1X24;
|
|
|
|
if (!base2_parameter) {
|
|
dev_desc = rockchip_get_bootdev();
|
|
if (!dev_desc) {
|
|
printf("%s: Could not find device\n", __func__);
|
|
goto null_basep;
|
|
}
|
|
|
|
ret = part_get_info_by_name(dev_desc, "baseparameter",
|
|
&part_info);
|
|
if (ret < 0) {
|
|
printf("Could not find baseparameter partition\n");
|
|
goto null_basep;
|
|
}
|
|
|
|
read_aux:
|
|
ret = blk_dread(dev_desc, part_info.start + offset, 1,
|
|
(void *)baseparameter_buf);
|
|
if (ret < 0) {
|
|
printf("read baseparameter failed\n");
|
|
goto null_basep;
|
|
}
|
|
|
|
memcpy(&base_parameter, baseparameter_buf,
|
|
sizeof(base_parameter));
|
|
scan = &base_parameter.scan;
|
|
|
|
screen_size = sizeof(base_parameter.screen_list) /
|
|
sizeof(base_parameter.screen_list[0]);
|
|
|
|
for (i = 0; i < screen_size; i++) {
|
|
if (base_parameter.screen_list[i].type ==
|
|
DRM_MODE_CONNECTOR_HDMIA) {
|
|
found = true;
|
|
screen_info = &base_parameter.screen_list[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found && !offset) {
|
|
printf("hdmi info isn't saved in main block\n");
|
|
offset += 16;
|
|
goto read_aux;
|
|
}
|
|
} else {
|
|
bool allm_en = false;
|
|
|
|
scan = &base2_parameter->overscan_info;
|
|
screen_size = sizeof(base2_parameter->screen_info) /
|
|
sizeof(base2_parameter->screen_info[0]);
|
|
|
|
for (i = 0; i < screen_size; i++) {
|
|
if (base2_parameter->screen_info[i].type ==
|
|
DRM_MODE_CONNECTOR_HDMIA) {
|
|
screen_info2 =
|
|
&base2_parameter->screen_info[i];
|
|
break;
|
|
}
|
|
}
|
|
screen_info = malloc(sizeof(*screen_info));
|
|
|
|
screen_info->type = screen_info2->type;
|
|
screen_info->mode = screen_info2->resolution;
|
|
screen_info->format = screen_info2->format;
|
|
screen_info->depth = screen_info2->depthc;
|
|
screen_info->feature = screen_info2->feature;
|
|
|
|
/* check if allm is enabled */
|
|
allm_en = base2_parameter->reserved[0] & BIT(0);
|
|
if (allm_en && (hdmi_info->add_func & SUPPORT_HDMI_ALLM))
|
|
hdmi->allm_en = true;
|
|
else
|
|
hdmi->allm_en = false;
|
|
}
|
|
|
|
if (scan->leftscale < min_scan && scan->leftscale > 0)
|
|
overscan->left_margin = min_scan;
|
|
else if (scan->leftscale < max_scan && scan->leftscale > 0)
|
|
overscan->left_margin = scan->leftscale;
|
|
|
|
if (scan->rightscale < min_scan && scan->rightscale > 0)
|
|
overscan->right_margin = min_scan;
|
|
else if (scan->rightscale < max_scan && scan->rightscale > 0)
|
|
overscan->right_margin = scan->rightscale;
|
|
|
|
if (scan->topscale < min_scan && scan->topscale > 0)
|
|
overscan->top_margin = min_scan;
|
|
else if (scan->topscale < max_scan && scan->topscale > 0)
|
|
overscan->top_margin = scan->topscale;
|
|
|
|
if (scan->bottomscale < min_scan && scan->bottomscale > 0)
|
|
overscan->bottom_margin = min_scan;
|
|
else if (scan->bottomscale < max_scan && scan->bottomscale > 0)
|
|
overscan->bottom_margin = scan->bottomscale;
|
|
|
|
null_basep:
|
|
|
|
if (screen_info)
|
|
printf("base_parameter.mode:%dx%d\n",
|
|
screen_info->mode.hdisplay,
|
|
screen_info->mode.vdisplay);
|
|
drm_rk_select_mode(edid_data, screen_info);
|
|
|
|
*bus_format = drm_rk_select_color(edid_data, screen_info,
|
|
dev_type, output_bus_format_rgb);
|
|
|
|
if (state->force_output) {
|
|
memcpy(edid_data->preferred_mode, &state->force_mode,
|
|
sizeof(struct drm_display_mode));
|
|
if (state->force_bus_format)
|
|
*bus_format = state->force_bus_format;
|
|
}
|
|
|
|
hdmi->bus_format = *bus_format;
|
|
color_depth = hdmi_bus_fmt_color_depth(*bus_format);
|
|
pixel_clk = edid_data->preferred_mode->clock;
|
|
tmdsclk = hdmi_get_tmdsclock(hdmi, pixel_clk);
|
|
if (hdmi_bus_fmt_is_yuv420(hdmi->output_bus_format))
|
|
tmdsclk /= 2;
|
|
hdmi_select_link_config(hdmi, edid_data->preferred_mode, tmdsclk);
|
|
dw_hdmi_qp_dsc_configure(hdmi, edid_data->preferred_mode);
|
|
if (hdmi->link_cfg.frl_mode) {
|
|
if (dm_gpio_is_valid(&hdmi->enable_gpio))
|
|
dm_gpio_set_value(&hdmi->enable_gpio, 0);
|
|
/* 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;
|
|
}
|
|
hdmi->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)
|
|
hdmi->bus_width |=
|
|
COLOR_DEPTH_10BIT | HDMI_FRL_MODE;
|
|
else
|
|
hdmi->bus_width |= HDMI_FRL_MODE;
|
|
} else {
|
|
if (dm_gpio_is_valid(&hdmi->enable_gpio))
|
|
dm_gpio_set_value(&hdmi->enable_gpio, 1);
|
|
hdmi->bus_width =
|
|
hdmi_get_tmdsclock(hdmi, pixel_clk * 10);
|
|
if (hdmi_bus_fmt_is_yuv420(*bus_format))
|
|
hdmi->bus_width /= 2;
|
|
|
|
if (color_depth == 10 && !hdmi_bus_fmt_is_yuv422(*bus_format))
|
|
hdmi->bus_width |= COLOR_DEPTH_10BIT;
|
|
}
|
|
|
|
rockchip_phy_set_bus_width(conn->phy, hdmi->bus_width);
|
|
}
|
|
|
|
bool dw_hdmi_qp_check_enable_gpio(void *data)
|
|
{
|
|
struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
|
|
|
if (!hdmi->enable_gpio.dev)
|
|
return false;
|
|
else
|
|
return true;
|
|
}
|
|
|
|
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)
|
|
writel(val, hdmi->vo1_grf + RK3588_GRF_VO1_CON4);
|
|
else
|
|
writel(val, hdmi->vo1_grf + RK3588_GRF_VO1_CON7);
|
|
|
|
val = HIWORD_UPDATE(0, RK3588_COMPRESS_MODE_MASK | RK3588_COLOR_FORMAT_MASK);
|
|
if (is_hdmi0)
|
|
writel(val, hdmi->vo1_grf + RK3588_GRF_VO1_CON3);
|
|
else
|
|
writel(val, hdmi->vo1_grf + RK3588_GRF_VO1_CON6);
|
|
|
|
return;
|
|
}
|
|
|
|
val = HIWORD_UPDATE(RK3588_HDMI21_MASK, RK3588_HDMI21_MASK);
|
|
if (is_hdmi0)
|
|
writel(val, hdmi->vo1_grf + RK3588_GRF_VO1_CON4);
|
|
else
|
|
writel(val, hdmi->vo1_grf + RK3588_GRF_VO1_CON7);
|
|
|
|
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)
|
|
writel(val, hdmi->vo1_grf + RK3588_GRF_VO1_CON3);
|
|
else
|
|
writel(val, hdmi->vo1_grf + RK3588_GRF_VO1_CON6);
|
|
} else {
|
|
val = HIWORD_UPDATE(0, RK3588_COMPRESS_MODE_MASK | RK3588_COLOR_FORMAT_MASK);
|
|
if (is_hdmi0)
|
|
writel(val, hdmi->vo1_grf + RK3588_GRF_VO1_CON3);
|
|
else
|
|
writel(val, hdmi->vo1_grf + RK3588_GRF_VO1_CON6);
|
|
}
|
|
}
|
|
|
|
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)
|
|
writel(val, hdmi->vo1_grf + RK3588_GRF_VO1_CON3);
|
|
else
|
|
writel(val, hdmi->vo1_grf + RK3588_GRF_VO1_CON6);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
void dw_hdmi_qp_set_iomux(void *data)
|
|
{
|
|
struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
|
u32 val;
|
|
|
|
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);
|
|
writel(val, hdmi->vo1_grf + RK3588_GRF_VO1_CON3);
|
|
|
|
val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK,
|
|
RK3588_SET_HPD_PATH_MASK);
|
|
writel(val, hdmi->grf + RK3588_GRF_SOC_CON7);
|
|
|
|
val = HIWORD_UPDATE(RK3588_HDMI0_GRANT_SEL,
|
|
RK3588_HDMI0_GRANT_SEL);
|
|
writel(val, hdmi->vo1_grf + RK3588_GRF_VO1_CON9);
|
|
} 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);
|
|
writel(val, hdmi->vo1_grf + RK3588_GRF_VO1_CON6);
|
|
|
|
val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK,
|
|
RK3588_SET_HPD_PATH_MASK);
|
|
writel(val, hdmi->grf + RK3588_GRF_SOC_CON7);
|
|
|
|
val = HIWORD_UPDATE(RK3588_HDMI1_GRANT_SEL,
|
|
RK3588_HDMI1_GRANT_SEL);
|
|
writel(val, hdmi->vo1_grf + RK3588_GRF_VO1_CON9);
|
|
}
|
|
}
|
|
|
|
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 void dw_hdmi_qp_rockchip_phy_disable(struct rockchip_connector *conn, void *data,
|
|
void *state)
|
|
{
|
|
rockchip_phy_power_off(conn->phy);
|
|
}
|
|
|
|
static int dw_hdmi_qp_rockchip_genphy_init(struct rockchip_connector *conn, void *data, void *state)
|
|
{
|
|
struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
|
|
|
dw_hdmi_qp_rockchip_phy_disable(conn, data, state);
|
|
|
|
rockchip_phy_set_bus_width(conn->phy, hdmi->bus_width);
|
|
|
|
return rockchip_phy_power_on(conn->phy);
|
|
}
|
|
|
|
static enum drm_connector_status dw_hdmi_rk3588_read_hpd(void *data)
|
|
{
|
|
u32 val;
|
|
int ret;
|
|
struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
|
|
|
val = readl(hdmi->grf + RK3588_GRF_SOC_STATUS1);
|
|
|
|
if (!hdmi->id) {
|
|
if (val & RK3588_HDMI0_LEVEL_INT)
|
|
ret = connector_status_connected;
|
|
else
|
|
ret = connector_status_disconnected;
|
|
} else {
|
|
if (val & RK3588_HDMI1_LEVEL_INT)
|
|
ret = connector_status_connected;
|
|
else
|
|
ret = connector_status_disconnected;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void dw_hdmi_rk3588_set_pll(struct rockchip_connector *conn, void *data, void *state)
|
|
{
|
|
struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
|
u32 rate = (hdmi->bus_width & DATA_RATE_MASK) * 100;
|
|
|
|
clk_set_rate(&hdmi->link_clk, rate);
|
|
}
|
|
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,
|
|
.set_pll = dw_hdmi_rk3588_set_pll,
|
|
};
|
|
|
|
static const struct rockchip_connector_funcs rockchip_dw_hdmi_qp_funcs = {
|
|
.init = rockchip_dw_hdmi_qp_init,
|
|
.deinit = rockchip_dw_hdmi_qp_deinit,
|
|
.prepare = rockchip_dw_hdmi_qp_prepare,
|
|
.check = rockchip_dw_hdmi_qp_check,
|
|
.enable = rockchip_dw_hdmi_qp_enable,
|
|
.disable = rockchip_dw_hdmi_qp_disable,
|
|
.get_timing = rockchip_dw_hdmi_qp_get_timing,
|
|
.detect = rockchip_dw_hdmi_qp_detect,
|
|
.get_edid = rockchip_dw_hdmi_qp_get_edid,
|
|
};
|
|
|
|
const struct dw_hdmi_plat_data rk3588_hdmi_drv_data = {
|
|
.qp_phy_ops = &rk3588_hdmi_phy_ops,
|
|
.phy_name = "samsung_hdptx_phy",
|
|
.dev_type = RK3588_HDMI,
|
|
};
|
|
|
|
static int rockchip_dw_hdmi_qp_probe(struct udevice *dev)
|
|
{
|
|
int ret;
|
|
struct regmap *map;
|
|
struct rockchip_hdmi *hdmi = dev_get_priv(dev);
|
|
|
|
hdmi->dev = dev;
|
|
|
|
hdmi->id = of_alias_get_id(ofnode_to_np(dev->node), "hdmi");
|
|
if (hdmi->id < 0)
|
|
hdmi->id = 0;
|
|
|
|
hdmi->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
|
|
|
|
if (hdmi->grf <= 0) {
|
|
dev_err(dev, "%s: Get syscon grf failed (ret=%p)\n",
|
|
__func__, hdmi->grf);
|
|
return -ENXIO;
|
|
}
|
|
|
|
map = syscon_regmap_lookup_by_phandle(dev, "rockchip,vo1_grf");
|
|
hdmi->vo1_grf = regmap_get_range(map, 0);
|
|
|
|
if (hdmi->vo1_grf <= 0) {
|
|
dev_err(dev, "%s: Get syscon vo1 grf failed (ret=%p)\n",
|
|
__func__, hdmi->vo1_grf);
|
|
return -ENXIO;
|
|
}
|
|
|
|
ret = gpio_request_by_name(dev, "enable-gpios", 0,
|
|
&hdmi->enable_gpio, GPIOD_IS_OUT);
|
|
if (ret && ret != -ENOENT) {
|
|
dev_err(dev, "Cannot get enable GPIO: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = clk_get_by_name(dev, "link_clk", &hdmi->link_clk);
|
|
if (ret) {
|
|
printf("%s: can't get link_clk\n", __func__);
|
|
return ret;
|
|
}
|
|
|
|
rockchip_connector_bind(&hdmi->connector, dev, hdmi->id, &rockchip_dw_hdmi_qp_funcs,
|
|
NULL, DRM_MODE_CONNECTOR_HDMIA);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct udevice_id rockchip_dw_hdmi_qp_ids[] = {
|
|
{
|
|
.compatible = "rockchip,rk3588-dw-hdmi",
|
|
.data = (ulong)&rk3588_hdmi_drv_data,
|
|
}, {}
|
|
};
|
|
|
|
U_BOOT_DRIVER(rockchip_dw_hdmi_qp) = {
|
|
.name = "rockchip_dw_hdmi_qp",
|
|
.id = UCLASS_DISPLAY,
|
|
.of_match = rockchip_dw_hdmi_qp_ids,
|
|
.probe = rockchip_dw_hdmi_qp_probe,
|
|
.priv_auto_alloc_size = sizeof(struct rockchip_hdmi),
|
|
};
|