diff --git a/Documentation/devicetree/bindings/drm/msm/mdss-dsi-panel.txt b/Documentation/devicetree/bindings/drm/msm/mdss-dsi-panel.txt index c8fb809a5a0f..697ff8672a9d 100644 --- a/Documentation/devicetree/bindings/drm/msm/mdss-dsi-panel.txt +++ b/Documentation/devicetree/bindings/drm/msm/mdss-dsi-panel.txt @@ -164,10 +164,7 @@ Optional properties: "dfps_immediate_porch_mode_vfp" = FPS change request is implemented immediately by changing panel vertical front porch values. -- qcom,min-refresh-rate: Minimum refresh rate supported by the panel. -- qcom,max-refresh-rate: Maximum refresh rate supported by the panel. If max refresh - rate is not specified, then the frame rate of the panel in - qcom,mdss-dsi-panel-framerate is used. +- qcom,dsi-supported-dfps-list: List containing all the supported refresh rates. - qcom,mdss-dsi-bl-pmic-control-type: A string that specifies the implementation of backlight control for this panel. "bl_ctrl_pwm" = Backlight controlled by PWM gpio. @@ -545,6 +542,10 @@ Optional properties: - qcom,mdss-dsi-ext-bridge-mode: External bridge chip is connected instead of panel. - qcom,mdss-dsi-dma-schedule-line: An integer value indicates the line number after vertical active region, at which command DMA needs to be triggered. +- qcom,dsi-dyn-clk-enable: Boolean to indicate dsi dynamic clock switch feature + is supported. +- qcom,dsi-dyn-clk-list: An u32 array which lists all the supported dsi bit clock + frequencies in Hz for the given panel. Required properties for sub-nodes: None Optional properties: @@ -669,8 +670,7 @@ Example: qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; qcom,mdss-dsi-pan-enable-dynamic-fps; qcom,mdss-dsi-pan-fps-update = "dfps_suspend_resume_mode"; - qcom,min-refresh-rate = <30>; - qcom,max-refresh-rate = <60>; + qcom,dsi-supported-dfps-list = <48 55 60>; qcom,mdss-dsi-bl-pmic-bank-select = <0>; qcom,mdss-dsi-bl-pmic-pwm-frequency = <0>; qcom,mdss-dsi-pwm-gpio = <&pm8941_mpps 5 0>; @@ -807,5 +807,7 @@ Example: <2 2 1>; qcom,default-topology-index = <0>; qcom,mdss-dsi-dma-schedule-line = <5>; + qcom,dsi-dyn-clk-enable; + qcom,dsi-dyn-clk-list = <798240576 801594528 804948480>; }; }; diff --git a/arch/arm64/boot/dts/qcom/sdmmagpie-sde-display.dtsi b/arch/arm64/boot/dts/qcom/sdmmagpie-sde-display.dtsi index e21f31ec47ce..251060a34548 100644 --- a/arch/arm64/boot/dts/qcom/sdmmagpie-sde-display.dtsi +++ b/arch/arm64/boot/dts/qcom/sdmmagpie-sde-display.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2018-2019, 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 @@ -104,7 +104,7 @@ qcom,dsi-ctrl-num = <0>; qcom,dsi-phy-num = <0>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_sw43404_amoled_video>; }; @@ -115,7 +115,7 @@ qcom,dsi-ctrl-num = <0>; qcom,dsi-phy-num = <0>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_sw43404_amoled_cmd>; }; @@ -126,7 +126,7 @@ qcom,dsi-ctrl-num = <0>; qcom,dsi-phy-num = <0>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_sw43404_amoled_fhd_plus_cmd>; }; @@ -137,7 +137,7 @@ qcom,dsi-ctrl-num = <0>; qcom,dsi-phy-num = <0>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_sim_vid>; }; @@ -148,7 +148,7 @@ qcom,dsi-ctrl-num = <0 1>; qcom,dsi-phy-num = <0 1>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_dual_sim_vid>; }; @@ -159,7 +159,7 @@ qcom,dsi-ctrl-num = <0>; qcom,dsi-phy-num = <0>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_sim_cmd>; }; @@ -170,7 +170,7 @@ qcom,dsi-ctrl-num = <0 1>; qcom,dsi-phy-num = <0 1>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_dual_sim_cmd>; }; @@ -181,7 +181,7 @@ qcom,dsi-ctrl-num = <0>; qcom,dsi-phy-num = <0>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_sim_dsc_375_cmd>; }; @@ -192,7 +192,7 @@ qcom,dsi-ctrl-num = <0 1>; qcom,dsi-phy-num = <0 1>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_dual_sim_dsc_375_cmd>; }; @@ -203,7 +203,7 @@ qcom,dsi-ctrl-num = <0 1>; qcom,dsi-phy-num = <0 1>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_dual_sharp_wqhd_video>; }; @@ -214,7 +214,7 @@ qcom,dsi-ctrl-num = <0 1>; qcom,dsi-phy-num = <0 1>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_dual_sharp_wqhd_cmd>; }; @@ -225,7 +225,7 @@ qcom,dsi-ctrl-num = <0>; qcom,dsi-phy-num = <0>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_rm69298_truly_amoled_video>; }; @@ -236,7 +236,7 @@ qcom,dsi-ctrl-num = <0>; qcom,dsi-phy-num = <0>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_rm69298_truly_amoled_cmd>; }; @@ -247,7 +247,7 @@ qcom,dsi-ctrl-num = <1>; qcom,dsi-phy-num = <1>; - qcom,dsi-select-clocks = "src_byte_clk1", "src_pixel_clk1"; + qcom,dsi-select-clocks = "mux_byte_clk1", "mux_pixel_clk1"; qcom,dsi-panel = <&dsi_nt35695b_truly_fhd_video>; }; @@ -258,7 +258,7 @@ qcom,dsi-ctrl-num = <1>; qcom,dsi-phy-num = <1>; - qcom,dsi-select-clocks = "src_byte_clk1", "src_pixel_clk1"; + qcom,dsi-select-clocks = "mux_byte_clk1", "mux_pixel_clk1"; qcom,dsi-panel = <&dsi_nt35695b_truly_fhd_cmd>; }; @@ -269,7 +269,7 @@ qcom,dsi-ctrl-num = <1>; qcom,dsi-phy-num = <1>; - qcom,dsi-select-clocks = "src_byte_clk1", "src_pixel_clk1"; + qcom,dsi-select-clocks = "mux_byte_clk1", "mux_pixel_clk1"; qcom,dsi-panel = <&dsi_nt35695b_truly_fhd_video>; }; @@ -280,7 +280,7 @@ qcom,dsi-ctrl-num = <1>; qcom,dsi-phy-num = <1>; - qcom,dsi-select-clocks = "src_byte_clk1", "src_pixel_clk1"; + qcom,dsi-select-clocks = "mux_byte_clk1", "mux_pixel_clk1"; qcom,dsi-panel = <&dsi_nt35695b_truly_fhd_cmd>; }; @@ -295,8 +295,8 @@ <&mdss_dsi0_pll PCLK_MUX_0_CLK>, <&mdss_dsi1_pll BYTECLK_MUX_1_CLK>, <&mdss_dsi1_pll PCLK_MUX_1_CLK>; - clock-names = "src_byte_clk0", "src_pixel_clk0", - "src_byte_clk1", "src_pixel_clk1"; + clock-names = "mux_byte_clk0", "mux_pixel_clk0", + "mux_byte_clk1", "mux_pixel_clk1"; pinctrl-names = "panel_active", "panel_suspend"; pinctrl-0 = <&sde_te_active &disp_pins_default>; @@ -341,8 +341,8 @@ <&mdss_dsi0_pll PCLK_MUX_0_CLK>, <&mdss_dsi1_pll BYTECLK_MUX_1_CLK>, <&mdss_dsi1_pll PCLK_MUX_1_CLK>; - clock-names = "src_byte_clk0", "src_pixel_clk0", - "src_byte_clk1", "src_pixel_clk1"; + clock-names = "mux_byte_clk0", "mux_pixel_clk0", + "mux_byte_clk1", "mux_pixel_clk1"; pinctrl-names = "panel_active", "panel_suspend"; pinctrl-0 = <&sde_te1_active>; @@ -386,8 +386,7 @@ qcom,mdss-dsi-panel-status-value = <0x9c>; qcom,mdss-dsi-panel-on-check-value = <0x9c>; qcom,mdss-dsi-panel-status-read-length = <1>; - qcom,mdss-dsi-min-refresh-rate = <55>; - qcom,mdss-dsi-max-refresh-rate = <60>; + qcom,dsi-supported-dfps-list = <60 57 55>; qcom,mdss-dsi-pan-enable-dynamic-fps; qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_hfp"; qcom,mdss-dsi-display-timings { @@ -456,8 +455,7 @@ qcom,mdss-dsi-panel-status-value = <0x9c>; qcom,mdss-dsi-panel-on-check-value = <0x9c>; qcom,mdss-dsi-panel-status-read-length = <1>; - qcom,mdss-dsi-min-refresh-rate = <55>; - qcom,mdss-dsi-max-refresh-rate = <60>; + qcom,dsi-supported-dfps-list = <60 57 55>; qcom,mdss-dsi-pan-enable-dynamic-fps; qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_hfp"; qcom,mdss-dsi-display-timings { @@ -661,9 +659,7 @@ qcom,mdss-dsi-panel-status-value = <0x9c>; qcom,mdss-dsi-panel-on-check-value = <0x9c>; qcom,mdss-dsi-panel-status-read-length = <1>; - - qcom,mdss-dsi-min-refresh-rate = <48>; - qcom,mdss-dsi-max-refresh-rate = <60>; + qcom,dsi-supported-dfps-list = <60 55 48>; qcom,mdss-dsi-pan-enable-dynamic-fps; qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_vfp"; diff --git a/arch/arm64/boot/dts/qcom/sdmshrike-sde-display.dtsi b/arch/arm64/boot/dts/qcom/sdmshrike-sde-display.dtsi index 97ed20b4afbf..4687aa1a5dc6 100644 --- a/arch/arm64/boot/dts/qcom/sdmshrike-sde-display.dtsi +++ b/arch/arm64/boot/dts/qcom/sdmshrike-sde-display.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2019, 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 @@ -111,7 +111,7 @@ qcom,dsi-ctrl-num = <0 1>; qcom,dsi-phy-num = <0 1>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_sharp_4k_dsc_video>; }; @@ -122,7 +122,7 @@ qcom,dsi-ctrl-num = <0 1>; qcom,dsi-phy-num = <0 1>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_sharp_4k_dsc_cmd>; }; @@ -133,7 +133,7 @@ qcom,dsi-ctrl-num = <0>; qcom,dsi-phy-num = <0>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_sharp_1080_cmd>; }; @@ -144,7 +144,7 @@ qcom,dsi-ctrl-num = <0 1>; qcom,dsi-phy-num = <0 1>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_dual_sharp_1080_120hz_cmd>; }; @@ -155,7 +155,7 @@ qcom,dsi-ctrl-num = <0 1>; qcom,dsi-phy-num = <0 1>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_dual_nt35597_truly_video>; }; @@ -166,7 +166,7 @@ qcom,dsi-ctrl-num = <0 1>; qcom,dsi-phy-num = <0 1>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_dual_nt35597_truly_cmd>; }; @@ -177,7 +177,7 @@ qcom,dsi-ctrl-num = <1>; qcom,dsi-phy-num = <1>; - qcom,dsi-select-clocks = "src_byte_clk1", "src_pixel_clk1"; + qcom,dsi-select-clocks = "mux_byte_clk1", "mux_pixel_clk1"; qcom,dsi-panel = <&dsi_nt35597_truly_dsc_cmd>; }; @@ -188,7 +188,7 @@ qcom,dsi-ctrl-num = <1>; qcom,dsi-phy-num = <1>; - qcom,dsi-select-clocks = "src_byte_clk1", "src_pixel_clk1"; + qcom,dsi-select-clocks = "mux_byte_clk1", "mux_pixel_clk1"; qcom,dsi-panel = <&dsi_nt35597_truly_dsc_video>; }; @@ -199,7 +199,7 @@ qcom,dsi-ctrl-num = <0>; qcom,dsi-phy-num = <0>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_sim_vid>; }; @@ -210,7 +210,7 @@ qcom,dsi-ctrl-num = <0 1>; qcom,dsi-phy-num = <0 1>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_dual_sim_vid>; }; @@ -221,7 +221,7 @@ qcom,dsi-ctrl-num = <0>; qcom,dsi-phy-num = <0>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_sim_cmd>; }; @@ -232,7 +232,7 @@ qcom,dsi-ctrl-num = <0 1>; qcom,dsi-phy-num = <0 1>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_dual_sim_cmd>; }; @@ -243,7 +243,7 @@ qcom,dsi-ctrl-num = <0>; qcom,dsi-phy-num = <0>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_sim_dsc_375_cmd>; }; @@ -254,7 +254,7 @@ qcom,dsi-ctrl-num = <0 1>; qcom,dsi-phy-num = <0 1>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_dual_sim_dsc_375_cmd>; }; @@ -265,7 +265,7 @@ qcom,dsi-ctrl-num = <0>; qcom,dsi-phy-num = <0>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_sw43404_amoled_cmd>; }; @@ -276,7 +276,7 @@ qcom,dsi-ctrl-num = <0>; qcom,dsi-phy-num = <0>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_nt35695b_truly_fhd_cmd>; @@ -288,7 +288,7 @@ qcom,dsi-ctrl-num = <0>; qcom,dsi-phy-num = <0>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_nt35695b_truly_fhd_video>; }; @@ -303,8 +303,8 @@ <&mdss_dsi0_pll PCLK_MUX_0_CLK>, <&mdss_dsi1_pll BYTECLK_MUX_1_CLK>, <&mdss_dsi1_pll PCLK_MUX_1_CLK>; - clock-names = "src_byte_clk0", "src_pixel_clk0", - "src_byte_clk1", "src_pixel_clk1"; + clock-names = "mux_byte_clk0", "mux_pixel_clk0", + "mux_byte_clk1", "mux_pixel_clk1"; pinctrl-names = "panel_active", "panel_suspend"; pinctrl-0 = <&sde_dsi_active &sde_te_active>; diff --git a/arch/arm64/boot/dts/qcom/sm6150-sde-display.dtsi b/arch/arm64/boot/dts/qcom/sm6150-sde-display.dtsi index 2c9c3e2e0ad8..bc8bf7df65d0 100644 --- a/arch/arm64/boot/dts/qcom/sm6150-sde-display.dtsi +++ b/arch/arm64/boot/dts/qcom/sm6150-sde-display.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2018-2019, 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 @@ -99,7 +99,7 @@ qcom,dsi-ctrl-num = <0>; qcom,dsi-phy-num = <0>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_sim_vid>; }; @@ -110,7 +110,7 @@ qcom,dsi-ctrl-num = <0>; qcom,dsi-phy-num = <0>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_sim_cmd>; }; @@ -121,7 +121,7 @@ qcom,dsi-ctrl-num = <0>; qcom,dsi-phy-num = <0>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_hx83112a_truly_video>; }; @@ -132,7 +132,7 @@ qcom,dsi-ctrl-num = <0>; qcom,dsi-phy-num = <0>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_td4328_truly_video>; }; @@ -143,7 +143,7 @@ qcom,dsi-ctrl-num = <0>; qcom,dsi-phy-num = <0>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_td4328_truly_cmd>; }; @@ -154,7 +154,7 @@ qcom,dsi-ctrl-num = <0>; qcom,dsi-phy-num = <0>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_rm69298_truly_amoled_video>; }; @@ -165,7 +165,7 @@ qcom,dsi-ctrl-num = <0>; qcom,dsi-phy-num = <0>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_rm69298_truly_amoled_cmd>; }; @@ -178,7 +178,7 @@ clocks = <&mdss_dsi0_pll BYTE0_MUX_CLK>, <&mdss_dsi0_pll PIX0_MUX_CLK>; - clock-names = "src_byte_clk0", "src_pixel_clk0"; + clock-names = "mux_byte_clk0", "mux_pixel_clk0"; pinctrl-names = "panel_active", "panel_suspend"; pinctrl-0 = <&sde_dsi_active &sde_te_active>; pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>; @@ -308,8 +308,7 @@ &dsi_hx83112a_truly_video { qcom,mdss-dsi-t-clk-post = <0x0e>; qcom,mdss-dsi-t-clk-pre = <0x31>; - qcom,mdss-dsi-min-refresh-rate = <48>; - qcom,mdss-dsi-max-refresh-rate = <60>; + qcom,dsi-supported-dfps-list = <60 55 53 43>; qcom,mdss-dsi-pan-enable-dynamic-fps; qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_vfp"; diff --git a/arch/arm64/boot/dts/qcom/sm8150-sde-display.dtsi b/arch/arm64/boot/dts/qcom/sm8150-sde-display.dtsi index fb477aa76987..f66bb5cc6cf9 100644 --- a/arch/arm64/boot/dts/qcom/sm8150-sde-display.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8150-sde-display.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2019, 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 @@ -143,7 +143,7 @@ qcom,dsi-ctrl-num = <0 1>; qcom,dsi-phy-num = <0 1>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_sharp_4k_dsc_video>; }; @@ -154,7 +154,7 @@ qcom,dsi-ctrl-num = <0 1>; qcom,dsi-phy-num = <0 1>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_sharp_4k_dsc_cmd>; }; @@ -165,7 +165,7 @@ qcom,dsi-ctrl-num = <0>; qcom,dsi-phy-num = <0>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_sharp_1080_cmd>; }; @@ -176,7 +176,7 @@ qcom,dsi-ctrl-num = <0 1>; qcom,dsi-phy-num = <0 1>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_dual_sharp_1080_120hz_cmd>; }; @@ -187,7 +187,7 @@ qcom,dsi-ctrl-num = <0 1>; qcom,dsi-phy-num = <0 1>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_dual_nt35597_truly_video>; }; @@ -198,7 +198,7 @@ qcom,dsi-ctrl-num = <0 1>; qcom,dsi-phy-num = <0 1>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_dual_nt35597_truly_cmd>; }; @@ -209,7 +209,7 @@ qcom,dsi-ctrl-num = <1>; qcom,dsi-phy-num = <1>; - qcom,dsi-select-clocks = "src_byte_clk1", "src_pixel_clk1"; + qcom,dsi-select-clocks = "mux_byte_clk1", "mux_pixel_clk1"; qcom,dsi-panel = <&dsi_nt35597_truly_dsc_cmd>; }; @@ -220,7 +220,7 @@ qcom,dsi-ctrl-num = <1>; qcom,dsi-phy-num = <1>; - qcom,dsi-select-clocks = "src_byte_clk1", "src_pixel_clk1"; + qcom,dsi-select-clocks = "mux_byte_clk1", "mux_pixel_clk1"; qcom,dsi-panel = <&dsi_nt35597_truly_dsc_video>; }; @@ -231,7 +231,7 @@ qcom,dsi-ctrl-num = <0>; qcom,dsi-phy-num = <0>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_sim_vid>; }; @@ -242,7 +242,7 @@ qcom,dsi-ctrl-num = <0 1>; qcom,dsi-phy-num = <0 1>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_dual_sim_vid>; }; @@ -253,7 +253,7 @@ qcom,dsi-ctrl-num = <0>; qcom,dsi-phy-num = <0>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_sim_cmd>; }; @@ -264,7 +264,7 @@ qcom,dsi-ctrl-num = <0 1>; qcom,dsi-phy-num = <0 1>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_dual_sim_cmd>; }; @@ -275,7 +275,7 @@ qcom,dsi-ctrl-num = <0>; qcom,dsi-phy-num = <0>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_sim_dsc_375_cmd>; }; @@ -286,7 +286,7 @@ qcom,dsi-ctrl-num = <0 1>; qcom,dsi-phy-num = <0 1>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_dual_sim_dsc_375_cmd>; }; @@ -297,7 +297,7 @@ qcom,dsi-ctrl-num = <0>; qcom,dsi-phy-num = <0>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_sw43404_amoled_cmd>; }; @@ -308,7 +308,7 @@ qcom,dsi-ctrl-num = <0>; qcom,dsi-phy-num = <0>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_nt35695b_truly_fhd_cmd>; @@ -320,7 +320,7 @@ qcom,dsi-ctrl-num = <0>; qcom,dsi-phy-num = <0>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_nt35695b_truly_fhd_video>; }; @@ -331,7 +331,7 @@ qcom,dsi-ctrl-num = <0>; qcom,dsi-phy-num = <0>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_sw43404_amoled_video>; }; @@ -342,7 +342,7 @@ qcom,dsi-ctrl-num = <0>; qcom,dsi-phy-num = <0>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_sw43404_amoled_fhd_plus_cmd>; }; @@ -353,7 +353,7 @@ qcom,dsi-ctrl-num = <0 1>; qcom,dsi-phy-num = <0 1>; - qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; qcom,dsi-panel = <&dsi_dual_nt36850_truly_cmd>; }; @@ -364,7 +364,7 @@ qcom,dsi-ctrl-num = <1>; qcom,dsi-phy-num = <1>; - qcom,dsi-select-clocks = "src_byte_clk1", "src_pixel_clk1"; + qcom,dsi-select-clocks = "mux_byte_clk1", "mux_pixel_clk1"; qcom,dsi-panel = <&dsi_nt35695b_truly_fhd_cmd>; @@ -376,7 +376,7 @@ qcom,dsi-ctrl-num = <1>; qcom,dsi-phy-num = <1>; - qcom,dsi-select-clocks = "src_byte_clk1", "src_pixel_clk1"; + qcom,dsi-select-clocks = "mux_byte_clk1", "mux_pixel_clk1"; qcom,dsi-panel = <&dsi_nt35695b_truly_fhd_video>; }; @@ -388,7 +388,7 @@ qcom,dsi-ctrl-num = <1>; qcom,dsi-phy-num = <1>; - qcom,dsi-select-clocks = "src_byte_clk1", "src_pixel_clk1"; + qcom,dsi-select-clocks = "mux_byte_clk1", "mux_pixel_clk1"; qcom,dsi-panel = <&dsi_sim_sec_hd_cmd>; }; @@ -404,8 +404,8 @@ <&mdss_dsi0_pll PCLK_MUX_0_CLK>, <&mdss_dsi1_pll BYTECLK_MUX_1_CLK>, <&mdss_dsi1_pll PCLK_MUX_1_CLK>; - clock-names = "src_byte_clk0", "src_pixel_clk0", - "src_byte_clk1", "src_pixel_clk1"; + clock-names = "mux_byte_clk0", "mux_pixel_clk0", + "mux_byte_clk1", "mux_pixel_clk1"; pinctrl-names = "panel_active", "panel_suspend"; pinctrl-0 = <&sde_dsi_active &sde_te_active>; @@ -453,8 +453,8 @@ <&mdss_dsi0_pll PCLK_MUX_0_CLK>, <&mdss_dsi1_pll BYTECLK_MUX_1_CLK>, <&mdss_dsi1_pll PCLK_MUX_1_CLK>; - clock-names = "src_byte_clk0", "src_pixel_clk0", - "src_byte_clk1", "src_pixel_clk1"; + clock-names = "mux_byte_clk0", "mux_pixel_clk0", + "mux_byte_clk1", "mux_pixel_clk1"; pinctrl-names = "panel_active", "panel_suspend"; pinctrl-0 = <&sde_dsi1_active &sde_te1_active>; @@ -507,8 +507,7 @@ /* PHY TIMINGS REVISION T */ &dsi_dual_nt35597_truly_video { - qcom,mdss-dsi-min-refresh-rate = <53>; - qcom,mdss-dsi-max-refresh-rate = <60>; + qcom,dsi-supported-dfps-list = <60 55 53>; qcom,mdss-dsi-pan-enable-dynamic-fps; qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_vfp"; qcom,esd-check-enabled; diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c index b03e46a136b6..33afaa047bc9 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2019, 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 @@ -71,6 +71,8 @@ static void dsi_catalog_cmn_init(struct dsi_ctrl_hw *ctrl, dsi_ctrl_hw_cmn_wait_for_cmd_mode_mdp_idle; ctrl->ops.setup_avr = dsi_ctrl_hw_cmn_setup_avr; ctrl->ops.set_continuous_clk = dsi_ctrl_hw_cmn_set_continuous_clk; + ctrl->ops.wait4dynamic_refresh_done = + dsi_ctrl_hw_cmn_wait4dynamic_refresh_done; switch (version) { case DSI_CTRL_VERSION_1_4: @@ -226,6 +228,14 @@ static void dsi_catalog_phy_3_0_init(struct dsi_phy_hw *phy) phy->ops.clamp_ctrl = dsi_phy_hw_v3_0_clamp_ctrl; phy->ops.phy_lane_reset = dsi_phy_hw_v3_0_lane_reset; phy->ops.toggle_resync_fifo = dsi_phy_hw_v3_0_toggle_resync_fifo; + phy->ops.dyn_refresh_ops.dyn_refresh_config = + dsi_phy_hw_v3_0_dyn_refresh_config; + phy->ops.dyn_refresh_ops.dyn_refresh_pipe_delay = + dsi_phy_hw_v3_0_dyn_refresh_pipe_delay; + phy->ops.dyn_refresh_ops.dyn_refresh_helper = + dsi_phy_hw_v3_0_dyn_refresh_helper; + phy->ops.dyn_refresh_ops.cache_phy_timings = + dsi_phy_hw_v3_0_cache_phy_timings; } /** diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h index e6c148ba12d4..5e8921faa058 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2019, 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 @@ -66,15 +66,17 @@ int dsi_phy_timing_calc_init(struct dsi_phy_hw *phy, * @mode: DSI mode information. * @host: DSI host configuration. * @timing: DSI phy lane configurations. + * @use_mode_bit_clk: Boolean to indicate whether to recalculate bit clk. * * This function setups the catalog information in the dsi_phy_hw object. * * return: error code for failure and 0 for success. */ int dsi_phy_hw_calculate_timing_params(struct dsi_phy_hw *phy, - struct dsi_mode_info *mode, - struct dsi_host_common_cfg *host, - struct dsi_phy_per_lane_cfgs *timing); + struct dsi_mode_info *mode, + struct dsi_host_common_cfg *host, + struct dsi_phy_per_lane_cfgs *timing, + bool use_mode_bit_clk); /* Definitions for 14nm PHY hardware driver */ void dsi_phy_hw_v2_0_regulator_enable(struct dsi_phy_hw *phy, @@ -248,4 +250,14 @@ void dsi_ctrl_hw_22_config_clk_gating(struct dsi_ctrl_hw *ctrl, bool enable, void dsi_ctrl_hw_cmn_set_continuous_clk(struct dsi_ctrl_hw *ctrl, bool enable); +/* dynamic refresh specific functions */ +void dsi_phy_hw_v3_0_dyn_refresh_helper(struct dsi_phy_hw *phy, u32 offset); +void dsi_phy_hw_v3_0_dyn_refresh_config(struct dsi_phy_hw *phy, + struct dsi_phy_cfg *cfg, bool is_master); +void dsi_phy_hw_v3_0_dyn_refresh_pipe_delay(struct dsi_phy_hw *phy, + struct dsi_dyn_clk_delay *delay); + +int dsi_ctrl_hw_cmn_wait4dynamic_refresh_done(struct dsi_ctrl_hw *ctrl); +int dsi_phy_hw_v3_0_cache_phy_timings(struct dsi_phy_per_lane_cfgs *timings, + u32 *dst, u32 size); #endif /* _DSI_CATALOG_H_ */ diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_clk.h b/drivers/gpu/drm/msm/dsi-staging/dsi_clk.h index 721a71705a71..9d47fbededf8 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_clk.h +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_clk.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2019, 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 @@ -318,4 +318,18 @@ int dsi_clk_set_byte_clk_rate(void *client, u64 byte_clk, u32 index); */ int dsi_clk_update_parent(struct dsi_clk_link_set *parent, struct dsi_clk_link_set *child); + +/** + * dsi_clk_prepare_enable() - prepare and enable dsi src clocks + * @clk: list of src clocks. + * + * @return: Zero on success and err no on failure + */ +int dsi_clk_prepare_enable(struct dsi_clk_link_set *clk); + +/** + * dsi_clk_disable_unprepare() - disable and unprepare dsi src clocks + * @clk: list of src clocks. + */ +void dsi_clk_disable_unprepare(struct dsi_clk_link_set *clk); #endif /* _DSI_CLK_H_ */ diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_clk_manager.c b/drivers/gpu/drm/msm/dsi-staging/dsi_clk_manager.c index 5d6808bf1b16..7aaeb95e030f 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_clk_manager.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_clk_manager.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2019, 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 @@ -113,8 +113,9 @@ int dsi_clk_set_link_frequencies(void *client, struct link_clk_freq freq, /** * dsi_clk_set_pixel_clk_rate() - set frequency for pixel clock - * @clks: DSI link clock information. - * @pixel_clk: Pixel clock rate in KHz. + * @clks: DSI link clock information. + * @pixel_clk: Pixel clock rate in KHz. + * @index: Index of the DSI controller. * * return: error code in case of failure or 0 for success. */ @@ -136,9 +137,9 @@ int dsi_clk_set_pixel_clk_rate(void *client, u64 pixel_clk, u32 index) /** * dsi_clk_set_byte_clk_rate() - set frequency for byte clock - * @client: DSI clock client pointer. - * @byte_clk: Pixel clock rate in Hz. - * @index: Index of the DSI controller. + * @client: DSI clock client pointer. + * @byte_clk: Byte clock rate in Hz. + * @index: Index of the DSI controller. * return: error code in case of failure or 0 for success. */ int dsi_clk_set_byte_clk_rate(void *client, u64 byte_clk, u32 index) @@ -146,6 +147,7 @@ int dsi_clk_set_byte_clk_rate(void *client, u64 byte_clk, u32 index) int rc = 0; struct dsi_clk_client_info *c = client; struct dsi_clk_mngr *mngr; + u64 byte_intf_rate; mngr = c->mngr; rc = clk_set_rate(mngr->link_clks[index].hs_clks.byte_clk, byte_clk); @@ -154,8 +156,16 @@ int dsi_clk_set_byte_clk_rate(void *client, u64 byte_clk, u32 index) else mngr->link_clks[index].freq.byte_clk_rate = byte_clk; - return rc; + if (mngr->link_clks[index].hs_clks.byte_intf_clk) { + byte_intf_rate = mngr->link_clks[index].freq.byte_clk_rate / 2; + rc = clk_set_rate(mngr->link_clks[index].hs_clks.byte_intf_clk, + byte_intf_rate); + if (rc) + pr_err("failed to set clk rate for byte intf clk=%d\n", + rc); + } + return rc; } /** @@ -183,6 +193,41 @@ error: return rc; } +/** + * dsi_clk_prepare_enable() - prepare and enable dsi src clocks + * @clk: list of src clocks. + * + * @return: Zero on success and err no on failure. + */ +int dsi_clk_prepare_enable(struct dsi_clk_link_set *clk) +{ + int rc; + + rc = clk_prepare_enable(clk->byte_clk); + if (rc) { + pr_err("failed to enable byte src clk %d\n", rc); + return rc; + } + + rc = clk_prepare_enable(clk->pixel_clk); + if (rc) { + pr_err("failed to enable pixel src clk %d\n", rc); + return rc; + } + + return 0; +} + +/** + * dsi_clk_disable_unprepare() - disable and unprepare dsi src clocks + * @clk: list of src clocks. + */ +void dsi_clk_disable_unprepare(struct dsi_clk_link_set *clk) +{ + clk_disable_unprepare(clk->pixel_clk); + clk_disable_unprepare(clk->byte_clk); +} + int dsi_core_clk_start(struct dsi_core_clks *c_clks) { int rc = 0; diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c index 948a662bc11e..1e0dc9fd14f1 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2019, 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 @@ -2880,7 +2880,12 @@ int dsi_ctrl_update_host_config(struct dsi_ctrl *ctrl, goto error; } - if (!(flags & (DSI_MODE_FLAG_SEAMLESS | DSI_MODE_FLAG_VRR))) { + if (!(flags & (DSI_MODE_FLAG_SEAMLESS | DSI_MODE_FLAG_VRR | + DSI_MODE_FLAG_DYN_CLK))) { + /* + * for dynamic clk switch case link frequence would + * be updated dsi_display_dynamic_clk_switch(). + */ rc = dsi_ctrl_update_link_freqs(ctrl, config, clk_handle); if (rc) { pr_err("[%s] failed to update link frequencies, rc=%d\n", @@ -3595,6 +3600,27 @@ void dsi_ctrl_irq_update(struct dsi_ctrl *dsi_ctrl, bool enable) mutex_unlock(&dsi_ctrl->ctrl_lock); } +/** + * dsi_ctrl_wait4dynamic_refresh_done() - Poll for dynamci refresh + * done interrupt. + * @dsi_ctrl: DSI controller handle. + */ +int dsi_ctrl_wait4dynamic_refresh_done(struct dsi_ctrl *ctrl) +{ + int rc = 0; + + if (!ctrl) + return 0; + + mutex_lock(&ctrl->ctrl_lock); + + if (ctrl->hw.ops.wait4dynamic_refresh_done) + rc = ctrl->hw.ops.wait4dynamic_refresh_done(&ctrl->hw); + + mutex_unlock(&ctrl->ctrl_lock); + return rc; +} + /** * dsi_ctrl_drv_register() - register platform driver for dsi controller */ diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h index 75f43abfbc7f..f49772dace7a 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2019, 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 @@ -805,4 +805,11 @@ int dsi_ctrl_pixel_format_to_bpp(enum dsi_pixel_format dst_format); * @enable: variable to control continuous clock. */ void dsi_ctrl_set_continuous_clk(struct dsi_ctrl *dsi_ctrl, bool enable); + +/** + * dsi_ctrl_wait4dynamic_refresh_done() - Poll for dynamic refresh done + * interrupt. + * @dsi_ctrl: DSI controller handle. + */ +int dsi_ctrl_wait4dynamic_refresh_done(struct dsi_ctrl *ctrl); #endif /* _DSI_CTRL_H_ */ diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h index 458d865c2cf4..848dea5b2f0c 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2019, 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 @@ -829,6 +829,12 @@ struct dsi_ctrl_hw_ops { * @enable: Bool to control continuous clock request. */ void (*set_continuous_clk)(struct dsi_ctrl_hw *ctrl, bool enable); + + /** + * hw.ops.wait4dynamic_refresh_done() - Wait for dynamic refresh done + * @ctrl: Pointer to the controller host hardware. + */ + int (*wait4dynamic_refresh_done)(struct dsi_ctrl_hw *ctrl); }; /* diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_cmn.c b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_cmn.c index 64d56d77a12e..b37c5b00aebf 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_cmn.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_cmn.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2019, 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 @@ -1516,6 +1516,13 @@ void dsi_ctrl_hw_cmn_mask_error_intr(struct dsi_ctrl_hw *ctrl, u32 idx, bool en) } } + if (idx & BIT(DSI_PLL_UNLOCK_ERR)) { + if (en) + reg |= BIT(28); + else + reg &= ~BIT(28); + } + DSI_W32(ctrl, 0x10c, reg); wmb(); /* ensure error is masked */ } @@ -1582,3 +1589,25 @@ void dsi_ctrl_hw_cmn_set_continuous_clk(struct dsi_ctrl_hw *ctrl, bool enable) DSI_W32(ctrl, DSI_LANE_CTRL, reg); wmb(); /* make sure request is set */ } + +int dsi_ctrl_hw_cmn_wait4dynamic_refresh_done(struct dsi_ctrl_hw *ctrl) +{ + int rc; + u32 const sleep_us = 1000; + u32 const timeout_us = 84000; /* approximately 5 vsyncs */ + u32 reg = 0, dyn_refresh_done = BIT(28); + + rc = readl_poll_timeout(ctrl->base + DSI_INT_CTRL, reg, + (reg & dyn_refresh_done), sleep_us, timeout_us); + if (rc) { + pr_err("wait4dynamic refresh timedout %d\n", rc); + return rc; + } + + /* ack dynamic refresh done status */ + reg = DSI_R32(ctrl, DSI_INT_CTRL); + reg |= dyn_refresh_done; + DSI_W32(ctrl, DSI_INT_CTRL, reg); + + return 0; +} diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_reg.h b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_reg.h index 6bf147bb00cb..72ac12dfd4b6 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_reg.h +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_reg.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2019, 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 @@ -138,44 +138,7 @@ #define DSI_SCRATCH_REGISTER_1 (0x01F8) #define DSI_SCRATCH_REGISTER_2 (0x01FC) #define DSI_DYNAMIC_REFRESH_CTRL (0x0200) -#define DSI_DYNAMIC_REFRESH_PIPE_DELAY (0x0204) -#define DSI_DYNAMIC_REFRESH_PIPE_DELAY2 (0x0208) -#define DSI_DYNAMIC_REFRESH_PLL_DELAY (0x020C) #define DSI_DYNAMIC_REFRESH_STATUS (0x0210) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL0 (0x0214) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL1 (0x0218) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL2 (0x021C) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL3 (0x0220) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL4 (0x0224) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL5 (0x0228) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL6 (0x022C) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL7 (0x0230) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL8 (0x0234) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL9 (0x0238) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL10 (0x023C) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL11 (0x0240) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL12 (0x0244) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL13 (0x0248) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL14 (0x024C) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL15 (0x0250) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL16 (0x0254) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL17 (0x0258) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL18 (0x025C) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL19 (0x0260) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL20 (0x0264) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL21 (0x0268) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL22 (0x026C) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL23 (0x0270) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL24 (0x0274) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL25 (0x0278) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL26 (0x027C) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL27 (0x0280) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL28 (0x0284) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL29 (0x0288) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL30 (0x028C) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL31 (0x0290) -#define DSI_DYNAMIC_REFRESH_PLL_UPPER_ADDR (0x0294) -#define DSI_DYNAMIC_REFRESH_PLL_UPPER_ADDR2 (0x0298) #define DSI_VIDEO_COMPRESSION_MODE_CTRL (0x02A0) #define DSI_VIDEO_COMPRESSION_MODE_CTRL2 (0x02A4) #define DSI_COMMAND_COMPRESSION_MODE_CTRL (0x02A8) diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h b/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h index c57f8b17eb3e..a39b59cd3379 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2019, 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 @@ -82,6 +82,7 @@ enum dsi_op_mode { * @DSI_MODE_FLAG_DMS: Seamless transition is dynamic mode switch * @DSI_MODE_FLAG_VRR: Seamless transition is DynamicFPS. * New timing values are sent from DAL. + * @DSI_MODE_FLAG_DYN_CLK: Seamless transition is dynamic clock change */ enum dsi_mode_flags { DSI_MODE_FLAG_SEAMLESS = BIT(0), @@ -89,6 +90,7 @@ enum dsi_mode_flags { DSI_MODE_FLAG_VBLANK_PRE_MODESET = BIT(2), DSI_MODE_FLAG_DMS = BIT(3), DSI_MODE_FLAG_VRR = BIT(4), + DSI_MODE_FLAG_DYN_CLK = BIT(5), }; /** @@ -635,12 +637,50 @@ struct dsi_event_cb_info { * @DSI_FIFO_OVERFLOW: DSI FIFO Overflow error * @DSI_FIFO_UNDERFLOW: DSI FIFO Underflow error * @DSI_LP_Rx_TIMEOUT: DSI LP/RX Timeout error + * @DSI_PLL_UNLOCK_ERR: DSI PLL unlock error */ enum dsi_error_status { DSI_FIFO_OVERFLOW = 1, DSI_FIFO_UNDERFLOW, DSI_LP_Rx_TIMEOUT, + DSI_PLL_UNLOCK_ERR, DSI_ERR_INTR_ALL, }; +/* structure containing the delays required for dynamic clk */ +struct dsi_dyn_clk_delay { + u32 pipe_delay; + u32 pipe_delay2; + u32 pll_delay; +}; + +/* dynamic refresh control bits */ +enum dsi_dyn_clk_control_bits { + DYN_REFRESH_INTF_SEL = 1, + DYN_REFRESH_SYNC_MODE, + DYN_REFRESH_SW_TRIGGER, + DYN_REFRESH_SWI_CTRL, +}; + +/* convert dsi pixel format into bits per pixel */ +static inline int dsi_pixel_format_to_bpp(enum dsi_pixel_format fmt) +{ + switch (fmt) { + case DSI_PIXEL_FORMAT_RGB888: + case DSI_PIXEL_FORMAT_MAX: + return 24; + case DSI_PIXEL_FORMAT_RGB666: + case DSI_PIXEL_FORMAT_RGB666_LOOSE: + return 18; + case DSI_PIXEL_FORMAT_RGB565: + return 16; + case DSI_PIXEL_FORMAT_RGB111: + return 3; + case DSI_PIXEL_FORMAT_RGB332: + return 8; + case DSI_PIXEL_FORMAT_RGB444: + return 12; + } + return 24; +} #endif /* _DSI_DEFS_H_ */ diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c index 862254c369d7..978b4e0c0140 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2019, 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 @@ -1628,7 +1628,7 @@ static void adjust_timing_by_ctrl_count(const struct dsi_display *display, mode->timing.h_back_porch /= sublinks_count; mode->timing.h_skew /= sublinks_count; mode->pixel_clk_khz /= sublinks_count; - } else if (display->ctrl_count > 1) { + } else { mode->timing.h_active /= display->ctrl_count; mode->timing.h_front_porch /= display->ctrl_count; mode->timing.h_sync_width /= display->ctrl_count; @@ -2255,7 +2255,7 @@ static int dsi_display_set_clk_src(struct dsi_display *display) m_ctrl = &display->ctrl[display->clk_master_idx]; rc = dsi_ctrl_set_clock_source(m_ctrl->ctrl, - &display->clock_info.src_clks); + &display->clock_info.mux_clks); if (rc) { pr_err("[%s] failed to set source clocks for master, rc=%d\n", display->name, rc); @@ -2269,7 +2269,7 @@ static int dsi_display_set_clk_src(struct dsi_display *display) continue; rc = dsi_ctrl_set_clock_source(ctrl->ctrl, - &display->clock_info.src_clks); + &display->clock_info.mux_clks); if (rc) { pr_err("[%s] failed to set source clocks, rc=%d\n", display->name, rc); @@ -2938,6 +2938,7 @@ static int dsi_display_clocks_init(struct dsi_display *display) struct dsi_clk_link_set *src = &display->clock_info.src_clks; struct dsi_clk_link_set *mux = &display->clock_info.mux_clks; struct dsi_clk_link_set *shadow = &display->clock_info.shadow_clks; + struct dsi_dyn_clk_caps *dyn_clk_caps = &(display->panel->dyn_clk_caps); num_clk = dsi_display_get_clocks_count(display); @@ -2953,7 +2954,32 @@ static int dsi_display_clocks_init(struct dsi_display *display) rc = PTR_ERR(dsi_clk); pr_err("failed to get %s, rc=%d\n", clk_name, rc); - goto error; + + if (dsi_display_check_prefix(mux_byte, clk_name)) { + mux->byte_clk = NULL; + goto error; + } + if (dsi_display_check_prefix(mux_pixel, clk_name)) { + mux->pixel_clk = NULL; + goto error; + } + + if (dyn_clk_caps->dyn_clk_support) { + if (dsi_display_check_prefix(src_byte, + clk_name)) + src->byte_clk = NULL; + if (dsi_display_check_prefix(src_pixel, + clk_name)) + src->pixel_clk = NULL; + if (dsi_display_check_prefix(shadow_byte, + clk_name)) + shadow->byte_clk = NULL; + if (dsi_display_check_prefix(shadow_pixel, + clk_name)) + shadow->pixel_clk = NULL; + + dyn_clk_caps->dyn_clk_support = false; + } } if (dsi_display_check_prefix(src_byte, clk_name)) { @@ -3746,6 +3772,305 @@ static bool dsi_display_is_seamless_dfps_possible( return true; } +static int dsi_display_update_dsi_bitrate(struct dsi_display *display, + u32 bit_clk_rate) +{ + int rc = 0; + int i; + + pr_debug("%s:bit rate:%d\n", __func__, bit_clk_rate); + if (!display->panel) { + pr_err("Invalid params\n"); + return -EINVAL; + } + + if (bit_clk_rate == 0) { + pr_err("Invalid bit clock rate\n"); + return -EINVAL; + } + + display->config.bit_clk_rate_hz = bit_clk_rate; + + for (i = 0; i < display->ctrl_count; i++) { + struct dsi_display_ctrl *dsi_disp_ctrl = &display->ctrl[i]; + struct dsi_ctrl *ctrl = dsi_disp_ctrl->ctrl; + u32 num_of_lanes = 0, bpp; + u64 bit_rate, pclk_rate, bit_rate_per_lane, byte_clk_rate; + struct dsi_host_common_cfg *host_cfg; + + mutex_lock(&ctrl->ctrl_lock); + + host_cfg = &display->panel->host_config; + if (host_cfg->data_lanes & DSI_DATA_LANE_0) + num_of_lanes++; + if (host_cfg->data_lanes & DSI_DATA_LANE_1) + num_of_lanes++; + if (host_cfg->data_lanes & DSI_DATA_LANE_2) + num_of_lanes++; + if (host_cfg->data_lanes & DSI_DATA_LANE_3) + num_of_lanes++; + + if (num_of_lanes == 0) { + pr_err("Invalid lane count\n"); + rc = -EINVAL; + goto error; + } + + bpp = dsi_pixel_format_to_bpp(host_cfg->dst_format); + + bit_rate = display->config.bit_clk_rate_hz * num_of_lanes; + bit_rate_per_lane = bit_rate; + do_div(bit_rate_per_lane, num_of_lanes); + pclk_rate = bit_rate; + do_div(pclk_rate, bpp); + byte_clk_rate = bit_rate_per_lane; + do_div(byte_clk_rate, 8); + pr_debug("bit_clk_rate = %llu, bit_clk_rate_per_lane = %llu\n", + bit_rate, bit_rate_per_lane); + pr_debug("byte_clk_rate = %llu, pclk_rate = %llu\n", + byte_clk_rate, pclk_rate); + + ctrl->clk_freq.byte_clk_rate = byte_clk_rate; + ctrl->clk_freq.pix_clk_rate = pclk_rate; + rc = dsi_clk_set_link_frequencies(display->dsi_clk_handle, + ctrl->clk_freq, ctrl->cell_index); + if (rc) { + pr_err("Failed to update link frequencies\n"); + goto error; + } + + ctrl->host_config.bit_clk_rate_hz = bit_clk_rate; +error: + mutex_unlock(&ctrl->ctrl_lock); + + /* TODO: recover ctrl->clk_freq in case of failure */ + if (rc) + return rc; + } + + return 0; +} + +static void _dsi_display_calc_pipe_delay(struct dsi_display *display, + struct dsi_dyn_clk_delay *delay, + struct dsi_display_mode *mode) +{ + u32 esc_clk_rate_hz; + u32 pclk_to_esc_ratio, byte_to_esc_ratio, hr_bit_to_esc_ratio; + u32 hsync_period = 0; + struct dsi_display_ctrl *m_ctrl; + struct dsi_ctrl *dsi_ctrl; + struct dsi_phy_cfg *cfg; + + m_ctrl = &display->ctrl[display->clk_master_idx]; + dsi_ctrl = m_ctrl->ctrl; + + cfg = &(m_ctrl->phy->cfg); + + esc_clk_rate_hz = dsi_ctrl->clk_freq.esc_clk_rate * 1000; + pclk_to_esc_ratio = ((dsi_ctrl->clk_freq.pix_clk_rate * 1000) / + esc_clk_rate_hz); + byte_to_esc_ratio = ((dsi_ctrl->clk_freq.byte_clk_rate * 1000) / + esc_clk_rate_hz); + hr_bit_to_esc_ratio = ((dsi_ctrl->clk_freq.byte_clk_rate * 4 * 1000) / + esc_clk_rate_hz); + + hsync_period = DSI_H_TOTAL_DSC(&mode->timing); + delay->pipe_delay = (hsync_period + 1) / pclk_to_esc_ratio; + if (!display->panel->video_config.eof_bllp_lp11_en) + delay->pipe_delay += (17 / pclk_to_esc_ratio) + + ((21 + (display->config.common_config.t_clk_pre + 1) + + (display->config.common_config.t_clk_post + 1)) / + byte_to_esc_ratio) + + ((((cfg->timing.lane_v3[8] >> 1) + 1) + + ((cfg->timing.lane_v3[6] >> 1) + 1) + + ((cfg->timing.lane_v3[3] * 4) + + (cfg->timing.lane_v3[5] >> 1) + 1) + + ((cfg->timing.lane_v3[7] >> 1) + 1) + + ((cfg->timing.lane_v3[1] >> 1) + 1) + + ((cfg->timing.lane_v3[4] >> 1) + 1)) / + hr_bit_to_esc_ratio); + + delay->pipe_delay2 = 0; + if (display->panel->host_config.force_hs_clk_lane) + delay->pipe_delay2 = (6 / byte_to_esc_ratio) + + ((((cfg->timing.lane_v3[1] >> 1) + 1) + + ((cfg->timing.lane_v3[4] >> 1) + 1)) / + hr_bit_to_esc_ratio); + + /* 130 us pll delay recommended by h/w doc */ + delay->pll_delay = ((130 * esc_clk_rate_hz) / 1000000) * 2; +} + +static int _dsi_display_dyn_update_clks(struct dsi_display *display, + struct link_clk_freq *bkp_freq) +{ + int rc = 0, i; + struct dsi_display_ctrl *m_ctrl, *ctrl; + + m_ctrl = &display->ctrl[display->clk_master_idx]; + + dsi_clk_prepare_enable(&display->clock_info.src_clks); + + rc = dsi_clk_update_parent(&display->clock_info.shadow_clks, + &display->clock_info.mux_clks); + if (rc) { + pr_err("failed update mux parent to shadow\n"); + goto exit; + } + + for (i = 0; (i < display->ctrl_count) && + (i < MAX_DSI_CTRLS_PER_DISPLAY); i++) { + ctrl = &display->ctrl[i]; + if (!ctrl->ctrl) + continue; + rc = dsi_clk_set_byte_clk_rate(display->dsi_clk_handle, + ctrl->ctrl->clk_freq.byte_clk_rate, i); + if (rc) { + pr_err("failed to set byte rate for index:%d\n", i); + goto recover_byte_clk; + } + rc = dsi_clk_set_pixel_clk_rate(display->dsi_clk_handle, + ctrl->ctrl->clk_freq.pix_clk_rate, i); + if (rc) { + pr_err("failed to set pix rate for index:%d\n", i); + goto recover_pix_clk; + } + } + + for (i = 0; (i < display->ctrl_count) && + (i < MAX_DSI_CTRLS_PER_DISPLAY); i++) { + ctrl = &display->ctrl[i]; + if (ctrl == m_ctrl) + continue; + dsi_phy_dynamic_refresh_trigger(ctrl->phy, false); + } + dsi_phy_dynamic_refresh_trigger(m_ctrl->phy, true); + + /* wait for dynamic refresh done */ + for (i = 0; (i < display->ctrl_count) && + (i < MAX_DSI_CTRLS_PER_DISPLAY); i++) { + ctrl = &display->ctrl[i]; + rc = dsi_ctrl_wait4dynamic_refresh_done(ctrl->ctrl); + if (rc) { + pr_err("wait4dynamic refresh failed for dsi:%d\n", i); + goto recover_pix_clk; + } else { + pr_info("dynamic refresh done on dsi: %s\n", + i ? "slave" : "master"); + } + } + + for (i = 0; (i < display->ctrl_count) && + (i < MAX_DSI_CTRLS_PER_DISPLAY); i++) { + ctrl = &display->ctrl[i]; + dsi_phy_dynamic_refresh_clear(ctrl->phy); + } + + rc = dsi_clk_update_parent(&display->clock_info.src_clks, + &display->clock_info.mux_clks); + if (rc) + pr_err("could not switch back to src clks %d\n", rc); + + dsi_clk_disable_unprepare(&display->clock_info.src_clks); + + return rc; + +recover_pix_clk: + for (i = 0; (i < display->ctrl_count) && + (i < MAX_DSI_CTRLS_PER_DISPLAY); i++) { + ctrl = &display->ctrl[i]; + if (!ctrl->ctrl) + continue; + dsi_clk_set_pixel_clk_rate(display->dsi_clk_handle, + bkp_freq->pix_clk_rate, i); + } + +recover_byte_clk: + for (i = 0; (i < display->ctrl_count) && + (i < MAX_DSI_CTRLS_PER_DISPLAY); i++) { + ctrl = &display->ctrl[i]; + if (!ctrl->ctrl) + continue; + dsi_clk_set_byte_clk_rate(display->dsi_clk_handle, + bkp_freq->byte_clk_rate, i); + } + +exit: + dsi_clk_disable_unprepare(&display->clock_info.src_clks); + + return rc; +} + +static int dsi_display_dynamic_clk_switch(struct dsi_display *display, + struct dsi_display_mode *mode) +{ + int rc = 0, mask, i; + struct dsi_display_ctrl *m_ctrl, *ctrl; + struct dsi_dyn_clk_delay delay; + struct link_clk_freq bkp_freq; + + dsi_panel_acquire_panel_lock(display->panel); + + m_ctrl = &display->ctrl[display->clk_master_idx]; + + dsi_display_clk_ctrl(display->dsi_clk_handle, DSI_ALL_CLKS, DSI_CLK_ON); + + /* mask PLL unlock, FIFO overflow and underflow errors */ + mask = BIT(DSI_PLL_UNLOCK_ERR) | BIT(DSI_FIFO_UNDERFLOW) | + BIT(DSI_FIFO_OVERFLOW); + dsi_display_mask_ctrl_error_interrupts(display, mask, true); + + /* update the phy timings based on new mode */ + for (i = 0; i < display->ctrl_count; i++) { + ctrl = &display->ctrl[i]; + dsi_phy_update_phy_timings(ctrl->phy, &display->config); + } + + /* back up existing rates to handle failure case */ + bkp_freq.byte_clk_rate = m_ctrl->ctrl->clk_freq.byte_clk_rate; + bkp_freq.pix_clk_rate = m_ctrl->ctrl->clk_freq.pix_clk_rate; + bkp_freq.esc_clk_rate = m_ctrl->ctrl->clk_freq.esc_clk_rate; + + rc = dsi_display_update_dsi_bitrate(display, mode->timing.clk_rate_hz); + if (rc) { + pr_err("failed set link frequencies %d\n", rc); + goto exit; + } + + /* calculate pipe delays */ + _dsi_display_calc_pipe_delay(display, &delay, mode); + + /* configure dynamic refresh ctrl registers */ + for (i = 0; i < display->ctrl_count; i++) { + ctrl = &display->ctrl[i]; + if (!ctrl->phy) + continue; + if (ctrl == m_ctrl) + dsi_phy_config_dynamic_refresh(ctrl->phy, &delay, true); + else + dsi_phy_config_dynamic_refresh(ctrl->phy, &delay, + false); + } + + rc = _dsi_display_dyn_update_clks(display, &bkp_freq); + +exit: + dsi_display_mask_ctrl_error_interrupts(display, mask, false); + + dsi_display_clk_ctrl(display->dsi_clk_handle, DSI_ALL_CLKS, + DSI_CLK_OFF); + + /* store newly calculated phy timings in mode private info */ + dsi_phy_dyn_refresh_cache_phy_timings(m_ctrl->phy, + mode->priv_info->phy_timing_val, + mode->priv_info->phy_timing_len); + + dsi_panel_release_panel_lock(display->panel); + + return rc; +} + static int dsi_display_dfps_update(struct dsi_display *display, struct dsi_display_mode *dsi_mode) { @@ -4011,6 +4336,16 @@ static int dsi_display_set_mode_sub(struct dsi_display *display, display->name, rc); goto error; } + } else if (mode->dsi_mode_flags & DSI_MODE_FLAG_DYN_CLK) { + rc = dsi_display_dynamic_clk_switch(display, mode); + if (rc) + pr_err("dynamic clk change failed %d\n", rc); + /* + * skip rest of the opearations since + * dsi_display_dynamic_clk_switch() already takes + * care of them. + */ + return rc; } display_for_each_ctrl(i, display) { @@ -4255,85 +4590,6 @@ static int dsi_display_force_update_dsi_clk(struct dsi_display *display) return rc; } -static int dsi_display_request_update_dsi_bitrate(struct dsi_display *display, - u32 bit_clk_rate) -{ - int rc = 0; - int i; - - pr_debug("%s:bit rate:%d\n", __func__, bit_clk_rate); - if (!display->panel) { - pr_err("Invalid params\n"); - return -EINVAL; - } - - if (bit_clk_rate == 0) { - pr_err("Invalid bit clock rate\n"); - return -EINVAL; - } - - display->config.bit_clk_rate_hz_override = bit_clk_rate; - - display_for_each_ctrl(i, display) { - struct dsi_display_ctrl *dsi_disp_ctrl = &display->ctrl[i]; - struct dsi_ctrl *ctrl = dsi_disp_ctrl->ctrl; - u32 num_of_lanes = 0; - u32 bpp = 3; - u64 bit_rate, pclk_rate, bit_rate_per_lane, byte_clk_rate; - struct dsi_host_common_cfg *host_cfg; - - mutex_lock(&ctrl->ctrl_lock); - - host_cfg = &display->panel->host_config; - if (host_cfg->data_lanes & DSI_DATA_LANE_0) - num_of_lanes++; - if (host_cfg->data_lanes & DSI_DATA_LANE_1) - num_of_lanes++; - if (host_cfg->data_lanes & DSI_DATA_LANE_2) - num_of_lanes++; - if (host_cfg->data_lanes & DSI_DATA_LANE_3) - num_of_lanes++; - - if (num_of_lanes == 0) { - pr_err("Invalid lane count\n"); - rc = -EINVAL; - goto error; - } - - bit_rate = display->config.bit_clk_rate_hz_override * - num_of_lanes; - bit_rate_per_lane = bit_rate; - do_div(bit_rate_per_lane, num_of_lanes); - pclk_rate = bit_rate; - do_div(pclk_rate, (8 * bpp)); - byte_clk_rate = bit_rate_per_lane; - do_div(byte_clk_rate, 8); - pr_debug("bit_clk_rate = %llu, bit_clk_rate_per_lane = %llu\n", - bit_rate, bit_rate_per_lane); - pr_debug("byte_clk_rate = %llu, pclk_rate = %llu\n", - byte_clk_rate, pclk_rate); - - ctrl->clk_freq.byte_clk_rate = byte_clk_rate; - ctrl->clk_freq.pix_clk_rate = pclk_rate; - rc = dsi_clk_set_link_frequencies(display->dsi_clk_handle, - ctrl->clk_freq, ctrl->cell_index); - if (rc) { - pr_err("Failed to update link frequencies\n"); - goto error; - } - - ctrl->host_config.bit_clk_rate_hz_override = bit_clk_rate; -error: - mutex_unlock(&ctrl->ctrl_lock); - - /* TODO: recover ctrl->clk_freq in case of failure */ - if (rc) - return rc; - } - - return 0; -} - static ssize_t sysfs_dynamic_dsi_clk_read(struct device *dev, struct device_attribute *attr, char *buf) { @@ -4384,6 +4640,11 @@ static ssize_t sysfs_dynamic_dsi_clk_write(struct device *dev, return rc; } + if (display->panel->panel_mode != DSI_OP_CMD_MODE) { + pr_err("only supported for command mode\n"); + return -ENOTSUPP; + } + if (clk_rate <= 0) { pr_err("%s: bitrate should be greater than 0\n", __func__); return -EINVAL; @@ -4399,7 +4660,7 @@ static ssize_t sysfs_dynamic_dsi_clk_write(struct device *dev, mutex_lock(&display->display_lock); display->cached_clk_rate = clk_rate; - rc = dsi_display_request_update_dsi_bitrate(display, clk_rate); + rc = dsi_display_update_dsi_bitrate(display, clk_rate); if (!rc) { pr_info("%s: bit clk is ready to be configured to '%d'\n", __func__, clk_rate); @@ -5554,7 +5815,8 @@ static int dsi_display_get_mode_count_no_lock(struct dsi_display *display, u32 *count) { struct dsi_dfps_capabilities dfps_caps; - int num_dfps_rates, rc = 0; + struct dsi_dyn_clk_caps *dyn_clk_caps; + int num_dfps_rates, num_bit_clks, rc = 0; if (!display || !display->panel) { pr_err("invalid display:%d panel:%d\n", display != NULL, @@ -5571,12 +5833,16 @@ static int dsi_display_get_mode_count_no_lock(struct dsi_display *display, return rc; } - num_dfps_rates = !dfps_caps.dfps_support ? 1 : - dfps_caps.max_refresh_rate - - dfps_caps.min_refresh_rate + 1; + num_dfps_rates = !dfps_caps.dfps_support ? 1 : dfps_caps.dfps_list_len; + + dyn_clk_caps = &(display->panel->dyn_clk_caps); + + num_bit_clks = !dyn_clk_caps->dyn_clk_support ? 1 : + dyn_clk_caps->bit_clk_list_len; - /* Inflate num_of_modes by fps in dfps */ - *count = display->panel->num_timing_nodes * num_dfps_rates; + /* Inflate num_of_modes by fps and bit clks in dfps */ + *count = display->panel->num_timing_nodes * + num_dfps_rates * num_bit_clks; return 0; } @@ -5599,6 +5865,73 @@ int dsi_display_get_mode_count(struct dsi_display *display, return 0; } +static void _dsi_display_populate_bit_clks(struct dsi_display *display, + int start, int end, u32 *mode_idx) +{ + struct dsi_dyn_clk_caps *dyn_clk_caps; + struct dsi_display_mode *src, *dst; + struct dsi_host_common_cfg *cfg; + int i, j, total_modes, bpp, lanes = 0; + + if (!display || !mode_idx) + return; + + dyn_clk_caps = &(display->panel->dyn_clk_caps); + if (!dyn_clk_caps->dyn_clk_support) + return; + + cfg = &(display->panel->host_config); + bpp = dsi_pixel_format_to_bpp(cfg->dst_format); + + if (cfg->data_lanes & DSI_DATA_LANE_0) + lanes++; + if (cfg->data_lanes & DSI_DATA_LANE_1) + lanes++; + if (cfg->data_lanes & DSI_DATA_LANE_2) + lanes++; + if (cfg->data_lanes & DSI_DATA_LANE_3) + lanes++; + + dsi_display_get_mode_count_no_lock(display, &total_modes); + + for (i = start; i < end; i++) { + src = &display->modes[i]; + if (!src) + return; + /* + * TODO: currently setting the first bit rate in + * the list as preferred rate. But ideally should + * be based on user or device tree preferrence. + */ + src->timing.clk_rate_hz = dyn_clk_caps->bit_clk_list[0]; + src->pixel_clk_khz = + div_u64(src->timing.clk_rate_hz * lanes, bpp); + src->pixel_clk_khz /= 1000; + src->pixel_clk_khz *= display->ctrl_count; + } + + for (i = 1; i < dyn_clk_caps->bit_clk_list_len; i++) { + if (*mode_idx >= total_modes) + return; + for (j = start; j < end; j++) { + src = &display->modes[j]; + dst = &display->modes[*mode_idx]; + + if (!src || !dst) { + pr_err("invalid mode index\n"); + return; + } + memcpy(dst, src, sizeof(struct dsi_display_mode)); + dst->timing.clk_rate_hz = dyn_clk_caps->bit_clk_list[i]; + dst->pixel_clk_khz = + div_u64(dst->timing.clk_rate_hz * lanes, bpp); + dst->pixel_clk_khz /= 1000; + dst->pixel_clk_khz *= display->ctrl_count; + (*mode_idx)++; + } + } +} + void dsi_display_put_mode(struct dsi_display *display, struct dsi_display_mode *mode) { @@ -5613,7 +5946,8 @@ int dsi_display_get_modes(struct dsi_display *display, bool is_split_link; u32 num_dfps_rates, panel_mode_count, total_mode_count; u32 sublinks_count, mode_idx, array_idx = 0; - int i, rc = -EINVAL; + struct dsi_dyn_clk_caps *dyn_clk_caps; + int i, start, end, rc = -EINVAL; if (!display || !out_modes) { pr_err("Invalid params\n"); @@ -5645,9 +5979,9 @@ int dsi_display_get_modes(struct dsi_display *display, goto error; } - num_dfps_rates = !dfps_caps.dfps_support ? 1 : - dfps_caps.max_refresh_rate - - dfps_caps.min_refresh_rate + 1; + dyn_clk_caps = &(display->panel->dyn_clk_caps); + + num_dfps_rates = !dfps_caps.dfps_support ? 1 : dfps_caps.dfps_list_len; panel_mode_count = display->panel->num_timing_nodes; @@ -5677,7 +6011,7 @@ int dsi_display_get_modes(struct dsi_display *display, panel_mode.timing.h_back_porch *= sublinks_count; panel_mode.timing.h_skew *= sublinks_count; panel_mode.pixel_clk_khz *= sublinks_count; - } else if (display->ctrl_count > 1) { + } else { panel_mode.timing.h_active *= display->ctrl_count; panel_mode.timing.h_front_porch *= display->ctrl_count; panel_mode.timing.h_sync_width *= display->ctrl_count; @@ -5686,6 +6020,8 @@ int dsi_display_get_modes(struct dsi_display *display, panel_mode.pixel_clk_khz *= display->ctrl_count; } + start = array_idx; + for (i = 0; i < num_dfps_rates; i++) { struct dsi_display_mode *sub_mode = &display->modes[array_idx]; @@ -5698,24 +6034,23 @@ int dsi_display_get_modes(struct dsi_display *display, } memcpy(sub_mode, &panel_mode, sizeof(panel_mode)); + array_idx++; - if (dfps_caps.dfps_support) { - curr_refresh_rate = - sub_mode->timing.refresh_rate; - sub_mode->timing.refresh_rate = - dfps_caps.min_refresh_rate + - (i % num_dfps_rates); + if (!dfps_caps.dfps_support) + continue; - dsi_display_get_dfps_timing(display, - sub_mode, curr_refresh_rate); + curr_refresh_rate = sub_mode->timing.refresh_rate; + sub_mode->timing.refresh_rate = dfps_caps.dfps_list[i]; - sub_mode->pixel_clk_khz = - (DSI_H_TOTAL_DSC(&sub_mode->timing) * - DSI_V_TOTAL(&sub_mode->timing) * - sub_mode->timing.refresh_rate) / 1000; - } - array_idx++; + dsi_display_get_dfps_timing(display, sub_mode, + curr_refresh_rate); } + end = array_idx; + /* + * if dynamic clk switch is supported then update all the bit + * clk rates. + */ + _dsi_display_populate_bit_clks(display, start, end, &array_idx); } exit: @@ -5812,7 +6147,8 @@ int dsi_display_find_mode(struct dsi_display *display, if (cmp->timing.v_active == m->timing.v_active && cmp->timing.h_active == m->timing.h_active && - cmp->timing.refresh_rate == m->timing.refresh_rate) { + cmp->timing.refresh_rate == m->timing.refresh_rate && + cmp->pixel_clk_khz == m->pixel_clk_khz) { *out_mode = m; rc = 0; break; @@ -5821,9 +6157,10 @@ int dsi_display_find_mode(struct dsi_display *display, mutex_unlock(&display->display_lock); if (!*out_mode) { - pr_err("[%s] failed to find mode for v_active %u h_active %u rate %u\n", + pr_err("[%s] failed to find mode for v_active %u h_active %u fps %u pclk %u\n", display->name, cmp->timing.v_active, - cmp->timing.h_active, cmp->timing.refresh_rate); + cmp->timing.h_active, cmp->timing.refresh_rate, + cmp->pixel_clk_khz); rc = -ENOENT; } @@ -5831,7 +6168,7 @@ int dsi_display_find_mode(struct dsi_display *display, } /** - * dsi_display_validate_mode_vrr() - Validate if varaible refresh case. + * dsi_display_validate_mode_change() - Validate if varaible refresh case. * @display: DSI display handle. * @cur_dsi_mode: Current DSI mode. * @mode: Mode value structure to be validated. @@ -5839,16 +6176,15 @@ int dsi_display_find_mode(struct dsi_display *display, * is change in fps but vactive and hactive are same. * Return: error code. */ -int dsi_display_validate_mode_vrr(struct dsi_display *display, - struct dsi_display_mode *cur_dsi_mode, - struct dsi_display_mode *mode) +int dsi_display_validate_mode_change(struct dsi_display *display, + struct dsi_display_mode *cur_mode, + struct dsi_display_mode *adj_mode) { int rc = 0; - struct dsi_display_mode adj_mode, cur_mode; struct dsi_dfps_capabilities dfps_caps; - u32 curr_refresh_rate; + struct dsi_dyn_clk_caps *dyn_clk_caps; - if (!display || !mode) { + if (!display || !adj_mode) { pr_err("Invalid params\n"); return -EINVAL; } @@ -5860,65 +6196,43 @@ int dsi_display_validate_mode_vrr(struct dsi_display *display, mutex_lock(&display->display_lock); - adj_mode = *mode; - cur_mode = *cur_dsi_mode; - - if ((cur_mode.timing.refresh_rate != adj_mode.timing.refresh_rate) && - (cur_mode.timing.v_active == adj_mode.timing.v_active) && - (cur_mode.timing.h_active == adj_mode.timing.h_active)) { - - curr_refresh_rate = cur_mode.timing.refresh_rate; - rc = dsi_panel_get_dfps_caps(display->panel, &dfps_caps); - if (rc) { - pr_err("[%s] failed to get dfps caps from panel\n", - display->name); - goto error; - } - - cur_mode.timing.refresh_rate = - adj_mode.timing.refresh_rate; - - rc = dsi_display_get_dfps_timing(display, - &cur_mode, curr_refresh_rate); - if (rc) { - pr_err("[%s] seamless vrr not possible rc=%d\n", - display->name, rc); - goto error; + if ((cur_mode->timing.v_active == adj_mode->timing.v_active) && + (cur_mode->timing.h_active == adj_mode->timing.h_active)) { + /* dfps change use case */ + if (cur_mode->timing.refresh_rate != + adj_mode->timing.refresh_rate) { + dsi_panel_get_dfps_caps(display->panel, &dfps_caps); + if (!dfps_caps.dfps_support) { + pr_err("invalid mode dfps not supported\n"); + rc = -ENOTSUPP; + goto error; + } + pr_debug("Mode switch is seamless variable refresh\n"); + adj_mode->dsi_mode_flags |= DSI_MODE_FLAG_VRR; + SDE_EVT32(cur_mode->timing.refresh_rate, + adj_mode->timing.refresh_rate, + cur_mode->timing.h_front_porch, + adj_mode->timing.h_front_porch); } - switch (dfps_caps.type) { - /* - * Ignore any round off factors in porch calculation. - * Worse case is set to 5. - */ - case DSI_DFPS_IMMEDIATE_VFP: - if (abs(DSI_V_TOTAL(&cur_mode.timing) - - DSI_V_TOTAL(&adj_mode.timing)) > 5) - pr_err("Mismatch vfp fps:%d new:%d given:%d\n", - adj_mode.timing.refresh_rate, - cur_mode.timing.v_front_porch, - adj_mode.timing.v_front_porch); - break; - - case DSI_DFPS_IMMEDIATE_HFP: - if (abs(DSI_H_TOTAL_DSC(&cur_mode.timing) - - DSI_H_TOTAL_DSC(&adj_mode.timing)) > 5) - pr_err("Mismatch hfp fps:%d new:%d given:%d\n", - adj_mode.timing.refresh_rate, - cur_mode.timing.h_front_porch, - adj_mode.timing.h_front_porch); - break; - default: - pr_err("Unsupported DFPS mode %d\n", - dfps_caps.type); - rc = -ENOTSUPP; + /* dynamic clk change use case */ + if (cur_mode->pixel_clk_khz != adj_mode->pixel_clk_khz) { + dyn_clk_caps = &(display->panel->dyn_clk_caps); + if (!dyn_clk_caps->dyn_clk_support) { + pr_err("dyn clk change not supported\n"); + rc = -ENOTSUPP; + goto error; + } + if (adj_mode->dsi_mode_flags & DSI_MODE_FLAG_VRR) { + pr_err("dfps and dyn clk not supported in same commit\n"); + rc = -ENOTSUPP; + goto error; + } + pr_debug("dynamic clk change detected\n"); + adj_mode->dsi_mode_flags |= DSI_MODE_FLAG_DYN_CLK; + SDE_EVT32(cur_mode->pixel_clk_khz, + adj_mode->pixel_clk_khz); } - - pr_debug("Mode switch is seamless variable refresh\n"); - mode->dsi_mode_flags |= DSI_MODE_FLAG_VRR; - SDE_EVT32(curr_refresh_rate, adj_mode.timing.refresh_rate, - cur_mode.timing.h_front_porch, - adj_mode.timing.h_front_porch); } error: diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.h b/drivers/gpu/drm/msm/dsi-staging/dsi_display.h index 807699604198..1eef2fa5aa03 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.h +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2018, The Linux Foundation.All rights reserved. + * Copyright (c) 2015-2019, 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 @@ -409,13 +409,14 @@ int dsi_display_validate_mode(struct dsi_display *display, u32 flags); /** - * dsi_display_validate_mode_vrr() - validates mode if variable refresh case + * dsi_display_validate_mode_change() - validates mode if variable refresh case + * or dynamic clk change case * @display: Handle to display. * @mode: Mode to be validated.. * * Return: 0 if error code. */ -int dsi_display_validate_mode_vrr(struct dsi_display *display, +int dsi_display_validate_mode_change(struct dsi_display *display, struct dsi_display_mode *cur_dsi_mode, struct dsi_display_mode *mode); diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c index d81d57a56318..5efef6cf5626 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2019, 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 @@ -80,6 +80,8 @@ static void convert_to_dsi_mode(const struct drm_display_mode *drm_mode, dsi_mode->dsi_mode_flags |= DSI_MODE_FLAG_DMS; if (msm_is_mode_seamless_vrr(drm_mode)) dsi_mode->dsi_mode_flags |= DSI_MODE_FLAG_VRR; + if (msm_is_mode_seamless_dyn_clk(drm_mode)) + dsi_mode->dsi_mode_flags |= DSI_MODE_FLAG_DYN_CLK; dsi_mode->timing.h_sync_polarity = !!(drm_mode->flags & DRM_MODE_FLAG_PHSYNC); @@ -122,13 +124,18 @@ void dsi_convert_to_drm_mode(const struct dsi_display_mode *dsi_mode, drm_mode->private_flags |= MSM_MODE_FLAG_SEAMLESS_DMS; if (dsi_mode->dsi_mode_flags & DSI_MODE_FLAG_VRR) drm_mode->private_flags |= MSM_MODE_FLAG_SEAMLESS_VRR; + if (dsi_mode->dsi_mode_flags & DSI_MODE_FLAG_DYN_CLK) + drm_mode->private_flags |= MSM_MODE_FLAG_SEAMLESS_DYN_CLK; if (dsi_mode->timing.h_sync_polarity) drm_mode->flags |= DRM_MODE_FLAG_PHSYNC; if (dsi_mode->timing.v_sync_polarity) drm_mode->flags |= DRM_MODE_FLAG_PVSYNC; - drm_mode_set_name(drm_mode); + /* set mode name */ + snprintf(drm_mode->name, DRM_DISPLAY_MODE_LEN, "%dx%dx%dx%d", + drm_mode->hdisplay, drm_mode->vdisplay, + drm_mode->vrefresh, drm_mode->clock); } static int dsi_bridge_attach(struct drm_bridge *bridge) @@ -173,7 +180,8 @@ static void dsi_bridge_pre_enable(struct drm_bridge *bridge) } if (c_bridge->dsi_mode.dsi_mode_flags & - (DSI_MODE_FLAG_SEAMLESS | DSI_MODE_FLAG_VRR)) { + (DSI_MODE_FLAG_SEAMLESS | DSI_MODE_FLAG_VRR | + DSI_MODE_FLAG_DYN_CLK)) { pr_debug("[%d] seamless pre-enable\n", c_bridge->id); return; } @@ -296,6 +304,12 @@ static void dsi_bridge_mode_set(struct drm_bridge *bridge, memset(&(c_bridge->dsi_mode), 0x0, sizeof(struct dsi_display_mode)); convert_to_dsi_mode(adjusted_mode, &(c_bridge->dsi_mode)); + + /* restore bit_clk_rate also for dynamic clk use cases */ + c_bridge->dsi_mode.timing.clk_rate_hz = + dsi_drm_find_bit_clk_rate(c_bridge->display, adjusted_mode); + + pr_debug("clk_rate: %llu\n", c_bridge->dsi_mode.timing.clk_rate_hz); } static bool dsi_bridge_mode_fixup(struct drm_bridge *bridge, @@ -364,17 +378,20 @@ static bool dsi_bridge_mode_fixup(struct drm_bridge *bridge, cur_dsi_mode.timing.dsc_enabled = dsi_mode.priv_info->dsc_enabled; cur_dsi_mode.timing.dsc = &dsi_mode.priv_info->dsc; - rc = dsi_display_validate_mode_vrr(c_bridge->display, + rc = dsi_display_validate_mode_change(c_bridge->display, &cur_dsi_mode, &dsi_mode); - if (rc) - pr_debug("[%s] vrr mode mismatch failure rc=%d\n", + if (rc) { + pr_err("[%s] seamless mode mismatch failure rc=%d\n", c_bridge->display->name, rc); + return false; + } cur_mode = crtc_state->crtc->mode; /* No DMS/VRR when drm pipeline is changing */ if (!drm_mode_equal(&cur_mode, adjusted_mode) && (!(dsi_mode.dsi_mode_flags & DSI_MODE_FLAG_VRR)) && + (!(dsi_mode.dsi_mode_flags & DSI_MODE_FLAG_DYN_CLK)) && (!crtc_state->active_changed || display->is_cont_splash_enabled)) dsi_mode.dsi_mode_flags |= DSI_MODE_FLAG_DMS; @@ -386,6 +403,33 @@ static bool dsi_bridge_mode_fixup(struct drm_bridge *bridge, return true; } +u64 dsi_drm_find_bit_clk_rate(void *display, + const struct drm_display_mode *drm_mode) +{ + int i = 0, count = 0; + struct dsi_display *dsi_display = display; + struct dsi_display_mode *dsi_mode; + u64 bit_clk_rate = 0; + + if (!dsi_display || !drm_mode) + return 0; + + dsi_display_get_mode_count(dsi_display, &count); + + for (i = 0; i < count; i++) { + dsi_mode = &dsi_display->modes[i]; + if ((dsi_mode->timing.v_active == drm_mode->vdisplay) && + (dsi_mode->timing.h_active == drm_mode->hdisplay) && + (dsi_mode->pixel_clk_khz == drm_mode->clock) && + (dsi_mode->timing.refresh_rate == drm_mode->vrefresh)) { + bit_clk_rate = dsi_mode->timing.clk_rate_hz; + break; + } + } + + return bit_clk_rate; +} + int dsi_conn_get_mode_info(struct drm_connector *connector, const struct drm_display_mode *drm_mode, struct msm_mode_info *mode_info, @@ -410,7 +454,7 @@ int dsi_conn_get_mode_info(struct drm_connector *connector, mode_info->prefill_lines = dsi_mode.priv_info->panel_prefill_lines; mode_info->jitter_numer = dsi_mode.priv_info->panel_jitter_numer; mode_info->jitter_denom = dsi_mode.priv_info->panel_jitter_denom; - mode_info->clk_rate = dsi_mode.priv_info->clk_rate_hz; + mode_info->clk_rate = dsi_drm_find_bit_clk_rate(display, drm_mode); mode_info->mdp_transfer_time_us = dsi_mode.priv_info->mdp_transfer_time_us; @@ -516,6 +560,9 @@ int dsi_conn_set_info_blob(struct drm_connector *connector, panel->dfps_caps.max_refresh_rate); } + sde_kms_info_add_keystr(info, "dyn bitclk support", + panel->dyn_clk_caps.dyn_clk_support ? "true" : "false"); + switch (panel->phy_props.rotation) { case DSI_PANEL_ROTATE_NONE: sde_kms_info_add_keystr(info, "panel orientation", "none"); @@ -676,6 +723,9 @@ int dsi_connector_get_modes(struct drm_connector *connector, } m->width_mm = connector->display_info.width_mm; m->height_mm = connector->display_info.height_mm; + /* set the first mode in list as preferred */ + if (i == 0) + m->type |= DRM_MODE_TYPE_PREFERRED; drm_mode_probed_add(connector, m); } end: @@ -783,6 +833,9 @@ int dsi_conn_post_kickoff(struct drm_connector *connector) c_bridge->dsi_mode.dsi_mode_flags &= ~DSI_MODE_FLAG_VRR; } + /* ensure dynamic clk switch flag is reset */ + c_bridge->dsi_mode.dsi_mode_flags &= ~DSI_MODE_FLAG_DYN_CLK; + return 0; } diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.h b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.h index 7ec55485cfab..1af30e1d8090 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.h +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2019, 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 @@ -139,4 +139,6 @@ int dsi_conn_post_kickoff(struct drm_connector *connector); void dsi_convert_to_drm_mode(const struct dsi_display_mode *dsi_mode, struct drm_display_mode *drm_mode); +u64 dsi_drm_find_bit_clk_rate(void *display, + const struct drm_display_mode *drm_mode); #endif /* _DSI_DRM_H_ */ diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_hw.h b/drivers/gpu/drm/msm/dsi-staging/dsi_hw.h index ec44e4b3f487..77c6694fd13e 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_hw.h +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_hw.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2019, 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 @@ -53,4 +53,14 @@ #define DSI_R64(dsi_hw, off) readq_relaxed((dsi_hw)->base + (off)) #define DSI_W64(dsi_hw, off, val) writeq_relaxed((val), (dsi_hw)->base + (off)) +#define PLL_CALC_DATA(addr0, addr1, data0, data1) \ + (((data1) << 24) | ((((addr1)/4) & 0xFF) << 16) | \ + ((data0) << 8) | (((addr0)/4) & 0xFF)) + +#define DSI_DYN_REF_REG_W(base, offset, addr0, addr1, data0, data1) \ + writel_relaxed(PLL_CALC_DATA(addr0, addr1, data0, data1), \ + (base) + (offset)) + +#define DSI_GEN_R32(base, offset) readl_relaxed(base + (offset)) +#define DSI_GEN_W32(base, offset, val) writel_relaxed((val), base + (offset)) #endif /* _DSI_HW_H_ */ diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c index cf73ffce17d8..a72cdbc10b0c 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2019, 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 @@ -1186,6 +1186,48 @@ static int dsi_panel_parse_qsync_caps(struct dsi_panel *panel, return rc; } +static int dsi_panel_parse_dyn_clk_caps(struct dsi_panel *panel) +{ + int rc = 0; + bool supported = false; + struct dsi_dyn_clk_caps *dyn_clk_caps = &panel->dyn_clk_caps; + struct dsi_parser_utils *utils = &panel->utils; + const char *name = panel->name; + + supported = utils->read_bool(utils->data, "qcom,dsi-dyn-clk-enable"); + + if (!supported) { + dyn_clk_caps->dyn_clk_support = false; + return rc; + } + + dyn_clk_caps->bit_clk_list_len = utils->count_u32_elems(utils->data, + "qcom,dsi-dyn-clk-list"); + + if (dyn_clk_caps->bit_clk_list_len < 1) { + pr_err("[%s] failed to get supported bit clk list\n", name); + return -EINVAL; + } + + dyn_clk_caps->bit_clk_list = kcalloc(dyn_clk_caps->bit_clk_list_len, + sizeof(u32), GFP_KERNEL); + if (!dyn_clk_caps->bit_clk_list) + return -ENOMEM; + + rc = utils->read_u32_array(utils->data, "qcom,dsi-dyn-clk-list", + dyn_clk_caps->bit_clk_list, + dyn_clk_caps->bit_clk_list_len); + + if (rc) { + pr_err("[%s] failed to parse supported bit clk list\n", name); + return -EINVAL; + } + + dyn_clk_caps->dyn_clk_support = true; + + return 0; +} + static int dsi_panel_parse_dfps_caps(struct dsi_panel *panel) { int rc = 0; @@ -1194,7 +1236,7 @@ static int dsi_panel_parse_dfps_caps(struct dsi_panel *panel) struct dsi_parser_utils *utils = &panel->utils; const char *name = panel->name; const char *type; - u32 val = 0; + u32 i; supported = utils->read_bool(utils->data, "qcom,mdss-dsi-pan-enable-dynamic-fps"); @@ -1202,66 +1244,64 @@ static int dsi_panel_parse_dfps_caps(struct dsi_panel *panel) if (!supported) { pr_debug("[%s] DFPS is not supported\n", name); dfps_caps->dfps_support = false; + return rc; + } + + type = utils->get_property(utils->data, + "qcom,mdss-dsi-pan-fps-update", NULL); + if (!type) { + pr_err("[%s] dfps type not defined\n", name); + rc = -EINVAL; + goto error; + } else if (!strcmp(type, "dfps_suspend_resume_mode")) { + dfps_caps->type = DSI_DFPS_SUSPEND_RESUME; + } else if (!strcmp(type, "dfps_immediate_clk_mode")) { + dfps_caps->type = DSI_DFPS_IMMEDIATE_CLK; + } else if (!strcmp(type, "dfps_immediate_porch_mode_hfp")) { + dfps_caps->type = DSI_DFPS_IMMEDIATE_HFP; + } else if (!strcmp(type, "dfps_immediate_porch_mode_vfp")) { + dfps_caps->type = DSI_DFPS_IMMEDIATE_VFP; } else { + pr_err("[%s] dfps type is not recognized\n", name); + rc = -EINVAL; + goto error; + } - type = utils->get_property(utils->data, - "qcom,mdss-dsi-pan-fps-update", - NULL); - if (!type) { - pr_err("[%s] dfps type not defined\n", name); - rc = -EINVAL; - goto error; - } else if (!strcmp(type, "dfps_suspend_resume_mode")) { - dfps_caps->type = DSI_DFPS_SUSPEND_RESUME; - } else if (!strcmp(type, "dfps_immediate_clk_mode")) { - dfps_caps->type = DSI_DFPS_IMMEDIATE_CLK; - } else if (!strcmp(type, "dfps_immediate_porch_mode_hfp")) { - dfps_caps->type = DSI_DFPS_IMMEDIATE_HFP; - } else if (!strcmp(type, "dfps_immediate_porch_mode_vfp")) { - dfps_caps->type = DSI_DFPS_IMMEDIATE_VFP; - } else { - pr_err("[%s] dfps type is not recognized\n", name); - rc = -EINVAL; - goto error; - } + dfps_caps->dfps_list_len = utils->count_u32_elems(utils->data, + "qcom,dsi-supported-dfps-list"); + if (dfps_caps->dfps_list_len < 1) { + pr_err("[%s] dfps refresh list not present\n", name); + rc = -EINVAL; + goto error; + } - rc = utils->read_u32(utils->data, - "qcom,mdss-dsi-min-refresh-rate", - &val); - if (rc) { - pr_err("[%s] Min refresh rate is not defined\n", name); - rc = -EINVAL; - goto error; - } - dfps_caps->min_refresh_rate = val; + dfps_caps->dfps_list = kcalloc(dfps_caps->dfps_list_len, sizeof(u32), + GFP_KERNEL); + if (!dfps_caps->dfps_list) { + rc = -ENOMEM; + goto error; + } - rc = utils->read_u32(utils->data, - "qcom,mdss-dsi-max-refresh-rate", - &val); - if (rc) { - pr_debug("[%s] Using default refresh rate\n", name); - rc = utils->read_u32(utils->data, - "qcom,mdss-dsi-panel-framerate", - &val); - if (rc) { - pr_err("[%s] max refresh rate is not defined\n", - name); - rc = -EINVAL; - goto error; - } - } - dfps_caps->max_refresh_rate = val; + rc = utils->read_u32_array(utils->data, + "qcom,dsi-supported-dfps-list", + dfps_caps->dfps_list, + dfps_caps->dfps_list_len); + if (rc) { + pr_err("[%s] dfps refresh rate list parse failed\n", name); + rc = -EINVAL; + goto error; + } + dfps_caps->dfps_support = true; - if (dfps_caps->min_refresh_rate > dfps_caps->max_refresh_rate) { - pr_err("[%s] min rate > max rate\n", name); - rc = -EINVAL; - } + /* calculate max and min fps */ + dfps_caps->max_refresh_rate = dfps_caps->dfps_list[0]; + dfps_caps->min_refresh_rate = dfps_caps->dfps_list[0]; - pr_debug("[%s] DFPS is supported %d-%d, mode %d\n", name, - dfps_caps->min_refresh_rate, - dfps_caps->max_refresh_rate, - dfps_caps->type); - dfps_caps->dfps_support = true; + for (i = 1; i < dfps_caps->dfps_list_len; i++) { + if (dfps_caps->dfps_list[i] < dfps_caps->min_refresh_rate) + dfps_caps->min_refresh_rate = dfps_caps->dfps_list[i]; + else if (dfps_caps->dfps_list[i] > dfps_caps->max_refresh_rate) + dfps_caps->max_refresh_rate = dfps_caps->dfps_list[i]; } error: @@ -3041,6 +3081,13 @@ struct dsi_panel *dsi_panel_get(struct device *parent, pr_err("failed to parse qsync features, rc=%d\n", rc); } + if (panel->panel_mode == DSI_OP_VIDEO_MODE) { + rc = dsi_panel_parse_dyn_clk_caps(panel); + if (rc) + pr_err("failed to parse dynamic clk config, rc=%d\n", + rc); + } + rc = dsi_panel_parse_phy_props(panel); if (rc) { pr_err("failed to parse panel physical dimension, rc=%d\n", rc); @@ -3430,7 +3477,7 @@ int dsi_panel_get_host_cfg_for_mode(struct dsi_panel *panel, config->video_timing.dsc_enabled = mode->priv_info->dsc_enabled; config->video_timing.dsc = &mode->priv_info->dsc; - config->bit_clk_rate_hz_override = mode->priv_info->clk_rate_hz; + config->bit_clk_rate_hz_override = mode->timing.clk_rate_hz; config->esc_clk_rate_hz = 19200000; mutex_unlock(&panel->panel_lock); return rc; diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h index 1f0c55882508..cfa710708426 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2019, 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 @@ -72,10 +72,18 @@ enum dsi_dms_mode { }; struct dsi_dfps_capabilities { - bool dfps_support; enum dsi_dfps_type type; u32 min_refresh_rate; u32 max_refresh_rate; + u32 *dfps_list; + u32 dfps_list_len; + bool dfps_support; +}; + +struct dsi_dyn_clk_caps { + bool dyn_clk_support; + u32 *bit_clk_list; + u32 bit_clk_list_len; }; struct dsi_pinctrl_info { @@ -167,6 +175,7 @@ struct dsi_panel { enum dsi_op_mode panel_mode; struct dsi_dfps_capabilities dfps_caps; + struct dsi_dyn_clk_caps dyn_clk_caps; struct dsi_panel_phy_props phy_props; struct dsi_display_mode *cur_mode; diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c index e86d03cac6a1..fb88c9403f68 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2019, 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 @@ -117,6 +117,9 @@ static int dsi_phy_regmap_init(struct platform_device *pdev, phy->hw.base = ptr; + ptr = msm_ioremap(pdev, "dyn_refresh_base", phy->name); + phy->hw.dyn_pll_base = ptr; + pr_debug("[%s] map dsi_phy registers to %pK\n", phy->name, phy->hw.base); @@ -639,11 +642,8 @@ int dsi_phy_validate_mode(struct msm_dsi_phy *dsi_phy, return -EINVAL; } - mutex_lock(&dsi_phy->phy_lock); - pr_debug("[PHY_%d] Skipping validation\n", dsi_phy->index); - mutex_unlock(&dsi_phy->phy_lock); return rc; } @@ -885,7 +885,7 @@ int dsi_phy_enable(struct msm_dsi_phy *phy, rc = phy->hw.ops.calculate_timing_params(&phy->hw, &phy->mode, &config->common_config, - &phy->cfg.timing); + &phy->cfg.timing, false); if (rc) { pr_err("[%s] failed to set timing, rc=%d\n", phy->name, rc); goto error; @@ -903,6 +903,27 @@ error: return rc; } +/* update dsi phy timings for dynamic clk switch use case */ +int dsi_phy_update_phy_timings(struct msm_dsi_phy *phy, + struct dsi_host_config *config) +{ + int rc = 0; + + if (!phy || !config) { + pr_err("invalid argument\n"); + return -EINVAL; + } + + memcpy(&phy->mode, &config->video_timing, sizeof(phy->mode)); + rc = phy->hw.ops.calculate_timing_params(&phy->hw, &phy->mode, + &config->common_config, + &phy->cfg.timing, true); + if (rc) + pr_err("failed to calculate phy timings %d\n", rc); + + return rc; +} + int dsi_phy_lane_reset(struct msm_dsi_phy *phy) { int ret = 0; @@ -1067,6 +1088,7 @@ int dsi_phy_set_timing_params(struct msm_dsi_phy *phy, rc = phy->hw.ops.phy_timing_val(&phy->cfg.timing, timing, size); if (!rc) phy->cfg.is_phy_timing_present = true; + mutex_unlock(&phy->phy_lock); return rc; } @@ -1115,6 +1137,106 @@ int dsi_phy_conv_logical_to_phy_lane( return i; } + +/** + * dsi_phy_config_dynamic_refresh() - Configure dynamic refresh registers + * @phy: DSI PHY handle + * @delay: pipe delays for dynamic refresh + * @is_master: Boolean to indicate if for master or slave. + */ +void dsi_phy_config_dynamic_refresh(struct msm_dsi_phy *phy, + struct dsi_dyn_clk_delay *delay, + bool is_master) +{ + struct dsi_phy_cfg *cfg; + + if (!phy) + return; + + mutex_lock(&phy->phy_lock); + + cfg = &phy->cfg; + if (phy->hw.ops.dyn_refresh_ops.dyn_refresh_config) + phy->hw.ops.dyn_refresh_ops.dyn_refresh_config(&phy->hw, cfg, + is_master); + if (phy->hw.ops.dyn_refresh_ops.dyn_refresh_pipe_delay) + phy->hw.ops.dyn_refresh_ops.dyn_refresh_pipe_delay( + &phy->hw, delay); + + mutex_unlock(&phy->phy_lock); +} + +/** + * dsi_phy_dynamic_refresh_trigger() - trigger dynamic refresh + * @phy: DSI PHY handle + * @is_master: Boolean to indicate if for master or slave. + */ +void dsi_phy_dynamic_refresh_trigger(struct msm_dsi_phy *phy, bool is_master) +{ + u32 off; + + if (!phy) + return; + + mutex_lock(&phy->phy_lock); + /* + * program PLL_SWI_INTF_SEL and SW_TRIGGER bit only for + * master and program SYNC_MODE bit only for slave. + */ + if (is_master) + off = BIT(DYN_REFRESH_INTF_SEL) | BIT(DYN_REFRESH_SWI_CTRL) | + BIT(DYN_REFRESH_SW_TRIGGER); + else + off = BIT(DYN_REFRESH_SYNC_MODE) | BIT(DYN_REFRESH_SWI_CTRL); + + if (phy->hw.ops.dyn_refresh_ops.dyn_refresh_helper) + phy->hw.ops.dyn_refresh_ops.dyn_refresh_helper(&phy->hw, off); + + mutex_unlock(&phy->phy_lock); +} + +/** + * dsi_phy_cache_phy_timings - cache the phy timings calculated as part of + * dynamic refresh. + * @phy: DSI PHY Handle. + * @dst: Pointer to cache location. + * @size: Number of phy lane settings. + */ +int dsi_phy_dyn_refresh_cache_phy_timings(struct msm_dsi_phy *phy, u32 *dst, + u32 size) +{ + int rc = 0; + + if (!phy || !dst || !size) + return -EINVAL; + + if (phy->hw.ops.dyn_refresh_ops.cache_phy_timings) + rc = phy->hw.ops.dyn_refresh_ops.cache_phy_timings( + &phy->cfg.timing, dst, size); + + if (rc) + pr_err("failed to cache phy timings %d\n", rc); + + return rc; +} + +/** + * dsi_phy_dynamic_refresh_clear() - clear dynamic refresh config + * @phy: DSI PHY handle + */ +void dsi_phy_dynamic_refresh_clear(struct msm_dsi_phy *phy) +{ + if (!phy) + return; + + mutex_lock(&phy->phy_lock); + + if (phy->hw.ops.dyn_refresh_ops.dyn_refresh_helper) + phy->hw.ops.dyn_refresh_ops.dyn_refresh_helper(&phy->hw, 0); + + mutex_unlock(&phy->phy_lock); +} + void dsi_phy_drv_register(void) { platform_driver_register(&dsi_phy_platform_driver); diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.h b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.h index e54b42fdd2df..ba317db5b279 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.h +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2019, 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 @@ -298,4 +298,45 @@ void dsi_phy_drv_register(void); */ void dsi_phy_drv_unregister(void); +/** + * dsi_phy_update_phy_timings() - Update dsi phy timings + * @phy: DSI PHY handle + * @config: DSI Host config parameters + * + * Return: error code. + */ +int dsi_phy_update_phy_timings(struct msm_dsi_phy *phy, + struct dsi_host_config *config); + +/** + * dsi_phy_config_dynamic_refresh() - Configure dynamic refresh registers + * @phy: DSI PHY handle + * @delay: pipe delays for dynamic refresh + * @is_master: Boolean to indicate if for master or slave + */ +void dsi_phy_config_dynamic_refresh(struct msm_dsi_phy *phy, + struct dsi_dyn_clk_delay *delay, + bool is_master); +/** + * dsi_phy_dynamic_refresh_trigger() - trigger dynamic refresh + * @phy: DSI PHY handle + * @is_master: Boolean to indicate if for master or slave. + */ +void dsi_phy_dynamic_refresh_trigger(struct msm_dsi_phy *phy, bool is_master); + +/** + * dsi_phy_dynamic_refresh_clear() - clear dynamic refresh config + * @phy: DSI PHY handle + */ +void dsi_phy_dynamic_refresh_clear(struct msm_dsi_phy *phy); + +/** + * dsi_phy_dyn_refresh_cache_phy_timings - cache the phy timings calculated + * as part of dynamic refresh. + * @phy: DSI PHY Handle. + * @dst: Pointer to cache location. + * @size: Number of phy lane settings. + */ +int dsi_phy_dyn_refresh_cache_phy_timings(struct msm_dsi_phy *phy, + u32 *dst, u32 size); #endif /* _DSI_PHY_H_ */ diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw.h b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw.h index df72005d9f54..2101e6b8b484 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw.h +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2019, 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 @@ -169,6 +169,43 @@ struct phy_ulps_config_ops { bool (*is_lanes_in_ulps)(u32 ulps, u32 ulps_lanes); }; +struct phy_dyn_refresh_ops { + /** + * dyn_refresh_helper - helper function to config particular registers + * @phy: Pointer to DSI PHY hardware instance. + * @offset: register offset to program. + */ + void (*dyn_refresh_helper)(struct dsi_phy_hw *phy, u32 offset); + + /** + * dyn_refresh_config - configure dynamic refresh ctrl registers + * @phy: Pointer to DSI PHY hardware instance. + * @cfg: Pointer to DSI PHY timings. + * @is_master: Boolean to indicate whether for master or slave. + */ + void (*dyn_refresh_config)(struct dsi_phy_hw *phy, + struct dsi_phy_cfg *cfg, bool is_master); + + /** + * dyn_refresh_pipe_delay - configure pipe delay registers for dynamic + * refresh. + * @phy: Pointer to DSI PHY hardware instance. + * @delay: structure containing all the delays to be programed. + */ + void (*dyn_refresh_pipe_delay)(struct dsi_phy_hw *phy, + struct dsi_dyn_clk_delay *delay); + + /** + * cache_phy_timings - cache the phy timings calculated as part of + * dynamic refresh. + * @timings: Pointer to calculated phy timing parameters. + * @dst: Pointer to cache location. + * @size: Number of phy lane settings. + */ + int (*cache_phy_timings)(struct dsi_phy_per_lane_cfgs *timings, + u32 *dst, u32 size); +}; + /** * struct dsi_phy_hw_ops - Operations for DSI PHY hardware. * @regulator_enable: Enable PHY regulators. @@ -228,11 +265,14 @@ struct dsi_phy_hw_ops { * @mode: Mode information for which timing has to be calculated. * @config: DSI host configuration for this mode. * @timing: Timing parameters for each lane which will be returned. + * @use_mode_bit_clk: Boolean to indicate whether reacalculate dsi + * bitclk or use the existing bitclk(for dynamic clk case). */ int (*calculate_timing_params)(struct dsi_phy_hw *phy, struct dsi_mode_info *mode, struct dsi_host_common_cfg *config, - struct dsi_phy_per_lane_cfgs *timing); + struct dsi_phy_per_lane_cfgs *timing, + bool use_mode_bit_clk); /** * phy_timing_val() - Gets PHY timing values. @@ -273,12 +313,15 @@ struct dsi_phy_hw_ops { void *timing_ops; struct phy_ulps_config_ops ulps_ops; + struct phy_dyn_refresh_ops dyn_refresh_ops; }; /** * struct dsi_phy_hw - DSI phy hardware object specific to an instance * @base: VA for the DSI PHY base address. * @length: Length of the DSI PHY register base map. + * @dyn_pll_base: VA for the DSI dynamic refresh base address. + * @length: Length of the DSI dynamic refresh register base map. * @index: Instance ID of the controller. * @version: DSI PHY version. * @phy_clamp_base: Base address of phy clamp register map. @@ -288,6 +331,8 @@ struct dsi_phy_hw_ops { struct dsi_phy_hw { void __iomem *base; u32 length; + void __iomem *dyn_pll_base; + u32 dyn_refresh_len; u32 index; enum dsi_phy_version version; diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v3_0.c b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v3_0.c index 8f375d717bc2..556c01911a68 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v3_0.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v3_0.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2019, 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 @@ -66,6 +66,47 @@ #define DSIPHY_LNX_LPRX_CTRL(n) (0x228 + (0x80 * (n))) #define DSIPHY_LNX_TX_DCTRL(n) (0x22C + (0x80 * (n))) +/* dynamic refresh control registers */ +#define DSI_DYN_REFRESH_CTRL (0x000) +#define DSI_DYN_REFRESH_PIPE_DELAY (0x004) +#define DSI_DYN_REFRESH_PIPE_DELAY2 (0x008) +#define DSI_DYN_REFRESH_PLL_DELAY (0x00C) +#define DSI_DYN_REFRESH_STATUS (0x010) +#define DSI_DYN_REFRESH_PLL_CTRL0 (0x014) +#define DSI_DYN_REFRESH_PLL_CTRL1 (0x018) +#define DSI_DYN_REFRESH_PLL_CTRL2 (0x01C) +#define DSI_DYN_REFRESH_PLL_CTRL3 (0x020) +#define DSI_DYN_REFRESH_PLL_CTRL4 (0x024) +#define DSI_DYN_REFRESH_PLL_CTRL5 (0x028) +#define DSI_DYN_REFRESH_PLL_CTRL6 (0x02C) +#define DSI_DYN_REFRESH_PLL_CTRL7 (0x030) +#define DSI_DYN_REFRESH_PLL_CTRL8 (0x034) +#define DSI_DYN_REFRESH_PLL_CTRL9 (0x038) +#define DSI_DYN_REFRESH_PLL_CTRL10 (0x03C) +#define DSI_DYN_REFRESH_PLL_CTRL11 (0x040) +#define DSI_DYN_REFRESH_PLL_CTRL12 (0x044) +#define DSI_DYN_REFRESH_PLL_CTRL13 (0x048) +#define DSI_DYN_REFRESH_PLL_CTRL14 (0x04C) +#define DSI_DYN_REFRESH_PLL_CTRL15 (0x050) +#define DSI_DYN_REFRESH_PLL_CTRL16 (0x054) +#define DSI_DYN_REFRESH_PLL_CTRL17 (0x058) +#define DSI_DYN_REFRESH_PLL_CTRL18 (0x05C) +#define DSI_DYN_REFRESH_PLL_CTRL19 (0x060) +#define DSI_DYN_REFRESH_PLL_CTRL20 (0x064) +#define DSI_DYN_REFRESH_PLL_CTRL21 (0x068) +#define DSI_DYN_REFRESH_PLL_CTRL22 (0x06C) +#define DSI_DYN_REFRESH_PLL_CTRL23 (0x070) +#define DSI_DYN_REFRESH_PLL_CTRL24 (0x074) +#define DSI_DYN_REFRESH_PLL_CTRL25 (0x078) +#define DSI_DYN_REFRESH_PLL_CTRL26 (0x07C) +#define DSI_DYN_REFRESH_PLL_CTRL27 (0x080) +#define DSI_DYN_REFRESH_PLL_CTRL28 (0x084) +#define DSI_DYN_REFRESH_PLL_CTRL29 (0x088) +#define DSI_DYN_REFRESH_PLL_CTRL30 (0x08C) +#define DSI_DYN_REFRESH_PLL_CTRL31 (0x090) +#define DSI_DYN_REFRESH_PLL_UPPER_ADDR (0x094) +#define DSI_DYN_REFRESH_PLL_UPPER_ADDR2 (0x098) + /** * regulator_enable() - enable regulators for DSI PHY * @phy: Pointer to DSI PHY hardware object. @@ -470,3 +511,163 @@ int dsi_phy_hw_timing_val_v3_0(struct dsi_phy_per_lane_cfgs *timing_cfg, timing_cfg->lane_v3[i] = timing_val[i]; return 0; } + +void dsi_phy_hw_v3_0_dyn_refresh_config(struct dsi_phy_hw *phy, + struct dsi_phy_cfg *cfg, bool is_master) +{ + u32 reg; + + if (is_master) { + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL9, + DSIPHY_CMN_GLBL_CTRL, DSIPHY_CMN_VREG_CTRL, + 0x10, 0x59); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL10, + DSIPHY_CMN_TIMING_CTRL_0, DSIPHY_CMN_TIMING_CTRL_1, + cfg->timing.lane_v3[0], cfg->timing.lane_v3[1]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL11, + DSIPHY_CMN_TIMING_CTRL_2, DSIPHY_CMN_TIMING_CTRL_3, + cfg->timing.lane_v3[2], cfg->timing.lane_v3[3]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL12, + DSIPHY_CMN_TIMING_CTRL_4, DSIPHY_CMN_TIMING_CTRL_5, + cfg->timing.lane_v3[4], cfg->timing.lane_v3[5]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL13, + DSIPHY_CMN_TIMING_CTRL_6, DSIPHY_CMN_TIMING_CTRL_7, + cfg->timing.lane_v3[6], cfg->timing.lane_v3[7]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL14, + DSIPHY_CMN_TIMING_CTRL_8, DSIPHY_CMN_TIMING_CTRL_9, + cfg->timing.lane_v3[8], cfg->timing.lane_v3[9]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL15, + DSIPHY_CMN_TIMING_CTRL_10, DSIPHY_CMN_TIMING_CTRL_11, + cfg->timing.lane_v3[10], cfg->timing.lane_v3[11]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL16, + DSIPHY_CMN_CTRL_0, DSIPHY_CMN_LANE_CTRL0, + 0x7f, 0x1f); + } else { + reg = DSI_R32(phy, DSIPHY_CMN_CLK_CFG0); + reg &= ~BIT(5); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL0, + DSIPHY_CMN_CLK_CFG0, DSIPHY_CMN_PLL_CNTRL, + reg, 0x0); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL1, + DSIPHY_CMN_RBUF_CTRL, DSIPHY_CMN_GLBL_CTRL, + 0x0, 0x10); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL2, + DSIPHY_CMN_VREG_CTRL, DSIPHY_CMN_TIMING_CTRL_0, + 0x59, cfg->timing.lane_v3[0]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL3, + DSIPHY_CMN_TIMING_CTRL_1, DSIPHY_CMN_TIMING_CTRL_2, + cfg->timing.lane_v3[1], cfg->timing.lane_v3[2]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL4, + DSIPHY_CMN_TIMING_CTRL_3, DSIPHY_CMN_TIMING_CTRL_4, + cfg->timing.lane_v3[3], cfg->timing.lane_v3[4]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL5, + DSIPHY_CMN_TIMING_CTRL_5, DSIPHY_CMN_TIMING_CTRL_6, + cfg->timing.lane_v3[5], cfg->timing.lane_v3[6]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL6, + DSIPHY_CMN_TIMING_CTRL_7, DSIPHY_CMN_TIMING_CTRL_8, + cfg->timing.lane_v3[7], cfg->timing.lane_v3[8]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL7, + DSIPHY_CMN_TIMING_CTRL_9, DSIPHY_CMN_TIMING_CTRL_10, + cfg->timing.lane_v3[9], cfg->timing.lane_v3[10]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL8, + DSIPHY_CMN_TIMING_CTRL_11, DSIPHY_CMN_CTRL_0, + cfg->timing.lane_v3[11], 0x7f); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL9, + DSIPHY_CMN_LANE_CTRL0, DSIPHY_CMN_CTRL_2, + 0x1f, 0x40); + /* + * fill with dummy register writes since controller will blindly + * send these values to DSI PHY. + */ + reg = DSI_DYN_REFRESH_PLL_CTRL11; + while (reg <= DSI_DYN_REFRESH_PLL_CTRL29) { + DSI_DYN_REF_REG_W(phy->dyn_pll_base, reg, + DSIPHY_CMN_LANE_CTRL0, DSIPHY_CMN_CTRL_0, + 0x1f, 0x7f); + reg += 0x4; + } + + DSI_GEN_W32(phy->dyn_pll_base, + DSI_DYN_REFRESH_PLL_UPPER_ADDR, 0); + DSI_GEN_W32(phy->dyn_pll_base, + DSI_DYN_REFRESH_PLL_UPPER_ADDR2, 0); + } + + wmb(); /* make sure all registers are updated */ +} + +void dsi_phy_hw_v3_0_dyn_refresh_pipe_delay(struct dsi_phy_hw *phy, + struct dsi_dyn_clk_delay *delay) +{ + if (!delay) + return; + + DSI_GEN_W32(phy->dyn_pll_base, DSI_DYN_REFRESH_PIPE_DELAY, + delay->pipe_delay); + DSI_GEN_W32(phy->dyn_pll_base, DSI_DYN_REFRESH_PIPE_DELAY2, + delay->pipe_delay2); + DSI_GEN_W32(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_DELAY, + delay->pll_delay); +} + +void dsi_phy_hw_v3_0_dyn_refresh_helper(struct dsi_phy_hw *phy, u32 offset) +{ + u32 reg; + + /* + * if no offset is mentioned then this means we want to clear + * the dynamic refresh ctrl register which is the last step + * of dynamic refresh sequence. + */ + if (!offset) { + reg = DSI_GEN_R32(phy->dyn_pll_base, DSI_DYN_REFRESH_CTRL); + reg &= ~(BIT(0) | BIT(8)); + DSI_GEN_W32(phy->dyn_pll_base, DSI_DYN_REFRESH_CTRL, reg); + wmb(); /* ensure dynamic fps is cleared */ + return; + } + + if (offset & BIT(DYN_REFRESH_INTF_SEL)) { + reg = DSI_GEN_R32(phy->dyn_pll_base, DSI_DYN_REFRESH_CTRL); + reg |= BIT(13); + DSI_GEN_W32(phy->dyn_pll_base, DSI_DYN_REFRESH_CTRL, reg); + } + + if (offset & BIT(DYN_REFRESH_SYNC_MODE)) { + reg = DSI_GEN_R32(phy->dyn_pll_base, DSI_DYN_REFRESH_CTRL); + reg |= BIT(16); + DSI_GEN_W32(phy->dyn_pll_base, DSI_DYN_REFRESH_CTRL, reg); + } + + if (offset & BIT(DYN_REFRESH_SWI_CTRL)) { + reg = DSI_GEN_R32(phy->dyn_pll_base, DSI_DYN_REFRESH_CTRL); + reg |= BIT(0); + DSI_GEN_W32(phy->dyn_pll_base, DSI_DYN_REFRESH_CTRL, reg); + } + + if (offset & BIT(DYN_REFRESH_SW_TRIGGER)) { + reg = DSI_GEN_R32(phy->dyn_pll_base, DSI_DYN_REFRESH_CTRL); + reg |= BIT(8); + DSI_GEN_W32(phy->dyn_pll_base, DSI_DYN_REFRESH_CTRL, reg); + wmb(); /* ensure dynamic fps is triggered */ + } +} + +int dsi_phy_hw_v3_0_cache_phy_timings(struct dsi_phy_per_lane_cfgs *timings, + u32 *dst, u32 size) +{ + int i; + + if (!timings || !dst || !size) + return -EINVAL; + + if (size != DSI_PHY_TIMING_V3_SIZE) { + pr_err("size mis-match\n"); + return -EINVAL; + } + + for (i = 0; i < size; i++) + dst[i] = timings->lane_v3[i]; + + return 0; +} diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_timing_calc.c b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_timing_calc.c index bb8c4a1c4ff5..1a41fbee8c7d 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_timing_calc.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_timing_calc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2019, 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 @@ -638,11 +638,14 @@ error: * @mode: Mode information for which timing has to be calculated. * @config: DSI host configuration for this mode. * @timing: Timing parameters for each lane which will be returned. + * @use_mode_bit_clk: Boolean to indicate whether reacalculate dsi + * bit clk or use the existing bit clk(for dynamic clk case). */ int dsi_phy_hw_calculate_timing_params(struct dsi_phy_hw *phy, - struct dsi_mode_info *mode, - struct dsi_host_common_cfg *host, - struct dsi_phy_per_lane_cfgs *timing) + struct dsi_mode_info *mode, + struct dsi_host_common_cfg *host, + struct dsi_phy_per_lane_cfgs *timing, + bool use_mode_bit_clk) { /* constants */ u32 const esc_clk_mhz = 192; /* TODO: esc clock is hardcoded */ @@ -685,7 +688,10 @@ int dsi_phy_hw_calculate_timing_params(struct dsi_phy_hw *phy, num_of_lanes++; - x = mult_frac(v_total * h_total, inter_num, num_of_lanes); + if (use_mode_bit_clk) + x = mode->clk_rate_hz; + else + x = mult_frac(v_total * h_total, inter_num, num_of_lanes); y = rounddown(x, 1); clk_params.bitclk_mbps = rounddown(DIV_ROUND_UP_ULL(y, 1000000), 1); diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c index 0ee45712be11..6f9ed34ca23d 100644 --- a/drivers/gpu/drm/msm/msm_atomic.c +++ b/drivers/gpu/drm/msm/msm_atomic.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. * Copyright (C) 2014 Red Hat * Author: Rob Clark * @@ -124,7 +124,8 @@ static inline bool _msm_seamless_for_crtc(struct drm_atomic_state *state, int conn_cnt = 0; if (msm_is_mode_seamless(&crtc_state->mode) || - msm_is_mode_seamless_vrr(&crtc_state->adjusted_mode)) + msm_is_mode_seamless_vrr(&crtc_state->adjusted_mode) || + msm_is_mode_seamless_dyn_clk(&crtc_state->adjusted_mode)) return true; if (msm_is_mode_seamless_dms(&crtc_state->adjusted_mode) && !enable) @@ -168,6 +169,10 @@ static inline bool _msm_seamless_for_conn(struct drm_connector *connector, &connector->encoder->crtc->state->adjusted_mode)) return true; + if (msm_is_mode_seamless_dyn_clk( + &connector->encoder->crtc->state->adjusted_mode)) + return true; + if (msm_is_mode_seamless_dms( &connector->encoder->crtc->state->adjusted_mode)) return true; diff --git a/drivers/gpu/drm/msm/msm_kms.h b/drivers/gpu/drm/msm/msm_kms.h index 48bcffcdbbea..0330eb0023db 100644 --- a/drivers/gpu/drm/msm/msm_kms.h +++ b/drivers/gpu/drm/msm/msm_kms.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark * @@ -38,6 +38,8 @@ #define MSM_MODE_FLAG_SEAMLESS_DMS (1<<2) /* Request to switch the fps */ #define MSM_MODE_FLAG_SEAMLESS_VRR (1<<3) +/* Request to switch the bit clk */ +#define MSM_MODE_FLAG_SEAMLESS_DYN_CLK (1<<4) /* As there are different display controller blocks depending on the * snapdragon version, the kms support is split out and the appropriate @@ -211,6 +213,13 @@ static inline bool msm_is_mode_seamless_vrr(const struct drm_display_mode *mode) : false; } +static inline bool msm_is_mode_seamless_dyn_clk( + const struct drm_display_mode *mode) +{ + return mode ? (mode->private_flags & MSM_MODE_FLAG_SEAMLESS_DYN_CLK) + : false; +} + static inline bool msm_needs_vblank_pre_modeset( const struct drm_display_mode *mode) { diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c index a33dc35c65d4..10ff05853af0 100644 --- a/drivers/gpu/drm/msm/sde/sde_connector.c +++ b/drivers/gpu/drm/msm/sde/sde_connector.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2019, 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 @@ -1980,6 +1980,9 @@ static int sde_connector_populate_mode_info(struct drm_connector *conn, sde_kms_info_add_keystr(info, "mode_name", mode->name); + sde_kms_info_add_keyint(info, "bit_clk_rate", + mode_info.clk_rate); + topology_idx = (int)sde_rm_get_topology_name( mode_info.topology); if (topology_idx < SDE_RM_TOPOLOGY_MAX) { diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c index 7bd47aa80ef0..e75be14b3314 100644 --- a/drivers/gpu/drm/msm/sde/sde_crtc.c +++ b/drivers/gpu/drm/msm/sde/sde_crtc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2018 The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2019 The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark * @@ -927,8 +927,9 @@ static bool sde_crtc_mode_fixup(struct drm_crtc *crtc, SDE_DEBUG("\n"); if ((msm_is_mode_seamless(adjusted_mode) || - msm_is_mode_seamless_vrr(adjusted_mode)) && - (!crtc->enabled)) { + (msm_is_mode_seamless_vrr(adjusted_mode) || + msm_is_mode_seamless_dyn_clk(adjusted_mode))) && + (!crtc->enabled)) { SDE_ERROR("crtc state prevents seamless transition\n"); return false; } diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c index 6ef182805dcb..186f984cc68a 100644 --- a/drivers/gpu/drm/msm/sde/sde_encoder.c +++ b/drivers/gpu/drm/msm/sde/sde_encoder.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2019, The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark * @@ -3100,7 +3100,8 @@ static void sde_encoder_virt_enable(struct drm_encoder *drm_enc) } if (!(msm_is_mode_seamless_vrr(cur_mode) - || msm_is_mode_seamless_dms(cur_mode))) + || msm_is_mode_seamless_dms(cur_mode) + || msm_is_mode_seamless_dyn_clk(cur_mode))) kthread_init_delayed_work(&sde_enc->delayed_off_work, sde_encoder_off_work);