Skip to contentSkip to conversation

platform: dwc: Add of DesignWare MIPI CSI-2 Host

PATCH Superseded Luis Oliveira 4 participants 19 messages Sep 20, 2018 – Oct 15, 2018

Subsystems (3)

v2 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

v2 cover platform: dwc: Add of DesignWare MIPI CSI-2 Host

This adds support for Synopsys MIPI CSI-2 Host and MIPI D-PHY. The patch series include support for initialization/configuration of the DW MIPI CSI-2 controller and DW MIPI D-PHY and both include a reference platform driver.

This will enable future SoCs to use this standard approach and possibly create a more clean environment.

This series also documents the dt-bindings needed for the platform drivers.

This was applied in: https://git.linuxtv.org/media_tree.git

Luis Oliveira (5):
media: platform: Add a DesignWare folder to have Synopsys drivers
Documentation: dt-bindings: Document the Synopsys MIPI DPHY Rx
bindings
media: platform: dwc: Add DW MIPI DPHY Rx platform
Documentation: dt-bindings: Document bindings for DW MIPI CSI-2 Host
media: platform: dwc: Add MIPI CSI-2 controller driver

 .../devicetree/bindings/media/snps,dw-csi-plat.txt |  74 +++
 .../devicetree/bindings/phy/snps,dphy-rx.txt       |  36 ++
 MAINTAINERS                                        |  11 +
 drivers/media/platform/Kconfig                     |   1 +
 drivers/media/platform/Makefile                    |   3 +
 drivers/media/platform/dwc/Kconfig                 |  43 ++
 drivers/media/platform/dwc/Makefile                |   4 +
 drivers/media/platform/dwc/dw-csi-plat.c           | 508 ++++++++++++++++++
 drivers/media/platform/dwc/dw-csi-plat.h           |  76 +++
 drivers/media/platform/dwc/dw-dphy-plat.c          | 365 +++++++++++++
 drivers/media/platform/dwc/dw-dphy-rx.c            | 594 +++++++++++++++++++++
 drivers/media/platform/dwc/dw-dphy-rx.h            | 176 ++++++
 drivers/media/platform/dwc/dw-mipi-csi.c           | 490 +++++++++++++++++
 drivers/media/platform/dwc/dw-mipi-csi.h           | 202 +++++++
 include/media/dwc/dw-mipi-csi-pltfrm.h             | 101 ++++
 15 files changed, 2684 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/snps,dw-csi-plat.txt
 create mode 100644 Documentation/devicetree/bindings/phy/snps,dphy-rx.txt
 create mode 100644 drivers/media/platform/dwc/Kconfig
 create mode 100644 drivers/media/platform/dwc/Makefile
 create mode 100644 drivers/media/platform/dwc/dw-csi-plat.c
 create mode 100644 drivers/media/platform/dwc/dw-csi-plat.h
 create mode 100644 drivers/media/platform/dwc/dw-dphy-plat.c
 create mode 100644 drivers/media/platform/dwc/dw-dphy-rx.c
 create mode 100644 drivers/media/platform/dwc/dw-dphy-rx.h
 create mode 100644 drivers/media/platform/dwc/dw-mipi-csi.c
 create mode 100644 drivers/media/platform/dwc/dw-mipi-csi.h
 create mode 100644 include/media/dwc/dw-mipi-csi-pltfrm.h
Signature · 1 line
2.9.3
#1 lore raw

PATCH v2 1/5 media: platform: Add a DesignWare folder to have Synopsys drivers

This patch has the intention of make the patch series more clear by creating a dwc folder.

---
Changelog
v2:
- Fix Kbuild error with no Makefile present

 drivers/media/platform/Kconfig      | 1 +
 drivers/media/platform/Makefile     | 3 +++
 drivers/media/platform/dwc/Kconfig  | 0
 drivers/media/platform/dwc/Makefile | 0
 4 files changed, 4 insertions(+)
 create mode 100644 drivers/media/platform/dwc/Kconfig
 create mode 100644 drivers/media/platform/dwc/Makefile
drivers/media/platform/Kconfig +1 -0
index f627587..f627a27 100644
@@ -137,6 +137,7 @@ source "drivers/media/platform/am437x/Kconfig"
 source "drivers/media/platform/xilinx/Kconfig"
 source "drivers/media/platform/rcar-vin/Kconfig"
 source "drivers/media/platform/atmel/Kconfig"
+source "drivers/media/platform/dwc/Kconfig"
 
 config VIDEO_TI_CAL
 	tristate "TI CAL (Camera Adaptation Layer) driver"
drivers/media/platform/Makefile +3 -0
index 6ab6200..def2f33 100644
@@ -98,3 +98,6 @@ obj-$(CONFIG_VIDEO_QCOM_VENUS)		+= qcom/venus/
 obj-y					+= meson/
 
 obj-y					+= cros-ec-cec/
+
+obj-y					+= dwc/
+
drivers/media/platform/dwc/Kconfig new file +0 -0
new file mode 100644
index 0000000..e69de29
drivers/media/platform/dwc/Makefile new file +0 -0
new file mode 100644
index 0000000..e69de29
Signature · 1 line
2.9.3
#2 lore raw

Re: [V2, 1/5] media: platform: Add a DesignWare folder to have Synopsys drivers

Hi Luis,

Thank you for the patch.

Quoted 4 lines · On Thursday, 20 September 2018 14:16:39 EEST Luis Oliveira wrote:

This patch has the intention of make the patch series more clear by creating a dwc folder.

No issue with the content, but you should fold this in patch 3/5, it doesn't deserve a patch on its own.

Quoted 42 lines

---
Changelog
v2:
- Fix Kbuild error with no Makefile present

 drivers/media/platform/Kconfig      | 1 +
 drivers/media/platform/Makefile     | 3 +++
 drivers/media/platform/dwc/Kconfig  | 0
 drivers/media/platform/dwc/Makefile | 0
 4 files changed, 4 insertions(+)
 create mode 100644 drivers/media/platform/dwc/Kconfig
 create mode 100644 drivers/media/platform/dwc/Makefile
drivers/media/platform/Kconfig +1 -0
index f627587..f627a27 100644
@@ -137,6 +137,7 @@ source "drivers/media/platform/am437x/Kconfig"
 source "drivers/media/platform/xilinx/Kconfig"
 source "drivers/media/platform/rcar-vin/Kconfig"
 source "drivers/media/platform/atmel/Kconfig"
+source "drivers/media/platform/dwc/Kconfig"
 
 config VIDEO_TI_CAL
 	tristate "TI CAL (Camera Adaptation Layer) driver"
(quoted diff) +0 -0

b/drivers/media/platform/Makefile index 6ab6200..def2f33 100644

drivers/media/platform/Makefile +3 -0
@@ -98,3 +98,6 @@ obj-$(CONFIG_VIDEO_QCOM_VENUS)		+= qcom/venus/
 obj-y					+= meson/
 
 obj-y					+= cros-ec-cec/
+
+obj-y					+= dwc/
+
(quoted diff) +0 -0

b/drivers/media/platform/dwc/Kconfig new file mode 100644 index 0000000..e69de29

(quoted diff) +0 -0

b/drivers/media/platform/dwc/Makefile new file mode 100644 index 0000000..e69de29

Signature · 3 lines
Regards,

Laurent Pinchart
#3 lore raw

Re: [V2, 1/5] media: platform: Add a DesignWare folder to have Synopsys drivers

Quoted 5 lines · Laurent Pinchart

Hi Luis,

Thank you for the patch.

Quoted 9 lines
Quoted 4 lines · On Thursday, 20 September 2018 14:16:39 EEST Luis Oliveira wrote:

This patch has the intention of make the patch series more clear by creating a dwc folder.

No issue with the content, but you should fold this in patch 3/5, it doesn't deserve a patch on its own.

Thank you, I will do that in V3.

Quoted 43 lines
Quoted 42 lines

---
Changelog
v2:
- Fix Kbuild error with no Makefile present

 drivers/media/platform/Kconfig      | 1 +
 drivers/media/platform/Makefile     | 3 +++
 drivers/media/platform/dwc/Kconfig  | 0
 drivers/media/platform/dwc/Makefile | 0
 4 files changed, 4 insertions(+)
 create mode 100644 drivers/media/platform/dwc/Kconfig
 create mode 100644 drivers/media/platform/dwc/Makefile
drivers/media/platform/Kconfig +1 -0
index f627587..f627a27 100644
@@ -137,6 +137,7 @@ source "drivers/media/platform/am437x/Kconfig"
 source "drivers/media/platform/xilinx/Kconfig"
 source "drivers/media/platform/rcar-vin/Kconfig"
 source "drivers/media/platform/atmel/Kconfig"
+source "drivers/media/platform/dwc/Kconfig"
 
 config VIDEO_TI_CAL
 	tristate "TI CAL (Camera Adaptation Layer) driver"
(quoted diff) +0 -0

b/drivers/media/platform/Makefile index 6ab6200..def2f33 100644

drivers/media/platform/Makefile +3 -0
@@ -98,3 +98,6 @@ obj-$(CONFIG_VIDEO_QCOM_VENUS)		+= qcom/venus/
 obj-y					+= meson/
 
 obj-y					+= cros-ec-cec/
+
+obj-y					+= dwc/
+
(quoted diff) +0 -0

b/drivers/media/platform/dwc/Kconfig new file mode 100644 index 0000000..e69de29

(quoted diff) +0 -0

b/drivers/media/platform/dwc/Makefile new file mode 100644 index 0000000..e69de29

#4 lore raw

PATCH v2 2/5 Documentation: dt-bindings: Document the Synopsys MIPI DPHY Rx bindings

In reply to Luis Oliveira

Add device-tree bindings documentation for SNPS DesignWare MIPI D-PHY in RX mode.

---
Changelog
v2:
- no changes

 .../devicetree/bindings/phy/snps,dphy-rx.txt       | 36 ++++++++++++++++++++++
 1 file changed, 36 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/snps,dphy-rx.txt
Documentation/devicetree/bindings/phy/snps,dphy-rx.txt new file +36 -0
new file mode 100644
index 0000000..9079f4a
@@ -0,0 +1,36 @@
+Synopsys DesignWare MIPI Rx D-PHY block details
+
+Description
+-----------
+
+The Synopsys MIPI D-PHY controller supports MIPI-DPHY in receiver mode.
+Please refer to phy-bindings.txt for more information.
+
+Required properties:
+- compatible		: Shall be "snps,dphy-rx".
+- #phy-cells		: Must be 1.
+- snps,dphy-frequency	: Output frequency of the D-PHY.
+- snps,dphy-te-len	: Size of the communication interface (8 bits->8 or 12bits->12).
+- reg			: Physical base address and size of the device memory mapped
+		 	  registers;
+
+Optional properties:
+- snps,compat-mode	: Compatibility mode control
+
+The per-board settings:
+- gpios 		: Synopsys testchip used as reference uses this to change setup
+		  	  configurations.
+
+Example:
+
+	mipi_dphy_rx1: dphy@3040 {
+		compatible = "snps,dphy-rx";
+		#phy-cells = <1>;
+		snps,dphy-frequency = <300000>;
+		snps,dphy-te-len = <12>;
+		snps,compat-mode = <1>;
+		reg = < 0x03040 0x20
+			0x08000 0x100
+			0x09000 0x100>;
+	};
+
Signature · 1 line
2.9.3
#5 lore raw

Re: [V2, 2/5] Documentation: dt-bindings: Document the Synopsys MIPI DPHY Rx bindings

Hi Louis,

Thank you for the patch.

Quoted 22 lines · On Thursday, 20 September 2018 14:16:40 EEST Luis Oliveira wrote:

Add device-tree bindings documentation for SNPS DesignWare MIPI D-PHY in RX mode.

---
Changelog
v2:
- no changes

 .../devicetree/bindings/phy/snps,dphy-rx.txt       | 36 +++++++++++++++++++
 1 file changed, 36 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/snps,dphy-rx.txt
(quoted diff) +0 -0

b/Documentation/devicetree/bindings/phy/snps,dphy-rx.txt new file mode 100644
index 0000000..9079f4a

Documentation/devicetree/bindings/phy/snps,dphy-rx.txt new file +2 -0
@@ -0,0 +1,36 @@
+Synopsys DesignWare MIPI Rx D-PHY block details
+

Documentation/devicetree/bindings/phy/snps,dphy-rx.txt line 12

Quoted 10 lines
Documentation/devicetree/bindings/phy/snps,dphy-rx.txt new file +10 -0
+Description
+-----------
+
+The Synopsys MIPI D-PHY controller supports MIPI-DPHY in receiver mode.
+Please refer to phy-bindings.txt for more information.
+
+Required properties:
+- compatible		: Shall be "snps,dphy-rx".
+- #phy-cells		: Must be 1.
+- snps,dphy-frequency	: Output frequency of the D-PHY.

If that's the frequency of the clock on the output side of the RX PHY, doesn't it depend on the frequency on the CSI-2 (or other) bus ? Can't it vary ? Why do you need to have it in DT ?

Quoted 2 lines
Documentation/devicetree/bindings/phy/snps,dphy-rx.txt new file +1 -0
+- snps,dphy-te-len	: Size of the communication interface (8 bits->8 or

12bits->12).

We have similar properties in various bindings, such as bus-width in video- interfaces.txt. Couldn't we use a more standard name ?

Documentation/devicetree/bindings/phy/snps,dphy-rx.txt line 15

Quoted 2 lines

+- reg : Physical base address and size of the device memory mapped

Documentation/devicetree/bindings/phy/snps,dphy-rx.txt new file +1 -0
+		 	  registers;

The example below shows three ranges. Could you document the ranges that are expected ?

Quoted 2 lines

+Optional properties:
+- snps,compat-mode : Compatibility mode control

What is this ?

Quoted 2 lines

+The per-board settings:
+- gpios : Synopsys testchip used as reference uses this to change

setup

Documentation/devicetree/bindings/phy/snps,dphy-rx.txt line 22

Quoted 1 line
Documentation/devicetree/bindings/phy/snps,dphy-rx.txt new file +1 -0
+		  	  configurations.

Here too, what is this for ?

Documentation/devicetree/bindings/phy/snps,dphy-rx.txt line 34

Quoted 11 lines
Documentation/devicetree/bindings/phy/snps,dphy-rx.txt new file +11 -0
+Example:
+
+	mipi_dphy_rx1: dphy@3040 {
+		compatible = "snps,dphy-rx";
+		#phy-cells = <1>;
+		snps,dphy-frequency = <300000>;
+		snps,dphy-te-len = <12>;
+		snps,compat-mode = <1>;
+		reg = < 0x03040 0x20
+			0x08000 0x100
+			0x09000 0x100>;

The base addresses are pretty low, what kind of bus does this sit on ?

Quoted 2 lines
Documentation/devicetree/bindings/phy/snps,dphy-rx.txt new file +2 -0
+	};
+
Signature · 3 lines
Regards,

Laurent Pinchart
#6 lore raw

Re: [V2, 2/5] Documentation: dt-bindings: Document the Synopsys MIPI DPHY Rx bindings

Quoted 4 lines · Laurent Pinchart

Hi Louis,

Thank you for the patch.

Hi Laurent, thank you for your review, my answers are inline.

Quoted 29 lines · On Thursday, 20 September 2018 14:16:40 EEST Luis Oliveira wrote:
Quoted 28 lines

Add device-tree bindings documentation for SNPS DesignWare MIPI D-PHY in RX mode.

---
Changelog
v2:
- no changes

 .../devicetree/bindings/phy/snps,dphy-rx.txt       | 36 +++++++++++++++++++
 1 file changed, 36 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/snps,dphy-rx.txt
(quoted diff) +0 -0

b/Documentation/devicetree/bindings/phy/snps,dphy-rx.txt new file mode 100644
index 0000000..9079f4a

Documentation/devicetree/bindings/phy/snps,dphy-rx.txt new file +8 -0
@@ -0,0 +1,36 @@
+Synopsys DesignWare MIPI Rx D-PHY block details
+
+Description
+-----------
+
+The Synopsys MIPI D-PHY controller supports MIPI-DPHY in receiver mode.
+Please refer to phy-bindings.txt for more information.
+
Quoted 9 lines

Documentation/devicetree/bindings/phy/snps,dphy-rx.txt line 12

Quoted 4 lines
Documentation/devicetree/bindings/phy/snps,dphy-rx.txt new file +4 -0
+Required properties:
+- compatible		: Shall be "snps,dphy-rx".
+- #phy-cells		: Must be 1.
+- snps,dphy-frequency	: Output frequency of the D-PHY.

If that's the frequency of the clock on the output side of the RX PHY, doesn't it depend on the frequency on the CSI-2 (or other) bus ? Can't it vary ? Why do you need to have it in DT ?

But you are right, I will move it to the CSI-2 block.
The reason for it to be on the DT is that my use case have a camera with fixed frequency connected, but of course I can change it after.

Quoted 5 lines
Quoted 2 lines
Documentation/devicetree/bindings/phy/snps,dphy-rx.txt new file +1 -0
+- snps,dphy-te-len	: Size of the communication interface (8 bits->8 or

12bits->12).

We have similar properties in various bindings, such as bus-width in video- interfaces.txt. Couldn't we use a more standard name ?

I have read the bus-width property but I don't this is the same. This is not a video quite a video interface. Our D-PHY is configured using an control interface, which is called "test interface" and can have 8-bit or 12-bit.

Quoted 7 lines

Documentation/devicetree/bindings/phy/snps,dphy-rx.txt line 15

Quoted 3 lines

+- reg : Physical base address and size of the device memory mapped

Documentation/devicetree/bindings/phy/snps,dphy-rx.txt new file +1 -0
+		 	  registers;

The example below shows three ranges. Could you document the ranges that are expected ?

The three ranges is optional, it can be only two.
- The first is the interface from which we communicate with the d-phy. A small window the for the referenced above "test interface".
- The second and third reg are regions for configuration for the d-phy before the d-phy bring up:
a few examples:
- 4+4 data lanes max, 1 clk
- 8 data lanes max, 1clk
- 4+4 data lanes, 2 clk

Quoted 4 lines
Quoted 2 lines

+Optional properties:
+- snps,compat-mode : Compatibility mode control

What is this ?

This toggles a mode for a specific d-phy that we can't auto-detect. So when we use it we activate the compatibility mode for it. I can remove it if you think it's best.

Quoted 7 lines
Quoted 3 lines

+The per-board settings:
+- gpios : Synopsys testchip used as reference uses this to change

setup

Documentation/devicetree/bindings/phy/snps,dphy-rx.txt line 22

Quoted 1 line
Documentation/devicetree/bindings/phy/snps,dphy-rx.txt new file +1 -0
+		  	  configurations.

Here too, what is this for ?

Most of our d-phys have a wrapper around that can be controlled by a gpio-driver that can halt the d-phy.

Quoted 15 lines

Documentation/devicetree/bindings/phy/snps,dphy-rx.txt line 34

Quoted 12 lines
Documentation/devicetree/bindings/phy/snps,dphy-rx.txt new file +11 -0
+Example:
+
+	mipi_dphy_rx1: dphy@3040 {
+		compatible = "snps,dphy-rx";
+		#phy-cells = <1>;
+		snps,dphy-frequency = <300000>;
+		snps,dphy-te-len = <12>;
+		snps,compat-mode = <1>;
+		reg = < 0x03040 0x20
+			0x08000 0x100
+			0x09000 0x100>;

The base addresses are pretty low, what kind of bus does this sit on ?

It sits on top of a bus like this for my configuration.

dx_mb {
		compatible = "simple-bus";
		#address-cells = <1>;
		#size-cells = <1>;
		ranges = <0x00000000 0x0 0xD0000000 0x10000000>;
		interrupt-parent = <&he_intc>;
		...
Quoted 3 lines
Quoted 2 lines
Documentation/devicetree/bindings/phy/snps,dphy-rx.txt new file +2 -0
+	};
+
maintainer
#7 lore raw

Re: [V2, 2/5] Documentation: dt-bindings: Document the Synopsys MIPI DPHY Rx bindings

In reply to Luis Oliveira
Quoted 2 lines · Luis Oliveira

Add device-tree bindings documentation for SNPS DesignWare MIPI D-PHY in RX mode.

"dt-bindings: phy: ..." for the subject.

Quoted 25 lines

---
Changelog
v2:
- no changes

 .../devicetree/bindings/phy/snps,dphy-rx.txt       | 36 ++++++++++++++++++++++
 1 file changed, 36 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/snps,dphy-rx.txt
Documentation/devicetree/bindings/phy/snps,dphy-rx.txt new file +8 -0
new file mode 100644
index 0000000..9079f4a
@@ -0,0 +1,36 @@
+Synopsys DesignWare MIPI Rx D-PHY block details
+
+Description
+-----------
+
+The Synopsys MIPI D-PHY controller supports MIPI-DPHY in receiver mode.
+Please refer to phy-bindings.txt for more information.
+

Documentation/devicetree/bindings/phy/snps,dphy-rx.txt line 18

Quoted 10 lines
Documentation/devicetree/bindings/phy/snps,dphy-rx.txt new file +10 -0
+Required properties:
+- compatible		: Shall be "snps,dphy-rx".
+- #phy-cells		: Must be 1.
+- snps,dphy-frequency	: Output frequency of the D-PHY.
+- snps,dphy-te-len	: Size of the communication interface (8 bits->8 or 12bits->12).
+- reg			: Physical base address and size of the device memory mapped
+		 	  registers;
+
+Optional properties:
+- snps,compat-mode	: Compatibility mode control

type? values?

Documentation/devicetree/bindings/phy/snps,dphy-rx.txt line 22

Quoted 4 lines
Documentation/devicetree/bindings/phy/snps,dphy-rx.txt new file +4 -0
+
+The per-board settings:
+- gpios 		: Synopsys testchip used as reference uses this to change setup
+		  	  configurations.

Preferred to be named (e.g. foo-gpios). How many? What are their functions?

Quoted 17 lines
Documentation/devicetree/bindings/phy/snps,dphy-rx.txt new file +14 -0
+
+Example:
+
+	mipi_dphy_rx1: dphy@3040 {
+		compatible = "snps,dphy-rx";
+		#phy-cells = <1>;
+		snps,dphy-frequency = <300000>;
+		snps,dphy-te-len = <12>;
+		snps,compat-mode = <1>;
+		reg = < 0x03040 0x20
+			0x08000 0x100
+			0x09000 0x100>;
+	};
+
Signature · 1 line
2.9.3
maintainer
#8 lore raw

Re: [V2, 2/5] Documentation: dt-bindings: Document the Synopsys MIPI DPHY Rx bindings

Quoted 8 lines · Rob Herring
Quoted 2 lines · Luis Oliveira

Add device-tree bindings documentation for SNPS DesignWare MIPI D-PHY in RX mode.

"dt-bindings: phy: ..." for the subject.

Quoted 2 lines

Also, checkpatch.pl complains the author and S-o-b emails don't match.

Rob

#9 lore raw

Re: [V2, 2/5] Documentation: dt-bindings: Document the Synopsys MIPI DPHY Rx bindings

In reply to Rob Herring

Hi Rob,

Quoted 6 lines · Rob Herring
Quoted 2 lines · Luis Oliveira

Add device-tree bindings documentation for SNPS DesignWare MIPI D-PHY in RX mode.

"dt-bindings: phy: ..." for the subject.

Yes, you are right.

Quoted 29 lines
Quoted 29 lines

---
Changelog
v2:
- no changes

 .../devicetree/bindings/phy/snps,dphy-rx.txt       | 36 ++++++++++++++++++++++
 1 file changed, 36 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/snps,dphy-rx.txt
Documentation/devicetree/bindings/phy/snps,dphy-rx.txt new file +12 -0
new file mode 100644
index 0000000..9079f4a
@@ -0,0 +1,36 @@
+Synopsys DesignWare MIPI Rx D-PHY block details
+
+Description
+-----------
+
+The Synopsys MIPI D-PHY controller supports MIPI-DPHY in receiver mode.
+Please refer to phy-bindings.txt for more information.
+
+Required properties:
+- compatible		: Shall be "snps,dphy-rx".
+- #phy-cells		: Must be 1.
+- snps,dphy-frequency	: Output frequency of the D-PHY.
Quoted 9 lines

Documentation/devicetree/bindings/phy/snps,dphy-rx.txt line 18

Quoted 6 lines
Documentation/devicetree/bindings/phy/snps,dphy-rx.txt new file +6 -0
+- snps,dphy-te-len	: Size of the communication interface (8 bits->8 or 12bits->12).
+- reg			: Physical base address and size of the device memory mapped
+		 	  registers;
+
+Optional properties:
+- snps,compat-mode	: Compatibility mode control

type? values?

I will remove this in V3.

Quoted 8 lines

Documentation/devicetree/bindings/phy/snps,dphy-rx.txt line 22

Quoted 4 lines
Documentation/devicetree/bindings/phy/snps,dphy-rx.txt new file +4 -0
+
+The per-board settings:
+- gpios 		: Synopsys testchip used as reference uses this to change setup
+		  	  configurations.

Preferred to be named (e.g. foo-gpios). How many? What are their functions?

Ok, thanks for reviewing this.

Quoted 17 lines
Quoted 17 lines
Documentation/devicetree/bindings/phy/snps,dphy-rx.txt new file +14 -0
+
+Example:
+
+	mipi_dphy_rx1: dphy@3040 {
+		compatible = "snps,dphy-rx";
+		#phy-cells = <1>;
+		snps,dphy-frequency = <300000>;
+		snps,dphy-te-len = <12>;
+		snps,compat-mode = <1>;
+		reg = < 0x03040 0x20
+			0x08000 0x100
+			0x09000 0x100>;
+	};
+
Signature · 1 line
2.9.3
#10 lore raw

PATCH v2 3/5 media: platform: dwc: Add DW MIPI DPHY Rx platform

In reply to Luis Oliveira

Add of Synopsys MIPI D-PHY in RX mode support.
Separated in the implementation are platform dependent probing functions.

---
Changelog
v2:
- fix uninitialization warning
- fix SDPX License to match: Documentation/process/license-rules.rst

 drivers/media/platform/dwc/Kconfig        |  32 ++
 drivers/media/platform/dwc/Makefile       |   2 +
 drivers/media/platform/dwc/dw-dphy-plat.c | 365 ++++++++++++++++++
 drivers/media/platform/dwc/dw-dphy-rx.c   | 594 ++++++++++++++++++++++++++++++
 drivers/media/platform/dwc/dw-dphy-rx.h   | 176 +++++++++
 5 files changed, 1169 insertions(+)
 create mode 100644 drivers/media/platform/dwc/dw-dphy-plat.c
 create mode 100644 drivers/media/platform/dwc/dw-dphy-rx.c
 create mode 100644 drivers/media/platform/dwc/dw-dphy-rx.h
drivers/media/platform/dwc/Kconfig +32 -0
index e69de29..a573297 100644
@@ -0,0 +1,32 @@
+#
+#  Synopsys DWC Platform drivers
+#	Most drivers here are currently for MIPI CSI-2 and MIPI DPHY support
+
+config DWC_MIPI_CSI2_HOST
+	bool "Synopsys Designware CSI-2 Host Controller and DPHY-RX support"
+	select VIDEO_DEV
+	select VIDEO_V4L2
+	select VIDEO_V4L2_SUBDEV_API
+	select VIDEOBUF2_VMALLOC
+	select VIDEOBUF2_DMA_CONTIG
+	select GENERIC_PHY
+	select VIDEO_OV5647
+	help
+	 This selects the CSI-2 host controller support.
+
+	 If you have a controller with this interface, say Y.
+
+	  If unsure, say N.
+
+if DWC_MIPI_CSI2_HOST
+
+config DWC_MIPI_TC_DPHY_G128
+	tristate "DesignWare platform support using a G128 Test Chip"
+	depends on DWC_MIPI_CSI2_HOST
+	help
+	 Synopsys Test Chip is a MIPI D-PHY for prototyping purposes.
+
+	  If unsure, say N.
+
+endif # DWC_MIPI_CSI2_HOST
+
drivers/media/platform/dwc/Makefile +2 -0
index e69de29..8be6f68 100644
@@ -0,0 +1,2 @@
+obj-$(CONFIG_DWC_MIPI_TC_DPHY_G128)	+= dw-dphy-platfrm.o
+dw-dphy-platfrm-objs	:= dw-dphy-plat.o dw-dphy-rx.o
drivers/media/platform/dwc/dw-dphy-plat.c new file +365 -0

365 lines not on this page · raw message

drivers/media/platform/dwc/dw-dphy-rx.c new file +594 -0

594 lines not on this page · raw message

drivers/media/platform/dwc/dw-dphy-rx.h new file +176 -0

176 lines not on this page · raw message

Signature · 1 line
2.9.3
#11 lore raw

Re: [V2, 3/5] media: platform: dwc: Add DW MIPI DPHY Rx platform

Hi Louis,

Thank you for the patch.

Quoted 10 lines · On Thursday, 20 September 2018 14:16:41 EEST Luis Oliveira wrote:

Add of Synopsys MIPI D-PHY in RX mode support.
Separated in the implementation are platform dependent probing functions.

---
Changelog
v2:
- fix uninitialization warning
- fix SDPX License to match: Documentation/process/license-rules.rst

Quoted 5 lines
 drivers/media/platform/dwc/Kconfig        |  32 ++
 drivers/media/platform/dwc/Makefile       |   2 +
 drivers/media/platform/dwc/dw-dphy-plat.c | 365 ++++++++++++++++++
 drivers/media/platform/dwc/dw-dphy-rx.c   | 594 +++++++++++++++++++++++++++
 drivers/media/platform/dwc/dw-dphy-rx.h   | 176 +++++++++

Is there really a need to split this in two source files ? It smells of OS abstraction layers.

Quoted 4 lines
 5 files changed, 1169 insertions(+)
 create mode 100644 drivers/media/platform/dwc/dw-dphy-plat.c
 create mode 100644 drivers/media/platform/dwc/dw-dphy-rx.c
 create mode 100644 drivers/media/platform/dwc/dw-dphy-rx.h

As this is a PHY driver, shouldn't it be located in drivers/phy/ ?

drivers/media/platform/dwc/Kconfig line 6

Quoted 11 lines
(quoted diff) +0 -0

b/drivers/media/platform/dwc/Kconfig index e69de29..a573297 100644

drivers/media/platform/dwc/Kconfig +6 -0
@@ -0,0 +1,32 @@
+#
+#  Synopsys DWC Platform drivers
+#	Most drivers here are currently for MIPI CSI-2 and MIPI DPHY support
+
+config DWC_MIPI_CSI2_HOST
+	bool "Synopsys Designware CSI-2 Host Controller and DPHY-RX support"

There's no CSI-2 host controller in this patch.

drivers/media/platform/dwc/Kconfig line 11

Quoted 5 lines
drivers/media/platform/dwc/Kconfig +5 -0
+	select VIDEO_DEV
+	select VIDEO_V4L2
+	select VIDEO_V4L2_SUBDEV_API
+	select VIDEOBUF2_VMALLOC
+	select VIDEOBUF2_DMA_CONTIG

All these don't seem to be needed.

drivers/media/platform/dwc/Kconfig line 13

Quoted 2 lines
drivers/media/platform/dwc/Kconfig +2 -0
+	select GENERIC_PHY
+	select VIDEO_OV5647

And I'm pretty sure you can use the PHY without an OV5647 sensor :-)

Quoted 32 lines
drivers/media/platform/dwc/Kconfig +19 -0
+	help
+	 This selects the CSI-2 host controller support.
+
+	 If you have a controller with this interface, say Y.
+
+	  If unsure, say N.
+
+if DWC_MIPI_CSI2_HOST
+
+config DWC_MIPI_TC_DPHY_G128
+	tristate "DesignWare platform support using a G128 Test Chip"
+	depends on DWC_MIPI_CSI2_HOST
+	help
+	 Synopsys Test Chip is a MIPI D-PHY for prototyping purposes.
+
+	  If unsure, say N.
+
+endif # DWC_MIPI_CSI2_HOST
+
(quoted diff) +0 -0

b/drivers/media/platform/dwc/Makefile index e69de29..8be6f68 100644

drivers/media/platform/dwc/Makefile +2 -0
@@ -0,0 +1,2 @@
+obj-$(CONFIG_DWC_MIPI_TC_DPHY_G128)	+= dw-dphy-platfrm.o
+dw-dphy-platfrm-objs	:= dw-dphy-plat.o dw-dphy-rx.o
(quoted diff) +0 -0

b/drivers/media/platform/dwc/dw-dphy-plat.c new file mode 100644 index 0000000..8e298f5

drivers/media/platform/dwc/dw-dphy-plat.c line 5

Quoted 5 lines
drivers/media/platform/dwc/dw-dphy-plat.c new file +5 -0
@@ -0,0 +1,365 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * dw-dphy-plat.c
+ *
+ * Copyright(c) 2018-present, Synopsys, Inc. and/or its affiliates.

I don't think that -present is right. The copyright notice should list the years explicitly.

Quoted 217 lines
drivers/media/platform/dwc/dw-dphy-plat.c new file +217 -0
+ * Luis Oliveira <[email protected]>
+ *
+ */
+
+#include "dw-dphy-rx.h"
+
+static struct phy *dw_dphy_xlate(struct device *dev,
+			struct of_phandle_args *args)
+{
+	struct dw_dphy_rx *dphy = dev_get_drvdata(dev);
+
+	return dphy->phy;
+}
+
+static ssize_t dphy_reset_show(struct device *dev,
+			struct device_attribute *attr,
+			char *buf)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct dw_dphy_rx *dphy = platform_get_drvdata(pdev);
+	char buffer[15];
+
+	dw_dphy_write(dphy, R_CSI2_DPHY_RSTZ, 0);
+	usleep_range(100, 200);
+	dw_dphy_write(dphy, R_CSI2_DPHY_RSTZ, 1);
+
+	return strlcpy(buf, buffer, PAGE_SIZE);
+}
+
+static ssize_t dphy_freq_store(struct device *dev,
+			struct device_attribute *attr,
+			const char *buf,
+			size_t count)
+{
+	int ret;
+	unsigned long freq;
+
+	struct platform_device *pdev = to_platform_device(dev);
+	struct dw_dphy_rx *dphy = platform_get_drvdata(pdev);
+
+	ret = kstrtoul(buf, 10, &freq);
+	if (ret < 0)
+		return ret;
+
+	if (freq > 2500) {
+		dev_info(dev, "Freq must be under 2500 Mhz\n");
+		return count;
+	}
+	if (freq < 80) {
+		dev_info(dev, "Freq must be over 80 Mhz\n");
+		return count;
+	}
+
+	dev_info(dev, "Data Rate %lu Mbps\n", freq);
+	dphy->dphy_freq = freq * 1000;
+
+	return count;
+
+}
+
+static ssize_t dphy_freq_show(struct device *dev,
+			struct device_attribute *attr,
+			char *buf)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct dw_dphy_rx *dphy = platform_get_drvdata(pdev);
+	char buffer[15];
+
+	snprintf(buffer, 15, "Freq %d\n", dphy->dphy_freq / 1000);
+
+	return strlcpy(buf, buffer, PAGE_SIZE);
+}
+
+static ssize_t dphy_addr_store(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t count)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct dw_dphy_rx *dphy = platform_get_drvdata(pdev);
+	unsigned long val;
+	int ret;
+	u8 addr, payload;
+
+	ret = kstrtoul(buf, 32, &val);
+	if (ret < 0)
+		return ret;
+
+	payload = (u16)val;
+	addr = (u16)(val >> 16);
+
+	dev_info(dev, "addr 0x%lX\n", val);
+	dev_info(dev, "payload: 0x%X\n", addr);
+
+	dev_info(dev,
+		"Addr [0x%x] -> 0x%x\n", (unsigned int)addr,
+		dw_dphy_te_read(dphy, addr));
+
+	return count;
+}
+
+static ssize_t idelay_show(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct dw_dphy_rx *dphy = platform_get_drvdata(pdev);
+	char buffer[15];
+
+	snprintf(buffer, 15, "idelay %d\n", dw_dphy_if_get_idelay(dphy));
+
+	return strlcpy(buf, buffer, PAGE_SIZE);
+}
+
+static ssize_t idelay_store(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t count)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct dw_dphy_rx *dphy = platform_get_drvdata(pdev);
+	int ret;
+	unsigned long val;
+	u8 lane, delay;
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret < 0)
+		return ret;
+
+	lane = (u8)val;
+	delay = (u8)(val >> 8);
+
+	dev_dbg(dev, "Lanes %u\n", lane);
+	dev_dbg(dev, "Delay %u\n", delay);
+
+	dw_dphy_if_set_idelay_lane(dphy, delay, lane);
+
+	return count;
+}
+
+static ssize_t len_config_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	int ret;
+	unsigned long length;
+
+	struct platform_device *pdev = to_platform_device(dev);
+	struct dw_dphy_rx *dphy = platform_get_drvdata(pdev);
+
+	ret = kstrtoul(buf, 10, &length);
+	if (ret < 0)
+		return ret;
+
+	if (length == BIT8)
+		pr_info("Configured for 8-bit interface\n");
+	else if (length == BIT12)
+		pr_info("Configured for 12-bit interface\n");
+	else
+		return count;
+
+	dphy->dphy_te_len = length;
+
+	return count;
+
+}
+
+static ssize_t len_config_show(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct dw_dphy_rx *dphy = platform_get_drvdata(pdev);
+	char buffer[20];
+
+	snprintf(buffer, 20, "Length %d\n", dphy->dphy_te_len);
+
+	return strlcpy(buf, buffer, PAGE_SIZE);
+}
+
+static ssize_t dw_dphy_g118_settle_store(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t count)
+{
+	int ret;
+	unsigned long lp_time;
+
+	struct platform_device *pdev = to_platform_device(dev);
+	struct dw_dphy_rx *dphy = platform_get_drvdata(pdev);
+
+	ret = kstrtoul(buf, 10, &lp_time);
+	if (ret < 0)
+		return ret;
+
+	if ((lp_time > 1) && (lp_time < 10000))
+		dphy->lp_time = lp_time;
+	else {
+		pr_info("Invalid Value configuring for 1000 ns\n");
+		dphy->lp_time = 1000;
+	}
+
+	dphy->lp_time = lp_time;
+
+	return count;
+
+}
+
+static ssize_t dw_dphy_g118_settle_show(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct dw_dphy_rx *dphy = platform_get_drvdata(pdev);
+	char buffer[10];
+
+	snprintf(buffer, 10, "Settle %d ns\n", dphy->lp_time);

drivers/media/platform/dwc/dw-dphy-plat.c line 232

Quoted 10 lines
drivers/media/platform/dwc/dw-dphy-plat.c new file +10 -0
+
+	return strlcpy(buf, buffer, PAGE_SIZE);
+}
+
+static DEVICE_ATTR_RO(dphy_reset);
+static DEVICE_ATTR_RW(dphy_freq);
+static DEVICE_ATTR_WO(dphy_addr);
+static DEVICE_ATTR_RW(idelay);
+static DEVICE_ATTR_RW(len_config);
+static DEVICE_ATTR_RW(dw_dphy_g118_settle);

If you want to expose an API through sysfs it should be properly documented. I don't think this is needed though, the PHY should be completely transparent for userspace, it should be handled fully within the kernel.

Quoted 121 lines
drivers/media/platform/dwc/dw-dphy-plat.c new file +121 -0
+static struct phy_ops dw_dphy_ops = {
+	.init = dw_dphy_init,
+	.reset = dw_dphy_reset,
+	.power_on = dw_dphy_power_on,
+	.power_off = dw_dphy_power_off,
+	.owner = THIS_MODULE,
+};
+
+static int dw_dphy_rx_probe(struct platform_device *pdev)
+{
+	struct dw_dphy_rx *dphy;
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	struct phy_provider *phy_provider;
+	struct phy *phy;
+
+	dphy = devm_kzalloc(dev, sizeof(*dphy), GFP_KERNEL);
+	if (!dphy)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	dphy->base_address = devm_ioremap(dev, res->start, resource_size(res));
+	if (IS_ERR(dphy->base_address)) {
+		dev_err(dev, "error requesting base address\n");
+		return PTR_ERR(dphy->base_address);
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	dphy->dphy1_if_addr = devm_ioremap_resource(dev, res);
+	if (IS_ERR(dphy->dphy1_if_addr)) {
+		dev_err(dev, "error requesting dphy 1 if regbank\n");
+		return PTR_ERR(dphy->dphy1_if_addr);
+	}
+
+	dphy->max_lanes =
+		dw_dphy_if_read_msk(dphy, DPHYID, DPHY_ID_LANE_SUPPORT, 4);
+
+	dphy->dphy_gen = dw_dphy_if_read_msk(dphy, DPHYID, DPHY_ID_GEN, 4);
+	dev_info(dev, "DPHY GEN %s with maximum %s lanes\n",
+			dphy->dphy_gen == GEN3 ? "3" : "2",
+			dphy->max_lanes == CTRL_8_LANES ? "8" : "4");
+
+	if (dphy->max_lanes == CTRL_8_LANES) {
+		res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+		dphy->dphy2_if_addr =
+			devm_ioremap(dev, res->start, resource_size(res));
+
+		if (IS_ERR(dphy->dphy2_if_addr)) {
+			dev_err(dev, "error requesting dphy 2 if regbank\n");
+			return PTR_ERR(dphy->dphy2_if_addr);
+		}
+
+		dphy->config_gpio = of_get_gpio(dev->of_node, 0);
+		if (!gpio_is_valid(dphy->config_gpio)) {
+			dev_err(dev, "failed to parse config gpio\n");
+			return dphy->config_gpio;
+		}
+	}
+
+	if (of_property_read_u32(dev->of_node,
+			"snps,dphy-frequency",
+			&dphy->dphy_freq)) {
+		dev_err(dev, "failed to find dphy frequency\n");
+		return -EINVAL;
+	}
+
+	if (of_property_read_u32(dev->of_node,
+			"snps,dphy-te-len",
+			&dphy->dphy_te_len)) {
+		dev_err(dev, "failed to find dphy te length\n");
+		return -EINVAL;
+	}
+
+	if (of_property_read_u32(dev->of_node,
+			"snps,compat-mode",
+			&dphy->compat_mode)) {
+		dev_err(dev, "failed to find compat mode\n");
+		return -EINVAL;
+	}
+
+	dev_set_drvdata(dev, dphy);
+	spin_lock_init(&dphy->slock);
+
+	phy = devm_phy_create(dev, NULL, &dw_dphy_ops);
+	if (IS_ERR(phy)) {
+		dev_err(dev, "failed to create PHY\n");
+		return PTR_ERR(phy);
+	}
+
+	dphy->phy = phy;
+	phy_set_drvdata(phy, dphy);
+
+	phy_provider = devm_of_phy_provider_register(dev, dw_dphy_xlate);
+	if (IS_ERR(phy_provider)) {
+		dev_err(dev, "error getting phy provider\n");
+		return PTR_ERR(phy_provider);
+	}
+
+	dphy->lp_time = 1000; /* 1000 ns */
+	dphy->lanes_config = dw_dphy_setup_config(dphy);
+	dev_dbg(dev, "rx-dphy created\n");
+
+	device_create_file(&pdev->dev, &dev_attr_dphy_reset);
+	device_create_file(&pdev->dev, &dev_attr_dphy_freq);
+	device_create_file(&pdev->dev, &dev_attr_dphy_addr);
+	device_create_file(&pdev->dev, &dev_attr_idelay);
+	device_create_file(&pdev->dev, &dev_attr_len_config);
+	device_create_file(&pdev->dev, &dev_attr_dw_dphy_g118_settle);
+
+	return 0;
+}
+
+static const struct of_device_id dw_dphy_rx_of_match[] = {
+	{ .compatible = "snps,dphy-rx" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, dw_dphy_rx_of_match);
+
+static struct platform_driver dw_dphy_rx_driver = {
+	.probe	= dw_dphy_rx_probe,
+	.driver = {

drivers/media/platform/dwc/dw-dphy-plat.c line 364

Quoted 10 lines
drivers/media/platform/dwc/dw-dphy-plat.c new file +10 -0
+		.of_match_table	= dw_dphy_rx_of_match,
+		.name  = "snps-dphy-rx",
+		.owner = THIS_MODULE,
+	}
+};
+module_platform_driver(dw_dphy_rx_driver);
+
+MODULE_DESCRIPTION("SNPS MIPI DPHY Rx driver");
+MODULE_AUTHOR("Luis Oliveira <[email protected]>");
+MODULE_LICENSE("GPL v2");

"GPL v2" means just that, while the SPDX header mentions GPL-2.0+. You can pick your license of choice (or rather your employer should, as it seems they own the copyright on the code based on the copyright header above), but SPDX and MODULE_LICENSE should match.

Quoted 782 lines
(quoted diff) +0 -0

b/drivers/media/platform/dwc/dw-dphy-rx.c new file mode 100644 index 0000000..4100ea5

drivers/media/platform/dwc/dw-dphy-rx.c new file +167 -0
@@ -0,0 +1,594 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Synopsys MIPI D-PHY driver
+ *
+ * Copyright (c) 2018 Synopsys, Inc. and/or its affiliates.
+ * Author: Luis Oliveira <[email protected]>
+ *
+ */
+
+#include "dw-dphy-rx.h"
+
+struct range_dphy_gen2 {
+	u64 freq;
+	u8 hsfregrange;
+};
+
+struct range_dphy_gen2 range_gen2[] = {
+	{  80, 0x00}, {  90, 0x10}, { 100, 0x20},
+	{ 110, 0x30}, { 120, 0x01}, { 130, 0x11},
+	{ 140, 0x21}, { 150, 0x31}, { 160, 0x02},
+	{ 170, 0x12}, { 180, 0x22}, { 190, 0x32},
+	{ 205, 0x03}, { 220, 0x13}, { 235, 0x23},
+	{ 250, 0x33}, { 275, 0x04}, { 300, 0x14},
+	{ 325, 0x05}, { 350, 0x15}, { 400, 0x25},
+	{ 450, 0x06}, { 500, 0x16}, { 550, 0x07},
+	{ 600, 0x17}, { 650, 0x08}, { 700, 0x18},
+	{ 750, 0x09}, { 800, 0x19}, { 850, 0x29},
+	{ 900, 0x39}, { 950, 0x0A}, {1000, 0x1A},
+	{1050, 0x2A}, {1100, 0x3A}, {1150, 0x0B},
+	{1200, 0x1B}, {1250, 0x2B}, {1300, 0x3B},
+	{1350, 0x0C}, {1400, 0x1C}, {1450, 0x2C},
+	{1500, 0x3C}, {1550, 0x0D}, {1600, 0x1D},
+	{1650, 0x2D}, {1700, 0x0E}, {1750, 0x1E},
+	{1800, 0x2E}, {1850, 0x3E}, {1900, 0x0F},
+	{1950, 0x1F}, {2000, 0x2F},
+};
+
+struct range_dphy_gen3 {
+	u64 freq;
+	u8 hsfregrange;
+	u32 osc_freq_target;
+};
+
+struct range_dphy_gen3 range_gen3[] = {
+
+	{  80, 0x00, 0x1B6}, {  90, 0x10, 0x1B6}, { 100, 0x20, 0x1B6},
+	{ 110, 0x30, 0x1B6}, { 120, 0x01, 0x1B6}, { 130, 0x11, 0x1B6},
+	{ 140, 0x21, 0x1B6}, { 150, 0x31, 0x1B6}, { 160, 0x02, 0x1B6},
+	{ 170, 0x12, 0x1B6}, { 180, 0x22, 0x1B6}, { 190, 0x32, 0x1B6},
+	{ 205, 0x03, 0x1B6}, { 220, 0x13, 0x1B6}, { 235, 0x23, 0x1B6},
+	{ 250, 0x33, 0x1B6}, { 275, 0x04, 0x1B6}, { 300, 0x14, 0x1B6},
+	{ 325, 0x25, 0x1B6}, { 350, 0x35, 0x1B6}, { 400, 0x05, 0x1B6},
+	{ 450, 0x16, 0x1B6}, { 500, 0x26, 0x1B6}, { 550, 0x37, 0x1B6},
+	{ 600, 0x07, 0x1B6}, { 650, 0x18, 0x1B6}, { 700, 0x28, 0x1B6},
+	{ 750, 0x39, 0x1B6}, { 800, 0x09, 0x1B6}, { 850, 0x19, 0x1B6},
+	{ 900, 0x29, 0x1B6}, { 950, 0x3A, 0x1B6}, {1000, 0x0A, 0x1B6},
+	{1050, 0x1A, 0x1B6}, {1100, 0x2A, 0x1B6}, {1150, 0x3B, 0x1B6},
+	{1200, 0x0B, 0x1B6}, {1250, 0x1B, 0x1B6}, {1300, 0x2B, 0x1B6},
+	{1350, 0x3C, 0x1B6}, {1400, 0x0C, 0x1B6}, {1450, 0x1C, 0x1B6},
+	{1500, 0x2C, 0x1B6}, {1550, 0x3D, 0x10F}, {1600, 0x0D, 0x118},
+	{1650, 0x1D, 0x121}, {1700, 0x2E, 0x12A}, {1750, 0x3E, 0x132},
+	{1800, 0x0E, 0x13B}, {1850, 0x1E, 0x144}, {1900, 0x2F, 0x14D},
+	{1950, 0x3F, 0x155}, {2000, 0x0F, 0x15E}, {2050, 0x40, 0x167},
+	{2100, 0x41, 0x170}, {2150, 0x42, 0x178}, {2200, 0x43, 0x181},
+	{2250, 0x44, 0x18A}, {2300, 0x45, 0x193}, {2350, 0x46, 0x19B},
+	{2400, 0x47, 0x1A4}, {2450, 0x48, 0x1AD}, {2500, 0x49, 0x1B6}
+};
+
+u8 dw_dphy_setup_config(struct dw_dphy_rx *dphy)
+{
+	u8 ret;
+	int setup_config;
+
+	if (dphy->max_lanes == CTRL_4_LANES)
+		return CTRL_4_LANES;
+
+	ret = gpio_request(dphy->config_gpio, "config");
+	if (ret < 0) {
+		pr_err("could not acquire config gpio (err=%d)\n", ret);
+		return ret;
+	}
+
+	setup_config = gpio_get_value(dphy->config_gpio);
+	pr_debug("CONFIG %s\n", setup_config == CTRL_8_LANES ? "8L" : "4+4L");
+	gpio_free(dphy->config_gpio);
+
+	return setup_config;
+}
+void dw_dphy_if_write(struct dw_dphy_rx *dphy, u32 address, u32 data)
+{
+	iowrite32(data, dphy->dphy1_if_addr + address);
+
+	if (dphy->lanes_config == CTRL_4_LANES)
+		return;
+
+	iowrite32(data, dphy->dphy2_if_addr + address);
+}
+
+u32 dw_dphy_if_read(struct dw_dphy_rx *dphy, u64 address)
+{
+	u32 if1 = 0, if2 = 0;
+
+	if1 = ioread32(dphy->dphy1_if_addr + address);
+
+	if (dphy->lanes_config == CTRL_4_LANES)
+		goto end;
+
+	if (dphy->lanes_config == DPHYID)
+		goto end;
+
+	if2 = ioread32(dphy->dphy2_if_addr + address);
+
+	if (if1 != if2)
+		pr_err("Values read different for each interface\n");
+
+end:
+	return if1;
+}
+
+void dw_dphy_write(struct dw_dphy_rx *dphy, u32 address, u32 data)
+{
+	iowrite32(data, dphy->base_address + address);
+
+	if (dphy->lanes_config == CTRL_4_LANES)
+		return;
+
+	if (address == R_CSI2_DPHY_TST_CTRL0)
+		iowrite32(data, dphy->base_address + R_CSI2_DPHY2_TST_CTRL0);
+	else if (address == R_CSI2_DPHY_TST_CTRL1)
+		iowrite32(data, dphy->base_address + R_CSI2_DPHY2_TST_CTRL1);
+}
+
+u32 dw_dphy_read(struct dw_dphy_rx *dphy, u64 address)
+{
+	u32 dphy1 = 0, dphy2 = 0;
+
+	dphy1 = ioread32(dphy->base_address + address);
+
+	if (dphy->lanes_config == CTRL_4_LANES)
+		goto end;
+
+	if (address == R_CSI2_DPHY_TST_CTRL0)
+		dphy2 = ioread32(dphy->base_address + R_CSI2_DPHY2_TST_CTRL0);
+	else if (address == R_CSI2_DPHY_TST_CTRL1)
+		dphy2 = ioread32(dphy->base_address + R_CSI2_DPHY2_TST_CTRL1);
+	else
+		return -ENODEV;
+
+	if (dphy1 != dphy2)
+		pr_err("Values read different for each dphy\n");
+
+end:
+	return dphy1;
+}
+
+void dw_dphy_write_msk(struct dw_dphy_rx *dev,
+		u64 address, u64 data, u8 shift, u8 width)
+{
+	u32 mask = (1 << width) - 1;
+	u32 temp = dw_dphy_read(dev, address);
+
+	temp &= ~(mask << shift);
+	temp |= (data & mask) << shift;
+	dw_dphy_write(dev, address, temp);
+}
+
+static void dw_dphy_te_12b_write(struct dw_dphy_rx *dphy, u16 addr, u8

data) +{

drivers/media/platform/dwc/dw-dphy-rx.c new file +426 -0
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 0, PHY_TESTEN, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLK, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 1, PHY_TESTEN, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLK, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 0x00, PHY_TESTDIN, 8);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLK, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 0, PHY_TESTEN, 1);
+	dw_dphy_write_msk(dphy,
+		R_CSI2_DPHY_TST_CTRL1, (u8) (addr >> 8), PHY_TESTDIN, 8);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLK, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLK, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 1, PHY_TESTEN, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLK, 1);
+	dw_dphy_write_msk(dphy,
+		R_CSI2_DPHY_TST_CTRL1, (u8) addr, PHY_TESTDIN, 8);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLK, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 0, PHY_TESTEN, 1);
+	dw_dphy_write_msk(dphy,
+		R_CSI2_DPHY_TST_CTRL1, (u8) data, PHY_TESTDIN, 8);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLK, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLK, 1);
+}
+
+static void dw_dphy_te_8b_write(struct dw_dphy_rx *dphy, u8 addr, u8 data)
+{
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLK, 1);
+	dw_dphy_write(dphy, R_CSI2_DPHY_TST_CTRL1, addr);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 1, PHY_TESTEN, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLK, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 0, PHY_TESTEN, 1);
+	dw_dphy_write(dphy, R_CSI2_DPHY_TST_CTRL1, data);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLK, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLK, 1);
+}
+
+static void dw_dphy_te_write(struct dw_dphy_rx *dphy, u16 addr, u8 data)
+{
+
+	if (dphy->dphy_te_len == BIT12)
+		dw_dphy_te_12b_write(dphy, addr, data);
+	else
+		dw_dphy_te_8b_write(dphy, addr, data);
+}
+
+static int dw_dphy_te_12b_read(struct dw_dphy_rx *dphy, u32 addr)
+{
+	u8 ret;
+
+	dw_dphy_write(dphy, R_CSI2_DPHY_SHUTDOWNZ, 0);
+	dw_dphy_write(dphy, R_CSI2_DPHY_RSTZ, 0);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLK, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 0, PHY_TESTEN, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 1, PHY_TESTEN, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLK, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 0x00, PHY_TESTDIN, 8);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLK, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 0, PHY_TESTEN, 1);
+	dw_dphy_write_msk(dphy,
+		R_CSI2_DPHY_TST_CTRL1, (u8) (addr >> 8), PHY_TESTDIN, 8);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLK, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLK, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 1, PHY_TESTEN, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLK, 1);
+	dw_dphy_write_msk(dphy,
+		R_CSI2_DPHY_TST_CTRL1, (u8) addr, PHY_TESTDIN, 8);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLK, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 0x00, 0, PHY_TESTDIN);
+	ret = dw_dphy_read_msk(dphy, R_CSI2_DPHY_TST_CTRL1, PHY_TESTDOUT, 8);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 0, PHY_TESTEN, 1);
+	dw_dphy_write(dphy, R_CSI2_DPHY_RSTZ, 1);
+	dw_dphy_write(dphy, R_CSI2_DPHY_SHUTDOWNZ, 1);
+
+	return ret;
+}
+
+static int dw_dphy_te_8b_read(struct dw_dphy_rx *dphy, u32 addr)
+{
+	u8 ret;
+
+	dw_dphy_write(dphy, R_CSI2_DPHY_SHUTDOWNZ, 0);
+	dw_dphy_write(dphy, R_CSI2_DPHY_RSTZ, 0);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 1, PHY_TESTEN, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLK, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, addr, PHY_TESTDIN, 8);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLK, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 0, PHY_TESTEN, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 0, PHY_TESTDIN, 8);
+	ret = dw_dphy_read_msk(dphy, R_CSI2_DPHY_TST_CTRL1, PHY_TESTDOUT, 8);
+	dw_dphy_write(dphy, R_CSI2_DPHY_RSTZ, 1);
+	dw_dphy_write(dphy, R_CSI2_DPHY_SHUTDOWNZ, 1);
+
+	return ret;
+}
+
+int dw_dphy_te_read(struct dw_dphy_rx *dphy, u32 addr)
+{
+	int ret;
+
+	if (dphy->dphy_te_len == BIT12)
+		ret = dw_dphy_te_12b_read(dphy, addr);
+	else
+		ret = dw_dphy_te_8b_read(dphy, addr);
+
+	return ret;
+}
+
+static void dw_dphy_if_init(struct dw_dphy_rx *dphy)
+{
+	dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, RESET);
+	dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, TX_PHY);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLR, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLR, 1);
+	dw_dphy_if_write(dphy, DPHYZCALCTRL, 0);
+	dw_dphy_if_write(dphy, DPHYZCALCTRL, 1);
+	dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, RESET);
+	dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, GLUELOGIC);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLR, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLR, 1);
+	dw_dphy_if_write(dphy, DPHYZCALCTRL, 0);
+	dw_dphy_if_write(dphy, DPHYZCALCTRL, 1);
+	dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, RESET);
+	dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, RX_PHY);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLR, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLR, 1);
+	dw_dphy_if_write(dphy, DPHYZCALCTRL, 0);
+	dw_dphy_if_write(dphy, DPHYZCALCTRL, 1);
+}
+
+static void dw_dphy_gen3_12bit_tc_power_up(struct dw_dphy_rx *dphy)
+{
+	dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, RESET);
+	dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, GLUELOGIC);
+	dw_dphy_te_write(dphy, CFGCLKFREQRANGE_TX, 0x1C);
+
+	/* CLKSEL | UPDATEPLL | SHADOW_CLEAR | SHADOW_CTRL | FORCEPLL */
+	dw_dphy_te_write(dphy, BYPASS, 0x3F);
+
+	/* IO_DS3 | IO_DS2 | IO_DS1 | IO_DS0 */
+	if (dphy->dphy_freq > 1500)
+		dw_dphy_te_write(dphy, IO_DS, 0x0F);
+
+	dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, RESET);
+	dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, RX_PHY);
+}
+
+static void dw_dphy_gen3_8bit_tc_power_up(struct dw_dphy_rx *dphy)
+{
+	u32 input_freq = dphy->dphy_freq / 1000;
+
+	dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, RESET);
+	dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, GLUELOGIC);
+	dw_dphy_te_write(dphy, CFGCLKFREQRANGE_RX, 0x1C);
+	dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, RESET);
+	dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, RX_PHY);
+	dw_dphy_te_write(dphy, OSC_FREQ_TARGET_RX0_MSB, 0x03);
+	dw_dphy_te_write(dphy, OSC_FREQ_TARGET_RX0_LSB, 0x02);
+	dw_dphy_te_write(dphy, OSC_FREQ_TARGET_RX1_MSB, 0x03);
+	dw_dphy_te_write(dphy, OSC_FREQ_TARGET_RX1_LSB, 0x02);
+	dw_dphy_te_write(dphy, OSC_FREQ_TARGET_RX2_MSB, 0x03);
+	dw_dphy_te_write(dphy, OSC_FREQ_TARGET_RX2_LSB, 0x02);
+	dw_dphy_te_write(dphy, OSC_FREQ_TARGET_RX3_MSB, 0x03);
+	dw_dphy_te_write(dphy, OSC_FREQ_TARGET_RX3_LSB, 0x02);
+	dw_dphy_te_write(dphy, BANDGAP_CTRL, 0x80);
+
+	if (input_freq < 2000)
+		dw_dphy_te_write(dphy, HS_RX_CTRL_LANE0, 0xC0);
+
+	if (input_freq < 1000) {
+		dw_dphy_te_write(dphy, HS_RX_CTRL_LANE1, 0xC0);
+		dw_dphy_te_write(dphy, HS_RX_CTRL_LANE2, 0xC0);
+		dw_dphy_te_write(dphy, HS_RX_CTRL_LANE3, 0xC0);
+	}
+}
+
+int dw_dphy_g118_settle(struct dw_dphy_rx *dphy)
+{
+	u32 input_freq, total_settle, settle_time, byte_clk, lp_time;
+
+	lp_time = dphy->lp_time;
+	input_freq = dphy->dphy_freq / 1000;
+
+	settle_time = (8 * (1000000/(input_freq))) + 115000;
+	byte_clk = (8000000/(input_freq));
+	total_settle = (settle_time + lp_time * 1000) / byte_clk;
+
+	if (total_settle > 0xFF)
+		total_settle = 0xFF;
+
+	return total_settle;
+}
+
+static void dw_dphy_pwr_down(struct dw_dphy_rx *dphy)
+{
+	dw_dphy_write(dphy, R_CSI2_DPHY_RSTZ, 0);
+
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLK, 1);
+	if (dphy->lanes_config == CTRL_8_LANES)
+		dw_dphy_write_msk(dphy,
+			R_CSI2_DPHY2_TST_CTRL0, 0, PHY_TESTCLK, 1);
+
+	dw_dphy_write(dphy, R_CSI2_DPHY_SHUTDOWNZ, 0);
+}
+
+static void dw_dphy_pwr_up(struct dw_dphy_rx *dphy)
+{
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLK, 1);
+	if (dphy->lanes_config == CTRL_8_LANES)
+		dw_dphy_write_msk(dphy,
+			R_CSI2_DPHY2_TST_CTRL0, 1, PHY_TESTCLK, 1);
+
+	dw_dphy_write(dphy, R_CSI2_DPHY_SHUTDOWNZ, 1);
+	dw_dphy_write(dphy, R_CSI2_DPHY_RSTZ, 1);
+}
+
+static int dw_dphy_gen3_12bit_configure(struct dw_dphy_rx *dphy)
+{
+	u32 input_freq = dphy->dphy_freq;
+	u8 range = 0;
+
+	pr_debug("PHY GEN 3 Freq: %u\n", input_freq);
+	for (range = 0; (range < ARRAY_SIZE(range_gen3) - 1) &&
+		((input_freq / 1000) > range_gen3[range].freq); range++)
+		;
+
+	dw_dphy_gen3_12bit_tc_power_up(dphy);
+
+	dw_dphy_te_write(dphy, RX_SYS_1, range_gen3[range].hsfregrange);
+	dw_dphy_te_write(dphy, RX_SYS_0, 0x20);
+	dw_dphy_te_write(dphy, RX_RX_STARTUP_OVR_2,
+			(u8) range_gen3[range].osc_freq_target);
+	dw_dphy_te_write(dphy, RX_RX_STARTUP_OVR_3,
+			(u8) (range_gen3[range].osc_freq_target >> 8));
+	dw_dphy_te_write(dphy, RX_RX_STARTUP_OVR_4, 0x01);
+
+	if (dphy->compat_mode) {
+		dw_dphy_te_write(dphy, RX_RX_STARTUP_OVR_1, 0x01);
+		dw_dphy_te_write(dphy, RX_RX_STARTUP_OVR_0, 0x80);
+	}
+
+	if ((dphy->compat_mode) || (input_freq <= 1500))
+		dw_dphy_te_write(dphy, RX_SYS_7, 0x38);
+
+	return 0;
+}
+
+static int dw_dphy_gen3_8bit_configure(struct dw_dphy_rx *dphy)
+{
+	u32 input_freq = dphy->dphy_freq;
+	u8 data;
+	u8 range = 0;
+
+	pr_debug("PHY GEN 3 Freq: %u\n", input_freq);
+	for (range = 0; (range < ARRAY_SIZE(range_gen3) - 1) &&
+		((input_freq / 1000) > range_gen3[range].freq); range++)
+		;
+
+	dw_dphy_te_write(dphy, RX_SKEW_CAL, dw_dphy_g118_settle(dphy));
+	data = 1<<7 | range_gen3[range].hsfregrange;
+	dw_dphy_te_write(dphy, HSFREQRANGE_8BIT, data);
+	dw_dphy_gen3_8bit_tc_power_up(dphy);
+
+	return 0;
+}
+
+static int dw_dphy_gen2_configure(struct dw_dphy_rx *dphy)
+{
+	u32 input_freq = dphy->dphy_freq;
+	u8 data;
+	u8 range = 0;
+
+	/* provide an initial active-high test clear pulse in TESTCLR  */
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLR, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLR, 1);
+
+	pr_debug("PHY GEN 2 Freq: %u\n", input_freq);
+	for (range = 0; (range < ARRAY_SIZE(range_gen2) - 1) &&
+		((input_freq / 1000) > range_gen2[range].freq); range++)
+		;
+
+	data = range_gen2[range].hsfregrange << 1;
+	dw_dphy_te_write(dphy, HSFREQRANGE_8BIT, data);
+
+	return 0;
+}
+
+static int dw_dphy_configure(struct dw_dphy_rx *dphy)
+{
+	dw_dphy_pwr_down(dphy);
+
+	if (dphy->dphy_gen == GEN3) {
+		dw_dphy_if_init(dphy);
+
+		if (dphy->dphy_te_len == BIT12)
+			dw_dphy_gen3_12bit_configure(dphy);
+		else
+			dw_dphy_gen3_8bit_configure(dphy);
+	} else
+		dw_dphy_gen2_configure(dphy);
+
+	dw_dphy_pwr_up(dphy);
+
+	return 0;
+}
+
+int dw_dphy_if_set_idelay(struct dw_dphy_rx *dphy, u8 dly, u8 cells)
+{
+	uint32_t val = 0;
+
+	dw_dphy_if_write(dphy, IDLYCFG, 0);
+
+	dw_dphy_if_write(dphy, IDLYSEL, cells);
+	dw_dphy_if_write(dphy, IDLYCNTINVAL, dly);
+
+	/* Pulse Value Set */
+	dw_dphy_if_write(dphy, IDLYCFG, 1);
+	usleep_range(10, 20);
+	dw_dphy_if_write(dphy, IDLYCFG, 0);
+
+	/* Pulse IDELAY CTRL Reset */
+	dw_dphy_if_write(dphy, DPHY1REGRSTN, 0);
+	usleep_range(10, 20);
+	dw_dphy_if_write(dphy, DPHY1REGRSTN, 1);
+
+	/* Get Value*/
+	val = dw_dphy_if_read(dphy, IDLYCNTOUTVAL);
+
+	if (val != dly) {
+		pr_info("odelay config failed, set %d get %d", dly, val);
+		return -1;
+	}
+
+	return 0;
+}
+
+int dw_dphy_if_get_idelay(struct dw_dphy_rx *dphy)
+{
+	return dw_dphy_if_read(dphy, IDLYCNTOUTVAL);
+}
+
+int dw_dphy_if_set_idelay_lane(struct dw_dphy_rx *dphy, u8 dly, u8 lane)
+{
+	int cell;
+
+	switch (lane) {
+	case 0:
+		for (cell = 3; cell <= 10; cell++)
+			dw_dphy_if_set_idelay(dphy, dly, cell);
+		break;
+	case 1:
+		for (cell = 14; cell <= 21; cell++)
+			dw_dphy_if_set_idelay(dphy, dly, cell);
+		break;
+	case 2:
+		for (cell = 24; cell <= 31; cell++)
+			dw_dphy_if_set_idelay(dphy, dly, cell);
+		break;
+	case 3:
+		for (cell = 34; cell <= 41; cell++)
+			dw_dphy_if_set_idelay(dphy, dly, cell);
+		break;
+	case 4: /* ALL */
+		dw_dphy_if_set_idelay(dphy, dly, 0x7F);
+		break;
+	default:
+		pr_err("Lane Value not recognized\n");
+		return -1;
+	}
+	return 0;
+}
+
+int dw_dphy_init(struct phy *phy)
+{
+	struct dw_dphy_rx *dphy = phy_get_drvdata(phy);
+
+	dw_dphy_write(dphy, R_CSI2_DPHY_RSTZ, 0);
+	dw_dphy_write(dphy, R_CSI2_DPHY_SHUTDOWNZ, 0);
+
+	return 0;
+}
+
+static int dw_dphy_set_phy_state(struct dw_dphy_rx *dphy, u32 on)
+{
+	u8 hs_freq;
+
+	dphy->lanes_config = dw_dphy_setup_config(dphy);
+
+	if (dphy->dphy_te_len == BIT12)
+		hs_freq = RX_SYS_1;
+	else
+		hs_freq = HSFREQRANGE_8BIT;
+
+	if (on) {
+		dw_dphy_configure(dphy);
+		pr_debug("HS Code: 0X%x\n", dw_dphy_te_read(dphy, hs_freq));
+	} else {
+		dw_dphy_write(dphy, R_CSI2_DPHY_SHUTDOWNZ, 0);
+		dw_dphy_write(dphy, R_CSI2_DPHY_RSTZ, 0);
+	}
+
+	return 0;
+}
+
+int dw_dphy_power_on(struct phy *phy)
+{
+	struct dw_dphy_rx *dphy = phy_get_drvdata(phy);
+
+	return dw_dphy_set_phy_state(dphy, 1);
+}
+
+int dw_dphy_power_off(struct phy *phy)
+{
+	struct dw_dphy_rx *dphy = phy_get_drvdata(phy);
+
+	return dw_dphy_set_phy_state(dphy, 0);
+}
+
+int dw_dphy_reset(struct phy *phy)
+{
+	struct dw_dphy_rx *dphy = phy_get_drvdata(phy);
+
+	dw_dphy_write(dphy, R_CSI2_DPHY_RSTZ, 0);
+	usleep_range(100, 200);
+	dw_dphy_write(dphy, R_CSI2_DPHY_RSTZ, 1);
+
+	return 0;
+}
(quoted diff) +0 -0

b/drivers/media/platform/dwc/dw-dphy-rx.h new file mode 100644 index 0000000..5375685

drivers/media/platform/dwc/dw-dphy-rx.h new file +172 -0
@@ -0,0 +1,176 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Synopsys MIPI D-PHY driver
+ *
+ * Copyright (c) 2018 Synopsys, Inc. and/or its affiliates.
+ * Author: Luis Oliveira <[email protected]>
+ */
+
+#ifndef __PHY_SNPS_DPHY_RX_H__
+#define __PHY_SNPS_DPHY_RX_H__
+
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_gpio.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+/* DPHY interface register bank*/
+#define R_CSI2_DPHY_SHUTDOWNZ 0x0
+#define R_CSI2_DPHY_RSTZ 0x4
+#define R_CSI2_DPHY_RX 0x8
+#define	R_CSI2_DPHY_STOPSTATE 0xC
+#define R_CSI2_DPHY_TST_CTRL0 0x10
+#define R_CSI2_DPHY_TST_CTRL1 0x14
+#define R_CSI2_DPHY2_TST_CTRL0 0x18
+#define R_CSI2_DPHY2_TST_CTRL1 0x1C
+
+enum dphy_id_mask {
+	DPHY_ID_LANE_SUPPORT = 0,
+	DPHY_ID_IF = 4,
+	DPHY_ID_GEN = 8,
+};
+
+enum dphy_gen_values {
+	GEN1 = 0,
+	GEN2 = 1,
+	GEN3 = 2,
+};
+
+enum dphy_interface_length {
+	BIT8 = 8,
+	BIT12 = 12,
+};
+
+enum tst_ctrl0 {
+	PHY_TESTCLR = 0,
+	PHY_TESTCLK = 1,
+};
+
+enum tst_ctrl1 {
+	PHY_TESTDIN = 0,
+	PHY_TESTDOUT = 8,
+	PHY_TESTEN = 16,
+};
+
+enum lanes_config_values {
+	CTRL_4_LANES = 0,
+	CTRL_8_LANES = 1,
+};
+
+enum dphy_tc {
+	CFGCLKFREQRANGE_TX = 0x02,
+	CFGCLKFREQRANGE_RX = 0x05,
+	BYPASS = 0x20,
+	IO_DS = 0x30,
+};
+
+enum dphy_8bit_interface_addr {
+	BANDGAP_CTRL = 0x24,
+	HS_RX_CTRL_LANE0 = 0x42,
+	HSFREQRANGE_8BIT = 0x44,
+	OSC_FREQ_TARGET_RX0_LSB	= 0x4e,
+	OSC_FREQ_TARGET_RX0_MSB	= 0x4f,
+	HS_RX_CTRL_LANE1 = 0x52,
+	OSC_FREQ_TARGET_RX1_LSB	= 0x5e,
+	OSC_FREQ_TARGET_RX1_MSB	= 0x5f,
+	RX_SKEW_CAL	= 0x7e,
+	HS_RX_CTRL_LANE2 = 0x82,
+	OSC_FREQ_TARGET_RX2_LSB	= 0x8e,
+	OSC_FREQ_TARGET_RX2_MSB	= 0x8f,
+	HS_RX_CTRL_LANE3 = 0x92,
+	OSC_FREQ_TARGET_RX3_LSB	= 0x9e,
+	OSC_FREQ_TARGET_RX3_MSB	= 0x9f,
+};
+
+enum dphy_12bit_interface_addr {
+	RX_SYS_0 = 0x01,
+	RX_SYS_1 = 0x02,
+	RX_SYS_7 = 0x08,
+	RX_RX_STARTUP_OVR_0 = 0xe0,
+	RX_RX_STARTUP_OVR_1 = 0xe1,
+	RX_RX_STARTUP_OVR_2 = 0xe2,
+	RX_RX_STARTUP_OVR_3 = 0xe3,
+	RX_RX_STARTUP_OVR_4 = 0xe4,
+};
+
+/* Gen3 interface register bank*/
+#define IDLYCFG	0x00
+#define IDLYSEL	0x04
+#define IDLYCNTINVAL 0x08
+#define IDLYCNTOUTVAL 0x0c
+#define DPHY1REGRSTN 0x10
+#define DPHYZCALSTAT 0x14
+#define DPHYZCALCTRL 0x18
+#define DPHYLANE0STAT 0x1c
+#define DPHYLANE1STAT 0x20
+#define DPHYLANE2STAT 0x24
+#define DPHYLANE3STAT 0x28
+#define DPHYCLKSTAT 0x2c
+#define DPHYZCLKCTRL 0x30
+#define TCGENPURPOSOUT 0x34
+#define TCGENPURPOSIN 0x38
+#define DPHYGENERICOUT 0x3c
+#define DPHYGENERICIN 0x40
+#define DPHYGLUEIFTESTER 0x44
+#define DPHYID 0x100
+
+enum glueiftester {
+	GLUELOGIC = 0x4,
+	RX_PHY = 0x2,
+	TX_PHY = 0x1,
+	RESET = 0x0,
+};
+
+struct dw_dphy_rx {
+	spinlock_t slock;
+	struct phy *phy;
+	uint32_t dphy_freq;
+	uint32_t dphy_gen;
+	uint32_t dphy_te_len;
+	uint32_t lanes_config;
+	uint32_t max_lanes;
+	uint32_t compat_mode;
+	uint32_t lp_time;
+
+	void __iomem *base_address; /* test interface */
+	void __iomem *dphy1_if_addr; /* gluelogic phy 1 */
+	void __iomem *dphy2_if_addr; /* gluelogic phy 2 */
+
+	int config_gpio;
+	uint8_t setup_config;
+};
+
+int dw_dphy_init(struct phy *phy);
+int dw_dphy_reset(struct phy *phy);
+int dw_dphy_power_off(struct phy *phy);
+int dw_dphy_power_on(struct phy *phy);
+
+u8 dw_dphy_setup_config(struct dw_dphy_rx *dphy);
+u32 dw_dphy_if_read(struct dw_dphy_rx *dphy, u64 address);
+void dw_dphy_write(struct dw_dphy_rx *dphy, u32 address, u32 data);
+u32 dw_dphy_read(struct dw_dphy_rx *dphy, u64 address);
+int dw_dphy_te_read(struct dw_dphy_rx *dphy, u32 addr);
+int dw_dphy_if_get_idelay(struct dw_dphy_rx *dphy);
+int dw_dphy_if_set_idelay_lane(struct dw_dphy_rx *dphy, u8 dly, u8 lane);
+
+static inline
+u32 dw_dphy_if_read_msk(struct dw_dphy_rx *dphy,
+		u32 address, u8 shift, u8 width)
+{
+	return (dw_dphy_if_read(dphy, address) >> shift) & ((1 << width) - 1);
+}
+
+static inline
+u32 dw_dphy_read_msk(struct dw_dphy_rx *dev, u32 address, u8 shift,  u8

width) +{

drivers/media/platform/dwc/dw-dphy-rx.h new file +3 -0
+	return (dw_dphy_read(dev, address) >> shift) & ((1 << width) - 1);
+}
+#endif /*__PHY_SNPS_DPHY_RX_H__*/
Signature · 3 lines
Regards,

Laurent Pinchart
#12 lore raw

Re: [V2, 3/5] media: platform: dwc: Add DW MIPI DPHY Rx platform

Quoted 3 lines · Laurent Pinchart

Hi Louis,

Thank you for the patch.

Hi Laurent,
My answers inline.

Quoted 12 lines · On Thursday, 20 September 2018 14:16:41 EEST Luis Oliveira wrote:
Quoted 10 lines

Add of Synopsys MIPI D-PHY in RX mode support.
Separated in the implementation are platform dependent probing functions.

---
Changelog
v2:
- fix uninitialization warning
- fix SDPX License to match: Documentation/process/license-rules.rst

Quoted 9 lines
Quoted 5 lines
 drivers/media/platform/dwc/Kconfig        |  32 ++
 drivers/media/platform/dwc/Makefile       |   2 +
 drivers/media/platform/dwc/dw-dphy-plat.c | 365 ++++++++++++++++++
 drivers/media/platform/dwc/dw-dphy-rx.c   | 594 +++++++++++++++++++++++++++
 drivers/media/platform/dwc/dw-dphy-rx.h   | 176 +++++++++

Is there really a need to split this in two source files ? It smells of OS abstraction layers.

The purpose of the division is to separate d-phy configuration from platform
dependent configuration. We will add PCI support in the future.

Quoted 7 lines
Quoted 4 lines
 5 files changed, 1169 insertions(+)
 create mode 100644 drivers/media/platform/dwc/dw-dphy-plat.c
 create mode 100644 drivers/media/platform/dwc/dw-dphy-rx.c
 create mode 100644 drivers/media/platform/dwc/dw-dphy-rx.h

As this is a PHY driver, shouldn't it be located in drivers/phy/ ?

You are right I suppose, I will move it.

Quoted 5 lines
Quoted 5 lines
(quoted diff) +0 -0

b/drivers/media/platform/dwc/Kconfig index e69de29..a573297 100644

drivers/media/platform/dwc/Kconfig +0 -0
@@ -0,0 +1,32 @@
Quoted 9 lines

drivers/media/platform/dwc/Kconfig line 6

Quoted 6 lines
drivers/media/platform/dwc/Kconfig +6 -0
+#
+#  Synopsys DWC Platform drivers
+#	Most drivers here are currently for MIPI CSI-2 and MIPI DPHY support
+
+config DWC_MIPI_CSI2_HOST
+	bool "Synopsys Designware CSI-2 Host Controller and DPHY-RX support"

There's no CSI-2 host controller in this patch.

This was initially a place holder for the

Quoted 8 lines

drivers/media/platform/dwc/Kconfig line 11

Quoted 5 lines
drivers/media/platform/dwc/Kconfig +5 -0
+	select VIDEO_DEV
+	select VIDEO_V4L2
+	select VIDEO_V4L2_SUBDEV_API
+	select VIDEOBUF2_VMALLOC
+	select VIDEOBUF2_DMA_CONTIG

All these don't seem to be needed.

Quoted 4 lines

drivers/media/platform/dwc/Kconfig line 13

Quoted 2 lines
drivers/media/platform/dwc/Kconfig +2 -0
+	select GENERIC_PHY
+	select VIDEO_OV5647

And I'm pretty sure you can use the PHY without an OV5647 sensor :-)

Yes. This is an error, I will fix it

Quoted 33 lines
Quoted 33 lines
drivers/media/platform/dwc/Kconfig +19 -0
+	help
+	 This selects the CSI-2 host controller support.
+
+	 If you have a controller with this interface, say Y.
+
+	  If unsure, say N.
+
+if DWC_MIPI_CSI2_HOST
+
+config DWC_MIPI_TC_DPHY_G128
+	tristate "DesignWare platform support using a G128 Test Chip"
+	depends on DWC_MIPI_CSI2_HOST
+	help
+	 Synopsys Test Chip is a MIPI D-PHY for prototyping purposes.
+
+	  If unsure, say N.
+
+endif # DWC_MIPI_CSI2_HOST
+
(quoted diff) +0 -0

b/drivers/media/platform/dwc/Makefile index e69de29..8be6f68 100644

drivers/media/platform/dwc/Makefile +2 -0
@@ -0,0 +1,2 @@
+obj-$(CONFIG_DWC_MIPI_TC_DPHY_G128)	+= dw-dphy-platfrm.o
+dw-dphy-platfrm-objs	:= dw-dphy-plat.o dw-dphy-rx.o
(quoted diff) +0 -0

b/drivers/media/platform/dwc/dw-dphy-plat.c new file mode 100644 index 0000000..8e298f5

drivers/media/platform/dwc/dw-dphy-plat.c new file +0 -0
@@ -0,0 +1,365 @@
Quoted 9 lines

drivers/media/platform/dwc/dw-dphy-plat.c line 5

Quoted 5 lines
drivers/media/platform/dwc/dw-dphy-plat.c new file +5 -0
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * dw-dphy-plat.c
+ *
+ * Copyright(c) 2018-present, Synopsys, Inc. and/or its affiliates.

I don't think that -present is right. The copyright notice should list the years explicitly.

Ok, I will check that

Quoted 223 lines
Quoted 223 lines
drivers/media/platform/dwc/dw-dphy-plat.c new file +223 -0
+ * Luis Oliveira <[email protected]>
+ *
+ */
+
+#include "dw-dphy-rx.h"
+
+static struct phy *dw_dphy_xlate(struct device *dev,
+			struct of_phandle_args *args)
+{
+	struct dw_dphy_rx *dphy = dev_get_drvdata(dev);
+
+	return dphy->phy;
+}
+
+static ssize_t dphy_reset_show(struct device *dev,
+			struct device_attribute *attr,
+			char *buf)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct dw_dphy_rx *dphy = platform_get_drvdata(pdev);
+	char buffer[15];
+
+	dw_dphy_write(dphy, R_CSI2_DPHY_RSTZ, 0);
+	usleep_range(100, 200);
+	dw_dphy_write(dphy, R_CSI2_DPHY_RSTZ, 1);
+
+	return strlcpy(buf, buffer, PAGE_SIZE);
+}
+
+static ssize_t dphy_freq_store(struct device *dev,
+			struct device_attribute *attr,
+			const char *buf,
+			size_t count)
+{
+	int ret;
+	unsigned long freq;
+
+	struct platform_device *pdev = to_platform_device(dev);
+	struct dw_dphy_rx *dphy = platform_get_drvdata(pdev);
+
+	ret = kstrtoul(buf, 10, &freq);
+	if (ret < 0)
+		return ret;
+
+	if (freq > 2500) {
+		dev_info(dev, "Freq must be under 2500 Mhz\n");
+		return count;
+	}
+	if (freq < 80) {
+		dev_info(dev, "Freq must be over 80 Mhz\n");
+		return count;
+	}
+
+	dev_info(dev, "Data Rate %lu Mbps\n", freq);
+	dphy->dphy_freq = freq * 1000;
+
+	return count;
+
+}
+
+static ssize_t dphy_freq_show(struct device *dev,
+			struct device_attribute *attr,
+			char *buf)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct dw_dphy_rx *dphy = platform_get_drvdata(pdev);
+	char buffer[15];
+
+	snprintf(buffer, 15, "Freq %d\n", dphy->dphy_freq / 1000);
+
+	return strlcpy(buf, buffer, PAGE_SIZE);
+}
+
+static ssize_t dphy_addr_store(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t count)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct dw_dphy_rx *dphy = platform_get_drvdata(pdev);
+	unsigned long val;
+	int ret;
+	u8 addr, payload;
+
+	ret = kstrtoul(buf, 32, &val);
+	if (ret < 0)
+		return ret;
+
+	payload = (u16)val;
+	addr = (u16)(val >> 16);
+
+	dev_info(dev, "addr 0x%lX\n", val);
+	dev_info(dev, "payload: 0x%X\n", addr);
+
+	dev_info(dev,
+		"Addr [0x%x] -> 0x%x\n", (unsigned int)addr,
+		dw_dphy_te_read(dphy, addr));
+
+	return count;
+}
+
+static ssize_t idelay_show(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct dw_dphy_rx *dphy = platform_get_drvdata(pdev);
+	char buffer[15];
+
+	snprintf(buffer, 15, "idelay %d\n", dw_dphy_if_get_idelay(dphy));
+
+	return strlcpy(buf, buffer, PAGE_SIZE);
+}
+
+static ssize_t idelay_store(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t count)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct dw_dphy_rx *dphy = platform_get_drvdata(pdev);
+	int ret;
+	unsigned long val;
+	u8 lane, delay;
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret < 0)
+		return ret;
+
+	lane = (u8)val;
+	delay = (u8)(val >> 8);
+
+	dev_dbg(dev, "Lanes %u\n", lane);
+	dev_dbg(dev, "Delay %u\n", delay);
+
+	dw_dphy_if_set_idelay_lane(dphy, delay, lane);
+
+	return count;
+}
+
+static ssize_t len_config_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	int ret;
+	unsigned long length;
+
+	struct platform_device *pdev = to_platform_device(dev);
+	struct dw_dphy_rx *dphy = platform_get_drvdata(pdev);
+
+	ret = kstrtoul(buf, 10, &length);
+	if (ret < 0)
+		return ret;
+
+	if (length == BIT8)
+		pr_info("Configured for 8-bit interface\n");
+	else if (length == BIT12)
+		pr_info("Configured for 12-bit interface\n");
+	else
+		return count;
+
+	dphy->dphy_te_len = length;
+
+	return count;
+
+}
+
+static ssize_t len_config_show(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct dw_dphy_rx *dphy = platform_get_drvdata(pdev);
+	char buffer[20];
+
+	snprintf(buffer, 20, "Length %d\n", dphy->dphy_te_len);
+
+	return strlcpy(buf, buffer, PAGE_SIZE);
+}
+
+static ssize_t dw_dphy_g118_settle_store(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t count)
+{
+	int ret;
+	unsigned long lp_time;
+
+	struct platform_device *pdev = to_platform_device(dev);
+	struct dw_dphy_rx *dphy = platform_get_drvdata(pdev);
+
+	ret = kstrtoul(buf, 10, &lp_time);
+	if (ret < 0)
+		return ret;
+
+	if ((lp_time > 1) && (lp_time < 10000))
+		dphy->lp_time = lp_time;
+	else {
+		pr_info("Invalid Value configuring for 1000 ns\n");
+		dphy->lp_time = 1000;
+	}
+
+	dphy->lp_time = lp_time;
+
+	return count;
+
+}
+
+static ssize_t dw_dphy_g118_settle_show(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct dw_dphy_rx *dphy = platform_get_drvdata(pdev);
+	char buffer[10];
+
+	snprintf(buffer, 10, "Settle %d ns\n", dphy->lp_time);
+
+	return strlcpy(buf, buffer, PAGE_SIZE);
+}
+
+static DEVICE_ATTR_RO(dphy_reset);
+static DEVICE_ATTR_RW(dphy_freq);
Quoted 9 lines

drivers/media/platform/dwc/dw-dphy-plat.c line 232

Quoted 4 lines
drivers/media/platform/dwc/dw-dphy-plat.c new file +4 -0
+static DEVICE_ATTR_WO(dphy_addr);
+static DEVICE_ATTR_RW(idelay);
+static DEVICE_ATTR_RW(len_config);
+static DEVICE_ATTR_RW(dw_dphy_g118_settle);

If you want to expose an API through sysfs it should be properly documented. I don't think this is needed though, the PHY should be completely transparent for userspace, it should be handled fully within the kernel.

We have clients that use this. This is very useful for debug but I will try to come up with a better solution.

Quoted 128 lines
Quoted 128 lines
drivers/media/platform/dwc/dw-dphy-plat.c new file +128 -0
+static struct phy_ops dw_dphy_ops = {
+	.init = dw_dphy_init,
+	.reset = dw_dphy_reset,
+	.power_on = dw_dphy_power_on,
+	.power_off = dw_dphy_power_off,
+	.owner = THIS_MODULE,
+};
+
+static int dw_dphy_rx_probe(struct platform_device *pdev)
+{
+	struct dw_dphy_rx *dphy;
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	struct phy_provider *phy_provider;
+	struct phy *phy;
+
+	dphy = devm_kzalloc(dev, sizeof(*dphy), GFP_KERNEL);
+	if (!dphy)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	dphy->base_address = devm_ioremap(dev, res->start, resource_size(res));
+	if (IS_ERR(dphy->base_address)) {
+		dev_err(dev, "error requesting base address\n");
+		return PTR_ERR(dphy->base_address);
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	dphy->dphy1_if_addr = devm_ioremap_resource(dev, res);
+	if (IS_ERR(dphy->dphy1_if_addr)) {
+		dev_err(dev, "error requesting dphy 1 if regbank\n");
+		return PTR_ERR(dphy->dphy1_if_addr);
+	}
+
+	dphy->max_lanes =
+		dw_dphy_if_read_msk(dphy, DPHYID, DPHY_ID_LANE_SUPPORT, 4);
+
+	dphy->dphy_gen = dw_dphy_if_read_msk(dphy, DPHYID, DPHY_ID_GEN, 4);
+	dev_info(dev, "DPHY GEN %s with maximum %s lanes\n",
+			dphy->dphy_gen == GEN3 ? "3" : "2",
+			dphy->max_lanes == CTRL_8_LANES ? "8" : "4");
+
+	if (dphy->max_lanes == CTRL_8_LANES) {
+		res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+		dphy->dphy2_if_addr =
+			devm_ioremap(dev, res->start, resource_size(res));
+
+		if (IS_ERR(dphy->dphy2_if_addr)) {
+			dev_err(dev, "error requesting dphy 2 if regbank\n");
+			return PTR_ERR(dphy->dphy2_if_addr);
+		}
+
+		dphy->config_gpio = of_get_gpio(dev->of_node, 0);
+		if (!gpio_is_valid(dphy->config_gpio)) {
+			dev_err(dev, "failed to parse config gpio\n");
+			return dphy->config_gpio;
+		}
+	}
+
+	if (of_property_read_u32(dev->of_node,
+			"snps,dphy-frequency",
+			&dphy->dphy_freq)) {
+		dev_err(dev, "failed to find dphy frequency\n");
+		return -EINVAL;
+	}
+
+	if (of_property_read_u32(dev->of_node,
+			"snps,dphy-te-len",
+			&dphy->dphy_te_len)) {
+		dev_err(dev, "failed to find dphy te length\n");
+		return -EINVAL;
+	}
+
+	if (of_property_read_u32(dev->of_node,
+			"snps,compat-mode",
+			&dphy->compat_mode)) {
+		dev_err(dev, "failed to find compat mode\n");
+		return -EINVAL;
+	}
+
+	dev_set_drvdata(dev, dphy);
+	spin_lock_init(&dphy->slock);
+
+	phy = devm_phy_create(dev, NULL, &dw_dphy_ops);
+	if (IS_ERR(phy)) {
+		dev_err(dev, "failed to create PHY\n");
+		return PTR_ERR(phy);
+	}
+
+	dphy->phy = phy;
+	phy_set_drvdata(phy, dphy);
+
+	phy_provider = devm_of_phy_provider_register(dev, dw_dphy_xlate);
+	if (IS_ERR(phy_provider)) {
+		dev_err(dev, "error getting phy provider\n");
+		return PTR_ERR(phy_provider);
+	}
+
+	dphy->lp_time = 1000; /* 1000 ns */
+	dphy->lanes_config = dw_dphy_setup_config(dphy);
+	dev_dbg(dev, "rx-dphy created\n");
+
+	device_create_file(&pdev->dev, &dev_attr_dphy_reset);
+	device_create_file(&pdev->dev, &dev_attr_dphy_freq);
+	device_create_file(&pdev->dev, &dev_attr_dphy_addr);
+	device_create_file(&pdev->dev, &dev_attr_idelay);
+	device_create_file(&pdev->dev, &dev_attr_len_config);
+	device_create_file(&pdev->dev, &dev_attr_dw_dphy_g118_settle);
+
+	return 0;
+}
+
+static const struct of_device_id dw_dphy_rx_of_match[] = {
+	{ .compatible = "snps,dphy-rx" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, dw_dphy_rx_of_match);
+
+static struct platform_driver dw_dphy_rx_driver = {
+	.probe	= dw_dphy_rx_probe,
+	.driver = {
+		.of_match_table	= dw_dphy_rx_of_match,
+		.name  = "snps-dphy-rx",
+		.owner = THIS_MODULE,
+	}
+};
+module_platform_driver(dw_dphy_rx_driver);
+
Quoted 9 lines

drivers/media/platform/dwc/dw-dphy-plat.c line 364

Quoted 3 lines
drivers/media/platform/dwc/dw-dphy-plat.c new file +3 -0
+MODULE_DESCRIPTION("SNPS MIPI DPHY Rx driver");
+MODULE_AUTHOR("Luis Oliveira <[email protected]>");
+MODULE_LICENSE("GPL v2");

"GPL v2" means just that, while the SPDX header mentions GPL-2.0+. You can pick your license of choice (or rather your employer should, as it seems they own the copyright on the code based on the copyright header above), but SPDX and MODULE_LICENSE should match.

Ok, thank you.

Quoted 784 lines
Quoted 782 lines
(quoted diff) +0 -0

b/drivers/media/platform/dwc/dw-dphy-rx.c new file mode 100644 index 0000000..4100ea5

drivers/media/platform/dwc/dw-dphy-rx.c new file +167 -0
@@ -0,0 +1,594 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Synopsys MIPI D-PHY driver
+ *
+ * Copyright (c) 2018 Synopsys, Inc. and/or its affiliates.
+ * Author: Luis Oliveira <[email protected]>
+ *
+ */
+
+#include "dw-dphy-rx.h"
+
+struct range_dphy_gen2 {
+	u64 freq;
+	u8 hsfregrange;
+};
+
+struct range_dphy_gen2 range_gen2[] = {
+	{  80, 0x00}, {  90, 0x10}, { 100, 0x20},
+	{ 110, 0x30}, { 120, 0x01}, { 130, 0x11},
+	{ 140, 0x21}, { 150, 0x31}, { 160, 0x02},
+	{ 170, 0x12}, { 180, 0x22}, { 190, 0x32},
+	{ 205, 0x03}, { 220, 0x13}, { 235, 0x23},
+	{ 250, 0x33}, { 275, 0x04}, { 300, 0x14},
+	{ 325, 0x05}, { 350, 0x15}, { 400, 0x25},
+	{ 450, 0x06}, { 500, 0x16}, { 550, 0x07},
+	{ 600, 0x17}, { 650, 0x08}, { 700, 0x18},
+	{ 750, 0x09}, { 800, 0x19}, { 850, 0x29},
+	{ 900, 0x39}, { 950, 0x0A}, {1000, 0x1A},
+	{1050, 0x2A}, {1100, 0x3A}, {1150, 0x0B},
+	{1200, 0x1B}, {1250, 0x2B}, {1300, 0x3B},
+	{1350, 0x0C}, {1400, 0x1C}, {1450, 0x2C},
+	{1500, 0x3C}, {1550, 0x0D}, {1600, 0x1D},
+	{1650, 0x2D}, {1700, 0x0E}, {1750, 0x1E},
+	{1800, 0x2E}, {1850, 0x3E}, {1900, 0x0F},
+	{1950, 0x1F}, {2000, 0x2F},
+};
+
+struct range_dphy_gen3 {
+	u64 freq;
+	u8 hsfregrange;
+	u32 osc_freq_target;
+};
+
+struct range_dphy_gen3 range_gen3[] = {
+
+	{  80, 0x00, 0x1B6}, {  90, 0x10, 0x1B6}, { 100, 0x20, 0x1B6},
+	{ 110, 0x30, 0x1B6}, { 120, 0x01, 0x1B6}, { 130, 0x11, 0x1B6},
+	{ 140, 0x21, 0x1B6}, { 150, 0x31, 0x1B6}, { 160, 0x02, 0x1B6},
+	{ 170, 0x12, 0x1B6}, { 180, 0x22, 0x1B6}, { 190, 0x32, 0x1B6},
+	{ 205, 0x03, 0x1B6}, { 220, 0x13, 0x1B6}, { 235, 0x23, 0x1B6},
+	{ 250, 0x33, 0x1B6}, { 275, 0x04, 0x1B6}, { 300, 0x14, 0x1B6},
+	{ 325, 0x25, 0x1B6}, { 350, 0x35, 0x1B6}, { 400, 0x05, 0x1B6},
+	{ 450, 0x16, 0x1B6}, { 500, 0x26, 0x1B6}, { 550, 0x37, 0x1B6},
+	{ 600, 0x07, 0x1B6}, { 650, 0x18, 0x1B6}, { 700, 0x28, 0x1B6},
+	{ 750, 0x39, 0x1B6}, { 800, 0x09, 0x1B6}, { 850, 0x19, 0x1B6},
+	{ 900, 0x29, 0x1B6}, { 950, 0x3A, 0x1B6}, {1000, 0x0A, 0x1B6},
+	{1050, 0x1A, 0x1B6}, {1100, 0x2A, 0x1B6}, {1150, 0x3B, 0x1B6},
+	{1200, 0x0B, 0x1B6}, {1250, 0x1B, 0x1B6}, {1300, 0x2B, 0x1B6},
+	{1350, 0x3C, 0x1B6}, {1400, 0x0C, 0x1B6}, {1450, 0x1C, 0x1B6},
+	{1500, 0x2C, 0x1B6}, {1550, 0x3D, 0x10F}, {1600, 0x0D, 0x118},
+	{1650, 0x1D, 0x121}, {1700, 0x2E, 0x12A}, {1750, 0x3E, 0x132},
+	{1800, 0x0E, 0x13B}, {1850, 0x1E, 0x144}, {1900, 0x2F, 0x14D},
+	{1950, 0x3F, 0x155}, {2000, 0x0F, 0x15E}, {2050, 0x40, 0x167},
+	{2100, 0x41, 0x170}, {2150, 0x42, 0x178}, {2200, 0x43, 0x181},
+	{2250, 0x44, 0x18A}, {2300, 0x45, 0x193}, {2350, 0x46, 0x19B},
+	{2400, 0x47, 0x1A4}, {2450, 0x48, 0x1AD}, {2500, 0x49, 0x1B6}
+};
+
+u8 dw_dphy_setup_config(struct dw_dphy_rx *dphy)
+{
+	u8 ret;
+	int setup_config;
+
+	if (dphy->max_lanes == CTRL_4_LANES)
+		return CTRL_4_LANES;
+
+	ret = gpio_request(dphy->config_gpio, "config");
+	if (ret < 0) {
+		pr_err("could not acquire config gpio (err=%d)\n", ret);
+		return ret;
+	}
+
+	setup_config = gpio_get_value(dphy->config_gpio);
+	pr_debug("CONFIG %s\n", setup_config == CTRL_8_LANES ? "8L" : "4+4L");
+	gpio_free(dphy->config_gpio);
+
+	return setup_config;
+}
+void dw_dphy_if_write(struct dw_dphy_rx *dphy, u32 address, u32 data)
+{
+	iowrite32(data, dphy->dphy1_if_addr + address);
+
+	if (dphy->lanes_config == CTRL_4_LANES)
+		return;
+
+	iowrite32(data, dphy->dphy2_if_addr + address);
+}
+
+u32 dw_dphy_if_read(struct dw_dphy_rx *dphy, u64 address)
+{
+	u32 if1 = 0, if2 = 0;
+
+	if1 = ioread32(dphy->dphy1_if_addr + address);
+
+	if (dphy->lanes_config == CTRL_4_LANES)
+		goto end;
+
+	if (dphy->lanes_config == DPHYID)
+		goto end;
+
+	if2 = ioread32(dphy->dphy2_if_addr + address);
+
+	if (if1 != if2)
+		pr_err("Values read different for each interface\n");
+
+end:
+	return if1;
+}
+
+void dw_dphy_write(struct dw_dphy_rx *dphy, u32 address, u32 data)
+{
+	iowrite32(data, dphy->base_address + address);
+
+	if (dphy->lanes_config == CTRL_4_LANES)
+		return;
+
+	if (address == R_CSI2_DPHY_TST_CTRL0)
+		iowrite32(data, dphy->base_address + R_CSI2_DPHY2_TST_CTRL0);
+	else if (address == R_CSI2_DPHY_TST_CTRL1)
+		iowrite32(data, dphy->base_address + R_CSI2_DPHY2_TST_CTRL1);
+}
+
+u32 dw_dphy_read(struct dw_dphy_rx *dphy, u64 address)
+{
+	u32 dphy1 = 0, dphy2 = 0;
+
+	dphy1 = ioread32(dphy->base_address + address);
+
+	if (dphy->lanes_config == CTRL_4_LANES)
+		goto end;
+
+	if (address == R_CSI2_DPHY_TST_CTRL0)
+		dphy2 = ioread32(dphy->base_address + R_CSI2_DPHY2_TST_CTRL0);
+	else if (address == R_CSI2_DPHY_TST_CTRL1)
+		dphy2 = ioread32(dphy->base_address + R_CSI2_DPHY2_TST_CTRL1);
+	else
+		return -ENODEV;
+
+	if (dphy1 != dphy2)
+		pr_err("Values read different for each dphy\n");
+
+end:
+	return dphy1;
+}
+
+void dw_dphy_write_msk(struct dw_dphy_rx *dev,
+		u64 address, u64 data, u8 shift, u8 width)
+{
+	u32 mask = (1 << width) - 1;
+	u32 temp = dw_dphy_read(dev, address);
+
+	temp &= ~(mask << shift);
+	temp |= (data & mask) << shift;
+	dw_dphy_write(dev, address, temp);
+}
+
+static void dw_dphy_te_12b_write(struct dw_dphy_rx *dphy, u16 addr, u8

data) +{

drivers/media/platform/dwc/dw-dphy-rx.c new file +426 -0
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 0, PHY_TESTEN, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLK, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 1, PHY_TESTEN, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLK, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 0x00, PHY_TESTDIN, 8);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLK, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 0, PHY_TESTEN, 1);
+	dw_dphy_write_msk(dphy,
+		R_CSI2_DPHY_TST_CTRL1, (u8) (addr >> 8), PHY_TESTDIN, 8);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLK, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLK, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 1, PHY_TESTEN, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLK, 1);
+	dw_dphy_write_msk(dphy,
+		R_CSI2_DPHY_TST_CTRL1, (u8) addr, PHY_TESTDIN, 8);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLK, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 0, PHY_TESTEN, 1);
+	dw_dphy_write_msk(dphy,
+		R_CSI2_DPHY_TST_CTRL1, (u8) data, PHY_TESTDIN, 8);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLK, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLK, 1);
+}
+
+static void dw_dphy_te_8b_write(struct dw_dphy_rx *dphy, u8 addr, u8 data)
+{
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLK, 1);
+	dw_dphy_write(dphy, R_CSI2_DPHY_TST_CTRL1, addr);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 1, PHY_TESTEN, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLK, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 0, PHY_TESTEN, 1);
+	dw_dphy_write(dphy, R_CSI2_DPHY_TST_CTRL1, data);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLK, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLK, 1);
+}
+
+static void dw_dphy_te_write(struct dw_dphy_rx *dphy, u16 addr, u8 data)
+{
+
+	if (dphy->dphy_te_len == BIT12)
+		dw_dphy_te_12b_write(dphy, addr, data);
+	else
+		dw_dphy_te_8b_write(dphy, addr, data);
+}
+
+static int dw_dphy_te_12b_read(struct dw_dphy_rx *dphy, u32 addr)
+{
+	u8 ret;
+
+	dw_dphy_write(dphy, R_CSI2_DPHY_SHUTDOWNZ, 0);
+	dw_dphy_write(dphy, R_CSI2_DPHY_RSTZ, 0);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLK, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 0, PHY_TESTEN, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 1, PHY_TESTEN, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLK, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 0x00, PHY_TESTDIN, 8);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLK, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 0, PHY_TESTEN, 1);
+	dw_dphy_write_msk(dphy,
+		R_CSI2_DPHY_TST_CTRL1, (u8) (addr >> 8), PHY_TESTDIN, 8);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLK, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLK, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 1, PHY_TESTEN, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLK, 1);
+	dw_dphy_write_msk(dphy,
+		R_CSI2_DPHY_TST_CTRL1, (u8) addr, PHY_TESTDIN, 8);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLK, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 0x00, 0, PHY_TESTDIN);
+	ret = dw_dphy_read_msk(dphy, R_CSI2_DPHY_TST_CTRL1, PHY_TESTDOUT, 8);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 0, PHY_TESTEN, 1);
+	dw_dphy_write(dphy, R_CSI2_DPHY_RSTZ, 1);
+	dw_dphy_write(dphy, R_CSI2_DPHY_SHUTDOWNZ, 1);
+
+	return ret;
+}
+
+static int dw_dphy_te_8b_read(struct dw_dphy_rx *dphy, u32 addr)
+{
+	u8 ret;
+
+	dw_dphy_write(dphy, R_CSI2_DPHY_SHUTDOWNZ, 0);
+	dw_dphy_write(dphy, R_CSI2_DPHY_RSTZ, 0);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 1, PHY_TESTEN, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLK, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, addr, PHY_TESTDIN, 8);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLK, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 0, PHY_TESTEN, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 0, PHY_TESTDIN, 8);
+	ret = dw_dphy_read_msk(dphy, R_CSI2_DPHY_TST_CTRL1, PHY_TESTDOUT, 8);
+	dw_dphy_write(dphy, R_CSI2_DPHY_RSTZ, 1);
+	dw_dphy_write(dphy, R_CSI2_DPHY_SHUTDOWNZ, 1);
+
+	return ret;
+}
+
+int dw_dphy_te_read(struct dw_dphy_rx *dphy, u32 addr)
+{
+	int ret;
+
+	if (dphy->dphy_te_len == BIT12)
+		ret = dw_dphy_te_12b_read(dphy, addr);
+	else
+		ret = dw_dphy_te_8b_read(dphy, addr);
+
+	return ret;
+}
+
+static void dw_dphy_if_init(struct dw_dphy_rx *dphy)
+{
+	dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, RESET);
+	dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, TX_PHY);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLR, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLR, 1);
+	dw_dphy_if_write(dphy, DPHYZCALCTRL, 0);
+	dw_dphy_if_write(dphy, DPHYZCALCTRL, 1);
+	dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, RESET);
+	dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, GLUELOGIC);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLR, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLR, 1);
+	dw_dphy_if_write(dphy, DPHYZCALCTRL, 0);
+	dw_dphy_if_write(dphy, DPHYZCALCTRL, 1);
+	dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, RESET);
+	dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, RX_PHY);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLR, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLR, 1);
+	dw_dphy_if_write(dphy, DPHYZCALCTRL, 0);
+	dw_dphy_if_write(dphy, DPHYZCALCTRL, 1);
+}
+
+static void dw_dphy_gen3_12bit_tc_power_up(struct dw_dphy_rx *dphy)
+{
+	dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, RESET);
+	dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, GLUELOGIC);
+	dw_dphy_te_write(dphy, CFGCLKFREQRANGE_TX, 0x1C);
+
+	/* CLKSEL | UPDATEPLL | SHADOW_CLEAR | SHADOW_CTRL | FORCEPLL */
+	dw_dphy_te_write(dphy, BYPASS, 0x3F);
+
+	/* IO_DS3 | IO_DS2 | IO_DS1 | IO_DS0 */
+	if (dphy->dphy_freq > 1500)
+		dw_dphy_te_write(dphy, IO_DS, 0x0F);
+
+	dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, RESET);
+	dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, RX_PHY);
+}
+
+static void dw_dphy_gen3_8bit_tc_power_up(struct dw_dphy_rx *dphy)
+{
+	u32 input_freq = dphy->dphy_freq / 1000;
+
+	dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, RESET);
+	dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, GLUELOGIC);
+	dw_dphy_te_write(dphy, CFGCLKFREQRANGE_RX, 0x1C);
+	dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, RESET);
+	dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, RX_PHY);
+	dw_dphy_te_write(dphy, OSC_FREQ_TARGET_RX0_MSB, 0x03);
+	dw_dphy_te_write(dphy, OSC_FREQ_TARGET_RX0_LSB, 0x02);
+	dw_dphy_te_write(dphy, OSC_FREQ_TARGET_RX1_MSB, 0x03);
+	dw_dphy_te_write(dphy, OSC_FREQ_TARGET_RX1_LSB, 0x02);
+	dw_dphy_te_write(dphy, OSC_FREQ_TARGET_RX2_MSB, 0x03);
+	dw_dphy_te_write(dphy, OSC_FREQ_TARGET_RX2_LSB, 0x02);
+	dw_dphy_te_write(dphy, OSC_FREQ_TARGET_RX3_MSB, 0x03);
+	dw_dphy_te_write(dphy, OSC_FREQ_TARGET_RX3_LSB, 0x02);
+	dw_dphy_te_write(dphy, BANDGAP_CTRL, 0x80);
+
+	if (input_freq < 2000)
+		dw_dphy_te_write(dphy, HS_RX_CTRL_LANE0, 0xC0);
+
+	if (input_freq < 1000) {
+		dw_dphy_te_write(dphy, HS_RX_CTRL_LANE1, 0xC0);
+		dw_dphy_te_write(dphy, HS_RX_CTRL_LANE2, 0xC0);
+		dw_dphy_te_write(dphy, HS_RX_CTRL_LANE3, 0xC0);
+	}
+}
+
+int dw_dphy_g118_settle(struct dw_dphy_rx *dphy)
+{
+	u32 input_freq, total_settle, settle_time, byte_clk, lp_time;
+
+	lp_time = dphy->lp_time;
+	input_freq = dphy->dphy_freq / 1000;
+
+	settle_time = (8 * (1000000/(input_freq))) + 115000;
+	byte_clk = (8000000/(input_freq));
+	total_settle = (settle_time + lp_time * 1000) / byte_clk;
+
+	if (total_settle > 0xFF)
+		total_settle = 0xFF;
+
+	return total_settle;
+}
+
+static void dw_dphy_pwr_down(struct dw_dphy_rx *dphy)
+{
+	dw_dphy_write(dphy, R_CSI2_DPHY_RSTZ, 0);
+
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLK, 1);
+	if (dphy->lanes_config == CTRL_8_LANES)
+		dw_dphy_write_msk(dphy,
+			R_CSI2_DPHY2_TST_CTRL0, 0, PHY_TESTCLK, 1);
+
+	dw_dphy_write(dphy, R_CSI2_DPHY_SHUTDOWNZ, 0);
+}
+
+static void dw_dphy_pwr_up(struct dw_dphy_rx *dphy)
+{
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLK, 1);
+	if (dphy->lanes_config == CTRL_8_LANES)
+		dw_dphy_write_msk(dphy,
+			R_CSI2_DPHY2_TST_CTRL0, 1, PHY_TESTCLK, 1);
+
+	dw_dphy_write(dphy, R_CSI2_DPHY_SHUTDOWNZ, 1);
+	dw_dphy_write(dphy, R_CSI2_DPHY_RSTZ, 1);
+}
+
+static int dw_dphy_gen3_12bit_configure(struct dw_dphy_rx *dphy)
+{
+	u32 input_freq = dphy->dphy_freq;
+	u8 range = 0;
+
+	pr_debug("PHY GEN 3 Freq: %u\n", input_freq);
+	for (range = 0; (range < ARRAY_SIZE(range_gen3) - 1) &&
+		((input_freq / 1000) > range_gen3[range].freq); range++)
+		;
+
+	dw_dphy_gen3_12bit_tc_power_up(dphy);
+
+	dw_dphy_te_write(dphy, RX_SYS_1, range_gen3[range].hsfregrange);
+	dw_dphy_te_write(dphy, RX_SYS_0, 0x20);
+	dw_dphy_te_write(dphy, RX_RX_STARTUP_OVR_2,
+			(u8) range_gen3[range].osc_freq_target);
+	dw_dphy_te_write(dphy, RX_RX_STARTUP_OVR_3,
+			(u8) (range_gen3[range].osc_freq_target >> 8));
+	dw_dphy_te_write(dphy, RX_RX_STARTUP_OVR_4, 0x01);
+
+	if (dphy->compat_mode) {
+		dw_dphy_te_write(dphy, RX_RX_STARTUP_OVR_1, 0x01);
+		dw_dphy_te_write(dphy, RX_RX_STARTUP_OVR_0, 0x80);
+	}
+
+	if ((dphy->compat_mode) || (input_freq <= 1500))
+		dw_dphy_te_write(dphy, RX_SYS_7, 0x38);
+
+	return 0;
+}
+
+static int dw_dphy_gen3_8bit_configure(struct dw_dphy_rx *dphy)
+{
+	u32 input_freq = dphy->dphy_freq;
+	u8 data;
+	u8 range = 0;
+
+	pr_debug("PHY GEN 3 Freq: %u\n", input_freq);
+	for (range = 0; (range < ARRAY_SIZE(range_gen3) - 1) &&
+		((input_freq / 1000) > range_gen3[range].freq); range++)
+		;
+
+	dw_dphy_te_write(dphy, RX_SKEW_CAL, dw_dphy_g118_settle(dphy));
+	data = 1<<7 | range_gen3[range].hsfregrange;
+	dw_dphy_te_write(dphy, HSFREQRANGE_8BIT, data);
+	dw_dphy_gen3_8bit_tc_power_up(dphy);
+
+	return 0;
+}
+
+static int dw_dphy_gen2_configure(struct dw_dphy_rx *dphy)
+{
+	u32 input_freq = dphy->dphy_freq;
+	u8 data;
+	u8 range = 0;
+
+	/* provide an initial active-high test clear pulse in TESTCLR  */
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLR, 1);
+	dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLR, 1);
+
+	pr_debug("PHY GEN 2 Freq: %u\n", input_freq);
+	for (range = 0; (range < ARRAY_SIZE(range_gen2) - 1) &&
+		((input_freq / 1000) > range_gen2[range].freq); range++)
+		;
+
+	data = range_gen2[range].hsfregrange << 1;
+	dw_dphy_te_write(dphy, HSFREQRANGE_8BIT, data);
+
+	return 0;
+}
+
+static int dw_dphy_configure(struct dw_dphy_rx *dphy)
+{
+	dw_dphy_pwr_down(dphy);
+
+	if (dphy->dphy_gen == GEN3) {
+		dw_dphy_if_init(dphy);
+
+		if (dphy->dphy_te_len == BIT12)
+			dw_dphy_gen3_12bit_configure(dphy);
+		else
+			dw_dphy_gen3_8bit_configure(dphy);
+	} else
+		dw_dphy_gen2_configure(dphy);
+
+	dw_dphy_pwr_up(dphy);
+
+	return 0;
+}
+
+int dw_dphy_if_set_idelay(struct dw_dphy_rx *dphy, u8 dly, u8 cells)
+{
+	uint32_t val = 0;
+
+	dw_dphy_if_write(dphy, IDLYCFG, 0);
+
+	dw_dphy_if_write(dphy, IDLYSEL, cells);
+	dw_dphy_if_write(dphy, IDLYCNTINVAL, dly);
+
+	/* Pulse Value Set */
+	dw_dphy_if_write(dphy, IDLYCFG, 1);
+	usleep_range(10, 20);
+	dw_dphy_if_write(dphy, IDLYCFG, 0);
+
+	/* Pulse IDELAY CTRL Reset */
+	dw_dphy_if_write(dphy, DPHY1REGRSTN, 0);
+	usleep_range(10, 20);
+	dw_dphy_if_write(dphy, DPHY1REGRSTN, 1);
+
+	/* Get Value*/
+	val = dw_dphy_if_read(dphy, IDLYCNTOUTVAL);
+
+	if (val != dly) {
+		pr_info("odelay config failed, set %d get %d", dly, val);
+		return -1;
+	}
+
+	return 0;
+}
+
+int dw_dphy_if_get_idelay(struct dw_dphy_rx *dphy)
+{
+	return dw_dphy_if_read(dphy, IDLYCNTOUTVAL);
+}
+
+int dw_dphy_if_set_idelay_lane(struct dw_dphy_rx *dphy, u8 dly, u8 lane)
+{
+	int cell;
+
+	switch (lane) {
+	case 0:
+		for (cell = 3; cell <= 10; cell++)
+			dw_dphy_if_set_idelay(dphy, dly, cell);
+		break;
+	case 1:
+		for (cell = 14; cell <= 21; cell++)
+			dw_dphy_if_set_idelay(dphy, dly, cell);
+		break;
+	case 2:
+		for (cell = 24; cell <= 31; cell++)
+			dw_dphy_if_set_idelay(dphy, dly, cell);
+		break;
+	case 3:
+		for (cell = 34; cell <= 41; cell++)
+			dw_dphy_if_set_idelay(dphy, dly, cell);
+		break;
+	case 4: /* ALL */
+		dw_dphy_if_set_idelay(dphy, dly, 0x7F);
+		break;
+	default:
+		pr_err("Lane Value not recognized\n");
+		return -1;
+	}
+	return 0;
+}
+
+int dw_dphy_init(struct phy *phy)
+{
+	struct dw_dphy_rx *dphy = phy_get_drvdata(phy);
+
+	dw_dphy_write(dphy, R_CSI2_DPHY_RSTZ, 0);
+	dw_dphy_write(dphy, R_CSI2_DPHY_SHUTDOWNZ, 0);
+
+	return 0;
+}
+
+static int dw_dphy_set_phy_state(struct dw_dphy_rx *dphy, u32 on)
+{
+	u8 hs_freq;
+
+	dphy->lanes_config = dw_dphy_setup_config(dphy);
+
+	if (dphy->dphy_te_len == BIT12)
+		hs_freq = RX_SYS_1;
+	else
+		hs_freq = HSFREQRANGE_8BIT;
+
+	if (on) {
+		dw_dphy_configure(dphy);
+		pr_debug("HS Code: 0X%x\n", dw_dphy_te_read(dphy, hs_freq));
+	} else {
+		dw_dphy_write(dphy, R_CSI2_DPHY_SHUTDOWNZ, 0);
+		dw_dphy_write(dphy, R_CSI2_DPHY_RSTZ, 0);
+	}
+
+	return 0;
+}
+
+int dw_dphy_power_on(struct phy *phy)
+{
+	struct dw_dphy_rx *dphy = phy_get_drvdata(phy);
+
+	return dw_dphy_set_phy_state(dphy, 1);
+}
+
+int dw_dphy_power_off(struct phy *phy)
+{
+	struct dw_dphy_rx *dphy = phy_get_drvdata(phy);
+
+	return dw_dphy_set_phy_state(dphy, 0);
+}
+
+int dw_dphy_reset(struct phy *phy)
+{
+	struct dw_dphy_rx *dphy = phy_get_drvdata(phy);
+
+	dw_dphy_write(dphy, R_CSI2_DPHY_RSTZ, 0);
+	usleep_range(100, 200);
+	dw_dphy_write(dphy, R_CSI2_DPHY_RSTZ, 1);
+
+	return 0;
+}
(quoted diff) +0 -0

b/drivers/media/platform/dwc/dw-dphy-rx.h new file mode 100644 index 0000000..5375685

drivers/media/platform/dwc/dw-dphy-rx.h new file +172 -0
@@ -0,0 +1,176 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Synopsys MIPI D-PHY driver
+ *
+ * Copyright (c) 2018 Synopsys, Inc. and/or its affiliates.
+ * Author: Luis Oliveira <[email protected]>
+ */
+
+#ifndef __PHY_SNPS_DPHY_RX_H__
+#define __PHY_SNPS_DPHY_RX_H__
+
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_gpio.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+/* DPHY interface register bank*/
+#define R_CSI2_DPHY_SHUTDOWNZ 0x0
+#define R_CSI2_DPHY_RSTZ 0x4
+#define R_CSI2_DPHY_RX 0x8
+#define	R_CSI2_DPHY_STOPSTATE 0xC
+#define R_CSI2_DPHY_TST_CTRL0 0x10
+#define R_CSI2_DPHY_TST_CTRL1 0x14
+#define R_CSI2_DPHY2_TST_CTRL0 0x18
+#define R_CSI2_DPHY2_TST_CTRL1 0x1C
+
+enum dphy_id_mask {
+	DPHY_ID_LANE_SUPPORT = 0,
+	DPHY_ID_IF = 4,
+	DPHY_ID_GEN = 8,
+};
+
+enum dphy_gen_values {
+	GEN1 = 0,
+	GEN2 = 1,
+	GEN3 = 2,
+};
+
+enum dphy_interface_length {
+	BIT8 = 8,
+	BIT12 = 12,
+};
+
+enum tst_ctrl0 {
+	PHY_TESTCLR = 0,
+	PHY_TESTCLK = 1,
+};
+
+enum tst_ctrl1 {
+	PHY_TESTDIN = 0,
+	PHY_TESTDOUT = 8,
+	PHY_TESTEN = 16,
+};
+
+enum lanes_config_values {
+	CTRL_4_LANES = 0,
+	CTRL_8_LANES = 1,
+};
+
+enum dphy_tc {
+	CFGCLKFREQRANGE_TX = 0x02,
+	CFGCLKFREQRANGE_RX = 0x05,
+	BYPASS = 0x20,
+	IO_DS = 0x30,
+};
+
+enum dphy_8bit_interface_addr {
+	BANDGAP_CTRL = 0x24,
+	HS_RX_CTRL_LANE0 = 0x42,
+	HSFREQRANGE_8BIT = 0x44,
+	OSC_FREQ_TARGET_RX0_LSB	= 0x4e,
+	OSC_FREQ_TARGET_RX0_MSB	= 0x4f,
+	HS_RX_CTRL_LANE1 = 0x52,
+	OSC_FREQ_TARGET_RX1_LSB	= 0x5e,
+	OSC_FREQ_TARGET_RX1_MSB	= 0x5f,
+	RX_SKEW_CAL	= 0x7e,
+	HS_RX_CTRL_LANE2 = 0x82,
+	OSC_FREQ_TARGET_RX2_LSB	= 0x8e,
+	OSC_FREQ_TARGET_RX2_MSB	= 0x8f,
+	HS_RX_CTRL_LANE3 = 0x92,
+	OSC_FREQ_TARGET_RX3_LSB	= 0x9e,
+	OSC_FREQ_TARGET_RX3_MSB	= 0x9f,
+};
+
+enum dphy_12bit_interface_addr {
+	RX_SYS_0 = 0x01,
+	RX_SYS_1 = 0x02,
+	RX_SYS_7 = 0x08,
+	RX_RX_STARTUP_OVR_0 = 0xe0,
+	RX_RX_STARTUP_OVR_1 = 0xe1,
+	RX_RX_STARTUP_OVR_2 = 0xe2,
+	RX_RX_STARTUP_OVR_3 = 0xe3,
+	RX_RX_STARTUP_OVR_4 = 0xe4,
+};
+
+/* Gen3 interface register bank*/
+#define IDLYCFG	0x00
+#define IDLYSEL	0x04
+#define IDLYCNTINVAL 0x08
+#define IDLYCNTOUTVAL 0x0c
+#define DPHY1REGRSTN 0x10
+#define DPHYZCALSTAT 0x14
+#define DPHYZCALCTRL 0x18
+#define DPHYLANE0STAT 0x1c
+#define DPHYLANE1STAT 0x20
+#define DPHYLANE2STAT 0x24
+#define DPHYLANE3STAT 0x28
+#define DPHYCLKSTAT 0x2c
+#define DPHYZCLKCTRL 0x30
+#define TCGENPURPOSOUT 0x34
+#define TCGENPURPOSIN 0x38
+#define DPHYGENERICOUT 0x3c
+#define DPHYGENERICIN 0x40
+#define DPHYGLUEIFTESTER 0x44
+#define DPHYID 0x100
+
+enum glueiftester {
+	GLUELOGIC = 0x4,
+	RX_PHY = 0x2,
+	TX_PHY = 0x1,
+	RESET = 0x0,
+};
+
+struct dw_dphy_rx {
+	spinlock_t slock;
+	struct phy *phy;
+	uint32_t dphy_freq;
+	uint32_t dphy_gen;
+	uint32_t dphy_te_len;
+	uint32_t lanes_config;
+	uint32_t max_lanes;
+	uint32_t compat_mode;
+	uint32_t lp_time;
+
+	void __iomem *base_address; /* test interface */
+	void __iomem *dphy1_if_addr; /* gluelogic phy 1 */
+	void __iomem *dphy2_if_addr; /* gluelogic phy 2 */
+
+	int config_gpio;
+	uint8_t setup_config;
+};
+
+int dw_dphy_init(struct phy *phy);
+int dw_dphy_reset(struct phy *phy);
+int dw_dphy_power_off(struct phy *phy);
+int dw_dphy_power_on(struct phy *phy);
+
+u8 dw_dphy_setup_config(struct dw_dphy_rx *dphy);
+u32 dw_dphy_if_read(struct dw_dphy_rx *dphy, u64 address);
+void dw_dphy_write(struct dw_dphy_rx *dphy, u32 address, u32 data);
+u32 dw_dphy_read(struct dw_dphy_rx *dphy, u64 address);
+int dw_dphy_te_read(struct dw_dphy_rx *dphy, u32 addr);
+int dw_dphy_if_get_idelay(struct dw_dphy_rx *dphy);
+int dw_dphy_if_set_idelay_lane(struct dw_dphy_rx *dphy, u8 dly, u8 lane);
+
+static inline
+u32 dw_dphy_if_read_msk(struct dw_dphy_rx *dphy,
+		u32 address, u8 shift, u8 width)
+{
+	return (dw_dphy_if_read(dphy, address) >> shift) & ((1 << width) - 1);
+}
+
+static inline
+u32 dw_dphy_read_msk(struct dw_dphy_rx *dev, u32 address, u8 shift,  u8

width) +{

drivers/media/platform/dwc/dw-dphy-rx.h new file +3 -0
+	return (dw_dphy_read(dev, address) >> shift) & ((1 << width) - 1);
+}
+#endif /*__PHY_SNPS_DPHY_RX_H__*/
#13 lore raw

PATCH v2 4/5 Documentation: dt-bindings: Document bindings for DW MIPI CSI-2 Host

In reply to Luis Oliveira

Add bindings for Synopsys DesignWare MIPI CSI-2 host.

---
Changelog
v2:
- no changes

 .../devicetree/bindings/media/snps,dw-csi-plat.txt | 74 ++++++++++++++++++++++
 1 file changed, 74 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/snps,dw-csi-plat.txt
Documentation/devicetree/bindings/media/snps,dw-csi-plat.txt new file +74 -0
new file mode 100644
index 0000000..028f5eb
@@ -0,0 +1,74 @@
+Synopsys DesignWare CSI-2 Host controller
+
+Description
+-----------
+
+This HW block is used to receive image coming from an MIPI CSI-2 compatible
+camera.
+
+Required properties:
+- compatible: shall be "snps,dw-csi-plat"
+- reg			: physical base address and size of the device memory mapped
+  registers;
+- interrupts		: CSI-2 Host interrupt
+- snps,output-type	: Core output to be used (IPI-> 0 or IDI->1 or BOTH->2) These
+			  values choose which of the Core outputs will be used, it
+			  can be Image Data Interface or Image Pixel Interface.
+- phys			: List of one PHY specifier (as defined in
+			  Documentation/devicetree/bindings/phy/phy-bindings.txt).
+			  This PHY is a MIPI DPHY working in RX mode.
+- resets		: Reference to a reset controller (optional)
+
+Optional properties(if in IPI mode):
+- snps,ipi-mode 	: Mode to be used when in IPI(Camera -> 0 or Controller -> 1)
+			  This property defines if the controller will use the video
+			  timings available
+			  in the video stream or if it will use pre-defined ones.
+- snps,ipi-color-mode	: Bus depth to be used in IPI (48 bits -> 0 or 16 bits -> 1)
+			  This property defines the width of the IPI bus.
+- snps,ipi-auto-flush	: Data auto-flush (1 -> Yes or 0 -> No). This property defines
+			  if the data is automatically flushed in each vsync or if
+			  this process is done manually
+- snps,virtual-channel	: Virtual channel where data is present when in IPI mode. This
+			  property chooses the virtual channel which IPI will use to
+			  retrieve the video stream.
+
+The per-board settings:
+ - port sub-node describing a single endpoint connected to the camera as
+   described in video-interfaces.txt[1].
+
+Example:
+
+	csi2_1: csi2@3000 {
+		compatible = "snps,dw-csi-plat";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg = < 0x03000 0x7FF>;
+		interrupts = <2>;
+		output-type = <2>;
+		resets = <&dw_rst 1>;
+		phys = <&mipi_dphy_rx1 0>;
+		phy-names = "csi2-dphy";
+
+		/* IPI optional Configurations */
+		snps,ipi-mode = <0>;
+		snps,ipi-color-mode = <0>;
+		snps,ipi-auto-flush = <1>;
+		snps,virtual-channel = <0>;
+
+		/* CSI-2 per-board settings */
+		port@1 {
+			reg = <1>;
+			csi1_ep1: endpoint {
+				remote-endpoint = <&camera_1>;
+				data-lanes = <1 2>;
+			};
+		};
+		port@2 {
+			csi1_ep2: endpoint {
+				remote-endpoint = <&vif1_ep>;
+			};
+		};
+	};
+
+
Signature · 1 line
2.9.3
#14 lore raw

Re: [V2, 4/5] Documentation: dt-bindings: Document bindings for DW MIPI CSI-2 Host

Hi Luis,

Thank you for the patch.

Quoted 47 lines · On Thursday, 20 September 2018 14:16:42 EEST Luis Oliveira wrote:

Add bindings for Synopsys DesignWare MIPI CSI-2 host.

---
Changelog
v2:
- no changes

 .../devicetree/bindings/media/snps,dw-csi-plat.txt | 74 +++++++++++++++++++
 1 file changed, 74 insertions(+)
 create mode 100644

Documentation/devicetree/bindings/media/snps,dw-csi-plat.txt

(quoted diff) +0 -0

b/Documentation/devicetree/bindings/media/snps,dw-csi-plat.txt new file mode 100644
index 0000000..028f5eb

Documentation/devicetree/bindings/media/snps,dw-csi-plat.txt new file +14 -0
@@ -0,0 +1,74 @@
+Synopsys DesignWare CSI-2 Host controller
+
+Description
+-----------
+
+This HW block is used to receive image coming from an MIPI CSI-2 compatible
+camera.
+
+Required properties:
+- compatible: shall be "snps,dw-csi-plat"
+- reg			: physical base address and size of the device memory mapped
+  registers;
+- interrupts		: CSI-2 Host interrupt
+- snps,output-type	: Core output to be used (IPI-> 0 or IDI->1 or BOTH-

2)
These

Documentation/devicetree/bindings/media/snps,dw-csi-plat.txt new file +9 -0
+			  values choose which of the Core outputs will be used, it
+			  can be Image Data Interface or Image Pixel Interface.
+- phys			: List of one PHY specifier (as defined in
+			  Documentation/devicetree/bindings/phy/phy-bindings.txt).
+			  This PHY is a MIPI DPHY working in RX mode.
+- resets		: Reference to a reset controller (optional)
+
+Optional properties(if in IPI mode):
+- snps,ipi-mode 	: Mode to be used when in IPI(Camera -> 0 or Controller -

1)

Documentation/devicetree/bindings/media/snps,dw-csi-plat.txt line 26

Quoted 3 lines
Documentation/devicetree/bindings/media/snps,dw-csi-plat.txt new file +3 -0
+			  This property defines if the controller will use the video
+			  timings available
+			  in the video stream or if it will use pre-defined ones.

How does one select this ?

Documentation/devicetree/bindings/media/snps,dw-csi-plat.txt line 28

Quoted 3 lines

+- snps,ipi-color-mode : Bus depth to be used in IPI (48 bits -> 0 or 16
bits -> 1)

Documentation/devicetree/bindings/media/snps,dw-csi-plat.txt new file +1 -0
+			  This property defines the width of the IPI bus.

How about using the standard bus-width property in the endpoint of the output port ?

Documentation/devicetree/bindings/media/snps,dw-csi-plat.txt line 31

Quoted 5 lines

+- snps,ipi-auto-flush : Data auto-flush (1 -> Yes or 0 -> No). This
property defines

Documentation/devicetree/bindings/media/snps,dw-csi-plat.txt new file +1 -0
+			  if the data is automatically flushed in each vsync

or if

Documentation/devicetree/bindings/media/snps,dw-csi-plat.txt new file +1 -0
+			  this process is done manually

This seems like a configuration option, not a hardware property. I don't think it belongs to DT.

Documentation/devicetree/bindings/media/snps,dw-csi-plat.txt line 34

Quoted 4 lines

+- snps,virtual-channel : Virtual channel where data is present when in IPI
mode. This

Documentation/devicetree/bindings/media/snps,dw-csi-plat.txt new file +2 -0
+			  property chooses the virtual channel which IPI will use to
+			  retrieve the video stream.

The virtual channel doesn't belong to DT, it should be queried from the sensor at runtime (and when a sensor can send multiple data streams, that should even be configurable).

Documentation/devicetree/bindings/media/snps,dw-csi-plat.txt line 38

Quoted 3 lines
Documentation/devicetree/bindings/media/snps,dw-csi-plat.txt new file +3 -0
+The per-board settings:
+ - port sub-node describing a single endpoint connected to the camera as
+   described in video-interfaces.txt[1].

You need to explicitly list all the ports for this device, with their number and function.

Quoted 25 lines
Documentation/devicetree/bindings/media/snps,dw-csi-plat.txt new file +25 -0
+Example:
+
+	csi2_1: csi2@3000 {
+		compatible = "snps,dw-csi-plat";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg = < 0x03000 0x7FF>;
+		interrupts = <2>;
+		output-type = <2>;
+		resets = <&dw_rst 1>;
+		phys = <&mipi_dphy_rx1 0>;
+		phy-names = "csi2-dphy";
+
+		/* IPI optional Configurations */
+		snps,ipi-mode = <0>;
+		snps,ipi-color-mode = <0>;
+		snps,ipi-auto-flush = <1>;
+		snps,virtual-channel = <0>;
+
+		/* CSI-2 per-board settings */
+		port@1 {
+			reg = <1>;
+			csi1_ep1: endpoint {
+				remote-endpoint = <&camera_1>;
+				data-lanes = <1 2>;

Documentation/devicetree/bindings/media/snps,dw-csi-plat.txt line 74

Quoted 10 lines
Documentation/devicetree/bindings/media/snps,dw-csi-plat.txt new file +10 -0
+			};
+		};
+		port@2 {
+			csi1_ep2: endpoint {
+				remote-endpoint = <&vif1_ep>;
+			};
+		};
+	};
+
+

Extra blank lines.

Signature · 3 lines
Regards,

Laurent Pinchart
#15 lore raw

Re: [V2, 4/5] Documentation: dt-bindings: Document bindings for DW MIPI CSI-2 Host

Quoted 4 lines · Laurent Pinchart

Hi Luis,

Thank you for the patch.

Hi Laurent, thank you for your review.
My answers inline.

Quoted 47 lines · On Thursday, 20 September 2018 14:16:42 EEST Luis Oliveira wrote:
Quoted 46 lines

Add bindings for Synopsys DesignWare MIPI CSI-2 host.

---
Changelog
v2:
- no changes

 .../devicetree/bindings/media/snps,dw-csi-plat.txt | 74 +++++++++++++++++++
 1 file changed, 74 insertions(+)
 create mode 100644

Documentation/devicetree/bindings/media/snps,dw-csi-plat.txt

(quoted diff) +0 -0

b/Documentation/devicetree/bindings/media/snps,dw-csi-plat.txt new file mode 100644
index 0000000..028f5eb

Documentation/devicetree/bindings/media/snps,dw-csi-plat.txt new file +14 -0
@@ -0,0 +1,74 @@
+Synopsys DesignWare CSI-2 Host controller
+
+Description
+-----------
+
+This HW block is used to receive image coming from an MIPI CSI-2 compatible
+camera.
+
+Required properties:
+- compatible: shall be "snps,dw-csi-plat"
+- reg			: physical base address and size of the device memory mapped
+  registers;
+- interrupts		: CSI-2 Host interrupt
+- snps,output-type	: Core output to be used (IPI-> 0 or IDI->1 or BOTH-

2)
These

Documentation/devicetree/bindings/media/snps,dw-csi-plat.txt new file +9 -0
+			  values choose which of the Core outputs will be used, it
+			  can be Image Data Interface or Image Pixel Interface.
+- phys			: List of one PHY specifier (as defined in
+			  Documentation/devicetree/bindings/phy/phy-bindings.txt).
+			  This PHY is a MIPI DPHY working in RX mode.
+- resets		: Reference to a reset controller (optional)
+
+Optional properties(if in IPI mode):
+- snps,ipi-mode 	: Mode to be used when in IPI(Camera -> 0 or Controller -
Quoted 7 lines

Documentation/devicetree/bindings/media/snps,dw-csi-plat.txt line 26

Quoted 4 lines

1)

Documentation/devicetree/bindings/media/snps,dw-csi-plat.txt new file +3 -0
+			  This property defines if the controller will use the video
+			  timings available
+			  in the video stream or if it will use pre-defined ones.

How does one select this ?

This is a hardware setting for timings in IPI (Image Pixel Interface - Synopsys specific interface), default is Controller mode - 1.

This can be selected in the DT or after by the driver.

Quoted 7 lines

Documentation/devicetree/bindings/media/snps,dw-csi-plat.txt line 28

Quoted 3 lines

+- snps,ipi-color-mode : Bus depth to be used in IPI (48 bits -> 0 or 16
bits -> 1)

Documentation/devicetree/bindings/media/snps,dw-csi-plat.txt new file +1 -0
+			  This property defines the width of the IPI bus.

How about using the standard bus-width property in the endpoint of the output port ?

Here I think It makes sense, I will use your suggestion.

Quoted 9 lines

Documentation/devicetree/bindings/media/snps,dw-csi-plat.txt line 31

Quoted 5 lines

+- snps,ipi-auto-flush : Data auto-flush (1 -> Yes or 0 -> No). This
property defines

Documentation/devicetree/bindings/media/snps,dw-csi-plat.txt new file +1 -0
+			  if the data is automatically flushed in each vsync

or if

Documentation/devicetree/bindings/media/snps,dw-csi-plat.txt new file +1 -0
+			  this process is done manually

This seems like a configuration option, not a hardware property. I don't think it belongs to DT.

I see your point,

Quoted 9 lines

Documentation/devicetree/bindings/media/snps,dw-csi-plat.txt line 34

Quoted 4 lines

+- snps,virtual-channel : Virtual channel where data is present when in IPI
mode. This

Documentation/devicetree/bindings/media/snps,dw-csi-plat.txt new file +2 -0
+			  property chooses the virtual channel which IPI will use to
+			  retrieve the video stream.

The virtual channel doesn't belong to DT, it should be queried from the sensor at runtime (and when a sensor can send multiple data streams, that should even be configurable).

I will do that.

I made this properties for my tests with cameras, thinking of SoCs that have fixed HW configurations. But I can remove all this fields from the DT because all of them can be made after in the driver.

Quoted 7 lines

Documentation/devicetree/bindings/media/snps,dw-csi-plat.txt line 38

Quoted 3 lines
Documentation/devicetree/bindings/media/snps,dw-csi-plat.txt new file +3 -0
+The per-board settings:
+ - port sub-node describing a single endpoint connected to the camera as
+   described in video-interfaces.txt[1].

You need to explicitly list all the ports for this device, with their number and function.

Ok.

Quoted 29 lines
Quoted 29 lines
Documentation/devicetree/bindings/media/snps,dw-csi-plat.txt new file +29 -0
+Example:
+
+	csi2_1: csi2@3000 {
+		compatible = "snps,dw-csi-plat";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg = < 0x03000 0x7FF>;
+		interrupts = <2>;
+		output-type = <2>;
+		resets = <&dw_rst 1>;
+		phys = <&mipi_dphy_rx1 0>;
+		phy-names = "csi2-dphy";
+
+		/* IPI optional Configurations */
+		snps,ipi-mode = <0>;
+		snps,ipi-color-mode = <0>;
+		snps,ipi-auto-flush = <1>;
+		snps,virtual-channel = <0>;
+
+		/* CSI-2 per-board settings */
+		port@1 {
+			reg = <1>;
+			csi1_ep1: endpoint {
+				remote-endpoint = <&camera_1>;
+				data-lanes = <1 2>;
+			};
+		};
+		port@2 {
+			csi1_ep2: endpoint {
Quoted 8 lines

Documentation/devicetree/bindings/media/snps,dw-csi-plat.txt line 74

Quoted 6 lines
Documentation/devicetree/bindings/media/snps,dw-csi-plat.txt new file +6 -0
+				remote-endpoint = <&vif1_ep>;
+			};
+		};
+	};
+
+

Extra blank lines.

Thanks.

Quoted 1 line
#16 lore raw

PATCH v2 5/5 media: platform: dwc: Add MIPI CSI-2 controller driver

In reply to Luis Oliveira

Add the Synopsys MIPI CSI-2 controller driver. This controller driver is divided in platform dependent functions and core functions. It also includes a platform for future DesignWare drivers.

---
Changelog
v2:
- fix SDPX License to match: Documentation/process/license-rules.rst

 MAINTAINERS                              |  11 +
 drivers/media/platform/dwc/Kconfig       |  11 +
 drivers/media/platform/dwc/Makefile      |   2 +
 drivers/media/platform/dwc/dw-csi-plat.c | 508 +++++++++++++++++++++++++++++++
 drivers/media/platform/dwc/dw-csi-plat.h |  76 +++++
 drivers/media/platform/dwc/dw-mipi-csi.c | 490 +++++++++++++++++++++++++++++
 drivers/media/platform/dwc/dw-mipi-csi.h | 202 ++++++++++++
 include/media/dwc/dw-mipi-csi-pltfrm.h   | 101 ++++++
 8 files changed, 1401 insertions(+)
 create mode 100644 drivers/media/platform/dwc/dw-csi-plat.c
 create mode 100644 drivers/media/platform/dwc/dw-csi-plat.h
 create mode 100644 drivers/media/platform/dwc/dw-mipi-csi.c
 create mode 100644 drivers/media/platform/dwc/dw-mipi-csi.h
 create mode 100644 include/media/dwc/dw-mipi-csi-pltfrm.h
MAINTAINERS +11 -0
index da2e509..fd5f1fc 100644
@@ -14032,6 +14032,16 @@ S:	Maintained
 F:	drivers/dma/dwi-axi-dmac/
 F:	Documentation/devicetree/bindings/dma/snps,dw-axi-dmac.txt
 
+SYNOPSYS DESIGNWARE MIPI CSI-2 HOST VIDEO PLATFORM
+M:	Luis Oliveira <[email protected]>
+L:	[email protected]
+T:	git git://linuxtv.org/media_tree.git
+S:	Maintained
+F:	drivers/media/platform/dwc
+F:	include/media/dwc/dw-mipi-csi-pltfrm.h
+F:	Documentation/devicetree/bindings/media/snps,dw-csi-plat.txt
+F:	Documentation/devicetree/bindings/phy/snps,dphy-rx.txt
+
 SYNOPSYS DESIGNWARE DMAC DRIVER
 M:	Viresh Kumar <[email protected]>
 R:	Andy Shevchenko <[email protected]>
@@ -16217,3 +16227,4 @@ T:	git git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
 S:	Buried alive in reporters
 F:	*
 F:	*/
+
drivers/media/platform/dwc/Kconfig +11 -0
index a573297..4b971a6 100644
@@ -20,6 +20,17 @@ config DWC_MIPI_CSI2_HOST
 
 if DWC_MIPI_CSI2_HOST
 
+config DWC_MIPI_CSI2_HOST_PLATFORM
+	tristate "Platform bus based CSI-2 Host Controller support"
+	depends on HAS_DMA
+	help
+	 This selects the CSI-2 host controller support. Select this if
+	 you have an CSI-2 Host controller on Platform bus.
+
+	 If you have a controller with this interface, say Y or M here.
+
+	  If unsure, say N.
+
 config DWC_MIPI_TC_DPHY_G128
 	tristate "DesignWare platform support using a G128 Test Chip"
 	depends on DWC_MIPI_CSI2_HOST
drivers/media/platform/dwc/Makefile +2 -0
index 8be6f68..e19cede 100644
@@ -1,2 +1,4 @@
+obj-$(CONFIG_DWC_MIPI_CSI2_HOST_PLATFORM)	+= dw-csi-platfrm.o
+dw-csi-platfrm-objs		:= dw-csi-plat.o dw-mipi-csi.o
 obj-$(CONFIG_DWC_MIPI_TC_DPHY_G128)	+= dw-dphy-platfrm.o
 dw-dphy-platfrm-objs	:= dw-dphy-plat.o dw-dphy-rx.o
drivers/media/platform/dwc/dw-csi-plat.c new file +508 -0

508 lines not on this page · raw message

drivers/media/platform/dwc/dw-csi-plat.h new file +76 -0

76 lines not on this page · raw message

drivers/media/platform/dwc/dw-mipi-csi.c new file +490 -0

490 lines not on this page · raw message

drivers/media/platform/dwc/dw-mipi-csi.h new file +202 -0

202 lines not on this page · raw message

include/media/dwc/dw-mipi-csi-pltfrm.h new file +101 -0

101 lines not on this page · raw message

Signature · 1 line
2.9.3
#17 lore raw
In reply to Luis Oliveira

Hi Luis,

Quoted 11 lines · Luis Oliveira

This adds support for Synopsys MIPI CSI-2 Host and MIPI D-PHY. The patch series include support for initialization/configuration of the DW MIPI CSI-2 controller and DW MIPI D-PHY and both include a reference platform driver.

This will enable future SoCs to use this standard approach and possibly create a more clean environment.

This series also documents the dt-bindings needed for the platform drivers.

This was applied in: https://git.linuxtv.org/media_tree.git

I'm currently working on some MIPI D-PHY support through the generic phy framework that could benefit your patches.

https://lwn.net/Articles/764173/

Feel free to comment on that serie if you have any particular constraints or if you believe that some issues should be addressed.

Thanks!
Maxime

Signature · 3 lines
Maxime Ripard, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
#18 lore raw
Quoted 15 lines · Maxime Ripard

Hi Luis,

Quoted 11 lines · Luis Oliveira

This adds support for Synopsys MIPI CSI-2 Host and MIPI D-PHY. The patch series include support for initialization/configuration of the DW MIPI CSI-2 controller and DW MIPI D-PHY and both include a reference platform driver.

This will enable future SoCs to use this standard approach and possibly create a more clean environment.

This series also documents the dt-bindings needed for the platform drivers.

This was applied in: https://git.linuxtv.org/media_tree.git

Quoted 7 lines

I'm currently working on some MIPI D-PHY support through the generic phy framework that could benefit your patches.

https://lwn.net/Articles/764173/

Feel free to comment on that serie if you have any particular constraints or if you believe that some issues should be addressed.

Hi Maxime,

I will check that, thanks!

Quoted 4 lines

Thanks!
Maxime