From 0c087e39101163055cd680e8d3bc1145d70ea898 Mon Sep 17 00:00:00 2001 From: Algea Cao Date: Tue, 26 Oct 2021 18:00:52 +0800 Subject: [PATCH] drm/rockchip: drv: Parse edid dsc info in rockchip driver To be compatible with GKI, we parse the edid dsc information in rockchup-drm driver. Signed-off-by: Algea Cao Change-Id: I2f2cc9e9fe8578865975e1631450dbbc723ce08e --- drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 224 ++++++++++++++++++++ drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 15 ++ 2 files changed, 239 insertions(+) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index 108229b8f1bf..e5b7460be2b8 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -250,6 +250,80 @@ int rockchip_drm_add_modes_noedid(struct drm_connector *connector) } EXPORT_SYMBOL(rockchip_drm_add_modes_noedid); +static int +cea_db_tag(const u8 *db) +{ + return db[0] >> 5; +} + +static int +cea_db_payload_len(const u8 *db) +{ + return db[0] & 0x1f; +} + +#define for_each_cea_db(cea, i, start, end) \ + for ((i) = (start); \ + (i) < (end) && (i) + cea_db_payload_len(&(cea)[(i)]) < (end); \ + (i) += cea_db_payload_len(&(cea)[(i)]) + 1) + +static bool cea_db_is_hdmi_forum_vsdb(const u8 *db) +{ + unsigned int oui; + + if (cea_db_tag(db) != 0x03) + return false; + + if (cea_db_payload_len(db) < 7) + return false; + + oui = db[3] << 16 | db[2] << 8 | db[1]; + + return oui == HDMI_FORUM_IEEE_OUI; +} + +static int +cea_db_offsets(const u8 *cea, int *start, int *end) +{ + /* DisplayID CTA extension blocks and top-level CEA EDID + * block header definitions differ in the following bytes: + * 1) Byte 2 of the header specifies length differently, + * 2) Byte 3 is only present in the CEA top level block. + * + * The different definitions for byte 2 follow. + * + * DisplayID CTA extension block defines byte 2 as: + * Number of payload bytes + * + * CEA EDID block defines byte 2 as: + * Byte number (decimal) within this block where the 18-byte + * DTDs begin. If no non-DTD data is present in this extension + * block, the value should be set to 04h (the byte after next). + * If set to 00h, there are no DTDs present in this block and + * no non-DTD data. + */ + if (cea[0] == 0x81) { + /* + * for_each_displayid_db() has already verified + * that these stay within expected bounds. + */ + *start = 3; + *end = *start + cea[2]; + } else if (cea[0] == 0x02) { + /* Data block offset in CEA extension block */ + *start = 4; + *end = cea[2]; + if (*end == 0) + *end = 127; + if (*end < 4 || *end > 127) + return -ERANGE; + } else { + return -EOPNOTSUPP; + } + + return 0; +} + static u8 *find_edid_extension(const struct edid *edid, int ext_id, int *ext_index) { @@ -383,6 +457,156 @@ int rockchip_drm_get_yuv422_format(struct drm_connector *connector, } EXPORT_SYMBOL(rockchip_drm_get_yuv422_format); +static +void get_max_frl_rate(int max_frl_rate, u8 *max_lanes, u8 *max_rate_per_lane) +{ + switch (max_frl_rate) { + case 1: + *max_lanes = 3; + *max_rate_per_lane = 3; + break; + case 2: + *max_lanes = 3; + *max_rate_per_lane = 6; + break; + case 3: + *max_lanes = 4; + *max_rate_per_lane = 6; + break; + case 4: + *max_lanes = 4; + *max_rate_per_lane = 8; + break; + case 5: + *max_lanes = 4; + *max_rate_per_lane = 10; + break; + case 6: + *max_lanes = 4; + *max_rate_per_lane = 12; + break; + case 0: + default: + *max_lanes = 0; + *max_rate_per_lane = 0; + } +} + +#define EDID_DSC_10BPC (1 << 0) +#define EDID_DSC_12BPC (1 << 1) +#define EDID_DSC_16BPC (1 << 2) +#define EDID_DSC_ALL_BPP (1 << 3) +#define EDID_DSC_NATIVE_420 (1 << 6) +#define EDID_DSC_1P2 (1 << 7) +#define EDID_DSC_MAX_FRL_RATE_MASK 0xf0 +#define EDID_DSC_MAX_SLICES 0xf +#define EDID_DSC_TOTAL_CHUNK_KBYTES 0x3f +#define EDID_MAX_FRL_RATE_MASK 0xf0 + +static +void parse_edid_forum_vsdb(struct rockchip_drm_dsc_cap *dsc_cap, + u8 *max_frl_rate_per_lane, u8 *max_lanes, + const u8 *hf_vsdb) +{ + u8 max_frl_rate; + u8 dsc_max_frl_rate; + u8 dsc_max_slices; + + if (!hf_vsdb[7]) + return; + + DRM_DEBUG_KMS("hdmi_21 sink detected. parsing edid\n"); + max_frl_rate = (hf_vsdb[7] & EDID_MAX_FRL_RATE_MASK) >> 4; + get_max_frl_rate(max_frl_rate, max_lanes, + max_frl_rate_per_lane); + dsc_cap->v_1p2 = hf_vsdb[11] & EDID_DSC_1P2; + + if (!dsc_cap->v_1p2) + return; + + dsc_cap->native_420 = hf_vsdb[11] & EDID_DSC_NATIVE_420; + dsc_cap->all_bpp = hf_vsdb[11] & EDID_DSC_ALL_BPP; + + if (hf_vsdb[11] & EDID_DSC_16BPC) + dsc_cap->bpc_supported = 16; + else if (hf_vsdb[11] & EDID_DSC_12BPC) + dsc_cap->bpc_supported = 12; + else if (hf_vsdb[11] & EDID_DSC_10BPC) + dsc_cap->bpc_supported = 10; + else + dsc_cap->bpc_supported = 0; + + dsc_max_frl_rate = (hf_vsdb[12] & EDID_DSC_MAX_FRL_RATE_MASK) >> 4; + get_max_frl_rate(dsc_max_frl_rate, &dsc_cap->max_lanes, + &dsc_cap->max_frl_rate_per_lane); + dsc_cap->total_chunk_kbytes = hf_vsdb[13] & EDID_DSC_TOTAL_CHUNK_KBYTES; + + dsc_max_slices = hf_vsdb[12] & EDID_DSC_MAX_SLICES; + switch (dsc_max_slices) { + case 1: + dsc_cap->max_slices = 1; + dsc_cap->clk_per_slice = 340; + break; + case 2: + dsc_cap->max_slices = 2; + dsc_cap->clk_per_slice = 340; + break; + case 3: + dsc_cap->max_slices = 4; + dsc_cap->clk_per_slice = 340; + break; + case 4: + dsc_cap->max_slices = 8; + dsc_cap->clk_per_slice = 340; + break; + case 5: + dsc_cap->max_slices = 8; + dsc_cap->clk_per_slice = 400; + break; + case 6: + dsc_cap->max_slices = 12; + dsc_cap->clk_per_slice = 400; + break; + case 7: + dsc_cap->max_slices = 16; + dsc_cap->clk_per_slice = 400; + break; + case 0: + default: + dsc_cap->max_slices = 0; + dsc_cap->clk_per_slice = 0; + } +} + +int rockchip_drm_parse_cea_ext(struct rockchip_drm_dsc_cap *dsc_cap, + u8 *max_frl_rate_per_lane, u8 *max_lanes, + const struct edid *edid) +{ + const u8 *edid_ext; + int i, start, end; + + if (!dsc_cap || !max_frl_rate_per_lane || !max_lanes || !edid) + return -EINVAL; + + edid_ext = find_cea_extension(edid); + if (!edid_ext) + return -EINVAL; + + if (cea_db_offsets(edid_ext, &start, &end)) + return -EINVAL; + + for_each_cea_db(edid_ext, i, start, end) { + const u8 *db = &edid_ext[i]; + + if (cea_db_is_hdmi_forum_vsdb(db)) + parse_edid_forum_vsdb(dsc_cap, max_frl_rate_per_lane, + max_lanes, db); + } + + return 0; +} +EXPORT_SYMBOL(rockchip_drm_parse_cea_ext); + /* * Attach a (component) device to the shared drm dma mapping from master drm * device. This is used by the VOPs to map GEM buffers to a common DMA diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h index 048bfe2deb9c..5454f845b086 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h @@ -209,6 +209,18 @@ struct loader_cubic_lut { u32 offset; }; +struct rockchip_drm_dsc_cap { + bool v_1p2; + bool native_420; + bool all_bpp; + u8 bpc_supported; + u8 max_slices; + u8 max_lanes; + u8 max_frl_rate_per_lane; + u8 total_chunk_kbytes; + int clk_per_slice; +}; + /* * Rockchip drm private crtc funcs. * @loader_protect: protect loader logo crtc's power @@ -326,6 +338,9 @@ uint32_t rockchip_drm_of_find_possible_crtcs(struct drm_device *dev, uint32_t rockchip_drm_get_bpp(const struct drm_format_info *info); int rockchip_drm_get_yuv422_format(struct drm_connector *connector, struct edid *edid); +int rockchip_drm_parse_cea_ext(struct rockchip_drm_dsc_cap *dsc_cap, + u8 *max_frl_rate_per_lane, u8 *max_lanes, + const struct edid *edid); extern struct platform_driver cdn_dp_driver; extern struct platform_driver dw_hdmi_rockchip_pltfm_driver;