Merge "drm/msm: add RGB 30-bit deep color support for HDMI"
This commit is contained in:
commit
a019f26af8
6 changed files with 200 additions and 8 deletions
drivers/gpu/drm/msm
|
@ -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__);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue