From b8aa5377755449b5c114b8ac039295a4b1343dfa Mon Sep 17 00:00:00 2001 From: Aravind Venkateswaran Date: Thu, 22 Dec 2016 17:20:55 -0800 Subject: [PATCH] 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 --- drivers/video/fbdev/msm/mdss_dp.c | 351 ++++++++++++++++++------- drivers/video/fbdev/msm/mdss_dp.h | 236 ++++++++++++++++- drivers/video/fbdev/msm/mdss_dp_aux.c | 254 +++++++++++++++++- drivers/video/fbdev/msm/mdss_dp_util.c | 21 +- drivers/video/fbdev/msm/mdss_dp_util.h | 4 +- 5 files changed, 742 insertions(+), 124 deletions(-) diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c index 2edd8c7dbd68..bae93b03a0fc 100644 --- a/drivers/video/fbdev/msm/mdss_dp.c +++ b/drivers/video/fbdev/msm/mdss_dp.c @@ -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; } diff --git a/drivers/video/fbdev/msm/mdss_dp.h b/drivers/video/fbdev/msm/mdss_dp.h index 29ff8e6d62b1..564f490ba497 100644 --- a/drivers/video/fbdev/msm/mdss_dp.h +++ b/drivers/video/fbdev/msm/mdss_dp.h @@ -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); diff --git a/drivers/video/fbdev/msm/mdss_dp_aux.c b/drivers/video/fbdev/msm/mdss_dp_aux.c index 83ce2384b044..4a3e1e0b259d 100644 --- a/drivers/video/fbdev/msm/mdss_dp_aux.c +++ b/drivers/video/fbdev/msm/mdss_dp_aux.c @@ -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); diff --git a/drivers/video/fbdev/msm/mdss_dp_util.c b/drivers/video/fbdev/msm/mdss_dp_util.c index 46243422e32a..26420836e530 100644 --- a/drivers/video/fbdev/msm/mdss_dp_util.c +++ b/drivers/video/fbdev/msm/mdss_dp_util.c @@ -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, diff --git a/drivers/video/fbdev/msm/mdss_dp_util.h b/drivers/video/fbdev/msm/mdss_dp_util.h index 1434ea3f0163..e08a16a02174 100644 --- a/drivers/video/fbdev/msm/mdss_dp_util.h +++ b/drivers/video/fbdev/msm/mdss_dp_util.h @@ -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);