linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 00/19] Allwinner MIPI CSI-2 support for A31/V3s/A83T
@ 2020-11-28 14:28 Paul Kocialkowski
  2020-11-28 14:28 ` [PATCH v2 01/19] docs: phy: Add a part about PHY mode and submode Paul Kocialkowski
                   ` (18 more replies)
  0 siblings, 19 replies; 39+ messages in thread
From: Paul Kocialkowski @ 2020-11-28 14:28 UTC (permalink / raw)
  To: linux-media, devicetree, linux-arm-kernel, linux-kernel,
	linux-doc, devel, linux-sunxi
  Cc: Yong Deng, Mauro Carvalho Chehab, Rob Herring, Maxime Ripard,
	Chen-Yu Tsai, Jernej Skrabec, Paul Kocialkowski, Jonathan Corbet,
	Kishon Vijay Abraham I, Vinod Koul, Helen Koike,
	Dafna Hirschfeld, Greg Kroah-Hartman, Philipp Zabel,
	Sakari Ailus, Hans Verkuil, Thomas Petazzoni, kevin.lhopital

This series introduces support for MIPI CSI-2, with the A31 controller that is
found on most SoCs (A31, V3s and probably V5) as well as the A83T-specific
controller. While the former uses the same MIPI D-PHY that is already supported
for DSI, the latter embeds its own D-PHY.

In order to distinguish the use of the D-PHY between Rx mode (for MIPI CSI-2)
and Tx mode (for MIPI DSI), a submode is introduced for D-PHY in the PHY API.
This allows adding Rx support in the A31 D-PHY driver.

A few changes and fixes are applied to the A31 CSI controller driver, in order
to support the MIPI CSI-2 use-case.

Changes since v1:
- reworked fwnode and media graph on the CSI controller end to have one port
  per interface, which solves the bus type representation issue;
- removed unused IRQ handlers in the MIPI CSI-2 bridges;
- avoided the use of devm_regmap_init_mmio_clk;
- deasserted reset before enabling clocks;
- fixed reported return code issues (ret |=, missing checks);
- applied requested cosmetic changes (backward goto, etc);
- switched over to runtime PM for the mipi csi-2 bridge drivers;
- selected PHY_SUN6I_MIPI_DPHY in Kconfig for sun6i-mipi-csi2;
- registered nodes with mipi csi-2 bridge subdevs;
- used V4L2 format info instead of switch/case for sun6i-csi bpp;
- fixed device-tree bindings as requested (useless properties, license);
- fixed mipi bridge dt instances names;
- added PHY API documentation about mode/power on order requirement;
- fixed clock error return code in d-phy code;
- fixed D-PHY mode check in d-phy code;
- added MAINTAINERS entries for the new drivers;
- added V4L2 compliance results;
- added various comments and rework commit mesages as requested.

V4L2 compliance runs are available below:

# sun6i-csi + sun6i-mipi-csi2 + ov5648

v4l2-compliance SHA: not available, 32 bits

Compliance test for sun6i-video device /dev/video0:

Driver Info:
	Driver name      : sun6i-video
	Card type        : sun6i-csi
	Bus info         : platform:camera
	Driver version   : 5.10.0
	Capabilities     : 0x84200001
		Video Capture
		Streaming
		Extended Pix Format
		Device Capabilities
	Device Caps      : 0x04200001
		Video Capture
		Streaming
		Extended Pix Format
Media Driver Info:
	Driver name      : sun6i-csi
	Model            : Allwinner Video Capture Device
	Serial           : 
	Bus info         : platform:1cb0000.camera
	Media version    : 5.10.0
	Hardware revision: 0x00000000 (0)
	Driver version   : 5.10.0
Interface Info:
	ID               : 0x03000004
	Type             : V4L Video
Entity Info:
	ID               : 0x00000001 (1)
	Name             : sun6i-csi
	Function         : V4L2 I/O
	Pad 0x01000002   : 0: Sink
	Pad 0x01000003   : 1: Sink
	  Link 0x0200000d: from remote pad 0x1000008 of entity 'sun6i-mipi-csi2': Data, Enabled

Required ioctls:
	test MC information (see 'Media Driver Info' above): OK
		warn: v4l2-compliance.cpp(633): media bus_info 'platform:1cb0000.camera' differs from V4L2 bus_info 'platform:camera'
	test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
	test second /dev/video0 open: OK
		warn: v4l2-compliance.cpp(633): media bus_info 'platform:1cb0000.camera' differs from V4L2 bus_info 'platform:camera'
	test VIDIOC_QUERYCAP: OK
	test VIDIOC_G/S_PRIORITY: OK
	test for unlimited opens: OK

Debug ioctls:
	test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
	test VIDIOC_LOG_STATUS: OK

Input ioctls:
	test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
	test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
	test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
	test VIDIOC_ENUMAUDIO: OK (Not Supported)
	test VIDIOC_G/S/ENUMINPUT: OK
	test VIDIOC_G/S_AUDIO: OK (Not Supported)
	Inputs: 1 Audio Inputs: 0 Tuners: 0

Output ioctls:
	test VIDIOC_G/S_MODULATOR: OK (Not Supported)
	test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
	test VIDIOC_ENUMAUDOUT: OK (Not Supported)
	test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
	test VIDIOC_G/S_AUDOUT: OK (Not Supported)
	Outputs: 0 Audio Outputs: 0 Modulators: 0

Input/Output configuration ioctls:
	test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
	test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)
	test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)
	test VIDIOC_G/S_EDID: OK (Not Supported)

Control ioctls (Input 0):
		warn: v4l2-test-controls.cpp(92): Exposure: (max - min) % step != 0
		warn: v4l2-test-controls.cpp(92): Gain: (max - min) % step != 0
		warn: v4l2-test-controls.cpp(92): Exposure: (max - min) % step != 0
		warn: v4l2-test-controls.cpp(92): Gain: (max - min) % step != 0
	test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK
	test VIDIOC_QUERYCTRL: OK
		warn: v4l2-test-controls.cpp(368): Gain: returned control value 44 not a multiple of step
		warn: v4l2-test-controls.cpp(368): Gain: returned control value 44 not a multiple of step
		warn: v4l2-test-controls.cpp(368): Gain: returned control value 44 not a multiple of step
		warn: v4l2-test-controls.cpp(368): Gain: returned control value 44 not a multiple of step
	test VIDIOC_G/S_CTRL: OK
		warn: v4l2-test-controls.cpp(555): Gain: returned control value 44 not a multiple of step
	test VIDIOC_G/S/TRY_EXT_CTRLS: OK
	test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK
	test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
	Standard Controls: 15 Private Controls: 0

Format ioctls (Input 0):
	test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
	test VIDIOC_G/S_PARM: OK (Not Supported)
	test VIDIOC_G_FBUF: OK (Not Supported)
	test VIDIOC_G_FMT: OK
	test VIDIOC_TRY_FMT: OK
	test VIDIOC_S_FMT: OK
	test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
	test Cropping: OK (Not Supported)
	test Composing: OK (Not Supported)
	test Scaling: OK

Codec ioctls (Input 0):
	test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
	test VIDIOC_G_ENC_INDEX: OK (Not Supported)
	test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)

Buffer ioctls (Input 0):
	test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
		fail: v4l2-test-buffers.cpp(755): q.export_bufs(node, q.g_type())
	test VIDIOC_EXPBUF: FAIL
	test Requests: OK (Not Supported)

Total for sun6i-video device /dev/video0: 45, Succeeded: 44, Failed: 1, Warnings: 1

# sun6i-csi + sun8i-a83t-mipi-csi2 + ov8865

v4l2-compliance SHA: not available, 32 bits

Compliance test for sun6i-video device /dev/video0:

Driver Info:
	Driver name      : sun6i-video
	Card type        : sun6i-csi
	Bus info         : platform:camera
	Driver version   : 5.10.0
	Capabilities     : 0x84200001
		Video Capture
		Streaming
		Extended Pix Format
		Device Capabilities
	Device Caps      : 0x04200001
		Video Capture
		Streaming
		Extended Pix Format
Media Driver Info:
	Driver name      : sun6i-csi
	Model            : Allwinner Video Capture Device
	Serial           : 
	Bus info         : platform:1cb0000.camera
	Media version    : 5.10.0
	Hardware revision: 0x00000000 (0)
	Driver version   : 5.10.0
Interface Info:
	ID               : 0x03000004
	Type             : V4L Video
Entity Info:
	ID               : 0x00000001 (1)
	Name             : sun6i-csi
	Function         : V4L2 I/O
	Pad 0x01000002   : 0: Sink
	Pad 0x01000003   : 1: Sink
	  Link 0x0200000d: from remote pad 0x1000008 of entity 'sun8i-a83t-mipi-csi2': Data, Enabled

Required ioctls:
	test MC information (see 'Media Driver Info' above): OK
		warn: v4l2-compliance.cpp(633): media bus_info 'platform:1cb0000.camera' differs from V4L2 bus_info 'platform:camera'
	test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
	test second /dev/video0 open: OK
		warn: v4l2-compliance.cpp(633): media bus_info 'platform:1cb0000.camera' differs from V4L2 bus_info 'platform:camera'
	test VIDIOC_QUERYCAP: OK
	test VIDIOC_G/S_PRIORITY: OK
	test for unlimited opens: OK

Debug ioctls:
	test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
	test VIDIOC_LOG_STATUS: OK

Input ioctls:
	test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
	test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
	test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
	test VIDIOC_ENUMAUDIO: OK (Not Supported)
	test VIDIOC_G/S/ENUMINPUT: OK
	test VIDIOC_G/S_AUDIO: OK (Not Supported)
	Inputs: 1 Audio Inputs: 0 Tuners: 0

Output ioctls:
	test VIDIOC_G/S_MODULATOR: OK (Not Supported)
	test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
	test VIDIOC_ENUMAUDOUT: OK (Not Supported)
	test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
	test VIDIOC_G/S_AUDOUT: OK (Not Supported)
	Outputs: 0 Audio Outputs: 0 Modulators: 0

Input/Output configuration ioctls:
	test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
	test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)
	test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)
	test VIDIOC_G/S_EDID: OK (Not Supported)

Control ioctls (Input 0):
		warn: v4l2-test-controls.cpp(92): Exposure: (max - min) % step != 0
		warn: v4l2-test-controls.cpp(92): Gain: (max - min) % step != 0
		warn: v4l2-test-controls.cpp(92): Exposure: (max - min) % step != 0
		warn: v4l2-test-controls.cpp(92): Gain: (max - min) % step != 0
	test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK
	test VIDIOC_QUERYCTRL: OK
	test VIDIOC_G/S_CTRL: OK
	test VIDIOC_G/S/TRY_EXT_CTRLS: OK
	test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK
	test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
	Standard Controls: 11 Private Controls: 0

Format ioctls (Input 0):
	test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
	test VIDIOC_G/S_PARM: OK (Not Supported)
	test VIDIOC_G_FBUF: OK (Not Supported)
	test VIDIOC_G_FMT: OK
	test VIDIOC_TRY_FMT: OK
	test VIDIOC_S_FMT: OK
	test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
	test Cropping: OK (Not Supported)
	test Composing: OK (Not Supported)
	test Scaling: OK

Codec ioctls (Input 0):
	test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
	test VIDIOC_G_ENC_INDEX: OK (Not Supported)
	test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)

Buffer ioctls (Input 0):
	test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
	test VIDIOC_EXPBUF: OK
	test Requests: OK (Not Supported)

Total for sun6i-video device /dev/video0: 45, Succeeded: 45, Failed: 0, Warnings: 6

Paul Kocialkowski (19):
  docs: phy: Add a part about PHY mode and submode
  phy: Distinguish between Rx and Tx for MIPI D-PHY with submodes
  phy: allwinner: phy-sun6i-mipi-dphy: Support D-PHY Rx mode for MIPI
    CSI-2
  media: sun6i-csi: Use common V4L2 format info for storage bpp
  media: sun6i-csi: Only configure the interface data width for parallel
  dt-bindings: media: sun6i-a31-csi: Add MIPI CSI-2 input port
  media: sun6i-csi: Add support for MIPI CSI-2 bridge input
  ARM: dts: sun8i: a83t: Add CSI controller ports
  ARM: dts: sunxi: h3/h5: Add CSI controller port for parallel input
  ARM: dts: sun8i: v3s: Add CSI1 controller port for parallel input
  arm64: dts: allwinner: a64: Add CSI controller port for parallel input
  dt-bindings: media: Add A31 MIPI CSI-2 bindings documentation
  media: sunxi: Add support for the A31 MIPI CSI-2 controller
  ARM: dts: sun8i: v3s: Add nodes for MIPI CSI-2 support
  MAINTAINERS: Add entry for the Allwinner A31 MIPI CSI-2 bridge
  dt-bindings: media: Add A83T MIPI CSI-2 bindings documentation
  media: sunxi: Add support for the A83T MIPI CSI-2 controller
  ARM: dts: sun8i: a83t: Add MIPI CSI-2 controller node
  MAINTAINERS: Add entry for the Allwinner A83T MIPI CSI-2 bridge

 .../media/allwinner,sun6i-a31-csi.yaml        |  86 ++-
 .../media/allwinner,sun6i-a31-mipi-csi2.yaml  | 151 ++++
 .../media/allwinner,sun8i-a83t-mipi-csi2.yaml | 147 ++++
 Documentation/driver-api/phy/phy.rst          |  18 +
 MAINTAINERS                                   |  16 +
 arch/arm/boot/dts/sun8i-a83t.dtsi             |  38 +-
 arch/arm/boot/dts/sun8i-v3s.dtsi              |  77 ++
 arch/arm/boot/dts/sunxi-h3-h5.dtsi            |   9 +
 arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi |   9 +
 drivers/media/platform/sunxi/Kconfig          |   2 +
 drivers/media/platform/sunxi/Makefile         |   2 +
 .../platform/sunxi/sun6i-csi/sun6i_csi.c      | 165 +++--
 .../platform/sunxi/sun6i-csi/sun6i_csi.h      |  58 +-
 .../platform/sunxi/sun6i-csi/sun6i_video.c    |  53 +-
 .../platform/sunxi/sun6i-csi/sun6i_video.h    |   7 +-
 .../platform/sunxi/sun6i-mipi-csi2/Kconfig    |  12 +
 .../platform/sunxi/sun6i-mipi-csi2/Makefile   |   4 +
 .../sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c   | 591 ++++++++++++++++
 .../sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.h   | 117 ++++
 .../sunxi/sun8i-a83t-mipi-csi2/Kconfig        |  11 +
 .../sunxi/sun8i-a83t-mipi-csi2/Makefile       |   4 +
 .../sun8i-a83t-mipi-csi2/sun8i_a83t_dphy.c    |  92 +++
 .../sun8i-a83t-mipi-csi2/sun8i_a83t_dphy.h    |  39 ++
 .../sun8i_a83t_mipi_csi2.c                    | 657 ++++++++++++++++++
 .../sun8i_a83t_mipi_csi2.h                    | 197 ++++++
 drivers/phy/allwinner/phy-sun6i-mipi-dphy.c   | 164 ++++-
 drivers/staging/media/rkisp1/rkisp1-isp.c     |   3 +-
 include/linux/phy/phy-mipi-dphy.h             |  13 +
 28 files changed, 2620 insertions(+), 122 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/media/allwinner,sun6i-a31-mipi-csi2.yaml
 create mode 100644 Documentation/devicetree/bindings/media/allwinner,sun8i-a83t-mipi-csi2.yaml
 create mode 100644 drivers/media/platform/sunxi/sun6i-mipi-csi2/Kconfig
 create mode 100644 drivers/media/platform/sunxi/sun6i-mipi-csi2/Makefile
 create mode 100644 drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c
 create mode 100644 drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.h
 create mode 100644 drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Kconfig
 create mode 100644 drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Makefile
 create mode 100644 drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_dphy.c
 create mode 100644 drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_dphy.h
 create mode 100644 drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c
 create mode 100644 drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.h

-- 
2.29.2


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [PATCH v2 01/19] docs: phy: Add a part about PHY mode and submode
  2020-11-28 14:28 [PATCH v2 00/19] Allwinner MIPI CSI-2 support for A31/V3s/A83T Paul Kocialkowski
@ 2020-11-28 14:28 ` Paul Kocialkowski
  2020-12-01 10:37   ` Maxime Ripard
  2020-11-28 14:28 ` [PATCH v2 02/19] phy: Distinguish between Rx and Tx for MIPI D-PHY with submodes Paul Kocialkowski
                   ` (17 subsequent siblings)
  18 siblings, 1 reply; 39+ messages in thread
From: Paul Kocialkowski @ 2020-11-28 14:28 UTC (permalink / raw)
  To: linux-media, devicetree, linux-arm-kernel, linux-kernel,
	linux-doc, devel, linux-sunxi
  Cc: Yong Deng, Mauro Carvalho Chehab, Rob Herring, Maxime Ripard,
	Chen-Yu Tsai, Jernej Skrabec, Paul Kocialkowski, Jonathan Corbet,
	Kishon Vijay Abraham I, Vinod Koul, Helen Koike,
	Dafna Hirschfeld, Greg Kroah-Hartman, Philipp Zabel,
	Sakari Ailus, Hans Verkuil, Thomas Petazzoni, kevin.lhopital

Besides giving pointers to the relevant functions for PHY mode and
submode configuration, this clarifies the need to set them before
powering on the PHY.

Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
---
 Documentation/driver-api/phy/phy.rst | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/Documentation/driver-api/phy/phy.rst b/Documentation/driver-api/phy/phy.rst
index 8fc1ce0bb905..6cbc72707a49 100644
--- a/Documentation/driver-api/phy/phy.rst
+++ b/Documentation/driver-api/phy/phy.rst
@@ -195,3 +195,21 @@ DeviceTree Binding
 
 The documentation for PHY dt binding can be found @
 Documentation/devicetree/bindings/phy/phy-bindings.txt
+
+PHY Mode and Submode
+====================
+
+Once a reference to a PHY is obtained by a controller, the PHY can be configured
+to a PHY mode and submode. PHY modes are described in the `phy_mode` enum while
+submodes are specific to the selected PHY mode.
+
+Mode and submode configuration is done by calling::
+
+	int phy_set_mode_ext(struct phy *phy, enum phy_mode mode, int submode);
+
+If no submode is to be configured, users can call::
+
+	int phy_set_mode(struct phy *phy, enum phy_mode mode);
+
+The PHY mode and submode must not be configured after the PHY has already been
+powered on.
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v2 02/19] phy: Distinguish between Rx and Tx for MIPI D-PHY with submodes
  2020-11-28 14:28 [PATCH v2 00/19] Allwinner MIPI CSI-2 support for A31/V3s/A83T Paul Kocialkowski
  2020-11-28 14:28 ` [PATCH v2 01/19] docs: phy: Add a part about PHY mode and submode Paul Kocialkowski
@ 2020-11-28 14:28 ` Paul Kocialkowski
  2020-11-28 14:28 ` [PATCH v2 03/19] phy: allwinner: phy-sun6i-mipi-dphy: Support D-PHY Rx mode for MIPI CSI-2 Paul Kocialkowski
                   ` (16 subsequent siblings)
  18 siblings, 0 replies; 39+ messages in thread
From: Paul Kocialkowski @ 2020-11-28 14:28 UTC (permalink / raw)
  To: linux-media, devicetree, linux-arm-kernel, linux-kernel,
	linux-doc, devel, linux-sunxi
  Cc: Yong Deng, Mauro Carvalho Chehab, Rob Herring, Maxime Ripard,
	Chen-Yu Tsai, Jernej Skrabec, Paul Kocialkowski, Jonathan Corbet,
	Kishon Vijay Abraham I, Vinod Koul, Helen Koike,
	Dafna Hirschfeld, Greg Kroah-Hartman, Philipp Zabel,
	Sakari Ailus, Hans Verkuil, Thomas Petazzoni, kevin.lhopital

As some D-PHY controllers support both Rx and Tx mode, we need a way for
users to explicitly request one or the other. For instance, Rx mode can
be used along with MIPI CSI-2 while Tx mode can be used with MIPI DSI.

Introduce new MIPI D-PHY PHY submodes to use with PHY_MODE_MIPI_DPHY.
The default (zero value) is kept to Tx so only the rkisp1 driver, which
uses D-PHY in Rx mode, needs to be adapted.

Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
Acked-by: Helen Koike <helen.koike@collabora.com>
---
 drivers/staging/media/rkisp1/rkisp1-isp.c |  3 ++-
 include/linux/phy/phy-mipi-dphy.h         | 13 +++++++++++++
 2 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/drivers/staging/media/rkisp1/rkisp1-isp.c b/drivers/staging/media/rkisp1/rkisp1-isp.c
index a9715b0b7264..f1167995688a 100644
--- a/drivers/staging/media/rkisp1/rkisp1-isp.c
+++ b/drivers/staging/media/rkisp1/rkisp1-isp.c
@@ -914,7 +914,8 @@ static int rkisp1_mipi_csi2_start(struct rkisp1_isp *isp,
 
 	phy_mipi_dphy_get_default_config(pixel_clock, isp->sink_fmt->bus_width,
 					 sensor->lanes, cfg);
-	phy_set_mode(sensor->dphy, PHY_MODE_MIPI_DPHY);
+	phy_set_mode_ext(cdev->dphy, PHY_MODE_MIPI_DPHY,
+			 PHY_MIPI_DPHY_SUBMODE_RX);
 	phy_configure(sensor->dphy, &opts);
 	phy_power_on(sensor->dphy);
 
diff --git a/include/linux/phy/phy-mipi-dphy.h b/include/linux/phy/phy-mipi-dphy.h
index a877ffee845d..0f57ef46a8b5 100644
--- a/include/linux/phy/phy-mipi-dphy.h
+++ b/include/linux/phy/phy-mipi-dphy.h
@@ -6,6 +6,19 @@
 #ifndef __PHY_MIPI_DPHY_H_
 #define __PHY_MIPI_DPHY_H_
 
+/**
+ * enum phy_mipi_dphy_submode - MIPI D-PHY sub-mode
+ *
+ * A MIPI D-PHY can be used to transmit or receive data.
+ * Since some controllers can support both, the direction to enable is specified
+ * with the PHY sub-mode. Transmit is assumed by default with phy_set_mode.
+ */
+
+enum phy_mipi_dphy_submode {
+	PHY_MIPI_DPHY_SUBMODE_TX = 0,
+	PHY_MIPI_DPHY_SUBMODE_RX,
+};
+
 /**
  * struct phy_configure_opts_mipi_dphy - MIPI D-PHY configuration set
  *
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v2 03/19] phy: allwinner: phy-sun6i-mipi-dphy: Support D-PHY Rx mode for MIPI CSI-2
  2020-11-28 14:28 [PATCH v2 00/19] Allwinner MIPI CSI-2 support for A31/V3s/A83T Paul Kocialkowski
  2020-11-28 14:28 ` [PATCH v2 01/19] docs: phy: Add a part about PHY mode and submode Paul Kocialkowski
  2020-11-28 14:28 ` [PATCH v2 02/19] phy: Distinguish between Rx and Tx for MIPI D-PHY with submodes Paul Kocialkowski
@ 2020-11-28 14:28 ` Paul Kocialkowski
  2020-12-01 10:38   ` Maxime Ripard
  2020-11-28 14:28 ` [PATCH v2 04/19] media: sun6i-csi: Use common V4L2 format info for storage bpp Paul Kocialkowski
                   ` (15 subsequent siblings)
  18 siblings, 1 reply; 39+ messages in thread
From: Paul Kocialkowski @ 2020-11-28 14:28 UTC (permalink / raw)
  To: linux-media, devicetree, linux-arm-kernel, linux-kernel,
	linux-doc, devel, linux-sunxi
  Cc: Yong Deng, Mauro Carvalho Chehab, Rob Herring, Maxime Ripard,
	Chen-Yu Tsai, Jernej Skrabec, Paul Kocialkowski, Jonathan Corbet,
	Kishon Vijay Abraham I, Vinod Koul, Helen Koike,
	Dafna Hirschfeld, Greg Kroah-Hartman, Philipp Zabel,
	Sakari Ailus, Hans Verkuil, Thomas Petazzoni, kevin.lhopital

The Allwinner A31 D-PHY supports both Rx and Tx modes. While the latter
is already supported and used for MIPI DSI this adds support for the
former, to be used with MIPI CSI-2.

This implementation is inspired by Allwinner's V3s Linux SDK
implementation, which was used as a documentation base.

Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
---
 drivers/phy/allwinner/phy-sun6i-mipi-dphy.c | 164 +++++++++++++++++++-
 1 file changed, 160 insertions(+), 4 deletions(-)

diff --git a/drivers/phy/allwinner/phy-sun6i-mipi-dphy.c b/drivers/phy/allwinner/phy-sun6i-mipi-dphy.c
index 1fa761ba6cbb..0389b6b670d6 100644
--- a/drivers/phy/allwinner/phy-sun6i-mipi-dphy.c
+++ b/drivers/phy/allwinner/phy-sun6i-mipi-dphy.c
@@ -24,6 +24,14 @@
 #define SUN6I_DPHY_TX_CTL_REG		0x04
 #define SUN6I_DPHY_TX_CTL_HS_TX_CLK_CONT	BIT(28)
 
+#define SUN6I_DPHY_RX_CTL_REG		0x08
+#define SUN6I_DPHY_RX_CTL_EN_DBC	BIT(31)
+#define SUN6I_DPHY_RX_CTL_RX_CLK_FORCE	BIT(24)
+#define SUN6I_DPHY_RX_CTL_RX_D3_FORCE	BIT(23)
+#define SUN6I_DPHY_RX_CTL_RX_D2_FORCE	BIT(22)
+#define SUN6I_DPHY_RX_CTL_RX_D1_FORCE	BIT(21)
+#define SUN6I_DPHY_RX_CTL_RX_D0_FORCE	BIT(20)
+
 #define SUN6I_DPHY_TX_TIME0_REG		0x10
 #define SUN6I_DPHY_TX_TIME0_HS_TRAIL(n)		(((n) & 0xff) << 24)
 #define SUN6I_DPHY_TX_TIME0_HS_PREPARE(n)	(((n) & 0xff) << 16)
@@ -44,12 +52,29 @@
 #define SUN6I_DPHY_TX_TIME4_HS_TX_ANA1(n)	(((n) & 0xff) << 8)
 #define SUN6I_DPHY_TX_TIME4_HS_TX_ANA0(n)	((n) & 0xff)
 
+#define SUN6I_DPHY_RX_TIME0_REG		0x30
+#define SUN6I_DPHY_RX_TIME0_HS_RX_SYNC(n)	(((n) & 0xff) << 24)
+#define SUN6I_DPHY_RX_TIME0_HS_RX_CLK_MISS(n)	(((n) & 0xff) << 16)
+#define SUN6I_DPHY_RX_TIME0_LP_RX(n)		(((n) & 0xff) << 8)
+
+#define SUN6I_DPHY_RX_TIME1_REG		0x34
+#define SUN6I_DPHY_RX_TIME1_RX_DLY(n)		(((n) & 0xfff) << 20)
+#define SUN6I_DPHY_RX_TIME1_LP_RX_ULPS_WP(n)	((n) & 0xfffff)
+
+#define SUN6I_DPHY_RX_TIME2_REG		0x38
+#define SUN6I_DPHY_RX_TIME2_HS_RX_ANA1(n)	(((n) & 0xff) << 8)
+#define SUN6I_DPHY_RX_TIME2_HS_RX_ANA0(n)	((n) & 0xff)
+
+#define SUN6I_DPHY_RX_TIME3_REG		0x40
+#define SUN6I_DPHY_RX_TIME3_LPRST_DLY(n)	(((n) & 0xffff) << 16)
+
 #define SUN6I_DPHY_ANA0_REG		0x4c
 #define SUN6I_DPHY_ANA0_REG_PWS			BIT(31)
 #define SUN6I_DPHY_ANA0_REG_DMPC		BIT(28)
 #define SUN6I_DPHY_ANA0_REG_DMPD(n)		(((n) & 0xf) << 24)
 #define SUN6I_DPHY_ANA0_REG_SLV(n)		(((n) & 7) << 12)
 #define SUN6I_DPHY_ANA0_REG_DEN(n)		(((n) & 0xf) << 8)
+#define SUN6I_DPHY_ANA0_REG_SFB(n)		(((n) & 3) << 2)
 
 #define SUN6I_DPHY_ANA1_REG		0x50
 #define SUN6I_DPHY_ANA1_REG_VTTMODE		BIT(31)
@@ -92,6 +117,8 @@ struct sun6i_dphy {
 
 	struct phy				*phy;
 	struct phy_configure_opts_mipi_dphy	config;
+
+	int					submode;
 };
 
 static int sun6i_dphy_init(struct phy *phy)
@@ -105,6 +132,18 @@ static int sun6i_dphy_init(struct phy *phy)
 	return 0;
 }
 
+static int sun6i_dphy_set_mode(struct phy *phy, enum phy_mode mode, int submode)
+{
+	struct sun6i_dphy *dphy = phy_get_drvdata(phy);
+
+	if (mode != PHY_MODE_MIPI_DPHY)
+		return -EINVAL;
+
+	dphy->submode = submode;
+
+	return 0;
+}
+
 static int sun6i_dphy_configure(struct phy *phy, union phy_configure_opts *opts)
 {
 	struct sun6i_dphy *dphy = phy_get_drvdata(phy);
@@ -119,9 +158,8 @@ static int sun6i_dphy_configure(struct phy *phy, union phy_configure_opts *opts)
 	return 0;
 }
 
-static int sun6i_dphy_power_on(struct phy *phy)
+static int sun6i_dphy_tx_power_on(struct sun6i_dphy *dphy)
 {
-	struct sun6i_dphy *dphy = phy_get_drvdata(phy);
 	u8 lanes_mask = GENMASK(dphy->config.lanes - 1, 0);
 
 	regmap_write(dphy->regs, SUN6I_DPHY_TX_CTL_REG,
@@ -211,12 +249,129 @@ static int sun6i_dphy_power_on(struct phy *phy)
 	return 0;
 }
 
+static int sun6i_dphy_rx_power_on(struct sun6i_dphy *dphy)
+{
+	/* Physical clock rate is actually half of symbol rate with DDR. */
+	unsigned long mipi_symbol_rate = dphy->config.hs_clk_rate;
+	unsigned long dphy_clk_rate;
+	unsigned int rx_dly;
+	unsigned int lprst_dly;
+	u32 value;
+
+	dphy_clk_rate = clk_get_rate(dphy->mod_clk);
+	if (!dphy_clk_rate)
+		return -EINVAL;
+
+	/* Hardcoded timing parameters from the Allwinner BSP. */
+	regmap_write(dphy->regs, SUN6I_DPHY_RX_TIME0_REG,
+		     SUN6I_DPHY_RX_TIME0_HS_RX_SYNC(255) |
+		     SUN6I_DPHY_RX_TIME0_HS_RX_CLK_MISS(255) |
+		     SUN6I_DPHY_RX_TIME0_LP_RX(255));
+
+	/*
+	 * Formula from the Allwinner BSP, with hardcoded coefficients
+	 * (probably internal divider/multiplier).
+	 */
+	rx_dly = 8 * (unsigned int)(dphy_clk_rate / (mipi_symbol_rate / 8));
+
+	/*
+	 * The Allwinner BSP has an alternative formula for LP_RX_ULPS_WP:
+	 * lp_ulps_wp_cnt = lp_ulps_wp_ms * lp_clk / 1000
+	 * but does not use it and hardcodes 255 instead.
+	 */
+	regmap_write(dphy->regs, SUN6I_DPHY_RX_TIME1_REG,
+		     SUN6I_DPHY_RX_TIME1_RX_DLY(rx_dly) |
+		     SUN6I_DPHY_RX_TIME1_LP_RX_ULPS_WP(255));
+
+	/* HS_RX_ANA0 value is hardcoded in the Allwinner BSP. */
+	regmap_write(dphy->regs, SUN6I_DPHY_RX_TIME2_REG,
+		     SUN6I_DPHY_RX_TIME2_HS_RX_ANA0(4));
+
+	/*
+	 * Formula from the Allwinner BSP, with hardcoded coefficients
+	 * (probably internal divider/multiplier).
+	 */
+	lprst_dly = 4 * (unsigned int)(dphy_clk_rate / (mipi_symbol_rate / 2));
+
+	regmap_write(dphy->regs, SUN6I_DPHY_RX_TIME3_REG,
+		     SUN6I_DPHY_RX_TIME3_LPRST_DLY(lprst_dly));
+
+	/* Analog parameters are hardcoded in the Allwinner BSP. */
+	regmap_write(dphy->regs, SUN6I_DPHY_ANA0_REG,
+		     SUN6I_DPHY_ANA0_REG_PWS |
+		     SUN6I_DPHY_ANA0_REG_SLV(7) |
+		     SUN6I_DPHY_ANA0_REG_SFB(2));
+
+	regmap_write(dphy->regs, SUN6I_DPHY_ANA1_REG,
+		     SUN6I_DPHY_ANA1_REG_SVTT(4));
+
+	regmap_write(dphy->regs, SUN6I_DPHY_ANA4_REG,
+		     SUN6I_DPHY_ANA4_REG_DMPLVC |
+		     SUN6I_DPHY_ANA4_REG_DMPLVD(1));
+
+	regmap_write(dphy->regs, SUN6I_DPHY_ANA2_REG,
+		     SUN6I_DPHY_ANA2_REG_ENIB);
+
+	regmap_write(dphy->regs, SUN6I_DPHY_ANA3_REG,
+		     SUN6I_DPHY_ANA3_EN_LDOR |
+		     SUN6I_DPHY_ANA3_EN_LDOC |
+		     SUN6I_DPHY_ANA3_EN_LDOD);
+
+	/*
+	 * Delay comes from the Allwinner BSP, likely for internal regulator
+	 * ramp-up.
+	 */
+	udelay(3);
+
+	value = SUN6I_DPHY_RX_CTL_EN_DBC | SUN6I_DPHY_RX_CTL_RX_CLK_FORCE;
+
+	/*
+	 * Rx data lane force-enable bits are used as regular RX enable by the
+	 * Allwinner BSP.
+	 */
+	if (dphy->config.lanes >= 1)
+		value |= SUN6I_DPHY_RX_CTL_RX_D0_FORCE;
+	if (dphy->config.lanes >= 2)
+		value |= SUN6I_DPHY_RX_CTL_RX_D1_FORCE;
+	if (dphy->config.lanes >= 3)
+		value |= SUN6I_DPHY_RX_CTL_RX_D2_FORCE;
+	if (dphy->config.lanes == 4)
+		value |= SUN6I_DPHY_RX_CTL_RX_D3_FORCE;
+
+	regmap_write(dphy->regs, SUN6I_DPHY_RX_CTL_REG, value);
+
+	regmap_write(dphy->regs, SUN6I_DPHY_GCTL_REG,
+		     SUN6I_DPHY_GCTL_LANE_NUM(dphy->config.lanes) |
+		     SUN6I_DPHY_GCTL_EN);
+
+	return 0;
+}
+
+static int sun6i_dphy_power_on(struct phy *phy)
+{
+	struct sun6i_dphy *dphy = phy_get_drvdata(phy);
+
+	switch (dphy->submode) {
+	case PHY_MIPI_DPHY_SUBMODE_TX:
+		return sun6i_dphy_tx_power_on(dphy);
+	case PHY_MIPI_DPHY_SUBMODE_RX:
+		return sun6i_dphy_rx_power_on(dphy);
+	default:
+		return -EINVAL;
+	}
+}
+
 static int sun6i_dphy_power_off(struct phy *phy)
 {
 	struct sun6i_dphy *dphy = phy_get_drvdata(phy);
 
-	regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA1_REG,
-			   SUN6I_DPHY_ANA1_REG_VTTMODE, 0);
+	regmap_write(dphy->regs, SUN6I_DPHY_GCTL_REG, 0);
+
+	regmap_write(dphy->regs, SUN6I_DPHY_ANA0_REG, 0);
+	regmap_write(dphy->regs, SUN6I_DPHY_ANA1_REG, 0);
+	regmap_write(dphy->regs, SUN6I_DPHY_ANA2_REG, 0);
+	regmap_write(dphy->regs, SUN6I_DPHY_ANA3_REG, 0);
+	regmap_write(dphy->regs, SUN6I_DPHY_ANA4_REG, 0);
 
 	return 0;
 }
@@ -234,6 +389,7 @@ static int sun6i_dphy_exit(struct phy *phy)
 
 
 static const struct phy_ops sun6i_dphy_ops = {
+	.set_mode	= sun6i_dphy_set_mode,
 	.configure	= sun6i_dphy_configure,
 	.power_on	= sun6i_dphy_power_on,
 	.power_off	= sun6i_dphy_power_off,
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v2 04/19] media: sun6i-csi: Use common V4L2 format info for storage bpp
  2020-11-28 14:28 [PATCH v2 00/19] Allwinner MIPI CSI-2 support for A31/V3s/A83T Paul Kocialkowski
                   ` (2 preceding siblings ...)
  2020-11-28 14:28 ` [PATCH v2 03/19] phy: allwinner: phy-sun6i-mipi-dphy: Support D-PHY Rx mode for MIPI CSI-2 Paul Kocialkowski
@ 2020-11-28 14:28 ` Paul Kocialkowski
  2020-12-01 10:39   ` Maxime Ripard
  2020-11-28 14:28 ` [PATCH v2 05/19] media: sun6i-csi: Only configure the interface data width for parallel Paul Kocialkowski
                   ` (14 subsequent siblings)
  18 siblings, 1 reply; 39+ messages in thread
From: Paul Kocialkowski @ 2020-11-28 14:28 UTC (permalink / raw)
  To: linux-media, devicetree, linux-arm-kernel, linux-kernel,
	linux-doc, devel, linux-sunxi
  Cc: Yong Deng, Mauro Carvalho Chehab, Rob Herring, Maxime Ripard,
	Chen-Yu Tsai, Jernej Skrabec, Paul Kocialkowski, Jonathan Corbet,
	Kishon Vijay Abraham I, Vinod Koul, Helen Koike,
	Dafna Hirschfeld, Greg Kroah-Hartman, Philipp Zabel,
	Sakari Ailus, Hans Verkuil, Thomas Petazzoni, kevin.lhopital

V4L2 has a common helper which can be used for calculating the number
of stored bits per pixels of a given (stored) image format.

Use the helper-returned structure instead of our own switch/case list.
Note that a few formats are not in that list so we keep them as
special cases.

The custom switch/case was also wrong concerning 10/12-bit Bayer
formats, which are aligned to 16 bits in memory. Using the common
helper fixes it.

Fixes: 5cc7522d8965 ("media: sun6i: Add support for Allwinner CSI V3s")
Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
---
 .../platform/sunxi/sun6i-csi/sun6i_csi.h      | 55 +++++++------------
 1 file changed, 20 insertions(+), 35 deletions(-)

diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
index c626821aaedb..092445f04c60 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
@@ -86,53 +86,38 @@ void sun6i_csi_update_buf_addr(struct sun6i_csi *csi, dma_addr_t addr);
  */
 void sun6i_csi_set_stream(struct sun6i_csi *csi, bool enable);
 
-/* get bpp form v4l2 pixformat */
+/* get memory storage bpp from v4l2 pixformat */
 static inline int sun6i_csi_get_bpp(unsigned int pixformat)
 {
+	const struct v4l2_format_info *info;
+	unsigned int i;
+	int bpp = 0;
+
+	/* Handle special cases unknown to V4L2 format info first. */
 	switch (pixformat) {
-	case V4L2_PIX_FMT_SBGGR8:
-	case V4L2_PIX_FMT_SGBRG8:
-	case V4L2_PIX_FMT_SGRBG8:
-	case V4L2_PIX_FMT_SRGGB8:
 	case V4L2_PIX_FMT_JPEG:
 		return 8;
-	case V4L2_PIX_FMT_SBGGR10:
-	case V4L2_PIX_FMT_SGBRG10:
-	case V4L2_PIX_FMT_SGRBG10:
-	case V4L2_PIX_FMT_SRGGB10:
-		return 10;
-	case V4L2_PIX_FMT_SBGGR12:
-	case V4L2_PIX_FMT_SGBRG12:
-	case V4L2_PIX_FMT_SGRBG12:
-	case V4L2_PIX_FMT_SRGGB12:
 	case V4L2_PIX_FMT_HM12:
-	case V4L2_PIX_FMT_NV12:
-	case V4L2_PIX_FMT_NV21:
-	case V4L2_PIX_FMT_YUV420:
-	case V4L2_PIX_FMT_YVU420:
 		return 12;
-	case V4L2_PIX_FMT_YUYV:
-	case V4L2_PIX_FMT_YVYU:
-	case V4L2_PIX_FMT_UYVY:
-	case V4L2_PIX_FMT_VYUY:
-	case V4L2_PIX_FMT_NV16:
-	case V4L2_PIX_FMT_NV61:
-	case V4L2_PIX_FMT_YUV422P:
-	case V4L2_PIX_FMT_RGB565:
 	case V4L2_PIX_FMT_RGB565X:
 		return 16;
-	case V4L2_PIX_FMT_RGB24:
-	case V4L2_PIX_FMT_BGR24:
-		return 24;
-	case V4L2_PIX_FMT_RGB32:
-	case V4L2_PIX_FMT_BGR32:
-		return 32;
-	default:
+	}
+
+	info = v4l2_format_info(pixformat);
+	if (!info) {
 		WARN(1, "Unsupported pixformat: 0x%x\n", pixformat);
-		break;
+		return 0;
+	}
+
+	for (i = 0; i < info->comp_planes; i++) {
+		unsigned int hdiv = (i == 0) ? 1 : info->hdiv;
+		unsigned int vdiv = (i == 0) ? 1 : info->vdiv;
+
+		/* We return bits per pixel while V4L2 format info is bytes. */
+		bpp += 8 * info->bpp[i] / hdiv / vdiv;
 	}
 
-	return 0;
+	return bpp;
 }
 
 #endif /* __SUN6I_CSI_H__ */
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v2 05/19] media: sun6i-csi: Only configure the interface data width for parallel
  2020-11-28 14:28 [PATCH v2 00/19] Allwinner MIPI CSI-2 support for A31/V3s/A83T Paul Kocialkowski
                   ` (3 preceding siblings ...)
  2020-11-28 14:28 ` [PATCH v2 04/19] media: sun6i-csi: Use common V4L2 format info for storage bpp Paul Kocialkowski
@ 2020-11-28 14:28 ` Paul Kocialkowski
  2020-11-28 14:28 ` [PATCH v2 06/19] dt-bindings: media: sun6i-a31-csi: Add MIPI CSI-2 input port Paul Kocialkowski
                   ` (13 subsequent siblings)
  18 siblings, 0 replies; 39+ messages in thread
From: Paul Kocialkowski @ 2020-11-28 14:28 UTC (permalink / raw)
  To: linux-media, devicetree, linux-arm-kernel, linux-kernel,
	linux-doc, devel, linux-sunxi
  Cc: Yong Deng, Mauro Carvalho Chehab, Rob Herring, Maxime Ripard,
	Chen-Yu Tsai, Jernej Skrabec, Paul Kocialkowski, Jonathan Corbet,
	Kishon Vijay Abraham I, Vinod Koul, Helen Koike,
	Dafna Hirschfeld, Greg Kroah-Hartman, Philipp Zabel,
	Sakari Ailus, Hans Verkuil, Thomas Petazzoni, kevin.lhopital,
	Kévin L'hôpital

Bits related to the interface data width are only applicable to the
parallel interface and are irrelevant when the CSI controller is taking
input from the MIPI CSI-2 controller.

In prevision of adding support for this case, set these bits
conditionally so there is no ambiguity. The conditional block is
moved around before the interlaced conditional block for nicer code
symmetry (conditional blocks first) while at it.

Co-developed-by: Kévin L'hôpital <kevin.lhopital@bootlin.com>
Signed-off-by: Kévin L'hôpital <kevin.lhopital@bootlin.com>
Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
---
 .../platform/sunxi/sun6i-csi/sun6i_csi.c      | 42 +++++++++++--------
 1 file changed, 25 insertions(+), 17 deletions(-)

diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
index 531a4cccd14a..f1150de94e98 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
@@ -378,8 +378,13 @@ static void sun6i_csi_setup_bus(struct sun6i_csi_dev *sdev)
 	unsigned char bus_width;
 	u32 flags;
 	u32 cfg;
+	bool input_parallel = false;
 	bool input_interlaced = false;
 
+	if (endpoint->bus_type == V4L2_MBUS_PARALLEL ||
+	    endpoint->bus_type == V4L2_MBUS_BT656)
+		input_parallel = true;
+
 	if (csi->config.field == V4L2_FIELD_INTERLACED
 	    || csi->config.field == V4L2_FIELD_INTERLACED_TB
 	    || csi->config.field == V4L2_FIELD_INTERLACED_BT)
@@ -395,6 +400,26 @@ static void sun6i_csi_setup_bus(struct sun6i_csi_dev *sdev)
 		 CSI_IF_CFG_HREF_POL_MASK | CSI_IF_CFG_FIELD_MASK |
 		 CSI_IF_CFG_SRC_TYPE_MASK);
 
+	if (input_parallel) {
+		switch (bus_width) {
+		case 8:
+			cfg |= CSI_IF_CFG_IF_DATA_WIDTH_8BIT;
+			break;
+		case 10:
+			cfg |= CSI_IF_CFG_IF_DATA_WIDTH_10BIT;
+			break;
+		case 12:
+			cfg |= CSI_IF_CFG_IF_DATA_WIDTH_12BIT;
+			break;
+		case 16: /* No need to configure DATA_WIDTH for 16bit */
+			break;
+		default:
+			dev_warn(sdev->dev, "Unsupported bus width: %u\n",
+				 bus_width);
+			break;
+		}
+	}
+
 	if (input_interlaced)
 		cfg |= CSI_IF_CFG_SRC_TYPE_INTERLACED;
 	else
@@ -440,23 +465,6 @@ static void sun6i_csi_setup_bus(struct sun6i_csi_dev *sdev)
 		break;
 	}
 
-	switch (bus_width) {
-	case 8:
-		cfg |= CSI_IF_CFG_IF_DATA_WIDTH_8BIT;
-		break;
-	case 10:
-		cfg |= CSI_IF_CFG_IF_DATA_WIDTH_10BIT;
-		break;
-	case 12:
-		cfg |= CSI_IF_CFG_IF_DATA_WIDTH_12BIT;
-		break;
-	case 16: /* No need to configure DATA_WIDTH for 16bit */
-		break;
-	default:
-		dev_warn(sdev->dev, "Unsupported bus width: %u\n", bus_width);
-		break;
-	}
-
 	regmap_write(sdev->regmap, CSI_IF_CFG_REG, cfg);
 }
 
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v2 06/19] dt-bindings: media: sun6i-a31-csi: Add MIPI CSI-2 input port
  2020-11-28 14:28 [PATCH v2 00/19] Allwinner MIPI CSI-2 support for A31/V3s/A83T Paul Kocialkowski
                   ` (4 preceding siblings ...)
  2020-11-28 14:28 ` [PATCH v2 05/19] media: sun6i-csi: Only configure the interface data width for parallel Paul Kocialkowski
@ 2020-11-28 14:28 ` Paul Kocialkowski
  2020-12-01 10:43   ` Maxime Ripard
  2020-11-28 14:28 ` [PATCH v2 07/19] media: sun6i-csi: Add support for MIPI CSI-2 bridge input Paul Kocialkowski
                   ` (12 subsequent siblings)
  18 siblings, 1 reply; 39+ messages in thread
From: Paul Kocialkowski @ 2020-11-28 14:28 UTC (permalink / raw)
  To: linux-media, devicetree, linux-arm-kernel, linux-kernel,
	linux-doc, devel, linux-sunxi
  Cc: Yong Deng, Mauro Carvalho Chehab, Rob Herring, Maxime Ripard,
	Chen-Yu Tsai, Jernej Skrabec, Paul Kocialkowski, Jonathan Corbet,
	Kishon Vijay Abraham I, Vinod Koul, Helen Koike,
	Dafna Hirschfeld, Greg Kroah-Hartman, Philipp Zabel,
	Sakari Ailus, Hans Verkuil, Thomas Petazzoni, kevin.lhopital

The A31 CSI controller supports two distinct input interfaces:
parallel and an external MIPI CSI-2 bridge. The parallel interface
is often connected to a set of hardware pins while the MIPI CSI-2
bridge is an internal FIFO-ish link. As a result, these two inputs
are distinguished as two different ports.

Note that only one of the two may be present on a controller instance.
For example, the V3s has one controller dedicated to MIPI-CSI2 and one
dedicated to parallel.

Update the binding with an explicit ports node that holds two distinct
port nodes: one for parallel input and one for MIPI CSI-2.

This is backward-compatible with the single-port approach that was
previously taken for representing the parallel interface port, which
stays enumerated as fwnode port 0. However, it is now marked as
deprecated and the multi-port approach should be preferred.

Note that additional ports may be added in the future, especially to
support feeding the CSI controller's output to the ISP.

Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
---
 .../media/allwinner,sun6i-a31-csi.yaml        | 86 ++++++++++++++++---
 1 file changed, 73 insertions(+), 13 deletions(-)

diff --git a/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-csi.yaml b/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-csi.yaml
index 1fd9b5532a21..3bcee2d44f3c 100644
--- a/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-csi.yaml
+++ b/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-csi.yaml
@@ -43,6 +43,7 @@ properties:
   # See ./video-interfaces.txt for details
   port:
     type: object
+    deprecated: true
 
     properties:
       endpoint:
@@ -67,6 +68,59 @@ properties:
 
     additionalProperties: false
 
+  ports:
+    type: object
+
+    properties:
+      port@0:
+        type: object
+        description: Parallel input port, connect to a parallel sensor
+
+        properties:
+          reg:
+            const: 0
+
+          endpoint:
+            type: object
+
+            properties:
+              remote-endpoint: true
+
+              bus-width:
+                enum: [ 8, 10, 12, 16 ]
+
+              pclk-sample: true
+              hsync-active: true
+              vsync-active: true
+
+            required:
+              - bus-width
+              - remote-endpoint
+
+        required:
+          - endpoint
+
+        additionalProperties: false
+
+      port@1:
+        type: object
+        description: MIPI CSI-2 bridge input port
+
+        properties:
+          reg:
+            const: 1
+
+          endpoint:
+            type: object
+
+            properties:
+              remote-endpoint: true
+
+            required:
+              - remote-endpoint
+
+        additionalProperties: false
+
 required:
   - compatible
   - reg
@@ -95,19 +149,25 @@ examples:
                       "ram";
         resets = <&ccu RST_BUS_CSI>;
 
-        port {
-            /* Parallel bus endpoint */
-            csi1_ep: endpoint {
-                remote-endpoint = <&adv7611_ep>;
-                bus-width = <16>;
-
-                /*
-                 * If hsync-active/vsync-active are missing,
-                 * embedded BT.656 sync is used.
-                 */
-                 hsync-active = <0>; /* Active low */
-                 vsync-active = <0>; /* Active low */
-                 pclk-sample = <1>;  /* Rising */
+        ports {
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            port@0 {
+                reg = <0>;
+                /* Parallel bus endpoint */
+                csi1_ep: endpoint {
+                    remote-endpoint = <&adv7611_ep>;
+                    bus-width = <16>;
+
+                    /*
+                     * If hsync-active/vsync-active are missing,
+                     * embedded BT.656 sync is used.
+                     */
+                     hsync-active = <0>; /* Active low */
+                     vsync-active = <0>; /* Active low */
+                     pclk-sample = <1>;  /* Rising */
+                };
             };
         };
     };
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v2 07/19] media: sun6i-csi: Add support for MIPI CSI-2 bridge input
  2020-11-28 14:28 [PATCH v2 00/19] Allwinner MIPI CSI-2 support for A31/V3s/A83T Paul Kocialkowski
                   ` (5 preceding siblings ...)
  2020-11-28 14:28 ` [PATCH v2 06/19] dt-bindings: media: sun6i-a31-csi: Add MIPI CSI-2 input port Paul Kocialkowski
@ 2020-11-28 14:28 ` Paul Kocialkowski
  2020-12-01 12:12   ` Maxime Ripard
  2020-11-28 14:28 ` [PATCH v2 08/19] ARM: dts: sun8i: a83t: Add CSI controller ports Paul Kocialkowski
                   ` (11 subsequent siblings)
  18 siblings, 1 reply; 39+ messages in thread
From: Paul Kocialkowski @ 2020-11-28 14:28 UTC (permalink / raw)
  To: linux-media, devicetree, linux-arm-kernel, linux-kernel,
	linux-doc, devel, linux-sunxi
  Cc: Yong Deng, Mauro Carvalho Chehab, Rob Herring, Maxime Ripard,
	Chen-Yu Tsai, Jernej Skrabec, Paul Kocialkowski, Jonathan Corbet,
	Kishon Vijay Abraham I, Vinod Koul, Helen Koike,
	Dafna Hirschfeld, Greg Kroah-Hartman, Philipp Zabel,
	Sakari Ailus, Hans Verkuil, Thomas Petazzoni, kevin.lhopital

The A31 CSI controller supports a MIPI CSI-2 bridge input, which has
its own dedicated port in the fwnode graph.

Support for this input is added with this change:
- two pads are defined for the media entity instead of one
  and only one needs to be connected at a time;
- the pads currently match the fwnode graph representation;
- links are created between our pads and the subdevs for each
  interface and are no longer immutable so that userspace can select
  which interface to use in case both are bound to a subdev;
- fwnode endpoints are parsed and stored for each interface;
- the active subdev (and fwnode endpoint) is retrieved when validating
  the media link at stream on time and cleared at stream off;
- an error is raised if both links are active at the same time;
- the MIPI interface bit is set if the MIPI CSI-2 bridge endpoint is
  active.

In the future, the media entity representation might evolve to:
- distinguish the internal parallel bridge and data formatter;
- represent each of the 4 internal channels that can exist between
  the parallel bridge (for BT656 time-multiplex) and MIPI CSI-2
  (internal channels can be mapped to virtual channels);
- connect the controller's output to the ISP instead of its
  DMA engine.

Finally note that the MIPI CSI-2 bridges should not be linked in
the fwnode graph unless they have a sensor subdev attached.

Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
---
 .../platform/sunxi/sun6i-csi/sun6i_csi.c      | 123 ++++++++++++++----
 .../platform/sunxi/sun6i-csi/sun6i_csi.h      |   3 -
 .../platform/sunxi/sun6i-csi/sun6i_video.c    |  53 ++++----
 .../platform/sunxi/sun6i-csi/sun6i_video.h    |   7 +-
 4 files changed, 135 insertions(+), 51 deletions(-)

diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
index f1150de94e98..481181038e1e 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
@@ -52,15 +52,16 @@ bool sun6i_csi_is_format_supported(struct sun6i_csi *csi,
 				   u32 pixformat, u32 mbus_code)
 {
 	struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
+	struct v4l2_fwnode_endpoint *endpoint = sdev->csi.video.source_endpoint;
 
 	/*
 	 * Some video receivers have the ability to be compatible with
 	 * 8bit and 16bit bus width.
 	 * Identify the media bus format from device tree.
 	 */
-	if ((sdev->csi.v4l2_ep.bus_type == V4L2_MBUS_PARALLEL
-	     || sdev->csi.v4l2_ep.bus_type == V4L2_MBUS_BT656)
-	     && sdev->csi.v4l2_ep.bus.parallel.bus_width == 16) {
+	if ((endpoint->bus_type == V4L2_MBUS_PARALLEL
+	     || endpoint->bus_type == V4L2_MBUS_BT656)
+	     && endpoint->bus.parallel.bus_width == 16) {
 		switch (pixformat) {
 		case V4L2_PIX_FMT_HM12:
 		case V4L2_PIX_FMT_NV12:
@@ -373,7 +374,7 @@ static enum csi_input_seq get_csi_input_seq(struct sun6i_csi_dev *sdev,
 
 static void sun6i_csi_setup_bus(struct sun6i_csi_dev *sdev)
 {
-	struct v4l2_fwnode_endpoint *endpoint = &sdev->csi.v4l2_ep;
+	struct v4l2_fwnode_endpoint *endpoint = sdev->csi.video.source_endpoint;
 	struct sun6i_csi *csi = &sdev->csi;
 	unsigned char bus_width;
 	u32 flags;
@@ -459,6 +460,9 @@ static void sun6i_csi_setup_bus(struct sun6i_csi_dev *sdev)
 		if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
 			cfg |= CSI_IF_CFG_CLK_POL_FALLING_EDGE;
 		break;
+	case V4L2_MBUS_CSI2_DPHY:
+		cfg |= CSI_IF_CFG_MIPI_IF_MIPI;
+		break;
 	default:
 		dev_warn(sdev->dev, "Unsupported bus type: %d\n",
 			 endpoint->bus_type);
@@ -636,11 +640,11 @@ void sun6i_csi_set_stream(struct sun6i_csi *csi, bool enable)
  * Media Controller and V4L2
  */
 static int sun6i_csi_link_entity(struct sun6i_csi *csi,
+				 struct media_pad *sink_pad,
 				 struct media_entity *entity,
-				 struct fwnode_handle *fwnode)
+				 struct fwnode_handle *fwnode, bool enabled)
 {
 	struct media_entity *sink;
-	struct media_pad *sink_pad;
 	int src_pad_index;
 	int ret;
 
@@ -654,14 +658,12 @@ static int sun6i_csi_link_entity(struct sun6i_csi *csi,
 	src_pad_index = ret;
 
 	sink = &csi->video.vdev.entity;
-	sink_pad = &csi->video.pad;
 
 	dev_dbg(csi->dev, "creating %s:%u -> %s:%u link\n",
 		entity->name, src_pad_index, sink->name, sink_pad->index);
 	ret = media_create_pad_link(entity, src_pad_index, sink,
 				    sink_pad->index,
-				    MEDIA_LNK_FL_ENABLED |
-				    MEDIA_LNK_FL_IMMUTABLE);
+				    enabled ? MEDIA_LNK_FL_ENABLED : 0);
 	if (ret < 0) {
 		dev_err(csi->dev, "failed to create %s:%u -> %s:%u link\n",
 			entity->name, src_pad_index,
@@ -676,19 +678,67 @@ static int sun6i_subdev_notify_complete(struct v4l2_async_notifier *notifier)
 {
 	struct sun6i_csi *csi = container_of(notifier, struct sun6i_csi,
 					     notifier);
+	struct sun6i_video *video = &csi->video;
 	struct v4l2_device *v4l2_dev = &csi->v4l2_dev;
-	struct v4l2_subdev *sd;
+	struct v4l2_subdev *parallel_sd = NULL;
+	struct v4l2_subdev *mipi_csi2_bridge_sd = NULL;
+	struct fwnode_handle *handle = NULL;
 	int ret;
 
 	dev_dbg(csi->dev, "notify complete, all subdevs registered\n");
 
-	sd = list_first_entry(&v4l2_dev->subdevs, struct v4l2_subdev, list);
-	if (!sd)
-		return -EINVAL;
+	/* Find the subdevs that match our fwnode ports. */
+	while (1) {
+		struct v4l2_fwnode_link link;
+		struct v4l2_subdev *sd;
 
-	ret = sun6i_csi_link_entity(csi, &sd->entity, sd->fwnode);
-	if (ret < 0)
-		return ret;
+		handle = fwnode_graph_get_next_endpoint(dev_fwnode(csi->dev),
+							handle);
+		if (!handle)
+			break;
+
+		ret = v4l2_fwnode_parse_link(handle, &link);
+		if (ret)
+			break;
+
+		list_for_each_entry(sd, &v4l2_dev->subdevs, list) {
+			if (!sd->fwnode || link.remote_node != sd->fwnode)
+				continue;
+
+			switch (link.local_port) {
+			case 0:
+				parallel_sd = sd;
+				break;
+			case 1:
+				mipi_csi2_bridge_sd = sd;
+				break;
+			}
+		}
+
+		v4l2_fwnode_put_link(&link);
+	}
+
+	if (parallel_sd) {
+		dev_dbg(csi->dev, "linking parallel interface subdev\n");
+
+		ret = sun6i_csi_link_entity(csi, &video->pads[0],
+					    &parallel_sd->entity,
+					    parallel_sd->fwnode, true);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (mipi_csi2_bridge_sd) {
+		dev_dbg(csi->dev, "linking MIPI CSI-2 bridge subdev\n");
+
+		/* Mark the link as disabled if a parallel subdev is there. */
+		ret = sun6i_csi_link_entity(csi, &video->pads[1],
+					    &mipi_csi2_bridge_sd->entity,
+					    mipi_csi2_bridge_sd->fwnode,
+					    parallel_sd ? false : true);
+		if (ret < 0)
+			return ret;
+	}
 
 	ret = v4l2_device_register_subdev_nodes(&csi->v4l2_dev);
 	if (ret < 0)
@@ -706,21 +756,44 @@ static int sun6i_csi_fwnode_parse(struct device *dev,
 				  struct v4l2_async_subdev *asd)
 {
 	struct sun6i_csi *csi = dev_get_drvdata(dev);
+	struct sun6i_video *video = &csi->video;
+	struct v4l2_fwnode_endpoint *endpoint = NULL;
+
+	switch (vep->base.port) {
+	case 0:
+		endpoint = &video->parallel_endpoint;
 
-	if (vep->base.port || vep->base.id) {
-		dev_warn(dev, "Only support a single port with one endpoint\n");
+		switch (vep->bus_type) {
+		case V4L2_MBUS_PARALLEL:
+		case V4L2_MBUS_BT656:
+			break;
+		default:
+			dev_err(dev, "Unsupported media bus type\n");
+			return -ENOTCONN;
+		}
+		break;
+	case 1:
+		endpoint = &video->mipi_csi2_bridge_endpoint;
+		break;
+	default:
+		dev_warn(dev, "Unsupported port at index %u\n", vep->base.port);
 		return -ENOTCONN;
 	}
 
-	switch (vep->bus_type) {
-	case V4L2_MBUS_PARALLEL:
-	case V4L2_MBUS_BT656:
-		csi->v4l2_ep = *vep;
-		return 0;
-	default:
-		dev_err(dev, "Unsupported media bus type\n");
+	if (vep->base.id) {
+		dev_warn(dev, "Unsupported endpoint at index %u for port %u\n",
+			 vep->base.id, vep->base.port);
 		return -ENOTCONN;
 	}
+
+	*endpoint = *vep;
+
+	if (vep->base.port == 1) {
+		/* Set this for our local convenience. */
+		endpoint->bus_type = V4L2_MBUS_CSI2_DPHY;
+	}
+
+	return 0;
 }
 
 static void sun6i_csi_v4l2_cleanup(struct sun6i_csi *csi)
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
index 092445f04c60..d7b15452ab3a 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
@@ -40,9 +40,6 @@ struct sun6i_csi {
 
 	struct v4l2_async_notifier	notifier;
 
-	/* video port settings */
-	struct v4l2_fwnode_endpoint	v4l2_ep;
-
 	struct sun6i_csi_config		config;
 
 	struct sun6i_video		video;
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c
index b55de9ab64d8..5bb7c013f6bb 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c
@@ -72,22 +72,6 @@ static bool is_pixformat_valid(unsigned int pixformat)
 	return false;
 }
 
-static struct v4l2_subdev *
-sun6i_video_remote_subdev(struct sun6i_video *video, u32 *pad)
-{
-	struct media_pad *remote;
-
-	remote = media_entity_remote_pad(&video->pad);
-
-	if (!remote || !is_media_entity_v4l2_subdev(remote->entity))
-		return NULL;
-
-	if (pad)
-		*pad = remote->index;
-
-	return media_entity_to_v4l2_subdev(remote->entity);
-}
-
 static int sun6i_video_queue_setup(struct vb2_queue *vq,
 				   unsigned int *nbuffers,
 				   unsigned int *nplanes,
@@ -150,7 +134,7 @@ static int sun6i_video_start_streaming(struct vb2_queue *vq, unsigned int count)
 		goto stop_media_pipeline;
 	}
 
-	subdev = sun6i_video_remote_subdev(video, NULL);
+	subdev = video->source_subdev;
 	if (!subdev)
 		goto stop_media_pipeline;
 
@@ -206,6 +190,7 @@ static int sun6i_video_start_streaming(struct vb2_queue *vq, unsigned int count)
 	sun6i_csi_set_stream(video->csi, false);
 stop_media_pipeline:
 	media_pipeline_stop(&video->vdev.entity);
+	video->source_subdev = NULL;
 clear_dma_queue:
 	spin_lock_irqsave(&video->dma_queue_lock, flags);
 	list_for_each_entry(buf, &video->dma_queue, list)
@@ -223,13 +208,16 @@ static void sun6i_video_stop_streaming(struct vb2_queue *vq)
 	unsigned long flags;
 	struct sun6i_csi_buffer *buf;
 
-	subdev = sun6i_video_remote_subdev(video, NULL);
+	subdev = video->source_subdev;
 	if (subdev)
 		v4l2_subdev_call(subdev, video, s_stream, 0);
 
 	sun6i_csi_set_stream(video->csi, false);
 
-	media_pipeline_stop(&video->vdev.entity);
+	if (subdev)
+		media_pipeline_stop(&video->vdev.entity);
+
+	video->source_subdev = NULL;
 
 	/* Release all active buffers */
 	spin_lock_irqsave(&video->dma_queue_lock, flags);
@@ -550,16 +538,36 @@ static int sun6i_video_link_validate(struct media_link *link)
 						 struct video_device, entity);
 	struct sun6i_video *video = video_get_drvdata(vdev);
 	struct v4l2_subdev_format source_fmt;
+	struct v4l2_subdev *subdev;
 	int ret;
 
 	video->mbus_code = 0;
 
-	if (!media_entity_remote_pad(link->sink->entity->pads)) {
+	if (!link->source) {
 		dev_info(video->csi->dev,
 			 "video node %s pad not connected\n", vdev->name);
 		return -ENOLINK;
 	}
 
+	if (!is_media_entity_v4l2_subdev(link->source->entity))
+		return -ENODEV;
+
+	subdev = media_entity_to_v4l2_subdev(link->source->entity);
+
+	if (video->source_subdev) {
+		dev_err(video->csi->dev,
+			"unable to connect both parallel and MIPI CSI-2 bridge interfaces\n");
+		return -ENOLINK;
+	}
+
+	if (link->sink == &video->pads[0]) {
+		video->source_endpoint = &video->parallel_endpoint;
+		video->source_subdev = subdev;
+	} else if (link->sink == &video->pads[1]) {
+		video->source_endpoint = &video->mipi_csi2_bridge_endpoint;
+		video->source_subdev = subdev;
+	}
+
 	ret = sun6i_video_link_validate_get_format(link->source, &source_fmt);
 	if (ret < 0)
 		return ret;
@@ -603,9 +611,10 @@ int sun6i_video_init(struct sun6i_video *video, struct sun6i_csi *csi,
 	video->csi = csi;
 
 	/* Initialize the media entity... */
-	video->pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
+	video->pads[0].flags = MEDIA_PAD_FL_SINK;
+	video->pads[1].flags = MEDIA_PAD_FL_SINK;
 	vdev->entity.ops = &sun6i_video_media_ops;
-	ret = media_entity_pads_init(&vdev->entity, 1, &video->pad);
+	ret = media_entity_pads_init(&vdev->entity, 2, video->pads);
 	if (ret < 0)
 		return ret;
 
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.h
index b9cd919c24ac..30d56d98d5e9 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.h
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.h
@@ -15,11 +15,16 @@ struct sun6i_csi;
 
 struct sun6i_video {
 	struct video_device		vdev;
-	struct media_pad		pad;
+	struct media_pad		pads[2];
 	struct sun6i_csi		*csi;
 
 	struct mutex			lock;
 
+	struct v4l2_fwnode_endpoint	parallel_endpoint;
+	struct v4l2_fwnode_endpoint	mipi_csi2_bridge_endpoint;
+	struct v4l2_fwnode_endpoint	*source_endpoint;
+	struct v4l2_subdev		*source_subdev;
+
 	struct vb2_queue		vb2_vidq;
 	spinlock_t			dma_queue_lock;
 	struct list_head		dma_queue;
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v2 08/19] ARM: dts: sun8i: a83t: Add CSI controller ports
  2020-11-28 14:28 [PATCH v2 00/19] Allwinner MIPI CSI-2 support for A31/V3s/A83T Paul Kocialkowski
                   ` (6 preceding siblings ...)
  2020-11-28 14:28 ` [PATCH v2 07/19] media: sun6i-csi: Add support for MIPI CSI-2 bridge input Paul Kocialkowski
@ 2020-11-28 14:28 ` Paul Kocialkowski
  2020-11-28 14:28 ` [PATCH v2 09/19] ARM: dts: sunxi: h3/h5: Add CSI controller port for parallel input Paul Kocialkowski
                   ` (10 subsequent siblings)
  18 siblings, 0 replies; 39+ messages in thread
From: Paul Kocialkowski @ 2020-11-28 14:28 UTC (permalink / raw)
  To: linux-media, devicetree, linux-arm-kernel, linux-kernel,
	linux-doc, devel, linux-sunxi
  Cc: Yong Deng, Mauro Carvalho Chehab, Rob Herring, Maxime Ripard,
	Chen-Yu Tsai, Jernej Skrabec, Paul Kocialkowski, Jonathan Corbet,
	Kishon Vijay Abraham I, Vinod Koul, Helen Koike,
	Dafna Hirschfeld, Greg Kroah-Hartman, Philipp Zabel,
	Sakari Ailus, Hans Verkuil, Thomas Petazzoni, kevin.lhopital

Since the CSI controller binding is getting a bit more complex due
to the addition of MIPI CSI-2 bridge support, make the ports node
explicit with the parallel and MIPI CSI-2 bridge ports.

This way, it's clear that the controller supports both parallel and
MIPI CSI-2 interface inputs and there's no confusion about their port
number.

Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
---
 arch/arm/boot/dts/sun8i-a83t.dtsi | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/sun8i-a83t.dtsi b/arch/arm/boot/dts/sun8i-a83t.dtsi
index c010b27fdb6a..3ce030f7e05d 100644
--- a/arch/arm/boot/dts/sun8i-a83t.dtsi
+++ b/arch/arm/boot/dts/sun8i-a83t.dtsi
@@ -1062,7 +1062,17 @@ csi: camera@1cb0000 {
 			resets = <&ccu RST_BUS_CSI>;
 			status = "disabled";
 
-			csi_in: port {
+			ports {
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				csi_in_parallel: port@0 {
+					reg = <0>;
+				};
+
+				csi_in_mipi_csi2_bridge: port@1 {
+					reg = <1>;
+				};
 			};
 		};
 
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v2 09/19] ARM: dts: sunxi: h3/h5: Add CSI controller port for parallel input
  2020-11-28 14:28 [PATCH v2 00/19] Allwinner MIPI CSI-2 support for A31/V3s/A83T Paul Kocialkowski
                   ` (7 preceding siblings ...)
  2020-11-28 14:28 ` [PATCH v2 08/19] ARM: dts: sun8i: a83t: Add CSI controller ports Paul Kocialkowski
@ 2020-11-28 14:28 ` Paul Kocialkowski
  2020-12-01 12:14   ` Maxime Ripard
  2020-11-28 14:28 ` [PATCH v2 10/19] ARM: dts: sun8i: v3s: Add CSI1 " Paul Kocialkowski
                   ` (9 subsequent siblings)
  18 siblings, 1 reply; 39+ messages in thread
From: Paul Kocialkowski @ 2020-11-28 14:28 UTC (permalink / raw)
  To: linux-media, devicetree, linux-arm-kernel, linux-kernel,
	linux-doc, devel, linux-sunxi
  Cc: Yong Deng, Mauro Carvalho Chehab, Rob Herring, Maxime Ripard,
	Chen-Yu Tsai, Jernej Skrabec, Paul Kocialkowski, Jonathan Corbet,
	Kishon Vijay Abraham I, Vinod Koul, Helen Koike,
	Dafna Hirschfeld, Greg Kroah-Hartman, Philipp Zabel,
	Sakari Ailus, Hans Verkuil, Thomas Petazzoni, kevin.lhopital

Since the CSI controller binding is getting a bit more complex due
to the addition of MIPI CSI-2 bridge support, make the ports node
explicit with the parallel port.

This way, it's clear that the controller only supports parallel
interface input and there's no confusion about the port number.

Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
---
 arch/arm/boot/dts/sunxi-h3-h5.dtsi | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/arch/arm/boot/dts/sunxi-h3-h5.dtsi b/arch/arm/boot/dts/sunxi-h3-h5.dtsi
index 9be13378d4df..02b698cace6a 100644
--- a/arch/arm/boot/dts/sunxi-h3-h5.dtsi
+++ b/arch/arm/boot/dts/sunxi-h3-h5.dtsi
@@ -803,6 +803,15 @@ csi: camera@1cb0000 {
 			pinctrl-names = "default";
 			pinctrl-0 = <&csi_pins>;
 			status = "disabled";
+
+			ports {
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				csi_in_parallel: port@0 {
+					reg = <0>;
+				};
+			};
 		};
 
 		hdmi: hdmi@1ee0000 {
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v2 10/19] ARM: dts: sun8i: v3s: Add CSI1 controller port for parallel input
  2020-11-28 14:28 [PATCH v2 00/19] Allwinner MIPI CSI-2 support for A31/V3s/A83T Paul Kocialkowski
                   ` (8 preceding siblings ...)
  2020-11-28 14:28 ` [PATCH v2 09/19] ARM: dts: sunxi: h3/h5: Add CSI controller port for parallel input Paul Kocialkowski
@ 2020-11-28 14:28 ` Paul Kocialkowski
  2020-11-28 14:28 ` [PATCH v2 11/19] arm64: dts: allwinner: a64: Add CSI " Paul Kocialkowski
                   ` (8 subsequent siblings)
  18 siblings, 0 replies; 39+ messages in thread
From: Paul Kocialkowski @ 2020-11-28 14:28 UTC (permalink / raw)
  To: linux-media, devicetree, linux-arm-kernel, linux-kernel,
	linux-doc, devel, linux-sunxi
  Cc: Yong Deng, Mauro Carvalho Chehab, Rob Herring, Maxime Ripard,
	Chen-Yu Tsai, Jernej Skrabec, Paul Kocialkowski, Jonathan Corbet,
	Kishon Vijay Abraham I, Vinod Koul, Helen Koike,
	Dafna Hirschfeld, Greg Kroah-Hartman, Philipp Zabel,
	Sakari Ailus, Hans Verkuil, Thomas Petazzoni, kevin.lhopital

Since the CSI controller binding is getting a bit more complex due
to the addition of MIPI CSI-2 bridge support, make the ports node
explicit with the parallel port.

This way, it's clear that the controller only supports parallel
interface input and there's no confusion about the port number.

Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
---
 arch/arm/boot/dts/sun8i-v3s.dtsi | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/arch/arm/boot/dts/sun8i-v3s.dtsi b/arch/arm/boot/dts/sun8i-v3s.dtsi
index 7b2d684aeb97..7926c8b2ac5e 100644
--- a/arch/arm/boot/dts/sun8i-v3s.dtsi
+++ b/arch/arm/boot/dts/sun8i-v3s.dtsi
@@ -540,6 +540,15 @@ csi1: camera@1cb4000 {
 			clock-names = "bus", "mod", "ram";
 			resets = <&ccu RST_BUS_CSI>;
 			status = "disabled";
+
+			ports {
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				csi1_in_parallel: port@0 {
+					reg = <0>;
+				};
+			};
 		};
 
 		gic: interrupt-controller@1c81000 {
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v2 11/19] arm64: dts: allwinner: a64: Add CSI controller port for parallel input
  2020-11-28 14:28 [PATCH v2 00/19] Allwinner MIPI CSI-2 support for A31/V3s/A83T Paul Kocialkowski
                   ` (9 preceding siblings ...)
  2020-11-28 14:28 ` [PATCH v2 10/19] ARM: dts: sun8i: v3s: Add CSI1 " Paul Kocialkowski
@ 2020-11-28 14:28 ` Paul Kocialkowski
  2020-11-28 14:28 ` [PATCH v2 12/19] dt-bindings: media: Add A31 MIPI CSI-2 bindings documentation Paul Kocialkowski
                   ` (7 subsequent siblings)
  18 siblings, 0 replies; 39+ messages in thread
From: Paul Kocialkowski @ 2020-11-28 14:28 UTC (permalink / raw)
  To: linux-media, devicetree, linux-arm-kernel, linux-kernel,
	linux-doc, devel, linux-sunxi
  Cc: Yong Deng, Mauro Carvalho Chehab, Rob Herring, Maxime Ripard,
	Chen-Yu Tsai, Jernej Skrabec, Paul Kocialkowski, Jonathan Corbet,
	Kishon Vijay Abraham I, Vinod Koul, Helen Koike,
	Dafna Hirschfeld, Greg Kroah-Hartman, Philipp Zabel,
	Sakari Ailus, Hans Verkuil, Thomas Petazzoni, kevin.lhopital

Since the CSI controller binding is getting a bit more complex due
to the addition of MIPI CSI-2 bridge support, make the ports node
explicit with the parallel port.

This way, it's clear that the controller only supports parallel
interface input and there's no confusion about the port number.

Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
---
 arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
index 51cc30e84e26..1e1f0d2097d5 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
@@ -1109,6 +1109,15 @@ csi: csi@1cb0000 {
 			pinctrl-names = "default";
 			pinctrl-0 = <&csi_pins>;
 			status = "disabled";
+
+			ports {
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				csi_in_parallel: port@0 {
+					reg = <0>;
+				};
+			};
 		};
 
 		dsi: dsi@1ca0000 {
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v2 12/19] dt-bindings: media: Add A31 MIPI CSI-2 bindings documentation
  2020-11-28 14:28 [PATCH v2 00/19] Allwinner MIPI CSI-2 support for A31/V3s/A83T Paul Kocialkowski
                   ` (10 preceding siblings ...)
  2020-11-28 14:28 ` [PATCH v2 11/19] arm64: dts: allwinner: a64: Add CSI " Paul Kocialkowski
@ 2020-11-28 14:28 ` Paul Kocialkowski
  2020-12-01 12:15   ` Maxime Ripard
  2020-11-28 14:28 ` [PATCH v2 13/19] media: sunxi: Add support for the A31 MIPI CSI-2 controller Paul Kocialkowski
                   ` (6 subsequent siblings)
  18 siblings, 1 reply; 39+ messages in thread
From: Paul Kocialkowski @ 2020-11-28 14:28 UTC (permalink / raw)
  To: linux-media, devicetree, linux-arm-kernel, linux-kernel,
	linux-doc, devel, linux-sunxi
  Cc: Yong Deng, Mauro Carvalho Chehab, Rob Herring, Maxime Ripard,
	Chen-Yu Tsai, Jernej Skrabec, Paul Kocialkowski, Jonathan Corbet,
	Kishon Vijay Abraham I, Vinod Koul, Helen Koike,
	Dafna Hirschfeld, Greg Kroah-Hartman, Philipp Zabel,
	Sakari Ailus, Hans Verkuil, Thomas Petazzoni, kevin.lhopital

This introduces YAML bindings documentation for the A31 MIPI CSI-2
controller.

Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
---
 .../media/allwinner,sun6i-a31-mipi-csi2.yaml  | 151 ++++++++++++++++++
 1 file changed, 151 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/allwinner,sun6i-a31-mipi-csi2.yaml

diff --git a/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-mipi-csi2.yaml b/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-mipi-csi2.yaml
new file mode 100644
index 000000000000..917cd09d6fda
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-mipi-csi2.yaml
@@ -0,0 +1,151 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/allwinner,sun6i-a31-mipi-csi2.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Allwinner A31 MIPI CSI-2 Device Tree Bindings
+
+maintainers:
+  - Paul Kocialkowski <paul.kocialkowski@bootlin.com>
+
+properties:
+  compatible:
+    oneOf:
+      - const: allwinner,sun6i-a31-mipi-csi2
+      - items:
+          - const: allwinner,sun8i-v3s-mipi-csi2
+          - const: allwinner,sun6i-a31-mipi-csi2
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    items:
+      - description: Bus Clock
+      - description: Module Clock
+
+  clock-names:
+    items:
+      - const: bus
+      - const: mod
+
+  phys:
+    items:
+      - description: MIPI D-PHY
+
+  phy-names:
+    items:
+      - const: dphy
+
+  resets:
+    maxItems: 1
+
+  # See ./video-interfaces.txt for details
+  ports:
+    type: object
+
+    properties:
+      port@0:
+        type: object
+        description: Input port, connect to a MIPI CSI-2 sensor
+
+        properties:
+          reg:
+            const: 0
+
+          endpoint:
+            type: object
+
+            properties:
+              remote-endpoint: true
+
+              data-lanes:
+                minItems: 1
+                maxItems: 4
+
+            required:
+              - data-lanes
+              - remote-endpoint
+
+        required:
+          - endpoint
+
+        additionalProperties: false
+
+      port@1:
+        type: object
+        description: Output port, connect to a CSI controller
+
+        properties:
+          reg:
+            const: 1
+
+          endpoint:
+            type: object
+
+            properties:
+              remote-endpoint: true
+
+        required:
+          - endpoint
+
+        additionalProperties: false
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - clock-names
+  - resets
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/clock/sun8i-v3s-ccu.h>
+    #include <dt-bindings/reset/sun8i-v3s-ccu.h>
+
+    mipi_csi2: csi@1cb1000 {
+        compatible = "allwinner,sun8i-v3s-mipi-csi2",
+                     "allwinner,sun6i-a31-mipi-csi2";
+        reg = <0x01cb1000 0x1000>;
+        interrupts = <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>;
+        clocks = <&ccu CLK_BUS_CSI>,
+                 <&ccu CLK_CSI1_SCLK>;
+        clock-names = "bus", "mod";
+        resets = <&ccu RST_BUS_CSI>;
+
+        phys = <&dphy>;
+        phy-names = "dphy";
+
+        ports {
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            mipi_csi2_in: port@0 {
+                reg = <0>;
+
+                mipi_csi2_in_ov5648: endpoint {
+                    data-lanes = <1 2 3 4>;
+
+                    remote-endpoint = <&ov5648_out_mipi_csi2>;
+                };
+            };
+
+            mipi_csi2_out: port@1 {
+                reg = <1>;
+
+                mipi_csi2_out_csi0: endpoint {
+                    remote-endpoint = <&csi0_in_mipi_csi2>;
+                };
+            };
+        };
+    };
+
+...
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v2 13/19] media: sunxi: Add support for the A31 MIPI CSI-2 controller
  2020-11-28 14:28 [PATCH v2 00/19] Allwinner MIPI CSI-2 support for A31/V3s/A83T Paul Kocialkowski
                   ` (11 preceding siblings ...)
  2020-11-28 14:28 ` [PATCH v2 12/19] dt-bindings: media: Add A31 MIPI CSI-2 bindings documentation Paul Kocialkowski
@ 2020-11-28 14:28 ` Paul Kocialkowski
  2020-12-01 12:20   ` Maxime Ripard
  2020-11-28 14:28 ` [PATCH v2 14/19] ARM: dts: sun8i: v3s: Add nodes for MIPI CSI-2 support Paul Kocialkowski
                   ` (5 subsequent siblings)
  18 siblings, 1 reply; 39+ messages in thread
From: Paul Kocialkowski @ 2020-11-28 14:28 UTC (permalink / raw)
  To: linux-media, devicetree, linux-arm-kernel, linux-kernel,
	linux-doc, devel, linux-sunxi
  Cc: Yong Deng, Mauro Carvalho Chehab, Rob Herring, Maxime Ripard,
	Chen-Yu Tsai, Jernej Skrabec, Paul Kocialkowski, Jonathan Corbet,
	Kishon Vijay Abraham I, Vinod Koul, Helen Koike,
	Dafna Hirschfeld, Greg Kroah-Hartman, Philipp Zabel,
	Sakari Ailus, Hans Verkuil, Thomas Petazzoni, kevin.lhopital

The A31 MIPI CSI-2 controller is a dedicated MIPI CSI-2 bridge
found on Allwinner SoCs such as the A31 and V3/V3s.

It is a standalone block, connected to the CSI controller on one side
and to the MIPI D-PHY block on the other. It has a dedicated address
space, interrupt line and clock.

It is represented as a V4L2 subdev to the CSI controller and takes a
MIPI CSI-2 sensor as its own subdev, all using the fwnode graph and
media controller API.

Only 8-bit and 10-bit Bayer formats are currently supported.
While up to 4 internal channels to the CSI controller exist, only one
is currently supported by this implementation.

Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
---
 drivers/media/platform/sunxi/Kconfig          |   1 +
 drivers/media/platform/sunxi/Makefile         |   1 +
 .../platform/sunxi/sun6i-mipi-csi2/Kconfig    |  12 +
 .../platform/sunxi/sun6i-mipi-csi2/Makefile   |   4 +
 .../sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c   | 591 ++++++++++++++++++
 .../sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.h   | 117 ++++
 6 files changed, 726 insertions(+)
 create mode 100644 drivers/media/platform/sunxi/sun6i-mipi-csi2/Kconfig
 create mode 100644 drivers/media/platform/sunxi/sun6i-mipi-csi2/Makefile
 create mode 100644 drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c
 create mode 100644 drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.h

diff --git a/drivers/media/platform/sunxi/Kconfig b/drivers/media/platform/sunxi/Kconfig
index 7151cc249afa..9684e07454ad 100644
--- a/drivers/media/platform/sunxi/Kconfig
+++ b/drivers/media/platform/sunxi/Kconfig
@@ -2,3 +2,4 @@
 
 source "drivers/media/platform/sunxi/sun4i-csi/Kconfig"
 source "drivers/media/platform/sunxi/sun6i-csi/Kconfig"
+source "drivers/media/platform/sunxi/sun6i-mipi-csi2/Kconfig"
diff --git a/drivers/media/platform/sunxi/Makefile b/drivers/media/platform/sunxi/Makefile
index fc537c9f5ca9..887a7cae8fca 100644
--- a/drivers/media/platform/sunxi/Makefile
+++ b/drivers/media/platform/sunxi/Makefile
@@ -2,5 +2,6 @@
 
 obj-y		+= sun4i-csi/
 obj-y		+= sun6i-csi/
+obj-y		+= sun6i-mipi-csi2/
 obj-y		+= sun8i-di/
 obj-y		+= sun8i-rotate/
diff --git a/drivers/media/platform/sunxi/sun6i-mipi-csi2/Kconfig b/drivers/media/platform/sunxi/sun6i-mipi-csi2/Kconfig
new file mode 100644
index 000000000000..3260591ed5c0
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun6i-mipi-csi2/Kconfig
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config VIDEO_SUN6I_MIPI_CSI2
+	tristate "Allwinner A31 MIPI CSI-2 Controller Driver"
+	depends on VIDEO_V4L2 && COMMON_CLK
+	depends on ARCH_SUNXI || COMPILE_TEST
+	select PHY_SUN6I_MIPI_DPHY
+	select MEDIA_CONTROLLER
+	select VIDEO_V4L2_SUBDEV_API
+	select REGMAP_MMIO
+	select V4L2_FWNODE
+	help
+	   Support for the Allwinner A31 MIPI CSI-2 Controller.
diff --git a/drivers/media/platform/sunxi/sun6i-mipi-csi2/Makefile b/drivers/media/platform/sunxi/sun6i-mipi-csi2/Makefile
new file mode 100644
index 000000000000..14e4e03818b5
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun6i-mipi-csi2/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+sun6i-mipi-csi2-y += sun6i_mipi_csi2.o
+
+obj-$(CONFIG_VIDEO_SUN6I_MIPI_CSI2) += sun6i-mipi-csi2.o
diff --git a/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c b/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c
new file mode 100644
index 000000000000..a6567ef82fb4
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c
@@ -0,0 +1,591 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2020 Bootlin
+ * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+
+#include "sun6i_mipi_csi2.h"
+
+#define MODULE_NAME	"sun6i-mipi-csi2"
+
+static const u32 sun6i_mipi_csi2_mbus_codes[] = {
+	MEDIA_BUS_FMT_SBGGR8_1X8,
+	MEDIA_BUS_FMT_SGBRG8_1X8,
+	MEDIA_BUS_FMT_SGRBG8_1X8,
+	MEDIA_BUS_FMT_SRGGB8_1X8,
+	MEDIA_BUS_FMT_SBGGR10_1X10,
+	MEDIA_BUS_FMT_SGBRG10_1X10,
+	MEDIA_BUS_FMT_SGRBG10_1X10,
+	MEDIA_BUS_FMT_SRGGB10_1X10,
+};
+
+/* Video */
+
+static int sun6i_mipi_csi2_s_stream(struct v4l2_subdev *subdev, int on)
+{
+	struct sun6i_mipi_csi2_video *video =
+		sun6i_mipi_csi2_subdev_video(subdev);
+	struct sun6i_mipi_csi2_dev *cdev = sun6i_mipi_csi2_video_dev(video);
+	struct v4l2_subdev *remote_subdev = video->remote_subdev;
+	struct v4l2_fwnode_bus_mipi_csi2 *bus_mipi_csi2 =
+		&video->endpoint.bus.mipi_csi2;
+	union phy_configure_opts dphy_opts = { 0 };
+	struct phy_configure_opts_mipi_dphy *dphy_cfg = &dphy_opts.mipi_dphy;
+	struct regmap *regmap = cdev->regmap;
+	struct v4l2_ctrl *ctrl;
+	unsigned int lanes_count;
+	unsigned int bpp;
+	unsigned long pixel_rate;
+	u8 data_type = 0;
+	u32 version = 0;
+	/* Initialize to 0 to use both in disable label (ret != 0) and off. */
+	int ret = 0;
+
+	if (!remote_subdev)
+		return -ENODEV;
+
+	if (!on) {
+		v4l2_subdev_call(remote_subdev, video, s_stream, 0);
+		goto disable;
+	}
+
+	switch (video->mbus_format.code) {
+	case MEDIA_BUS_FMT_SBGGR8_1X8:
+	case MEDIA_BUS_FMT_SGBRG8_1X8:
+	case MEDIA_BUS_FMT_SGRBG8_1X8:
+	case MEDIA_BUS_FMT_SRGGB8_1X8:
+		data_type = MIPI_CSI2_DATA_TYPE_RAW8;
+		bpp = 8;
+		break;
+	case MEDIA_BUS_FMT_SBGGR10_1X10:
+	case MEDIA_BUS_FMT_SGBRG10_1X10:
+	case MEDIA_BUS_FMT_SGRBG10_1X10:
+	case MEDIA_BUS_FMT_SRGGB10_1X10:
+		data_type = MIPI_CSI2_DATA_TYPE_RAW10;
+		bpp = 10;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* Sensor pixel rate */
+
+	ctrl = v4l2_ctrl_find(remote_subdev->ctrl_handler, V4L2_CID_PIXEL_RATE);
+	if (!ctrl) {
+		dev_err(cdev->dev,
+			"%s: no MIPI CSI-2 pixel rate from the sensor\n",
+			__func__);
+		return -ENODEV;
+	}
+
+	pixel_rate = (unsigned long)v4l2_ctrl_g_ctrl_int64(ctrl);
+	if (!pixel_rate) {
+		dev_err(cdev->dev,
+			"%s: zero MIPI CSI-2 pixel rate from the sensor\n",
+			__func__);
+		return -ENODEV;
+	}
+
+	/* Power management */
+
+	ret = pm_runtime_get_sync(cdev->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(cdev->dev);
+		return ret;
+	}
+
+	/* D-PHY configuration */
+
+	lanes_count = bus_mipi_csi2->num_data_lanes;
+	phy_mipi_dphy_get_default_config(pixel_rate, bpp, lanes_count,
+					 dphy_cfg);
+
+	/*
+	 * Note that our hardware is using DDR, which is not taken in account by
+	 * phy_mipi_dphy_get_default_config when calculating hs_clk_rate from
+	 * the pixel rate, lanes count and bpp.
+	 *
+	 * The resulting clock rate is basically the symbol rate over the whole
+	 * link. The actual clock rate is calculated with division by two since
+	 * DDR samples both on rising and falling edges.
+	 */
+
+	dev_dbg(cdev->dev, "A31 MIPI CSI-2 config:\n");
+	dev_dbg(cdev->dev, "%ld pixels/s, %u bits/pixel, %lu Hz clock\n",
+		pixel_rate, bpp, dphy_cfg->hs_clk_rate / 2);
+
+	ret = phy_reset(cdev->dphy);
+	if (ret) {
+		dev_err(cdev->dev, "failed to reset MIPI D-PHY\n");
+		goto error_pm;
+	}
+
+	ret = phy_set_mode_ext(cdev->dphy, PHY_MODE_MIPI_DPHY,
+			       PHY_MIPI_DPHY_SUBMODE_RX);
+	if (ret) {
+		dev_err(cdev->dev, "failed to set MIPI D-PHY mode\n");
+		goto error_pm;
+	}
+
+	ret = phy_configure(cdev->dphy, &dphy_opts);
+	if (ret) {
+		dev_err(cdev->dev, "failed to configure MIPI D-PHY\n");
+		goto error_pm;
+	}
+
+	ret = phy_power_on(cdev->dphy);
+	if (ret) {
+		dev_err(cdev->dev, "failed to power on MIPI D-PHY\n");
+		goto error_pm;
+	}
+
+	/* MIPI CSI-2 controller setup */
+
+	/*
+	 * The enable flow in the Allwinner BSP is a bit different: the enable
+	 * and reset bits are set together before starting the CSI controller.
+	 *
+	 * In mainline we enable the CSI controller first (due to subdev logic).
+	 * One reliable way to make this work is to deassert reset, configure
+	 * registers and enable the controller when everything's ready.
+	 *
+	 * However, setting the version enable bit and removing it afterwards
+	 * appears necessary for capture to work reliably, while replacing it
+	 * with a delay doesn't do the trick.
+	 */
+	regmap_write(regmap, SUN6I_MIPI_CSI2_CTL_REG,
+		     SUN6I_MIPI_CSI2_CTL_RESET_N |
+		     SUN6I_MIPI_CSI2_CTL_VERSION_EN |
+		     SUN6I_MIPI_CSI2_CTL_UNPK_EN);
+
+	regmap_read(regmap, SUN6I_MIPI_CSI2_VERSION_REG, &version);
+
+	regmap_update_bits(regmap, SUN6I_MIPI_CSI2_CTL_REG,
+				   SUN6I_MIPI_CSI2_CTL_VERSION_EN, 0);
+
+	dev_dbg(cdev->dev, "A31 MIPI CSI-2 version: %04x\n", version);
+
+	regmap_write(regmap, SUN6I_MIPI_CSI2_CFG_REG,
+		     SUN6I_MIPI_CSI2_CFG_CHANNEL_MODE(1) |
+		     SUN6I_MIPI_CSI2_CFG_LANE_COUNT(lanes_count));
+
+	/*
+	 * Our MIPI CSI-2 controller has internal channels that can be
+	 * configured to match a specific MIPI CSI-2 virtual channel and/or
+	 * a specific data type. Each internal channel can be piped to an
+	 * internal channel of the CSI controller.
+	 *
+	 * We set virtual channel numbers to all channels to make sure that
+	 * virtual channel 0 goes to CSI channel 0 only.
+	 */
+	regmap_write(regmap, SUN6I_MIPI_CSI2_VCDT_RX_REG,
+		     SUN6I_MIPI_CSI2_VCDT_RX_CH_VC(3, 3) |
+		     SUN6I_MIPI_CSI2_VCDT_RX_CH_VC(2, 2) |
+		     SUN6I_MIPI_CSI2_VCDT_RX_CH_VC(1, 1) |
+		     SUN6I_MIPI_CSI2_VCDT_RX_CH_VC(0, 0) |
+		     SUN6I_MIPI_CSI2_VCDT_RX_CH_DT(0, data_type));
+
+	regmap_update_bits(regmap, SUN6I_MIPI_CSI2_CTL_REG,
+			   SUN6I_MIPI_CSI2_CTL_EN, SUN6I_MIPI_CSI2_CTL_EN);
+
+	ret = v4l2_subdev_call(remote_subdev, video, s_stream, 1);
+	if (ret)
+		goto disable;
+
+	return 0;
+
+disable:
+	regmap_update_bits(regmap, SUN6I_MIPI_CSI2_CTL_REG,
+			   SUN6I_MIPI_CSI2_CTL_EN, 0);
+
+	phy_power_off(cdev->dphy);
+
+error_pm:
+	pm_runtime_put(cdev->dev);
+
+	return ret;
+}
+
+static const struct v4l2_subdev_video_ops sun6i_mipi_csi2_subdev_video_ops = {
+	.s_stream	= sun6i_mipi_csi2_s_stream,
+};
+
+/* Pad */
+
+static int
+sun6i_mipi_csi2_enum_mbus_code(struct v4l2_subdev *subdev,
+			       struct v4l2_subdev_pad_config *config,
+			       struct v4l2_subdev_mbus_code_enum *code_enum)
+{
+	if (code_enum->index >= ARRAY_SIZE(sun6i_mipi_csi2_mbus_codes))
+		return -EINVAL;
+
+	code_enum->code = sun6i_mipi_csi2_mbus_codes[code_enum->index];
+
+	return 0;
+}
+
+static int sun6i_mipi_csi2_get_fmt(struct v4l2_subdev *subdev,
+				   struct v4l2_subdev_pad_config *config,
+				   struct v4l2_subdev_format *format)
+{
+	struct sun6i_mipi_csi2_video *video =
+		sun6i_mipi_csi2_subdev_video(subdev);
+	struct v4l2_mbus_framefmt *mbus_format = &format->format;
+
+	if (format->which == V4L2_SUBDEV_FORMAT_TRY)
+		*mbus_format = *v4l2_subdev_get_try_format(subdev, config,
+							   format->pad);
+	else
+		*mbus_format = video->mbus_format;
+
+	return 0;
+}
+
+static int sun6i_mipi_csi2_set_fmt(struct v4l2_subdev *subdev,
+				   struct v4l2_subdev_pad_config *config,
+				   struct v4l2_subdev_format *format)
+{
+	struct sun6i_mipi_csi2_video *video =
+		sun6i_mipi_csi2_subdev_video(subdev);
+	struct v4l2_mbus_framefmt *mbus_format = &format->format;
+
+	if (format->which == V4L2_SUBDEV_FORMAT_TRY)
+		*v4l2_subdev_get_try_format(subdev, config, format->pad) =
+			*mbus_format;
+	else
+		video->mbus_format = *mbus_format;
+
+	return 0;
+}
+
+static const struct v4l2_subdev_pad_ops sun6i_mipi_csi2_subdev_pad_ops = {
+	.enum_mbus_code	= sun6i_mipi_csi2_enum_mbus_code,
+	.get_fmt	= sun6i_mipi_csi2_get_fmt,
+	.set_fmt	= sun6i_mipi_csi2_set_fmt,
+};
+
+/* Subdev */
+
+static const struct v4l2_subdev_ops sun6i_mipi_csi2_subdev_ops = {
+	.video		= &sun6i_mipi_csi2_subdev_video_ops,
+	.pad		= &sun6i_mipi_csi2_subdev_pad_ops,
+};
+
+/* Notifier */
+
+static int
+sun6i_mipi_csi2_notifier_bound(struct v4l2_async_notifier *notifier,
+			       struct v4l2_subdev *remote_subdev,
+			       struct v4l2_async_subdev *remote_subdev_async)
+{
+	struct v4l2_subdev *subdev = notifier->sd;
+	struct sun6i_mipi_csi2_video *video =
+		sun6i_mipi_csi2_subdev_video(subdev);
+	struct sun6i_mipi_csi2_dev *cdev = sun6i_mipi_csi2_video_dev(video);
+	int source_pad;
+	int ret;
+
+	source_pad = media_entity_get_fwnode_pad(&remote_subdev->entity,
+						 remote_subdev->fwnode,
+						 MEDIA_PAD_FL_SOURCE);
+	if (source_pad < 0)
+		return source_pad;
+
+	ret = media_create_pad_link(&remote_subdev->entity, source_pad,
+				    &subdev->entity, 0,
+				    MEDIA_LNK_FL_ENABLED |
+				    MEDIA_LNK_FL_IMMUTABLE);
+	if (ret) {
+		dev_err(cdev->dev, "failed to create %s:%u -> %s:%u link\n",
+			remote_subdev->entity.name, source_pad,
+			subdev->entity.name, 0);
+		return ret;
+	}
+
+	video->remote_subdev = remote_subdev;
+
+	return 0;
+}
+
+static const
+struct v4l2_async_notifier_operations sun6i_mipi_csi2_notifier_ops = {
+	.bound		= sun6i_mipi_csi2_notifier_bound,
+};
+
+/* Media Entity */
+
+static const struct media_entity_operations sun6i_mipi_csi2_entity_ops = {
+	.link_validate	= v4l2_subdev_link_validate,
+};
+
+/* Base Driver */
+
+static int __maybe_unused sun6i_mipi_csi2_suspend(struct device *dev)
+{
+	struct sun6i_mipi_csi2_dev *cdev = dev_get_drvdata(dev);
+
+	clk_disable_unprepare(cdev->clk_mod);
+	clk_disable_unprepare(cdev->clk_bus);
+	reset_control_assert(cdev->reset);
+
+	return 0;
+}
+
+static int __maybe_unused sun6i_mipi_csi2_resume(struct device *dev)
+{
+	struct sun6i_mipi_csi2_dev *cdev = dev_get_drvdata(dev);
+	int ret;
+
+	ret = reset_control_deassert(cdev->reset);
+	if (ret) {
+		dev_err(cdev->dev, "failed to deassert reset\n");
+		return ret;
+	}
+
+	ret = clk_prepare_enable(cdev->clk_bus);
+	if (ret) {
+		dev_err(cdev->dev, "failed to enable bus clock\n");
+		goto error_reset;
+	}
+
+	ret = clk_prepare_enable(cdev->clk_mod);
+	if (ret) {
+		dev_err(cdev->dev, "failed to enable module clock\n");
+		goto error_clk_bus;
+	}
+
+	return 0;
+
+error_clk_bus:
+	clk_disable_unprepare(cdev->clk_bus);
+
+error_reset:
+	reset_control_assert(cdev->reset);
+
+	return ret;
+}
+
+static int sun6i_mipi_csi2_v4l2_setup(struct sun6i_mipi_csi2_dev *cdev)
+{
+	struct sun6i_mipi_csi2_video *video = &cdev->video;
+	struct v4l2_subdev *subdev = &video->subdev;
+	struct v4l2_async_notifier *notifier = &video->notifier;
+	struct fwnode_handle *handle;
+	struct v4l2_fwnode_endpoint *endpoint;
+	struct v4l2_async_subdev *subdev_async;
+	int ret;
+
+	/* Subdev */
+
+	v4l2_subdev_init(subdev, &sun6i_mipi_csi2_subdev_ops);
+	subdev->dev = cdev->dev;
+	subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	strscpy(subdev->name, MODULE_NAME, sizeof(subdev->name));
+	v4l2_set_subdevdata(subdev, cdev);
+
+	/* Entity */
+
+	subdev->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
+	subdev->entity.ops = &sun6i_mipi_csi2_entity_ops;
+
+	/* Pads */
+
+	video->pads[0].flags = MEDIA_PAD_FL_SINK;
+	video->pads[1].flags = MEDIA_PAD_FL_SOURCE;
+
+	ret = media_entity_pads_init(&subdev->entity, 2, video->pads);
+	if (ret)
+		return ret;
+
+	/* Endpoint */
+
+	handle = fwnode_graph_get_endpoint_by_id(dev_fwnode(cdev->dev), 0, 0,
+						 FWNODE_GRAPH_ENDPOINT_NEXT);
+	if (!handle) {
+		ret = -ENODEV;
+		goto error_media_entity;
+	}
+
+	endpoint = &video->endpoint;
+	endpoint->bus_type = V4L2_MBUS_CSI2_DPHY;
+
+	ret = v4l2_fwnode_endpoint_parse(handle, endpoint);
+	fwnode_handle_put(handle);
+	if (ret)
+		goto error_media_entity;
+
+	/* Notifier */
+
+	v4l2_async_notifier_init(notifier);
+
+	subdev_async = &video->subdev_async;
+	ret = v4l2_async_notifier_add_fwnode_remote_subdev(notifier, handle,
+							   subdev_async);
+	if (ret)
+		goto error_media_entity;
+
+	video->notifier.ops = &sun6i_mipi_csi2_notifier_ops;
+
+	ret = v4l2_async_subdev_notifier_register(subdev, notifier);
+	if (ret < 0)
+		goto error_notifier;
+
+	/* Subdev */
+
+	ret = v4l2_async_register_subdev(subdev);
+	if (ret < 0)
+		goto error_notifier_registered;
+
+	/* Runtime PM */
+
+	pm_runtime_enable(cdev->dev);
+	pm_runtime_set_suspended(cdev->dev);
+
+	return 0;
+
+error_notifier_registered:
+	v4l2_async_notifier_unregister(notifier);
+error_notifier:
+	v4l2_async_notifier_cleanup(notifier);
+error_media_entity:
+	media_entity_cleanup(&subdev->entity);
+
+	return ret;
+}
+
+static int sun6i_mipi_csi2_v4l2_teardown(struct sun6i_mipi_csi2_dev *cdev)
+{
+	struct sun6i_mipi_csi2_video *video = &cdev->video;
+	struct v4l2_subdev *subdev = &video->subdev;
+	struct v4l2_async_notifier *notifier = &video->notifier;
+
+	v4l2_async_unregister_subdev(subdev);
+	v4l2_async_notifier_unregister(notifier);
+	v4l2_async_notifier_cleanup(notifier);
+	media_entity_cleanup(&subdev->entity);
+	v4l2_device_unregister_subdev(subdev);
+
+	return 0;
+}
+
+static const struct regmap_config sun6i_mipi_csi2_regmap_config = {
+	.reg_bits       = 32,
+	.reg_stride     = 4,
+	.val_bits       = 32,
+	.max_register	= 0x400,
+};
+
+static int sun6i_mipi_csi2_probe(struct platform_device *pdev)
+{
+	struct sun6i_mipi_csi2_dev *cdev;
+	struct resource *res;
+	void __iomem *io_base;
+	int ret;
+
+	cdev = devm_kzalloc(&pdev->dev, sizeof(*cdev), GFP_KERNEL);
+	if (!cdev)
+		return -ENOMEM;
+
+	cdev->dev = &pdev->dev;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	io_base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(io_base))
+		return PTR_ERR(io_base);
+
+	cdev->regmap = devm_regmap_init_mmio(&pdev->dev, io_base,
+					     &sun6i_mipi_csi2_regmap_config);
+	if (IS_ERR(cdev->regmap)) {
+		dev_err(&pdev->dev, "failed to init register map\n");
+		return PTR_ERR(cdev->regmap);
+	}
+
+	cdev->clk_bus = devm_clk_get(&pdev->dev, "bus");
+	if (IS_ERR(cdev->clk_bus)) {
+		dev_err(&pdev->dev, "failed to acquire bus clock\n");
+		return PTR_ERR(cdev->clk_bus);
+	}
+
+	cdev->clk_mod = devm_clk_get(&pdev->dev, "mod");
+	if (IS_ERR(cdev->clk_mod)) {
+		dev_err(&pdev->dev, "failed to acquire mod clock\n");
+		return PTR_ERR(cdev->clk_mod);
+	}
+
+	cdev->reset = devm_reset_control_get_shared(&pdev->dev, NULL);
+	if (IS_ERR(cdev->reset)) {
+		dev_err(&pdev->dev, "failed to get reset controller\n");
+		return PTR_ERR(cdev->reset);
+	}
+
+	cdev->dphy = devm_phy_get(&pdev->dev, "dphy");
+	if (IS_ERR(cdev->dphy)) {
+		dev_err(&pdev->dev, "failed to get the MIPI D-PHY\n");
+		return PTR_ERR(cdev->dphy);
+	}
+
+	ret = phy_init(cdev->dphy);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to initialize the MIPI D-PHY\n");
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, cdev);
+
+	ret = sun6i_mipi_csi2_v4l2_setup(cdev);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int sun6i_mipi_csi2_remove(struct platform_device *pdev)
+{
+	struct sun6i_mipi_csi2_dev *cdev = platform_get_drvdata(pdev);
+
+	phy_exit(cdev->dphy);
+
+	return sun6i_mipi_csi2_v4l2_teardown(cdev);
+}
+
+static const struct dev_pm_ops sun6i_mipi_csi2_pm_ops = {
+	SET_RUNTIME_PM_OPS(sun6i_mipi_csi2_suspend, sun6i_mipi_csi2_resume,
+			   NULL)
+};
+
+static const struct of_device_id sun6i_mipi_csi2_of_match[] = {
+	{ .compatible = "allwinner,sun6i-a31-mipi-csi2" },
+	{ .compatible = "allwinner,sun8i-v3s-mipi-csi2", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, sun6i_mipi_csi2_of_match);
+
+static struct platform_driver sun6i_mipi_csi2_platform_driver = {
+	.probe = sun6i_mipi_csi2_probe,
+	.remove = sun6i_mipi_csi2_remove,
+	.driver = {
+		.name = MODULE_NAME,
+		.of_match_table = of_match_ptr(sun6i_mipi_csi2_of_match),
+		.pm = &sun6i_mipi_csi2_pm_ops,
+	},
+};
+module_platform_driver(sun6i_mipi_csi2_platform_driver);
+
+MODULE_DESCRIPTION("Allwinner A31 MIPI CSI-2 Controller Driver");
+MODULE_AUTHOR("Paul Kocialkowski <paul.kocialkowski@bootlin.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.h b/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.h
new file mode 100644
index 000000000000..b132ac8873b6
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.h
@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2020 Bootlin
+ * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
+ */
+
+#ifndef __SUN6I_MIPI_CSI2_H__
+#define __SUN6I_MIPI_CSI2_H__
+
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+
+#define SUN6I_MIPI_CSI2_CTL_REG				0x0
+#define SUN6I_MIPI_CSI2_CTL_RESET_N			BIT(31)
+#define SUN6I_MIPI_CSI2_CTL_VERSION_EN			BIT(30)
+#define SUN6I_MIPI_CSI2_CTL_UNPK_EN			BIT(1)
+#define SUN6I_MIPI_CSI2_CTL_EN				BIT(0)
+#define SUN6I_MIPI_CSI2_CFG_REG				0x4
+#define SUN6I_MIPI_CSI2_CFG_CHANNEL_MODE(v)		((((v) - 1) << 8) & \
+							 GENMASK(9, 8))
+#define SUN6I_MIPI_CSI2_CFG_LANE_COUNT(v)		(((v) - 1) & GENMASK(1, 0))
+#define SUN6I_MIPI_CSI2_VCDT_RX_REG			0x8
+#define SUN6I_MIPI_CSI2_VCDT_RX_CH_VC(ch, vc)		(((vc) & GENMASK(1, 0)) << \
+							 ((ch) * 8 + 6))
+#define SUN6I_MIPI_CSI2_VCDT_RX_CH_DT(ch, t)		(((t) & GENMASK(5, 0)) << \
+							 ((ch) * 8))
+#define SUN6I_MIPI_CSI2_RX_PKT_NUM_REG			0xc
+
+#define SUN6I_MIPI_CSI2_VERSION_REG			0x3c
+
+#define SUN6I_MIPI_CSI2_CH_BASE				0x1000
+#define SUN6I_MIPI_CSI2_CH_OFFSET			0x100
+
+#define SUN6I_MIPI_CSI2_CH_CFG_REG			0x40
+#define SUN6I_MIPI_CSI2_CH_INT_EN_REG			0x50
+#define SUN6I_MIPI_CSI2_CH_INT_EN_EOT_ERR		BIT(29)
+#define SUN6I_MIPI_CSI2_CH_INT_EN_CHKSUM_ERR		BIT(28)
+#define SUN6I_MIPI_CSI2_CH_INT_EN_ECC_WRN		BIT(27)
+#define SUN6I_MIPI_CSI2_CH_INT_EN_ECC_ERR		BIT(26)
+#define SUN6I_MIPI_CSI2_CH_INT_EN_LINE_SYNC_ERR		BIT(25)
+#define SUN6I_MIPI_CSI2_CH_INT_EN_FRAME_SYNC_ERR	BIT(24)
+#define SUN6I_MIPI_CSI2_CH_INT_EN_EMB_DATA		BIT(18)
+#define SUN6I_MIPI_CSI2_CH_INT_EN_PF			BIT(17)
+#define SUN6I_MIPI_CSI2_CH_INT_EN_PH_UPDATE		BIT(16)
+#define SUN6I_MIPI_CSI2_CH_INT_EN_LINE_START_SYNC	BIT(11)
+#define SUN6I_MIPI_CSI2_CH_INT_EN_LINE_END_SYNC	BIT(10)
+#define SUN6I_MIPI_CSI2_CH_INT_EN_FRAME_START_SYNC	BIT(9)
+#define SUN6I_MIPI_CSI2_CH_INT_EN_FRAME_END_SYNC	BIT(8)
+#define SUN6I_MIPI_CSI2_CH_INT_EN_FIFO_OVER		BIT(0)
+
+#define SUN6I_MIPI_CSI2_CH_INT_PD_REG			0x58
+#define SUN6I_MIPI_CSI2_CH_INT_PD_EOT_ERR		BIT(29)
+#define SUN6I_MIPI_CSI2_CH_INT_PD_CHKSUM_ERR		BIT(28)
+#define SUN6I_MIPI_CSI2_CH_INT_PD_ECC_WRN		BIT(27)
+#define SUN6I_MIPI_CSI2_CH_INT_PD_ECC_ERR		BIT(26)
+#define SUN6I_MIPI_CSI2_CH_INT_PD_LINE_SYNC_ERR		BIT(25)
+#define SUN6I_MIPI_CSI2_CH_INT_PD_FRAME_SYNC_ERR	BIT(24)
+#define SUN6I_MIPI_CSI2_CH_INT_PD_EMB_DATA		BIT(18)
+#define SUN6I_MIPI_CSI2_CH_INT_PD_PF			BIT(17)
+#define SUN6I_MIPI_CSI2_CH_INT_PD_PH_UPDATE		BIT(16)
+#define SUN6I_MIPI_CSI2_CH_INT_PD_LINE_START_SYNC	BIT(11)
+#define SUN6I_MIPI_CSI2_CH_INT_PD_LINE_END_SYNC		BIT(10)
+#define SUN6I_MIPI_CSI2_CH_INT_PD_FRAME_START_SYNC	BIT(9)
+#define SUN6I_MIPI_CSI2_CH_INT_PD_FRAME_END_SYNC	BIT(8)
+#define SUN6I_MIPI_CSI2_CH_INT_PD_FIFO_OVER		BIT(0)
+
+#define SUN6I_MIPI_CSI2_CH_DT_TRIGGER_REG		0x60
+#define SUN6I_MIPI_CSI2_CH_CUR_PH_REG			0x70
+#define SUN6I_MIPI_CSI2_CH_ECC_REG			0x74
+#define SUN6I_MIPI_CSI2_CH_CKS_REG			0x78
+#define SUN6I_MIPI_CSI2_CH_FRAME_NUM_REG		0x7c
+#define SUN6I_MIPI_CSI2_CH_LINE_NUM_REG			0x80
+
+#define SUN6I_MIPI_CSI2_CH_REG(reg, ch) \
+	(SUN6I_MIPI_CSI2_CH_BASE + SUN6I_MIPI_CSI2_CH_OFFSET * (ch) + (reg))
+
+enum mipi_csi2_data_type {
+	MIPI_CSI2_DATA_TYPE_RAW8	= 0x2a,
+	MIPI_CSI2_DATA_TYPE_RAW10	= 0x2b,
+	MIPI_CSI2_DATA_TYPE_RAW12	= 0x2c,
+};
+
+struct sun6i_mipi_csi2_video {
+	struct v4l2_fwnode_endpoint endpoint;
+	struct v4l2_subdev subdev;
+	struct media_pad pads[2];
+
+	struct v4l2_async_subdev subdev_async;
+	struct v4l2_async_notifier notifier;
+
+	struct v4l2_subdev *remote_subdev;
+
+	struct v4l2_mbus_framefmt mbus_format;
+};
+
+struct sun6i_mipi_csi2_dev {
+	struct device *dev;
+
+	struct regmap *regmap;
+	struct clk *clk_bus;
+	struct clk *clk_mod;
+	struct reset_control *reset;
+	struct phy *dphy;
+
+	struct sun6i_mipi_csi2_video video;
+};
+
+#define sun6i_mipi_csi2_subdev_video(subdev) \
+	container_of(subdev, struct sun6i_mipi_csi2_video, subdev)
+
+#define sun6i_mipi_csi2_video_dev(video) \
+	container_of(video, struct sun6i_mipi_csi2_dev, video)
+
+#endif /* __SUN6I_MIPI_CSI2_H__ */
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v2 14/19] ARM: dts: sun8i: v3s: Add nodes for MIPI CSI-2 support
  2020-11-28 14:28 [PATCH v2 00/19] Allwinner MIPI CSI-2 support for A31/V3s/A83T Paul Kocialkowski
                   ` (12 preceding siblings ...)
  2020-11-28 14:28 ` [PATCH v2 13/19] media: sunxi: Add support for the A31 MIPI CSI-2 controller Paul Kocialkowski
@ 2020-11-28 14:28 ` Paul Kocialkowski
  2020-11-28 14:28 ` [PATCH v2 15/19] MAINTAINERS: Add entry for the Allwinner A31 MIPI CSI-2 bridge Paul Kocialkowski
                   ` (4 subsequent siblings)
  18 siblings, 0 replies; 39+ messages in thread
From: Paul Kocialkowski @ 2020-11-28 14:28 UTC (permalink / raw)
  To: linux-media, devicetree, linux-arm-kernel, linux-kernel,
	linux-doc, devel, linux-sunxi
  Cc: Yong Deng, Mauro Carvalho Chehab, Rob Herring, Maxime Ripard,
	Chen-Yu Tsai, Jernej Skrabec, Paul Kocialkowski, Jonathan Corbet,
	Kishon Vijay Abraham I, Vinod Koul, Helen Koike,
	Dafna Hirschfeld, Greg Kroah-Hartman, Philipp Zabel,
	Sakari Ailus, Hans Verkuil, Thomas Petazzoni, kevin.lhopital

MIPI CSI-2 is supported on the V3s with an A31-based MIPI CSI-2 bridge
controller. The controller uses a separate D-PHY, which is the same
that is otherwise used for MIPI DSI, but used in Rx mode.

On the V3s, the CSI0 controller is dedicated to MIPI CSI-2 as it does
not have access to any parallel interface pins.

Add all the necessary nodes (CSI0, MIPI CSI-2 bridge and D-PHY) to
support the MIPI CSI-2 interface.

Note that a fwnode graph link is created between CSI0 and MIPI CSI-2
even when no sensor is connected. This will result in a probe failure
for the controller as long as no sensor is connected but this is fine
since no other interface is available.

Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
---
 arch/arm/boot/dts/sun8i-v3s.dtsi | 68 ++++++++++++++++++++++++++++++++
 1 file changed, 68 insertions(+)

diff --git a/arch/arm/boot/dts/sun8i-v3s.dtsi b/arch/arm/boot/dts/sun8i-v3s.dtsi
index 7926c8b2ac5e..641da6c7bca0 100644
--- a/arch/arm/boot/dts/sun8i-v3s.dtsi
+++ b/arch/arm/boot/dts/sun8i-v3s.dtsi
@@ -530,6 +530,31 @@ spi0: spi@1c68000 {
 			#size-cells = <0>;
 		};
 
+		csi0: camera@1cb0000 {
+			compatible = "allwinner,sun8i-v3s-csi";
+			reg = <0x01cb0000 0x1000>;
+			interrupts = <GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&ccu CLK_BUS_CSI>,
+				 <&ccu CLK_CSI1_SCLK>,
+				 <&ccu CLK_DRAM_CSI>;
+			clock-names = "bus", "mod", "ram";
+			resets = <&ccu RST_BUS_CSI>;
+			status = "disabled";
+
+			ports {
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				port@1 {
+					reg = <1>;
+
+					csi0_in_mipi_csi2: endpoint {
+						remote-endpoint = <&mipi_csi2_out_csi0>;
+					};
+				};
+			};
+		};
+
 		csi1: camera@1cb4000 {
 			compatible = "allwinner,sun8i-v3s-csi";
 			reg = <0x01cb4000 0x3000>;
@@ -561,5 +586,48 @@ gic: interrupt-controller@1c81000 {
 			#interrupt-cells = <3>;
 			interrupts = <GIC_PPI 9 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>;
 		};
+
+		mipi_csi2: csi@1cb1000 {
+			compatible = "allwinner,sun8i-v3s-mipi-csi2",
+				     "allwinner,sun6i-a31-mipi-csi2";
+			reg = <0x01cb1000 0x1000>;
+			interrupts = <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&ccu CLK_BUS_CSI>,
+				 <&ccu CLK_CSI1_SCLK>;
+			clock-names = "bus", "mod";
+			resets = <&ccu RST_BUS_CSI>;
+			status = "disabled";
+
+			phys = <&dphy>;
+			phy-names = "dphy";
+
+			ports {
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				mipi_csi2_in: port@0 {
+					reg = <0>;
+				};
+
+				mipi_csi2_out: port@1 {
+					reg = <1>;
+
+					mipi_csi2_out_csi0: endpoint {
+						remote-endpoint = <&csi0_in_mipi_csi2>;
+					};
+				};
+			};
+		};
+
+		dphy: d-phy@1cb2000 {
+			compatible = "allwinner,sun6i-a31-mipi-dphy";
+			reg = <0x01cb2000 0x1000>;
+			clocks = <&ccu CLK_BUS_CSI>,
+				 <&ccu CLK_MIPI_CSI>;
+			clock-names = "bus", "mod";
+			resets = <&ccu RST_BUS_CSI>;
+			status = "disabled";
+			#phy-cells = <0>;
+		};
 	};
 };
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v2 15/19] MAINTAINERS: Add entry for the Allwinner A31 MIPI CSI-2 bridge
  2020-11-28 14:28 [PATCH v2 00/19] Allwinner MIPI CSI-2 support for A31/V3s/A83T Paul Kocialkowski
                   ` (13 preceding siblings ...)
  2020-11-28 14:28 ` [PATCH v2 14/19] ARM: dts: sun8i: v3s: Add nodes for MIPI CSI-2 support Paul Kocialkowski
@ 2020-11-28 14:28 ` Paul Kocialkowski
  2020-11-28 14:28 ` [PATCH v2 16/19] dt-bindings: media: Add A83T MIPI CSI-2 bindings documentation Paul Kocialkowski
                   ` (3 subsequent siblings)
  18 siblings, 0 replies; 39+ messages in thread
From: Paul Kocialkowski @ 2020-11-28 14:28 UTC (permalink / raw)
  To: linux-media, devicetree, linux-arm-kernel, linux-kernel,
	linux-doc, devel, linux-sunxi
  Cc: Yong Deng, Mauro Carvalho Chehab, Rob Herring, Maxime Ripard,
	Chen-Yu Tsai, Jernej Skrabec, Paul Kocialkowski, Jonathan Corbet,
	Kishon Vijay Abraham I, Vinod Koul, Helen Koike,
	Dafna Hirschfeld, Greg Kroah-Hartman, Philipp Zabel,
	Sakari Ailus, Hans Verkuil, Thomas Petazzoni, kevin.lhopital

Add myself as maintainer of the A31 MIPI CSI-2 bridge media driver.

Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 0644128640fb..a1352171778b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -709,6 +709,14 @@ T:	git git://linuxtv.org/media_tree.git
 F:	Documentation/devicetree/bindings/media/allwinner,sun4i-a10-csi.yaml
 F:	drivers/media/platform/sunxi/sun4i-csi/
 
+ALLWINNER A31 MIPI CSI-2 BRIDGE
+M:	Paul Kocialkowski <paul.kocialkowski@bootlin.com>
+L:	linux-media@vger.kernel.org
+S:	Maintained
+T:	git git://linuxtv.org/media_tree.git
+F:	Documentation/devicetree/bindings/media/allwinner,sun6i-a31-mipi-csi2.yaml
+F:	drivers/media/platform/sunxi/sun6i-mipi-csi2/
+
 ALLWINNER CPUFREQ DRIVER
 M:	Yangtao Li <tiny.windzz@gmail.com>
 L:	linux-pm@vger.kernel.org
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v2 16/19] dt-bindings: media: Add A83T MIPI CSI-2 bindings documentation
  2020-11-28 14:28 [PATCH v2 00/19] Allwinner MIPI CSI-2 support for A31/V3s/A83T Paul Kocialkowski
                   ` (14 preceding siblings ...)
  2020-11-28 14:28 ` [PATCH v2 15/19] MAINTAINERS: Add entry for the Allwinner A31 MIPI CSI-2 bridge Paul Kocialkowski
@ 2020-11-28 14:28 ` Paul Kocialkowski
  2020-12-11  3:12   ` Rob Herring
  2020-11-28 14:28 ` [PATCH v2 17/19] media: sunxi: Add support for the A83T MIPI CSI-2 controller Paul Kocialkowski
                   ` (2 subsequent siblings)
  18 siblings, 1 reply; 39+ messages in thread
From: Paul Kocialkowski @ 2020-11-28 14:28 UTC (permalink / raw)
  To: linux-media, devicetree, linux-arm-kernel, linux-kernel,
	linux-doc, devel, linux-sunxi
  Cc: Yong Deng, Mauro Carvalho Chehab, Rob Herring, Maxime Ripard,
	Chen-Yu Tsai, Jernej Skrabec, Paul Kocialkowski, Jonathan Corbet,
	Kishon Vijay Abraham I, Vinod Koul, Helen Koike,
	Dafna Hirschfeld, Greg Kroah-Hartman, Philipp Zabel,
	Sakari Ailus, Hans Verkuil, Thomas Petazzoni, kevin.lhopital

This introduces YAML bindings documentation for the A83T MIPI CSI-2
controller.

Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
---
 .../media/allwinner,sun8i-a83t-mipi-csi2.yaml | 147 ++++++++++++++++++
 1 file changed, 147 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/allwinner,sun8i-a83t-mipi-csi2.yaml

diff --git a/Documentation/devicetree/bindings/media/allwinner,sun8i-a83t-mipi-csi2.yaml b/Documentation/devicetree/bindings/media/allwinner,sun8i-a83t-mipi-csi2.yaml
new file mode 100644
index 000000000000..78309084f904
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/allwinner,sun8i-a83t-mipi-csi2.yaml
@@ -0,0 +1,147 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/allwinner,sun8i-a83t-mipi-csi2.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Allwinner A83T MIPI CSI-2 Device Tree Bindings
+
+maintainers:
+  - Paul Kocialkowski <paul.kocialkowski@bootlin.com>
+
+properties:
+  compatible:
+    const: allwinner,sun8i-a83t-mipi-csi2
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    items:
+      - description: Bus Clock
+      - description: Module Clock
+      - description: MIPI-specific Clock
+      - description: Misc CSI Clock
+
+  clock-names:
+    items:
+      - const: bus
+      - const: mod
+      - const: mipi
+      - const: misc
+
+  resets:
+    maxItems: 1
+
+  # See ./video-interfaces.txt for details
+  ports:
+    type: object
+
+    properties:
+      port@0:
+        type: object
+        description: Input port, connect to a MIPI CSI-2 sensor
+
+        properties:
+          reg:
+            const: 0
+
+          endpoint:
+            type: object
+
+            properties:
+              remote-endpoint: true
+
+              clock-lanes:
+                maxItems: 1
+
+              data-lanes:
+                minItems: 1
+                maxItems: 4
+
+            required:
+              - data-lanes
+              - remote-endpoint
+
+        required:
+          - endpoint
+
+        additionalProperties: false
+
+      port@1:
+        type: object
+        description: Output port, connect to a CSI controller
+
+        properties:
+          reg:
+            const: 1
+
+          endpoint:
+            type: object
+
+            properties:
+              remote-endpoint: true
+
+              bus-type:
+                const: 4
+
+        required:
+          - endpoint
+
+        additionalProperties: false
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - clock-names
+  - resets
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/clock/sun8i-a83t-ccu.h>
+    #include <dt-bindings/reset/sun8i-a83t-ccu.h>
+
+    mipi_csi2: csi@1cb1000 {
+        compatible = "allwinner,sun8i-a83t-mipi-csi2";
+        reg = <0x01cb1000 0x1000>;
+        interrupts = <GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>;
+        clocks = <&ccu CLK_BUS_CSI>,
+                 <&ccu CLK_CSI_SCLK>,
+                 <&ccu CLK_MIPI_CSI>,
+                 <&ccu CLK_CSI_MISC>;
+        clock-names = "bus", "mod", "mipi", "misc";
+        resets = <&ccu RST_BUS_CSI>;
+
+        ports {
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            mipi_csi2_in: port@0 {
+                reg = <0>;
+
+                mipi_csi2_in_ov8865: endpoint {
+                    data-lanes = <1 2 3 4>;
+
+                    remote-endpoint = <&ov8865_out_mipi_csi2>;
+                };
+            };
+
+            mipi_csi2_out: port@1 {
+                reg = <1>;
+
+                mipi_csi2_out_csi: endpoint {
+                    remote-endpoint = <&csi_in_mipi_csi2>;
+                };
+            };
+        };
+    };
+
+...
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v2 17/19] media: sunxi: Add support for the A83T MIPI CSI-2 controller
  2020-11-28 14:28 [PATCH v2 00/19] Allwinner MIPI CSI-2 support for A31/V3s/A83T Paul Kocialkowski
                   ` (15 preceding siblings ...)
  2020-11-28 14:28 ` [PATCH v2 16/19] dt-bindings: media: Add A83T MIPI CSI-2 bindings documentation Paul Kocialkowski
@ 2020-11-28 14:28 ` Paul Kocialkowski
  2020-11-28 14:28 ` [PATCH v2 18/19] ARM: dts: sun8i: a83t: Add MIPI CSI-2 controller node Paul Kocialkowski
  2020-11-28 14:28 ` [PATCH v2 19/19] MAINTAINERS: Add entry for the Allwinner A83T MIPI CSI-2 bridge Paul Kocialkowski
  18 siblings, 0 replies; 39+ messages in thread
From: Paul Kocialkowski @ 2020-11-28 14:28 UTC (permalink / raw)
  To: linux-media, devicetree, linux-arm-kernel, linux-kernel,
	linux-doc, devel, linux-sunxi
  Cc: Yong Deng, Mauro Carvalho Chehab, Rob Herring, Maxime Ripard,
	Chen-Yu Tsai, Jernej Skrabec, Paul Kocialkowski, Jonathan Corbet,
	Kishon Vijay Abraham I, Vinod Koul, Helen Koike,
	Dafna Hirschfeld, Greg Kroah-Hartman, Philipp Zabel,
	Sakari Ailus, Hans Verkuil, Thomas Petazzoni, kevin.lhopital

The A83T supports MIPI CSI-2 with a composite controller, covering both the
protocol logic and the D-PHY implementation. This controller seems to be found
on the A83T only and probably was abandoned since.

This implementation splits the protocol and D-PHY registers and uses the PHY
framework internally. The D-PHY is not registered as a standalone PHY driver
since it cannot be used with any other controller.

There are a few notable points about the controller:
- The initialisation sequence involes writing specific magic init values that
  do not seem to make any particular sense given the concerned register fields.
- Interrupts appear to be hitting regardless of the interrupt mask registers,
  which can cause a serious flood when transmission errors occur.

Only 8-bit and 10-bit Bayer formats are currently supported.
While up to 4 internal channels to the CSI controller exist, only one
is currently supported by this implementation.

This work is based on the first version of the driver submitted by
Kévin L'hôpital, which was adapted to mainline from the Allwinner BSP.
This version integrates MIPI CSI-2 support as a standalone V4L2 subdev
instead of merging it in the sun6i-csi driver.

It was tested on a Banana Pi M3 board with an OV8865 sensor in a 4-lane
configuration.

Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
---
 drivers/media/platform/sunxi/Kconfig          |   1 +
 drivers/media/platform/sunxi/Makefile         |   1 +
 .../sunxi/sun8i-a83t-mipi-csi2/Kconfig        |  11 +
 .../sunxi/sun8i-a83t-mipi-csi2/Makefile       |   4 +
 .../sun8i-a83t-mipi-csi2/sun8i_a83t_dphy.c    |  92 +++
 .../sun8i-a83t-mipi-csi2/sun8i_a83t_dphy.h    |  39 ++
 .../sun8i_a83t_mipi_csi2.c                    | 657 ++++++++++++++++++
 .../sun8i_a83t_mipi_csi2.h                    | 197 ++++++
 8 files changed, 1002 insertions(+)
 create mode 100644 drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Kconfig
 create mode 100644 drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Makefile
 create mode 100644 drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_dphy.c
 create mode 100644 drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_dphy.h
 create mode 100644 drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c
 create mode 100644 drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.h

diff --git a/drivers/media/platform/sunxi/Kconfig b/drivers/media/platform/sunxi/Kconfig
index 9684e07454ad..db4c07be7e4c 100644
--- a/drivers/media/platform/sunxi/Kconfig
+++ b/drivers/media/platform/sunxi/Kconfig
@@ -3,3 +3,4 @@
 source "drivers/media/platform/sunxi/sun4i-csi/Kconfig"
 source "drivers/media/platform/sunxi/sun6i-csi/Kconfig"
 source "drivers/media/platform/sunxi/sun6i-mipi-csi2/Kconfig"
+source "drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Kconfig"
diff --git a/drivers/media/platform/sunxi/Makefile b/drivers/media/platform/sunxi/Makefile
index 887a7cae8fca..9aa01cb01883 100644
--- a/drivers/media/platform/sunxi/Makefile
+++ b/drivers/media/platform/sunxi/Makefile
@@ -3,5 +3,6 @@
 obj-y		+= sun4i-csi/
 obj-y		+= sun6i-csi/
 obj-y		+= sun6i-mipi-csi2/
+obj-y		+= sun8i-a83t-mipi-csi2/
 obj-y		+= sun8i-di/
 obj-y		+= sun8i-rotate/
diff --git a/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Kconfig b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Kconfig
new file mode 100644
index 000000000000..162f5d1dc25f
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Kconfig
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config VIDEO_SUN8I_A83T_MIPI_CSI2
+	tristate "Allwinner A83T MIPI CSI-2 Controller and D-PHY Driver"
+	depends on VIDEO_V4L2 && COMMON_CLK
+	depends on ARCH_SUNXI || COMPILE_TEST
+	select MEDIA_CONTROLLER
+	select VIDEO_V4L2_SUBDEV_API
+	select REGMAP_MMIO
+	select V4L2_FWNODE
+	help
+	   Support for the Allwinner A83T MIPI CSI-2 Controller and D-PHY.
diff --git a/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Makefile b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Makefile
new file mode 100644
index 000000000000..1427d15a879a
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+sun8i-a83t-mipi-csi2-y += sun8i_a83t_mipi_csi2.o sun8i_a83t_dphy.o
+
+obj-$(CONFIG_VIDEO_SUN8I_A83T_MIPI_CSI2) += sun8i-a83t-mipi-csi2.o
diff --git a/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_dphy.c b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_dphy.c
new file mode 100644
index 000000000000..ebb504247956
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_dphy.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2020 Bootlin
+ * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
+ */
+
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+
+#include "sun8i_a83t_dphy.h"
+#include "sun8i_a83t_mipi_csi2.h"
+
+static int sun8i_a83t_dphy_set_mode(struct phy *dphy, enum phy_mode mode,
+				    int submode)
+{
+	if (mode != PHY_MODE_MIPI_DPHY ||
+	    submode != PHY_MIPI_DPHY_SUBMODE_RX)
+		return -EINVAL;
+
+	return 0;
+};
+
+static int sun8i_a83t_dphy_configure(struct phy *dphy,
+				     union phy_configure_opts *opts)
+{
+	struct sun8i_a83t_mipi_csi2_dev *cdev = phy_get_drvdata(dphy);
+	int ret;
+
+	ret = phy_mipi_dphy_config_validate(&opts->mipi_dphy);
+	if (ret)
+		return ret;
+
+	memcpy(&cdev->dphy_config, opts, sizeof(cdev->dphy_config));
+
+	return 0;
+};
+
+static int sun8i_a83t_dphy_power_on(struct phy *dphy)
+{
+	struct sun8i_a83t_mipi_csi2_dev *cdev = phy_get_drvdata(dphy);
+	struct regmap *regmap = cdev->regmap;
+
+	regmap_write(regmap, SUN8I_A83T_DPHY_CTRL_REG,
+		     SUN8I_A83T_DPHY_CTRL_RESET_N |
+		     SUN8I_A83T_DPHY_CTRL_SHUTDOWN_N);
+
+	regmap_write(regmap, SUN8I_A83T_DPHY_ANA0_REG,
+		     SUN8I_A83T_DPHY_ANA0_REXT_EN |
+		     SUN8I_A83T_DPHY_ANA0_RINT(2) |
+		     SUN8I_A83T_DPHY_ANA0_SNK(2));
+
+	return 0;
+};
+
+static int sun8i_a83t_dphy_power_off(struct phy *dphy)
+{
+	struct sun8i_a83t_mipi_csi2_dev *cdev = phy_get_drvdata(dphy);
+	struct regmap *regmap = cdev->regmap;
+
+	regmap_write(regmap, SUN8I_A83T_DPHY_CTRL_REG, 0);
+
+	return 0;
+};
+
+static struct phy_ops sun8i_a83t_dphy_ops = {
+	.set_mode	= sun8i_a83t_dphy_set_mode,
+	.configure	= sun8i_a83t_dphy_configure,
+	.power_on	= sun8i_a83t_dphy_power_on,
+	.power_off	= sun8i_a83t_dphy_power_off,
+};
+
+int sun8i_a83t_dphy_register(struct sun8i_a83t_mipi_csi2_dev *cdev)
+{
+	struct phy_provider *phy_provider;
+
+	cdev->dphy = devm_phy_create(cdev->dev, NULL, &sun8i_a83t_dphy_ops);
+	if (IS_ERR(cdev->dphy)) {
+		dev_err(cdev->dev, "failed to create D-PHY\n");
+		return PTR_ERR(cdev->dphy);
+	}
+
+	phy_set_drvdata(cdev->dphy, cdev);
+
+	phy_provider = devm_of_phy_provider_register(cdev->dev,
+						     of_phy_simple_xlate);
+	if (IS_ERR(phy_provider)) {
+		dev_err(cdev->dev, "failed to register D-PHY provider\n");
+		return PTR_ERR(phy_provider);
+	}
+
+	return 0;
+}
diff --git a/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_dphy.h b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_dphy.h
new file mode 100644
index 000000000000..a4ed355e5f6f
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_dphy.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2020 Kévin L'hôpital <kevin.lhopital@bootlin.com>
+ * Copyright 2020 Bootlin
+ * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
+ */
+
+#ifndef __SUN8I_A83T_DPHY_H__
+#define __SUN8I_A83T_DPHY_H__
+
+#include "sun8i_a83t_mipi_csi2.h"
+
+#define SUN8I_A83T_DPHY_CTRL_REG		0x10
+#define SUN8I_A83T_DPHY_CTRL_INIT_VALUE		0xb8df698e
+#define SUN8I_A83T_DPHY_CTRL_RESET_N		BIT(31)
+#define SUN8I_A83T_DPHY_CTRL_SHUTDOWN_N		BIT(15)
+#define SUN8I_A83T_DPHY_CTRL_DEBUG		BIT(8)
+#define SUN8I_A83T_DPHY_STATUS_REG		0x14
+#define SUN8I_A83T_DPHY_STATUS_CLK_STOP		BIT(10)
+#define SUN8I_A83T_DPHY_STATUS_CLK_ULPS		BIT(9)
+#define SUN8I_A83T_DPHY_STATUS_HSCLK		BIT(8)
+#define SUN8I_A83T_DPHY_STATUS_D3_STOP		BIT(7)
+#define SUN8I_A83T_DPHY_STATUS_D2_STOP		BIT(6)
+#define SUN8I_A83T_DPHY_STATUS_D1_STOP		BIT(5)
+#define SUN8I_A83T_DPHY_STATUS_D0_STOP		BIT(4)
+#define SUN8I_A83T_DPHY_STATUS_D3_ULPS		BIT(3)
+#define SUN8I_A83T_DPHY_STATUS_D2_ULPS		BIT(2)
+#define SUN8I_A83T_DPHY_STATUS_D1_ULPS		BIT(1)
+#define SUN8I_A83T_DPHY_STATUS_D0_ULPS		BIT(0)
+
+#define SUN8I_A83T_DPHY_ANA0_REG		0x30
+#define SUN8I_A83T_DPHY_ANA0_REXT_EN		BIT(31)
+#define SUN8I_A83T_DPHY_ANA0_REXT		BIT(30)
+#define SUN8I_A83T_DPHY_ANA0_RINT(v)		(((v) << 28) & GENMASK(29, 28))
+#define SUN8I_A83T_DPHY_ANA0_SNK(v)		(((v) << 20) & GENMASK(22, 20))
+
+int sun8i_a83t_dphy_register(struct sun8i_a83t_mipi_csi2_dev *cdev);
+
+#endif /* __SUN8I_A83T_DPHY_H__ */
diff --git a/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c
new file mode 100644
index 000000000000..84132732d40e
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c
@@ -0,0 +1,657 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2020 Kévin L'hôpital <kevin.lhopital@bootlin.com>
+ * Copyright 2020 Bootlin
+ * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+
+#include "sun8i_a83t_dphy.h"
+#include "sun8i_a83t_mipi_csi2.h"
+
+#define MODULE_NAME	"sun8i-a83t-mipi-csi2"
+
+static const u32 sun8i_a83t_mipi_csi2_mbus_codes[] = {
+	MEDIA_BUS_FMT_SBGGR8_1X8,
+	MEDIA_BUS_FMT_SGBRG8_1X8,
+	MEDIA_BUS_FMT_SGRBG8_1X8,
+	MEDIA_BUS_FMT_SRGGB8_1X8,
+	MEDIA_BUS_FMT_SBGGR10_1X10,
+	MEDIA_BUS_FMT_SGBRG10_1X10,
+	MEDIA_BUS_FMT_SGRBG10_1X10,
+	MEDIA_BUS_FMT_SRGGB10_1X10,
+};
+
+/* Core */
+
+static void sun8i_a83t_mipi_csi2_init(struct sun8i_a83t_mipi_csi2_dev *cdev)
+{
+	struct regmap *regmap = cdev->regmap;
+
+	/*
+	 * The Allwinner BSP sets various magic values on a bunch of registers.
+	 * This is apparently a necessary initialization process that will cause
+	 * the capture to fail with unsolicited interrupts hitting if skipped.
+	 *
+	 * Most of the registers are set to proper values later, except for the
+	 * two reserved registers. They are said to hold a "hardware lock"
+	 * value, without more information available.
+	 */
+
+	regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_CTRL_REG, 0);
+	regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_CTRL_REG,
+		     SUN8I_A83T_MIPI_CSI2_CTRL_INIT_VALUE);
+
+	regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_RX_PKT_NUM_REG, 0);
+	regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_RX_PKT_NUM_REG,
+		     SUN8I_A83T_MIPI_CSI2_RX_PKT_NUM_INIT_VALUE);
+
+	regmap_write(regmap, SUN8I_A83T_DPHY_CTRL_REG, 0);
+	regmap_write(regmap, SUN8I_A83T_DPHY_CTRL_REG,
+		     SUN8I_A83T_DPHY_CTRL_INIT_VALUE);
+
+	regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_RSVD1_REG, 0);
+	regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_RSVD1_REG,
+		     SUN8I_A83T_MIPI_CSI2_RSVD1_HW_LOCK_VALUE);
+
+	regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_RSVD2_REG, 0);
+	regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_RSVD2_REG,
+		     SUN8I_A83T_MIPI_CSI2_RSVD2_HW_LOCK_VALUE);
+
+	regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_CFG_REG, 0);
+	regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_CFG_REG,
+		     SUN8I_A83T_MIPI_CSI2_CFG_INIT_VALUE);
+}
+
+/* Video */
+
+static int sun8i_a83t_mipi_csi2_s_stream(struct v4l2_subdev *subdev, int on)
+{
+	struct sun8i_a83t_mipi_csi2_video *video =
+		sun8i_a83t_mipi_csi2_subdev_video(subdev);
+	struct sun8i_a83t_mipi_csi2_dev *cdev =
+		sun8i_a83t_mipi_csi2_video_dev(video);
+	struct v4l2_subdev *remote_subdev = video->remote_subdev;
+	struct v4l2_fwnode_bus_mipi_csi2 *bus_mipi_csi2 =
+		&video->endpoint.bus.mipi_csi2;
+	union phy_configure_opts dphy_opts = { 0 };
+	struct phy_configure_opts_mipi_dphy *dphy_cfg = &dphy_opts.mipi_dphy;
+	struct regmap *regmap = cdev->regmap;
+	struct v4l2_ctrl *ctrl;
+	unsigned int lanes_count;
+	unsigned int bpp;
+	unsigned long pixel_rate;
+	u8 data_type = 0;
+	u32 version = 0;
+	/* Initialize to 0 to use both in disable label (ret != 0) and off. */
+	int ret = 0;
+
+	if (!remote_subdev)
+		return -ENODEV;
+
+	if (!on) {
+		v4l2_subdev_call(remote_subdev, video, s_stream, 0);
+		goto disable;
+	}
+
+	switch (video->mbus_format.code) {
+	case MEDIA_BUS_FMT_SBGGR8_1X8:
+	case MEDIA_BUS_FMT_SGBRG8_1X8:
+	case MEDIA_BUS_FMT_SGRBG8_1X8:
+	case MEDIA_BUS_FMT_SRGGB8_1X8:
+		data_type = MIPI_CSI2_DATA_TYPE_RAW8;
+		bpp = 8;
+		break;
+	case MEDIA_BUS_FMT_SBGGR10_1X10:
+	case MEDIA_BUS_FMT_SGBRG10_1X10:
+	case MEDIA_BUS_FMT_SGRBG10_1X10:
+	case MEDIA_BUS_FMT_SRGGB10_1X10:
+		data_type = MIPI_CSI2_DATA_TYPE_RAW10;
+		bpp = 10;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* Sensor pixel rate */
+
+	ctrl = v4l2_ctrl_find(remote_subdev->ctrl_handler, V4L2_CID_PIXEL_RATE);
+	if (!ctrl) {
+		dev_err(cdev->dev,
+			"%s: no MIPI CSI-2 pixel rate from the sensor\n",
+			__func__);
+		return -ENODEV;
+	}
+
+	pixel_rate = (unsigned long)v4l2_ctrl_g_ctrl_int64(ctrl);
+	if (!pixel_rate) {
+		dev_err(cdev->dev,
+			"%s: zero MIPI CSI-2 pixel rate from the sensor\n",
+			__func__);
+		return -ENODEV;
+	}
+
+	/* Power management */
+
+	ret = pm_runtime_get_sync(cdev->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(cdev->dev);
+		return ret;
+	}
+
+	/* D-PHY configuration */
+
+	lanes_count = bus_mipi_csi2->num_data_lanes;
+	phy_mipi_dphy_get_default_config(pixel_rate, bpp, lanes_count,
+					 dphy_cfg);
+
+	/*
+	 * Note that our hardware is using DDR, which is not taken in account by
+	 * phy_mipi_dphy_get_default_config when calculating hs_clk_rate from
+	 * the pixel rate, lanes count and bpp.
+	 *
+	 * The resulting clock rate is basically the symbol rate over the whole
+	 * link. The actual clock rate is calculated with division by two since
+	 * DDR samples both on rising and falling edges.
+	 */
+
+	dev_dbg(cdev->dev, "A83T MIPI CSI-2 config:\n");
+	dev_dbg(cdev->dev,
+		"%ld pixels/s, %u bits/pixel, %u lanes, %lu Hz clock\n",
+		pixel_rate, bpp, lanes_count, dphy_cfg->hs_clk_rate / 2);
+
+	ret = phy_reset(cdev->dphy);
+	if (ret) {
+		dev_err(cdev->dev, "failed to reset MIPI D-PHY\n");
+		goto error_pm;
+	}
+
+	ret = phy_set_mode_ext(cdev->dphy, PHY_MODE_MIPI_DPHY,
+			       PHY_MIPI_DPHY_SUBMODE_RX);
+	if (ret) {
+		dev_err(cdev->dev, "failed to set MIPI D-PHY mode\n");
+		goto error_pm;
+	}
+
+	ret = phy_configure(cdev->dphy, &dphy_opts);
+	if (ret) {
+		dev_err(cdev->dev, "failed to configure MIPI D-PHY\n");
+		goto error_pm;
+	}
+
+	ret = phy_power_on(cdev->dphy);
+	if (ret) {
+		dev_err(cdev->dev, "failed to power on MIPI D-PHY\n");
+		goto error_pm;
+	}
+
+	/* MIPI CSI-2 controller setup */
+
+	regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_CTRL_REG,
+		     SUN8I_A83T_MIPI_CSI2_CTRL_RESET_N);
+
+	regmap_read(regmap, SUN8I_A83T_MIPI_CSI2_VERSION_REG, &version);
+
+	dev_dbg(cdev->dev, "A83T MIPI CSI-2 version: %04x\n", version);
+
+	regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_CFG_REG,
+		     SUN8I_A83T_MIPI_CSI2_CFG_UNPKT_EN |
+		     SUN8I_A83T_MIPI_CSI2_CFG_SYNC_DLY_CYCLE(8) |
+		     SUN8I_A83T_MIPI_CSI2_CFG_N_CHANNEL(1) |
+		     SUN8I_A83T_MIPI_CSI2_CFG_N_LANE(lanes_count));
+
+	/*
+	 * Our MIPI CSI-2 controller has internal channels that can be
+	 * configured to match a specific MIPI CSI-2 virtual channel and/or
+	 * a specific data type. Each internal channel can be piped to an
+	 * internal channel of the CSI controller.
+	 *
+	 * We set virtual channel numbers to all channels to make sure that
+	 * virtual channel 0 goes to CSI channel 0 only.
+	 */
+	regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_VCDT0_REG,
+		     SUN8I_A83T_MIPI_CSI2_VCDT0_CH_VC(3, 3) |
+		     SUN8I_A83T_MIPI_CSI2_VCDT0_CH_VC(2, 2) |
+		     SUN8I_A83T_MIPI_CSI2_VCDT0_CH_VC(1, 1) |
+		     SUN8I_A83T_MIPI_CSI2_VCDT0_CH_VC(0, 0) |
+		     SUN8I_A83T_MIPI_CSI2_VCDT0_CH_DT(0, data_type));
+
+	/* Start streaming. */
+	regmap_update_bits(regmap, SUN8I_A83T_MIPI_CSI2_CFG_REG,
+			   SUN8I_A83T_MIPI_CSI2_CFG_SYNC_EN,
+			   SUN8I_A83T_MIPI_CSI2_CFG_SYNC_EN);
+
+	ret = v4l2_subdev_call(remote_subdev, video, s_stream, 1);
+	if (ret)
+		goto disable;
+
+	return 0;
+
+disable:
+	regmap_update_bits(regmap, SUN8I_A83T_MIPI_CSI2_CFG_REG,
+			   SUN8I_A83T_MIPI_CSI2_CFG_SYNC_EN, 0);
+
+	regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_CTRL_REG, 0);
+
+	phy_power_off(cdev->dphy);
+
+error_pm:
+	pm_runtime_put(cdev->dev);
+
+	return ret;
+}
+
+static const
+struct v4l2_subdev_video_ops sun8i_a83t_mipi_csi2_subdev_video_ops = {
+	.s_stream	= sun8i_a83t_mipi_csi2_s_stream,
+};
+
+/* Pad */
+
+static int
+sun8i_a83t_mipi_csi2_enum_mbus_code(struct v4l2_subdev *subdev,
+				    struct v4l2_subdev_pad_config *config,
+				    struct v4l2_subdev_mbus_code_enum *code_enum)
+{
+	if (code_enum->index >= ARRAY_SIZE(sun8i_a83t_mipi_csi2_mbus_codes))
+		return -EINVAL;
+
+	code_enum->code = sun8i_a83t_mipi_csi2_mbus_codes[code_enum->index];
+
+	return 0;
+}
+
+static int sun8i_a83t_mipi_csi2_get_fmt(struct v4l2_subdev *subdev,
+				   struct v4l2_subdev_pad_config *config,
+				   struct v4l2_subdev_format *format)
+{
+	struct sun8i_a83t_mipi_csi2_video *video =
+		sun8i_a83t_mipi_csi2_subdev_video(subdev);
+	struct v4l2_mbus_framefmt *mbus_format = &format->format;
+
+	if (format->which == V4L2_SUBDEV_FORMAT_TRY)
+		*mbus_format = *v4l2_subdev_get_try_format(subdev, config,
+							   format->pad);
+	else
+		*mbus_format = video->mbus_format;
+
+	return 0;
+}
+
+static int sun8i_a83t_mipi_csi2_set_fmt(struct v4l2_subdev *subdev,
+				   struct v4l2_subdev_pad_config *config,
+				   struct v4l2_subdev_format *format)
+{
+	struct sun8i_a83t_mipi_csi2_video *video =
+		sun8i_a83t_mipi_csi2_subdev_video(subdev);
+	struct v4l2_mbus_framefmt *mbus_format = &format->format;
+
+	if (format->which == V4L2_SUBDEV_FORMAT_TRY)
+		*v4l2_subdev_get_try_format(subdev, config, format->pad) =
+			*mbus_format;
+	else
+		video->mbus_format = *mbus_format;
+
+	return 0;
+}
+
+static const struct v4l2_subdev_pad_ops sun8i_a83t_mipi_csi2_subdev_pad_ops = {
+	.enum_mbus_code	= sun8i_a83t_mipi_csi2_enum_mbus_code,
+	.get_fmt	= sun8i_a83t_mipi_csi2_get_fmt,
+	.set_fmt	= sun8i_a83t_mipi_csi2_set_fmt,
+};
+
+/* Subdev */
+
+static const struct v4l2_subdev_ops sun8i_a83t_mipi_csi2_subdev_ops = {
+	.video		= &sun8i_a83t_mipi_csi2_subdev_video_ops,
+	.pad		= &sun8i_a83t_mipi_csi2_subdev_pad_ops,
+};
+
+/* Notifier */
+
+static int
+sun8i_a83t_mipi_csi2_notifier_bound(struct v4l2_async_notifier *notifier,
+				    struct v4l2_subdev *remote_subdev,
+				    struct v4l2_async_subdev *remote_subdev_async)
+{
+	struct v4l2_subdev *subdev = notifier->sd;
+	struct sun8i_a83t_mipi_csi2_video *video =
+		sun8i_a83t_mipi_csi2_subdev_video(subdev);
+	struct sun8i_a83t_mipi_csi2_dev *cdev =
+		sun8i_a83t_mipi_csi2_video_dev(video);
+	int source_pad;
+	int ret;
+
+	source_pad = media_entity_get_fwnode_pad(&remote_subdev->entity,
+						 remote_subdev->fwnode,
+						 MEDIA_PAD_FL_SOURCE);
+	if (source_pad < 0)
+		return source_pad;
+
+	ret = media_create_pad_link(&remote_subdev->entity, source_pad,
+				    &subdev->entity, 0,
+				    MEDIA_LNK_FL_ENABLED |
+				    MEDIA_LNK_FL_IMMUTABLE);
+	if (ret) {
+		dev_err(cdev->dev, "failed to create %s:%u -> %s:%u link\n",
+			remote_subdev->entity.name, source_pad,
+			subdev->entity.name, 0);
+		return ret;
+	}
+
+	video->remote_subdev = remote_subdev;
+
+	return 0;
+}
+
+static const
+struct v4l2_async_notifier_operations sun8i_a83t_mipi_csi2_notifier_ops = {
+	.bound		= sun8i_a83t_mipi_csi2_notifier_bound,
+};
+
+/* Media Entity */
+
+static const struct media_entity_operations sun8i_a83t_mipi_csi2_entity_ops = {
+	.link_validate	= v4l2_subdev_link_validate,
+};
+
+/* Base Driver */
+
+static int __maybe_unused sun8i_a83t_mipi_csi2_suspend(struct device *dev)
+{
+	struct sun8i_a83t_mipi_csi2_dev *cdev = dev_get_drvdata(dev);
+
+	clk_disable_unprepare(cdev->clk_misc);
+	clk_disable_unprepare(cdev->clk_mipi);
+	clk_disable_unprepare(cdev->clk_mod);
+	clk_disable_unprepare(cdev->clk_bus);
+	reset_control_assert(cdev->reset);
+
+	return 0;
+}
+
+static int __maybe_unused sun8i_a83t_mipi_csi2_resume(struct device *dev)
+{
+	struct sun8i_a83t_mipi_csi2_dev *cdev = dev_get_drvdata(dev);
+	int ret;
+
+	ret = reset_control_deassert(cdev->reset);
+	if (ret) {
+		dev_err(cdev->dev, "failed to deassert reset\n");
+		return ret;
+	}
+
+	ret = clk_prepare_enable(cdev->clk_bus);
+	if (ret) {
+		dev_err(cdev->dev, "failed to enable bus clock\n");
+		goto error_reset;
+	}
+
+	ret = clk_prepare_enable(cdev->clk_mod);
+	if (ret) {
+		dev_err(cdev->dev, "failed to enable module clock\n");
+		goto error_clk_bus;
+	}
+
+	ret = clk_prepare_enable(cdev->clk_mipi);
+	if (ret) {
+		dev_err(cdev->dev, "failed to enable MIPI clock\n");
+		goto error_clk_mod;
+	}
+
+	ret = clk_prepare_enable(cdev->clk_misc);
+	if (ret) {
+		dev_err(cdev->dev, "failed to enable CSI misc clock\n");
+		goto error_clk_mipi;
+	}
+
+	sun8i_a83t_mipi_csi2_init(cdev);
+
+	return 0;
+
+error_clk_mipi:
+	clk_disable_unprepare(cdev->clk_mipi);
+
+error_clk_mod:
+	clk_disable_unprepare(cdev->clk_mod);
+
+error_clk_bus:
+	clk_disable_unprepare(cdev->clk_bus);
+
+error_reset:
+	reset_control_assert(cdev->reset);
+
+	return ret;
+}
+
+static int
+sun8i_a83t_mipi_csi2_v4l2_setup(struct sun8i_a83t_mipi_csi2_dev *cdev)
+{
+	struct sun8i_a83t_mipi_csi2_video *video = &cdev->video;
+	struct v4l2_subdev *subdev = &video->subdev;
+	struct v4l2_async_notifier *notifier = &video->notifier;
+	struct fwnode_handle *handle;
+	struct v4l2_fwnode_endpoint *endpoint;
+	struct v4l2_async_subdev *subdev_async;
+	int ret;
+
+	/* Subdev */
+
+	v4l2_subdev_init(subdev, &sun8i_a83t_mipi_csi2_subdev_ops);
+	subdev->dev = cdev->dev;
+	subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	strscpy(subdev->name, MODULE_NAME, sizeof(subdev->name));
+	v4l2_set_subdevdata(subdev, cdev);
+
+	/* Entity */
+
+	subdev->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
+	subdev->entity.ops = &sun8i_a83t_mipi_csi2_entity_ops;
+
+	/* Pads */
+
+	video->pads[0].flags = MEDIA_PAD_FL_SINK;
+	video->pads[1].flags = MEDIA_PAD_FL_SOURCE;
+
+	ret = media_entity_pads_init(&subdev->entity, 2, video->pads);
+	if (ret)
+		return ret;
+
+	/* Endpoint */
+
+	handle = fwnode_graph_get_endpoint_by_id(dev_fwnode(cdev->dev), 0, 0,
+						 FWNODE_GRAPH_ENDPOINT_NEXT);
+	if (!handle) {
+		ret = -ENODEV;
+		goto error_media_entity;
+	}
+
+	endpoint = &video->endpoint;
+	endpoint->bus_type = V4L2_MBUS_CSI2_DPHY;
+
+	ret = v4l2_fwnode_endpoint_parse(handle, endpoint);
+	fwnode_handle_put(handle);
+	if (ret)
+		goto error_media_entity;
+
+	/* Notifier */
+
+	v4l2_async_notifier_init(notifier);
+
+	subdev_async = &video->subdev_async;
+	ret = v4l2_async_notifier_add_fwnode_remote_subdev(notifier, handle,
+							   subdev_async);
+	if (ret)
+		goto error_media_entity;
+
+	video->notifier.ops = &sun8i_a83t_mipi_csi2_notifier_ops;
+
+	ret = v4l2_async_subdev_notifier_register(subdev, notifier);
+	if (ret < 0)
+		goto error_notifier;
+
+	/* Subdev */
+
+	ret = v4l2_async_register_subdev(subdev);
+	if (ret < 0)
+		goto error_notifier_registered;
+
+	/* Runtime PM */
+
+	pm_runtime_enable(cdev->dev);
+	pm_runtime_set_suspended(cdev->dev);
+
+	return 0;
+
+error_notifier_registered:
+	v4l2_async_notifier_unregister(notifier);
+error_notifier:
+	v4l2_async_notifier_cleanup(notifier);
+error_media_entity:
+	media_entity_cleanup(&subdev->entity);
+
+	return ret;
+}
+
+static int
+sun8i_a83t_mipi_csi2_v4l2_teardown(struct sun8i_a83t_mipi_csi2_dev *cdev)
+{
+	struct sun8i_a83t_mipi_csi2_video *video = &cdev->video;
+	struct v4l2_subdev *subdev = &video->subdev;
+	struct v4l2_async_notifier *notifier = &video->notifier;
+
+	v4l2_async_unregister_subdev(subdev);
+	v4l2_async_notifier_unregister(notifier);
+	v4l2_async_notifier_cleanup(notifier);
+	media_entity_cleanup(&subdev->entity);
+	v4l2_device_unregister_subdev(subdev);
+
+	return 0;
+}
+
+static const struct regmap_config sun8i_a83t_mipi_csi2_regmap_config = {
+	.reg_bits       = 32,
+	.reg_stride     = 4,
+	.val_bits       = 32,
+	.max_register	= 0x120,
+};
+
+static int sun8i_a83t_mipi_csi2_probe(struct platform_device *pdev)
+{
+	struct sun8i_a83t_mipi_csi2_dev *cdev;
+	struct resource *res;
+	void __iomem *io_base;
+	int ret;
+
+	cdev = devm_kzalloc(&pdev->dev, sizeof(*cdev), GFP_KERNEL);
+	if (!cdev)
+		return -ENOMEM;
+
+	cdev->dev = &pdev->dev;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	io_base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(io_base))
+		return PTR_ERR(io_base);
+
+	cdev->regmap =
+		devm_regmap_init_mmio(&pdev->dev, io_base,
+				      &sun8i_a83t_mipi_csi2_regmap_config);
+	if (IS_ERR(cdev->regmap)) {
+		dev_err(&pdev->dev, "failed to init register map\n");
+		return PTR_ERR(cdev->regmap);
+	}
+
+	cdev->clk_bus = devm_clk_get(&pdev->dev, "bus");
+	if (IS_ERR(cdev->clk_bus)) {
+		dev_err(&pdev->dev, "failed to acquire bus clock\n");
+		return PTR_ERR(cdev->clk_bus);
+	}
+
+	cdev->clk_mod = devm_clk_get(&pdev->dev, "mod");
+	if (IS_ERR(cdev->clk_mod)) {
+		dev_err(&pdev->dev, "failed to acquire mod clock\n");
+		return PTR_ERR(cdev->clk_mod);
+	}
+
+	cdev->clk_mipi = devm_clk_get(&pdev->dev, "mipi");
+	if (IS_ERR(cdev->clk_mipi)) {
+		dev_err(&pdev->dev, "failed to acquire mipi clock\n");
+		return PTR_ERR(cdev->clk_mipi);
+	}
+
+	cdev->clk_misc = devm_clk_get(&pdev->dev, "misc");
+	if (IS_ERR(cdev->clk_misc)) {
+		dev_err(&pdev->dev, "failed to acquire misc clock\n");
+		return PTR_ERR(cdev->clk_misc);
+	}
+
+	cdev->reset = devm_reset_control_get_shared(&pdev->dev, NULL);
+	if (IS_ERR(cdev->reset)) {
+		dev_err(&pdev->dev, "failed to get reset controller\n");
+		return PTR_ERR(cdev->reset);
+	}
+
+	ret = sun8i_a83t_dphy_register(cdev);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to init MIPI D-PHY\n");
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, cdev);
+
+	ret = sun8i_a83t_mipi_csi2_v4l2_setup(cdev);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int sun8i_a83t_mipi_csi2_remove(struct platform_device *pdev)
+{
+	struct sun8i_a83t_mipi_csi2_dev *cdev = platform_get_drvdata(pdev);
+
+	phy_exit(cdev->dphy);
+
+	return sun8i_a83t_mipi_csi2_v4l2_teardown(cdev);
+}
+
+static const struct dev_pm_ops sun8i_a83t_mipi_csi2_pm_ops = {
+	SET_RUNTIME_PM_OPS(sun8i_a83t_mipi_csi2_suspend,
+			   sun8i_a83t_mipi_csi2_resume, NULL)
+};
+
+static const struct of_device_id sun8i_a83t_mipi_csi2_of_match[] = {
+	{ .compatible = "allwinner,sun8i-a83t-mipi-csi2" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, sun8i_a83t_mipi_csi2_of_match);
+
+static struct platform_driver sun8i_a83t_mipi_csi2_platform_driver = {
+	.probe = sun8i_a83t_mipi_csi2_probe,
+	.remove = sun8i_a83t_mipi_csi2_remove,
+	.driver = {
+		.name = MODULE_NAME,
+		.of_match_table = of_match_ptr(sun8i_a83t_mipi_csi2_of_match),
+		.pm = &sun8i_a83t_mipi_csi2_pm_ops,
+	},
+};
+module_platform_driver(sun8i_a83t_mipi_csi2_platform_driver);
+
+MODULE_DESCRIPTION("Allwinner A83T MIPI CSI-2 and D-PHY Controller Driver");
+MODULE_AUTHOR("Paul Kocialkowski <paul.kocialkowski@bootlin.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.h b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.h
new file mode 100644
index 000000000000..5b4e995392bb
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.h
@@ -0,0 +1,197 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2020 Kévin L'hôpital <kevin.lhopital@bootlin.com>
+ * Copyright 2020 Bootlin
+ * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
+ */
+
+#ifndef __SUN8I_A83T_MIPI_CSI2_H__
+#define __SUN8I_A83T_MIPI_CSI2_H__
+
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+
+#define SUN8I_A83T_MIPI_CSI2_VERSION_REG			0x0
+#define SUN8I_A83T_MIPI_CSI2_CTRL_REG				0x4
+#define SUN8I_A83T_MIPI_CSI2_CTRL_INIT_VALUE			0xb8c39bec
+#define SUN8I_A83T_MIPI_CSI2_CTRL_RESET_N			BIT(31)
+#define SUN8I_A83T_MIPI_CSI2_RX_PKT_NUM_REG			0x8
+#define SUN8I_A83T_MIPI_CSI2_RX_PKT_NUM_INIT_VALUE		0xb8d257f8
+#define SUN8I_A83T_MIPI_CSI2_RSVD0_REG				0xc
+
+#define SUN8I_A83T_MIPI_CSI2_RSVD1_REG				0x18
+#define SUN8I_A83T_MIPI_CSI2_RSVD1_HW_LOCK_VALUE		0xb8c8a30c
+#define SUN8I_A83T_MIPI_CSI2_RSVD2_REG				0x1c
+#define SUN8I_A83T_MIPI_CSI2_RSVD2_HW_LOCK_VALUE		0xb8df8ad7
+#define SUN8I_A83T_MIPI_CSI2_INT_STA0_REG			0x20
+#define SUN8I_A83T_MIPI_CSI2_INT_STA0_ECC_ERR_DBL		BIT(28)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA0_LINE_CKSM_ERR_VC3		BIT(27)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA0_LINE_CKSM_ERR_VC2		BIT(26)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA0_LINE_CKSM_ERR_VC1		BIT(25)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA0_LINE_CKSM_ERR_VC0		BIT(24)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA0_LINE_SEQ_ERR_DT3		BIT(23)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA0_LINE_SEQ_ERR_DT2		BIT(22)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA0_LINE_SEQ_ERR_DT1		BIT(21)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA0_LINE_SEQ_ERR_DT0		BIT(20)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA0_LS_LE_ERR_DT3		BIT(19)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA0_LS_LE_ERR_DT2		BIT(18)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA0_LS_LE_ERR_DT1		BIT(17)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA0_LS_LE_ERR_DT0		BIT(16)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA0_CRC_ERR_VC3		BIT(15)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA0_CRC_ERR_VC2		BIT(14)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA0_CRC_ERR_VC1		BIT(13)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA0_CRC_ERR_VC0		BIT(12)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA0_FRM_SEQ_ERR_VC3		BIT(11)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA0_FRM_SEQ_ERR_VC2		BIT(10)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA0_FRM_SEQ_ERR_VC1		BIT(9)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA0_FRM_SEQ_ERR_VC0		BIT(8)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA0_FS_FE_ERR_VC3		BIT(7)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA0_FS_FE_ERR_VC2		BIT(6)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA0_FS_FE_ERR_VC1		BIT(5)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA0_FS_FE_ERR_VC0		BIT(4)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA0_SOT_SYNC_ERR_3		BIT(3)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA0_SOT_SYNC_ERR_2		BIT(2)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA0_SOT_SYNC_ERR_1		BIT(1)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA0_SOT_SYNC_ERR_0		BIT(0)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA1_REG			0x24
+#define SUN8I_A83T_MIPI_CSI2_INT_STA1_LINE_SEQ_ERR_DT7		BIT(23)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA1_LINE_SEQ_ERR_DT6		BIT(22)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA1_LINE_SEQ_ERR_DT5		BIT(21)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA1_LINE_SEQ_ERR_DT4		BIT(20)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA1_LS_LE_ERR_DT7		BIT(19)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA1_LS_LE_ERR_DT6		BIT(18)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA1_LS_LE_ERR_DT5		BIT(17)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA1_LS_LE_ERR_DT4		BIT(16)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA1_DT_ERR_VC3		BIT(15)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA1_DT_ERR_VC2		BIT(14)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA1_DT_ERR_VC1		BIT(13)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA1_DT_ERR_VC0		BIT(12)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA1_ECC_ERR1_VC3		BIT(11)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA1_ECC_ERR1_VC2		BIT(10)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA1_ECC_ERR1_VC1		BIT(9)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA1_ECC_ERR1_VC0		BIT(8)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA1_SOT_ERR_3			BIT(7)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA1_SOT_ERR_2			BIT(6)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA1_SOT_ERR_1			BIT(5)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA1_SOT_ERR_0			BIT(4)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA1_ESC_ENTRY_ERR_3		BIT(3)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA1_ESC_ENTRY_ERR_2		BIT(2)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA1_ESC_ENTRY_ERR_1		BIT(1)
+#define SUN8I_A83T_MIPI_CSI2_INT_STA1_ESC_ENTRY_ERR_0		BIT(0)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_REG			0x28
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_ECC_ERR_DBL		BIT(28)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_CKSM_ERR_VC3		BIT(27)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_CKSM_ERR_VC2		BIT(26)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_CKSM_ERR_VC1		BIT(25)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_CKSM_ERR_VC0		BIT(24)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_LINE_SEQ_ERR_DT3		BIT(23)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_LINE_SEQ_ERR_DT2		BIT(22)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_LINE_SEQ_ERR_DT1		BIT(21)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_LINE_SEQ_ERR_DT0		BIT(20)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_LS_LE_ERR_DT3		BIT(19)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_LS_LE_ERR_DT2		BIT(18)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_LS_LE_ERR_DT1		BIT(17)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_LS_LE_ERR_DT0		BIT(16)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_CRC_ERR_VC3		BIT(15)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_CRC_ERR_VC2		BIT(14)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_CRC_ERR_VC1		BIT(13)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_CRC_ERR_VC0		BIT(12)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_FRM_SEQ_ERR_VC3		BIT(11)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_FRM_SEQ_ERR_VC2		BIT(10)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_FRM_SEQ_ERR_VC1		BIT(9)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_FRM_SEQ_ERR_VC0		BIT(8)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_FS_FE_ERR_VC3		BIT(7)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_FS_FE_ERR_VC2		BIT(6)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_FS_FE_ERR_VC1		BIT(5)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_FS_FE_ERR_VC0		BIT(4)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_SOT_SYNC_ERR_3		BIT(3)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_SOT_SYNC_ERR_2		BIT(2)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_SOT_SYNC_ERR_1		BIT(1)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_SOT_SYNC_ERR_0		BIT(0)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK1_REG			0x2c
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK1_DT_ERR_VC3		BIT(15)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK1_DT_ERR_VC2		BIT(14)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK1_DT_ERR_VC1		BIT(13)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK1_DT_ERR_VC0		BIT(12)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK1_ECC_ERR1_VC3		BIT(11)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK1_ECC_ERR1_VC2		BIT(10)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK1_ECC_ERR1_VC1		BIT(9)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK1_ECC_ERR1_VC0		BIT(8)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK1_SOT_ERR_3			BIT(7)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK1_SOT_ERR_2			BIT(6)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK1_SOT_ERR_1			BIT(5)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK1_SOT_ERR_0			BIT(4)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK1_ESC_ENTRY_ERR_3		BIT(3)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK1_ESC_ENTRY_ERR_2		BIT(2)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK1_ESC_ENTRY_ERR_1		BIT(1)
+#define SUN8I_A83T_MIPI_CSI2_INT_MSK1_ESC_ENTRY_ERR_0		BIT(0)
+
+#define SUN8I_A83T_MIPI_CSI2_CFG_REG				0x100
+#define SUN8I_A83T_MIPI_CSI2_CFG_INIT_VALUE			0xb8c64f24
+#define SUN8I_A83T_MIPI_CSI2_CFG_SYNC_EN			BIT(31)
+#define SUN8I_A83T_MIPI_CSI2_CFG_BYPASS_ECC_EN			BIT(29)
+#define SUN8I_A83T_MIPI_CSI2_CFG_UNPKT_EN			BIT(28)
+#define SUN8I_A83T_MIPI_CSI2_CFG_NONE_UNPKT_RX_MODE		BIT(27)
+#define SUN8I_A83T_MIPI_CSI2_CFG_YC_SWAB			BIT(26)
+#define SUN8I_A83T_MIPI_CSI2_CFG_N_BYTE				BIT(24)
+#define SUN8I_A83T_MIPI_CSI2_CFG_SYNC_DLY_CYCLE(v)		(((v) << 18) & \
+								 GENMASK(22, 18))
+#define SUN8I_A83T_MIPI_CSI2_CFG_N_CHANNEL(v)			((((v) - 1) << 16) & \
+								 GENMASK(17, 16))
+#define SUN8I_A83T_MIPI_CSI2_CFG_N_LANE(v)			((((v) - 1) << 4) & \
+								 GENMASK(5, 4))
+#define SUN8I_A83T_MIPI_CSI2_VCDT0_REG				0x104
+#define SUN8I_A83T_MIPI_CSI2_VCDT0_CH_VC(ch, vc)		(((vc) & GENMASK(1, 0)) << \
+								 ((ch) * 8 + 6))
+#define SUN8I_A83T_MIPI_CSI2_VCDT0_CH_DT(ch, t)			(((t) & GENMASK(5, 0)) << \
+								 ((ch) * 8))
+#define SUN8I_A83T_MIPI_CSI2_VCDT1_REG				0x108
+#define SUN8I_A83T_MIPI_CSI2_VCDT1_CH_VC(ch, vc)		(((vc) & GENMASK(1, 0)) << \
+								 (((ch) - 4) * 8 + 6))
+#define SUN8I_A83T_MIPI_CSI2_VCDT1_CH_DT(ch, t)			(((t) & GENMASK(5, 0)) << \
+								 (((ch) - 4) * 8))
+
+enum mipi_csi2_data_type {
+	MIPI_CSI2_DATA_TYPE_RAW8	= 0x2a,
+	MIPI_CSI2_DATA_TYPE_RAW10	= 0x2b,
+	MIPI_CSI2_DATA_TYPE_RAW12	= 0x2c,
+};
+
+struct sun8i_a83t_mipi_csi2_video {
+	struct v4l2_fwnode_endpoint endpoint;
+	struct v4l2_subdev subdev;
+	struct media_pad pads[2];
+
+	struct v4l2_async_subdev subdev_async;
+	struct v4l2_async_notifier notifier;
+
+	struct v4l2_subdev *remote_subdev;
+
+	struct v4l2_mbus_framefmt mbus_format;
+};
+
+struct sun8i_a83t_mipi_csi2_dev {
+	struct device *dev;
+
+	struct regmap *regmap;
+	struct clk *clk_bus;
+	struct clk *clk_mod;
+	struct clk *clk_mipi;
+	struct clk *clk_misc;
+	struct reset_control *reset;
+	struct phy *dphy;
+	struct phy_configure_opts_mipi_dphy dphy_config;
+
+	struct sun8i_a83t_mipi_csi2_video video;
+};
+
+#define sun8i_a83t_mipi_csi2_subdev_video(subdev) \
+	container_of(subdev, struct sun8i_a83t_mipi_csi2_video, subdev)
+
+#define sun8i_a83t_mipi_csi2_video_dev(video) \
+	container_of(video, struct sun8i_a83t_mipi_csi2_dev, video)
+
+#endif /* __SUN8I_A83T_MIPI_CSI2_H__ */
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v2 18/19] ARM: dts: sun8i: a83t: Add MIPI CSI-2 controller node
  2020-11-28 14:28 [PATCH v2 00/19] Allwinner MIPI CSI-2 support for A31/V3s/A83T Paul Kocialkowski
                   ` (16 preceding siblings ...)
  2020-11-28 14:28 ` [PATCH v2 17/19] media: sunxi: Add support for the A83T MIPI CSI-2 controller Paul Kocialkowski
@ 2020-11-28 14:28 ` Paul Kocialkowski
  2020-11-28 14:28 ` [PATCH v2 19/19] MAINTAINERS: Add entry for the Allwinner A83T MIPI CSI-2 bridge Paul Kocialkowski
  18 siblings, 0 replies; 39+ messages in thread
From: Paul Kocialkowski @ 2020-11-28 14:28 UTC (permalink / raw)
  To: linux-media, devicetree, linux-arm-kernel, linux-kernel,
	linux-doc, devel, linux-sunxi
  Cc: Yong Deng, Mauro Carvalho Chehab, Rob Herring, Maxime Ripard,
	Chen-Yu Tsai, Jernej Skrabec, Paul Kocialkowski, Jonathan Corbet,
	Kishon Vijay Abraham I, Vinod Koul, Helen Koike,
	Dafna Hirschfeld, Greg Kroah-Hartman, Philipp Zabel,
	Sakari Ailus, Hans Verkuil, Thomas Petazzoni, kevin.lhopital

MIPI CSI-2 is supported on the A83T with a dedicated controller that
covers both the protocol and D-PHY. It can be connected to the CSI
interface as a V4L2 subdev through the fwnode graph.

This is not done by default since connecting the bridge without a
subdev attached to it will cause a failure on the CSI driver.

Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
---
 arch/arm/boot/dts/sun8i-a83t.dtsi | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/arch/arm/boot/dts/sun8i-a83t.dtsi b/arch/arm/boot/dts/sun8i-a83t.dtsi
index 3ce030f7e05d..ee19cbb565a6 100644
--- a/arch/arm/boot/dts/sun8i-a83t.dtsi
+++ b/arch/arm/boot/dts/sun8i-a83t.dtsi
@@ -1076,6 +1076,32 @@ csi_in_mipi_csi2_bridge: port@1 {
 			};
 		};
 
+		mipi_csi2: csi@1cb1000 {
+			compatible = "allwinner,sun8i-a83t-mipi-csi2";
+			reg = <0x01cb1000 0x1000>;
+			interrupts = <GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&ccu CLK_BUS_CSI>,
+				 <&ccu CLK_CSI_SCLK>,
+				 <&ccu CLK_MIPI_CSI>,
+				 <&ccu CLK_CSI_MISC>;
+			clock-names = "bus", "mod", "mipi", "misc";
+			resets = <&ccu RST_BUS_CSI>;
+			status = "disabled";
+
+			ports {
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				mipi_csi2_in: port@0 {
+					reg = <0>;
+				};
+
+				mipi_csi2_out: port@1 {
+					reg = <1>;
+				};
+			};
+		};
+
 		hdmi: hdmi@1ee0000 {
 			compatible = "allwinner,sun8i-a83t-dw-hdmi";
 			reg = <0x01ee0000 0x10000>;
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v2 19/19] MAINTAINERS: Add entry for the Allwinner A83T MIPI CSI-2 bridge
  2020-11-28 14:28 [PATCH v2 00/19] Allwinner MIPI CSI-2 support for A31/V3s/A83T Paul Kocialkowski
                   ` (17 preceding siblings ...)
  2020-11-28 14:28 ` [PATCH v2 18/19] ARM: dts: sun8i: a83t: Add MIPI CSI-2 controller node Paul Kocialkowski
@ 2020-11-28 14:28 ` Paul Kocialkowski
  18 siblings, 0 replies; 39+ messages in thread
From: Paul Kocialkowski @ 2020-11-28 14:28 UTC (permalink / raw)
  To: linux-media, devicetree, linux-arm-kernel, linux-kernel,
	linux-doc, devel, linux-sunxi
  Cc: Yong Deng, Mauro Carvalho Chehab, Rob Herring, Maxime Ripard,
	Chen-Yu Tsai, Jernej Skrabec, Paul Kocialkowski, Jonathan Corbet,
	Kishon Vijay Abraham I, Vinod Koul, Helen Koike,
	Dafna Hirschfeld, Greg Kroah-Hartman, Philipp Zabel,
	Sakari Ailus, Hans Verkuil, Thomas Petazzoni, kevin.lhopital

Add myself as maintainer of the A83T MIPI CSI-2 bridge media driver.

Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index a1352171778b..3b48612657b6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -717,6 +717,14 @@ T:	git git://linuxtv.org/media_tree.git
 F:	Documentation/devicetree/bindings/media/allwinner,sun6i-a31-mipi-csi2.yaml
 F:	drivers/media/platform/sunxi/sun6i-mipi-csi2/
 
+ALLWINNER A83T MIPI CSI-2 BRIDGE
+M:	Paul Kocialkowski <paul.kocialkowski@bootlin.com>
+L:	linux-media@vger.kernel.org
+S:	Maintained
+T:	git git://linuxtv.org/media_tree.git
+F:	Documentation/devicetree/bindings/media/allwinner,sun8i-a83t-mipi-csi2.yaml
+F:	drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/
+
 ALLWINNER CPUFREQ DRIVER
 M:	Yangtao Li <tiny.windzz@gmail.com>
 L:	linux-pm@vger.kernel.org
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 01/19] docs: phy: Add a part about PHY mode and submode
  2020-11-28 14:28 ` [PATCH v2 01/19] docs: phy: Add a part about PHY mode and submode Paul Kocialkowski
@ 2020-12-01 10:37   ` Maxime Ripard
  0 siblings, 0 replies; 39+ messages in thread
From: Maxime Ripard @ 2020-12-01 10:37 UTC (permalink / raw)
  To: Paul Kocialkowski
  Cc: linux-media, devicetree, linux-arm-kernel, linux-kernel,
	linux-doc, devel, linux-sunxi, Yong Deng, Mauro Carvalho Chehab,
	Rob Herring, Chen-Yu Tsai, Jernej Skrabec, Jonathan Corbet,
	Kishon Vijay Abraham I, Vinod Koul, Helen Koike,
	Dafna Hirschfeld, Greg Kroah-Hartman, Philipp Zabel,
	Sakari Ailus, Hans Verkuil, Thomas Petazzoni, kevin.lhopital

[-- Attachment #1: Type: text/plain, Size: 364 bytes --]

On Sat, Nov 28, 2020 at 03:28:21PM +0100, Paul Kocialkowski wrote:
> Besides giving pointers to the relevant functions for PHY mode and
> submode configuration, this clarifies the need to set them before
> powering on the PHY.
> 
> Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>

Reviewed-by: Maxime Ripard <mripard@kernel.org>

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 03/19] phy: allwinner: phy-sun6i-mipi-dphy: Support D-PHY Rx mode for MIPI CSI-2
  2020-11-28 14:28 ` [PATCH v2 03/19] phy: allwinner: phy-sun6i-mipi-dphy: Support D-PHY Rx mode for MIPI CSI-2 Paul Kocialkowski
@ 2020-12-01 10:38   ` Maxime Ripard
  0 siblings, 0 replies; 39+ messages in thread
From: Maxime Ripard @ 2020-12-01 10:38 UTC (permalink / raw)
  To: Paul Kocialkowski
  Cc: linux-media, devicetree, linux-arm-kernel, linux-kernel,
	linux-doc, devel, linux-sunxi, Yong Deng, Mauro Carvalho Chehab,
	Rob Herring, Chen-Yu Tsai, Jernej Skrabec, Jonathan Corbet,
	Kishon Vijay Abraham I, Vinod Koul, Helen Koike,
	Dafna Hirschfeld, Greg Kroah-Hartman, Philipp Zabel,
	Sakari Ailus, Hans Verkuil, Thomas Petazzoni, kevin.lhopital

[-- Attachment #1: Type: text/plain, Size: 511 bytes --]

On Sat, Nov 28, 2020 at 03:28:23PM +0100, Paul Kocialkowski wrote:
> The Allwinner A31 D-PHY supports both Rx and Tx modes. While the latter
> is already supported and used for MIPI DSI this adds support for the
> former, to be used with MIPI CSI-2.
> 
> This implementation is inspired by Allwinner's V3s Linux SDK
> implementation, which was used as a documentation base.
> 
> Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>

Acked-by: Maxime Ripard <mripard@kernel.org>

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 04/19] media: sun6i-csi: Use common V4L2 format info for storage bpp
  2020-11-28 14:28 ` [PATCH v2 04/19] media: sun6i-csi: Use common V4L2 format info for storage bpp Paul Kocialkowski
@ 2020-12-01 10:39   ` Maxime Ripard
  0 siblings, 0 replies; 39+ messages in thread
From: Maxime Ripard @ 2020-12-01 10:39 UTC (permalink / raw)
  To: Paul Kocialkowski
  Cc: linux-media, devicetree, linux-arm-kernel, linux-kernel,
	linux-doc, devel, linux-sunxi, Yong Deng, Mauro Carvalho Chehab,
	Rob Herring, Chen-Yu Tsai, Jernej Skrabec, Jonathan Corbet,
	Kishon Vijay Abraham I, Vinod Koul, Helen Koike,
	Dafna Hirschfeld, Greg Kroah-Hartman, Philipp Zabel,
	Sakari Ailus, Hans Verkuil, Thomas Petazzoni, kevin.lhopital

[-- Attachment #1: Type: text/plain, Size: 733 bytes --]

On Sat, Nov 28, 2020 at 03:28:24PM +0100, Paul Kocialkowski wrote:
> V4L2 has a common helper which can be used for calculating the number
> of stored bits per pixels of a given (stored) image format.
> 
> Use the helper-returned structure instead of our own switch/case list.
> Note that a few formats are not in that list so we keep them as
> special cases.
> 
> The custom switch/case was also wrong concerning 10/12-bit Bayer
> formats, which are aligned to 16 bits in memory. Using the common
> helper fixes it.
> 
> Fixes: 5cc7522d8965 ("media: sun6i: Add support for Allwinner CSI V3s")
> Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>

Acked-by: Maxime Ripard <mripard@kernel.org>

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 06/19] dt-bindings: media: sun6i-a31-csi: Add MIPI CSI-2 input port
  2020-11-28 14:28 ` [PATCH v2 06/19] dt-bindings: media: sun6i-a31-csi: Add MIPI CSI-2 input port Paul Kocialkowski
@ 2020-12-01 10:43   ` Maxime Ripard
  2020-12-02 14:16     ` Paul Kocialkowski
  0 siblings, 1 reply; 39+ messages in thread
From: Maxime Ripard @ 2020-12-01 10:43 UTC (permalink / raw)
  To: Paul Kocialkowski
  Cc: linux-media, devicetree, linux-arm-kernel, linux-kernel,
	linux-doc, devel, linux-sunxi, Yong Deng, Mauro Carvalho Chehab,
	Rob Herring, Chen-Yu Tsai, Jernej Skrabec, Jonathan Corbet,
	Kishon Vijay Abraham I, Vinod Koul, Helen Koike,
	Dafna Hirschfeld, Greg Kroah-Hartman, Philipp Zabel,
	Sakari Ailus, Hans Verkuil, Thomas Petazzoni, kevin.lhopital

[-- Attachment #1: Type: text/plain, Size: 3359 bytes --]

On Sat, Nov 28, 2020 at 03:28:26PM +0100, Paul Kocialkowski wrote:
> The A31 CSI controller supports two distinct input interfaces:
> parallel and an external MIPI CSI-2 bridge. The parallel interface
> is often connected to a set of hardware pins while the MIPI CSI-2
> bridge is an internal FIFO-ish link. As a result, these two inputs
> are distinguished as two different ports.
> 
> Note that only one of the two may be present on a controller instance.
> For example, the V3s has one controller dedicated to MIPI-CSI2 and one
> dedicated to parallel.
> 
> Update the binding with an explicit ports node that holds two distinct
> port nodes: one for parallel input and one for MIPI CSI-2.
> 
> This is backward-compatible with the single-port approach that was
> previously taken for representing the parallel interface port, which
> stays enumerated as fwnode port 0. However, it is now marked as
> deprecated and the multi-port approach should be preferred.
> 
> Note that additional ports may be added in the future, especially to
> support feeding the CSI controller's output to the ISP.
> 
> Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
> ---
>  .../media/allwinner,sun6i-a31-csi.yaml        | 86 ++++++++++++++++---
>  1 file changed, 73 insertions(+), 13 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-csi.yaml b/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-csi.yaml
> index 1fd9b5532a21..3bcee2d44f3c 100644
> --- a/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-csi.yaml
> +++ b/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-csi.yaml
> @@ -43,6 +43,7 @@ properties:
>    # See ./video-interfaces.txt for details
>    port:
>      type: object
> +    deprecated: true

I'm not sure we want to deprecate it. There's some SoCs with the same
controller but without the MIPI-CSI block where this would be completely
valid

>      properties:
>        endpoint:
> @@ -67,6 +68,59 @@ properties:
>  
>      additionalProperties: false
>  
> +  ports:
> +    type: object
> +
> +    properties:
> +      port@0:
> +        type: object
> +        description: Parallel input port, connect to a parallel sensor
> +
> +        properties:
> +          reg:
> +            const: 0
> +
> +          endpoint:
> +            type: object
> +
> +            properties:
> +              remote-endpoint: true
> +
> +              bus-width:
> +                enum: [ 8, 10, 12, 16 ]
> +
> +              pclk-sample: true
> +              hsync-active: true
> +              vsync-active: true
> +
> +            required:
> +              - bus-width
> +              - remote-endpoint
> +
> +        required:
> +          - endpoint
> +
> +        additionalProperties: false
> +
> +      port@1:
> +        type: object
> +        description: MIPI CSI-2 bridge input port
> +
> +        properties:
> +          reg:
> +            const: 1
> +
> +          endpoint:
> +            type: object
> +
> +            properties:
> +              remote-endpoint: true
> +
> +            required:
> +              - remote-endpoint
> +
> +        additionalProperties: false

There's a schema for the OF-graph now, you'll want to use it.

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 07/19] media: sun6i-csi: Add support for MIPI CSI-2 bridge input
  2020-11-28 14:28 ` [PATCH v2 07/19] media: sun6i-csi: Add support for MIPI CSI-2 bridge input Paul Kocialkowski
@ 2020-12-01 12:12   ` Maxime Ripard
  2020-12-02 14:19     ` Paul Kocialkowski
  0 siblings, 1 reply; 39+ messages in thread
From: Maxime Ripard @ 2020-12-01 12:12 UTC (permalink / raw)
  To: Paul Kocialkowski
  Cc: linux-media, devicetree, linux-arm-kernel, linux-kernel,
	linux-doc, devel, linux-sunxi, Yong Deng, Mauro Carvalho Chehab,
	Rob Herring, Chen-Yu Tsai, Jernej Skrabec, Jonathan Corbet,
	Kishon Vijay Abraham I, Vinod Koul, Helen Koike,
	Dafna Hirschfeld, Greg Kroah-Hartman, Philipp Zabel,
	Sakari Ailus, Hans Verkuil, Thomas Petazzoni, kevin.lhopital

[-- Attachment #1: Type: text/plain, Size: 1961 bytes --]

Hi,

On Sat, Nov 28, 2020 at 03:28:27PM +0100, Paul Kocialkowski wrote:
> The A31 CSI controller supports a MIPI CSI-2 bridge input, which has
> its own dedicated port in the fwnode graph.
> 
> Support for this input is added with this change:
> - two pads are defined for the media entity instead of one
>   and only one needs to be connected at a time;
> - the pads currently match the fwnode graph representation;
> - links are created between our pads and the subdevs for each
>   interface and are no longer immutable so that userspace can select
>   which interface to use in case both are bound to a subdev;
> - fwnode endpoints are parsed and stored for each interface;
> - the active subdev (and fwnode endpoint) is retrieved when validating
>   the media link at stream on time and cleared at stream off;
> - an error is raised if both links are active at the same time;
> - the MIPI interface bit is set if the MIPI CSI-2 bridge endpoint is
>   active.
> 
> In the future, the media entity representation might evolve to:
> - distinguish the internal parallel bridge and data formatter;
> - represent each of the 4 internal channels that can exist between
>   the parallel bridge (for BT656 time-multiplex) and MIPI CSI-2
>   (internal channels can be mapped to virtual channels);
> - connect the controller's output to the ISP instead of its
>   DMA engine.
> 
> Finally note that the MIPI CSI-2 bridges should not be linked in
> the fwnode graph unless they have a sensor subdev attached.

I'll leave most of the review to Laurent and Sakari, but I'm not quite
sure what you meant in the last paragraph. Did you mean that the
MIPI-CSI controller in the Allwinner SoC should only be linked if it has
a sensor attached, or did you mean that any MIPI-CSI2 bridge cannot be
attached to the controller?

Also, having somewhere (like your cover letter) the media-ctl setup you
tested with would be great.

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 09/19] ARM: dts: sunxi: h3/h5: Add CSI controller port for parallel input
  2020-11-28 14:28 ` [PATCH v2 09/19] ARM: dts: sunxi: h3/h5: Add CSI controller port for parallel input Paul Kocialkowski
@ 2020-12-01 12:14   ` Maxime Ripard
  2020-12-02 15:02     ` Paul Kocialkowski
  0 siblings, 1 reply; 39+ messages in thread
From: Maxime Ripard @ 2020-12-01 12:14 UTC (permalink / raw)
  To: Paul Kocialkowski
  Cc: linux-media, devicetree, linux-arm-kernel, linux-kernel,
	linux-doc, devel, linux-sunxi, Yong Deng, Mauro Carvalho Chehab,
	Rob Herring, Chen-Yu Tsai, Jernej Skrabec, Jonathan Corbet,
	Kishon Vijay Abraham I, Vinod Koul, Helen Koike,
	Dafna Hirschfeld, Greg Kroah-Hartman, Philipp Zabel,
	Sakari Ailus, Hans Verkuil, Thomas Petazzoni, kevin.lhopital

[-- Attachment #1: Type: text/plain, Size: 1181 bytes --]

On Sat, Nov 28, 2020 at 03:28:29PM +0100, Paul Kocialkowski wrote:
> Since the CSI controller binding is getting a bit more complex due
> to the addition of MIPI CSI-2 bridge support, make the ports node
> explicit with the parallel port.
> 
> This way, it's clear that the controller only supports parallel
> interface input and there's no confusion about the port number.
> 
> Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
> ---
>  arch/arm/boot/dts/sunxi-h3-h5.dtsi | 9 +++++++++
>  1 file changed, 9 insertions(+)
> 
> diff --git a/arch/arm/boot/dts/sunxi-h3-h5.dtsi b/arch/arm/boot/dts/sunxi-h3-h5.dtsi
> index 9be13378d4df..02b698cace6a 100644
> --- a/arch/arm/boot/dts/sunxi-h3-h5.dtsi
> +++ b/arch/arm/boot/dts/sunxi-h3-h5.dtsi
> @@ -803,6 +803,15 @@ csi: camera@1cb0000 {
>  			pinctrl-names = "default";
>  			pinctrl-0 = <&csi_pins>;
>  			status = "disabled";
> +
> +			ports {
> +				#address-cells = <1>;
> +				#size-cells = <0>;
> +
> +				csi_in_parallel: port@0 {
> +					reg = <0>;
> +				};
> +			};
>  		};

This will create a DTC warning, since port@0 is the only node, and is
equivalent to port

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 12/19] dt-bindings: media: Add A31 MIPI CSI-2 bindings documentation
  2020-11-28 14:28 ` [PATCH v2 12/19] dt-bindings: media: Add A31 MIPI CSI-2 bindings documentation Paul Kocialkowski
@ 2020-12-01 12:15   ` Maxime Ripard
  0 siblings, 0 replies; 39+ messages in thread
From: Maxime Ripard @ 2020-12-01 12:15 UTC (permalink / raw)
  To: Paul Kocialkowski
  Cc: linux-media, devicetree, linux-arm-kernel, linux-kernel,
	linux-doc, devel, linux-sunxi, Yong Deng, Mauro Carvalho Chehab,
	Rob Herring, Chen-Yu Tsai, Jernej Skrabec, Jonathan Corbet,
	Kishon Vijay Abraham I, Vinod Koul, Helen Koike,
	Dafna Hirschfeld, Greg Kroah-Hartman, Philipp Zabel,
	Sakari Ailus, Hans Verkuil, Thomas Petazzoni, kevin.lhopital

[-- Attachment #1: Type: text/plain, Size: 3123 bytes --]

On Sat, Nov 28, 2020 at 03:28:32PM +0100, Paul Kocialkowski wrote:
> This introduces YAML bindings documentation for the A31 MIPI CSI-2
> controller.
> 
> Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
> ---
>  .../media/allwinner,sun6i-a31-mipi-csi2.yaml  | 151 ++++++++++++++++++
>  1 file changed, 151 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/allwinner,sun6i-a31-mipi-csi2.yaml
> 
> diff --git a/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-mipi-csi2.yaml b/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-mipi-csi2.yaml
> new file mode 100644
> index 000000000000..917cd09d6fda
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-mipi-csi2.yaml
> @@ -0,0 +1,151 @@
> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/media/allwinner,sun6i-a31-mipi-csi2.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Allwinner A31 MIPI CSI-2 Device Tree Bindings
> +
> +maintainers:
> +  - Paul Kocialkowski <paul.kocialkowski@bootlin.com>
> +
> +properties:
> +  compatible:
> +    oneOf:
> +      - const: allwinner,sun6i-a31-mipi-csi2
> +      - items:
> +          - const: allwinner,sun8i-v3s-mipi-csi2
> +          - const: allwinner,sun6i-a31-mipi-csi2
> +
> +  reg:
> +    maxItems: 1
> +
> +  interrupts:
> +    maxItems: 1
> +
> +  clocks:
> +    items:
> +      - description: Bus Clock
> +      - description: Module Clock
> +
> +  clock-names:
> +    items:
> +      - const: bus
> +      - const: mod
> +
> +  phys:
> +    items:
> +      - description: MIPI D-PHY
> +
> +  phy-names:
> +    items:
> +      - const: dphy

If there's a single item, you don't need the items: in phys and phy-names

> +  resets:
> +    maxItems: 1
> +
> +  # See ./video-interfaces.txt for details
> +  ports:
> +    type: object
> +
> +    properties:
> +      port@0:
> +        type: object
> +        description: Input port, connect to a MIPI CSI-2 sensor
> +
> +        properties:
> +          reg:
> +            const: 0
> +
> +          endpoint:
> +            type: object
> +
> +            properties:
> +              remote-endpoint: true
> +
> +              data-lanes:
> +                minItems: 1
> +                maxItems: 4
> +
> +            required:
> +              - data-lanes
> +              - remote-endpoint
> +
> +        required:
> +          - endpoint
> +
> +        additionalProperties: false
> +
> +      port@1:
> +        type: object
> +        description: Output port, connect to a CSI controller
> +
> +        properties:
> +          reg:
> +            const: 1
> +
> +          endpoint:
> +            type: object
> +
> +            properties:
> +              remote-endpoint: true
> +
> +        required:
> +          - endpoint
> +
> +        additionalProperties: false

Same thing than for the other dt binding patch, there's a schema for it now

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 13/19] media: sunxi: Add support for the A31 MIPI CSI-2 controller
  2020-11-28 14:28 ` [PATCH v2 13/19] media: sunxi: Add support for the A31 MIPI CSI-2 controller Paul Kocialkowski
@ 2020-12-01 12:20   ` Maxime Ripard
  2020-12-02 14:44     ` Paul Kocialkowski
  0 siblings, 1 reply; 39+ messages in thread
From: Maxime Ripard @ 2020-12-01 12:20 UTC (permalink / raw)
  To: Paul Kocialkowski
  Cc: linux-media, devicetree, linux-arm-kernel, linux-kernel,
	linux-doc, devel, linux-sunxi, Yong Deng, Mauro Carvalho Chehab,
	Rob Herring, Chen-Yu Tsai, Jernej Skrabec, Jonathan Corbet,
	Kishon Vijay Abraham I, Vinod Koul, Helen Koike,
	Dafna Hirschfeld, Greg Kroah-Hartman, Philipp Zabel,
	Sakari Ailus, Hans Verkuil, Thomas Petazzoni, kevin.lhopital

[-- Attachment #1: Type: text/plain, Size: 21664 bytes --]

Hi,

On Sat, Nov 28, 2020 at 03:28:33PM +0100, Paul Kocialkowski wrote:
> The A31 MIPI CSI-2 controller is a dedicated MIPI CSI-2 bridge
> found on Allwinner SoCs such as the A31 and V3/V3s.
> 
> It is a standalone block, connected to the CSI controller on one side
> and to the MIPI D-PHY block on the other. It has a dedicated address
> space, interrupt line and clock.
> 
> It is represented as a V4L2 subdev to the CSI controller and takes a
> MIPI CSI-2 sensor as its own subdev, all using the fwnode graph and
> media controller API.
> 
> Only 8-bit and 10-bit Bayer formats are currently supported.
> While up to 4 internal channels to the CSI controller exist, only one
> is currently supported by this implementation.
> 
> Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
> ---
>  drivers/media/platform/sunxi/Kconfig          |   1 +
>  drivers/media/platform/sunxi/Makefile         |   1 +
>  .../platform/sunxi/sun6i-mipi-csi2/Kconfig    |  12 +
>  .../platform/sunxi/sun6i-mipi-csi2/Makefile   |   4 +
>  .../sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c   | 591 ++++++++++++++++++
>  .../sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.h   | 117 ++++
>  6 files changed, 726 insertions(+)
>  create mode 100644 drivers/media/platform/sunxi/sun6i-mipi-csi2/Kconfig
>  create mode 100644 drivers/media/platform/sunxi/sun6i-mipi-csi2/Makefile
>  create mode 100644 drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c
>  create mode 100644 drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.h
> 
> diff --git a/drivers/media/platform/sunxi/Kconfig b/drivers/media/platform/sunxi/Kconfig
> index 7151cc249afa..9684e07454ad 100644
> --- a/drivers/media/platform/sunxi/Kconfig
> +++ b/drivers/media/platform/sunxi/Kconfig
> @@ -2,3 +2,4 @@
>  
>  source "drivers/media/platform/sunxi/sun4i-csi/Kconfig"
>  source "drivers/media/platform/sunxi/sun6i-csi/Kconfig"
> +source "drivers/media/platform/sunxi/sun6i-mipi-csi2/Kconfig"
> diff --git a/drivers/media/platform/sunxi/Makefile b/drivers/media/platform/sunxi/Makefile
> index fc537c9f5ca9..887a7cae8fca 100644
> --- a/drivers/media/platform/sunxi/Makefile
> +++ b/drivers/media/platform/sunxi/Makefile
> @@ -2,5 +2,6 @@
>  
>  obj-y		+= sun4i-csi/
>  obj-y		+= sun6i-csi/
> +obj-y		+= sun6i-mipi-csi2/

I'm not sure we need a new folder here, it's only ever tied to sun6i-csi
so it would make more sense to have it in the same folder.

>  obj-y		+= sun8i-di/
>  obj-y		+= sun8i-rotate/
> diff --git a/drivers/media/platform/sunxi/sun6i-mipi-csi2/Kconfig b/drivers/media/platform/sunxi/sun6i-mipi-csi2/Kconfig
> new file mode 100644
> index 000000000000..3260591ed5c0
> --- /dev/null
> +++ b/drivers/media/platform/sunxi/sun6i-mipi-csi2/Kconfig
> @@ -0,0 +1,12 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +config VIDEO_SUN6I_MIPI_CSI2
> +	tristate "Allwinner A31 MIPI CSI-2 Controller Driver"
> +	depends on VIDEO_V4L2 && COMMON_CLK
> +	depends on ARCH_SUNXI || COMPILE_TEST
> +	select PHY_SUN6I_MIPI_DPHY
> +	select MEDIA_CONTROLLER
> +	select VIDEO_V4L2_SUBDEV_API
> +	select REGMAP_MMIO
> +	select V4L2_FWNODE
> +	help
> +	   Support for the Allwinner A31 MIPI CSI-2 Controller.
> diff --git a/drivers/media/platform/sunxi/sun6i-mipi-csi2/Makefile b/drivers/media/platform/sunxi/sun6i-mipi-csi2/Makefile
> new file mode 100644
> index 000000000000..14e4e03818b5
> --- /dev/null
> +++ b/drivers/media/platform/sunxi/sun6i-mipi-csi2/Makefile
> @@ -0,0 +1,4 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +sun6i-mipi-csi2-y += sun6i_mipi_csi2.o
> +
> +obj-$(CONFIG_VIDEO_SUN6I_MIPI_CSI2) += sun6i-mipi-csi2.o
> diff --git a/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c b/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c
> new file mode 100644
> index 000000000000..a6567ef82fb4
> --- /dev/null
> +++ b/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c
> @@ -0,0 +1,591 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright 2020 Bootlin
> + * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/reset.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-fwnode.h>
> +
> +#include "sun6i_mipi_csi2.h"
> +
> +#define MODULE_NAME	"sun6i-mipi-csi2"
> +
> +static const u32 sun6i_mipi_csi2_mbus_codes[] = {
> +	MEDIA_BUS_FMT_SBGGR8_1X8,
> +	MEDIA_BUS_FMT_SGBRG8_1X8,
> +	MEDIA_BUS_FMT_SGRBG8_1X8,
> +	MEDIA_BUS_FMT_SRGGB8_1X8,
> +	MEDIA_BUS_FMT_SBGGR10_1X10,
> +	MEDIA_BUS_FMT_SGBRG10_1X10,
> +	MEDIA_BUS_FMT_SGRBG10_1X10,
> +	MEDIA_BUS_FMT_SRGGB10_1X10,
> +};
> +
> +/* Video */
> +
> +static int sun6i_mipi_csi2_s_stream(struct v4l2_subdev *subdev, int on)
> +{
> +	struct sun6i_mipi_csi2_video *video =
> +		sun6i_mipi_csi2_subdev_video(subdev);
> +	struct sun6i_mipi_csi2_dev *cdev = sun6i_mipi_csi2_video_dev(video);
> +	struct v4l2_subdev *remote_subdev = video->remote_subdev;
> +	struct v4l2_fwnode_bus_mipi_csi2 *bus_mipi_csi2 =
> +		&video->endpoint.bus.mipi_csi2;
> +	union phy_configure_opts dphy_opts = { 0 };
> +	struct phy_configure_opts_mipi_dphy *dphy_cfg = &dphy_opts.mipi_dphy;
> +	struct regmap *regmap = cdev->regmap;
> +	struct v4l2_ctrl *ctrl;
> +	unsigned int lanes_count;
> +	unsigned int bpp;
> +	unsigned long pixel_rate;
> +	u8 data_type = 0;
> +	u32 version = 0;
> +	/* Initialize to 0 to use both in disable label (ret != 0) and off. */
> +	int ret = 0;
> +
> +	if (!remote_subdev)
> +		return -ENODEV;
> +
> +	if (!on) {
> +		v4l2_subdev_call(remote_subdev, video, s_stream, 0);
> +		goto disable;
> +	}
> +
> +	switch (video->mbus_format.code) {
> +	case MEDIA_BUS_FMT_SBGGR8_1X8:
> +	case MEDIA_BUS_FMT_SGBRG8_1X8:
> +	case MEDIA_BUS_FMT_SGRBG8_1X8:
> +	case MEDIA_BUS_FMT_SRGGB8_1X8:
> +		data_type = MIPI_CSI2_DATA_TYPE_RAW8;
> +		bpp = 8;
> +		break;
> +	case MEDIA_BUS_FMT_SBGGR10_1X10:
> +	case MEDIA_BUS_FMT_SGBRG10_1X10:
> +	case MEDIA_BUS_FMT_SGRBG10_1X10:
> +	case MEDIA_BUS_FMT_SRGGB10_1X10:
> +		data_type = MIPI_CSI2_DATA_TYPE_RAW10;
> +		bpp = 10;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	/* Sensor pixel rate */
> +
> +	ctrl = v4l2_ctrl_find(remote_subdev->ctrl_handler, V4L2_CID_PIXEL_RATE);
> +	if (!ctrl) {
> +		dev_err(cdev->dev,
> +			"%s: no MIPI CSI-2 pixel rate from the sensor\n",
> +			__func__);
> +		return -ENODEV;
> +	}
> +
> +	pixel_rate = (unsigned long)v4l2_ctrl_g_ctrl_int64(ctrl);
> +	if (!pixel_rate) {
> +		dev_err(cdev->dev,
> +			"%s: zero MIPI CSI-2 pixel rate from the sensor\n",
> +			__func__);
> +		return -ENODEV;
> +	}
> +
> +	/* Power management */
> +
> +	ret = pm_runtime_get_sync(cdev->dev);
> +	if (ret < 0) {
> +		pm_runtime_put_noidle(cdev->dev);
> +		return ret;
> +	}
> +
> +	/* D-PHY configuration */
> +
> +	lanes_count = bus_mipi_csi2->num_data_lanes;
> +	phy_mipi_dphy_get_default_config(pixel_rate, bpp, lanes_count,
> +					 dphy_cfg);
> +
> +	/*
> +	 * Note that our hardware is using DDR, which is not taken in account by
> +	 * phy_mipi_dphy_get_default_config when calculating hs_clk_rate from
> +	 * the pixel rate, lanes count and bpp.
> +	 *
> +	 * The resulting clock rate is basically the symbol rate over the whole
> +	 * link. The actual clock rate is calculated with division by two since
> +	 * DDR samples both on rising and falling edges.
> +	 */
> +
> +	dev_dbg(cdev->dev, "A31 MIPI CSI-2 config:\n");
> +	dev_dbg(cdev->dev, "%ld pixels/s, %u bits/pixel, %lu Hz clock\n",
> +		pixel_rate, bpp, dphy_cfg->hs_clk_rate / 2);
> +
> +	ret = phy_reset(cdev->dphy);
> +	if (ret) {
> +		dev_err(cdev->dev, "failed to reset MIPI D-PHY\n");
> +		goto error_pm;
> +	}
> +
> +	ret = phy_set_mode_ext(cdev->dphy, PHY_MODE_MIPI_DPHY,
> +			       PHY_MIPI_DPHY_SUBMODE_RX);
> +	if (ret) {
> +		dev_err(cdev->dev, "failed to set MIPI D-PHY mode\n");
> +		goto error_pm;
> +	}
> +
> +	ret = phy_configure(cdev->dphy, &dphy_opts);
> +	if (ret) {
> +		dev_err(cdev->dev, "failed to configure MIPI D-PHY\n");
> +		goto error_pm;
> +	}
> +
> +	ret = phy_power_on(cdev->dphy);
> +	if (ret) {
> +		dev_err(cdev->dev, "failed to power on MIPI D-PHY\n");
> +		goto error_pm;
> +	}
> +
> +	/* MIPI CSI-2 controller setup */
> +
> +	/*
> +	 * The enable flow in the Allwinner BSP is a bit different: the enable
> +	 * and reset bits are set together before starting the CSI controller.
> +	 *
> +	 * In mainline we enable the CSI controller first (due to subdev logic).
> +	 * One reliable way to make this work is to deassert reset, configure
> +	 * registers and enable the controller when everything's ready.
> +	 *
> +	 * However, setting the version enable bit and removing it afterwards
> +	 * appears necessary for capture to work reliably, while replacing it
> +	 * with a delay doesn't do the trick.
> +	 */
> +	regmap_write(regmap, SUN6I_MIPI_CSI2_CTL_REG,
> +		     SUN6I_MIPI_CSI2_CTL_RESET_N |
> +		     SUN6I_MIPI_CSI2_CTL_VERSION_EN |
> +		     SUN6I_MIPI_CSI2_CTL_UNPK_EN);
> +
> +	regmap_read(regmap, SUN6I_MIPI_CSI2_VERSION_REG, &version);
> +
> +	regmap_update_bits(regmap, SUN6I_MIPI_CSI2_CTL_REG,
> +				   SUN6I_MIPI_CSI2_CTL_VERSION_EN, 0);
> +
> +	dev_dbg(cdev->dev, "A31 MIPI CSI-2 version: %04x\n", version);
> +
> +	regmap_write(regmap, SUN6I_MIPI_CSI2_CFG_REG,
> +		     SUN6I_MIPI_CSI2_CFG_CHANNEL_MODE(1) |
> +		     SUN6I_MIPI_CSI2_CFG_LANE_COUNT(lanes_count));
> +
> +	/*
> +	 * Our MIPI CSI-2 controller has internal channels that can be
> +	 * configured to match a specific MIPI CSI-2 virtual channel and/or
> +	 * a specific data type. Each internal channel can be piped to an
> +	 * internal channel of the CSI controller.
> +	 *
> +	 * We set virtual channel numbers to all channels to make sure that
> +	 * virtual channel 0 goes to CSI channel 0 only.
> +	 */
> +	regmap_write(regmap, SUN6I_MIPI_CSI2_VCDT_RX_REG,
> +		     SUN6I_MIPI_CSI2_VCDT_RX_CH_VC(3, 3) |
> +		     SUN6I_MIPI_CSI2_VCDT_RX_CH_VC(2, 2) |
> +		     SUN6I_MIPI_CSI2_VCDT_RX_CH_VC(1, 1) |
> +		     SUN6I_MIPI_CSI2_VCDT_RX_CH_VC(0, 0) |
> +		     SUN6I_MIPI_CSI2_VCDT_RX_CH_DT(0, data_type));
> +
> +	regmap_update_bits(regmap, SUN6I_MIPI_CSI2_CTL_REG,
> +			   SUN6I_MIPI_CSI2_CTL_EN, SUN6I_MIPI_CSI2_CTL_EN);
> +
> +	ret = v4l2_subdev_call(remote_subdev, video, s_stream, 1);
> +	if (ret)
> +		goto disable;
> +
> +	return 0;
> +
> +disable:
> +	regmap_update_bits(regmap, SUN6I_MIPI_CSI2_CTL_REG,
> +			   SUN6I_MIPI_CSI2_CTL_EN, 0);
> +
> +	phy_power_off(cdev->dphy);
> +
> +error_pm:
> +	pm_runtime_put(cdev->dev);
> +
> +	return ret;
> +}
> +
> +static const struct v4l2_subdev_video_ops sun6i_mipi_csi2_subdev_video_ops = {
> +	.s_stream	= sun6i_mipi_csi2_s_stream,
> +};
> +
> +/* Pad */
> +
> +static int
> +sun6i_mipi_csi2_enum_mbus_code(struct v4l2_subdev *subdev,
> +			       struct v4l2_subdev_pad_config *config,
> +			       struct v4l2_subdev_mbus_code_enum *code_enum)
> +{
> +	if (code_enum->index >= ARRAY_SIZE(sun6i_mipi_csi2_mbus_codes))
> +		return -EINVAL;
> +
> +	code_enum->code = sun6i_mipi_csi2_mbus_codes[code_enum->index];
> +
> +	return 0;
> +}
> +
> +static int sun6i_mipi_csi2_get_fmt(struct v4l2_subdev *subdev,
> +				   struct v4l2_subdev_pad_config *config,
> +				   struct v4l2_subdev_format *format)
> +{
> +	struct sun6i_mipi_csi2_video *video =
> +		sun6i_mipi_csi2_subdev_video(subdev);
> +	struct v4l2_mbus_framefmt *mbus_format = &format->format;
> +
> +	if (format->which == V4L2_SUBDEV_FORMAT_TRY)
> +		*mbus_format = *v4l2_subdev_get_try_format(subdev, config,
> +							   format->pad);
> +	else
> +		*mbus_format = video->mbus_format;
> +
> +	return 0;
> +}
> +
> +static int sun6i_mipi_csi2_set_fmt(struct v4l2_subdev *subdev,
> +				   struct v4l2_subdev_pad_config *config,
> +				   struct v4l2_subdev_format *format)
> +{
> +	struct sun6i_mipi_csi2_video *video =
> +		sun6i_mipi_csi2_subdev_video(subdev);
> +	struct v4l2_mbus_framefmt *mbus_format = &format->format;
> +
> +	if (format->which == V4L2_SUBDEV_FORMAT_TRY)
> +		*v4l2_subdev_get_try_format(subdev, config, format->pad) =
> +			*mbus_format;
> +	else
> +		video->mbus_format = *mbus_format;
> +
> +	return 0;
> +}
> +
> +static const struct v4l2_subdev_pad_ops sun6i_mipi_csi2_subdev_pad_ops = {
> +	.enum_mbus_code	= sun6i_mipi_csi2_enum_mbus_code,
> +	.get_fmt	= sun6i_mipi_csi2_get_fmt,
> +	.set_fmt	= sun6i_mipi_csi2_set_fmt,
> +};
> +
> +/* Subdev */
> +
> +static const struct v4l2_subdev_ops sun6i_mipi_csi2_subdev_ops = {
> +	.video		= &sun6i_mipi_csi2_subdev_video_ops,
> +	.pad		= &sun6i_mipi_csi2_subdev_pad_ops,
> +};
> +
> +/* Notifier */
> +
> +static int
> +sun6i_mipi_csi2_notifier_bound(struct v4l2_async_notifier *notifier,
> +			       struct v4l2_subdev *remote_subdev,
> +			       struct v4l2_async_subdev *remote_subdev_async)
> +{
> +	struct v4l2_subdev *subdev = notifier->sd;
> +	struct sun6i_mipi_csi2_video *video =
> +		sun6i_mipi_csi2_subdev_video(subdev);
> +	struct sun6i_mipi_csi2_dev *cdev = sun6i_mipi_csi2_video_dev(video);
> +	int source_pad;
> +	int ret;
> +
> +	source_pad = media_entity_get_fwnode_pad(&remote_subdev->entity,
> +						 remote_subdev->fwnode,
> +						 MEDIA_PAD_FL_SOURCE);
> +	if (source_pad < 0)
> +		return source_pad;
> +
> +	ret = media_create_pad_link(&remote_subdev->entity, source_pad,
> +				    &subdev->entity, 0,
> +				    MEDIA_LNK_FL_ENABLED |
> +				    MEDIA_LNK_FL_IMMUTABLE);
> +	if (ret) {
> +		dev_err(cdev->dev, "failed to create %s:%u -> %s:%u link\n",
> +			remote_subdev->entity.name, source_pad,
> +			subdev->entity.name, 0);
> +		return ret;
> +	}
> +
> +	video->remote_subdev = remote_subdev;
> +
> +	return 0;
> +}
> +
> +static const
> +struct v4l2_async_notifier_operations sun6i_mipi_csi2_notifier_ops = {
> +	.bound		= sun6i_mipi_csi2_notifier_bound,
> +};
> +
> +/* Media Entity */
> +
> +static const struct media_entity_operations sun6i_mipi_csi2_entity_ops = {
> +	.link_validate	= v4l2_subdev_link_validate,
> +};
> +
> +/* Base Driver */
> +
> +static int __maybe_unused sun6i_mipi_csi2_suspend(struct device *dev)
> +{
> +	struct sun6i_mipi_csi2_dev *cdev = dev_get_drvdata(dev);
> +
> +	clk_disable_unprepare(cdev->clk_mod);
> +	clk_disable_unprepare(cdev->clk_bus);
> +	reset_control_assert(cdev->reset);
> +
> +	return 0;
> +}
> +
> +static int __maybe_unused sun6i_mipi_csi2_resume(struct device *dev)
> +{
> +	struct sun6i_mipi_csi2_dev *cdev = dev_get_drvdata(dev);
> +	int ret;
> +
> +	ret = reset_control_deassert(cdev->reset);
> +	if (ret) {
> +		dev_err(cdev->dev, "failed to deassert reset\n");
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(cdev->clk_bus);
> +	if (ret) {
> +		dev_err(cdev->dev, "failed to enable bus clock\n");
> +		goto error_reset;
> +	}
> +
> +	ret = clk_prepare_enable(cdev->clk_mod);
> +	if (ret) {
> +		dev_err(cdev->dev, "failed to enable module clock\n");
> +		goto error_clk_bus;
> +	}
> +
> +	return 0;
> +
> +error_clk_bus:
> +	clk_disable_unprepare(cdev->clk_bus);
> +
> +error_reset:
> +	reset_control_assert(cdev->reset);
> +
> +	return ret;
> +}

I'm guessing you set the __maybe_unused attribute because you're using
SET_RUNTIME_PM_OPS, but what would happen if runtime_pm isn't selected?
It looks like you don't handle that case.

> +static int sun6i_mipi_csi2_v4l2_setup(struct sun6i_mipi_csi2_dev *cdev)
> +{
> +	struct sun6i_mipi_csi2_video *video = &cdev->video;
> +	struct v4l2_subdev *subdev = &video->subdev;
> +	struct v4l2_async_notifier *notifier = &video->notifier;
> +	struct fwnode_handle *handle;
> +	struct v4l2_fwnode_endpoint *endpoint;
> +	struct v4l2_async_subdev *subdev_async;
> +	int ret;
> +
> +	/* Subdev */
> +
> +	v4l2_subdev_init(subdev, &sun6i_mipi_csi2_subdev_ops);
> +	subdev->dev = cdev->dev;
> +	subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> +	strscpy(subdev->name, MODULE_NAME, sizeof(subdev->name));
> +	v4l2_set_subdevdata(subdev, cdev);
> +
> +	/* Entity */
> +
> +	subdev->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
> +	subdev->entity.ops = &sun6i_mipi_csi2_entity_ops;
> +
> +	/* Pads */
> +
> +	video->pads[0].flags = MEDIA_PAD_FL_SINK;
> +	video->pads[1].flags = MEDIA_PAD_FL_SOURCE;
> +
> +	ret = media_entity_pads_init(&subdev->entity, 2, video->pads);
> +	if (ret)
> +		return ret;
> +
> +	/* Endpoint */
> +
> +	handle = fwnode_graph_get_endpoint_by_id(dev_fwnode(cdev->dev), 0, 0,
> +						 FWNODE_GRAPH_ENDPOINT_NEXT);
> +	if (!handle) {
> +		ret = -ENODEV;
> +		goto error_media_entity;
> +	}
> +
> +	endpoint = &video->endpoint;
> +	endpoint->bus_type = V4L2_MBUS_CSI2_DPHY;
> +
> +	ret = v4l2_fwnode_endpoint_parse(handle, endpoint);
> +	fwnode_handle_put(handle);
> +	if (ret)
> +		goto error_media_entity;
> +
> +	/* Notifier */
> +
> +	v4l2_async_notifier_init(notifier);
> +
> +	subdev_async = &video->subdev_async;
> +	ret = v4l2_async_notifier_add_fwnode_remote_subdev(notifier, handle,
> +							   subdev_async);
> +	if (ret)
> +		goto error_media_entity;
> +
> +	video->notifier.ops = &sun6i_mipi_csi2_notifier_ops;
> +
> +	ret = v4l2_async_subdev_notifier_register(subdev, notifier);
> +	if (ret < 0)
> +		goto error_notifier;
> +
> +	/* Subdev */
> +
> +	ret = v4l2_async_register_subdev(subdev);
> +	if (ret < 0)
> +		goto error_notifier_registered;
> +
> +	/* Runtime PM */
> +
> +	pm_runtime_enable(cdev->dev);
> +	pm_runtime_set_suspended(cdev->dev);
> +
> +	return 0;
> +
> +error_notifier_registered:
> +	v4l2_async_notifier_unregister(notifier);
> +error_notifier:
> +	v4l2_async_notifier_cleanup(notifier);
> +error_media_entity:
> +	media_entity_cleanup(&subdev->entity);
> +
> +	return ret;
> +}
> +
> +static int sun6i_mipi_csi2_v4l2_teardown(struct sun6i_mipi_csi2_dev *cdev)
> +{
> +	struct sun6i_mipi_csi2_video *video = &cdev->video;
> +	struct v4l2_subdev *subdev = &video->subdev;
> +	struct v4l2_async_notifier *notifier = &video->notifier;
> +
> +	v4l2_async_unregister_subdev(subdev);
> +	v4l2_async_notifier_unregister(notifier);
> +	v4l2_async_notifier_cleanup(notifier);
> +	media_entity_cleanup(&subdev->entity);
> +	v4l2_device_unregister_subdev(subdev);
> +
> +	return 0;
> +}
> +
> +static const struct regmap_config sun6i_mipi_csi2_regmap_config = {
> +	.reg_bits       = 32,
> +	.reg_stride     = 4,
> +	.val_bits       = 32,
> +	.max_register	= 0x400,
> +};
> +
> +static int sun6i_mipi_csi2_probe(struct platform_device *pdev)
> +{
> +	struct sun6i_mipi_csi2_dev *cdev;
> +	struct resource *res;
> +	void __iomem *io_base;
> +	int ret;
> +
> +	cdev = devm_kzalloc(&pdev->dev, sizeof(*cdev), GFP_KERNEL);
> +	if (!cdev)
> +		return -ENOMEM;
> +
> +	cdev->dev = &pdev->dev;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	io_base = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(io_base))
> +		return PTR_ERR(io_base);
> +
> +	cdev->regmap = devm_regmap_init_mmio(&pdev->dev, io_base,
> +					     &sun6i_mipi_csi2_regmap_config);
> +	if (IS_ERR(cdev->regmap)) {
> +		dev_err(&pdev->dev, "failed to init register map\n");
> +		return PTR_ERR(cdev->regmap);
> +	}
> +
> +	cdev->clk_bus = devm_clk_get(&pdev->dev, "bus");
> +	if (IS_ERR(cdev->clk_bus)) {
> +		dev_err(&pdev->dev, "failed to acquire bus clock\n");
> +		return PTR_ERR(cdev->clk_bus);
> +	}
> +
> +	cdev->clk_mod = devm_clk_get(&pdev->dev, "mod");
> +	if (IS_ERR(cdev->clk_mod)) {
> +		dev_err(&pdev->dev, "failed to acquire mod clock\n");
> +		return PTR_ERR(cdev->clk_mod);
> +	}
> +
> +	cdev->reset = devm_reset_control_get_shared(&pdev->dev, NULL);
> +	if (IS_ERR(cdev->reset)) {
> +		dev_err(&pdev->dev, "failed to get reset controller\n");
> +		return PTR_ERR(cdev->reset);
> +	}
> +
> +	cdev->dphy = devm_phy_get(&pdev->dev, "dphy");
> +	if (IS_ERR(cdev->dphy)) {
> +		dev_err(&pdev->dev, "failed to get the MIPI D-PHY\n");
> +		return PTR_ERR(cdev->dphy);
> +	}
> +
> +	ret = phy_init(cdev->dphy);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to initialize the MIPI D-PHY\n");
> +		return ret;
> +	}
> +
> +	platform_set_drvdata(pdev, cdev);
> +
> +	ret = sun6i_mipi_csi2_v4l2_setup(cdev);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int sun6i_mipi_csi2_remove(struct platform_device *pdev)
> +{
> +	struct sun6i_mipi_csi2_dev *cdev = platform_get_drvdata(pdev);
> +
> +	phy_exit(cdev->dphy);
> +
> +	return sun6i_mipi_csi2_v4l2_teardown(cdev);

This seem to be the only user and its content is pretty straightforward,
so we shouldn't have a separate function here

> +}
> +
> +static const struct dev_pm_ops sun6i_mipi_csi2_pm_ops = {
> +	SET_RUNTIME_PM_OPS(sun6i_mipi_csi2_suspend, sun6i_mipi_csi2_resume,
> +			   NULL)
> +};
> +
> +static const struct of_device_id sun6i_mipi_csi2_of_match[] = {
> +	{ .compatible = "allwinner,sun6i-a31-mipi-csi2" },
> +	{ .compatible = "allwinner,sun8i-v3s-mipi-csi2", },

There's no need for the v3s compatible here, it will fallback to the a31
one anyway.

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 06/19] dt-bindings: media: sun6i-a31-csi: Add MIPI CSI-2 input port
  2020-12-01 10:43   ` Maxime Ripard
@ 2020-12-02 14:16     ` Paul Kocialkowski
  2020-12-08 19:54       ` Rob Herring
  0 siblings, 1 reply; 39+ messages in thread
From: Paul Kocialkowski @ 2020-12-02 14:16 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: linux-media, devicetree, linux-arm-kernel, linux-kernel,
	linux-doc, devel, linux-sunxi, Yong Deng, Mauro Carvalho Chehab,
	Rob Herring, Chen-Yu Tsai, Jernej Skrabec, Jonathan Corbet,
	Kishon Vijay Abraham I, Vinod Koul, Helen Koike,
	Dafna Hirschfeld, Greg Kroah-Hartman, Philipp Zabel,
	Sakari Ailus, Hans Verkuil, Thomas Petazzoni, kevin.lhopital

[-- Attachment #1: Type: text/plain, Size: 3938 bytes --]

Hi,

On Tue 01 Dec 20, 11:43, Maxime Ripard wrote:
> On Sat, Nov 28, 2020 at 03:28:26PM +0100, Paul Kocialkowski wrote:
> > The A31 CSI controller supports two distinct input interfaces:
> > parallel and an external MIPI CSI-2 bridge. The parallel interface
> > is often connected to a set of hardware pins while the MIPI CSI-2
> > bridge is an internal FIFO-ish link. As a result, these two inputs
> > are distinguished as two different ports.
> > 
> > Note that only one of the two may be present on a controller instance.
> > For example, the V3s has one controller dedicated to MIPI-CSI2 and one
> > dedicated to parallel.
> > 
> > Update the binding with an explicit ports node that holds two distinct
> > port nodes: one for parallel input and one for MIPI CSI-2.
> > 
> > This is backward-compatible with the single-port approach that was
> > previously taken for representing the parallel interface port, which
> > stays enumerated as fwnode port 0. However, it is now marked as
> > deprecated and the multi-port approach should be preferred.
> > 
> > Note that additional ports may be added in the future, especially to
> > support feeding the CSI controller's output to the ISP.
> > 
> > Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
> > ---
> >  .../media/allwinner,sun6i-a31-csi.yaml        | 86 ++++++++++++++++---
> >  1 file changed, 73 insertions(+), 13 deletions(-)
> > 
> > diff --git a/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-csi.yaml b/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-csi.yaml
> > index 1fd9b5532a21..3bcee2d44f3c 100644
> > --- a/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-csi.yaml
> > +++ b/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-csi.yaml
> > @@ -43,6 +43,7 @@ properties:
> >    # See ./video-interfaces.txt for details
> >    port:
> >      type: object
> > +    deprecated: true
> 
> I'm not sure we want to deprecate it. There's some SoCs with the same
> controller but without the MIPI-CSI block where this would be completely
> valid

I think we'll need to deprecate it when adding support for the ISP anyway,
so I figured we might as well do it now. But I'm okay to postpone this for now.

> >      properties:
> >        endpoint:
> > @@ -67,6 +68,59 @@ properties:
> >  
> >      additionalProperties: false
> >  
> > +  ports:
> > +    type: object
> > +
> > +    properties:
> > +      port@0:
> > +        type: object
> > +        description: Parallel input port, connect to a parallel sensor
> > +
> > +        properties:
> > +          reg:
> > +            const: 0
> > +
> > +          endpoint:
> > +            type: object
> > +
> > +            properties:
> > +              remote-endpoint: true
> > +
> > +              bus-width:
> > +                enum: [ 8, 10, 12, 16 ]
> > +
> > +              pclk-sample: true
> > +              hsync-active: true
> > +              vsync-active: true
> > +
> > +            required:
> > +              - bus-width
> > +              - remote-endpoint
> > +
> > +        required:
> > +          - endpoint
> > +
> > +        additionalProperties: false
> > +
> > +      port@1:
> > +        type: object
> > +        description: MIPI CSI-2 bridge input port
> > +
> > +        properties:
> > +          reg:
> > +            const: 1
> > +
> > +          endpoint:
> > +            type: object
> > +
> > +            properties:
> > +              remote-endpoint: true
> > +
> > +            required:
> > +              - remote-endpoint
> > +
> > +        additionalProperties: false
> 
> There's a schema for the OF-graph now, you'll want to use it.

I didn't know that, thanks for the tip! Will look into it.

Cheers,

Paul

-- 
Paul Kocialkowski, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 07/19] media: sun6i-csi: Add support for MIPI CSI-2 bridge input
  2020-12-01 12:12   ` Maxime Ripard
@ 2020-12-02 14:19     ` Paul Kocialkowski
  2020-12-02 15:40       ` Maxime Ripard
  0 siblings, 1 reply; 39+ messages in thread
From: Paul Kocialkowski @ 2020-12-02 14:19 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: linux-media, devicetree, linux-arm-kernel, linux-kernel,
	linux-doc, devel, linux-sunxi, Yong Deng, Mauro Carvalho Chehab,
	Rob Herring, Chen-Yu Tsai, Jernej Skrabec, Jonathan Corbet,
	Kishon Vijay Abraham I, Vinod Koul, Helen Koike,
	Dafna Hirschfeld, Greg Kroah-Hartman, Philipp Zabel,
	Sakari Ailus, Hans Verkuil, Thomas Petazzoni, kevin.lhopital

[-- Attachment #1: Type: text/plain, Size: 2554 bytes --]

Hi,

On Tue 01 Dec 20, 13:12, Maxime Ripard wrote:
> Hi,
> 
> On Sat, Nov 28, 2020 at 03:28:27PM +0100, Paul Kocialkowski wrote:
> > The A31 CSI controller supports a MIPI CSI-2 bridge input, which has
> > its own dedicated port in the fwnode graph.
> > 
> > Support for this input is added with this change:
> > - two pads are defined for the media entity instead of one
> >   and only one needs to be connected at a time;
> > - the pads currently match the fwnode graph representation;
> > - links are created between our pads and the subdevs for each
> >   interface and are no longer immutable so that userspace can select
> >   which interface to use in case both are bound to a subdev;
> > - fwnode endpoints are parsed and stored for each interface;
> > - the active subdev (and fwnode endpoint) is retrieved when validating
> >   the media link at stream on time and cleared at stream off;
> > - an error is raised if both links are active at the same time;
> > - the MIPI interface bit is set if the MIPI CSI-2 bridge endpoint is
> >   active.
> > 
> > In the future, the media entity representation might evolve to:
> > - distinguish the internal parallel bridge and data formatter;
> > - represent each of the 4 internal channels that can exist between
> >   the parallel bridge (for BT656 time-multiplex) and MIPI CSI-2
> >   (internal channels can be mapped to virtual channels);
> > - connect the controller's output to the ISP instead of its
> >   DMA engine.
> > 
> > Finally note that the MIPI CSI-2 bridges should not be linked in
> > the fwnode graph unless they have a sensor subdev attached.
> 
> I'll leave most of the review to Laurent and Sakari, but I'm not quite
> sure what you meant in the last paragraph. Did you mean that the
> MIPI-CSI controller in the Allwinner SoC should only be linked if it has
> a sensor attached, or did you mean that any MIPI-CSI2 bridge cannot be
> attached to the controller?

So the use of plural was a mistake and your first understanding is the correct
one: if the bridge is linked to the CSI controller in the OF graph but the
bridge doesn't have a sensor attached, the CSI controller driver will fail
to probe, as far as I could see.

I haven't investigated much but it looks like this is expected behavior.

> Also, having somewhere (like your cover letter) the media-ctl setup you
> tested with would be great.

Understood!

Cheers,

Paul

-- 
Paul Kocialkowski, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 13/19] media: sunxi: Add support for the A31 MIPI CSI-2 controller
  2020-12-01 12:20   ` Maxime Ripard
@ 2020-12-02 14:44     ` Paul Kocialkowski
  2020-12-02 15:48       ` Maxime Ripard
  0 siblings, 1 reply; 39+ messages in thread
From: Paul Kocialkowski @ 2020-12-02 14:44 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: linux-media, devicetree, linux-arm-kernel, linux-kernel,
	linux-doc, devel, linux-sunxi, Yong Deng, Mauro Carvalho Chehab,
	Rob Herring, Chen-Yu Tsai, Jernej Skrabec, Jonathan Corbet,
	Kishon Vijay Abraham I, Vinod Koul, Helen Koike,
	Dafna Hirschfeld, Greg Kroah-Hartman, Philipp Zabel,
	Sakari Ailus, Hans Verkuil, Thomas Petazzoni, kevin.lhopital

[-- Attachment #1: Type: text/plain, Size: 24060 bytes --]

Hi,

On Tue 01 Dec 20, 13:20, Maxime Ripard wrote:
> Hi,
> 
> On Sat, Nov 28, 2020 at 03:28:33PM +0100, Paul Kocialkowski wrote:
> > The A31 MIPI CSI-2 controller is a dedicated MIPI CSI-2 bridge
> > found on Allwinner SoCs such as the A31 and V3/V3s.
> > 
> > It is a standalone block, connected to the CSI controller on one side
> > and to the MIPI D-PHY block on the other. It has a dedicated address
> > space, interrupt line and clock.
> > 
> > It is represented as a V4L2 subdev to the CSI controller and takes a
> > MIPI CSI-2 sensor as its own subdev, all using the fwnode graph and
> > media controller API.
> > 
> > Only 8-bit and 10-bit Bayer formats are currently supported.
> > While up to 4 internal channels to the CSI controller exist, only one
> > is currently supported by this implementation.
> > 
> > Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
> > ---
> >  drivers/media/platform/sunxi/Kconfig          |   1 +
> >  drivers/media/platform/sunxi/Makefile         |   1 +
> >  .../platform/sunxi/sun6i-mipi-csi2/Kconfig    |  12 +
> >  .../platform/sunxi/sun6i-mipi-csi2/Makefile   |   4 +
> >  .../sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c   | 591 ++++++++++++++++++
> >  .../sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.h   | 117 ++++
> >  6 files changed, 726 insertions(+)
> >  create mode 100644 drivers/media/platform/sunxi/sun6i-mipi-csi2/Kconfig
> >  create mode 100644 drivers/media/platform/sunxi/sun6i-mipi-csi2/Makefile
> >  create mode 100644 drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c
> >  create mode 100644 drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.h
> > 
> > diff --git a/drivers/media/platform/sunxi/Kconfig b/drivers/media/platform/sunxi/Kconfig
> > index 7151cc249afa..9684e07454ad 100644
> > --- a/drivers/media/platform/sunxi/Kconfig
> > +++ b/drivers/media/platform/sunxi/Kconfig
> > @@ -2,3 +2,4 @@
> >  
> >  source "drivers/media/platform/sunxi/sun4i-csi/Kconfig"
> >  source "drivers/media/platform/sunxi/sun6i-csi/Kconfig"
> > +source "drivers/media/platform/sunxi/sun6i-mipi-csi2/Kconfig"
> > diff --git a/drivers/media/platform/sunxi/Makefile b/drivers/media/platform/sunxi/Makefile
> > index fc537c9f5ca9..887a7cae8fca 100644
> > --- a/drivers/media/platform/sunxi/Makefile
> > +++ b/drivers/media/platform/sunxi/Makefile
> > @@ -2,5 +2,6 @@
> >  
> >  obj-y		+= sun4i-csi/
> >  obj-y		+= sun6i-csi/
> > +obj-y		+= sun6i-mipi-csi2/
> 
> I'm not sure we need a new folder here, it's only ever tied to sun6i-csi
> so it would make more sense to have it in the same folder.

My thinking was that it's a different driver and a different hardware block,
so it's less confusing to have it in a different directory. I think many would
expect the driver to share code with sun6i-csi (in a component driver kind of
way) if it was in the same directory, which is not the case here.

> >  obj-y		+= sun8i-di/
> >  obj-y		+= sun8i-rotate/
> > diff --git a/drivers/media/platform/sunxi/sun6i-mipi-csi2/Kconfig b/drivers/media/platform/sunxi/sun6i-mipi-csi2/Kconfig
> > new file mode 100644
> > index 000000000000..3260591ed5c0
> > --- /dev/null
> > +++ b/drivers/media/platform/sunxi/sun6i-mipi-csi2/Kconfig
> > @@ -0,0 +1,12 @@
> > +# SPDX-License-Identifier: GPL-2.0-only
> > +config VIDEO_SUN6I_MIPI_CSI2
> > +	tristate "Allwinner A31 MIPI CSI-2 Controller Driver"
> > +	depends on VIDEO_V4L2 && COMMON_CLK
> > +	depends on ARCH_SUNXI || COMPILE_TEST
> > +	select PHY_SUN6I_MIPI_DPHY
> > +	select MEDIA_CONTROLLER
> > +	select VIDEO_V4L2_SUBDEV_API
> > +	select REGMAP_MMIO
> > +	select V4L2_FWNODE
> > +	help
> > +	   Support for the Allwinner A31 MIPI CSI-2 Controller.
> > diff --git a/drivers/media/platform/sunxi/sun6i-mipi-csi2/Makefile b/drivers/media/platform/sunxi/sun6i-mipi-csi2/Makefile
> > new file mode 100644
> > index 000000000000..14e4e03818b5
> > --- /dev/null
> > +++ b/drivers/media/platform/sunxi/sun6i-mipi-csi2/Makefile
> > @@ -0,0 +1,4 @@
> > +# SPDX-License-Identifier: GPL-2.0-only
> > +sun6i-mipi-csi2-y += sun6i_mipi_csi2.o
> > +
> > +obj-$(CONFIG_VIDEO_SUN6I_MIPI_CSI2) += sun6i-mipi-csi2.o
> > diff --git a/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c b/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c
> > new file mode 100644
> > index 000000000000..a6567ef82fb4
> > --- /dev/null
> > +++ b/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c
> > @@ -0,0 +1,591 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + * Copyright 2020 Bootlin
> > + * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
> > + */
> > +
> > +#include <linux/clk.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/of_device.h>
> > +#include <linux/phy/phy.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/regmap.h>
> > +#include <linux/reset.h>
> > +#include <media/v4l2-ctrls.h>
> > +#include <media/v4l2-device.h>
> > +#include <media/v4l2-fwnode.h>
> > +
> > +#include "sun6i_mipi_csi2.h"
> > +
> > +#define MODULE_NAME	"sun6i-mipi-csi2"
> > +
> > +static const u32 sun6i_mipi_csi2_mbus_codes[] = {
> > +	MEDIA_BUS_FMT_SBGGR8_1X8,
> > +	MEDIA_BUS_FMT_SGBRG8_1X8,
> > +	MEDIA_BUS_FMT_SGRBG8_1X8,
> > +	MEDIA_BUS_FMT_SRGGB8_1X8,
> > +	MEDIA_BUS_FMT_SBGGR10_1X10,
> > +	MEDIA_BUS_FMT_SGBRG10_1X10,
> > +	MEDIA_BUS_FMT_SGRBG10_1X10,
> > +	MEDIA_BUS_FMT_SRGGB10_1X10,
> > +};
> > +
> > +/* Video */
> > +
> > +static int sun6i_mipi_csi2_s_stream(struct v4l2_subdev *subdev, int on)
> > +{
> > +	struct sun6i_mipi_csi2_video *video =
> > +		sun6i_mipi_csi2_subdev_video(subdev);
> > +	struct sun6i_mipi_csi2_dev *cdev = sun6i_mipi_csi2_video_dev(video);
> > +	struct v4l2_subdev *remote_subdev = video->remote_subdev;
> > +	struct v4l2_fwnode_bus_mipi_csi2 *bus_mipi_csi2 =
> > +		&video->endpoint.bus.mipi_csi2;
> > +	union phy_configure_opts dphy_opts = { 0 };
> > +	struct phy_configure_opts_mipi_dphy *dphy_cfg = &dphy_opts.mipi_dphy;
> > +	struct regmap *regmap = cdev->regmap;
> > +	struct v4l2_ctrl *ctrl;
> > +	unsigned int lanes_count;
> > +	unsigned int bpp;
> > +	unsigned long pixel_rate;
> > +	u8 data_type = 0;
> > +	u32 version = 0;
> > +	/* Initialize to 0 to use both in disable label (ret != 0) and off. */
> > +	int ret = 0;
> > +
> > +	if (!remote_subdev)
> > +		return -ENODEV;
> > +
> > +	if (!on) {
> > +		v4l2_subdev_call(remote_subdev, video, s_stream, 0);
> > +		goto disable;
> > +	}
> > +
> > +	switch (video->mbus_format.code) {
> > +	case MEDIA_BUS_FMT_SBGGR8_1X8:
> > +	case MEDIA_BUS_FMT_SGBRG8_1X8:
> > +	case MEDIA_BUS_FMT_SGRBG8_1X8:
> > +	case MEDIA_BUS_FMT_SRGGB8_1X8:
> > +		data_type = MIPI_CSI2_DATA_TYPE_RAW8;
> > +		bpp = 8;
> > +		break;
> > +	case MEDIA_BUS_FMT_SBGGR10_1X10:
> > +	case MEDIA_BUS_FMT_SGBRG10_1X10:
> > +	case MEDIA_BUS_FMT_SGRBG10_1X10:
> > +	case MEDIA_BUS_FMT_SRGGB10_1X10:
> > +		data_type = MIPI_CSI2_DATA_TYPE_RAW10;
> > +		bpp = 10;
> > +		break;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +
> > +	/* Sensor pixel rate */
> > +
> > +	ctrl = v4l2_ctrl_find(remote_subdev->ctrl_handler, V4L2_CID_PIXEL_RATE);
> > +	if (!ctrl) {
> > +		dev_err(cdev->dev,
> > +			"%s: no MIPI CSI-2 pixel rate from the sensor\n",
> > +			__func__);
> > +		return -ENODEV;
> > +	}
> > +
> > +	pixel_rate = (unsigned long)v4l2_ctrl_g_ctrl_int64(ctrl);
> > +	if (!pixel_rate) {
> > +		dev_err(cdev->dev,
> > +			"%s: zero MIPI CSI-2 pixel rate from the sensor\n",
> > +			__func__);
> > +		return -ENODEV;
> > +	}
> > +
> > +	/* Power management */
> > +
> > +	ret = pm_runtime_get_sync(cdev->dev);
> > +	if (ret < 0) {
> > +		pm_runtime_put_noidle(cdev->dev);
> > +		return ret;
> > +	}
> > +
> > +	/* D-PHY configuration */
> > +
> > +	lanes_count = bus_mipi_csi2->num_data_lanes;
> > +	phy_mipi_dphy_get_default_config(pixel_rate, bpp, lanes_count,
> > +					 dphy_cfg);
> > +
> > +	/*
> > +	 * Note that our hardware is using DDR, which is not taken in account by
> > +	 * phy_mipi_dphy_get_default_config when calculating hs_clk_rate from
> > +	 * the pixel rate, lanes count and bpp.
> > +	 *
> > +	 * The resulting clock rate is basically the symbol rate over the whole
> > +	 * link. The actual clock rate is calculated with division by two since
> > +	 * DDR samples both on rising and falling edges.
> > +	 */
> > +
> > +	dev_dbg(cdev->dev, "A31 MIPI CSI-2 config:\n");
> > +	dev_dbg(cdev->dev, "%ld pixels/s, %u bits/pixel, %lu Hz clock\n",
> > +		pixel_rate, bpp, dphy_cfg->hs_clk_rate / 2);
> > +
> > +	ret = phy_reset(cdev->dphy);
> > +	if (ret) {
> > +		dev_err(cdev->dev, "failed to reset MIPI D-PHY\n");
> > +		goto error_pm;
> > +	}
> > +
> > +	ret = phy_set_mode_ext(cdev->dphy, PHY_MODE_MIPI_DPHY,
> > +			       PHY_MIPI_DPHY_SUBMODE_RX);
> > +	if (ret) {
> > +		dev_err(cdev->dev, "failed to set MIPI D-PHY mode\n");
> > +		goto error_pm;
> > +	}
> > +
> > +	ret = phy_configure(cdev->dphy, &dphy_opts);
> > +	if (ret) {
> > +		dev_err(cdev->dev, "failed to configure MIPI D-PHY\n");
> > +		goto error_pm;
> > +	}
> > +
> > +	ret = phy_power_on(cdev->dphy);
> > +	if (ret) {
> > +		dev_err(cdev->dev, "failed to power on MIPI D-PHY\n");
> > +		goto error_pm;
> > +	}
> > +
> > +	/* MIPI CSI-2 controller setup */
> > +
> > +	/*
> > +	 * The enable flow in the Allwinner BSP is a bit different: the enable
> > +	 * and reset bits are set together before starting the CSI controller.
> > +	 *
> > +	 * In mainline we enable the CSI controller first (due to subdev logic).
> > +	 * One reliable way to make this work is to deassert reset, configure
> > +	 * registers and enable the controller when everything's ready.
> > +	 *
> > +	 * However, setting the version enable bit and removing it afterwards
> > +	 * appears necessary for capture to work reliably, while replacing it
> > +	 * with a delay doesn't do the trick.
> > +	 */
> > +	regmap_write(regmap, SUN6I_MIPI_CSI2_CTL_REG,
> > +		     SUN6I_MIPI_CSI2_CTL_RESET_N |
> > +		     SUN6I_MIPI_CSI2_CTL_VERSION_EN |
> > +		     SUN6I_MIPI_CSI2_CTL_UNPK_EN);
> > +
> > +	regmap_read(regmap, SUN6I_MIPI_CSI2_VERSION_REG, &version);
> > +
> > +	regmap_update_bits(regmap, SUN6I_MIPI_CSI2_CTL_REG,
> > +				   SUN6I_MIPI_CSI2_CTL_VERSION_EN, 0);
> > +
> > +	dev_dbg(cdev->dev, "A31 MIPI CSI-2 version: %04x\n", version);
> > +
> > +	regmap_write(regmap, SUN6I_MIPI_CSI2_CFG_REG,
> > +		     SUN6I_MIPI_CSI2_CFG_CHANNEL_MODE(1) |
> > +		     SUN6I_MIPI_CSI2_CFG_LANE_COUNT(lanes_count));
> > +
> > +	/*
> > +	 * Our MIPI CSI-2 controller has internal channels that can be
> > +	 * configured to match a specific MIPI CSI-2 virtual channel and/or
> > +	 * a specific data type. Each internal channel can be piped to an
> > +	 * internal channel of the CSI controller.
> > +	 *
> > +	 * We set virtual channel numbers to all channels to make sure that
> > +	 * virtual channel 0 goes to CSI channel 0 only.
> > +	 */
> > +	regmap_write(regmap, SUN6I_MIPI_CSI2_VCDT_RX_REG,
> > +		     SUN6I_MIPI_CSI2_VCDT_RX_CH_VC(3, 3) |
> > +		     SUN6I_MIPI_CSI2_VCDT_RX_CH_VC(2, 2) |
> > +		     SUN6I_MIPI_CSI2_VCDT_RX_CH_VC(1, 1) |
> > +		     SUN6I_MIPI_CSI2_VCDT_RX_CH_VC(0, 0) |
> > +		     SUN6I_MIPI_CSI2_VCDT_RX_CH_DT(0, data_type));
> > +
> > +	regmap_update_bits(regmap, SUN6I_MIPI_CSI2_CTL_REG,
> > +			   SUN6I_MIPI_CSI2_CTL_EN, SUN6I_MIPI_CSI2_CTL_EN);
> > +
> > +	ret = v4l2_subdev_call(remote_subdev, video, s_stream, 1);
> > +	if (ret)
> > +		goto disable;
> > +
> > +	return 0;
> > +
> > +disable:
> > +	regmap_update_bits(regmap, SUN6I_MIPI_CSI2_CTL_REG,
> > +			   SUN6I_MIPI_CSI2_CTL_EN, 0);
> > +
> > +	phy_power_off(cdev->dphy);
> > +
> > +error_pm:
> > +	pm_runtime_put(cdev->dev);
> > +
> > +	return ret;
> > +}
> > +
> > +static const struct v4l2_subdev_video_ops sun6i_mipi_csi2_subdev_video_ops = {
> > +	.s_stream	= sun6i_mipi_csi2_s_stream,
> > +};
> > +
> > +/* Pad */
> > +
> > +static int
> > +sun6i_mipi_csi2_enum_mbus_code(struct v4l2_subdev *subdev,
> > +			       struct v4l2_subdev_pad_config *config,
> > +			       struct v4l2_subdev_mbus_code_enum *code_enum)
> > +{
> > +	if (code_enum->index >= ARRAY_SIZE(sun6i_mipi_csi2_mbus_codes))
> > +		return -EINVAL;
> > +
> > +	code_enum->code = sun6i_mipi_csi2_mbus_codes[code_enum->index];
> > +
> > +	return 0;
> > +}
> > +
> > +static int sun6i_mipi_csi2_get_fmt(struct v4l2_subdev *subdev,
> > +				   struct v4l2_subdev_pad_config *config,
> > +				   struct v4l2_subdev_format *format)
> > +{
> > +	struct sun6i_mipi_csi2_video *video =
> > +		sun6i_mipi_csi2_subdev_video(subdev);
> > +	struct v4l2_mbus_framefmt *mbus_format = &format->format;
> > +
> > +	if (format->which == V4L2_SUBDEV_FORMAT_TRY)
> > +		*mbus_format = *v4l2_subdev_get_try_format(subdev, config,
> > +							   format->pad);
> > +	else
> > +		*mbus_format = video->mbus_format;
> > +
> > +	return 0;
> > +}
> > +
> > +static int sun6i_mipi_csi2_set_fmt(struct v4l2_subdev *subdev,
> > +				   struct v4l2_subdev_pad_config *config,
> > +				   struct v4l2_subdev_format *format)
> > +{
> > +	struct sun6i_mipi_csi2_video *video =
> > +		sun6i_mipi_csi2_subdev_video(subdev);
> > +	struct v4l2_mbus_framefmt *mbus_format = &format->format;
> > +
> > +	if (format->which == V4L2_SUBDEV_FORMAT_TRY)
> > +		*v4l2_subdev_get_try_format(subdev, config, format->pad) =
> > +			*mbus_format;
> > +	else
> > +		video->mbus_format = *mbus_format;
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct v4l2_subdev_pad_ops sun6i_mipi_csi2_subdev_pad_ops = {
> > +	.enum_mbus_code	= sun6i_mipi_csi2_enum_mbus_code,
> > +	.get_fmt	= sun6i_mipi_csi2_get_fmt,
> > +	.set_fmt	= sun6i_mipi_csi2_set_fmt,
> > +};
> > +
> > +/* Subdev */
> > +
> > +static const struct v4l2_subdev_ops sun6i_mipi_csi2_subdev_ops = {
> > +	.video		= &sun6i_mipi_csi2_subdev_video_ops,
> > +	.pad		= &sun6i_mipi_csi2_subdev_pad_ops,
> > +};
> > +
> > +/* Notifier */
> > +
> > +static int
> > +sun6i_mipi_csi2_notifier_bound(struct v4l2_async_notifier *notifier,
> > +			       struct v4l2_subdev *remote_subdev,
> > +			       struct v4l2_async_subdev *remote_subdev_async)
> > +{
> > +	struct v4l2_subdev *subdev = notifier->sd;
> > +	struct sun6i_mipi_csi2_video *video =
> > +		sun6i_mipi_csi2_subdev_video(subdev);
> > +	struct sun6i_mipi_csi2_dev *cdev = sun6i_mipi_csi2_video_dev(video);
> > +	int source_pad;
> > +	int ret;
> > +
> > +	source_pad = media_entity_get_fwnode_pad(&remote_subdev->entity,
> > +						 remote_subdev->fwnode,
> > +						 MEDIA_PAD_FL_SOURCE);
> > +	if (source_pad < 0)
> > +		return source_pad;
> > +
> > +	ret = media_create_pad_link(&remote_subdev->entity, source_pad,
> > +				    &subdev->entity, 0,
> > +				    MEDIA_LNK_FL_ENABLED |
> > +				    MEDIA_LNK_FL_IMMUTABLE);
> > +	if (ret) {
> > +		dev_err(cdev->dev, "failed to create %s:%u -> %s:%u link\n",
> > +			remote_subdev->entity.name, source_pad,
> > +			subdev->entity.name, 0);
> > +		return ret;
> > +	}
> > +
> > +	video->remote_subdev = remote_subdev;
> > +
> > +	return 0;
> > +}
> > +
> > +static const
> > +struct v4l2_async_notifier_operations sun6i_mipi_csi2_notifier_ops = {
> > +	.bound		= sun6i_mipi_csi2_notifier_bound,
> > +};
> > +
> > +/* Media Entity */
> > +
> > +static const struct media_entity_operations sun6i_mipi_csi2_entity_ops = {
> > +	.link_validate	= v4l2_subdev_link_validate,
> > +};
> > +
> > +/* Base Driver */
> > +
> > +static int __maybe_unused sun6i_mipi_csi2_suspend(struct device *dev)
> > +{
> > +	struct sun6i_mipi_csi2_dev *cdev = dev_get_drvdata(dev);
> > +
> > +	clk_disable_unprepare(cdev->clk_mod);
> > +	clk_disable_unprepare(cdev->clk_bus);
> > +	reset_control_assert(cdev->reset);
> > +
> > +	return 0;
> > +}
> > +
> > +static int __maybe_unused sun6i_mipi_csi2_resume(struct device *dev)
> > +{
> > +	struct sun6i_mipi_csi2_dev *cdev = dev_get_drvdata(dev);
> > +	int ret;
> > +
> > +	ret = reset_control_deassert(cdev->reset);
> > +	if (ret) {
> > +		dev_err(cdev->dev, "failed to deassert reset\n");
> > +		return ret;
> > +	}
> > +
> > +	ret = clk_prepare_enable(cdev->clk_bus);
> > +	if (ret) {
> > +		dev_err(cdev->dev, "failed to enable bus clock\n");
> > +		goto error_reset;
> > +	}
> > +
> > +	ret = clk_prepare_enable(cdev->clk_mod);
> > +	if (ret) {
> > +		dev_err(cdev->dev, "failed to enable module clock\n");
> > +		goto error_clk_bus;
> > +	}
> > +
> > +	return 0;
> > +
> > +error_clk_bus:
> > +	clk_disable_unprepare(cdev->clk_bus);
> > +
> > +error_reset:
> > +	reset_control_assert(cdev->reset);
> > +
> > +	return ret;
> > +}
> 
> I'm guessing you set the __maybe_unused attribute because you're using
> SET_RUNTIME_PM_OPS, but what would happen if runtime_pm isn't selected?
> It looks like you don't handle that case.

Indeed, __maybe_unused is because of the conditional definition of
SET_RUNTIME_PM_OPS. If CONFIG_PM is not selected, then I guess the controller
wouldn't be powered and wouldn't work. So I should definitely add a Kconfig
dependency on PM then, right?

Thanks for catching this!

> > +static int sun6i_mipi_csi2_v4l2_setup(struct sun6i_mipi_csi2_dev *cdev)
> > +{
> > +	struct sun6i_mipi_csi2_video *video = &cdev->video;
> > +	struct v4l2_subdev *subdev = &video->subdev;
> > +	struct v4l2_async_notifier *notifier = &video->notifier;
> > +	struct fwnode_handle *handle;
> > +	struct v4l2_fwnode_endpoint *endpoint;
> > +	struct v4l2_async_subdev *subdev_async;
> > +	int ret;
> > +
> > +	/* Subdev */
> > +
> > +	v4l2_subdev_init(subdev, &sun6i_mipi_csi2_subdev_ops);
> > +	subdev->dev = cdev->dev;
> > +	subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> > +	strscpy(subdev->name, MODULE_NAME, sizeof(subdev->name));
> > +	v4l2_set_subdevdata(subdev, cdev);
> > +
> > +	/* Entity */
> > +
> > +	subdev->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
> > +	subdev->entity.ops = &sun6i_mipi_csi2_entity_ops;
> > +
> > +	/* Pads */
> > +
> > +	video->pads[0].flags = MEDIA_PAD_FL_SINK;
> > +	video->pads[1].flags = MEDIA_PAD_FL_SOURCE;
> > +
> > +	ret = media_entity_pads_init(&subdev->entity, 2, video->pads);
> > +	if (ret)
> > +		return ret;
> > +
> > +	/* Endpoint */
> > +
> > +	handle = fwnode_graph_get_endpoint_by_id(dev_fwnode(cdev->dev), 0, 0,
> > +						 FWNODE_GRAPH_ENDPOINT_NEXT);
> > +	if (!handle) {
> > +		ret = -ENODEV;
> > +		goto error_media_entity;
> > +	}
> > +
> > +	endpoint = &video->endpoint;
> > +	endpoint->bus_type = V4L2_MBUS_CSI2_DPHY;
> > +
> > +	ret = v4l2_fwnode_endpoint_parse(handle, endpoint);
> > +	fwnode_handle_put(handle);
> > +	if (ret)
> > +		goto error_media_entity;
> > +
> > +	/* Notifier */
> > +
> > +	v4l2_async_notifier_init(notifier);
> > +
> > +	subdev_async = &video->subdev_async;
> > +	ret = v4l2_async_notifier_add_fwnode_remote_subdev(notifier, handle,
> > +							   subdev_async);
> > +	if (ret)
> > +		goto error_media_entity;
> > +
> > +	video->notifier.ops = &sun6i_mipi_csi2_notifier_ops;
> > +
> > +	ret = v4l2_async_subdev_notifier_register(subdev, notifier);
> > +	if (ret < 0)
> > +		goto error_notifier;
> > +
> > +	/* Subdev */
> > +
> > +	ret = v4l2_async_register_subdev(subdev);
> > +	if (ret < 0)
> > +		goto error_notifier_registered;
> > +
> > +	/* Runtime PM */
> > +
> > +	pm_runtime_enable(cdev->dev);
> > +	pm_runtime_set_suspended(cdev->dev);
> > +
> > +	return 0;
> > +
> > +error_notifier_registered:
> > +	v4l2_async_notifier_unregister(notifier);
> > +error_notifier:
> > +	v4l2_async_notifier_cleanup(notifier);
> > +error_media_entity:
> > +	media_entity_cleanup(&subdev->entity);
> > +
> > +	return ret;
> > +}
> > +
> > +static int sun6i_mipi_csi2_v4l2_teardown(struct sun6i_mipi_csi2_dev *cdev)
> > +{
> > +	struct sun6i_mipi_csi2_video *video = &cdev->video;
> > +	struct v4l2_subdev *subdev = &video->subdev;
> > +	struct v4l2_async_notifier *notifier = &video->notifier;
> > +
> > +	v4l2_async_unregister_subdev(subdev);
> > +	v4l2_async_notifier_unregister(notifier);
> > +	v4l2_async_notifier_cleanup(notifier);
> > +	media_entity_cleanup(&subdev->entity);
> > +	v4l2_device_unregister_subdev(subdev);
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct regmap_config sun6i_mipi_csi2_regmap_config = {
> > +	.reg_bits       = 32,
> > +	.reg_stride     = 4,
> > +	.val_bits       = 32,
> > +	.max_register	= 0x400,
> > +};
> > +
> > +static int sun6i_mipi_csi2_probe(struct platform_device *pdev)
> > +{
> > +	struct sun6i_mipi_csi2_dev *cdev;
> > +	struct resource *res;
> > +	void __iomem *io_base;
> > +	int ret;
> > +
> > +	cdev = devm_kzalloc(&pdev->dev, sizeof(*cdev), GFP_KERNEL);
> > +	if (!cdev)
> > +		return -ENOMEM;
> > +
> > +	cdev->dev = &pdev->dev;
> > +
> > +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +	io_base = devm_ioremap_resource(&pdev->dev, res);
> > +	if (IS_ERR(io_base))
> > +		return PTR_ERR(io_base);
> > +
> > +	cdev->regmap = devm_regmap_init_mmio(&pdev->dev, io_base,
> > +					     &sun6i_mipi_csi2_regmap_config);
> > +	if (IS_ERR(cdev->regmap)) {
> > +		dev_err(&pdev->dev, "failed to init register map\n");
> > +		return PTR_ERR(cdev->regmap);
> > +	}
> > +
> > +	cdev->clk_bus = devm_clk_get(&pdev->dev, "bus");
> > +	if (IS_ERR(cdev->clk_bus)) {
> > +		dev_err(&pdev->dev, "failed to acquire bus clock\n");
> > +		return PTR_ERR(cdev->clk_bus);
> > +	}
> > +
> > +	cdev->clk_mod = devm_clk_get(&pdev->dev, "mod");
> > +	if (IS_ERR(cdev->clk_mod)) {
> > +		dev_err(&pdev->dev, "failed to acquire mod clock\n");
> > +		return PTR_ERR(cdev->clk_mod);
> > +	}
> > +
> > +	cdev->reset = devm_reset_control_get_shared(&pdev->dev, NULL);
> > +	if (IS_ERR(cdev->reset)) {
> > +		dev_err(&pdev->dev, "failed to get reset controller\n");
> > +		return PTR_ERR(cdev->reset);
> > +	}
> > +
> > +	cdev->dphy = devm_phy_get(&pdev->dev, "dphy");
> > +	if (IS_ERR(cdev->dphy)) {
> > +		dev_err(&pdev->dev, "failed to get the MIPI D-PHY\n");
> > +		return PTR_ERR(cdev->dphy);
> > +	}
> > +
> > +	ret = phy_init(cdev->dphy);
> > +	if (ret) {
> > +		dev_err(&pdev->dev, "failed to initialize the MIPI D-PHY\n");
> > +		return ret;
> > +	}
> > +
> > +	platform_set_drvdata(pdev, cdev);
> > +
> > +	ret = sun6i_mipi_csi2_v4l2_setup(cdev);
> > +	if (ret)
> > +		return ret;
> > +
> > +	return 0;
> > +}
> > +
> > +static int sun6i_mipi_csi2_remove(struct platform_device *pdev)
> > +{
> > +	struct sun6i_mipi_csi2_dev *cdev = platform_get_drvdata(pdev);
> > +
> > +	phy_exit(cdev->dphy);
> > +
> > +	return sun6i_mipi_csi2_v4l2_teardown(cdev);
> 
> This seem to be the only user and its content is pretty straightforward,
> so we shouldn't have a separate function here

This is for symmetry with sun6i_mipi_csi2_v4l2_setup and the intent is to split
v4l2 setup/teardown from driver resources, which makes things clearer and nicer
to read overall.

What are the downsides and why is it a problem that it has a single caller?

> > +}
> > +
> > +static const struct dev_pm_ops sun6i_mipi_csi2_pm_ops = {
> > +	SET_RUNTIME_PM_OPS(sun6i_mipi_csi2_suspend, sun6i_mipi_csi2_resume,
> > +			   NULL)
> > +};
> > +
> > +static const struct of_device_id sun6i_mipi_csi2_of_match[] = {
> > +	{ .compatible = "allwinner,sun6i-a31-mipi-csi2" },
> > +	{ .compatible = "allwinner,sun8i-v3s-mipi-csi2", },
> 
> There's no need for the v3s compatible here, it will fallback to the a31
> one anyway.

Understood.

Thanks for the review!

Cheers,

Paul


-- 
Paul Kocialkowski, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 09/19] ARM: dts: sunxi: h3/h5: Add CSI controller port for parallel input
  2020-12-01 12:14   ` Maxime Ripard
@ 2020-12-02 15:02     ` Paul Kocialkowski
  2020-12-02 15:46       ` Maxime Ripard
  0 siblings, 1 reply; 39+ messages in thread
From: Paul Kocialkowski @ 2020-12-02 15:02 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: linux-media, devicetree, linux-arm-kernel, linux-kernel,
	linux-doc, devel, linux-sunxi, Yong Deng, Mauro Carvalho Chehab,
	Rob Herring, Chen-Yu Tsai, Jernej Skrabec, Jonathan Corbet,
	Kishon Vijay Abraham I, Vinod Koul, Helen Koike,
	Dafna Hirschfeld, Greg Kroah-Hartman, Philipp Zabel,
	Sakari Ailus, Hans Verkuil, Thomas Petazzoni, kevin.lhopital

[-- Attachment #1: Type: text/plain, Size: 1735 bytes --]

Hi,

On Tue 01 Dec 20, 13:14, Maxime Ripard wrote:
> On Sat, Nov 28, 2020 at 03:28:29PM +0100, Paul Kocialkowski wrote:
> > Since the CSI controller binding is getting a bit more complex due
> > to the addition of MIPI CSI-2 bridge support, make the ports node
> > explicit with the parallel port.
> > 
> > This way, it's clear that the controller only supports parallel
> > interface input and there's no confusion about the port number.
> > 
> > Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
> > ---
> >  arch/arm/boot/dts/sunxi-h3-h5.dtsi | 9 +++++++++
> >  1 file changed, 9 insertions(+)
> > 
> > diff --git a/arch/arm/boot/dts/sunxi-h3-h5.dtsi b/arch/arm/boot/dts/sunxi-h3-h5.dtsi
> > index 9be13378d4df..02b698cace6a 100644
> > --- a/arch/arm/boot/dts/sunxi-h3-h5.dtsi
> > +++ b/arch/arm/boot/dts/sunxi-h3-h5.dtsi
> > @@ -803,6 +803,15 @@ csi: camera@1cb0000 {
> >  			pinctrl-names = "default";
> >  			pinctrl-0 = <&csi_pins>;
> >  			status = "disabled";
> > +
> > +			ports {
> > +				#address-cells = <1>;
> > +				#size-cells = <0>;
> > +
> > +				csi_in_parallel: port@0 {
> > +					reg = <0>;
> > +				};
> > +			};
> >  		};
> 
> This will create a DTC warning, since port@0 is the only node, and is
> equivalent to port

I'm not seeing the warning when running dtbs_check.
More generally, why is it a problem that there's only one node defined?

One issue that I did see is that the port node doesn't have an endpoint
here, so I will remove the requirement to have an endpoint in the bindings
documentation to allow this kind of definition.

Cheers,

Paul

-- 
Paul Kocialkowski, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 07/19] media: sun6i-csi: Add support for MIPI CSI-2 bridge input
  2020-12-02 14:19     ` Paul Kocialkowski
@ 2020-12-02 15:40       ` Maxime Ripard
  2020-12-02 16:07         ` Paul Kocialkowski
  0 siblings, 1 reply; 39+ messages in thread
From: Maxime Ripard @ 2020-12-02 15:40 UTC (permalink / raw)
  To: Paul Kocialkowski
  Cc: linux-media, devicetree, linux-arm-kernel, linux-kernel,
	linux-doc, devel, linux-sunxi, Yong Deng, Mauro Carvalho Chehab,
	Rob Herring, Chen-Yu Tsai, Jernej Skrabec, Jonathan Corbet,
	Kishon Vijay Abraham I, Vinod Koul, Helen Koike,
	Dafna Hirschfeld, Greg Kroah-Hartman, Philipp Zabel,
	Sakari Ailus, Hans Verkuil, Thomas Petazzoni, kevin.lhopital

[-- Attachment #1: Type: text/plain, Size: 2710 bytes --]

On Wed, Dec 02, 2020 at 03:19:11PM +0100, Paul Kocialkowski wrote:
> Hi,
> 
> On Tue 01 Dec 20, 13:12, Maxime Ripard wrote:
> > Hi,
> > 
> > On Sat, Nov 28, 2020 at 03:28:27PM +0100, Paul Kocialkowski wrote:
> > > The A31 CSI controller supports a MIPI CSI-2 bridge input, which has
> > > its own dedicated port in the fwnode graph.
> > > 
> > > Support for this input is added with this change:
> > > - two pads are defined for the media entity instead of one
> > >   and only one needs to be connected at a time;
> > > - the pads currently match the fwnode graph representation;
> > > - links are created between our pads and the subdevs for each
> > >   interface and are no longer immutable so that userspace can select
> > >   which interface to use in case both are bound to a subdev;
> > > - fwnode endpoints are parsed and stored for each interface;
> > > - the active subdev (and fwnode endpoint) is retrieved when validating
> > >   the media link at stream on time and cleared at stream off;
> > > - an error is raised if both links are active at the same time;
> > > - the MIPI interface bit is set if the MIPI CSI-2 bridge endpoint is
> > >   active.
> > > 
> > > In the future, the media entity representation might evolve to:
> > > - distinguish the internal parallel bridge and data formatter;
> > > - represent each of the 4 internal channels that can exist between
> > >   the parallel bridge (for BT656 time-multiplex) and MIPI CSI-2
> > >   (internal channels can be mapped to virtual channels);
> > > - connect the controller's output to the ISP instead of its
> > >   DMA engine.
> > > 
> > > Finally note that the MIPI CSI-2 bridges should not be linked in
> > > the fwnode graph unless they have a sensor subdev attached.
> > 
> > I'll leave most of the review to Laurent and Sakari, but I'm not quite
> > sure what you meant in the last paragraph. Did you mean that the
> > MIPI-CSI controller in the Allwinner SoC should only be linked if it has
> > a sensor attached, or did you mean that any MIPI-CSI2 bridge cannot be
> > attached to the controller?
> 
> So the use of plural was a mistake and your first understanding is the correct
> one: if the bridge is linked to the CSI controller in the OF graph but the
> bridge doesn't have a sensor attached, the CSI controller driver will fail
> to probe, as far as I could see.

I'm not sure it's reasonable to not link it in the DTSI then, we'll want
to reduce as much the boilerplate from the board DTS as possible, and
the MIPI-CSI controller is always there anyway. However, we should
definitely have it disabled if there's no sensor, which should solve
your probe issue

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 09/19] ARM: dts: sunxi: h3/h5: Add CSI controller port for parallel input
  2020-12-02 15:02     ` Paul Kocialkowski
@ 2020-12-02 15:46       ` Maxime Ripard
  0 siblings, 0 replies; 39+ messages in thread
From: Maxime Ripard @ 2020-12-02 15:46 UTC (permalink / raw)
  To: Paul Kocialkowski
  Cc: linux-media, devicetree, linux-arm-kernel, linux-kernel,
	linux-doc, devel, linux-sunxi, Yong Deng, Mauro Carvalho Chehab,
	Rob Herring, Chen-Yu Tsai, Jernej Skrabec, Jonathan Corbet,
	Kishon Vijay Abraham I, Vinod Koul, Helen Koike,
	Dafna Hirschfeld, Greg Kroah-Hartman, Philipp Zabel,
	Sakari Ailus, Hans Verkuil, Thomas Petazzoni, kevin.lhopital

[-- Attachment #1: Type: text/plain, Size: 2043 bytes --]

On Wed, Dec 02, 2020 at 04:02:09PM +0100, Paul Kocialkowski wrote:
> Hi,
> 
> On Tue 01 Dec 20, 13:14, Maxime Ripard wrote:
> > On Sat, Nov 28, 2020 at 03:28:29PM +0100, Paul Kocialkowski wrote:
> > > Since the CSI controller binding is getting a bit more complex due
> > > to the addition of MIPI CSI-2 bridge support, make the ports node
> > > explicit with the parallel port.
> > > 
> > > This way, it's clear that the controller only supports parallel
> > > interface input and there's no confusion about the port number.
> > > 
> > > Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
> > > ---
> > >  arch/arm/boot/dts/sunxi-h3-h5.dtsi | 9 +++++++++
> > >  1 file changed, 9 insertions(+)
> > > 
> > > diff --git a/arch/arm/boot/dts/sunxi-h3-h5.dtsi b/arch/arm/boot/dts/sunxi-h3-h5.dtsi
> > > index 9be13378d4df..02b698cace6a 100644
> > > --- a/arch/arm/boot/dts/sunxi-h3-h5.dtsi
> > > +++ b/arch/arm/boot/dts/sunxi-h3-h5.dtsi
> > > @@ -803,6 +803,15 @@ csi: camera@1cb0000 {
> > >  			pinctrl-names = "default";
> > >  			pinctrl-0 = <&csi_pins>;
> > >  			status = "disabled";
> > > +
> > > +			ports {
> > > +				#address-cells = <1>;
> > > +				#size-cells = <0>;
> > > +
> > > +				csi_in_parallel: port@0 {
> > > +					reg = <0>;
> > > +				};
> > > +			};
> > >  		};
> > 
> > This will create a DTC warning, since port@0 is the only node, and is
> > equivalent to port
> 
> I'm not seeing the warning when running dtbs_check.

Some are silenced by the Linux build system. You can pass W=1 to your
make command line enable all of them.

> More generally, why is it a problem that there's only one node defined?
> 
> One issue that I did see is that the port node doesn't have an endpoint
> here, so I will remove the requirement to have an endpoint in the bindings
> documentation to allow this kind of definition.

We definitely want to have the endpoint required. If the CSI node is
disabled, the error should be ignored by the dt-validate tool though

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 13/19] media: sunxi: Add support for the A31 MIPI CSI-2 controller
  2020-12-02 14:44     ` Paul Kocialkowski
@ 2020-12-02 15:48       ` Maxime Ripard
  2020-12-02 16:09         ` Paul Kocialkowski
  0 siblings, 1 reply; 39+ messages in thread
From: Maxime Ripard @ 2020-12-02 15:48 UTC (permalink / raw)
  To: Paul Kocialkowski
  Cc: linux-media, devicetree, linux-arm-kernel, linux-kernel,
	linux-doc, devel, linux-sunxi, Yong Deng, Mauro Carvalho Chehab,
	Rob Herring, Chen-Yu Tsai, Jernej Skrabec, Jonathan Corbet,
	Kishon Vijay Abraham I, Vinod Koul, Helen Koike,
	Dafna Hirschfeld, Greg Kroah-Hartman, Philipp Zabel,
	Sakari Ailus, Hans Verkuil, Thomas Petazzoni, kevin.lhopital

[-- Attachment #1: Type: text/plain, Size: 2015 bytes --]

On Wed, Dec 02, 2020 at 03:44:47PM +0100, Paul Kocialkowski wrote:
> > > +static int __maybe_unused sun6i_mipi_csi2_suspend(struct device *dev)
> > > +{
> > > +	struct sun6i_mipi_csi2_dev *cdev = dev_get_drvdata(dev);
> > > +
> > > +	clk_disable_unprepare(cdev->clk_mod);
> > > +	clk_disable_unprepare(cdev->clk_bus);
> > > +	reset_control_assert(cdev->reset);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int __maybe_unused sun6i_mipi_csi2_resume(struct device *dev)
> > > +{
> > > +	struct sun6i_mipi_csi2_dev *cdev = dev_get_drvdata(dev);
> > > +	int ret;
> > > +
> > > +	ret = reset_control_deassert(cdev->reset);
> > > +	if (ret) {
> > > +		dev_err(cdev->dev, "failed to deassert reset\n");
> > > +		return ret;
> > > +	}
> > > +
> > > +	ret = clk_prepare_enable(cdev->clk_bus);
> > > +	if (ret) {
> > > +		dev_err(cdev->dev, "failed to enable bus clock\n");
> > > +		goto error_reset;
> > > +	}
> > > +
> > > +	ret = clk_prepare_enable(cdev->clk_mod);
> > > +	if (ret) {
> > > +		dev_err(cdev->dev, "failed to enable module clock\n");
> > > +		goto error_clk_bus;
> > > +	}
> > > +
> > > +	return 0;
> > > +
> > > +error_clk_bus:
> > > +	clk_disable_unprepare(cdev->clk_bus);
> > > +
> > > +error_reset:
> > > +	reset_control_assert(cdev->reset);
> > > +
> > > +	return ret;
> > > +}
> > 
> > I'm guessing you set the __maybe_unused attribute because you're using
> > SET_RUNTIME_PM_OPS, but what would happen if runtime_pm isn't selected?
> > It looks like you don't handle that case.
> 
> Indeed, __maybe_unused is because of the conditional definition of
> SET_RUNTIME_PM_OPS. If CONFIG_PM is not selected, then I guess the controller
> wouldn't be powered and wouldn't work. So I should definitely add a Kconfig
> dependency on PM then, right?

There's two ways we can do it. What you suggested is one, the other is
to have something like our SPI driver to call directly the resume
function if there's no runtime pm support.

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 07/19] media: sun6i-csi: Add support for MIPI CSI-2 bridge input
  2020-12-02 15:40       ` Maxime Ripard
@ 2020-12-02 16:07         ` Paul Kocialkowski
  0 siblings, 0 replies; 39+ messages in thread
From: Paul Kocialkowski @ 2020-12-02 16:07 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: linux-media, devicetree, linux-arm-kernel, linux-kernel,
	linux-doc, devel, linux-sunxi, Yong Deng, Mauro Carvalho Chehab,
	Rob Herring, Chen-Yu Tsai, Jernej Skrabec, Jonathan Corbet,
	Kishon Vijay Abraham I, Vinod Koul, Helen Koike,
	Dafna Hirschfeld, Greg Kroah-Hartman, Philipp Zabel,
	Sakari Ailus, Hans Verkuil, Thomas Petazzoni, kevin.lhopital

[-- Attachment #1: Type: text/plain, Size: 3061 bytes --]

Hi,

On Wed 02 Dec 20, 16:40, Maxime Ripard wrote:
> On Wed, Dec 02, 2020 at 03:19:11PM +0100, Paul Kocialkowski wrote:
> > Hi,
> > 
> > On Tue 01 Dec 20, 13:12, Maxime Ripard wrote:
> > > Hi,
> > > 
> > > On Sat, Nov 28, 2020 at 03:28:27PM +0100, Paul Kocialkowski wrote:
> > > > The A31 CSI controller supports a MIPI CSI-2 bridge input, which has
> > > > its own dedicated port in the fwnode graph.
> > > > 
> > > > Support for this input is added with this change:
> > > > - two pads are defined for the media entity instead of one
> > > >   and only one needs to be connected at a time;
> > > > - the pads currently match the fwnode graph representation;
> > > > - links are created between our pads and the subdevs for each
> > > >   interface and are no longer immutable so that userspace can select
> > > >   which interface to use in case both are bound to a subdev;
> > > > - fwnode endpoints are parsed and stored for each interface;
> > > > - the active subdev (and fwnode endpoint) is retrieved when validating
> > > >   the media link at stream on time and cleared at stream off;
> > > > - an error is raised if both links are active at the same time;
> > > > - the MIPI interface bit is set if the MIPI CSI-2 bridge endpoint is
> > > >   active.
> > > > 
> > > > In the future, the media entity representation might evolve to:
> > > > - distinguish the internal parallel bridge and data formatter;
> > > > - represent each of the 4 internal channels that can exist between
> > > >   the parallel bridge (for BT656 time-multiplex) and MIPI CSI-2
> > > >   (internal channels can be mapped to virtual channels);
> > > > - connect the controller's output to the ISP instead of its
> > > >   DMA engine.
> > > > 
> > > > Finally note that the MIPI CSI-2 bridges should not be linked in
> > > > the fwnode graph unless they have a sensor subdev attached.
> > > 
> > > I'll leave most of the review to Laurent and Sakari, but I'm not quite
> > > sure what you meant in the last paragraph. Did you mean that the
> > > MIPI-CSI controller in the Allwinner SoC should only be linked if it has
> > > a sensor attached, or did you mean that any MIPI-CSI2 bridge cannot be
> > > attached to the controller?
> > 
> > So the use of plural was a mistake and your first understanding is the correct
> > one: if the bridge is linked to the CSI controller in the OF graph but the
> > bridge doesn't have a sensor attached, the CSI controller driver will fail
> > to probe, as far as I could see.
> 
> I'm not sure it's reasonable to not link it in the DTSI then, we'll want
> to reduce as much the boilerplate from the board DTS as possible, and
> the MIPI-CSI controller is always there anyway. However, we should
> definitely have it disabled if there's no sensor, which should solve
> your probe issue

Ah yes there's a good chance that it will solve it.
I'll try that and get back to you!

Cheers,

Paul

-- 
Paul Kocialkowski, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 13/19] media: sunxi: Add support for the A31 MIPI CSI-2 controller
  2020-12-02 15:48       ` Maxime Ripard
@ 2020-12-02 16:09         ` Paul Kocialkowski
  0 siblings, 0 replies; 39+ messages in thread
From: Paul Kocialkowski @ 2020-12-02 16:09 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: linux-media, devicetree, linux-arm-kernel, linux-kernel,
	linux-doc, devel, linux-sunxi, Yong Deng, Mauro Carvalho Chehab,
	Rob Herring, Chen-Yu Tsai, Jernej Skrabec, Jonathan Corbet,
	Kishon Vijay Abraham I, Vinod Koul, Helen Koike,
	Dafna Hirschfeld, Greg Kroah-Hartman, Philipp Zabel,
	Sakari Ailus, Hans Verkuil, Thomas Petazzoni, kevin.lhopital

[-- Attachment #1: Type: text/plain, Size: 2393 bytes --]

Hi,

On Wed 02 Dec 20, 16:48, Maxime Ripard wrote:
> On Wed, Dec 02, 2020 at 03:44:47PM +0100, Paul Kocialkowski wrote:
> > > > +static int __maybe_unused sun6i_mipi_csi2_suspend(struct device *dev)
> > > > +{
> > > > +	struct sun6i_mipi_csi2_dev *cdev = dev_get_drvdata(dev);
> > > > +
> > > > +	clk_disable_unprepare(cdev->clk_mod);
> > > > +	clk_disable_unprepare(cdev->clk_bus);
> > > > +	reset_control_assert(cdev->reset);
> > > > +
> > > > +	return 0;
> > > > +}
> > > > +
> > > > +static int __maybe_unused sun6i_mipi_csi2_resume(struct device *dev)
> > > > +{
> > > > +	struct sun6i_mipi_csi2_dev *cdev = dev_get_drvdata(dev);
> > > > +	int ret;
> > > > +
> > > > +	ret = reset_control_deassert(cdev->reset);
> > > > +	if (ret) {
> > > > +		dev_err(cdev->dev, "failed to deassert reset\n");
> > > > +		return ret;
> > > > +	}
> > > > +
> > > > +	ret = clk_prepare_enable(cdev->clk_bus);
> > > > +	if (ret) {
> > > > +		dev_err(cdev->dev, "failed to enable bus clock\n");
> > > > +		goto error_reset;
> > > > +	}
> > > > +
> > > > +	ret = clk_prepare_enable(cdev->clk_mod);
> > > > +	if (ret) {
> > > > +		dev_err(cdev->dev, "failed to enable module clock\n");
> > > > +		goto error_clk_bus;
> > > > +	}
> > > > +
> > > > +	return 0;
> > > > +
> > > > +error_clk_bus:
> > > > +	clk_disable_unprepare(cdev->clk_bus);
> > > > +
> > > > +error_reset:
> > > > +	reset_control_assert(cdev->reset);
> > > > +
> > > > +	return ret;
> > > > +}
> > > 
> > > I'm guessing you set the __maybe_unused attribute because you're using
> > > SET_RUNTIME_PM_OPS, but what would happen if runtime_pm isn't selected?
> > > It looks like you don't handle that case.
> > 
> > Indeed, __maybe_unused is because of the conditional definition of
> > SET_RUNTIME_PM_OPS. If CONFIG_PM is not selected, then I guess the controller
> > wouldn't be powered and wouldn't work. So I should definitely add a Kconfig
> > dependency on PM then, right?
> 
> There's two ways we can do it. What you suggested is one, the other is
> to have something like our SPI driver to call directly the resume
> function if there's no runtime pm support.

Understood! I think I'll stick to depending on PM (unless you prefer not to)
but it's good to know.

Cheers,

Paul

-- 
Paul Kocialkowski, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 06/19] dt-bindings: media: sun6i-a31-csi: Add MIPI CSI-2 input port
  2020-12-02 14:16     ` Paul Kocialkowski
@ 2020-12-08 19:54       ` Rob Herring
  0 siblings, 0 replies; 39+ messages in thread
From: Rob Herring @ 2020-12-08 19:54 UTC (permalink / raw)
  To: Paul Kocialkowski
  Cc: Maxime Ripard, linux-media, devicetree, linux-arm-kernel,
	linux-kernel, linux-doc, devel, linux-sunxi, Yong Deng,
	Mauro Carvalho Chehab, Chen-Yu Tsai, Jernej Skrabec,
	Jonathan Corbet, Kishon Vijay Abraham I, Vinod Koul, Helen Koike,
	Dafna Hirschfeld, Greg Kroah-Hartman, Philipp Zabel,
	Sakari Ailus, Hans Verkuil, Thomas Petazzoni, kevin.lhopital

On Wed, Dec 02, 2020 at 03:16:44PM +0100, Paul Kocialkowski wrote:
> Hi,
> 
> On Tue 01 Dec 20, 11:43, Maxime Ripard wrote:
> > On Sat, Nov 28, 2020 at 03:28:26PM +0100, Paul Kocialkowski wrote:
> > > The A31 CSI controller supports two distinct input interfaces:
> > > parallel and an external MIPI CSI-2 bridge. The parallel interface
> > > is often connected to a set of hardware pins while the MIPI CSI-2
> > > bridge is an internal FIFO-ish link. As a result, these two inputs
> > > are distinguished as two different ports.
> > > 
> > > Note that only one of the two may be present on a controller instance.
> > > For example, the V3s has one controller dedicated to MIPI-CSI2 and one
> > > dedicated to parallel.
> > > 
> > > Update the binding with an explicit ports node that holds two distinct
> > > port nodes: one for parallel input and one for MIPI CSI-2.
> > > 
> > > This is backward-compatible with the single-port approach that was
> > > previously taken for representing the parallel interface port, which
> > > stays enumerated as fwnode port 0. However, it is now marked as
> > > deprecated and the multi-port approach should be preferred.
> > > 
> > > Note that additional ports may be added in the future, especially to
> > > support feeding the CSI controller's output to the ISP.
> > > 
> > > Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
> > > ---
> > >  .../media/allwinner,sun6i-a31-csi.yaml        | 86 ++++++++++++++++---
> > >  1 file changed, 73 insertions(+), 13 deletions(-)
> > > 
> > > diff --git a/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-csi.yaml b/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-csi.yaml
> > > index 1fd9b5532a21..3bcee2d44f3c 100644
> > > --- a/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-csi.yaml
> > > +++ b/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-csi.yaml
> > > @@ -43,6 +43,7 @@ properties:
> > >    # See ./video-interfaces.txt for details
> > >    port:
> > >      type: object
> > > +    deprecated: true
> > 
> > I'm not sure we want to deprecate it. There's some SoCs with the same
> > controller but without the MIPI-CSI block where this would be completely
> > valid
> 
> I think we'll need to deprecate it when adding support for the ISP anyway,
> so I figured we might as well do it now. But I'm okay to postpone this for now.
> 
> > >      properties:
> > >        endpoint:
> > > @@ -67,6 +68,59 @@ properties:
> > >  
> > >      additionalProperties: false
> > >  
> > > +  ports:
> > > +    type: object
> > > +
> > > +    properties:
> > > +      port@0:
> > > +        type: object
> > > +        description: Parallel input port, connect to a parallel sensor
> > > +
> > > +        properties:
> > > +          reg:
> > > +            const: 0
> > > +
> > > +          endpoint:
> > > +            type: object
> > > +
> > > +            properties:
> > > +              remote-endpoint: true
> > > +
> > > +              bus-width:
> > > +                enum: [ 8, 10, 12, 16 ]
> > > +
> > > +              pclk-sample: true
> > > +              hsync-active: true
> > > +              vsync-active: true
> > > +
> > > +            required:
> > > +              - bus-width
> > > +              - remote-endpoint
> > > +
> > > +        required:
> > > +          - endpoint
> > > +
> > > +        additionalProperties: false
> > > +
> > > +      port@1:
> > > +        type: object
> > > +        description: MIPI CSI-2 bridge input port
> > > +
> > > +        properties:
> > > +          reg:
> > > +            const: 1
> > > +
> > > +          endpoint:
> > > +            type: object
> > > +
> > > +            properties:
> > > +              remote-endpoint: true
> > > +
> > > +            required:
> > > +              - remote-endpoint
> > > +
> > > +        additionalProperties: false
> > 
> > There's a schema for the OF-graph now, you'll want to use it.
> 
> I didn't know that, thanks for the tip! Will look into it.

There's about to be. Don't use what's in graph.yaml (in dt-schema) yet.

Rob

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 16/19] dt-bindings: media: Add A83T MIPI CSI-2 bindings documentation
  2020-11-28 14:28 ` [PATCH v2 16/19] dt-bindings: media: Add A83T MIPI CSI-2 bindings documentation Paul Kocialkowski
@ 2020-12-11  3:12   ` Rob Herring
  0 siblings, 0 replies; 39+ messages in thread
From: Rob Herring @ 2020-12-11  3:12 UTC (permalink / raw)
  To: Paul Kocialkowski
  Cc: linux-media, Thomas Petazzoni, Helen Koike, Hans Verkuil,
	Mauro Carvalho Chehab, Vinod Koul, Philipp Zabel,
	Dafna Hirschfeld, linux-arm-kernel, linux-doc, Sakari Ailus,
	Chen-Yu Tsai, Rob Herring, devicetree, devel, linux-sunxi,
	Jonathan Corbet, Maxime Ripard, Jernej Skrabec, kevin.lhopital,
	linux-kernel, Greg Kroah-Hartman, Yong Deng,
	Kishon Vijay Abraham I

On Sat, 28 Nov 2020 15:28:36 +0100, Paul Kocialkowski wrote:
> This introduces YAML bindings documentation for the A83T MIPI CSI-2
> controller.
> 
> Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
> ---
>  .../media/allwinner,sun8i-a83t-mipi-csi2.yaml | 147 ++++++++++++++++++
>  1 file changed, 147 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/allwinner,sun8i-a83t-mipi-csi2.yaml
> 

Reviewed-by: Rob Herring <robh@kernel.org>

^ permalink raw reply	[flat|nested] 39+ messages in thread

end of thread, other threads:[~2020-12-11  3:13 UTC | newest]

Thread overview: 39+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-11-28 14:28 [PATCH v2 00/19] Allwinner MIPI CSI-2 support for A31/V3s/A83T Paul Kocialkowski
2020-11-28 14:28 ` [PATCH v2 01/19] docs: phy: Add a part about PHY mode and submode Paul Kocialkowski
2020-12-01 10:37   ` Maxime Ripard
2020-11-28 14:28 ` [PATCH v2 02/19] phy: Distinguish between Rx and Tx for MIPI D-PHY with submodes Paul Kocialkowski
2020-11-28 14:28 ` [PATCH v2 03/19] phy: allwinner: phy-sun6i-mipi-dphy: Support D-PHY Rx mode for MIPI CSI-2 Paul Kocialkowski
2020-12-01 10:38   ` Maxime Ripard
2020-11-28 14:28 ` [PATCH v2 04/19] media: sun6i-csi: Use common V4L2 format info for storage bpp Paul Kocialkowski
2020-12-01 10:39   ` Maxime Ripard
2020-11-28 14:28 ` [PATCH v2 05/19] media: sun6i-csi: Only configure the interface data width for parallel Paul Kocialkowski
2020-11-28 14:28 ` [PATCH v2 06/19] dt-bindings: media: sun6i-a31-csi: Add MIPI CSI-2 input port Paul Kocialkowski
2020-12-01 10:43   ` Maxime Ripard
2020-12-02 14:16     ` Paul Kocialkowski
2020-12-08 19:54       ` Rob Herring
2020-11-28 14:28 ` [PATCH v2 07/19] media: sun6i-csi: Add support for MIPI CSI-2 bridge input Paul Kocialkowski
2020-12-01 12:12   ` Maxime Ripard
2020-12-02 14:19     ` Paul Kocialkowski
2020-12-02 15:40       ` Maxime Ripard
2020-12-02 16:07         ` Paul Kocialkowski
2020-11-28 14:28 ` [PATCH v2 08/19] ARM: dts: sun8i: a83t: Add CSI controller ports Paul Kocialkowski
2020-11-28 14:28 ` [PATCH v2 09/19] ARM: dts: sunxi: h3/h5: Add CSI controller port for parallel input Paul Kocialkowski
2020-12-01 12:14   ` Maxime Ripard
2020-12-02 15:02     ` Paul Kocialkowski
2020-12-02 15:46       ` Maxime Ripard
2020-11-28 14:28 ` [PATCH v2 10/19] ARM: dts: sun8i: v3s: Add CSI1 " Paul Kocialkowski
2020-11-28 14:28 ` [PATCH v2 11/19] arm64: dts: allwinner: a64: Add CSI " Paul Kocialkowski
2020-11-28 14:28 ` [PATCH v2 12/19] dt-bindings: media: Add A31 MIPI CSI-2 bindings documentation Paul Kocialkowski
2020-12-01 12:15   ` Maxime Ripard
2020-11-28 14:28 ` [PATCH v2 13/19] media: sunxi: Add support for the A31 MIPI CSI-2 controller Paul Kocialkowski
2020-12-01 12:20   ` Maxime Ripard
2020-12-02 14:44     ` Paul Kocialkowski
2020-12-02 15:48       ` Maxime Ripard
2020-12-02 16:09         ` Paul Kocialkowski
2020-11-28 14:28 ` [PATCH v2 14/19] ARM: dts: sun8i: v3s: Add nodes for MIPI CSI-2 support Paul Kocialkowski
2020-11-28 14:28 ` [PATCH v2 15/19] MAINTAINERS: Add entry for the Allwinner A31 MIPI CSI-2 bridge Paul Kocialkowski
2020-11-28 14:28 ` [PATCH v2 16/19] dt-bindings: media: Add A83T MIPI CSI-2 bindings documentation Paul Kocialkowski
2020-12-11  3:12   ` Rob Herring
2020-11-28 14:28 ` [PATCH v2 17/19] media: sunxi: Add support for the A83T MIPI CSI-2 controller Paul Kocialkowski
2020-11-28 14:28 ` [PATCH v2 18/19] ARM: dts: sun8i: a83t: Add MIPI CSI-2 controller node Paul Kocialkowski
2020-11-28 14:28 ` [PATCH v2 19/19] MAINTAINERS: Add entry for the Allwinner A83T MIPI CSI-2 bridge Paul Kocialkowski

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).