Loading Documentation/devicetree/bindings/fb/mdss-mdp.txt +2 −0 Original line number Diff line number Diff line Loading @@ -566,6 +566,8 @@ Subnode properties: Example: Width = 1920, Height = 1080, BytesPerPixel = 4, Number of frame-buffers reserved = 2. Size = 1920*1080*4*2 = ROUND_1MB(15.8MB) = 16MB. - qcom,mdss-intf: Phandle to the kernel driver module that is mapped to the frame buffer virtual device. - qcom,mdss-fb-splash-logo-enabled: The boolean entry enables the framebuffer driver to display the splash logo image. It is independent of continuous splash Loading arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi +2 −0 Original line number Diff line number Diff line Loading @@ -268,11 +268,13 @@ mdss_fb2: qcom,mdss_fb_hdmi { cell-index = <2>; compatible = "qcom,mdss-fb"; qcom,mdss-intf = <&mdss_hdmi_tx>; }; mdss_fb3: qcom,mdss_fb_dp { cell-index = <3>; compatible = "qcom,mdss-fb"; qcom,mdss-intf = <&mdss_dp_ctrl>; }; }; Loading drivers/video/fbdev/msm/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -46,6 +46,7 @@ obj-$(CONFIG_FB_MSM_MDSS) += mdss_dba_utils.o obj-$(CONFIG_FB_MSM_MDSS) += mdss_hdcp_1x.o obj-$(CONFIG_FB_MSM_MDSS_DP_PANEL) += mdss_dp.o mdss_dp_util.o obj-$(CONFIG_FB_MSM_MDSS_DP_PANEL) += mdss_dp_aux.o obj-$(CONFIG_FB_MSM_MDSS_DP_PANEL) += mdss_dp_hdcp2p2.o obj-$(CONFIG_FB_MSM_MDSS) += mdss_io_util.o obj-$(CONFIG_FB_MSM_MDSS) += msm_ext_display.o Loading drivers/video/fbdev/msm/mdss_dp.c +557 −144 Original line number Diff line number Diff line Loading @@ -36,7 +36,7 @@ #include "mdss_dp_util.h" #include "mdss_hdmi_panel.h" #include <linux/hdcp_qseecom.h> #include "mdss_hdcp_1x.h" #include "mdss_hdcp.h" #include "mdss_debug.h" #define RGB_COMPONENTS 3 Loading Loading @@ -857,6 +857,18 @@ int mdss_dp_wait4train(struct mdss_dp_drv_pdata *dp_drv) return ret; } static void mdss_dp_update_cable_status(struct mdss_dp_drv_pdata *dp, bool connected) { mutex_lock(&dp->pd_msg_mutex); pr_debug("cable_connected to %d\n", connected); if (dp->cable_connected != connected) dp->cable_connected = connected; else pr_debug("no change in cable status\n"); mutex_unlock(&dp->pd_msg_mutex); } static int dp_get_cable_status(struct platform_device *pdev, u32 vote) { struct mdss_dp_drv_pdata *dp_ctrl = platform_get_drvdata(pdev); Loading Loading @@ -1038,32 +1050,186 @@ static inline void mdss_dp_set_audio_switch_node( val); } int mdss_dp_on(struct mdss_panel_data *pdata) /** * mdss_dp_get_lane_mapping() - returns lane mapping based on given orientation * @orientation: usb plug orientation * @lane_map: the configured lane mapping * * Returns 0 when the lane mapping is successfully determined based on the * given usb plug orientation. */ static int mdss_dp_get_lane_mapping(struct mdss_dp_drv_pdata *dp, enum plug_orientation orientation, struct lane_mapping *lane_map) { int ret = 0; pr_debug("enter: orientation = %d\n", orientation); if (!lane_map) { pr_err("invalid lane map input"); ret = -EINVAL; goto exit; } /* Set the default lane mapping */ lane_map->lane0 = 2; lane_map->lane1 = 3; lane_map->lane2 = 1; lane_map->lane3 = 0; if (orientation == ORIENTATION_CC2) { lane_map->lane0 = 1; lane_map->lane1 = 0; lane_map->lane2 = 2; lane_map->lane3 = 3; if (gpio_is_valid(dp->usbplug_cc_gpio)) { gpio_set_value(dp->usbplug_cc_gpio, 1); pr_debug("Configured cc gpio for new Orientation\n"); } } pr_debug("lane0 = %d, lane1 = %d, lane2 =%d, lane3 =%d\n", lane_map->lane0, lane_map->lane1, lane_map->lane2, lane_map->lane3); exit: return ret; } /** * mdss_dp_enable_mainlink_clocks() - enables Display Port main link clocks * @dp: Display Port Driver data * * Returns 0 when the main link clocks are successfully enabled. */ static int mdss_dp_enable_mainlink_clocks(struct mdss_dp_drv_pdata *dp) { int ret = 0; dp->power_data[DP_CTRL_PM].clk_config[0].rate = ((dp->link_rate * DP_LINK_RATE_MULTIPLIER) / 1000);/* KHz */ dp->pixel_rate = dp->panel_data.panel_info.clk_rate; dp->power_data[DP_CTRL_PM].clk_config[3].rate = (dp->pixel_rate / 1000);/* KHz */ ret = mdss_dp_clk_ctrl(dp, DP_CTRL_PM, true); if (ret) { pr_err("Unabled to start link clocks\n"); ret = -EINVAL; } return ret; } /** * mdss_dp_disable_mainlink_clocks() - disables Display Port main link clocks * @dp: Display Port Driver data */ static void mdss_dp_disable_mainlink_clocks(struct mdss_dp_drv_pdata *dp_drv) { mdss_dp_clk_ctrl(dp_drv, DP_CTRL_PM, false); } /** * mdss_dp_configure_source_params() - configures DP transmitter source params * @dp: Display Port Driver data * @lane_map: usb port lane mapping * * Configures the DP transmitter source params including details such as lane * configuration, output format and sink/panel timing information. */ static void mdss_dp_configure_source_params(struct mdss_dp_drv_pdata *dp, struct lane_mapping *lane_map) { mdss_dp_ctrl_lane_mapping(&dp->ctrl_io, *lane_map); mdss_dp_fill_link_cfg(dp); mdss_dp_mainlink_ctrl(&dp->ctrl_io, true); mdss_dp_config_ctrl(dp); mdss_dp_sw_mvid_nvid(&dp->ctrl_io); mdss_dp_timing_cfg(&dp->ctrl_io, &dp->panel_data.panel_info); } /** * mdss_dp_train_main_link() - initiates training of DP main link * @dp: Display Port Driver data * * Initiates training of the DP main link and checks the state of the main * link after the training is complete. */ static void mdss_dp_train_main_link(struct mdss_dp_drv_pdata *dp) { int ready = 0; pr_debug("enter\n"); mdss_dp_link_train(dp); mdss_dp_wait4train(dp); ready = mdss_dp_mainlink_ready(dp, BIT(0)); pr_debug("main link %s\n", ready ? "READY" : "NOT READY"); } static int mdss_dp_on_irq(struct mdss_dp_drv_pdata *dp_drv) { struct mdss_dp_drv_pdata *dp_drv = NULL; int ret = 0; enum plug_orientation orientation = ORIENTATION_NONE; struct lane_mapping ln_map; if (!pdata) { pr_err("Invalid input data\n"); return -EINVAL; /* wait until link training is completed */ mutex_lock(&dp_drv->train_mutex); pr_debug("enter\n"); orientation = usbpd_get_plug_orientation(dp_drv->pd); pr_debug("plug orientation = %d\n", orientation); ret = mdss_dp_get_lane_mapping(dp_drv, orientation, &ln_map); if (ret) goto exit; mdss_dp_phy_share_lane_config(&dp_drv->phy_io, orientation, dp_drv->dpcd.max_lane_count); ret = mdss_dp_enable_mainlink_clocks(dp_drv); if (ret) goto exit; mdss_dp_mainlink_reset(&dp_drv->ctrl_io); reinit_completion(&dp_drv->idle_comp); mdss_dp_configure_source_params(dp_drv, &ln_map); mdss_dp_train_main_link(dp_drv); dp_drv->power_on = true; pr_debug("end\n"); exit: mutex_unlock(&dp_drv->train_mutex); return ret; } dp_drv = container_of(pdata, struct mdss_dp_drv_pdata, panel_data); int mdss_dp_on_hpd(struct mdss_dp_drv_pdata *dp_drv) { int ret = 0; enum plug_orientation orientation = ORIENTATION_NONE; struct lane_mapping ln_map; /* wait until link training is completed */ mutex_lock(&dp_drv->train_mutex); pr_debug("Enter++ cont_splash=%d\n", dp_drv->cont_splash); /* Default lane mapping */ ln_map.lane0 = 2; ln_map.lane1 = 3; ln_map.lane2 = 1; ln_map.lane3 = 0; if (!dp_drv->cont_splash) { /* vote for clocks */ if (dp_drv->cont_splash) { mdss_dp_aux_ctrl(&dp_drv->ctrl_io, true); goto link_training; } ret = mdss_dp_clk_ctrl(dp_drv, DP_CORE_PM, true); if (ret) { pr_err("Unabled to start core clocks\n"); Loading @@ -1074,20 +1240,9 @@ int mdss_dp_on(struct mdss_panel_data *pdata) orientation = usbpd_get_plug_orientation(dp_drv->pd); pr_debug("plug Orientation = %d\n", orientation); if (orientation == ORIENTATION_CC2) { /* update lane mapping */ ln_map.lane0 = 1; ln_map.lane1 = 0; ln_map.lane2 = 2; ln_map.lane3 = 3; if (gpio_is_valid(dp_drv->usbplug_cc_gpio)) { gpio_set_value( dp_drv->usbplug_cc_gpio, 1); pr_debug("Configured cc gpio for new Orientation\n"); } } ret = mdss_dp_get_lane_mapping(dp_drv, orientation, &ln_map); if (ret) goto exit; if (dp_drv->new_vic && (dp_drv->new_vic != dp_drv->vic)) dp_init_panel_info(dp_drv, dp_drv->new_vic); Loading @@ -1109,45 +1264,21 @@ int mdss_dp_on(struct mdss_panel_data *pdata) pr_debug("link_rate = 0x%x\n", dp_drv->link_rate); dp_drv->power_data[DP_CTRL_PM].clk_config[0].rate = ((dp_drv->link_rate * DP_LINK_RATE_MULTIPLIER) / 1000); /* KHz */ dp_drv->pixel_rate = dp_drv->panel_data.panel_info.clk_rate; dp_drv->power_data[DP_CTRL_PM].clk_config[3].rate = (dp_drv->pixel_rate / 1000); /* KHz */ ret = mdss_dp_clk_ctrl(dp_drv, DP_CTRL_PM, true); if (ret) { pr_err("Unabled to start link clocks\n"); ret = mdss_dp_enable_mainlink_clocks(dp_drv); if (ret) goto exit; } mdss_dp_mainlink_reset(&dp_drv->ctrl_io); mdss_dp_ctrl_lane_mapping(&dp_drv->ctrl_io, ln_map); reinit_completion(&dp_drv->idle_comp); mdss_dp_fill_link_cfg(dp_drv); mdss_dp_mainlink_ctrl(&dp_drv->ctrl_io, true); mdss_dp_config_ctrl(dp_drv); mdss_dp_sw_mvid_nvid(&dp_drv->ctrl_io); mdss_dp_timing_cfg(&dp_drv->ctrl_io, &dp_drv->panel_data.panel_info); } else { mdss_dp_aux_ctrl(&dp_drv->ctrl_io, true); } pr_debug("call link_training\n"); mdss_dp_link_train(dp_drv); mdss_dp_configure_source_params(dp_drv, &ln_map); mdss_dp_wait4train(dp_drv); link_training: mdss_dp_train_main_link(dp_drv); dp_drv->cont_splash = 0; if (mdss_dp_mainlink_ready(dp_drv, BIT(0))) pr_debug("mainlink ready\n"); dp_drv->power_on = true; mdss_dp_set_audio_switch_node(dp_drv, true); pr_debug("End-\n"); Loading @@ -1157,48 +1288,77 @@ exit: return ret; } static void mdss_dp_mainlink_off(struct mdss_panel_data *pdata) int mdss_dp_on(struct mdss_panel_data *pdata) { struct mdss_dp_drv_pdata *dp_drv = NULL; const int idle_pattern_completion_timeout_ms = 3 * HZ / 100; if (!pdata) { pr_err("Invalid input data\n"); return -EINVAL; } dp_drv = container_of(pdata, struct mdss_dp_drv_pdata, panel_data); if (!dp_drv) { pr_err("Invalid input data\n"); return; return mdss_dp_on_hpd(dp_drv); } static void mdss_dp_reset_test_data(struct mdss_dp_drv_pdata *dp) { dp->test_data = (const struct dpcd_test_request){ 0 }; } static bool mdss_dp_is_link_training_requested(struct mdss_dp_drv_pdata *dp) { return (dp->test_data.test_requested == TEST_LINK_TRAINING); } static bool mdss_dp_soft_hpd_reset(struct mdss_dp_drv_pdata *dp) { return mdss_dp_is_link_training_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) { pr_debug("panel already powered off\n"); return 0; } pr_debug("Entered++\n"); /* wait until link training is completed */ mutex_lock(&dp_drv->train_mutex); reinit_completion(&dp_drv->idle_comp); mdss_dp_state_ctrl(&dp_drv->ctrl_io, ST_PUSH_IDLE); if (!wait_for_completion_timeout(&dp_drv->idle_comp, idle_pattern_completion_timeout_ms)) pr_warn("PUSH_IDLE pattern timedout\n"); pr_debug("start\n"); mdss_dp_mainlink_ctrl(&dp_drv->ctrl_io, false); mdss_dp_audio_enable(&dp_drv->ctrl_io, false); /* Make sure the DP main link is disabled before clk disable */ wmb(); mdss_dp_disable_mainlink_clocks(dp_drv); dp_drv->power_on = false; mutex_unlock(&dp_drv->train_mutex); pr_debug("mainlink off done\n"); complete_all(&dp_drv->irq_comp); pr_debug("end\n"); return 0; } int mdss_dp_off(struct mdss_panel_data *pdata) static int mdss_dp_off_hpd(struct mdss_dp_drv_pdata *dp_drv) { struct mdss_dp_drv_pdata *dp_drv = NULL; dp_drv = container_of(pdata, struct mdss_dp_drv_pdata, panel_data); if (!dp_drv) { pr_err("Invalid input data\n"); return -EINVAL; if (!dp_drv->power_on) { pr_debug("panel already powered off\n"); return 0; } pr_debug("Entered++, cont_splash=%d\n", dp_drv->cont_splash); /* wait until link training is completed */ mutex_lock(&dp_drv->train_mutex); if (dp_drv->link_clks_on) pr_debug("Entered++, cont_splash=%d\n", dp_drv->cont_splash); mdss_dp_mainlink_ctrl(&dp_drv->ctrl_io, false); mdss_dp_aux_ctrl(&dp_drv->ctrl_io, false); Loading @@ -1219,7 +1379,7 @@ int mdss_dp_off(struct mdss_panel_data *pdata) /* Make sure DP is disabled before clk disable */ wmb(); mdss_dp_clk_ctrl(dp_drv, DP_CTRL_PM, false); mdss_dp_disable_mainlink_clocks(dp_drv); mdss_dp_clk_ctrl(dp_drv, DP_CORE_PM, false); mdss_dp_regulator_ctrl(dp_drv, false); Loading @@ -1232,6 +1392,23 @@ int mdss_dp_off(struct mdss_panel_data *pdata) return 0; } int mdss_dp_off(struct mdss_panel_data *pdata) { struct mdss_dp_drv_pdata *dp = NULL; dp = container_of(pdata, struct mdss_dp_drv_pdata, panel_data); if (!dp) { pr_err("Invalid input data\n"); return -EINVAL; } if (mdss_dp_soft_hpd_reset(dp)) return mdss_dp_off_irq(dp); else return mdss_dp_off_hpd(dp); } static void mdss_dp_send_cable_notification( struct mdss_dp_drv_pdata *dp, int val) { Loading Loading @@ -1444,22 +1621,24 @@ static void mdss_dp_hdcp_cb(void *ptr, enum hdcp_states status) return; } ops = dp->hdcp_ops; ops = dp->hdcp.ops; mutex_lock(&dp->train_mutex); switch (status) { case HDCP_STATE_AUTHENTICATED: pr_debug("hdcp 1.3 authenticated\n"); pr_debug("hdcp authenticated\n"); dp->hdcp.auth_state = true; break; case HDCP_STATE_AUTH_FAIL: dp->hdcp.auth_state = false; if (dp->power_on) { pr_debug("Reauthenticating\n"); if (ops && ops->reauthenticate) { rc = ops->reauthenticate(dp->hdcp_data); rc = ops->reauthenticate(dp->hdcp.data); if (rc) pr_err("HDCP reauth failed. rc=%d\n", rc); pr_err("reauth failed rc=%d\n", rc); } } else { pr_debug("not reauthenticating, cable disconnected\n"); Loading Loading @@ -1508,19 +1687,22 @@ static int mdss_dp_hdcp_init(struct mdss_panel_data *pdata) hdcp_init_data.sec_access = true; hdcp_init_data.client_id = HDCP_CLIENT_DP; dp_drv->hdcp_data = hdcp_1x_init(&hdcp_init_data); if (IS_ERR_OR_NULL(dp_drv->hdcp_data)) { dp_drv->hdcp.data = hdcp_1x_init(&hdcp_init_data); if (IS_ERR_OR_NULL(dp_drv->hdcp.data)) { pr_err("Error hdcp init\n"); rc = -EINVAL; goto error; } dp_drv->panel_data.panel_info.hdcp_1x_data = dp_drv->hdcp_data; dp_drv->panel_data.panel_info.hdcp_1x_data = dp_drv->hdcp.data; pr_debug("HDCP 1.3 initialized\n"); dp_drv->hdcp_ops = hdcp_1x_start(dp_drv->hdcp_data); dp_drv->hdcp.hdcp2 = dp_hdcp2p2_init(&hdcp_init_data); if (!IS_ERR_OR_NULL(dp_drv->hdcp.data)) pr_debug("HDCP 2.2 initialized\n"); dp_drv->hdcp.feature_enabled = true; return 0; error: return rc; Loading Loading @@ -1634,13 +1816,74 @@ static int mdss_dp_sysfs_create(struct mdss_dp_drv_pdata *dp, return 0; } static void mdss_dp_mainlink_push_idle(struct mdss_panel_data *pdata) { struct mdss_dp_drv_pdata *dp_drv = NULL; const int idle_pattern_completion_timeout_ms = 3 * HZ / 100; dp_drv = container_of(pdata, struct mdss_dp_drv_pdata, panel_data); if (!dp_drv) { pr_err("Invalid input data\n"); return; } pr_debug("Entered++\n"); /* wait until link training is completed */ mutex_lock(&dp_drv->train_mutex); mdss_dp_aux_set_sink_power_state(dp_drv, SINK_POWER_OFF); reinit_completion(&dp_drv->idle_comp); mdss_dp_state_ctrl(&dp_drv->ctrl_io, ST_PUSH_IDLE); if (!wait_for_completion_timeout(&dp_drv->idle_comp, idle_pattern_completion_timeout_ms)) pr_warn("PUSH_IDLE pattern timedout\n"); mutex_unlock(&dp_drv->train_mutex); pr_debug("mainlink off done\n"); } static void mdss_dp_update_hdcp_info(struct mdss_dp_drv_pdata *dp) { void *fd = NULL; struct hdcp_ops *ops = NULL; if (!dp) { pr_err("invalid input\n"); return; } /* check first if hdcp2p2 is supported */ fd = dp->hdcp.hdcp2; if (fd) ops = dp_hdcp2p2_start(fd); if (ops && ops->feature_supported) dp->hdcp.hdcp2_present = ops->feature_supported(fd); else dp->hdcp.hdcp2_present = false; if (!dp->hdcp.hdcp2_present) { dp->hdcp.hdcp1_present = hdcp1_check_if_supported_load_app(); if (dp->hdcp.hdcp1_present) { fd = dp->hdcp.hdcp1; ops = hdcp_1x_start(fd); } } /* update internal data about hdcp */ dp->hdcp.data = fd; dp->hdcp.ops = ops; } static int mdss_dp_event_handler(struct mdss_panel_data *pdata, int event, void *arg) { int rc = 0; struct fb_info *fbi; struct mdss_dp_drv_pdata *dp = NULL; struct hdcp_ops *ops; if (!pdata) { pr_err("%s: Invalid input data\n", __func__); Loading @@ -1652,25 +1895,24 @@ static int mdss_dp_event_handler(struct mdss_panel_data *pdata, dp = container_of(pdata, struct mdss_dp_drv_pdata, panel_data); ops = dp->hdcp_ops; switch (event) { case MDSS_EVENT_UNBLANK: rc = mdss_dp_on(pdata); break; case MDSS_EVENT_PANEL_ON: if (hdcp1_check_if_supported_load_app()) { if (ops && ops->authenticate) rc = ops->authenticate(dp->hdcp_data); } mdss_dp_update_hdcp_info(dp); if (dp->hdcp.ops && dp->hdcp.ops->authenticate) rc = dp->hdcp.ops->authenticate(dp->hdcp.data); break; case MDSS_EVENT_PANEL_OFF: rc = mdss_dp_off(pdata); break; case MDSS_EVENT_BLANK: if (ops && ops->off) ops->off(dp->hdcp_data); mdss_dp_mainlink_off(pdata); if (dp->hdcp.ops && dp->hdcp.ops->off) dp->hdcp.ops->off(dp->hdcp.data); mdss_dp_mainlink_push_idle(pdata); break; case MDSS_EVENT_FB_REGISTERED: fbi = (struct fb_info *)arg; Loading Loading @@ -1704,6 +1946,7 @@ static int mdss_dp_remove(struct platform_device *pdev) struct mdss_dp_drv_pdata *dp_drv = NULL; dp_drv = platform_get_drvdata(pdev); dp_hdcp2p2_deinit(dp_drv->hdcp.data); iounmap(dp_drv->ctrl_io.base); dp_drv->ctrl_io.base = NULL; Loading Loading @@ -1944,11 +2187,6 @@ irqreturn_t dp_isr(int irq, void *ptr) dp_aux_native_handler(dp, isr1); } if (dp->hdcp_ops && dp->hdcp_ops->isr) { if (dp->hdcp_ops->isr(dp->hdcp_data)) pr_err("dp_hdcp_isr failed\n"); } return IRQ_HANDLED; } Loading Loading @@ -1976,10 +2214,8 @@ static void usbpd_connect_callback(struct usbpd_svid_handler *hdlr) return; } mutex_lock(&dp_drv->pd_msg_mutex); dp_drv->cable_connected = true; mdss_dp_update_cable_status(dp_drv, true); dp_send_events(dp_drv, EV_USBPD_DISCOVER_MODES); mutex_unlock(&dp_drv->pd_msg_mutex); pr_debug("discover_mode event sent\n"); } Loading @@ -1994,10 +2230,8 @@ static void usbpd_disconnect_callback(struct usbpd_svid_handler *hdlr) } pr_debug("cable disconnected\n"); mutex_lock(&dp_drv->pd_msg_mutex); dp_drv->cable_connected = false; mdss_dp_update_cable_status(dp_drv, false); dp_drv->alt_mode.current_state = UNKNOWN_STATE; mutex_unlock(&dp_drv->pd_msg_mutex); mdss_dp_notify_clients(dp_drv, false); } Loading Loading @@ -2040,6 +2274,108 @@ end: return ret; } /** * mdss_dp_send_test_response() - sends the test response to the sink * @dp: Display Port Driver data * * This function will send the test response to the sink but only after * any previous link training has been completed. */ static void mdss_dp_send_test_response(struct mdss_dp_drv_pdata *dp) { mutex_lock(&dp->train_mutex); mdss_dp_aux_send_test_response(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) { mdss_dp_notify_clients(dp, false); 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"); return -EINVAL; } } return 0; } /** * mdss_dp_process_hpd_irq_high() - handle HPD IRQ transition to HIGH * @dp: Display Port Driver data * * This function will handle the HPD IRQ state transitions from HIGH to HIGH * or LOW to HIGH, indicating the start of a new test request. */ static void mdss_dp_process_hpd_irq_high(struct mdss_dp_drv_pdata *dp) { pr_debug("enter: HPD IRQ High\n"); dp->hpd_irq_on = true; mdss_dp_aux_parse_sink_status_field(dp); if (mdss_dp_is_link_training_requested(dp)) { mdss_dp_send_test_response(dp); pr_info("%s requested: link rate = 0x%x, lane count = 0x%x\n", mdss_dp_get_test_name(TEST_LINK_TRAINING), dp->test_data.test_link_rate, dp->test_data.test_lane_count); dp->dpcd.max_lane_count = dp->test_data.test_lane_count; dp->link_rate = dp->test_data.test_link_rate; if (mdss_dp_hpd_irq_notify_clients(dp)) return; mdss_dp_on_irq(dp); } mdss_dp_reset_test_data(dp); pr_debug("done\n"); } /** * mdss_dp_process_hpd_irq_low() - handle HPD IRQ transition to LOW * @dp: Display Port Driver data * * This function will handle the HPD IRQ state transitions from HIGH to LOW, * indicating the end of a test request. */ static void mdss_dp_process_hpd_irq_low(struct mdss_dp_drv_pdata *dp) { pr_debug("enter: HPD IRQ low\n"); dp->hpd_irq_on = false; mdss_dp_update_cable_status(dp, false); mdss_dp_mainlink_push_idle(&dp->panel_data); mdss_dp_off_hpd(dp); mdss_dp_reset_test_data(dp); pr_debug("done\n"); } static void usbpd_response_callback(struct usbpd_svid_handler *hdlr, u8 cmd, enum usbpd_svdm_cmd_type cmd_type, const u32 *vdos, int num_vdos) Loading @@ -2055,8 +2391,10 @@ static void usbpd_response_callback(struct usbpd_svid_handler *hdlr, u8 cmd, pr_debug("callback -> cmd: 0x%x, *vdos = 0x%x, num_vdos = %d\n", cmd, *vdos, num_vdos); if (mdss_dp_validate_callback(cmd, cmd_type, num_vdos)) if (mdss_dp_validate_callback(cmd, cmd_type, num_vdos)) { pr_debug("invalid callback received\n"); return; } switch (cmd) { case USBPD_SVDM_DISCOVER_MODES: Loading @@ -2073,10 +2411,31 @@ static void usbpd_response_callback(struct usbpd_svid_handler *hdlr, u8 cmd, dp_drv->alt_mode.dp_status.response = *vdos; mdss_dp_usbpd_ext_dp_status(&dp_drv->alt_mode.dp_status); if (!dp_drv->alt_mode.dp_status.hpd_high) return; dp_drv->hpd_irq_toggled = dp_drv->hpd_irq_on != dp_drv->alt_mode.dp_status.hpd_irq; if (dp_drv->alt_mode.dp_status.hpd_irq) { mdss_dp_process_hpd_irq_high(dp_drv); break; } if (dp_drv->hpd_irq_toggled && !dp_drv->alt_mode.dp_status.hpd_irq) { mdss_dp_process_hpd_irq_low(dp_drv); break; } if (!dp_drv->alt_mode.dp_status.hpd_high) { pr_debug("Attention: HPD low\n"); mdss_dp_update_cable_status(dp_drv, false); mdss_dp_notify_clients(dp_drv, false); pr_debug("Attention: Notified clients\n"); break; } pr_debug("Attention: HPD high\n"); pr_debug("HPD high\n"); mdss_dp_update_cable_status(dp_drv, true); dp_drv->alt_mode.current_state |= DP_STATUS_DONE; Loading @@ -2084,6 +2443,10 @@ static void usbpd_response_callback(struct usbpd_svid_handler *hdlr, u8 cmd, mdss_dp_host_init(&dp_drv->panel_data); else dp_send_events(dp_drv, EV_USBPD_DP_CONFIGURE); if (dp_drv->alt_mode.dp_status.hpd_irq && dp_drv->power_on && dp_drv->hdcp.ops && dp_drv->hdcp.ops->isr) dp_drv->hdcp.ops->isr(dp_drv->hdcp.data); break; case DP_VDM_STATUS: dp_drv->alt_mode.dp_status.response = *vdos; Loading @@ -2096,7 +2459,7 @@ static void usbpd_response_callback(struct usbpd_svid_handler *hdlr, u8 cmd, break; case DP_VDM_CONFIGURE: dp_drv->alt_mode.current_state |= DP_CONFIGURE_DONE; pr_debug("config USBPD to DP done\n"); pr_debug("Configure: config USBPD to DP done\n"); if (dp_drv->alt_mode.dp_status.hpd_high) mdss_dp_host_init(&dp_drv->panel_data); Loading Loading @@ -2259,7 +2622,10 @@ static int mdss_dp_probe(struct platform_device *pdev) dp_drv->inited = true; dp_drv->wait_for_audio_comp = false; dp_drv->hpd_irq_on = false; mdss_dp_reset_test_data(dp_drv); init_completion(&dp_drv->audio_comp); init_completion(&dp_drv->irq_comp); pr_debug("done\n"); Loading @@ -2280,6 +2646,53 @@ probe_err: } void *mdss_dp_get_hdcp_data(struct device *dev) { struct mdss_dp_drv_pdata *dp_drv = NULL; if (!dev) { pr_err("%s:Invalid input\n", __func__); return NULL; } dp_drv = dev_get_drvdata(dev); if (!dp_drv) { pr_err("%s:Invalid dp driver\n", __func__); return NULL; } return dp_drv->hdcp.data; } static inline bool dp_is_hdcp_enabled(struct mdss_dp_drv_pdata *dp_drv) { return dp_drv->hdcp.feature_enabled && (dp_drv->hdcp.hdcp1_present || dp_drv->hdcp.hdcp2_present) && dp_drv->hdcp.ops; } static inline bool dp_is_stream_shareable(struct mdss_dp_drv_pdata *dp_drv) { bool ret = 0; switch (dp_drv->hdcp.enc_lvl) { case HDCP_STATE_AUTH_ENC_NONE: ret = true; break; case HDCP_STATE_AUTH_ENC_1X: ret = dp_is_hdcp_enabled(dp_drv) && dp_drv->hdcp.auth_state; break; case HDCP_STATE_AUTH_ENC_2P2: ret = dp_drv->hdcp.feature_enabled && dp_drv->hdcp.hdcp2_present && dp_drv->hdcp.auth_state; break; default: ret = false; } return ret; } static const struct of_device_id msm_mdss_dp_dt_match[] = { {.compatible = "qcom,mdss-dp"}, {} Loading Loading
Documentation/devicetree/bindings/fb/mdss-mdp.txt +2 −0 Original line number Diff line number Diff line Loading @@ -566,6 +566,8 @@ Subnode properties: Example: Width = 1920, Height = 1080, BytesPerPixel = 4, Number of frame-buffers reserved = 2. Size = 1920*1080*4*2 = ROUND_1MB(15.8MB) = 16MB. - qcom,mdss-intf: Phandle to the kernel driver module that is mapped to the frame buffer virtual device. - qcom,mdss-fb-splash-logo-enabled: The boolean entry enables the framebuffer driver to display the splash logo image. It is independent of continuous splash Loading
arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi +2 −0 Original line number Diff line number Diff line Loading @@ -268,11 +268,13 @@ mdss_fb2: qcom,mdss_fb_hdmi { cell-index = <2>; compatible = "qcom,mdss-fb"; qcom,mdss-intf = <&mdss_hdmi_tx>; }; mdss_fb3: qcom,mdss_fb_dp { cell-index = <3>; compatible = "qcom,mdss-fb"; qcom,mdss-intf = <&mdss_dp_ctrl>; }; }; Loading
drivers/video/fbdev/msm/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -46,6 +46,7 @@ obj-$(CONFIG_FB_MSM_MDSS) += mdss_dba_utils.o obj-$(CONFIG_FB_MSM_MDSS) += mdss_hdcp_1x.o obj-$(CONFIG_FB_MSM_MDSS_DP_PANEL) += mdss_dp.o mdss_dp_util.o obj-$(CONFIG_FB_MSM_MDSS_DP_PANEL) += mdss_dp_aux.o obj-$(CONFIG_FB_MSM_MDSS_DP_PANEL) += mdss_dp_hdcp2p2.o obj-$(CONFIG_FB_MSM_MDSS) += mdss_io_util.o obj-$(CONFIG_FB_MSM_MDSS) += msm_ext_display.o Loading
drivers/video/fbdev/msm/mdss_dp.c +557 −144 Original line number Diff line number Diff line Loading @@ -36,7 +36,7 @@ #include "mdss_dp_util.h" #include "mdss_hdmi_panel.h" #include <linux/hdcp_qseecom.h> #include "mdss_hdcp_1x.h" #include "mdss_hdcp.h" #include "mdss_debug.h" #define RGB_COMPONENTS 3 Loading Loading @@ -857,6 +857,18 @@ int mdss_dp_wait4train(struct mdss_dp_drv_pdata *dp_drv) return ret; } static void mdss_dp_update_cable_status(struct mdss_dp_drv_pdata *dp, bool connected) { mutex_lock(&dp->pd_msg_mutex); pr_debug("cable_connected to %d\n", connected); if (dp->cable_connected != connected) dp->cable_connected = connected; else pr_debug("no change in cable status\n"); mutex_unlock(&dp->pd_msg_mutex); } static int dp_get_cable_status(struct platform_device *pdev, u32 vote) { struct mdss_dp_drv_pdata *dp_ctrl = platform_get_drvdata(pdev); Loading Loading @@ -1038,32 +1050,186 @@ static inline void mdss_dp_set_audio_switch_node( val); } int mdss_dp_on(struct mdss_panel_data *pdata) /** * mdss_dp_get_lane_mapping() - returns lane mapping based on given orientation * @orientation: usb plug orientation * @lane_map: the configured lane mapping * * Returns 0 when the lane mapping is successfully determined based on the * given usb plug orientation. */ static int mdss_dp_get_lane_mapping(struct mdss_dp_drv_pdata *dp, enum plug_orientation orientation, struct lane_mapping *lane_map) { int ret = 0; pr_debug("enter: orientation = %d\n", orientation); if (!lane_map) { pr_err("invalid lane map input"); ret = -EINVAL; goto exit; } /* Set the default lane mapping */ lane_map->lane0 = 2; lane_map->lane1 = 3; lane_map->lane2 = 1; lane_map->lane3 = 0; if (orientation == ORIENTATION_CC2) { lane_map->lane0 = 1; lane_map->lane1 = 0; lane_map->lane2 = 2; lane_map->lane3 = 3; if (gpio_is_valid(dp->usbplug_cc_gpio)) { gpio_set_value(dp->usbplug_cc_gpio, 1); pr_debug("Configured cc gpio for new Orientation\n"); } } pr_debug("lane0 = %d, lane1 = %d, lane2 =%d, lane3 =%d\n", lane_map->lane0, lane_map->lane1, lane_map->lane2, lane_map->lane3); exit: return ret; } /** * mdss_dp_enable_mainlink_clocks() - enables Display Port main link clocks * @dp: Display Port Driver data * * Returns 0 when the main link clocks are successfully enabled. */ static int mdss_dp_enable_mainlink_clocks(struct mdss_dp_drv_pdata *dp) { int ret = 0; dp->power_data[DP_CTRL_PM].clk_config[0].rate = ((dp->link_rate * DP_LINK_RATE_MULTIPLIER) / 1000);/* KHz */ dp->pixel_rate = dp->panel_data.panel_info.clk_rate; dp->power_data[DP_CTRL_PM].clk_config[3].rate = (dp->pixel_rate / 1000);/* KHz */ ret = mdss_dp_clk_ctrl(dp, DP_CTRL_PM, true); if (ret) { pr_err("Unabled to start link clocks\n"); ret = -EINVAL; } return ret; } /** * mdss_dp_disable_mainlink_clocks() - disables Display Port main link clocks * @dp: Display Port Driver data */ static void mdss_dp_disable_mainlink_clocks(struct mdss_dp_drv_pdata *dp_drv) { mdss_dp_clk_ctrl(dp_drv, DP_CTRL_PM, false); } /** * mdss_dp_configure_source_params() - configures DP transmitter source params * @dp: Display Port Driver data * @lane_map: usb port lane mapping * * Configures the DP transmitter source params including details such as lane * configuration, output format and sink/panel timing information. */ static void mdss_dp_configure_source_params(struct mdss_dp_drv_pdata *dp, struct lane_mapping *lane_map) { mdss_dp_ctrl_lane_mapping(&dp->ctrl_io, *lane_map); mdss_dp_fill_link_cfg(dp); mdss_dp_mainlink_ctrl(&dp->ctrl_io, true); mdss_dp_config_ctrl(dp); mdss_dp_sw_mvid_nvid(&dp->ctrl_io); mdss_dp_timing_cfg(&dp->ctrl_io, &dp->panel_data.panel_info); } /** * mdss_dp_train_main_link() - initiates training of DP main link * @dp: Display Port Driver data * * Initiates training of the DP main link and checks the state of the main * link after the training is complete. */ static void mdss_dp_train_main_link(struct mdss_dp_drv_pdata *dp) { int ready = 0; pr_debug("enter\n"); mdss_dp_link_train(dp); mdss_dp_wait4train(dp); ready = mdss_dp_mainlink_ready(dp, BIT(0)); pr_debug("main link %s\n", ready ? "READY" : "NOT READY"); } static int mdss_dp_on_irq(struct mdss_dp_drv_pdata *dp_drv) { struct mdss_dp_drv_pdata *dp_drv = NULL; int ret = 0; enum plug_orientation orientation = ORIENTATION_NONE; struct lane_mapping ln_map; if (!pdata) { pr_err("Invalid input data\n"); return -EINVAL; /* wait until link training is completed */ mutex_lock(&dp_drv->train_mutex); pr_debug("enter\n"); orientation = usbpd_get_plug_orientation(dp_drv->pd); pr_debug("plug orientation = %d\n", orientation); ret = mdss_dp_get_lane_mapping(dp_drv, orientation, &ln_map); if (ret) goto exit; mdss_dp_phy_share_lane_config(&dp_drv->phy_io, orientation, dp_drv->dpcd.max_lane_count); ret = mdss_dp_enable_mainlink_clocks(dp_drv); if (ret) goto exit; mdss_dp_mainlink_reset(&dp_drv->ctrl_io); reinit_completion(&dp_drv->idle_comp); mdss_dp_configure_source_params(dp_drv, &ln_map); mdss_dp_train_main_link(dp_drv); dp_drv->power_on = true; pr_debug("end\n"); exit: mutex_unlock(&dp_drv->train_mutex); return ret; } dp_drv = container_of(pdata, struct mdss_dp_drv_pdata, panel_data); int mdss_dp_on_hpd(struct mdss_dp_drv_pdata *dp_drv) { int ret = 0; enum plug_orientation orientation = ORIENTATION_NONE; struct lane_mapping ln_map; /* wait until link training is completed */ mutex_lock(&dp_drv->train_mutex); pr_debug("Enter++ cont_splash=%d\n", dp_drv->cont_splash); /* Default lane mapping */ ln_map.lane0 = 2; ln_map.lane1 = 3; ln_map.lane2 = 1; ln_map.lane3 = 0; if (!dp_drv->cont_splash) { /* vote for clocks */ if (dp_drv->cont_splash) { mdss_dp_aux_ctrl(&dp_drv->ctrl_io, true); goto link_training; } ret = mdss_dp_clk_ctrl(dp_drv, DP_CORE_PM, true); if (ret) { pr_err("Unabled to start core clocks\n"); Loading @@ -1074,20 +1240,9 @@ int mdss_dp_on(struct mdss_panel_data *pdata) orientation = usbpd_get_plug_orientation(dp_drv->pd); pr_debug("plug Orientation = %d\n", orientation); if (orientation == ORIENTATION_CC2) { /* update lane mapping */ ln_map.lane0 = 1; ln_map.lane1 = 0; ln_map.lane2 = 2; ln_map.lane3 = 3; if (gpio_is_valid(dp_drv->usbplug_cc_gpio)) { gpio_set_value( dp_drv->usbplug_cc_gpio, 1); pr_debug("Configured cc gpio for new Orientation\n"); } } ret = mdss_dp_get_lane_mapping(dp_drv, orientation, &ln_map); if (ret) goto exit; if (dp_drv->new_vic && (dp_drv->new_vic != dp_drv->vic)) dp_init_panel_info(dp_drv, dp_drv->new_vic); Loading @@ -1109,45 +1264,21 @@ int mdss_dp_on(struct mdss_panel_data *pdata) pr_debug("link_rate = 0x%x\n", dp_drv->link_rate); dp_drv->power_data[DP_CTRL_PM].clk_config[0].rate = ((dp_drv->link_rate * DP_LINK_RATE_MULTIPLIER) / 1000); /* KHz */ dp_drv->pixel_rate = dp_drv->panel_data.panel_info.clk_rate; dp_drv->power_data[DP_CTRL_PM].clk_config[3].rate = (dp_drv->pixel_rate / 1000); /* KHz */ ret = mdss_dp_clk_ctrl(dp_drv, DP_CTRL_PM, true); if (ret) { pr_err("Unabled to start link clocks\n"); ret = mdss_dp_enable_mainlink_clocks(dp_drv); if (ret) goto exit; } mdss_dp_mainlink_reset(&dp_drv->ctrl_io); mdss_dp_ctrl_lane_mapping(&dp_drv->ctrl_io, ln_map); reinit_completion(&dp_drv->idle_comp); mdss_dp_fill_link_cfg(dp_drv); mdss_dp_mainlink_ctrl(&dp_drv->ctrl_io, true); mdss_dp_config_ctrl(dp_drv); mdss_dp_sw_mvid_nvid(&dp_drv->ctrl_io); mdss_dp_timing_cfg(&dp_drv->ctrl_io, &dp_drv->panel_data.panel_info); } else { mdss_dp_aux_ctrl(&dp_drv->ctrl_io, true); } pr_debug("call link_training\n"); mdss_dp_link_train(dp_drv); mdss_dp_configure_source_params(dp_drv, &ln_map); mdss_dp_wait4train(dp_drv); link_training: mdss_dp_train_main_link(dp_drv); dp_drv->cont_splash = 0; if (mdss_dp_mainlink_ready(dp_drv, BIT(0))) pr_debug("mainlink ready\n"); dp_drv->power_on = true; mdss_dp_set_audio_switch_node(dp_drv, true); pr_debug("End-\n"); Loading @@ -1157,48 +1288,77 @@ exit: return ret; } static void mdss_dp_mainlink_off(struct mdss_panel_data *pdata) int mdss_dp_on(struct mdss_panel_data *pdata) { struct mdss_dp_drv_pdata *dp_drv = NULL; const int idle_pattern_completion_timeout_ms = 3 * HZ / 100; if (!pdata) { pr_err("Invalid input data\n"); return -EINVAL; } dp_drv = container_of(pdata, struct mdss_dp_drv_pdata, panel_data); if (!dp_drv) { pr_err("Invalid input data\n"); return; return mdss_dp_on_hpd(dp_drv); } static void mdss_dp_reset_test_data(struct mdss_dp_drv_pdata *dp) { dp->test_data = (const struct dpcd_test_request){ 0 }; } static bool mdss_dp_is_link_training_requested(struct mdss_dp_drv_pdata *dp) { return (dp->test_data.test_requested == TEST_LINK_TRAINING); } static bool mdss_dp_soft_hpd_reset(struct mdss_dp_drv_pdata *dp) { return mdss_dp_is_link_training_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) { pr_debug("panel already powered off\n"); return 0; } pr_debug("Entered++\n"); /* wait until link training is completed */ mutex_lock(&dp_drv->train_mutex); reinit_completion(&dp_drv->idle_comp); mdss_dp_state_ctrl(&dp_drv->ctrl_io, ST_PUSH_IDLE); if (!wait_for_completion_timeout(&dp_drv->idle_comp, idle_pattern_completion_timeout_ms)) pr_warn("PUSH_IDLE pattern timedout\n"); pr_debug("start\n"); mdss_dp_mainlink_ctrl(&dp_drv->ctrl_io, false); mdss_dp_audio_enable(&dp_drv->ctrl_io, false); /* Make sure the DP main link is disabled before clk disable */ wmb(); mdss_dp_disable_mainlink_clocks(dp_drv); dp_drv->power_on = false; mutex_unlock(&dp_drv->train_mutex); pr_debug("mainlink off done\n"); complete_all(&dp_drv->irq_comp); pr_debug("end\n"); return 0; } int mdss_dp_off(struct mdss_panel_data *pdata) static int mdss_dp_off_hpd(struct mdss_dp_drv_pdata *dp_drv) { struct mdss_dp_drv_pdata *dp_drv = NULL; dp_drv = container_of(pdata, struct mdss_dp_drv_pdata, panel_data); if (!dp_drv) { pr_err("Invalid input data\n"); return -EINVAL; if (!dp_drv->power_on) { pr_debug("panel already powered off\n"); return 0; } pr_debug("Entered++, cont_splash=%d\n", dp_drv->cont_splash); /* wait until link training is completed */ mutex_lock(&dp_drv->train_mutex); if (dp_drv->link_clks_on) pr_debug("Entered++, cont_splash=%d\n", dp_drv->cont_splash); mdss_dp_mainlink_ctrl(&dp_drv->ctrl_io, false); mdss_dp_aux_ctrl(&dp_drv->ctrl_io, false); Loading @@ -1219,7 +1379,7 @@ int mdss_dp_off(struct mdss_panel_data *pdata) /* Make sure DP is disabled before clk disable */ wmb(); mdss_dp_clk_ctrl(dp_drv, DP_CTRL_PM, false); mdss_dp_disable_mainlink_clocks(dp_drv); mdss_dp_clk_ctrl(dp_drv, DP_CORE_PM, false); mdss_dp_regulator_ctrl(dp_drv, false); Loading @@ -1232,6 +1392,23 @@ int mdss_dp_off(struct mdss_panel_data *pdata) return 0; } int mdss_dp_off(struct mdss_panel_data *pdata) { struct mdss_dp_drv_pdata *dp = NULL; dp = container_of(pdata, struct mdss_dp_drv_pdata, panel_data); if (!dp) { pr_err("Invalid input data\n"); return -EINVAL; } if (mdss_dp_soft_hpd_reset(dp)) return mdss_dp_off_irq(dp); else return mdss_dp_off_hpd(dp); } static void mdss_dp_send_cable_notification( struct mdss_dp_drv_pdata *dp, int val) { Loading Loading @@ -1444,22 +1621,24 @@ static void mdss_dp_hdcp_cb(void *ptr, enum hdcp_states status) return; } ops = dp->hdcp_ops; ops = dp->hdcp.ops; mutex_lock(&dp->train_mutex); switch (status) { case HDCP_STATE_AUTHENTICATED: pr_debug("hdcp 1.3 authenticated\n"); pr_debug("hdcp authenticated\n"); dp->hdcp.auth_state = true; break; case HDCP_STATE_AUTH_FAIL: dp->hdcp.auth_state = false; if (dp->power_on) { pr_debug("Reauthenticating\n"); if (ops && ops->reauthenticate) { rc = ops->reauthenticate(dp->hdcp_data); rc = ops->reauthenticate(dp->hdcp.data); if (rc) pr_err("HDCP reauth failed. rc=%d\n", rc); pr_err("reauth failed rc=%d\n", rc); } } else { pr_debug("not reauthenticating, cable disconnected\n"); Loading Loading @@ -1508,19 +1687,22 @@ static int mdss_dp_hdcp_init(struct mdss_panel_data *pdata) hdcp_init_data.sec_access = true; hdcp_init_data.client_id = HDCP_CLIENT_DP; dp_drv->hdcp_data = hdcp_1x_init(&hdcp_init_data); if (IS_ERR_OR_NULL(dp_drv->hdcp_data)) { dp_drv->hdcp.data = hdcp_1x_init(&hdcp_init_data); if (IS_ERR_OR_NULL(dp_drv->hdcp.data)) { pr_err("Error hdcp init\n"); rc = -EINVAL; goto error; } dp_drv->panel_data.panel_info.hdcp_1x_data = dp_drv->hdcp_data; dp_drv->panel_data.panel_info.hdcp_1x_data = dp_drv->hdcp.data; pr_debug("HDCP 1.3 initialized\n"); dp_drv->hdcp_ops = hdcp_1x_start(dp_drv->hdcp_data); dp_drv->hdcp.hdcp2 = dp_hdcp2p2_init(&hdcp_init_data); if (!IS_ERR_OR_NULL(dp_drv->hdcp.data)) pr_debug("HDCP 2.2 initialized\n"); dp_drv->hdcp.feature_enabled = true; return 0; error: return rc; Loading Loading @@ -1634,13 +1816,74 @@ static int mdss_dp_sysfs_create(struct mdss_dp_drv_pdata *dp, return 0; } static void mdss_dp_mainlink_push_idle(struct mdss_panel_data *pdata) { struct mdss_dp_drv_pdata *dp_drv = NULL; const int idle_pattern_completion_timeout_ms = 3 * HZ / 100; dp_drv = container_of(pdata, struct mdss_dp_drv_pdata, panel_data); if (!dp_drv) { pr_err("Invalid input data\n"); return; } pr_debug("Entered++\n"); /* wait until link training is completed */ mutex_lock(&dp_drv->train_mutex); mdss_dp_aux_set_sink_power_state(dp_drv, SINK_POWER_OFF); reinit_completion(&dp_drv->idle_comp); mdss_dp_state_ctrl(&dp_drv->ctrl_io, ST_PUSH_IDLE); if (!wait_for_completion_timeout(&dp_drv->idle_comp, idle_pattern_completion_timeout_ms)) pr_warn("PUSH_IDLE pattern timedout\n"); mutex_unlock(&dp_drv->train_mutex); pr_debug("mainlink off done\n"); } static void mdss_dp_update_hdcp_info(struct mdss_dp_drv_pdata *dp) { void *fd = NULL; struct hdcp_ops *ops = NULL; if (!dp) { pr_err("invalid input\n"); return; } /* check first if hdcp2p2 is supported */ fd = dp->hdcp.hdcp2; if (fd) ops = dp_hdcp2p2_start(fd); if (ops && ops->feature_supported) dp->hdcp.hdcp2_present = ops->feature_supported(fd); else dp->hdcp.hdcp2_present = false; if (!dp->hdcp.hdcp2_present) { dp->hdcp.hdcp1_present = hdcp1_check_if_supported_load_app(); if (dp->hdcp.hdcp1_present) { fd = dp->hdcp.hdcp1; ops = hdcp_1x_start(fd); } } /* update internal data about hdcp */ dp->hdcp.data = fd; dp->hdcp.ops = ops; } static int mdss_dp_event_handler(struct mdss_panel_data *pdata, int event, void *arg) { int rc = 0; struct fb_info *fbi; struct mdss_dp_drv_pdata *dp = NULL; struct hdcp_ops *ops; if (!pdata) { pr_err("%s: Invalid input data\n", __func__); Loading @@ -1652,25 +1895,24 @@ static int mdss_dp_event_handler(struct mdss_panel_data *pdata, dp = container_of(pdata, struct mdss_dp_drv_pdata, panel_data); ops = dp->hdcp_ops; switch (event) { case MDSS_EVENT_UNBLANK: rc = mdss_dp_on(pdata); break; case MDSS_EVENT_PANEL_ON: if (hdcp1_check_if_supported_load_app()) { if (ops && ops->authenticate) rc = ops->authenticate(dp->hdcp_data); } mdss_dp_update_hdcp_info(dp); if (dp->hdcp.ops && dp->hdcp.ops->authenticate) rc = dp->hdcp.ops->authenticate(dp->hdcp.data); break; case MDSS_EVENT_PANEL_OFF: rc = mdss_dp_off(pdata); break; case MDSS_EVENT_BLANK: if (ops && ops->off) ops->off(dp->hdcp_data); mdss_dp_mainlink_off(pdata); if (dp->hdcp.ops && dp->hdcp.ops->off) dp->hdcp.ops->off(dp->hdcp.data); mdss_dp_mainlink_push_idle(pdata); break; case MDSS_EVENT_FB_REGISTERED: fbi = (struct fb_info *)arg; Loading Loading @@ -1704,6 +1946,7 @@ static int mdss_dp_remove(struct platform_device *pdev) struct mdss_dp_drv_pdata *dp_drv = NULL; dp_drv = platform_get_drvdata(pdev); dp_hdcp2p2_deinit(dp_drv->hdcp.data); iounmap(dp_drv->ctrl_io.base); dp_drv->ctrl_io.base = NULL; Loading Loading @@ -1944,11 +2187,6 @@ irqreturn_t dp_isr(int irq, void *ptr) dp_aux_native_handler(dp, isr1); } if (dp->hdcp_ops && dp->hdcp_ops->isr) { if (dp->hdcp_ops->isr(dp->hdcp_data)) pr_err("dp_hdcp_isr failed\n"); } return IRQ_HANDLED; } Loading Loading @@ -1976,10 +2214,8 @@ static void usbpd_connect_callback(struct usbpd_svid_handler *hdlr) return; } mutex_lock(&dp_drv->pd_msg_mutex); dp_drv->cable_connected = true; mdss_dp_update_cable_status(dp_drv, true); dp_send_events(dp_drv, EV_USBPD_DISCOVER_MODES); mutex_unlock(&dp_drv->pd_msg_mutex); pr_debug("discover_mode event sent\n"); } Loading @@ -1994,10 +2230,8 @@ static void usbpd_disconnect_callback(struct usbpd_svid_handler *hdlr) } pr_debug("cable disconnected\n"); mutex_lock(&dp_drv->pd_msg_mutex); dp_drv->cable_connected = false; mdss_dp_update_cable_status(dp_drv, false); dp_drv->alt_mode.current_state = UNKNOWN_STATE; mutex_unlock(&dp_drv->pd_msg_mutex); mdss_dp_notify_clients(dp_drv, false); } Loading Loading @@ -2040,6 +2274,108 @@ end: return ret; } /** * mdss_dp_send_test_response() - sends the test response to the sink * @dp: Display Port Driver data * * This function will send the test response to the sink but only after * any previous link training has been completed. */ static void mdss_dp_send_test_response(struct mdss_dp_drv_pdata *dp) { mutex_lock(&dp->train_mutex); mdss_dp_aux_send_test_response(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) { mdss_dp_notify_clients(dp, false); 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"); return -EINVAL; } } return 0; } /** * mdss_dp_process_hpd_irq_high() - handle HPD IRQ transition to HIGH * @dp: Display Port Driver data * * This function will handle the HPD IRQ state transitions from HIGH to HIGH * or LOW to HIGH, indicating the start of a new test request. */ static void mdss_dp_process_hpd_irq_high(struct mdss_dp_drv_pdata *dp) { pr_debug("enter: HPD IRQ High\n"); dp->hpd_irq_on = true; mdss_dp_aux_parse_sink_status_field(dp); if (mdss_dp_is_link_training_requested(dp)) { mdss_dp_send_test_response(dp); pr_info("%s requested: link rate = 0x%x, lane count = 0x%x\n", mdss_dp_get_test_name(TEST_LINK_TRAINING), dp->test_data.test_link_rate, dp->test_data.test_lane_count); dp->dpcd.max_lane_count = dp->test_data.test_lane_count; dp->link_rate = dp->test_data.test_link_rate; if (mdss_dp_hpd_irq_notify_clients(dp)) return; mdss_dp_on_irq(dp); } mdss_dp_reset_test_data(dp); pr_debug("done\n"); } /** * mdss_dp_process_hpd_irq_low() - handle HPD IRQ transition to LOW * @dp: Display Port Driver data * * This function will handle the HPD IRQ state transitions from HIGH to LOW, * indicating the end of a test request. */ static void mdss_dp_process_hpd_irq_low(struct mdss_dp_drv_pdata *dp) { pr_debug("enter: HPD IRQ low\n"); dp->hpd_irq_on = false; mdss_dp_update_cable_status(dp, false); mdss_dp_mainlink_push_idle(&dp->panel_data); mdss_dp_off_hpd(dp); mdss_dp_reset_test_data(dp); pr_debug("done\n"); } static void usbpd_response_callback(struct usbpd_svid_handler *hdlr, u8 cmd, enum usbpd_svdm_cmd_type cmd_type, const u32 *vdos, int num_vdos) Loading @@ -2055,8 +2391,10 @@ static void usbpd_response_callback(struct usbpd_svid_handler *hdlr, u8 cmd, pr_debug("callback -> cmd: 0x%x, *vdos = 0x%x, num_vdos = %d\n", cmd, *vdos, num_vdos); if (mdss_dp_validate_callback(cmd, cmd_type, num_vdos)) if (mdss_dp_validate_callback(cmd, cmd_type, num_vdos)) { pr_debug("invalid callback received\n"); return; } switch (cmd) { case USBPD_SVDM_DISCOVER_MODES: Loading @@ -2073,10 +2411,31 @@ static void usbpd_response_callback(struct usbpd_svid_handler *hdlr, u8 cmd, dp_drv->alt_mode.dp_status.response = *vdos; mdss_dp_usbpd_ext_dp_status(&dp_drv->alt_mode.dp_status); if (!dp_drv->alt_mode.dp_status.hpd_high) return; dp_drv->hpd_irq_toggled = dp_drv->hpd_irq_on != dp_drv->alt_mode.dp_status.hpd_irq; if (dp_drv->alt_mode.dp_status.hpd_irq) { mdss_dp_process_hpd_irq_high(dp_drv); break; } if (dp_drv->hpd_irq_toggled && !dp_drv->alt_mode.dp_status.hpd_irq) { mdss_dp_process_hpd_irq_low(dp_drv); break; } if (!dp_drv->alt_mode.dp_status.hpd_high) { pr_debug("Attention: HPD low\n"); mdss_dp_update_cable_status(dp_drv, false); mdss_dp_notify_clients(dp_drv, false); pr_debug("Attention: Notified clients\n"); break; } pr_debug("Attention: HPD high\n"); pr_debug("HPD high\n"); mdss_dp_update_cable_status(dp_drv, true); dp_drv->alt_mode.current_state |= DP_STATUS_DONE; Loading @@ -2084,6 +2443,10 @@ static void usbpd_response_callback(struct usbpd_svid_handler *hdlr, u8 cmd, mdss_dp_host_init(&dp_drv->panel_data); else dp_send_events(dp_drv, EV_USBPD_DP_CONFIGURE); if (dp_drv->alt_mode.dp_status.hpd_irq && dp_drv->power_on && dp_drv->hdcp.ops && dp_drv->hdcp.ops->isr) dp_drv->hdcp.ops->isr(dp_drv->hdcp.data); break; case DP_VDM_STATUS: dp_drv->alt_mode.dp_status.response = *vdos; Loading @@ -2096,7 +2459,7 @@ static void usbpd_response_callback(struct usbpd_svid_handler *hdlr, u8 cmd, break; case DP_VDM_CONFIGURE: dp_drv->alt_mode.current_state |= DP_CONFIGURE_DONE; pr_debug("config USBPD to DP done\n"); pr_debug("Configure: config USBPD to DP done\n"); if (dp_drv->alt_mode.dp_status.hpd_high) mdss_dp_host_init(&dp_drv->panel_data); Loading Loading @@ -2259,7 +2622,10 @@ static int mdss_dp_probe(struct platform_device *pdev) dp_drv->inited = true; dp_drv->wait_for_audio_comp = false; dp_drv->hpd_irq_on = false; mdss_dp_reset_test_data(dp_drv); init_completion(&dp_drv->audio_comp); init_completion(&dp_drv->irq_comp); pr_debug("done\n"); Loading @@ -2280,6 +2646,53 @@ probe_err: } void *mdss_dp_get_hdcp_data(struct device *dev) { struct mdss_dp_drv_pdata *dp_drv = NULL; if (!dev) { pr_err("%s:Invalid input\n", __func__); return NULL; } dp_drv = dev_get_drvdata(dev); if (!dp_drv) { pr_err("%s:Invalid dp driver\n", __func__); return NULL; } return dp_drv->hdcp.data; } static inline bool dp_is_hdcp_enabled(struct mdss_dp_drv_pdata *dp_drv) { return dp_drv->hdcp.feature_enabled && (dp_drv->hdcp.hdcp1_present || dp_drv->hdcp.hdcp2_present) && dp_drv->hdcp.ops; } static inline bool dp_is_stream_shareable(struct mdss_dp_drv_pdata *dp_drv) { bool ret = 0; switch (dp_drv->hdcp.enc_lvl) { case HDCP_STATE_AUTH_ENC_NONE: ret = true; break; case HDCP_STATE_AUTH_ENC_1X: ret = dp_is_hdcp_enabled(dp_drv) && dp_drv->hdcp.auth_state; break; case HDCP_STATE_AUTH_ENC_2P2: ret = dp_drv->hdcp.feature_enabled && dp_drv->hdcp.hdcp2_present && dp_drv->hdcp.auth_state; break; default: ret = false; } return ret; } static const struct of_device_id msm_mdss_dp_dt_match[] = { {.compatible = "qcom,mdss-dp"}, {} Loading