diff options
43 files changed, 2091 insertions, 275 deletions
diff --git a/Documentation/devicetree/bindings/fb/mdss-mdp.txt b/Documentation/devicetree/bindings/fb/mdss-mdp.txt index c8cf395d5669..389c2c32fd5b 100644 --- a/Documentation/devicetree/bindings/fb/mdss-mdp.txt +++ b/Documentation/devicetree/bindings/fb/mdss-mdp.txt @@ -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 diff --git a/arch/arm/boot/dts/qcom/msmcobalt-cdp.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-cdp.dtsi index fcceac6e2469..4822823aa63f 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-cdp.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-cdp.dtsi @@ -302,6 +302,10 @@ qpnp,qpnp-labibb-mode = "lcd"; }; +&pmicobalt_wled { + qcom,led-strings-list = [00 01]; +}; + &dsi_dual_nt35597_video { qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; qcom,mdss-dsi-bl-min-level = <1>; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi index ec38e46b1d89..9c72ebf4a0bd 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi @@ -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>; }; }; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi index 7948dc3489cb..b77bab712ecf 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi @@ -355,6 +355,10 @@ qpnp,qpnp-labibb-mode = "lcd"; }; +&pmicobalt_wled { + qcom,led-strings-list = [00 01]; +}; + &dsi_dual_nt35597_video { qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; qcom,mdss-dsi-bl-min-level = <1>; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-qrd.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-qrd.dtsi index 51e1154beaa9..7d5509f0016c 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-qrd.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-qrd.dtsi @@ -263,6 +263,10 @@ qpnp,qpnp-labibb-mode = "lcd"; }; +&pmicobalt_wled { + qcom,led-strings-list = [00 01]; +}; + &dsi_dual_nt35597_video { qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; qcom,mdss-dsi-bl-min-level = <1>; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi index 8da491cea8dc..cde74aa2866d 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi @@ -607,22 +607,22 @@ 0 0 1627 0 1578 1353 0 0>; qcom,cpr-ro-scaling-factor = - < 0 0 0 0 3005 3111 0 0 - 0 0 3487 0 3280 1896 1874 0>, - < 0 0 0 0 3005 3111 0 0 - 0 0 3487 0 3280 1896 1874 0>, - < 0 0 0 0 3005 3111 0 0 - 0 0 3487 0 3280 1896 1874 0>, - < 0 0 0 0 3005 3111 0 0 - 0 0 3487 0 3280 1896 1874 0>, - < 0 0 0 0 3005 3111 0 0 - 0 0 3487 0 3280 1896 1874 0>, - < 0 0 0 0 3005 3111 0 0 - 0 0 3487 0 3280 1896 1874 0>, - < 0 0 0 0 3005 3111 0 0 - 0 0 3487 0 3280 1896 1874 0>, - < 0 0 0 0 3005 3111 0 0 - 0 0 3487 0 3280 1896 1874 0>; + < 0 0 0 0 2377 2571 0 0 + 0 0 2168 0 2209 1849 1997 0>, + < 0 0 0 0 2377 2571 0 0 + 0 0 2168 0 2209 1849 1997 0>, + < 0 0 0 0 2377 2571 0 0 + 0 0 2168 0 2209 1849 1997 0>, + < 0 0 0 0 2377 2571 0 0 + 0 0 2168 0 2209 1849 1997 0>, + < 0 0 0 0 2377 2571 0 0 + 0 0 2168 0 2209 1849 1997 0>, + < 0 0 0 0 2377 2571 0 0 + 0 0 2168 0 2209 1849 1997 0>, + < 0 0 0 0 2377 2571 0 0 + 0 0 2168 0 2209 1849 1997 0>, + < 0 0 0 0 2377 2571 0 0 + 0 0 2168 0 2209 1849 1997 0>; qcom,cpr-open-loop-voltage-fuse-adjustment = < 100000 0 0 0>, diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 1c625764133d..97a604755053 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1449,7 +1449,7 @@ static struct clk_core *clk_propagate_rate_change(struct clk_core *core, * walk down a subtree and set the new rates notifying the rate * change on the way */ -static void clk_change_rate(struct clk_core *core) +static int clk_change_rate(struct clk_core *core) { struct clk_core *child; struct hlist_node *tmp; @@ -1457,6 +1457,7 @@ static void clk_change_rate(struct clk_core *core) unsigned long best_parent_rate = 0; bool skip_set_rate = false; struct clk_core *old_parent; + int rc = 0; old_rate = core->rate; @@ -1484,8 +1485,12 @@ static void clk_change_rate(struct clk_core *core) trace_clk_set_rate(core, core->new_rate); - if (!skip_set_rate && core->ops->set_rate) - core->ops->set_rate(core->hw, core->new_rate, best_parent_rate); + if (!skip_set_rate && core->ops->set_rate) { + rc = core->ops->set_rate(core->hw, core->new_rate, + best_parent_rate); + if (rc) + goto out; + } trace_clk_set_rate_complete(core, core->new_rate); @@ -1511,6 +1516,13 @@ static void clk_change_rate(struct clk_core *core) /* handle the new child who might not be in core->children yet */ if (core->new_child) clk_change_rate(core->new_child); + + return rc; + +out: + trace_clk_set_rate_complete(core, core->new_rate); + + return rc; } static int clk_core_set_rate_nolock(struct clk_core *core, @@ -1545,7 +1557,13 @@ static int clk_core_set_rate_nolock(struct clk_core *core, } /* change the rates */ - clk_change_rate(top); + ret = clk_change_rate(top); + if (ret) { + pr_err("%s: failed to set %s rate\n", __func__, + top->name); + clk_propagate_rate_change(top, ABORT_RATE_CHANGE); + return ret; + } core->req_rate = req_rate; diff --git a/drivers/clk/qcom/clk-branch.c b/drivers/clk/qcom/clk-branch.c index 0987c8e7f807..8aea1d519311 100644 --- a/drivers/clk/qcom/clk-branch.c +++ b/drivers/clk/qcom/clk-branch.c @@ -83,9 +83,6 @@ static int clk_branch_wait(const struct clk_branch *br, bool enabling, if (br->halt_check == BRANCH_HALT_DELAY || (!enabling && voted)) { udelay(10); - } else if ((br->halt_check == BRANCH_HALT_NO_CHECK_ON_DISABLE) && - !enabling) { - return 0; } else if (br->halt_check == BRANCH_HALT_ENABLE || br->halt_check == BRANCH_HALT || (enabling && voted)) { diff --git a/drivers/clk/qcom/clk-branch.h b/drivers/clk/qcom/clk-branch.h index 331f58d651e5..8a934cf8bed1 100644 --- a/drivers/clk/qcom/clk-branch.h +++ b/drivers/clk/qcom/clk-branch.h @@ -42,10 +42,6 @@ struct clk_branch { #define BRANCH_HALT_ENABLE 1 /* pol: 0 = halt */ #define BRANCH_HALT_ENABLE_VOTED (BRANCH_HALT_ENABLE | BRANCH_VOTED) #define BRANCH_HALT_DELAY 2 /* No bit to check; just delay */ -/* No halt check during clk disable for the clocks controlled by other masters - * via voting registers like SMMU clocks. - */ -#define BRANCH_HALT_NO_CHECK_ON_DISABLE 4 struct clk_regmap clkr; }; diff --git a/drivers/clk/qcom/gcc-msm8996.c b/drivers/clk/qcom/gcc-msm8996.c index a7b8ac07e73a..0f39bf278cd4 100644 --- a/drivers/clk/qcom/gcc-msm8996.c +++ b/drivers/clk/qcom/gcc-msm8996.c @@ -1253,7 +1253,7 @@ static struct clk_branch gcc_mmss_noc_cfg_ahb_clk = { static struct clk_branch gcc_mmss_bimc_gfx_clk = { .halt_reg = 0x9010, - .halt_check = BRANCH_HALT_NO_CHECK_ON_DISABLE, + .halt_check = BRANCH_VOTED, .clkr = { .enable_reg = 0x9010, .enable_mask = BIT(0), @@ -2692,7 +2692,7 @@ static struct clk_branch gcc_aggre0_cnoc_ahb_clk = { static struct clk_branch gcc_smmu_aggre0_axi_clk = { .halt_reg = 0x81014, - .halt_check = BRANCH_HALT_NO_CHECK_ON_DISABLE, + .halt_check = BRANCH_VOTED, .clkr = { .enable_reg = 0x81014, .enable_mask = BIT(0), @@ -2717,7 +2717,7 @@ static struct clk_gate2 gcc_aggre0_noc_qosgen_extref_clk = { static struct clk_branch gcc_smmu_aggre0_ahb_clk = { .halt_reg = 0x81018, - .halt_check = BRANCH_HALT_NO_CHECK_ON_DISABLE, + .halt_check = BRANCH_VOTED, .clkr = { .enable_reg = 0x81018, .enable_mask = BIT(0), @@ -2871,7 +2871,7 @@ static struct clk_branch gcc_rx1_usb2_clkref_clk = { static struct clk_branch hlos1_vote_lpass_core_smmu_clk = { .halt_reg = 0x7d010, - .halt_check = BRANCH_HALT_NO_CHECK_ON_DISABLE, + .halt_check = BRANCH_VOTED, .clkr = { .enable_reg = 0x7d010, .enable_mask = BIT(0), @@ -2884,7 +2884,7 @@ static struct clk_branch hlos1_vote_lpass_core_smmu_clk = { static struct clk_branch hlos1_vote_lpass_adsp_smmu_clk = { .halt_reg = 0x7d014, - .halt_check = BRANCH_HALT_NO_CHECK_ON_DISABLE, + .halt_check = BRANCH_VOTED, .clkr = { .enable_reg = 0x7d014, .enable_mask = BIT(0), diff --git a/drivers/clk/qcom/gcc-msmfalcon.c b/drivers/clk/qcom/gcc-msmfalcon.c index 2cbc9dff047b..42b91d70aa54 100644 --- a/drivers/clk/qcom/gcc-msmfalcon.c +++ b/drivers/clk/qcom/gcc-msmfalcon.c @@ -1173,7 +1173,7 @@ static struct clk_branch gcc_aggre2_usb3_axi_clk = { static struct clk_branch gcc_bimc_gfx_clk = { .halt_reg = 0x7106c, - .halt_check = BRANCH_HALT_NO_CHECK_ON_DISABLE, + .halt_check = BRANCH_VOTED, .clkr = { .enable_reg = 0x7106c, .enable_mask = BIT(0), @@ -1711,7 +1711,7 @@ static struct clk_branch gcc_gp3_clk = { static struct clk_branch gcc_gpu_bimc_gfx_clk = { .halt_reg = 0x71010, - .halt_check = BRANCH_HALT_NO_CHECK_ON_DISABLE, + .halt_check = BRANCH_VOTED, .clkr = { .enable_reg = 0x71010, .enable_mask = BIT(0), @@ -1737,7 +1737,7 @@ static struct clk_branch gcc_gpu_bimc_gfx_src_clk = { static struct clk_branch gcc_gpu_cfg_ahb_clk = { .halt_reg = 0x71004, - .halt_check = BRANCH_HALT_NO_CHECK_ON_DISABLE, + .halt_check = BRANCH_VOTED, .clkr = { .enable_reg = 0x71004, .enable_mask = BIT(0), @@ -2516,7 +2516,7 @@ static struct clk_branch gcc_usb_phy_cfg_ahb2phy_clk = { static struct clk_branch hlos1_vote_lpass_adsp_smmu_clk = { .halt_reg = 0x7d014, - .halt_check = BRANCH_HALT_NO_CHECK_ON_DISABLE, + .halt_check = BRANCH_VOTED, .clkr = { .enable_reg = 0x7d014, .enable_mask = BIT(0), @@ -2529,7 +2529,7 @@ static struct clk_branch hlos1_vote_lpass_adsp_smmu_clk = { static struct clk_branch hlos1_vote_turing_adsp_smmu_clk = { .halt_reg = 0x7d048, - .halt_check = BRANCH_HALT_NO_CHECK_ON_DISABLE, + .halt_check = BRANCH_VOTED, .clkr = { .enable_reg = 0x7d048, .enable_mask = BIT(0), @@ -2542,7 +2542,7 @@ static struct clk_branch hlos1_vote_turing_adsp_smmu_clk = { static struct clk_branch hlos2_vote_turing_adsp_smmu_clk = { .halt_reg = 0x7e048, - .halt_check = BRANCH_HALT_NO_CHECK_ON_DISABLE, + .halt_check = BRANCH_VOTED, .clkr = { .enable_reg = 0x7e048, .enable_mask = BIT(0), diff --git a/drivers/cpuidle/lpm-levels-of.c b/drivers/cpuidle/lpm-levels-of.c index b40231dd8dd1..ffbfd1c11af9 100644 --- a/drivers/cpuidle/lpm-levels-of.c +++ b/drivers/cpuidle/lpm-levels-of.c @@ -748,7 +748,7 @@ static int calculate_residency(struct power_params *base_pwr, residency /= (int32_t)(base_pwr->ss_power - next_pwr->ss_power); if (residency < 0) { - __WARN_printf("%s: Incorrect power attributes for LPM\n", + pr_err("%s: residency < 0 for LPM\n", __func__); return next_pwr->time_overhead_us; } diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 702706ae60f7..fda10abae58a 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -1870,6 +1870,10 @@ free_irqs: arm_smmu_free_context_idx(smmu, cfg->cbndx); smmu_domain->smmu = NULL; + cfg->cbndx = INVALID_CBNDX; + cfg->irptndx = INVALID_IRPTNDX; + cfg->asid = INVALID_ASID; + cfg->vmid = INVALID_VMID; } static struct iommu_domain *arm_smmu_domain_alloc(unsigned type) diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c index 98b29cc3a9e3..9e68af87b86c 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c @@ -1747,7 +1747,7 @@ static int msm_vfe40_axi_halt(struct vfe_device *vfe_dev, msm_camera_io_w(0xFEFFFEFF, vfe_dev->vfe_base + 0x34); msm_camera_io_w(0x1, vfe_dev->vfe_base + 0x24); - msm_isp_get_timestamp(&ts); + msm_isp_get_timestamp(&ts, vfe_dev); /* if any stream is waiting for update, signal complete */ for (i = VFE_PIX_0; i <= VFE_RAW_2; i++) { msm_isp_axi_stream_update(vfe_dev, i, &ts); diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c index 0e14e0957a2a..fb4f7a1dcc92 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c @@ -1377,7 +1377,7 @@ static int msm_vfe44_axi_halt(struct vfe_device *vfe_dev, msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x2C0); } - msm_isp_get_timestamp(&ts); + msm_isp_get_timestamp(&ts, vfe_dev); for (i = VFE_PIX_0; i <= VFE_RAW_2; i++) { /* if any stream is waiting for update, signal complete */ msm_isp_axi_stream_update(vfe_dev, i, &ts); diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c index 5237b84e9477..d45b6ff0a7d0 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c @@ -1464,7 +1464,7 @@ static int msm_vfe46_axi_halt(struct vfe_device *vfe_dev, msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x374); } - msm_isp_get_timestamp(&ts); + msm_isp_get_timestamp(&ts, vfe_dev); for (i = VFE_PIX_0; i <= VFE_RAW_2; i++) { msm_isp_axi_stream_update(vfe_dev, i, &ts); msm_isp_axi_stream_update(vfe_dev, i, &ts); diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c index df7e2a88e7ca..6d1ad8ef6804 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c @@ -1798,7 +1798,7 @@ int msm_vfe47_axi_halt(struct vfe_device *vfe_dev, msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x400); } - msm_isp_get_timestamp(&ts); + msm_isp_get_timestamp(&ts, vfe_dev); for (i = VFE_PIX_0; i <= VFE_RAW_2; i++) { /* if any stream is waiting for update, signal fake completes */ msm_isp_axi_stream_update(vfe_dev, i, &ts); diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c index 7488f371545b..fbc2fee5a51d 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c @@ -1060,7 +1060,7 @@ void msm_isp_start_avtimer(void) avcs_core_disable_power_collapse(1); } -static inline void msm_isp_get_avtimer_ts( +void msm_isp_get_avtimer_ts( struct msm_isp_timestamp *time_stamp) { int rc = 0; @@ -1088,7 +1088,7 @@ void msm_isp_start_avtimer(void) pr_err("AV Timer is not supported\n"); } -static inline void msm_isp_get_avtimer_ts( +void msm_isp_get_avtimer_ts( struct msm_isp_timestamp *time_stamp) { pr_err_ratelimited("%s: Error: AVTimer driver not available\n", @@ -2282,7 +2282,7 @@ int msm_isp_axi_reset(struct vfe_device *vfe_dev, update_vfes[vfe_dev->pdev->id] = vfe_dev; } - msm_isp_get_timestamp(×tamp); + msm_isp_get_timestamp(×tamp, vfe_dev); for (k = 0; k < MAX_VFE; k++) { vfe_dev = update_vfes[k]; @@ -2700,8 +2700,8 @@ static int __msm_isp_check_stream_state(struct msm_vfe_axi_stream *stream_info, } -static void __msm_isp_stop_axi_streams(struct msm_vfe_axi_stream **streams, - int num_streams, int cmd_type) +static void __msm_isp_stop_axi_streams(struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream **streams, int num_streams, int cmd_type) { int i; struct msm_vfe_axi_shared_data *axi_data; @@ -2712,11 +2712,10 @@ static void __msm_isp_stop_axi_streams(struct msm_vfe_axi_stream **streams, unsigned long flags; uint32_t intf; int rc; - struct vfe_device *vfe_dev; struct vfe_device *update_vfes[MAX_VFE] = {0, 0}; int k; - msm_isp_get_timestamp(×tamp); + msm_isp_get_timestamp(×tamp, vfe_dev); for (i = 0; i < num_streams; i++) { stream_info = streams[i]; @@ -2881,7 +2880,7 @@ static int msm_isp_start_axi_stream(struct vfe_device *vfe_dev_ioctl, if (stream_cfg_cmd->num_streams > MAX_NUM_STREAM) return -EINVAL; - msm_isp_get_timestamp(×tamp); + msm_isp_get_timestamp(×tamp, vfe_dev_ioctl); for (i = 0; i < stream_cfg_cmd->num_streams; i++) { if (stream_cfg_cmd->stream_handle[i] == 0) @@ -3005,7 +3004,7 @@ static int msm_isp_start_axi_stream(struct vfe_device *vfe_dev_ioctl, return 0; error: - __msm_isp_stop_axi_streams(streams, num_streams, + __msm_isp_stop_axi_streams(vfe_dev_ioctl, streams, num_streams, STOP_STREAM); return rc; @@ -3043,7 +3042,7 @@ static int msm_isp_stop_axi_stream(struct vfe_device *vfe_dev_ioctl, } streams[num_streams++] = stream_info; } - __msm_isp_stop_axi_streams(streams, num_streams, + __msm_isp_stop_axi_streams(vfe_dev_ioctl, streams, num_streams, stream_cfg_cmd->cmd); return rc; @@ -3172,7 +3171,7 @@ static int msm_isp_return_empty_buffer(struct vfe_device *vfe_dev, return rc; } - msm_isp_get_timestamp(×tamp); + msm_isp_get_timestamp(×tamp, vfe_dev); buf->buf_debug.put_state[buf->buf_debug.put_state_last] = MSM_ISP_BUFFER_STATE_DROP_REG; buf->buf_debug.put_state_last ^= 1; @@ -3576,7 +3575,7 @@ int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg) stream_info = msm_isp_get_stream_common_data(vfe_dev, HANDLE_TO_IDX(update_info->stream_handle)); stream_info->buf_divert = 0; - msm_isp_get_timestamp(×tamp); + msm_isp_get_timestamp(×tamp, vfe_dev); frame_id = vfe_dev->axi_data.src_info[ SRC_TO_INTF(stream_info->stream_src)].frame_id; /* set ping pong address to scratch before flush */ diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h index 84720f3d8625..9c642370b1a1 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h @@ -27,6 +27,7 @@ void msm_isp_reset_framedrop(struct vfe_device *vfe_dev, struct msm_vfe_axi_stream *stream_info); int msm_isp_request_axi_stream(struct vfe_device *vfe_dev, void *arg); +void msm_isp_get_avtimer_ts(struct msm_isp_timestamp *time_stamp); int msm_isp_cfg_axi_stream(struct vfe_device *vfe_dev, void *arg); int msm_isp_release_axi_stream(struct vfe_device *vfe_dev, void *arg); int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg); diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c index afa498f80928..f1103183c326 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c @@ -852,7 +852,7 @@ int msm_isp_stats_reset(struct vfe_device *vfe_dev) unsigned long flags; int k; - msm_isp_get_timestamp(×tamp); + msm_isp_get_timestamp(×tamp, vfe_dev); if (vfe_dev->is_split) { for (i = 0; i < MAX_VFE; i++) @@ -1077,7 +1077,7 @@ static int msm_isp_start_stats_stream(struct vfe_device *vfe_dev_ioctl, uint32_t num_active_streams[MAX_VFE] = {0, 0}; struct vfe_device *vfe_dev; - msm_isp_get_timestamp(×tamp); + msm_isp_get_timestamp(×tamp, vfe_dev_ioctl); num_stats_comp_mask = vfe_dev_ioctl->hw_info->stats_hw_info->num_stats_comp_mask; @@ -1164,7 +1164,7 @@ static int msm_isp_stop_stats_stream(struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *streams[MSM_ISP_STATS_MAX]; unsigned long flags; - msm_isp_get_timestamp(×tamp); + msm_isp_get_timestamp(×tamp, vfe_dev); num_stats_comp_mask = vfe_dev->hw_info->stats_hw_info->num_stats_comp_mask; diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c index 71c907f2b381..b0789ce4a71c 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c @@ -198,13 +198,21 @@ uint32_t msm_isp_get_framedrop_period( return 1; } -void msm_isp_get_timestamp(struct msm_isp_timestamp *time_stamp) +void msm_isp_get_timestamp(struct msm_isp_timestamp *time_stamp, + struct vfe_device *vfe_dev) { struct timespec ts; - get_monotonic_boottime(&ts); - time_stamp->buf_time.tv_sec = ts.tv_sec; - time_stamp->buf_time.tv_usec = ts.tv_nsec/1000; do_gettimeofday(&(time_stamp->event_time)); + if (vfe_dev->vt_enable) { + msm_isp_get_avtimer_ts(time_stamp); + time_stamp->buf_time.tv_sec = time_stamp->vt_time.tv_sec; + time_stamp->buf_time.tv_usec = time_stamp->vt_time.tv_usec; + } else { + get_monotonic_boottime(&ts); + time_stamp->buf_time.tv_sec = ts.tv_sec; + time_stamp->buf_time.tv_usec = ts.tv_nsec/1000; + } + } static inline u32 msm_isp_evt_mask_to_isp_event(u32 evt_mask) @@ -1820,7 +1828,8 @@ static void msm_isp_enqueue_tasklet_cmd(struct vfe_device *vfe_dev, } queue_cmd->vfeInterruptStatus0 = irq_status0; queue_cmd->vfeInterruptStatus1 = irq_status1; - msm_isp_get_timestamp(&queue_cmd->ts); + msm_isp_get_timestamp(&queue_cmd->ts, vfe_dev); + queue_cmd->cmd_used = 1; vfe_dev->taskletq_idx = (vfe_dev->taskletq_idx + 1) % MSM_VFE_TASKLETQ_SIZE; diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.h index 16e3198f35b7..f4280581a730 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.h +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.h @@ -70,5 +70,6 @@ void msm_isp_fetch_engine_done_notify(struct vfe_device *vfe_dev, struct msm_vfe_fetch_engine_info *fetch_engine_info); void msm_isp_print_fourcc_error(const char *origin, uint32_t fourcc_format); void msm_isp_flush_tasklet(struct vfe_device *vfe_dev); -void msm_isp_get_timestamp(struct msm_isp_timestamp *time_stamp); +void msm_isp_get_timestamp(struct msm_isp_timestamp *time_stamp, + struct vfe_device *vfe_dev); #endif /* __MSM_ISP_UTIL_H__ */ diff --git a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c index bf3973888573..a700f836061c 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c +++ b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c @@ -1559,11 +1559,13 @@ static long msm_actuator_subdev_ioctl(struct v4l2_subdev *sd, pr_err("a_ctrl->i2c_client.i2c_func_tbl NULL\n"); return -EINVAL; } + mutex_lock(a_ctrl->actuator_mutex); rc = msm_actuator_power_down(a_ctrl); if (rc < 0) { pr_err("%s:%d Actuator Power down failed\n", __func__, __LINE__); } + mutex_unlock(a_ctrl->actuator_mutex); return msm_actuator_close(sd, NULL); default: return -ENOIOCTLCMD; diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_1_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_1_hwreg.h index bba0b2bc9cdb..07d522cb01bb 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_1_hwreg.h +++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_1_hwreg.h @@ -105,11 +105,11 @@ struct csiphy_settings_t csiphy_combo_mode_v5_0_1 = { { {0x818, 0x1}, {0x81c, 0x2}, - {0x004, 0x08}, - {0x704, 0x08}, - {0x204, 0x08}, - {0x404, 0x08}, - {0x604, 0x08}, + {0x004, 0x0C}, + {0x704, 0x0C}, + {0x204, 0x0C}, + {0x404, 0x0C}, + {0x604, 0x0C}, {0x02c, 0x1}, {0x22c, 0x1}, {0x42c, 0x1}, diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_cci_i2c.c b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_cci_i2c.c index a4ee5041bfff..1a40aa1c78bd 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_cci_i2c.c +++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_cci_i2c.c @@ -69,6 +69,12 @@ int32_t msm_camera_cci_i2c_read_seq(struct msm_camera_i2c_client *client, || num_byte == 0) return rc; + if (num_byte > I2C_REG_DATA_MAX) { + S_I2C_DBG("%s: Error num_byte:0x%x exceeds max:0x%x\n", + __func__, num_byte, I2C_REG_DATA_MAX); + return rc; + } + buf = kzalloc(num_byte, GFP_KERNEL); if (!buf) { pr_err("%s:%d no memory\n", __func__, __LINE__); diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_qup_i2c.c b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_qup_i2c.c index 7a0fb97061d5..62c5cf8c91d9 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_qup_i2c.c +++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_qup_i2c.c @@ -73,7 +73,7 @@ int32_t msm_camera_qup_i2c_read(struct msm_camera_i2c_client *client, enum msm_camera_i2c_data_type data_type) { int32_t rc = -EFAULT; - unsigned char buf[client->addr_type+data_type]; + unsigned char *buf = NULL; if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR && client->addr_type != MSM_CAMERA_I2C_WORD_ADDR) @@ -81,6 +81,17 @@ int32_t msm_camera_qup_i2c_read(struct msm_camera_i2c_client *client, && data_type != MSM_CAMERA_I2C_WORD_DATA)) return rc; + if (client->addr_type > UINT_MAX - data_type) { + S_I2C_DBG("%s: integer overflow prevented\n", __func__); + return rc; + } + + buf = kzalloc(client->addr_type+data_type, GFP_KERNEL); + if (!buf) { + S_I2C_DBG("%s:%d no memory\n", __func__, __LINE__); + return -ENOMEM; + } + if (client->addr_type == MSM_CAMERA_I2C_BYTE_ADDR) { buf[0] = addr; } else if (client->addr_type == MSM_CAMERA_I2C_WORD_ADDR) { @@ -90,6 +101,8 @@ int32_t msm_camera_qup_i2c_read(struct msm_camera_i2c_client *client, rc = msm_camera_qup_i2c_rxdata(client, buf, data_type); if (rc < 0) { S_I2C_DBG("%s fail\n", __func__); + kfree(buf); + buf = NULL; return rc; } @@ -99,6 +112,8 @@ int32_t msm_camera_qup_i2c_read(struct msm_camera_i2c_client *client, *data = buf[0] << 8 | buf[1]; S_I2C_DBG("%s addr = 0x%x data: 0x%x\n", __func__, addr, *data); + kfree(buf); + buf = NULL; return rc; } @@ -106,7 +121,7 @@ int32_t msm_camera_qup_i2c_read_seq(struct msm_camera_i2c_client *client, uint32_t addr, uint8_t *data, uint32_t num_byte) { int32_t rc = -EFAULT; - unsigned char buf[client->addr_type+num_byte]; + unsigned char *buf = NULL; int i; if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR @@ -114,6 +129,22 @@ int32_t msm_camera_qup_i2c_read_seq(struct msm_camera_i2c_client *client, || num_byte == 0) return rc; + if (num_byte > I2C_REG_DATA_MAX) { + S_I2C_DBG("%s: Error num_byte:0x%x exceeds max:0x%x\n", + __func__, num_byte, I2C_REG_DATA_MAX); + return rc; + } + if (client->addr_type > UINT_MAX - num_byte) { + S_I2C_DBG("%s: integer overflow prevented\n", __func__); + return rc; + } + + buf = kzalloc(client->addr_type+num_byte, GFP_KERNEL); + if (!buf) { + S_I2C_DBG("%s:%d no memory\n", __func__, __LINE__); + return -ENOMEM; + } + if (client->addr_type == MSM_CAMERA_I2C_BYTE_ADDR) { buf[0] = addr; } else if (client->addr_type == MSM_CAMERA_I2C_WORD_ADDR) { @@ -123,6 +154,8 @@ int32_t msm_camera_qup_i2c_read_seq(struct msm_camera_i2c_client *client, rc = msm_camera_qup_i2c_rxdata(client, buf, num_byte); if (rc < 0) { S_I2C_DBG("%s fail\n", __func__); + kfree(buf); + buf = NULL; return rc; } @@ -132,6 +165,8 @@ int32_t msm_camera_qup_i2c_read_seq(struct msm_camera_i2c_client *client, S_I2C_DBG("Byte %d: 0x%x\n", i, buf[i]); S_I2C_DBG("Data: 0x%x\n", data[i]); } + kfree(buf); + buf = NULL; return rc; } diff --git a/drivers/media/platform/msm/vidc/msm_smem.c b/drivers/media/platform/msm/vidc/msm_smem.c index bb2715654f45..90047a608984 100644 --- a/drivers/media/platform/msm/vidc/msm_smem.c +++ b/drivers/media/platform/msm/vidc/msm_smem.c @@ -475,7 +475,7 @@ bool msm_smem_compare_buffers(void *clt, int fd, void *priv) } handle = ion_import_dma_buf(client->clnt, fd); ret = handle == priv; - handle ? ion_free(client->clnt, handle) : 0; + (!IS_ERR_OR_NULL(handle)) ? ion_free(client->clnt, handle) : 0; return ret; } diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c index 8332c7f4db43..c87b6fc585c7 100644 --- a/drivers/media/platform/msm/vidc/venus_hfi.c +++ b/drivers/media/platform/msm/vidc/venus_hfi.c @@ -4263,7 +4263,7 @@ static inline int __suspend(struct venus_hfi_device *device) return 0; } - dprintk(VIDC_DBG, "Entering power collapse\n"); + dprintk(VIDC_PROF, "Entering power collapse\n"); if (device->res->pm_qos_latency_us && pm_qos_request_active(&device->qos)) @@ -4276,7 +4276,7 @@ static inline int __suspend(struct venus_hfi_device *device) } __venus_power_off(device, true); - dprintk(VIDC_INFO, "Venus power collapsed\n"); + dprintk(VIDC_PROF, "Venus power collapsed\n"); return rc; err_tzbsp_suspend: @@ -4298,7 +4298,7 @@ static inline int __resume(struct venus_hfi_device *device) return -EINVAL; } - dprintk(VIDC_DBG, "Resuming from power collapse\n"); + dprintk(VIDC_PROF, "Resuming from power collapse\n"); rc = __venus_power_on(device); if (rc) { dprintk(VIDC_ERR, "Failed to power on venus\n"); @@ -4334,7 +4334,7 @@ static inline int __resume(struct venus_hfi_device *device) pm_qos_add_request(&device->qos, PM_QOS_CPU_DMA_LATENCY, device->res->pm_qos_latency_us); } - dprintk(VIDC_INFO, "Resumed from power collapse\n"); + dprintk(VIDC_PROF, "Resumed from power collapse\n"); exit: device->skip_pc_count = 0; return rc; diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index 5e7f5c8bd2a1..bd7fc82afda8 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -2443,12 +2443,16 @@ static int icnss_call_driver_reinit(struct icnss_priv *priv) out: clear_bit(ICNSS_PD_RESTART, &priv->state); + icnss_pm_relax(priv); + return 0; out_power_off: icnss_hw_power_off(priv); clear_bit(ICNSS_PD_RESTART, &priv->state); + + icnss_pm_relax(priv); return ret; } @@ -2568,6 +2572,8 @@ static int icnss_call_driver_shutdown(struct icnss_priv *priv) set_bit(ICNSS_PD_RESTART, &priv->state); clear_bit(ICNSS_FW_READY, &priv->state); + icnss_pm_stay_awake(priv); + if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state)) return 0; diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c index ec9288791667..f0d5c96ac2e0 100644 --- a/drivers/tty/serial/msm_serial_hs.c +++ b/drivers/tty/serial/msm_serial_hs.c @@ -1596,6 +1596,16 @@ static void flip_insert_work(struct work_struct *work) struct tty_struct *tty = msm_uport->uport.state->port.tty; spin_lock_irqsave(&msm_uport->uport.lock, flags); + if (!tty || msm_uport->rx.flush == FLUSH_SHUTDOWN) { + dev_err(msm_uport->uport.dev, + "%s:Invalid driver state flush %d\n", + __func__, msm_uport->rx.flush); + MSM_HS_ERR("%s:Invalid driver state flush %d\n", + __func__, msm_uport->rx.flush); + spin_unlock_irqrestore(&msm_uport->uport.lock, flags); + return; + } + if (msm_uport->rx.buffer_pending == NONE_PENDING) { MSM_HS_ERR("Error: No buffer pending in %s", __func__); spin_unlock_irqrestore(&msm_uport->uport.lock, flags); @@ -1668,6 +1678,16 @@ static void msm_serial_hs_rx_work(struct kthread_work *work) spin_lock_irqsave(&uport->lock, flags); + if (!tty || rx->flush == FLUSH_SHUTDOWN) { + dev_err(uport->dev, "%s:Invalid driver state flush %d\n", + __func__, rx->flush); + MSM_HS_ERR("%s:Invalid driver state flush %d\n", + __func__, rx->flush); + spin_unlock_irqrestore(&uport->lock, flags); + msm_hs_resource_unvote(msm_uport); + return; + } + /* * Process all pending descs or if nothing is * queued - called from termios @@ -3664,12 +3684,12 @@ static void msm_hs_shutdown(struct uart_port *uport) if (msm_uport->rx.flush != FLUSH_SHUTDOWN) { /* disable and disconnect rx */ - msm_hs_disconnect_rx(uport); ret = wait_event_timeout(msm_uport->rx.wait, - msm_uport->rx.flush == FLUSH_SHUTDOWN, 500); + !msm_uport->rx.pending_flag, 500); if (!ret) MSM_HS_WARN("%s(): rx disconnect not complete", __func__); + msm_hs_disconnect_rx(uport); } cancel_delayed_work_sync(&msm_uport->rx.flip_insert_work); diff --git a/drivers/video/fbdev/msm/Makefile b/drivers/video/fbdev/msm/Makefile index 6009a9b97d1c..b905c0e855dd 100644 --- a/drivers/video/fbdev/msm/Makefile +++ b/drivers/video/fbdev/msm/Makefile @@ -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 diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c index b246204f3181..516cbdc9192b 100644 --- a/drivers/video/fbdev/msm/mdss_dp.c +++ b/drivers/video/fbdev/msm/mdss_dp.c @@ -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 @@ -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); @@ -1038,115 +1050,234 @@ 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) { - 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; + pr_debug("enter: orientation = %d\n", orientation); + + if (!lane_map) { + pr_err("invalid lane map input"); + ret = -EINVAL; + goto exit; } - dp_drv = container_of(pdata, struct mdss_dp_drv_pdata, - panel_data); + /* 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) +{ + 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 */ - ret = mdss_dp_clk_ctrl(dp_drv, DP_CORE_PM, true); - if (ret) { - pr_err("Unabled to start core clocks\n"); - goto exit; - } - mdss_dp_hpd_configure(&dp_drv->ctrl_io, true); - - 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"); - } + pr_debug("enter\n"); - } + orientation = usbpd_get_plug_orientation(dp_drv->pd); + pr_debug("plug orientation = %d\n", orientation); - if (dp_drv->new_vic && (dp_drv->new_vic != dp_drv->vic)) - dp_init_panel_info(dp_drv, dp_drv->new_vic); + ret = mdss_dp_get_lane_mapping(dp_drv, orientation, &ln_map); + if (ret) + goto exit; - dp_drv->link_rate = - mdss_dp_gen_link_clk(&dp_drv->panel_data.panel_info, - dp_drv->dpcd.max_lane_count); + mdss_dp_phy_share_lane_config(&dp_drv->phy_io, + orientation, dp_drv->dpcd.max_lane_count); - pr_debug("link_rate=0x%x, Max rate supported by sink=0x%x\n", - dp_drv->link_rate, dp_drv->dpcd.max_link_rate); - if (!dp_drv->link_rate) { - pr_err("Unable to configure required link rate\n"); - ret = -EINVAL; - goto exit; - } + ret = mdss_dp_enable_mainlink_clocks(dp_drv); + if (ret) + goto exit; + + mdss_dp_mainlink_reset(&dp_drv->ctrl_io); - mdss_dp_phy_share_lane_config(&dp_drv->phy_io, - orientation, dp_drv->dpcd.max_lane_count); + reinit_completion(&dp_drv->idle_comp); - pr_debug("link_rate = 0x%x\n", dp_drv->link_rate); + mdss_dp_configure_source_params(dp_drv, &ln_map); - dp_drv->power_data[DP_CTRL_PM].clk_config[0].rate = - ((dp_drv->link_rate * DP_LINK_RATE_MULTIPLIER) / - 1000); /* KHz */ + mdss_dp_train_main_link(dp_drv); - 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 */ + dp_drv->power_on = true; + pr_debug("end\n"); - ret = mdss_dp_clk_ctrl(dp_drv, DP_CTRL_PM, true); - if (ret) { - pr_err("Unabled to start link clocks\n"); - goto exit; - } +exit: + mutex_unlock(&dp_drv->train_mutex); + return ret; +} - mdss_dp_mainlink_reset(&dp_drv->ctrl_io); +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; - 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 { + /* wait until link training is completed */ + mutex_lock(&dp_drv->train_mutex); + + pr_debug("Enter++ cont_splash=%d\n", dp_drv->cont_splash); + + if (dp_drv->cont_splash) { mdss_dp_aux_ctrl(&dp_drv->ctrl_io, true); + goto link_training; } - pr_debug("call link_training\n"); - mdss_dp_link_train(dp_drv); + ret = mdss_dp_clk_ctrl(dp_drv, DP_CORE_PM, true); + if (ret) { + pr_err("Unabled to start core clocks\n"); + goto exit; + } + mdss_dp_hpd_configure(&dp_drv->ctrl_io, true); - mdss_dp_wait4train(dp_drv); + orientation = usbpd_get_plug_orientation(dp_drv->pd); + pr_debug("plug Orientation = %d\n", orientation); - dp_drv->cont_splash = 0; + 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); + + dp_drv->link_rate = + mdss_dp_gen_link_clk(&dp_drv->panel_data.panel_info, + dp_drv->dpcd.max_lane_count); + + pr_debug("link_rate=0x%x, Max rate supported by sink=0x%x\n", + dp_drv->link_rate, dp_drv->dpcd.max_link_rate); + if (!dp_drv->link_rate) { + pr_err("Unable to configure required link rate\n"); + ret = -EINVAL; + goto exit; + } + + mdss_dp_phy_share_lane_config(&dp_drv->phy_io, + orientation, dp_drv->dpcd.max_lane_count); + + pr_debug("link_rate = 0x%x\n", dp_drv->link_rate); + + 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); - if (mdss_dp_mainlink_ready(dp_drv, BIT(0))) - pr_debug("mainlink ready\n"); + mdss_dp_configure_source_params(dp_drv, &ln_map); + +link_training: + mdss_dp_train_main_link(dp_drv); + + dp_drv->cont_splash = 0; dp_drv->power_on = true; mdss_dp_set_audio_switch_node(dp_drv, true); @@ -1157,49 +1288,78 @@ 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; - dp_drv = container_of(pdata, struct mdss_dp_drv_pdata, - panel_data); - if (!dp_drv) { + if (!pdata) { pr_err("Invalid input data\n"); - return; + return -EINVAL; + } + + dp_drv = container_of(pdata, struct mdss_dp_drv_pdata, + panel_data); + + 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) - mdss_dp_mainlink_ctrl(&dp_drv->ctrl_io, false); + 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); @@ -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); @@ -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) { @@ -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"); @@ -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; @@ -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__); @@ -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; @@ -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; @@ -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; } @@ -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"); } @@ -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); } @@ -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) @@ -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: @@ -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; + } - pr_debug("HPD high\n"); + 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"); + + mdss_dp_update_cable_status(dp_drv, true); dp_drv->alt_mode.current_state |= DP_STATUS_DONE; @@ -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; @@ -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); @@ -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"); @@ -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"}, {} diff --git a/drivers/video/fbdev/msm/mdss_dp.h b/drivers/video/fbdev/msm/mdss_dp.h index 8d5af4dc5bf3..beeb4d4b1a91 100644 --- a/drivers/video/fbdev/msm/mdss_dp.h +++ b/drivers/video/fbdev/msm/mdss_dp.h @@ -70,8 +70,6 @@ #define EDP_INTR_ACK_SHIFT 1 #define EDP_INTR_MASK_SHIFT 2 -#define EDP_MAX_LANE 4 - /* isr */ #define EDP_INTR_HPD BIT(0) #define EDP_INTR_AUX_I2C_DONE BIT(3) @@ -105,7 +103,7 @@ EDP_INTR_FRAME_END | EDP_INTR_CRC_UPDATED) #define EDP_INTR_MASK2 (EDP_INTR_STATUS2 << 2) -#define EV_EVENT_STR(x) #x +#define DP_ENUM_STR(x) #x struct edp_buf { char *start; /* buffer start addr */ @@ -256,6 +254,18 @@ struct dpcd_link_status { char req_pre_emphasis[4]; }; +struct dpcd_test_request { + u32 test_requested; + u32 test_link_rate; + u32 test_lane_count; + u32 response; +}; + +struct dpcd_sink_count { + u32 count; + bool cp_ready; +}; + struct display_timing_desc { u32 pclk; u32 h_addressable; /* addressable + boder = active */ @@ -317,8 +327,19 @@ struct dp_statistic { u32 aux_native_rx; }; -#define DPCD_LINK_VOLTAGE_MAX 4 -#define DPCD_LINK_PRE_EMPHASIS_MAX 4 +enum dpcd_link_voltage_level { + DPCD_LINK_VOLTAGE_LEVEL_0 = 0, + DPCD_LINK_VOLTAGE_LEVEL_1 = 1, + DPCD_LINK_VOLTAGE_LEVEL_2 = 2, + DPCD_LINK_VOLTAGE_MAX = DPCD_LINK_VOLTAGE_LEVEL_2, +}; + +enum dpcd_link_preemaphasis_level { + DPCD_LINK_PRE_EMPHASIS_LEVEL_0 = 0, + DPCD_LINK_PRE_EMPHASIS_LEVEL_1 = 1, + DPCD_LINK_PRE_EMPHASIS_LEVEL_2 = 2, + DPCD_LINK_PRE_EMPHASIS_MAX = DPCD_LINK_PRE_EMPHASIS_LEVEL_2, +}; struct dp_pinctrl_res { struct pinctrl *pinctrl; @@ -329,6 +350,21 @@ struct dp_pinctrl_res { irqreturn_t dp_isr(int irq, void *ptr); +struct dp_hdcp { + void *data; + struct hdcp_ops *ops; + + void *hdcp1; + void *hdcp2; + + int enc_lvl; + + bool auth_state; + bool hdcp1_present; + bool hdcp2_present; + bool feature_enabled; +}; + struct mdss_dp_drv_pdata { /* device driver */ int (*on) (struct mdss_panel_data *pdata); @@ -337,6 +373,7 @@ struct mdss_dp_drv_pdata { struct platform_device *ext_pdev; struct usbpd *pd; + struct dp_hdcp hdcp; struct usbpd_svid_handler svid_handler; struct dp_alt_mode alt_mode; bool dp_initialized; @@ -401,6 +438,7 @@ struct mdss_dp_drv_pdata { struct completion idle_comp; struct completion video_comp; struct completion audio_comp; + struct completion irq_comp; struct mutex aux_mutex; struct mutex train_mutex; struct mutex pd_msg_mutex; @@ -426,6 +464,8 @@ struct mdss_dp_drv_pdata { u32 bpp; struct dp_statistic dp_stat; bool wait_for_audio_comp; + bool hpd_irq_on; + bool hpd_irq_toggled; /* event */ struct workqueue_struct *workq; @@ -441,10 +481,47 @@ struct mdss_dp_drv_pdata { u32 new_vic; int fb_node; - void *hdcp_data; - struct hdcp_ops *hdcp_ops; + struct dpcd_test_request test_data; + struct dpcd_sink_count sink_count; }; +enum dp_lane_count { + DP_LANE_COUNT_1 = 1, + DP_LANE_COUNT_2 = 2, + DP_LANE_COUNT_4 = 4, +}; + +enum test_response { + TEST_NACK = 0x0, + TEST_ACK = 0x1, +}; + +static inline char *mdss_dp_get_test_response(u32 test_response) +{ + switch (test_response) { + case TEST_NACK: return DP_ENUM_STR(TEST_NACK); + case TEST_ACK: return DP_ENUM_STR(TEST_ACK); + default: return "unknown"; + } +} + +enum test_type { + UNKNOWN_TEST = 0, + TEST_LINK_TRAINING = BIT(0), + TEST_PATTERN = BIT(1), + TEST_EDID_READ = BIT(2), +}; + +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_PATTERN: return DP_ENUM_STR(TEST_PATTERN); + case TEST_EDID_READ: return DP_ENUM_STR(TEST_EDID_READ); + default: return "unknown"; + } +} + static inline const char *__mdss_dp_pm_name(enum dp_pm_type module) { switch (module) { @@ -470,19 +547,19 @@ static inline char *mdss_dp_ev_event_to_string(int event) { switch (event) { case EV_EDP_AUX_SETUP: - return EV_EVENT_STR(EV_EDP_AUX_SETUP); + return DP_ENUM_STR(EV_EDP_AUX_SETUP); case EV_EDID_READ: - return EV_EVENT_STR(EV_EDID_READ); + return DP_ENUM_STR(EV_EDID_READ); case EV_DPCD_CAP_READ: - return EV_EVENT_STR(EV_DPCD_CAP_READ); + return DP_ENUM_STR(EV_DPCD_CAP_READ); case EV_DPCD_STATUS_READ: - return EV_EVENT_STR(EV_DPCD_STATUS_READ); + return DP_ENUM_STR(EV_DPCD_STATUS_READ); case EV_LINK_TRAIN: - return EV_EVENT_STR(EV_LINK_TRAIN); + return DP_ENUM_STR(EV_LINK_TRAIN); case EV_IDLE_PATTERNS_SENT: - return EV_EVENT_STR(EV_IDLE_PATTERNS_SENT); + return DP_ENUM_STR(EV_IDLE_PATTERNS_SENT); case EV_VIDEO_READY: - return EV_EVENT_STR(EV_VIDEO_READY); + return DP_ENUM_STR(EV_VIDEO_READY); default: return "unknown"; } @@ -492,6 +569,7 @@ void mdss_dp_phy_initialize(struct mdss_dp_drv_pdata *dp); void mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *dp); int mdss_dp_dpcd_status_read(struct mdss_dp_drv_pdata *dp); +void mdss_dp_aux_parse_sink_status_field(struct mdss_dp_drv_pdata *dp); int mdss_dp_edid_read(struct mdss_dp_drv_pdata *dp); int mdss_dp_link_train(struct mdss_dp_drv_pdata *dp); void dp_aux_i2c_handler(struct mdss_dp_drv_pdata *dp, u32 isr); @@ -503,5 +581,9 @@ void mdss_dp_sink_power_down(struct mdss_dp_drv_pdata *ep); void mdss_dp_lane_power_ctrl(struct mdss_dp_drv_pdata *ep, int up); void mdss_dp_config_ctrl(struct mdss_dp_drv_pdata *ep); char mdss_dp_gen_link_clk(struct mdss_panel_info *pinfo, char lane_cnt); +int mdss_dp_aux_set_sink_power_state(struct mdss_dp_drv_pdata *ep, char state); +void mdss_dp_aux_send_test_response(struct mdss_dp_drv_pdata *ep); +void *mdss_dp_get_hdcp_data(struct device *dev); +int mdss_dp_hdcp2p2_init(struct mdss_dp_drv_pdata *dp_drv); #endif /* MDSS_DP_H */ diff --git a/drivers/video/fbdev/msm/mdss_dp_aux.c b/drivers/video/fbdev/msm/mdss_dp_aux.c index 3c525b0dac4f..4d9a110cf6af 100644 --- a/drivers/video/fbdev/msm/mdss_dp_aux.c +++ b/drivers/video/fbdev/msm/mdss_dp_aux.c @@ -277,13 +277,15 @@ static int dp_aux_read_cmds(struct mdss_dp_drv_pdata *ep, wait_for_completion(&ep->aux_comp); - if (ep->aux_error_num == EDP_AUX_ERR_NONE) + if (ep->aux_error_num == EDP_AUX_ERR_NONE) { ret = dp_cmd_fifo_rx(rp, len, ep->base); - else - ret = ep->aux_error_num; - if (cmds->out_buf) - memcpy(cmds->out_buf, rp->data, cmds->len); + if (cmds->out_buf) + memcpy(cmds->out_buf, rp->data, cmds->len); + + } else { + ret = ep->aux_error_num; + } ep->aux_cmd_busy = 0; mutex_unlock(&ep->aux_mutex); @@ -952,6 +954,232 @@ static int dp_link_status_read(struct mdss_dp_drv_pdata *ep, int len) return len; } +/** + * mdss_dp_aux_send_test_response() - sends a test response to the sink + * @dp: Display Port Driver data + */ +void mdss_dp_aux_send_test_response(struct mdss_dp_drv_pdata *dp) +{ + char test_response[4]; + + test_response[0] = dp->test_data.response; + + pr_debug("sending test response %s", + mdss_dp_get_test_response(test_response[0])); + dp_aux_write_buf(dp, 0x260, test_response, 1, 0); +} + +/** + * dp_is_link_rate_valid() - validates the link rate + * @lane_rate: link rate requested by the sink + * + * Returns true if the requested link rate is supported. + */ +static bool dp_is_link_rate_valid(u32 link_rate) +{ + return (link_rate == DP_LINK_RATE_162) || + (link_rate == DP_LINK_RATE_270) || + (link_rate == DP_LINK_RATE_540); +} + +/** + * dp_is_lane_count_valid() - validates the lane count + * @lane_count: lane count requested by the sink + * + * Returns true if the requested lane count is supported. + */ +static bool dp_is_lane_count_valid(u32 lane_count) +{ + return (lane_count == DP_LANE_COUNT_1) || + (lane_count == DP_LANE_COUNT_2) || + (lane_count == DP_LANE_COUNT_4); +} + +/** + * dp_parse_link_training_params() - parses link training parameters from DPCD + * @ep: Display Port Driver data + * + * Returns 0 if it successfully parses the link rate (Byte 0x219) and lane + * count (Byte 0x220), and if these values parse are valid. + */ +static int dp_parse_link_training_params(struct mdss_dp_drv_pdata *ep) +{ + int ret = 0; + char *bp; + char data; + struct edp_buf *rp; + int rlen; + int const test_parameter_len = 0x1; + int const test_link_rate_addr = 0x219; + int const test_lane_count_addr = 0x220; + + /* Read the requested link rate (Byte 0x219). */ + rlen = dp_aux_read_buf(ep, test_link_rate_addr, + test_parameter_len, 0); + if (rlen < test_parameter_len) { + pr_err("failed to read link rate\n"); + ret = -EINVAL; + goto exit; + } + rp = &ep->rxp; + bp = rp->data; + data = *bp++; + + if (!dp_is_link_rate_valid(data)) { + pr_err("invalid link rate = 0x%x\n", data); + ret = -EINVAL; + goto exit; + } + + ep->test_data.test_link_rate = data; + pr_debug("link rate = 0x%x\n", ep->test_data.test_link_rate); + + /* Read the requested lane count (Byte 0x220). */ + rlen = dp_aux_read_buf(ep, test_lane_count_addr, + test_parameter_len, 0); + if (rlen < test_parameter_len) { + pr_err("failed to read lane count\n"); + ret = -EINVAL; + goto exit; + } + rp = &ep->rxp; + bp = rp->data; + data = *bp++; + data &= 0x1F; + + if (!dp_is_lane_count_valid(data)) { + pr_err("invalid lane count = 0x%x\n", data); + ret = -EINVAL; + goto exit; + } + + ep->test_data.test_lane_count = data; + pr_debug("lane count = 0x%x\n", ep->test_data.test_lane_count); + +exit: + return ret; +} + +/** + * dp_sink_parse_sink_count() - parses the sink count + * @ep: Display Port Driver data + * + * Parses the DPCD to check if there is an update to the sink count + * (Byte 0x200), and whether all the sink devices connected have Content + * Protection enabled. + */ +static void dp_sink_parse_sink_count(struct mdss_dp_drv_pdata *ep) +{ + char *bp; + char data; + struct edp_buf *rp; + int rlen; + int const param_len = 0x1; + int const sink_count_addr = 0x200; + + rlen = dp_aux_read_buf(ep, sink_count_addr, param_len, 0); + if (rlen < param_len) { + pr_err("failed to read sink count\n"); + return; + } + rp = &ep->rxp; + bp = rp->data; + data = *bp++; + + /* BIT 7, BIT 5:0 */ + ep->sink_count.count = (data & BIT(7)) << 6 | (data & 0x63); + /* BIT 6*/ + ep->sink_count.cp_ready = data & BIT(6); + + pr_debug("sink_count = 0x%x, cp_ready = 0x%x\n", + ep->sink_count.count, ep->sink_count.cp_ready); +} + +/** + * dp_is_test_supported() - checks if test requested by sink is supported + * @test_requested: test requested by the sink + * + * Returns true if the requested test is supported. + */ +static bool dp_is_test_supported(u32 test_requested) +{ + return test_requested == TEST_LINK_TRAINING; +} + +/** + * dp_sink_parse_test_request() - parses test request parameters from sink + * @ep: Display Port Driver data + * + * Parses the DPCD to check if an automated test is requested (Byte 0x201), + * and what type of test automation is being requested (Byte 0x218). + */ +static void dp_sink_parse_test_request(struct mdss_dp_drv_pdata *ep) +{ + int ret = 0; + char *bp; + char data; + struct edp_buf *rp; + int rlen; + int const test_parameter_len = 0x1; + int const device_service_irq_addr = 0x201; + int const test_request_addr = 0x218; + + /** + * Read the device service IRQ vector (Byte 0x201) to determine + * whether an automated test has been requested by the sink. + */ + rlen = dp_aux_read_buf(ep, device_service_irq_addr, + test_parameter_len, 0); + if (rlen < test_parameter_len) { + pr_err("failed to read device service IRQ vector\n"); + return; + } + rp = &ep->rxp; + bp = rp->data; + data = *bp++; + + pr_debug("device service irq vector = 0x%x\n", data); + + if (!(data & BIT(1))) { + pr_debug("no test requested\n"); + return; + } + + /** + * Read the test request byte (Byte 0x218) to determine what type + * of automated test has been requested by the sink. + */ + rlen = dp_aux_read_buf(ep, test_request_addr, + test_parameter_len, 0); + if (rlen < test_parameter_len) { + pr_err("failed to read test_requested\n"); + return; + } + rp = &ep->rxp; + bp = rp->data; + data = *bp++; + + if (!dp_is_test_supported(data)) { + pr_debug("test 0x%x not supported\n", data); + return; + } + + pr_debug("%s requested\n", mdss_dp_get_test_name(data)); + ep->test_data.test_requested = data; + + if (ep->test_data.test_requested == TEST_LINK_TRAINING) + ret = dp_parse_link_training_params(ep); + + /** + * Send a TEST_ACK if all test parameters are valid, otherwise send + * a TEST_NACK. + */ + if (ret) + ep->test_data.response = TEST_NACK; + else + ep->test_data.response = TEST_ACK; +} + static int dp_cap_lane_rate_set(struct mdss_dp_drv_pdata *ep) { char buf[4]; @@ -975,17 +1203,26 @@ static int dp_lane_set_write(struct mdss_dp_drv_pdata *ep, int voltage_level, { int i; char buf[4]; + u32 max_level_reached = 0; - if (voltage_level >= DPCD_LINK_VOLTAGE_MAX) - voltage_level |= 0x04; + if (voltage_level == DPCD_LINK_VOLTAGE_MAX) { + pr_debug("max. voltage swing level reached %d\n", + voltage_level); + max_level_reached |= BIT(2); + } + + if (pre_emphasis_level == DPCD_LINK_PRE_EMPHASIS_MAX) { + pr_debug("max. pre-emphasis level reached %d\n", + pre_emphasis_level); + max_level_reached |= BIT(5); + } - if (pre_emphasis_level >= DPCD_LINK_PRE_EMPHASIS_MAX) - pre_emphasis_level |= 0x04; + pr_debug("max_level_reached = 0x%x\n", max_level_reached); pre_emphasis_level <<= 3; for (i = 0; i < 4; i++) - buf[i] = voltage_level | pre_emphasis_level; + buf[i] = voltage_level | pre_emphasis_level | max_level_reached; pr_debug("p|v=0x%x", voltage_level | pre_emphasis_level); return dp_aux_write_buf(ep, 0x103, buf, 4, 0); @@ -1087,6 +1324,30 @@ void dp_sink_train_set_adjust(struct mdss_dp_drv_pdata *ep) } ep->p_level = max; + + /** + * Adjust the voltage swing and pre-emphasis level combination to within + * the allowable range. + */ + if (ep->v_level > DPCD_LINK_VOLTAGE_MAX) { + pr_debug("Requested vSwingLevel=%d, change to %d\n", + ep->v_level, DPCD_LINK_VOLTAGE_MAX); + ep->v_level = DPCD_LINK_VOLTAGE_MAX; + } + + if (ep->p_level > DPCD_LINK_PRE_EMPHASIS_MAX) { + pr_debug("Requested preEmphasisLevel=%d, change to %d\n", + ep->p_level, DPCD_LINK_PRE_EMPHASIS_MAX); + ep->p_level = DPCD_LINK_PRE_EMPHASIS_MAX; + } + + if ((ep->p_level > DPCD_LINK_PRE_EMPHASIS_LEVEL_1) + && (ep->v_level == DPCD_LINK_VOLTAGE_LEVEL_2)) { + pr_debug("Requested preEmphasisLevel=%d, change to %d\n", + ep->p_level, DPCD_LINK_PRE_EMPHASIS_LEVEL_1); + ep->p_level = DPCD_LINK_PRE_EMPHASIS_LEVEL_1; + } + pr_debug("v_level=%d, p_level=%d", ep->v_level, ep->p_level); } @@ -1300,7 +1561,7 @@ static int dp_link_rate_down_shift(struct mdss_dp_drv_pdata *ep) return -EINVAL; } -static int mdss_dp_sink_power_state(struct mdss_dp_drv_pdata *ep, char state) +int mdss_dp_aux_set_sink_power_state(struct mdss_dp_drv_pdata *ep, char state) { int ret; @@ -1332,7 +1593,7 @@ int mdss_dp_link_train(struct mdss_dp_drv_pdata *dp) dp_write(dp->base + DP_MAINLINK_CTRL, 0x1); - mdss_dp_sink_power_state(dp, SINK_POWER_ON); + mdss_dp_aux_set_sink_power_state(dp, SINK_POWER_ON); train_start: dp->v_level = 0; /* start from default level */ @@ -1387,6 +1648,13 @@ void mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *ep) dp_sink_capability_read(ep, 16); } +void mdss_dp_aux_parse_sink_status_field(struct mdss_dp_drv_pdata *ep) +{ + dp_sink_parse_sink_count(ep); + dp_sink_parse_test_request(ep); + dp_link_status_read(ep, 6); +} + int mdss_dp_dpcd_status_read(struct mdss_dp_drv_pdata *ep) { struct dpcd_link_status *sp; @@ -1428,3 +1696,36 @@ void mdss_dp_aux_init(struct mdss_dp_drv_pdata *ep) dp_buf_init(&ep->txp, ep->txbuf, sizeof(ep->txbuf)); dp_buf_init(&ep->rxp, ep->rxbuf, sizeof(ep->rxbuf)); } + +int mdss_dp_aux_read_rx_status(struct mdss_dp_drv_pdata *dp, u8 *rx_status) +{ + bool cp_irq; + int rc = 0; + + if (!dp) { + pr_err("%s Invalid input\n", __func__); + return -EINVAL; + } + + *rx_status = 0; + + rc = dp_aux_read_buf(dp, DP_DPCD_CP_IRQ, 1, 0); + if (!rc) { + pr_err("Error reading CP_IRQ\n"); + return -EINVAL; + } + + cp_irq = *dp->rxp.data & BIT(2); + + if (cp_irq) { + rc = dp_aux_read_buf(dp, DP_DPCD_RXSTATUS, 1, 0); + if (!rc) { + pr_err("Error reading RxStatus\n"); + return -EINVAL; + } + + *rx_status = *dp->rxp.data; + } + + return 0; +} diff --git a/drivers/video/fbdev/msm/mdss_dp_hdcp2p2.c b/drivers/video/fbdev/msm/mdss_dp_hdcp2p2.c new file mode 100644 index 000000000000..3891806b09bb --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_dp_hdcp2p2.c @@ -0,0 +1,861 @@ +/* Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/stat.h> +#include <linux/types.h> +#include <linux/kthread.h> + +#include <linux/hdcp_qseecom.h> +#include "mdss_hdcp.h" +#include "mdss_dp_util.h" + +enum dp_hdcp2p2_sink_status { + SINK_DISCONNECTED, + SINK_CONNECTED +}; + +enum dp_auth_status { + DP_HDCP_AUTH_STATUS_FAILURE, + DP_HDCP_AUTH_STATUS_SUCCESS +}; + +struct dp_hdcp2p2_ctrl { + atomic_t auth_state; + enum dp_hdcp2p2_sink_status sink_status; /* Is sink connected */ + struct hdcp_init_data init_data; + struct mutex mutex; /* mutex to protect access to ctrl */ + struct mutex msg_lock; /* mutex to protect access to msg buffer */ + struct mutex wakeup_mutex; /* mutex to protect access to wakeup call*/ + struct hdcp_ops *ops; + void *lib_ctx; /* Handle to HDCP 2.2 Trustzone library */ + struct hdcp_txmtr_ops *lib; /* Ops for driver to call into TZ */ + enum hdmi_hdcp_wakeup_cmd wakeup_cmd; + enum dp_auth_status auth_status; + + struct task_struct *thread; + struct kthread_worker worker; + struct kthread_work status; + struct kthread_work auth; + struct kthread_work send_msg; + struct kthread_work recv_msg; + struct kthread_work link; + struct kthread_work poll; + char *msg_buf; + uint32_t send_msg_len; /* length of all parameters in msg */ + uint32_t timeout; + uint32_t num_messages; + struct hdcp_msg_part msg_part[HDCP_MAX_MESSAGE_PARTS]; + u8 sink_rx_status; + u8 rx_status; + char abort_mask; + + bool cp_irq_done; + bool polling; +}; + +static inline char *dp_hdcp_cmd_to_str(uint32_t cmd) +{ + switch (cmd) { + case HDMI_HDCP_WKUP_CMD_SEND_MESSAGE: + return "HDMI_HDCP_WKUP_CMD_SEND_MESSAGE"; + case HDMI_HDCP_WKUP_CMD_RECV_MESSAGE: + return "HDMI_HDCP_WKUP_CMD_RECV_MESSAGE"; + case HDMI_HDCP_WKUP_CMD_STATUS_SUCCESS: + return "HDMI_HDCP_WKUP_CMD_STATUS_SUCCESS"; + case HDMI_HDCP_WKUP_CMD_STATUS_FAILED: + return "DP_HDCP_WKUP_CMD_STATUS_FAIL"; + case HDMI_HDCP_WKUP_CMD_LINK_POLL: + return "HDMI_HDCP_WKUP_CMD_LINK_POLL"; + case HDMI_HDCP_WKUP_CMD_AUTHENTICATE: + return "HDMI_HDCP_WKUP_CMD_AUTHENTICATE"; + default: + return "???"; + } +} + +static inline bool dp_hdcp2p2_is_valid_state(struct dp_hdcp2p2_ctrl *ctrl) +{ + if (ctrl->wakeup_cmd == HDMI_HDCP_WKUP_CMD_AUTHENTICATE) + return true; + + if (atomic_read(&ctrl->auth_state) != HDCP_STATE_INACTIVE) + return true; + + return false; +} + +static int dp_hdcp2p2_copy_buf(struct dp_hdcp2p2_ctrl *ctrl, + struct hdmi_hdcp_wakeup_data *data) +{ + int i = 0; + + if (!data || !data->message_data) + return 0; + + mutex_lock(&ctrl->msg_lock); + + ctrl->timeout = data->timeout; + ctrl->num_messages = data->message_data->num_messages; + ctrl->send_msg_len = 0; /* Total len of all messages */ + + for (i = 0; i < ctrl->num_messages ; i++) + ctrl->send_msg_len += data->message_data->messages[i].length; + + memcpy(ctrl->msg_part, data->message_data->messages, + sizeof(data->message_data->messages)); + + ctrl->rx_status = data->message_data->rx_status; + ctrl->abort_mask = data->abort_mask; + + if (!data->send_msg_len) { + mutex_unlock(&ctrl->msg_lock); + return 0; + } + + kzfree(ctrl->msg_buf); + + ctrl->msg_buf = kzalloc(ctrl->send_msg_len, GFP_KERNEL); + + if (!ctrl->msg_buf) { + mutex_unlock(&ctrl->msg_lock); + return -ENOMEM; + } + + /* ignore first byte as it contains message id */ + memcpy(ctrl->msg_buf, data->send_msg_buf + 1, ctrl->send_msg_len); + + mutex_unlock(&ctrl->msg_lock); + + return 0; +} + +static int dp_hdcp2p2_wakeup(struct hdmi_hdcp_wakeup_data *data) +{ + struct dp_hdcp2p2_ctrl *ctrl; + u32 const default_timeout_us = 500; + + if (!data) { + pr_err("invalid input\n"); + return -EINVAL; + } + + ctrl = data->context; + if (!ctrl) { + pr_err("invalid ctrl\n"); + return -EINVAL; + } + + mutex_lock(&ctrl->wakeup_mutex); + + ctrl->wakeup_cmd = data->cmd; + + if (data->timeout) + ctrl->timeout = (data->timeout) * 2; + else + ctrl->timeout = default_timeout_us; + + if (!dp_hdcp2p2_is_valid_state(ctrl)) { + pr_err("invalid state\n"); + goto exit; + } + + if (dp_hdcp2p2_copy_buf(ctrl, data)) + goto exit; + + if (ctrl->wakeup_cmd == HDMI_HDCP_WKUP_CMD_STATUS_SUCCESS) + ctrl->auth_status = DP_HDCP_AUTH_STATUS_SUCCESS; + else if (ctrl->wakeup_cmd == HDMI_HDCP_WKUP_CMD_STATUS_FAILED) + ctrl->auth_status = DP_HDCP_AUTH_STATUS_FAILURE; + + switch (ctrl->wakeup_cmd) { + case HDMI_HDCP_WKUP_CMD_SEND_MESSAGE: + queue_kthread_work(&ctrl->worker, &ctrl->send_msg); + break; + case HDMI_HDCP_WKUP_CMD_RECV_MESSAGE: + queue_kthread_work(&ctrl->worker, &ctrl->recv_msg); + break; + case HDMI_HDCP_WKUP_CMD_STATUS_SUCCESS: + case HDMI_HDCP_WKUP_CMD_STATUS_FAILED: + queue_kthread_work(&ctrl->worker, &ctrl->status); + break; + case HDMI_HDCP_WKUP_CMD_LINK_POLL: + queue_kthread_work(&ctrl->worker, &ctrl->poll); + break; + case HDMI_HDCP_WKUP_CMD_AUTHENTICATE: + queue_kthread_work(&ctrl->worker, &ctrl->auth); + break; + default: + pr_err("invalid wakeup command %d\n", ctrl->wakeup_cmd); + } +exit: + mutex_unlock(&ctrl->wakeup_mutex); + return 0; +} + +static inline int dp_hdcp2p2_wakeup_lib(struct dp_hdcp2p2_ctrl *ctrl, + struct hdcp_lib_wakeup_data *data) +{ + int rc = 0; + + if (ctrl && ctrl->lib && ctrl->lib->wakeup && + data && (data->cmd != HDCP_LIB_WKUP_CMD_INVALID)) { + rc = ctrl->lib->wakeup(data); + if (rc) + pr_err("error sending %s to lib\n", + hdcp_lib_cmd_to_str(data->cmd)); + } + + return rc; +} + +static void dp_hdcp2p2_reset(struct dp_hdcp2p2_ctrl *ctrl) +{ + if (!ctrl) { + pr_err("invalid input\n"); + return; + } + + ctrl->sink_status = SINK_DISCONNECTED; + atomic_set(&ctrl->auth_state, HDCP_STATE_INACTIVE); +} + +static void dp_hdcp2p2_off(void *input) +{ + struct dp_hdcp2p2_ctrl *ctrl = (struct dp_hdcp2p2_ctrl *)input; + struct hdmi_hdcp_wakeup_data cdata = {HDMI_HDCP_WKUP_CMD_AUTHENTICATE}; + + if (!ctrl) { + pr_err("invalid input\n"); + return; + } + + dp_hdcp2p2_reset(ctrl); + + flush_kthread_worker(&ctrl->worker); + + cdata.context = input; + dp_hdcp2p2_wakeup(&cdata); +} + +static int dp_hdcp2p2_authenticate(void *input) +{ + struct dp_hdcp2p2_ctrl *ctrl = input; + struct hdmi_hdcp_wakeup_data cdata = {HDMI_HDCP_WKUP_CMD_AUTHENTICATE}; + int rc = 0; + + flush_kthread_worker(&ctrl->worker); + + ctrl->sink_status = SINK_CONNECTED; + atomic_set(&ctrl->auth_state, HDCP_STATE_AUTHENTICATING); + + cdata.context = input; + dp_hdcp2p2_wakeup(&cdata); + + return rc; +} + +static int dp_hdcp2p2_reauthenticate(void *input) +{ + struct dp_hdcp2p2_ctrl *ctrl = (struct dp_hdcp2p2_ctrl *)input; + + if (!ctrl) { + pr_err("invalid input\n"); + return -EINVAL; + } + + dp_hdcp2p2_reset((struct dp_hdcp2p2_ctrl *)input); + + return dp_hdcp2p2_authenticate(input); +} + +static ssize_t dp_hdcp2p2_sysfs_wta_min_level_change(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct dp_hdcp2p2_ctrl *ctrl = mdss_dp_get_hdcp_data(dev); + struct hdcp_lib_wakeup_data cdata = { + HDCP_LIB_WKUP_CMD_QUERY_STREAM_TYPE}; + bool enc_notify = true; + int enc_lvl; + int min_enc_lvl; + int rc; + + if (!ctrl) { + pr_err("invalid input\n"); + rc = -EINVAL; + goto exit; + } + + rc = kstrtoint(buf, 10, &min_enc_lvl); + if (rc) { + pr_err("kstrtoint failed. rc=%d\n", rc); + goto exit; + } + + switch (min_enc_lvl) { + case 0: + enc_lvl = HDCP_STATE_AUTH_ENC_NONE; + break; + case 1: + enc_lvl = HDCP_STATE_AUTH_ENC_1X; + break; + case 2: + enc_lvl = HDCP_STATE_AUTH_ENC_2P2; + break; + default: + enc_notify = false; + } + + pr_debug("enc level changed %d\n", min_enc_lvl); + + cdata.context = ctrl->lib_ctx; + dp_hdcp2p2_wakeup_lib(ctrl, &cdata); + + if (enc_notify && ctrl->init_data.notify_status) + ctrl->init_data.notify_status(ctrl->init_data.cb_data, enc_lvl); + + rc = count; +exit: + return rc; +} + +static void dp_hdcp2p2_auth_failed(struct dp_hdcp2p2_ctrl *ctrl) +{ + if (!ctrl) { + pr_err("invalid input\n"); + return; + } + + atomic_set(&ctrl->auth_state, HDCP_STATE_AUTH_FAIL); + + /* notify DP about HDCP failure */ + ctrl->init_data.notify_status(ctrl->init_data.cb_data, + HDCP_STATE_AUTH_FAIL); +} + +static int dp_hdcp2p2_aux_read_message(struct dp_hdcp2p2_ctrl *ctrl, + u8 *buf, int size, int offset, u32 timeout) +{ + int rc, max_size = 16, read_size, len = size; + u8 *buf_start = buf; + + if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) { + pr_err("hdcp is off\n"); + return -EINVAL; + } + + do { + struct edp_cmd cmd = {0}; + + read_size = min(size, max_size); + + cmd.read = 1; + cmd.addr = offset; + cmd.len = read_size; + cmd.out_buf = buf; + + rc = dp_aux_read(ctrl->init_data.cb_data, &cmd); + if (rc) { + pr_err("Aux read failed\n"); + break; + } + + buf += read_size; + offset += read_size; + size -= read_size; + } while (size > 0); + + print_hex_dump(KERN_DEBUG, "hdcp2p2: ", DUMP_PREFIX_NONE, + 16, 1, buf_start, len, false); + return rc; +} + +static int dp_hdcp2p2_aux_write_message(struct dp_hdcp2p2_ctrl *ctrl, + u8 *buf, int size, uint offset, uint timeout) +{ + int rc, max_size = 16, write_size; + + do { + struct edp_cmd cmd = {0}; + + write_size = min(size, max_size); + + cmd.read = 0; + cmd.addr = offset; + cmd.len = write_size; + cmd.datap = buf; + + rc = dp_aux_write(ctrl->init_data.cb_data, &cmd); + if (rc) { + pr_err("Aux write failed\n"); + break; + } + + buf += write_size; + offset += write_size; + size -= write_size; + } while (size > 0); + + return rc; +} + +static DEVICE_ATTR(min_level_change, S_IWUSR, NULL, + dp_hdcp2p2_sysfs_wta_min_level_change); + +static struct attribute *dp_hdcp2p2_fs_attrs[] = { + &dev_attr_min_level_change.attr, + NULL, +}; + +static struct attribute_group dp_hdcp2p2_fs_attr_group = { + .name = "dp_hdcp2p2", + .attrs = dp_hdcp2p2_fs_attrs, +}; + +static bool dp_hdcp2p2_feature_supported(void *input) +{ + struct dp_hdcp2p2_ctrl *ctrl = input; + struct hdcp_txmtr_ops *lib = NULL; + bool supported = false; + + if (!ctrl) { + pr_err("invalid input\n"); + goto end; + } + + lib = ctrl->lib; + if (!lib) { + pr_err("invalid lib ops data\n"); + goto end; + } + + if (lib->feature_supported) + supported = lib->feature_supported( + ctrl->lib_ctx); +end: + return supported; +} + +static void dp_hdcp2p2_send_msg_work(struct kthread_work *work) +{ + int rc = 0; + int i; + int sent_bytes = 0; + struct dp_hdcp2p2_ctrl *ctrl = container_of(work, + struct dp_hdcp2p2_ctrl, send_msg); + struct hdcp_lib_wakeup_data cdata = {HDCP_LIB_WKUP_CMD_INVALID}; + char *buf = NULL; + + if (!ctrl) { + pr_err("invalid input\n"); + rc = -EINVAL; + goto exit; + } + + cdata.context = ctrl->lib_ctx; + + if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) { + pr_err("hdcp is off\n"); + goto exit; + } + + mutex_lock(&ctrl->msg_lock); + + /* Loop through number of parameters in the messages. */ + for (i = 0; i < ctrl->num_messages; i++) { + buf = ctrl->msg_buf + sent_bytes; + + /* Forward the message to the sink */ + rc = dp_hdcp2p2_aux_write_message(ctrl, buf, + (size_t)ctrl->msg_part[i].length, + ctrl->msg_part[i].offset, ctrl->timeout); + if (rc) { + pr_err("Error sending msg to sink %d\n", rc); + mutex_unlock(&ctrl->msg_lock); + goto exit; + } + sent_bytes += ctrl->msg_part[i].length; + } + + cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_SEND_SUCCESS; + cdata.timeout = ctrl->timeout; + mutex_unlock(&ctrl->msg_lock); + +exit: + if (rc == -ETIMEDOUT) + cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_RECV_TIMEOUT; + else if (rc) + cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_RECV_FAILED; + + dp_hdcp2p2_wakeup_lib(ctrl, &cdata); +} + +static int dp_hdcp2p2_get_msg_from_sink(struct dp_hdcp2p2_ctrl *ctrl) +{ + int i, rc = 0; + char *recvd_msg_buf = NULL; + struct hdcp_lib_wakeup_data cdata = { HDCP_LIB_WKUP_CMD_INVALID }; + int bytes_read = 0; + + cdata.context = ctrl->lib_ctx; + + recvd_msg_buf = kzalloc(ctrl->send_msg_len, GFP_KERNEL); + if (!recvd_msg_buf) { + rc = -ENOMEM; + goto exit; + } + + for (i = 0; i < ctrl->num_messages; i++) { + rc = dp_hdcp2p2_aux_read_message( + ctrl, recvd_msg_buf + bytes_read, + ctrl->msg_part[i].length, + ctrl->msg_part[i].offset, + ctrl->timeout); + if (rc) { + pr_err("error reading message %d\n", rc); + goto exit; + } + bytes_read += ctrl->msg_part[i].length; + } + + cdata.recvd_msg_buf = recvd_msg_buf; + cdata.recvd_msg_len = ctrl->send_msg_len; + cdata.timeout = ctrl->timeout; +exit: + if (rc == -ETIMEDOUT) + cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_RECV_TIMEOUT; + else if (rc) + cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_RECV_FAILED; + else + cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_RECV_SUCCESS; + + dp_hdcp2p2_wakeup_lib(ctrl, &cdata); + kfree(recvd_msg_buf); + + return rc; +} + +static void dp_hdcp2p2_recv_msg_work(struct kthread_work *work) +{ + int rc = 0; + struct hdcp_lib_wakeup_data cdata = { HDCP_LIB_WKUP_CMD_INVALID }; + struct dp_hdcp2p2_ctrl *ctrl = container_of(work, + struct dp_hdcp2p2_ctrl, recv_msg); + + cdata.context = ctrl->lib_ctx; + + if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) { + pr_err("hdcp is off\n"); + goto exit; + } + + if (ctrl->sink_rx_status & ctrl->abort_mask) { + pr_err("reauth or Link fail triggered by sink\n"); + + ctrl->sink_rx_status = 0; + rc = -ENOLINK; + cdata.cmd = HDCP_LIB_WKUP_CMD_STOP; + + goto exit; + } + + if (ctrl->rx_status && !ctrl->sink_rx_status) { + pr_debug("Recv msg for RxStatus, but no CP_IRQ yet\n"); + ctrl->polling = true; + goto exit; + } + + dp_hdcp2p2_get_msg_from_sink(ctrl); + + return; +exit: + if (rc) + dp_hdcp2p2_wakeup_lib(ctrl, &cdata); +} + +static void dp_hdcp2p2_poll_work(struct kthread_work *work) +{ + struct dp_hdcp2p2_ctrl *ctrl = container_of(work, + struct dp_hdcp2p2_ctrl, poll); + + if (ctrl->cp_irq_done) { + ctrl->cp_irq_done = false; + dp_hdcp2p2_get_msg_from_sink(ctrl); + } else { + ctrl->polling = true; + } +} + +static void dp_hdcp2p2_auth_status_work(struct kthread_work *work) +{ + struct dp_hdcp2p2_ctrl *ctrl = container_of(work, + struct dp_hdcp2p2_ctrl, status); + + if (!ctrl) { + pr_err("invalid input\n"); + return; + } + + if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) { + pr_err("hdcp is off\n"); + return; + } + + if (ctrl->auth_status == DP_HDCP_AUTH_STATUS_SUCCESS) { + ctrl->init_data.notify_status(ctrl->init_data.cb_data, + HDCP_STATE_AUTHENTICATED); + + atomic_set(&ctrl->auth_state, HDCP_STATE_AUTHENTICATED); + } else { + dp_hdcp2p2_auth_failed(ctrl); + } +} + +static void dp_hdcp2p2_link_work(struct kthread_work *work) +{ + int rc = 0; + struct dp_hdcp2p2_ctrl *ctrl = container_of(work, + struct dp_hdcp2p2_ctrl, link); + struct hdcp_lib_wakeup_data cdata = {HDCP_LIB_WKUP_CMD_INVALID}; + + if (!ctrl) { + pr_err("invalid input\n"); + return; + } + + cdata.context = ctrl->lib_ctx; + + ctrl->sink_rx_status = 0; + rc = mdss_dp_aux_read_rx_status(ctrl->init_data.cb_data, + &ctrl->sink_rx_status); + + if (rc) { + pr_err("failed to read rx status\n"); + + cdata.cmd = HDCP_LIB_WKUP_CMD_STOP; + goto exit; + } + + if (ctrl->sink_rx_status & ctrl->abort_mask) { + pr_err("reauth or Link fail triggered by sink\n"); + + ctrl->sink_rx_status = 0; + ctrl->rx_status = 0; + + rc = -ENOLINK; + cdata.cmd = HDCP_LIB_WKUP_CMD_STOP; + goto exit; + } + + /* if polling, get message from sink else let polling start */ + if (ctrl->polling && (ctrl->sink_rx_status & ctrl->rx_status)) { + ctrl->sink_rx_status = 0; + ctrl->rx_status = 0; + + rc = dp_hdcp2p2_get_msg_from_sink(ctrl); + + ctrl->polling = false; + } else { + ctrl->cp_irq_done = true; + } +exit: + dp_hdcp2p2_wakeup_lib(ctrl, &cdata); + + if (rc) { + dp_hdcp2p2_auth_failed(ctrl); + return; + } +} + +static void dp_hdcp2p2_auth_work(struct kthread_work *work) +{ + int rc = 0; + struct hdcp_lib_wakeup_data cdata = {HDCP_LIB_WKUP_CMD_INVALID}; + struct dp_hdcp2p2_ctrl *ctrl = container_of(work, + struct dp_hdcp2p2_ctrl, auth); + + cdata.context = ctrl->lib_ctx; + + if (atomic_read(&ctrl->auth_state) == HDCP_STATE_AUTHENTICATING) + cdata.cmd = HDCP_LIB_WKUP_CMD_START; + else + cdata.cmd = HDCP_LIB_WKUP_CMD_STOP; + + rc = dp_hdcp2p2_wakeup_lib(ctrl, &cdata); + if (rc) + dp_hdcp2p2_auth_failed(ctrl); +} + +static int dp_hdcp2p2_isr(void *input) +{ + struct dp_hdcp2p2_ctrl *ctrl = input; + + if (!ctrl) { + pr_err("invalid input\n"); + return -EINVAL; + } + + queue_kthread_work(&ctrl->worker, &ctrl->link); + + return 0; +} + +void dp_hdcp2p2_deinit(void *input) +{ + struct dp_hdcp2p2_ctrl *ctrl = (struct dp_hdcp2p2_ctrl *)input; + struct hdcp_lib_wakeup_data cdata = {HDCP_LIB_WKUP_CMD_INVALID}; + + if (!ctrl) { + pr_err("invalid input\n"); + return; + } + + cdata.cmd = HDCP_LIB_WKUP_CMD_STOP; + cdata.context = ctrl->lib_ctx; + dp_hdcp2p2_wakeup_lib(ctrl, &cdata); + + kthread_stop(ctrl->thread); + + sysfs_remove_group(ctrl->init_data.sysfs_kobj, + &dp_hdcp2p2_fs_attr_group); + + mutex_destroy(&ctrl->mutex); + mutex_destroy(&ctrl->msg_lock); + mutex_destroy(&ctrl->wakeup_mutex); + kzfree(ctrl->msg_buf); + kfree(ctrl); +} + +void *dp_hdcp2p2_init(struct hdcp_init_data *init_data) +{ + int rc; + struct dp_hdcp2p2_ctrl *ctrl; + static struct hdcp_ops ops = { + .reauthenticate = dp_hdcp2p2_reauthenticate, + .authenticate = dp_hdcp2p2_authenticate, + .feature_supported = dp_hdcp2p2_feature_supported, + .off = dp_hdcp2p2_off, + .isr = dp_hdcp2p2_isr + }; + + static struct hdcp_client_ops client_ops = { + .wakeup = dp_hdcp2p2_wakeup, + }; + + static struct hdcp_txmtr_ops txmtr_ops; + struct hdcp_register_data register_data = {0}; + + if (!init_data || !init_data->cb_data || + !init_data->notify_status) { + pr_err("invalid input\n"); + return ERR_PTR(-EINVAL); + } + + ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) + return ERR_PTR(-ENOMEM); + + ctrl->init_data = *init_data; + ctrl->lib = &txmtr_ops; + ctrl->msg_buf = NULL; + rc = sysfs_create_group(init_data->sysfs_kobj, + &dp_hdcp2p2_fs_attr_group); + if (rc) { + pr_err("dp_hdcp2p2 sysfs group creation failed\n"); + goto error; + } + + ctrl->sink_status = SINK_DISCONNECTED; + + atomic_set(&ctrl->auth_state, HDCP_STATE_INACTIVE); + + ctrl->ops = &ops; + mutex_init(&ctrl->mutex); + mutex_init(&ctrl->msg_lock); + mutex_init(&ctrl->wakeup_mutex); + + register_data.hdcp_ctx = &ctrl->lib_ctx; + register_data.client_ops = &client_ops; + register_data.txmtr_ops = &txmtr_ops; + register_data.device_type = HDCP_TXMTR_DP; + register_data.client_ctx = ctrl; + + rc = hdcp_library_register(®ister_data); + if (rc) { + pr_err("Unable to register with HDCP 2.2 library\n"); + goto error; + } + + init_kthread_worker(&ctrl->worker); + + init_kthread_work(&ctrl->auth, dp_hdcp2p2_auth_work); + init_kthread_work(&ctrl->send_msg, dp_hdcp2p2_send_msg_work); + init_kthread_work(&ctrl->recv_msg, dp_hdcp2p2_recv_msg_work); + init_kthread_work(&ctrl->status, dp_hdcp2p2_auth_status_work); + init_kthread_work(&ctrl->link, dp_hdcp2p2_link_work); + init_kthread_work(&ctrl->poll, dp_hdcp2p2_poll_work); + + ctrl->thread = kthread_run(kthread_worker_fn, + &ctrl->worker, "dp_hdcp2p2"); + + if (IS_ERR(ctrl->thread)) { + pr_err("unable to start DP hdcp2p2 thread\n"); + rc = PTR_ERR(ctrl->thread); + ctrl->thread = NULL; + goto error; + } + + return ctrl; +error: + kfree(ctrl); + return ERR_PTR(rc); +} + +static bool dp_hdcp2p2_supported(struct dp_hdcp2p2_ctrl *ctrl) +{ + struct edp_cmd cmd = {0}; + const u32 offset = 0x6921d; + u8 buf; + + cmd.read = 1; + cmd.addr = offset; + cmd.len = sizeof(buf); + cmd.out_buf = &buf; + + if (dp_aux_read(ctrl->init_data.cb_data, &cmd)) { + pr_err("RxCaps read failed\n"); + goto error; + } + + pr_debug("rxcaps 0x%x\n", buf); + + if (buf & BIT(1)) + return true; +error: + return false; +} + +struct hdcp_ops *dp_hdcp2p2_start(void *input) +{ + struct dp_hdcp2p2_ctrl *ctrl = input; + + pr_debug("Checking sink capability\n"); + if (dp_hdcp2p2_supported(ctrl)) + return ctrl->ops; + else + return NULL; +} + diff --git a/drivers/video/fbdev/msm/mdss_dp_util.h b/drivers/video/fbdev/msm/mdss_dp_util.h index cf2286f9b58a..334c0071050d 100644 --- a/drivers/video/fbdev/msm/mdss_dp_util.h +++ b/drivers/video/fbdev/msm/mdss_dp_util.h @@ -37,6 +37,9 @@ #define DP_AUX_TRANS_CTRL (0x00000238) #define DP_AUX_STATUS (0x00000244) +#define DP_DPCD_CP_IRQ (0x201) +#define DP_DPCD_RXSTATUS (0x69493) + #define DP_INTERRUPT_TRANS_NUM (0x000002A0) #define DP_MAINLINK_CTRL (0x00000400) @@ -299,5 +302,6 @@ void mdss_dp_audio_set_sample_rate(struct dss_io_data *ctrl_io, char dp_link_rate, uint32_t audio_freq); void mdss_dp_set_safe_to_exit_level(struct dss_io_data *ctrl_io, uint32_t lane_cnt); +int mdss_dp_aux_read_rx_status(struct mdss_dp_drv_pdata *dp, u8 *rx_status); #endif /* __DP_UTIL_H__ */ diff --git a/drivers/video/fbdev/msm/mdss_hdcp_1x.h b/drivers/video/fbdev/msm/mdss_hdcp.h index 426b13a340f4..d373d22384e8 100644 --- a/drivers/video/fbdev/msm/mdss_hdcp_1x.h +++ b/drivers/video/fbdev/msm/mdss_hdcp.h @@ -14,6 +14,7 @@ #define __MDSS_HDMI_HDCP_H__ #include "mdss_hdmi_util.h" +#include "mdss_dp.h" #include <video/msm_hdmi_modes.h> #include <soc/qcom/scm.h> @@ -42,6 +43,8 @@ struct hdcp_init_data { void *cb_data; void (*notify_status)(void *cb_data, enum hdcp_states status); struct hdmi_tx_ddc_ctrl *ddc_ctrl; + u8 sink_rx_status; + void *dp_data; u32 phy_addr; u32 hdmi_tx_ver; struct msm_hdmi_mode_timing_info *timing; @@ -59,12 +62,17 @@ struct hdcp_ops { }; void *hdcp_1x_init(struct hdcp_init_data *init_data); -void *hdmi_hdcp2p2_init(struct hdcp_init_data *init_data); void hdcp_1x_deinit(void *input); + +void *hdmi_hdcp2p2_init(struct hdcp_init_data *init_data); void hdmi_hdcp2p2_deinit(void *input); +void *dp_hdcp2p2_init(struct hdcp_init_data *init_data); +void dp_hdcp2p2_deinit(void *input); + struct hdcp_ops *hdcp_1x_start(void *input); struct hdcp_ops *hdmi_hdcp2p2_start(void *input); +struct hdcp_ops *dp_hdcp2p2_start(void *input); const char *hdcp_state_name(enum hdcp_states hdcp_state); diff --git a/drivers/video/fbdev/msm/mdss_hdcp_1x.c b/drivers/video/fbdev/msm/mdss_hdcp_1x.c index 5b490ad67e65..1e502cf750a6 100644 --- a/drivers/video/fbdev/msm/mdss_hdcp_1x.c +++ b/drivers/video/fbdev/msm/mdss_hdcp_1x.c @@ -18,7 +18,7 @@ #include <linux/iopoll.h> #include <soc/qcom/scm.h> #include <linux/hdcp_qseecom.h> -#include "mdss_hdcp_1x.h" +#include "mdss_hdcp.h" #include "mdss_fb.h" #include "mdss_dp_util.h" #include "video/msm_hdmi_hdcp_mgr.h" diff --git a/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c b/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c index 1f4760d87172..481fc118c7ad 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c @@ -20,7 +20,7 @@ #include <linux/kthread.h> #include <linux/hdcp_qseecom.h> -#include "mdss_hdcp_1x.h" +#include "mdss_hdcp.h" #include "video/msm_hdmi_hdcp_mgr.h" #include "mdss_hdmi_util.h" diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.c b/drivers/video/fbdev/msm/mdss_hdmi_tx.c index 94cc2f2dc370..9c90a72bce99 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_tx.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.c @@ -29,7 +29,7 @@ #include "mdss_fb.h" #include "mdss_hdmi_cec.h" #include "mdss_hdmi_edid.h" -#include "mdss_hdcp_1x.h" +#include "mdss_hdcp.h" #include "mdss_hdmi_tx.h" #include "mdss_hdmi_audio.h" #include "mdss.h" diff --git a/drivers/video/fbdev/msm/msm_ext_display.c b/drivers/video/fbdev/msm/msm_ext_display.c index 4899231787f2..f6c6548bdaa5 100644 --- a/drivers/video/fbdev/msm/msm_ext_display.c +++ b/drivers/video/fbdev/msm/msm_ext_display.c @@ -24,6 +24,7 @@ #include <linux/msm_ext_display.h> #include "mdss_hdmi_util.h" +#include "mdss_fb.h" struct msm_ext_disp_list { struct msm_ext_disp_init_data *data; @@ -97,41 +98,55 @@ end: return; } -static int msm_ext_disp_get_pdev(struct device *dev, - struct platform_device *pdev) { - int ret = 0; +static void msm_ext_disp_get_pdev_by_name(struct device *dev, + const char *phandle, struct platform_device **pdev) +{ struct device_node *pd_np; - const char *phandle = "qcom,msm_ext_disp"; - if (!dev || !dev->of_node) { - ret = -ENODEV; - pr_err("Invalid params\n"); - goto end; + if (!dev) { + pr_err("Invalid device\n"); + return; + } + + if (!dev->of_node) { + pr_err("Invalid of_node\n"); + return; } pd_np = of_parse_phandle(dev->of_node, phandle, 0); if (!pd_np) { pr_err("Cannot find %s dev\n", phandle); - ret = -ENODEV; - goto end; + return; } - pdev = of_find_device_by_node(pd_np); - if (!pdev) { - pr_err("Cannot find %s pdev\n", phandle); - ret = -ENODEV; + *pdev = of_find_device_by_node(pd_np); +} + +static void msm_ext_disp_get_fb_pdev(struct device *device, + struct platform_device **fb_pdev) +{ + struct msm_fb_data_type *mfd = NULL; + struct fb_info *fbi = dev_get_drvdata(device); + + if (!fbi) { + pr_err("fb_info is null\n"); + return; } -end: - return ret; -} + mfd = (struct msm_fb_data_type *)fbi->par; + *fb_pdev = mfd->pdev; +} static ssize_t msm_ext_disp_sysfs_wta_audio_cb(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int ack, ret = 0; ssize_t size = strnlen(buf, PAGE_SIZE); - struct platform_device *pdev = NULL; + const char *ext_phandle = "qcom,msm_ext_disp"; + struct platform_device *ext_pdev = NULL; + const char *intf_phandle = "qcom,mdss-intf"; + struct platform_device *intf_pdev = NULL; + struct platform_device *fb_pdev = NULL; ret = kstrtoint(buf, 10, &ack); if (ret) { @@ -139,13 +154,25 @@ static ssize_t msm_ext_disp_sysfs_wta_audio_cb(struct device *dev, goto end; } - ret = msm_ext_disp_get_pdev(dev, pdev); - if (ret) { - pr_err("Failed to get pdev. ret=%d\n", ret); + msm_ext_disp_get_fb_pdev(dev, &fb_pdev); + if (!fb_pdev) { + pr_err("failed to get fb pdev\n"); + goto end; + } + + msm_ext_disp_get_pdev_by_name(&fb_pdev->dev, intf_phandle, &intf_pdev); + if (!intf_pdev) { + pr_err("failed to get display intf pdev\n"); + goto end; + } + + msm_ext_disp_get_pdev_by_name(&intf_pdev->dev, ext_phandle, &ext_pdev); + if (!ext_pdev) { + pr_err("failed to get ext_pdev\n"); goto end; } - ret = msm_ext_disp_audio_ack(pdev, ack); + ret = msm_ext_disp_audio_ack(ext_pdev, ack); if (ret) pr_err("Failed to process ack. ret=%d\n", ret); diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index ed984496aec1..8f6276f1f3d4 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -5792,6 +5792,7 @@ static int tasha_codec_enable_dec(struct snd_soc_dapm_widget *w, char *wname; int ret = 0, amic_n; u16 tx_vol_ctl_reg, pwr_level_reg = 0, dec_cfg_reg, hpf_gate_reg; + u16 tx_gain_ctl_reg; char *dec; u8 hpf_cut_off_freq; struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); @@ -5834,6 +5835,7 @@ static int tasha_codec_enable_dec(struct snd_soc_dapm_widget *w, tx_vol_ctl_reg = WCD9335_CDC_TX0_TX_PATH_CTL + 16 * decimator; hpf_gate_reg = WCD9335_CDC_TX0_TX_PATH_SEC2 + 16 * decimator; dec_cfg_reg = WCD9335_CDC_TX0_TX_PATH_CFG0 + 16 * decimator; + tx_gain_ctl_reg = WCD9335_CDC_TX0_TX_VOL_CTL + 16 * decimator; switch (event) { case SND_SOC_DAPM_PRE_PMU: @@ -5896,6 +5898,9 @@ static int tasha_codec_enable_dec(struct snd_soc_dapm_widget *w, schedule_delayed_work( &tasha->tx_hpf_work[decimator].dwork, msecs_to_jiffies(300)); + /* apply gain after decimator is enabled */ + snd_soc_write(codec, tx_gain_ctl_reg, + snd_soc_read(codec, tx_gain_ctl_reg)); break; case SND_SOC_DAPM_PRE_PMD: hpf_cut_off_freq = diff --git a/sound/soc/codecs/wcd934x/wcd934x.c b/sound/soc/codecs/wcd934x/wcd934x.c index b8dcd264b5d2..2f117159402e 100644 --- a/sound/soc/codecs/wcd934x/wcd934x.c +++ b/sound/soc/codecs/wcd934x/wcd934x.c @@ -3667,6 +3667,7 @@ static int tavil_codec_enable_dec(struct snd_soc_dapm_widget *w, char *wname; int ret = 0, amic_n; u16 tx_vol_ctl_reg, pwr_level_reg = 0, dec_cfg_reg, hpf_gate_reg; + u16 tx_gain_ctl_reg; char *dec; u8 hpf_cut_off_freq; @@ -3708,6 +3709,7 @@ static int tavil_codec_enable_dec(struct snd_soc_dapm_widget *w, tx_vol_ctl_reg = WCD934X_CDC_TX0_TX_PATH_CTL + 16 * decimator; hpf_gate_reg = WCD934X_CDC_TX0_TX_PATH_SEC2 + 16 * decimator; dec_cfg_reg = WCD934X_CDC_TX0_TX_PATH_CFG0 + 16 * decimator; + tx_gain_ctl_reg = WCD934X_CDC_TX0_TX_VOL_CTL + 16 * decimator; switch (event) { case SND_SOC_DAPM_PRE_PMU: @@ -3768,7 +3770,9 @@ static int tavil_codec_enable_dec(struct snd_soc_dapm_widget *w, schedule_delayed_work( &tavil->tx_hpf_work[decimator].dwork, msecs_to_jiffies(300)); - + /* apply gain after decimator is enabled */ + snd_soc_write(codec, tx_gain_ctl_reg, + snd_soc_read(codec, tx_gain_ctl_reg)); break; case SND_SOC_DAPM_PRE_PMD: hpf_cut_off_freq = |
