Skip to contentSkip to conversation

Add support for DisplayPort driver on SnapDragon.

PATCH In review Vara Reddy 5 participants 13 messages Mar 5, 2020 – Mar 27, 2020

Subsystems (7)
lore
Share & tools
lore

Vara Reddy

author
#0 lore raw

v4 cover Add support for DisplayPort driver on SnapDragon.

These patches add support for Display-Port driver on SnapDragon hardware. It adds DP driver and DP PLL driver files along with the needed device-tree bindings.

The block diagram of DP driver is shown below:

                 +-------------+
                 |DRM FRAMEWORK|
                 +------+------+
                        |
                   +----v----+
                   | DP DRM  |
                   +----+----+
                        |
                   +----v----+
     +------------+|   DP    +----------++------+
     +        +---+| DISPLAY |+---+      |      |
     |        +    +-+-----+-+    |      |      |
     |        |      |     |      |      |      |
     |        |      |     |      |      |      |
     |        |      |     |      |      |      |
     v        v      v     v      v      v      v
 +------+ +------+ +---+ +----+ +----+ +---+ +-----+
 |  DP  | |  DP  | |DP | | DP | | DP | |DP | | DP  |
 |PARSER| | HPD  | |AUX| |LINK| |CTRL| |PHY| |POWER|
 +--+---+ +---+--+ +---+ +----+ +--+-+ +-+-+ +-----+
    |                              |     |
 +--v---+                         +v-----v+
 |DEVICE|                         |  DP   |
 | TREE |                         |CATALOG|
 +------+                         +---+---+
                                      |
                                  +---v----+
                                  |CTRL/PHY|
                                  |   HW   |
                                  +--------+

These patches have dependency on clock driver changes mentioned below:
https://patchwork.kernel.org/patch/11245895/
https://patchwork.kernel.org/project/linux-clk/list/?series=153657

Chandan Uddaraju (4):
dt-bindings: msm/dp: add bindings of DP/DP-PLL driver for Snapdragon
drm: add constant N value in helper file
drm/msm/dp: add displayPort driver support
drm/msm/dp: add support for DP PLL driver

Jeykumar Sankaran (1):
drm/msm/dpu: add display port support in DPU

 .../devicetree/bindings/display/msm/dp.txt         |  252 +++
 .../devicetree/bindings/display/msm/dpu.txt        |   16 +-
 drivers/gpu/drm/i915/display/intel_display.c       |    2 +-
 drivers/gpu/drm/msm/Kconfig                        |   22 +
 drivers/gpu/drm/msm/Makefile                       |   17 +
 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c        |   28 +-
 .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c   |    8 +
 drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c            |   65 +-
 drivers/gpu/drm/msm/dp/dp_aux.c                    |  549 +++++++
 drivers/gpu/drm/msm/dp/dp_aux.h                    |   37 +
 drivers/gpu/drm/msm/dp/dp_catalog.c                | 1118 +++++++++++++
 drivers/gpu/drm/msm/dp/dp_catalog.h                |   87 +
 drivers/gpu/drm/msm/dp/dp_ctrl.c                   | 1727 ++++++++++++++++++++
 drivers/gpu/drm/msm/dp/dp_ctrl.h                   |   35 +
 drivers/gpu/drm/msm/dp/dp_display.c                | 1025 ++++++++++++
 drivers/gpu/drm/msm/dp/dp_display.h                |   31 +
 drivers/gpu/drm/msm/dp/dp_drm.c                    |  176 ++
 drivers/gpu/drm/msm/dp/dp_drm.h                    |   19 +
 drivers/gpu/drm/msm/dp/dp_hpd.c                    |   87 +
 drivers/gpu/drm/msm/dp/dp_hpd.h                    |   81 +
 drivers/gpu/drm/msm/dp/dp_link.c                   | 1246 ++++++++++++++
 drivers/gpu/drm/msm/dp/dp_link.h                   |  140 ++
 drivers/gpu/drm/msm/dp/dp_panel.c                  |  500 ++++++
 drivers/gpu/drm/msm/dp/dp_panel.h                  |   95 ++
 drivers/gpu/drm/msm/dp/dp_parser.c                 |  507 ++++++
 drivers/gpu/drm/msm/dp/dp_parser.h                 |  228 +++
 drivers/gpu/drm/msm/dp/dp_power.c                  |  563 +++++++
 drivers/gpu/drm/msm/dp/dp_power.h                  |   51 +
 drivers/gpu/drm/msm/dp/dp_reg.h                    |  489 ++++++
 drivers/gpu/drm/msm/dp/pll/dp_pll.c                |  136 ++
 drivers/gpu/drm/msm/dp/pll/dp_pll.h                |   57 +
 drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c           |  406 +++++
 drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.h           |   86 +
 drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c      |  524 ++++++
 drivers/gpu/drm/msm/msm_drv.c                      |    2 +
 drivers/gpu/drm/msm/msm_drv.h                      |   53 +-
 include/drm/drm_dp_helper.h                        |    2 +
 37 files changed, 10444 insertions(+), 23 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/display/msm/dp.txt
 create mode 100644 drivers/gpu/drm/msm/dp/dp_aux.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_aux.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_catalog.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_catalog.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_ctrl.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_ctrl.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_display.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_display.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_drm.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_drm.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_hpd.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_hpd.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_link.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_link.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_panel.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_panel.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_parser.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_parser.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_power.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_power.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_reg.h
 create mode 100644 drivers/gpu/drm/msm/dp/pll/dp_pll.c
 create mode 100644 drivers/gpu/drm/msm/dp/pll/dp_pll.h
 create mode 100644 drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c
 create mode 100644 drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.h
 create mode 100644 drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c
Signature · 2 lines
$(echo -e 'The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project')
author
#1 lore raw

PATCH v4 1/5 dt-bindings: msm/dp: add bindings of DP/DP-PLL driver for Snapdragon

From: Chandan Uddaraju <[email protected]>

Add bindings for Snapdragon DisplayPort and
display-port PLL driver.

Changes in V2:
Provide details about sel-gpio

Changes in V4:
Provide details about max dp lanes
Change the commit text

---
 .../devicetree/bindings/display/msm/dp.txt         | 252 +++++++++++++++++++++
 .../devicetree/bindings/display/msm/dpu.txt        |  16 +-
 2 files changed, 264 insertions(+), 4 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/display/msm/dp.txt
Documentation/devicetree/bindings/display/msm/dp.txt new file +252 -0
new file mode 100644
index 0000000..1a4e17f
@@ -0,0 +1,252 @@
+Qualcomm Technologies, Inc.
+DP is the master Display Port device which supports DP host controllers that are compatible with VESA Display Port interface specification.
+DP Controller: Required properties:
+- compatible:           Should be "qcom,dp-display".
+- reg:                  Base address and length of DP hardware's memory mapped regions.
+- cell-index:           Specifies the controller instance.
+- reg-names:            A list of strings that name the list of regs.
+			"dp_ahb" - DP controller memory region.
+			"dp_aux" - DP AUX memory region.
+			"dp_link" - DP link layer memory region.
+			"dp_p0" - DP pixel clock domain memory region.
+			"dp_phy" - DP PHY memory region.
+			"dp_ln_tx0" - USB3 DP PHY combo TX-0 lane memory region.
+			"dp_ln_tx1" - USB3 DP PHY combo TX-1 lane memory region.
+			"dp_mmss_cc" - Display Clock Control memory region.
+			"qfprom_physical" - QFPROM Phys memory region.
+			"dp_pll" - USB3 DP combo PLL memory region.
+			"usb3_dp_com" - USB3 DP PHY combo memory region.
+			"hdcp_physical" - DP HDCP memory region.
+- interrupt-parent	phandle to the interrupt parent device node.
+- interrupts:		The interrupt signal from the DP block.
+- clocks:               Clocks required for Display Port operation. See [1] for details on clock bindings.
+- clock-names:          Names of the clocks corresponding to handles. Following clocks are required:
+			"core_aux_clk", "core_usb_ref_clk_src","core_usb_ref_clk", "core_usb_cfg_ahb_clk",
+			"core_usb_pipe_clk", "ctrl_link_clk", "ctrl_link_iface_clk", "ctrl_crypto_clk",
+			"ctrl_pixel_clk", "pixel_clk_rcg", "pixel_parent".
+- pll-node:		phandle to DP PLL node.
+- vdda-1p2-supply:		phandle to vdda 1.2V regulator node.
+- vdda-0p9-supply:		phandle to vdda 0.9V regulator node.
+- qcom,aux-cfg0-settings:		Specifies the DP AUX configuration 0 settings. The first
+					entry in this array corresponds to the register offset
+					within DP AUX, while the remaining entries indicate the
+					programmable values.
+- qcom,aux-cfg1-settings:		Specifies the DP AUX configuration 1 settings. The first
+					entry in this array corresponds to the register offset
+					within DP AUX, while the remaining entries indicate the
+					programmable values.
+- qcom,aux-cfg2-settings:		Specifies the DP AUX configuration 2 settings. The first
+					entry in this array corresponds to the register offset
+					within DP AUX, while the remaining entries indicate the
+					programmable values.
+- qcom,aux-cfg3-settings:		Specifies the DP AUX configuration 3 settings. The first
+					entry in this array corresponds to the register offset
+					within DP AUX, while the remaining entries indicate the
+					programmable values.
+- qcom,aux-cfg4-settings:		Specifies the DP AUX configuration 4 settings. The first
+					entry in this array corresponds to the register offset
+					within DP AUX, while the remaining entries indicate the
+					programmable values.
+- qcom,aux-cfg5-settings:		Specifies the DP AUX configuration 5 settings. The first
+					entry in this array corresponds to the register offset
+					within DP AUX, while the remaining entries indicate the
+					programmable values.
+- qcom,aux-cfg6-settings:		Specifies the DP AUX configuration 6 settings. The first
+					entry in this array corresponds to the register offset
+					within DP AUX, while the remaining entries indicate the
+					programmable values.
+- qcom,aux-cfg7-settings:		Specifies the DP AUX configuration 7 settings. The first
+					entry in this array corresponds to the register offset
+					within DP AUX, while the remaining entries indicate the
+					programmable values.
+- qcom,aux-cfg8-settings:		Specifies the DP AUX configuration 8 settings. The first
+					entry in this array corresponds to the register offset
+					within DP AUX, while the remaining entries indicate the
+					programmable values.
+- qcom,aux-cfg9-settings:		Specifies the DP AUX configuration 9 settings. The first
+					entry in this array corresponds to the register offset
+					within DP AUX, while the remaining entries indicate the
+					programmable values.
+- qcom,max-pclk-frequency-khz:		An integer specifying the maximum. pixel clock in KHz supported by Display Port.
+- qcom,max-lanes-for-dp:		Maximum number of lanes that can be used for Display port.
+- qcom,<type>-supply-entries:		A node that lists the elements of the supply used by the a particular "type" of DP module. The module "types"
+					can be "core", "ctrl", and "phy". Within the same type,
+					there can be more than one instance of this binding,
+					in which case the entry would be appended with the
+					supply entry index.
+					e.g. qcom,ctrl-supply-entry@0
+					-- qcom,supply-name: name of the supply (vdd/vdda/vddio)
+					-- qcom,supply-min-voltage: minimum voltage level (uV)
+					-- qcom,supply-max-voltage: maximum voltage level (uV)
+					-- qcom,supply-enable-load: load drawn (uA) from enabled supply
+					-- qcom,supply-disable-load: load drawn (uA) from disabled supply
+					-- qcom,supply-pre-on-sleep: time to sleep (ms) before turning on
+					-- qcom,supply-post-on-sleep: time to sleep (ms) after turning on
+					-- qcom,supply-pre-off-sleep: time to sleep (ms) before turning off
+					-- qcom,supply-post-off-sleep: time to sleep (ms) after turning off
+- pinctrl-names:	List of names to assign mdss pin states defined in pinctrl device node
+					Refer to pinctrl-bindings.txt
+- pinctrl-<0..n>:	Lists phandles each pointing to the pin configuration node within a pin
+					controller. These pin configurations are installed in the pinctrl
+					device node. Refer to pinctrl-bindings.txt
+DP Endpoint properties:
+  - remote-endpoint: For port@0, set to phandle of the connected panel/bridge's
+    input endpoint. For port@1, set to the DPU interface output. See [2] for
+    device graph info.
+
+Optional properties:
+- qcom,aux-en-gpio:		Specifies the aux-channel enable gpio.
+- qcom,aux-sel-gpio:		Specifies the mux-selection that might be needed for aux interface.
+
+
+DP PLL: Required properties:
+- compatible:           Should be "qcom,dp-pll-10nm".
+- reg:                  Base address and length of DP hardware's memory mapped regions.
+- cell-index:           Specifies the PLL instance.
+- reg-names:            A list of strings that name the list of regs.
+			"pll_base" - DP PLL memory region.
+			"phy_base" - DP PHY memory region.
+			"ln_tx0_base" - USB3 DP PHY combo TX-0 lane memory region.
+			"ln_tx1_base" - USB3 DP PHY combo TX-1 lane memory region.
+			"gdsc_base" - gdsc memory region.
+- interrupt-parent	phandle to the interrupt parent device node.
+- interrupts:		The interrupt signal from the DP block.
+- clocks:               Clocks required for Display Port operation. See [1] for details on clock bindings.
+- clock-names:          Names of the clocks corresponding to handles. Following clocks are required:
+			"iface_clk", "ref_clk", cfg_ahb_clk", "pipe_clk".
+- clock-rate:           Initial clock rate to be configured. For the shared clocks, the default value			     is set to zero so that minimum clock value is configured. Non-zero clock
+			value can be used to configure DP pixel clock.
+
+
+[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
+[2] Documentation/devicetree/bindings/graph.txt
+
+Example:
+	msm_dp: dp_display@ae90000{
+		cell-index = <0>;
+		compatible = "qcom,dp-display";
+
+		reg =   <0 0x90000 0x0dc>,
+			<0 0x90200 0x0c0>,
+			<0 0x90400 0x508>,
+			<0 0x90a00 0x094>,
+			<1 0xeaa00 0x200>,
+			<1 0xea200 0x200>,
+			<1 0xea600 0x200>,
+			<2 0x02000 0x1a0>,
+			<3 0x00000 0x621c>,
+			<1 0xea000 0x180>,
+			<1 0xe8000 0x20>,
+			<4 0xe1000 0x034>;
+		reg-names = "dp_ahb", "dp_aux", "dp_link",
+			"dp_p0", "dp_phy", "dp_ln_tx0", "dp_ln_tx1",
+			"dp_mmss_cc", "qfprom_physical", "dp_pll",
+			"usb3_dp_com", "hdcp_physical";
+
+		interrupt-parent = <&display_subsystem>;
+		interrupts = <12 0>;
+
+		extcon = <&usb_1_ssphy>;
+		clocks =  <&dispcc DISP_CC_MDSS_DP_AUX_CLK>,
+			<&rpmhcc RPMH_CXO_CLK>,
+			<&gcc GCC_USB3_PRIM_CLKREF_CLK>,
+			<&gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>,
+			<&gcc GCC_USB3_PRIM_PHY_PIPE_CLK>,
+			<&dispcc DISP_CC_MDSS_DP_LINK_CLK>,
+			<&dispcc DISP_CC_MDSS_DP_LINK_INTF_CLK>,
+			<&dispcc DISP_CC_MDSS_DP_PIXEL_CLK>,
+			<&dispcc DISP_CC_MDSS_DP_CRYPTO_CLK>,
+			<&dispcc DISP_CC_MDSS_DP_PIXEL_CLK_SRC>;
+		clock-names = "core_aux_clk", "core_ref_clk_src",
+			"core_usb_ref_clk", "core_usb_cfg_ahb_clk",
+			"core_usb_pipe_clk", "ctrl_link_clk",
+			"ctrl_link_iface_clk", "ctrl_pixel_clk",
+			"crypto_clk", "pixel_clk_rcg";
+
+		pll-node = <&dp_pll>;
+		vdda-1p2-supply = <&vreg_l3c_1p2>;
+		vdda-0p9-supply = <&vreg_l4a_0p8>;
+		qcom,aux-cfg0-settings = [20 00];
+		qcom,aux-cfg1-settings = [24 13 23 1d];
+		qcom,aux-cfg2-settings = [28 24];
+		qcom,aux-cfg3-settings = [2c 00];
+		qcom,aux-cfg4-settings = [30 0a];
+		qcom,aux-cfg5-settings = [34 26];
+		qcom,aux-cfg6-settings = [38 0a];
+		qcom,aux-cfg7-settings = [3c 03];
+		qcom,aux-cfg8-settings = [40 bb];
+		qcom,aux-cfg9-settings = [44 03];
+
+		qcom,max-pclk-frequency-khz = <675000>;
+		qcom,max-lanes-for-dp = <2>;
+
+		qcom,ctrl-supply-entries {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			qcom,ctrl-supply-entry@0 {
+				reg = <0>;
+				qcom,supply-name = "vdda-1p2";
+				qcom,supply-min-voltage = <1200000>;
+				qcom,supply-max-voltage = <1200000>;
+				qcom,supply-enable-load = <21800>;
+				qcom,supply-disable-load = <4>;
+			};
+		};
+
+		qcom,phy-supply-entries {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			qcom,phy-supply-entry@0 {
+				reg = <0>;
+				qcom,supply-name = "vdda-0p9";
+				qcom,supply-min-voltage = <880000>;
+				qcom,supply-max-voltage = <880000>;
+				qcom,supply-enable-load = <36000>;
+				qcom,supply-disable-load = <32>;
+			};
+		};
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@0 {
+				reg = <0>;
+				dp_in: endpoint {
+					remote-endpoint = <&dpu_intf0_out>;
+				};
+			};
+
+			port@1 {
+				reg = <1>;
+				dp_out: endpoint {
+				};
+			};
+		};
+	};
+
+	dp_pll: dp-pll@c011000 {
+		compatible = "qcom,dp-pll-10nm";
+		label = "DP PLL";
+		cell-index = <0>;
+		#clock-cells = <1>;
+
+		reg = <1 0xea000 0x200>,
+		      <1 0xeaa00 0x200>,
+		      <1 0xea200 0x200>,
+		      <1 0xea600 0x200>,
+		      <2 0x03000 0x8>;
+		reg-names = "pll_base", "phy_base", "ln_tx0_base",
+			"ln_tx1_base", "gdsc_base";
+
+		clocks = <&dispcc DISP_CC_MDSS_AHB_CLK>,
+			 <&gcc GCC_USB3_PRIM_CLKREF_CLK>,
+			 <&gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>,
+			 <&gcc GCC_USB3_PRIM_PHY_PIPE_CLK>;
+		clock-names = "iface_clk", "ref_clk",
+			"cfg_ahb_clk", "pipe_clk";
+		clock-rate = <0>;
+
+	};
Documentation/devicetree/bindings/display/msm/dpu.txt +12 -4
index 551ae26..7e99e45 100644
@@ -63,8 +63,9 @@ Required properties:
 	Documentation/devicetree/bindings/graph.txt
 	Documentation/devicetree/bindings/media/video-interfaces.txt
 
-	Port 0 -> DPU_INTF1 (DSI1)
-	Port 1 -> DPU_INTF2 (DSI2)
+	Port 0 -> DPU_INTF0 (DP)
+	Port 1 -> DPU_INTF1 (DSI1)
+	Port 2 -> DPU_INTF2 (DSI2)
 
 Optional properties:
 - assigned-clocks: list of clock specifiers for clocks needing rate assignment
@@ -125,13 +126,20 @@ Example:
 
 				port@0 {
 					reg = <0>;
-					dpu_intf1_out: endpoint {
-						remote-endpoint = <&dsi0_in>;
+					dpu_intf0_out: endpoint {
+						remote-endpoint = <&dp_in>;
 					};
 				};
 
 				port@1 {
 					reg = <1>;
+					dpu_intf1_out: endpoint {
+						remote-endpoint = <&dsi0_in>;
+					};
+				};
+
+				port@2 {
+					reg = <2>;
 					dpu_intf2_out: endpoint {
 						remote-endpoint = <&dsi1_in>;
 					};
Signature · 2 lines
$(echo -e 'The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project')
maintainer
#2 lore raw

Re: [DPU PATCH v4 1/5] dt-bindings: msm/dp: add bindings of DP/DP-PLL driver for Snapdragon

Quoted 12 lines · Vara Reddy

From: Chandan Uddaraju <[email protected]>

Add bindings for Snapdragon DisplayPort and
display-port PLL driver.

Changes in V2:
Provide details about sel-gpio

Changes in V4:
Provide details about max dp lanes
Change the commit text

Quoted 7 lines
---
 .../devicetree/bindings/display/msm/dp.txt         | 252 +++++++++++++++++++++
 .../devicetree/bindings/display/msm/dpu.txt        |  16 +-
 2 files changed, 264 insertions(+), 4 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/display/msm/dp.txt

Sorry for the moving target, but bindings have moved to DT schema format now.

Rob
_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

#3 lore raw

Re: [DPU PATCH v4 1/5] dt-bindings: msm/dp: add bindings of DP/DP-PLL driver for Snapdragon

In reply to Vara Reddy

Quoting Vara Reddy (2020-03-04 16:10:24)

Quoted 12 lines

From: Chandan Uddaraju <[email protected]>

Add bindings for Snapdragon DisplayPort and
display-port PLL driver.

Changes in V2:
Provide details about sel-gpio

Changes in V4:
Provide details about max dp lanes
Change the commit text

Quoted 7 lines
---
 .../devicetree/bindings/display/msm/dp.txt         | 252 +++++++++++++++++++++
 .../devicetree/bindings/display/msm/dpu.txt        |  16 +-
 2 files changed, 264 insertions(+), 4 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/display/msm/dp.txt

Having this in YAML will certainly make it easier to review. Please do that as Rob H has asked.

Quoted 16 lines
Documentation/devicetree/bindings/display/msm/dp.txt new file +9 -0
new file mode 100644
index 0000000..1a4e17f
@@ -0,0 +1,252 @@
+Qualcomm Technologies, Inc.
+DP is the master Display Port device which supports DP host controllers that are compatible with VESA Display Port interface specification.
+DP Controller: Required properties:
+- compatible:           Should be "qcom,dp-display".
+- reg:                  Base address and length of DP hardware's memory mapped regions.
+- cell-index:           Specifies the controller instance.
+- reg-names:            A list of strings that name the list of regs.
+                       "dp_ahb" - DP controller memory region.
+                       "dp_aux" - DP AUX memory region.

Documentation/devicetree/bindings/display/msm/dp.txt line 19

Quoted 10 lines
Documentation/devicetree/bindings/display/msm/dp.txt new file +10 -0
+                       "dp_link" - DP link layer memory region.
+                       "dp_p0" - DP pixel clock domain memory region.
+                       "dp_phy" - DP PHY memory region.
+                       "dp_ln_tx0" - USB3 DP PHY combo TX-0 lane memory region.
+                       "dp_ln_tx1" - USB3 DP PHY combo TX-1 lane memory region.
+                       "dp_mmss_cc" - Display Clock Control memory region.
+                       "qfprom_physical" - QFPROM Phys memory region.
+                       "dp_pll" - USB3 DP combo PLL memory region.
+                       "usb3_dp_com" - USB3 DP PHY combo memory region.
+                       "hdcp_physical" - DP HDCP memory region.

A handful of these register properties overlap with the USB PHY region. I suspect the existing qcom,sc7180-qmp-usb3-phy USB PHY binding that we have is wrong. It should describe both the USB part and the DP part of the combo PHY. Probably the DP part can be a subnode like how the superspeed PHY is a child node of the wrapper node. Then we'll have to figure out how to coordinate the access to usb3_dp_com (which I presume corresponds to the dp_com region in the usb3-phy binding) so that the USB and DP PHY drivers can figure out how to configure the "type-c" pins on the SoC to be 2 lanes DP and 2 lanes USB or 4 lanes USB or 4 lanes DP.

Documentation/devicetree/bindings/display/msm/dp.txt line 26

Quoted 7 lines
Documentation/devicetree/bindings/display/msm/dp.txt new file +7 -0
+- interrupt-parent     phandle to the interrupt parent device node.
+- interrupts:          The interrupt signal from the DP block.
+- clocks:               Clocks required for Display Port operation. See [1] for details on clock bindings.
+- clock-names:          Names of the clocks corresponding to handles. Following clocks are required:
+                       "core_aux_clk", "core_usb_ref_clk_src","core_usb_ref_clk", "core_usb_cfg_ahb_clk",
+                       "core_usb_pipe_clk", "ctrl_link_clk", "ctrl_link_iface_clk", "ctrl_crypto_clk",
+                       "ctrl_pixel_clk", "pixel_clk_rcg", "pixel_parent".

Please remove _clk suffix on all clock names. It's redundant.

Quoted 33 lines
Documentation/devicetree/bindings/display/msm/dp.txt new file +33 -0
+- pll-node:            phandle to DP PLL node.
+- vdda-1p2-supply:             phandle to vdda 1.2V regulator node.
+- vdda-0p9-supply:             phandle to vdda 0.9V regulator node.
+- qcom,aux-cfg0-settings:              Specifies the DP AUX configuration 0 settings. The first
+                                       entry in this array corresponds to the register offset
+                                       within DP AUX, while the remaining entries indicate the
+                                       programmable values.
+- qcom,aux-cfg1-settings:              Specifies the DP AUX configuration 1 settings. The first
+                                       entry in this array corresponds to the register offset
+                                       within DP AUX, while the remaining entries indicate the
+                                       programmable values.
+- qcom,aux-cfg2-settings:              Specifies the DP AUX configuration 2 settings. The first
+                                       entry in this array corresponds to the register offset
+                                       within DP AUX, while the remaining entries indicate the
+                                       programmable values.
+- qcom,aux-cfg3-settings:              Specifies the DP AUX configuration 3 settings. The first
+                                       entry in this array corresponds to the register offset
+                                       within DP AUX, while the remaining entries indicate the
+                                       programmable values.
+- qcom,aux-cfg4-settings:              Specifies the DP AUX configuration 4 settings. The first
+                                       entry in this array corresponds to the register offset
+                                       within DP AUX, while the remaining entries indicate the
+                                       programmable values.
+- qcom,aux-cfg5-settings:              Specifies the DP AUX configuration 5 settings. The first
+                                       entry in this array corresponds to the register offset
+                                       within DP AUX, while the remaining entries indicate the
+                                       programmable values.
+- qcom,aux-cfg6-settings:              Specifies the DP AUX configuration 6 settings. The first
+                                       entry in this array corresponds to the register offset
+                                       within DP AUX, while the remaining entries indicate the
+                                       programmable values.
+- qcom,aux-cfg7-settings:              Specifies the DP AUX configuration 7 settings. The first
+                                       entry in this array corresponds to the register offset

Documentation/devicetree/bindings/display/msm/dp.txt line 69

Quoted 10 lines
Documentation/devicetree/bindings/display/msm/dp.txt new file +10 -0
+                                       within DP AUX, while the remaining entries indicate the
+                                       programmable values.
+- qcom,aux-cfg8-settings:              Specifies the DP AUX configuration 8 settings. The first
+                                       entry in this array corresponds to the register offset
+                                       within DP AUX, while the remaining entries indicate the
+                                       programmable values.
+- qcom,aux-cfg9-settings:              Specifies the DP AUX configuration 9 settings. The first
+                                       entry in this array corresponds to the register offset
+                                       within DP AUX, while the remaining entries indicate the
+                                       programmable values.

It would be better if these properties were understandable by us mere kernel developers out there. Is it possible to make them into standalone properties that control certain configuration tunings? Are any of these not variable per-board and can be hardcoded based on the SoC into the driver?

Documentation/devicetree/bindings/display/msm/dp.txt line 70

Quoted 1 line
Documentation/devicetree/bindings/display/msm/dp.txt new file +1 -0
+- qcom,max-pclk-frequency-khz:         An integer specifying the maximum. pixel clock in KHz supported by Display Port.

This can't be detected at runtime?

Documentation/devicetree/bindings/display/msm/dp.txt line 71

Quoted 1 line
Documentation/devicetree/bindings/display/msm/dp.txt new file +1 -0
+- qcom,max-lanes-for-dp:               Maximum number of lanes that can be used for Display port.

This can come from an of graph walk to figure out how many possible lanes are connected? Or some more standard property that describes logical to physical lane mapping, like data-lanes?

Quoted 5 lines
Documentation/devicetree/bindings/display/msm/dp.txt new file +5 -0
+- qcom,<type>-supply-entries:          A node that lists the elements of the supply used by the a particular "type" of DP module. The module "types"
+                                       can be "core", "ctrl", and "phy". Within the same type,
+                                       there can be more than one instance of this binding,
+                                       in which case the entry would be appended with the
+                                       supply entry index.

Documentation/devicetree/bindings/display/msm/dp.txt line 86

Quoted 10 lines
Documentation/devicetree/bindings/display/msm/dp.txt new file +10 -0
+                                       e.g. qcom,ctrl-supply-entry@0
+                                       -- qcom,supply-name: name of the supply (vdd/vdda/vddio)
+                                       -- qcom,supply-min-voltage: minimum voltage level (uV)
+                                       -- qcom,supply-max-voltage: maximum voltage level (uV)
+                                       -- qcom,supply-enable-load: load drawn (uA) from enabled supply
+                                       -- qcom,supply-disable-load: load drawn (uA) from disabled supply
+                                       -- qcom,supply-pre-on-sleep: time to sleep (ms) before turning on
+                                       -- qcom,supply-post-on-sleep: time to sleep (ms) after turning on
+                                       -- qcom,supply-pre-off-sleep: time to sleep (ms) before turning off
+                                       -- qcom,supply-post-off-sleep: time to sleep (ms) after turning off

All these regulator properties look like things that should be handled by board constraints on certain supplies, not by this binding or the driver that matches this binding.

Documentation/devicetree/bindings/display/msm/dp.txt line 91

Quoted 5 lines
Documentation/devicetree/bindings/display/msm/dp.txt new file +5 -0
+- pinctrl-names:       List of names to assign mdss pin states defined in pinctrl device node
+                                       Refer to pinctrl-bindings.txt
+- pinctrl-<0..n>:      Lists phandles each pointing to the pin configuration node within a pin
+                                       controller. These pin configurations are installed in the pinctrl
+                                       device node. Refer to pinctrl-bindings.txt

These two properties aren't needed.

Documentation/devicetree/bindings/display/msm/dp.txt line 99

Quoted 8 lines
Documentation/devicetree/bindings/display/msm/dp.txt new file +8 -0
+DP Endpoint properties:
+  - remote-endpoint: For port@0, set to phandle of the connected panel/bridge's
+    input endpoint. For port@1, set to the DPU interface output. See [2] for
+    device graph info.
+
+Optional properties:
+- qcom,aux-en-gpio:            Specifies the aux-channel enable gpio.
+- qcom,aux-sel-gpio:           Specifies the mux-selection that might be needed for aux interface.

Please use the normal '<your name here>-gpios' property for gpios.

Documentation/devicetree/bindings/display/msm/dp.txt line 110

Quoted 11 lines
Documentation/devicetree/bindings/display/msm/dp.txt new file +11 -0
+
+
+DP PLL: Required properties:
+- compatible:           Should be "qcom,dp-pll-10nm".
+- reg:                  Base address and length of DP hardware's memory mapped regions.
+- cell-index:           Specifies the PLL instance.
+- reg-names:            A list of strings that name the list of regs.
+                       "pll_base" - DP PLL memory region.
+                       "phy_base" - DP PHY memory region.
+                       "ln_tx0_base" - USB3 DP PHY combo TX-0 lane memory region.
+                       "ln_tx1_base" - USB3 DP PHY combo TX-1 lane memory region.

This was in the other node binding. Probably the only reg property required here is pll_base?

Documentation/devicetree/bindings/display/msm/dp.txt line 117

Quoted 7 lines
Documentation/devicetree/bindings/display/msm/dp.txt new file +7 -0
+                       "gdsc_base" - gdsc memory region.
+- interrupt-parent     phandle to the interrupt parent device node.
+- interrupts:          The interrupt signal from the DP block.
+- clocks:               Clocks required for Display Port operation. See [1] for details on clock bindings.
+- clock-names:          Names of the clocks corresponding to handles. Following clocks are required:
+                       "iface_clk", "ref_clk", cfg_ahb_clk", "pipe_clk".
+- clock-rate:           Initial clock rate to be configured. For the shared clocks, the default value                       is set to zero so that minimum clock value is configured. Non-zero clock

We have assigned-clock-rates for this.

Documentation/devicetree/bindings/display/msm/dp.txt line 125

Quoted 8 lines
Documentation/devicetree/bindings/display/msm/dp.txt new file +8 -0
+                       value can be used to configure DP pixel clock.
+
+
+[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
+[2] Documentation/devicetree/bindings/graph.txt
+
+Example:
+       msm_dp: dp_display@ae90000{

No underscores in node names. It should also be somewhat generic like 'display-controller' or 'displayport-controller' and the first reg property should match the unit address after the at sign (@).

Quoted 14 lines
Documentation/devicetree/bindings/display/msm/dp.txt new file +14 -0
+               cell-index = <0>;
+               compatible = "qcom,dp-display";
+
+               reg =   <0 0x90000 0x0dc>,
+                       <0 0x90200 0x0c0>,
+                       <0 0x90400 0x508>,
+                       <0 0x90a00 0x094>,
+                       <1 0xeaa00 0x200>,
+                       <1 0xea200 0x200>,
+                       <1 0xea600 0x200>,
+                       <2 0x02000 0x1a0>,
+                       <3 0x00000 0x621c>,
+                       <1 0xea000 0x180>,
+                       <1 0xe8000 0x20>,

Documentation/devicetree/bindings/display/msm/dp.txt line 149

Quoted 10 lines
Documentation/devicetree/bindings/display/msm/dp.txt new file +10 -0
+                       <4 0xe1000 0x034>;
+               reg-names = "dp_ahb", "dp_aux", "dp_link",
+                       "dp_p0", "dp_phy", "dp_ln_tx0", "dp_ln_tx1",
+                       "dp_mmss_cc", "qfprom_physical", "dp_pll",
+                       "usb3_dp_com", "hdcp_physical";
+
+               interrupt-parent = <&display_subsystem>;
+               interrupts = <12 0>;
+
+               extcon = <&usb_1_ssphy>;

Please no extcon

Quoted 92 lines
Documentation/devicetree/bindings/display/msm/dp.txt new file +92 -0
+               clocks =  <&dispcc DISP_CC_MDSS_DP_AUX_CLK>,
+                       <&rpmhcc RPMH_CXO_CLK>,
+                       <&gcc GCC_USB3_PRIM_CLKREF_CLK>,
+                       <&gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>,
+                       <&gcc GCC_USB3_PRIM_PHY_PIPE_CLK>,
+                       <&dispcc DISP_CC_MDSS_DP_LINK_CLK>,
+                       <&dispcc DISP_CC_MDSS_DP_LINK_INTF_CLK>,
+                       <&dispcc DISP_CC_MDSS_DP_PIXEL_CLK>,
+                       <&dispcc DISP_CC_MDSS_DP_CRYPTO_CLK>,
+                       <&dispcc DISP_CC_MDSS_DP_PIXEL_CLK_SRC>;
+               clock-names = "core_aux_clk", "core_ref_clk_src",
+                       "core_usb_ref_clk", "core_usb_cfg_ahb_clk",
+                       "core_usb_pipe_clk", "ctrl_link_clk",
+                       "ctrl_link_iface_clk", "ctrl_pixel_clk",
+                       "crypto_clk", "pixel_clk_rcg";
+
+               pll-node = <&dp_pll>;
+               vdda-1p2-supply = <&vreg_l3c_1p2>;
+               vdda-0p9-supply = <&vreg_l4a_0p8>;
+               qcom,aux-cfg0-settings = [20 00];
+               qcom,aux-cfg1-settings = [24 13 23 1d];
+               qcom,aux-cfg2-settings = [28 24];
+               qcom,aux-cfg3-settings = [2c 00];
+               qcom,aux-cfg4-settings = [30 0a];
+               qcom,aux-cfg5-settings = [34 26];
+               qcom,aux-cfg6-settings = [38 0a];
+               qcom,aux-cfg7-settings = [3c 03];
+               qcom,aux-cfg8-settings = [40 bb];
+               qcom,aux-cfg9-settings = [44 03];
+
+               qcom,max-pclk-frequency-khz = <675000>;
+               qcom,max-lanes-for-dp = <2>;
+
+               qcom,ctrl-supply-entries {
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+
+                       qcom,ctrl-supply-entry@0 {
+                               reg = <0>;
+                               qcom,supply-name = "vdda-1p2";
+                               qcom,supply-min-voltage = <1200000>;
+                               qcom,supply-max-voltage = <1200000>;
+                               qcom,supply-enable-load = <21800>;
+                               qcom,supply-disable-load = <4>;
+                       };
+               };
+
+               qcom,phy-supply-entries {
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+
+                       qcom,phy-supply-entry@0 {
+                               reg = <0>;
+                               qcom,supply-name = "vdda-0p9";
+                               qcom,supply-min-voltage = <880000>;
+                               qcom,supply-max-voltage = <880000>;
+                               qcom,supply-enable-load = <36000>;
+                               qcom,supply-disable-load = <32>;
+                       };
+               };
+
+               ports {
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+
+                       port@0 {
+                               reg = <0>;
+                               dp_in: endpoint {
+                                       remote-endpoint = <&dpu_intf0_out>;
+                               };
+                       };
+
+                       port@1 {
+                               reg = <1>;
+                               dp_out: endpoint {
+                               };
+                       };
+               };
+       };
+
+       dp_pll: dp-pll@c011000 {
+               compatible = "qcom,dp-pll-10nm";
+               label = "DP PLL";
+               cell-index = <0>;
+               #clock-cells = <1>;
+
+               reg = <1 0xea000 0x200>,
+                     <1 0xeaa00 0x200>,
+                     <1 0xea200 0x200>,
+                     <1 0xea600 0x200>,
+                     <2 0x03000 0x8>;
+               reg-names = "pll_base", "phy_base", "ln_tx0_base",

Documentation/devicetree/bindings/display/msm/dp.txt line 251

Quoted 10 lines
Documentation/devicetree/bindings/display/msm/dp.txt new file +10 -0
+                       "ln_tx1_base", "gdsc_base";
+
+               clocks = <&dispcc DISP_CC_MDSS_AHB_CLK>,
+                        <&gcc GCC_USB3_PRIM_CLKREF_CLK>,
+                        <&gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>,
+                        <&gcc GCC_USB3_PRIM_PHY_PIPE_CLK>;
+               clock-names = "iface_clk", "ref_clk",
+                       "cfg_ahb_clk", "pipe_clk";
+               clock-rate = <0>;
+

Remove this extra newline.

Documentation/devicetree/bindings/display/msm/dpu.txt Required properties: · line 66

Quoted 12 lines
Documentation/devicetree/bindings/display/msm/dp.txt new file +1 -0
+       };
Documentation/devicetree/bindings/display/msm/dpu.txt +1 -2
index 551ae26..7e99e45 100644
@@ -63,8 +63,9 @@ Required properties:
        Documentation/devicetree/bindings/graph.txt
        Documentation/devicetree/bindings/media/video-interfaces.txt
 
-       Port 0 -> DPU_INTF1 (DSI1)
-       Port 1 -> DPU_INTF2 (DSI2)
+       Port 0 -> DPU_INTF0 (DP)

Why is port 0 changed to be DP? Can't we add DP to the end so that older DTs remain compatible?

Quoted 30 lines
Documentation/devicetree/bindings/display/msm/dpu.txt +11 -2
+       Port 1 -> DPU_INTF1 (DSI1)
+       Port 2 -> DPU_INTF2 (DSI2)
 
 Optional properties:
 - assigned-clocks: list of clock specifiers for clocks needing rate assignment
@@ -125,13 +126,20 @@ Example:
 
                                port@0 {
                                        reg = <0>;
-                                       dpu_intf1_out: endpoint {
-                                               remote-endpoint = <&dsi0_in>;
+                                       dpu_intf0_out: endpoint {
+                                               remote-endpoint = <&dp_in>;
                                        };
                                };
 
                                port@1 {
                                        reg = <1>;
+                                       dpu_intf1_out: endpoint {
+                                               remote-endpoint = <&dsi0_in>;
+                                       };
+                               };
+
+                               port@2 {
+                                       reg = <2>;
                                        dpu_intf2_out: endpoint {
                                                remote-endpoint = <&dsi1_in>;
                                        };
Signature · 1 line
$(echo -e 'The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,

You should fix your signature :)

#4 lore raw

Re: [DPU PATCH v4 1/5] dt-bindings: msm/dp: add bindings of DP/DP-PLL driver for Snapdragon

Hi Stephen Boyd,

Thanks for reviewing the change.
I have updated my comments inline for some comments, please check and let us know your views. Rest of the comments are addressed, will try to post a new patchset in couple of days.

Thanks,
--Vara

Quoted 60 lines · Stephen Boyd

Quoting Vara Reddy (2020-03-04 16:10:24)

Quoted 14 lines

From: Chandan Uddaraju <[email protected]>

Add bindings for Snapdragon DisplayPort and
display-port PLL driver.

Changes in V2:
Provide details about sel-gpio

Changes in V4:
Provide details about max dp lanes
Change the commit text

Quoted 7 lines
---
 .../devicetree/bindings/display/msm/dp.txt         | 252 
+++++++++++++++++++++
 .../devicetree/bindings/display/msm/dpu.txt        |  16 +-
 2 files changed, 264 insertions(+), 4 deletions(-)
 create mode 100644 

Documentation/devicetree/bindings/display/msm/dp.txt

Having this in YAML will certainly make it easier to review. Please do that as Rob H has asked.

Quoted 30 lines
Documentation/devicetree/bindings/display/msm/dp.txt new file +2 -0
new file mode 100644
index 0000000..1a4e17f
@@ -0,0 +1,252 @@
+Qualcomm Technologies, Inc.
+DP is the master Display Port device which supports DP host 

controllers that are compatible with VESA Display Port interface specification. +DP Controller: Required properties:

+- compatible:           Should be "qcom,dp-display".
+- reg:                  Base address and length of DP hardware's 
memory mapped regions.
+- cell-index:           Specifies the controller instance.
+- reg-names:            A list of strings that name the list of regs.
Documentation/devicetree/bindings/display/msm/dp.txt new file +6 -0
+                       "dp_ahb" - DP controller memory region.
+                       "dp_aux" - DP AUX memory region.
+                       "dp_link" - DP link layer memory region.
+                       "dp_p0" - DP pixel clock domain memory region.
+                       "dp_phy" - DP PHY memory region.
+                       "dp_ln_tx0" - USB3 DP PHY combo TX-0 lane 

memory region.
+ "dp_ln_tx1" - USB3 DP PHY combo TX-1 lane
memory region.
+ "dp_mmss_cc" - Display Clock Control memory
region.

Documentation/devicetree/bindings/display/msm/dp.txt new file +3 -0
+                       "qfprom_physical" - QFPROM Phys memory region.
+                       "dp_pll" - USB3 DP combo PLL memory region.
+                       "usb3_dp_com" - USB3 DP PHY combo memory 
Quoted 2 lines

region.
+ "hdcp_physical" - DP HDCP memory region.

A handful of these register properties overlap with the USB PHY region.

Quoted 8 lines

I suspect the existing qcom,sc7180-qmp-usb3-phy USB PHY binding that we have is wrong. It should describe both the USB part and the DP part of the combo PHY. Probably the DP part can be a subnode like how the superspeed PHY is a child node of the wrapper node. Then we'll have to figure out how to coordinate the access to usb3_dp_com (which I presume corresponds to the dp_com region in the usb3-phy binding) so that the USB and DP PHY drivers can figure out how to configure the "type-c" pins on the SoC to be 2 lanes DP and 2 lanes USB or 4 lanes USB or 4 lanes DP.

msm usb3_dp_com region has two separate software interface to combo phy used by DP and USB. We are trying to map DP specific address space for configuration.
I am not sure if USB can figure out dynamically a 2 lane DP and 2 lane USB case as i think we need to feed this information based on our hardware design through dtsi.

Quoted 90 lines
Quoted 4 lines
+- interrupt-parent     phandle to the interrupt parent device node.
+- interrupts:          The interrupt signal from the DP block.
+- clocks:               Clocks required for Display Port operation. 
Quoted 9 lines

See [1] for details on clock bindings.
+- clock-names: Names of the clocks corresponding to handles.
Following clocks are required:
+ "core_aux_clk",
"core_usb_ref_clk_src","core_usb_ref_clk", "core_usb_cfg_ahb_clk",
+ "core_usb_pipe_clk", "ctrl_link_clk",
"ctrl_link_iface_clk", "ctrl_crypto_clk",
+ "ctrl_pixel_clk", "pixel_clk_rcg",
"pixel_parent".

Please remove _clk suffix on all clock names. It's redundant.

Quoted 65 lines
+- pll-node:            phandle to DP PLL node.
+- vdda-1p2-supply:             phandle to vdda 1.2V regulator node.
+- vdda-0p9-supply:             phandle to vdda 0.9V regulator node.
+- qcom,aux-cfg0-settings:              Specifies the DP AUX 

configuration 0 settings. The first
+ entry in this array
corresponds to the register offset
+ within DP AUX, while the
remaining entries indicate the
+ programmable values.
+- qcom,aux-cfg1-settings: Specifies the DP AUX
configuration 1 settings. The first
+ entry in this array
corresponds to the register offset
+ within DP AUX, while the
remaining entries indicate the
+ programmable values.
+- qcom,aux-cfg2-settings: Specifies the DP AUX
configuration 2 settings. The first
+ entry in this array
corresponds to the register offset
+ within DP AUX, while the
remaining entries indicate the
+ programmable values.
+- qcom,aux-cfg3-settings: Specifies the DP AUX
configuration 3 settings. The first
+ entry in this array
corresponds to the register offset
+ within DP AUX, while the
remaining entries indicate the
+ programmable values.
+- qcom,aux-cfg4-settings: Specifies the DP AUX
configuration 4 settings. The first
+ entry in this array
corresponds to the register offset
+ within DP AUX, while the
remaining entries indicate the
+ programmable values.
+- qcom,aux-cfg5-settings: Specifies the DP AUX
configuration 5 settings. The first
+ entry in this array
corresponds to the register offset
+ within DP AUX, while the
remaining entries indicate the
+ programmable values.
+- qcom,aux-cfg6-settings: Specifies the DP AUX
configuration 6 settings. The first
+ entry in this array
corresponds to the register offset
+ within DP AUX, while the
remaining entries indicate the
+ programmable values.
+- qcom,aux-cfg7-settings: Specifies the DP AUX
configuration 7 settings. The first
+ entry in this array
corresponds to the register offset
+ within DP AUX, while the
remaining entries indicate the
+ programmable values.
+- qcom,aux-cfg8-settings: Specifies the DP AUX
configuration 8 settings. The first
+ entry in this array
corresponds to the register offset
+ within DP AUX, while the
remaining entries indicate the

Quoted 8 lines

+ programmable values.
+- qcom,aux-cfg9-settings: Specifies the DP AUX
configuration 9 settings. The first
+ entry in this array
corresponds to the register offset
+ within DP AUX, while the
remaining entries indicate the
+ programmable values.

Quoted 6 lines

It would be better if these properties were understandable by us mere kernel developers out there. Is it possible to make them into standalone properties that control certain configuration tunings? Are any of these not variable per-board and can be hardcoded based on the SoC into the driver?

These properties are used to tune the aux configurations and these vary per board and some customers need to tune these settings to get through dp phy compliance. I will move these settings to the driver code, if it is preferred.

Quoted 4 lines
Quoted 2 lines

+- qcom,max-pclk-frequency-khz: An integer specifying the
maximum. pixel clock in KHz supported by Display Port.

This can't be detected at runtime?

Some variants of DP controllers has a cap on the max-pclk frequency from DP controller side or from whole system bandwidth. To maintain same driver code across chipsets, we are trying to use this to cap the max frequency through dtsi.

Quoted 75 lines
Quoted 3 lines

+- qcom,max-lanes-for-dp: Maximum number of lanes that
can be used for Display port.

This can come from an of graph walk to figure out how many possible lanes are connected? Or some more standard property that describes logical to physical lane mapping, like data-lanes?

Quoted 20 lines

+- qcom,<type>-supply-entries: A node that lists the elements
of the supply used by the a particular "type" of DP module. The module "types"
+ can be "core", "ctrl", and
"phy". Within the same type,
+ there can be more than one
instance of this binding,
+ in which case the entry would
be appended with the

Documentation/devicetree/bindings/display/msm/dp.txt new file +3 -0
+                                       supply entry index.
+                                       e.g. qcom,ctrl-supply-entry@0
+                                       -- qcom,supply-name: name of 

the supply (vdd/vdda/vddio)
+ -- qcom,supply-min-voltage:
minimum voltage level (uV)
+ -- qcom,supply-max-voltage:
maximum voltage level (uV)
+ -- qcom,supply-enable-load:
load drawn (uA) from enabled supply
+ -- qcom,supply-disable-load:
load drawn (uA) from disabled supply

Quoted 8 lines

+ -- qcom,supply-pre-on-sleep:
time to sleep (ms) before turning on
+ -- qcom,supply-post-on-sleep:
time to sleep (ms) after turning on
+ -- qcom,supply-pre-off-sleep:
time to sleep (ms) before turning off
+ -- qcom,supply-post-off-sleep:
time to sleep (ms) after turning off

All these regulator properties look like things that should be handled by board constraints on certain supplies, not by this binding or the driver that matches this binding.

Quoted 9 lines

+- pinctrl-names: List of names to assign mdss pin states
defined in pinctrl device node
+ Refer to pinctrl-bindings.txt
+- pinctrl-<0..n>: Lists phandles each pointing to the pin
configuration node within a pin
+ controller. These pin
configurations are installed in the pinctrl
+ device node. Refer to
pinctrl-bindings.txt

These two properties aren't needed.

Quoted 11 lines
Documentation/devicetree/bindings/display/msm/dp.txt new file +2 -0
+DP Endpoint properties:
+  - remote-endpoint: For port@0, set to phandle of the connected 

panel/bridge's
+ input endpoint. For port@1, set to the DPU interface output. See
[2] for

Documentation/devicetree/bindings/display/msm/dp.txt new file +5 -0
+    device graph info.
+
+Optional properties:
+- qcom,aux-en-gpio:            Specifies the aux-channel enable gpio.
+- qcom,aux-sel-gpio:           Specifies the mux-selection that might 

be needed for aux interface.

Please use the normal '<your name here>-gpios' property for gpios.

Quoted 9 lines
Documentation/devicetree/bindings/display/msm/dp.txt new file +5 -0
+
+
+DP PLL: Required properties:
+- compatible:           Should be "qcom,dp-pll-10nm".
+- reg:                  Base address and length of DP hardware's 

memory mapped regions.

+- cell-index:           Specifies the PLL instance.
+- reg-names:            A list of strings that name the list of regs.
+                       "pll_base" - DP PLL memory region.
Quoted 8 lines
Quoted 5 lines
Documentation/devicetree/bindings/display/msm/dp.txt new file +2 -0
+                       "phy_base" - DP PHY memory region.
+                       "ln_tx0_base" - USB3 DP PHY combo TX-0 lane 

memory region.
+ "ln_tx1_base" - USB3 DP PHY combo TX-1 lane
memory region.

This was in the other node binding. Probably the only reg property required here is pll_base?

Unfortunately msm DP controller PHY and PLL modules are tightly coupled in hardware. DP phy hardware programming sequence has lot of register programming sequence interleaved between PLL and PHY address space.
Our design has different DP PLL and DP PHY drivers, we need to map the needed address space.

Quoted 170 lines
Quoted 4 lines
+                       "gdsc_base" - gdsc memory region.
+- interrupt-parent     phandle to the interrupt parent device node.
+- interrupts:          The interrupt signal from the DP block.
Quoted 8 lines

+- clocks: Clocks required for Display Port operation.
See [1] for details on clock bindings.
+- clock-names: Names of the clocks corresponding to handles.
Following clocks are required:
+ "iface_clk", "ref_clk", cfg_ahb_clk",
"pipe_clk".

+- clock-rate:           Initial clock rate to be configured. For the 
shared clocks, the default value                       is set to zero so that minimum clock value is configured. Non-zero clock

We have assigned-clock-rates for this.

Documentation/devicetree/bindings/display/msm/dp.txt line 125

Quoted 8 lines
Documentation/devicetree/bindings/display/msm/dp.txt new file +8 -0
+                       value can be used to configure DP pixel clock.
+
+
+[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
+[2] Documentation/devicetree/bindings/graph.txt
+
+Example:
+       msm_dp: dp_display@ae90000{

No underscores in node names. It should also be somewhat generic like 'display-controller' or 'displayport-controller' and the first reg property should match the unit address after the at sign (@).

Quoted 14 lines
Documentation/devicetree/bindings/display/msm/dp.txt new file +14 -0
+               cell-index = <0>;
+               compatible = "qcom,dp-display";
+
+               reg =   <0 0x90000 0x0dc>,
+                       <0 0x90200 0x0c0>,
+                       <0 0x90400 0x508>,
+                       <0 0x90a00 0x094>,
+                       <1 0xeaa00 0x200>,
+                       <1 0xea200 0x200>,
+                       <1 0xea600 0x200>,
+                       <2 0x02000 0x1a0>,
+                       <3 0x00000 0x621c>,
+                       <1 0xea000 0x180>,
+                       <1 0xe8000 0x20>,

Documentation/devicetree/bindings/display/msm/dp.txt line 149

Quoted 10 lines
Documentation/devicetree/bindings/display/msm/dp.txt new file +10 -0
+                       <4 0xe1000 0x034>;
+               reg-names = "dp_ahb", "dp_aux", "dp_link",
+                       "dp_p0", "dp_phy", "dp_ln_tx0", "dp_ln_tx1",
+                       "dp_mmss_cc", "qfprom_physical", "dp_pll",
+                       "usb3_dp_com", "hdcp_physical";
+
+               interrupt-parent = <&display_subsystem>;
+               interrupts = <12 0>;
+
+               extcon = <&usb_1_ssphy>;

Please no extcon

Quoted 93 lines
Documentation/devicetree/bindings/display/msm/dp.txt new file +69 -0
+               clocks =  <&dispcc DISP_CC_MDSS_DP_AUX_CLK>,
+                       <&rpmhcc RPMH_CXO_CLK>,
+                       <&gcc GCC_USB3_PRIM_CLKREF_CLK>,
+                       <&gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>,
+                       <&gcc GCC_USB3_PRIM_PHY_PIPE_CLK>,
+                       <&dispcc DISP_CC_MDSS_DP_LINK_CLK>,
+                       <&dispcc DISP_CC_MDSS_DP_LINK_INTF_CLK>,
+                       <&dispcc DISP_CC_MDSS_DP_PIXEL_CLK>,
+                       <&dispcc DISP_CC_MDSS_DP_CRYPTO_CLK>,
+                       <&dispcc DISP_CC_MDSS_DP_PIXEL_CLK_SRC>;
+               clock-names = "core_aux_clk", "core_ref_clk_src",
+                       "core_usb_ref_clk", "core_usb_cfg_ahb_clk",
+                       "core_usb_pipe_clk", "ctrl_link_clk",
+                       "ctrl_link_iface_clk", "ctrl_pixel_clk",
+                       "crypto_clk", "pixel_clk_rcg";
+
+               pll-node = <&dp_pll>;
+               vdda-1p2-supply = <&vreg_l3c_1p2>;
+               vdda-0p9-supply = <&vreg_l4a_0p8>;
+               qcom,aux-cfg0-settings = [20 00];
+               qcom,aux-cfg1-settings = [24 13 23 1d];
+               qcom,aux-cfg2-settings = [28 24];
+               qcom,aux-cfg3-settings = [2c 00];
+               qcom,aux-cfg4-settings = [30 0a];
+               qcom,aux-cfg5-settings = [34 26];
+               qcom,aux-cfg6-settings = [38 0a];
+               qcom,aux-cfg7-settings = [3c 03];
+               qcom,aux-cfg8-settings = [40 bb];
+               qcom,aux-cfg9-settings = [44 03];
+
+               qcom,max-pclk-frequency-khz = <675000>;
+               qcom,max-lanes-for-dp = <2>;
+
+               qcom,ctrl-supply-entries {
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+
+                       qcom,ctrl-supply-entry@0 {
+                               reg = <0>;
+                               qcom,supply-name = "vdda-1p2";
+                               qcom,supply-min-voltage = <1200000>;
+                               qcom,supply-max-voltage = <1200000>;
+                               qcom,supply-enable-load = <21800>;
+                               qcom,supply-disable-load = <4>;
+                       };
+               };
+
+               qcom,phy-supply-entries {
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+
+                       qcom,phy-supply-entry@0 {
+                               reg = <0>;
+                               qcom,supply-name = "vdda-0p9";
+                               qcom,supply-min-voltage = <880000>;
+                               qcom,supply-max-voltage = <880000>;
+                               qcom,supply-enable-load = <36000>;
+                               qcom,supply-disable-load = <32>;
+                       };
+               };
+
+               ports {
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+
+                       port@0 {
+                               reg = <0>;
+                               dp_in: endpoint {
+                                       remote-endpoint = 

<&dpu_intf0_out>;

Documentation/devicetree/bindings/display/msm/dp.txt new file +23 -0
+                               };
+                       };
+
+                       port@1 {
+                               reg = <1>;
+                               dp_out: endpoint {
+                               };
+                       };
+               };
+       };
+
+       dp_pll: dp-pll@c011000 {
+               compatible = "qcom,dp-pll-10nm";
+               label = "DP PLL";
+               cell-index = <0>;
+               #clock-cells = <1>;
+
+               reg = <1 0xea000 0x200>,
+                     <1 0xeaa00 0x200>,
+                     <1 0xea200 0x200>,
+                     <1 0xea600 0x200>,
+                     <2 0x03000 0x8>;
+               reg-names = "pll_base", "phy_base", "ln_tx0_base",

Documentation/devicetree/bindings/display/msm/dp.txt line 251

Quoted 10 lines
Documentation/devicetree/bindings/display/msm/dp.txt new file +10 -0
+                       "ln_tx1_base", "gdsc_base";
+
+               clocks = <&dispcc DISP_CC_MDSS_AHB_CLK>,
+                        <&gcc GCC_USB3_PRIM_CLKREF_CLK>,
+                        <&gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>,
+                        <&gcc GCC_USB3_PRIM_PHY_PIPE_CLK>;
+               clock-names = "iface_clk", "ref_clk",
+                       "cfg_ahb_clk", "pipe_clk";
+               clock-rate = <0>;
+

Remove this extra newline.

Quoted 9 lines
Documentation/devicetree/bindings/display/msm/dp.txt new file +1 -0
+       };
Documentation/devicetree/bindings/display/msm/dpu.txt +0 -0
index 551ae26..7e99e45 100644
@@ -63,8 +63,9 @@ Required properties:
        Documentation/devicetree/bindings/graph.txt
        Documentation/devicetree/bindings/media/video-interfaces.txt
 
Quoted 6 lines

Documentation/devicetree/bindings/display/msm/dpu.txt Required properties: · line 66

Quoted 3 lines
Documentation/devicetree/bindings/display/msm/dpu.txt +1 -2
-       Port 0 -> DPU_INTF1 (DSI1)
-       Port 1 -> DPU_INTF2 (DSI2)
+       Port 0 -> DPU_INTF0 (DP)

Why is port 0 changed to be DP? Can't we add DP to the end so that older DTs remain compatible?

Interface 0 block of msm hardware is always connected to Displayport. We are trying to retain the ports as per the hardware layed-out.

Quoted 38 lines
Quoted 34 lines
Documentation/devicetree/bindings/display/msm/dpu.txt +2 -0
+       Port 1 -> DPU_INTF1 (DSI1)
+       Port 2 -> DPU_INTF2 (DSI2)
 
 Optional properties:
 - assigned-clocks: list of clock specifiers for clocks needing rate 

assignment

Documentation/devicetree/bindings/display/msm/dpu.txt +9 -2
@@ -125,13 +126,20 @@ Example:
 
                                port@0 {
                                        reg = <0>;
-                                       dpu_intf1_out: endpoint {
-                                               remote-endpoint = 
 <&dsi0_in>;
+                                       dpu_intf0_out: endpoint {
+                                               remote-endpoint = 
 <&dp_in>;
                                        };
                                };
 
                                port@1 {
                                        reg = <1>;
+                                       dpu_intf1_out: endpoint {
+                                               remote-endpoint = 
 <&dsi0_in>;
+                                       };
+                               };
+
+                               port@2 {
+                                       reg = <2>;
                                        dpu_intf2_out: endpoint {
                                                remote-endpoint = 
<&dsi1_in>;
                                        };
Quoted 2 lines

--
$(echo -e 'The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,

You should fix your signature :)

author
#5 lore raw

PATCH v4 2/5 drm: add constant N value in helper file

In reply to Vara Reddy

From: Chandan Uddaraju <[email protected]>

The constant N value (0x8000) is used by multiple DP drivers. Define this value in header file and use this in the existing i915 display driver.

---
 drivers/gpu/drm/i915/display/intel_display.c | 2 +-
 include/drm/drm_dp_helper.h                  | 2 ++
 2 files changed, 3 insertions(+), 1 deletion(-)
drivers/gpu/drm/i915/display/intel_display.c +1 -1
index effc425..3c016f3 100644
@@ -7618,7 +7618,7 @@ static void compute_m_n(unsigned int m, unsigned int n,
 	 * which the devices expect also in synchronous clock mode.
 	 */
 	if (constant_n)
-		*ret_n = 0x8000;
+		*ret_n = DP_LINK_CONSTANT_N_VALUE;
 	else
 		*ret_n = min_t(unsigned int, roundup_pow_of_two(n), DATA_LINK_N_MAX);
 
include/drm/drm_dp_helper.h +2 -0
index 51ecb51..b509e74 100644
@@ -1455,6 +1455,8 @@ static inline ssize_t drm_dp_dpcd_writeb(struct drm_dp_aux *aux,
 int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux,
 				 u8 status[DP_LINK_STATUS_SIZE]);
 
+#define DP_LINK_CONSTANT_N_VALUE 0x8000
+
 int drm_dp_downstream_max_clock(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
 				const u8 port_cap[4]);
 int drm_dp_downstream_max_bpc(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
Signature · 2 lines
$(echo -e 'The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project')
author
#6 lore raw

PATCH v4 3/5 drm/msm/dp: add displayPort driver support

In reply to Vara Reddy

From: Chandan Uddaraju <[email protected]>

Add the needed displayPort files to enable DP driver on msm target.

"dp_display" module is the main module that calls into other sub-modules. "dp_drm" file represents the interface between DRM framework and DP driver.

changes in v2:
-- Update copyright markings on all relevant files.
-- Change pr_err() to DRM_ERROR()
-- Use APIs directly instead of function pointers.
-- Use drm_display_mode structure to store link parameters. -- Use macros for register definitions instead of hardcoded values. -- Replace writel_relaxed/readl_relaxed with writel/readl
and remove memory barriers.
-- Remove unnecessary NULL checks.
-- Use drm helper functions for dpcd read/write.
-- Use DRM_DEBUG_DP for debug msgs.

changes in V3:
-- Removed changes in dpu_io_util.[ch]
-- Added locking around "is_connected" flag and removed atomic_set() -- Removed the argument validation checks in all the static functions
except initialization functions and few API across msm/dp files.
-- Removed hardcoded values for register reads/writes
-- Removed vreg related generic structures.
-- Added return values where ever necessary.
-- Updated dp_ctrl_on function.
-- Calling the ctrl specific catalog functions directly instead of
function pointers.
-- Added separate change that adds standard value in drm_dp_helper file. -- Added separate change in this list that is used to initialize DP. -- Added change to use drm_dp_get_adjust_request_voltage() function.

changes in V4:
-- Fix mvid, nvid calculation.
-- Shift video timings to align to bottom right. -- Fix register offsets for pixel_m and pixel_n. -- Fix TPG programming sequence.
-- Update DP ack override register to recommended value. -- Added code to log displayport registers.
-- Update aux setup with recommended settings. -- Fix TU calculations for all resolutions.
-- Limit linkrate to HBR2 until HBR3 support is added. -- Update DPCD read sequence.
-- Update code comments in dp_power_init() of V3. -- Remove extcon dependencies.
-- Move drm helper functions locally.

---
 drivers/gpu/drm/msm/Kconfig                        |    9 +
 drivers/gpu/drm/msm/Makefile                       |   13 +
 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c        |   20 +
 .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c   |    8 +
 drivers/gpu/drm/msm/dp/dp_aux.c                    |  549 +++++++
 drivers/gpu/drm/msm/dp/dp_aux.h                    |   37 +
 drivers/gpu/drm/msm/dp/dp_catalog.c                | 1118 +++++++++++++
 drivers/gpu/drm/msm/dp/dp_catalog.h                |   87 +
 drivers/gpu/drm/msm/dp/dp_ctrl.c                   | 1727 ++++++++++++++++++++
 drivers/gpu/drm/msm/dp/dp_ctrl.h                   |   35 +
 drivers/gpu/drm/msm/dp/dp_display.c                |  974 +++++++++++
 drivers/gpu/drm/msm/dp/dp_display.h                |   28 +
 drivers/gpu/drm/msm/dp/dp_drm.c                    |  176 ++
 drivers/gpu/drm/msm/dp/dp_drm.h                    |   19 +
 drivers/gpu/drm/msm/dp/dp_hpd.c                    |   87 +
 drivers/gpu/drm/msm/dp/dp_hpd.h                    |   81 +
 drivers/gpu/drm/msm/dp/dp_link.c                   | 1246 ++++++++++++++
 drivers/gpu/drm/msm/dp/dp_link.h                   |  140 ++
 drivers/gpu/drm/msm/dp/dp_panel.c                  |  500 ++++++
 drivers/gpu/drm/msm/dp/dp_panel.h                  |   95 ++
 drivers/gpu/drm/msm/dp/dp_parser.c                 |  507 ++++++
 drivers/gpu/drm/msm/dp/dp_parser.h                 |  224 +++
 drivers/gpu/drm/msm/dp/dp_power.c                  |  563 +++++++
 drivers/gpu/drm/msm/dp/dp_power.h                  |   52 +
 drivers/gpu/drm/msm/dp/dp_reg.h                    |  489 ++++++
 drivers/gpu/drm/msm/msm_drv.c                      |    2 +
 drivers/gpu/drm/msm/msm_drv.h                      |   53 +-
 27 files changed, 8836 insertions(+), 3 deletions(-)
 create mode 100644 drivers/gpu/drm/msm/dp/dp_aux.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_aux.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_catalog.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_catalog.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_ctrl.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_ctrl.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_display.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_display.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_drm.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_drm.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_hpd.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_hpd.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_link.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_link.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_panel.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_panel.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_parser.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_parser.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_power.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_power.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_reg.h
drivers/gpu/drm/msm/Kconfig +9 -0
index 6deaa7d..7946cb1 100644
@@ -57,6 +57,15 @@ config DRM_MSM_HDMI_HDCP
 	help
 	  Choose this option to enable HDCP state machine
 
+config DRM_MSM_DP
+	bool "Enable DP support in MSM DRM driver"
+	depends on DRM_MSM
+	default n
+	help
+	  Compile in support for DP driver in msm drm driver. DP external
+	  display support is enabled through this config option. It can
+	  be primary or secondary display on device.
+
 config DRM_MSM_DSI
 	bool "Enable DSI support in MSM DRM driver"
 	depends on DRM_MSM
drivers/gpu/drm/msm/Makefile +13 -0
index 1579cf0..c846599 100644
@@ -2,6 +2,7 @@
 ccflags-y := -I $(srctree)/$(src)
 ccflags-y += -I $(srctree)/$(src)/disp/dpu1
 ccflags-$(CONFIG_DRM_MSM_DSI) += -I $(srctree)/$(src)/dsi
+ccflags-$(CONFIG_DRM_MSM_DP) += -I $(srctree)/$(src)/dp
 
 msm-y := \
 	adreno/adreno_device.o \
@@ -96,8 +97,20 @@ msm-y := \
 
 msm-$(CONFIG_DEBUG_FS) += adreno/a5xx_debugfs.o
 
+
 msm-$(CONFIG_DRM_MSM_GPU_STATE)	+= adreno/a6xx_gpu_state.o
 
+msm-$(CONFIG_DRM_MSM_DP)+= dp/dp_aux.o \
+	dp/dp_catalog.o \
+	dp/dp_ctrl.o \
+	dp/dp_display.o \
+	dp/dp_drm.o \
+	dp/dp_hpd.o \
+	dp/dp_link.o \
+	dp/dp_panel.o \
+	dp/dp_parser.o \
+	dp/dp_power.o
+
 msm-$(CONFIG_DRM_FBDEV_EMULATION) += msm_fbdev.o
 msm-$(CONFIG_COMMON_CLK) += disp/mdp4/mdp4_lvds_pll.o
 msm-$(CONFIG_COMMON_CLK) += hdmi/hdmi_pll_8960.o
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +20 -0
index f8ac3bf..b063243 100644
@@ -978,6 +978,9 @@ static void dpu_encoder_virt_mode_set(struct drm_encoder *drm_enc,
 
 	trace_dpu_enc_mode_set(DRMID(drm_enc));
 
+	if (drm_enc->encoder_type == DRM_MODE_ENCODER_TMDS && priv->dp)
+		msm_dp_display_mode_set(priv->dp, drm_enc, mode, adj_mode);
+
 	list_for_each_entry(conn_iter, connector_list, head)
 		if (conn_iter->encoder == drm_enc)
 			conn = conn_iter;
@@ -1141,6 +1144,7 @@ static void dpu_encoder_virt_enable(struct drm_encoder *drm_enc)
 {
 	struct dpu_encoder_virt *dpu_enc = NULL;
 	int ret = 0;
+	struct msm_drm_private *priv;
 	struct drm_display_mode *cur_mode = NULL;
 
 	if (!drm_enc) {
@@ -1151,6 +1155,7 @@ static void dpu_encoder_virt_enable(struct drm_encoder *drm_enc)
 
 	mutex_lock(&dpu_enc->enc_lock);
 	cur_mode = &dpu_enc->base.crtc->state->adjusted_mode;
+	priv = drm_enc->dev->dev_private;
 
 	trace_dpu_enc_enable(DRMID(drm_enc), cur_mode->hdisplay,
 			     cur_mode->vdisplay);
@@ -1171,6 +1176,15 @@ static void dpu_encoder_virt_enable(struct drm_encoder *drm_enc)
 
 	_dpu_encoder_virt_enable_helper(drm_enc);
 
+	if (drm_enc->encoder_type == DRM_MODE_ENCODER_TMDS && priv->dp) {
+		ret = msm_dp_display_enable(priv->dp,
+						drm_enc);
+		if (ret) {
+			DPU_ERROR_ENC(dpu_enc, "dp display enable failed: %d\n",
+				ret);
+			goto out;
+		}
+	}
 	dpu_enc->enabled = true;
 
 out:
@@ -1201,6 +1215,12 @@ static void dpu_encoder_virt_disable(struct drm_encoder *drm_enc)
 	priv = drm_enc->dev->dev_private;
 	dpu_kms = to_dpu_kms(priv->kms);
 
+	if (drm_enc->encoder_type == DRM_MODE_ENCODER_TMDS && priv->dp) {
+		if (msm_dp_display_disable(priv->dp, drm_enc)) {
+			DPU_ERROR_ENC(dpu_enc, "dp display disable failed\n");
+			return;
+		}
+	}
 	trace_dpu_enc_disable(DRMID(drm_enc));
 
 	/* wait for idle */
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c +8 -0
index c71c18d..c1dfcc4 100644
@@ -100,6 +100,14 @@ static void drm_mode_to_intf_timing_params(
 	 * display_v_end -= mode->hsync_start - mode->hdisplay;
 	 * }
 	 */
+	/* for DP/EDP, Shift timings to align it to bottom right */
+	if ((phys_enc->hw_intf->cap->type == INTF_DP) ||
+		(phys_enc->hw_intf->cap->type == INTF_EDP)) {
+		timing->h_back_porch += timing->h_front_porch;
+		timing->h_front_porch = 0;
+		timing->v_back_porch += timing->v_front_porch;
+		timing->v_front_porch = 0;
+	}
 }
 
 static u32 get_horizontal_total(const struct intf_timing_params *timing)
drivers/gpu/drm/msm/dp/dp_aux.c new file +549 -0

549 lines not on this page · raw message

drivers/gpu/drm/msm/dp/dp_aux.h new file +37 -0
new file mode 100644
index 0000000..ca22b5c
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _DP_AUX_H_
+#define _DP_AUX_H_
+
+#include "dp_catalog.h"
+#include <drm/drm_dp_helper.h>
+
+enum dp_aux_error {
+	DP_AUX_ERR_NONE	= 0,
+	DP_AUX_ERR_ADDR	= -1,
+	DP_AUX_ERR_TOUT	= -2,
+	DP_AUX_ERR_NACK	= -3,
+	DP_AUX_ERR_DEFER	= -4,
+	DP_AUX_ERR_NACK_DEFER	= -5,
+};
+
+struct dp_aux {
+	struct drm_dp_aux *drm_aux;
+	struct dp_aux_cfg *cfg;
+};
+
+int dp_aux_register(struct dp_aux *aux);
+void dp_aux_unregister(struct dp_aux *aux);
+void dp_aux_isr(struct dp_aux *aux);
+void dp_aux_init(struct dp_aux *aux, struct dp_aux_cfg *aux_cfg);
+void dp_aux_deinit(struct dp_aux *aux);
+void dp_aux_reconfig(struct dp_aux *aux);
+
+struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog *catalog,
+			  struct dp_aux_cfg *aux_cfg);
+void dp_aux_put(struct dp_aux *aux);
+
+#endif /*__DP_AUX_H_*/
drivers/gpu/drm/msm/dp/dp_catalog.c new file +1118 -0

1,118 lines not on this page · raw message

drivers/gpu/drm/msm/dp/dp_catalog.h new file +87 -0

87 lines not on this page · raw message

drivers/gpu/drm/msm/dp/dp_ctrl.c new file +1727 -0

1,103 lines not on this page · raw message

drivers/gpu/drm/msm/dp/dp_ctrl.h new file +35 -0
new file mode 100644
index 0000000..63681e82

Lines of this file are not shown. View the raw message.

drivers/gpu/drm/msm/dp/dp_display.c new file +974 -0

974 lines not on this page · raw message

drivers/gpu/drm/msm/dp/dp_display.h new file +28 -0
new file mode 100644
index 0000000..dad8610

Lines of this file are not shown. View the raw message.

drivers/gpu/drm/msm/dp/dp_drm.c new file +176 -0

176 lines not on this page · raw message

drivers/gpu/drm/msm/dp/dp_drm.h new file +19 -0
new file mode 100644
index 0000000..a4ffdef

Lines of this file are not shown. View the raw message.

drivers/gpu/drm/msm/dp/dp_hpd.c new file +87 -0

87 lines not on this page · raw message

drivers/gpu/drm/msm/dp/dp_hpd.h new file +81 -0

81 lines not on this page · raw message

drivers/gpu/drm/msm/dp/dp_link.c new file +1246 -0

1,246 lines not on this page · raw message

drivers/gpu/drm/msm/dp/dp_link.h new file +140 -0

140 lines not on this page · raw message

drivers/gpu/drm/msm/dp/dp_panel.c new file +500 -0

500 lines not on this page · raw message

drivers/gpu/drm/msm/dp/dp_panel.h new file +95 -0

95 lines not on this page · raw message

drivers/gpu/drm/msm/dp/dp_parser.c new file +507 -0

507 lines not on this page · raw message

drivers/gpu/drm/msm/dp/dp_parser.h new file +224 -0

224 lines not on this page · raw message

drivers/gpu/drm/msm/dp/dp_power.c new file +563 -0

563 lines not on this page · raw message

drivers/gpu/drm/msm/dp/dp_power.h new file +52 -0

52 lines not on this page · raw message

drivers/gpu/drm/msm/dp/dp_reg.h new file +489 -0

489 lines not on this page · raw message

drivers/gpu/drm/msm/msm_drv.c +2 -0
index f50fefb..4b8bd2b 100644

Lines of this file are not shown. View the raw message.

drivers/gpu/drm/msm/msm_drv.h +50 -3

53 lines not on this page · raw message

The rest of this diff is not shown. View the raw message.

Signature · 2 lines
$(echo -e 'The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project')

Part of the diff is not shown here. View the raw message.

#7 lore raw

Re: [Freedreno] [DPU PATCH v4 3/5] drm/msm/dp: add displayPort driver support

Quoted 109 lines · Vara Reddy

From: Chandan Uddaraju <[email protected]>

Add the needed displayPort files to enable DP driver on msm target.

"dp_display" module is the main module that calls into other sub-modules. "dp_drm" file represents the interface between DRM framework and DP driver.

changes in v2:
-- Update copyright markings on all relevant files.
-- Change pr_err() to DRM_ERROR()
-- Use APIs directly instead of function pointers.
-- Use drm_display_mode structure to store link parameters. -- Use macros for register definitions instead of hardcoded values. -- Replace writel_relaxed/readl_relaxed with writel/readl
and remove memory barriers.
-- Remove unnecessary NULL checks.
-- Use drm helper functions for dpcd read/write.
-- Use DRM_DEBUG_DP for debug msgs.

changes in V3:
-- Removed changes in dpu_io_util.[ch]
-- Added locking around "is_connected" flag and removed atomic_set() -- Removed the argument validation checks in all the static functions
except initialization functions and few API across msm/dp files.
-- Removed hardcoded values for register reads/writes
-- Removed vreg related generic structures.
-- Added return values where ever necessary.
-- Updated dp_ctrl_on function.
-- Calling the ctrl specific catalog functions directly instead of
function pointers.
-- Added separate change that adds standard value in drm_dp_helper file. -- Added separate change in this list that is used to initialize DP. -- Added change to use drm_dp_get_adjust_request_voltage() function.

changes in V4:
-- Fix mvid, nvid calculation.
-- Shift video timings to align to bottom right. -- Fix register offsets for pixel_m and pixel_n. -- Fix TPG programming sequence.
-- Update DP ack override register to recommended value. -- Added code to log displayport registers.
-- Update aux setup with recommended settings. -- Fix TU calculations for all resolutions.
-- Limit linkrate to HBR2 until HBR3 support is added. -- Update DPCD read sequence.
-- Update code comments in dp_power_init() of V3. -- Remove extcon dependencies.
-- Move drm helper functions locally.

---
 drivers/gpu/drm/msm/Kconfig                        |    9 +
 drivers/gpu/drm/msm/Makefile                       |   13 +
 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c        |   20 +
 .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c   |    8 +
 drivers/gpu/drm/msm/dp/dp_aux.c                    |  549 +++++++
 drivers/gpu/drm/msm/dp/dp_aux.h                    |   37 +
 drivers/gpu/drm/msm/dp/dp_catalog.c                | 1118 +++++++++++++
 drivers/gpu/drm/msm/dp/dp_catalog.h                |   87 +
 drivers/gpu/drm/msm/dp/dp_ctrl.c                   | 1727 ++++++++++++++++++++
 drivers/gpu/drm/msm/dp/dp_ctrl.h                   |   35 +
 drivers/gpu/drm/msm/dp/dp_display.c                |  974 +++++++++++
 drivers/gpu/drm/msm/dp/dp_display.h                |   28 +
 drivers/gpu/drm/msm/dp/dp_drm.c                    |  176 ++
 drivers/gpu/drm/msm/dp/dp_drm.h                    |   19 +
 drivers/gpu/drm/msm/dp/dp_hpd.c                    |   87 +
 drivers/gpu/drm/msm/dp/dp_hpd.h                    |   81 +
 drivers/gpu/drm/msm/dp/dp_link.c                   | 1246 ++++++++++++++
 drivers/gpu/drm/msm/dp/dp_link.h                   |  140 ++
 drivers/gpu/drm/msm/dp/dp_panel.c                  |  500 ++++++
 drivers/gpu/drm/msm/dp/dp_panel.h                  |   95 ++
 drivers/gpu/drm/msm/dp/dp_parser.c                 |  507 ++++++
 drivers/gpu/drm/msm/dp/dp_parser.h                 |  224 +++
 drivers/gpu/drm/msm/dp/dp_power.c                  |  563 +++++++
 drivers/gpu/drm/msm/dp/dp_power.h                  |   52 +
 drivers/gpu/drm/msm/dp/dp_reg.h                    |  489 ++++++
 drivers/gpu/drm/msm/msm_drv.c                      |    2 +
 drivers/gpu/drm/msm/msm_drv.h                      |   53 +-
 27 files changed, 8836 insertions(+), 3 deletions(-)
 create mode 100644 drivers/gpu/drm/msm/dp/dp_aux.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_aux.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_catalog.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_catalog.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_ctrl.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_ctrl.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_display.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_display.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_drm.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_drm.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_hpd.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_hpd.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_link.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_link.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_panel.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_panel.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_parser.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_parser.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_power.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_power.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_reg.h
drivers/gpu/drm/msm/Kconfig +0 -0
index 6deaa7d..7946cb1 100644

drivers/gpu/drm/msm/Kconfig config DRM_MSM_HDMI_HDCP · line 63

Quoted 7 lines
drivers/gpu/drm/msm/Kconfig +4 -0
@@ -57,6 +57,15 @@ config DRM_MSM_HDMI_HDCP
 	help
 	  Choose this option to enable HDCP state machine
 
+config DRM_MSM_DP
+	bool "Enable DP support in MSM DRM driver"
+	depends on DRM_MSM
+	default n

default n is assumed if there is no default specified.

Quoted 21 lines
drivers/gpu/drm/msm/Kconfig +5 -0
+	help
+	  Compile in support for DP driver in msm drm driver. DP external
+	  display support is enabled through this config option. It can
+	  be primary or secondary display on device.
+
 config DRM_MSM_DSI
 	bool "Enable DSI support in MSM DRM driver"
 	depends on DRM_MSM
drivers/gpu/drm/msm/Makefile +1 -0
index 1579cf0..c846599 100644
@@ -2,6 +2,7 @@
 ccflags-y := -I $(srctree)/$(src)
 ccflags-y += -I $(srctree)/$(src)/disp/dpu1
 ccflags-$(CONFIG_DRM_MSM_DSI) += -I $(srctree)/$(src)/dsi
+ccflags-$(CONFIG_DRM_MSM_DP) += -I $(srctree)/$(src)/dp
 
 msm-y := \
 	adreno/adreno_device.o \

drivers/gpu/drm/msm/Makefile msm-y := \ · line 100

Quoted 4 lines
drivers/gpu/drm/msm/Makefile +1 -0
@@ -96,8 +97,20 @@ msm-y := \
 
 msm-$(CONFIG_DEBUG_FS) += adreno/a5xx_debugfs.o
 
+

Extra blank line here.

Quoted 100 lines
drivers/gpu/drm/msm/Makefile +11 -0
 msm-$(CONFIG_DRM_MSM_GPU_STATE)	+= adreno/a6xx_gpu_state.o
 
+msm-$(CONFIG_DRM_MSM_DP)+= dp/dp_aux.o \
+	dp/dp_catalog.o \
+	dp/dp_ctrl.o \
+	dp/dp_display.o \
+	dp/dp_drm.o \
+	dp/dp_hpd.o \
+	dp/dp_link.o \
+	dp/dp_panel.o \
+	dp/dp_parser.o \
+	dp/dp_power.o
+
 msm-$(CONFIG_DRM_FBDEV_EMULATION) += msm_fbdev.o
 msm-$(CONFIG_COMMON_CLK) += disp/mdp4/mdp4_lvds_pll.o
 msm-$(CONFIG_COMMON_CLK) += hdmi/hdmi_pll_8960.o
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +20 -0
index f8ac3bf..b063243 100644
@@ -978,6 +978,9 @@ static void dpu_encoder_virt_mode_set(struct drm_encoder *drm_enc,
 
 	trace_dpu_enc_mode_set(DRMID(drm_enc));
 
+	if (drm_enc->encoder_type == DRM_MODE_ENCODER_TMDS && priv->dp)
+		msm_dp_display_mode_set(priv->dp, drm_enc, mode, adj_mode);
+
 	list_for_each_entry(conn_iter, connector_list, head)
 		if (conn_iter->encoder == drm_enc)
 			conn = conn_iter;
@@ -1141,6 +1144,7 @@ static void dpu_encoder_virt_enable(struct drm_encoder *drm_enc)
 {
 	struct dpu_encoder_virt *dpu_enc = NULL;
 	int ret = 0;
+	struct msm_drm_private *priv;
 	struct drm_display_mode *cur_mode = NULL;
 
 	if (!drm_enc) {
@@ -1151,6 +1155,7 @@ static void dpu_encoder_virt_enable(struct drm_encoder *drm_enc)
 
 	mutex_lock(&dpu_enc->enc_lock);
 	cur_mode = &dpu_enc->base.crtc->state->adjusted_mode;
+	priv = drm_enc->dev->dev_private;
 
 	trace_dpu_enc_enable(DRMID(drm_enc), cur_mode->hdisplay,
 			     cur_mode->vdisplay);
@@ -1171,6 +1176,15 @@ static void dpu_encoder_virt_enable(struct drm_encoder *drm_enc)
 
 	_dpu_encoder_virt_enable_helper(drm_enc);
 
+	if (drm_enc->encoder_type == DRM_MODE_ENCODER_TMDS && priv->dp) {
+		ret = msm_dp_display_enable(priv->dp,
+						drm_enc);
+		if (ret) {
+			DPU_ERROR_ENC(dpu_enc, "dp display enable failed: %d\n",
+				ret);
+			goto out;
+		}
+	}
 	dpu_enc->enabled = true;
 
 out:
@@ -1201,6 +1215,12 @@ static void dpu_encoder_virt_disable(struct drm_encoder *drm_enc)
 	priv = drm_enc->dev->dev_private;
 	dpu_kms = to_dpu_kms(priv->kms);
 
+	if (drm_enc->encoder_type == DRM_MODE_ENCODER_TMDS && priv->dp) {
+		if (msm_dp_display_disable(priv->dp, drm_enc)) {
+			DPU_ERROR_ENC(dpu_enc, "dp display disable failed\n");
+			return;
+		}
+	}
 	trace_dpu_enc_disable(DRMID(drm_enc));
 
 	/* wait for idle */
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c +8 -0
index c71c18d..c1dfcc4 100644
@@ -100,6 +100,14 @@ static void drm_mode_to_intf_timing_params(
 	 * display_v_end -= mode->hsync_start - mode->hdisplay;
 	 * }
 	 */
+	/* for DP/EDP, Shift timings to align it to bottom right */
+	if ((phys_enc->hw_intf->cap->type == INTF_DP) ||
+		(phys_enc->hw_intf->cap->type == INTF_EDP)) {
+		timing->h_back_porch += timing->h_front_porch;
+		timing->h_front_porch = 0;
+		timing->v_back_porch += timing->v_front_porch;
+		timing->v_front_porch = 0;
+	}
 }
 
 static u32 get_horizontal_total(const struct intf_timing_params *timing)
drivers/gpu/drm/msm/dp/dp_aux.c new file +0 -0
new file mode 100644
index 0000000..e033a90

drivers/gpu/drm/msm/dp/dp_aux.c line 6

Quoted 6 lines
drivers/gpu/drm/msm/dp/dp_aux.c new file +6 -0
@@ -0,0 +1,549 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
+ */
+
+#define pr_fmt(fmt)	"[drm-dp] %s: " fmt, __func__

There is only one pr_ log in this file, and I think it might be a mistake (see below). If so, yo you drop this.

Quoted 21 lines
drivers/gpu/drm/msm/dp/dp_aux.c new file +21 -0
+
+#include <linux/delay.h>
+
+#include "dp_reg.h"
+#include "dp_aux.h"
+
+#define DP_AUX_ENUM_STR(x)		#x
+
+struct dp_aux_private {
+	struct device *dev;
+	struct dp_aux dp_aux;
+	struct dp_catalog *catalog;
+
+	struct mutex mutex;
+	struct completion comp;
+
+	u32 aux_error_num;
+	u32 retry_cnt;
+	bool cmd_busy;
+	bool native;
+	bool read;

drivers/gpu/drm/msm/dp/dp_aux.c line 37

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_aux.c new file +10 -0
+	bool no_send_addr;
+	bool no_send_stop;
+	u32 offset;
+	u32 segment;
+	u32 isr;
+
+	struct drm_dp_aux drm_aux;
+};
+
+static char *dp_aux_get_error(u32 aux_error)

This should be static const char * const dp_aux_get_error

drivers/gpu/drm/msm/dp/dp_aux.c line 41

Quoted 4 lines
drivers/gpu/drm/msm/dp/dp_aux.c new file +4 -0
+{
+	switch (aux_error) {
+	case DP_AUX_ERR_NONE:
+		return DP_AUX_ENUM_STR(DP_AUX_ERR_NONE);

This is a really fancy way of saying

	return "DP_AUX_ERR_NONE";

You can use the macro if you want, but it just introduces an extra layer of sugar that isn't pleasing to the eye.

Also, my personal preference is to use if/else instead of a switch statement for these sorts of things, but it all ends up the same to the compiler.

Quoted 11 lines
drivers/gpu/drm/msm/dp/dp_aux.c new file +11 -0
+	case DP_AUX_ERR_ADDR:
+		return DP_AUX_ENUM_STR(DP_AUX_ERR_ADDR);
+	case DP_AUX_ERR_TOUT:
+		return DP_AUX_ENUM_STR(DP_AUX_ERR_TOUT);
+	case DP_AUX_ERR_NACK:
+		return DP_AUX_ENUM_STR(DP_AUX_ERR_NACK);
+	case DP_AUX_ERR_DEFER:
+		return DP_AUX_ENUM_STR(DP_AUX_ERR_DEFER);
+	case DP_AUX_ERR_NACK_DEFER:
+		return DP_AUX_ENUM_STR(DP_AUX_ERR_NACK_DEFER);
+	default:

drivers/gpu/drm/msm/dp/dp_aux.c line 62

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_aux.c new file +10 -0
+		return "unknown";
+	}
+}
+
+static u32 dp_aux_write(struct dp_aux_private *aux,
+			struct drm_dp_aux_msg *msg)
+{
+	u32 data[4], reg, len;
+	u8 *msgdata = msg->buffer;
+	int const aux_cmd_fifo_len = 128;

I do not find const variables useful, especially when they are lower case. I would suggest either using a #define or just use the constant directly - it looks like this is only used in one place anyway and the comment specifically states that you want to restrict it to 128 bytes.

drivers/gpu/drm/msm/dp/dp_aux.c line 75

Quoted 13 lines
drivers/gpu/drm/msm/dp/dp_aux.c new file +13 -0
+	int i = 0;
+
+	if (aux->read)
+		len = 4;
+	else
+		len = msg->size + 4;
+
+	/*
+	 * cmd fifo only has depth of 144 bytes
+	 * limit buf length to 128 bytes here
+	 */
+	if (len > aux_cmd_fifo_len) {
+		DRM_ERROR("buf len error\n");

I feel like this could be a more descriptive error, especially since you are purposely limiting the size.

Quoted 37 lines
drivers/gpu/drm/msm/dp/dp_aux.c new file +37 -0
+		return 0;
+	}
+
+	/* Pack cmd and write to HW */
+	data[0] = (msg->address >> 16) & 0xf; /* addr[19:16] */
+	if (aux->read)
+		data[0] |=  BIT(4); /* R/W */
+
+	data[1] = (msg->address >> 8) & 0xff;	/* addr[15:8] */
+	data[2] = msg->address & 0xff;		/* addr[7:0] */
+	data[3] = (msg->size - 1) & 0xff;	/* len[7:0] */
+
+	for (i = 0; i < len; i++) {
+		reg = (i < 4) ? data[i] : msgdata[i - 4];
+		/* index = 0, write */
+		reg = (((reg) << DP_AUX_DATA_OFFSET)
+		       & DP_AUX_DATA_MASK) | DP_AUX_DATA_WRITE;
+		if (i == 0)
+			reg |= DP_AUX_DATA_INDEX_WRITE;
+		aux->catalog->aux_data = reg;
+		dp_catalog_aux_write_data(aux->catalog);
+	}
+
+	dp_catalog_aux_clear_trans(aux->catalog, false);
+
+	reg = 0; /* Transaction number == 1 */
+	if (!aux->native) { /* i2c */
+		reg |= DP_AUX_TRANS_CTRL_I2C;
+
+		if (aux->no_send_addr)
+			reg |= DP_AUX_TRANS_CTRL_NO_SEND_ADDR;
+
+		if (aux->no_send_stop)
+			reg |= DP_AUX_TRANS_CTRL_NO_SEND_STOP;
+	}
+
+	reg |= DP_AUX_TRANS_CTRL_GO;

drivers/gpu/drm/msm/dp/dp_aux.c line 122

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_aux.c new file +10 -0
+	aux->catalog->aux_data = reg;
+	dp_catalog_aux_write_trans(aux->catalog);
+
+	return len;
+}
+
+static int dp_aux_cmd_fifo_tx(struct dp_aux_private *aux,
+			      struct drm_dp_aux_msg *msg)
+{
+	u32 ret = 0, len = 0, timeout;

You don't need to initialize ret or len.

drivers/gpu/drm/msm/dp/dp_aux.c line 123

Quoted 1 line
drivers/gpu/drm/msm/dp/dp_aux.c new file +1 -0
+	int const aux_timeout_ms = HZ/4;

No need for const. Especially for things like timeouts using an actual function call is a saver because you don't need to grep around to figure out what timeout you expect.

Quoted 30 lines
drivers/gpu/drm/msm/dp/dp_aux.c new file +29 -0
+
 
+	reinit_completion(&aux->comp);
+
+	len = dp_aux_write(aux, msg);
+	if (len == 0) {
+		DRM_ERROR("DP AUX write failed\n");
+		return -EINVAL;
+	}
+
+	timeout = wait_for_completion_timeout(&aux->comp, aux_timeout_ms);
+	if (!timeout) {
+		DRM_ERROR("aux %s timeout\n", (aux->read ? "read" : "write"));
+		return -ETIMEDOUT;
+	}
+
+	if (aux->aux_error_num == DP_AUX_ERR_NONE) {
+		ret = len;
+	} else {
+		DRM_ERROR_RATELIMITED("aux err: %s\n",
+			dp_aux_get_error(aux->aux_error_num));
+
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static void dp_aux_cmd_fifo_rx(struct dp_aux_private *aux,
+		struct drm_dp_aux_msg *msg)

drivers/gpu/drm/msm/dp/dp_aux.c line 162

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_aux.c new file +10 -0
+{
+	u32 data;
+	u8 *dp;
+	u32 i, actual_i;
+	u32 len = msg->size;
+
+	dp_catalog_aux_clear_trans(aux->catalog, true);
+
+	data = 0;
+	data |= DP_AUX_DATA_INDEX_WRITE; /* INDEX_WRITE */

You can set data = 0 or you can set

	data = DP_AUX_DATA_INDEX_WRITE;

I prefer the latter.

Quoted 7 lines
drivers/gpu/drm/msm/dp/dp_aux.c new file +7 -0
+	data |= DP_AUX_DATA_READ;  /* read */
+
+	aux->catalog->aux_data = data;
+	dp_catalog_aux_write_data(aux->catalog);
+
+	dp = msg->buffer;
+

drivers/gpu/drm/msm/dp/dp_aux.c line 179

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_aux.c new file +10 -0
+	/* discard first byte */
+	data = dp_catalog_aux_read_data(aux->catalog);
+
+	for (i = 0; i < len; i++) {
+		data = dp_catalog_aux_read_data(aux->catalog);
+		*dp++ = (u8)((data >> DP_AUX_DATA_OFFSET) & 0xff);
+
+		actual_i = (data >> DP_AUX_DATA_INDEX_OFFSET) & 0xFF;
+		if (i != actual_i)
+			pr_warn("Index mismatch: expected %d, found %d\n",

You use DRM errors why nearly everywhere, why switch back?

Quoted 40 lines
drivers/gpu/drm/msm/dp/dp_aux.c new file +40 -0
+				i, actual_i);
+	}
+}
+
+static void dp_aux_native_handler(struct dp_aux_private *aux)
+{
+	u32 isr = aux->isr;
+
+	if (isr & DP_INTR_AUX_I2C_DONE)
+		aux->aux_error_num = DP_AUX_ERR_NONE;
+	else if (isr & DP_INTR_WRONG_ADDR)
+		aux->aux_error_num = DP_AUX_ERR_ADDR;
+	else if (isr & DP_INTR_TIMEOUT)
+		aux->aux_error_num = DP_AUX_ERR_TOUT;
+	if (isr & DP_INTR_NACK_DEFER)
+		aux->aux_error_num = DP_AUX_ERR_NACK;
+
+	complete(&aux->comp);
+}
+
+static void dp_aux_i2c_handler(struct dp_aux_private *aux)
+{
+	u32 isr = aux->isr;
+
+	if (isr & DP_INTR_AUX_I2C_DONE) {
+		if (isr & (DP_INTR_I2C_NACK | DP_INTR_I2C_DEFER))
+			aux->aux_error_num = DP_AUX_ERR_NACK;
+		else
+			aux->aux_error_num = DP_AUX_ERR_NONE;
+	} else {
+		if (isr & DP_INTR_WRONG_ADDR)
+			aux->aux_error_num = DP_AUX_ERR_ADDR;
+		else if (isr & DP_INTR_TIMEOUT)
+			aux->aux_error_num = DP_AUX_ERR_TOUT;
+		if (isr & DP_INTR_NACK_DEFER)
+			aux->aux_error_num = DP_AUX_ERR_NACK_DEFER;
+		if (isr & DP_INTR_I2C_NACK)
+			aux->aux_error_num = DP_AUX_ERR_NACK;
+		if (isr & DP_INTR_I2C_DEFER)
+			aux->aux_error_num = DP_AUX_ERR_DEFER;

drivers/gpu/drm/msm/dp/dp_aux.c line 229

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_aux.c new file +10 -0
+	}
+
+	complete(&aux->comp);
+}
+
+static void dp_aux_update_offset_and_segment(struct dp_aux_private *aux,
+					     struct drm_dp_aux_msg *input_msg)
+{
+	u32 const edid_address = 0x50;
+	u32 const segment_address = 0x30;

Same concern about const - this would be readable if you just used the constants.

drivers/gpu/drm/msm/dp/dp_aux.c line 232

Quoted 3 lines
drivers/gpu/drm/msm/dp/dp_aux.c new file +3 -0
+	bool i2c_read = input_msg->request &
+		(DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
+	u8 *data = NULL;

Data does not need to be initialized. In fact, data doesn't need to exist if you are just reading the first byte of input_msg->buffer.

Quoted 14 lines
drivers/gpu/drm/msm/dp/dp_aux.c new file +14 -0
+
+	if (aux->native || i2c_read || ((input_msg->address != edid_address) &&
+		(input_msg->address != segment_address)))
+		return;
+
+
+	data = input_msg->buffer;
+	if (input_msg->address == segment_address)
+		aux->segment = *data;
+	else
+		aux->offset = *data;
+}
+
+/**

drivers/gpu/drm/msm/dp/dp_aux.c line 256

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_aux.c new file +10 -0
+ * dp_aux_transfer_helper() - helper function for EDID read transactions
+ *
+ * @aux: DP AUX private structure
+ * @input_msg: input message from DRM upstream APIs
+ *
+ * return: void
+ *
+ * This helper function is used to fix EDID reads for non-compliant
+ * sinks that do not handle the i2c middle-of-transaction flag correctly.
+ */

Per the DRM standards, you don't need to use formal kerneldoc comments for static functions, but its up to Rob to decide how he feels about these.

drivers/gpu/drm/msm/dp/dp_aux.c line 262

Quoted 6 lines
drivers/gpu/drm/msm/dp/dp_aux.c new file +6 -0
+static void dp_aux_transfer_helper(struct dp_aux_private *aux,
+				   struct drm_dp_aux_msg *input_msg)
+{
+	struct drm_dp_aux_msg helper_msg;
+	u32 const message_size = 0x10;
+	u32 const segment_address = 0x30;

As throughout, const variables are more of a hindrance than a help.

Quoted 65 lines
drivers/gpu/drm/msm/dp/dp_aux.c new file +65 -0
+	bool i2c_mot = input_msg->request & DP_AUX_I2C_MOT;
+	bool i2c_read = input_msg->request &
+		(DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
+
+	if (!i2c_mot || !i2c_read || (input_msg->size == 0))
+		return;
+
+	aux->read = false;
+	aux->cmd_busy = true;
+	aux->no_send_addr = true;
+	aux->no_send_stop = true;
+
+	/*
+	 * Send the segment address for every i2c read in which the
+	 * middle-of-tranaction flag is set. This is required to support EDID
+	 * reads of more than 2 blocks as the segment address is reset to 0
+	 * since we are overriding the middle-of-transaction flag for read
+	 * transactions.
+	 */
+	memset(&helper_msg, 0, sizeof(helper_msg));
+	helper_msg.address = segment_address;
+	helper_msg.buffer = &aux->segment;
+	helper_msg.size = 1;
+	dp_aux_cmd_fifo_tx(aux, &helper_msg);
+
+	/*
+	 * Send the offset address for every i2c read in which the
+	 * middle-of-transaction flag is set. This will ensure that the sink
+	 * will update its read pointer and return the correct portion of the
+	 * EDID buffer in the subsequent i2c read trasntion triggered in the
+	 * native AUX transfer function.
+	 */
+	memset(&helper_msg, 0, sizeof(helper_msg));
+	helper_msg.address = input_msg->address;
+	helper_msg.buffer = &aux->offset;
+	helper_msg.size = 1;
+	dp_aux_cmd_fifo_tx(aux, &helper_msg);
+	aux->offset += message_size;
+
+	if (aux->offset == 0x80 || aux->offset == 0x100)
+		aux->segment = 0x0; /* reset segment at end of block */
+}
+
+/*
+ * This function does the real job to process an AUX transaction.
+ * It will call aux_reset() function to reset the AUX channel,
+ * if the waiting is timeout.
+ */
+static ssize_t dp_aux_transfer(struct drm_dp_aux *drm_aux,
+			       struct drm_dp_aux_msg *msg)
+{
+	ssize_t ret;
+	int const aux_cmd_native_max = 16;
+	int const aux_cmd_i2c_max = 128;
+	int const retry_count = 5;
+	struct dp_aux_private *aux = container_of(drm_aux,
+		struct dp_aux_private, drm_aux);
+
+	mutex_lock(&aux->mutex);
+
+	aux->native = msg->request & (DP_AUX_NATIVE_WRITE & DP_AUX_NATIVE_READ);
+
+	/* Ignore address only message */
+	if ((msg->size == 0) || (msg->buffer == NULL)) {
+		msg->reply = aux->native ?

drivers/gpu/drm/msm/dp/dp_aux.c line 337

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_aux.c new file +10 -0
+			DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;
+		ret = msg->size;
+		goto unlock_exit;
+	}
+
+	/* msg sanity check */
+	if ((aux->native && (msg->size > aux_cmd_native_max)) ||
+		(msg->size > aux_cmd_i2c_max)) {
+		DRM_ERROR("%s: invalid msg: size(%zu), request(%x)\n",
+			__func__, msg->size, msg->request);

Specifically this is a invalid sized message. I think it would make more sense to print the size and the expected size than to print msg->request and force the developer to do the translation in their head.

Quoted 19 lines
drivers/gpu/drm/msm/dp/dp_aux.c new file +19 -0
+		ret = -EINVAL;
+		goto unlock_exit;
+	}
+
+	dp_aux_update_offset_and_segment(aux, msg);
+	dp_aux_transfer_helper(aux, msg);
+
+	aux->read = msg->request & (DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
+	aux->cmd_busy = true;
+
+	if (aux->read) {
+		aux->no_send_addr = true;
+		aux->no_send_stop = false;
+	} else {
+		aux->no_send_addr = true;
+		aux->no_send_stop = true;
+	}
+
+	ret = dp_aux_cmd_fifo_tx(aux, msg);

drivers/gpu/drm/msm/dp/dp_aux.c line 366

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_aux.c new file +10 -0
+	if ((ret < 0) && aux->native) {
+		aux->retry_cnt++;
+		if (!(aux->retry_cnt % retry_count))
+			dp_catalog_aux_update_cfg(aux->catalog,
+				aux->dp_aux.cfg, PHY_AUX_CFG1);
+		dp_catalog_aux_reset(aux->catalog);
+		goto unlock_exit;
+	} else if (ret < 0) {
+		goto unlock_exit;
+	}

This looks like it could be rewritten as:

if (ret < 0) {
	if (aux->native) {
		aux->retry_cnt++;
		if (!(aux->retry_cnt % retry_count))
			dp_catalog_aux_update_cfg(...);
		dp_catalog_aux_reset(aux->catalog);
	}

	goto unlock_exit;
}
Quoted 16 lines
drivers/gpu/drm/msm/dp/dp_aux.c new file +16 -0
+
+	if (aux->aux_error_num == DP_AUX_ERR_NONE) {
+		if (aux->read)
+			dp_aux_cmd_fifo_rx(aux, msg);
+
+		msg->reply = aux->native ?
+			DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;
+	} else {
+		/* Reply defer to retry */
+		msg->reply = aux->native ?
+			DP_AUX_NATIVE_REPLY_DEFER : DP_AUX_I2C_REPLY_DEFER;
+	}
+
+	/* Return requested size for success or retry */
+	ret = msg->size;
+	aux->retry_cnt = 0;

drivers/gpu/drm/msm/dp/dp_aux.c line 392

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_aux.c new file +10 -0
+
+unlock_exit:
+	aux->cmd_busy = false;
+	mutex_unlock(&aux->mutex);
+	return ret;
+}
+
+static void dp_aux_reset_phy_config_indices(struct dp_aux_cfg *aux_cfg)
+{
+	int i = 0;

You do not need to initialize 'i' here.

Quoted 52 lines
drivers/gpu/drm/msm/dp/dp_aux.c new file +52 -0
+
+	for (i = 0; i < PHY_AUX_CFG_MAX; i++)
+		aux_cfg[i].current_index = 0;
+}
+
+void dp_aux_isr(struct dp_aux *dp_aux)
+{
+	struct dp_aux_private *aux;
+
+	if (!dp_aux) {
+		DRM_ERROR("invalid input\n");
+		return;
+	}
+
+	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
+
+	aux->isr = dp_catalog_aux_get_irq(aux->catalog);
+
+	if (!aux->cmd_busy)
+		return;
+
+	if (aux->native)
+		dp_aux_native_handler(aux);
+	else
+		dp_aux_i2c_handler(aux);
+}
+
+void dp_aux_reconfig(struct dp_aux *dp_aux)
+{
+	struct dp_aux_private *aux;
+
+	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
+
+	dp_catalog_aux_update_cfg(aux->catalog,
+			dp_aux->cfg, PHY_AUX_CFG1);
+	dp_catalog_aux_reset(aux->catalog);
+}
+
+void dp_aux_init(struct dp_aux *dp_aux, struct dp_aux_cfg *aux_cfg)
+{
+	struct dp_aux_private *aux;
+
+	if (!dp_aux || !aux_cfg) {
+		DRM_ERROR("invalid input\n");
+		return;
+	}
+
+	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
+
+	dp_aux_reset_phy_config_indices(aux_cfg);
+	dp_catalog_aux_setup(aux->catalog, aux_cfg);
+	dp_catalog_aux_reset(aux->catalog);

drivers/gpu/drm/msm/dp/dp_aux.c line 454

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_aux.c new file +10 -0
+	dp_catalog_aux_enable(aux->catalog, true);
+	aux->retry_cnt = 0;
+}
+
+void dp_aux_deinit(struct dp_aux *dp_aux)
+{
+	struct dp_aux_private *aux;
+
+	if (!dp_aux) {
+		DRM_ERROR("invalid input\n");

If seems if you get to this point, you don't care if dp_aux is NULL or not. Might not be worth a message.

drivers/gpu/drm/msm/dp/dp_aux.c line 466

Quoted 12 lines
drivers/gpu/drm/msm/dp/dp_aux.c new file +12 -0
+		return;
+	}
+
+	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
+
+	dp_catalog_aux_enable(aux->catalog, false);
+}
+
+int dp_aux_register(struct dp_aux *dp_aux)
+{
+	struct dp_aux_private *aux;
+	int ret = 0;

You do not need to initialize 'ret'.

drivers/gpu/drm/msm/dp/dp_aux.c line 403

Quoted 3 lines
drivers/gpu/drm/msm/dp/dp_aux.c new file +3 -0
+
+	if (!dp_aux) {
+		DRM_ERROR("invalid input\n");

Just return -EINVAL; here

drivers/gpu/drm/msm/dp/dp_aux.c line 482

Quoted 13 lines
drivers/gpu/drm/msm/dp/dp_aux.c new file +13 -0
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
+
+	aux->drm_aux.name = "dpu_dp_aux";
+	aux->drm_aux.dev = aux->dev;
+	aux->drm_aux.transfer = dp_aux_transfer;
+	ret = drm_dp_aux_register(&aux->drm_aux);
+	if (ret) {
+		DRM_ERROR("%s: failed to register drm aux: %d\n", __func__,
+				ret);

return ret; and then you can drop the goto.

drivers/gpu/drm/msm/dp/dp_aux.c line 485

Quoted 3 lines
drivers/gpu/drm/msm/dp/dp_aux.c new file +3 -0
+		goto exit;
+	}
+	dp_aux->drm_aux = &aux->drm_aux;

return 0;

drivers/gpu/drm/msm/dp/dp_aux.c line 495

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_aux.c new file +10 -0
+exit:
+	return ret;
+}
+
+void dp_aux_unregister(struct dp_aux *dp_aux)
+{
+	struct dp_aux_private *aux;
+
+	if (!dp_aux) {
+		DRM_ERROR("invalid input\n");

This definitely doesn't seem worth logging.

Quoted 6 lines
drivers/gpu/drm/msm/dp/dp_aux.c new file +6 -0
+		return;
+	}
+
+	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
+	drm_dp_aux_unregister(&aux->drm_aux);
+}

drivers/gpu/drm/msm/dp/dp_aux.c line 511

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_aux.c new file +10 -0
+
+struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog *catalog,
+			  struct dp_aux_cfg *aux_cfg)
+{
+	int rc = 0;
+	struct dp_aux_private *aux;
+	struct dp_aux *dp_aux;
+
+	if (!catalog || !aux_cfg) {
+		DRM_ERROR("invalid input\n");

return ERR_PTR(-ENODEV);

drivers/gpu/drm/msm/dp/dp_aux.c line 517

Quoted 6 lines
drivers/gpu/drm/msm/dp/dp_aux.c new file +6 -0
+		rc = -ENODEV;
+		goto error;
+	}
+
+	aux = devm_kzalloc(dev, sizeof(*aux), GFP_KERNEL);
+	if (!aux) {

return ERR_PTR(-ENOMEM);

Quoted 7 lines
drivers/gpu/drm/msm/dp/dp_aux.c new file +7 -0
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	init_completion(&aux->comp);
+	aux->cmd_busy = false;
+	mutex_init(&aux->mutex);

drivers/gpu/drm/msm/dp/dp_aux.c line 534

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_aux.c new file +10 -0
+
+	aux->dev = dev;
+	aux->catalog = catalog;
+	aux->dp_aux.cfg = aux_cfg;
+	dp_aux = &aux->dp_aux;
+	aux->retry_cnt = 0;
+
+	return dp_aux;
+error:
+	return ERR_PTR(rc);

And then you can get rid of the label and the error return. And 'rc'.

Quoted 4 lines
drivers/gpu/drm/msm/dp/dp_aux.c new file +4 -0
+}
+
+void dp_aux_put(struct dp_aux *dp_aux)
+{

drivers/gpu/drm/msm/dp/dp_aux.c line 548

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_aux.c new file +10 -0
+	struct dp_aux_private *aux;
+
+	if (!dp_aux)
+		return;
+
+	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
+
+	mutex_destroy(&aux->mutex);
+
+	devm_kfree(aux->dev, aux);

I will have to check where this is called from to be sure but if this is a probed device the resource will be collected for you, so you do not need to free it.

Quoted 16 lines
drivers/gpu/drm/msm/dp/dp_aux.c new file +1 -0
+}
drivers/gpu/drm/msm/dp/dp_aux.h new file +9 -0
new file mode 100644
index 0000000..ca22b5c
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _DP_AUX_H_
+#define _DP_AUX_H_
+
+#include "dp_catalog.h"

drivers/gpu/drm/msm/dp/dp_aux.h line 19

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_aux.h new file +10 -0
+#include <drm/drm_dp_helper.h>
+
+enum dp_aux_error {
+	DP_AUX_ERR_NONE	= 0,
+	DP_AUX_ERR_ADDR	= -1,
+	DP_AUX_ERR_TOUT	= -2,
+	DP_AUX_ERR_NACK	= -3,
+	DP_AUX_ERR_DEFER	= -4,
+	DP_AUX_ERR_NACK_DEFER	= -5,
+};

I have an extremely dim view of enums especially enums that go the wrong direction. If you are doing to define each member of the enum then for my money a #define is a better choice.

Quoted 7 lines
drivers/gpu/drm/msm/dp/dp_aux.h new file +7 -0
+struct dp_aux {
+	struct drm_dp_aux *drm_aux;
+	struct dp_aux_cfg *cfg;
+};
+
+int dp_aux_register(struct dp_aux *aux);
+void dp_aux_unregister(struct dp_aux *aux);

drivers/gpu/drm/msm/dp/dp_aux.h line 35

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_aux.h new file +8 -0
+void dp_aux_isr(struct dp_aux *aux);
+void dp_aux_init(struct dp_aux *aux, struct dp_aux_cfg *aux_cfg);
+void dp_aux_deinit(struct dp_aux *aux);
+void dp_aux_reconfig(struct dp_aux *aux);
 
 
+
+struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog *catalog,
+			  struct dp_aux_cfg *aux_cfg);
+void dp_aux_put(struct dp_aux *aux);

Not sure how Rob feels, but I think some documentation here would be useful.

Quoted 92 lines
drivers/gpu/drm/msm/dp/dp_aux.h new file +2 -0
+
+#endif /*__DP_AUX_H_*/
drivers/gpu/drm/msm/dp/dp_catalog.c new file +84 -0
new file mode 100644
index 0000000..bd1debd
@@ -0,0 +1,1118 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
+ */
+
+#define pr_fmt(fmt)	"[drm-dp] %s: " fmt, __func__
+
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+#include <drm/drm_dp_helper.h>
+
+#include "dp_catalog.h"
+#include "dp_reg.h"
+
+#define POLLING_SLEEP_US			1000
+#define POLLING_TIMEOUT_US			10000
+
+#define REFTIMER_DEFAULT_VALUE			0x20000
+#define SCRAMBLER_RESET_COUNT_VALUE		0xFC
+
+#define DP_INTERRUPT_STATUS_ACK_SHIFT	1
+#define DP_INTERRUPT_STATUS_MASK_SHIFT	2
+
+#define DP_INTERRUPT_STATUS1 \
+	(DP_INTR_AUX_I2C_DONE| \
+	DP_INTR_WRONG_ADDR | DP_INTR_TIMEOUT | \
+	DP_INTR_NACK_DEFER | DP_INTR_WRONG_DATA_CNT | \
+	DP_INTR_I2C_NACK | DP_INTR_I2C_DEFER | \
+	DP_INTR_PLL_UNLOCKED | DP_INTR_AUX_ERROR)
+
+#define DP_INTERRUPT_STATUS1_ACK \
+	(DP_INTERRUPT_STATUS1 << DP_INTERRUPT_STATUS_ACK_SHIFT)
+#define DP_INTERRUPT_STATUS1_MASK \
+	(DP_INTERRUPT_STATUS1 << DP_INTERRUPT_STATUS_MASK_SHIFT)
+
+#define DP_INTERRUPT_STATUS2 \
+	(DP_INTR_READY_FOR_VIDEO | DP_INTR_IDLE_PATTERN_SENT | \
+	DP_INTR_FRAME_END | DP_INTR_CRC_UPDATED)
+
+#define DP_INTERRUPT_STATUS2_ACK \
+	(DP_INTERRUPT_STATUS2 << DP_INTERRUPT_STATUS_ACK_SHIFT)
+#define DP_INTERRUPT_STATUS2_MASK \
+	(DP_INTERRUPT_STATUS2 << DP_INTERRUPT_STATUS_MASK_SHIFT)
+
+static u8 const vm_pre_emphasis[4][4] = {
+	{0x00, 0x0B, 0x12, 0xFF},       /* pe0, 0 db */
+	{0x00, 0x0A, 0x12, 0xFF},       /* pe1, 3.5 db */
+	{0x00, 0x0C, 0xFF, 0xFF},       /* pe2, 6.0 db */
+	{0xFF, 0xFF, 0xFF, 0xFF}        /* pe3, 9.5 db */
+};
+
+/* voltage swing, 0.2v and 1.0v are not support */
+static u8 const vm_voltage_swing[4][4] = {
+	{0x07, 0x0F, 0x14, 0xFF}, /* sw0, 0.4v  */
+	{0x11, 0x1D, 0x1F, 0xFF}, /* sw1, 0.6 v */
+	{0x18, 0x1F, 0xFF, 0xFF}, /* sw1, 0.8 v */
+	{0xFF, 0xFF, 0xFF, 0xFF}  /* sw1, 1.2 v, optional */
+};
+
+static u8 const vm_pre_emphasis_hbr3_hbr2[4][4] = {
+	{0x00, 0x0C, 0x15, 0x1A},
+	{0x02, 0x0E, 0x16, 0xFF},
+	{0x02, 0x11, 0xFF, 0xFF},
+	{0x04, 0xFF, 0xFF, 0xFF}
+};
+
+static u8 const vm_voltage_swing_hbr3_hbr2[4][4] = {
+	{0x02, 0x12, 0x16, 0x1A},
+	{0x09, 0x19, 0x1F, 0xFF},
+	{0x10, 0x1F, 0xFF, 0xFF},
+	{0x1F, 0xFF, 0xFF, 0xFF}
+};
+
+static u8 const vm_pre_emphasis_hbr_rbr[4][4] = {
+	{0x00, 0x0C, 0x14, 0x19},
+	{0x00, 0x0B, 0x12, 0xFF},
+	{0x00, 0x0B, 0xFF, 0xFF},
+	{0x04, 0xFF, 0xFF, 0xFF}
+};
+
+static u8 const vm_voltage_swing_hbr_rbr[4][4] = {
+	{0x08, 0x0F, 0x16, 0x1F},
+	{0x11, 0x1E, 0x1F, 0xFF},
+	{0x19, 0x1F, 0xFF, 0xFF},

drivers/gpu/drm/msm/dp/dp_catalog.c line 94

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +10 -0
+	{0x1F, 0xFF, 0xFF, 0xFF}
+};
+
+struct dp_catalog_private {
+	struct device *dev;
+	struct dp_io *io;
+	struct dp_catalog dp_catalog;
+};
+
+static inline u32 dp_read_aux(struct dp_catalog_private *catalog, u32 offset)

Personally I don't like tagging functions in a C file with inline, I prefer to let the compiler decide.

Quoted 129 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +128 -0
+{
+	return readl_relaxed(catalog->io->dp_aux.base + offset);
+}
+
+static inline void dp_write_aux(struct dp_catalog_private *catalog,
+			       u32 offset, u32 data)
+{
+	/*
+	 * To make sure aux reg writes happens before any other operation,
+	 * this function uses writel() instread of writel_relaxed()
+	 */
+	writel(data, catalog->io->dp_aux.base + offset);
+}
+
+static inline u32 dp_read_ahb(struct dp_catalog_private *catalog, u32 offset)
+{
+	return readl_relaxed(catalog->io->dp_ahb.base + offset);
+}
+
+static inline void dp_write_ahb(struct dp_catalog_private *catalog,
+			       u32 offset, u32 data)
+{
+	/*
+	 * To make sure phy reg writes happens before any other operation,
+	 * this function uses writel() instread of writel_relaxed()
+	 */
+	writel(data, catalog->io->dp_ahb.base + offset);
+}
+
+static inline u32 dp_read_cc(struct dp_catalog_private *catalog, u32 offset)
+{
+	return readl_relaxed(catalog->io->dp_cc_io.base + offset);
+}
+
+static inline void dp_write_phy(struct dp_catalog_private *catalog,
+			       u32 offset, u32 data)
+{
+	/*
+	 * To make sure phy reg writes happens before any other operation,
+	 * this function uses writel() instread of writel_relaxed()
+	 */
+	writel(data, catalog->io->phy_io.base + offset);
+}
+
+static inline u32 dp_read_phy(struct dp_catalog_private *catalog,
+			       u32 offset)
+{
+	/*
+	 * To make sure phy reg writes happens before any other operation,
+	 * this function uses writel() instread of writel_relaxed()
+	 */
+	return readl_relaxed(catalog->io->phy_io.base + offset);
+}
+
+static inline void dp_write_pll(struct dp_catalog_private *catalog,
+			       u32 offset, u32 data)
+{
+	writel_relaxed(data, catalog->io->dp_pll_io.base + offset);
+}
+
+static inline void dp_write_ln_tx0(struct dp_catalog_private *catalog,
+			       u32 offset, u32 data)
+{
+	writel_relaxed(data, catalog->io->ln_tx0_io.base + offset);
+}
+
+static inline void dp_write_ln_tx1(struct dp_catalog_private *catalog,
+			       u32 offset, u32 data)
+{
+	writel_relaxed(data, catalog->io->ln_tx1_io.base + offset);
+}
+
+static inline u32 dp_read_ln_tx0(struct dp_catalog_private *catalog,
+			       u32 offset)
+{
+	return readl_relaxed(catalog->io->ln_tx0_io.base + offset);
+}
+
+static inline u32 dp_read_ln_tx1(struct dp_catalog_private *catalog,
+			       u32 offset)
+{
+	return readl_relaxed(catalog->io->ln_tx1_io.base + offset);
+}
+
+static inline void dp_write_usb_cm(struct dp_catalog_private *catalog,
+			       u32 offset, u32 data)
+{
+	/*
+	 * To make sure usb reg writes happens before any other operation,
+	 * this function uses writel() instread of writel_relaxed()
+	 */
+	writel(data, catalog->io->usb3_dp_com.base + offset);
+}
+
+static inline u32 dp_read_usb_cm(struct dp_catalog_private *catalog,
+			       u32 offset)
+{
+	/*
+	 * To make sure usb reg writes happens before any other operation,
+	 * this function uses writel() instread of writel_relaxed()
+	 */
+	return readl_relaxed(catalog->io->usb3_dp_com.base + offset);
+}
+
+static inline void dp_write_p0(struct dp_catalog_private *catalog,
+			       u32 offset, u32 data)
+{
+	/*
+	 * To make sure interface reg writes happens before any other operation,
+	 * this function uses writel() instread of writel_relaxed()
+	 */
+	writel(data, catalog->io->dp_p0.base + offset);
+}
+
+static inline u32 dp_read_p0(struct dp_catalog_private *catalog,
+			       u32 offset)
+{
+	/*
+	 * To make sure interface reg writes happens before any other operation,
+	 * this function uses writel() instread of writel_relaxed()
+	 */
+	return readl_relaxed(catalog->io->dp_p0.base + offset);
+}
 
+static inline u32 dp_read_link(struct dp_catalog_private *catalog, u32 offset)
+{
+	return readl_relaxed(catalog->io->dp_link.base + offset);
+}
+

drivers/gpu/drm/msm/dp/dp_catalog.c line 233

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +10 -0
+static inline void dp_write_link(struct dp_catalog_private *catalog,
+			       u32 offset, u32 data)
+{
+	/*
+	 * To make sure link reg writes happens before any other operation,
+	 * this function uses writel() instread of writel_relaxed()
+	 */
+	writel(data, catalog->io->dp_link.base + offset);
+}
+

It is hard to tell in an email review, but these are a lot of helper functions for what is essentially a one line operation. You would never save enough lines to make a helper function pay off since you will always be trading one line in the code for a full function. I would suggest going through and determining these helper functions are really worth it.

Quoted 7 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +7 -0
+/* aux related catalog functions */
+u32 dp_catalog_aux_read_data(struct dp_catalog *dp_catalog)
+{
+	struct dp_catalog_private *catalog = container_of(dp_catalog,
+				struct dp_catalog_private, dp_catalog);
+
+	return dp_read_aux(catalog, REG_DP_AUX_DATA);

drivers/gpu/drm/msm/dp/dp_catalog.c line 250

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +10 -0
+}
+
+int dp_catalog_aux_write_data(struct dp_catalog *dp_catalog)
+{
+	int rc = 0;
+	struct dp_catalog_private *catalog = container_of(dp_catalog,
+				struct dp_catalog_private, dp_catalog);
+
+	dp_write_aux(catalog, REG_DP_AUX_DATA, dp_catalog->aux_data);
+	return rc;

rc is not needed. Just return 0.

drivers/gpu/drm/msm/dp/dp_catalog.c line 260

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +10 -0
+}
+
+int dp_catalog_aux_write_trans(struct dp_catalog *dp_catalog)
+{
+	int rc = 0;
+	struct dp_catalog_private *catalog = container_of(dp_catalog,
+				struct dp_catalog_private, dp_catalog);
+
+	dp_write_aux(catalog, REG_DP_AUX_TRANS_CTRL, dp_catalog->aux_data);
+	return rc;

rc is not needed.

Quoted 7 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +7 -0
+}
+
+int dp_catalog_aux_clear_trans(struct dp_catalog *dp_catalog, bool read)
+{
+	int rc = 0;
+	u32 data = 0;
+	struct dp_catalog_private *catalog = container_of(dp_catalog,

drivers/gpu/drm/msm/dp/dp_catalog.c line 277

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +10 -0
+				struct dp_catalog_private, dp_catalog);
+
+	if (read) {
+		data = dp_read_aux(catalog, REG_DP_AUX_TRANS_CTRL);
+		data &= ~DP_AUX_TRANS_CTRL_GO;
+		dp_write_aux(catalog, REG_DP_AUX_TRANS_CTRL, data);
+	} else {
+		dp_write_aux(catalog, REG_DP_AUX_TRANS_CTRL, 0);
+	}
+	return rc;

rc is not needed.

Quoted 54 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +54 -0
+}
+
+void dp_catalog_aux_reset(struct dp_catalog *dp_catalog)
+{
+	u32 aux_ctrl;
+	struct dp_catalog_private *catalog = container_of(dp_catalog,
+				struct dp_catalog_private, dp_catalog);
+
+	aux_ctrl = dp_read_aux(catalog, REG_DP_AUX_CTRL);
+
+	aux_ctrl |= DP_AUX_CTRL_RESET;
+	dp_write_aux(catalog, REG_DP_AUX_CTRL, aux_ctrl);
+	usleep_range(1000, 1100); /* h/w recommended delay */
+
+	aux_ctrl &= ~DP_AUX_CTRL_RESET;
+	dp_write_aux(catalog, REG_DP_AUX_CTRL, aux_ctrl);
+}
+
+void dp_catalog_aux_enable(struct dp_catalog *dp_catalog, bool enable)
+{
+	u32 aux_ctrl;
+	struct dp_catalog_private *catalog = container_of(dp_catalog,
+				struct dp_catalog_private, dp_catalog);
+
+	aux_ctrl = dp_read_aux(catalog, REG_DP_AUX_CTRL);
+
+	if (enable) {
+		dp_write_aux(catalog, REG_DP_TIMEOUT_COUNT, 0xffff);
+		dp_write_aux(catalog, REG_DP_AUX_LIMITS, 0xffff);
+		aux_ctrl |= DP_AUX_CTRL_ENABLE;
+	} else {
+		aux_ctrl &= ~DP_AUX_CTRL_ENABLE;
+	}
+
+	dp_write_aux(catalog, REG_DP_AUX_CTRL, aux_ctrl);
+}
+
+void dp_catalog_aux_update_cfg(struct dp_catalog *dp_catalog,
+		struct dp_aux_cfg *cfg, enum dp_phy_aux_config_type type)
+{
+	struct dp_catalog_private *catalog = container_of(dp_catalog,
+				struct dp_catalog_private, dp_catalog);
+	u32 new_index = 0, current_index = 0;
+
+	if (type >= PHY_AUX_CFG_MAX) {
+		DRM_ERROR("invalid input\n");
+		return;
+	}
+
+	current_index = cfg[type].current_index;
+	new_index = (current_index + 1) % cfg[type].cfg_cnt;
+	DRM_DEBUG_DP("Updating %s from 0x%08x to 0x%08x\n",
+		dp_phy_aux_config_type_to_string(type),
+	cfg[type].lut[current_index], cfg[type].lut[new_index]);

drivers/gpu/drm/msm/dp/dp_catalog.c line 341

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +10 -0
+
+	dp_write_phy(catalog, cfg[type].offset,
+			cfg[type].lut[new_index]);
+	cfg[type].current_index = new_index;
+}
+
+void dp_catalog_dump_regs(struct dp_catalog *dp_catalog)
+{
+	struct dp_catalog_private *catalog = container_of(dp_catalog,
+		struct dp_catalog_private, dp_catalog);

By this point a helper function function to get the catalog private struct would have saved you lines.

drivers/gpu/drm/msm/dp/dp_catalog.c line 352

Quoted 13 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +11 -0
+	int i;
+	u32 len = 0;
+	u32 addr_off = 0;
+	u32 x0, x4, x8, xc;
+
+	pr_info("AHB regs\n");
 
+	len = catalog->io->dp_ahb.len;
+
 
+	if (len % 16)
+		len += 16;
+	len /= 16;

This looks DIV_ROUND_UP. But more fundamentally, if len isn't a multiple of 16 should you be printing extra registers at the end?

Quoted 8 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +8 -0
+	for (i = 0; i < len; i++) {
+		x0 = dp_read_ahb(catalog, addr_off + 0x0);
+		x4 = dp_read_ahb(catalog, addr_off + 0x4);
+		x8 = dp_read_ahb(catalog, addr_off + 0x8);
+		xc = dp_read_ahb(catalog, addr_off + 0xc);
+		pr_info("%08x : %08x %08x %08x %08x\n", addr_off, x0, x4, x8,
+			xc);
+

drivers/gpu/drm/msm/dp/dp_catalog.c line 371

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +10 -0
+		addr_off += 16;
+	}
+
+	pr_info("AUXCLK regs\n");
+	addr_off = 0;
+	len = catalog->io->dp_aux.len;
+
+	if (len % 16)
+		len += 16;
+	len /= 16;

Same as above.

Quoted 11 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +11 -0
+
+	for (i = 0; i < len; i++) {
+		x0 = dp_read_aux(catalog, addr_off + 0x0);
+		x4 = dp_read_aux(catalog, addr_off + 0x4);
+		x8 = dp_read_aux(catalog, addr_off + 0x8);
+		xc = dp_read_aux(catalog, addr_off + 0xc);
+		pr_info("%08x : %08x %08x %08x %08x\n", addr_off, x0, x4, x8,
+			xc);
+
+		addr_off += 16;
+	}

drivers/gpu/drm/msm/dp/dp_catalog.c line 392

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +10 -0
+
+
+	pr_info("LCLK regs\n");
+	len = catalog->io->dp_link.len;
+	addr_off = 0;
+
+	if (len % 16)
+		len += 16;
+	len /= 16;
+

Same as above.

Quoted 8 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +8 -0
+	for (i = 0; i < len; i++) {
+		x0 = dp_read_link(catalog, addr_off + 0x0);
+		x4 = dp_read_link(catalog, addr_off + 0x4);
+		x8 = dp_read_link(catalog, addr_off + 0x8);
+		xc = dp_read_link(catalog, addr_off + 0xc);
+		pr_info("%08x : %08x %08x %08x %08x\n", addr_off, x0, x4, x8,
+			xc);
+

drivers/gpu/drm/msm/dp/dp_catalog.c line 410

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +10 -0
+		addr_off += 16;
+	}
+
+	pr_info("P0CLK regs\n");
+	len = catalog->io->dp_p0.len;
+	addr_off = 0;
+
+	if (len % 16)
+		len += 16;
+	len /= 16;

Same as above.

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +10 -0
+
+	for (i = 0; i < len; i++) {
+		x0 = dp_read_p0(catalog, addr_off + 0x0);
+		x4 = dp_read_p0(catalog, addr_off + 0x4);
+		x8 = dp_read_p0(catalog, addr_off + 0x8);
+		xc = dp_read_p0(catalog, addr_off + 0xc);
+		pr_info("%08x : %08x %08x %08x %08x\n", addr_off, x0, x4, x8,
+			xc);
+
+		addr_off += 16;

drivers/gpu/drm/msm/dp/dp_catalog.c line 430

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +10 -0
+	}
+
+	pr_info("USB3 DP COM regs\n");
+	len = catalog->io->usb3_dp_com.len;
+	addr_off = 0;
+
+	if (len % 16)
+		len += 16;
+	len /= 16;
+

Same

Quoted 8 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +8 -0
+	for (i = 0; i < len; i++) {
+		x0 = dp_read_usb_cm(catalog, addr_off + 0x0);
+		x4 = dp_read_usb_cm(catalog, addr_off + 0x4);
+		x8 = dp_read_usb_cm(catalog, addr_off + 0x8);
+		xc = dp_read_usb_cm(catalog, addr_off + 0xc);
+		pr_info("%08x : %08x %08x %08x %08x\n", addr_off, x0, x4, x8,
+			xc);
+

drivers/gpu/drm/msm/dp/dp_catalog.c line 448

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +10 -0
+		addr_off += 16;
+	}
+
+	pr_info("LN TX0 regs\n");
+	len = catalog->io->ln_tx0_io.len;
+	addr_off = 0;
+
+	if (len % 16)
+		len += 16;
+	len /= 16;

Same

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +10 -0
+
+	for (i = 0; i < len; i++) {
+		x0 = dp_read_ln_tx0(catalog, addr_off + 0x0);
+		x4 = dp_read_ln_tx0(catalog, addr_off + 0x4);
+		x8 = dp_read_ln_tx0(catalog, addr_off + 0x8);
+		xc = dp_read_ln_tx0(catalog, addr_off + 0xc);
+		pr_info("%08x : %08x %08x %08x %08x\n", addr_off, x0, x4, x8,
+			xc);
+
+		addr_off += 16;

drivers/gpu/drm/msm/dp/dp_catalog.c line 468

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +10 -0
+	}
+
+	pr_info("LN TX1 regs\n");
+	len = catalog->io->ln_tx1_io.len;
+	addr_off = 0;
+
+	if (len % 16)
+		len += 16;
+	len /= 16;
+

Same

Quoted 9 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +9 -0
+	for (i = 0; i < len; i++) {
+		x0 = dp_read_ln_tx1(catalog, addr_off + 0x0);
+		x4 = dp_read_ln_tx1(catalog, addr_off + 0x4);
+		x8 = dp_read_ln_tx1(catalog, addr_off + 0x8);
+		xc = dp_read_ln_tx1(catalog, addr_off + 0xc);
+		pr_info("%08x : %08x %08x %08x %08x\n", addr_off, x0, x4, x8,
+			xc);
+
+		addr_off += 16;

drivers/gpu/drm/msm/dp/dp_catalog.c line 487

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +10 -0
+	}
+
+	pr_info("DP PHY regs\n");
+	len = catalog->io->phy_io.len;
+	addr_off = 0;
+
+	if (len % 16)
+		len += 16;
+	len /= 16;
+

Same

drivers/gpu/drm/msm/dp/dp_catalog.c line 497

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +10 -0
+	for (i = 0; i < len; i++) {
+		x0 = dp_read_phy(catalog, addr_off + 0x0);
+		x4 = dp_read_phy(catalog, addr_off + 0x4);
+		x8 = dp_read_phy(catalog, addr_off + 0x8);
+		xc = dp_read_phy(catalog, addr_off + 0xc);
+		pr_info("%08x : %08x %08x %08x %08x\n", addr_off, x0, x4, x8,
+			xc);
+
+		addr_off += 16;
+	}

There feels like a ton of code repetition here, especially since most of the read helper functions can be broken down into single lines. I feel like this whole function could be greatly compressed - say for example:

void dump_regs(void * __iomem *base, int len)

{
    int i;

    for(i = 0; i < len; i += 16) {
    	x0 = readl_relaxed(base);
    	x4 = readl_relaxed(base + 0x04);
    	x8 = readl_relaxed(base + 0x08);
    	xc = readl_relaxed(base + 0x0c);

	pr_info(%08x: %08x %08x %08x %08x, base, x0, x4, x7, xc);
	base += 16;

}
}

And then as an example the last block would turn into:

dump_regs(catalog->pcatalog->io->phy_io.base, catalog->io->phy_io.len);

Quoted 166 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +166 -0
+}
+
+void dp_catalog_aux_setup(struct dp_catalog *dp_catalog,
+		struct dp_aux_cfg *cfg)
+{
+	struct dp_catalog_private *catalog = container_of(dp_catalog,
+				struct dp_catalog_private, dp_catalog);
+	int i = 0;
+
+	dp_write_phy(catalog, REG_DP_PHY_PD_CTL, DP_PHY_PD_CTL_PWRDN |
+		DP_PHY_PD_CTL_AUX_PWRDN | DP_PHY_PD_CTL_PLL_PWRDN |
+		DP_PHY_PD_CTL_DP_CLAMP_EN);
+
+	/* Turn on BIAS current for PHY/PLL */
+	dp_write_pll(catalog,
+		QSERDES_COM_BIAS_EN_CLKBUFLR_EN, QSERDES_COM_BIAS_EN |
+		QSERDES_COM_BIAS_EN_MUX | QSERDES_COM_CLKBUF_L_EN |
+		QSERDES_COM_EN_SYSCLK_TX_SEL);
+
+	dp_write_phy(catalog, REG_DP_PHY_PD_CTL, DP_PHY_PD_CTL_PSR_PWRDN);
+
+	/* Make sure that hardware is done with  PSR power down */
+	wmb();
+	dp_write_phy(catalog, REG_DP_PHY_PD_CTL, DP_PHY_PD_CTL_PWRDN |
+		DP_PHY_PD_CTL_AUX_PWRDN | DP_PHY_PD_CTL_LANE_0_1_PWRDN
+		| DP_PHY_PD_CTL_LANE_2_3_PWRDN | DP_PHY_PD_CTL_PLL_PWRDN
+		| DP_PHY_PD_CTL_DP_CLAMP_EN);
+
+	dp_write_pll(catalog,
+		QSERDES_COM_BIAS_EN_CLKBUFLR_EN, QSERDES_COM_BIAS_EN |
+		QSERDES_COM_BIAS_EN_MUX | QSERDES_COM_CLKBUF_R_EN |
+		QSERDES_COM_CLKBUF_L_EN | QSERDES_COM_EN_SYSCLK_TX_SEL |
+		QSERDES_COM_CLKBUF_RX_DRIVE_L);
+
+	/* DP AUX CFG register programming */
+	for (i = 0; i < PHY_AUX_CFG_MAX; i++) {
+		DRM_DEBUG_DP("%s: offset=0x%08x, value=0x%08x\n",
+			dp_phy_aux_config_type_to_string(i),
+			cfg[i].offset, cfg[i].lut[cfg[i].current_index]);
+		dp_write_phy(catalog, cfg[i].offset,
+			cfg[i].lut[cfg[i].current_index]);
+	}
+
+	dp_write_phy(catalog, REG_DP_PHY_AUX_INTERRUPT_MASK,
+			PHY_AUX_STOP_ERR_MASK |	PHY_AUX_DEC_ERR_MASK |
+			PHY_AUX_SYNC_ERR_MASK |	PHY_AUX_ALIGN_ERR_MASK |
+			PHY_AUX_REQ_ERR_MASK);
+}
+
+int dp_catalog_aux_get_irq(struct dp_catalog *dp_catalog)
+{
+	struct dp_catalog_private *catalog = container_of(dp_catalog,
+				struct dp_catalog_private, dp_catalog);
+	u32 intr, intr_ack;
+
+	intr = dp_read_ahb(catalog, REG_DP_INTR_STATUS);
+	intr &= ~DP_INTERRUPT_STATUS1_MASK;
+	intr_ack = (intr & DP_INTERRUPT_STATUS1)
+			<< DP_INTERRUPT_STATUS_ACK_SHIFT;
+	dp_write_ahb(catalog, REG_DP_INTR_STATUS, intr_ack |
+			DP_INTERRUPT_STATUS1_MASK);
+
+	return intr;
+
+}
+
+/* controller related catalog functions */
+void dp_catalog_ctrl_update_transfer_unit(struct dp_catalog *dp_catalog,
+				u32 dp_tu, u32 valid_boundary,
+				u32 valid_boundary2)
+{
+	struct dp_catalog_private *catalog = container_of(dp_catalog,
+				struct dp_catalog_private, dp_catalog);
+
+	dp_write_link(catalog, REG_DP_VALID_BOUNDARY, valid_boundary);
+	dp_write_link(catalog, REG_DP_TU, dp_tu);
+	dp_write_link(catalog, REG_DP_VALID_BOUNDARY_2, valid_boundary2);
+}
+
+void dp_catalog_ctrl_state_ctrl(struct dp_catalog *dp_catalog, u32 state)
+{
+	struct dp_catalog_private *catalog = container_of(dp_catalog,
+				struct dp_catalog_private, dp_catalog);
+
+	dp_write_link(catalog, REG_DP_STATE_CTRL, state);
+}
+
+void dp_catalog_ctrl_config_ctrl(struct dp_catalog *dp_catalog, u32 cfg)
+{
+	struct dp_catalog_private *catalog = container_of(dp_catalog,
+				struct dp_catalog_private, dp_catalog);
+
+	DRM_DEBUG_DP("DP_CONFIGURATION_CTRL=0x%x\n", cfg);
+
+	dp_write_link(catalog, REG_DP_CONFIGURATION_CTRL, cfg);
+}
+
+void dp_catalog_ctrl_lane_mapping(struct dp_catalog *dp_catalog)
+{
+	struct dp_catalog_private *catalog = container_of(dp_catalog,
+				struct dp_catalog_private, dp_catalog);
+	u32 ln_0 = 0, ln_1 = 1, ln_2 = 2, ln_3 = 3; /* One-to-One mapping */
+	u32 ln_mapping;
+
+	ln_mapping = ln_0 << LANE0_MAPPING_SHIFT;
+	ln_mapping |= ln_1 << LANE1_MAPPING_SHIFT;
+	ln_mapping |= ln_2 << LANE2_MAPPING_SHIFT;
+	ln_mapping |= ln_3 << LANE3_MAPPING_SHIFT;
+
+	dp_write_link(catalog, REG_DP_LOGICAL2PHYSICAL_LANE_MAPPING,
+			ln_mapping);
+}
+
+void dp_catalog_ctrl_mainlink_ctrl(struct dp_catalog *dp_catalog,
+						bool enable)
+{
+	u32 mainlink_ctrl;
+	struct dp_catalog_private *catalog = container_of(dp_catalog,
+				struct dp_catalog_private, dp_catalog);
+
+	if (enable) {
+		/*
+		 * To make sure link reg writes happens before other operation,
+		 * dp_write_link() function uses writel()
+		 */
+		dp_write_link(catalog, REG_DP_MAINLINK_CTRL,
+				DP_MAINLINK_FB_BOUNDARY_SEL);
+		dp_write_link(catalog, REG_DP_MAINLINK_CTRL,
+					DP_MAINLINK_FB_BOUNDARY_SEL |
+					DP_MAINLINK_CTRL_RESET);
+		dp_write_link(catalog, REG_DP_MAINLINK_CTRL,
+					DP_MAINLINK_FB_BOUNDARY_SEL);
+		dp_write_link(catalog, REG_DP_MAINLINK_CTRL,
+					DP_MAINLINK_FB_BOUNDARY_SEL |
+					DP_MAINLINK_CTRL_ENABLE);
+	} else {
+		mainlink_ctrl = dp_read_link(catalog, REG_DP_MAINLINK_CTRL);
+		mainlink_ctrl &= ~DP_MAINLINK_CTRL_ENABLE;
+		dp_write_link(catalog, REG_DP_MAINLINK_CTRL, mainlink_ctrl);
+	}
+}
+
+void dp_catalog_ctrl_config_misc(struct dp_catalog *dp_catalog,
+					u32 colorimetry_cfg,
+					u32 test_bits_depth)
+{
+	u32 misc_val;
+	struct dp_catalog_private *catalog = container_of(dp_catalog,
+				struct dp_catalog_private, dp_catalog);
+
+	misc_val = dp_read_link(catalog, REG_DP_MISC1_MISC0);
+	misc_val |= colorimetry_cfg << DP_MISC0_COLORIMETRY_CFG_SHIFT;
+	misc_val |= test_bits_depth << DP_MISC0_TEST_BITS_DEPTH_SHIFT;
+	/* Configure clock to synchronous mode */
+	misc_val |= DP_MISC0_SYNCHRONOUS_CLK;
+
+	DRM_DEBUG_DP("misc settings = 0x%x\n", misc_val);
+	dp_write_link(catalog, REG_DP_MISC1_MISC0, misc_val);
+}
+
+void dp_catalog_ctrl_config_msa(struct dp_catalog *dp_catalog,
+					u32 rate, u32 stream_rate_khz,
+					bool fixed_nvid)
+{
+	u32 pixel_m, pixel_n;
+	u32 mvid, nvid;

drivers/gpu/drm/msm/dp/dp_catalog.c line 673

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +10 -0
+	u32 const nvid_fixed = DP_LINK_CONSTANT_N_VALUE;
+	u32 const link_rate_hbr2 = 540000;
+	u32 const link_rate_hbr3 = 810000;
+
+	struct dp_catalog_private *catalog = container_of(dp_catalog,
+				struct dp_catalog_private, dp_catalog);
+
+	pixel_m = dp_read_cc(catalog, MMSS_DP_PIXEL_M);
+	pixel_n = dp_read_cc(catalog, MMSS_DP_PIXEL_N);
+	pr_debug("pixel_m=0x%x, pixel_n=0x%x\n", pixel_m, pixel_n);

You use DRM_DEBUG_DP a lot - you probably don't want to mix debugging methods.

Quoted 79 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +79 -0
+
+	mvid = (pixel_m & 0xFFFF) * 5;
+	nvid = (0xFFFF & (~pixel_n)) + (pixel_m & 0xFFFF);
+
+	if (nvid < nvid_fixed) {
+		u32 temp;
+
+		temp = (nvid_fixed / nvid) * nvid;
+		mvid = (nvid_fixed / nvid) * mvid;
+		nvid = temp;
+	}
+
+	if (link_rate_hbr2 == rate)
+		nvid *= 2;
+
+	if (link_rate_hbr3 == rate)
+		nvid *= 3;
+
+	DRM_DEBUG_DP("mvid=0x%x, nvid=0x%x\n", mvid, nvid);
+	dp_write_link(catalog, REG_DP_SOFTWARE_MVID, mvid);
+	dp_write_link(catalog, REG_DP_SOFTWARE_NVID, nvid);
+	dp_write_p0(catalog, MMSS_DP_DSC_DTO, 0x0);
+}
+
+
+int dp_catalog_ctrl_set_pattern(struct dp_catalog *dp_catalog,
+					u32 pattern)
+{
+	int bit, ret;
+	u32 data;
+	struct dp_catalog_private *catalog = container_of(dp_catalog,
+				struct dp_catalog_private, dp_catalog);
+
+	bit = BIT(pattern - 1);
+	DRM_DEBUG_DP("hw: bit=%d train=%d\n", bit, pattern);
+	dp_write_link(catalog, REG_DP_STATE_CTRL, bit);
+
+	bit = BIT(pattern - 1) << DP_MAINLINK_READY_LINK_TRAINING_SHIFT;
+
+	/* Poll for mainlink ready status */
+	ret = readx_poll_timeout(readl, catalog->io->dp_link.base +
+					REG_DP_MAINLINK_READY,
+					data, data & bit,
+					POLLING_SLEEP_US, POLLING_TIMEOUT_US);
+	if (ret < 0) {
+		DRM_ERROR("set pattern for link_train=%d failed\n", pattern);
+		return ret;
+	}
+	return 0;
+}
+
+void dp_catalog_ctrl_usb_reset(struct dp_catalog *dp_catalog, bool flip)
+{
+	struct dp_catalog_private *catalog = container_of(dp_catalog,
+				struct dp_catalog_private, dp_catalog);
+	u32 typec_ctrl;
+
+	dp_write_usb_cm(catalog, REG_USB3_DP_COM_RESET_OVRD_CTRL,
+			USB3_DP_COM_OVRD_CTRL_SW_DPPHY_RESET_MUX |
+			USB3_DP_COM_OVRD_CTRL_SW_USB3PHY_RESET_MUX);
+	dp_write_usb_cm(catalog, REG_USB3_DP_COM_PHY_MODE_CTRL,
+						USB3_DP_COM_PHY_MODE_DP);
+	dp_write_usb_cm(catalog, REG_USB3_DP_COM_SW_RESET,
+						USB3_DP_COM_SW_RESET_SET);
+
+	/* Default configuration i.e CC1 */
+	typec_ctrl = USB3_DP_COM_TYPEC_CTRL_PORTSEL_MUX;
+	if (flip)
+		typec_ctrl |= USB3_DP_COM_TYPEC_CTRL_PORTSEL;
+
+	dp_write_usb_cm(catalog, REG_USB3_DP_COM_TYPEC_CTRL, typec_ctrl);
+
+	dp_write_usb_cm(catalog, REG_USB3_DP_COM_SWI_CTRL, 0x00);
+	dp_write_usb_cm(catalog, REG_USB3_DP_COM_SW_RESET, 0x00);
+
+	dp_write_usb_cm(catalog, REG_USB3_DP_COM_POWER_DOWN_CTRL,
+					USB3_DP_COM_POWER_DOWN_CTRL_SW_PWRDN);
+	dp_write_usb_cm(catalog, REG_USB3_DP_COM_RESET_OVRD_CTRL, 0x00);
+

drivers/gpu/drm/msm/dp/dp_catalog.c line 762

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +10 -0
+}
+
+void dp_catalog_ctrl_reset(struct dp_catalog *dp_catalog)
+{
+	u32 sw_reset;
+	struct dp_catalog_private *catalog = container_of(dp_catalog,
+				struct dp_catalog_private, dp_catalog);
+	void __iomem *base;
+
+	base = catalog->io->dp_ahb.base;

base appears to be unused?

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +10 -0
+
+	sw_reset = dp_read_ahb(catalog, REG_DP_SW_RESET);
+
+	sw_reset |= DP_SW_RESET;
+	dp_write_ahb(catalog, REG_DP_SW_RESET, sw_reset);
+	usleep_range(1000, 1100); /* h/w recommended delay */
+
+	sw_reset &= ~DP_SW_RESET;
+	dp_write_ahb(catalog, REG_DP_SW_RESET, sw_reset);
+}

drivers/gpu/drm/msm/dp/dp_catalog.c line 782

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +10 -0
+
+bool dp_catalog_ctrl_mainlink_ready(struct dp_catalog *dp_catalog)
+{
+	u32 data;
+	int ret;
+	struct dp_catalog_private *catalog = container_of(dp_catalog,
+				struct dp_catalog_private, dp_catalog);
+
+	/* Poll for mainlink ready status */
+	ret = readx_poll_timeout(readl,

You shouldn't use readx_poll_timeout directly - use readl_poll_timeout instead.

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +10 -0
+				catalog->io->dp_link.base +
+				REG_DP_MAINLINK_READY,
+				data, data & DP_MAINLINK_READY_FOR_VIDEO,
+				POLLING_SLEEP_US, POLLING_TIMEOUT_US);
+	if (ret < 0) {
+		DRM_ERROR("mainlink not ready\n");
+		return false;
+	}
+
+	return true;

drivers/gpu/drm/msm/dp/dp_catalog.c line 802

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +10 -0
+}
+
+void dp_catalog_ctrl_enable_irq(struct dp_catalog *dp_catalog,
+						bool enable)
+{
+	struct dp_catalog_private *catalog = container_of(dp_catalog,
+				struct dp_catalog_private, dp_catalog);
+	void __iomem *base;
+
+	base = catalog->io->dp_ahb.base;

Base again appeasr to be unused.

Quoted 274 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +274 -0
+
+	if (enable) {
+		dp_write_ahb(catalog, REG_DP_INTR_STATUS,
+				DP_INTERRUPT_STATUS1_MASK);
+		dp_write_ahb(catalog, REG_DP_INTR_STATUS2,
+				DP_INTERRUPT_STATUS2_MASK);
+	} else {
+		dp_write_ahb(catalog, REG_DP_INTR_STATUS, 0x00);
+		dp_write_ahb(catalog, REG_DP_INTR_STATUS2, 0x00);
+	}
+}
+
+void dp_catalog_ctrl_hpd_config(struct dp_catalog *dp_catalog, bool en)
+{
+	struct dp_catalog_private *catalog = container_of(dp_catalog,
+				struct dp_catalog_private, dp_catalog);
+
+	if (en) {
+		u32 reftimer = dp_read_aux(catalog, REG_DP_DP_HPD_REFTIMER);
+
+		dp_write_aux(catalog, REG_DP_DP_HPD_INT_ACK,
+				DP_DP_HPD_PLUG_INT_ACK |
+				DP_DP_IRQ_HPD_INT_ACK |
+				DP_DP_HPD_REPLUG_INT_ACK |
+				DP_DP_HPD_UNPLUG_INT_ACK);
+		dp_write_aux(catalog, REG_DP_DP_HPD_INT_MASK,
+				DP_DP_HPD_PLUG_INT_MASK |
+				DP_DP_IRQ_HPD_INT_MASK |
+				DP_DP_HPD_REPLUG_INT_MASK |
+				DP_DP_HPD_UNPLUG_INT_MASK);
+
+		/* Configure REFTIMER */
+		reftimer |= REFTIMER_DEFAULT_VALUE;
+		dp_write_aux(catalog, REG_DP_DP_HPD_REFTIMER, reftimer);
+		/* Enable HPD */
+		dp_write_aux(catalog, REG_DP_DP_HPD_CTRL,
+				DP_DP_HPD_CTRL_HPD_EN);
+	} else {
+		/* Disable HPD */
+		dp_write_aux(catalog, REG_DP_DP_HPD_CTRL, 0x0);
+	}
+}
+
+int dp_catalog_ctrl_get_interrupt(struct dp_catalog *dp_catalog)
+{
+	struct dp_catalog_private *catalog = container_of(dp_catalog,
+				struct dp_catalog_private, dp_catalog);
+	u32 intr, intr_ack;
+
+	intr = dp_read_ahb(catalog, REG_DP_INTR_STATUS2);
+	intr &= ~DP_INTERRUPT_STATUS2_MASK;
+	intr_ack = (intr & DP_INTERRUPT_STATUS2)
+			<< DP_INTERRUPT_STATUS_ACK_SHIFT;
+	dp_write_ahb(catalog, REG_DP_INTR_STATUS2,
+			intr_ack | DP_INTERRUPT_STATUS2_MASK);
+
+	return intr;
+}
+
+void dp_catalog_ctrl_phy_reset(struct dp_catalog *dp_catalog)
+{
+	struct dp_catalog_private *catalog = container_of(dp_catalog,
+				struct dp_catalog_private, dp_catalog);
+
+	dp_write_ahb(catalog, REG_DP_PHY_CTRL,
+			DP_PHY_CTRL_SW_RESET_PLL | DP_PHY_CTRL_SW_RESET);
+	usleep_range(1000, 1100); /* h/w recommended delay */
+	dp_write_ahb(catalog, REG_DP_PHY_CTRL, 0x0);
+}
+
+void dp_catalog_ctrl_phy_lane_cfg(struct dp_catalog *dp_catalog,
+		bool flipped, u8 ln_cnt)
+{
+	u32 info;
+	struct dp_catalog_private *catalog = container_of(dp_catalog,
+				struct dp_catalog_private, dp_catalog);
+	u8 orientation = BIT(!!flipped);
+
+	info = ln_cnt & DP_PHY_SPARE0_MASK;
+	info |= (orientation & DP_PHY_SPARE0_MASK)
+			<< DP_PHY_SPARE0_ORIENTATION_INFO_SHIFT;
+	DRM_DEBUG_DP("Shared Info = 0x%x\n", info);
+
+	dp_write_phy(catalog, REG_DP_PHY_SPARE0, info);
+}
+
+int dp_catalog_ctrl_update_vx_px(struct dp_catalog *dp_catalog,
+		u8 v_level, u8 p_level)
+{
+	struct dp_catalog_private *catalog = container_of(dp_catalog,
+				struct dp_catalog_private, dp_catalog);
+	u8 voltage_swing_cfg, pre_emphasis_cfg;
+
+	DRM_DEBUG_DP("hw: v=%d p=%d\n", v_level, p_level);
+
+	voltage_swing_cfg = vm_voltage_swing_hbr_rbr[v_level][p_level];
+	pre_emphasis_cfg = vm_pre_emphasis_hbr_rbr[v_level][p_level];
+
+	if (voltage_swing_cfg == 0xFF && pre_emphasis_cfg == 0xFF) {
+		DRM_ERROR("invalid vx (0x%x=0x%x), px (0x%x=0x%x\n",
+			v_level, voltage_swing_cfg, p_level, pre_emphasis_cfg);
+		return -EINVAL;
+	}
+
+	/* Enable MUX to use Cursor values from these registers */
+	voltage_swing_cfg |= DP_PHY_TXn_TX_DRV_LVL_MUX_EN;
+	pre_emphasis_cfg |= DP_PHY_TXn_TX_EMP_POST1_LVL_MUX_EN;
+
+	/* Configure host and panel only if both values are allowed */
+	dp_write_ln_tx0(catalog, REG_DP_PHY_TXn_TX_DRV_LVL, voltage_swing_cfg);
+	dp_write_ln_tx1(catalog, REG_DP_PHY_TXn_TX_DRV_LVL, voltage_swing_cfg);
+	dp_write_ln_tx0(catalog, REG_DP_PHY_TXn_TX_EMP_POST1_LVL,
+					pre_emphasis_cfg);
+	dp_write_ln_tx1(catalog, REG_DP_PHY_TXn_TX_EMP_POST1_LVL,
+					pre_emphasis_cfg);
+	DRM_DEBUG_DP("hw: vx_value=0x%x px_value=0x%x\n",
+			voltage_swing_cfg, pre_emphasis_cfg);
+
+	return 0;
+}
+
+void dp_catalog_ctrl_send_phy_pattern(struct dp_catalog *dp_catalog,
+			u32 pattern)
+{
+	struct dp_catalog_private *catalog = container_of(dp_catalog,
+				struct dp_catalog_private, dp_catalog);
+	u32 value = 0x0;
+
+	/* Make sure to clear the current pattern before starting a new one */
+	dp_write_link(catalog, REG_DP_STATE_CTRL, 0x0);
+
+	switch (pattern) {
+	case DP_LINK_QUAL_PATTERN_D10_2:
+		dp_write_link(catalog, REG_DP_STATE_CTRL,
+				DP_STATE_CTRL_LINK_TRAINING_PATTERN1);
+		return;
+	case DP_LINK_QUAL_PATTERN_PRBS7:
+		dp_write_link(catalog, REG_DP_STATE_CTRL,
+				DP_STATE_CTRL_LINK_PRBS7);
+		return;
+	case DP_LINK_QUAL_PATTERN_80BIT_CUSTOM:
+		dp_write_link(catalog, REG_DP_STATE_CTRL,
+				DP_STATE_CTRL_LINK_TEST_CUSTOM_PATTERN);
+		/* 00111110000011111000001111100000 */
+		dp_write_link(catalog, REG_DP_TEST_80BIT_CUSTOM_PATTERN_REG0,
+				0x3E0F83E0);
+		/* 00001111100000111110000011111000 */
+		dp_write_link(catalog, REG_DP_TEST_80BIT_CUSTOM_PATTERN_REG1,
+				0x0F83E0F8);
+		/* 1111100000111110 */
+		dp_write_link(catalog, REG_DP_TEST_80BIT_CUSTOM_PATTERN_REG2,
+				0x0000F83E);
+		return;
+	case DP_LINK_QUAL_PATTERN_HBR2_EYE:
+	case DP_LINK_QUAL_PATTERN_ERROR_RATE:
+		value &= ~DP_HBR2_ERM_PATTERN;
+		if (pattern == DP_LINK_QUAL_PATTERN_HBR2_EYE)
+			value = DP_HBR2_ERM_PATTERN;
+		dp_write_link(catalog, REG_DP_HBR2_COMPLIANCE_SCRAMBLER_RESET,
+					value);
+		value |= SCRAMBLER_RESET_COUNT_VALUE;
+		dp_write_link(catalog, REG_DP_HBR2_COMPLIANCE_SCRAMBLER_RESET,
+					value);
+		dp_write_link(catalog, REG_DP_MAINLINK_LEVELS,
+					DP_MAINLINK_SAFE_TO_EXIT_LEVEL_2);
+		dp_write_link(catalog, REG_DP_STATE_CTRL,
+					DP_STATE_CTRL_LINK_SYMBOL_ERR_MEASURE);
+		return;
+	default:
+		DRM_DEBUG_DP("No valid test pattern requested:0x%x\n", pattern);
+		return;
+	}
+}
+
+u32 dp_catalog_ctrl_read_phy_pattern(struct dp_catalog *dp_catalog)
+{
+	struct dp_catalog_private *catalog = container_of(dp_catalog,
+				struct dp_catalog_private, dp_catalog);
+
+	return dp_read_link(catalog, REG_DP_MAINLINK_READY);
+}
+
+/* panel related catalog functions */
+int dp_catalog_panel_timing_cfg(struct dp_catalog *dp_catalog)
+{
+	struct dp_catalog_private *catalog = container_of(dp_catalog,
+				struct dp_catalog_private, dp_catalog);
+
+	dp_write_link(catalog, REG_DP_TOTAL_HOR_VER,
+				dp_catalog->total);
+	dp_write_link(catalog, REG_DP_START_HOR_VER_FROM_SYNC,
+				dp_catalog->sync_start);
+	dp_write_link(catalog, REG_DP_HSYNC_VSYNC_WIDTH_POLARITY,
+				dp_catalog->width_blanking);
+	dp_write_link(catalog, REG_DP_ACTIVE_HOR_VER, dp_catalog->dp_active);
+	return 0;
+}
+
+void dp_catalog_panel_tpg_enable(struct dp_catalog *dp_catalog,
+				struct drm_display_mode *drm_mode)
+{
+	struct dp_catalog_private *catalog = container_of(dp_catalog,
+				struct dp_catalog_private, dp_catalog);
+	u32 hsync_period, vsync_period;
+	u32 display_v_start, display_v_end;
+	u32 hsync_start_x, hsync_end_x;
+	u32 v_sync_width;
+	u32 hsync_ctl;
+	u32 display_hctl;
+
+	/* TPG config parameters*/
+	hsync_period = drm_mode->htotal;
+	vsync_period = drm_mode->vtotal;
+
+	display_v_start = ((drm_mode->vtotal - drm_mode->vsync_start) *
+					hsync_period);
+	display_v_end = ((vsync_period - (drm_mode->vsync_start -
+					drm_mode->vdisplay))
+					* hsync_period) - 1;
+
+	display_v_start += drm_mode->htotal - drm_mode->hsync_start;
+	display_v_end -= (drm_mode->hsync_start - drm_mode->hdisplay);
+
+	hsync_start_x = drm_mode->htotal - drm_mode->hsync_start;
+	hsync_end_x = hsync_period - (drm_mode->hsync_start -
+					drm_mode->hdisplay) - 1;
+
+	v_sync_width = drm_mode->vsync_end - drm_mode->vsync_start;
+
+	hsync_ctl = (hsync_period << 16) |
+			(drm_mode->hsync_end - drm_mode->hsync_start);
+	display_hctl = (hsync_end_x << 16) | hsync_start_x;
+
+
+	dp_write_p0(catalog, MMSS_DP_INTF_CONFIG, 0x0);
+	dp_write_p0(catalog, MMSS_DP_INTF_HSYNC_CTL, hsync_ctl);
+	dp_write_p0(catalog, MMSS_DP_INTF_VSYNC_PERIOD_F0, vsync_period *
+			hsync_period);
+	dp_write_p0(catalog, MMSS_DP_INTF_VSYNC_PULSE_WIDTH_F0, v_sync_width *
+			hsync_period);
+	dp_write_p0(catalog, MMSS_DP_INTF_VSYNC_PERIOD_F1, 0);
+	dp_write_p0(catalog, MMSS_DP_INTF_VSYNC_PULSE_WIDTH_F1, 0);
+	dp_write_p0(catalog, MMSS_DP_INTF_DISPLAY_HCTL, display_hctl);
+	dp_write_p0(catalog, MMSS_DP_INTF_ACTIVE_HCTL, 0);
+	dp_write_p0(catalog, MMSS_INTF_DISPLAY_V_START_F0, display_v_start);
+	dp_write_p0(catalog, MMSS_DP_INTF_DISPLAY_V_END_F0, display_v_end);
+	dp_write_p0(catalog, MMSS_INTF_DISPLAY_V_START_F1, 0);
+	dp_write_p0(catalog, MMSS_DP_INTF_DISPLAY_V_END_F1, 0);
+	dp_write_p0(catalog, MMSS_DP_INTF_ACTIVE_V_START_F0, 0);
+	dp_write_p0(catalog, MMSS_DP_INTF_ACTIVE_V_END_F0, 0);
+	dp_write_p0(catalog, MMSS_DP_INTF_ACTIVE_V_START_F1, 0);
+	dp_write_p0(catalog, MMSS_DP_INTF_ACTIVE_V_END_F1, 0);
+	dp_write_p0(catalog, MMSS_DP_INTF_POLARITY_CTL, 0);
+
+	dp_write_p0(catalog, MMSS_DP_TPG_MAIN_CONTROL,
+				DP_TPG_CHECKERED_RECT_PATTERN);
+	dp_write_p0(catalog, MMSS_DP_TPG_VIDEO_CONFIG,
+				DP_TPG_VIDEO_CONFIG_BPP_8BIT |
+				DP_TPG_VIDEO_CONFIG_RGB);
+	dp_write_p0(catalog, MMSS_DP_BIST_ENABLE,
+				DP_BIST_ENABLE_DPBIST_EN);
+	dp_write_p0(catalog, MMSS_DP_TIMING_ENGINE_EN,
+				DP_TIMING_ENGINE_EN_EN);
+	DRM_DEBUG_DP("%s: enabled tpg\n", __func__);
+}
+
+void dp_catalog_panel_tpg_disable(struct dp_catalog *dp_catalog)
+{
+	struct dp_catalog_private *catalog = container_of(dp_catalog,
+				struct dp_catalog_private, dp_catalog);
+
+	dp_write_p0(catalog, MMSS_DP_TPG_MAIN_CONTROL, 0x0);
+	dp_write_p0(catalog, MMSS_DP_BIST_ENABLE, 0x0);
+	dp_write_p0(catalog, MMSS_DP_TIMING_ENGINE_EN, 0x0);

drivers/gpu/drm/msm/dp/dp_catalog.c line 1086

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +10 -0
+}
+
+struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_io *io)
+{
+	int rc = 0;
+	struct dp_catalog *dp_catalog;
+	struct dp_catalog_private *catalog;
+
+	if (!io) {
+		DRM_ERROR("invalid input\n");

return ERR_PTR(-EINVAL)

drivers/gpu/drm/msm/dp/dp_catalog.c line 1092

Quoted 6 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +6 -0
+		rc = -EINVAL;
+		goto error;
+	}
+
+	catalog  = devm_kzalloc(dev, sizeof(*catalog), GFP_KERNEL);
+	if (!catalog) {

return ERR_PTR(-ENOMEM)

drivers/gpu/drm/msm/dp/dp_catalog.c line 1100

Quoted 8 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +8 -0
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	catalog->dev = dev;
+	catalog->io = io;
+
+	dp_catalog = &catalog->dp_catalog;

You can just return &catalog->dp_catalog and save the temporary variable.

drivers/gpu/drm/msm/dp/dp_catalog.c line 1104

Quoted 4 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +4 -0
+
+	return dp_catalog;
+error:
+	return ERR_PTR(rc);

goto, label and rc are not needed.

drivers/gpu/drm/msm/dp/dp_catalog.c line 1117

Quoted 13 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +13 -0
+}
+
+void dp_catalog_put(struct dp_catalog *dp_catalog)
+{
+	struct dp_catalog_private *catalog;
+
+	if (!dp_catalog)
+		return;
+
+	catalog = container_of(dp_catalog, struct dp_catalog_private,
+				dp_catalog);
+
+	devm_kfree(catalog->dev, catalog);

You likely do not need to call devm_kfree() and in fact, you probably don't need this function at all.

Quoted 82 lines
drivers/gpu/drm/msm/dp/dp_catalog.c new file +1 -0
+}
drivers/gpu/drm/msm/dp/dp_catalog.h new file +75 -0
new file mode 100644
index 0000000..c453586d
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _DP_CATALOG_H_
+#define _DP_CATALOG_H_
+
+#include "dp_parser.h"
+
+/* interrupts */
+#define DP_INTR_HPD		BIT(0)
+#define DP_INTR_AUX_I2C_DONE	BIT(3)
+#define DP_INTR_WRONG_ADDR	BIT(6)
+#define DP_INTR_TIMEOUT		BIT(9)
+#define DP_INTR_NACK_DEFER	BIT(12)
+#define DP_INTR_WRONG_DATA_CNT	BIT(15)
+#define DP_INTR_I2C_NACK	BIT(18)
+#define DP_INTR_I2C_DEFER	BIT(21)
+#define DP_INTR_PLL_UNLOCKED	BIT(24)
+#define DP_INTR_AUX_ERROR	BIT(27)
+
+#define DP_INTR_READY_FOR_VIDEO		BIT(0)
+#define DP_INTR_IDLE_PATTERN_SENT	BIT(3)
+#define DP_INTR_FRAME_END		BIT(6)
+#define DP_INTR_CRC_UPDATED		BIT(9)
+
+struct dp_catalog {
+	u32 aux_data;
+	u32 total;
+	u32 sync_start;
+	u32 width_blanking;
+	u32 dp_active;
+};
+
+/* AUX APIs */
+u32 dp_catalog_aux_read_data(struct dp_catalog *dp_catalog);
+int dp_catalog_aux_write_data(struct dp_catalog *dp_catalog);
+int dp_catalog_aux_write_trans(struct dp_catalog *dp_catalog);
+int dp_catalog_aux_clear_trans(struct dp_catalog *dp_catalog, bool read);
+void dp_catalog_aux_reset(struct dp_catalog *dp_catalog);
+void dp_catalog_aux_enable(struct dp_catalog *dp_catalog, bool enable);
+void dp_catalog_aux_update_cfg(struct dp_catalog *dp_catalog,
+			struct dp_aux_cfg *cfg,
+			enum dp_phy_aux_config_type type);
+void dp_catalog_aux_setup(struct dp_catalog *dp_catalog,
+			struct dp_aux_cfg *aux_cfg);
+int dp_catalog_aux_get_irq(struct dp_catalog *dp_catalog);
+
+/* DP Controller APIs */
+void dp_catalog_ctrl_state_ctrl(struct dp_catalog *dp_catalog, u32 state);
+void dp_catalog_ctrl_config_ctrl(struct dp_catalog *dp_catalog, u32 config);
+void dp_catalog_ctrl_lane_mapping(struct dp_catalog *dp_catalog);
+void dp_catalog_ctrl_mainlink_ctrl(struct dp_catalog *dp_catalog, bool enable);
+void dp_catalog_ctrl_config_misc(struct dp_catalog *dp_catalog, u32 cc, u32 tb);
+void dp_catalog_ctrl_config_msa(struct dp_catalog *dp_catalog, u32 rate,
+				u32 stream_rate_khz, bool fixed_nvid);
+int dp_catalog_ctrl_set_pattern(struct dp_catalog *dp_catalog, u32 pattern);
+void dp_catalog_ctrl_reset(struct dp_catalog *dp_catalog);
+void dp_catalog_ctrl_usb_reset(struct dp_catalog *dp_catalog, bool flip);
+bool dp_catalog_ctrl_mainlink_ready(struct dp_catalog *dp_catalog);
+void dp_catalog_ctrl_enable_irq(struct dp_catalog *dp_catalog, bool enable);
+void dp_catalog_ctrl_hpd_config(struct dp_catalog *dp_catalog, bool enable);
+void dp_catalog_ctrl_phy_reset(struct dp_catalog *dp_catalog);
+void dp_catalog_ctrl_phy_lane_cfg(struct dp_catalog *dp_catalog, bool flipped,
+				u8 lane_cnt);
+int dp_catalog_ctrl_update_vx_px(struct dp_catalog *dp_catalog, u8 v_level,
+				u8 p_level);
+int dp_catalog_ctrl_get_interrupt(struct dp_catalog *dp_catalog);
+void dp_catalog_ctrl_update_transfer_unit(struct dp_catalog *dp_catalog,
+				u32 dp_tu, u32 valid_boundary,
+				u32 valid_boundary2);
+void dp_catalog_ctrl_send_phy_pattern(struct dp_catalog *dp_catalog,
+				u32 pattern);
+u32 dp_catalog_ctrl_read_phy_pattern(struct dp_catalog *dp_catalog);

drivers/gpu/drm/msm/dp/dp_catalog.h line 85

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_catalog.h new file +10 -0
+
+/* DP Panel APIs */
+int dp_catalog_panel_timing_cfg(struct dp_catalog *dp_catalog);
+void dp_catalog_dump_regs(struct dp_catalog *dp_catalog);
+void dp_catalog_panel_tpg_enable(struct dp_catalog *dp_catalog,
+				struct drm_display_mode *drm_mode);
+void dp_catalog_panel_tpg_disable(struct dp_catalog *dp_catalog);
+
+struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_io *io);
+void dp_catalog_put(struct dp_catalog *catalog);

Documentation may be helpful here.

Quoted 155 lines
drivers/gpu/drm/msm/dp/dp_catalog.h new file +2 -0
+
+#endif /* _DP_CATALOG_H_ */
drivers/gpu/drm/msm/dp/dp_ctrl.c new file +147 -0
new file mode 100644
index 0000000..ed31521
@@ -0,0 +1,1727 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
+ */
+
+#define pr_fmt(fmt)	"[drm-dp] %s: " fmt, __func__
+
+#include <linux/types.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <drm/drm_fixed.h>
+#include <drm/drm_dp_helper.h>
+
+#include "dp_reg.h"
+#include "dp_ctrl.h"
+#include "dp_link.h"
+
+#define DP_KHZ_TO_HZ 1000
+#define IDLE_PATTERN_COMPLETION_TIMEOUT_JIFFIES	(30 * HZ / 1000) /* 30 ms */
+#define WAIT_FOR_VIDEO_READY_TIMEOUT_JIFFIES (HZ / 2)
+
+#define DP_CTRL_INTR_READY_FOR_VIDEO     BIT(0)
+#define DP_CTRL_INTR_IDLE_PATTERN_SENT  BIT(3)
+
+#define MR_LINK_TRAINING1  0x8
+#define MR_LINK_SYMBOL_ERM 0x80
+#define MR_LINK_PRBS7 0x100
+#define MR_LINK_CUSTOM80 0x200
+
+struct dp_tu_calc_input {
+	u64 lclk;        /* 162, 270, 540 and 810 */
+	u64 pclk_khz;    /* in KHz */
+	u64 hactive;     /* active h-width */
+	u64 hporch;      /* bp + fp + pulse */
+	int nlanes;      /* no.of.lanes */
+	int bpp;         /* bits */
+	int pixel_enc;   /* 444, 420, 422 */
+	int dsc_en;     /* dsc on/off */
+	int async_en;   /* async mode */
+	int fec_en;     /* fec */
+	int compress_ratio; /* 2:1 = 200, 3:1 = 300, 3.75:1 = 375 */
+	int num_of_dsc_slices; /* number of slices per line */
+};
+
+struct dp_vc_tu_mapping_table {
+	u32 vic;
+	u8 lanes;
+	u8 lrate; /* DP_LINK_RATE -> 162(6), 270(10), 540(20), 810 (30) */
+	u8 bpp;
+	u8 valid_boundary_link;
+	u16 delay_start_link;
+	bool boundary_moderation_en;
+	u8 valid_lower_boundary_link;
+	u8 upper_boundary_count;
+	u8 lower_boundary_count;
+	u8 tu_size_minus1;
+};
+
+struct dp_ctrl_private {
+	struct dp_ctrl dp_ctrl;
+
+	struct device *dev;
+	struct dp_aux *aux;
+	struct dp_panel *panel;
+	struct dp_link *link;
+	struct dp_power *power;
+	struct dp_parser *parser;
+	struct dp_catalog *catalog;
+
+	struct completion idle_comp;
+	struct mutex push_idle_mutex;
+	struct completion video_comp;
+};
+
+static int dp_aux_link_configure(struct drm_dp_aux *aux,
+					struct dp_link_info *link)
+{
+	u8 values[2];
+	int err;
+
+	values[0] = drm_dp_link_rate_to_bw_code(link->rate);
+	values[1] = link->num_lanes;
+
+	if (link->capabilities & DP_LINK_CAP_ENHANCED_FRAMING)
+		values[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
+
+	err = drm_dp_dpcd_write(aux, DP_LINK_BW_SET, values, sizeof(values));
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+void dp_ctrl_push_idle(struct dp_ctrl *dp_ctrl)
+{
+	struct dp_ctrl_private *ctrl;
+
+	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+	mutex_lock(&ctrl->push_idle_mutex);
+
+	reinit_completion(&ctrl->idle_comp);
+	dp_catalog_ctrl_state_ctrl(ctrl->catalog, DP_STATE_CTRL_PUSH_IDLE);
+
+	if (!wait_for_completion_timeout(&ctrl->idle_comp,
+			IDLE_PATTERN_COMPLETION_TIMEOUT_JIFFIES))
+		pr_warn("PUSH_IDLE pattern timedout\n");
+
+	mutex_unlock(&ctrl->push_idle_mutex);
+	pr_debug("mainlink off done\n");
+}
+
+static void dp_ctrl_config_ctrl(struct dp_ctrl_private *ctrl)
+{
+	u32 config = 0, tbd;
+	u8 *dpcd = ctrl->panel->dpcd;
+
+	/* Default-> LSCLK DIV: 1/4 LCLK  */
+	config |= (2 << DP_CONFIGURATION_CTRL_LSCLK_DIV_SHIFT);
+
+	/* Scrambler reset enable */
+	if (dpcd[DP_EDP_CONFIGURATION_CAP] & DP_ALTERNATE_SCRAMBLER_RESET_CAP)
+		config |= DP_CONFIGURATION_CTRL_ASSR;
+
+	tbd = dp_link_get_test_bits_depth(ctrl->link,
+			ctrl->panel->dp_mode.bpp);
+
+	if (tbd == DP_TEST_BIT_DEPTH_UNKNOWN) {
+		pr_debug("BIT_DEPTH not set. Configure default\n");
+		tbd = DP_TEST_BIT_DEPTH_8;
+	}
+
+	config |= tbd << DP_CONFIGURATION_CTRL_BPC_SHIFT;
+
+	/* Num of Lanes */
+	config |= ((ctrl->link->link_params.num_lanes - 1)
+			<< DP_CONFIGURATION_CTRL_NUM_OF_LANES_SHIFT);
+
+	if (drm_dp_enhanced_frame_cap(dpcd))
+		config |= DP_CONFIGURATION_CTRL_ENHANCED_FRAMING;
+
+	config |= DP_CONFIGURATION_CTRL_P_INTERLACED; /* progressive video */
+
+	/* sync clock & static Mvid */
+	config |= DP_CONFIGURATION_CTRL_STATIC_DYNAMIC_CN;
+	config |= DP_CONFIGURATION_CTRL_SYNC_ASYNC_CLK;
+

drivers/gpu/drm/msm/dp/dp_ctrl.c line 157

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_ctrl.c new file +10 -0
+	dp_catalog_ctrl_config_ctrl(ctrl->catalog, config);
+}
+
+/**
+ * dp_ctrl_configure_source_params() - configures DP transmitter source params
+ * @ctrl: Display Port Driver data
+ *
+ * Configures the DP transmitter source params including details such as lane
+ * configuration, output format and sink/panel timing information.
+ */

You don't need to formally document static funcs.

Quoted 862 lines
drivers/gpu/drm/msm/dp/dp_ctrl.c new file +862 -0
+static void dp_ctrl_configure_source_params(struct dp_ctrl_private *ctrl)
+{
+	u32 cc, tb;
+
+	dp_catalog_ctrl_lane_mapping(ctrl->catalog);
+	dp_catalog_ctrl_mainlink_ctrl(ctrl->catalog, true);
+
+	dp_ctrl_config_ctrl(ctrl);
+
+	tb = dp_link_get_test_bits_depth(ctrl->link,
+		ctrl->panel->dp_mode.bpp);
+	cc = dp_link_get_colorimetry_config(ctrl->link);
+	dp_catalog_ctrl_config_misc(ctrl->catalog, cc, tb);
+	dp_panel_timing_cfg(ctrl->panel);
+}
+
+/*
+ * The structure and few functions present below are IP/Hardware
+ * specific implementation. Most of the implementation will not
+ * have coding comments
+ */
+struct tu_algo_data {
+	s64 lclk_fp;
+	s64 pclk_fp;
+	s64 lwidth;
+	s64 lwidth_fp;
+	s64 hbp_relative_to_pclk;
+	s64 hbp_relative_to_pclk_fp;
+	int nlanes;
+	int bpp;
+	int pixelEnc;
+	int dsc_en;
+	int async_en;
+	int bpc;
+
+	uint delay_start_link_extra_pixclk;
+	int extra_buffer_margin;
+	s64 ratio_fp;
+	s64 original_ratio_fp;
+
+	s64 err_fp;
+	s64 n_err_fp;
+	s64 n_n_err_fp;
+	int tu_size;
+	int tu_size_desired;
+	int tu_size_minus1;
+
+	int valid_boundary_link;
+	s64 resulting_valid_fp;
+	s64 total_valid_fp;
+	s64 effective_valid_fp;
+	s64 effective_valid_recorded_fp;
+	int n_tus;
+	int n_tus_per_lane;
+	int paired_tus;
+	int remainder_tus;
+	int remainder_tus_upper;
+	int remainder_tus_lower;
+	int extra_bytes;
+	int filler_size;
+	int delay_start_link;
+
+	int extra_pclk_cycles;
+	int extra_pclk_cycles_in_link_clk;
+	s64 ratio_by_tu_fp;
+	s64 average_valid2_fp;
+	int new_valid_boundary_link;
+	int remainder_symbols_exist;
+	int n_symbols;
+	s64 n_remainder_symbols_per_lane_fp;
+	s64 last_partial_tu_fp;
+	s64 TU_ratio_err_fp;
+
+	int n_tus_incl_last_incomplete_tu;
+	int extra_pclk_cycles_tmp;
+	int extra_pclk_cycles_in_link_clk_tmp;
+	int extra_required_bytes_new_tmp;
+	int filler_size_tmp;
+	int lower_filler_size_tmp;
+	int delay_start_link_tmp;
+
+	bool boundary_moderation_en;
+	int boundary_mod_lower_err;
+	int upper_boundary_count;
+	int lower_boundary_count;
+	int i_upper_boundary_count;
+	int i_lower_boundary_count;
+	int valid_lower_boundary_link;
+	int even_distribution_BF;
+	int even_distribution_legacy;
+	int even_distribution;
+	int min_hblank_violated;
+	s64 delay_start_time_fp;
+	s64 hbp_time_fp;
+	s64 hactive_time_fp;
+	s64 diff_abs_fp;
+
+	s64 ratio;
+};
+
+static int _tu_param_compare(s64 a, s64 b)
+{
+	u32 a_int, a_frac, a_sign;
+	u32 b_int, b_frac, b_sign;
+	s64 a_temp, b_temp, minus_1;
+
+	if (a == b)
+		return 0;
+
+	minus_1 = drm_fixp_from_fraction(-1, 1);
+
+	a_int = (a >> 32) & 0x7FFFFFFF;
+	a_frac = a & 0xFFFFFFFF;
+	a_sign = (a >> 32) & 0x80000000 ? 1 : 0;
+
+	b_int = (b >> 32) & 0x7FFFFFFF;
+	b_frac = b & 0xFFFFFFFF;
+	b_sign = (b >> 32) & 0x80000000 ? 1 : 0;
+
+	if (a_sign > b_sign)
+		return 2;
+	else if (b_sign > a_sign)
+		return 1;
+
+	if (!a_sign && !b_sign) { /* positive */
+		if (a > b)
+			return 1;
+		else
+			return 2;
+	} else { /* negative */
+		a_temp = drm_fixp_mul(a, minus_1);
+		b_temp = drm_fixp_mul(b, minus_1);
+
+		if (a_temp > b_temp)
+			return 2;
+		else
+			return 1;
+	}
+}
+
+static void dp_panel_update_tu_timings(struct dp_tu_calc_input *in,
+					struct tu_algo_data *tu)
+{
+	int nlanes = in->nlanes;
+	int dsc_num_slices = in->num_of_dsc_slices;
+	int dsc_num_bytes  = 0;
+	int numerator;
+	s64 pclk_dsc_fp;
+	s64 dwidth_dsc_fp;
+	s64 hbp_dsc_fp;
+	s64 overhead_dsc;
+
+	int tot_num_eoc_symbols = 0;
+	int tot_num_hor_bytes   = 0;
+	int tot_num_dummy_bytes = 0;
+	int dwidth_dsc_bytes    = 0;
+	int  eoc_bytes           = 0;
+
+	s64 temp1_fp, temp2_fp, temp3_fp;
+
+	tu->lclk_fp              = drm_fixp_from_fraction(in->lclk, 1);
+	tu->pclk_fp              = drm_fixp_from_fraction(in->pclk_khz, 1000);
+	tu->lwidth               = in->hactive;
+	tu->hbp_relative_to_pclk = in->hporch;
+	tu->nlanes               = in->nlanes;
+	tu->bpp                  = in->bpp;
+	tu->pixelEnc             = in->pixel_enc;
+	tu->dsc_en               = in->dsc_en;
+	tu->async_en             = in->async_en;
+	tu->lwidth_fp            = drm_fixp_from_fraction(in->hactive, 1);
+	tu->hbp_relative_to_pclk_fp = drm_fixp_from_fraction(in->hporch, 1);
+
+	if (tu->pixelEnc == 420) {
+		temp1_fp = drm_fixp_from_fraction(2, 1);
+		tu->pclk_fp = drm_fixp_div(tu->pclk_fp, temp1_fp);
+		tu->lwidth_fp = drm_fixp_div(tu->lwidth_fp, temp1_fp);
+		tu->hbp_relative_to_pclk_fp =
+				drm_fixp_div(tu->hbp_relative_to_pclk_fp, 2);
+	}
+
+	if (tu->pixelEnc == 422) {
+		switch (tu->bpp) {
+		case 24:
+			tu->bpp = 16;
+			tu->bpc = 8;
+			break;
+		case 30:
+			tu->bpp = 20;
+			tu->bpc = 10;
+			break;
+		default:
+			tu->bpp = 16;
+			tu->bpc = 8;
+			break;
+		}
+	} else {
+		tu->bpc = tu->bpp/3;
+	}
+
+	if (!in->dsc_en)
+		goto fec_check;
+
+	temp1_fp = drm_fixp_from_fraction(in->compress_ratio, 100);
+	temp2_fp = drm_fixp_from_fraction(in->bpp, 1);
+	temp3_fp = drm_fixp_div(temp2_fp, temp1_fp);
+	temp2_fp = drm_fixp_mul(tu->lwidth_fp, temp3_fp);
+
+	temp1_fp = drm_fixp_from_fraction(8, 1);
+	temp3_fp = drm_fixp_div(temp2_fp, temp1_fp);
+
+	numerator = drm_fixp2int(temp3_fp);
+
+	dsc_num_bytes  = numerator / dsc_num_slices;
+	eoc_bytes           = dsc_num_bytes % nlanes;
+	tot_num_eoc_symbols = nlanes * dsc_num_slices;
+	tot_num_hor_bytes   = dsc_num_bytes * dsc_num_slices;
+	tot_num_dummy_bytes = (nlanes - eoc_bytes) * dsc_num_slices;
+
+	if (dsc_num_bytes == 0)
+		pr_info("incorrect no of bytes per slice=%d\n", dsc_num_bytes);
+
+	dwidth_dsc_bytes = (tot_num_hor_bytes +
+				tot_num_eoc_symbols +
+				(eoc_bytes == 0 ? 0 : tot_num_dummy_bytes));
+	overhead_dsc     = dwidth_dsc_bytes / tot_num_hor_bytes;
+
+	dwidth_dsc_fp = drm_fixp_from_fraction(dwidth_dsc_bytes, 3);
+
+	temp2_fp = drm_fixp_mul(tu->pclk_fp, dwidth_dsc_fp);
+	temp1_fp = drm_fixp_div(temp2_fp, tu->lwidth_fp);
+	pclk_dsc_fp = temp1_fp;
+
+	temp1_fp = drm_fixp_div(pclk_dsc_fp, tu->pclk_fp);
+	temp2_fp = drm_fixp_mul(tu->hbp_relative_to_pclk_fp, temp1_fp);
+	hbp_dsc_fp = temp2_fp;
+
+	/* output */
+	tu->pclk_fp = pclk_dsc_fp;
+	tu->lwidth_fp = dwidth_dsc_fp;
+	tu->hbp_relative_to_pclk_fp = hbp_dsc_fp;
+
+fec_check:
+	if (in->fec_en) {
+		temp1_fp = drm_fixp_from_fraction(976, 1000); /* 0.976 */
+		tu->lclk_fp = drm_fixp_mul(tu->lclk_fp, temp1_fp);
+	}
+}
+
+static void _tu_valid_boundary_calc(struct tu_algo_data *tu)
+{
+	s64 temp1_fp, temp2_fp, temp, temp1, temp2;
+	int compare_result_1, compare_result_2, compare_result_3;
+
+	temp1_fp = drm_fixp_from_fraction(tu->tu_size, 1);
+	temp2_fp = drm_fixp_mul(tu->ratio_fp, temp1_fp);
+
+	tu->new_valid_boundary_link = drm_fixp2int_ceil(temp2_fp);
+
+	temp = (tu->i_upper_boundary_count *
+				tu->new_valid_boundary_link +
+				tu->i_lower_boundary_count *
+				(tu->new_valid_boundary_link-1));
+	tu->average_valid2_fp = drm_fixp_from_fraction(temp,
+					(tu->i_upper_boundary_count +
+					tu->i_lower_boundary_count));
+
+	temp1_fp = drm_fixp_from_fraction(tu->bpp, 8);
+	temp2_fp = tu->lwidth_fp;
+	temp1_fp = drm_fixp_mul(temp2_fp, temp1_fp);
+	temp2_fp = drm_fixp_div(temp1_fp, tu->average_valid2_fp);
+	tu->n_tus = drm_fixp2int(temp2_fp);
+	if ((temp2_fp & 0xFFFFFFFF) > 0xFFFFF000)
+		tu->n_tus += 1;
+
+	temp1_fp = drm_fixp_from_fraction(tu->n_tus, 1);
+	temp2_fp = drm_fixp_mul(temp1_fp, tu->average_valid2_fp);
+	temp1_fp = drm_fixp_from_fraction(tu->n_symbols, 1);
+	temp2_fp = temp1_fp - temp2_fp;
+	temp1_fp = drm_fixp_from_fraction(tu->nlanes, 1);
+	temp2_fp = drm_fixp_div(temp2_fp, temp1_fp);
+	tu->n_remainder_symbols_per_lane_fp = temp2_fp;
+
+	temp1_fp = drm_fixp_from_fraction(tu->tu_size, 1);
+	tu->last_partial_tu_fp =
+			drm_fixp_div(tu->n_remainder_symbols_per_lane_fp,
+					temp1_fp);
+
+	if (tu->n_remainder_symbols_per_lane_fp != 0)
+		tu->remainder_symbols_exist = 1;
+	else
+		tu->remainder_symbols_exist = 0;
+
+	temp1_fp = drm_fixp_from_fraction(tu->n_tus, tu->nlanes);
+	tu->n_tus_per_lane = drm_fixp2int(temp1_fp);
+
+	tu->paired_tus = (int)((tu->n_tus_per_lane) /
+					(tu->i_upper_boundary_count +
+					 tu->i_lower_boundary_count));
+
+	tu->remainder_tus = tu->n_tus_per_lane - tu->paired_tus *
+						(tu->i_upper_boundary_count +
+						tu->i_lower_boundary_count);
+
+	if ((tu->remainder_tus - tu->i_upper_boundary_count) > 0) {
+		tu->remainder_tus_upper = tu->i_upper_boundary_count;
+		tu->remainder_tus_lower = tu->remainder_tus -
+						tu->i_upper_boundary_count;
+	} else {
+		tu->remainder_tus_upper = tu->remainder_tus;
+		tu->remainder_tus_lower = 0;
+	}
+
+	temp = tu->paired_tus * (tu->i_upper_boundary_count *
+				tu->new_valid_boundary_link +
+				tu->i_lower_boundary_count *
+				(tu->new_valid_boundary_link - 1)) +
+				(tu->remainder_tus_upper *
+				 tu->new_valid_boundary_link) +
+				(tu->remainder_tus_lower *
+				(tu->new_valid_boundary_link - 1));
+	tu->total_valid_fp = drm_fixp_from_fraction(temp, 1);
+
+	if (tu->remainder_symbols_exist) {
+		temp1_fp = tu->total_valid_fp +
+				tu->n_remainder_symbols_per_lane_fp;
+		temp2_fp = drm_fixp_from_fraction(tu->n_tus_per_lane, 1);
+		temp2_fp = temp2_fp + tu->last_partial_tu_fp;
+		temp1_fp = drm_fixp_div(temp1_fp, temp2_fp);
+	} else {
+		temp2_fp = drm_fixp_from_fraction(tu->n_tus_per_lane, 1);
+		temp1_fp = drm_fixp_div(tu->total_valid_fp, temp2_fp);
+	}
+	tu->effective_valid_fp = temp1_fp;
+
+	temp1_fp = drm_fixp_from_fraction(tu->tu_size, 1);
+	temp2_fp = drm_fixp_mul(tu->ratio_fp, temp1_fp);
+	tu->n_n_err_fp = tu->effective_valid_fp - temp2_fp;
+
+	temp1_fp = drm_fixp_from_fraction(tu->tu_size, 1);
+	temp2_fp = drm_fixp_mul(tu->ratio_fp, temp1_fp);
+	tu->n_err_fp = tu->average_valid2_fp - temp2_fp;
+
+	tu->even_distribution = tu->n_tus % tu->nlanes == 0 ? 1 : 0;
+
+	temp1_fp = drm_fixp_from_fraction(tu->bpp, 8);
+	temp2_fp = tu->lwidth_fp;
+	temp1_fp = drm_fixp_mul(temp2_fp, temp1_fp);
+	temp2_fp = drm_fixp_div(temp1_fp, tu->average_valid2_fp);
+
+	if (temp2_fp)
+		tu->n_tus_incl_last_incomplete_tu = drm_fixp2int_ceil(temp2_fp);
+	else
+		tu->n_tus_incl_last_incomplete_tu = 0;
+
+	temp1 = 0;
+	temp1_fp = drm_fixp_from_fraction(tu->tu_size, 1);
+	temp2_fp = drm_fixp_mul(tu->original_ratio_fp, temp1_fp);
+	temp1_fp = tu->average_valid2_fp - temp2_fp;
+	temp2_fp = drm_fixp_from_fraction(tu->n_tus_incl_last_incomplete_tu, 1);
+	temp1_fp = drm_fixp_mul(temp2_fp, temp1_fp);
+
+	if (temp1_fp)
+		temp1 = drm_fixp2int_ceil(temp1_fp);
+
+	temp = tu->i_upper_boundary_count * tu->nlanes;
+	temp1_fp = drm_fixp_from_fraction(tu->tu_size, 1);
+	temp2_fp = drm_fixp_mul(tu->original_ratio_fp, temp1_fp);
+	temp1_fp = drm_fixp_from_fraction(tu->new_valid_boundary_link, 1);
+	temp2_fp = temp1_fp - temp2_fp;
+	temp1_fp = drm_fixp_from_fraction(temp, 1);
+	temp2_fp = drm_fixp_mul(temp1_fp, temp2_fp);
+
+	if (temp2_fp)
+		temp2 = drm_fixp2int_ceil(temp2_fp);
+	else
+		temp2 = 0;
+	tu->extra_required_bytes_new_tmp = (int)(temp1 + temp2);
+
+	temp1_fp = drm_fixp_from_fraction(8, tu->bpp);
+	temp2_fp = drm_fixp_from_fraction(
+	tu->extra_required_bytes_new_tmp, 1);
+	temp1_fp = drm_fixp_mul(temp2_fp, temp1_fp);
+
+	if (temp1_fp)
+		tu->extra_pclk_cycles_tmp = drm_fixp2int_ceil(temp1_fp);
+	else
+		tu->extra_pclk_cycles_tmp = 0;
+
+	temp1_fp = drm_fixp_from_fraction(tu->extra_pclk_cycles_tmp, 1);
+	temp2_fp = drm_fixp_div(tu->lclk_fp, tu->pclk_fp);
+	temp1_fp = drm_fixp_mul(temp1_fp, temp2_fp);
+
+	if (temp1_fp)
+		tu->extra_pclk_cycles_in_link_clk_tmp =
+						drm_fixp2int_ceil(temp1_fp);
+	else
+		tu->extra_pclk_cycles_in_link_clk_tmp = 0;
+
+	tu->filler_size_tmp = tu->tu_size - tu->new_valid_boundary_link;
+
+	tu->lower_filler_size_tmp = tu->filler_size_tmp + 1;
+
+	tu->delay_start_link_tmp = tu->extra_pclk_cycles_in_link_clk_tmp +
+					tu->lower_filler_size_tmp +
+					tu->extra_buffer_margin;
+
+	temp1_fp = drm_fixp_from_fraction(tu->delay_start_link_tmp, 1);
+	tu->delay_start_time_fp = drm_fixp_div(temp1_fp, tu->lclk_fp);
+
+	compare_result_1 = _tu_param_compare(tu->n_n_err_fp, tu->diff_abs_fp);
+	if (compare_result_1 == 2)
+		compare_result_1 = 1;
+	else
+		compare_result_1 = 0;
+
+	compare_result_2 = _tu_param_compare(tu->n_n_err_fp, tu->err_fp);
+	if (compare_result_2 == 2)
+		compare_result_2 = 1;
+	else
+		compare_result_2 = 0;
+
+	compare_result_3 = _tu_param_compare(tu->hbp_time_fp,
+					tu->delay_start_time_fp);
+	if (compare_result_3 == 2)
+		compare_result_3 = 0;
+	else
+		compare_result_3 = 1;
+
+	if (((tu->even_distribution == 1) ||
+			((tu->even_distribution_BF == 0) &&
+			(tu->even_distribution_legacy == 0))) &&
+			tu->n_err_fp >= 0 && tu->n_n_err_fp >= 0 &&
+			compare_result_2 &&
+			(compare_result_1 || (tu->min_hblank_violated == 1)) &&
+			(tu->new_valid_boundary_link - 1) > 0 &&
+			compare_result_3 &&
+			(tu->delay_start_link_tmp <= 1023)) {
+		tu->upper_boundary_count = tu->i_upper_boundary_count;
+		tu->lower_boundary_count = tu->i_lower_boundary_count;
+		tu->err_fp = tu->n_n_err_fp;
+		tu->boundary_moderation_en = true;
+		tu->tu_size_desired = tu->tu_size;
+		tu->valid_boundary_link = tu->new_valid_boundary_link;
+		tu->effective_valid_recorded_fp = tu->effective_valid_fp;
+		tu->even_distribution_BF = 1;
+		tu->delay_start_link = tu->delay_start_link_tmp;
+	} else if (tu->boundary_mod_lower_err == 0) {
+		compare_result_1 = _tu_param_compare(tu->n_n_err_fp,
+							tu->diff_abs_fp);
+		if (compare_result_1 == 2)
+			tu->boundary_mod_lower_err = 1;
+	}
+}
+
+static void _dp_ctrl_calc_tu(struct dp_tu_calc_input *in,
+				   struct dp_vc_tu_mapping_table *tu_table)
+{
+	struct tu_algo_data tu;
+	int compare_result_1, compare_result_2;
+	u64 temp = 0;
+	s64 temp_fp = 0, temp1_fp = 0, temp2_fp = 0;
+
+	s64 LCLK_FAST_SKEW_fp = drm_fixp_from_fraction(6, 10000); /* 0.0006 */
+	s64 const_p49_fp = drm_fixp_from_fraction(49, 100); /* 0.49 */
+	s64 const_p56_fp = drm_fixp_from_fraction(56, 100); /* 0.56 */
+	s64 RATIO_SCALE_fp = drm_fixp_from_fraction(1001, 1000);
+
+	u8 DP_BRUTE_FORCE = 1;
+	s64 BRUTE_FORCE_THRESHOLD_fp = drm_fixp_from_fraction(1, 10); /* 0.1 */
+	uint EXTRA_PIXCLK_CYCLE_DELAY = 4;
+	uint HBLANK_MARGIN = 4;
+
+	memset(&tu, 0, sizeof(tu));
+
+	dp_panel_update_tu_timings(in, &tu);
+
+	tu.err_fp = drm_fixp_from_fraction(1000, 1); /* 1000 */
+
+	temp1_fp = drm_fixp_from_fraction(4, 1);
+	temp2_fp = drm_fixp_mul(temp1_fp, tu.lclk_fp);
+	temp_fp = drm_fixp_div(temp2_fp, tu.pclk_fp);
+	tu.extra_buffer_margin = drm_fixp2int_ceil(temp_fp);
+
+	temp1_fp = drm_fixp_from_fraction(tu.bpp, 8);
+	temp2_fp = drm_fixp_mul(tu.pclk_fp, temp1_fp);
+	temp1_fp = drm_fixp_from_fraction(tu.nlanes, 1);
+	temp2_fp = drm_fixp_div(temp2_fp, temp1_fp);
+	tu.ratio_fp = drm_fixp_div(temp2_fp, tu.lclk_fp);
+
+	tu.original_ratio_fp = tu.ratio_fp;
+	tu.boundary_moderation_en = false;
+	tu.upper_boundary_count = 0;
+	tu.lower_boundary_count = 0;
+	tu.i_upper_boundary_count = 0;
+	tu.i_lower_boundary_count = 0;
+	tu.valid_lower_boundary_link = 0;
+	tu.even_distribution_BF = 0;
+	tu.even_distribution_legacy = 0;
+	tu.even_distribution = 0;
+	tu.delay_start_time_fp = 0;
+
+	tu.err_fp = drm_fixp_from_fraction(1000, 1);
+	tu.n_err_fp = 0;
+	tu.n_n_err_fp = 0;
+
+	tu.ratio = drm_fixp2int(tu.ratio_fp);
+	temp1_fp = drm_fixp_from_fraction(tu.nlanes, 1);
+	temp2_fp = tu.lwidth_fp % temp1_fp;
+	if (temp2_fp != 0 &&
+			!tu.ratio && tu.dsc_en == 0) {
+		tu.ratio_fp = drm_fixp_mul(tu.ratio_fp, RATIO_SCALE_fp);
+		tu.ratio = drm_fixp2int(tu.ratio_fp);
+		if (tu.ratio)
+			tu.ratio_fp = drm_fixp_from_fraction(1, 1);
+	}
+
+	if (tu.ratio > 1)
+		tu.ratio = 1;
+
+	if (tu.ratio == 1)
+		goto tu_size_calc;
+
+	compare_result_1 = _tu_param_compare(tu.ratio_fp, const_p49_fp);
+	if (!compare_result_1 || compare_result_1 == 1)
+		compare_result_1 = 1;
+	else
+		compare_result_1 = 0;
+
+	compare_result_2 = _tu_param_compare(tu.ratio_fp, const_p56_fp);
+	if (!compare_result_2 || compare_result_2 == 2)
+		compare_result_2 = 1;
+	else
+		compare_result_2 = 0;
+
+	if (tu.dsc_en && compare_result_1 && compare_result_2) {
+		HBLANK_MARGIN += 4;
+		DRM_DEBUG_DP("Info: increase HBLANK_MARGIN to %d\n",
+				HBLANK_MARGIN);
+	}
+
+tu_size_calc:
+	for (tu.tu_size = 32; tu.tu_size <= 64; tu.tu_size++) {
+		temp1_fp = drm_fixp_from_fraction(tu.tu_size, 1);
+		temp2_fp = drm_fixp_mul(tu.ratio_fp, temp1_fp);
+		temp = drm_fixp2int_ceil(temp2_fp);
+		temp1_fp = drm_fixp_from_fraction(temp, 1);
+		tu.n_err_fp = temp1_fp - temp2_fp;
+
+		if (tu.n_err_fp < tu.err_fp) {
+			tu.err_fp = tu.n_err_fp;
+			tu.tu_size_desired = tu.tu_size;
+		}
+	}
+
+	tu.tu_size_minus1 = tu.tu_size_desired - 1;
+
+	temp1_fp = drm_fixp_from_fraction(tu.tu_size_desired, 1);
+	temp2_fp = drm_fixp_mul(tu.ratio_fp, temp1_fp);
+	tu.valid_boundary_link = drm_fixp2int_ceil(temp2_fp);
+
+	temp1_fp = drm_fixp_from_fraction(tu.bpp, 8);
+	temp2_fp = tu.lwidth_fp;
+	temp2_fp = drm_fixp_mul(temp2_fp, temp1_fp);
+
+	temp1_fp = drm_fixp_from_fraction(tu.valid_boundary_link, 1);
+	temp2_fp = drm_fixp_div(temp2_fp, temp1_fp);
+	tu.n_tus = drm_fixp2int(temp2_fp);
+	if ((temp2_fp & 0xFFFFFFFF) > 0xFFFFF000)
+		tu.n_tus += 1;
+
+	tu.even_distribution_legacy = tu.n_tus % tu.nlanes == 0 ? 1 : 0;
+	DRM_DEBUG_DP("Info: n_sym = %d, num_of_tus = %d\n",
+		tu.valid_boundary_link, tu.n_tus);
+
+	temp1_fp = drm_fixp_from_fraction(tu.tu_size_desired, 1);
+	temp2_fp = drm_fixp_mul(tu.original_ratio_fp, temp1_fp);
+	temp1_fp = drm_fixp_from_fraction(tu.valid_boundary_link, 1);
+	temp2_fp = temp1_fp - temp2_fp;
+	temp1_fp = drm_fixp_from_fraction(tu.n_tus + 1, 1);
+	temp2_fp = drm_fixp_mul(temp1_fp, temp2_fp);
+
+	temp = drm_fixp2int(temp2_fp);
+	if (temp && temp2_fp)
+		tu.extra_bytes = drm_fixp2int_ceil(temp2_fp);
+	else
+		tu.extra_bytes = 0;
+
+	temp1_fp = drm_fixp_from_fraction(tu.extra_bytes, 1);
+	temp2_fp = drm_fixp_from_fraction(8, tu.bpp);
+	temp1_fp = drm_fixp_mul(temp1_fp, temp2_fp);
+
+	if (temp && temp1_fp)
+		tu.extra_pclk_cycles = drm_fixp2int_ceil(temp1_fp);
+	else
+		tu.extra_pclk_cycles = drm_fixp2int(temp1_fp);
+
+	temp1_fp = drm_fixp_div(tu.lclk_fp, tu.pclk_fp);
+	temp2_fp = drm_fixp_from_fraction(tu.extra_pclk_cycles, 1);
+	temp1_fp = drm_fixp_mul(temp2_fp, temp1_fp);
+
+	if (temp1_fp)
+		tu.extra_pclk_cycles_in_link_clk = drm_fixp2int_ceil(temp1_fp);
+	else
+		tu.extra_pclk_cycles_in_link_clk = drm_fixp2int(temp1_fp);
+
+	tu.filler_size = tu.tu_size_desired - tu.valid_boundary_link;
+
+	temp1_fp = drm_fixp_from_fraction(tu.tu_size_desired, 1);
+	tu.ratio_by_tu_fp = drm_fixp_mul(tu.ratio_fp, temp1_fp);
+
+	tu.delay_start_link = tu.extra_pclk_cycles_in_link_clk +
+				tu.filler_size + tu.extra_buffer_margin;
+
+	tu.resulting_valid_fp =
+			drm_fixp_from_fraction(tu.valid_boundary_link, 1);
+
+	temp1_fp = drm_fixp_from_fraction(tu.tu_size_desired, 1);
+	temp2_fp = drm_fixp_div(tu.resulting_valid_fp, temp1_fp);
+	tu.TU_ratio_err_fp = temp2_fp - tu.original_ratio_fp;
+
+	temp1_fp = drm_fixp_from_fraction(HBLANK_MARGIN, 1);
+	temp1_fp = tu.hbp_relative_to_pclk_fp - temp1_fp;
+	tu.hbp_time_fp = drm_fixp_div(temp1_fp, tu.pclk_fp);
+
+	temp1_fp = drm_fixp_from_fraction(tu.delay_start_link, 1);
+	tu.delay_start_time_fp = drm_fixp_div(temp1_fp, tu.lclk_fp);
+
+	compare_result_1 = _tu_param_compare(tu.hbp_time_fp,
+					tu.delay_start_time_fp);
+	if (compare_result_1 == 2) /* if (hbp_time_fp < delay_start_time_fp) */
+		tu.min_hblank_violated = 1;
+
+	tu.hactive_time_fp = drm_fixp_div(tu.lwidth_fp, tu.pclk_fp);
+
+	compare_result_2 = _tu_param_compare(tu.hactive_time_fp,
+						tu.delay_start_time_fp);
+	if (compare_result_2 == 2)
+		tu.min_hblank_violated = 1;
+
+	tu.delay_start_time_fp = 0;
+
+	/* brute force */
+
+	tu.delay_start_link_extra_pixclk = EXTRA_PIXCLK_CYCLE_DELAY;
+	tu.diff_abs_fp = tu.resulting_valid_fp - tu.ratio_by_tu_fp;
+
+	temp = drm_fixp2int(tu.diff_abs_fp);
+	if (!temp && tu.diff_abs_fp <= 0xffff)
+		tu.diff_abs_fp = 0;
+
+	/* if(diff_abs < 0) diff_abs *= -1 */
+	if (tu.diff_abs_fp < 0)
+		tu.diff_abs_fp = drm_fixp_mul(tu.diff_abs_fp, -1);
+
+	tu.boundary_mod_lower_err = 0;
+	if ((tu.diff_abs_fp != 0 &&
+			((tu.diff_abs_fp > BRUTE_FORCE_THRESHOLD_fp) ||
+			 (tu.even_distribution_legacy == 0) ||
+			 (DP_BRUTE_FORCE == 1))) ||
+			(tu.min_hblank_violated == 1)) {
+		do {
+			tu.err_fp = drm_fixp_from_fraction(1000, 1);
+
+			temp1_fp = drm_fixp_div(tu.lclk_fp, tu.pclk_fp);
+			temp2_fp = drm_fixp_from_fraction(
+					tu.delay_start_link_extra_pixclk, 1);
+			temp1_fp = drm_fixp_mul(temp2_fp, temp1_fp);
+
+			if (temp1_fp)
+				tu.extra_buffer_margin =
+					drm_fixp2int_ceil(temp1_fp);
+			else
+				tu.extra_buffer_margin = 0;
+
+			temp1_fp = drm_fixp_from_fraction(tu.bpp, 8);
+			temp1_fp = drm_fixp_mul(tu.lwidth_fp, temp1_fp);
+
+			if (temp1_fp)
+				tu.n_symbols = drm_fixp2int_ceil(temp1_fp);
+			else
+				tu.n_symbols = 0;
+
+			for (tu.tu_size = 32; tu.tu_size <= 64; tu.tu_size++) {
+				for (tu.i_upper_boundary_count = 1;
+					tu.i_upper_boundary_count <= 15;
+					tu.i_upper_boundary_count++) {
+					for (tu.i_lower_boundary_count = 1;
+						tu.i_lower_boundary_count <= 15;
+						tu.i_lower_boundary_count++) {
+						_tu_valid_boundary_calc(&tu);
+					}
+				}
+			}
+			tu.delay_start_link_extra_pixclk--;
+		} while (tu.boundary_moderation_en != true &&
+			tu.boundary_mod_lower_err == 1 &&
+			tu.delay_start_link_extra_pixclk != 0);
+
+		if (tu.boundary_moderation_en == true) {
+			temp1_fp = drm_fixp_from_fraction(
+					(tu.upper_boundary_count *
+					tu.valid_boundary_link +
+					tu.lower_boundary_count *
+					(tu.valid_boundary_link - 1)), 1);
+			temp2_fp = drm_fixp_from_fraction(
+					(tu.upper_boundary_count +
+					tu.lower_boundary_count), 1);
+			tu.resulting_valid_fp =
+					drm_fixp_div(temp1_fp, temp2_fp);
+
+			temp1_fp = drm_fixp_from_fraction(
+					tu.tu_size_desired, 1);
+			tu.ratio_by_tu_fp =
+				drm_fixp_mul(tu.original_ratio_fp, temp1_fp);
+
+			tu.valid_lower_boundary_link =
+				tu.valid_boundary_link - 1;
+
+			temp1_fp = drm_fixp_from_fraction(tu.bpp, 8);
+			temp1_fp = drm_fixp_mul(tu.lwidth_fp, temp1_fp);
+			temp2_fp = drm_fixp_div(temp1_fp,
+						tu.resulting_valid_fp);
+			tu.n_tus = drm_fixp2int(temp2_fp);
+
+			tu.tu_size_minus1 = tu.tu_size_desired - 1;
+			tu.even_distribution_BF = 1;
+
+			temp1_fp =
+				drm_fixp_from_fraction(tu.tu_size_desired, 1);
+			temp2_fp =
+				drm_fixp_div(tu.resulting_valid_fp, temp1_fp);
+			tu.TU_ratio_err_fp = temp2_fp - tu.original_ratio_fp;
+		}
+	}
+
+	temp2_fp = drm_fixp_mul(LCLK_FAST_SKEW_fp, tu.lwidth_fp);
+
+	if (temp2_fp)
+		temp = drm_fixp2int_ceil(temp2_fp);
+	else
+		temp = 0;
+
+	temp1_fp = drm_fixp_from_fraction(tu.nlanes, 1);
+	temp2_fp = drm_fixp_mul(tu.original_ratio_fp, temp1_fp);
+	temp1_fp = drm_fixp_from_fraction(tu.bpp, 8);
+	temp2_fp = drm_fixp_div(temp1_fp, temp2_fp);
+	temp1_fp = drm_fixp_from_fraction(temp, 1);
+	temp2_fp = drm_fixp_mul(temp1_fp, temp2_fp);
+	temp = drm_fixp2int(temp2_fp);
+
+	if (tu.async_en)
+		tu.delay_start_link += (int)temp;
+
+	temp1_fp = drm_fixp_from_fraction(tu.delay_start_link, 1);
+	tu.delay_start_time_fp = drm_fixp_div(temp1_fp, tu.lclk_fp);
+
+	/* OUTPUTS */
+	tu_table->valid_boundary_link       = tu.valid_boundary_link;
+	tu_table->delay_start_link          = tu.delay_start_link;
+	tu_table->boundary_moderation_en    = tu.boundary_moderation_en;
+	tu_table->valid_lower_boundary_link = tu.valid_lower_boundary_link;
+	tu_table->upper_boundary_count      = tu.upper_boundary_count;
+	tu_table->lower_boundary_count      = tu.lower_boundary_count;
+	tu_table->tu_size_minus1            = tu.tu_size_minus1;
+
+	DRM_DEBUG_DP("TU: valid_boundary_link: %d\n",
+				tu_table->valid_boundary_link);
+	DRM_DEBUG_DP("TU: delay_start_link: %d\n",
+				tu_table->delay_start_link);
+	DRM_DEBUG_DP("TU: boundary_moderation_en: %d\n",
+			tu_table->boundary_moderation_en);
+	DRM_DEBUG_DP("TU: valid_lower_boundary_link: %d\n",
+			tu_table->valid_lower_boundary_link);
+	DRM_DEBUG_DP("TU: upper_boundary_count: %d\n",
+			tu_table->upper_boundary_count);
+	DRM_DEBUG_DP("TU: lower_boundary_count: %d\n",
+			tu_table->lower_boundary_count);
+	DRM_DEBUG_DP("TU: tu_size_minus1: %d\n", tu_table->tu_size_minus1);
+}
+
+static void dp_ctrl_calc_tu_parameters(struct dp_ctrl_private *ctrl,
+		struct dp_vc_tu_mapping_table *tu_table)
+{
+	struct dp_tu_calc_input in;
+	struct drm_display_mode *drm_mode;
+
+	drm_mode = &ctrl->panel->dp_mode.drm_mode;
+
+	in.lclk = ctrl->link->link_params.rate / 1000;
+	in.pclk_khz = drm_mode->clock;
+	in.hactive = drm_mode->hdisplay;
+	in.hporch = drm_mode->htotal - drm_mode->hdisplay;
+	in.nlanes = ctrl->link->link_params.num_lanes;
+	in.bpp = ctrl->panel->dp_mode.bpp;
+	in.pixel_enc = 444;
+	in.dsc_en = 0;
+	in.async_en = 0;
+	in.fec_en = 0;
+	in.num_of_dsc_slices = 0;
+	in.compress_ratio = 100;
+
+	_dp_ctrl_calc_tu(&in, tu_table);
+}
+
+static void dp_ctrl_setup_tr_unit(struct dp_ctrl_private *ctrl)
+{
+	u32 dp_tu = 0x0;
+	u32 valid_boundary = 0x0;
+	u32 valid_boundary2 = 0x0;
+	struct dp_vc_tu_mapping_table tu_calc_table;
+
+	dp_ctrl_calc_tu_parameters(ctrl, &tu_calc_table);
+
+	dp_tu |= tu_calc_table.tu_size_minus1;
+	valid_boundary |= tu_calc_table.valid_boundary_link;
+	valid_boundary |= (tu_calc_table.delay_start_link << 16);
+
+	valid_boundary2 |= (tu_calc_table.valid_lower_boundary_link << 1);
+	valid_boundary2 |= (tu_calc_table.upper_boundary_count << 16);
+	valid_boundary2 |= (tu_calc_table.lower_boundary_count << 20);
+
+	if (tu_calc_table.boundary_moderation_en)
+		valid_boundary2 |= BIT(0);
+
+	pr_debug("dp_tu=0x%x, valid_boundary=0x%x, valid_boundary2=0x%x\n",
+			dp_tu, valid_boundary, valid_boundary2);
+
+	dp_catalog_ctrl_update_transfer_unit(ctrl->catalog,
+				dp_tu, valid_boundary, valid_boundary2);
+}
+
+static int dp_ctrl_wait4video_ready(struct dp_ctrl_private *ctrl)
+{
+	int ret = 0;
+
+	if (!wait_for_completion_timeout(&ctrl->video_comp,
+				WAIT_FOR_VIDEO_READY_TIMEOUT_JIFFIES)) {
+		DRM_ERROR("Link Train timedout\n");
+		ret = -ETIMEDOUT;
+	}
+	return ret;
+}
+
+static int dp_ctrl_update_vx_px(struct dp_ctrl_private *ctrl)
+{
+	struct dp_link *link = ctrl->link;
+	int ret = 0, lane, lane_cnt;
+	u8 buf[4];
+	u32 max_level_reached = 0;
+	u32 voltage_swing_level = link->phy_params.v_level;
+	u32 pre_emphasis_level = link->phy_params.p_level;
+
+	ret = dp_catalog_ctrl_update_vx_px(ctrl->catalog,
+		voltage_swing_level, pre_emphasis_level);
+
+	if (ret)
+		return ret;
+
+	if (voltage_swing_level > DP_TRAIN_VOLTAGE_SWING_MAX) {
+		DRM_DEBUG_DP("max. voltage swing level reached %d\n",
+				voltage_swing_level);
+		max_level_reached |= DP_TRAIN_MAX_SWING_REACHED;

drivers/gpu/drm/msm/dp/dp_ctrl.c line 1029

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_ctrl.c new file +10 -0
+	}
+
+	if (pre_emphasis_level == DP_TRAIN_PRE_EMPHASIS_MAX) {
+		DRM_DEBUG_DP("max. pre-emphasis level reached %d\n",
+				pre_emphasis_level);
+		max_level_reached  |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
+	}
+
+	pre_emphasis_level <<= DP_TRAIN_PRE_EMPHASIS_SHIFT;
+

`

Quoted 112 lines
drivers/gpu/drm/msm/dp/dp_ctrl.c new file +112 -0
+	lane_cnt = ctrl->link->link_params.num_lanes;
+	for (lane = 0; lane < lane_cnt; lane++)
+		buf[lane] = voltage_swing_level | pre_emphasis_level
+				| max_level_reached;
+
+	DRM_DEBUG_DP("sink: p|v=0x%x\n", voltage_swing_level
+					| pre_emphasis_level);
+	ret = drm_dp_dpcd_write(ctrl->aux->drm_aux, DP_TRAINING_LANE0_SET,
+					buf, lane_cnt);
+	if (ret == lane_cnt)
+		ret = 0;
+
+	return ret;
+}
+
+static bool dp_ctrl_train_pattern_set(struct dp_ctrl_private *ctrl,
+		u8 pattern)
+{
+	u8 buf;
+	int ret = 0;
+
+	DRM_DEBUG_DP("sink: pattern=%x\n", pattern);
+
+	buf = pattern;
+	ret = drm_dp_dpcd_writeb(ctrl->aux->drm_aux,
+					DP_TRAINING_PATTERN_SET, buf);
+	return ret == 1;
+}
+
+static int dp_ctrl_read_link_status(struct dp_ctrl_private *ctrl,
+				    u8 *link_status)
+{
+	int len = 0;
+	u32 const offset = DP_LANE_ALIGN_STATUS_UPDATED - DP_LANE0_1_STATUS;
+	u32 link_status_read_max_retries = 100;
+
+	while (--link_status_read_max_retries) {
+		len = drm_dp_dpcd_read_link_status(ctrl->aux->drm_aux,
+			link_status);
+		if (len != DP_LINK_STATUS_SIZE) {
+			DRM_ERROR("DP link status read failed, err: %d\n", len);
+			return len;
+		}
+
+		if (!(link_status[offset] & DP_LINK_STATUS_UPDATED))
+			return 0;
+	}
+
+	return -ETIMEDOUT;
+}
+
+static int dp_ctrl_link_train_1(struct dp_ctrl_private *ctrl)
+{
+	int tries, old_v_level, ret = 0;
+	u8 link_status[DP_LINK_STATUS_SIZE];
+	int const maximum_retries = 5;
+
+	dp_catalog_ctrl_state_ctrl(ctrl->catalog, 0);
+
+	ret = dp_catalog_ctrl_set_pattern(ctrl->catalog, DP_TRAINING_PATTERN_1);
+	if (ret)
[… more lines in the raw message]

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

drivers/gpu/drm/msm/dp/dp_ctrl.c line 1100

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_ctrl.c new file +10 -0
+		return ret;
+	dp_ctrl_train_pattern_set(ctrl, DP_TRAINING_PATTERN_1 |
+		DP_LINK_SCRAMBLING_DISABLE);
+	ret = dp_ctrl_update_vx_px(ctrl);
+	if (ret)
+		return ret;
+
+	tries = 0;
+	old_v_level = ctrl->link->phy_params.v_level;
+	for (tries = 0; tries < maximum_retries; tries++) {
[… more lines in the raw message]

This seems like it could be broken down to:

if (ctrl->lin->link_params.rate == 810000)
ctrl->link->link_params.rate = 540000;
else if (ctrl->link->link_params.rate == 540000)
ctrl->link->link_params.rate = 270000;
else
ctrl->link->link_params.rate = 162000;

drivers/gpu/drm/msm/dp/dp_ctrl.c

Quoted 4 lines
drivers/gpu/drm/msm/dp/dp_ctrl.c new file +4 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

This function doesn't look like it needs to return a value.

drivers/gpu/drm/msm/dp/dp_ctrl.c

Quoted 42 lines
drivers/gpu/drm/msm/dp/dp_ctrl.c new file +42 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

Is this the success path? If so, replacing this with return 0 might be a bit less confusing.

drivers/gpu/drm/msm/dp/dp_ctrl.c

Quoted 117 lines
drivers/gpu/drm/msm/dp/dp_ctrl.c new file +116 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

The actual error here seems to be that clock 'name' doesn't exist, not that you couldn't set the rate.

drivers/gpu/drm/msm/dp/dp_ctrl.c

Quoted 18 lines
drivers/gpu/drm/msm/dp/dp_ctrl.c new file +18 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

Unabled -> Unable

drivers/gpu/drm/msm/dp/dp_ctrl.c

Quoted 108 lines
drivers/gpu/drm/msm/dp/dp_ctrl.c new file +108 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

tried < 10 is clearer here rather than using a temporary variable.

drivers/gpu/drm/msm/dp/dp_ctrl.c

Quoted 244 lines
drivers/gpu/drm/msm/dp/dp_ctrl.c new file +244 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

return ERR_PTR(-EINVAL)

drivers/gpu/drm/msm/dp/dp_ctrl.c

Quoted 7 lines
drivers/gpu/drm/msm/dp/dp_ctrl.c new file +7 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

return ERR_PTR(-ENOMEM)

drivers/gpu/drm/msm/dp/dp_ctrl.c

Quoted 17 lines
drivers/gpu/drm/msm/dp/dp_ctrl.c new file +17 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

return &ctrl->dp_ctrl;

drivers/gpu/drm/msm/dp/dp_ctrl.c

Quoted 5 lines
drivers/gpu/drm/msm/dp/dp_ctrl.c new file +5 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

You don't need the goto, or the label or 'rc'

drivers/gpu/drm/msm/dp/dp_ctrl.c

Quoted 14 lines
drivers/gpu/drm/msm/dp/dp_ctrl.c new file +14 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

You almost never need to call devm_kfree

drivers/gpu/drm/msm/dp/dp_display.c

Quoted 54 lines
drivers/gpu/drm/msm/dp/dp_ctrl.c new file +1 -0

Lines of this file are not shown. View the raw message.

drivers/gpu/drm/msm/dp/dp_ctrl.h new file +35 -0
new file mode 100644
index 0000000..63681e82

Lines of this file are not shown. View the raw message.

drivers/gpu/drm/msm/dp/dp_display.c new file +6 -0
new file mode 100644
index 0000000..5525405

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

I don't think I saw pr_ being used in this file. You can remove this.

drivers/gpu/drm/msm/dp/dp_display.c

Quoted 65 lines
drivers/gpu/drm/msm/dp/dp_display.c new file +65 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

This isn't possible - assume the priv is valid on an interrupt handler callback.

drivers/gpu/drm/msm/dp/dp_display.c

Quoted 25 lines
drivers/gpu/drm/msm/dp/dp_display.c new file +25 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

None of these will be NULL on a component bind.

Quoted 9 lines
drivers/gpu/drm/msm/dp/dp_display.c new file +8 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

. + }

Neither of these will be NULL. You don't need to check these.

drivers/gpu/drm/msm/dp/dp_display.c

Quoted 46 lines
drivers/gpu/drm/msm/dp/dp_display.c new file +46 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

Neither of the two if statements above will ever be false;

drivers/gpu/drm/msm/dp/dp_display.c

Quoted 2 lines
drivers/gpu/drm/msm/dp/dp_display.c new file +2 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

Aren't these private functions? If you specifically do not want them to return a error code, then you should make them void rather than casting them at call time.

drivers/gpu/drm/msm/dp/dp_display.c

Quoted 194 lines
drivers/gpu/drm/msm/dp/dp_display.c new file +194 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

This doesn't seem like it is possible.

+

drivers/gpu/drm/msm/dp/dp_display.c

Quoted 6 lines
drivers/gpu/drm/msm/dp/dp_display.c new file +6 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

and this seems very unlikely that if you have a valid device that it wouldn't have drvdata set up.

drivers/gpu/drm/msm/dp/dp_display.c

Quoted 61 lines
drivers/gpu/drm/msm/dp/dp_display.c new file +61 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

As above - these checks to not seem useful.

drivers/gpu/drm/msm/dp/dp_display.c

Quoted 151 lines
drivers/gpu/drm/msm/dp/dp_display.c new file +151 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

yo udon not need rc - just return 0;

drivers/gpu/drm/msm/dp/dp_display.c

Quoted 48 lines
drivers/gpu/drm/msm/dp/dp_display.c new file +48 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

return 0;

drivers/gpu/drm/msm/dp/dp_display.c

Quoted 14 lines
drivers/gpu/drm/msm/dp/dp_display.c new file +14 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

return 0;

drivers/gpu/drm/msm/dp/dp_display.c

Quoted 11 lines
drivers/gpu/drm/msm/dp/dp_display.c new file +11 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

but you don't actually return a error.

drivers/gpu/drm/msm/dp/dp_display.c

Quoted 8 lines
drivers/gpu/drm/msm/dp/dp_display.c new file +8 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

rc is unused - you can return 0 here.

drivers/gpu/drm/msm/dp/dp_display.c

Quoted 104 lines
drivers/gpu/drm/msm/dp/dp_display.c new file +104 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

This could all be return (dp_display->panel->video_test);

drivers/gpu/drm/msm/dp/dp_display.c

Quoted 40 lines
drivers/gpu/drm/msm/dp/dp_display.c new file +40 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

You do not need to call devm_kfree out of a probe function.

drivers/gpu/drm/msm/dp/dp_display.c

Quoted 13 lines
drivers/gpu/drm/msm/dp/dp_display.c new file +13 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

Same

drivers/gpu/drm/msm/dp/dp_display.c

Quoted 11 lines
drivers/gpu/drm/msm/dp/dp_display.c new file +11 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

Not possible.

drivers/gpu/drm/msm/dp/dp_display.c

Quoted 6 lines
drivers/gpu/drm/msm/dp/dp_display.c new file +6 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

You do not need to call devm_kfree in a release function

drivers/gpu/drm/msm/dp/dp_display.c

Quoted 21 lines
drivers/gpu/drm/msm/dp/dp_display.c new file +21 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

It would be helpful to say which driver register failed.

drivers/gpu/drm/msm/dp/dp_display.c

Quoted 1 line
drivers/gpu/drm/msm/dp/dp_display.c new file +1 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

You don't need this - you can drop out and return ret at the bottom of the function.

drivers/gpu/drm/msm/dp/dp_display.c

Quoted 18 lines
drivers/gpu/drm/msm/dp/dp_display.c new file +18 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

This could be WARN_ON(!encoder || !dp_display || !dev)

drivers/gpu/drm/msm/dp/dp_drm.c

Quoted 159 lines
drivers/gpu/drm/msm/dp/dp_display.c new file +113 -0

Lines of this file are not shown. View the raw message.

drivers/gpu/drm/msm/dp/dp_display.h new file +28 -0
new file mode 100644
index 0000000..dad8610

Lines of this file are not shown. View the raw message.

drivers/gpu/drm/msm/dp/dp_drm.c new file +6 -0
new file mode 100644
index 0000000..740fc6d

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

pr_ does not appear to be used in this file.

drivers/gpu/drm/msm/dp/dp_drm.c

Quoted 14 lines
drivers/gpu/drm/msm/dp/dp_drm.c new file +14 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

I believe we prefer these to be functions for typechecking purposes.

drivers/gpu/drm/msm/dp/dp_drm.c

Quoted 39 lines
drivers/gpu/drm/msm/dp/dp_drm.c new file +39 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

Curious - why do you need to allocate dp_mode on the heap. Is it huge?

drivers/gpu/drm/msm/dp/dp_drm.c

Quoted 15 lines
drivers/gpu/drm/msm/dp/dp_drm.c new file +15 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

Did you forget to kfree dp_mode here?

drivers/gpu/drm/msm/dp/dp_drm.c

Quoted 41 lines
drivers/gpu/drm/msm/dp/dp_drm.c new file +41 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

This is a callback - it doesn't seem likely you'll be called with NULL variables here.

drivers/gpu/drm/msm/dp/dp_hpd.c

Quoted 151 lines
drivers/gpu/drm/msm/dp/dp_drm.c new file +61 -0

Lines of this file are not shown. View the raw message.

drivers/gpu/drm/msm/dp/dp_drm.h new file +19 -0
new file mode 100644
index 0000000..a4ffdef

Lines of this file are not shown. View the raw message.

drivers/gpu/drm/msm/dp/dp_hpd.c new file +59 -0
new file mode 100644
index 0000000..a3d99b8

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

return ERR_PTR(-EINVAL);

drivers/gpu/drm/msm/dp/dp_hpd.c

Quoted 4 lines
drivers/gpu/drm/msm/dp/dp_hpd.c new file +4 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

return ERR_PTR(-ENOMEM);

And then it doesn't appear like you need rc anymore.

drivers/gpu/drm/msm/dp/dp_hpd.c

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_hpd.c new file +10 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

return &dp_hdp->dp_usbpd and you don't need dp_usbpd;

drivers/gpu/drm/msm/dp/dp_hpd.c

Quoted 13 lines
drivers/gpu/drm/msm/dp/dp_hpd.c new file +13 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

You almost never need to call devm_kfree and as a result, it appears this function doesn't have much value.

drivers/gpu/drm/msm/dp/dp_link.c

Quoted 353 lines
drivers/gpu/drm/msm/dp/dp_hpd.c new file +1 -0

Lines of this file are not shown. View the raw message.

drivers/gpu/drm/msm/dp/dp_hpd.h new file +81 -0
new file mode 100644
index 0000000..08ae8aa

Lines of this file are not shown. View the raw message.

drivers/gpu/drm/msm/dp/dp_link.c new file +259 -0
new file mode 100644
index 0000000..763a4b4

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

You don't need t formally document static functions

drivers/gpu/drm/msm/dp/dp_link.c

Quoted 760 lines
drivers/gpu/drm/msm/dp/dp_link.c new file +760 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

I'm not sure what the whole struct looks like but maybe a memset() would be more useful here?

drivers/gpu/drm/msm/dp/dp_link.c

Quoted 193 lines
drivers/gpu/drm/msm/dp/dp_link.c new file +193 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

return ERR_PTR(-EINVAL);

drivers/gpu/drm/msm/dp/dp_link.c

Quoted 7 lines
drivers/gpu/drm/msm/dp/dp_link.c new file +7 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

return ERR_PTR(-ENOMEM)

drivers/gpu/drm/msm/dp/dp_link.c

Quoted 14 lines
drivers/gpu/drm/msm/dp/dp_link.c new file +14 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

Not needed, and neither is rc.

drivers/gpu/drm/msm/dp/dp_link.c

Quoted 12 lines
drivers/gpu/drm/msm/dp/dp_link.c new file +12 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

Since you probably don't need to call devm_kfree here, this whole function isn't needed.

drivers/gpu/drm/msm/dp/dp_panel.c

Quoted 159 lines
drivers/gpu/drm/msm/dp/dp_link.c new file +1 -0

Lines of this file are not shown. View the raw message.

drivers/gpu/drm/msm/dp/dp_link.h new file +140 -0
new file mode 100644
index 0000000..7d7866d

Lines of this file are not shown. View the raw message.

drivers/gpu/drm/msm/dp/dp_panel.c new file +6 -0
new file mode 100644
index 0000000..1e686c1

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

I didn't see any uses of pr_ in this file

drivers/gpu/drm/msm/dp/dp_panel.c

Quoted 456 lines
drivers/gpu/drm/msm/dp/dp_panel.c new file +456 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

return ERR_PTR(-EINVAL);

drivers/gpu/drm/msm/dp/dp_panel.c

Quoted 7 lines
drivers/gpu/drm/msm/dp/dp_panel.c new file +7 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

return ERR_PTR(-ENOMEM)

drivers/gpu/drm/msm/dp/dp_panel.c

Quoted 16 lines
drivers/gpu/drm/msm/dp/dp_panel.c new file +16 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

Not needed

drivers/gpu/drm/msm/dp/dp_panel.c

Quoted 14 lines
drivers/gpu/drm/msm/dp/dp_panel.c new file +14 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

You do not need to call devm_kfree here nor do you need to set dp_panel_edid = NULL since that memory will soon disappear.

drivers/gpu/drm/msm/dp/dp_parser.c

Quoted 114 lines
drivers/gpu/drm/msm/dp/dp_panel.c new file +1 -0

Lines of this file are not shown. View the raw message.

drivers/gpu/drm/msm/dp/dp_panel.h new file +95 -0
new file mode 100644
index 0000000..85e61a8

Lines of this file are not shown. View the raw message.

drivers/gpu/drm/msm/dp/dp_parser.c new file +6 -0
new file mode 100644
index 0000000..8b7518b

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

I didn't see pr_ used here.

drivers/gpu/drm/msm/dp/dp_parser.c

Quoted 23 lines
drivers/gpu/drm/msm/dp/dp_parser.c new file +23 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

It looks like none of the callers are calling this function with a NULL for io_data so this isn't needed.

drivers/gpu/drm/msm/dp/dp_parser.c

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_parser.c new file +10 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

Why not devm - is this function ever called for regions that don't exist for the entire device lifespan?

drivers/gpu/drm/msm/dp/dp_parser.c

Quoted 14 lines
drivers/gpu/drm/msm/dp/dp_parser.c new file +14 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

This error message isn't useful - you don't care if they give you a NULL function for a unmap function and it looks like none of the callers ever will.

drivers/gpu/drm/msm/dp/dp_parser.c

Quoted 44 lines
drivers/gpu/drm/msm/dp/dp_parser.c new file +44 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

msm_dss_ioremap_byname returns an error message on each failure path so you do not need an addtional one here (and throughout this function).

drivers/gpu/drm/msm/dp/dp_parser.c

Quoted 67 lines
drivers/gpu/drm/msm/dp/dp_parser.c new file +67 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

static const char * const ?

drivers/gpu/drm/msm/dp/dp_parser.c

Quoted 69 lines
drivers/gpu/drm/msm/dp/dp_parser.c new file +69 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

memset(parser->aux_cfg, 0, sizeof(parser->aux_cfg)) ?

drivers/gpu/drm/msm/dp/dp_parser.c

Quoted 13 lines
drivers/gpu/drm/msm/dp/dp_parser.c new file +13 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

of_property_read_u32 does not modify the value of the pointer on error, so you can safely do this:

parser->max_pclk_khz = DP_MAX_PIXEL_CLK_KHZ; of_property_read_u32(of_node, "qcom,max-pclk-frequency-khz",

	&parser->max_pclk_khz);

drivers/gpu/drm/msm/dp/dp_parser.c

Quoted 7 lines
drivers/gpu/drm/msm/dp/dp_parser.c new file +7 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

Same.

drivers/gpu/drm/msm/dp/dp_parser.c

Quoted 1 line
drivers/gpu/drm/msm/dp/dp_parser.c new file +1 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

This function doesn't need to return an error.

drivers/gpu/drm/msm/dp/dp_parser.c

Quoted 9 lines
drivers/gpu/drm/msm/dp/dp_parser.c new file +9 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

devm_pinctrl_get never returns NULL so you just use IS_ERR()

drivers/gpu/drm/msm/dp/dp_parser.c

Quoted 1 line
drivers/gpu/drm/msm/dp/dp_parser.c new file +1 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

ERR_PTR always returns an unsigned long so this should be rc=%ld

drivers/gpu/drm/msm/dp/dp_parser.c

Quoted 1 line
drivers/gpu/drm/msm/dp/dp_parser.c new file +1 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

And here you can use ERR_PTR()

drivers/gpu/drm/msm/dp/dp_parser.c

Quoted 6 lines
drivers/gpu/drm/msm/dp/dp_parser.c new file +6 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

Same as above - this only returns IS_ERR() on error

drivers/gpu/drm/msm/dp/dp_parser.c

Quoted 2 lines
drivers/gpu/drm/msm/dp/dp_parser.c new file +2 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

Same as above

drivers/gpu/drm/msm/dp/dp_parser.c

Quoted 6 lines
drivers/gpu/drm/msm/dp/dp_parser.c new file +6 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

Same as above

drivers/gpu/drm/msm/dp/dp_parser.c

Quoted 2 lines
drivers/gpu/drm/msm/dp/dp_parser.c new file +2 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

Same as above.

drivers/gpu/drm/msm/dp/dp_parser.c

Quoted 65 lines
drivers/gpu/drm/msm/dp/dp_parser.c new file +63 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

This is very unlikely since you already queried the number of clock names.

drivers/gpu/drm/msm/dp/dp_parser.c

Quoted 18 lines
drivers/gpu/drm/msm/dp/dp_parser.c new file +18 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

This looks like it should be devm_kcalloc()

drivers/gpu/drm/msm/dp/dp_parser.c

Quoted 13 lines
drivers/gpu/drm/msm/dp/dp_parser.c new file +13 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

devm_kcalloc()

drivers/gpu/drm/msm/dp/dp_parser.c

Quoted 133 lines
drivers/gpu/drm/msm/dp/dp_parser.c new file +132 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

Doesn't seem like you even need to bother defining this.

drivers/gpu/drm/msm/dp/dp_power.c

Quoted 367 lines
drivers/gpu/drm/msm/dp/dp_parser.h new file +224 -0
new file mode 100644
index 0000000..1dc592f

Lines of this file are not shown. View the raw message.

drivers/gpu/drm/msm/dp/dp_power.c new file +131 -0
new file mode 100644
index 0000000..593b869

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

The other functions use DRM_ERROR instead

drivers/gpu/drm/msm/dp/dp_power.c

Quoted 40 lines
drivers/gpu/drm/msm/dp/dp_power.c new file +40 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

Not a useful log message on a de-init path

drivers/gpu/drm/msm/dp/dp_power.c

Quoted 360 lines
drivers/gpu/drm/msm/dp/dp_power.c new file +360 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

return ERR_PTR(-EINVAL);

drivers/gpu/drm/msm/dp/dp_power.c

Quoted 7 lines
drivers/gpu/drm/msm/dp/dp_power.c new file +7 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

return ERR_PTR(-ENOMEM)

drivers/gpu/drm/msm/dp/dp_power.c

Quoted 12 lines
drivers/gpu/drm/msm/dp/dp_power.c new file +12 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

Not needed

drivers/gpu/drm/msm/dp/dp_power.c

Quoted 12 lines
drivers/gpu/drm/msm/dp/dp_power.c new file +12 -0

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

devm_kfree is not needed here and the rest of the function likely isn't either.

Quoted 666 lines
drivers/gpu/drm/msm/dp/dp_power.c new file +1 -0

Lines of this file are not shown. View the raw message.

drivers/gpu/drm/msm/dp/dp_power.h new file +52 -0
new file mode 100644
index 0000000..52b6194

Lines of this file are not shown. View the raw message.

drivers/gpu/drm/msm/dp/dp_reg.h new file +489 -0
new file mode 100644
index 0000000..28c1682

Lines of this file are not shown. View the raw message.

drivers/gpu/drm/msm/msm_drv.c +2 -0
index f50fefb..4b8bd2b 100644

Lines of this file are not shown. View the raw message.

drivers/gpu/drm/msm/msm_drv.h +50 -3
index 71547e7..fc4fda1 100644

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

Signature · 6 lines
$(echo -e 'The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project')
_______________________________________________
Freedreno mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/freedreno
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

Part of the diff is not shown here. View the raw message.

author
#8 lore raw

PATCH v4 4/5 drm/msm/dp: add support for DP PLL driver

In reply to Vara Reddy

From: Chandan Uddaraju <[email protected]>

Add the needed DP PLL specific files to support
display port interface on msm targets.

The DP driver calls the DP PLL driver registration. The DP driver sets the link and pixel clock sources.

Changes in v2:
-- Update copyright markings on all relevant files. -- Use DRM_DEBUG_DP for debug msgs.

Changes in v4:
-- Update the DP link clock provider names

---
 drivers/gpu/drm/msm/Kconfig                   |  13 +
 drivers/gpu/drm/msm/Makefile                  |   4 +
 drivers/gpu/drm/msm/dp/dp_display.c           |  51 +++
 drivers/gpu/drm/msm/dp/dp_display.h           |   3 +
 drivers/gpu/drm/msm/dp/dp_parser.h            |   4 +
 drivers/gpu/drm/msm/dp/dp_power.h             |   1 -
 drivers/gpu/drm/msm/dp/pll/dp_pll.c           | 136 +++++++
 drivers/gpu/drm/msm/dp/pll/dp_pll.h           |  57 +++
 drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c      | 406 ++++++++++++++++++++
 drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.h      |  86 +++++
 drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c | 524 ++++++++++++++++++++++++++
 11 files changed, 1284 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/msm/dp/pll/dp_pll.c
 create mode 100644 drivers/gpu/drm/msm/dp/pll/dp_pll.h
 create mode 100644 drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c
 create mode 100644 drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.h
 create mode 100644 drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c
drivers/gpu/drm/msm/Kconfig +13 -0
index 7946cb1..e73ad23 100644
@@ -66,6 +66,19 @@ config DRM_MSM_DP
 	  display support is enabled through this config option. It can
 	  be primary or secondary display on device.
 
+config DRM_MSM_DP_PLL
+	bool "Enable DP PLL driver in MSM DRM"
+	depends on DRM_MSM_DP && COMMON_CLK
+	help
+	  Choose this option to enable DP PLL driver which provides DP
+	  source clocks under common clock framework.
+
+config DRM_MSM_DP_10NM_PLL
+	bool "Enable DP 10nm PLL driver in MSM DRM (used by SDM845)"
+	depends on DRM_MSM_DP_PLL
+	help
+	  Choose this option if DP PLL on SDM845 is used on the platform.
+
 config DRM_MSM_DSI
 	bool "Enable DSI support in MSM DRM driver"
 	depends on DRM_MSM
drivers/gpu/drm/msm/Makefile +4 -0
index c846599..1621f3f 100644
@@ -140,4 +140,8 @@ msm-$(CONFIG_DRM_MSM_DSI_14NM_PHY) += dsi/pll/dsi_pll_14nm.o
 msm-$(CONFIG_DRM_MSM_DSI_10NM_PHY) += dsi/pll/dsi_pll_10nm.o
 endif
 
+msm-$(CONFIG_DRM_MSM_DP_PLL)+= dp/pll/dp_pll.o
+msm-$(CONFIG_DRM_MSM_DP_10NM_PLL)+= dp/pll/dp_pll_10nm.o \
+	dp/pll/dp_pll_10nm_util.o
+
 obj-$(CONFIG_DRM_MSM)	+= msm.o
drivers/gpu/drm/msm/dp/dp_display.c +51 -0

75 lines not on this page · raw message

drivers/gpu/drm/msm/dp/dp_display.h +3 -0
index dad8610..4c53ed5 100644
@@ -25,4 +25,7 @@ int dp_display_get_modes(struct msm_dp *dp_display,
 bool dp_display_check_video_test(struct msm_dp *dp_display);
 int dp_display_get_test_bpp(struct msm_dp *dp_display);
 
+void __init msm_dp_pll_driver_register(void);
+void __exit msm_dp_pll_driver_unregister(void);
+
 #endif /* _DP_DISPLAY_H_ */
drivers/gpu/drm/msm/dp/dp_parser.h +4 -0
index 1dc592f..e775cbc 100644
@@ -6,6 +6,8 @@
 #ifndef _DP_PARSER_H_
 #define _DP_PARSER_H_
 
+#include "pll/dp_pll.h"
+
 #define DP_LABEL "MDSS DP DISPLAY"
 #define AUX_CFG_LEN	10
 #define DP_MAX_PIXEL_CLK_KHZ	675000
@@ -193,6 +195,8 @@ struct dp_parser {
 	bool combo_phy_en;
 	struct dp_io io;
 	struct dp_display_data disp_data;
+	struct msm_dp_pll *pll;
+	struct device *pll_dev;
 	const struct dp_regulator_cfg *regulator_cfg;
 	u8 l_map[4];
 	struct dp_aux_cfg aux_cfg[AUX_CFG_LEN];
drivers/gpu/drm/msm/dp/dp_power.h +0 -1
index 52b6194..b24b2c5 100644
@@ -14,7 +14,6 @@
  * @init: initializes the regulators/core clocks/GPIOs/pinctrl
  * @deinit: turns off the regulators/core clocks/GPIOs/pinctrl
  * @clk_enable: enable/disable the DP clocks
- * @set_link_clk_parent: set the parent of DP link clock
  * @set_pixel_clk_parent: set the parent of DP pixel clock
  */
 struct dp_power {
drivers/gpu/drm/msm/dp/pll/dp_pll.c new file +136 -0

136 lines not on this page · raw message

drivers/gpu/drm/msm/dp/pll/dp_pll.h new file +57 -0

57 lines not on this page · raw message

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +406 -0

406 lines not on this page · raw message

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.h new file +86 -0

86 lines not on this page · raw message

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +524 -0

524 lines not on this page · raw message

Signature · 2 lines
$(echo -e 'The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project')
#9 lore raw

Re: [DPU PATCH v4 4/5] drm/msm/dp: add support for DP PLL driver

Quoting Vara Reddy (2020-03-04 16:10:27)

Quoted 80 lines

From: Chandan Uddaraju <[email protected]>

Add the needed DP PLL specific files to support
display port interface on msm targets.

The DP driver calls the DP PLL driver registration. The DP driver sets the link and pixel clock sources.

Changes in v2:
-- Update copyright markings on all relevant files. -- Use DRM_DEBUG_DP for debug msgs.

Changes in v4:
-- Update the DP link clock provider names

---
 drivers/gpu/drm/msm/Kconfig                   |  13 +
 drivers/gpu/drm/msm/Makefile                  |   4 +
 drivers/gpu/drm/msm/dp/dp_display.c           |  51 +++
 drivers/gpu/drm/msm/dp/dp_display.h           |   3 +
 drivers/gpu/drm/msm/dp/dp_parser.h            |   4 +
 drivers/gpu/drm/msm/dp/dp_power.h             |   1 -
 drivers/gpu/drm/msm/dp/pll/dp_pll.c           | 136 +++++++
 drivers/gpu/drm/msm/dp/pll/dp_pll.h           |  57 +++
 drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c      | 406 ++++++++++++++++++++
 drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.h      |  86 +++++
 drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c | 524 ++++++++++++++++++++++++++
 11 files changed, 1284 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/msm/dp/pll/dp_pll.c
 create mode 100644 drivers/gpu/drm/msm/dp/pll/dp_pll.h
 create mode 100644 drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c
 create mode 100644 drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.h
 create mode 100644 drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c
drivers/gpu/drm/msm/Kconfig +13 -0
index 7946cb1..e73ad23 100644
@@ -66,6 +66,19 @@ config DRM_MSM_DP
          display support is enabled through this config option. It can
          be primary or secondary display on device.
 
+config DRM_MSM_DP_PLL
+       bool "Enable DP PLL driver in MSM DRM"
+       depends on DRM_MSM_DP && COMMON_CLK
+       help
+         Choose this option to enable DP PLL driver which provides DP
+         source clocks under common clock framework.
+
+config DRM_MSM_DP_10NM_PLL
+       bool "Enable DP 10nm PLL driver in MSM DRM (used by SDM845)"
+       depends on DRM_MSM_DP_PLL
+       help
+         Choose this option if DP PLL on SDM845 is used on the platform.
+
 config DRM_MSM_DSI
        bool "Enable DSI support in MSM DRM driver"
        depends on DRM_MSM
drivers/gpu/drm/msm/Makefile +4 -0
index c846599..1621f3f 100644
@@ -140,4 +140,8 @@ msm-$(CONFIG_DRM_MSM_DSI_14NM_PHY) += dsi/pll/dsi_pll_14nm.o
 msm-$(CONFIG_DRM_MSM_DSI_10NM_PHY) += dsi/pll/dsi_pll_10nm.o
 endif
 
+msm-$(CONFIG_DRM_MSM_DP_PLL)+= dp/pll/dp_pll.o
+msm-$(CONFIG_DRM_MSM_DP_10NM_PLL)+= dp/pll/dp_pll_10nm.o \
+       dp/pll/dp_pll_10nm_util.o
+
 obj-$(CONFIG_DRM_MSM)  += msm.o
drivers/gpu/drm/msm/dp/dp_display.c +0 -0
index 5525405..ded8f5c 100644
@@ -61,6 +61,51 @@ struct dp_display_private {
        {}
 };

drivers/gpu/drm/msm/dp/dp_display.c struct dp_display_private { · line 72

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_display.c +9 -0
 
+static int dp_get_pll(struct dp_display_private *dp_priv)
+{
+       struct platform_device *pdev = NULL;
+       struct platform_device *pll_pdev;
+       struct device_node *pll_node;
+       struct dp_parser *dp_parser = NULL;
+
+       if (!dp_priv) {
+               DRM_ERROR("Invalid Arguments\n");

It's a static function. Is this even possible? The bad code would be in the same file.

Quoted 105 lines
drivers/gpu/drm/msm/dp/dp_display.c +42 -0
+               return -EINVAL;
+       }
+
+       pdev = dp_priv->pdev;
+       dp_parser = dp_priv->parser;
+
+       if (!dp_parser) {
+               DRM_DEV_ERROR(&pdev->dev, "%s: Parser not initialized\n",
+                               __func__);
+               return -EINVAL;
+       }
+
+       pll_node = of_parse_phandle(pdev->dev.of_node, "pll-node", 0);
+       if (!pll_node) {
+               DRM_DEV_ERROR(&pdev->dev, "%s: cannot find pll device\n",
+                               __func__);
+               return -ENXIO;
+       }
+
+       pll_pdev = of_find_device_by_node(pll_node);
+       if (pll_pdev)
+               dp_parser->pll = platform_get_drvdata(pll_pdev);
+
+       of_node_put(pll_node);
+
+       if (!pll_pdev || !dp_parser->pll) {
+               DRM_DEV_ERROR(&pdev->dev, "%s: pll driver is not ready\n",
+                               __func__);
+               return -EPROBE_DEFER;
+       }
+
+       dp_parser->pll_dev = get_device(&pll_pdev->dev);
+
+       return 0;
+}
+
 static irqreturn_t dp_display_irq(int irq, void *dev_id)
 {
        struct dp_display_private *dp = dev_id;
@@ -114,6 +159,10 @@ static int dp_display_bind(struct device *dev, struct device *master,
                goto end;
        }
 
+       rc = dp_get_pll(dp);
+       if (rc)
+               goto end;
+
        rc = dp_aux_register(dp->aux);
        if (rc) {
                DRM_ERROR("DRM DP AUX register failed\n");
@@ -837,6 +886,7 @@ int __init msm_dp_register(void)
 {
        int ret;
 
+       msm_dp_pll_driver_register();
        ret = platform_driver_register(&dp_display_driver);
        if (ret) {
                DRM_ERROR("driver register failed");
@@ -848,6 +898,7 @@ int __init msm_dp_register(void)
 
 void __exit msm_dp_unregister(void)
 {
+       msm_dp_pll_driver_unregister();
        platform_driver_unregister(&dp_display_driver);
 }
 
drivers/gpu/drm/msm/dp/dp_display.h +3 -0
index dad8610..4c53ed5 100644
@@ -25,4 +25,7 @@ int dp_display_get_modes(struct msm_dp *dp_display,
 bool dp_display_check_video_test(struct msm_dp *dp_display);
 int dp_display_get_test_bpp(struct msm_dp *dp_display);
 
+void __init msm_dp_pll_driver_register(void);
+void __exit msm_dp_pll_driver_unregister(void);
+
 #endif /* _DP_DISPLAY_H_ */
drivers/gpu/drm/msm/dp/dp_parser.h +4 -0
index 1dc592f..e775cbc 100644
@@ -6,6 +6,8 @@
 #ifndef _DP_PARSER_H_
 #define _DP_PARSER_H_
 
+#include "pll/dp_pll.h"
+
 #define DP_LABEL "MDSS DP DISPLAY"
 #define AUX_CFG_LEN    10
 #define DP_MAX_PIXEL_CLK_KHZ   675000
@@ -193,6 +195,8 @@ struct dp_parser {
        bool combo_phy_en;
        struct dp_io io;
        struct dp_display_data disp_data;
+       struct msm_dp_pll *pll;
+       struct device *pll_dev;
        const struct dp_regulator_cfg *regulator_cfg;
        u8 l_map[4];
        struct dp_aux_cfg aux_cfg[AUX_CFG_LEN];
drivers/gpu/drm/msm/dp/dp_power.h +0 -0
index 52b6194..b24b2c5 100644

drivers/gpu/drm/msm/dp/dp_power.h line 19

Quoted 7 lines
drivers/gpu/drm/msm/dp/dp_power.h +0 -1
@@ -14,7 +14,6 @@
  * @init: initializes the regulators/core clocks/GPIOs/pinctrl
  * @deinit: turns off the regulators/core clocks/GPIOs/pinctrl
  * @clk_enable: enable/disable the DP clocks
- * @set_link_clk_parent: set the parent of DP link clock
  * @set_pixel_clk_parent: set the parent of DP pixel clock
  */
 struct dp_power {

This can be another patch to update documentation?

Quoted 20 lines
drivers/gpu/drm/msm/dp/pll/dp_pll.c new file +14 -0
new file mode 100644
index 0000000..11e26cf
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
+ */
+
+#include "dp_pll.h"
+
+int msm_dp_pll_util_parse_dt_clock(struct platform_device *pdev,
+                                       struct msm_dp_pll *pll)
+{
+       u32 i = 0, rc = 0;
+       struct dss_module_power *mp = &pll->mp;
+       const char *clock_name;
+       u32 clock_rate;

drivers/gpu/drm/msm/dp/pll/dp_pll.c line 24

Quoted 10 lines
drivers/gpu/drm/msm/dp/pll/dp_pll.c new file +10 -0
+
+       mp->num_clk = of_property_count_strings(pdev->dev.of_node,
+                                                       "clock-names");
+       if (mp->num_clk <= 0) {
+               DRM_DEV_ERROR(&pdev->dev, "%s:clocks are not defined\n",
+                               __func__);
+               goto clk_err;
+       }
+
+       mp->clk_config = devm_kzalloc(&pdev->dev,

Use devm_kcalloc() for arrays.

Quoted 46 lines
drivers/gpu/drm/msm/dp/pll/dp_pll.c new file +46 -0
+                       sizeof(struct dss_clk) * mp->num_clk, GFP_KERNEL);
+       if (!mp->clk_config) {
+               rc = -ENOMEM;
+               mp->num_clk = 0;
+               goto clk_err;
+       }
+
+       for (i = 0; i < mp->num_clk; i++) {
+               of_property_read_string_index(pdev->dev.of_node, "clock-names",
+                                                       i, &clock_name);
+               strlcpy(mp->clk_config[i].clk_name, clock_name,
+                               sizeof(mp->clk_config[i].clk_name));
+
+               of_property_read_u32_index(pdev->dev.of_node, "clock-rate",
+                                                       i, &clock_rate);
+               mp->clk_config[i].rate = clock_rate;
+
+               if (!clock_rate)
+                       mp->clk_config[i].type = DSS_CLK_AHB;
+               else
+                       mp->clk_config[i].type = DSS_CLK_PCLK;
+       }
+
+clk_err:
+       return rc;
+}
+
+struct msm_dp_pll *msm_dp_pll_init(struct platform_device *pdev,
+                       enum msm_dp_pll_type type, int id)
+{
+       struct device *dev = &pdev->dev;
+       struct msm_dp_pll *pll;
+
+       switch (type) {
+       case MSM_DP_PLL_10NM:
+               pll = msm_dp_pll_10nm_init(pdev, id);
+               break;
+       default:
+               pll = ERR_PTR(-ENXIO);
+               break;
+       }
+
+       if (IS_ERR(pll)) {
+               DRM_DEV_ERROR(dev, "%s: failed to init DP PLL\n", __func__);
+               return pll;
+       }

drivers/gpu/drm/msm/dp/pll/dp_pll.c line 80

Quoted 10 lines
drivers/gpu/drm/msm/dp/pll/dp_pll.c new file +10 -0
+
+       pll->type = type;
+
+       DRM_DEBUG_DP("DP:%d PLL registered", id);
+
+       return pll;
+}
+
+static const struct of_device_id dp_pll_dt_match[] = {
+       { .compatible = "qcom,dp-pll-10nm" },

Add a terminator here { }.

drivers/gpu/drm/msm/dp/pll/dp_pll.c line 93

Quoted 13 lines
drivers/gpu/drm/msm/dp/pll/dp_pll.c new file +13 -0
+};
+
+static int dp_pll_driver_probe(struct platform_device *pdev)
+{
+       struct msm_dp_pll *pll;
+       struct device *dev = &pdev->dev;
+       const struct of_device_id *match;
+
+       match = of_match_node(dp_pll_dt_match, dev->of_node);
+       if (!match)
+               return -ENODEV;
+
+       /* Currently supporting only 10nm-DP PLL */

Please remove this useless comment that can only require being updated in the future.

drivers/gpu/drm/msm/dp/pll/dp_pll.c line 97

Quoted 4 lines
drivers/gpu/drm/msm/dp/pll/dp_pll.c new file +4 -0
+       pll = msm_dp_pll_init(pdev, MSM_DP_PLL_10NM, 0);
+       if (IS_ERR_OR_NULL(pll)) {
+               DRM_DEV_ERROR(dev,
+                       "%s: pll init failed: %ld, need separate pll clk driver\n",

What does this error mean?

Quoted 45 lines
drivers/gpu/drm/msm/dp/pll/dp_pll.c new file +39 -0
+                       __func__, PTR_ERR(pll));
+               return -ENODEV;
+       }
+
+       platform_set_drvdata(pdev, pll);
+
+       return 0;
+}
+
+static int dp_pll_driver_remove(struct platform_device *pdev)
+{
+       struct msm_dp_pll *pll = platform_get_drvdata(pdev);
+
+       if (pll)
+               pll = NULL;
+
+       platform_set_drvdata(pdev, NULL);
+
+       return 0;
+}
+
+static struct platform_driver dp_pll_platform_driver = {
+       .probe      = dp_pll_driver_probe,
+       .remove     = dp_pll_driver_remove,
+       .driver     = {
+               .name   = "msm_dp_pll",
+               .of_match_table = dp_pll_dt_match,
+       },
+};
+
+void __init msm_dp_pll_driver_register(void)
+{
+       platform_driver_register(&dp_pll_platform_driver);
+}
+
+void __exit msm_dp_pll_driver_unregister(void)
+{
+       platform_driver_unregister(&dp_pll_platform_driver);
+}
drivers/gpu/drm/msm/dp/pll/dp_pll.h new file +0 -0
new file mode 100644
index 0000000..185f1e9

drivers/gpu/drm/msm/dp/pll/dp_pll.h line 10

Quoted 10 lines
drivers/gpu/drm/msm/dp/pll/dp_pll.h new file +10 -0
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef __DP_PLL_H
+#define __DP_PLL_H
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>

You're making a clk provider. Please Cc clk maintainers and [email protected]

Quoted 11 lines
drivers/gpu/drm/msm/dp/pll/dp_pll.h new file +11 -0
+#include <linux/platform_device.h>
+
+#include "dpu_io_util.h"
+#include "msm_drv.h"
+
+#define PLL_REG_W(base, offset, data)  \
+                               writel((data), (base) + (offset))
+#define PLL_REG_R(base, offset)        readl((base) + (offset))
+
+enum msm_dp_pll_type {
+       MSM_DP_PLL_10NM,

drivers/gpu/drm/msm/dp/pll/dp_pll.h line 31

Quoted 10 lines
drivers/gpu/drm/msm/dp/pll/dp_pll.h new file +10 -0
+       MSM_DP_PLL_MAX
+};
+
+struct msm_dp_pll {
+       enum msm_dp_pll_type type;
+       struct clk_hw clk_hw;
+       unsigned long   rate;           /* current vco rate */
+       u64             min_rate;       /* min vco rate */
+       u64             max_rate;       /* max vco rate */
+       bool            pll_on;

Are these struct members here to save/restore the rate across suspend/resume?

drivers/gpu/drm/msm/dp/pll/dp_pll.h line 40

Quoted 9 lines
drivers/gpu/drm/msm/dp/pll/dp_pll.h new file +9 -0
+       void            *priv;
+       /* Pll specific resources like GPIO, power supply, clocks, etc*/
+       struct dss_module_power mp;
+       int (*get_provider)(struct msm_dp_pll *pll,
+                       struct clk **link_clk_provider,
+                       struct clk **pixel_clk_provider);
+};
+
+#define hw_clk_to_pll(x) container_of(x, struct msm_dp_pll, clk_hw)

Why not to_msm_dp_pll() for this macro.

drivers/gpu/drm/msm/dp/pll/dp_pll.h line 52

Quoted 12 lines
drivers/gpu/drm/msm/dp/pll/dp_pll.h new file +12 -0
+
+struct msm_dp_pll *msm_dp_pll_init(struct platform_device *pdev,
+                               enum msm_dp_pll_type type, int id);
+
+int msm_dp_pll_util_parse_dt_clock(struct platform_device *pdev,
+                               struct msm_dp_pll *pll);
+
+#ifdef CONFIG_DRM_MSM_DP_10NM_PLL
+struct msm_dp_pll *msm_dp_pll_10nm_init(struct platform_device *pdev, int id);
+#else
+static inline struct msm_dp_pll *msm_dp_pll_10nm_init
+                               (struct platform_device *pdev, int id)

I would write this as

static inline struct msm_dp_pll * msm_dp_pll_10nm_init(struct platform_device *pdev, int id)

so that we don't have parameters all by themselves.

Quoted 55 lines
drivers/gpu/drm/msm/dp/pll/dp_pll.h new file +5 -0
+{
+       return ERR_PTR(-ENODEV);
+}
+#endif
+#endif /* __DP_PLL_H */
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +44 -0
new file mode 100644
index 0000000..5991e09
@@ -0,0 +1,406 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
+ */
+
+/*
+ * Display Port PLL driver block diagram for branch clocks
+ *
+ *              +------------------------------+
+ *              |         DP_VCO_CLK           |
+ *              |                              |
+ *              |    +-------------------+     |
+ *              |    |   (DP PLL/VCO)    |     |
+ *              |    +---------+---------+     |
+ *              |              v               |
+ *              |   +----------+-----------+   |
+ *              |   | hsclk_divsel_clk_src |   |
+ *              |   +----------+-----------+   |
+ *              +------------------------------+
+ *                              |
+ *          +---------<---------v------------>----------+
+ *          |                                           |
+ * +--------v---------+                                 |
+ * |    dp_phy_pll    |                                 |
+ * |     link_clk     |                                 |
+ * +--------+---------+                                 |
+ *          |                                           |
+ *          |                                           |
+ *          v                                           v
+ * Input to DISPCC block                                |
+ * for link clk, crypto clk                             |
+ * and interface clock                                  |
+ *                                                      |
+ *                                                      |
+ *      +--------<------------+-----------------+---<---+
+ *      |                     |                 |
+ * +----v---------+  +--------v-----+  +--------v------+
+ * | vco_divided  |  | vco_divided  |  | vco_divided   |
+ * |    _clk_src  |  |    _clk_src  |  |    _clk_src   |
+ * |              |  |              |  |               |
+ * |divsel_six    |  |  divsel_two  |  |  divsel_four  |
+ * +-------+------+  +-----+--------+  +--------+------+
+ *         |                 |                  |
+ *         v---->----------v-------------<------v

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 54

Quoted 10 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +10 -0
+ *                         |
+ *              +----------+---------+
+ *              |   dp_phy_pll_vco   |
+ *              |       div_clk      |
+ *              +---------+----------+
+ *                        |
+ *                        v
+ *              Input to DISPCC block
+ *              for DP pixel clock
+ *

Awesome diagram!

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 57

Quoted 3 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +3 -0
+ */
+
+#include <linux/clk.h>

Please try to avoid making a clk provider a clk consumer at the same time.

Quoted 40 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +40 -0
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/regmap.h>
+
+#include "dp_pll_10nm.h"
+
+#define NUM_PROVIDED_CLKS              2
+
+#define DP_LINK_CLK_SRC                        0
+#define DP_PIXEL_CLK_SRC               1
+
+static struct dp_pll_10nm *dp_pdb;
+
+static const struct clk_ops dp_10nm_vco_clk_ops = {
+       .recalc_rate = dp_vco_recalc_rate_10nm,
+       .set_rate = dp_vco_set_rate_10nm,
+       .round_rate = dp_vco_round_rate_10nm,
+       .prepare = dp_vco_prepare_10nm,
+       .unprepare = dp_vco_unprepare_10nm,
+};
+
+struct dp_pll_10nm_pclksel {
+       struct clk_hw hw;
+
+       /* divider params */
+       u8 shift;
+       u8 width;
+       u8 flags; /* same flags as used by clk_divider struct */
+
+       struct dp_pll_10nm *pll;
+};
+#define to_pll_10nm_pclksel(_hw) \
+       container_of(_hw, struct dp_pll_10nm_pclksel, hw)
+
+static int dp_mux_set_parent_10nm(struct clk_hw *hw, u8 val)
+{
+       struct dp_pll_10nm_pclksel *pclksel = to_pll_10nm_pclksel(hw);
+       struct dp_pll_10nm *dp_res = pclksel->pll;
+       u32 auxclk_div;

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 107

Quoted 10 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +10 -0
+
+       auxclk_div = PLL_REG_R(dp_res->phy_base, REG_DP_PHY_VCO_DIV);
+       auxclk_div &= ~0x03;    /* bits 0 to 1 */
+
+       if (val == 0) /* mux parent index = 0 */
+               auxclk_div |= 1;
+       else if (val == 1) /* mux parent index = 1 */
+               auxclk_div |= 2;
+       else if (val == 2) /* mux parent index = 2 */
+               auxclk_div |= 0;

These comments are just saying what the code says. Can you remove them?

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 111

Quoted 4 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +4 -0
+
+       PLL_REG_W(dp_res->phy_base,
+                       REG_DP_PHY_VCO_DIV, auxclk_div);
+       DRM_DEBUG_DP("%s: mux=%d auxclk_div=%x\n", __func__, val, auxclk_div);

Is it a mux and divider combined? The diagram seems to imply that the mux picks between fixed dividers but I don't see why that isn't just a divider clk because the parent is always the same for the fixed dividers. Put another way, why is a parent API being used for this? It would make more sense to just treat this like a divider and use rate setting APIs instead.

Quoted 45 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +45 -0
+
+       return 0;
+}
+
+static u8 dp_mux_get_parent_10nm(struct clk_hw *hw)
+{
+       u32 auxclk_div = 0;
+       struct dp_pll_10nm_pclksel *pclksel = to_pll_10nm_pclksel(hw);
+       struct dp_pll_10nm *dp_res = pclksel->pll;
+       u8 val = 0;
+
+       auxclk_div = PLL_REG_R(dp_res->phy_base, REG_DP_PHY_VCO_DIV);
+       auxclk_div &= 0x03;
+
+       if (auxclk_div == 1) /* Default divider */
+               val = 0;
+       else if (auxclk_div == 2)
+               val = 1;
+       else if (auxclk_div == 0)
+               val = 2;
+
+       DRM_DEBUG_DP("%s: auxclk_div=%d, val=%d\n", __func__, auxclk_div, val);
+
+       return val;
+}
+
+static int clk_mux_determine_rate(struct clk_hw *hw,
+                                    struct clk_rate_request *req)
+{
+       unsigned long rate = 0;
+       int ret = 0;
+
+       rate = clk_get_rate(hw->clk);
+
+       if (rate <= 0) {
+               DRM_ERROR("Rate is not set properly\n");
+               return -EINVAL;
+       }
+
+       req->rate = rate;
+
+       DRM_DEBUG_DP("%s: rate=%ld\n", __func__, req->rate);
+       /* Set the new parent of mux if there is a new valid parent */
+       if (hw->clk && req->best_parent_hw->clk) {
+               ret = clk_set_parent(hw->clk, req->best_parent_hw->clk);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 166

Quoted 10 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +10 -0
+               if (ret) {
+                       DRM_ERROR("%s: clk_set_parent failed: ret=%d\n",
+                                       __func__, ret);
+                       return ret;
+               }
+       }
+       return 0;
+}
+
+static unsigned long mux_recalc_rate(struct clk_hw *hw,

Please prefix all functions with dp_pll_

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 172

Quoted 6 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +6 -0
+                                       unsigned long parent_rate)
+{
+       struct clk *div_clk = NULL, *vco_clk = NULL;
+       struct msm_dp_pll *vco = NULL;
+
+       div_clk = clk_get_parent(hw->clk);

We have clk_hw_get_parent() for this stuff. Please use it.

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 180

Quoted 8 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +8 -0
+       if (!div_clk)
+               return 0;
+
+       vco_clk = clk_get_parent(div_clk);
+       if (!vco_clk)
+               return 0;
+
+       vco = hw_clk_to_pll(__clk_get_hw(vco_clk));

Please don't use __clk_get_hw().

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 189

Quoted 9 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +9 -0
+       if (!vco)
+               return 0;
+
+       if (vco->rate == DP_VCO_HSCLK_RATE_8100MHZDIV1000)
+               return (vco->rate / 6);
+       else if (vco->rate == DP_VCO_HSCLK_RATE_5400MHZDIV1000)
+               return (vco->rate / 4);
+       else
+               return (vco->rate / 2);

else return is shunned. Just have return instead.

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 202

Quoted 13 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +13 -0
+}
+
+static int dp_pll_10nm_get_provider(struct msm_dp_pll *pll,
+                                    struct clk **link_clk_provider,
+                                    struct clk **pixel_clk_provider)
+{
+       struct dp_pll_10nm *pll_10nm = to_dp_pll_10nm(pll);
+       struct clk_hw_onecell_data *hw_data = pll_10nm->hw_data;
+
+       if (link_clk_provider)
+               *link_clk_provider = hw_data->hws[DP_LINK_CLK_SRC]->clk;
+       if (pixel_clk_provider)
+               *pixel_clk_provider = hw_data->hws[DP_PIXEL_CLK_SRC]->clk;

I don't know what this is but it looks weird. Is this a clk provider on top of another clk provider?

Quoted 10 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +10 -0
+
+       return 0;
+}
+
+static const struct clk_ops dp_10nm_pclksel_clk_ops = {
+       .get_parent = dp_mux_get_parent_10nm,
+       .set_parent = dp_mux_set_parent_10nm,
+       .recalc_rate = mux_recalc_rate,
+       .determine_rate = clk_mux_determine_rate,
+};

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 222

Quoted 10 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +10 -0
+
+static struct clk_hw *dp_pll_10nm_pixel_clk_sel(struct dp_pll_10nm *pll_10nm)
+{
+       struct device *dev = &pll_10nm->pdev->dev;
+       struct dp_pll_10nm_pclksel *pll_pclksel;
+       struct clk_init_data pclksel_init = {
+               .parent_names = (const char *[]){
+                               "dp_vco_divsel_two_clk_src",
+                               "dp_vco_divsel_four_clk_src",
+                               "dp_vco_divsel_six_clk_src" },

It looks like a clk divider still, not a mux.

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 225

Quoted 3 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +3 -0
+               .num_parents = 3,
+               .name = "dp_phy_pll_vco_div_clk",
+               .flags = CLK_IGNORE_UNUSED,

Why? Please comment why this is needed in the code. But more importantly, please just don't use this flag.

Quoted 16 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +16 -0
+               .ops = &dp_10nm_pclksel_clk_ops,
+       };
+       int ret;
+
+       pll_pclksel = devm_kzalloc(dev, sizeof(*pll_pclksel), GFP_KERNEL);
+       if (!pll_pclksel)
+               return ERR_PTR(-ENOMEM);
+
+       pll_pclksel->pll = pll_10nm;
+       pll_pclksel->shift = 0;
+       pll_pclksel->width = 4;
+       pll_pclksel->flags = CLK_DIVIDER_ONE_BASED;
+       pll_pclksel->hw.init = &pclksel_init;
+
+       ret = clk_hw_register(dev, &pll_pclksel->hw);
+       if (ret)

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 251

Quoted 10 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +10 -0
+               return ERR_PTR(ret);
+
+       return &pll_pclksel->hw;
+}
+
+static int dp_pll_10nm_register(struct dp_pll_10nm *pll_10nm)
+{
+       char clk_name[32], parent[32], vco_name[32];
+       struct clk_init_data vco_init = {
+               .parent_names = (const char *[]){ "bi_tcxo" },

Please use the new way of specifying clk parents through .fw_name and DT indices.

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 254

Quoted 3 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +3 -0
+               .num_parents = 1,
+               .name = vco_name,
+               .flags = CLK_IGNORE_UNUSED,

Ugh there's that flag again.

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 266

Quoted 12 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +12 -0
+               .ops = &dp_10nm_vco_clk_ops,
+       };
+       struct device *dev = &pll_10nm->pdev->dev;
+       struct clk_hw **hws = pll_10nm->hws;
+       struct clk_hw_onecell_data *hw_data;
+       struct clk_hw *hw;
+       int num = 0;
+       int ret;
+
+       DRM_DEBUG_DP("DP->id = %d", pll_10nm->id);
+
+       hw_data = devm_kzalloc(dev, sizeof(*hw_data) +

Use devm_kcalloc()

Quoted 4 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +4 -0
+                              NUM_PROVIDED_CLKS * sizeof(struct clk_hw *),
+                              GFP_KERNEL);
+       if (!hw_data)
+               return -ENOMEM;

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 280

Quoted 10 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +10 -0
+
+       snprintf(vco_name, 32, "dp_vco_clk");
+       pll_10nm->base.clk_hw.init = &vco_init;
+       ret = clk_hw_register(dev, &pll_10nm->base.clk_hw);
+       if (ret)
+               return ret;
+       hws[num++] = &pll_10nm->base.clk_hw;
+
+       snprintf(clk_name, 32, "dp_phy_pll_link_clk");
+       snprintf(parent, 32, "dp_vco_clk");

Is there a need to make these names with snprintf?

Quoted 112 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +112 -0
+       hw = clk_hw_register_fixed_factor(dev, clk_name, parent,
+                                         CLK_SET_RATE_PARENT, 1, 10);
+       if (IS_ERR(hw))
+               return PTR_ERR(hw);
+       hws[num++] = hw;
+       hw_data->hws[DP_LINK_CLK_SRC] = hw;
+
+       snprintf(clk_name, 32, "dp_vco_divsel_two_clk_src");
+       snprintf(parent, 32, "dp_vco_clk");
+       hw = clk_hw_register_fixed_factor(dev, clk_name, parent,
+                                         0, 1, 2);
+       if (IS_ERR(hw))
+               return PTR_ERR(hw);
+       hws[num++] = hw;
+
+       snprintf(clk_name, 32, "dp_vco_divsel_four_clk_src");
+       snprintf(parent, 32, "dp_vco_clk");
+       hw = clk_hw_register_fixed_factor(dev, clk_name, parent,
+                                         0, 1, 4);
+       if (IS_ERR(hw))
+               return PTR_ERR(hw);
+       hws[num++] = hw;
+
+       snprintf(clk_name, 32, "dp_vco_divsel_six_clk_src");
+       snprintf(parent, 32, "dp_vco_clk");
+       hw = clk_hw_register_fixed_factor(dev, clk_name, parent,
+                                         0, 1, 6);
+       if (IS_ERR(hw))
+               return PTR_ERR(hw);
+       hws[num++] = hw;
+
+       hw = dp_pll_10nm_pixel_clk_sel(pll_10nm);
+       if (IS_ERR(hw))
+               return PTR_ERR(hw);
+
+       hws[num++] = hw;
+       hw_data->hws[DP_PIXEL_CLK_SRC] = hw;
+
+       pll_10nm->num_hws = num;
+
+       hw_data->num = NUM_PROVIDED_CLKS;
+       pll_10nm->hw_data = hw_data;
+
+       ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
+                                    pll_10nm->hw_data);
+       if (ret) {
+               DRM_DEV_ERROR(dev, "failed to register clk provider: %d\n",
+                               ret);
+               return ret;
+       }
+
+       return ret;
+}
+
+struct msm_dp_pll *msm_dp_pll_10nm_init(struct platform_device *pdev, int id)
+{
+       struct dp_pll_10nm *dp_10nm_pll;
+       struct msm_dp_pll *pll;
+       int ret;
+
+       if (!pdev)
+               return ERR_PTR(-ENODEV);
+
+       dp_10nm_pll = devm_kzalloc(&pdev->dev,
+                                       sizeof(*dp_10nm_pll), GFP_KERNEL);
+       if (!dp_10nm_pll)
+               return ERR_PTR(-ENOMEM);
+
+       DRM_DEBUG_DP("DP PLL%d", id);
+
+       dp_10nm_pll->pdev = pdev;
+       dp_10nm_pll->id = id;
+       dp_pdb = dp_10nm_pll;
+
+       dp_10nm_pll->pll_base = msm_ioremap(pdev, "pll_base", "DP_PLL");
+       if (IS_ERR_OR_NULL(dp_10nm_pll->pll_base)) {
+               DRM_DEV_ERROR(&pdev->dev, "failed to map CMN PLL base\n");
+               return ERR_PTR(-ENOMEM);
+       }
+
+       dp_10nm_pll->phy_base = msm_ioremap(pdev, "phy_base", "DP_PHY");
+       if (IS_ERR_OR_NULL(dp_10nm_pll->phy_base)) {
+               DRM_DEV_ERROR(&pdev->dev, "failed to map CMN PHY base\n");
+               return ERR_PTR(-ENOMEM);
+       }
+
+       dp_10nm_pll->ln_tx0_base = msm_ioremap(pdev, "ln_tx0_base",
+                                               "DP_LN_TX0");
+       if (IS_ERR_OR_NULL(dp_10nm_pll->ln_tx0_base)) {
+               DRM_DEV_ERROR(&pdev->dev, "failed to map CMN LN_TX0 base\n");
+               return ERR_PTR(-ENOMEM);
+       }
+
+       dp_10nm_pll->ln_tx1_base = msm_ioremap(pdev, "ln_tx1_base",
+                                               "DP_LN_TX1");
+       if (IS_ERR_OR_NULL(dp_10nm_pll->ln_tx1_base)) {
+               DRM_DEV_ERROR(&pdev->dev, "failed to map CMN LN_TX1 base\n");
+               return ERR_PTR(-ENOMEM);
+       }
+
+       ret = of_property_read_u32(pdev->dev.of_node, "cell-index",
+                               &dp_10nm_pll->index);
+       if (ret) {
+               DRM_ERROR("Unable to get the cell-index ret=%d\n", ret);
+               dp_10nm_pll->index = 0;
+       }
+
+       ret = msm_dp_pll_util_parse_dt_clock(pdev, &dp_10nm_pll->base);
+       if (ret) {
+               DRM_ERROR("Unable to parse dt clocks ret=%d\n", ret);
+               return ERR_PTR(ret);
+       }

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 402

Quoted 10 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +10 -0
+
+       ret = dp_pll_10nm_register(dp_10nm_pll);
+       if (ret) {
+               DRM_DEV_ERROR(&pdev->dev, "failed to register PLL: %d\n", ret);
+               return ERR_PTR(ret);
+       }
+
+       pll = &dp_10nm_pll->base;
+       pll->min_rate = DP_VCO_HSCLK_RATE_1620MHZDIV1000;
+       pll->max_rate = DP_VCO_HSCLK_RATE_8100MHZDIV1000;

Maybe min/max can be set with clk_hw_set_rate_range()?

Quoted 76 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +4 -0
+       pll->get_provider = dp_pll_10nm_get_provider;
+
+       return pll;
+}
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.h new file +66 -0
new file mode 100644
index 0000000..8466463
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef __DP_PLL_10NM_H
+#define __DP_PLL_10NM_H
+
+#include "dp_pll.h"
+#include "dp_reg.h"
+
+#define DP_VCO_HSCLK_RATE_1620MHZDIV1000       1620000UL
+#define DP_VCO_HSCLK_RATE_2700MHZDIV1000       2700000UL
+#define DP_VCO_HSCLK_RATE_5400MHZDIV1000       5400000UL
+#define DP_VCO_HSCLK_RATE_8100MHZDIV1000       8100000UL
+
+#define NUM_DP_CLOCKS_MAX                      6
+
+#define DP_PHY_PLL_POLL_SLEEP_US               500
+#define DP_PHY_PLL_POLL_TIMEOUT_US             10000
+
+#define DP_VCO_RATE_8100MHZDIV1000             8100000UL
+#define DP_VCO_RATE_9720MHZDIV1000             9720000UL
+#define DP_VCO_RATE_10800MHZDIV1000            10800000UL
+
+struct dp_pll_10nm {
+       struct msm_dp_pll base;
+
+       int id;
+       struct platform_device *pdev;
+
+       void __iomem *pll_base;
+       void __iomem *phy_base;
+       void __iomem *ln_tx0_base;
+       void __iomem *ln_tx1_base;
+
+       /* private clocks: */
+       struct clk_hw *hws[NUM_DP_CLOCKS_MAX];
+       u32 num_hws;
+
+       /* clock-provider: */
+       struct clk_hw_onecell_data *hw_data;
+
+       /* lane and orientation settings */
+       u8 lane_cnt;
+       u8 orientation;
+
+       /* COM PHY settings */
+       u32 hsclk_sel;
+       u32 dec_start_mode0;
+       u32 div_frac_start1_mode0;
+       u32 div_frac_start2_mode0;
+       u32 div_frac_start3_mode0;
+       u32 integloop_gain0_mode0;
+       u32 integloop_gain1_mode0;
+       u32 vco_tune_map;
+       u32 lock_cmp1_mode0;
+       u32 lock_cmp2_mode0;
+       u32 lock_cmp3_mode0;
+       u32 lock_cmp_en;
+
+       /* PHY vco divider */
+       u32 phy_vco_div;
+       /*
+        * Certain pll's needs to update the same vco rate after resume in
+        * suspend/resume scenario. Cached the vco rate for such plls.

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.h line 76

Quoted 10 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.h new file +10 -0
+        */
+       unsigned long   vco_cached_rate;
+       u32             cached_cfg0;
+       u32             cached_cfg1;
+       u32             cached_outdiv;
+
+       uint32_t index;
+};
+
+#define to_dp_pll_10nm(x)      container_of(x, struct dp_pll_10nm, base)

Why is this struct in the header file? Does something outside of the PLL driver need to access this struct?

Quoted 31 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.h new file +10 -0
+
+int dp_vco_set_rate_10nm(struct clk_hw *hw, unsigned long rate,
+                               unsigned long parent_rate);
+unsigned long dp_vco_recalc_rate_10nm(struct clk_hw *hw,
+                               unsigned long parent_rate);
+long dp_vco_round_rate_10nm(struct clk_hw *hw, unsigned long rate,
+                               unsigned long *parent_rate);
+int dp_vco_prepare_10nm(struct clk_hw *hw);
+void dp_vco_unprepare_10nm(struct clk_hw *hw);
+#endif /* __DP_PLL_10NM_H */
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +15 -0
new file mode 100644
index 0000000..db3983d5
@@ -0,0 +1,524 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
+ */
+
+#define pr_fmt(fmt)    "%s: " fmt, __func__
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+
+#include "dp_hpd.h"
+#include "dp_pll.h"
+#include "dp_pll_10nm.h"

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c line 25

Quoted 10 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +10 -0
+
+static int dp_vco_pll_init_db_10nm(struct msm_dp_pll *pll,
+               unsigned long rate)
+{
+       struct dp_pll_10nm *dp_res = to_dp_pll_10nm(pll);
+       u32 spare_value = 0;
+
+       spare_value = PLL_REG_R(dp_res->phy_base, REG_DP_PHY_SPARE0);
+       dp_res->lane_cnt = spare_value & 0x0F;
+       dp_res->orientation = (spare_value & 0xF0) >> 4;

Can this be read at any time from the spare register? Just curious why we need to read this here in init vs. when it's actually used. We could avoid stashing away these values in the struct. It also looks like the PLL part is intimately tied to the phy, but there isn't an actual phy driver. Why? It looks like the phy driver is a clk driver, and the clk framework is not prepared to handle things like lane count and orientation. I'd expect that to come through a PHY framework and then have the phy driver control the PLL by powering it back up and changing it's rates, etc. Not everything needs to be done through clk APIs.

Quoted 482 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +482 -0
+
+       DRM_DEBUG_DP("%s: spare_value=0x%x, ln_cnt=0x%x, orientation=0x%x\n",
+                       __func__, spare_value, dp_res->lane_cnt,
+                       dp_res->orientation);
+
+       switch (rate) {
+       case DP_VCO_HSCLK_RATE_1620MHZDIV1000:
+               DRM_DEBUG_DP("%s: VCO rate: %ld\n", __func__,
+                               DP_VCO_RATE_9720MHZDIV1000);
+               dp_res->hsclk_sel = 0x0c;
+               dp_res->dec_start_mode0 = 0x69;
+               dp_res->div_frac_start1_mode0 = 0x00;
+               dp_res->div_frac_start2_mode0 = 0x80;
+               dp_res->div_frac_start3_mode0 = 0x07;
+               dp_res->integloop_gain0_mode0 = 0x3f;
+               dp_res->integloop_gain1_mode0 = 0x00;
+               dp_res->vco_tune_map = 0x00;
+               dp_res->lock_cmp1_mode0 = 0x6f;
+               dp_res->lock_cmp2_mode0 = 0x08;
+               dp_res->lock_cmp3_mode0 = 0x00;
+               dp_res->phy_vco_div = 0x1;
+               dp_res->lock_cmp_en = 0x00;
+               break;
+       case DP_VCO_HSCLK_RATE_2700MHZDIV1000:
+               DRM_DEBUG_DP("%s: VCO rate: %ld\n", __func__,
+                               DP_VCO_RATE_10800MHZDIV1000);
+               dp_res->hsclk_sel = 0x04;
+               dp_res->dec_start_mode0 = 0x69;
+               dp_res->div_frac_start1_mode0 = 0x00;
+               dp_res->div_frac_start2_mode0 = 0x80;
+               dp_res->div_frac_start3_mode0 = 0x07;
+               dp_res->integloop_gain0_mode0 = 0x3f;
+               dp_res->integloop_gain1_mode0 = 0x00;
+               dp_res->vco_tune_map = 0x00;
+               dp_res->lock_cmp1_mode0 = 0x0f;
+               dp_res->lock_cmp2_mode0 = 0x0e;
+               dp_res->lock_cmp3_mode0 = 0x00;
+               dp_res->phy_vco_div = 0x1;
+               dp_res->lock_cmp_en = 0x00;
+               break;
+       case DP_VCO_HSCLK_RATE_5400MHZDIV1000:
+               DRM_DEBUG_DP("%s: VCO rate: %ld\n", __func__,
+                               DP_VCO_RATE_10800MHZDIV1000);
+               dp_res->hsclk_sel = 0x00;
+               dp_res->dec_start_mode0 = 0x8c;
+               dp_res->div_frac_start1_mode0 = 0x00;
+               dp_res->div_frac_start2_mode0 = 0x00;
+               dp_res->div_frac_start3_mode0 = 0x0a;
+               dp_res->integloop_gain0_mode0 = 0x3f;
+               dp_res->integloop_gain1_mode0 = 0x00;
+               dp_res->vco_tune_map = 0x00;
+               dp_res->lock_cmp1_mode0 = 0x1f;
+               dp_res->lock_cmp2_mode0 = 0x1c;
+               dp_res->lock_cmp3_mode0 = 0x00;
+               dp_res->phy_vco_div = 0x2;
+               dp_res->lock_cmp_en = 0x00;
+               break;
+       case DP_VCO_HSCLK_RATE_8100MHZDIV1000:
+               DRM_DEBUG_DP("%s: VCO rate: %ld\n", __func__,
+                               DP_VCO_RATE_8100MHZDIV1000);
+               dp_res->hsclk_sel = 0x03;
+               dp_res->dec_start_mode0 = 0x69;
+               dp_res->div_frac_start1_mode0 = 0x00;
+               dp_res->div_frac_start2_mode0 = 0x80;
+               dp_res->div_frac_start3_mode0 = 0x07;
+               dp_res->integloop_gain0_mode0 = 0x3f;
+               dp_res->integloop_gain1_mode0 = 0x00;
+               dp_res->vco_tune_map = 0x00;
+               dp_res->lock_cmp1_mode0 = 0x2f;
+               dp_res->lock_cmp2_mode0 = 0x2a;
+               dp_res->lock_cmp3_mode0 = 0x00;
+               dp_res->phy_vco_div = 0x0;
+               dp_res->lock_cmp_en = 0x08;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int dp_config_vco_rate_10nm(struct msm_dp_pll *pll,
+               unsigned long rate)
+{
+       u32 res = 0;
+       struct dp_pll_10nm *dp_res = to_dp_pll_10nm(pll);
+
+       res = dp_vco_pll_init_db_10nm(pll, rate);
+       if (res) {
+               DRM_ERROR("VCO Init DB failed\n");
+               return res;
+       }
+
+       if (dp_res->lane_cnt != 4) {
+               if (dp_res->orientation == ORIENTATION_CC2)
+                       PLL_REG_W(dp_res->phy_base, REG_DP_PHY_PD_CTL, 0x6d);
+               else
+                       PLL_REG_W(dp_res->phy_base, REG_DP_PHY_PD_CTL, 0x75);
+       } else {
+               PLL_REG_W(dp_res->phy_base, REG_DP_PHY_PD_CTL, 0x7d);
+       }
+
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_SVS_MODE_CLK_SEL, 0x01);
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_SYSCLK_EN_SEL, 0x37);
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_SYS_CLK_CTRL, 0x02);
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_CLK_ENABLE1, 0x0e);
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_SYSCLK_BUF_ENABLE, 0x06);
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_CLK_SEL, 0x30);
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_CMN_CONFIG, 0x02);
+
+       /* Different for each clock rates */
+       PLL_REG_W(dp_res->pll_base,
+               QSERDES_COM_HSCLK_SEL, dp_res->hsclk_sel);
+       PLL_REG_W(dp_res->pll_base,
+               QSERDES_COM_DEC_START_MODE0, dp_res->dec_start_mode0);
+       PLL_REG_W(dp_res->pll_base,
+               QSERDES_COM_DIV_FRAC_START1_MODE0,
+               dp_res->div_frac_start1_mode0);
+       PLL_REG_W(dp_res->pll_base,
+               QSERDES_COM_DIV_FRAC_START2_MODE0,
+               dp_res->div_frac_start2_mode0);
+       PLL_REG_W(dp_res->pll_base,
+               QSERDES_COM_DIV_FRAC_START3_MODE0,
+               dp_res->div_frac_start3_mode0);
+       PLL_REG_W(dp_res->pll_base,
+               QSERDES_COM_INTEGLOOP_GAIN0_MODE0,
+               dp_res->integloop_gain0_mode0);
+       PLL_REG_W(dp_res->pll_base,
+               QSERDES_COM_INTEGLOOP_GAIN1_MODE0,
+               dp_res->integloop_gain1_mode0);
+       PLL_REG_W(dp_res->pll_base,
+               QSERDES_COM_VCO_TUNE_MAP, dp_res->vco_tune_map);
+       PLL_REG_W(dp_res->pll_base,
+               QSERDES_COM_LOCK_CMP1_MODE0, dp_res->lock_cmp1_mode0);
+       PLL_REG_W(dp_res->pll_base,
+               QSERDES_COM_LOCK_CMP2_MODE0, dp_res->lock_cmp2_mode0);
+       PLL_REG_W(dp_res->pll_base,
+               QSERDES_COM_LOCK_CMP3_MODE0, dp_res->lock_cmp3_mode0);
+
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_BG_TIMER, 0x0a);
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_CORECLK_DIV_MODE0, 0x0a);
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_VCO_TUNE_CTRL, 0x00);
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x3f);
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_CORE_CLK_EN, 0x1f);
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_PLL_IVCO, 0x07);
+       PLL_REG_W(dp_res->pll_base,
+               QSERDES_COM_LOCK_CMP_EN, dp_res->lock_cmp_en);
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_PLL_CCTRL_MODE0, 0x36);
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_PLL_RCTRL_MODE0, 0x16);
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_CP_CTRL_MODE0, 0x06);
+
+       if (dp_res->orientation == ORIENTATION_CC2)
+               PLL_REG_W(dp_res->phy_base, REG_DP_PHY_MODE, 0x4c);
+       else
+               PLL_REG_W(dp_res->phy_base, REG_DP_PHY_MODE, 0x5c);
+
+       /* TX Lane configuration */
+       PLL_REG_W(dp_res->phy_base,
+                       REG_DP_PHY_TX0_TX1_LANE_CTL, 0x05);
+       PLL_REG_W(dp_res->phy_base,
+                       REG_DP_PHY_TX2_TX3_LANE_CTL, 0x05);
+
+       /* TX-0 register configuration */
+       PLL_REG_W(dp_res->ln_tx0_base,
+                       REG_DP_PHY_TXn_TRANSCEIVER_BIAS_EN, 0x1a);
+       PLL_REG_W(dp_res->ln_tx0_base,
+                       REG_DP_PHY_TXn_VMODE_CTRL1, 0x40);
+       PLL_REG_W(dp_res->ln_tx0_base,
+                       REG_DP_PHY_TXn_PRE_STALL_LDO_BOOST_EN, 0x30);
+       PLL_REG_W(dp_res->ln_tx0_base,
+                       REG_DP_PHY_TXn_INTERFACE_SELECT, 0x3d);
+       PLL_REG_W(dp_res->ln_tx0_base,
+                       REG_DP_PHY_TXn_CLKBUF_ENABLE, 0x0f);
+       PLL_REG_W(dp_res->ln_tx0_base,
+                       REG_DP_PHY_TXn_RESET_TSYNC_EN, 0x03);
+       PLL_REG_W(dp_res->ln_tx0_base,
+                       REG_DP_PHY_TXn_TRAN_DRVR_EMP_EN, 0x03);
+       PLL_REG_W(dp_res->ln_tx0_base,
+                       REG_DP_PHY_TXn_PARRATE_REC_DETECT_IDLE_EN, 0x00);
+       PLL_REG_W(dp_res->ln_tx0_base,
+                       REG_DP_PHY_TXn_TX_INTERFACE_MODE, 0x00);
+       PLL_REG_W(dp_res->ln_tx0_base, REG_DP_PHY_TXn_TX_BAND, 0x4);
+
+       /* TX-1 register configuration */
+       PLL_REG_W(dp_res->ln_tx1_base,
+                       REG_DP_PHY_TXn_TRANSCEIVER_BIAS_EN, 0x1a);
+       PLL_REG_W(dp_res->ln_tx1_base,
+                       REG_DP_PHY_TXn_VMODE_CTRL1, 0x40);
+       PLL_REG_W(dp_res->ln_tx1_base,
+                       REG_DP_PHY_TXn_PRE_STALL_LDO_BOOST_EN, 0x30);
+       PLL_REG_W(dp_res->ln_tx1_base,
+                       REG_DP_PHY_TXn_INTERFACE_SELECT, 0x3d);
+       PLL_REG_W(dp_res->ln_tx1_base,
+                       REG_DP_PHY_TXn_CLKBUF_ENABLE, 0x0f);
+       PLL_REG_W(dp_res->ln_tx1_base,
+                       REG_DP_PHY_TXn_RESET_TSYNC_EN, 0x03);
+       PLL_REG_W(dp_res->ln_tx1_base,
+                       REG_DP_PHY_TXn_TRAN_DRVR_EMP_EN, 0x03);
+       PLL_REG_W(dp_res->ln_tx1_base,
+                       REG_DP_PHY_TXn_PARRATE_REC_DETECT_IDLE_EN, 0x00);
+       PLL_REG_W(dp_res->ln_tx1_base,
+                       REG_DP_PHY_TXn_TX_INTERFACE_MODE, 0x00);
+       PLL_REG_W(dp_res->ln_tx1_base,
+                       REG_DP_PHY_TXn_TX_BAND, 0x4);
+
+       /* dependent on the vco frequency */
+       PLL_REG_W(dp_res->phy_base,
+                       REG_DP_PHY_VCO_DIV, dp_res->phy_vco_div);
+
+       return res;
+}
+
+static bool dp_10nm_pll_lock_status(struct dp_pll_10nm *dp_res)
+{
+       u32 status;
+       bool pll_locked;
+
+       /* poll for PLL lock status */
+       if (readl_poll_timeout_atomic((dp_res->pll_base +
+                       QSERDES_COM_C_READY_STATUS),
+                       status,
+                       ((status & BIT(0)) > 0),
+                       DP_PHY_PLL_POLL_SLEEP_US,
+                       DP_PHY_PLL_POLL_TIMEOUT_US)) {
+               DRM_ERROR("%s: C_READY status is not high. Status=%x\n",
+                               __func__, status);
+               pll_locked = false;
+       } else {
+               pll_locked = true;
+       }
+
+       return pll_locked;
+}
+
+static bool dp_10nm_phy_rdy_status(struct dp_pll_10nm *dp_res)
+{
+       u32 status;
+       bool phy_ready = true;
+
+       /* poll for PHY ready status */
+       if (readl_poll_timeout_atomic((dp_res->phy_base +
+                       REG_DP_PHY_STATUS),
+                       status,
+                       ((status & (BIT(1))) > 0),
+                       DP_PHY_PLL_POLL_SLEEP_US,
+                       DP_PHY_PLL_POLL_TIMEOUT_US)) {
+               DRM_ERROR("%s: Phy_ready is not high. Status=%x\n",
+                               __func__, status);
+               phy_ready = false;
+       }
+
+       return phy_ready;
+}
+
+static int dp_pll_enable_10nm(struct clk_hw *hw)
+{
+       int rc = 0;
+       struct msm_dp_pll *pll = hw_clk_to_pll(hw);
+       struct dp_pll_10nm *dp_res = to_dp_pll_10nm(pll);
+       u32 bias_en, drvr_en;
+
+       PLL_REG_W(dp_res->phy_base, REG_DP_PHY_AUX_CFG2, 0x04);
+       PLL_REG_W(dp_res->phy_base, REG_DP_PHY_CFG, 0x01);
+       PLL_REG_W(dp_res->phy_base, REG_DP_PHY_CFG, 0x05);
+       PLL_REG_W(dp_res->phy_base, REG_DP_PHY_CFG, 0x01);
+       PLL_REG_W(dp_res->phy_base, REG_DP_PHY_CFG, 0x09);
+
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_RESETSM_CNTRL, 0x20);
+
+       if (!dp_10nm_pll_lock_status(dp_res)) {
+               rc = -EINVAL;
+               goto lock_err;
+       }
+
+       PLL_REG_W(dp_res->phy_base, REG_DP_PHY_CFG, 0x19);
+       /* poll for PHY ready status */
+       if (!dp_10nm_phy_rdy_status(dp_res)) {
+               rc = -EINVAL;
+               goto lock_err;
+       }
+
+       DRM_DEBUG_DP("%s: PLL is locked\n", __func__);
+
+       if (dp_res->lane_cnt == 1) {
+               bias_en = 0x3e;
+               drvr_en = 0x13;
+       } else {
+               bias_en = 0x3f;
+               drvr_en = 0x10;
+       }
+
+       if (dp_res->lane_cnt != 4) {
+               if (dp_res->orientation == ORIENTATION_CC1) {
+                       PLL_REG_W(dp_res->ln_tx1_base,
+                               REG_DP_PHY_TXn_HIGHZ_DRVR_EN, drvr_en);
+                       PLL_REG_W(dp_res->ln_tx1_base,
+                               REG_DP_PHY_TXn_TRANSCEIVER_BIAS_EN, bias_en);
+               } else {
+                       PLL_REG_W(dp_res->ln_tx0_base,
+                               REG_DP_PHY_TXn_HIGHZ_DRVR_EN, drvr_en);
+                       PLL_REG_W(dp_res->ln_tx0_base,
+                               REG_DP_PHY_TXn_TRANSCEIVER_BIAS_EN, bias_en);
+               }
+       } else {
+               PLL_REG_W(dp_res->ln_tx0_base,
+                               REG_DP_PHY_TXn_HIGHZ_DRVR_EN, drvr_en);
+               PLL_REG_W(dp_res->ln_tx0_base,
+                               REG_DP_PHY_TXn_TRANSCEIVER_BIAS_EN, bias_en);
+               PLL_REG_W(dp_res->ln_tx1_base,
+                               REG_DP_PHY_TXn_HIGHZ_DRVR_EN, drvr_en);
+               PLL_REG_W(dp_res->ln_tx1_base,
+                               REG_DP_PHY_TXn_TRANSCEIVER_BIAS_EN, bias_en);
+       }
+
+       PLL_REG_W(dp_res->ln_tx0_base, REG_DP_PHY_TXn_TX_POL_INV, 0x0a);
+       PLL_REG_W(dp_res->ln_tx1_base, REG_DP_PHY_TXn_TX_POL_INV, 0x0a);
+       PLL_REG_W(dp_res->phy_base, REG_DP_PHY_CFG, 0x18);
+       udelay(2000);
+
+       PLL_REG_W(dp_res->phy_base, REG_DP_PHY_CFG, 0x19);
+
+       /* poll for PHY ready status */
+       if (!dp_10nm_phy_rdy_status(dp_res)) {
+               rc = -EINVAL;
+               goto lock_err;
+       }
+
+       PLL_REG_W(dp_res->ln_tx0_base, REG_DP_PHY_TXn_TX_DRV_LVL, 0x38);
+       PLL_REG_W(dp_res->ln_tx1_base, REG_DP_PHY_TXn_TX_DRV_LVL, 0x38);
+       PLL_REG_W(dp_res->ln_tx0_base, REG_DP_PHY_TXn_TX_EMP_POST1_LVL, 0x20);
+       PLL_REG_W(dp_res->ln_tx1_base, REG_DP_PHY_TXn_TX_EMP_POST1_LVL, 0x20);
+       PLL_REG_W(dp_res->ln_tx0_base,
+                       REG_DP_PHY_TXn_RES_CODE_LANE_OFFSET_TX, 0x06);
+       PLL_REG_W(dp_res->ln_tx1_base,
+                       REG_DP_PHY_TXn_RES_CODE_LANE_OFFSET_TX, 0x06);
+       PLL_REG_W(dp_res->ln_tx0_base,
+                       REG_DP_PHY_TXn_RES_CODE_LANE_OFFSET_RX, 0x07);
+       PLL_REG_W(dp_res->ln_tx1_base,
+                       REG_DP_PHY_TXn_RES_CODE_LANE_OFFSET_RX, 0x07);
+
+lock_err:
+       return rc;
+}
+
+static int dp_pll_disable_10nm(struct clk_hw *hw)
+{
+       int rc = 0;
+       struct msm_dp_pll *pll = hw_clk_to_pll(hw);
+       struct dp_pll_10nm *dp_res = to_dp_pll_10nm(pll);
+
+       /* Assert DP PHY power down */
+       PLL_REG_W(dp_res->phy_base, REG_DP_PHY_PD_CTL, 0x2);
+
+       return rc;
+}
+
+
+int dp_vco_prepare_10nm(struct clk_hw *hw)
+{
+       int rc = 0;
+       struct msm_dp_pll *pll = hw_clk_to_pll(hw);
+       struct dp_pll_10nm *dp_res = to_dp_pll_10nm(pll);
+
+       DRM_DEBUG_DP("%s: rate = %ld\n", __func__, pll->rate);
+       if ((dp_res->vco_cached_rate != 0)
+               && (dp_res->vco_cached_rate == pll->rate)) {
+               rc = dp_vco_set_rate_10nm(hw,
+                       dp_res->vco_cached_rate, dp_res->vco_cached_rate);
+               if (rc) {
+                       DRM_ERROR("index=%d vco_set_rate failed. rc=%d\n",
+                               rc, dp_res->index);
+                       goto error;
+               }
+       }
+
+       rc = dp_pll_enable_10nm(hw);
+       if (rc) {
+               DRM_ERROR("ndx=%d failed to enable dp pll\n",
+                                       dp_res->index);
+               goto error;
+       }
+
+       pll->pll_on = true;
+error:
+       return rc;
+}
+
+void dp_vco_unprepare_10nm(struct clk_hw *hw)
+{
+       struct msm_dp_pll *pll = hw_clk_to_pll(hw);
+       struct dp_pll_10nm *dp_res = to_dp_pll_10nm(pll);
+
+       if (!dp_res) {
+               DRM_ERROR("Invalid input parameter\n");
+               return;
+       }
+
+       if (!pll->pll_on) {
+               DRM_ERROR("pll resource can't be enabled\n");
+               return;
+       }
+       dp_res->vco_cached_rate = pll->rate;
+       dp_pll_disable_10nm(hw);
+
+       pll->pll_on = false;
+}
+
+int dp_vco_set_rate_10nm(struct clk_hw *hw, unsigned long rate,
+                                       unsigned long parent_rate)
+{
+       struct msm_dp_pll *pll = hw_clk_to_pll(hw);
+       int rc;
+
+       DRM_DEBUG_DP("DP lane CLK rate=%ld\n", rate);
+
+       rc = dp_config_vco_rate_10nm(pll, rate);
+       if (rc)
+               DRM_ERROR("%s: Failed to set clk rate\n", __func__);
+
+       pll->rate = rate;
+
+       return 0;
+}
+
+unsigned long dp_vco_recalc_rate_10nm(struct clk_hw *hw,
+                                       unsigned long parent_rate)
+{
+       struct msm_dp_pll *pll = hw_clk_to_pll(hw);
+       struct dp_pll_10nm *dp_res = to_dp_pll_10nm(pll);
+       u32 div, hsclk_div, link_clk_div = 0;
+       u64 vco_rate;
+
+       div = PLL_REG_R(dp_res->pll_base, QSERDES_COM_HSCLK_SEL);
+       div &= 0x0f;
+
+       if (div == 12)
+               hsclk_div = 6; /* Default */
+       else if (div == 4)
+               hsclk_div = 4;
+       else if (div == 0)
+               hsclk_div = 2;
+       else if (div == 3)
+               hsclk_div = 1;
+       else {
+               DRM_DEBUG_DP("unknown divider. forcing to default\n");
+               hsclk_div = 5;
+       }
+
+       div = PLL_REG_R(dp_res->phy_base, REG_DP_PHY_AUX_CFG2);
+       div >>= 2;
+
+       if ((div & 0x3) == 0)
+               link_clk_div = 5;
+       else if ((div & 0x3) == 1)
+               link_clk_div = 10;
+       else if ((div & 0x3) == 2)
+               link_clk_div = 20;
+       else
+               DRM_ERROR("%s: unsupported div. Phy_mode: %d\n", __func__, div);
+
+       if (link_clk_div == 20) {
+               vco_rate = DP_VCO_HSCLK_RATE_2700MHZDIV1000;
+       } else {
+               if (hsclk_div == 6)
+                       vco_rate = DP_VCO_HSCLK_RATE_1620MHZDIV1000;
+               else if (hsclk_div == 4)
+                       vco_rate = DP_VCO_HSCLK_RATE_2700MHZDIV1000;
+               else if (hsclk_div == 2)
+                       vco_rate = DP_VCO_HSCLK_RATE_5400MHZDIV1000;
+               else
+                       vco_rate = DP_VCO_HSCLK_RATE_8100MHZDIV1000;
+       }
+
+       DRM_DEBUG_DP("returning vco rate = %lu\n", (unsigned long)vco_rate);
+
+       dp_res->vco_cached_rate = pll->rate = vco_rate;
+       return (unsigned long)vco_rate;
+}
+
+long dp_vco_round_rate_10nm(struct clk_hw *hw, unsigned long rate,
+                       unsigned long *parent_rate)
+{
+       unsigned long rrate = rate;

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c line 517

Quoted 10 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +10 -0
+       struct msm_dp_pll *pll = hw_clk_to_pll(hw);
+
+       if (rate <= pll->min_rate)
+               rrate = pll->min_rate;
+       else if (rate <= DP_VCO_HSCLK_RATE_2700MHZDIV1000)
+               rrate = DP_VCO_HSCLK_RATE_2700MHZDIV1000;
+       else if (rate <= DP_VCO_HSCLK_RATE_5400MHZDIV1000)
+               rrate = DP_VCO_HSCLK_RATE_5400MHZDIV1000;
+       else
+               rrate = pll->max_rate;

A switch statement would be more readable. Not sure why pll->min_rate is needed though.

	switch (rate) {
	case 0 .. pll->min_rate:
		rrate = 
		break;
	case pll->min_rate .. DP_VCO_HSCLK_RATE_2700MHZDIV1000:
		...
	}

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c line 523

Quoted 6 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +6 -0
+
+       DRM_DEBUG_DP("%s: rrate=%ld\n", __func__, rrate);
+
+       *parent_rate = rrate;
+       return rrate;
+}

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

#10 lore raw

Re: [DPU PATCH v4 4/5] drm/msm/dp: add support for DP PLL driver

Quoted 421 lines · Stephen Boyd

Quoting Vara Reddy (2020-03-04 16:10:27)

Quoted 85 lines

From: Chandan Uddaraju <[email protected]>

Add the needed DP PLL specific files to support
display port interface on msm targets.

The DP driver calls the DP PLL driver registration. The DP driver sets the link and pixel clock sources.

Changes in v2:
-- Update copyright markings on all relevant files. -- Use DRM_DEBUG_DP for debug msgs.

Changes in v4:
-- Update the DP link clock provider names

---
 drivers/gpu/drm/msm/Kconfig                   |  13 +
 drivers/gpu/drm/msm/Makefile                  |   4 +
 drivers/gpu/drm/msm/dp/dp_display.c           |  51 +++
 drivers/gpu/drm/msm/dp/dp_display.h           |   3 +
 drivers/gpu/drm/msm/dp/dp_parser.h            |   4 +
 drivers/gpu/drm/msm/dp/dp_power.h             |   1 -
 drivers/gpu/drm/msm/dp/pll/dp_pll.c           | 136 +++++++
 drivers/gpu/drm/msm/dp/pll/dp_pll.h           |  57 +++
 drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c      | 406 
++++++++++++++++++++
 drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.h      |  86 +++++
 drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c | 524 
++++++++++++++++++++++++++
 11 files changed, 1284 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/msm/dp/pll/dp_pll.c
 create mode 100644 drivers/gpu/drm/msm/dp/pll/dp_pll.h
 create mode 100644 drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c
 create mode 100644 drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.h
 create mode 100644 drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c
drivers/gpu/drm/msm/Kconfig +0 -0
index 7946cb1..e73ad23 100644
@@ -66,6 +66,19 @@ config DRM_MSM_DP
          display support is enabled through this config option. It 

can
be primary or secondary display on device.

drivers/gpu/drm/msm/Kconfig +12 -0
+config DRM_MSM_DP_PLL
+       bool "Enable DP PLL driver in MSM DRM"
+       depends on DRM_MSM_DP && COMMON_CLK
+       help
+         Choose this option to enable DP PLL driver which provides DP
+         source clocks under common clock framework.
+
+config DRM_MSM_DP_10NM_PLL
+       bool "Enable DP 10nm PLL driver in MSM DRM (used by SDM845)"
+       depends on DRM_MSM_DP_PLL
+       help
+         Choose this option if DP PLL on SDM845 is used on the 

platform.

drivers/gpu/drm/msm/Kconfig +1 -0
+
 config DRM_MSM_DSI
        bool "Enable DSI support in MSM DRM driver"
        depends on DRM_MSM
drivers/gpu/drm/msm/Makefile +0 -0
index c846599..1621f3f 100644
@@ -140,4 +140,8 @@ msm-$(CONFIG_DRM_MSM_DSI_14NM_PHY) += 

dsi/pll/dsi_pll_14nm.o
msm-$(CONFIG_DRM_MSM_DSI_10NM_PHY) += dsi/pll/dsi_pll_10nm.o endif

drivers/gpu/drm/msm/Makefile +4 -0
+msm-$(CONFIG_DRM_MSM_DP_PLL)+= dp/pll/dp_pll.o
+msm-$(CONFIG_DRM_MSM_DP_10NM_PLL)+= dp/pll/dp_pll_10nm.o \
+       dp/pll/dp_pll_10nm_util.o
+
 obj-$(CONFIG_DRM_MSM)  += msm.o
drivers/gpu/drm/msm/dp/dp_display.c +0 -0
index 5525405..ded8f5c 100644
@@ -61,6 +61,51 @@ struct dp_display_private {
        {}
 };

drivers/gpu/drm/msm/dp/dp_display.c struct dp_display_private { · line 72

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_display.c +9 -0
 
+static int dp_get_pll(struct dp_display_private *dp_priv)
+{
+       struct platform_device *pdev = NULL;
+       struct platform_device *pll_pdev;
+       struct device_node *pll_node;
+       struct dp_parser *dp_parser = NULL;
+
+       if (!dp_priv) {
+               DRM_ERROR("Invalid Arguments\n");

It's a static function. Is this even possible? The bad code would be in the same file.

Quoted 109 lines
drivers/gpu/drm/msm/dp/dp_display.c +36 -0
+               return -EINVAL;
+       }
+
+       pdev = dp_priv->pdev;
+       dp_parser = dp_priv->parser;
+
+       if (!dp_parser) {
+               DRM_DEV_ERROR(&pdev->dev, "%s: Parser not 
 initialized\n",
+                               __func__);
+               return -EINVAL;
+       }
+
+       pll_node = of_parse_phandle(pdev->dev.of_node, "pll-node", 0);
+       if (!pll_node) {
+               DRM_DEV_ERROR(&pdev->dev, "%s: cannot find pll 
 device\n",
+                               __func__);
+               return -ENXIO;
+       }
+
+       pll_pdev = of_find_device_by_node(pll_node);
+       if (pll_pdev)
+               dp_parser->pll = platform_get_drvdata(pll_pdev);
+
+       of_node_put(pll_node);
+
+       if (!pll_pdev || !dp_parser->pll) {
+               DRM_DEV_ERROR(&pdev->dev, "%s: pll driver is not 
 ready\n",
+                               __func__);
+               return -EPROBE_DEFER;
+       }
+
+       dp_parser->pll_dev = get_device(&pll_pdev->dev);
+
+       return 0;
+}
+

static irqreturn_t dp_display_irq(int irq, void *dev_id)

 {
        struct dp_display_private *dp = dev_id;
drivers/gpu/drm/msm/dp/dp_display.c +4 -0
@@ -114,6 +159,10 @@ static int dp_display_bind(struct device *dev, 
 struct device *master,
                goto end;
        }
 
+       rc = dp_get_pll(dp);
+       if (rc)
+               goto end;
+
        rc = dp_aux_register(dp->aux);
        if (rc) {
                DRM_ERROR("DRM DP AUX register failed\n");
drivers/gpu/drm/msm/dp/dp_display.c +2 -0
@@ -837,6 +886,7 @@ int __init msm_dp_register(void)
 {
        int ret;
 
+       msm_dp_pll_driver_register();
        ret = platform_driver_register(&dp_display_driver);
        if (ret) {
                DRM_ERROR("driver register failed");
@@ -848,6 +898,7 @@ int __init msm_dp_register(void)
 
 void __exit msm_dp_unregister(void)
 {
+       msm_dp_pll_driver_unregister();
        platform_driver_unregister(&dp_display_driver);
 }
 
drivers/gpu/drm/msm/dp/dp_display.h +3 -0
index dad8610..4c53ed5 100644
@@ -25,4 +25,7 @@ int dp_display_get_modes(struct msm_dp *dp_display,
 bool dp_display_check_video_test(struct msm_dp *dp_display);
 int dp_display_get_test_bpp(struct msm_dp *dp_display);
 
+void __init msm_dp_pll_driver_register(void);
+void __exit msm_dp_pll_driver_unregister(void);
+
 #endif /* _DP_DISPLAY_H_ */
drivers/gpu/drm/msm/dp/dp_parser.h +4 -0
index 1dc592f..e775cbc 100644
@@ -6,6 +6,8 @@
 #ifndef _DP_PARSER_H_
 #define _DP_PARSER_H_
 
+#include "pll/dp_pll.h"
+
 #define DP_LABEL "MDSS DP DISPLAY"
 #define AUX_CFG_LEN    10
 #define DP_MAX_PIXEL_CLK_KHZ   675000
@@ -193,6 +195,8 @@ struct dp_parser {
        bool combo_phy_en;
        struct dp_io io;
        struct dp_display_data disp_data;
+       struct msm_dp_pll *pll;
+       struct device *pll_dev;
        const struct dp_regulator_cfg *regulator_cfg;
        u8 l_map[4];
        struct dp_aux_cfg aux_cfg[AUX_CFG_LEN];
drivers/gpu/drm/msm/dp/dp_power.h +0 -0
index 52b6194..b24b2c5 100644

drivers/gpu/drm/msm/dp/dp_power.h line 19

Quoted 7 lines
drivers/gpu/drm/msm/dp/dp_power.h +0 -1
@@ -14,7 +14,6 @@
  * @init: initializes the regulators/core clocks/GPIOs/pinctrl
  * @deinit: turns off the regulators/core clocks/GPIOs/pinctrl
  * @clk_enable: enable/disable the DP clocks
- * @set_link_clk_parent: set the parent of DP link clock
  * @set_pixel_clk_parent: set the parent of DP pixel clock
  */
 struct dp_power {

This can be another patch to update documentation?

Quoted 28 lines
drivers/gpu/drm/msm/dp/pll/dp_pll.c new file +3 -0
new file mode 100644
index 0000000..11e26cf
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights 

reserved.
+ */

drivers/gpu/drm/msm/dp/pll/dp_pll.c new file +13 -0
+
+#include "dp_pll.h"
+
+int msm_dp_pll_util_parse_dt_clock(struct platform_device *pdev,
+                                       struct msm_dp_pll *pll)
+{
+       u32 i = 0, rc = 0;
+       struct dss_module_power *mp = &pll->mp;
+       const char *clock_name;
+       u32 clock_rate;
+
+       mp->num_clk = of_property_count_strings(pdev->dev.of_node,
+                                                       

"clock-names");

drivers/gpu/drm/msm/dp/pll/dp_pll.c new file +2 -0
+       if (mp->num_clk <= 0) {
+               DRM_DEV_ERROR(&pdev->dev, "%s:clocks are not 

defined\n",

drivers/gpu/drm/msm/dp/pll/dp_pll.c line 24

Quoted 5 lines
drivers/gpu/drm/msm/dp/pll/dp_pll.c new file +5 -0
+                               __func__);
+               goto clk_err;
+       }
+
+       mp->clk_config = devm_kzalloc(&pdev->dev,

Use devm_kcalloc() for arrays.

Quoted 52 lines

+ sizeof(struct dss_clk) * mp->num_clk,
GFP_KERNEL);

drivers/gpu/drm/msm/dp/pll/dp_pll.c new file +8 -0
+       if (!mp->clk_config) {
+               rc = -ENOMEM;
+               mp->num_clk = 0;
+               goto clk_err;
+       }
+
+       for (i = 0; i < mp->num_clk; i++) {
+               of_property_read_string_index(pdev->dev.of_node, 

"clock-names",
+ i,
&clock_name);

drivers/gpu/drm/msm/dp/pll/dp_pll.c new file +4 -0
+               strlcpy(mp->clk_config[i].clk_name, clock_name,
+                               sizeof(mp->clk_config[i].clk_name));
+
+               of_property_read_u32_index(pdev->dev.of_node, 

"clock-rate",
+ i,
&clock_rate);

drivers/gpu/drm/msm/dp/pll/dp_pll.c new file +29 -0
+               mp->clk_config[i].rate = clock_rate;
+
+               if (!clock_rate)
+                       mp->clk_config[i].type = DSS_CLK_AHB;
+               else
+                       mp->clk_config[i].type = DSS_CLK_PCLK;
+       }
+
+clk_err:
+       return rc;
+}
+
+struct msm_dp_pll *msm_dp_pll_init(struct platform_device *pdev,
+                       enum msm_dp_pll_type type, int id)
+{
+       struct device *dev = &pdev->dev;
+       struct msm_dp_pll *pll;
+
+       switch (type) {
+       case MSM_DP_PLL_10NM:
+               pll = msm_dp_pll_10nm_init(pdev, id);
+               break;
+       default:
+               pll = ERR_PTR(-ENXIO);
+               break;
+       }
+
+       if (IS_ERR(pll)) {
+               DRM_DEV_ERROR(dev, "%s: failed to init DP PLL\n", 

__func__);

drivers/gpu/drm/msm/dp/pll/dp_pll.c new file +2 -0
+               return pll;
+       }

drivers/gpu/drm/msm/dp/pll/dp_pll.c line 80

Quoted 10 lines
drivers/gpu/drm/msm/dp/pll/dp_pll.c new file +10 -0
+
+       pll->type = type;
+
+       DRM_DEBUG_DP("DP:%d PLL registered", id);
+
+       return pll;
+}
+
+static const struct of_device_id dp_pll_dt_match[] = {
+       { .compatible = "qcom,dp-pll-10nm" },

Add a terminator here { }.

drivers/gpu/drm/msm/dp/pll/dp_pll.c line 93

Quoted 13 lines
drivers/gpu/drm/msm/dp/pll/dp_pll.c new file +13 -0
+};
+
+static int dp_pll_driver_probe(struct platform_device *pdev)
+{
+       struct msm_dp_pll *pll;
+       struct device *dev = &pdev->dev;
+       const struct of_device_id *match;
+
+       match = of_match_node(dp_pll_dt_match, dev->of_node);
+       if (!match)
+               return -ENODEV;
+
+       /* Currently supporting only 10nm-DP PLL */

Please remove this useless comment that can only require being updated in the future.

Quoted 5 lines
drivers/gpu/drm/msm/dp/pll/dp_pll.c new file +4 -0
+       pll = msm_dp_pll_init(pdev, MSM_DP_PLL_10NM, 0);
+       if (IS_ERR_OR_NULL(pll)) {
+               DRM_DEV_ERROR(dev,
+                       "%s: pll init failed: %ld, need separate pll 

clk driver\n",

What does this error mean?

Quoted 50 lines
drivers/gpu/drm/msm/dp/pll/dp_pll.c new file +39 -0
+                       __func__, PTR_ERR(pll));
+               return -ENODEV;
+       }
+
+       platform_set_drvdata(pdev, pll);
+
+       return 0;
+}
+
+static int dp_pll_driver_remove(struct platform_device *pdev)
+{
+       struct msm_dp_pll *pll = platform_get_drvdata(pdev);
+
+       if (pll)
+               pll = NULL;
+
+       platform_set_drvdata(pdev, NULL);
+
+       return 0;
+}
+
+static struct platform_driver dp_pll_platform_driver = {
+       .probe      = dp_pll_driver_probe,
+       .remove     = dp_pll_driver_remove,
+       .driver     = {
+               .name   = "msm_dp_pll",
+               .of_match_table = dp_pll_dt_match,
+       },
+};
+
+void __init msm_dp_pll_driver_register(void)
+{
+       platform_driver_register(&dp_pll_platform_driver);
+}
+
+void __exit msm_dp_pll_driver_unregister(void)
+{
+       platform_driver_unregister(&dp_pll_platform_driver);
+}
drivers/gpu/drm/msm/dp/pll/dp_pll.h new file +3 -0
new file mode 100644
index 0000000..185f1e9
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights 

reserved.
+ */

drivers/gpu/drm/msm/dp/pll/dp_pll.h line 10

Quoted 6 lines
drivers/gpu/drm/msm/dp/pll/dp_pll.h new file +6 -0
+
+#ifndef __DP_PLL_H
+#define __DP_PLL_H
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>

You're making a clk provider. Please Cc clk maintainers and [email protected]

Quoted 16 lines
drivers/gpu/drm/msm/dp/pll/dp_pll.h new file +16 -0
+#include <linux/platform_device.h>
+
+#include "dpu_io_util.h"
+#include "msm_drv.h"
+
+#define PLL_REG_W(base, offset, data)  \
+                               writel((data), (base) + (offset))
+#define PLL_REG_R(base, offset)        readl((base) + (offset))
+
+enum msm_dp_pll_type {
+       MSM_DP_PLL_10NM,
+       MSM_DP_PLL_MAX
+};
+
+struct msm_dp_pll {
+       enum msm_dp_pll_type type;
Quoted 8 lines

drivers/gpu/drm/msm/dp/pll/dp_pll.h line 31

Quoted 5 lines
drivers/gpu/drm/msm/dp/pll/dp_pll.h new file +5 -0
+       struct clk_hw clk_hw;
+       unsigned long   rate;           /* current vco rate */
+       u64             min_rate;       /* min vco rate */
+       u64             max_rate;       /* max vco rate */
+       bool            pll_on;

Are these struct members here to save/restore the rate across suspend/resume?

We are trying to retain the current vco rate across suspend/resume.
min_rate and max_rate are the minimum and maximum vco rates that are possible for the chipset.

Quoted 170 lines

drivers/gpu/drm/msm/dp/pll/dp_pll.h line 40

Quoted 11 lines
drivers/gpu/drm/msm/dp/pll/dp_pll.h new file +2 -0
+       void            *priv;
+       /* Pll specific resources like GPIO, power supply, clocks, 

etc*/

drivers/gpu/drm/msm/dp/pll/dp_pll.h new file +7 -0
+       struct dss_module_power mp;
+       int (*get_provider)(struct msm_dp_pll *pll,
+                       struct clk **link_clk_provider,
+                       struct clk **pixel_clk_provider);
+};
+
+#define hw_clk_to_pll(x) container_of(x, struct msm_dp_pll, clk_hw)

Why not to_msm_dp_pll() for this macro.

drivers/gpu/drm/msm/dp/pll/dp_pll.h line 52

Quoted 13 lines
drivers/gpu/drm/msm/dp/pll/dp_pll.h new file +9 -0
+
+struct msm_dp_pll *msm_dp_pll_init(struct platform_device *pdev,
+                               enum msm_dp_pll_type type, int id);
+
+int msm_dp_pll_util_parse_dt_clock(struct platform_device *pdev,
+                               struct msm_dp_pll *pll);
+
+#ifdef CONFIG_DRM_MSM_DP_10NM_PLL
+struct msm_dp_pll *msm_dp_pll_10nm_init(struct platform_device *pdev, 

int id);

drivers/gpu/drm/msm/dp/pll/dp_pll.h new file +3 -0
+#else
+static inline struct msm_dp_pll *msm_dp_pll_10nm_init
+                               (struct platform_device *pdev, int id)

I would write this as

static inline struct msm_dp_pll * msm_dp_pll_10nm_init(struct platform_device *pdev, int id)

so that we don't have parameters all by themselves.

Quoted 56 lines
drivers/gpu/drm/msm/dp/pll/dp_pll.h new file +5 -0
+{
+       return ERR_PTR(-ENODEV);
+}
+#endif
+#endif /* __DP_PLL_H */
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +3 -0
new file mode 100644
index 0000000..5991e09
@@ -0,0 +1,406 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights 

reserved.
+ */

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +40 -0
+
+/*
+ * Display Port PLL driver block diagram for branch clocks
+ *
+ *              +------------------------------+
+ *              |         DP_VCO_CLK           |
+ *              |                              |
+ *              |    +-------------------+     |
+ *              |    |   (DP PLL/VCO)    |     |
+ *              |    +---------+---------+     |
+ *              |              v               |
+ *              |   +----------+-----------+   |
+ *              |   | hsclk_divsel_clk_src |   |
+ *              |   +----------+-----------+   |
+ *              +------------------------------+
+ *                              |
+ *          +---------<---------v------------>----------+
+ *          |                                           |
+ * +--------v---------+                                 |
+ * |    dp_phy_pll    |                                 |
+ * |     link_clk     |                                 |
+ * +--------+---------+                                 |
+ *          |                                           |
+ *          |                                           |
+ *          v                                           v
+ * Input to DISPCC block                                |
+ * for link clk, crypto clk                             |
+ * and interface clock                                  |
+ *                                                      |
+ *                                                      |
+ *      +--------<------------+-----------------+---<---+
+ *      |                     |                 |
+ * +----v---------+  +--------v-----+  +--------v------+
+ * | vco_divided  |  | vco_divided  |  | vco_divided   |
+ * |    _clk_src  |  |    _clk_src  |  |    _clk_src   |
+ * |              |  |              |  |               |
+ * |divsel_six    |  |  divsel_two  |  |  divsel_four  |
+ * +-------+------+  +-----+--------+  +--------+------+
+ *         |                 |                  |
+ *         v---->----------v-------------<------v

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 54

Quoted 10 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +10 -0
+ *                         |
+ *              +----------+---------+
+ *              |   dp_phy_pll_vco   |
+ *              |       div_clk      |
+ *              +---------+----------+
+ *                        |
+ *                        v
+ *              Input to DISPCC block
+ *              for DP pixel clock
+ *

Awesome diagram!

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c

Quoted 3 lines

+ */

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +2 -0
+
+#include <linux/clk.h>

Please try to avoid making a clk provider a clk consumer at the same time.

Quoted 40 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +40 -0
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/regmap.h>
+
+#include "dp_pll_10nm.h"
+
+#define NUM_PROVIDED_CLKS              2
+
+#define DP_LINK_CLK_SRC                        0
+#define DP_PIXEL_CLK_SRC               1
+
+static struct dp_pll_10nm *dp_pdb;
+
+static const struct clk_ops dp_10nm_vco_clk_ops = {
+       .recalc_rate = dp_vco_recalc_rate_10nm,
+       .set_rate = dp_vco_set_rate_10nm,
+       .round_rate = dp_vco_round_rate_10nm,
+       .prepare = dp_vco_prepare_10nm,
+       .unprepare = dp_vco_unprepare_10nm,
+};
+
+struct dp_pll_10nm_pclksel {
+       struct clk_hw hw;
+
+       /* divider params */
+       u8 shift;
+       u8 width;
+       u8 flags; /* same flags as used by clk_divider struct */
+
+       struct dp_pll_10nm *pll;
+};
+#define to_pll_10nm_pclksel(_hw) \
+       container_of(_hw, struct dp_pll_10nm_pclksel, hw)
+
+static int dp_mux_set_parent_10nm(struct clk_hw *hw, u8 val)
+{
+       struct dp_pll_10nm_pclksel *pclksel = to_pll_10nm_pclksel(hw);
+       struct dp_pll_10nm *dp_res = pclksel->pll;
+       u32 auxclk_div;

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 107

Quoted 10 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +10 -0
+
+       auxclk_div = PLL_REG_R(dp_res->phy_base, REG_DP_PHY_VCO_DIV);
+       auxclk_div &= ~0x03;    /* bits 0 to 1 */
+
+       if (val == 0) /* mux parent index = 0 */
+               auxclk_div |= 1;
+       else if (val == 1) /* mux parent index = 1 */
+               auxclk_div |= 2;
+       else if (val == 2) /* mux parent index = 2 */
+               auxclk_div |= 0;

These comments are just saying what the code says. Can you remove them?

Quoted 5 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +4 -0
+
+       PLL_REG_W(dp_res->phy_base,
+                       REG_DP_PHY_VCO_DIV, auxclk_div);
+       DRM_DEBUG_DP("%s: mux=%d auxclk_div=%x\n", __func__, val, 

auxclk_div);

Quoted 6 lines

Is it a mux and divider combined? The diagram seems to imply that the mux picks between fixed dividers but I don't see why that isn't just a divider clk because the parent is always the same for the fixed dividers. Put another way, why is a parent API being used for this? It would make more sense to just treat this like a divider and use rate setting APIs instead.

I am not sure if I completely understand the concern. I am thinking that you are suggesting that we remove the mux altogether and just use the fixed dividers, while programming the registers as part of the the set_rate for those dividers. If that is the case, then we would have to list 3 parents for the RCG clock in display clock controller (dispcc). However, as per the clock plan, the RCG clocks have a well defined set of parents (DP PLL, or the reference clock). It would be incorrect to list all these PLL specific clocks as parents of the RCG. We are required to list a a single provider clock for DP Pixel clock. Hence the choice of a mux clock.

Quoted 71 lines
Quoted 51 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +22 -0
+
+       return 0;
+}
+
+static u8 dp_mux_get_parent_10nm(struct clk_hw *hw)
+{
+       u32 auxclk_div = 0;
+       struct dp_pll_10nm_pclksel *pclksel = to_pll_10nm_pclksel(hw);
+       struct dp_pll_10nm *dp_res = pclksel->pll;
+       u8 val = 0;
+
+       auxclk_div = PLL_REG_R(dp_res->phy_base, REG_DP_PHY_VCO_DIV);
+       auxclk_div &= 0x03;
+
+       if (auxclk_div == 1) /* Default divider */
+               val = 0;
+       else if (auxclk_div == 2)
+               val = 1;
+       else if (auxclk_div == 0)
+               val = 2;
+
+       DRM_DEBUG_DP("%s: auxclk_div=%d, val=%d\n", __func__, 

auxclk_div, val);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +23 -0
+
+       return val;
+}
+
+static int clk_mux_determine_rate(struct clk_hw *hw,
+                                    struct clk_rate_request *req)
+{
+       unsigned long rate = 0;
+       int ret = 0;
+
+       rate = clk_get_rate(hw->clk);
+
+       if (rate <= 0) {
+               DRM_ERROR("Rate is not set properly\n");
+               return -EINVAL;
+       }
+
+       req->rate = rate;
+
+       DRM_DEBUG_DP("%s: rate=%ld\n", __func__, req->rate);
+       /* Set the new parent of mux if there is a new valid parent */
+       if (hw->clk && req->best_parent_hw->clk) {
+               ret = clk_set_parent(hw->clk, 

req->best_parent_hw->clk);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +2 -0
+               if (ret) {
+                       DRM_ERROR("%s: clk_set_parent failed: 

ret=%d\n",

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 166

Quoted 8 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +8 -0
+                                       __func__, ret);
+                       return ret;
+               }
+       }
+       return 0;
+}
+
+static unsigned long mux_recalc_rate(struct clk_hw *hw,

Please prefix all functions with dp_pll_

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 172

Quoted 6 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +6 -0
+                                       unsigned long parent_rate)
+{
+       struct clk *div_clk = NULL, *vco_clk = NULL;
+       struct msm_dp_pll *vco = NULL;
+
+       div_clk = clk_get_parent(hw->clk);

We have clk_hw_get_parent() for this stuff. Please use it.

Quoted 10 lines

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 180

Quoted 8 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +8 -0
+       if (!div_clk)
+               return 0;
+
+       vco_clk = clk_get_parent(div_clk);
+       if (!vco_clk)
+               return 0;
+
+       vco = hw_clk_to_pll(__clk_get_hw(vco_clk));

Please don't use __clk_get_hw().

Could you please elaborate on this or what to use. Should we use the api implementation directly.

Quoted 23 lines

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 189

Quoted 10 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +9 -0
+       if (!vco)
+               return 0;
+
+       if (vco->rate == DP_VCO_HSCLK_RATE_8100MHZDIV1000)
+               return (vco->rate / 6);
+       else if (vco->rate == DP_VCO_HSCLK_RATE_5400MHZDIV1000)
+               return (vco->rate / 4);
+       else
+               return (vco->rate / 2);

else return is shunned. Just have return instead.

Quoted 10 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +10 -0
+}
+
+static int dp_pll_10nm_get_provider(struct msm_dp_pll *pll,
+                                    struct clk **link_clk_provider,
+                                    struct clk **pixel_clk_provider)
+{
+       struct dp_pll_10nm *pll_10nm = to_dp_pll_10nm(pll);
+       struct clk_hw_onecell_data *hw_data = pll_10nm->hw_data;
+
+       if (link_clk_provider)
Quoted 8 lines
Quoted 5 lines

+ *link_clk_provider =
hw_data->hws[DP_LINK_CLK_SRC]->clk;

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +2 -0
+       if (pixel_clk_provider)
+               *pixel_clk_provider = 

hw_data->hws[DP_PIXEL_CLK_SRC]->clk;

I don't know what this is but it looks weird. Is this a clk provider on top of another clk provider?

Here we are setting the parent of RCG.
RCG needs called by the consumer to set the parent. DP is a consumer setting a rate on provider.

Quoted 25 lines
Quoted 14 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +12 -0
+
+       return 0;
+}
+
+static const struct clk_ops dp_10nm_pclksel_clk_ops = {
+       .get_parent = dp_mux_get_parent_10nm,
+       .set_parent = dp_mux_set_parent_10nm,
+       .recalc_rate = mux_recalc_rate,
+       .determine_rate = clk_mux_determine_rate,
+};
+
+static struct clk_hw *dp_pll_10nm_pixel_clk_sel(struct dp_pll_10nm 

*pll_10nm)

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 222

Quoted 8 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +8 -0
+{
+       struct device *dev = &pll_10nm->pdev->dev;
+       struct dp_pll_10nm_pclksel *pll_pclksel;
+       struct clk_init_data pclksel_init = {
+               .parent_names = (const char *[]){
+                               "dp_vco_divsel_two_clk_src",
+                               "dp_vco_divsel_four_clk_src",
+                               "dp_vco_divsel_six_clk_src" },

It looks like a clk divider still, not a mux.

Quoted 6 lines

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 225

Quoted 3 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +3 -0
+               .num_parents = 3,
+               .name = "dp_phy_pll_vco_div_clk",
+               .flags = CLK_IGNORE_UNUSED,

Why? Please comment why this is needed in the code. But more importantly, please just don't use this flag.

We are working to remove this flag CLK_IGNORE_UNUSED.

Quoted 200 lines
Quoted 18 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +5 -0
+               .ops = &dp_10nm_pclksel_clk_ops,
+       };
+       int ret;
+
+       pll_pclksel = devm_kzalloc(dev, sizeof(*pll_pclksel), 

GFP_KERNEL);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +11 -0
+       if (!pll_pclksel)
+               return ERR_PTR(-ENOMEM);
+
+       pll_pclksel->pll = pll_10nm;
+       pll_pclksel->shift = 0;
+       pll_pclksel->width = 4;
+       pll_pclksel->flags = CLK_DIVIDER_ONE_BASED;
+       pll_pclksel->hw.init = &pclksel_init;
+
+       ret = clk_hw_register(dev, &pll_pclksel->hw);
+       if (ret)

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 251

Quoted 10 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +10 -0
+               return ERR_PTR(ret);
+
+       return &pll_pclksel->hw;
+}
+
+static int dp_pll_10nm_register(struct dp_pll_10nm *pll_10nm)
+{
+       char clk_name[32], parent[32], vco_name[32];
+       struct clk_init_data vco_init = {
+               .parent_names = (const char *[]){ "bi_tcxo" },

Please use the new way of specifying clk parents through .fw_name and DT indices.

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 254

Quoted 3 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +3 -0
+               .num_parents = 1,
+               .name = vco_name,
+               .flags = CLK_IGNORE_UNUSED,

Ugh there's that flag again.

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 266

Quoted 12 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +12 -0
+               .ops = &dp_10nm_vco_clk_ops,
+       };
+       struct device *dev = &pll_10nm->pdev->dev;
+       struct clk_hw **hws = pll_10nm->hws;
+       struct clk_hw_onecell_data *hw_data;
+       struct clk_hw *hw;
+       int num = 0;
+       int ret;
+
+       DRM_DEBUG_DP("DP->id = %d", pll_10nm->id);
+
+       hw_data = devm_kzalloc(dev, sizeof(*hw_data) +

Use devm_kcalloc()

Quoted 5 lines

+ NUM_PROVIDED_CLKS * sizeof(struct
clk_hw *),

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +3 -0
+                              GFP_KERNEL);
+       if (!hw_data)
+               return -ENOMEM;

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 280

Quoted 10 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +10 -0
+
+       snprintf(vco_name, 32, "dp_vco_clk");
+       pll_10nm->base.clk_hw.init = &vco_init;
+       ret = clk_hw_register(dev, &pll_10nm->base.clk_hw);
+       if (ret)
+               return ret;
+       hws[num++] = &pll_10nm->base.clk_hw;
+
+       snprintf(clk_name, 32, "dp_phy_pll_link_clk");
+       snprintf(parent, 32, "dp_vco_clk");

Is there a need to make these names with snprintf?

Quoted 129 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +44 -0
+       hw = clk_hw_register_fixed_factor(dev, clk_name, parent,
+                                         CLK_SET_RATE_PARENT, 1, 10);
+       if (IS_ERR(hw))
+               return PTR_ERR(hw);
+       hws[num++] = hw;
+       hw_data->hws[DP_LINK_CLK_SRC] = hw;
+
+       snprintf(clk_name, 32, "dp_vco_divsel_two_clk_src");
+       snprintf(parent, 32, "dp_vco_clk");
+       hw = clk_hw_register_fixed_factor(dev, clk_name, parent,
+                                         0, 1, 2);
+       if (IS_ERR(hw))
+               return PTR_ERR(hw);
+       hws[num++] = hw;
+
+       snprintf(clk_name, 32, "dp_vco_divsel_four_clk_src");
+       snprintf(parent, 32, "dp_vco_clk");
+       hw = clk_hw_register_fixed_factor(dev, clk_name, parent,
+                                         0, 1, 4);
+       if (IS_ERR(hw))
+               return PTR_ERR(hw);
+       hws[num++] = hw;
+
+       snprintf(clk_name, 32, "dp_vco_divsel_six_clk_src");
+       snprintf(parent, 32, "dp_vco_clk");
+       hw = clk_hw_register_fixed_factor(dev, clk_name, parent,
+                                         0, 1, 6);
+       if (IS_ERR(hw))
+               return PTR_ERR(hw);
+       hws[num++] = hw;
+
+       hw = dp_pll_10nm_pixel_clk_sel(pll_10nm);
+       if (IS_ERR(hw))
+               return PTR_ERR(hw);
+
+       hws[num++] = hw;
+       hw_data->hws[DP_PIXEL_CLK_SRC] = hw;
+
+       pll_10nm->num_hws = num;
+
+       hw_data->num = NUM_PROVIDED_CLKS;
+       pll_10nm->hw_data = hw_data;
+
+       ret = of_clk_add_hw_provider(dev->of_node, 

of_clk_hw_onecell_get,

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +3 -0
+                                    pll_10nm->hw_data);
+       if (ret) {
+               DRM_DEV_ERROR(dev, "failed to register clk provider: 

%d\n",

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +8 -0
+                               ret);
+               return ret;
+       }
+
+       return ret;
+}
+
+struct msm_dp_pll *msm_dp_pll_10nm_init(struct platform_device *pdev, 

int id)

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +10 -0
+{
+       struct dp_pll_10nm *dp_10nm_pll;
+       struct msm_dp_pll *pll;
+       int ret;
+
+       if (!pdev)
+               return ERR_PTR(-ENODEV);
+
+       dp_10nm_pll = devm_kzalloc(&pdev->dev,
+                                       sizeof(*dp_10nm_pll), 

GFP_KERNEL);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +10 -0
+       if (!dp_10nm_pll)
+               return ERR_PTR(-ENOMEM);
+
+       DRM_DEBUG_DP("DP PLL%d", id);
+
+       dp_10nm_pll->pdev = pdev;
+       dp_10nm_pll->id = id;
+       dp_pdb = dp_10nm_pll;
+
+       dp_10nm_pll->pll_base = msm_ioremap(pdev, "pll_base", 

"DP_PLL");

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +2 -0
+       if (IS_ERR_OR_NULL(dp_10nm_pll->pll_base)) {
+               DRM_DEV_ERROR(&pdev->dev, "failed to map CMN PLL 

base\n");

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +4 -0
+               return ERR_PTR(-ENOMEM);
+       }
+
+       dp_10nm_pll->phy_base = msm_ioremap(pdev, "phy_base", 

"DP_PHY");

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +2 -0
+       if (IS_ERR_OR_NULL(dp_10nm_pll->phy_base)) {
+               DRM_DEV_ERROR(&pdev->dev, "failed to map CMN PHY 

base\n");

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +7 -0
+               return ERR_PTR(-ENOMEM);
+       }
+
+       dp_10nm_pll->ln_tx0_base = msm_ioremap(pdev, "ln_tx0_base",
+                                               "DP_LN_TX0");
+       if (IS_ERR_OR_NULL(dp_10nm_pll->ln_tx0_base)) {
+               DRM_DEV_ERROR(&pdev->dev, "failed to map CMN LN_TX0 

base\n");

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +7 -0
+               return ERR_PTR(-ENOMEM);
+       }
+
+       dp_10nm_pll->ln_tx1_base = msm_ioremap(pdev, "ln_tx1_base",
+                                               "DP_LN_TX1");
+       if (IS_ERR_OR_NULL(dp_10nm_pll->ln_tx1_base)) {
+               DRM_DEV_ERROR(&pdev->dev, "failed to map CMN LN_TX1 

base\n");

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +7 -0
+               return ERR_PTR(-ENOMEM);
+       }
+
+       ret = of_property_read_u32(pdev->dev.of_node, "cell-index",
+                               &dp_10nm_pll->index);
+       if (ret) {
+               DRM_ERROR("Unable to get the cell-index ret=%d\n", 

ret);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +4 -0
+               dp_10nm_pll->index = 0;
+       }
+
+       ret = msm_dp_pll_util_parse_dt_clock(pdev, 

&dp_10nm_pll->base);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +8 -0
+       if (ret) {
+               DRM_ERROR("Unable to parse dt clocks ret=%d\n", ret);
+               return ERR_PTR(ret);
+       }
+
+       ret = dp_pll_10nm_register(dp_10nm_pll);
+       if (ret) {
+               DRM_DEV_ERROR(&pdev->dev, "failed to register PLL: 

%d\n", ret);

Quoted 8 lines

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 402

Quoted 6 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +6 -0
+               return ERR_PTR(ret);
+       }
+
+       pll = &dp_10nm_pll->base;
+       pll->min_rate = DP_VCO_HSCLK_RATE_1620MHZDIV1000;
+       pll->max_rate = DP_VCO_HSCLK_RATE_8100MHZDIV1000;

Maybe min/max can be set with clk_hw_set_rate_range()?

Here pll->min_rate and pll->max_rate stores the VCO range in local structure.

Quoted 138 lines
Quoted 89 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +4 -0
+       pll->get_provider = dp_pll_10nm_get_provider;
+
+       return pll;
+}
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.h new file +3 -0
new file mode 100644
index 0000000..8466463
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights 

reserved.
+ */

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.h new file +61 -0
+
+#ifndef __DP_PLL_10NM_H
+#define __DP_PLL_10NM_H
+
+#include "dp_pll.h"
+#include "dp_reg.h"
+
+#define DP_VCO_HSCLK_RATE_1620MHZDIV1000       1620000UL
+#define DP_VCO_HSCLK_RATE_2700MHZDIV1000       2700000UL
+#define DP_VCO_HSCLK_RATE_5400MHZDIV1000       5400000UL
+#define DP_VCO_HSCLK_RATE_8100MHZDIV1000       8100000UL
+
+#define NUM_DP_CLOCKS_MAX                      6
+
+#define DP_PHY_PLL_POLL_SLEEP_US               500
+#define DP_PHY_PLL_POLL_TIMEOUT_US             10000
+
+#define DP_VCO_RATE_8100MHZDIV1000             8100000UL
+#define DP_VCO_RATE_9720MHZDIV1000             9720000UL
+#define DP_VCO_RATE_10800MHZDIV1000            10800000UL
+
+struct dp_pll_10nm {
+       struct msm_dp_pll base;
+
+       int id;
+       struct platform_device *pdev;
+
+       void __iomem *pll_base;
+       void __iomem *phy_base;
+       void __iomem *ln_tx0_base;
+       void __iomem *ln_tx1_base;
+
+       /* private clocks: */
+       struct clk_hw *hws[NUM_DP_CLOCKS_MAX];
+       u32 num_hws;
+
+       /* clock-provider: */
+       struct clk_hw_onecell_data *hw_data;
+
+       /* lane and orientation settings */
+       u8 lane_cnt;
+       u8 orientation;
+
+       /* COM PHY settings */
+       u32 hsclk_sel;
+       u32 dec_start_mode0;
+       u32 div_frac_start1_mode0;
+       u32 div_frac_start2_mode0;
+       u32 div_frac_start3_mode0;
+       u32 integloop_gain0_mode0;
+       u32 integloop_gain1_mode0;
+       u32 vco_tune_map;
+       u32 lock_cmp1_mode0;
+       u32 lock_cmp2_mode0;
+       u32 lock_cmp3_mode0;
+       u32 lock_cmp_en;
+
+       /* PHY vco divider */
+       u32 phy_vco_div;
+       /*
+        * Certain pll's needs to update the same vco rate after 

resume in

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.h new file +11 -0
+        * suspend/resume scenario. Cached the vco rate for such plls.
+        */
+       unsigned long   vco_cached_rate;
+       u32             cached_cfg0;
+       u32             cached_cfg1;
+       u32             cached_outdiv;
+
+       uint32_t index;
+};
+
+#define to_dp_pll_10nm(x)      container_of(x, struct dp_pll_10nm, 
Quoted 1 line

base)

Why is this struct in the header file? Does something outside of the PLL driver need to access this struct?

Quoted 32 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.h new file +10 -0
+
+int dp_vco_set_rate_10nm(struct clk_hw *hw, unsigned long rate,
+                               unsigned long parent_rate);
+unsigned long dp_vco_recalc_rate_10nm(struct clk_hw *hw,
+                               unsigned long parent_rate);
+long dp_vco_round_rate_10nm(struct clk_hw *hw, unsigned long rate,
+                               unsigned long *parent_rate);
+int dp_vco_prepare_10nm(struct clk_hw *hw);
+void dp_vco_unprepare_10nm(struct clk_hw *hw);
+#endif /* __DP_PLL_10NM_H */
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +3 -0
new file mode 100644
index 0000000..db3983d5
@@ -0,0 +1,524 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights 

reserved.
+ */

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +11 -0
+
+#define pr_fmt(fmt)    "%s: " fmt, __func__
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+
+#include "dp_hpd.h"
+#include "dp_pll.h"
+#include "dp_pll_10nm.h"

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c line 25

Quoted 10 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +10 -0
+
+static int dp_vco_pll_init_db_10nm(struct msm_dp_pll *pll,
+               unsigned long rate)
+{
+       struct dp_pll_10nm *dp_res = to_dp_pll_10nm(pll);
+       u32 spare_value = 0;
+
+       spare_value = PLL_REG_R(dp_res->phy_base, REG_DP_PHY_SPARE0);
+       dp_res->lane_cnt = spare_value & 0x0F;
+       dp_res->orientation = (spare_value & 0xF0) >> 4;

Can this be read at any time from the spare register? Just curious why

Quoted 8 lines

we need to read this here in init vs. when it's actually used. We could avoid stashing away these values in the struct. It also looks like the PLL part is intimately tied to the phy, but there isn't an actual phy driver. Why? It looks like the phy driver is a clk driver, and the clk framework is not prepared to handle things like lane count and orientation. I'd expect that to come through a PHY framework and then have the phy driver control the PLL by powering it back up and changing it's rates, etc. Not everything needs to be done through clk APIs.

Presently we need this spare register information in many functions
to program dp hardware. There are atleast 6 instances we use them, so we are trying to store in the structure once.
Unfortunately msm DP pll and phy is one hardware tightly bound.

Quoted 518 lines
Quoted 518 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +2 -0
+
+       DRM_DEBUG_DP("%s: spare_value=0x%x, ln_cnt=0x%x, 

orientation=0x%x\n",

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +93 -0
+                       __func__, spare_value, dp_res->lane_cnt,
+                       dp_res->orientation);
+
+       switch (rate) {
+       case DP_VCO_HSCLK_RATE_1620MHZDIV1000:
+               DRM_DEBUG_DP("%s: VCO rate: %ld\n", __func__,
+                               DP_VCO_RATE_9720MHZDIV1000);
+               dp_res->hsclk_sel = 0x0c;
+               dp_res->dec_start_mode0 = 0x69;
+               dp_res->div_frac_start1_mode0 = 0x00;
+               dp_res->div_frac_start2_mode0 = 0x80;
+               dp_res->div_frac_start3_mode0 = 0x07;
+               dp_res->integloop_gain0_mode0 = 0x3f;
+               dp_res->integloop_gain1_mode0 = 0x00;
+               dp_res->vco_tune_map = 0x00;
+               dp_res->lock_cmp1_mode0 = 0x6f;
+               dp_res->lock_cmp2_mode0 = 0x08;
+               dp_res->lock_cmp3_mode0 = 0x00;
+               dp_res->phy_vco_div = 0x1;
+               dp_res->lock_cmp_en = 0x00;
+               break;
+       case DP_VCO_HSCLK_RATE_2700MHZDIV1000:
+               DRM_DEBUG_DP("%s: VCO rate: %ld\n", __func__,
+                               DP_VCO_RATE_10800MHZDIV1000);
+               dp_res->hsclk_sel = 0x04;
+               dp_res->dec_start_mode0 = 0x69;
+               dp_res->div_frac_start1_mode0 = 0x00;
+               dp_res->div_frac_start2_mode0 = 0x80;
+               dp_res->div_frac_start3_mode0 = 0x07;
+               dp_res->integloop_gain0_mode0 = 0x3f;
+               dp_res->integloop_gain1_mode0 = 0x00;
+               dp_res->vco_tune_map = 0x00;
+               dp_res->lock_cmp1_mode0 = 0x0f;
+               dp_res->lock_cmp2_mode0 = 0x0e;
+               dp_res->lock_cmp3_mode0 = 0x00;
+               dp_res->phy_vco_div = 0x1;
+               dp_res->lock_cmp_en = 0x00;
+               break;
+       case DP_VCO_HSCLK_RATE_5400MHZDIV1000:
+               DRM_DEBUG_DP("%s: VCO rate: %ld\n", __func__,
+                               DP_VCO_RATE_10800MHZDIV1000);
+               dp_res->hsclk_sel = 0x00;
+               dp_res->dec_start_mode0 = 0x8c;
+               dp_res->div_frac_start1_mode0 = 0x00;
+               dp_res->div_frac_start2_mode0 = 0x00;
+               dp_res->div_frac_start3_mode0 = 0x0a;
+               dp_res->integloop_gain0_mode0 = 0x3f;
+               dp_res->integloop_gain1_mode0 = 0x00;
+               dp_res->vco_tune_map = 0x00;
+               dp_res->lock_cmp1_mode0 = 0x1f;
+               dp_res->lock_cmp2_mode0 = 0x1c;
+               dp_res->lock_cmp3_mode0 = 0x00;
+               dp_res->phy_vco_div = 0x2;
+               dp_res->lock_cmp_en = 0x00;
+               break;
+       case DP_VCO_HSCLK_RATE_8100MHZDIV1000:
+               DRM_DEBUG_DP("%s: VCO rate: %ld\n", __func__,
+                               DP_VCO_RATE_8100MHZDIV1000);
+               dp_res->hsclk_sel = 0x03;
+               dp_res->dec_start_mode0 = 0x69;
+               dp_res->div_frac_start1_mode0 = 0x00;
+               dp_res->div_frac_start2_mode0 = 0x80;
+               dp_res->div_frac_start3_mode0 = 0x07;
+               dp_res->integloop_gain0_mode0 = 0x3f;
+               dp_res->integloop_gain1_mode0 = 0x00;
+               dp_res->vco_tune_map = 0x00;
+               dp_res->lock_cmp1_mode0 = 0x2f;
+               dp_res->lock_cmp2_mode0 = 0x2a;
+               dp_res->lock_cmp3_mode0 = 0x00;
+               dp_res->phy_vco_div = 0x0;
+               dp_res->lock_cmp_en = 0x08;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int dp_config_vco_rate_10nm(struct msm_dp_pll *pll,
+               unsigned long rate)
+{
+       u32 res = 0;
+       struct dp_pll_10nm *dp_res = to_dp_pll_10nm(pll);
+
+       res = dp_vco_pll_init_db_10nm(pll, rate);
+       if (res) {
+               DRM_ERROR("VCO Init DB failed\n");
+               return res;
+       }
+
+       if (dp_res->lane_cnt != 4) {
+               if (dp_res->orientation == ORIENTATION_CC2)
+                       PLL_REG_W(dp_res->phy_base, REG_DP_PHY_PD_CTL, 

0x6d);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +2 -0
+               else
+                       PLL_REG_W(dp_res->phy_base, REG_DP_PHY_PD_CTL, 

0x75);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +5 -0
+       } else {
+               PLL_REG_W(dp_res->phy_base, REG_DP_PHY_PD_CTL, 0x7d);
+       }
+
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_SVS_MODE_CLK_SEL, 

0x01);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +4 -0
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_SYSCLK_EN_SEL, 0x37);
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_SYS_CLK_CTRL, 0x02);
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_CLK_ENABLE1, 0x0e);
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_SYSCLK_BUF_ENABLE, 

0x06);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +34 -0
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_CLK_SEL, 0x30);
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_CMN_CONFIG, 0x02);
+
+       /* Different for each clock rates */
+       PLL_REG_W(dp_res->pll_base,
+               QSERDES_COM_HSCLK_SEL, dp_res->hsclk_sel);
+       PLL_REG_W(dp_res->pll_base,
+               QSERDES_COM_DEC_START_MODE0, dp_res->dec_start_mode0);
+       PLL_REG_W(dp_res->pll_base,
+               QSERDES_COM_DIV_FRAC_START1_MODE0,
+               dp_res->div_frac_start1_mode0);
+       PLL_REG_W(dp_res->pll_base,
+               QSERDES_COM_DIV_FRAC_START2_MODE0,
+               dp_res->div_frac_start2_mode0);
+       PLL_REG_W(dp_res->pll_base,
+               QSERDES_COM_DIV_FRAC_START3_MODE0,
+               dp_res->div_frac_start3_mode0);
+       PLL_REG_W(dp_res->pll_base,
+               QSERDES_COM_INTEGLOOP_GAIN0_MODE0,
+               dp_res->integloop_gain0_mode0);
+       PLL_REG_W(dp_res->pll_base,
+               QSERDES_COM_INTEGLOOP_GAIN1_MODE0,
+               dp_res->integloop_gain1_mode0);
+       PLL_REG_W(dp_res->pll_base,
+               QSERDES_COM_VCO_TUNE_MAP, dp_res->vco_tune_map);
+       PLL_REG_W(dp_res->pll_base,
+               QSERDES_COM_LOCK_CMP1_MODE0, dp_res->lock_cmp1_mode0);
+       PLL_REG_W(dp_res->pll_base,
+               QSERDES_COM_LOCK_CMP2_MODE0, dp_res->lock_cmp2_mode0);
+       PLL_REG_W(dp_res->pll_base,
+               QSERDES_COM_LOCK_CMP3_MODE0, dp_res->lock_cmp3_mode0);
+
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_BG_TIMER, 0x0a);
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_CORECLK_DIV_MODE0, 

0x0a);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +2 -0
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_VCO_TUNE_CTRL, 0x00);
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 

0x3f);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +5 -0
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_CORE_CLK_EN, 0x1f);
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_PLL_IVCO, 0x07);
+       PLL_REG_W(dp_res->pll_base,
+               QSERDES_COM_LOCK_CMP_EN, dp_res->lock_cmp_en);
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_PLL_CCTRL_MODE0, 

0x36);
+ PLL_REG_W(dp_res->pll_base, QSERDES_COM_PLL_RCTRL_MODE0,
0x16);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +30 -0
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_CP_CTRL_MODE0, 0x06);
+
+       if (dp_res->orientation == ORIENTATION_CC2)
+               PLL_REG_W(dp_res->phy_base, REG_DP_PHY_MODE, 0x4c);
+       else
+               PLL_REG_W(dp_res->phy_base, REG_DP_PHY_MODE, 0x5c);
+
+       /* TX Lane configuration */
+       PLL_REG_W(dp_res->phy_base,
+                       REG_DP_PHY_TX0_TX1_LANE_CTL, 0x05);
+       PLL_REG_W(dp_res->phy_base,
+                       REG_DP_PHY_TX2_TX3_LANE_CTL, 0x05);
+
+       /* TX-0 register configuration */
+       PLL_REG_W(dp_res->ln_tx0_base,
+                       REG_DP_PHY_TXn_TRANSCEIVER_BIAS_EN, 0x1a);
+       PLL_REG_W(dp_res->ln_tx0_base,
+                       REG_DP_PHY_TXn_VMODE_CTRL1, 0x40);
+       PLL_REG_W(dp_res->ln_tx0_base,
+                       REG_DP_PHY_TXn_PRE_STALL_LDO_BOOST_EN, 0x30);
+       PLL_REG_W(dp_res->ln_tx0_base,
+                       REG_DP_PHY_TXn_INTERFACE_SELECT, 0x3d);
+       PLL_REG_W(dp_res->ln_tx0_base,
+                       REG_DP_PHY_TXn_CLKBUF_ENABLE, 0x0f);
+       PLL_REG_W(dp_res->ln_tx0_base,
+                       REG_DP_PHY_TXn_RESET_TSYNC_EN, 0x03);
+       PLL_REG_W(dp_res->ln_tx0_base,
+                       REG_DP_PHY_TXn_TRAN_DRVR_EMP_EN, 0x03);
+       PLL_REG_W(dp_res->ln_tx0_base,
+                       REG_DP_PHY_TXn_PARRATE_REC_DETECT_IDLE_EN, 

0x00);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +21 -0
+       PLL_REG_W(dp_res->ln_tx0_base,
+                       REG_DP_PHY_TXn_TX_INTERFACE_MODE, 0x00);
+       PLL_REG_W(dp_res->ln_tx0_base, REG_DP_PHY_TXn_TX_BAND, 0x4);
+
+       /* TX-1 register configuration */
+       PLL_REG_W(dp_res->ln_tx1_base,
+                       REG_DP_PHY_TXn_TRANSCEIVER_BIAS_EN, 0x1a);
+       PLL_REG_W(dp_res->ln_tx1_base,
+                       REG_DP_PHY_TXn_VMODE_CTRL1, 0x40);
+       PLL_REG_W(dp_res->ln_tx1_base,
+                       REG_DP_PHY_TXn_PRE_STALL_LDO_BOOST_EN, 0x30);
+       PLL_REG_W(dp_res->ln_tx1_base,
+                       REG_DP_PHY_TXn_INTERFACE_SELECT, 0x3d);
+       PLL_REG_W(dp_res->ln_tx1_base,
+                       REG_DP_PHY_TXn_CLKBUF_ENABLE, 0x0f);
+       PLL_REG_W(dp_res->ln_tx1_base,
+                       REG_DP_PHY_TXn_RESET_TSYNC_EN, 0x03);
+       PLL_REG_W(dp_res->ln_tx1_base,
+                       REG_DP_PHY_TXn_TRAN_DRVR_EMP_EN, 0x03);
+       PLL_REG_W(dp_res->ln_tx1_base,
+                       REG_DP_PHY_TXn_PARRATE_REC_DETECT_IDLE_EN, 

0x00);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +25 -0
+       PLL_REG_W(dp_res->ln_tx1_base,
+                       REG_DP_PHY_TXn_TX_INTERFACE_MODE, 0x00);
+       PLL_REG_W(dp_res->ln_tx1_base,
+                       REG_DP_PHY_TXn_TX_BAND, 0x4);
+
+       /* dependent on the vco frequency */
+       PLL_REG_W(dp_res->phy_base,
+                       REG_DP_PHY_VCO_DIV, dp_res->phy_vco_div);
+
+       return res;
+}
+
+static bool dp_10nm_pll_lock_status(struct dp_pll_10nm *dp_res)
+{
+       u32 status;
+       bool pll_locked;
+
+       /* poll for PLL lock status */
+       if (readl_poll_timeout_atomic((dp_res->pll_base +
+                       QSERDES_COM_C_READY_STATUS),
+                       status,
+                       ((status & BIT(0)) > 0),
+                       DP_PHY_PLL_POLL_SLEEP_US,
+                       DP_PHY_PLL_POLL_TIMEOUT_US)) {
+               DRM_ERROR("%s: C_READY status is not high. 

Status=%x\n",

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +70 -0
+                               __func__, status);
+               pll_locked = false;
+       } else {
+               pll_locked = true;
+       }
+
+       return pll_locked;
+}
+
+static bool dp_10nm_phy_rdy_status(struct dp_pll_10nm *dp_res)
+{
+       u32 status;
+       bool phy_ready = true;
+
+       /* poll for PHY ready status */
+       if (readl_poll_timeout_atomic((dp_res->phy_base +
+                       REG_DP_PHY_STATUS),
+                       status,
+                       ((status & (BIT(1))) > 0),
+                       DP_PHY_PLL_POLL_SLEEP_US,
+                       DP_PHY_PLL_POLL_TIMEOUT_US)) {
+               DRM_ERROR("%s: Phy_ready is not high. Status=%x\n",
+                               __func__, status);
+               phy_ready = false;
+       }
+
+       return phy_ready;
+}
+
+static int dp_pll_enable_10nm(struct clk_hw *hw)
+{
+       int rc = 0;
+       struct msm_dp_pll *pll = hw_clk_to_pll(hw);
+       struct dp_pll_10nm *dp_res = to_dp_pll_10nm(pll);
+       u32 bias_en, drvr_en;
+
+       PLL_REG_W(dp_res->phy_base, REG_DP_PHY_AUX_CFG2, 0x04);
+       PLL_REG_W(dp_res->phy_base, REG_DP_PHY_CFG, 0x01);
+       PLL_REG_W(dp_res->phy_base, REG_DP_PHY_CFG, 0x05);
+       PLL_REG_W(dp_res->phy_base, REG_DP_PHY_CFG, 0x01);
+       PLL_REG_W(dp_res->phy_base, REG_DP_PHY_CFG, 0x09);
+
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_RESETSM_CNTRL, 0x20);
+
+       if (!dp_10nm_pll_lock_status(dp_res)) {
+               rc = -EINVAL;
+               goto lock_err;
+       }
+
+       PLL_REG_W(dp_res->phy_base, REG_DP_PHY_CFG, 0x19);
+       /* poll for PHY ready status */
+       if (!dp_10nm_phy_rdy_status(dp_res)) {
+               rc = -EINVAL;
+               goto lock_err;
+       }
+
+       DRM_DEBUG_DP("%s: PLL is locked\n", __func__);
+
+       if (dp_res->lane_cnt == 1) {
+               bias_en = 0x3e;
+               drvr_en = 0x13;
+       } else {
+               bias_en = 0x3f;
+               drvr_en = 0x10;
+       }
+
+       if (dp_res->lane_cnt != 4) {
+               if (dp_res->orientation == ORIENTATION_CC1) {
+                       PLL_REG_W(dp_res->ln_tx1_base,
+                               REG_DP_PHY_TXn_HIGHZ_DRVR_EN, 

drvr_en);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +2 -0
+                       PLL_REG_W(dp_res->ln_tx1_base,
+                               REG_DP_PHY_TXn_TRANSCEIVER_BIAS_EN, 

bias_en);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +3 -0
+               } else {
+                       PLL_REG_W(dp_res->ln_tx0_base,
+                               REG_DP_PHY_TXn_HIGHZ_DRVR_EN, 

drvr_en);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +2 -0
+                       PLL_REG_W(dp_res->ln_tx0_base,
+                               REG_DP_PHY_TXn_TRANSCEIVER_BIAS_EN, 

bias_en);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +4 -0
+               }
+       } else {
+               PLL_REG_W(dp_res->ln_tx0_base,
+                               REG_DP_PHY_TXn_HIGHZ_DRVR_EN, 

drvr_en);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +2 -0
+               PLL_REG_W(dp_res->ln_tx0_base,
+                               REG_DP_PHY_TXn_TRANSCEIVER_BIAS_EN, 

bias_en);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +2 -0
+               PLL_REG_W(dp_res->ln_tx1_base,
+                               REG_DP_PHY_TXn_HIGHZ_DRVR_EN, 

drvr_en);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +2 -0
+               PLL_REG_W(dp_res->ln_tx1_base,
+                               REG_DP_PHY_TXn_TRANSCEIVER_BIAS_EN, 

bias_en);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +3 -0
+       }
+
+       PLL_REG_W(dp_res->ln_tx0_base, REG_DP_PHY_TXn_TX_POL_INV, 

0x0a);
+ PLL_REG_W(dp_res->ln_tx1_base, REG_DP_PHY_TXn_TX_POL_INV,
0x0a);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +12 -0
+       PLL_REG_W(dp_res->phy_base, REG_DP_PHY_CFG, 0x18);
+       udelay(2000);
+
+       PLL_REG_W(dp_res->phy_base, REG_DP_PHY_CFG, 0x19);
+
+       /* poll for PHY ready status */
+       if (!dp_10nm_phy_rdy_status(dp_res)) {
+               rc = -EINVAL;
+               goto lock_err;
+       }
+
+       PLL_REG_W(dp_res->ln_tx0_base, REG_DP_PHY_TXn_TX_DRV_LVL, 

0x38);
+ PLL_REG_W(dp_res->ln_tx1_base, REG_DP_PHY_TXn_TX_DRV_LVL,
0x38);
+ PLL_REG_W(dp_res->ln_tx0_base,
REG_DP_PHY_TXn_TX_EMP_POST1_LVL, 0x20);
+ PLL_REG_W(dp_res->ln_tx1_base,
REG_DP_PHY_TXn_TX_EMP_POST1_LVL, 0x20);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +37 -0
+       PLL_REG_W(dp_res->ln_tx0_base,
+                       REG_DP_PHY_TXn_RES_CODE_LANE_OFFSET_TX, 0x06);
+       PLL_REG_W(dp_res->ln_tx1_base,
+                       REG_DP_PHY_TXn_RES_CODE_LANE_OFFSET_TX, 0x06);
+       PLL_REG_W(dp_res->ln_tx0_base,
+                       REG_DP_PHY_TXn_RES_CODE_LANE_OFFSET_RX, 0x07);
+       PLL_REG_W(dp_res->ln_tx1_base,
+                       REG_DP_PHY_TXn_RES_CODE_LANE_OFFSET_RX, 0x07);
+
+lock_err:
+       return rc;
+}
+
+static int dp_pll_disable_10nm(struct clk_hw *hw)
+{
+       int rc = 0;
+       struct msm_dp_pll *pll = hw_clk_to_pll(hw);
+       struct dp_pll_10nm *dp_res = to_dp_pll_10nm(pll);
+
+       /* Assert DP PHY power down */
+       PLL_REG_W(dp_res->phy_base, REG_DP_PHY_PD_CTL, 0x2);
+
+       return rc;
+}
+
+
+int dp_vco_prepare_10nm(struct clk_hw *hw)
+{
+       int rc = 0;
+       struct msm_dp_pll *pll = hw_clk_to_pll(hw);
+       struct dp_pll_10nm *dp_res = to_dp_pll_10nm(pll);
+
+       DRM_DEBUG_DP("%s: rate = %ld\n", __func__, pll->rate);
+       if ((dp_res->vco_cached_rate != 0)
+               && (dp_res->vco_cached_rate == pll->rate)) {
+               rc = dp_vco_set_rate_10nm(hw,
+                       dp_res->vco_cached_rate, 

dp_res->vco_cached_rate);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +2 -0
+               if (rc) {
+                       DRM_ERROR("index=%d vco_set_rate failed. 

rc=%d\n",

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +89 -0
+                               rc, dp_res->index);
+                       goto error;
+               }
+       }
+
+       rc = dp_pll_enable_10nm(hw);
+       if (rc) {
+               DRM_ERROR("ndx=%d failed to enable dp pll\n",
+                                       dp_res->index);
+               goto error;
+       }
+
+       pll->pll_on = true;
+error:
+       return rc;
+}
+
+void dp_vco_unprepare_10nm(struct clk_hw *hw)
+{
+       struct msm_dp_pll *pll = hw_clk_to_pll(hw);
+       struct dp_pll_10nm *dp_res = to_dp_pll_10nm(pll);
+
+       if (!dp_res) {
+               DRM_ERROR("Invalid input parameter\n");
+               return;
+       }
+
+       if (!pll->pll_on) {
+               DRM_ERROR("pll resource can't be enabled\n");
+               return;
+       }
+       dp_res->vco_cached_rate = pll->rate;
+       dp_pll_disable_10nm(hw);
+
+       pll->pll_on = false;
+}
+
+int dp_vco_set_rate_10nm(struct clk_hw *hw, unsigned long rate,
+                                       unsigned long parent_rate)
+{
+       struct msm_dp_pll *pll = hw_clk_to_pll(hw);
+       int rc;
+
+       DRM_DEBUG_DP("DP lane CLK rate=%ld\n", rate);
+
+       rc = dp_config_vco_rate_10nm(pll, rate);
+       if (rc)
+               DRM_ERROR("%s: Failed to set clk rate\n", __func__);
+
+       pll->rate = rate;
+
+       return 0;
+}
+
+unsigned long dp_vco_recalc_rate_10nm(struct clk_hw *hw,
+                                       unsigned long parent_rate)
+{
+       struct msm_dp_pll *pll = hw_clk_to_pll(hw);
+       struct dp_pll_10nm *dp_res = to_dp_pll_10nm(pll);
+       u32 div, hsclk_div, link_clk_div = 0;
+       u64 vco_rate;
+
+       div = PLL_REG_R(dp_res->pll_base, QSERDES_COM_HSCLK_SEL);
+       div &= 0x0f;
+
+       if (div == 12)
+               hsclk_div = 6; /* Default */
+       else if (div == 4)
+               hsclk_div = 4;
+       else if (div == 0)
+               hsclk_div = 2;
+       else if (div == 3)
+               hsclk_div = 1;
+       else {
+               DRM_DEBUG_DP("unknown divider. forcing to default\n");
+               hsclk_div = 5;
+       }
+
+       div = PLL_REG_R(dp_res->phy_base, REG_DP_PHY_AUX_CFG2);
+       div >>= 2;
+
+       if ((div & 0x3) == 0)
+               link_clk_div = 5;
+       else if ((div & 0x3) == 1)
+               link_clk_div = 10;
+       else if ((div & 0x3) == 2)
+               link_clk_div = 20;
+       else
+               DRM_ERROR("%s: unsupported div. Phy_mode: %d\n", 

__func__, div);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +15 -0
+
+       if (link_clk_div == 20) {
+               vco_rate = DP_VCO_HSCLK_RATE_2700MHZDIV1000;
+       } else {
+               if (hsclk_div == 6)
+                       vco_rate = DP_VCO_HSCLK_RATE_1620MHZDIV1000;
+               else if (hsclk_div == 4)
+                       vco_rate = DP_VCO_HSCLK_RATE_2700MHZDIV1000;
+               else if (hsclk_div == 2)
+                       vco_rate = DP_VCO_HSCLK_RATE_5400MHZDIV1000;
+               else
+                       vco_rate = DP_VCO_HSCLK_RATE_8100MHZDIV1000;
+       }
+
+       DRM_DEBUG_DP("returning vco rate = %lu\n", (unsigned 

long)vco_rate);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +14 -0
+
+       dp_res->vco_cached_rate = pll->rate = vco_rate;
+       return (unsigned long)vco_rate;
+}
+
+long dp_vco_round_rate_10nm(struct clk_hw *hw, unsigned long rate,
+                       unsigned long *parent_rate)
+{
+       unsigned long rrate = rate;
+       struct msm_dp_pll *pll = hw_clk_to_pll(hw);
+
+       if (rate <= pll->min_rate)
+               rrate = pll->min_rate;
+       else if (rate <= DP_VCO_HSCLK_RATE_2700MHZDIV1000)
Quoted 8 lines

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c line 517

Quoted 5 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +5 -0
+               rrate = DP_VCO_HSCLK_RATE_2700MHZDIV1000;
+       else if (rate <= DP_VCO_HSCLK_RATE_5400MHZDIV1000)
+               rrate = DP_VCO_HSCLK_RATE_5400MHZDIV1000;
+       else
+               rrate = pll->max_rate;

A switch statement would be more readable. Not sure why pll->min_rate is needed though.

pll->min_rate is the minimum vco clock that is supported on the chipset.
We are trying to make sure that we dont go below the min vco
level for that chipset.

Quoted 15 lines
	switch (rate) {
	case 0 .. pll->min_rate:
		rrate =
		break;
	case pll->min_rate .. DP_VCO_HSCLK_RATE_2700MHZDIV1000:
		...
	}
Quoted 6 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +6 -0
+
+       DRM_DEBUG_DP("%s: rrate=%ld\n", __func__, rrate);
+
+       *parent_rate = rrate;
+       return rrate;
+}
#11 lore raw

Re: [DPU PATCH v4 4/5] drm/msm/dp: add support for DP PLL driver

Hi Stephen Boyd,

I have updated our comments inline for the queries.
Could you please check and let know your views.
I have addressed most of the comments from Rob/Jordon and planning to update new patchset in couple of days.

Thanks,
--Vara

Quoted 1,700 lines · On 2020-03-27 13:28, [email protected] wrote:
Quoted 423 lines · Stephen Boyd

Quoting Vara Reddy (2020-03-04 16:10:27)

Quoted 86 lines

From: Chandan Uddaraju <[email protected]>

Add the needed DP PLL specific files to support
display port interface on msm targets.

The DP driver calls the DP PLL driver registration. The DP driver sets the link and pixel clock sources.

Changes in v2:
-- Update copyright markings on all relevant files. -- Use DRM_DEBUG_DP for debug msgs.

Changes in v4:
-- Update the DP link clock provider names

---
 drivers/gpu/drm/msm/Kconfig                   |  13 +
 drivers/gpu/drm/msm/Makefile                  |   4 +
 drivers/gpu/drm/msm/dp/dp_display.c           |  51 +++
 drivers/gpu/drm/msm/dp/dp_display.h           |   3 +
 drivers/gpu/drm/msm/dp/dp_parser.h            |   4 +
 drivers/gpu/drm/msm/dp/dp_power.h             |   1 -
 drivers/gpu/drm/msm/dp/pll/dp_pll.c           | 136 +++++++
 drivers/gpu/drm/msm/dp/pll/dp_pll.h           |  57 +++
 drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c      | 406 
++++++++++++++++++++
 drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.h      |  86 +++++
 drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c | 524 
++++++++++++++++++++++++++
 11 files changed, 1284 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/msm/dp/pll/dp_pll.c
 create mode 100644 drivers/gpu/drm/msm/dp/pll/dp_pll.h
 create mode 100644 drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c
 create mode 100644 drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.h
 create mode 100644 drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c
drivers/gpu/drm/msm/Kconfig +0 -0
index 7946cb1..e73ad23 100644
@@ -66,6 +66,19 @@ config DRM_MSM_DP
          display support is enabled through this config option. It 

can
be primary or secondary display on device.

drivers/gpu/drm/msm/Kconfig +5 -0
+config DRM_MSM_DP_PLL
+       bool "Enable DP PLL driver in MSM DRM"
+       depends on DRM_MSM_DP && COMMON_CLK
+       help
+         Choose this option to enable DP PLL driver which provides 

DP

drivers/gpu/drm/msm/Kconfig +7 -0
+         source clocks under common clock framework.
+
+config DRM_MSM_DP_10NM_PLL
+       bool "Enable DP 10nm PLL driver in MSM DRM (used by SDM845)"
+       depends on DRM_MSM_DP_PLL
+       help
+         Choose this option if DP PLL on SDM845 is used on the 

platform.

drivers/gpu/drm/msm/Kconfig +1 -0
+
 config DRM_MSM_DSI
        bool "Enable DSI support in MSM DRM driver"
        depends on DRM_MSM
drivers/gpu/drm/msm/Makefile +0 -0
index c846599..1621f3f 100644
@@ -140,4 +140,8 @@ msm-$(CONFIG_DRM_MSM_DSI_14NM_PHY) += 

dsi/pll/dsi_pll_14nm.o
msm-$(CONFIG_DRM_MSM_DSI_10NM_PHY) += dsi/pll/dsi_pll_10nm.o endif

drivers/gpu/drm/msm/Makefile +4 -0
+msm-$(CONFIG_DRM_MSM_DP_PLL)+= dp/pll/dp_pll.o
+msm-$(CONFIG_DRM_MSM_DP_10NM_PLL)+= dp/pll/dp_pll_10nm.o \
+       dp/pll/dp_pll_10nm_util.o
+
 obj-$(CONFIG_DRM_MSM)  += msm.o
drivers/gpu/drm/msm/dp/dp_display.c +0 -0
index 5525405..ded8f5c 100644
@@ -61,6 +61,51 @@ struct dp_display_private {
        {}
 };

drivers/gpu/drm/msm/dp/dp_display.c struct dp_display_private { · line 72

Quoted 10 lines
drivers/gpu/drm/msm/dp/dp_display.c +9 -0
 
+static int dp_get_pll(struct dp_display_private *dp_priv)
+{
+       struct platform_device *pdev = NULL;
+       struct platform_device *pll_pdev;
+       struct device_node *pll_node;
+       struct dp_parser *dp_parser = NULL;
+
+       if (!dp_priv) {
+               DRM_ERROR("Invalid Arguments\n");

It's a static function. Is this even possible? The bad code would be in the same file.

Quoted 110 lines
drivers/gpu/drm/msm/dp/dp_display.c +27 -0
+               return -EINVAL;
+       }
+
+       pdev = dp_priv->pdev;
+       dp_parser = dp_priv->parser;
+
+       if (!dp_parser) {
+               DRM_DEV_ERROR(&pdev->dev, "%s: Parser not 
 initialized\n",
+                               __func__);
+               return -EINVAL;
+       }
+
+       pll_node = of_parse_phandle(pdev->dev.of_node, "pll-node", 
 0);
+       if (!pll_node) {
+               DRM_DEV_ERROR(&pdev->dev, "%s: cannot find pll 
 device\n",
+                               __func__);
+               return -ENXIO;
+       }
+
+       pll_pdev = of_find_device_by_node(pll_node);
+       if (pll_pdev)
+               dp_parser->pll = platform_get_drvdata(pll_pdev);
+
+       of_node_put(pll_node);
+
+       if (!pll_pdev || !dp_parser->pll) {
+               DRM_DEV_ERROR(&pdev->dev, "%s: pll driver is not 

ready\n",

drivers/gpu/drm/msm/dp/dp_display.c +13 -0
+                               __func__);
+               return -EPROBE_DEFER;
+       }
+
+       dp_parser->pll_dev = get_device(&pll_pdev->dev);
+
+       return 0;
+}
+
 static irqreturn_t dp_display_irq(int irq, void *dev_id)
 {
        struct dp_display_private *dp = dev_id;
@@ -114,6 +159,10 @@ static int dp_display_bind(struct device *dev, 
 struct device *master,
                goto end;
        }
 
+       rc = dp_get_pll(dp);
+       if (rc)
+               goto end;
+
        rc = dp_aux_register(dp->aux);
        if (rc) {
                DRM_ERROR("DRM DP AUX register failed\n");
drivers/gpu/drm/msm/dp/dp_display.c +2 -0
@@ -837,6 +886,7 @@ int __init msm_dp_register(void)
 {
        int ret;
 
+       msm_dp_pll_driver_register();
        ret = platform_driver_register(&dp_display_driver);
        if (ret) {
                DRM_ERROR("driver register failed");
@@ -848,6 +898,7 @@ int __init msm_dp_register(void)
 
 void __exit msm_dp_unregister(void)
 {
+       msm_dp_pll_driver_unregister();
        platform_driver_unregister(&dp_display_driver);
 }
 
drivers/gpu/drm/msm/dp/dp_display.h +3 -0
index dad8610..4c53ed5 100644
@@ -25,4 +25,7 @@ int dp_display_get_modes(struct msm_dp *dp_display,
 bool dp_display_check_video_test(struct msm_dp *dp_display);
 int dp_display_get_test_bpp(struct msm_dp *dp_display);
 
+void __init msm_dp_pll_driver_register(void);
+void __exit msm_dp_pll_driver_unregister(void);
+
 #endif /* _DP_DISPLAY_H_ */
drivers/gpu/drm/msm/dp/dp_parser.h +4 -0
index 1dc592f..e775cbc 100644
@@ -6,6 +6,8 @@
 #ifndef _DP_PARSER_H_
 #define _DP_PARSER_H_
 
+#include "pll/dp_pll.h"
+
 #define DP_LABEL "MDSS DP DISPLAY"
 #define AUX_CFG_LEN    10
 #define DP_MAX_PIXEL_CLK_KHZ   675000
@@ -193,6 +195,8 @@ struct dp_parser {
        bool combo_phy_en;
        struct dp_io io;
        struct dp_display_data disp_data;
+       struct msm_dp_pll *pll;
+       struct device *pll_dev;
        const struct dp_regulator_cfg *regulator_cfg;
        u8 l_map[4];
        struct dp_aux_cfg aux_cfg[AUX_CFG_LEN];
drivers/gpu/drm/msm/dp/dp_power.h +0 -0
index 52b6194..b24b2c5 100644

drivers/gpu/drm/msm/dp/dp_power.h line 19

Quoted 7 lines
drivers/gpu/drm/msm/dp/dp_power.h +0 -1
@@ -14,7 +14,6 @@
  * @init: initializes the regulators/core clocks/GPIOs/pinctrl
  * @deinit: turns off the regulators/core clocks/GPIOs/pinctrl
  * @clk_enable: enable/disable the DP clocks
- * @set_link_clk_parent: set the parent of DP link clock
  * @set_pixel_clk_parent: set the parent of DP pixel clock
  */
 struct dp_power {

This can be another patch to update documentation?

Quoted 28 lines
drivers/gpu/drm/msm/dp/pll/dp_pll.c new file +3 -0
new file mode 100644
index 0000000..11e26cf
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights 

reserved.
+ */

drivers/gpu/drm/msm/dp/pll/dp_pll.c new file +13 -0
+
+#include "dp_pll.h"
+
+int msm_dp_pll_util_parse_dt_clock(struct platform_device *pdev,
+                                       struct msm_dp_pll *pll)
+{
+       u32 i = 0, rc = 0;
+       struct dss_module_power *mp = &pll->mp;
+       const char *clock_name;
+       u32 clock_rate;
+
+       mp->num_clk = of_property_count_strings(pdev->dev.of_node,
+                                                       

"clock-names");

drivers/gpu/drm/msm/dp/pll/dp_pll.c new file +2 -0
+       if (mp->num_clk <= 0) {
+               DRM_DEV_ERROR(&pdev->dev, "%s:clocks are not 

defined\n",

drivers/gpu/drm/msm/dp/pll/dp_pll.c line 24

Quoted 5 lines
drivers/gpu/drm/msm/dp/pll/dp_pll.c new file +5 -0
+                               __func__);
+               goto clk_err;
+       }
+
+       mp->clk_config = devm_kzalloc(&pdev->dev,

Use devm_kcalloc() for arrays.

Quoted 52 lines

+ sizeof(struct dss_clk) * mp->num_clk,
GFP_KERNEL);

drivers/gpu/drm/msm/dp/pll/dp_pll.c new file +8 -0
+       if (!mp->clk_config) {
+               rc = -ENOMEM;
+               mp->num_clk = 0;
+               goto clk_err;
+       }
+
+       for (i = 0; i < mp->num_clk; i++) {
+               of_property_read_string_index(pdev->dev.of_node, 

"clock-names",
+ i,
&clock_name);

drivers/gpu/drm/msm/dp/pll/dp_pll.c new file +4 -0
+               strlcpy(mp->clk_config[i].clk_name, clock_name,
+                               sizeof(mp->clk_config[i].clk_name));
+
+               of_property_read_u32_index(pdev->dev.of_node, 

"clock-rate",
+ i,
&clock_rate);

drivers/gpu/drm/msm/dp/pll/dp_pll.c new file +29 -0
+               mp->clk_config[i].rate = clock_rate;
+
+               if (!clock_rate)
+                       mp->clk_config[i].type = DSS_CLK_AHB;
+               else
+                       mp->clk_config[i].type = DSS_CLK_PCLK;
+       }
+
+clk_err:
+       return rc;
+}
+
+struct msm_dp_pll *msm_dp_pll_init(struct platform_device *pdev,
+                       enum msm_dp_pll_type type, int id)
+{
+       struct device *dev = &pdev->dev;
+       struct msm_dp_pll *pll;
+
+       switch (type) {
+       case MSM_DP_PLL_10NM:
+               pll = msm_dp_pll_10nm_init(pdev, id);
+               break;
+       default:
+               pll = ERR_PTR(-ENXIO);
+               break;
+       }
+
+       if (IS_ERR(pll)) {
+               DRM_DEV_ERROR(dev, "%s: failed to init DP PLL\n", 

__func__);

drivers/gpu/drm/msm/dp/pll/dp_pll.c new file +2 -0
+               return pll;
+       }

drivers/gpu/drm/msm/dp/pll/dp_pll.c line 80

Quoted 10 lines
drivers/gpu/drm/msm/dp/pll/dp_pll.c new file +10 -0
+
+       pll->type = type;
+
+       DRM_DEBUG_DP("DP:%d PLL registered", id);
+
+       return pll;
+}
+
+static const struct of_device_id dp_pll_dt_match[] = {
+       { .compatible = "qcom,dp-pll-10nm" },

Add a terminator here { }.

drivers/gpu/drm/msm/dp/pll/dp_pll.c line 93

Quoted 13 lines
drivers/gpu/drm/msm/dp/pll/dp_pll.c new file +13 -0
+};
+
+static int dp_pll_driver_probe(struct platform_device *pdev)
+{
+       struct msm_dp_pll *pll;
+       struct device *dev = &pdev->dev;
+       const struct of_device_id *match;
+
+       match = of_match_node(dp_pll_dt_match, dev->of_node);
+       if (!match)
+               return -ENODEV;
+
+       /* Currently supporting only 10nm-DP PLL */

Please remove this useless comment that can only require being updated in the future.

Quoted 5 lines
drivers/gpu/drm/msm/dp/pll/dp_pll.c new file +4 -0
+       pll = msm_dp_pll_init(pdev, MSM_DP_PLL_10NM, 0);
+       if (IS_ERR_OR_NULL(pll)) {
+               DRM_DEV_ERROR(dev,
+                       "%s: pll init failed: %ld, need separate pll 

clk driver\n",

What does this error mean?

Quoted 50 lines
drivers/gpu/drm/msm/dp/pll/dp_pll.c new file +39 -0
+                       __func__, PTR_ERR(pll));
+               return -ENODEV;
+       }
+
+       platform_set_drvdata(pdev, pll);
+
+       return 0;
+}
+
+static int dp_pll_driver_remove(struct platform_device *pdev)
+{
+       struct msm_dp_pll *pll = platform_get_drvdata(pdev);
+
+       if (pll)
+               pll = NULL;
+
+       platform_set_drvdata(pdev, NULL);
+
+       return 0;
+}
+
+static struct platform_driver dp_pll_platform_driver = {
+       .probe      = dp_pll_driver_probe,
+       .remove     = dp_pll_driver_remove,
+       .driver     = {
+               .name   = "msm_dp_pll",
+               .of_match_table = dp_pll_dt_match,
+       },
+};
+
+void __init msm_dp_pll_driver_register(void)
+{
+       platform_driver_register(&dp_pll_platform_driver);
+}
+
+void __exit msm_dp_pll_driver_unregister(void)
+{
+       platform_driver_unregister(&dp_pll_platform_driver);
+}
drivers/gpu/drm/msm/dp/pll/dp_pll.h new file +3 -0
new file mode 100644
index 0000000..185f1e9
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights 

reserved.
+ */

drivers/gpu/drm/msm/dp/pll/dp_pll.h line 10

Quoted 6 lines
drivers/gpu/drm/msm/dp/pll/dp_pll.h new file +6 -0
+
+#ifndef __DP_PLL_H
+#define __DP_PLL_H
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>

You're making a clk provider. Please Cc clk maintainers and [email protected]

Quoted 16 lines
drivers/gpu/drm/msm/dp/pll/dp_pll.h new file +16 -0
+#include <linux/platform_device.h>
+
+#include "dpu_io_util.h"
+#include "msm_drv.h"
+
+#define PLL_REG_W(base, offset, data)  \
+                               writel((data), (base) + (offset))
+#define PLL_REG_R(base, offset)        readl((base) + (offset))
+
+enum msm_dp_pll_type {
+       MSM_DP_PLL_10NM,
+       MSM_DP_PLL_MAX
+};
+
+struct msm_dp_pll {
+       enum msm_dp_pll_type type;
Quoted 8 lines

drivers/gpu/drm/msm/dp/pll/dp_pll.h line 31

Quoted 5 lines
drivers/gpu/drm/msm/dp/pll/dp_pll.h new file +5 -0
+       struct clk_hw clk_hw;
+       unsigned long   rate;           /* current vco rate */
+       u64             min_rate;       /* min vco rate */
+       u64             max_rate;       /* max vco rate */
+       bool            pll_on;

Are these struct members here to save/restore the rate across suspend/resume?

We are trying to retain the current vco rate across suspend/resume. min_rate and max_rate are the minimum and maximum vco rates that are possible for the chipset.

Quoted 172 lines

drivers/gpu/drm/msm/dp/pll/dp_pll.h line 40

Quoted 11 lines
drivers/gpu/drm/msm/dp/pll/dp_pll.h new file +2 -0
+       void            *priv;
+       /* Pll specific resources like GPIO, power supply, clocks, 

etc*/

drivers/gpu/drm/msm/dp/pll/dp_pll.h new file +7 -0
+       struct dss_module_power mp;
+       int (*get_provider)(struct msm_dp_pll *pll,
+                       struct clk **link_clk_provider,
+                       struct clk **pixel_clk_provider);
+};
+
+#define hw_clk_to_pll(x) container_of(x, struct msm_dp_pll, clk_hw)

Why not to_msm_dp_pll() for this macro.

Quoted 13 lines
drivers/gpu/drm/msm/dp/pll/dp_pll.h new file +9 -0
+
+struct msm_dp_pll *msm_dp_pll_init(struct platform_device *pdev,
+                               enum msm_dp_pll_type type, int id);
+
+int msm_dp_pll_util_parse_dt_clock(struct platform_device *pdev,
+                               struct msm_dp_pll *pll);
+
+#ifdef CONFIG_DRM_MSM_DP_10NM_PLL
+struct msm_dp_pll *msm_dp_pll_10nm_init(struct platform_device 

*pdev, int id);

drivers/gpu/drm/msm/dp/pll/dp_pll.h new file +3 -0
+#else
+static inline struct msm_dp_pll *msm_dp_pll_10nm_init
+                               (struct platform_device *pdev, int 
Quoted 1 line

id)

I would write this as

static inline struct msm_dp_pll * msm_dp_pll_10nm_init(struct platform_device *pdev, int id)

so that we don't have parameters all by themselves.

Quoted 56 lines
drivers/gpu/drm/msm/dp/pll/dp_pll.h new file +5 -0
+{
+       return ERR_PTR(-ENODEV);
+}
+#endif
+#endif /* __DP_PLL_H */
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +3 -0
new file mode 100644
index 0000000..5991e09
@@ -0,0 +1,406 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights 

reserved.
+ */

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +40 -0
+
+/*
+ * Display Port PLL driver block diagram for branch clocks
+ *
+ *              +------------------------------+
+ *              |         DP_VCO_CLK           |
+ *              |                              |
+ *              |    +-------------------+     |
+ *              |    |   (DP PLL/VCO)    |     |
+ *              |    +---------+---------+     |
+ *              |              v               |
+ *              |   +----------+-----------+   |
+ *              |   | hsclk_divsel_clk_src |   |
+ *              |   +----------+-----------+   |
+ *              +------------------------------+
+ *                              |
+ *          +---------<---------v------------>----------+
+ *          |                                           |
+ * +--------v---------+                                 |
+ * |    dp_phy_pll    |                                 |
+ * |     link_clk     |                                 |
+ * +--------+---------+                                 |
+ *          |                                           |
+ *          |                                           |
+ *          v                                           v
+ * Input to DISPCC block                                |
+ * for link clk, crypto clk                             |
+ * and interface clock                                  |
+ *                                                      |
+ *                                                      |
+ *      +--------<------------+-----------------+---<---+
+ *      |                     |                 |
+ * +----v---------+  +--------v-----+  +--------v------+
+ * | vco_divided  |  | vco_divided  |  | vco_divided   |
+ * |    _clk_src  |  |    _clk_src  |  |    _clk_src   |
+ * |              |  |              |  |               |
+ * |divsel_six    |  |  divsel_two  |  |  divsel_four  |
+ * +-------+------+  +-----+--------+  +--------+------+
+ *         |                 |                  |
+ *         v---->----------v-------------<------v

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 54

Quoted 10 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +10 -0
+ *                         |
+ *              +----------+---------+
+ *              |   dp_phy_pll_vco   |
+ *              |       div_clk      |
+ *              +---------+----------+
+ *                        |
+ *                        v
+ *              Input to DISPCC block
+ *              for DP pixel clock
+ *

Awesome diagram!

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c

Quoted 3 lines

+ */

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +2 -0
+
+#include <linux/clk.h>

Please try to avoid making a clk provider a clk consumer at the same time.

Quoted 41 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +38 -0
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/regmap.h>
+
+#include "dp_pll_10nm.h"
+
+#define NUM_PROVIDED_CLKS              2
+
+#define DP_LINK_CLK_SRC                        0
+#define DP_PIXEL_CLK_SRC               1
+
+static struct dp_pll_10nm *dp_pdb;
+
+static const struct clk_ops dp_10nm_vco_clk_ops = {
+       .recalc_rate = dp_vco_recalc_rate_10nm,
+       .set_rate = dp_vco_set_rate_10nm,
+       .round_rate = dp_vco_round_rate_10nm,
+       .prepare = dp_vco_prepare_10nm,
+       .unprepare = dp_vco_unprepare_10nm,
+};
+
+struct dp_pll_10nm_pclksel {
+       struct clk_hw hw;
+
+       /* divider params */
+       u8 shift;
+       u8 width;
+       u8 flags; /* same flags as used by clk_divider struct */
+
+       struct dp_pll_10nm *pll;
+};
+#define to_pll_10nm_pclksel(_hw) \
+       container_of(_hw, struct dp_pll_10nm_pclksel, hw)
+
+static int dp_mux_set_parent_10nm(struct clk_hw *hw, u8 val)
+{
+       struct dp_pll_10nm_pclksel *pclksel = 

to_pll_10nm_pclksel(hw);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +2 -0
+       struct dp_pll_10nm *dp_res = pclksel->pll;
+       u32 auxclk_div;

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 107

Quoted 10 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +10 -0
+
+       auxclk_div = PLL_REG_R(dp_res->phy_base, REG_DP_PHY_VCO_DIV);
+       auxclk_div &= ~0x03;    /* bits 0 to 1 */
+
+       if (val == 0) /* mux parent index = 0 */
+               auxclk_div |= 1;
+       else if (val == 1) /* mux parent index = 1 */
+               auxclk_div |= 2;
+       else if (val == 2) /* mux parent index = 2 */
+               auxclk_div |= 0;

These comments are just saying what the code says. Can you remove them?

Quoted 5 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +4 -0
+
+       PLL_REG_W(dp_res->phy_base,
+                       REG_DP_PHY_VCO_DIV, auxclk_div);
+       DRM_DEBUG_DP("%s: mux=%d auxclk_div=%x\n", __func__, val, 

auxclk_div);

Quoted 6 lines

Is it a mux and divider combined? The diagram seems to imply that the mux picks between fixed dividers but I don't see why that isn't just a divider clk because the parent is always the same for the fixed dividers. Put another way, why is a parent API being used for this? It would make more sense to just treat this like a divider and use rate setting APIs instead.

I am not sure if I completely understand the concern. I am thinking that you are suggesting that we remove the mux altogether and just use the fixed dividers, while programming the registers as part of the the set_rate for those dividers.
If that is the case, then we would have to list 3 parents for the RCG clock in display clock controller (dispcc). However, as per the clock plan, the RCG clocks have a well defined set of parents (DP PLL, or the reference clock). It would be incorrect
to list all these PLL specific clocks as parents of the RCG. We are required to list a a single provider clock for DP Pixel clock. Hence the choice of a mux clock.

Quoted 73 lines
Quoted 53 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +8 -0
+
+       return 0;
+}
+
+static u8 dp_mux_get_parent_10nm(struct clk_hw *hw)
+{
+       u32 auxclk_div = 0;
+       struct dp_pll_10nm_pclksel *pclksel = 

to_pll_10nm_pclksel(hw);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +14 -0
+       struct dp_pll_10nm *dp_res = pclksel->pll;
+       u8 val = 0;
+
+       auxclk_div = PLL_REG_R(dp_res->phy_base, REG_DP_PHY_VCO_DIV);
+       auxclk_div &= 0x03;
+
+       if (auxclk_div == 1) /* Default divider */
+               val = 0;
+       else if (auxclk_div == 2)
+               val = 1;
+       else if (auxclk_div == 0)
+               val = 2;
+
+       DRM_DEBUG_DP("%s: auxclk_div=%d, val=%d\n", __func__, 

auxclk_div, val);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +21 -0
+
+       return val;
+}
+
+static int clk_mux_determine_rate(struct clk_hw *hw,
+                                    struct clk_rate_request *req)
+{
+       unsigned long rate = 0;
+       int ret = 0;
+
+       rate = clk_get_rate(hw->clk);
+
+       if (rate <= 0) {
+               DRM_ERROR("Rate is not set properly\n");
+               return -EINVAL;
+       }
+
+       req->rate = rate;
+
+       DRM_DEBUG_DP("%s: rate=%ld\n", __func__, req->rate);
+       /* Set the new parent of mux if there is a new valid parent 

*/

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +2 -0
+       if (hw->clk && req->best_parent_hw->clk) {
+               ret = clk_set_parent(hw->clk, 

req->best_parent_hw->clk);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +2 -0
+               if (ret) {
+                       DRM_ERROR("%s: clk_set_parent failed: 

ret=%d\n",

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 166

Quoted 8 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +8 -0
+                                       __func__, ret);
+                       return ret;
+               }
+       }
+       return 0;
+}
+
+static unsigned long mux_recalc_rate(struct clk_hw *hw,

Please prefix all functions with dp_pll_

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 172

Quoted 6 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +6 -0
+                                       unsigned long parent_rate)
+{
+       struct clk *div_clk = NULL, *vco_clk = NULL;
+       struct msm_dp_pll *vco = NULL;
+
+       div_clk = clk_get_parent(hw->clk);

We have clk_hw_get_parent() for this stuff. Please use it.

Quoted 10 lines

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 180

Quoted 8 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +8 -0
+       if (!div_clk)
+               return 0;
+
+       vco_clk = clk_get_parent(div_clk);
+       if (!vco_clk)
+               return 0;
+
+       vco = hw_clk_to_pll(__clk_get_hw(vco_clk));

Please don't use __clk_get_hw().

Could you please elaborate on this or what to use. Should we use the api implementation directly.

Quoted 23 lines

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 189

Quoted 10 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +9 -0
+       if (!vco)
+               return 0;
+
+       if (vco->rate == DP_VCO_HSCLK_RATE_8100MHZDIV1000)
+               return (vco->rate / 6);
+       else if (vco->rate == DP_VCO_HSCLK_RATE_5400MHZDIV1000)
+               return (vco->rate / 4);
+       else
+               return (vco->rate / 2);

else return is shunned. Just have return instead.

Quoted 10 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +10 -0
+}
+
+static int dp_pll_10nm_get_provider(struct msm_dp_pll *pll,
+                                    struct clk **link_clk_provider,
+                                    struct clk **pixel_clk_provider)
+{
+       struct dp_pll_10nm *pll_10nm = to_dp_pll_10nm(pll);
+       struct clk_hw_onecell_data *hw_data = pll_10nm->hw_data;
+
+       if (link_clk_provider)
Quoted 8 lines
Quoted 5 lines

+ *link_clk_provider =
hw_data->hws[DP_LINK_CLK_SRC]->clk;

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +2 -0
+       if (pixel_clk_provider)
+               *pixel_clk_provider = 

hw_data->hws[DP_PIXEL_CLK_SRC]->clk;

I don't know what this is but it looks weird. Is this a clk provider on top of another clk provider?

Here we are setting the parent of RCG.
RCG needs called by the consumer to set the parent. DP is a consumer setting a rate on provider.

Quoted 25 lines
Quoted 14 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +12 -0
+
+       return 0;
+}
+
+static const struct clk_ops dp_10nm_pclksel_clk_ops = {
+       .get_parent = dp_mux_get_parent_10nm,
+       .set_parent = dp_mux_set_parent_10nm,
+       .recalc_rate = mux_recalc_rate,
+       .determine_rate = clk_mux_determine_rate,
+};
+
+static struct clk_hw *dp_pll_10nm_pixel_clk_sel(struct dp_pll_10nm 

*pll_10nm)

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 222

Quoted 8 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +8 -0
+{
+       struct device *dev = &pll_10nm->pdev->dev;
+       struct dp_pll_10nm_pclksel *pll_pclksel;
+       struct clk_init_data pclksel_init = {
+               .parent_names = (const char *[]){
+                               "dp_vco_divsel_two_clk_src",
+                               "dp_vco_divsel_four_clk_src",
+                               "dp_vco_divsel_six_clk_src" },

It looks like a clk divider still, not a mux.

Quoted 6 lines

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 225

Quoted 3 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +3 -0
+               .num_parents = 3,
+               .name = "dp_phy_pll_vco_div_clk",
+               .flags = CLK_IGNORE_UNUSED,

Why? Please comment why this is needed in the code. But more importantly, please just don't use this flag.

We are working to remove this flag CLK_IGNORE_UNUSED.

Quoted 201 lines
Quoted 18 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +5 -0
+               .ops = &dp_10nm_pclksel_clk_ops,
+       };
+       int ret;
+
+       pll_pclksel = devm_kzalloc(dev, sizeof(*pll_pclksel), 

GFP_KERNEL);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +11 -0
+       if (!pll_pclksel)
+               return ERR_PTR(-ENOMEM);
+
+       pll_pclksel->pll = pll_10nm;
+       pll_pclksel->shift = 0;
+       pll_pclksel->width = 4;
+       pll_pclksel->flags = CLK_DIVIDER_ONE_BASED;
+       pll_pclksel->hw.init = &pclksel_init;
+
+       ret = clk_hw_register(dev, &pll_pclksel->hw);
+       if (ret)

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 251

Quoted 10 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +10 -0
+               return ERR_PTR(ret);
+
+       return &pll_pclksel->hw;
+}
+
+static int dp_pll_10nm_register(struct dp_pll_10nm *pll_10nm)
+{
+       char clk_name[32], parent[32], vco_name[32];
+       struct clk_init_data vco_init = {
+               .parent_names = (const char *[]){ "bi_tcxo" },

Please use the new way of specifying clk parents through .fw_name and DT indices.

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 254

Quoted 3 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +3 -0
+               .num_parents = 1,
+               .name = vco_name,
+               .flags = CLK_IGNORE_UNUSED,

Ugh there's that flag again.

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 266

Quoted 12 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +12 -0
+               .ops = &dp_10nm_vco_clk_ops,
+       };
+       struct device *dev = &pll_10nm->pdev->dev;
+       struct clk_hw **hws = pll_10nm->hws;
+       struct clk_hw_onecell_data *hw_data;
+       struct clk_hw *hw;
+       int num = 0;
+       int ret;
+
+       DRM_DEBUG_DP("DP->id = %d", pll_10nm->id);
+
+       hw_data = devm_kzalloc(dev, sizeof(*hw_data) +

Use devm_kcalloc()

Quoted 5 lines

+ NUM_PROVIDED_CLKS * sizeof(struct
clk_hw *),

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +3 -0
+                              GFP_KERNEL);
+       if (!hw_data)
+               return -ENOMEM;

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 280

Quoted 10 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +10 -0
+
+       snprintf(vco_name, 32, "dp_vco_clk");
+       pll_10nm->base.clk_hw.init = &vco_init;
+       ret = clk_hw_register(dev, &pll_10nm->base.clk_hw);
+       if (ret)
+               return ret;
+       hws[num++] = &pll_10nm->base.clk_hw;
+
+       snprintf(clk_name, 32, "dp_phy_pll_link_clk");
+       snprintf(parent, 32, "dp_vco_clk");

Is there a need to make these names with snprintf?

Quoted 130 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +2 -0
+       hw = clk_hw_register_fixed_factor(dev, clk_name, parent,
+                                         CLK_SET_RATE_PARENT, 1, 

10);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +42 -0
+       if (IS_ERR(hw))
+               return PTR_ERR(hw);
+       hws[num++] = hw;
+       hw_data->hws[DP_LINK_CLK_SRC] = hw;
+
+       snprintf(clk_name, 32, "dp_vco_divsel_two_clk_src");
+       snprintf(parent, 32, "dp_vco_clk");
+       hw = clk_hw_register_fixed_factor(dev, clk_name, parent,
+                                         0, 1, 2);
+       if (IS_ERR(hw))
+               return PTR_ERR(hw);
+       hws[num++] = hw;
+
+       snprintf(clk_name, 32, "dp_vco_divsel_four_clk_src");
+       snprintf(parent, 32, "dp_vco_clk");
+       hw = clk_hw_register_fixed_factor(dev, clk_name, parent,
+                                         0, 1, 4);
+       if (IS_ERR(hw))
+               return PTR_ERR(hw);
+       hws[num++] = hw;
+
+       snprintf(clk_name, 32, "dp_vco_divsel_six_clk_src");
+       snprintf(parent, 32, "dp_vco_clk");
+       hw = clk_hw_register_fixed_factor(dev, clk_name, parent,
+                                         0, 1, 6);
+       if (IS_ERR(hw))
+               return PTR_ERR(hw);
+       hws[num++] = hw;
+
+       hw = dp_pll_10nm_pixel_clk_sel(pll_10nm);
+       if (IS_ERR(hw))
+               return PTR_ERR(hw);
+
+       hws[num++] = hw;
+       hw_data->hws[DP_PIXEL_CLK_SRC] = hw;
+
+       pll_10nm->num_hws = num;
+
+       hw_data->num = NUM_PROVIDED_CLKS;
+       pll_10nm->hw_data = hw_data;
+
+       ret = of_clk_add_hw_provider(dev->of_node, 

of_clk_hw_onecell_get,

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +3 -0
+                                    pll_10nm->hw_data);
+       if (ret) {
+               DRM_DEV_ERROR(dev, "failed to register clk provider: 

%d\n",

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +8 -0
+                               ret);
+               return ret;
+       }
+
+       return ret;
+}
+
+struct msm_dp_pll *msm_dp_pll_10nm_init(struct platform_device 

*pdev, int id)

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +10 -0
+{
+       struct dp_pll_10nm *dp_10nm_pll;
+       struct msm_dp_pll *pll;
+       int ret;
+
+       if (!pdev)
+               return ERR_PTR(-ENODEV);
+
+       dp_10nm_pll = devm_kzalloc(&pdev->dev,
+                                       sizeof(*dp_10nm_pll), 

GFP_KERNEL);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +10 -0
+       if (!dp_10nm_pll)
+               return ERR_PTR(-ENOMEM);
+
+       DRM_DEBUG_DP("DP PLL%d", id);
+
+       dp_10nm_pll->pdev = pdev;
+       dp_10nm_pll->id = id;
+       dp_pdb = dp_10nm_pll;
+
+       dp_10nm_pll->pll_base = msm_ioremap(pdev, "pll_base", 

"DP_PLL");

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +2 -0
+       if (IS_ERR_OR_NULL(dp_10nm_pll->pll_base)) {
+               DRM_DEV_ERROR(&pdev->dev, "failed to map CMN PLL 

base\n");

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +4 -0
+               return ERR_PTR(-ENOMEM);
+       }
+
+       dp_10nm_pll->phy_base = msm_ioremap(pdev, "phy_base", 

"DP_PHY");

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +2 -0
+       if (IS_ERR_OR_NULL(dp_10nm_pll->phy_base)) {
+               DRM_DEV_ERROR(&pdev->dev, "failed to map CMN PHY 

base\n");

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +7 -0
+               return ERR_PTR(-ENOMEM);
+       }
+
+       dp_10nm_pll->ln_tx0_base = msm_ioremap(pdev, "ln_tx0_base",
+                                               "DP_LN_TX0");
+       if (IS_ERR_OR_NULL(dp_10nm_pll->ln_tx0_base)) {
+               DRM_DEV_ERROR(&pdev->dev, "failed to map CMN LN_TX0 

base\n");

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +7 -0
+               return ERR_PTR(-ENOMEM);
+       }
+
+       dp_10nm_pll->ln_tx1_base = msm_ioremap(pdev, "ln_tx1_base",
+                                               "DP_LN_TX1");
+       if (IS_ERR_OR_NULL(dp_10nm_pll->ln_tx1_base)) {
+               DRM_DEV_ERROR(&pdev->dev, "failed to map CMN LN_TX1 

base\n");

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +7 -0
+               return ERR_PTR(-ENOMEM);
+       }
+
+       ret = of_property_read_u32(pdev->dev.of_node, "cell-index",
+                               &dp_10nm_pll->index);
+       if (ret) {
+               DRM_ERROR("Unable to get the cell-index ret=%d\n", 

ret);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +4 -0
+               dp_10nm_pll->index = 0;
+       }
+
+       ret = msm_dp_pll_util_parse_dt_clock(pdev, 

&dp_10nm_pll->base);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +8 -0
+       if (ret) {
+               DRM_ERROR("Unable to parse dt clocks ret=%d\n", ret);
+               return ERR_PTR(ret);
+       }
+
+       ret = dp_pll_10nm_register(dp_10nm_pll);
+       if (ret) {
+               DRM_DEV_ERROR(&pdev->dev, "failed to register PLL: 

%d\n", ret);

Quoted 8 lines

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c line 402

Quoted 6 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +6 -0
+               return ERR_PTR(ret);
+       }
+
+       pll = &dp_10nm_pll->base;
+       pll->min_rate = DP_VCO_HSCLK_RATE_1620MHZDIV1000;
+       pll->max_rate = DP_VCO_HSCLK_RATE_8100MHZDIV1000;

Maybe min/max can be set with clk_hw_set_rate_range()?

Here pll->min_rate and pll->max_rate stores the VCO range in local structure.

Quoted 139 lines
Quoted 90 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c new file +4 -0
+       pll->get_provider = dp_pll_10nm_get_provider;
+
+       return pll;
+}
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.h new file +3 -0
new file mode 100644
index 0000000..8466463
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights 

reserved.
+ */

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.h new file +61 -0
+
+#ifndef __DP_PLL_10NM_H
+#define __DP_PLL_10NM_H
+
+#include "dp_pll.h"
+#include "dp_reg.h"
+
+#define DP_VCO_HSCLK_RATE_1620MHZDIV1000       1620000UL
+#define DP_VCO_HSCLK_RATE_2700MHZDIV1000       2700000UL
+#define DP_VCO_HSCLK_RATE_5400MHZDIV1000       5400000UL
+#define DP_VCO_HSCLK_RATE_8100MHZDIV1000       8100000UL
+
+#define NUM_DP_CLOCKS_MAX                      6
+
+#define DP_PHY_PLL_POLL_SLEEP_US               500
+#define DP_PHY_PLL_POLL_TIMEOUT_US             10000
+
+#define DP_VCO_RATE_8100MHZDIV1000             8100000UL
+#define DP_VCO_RATE_9720MHZDIV1000             9720000UL
+#define DP_VCO_RATE_10800MHZDIV1000            10800000UL
+
+struct dp_pll_10nm {
+       struct msm_dp_pll base;
+
+       int id;
+       struct platform_device *pdev;
+
+       void __iomem *pll_base;
+       void __iomem *phy_base;
+       void __iomem *ln_tx0_base;
+       void __iomem *ln_tx1_base;
+
+       /* private clocks: */
+       struct clk_hw *hws[NUM_DP_CLOCKS_MAX];
+       u32 num_hws;
+
+       /* clock-provider: */
+       struct clk_hw_onecell_data *hw_data;
+
+       /* lane and orientation settings */
+       u8 lane_cnt;
+       u8 orientation;
+
+       /* COM PHY settings */
+       u32 hsclk_sel;
+       u32 dec_start_mode0;
+       u32 div_frac_start1_mode0;
+       u32 div_frac_start2_mode0;
+       u32 div_frac_start3_mode0;
+       u32 integloop_gain0_mode0;
+       u32 integloop_gain1_mode0;
+       u32 vco_tune_map;
+       u32 lock_cmp1_mode0;
+       u32 lock_cmp2_mode0;
+       u32 lock_cmp3_mode0;
+       u32 lock_cmp_en;
+
+       /* PHY vco divider */
+       u32 phy_vco_div;
+       /*
+        * Certain pll's needs to update the same vco rate after 

resume in
+ * suspend/resume scenario. Cached the vco rate for such
plls.

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.h new file +10 -0
+        */
+       unsigned long   vco_cached_rate;
+       u32             cached_cfg0;
+       u32             cached_cfg1;
+       u32             cached_outdiv;
+
+       uint32_t index;
+};
+
+#define to_dp_pll_10nm(x)      container_of(x, struct dp_pll_10nm, 
Quoted 1 line

base)

Why is this struct in the header file? Does something outside of the PLL driver need to access this struct?

Quoted 32 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.h new file +10 -0
+
+int dp_vco_set_rate_10nm(struct clk_hw *hw, unsigned long rate,
+                               unsigned long parent_rate);
+unsigned long dp_vco_recalc_rate_10nm(struct clk_hw *hw,
+                               unsigned long parent_rate);
+long dp_vco_round_rate_10nm(struct clk_hw *hw, unsigned long rate,
+                               unsigned long *parent_rate);
+int dp_vco_prepare_10nm(struct clk_hw *hw);
+void dp_vco_unprepare_10nm(struct clk_hw *hw);
+#endif /* __DP_PLL_10NM_H */
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +3 -0
new file mode 100644
index 0000000..db3983d5
@@ -0,0 +1,524 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights 

reserved.
+ */

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +11 -0
+
+#define pr_fmt(fmt)    "%s: " fmt, __func__
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+
+#include "dp_hpd.h"
+#include "dp_pll.h"
+#include "dp_pll_10nm.h"

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c line 25

Quoted 10 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +10 -0
+
+static int dp_vco_pll_init_db_10nm(struct msm_dp_pll *pll,
+               unsigned long rate)
+{
+       struct dp_pll_10nm *dp_res = to_dp_pll_10nm(pll);
+       u32 spare_value = 0;
+
+       spare_value = PLL_REG_R(dp_res->phy_base, REG_DP_PHY_SPARE0);
+       dp_res->lane_cnt = spare_value & 0x0F;
+       dp_res->orientation = (spare_value & 0xF0) >> 4;

Can this be read at any time from the spare register? Just curious why

Quoted 8 lines

we need to read this here in init vs. when it's actually used. We could avoid stashing away these values in the struct. It also looks like the PLL part is intimately tied to the phy, but there isn't an actual phy driver. Why? It looks like the phy driver is a clk driver, and the clk framework is not prepared to handle things like lane count and orientation. I'd expect that to come through a PHY framework and then have the phy driver control the PLL by powering it back up and changing it's rates, etc. Not everything needs to be done through clk APIs.

Presently we need this spare register information in many functions to program dp hardware. There are atleast 6 instances we use them, so we are trying
to store in the structure once.
Unfortunately msm DP pll and phy is one hardware tightly bound.

Quoted 527 lines
Quoted 527 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +2 -0
+
+       DRM_DEBUG_DP("%s: spare_value=0x%x, ln_cnt=0x%x, 

orientation=0x%x\n",

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +93 -0
+                       __func__, spare_value, dp_res->lane_cnt,
+                       dp_res->orientation);
+
+       switch (rate) {
+       case DP_VCO_HSCLK_RATE_1620MHZDIV1000:
+               DRM_DEBUG_DP("%s: VCO rate: %ld\n", __func__,
+                               DP_VCO_RATE_9720MHZDIV1000);
+               dp_res->hsclk_sel = 0x0c;
+               dp_res->dec_start_mode0 = 0x69;
+               dp_res->div_frac_start1_mode0 = 0x00;
+               dp_res->div_frac_start2_mode0 = 0x80;
+               dp_res->div_frac_start3_mode0 = 0x07;
+               dp_res->integloop_gain0_mode0 = 0x3f;
+               dp_res->integloop_gain1_mode0 = 0x00;
+               dp_res->vco_tune_map = 0x00;
+               dp_res->lock_cmp1_mode0 = 0x6f;
+               dp_res->lock_cmp2_mode0 = 0x08;
+               dp_res->lock_cmp3_mode0 = 0x00;
+               dp_res->phy_vco_div = 0x1;
+               dp_res->lock_cmp_en = 0x00;
+               break;
+       case DP_VCO_HSCLK_RATE_2700MHZDIV1000:
+               DRM_DEBUG_DP("%s: VCO rate: %ld\n", __func__,
+                               DP_VCO_RATE_10800MHZDIV1000);
+               dp_res->hsclk_sel = 0x04;
+               dp_res->dec_start_mode0 = 0x69;
+               dp_res->div_frac_start1_mode0 = 0x00;
+               dp_res->div_frac_start2_mode0 = 0x80;
+               dp_res->div_frac_start3_mode0 = 0x07;
+               dp_res->integloop_gain0_mode0 = 0x3f;
+               dp_res->integloop_gain1_mode0 = 0x00;
+               dp_res->vco_tune_map = 0x00;
+               dp_res->lock_cmp1_mode0 = 0x0f;
+               dp_res->lock_cmp2_mode0 = 0x0e;
+               dp_res->lock_cmp3_mode0 = 0x00;
+               dp_res->phy_vco_div = 0x1;
+               dp_res->lock_cmp_en = 0x00;
+               break;
+       case DP_VCO_HSCLK_RATE_5400MHZDIV1000:
+               DRM_DEBUG_DP("%s: VCO rate: %ld\n", __func__,
+                               DP_VCO_RATE_10800MHZDIV1000);
+               dp_res->hsclk_sel = 0x00;
+               dp_res->dec_start_mode0 = 0x8c;
+               dp_res->div_frac_start1_mode0 = 0x00;
+               dp_res->div_frac_start2_mode0 = 0x00;
+               dp_res->div_frac_start3_mode0 = 0x0a;
+               dp_res->integloop_gain0_mode0 = 0x3f;
+               dp_res->integloop_gain1_mode0 = 0x00;
+               dp_res->vco_tune_map = 0x00;
+               dp_res->lock_cmp1_mode0 = 0x1f;
+               dp_res->lock_cmp2_mode0 = 0x1c;
+               dp_res->lock_cmp3_mode0 = 0x00;
+               dp_res->phy_vco_div = 0x2;
+               dp_res->lock_cmp_en = 0x00;
+               break;
+       case DP_VCO_HSCLK_RATE_8100MHZDIV1000:
+               DRM_DEBUG_DP("%s: VCO rate: %ld\n", __func__,
+                               DP_VCO_RATE_8100MHZDIV1000);
+               dp_res->hsclk_sel = 0x03;
+               dp_res->dec_start_mode0 = 0x69;
+               dp_res->div_frac_start1_mode0 = 0x00;
+               dp_res->div_frac_start2_mode0 = 0x80;
+               dp_res->div_frac_start3_mode0 = 0x07;
+               dp_res->integloop_gain0_mode0 = 0x3f;
+               dp_res->integloop_gain1_mode0 = 0x00;
+               dp_res->vco_tune_map = 0x00;
+               dp_res->lock_cmp1_mode0 = 0x2f;
+               dp_res->lock_cmp2_mode0 = 0x2a;
+               dp_res->lock_cmp3_mode0 = 0x00;
+               dp_res->phy_vco_div = 0x0;
+               dp_res->lock_cmp_en = 0x08;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int dp_config_vco_rate_10nm(struct msm_dp_pll *pll,
+               unsigned long rate)
+{
+       u32 res = 0;
+       struct dp_pll_10nm *dp_res = to_dp_pll_10nm(pll);
+
+       res = dp_vco_pll_init_db_10nm(pll, rate);
+       if (res) {
+               DRM_ERROR("VCO Init DB failed\n");
+               return res;
+       }
+
+       if (dp_res->lane_cnt != 4) {
+               if (dp_res->orientation == ORIENTATION_CC2)
+                       PLL_REG_W(dp_res->phy_base, 

REG_DP_PHY_PD_CTL, 0x6d);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +2 -0
+               else
+                       PLL_REG_W(dp_res->phy_base, 

REG_DP_PHY_PD_CTL, 0x75);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +5 -0
+       } else {
+               PLL_REG_W(dp_res->phy_base, REG_DP_PHY_PD_CTL, 0x7d);
+       }
+
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_SVS_MODE_CLK_SEL, 

0x01);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +4 -0
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_SYSCLK_EN_SEL, 0x37);
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_SYS_CLK_CTRL, 0x02);
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_CLK_ENABLE1, 0x0e);
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_SYSCLK_BUF_ENABLE, 

0x06);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +8 -0
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_CLK_SEL, 0x30);
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_CMN_CONFIG, 0x02);
+
+       /* Different for each clock rates */
+       PLL_REG_W(dp_res->pll_base,
+               QSERDES_COM_HSCLK_SEL, dp_res->hsclk_sel);
+       PLL_REG_W(dp_res->pll_base,
+               QSERDES_COM_DEC_START_MODE0, 

dp_res->dec_start_mode0);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +19 -0
+       PLL_REG_W(dp_res->pll_base,
+               QSERDES_COM_DIV_FRAC_START1_MODE0,
+               dp_res->div_frac_start1_mode0);
+       PLL_REG_W(dp_res->pll_base,
+               QSERDES_COM_DIV_FRAC_START2_MODE0,
+               dp_res->div_frac_start2_mode0);
+       PLL_REG_W(dp_res->pll_base,
+               QSERDES_COM_DIV_FRAC_START3_MODE0,
+               dp_res->div_frac_start3_mode0);
+       PLL_REG_W(dp_res->pll_base,
+               QSERDES_COM_INTEGLOOP_GAIN0_MODE0,
+               dp_res->integloop_gain0_mode0);
+       PLL_REG_W(dp_res->pll_base,
+               QSERDES_COM_INTEGLOOP_GAIN1_MODE0,
+               dp_res->integloop_gain1_mode0);
+       PLL_REG_W(dp_res->pll_base,
+               QSERDES_COM_VCO_TUNE_MAP, dp_res->vco_tune_map);
+       PLL_REG_W(dp_res->pll_base,
+               QSERDES_COM_LOCK_CMP1_MODE0, 

dp_res->lock_cmp1_mode0);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +2 -0
+       PLL_REG_W(dp_res->pll_base,
+               QSERDES_COM_LOCK_CMP2_MODE0, 

dp_res->lock_cmp2_mode0);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +2 -0
+       PLL_REG_W(dp_res->pll_base,
+               QSERDES_COM_LOCK_CMP3_MODE0, 

dp_res->lock_cmp3_mode0);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +3 -0
+
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_BG_TIMER, 0x0a);
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_CORECLK_DIV_MODE0, 

0x0a);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +2 -0
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_VCO_TUNE_CTRL, 0x00);
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 

0x3f);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +5 -0
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_CORE_CLK_EN, 0x1f);
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_PLL_IVCO, 0x07);
+       PLL_REG_W(dp_res->pll_base,
+               QSERDES_COM_LOCK_CMP_EN, dp_res->lock_cmp_en);
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_PLL_CCTRL_MODE0, 

0x36);
+ PLL_REG_W(dp_res->pll_base, QSERDES_COM_PLL_RCTRL_MODE0,
0x16);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +30 -0
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_CP_CTRL_MODE0, 0x06);
+
+       if (dp_res->orientation == ORIENTATION_CC2)
+               PLL_REG_W(dp_res->phy_base, REG_DP_PHY_MODE, 0x4c);
+       else
+               PLL_REG_W(dp_res->phy_base, REG_DP_PHY_MODE, 0x5c);
+
+       /* TX Lane configuration */
+       PLL_REG_W(dp_res->phy_base,
+                       REG_DP_PHY_TX0_TX1_LANE_CTL, 0x05);
+       PLL_REG_W(dp_res->phy_base,
+                       REG_DP_PHY_TX2_TX3_LANE_CTL, 0x05);
+
+       /* TX-0 register configuration */
+       PLL_REG_W(dp_res->ln_tx0_base,
+                       REG_DP_PHY_TXn_TRANSCEIVER_BIAS_EN, 0x1a);
+       PLL_REG_W(dp_res->ln_tx0_base,
+                       REG_DP_PHY_TXn_VMODE_CTRL1, 0x40);
+       PLL_REG_W(dp_res->ln_tx0_base,
+                       REG_DP_PHY_TXn_PRE_STALL_LDO_BOOST_EN, 0x30);
+       PLL_REG_W(dp_res->ln_tx0_base,
+                       REG_DP_PHY_TXn_INTERFACE_SELECT, 0x3d);
+       PLL_REG_W(dp_res->ln_tx0_base,
+                       REG_DP_PHY_TXn_CLKBUF_ENABLE, 0x0f);
+       PLL_REG_W(dp_res->ln_tx0_base,
+                       REG_DP_PHY_TXn_RESET_TSYNC_EN, 0x03);
+       PLL_REG_W(dp_res->ln_tx0_base,
+                       REG_DP_PHY_TXn_TRAN_DRVR_EMP_EN, 0x03);
+       PLL_REG_W(dp_res->ln_tx0_base,
+                       REG_DP_PHY_TXn_PARRATE_REC_DETECT_IDLE_EN, 

0x00);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +21 -0
+       PLL_REG_W(dp_res->ln_tx0_base,
+                       REG_DP_PHY_TXn_TX_INTERFACE_MODE, 0x00);
+       PLL_REG_W(dp_res->ln_tx0_base, REG_DP_PHY_TXn_TX_BAND, 0x4);
+
+       /* TX-1 register configuration */
+       PLL_REG_W(dp_res->ln_tx1_base,
+                       REG_DP_PHY_TXn_TRANSCEIVER_BIAS_EN, 0x1a);
+       PLL_REG_W(dp_res->ln_tx1_base,
+                       REG_DP_PHY_TXn_VMODE_CTRL1, 0x40);
+       PLL_REG_W(dp_res->ln_tx1_base,
+                       REG_DP_PHY_TXn_PRE_STALL_LDO_BOOST_EN, 0x30);
+       PLL_REG_W(dp_res->ln_tx1_base,
+                       REG_DP_PHY_TXn_INTERFACE_SELECT, 0x3d);
+       PLL_REG_W(dp_res->ln_tx1_base,
+                       REG_DP_PHY_TXn_CLKBUF_ENABLE, 0x0f);
+       PLL_REG_W(dp_res->ln_tx1_base,
+                       REG_DP_PHY_TXn_RESET_TSYNC_EN, 0x03);
+       PLL_REG_W(dp_res->ln_tx1_base,
+                       REG_DP_PHY_TXn_TRAN_DRVR_EMP_EN, 0x03);
+       PLL_REG_W(dp_res->ln_tx1_base,
+                       REG_DP_PHY_TXn_PARRATE_REC_DETECT_IDLE_EN, 

0x00);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +25 -0
+       PLL_REG_W(dp_res->ln_tx1_base,
+                       REG_DP_PHY_TXn_TX_INTERFACE_MODE, 0x00);
+       PLL_REG_W(dp_res->ln_tx1_base,
+                       REG_DP_PHY_TXn_TX_BAND, 0x4);
+
+       /* dependent on the vco frequency */
+       PLL_REG_W(dp_res->phy_base,
+                       REG_DP_PHY_VCO_DIV, dp_res->phy_vco_div);
+
+       return res;
+}
+
+static bool dp_10nm_pll_lock_status(struct dp_pll_10nm *dp_res)
+{
+       u32 status;
+       bool pll_locked;
+
+       /* poll for PLL lock status */
+       if (readl_poll_timeout_atomic((dp_res->pll_base +
+                       QSERDES_COM_C_READY_STATUS),
+                       status,
+                       ((status & BIT(0)) > 0),
+                       DP_PHY_PLL_POLL_SLEEP_US,
+                       DP_PHY_PLL_POLL_TIMEOUT_US)) {
+               DRM_ERROR("%s: C_READY status is not high. 

Status=%x\n",

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +70 -0
+                               __func__, status);
+               pll_locked = false;
+       } else {
+               pll_locked = true;
+       }
+
+       return pll_locked;
+}
+
+static bool dp_10nm_phy_rdy_status(struct dp_pll_10nm *dp_res)
+{
+       u32 status;
+       bool phy_ready = true;
+
+       /* poll for PHY ready status */
+       if (readl_poll_timeout_atomic((dp_res->phy_base +
+                       REG_DP_PHY_STATUS),
+                       status,
+                       ((status & (BIT(1))) > 0),
+                       DP_PHY_PLL_POLL_SLEEP_US,
+                       DP_PHY_PLL_POLL_TIMEOUT_US)) {
+               DRM_ERROR("%s: Phy_ready is not high. Status=%x\n",
+                               __func__, status);
+               phy_ready = false;
+       }
+
+       return phy_ready;
+}
+
+static int dp_pll_enable_10nm(struct clk_hw *hw)
+{
+       int rc = 0;
+       struct msm_dp_pll *pll = hw_clk_to_pll(hw);
+       struct dp_pll_10nm *dp_res = to_dp_pll_10nm(pll);
+       u32 bias_en, drvr_en;
+
+       PLL_REG_W(dp_res->phy_base, REG_DP_PHY_AUX_CFG2, 0x04);
+       PLL_REG_W(dp_res->phy_base, REG_DP_PHY_CFG, 0x01);
+       PLL_REG_W(dp_res->phy_base, REG_DP_PHY_CFG, 0x05);
+       PLL_REG_W(dp_res->phy_base, REG_DP_PHY_CFG, 0x01);
+       PLL_REG_W(dp_res->phy_base, REG_DP_PHY_CFG, 0x09);
+
+       PLL_REG_W(dp_res->pll_base, QSERDES_COM_RESETSM_CNTRL, 0x20);
+
+       if (!dp_10nm_pll_lock_status(dp_res)) {
+               rc = -EINVAL;
+               goto lock_err;
+       }
+
+       PLL_REG_W(dp_res->phy_base, REG_DP_PHY_CFG, 0x19);
+       /* poll for PHY ready status */
+       if (!dp_10nm_phy_rdy_status(dp_res)) {
+               rc = -EINVAL;
+               goto lock_err;
+       }
+
+       DRM_DEBUG_DP("%s: PLL is locked\n", __func__);
+
+       if (dp_res->lane_cnt == 1) {
+               bias_en = 0x3e;
+               drvr_en = 0x13;
+       } else {
+               bias_en = 0x3f;
+               drvr_en = 0x10;
+       }
+
+       if (dp_res->lane_cnt != 4) {
+               if (dp_res->orientation == ORIENTATION_CC1) {
+                       PLL_REG_W(dp_res->ln_tx1_base,
+                               REG_DP_PHY_TXn_HIGHZ_DRVR_EN, 

drvr_en);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +2 -0
+                       PLL_REG_W(dp_res->ln_tx1_base,
+                               REG_DP_PHY_TXn_TRANSCEIVER_BIAS_EN, 

bias_en);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +3 -0
+               } else {
+                       PLL_REG_W(dp_res->ln_tx0_base,
+                               REG_DP_PHY_TXn_HIGHZ_DRVR_EN, 

drvr_en);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +2 -0
+                       PLL_REG_W(dp_res->ln_tx0_base,
+                               REG_DP_PHY_TXn_TRANSCEIVER_BIAS_EN, 

bias_en);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +4 -0
+               }
+       } else {
+               PLL_REG_W(dp_res->ln_tx0_base,
+                               REG_DP_PHY_TXn_HIGHZ_DRVR_EN, 

drvr_en);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +2 -0
+               PLL_REG_W(dp_res->ln_tx0_base,
+                               REG_DP_PHY_TXn_TRANSCEIVER_BIAS_EN, 

bias_en);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +2 -0
+               PLL_REG_W(dp_res->ln_tx1_base,
+                               REG_DP_PHY_TXn_HIGHZ_DRVR_EN, 

drvr_en);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +2 -0
+               PLL_REG_W(dp_res->ln_tx1_base,
+                               REG_DP_PHY_TXn_TRANSCEIVER_BIAS_EN, 

bias_en);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +3 -0
+       }
+
+       PLL_REG_W(dp_res->ln_tx0_base, REG_DP_PHY_TXn_TX_POL_INV, 

0x0a);
+ PLL_REG_W(dp_res->ln_tx1_base, REG_DP_PHY_TXn_TX_POL_INV,
0x0a);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +12 -0
+       PLL_REG_W(dp_res->phy_base, REG_DP_PHY_CFG, 0x18);
+       udelay(2000);
+
+       PLL_REG_W(dp_res->phy_base, REG_DP_PHY_CFG, 0x19);
+
+       /* poll for PHY ready status */
+       if (!dp_10nm_phy_rdy_status(dp_res)) {
+               rc = -EINVAL;
+               goto lock_err;
+       }
+
+       PLL_REG_W(dp_res->ln_tx0_base, REG_DP_PHY_TXn_TX_DRV_LVL, 

0x38);
+ PLL_REG_W(dp_res->ln_tx1_base, REG_DP_PHY_TXn_TX_DRV_LVL,
0x38);
+ PLL_REG_W(dp_res->ln_tx0_base,
REG_DP_PHY_TXn_TX_EMP_POST1_LVL, 0x20);
+ PLL_REG_W(dp_res->ln_tx1_base,
REG_DP_PHY_TXn_TX_EMP_POST1_LVL, 0x20);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +2 -0
+       PLL_REG_W(dp_res->ln_tx0_base,
+                       REG_DP_PHY_TXn_RES_CODE_LANE_OFFSET_TX, 

0x06);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +2 -0
+       PLL_REG_W(dp_res->ln_tx1_base,
+                       REG_DP_PHY_TXn_RES_CODE_LANE_OFFSET_TX, 

0x06);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +2 -0
+       PLL_REG_W(dp_res->ln_tx0_base,
+                       REG_DP_PHY_TXn_RES_CODE_LANE_OFFSET_RX, 

0x07);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +2 -0
+       PLL_REG_W(dp_res->ln_tx1_base,
+                       REG_DP_PHY_TXn_RES_CODE_LANE_OFFSET_RX, 

0x07);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +29 -0
+
+lock_err:
+       return rc;
+}
+
+static int dp_pll_disable_10nm(struct clk_hw *hw)
+{
+       int rc = 0;
+       struct msm_dp_pll *pll = hw_clk_to_pll(hw);
+       struct dp_pll_10nm *dp_res = to_dp_pll_10nm(pll);
+
+       /* Assert DP PHY power down */
+       PLL_REG_W(dp_res->phy_base, REG_DP_PHY_PD_CTL, 0x2);
+
+       return rc;
+}
+
+
+int dp_vco_prepare_10nm(struct clk_hw *hw)
+{
+       int rc = 0;
+       struct msm_dp_pll *pll = hw_clk_to_pll(hw);
+       struct dp_pll_10nm *dp_res = to_dp_pll_10nm(pll);
+
+       DRM_DEBUG_DP("%s: rate = %ld\n", __func__, pll->rate);
+       if ((dp_res->vco_cached_rate != 0)
+               && (dp_res->vco_cached_rate == pll->rate)) {
+               rc = dp_vco_set_rate_10nm(hw,
+                       dp_res->vco_cached_rate, 

dp_res->vco_cached_rate);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +2 -0
+               if (rc) {
+                       DRM_ERROR("index=%d vco_set_rate failed. 

rc=%d\n",

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +75 -0
+                               rc, dp_res->index);
+                       goto error;
+               }
+       }
+
+       rc = dp_pll_enable_10nm(hw);
+       if (rc) {
+               DRM_ERROR("ndx=%d failed to enable dp pll\n",
+                                       dp_res->index);
+               goto error;
+       }
+
+       pll->pll_on = true;
+error:
+       return rc;
+}
+
+void dp_vco_unprepare_10nm(struct clk_hw *hw)
+{
+       struct msm_dp_pll *pll = hw_clk_to_pll(hw);
+       struct dp_pll_10nm *dp_res = to_dp_pll_10nm(pll);
+
+       if (!dp_res) {
+               DRM_ERROR("Invalid input parameter\n");
+               return;
+       }
+
+       if (!pll->pll_on) {
+               DRM_ERROR("pll resource can't be enabled\n");
+               return;
+       }
+       dp_res->vco_cached_rate = pll->rate;
+       dp_pll_disable_10nm(hw);
+
+       pll->pll_on = false;
+}
+
+int dp_vco_set_rate_10nm(struct clk_hw *hw, unsigned long rate,
+                                       unsigned long parent_rate)
+{
+       struct msm_dp_pll *pll = hw_clk_to_pll(hw);
+       int rc;
+
+       DRM_DEBUG_DP("DP lane CLK rate=%ld\n", rate);
+
+       rc = dp_config_vco_rate_10nm(pll, rate);
+       if (rc)
+               DRM_ERROR("%s: Failed to set clk rate\n", __func__);
+
+       pll->rate = rate;
+
+       return 0;
+}
+
+unsigned long dp_vco_recalc_rate_10nm(struct clk_hw *hw,
+                                       unsigned long parent_rate)
+{
+       struct msm_dp_pll *pll = hw_clk_to_pll(hw);
+       struct dp_pll_10nm *dp_res = to_dp_pll_10nm(pll);
+       u32 div, hsclk_div, link_clk_div = 0;
+       u64 vco_rate;
+
+       div = PLL_REG_R(dp_res->pll_base, QSERDES_COM_HSCLK_SEL);
+       div &= 0x0f;
+
+       if (div == 12)
+               hsclk_div = 6; /* Default */
+       else if (div == 4)
+               hsclk_div = 4;
+       else if (div == 0)
+               hsclk_div = 2;
+       else if (div == 3)
+               hsclk_div = 1;
+       else {
+               DRM_DEBUG_DP("unknown divider. forcing to 

default\n");

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +14 -0
+               hsclk_div = 5;
+       }
+
+       div = PLL_REG_R(dp_res->phy_base, REG_DP_PHY_AUX_CFG2);
+       div >>= 2;
+
+       if ((div & 0x3) == 0)
+               link_clk_div = 5;
+       else if ((div & 0x3) == 1)
+               link_clk_div = 10;
+       else if ((div & 0x3) == 2)
+               link_clk_div = 20;
+       else
+               DRM_ERROR("%s: unsupported div. Phy_mode: %d\n", 

__func__, div);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +15 -0
+
+       if (link_clk_div == 20) {
+               vco_rate = DP_VCO_HSCLK_RATE_2700MHZDIV1000;
+       } else {
+               if (hsclk_div == 6)
+                       vco_rate = DP_VCO_HSCLK_RATE_1620MHZDIV1000;
+               else if (hsclk_div == 4)
+                       vco_rate = DP_VCO_HSCLK_RATE_2700MHZDIV1000;
+               else if (hsclk_div == 2)
+                       vco_rate = DP_VCO_HSCLK_RATE_5400MHZDIV1000;
+               else
+                       vco_rate = DP_VCO_HSCLK_RATE_8100MHZDIV1000;
+       }
+
+       DRM_DEBUG_DP("returning vco rate = %lu\n", (unsigned 

long)vco_rate);

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +14 -0
+
+       dp_res->vco_cached_rate = pll->rate = vco_rate;
+       return (unsigned long)vco_rate;
+}
+
+long dp_vco_round_rate_10nm(struct clk_hw *hw, unsigned long rate,
+                       unsigned long *parent_rate)
+{
+       unsigned long rrate = rate;
+       struct msm_dp_pll *pll = hw_clk_to_pll(hw);
+
+       if (rate <= pll->min_rate)
+               rrate = pll->min_rate;
+       else if (rate <= DP_VCO_HSCLK_RATE_2700MHZDIV1000)
Quoted 8 lines

drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c line 517

Quoted 5 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +5 -0
+               rrate = DP_VCO_HSCLK_RATE_2700MHZDIV1000;
+       else if (rate <= DP_VCO_HSCLK_RATE_5400MHZDIV1000)
+               rrate = DP_VCO_HSCLK_RATE_5400MHZDIV1000;
+       else
+               rrate = pll->max_rate;

A switch statement would be more readable. Not sure why pll->min_rate is needed though.

pll->min_rate is the minimum vco clock that is supported on the chipset.
We are trying to make sure that we dont go below the min vco
level for that chipset.

Quoted 15 lines
	switch (rate) {
	case 0 .. pll->min_rate:
		rrate =
		break;
	case pll->min_rate .. DP_VCO_HSCLK_RATE_2700MHZDIV1000:
		...
	}
Quoted 6 lines
drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c new file +6 -0
+
+       DRM_DEBUG_DP("%s: rrate=%ld\n", __func__, rrate);
+
+       *parent_rate = rrate;
+       return rrate;
+}
author
#12 lore raw

PATCH v4 5/5 drm/msm/dpu: add display port support in DPU

In reply to Vara Reddy

From: Jeykumar Sankaran <[email protected]>

Add display port support in DPU by creating hooks for DP encoder enumeration and encoder mode initialization.

This change is based on the Snapdragon Display port driver changes[1].

changes in v2:
- rebase on [2] (Sean Paul)
- remove unwanted error checks and
switch cases (Jordan Crouse)

[1] https://lwn.net/Articles/768265/
[2] https://lkml.org/lkml/2018/11/17/87

changes in V3:
-- Moved this change as part of the DP driver changes. -- Addressed compilation issues on the latest code base.

---
 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c |  8 ++--
 drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c     | 65 ++++++++++++++++++++++++-----
 2 files changed, 58 insertions(+), 15 deletions(-)
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +4 -4
index b063243..7c95f72 100644
@@ -2025,7 +2025,7 @@ static int dpu_encoder_setup_display(struct dpu_encoder_virt *dpu_enc,
 {
 	int ret = 0;
 	int i = 0;
-	enum dpu_intf_type intf_type;
+	enum dpu_intf_type intf_type = INTF_NONE;
 	struct dpu_enc_phys_init_params phys_params;
 
 	if (!dpu_enc) {
@@ -2047,9 +2047,9 @@ static int dpu_encoder_setup_display(struct dpu_encoder_virt *dpu_enc,
 	case DRM_MODE_ENCODER_DSI:
 		intf_type = INTF_DSI;
 		break;
-	default:
-		DPU_ERROR_ENC(dpu_enc, "unsupported display interface type\n");
-		return -EINVAL;
+	case DRM_MODE_ENCODER_TMDS:
+		intf_type = INTF_DP;
+		break;
 	}
 
 	WARN_ON(disp_info->num_of_h_tiles < 1);
drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c +54 -11

85 lines not on this page · raw message

Signature · 2 lines
$(echo -e 'The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project')