drm/msm/dsi-staging: add support for dsi dynamic clock switch

This change adds support for dynamic switching of dsi clocks
to avoid RF interference issues. Also with dynamic dsi clock
switch feature coming into picture, now populate the supported
refresh rate as list instead of providing a range. Modify the
logic to enumerate all the modes in dsi driver, taking dynamic
bit clocks, resolutions and refresh rates into account.

Change-Id: I5b6e62bc935cf2234bdd96fcb3c7537b4e735fff
Signed-off-by: Sandeep Panda <spanda@codeaurora.org>
Signed-off-by: Ritesh Kumar <riteshk@codeaurora.org>
tirimbino
Sandeep Panda 6 years ago committed by Ritesh Kumar
parent 8264f12b2a
commit 9dde4d5299
  1. 14
      Documentation/devicetree/bindings/drm/msm/mdss-dsi-panel.txt
  2. 54
      arch/arm64/boot/dts/qcom/sdmmagpie-sde-display.dtsi
  3. 40
      arch/arm64/boot/dts/qcom/sdmshrike-sde-display.dtsi
  4. 21
      arch/arm64/boot/dts/qcom/sm6150-sde-display.dtsi
  5. 59
      arch/arm64/boot/dts/qcom/sm8150-sde-display.dtsi
  6. 12
      drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c
  7. 16
      drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h
  8. 16
      drivers/gpu/drm/msm/dsi-staging/dsi_clk.h
  9. 51
      drivers/gpu/drm/msm/dsi-staging/dsi_clk_manager.c
  10. 30
      drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
  11. 9
      drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h
  12. 8
      drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h
  13. 31
      drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_cmn.c
  14. 39
      drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_reg.h
  15. 42
      drivers/gpu/drm/msm/dsi-staging/dsi_defs.h
  16. 658
      drivers/gpu/drm/msm/dsi-staging/dsi_display.c
  17. 7
      drivers/gpu/drm/msm/dsi-staging/dsi_display.h
  18. 67
      drivers/gpu/drm/msm/dsi-staging/dsi_drm.c
  19. 4
      drivers/gpu/drm/msm/dsi-staging/dsi_drm.h
  20. 12
      drivers/gpu/drm/msm/dsi-staging/dsi_hw.h
  21. 113
      drivers/gpu/drm/msm/dsi-staging/dsi_panel.c
  22. 13
      drivers/gpu/drm/msm/dsi-staging/dsi_panel.h
  23. 132
      drivers/gpu/drm/msm/dsi-staging/dsi_phy.c
  24. 43
      drivers/gpu/drm/msm/dsi-staging/dsi_phy.h
  25. 49
      drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw.h
  26. 203
      drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v3_0.c
  27. 10
      drivers/gpu/drm/msm/dsi-staging/dsi_phy_timing_calc.c
  28. 9
      drivers/gpu/drm/msm/msm_atomic.c
  29. 11
      drivers/gpu/drm/msm/msm_kms.h
  30. 5
      drivers/gpu/drm/msm/sde/sde_connector.c
  31. 5
      drivers/gpu/drm/msm/sde/sde_crtc.c
  32. 5
      drivers/gpu/drm/msm/sde/sde_encoder.c

@ -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>;
};
};

@ -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";

@ -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>;

@ -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";

@ -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;

@ -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;
}
/**

@ -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,6 +66,7 @@ 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.
*
@ -74,7 +75,8 @@ int dsi_phy_timing_calc_init(struct dsi_phy_hw *phy,
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_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_ */

@ -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_ */

@ -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
@ -115,6 +115,7 @@ 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.
* @index: Index of the DSI controller.
*
* return: error code in case of failure or 0 for success.
*/
@ -137,7 +138,7 @@ 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.
* @byte_clk: Byte clock rate in Hz.
* @index: Index of the DSI controller.
* return: error code in case of failure or 0 for success.
*/
@ -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;

@ -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
*/

@ -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_ */

@ -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);
};
/*

@ -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;
}

@ -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)

@ -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_ */

@ -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,8 +2954,33 @@ 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);
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)) {
src->byte_clk = dsi_clk;
@ -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);
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;
}
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);
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);
}
/* 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;
}
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);
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:

@ -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);

@ -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;
}

@ -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_ */

@ -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_ */

@ -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,11 +1244,11 @@ 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;
} else {
return rc;
}
type = utils->get_property(utils->data,
"qcom,mdss-dsi-pan-fps-update",
NULL);
"qcom,mdss-dsi-pan-fps-update", NULL);
if (!type) {
pr_err("[%s] dfps type not defined\n", name);
rc = -EINVAL;
@ -1225,43 +1267,41 @@ static int dsi_panel_parse_dfps_caps(struct dsi_panel *panel)
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);
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;
}
dfps_caps->min_refresh_rate = val;
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;
dfps_caps->dfps_list = kcalloc(dfps_caps->dfps_list_len, sizeof(u32),
GFP_KERNEL);
if (!dfps_caps->dfps_list) {
rc = -ENOMEM;
goto error;
}
}
dfps_caps->max_refresh_rate = val;
if (dfps_caps->min_refresh_rate > dfps_caps->max_refresh_rate) {
pr_err("[%s] min rate > max rate\n", name);
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;
}
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;
/* 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];
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;

@ -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;

@ -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);

@ -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_ */

@ -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;

@ -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;
}

@ -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_phy_per_lane_cfgs *timing,
bool use_mode_bit_clk)
{
/* constants */
u32 const esc_clk_mhz = 192; /* TODO: esc clock is hardcoded */
@ -685,6 +688,9 @@ int dsi_phy_hw_calculate_timing_params(struct dsi_phy_hw *phy,
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);

@ -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 <robdclark@gmail.com>
*
@ -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;

@ -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 <robdclark@gmail.com>
*
@ -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)
{

@ -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) {

@ -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 <robdclark@gmail.com>
*
@ -927,7 +927,8 @@ 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)) &&
(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;

@ -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 <robdclark@gmail.com>
*
@ -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);

Loading…
Cancel
Save