Merge "drm/msm: add RGB 30-bit deep color support for HDMI"

This commit is contained in:
Linux Build Service Account 2017-07-15 13:40:02 -07:00 committed by Gerrit - the friendly Code Review server
commit a019f26af8
6 changed files with 200 additions and 8 deletions

View file

@ -556,13 +556,13 @@ static const struct file_operations sde_hdmi_hdcp_state_fops = {
.read = _sde_hdmi_hdcp_state_read,
};
static u64 _sde_hdmi_clip_valid_pclk(struct drm_display_mode *mode, u64 pclk_in)
static u64 _sde_hdmi_clip_valid_pclk(struct hdmi *hdmi, u64 pclk_in)
{
u32 pclk_delta, pclk;
u64 pclk_clip = pclk_in;
/* as per standard, 0.5% of deviation is allowed */
pclk = mode->clock * HDMI_KHZ_TO_HZ;
pclk = hdmi->pixclock;
pclk_delta = pclk * 5 / 1000;
if (pclk_in < (pclk - pclk_delta))
@ -700,7 +700,6 @@ static void sde_hdmi_tx_hdcp_cb_work(struct work_struct *work)
static int _sde_hdmi_update_pll_delta(struct sde_hdmi *display, s32 ppm)
{
struct hdmi *hdmi = display->ctrl.ctrl;
struct drm_display_mode *current_mode = &display->mode;
u64 cur_pclk, dst_pclk;
u64 clip_pclk;
int rc = 0;
@ -725,7 +724,7 @@ static int _sde_hdmi_update_pll_delta(struct sde_hdmi *display, s32 ppm)
dst_pclk = cur_pclk * (1000000000 + ppm);
do_div(dst_pclk, 1000000000);
clip_pclk = _sde_hdmi_clip_valid_pclk(current_mode, dst_pclk);
clip_pclk = _sde_hdmi_clip_valid_pclk(hdmi, dst_pclk);
/* update pclk */
if (clip_pclk != cur_pclk) {
@ -2126,9 +2125,13 @@ static int sde_hdmi_tx_check_capability(struct sde_hdmi *sde_hdmi)
}
}
SDE_DEBUG("%s: Features <HDMI:%s, HDCP:%s>\n", __func__,
if (sde_hdmi->hdmi_tx_major_version >= HDMI_TX_VERSION_4)
sde_hdmi->dc_feature_supported = true;
SDE_DEBUG("%s: Features <HDMI:%s, HDCP:%s, Deep Color:%s>\n", __func__,
hdmi_disabled ? "OFF" : "ON",
hdcp_disabled ? "OFF" : "ON");
hdcp_disabled ? "OFF" : "ON",
sde_hdmi->dc_feature_supported ? "ON" : "OFF");
if (hdmi_disabled) {
DEV_ERR("%s: HDMI disabled\n", __func__);

View file

@ -33,6 +33,9 @@
#include "sde_hdmi_util.h"
#include "sde_hdcp.h"
#ifndef MIN
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#endif
#ifdef HDMI_DEBUG_ENABLE
#define SDE_HDMI_DEBUG(fmt, args...) SDE_ERROR(fmt, ##args)
#else
@ -110,6 +113,8 @@ enum hdmi_tx_feature_type {
* @client_notify_pending: If there is client notification pending.
* @irq_domain: IRQ domain structure.
* @pll_update_enable: if it's allowed to update HDMI PLL ppm.
* @dc_enable: If deep color is enabled. Only DC_30 so far.
* @dc_feature_supported: If deep color feature is supported.
* @notifier: CEC notifider to convey physical address information.
* @root: Debug fs root entry.
*/
@ -161,6 +166,8 @@ struct sde_hdmi {
struct irq_domain *irq_domain;
struct cec_notifier *notifier;
bool pll_update_enable;
bool dc_enable;
bool dc_feature_supported;
struct delayed_work hdcp_cb_work;
struct dss_io_data io[HDMI_TX_MAX_IO];
@ -188,6 +195,8 @@ enum hdmi_tx_scdc_access_type {
#define HDMI_KHZ_TO_HZ 1000
#define HDMI_MHZ_TO_HZ 1000000
#define HDMI_YUV420_24BPP_PCLK_TMDS_CH_RATE_RATIO 2
#define HDMI_RGB_24BPP_PCLK_TMDS_CH_RATE_RATIO 1
/* Maximum pixel clock rates for hdmi tx */
#define HDMI_DEFAULT_MAX_PCLK_RATE 148500

View file

@ -345,7 +345,9 @@ static int _sde_hdmi_bridge_setup_scrambler(struct hdmi *hdmi,
return 0;
}
if (mode->clock > HDMI_TX_SCRAMBLER_THRESHOLD_RATE_KHZ) {
/* use actual clock instead of mode clock */
if (hdmi->pixclock >
HDMI_TX_SCRAMBLER_THRESHOLD_RATE_KHZ * HDMI_KHZ_TO_HZ) {
scrambler_on = true;
tmds_clock_ratio = 1;
} else {
@ -402,6 +404,38 @@ static int _sde_hdmi_bridge_setup_scrambler(struct hdmi *hdmi,
return rc;
}
static void _sde_hdmi_bridge_setup_deep_color(struct hdmi *hdmi)
{
struct drm_connector *connector = hdmi->connector;
struct sde_connector *c_conn = to_sde_connector(connector);
struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display;
u32 hdmi_ctrl_reg, vbi_pkt_reg;
SDE_DEBUG("Deep Color: %s\n", display->dc_enable ? "On" : "Off");
if (display->dc_enable) {
hdmi_ctrl_reg = hdmi_read(hdmi, REG_HDMI_CTRL);
/* GC CD override */
hdmi_ctrl_reg |= BIT(27);
/* enable deep color for RGB888/YUV444/YUV420 30 bits */
hdmi_ctrl_reg |= BIT(24);
hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl_reg);
/* Enable GC_CONT and GC_SEND in General Control Packet
* (GCP) register so that deep color data is
* transmitted to the sink on every frame, allowing
* the sink to decode the data correctly.
*
* GC_CONT: 0x1 - Send GCP on every frame
* GC_SEND: 0x1 - Enable GCP Transmission
*/
vbi_pkt_reg = hdmi_read(hdmi, REG_HDMI_VBI_PKT_CTRL);
vbi_pkt_reg |= BIT(5) | BIT(4);
hdmi_write(hdmi, REG_HDMI_VBI_PKT_CTRL, vbi_pkt_reg);
}
}
static void _sde_hdmi_bridge_pre_enable(struct drm_bridge *bridge)
{
struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge);
@ -660,18 +694,52 @@ static inline void _sde_hdmi_save_mode(struct hdmi *hdmi,
drm_mode_copy(&display->mode, mode);
}
static u32 _sde_hdmi_choose_best_format(struct hdmi *hdmi,
struct drm_display_mode *mode)
{
/*
* choose priority:
* 1. DC + RGB
* 2. DC + YUV
* 3. RGB
* 4. YUV
*/
int dc_format;
struct drm_connector *connector = hdmi->connector;
dc_format = sde_hdmi_sink_dc_support(connector, mode);
if (dc_format & MSM_MODE_FLAG_RGB444_DC_ENABLE)
return (MSM_MODE_FLAG_COLOR_FORMAT_RGB444
| MSM_MODE_FLAG_RGB444_DC_ENABLE);
return MSM_MODE_FLAG_COLOR_FORMAT_RGB444;
}
static void _sde_hdmi_bridge_mode_set(struct drm_bridge *bridge,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge);
struct hdmi *hdmi = sde_hdmi_bridge->hdmi;
struct drm_connector *connector = hdmi->connector;
struct sde_connector *c_conn = to_sde_connector(connector);
struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display;
int hstart, hend, vstart, vend;
uint32_t frame_ctrl;
mode = adjusted_mode;
hdmi->pixclock = mode->clock * 1000;
display->dc_enable = mode->private_flags &
(MSM_MODE_FLAG_RGB444_DC_ENABLE |
MSM_MODE_FLAG_YUV420_DC_ENABLE);
/* compute pixclock as per color format and bit depth */
hdmi->pixclock = sde_hdmi_calc_pixclk(
mode->clock * HDMI_KHZ_TO_HZ,
mode->private_flags,
display->dc_enable);
SDE_DEBUG("Actual PCLK: %lu, Mode PCLK: %d\n",
hdmi->pixclock, mode->clock);
hstart = mode->htotal - mode->hsync_start;
hend = mode->htotal - mode->hsync_start + mode->hdisplay;
@ -734,6 +802,20 @@ static void _sde_hdmi_bridge_mode_set(struct drm_bridge *bridge,
_sde_hdmi_save_mode(hdmi, mode);
_sde_hdmi_bridge_setup_scrambler(hdmi, mode);
_sde_hdmi_bridge_setup_deep_color(hdmi);
}
static bool _sde_hdmi_bridge_mode_fixup(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge);
struct hdmi *hdmi = sde_hdmi_bridge->hdmi;
adjusted_mode->private_flags |=
_sde_hdmi_choose_best_format(hdmi, adjusted_mode);
return true;
}
static const struct drm_bridge_funcs _sde_hdmi_bridge_funcs = {
@ -742,6 +824,7 @@ static const struct drm_bridge_funcs _sde_hdmi_bridge_funcs = {
.disable = _sde_hdmi_bridge_disable,
.post_disable = _sde_hdmi_bridge_post_disable,
.mode_set = _sde_hdmi_bridge_mode_set,
.mode_fixup = _sde_hdmi_bridge_mode_fixup,
};

View file

@ -825,3 +825,76 @@ int sde_hdmi_hdcp2p2_read_rxstatus(void *hdmi_display)
}
return rc;
}
unsigned long sde_hdmi_calc_pixclk(unsigned long pixel_freq,
u32 out_format, bool dc_enable)
{
u32 rate_ratio = HDMI_RGB_24BPP_PCLK_TMDS_CH_RATE_RATIO;
if (out_format & MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420)
rate_ratio = HDMI_YUV420_24BPP_PCLK_TMDS_CH_RATE_RATIO;
pixel_freq /= rate_ratio;
if (dc_enable)
pixel_freq += pixel_freq >> 2;
return pixel_freq;
}
bool sde_hdmi_validate_pixclk(struct drm_connector *connector,
unsigned long pclk)
{
struct sde_connector *c_conn = to_sde_connector(connector);
struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display;
unsigned long max_pclk = display->max_pclk_khz * HDMI_KHZ_TO_HZ;
if (connector->max_tmds_char)
max_pclk = MIN(max_pclk,
connector->max_tmds_char * HDMI_MHZ_TO_HZ);
else if (connector->max_tmds_clock)
max_pclk = MIN(max_pclk,
connector->max_tmds_clock * HDMI_MHZ_TO_HZ);
SDE_DEBUG("MAX PCLK = %ld, PCLK = %ld\n", max_pclk, pclk);
return pclk < max_pclk;
}
static bool sde_hdmi_check_dc_clock(struct drm_connector *connector,
struct drm_display_mode *mode, u32 format)
{
struct sde_connector *c_conn = to_sde_connector(connector);
struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display;
u32 tmds_clk_with_dc = sde_hdmi_calc_pixclk(
mode->clock * HDMI_KHZ_TO_HZ,
format,
true);
return (display->dc_feature_supported &&
sde_hdmi_validate_pixclk(connector, tmds_clk_with_dc));
}
int sde_hdmi_sink_dc_support(struct drm_connector *connector,
struct drm_display_mode *mode)
{
int dc_format = 0;
if ((mode->flags & DRM_MODE_FLAG_SUPPORTS_YUV) &&
(connector->display_info.edid_hdmi_dc_modes
& DRM_EDID_YCBCR420_DC_30))
if (sde_hdmi_check_dc_clock(connector, mode,
MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420))
dc_format |= MSM_MODE_FLAG_YUV420_DC_ENABLE;
if ((mode->flags & DRM_MODE_FLAG_SUPPORTS_RGB) &&
(connector->display_info.edid_hdmi_dc_modes
& DRM_EDID_HDMI_DC_30))
if (sde_hdmi_check_dc_clock(connector, mode,
MSM_MODE_FLAG_COLOR_FORMAT_RGB444))
dc_format |= MSM_MODE_FLAG_RGB444_DC_ENABLE;
return dc_format;
}

View file

@ -164,4 +164,10 @@ int sde_hdmi_hdcp2p2_read_rxstatus(void *hdmi_display);
void sde_hdmi_ddc_config(void *hdmi_display);
int sde_hdmi_ddc_hdcp2p2_isr(void *hdmi_display);
void sde_hdmi_dump_regs(void *hdmi_display);
unsigned long sde_hdmi_calc_pixclk(unsigned long pixel_freq,
u32 out_format, bool dc_enable);
bool sde_hdmi_validate_pixclk(struct drm_connector *connector,
unsigned long pclk);
int sde_hdmi_sink_dc_support(struct drm_connector *connector,
struct drm_display_mode *mode);
#endif /* _SDE_HDMI_UTIL_H_ */

View file

@ -34,6 +34,24 @@
#define MSM_MODE_FLAG_SEAMLESS_DYNAMIC_FPS (1<<0)
/* Transition to new mode requires a wait-for-vblank before the modeset */
#define MSM_MODE_FLAG_VBLANK_PRE_MODESET (1<<1)
/*
* We need setting some flags in bridge, and using them in encoder. Add them in
* private_flags would be better for use. DRM_MODE_FLAG_SUPPORTS_RGB/YUV are
* flags that indicating the SINK supported color formats read from EDID. While,
* these flags defined here indicate the best color/bit depth foramt we choosed
* that would be better for display. For example the best mode display like:
* RGB+RGB_DC,YUV+YUV_DC, RGB,YUV. And we could not set RGB and YUV format at
* the same time. And also RGB_DC only set when RGB format is set,the same for
* YUV_DC.
*/
/* Enable RGB444 30 bit deep color */
#define MSM_MODE_FLAG_RGB444_DC_ENABLE (1<<2)
/* Enable YUV420 30 bit deep color */
#define MSM_MODE_FLAG_YUV420_DC_ENABLE (1<<3)
/* Choose RGB444 format to display */
#define MSM_MODE_FLAG_COLOR_FORMAT_RGB444 (1<<4)
/* Choose YUV420 format to display */
#define MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420 (1<<5)
/* As there are different display controller blocks depending on the
* snapdragon version, the kms support is split out and the appropriate