msm: mdss: dp: add support for automated video pattern tests

Add support for parsing parameters in the automated CTS mode when
the test type is set to TEST_VIDEO_PATTERN. The parameters parsed
are the color bit depth, colorimetry and the test pattern type.
Configure the source accordingly and transmit the requested
video pattern.

CRs-Fixed: 1109812
Change-Id: I552a50e86dc299b4cf7955992ad82dee19f35cbc
Signed-off-by: Aravind Venkateswaran <aravindh@codeaurora.org>
This commit is contained in:
Aravind Venkateswaran 2016-12-22 17:20:55 -08:00
parent 9be29e6aaa
commit b8aa537775
5 changed files with 742 additions and 124 deletions

View file

@ -60,6 +60,48 @@ static inline void mdss_dp_link_retraining(struct mdss_dp_drv_pdata *dp);
static void mdss_dp_handle_attention(struct mdss_dp_drv_pdata *dp_drv);
static void dp_send_events(struct mdss_dp_drv_pdata *dp, u32 events);
static inline void mdss_dp_reset_test_data(struct mdss_dp_drv_pdata *dp)
{
dp->test_data = (const struct dpcd_test_request){ 0 };
dp->test_data.test_bit_depth = DP_TEST_BIT_DEPTH_UNKNOWN;
}
static inline bool mdss_dp_is_link_status_updated(struct mdss_dp_drv_pdata *dp)
{
return dp->link_status.link_status_updated;
}
static inline bool mdss_dp_is_downstream_port_status_changed(
struct mdss_dp_drv_pdata *dp)
{
return dp->link_status.downstream_port_status_changed;
}
static inline bool mdss_dp_is_link_training_requested(
struct mdss_dp_drv_pdata *dp)
{
return (dp->test_data.test_requested == TEST_LINK_TRAINING);
}
static inline bool mdss_dp_is_video_pattern_requested(
struct mdss_dp_drv_pdata *dp)
{
return (dp->test_data.test_requested == TEST_VIDEO_PATTERN);
}
static inline bool mdss_dp_is_phy_test_pattern_requested(
struct mdss_dp_drv_pdata *dp)
{
return (dp->test_data.test_requested == PHY_TEST_PATTERN);
}
static inline bool mdss_dp_soft_hpd_reset(struct mdss_dp_drv_pdata *dp)
{
return (mdss_dp_is_link_training_requested(dp) ||
mdss_dp_is_phy_test_pattern_requested(dp)) &&
dp->alt_mode.dp_status.hpd_irq;
}
static void mdss_dp_put_dt_clk_data(struct device *dev,
struct dss_module_power *module_power)
{
@ -798,9 +840,11 @@ void mdss_dp_config_ctrl(struct mdss_dp_drv_pdata *dp)
{
struct dpcd_cap *cap;
struct display_timing_desc *timing;
struct mdss_panel_info *pinfo;
u32 data = 0;
timing = &dp->edid.timing[0];
pinfo = &dp->panel_data.panel_info;
cap = &dp->dpcd;
@ -823,8 +867,8 @@ void mdss_dp_config_ctrl(struct mdss_dp_drv_pdata *dp)
if (cap->scrambler_reset)
data |= (1 << 10);
if (dp->edid.color_depth != 6)
data |= 0x100; /* Default: 8 bits */
/* Bits per components */
data |= (mdss_dp_bpp_to_test_bit_depth(pinfo->bpp) << 8);
/* Num of Lanes */
data |= ((dp->lane_cnt - 1) << 4);
@ -983,6 +1027,55 @@ end:
return ret;
}
static u32 mdss_dp_get_bpp(struct mdss_dp_drv_pdata *dp)
{
u32 bpp;
u32 bit_depth;
/*
* Set bpp value based on whether a test video pattern is requested.
* For test pattern, the test data has the bit depth per color
* component. Otherwise, set it based on EDID.
*/
if (mdss_dp_is_video_pattern_requested(dp))
bit_depth = dp->test_data.test_bit_depth;
else
bit_depth = dp->edid.color_depth;
if (!mdss_dp_is_test_bit_depth_valid(bit_depth)) {
pr_debug("invalid bit_depth=%d. fall back to default\n",
bit_depth);
bit_depth = DP_TEST_BIT_DEPTH_8; /* default to 24bpp */
}
bpp = mdss_dp_test_bit_depth_to_bpp(bit_depth);
return bpp;
}
static u32 mdss_dp_get_colorimetry_config(struct mdss_dp_drv_pdata *dp)
{
u32 cc;
enum dynamic_range dr;
/* unless a video pattern CTS test is ongoing, use CEA_VESA */
if (mdss_dp_is_video_pattern_requested(dp))
dr = dp->test_data.test_dyn_range;
else
dr = DP_DYNAMIC_RANGE_RGB_VESA;
/* Only RGB_VESA nd RGB_CEA supported for now */
switch (dr) {
case DP_DYNAMIC_RANGE_RGB_CEA:
cc = BIT(3);
break;
case DP_DYNAMIC_RANGE_RGB_VESA:
default:
cc = 0;
}
return cc;
}
static int dp_init_panel_info(struct mdss_dp_drv_pdata *dp_drv, u32 vic)
{
struct mdss_panel_info *pinfo;
@ -1023,7 +1116,6 @@ static int dp_init_panel_info(struct mdss_dp_drv_pdata *dp_drv, u32 vic)
pinfo->type = DP_PANEL;
pinfo->pdest = DISPLAY_4;
pinfo->wait_cycle = 0;
pinfo->bpp = 24;
pinfo->fb_num = 1;
pinfo->lcdc.border_clr = 0; /* blk */
@ -1031,8 +1123,8 @@ static int dp_init_panel_info(struct mdss_dp_drv_pdata *dp_drv, u32 vic)
pinfo->lcdc.hsync_skew = 0;
pinfo->is_pluggable = true;
dp_drv->bpp = pinfo->bpp;
pinfo->bpp = mdss_dp_get_bpp(dp_drv);
pr_debug("bpp=%d\n", pinfo->bpp);
pr_debug("update res. vic= %d, pclk_rate = %llu\n",
dp_drv->vic, pinfo->clk_rate);
@ -1157,6 +1249,9 @@ static void mdss_dp_configure_source_params(struct mdss_dp_drv_pdata *dp,
mdss_dp_fill_link_cfg(dp);
mdss_dp_mainlink_ctrl(&dp->ctrl_io, true);
mdss_dp_config_ctrl(dp);
mdss_dp_config_misc(dp,
mdss_dp_bpp_to_test_bit_depth(mdss_dp_get_bpp(dp)),
mdss_dp_get_colorimetry_config(dp));
mdss_dp_sw_config_msa(&dp->ctrl_io, dp->link_rate, &dp->dp_cc_io);
mdss_dp_timing_cfg(&dp->ctrl_io, &dp->panel_data.panel_info);
}
@ -1347,44 +1442,14 @@ int mdss_dp_on(struct mdss_panel_data *pdata)
dp_drv = container_of(pdata, struct mdss_dp_drv_pdata,
panel_data);
if (dp_drv->power_on) {
pr_debug("Link already setup, return\n");
return 0;
}
return mdss_dp_on_hpd(dp_drv);
}
static inline void mdss_dp_reset_test_data(struct mdss_dp_drv_pdata *dp)
{
dp->test_data = (const struct dpcd_test_request){ 0 };
}
static inline bool mdss_dp_is_link_status_updated(struct mdss_dp_drv_pdata *dp)
{
return dp->link_status.link_status_updated;
}
static inline bool mdss_dp_is_downstream_port_status_changed(
struct mdss_dp_drv_pdata *dp)
{
return dp->link_status.downstream_port_status_changed;
}
static inline bool mdss_dp_is_link_training_requested(
struct mdss_dp_drv_pdata *dp)
{
return (dp->test_data.test_requested == TEST_LINK_TRAINING);
}
static inline bool mdss_dp_is_phy_test_pattern_requested(
struct mdss_dp_drv_pdata *dp)
{
return (dp->test_data.test_requested == PHY_TEST_PATTERN);
}
static inline bool mdss_dp_soft_hpd_reset(struct mdss_dp_drv_pdata *dp)
{
return (mdss_dp_is_link_training_requested(dp) ||
mdss_dp_is_phy_test_pattern_requested(dp)) &&
dp->alt_mode.dp_status.hpd_irq;
}
static int mdss_dp_off_irq(struct mdss_dp_drv_pdata *dp_drv)
{
if (!dp_drv->power_on) {
@ -1504,11 +1569,6 @@ end:
return ret;
}
static int mdss_dp_notify_clients(struct mdss_dp_drv_pdata *dp, bool enable)
{
return mdss_dp_send_cable_notification(dp, enable);
}
static void mdss_dp_set_default_resolution(struct mdss_dp_drv_pdata *dp)
{
hdmi_edid_set_video_resolution(dp->panel_data.panel_info.edid_data,
@ -1617,6 +1677,93 @@ vreg_error:
return ret;
}
/**
* mdss_dp_notify_clients() - notifies DP clients of cable connection
* @dp: Display Port Driver data
* @status: HPD notification status requested
*
* This function will send a notification to display/audio clients of change
* in DP connection status.
*/
static int mdss_dp_notify_clients(struct mdss_dp_drv_pdata *dp,
enum notification_status status)
{
const int irq_comp_timeout = HZ * 2;
int ret = 0;
mutex_lock(&dp->pd_msg_mutex);
if (status == dp->hpd_notification_status) {
pr_debug("No change in status %s --> %s\n",
mdss_dp_notification_status_to_string(status),
mdss_dp_notification_status_to_string(
dp->hpd_notification_status));
goto end;
}
switch (status) {
case NOTIFY_CONNECT_IRQ_HPD:
if (dp->hpd_notification_status != NOTIFY_DISCONNECT_IRQ_HPD)
goto invalid_request;
/* Follow the same programming as for NOTIFY_CONNECT */
mdss_dp_host_init(&dp->panel_data);
mdss_dp_send_cable_notification(dp, true);
break;
case NOTIFY_CONNECT:
if ((dp->hpd_notification_status == NOTIFY_CONNECT_IRQ_HPD) ||
(dp->hpd_notification_status ==
NOTIFY_DISCONNECT_IRQ_HPD))
goto invalid_request;
mdss_dp_host_init(&dp->panel_data);
mdss_dp_send_cable_notification(dp, true);
break;
case NOTIFY_DISCONNECT:
mdss_dp_send_cable_notification(dp, false);
break;
case NOTIFY_DISCONNECT_IRQ_HPD:
if (dp->hpd_notification_status == NOTIFY_DISCONNECT)
goto invalid_request;
mdss_dp_send_cable_notification(dp, false);
if (!IS_ERR_VALUE(ret) && ret) {
reinit_completion(&dp->irq_comp);
ret = wait_for_completion_timeout(&dp->irq_comp,
irq_comp_timeout);
if (ret <= 0) {
pr_warn("irq_comp timed out\n");
ret = -EINVAL;
} else {
ret = 0;
}
}
break;
default:
pr_err("Invalid notification status = %d\n", status);
ret = -EINVAL;
break;
}
goto end;
invalid_request:
pr_err("Invalid request %s --> %s\n",
mdss_dp_notification_status_to_string(
dp->hpd_notification_status),
mdss_dp_notification_status_to_string(status));
ret = -EINVAL;
end:
if (!ret) {
pr_debug("Successfully sent notification %s --> %s\n",
mdss_dp_notification_status_to_string(
dp->hpd_notification_status),
mdss_dp_notification_status_to_string(status));
dp->hpd_notification_status = status;
}
mutex_unlock(&dp->pd_msg_mutex);
return ret;
}
static int mdss_dp_process_hpd_high(struct mdss_dp_drv_pdata *dp)
{
int ret;
@ -1643,7 +1790,7 @@ static int mdss_dp_process_hpd_high(struct mdss_dp_drv_pdata *dp)
dp->sink_info_read = true;
end:
mdss_dp_update_cable_status(dp, true);
mdss_dp_notify_clients(dp, true);
mdss_dp_notify_clients(dp, NOTIFY_CONNECT);
return ret;
@ -1885,7 +2032,7 @@ static ssize_t mdss_dp_sysfs_rda_s3d_mode(struct device *dev,
static bool mdss_dp_is_test_ongoing(struct mdss_dp_drv_pdata *dp)
{
return dp->hpd_irq_clients_notified;
return (dp->hpd_notification_status == NOTIFY_DISCONNECT_IRQ_HPD);
}
/**
@ -1927,7 +2074,7 @@ static int mdss_dp_psm_config(struct mdss_dp_drv_pdata *dp, bool enable)
mdss_dp_mainlink_push_idle(&dp->panel_data);
mdss_dp_off_irq(dp);
} else {
mdss_dp_notify_clients(dp, false);
mdss_dp_notify_clients(dp, NOTIFY_DISCONNECT);
}
} else {
/*
@ -1942,7 +2089,7 @@ static int mdss_dp_psm_config(struct mdss_dp_drv_pdata *dp, bool enable)
mdss_dp_link_retraining(dp);
} else {
mdss_dp_host_init(&dp->panel_data);
mdss_dp_notify_clients(dp, true);
mdss_dp_notify_clients(dp, NOTIFY_CONNECT);
}
}
@ -2032,7 +2179,7 @@ static ssize_t mdss_dp_wta_hpd(struct device *dev,
dp_send_events(dp, EV_USBPD_DISCOVER_MODES);
}
} else if (!dp->hpd && dp->power_on) {
mdss_dp_notify_clients(dp, false);
mdss_dp_notify_clients(dp, NOTIFY_DISCONNECT);
}
end:
return ret;
@ -2577,7 +2724,7 @@ static void usbpd_disconnect_callback(struct usbpd_svid_handler *hdlr)
pr_debug("cable disconnected\n");
mdss_dp_update_cable_status(dp_drv, false);
dp_drv->alt_mode.current_state = UNKNOWN_STATE;
mdss_dp_notify_clients(dp_drv, false);
mdss_dp_notify_clients(dp_drv, NOTIFY_DISCONNECT);
}
static int mdss_dp_validate_callback(u8 cmd,
@ -2633,41 +2780,6 @@ static inline void mdss_dp_send_test_response(struct mdss_dp_drv_pdata *dp)
mutex_unlock(&dp->train_mutex);
}
/**
* mdss_dp_hpd_irq_notify_clients() - notifies DP clients of HPD IRQ tear down
* @dp: Display Port Driver data
*
* This function will send a notification to display/audio clients of DP tear
* down during an HPD IRQ. This happens only if HPD IRQ is toggled,
* in which case the user space proceeds with shutdown of DP driver, including
* mainlink disable, and pushing the controller into idle state.
*/
static int mdss_dp_hpd_irq_notify_clients(struct mdss_dp_drv_pdata *dp)
{
const int irq_comp_timeout = HZ * 2;
int ret = 0;
if (dp->hpd_irq_toggled) {
dp->hpd_irq_clients_notified = true;
ret = mdss_dp_notify_clients(dp, false);
if (!IS_ERR_VALUE(ret) && ret) {
reinit_completion(&dp->irq_comp);
ret = wait_for_completion_timeout(&dp->irq_comp,
irq_comp_timeout);
if (ret <= 0) {
pr_warn("irq_comp timed out\n");
ret = -EINVAL;
} else {
ret = 0;
}
}
}
return 0;
}
/**
* mdss_dp_link_retraining() - initiates link retraining
* @dp: Display Port Driver data
@ -2678,7 +2790,7 @@ static int mdss_dp_hpd_irq_notify_clients(struct mdss_dp_drv_pdata *dp)
*/
static inline void mdss_dp_link_retraining(struct mdss_dp_drv_pdata *dp)
{
if (mdss_dp_hpd_irq_notify_clients(dp))
if (mdss_dp_notify_clients(dp, NOTIFY_DISCONNECT_IRQ_HPD))
return;
mdss_dp_on_irq(dp);
@ -2813,6 +2925,41 @@ static int mdss_dp_process_downstream_port_status_change(
return mdss_dp_edid_read(dp);
}
/**
* mdss_dp_process_video_pattern_request() - process new video pattern request
* @dp: Display Port Driver data
*
* This function will handle a new video pattern request that are initiated by
* the sink. This is acheieved by first sending a disconnect notification to
* the sink followed by a subsequent connect notification to the user modules,
* where it is expected that the user modules would draw the required test
* pattern.
*/
static int mdss_dp_process_video_pattern_request(struct mdss_dp_drv_pdata *dp)
{
if (!mdss_dp_is_video_pattern_requested(dp))
return -EINVAL;
pr_info("%s: bit depth=%d(%d bpp) pattern=%s\n",
mdss_dp_get_test_name(TEST_VIDEO_PATTERN),
dp->test_data.test_bit_depth,
mdss_dp_test_bit_depth_to_bpp(dp->test_data.test_bit_depth),
mdss_dp_test_video_pattern_to_string(
dp->test_data.test_video_pattern));
/* Send a disconnect notification */
mdss_dp_notify_clients(dp, NOTIFY_DISCONNECT_IRQ_HPD);
mdss_dp_off_irq(dp);
/* Send a connect notification */
mdss_dp_notify_clients(dp, NOTIFY_CONNECT_IRQ_HPD);
mdss_dp_send_test_response(dp);
return 0;
}
/**
* mdss_dp_process_hpd_irq_high() - handle HPD IRQ transition to HIGH
* @dp: Display Port Driver data
@ -2827,6 +2974,8 @@ static int mdss_dp_process_hpd_irq_high(struct mdss_dp_drv_pdata *dp)
dp->hpd_irq_on = true;
mdss_dp_reset_test_data(dp);
mdss_dp_aux_parse_sink_status_field(dp);
ret = mdss_dp_process_link_training_request(dp);
@ -2844,10 +2993,14 @@ static int mdss_dp_process_hpd_irq_high(struct mdss_dp_drv_pdata *dp)
ret = mdss_dp_process_phy_test_pattern_request(dp);
if (!ret)
goto exit;
pr_debug("done\n");
exit:
mdss_dp_reset_test_data(dp);
ret = mdss_dp_process_video_pattern_request(dp);
if (!ret)
goto exit;
pr_debug("done\n");
exit:
return ret;
}
@ -2860,17 +3013,21 @@ exit:
*/
static int mdss_dp_process_hpd_irq_low(struct mdss_dp_drv_pdata *dp)
{
if (!dp->hpd_irq_clients_notified)
if (!dp->hpd_irq_on)
return -EINVAL;
pr_debug("enter: HPD IRQ low\n");
dp->hpd_irq_on = false;
dp->hpd_irq_clients_notified = false;
if (dp->hpd_notification_status == NOTIFY_CONNECT_IRQ_HPD) {
mdss_dp_notify_clients(dp, NOTIFY_DISCONNECT);
} else {
/* Clients already notified, so shudown interface directly */
mdss_dp_update_cable_status(dp, false);
mdss_dp_mainlink_push_idle(&dp->panel_data);
mdss_dp_off_hpd(dp);
}
mdss_dp_update_cable_status(dp, false);
mdss_dp_mainlink_push_idle(&dp->panel_data);
mdss_dp_off_hpd(dp);
dp->hpd_irq_on = false;
mdss_dp_reset_test_data(dp);
@ -2977,7 +3134,7 @@ static void mdss_dp_process_attention(struct mdss_dp_drv_pdata *dp_drv)
}
mdss_dp_update_cable_status(dp_drv, false);
mdss_dp_notify_clients(dp_drv, false);
mdss_dp_notify_clients(dp_drv, NOTIFY_DISCONNECT);
pr_debug("Attention: Notified clients\n");
return;
}

View file

@ -269,6 +269,21 @@ struct dpcd_test_request {
u32 test_link_rate;
u32 test_lane_count;
u32 phy_test_pattern_sel;
u32 test_video_pattern;
u32 test_bit_depth;
u32 test_dyn_range;
u32 test_h_total;
u32 test_v_total;
u32 test_h_start;
u32 test_v_start;
u32 test_hsync_pol;
u32 test_hsync_width;
u32 test_vsync_pol;
u32 test_vsync_width;
u32 test_h_width;
u32 test_v_height;
u32 test_rr_d;
u32 test_rr_n;
u32 response;
};
@ -493,11 +508,10 @@ struct mdss_dp_drv_pdata {
char tu_desired;
char valid_boundary;
char delay_start;
u32 bpp;
struct dp_statistic dp_stat;
bool hpd_irq_on;
bool hpd_irq_toggled;
bool hpd_irq_clients_notified;
u32 hpd_notification_status;
struct mdss_dp_event_data dp_event;
struct task_struct *ev_thread;
@ -623,6 +637,7 @@ static inline char *mdss_dp_get_test_response(u32 test_response)
enum test_type {
UNKNOWN_TEST = 0,
TEST_LINK_TRAINING = BIT(0),
TEST_VIDEO_PATTERN = BIT(1),
PHY_TEST_PATTERN = BIT(3),
TEST_EDID_READ = BIT(2),
};
@ -631,6 +646,7 @@ static inline char *mdss_dp_get_test_name(u32 test_requested)
{
switch (test_requested) {
case TEST_LINK_TRAINING: return DP_ENUM_STR(TEST_LINK_TRAINING);
case TEST_VIDEO_PATTERN: return DP_ENUM_STR(TEST_VIDEO_PATTERN);
case PHY_TEST_PATTERN: return DP_ENUM_STR(PHY_TEST_PATTERN);
case TEST_EDID_READ: return DP_ENUM_STR(TEST_EDID_READ);
default: return "unknown";
@ -694,6 +710,222 @@ static inline char *mdss_dp_ev_event_to_string(int event)
}
}
enum dynamic_range {
DP_DYNAMIC_RANGE_RGB_VESA = 0x00,
DP_DYNAMIC_RANGE_RGB_CEA = 0x01,
DP_DYNAMIC_RANGE_UNKNOWN = 0xFFFFFFFF,
};
static inline char *mdss_dp_dynamic_range_to_string(u32 dr)
{
switch (dr) {
case DP_DYNAMIC_RANGE_RGB_VESA:
return DP_ENUM_STR(DP_DYNAMIC_RANGE_RGB_VESA);
case DP_DYNAMIC_RANGE_RGB_CEA:
return DP_ENUM_STR(DP_DYNAMIC_RANGE_RGB_CEA);
case DP_DYNAMIC_RANGE_UNKNOWN:
default:
return "unknown";
}
}
/**
* mdss_dp_is_dynamic_range_valid() - validates the dynamic range
* @bit_depth: the dynamic range value to be checked
*
* Returns true if the dynamic range value is supported.
*/
static inline bool mdss_dp_is_dynamic_range_valid(u32 dr)
{
switch (dr) {
case DP_DYNAMIC_RANGE_RGB_VESA:
case DP_DYNAMIC_RANGE_RGB_CEA:
return true;
default:
return false;
}
}
enum test_bit_depth {
DP_TEST_BIT_DEPTH_6 = 0x00,
DP_TEST_BIT_DEPTH_8 = 0x01,
DP_TEST_BIT_DEPTH_10 = 0x02,
DP_TEST_BIT_DEPTH_UNKNOWN = 0xFFFFFFFF,
};
static inline char *mdss_dp_test_bit_depth_to_string(u32 tbd)
{
switch (tbd) {
case DP_TEST_BIT_DEPTH_6:
return DP_ENUM_STR(DP_TEST_BIT_DEPTH_6);
case DP_TEST_BIT_DEPTH_8:
return DP_ENUM_STR(DP_TEST_BIT_DEPTH_8);
case DP_TEST_BIT_DEPTH_10:
return DP_ENUM_STR(DP_TEST_BIT_DEPTH_10);
case DP_TEST_BIT_DEPTH_UNKNOWN:
default:
return "unknown";
}
}
/**
* mdss_dp_is_test_bit_depth_valid() - validates the bit depth requested
* @bit_depth: bit depth requested by the sink
*
* Returns true if the requested bit depth is supported.
*/
static inline bool mdss_dp_is_test_bit_depth_valid(u32 tbd)
{
/* DP_TEST_VIDEO_PATTERN_NONE is treated as invalid */
switch (tbd) {
case DP_TEST_BIT_DEPTH_6:
case DP_TEST_BIT_DEPTH_8:
case DP_TEST_BIT_DEPTH_10:
return true;
default:
return false;
}
}
/**
* mdss_dp_test_bit_depth_to_bpp() - convert test bit depth to bpp
* @tbd: test bit depth
*
* Returns the bits per pixel (bpp) to be used corresponding to the
* git bit depth value. This function assumes that bit depth has
* already been validated.
*/
static inline u32 mdss_dp_test_bit_depth_to_bpp(enum test_bit_depth tbd)
{
u32 bpp;
/*
* Few simplistic rules and assumptions made here:
* 1. Bit depth is per color component
* 2. If bit depth is unknown return 0
* 3. Assume 3 color components
*/
switch (tbd) {
case DP_TEST_BIT_DEPTH_6:
bpp = 18;
break;
case DP_TEST_BIT_DEPTH_8:
bpp = 24;
break;
case DP_TEST_BIT_DEPTH_10:
bpp = 30;
break;
case DP_TEST_BIT_DEPTH_UNKNOWN:
default:
bpp = 0;
}
return bpp;
}
/**
* mdss_dp_bpp_to_test_bit_depth() - convert bpp to test bit depth
* &bpp: the bpp to be converted
*
* Return the bit depth per color component to used with the video
* test pattern data based on the bits per pixel value.
*/
static inline u32 mdss_dp_bpp_to_test_bit_depth(u32 bpp)
{
enum test_bit_depth tbd;
/*
* Few simplistic rules and assumptions made here:
* 1. Test bit depth is bit depth per color component
* 2. Assume 3 color components
*/
switch (bpp) {
case 18:
tbd = DP_TEST_BIT_DEPTH_6;
break;
case 24:
tbd = DP_TEST_BIT_DEPTH_8;
break;
case 30:
tbd = DP_TEST_BIT_DEPTH_10;
break;
default:
tbd = DP_TEST_BIT_DEPTH_UNKNOWN;
break;
}
return tbd;
}
enum test_video_pattern {
DP_TEST_VIDEO_PATTERN_NONE = 0x00,
DP_TEST_VIDEO_PATTERN_COLOR_RAMPS = 0x01,
DP_TEST_VIDEO_PATTERN_BW_VERT_LINES = 0x02,
DP_TEST_VIDEO_PATTERN_COLOR_SQUARE = 0x03,
};
static inline char *mdss_dp_test_video_pattern_to_string(u32 test_video_pattern)
{
switch (test_video_pattern) {
case DP_TEST_VIDEO_PATTERN_NONE:
return DP_ENUM_STR(DP_TEST_VIDEO_PATTERN_NONE);
case DP_TEST_VIDEO_PATTERN_COLOR_RAMPS:
return DP_ENUM_STR(DP_TEST_VIDEO_PATTERN_COLOR_RAMPS);
case DP_TEST_VIDEO_PATTERN_BW_VERT_LINES:
return DP_ENUM_STR(DP_TEST_VIDEO_PATTERN_BW_VERT_LINES);
case DP_TEST_VIDEO_PATTERN_COLOR_SQUARE:
return DP_ENUM_STR(DP_TEST_VIDEO_PATTERN_COLOR_SQUARE);
default:
return "unknown";
}
}
/**
* mdss_dp_is_test_video_pattern_valid() - validates the video pattern
* @pattern: video pattern requested by the sink
*
* Returns true if the requested video pattern is supported.
*/
static inline bool mdss_dp_is_test_video_pattern_valid(u32 pattern)
{
switch (pattern) {
case DP_TEST_VIDEO_PATTERN_NONE:
case DP_TEST_VIDEO_PATTERN_COLOR_RAMPS:
case DP_TEST_VIDEO_PATTERN_BW_VERT_LINES:
case DP_TEST_VIDEO_PATTERN_COLOR_SQUARE:
return true;
default:
return false;
}
}
enum notification_status {
NOTIFY_UNKNOWN,
NOTIFY_CONNECT,
NOTIFY_DISCONNECT,
NOTIFY_CONNECT_IRQ_HPD,
NOTIFY_DISCONNECT_IRQ_HPD,
};
static inline char const *mdss_dp_notification_status_to_string(
enum notification_status status)
{
switch (status) {
case NOTIFY_UNKNOWN:
return DP_ENUM_STR(NOTIFY_UNKNOWN);
case NOTIFY_CONNECT:
return DP_ENUM_STR(NOTIFY_CONNECT);
case NOTIFY_DISCONNECT:
return DP_ENUM_STR(NOTIFY_DISCONNECT);
case NOTIFY_CONNECT_IRQ_HPD:
return DP_ENUM_STR(NOTIFY_CONNECT_IRQ_HPD);
case NOTIFY_DISCONNECT_IRQ_HPD:
return DP_ENUM_STR(NOTIFY_DISCONNECT_IRQ_HPD);
default:
return "unknown";
}
}
void mdss_dp_phy_initialize(struct mdss_dp_drv_pdata *dp);
void mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *dp);

View file

@ -466,10 +466,8 @@ void dp_extract_edid_video_support(struct edp_edid *edid, char *buf)
edid->video_intf = *bp & 0x0f;
/* 6, 8, 10, 12, 14 and 16 bit per component */
edid->color_depth = ((*bp & 0x70) >> 4); /* color bit depth */
if (edid->color_depth) {
edid->color_depth *= 2;
edid->color_depth += 4;
}
/* decrement to match with the test_bit_depth enum definition */
edid->color_depth--;
pr_debug("Digital Video intf=%d color_depth=%d\n",
edid->video_intf, edid->color_depth);
} else {
@ -1258,6 +1256,243 @@ end:
return ret;
}
static int dp_parse_test_timing_params1(struct mdss_dp_drv_pdata *ep,
int const addr, int const len, u32 *val)
{
char *bp;
struct edp_buf *rp;
int rlen;
if (len < 2)
return -EINVAL;
/* Read the requested video test pattern (Byte 0x221). */
rlen = dp_aux_read_buf(ep, addr, len, 0);
if (rlen < len) {
pr_err("failed to read 0x%x\n", addr);
return -EINVAL;
}
rp = &ep->rxp;
bp = rp->data;
*val = bp[1] | (bp[0] << 8);
return 0;
}
static int dp_parse_test_timing_params2(struct mdss_dp_drv_pdata *ep,
int const addr, int const len, u32 *val1, u32 *val2)
{
char *bp;
struct edp_buf *rp;
int rlen;
if (len < 2)
return -EINVAL;
/* Read the requested video test pattern (Byte 0x221). */
rlen = dp_aux_read_buf(ep, addr, len, 0);
if (rlen < len) {
pr_err("failed to read 0x%x\n", addr);
return -EINVAL;
}
rp = &ep->rxp;
bp = rp->data;
*val1 = (bp[0] & BIT(7)) >> 7;
*val2 = bp[1] | ((bp[0] & 0x7F) << 8);
return 0;
}
static int dp_parse_test_timing_params3(struct mdss_dp_drv_pdata *ep,
int const addr, u32 *val)
{
char *bp;
struct edp_buf *rp;
int rlen;
/* Read the requested video test pattern (Byte 0x221). */
rlen = dp_aux_read_buf(ep, addr, 1, 0);
if (rlen < 1) {
pr_err("failed to read 0x%x\n", addr);
return -EINVAL;
}
rp = &ep->rxp;
bp = rp->data;
*val = bp[0];
return 0;
}
/**
* dp_parse_video_pattern_params() - parses video pattern parameters from DPCD
* @ep: Display Port Driver data
*
* Returns 0 if it successfully parses the video test pattern and the test
* bit depth requested by the sink and, and if the values parsed are valid.
*/
static int dp_parse_video_pattern_params(struct mdss_dp_drv_pdata *ep)
{
int ret = 0;
char *bp;
char data;
struct edp_buf *rp;
int rlen;
u32 dyn_range;
int const test_parameter_len = 0x1;
int const test_video_pattern_addr = 0x221;
int const test_misc_addr = 0x232;
/* Read the requested video test pattern (Byte 0x221). */
rlen = dp_aux_read_buf(ep, test_video_pattern_addr,
test_parameter_len, 0);
if (rlen < test_parameter_len) {
pr_err("failed to read test video pattern\n");
ret = -EINVAL;
goto exit;
}
rp = &ep->rxp;
bp = rp->data;
data = *bp++;
if (!mdss_dp_is_test_video_pattern_valid(data)) {
pr_err("invalid test video pattern = 0x%x\n", data);
ret = -EINVAL;
goto exit;
}
ep->test_data.test_video_pattern = data;
pr_debug("test video pattern = 0x%x (%s)\n",
ep->test_data.test_video_pattern,
mdss_dp_test_video_pattern_to_string(
ep->test_data.test_video_pattern));
/* Read the requested color bit depth and dynamic range (Byte 0x232) */
rlen = dp_aux_read_buf(ep, test_misc_addr, test_parameter_len, 0);
if (rlen < test_parameter_len) {
pr_err("failed to read test bit depth\n");
ret = -EINVAL;
goto exit;
}
rp = &ep->rxp;
bp = rp->data;
data = *bp++;
/* Dynamic Range */
dyn_range = (data & BIT(3)) >> 3;
if (!mdss_dp_is_dynamic_range_valid(dyn_range)) {
pr_err("invalid test dynamic range = 0x%x", dyn_range);
ret = -EINVAL;
goto exit;
}
ep->test_data.test_dyn_range = dyn_range;
pr_debug("test dynamic range = 0x%x (%s)\n",
ep->test_data.test_dyn_range,
mdss_dp_dynamic_range_to_string(ep->test_data.test_dyn_range));
/* Color bit depth */
data &= (BIT(5) | BIT(6) | BIT(7));
data >>= 5;
if (!mdss_dp_is_test_bit_depth_valid(data)) {
pr_err("invalid test bit depth = 0x%x\n", data);
ret = -EINVAL;
goto exit;
}
ep->test_data.test_bit_depth = data;
pr_debug("test bit depth = 0x%x (%s)\n",
ep->test_data.test_bit_depth,
mdss_dp_test_bit_depth_to_string(ep->test_data.test_bit_depth));
/* resolution timing params */
ret = dp_parse_test_timing_params1(ep, 0x222, 2,
&ep->test_data.test_h_total);
if (ret) {
pr_err("failed to parse test_h_total (0x222)\n");
goto exit;
}
pr_debug("TEST_H_TOTAL = %d\n", ep->test_data.test_h_total);
ret = dp_parse_test_timing_params1(ep, 0x224, 2,
&ep->test_data.test_v_total);
if (ret) {
pr_err("failed to parse test_v_total (0x224)\n");
goto exit;
}
pr_debug("TEST_V_TOTAL = %d\n", ep->test_data.test_v_total);
ret = dp_parse_test_timing_params1(ep, 0x226, 2,
&ep->test_data.test_h_start);
if (ret) {
pr_err("failed to parse test_h_start (0x226)\n");
goto exit;
}
pr_debug("TEST_H_START = %d\n", ep->test_data.test_h_start);
ret = dp_parse_test_timing_params1(ep, 0x228, 2,
&ep->test_data.test_v_start);
if (ret) {
pr_err("failed to parse test_v_start (0x228)\n");
goto exit;
}
pr_debug("TEST_V_START = %d\n", ep->test_data.test_v_start);
ret = dp_parse_test_timing_params2(ep, 0x22A, 2,
&ep->test_data.test_hsync_pol,
&ep->test_data.test_hsync_width);
if (ret) {
pr_err("failed to parse (0x22A)\n");
goto exit;
}
pr_debug("TEST_HSYNC_POL = %d\n", ep->test_data.test_hsync_pol);
pr_debug("TEST_HSYNC_WIDTH = %d\n", ep->test_data.test_hsync_width);
ret = dp_parse_test_timing_params2(ep, 0x22C, 2,
&ep->test_data.test_vsync_pol,
&ep->test_data.test_vsync_width);
if (ret) {
pr_err("failed to parse (0x22C)\n");
goto exit;
}
pr_debug("TEST_VSYNC_POL = %d\n", ep->test_data.test_vsync_pol);
pr_debug("TEST_VSYNC_WIDTH = %d\n", ep->test_data.test_vsync_width);
ret = dp_parse_test_timing_params1(ep, 0x22E, 2,
&ep->test_data.test_h_width);
if (ret) {
pr_err("failed to parse test_h_width (0x22E)\n");
goto exit;
}
pr_debug("TEST_H_WIDTH = %d\n", ep->test_data.test_h_width);
ret = dp_parse_test_timing_params1(ep, 0x230, 2,
&ep->test_data.test_v_height);
if (ret) {
pr_err("failed to parse test_v_height (0x230)\n");
goto exit;
}
pr_debug("TEST_V_HEIGHT = %d\n", ep->test_data.test_v_height);
ret = dp_parse_test_timing_params3(ep, 0x233, &ep->test_data.test_rr_d);
ep->test_data.test_rr_d &= BIT(0);
if (ret) {
pr_err("failed to parse test_rr_d (0x233)\n");
goto exit;
}
pr_debug("TEST_REFRESH_DENOMINATOR = %d\n", ep->test_data.test_rr_d);
ret = dp_parse_test_timing_params3(ep, 0x234, &ep->test_data.test_rr_n);
if (ret) {
pr_err("failed to parse test_rr_n (0x234)\n");
goto exit;
}
pr_debug("TEST_REFRESH_NUMERATOR = %d\n", ep->test_data.test_rr_n);
exit:
return ret;
}
/**
* dp_is_test_supported() - checks if test requested by sink is supported
@ -1268,6 +1503,7 @@ end:
static bool dp_is_test_supported(u32 test_requested)
{
return (test_requested == TEST_LINK_TRAINING) ||
(test_requested == TEST_VIDEO_PATTERN) ||
(test_requested == TEST_EDID_READ) ||
(test_requested == PHY_TEST_PATTERN);
}
@ -1289,6 +1525,7 @@ static void dp_sink_parse_test_request(struct mdss_dp_drv_pdata *ep)
int const test_parameter_len = 0x1;
int const device_service_irq_addr = 0x201;
int const test_request_addr = 0x218;
char buf[4];
/**
* Read the device service IRQ vector (Byte 0x201) to determine
@ -1341,12 +1578,19 @@ static void dp_sink_parse_test_request(struct mdss_dp_drv_pdata *ep)
case TEST_LINK_TRAINING:
ret = dp_parse_link_training_params(ep);
break;
case TEST_VIDEO_PATTERN:
ret = dp_parse_video_pattern_params(ep);
break;
default:
pr_debug("test 0x%x not supported\n",
ep->test_data.test_requested);
return;
}
/* clear the test request IRQ */
buf[0] = 1;
dp_aux_write_buf(ep, test_request_addr, buf, 1, 0);
/**
* Send a TEST_ACK if all test parameters are valid, otherwise send
* a TEST_NACK.
@ -1833,8 +2077,6 @@ int mdss_dp_link_train(struct mdss_dp_drv_pdata *dp)
clear:
dp_clear_training_pattern(dp);
if (ret != -EINVAL) {
mdss_dp_config_misc_settings(&dp->ctrl_io,
&dp->panel_data.panel_info);
mdss_dp_setup_tr_unit(&dp->ctrl_io, dp->link_rate,
dp->lane_cnt, dp->vic,
&dp->panel_data.panel_info);

View file

@ -767,28 +767,15 @@ void mdss_dp_sw_config_msa(struct dss_io_data *ctrl_io,
writel_relaxed(nvid, ctrl_io->base + DP_SOFTWARE_NVID);
}
void mdss_dp_config_misc_settings(struct dss_io_data *ctrl_io,
struct mdss_panel_info *pinfo)
void mdss_dp_config_misc(struct mdss_dp_drv_pdata *dp, u32 bd, u32 cc)
{
u32 bpp = pinfo->bpp;
u32 misc_val = 0x0;
switch (bpp) {
case 18:
misc_val |= (0x0 << 5);
break;
case 30:
misc_val |= (0x2 << 5);
break;
case 24:
default:
misc_val |= (0x1 << 5);
}
u32 misc_val = cc;
misc_val |= (bd << 5);
misc_val |= BIT(0); /* Configure clock to synchronous mode */
pr_debug("Misc settings = 0x%x\n", misc_val);
writel_relaxed(misc_val, ctrl_io->base + DP_MISC1_MISC0);
writel_relaxed(misc_val, dp->ctrl_io.base + DP_MISC1_MISC0);
}
void mdss_dp_setup_tr_unit(struct dss_io_data *ctrl_io, u8 link_rate,

View file

@ -65,6 +65,7 @@
#define DP_TEST_80BIT_CUSTOM_PATTERN_REG1 (0x000004C4)
#define DP_TEST_80BIT_CUSTOM_PATTERN_REG2 (0x000004C8)
#define MMSS_DP_MISC1_MISC0 (0x0000042C)
#define MMSS_DP_AUDIO_TIMING_GEN (0x00000480)
#define MMSS_DP_AUDIO_TIMING_RBR_32 (0x00000484)
#define MMSS_DP_AUDIO_TIMING_HBR_32 (0x00000488)
@ -285,8 +286,7 @@ void mdss_dp_switch_usb3_phy_to_dp_mode(struct dss_io_data *tcsr_reg_io);
void mdss_dp_assert_phy_reset(struct dss_io_data *ctrl_io, bool assert);
void mdss_dp_setup_tr_unit(struct dss_io_data *ctrl_io, u8 link_rate,
u8 ln_cnt, u32 res, struct mdss_panel_info *pinfo);
void mdss_dp_config_misc_settings(struct dss_io_data *ctrl_io,
struct mdss_panel_info *pinfo);
void mdss_dp_config_misc(struct mdss_dp_drv_pdata *dp, u32 bd, u32 cc);
void mdss_dp_phy_aux_setup(struct dss_io_data *phy_io);
void mdss_dp_hpd_configure(struct dss_io_data *ctrl_io, bool enable);
void mdss_dp_aux_ctrl(struct dss_io_data *ctrl_io, bool enable);