* [PATCH v13 0/7] Add Toshiba Visconti Video Input Interface driver
@ 2025-10-16 2:24 Yuji Ishikawa
2025-10-16 2:24 ` [PATCH v13 1/7] dt-bindings: media: platform: visconti: Add Toshiba Visconti MIPI CSI-2 Receiver Yuji Ishikawa
` (5 more replies)
0 siblings, 6 replies; 15+ messages in thread
From: Yuji Ishikawa @ 2025-10-16 2:24 UTC (permalink / raw)
To: Nobuhiro Iwamatsu, Yuji Ishikawa, Mauro Carvalho Chehab,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Philipp Zabel
Cc: linux-media, devicetree, linux-arm-kernel, linux-kernel,
Laurent Pinchart
This series is the Video Input Interface driver
for Toshiba's ARM SoC, Visconti.
This provides DT binding documentation,
device driver, documentation and MAINTAINER files.
A visconti VIIF driver instance exposes
1 media control device file, 3 video device files for capture
and 2 video device files for controlling image signal processor.
Detailed HW/SW are described in documentation directory.
The VIIF hardware has CSI2 receiver,
image signal processor and video DMAC.
The device driver depends on two other drivers under development;
clock framework driver and IOMMU driver.
Corresponding features will be added later.
Best regards,
Yuji
Changelog v2:
- Resend v1 because a patch exceeds size limit.
Changelog v3:
- Add documentation to describe SW and HW
- Adapted to media control framework
- Introduced ISP subdevice, capture device
- Remove private IOCTLs and add vendor specific V4L2 controls
- Change function name avoiding camelcase and uppercase letters
Changelog v4:
- Split patches because a patch exceeds size limit
- fix dt-bindings document
- stop specifying ID numbers for driver instance explicitly at device tree
- use pm_runtime to trigger initialization of HW
along with open/close of device files.
- add a entry for a header file at MAINTAINERS file
Changelog v5:
- Fix coding style problem in viif.c (patch 2/6)
Changelog v6:
- add register definition of BUS-IF and MPU in dt-bindings
- add CSI2RX subdevice (separated from ISP subdevice)
- change directory layout (moved to media/platform/toshiba/visconti)
- change source file layout (removed hwd_xxxx.c)
- pointer to userland memory is removed from uAPI parameters
- change register access (from struct style to macro style)
- remove unused macros
Changelog v7:
- remove redundant "bindings" from header and description text
- fix multiline text of "description"
- change "compatible" to "visconti5-viif"
- explicitly define allowed properties for port::endpoint
- remove unused variables
- update kerneldoc comments
- update references to headers
Changelog v8:
- rename bindings description file
- remove/simplify items in bindings
- update operations around v4l2_async_notifier
- use v4l2_async_connection instead of v4l2_async_subdev
- use dev_err_probe()
- better error handling at probe
- remove redundant mutex
- add V4L2_CTRL_TYPE_VISCONTI_ISP constant
Changelog v9:
- dictionary ordering of dt-bindings properties
- applied sparse checker
- call div64_u64 for 64bit division
- rebase to media_staging tree
- fix warning for cast between ptr and dma_addr_t
Changelog v10:
- add an independent entry in MAINTAINERS
- add paddings to uAPI structs
- use parameter buffer to control ISP (instead of vendor specific controls)
Changelog v11:
- stop merging sensor's controls and capture device's
- fix strange indents at initializations
- remove feature VB2_USERPTR from viif_params and viif_stats
- fix usage in the document
Changelog v12:
- Separated CSI2RX driver and made it independent driver
- Add a bindings for CSI2RX driver
- Add description of parameter/statistics interface to v4l2-ioctl.c
- use PM_RUNTIME_OPS macro for power management routines
- use v4l2_subdev_enable_streams() to start streaming
- implement callback enable_streams and disable_streams,
instead of s_stream
- add spinlocks for variables shared among interrupt handlers
- use guard(spinlock)(locked_variable) macros
- call pm_runtime APIs at start/stop streaming,
instead of file handle callbacks
- add new "resizer" subdevice between ISP and Capture devices.
- update capability of sub path capture: capture only RAW8 or RAW16
- document: add description of CSI2RX driver
- document: add description of resizer subdevice
- document: add block diagrams of VIIF and ISP
- document: update usage of the driver
Changelog v13:
- Link to v12: https://lore.kernel.org/all/20241125092146.1561901-1-yuji2.ishikawa@toshiba.co.jp/
- working tree: https://git.linuxtv.org/media_stage.git
- base-commit(v12): 6390834c6f9b2c5e33f52f34579efa0d0df073db
- base-commit(v13): 3a8660878839faadb4f1a6dd72c3179c1df56787
- corresponding update of clock framework driver is discussed in:
- https://lore.kernel.org/all/20251016013328.303611-1-yuji2.ishikawa@toshiba.co.jp/
- remove resizer subdevice
- rebase to Linux 6.18-rc1
- wrap one line at 80 characters
- change banner comment style
- update comment style; spacing at the start and end, capitalize first letter
- add support for clock and reset framework. The clock driver will be updated
by another submission.
- add debugfs to pass debug and status information
- add callback for ioctl(VIDIOC_ENUM_FRAMESIZES)
- MAINTAINERS: update email address of Nobuhiro Iwamatsu
- csi2: change compatible string
- viif: remove CROP and COMPOSE API from ISP subdevice, add struct
viif_l2_crop_config instead.
- viif: correct teardown sequence at error of probe() and remove()
- document: VIIF specific keyword "AG" stands for "algorithm gain" instead of
"analog gain"
- document: update illustration and description on preprocess part
Yuji Ishikawa (8):
dt-bindings: media: platform: visconti: Add Toshiba Visconti MIPI
CSI-2 Receiver
dt-bindings: media: platform: visconti: Add Toshiba Visconti Video
Input Interface
media: uapi: add visconti viif meta buffer format
media: platform: visconti: Add Toshiba Visconti CSI-2 Receiver driver
media: platform: visconti: Add Toshiba Visconti Video Input Interface
driver
media: platform: visconti: Add streaming interface for ISP parameters
and status
documentation: media: add documentation for Toshiba Visconti Video
Input Interface driver
MAINTAINERS: Add entries for Toshiba Visconti Video Input Interface
.../admin-guide/media/v4l-drivers.rst | 1 +
.../admin-guide/media/visconti-viif.dot | 22 +
.../admin-guide/media/visconti-viif.rst | 435 ++++
.../media/toshiba,visconti5-csi2rx.yaml | 104 +
.../media/toshiba,visconti5-viif.yaml | 95 +
.../userspace-api/media/v4l/meta-formats.rst | 1 +
.../media/v4l/metafmt-visconti-viif.rst | 48 +
MAINTAINERS | 12 +
drivers/media/platform/Kconfig | 1 +
drivers/media/platform/Makefile | 1 +
drivers/media/platform/toshiba/Kconfig | 6 +
drivers/media/platform/toshiba/Makefile | 2 +
.../media/platform/toshiba/visconti/Kconfig | 34 +
.../media/platform/toshiba/visconti/Makefile | 10 +
.../platform/toshiba/visconti/csi2rx_drv.c | 791 +++++++
.../media/platform/toshiba/visconti/viif.c | 598 +++++
.../media/platform/toshiba/visconti/viif.h | 379 +++
.../platform/toshiba/visconti/viif_capture.c | 1285 +++++++++++
.../platform/toshiba/visconti/viif_capture.h | 21 +
.../platform/toshiba/visconti/viif_common.c | 239 ++
.../platform/toshiba/visconti/viif_common.h | 45 +
.../platform/toshiba/visconti/viif_isp.c | 911 ++++++++
.../platform/toshiba/visconti/viif_isp.h | 19 +
.../platform/toshiba/visconti/viif_params.c | 2034 +++++++++++++++++
.../platform/toshiba/visconti/viif_params.h | 24 +
.../platform/toshiba/visconti/viif_regs.h | 717 ++++++
.../platform/toshiba/visconti/viif_resizer.c | 491 ++++
.../platform/toshiba/visconti/viif_resizer.h | 18 +
.../platform/toshiba/visconti/viif_stats.c | 301 +++
.../platform/toshiba/visconti/viif_stats.h | 14 +
drivers/media/v4l2-core/v4l2-ioctl.c | 2 +
include/uapi/linux/videodev2.h | 4 +
include/uapi/linux/visconti_viif.h | 1921 ++++++++++++++++
33 files changed, 10586 insertions(+)
create mode 100644 Documentation/admin-guide/media/visconti-viif.dot
create mode 100644 Documentation/admin-guide/media/visconti-viif.rst
create mode 100644 Documentation/devicetree/bindings/media/toshiba,visconti5-csi2rx.yaml
create mode 100644 Documentation/devicetree/bindings/media/toshiba,visconti5-viif.yaml
create mode 100644 Documentation/userspace-api/media/v4l/metafmt-visconti-viif.rst
create mode 100644 drivers/media/platform/toshiba/Kconfig
create mode 100644 drivers/media/platform/toshiba/Makefile
create mode 100644 drivers/media/platform/toshiba/visconti/Kconfig
create mode 100644 drivers/media/platform/toshiba/visconti/Makefile
create mode 100644 drivers/media/platform/toshiba/visconti/csi2rx_drv.c
create mode 100644 drivers/media/platform/toshiba/visconti/viif.c
create mode 100644 drivers/media/platform/toshiba/visconti/viif.h
create mode 100644 drivers/media/platform/toshiba/visconti/viif_capture.c
create mode 100644 drivers/media/platform/toshiba/visconti/viif_capture.h
create mode 100644 drivers/media/platform/toshiba/visconti/viif_common.c
create mode 100644 drivers/media/platform/toshiba/visconti/viif_common.h
create mode 100644 drivers/media/platform/toshiba/visconti/viif_isp.c
create mode 100644 drivers/media/platform/toshiba/visconti/viif_isp.h
create mode 100644 drivers/media/platform/toshiba/visconti/viif_params.c
create mode 100644 drivers/media/platform/toshiba/visconti/viif_params.h
create mode 100644 drivers/media/platform/toshiba/visconti/viif_regs.h
create mode 100644 drivers/media/platform/toshiba/visconti/viif_resizer.c
create mode 100644 drivers/media/platform/toshiba/visconti/viif_resizer.h
create mode 100644 drivers/media/platform/toshiba/visconti/viif_stats.c
create mode 100644 drivers/media/platform/toshiba/visconti/viif_stats.h
create mode 100644 include/uapi/linux/visconti_viif.h
--
2.25.1
---
Yuji Ishikawa (7):
dt-bindings: media: platform: visconti: Add Toshiba Visconti MIPI CSI-2 Receiver
dt-bindings: media: platform: visconti: Add Toshiba Visconti Video Input Interface
media: uapi: Add visconti viif meta buffer formats
media: platform: visconti: Add Toshiba Visconti CSI-2 Receiver driver
media: platform: visconti: Add Toshiba Visconti Video Input Interface driver
media: platform: visconti: Add streaming interface for ISP parameters and statistics
documentation: media: Add documentation for Toshiba Visconti Video Input Interface driver
Documentation/admin-guide/media/v4l-drivers.rst | 1 +
Documentation/admin-guide/media/visconti-viif.dot | 18 +
Documentation/admin-guide/media/visconti-viif.rst | 540 +++++
.../bindings/media/toshiba,visconti5-csi2.yaml | 125 ++
.../bindings/media/toshiba,visconti5-viif.yaml | 110 +
.../userspace-api/media/v4l/meta-formats.rst | 1 +
.../media/v4l/metafmt-visconti-viif.rst | 48 +
MAINTAINERS | 12 +
drivers/media/platform/Kconfig | 1 +
drivers/media/platform/Makefile | 1 +
drivers/media/platform/toshiba/Kconfig | 6 +
drivers/media/platform/toshiba/Makefile | 3 +
drivers/media/platform/toshiba/visconti/Kconfig | 35 +
drivers/media/platform/toshiba/visconti/Makefile | 10 +
.../media/platform/toshiba/visconti/csi2rx_drv.c | 954 +++++++++
drivers/media/platform/toshiba/visconti/viif.c | 710 ++++++
drivers/media/platform/toshiba/visconti/viif.h | 391 ++++
.../media/platform/toshiba/visconti/viif_capture.c | 1470 +++++++++++++
.../media/platform/toshiba/visconti/viif_capture.h | 24 +
.../media/platform/toshiba/visconti/viif_common.c | 250 +++
.../media/platform/toshiba/visconti/viif_common.h | 47 +
drivers/media/platform/toshiba/visconti/viif_isp.c | 981 +++++++++
drivers/media/platform/toshiba/visconti/viif_isp.h | 20 +
.../media/platform/toshiba/visconti/viif_params.c | 2257 ++++++++++++++++++++
.../media/platform/toshiba/visconti/viif_params.h | 20 +
.../media/platform/toshiba/visconti/viif_regs.h | 726 +++++++
.../media/platform/toshiba/visconti/viif_stats.c | 320 +++
.../media/platform/toshiba/visconti/viif_stats.h | 16 +
drivers/media/v4l2-core/v4l2-ioctl.c | 2 +
include/uapi/linux/videodev2.h | 4 +
include/uapi/linux/visconti_viif.h | 1911 +++++++++++++++++
31 files changed, 11014 insertions(+)
---
base-commit: 3a8660878839faadb4f1a6dd72c3179c1df56787
change-id: 20250925-visconti-viif-f9014920a58a
prerequisite-message-id: <20251016013328.303611-1-yuji2.ishikawa@toshiba.co.jp>
prerequisite-patch-id: 73a37b4200a5a30406de3bc8eb79af986ff9592d
prerequisite-patch-id: 78e9d304c8aeee089bc3381505724b466f01ae47
Best regards,
--
Yuji Ishikawa <yuji2.ishikawa@toshiba.co.jp>
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v13 1/7] dt-bindings: media: platform: visconti: Add Toshiba Visconti MIPI CSI-2 Receiver
2025-10-16 2:24 [PATCH v13 0/7] Add Toshiba Visconti Video Input Interface driver Yuji Ishikawa
@ 2025-10-16 2:24 ` Yuji Ishikawa
2025-10-16 4:34 ` Frank Li
2025-10-16 6:38 ` Krzysztof Kozlowski
2025-10-16 2:24 ` [PATCH v13 2/7] dt-bindings: media: platform: visconti: Add Toshiba Visconti Video Input Interface Yuji Ishikawa
` (4 subsequent siblings)
5 siblings, 2 replies; 15+ messages in thread
From: Yuji Ishikawa @ 2025-10-16 2:24 UTC (permalink / raw)
To: Nobuhiro Iwamatsu, Yuji Ishikawa, Mauro Carvalho Chehab,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Philipp Zabel
Cc: linux-media, devicetree, linux-arm-kernel, linux-kernel
Adds the Device Tree binding documentation that allows to describe
the MIPI CSI-2 Receiver found in Toshiba Visconti SoCs.
Signed-off-by: Yuji Ishikawa <yuji2.ishikawa@toshiba.co.jp>
---
Changelog v12:
- Newly add bindings for CSI2RX driver
Changelog v13:
- add entries to MAINTAINERS file.
- update email address of Nobuhiro Iwamatsu in /maintainers
- add Yuji Ishikawa to /maintainers
- change /properties/compatible: toshiba,visconti5-csi2rx -> toshiba,visconti5-csi2
- change bindings file name: toshiba,visconti5-csi2rx -> toshiba,visconti5-csi2
- change node name in sample DTS: csi2rx -> csi
- remove "|-" from /description
- update /description
- add definitions of clock and reset
- update /properties/ports/properties/port@0/description for better comment
- update /properties/ports/properties/port@0/$ref to specify full pathname
- remove /properties/ports/properties/port@0/properties/endpoint/properties/data-lanes/description because the default text provides enough information.
- update sample dts
---
.../bindings/media/toshiba,visconti5-csi2.yaml | 125 +++++++++++++++++++++
MAINTAINERS | 7 ++
2 files changed, 132 insertions(+)
diff --git a/Documentation/devicetree/bindings/media/toshiba,visconti5-csi2.yaml b/Documentation/devicetree/bindings/media/toshiba,visconti5-csi2.yaml
new file mode 100644
index 000000000000..21fb46de5b6e
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/toshiba,visconti5-csi2.yaml
@@ -0,0 +1,125 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/toshiba,visconti5-csi2.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Toshiba Visconti5 SoC MIPI CSI-2 receiver
+
+maintainers:
+ - Nobuhiro Iwamatsu <nobuhiro.iwamatsu.x90@mail.toshiba>
+ - Yuji Ishikawa <yuji2.ishikawa@toshiba.co.jp>
+
+description:
+ Toshiba Visconti5 SoC MIPI CSI-2 receiver device receives MIPI CSI-2 video
+ stream. The obtained video stream is used as input for the Visconti5 VIIF.
+
+properties:
+ compatible:
+ const: toshiba,visconti5-csi2
+
+ reg:
+ items:
+ - description: Registers for CSI2 receiver control
+
+ interrupts:
+ items:
+ - description: CSI2 Receiver Interrupt
+
+ clocks:
+ items:
+ - description: MIPI DPHY configuration clock
+ - description: Register access clock
+
+ clock-names:
+ items:
+ - const: cfg
+ - const: apb
+
+ resets:
+ maxItems: 1
+
+ ports:
+ $ref: /schemas/graph.yaml#/properties/ports
+
+ properties:
+ port@0:
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ unevaluatedProperties: false
+ description:
+ Input port node, with an endpoint pointing to the image sensor.
+
+ properties:
+ endpoint:
+ $ref: /schemas/media/video-interfaces.yaml#
+ unevaluatedProperties: false
+
+ properties:
+ data-lanes:
+ minItems: 1
+ items:
+ - const: 1
+ - const: 2
+ - const: 3
+ - const: 4
+ required:
+ - data-lanes
+
+ port@1:
+ $ref: /schemas/graph.yaml#/properties/port
+ description:
+ Output port node, with an endpoint pointing to the Visconti VIIF.
+
+ required:
+ - port@0
+ - port@1
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+ - clock-names
+ - resets
+ - ports
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/toshiba,tmpv770x.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ soc {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ csi0: csi@1c008000 {
+ compatible = "toshiba,visconti5-csi2";
+ reg = <0 0x1c008000 0 0x400>;
+ interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&pismu TMPV770X_CLK_VIIFBS0_CFG>,
+ <&pismu TMPV770X_CLK_VIIFBS0_APB>;
+ clock-names = "cfg", "apb";
+ resets = <&pismu TMPV770X_RESET_VIIFBS0_APB>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ port@0 {
+ reg = <0>;
+ csi0_in: endpoint {
+ data-lanes = <1 2>;
+ remote-endpoint = <&imx219_out>;
+ };
+ };
+ port@1 {
+ reg = <1>;
+ csi0_out: endpoint {
+ remote-endpoint = <&video0_in>;
+ };
+ };
+ };
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index 46126ce2f968..e4634a0aad74 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -25979,6 +25979,13 @@ F: Documentation/devicetree/bindings/media/i2c/toshiba,tc358743.txt
F: drivers/media/i2c/tc358743*
F: include/media/i2c/tc358743.h
+TOSHIBA VISCONTI VIIF DRIVER
+M: Nobuhiro Iwamatsu <nobuhiro.iwamatsu.x90@mail.toshiba>
+M: Yuji Ishikawa <yuji2.ishikawa@toshiba.co.jp>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/media/toshiba,visconti5-csi2.yaml
+
TOSHIBA WMI HOTKEYS DRIVER
M: Azael Avalos <coproscefalo@gmail.com>
L: platform-driver-x86@vger.kernel.org
--
2.34.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v13 2/7] dt-bindings: media: platform: visconti: Add Toshiba Visconti Video Input Interface
2025-10-16 2:24 [PATCH v13 0/7] Add Toshiba Visconti Video Input Interface driver Yuji Ishikawa
2025-10-16 2:24 ` [PATCH v13 1/7] dt-bindings: media: platform: visconti: Add Toshiba Visconti MIPI CSI-2 Receiver Yuji Ishikawa
@ 2025-10-16 2:24 ` Yuji Ishikawa
2025-10-16 6:39 ` Krzysztof Kozlowski
2025-10-16 2:24 ` [PATCH v13 3/7] media: uapi: Add visconti viif meta buffer formats Yuji Ishikawa
` (3 subsequent siblings)
5 siblings, 1 reply; 15+ messages in thread
From: Yuji Ishikawa @ 2025-10-16 2:24 UTC (permalink / raw)
To: Nobuhiro Iwamatsu, Yuji Ishikawa, Mauro Carvalho Chehab,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Philipp Zabel
Cc: linux-media, devicetree, linux-arm-kernel, linux-kernel
Adds the Device Tree binding documentation that allows to describe
the Video Input Interface found in Toshiba Visconti SoCs.
Signed-off-by: Yuji Ishikawa <yuji2.ishikawa@toshiba.co.jp>
---
Changelog v2:
- no change
Changelog v3:
- no change
Changelog v4:
- fix style problems at the v3 patch
- remove "index" member
- update example
Changelog v5:
- no change
Changelog v6:
- add register definition of BUS-IF and MPU
Changelog v7:
- remove trailing "bindings" from commit header message
- remove trailing "Device Tree Bindings" from title
- fix text wrapping of description
- change compatible to visconti5-viif
- explicitly define allowed properties for port::endpoint
Changelog v8:
- Suggestion from Krzysztof Kozlowski
- rename bindings description file
- use block style array instead of inline style
- remove clock-lane (as it is fixed at position 0)
- update sample node's name
- use lowercase hex for literals
- Suggestion from Laurent Pinchart
- update description message port::description
- remove port::endpoint::bus-type as it is fixed to <4>
- remove port::endpoint::clock-lanes from example
- add port::endpoint::data-lanes to required parameters list
- fix sequence of data-lanes: <1 2 3 4> because current driver does not support data reordering
- update port::endpoint::data-lanes::description
- remove redundant type definition for port::endpoint::data-lanes
Changelog v9:
- place "required" after "properties"
- dictionary ordering of properties
Changelog v10:
- no change
Changelog v11:
- no change
Changelog v12:
- remove property "clock-noncontinuous" as VIIF switches both modes automatically
- remove property "link-frequencies" as VIIF does not use the information
- remove reg[2] and interrupts[3] which are used for CSI2RX driver
- update example to refer csi2rx for remote-endpoint
Changelog v13:
- add entries to MAINTAINERS flle
- update email address of Nobuhiro Iwamatsu in /maintainers
- add Yuji Ishikawa to /maintainers
- remove "|-" from /description
- add definitions of clock and reset
- update /port/$ref to point port instead of port-base
- update /port/description
- remove CSI2 receiver specific definition from /port/properties/endpoint/properties because CSI2 is handled by the CSI2 driver.
- update sample dts
---
.../bindings/media/toshiba,visconti5-viif.yaml | 110 +++++++++++++++++++++
MAINTAINERS | 1 +
2 files changed, 111 insertions(+)
diff --git a/Documentation/devicetree/bindings/media/toshiba,visconti5-viif.yaml b/Documentation/devicetree/bindings/media/toshiba,visconti5-viif.yaml
new file mode 100644
index 000000000000..92ffe844cdde
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/toshiba,visconti5-viif.yaml
@@ -0,0 +1,110 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/toshiba,visconti5-viif.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Toshiba Visconti5 SoC Video Input Interface
+
+maintainers:
+ - Nobuhiro Iwamatsu <nobuhiro.iwamatsu.x90@mail.toshiba>
+ - Yuji Ishikawa <yuji2.ishikawa@toshiba.co.jp>
+
+description:
+ Toshiba Visconti5 SoC Video Input Interface (VIIF) receives videostream
+ from MIPI CSI-2 receiver device, processes the stream with image signal
+ processors (L1ISP, L2ISP), then stores pictures to main memory.
+
+properties:
+ compatible:
+ const: toshiba,visconti5-viif
+
+ reg:
+ items:
+ - description: Registers for capture control
+ - description: Registers for bus interface unit control
+ - description: Registers for Memory Protection Unit
+
+ interrupts:
+ items:
+ - description: Sync Interrupt
+ - description: Status (Error) Interrupt
+ - description: L1ISP Interrupt
+
+ clocks:
+ items:
+ - description: Common clock
+ - description: Interface clock
+ - description: L1ISP clock
+ - description: L2ISP clock
+
+ clock-names:
+ items:
+ - const: bsproc
+ - const: proc
+ - const: l1isp
+ - const: l2isp
+
+ resets:
+ items:
+ - description: Interface reset
+ - description: L1ISP reset
+ - description: L2ISP reset
+
+ reset-names:
+ items:
+ - const: core
+ - const: l1isp
+ - const: l2isp
+
+ port:
+ $ref: /schemas/graph.yaml#/properties/port
+ description:
+ Input port node with an endpoint pointing to the CSI-2 receiver.
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - port
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/toshiba,tmpv770x.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ soc {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ video0: video@1c000000 {
+ compatible = "toshiba,visconti5-viif";
+ reg = <0 0x1c000000 0 0x6000>,
+ <0 0x1c00e000 0 0x1000>,
+ <0 0x2417a000 0 0x1000>;
+ interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&pismu TMPV770X_CLK_VIIFBS0_PROC>,
+ <&pismu TMPV770X_CLK_VIIF0_PROC>,
+ <&pismu TMPV770X_CLK_VIIF0_L1ISP>,
+ <&pismu TMPV770X_CLK_VIIF0_L2ISP>;
+ clock-names = "bsproc", "proc", "l1isp", "l2isp";
+ resets = <&pismu TMPV770X_RESET_VIIFBS0>,
+ <&pismu TMPV770X_RESET_VIIFBS0_L1ISP>,
+ <&pismu TMPV770X_RESET_VIIFBS0_L2ISP>;
+ reset-names = "core", "l1isp", "l2isp";
+
+ port {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ video0_in: endpoint {
+ remote-endpoint = <&csi0_out>;
+ };
+ };
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index e4634a0aad74..c17c7ddba5af 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -25985,6 +25985,7 @@ M: Yuji Ishikawa <yuji2.ishikawa@toshiba.co.jp>
L: linux-media@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/media/toshiba,visconti5-csi2.yaml
+F: Documentation/devicetree/bindings/media/toshiba,visconti5-viif.yaml
TOSHIBA WMI HOTKEYS DRIVER
M: Azael Avalos <coproscefalo@gmail.com>
--
2.34.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v13 3/7] media: uapi: Add visconti viif meta buffer formats
2025-10-16 2:24 [PATCH v13 0/7] Add Toshiba Visconti Video Input Interface driver Yuji Ishikawa
2025-10-16 2:24 ` [PATCH v13 1/7] dt-bindings: media: platform: visconti: Add Toshiba Visconti MIPI CSI-2 Receiver Yuji Ishikawa
2025-10-16 2:24 ` [PATCH v13 2/7] dt-bindings: media: platform: visconti: Add Toshiba Visconti Video Input Interface Yuji Ishikawa
@ 2025-10-16 2:24 ` Yuji Ishikawa
2025-10-16 2:24 ` [PATCH v13 4/7] media: platform: visconti: Add Toshiba Visconti CSI-2 Receiver driver Yuji Ishikawa
` (2 subsequent siblings)
5 siblings, 0 replies; 15+ messages in thread
From: Yuji Ishikawa @ 2025-10-16 2:24 UTC (permalink / raw)
To: Nobuhiro Iwamatsu, Yuji Ishikawa, Mauro Carvalho Chehab,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Philipp Zabel
Cc: linux-media, devicetree, linux-arm-kernel, linux-kernel,
Laurent Pinchart
Add the Toshiba Visconti VIIF specific metadata formats.
- V4L2_META_FMT_VISCONTI_VIIF_PARAMS for ISP parameters
- V4L2_META_FMT_VISCONTI_VIIF_STATS for ISP statistics
Signed-off-by: Yuji Ishikawa <yuji2.ishikawa@toshiba.co.jp>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
Changelog v10:
- add entry for V4L2_META_FMT_VISCONTI_VIIF_PARAMS
- add entry for V4L2_META_FMT_VISCONTI_VIIF_STATS
Changelog v11:
- no change
Changelog v12:
- add description for meta formats at v4l2-ioctl.c
Changelog v13:
- no change
- Capitalize the first letter of the commit message header.
---
drivers/media/v4l2-core/v4l2-ioctl.c | 2 ++
include/uapi/linux/videodev2.h | 4 ++++
2 files changed, 6 insertions(+)
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index 01cf52c3ea33..cfc9a8ab66cb 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -1479,6 +1479,8 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
case V4L2_META_FMT_RPI_BE_CFG: descr = "RPi PiSP BE Config format"; break;
case V4L2_META_FMT_RPI_FE_CFG: descr = "RPi PiSP FE Config format"; break;
case V4L2_META_FMT_RPI_FE_STATS: descr = "RPi PiSP FE Statistics format"; break;
+ case V4L2_META_FMT_VISCONTI_VIIF_PARAMS: descr = "Visconti ISP Parameters"; break;
+ case V4L2_META_FMT_VISCONTI_VIIF_STATS: descr = "Visconti ISP Statistics"; break;
case V4L2_META_FMT_GENERIC_8: descr = "8-bit Generic Metadata"; break;
case V4L2_META_FMT_GENERIC_CSI2_10: descr = "8-bit Generic Meta, 10b CSI-2"; break;
case V4L2_META_FMT_GENERIC_CSI2_12: descr = "8-bit Generic Meta, 12b CSI-2"; break;
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index becd08fdbddb..980ce7d2da1a 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -884,6 +884,10 @@ struct v4l2_pix_format {
#define V4L2_META_FMT_RPI_FE_CFG v4l2_fourcc('R', 'P', 'F', 'C') /* PiSP FE configuration */
#define V4L2_META_FMT_RPI_FE_STATS v4l2_fourcc('R', 'P', 'F', 'S') /* PiSP FE stats */
+/* Vendor specific - used for Visconti VIIF sub-system */
+#define V4L2_META_FMT_VISCONTI_VIIF_PARAMS v4l2_fourcc('V', 'I', 'F', 'P') /* ISP Params */
+#define V4L2_META_FMT_VISCONTI_VIIF_STATS v4l2_fourcc('V', 'I', 'F', 'S') /* ISP Stats */
+
#ifdef __KERNEL__
/*
* Line-based metadata formats. Remember to update v4l_fill_fmtdesc() when
--
2.34.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v13 4/7] media: platform: visconti: Add Toshiba Visconti CSI-2 Receiver driver
2025-10-16 2:24 [PATCH v13 0/7] Add Toshiba Visconti Video Input Interface driver Yuji Ishikawa
` (2 preceding siblings ...)
2025-10-16 2:24 ` [PATCH v13 3/7] media: uapi: Add visconti viif meta buffer formats Yuji Ishikawa
@ 2025-10-16 2:24 ` Yuji Ishikawa
2025-10-16 4:45 ` Frank Li
2025-10-16 2:24 ` [PATCH v13 6/7] media: platform: visconti: Add streaming interface for ISP parameters and statistics Yuji Ishikawa
2025-10-16 2:24 ` [PATCH v13 7/7] documentation: media: Add documentation for Toshiba Visconti Video Input Interface driver Yuji Ishikawa
5 siblings, 1 reply; 15+ messages in thread
From: Yuji Ishikawa @ 2025-10-16 2:24 UTC (permalink / raw)
To: Nobuhiro Iwamatsu, Yuji Ishikawa, Mauro Carvalho Chehab,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Philipp Zabel
Cc: linux-media, devicetree, linux-arm-kernel, linux-kernel
Add support to MIPI CSI-2 Receiver on Toshiba Visconti ARM SoCs.
This driver is used with Visconti Video Input Interface driver.
Signed-off-by: Yuji Ishikawa <yuji2.ishikawa@toshiba.co.jp>
---
Changelog v12:
- Separate CSI2RX driver and made it independent driver
- viif_csi2rx subdevice driver (in v11 patch) was removed.
- dictionary order at Kconfig and Makefile
Changelog v13:
- wrap one line at 80 characters
- change banner comment style
- update comment style; spacing at the start and end, capitalize first letter
- add support for clock and reset framework
- add debugfs to pass debug and status information
- add entries to MAINTAINERS file
- Kconfig: add a blank line just after License Identifier.
- update references to header files
- remove redundant inline qualifier
- shorten function/variable names: visconti_csi2rx -> viscsi2
- simplify dphy_write and dphy read operations
- remove osc_freq_target from struct csi2rx_dphy_hs_info, which is always the same value.
- add comment about MASK register's behavior (reversed polarity)
- use v4l2_get_link_freq() instead of get_pixelclock()
- set driver name according to module name: visconti_csi2rx_dev -> visconti-csi2rx
- check error before setting priv->irq in probe()
- check error at fmt_for_mbus_code()
- add callback for ioctl(VIDIOC_ENUM_FRAMESIZES)
- improve viscsi2_parse_dt() by assuming bus_type is CSI2_DPHY
- use dev_err_ratelimited() for irq handler
- bugfix on fmt_for_mbus_code(): in case unsupported mbus_code is given
- add goto based error handling sequence to viscsi2_parse_dt()
- specify default value of colorspace, ycbcr_enc, quantization and xfer_func of sink/src_fmt
- specify sensor at enable_streams() using previously set ID, instead of checking remote pad every time
- remove U suffix on numeric value
- use unsigned int variable for loop index
- remove redundant casting
- use GENMASK instead of literal
- remove unused constants
- remove unused visconti_csi2rx_video_ops
---
MAINTAINERS | 1 +
drivers/media/platform/Kconfig | 1 +
drivers/media/platform/Makefile | 1 +
drivers/media/platform/toshiba/Kconfig | 6 +
drivers/media/platform/toshiba/Makefile | 3 +
drivers/media/platform/toshiba/visconti/Kconfig | 17 +
drivers/media/platform/toshiba/visconti/Makefile | 8 +
.../media/platform/toshiba/visconti/csi2rx_drv.c | 954 +++++++++++++++++++++
8 files changed, 991 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index c17c7ddba5af..ce973791b367 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -25986,6 +25986,7 @@ L: linux-media@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/media/toshiba,visconti5-csi2.yaml
F: Documentation/devicetree/bindings/media/toshiba,visconti5-viif.yaml
+F: drivers/media/platform/toshiba/visconti/
TOSHIBA WMI HOTKEYS DRIVER
M: Azael Avalos <coproscefalo@gmail.com>
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 9287faafdce5..d5265aa16c88 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -87,6 +87,7 @@ source "drivers/media/platform/st/Kconfig"
source "drivers/media/platform/sunxi/Kconfig"
source "drivers/media/platform/synopsys/Kconfig"
source "drivers/media/platform/ti/Kconfig"
+source "drivers/media/platform/toshiba/Kconfig"
source "drivers/media/platform/verisilicon/Kconfig"
source "drivers/media/platform/via/Kconfig"
source "drivers/media/platform/xilinx/Kconfig"
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 6fd7db0541c7..09e67ecb9559 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -30,6 +30,7 @@ obj-y += st/
obj-y += sunxi/
obj-y += synopsys/
obj-y += ti/
+obj-y += toshiba/
obj-y += verisilicon/
obj-y += via/
obj-y += xilinx/
diff --git a/drivers/media/platform/toshiba/Kconfig b/drivers/media/platform/toshiba/Kconfig
new file mode 100644
index 000000000000..f02983f4fc97
--- /dev/null
+++ b/drivers/media/platform/toshiba/Kconfig
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+comment "Toshiba media platform drivers"
+
+source "drivers/media/platform/toshiba/visconti/Kconfig"
+
diff --git a/drivers/media/platform/toshiba/Makefile b/drivers/media/platform/toshiba/Makefile
new file mode 100644
index 000000000000..dd89a9a35704
--- /dev/null
+++ b/drivers/media/platform/toshiba/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-y += visconti/
diff --git a/drivers/media/platform/toshiba/visconti/Kconfig b/drivers/media/platform/toshiba/visconti/Kconfig
new file mode 100644
index 000000000000..aa0b63f9f008
--- /dev/null
+++ b/drivers/media/platform/toshiba/visconti/Kconfig
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config VIDEO_VISCONTI_CSI2RX
+ tristate "Visconti MIPI CSI-2 Receiver driver"
+ depends on V4L_PLATFORM_DRIVERS
+ depends on VIDEO_DEV && OF
+ depends on ARCH_VISCONTI || COMPILE_TEST
+ select MEDIA_CONTROLLER
+ select VIDEO_V4L2_SUBDEV_API
+ select V4L2_FWNODE
+ help
+ Support for Toshiba Visconti MIPI CSI-2 receiver,
+ which is used with Visconti Camera Interface driver.
+
+ This driver yields 1 subdevice node for a hardware instance.
+ To compile this driver as a module, choose M here: the
+ module will be called visconti-csi2rx.
diff --git a/drivers/media/platform/toshiba/visconti/Makefile b/drivers/media/platform/toshiba/visconti/Makefile
new file mode 100644
index 000000000000..62a029376134
--- /dev/null
+++ b/drivers/media/platform/toshiba/visconti/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the Visconti video input device driver
+#
+
+visconti-csi2rx-objs = csi2rx_drv.o
+
+obj-$(CONFIG_VIDEO_VISCONTI_CSI2RX) += visconti-csi2rx.o
diff --git a/drivers/media/platform/toshiba/visconti/csi2rx_drv.c b/drivers/media/platform/toshiba/visconti/csi2rx_drv.c
new file mode 100644
index 000000000000..53d112432a86
--- /dev/null
+++ b/drivers/media/platform/toshiba/visconti/csi2rx_drv.c
@@ -0,0 +1,954 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * Toshiba Visconti Video Capture Support
+ *
+ * (C) Copyright 2025 TOSHIBA CORPORATION
+ * (C) Copyright 2025 Toshiba Electronic Devices & Storage Corporation
+ */
+
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/reset.h>
+#include <linux/seq_file.h>
+
+#include <media/mipi-csi2.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+/* CSI2HOST register space */
+#define REG_CSI2RX_NLANES 0x4
+#define REG_CSI2RX_RESETN 0x8
+#define REG_CSI2RX_INT_ST_MAIN 0xc
+#define REG_CSI2RX_DATA_IDS_1 0x10
+#define REG_CSI2RX_DATA_IDS_2 0x14
+#define REG_CSI2RX_PHY_SHUTDOWNZ 0x40
+#define REG_CSI2RX_PHY_RSTZ 0x44
+
+/* Access to dphy external registers */
+#define REG_CSI2RX_PHY_TESTCTRL0 0x50
+#define BIT_TESTCTRL0_CLK_0 0
+#define BIT_TESTCTRL0_CLK_1 BIT(1)
+
+#define REG_CSI2RX_PHY_TESTCTRL1 0x54
+#define BIT_TESTCTRL1_ADDR BIT(16)
+#define MASK_TESTCTRL1_DOUT GENMASK(15, 8)
+
+#define REG_CSI2RX_INT_ST_PHY_FATAL 0xe0
+#define REG_CSI2RX_INT_MSK_PHY_FATAL 0xe4
+#define MASK_PHY_FATAL_ALL 0x0000000f
+
+#define REG_CSI2RX_INT_ST_PKT_FATAL 0xf0
+#define REG_CSI2RX_INT_MSK_PKT_FATAL 0xf4
+#define MASK_PKT_FATAL_ALL 0x0001000f
+
+#define REG_CSI2RX_INT_ST_FRAME_FATAL 0x100
+#define REG_CSI2RX_INT_MSK_FRAME_FATAL 0x104
+#define MASK_FRAME_FATAL_ALL 0x000f0f0f
+
+#define REG_CSI2RX_INT_ST_PHY 0x110
+#define REG_CSI2RX_INT_MSK_PHY 0x114
+#define MASK_PHY_ERROR_ALL 0x000f000f
+
+#define REG_CSI2RX_INT_ST_PKT 0x120
+#define REG_CSI2RX_INT_MSK_PKT 0x124
+#define MASK_PKT_ERROR_ALL 0x000f000f
+
+#define REG_CSI2RX_INT_ST_LINE 0x130
+#define REG_CSI2RX_INT_MSK_LINE 0x134
+#define MASK_LINE_ERROR_ALL 0x00ff00ff
+
+/* DPHY register space */
+enum dphy_testcode {
+ DIG_TESTCODE_EXT = 0,
+ DIG_SYS_0 = 0x001,
+ DIG_SYS_1 = 0x002,
+ DIG_SYS_7 = 0x008,
+ DIG_RX_STARTUP_OVR_2 = 0x0e2,
+ DIG_RX_STARTUP_OVR_3 = 0x0e3,
+ DIG_RX_STARTUP_OVR_4 = 0x0e4,
+ DIG_RX_STARTUP_OVR_5 = 0x0e5,
+ DIG_CB_2 = 0x1ac,
+ DIG_TERM_CAL_0 = 0x220,
+ DIG_TERM_CAL_1 = 0x221,
+ DIG_TERM_CAL_2 = 0x222,
+ DIG_CLKLANE_LANE_6 = 0x307,
+ DIG_CLKLANE_OFFSET_CAL_0 = 0x39d,
+ DIG_LANE0_OFFSET_CAL_0 = 0x59f,
+ DIG_LANE0_DDL_0 = 0x5e0,
+ DIG_LANE1_OFFSET_CAL_0 = 0x79f,
+ DIG_LANE1_DDL_0 = 0x7e0,
+ DIG_LANE2_OFFSET_CAL_0 = 0x99f,
+ DIG_LANE2_DDL_0 = 0x9e0,
+ DIG_LANE3_OFFSET_CAL_0 = 0xb9f,
+ DIG_LANE3_DDL_0 = 0xbe0,
+};
+
+#define SYS_0_HSFREQRANGE_OVR BIT(5)
+#define SYS_7_RESERVED FIELD_PREP(0x1f, 0x0c)
+#define SYS_7_DESKEW_POL BIT(5)
+#define STARTUP_OVR_4_CNTVAL FIELD_PREP(0x70, 0x01)
+#define STARTUP_OVR_4_DDL_EN BIT(0)
+#define STARTUP_OVR_5_BYPASS BIT(0)
+#define CB_2_LPRX_BIAS BIT(6)
+#define CB_2_RESERVED FIELD_PREP(0x3f, 0x0b)
+#define CLKLANE_RXHS_PULL_LONG BIT(7)
+
+/* bit mask for calibration result registers */
+#define MASK_TERM_CAL_ERR 0
+#define MASK_TERM_CAL_DONE BIT(7)
+#define MASK_CLK_CAL_ERR BIT(4)
+#define MASK_CLK_CAL_DONE BIT(0)
+#define MASK_CAL_ERR BIT(2)
+#define MASK_CAL_DONE BIT(1)
+#define MASK_DDL_ERR BIT(1)
+#define MASK_DDL_DONE BIT(2)
+
+#define VISCSI2_ERROR_MONITORS_NUM 8
+
+/**
+ * struct viscsi2_line_err_target
+ *
+ * Virtual Channel and Data Type pair for CSI2RX line error monitor
+ *
+ * When 0 is set to dt, line error detection is disabled.
+ *
+ * @vc: Virtual Channel to monitor; Range 0..3
+ * @dt: Data Type to monitor; Range 0, 0x10..0x3f
+ */
+struct viscsi2_line_err_target {
+ u32 vc[VISCSI2_ERROR_MONITORS_NUM];
+ u32 dt[VISCSI2_ERROR_MONITORS_NUM];
+};
+
+#define CSI2RX_MIN_DATA_RATE 80
+#define CSI2RX_MAX_DATA_RATE 1500
+
+#define VISCSI2_PAD_SINK 0
+#define VISCSI2_PAD_SRC 1
+#define VISCSI2_PAD_NUM 2
+
+#define VISCSI2_DEF_WIDTH 1920
+#define VISCSI2_DEF_HEIGHT 1080
+#define VISCSI2_MIN_WIDTH 640
+#define VISCSI2_MAX_WIDTH 3840
+#define VISCSI2_MIN_HEIGHT 480
+#define VISCSI2_MAX_HEIGHT 2160
+
+struct viscsi2 {
+ struct device *dev;
+ void __iomem *base;
+
+ struct v4l2_subdev subdev;
+ struct media_pad pads[VISCSI2_PAD_NUM];
+ struct v4l2_async_notifier notifier;
+ struct v4l2_subdev *remote;
+ unsigned int remote_pad;
+
+ unsigned int lanes;
+
+ unsigned int irq;
+ struct clk *clk_apb;
+ struct clk *clk_cfg;
+ struct reset_control *rst;
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *debugfs_dir;
+ u32 debug_phy_fatal;
+ u32 debug_pkt_fatal;
+ u32 debug_frame_fatal;
+ u32 debug_phy;
+ u32 debug_pkt;
+ u32 debug_line;
+#endif
+ bool running;
+};
+
+static inline struct viscsi2 *notifier_to_csi2(struct v4l2_async_notifier *n)
+{
+ return container_of(n, struct viscsi2, notifier);
+}
+
+static inline struct viscsi2 *sd_to_csi2(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct viscsi2, subdev);
+}
+
+static inline void viscsi2_write(struct viscsi2 *priv, u32 regid, u32 val)
+{
+ writel(val, priv->base + regid);
+}
+
+static inline u32 viscsi2_read(struct viscsi2 *priv, u32 regid)
+{
+ return readl(priv->base + regid);
+}
+
+static void viscsi2_set_dphy_param(struct viscsi2 *priv, u32 val)
+{
+ viscsi2_write(priv, REG_CSI2RX_PHY_TESTCTRL1, val);
+ viscsi2_write(priv, REG_CSI2RX_PHY_TESTCTRL0, BIT_TESTCTRL0_CLK_1);
+ viscsi2_write(priv, REG_CSI2RX_PHY_TESTCTRL0, BIT_TESTCTRL0_CLK_0);
+}
+
+static void viscsi2_set_dphy_addr(struct viscsi2 *priv, u32 test_mode)
+{
+ /* Select testcode Ex space with top 4bits of test_mode */
+ viscsi2_set_dphy_param(priv, BIT_TESTCTRL1_ADDR | DIG_TESTCODE_EXT);
+ viscsi2_set_dphy_param(priv, FIELD_GET(0xf00, test_mode));
+ viscsi2_set_dphy_param(priv,
+ BIT_TESTCTRL1_ADDR | FIELD_GET(0xff, test_mode));
+}
+
+static void dphy_write(struct viscsi2 *priv, u32 test_mode, u8 test_in)
+{
+ viscsi2_set_dphy_addr(priv, test_mode);
+ viscsi2_set_dphy_param(priv, test_in);
+}
+
+#ifdef CONFIG_DEBUG_FS
+static u8 dphy_read(struct viscsi2 *priv, u32 test_mode)
+{
+ u32 read_data;
+
+ viscsi2_set_dphy_addr(priv, test_mode);
+ read_data = viscsi2_read(priv, REG_CSI2RX_PHY_TESTCTRL1);
+ return FIELD_GET(MASK_TESTCTRL1_DOUT, read_data);
+}
+
+static int viscsi2_read_calibration_status(struct viscsi2 *priv, u32 test_mode,
+ u32 mask_err, u32 mask_done)
+{
+ u32 read_data = (u32)dphy_read(priv, test_mode);
+
+ if (!(read_data & mask_done))
+ return -EAGAIN;
+
+ if (read_data & mask_err)
+ return -EIO;
+
+ return 0;
+}
+
+struct viscsi2_calibration_status_def {
+ const char *name;
+ u32 test_mode;
+ u32 mask_err;
+ u32 mask_done;
+};
+
+static const struct viscsi2_calibration_status_def viscsi2_caldef[] = {
+ { "term_cal_with_rext", DIG_TERM_CAL_1, MASK_TERM_CAL_ERR,
+ MASK_TERM_CAL_DONE },
+ { "clock_lane_offset_cal", DIG_CLKLANE_OFFSET_CAL_0, MASK_CLK_CAL_ERR,
+ MASK_CLK_CAL_DONE },
+ { "data_lane0_offset_cal", DIG_LANE0_OFFSET_CAL_0, MASK_CAL_ERR,
+ MASK_CAL_DONE },
+ { "data_lane1_offset_cal", DIG_LANE1_OFFSET_CAL_0, MASK_CAL_ERR,
+ MASK_CAL_DONE },
+ { "data_lane2_offset_cal", DIG_LANE2_OFFSET_CAL_0, MASK_CAL_ERR,
+ MASK_CAL_DONE },
+ { "data_lane3_offset_cal", DIG_LANE3_OFFSET_CAL_0, MASK_CAL_ERR,
+ MASK_CAL_DONE },
+ { "data_lane0_ddl_tuning_cal", DIG_LANE0_DDL_0, MASK_DDL_ERR,
+ MASK_DDL_DONE },
+ { "data_lane1_ddl_tuning_cal", DIG_LANE1_DDL_0, MASK_DDL_ERR,
+ MASK_DDL_DONE },
+ { "data_lane2_ddl_tuning_cal", DIG_LANE2_DDL_0, MASK_DDL_ERR,
+ MASK_DDL_DONE },
+ { "data_lane3_ddl_tuning_cal", DIG_LANE3_DDL_0, MASK_DDL_ERR,
+ MASK_DDL_DONE },
+};
+
+static int viscsi2_debug_calibration_status_show(struct seq_file *m, void *p)
+{
+ struct viscsi2 *priv = m->private;
+ unsigned int i;
+
+ if (!priv->running)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(viscsi2_caldef); i++) {
+ const struct viscsi2_calibration_status_def *cd =
+ &viscsi2_caldef[i];
+
+ seq_printf(m, "%s: %d\n", cd->name,
+ viscsi2_read_calibration_status(priv, cd->test_mode,
+ cd->mask_err,
+ cd->mask_done));
+ }
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(viscsi2_debug_calibration_status);
+
+static int viscsi2_debug_err_status_show(struct seq_file *m, void *p)
+{
+ struct viscsi2 *priv = m->private;
+
+ seq_printf(m, "err_phy_fatal: 0x%08x\n", priv->debug_phy_fatal);
+ seq_printf(m, "err_pkt_fatal: 0x%08x\n", priv->debug_pkt_fatal);
+ seq_printf(m, "err_frame_fatal: 0x%08x\n", priv->debug_frame_fatal);
+ seq_printf(m, "err_phy: 0x%08x\n", priv->debug_phy);
+ seq_printf(m, "err_pkt: 0x%08x\n", priv->debug_pkt);
+ seq_printf(m, "err_line: 0x%08x\n", priv->debug_line);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(viscsi2_debug_err_status);
+
+static void viscsi2_debug_init(struct viscsi2 *csi2)
+{
+ csi2->debugfs_dir = debugfs_create_dir(dev_name(csi2->dev), NULL);
+ if (IS_ERR(csi2->debugfs_dir))
+ return;
+ debugfs_create_file("calibration_status", 0444, csi2->debugfs_dir, csi2,
+ &viscsi2_debug_calibration_status_fops);
+ debugfs_create_file("err_status", 0444, csi2->debugfs_dir, csi2,
+ &viscsi2_debug_err_status_fops);
+}
+
+static void viscsi2_debug_cleanup(struct viscsi2 *csi2)
+{
+ debugfs_remove_recursive(csi2->debugfs_dir);
+}
+#endif
+
+#define OSC_FREQ_TARGET 0x1cc
+
+struct viscsi2_dphy_hs_info {
+ u32 rate;
+ u32 hsfreqrange;
+};
+
+static const struct viscsi2_dphy_hs_info dphy_hs_info[] = {
+ { 80, 0x0 }, { 85, 0x10 }, { 95, 0x20 }, { 105, 0x30 },
+ { 115, 0x1 }, { 125, 0x11 }, { 135, 0x21 }, { 145, 0x31 },
+ { 155, 0x2 }, { 165, 0x12 }, { 175, 0x22 }, { 185, 0x32 },
+ { 198, 0x3 }, { 213, 0x13 }, { 228, 0x23 }, { 243, 0x33 },
+ { 263, 0x4 }, { 288, 0x14 }, { 313, 0x25 }, { 338, 0x35 },
+ { 375, 0x5 }, { 425, 0x16 }, { 475, 0x26 }, { 525, 0x37 },
+ { 575, 0x7 }, { 625, 0x18 }, { 675, 0x28 }, { 725, 0x39 },
+ { 775, 0x9 }, { 825, 0x19 }, { 875, 0x29 }, { 925, 0x3a },
+ { 975, 0xa }, { 1025, 0x1a }, { 1075, 0x2a }, { 1125, 0x3b },
+ { 1175, 0xb }, { 1225, 0x1b }, { 1275, 0x2b }, { 1325, 0x3c },
+ { 1375, 0xc }, { 1425, 0x1c }, { 1475, 0x2c }
+};
+
+static void get_dphy_hs_transfer_info(u32 dphy_rate, u32 *hsfreqrange)
+{
+ unsigned int i;
+
+ for (i = 1; i < ARRAY_SIZE(dphy_hs_info); i++) {
+ if (dphy_rate < dphy_hs_info[i].rate) {
+ *hsfreqrange = dphy_hs_info[i - 1].hsfreqrange;
+ return;
+ }
+ }
+
+ /* Not found; return the largest entry */
+ *hsfreqrange = dphy_hs_info[ARRAY_SIZE(dphy_hs_info) - 1].hsfreqrange;
+}
+
+static void viscsi2_set_dphy_rate(struct viscsi2 *priv, u32 dphy_rate)
+{
+ u32 hsfreqrange;
+
+ get_dphy_hs_transfer_info(dphy_rate, &hsfreqrange);
+
+ dphy_write(priv, DIG_SYS_1, hsfreqrange);
+ dphy_write(priv, DIG_SYS_0, SYS_0_HSFREQRANGE_OVR);
+ dphy_write(priv, DIG_RX_STARTUP_OVR_5, STARTUP_OVR_5_BYPASS);
+ dphy_write(priv, DIG_RX_STARTUP_OVR_4, STARTUP_OVR_4_CNTVAL);
+ dphy_write(priv, DIG_CB_2, CB_2_LPRX_BIAS | CB_2_RESERVED);
+ dphy_write(priv, DIG_SYS_7, SYS_7_DESKEW_POL | SYS_7_RESERVED);
+ dphy_write(priv, DIG_CLKLANE_LANE_6, CLKLANE_RXHS_PULL_LONG);
+ dphy_write(priv, DIG_RX_STARTUP_OVR_2,
+ FIELD_GET(0xff, OSC_FREQ_TARGET));
+ dphy_write(priv, DIG_RX_STARTUP_OVR_3,
+ FIELD_GET(0xf00, OSC_FREQ_TARGET));
+ dphy_write(priv, DIG_RX_STARTUP_OVR_4,
+ STARTUP_OVR_4_CNTVAL | STARTUP_OVR_4_DDL_EN);
+}
+
+static int viscsi2_initialize(struct viscsi2 *priv, u32 num_lane, u32 dphy_rate,
+ const struct viscsi2_line_err_target *err_target)
+{
+ u32 val;
+
+ if (dphy_rate < CSI2RX_MIN_DATA_RATE ||
+ dphy_rate > CSI2RX_MAX_DATA_RATE) {
+ dev_err(priv->dev, "Unsupported PHY speed (%u Mbps)",
+ dphy_rate);
+ return -ERANGE;
+ }
+
+ /* 1st phase of initialization */
+ viscsi2_write(priv, REG_CSI2RX_RESETN, 1);
+ viscsi2_write(priv, REG_CSI2RX_PHY_RSTZ, 0);
+ viscsi2_write(priv, REG_CSI2RX_PHY_SHUTDOWNZ, 0);
+ viscsi2_write(priv, REG_CSI2RX_PHY_TESTCTRL0, 1);
+ ndelay(15);
+ viscsi2_write(priv, REG_CSI2RX_PHY_TESTCTRL0, 0);
+
+ /* Configure D-PHY frequency range */
+ viscsi2_set_dphy_rate(priv, dphy_rate);
+
+ /* 2nd phase of initialization */
+ viscsi2_write(priv, REG_CSI2RX_NLANES, num_lane - 1);
+ ndelay(5);
+
+ /* Release D-PHY from Reset */
+ viscsi2_write(priv, REG_CSI2RX_PHY_SHUTDOWNZ, 1);
+ ndelay(5);
+ viscsi2_write(priv, REG_CSI2RX_PHY_RSTZ, 1);
+
+ /* Configuration of line error target */
+ val = (err_target->vc[3] << 30) | (err_target->dt[3] << 24) |
+ (err_target->vc[2] << 22) | (err_target->dt[2] << 16) |
+ (err_target->vc[1] << 14) | (err_target->dt[1] << 8) |
+ (err_target->vc[0] << 6) | (err_target->dt[0]);
+ viscsi2_write(priv, REG_CSI2RX_DATA_IDS_1, val);
+ val = (err_target->vc[7] << 30) | (err_target->dt[7] << 24) |
+ (err_target->vc[6] << 22) | (err_target->dt[6] << 16) |
+ (err_target->vc[5] << 14) | (err_target->dt[5] << 8) |
+ (err_target->vc[4] << 6) | (err_target->dt[4]);
+ viscsi2_write(priv, REG_CSI2RX_DATA_IDS_2, val);
+
+ /* Configuration of mask */
+ viscsi2_write(priv, REG_CSI2RX_INT_MSK_PHY_FATAL, MASK_PHY_FATAL_ALL);
+ viscsi2_write(priv, REG_CSI2RX_INT_MSK_PKT_FATAL, MASK_PKT_FATAL_ALL);
+ viscsi2_write(priv, REG_CSI2RX_INT_MSK_FRAME_FATAL,
+ MASK_FRAME_FATAL_ALL);
+ viscsi2_write(priv, REG_CSI2RX_INT_MSK_PHY, MASK_PHY_ERROR_ALL);
+ viscsi2_write(priv, REG_CSI2RX_INT_MSK_PKT, MASK_PKT_ERROR_ALL);
+ viscsi2_write(priv, REG_CSI2RX_INT_MSK_LINE, MASK_LINE_ERROR_ALL);
+
+ return 0;
+}
+
+struct viscsi2_format {
+ u32 code;
+ unsigned int bpp;
+};
+
+static const struct viscsi2_format viscsi2_formats[] = {
+ { .code = MEDIA_BUS_FMT_RGB888_1X24, .bpp = 24 },
+ { .code = MEDIA_BUS_FMT_UYVY8_1X16, .bpp = 16 },
+ { .code = MEDIA_BUS_FMT_UYVY10_1X20, .bpp = 20 },
+ { .code = MEDIA_BUS_FMT_RGB565_1X16, .bpp = 16 },
+ { .code = MEDIA_BUS_FMT_SBGGR8_1X8, .bpp = 8 },
+ { .code = MEDIA_BUS_FMT_SGBRG8_1X8, .bpp = 8 },
+ { .code = MEDIA_BUS_FMT_SGRBG8_1X8, .bpp = 8 },
+ { .code = MEDIA_BUS_FMT_SRGGB8_1X8, .bpp = 8 },
+ { .code = MEDIA_BUS_FMT_SBGGR10_1X10, .bpp = 10 },
+ { .code = MEDIA_BUS_FMT_SGBRG10_1X10, .bpp = 10 },
+ { .code = MEDIA_BUS_FMT_SGRBG10_1X10, .bpp = 10 },
+ { .code = MEDIA_BUS_FMT_SRGGB10_1X10, .bpp = 10 },
+ { .code = MEDIA_BUS_FMT_SBGGR12_1X12, .bpp = 12 },
+ { .code = MEDIA_BUS_FMT_SGBRG12_1X12, .bpp = 12 },
+ { .code = MEDIA_BUS_FMT_SGRBG12_1X12, .bpp = 12 },
+ { .code = MEDIA_BUS_FMT_SRGGB12_1X12, .bpp = 12 },
+ { .code = MEDIA_BUS_FMT_SBGGR14_1X14, .bpp = 14 },
+ { .code = MEDIA_BUS_FMT_SGBRG14_1X14, .bpp = 14 },
+ { .code = MEDIA_BUS_FMT_SGRBG14_1X14, .bpp = 14 },
+ { .code = MEDIA_BUS_FMT_SRGGB14_1X14, .bpp = 14 },
+};
+
+static const struct viscsi2_format *fmt_for_mbus_code(unsigned int mbus_code)
+{
+ unsigned int i;
+
+ for (i = 0; ARRAY_SIZE(viscsi2_formats); i++) {
+ if (viscsi2_formats[i].code == mbus_code)
+ return &viscsi2_formats[i];
+ }
+ return NULL;
+}
+
+static const struct viscsi2_line_err_target err_target_vc0_alldt = {
+ /* Select VC=0 */
+ /* Select all supported DataTypes */
+ .dt = {
+ MIPI_CSI2_DT_RGB565,
+ MIPI_CSI2_DT_YUV422_8B,
+ MIPI_CSI2_DT_YUV422_10B,
+ MIPI_CSI2_DT_RGB888,
+ MIPI_CSI2_DT_RAW8,
+ MIPI_CSI2_DT_RAW10,
+ MIPI_CSI2_DT_RAW12,
+ MIPI_CSI2_DT_RAW14,
+ }
+};
+
+static int viscsi2_start(struct viscsi2 *priv, struct v4l2_subdev_state *state)
+{
+ const struct v4l2_mbus_framefmt *sink_fmt;
+ const struct viscsi2_format *cur_fmt;
+ struct media_pad *src_pad;
+ int cur_bpp, dphy_rate;
+ s64 link_freq;
+ int ret = 0;
+
+ /* Get bpp for current format */
+ sink_fmt = v4l2_subdev_state_get_format(state, VISCSI2_PAD_SINK);
+ cur_fmt = fmt_for_mbus_code(sink_fmt->code);
+ if (!cur_fmt)
+ return -EINVAL;
+ cur_bpp = cur_fmt->bpp;
+
+ /* Get DPHY rate [unit: Mbps]; note that the signal is DDR */
+ src_pad = &priv->remote->entity.pads[priv->remote_pad];
+ link_freq = v4l2_get_link_freq(src_pad, cur_bpp, 2 * priv->lanes);
+ if (link_freq < 0)
+ return link_freq;
+ dphy_rate = div_s64(link_freq, 500000);
+
+ clk_prepare_enable(priv->clk_apb);
+ clk_prepare_enable(priv->clk_cfg);
+ ndelay(15);
+ reset_control_deassert(priv->rst);
+
+ ret = viscsi2_initialize(priv, priv->lanes, dphy_rate,
+ &err_target_vc0_alldt);
+
+ if (ret) {
+ reset_control_assert(priv->rst);
+ clk_disable_unprepare(priv->clk_cfg);
+ clk_disable_unprepare(priv->clk_apb);
+ return ret;
+ }
+
+ priv->running = true;
+ return 0;
+}
+
+static void viscsi2_stop(struct viscsi2 *priv)
+{
+ priv->running = false;
+
+ /* Disable interrupt by clearing bits of MSK registers */
+ viscsi2_write(priv, REG_CSI2RX_INT_MSK_PHY_FATAL, 0);
+ viscsi2_write(priv, REG_CSI2RX_INT_MSK_PKT_FATAL, 0);
+ viscsi2_write(priv, REG_CSI2RX_INT_MSK_FRAME_FATAL, 0);
+ viscsi2_write(priv, REG_CSI2RX_INT_MSK_PHY, 0);
+ viscsi2_write(priv, REG_CSI2RX_INT_MSK_PKT, 0);
+ viscsi2_write(priv, REG_CSI2RX_INT_MSK_LINE, 0);
+ /* Make sure registers cleared */
+ viscsi2_read(priv, REG_CSI2RX_INT_MSK_PHY_FATAL);
+ viscsi2_read(priv, REG_CSI2RX_INT_MSK_PKT_FATAL);
+ viscsi2_read(priv, REG_CSI2RX_INT_MSK_FRAME_FATAL);
+ viscsi2_read(priv, REG_CSI2RX_INT_MSK_PHY);
+ viscsi2_read(priv, REG_CSI2RX_INT_MSK_PKT);
+ viscsi2_read(priv, REG_CSI2RX_INT_MSK_LINE);
+ /* Wait for current handlers finish */
+ synchronize_irq(priv->irq);
+
+ /* Shutdown hardware */
+ viscsi2_write(priv, REG_CSI2RX_PHY_SHUTDOWNZ, 0);
+ viscsi2_write(priv, REG_CSI2RX_PHY_RSTZ, 0);
+ viscsi2_write(priv, REG_CSI2RX_PHY_TESTCTRL0, 1);
+ viscsi2_write(priv, REG_CSI2RX_RESETN, 0);
+
+ reset_control_assert(priv->rst);
+ clk_disable_unprepare(priv->clk_cfg);
+ clk_disable_unprepare(priv->clk_apb);
+}
+
+static int viscsi2_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct viscsi2 *priv = sd_to_csi2(sd);
+ int ret;
+
+ /* Enabling: turn on CSI2RX -> turn on sensor */
+ ret = viscsi2_start(priv, state);
+ if (ret)
+ return ret;
+
+ /* Currently CSI2RX supports only stream0 in source pad */
+ ret = v4l2_subdev_enable_streams(priv->remote, priv->remote_pad,
+ BIT(0));
+ if (ret) {
+ viscsi2_stop(priv);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int viscsi2_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct viscsi2 *priv = sd_to_csi2(sd);
+
+ /* Disabling: turn off sensor -> turn off CSI2RX */
+ v4l2_subdev_disable_streams(priv->remote, priv->remote_pad, BIT(0));
+ viscsi2_stop(priv);
+
+ return 0;
+}
+
+static int viscsi2_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->pad == VISCSI2_PAD_SRC) {
+ const struct v4l2_mbus_framefmt *sink_fmt;
+
+ /* SRC pad supports exactly the same format as SINK pad */
+ if (code->index)
+ return -EINVAL;
+ sink_fmt = v4l2_subdev_state_get_format(sd_state,
+ VISCSI2_PAD_SINK);
+ code->code = sink_fmt->code;
+ return 0;
+ }
+
+ if (code->index >= ARRAY_SIZE(viscsi2_formats))
+ return -EINVAL;
+ code->code = viscsi2_formats[code->index].code;
+
+ return 0;
+}
+
+static int viscsi2_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ if (fse->index > 0)
+ return -EINVAL;
+
+ if (!fmt_for_mbus_code(fse->code))
+ return -EINVAL;
+
+ fse->min_width = VISCSI2_MIN_WIDTH;
+ fse->max_width = VISCSI2_MAX_WIDTH;
+ fse->min_height = VISCSI2_MIN_HEIGHT;
+ fse->max_height = VISCSI2_MAX_HEIGHT;
+
+ return 0;
+}
+
+static int viscsi2_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state)
+{
+ struct v4l2_mbus_framefmt *sink_fmt, *src_fmt;
+
+ sink_fmt = v4l2_subdev_state_get_format(sd_state, VISCSI2_PAD_SINK);
+ src_fmt = v4l2_subdev_state_get_format(sd_state, VISCSI2_PAD_SRC);
+
+ sink_fmt->width = VISCSI2_DEF_WIDTH;
+ sink_fmt->height = VISCSI2_DEF_HEIGHT;
+ sink_fmt->field = V4L2_FIELD_NONE;
+ sink_fmt->code = viscsi2_formats[0].code;
+ sink_fmt->colorspace = V4L2_COLORSPACE_RAW;
+ sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
+ sink_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ sink_fmt->xfer_func = V4L2_XFER_FUNC_NONE;
+
+ *src_fmt = *sink_fmt;
+
+ return 0;
+}
+
+static int viscsi2_set_pad_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *fmt)
+{
+ struct v4l2_mbus_framefmt *sink_fmt, *src_fmt;
+
+ /* SRC PAD has the same format as SINK PAD */
+ if (fmt->pad == VISCSI2_PAD_SRC)
+ return v4l2_subdev_get_fmt(sd, sd_state, fmt);
+
+ sink_fmt = v4l2_subdev_state_get_format(sd_state, VISCSI2_PAD_SINK);
+
+ *sink_fmt = fmt->format;
+ sink_fmt->width = clamp_t(u32, fmt->format.width, VISCSI2_MIN_WIDTH,
+ VISCSI2_MAX_WIDTH);
+ sink_fmt->height = clamp_t(u32, fmt->format.height, VISCSI2_MIN_HEIGHT,
+ VISCSI2_MAX_HEIGHT);
+ if (!fmt_for_mbus_code(sink_fmt->code))
+ sink_fmt->code = viscsi2_formats[0].code;
+ fmt->format = *sink_fmt;
+
+ /* Source pad should have the same format */
+ src_fmt = v4l2_subdev_state_get_format(sd_state, VISCSI2_PAD_SRC);
+ *src_fmt = *sink_fmt;
+
+ return 0;
+}
+
+static const struct media_entity_operations viscsi2_entity_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+static const struct v4l2_subdev_pad_ops viscsi2_pad_ops = {
+ .enum_mbus_code = viscsi2_enum_mbus_code,
+ .enum_frame_size = viscsi2_enum_frame_size,
+ .disable_streams = viscsi2_disable_streams,
+ .enable_streams = viscsi2_enable_streams,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = viscsi2_set_pad_format,
+};
+
+static const struct v4l2_subdev_ops viscsi2_subdev_ops = {
+ .pad = &viscsi2_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops viscsi2_internal_ops = {
+ .init_state = viscsi2_init_state,
+};
+
+static int viscsi2_notify_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_connection *asc)
+{
+ struct viscsi2 *priv = notifier_to_csi2(notifier);
+ int pad;
+
+ pad = media_entity_get_fwnode_pad(&subdev->entity, asc->match.fwnode,
+ MEDIA_PAD_FL_SOURCE);
+ if (pad < 0) {
+ dev_err(priv->dev, "Failed to find pad for %s\n", subdev->name);
+ return pad;
+ }
+
+ priv->remote = subdev;
+ priv->remote_pad = pad;
+
+ return media_create_pad_link(
+ &subdev->entity, pad, &priv->subdev.entity, 0,
+ MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
+}
+
+static void viscsi2_notify_unbind(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_connection *asc)
+{
+ struct viscsi2 *priv = notifier_to_csi2(notifier);
+
+ priv->remote = NULL;
+}
+
+static const struct v4l2_async_notifier_operations viscsi2_notify_ops = {
+ .bound = viscsi2_notify_bound,
+ .unbind = viscsi2_notify_unbind,
+};
+
+static int viscsi2_parse_dt(struct viscsi2 *priv)
+{
+ struct v4l2_async_connection *asc;
+ struct fwnode_handle *fwnode;
+ struct fwnode_handle *ep;
+ struct v4l2_fwnode_endpoint v4l2_ep = {
+ .bus_type = V4L2_MBUS_CSI2_DPHY,
+ };
+ int ret;
+
+ ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(priv->dev), 0, 0, 0);
+ if (!ep) {
+ dev_err(priv->dev, "Not connected to subdevice\n");
+ return -EINVAL;
+ }
+
+ ret = v4l2_fwnode_endpoint_parse(ep, &v4l2_ep);
+ if (ret) {
+ dev_err(priv->dev, "Could not parse v4l2 endpoint\n");
+ goto error_fwnode_handle_put;
+ }
+
+ priv->lanes = v4l2_ep.bus.mipi_csi2.num_data_lanes;
+ if (priv->lanes != 1 && priv->lanes != 2 && priv->lanes != 4) {
+ dev_err(priv->dev,
+ "Unsupported number of data-lanes for D-PHY: %u\n",
+ priv->lanes);
+ goto error_fwnode_handle_put;
+ }
+
+ fwnode = fwnode_graph_get_remote_endpoint(ep);
+ fwnode_handle_put(ep);
+
+ v4l2_async_subdev_nf_init(&priv->notifier, &priv->subdev);
+ priv->notifier.ops = &viscsi2_notify_ops;
+
+ asc = v4l2_async_nf_add_fwnode(&priv->notifier, fwnode,
+ struct v4l2_async_connection);
+ fwnode_handle_put(fwnode);
+ if (IS_ERR(asc))
+ return PTR_ERR(asc);
+
+ ret = v4l2_async_nf_register(&priv->notifier);
+ if (ret)
+ v4l2_async_nf_cleanup(&priv->notifier);
+
+ return ret;
+
+error_fwnode_handle_put:
+ fwnode_handle_put(ep);
+ return -EINVAL;
+}
+
+static irqreturn_t viscsi2_irq(int irq, void *dev_id)
+{
+ struct viscsi2 *priv = dev_id;
+ u32 event;
+
+ event = viscsi2_read(priv, REG_CSI2RX_INT_ST_MAIN);
+ dev_err_ratelimited(priv->dev, "CSI2RX error 0x%x.\n", event);
+
+#ifdef CONFIG_DEBUG_FS
+ priv->debug_phy_fatal |=
+ viscsi2_read(priv, REG_CSI2RX_INT_ST_PHY_FATAL);
+ priv->debug_pkt_fatal |=
+ viscsi2_read(priv, REG_CSI2RX_INT_ST_PKT_FATAL);
+ priv->debug_frame_fatal |=
+ viscsi2_read(priv, REG_CSI2RX_INT_ST_FRAME_FATAL);
+ priv->debug_phy |= viscsi2_read(priv, REG_CSI2RX_INT_ST_PHY);
+ priv->debug_pkt |= viscsi2_read(priv, REG_CSI2RX_INT_ST_PKT);
+ priv->debug_line |= viscsi2_read(priv, REG_CSI2RX_INT_ST_LINE);
+#endif
+
+ return IRQ_HANDLED;
+}
+
+static const struct of_device_id viscsi2_of_table[] = {
+ {
+ .compatible = "toshiba,visconti5-csi2",
+ },
+ { /* Sentinel */ }
+};
+
+static int viscsi2_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct viscsi2 *priv;
+ int irq, ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->running = false;
+
+ priv->dev = dev;
+
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+ ret = devm_request_irq(dev, irq, viscsi2_irq, 0, KBUILD_MODNAME, priv);
+ if (ret)
+ return dev_err_probe(dev, ret, "irq request failed");
+
+ priv->irq = irq;
+
+ priv->clk_cfg = devm_clk_get(dev, "cfg");
+ if (IS_ERR(priv->clk_cfg))
+ return dev_err_probe(dev, PTR_ERR(priv->clk_cfg),
+ "cannot get clock cfg");
+ priv->clk_apb = devm_clk_get(dev, "apb");
+ if (IS_ERR(priv->clk_apb))
+ return dev_err_probe(dev, PTR_ERR(priv->clk_apb),
+ "cannot get clock apb");
+ priv->rst = devm_reset_control_get_exclusive_by_index(dev, 0);
+ if (IS_ERR(priv->rst))
+ return dev_err_probe(dev, PTR_ERR(priv->rst),
+ "cannot get reset");
+
+ platform_set_drvdata(pdev, priv);
+
+ ret = viscsi2_parse_dt(priv);
+ if (ret)
+ return ret;
+
+ priv->subdev.dev = &pdev->dev;
+ v4l2_subdev_init(&priv->subdev, &viscsi2_subdev_ops);
+ v4l2_set_subdevdata(&priv->subdev, &pdev->dev);
+ snprintf(priv->subdev.name, sizeof(priv->subdev.name), "%s %s",
+ KBUILD_MODNAME, dev_name(&pdev->dev));
+
+ priv->subdev.internal_ops = &viscsi2_internal_ops;
+ priv->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+ priv->subdev.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
+ priv->subdev.entity.ops = &viscsi2_entity_ops;
+
+ priv->pads[VISCSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ priv->pads[VISCSI2_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
+
+ ret = media_entity_pads_init(&priv->subdev.entity,
+ ARRAY_SIZE(priv->pads), priv->pads);
+ if (ret)
+ goto err_cleanup_async;
+
+ ret = v4l2_subdev_init_finalize(&priv->subdev);
+ if (ret)
+ goto err_cleanup_media_entity;
+
+ ret = v4l2_async_register_subdev(&priv->subdev);
+ if (ret < 0)
+ goto err_cleanup_subdev_state;
+
+#ifdef CONFIG_DEBUG_FS
+ viscsi2_debug_init(priv);
+#endif
+
+ return 0;
+
+err_cleanup_subdev_state:
+ v4l2_subdev_cleanup(&priv->subdev);
+
+err_cleanup_media_entity:
+ media_entity_cleanup(&priv->subdev.entity);
+
+err_cleanup_async:
+ v4l2_async_nf_unregister(&priv->notifier);
+ v4l2_async_nf_cleanup(&priv->notifier);
+
+ return ret;
+}
+
+static void viscsi2_remove(struct platform_device *pdev)
+{
+ struct viscsi2 *priv = platform_get_drvdata(pdev);
+
+#ifdef CONFIG_DEBUG_FS
+ viscsi2_debug_cleanup(priv);
+#endif
+
+ v4l2_async_nf_unregister(&priv->notifier);
+ v4l2_async_nf_cleanup(&priv->notifier);
+ v4l2_async_unregister_subdev(&priv->subdev);
+
+ v4l2_subdev_cleanup(&priv->subdev);
+ media_entity_cleanup(&priv->subdev.entity);
+}
+
+static struct platform_driver viscsi2_driver = {
+ .probe = viscsi2_probe,
+ .remove = viscsi2_remove,
+ .driver = {
+ .name = "visconti-csi2rx",
+ .of_match_table = viscsi2_of_table,
+ },
+};
+
+module_platform_driver(viscsi2_driver);
+
+MODULE_AUTHOR("Yuji Ishikawa <yuji2.ishikawa@toshiba.co.jp>");
+MODULE_DESCRIPTION("Toshiba Visconti CSI-2 receiver driver");
+MODULE_LICENSE("Dual BSD/GPL");
--
2.34.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v13 6/7] media: platform: visconti: Add streaming interface for ISP parameters and statistics
2025-10-16 2:24 [PATCH v13 0/7] Add Toshiba Visconti Video Input Interface driver Yuji Ishikawa
` (3 preceding siblings ...)
2025-10-16 2:24 ` [PATCH v13 4/7] media: platform: visconti: Add Toshiba Visconti CSI-2 Receiver driver Yuji Ishikawa
@ 2025-10-16 2:24 ` Yuji Ishikawa
2025-10-16 2:24 ` [PATCH v13 7/7] documentation: media: Add documentation for Toshiba Visconti Video Input Interface driver Yuji Ishikawa
5 siblings, 0 replies; 15+ messages in thread
From: Yuji Ishikawa @ 2025-10-16 2:24 UTC (permalink / raw)
To: Nobuhiro Iwamatsu, Yuji Ishikawa, Mauro Carvalho Chehab,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Philipp Zabel
Cc: linux-media, devicetree, linux-arm-kernel, linux-kernel
Add support to Image Signal Processors of Visconti's Video Input Interface.
This patch adds two streaming interfaces;
one for passing parameters to the signal processor,
the other for receiving statistics.
Signed-off-by: Yuji Ishikawa <yuji2.ishikawa@toshiba.co.jp>
---
Changelog v2:
- Resend v1 because a patch exceeds size limit.
Changelog v3:
- Adapted to media control framework
- Introduced ISP subdevice, capture device
- Remove private IOCTLs and add vendor specific V4L2 controls
- Change function name avoiding camelcase and uppercase letters
Changelog v4:
- Split patches because the v3 patch exceeds size limit
- Stop using ID number to identify driver instance:
- Use dynamically allocated structure to hold HW specific context,
instead of static one.
- Call HW layer functions with the context structure instead of ID number
Changelog v5:
- no change
Changelog v6:
- remove unused macros
- removed hwd_ and HWD_ prefix
- update source code documentation
- Suggestion from Hans Verkuil
- pointer to userland memory is removed from uAPI arguments
- style of structure is now "nested" instead of "chained by pointer";
- use div64_u64 for 64bit division
- vendor specific controls support TRY_EXT_CTRLS
- add READ_ONLY flag to GET_CALIBRATION_STATUS control and similar ones
- human friendry control names for vendor specific controls
- add initial value to each vendor specific control
- GET_LAST_CAPTURE_STATUS control is updated asyncnously from workqueue
- remove EXECUTE_ON_WRITE flag of vendor specific control
- uAPI: return value of GET_CALIBRATION_STATUS follows common rules of error codes
- applied v4l2-compliance
- Suggestion from Sakari Ailus
- use div64_u64 for 64bit division
- update copyright's year
- remove redandunt cast
- use bool instead of HWD_VIIF_ENABLE/DISABLE
- simplify comparison to 0
- simplify statements with trigram operator
- remove redundant local variables
- use general integer types instead of u32/s32
- Suggestion from Laurent Pinchart
- moved VIIF driver to driver/platform/toshiba/visconti
- change register access: struct-style to macro-style
- remove unused type definitions
- define enums instead of successive macro constants
- remove redundant parenthesis of macro constant
- embed struct hwd_res into struct viif_device
- use xxx_dma instead of xxx_paddr for variable names of IOVA
- literal value: just 0 instead of 0x0
- use literal 1 or 0 instead of HWD_VIIF_ENABLE, DISABLE for register access
- use true or false instead of HWD_VIIF_ENABLE, DISABLE for function calls
- uAPI: return value of GET_CALIBRATION_STATUS follows common rules of error codes
Changelog v7:
- remove unused variables
- split long statements which have multiple logical-OR and trigram operators
Changelog v8:
- define constant V4L2_CTRL_TYPE_VISCONTI_ISP for datatype
of Visconti specific controls
- Suggestion from Hans Verkuil
- remove pr_info()
- use pm_runtime_get_if_in_use() to get power status
Changelog v9:
- fix warning for cast between ptr and dma_addr_t
Changelog v10:
- use parameter buffer instead of vendor specific compound controls
- add viif_params interface for passing ISP parameters
- add viif_stats interface for passing ISP status
- remove parameter validation routine; moved to userland library
Changelog v11:
- remove feature VB2_USERPTR from viif_params and viif_stats
- fix strange indents at initializations
- remove a redundant default setting of the ISP: L2_ROI
- update copyright year
Changelog v12:
- use guard(spinlock)(locked_variable) macros
- also use custom guard macros for viif_isp_guard
- improve cast operations for viif_dev->tables_dma
- add default parameter for undistortion (identical transformation)
- add function to calculate the default scaling
according to the request from the resizer subdevice
Changelog v13:
- wrap one line at 80 characters
- change banner comment style
- update comment style; spacing at the start and end, capitalize first letter
- remove redundant inline qualifier
- remove unused constants
- use system clock rate obtained from the framework, instead of constant value
- remove U suffix on numeric value
- remove members indicating status; they are moved to debugfs
- remove unused members from statistics interface
- fix v4l-compliance fail: v4l2-test-buffers.cpp(924): q.create_bufs(node, 1, &fmt) != EINVAL
- use ffs() instead of gen_grid_size()
- change signature of viif_apply_queued_parameter; int -> bool
- use ARRAY_SIZE macros for maximum loop iterations
- use scoped_guard() to acquire a lock
- use list_for_each_entry_safe() to modify list
- shorten the names of ISP guard functions.
---
drivers/media/platform/toshiba/visconti/Makefile | 2 +-
drivers/media/platform/toshiba/visconti/viif.c | 27 +-
drivers/media/platform/toshiba/visconti/viif_isp.c | 5 +
.../media/platform/toshiba/visconti/viif_params.c | 2257 ++++++++++++++++++++
.../media/platform/toshiba/visconti/viif_params.h | 20 +
.../media/platform/toshiba/visconti/viif_stats.c | 320 +++
.../media/platform/toshiba/visconti/viif_stats.h | 16 +
7 files changed, 2644 insertions(+), 3 deletions(-)
diff --git a/drivers/media/platform/toshiba/visconti/Makefile b/drivers/media/platform/toshiba/visconti/Makefile
index e9fe874d6447..da493e21532d 100644
--- a/drivers/media/platform/toshiba/visconti/Makefile
+++ b/drivers/media/platform/toshiba/visconti/Makefile
@@ -4,7 +4,7 @@
#
visconti-csi2rx-objs = csi2rx_drv.o
-visconti-viif-objs = viif.o viif_capture.o viif_common.o viif_isp.o
+visconti-viif-objs = viif.o viif_capture.o viif_common.o viif_isp.o viif_params.o viif_stats.o
obj-$(CONFIG_VIDEO_VISCONTI_CSI2RX) += visconti-csi2rx.o
obj-$(CONFIG_VIDEO_VISCONTI_VIIF) += visconti-viif.o
diff --git a/drivers/media/platform/toshiba/visconti/viif.c b/drivers/media/platform/toshiba/visconti/viif.c
index 16e8f108d996..04b4e3295645 100644
--- a/drivers/media/platform/toshiba/visconti/viif.c
+++ b/drivers/media/platform/toshiba/visconti/viif.c
@@ -24,7 +24,9 @@
#include "viif_capture.h"
#include "viif_common.h"
#include "viif_isp.h"
+#include "viif_params.h"
#include "viif_regs.h"
+#include "viif_stats.h"
/*----------------------------------------------
* Register Access
@@ -144,6 +146,9 @@ static irqreturn_t viif_vsync_irq_handler(int irq, void *dev_id)
visconti_viif_capture_switch_buffer(&viif_dev->cap_post1,
status_err,
l2_transfer_status, ts);
+ visconti_viif_stats_isr(viif_dev, viif_dev->cap_post0.sequence,
+ ts);
+ visconti_viif_params_isr(viif_dev);
}
/* Delayed Vsync of SUB unit */
@@ -533,13 +538,25 @@ static int visconti_viif_probe(struct platform_device *pdev)
goto error_isp_unregister;
}
+ ret = visconti_viif_params_register(viif_dev);
+ if (ret) {
+ dev_err_probe(dev, ret, "failed to register parameter node\n");
+ goto error_capture_unregister;
+ }
+
+ ret = visconti_viif_stats_register(viif_dev);
+ if (ret) {
+ dev_err_probe(dev, ret, "failed to register stat node\n");
+ goto error_params_unregister;
+ }
+
ret = visconti_viif_create_links(viif_dev);
if (ret)
- goto error_capture_unregister;
+ goto error_stats_unregister;
ret = visconti_viif_subdev_notifier_register(viif_dev);
if (ret)
- goto error_capture_unregister;
+ goto error_stats_unregister;
#ifdef CONFIG_DEBUG_FS
viif_debug_init(viif_dev);
@@ -547,6 +564,10 @@ static int visconti_viif_probe(struct platform_device *pdev)
return 0;
+error_stats_unregister:
+ visconti_viif_stats_unregister(viif_dev);
+error_params_unregister:
+ visconti_viif_params_unregister(viif_dev);
error_capture_unregister:
visconti_viif_capture_unregister(viif_dev);
error_isp_unregister:
@@ -576,6 +597,8 @@ static void visconti_viif_remove(struct platform_device *pdev)
v4l2_async_nf_unregister(&viif_dev->notifier);
v4l2_async_nf_cleanup(&viif_dev->notifier);
+ visconti_viif_stats_unregister(viif_dev);
+ visconti_viif_params_unregister(viif_dev);
visconti_viif_capture_unregister(viif_dev);
visconti_viif_isp_unregister(viif_dev);
media_device_unregister(&viif_dev->media_dev);
diff --git a/drivers/media/platform/toshiba/visconti/viif_isp.c b/drivers/media/platform/toshiba/visconti/viif_isp.c
index 6ad4723a37cc..db00db79ab06 100644
--- a/drivers/media/platform/toshiba/visconti/viif_isp.c
+++ b/drivers/media/platform/toshiba/visconti/viif_isp.c
@@ -15,6 +15,7 @@
#include "viif.h"
#include "viif_common.h"
#include "viif_isp.h"
+#include "viif_params.h"
#include "viif_regs.h"
/* Disable CSI2 capture at viif_mux_start() */
@@ -559,6 +560,9 @@ int visconti_viif_isp_main_set_unit(struct viif_device *viif_dev)
/* Enable regbuf */
visconti_viif_isp_set_regbuf_auto_transmission(viif_dev);
+ /* L2 Undist: enable through mode as default */
+ visconti_viif_l2_undist_through(viif_dev);
+
return 0;
}
@@ -612,6 +616,7 @@ static int visconti_viif_isp_enable_streams(struct v4l2_subdev *sd,
remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
/* Enabling: start ISP, MUX -> start CSI2RX, sensor */
+ visconti_viif_params_eval_queue(viif_dev);
viif_dev->masked_gamma_path = 0;
viif_mux_start(viif_dev, 0, 0);
diff --git a/drivers/media/platform/toshiba/visconti/viif_params.c b/drivers/media/platform/toshiba/visconti/viif_params.c
new file mode 100644
index 000000000000..047ac5e74814
--- /dev/null
+++ b/drivers/media/platform/toshiba/visconti/viif_params.c
@@ -0,0 +1,2257 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * Toshiba Visconti Video Capture Support
+ *
+ * (C) Copyright 2025 TOSHIBA CORPORATION
+ * (C) Copyright 2025 Toshiba Electronic Devices & Storage Corporation
+ */
+
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-vmalloc.h>
+
+#include "viif.h"
+#include "viif_capture.h"
+#include "viif_common.h"
+#include "viif_isp.h"
+#include "viif_params.h"
+#include "viif_regs.h"
+
+/* ISP_L1_SET_HDRC */
+#define VIIF_L1_HDRC_RATIO_OFFSET 10
+#define VIIF_REGBUF_ACCESS_TIME 15360
+#define VIIF_L1_DELAY_W_HDRC 31
+
+/* V4L2_CID_VISCONTI_VIIF_ISP_L2_SET_UNDIST */
+#define VIIF_L2_UNDIST_POLY_NUM 11
+
+#define VIIF_PARAMS_REQ_BUFS_MIN 2
+#define VIIF_PARAMS_REQ_BUFS_MAX 8
+
+/*----------------------------------------------
+ * ISP parameter configuration
+ */
+static void viif_l1_set_input_mode(struct viif_device *viif_dev,
+ const struct viif_l1_input_mode_config *arg)
+{
+ guard(spinlock_irqsave)(&viif_dev->regbuf_lock);
+ guard(viif_isp)(viif_dev);
+
+ viif_capture_write(viif_dev, REG_L1_SYSM_INPUT_MODE, (u32)arg->mode);
+}
+
+static void
+viif_l1_set_rgb_to_y_coef(struct viif_device *viif_dev,
+ const struct viif_l1_rgb_to_y_coef_config *arg)
+{
+ guard(spinlock_irqsave)(&viif_dev->regbuf_lock);
+ guard(viif_isp)(viif_dev);
+
+ viif_capture_write(viif_dev, REG_L1_SYSM_YCOEF_R, (u32)arg->coef_r);
+ viif_capture_write(viif_dev, REG_L1_SYSM_YCOEF_G, (u32)arg->coef_g);
+ viif_capture_write(viif_dev, REG_L1_SYSM_YCOEF_B, (u32)arg->coef_b);
+}
+
+static void viif_l1_set_ag_mode(struct viif_device *viif_dev,
+ const struct viif_l1_ag_mode_config *arg)
+{
+ u32 val;
+
+ guard(spinlock_irqsave)(&viif_dev->regbuf_lock);
+ guard(viif_isp)(viif_dev);
+
+ /* SYSM_AG_PARAM */
+ viif_capture_write(viif_dev, REG_L1_SYSM_AG_PARAM_A,
+ PACK_L1_SYSM_AG_PARAM(arg->sysm_ag_grad[0],
+ arg->sysm_ag_ofst[0]));
+ viif_capture_write(viif_dev, REG_L1_SYSM_AG_PARAM_B,
+ PACK_L1_SYSM_AG_PARAM(arg->sysm_ag_grad[1],
+ arg->sysm_ag_ofst[1]));
+ viif_capture_write(viif_dev, REG_L1_SYSM_AG_PARAM_C,
+ PACK_L1_SYSM_AG_PARAM(arg->sysm_ag_grad[2],
+ arg->sysm_ag_ofst[2]));
+ viif_capture_write(viif_dev, REG_L1_SYSM_AG_PARAM_D,
+ PACK_L1_SYSM_AG_PARAM(arg->sysm_ag_grad[3],
+ arg->sysm_ag_ofst[3]));
+
+ /* SYSM_AG_SEL */
+ viif_capture_write(
+ viif_dev, REG_L1_SYSM_AG_SEL_HOBC,
+ PACK_L1_SYSM_AG_SEL_HML(arg->sysm_ag_psel_hobc_high,
+ arg->sysm_ag_psel_hobc_middle_led,
+ arg->sysm_ag_psel_hobc_low));
+ viif_capture_write(
+ viif_dev, REG_L1_SYSM_AG_SEL_ABPC,
+ PACK_L1_SYSM_AG_SEL_HML(arg->sysm_ag_psel_abpc_high,
+ arg->sysm_ag_psel_abpc_middle_led,
+ arg->sysm_ag_psel_abpc_low));
+ viif_capture_write(
+ viif_dev, REG_L1_SYSM_AG_SEL_RCNR,
+ PACK_L1_SYSM_AG_SEL_HML(arg->sysm_ag_psel_rcnr_high,
+ arg->sysm_ag_psel_rcnr_middle_led,
+ arg->sysm_ag_psel_rcnr_low));
+
+ viif_capture_write(viif_dev, REG_L1_SYSM_AG_SEL_LSSC,
+ PACK_L1_SYSM_AG_SEL_SP(arg->sysm_ag_ssel_lssc,
+ arg->sysm_ag_psel_lssc));
+ viif_capture_write(viif_dev, REG_L1_SYSM_AG_SEL_MPRO,
+ PACK_L1_SYSM_AG_SEL_SP(arg->sysm_ag_ssel_mpro,
+ arg->sysm_ag_psel_mpro));
+ viif_capture_write(viif_dev, REG_L1_SYSM_AG_SEL_VPRO,
+ PACK_L1_SYSM_AG_SEL_SP(arg->sysm_ag_ssel_vpro,
+ arg->sysm_ag_psel_vpro));
+
+ /* SYSM_AG_CONT */
+ val = arg->sysm_ag_cont_hobc_en_middle_led ? MASK_L1_SYSM_AG_CONT_M_EN :
+ 0;
+ val |= FIELD_PREP(MASK_L1_SYSM_AG_CONT_M_VAL,
+ arg->sysm_ag_cont_hobc_test_middle_led);
+ val |= arg->sysm_ag_cont_hobc_en_high ? MASK_L1_SYSM_AG_CONT_H_EN : 0;
+ val |= FIELD_PREP(MASK_L1_SYSM_AG_CONT_H_VAL,
+ arg->sysm_ag_cont_hobc_test_high);
+ viif_capture_write(viif_dev, REG_L1_SYSM_AG_CONT_HOBC01_EN, val);
+
+ val = arg->sysm_ag_cont_hobc_en_low ? MASK_L1_SYSM_AG_CONT_L_EN : 0;
+ val |= FIELD_PREP(MASK_L1_SYSM_AG_CONT_L_VAL,
+ arg->sysm_ag_cont_hobc_test_low);
+ viif_capture_write(viif_dev, REG_L1_SYSM_AG_CONT_HOBC2_EN, val);
+
+ val = arg->sysm_ag_cont_abpc_en_middle_led ? MASK_L1_SYSM_AG_CONT_M_EN :
+ 0;
+ val |= FIELD_PREP(MASK_L1_SYSM_AG_CONT_M_VAL,
+ arg->sysm_ag_cont_abpc_test_middle_led);
+ val |= arg->sysm_ag_cont_abpc_en_high ? MASK_L1_SYSM_AG_CONT_H_EN : 0;
+ val |= FIELD_PREP(MASK_L1_SYSM_AG_CONT_H_VAL,
+ arg->sysm_ag_cont_abpc_test_high);
+ viif_capture_write(viif_dev, REG_L1_SYSM_AG_CONT_ABPC01_EN, val);
+
+ val = arg->sysm_ag_cont_abpc_en_low ? MASK_L1_SYSM_AG_CONT_L_EN : 0;
+ val |= FIELD_PREP(MASK_L1_SYSM_AG_CONT_L_VAL,
+ arg->sysm_ag_cont_abpc_test_low);
+ viif_capture_write(viif_dev, REG_L1_SYSM_AG_CONT_ABPC2_EN, val);
+
+ val = arg->sysm_ag_cont_rcnr_en_middle_led ? MASK_L1_SYSM_AG_CONT_M_EN :
+ 0;
+ val |= FIELD_PREP(MASK_L1_SYSM_AG_CONT_M_VAL,
+ arg->sysm_ag_cont_rcnr_test_middle_led);
+ val |= arg->sysm_ag_cont_rcnr_en_high ? MASK_L1_SYSM_AG_CONT_H_EN : 0;
+ val |= FIELD_PREP(MASK_L1_SYSM_AG_CONT_H_VAL,
+ arg->sysm_ag_cont_rcnr_test_high);
+ viif_capture_write(viif_dev, REG_L1_SYSM_AG_CONT_RCNR01_EN, val);
+
+ val = arg->sysm_ag_cont_rcnr_en_low ? MASK_L1_SYSM_AG_CONT_L_EN : 0;
+ val |= FIELD_PREP(MASK_L1_SYSM_AG_CONT_L_VAL,
+ arg->sysm_ag_cont_rcnr_test_low);
+ viif_capture_write(viif_dev, REG_L1_SYSM_AG_CONT_RCNR2_EN, val);
+
+ val = arg->sysm_ag_cont_lssc_en ? MASK_L1_SYSM_AG_CONT_EN : 0;
+ val |= FIELD_PREP(MASK_L1_SYSM_AG_CONT_VAL,
+ arg->sysm_ag_cont_lssc_test);
+ viif_capture_write(viif_dev, REG_L1_SYSM_AG_CONT_LSSC_EN, val);
+
+ val = arg->sysm_ag_cont_mpro_en ? MASK_L1_SYSM_AG_CONT_EN : 0;
+ val |= FIELD_PREP(MASK_L1_SYSM_AG_CONT_VAL,
+ arg->sysm_ag_cont_mpro_test);
+ viif_capture_write(viif_dev, REG_L1_SYSM_AG_CONT_MPRO_EN, val);
+
+ val = arg->sysm_ag_cont_vpro_en ? MASK_L1_SYSM_AG_CONT_EN : 0;
+ val |= FIELD_PREP(MASK_L1_SYSM_AG_CONT_VAL,
+ arg->sysm_ag_cont_vpro_test);
+ viif_capture_write(viif_dev, REG_L1_SYSM_AG_CONT_VPRO_EN, val);
+}
+
+static void viif_l1_set_ag(struct viif_device *viif_dev,
+ const struct viif_l1_ag_config *arg)
+{
+ guard(spinlock_irqsave)(&viif_dev->regbuf_lock);
+ guard(viif_isp)(viif_dev);
+
+ viif_capture_write(viif_dev, REG_L1_SYSM_AG_H, (u32)arg->gain_h);
+ viif_capture_write(viif_dev, REG_L1_SYSM_AG_M, (u32)arg->gain_m);
+ viif_capture_write(viif_dev, REG_L1_SYSM_AG_L, (u32)arg->gain_l);
+}
+
+static void viif_l1_set_hdre(struct viif_device *viif_dev,
+ const struct viif_l1_hdre_config *arg)
+{
+ int i;
+
+ guard(spinlock_irqsave)(&viif_dev->regbuf_lock);
+ guard(viif_isp)(viif_dev);
+
+ for (i = 0; i < LEN_L1_HDRE_SRCPOINT; i++)
+ viif_capture_write(viif_dev, REG_L1_HDRE_SRCPOINT(i),
+ arg->hdre_src_point[i]);
+
+ viif_capture_write(viif_dev, REG_L1_HDRE_SRCBASE(0), 0);
+ for (i = 1; i < LEN_L1_HDRE_SRCBASE; i++)
+ viif_capture_write(viif_dev, REG_L1_HDRE_SRCBASE(i),
+ arg->hdre_src_point[i - 1]);
+
+ for (i = 0; i < LEN_L1_HDRE_DSTBASE; i++)
+ viif_capture_write(viif_dev, REG_L1_HDRE_DSTBASE(i),
+ arg->hdre_dst_base[i]);
+
+ for (i = 0; i < LEN_L1_HDRE_RATIO; i++)
+ viif_capture_write(viif_dev, REG_L1_HDRE_RATIO(i),
+ arg->hdre_ratio[i]);
+
+ viif_capture_write(viif_dev, REG_L1_HDRE_DSTMAXVAL,
+ arg->hdre_dst_max_val);
+}
+
+static void
+viif_l1_set_img_extraction(struct viif_device *viif_dev,
+ const struct viif_l1_img_extraction_config *arg)
+{
+ guard(spinlock_irqsave)(&viif_dev->regbuf_lock);
+ guard(viif_isp)(viif_dev);
+
+ viif_capture_write(viif_dev, REG_L1_SLIC_SRCBLACKLEVEL_GR,
+ arg->input_black_gr);
+ viif_capture_write(viif_dev, REG_L1_SLIC_SRCBLACKLEVEL_R,
+ arg->input_black_r);
+ viif_capture_write(viif_dev, REG_L1_SLIC_SRCBLACKLEVEL_B,
+ arg->input_black_b);
+ viif_capture_write(viif_dev, REG_L1_SLIC_SRCBLACKLEVEL_GB,
+ arg->input_black_gb);
+}
+
+static void viif_config_vdm_tgroup(struct viif_device *viif_dev, int idx)
+{
+ const struct {
+ u32 cfg;
+ u32 sram_base;
+ u32 sram_size;
+ } conf[] = {
+ /* T01: L1_SET_DPC, L1_SET_LSC */
+ { VAL_TGROUP_CFG_64BIT_RD, 0x600, 0x20 },
+ /* T02: L2_UNDIST grid table */
+ { VAL_TGROUP_CFG_32BIT_RD, 0x620, 0x20 },
+ /* T02: L2_GAMMA (path0) */
+ { VAL_TGROUP_CFG_32BIT_RD, 0x640, 0x20 },
+ /* T03: L2 GAMMA (path1) */
+ { VAL_TGROUP_CFG_32BIT_RD, 0x660, 0x20 },
+ };
+
+ viif_capture_write(viif_dev, REG_VDM_TGROUP_X_CFG(idx), conf[idx].cfg);
+ viif_capture_write(viif_dev, REG_VDM_TGROUP_X_SRAM_BASE(idx),
+ conf[idx].sram_base);
+ viif_capture_write(viif_dev, REG_VDM_TGROUP_X_SRAM_SIZE(idx),
+ conf[idx].sram_size);
+}
+
+static void dpc_table_transmission(struct viif_device *viif_dev,
+ uintptr_t table_h, uintptr_t table_m,
+ uintptr_t table_l)
+{
+ u32 val = 0;
+
+ viif_config_vdm_tgroup(viif_dev, IDX_TGROUP_L1_ISP);
+
+ if (table_h) {
+ viif_capture_write(viif_dev,
+ REG_VDM_TPORT_X_STADR(IDX_TPORT_L1_DPC_H),
+ (u32)table_h);
+ viif_capture_write(viif_dev,
+ REG_VDM_TPORT_X_SIZE(IDX_TPORT_L1_DPC_H),
+ VIIF_DPC_TABLE_BYTES);
+ val |= MASK_VDM_T_ENABLE_L1_DPC_H;
+ }
+
+ if (table_m) {
+ viif_capture_write(viif_dev,
+ REG_VDM_TPORT_X_STADR(IDX_TPORT_L1_DPC_M),
+ (u32)table_m);
+ viif_capture_write(viif_dev,
+ REG_VDM_TPORT_X_SIZE(IDX_TPORT_L1_DPC_M),
+ VIIF_DPC_TABLE_BYTES);
+ val |= MASK_VDM_T_ENABLE_L1_DPC_M;
+ }
+
+ if (table_l) {
+ viif_capture_write(viif_dev,
+ REG_VDM_TPORT_X_STADR(IDX_TPORT_L1_DPC_L),
+ (u32)table_l);
+ viif_capture_write(viif_dev,
+ REG_VDM_TPORT_X_SIZE(IDX_TPORT_L1_DPC_L),
+ VIIF_DPC_TABLE_BYTES);
+ val |= MASK_VDM_T_ENABLE_L1_DPC_L;
+ }
+
+ val |= (viif_capture_read(viif_dev, REG_VDM_T_ENABLE) &
+ ~MASK_VDM_T_ENABLE_L1_DPC);
+ viif_capture_write(viif_dev, REG_VDM_T_ENABLE, val);
+}
+
+static void viif_l1_set_dpc(struct viif_device *viif_dev,
+ const struct viif_l1_dpc_config *arg)
+{
+ const struct viif_l1_dpc *param_h, *param_m, *param_l;
+ dma_addr_t table_h = 0, table_m = 0, table_l = 0;
+ u32 val;
+
+ if (arg->param_h.abpc_sta_en) {
+ memcpy(viif_dev->tables->dpc_table_h, arg->table_h,
+ VIIF_DPC_TABLE_BYTES);
+ table_h = viif_dev->tables_dma +
+ offsetof(struct viif_table_area, dpc_table_h);
+ }
+ if (arg->param_m.abpc_sta_en) {
+ memcpy(viif_dev->tables->dpc_table_m, arg->table_m,
+ VIIF_DPC_TABLE_BYTES);
+ table_m = viif_dev->tables_dma +
+ offsetof(struct viif_table_area, dpc_table_m);
+ }
+ if (arg->param_l.abpc_sta_en) {
+ memcpy(viif_dev->tables->dpc_table_l, arg->table_l,
+ VIIF_DPC_TABLE_BYTES);
+ table_l = viif_dev->tables_dma +
+ offsetof(struct viif_table_area, dpc_table_l);
+ }
+
+ guard(spinlock_irqsave)(&viif_dev->regbuf_lock);
+ guard(viif_isp)(viif_dev);
+
+ dpc_table_transmission(viif_dev, table_h, table_m, table_l);
+
+ param_h = &arg->param_h;
+ param_m = &arg->param_m;
+ param_l = &arg->param_l;
+
+ val = (param_h->abpc_sta_en) ? MASK_L1_ABPC_ENABLE_H : 0;
+ val |= (param_m->abpc_sta_en) ? MASK_L1_ABPC_ENABLE_M : 0;
+ val |= (param_l->abpc_sta_en) ? MASK_L1_ABPC_ENABLE_L : 0;
+ viif_capture_write(viif_dev, REG_L1_ABPC012_STA_EN, val);
+
+ val = (param_h->abpc_dyn_en) ? MASK_L1_ABPC_ENABLE_H : 0;
+ val |= (param_m->abpc_dyn_en) ? MASK_L1_ABPC_ENABLE_M : 0;
+ val |= (param_l->abpc_dyn_en) ? MASK_L1_ABPC_ENABLE_L : 0;
+ viif_capture_write(viif_dev, REG_L1_ABPC012_DYN_EN, val);
+
+ val = (param_h->abpc_dyn_mode == VIIF_L1_DPC_2PIXEL) ?
+ MASK_L1_ABPC_DYN_MODE_2PIXEL_H :
+ 0;
+ val |= (param_m->abpc_dyn_mode == VIIF_L1_DPC_2PIXEL) ?
+ MASK_L1_ABPC_DYN_MODE_2PIXEL_M :
+ 0;
+ val |= (param_l->abpc_dyn_mode == VIIF_L1_DPC_2PIXEL) ?
+ MASK_L1_ABPC_DYN_MODE_2PIXEL_L :
+ 0;
+ viif_capture_write(viif_dev, REG_L1_ABPC012_DYN_MODE, val);
+
+ /* Setup param_h */
+ viif_capture_write(viif_dev, REG_L1_ABPC0_RATIO_LIMIT,
+ param_h->abpc_ratio_limit);
+ viif_capture_write(viif_dev, REG_L1_ABPC0_DARK_LIMIT,
+ param_h->abpc_dark_limit);
+ viif_capture_write(viif_dev, REG_L1_ABPC0_SN_COEF_W_AG_MIN,
+ param_h->abpc_sn_coef_w_ag_min);
+ viif_capture_write(viif_dev, REG_L1_ABPC0_SN_COEF_W_AG_MID,
+ param_h->abpc_sn_coef_w_ag_mid);
+ viif_capture_write(viif_dev, REG_L1_ABPC0_SN_COEF_W_AG_MAX,
+ param_h->abpc_sn_coef_w_ag_max);
+ viif_capture_write(viif_dev, REG_L1_ABPC0_SN_COEF_B_AG_MIN,
+ param_h->abpc_sn_coef_b_ag_min);
+ viif_capture_write(viif_dev, REG_L1_ABPC0_SN_COEF_B_AG_MID,
+ param_h->abpc_sn_coef_b_ag_mid);
+ viif_capture_write(viif_dev, REG_L1_ABPC0_SN_COEF_B_AG_MAX,
+ param_h->abpc_sn_coef_b_ag_max);
+ viif_capture_write(viif_dev, REG_L1_ABPC0_SN_COEF_W_TH_MIN,
+ param_h->abpc_sn_coef_w_th_min);
+ viif_capture_write(viif_dev, REG_L1_ABPC0_SN_COEF_W_TH_MAX,
+ param_h->abpc_sn_coef_w_th_max);
+ viif_capture_write(viif_dev, REG_L1_ABPC0_SN_COEF_B_TH_MIN,
+ param_h->abpc_sn_coef_b_th_min);
+ viif_capture_write(viif_dev, REG_L1_ABPC0_SN_COEF_B_TH_MAX,
+ param_h->abpc_sn_coef_b_th_max);
+
+ /* Setup param_m */
+ viif_capture_write(viif_dev, REG_L1_ABPC1_RATIO_LIMIT,
+ param_m->abpc_ratio_limit);
+ viif_capture_write(viif_dev, REG_L1_ABPC1_DARK_LIMIT,
+ param_m->abpc_dark_limit);
+ viif_capture_write(viif_dev, REG_L1_ABPC1_SN_COEF_W_AG_MIN,
+ param_m->abpc_sn_coef_w_ag_min);
+ viif_capture_write(viif_dev, REG_L1_ABPC1_SN_COEF_W_AG_MID,
+ param_m->abpc_sn_coef_w_ag_mid);
+ viif_capture_write(viif_dev, REG_L1_ABPC1_SN_COEF_W_AG_MAX,
+ param_m->abpc_sn_coef_w_ag_max);
+ viif_capture_write(viif_dev, REG_L1_ABPC1_SN_COEF_B_AG_MIN,
+ param_m->abpc_sn_coef_b_ag_min);
+ viif_capture_write(viif_dev, REG_L1_ABPC1_SN_COEF_B_AG_MID,
+ param_m->abpc_sn_coef_b_ag_mid);
+ viif_capture_write(viif_dev, REG_L1_ABPC1_SN_COEF_B_AG_MAX,
+ param_m->abpc_sn_coef_b_ag_max);
+ viif_capture_write(viif_dev, REG_L1_ABPC1_SN_COEF_W_TH_MIN,
+ param_m->abpc_sn_coef_w_th_min);
+ viif_capture_write(viif_dev, REG_L1_ABPC1_SN_COEF_W_TH_MAX,
+ param_m->abpc_sn_coef_w_th_max);
+ viif_capture_write(viif_dev, REG_L1_ABPC1_SN_COEF_B_TH_MIN,
+ param_m->abpc_sn_coef_b_th_min);
+ viif_capture_write(viif_dev, REG_L1_ABPC1_SN_COEF_B_TH_MAX,
+ param_m->abpc_sn_coef_b_th_max);
+
+ /* Setup param_l */
+ viif_capture_write(viif_dev, REG_L1_ABPC2_RATIO_LIMIT,
+ param_l->abpc_ratio_limit);
+ viif_capture_write(viif_dev, REG_L1_ABPC2_DARK_LIMIT,
+ param_l->abpc_dark_limit);
+ viif_capture_write(viif_dev, REG_L1_ABPC2_SN_COEF_W_AG_MIN,
+ param_l->abpc_sn_coef_w_ag_min);
+ viif_capture_write(viif_dev, REG_L1_ABPC2_SN_COEF_W_AG_MID,
+ param_l->abpc_sn_coef_w_ag_mid);
+ viif_capture_write(viif_dev, REG_L1_ABPC2_SN_COEF_W_AG_MAX,
+ param_l->abpc_sn_coef_w_ag_max);
+ viif_capture_write(viif_dev, REG_L1_ABPC2_SN_COEF_B_AG_MIN,
+ param_l->abpc_sn_coef_b_ag_min);
+ viif_capture_write(viif_dev, REG_L1_ABPC2_SN_COEF_B_AG_MID,
+ param_l->abpc_sn_coef_b_ag_mid);
+ viif_capture_write(viif_dev, REG_L1_ABPC2_SN_COEF_B_AG_MAX,
+ param_l->abpc_sn_coef_b_ag_max);
+ viif_capture_write(viif_dev, REG_L1_ABPC2_SN_COEF_W_TH_MIN,
+ param_l->abpc_sn_coef_w_th_min);
+ viif_capture_write(viif_dev, REG_L1_ABPC2_SN_COEF_W_TH_MAX,
+ param_l->abpc_sn_coef_w_th_max);
+ viif_capture_write(viif_dev, REG_L1_ABPC2_SN_COEF_B_TH_MIN,
+ param_l->abpc_sn_coef_b_th_min);
+ viif_capture_write(viif_dev, REG_L1_ABPC2_SN_COEF_B_TH_MAX,
+ param_l->abpc_sn_coef_b_th_max);
+}
+
+static void viif_l1_set_preset_white_balance(
+ struct viif_device *viif_dev,
+ const struct viif_l1_preset_white_balance_config *arg)
+{
+ const struct viif_l1_preset_wb *param_h, *param_m, *param_l;
+
+ guard(spinlock_irqsave)(&viif_dev->regbuf_lock);
+ guard(viif_isp)(viif_dev);
+
+ param_h = &arg->param_h;
+ param_m = &arg->param_m;
+ param_l = &arg->param_l;
+
+ viif_capture_write(viif_dev, REG_L1_PWHB_DSTMAXVAL, arg->dstmaxval);
+
+ viif_capture_write(viif_dev, REG_L1_PWHB_H_GR, param_h->gain_gr);
+ viif_capture_write(viif_dev, REG_L1_PWHB_HR, param_h->gain_r);
+ viif_capture_write(viif_dev, REG_L1_PWHB_HB, param_h->gain_b);
+ viif_capture_write(viif_dev, REG_L1_PWHB_H_GB, param_h->gain_gb);
+
+ viif_capture_write(viif_dev, REG_L1_PWHB_M_GR, param_m->gain_gr);
+ viif_capture_write(viif_dev, REG_L1_PWHB_MR, param_m->gain_r);
+ viif_capture_write(viif_dev, REG_L1_PWHB_MB, param_m->gain_b);
+ viif_capture_write(viif_dev, REG_L1_PWHB_M_GB, param_m->gain_gb);
+
+ viif_capture_write(viif_dev, REG_L1_PWHB_L_GR, param_l->gain_gr);
+ viif_capture_write(viif_dev, REG_L1_PWHB_LR, param_l->gain_r);
+ viif_capture_write(viif_dev, REG_L1_PWHB_LB, param_l->gain_b);
+ viif_capture_write(viif_dev, REG_L1_PWHB_L_GB, param_l->gain_gb);
+}
+
+static void viif_l1_set_raw_color_noise_reduction(
+ struct viif_device *viif_dev,
+ const struct viif_l1_raw_color_noise_reduction_config *arg)
+{
+ const struct viif_l1_raw_color_noise_reduction *params[] = {
+ &arg->param_h,
+ &arg->param_m,
+ &arg->param_l,
+ };
+ int i;
+
+ guard(spinlock_irqsave)(&viif_dev->regbuf_lock);
+ guard(viif_isp)(viif_dev);
+
+ for (i = 0; i < 3; i++) {
+ const struct viif_l1_raw_color_noise_reduction *param =
+ params[i];
+ /* param_h */
+ viif_capture_write(viif_dev, REG_L1_RCNR_X_SW(i),
+ param->rcnr_sw ? 1 : 0);
+
+ viif_capture_write(viif_dev, REG_L1_RCNR_X_CNF_DARK_AG0(i),
+ param->rcnr_cnf_dark_ag0);
+ viif_capture_write(viif_dev, REG_L1_RCNR_X_CNF_DARK_AG1(i),
+ param->rcnr_cnf_dark_ag1);
+ viif_capture_write(viif_dev, REG_L1_RCNR_X_CNF_DARK_AG2(i),
+ param->rcnr_cnf_dark_ag2);
+
+ viif_capture_write(viif_dev, REG_L1_RCNR_X_CNF_RATIO_AG0(i),
+ param->rcnr_cnf_ratio_ag0);
+ viif_capture_write(viif_dev, REG_L1_RCNR_X_CNF_RATIO_AG1(i),
+ param->rcnr_cnf_ratio_ag1);
+ viif_capture_write(viif_dev, REG_L1_RCNR_X_CNF_RATIO_AG2(i),
+ param->rcnr_cnf_ratio_ag2);
+
+ viif_capture_write(viif_dev, REG_L1_RCNR_X_CNF_CLIP_GAIN_R(i),
+ param->rcnr_cnf_clip_gain_r);
+ viif_capture_write(viif_dev, REG_L1_RCNR_X_CNF_CLIP_GAIN_G(i),
+ param->rcnr_cnf_clip_gain_g);
+ viif_capture_write(viif_dev, REG_L1_RCNR_X_CNF_CLIP_GAIN_B(i),
+ param->rcnr_cnf_clip_gain_b);
+
+ viif_capture_write(viif_dev, REG_L1_RCNR_X_A1L_DARK_AG0(i),
+ param->rcnr_a1l_dark_ag0);
+ viif_capture_write(viif_dev, REG_L1_RCNR_X_A1L_DARK_AG1(i),
+ param->rcnr_a1l_dark_ag1);
+ viif_capture_write(viif_dev, REG_L1_RCNR_X_A1L_DARK_AG2(i),
+ param->rcnr_a1l_dark_ag2);
+
+ viif_capture_write(viif_dev, REG_L1_RCNR_X_A1L_RATIO_AG0(i),
+ param->rcnr_a1l_ratio_ag0);
+ viif_capture_write(viif_dev, REG_L1_RCNR_X_A1L_RATIO_AG1(i),
+ param->rcnr_a1l_ratio_ag1);
+ viif_capture_write(viif_dev, REG_L1_RCNR_X_A1L_RATIO_AG2(i),
+ param->rcnr_a1l_ratio_ag2);
+
+ viif_capture_write(viif_dev, REG_L1_RCNR_X_INF_ZERO_CLIP(i),
+ param->rcnr_inf_zero_clip);
+
+ viif_capture_write(viif_dev, REG_L1_RCNR_X_MERGE_D2BLEND_AG0(i),
+ param->rcnr_merge_d2blend_ag0);
+ viif_capture_write(viif_dev, REG_L1_RCNR_X_MERGE_D2BLEND_AG1(i),
+ param->rcnr_merge_d2blend_ag1);
+ viif_capture_write(viif_dev, REG_L1_RCNR_X_MERGE_D2BLEND_AG2(i),
+ param->rcnr_merge_d2blend_ag2);
+ viif_capture_write(viif_dev, REG_L1_RCNR_X_MERGE_BLACK(i),
+ param->rcnr_merge_black);
+ viif_capture_write(viif_dev, REG_L1_RCNR_X_MERGE_MINDIV(i),
+ param->rcnr_merge_mindiv);
+
+ viif_capture_write(viif_dev, REG_L1_RCNR_X_HRY_TYPE(i),
+ param->rcnr_hry_type);
+
+ viif_capture_write(viif_dev, REG_L1_RCNR_X_ANF_BLEND_AG0(i),
+ param->rcnr_anf_blend_ag0);
+ viif_capture_write(viif_dev, REG_L1_RCNR_X_ANF_BLEND_AG1(i),
+ param->rcnr_anf_blend_ag1);
+ viif_capture_write(viif_dev, REG_L1_RCNR_X_ANF_BLEND_AG2(i),
+ param->rcnr_anf_blend_ag2);
+
+ viif_capture_write(viif_dev, REG_L1_RCNR_X_LPF_THRESHOLD(i),
+ param->rcnr_lpf_threshold);
+
+ viif_capture_write(viif_dev, REG_L1_RCNR_X_MERGE_HLBLEND_AG0(i),
+ param->rcnr_merge_hlblend_ag0);
+ viif_capture_write(viif_dev, REG_L1_RCNR_X_MERGE_HLBLEND_AG1(i),
+ param->rcnr_merge_hlblend_ag1);
+ viif_capture_write(viif_dev, REG_L1_RCNR_X_MERGE_HLBLEND_AG2(i),
+ param->rcnr_merge_hlblend_ag2);
+
+ viif_capture_write(viif_dev, REG_L1_RCNR_X_GNR_SW(i),
+ param->rcnr_gnr_sw ? 1 : 0);
+
+ if (param->rcnr_gnr_sw) {
+ viif_capture_write(viif_dev, REG_L1_RCNR_X_GNR_RATIO(i),
+ param->rcnr_gnr_ratio);
+ viif_capture_write(viif_dev,
+ REG_L1_RCNR_X_GNR_WIDE_EN(i),
+ param->rcnr_gnr_wide_en ? 1 : 0);
+ }
+ }
+}
+
+static void viif_l1_set_hdrs(struct viif_device *viif_dev,
+ const struct viif_l1_hdrs_config *arg)
+{
+ guard(spinlock_irqsave)(&viif_dev->regbuf_lock);
+ guard(viif_isp)(viif_dev);
+
+ viif_capture_write(viif_dev, REG_L1_HDRS_HDRMODE, arg->hdrs_hdr_mode);
+
+ viif_capture_write(viif_dev, REG_L1_HDRS_HDRRATIO_M,
+ arg->hdrs_hdr_ratio_m);
+ viif_capture_write(viif_dev, REG_L1_HDRS_HDRRATIO_L,
+ arg->hdrs_hdr_ratio_l);
+ viif_capture_write(viif_dev, REG_L1_HDRS_HDRRATIO_E,
+ arg->hdrs_hdr_ratio_e);
+
+ viif_capture_write(viif_dev, REG_L1_HDRS_DG_H, arg->hdrs_dg_h);
+ viif_capture_write(viif_dev, REG_L1_HDRS_DG_M, arg->hdrs_dg_m);
+ viif_capture_write(viif_dev, REG_L1_HDRS_DG_L, arg->hdrs_dg_l);
+ viif_capture_write(viif_dev, REG_L1_HDRS_DG_E, arg->hdrs_dg_e);
+
+ viif_capture_write(viif_dev, REG_L1_HDRS_BLENDEND_H,
+ arg->hdrs_blendend_h);
+ viif_capture_write(viif_dev, REG_L1_HDRS_BLENDEND_M,
+ arg->hdrs_blendend_m);
+ viif_capture_write(viif_dev, REG_L1_HDRS_BLENDEND_E,
+ arg->hdrs_blendend_e);
+
+ viif_capture_write(viif_dev, REG_L1_HDRS_BLENDBEG_H,
+ arg->hdrs_blendbeg_h);
+ viif_capture_write(viif_dev, REG_L1_HDRS_BLENDBEG_M,
+ arg->hdrs_blendbeg_m);
+ viif_capture_write(viif_dev, REG_L1_HDRS_BLENDBEG_E,
+ arg->hdrs_blendbeg_e);
+
+ viif_capture_write(viif_dev, REG_L1_HDRS_LEDMODE_ON,
+ arg->hdrs_led_mode_on ? 1 : 0);
+ viif_capture_write(viif_dev, REG_L1_HDRS_DSTMAXVAL,
+ arg->hdrs_dst_max_val);
+}
+
+static void viif_l1_set_black_level_correction(
+ struct viif_device *viif_dev,
+ const struct viif_l1_black_level_correction_config *arg)
+{
+ guard(spinlock_irqsave)(&viif_dev->regbuf_lock);
+ guard(viif_isp)(viif_dev);
+
+ viif_capture_write(viif_dev, REG_L1_BLVC_SRCBLACKLEVEL_GR,
+ arg->srcblacklevel_gr);
+ viif_capture_write(viif_dev, REG_L1_BLVC_SRCBLACKLEVEL_R,
+ arg->srcblacklevel_r);
+ viif_capture_write(viif_dev, REG_L1_BLVC_SRCBLACKLEVEL_B,
+ arg->srcblacklevel_b);
+ viif_capture_write(viif_dev, REG_L1_BLVC_SRCBLACKLEVELGB,
+ arg->srcblacklevel_gb);
+
+ viif_capture_write(viif_dev, REG_L1_BLVC_MULTVAL_GR, arg->mulval_gr);
+ viif_capture_write(viif_dev, REG_L1_BLVC_MULTVAL_R, arg->mulval_r);
+ viif_capture_write(viif_dev, REG_L1_BLVC_MULTVAL_B, arg->mulval_b);
+ viif_capture_write(viif_dev, REG_L1_BLVC_MULTVAL_GB, arg->mulval_gb);
+
+ viif_capture_write(viif_dev, REG_L1_BLVC_DSTMAXVAL, arg->dstmaxval);
+}
+
+static void lsc_table_transmission(struct viif_device *viif_dev,
+ dma_addr_t table_gr, dma_addr_t table_r,
+ dma_addr_t table_b, dma_addr_t table_gb)
+{
+ u32 val = 0;
+
+ viif_config_vdm_tgroup(viif_dev, IDX_TGROUP_L1_ISP);
+
+ if (table_gr) {
+ viif_capture_write(viif_dev,
+ REG_VDM_TPORT_X_STADR(IDX_TPORT_L1_LSSC_GR),
+ (u32)table_gr);
+ viif_capture_write(viif_dev,
+ REG_VDM_TPORT_X_SIZE(IDX_TPORT_L1_LSSC_GR),
+ VIIF_LSC_TABLE_BYTES);
+ val |= MASK_VDM_T_ENABLE_L1_LSSC_GR;
+ }
+
+ if (table_r) {
+ viif_capture_write(viif_dev,
+ REG_VDM_TPORT_X_STADR(IDX_TPORT_L1_LSSC_R),
+ (u32)table_r);
+ viif_capture_write(viif_dev,
+ REG_VDM_TPORT_X_SIZE(IDX_TPORT_L1_LSSC_R),
+ VIIF_LSC_TABLE_BYTES);
+ val |= MASK_VDM_T_ENABLE_L1_LSSC_R;
+ }
+
+ if (table_b) {
+ viif_capture_write(viif_dev,
+ REG_VDM_TPORT_X_STADR(IDX_TPORT_L1_LSSC_B),
+ (u32)table_b);
+ viif_capture_write(viif_dev,
+ REG_VDM_TPORT_X_SIZE(IDX_TPORT_L1_LSSC_B),
+ VIIF_LSC_TABLE_BYTES);
+ val |= MASK_VDM_T_ENABLE_L1_LSSC_B;
+ }
+
+ if (table_gb) {
+ viif_capture_write(viif_dev,
+ REG_VDM_TPORT_X_STADR(IDX_TPORT_L1_LSSC_GB),
+ (u32)table_gb);
+ viif_capture_write(viif_dev,
+ REG_VDM_TPORT_X_SIZE(IDX_TPORT_L1_LSSC_GB),
+ VIIF_LSC_TABLE_BYTES);
+ val |= MASK_VDM_T_ENABLE_L1_LSSC_GB;
+ }
+
+ val |= (viif_capture_read(viif_dev, REG_VDM_T_ENABLE) &
+ ~MASK_VDM_T_ENABLE_L1_LSSC);
+ viif_capture_write(viif_dev, REG_VDM_T_ENABLE, val);
+}
+
+#define PACK_PARA_COEF(max, min) \
+ (FIELD_PREP(0x1fff0000, (max)) | FIELD_PREP(0x1fff, (min)))
+
+static void viif_l1_set_lsc(struct viif_device *viif_dev,
+ const struct viif_l1_lsc_config *arg)
+{
+ dma_addr_t table_gr = 0, table_gb = 0, table_r = 0, table_b = 0;
+ u32 val;
+
+ if (!arg->enable) {
+ guard(spinlock_irqsave)(&viif_dev->regbuf_lock);
+ guard(viif_isp)(viif_dev);
+
+ viif_capture_write(viif_dev, REG_L1_LSSC_EN, 0);
+
+ return;
+ }
+
+ if (arg->enable & VIIF_L1_LSC_GRID_EN_MASK) {
+ memcpy(viif_dev->tables->lsc_table_gr, arg->table_gr,
+ VIIF_LSC_TABLE_BYTES);
+ memcpy(viif_dev->tables->lsc_table_r, arg->table_r,
+ VIIF_LSC_TABLE_BYTES);
+ memcpy(viif_dev->tables->lsc_table_b, arg->table_b,
+ VIIF_LSC_TABLE_BYTES);
+ memcpy(viif_dev->tables->lsc_table_gb, arg->table_gb,
+ VIIF_LSC_TABLE_BYTES);
+ table_gr = viif_dev->tables_dma +
+ offsetof(struct viif_table_area, lsc_table_gr);
+ table_r = viif_dev->tables_dma +
+ offsetof(struct viif_table_area, lsc_table_r);
+ table_b = viif_dev->tables_dma +
+ offsetof(struct viif_table_area, lsc_table_b);
+ table_gb = viif_dev->tables_dma +
+ offsetof(struct viif_table_area, lsc_table_gb);
+ }
+
+ guard(spinlock_irqsave)(&viif_dev->regbuf_lock);
+ guard(viif_isp)(viif_dev);
+
+ lsc_table_transmission(viif_dev, table_gr, table_r, table_b, table_gb);
+
+ /* Parabola shading */
+ if (arg->enable & VIIF_L1_LSC_PARABOLA_EN_MASK) {
+ const struct viif_l1_lsc_parabola_param *parabola_param =
+ &arg->param.lssc_parabola_param;
+ const struct viif_l1_lsc_parabola_ag_param *params[] = {
+ ¶bola_param->r_2d, ¶bola_param->r_4d,
+ ¶bola_param->gr_2d, ¶bola_param->gr_4d,
+ ¶bola_param->gb_2d, ¶bola_param->gb_4d,
+ ¶bola_param->b_2d, ¶bola_param->b_4d,
+ };
+ int i;
+
+ viif_capture_write(viif_dev, REG_L1_LSSC_PARA_EN, 1);
+
+ viif_capture_write(viif_dev, REG_L1_LSSC_PARA_H_CENTER,
+ parabola_param->lssc_para_h_center);
+ viif_capture_write(viif_dev, REG_L1_LSSC_PARA_V_CENTER,
+ parabola_param->lssc_para_v_center);
+
+ viif_capture_write(viif_dev, REG_L1_LSSC_PARA_H_GAIN,
+ parabola_param->lssc_para_h_gain);
+ viif_capture_write(viif_dev, REG_L1_LSSC_PARA_V_GAIN,
+ parabola_param->lssc_para_v_gain);
+
+ viif_capture_write(viif_dev, REG_L1_LSSC_PARA_MGSEL2,
+ parabola_param->lssc_para_mgsel2);
+ viif_capture_write(viif_dev, REG_L1_LSSC_PARA_MGSEL4,
+ parabola_param->lssc_para_mgsel4);
+
+ for (i = 0; i < ARRAY_SIZE(params); i++) {
+ const struct viif_l1_lsc_parabola_ag_param *p =
+ params[i];
+
+ viif_capture_write(
+ viif_dev, REG_L1_LSSC_PARA_COEF_X_H_L(i),
+ PACK_PARA_COEF(p->lssc_paracoef_h_l_max,
+ p->lssc_paracoef_h_l_min));
+ viif_capture_write(
+ viif_dev, REG_L1_LSSC_PARA_COEF_X_H_R(i),
+ PACK_PARA_COEF(p->lssc_paracoef_h_r_max,
+ p->lssc_paracoef_h_r_min));
+ viif_capture_write(
+ viif_dev, REG_L1_LSSC_PARA_COEF_X_V_U(i),
+ PACK_PARA_COEF(p->lssc_paracoef_v_u_max,
+ p->lssc_paracoef_v_u_min));
+ viif_capture_write(
+ viif_dev, REG_L1_LSSC_PARA_COEF_X_V_D(i),
+ PACK_PARA_COEF(p->lssc_paracoef_v_d_max,
+ p->lssc_paracoef_v_d_min));
+ viif_capture_write(
+ viif_dev, REG_L1_LSSC_PARA_COEF_X_HV_LU(i),
+ PACK_PARA_COEF(p->lssc_paracoef_hv_lu_max,
+ p->lssc_paracoef_hv_lu_min));
+ viif_capture_write(
+ viif_dev, REG_L1_LSSC_PARA_COEF_X_HV_RU(i),
+ PACK_PARA_COEF(p->lssc_paracoef_hv_ru_max,
+ p->lssc_paracoef_hv_ru_min));
+ viif_capture_write(
+ viif_dev, REG_L1_LSSC_PARA_COEF_X_HV_LD(i),
+ PACK_PARA_COEF(p->lssc_paracoef_hv_ld_max,
+ p->lssc_paracoef_hv_ld_min));
+ viif_capture_write(
+ viif_dev, REG_L1_LSSC_PARA_COEF_X_HV_RD(i),
+ PACK_PARA_COEF(p->lssc_paracoef_hv_rd_max,
+ p->lssc_paracoef_hv_rd_min));
+ }
+ } else {
+ viif_capture_write(viif_dev, REG_L1_LSSC_PARA_EN, 0);
+ }
+
+ /* Grid shading */
+ if (arg->enable & VIIF_L1_LSC_GRID_EN_MASK) {
+ const struct viif_l1_lsc_grid_param *grid_param =
+ &arg->param.lssc_grid_param;
+
+ viif_capture_write(viif_dev, REG_L1_LSSC_GRID_EN, 1);
+ viif_capture_write(viif_dev, REG_L1_LSSC_GRID_H_SIZE,
+ ffs(grid_param->lssc_grid_h_size));
+ viif_capture_write(viif_dev, REG_L1_LSSC_GRID_V_SIZE,
+ ffs(grid_param->lssc_grid_v_size));
+ viif_capture_write(viif_dev, REG_L1_LSSC_GRID_H_CENTER,
+ grid_param->lssc_grid_h_center);
+ viif_capture_write(viif_dev, REG_L1_LSSC_GRID_V_CENTER,
+ grid_param->lssc_grid_v_center);
+ viif_capture_write(viif_dev, REG_L1_LSSC_GRID_MGSEL,
+ grid_param->lssc_grid_mgsel);
+ } else {
+ viif_capture_write(viif_dev, REG_L1_LSSC_GRID_EN, 0);
+ }
+
+ /* Preset white balance */
+ val = (arg->param.lssc_pwhb_r_gain_max << 16) |
+ (arg->param.lssc_pwhb_r_gain_min);
+ viif_capture_write(viif_dev, REG_L1_LSSC_PWHB_R_GAIN, val);
+
+ val = (arg->param.lssc_pwhb_gr_gain_max << 16) |
+ (arg->param.lssc_pwhb_gr_gain_min);
+ viif_capture_write(viif_dev, REG_L1_LSSC_PWHB_GR_GAIN, val);
+
+ val = (arg->param.lssc_pwhb_gb_gain_max << 16) |
+ (arg->param.lssc_pwhb_gb_gain_min);
+ viif_capture_write(viif_dev, REG_L1_LSSC_PWHB_GB_GAIN, val);
+
+ val = (arg->param.lssc_pwhb_b_gain_max << 16) |
+ (arg->param.lssc_pwhb_b_gain_min);
+ viif_capture_write(viif_dev, REG_L1_LSSC_PWHB_B_GAIN, val);
+
+ viif_capture_write(viif_dev, REG_L1_LSSC_EN, 1);
+}
+
+static void
+viif_l1_set_main_process(struct viif_device *viif_dev,
+ const struct viif_l1_main_process_config *arg)
+{
+ u32 val;
+
+ guard(spinlock_irqsave)(&viif_dev->regbuf_lock);
+ guard(viif_isp)(viif_dev);
+
+ viif_capture_write(viif_dev, REG_L1_MPRO_CONF, arg->damp_lsbsel << 4);
+ viif_capture_write(viif_dev, REG_L1_MPRO_LCS_MODE, arg->demosaic_mode);
+
+ if (arg->colormat_enable) {
+ const struct viif_l1_color_matrix_correction *color_matrix =
+ &arg->colormat_param;
+
+ viif_capture_write(viif_dev, REG_L1_MPRO_SW, 1);
+
+ val = (u32)color_matrix->coef_rmg_min & 0xffffU;
+ viif_capture_write(viif_dev, REG_L1_MPRO_LM0_RMG_MIN, val);
+
+ val = (u32)color_matrix->coef_rmg_max & 0xffffU;
+ viif_capture_write(viif_dev, REG_L1_MPRO_LM0_RMG_MAX, val);
+
+ val = (u32)color_matrix->coef_rmb_min & 0xffffU;
+ viif_capture_write(viif_dev, REG_L1_MPRO_LM0_RMB_MIN, val);
+
+ val = (u32)color_matrix->coef_rmb_max & 0xffffU;
+ viif_capture_write(viif_dev, REG_L1_MPRO_LM0_RMB_MAX, val);
+
+ val = (u32)color_matrix->coef_gmr_min & 0xffffU;
+ viif_capture_write(viif_dev, REG_L1_MPRO_LM0_GMR_MIN, val);
+
+ val = (u32)color_matrix->coef_gmr_max & 0xffffU;
+ viif_capture_write(viif_dev, REG_L1_MPRO_LM0_GMR_MAX, val);
+
+ val = (u32)color_matrix->coef_gmb_min & 0xffffU;
+ viif_capture_write(viif_dev, REG_L1_MPRO_LM0_GMB_MIN, val);
+
+ val = (u32)color_matrix->coef_gmb_max & 0xffffU;
+ viif_capture_write(viif_dev, REG_L1_MPRO_LM0_GMB_MAX, val);
+
+ val = (u32)color_matrix->coef_bmr_min & 0xffffU;
+ viif_capture_write(viif_dev, REG_L1_MPRO_LM0_BMR_MIN, val);
+
+ val = (u32)color_matrix->coef_bmr_max & 0xffffU;
+ viif_capture_write(viif_dev, REG_L1_MPRO_LM0_BMR_MAX, val);
+
+ val = (u32)color_matrix->coef_bmg_min & 0xffffU;
+ viif_capture_write(viif_dev, REG_L1_MPRO_LM0_BMG_MIN, val);
+
+ val = (u32)color_matrix->coef_bmg_max & 0xffffU;
+ viif_capture_write(viif_dev, REG_L1_MPRO_LM0_BMG_MAX, val);
+
+ viif_capture_write(viif_dev, REG_L1_MPRO_DST_MINVAL,
+ (u32)color_matrix->dst_minval);
+ } else {
+ viif_capture_write(viif_dev, REG_L1_MPRO_SW, 0);
+ }
+
+ viif_capture_write(viif_dev, REG_L1_MPRO_DST_MAXVAL, arg->dst_maxval);
+}
+
+static void viif_l1_set_awb(struct viif_device *viif_dev,
+ const struct viif_l1_awb_config *arg)
+{
+ const struct viif_l1_awb *param = &arg->param;
+ u32 val, ygate_data;
+
+ guard(spinlock_irqsave)(&viif_dev->regbuf_lock);
+ guard(viif_isp)(viif_dev);
+
+ viif_capture_write(viif_dev, REG_L1_AWHB_WBMRG, arg->awhb_wbmrg);
+ viif_capture_write(viif_dev, REG_L1_AWHB_WBMGG, arg->awhb_wbmgg);
+ viif_capture_write(viif_dev, REG_L1_AWHB_WBMBG, arg->awhb_wbmbg);
+
+ val = viif_capture_read(viif_dev, REG_L1_AWHB_SW) & ~MASK_L1_AWHB_SW_EN;
+
+ /* Disabling AWB */
+ if (!arg->enable) {
+ viif_capture_write(viif_dev, REG_L1_AWHB_SW, val);
+ return;
+ }
+
+ /* Enabling AWB */
+ viif_capture_write(viif_dev, REG_L1_AWHB_SW, val | MASK_L1_AWHB_SW_EN);
+
+ if (param->awhb_ygate_data == 64)
+ ygate_data = 0;
+ else if (param->awhb_ygate_data == 128)
+ ygate_data = 1;
+ else if (param->awhb_ygate_data == 256)
+ ygate_data = 2;
+ else
+ ygate_data = 3;
+
+ val = param->awhb_ygate_sel ? MASK_L1_AWHB_GATE_YGATE_SEL : 0;
+ val |= FIELD_PREP(MASK_L1_AWHB_GATE_YGATE_DATA, ygate_data);
+ val |= FIELD_PREP(MASK_L1_AWHB_GATE_CGRANGE, param->awhb_cgrange);
+ viif_capture_write(viif_dev, REG_L1_AWHB_GATE_CONF0, val);
+
+ val = param->awhb_ygatesw ? MASK_L1_AWHB_GATE_YGATESW : 0;
+ val |= param->awhb_hexsw ? MASK_L1_AWHB_GATE_HEXSW : 0;
+ val |= FIELD_PREP(MASK_L1_AWHB_GATE_AREAMODE, param->awhb_areamode);
+ viif_capture_write(viif_dev, REG_L1_AWHB_GATE_CONF1, val);
+
+ viif_capture_write(viif_dev, REG_L1_AWHB_AREA_HSIZE,
+ param->awhb_area_hsize);
+ viif_capture_write(viif_dev, REG_L1_AWHB_AREA_VSIZE,
+ param->awhb_area_vsize);
+ viif_capture_write(viif_dev, REG_L1_AWHB_AREA_HOFS,
+ param->awhb_area_hofs);
+ viif_capture_write(viif_dev, REG_L1_AWHB_AREA_VOFS,
+ param->awhb_area_vofs);
+
+ viif_capture_write(viif_dev, REG_L1_AWHB_AREA_MASKH,
+ param->awhb_area_maskh);
+ viif_capture_write(viif_dev, REG_L1_AWHB_AREA_MASKL,
+ param->awhb_area_maskl);
+
+ val = param->awhb_sq_sw[0] ? MASK_L1_AWHB_SQ_CONF_SQ1SW : 0;
+ val |= param->awhb_sq_pol[0] ? MASK_L1_AWHB_SQ_CONF_SQ1POL : 0;
+ val |= param->awhb_sq_sw[1] ? MASK_L1_AWHB_SQ_CONF_SQ2SW : 0;
+ val |= param->awhb_sq_pol[1] ? MASK_L1_AWHB_SQ_CONF_SQ2POL : 0;
+ val |= param->awhb_sq_sw[2] ? MASK_L1_AWHB_SQ_CONF_SQ3SW : 0;
+ val |= param->awhb_sq_pol[2] ? MASK_L1_AWHB_SQ_CONF_SQ3POL : 0;
+ viif_capture_write(viif_dev, REG_L1_AWHB_SQ_CONF, val);
+
+ viif_capture_write(viif_dev, REG_L1_AWHB_YGATEH,
+ (u32)param->awhb_ygateh);
+ viif_capture_write(viif_dev, REG_L1_AWHB_YGATEL,
+ (u32)param->awhb_ygatel);
+
+ viif_capture_write(viif_dev, REG_L1_AWHB_BYCUT0P, param->awhb_bycut0p);
+ viif_capture_write(viif_dev, REG_L1_AWHB_BYCUT0N, param->awhb_bycut0n);
+ viif_capture_write(viif_dev, REG_L1_AWHB_RYCUT0P, param->awhb_rycut0p);
+ viif_capture_write(viif_dev, REG_L1_AWHB_RYCUT0N, param->awhb_rycut0n);
+
+ val = (u32)param->awhb_rbcut0h & 0xffU;
+ viif_capture_write(viif_dev, REG_L1_AWHB_RBCUT0H, val);
+ val = (u32)param->awhb_rbcut0l & 0xffU;
+ viif_capture_write(viif_dev, REG_L1_AWHB_RBCUT0L, val);
+
+ val = (u32)param->awhb_bycut_h[0] & 0xffU;
+ viif_capture_write(viif_dev, REG_L1_AWHB_BYCUT1H, val);
+ viif_capture_write(viif_dev, REG_L1_AWHB_BYCUT1L,
+ param->awhb_bycut_l[0]);
+ val = (u32)param->awhb_bycut_h[1] & 0xffU;
+ viif_capture_write(viif_dev, REG_L1_AWHB_BYCUT2H, val);
+ viif_capture_write(viif_dev, REG_L1_AWHB_BYCUT2L,
+ param->awhb_bycut_l[1]);
+ val = (u32)param->awhb_bycut_h[2] & 0xffU;
+ viif_capture_write(viif_dev, REG_L1_AWHB_BYCUT3H, val);
+ viif_capture_write(viif_dev, REG_L1_AWHB_BYCUT3L,
+ param->awhb_bycut_l[2]);
+
+ val = (u32)param->awhb_rycut_h[0] & 0xffU;
+ viif_capture_write(viif_dev, REG_L1_AWHB_RYCUT1H, val);
+ viif_capture_write(viif_dev, REG_L1_AWHB_RYCUT1L,
+ param->awhb_rycut_l[0]);
+ val = (u32)param->awhb_rycut_h[1] & 0xffU;
+ viif_capture_write(viif_dev, REG_L1_AWHB_RYCUT2H, val);
+ viif_capture_write(viif_dev, REG_L1_AWHB_RYCUT2L,
+ param->awhb_rycut_l[1]);
+ val = (u32)param->awhb_rycut_h[2] & 0xffU;
+ viif_capture_write(viif_dev, REG_L1_AWHB_RYCUT3H, val);
+ viif_capture_write(viif_dev, REG_L1_AWHB_RYCUT3L,
+ param->awhb_rycut_l[2]);
+
+ val = (u32)param->awhb_awbsftu & 0xffU;
+ viif_capture_write(viif_dev, REG_L1_AWHB_AWBSFTU, val);
+ val = (u32)param->awhb_awbsftv & 0xffU;
+ viif_capture_write(viif_dev, REG_L1_AWHB_AWBSFTV, val);
+
+ val = (param->awhb_awbhuecor ? MASK_L1_AWHB_AWBSPD_HUECOR : 0);
+ val |= FIELD_PREP(MASK_L1_AWHB_AWBSPD_SPD, param->awhb_awbspd);
+ viif_capture_write(viif_dev, REG_L1_AWHB_AWBSPD, val);
+
+ viif_capture_write(viif_dev, REG_L1_AWHB_AWBULV, param->awhb_awbulv);
+ viif_capture_write(viif_dev, REG_L1_AWHB_AWBVLV, param->awhb_awbvlv);
+ viif_capture_write(viif_dev, REG_L1_AWHB_AWBWAIT,
+ (u32)param->awhb_awbwait);
+
+ viif_capture_write(viif_dev, REG_L1_AWHB_AWBONDOT,
+ param->awhb_awbondot);
+ viif_capture_write(viif_dev, REG_L1_AWHB_AWBFZTIM,
+ param->awhb_awbfztim);
+
+ viif_capture_write(viif_dev, REG_L1_AWHB_WBGRMAX,
+ (u32)param->awhb_wbgrmax);
+ viif_capture_write(viif_dev, REG_L1_AWHB_WBGBMAX,
+ (u32)param->awhb_wbgbmax);
+ viif_capture_write(viif_dev, REG_L1_AWHB_WBGRMIN,
+ (u32)param->awhb_wbgrmin);
+ viif_capture_write(viif_dev, REG_L1_AWHB_WBGBMIN,
+ (u32)param->awhb_wbgbmin);
+}
+
+static void viif_l1_lock_awb_gain(struct viif_device *viif_dev,
+ const u32 *enable)
+{
+ u32 val;
+
+ guard(spinlock_irqsave)(&viif_dev->regbuf_lock);
+ guard(viif_isp)(viif_dev);
+
+ val = viif_capture_read(viif_dev, REG_L1_AWHB_SW) &
+ ~MASK_L1_AWHB_SW_LOCK;
+ val |= (*enable ? MASK_L1_AWHB_SW_LOCK : 0);
+ viif_capture_write(viif_dev, REG_L1_AWHB_SW, val);
+}
+
+/* Convert the unit of time-period (from sysclk, to num lines in the image) */
+static u32 sysclk_to_numlines(u32 time_in_sysclk, unsigned long sys_rate,
+ const struct viif_img_clk *img_clk)
+{
+ u64 v1 = (u64)time_in_sysclk * img_clk->pixel_clock;
+ u64 v2 = (u64)img_clk->htotal_size * sys_rate;
+
+ return (u32)div64_u64(v1, v2);
+}
+
+static void viif_l1_set_hdrc(struct viif_device *viif_dev,
+ const struct viif_l1_hdrc_config *arg)
+{
+ const struct viif_l1_hdrc *param = &arg->param;
+ u32 val, sw_delay1;
+ int i;
+
+ guard(spinlock_irqsave)(&viif_dev->regbuf_lock);
+ guard(viif_isp)(viif_dev);
+
+ /* Disabling HDRC */
+ if (!arg->enable) {
+ viif_capture_write(viif_dev, REG_L1_HDRC_THR_SFT_AMT,
+ arg->hdrc_thr_sft_amt);
+ viif_capture_write(viif_dev, REG_L1_HDRC_EN, 0);
+ return;
+ }
+
+ /* Enabling HDRC */
+ viif_capture_write(viif_dev, REG_L1_HDRC_RATIO,
+ (param->hdrc_ratio - VIIF_L1_HDRC_RATIO_OFFSET));
+ viif_capture_write(viif_dev, REG_L1_HDRC_PT_RATIO,
+ param->hdrc_pt_ratio);
+
+ viif_capture_write(viif_dev, REG_L1_HDRC_PT_BLEND,
+ param->hdrc_pt_blend);
+ viif_capture_write(viif_dev, REG_L1_HDRC_PT_BLEND2,
+ param->hdrc_pt_blend2);
+
+ viif_capture_write(viif_dev, REG_L1_HDRC_PT_SAT, param->hdrc_pt_sat);
+ viif_capture_write(viif_dev, REG_L1_HDRC_TN_TYPE, param->hdrc_tn_type);
+
+ for (i = 0; i < LEN_L1_HDRC_UTN_TBL; i++)
+ viif_capture_write(viif_dev, REG_L1_HDRC_UTN_TBL(i),
+ param->hdrc_utn_tbl[i]);
+
+ viif_capture_write(viif_dev, REG_L1_HDRC_FLR_VAL, param->hdrc_flr_val);
+ viif_capture_write(viif_dev, REG_L1_HDRC_FLR_ADP,
+ param->hdrc_flr_adp ? 1 : 0);
+
+ viif_capture_write(viif_dev, REG_L1_HDRC_YBR_OFF,
+ param->hdrc_ybr_off ? 1 : 0);
+ viif_capture_write(viif_dev, REG_L1_HDRC_ORGY_BLEND,
+ param->hdrc_orgy_blend);
+
+ val = ((viif_capture_read(viif_dev, REG_L1_SYSM_HEIGHT)) % 64) / 2;
+ viif_capture_write(viif_dev, REG_L1_HDRC_MAR_TOP, val);
+ val = ((viif_capture_read(viif_dev, REG_L1_SYSM_WIDTH)) % 64) / 2;
+ viif_capture_write(viif_dev, REG_L1_HDRC_MAR_LEFT, val);
+
+ viif_capture_write(viif_dev, REG_L1_HDRC_EN, 1);
+
+ /* Update of sw_delay1 must be done when MAIN unit is NOT running. */
+ if (!viif_dev->run_flag_main) {
+ sw_delay1 = sysclk_to_numlines(VIIF_REGBUF_ACCESS_TIME,
+ viif_dev->clkrate,
+ &viif_dev->img_clk) +
+ VIIF_L1_DELAY_W_HDRC + 1;
+ val = viif_capture_read(viif_dev, REG_INT_M1_LINE) & 0xffffU;
+ val |= (sw_delay1 << 16);
+ viif_capture_write(viif_dev, REG_INT_M1_LINE, val);
+ /* M2_LINE is the same condition as M1_LINE */
+ viif_capture_write(viif_dev, REG_INT_M2_LINE, val);
+ }
+}
+
+static void viif_l1_set_hdrc_ltm(struct viif_device *viif_dev,
+ const struct viif_l1_hdrc_ltm_config *arg)
+{
+ int i;
+
+ guard(spinlock_irqsave)(&viif_dev->regbuf_lock);
+ guard(viif_isp)(viif_dev);
+
+ viif_capture_write(viif_dev, REG_L1_HDRC_TNP_MAX, arg->tnp_max);
+ viif_capture_write(viif_dev, REG_L1_HDRC_TNP_MAG, arg->tnp_mag);
+
+ for (i = 0; i < LEN_L1_HDRC_TNP_FIL; i++)
+ viif_capture_write(viif_dev, REG_L1_HDRC_TNP_FIL(i),
+ (u32)arg->tnp_fil[i]);
+}
+
+static void viif_l1_set_gamma(struct viif_device *viif_dev,
+ const struct viif_l1_gamma_config *arg)
+{
+ const struct viif_l1_gamma *param = &arg->param;
+ int i;
+
+ guard(spinlock_irqsave)(&viif_dev->regbuf_lock);
+ guard(viif_isp)(viif_dev);
+
+ /* Disabling L1 gamma */
+ if (!arg->enable) {
+ viif_capture_write(viif_dev, REG_L1_VPRO_PGC_SW, 0);
+ return;
+ }
+
+ /* Enabling L1 gamma */
+ for (i = 0; i < 44; i++)
+ viif_capture_write(viif_dev, REG_L1_VPRO_GAMxP(i),
+ param->gam_p[i]);
+ viif_capture_write(viif_dev, REG_L1_VPRO_BLKADJ, param->blkadj);
+ viif_capture_write(viif_dev, REG_L1_VPRO_PGC_SW, 1);
+}
+
+static void viif_l1_set_img_quality_adjustment(
+ struct viif_device *viif_dev,
+ const struct viif_l1_img_quality_adjustment_config *arg)
+{
+ u32 val;
+
+ guard(spinlock_irqsave)(&viif_dev->regbuf_lock);
+ guard(viif_isp)(viif_dev);
+
+ /* RGB to YUV (enabled by default, should be enabled) */
+ viif_capture_write(viif_dev, REG_L1_VPRO_YUVC_SW, 1);
+ viif_capture_write(viif_dev, REG_L1_VPRO_CB_MAT, (u32)arg->coef_cb);
+ viif_capture_write(viif_dev, REG_L1_VPRO_CR_MAT, (u32)arg->coef_cr);
+
+ /* Brightness */
+ val = (u32)arg->brightness & 0xffffU;
+ if (val) {
+ viif_capture_write(viif_dev, REG_L1_VPRO_BRIGHT_SW, 1);
+ viif_capture_write(viif_dev, REG_L1_VPRO_BRIGHT, val);
+ } else {
+ viif_capture_write(viif_dev, REG_L1_VPRO_BRIGHT_SW, 0);
+ }
+
+ /* Linear contrast */
+ if ((u32)arg->linear_contrast != 128) {
+ viif_capture_write(viif_dev, REG_L1_VPRO_LCNT_SW, 1);
+ viif_capture_write(viif_dev, REG_L1_VPRO_LCONT_LEV,
+ arg->linear_contrast);
+ } else {
+ viif_capture_write(viif_dev, REG_L1_VPRO_LCNT_SW, 0);
+ }
+
+ /* Nonlinear contrast */
+ if (arg->enable & VIIF_L1_IQA_NONLINEAR_CONTRAST_EN_MASK) {
+ const struct viif_l1_nonlinear_contrast *nonlinear_contrast =
+ &arg->nonlinear_contrast;
+
+ viif_capture_write(viif_dev, REG_L1_VPRO_NLCNT_SW, 1);
+ viif_capture_write(viif_dev, REG_L1_VPRO_BLK_KNEE,
+ nonlinear_contrast->blk_knee);
+ viif_capture_write(viif_dev, REG_L1_VPRO_WHT_KNEE,
+ nonlinear_contrast->wht_knee);
+
+ viif_capture_write(viif_dev, REG_L1_VPRO_BLK_CONT0,
+ nonlinear_contrast->blk_cont[0]);
+ viif_capture_write(viif_dev, REG_L1_VPRO_BLK_CONT1,
+ nonlinear_contrast->blk_cont[1]);
+ viif_capture_write(viif_dev, REG_L1_VPRO_BLK_CONT2,
+ nonlinear_contrast->blk_cont[2]);
+
+ viif_capture_write(viif_dev, REG_L1_VPRO_WHT_CONT0,
+ nonlinear_contrast->wht_cont[0]);
+ viif_capture_write(viif_dev, REG_L1_VPRO_WHT_CONT1,
+ nonlinear_contrast->wht_cont[1]);
+ viif_capture_write(viif_dev, REG_L1_VPRO_WHT_CONT2,
+ nonlinear_contrast->wht_cont[2]);
+ } else {
+ viif_capture_write(viif_dev, REG_L1_VPRO_NLCNT_SW, 0);
+ }
+
+ /* Luminance noise reduction */
+ if (arg->enable & VIIF_L1_IQA_LUM_NOISE_REDUCTION_EN_MASK) {
+ const struct viif_l1_lum_noise_reduction *lum_noise_reduction =
+ &arg->lum_noise_reduction;
+
+ viif_capture_write(viif_dev, REG_L1_VPRO_YNR_SW, 1);
+ viif_capture_write(viif_dev, REG_L1_VPRO_YNR_GAIN_MIN,
+ lum_noise_reduction->gain_min);
+ viif_capture_write(viif_dev, REG_L1_VPRO_YNR_GAIN_MAX,
+ lum_noise_reduction->gain_max);
+ viif_capture_write(viif_dev, REG_L1_VPRO_YNR_LIM_MIN,
+ lum_noise_reduction->lim_min);
+ viif_capture_write(viif_dev, REG_L1_VPRO_YNR_LIM_MAX,
+ lum_noise_reduction->lim_max);
+ } else {
+ viif_capture_write(viif_dev, REG_L1_VPRO_YNR_SW, 0);
+ }
+
+ /* Edge enhancement */
+ if (arg->enable & VIIF_L1_IQA_EDGE_ENHANCEMENT_EN_MASK) {
+ const struct viif_l1_edge_enhancement *edge_enhancement =
+ &arg->edge_enhancement;
+
+ viif_capture_write(viif_dev, REG_L1_VPRO_ETE_SW, 1);
+ viif_capture_write(viif_dev, REG_L1_VPRO_ETE_GAIN_MIN,
+ edge_enhancement->gain_min);
+ viif_capture_write(viif_dev, REG_L1_VPRO_ETE_GAIN_MAX,
+ edge_enhancement->gain_max);
+ viif_capture_write(viif_dev, REG_L1_VPRO_ETE_LIM_MIN,
+ edge_enhancement->lim_min);
+ viif_capture_write(viif_dev, REG_L1_VPRO_ETE_LIM_MAX,
+ edge_enhancement->lim_max);
+ viif_capture_write(viif_dev, REG_L1_VPRO_ETE_CORING_MIN,
+ edge_enhancement->coring_min);
+ viif_capture_write(viif_dev, REG_L1_VPRO_ETE_CORING_MAX,
+ edge_enhancement->coring_max);
+ } else {
+ viif_capture_write(viif_dev, REG_L1_VPRO_ETE_SW, 0);
+ }
+
+ /* UV suppression */
+ if (arg->enable & VIIF_L1_IQA_UV_SUPPRESSION_EN_MASK) {
+ const struct viif_l1_uv_suppression *uv_suppression =
+ &arg->uv_suppression;
+
+ viif_capture_write(viif_dev, REG_L1_VPRO_CSUP_UVSUP_SW, 1);
+ viif_capture_write(viif_dev, REG_L1_VPRO_CSUP_BK_SLV,
+ uv_suppression->bk_slv);
+ viif_capture_write(viif_dev, REG_L1_VPRO_CSUP_BK_MP,
+ uv_suppression->bk_mp);
+ viif_capture_write(viif_dev, REG_L1_VPRO_CSUP_BLACK,
+ uv_suppression->black);
+
+ viif_capture_write(viif_dev, REG_L1_VPRO_CSUP_WH_SLV,
+ uv_suppression->wh_slv);
+ viif_capture_write(viif_dev, REG_L1_VPRO_CSUP_WH_MP,
+ uv_suppression->wh_mp);
+ viif_capture_write(viif_dev, REG_L1_VPRO_CSUP_WHITE,
+ uv_suppression->white);
+ } else {
+ viif_capture_write(viif_dev, REG_L1_VPRO_CSUP_UVSUP_SW, 0);
+ }
+
+ /* Coring suppression */
+ if (arg->enable & VIIF_L1_IQA_CORING_SUPPRESSION_EN_MASK) {
+ const struct viif_l1_coring_suppression *coring_suppression =
+ &arg->coring_suppression;
+
+ viif_capture_write(viif_dev, REG_L1_VPRO_CSUP_CORING_SW, 1);
+ viif_capture_write(viif_dev, REG_L1_VPRO_CSUP_CORING_LV_MIN,
+ coring_suppression->lv_min);
+ viif_capture_write(viif_dev, REG_L1_VPRO_CSUP_CORING_LV_MAX,
+ coring_suppression->lv_max);
+ viif_capture_write(viif_dev, REG_L1_VPRO_CSUP_CORING_GAIN_MIN,
+ coring_suppression->gain_min);
+ viif_capture_write(viif_dev, REG_L1_VPRO_CSUP_CORING_GAIN_MAX,
+ coring_suppression->gain_max);
+ } else {
+ viif_capture_write(viif_dev, REG_L1_VPRO_CSUP_CORING_SW, 0);
+ }
+
+ /* Edge suppression */
+ if (arg->enable & VIIF_L1_IQA_EDGE_SUPPRESSION_EN_MASK) {
+ const struct viif_l1_edge_suppression *edge_suppression =
+ &arg->edge_suppression;
+
+ viif_capture_write(viif_dev, REG_L1_VPRO_EDGE_SUP_SW, 1);
+ viif_capture_write(viif_dev, REG_L1_VPRO_EDGE_SUP_GAIN,
+ edge_suppression->gain);
+ viif_capture_write(viif_dev, REG_L1_VPRO_EDGE_SUP_LIM,
+ edge_suppression->lim);
+ } else {
+ viif_capture_write(viif_dev, REG_L1_VPRO_EDGE_SUP_SW, 0);
+ }
+
+ /* Color level */
+ if (arg->enable & VIIF_L1_IQA_COLOR_LEVEL_EN_MASK) {
+ const struct viif_l1_color_level *color_level =
+ &arg->color_level;
+
+ viif_capture_write(viif_dev, REG_L1_VPRO_CB_GAIN,
+ color_level->cb_gain);
+ viif_capture_write(viif_dev, REG_L1_VPRO_CR_GAIN,
+ color_level->cr_gain);
+ viif_capture_write(viif_dev, REG_L1_VPRO_CBR_MGAIN_MIN,
+ color_level->cbr_mgain_min);
+ viif_capture_write(viif_dev, REG_L1_VPRO_CB_P_GAIN_MAX,
+ color_level->cbp_gain_max);
+ viif_capture_write(viif_dev, REG_L1_VPRO_CB_M_GAIN_MAX,
+ color_level->cbm_gain_max);
+ viif_capture_write(viif_dev, REG_L1_VPRO_CR_P_GAIN_MAX,
+ color_level->crp_gain_max);
+ viif_capture_write(viif_dev, REG_L1_VPRO_CR_M_GAIN_MAX,
+ color_level->crm_gain_max);
+ } else {
+ viif_capture_write(viif_dev, REG_L1_VPRO_CB_GAIN, 1024U);
+ viif_capture_write(viif_dev, REG_L1_VPRO_CR_GAIN, 1024U);
+ viif_capture_write(viif_dev, REG_L1_VPRO_CBR_MGAIN_MIN, 1024U);
+ viif_capture_write(viif_dev, REG_L1_VPRO_CB_P_GAIN_MAX, 0U);
+ viif_capture_write(viif_dev, REG_L1_VPRO_CB_M_GAIN_MAX, 0U);
+ viif_capture_write(viif_dev, REG_L1_VPRO_CR_P_GAIN_MAX, 0U);
+ viif_capture_write(viif_dev, REG_L1_VPRO_CR_M_GAIN_MAX, 0U);
+ }
+
+ /* Color noise reduction */
+ viif_capture_write(
+ viif_dev, REG_L1_VPRO_CNR_SW,
+ arg->enable & VIIF_L1_IQA_COLOR_NOISE_REDUCTION_EN_MASK ? 1 :
+ 0);
+}
+
+static u32 pack_weight(const u32 *vec)
+{
+ return (vec[0] << 14) | (vec[1] << 12) | (vec[2] << 10) |
+ (vec[3] << 8) | (vec[4] << 6) | (vec[5] << 4) | (vec[6] << 2) |
+ (vec[7]);
+}
+
+static void viif_l1_set_avg_lum_generation(
+ struct viif_device *viif_dev,
+ const struct viif_l1_avg_lum_generation_config *arg)
+{
+ guard(spinlock_irqsave)(&viif_dev->regbuf_lock);
+ guard(viif_isp)(viif_dev);
+
+ /* Disabling aggregation */
+ if (!arg->enable) {
+ viif_capture_write(viif_dev, REG_L1_AEXP_ON, 0);
+ return;
+ }
+
+ /* Enabling aggregation */
+ viif_capture_write(viif_dev, REG_L1_AEXP_ON, 1);
+ viif_capture_write(viif_dev, REG_L1_AEXP_START_X, arg->aexp_start_x);
+ viif_capture_write(viif_dev, REG_L1_AEXP_START_Y, arg->aexp_start_y);
+ viif_capture_write(viif_dev, REG_L1_AEXP_BLOCK_WIDTH,
+ arg->aexp_block_width);
+ viif_capture_write(viif_dev, REG_L1_AEXP_BLOCK_HEIGHT,
+ arg->aexp_block_height);
+
+ viif_capture_write(viif_dev, REG_L1_AEXP_WEIGHT_0,
+ pack_weight(arg->aexp_weight[0]));
+ viif_capture_write(viif_dev, REG_L1_AEXP_WEIGHT_1,
+ pack_weight(arg->aexp_weight[1]));
+ viif_capture_write(viif_dev, REG_L1_AEXP_WEIGHT_2,
+ pack_weight(arg->aexp_weight[2]));
+ viif_capture_write(viif_dev, REG_L1_AEXP_WEIGHT_3,
+ pack_weight(arg->aexp_weight[3]));
+ viif_capture_write(viif_dev, REG_L1_AEXP_WEIGHT_3,
+ pack_weight(arg->aexp_weight[4]));
+ viif_capture_write(viif_dev, REG_L1_AEXP_WEIGHT_4,
+ pack_weight(arg->aexp_weight[5]));
+ viif_capture_write(viif_dev, REG_L1_AEXP_WEIGHT_5,
+ pack_weight(arg->aexp_weight[6]));
+ viif_capture_write(viif_dev, REG_L1_AEXP_WEIGHT_7,
+ pack_weight(arg->aexp_weight[7]));
+
+ viif_capture_write(viif_dev, REG_L1_AEXP_SATUR_RATIO,
+ arg->aexp_satur_ratio);
+ viif_capture_write(viif_dev, REG_L1_AEXP_BLACK_RATIO,
+ arg->aexp_black_ratio);
+ viif_capture_write(viif_dev, REG_L1_AEXP_SATUR_LEVEL,
+ arg->aexp_satur_level);
+
+ viif_capture_write(viif_dev, REG_L1_AEXP_AVE4LINESY0,
+ arg->aexp_ave4linesy[0]);
+ viif_capture_write(viif_dev, REG_L1_AEXP_AVE4LINESY1,
+ arg->aexp_ave4linesy[1]);
+ viif_capture_write(viif_dev, REG_L1_AEXP_AVE4LINESY2,
+ arg->aexp_ave4linesy[2]);
+ viif_capture_write(viif_dev, REG_L1_AEXP_AVE4LINESY3,
+ arg->aexp_ave4linesy[3]);
+}
+
+static void undist_table_transmission(struct viif_device *viif_dev,
+ dma_addr_t write_g, dma_addr_t read_b,
+ dma_addr_t read_g, dma_addr_t read_r,
+ u32 size)
+{
+ u32 val = 0;
+
+ if (read_b) {
+ viif_capture_write(
+ viif_dev,
+ REG_VDM_TPORT_X_STADR(IDX_TPORT_L2_UNDIST_RD_B),
+ (u32)read_b);
+ viif_capture_write(
+ viif_dev,
+ REG_VDM_TPORT_X_SIZE(IDX_TPORT_L2_UNDIST_RD_B), size);
+ val |= MASK_VDM_T_ENABLE_L2_UNDIST_RD_B;
+ }
+ if (read_g) {
+ viif_capture_write(
+ viif_dev,
+ REG_VDM_TPORT_X_STADR(IDX_TPORT_L2_UNDIST_RD_G),
+ (u32)read_g);
+ viif_capture_write(
+ viif_dev,
+ REG_VDM_TPORT_X_SIZE(IDX_TPORT_L2_UNDIST_RD_G), size);
+ val |= MASK_VDM_T_ENABLE_L2_UNDIST_RD_G;
+ }
+ if (read_r) {
+ viif_capture_write(
+ viif_dev,
+ REG_VDM_TPORT_X_STADR(IDX_TPORT_L2_UNDIST_RD_R),
+ (u32)read_r);
+ viif_capture_write(
+ viif_dev,
+ REG_VDM_TPORT_X_SIZE(IDX_TPORT_L2_UNDIST_RD_R), size);
+ val |= MASK_VDM_T_ENABLE_L2_UNDIST_RD_R;
+ }
+ if (write_g) {
+ viif_capture_write(
+ viif_dev,
+ REG_VDM_TPORT_X_STADR(IDX_TPORT_L2_UNDIST_WR_G),
+ (u32)write_g);
+ viif_capture_write(
+ viif_dev,
+ REG_VDM_TPORT_X_SIZE(IDX_TPORT_L2_UNDIST_WR_G), size);
+ val |= MASK_VDM_T_ENABLE_L2_UNDIST_WR_G;
+ }
+
+ if (val)
+ viif_config_vdm_tgroup(viif_dev, IDX_TGROUP_L2_UNDIST);
+
+ val |= viif_capture_read(viif_dev, REG_VDM_T_ENABLE) &
+ ~MASK_VDM_T_ENABLE_L2_UNDIST;
+ viif_capture_write(viif_dev, REG_VDM_T_ENABLE, val);
+}
+
+static void undist_setup(struct viif_device *viif_dev,
+ const struct viif_l2_undist *param)
+{
+ u32 val;
+ unsigned int i;
+
+ /* Undist through mode */
+ if (param->through_mode) {
+ /* Enable through mode */
+ viif_capture_write(viif_dev, REG_L2_MODE, 1);
+ return;
+ }
+
+ /* Undist operation */
+ val = (param->roi_mode[0] << 1) | (param->roi_mode[1] << 3);
+ viif_capture_write(viif_dev, REG_L2_MODE, val);
+ val = (u32)param->sensor_crop_ofs_h & GENMASK(13, 0);
+ viif_capture_write(viif_dev, REG_L2_SENSOR_CROP_OFS_H, val);
+ val = (u32)param->sensor_crop_ofs_v & GENMASK(12, 0);
+ viif_capture_write(viif_dev, REG_L2_SENSOR_CROP_OFS_V, val);
+ viif_capture_write(viif_dev, REG_L2_NORM_SCALE, param->norm_scale);
+ viif_capture_write(viif_dev, REG_L2_VALID_R_NORM2_POLY,
+ param->valid_r_norm2_poly);
+ viif_capture_write(viif_dev, REG_L2_VALID_R_NORM2_GRID,
+ param->valid_r_norm2_grid);
+ viif_capture_write(viif_dev, REG_L2_ROI_WRITE_AREA_DELTA(0),
+ param->roi_write_area_delta[0]);
+ viif_capture_write(viif_dev, REG_L2_ROI_WRITE_AREA_DELTA(1),
+ param->roi_write_area_delta[1]);
+
+ for (i = 0; i < VIIF_L2_UNDIST_POLY_NUM; i++) {
+ val = (u32)param->poly_write_g_coef[i];
+ viif_capture_write(viif_dev, REG_L2_POLY10_WRITE_G_COEF(i),
+ val);
+ val = (u32)param->poly_read_b_coef[i];
+ viif_capture_write(viif_dev, REG_L2_POLY10_READ_B_COEF(i), val);
+ val = (u32)param->poly_read_g_coef[i];
+ viif_capture_write(viif_dev, REG_L2_POLY10_READ_G_COEF(i), val);
+ val = (u32)param->poly_read_r_coef[i];
+ viif_capture_write(viif_dev, REG_L2_POLY10_READ_R_COEF(i), val);
+ }
+ viif_capture_write(viif_dev, REG_L2_GRID_NODE_NUM_H,
+ param->grid_node_num_h);
+ viif_capture_write(viif_dev, REG_L2_GRID_NODE_NUM_V,
+ param->grid_node_num_v);
+ viif_capture_write(viif_dev, REG_L2_GRID_PATCH_HSIZE_INV,
+ param->grid_patch_hsize_inv);
+ viif_capture_write(viif_dev, REG_L2_GRID_PATCH_VSIZE_INV,
+ param->grid_patch_vsize_inv);
+}
+
+static void viif_l2_set_undist(struct viif_device *viif_dev,
+ const struct viif_l2_undist_config *arg)
+{
+ dma_addr_t table_write_g = 0;
+ dma_addr_t table_read_b = 0;
+ dma_addr_t table_read_g = 0;
+ dma_addr_t table_read_r = 0;
+
+ if (arg->param.roi_mode[0] != VIIF_L2_UNDIST_POLY ||
+ arg->param.roi_mode[1] != VIIF_L2_UNDIST_POLY) {
+ memcpy(viif_dev->tables->undist_write_g, arg->write_g,
+ arg->size);
+ memcpy(viif_dev->tables->undist_read_b, arg->read_b, arg->size);
+ memcpy(viif_dev->tables->undist_read_g, arg->read_g, arg->size);
+ memcpy(viif_dev->tables->undist_read_r, arg->read_r, arg->size);
+
+ table_write_g =
+ viif_dev->tables_dma +
+ offsetof(struct viif_table_area, undist_write_g);
+ table_read_b = viif_dev->tables_dma +
+ offsetof(struct viif_table_area, undist_read_b);
+ table_read_g = viif_dev->tables_dma +
+ offsetof(struct viif_table_area, undist_read_g);
+ table_read_r = viif_dev->tables_dma +
+ offsetof(struct viif_table_area, undist_read_r);
+ }
+
+ guard(spinlock_irqsave)(&viif_dev->regbuf_lock);
+ guard(viif_isp)(viif_dev);
+
+ undist_table_transmission(viif_dev, table_write_g, table_read_b,
+ table_read_g, table_read_r, arg->size);
+ undist_setup(viif_dev, &arg->param);
+}
+
+void visconti_viif_l2_undist_through(struct viif_device *viif_dev)
+{
+ struct viif_l2_undist undist = { 0 };
+
+ undist.through_mode = VIIF_ENABLE;
+ undist.sensor_crop_ofs_h =
+ 1 -
+ FIELD_GET(0x1fff, viif_capture_read(viif_dev,
+ REG_L2_SENSOR_CROP_HSIZE));
+ undist.sensor_crop_ofs_v =
+ 1 -
+ FIELD_GET(0x0fff, viif_capture_read(viif_dev,
+ REG_L2_SENSOR_CROP_VSIZE));
+ undist.grid_node_num_h = 16;
+ undist.grid_node_num_v = 16;
+
+ undist_setup(viif_dev, &undist);
+}
+
+static void viif_l2_set_roi(struct viif_device *viif_dev,
+ const struct viif_l2_roi_config *roi)
+{
+ u32 val;
+ int i;
+
+ /* Update ROI parameter */
+ guard(spinlock_irqsave)(&viif_dev->regbuf_lock);
+ guard(viif_isp)(viif_dev);
+
+ /* Set the number of ROI and update resource info with roi_num */
+ viif_capture_write(viif_dev, REG_L2_ROI_NUM, roi->roi_num);
+ viif_dev->l2_roi_path_info.roi_num = roi->roi_num;
+
+ /* Update ROI area and input to each POST */
+ visconti_viif_l2_set_roi_path(viif_dev);
+
+ /* Set the remaining parameters */
+ for (i = 0; i < 2; i++) {
+ viif_capture_write(viif_dev, REG_L2_ROI_X_SCALE(i),
+ roi->roi_scale[i]);
+ viif_capture_write(viif_dev, REG_L2_ROI_X_SCALE_INV(i),
+ roi->roi_scale_inv[i]);
+ val = (roi->corrected_wo_scale_hsize[i] << 13) |
+ roi->corrected_hsize[i];
+ viif_capture_write(viif_dev, REG_L2_ROI_X_CORRECTED_HSIZE(i),
+ val);
+ val = (roi->corrected_wo_scale_vsize[i] << 12) |
+ roi->corrected_vsize[i];
+ viif_capture_write(viif_dev, REG_L2_ROI_X_CORRECTED_VSIZE(i),
+ val);
+ }
+}
+
+struct viif_l2_gamma_table {
+ dma_addr_t table[VIIF_L2_GAMMA_TABLE_CH_NUM];
+};
+
+static void
+l2_gamma_table_transmission(struct viif_device *viif_dev, u32 post_id,
+ const struct viif_l2_gamma_table *gamma_table)
+{
+ u32 vdm_enable = 0;
+ u32 i;
+
+ /* 0: LUT0-G/Y, 1: LUT1-G/Y, 2: LUT0-B/U */
+ /* 3: LUT1-B/U, 4: LUT0-R/V, 5: LUT1-R/V */
+ for (i = 0; i < VIIF_L2_GAMMA_TABLE_CH_NUM; i++) {
+ if (gamma_table->table[i]) {
+ int idx = IDX_TPORT_L2_GAMMA_LUT(post_id, i);
+
+ viif_capture_write(viif_dev, REG_VDM_TPORT_X_STADR(idx),
+ (u32)gamma_table->table[i]);
+ viif_capture_write(viif_dev, REG_VDM_TPORT_X_SIZE(idx),
+ VIIF_L2_GAMMA_TABLE_BYTES);
+ vdm_enable |= MASK_VDM_T_ENABLE_L2_GAMMA(post_id, i);
+ }
+ }
+ if (vdm_enable)
+ viif_config_vdm_tgroup(viif_dev,
+ IDX_TGROUP_L2_GAMMA_LUT(post_id));
+
+ vdm_enable |= viif_capture_read(viif_dev, REG_VDM_T_ENABLE) &
+ ~MASK_VDM_T_ENABLE_L2_GAMMA_ALL(post_id);
+
+ viif_capture_write(viif_dev, REG_VDM_T_ENABLE, vdm_enable);
+}
+
+static void viif_l2_set_gamma(struct viif_device *viif_dev, int pathid,
+ const struct viif_l2_gamma_config *l2_gamma)
+{
+ struct viif_l2_gamma_table dma_table = { 0 };
+ int postid = (pathid == CAPTURE_PATH_MAIN_POST0) ? VIIF_L2ISP_POST_0 :
+ VIIF_L2ISP_POST_1;
+ int table_en;
+ u32 val;
+ int i;
+
+ table_en = l2_gamma->table_en;
+ for (i = 0; i < 6; i++) {
+ if (table_en & BIT(i)) {
+ memcpy(viif_dev->tables->l2_gamma_table[pathid][i],
+ l2_gamma->table[i], VIIF_L2_GAMMA_TABLE_BYTES);
+ dma_table.table[i] =
+ viif_dev->tables_dma +
+ offsetof(struct viif_table_area,
+ l2_gamma_table[pathid][i]);
+ }
+ }
+
+ guard(spinlock_irqsave)(&viif_dev->regbuf_lock);
+ guard(viif_isp)(viif_dev);
+
+ l2_gamma_table_transmission(viif_dev, postid, &dma_table);
+
+ val = (l2_gamma->vsplit << 16) | (l2_gamma->mode << 4) |
+ (table_en != 0 ? 1 : 0);
+ viif_capture_write(viif_dev, REG_L2_POST_X_GAMMA_M(postid), val);
+}
+
+static void viif_l2_set_crop(struct viif_device *viif_dev, int pathid,
+ const struct viif_l2_crop_config *l2_crop)
+{
+ struct cap_dev *cap_dev = (pathid == CAPTURE_PATH_MAIN_POST0) ?
+ &viif_dev->cap_post0 :
+ &viif_dev->cap_post1;
+
+ cap_dev->img_area.left = l2_crop->left;
+ cap_dev->img_area.top = l2_crop->top;
+}
+
+static const struct viif_l1_input_mode_config defval_l1_set_input_mode = {
+ .mode = VIIF_L1_INPUT_HDR,
+};
+
+static const struct viif_l1_rgb_to_y_coef_config defval_l1_set_rgb_to_y_coef = {
+ /* ITU-R BT.601 */
+ .coef_r = 0x4c8c,
+ .coef_g = 0x9644,
+ .coef_b = 0x1d30,
+};
+
+static const struct viif_l1_ag_mode_config defval_l1_set_ag_mode = { 0 };
+
+static const struct viif_l1_ag_config defval_l1_set_ag = { 0 };
+
+static const struct viif_l1_hdre_config defval_l1_set_hdre = {
+ .hdre_src_point[0] = 0x3fff,
+ .hdre_dst_max_val = 0xffffff,
+};
+
+static const struct viif_l1_img_extraction_config defval_l1_set_img_extraction = {
+ .input_black_gr = 256,
+ .input_black_r = 256,
+ .input_black_b = 256,
+ .input_black_gb = 256,
+};
+
+static const struct viif_l1_dpc_config defval_l1_set_dpc = { 0 };
+
+static const struct viif_l1_preset_white_balance_config
+defval_l1_set_preset_white_balance = {
+ .dstmaxval = 0x0fff,
+ .param_h = {
+ .gain_gr = 0x4000,
+ .gain_r = 0x4000,
+ .gain_b = 0x4000,
+ .gain_gb = 0x4000,
+ },
+ .param_m = {
+ .gain_gr = 0x4000,
+ .gain_r = 0x4000,
+ .gain_b = 0x4000,
+ .gain_gb = 0x4000,
+ },
+ .param_l = {
+ .gain_gr = 0x4000,
+ .gain_r = 0x4000,
+ .gain_b = 0x4000,
+ .gain_gb = 0x4000,
+ },
+};
+
+static const struct viif_l1_raw_color_noise_reduction_config
+defval_l1_set_raw_color_noise_reduction = {
+ .param_h = {
+ .rcnr_cnf_clip_gain_r = 3,
+ .rcnr_cnf_clip_gain_g = 2,
+ .rcnr_cnf_clip_gain_b = 3,
+ .rcnr_merge_black = 0x20,
+ .rcnr_merge_mindiv = 4,
+ .rcnr_anf_blend_ag0 = 1,
+ .rcnr_anf_blend_ag1 = 2,
+ .rcnr_anf_blend_ag2 = 2,
+ .rcnr_lpf_threshold = 8,
+ },
+ .param_m = {
+ .rcnr_cnf_clip_gain_r = 3,
+ .rcnr_cnf_clip_gain_g = 2,
+ .rcnr_cnf_clip_gain_b = 3,
+ .rcnr_merge_black = 0x20,
+ .rcnr_merge_mindiv = 4,
+ .rcnr_anf_blend_ag0 = 1,
+ .rcnr_anf_blend_ag1 = 2,
+ .rcnr_anf_blend_ag2 = 2,
+ .rcnr_lpf_threshold = 8,
+ },
+ .param_l = {
+ .rcnr_cnf_clip_gain_r = 3,
+ .rcnr_cnf_clip_gain_g = 2,
+ .rcnr_cnf_clip_gain_b = 3,
+ .rcnr_merge_black = 0x20,
+ .rcnr_merge_mindiv = 4,
+ .rcnr_anf_blend_ag0 = 1,
+ .rcnr_anf_blend_ag1 = 2,
+ .rcnr_anf_blend_ag2 = 2,
+ .rcnr_lpf_threshold = 8,
+ },
+};
+
+static const struct viif_l1_hdrs_config defval_l1_set_hdrs = {
+ .hdrs_hdr_mode = 1,
+ .hdrs_hdr_ratio_m = 0x10000,
+ .hdrs_hdr_ratio_l = 0x400000,
+ .hdrs_hdr_ratio_e = 0x400,
+ .hdrs_dg_h = 0x400,
+ .hdrs_dg_m = 0x400,
+ .hdrs_dg_l = 0x400,
+ .hdrs_dg_e = 0x400,
+ .hdrs_blendend_h = 0xfa0,
+ .hdrs_blendend_m = 0xfa0,
+ .hdrs_blendend_e = 0xfa0,
+ .hdrs_blendbeg_h = 0x12c,
+ .hdrs_blendbeg_m = 0x12c,
+ .hdrs_blendbeg_e = 0x12c,
+ .hdrs_dst_max_val = 0xffffff,
+};
+
+static const struct viif_l1_black_level_correction_config
+ defval_l1_set_black_level_correction = {
+ .srcblacklevel_gr = 0x40,
+ .srcblacklevel_r = 0x40,
+ .srcblacklevel_b = 0x40,
+ .srcblacklevel_gb = 0x40,
+ .mulval_gr = 0x40000,
+ .mulval_r = 0x40000,
+ .mulval_b = 0x40000,
+ .mulval_gb = 0x40000,
+ .dstmaxval = 0xffffff,
+ };
+
+static const struct viif_l1_lsc_config defval_l1_set_lsc = { 0 };
+
+static const struct viif_l1_main_process_config defval_l1_set_main_process = {
+ .damp_lsbsel = 0x8,
+ .demosaic_mode = 1,
+ .colormat_enable = 0,
+ .dst_maxval = 0xffffff,
+};
+
+static const struct viif_l1_awb_config defval_l1_set_awb = {
+ .enable = 0,
+ .awhb_wbmrg = 256,
+ .awhb_wbmgg = 256,
+ .awhb_wbmbg = 256,
+};
+
+static const u32 defval_l1_lock_awb_gain;
+
+static const struct viif_l1_hdrc_config defval_l1_set_hdrc = {
+ .enable = 1,
+ .param = {
+ .hdrc_ratio = 0x0e + VIIF_L1_HDRC_RATIO_OFFSET,
+ .hdrc_pt_ratio = 7,
+ .hdrc_pt_sat = 0xffc0,
+ .hdrc_tn_type = 1,
+ },
+};
+
+static const struct viif_l1_hdrc_ltm_config defval_l1_set_hdrc_ltm = {
+ .tnp_max = 0x3fffff,
+ .tnp_mag = 0x40,
+ .tnp_fil = { 0x88, 0x84, 0x7a, 0x6a, 0x54 },
+};
+
+static const struct viif_l1_gamma_config defval_l1_set_gamma = {
+ .enable = 1,
+ .param = {
+ .gam_p = {0x02f, 0x01b, 0x02a, 0x023, 0x020, 0x037, 0x031, 0x057,
+ 0x04d, 0x088, 0x078, 0x0d6, 0x0bd, 0x14f, 0x12a, 0x20d,
+ 0x1d3, 0x1ab, 0x18d, 0x2dc, 0x29e, 0x271, 0x47c, 0x41b,
+ 0x3d4, 0x70a, 0x672, 0x601, 0xb0c, 0xa1d, 0x96c, 0x8e2,
+ 0x874, 0xfdd, 0xec9, 0xdf2, 0xd42, 0xcb1, 0xc35, 0xbc9,
+ 0xb6a, 0xb16, 0xacb, 0xa86},
+ .blkadj = 0x1000,
+ },
+};
+
+static const struct viif_l1_img_quality_adjustment_config
+ defval_l1_set_img_quality_adjustment = {
+ .enable = 0,
+ .coef_cb = 0x9078,
+ .coef_cr = 0xb699,
+ .brightness = 0,
+ .linear_contrast = 128,
+ };
+
+static const struct viif_l1_avg_lum_generation_config
+ defval_l1_set_avg_lum_generation = { .enable = 0 };
+
+static const struct viif_l2_undist_config defval_l2_set_undist = {
+ .param = {
+ .through_mode = 1,
+ .roi_mode = {0, 0},
+ .grid_node_num_h = 0x10,
+ .grid_node_num_v = 0x10,
+ },
+};
+
+static const struct viif_l2_gamma_config defval_l2_set_gamma = { 0 };
+
+static const struct viif_l2_crop_config defval_l2_set_crop = { 0, 0 };
+
+static void viif_apply_default_parameter(struct viif_device *viif_dev)
+{
+ viif_l1_set_input_mode(viif_dev, &defval_l1_set_input_mode);
+ viif_l1_set_rgb_to_y_coef(viif_dev, &defval_l1_set_rgb_to_y_coef);
+ viif_l1_set_ag_mode(viif_dev, &defval_l1_set_ag_mode);
+ viif_l1_set_ag(viif_dev, &defval_l1_set_ag);
+ viif_l1_set_hdre(viif_dev, &defval_l1_set_hdre);
+ viif_l1_set_img_extraction(viif_dev, &defval_l1_set_img_extraction);
+ viif_l1_set_dpc(viif_dev, &defval_l1_set_dpc);
+ viif_l1_set_preset_white_balance(viif_dev,
+ &defval_l1_set_preset_white_balance);
+ viif_l1_set_raw_color_noise_reduction(
+ viif_dev, &defval_l1_set_raw_color_noise_reduction);
+ viif_l1_set_hdrs(viif_dev, &defval_l1_set_hdrs);
+ viif_l1_set_black_level_correction(
+ viif_dev, &defval_l1_set_black_level_correction);
+ viif_l1_set_lsc(viif_dev, &defval_l1_set_lsc);
+ viif_l1_set_main_process(viif_dev, &defval_l1_set_main_process);
+ viif_l1_set_awb(viif_dev, &defval_l1_set_awb);
+ viif_l1_lock_awb_gain(viif_dev, &defval_l1_lock_awb_gain);
+ viif_l1_set_hdrc(viif_dev, &defval_l1_set_hdrc);
+ viif_l1_set_hdrc_ltm(viif_dev, &defval_l1_set_hdrc_ltm);
+ viif_l1_set_gamma(viif_dev, &defval_l1_set_gamma);
+ viif_l1_set_img_quality_adjustment(
+ viif_dev, &defval_l1_set_img_quality_adjustment);
+ viif_l1_set_avg_lum_generation(viif_dev,
+ &defval_l1_set_avg_lum_generation);
+ viif_l2_set_undist(viif_dev, &defval_l2_set_undist);
+ viif_l2_set_gamma(viif_dev, CAPTURE_PATH_MAIN_POST0,
+ &defval_l2_set_gamma);
+ viif_l2_set_gamma(viif_dev, CAPTURE_PATH_MAIN_POST1,
+ &defval_l2_set_gamma);
+ viif_l2_set_crop(viif_dev, CAPTURE_PATH_MAIN_POST0,
+ &defval_l2_set_crop);
+ viif_l2_set_crop(viif_dev, CAPTURE_PATH_MAIN_POST1,
+ &defval_l2_set_crop);
+}
+
+/*----------------------------------------------
+ * Parameter buffer streaming interface
+ */
+struct viif_buffer {
+ struct vb2_v4l2_buffer vb;
+ struct list_head queue;
+};
+
+static bool viif_params_get_buffer(struct params_dev *params_dev,
+ struct viif_buffer **buf,
+ struct visconti_viif_isp_config **cfg)
+{
+ if (list_empty(¶ms_dev->params_queue))
+ return false;
+
+ *buf = list_first_entry(¶ms_dev->params_queue, struct viif_buffer,
+ queue);
+ *cfg = vb2_plane_vaddr(&(*buf)->vb.vb2_buf, 0);
+
+ return true;
+}
+
+static bool viif_apply_queued_parameter(struct viif_device *viif_dev,
+ bool initial_cfg)
+{
+ struct params_dev *params_dev = &viif_dev->params_dev;
+ struct visconti_viif_isp_config *new_params;
+ struct viif_buffer *cur_buf;
+
+ guard(spinlock)(¶ms_dev->params_lock);
+
+ if (!viif_params_get_buffer(params_dev, &cur_buf, &new_params))
+ return false;
+
+ /* Evaluate new_params */
+ if (initial_cfg) {
+ if (new_params->update_cfg &
+ VISCONTI_VIIF_CFG_ISP_L1_INPUT_MODE)
+ viif_l1_set_input_mode(viif_dev,
+ &new_params->l1_input_mode);
+ }
+ if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L1_RGB_TO_Y_COEF)
+ viif_l1_set_rgb_to_y_coef(viif_dev,
+ &new_params->l1_rgb_to_y_coef);
+ if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L1_AG_MODE)
+ viif_l1_set_ag_mode(viif_dev, &new_params->l1_ag_mode);
+ if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L1_AG)
+ viif_l1_set_ag(viif_dev, &new_params->l1_ag);
+ if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L1_HDRE)
+ viif_l1_set_hdre(viif_dev, &new_params->l1_hdre);
+ if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L1_IMG_EXTRACTION)
+ viif_l1_set_img_extraction(viif_dev,
+ &new_params->l1_img_extraction);
+ if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L1_DPC)
+ viif_l1_set_dpc(viif_dev, &new_params->l1_dpc);
+ if (new_params->update_cfg &
+ VISCONTI_VIIF_CFG_ISP_L1_PRESET_WHITE_BALANCE)
+ viif_l1_set_preset_white_balance(
+ viif_dev, &new_params->l1_preset_white_balance);
+ if (new_params->update_cfg &
+ VISCONTI_VIIF_CFG_ISP_L1_RAW_COLOR_NOISE_REDUCTION)
+ viif_l1_set_raw_color_noise_reduction(
+ viif_dev, &new_params->l1_raw_color_noise_reduction);
+ if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L1_HDRS)
+ viif_l1_set_hdrs(viif_dev, &new_params->l1_hdrs);
+ if (new_params->update_cfg &
+ VISCONTI_VIIF_CFG_ISP_L1_BLACK_LEVEL_CORRECTION)
+ viif_l1_set_black_level_correction(
+ viif_dev, &new_params->l1_black_level_correction);
+ if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L1_LSC)
+ viif_l1_set_lsc(viif_dev, &new_params->l1_lsc);
+ if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L1_MAIN_PROCESS)
+ viif_l1_set_main_process(viif_dev,
+ &new_params->l1_main_process);
+ if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L1_AWB)
+ viif_l1_set_awb(viif_dev, &new_params->l1_awb);
+ if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L1_LOCK_AWB_GAIN)
+ viif_l1_lock_awb_gain(viif_dev, &new_params->lock_awb_gain);
+ if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L1_HDRC)
+ viif_l1_set_hdrc(viif_dev, &new_params->l1_hdrc);
+ if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L1_HDRC_LTM)
+ viif_l1_set_hdrc_ltm(viif_dev, &new_params->l1_hdrc_ltm);
+ if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L1_GAMMA)
+ viif_l1_set_gamma(viif_dev, &new_params->l1_gamma);
+ if (new_params->update_cfg &
+ VISCONTI_VIIF_CFG_ISP_L1_IMG_QUALITY_ADJUSTMENT)
+ viif_l1_set_img_quality_adjustment(
+ viif_dev, &new_params->l1_img_quality_adjustment);
+ if (new_params->update_cfg &
+ VISCONTI_VIIF_CFG_ISP_L1_AVG_LUM_GENERATION)
+ viif_l1_set_avg_lum_generation(
+ viif_dev, &new_params->l1_avg_lum_generation);
+ if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L2_UNDIST)
+ viif_l2_set_undist(viif_dev, &new_params->l2_undist);
+ if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L2_ROI)
+ viif_l2_set_roi(viif_dev, &new_params->l2_roi);
+ if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L2_GAMMA_POST0)
+ viif_l2_set_gamma(viif_dev, CAPTURE_PATH_MAIN_POST0,
+ &new_params->l2_gamma_post0);
+ if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L2_GAMMA_POST1)
+ viif_l2_set_gamma(viif_dev, CAPTURE_PATH_MAIN_POST1,
+ &new_params->l2_gamma_post1);
+ if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L2_CROP_POST0)
+ viif_l2_set_crop(viif_dev, CAPTURE_PATH_MAIN_POST0,
+ &new_params->l2_crop_post0);
+ if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L2_CROP_POST1)
+ viif_l2_set_crop(viif_dev, CAPTURE_PATH_MAIN_POST1,
+ &new_params->l2_crop_post1);
+
+ /* Release buffer */
+ list_del(&cur_buf->queue);
+ cur_buf->vb.sequence = 0;
+ vb2_buffer_done(&cur_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+
+ return true;
+}
+
+void visconti_viif_params_isr(struct viif_device *viif_dev)
+{
+ viif_apply_queued_parameter(viif_dev, false);
+}
+
+void visconti_viif_params_eval_queue(struct viif_device *viif_dev)
+{
+ if (!viif_apply_queued_parameter(viif_dev, true))
+ viif_apply_default_parameter(viif_dev);
+}
+
+static int viif_params_enum_fmt_meta_out(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (f->index > 0 || f->type != vdev->queue->type)
+ return -EINVAL;
+
+ f->pixelformat = V4L2_META_FMT_VISCONTI_VIIF_PARAMS;
+
+ return 0;
+}
+
+static int viif_params_g_fmt_meta_out(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_meta_format *meta = &f->fmt.meta;
+
+ if (f->type != vdev->queue->type)
+ return -EINVAL;
+
+ memset(meta, 0, sizeof(*meta));
+ meta->dataformat = V4L2_META_FMT_VISCONTI_VIIF_PARAMS;
+ meta->buffersize = sizeof(struct visconti_viif_isp_config);
+
+ return 0;
+}
+
+static int viif_params_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ strscpy(cap->driver, VIIF_DRIVER_NAME, sizeof(cap->driver));
+ strscpy(cap->card, vdev->name, sizeof(cap->card));
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops viif_params_ioctl = {
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+ .vidioc_enum_fmt_meta_out = viif_params_enum_fmt_meta_out,
+ .vidioc_g_fmt_meta_out = viif_params_g_fmt_meta_out,
+ .vidioc_s_fmt_meta_out = viif_params_g_fmt_meta_out,
+ .vidioc_try_fmt_meta_out = viif_params_g_fmt_meta_out,
+ .vidioc_querycap = viif_params_querycap,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static const struct v4l2_file_operations viif_params_fops = {
+ .mmap = vb2_fop_mmap,
+ .unlocked_ioctl = video_ioctl2,
+ .poll = vb2_fop_poll,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+};
+
+static int viif_params_vb2_queue_setup(struct vb2_queue *q,
+ unsigned int *num_buffers,
+ unsigned int *num_planes,
+ unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ if (*num_planes) {
+ if (*num_planes != 1)
+ return -EINVAL;
+ if (sizes[0] < sizeof(struct visconti_viif_isp_config))
+ return -EINVAL;
+ } else {
+ *num_planes = 1;
+ *num_buffers = clamp_t(u32, *num_buffers,
+ VIIF_PARAMS_REQ_BUFS_MIN,
+ VIIF_PARAMS_REQ_BUFS_MAX);
+ sizes[0] = sizeof(struct visconti_viif_isp_config);
+ }
+
+ return 0;
+}
+
+static inline struct viif_buffer *vb2_to_viif(struct vb2_v4l2_buffer *vbuf)
+{
+ return container_of(vbuf, struct viif_buffer, vb);
+}
+
+static inline struct params_dev *vb2queue_to_paramsdev(struct vb2_queue *vq)
+{
+ return (struct params_dev *)vb2_get_drv_priv(vq);
+}
+
+static void viif_params_vb2_buf_queue(struct vb2_buffer *vb)
+{
+ struct params_dev *params_dev = vb2queue_to_paramsdev(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct viif_buffer *buf = vb2_to_viif(vbuf);
+
+ guard(spinlock_irq)(¶ms_dev->params_lock);
+ list_add_tail(&buf->queue, ¶ms_dev->params_queue);
+}
+
+static int viif_params_vb2_buf_prepare(struct vb2_buffer *vb)
+{
+ if (vb2_plane_size(vb, 0) < sizeof(struct visconti_viif_isp_config))
+ return -EINVAL;
+
+ vb2_set_plane_payload(vb, 0, sizeof(struct visconti_viif_isp_config));
+ return 0;
+}
+
+static int viif_params_vb2_start_streaming(struct vb2_queue *q,
+ unsigned int arg)
+{
+ return 0;
+}
+
+static void viif_params_vb2_stop_streaming(struct vb2_queue *q)
+{
+ struct params_dev *params_dev = vb2queue_to_paramsdev(q);
+ struct viif_buffer *buf;
+ LIST_HEAD(tmp_list);
+
+ scoped_guard(spinlock_irq, ¶ms_dev->params_lock)
+ list_splice_init(¶ms_dev->params_queue, &tmp_list);
+
+ list_for_each_entry(buf, &tmp_list, queue)
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+}
+
+static const struct vb2_ops viif_params_vb2_ops = {
+ .queue_setup = viif_params_vb2_queue_setup,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .buf_queue = viif_params_vb2_buf_queue,
+ .buf_prepare = viif_params_vb2_buf_prepare,
+ .start_streaming = viif_params_vb2_start_streaming,
+ .stop_streaming = viif_params_vb2_stop_streaming,
+};
+
+int visconti_viif_params_register(struct viif_device *viif_dev)
+{
+ struct params_dev *params_dev = &viif_dev->params_dev;
+ struct video_device *vdev = ¶ms_dev->vdev;
+ struct vb2_queue *q = ¶ms_dev->vb2_vq;
+ int ret;
+
+ mutex_init(¶ms_dev->vlock);
+ INIT_LIST_HEAD(¶ms_dev->params_queue);
+ spin_lock_init(¶ms_dev->params_lock);
+
+ strscpy(vdev->name, "viif_params", sizeof(vdev->name));
+
+ /* Register the video device */
+ video_set_drvdata(vdev, params_dev);
+ vdev->ioctl_ops = &viif_params_ioctl;
+ vdev->fops = &viif_params_fops;
+ vdev->release = video_device_release_empty;
+ vdev->lock = ¶ms_dev->vlock;
+ vdev->v4l2_dev = &viif_dev->v4l2_dev;
+ vdev->queue = ¶ms_dev->vb2_vq;
+ vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_META_OUTPUT;
+ vdev->vfl_dir = VFL_DIR_TX;
+
+ /* Initialize vb2 queue */
+ q->type = V4L2_BUF_TYPE_META_OUTPUT;
+ q->io_modes = VB2_MMAP | VB2_DMABUF;
+ q->drv_priv = params_dev;
+ q->ops = &viif_params_vb2_ops;
+ q->mem_ops = &vb2_vmalloc_memops;
+ q->buf_struct_size = sizeof(struct viif_buffer);
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->lock = ¶ms_dev->vlock;
+ q->dev = viif_dev->v4l2_dev.dev;
+
+ ret = vb2_queue_init(q);
+ if (ret)
+ return ret;
+
+ params_dev->params_pad.flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_pads_init(&vdev->entity, VIIF_PARAMS_PAD_NUM,
+ ¶ms_dev->params_pad);
+ if (ret)
+ goto error;
+
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+ if (ret) {
+ dev_err(viif_dev->v4l2_dev.dev,
+ "video_register_device failed: %d\n", ret);
+ goto error;
+ }
+
+ return 0;
+
+error:
+ media_entity_cleanup(&vdev->entity);
+ mutex_destroy(¶ms_dev->vlock);
+
+ return ret;
+}
+
+void visconti_viif_params_unregister(struct viif_device *viif_dev)
+{
+ struct params_dev *params = &viif_dev->params_dev;
+ struct video_device *vdev = ¶ms->vdev;
+
+ if (!video_is_registered(vdev))
+ return;
+
+ vb2_video_unregister_device(vdev);
+ media_entity_cleanup(&vdev->entity);
+ mutex_destroy(¶ms->vlock);
+}
diff --git a/drivers/media/platform/toshiba/visconti/viif_params.h b/drivers/media/platform/toshiba/visconti/viif_params.h
new file mode 100644
index 000000000000..a244459b6530
--- /dev/null
+++ b/drivers/media/platform/toshiba/visconti/viif_params.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * Toshiba Visconti Video Capture Support
+ *
+ * (C) Copyright 2025 TOSHIBA CORPORATION
+ * (C) Copyright 2025 Toshiba Electronic Devices & Storage Corporation
+ */
+
+#ifndef __VIIF_PARAMS_H__
+#define __VIIF_PARAMS_H__
+
+struct viif_device;
+
+void visconti_viif_params_eval_queue(struct viif_device *viif_dev);
+void visconti_viif_params_isr(struct viif_device *viif_dev);
+int visconti_viif_params_register(struct viif_device *viif_dev);
+void visconti_viif_params_unregister(struct viif_device *viif_dev);
+
+void visconti_viif_l2_undist_through(struct viif_device *viif_dev);
+#endif /* __VIIF_PARAMS_H__ */
diff --git a/drivers/media/platform/toshiba/visconti/viif_stats.c b/drivers/media/platform/toshiba/visconti/viif_stats.c
new file mode 100644
index 000000000000..da20824e64a5
--- /dev/null
+++ b/drivers/media/platform/toshiba/visconti/viif_stats.c
@@ -0,0 +1,320 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * Toshiba Visconti Video Capture Support
+ *
+ * (C) Copyright 2025 TOSHIBA CORPORATION
+ * (C) Copyright 2025 Toshiba Electronic Devices & Storage Corporation
+ */
+
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-vmalloc.h>
+
+#include "viif.h"
+#include "viif_common.h"
+#include "viif_isp.h"
+#include "viif_regs.h"
+#include "viif_stats.h"
+
+#define VIIF_STATS_REQ_BUFS_MIN 2
+#define VIIF_STATS_REQ_BUFS_MAX 8
+
+struct viif_buffer {
+ struct vb2_v4l2_buffer vb;
+ struct list_head queue;
+};
+
+static void viif_stats_read_isp_regs(struct viif_l1_info *l1_info,
+ struct viif_device *viif_dev)
+{
+ int i, j;
+ u32 val;
+
+ guard(spinlock_irqsave)(&viif_dev->regbuf_lock);
+ guard(viif_isp)(viif_dev);
+
+ /* Change register buffer to regbuf0 where driver gets information */
+ viif_capture_write(viif_dev, REG_L1_CRGBF_ACC_CONF,
+ VAL_L1_CRGBF_ACC_CONF_MODE_BUFFER0);
+
+ l1_info->awb_ave_u = viif_capture_read(viif_dev, REG_L1_AWHB_AVE_USIG);
+ l1_info->awb_ave_v = viif_capture_read(viif_dev, REG_L1_AWHB_AVE_VSIG);
+ l1_info->awb_accumulated_pixel =
+ viif_capture_read(viif_dev, REG_L1_AWHB_NUM_UVON);
+ l1_info->awb_gain_r = viif_capture_read(viif_dev, REG_L1_AWHB_AWBGAINR);
+ l1_info->awb_gain_g = viif_capture_read(viif_dev, REG_L1_AWHB_AWBGAING);
+ l1_info->awb_gain_b = viif_capture_read(viif_dev, REG_L1_AWHB_AWBGAINB);
+ val = viif_capture_read(viif_dev, REG_L1_AWHB_R_CTR_STOP);
+ l1_info->awb_status_u = (FIELD_GET(BIT(1), val) != 0);
+ l1_info->awb_status_v = (FIELD_GET(BIT(0), val) != 0);
+
+ l1_info->avg_lum_weight =
+ viif_capture_read(viif_dev, REG_L1_AEXP_RESULT_AVE);
+ val = viif_capture_read(viif_dev, REG_L1_AEXP_SATUR_BLACK_PIXNUM);
+ l1_info->avg_satur_pixnum = FIELD_GET(GENMASK(31, 16), val);
+ l1_info->avg_black_pixnum = FIELD_GET(GENMASK(15, 0), val);
+ for (i = 0; i < 8; i++) {
+ for (j = 0; j < 8; j++) {
+ l1_info->avg_lum_block[i][j] = viif_capture_read(
+ viif_dev, REG_L1_AEXP_AVE(i, j));
+ }
+ }
+ l1_info->avg_lum_four_line_lum[0] =
+ viif_capture_read(viif_dev, REG_L1_AEXP_AVE4LINES0);
+ l1_info->avg_lum_four_line_lum[1] =
+ viif_capture_read(viif_dev, REG_L1_AEXP_AVE4LINES1);
+ l1_info->avg_lum_four_line_lum[2] =
+ viif_capture_read(viif_dev, REG_L1_AEXP_AVE4LINES2);
+ l1_info->avg_lum_four_line_lum[3] =
+ viif_capture_read(viif_dev, REG_L1_AEXP_AVE4LINES3);
+
+ /* Revert to register access from register buffer access */
+ viif_capture_write(viif_dev, REG_L1_CRGBF_ACC_CONF,
+ VAL_L1_CRGBF_ACC_CONF_MODE_BYPASS);
+}
+
+void visconti_viif_stats_isr(struct viif_device *viif_dev,
+ unsigned int sequence, u64 timestamp)
+{
+ struct visconti_viif_isp_stat *cur_stat_buf;
+ struct stats_dev *stats_dev = &viif_dev->stats_dev;
+ struct viif_buffer *cur_buf;
+
+ guard(spinlock)(&stats_dev->stats_lock);
+
+ if (list_empty(&stats_dev->stats_queue))
+ return;
+
+ cur_buf = list_first_entry(&stats_dev->stats_queue, struct viif_buffer,
+ queue);
+ list_del(&cur_buf->queue);
+ cur_stat_buf = (struct visconti_viif_isp_stat *)vb2_plane_vaddr(
+ &cur_buf->vb.vb2_buf, 0);
+
+ viif_stats_read_isp_regs(&cur_stat_buf->isp_capture.l1_info, viif_dev);
+ cur_stat_buf->stat_type |= VISCONTI_VIIF_STAT_L1_INFO;
+
+ vb2_set_plane_payload(&cur_buf->vb.vb2_buf, 0,
+ sizeof(struct visconti_viif_isp_stat));
+
+ cur_buf->vb.sequence = sequence;
+ cur_buf->vb.vb2_buf.timestamp = timestamp;
+ vb2_buffer_done(&cur_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+}
+
+static int viif_stats_enum_fmt_meta_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (f->index > 0 || f->type != vdev->queue->type)
+ return -EINVAL;
+
+ f->pixelformat = V4L2_META_FMT_VISCONTI_VIIF_STATS;
+
+ return 0;
+}
+
+static int viif_stats_g_fmt_meta_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_meta_format *meta = &f->fmt.meta;
+
+ if (f->type != vdev->queue->type)
+ return -EINVAL;
+
+ memset(meta, 0, sizeof(*meta));
+ meta->dataformat = V4L2_META_FMT_VISCONTI_VIIF_STATS;
+ meta->buffersize = sizeof(struct visconti_viif_isp_stat);
+
+ return 0;
+}
+
+static int viif_stats_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ strscpy(cap->driver, VIIF_DRIVER_NAME, sizeof(cap->driver));
+ strscpy(cap->card, vdev->name, sizeof(cap->card));
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops viif_stats_ioctl = {
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+ .vidioc_enum_fmt_meta_cap = viif_stats_enum_fmt_meta_cap,
+ .vidioc_g_fmt_meta_cap = viif_stats_g_fmt_meta_cap,
+ .vidioc_s_fmt_meta_cap = viif_stats_g_fmt_meta_cap,
+ .vidioc_try_fmt_meta_cap = viif_stats_g_fmt_meta_cap,
+ .vidioc_querycap = viif_stats_querycap,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static const struct v4l2_file_operations viif_stats_fops = {
+ .mmap = vb2_fop_mmap,
+ .unlocked_ioctl = video_ioctl2,
+ .poll = vb2_fop_poll,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+};
+
+static int viif_stats_vb2_queue_setup(struct vb2_queue *vq,
+ unsigned int *num_buffers,
+ unsigned int *num_planes,
+ unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ if (*num_planes) {
+ if (*num_planes != 1)
+ return -EINVAL;
+ if (sizes[0] < sizeof(struct visconti_viif_isp_stat))
+ return -EINVAL;
+ } else {
+ *num_planes = 1;
+ *num_buffers = clamp_t(u32, *num_buffers,
+ VIIF_STATS_REQ_BUFS_MIN,
+ VIIF_STATS_REQ_BUFS_MAX);
+ sizes[0] = sizeof(struct visconti_viif_isp_stat);
+ }
+
+ return 0;
+}
+
+static inline struct viif_buffer *vb2_to_viif(struct vb2_v4l2_buffer *vbuf)
+{
+ return container_of(vbuf, struct viif_buffer, vb);
+}
+
+static inline struct stats_dev *vb2queue_to_statsdev(struct vb2_queue *q)
+{
+ return (struct stats_dev *)vb2_get_drv_priv(q);
+}
+
+static void viif_stats_vb2_buf_queue(struct vb2_buffer *vb)
+{
+ struct stats_dev *stats_dev = vb2queue_to_statsdev(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct viif_buffer *buf = vb2_to_viif(vbuf);
+
+ guard(spinlock_irq)(&stats_dev->stats_lock);
+ list_add_tail(&buf->queue, &stats_dev->stats_queue);
+}
+
+static int viif_stats_vb2_buf_prepare(struct vb2_buffer *vb)
+{
+ if (vb2_plane_size(vb, 0) < sizeof(struct visconti_viif_isp_stat))
+ return -EINVAL;
+
+ vb2_set_plane_payload(vb, 0, sizeof(struct visconti_viif_isp_stat));
+
+ return 0;
+}
+
+static void viif_stats_vb2_stop_streaming(struct vb2_queue *q)
+{
+ struct stats_dev *stats_dev = vb2queue_to_statsdev(q);
+ struct viif_buffer *buf, *tmp;
+
+ guard(spinlock_irq)(&stats_dev->stats_lock);
+
+ list_for_each_entry_safe(buf, tmp, &stats_dev->stats_queue, queue) {
+ list_del(&buf->queue);
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ }
+}
+
+static const struct vb2_ops viif_stats_vb2_ops = {
+ .queue_setup = viif_stats_vb2_queue_setup,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .buf_queue = viif_stats_vb2_buf_queue,
+ .buf_prepare = viif_stats_vb2_buf_prepare,
+ .stop_streaming = viif_stats_vb2_stop_streaming,
+};
+
+int visconti_viif_stats_register(struct viif_device *viif_dev)
+{
+ struct stats_dev *stats_dev = &viif_dev->stats_dev;
+ struct video_device *vdev = &stats_dev->vdev;
+ struct vb2_queue *q = &stats_dev->vb2_vq;
+ int ret;
+
+ mutex_init(&stats_dev->vlock);
+ INIT_LIST_HEAD(&stats_dev->stats_queue);
+ spin_lock_init(&stats_dev->stats_lock);
+
+ strscpy(vdev->name, "viif_stats", sizeof(vdev->name));
+
+ /* Register the video device */
+ video_set_drvdata(vdev, stats_dev);
+ vdev->ioctl_ops = &viif_stats_ioctl;
+ vdev->fops = &viif_stats_fops;
+ vdev->release = video_device_release_empty;
+ vdev->lock = &stats_dev->vlock;
+ vdev->v4l2_dev = &viif_dev->v4l2_dev;
+ vdev->queue = &stats_dev->vb2_vq;
+ vdev->device_caps = V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING;
+ vdev->vfl_dir = VFL_DIR_RX;
+
+ /* Initialize vb2 queue */
+ q->type = V4L2_BUF_TYPE_META_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_DMABUF;
+ q->drv_priv = stats_dev;
+ q->ops = &viif_stats_vb2_ops;
+ q->mem_ops = &vb2_vmalloc_memops;
+ q->buf_struct_size = sizeof(struct viif_buffer);
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->lock = &stats_dev->vlock;
+ q->dev = viif_dev->v4l2_dev.dev;
+
+ ret = vb2_queue_init(q);
+ if (ret)
+ return ret;
+
+ stats_dev->stats_pad.flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_pads_init(&vdev->entity, VIIF_STATS_PAD_NUM,
+ &stats_dev->stats_pad);
+ if (ret)
+ goto error;
+
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+ if (ret) {
+ dev_err(viif_dev->v4l2_dev.dev,
+ "video_register_device failed: %d\n", ret);
+ goto error;
+ }
+
+ return 0;
+
+error:
+ media_entity_cleanup(&vdev->entity);
+ mutex_destroy(&stats_dev->vlock);
+
+ return ret;
+}
+
+void visconti_viif_stats_unregister(struct viif_device *viif_dev)
+{
+ struct stats_dev *stats_dev = &viif_dev->stats_dev;
+ struct video_device *vdev = &stats_dev->vdev;
+
+ if (!video_is_registered(vdev))
+ return;
+
+ vb2_video_unregister_device(vdev);
+ media_entity_cleanup(&vdev->entity);
+ mutex_destroy(&stats_dev->vlock);
+}
diff --git a/drivers/media/platform/toshiba/visconti/viif_stats.h b/drivers/media/platform/toshiba/visconti/viif_stats.h
new file mode 100644
index 000000000000..ca83d45e3b39
--- /dev/null
+++ b/drivers/media/platform/toshiba/visconti/viif_stats.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * Toshiba Visconti Video Capture Support
+ *
+ * (C) Copyright 2025 TOSHIBA CORPORATION
+ * (C) Copyright 2025 Toshiba Electronic Devices & Storage Corporation
+ */
+
+#ifndef __VIIF_STATS_H__
+#define __VIIF_STATS_H__
+
+void visconti_viif_stats_isr(struct viif_device *viif_dev,
+ unsigned int sequence, u64 timestamp);
+int visconti_viif_stats_register(struct viif_device *viif_dev);
+void visconti_viif_stats_unregister(struct viif_device *viif_dev);
+#endif /* __VIIF_STATS_H__ */
--
2.34.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v13 7/7] documentation: media: Add documentation for Toshiba Visconti Video Input Interface driver
2025-10-16 2:24 [PATCH v13 0/7] Add Toshiba Visconti Video Input Interface driver Yuji Ishikawa
` (4 preceding siblings ...)
2025-10-16 2:24 ` [PATCH v13 6/7] media: platform: visconti: Add streaming interface for ISP parameters and statistics Yuji Ishikawa
@ 2025-10-16 2:24 ` Yuji Ishikawa
5 siblings, 0 replies; 15+ messages in thread
From: Yuji Ishikawa @ 2025-10-16 2:24 UTC (permalink / raw)
To: Nobuhiro Iwamatsu, Yuji Ishikawa, Mauro Carvalho Chehab,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Philipp Zabel
Cc: linux-media, devicetree, linux-arm-kernel, linux-kernel
Add description of Video Input Interface driver of
Toshiba Visconti architecture.
It includes hardware organization, structure of the driver
and metadata format for embedded image signal processor.
Signed-off-by: Yuji Ishikawa <yuji2.ishikawa@toshiba.co.jp>
---
Changelog v3:
- Newly add documentation to describe SW and HW
Changelog v4:
- no change
Changelog v5:
- no change
Changelog v6:
- add description of CSI2RX subdevice
- add ordering of ioctl(S_FMT) and ioctl(S_EXT_CTRLS)
Changelog v7:
- no change
Changelog v8:
- add usage of V4L2_CTRL_TYPE_VISCONTI_ISP
Changelog v9:
- fix warning: set reference target for keyword V4L2_CTRL_TYPE_VISCONTI_ISP
Changelog v10:
- use parameter buffers instead of compound control
- removed description of vendor specific compound control
- add description of parameter buffers for ISP control
- update directory structure
- remove documents under driver-api
- add documents to admin-guide, userspace-api
Changelog v11:
- update usage of the driver
Changelog v12:
- add description of CSI2RX driver
- description of resizer subdevice
- add block diagrams of VIIF and ISP
- update usage of the driver
Changelog v13:
- wrap one line at 80 characters
- add entries to MAINTAINERS flle
- update media subdevice diagram to remove resizer subdevices
- Clarify whether it is a line break around the 80-character mark or a paragraph transition.
- add detailed description for SLIC module.
- update description of HDRC: use of term "global and local tone mapping"
- update description of AWHB: algorithm of whitebalance is handled by hardware only
- what a keyword "AG" mean?: analog gain (general term for image sensors) / algorithm gain (VIIF specific term)
- PWL image have 14bit depth
- update illustration of preprocessing; most of the modules handle 12bit intermediate images
- update illustration of L2ISP
- add output format UYVY
- fix references to struct viif_l2_undist_config and viif_l2_roi_config
- add description of scaling and cropping
- add reference to viif_l2_crop_config
- update Capturing example
- add description of debugfs
---
Documentation/admin-guide/media/v4l-drivers.rst | 1 +
Documentation/admin-guide/media/visconti-viif.dot | 18 +
Documentation/admin-guide/media/visconti-viif.rst | 540 +++++++++++++++++++++
.../userspace-api/media/v4l/meta-formats.rst | 1 +
.../media/v4l/metafmt-visconti-viif.rst | 48 ++
MAINTAINERS | 2 +
6 files changed, 610 insertions(+)
diff --git a/Documentation/admin-guide/media/v4l-drivers.rst b/Documentation/admin-guide/media/v4l-drivers.rst
index 3bac5165b134..b6b0b22a12a6 100644
--- a/Documentation/admin-guide/media/v4l-drivers.rst
+++ b/Documentation/admin-guide/media/v4l-drivers.rst
@@ -33,5 +33,6 @@ Video4Linux (V4L) driver-specific documentation
si476x
starfive_camss
vimc
+ visconti-viif
visl
vivid
diff --git a/Documentation/admin-guide/media/visconti-viif.dot b/Documentation/admin-guide/media/visconti-viif.dot
new file mode 100644
index 000000000000..fb74c350b50d
--- /dev/null
+++ b/Documentation/admin-guide/media/visconti-viif.dot
@@ -0,0 +1,18 @@
+digraph board {
+ rankdir=TB
+ n00000001 [label="{{<port0> 0 | <port4> 4} | visconti-viif:isp\n/dev/v4l-subdev0 | {<port1> 1 | <port2> 2 | <port3> 3 | <port5> 5}}", shape=Mrecord, style=filled, fillcolor=green]
+ n00000001:port1 -> n00000008
+ n00000001:port2 -> n0000000c
+ n00000001:port3 -> n00000010
+ n00000001:port5 -> n00000018
+ n00000008 [label="viif_capture_post0\n/dev/video0", shape=box, style=filled, fillcolor=yellow]
+ n0000000c [label="viif_capture_post1\n/dev/video1", shape=box, style=filled, fillcolor=yellow]
+ n00000010 [label="viif_capture_sub\n/dev/video2", shape=box, style=filled, fillcolor=yellow]
+ n00000014 [label="viif_params\n/dev/video3", shape=box, style=filled, fillcolor=yellow]
+ n00000014 -> n00000001:port4
+ n00000018 [label="viif_stats\n/dev/video4", shape=box, style=filled, fillcolor=yellow]
+ n00000026 [label="{{<port0> 0} | visconti_csi2rx 1c008000.csi2rx\n/dev/v4l-subdev1 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
+ n00000026:port1 -> n00000001:port0
+ n0000002b [label="{{} | imx219 1-0010\n/dev/v4l-subdev2 | {<port0> 0}}", shape=Mrecord, style=filled, fillcolor=green]
+ n0000002b:port0 -> n00000026:port0 [style=bold]
+}
diff --git a/Documentation/admin-guide/media/visconti-viif.rst b/Documentation/admin-guide/media/visconti-viif.rst
new file mode 100644
index 000000000000..fbe177b0709a
--- /dev/null
+++ b/Documentation/admin-guide/media/visconti-viif.rst
@@ -0,0 +1,540 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+======================================================
+Visconti Video Input Interface Driver (visconti-viif)
+======================================================
+
+Introduction
+============
+
+This file documents the driver for the Video Input Interface (VIIF) that is
+part of Toshiba Visconti SoCs. The driver is located under
+drivers/media/platform/toshiba/visconti and uses the Media-Controller API.
+
+The driver module is named visconti-viif, and is enabled through the
+CONFIG_VIDEO_VISCONTI_VIIF config option. The CSI-2 receiver part is controlled
+by another module named visconti-csi2rx, which is enabled through the
+CONFIG_VIDEO_VISCONTI_CSI2RX config option.
+
+The Visconti VIIF Hardware
+==========================
+
+The Visconti VIIF hardware is an internally developed video capture device.
+Following function modules are integrated:
+
+* MIPI CSI-2 receiver (CSI2RX)
+* L1 Image Signal Processor (L1ISP)
+
+ * Correction, enhancement, adjustment on bayer images.
+
+* L2 Image Signal Processor (L2ISP)
+
+ * Lens distortion correction
+ * Scaling & Cropping with up to 2 parameter sets
+ * Formatting picture (RGB, YUV, Grayscale, ...)
+ * Integrated DMAC: Writing picture into main memory
+
+* Video DMAC
+
+ * Writing picture into main memory
+
+Visconti5 SoC has two VIIF hardware instances.
+
+
+The hardware block diagram is shown below.::
+
+ The VIIF hardware
+ "POST0"
+ "RGB with scale 0"
+ +--------+ +----------+ +-----+ +-----+ +-----+ +--------+
+ | Sensor |--->| CSI2RX |--->| | | | | |--->| memory |
+ +--------+ +----------+ | | | | | | +--------+
+ | | | L1 | | L2 | "POST1"
+ | |--->| ISP |--->| ISP | "RGB with scale 1"
+ | | | | | | +--------+
+ | MUX | | | | |--->| memory |
+ | | +-----+ +-----+ +--------+
+ | | "SUB"
+ | | "RAW w/o scale"
+ | | +------------+ +--------+
+ | |------> | Video DMAC |--->| memory |
+ +-----+ +------------+ +--------+
+
+Topology
+========
+
+Graph
+-----
+
+.. _visconti_viif_topology_graph:
+
+.. kernel-figure:: visconti-viif.dot
+ :alt: Diagram of the default media pipeline topology
+ :align: center
+
+The driver has 3 video devices for capturing images:
+
+- viif_capture_post0: capture device for image.
+ - corresponds to L2ISP.
+- viif_capture_post1: capture device for image.
+ - corresponds to L2ISP.
+- viif_capture_sub: capture device for bayer image.
+ - corresponds to Video DMAC.
+
+The driver has 2 video devices for controlling ISP.
+
+- viif_params: a metadata output device that receives ISP parameters.
+ - corresponds to L1ISP and L2ISP.
+- viif_stats: a metadata capture device that sends statistics.
+ - corresponds to L1ISP and L2ISP.
+
+The driver has 2 subdevices:
+
+- visconti_csi2rx: CSI-2 receiver operation.
+ - corresponds to CSI2RX.
+- visconti-viif:isp: Image Signal Processor operation.
+ - corresponds to L1ISP and L2ISP.
+
+visconti_csi2rx - CSI2 Receiver Subdevice Node
+----------------------------------------------
+
+This subdevice node corresponds to a MIPI CSI2 receiver. It resides between
+an image sensor subdevice and the ISP subdevice. It controls CSI2 link
+configuration and training process.
+
+visconti-viif:isp - ISP Subdevice Node
+--------------------------------------
+
+This subdevice node corresponds to L1/L2 ISPs. It receives pictures from a
+sensor (via CSI2RX), applies multiple operations on pictures, then passes
+resulting images to capture nodes.
+
+ISP configurations/parameters are passed from userland via viif_params node.
+The statistics computed by the ISP are passed to userland via viif_stats node.
+
+L1 ISP provides following operations:
+
+- Input: accepts 8, 10, 12, 14bit bayer format
+ - Operation selector; see :c:type:`viif_l1_input_mode_config`
+ - HDR image / PWL (Piecewise Linear Compression) image
+ - with preprocessing / without preprocessing
+ - HDRE: HDR expansion (only for PWL image);
+ see :c:type:`viif_l1_hdre_config`
+- Preprocessing: generate intermediate data (24bit RAW)
+ - SLIC: Bit slicing
+ - see :c:type:`viif_l1_img_extraction_config`
+ - From a 24-bit input image, up to three 12-bit images with different
+ sensitivities (high, middle, low) are generated. Each preprocessing
+ operation is performed on the generated images. In preprocess,
+ parameters with the modifiers h, m, and l are used respectively.
+ - This driver does not skip SLIC operation. As a result, it does not
+ support DOL-HDR, in which multiple 12-bit images are directly
+ acquired from the sensor for preprocessing.
+ - ABPC/DPC: Blemish/Defect pixel correction :c:type:`viif_l1_dpc_config`
+ - PWHB: Preset white balance;
+ see :c:type:`viif_l1_preset_white_balance_config`
+ - RCNR: RAW color noise reduction;
+ see :c:type:`viif_l1_raw_color_noise_reduction_config`
+ - HDRS: HDR synthesis
+ - see :c:type:`viif_l1_hdrs_config`
+ - A 24-bit image is generated from the preprocessed 12-bit images.
+- Processing on RAW image: Main Process (MPRO)
+ - BLVC: black level correction and normalization;
+ see :c:type:`viif_l1_black_level_correction_config`
+ - LSSC: Lens shading correction; see :c:type:`viif_l1_lsc_config`
+ - MPRO: digital amplifier; see :c:type:`viif_l1_main_process_config`
+ - MPRO: bayer demosaicing; see :c:type:`viif_l1_main_process_config`
+ - MPRO: color matrix correction; see :c:type:`viif_l1_main_process_config`
+ - HDRC: HDR compression (global and local tone mapping);
+ see :c:type:`viif_l1_hdrc_config`, :c:type:`viif_l1_hdrc_ltm_config`,
+ :c:type:`viif_l1_rgb_to_y_coef_config`
+- Processing on RGB/YUV image: Video Process (VPRO)
+ - VPRO: gamma correction; see :c:type:`viif_l1_gamma_config`
+ - VPRO: RGB2YUV;
+ see :c:type:`viif_l1_rgb_to_y_coef_config`,
+ :c:type:`viif_l1_img_quality_adjustment_config`
+ - VPRO: image quality adjustment;
+ see :c:type:`viif_l1_img_quality_adjustment_config`
+- Output: 16bit YUV
+- Feedback loop
+ - AWHB: auto white balance
+ - see :c:type:`viif_l1_awb_config`, :c:type:`viif_isp_capture_status`
+ - The gain for the R and B components is adjusted so that the average U
+ and V components of the image become zero. This process is handled by
+ hardware and does not require feedback from external software.
+ - AEXP: auto exposure (average luminance calculation);
+ see :c:type:`viif_l1_avg_lum_generation_config`,
+ :c:type:`viif_l1_rgb_to_y_coef_config`, :c:type:`viif_isp_capture_status`
+ - AG: algorithm gain calculation;
+ see :c:type:`viif_l1_ag_mode_config`, :c:type:`viif_l1_ag_config`
+
+Below is the block diagram::
+
+ L1ISP::INPUT
+
+ +--------+ +-----+ +-----+
+ | Input |--------------->| |--------------------->| |
+ | 24bHDR | | | | |
+ +--------+ | 24b | | 24b |
+ | RAW | | RAW |
+ +--------+ +------+ | (0) | | (1) |
+ | Input |--->| HDRE |--->| | +------------+ | |
+ | 14bPWL | | | | |--->| preprocess |--->| |
+ +--------+ +------+ +-----+ +------------+ +-----+
+
+ L1ISP::INPUT::preprocess
+
+ +-----+ +-----+
+ | 24b | +------+ | 12b |
+ | RAW |--->| SLIC |--->| RAW |---+
+ | (0) | +------+ | x3 | |
+ +-----+ +-----+ |
+ |
+ +-----------------------------+
+ |
+ | +-----+ +-----+
+ | +------+ +------+ +------+ | 12b | +------+ | 24b |
+ +--->| ABPC |--->| PWHB |--->| RCNR |--->| RAW |--->| HDRS |--->| RAW |
+ +------+ +------+ +------+ | x3 | +------+ | (1) |
+ +-----+ +-----+
+
+
+ L1ISP::MainProcess(MPRO)
+
+ +-----+
+ | 24b | +------+ +------+
+ | RAW |--->| BLVC |--->| LSSC |---+
+ | (1) | +------+ +------+ |
+ +-----+ |
+ |
+ +------------------------------+
+ |
+ | +-----------+ +-------------+ +--------+ +-----+
+ +--->| MPRO | | MPRO | | MPRO | +------+ | 16b |
+ | Digital |--->| Demosaicing |----| Color |--->| HDRC |--->| RGB |
+ +--->| Amplifier | | | | Matrix | +------+ | |
+ | +-----------+ +-------------+ +--------+ +-----+
+ | | |
+ | +--------------+ | | +------+
+ +----| Auto |<----+ +--->| AEXP |---> Auto-Exposure statistics
+ | Whitebalance | +------+
+ +--------------+
+ |
+ +------------------------------> Auto-Whitebalance statistics
+
+ L1ISP::VideoProcess(VPRO)
+
+ +-----+ +------------+ +------------+ +---------------+ +--------+
+ | 16b |--->| Gamma |--->| RGB2YUV |--->| Image Quality |--->| Output |
+ | RGB | | Correction | | Conversion | | Adjustment | | 16b |
+ | | +------------+ +------------+ +---------------+ | YUV |
+ +-----+ +--------+
+
+ L1ISP::AlgorithmGain
+
+ statistics +----------------+ +------------------+
+ information ---> (user SW) --->| Algorithm Gain |--->| ABPC, RCNR, LSSC |
+ +----------------+ | MPRO, VPRO |
+ +------------------+
+
+L2 ISP provides following operations:
+
+- Input: accepts 16bit YUV / RGB
+- Operations:
+ - Lens undistortion; see :c:type:`viif_l2_undist_config`
+ - Scaling; see :c:type:`viif_l2_roi_config`
+ - Cropping; see :c:type:`viif_l2_roi_config`, :c:type:`viif_l2_crop_config`
+ - Gamma correction; see :c:type:`viif_l2_gamma_config`
+ - YUV2RGB
+- Output: RGB, YUV422, YUV444
+
+Below is the block diagram::
+
+ L2ISP
+
+ +-------+ +------------+ +--------------+
+ | Input |--->| YUV2RGB |--->| Lens |---+
+ | Image | | Conversion | | Undistortion | |
+ +-------+ +------------+ +--------------+ |
+ |
+ +-------------------------------------------------+
+ |
+ | +---------+ +------------+ +------------+ +--------+ +--------+
+ +--->| Scaling |--->| Gamma |--->| Colorspace |--->| Data |--->| Output |
+ | | | | Correction | | Conversion | | Packer | | Image |
+ | +---------+ +------------+ +------------+ +--------+ +--------+
+ |
+ | +---------+ +------------+ +------------+ +--------+ +--------+
+ +--->| Scaling |--->| Gamma |--->| Colorspace |--->| Data |--->| Output |
+ | | | Correction | | Conversion | | Packer | | Image |
+ +---------+ +------------+ +------------+ +--------+ +--------+
+
+viif_capture_post0, viif_capture_post1 - Processed Image Capture Video Node
+---------------------------------------------------------------------------
+
+These video nodes are used for capturing images processed at ISPs. Supported
+capture formats are as follows:
+
+- V4L2_PIX_FMT_ABGR32
+- V4L2_PIX_FMT_RGB24
+- V4L2_PIX_FMT_UYVY
+- V4L2_PIX_FMT_Y16
+- V4L2_PIX_FMT_YUV422M
+- V4L2_PIX_FMT_YUV444M
+
+Bayer format is not supported. Use viif_capture_sub instead.
+
+POST0 and POST1 can output images from the same input image using different
+cropping and scaling settings.
+
+viif_capture_sub - Raw Image Capture Video Node
+-----------------------------------------------
+
+This video node is used for capturing bayer image from the sensor. The output
+picture has exactly the same resolution and format as the sensor input. The
+pipeline does not edit pixel values. However, when writing pixel values to
+memory, they are shifted to the MSB to match either 8bit or 16bit.
+
+Therefore, resulting capture formats are as follows:
+
+- for 8bit RAW input:
+ - V4L2_PIX_FMT_SRGGB8
+ - V4L2_PIX_FMT_SGRBG8
+ - V4L2_PIX_FMT_SGBRG8
+ - V4L2_PIX_FMT_SBGGR8
+- for 10, 12, 14bit RAW input:
+ - V4L2_PIX_FMT_SRGGB16
+ - V4L2_PIX_FMT_SGRBG16
+ - V4L2_PIX_FMT_SGBRG16
+ - V4L2_PIX_FMT_SBGGR16
+
+.. _viif_params:
+
+viif_params - ISP Parameters Video Node
+---------------------------------------
+
+The viif_params video node receives a set of ISP parameters from userspace to be
+applied to the hardware during a video stream.
+
+The buffer format is defined by struct :c:type:`visconti_viif_isp_config`, and
+userspace should set
+:ref:`V4L2_META_FMT_VISCONTI_VIIF_PARAMS <v4l2-meta-fmt-visconti-viif-params>`
+as the data format.
+
+.. _viif_stats:
+
+viif_stats - Statistics Video Node
+----------------------------------
+
+The viif_stats video node provides statistics computed by the ISP.
+
+Following information is included:
+
+* statistics of auto white balance
+* average luminance information which can be used by auto exposure software
+ implementation.
+
+The buffer format is defined by struct :c:type:`visconti_viif_isp_stat`, and
+userspace should set
+:ref:`V4L2_META_FMT_VISCONTI_VIIF_STATS <v4l2-meta-fmt-visconti-viif-stats>`
+as the data format.
+
+Feedback Operations
+===================
+
+Among the so-called 3A functions, VIIF provides only auto-whitebalance and
+auto-exposure. Auto-whitebalance is a standalone hardware feature. Some status
+information is available through the ISP statistics interface.
+
+Auto-exposure is realized through a combination of hardware and userland
+software. VIIF provides weighted average luminance information through the ISP
+statistics interface. The userland application calculates the sensor gain,
+sensor exposure and ISP digital gain. The calculated parameters are then passed
+to sensor's controls and the ISP parameter interface.
+
+AG (algorithm gain) is a mechanism that synchronously adjusts the processing
+strength across multiple functional blocks. For specific usage details please
+refer to :c:type:`viif_l1_ag_mode_config`. The following is the list of
+functions affected by AG parameters.
+
+- ABPC/DPC
+- RCNR
+- LSSC
+- MPRO: color matrix correction
+- VPRO
+
+Scaling and cropping
+====================
+
+The L2 ISP provides an image transformation operation that integrates
+undistortion and scaling. Therefore, scaling and cropping cannot be represented
+using the COMPOSE and CROP rectangles commonly used in V4L2.
+
+This driver represents scaling and cropping using the following method.
+
+- Scaling is configured using :c:type:`viif_l2_roi_config`.
+ - The scaling ratio can be specified within the range of 0.5 to 2.0.
+ - The aspect ratio cannot be changed.
+ - In :c:type:`viif_l2_roi_config`, it is necessary to properly specify the
+ boundaries used in coordinate calculations for scaling and undistortion.
+ - The final image size is specified by corrected_hsize and corrected_vsize.
+- Cropping is configured using following information.
+ - The source image size for cropping is corrected_hsize and corrected_vsize.
+ - The top-left coordinate is set using :c:type:`viif_l2_crop_config`.
+ - The cropped size is specified by the format size of the corresponding ISP
+ source pad.
+
+Capturing Video Frames Example
+==============================
+
+In the following example, imx219 camera is connected to pad 0 of
+'visconti_csi2rx' subdevice.
+
+The following commands yield three pictures:
+
+- main path 0: 1920x1080, RGB picture
+- main path 1: 640x480, UYVY picture, cropping (0, 0, 640, 480)
+- sub path: 1920x1080 RAW picture
+
+.. code-block:: bash
+
+ # set the links
+ media-ctl -d platform:1c000000.viif -r
+ media-ctl -d platform:1c000000.viif -l '"imx219 1-0010":0 -> "visconti_csi2rx 1c008000.csi2rx":0 [1]'
+ media-ctl -d platform:1c000000.viif -l '"visconti_csi2rx 1c008000.csi2rx":1 -> "visconti-viif:isp":0 [1]'
+ media-ctl -d platform:1c000000.viif -l '"visconti-viif:isp":1 -> "viif_capture_post0":0 [1]'
+ media-ctl -d platform:1c000000.viif -l '"visconti-viif:isp":2 -> "viif_capture_post1":0 [1]'
+ media-ctl -d platform:1c000000.viif -l '"visconti-viif:isp":3 -> "viif_capture_sub":0 [1]'
+ media-ctl -d platform:1c000000.viif -l '"viif_params":0 -> "visconti-viif:isp":4 [1]'
+ media-ctl -d platform:1c000000.viif -l '"visconti-viif:isp":5 -> "viif_stats":0 [1]'
+
+ # set format for imx219
+ media-ctl -d platform:1c000000.viif --set-v4l2 '"imx219 1-0010":0 [fmt:SRGGB10_1X10/1920x1080]'
+
+ # set format for csi2rx
+ media-ctl -d platform:1c000000.viif --set-v4l2 '"visconti_csi2rx 1c008000.csi2rx":0 [fmt:SRGGB10_1X10/1920x1080 field:none colorspace:raw xfer:none ycbcr:601 quantization:full-range]'
+
+ # set format for isp
+ media-ctl -d platform:1c000000.viif --set-v4l2 '"visconti-viif:isp":0 [fmt:SRGGB10_1X10/1920x1080]'
+ media-ctl -d platform:1c000000.viif --set-v4l2 '"visconti-viif:isp":1 [fmt:fmt:YUV8_1X24/1920x1080]'
+ media-ctl -d platform:1c000000.viif --set-v4l2 '"visconti-viif:isp":2 [fmt:fmt:YUV8_1X24/640x480]'
+
+ # set format for main path0
+ v4l2-ctl -z platform:1c000000.viif -d viif_capture_post0 -v "width=1920,height=1080"
+ v4l2-ctl -z platform:1c000000.viif -d viif_capture_post0 -v "pixelformat=RGB3"
+
+ # set format for main path1
+ v4l2-ctl -z platform:1c000000.viif -d viif_capture_post1 -v "width=640,height=480"
+ v4l2-ctl -z platform:1c000000.viif -d viif_capture_post1 -v "pixelformat=UYVY"
+
+ # start streaming
+ v4l2-ctl -z platform:visconti-viif-0 -d viif_capture_post0 --stream-mmap --stream-count 1000 &
+
+ # start streaming with other devices while viif_capture_post0 is running
+ v4l2-ctl -z platform:visconti-viif-0 -d viif_capture_post1 --stream-mmap --stream-count 10
+ v4l2-ctl -z platform:visconti-viif-0 -d viif_capture_sub --stream-mmap --stream-count 10
+
+Debug filesystem
+================
+
+Some status information of CSI2 receiver and VIIF can be obtained via debugfs.
+The driver exposes following files.
+
+- /sys/kernel/debug/<device-name-for-csi2rx>/calibration_status
+ - This file contains some key-value pairs representing status of
+ calibration.
+ - Value can be either of DONE(=0), ERROR(=-11) or DONE(=-5).
+ - Keys are:
+ - term_cal_with_rext: Result of termination calibration with rext
+ - clock_lane_offset_cal: Result of offset calibration of clock lane
+ - data_lane0_offset_cal: Result of offset calibration of data lane0
+ - data_lane1_offset_cal: Result of offset calibration of data lane1
+ - data_lane2_offset_cal: Result of offset calibration of data lane2
+ - data_lane3_offset_cal: Result of offset calibration of data lane3
+ - data_lane0_ddl_tuning_cal:
+ Result of digital delay line tuning calibration of data lane0
+ - data_lane1_ddl_tuning_cal:
+ Result of digital delay line tuning calibration of data lane1
+ - data_lane2_ddl_tuning_cal:
+ Result of digital delay line tuning calibration of data lane2
+ - data_lane3_ddl_tuning_cal:
+ Result of digital delay line tuning calibration of data lane3
+- /sys/kernel/debug/<device-name-for-csi2rx>/err_status
+ - This file contains some key-value pairs representing CSI2 receiver
+ errors.
+ - Each bit of values indicates a specific error status
+ - Keys are:
+ - err_phy_fatal: D-PHY FATAL error.
+ - bit 0-3: Start of transmission error on DATA lane 0-3
+ - err_pkt_fatal: Packet FATAL error.
+ - bit 16: Header ECC contains 2 errors, unrecoverable.
+ - bit 0-3: Checksum error detected on virtual channel 0-3
+ - err_frame_fatal: Frame FATAL error.
+ - bit 16-19:
+ Last received Frame, in virtual channel 0-3, has at least one CRC
+ error.
+ - bit 8-11:
+ Incorrect Frame Sequence detected in virtual channel 0-3.
+ - bit 0-3:
+ Error matching Frame Start with Frame End for virtual channel 0-3.
+ - err_phy: D-PHY error.
+ - bit 16-19: Escape Entry Error on Data Lane 0-3.
+ - bit 0-3:
+ Start of Transmission Error on Data Lane 3 (synchronization can
+ still be achieved).
+ - err_pkt: Packet error.
+ - bit 16-19:
+ Header Error detected and corrected on virtual channel 0-3.
+ - bit 0-3:
+ Unrecognized or unimplemented data type detected in virtual
+ channel 0-3.
+ - err_line: Line error.
+ - bit 16-23: Error in the sequence of lines for vc0-7 and dt0-7.
+ - bit 0-7:
+ Error matching Line Start with Line End for vc0-7 and dt0-7.
+- /sys/kernel/debug/<device-name-for-viif>/reported_err_main
+ - This file contains integer value representing errors occurred in
+ viif_capture_post0 and viif_capture_post1.
+ - Each bit of the value indicates a specific error status
+ - bit 24: VSync generator error
+ - bit 20: L1ISP input size inconsistency
+ - bit 19: L1ISP output size inconsistency
+ - bit 18: L1ISP ABPC table transfer error
+ - bit 17: L1ISP LSSC table transfer error
+ - bit 11: L2ISP grid table transfer error
+ - bit 9: L2ISP POST1 table transfer error
+ - bit 8: L2ISP POST0 table transfer error
+ - bit 0: L2ISP size error
+- /sys/kernel/debug/<device-name-for-viif>/reported_err_sub
+ - This file contains integer value representing errors occurred in
+ viif_capture_sub.
+ - Each bit of the value indicates a specific error status
+ - bit 24: Vsync generator error
+ - bit 0: data transfer error
+
+Use of coherent memory
+======================
+
+Visconti5 SoC has two independent DDR SDRAM controllers. Each controller is
+mapped to 36bit address space.
+
+Accelerator bus masters have two paths to access memory; one is directly
+connected to SDRAM controller, the another is connected via a cache coherency
+bus which keeps coherency among CPUs.
+
+From accelerators and CPUs, the address map is following:
+
+* 0x0_8000_0000 DDR0 direct access
+* 0x4_8000_0000 DDR0 coherency bus
+* 0x8_8000_0000 DDR1 direct access
+* 0xC_8000_0000 DDR1 coherency bus
+
+The base address can be specified with "memory" and "reserved-memory" elements
+in a device tree description. It's not recommended to mix direct address and
+coherent address.
+
+The Visconti5 VIIF driver always use only direct address to configure Video
+DMACs of the hardware. This design is to avoid great performance loss at
+coherency bus caused by massive memory access. You should not put the
+dma_coherent attribute to viif element in device tree. Cache operations are
+done automatically by videobuf2 driver.
diff --git a/Documentation/userspace-api/media/v4l/meta-formats.rst b/Documentation/userspace-api/media/v4l/meta-formats.rst
index 0de80328c36b..b4ff1a7f403d 100644
--- a/Documentation/userspace-api/media/v4l/meta-formats.rst
+++ b/Documentation/userspace-api/media/v4l/meta-formats.rst
@@ -21,6 +21,7 @@ These formats are used for the :ref:`metadata` interface only.
metafmt-rkisp1
metafmt-uvc
metafmt-uvc-msxu-1-5
+ metafmt-visconti-viif
metafmt-vivid
metafmt-vsp1-hgo
metafmt-vsp1-hgt
diff --git a/Documentation/userspace-api/media/v4l/metafmt-visconti-viif.rst b/Documentation/userspace-api/media/v4l/metafmt-visconti-viif.rst
new file mode 100644
index 000000000000..dc4b31627fe1
--- /dev/null
+++ b/Documentation/userspace-api/media/v4l/metafmt-visconti-viif.rst
@@ -0,0 +1,48 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+.. _v4l2-meta-fmt-visconti-viif-params:
+
+.. _v4l2-meta-fmt-visconti-viif-stats:
+
+***************************************************************************************
+V4L2_META_FMT_VISCONTI_VIIF_PARAMS ('vifp'), V4L2_META_FMT_VISCONTI_VIIF_STATS ('vifs')
+***************************************************************************************
+
+Configuration parameters
+========================
+
+The configuration parameters are passed to the
+:ref:`viif_params <viif_params>` metadata output video node, using
+the :c:type:`v4l2_meta_format` interface. The buffer contains
+a single instance of the C structure :c:type:`visconti_viif_isp_config` defined in
+``visconti_viif.h``. So the structure can be obtained from the buffer by:
+
+.. code-block:: c
+
+ struct visconti_viif_isp_config *params = (struct visconti_viif_isp_config*) buffer;
+
+VIIF statistics
+===============
+
+The VIIF device collects different statistics over an input Bayer frame.
+Those statistics are obtained from the :ref:`viif_stats <viif_stats>`
+metadata capture video node,
+using the :c:type:`v4l2_meta_format` interface. The buffer contains a single
+instance of the C structure :c:type:`visconti_viif_isp_stat` defined in
+``visconti_viif.h``. So the structure can be obtained from the buffer by:
+
+.. code-block:: c
+
+ struct visconti_viif_isp_stat *stats = (struct visconti_viif_isp_stat*) buffer;
+
+The statistics collected are Exposure, AWB (auto white balance) and errors.
+See :c:type:`visconti_viif_isp_stat` for details of the statistics.
+
+The statistics and configuration parameters described here are usually
+consumed and produced by dedicated user space libraries that comprise the
+tuning tools using software control loop.
+
+visconti viif uAPI data types
+=============================
+
+.. kernel-doc:: include/uapi/linux/visconti_viif.h
diff --git a/MAINTAINERS b/MAINTAINERS
index cdd04f9a4459..f2475f07059f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -25984,8 +25984,10 @@ M: Nobuhiro Iwamatsu <nobuhiro.iwamatsu.x90@mail.toshiba>
M: Yuji Ishikawa <yuji2.ishikawa@toshiba.co.jp>
L: linux-media@vger.kernel.org
S: Maintained
+F: Documentation/admin-guide/media/visconti-viif.*
F: Documentation/devicetree/bindings/media/toshiba,visconti5-csi2.yaml
F: Documentation/devicetree/bindings/media/toshiba,visconti5-viif.yaml
+F: Documentation/userspace-api/media/v4l/metafmt-visconti-viif.rst
F: drivers/media/platform/toshiba/visconti/
F: include/uapi/linux/visconti_viif.h
--
2.34.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH v13 1/7] dt-bindings: media: platform: visconti: Add Toshiba Visconti MIPI CSI-2 Receiver
2025-10-16 2:24 ` [PATCH v13 1/7] dt-bindings: media: platform: visconti: Add Toshiba Visconti MIPI CSI-2 Receiver Yuji Ishikawa
@ 2025-10-16 4:34 ` Frank Li
2025-10-20 6:11 ` yuji2.ishikawa
2025-10-16 6:38 ` Krzysztof Kozlowski
1 sibling, 1 reply; 15+ messages in thread
From: Frank Li @ 2025-10-16 4:34 UTC (permalink / raw)
To: Yuji Ishikawa
Cc: Nobuhiro Iwamatsu, Mauro Carvalho Chehab, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Philipp Zabel, linux-media,
devicetree, linux-arm-kernel, linux-kernel
On Thu, Oct 16, 2025 at 11:24:38AM +0900, Yuji Ishikawa wrote:
> Adds the Device Tree binding documentation that allows to describe
> the MIPI CSI-2 Receiver found in Toshiba Visconti SoCs.
>
> Signed-off-by: Yuji Ishikawa <yuji2.ishikawa@toshiba.co.jp>
> ---
> Changelog v12:
> - Newly add bindings for CSI2RX driver
>
> Changelog v13:
> - add entries to MAINTAINERS file.
> - update email address of Nobuhiro Iwamatsu in /maintainers
> - add Yuji Ishikawa to /maintainers
> - change /properties/compatible: toshiba,visconti5-csi2rx -> toshiba,visconti5-csi2
> - change bindings file name: toshiba,visconti5-csi2rx -> toshiba,visconti5-csi2
> - change node name in sample DTS: csi2rx -> csi
> - remove "|-" from /description
> - update /description
> - add definitions of clock and reset
> - update /properties/ports/properties/port@0/description for better comment
> - update /properties/ports/properties/port@0/$ref to specify full pathname
> - remove /properties/ports/properties/port@0/properties/endpoint/properties/data-lanes/description because the default text provides enough information.
> - update sample dts
> ---
> .../bindings/media/toshiba,visconti5-csi2.yaml | 125 +++++++++++++++++++++
> MAINTAINERS | 7 ++
> 2 files changed, 132 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/media/toshiba,visconti5-csi2.yaml b/Documentation/devicetree/bindings/media/toshiba,visconti5-csi2.yaml
> new file mode 100644
> index 000000000000..21fb46de5b6e
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/toshiba,visconti5-csi2.yaml
> @@ -0,0 +1,125 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/media/toshiba,visconti5-csi2.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Toshiba Visconti5 SoC MIPI CSI-2 receiver
> +
> +maintainers:
> + - Nobuhiro Iwamatsu <nobuhiro.iwamatsu.x90@mail.toshiba>
> + - Yuji Ishikawa <yuji2.ishikawa@toshiba.co.jp>
> +
> +description:
> + Toshiba Visconti5 SoC MIPI CSI-2 receiver device receives MIPI CSI-2 video
> + stream. The obtained video stream is used as input for the Visconti5 VIIF.
> +
> +properties:
> + compatible:
> + const: toshiba,visconti5-csi2
> +
...
> +
> +examples:
> + - |
> + #include <dt-bindings/clock/toshiba,tmpv770x.h>
> + #include <dt-bindings/interrupt-controller/arm-gic.h>
> + #include <dt-bindings/interrupt-controller/irq.h>
> +
> + soc {
> + #address-cells = <2>;
> + #size-cells = <2>;
> +
> + csi0: csi@1c008000 {
Needn't label csi0
> + compatible = "toshiba,visconti5-csi2";
> + reg = <0 0x1c008000 0 0x400>;
> + interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>;
> + clocks = <&pismu TMPV770X_CLK_VIIFBS0_CFG>,
> + <&pismu TMPV770X_CLK_VIIFBS0_APB>;
> + clock-names = "cfg", "apb";
> + resets = <&pismu TMPV770X_RESET_VIIFBS0_APB>;
> +
> + ports {
> + #address-cells = <1>;
> + #size-cells = <0>;
need empty line here.
> + port@0 {
> + reg = <0>;
empty line here
> + csi0_in: endpoint {
> + data-lanes = <1 2>;
> + remote-endpoint = <&imx219_out>;
> + };
> + };
empty line here
Frank
> + port@1 {
> + reg = <1>;
> + csi0_out: endpoint {
> + remote-endpoint = <&video0_in>;
> + };
> + };
> + };
> + };
> + };
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 46126ce2f968..e4634a0aad74 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -25979,6 +25979,13 @@ F: Documentation/devicetree/bindings/media/i2c/toshiba,tc358743.txt
> F: drivers/media/i2c/tc358743*
> F: include/media/i2c/tc358743.h
>
> +TOSHIBA VISCONTI VIIF DRIVER
> +M: Nobuhiro Iwamatsu <nobuhiro.iwamatsu.x90@mail.toshiba>
> +M: Yuji Ishikawa <yuji2.ishikawa@toshiba.co.jp>
> +L: linux-media@vger.kernel.org
> +S: Maintained
> +F: Documentation/devicetree/bindings/media/toshiba,visconti5-csi2.yaml
> +
> TOSHIBA WMI HOTKEYS DRIVER
> M: Azael Avalos <coproscefalo@gmail.com>
> L: platform-driver-x86@vger.kernel.org
>
> --
> 2.34.1
>
>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v13 4/7] media: platform: visconti: Add Toshiba Visconti CSI-2 Receiver driver
2025-10-16 2:24 ` [PATCH v13 4/7] media: platform: visconti: Add Toshiba Visconti CSI-2 Receiver driver Yuji Ishikawa
@ 2025-10-16 4:45 ` Frank Li
2025-10-20 6:13 ` yuji2.ishikawa
0 siblings, 1 reply; 15+ messages in thread
From: Frank Li @ 2025-10-16 4:45 UTC (permalink / raw)
To: Yuji Ishikawa
Cc: Nobuhiro Iwamatsu, Mauro Carvalho Chehab, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Philipp Zabel, linux-media,
devicetree, linux-arm-kernel, linux-kernel
On Thu, Oct 16, 2025 at 11:24:41AM +0900, Yuji Ishikawa wrote:
> Add support to MIPI CSI-2 Receiver on Toshiba Visconti ARM SoCs.
> This driver is used with Visconti Video Input Interface driver.
>
> Signed-off-by: Yuji Ishikawa <yuji2.ishikawa@toshiba.co.jp>
> ---
> Changelog v12:
> - Separate CSI2RX driver and made it independent driver
> - viif_csi2rx subdevice driver (in v11 patch) was removed.
> - dictionary order at Kconfig and Makefile
>
> Changelog v13:
> - wrap one line at 80 characters
> - change banner comment style
> - update comment style; spacing at the start and end, capitalize first letter
> - add support for clock and reset framework
> - add debugfs to pass debug and status information
> - add entries to MAINTAINERS file
> - Kconfig: add a blank line just after License Identifier.
> - update references to header files
> - remove redundant inline qualifier
> - shorten function/variable names: visconti_csi2rx -> viscsi2
> - simplify dphy_write and dphy read operations
> - remove osc_freq_target from struct csi2rx_dphy_hs_info, which is always the same value.
> - add comment about MASK register's behavior (reversed polarity)
> - use v4l2_get_link_freq() instead of get_pixelclock()
> - set driver name according to module name: visconti_csi2rx_dev -> visconti-csi2rx
> - check error before setting priv->irq in probe()
> - check error at fmt_for_mbus_code()
> - add callback for ioctl(VIDIOC_ENUM_FRAMESIZES)
> - improve viscsi2_parse_dt() by assuming bus_type is CSI2_DPHY
> - use dev_err_ratelimited() for irq handler
> - bugfix on fmt_for_mbus_code(): in case unsupported mbus_code is given
> - add goto based error handling sequence to viscsi2_parse_dt()
> - specify default value of colorspace, ycbcr_enc, quantization and xfer_func of sink/src_fmt
> - specify sensor at enable_streams() using previously set ID, instead of checking remote pad every time
> - remove U suffix on numeric value
> - use unsigned int variable for loop index
> - remove redundant casting
> - use GENMASK instead of literal
> - remove unused constants
> - remove unused visconti_csi2rx_video_ops
> ---
> MAINTAINERS | 1 +
> drivers/media/platform/Kconfig | 1 +
> drivers/media/platform/Makefile | 1 +
> drivers/media/platform/toshiba/Kconfig | 6 +
> drivers/media/platform/toshiba/Makefile | 3 +
> drivers/media/platform/toshiba/visconti/Kconfig | 17 +
> drivers/media/platform/toshiba/visconti/Makefile | 8 +
> .../media/platform/toshiba/visconti/csi2rx_drv.c | 954 +++++++++++++++++++++
> 8 files changed, 991 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index c17c7ddba5af..ce973791b367 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -25986,6 +25986,7 @@ L: linux-media@vger.kernel.org
> S: Maintained
> F: Documentation/devicetree/bindings/media/toshiba,visconti5-csi2.yaml
> F: Documentation/devicetree/bindings/media/toshiba,visconti5-viif.yaml
> +F: drivers/media/platform/toshiba/visconti/
>
> TOSHIBA WMI HOTKEYS DRIVER
> M: Azael Avalos <coproscefalo@gmail.com>
> diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
> index 9287faafdce5..d5265aa16c88 100644
> --- a/drivers/media/platform/Kconfig
> +++ b/drivers/media/platform/Kconfig
> @@ -87,6 +87,7 @@ source "drivers/media/platform/st/Kconfig"
> source "drivers/media/platform/sunxi/Kconfig"
> source "drivers/media/platform/synopsys/Kconfig"
> source "drivers/media/platform/ti/Kconfig"
> +source "drivers/media/platform/toshiba/Kconfig"
> source "drivers/media/platform/verisilicon/Kconfig"
> source "drivers/media/platform/via/Kconfig"
> source "drivers/media/platform/xilinx/Kconfig"
> diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
> index 6fd7db0541c7..09e67ecb9559 100644
> --- a/drivers/media/platform/Makefile
> +++ b/drivers/media/platform/Makefile
> @@ -30,6 +30,7 @@ obj-y += st/
> obj-y += sunxi/
> obj-y += synopsys/
> obj-y += ti/
> +obj-y += toshiba/
> obj-y += verisilicon/
> obj-y += via/
> obj-y += xilinx/
> diff --git a/drivers/media/platform/toshiba/Kconfig b/drivers/media/platform/toshiba/Kconfig
> new file mode 100644
> index 000000000000..f02983f4fc97
> --- /dev/null
> +++ b/drivers/media/platform/toshiba/Kconfig
> @@ -0,0 +1,6 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +
> +comment "Toshiba media platform drivers"
> +
> +source "drivers/media/platform/toshiba/visconti/Kconfig"
> +
> diff --git a/drivers/media/platform/toshiba/Makefile b/drivers/media/platform/toshiba/Makefile
> new file mode 100644
> index 000000000000..dd89a9a35704
> --- /dev/null
> +++ b/drivers/media/platform/toshiba/Makefile
> @@ -0,0 +1,3 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +
> +obj-y += visconti/
> diff --git a/drivers/media/platform/toshiba/visconti/Kconfig b/drivers/media/platform/toshiba/visconti/Kconfig
> new file mode 100644
> index 000000000000..aa0b63f9f008
> --- /dev/null
> +++ b/drivers/media/platform/toshiba/visconti/Kconfig
> @@ -0,0 +1,17 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +
> +config VIDEO_VISCONTI_CSI2RX
> + tristate "Visconti MIPI CSI-2 Receiver driver"
> + depends on V4L_PLATFORM_DRIVERS
> + depends on VIDEO_DEV && OF
> + depends on ARCH_VISCONTI || COMPILE_TEST
> + select MEDIA_CONTROLLER
> + select VIDEO_V4L2_SUBDEV_API
> + select V4L2_FWNODE
> + help
> + Support for Toshiba Visconti MIPI CSI-2 receiver,
> + which is used with Visconti Camera Interface driver.
> +
> + This driver yields 1 subdevice node for a hardware instance.
> + To compile this driver as a module, choose M here: the
> + module will be called visconti-csi2rx.
> diff --git a/drivers/media/platform/toshiba/visconti/Makefile b/drivers/media/platform/toshiba/visconti/Makefile
> new file mode 100644
> index 000000000000..62a029376134
> --- /dev/null
> +++ b/drivers/media/platform/toshiba/visconti/Makefile
> @@ -0,0 +1,8 @@
> +# SPDX-License-Identifier: GPL-2.0
> +#
> +# Makefile for the Visconti video input device driver
> +#
> +
> +visconti-csi2rx-objs = csi2rx_drv.o
> +
> +obj-$(CONFIG_VIDEO_VISCONTI_CSI2RX) += visconti-csi2rx.o
> diff --git a/drivers/media/platform/toshiba/visconti/csi2rx_drv.c b/drivers/media/platform/toshiba/visconti/csi2rx_drv.c
> new file mode 100644
> index 000000000000..53d112432a86
> --- /dev/null
> +++ b/drivers/media/platform/toshiba/visconti/csi2rx_drv.c
> @@ -0,0 +1,954 @@
> +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
> +/*
> + * Toshiba Visconti Video Capture Support
> + *
> + * (C) Copyright 2025 TOSHIBA CORPORATION
> + * (C) Copyright 2025 Toshiba Electronic Devices & Storage Corporation
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/debugfs.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/property.h>
> +#include <linux/reset.h>
> +#include <linux/seq_file.h>
> +
> +#include <media/mipi-csi2.h>
> +#include <media/v4l2-common.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-fwnode.h>
> +#include <media/v4l2-subdev.h>
> +
> +/* CSI2HOST register space */
> +#define REG_CSI2RX_NLANES 0x4
> +#define REG_CSI2RX_RESETN 0x8
> +#define REG_CSI2RX_INT_ST_MAIN 0xc
> +#define REG_CSI2RX_DATA_IDS_1 0x10
> +#define REG_CSI2RX_DATA_IDS_2 0x14
> +#define REG_CSI2RX_PHY_SHUTDOWNZ 0x40
> +#define REG_CSI2RX_PHY_RSTZ 0x44
> +
> +/* Access to dphy external registers */
> +#define REG_CSI2RX_PHY_TESTCTRL0 0x50
> +#define BIT_TESTCTRL0_CLK_0 0
> +#define BIT_TESTCTRL0_CLK_1 BIT(1)
> +
> +#define REG_CSI2RX_PHY_TESTCTRL1 0x54
> +#define BIT_TESTCTRL1_ADDR BIT(16)
> +#define MASK_TESTCTRL1_DOUT GENMASK(15, 8)
> +
> +#define REG_CSI2RX_INT_ST_PHY_FATAL 0xe0
> +#define REG_CSI2RX_INT_MSK_PHY_FATAL 0xe4
> +#define MASK_PHY_FATAL_ALL 0x0000000f
> +
> +#define REG_CSI2RX_INT_ST_PKT_FATAL 0xf0
> +#define REG_CSI2RX_INT_MSK_PKT_FATAL 0xf4
> +#define MASK_PKT_FATAL_ALL 0x0001000f
> +
> +#define REG_CSI2RX_INT_ST_FRAME_FATAL 0x100
> +#define REG_CSI2RX_INT_MSK_FRAME_FATAL 0x104
> +#define MASK_FRAME_FATAL_ALL 0x000f0f0f
> +
> +#define REG_CSI2RX_INT_ST_PHY 0x110
> +#define REG_CSI2RX_INT_MSK_PHY 0x114
> +#define MASK_PHY_ERROR_ALL 0x000f000f
> +
> +#define REG_CSI2RX_INT_ST_PKT 0x120
> +#define REG_CSI2RX_INT_MSK_PKT 0x124
> +#define MASK_PKT_ERROR_ALL 0x000f000f
> +
> +#define REG_CSI2RX_INT_ST_LINE 0x130
> +#define REG_CSI2RX_INT_MSK_LINE 0x134
> +#define MASK_LINE_ERROR_ALL 0x00ff00ff
Look like it is dwc CSI2RX controller. Can we work out a common dwc
CSI2RX driver to avoid every duplicate the same code
A attempt at
https://lore.kernel.org/imx/20250821-95_cam-v3-20-c9286fbb34b9@nxp.com/
The above is the base on stage's imx6. we try to find a path to workout
a common dwc csi2rx.
Frank
> +
> +/* DPHY register space */
> +enum dphy_testcode {
> + DIG_TESTCODE_EXT = 0,
> + DIG_SYS_0 = 0x001,
> + DIG_SYS_1 = 0x002,
> + DIG_SYS_7 = 0x008,
> + DIG_RX_STARTUP_OVR_2 = 0x0e2,
> + DIG_RX_STARTUP_OVR_3 = 0x0e3,
> + DIG_RX_STARTUP_OVR_4 = 0x0e4,
> + DIG_RX_STARTUP_OVR_5 = 0x0e5,
> + DIG_CB_2 = 0x1ac,
> + DIG_TERM_CAL_0 = 0x220,
> + DIG_TERM_CAL_1 = 0x221,
> + DIG_TERM_CAL_2 = 0x222,
> + DIG_CLKLANE_LANE_6 = 0x307,
> + DIG_CLKLANE_OFFSET_CAL_0 = 0x39d,
> + DIG_LANE0_OFFSET_CAL_0 = 0x59f,
> + DIG_LANE0_DDL_0 = 0x5e0,
> + DIG_LANE1_OFFSET_CAL_0 = 0x79f,
> + DIG_LANE1_DDL_0 = 0x7e0,
> + DIG_LANE2_OFFSET_CAL_0 = 0x99f,
> + DIG_LANE2_DDL_0 = 0x9e0,
> + DIG_LANE3_OFFSET_CAL_0 = 0xb9f,
> + DIG_LANE3_DDL_0 = 0xbe0,
> +};
> +
> +#define SYS_0_HSFREQRANGE_OVR BIT(5)
> +#define SYS_7_RESERVED FIELD_PREP(0x1f, 0x0c)
> +#define SYS_7_DESKEW_POL BIT(5)
> +#define STARTUP_OVR_4_CNTVAL FIELD_PREP(0x70, 0x01)
> +#define STARTUP_OVR_4_DDL_EN BIT(0)
> +#define STARTUP_OVR_5_BYPASS BIT(0)
> +#define CB_2_LPRX_BIAS BIT(6)
> +#define CB_2_RESERVED FIELD_PREP(0x3f, 0x0b)
> +#define CLKLANE_RXHS_PULL_LONG BIT(7)
> +
> +/* bit mask for calibration result registers */
> +#define MASK_TERM_CAL_ERR 0
> +#define MASK_TERM_CAL_DONE BIT(7)
> +#define MASK_CLK_CAL_ERR BIT(4)
> +#define MASK_CLK_CAL_DONE BIT(0)
> +#define MASK_CAL_ERR BIT(2)
> +#define MASK_CAL_DONE BIT(1)
> +#define MASK_DDL_ERR BIT(1)
> +#define MASK_DDL_DONE BIT(2)
> +
> +#define VISCSI2_ERROR_MONITORS_NUM 8
> +
> +/**
> + * struct viscsi2_line_err_target
> + *
> + * Virtual Channel and Data Type pair for CSI2RX line error monitor
> + *
> + * When 0 is set to dt, line error detection is disabled.
> + *
> + * @vc: Virtual Channel to monitor; Range 0..3
> + * @dt: Data Type to monitor; Range 0, 0x10..0x3f
> + */
> +struct viscsi2_line_err_target {
> + u32 vc[VISCSI2_ERROR_MONITORS_NUM];
> + u32 dt[VISCSI2_ERROR_MONITORS_NUM];
> +};
> +
> +#define CSI2RX_MIN_DATA_RATE 80
> +#define CSI2RX_MAX_DATA_RATE 1500
> +
> +#define VISCSI2_PAD_SINK 0
> +#define VISCSI2_PAD_SRC 1
> +#define VISCSI2_PAD_NUM 2
> +
> +#define VISCSI2_DEF_WIDTH 1920
> +#define VISCSI2_DEF_HEIGHT 1080
> +#define VISCSI2_MIN_WIDTH 640
> +#define VISCSI2_MAX_WIDTH 3840
> +#define VISCSI2_MIN_HEIGHT 480
> +#define VISCSI2_MAX_HEIGHT 2160
> +
> +struct viscsi2 {
> + struct device *dev;
> + void __iomem *base;
> +
> + struct v4l2_subdev subdev;
> + struct media_pad pads[VISCSI2_PAD_NUM];
> + struct v4l2_async_notifier notifier;
> + struct v4l2_subdev *remote;
> + unsigned int remote_pad;
> +
> + unsigned int lanes;
> +
> + unsigned int irq;
> + struct clk *clk_apb;
> + struct clk *clk_cfg;
> + struct reset_control *rst;
> +
> +#ifdef CONFIG_DEBUG_FS
> + struct dentry *debugfs_dir;
> + u32 debug_phy_fatal;
> + u32 debug_pkt_fatal;
> + u32 debug_frame_fatal;
> + u32 debug_phy;
> + u32 debug_pkt;
> + u32 debug_line;
> +#endif
> + bool running;
> +};
> +
> +static inline struct viscsi2 *notifier_to_csi2(struct v4l2_async_notifier *n)
> +{
> + return container_of(n, struct viscsi2, notifier);
> +}
> +
> +static inline struct viscsi2 *sd_to_csi2(struct v4l2_subdev *sd)
> +{
> + return container_of(sd, struct viscsi2, subdev);
> +}
> +
> +static inline void viscsi2_write(struct viscsi2 *priv, u32 regid, u32 val)
> +{
> + writel(val, priv->base + regid);
> +}
> +
> +static inline u32 viscsi2_read(struct viscsi2 *priv, u32 regid)
> +{
> + return readl(priv->base + regid);
> +}
> +
> +static void viscsi2_set_dphy_param(struct viscsi2 *priv, u32 val)
> +{
> + viscsi2_write(priv, REG_CSI2RX_PHY_TESTCTRL1, val);
> + viscsi2_write(priv, REG_CSI2RX_PHY_TESTCTRL0, BIT_TESTCTRL0_CLK_1);
> + viscsi2_write(priv, REG_CSI2RX_PHY_TESTCTRL0, BIT_TESTCTRL0_CLK_0);
> +}
> +
> +static void viscsi2_set_dphy_addr(struct viscsi2 *priv, u32 test_mode)
> +{
> + /* Select testcode Ex space with top 4bits of test_mode */
> + viscsi2_set_dphy_param(priv, BIT_TESTCTRL1_ADDR | DIG_TESTCODE_EXT);
> + viscsi2_set_dphy_param(priv, FIELD_GET(0xf00, test_mode));
> + viscsi2_set_dphy_param(priv,
> + BIT_TESTCTRL1_ADDR | FIELD_GET(0xff, test_mode));
> +}
> +
> +static void dphy_write(struct viscsi2 *priv, u32 test_mode, u8 test_in)
> +{
> + viscsi2_set_dphy_addr(priv, test_mode);
> + viscsi2_set_dphy_param(priv, test_in);
> +}
> +
> +#ifdef CONFIG_DEBUG_FS
> +static u8 dphy_read(struct viscsi2 *priv, u32 test_mode)
> +{
> + u32 read_data;
> +
> + viscsi2_set_dphy_addr(priv, test_mode);
> + read_data = viscsi2_read(priv, REG_CSI2RX_PHY_TESTCTRL1);
> + return FIELD_GET(MASK_TESTCTRL1_DOUT, read_data);
> +}
> +
> +static int viscsi2_read_calibration_status(struct viscsi2 *priv, u32 test_mode,
> + u32 mask_err, u32 mask_done)
> +{
> + u32 read_data = (u32)dphy_read(priv, test_mode);
> +
> + if (!(read_data & mask_done))
> + return -EAGAIN;
> +
> + if (read_data & mask_err)
> + return -EIO;
> +
> + return 0;
> +}
> +
> +struct viscsi2_calibration_status_def {
> + const char *name;
> + u32 test_mode;
> + u32 mask_err;
> + u32 mask_done;
> +};
> +
> +static const struct viscsi2_calibration_status_def viscsi2_caldef[] = {
> + { "term_cal_with_rext", DIG_TERM_CAL_1, MASK_TERM_CAL_ERR,
> + MASK_TERM_CAL_DONE },
> + { "clock_lane_offset_cal", DIG_CLKLANE_OFFSET_CAL_0, MASK_CLK_CAL_ERR,
> + MASK_CLK_CAL_DONE },
> + { "data_lane0_offset_cal", DIG_LANE0_OFFSET_CAL_0, MASK_CAL_ERR,
> + MASK_CAL_DONE },
> + { "data_lane1_offset_cal", DIG_LANE1_OFFSET_CAL_0, MASK_CAL_ERR,
> + MASK_CAL_DONE },
> + { "data_lane2_offset_cal", DIG_LANE2_OFFSET_CAL_0, MASK_CAL_ERR,
> + MASK_CAL_DONE },
> + { "data_lane3_offset_cal", DIG_LANE3_OFFSET_CAL_0, MASK_CAL_ERR,
> + MASK_CAL_DONE },
> + { "data_lane0_ddl_tuning_cal", DIG_LANE0_DDL_0, MASK_DDL_ERR,
> + MASK_DDL_DONE },
> + { "data_lane1_ddl_tuning_cal", DIG_LANE1_DDL_0, MASK_DDL_ERR,
> + MASK_DDL_DONE },
> + { "data_lane2_ddl_tuning_cal", DIG_LANE2_DDL_0, MASK_DDL_ERR,
> + MASK_DDL_DONE },
> + { "data_lane3_ddl_tuning_cal", DIG_LANE3_DDL_0, MASK_DDL_ERR,
> + MASK_DDL_DONE },
> +};
> +
> +static int viscsi2_debug_calibration_status_show(struct seq_file *m, void *p)
> +{
> + struct viscsi2 *priv = m->private;
> + unsigned int i;
> +
> + if (!priv->running)
> + return 0;
> +
> + for (i = 0; i < ARRAY_SIZE(viscsi2_caldef); i++) {
> + const struct viscsi2_calibration_status_def *cd =
> + &viscsi2_caldef[i];
> +
> + seq_printf(m, "%s: %d\n", cd->name,
> + viscsi2_read_calibration_status(priv, cd->test_mode,
> + cd->mask_err,
> + cd->mask_done));
> + }
> +
> + return 0;
> +}
> +DEFINE_SHOW_ATTRIBUTE(viscsi2_debug_calibration_status);
> +
> +static int viscsi2_debug_err_status_show(struct seq_file *m, void *p)
> +{
> + struct viscsi2 *priv = m->private;
> +
> + seq_printf(m, "err_phy_fatal: 0x%08x\n", priv->debug_phy_fatal);
> + seq_printf(m, "err_pkt_fatal: 0x%08x\n", priv->debug_pkt_fatal);
> + seq_printf(m, "err_frame_fatal: 0x%08x\n", priv->debug_frame_fatal);
> + seq_printf(m, "err_phy: 0x%08x\n", priv->debug_phy);
> + seq_printf(m, "err_pkt: 0x%08x\n", priv->debug_pkt);
> + seq_printf(m, "err_line: 0x%08x\n", priv->debug_line);
> +
> + return 0;
> +}
> +DEFINE_SHOW_ATTRIBUTE(viscsi2_debug_err_status);
> +
> +static void viscsi2_debug_init(struct viscsi2 *csi2)
> +{
> + csi2->debugfs_dir = debugfs_create_dir(dev_name(csi2->dev), NULL);
> + if (IS_ERR(csi2->debugfs_dir))
> + return;
> + debugfs_create_file("calibration_status", 0444, csi2->debugfs_dir, csi2,
> + &viscsi2_debug_calibration_status_fops);
> + debugfs_create_file("err_status", 0444, csi2->debugfs_dir, csi2,
> + &viscsi2_debug_err_status_fops);
> +}
> +
> +static void viscsi2_debug_cleanup(struct viscsi2 *csi2)
> +{
> + debugfs_remove_recursive(csi2->debugfs_dir);
> +}
> +#endif
> +
> +#define OSC_FREQ_TARGET 0x1cc
> +
> +struct viscsi2_dphy_hs_info {
> + u32 rate;
> + u32 hsfreqrange;
> +};
> +
> +static const struct viscsi2_dphy_hs_info dphy_hs_info[] = {
> + { 80, 0x0 }, { 85, 0x10 }, { 95, 0x20 }, { 105, 0x30 },
> + { 115, 0x1 }, { 125, 0x11 }, { 135, 0x21 }, { 145, 0x31 },
> + { 155, 0x2 }, { 165, 0x12 }, { 175, 0x22 }, { 185, 0x32 },
> + { 198, 0x3 }, { 213, 0x13 }, { 228, 0x23 }, { 243, 0x33 },
> + { 263, 0x4 }, { 288, 0x14 }, { 313, 0x25 }, { 338, 0x35 },
> + { 375, 0x5 }, { 425, 0x16 }, { 475, 0x26 }, { 525, 0x37 },
> + { 575, 0x7 }, { 625, 0x18 }, { 675, 0x28 }, { 725, 0x39 },
> + { 775, 0x9 }, { 825, 0x19 }, { 875, 0x29 }, { 925, 0x3a },
> + { 975, 0xa }, { 1025, 0x1a }, { 1075, 0x2a }, { 1125, 0x3b },
> + { 1175, 0xb }, { 1225, 0x1b }, { 1275, 0x2b }, { 1325, 0x3c },
> + { 1375, 0xc }, { 1425, 0x1c }, { 1475, 0x2c }
> +};
> +
> +static void get_dphy_hs_transfer_info(u32 dphy_rate, u32 *hsfreqrange)
> +{
> + unsigned int i;
> +
> + for (i = 1; i < ARRAY_SIZE(dphy_hs_info); i++) {
> + if (dphy_rate < dphy_hs_info[i].rate) {
> + *hsfreqrange = dphy_hs_info[i - 1].hsfreqrange;
> + return;
> + }
> + }
> +
> + /* Not found; return the largest entry */
> + *hsfreqrange = dphy_hs_info[ARRAY_SIZE(dphy_hs_info) - 1].hsfreqrange;
> +}
> +
> +static void viscsi2_set_dphy_rate(struct viscsi2 *priv, u32 dphy_rate)
> +{
> + u32 hsfreqrange;
> +
> + get_dphy_hs_transfer_info(dphy_rate, &hsfreqrange);
> +
> + dphy_write(priv, DIG_SYS_1, hsfreqrange);
> + dphy_write(priv, DIG_SYS_0, SYS_0_HSFREQRANGE_OVR);
> + dphy_write(priv, DIG_RX_STARTUP_OVR_5, STARTUP_OVR_5_BYPASS);
> + dphy_write(priv, DIG_RX_STARTUP_OVR_4, STARTUP_OVR_4_CNTVAL);
> + dphy_write(priv, DIG_CB_2, CB_2_LPRX_BIAS | CB_2_RESERVED);
> + dphy_write(priv, DIG_SYS_7, SYS_7_DESKEW_POL | SYS_7_RESERVED);
> + dphy_write(priv, DIG_CLKLANE_LANE_6, CLKLANE_RXHS_PULL_LONG);
> + dphy_write(priv, DIG_RX_STARTUP_OVR_2,
> + FIELD_GET(0xff, OSC_FREQ_TARGET));
> + dphy_write(priv, DIG_RX_STARTUP_OVR_3,
> + FIELD_GET(0xf00, OSC_FREQ_TARGET));
> + dphy_write(priv, DIG_RX_STARTUP_OVR_4,
> + STARTUP_OVR_4_CNTVAL | STARTUP_OVR_4_DDL_EN);
> +}
> +
> +static int viscsi2_initialize(struct viscsi2 *priv, u32 num_lane, u32 dphy_rate,
> + const struct viscsi2_line_err_target *err_target)
> +{
> + u32 val;
> +
> + if (dphy_rate < CSI2RX_MIN_DATA_RATE ||
> + dphy_rate > CSI2RX_MAX_DATA_RATE) {
> + dev_err(priv->dev, "Unsupported PHY speed (%u Mbps)",
> + dphy_rate);
> + return -ERANGE;
> + }
> +
> + /* 1st phase of initialization */
> + viscsi2_write(priv, REG_CSI2RX_RESETN, 1);
> + viscsi2_write(priv, REG_CSI2RX_PHY_RSTZ, 0);
> + viscsi2_write(priv, REG_CSI2RX_PHY_SHUTDOWNZ, 0);
> + viscsi2_write(priv, REG_CSI2RX_PHY_TESTCTRL0, 1);
> + ndelay(15);
> + viscsi2_write(priv, REG_CSI2RX_PHY_TESTCTRL0, 0);
> +
> + /* Configure D-PHY frequency range */
> + viscsi2_set_dphy_rate(priv, dphy_rate);
> +
> + /* 2nd phase of initialization */
> + viscsi2_write(priv, REG_CSI2RX_NLANES, num_lane - 1);
> + ndelay(5);
> +
> + /* Release D-PHY from Reset */
> + viscsi2_write(priv, REG_CSI2RX_PHY_SHUTDOWNZ, 1);
> + ndelay(5);
> + viscsi2_write(priv, REG_CSI2RX_PHY_RSTZ, 1);
> +
> + /* Configuration of line error target */
> + val = (err_target->vc[3] << 30) | (err_target->dt[3] << 24) |
> + (err_target->vc[2] << 22) | (err_target->dt[2] << 16) |
> + (err_target->vc[1] << 14) | (err_target->dt[1] << 8) |
> + (err_target->vc[0] << 6) | (err_target->dt[0]);
> + viscsi2_write(priv, REG_CSI2RX_DATA_IDS_1, val);
> + val = (err_target->vc[7] << 30) | (err_target->dt[7] << 24) |
> + (err_target->vc[6] << 22) | (err_target->dt[6] << 16) |
> + (err_target->vc[5] << 14) | (err_target->dt[5] << 8) |
> + (err_target->vc[4] << 6) | (err_target->dt[4]);
> + viscsi2_write(priv, REG_CSI2RX_DATA_IDS_2, val);
> +
> + /* Configuration of mask */
> + viscsi2_write(priv, REG_CSI2RX_INT_MSK_PHY_FATAL, MASK_PHY_FATAL_ALL);
> + viscsi2_write(priv, REG_CSI2RX_INT_MSK_PKT_FATAL, MASK_PKT_FATAL_ALL);
> + viscsi2_write(priv, REG_CSI2RX_INT_MSK_FRAME_FATAL,
> + MASK_FRAME_FATAL_ALL);
> + viscsi2_write(priv, REG_CSI2RX_INT_MSK_PHY, MASK_PHY_ERROR_ALL);
> + viscsi2_write(priv, REG_CSI2RX_INT_MSK_PKT, MASK_PKT_ERROR_ALL);
> + viscsi2_write(priv, REG_CSI2RX_INT_MSK_LINE, MASK_LINE_ERROR_ALL);
> +
> + return 0;
> +}
> +
> +struct viscsi2_format {
> + u32 code;
> + unsigned int bpp;
> +};
> +
> +static const struct viscsi2_format viscsi2_formats[] = {
> + { .code = MEDIA_BUS_FMT_RGB888_1X24, .bpp = 24 },
> + { .code = MEDIA_BUS_FMT_UYVY8_1X16, .bpp = 16 },
> + { .code = MEDIA_BUS_FMT_UYVY10_1X20, .bpp = 20 },
> + { .code = MEDIA_BUS_FMT_RGB565_1X16, .bpp = 16 },
> + { .code = MEDIA_BUS_FMT_SBGGR8_1X8, .bpp = 8 },
> + { .code = MEDIA_BUS_FMT_SGBRG8_1X8, .bpp = 8 },
> + { .code = MEDIA_BUS_FMT_SGRBG8_1X8, .bpp = 8 },
> + { .code = MEDIA_BUS_FMT_SRGGB8_1X8, .bpp = 8 },
> + { .code = MEDIA_BUS_FMT_SBGGR10_1X10, .bpp = 10 },
> + { .code = MEDIA_BUS_FMT_SGBRG10_1X10, .bpp = 10 },
> + { .code = MEDIA_BUS_FMT_SGRBG10_1X10, .bpp = 10 },
> + { .code = MEDIA_BUS_FMT_SRGGB10_1X10, .bpp = 10 },
> + { .code = MEDIA_BUS_FMT_SBGGR12_1X12, .bpp = 12 },
> + { .code = MEDIA_BUS_FMT_SGBRG12_1X12, .bpp = 12 },
> + { .code = MEDIA_BUS_FMT_SGRBG12_1X12, .bpp = 12 },
> + { .code = MEDIA_BUS_FMT_SRGGB12_1X12, .bpp = 12 },
> + { .code = MEDIA_BUS_FMT_SBGGR14_1X14, .bpp = 14 },
> + { .code = MEDIA_BUS_FMT_SGBRG14_1X14, .bpp = 14 },
> + { .code = MEDIA_BUS_FMT_SGRBG14_1X14, .bpp = 14 },
> + { .code = MEDIA_BUS_FMT_SRGGB14_1X14, .bpp = 14 },
> +};
> +
> +static const struct viscsi2_format *fmt_for_mbus_code(unsigned int mbus_code)
> +{
> + unsigned int i;
> +
> + for (i = 0; ARRAY_SIZE(viscsi2_formats); i++) {
> + if (viscsi2_formats[i].code == mbus_code)
> + return &viscsi2_formats[i];
> + }
> + return NULL;
> +}
> +
> +static const struct viscsi2_line_err_target err_target_vc0_alldt = {
> + /* Select VC=0 */
> + /* Select all supported DataTypes */
> + .dt = {
> + MIPI_CSI2_DT_RGB565,
> + MIPI_CSI2_DT_YUV422_8B,
> + MIPI_CSI2_DT_YUV422_10B,
> + MIPI_CSI2_DT_RGB888,
> + MIPI_CSI2_DT_RAW8,
> + MIPI_CSI2_DT_RAW10,
> + MIPI_CSI2_DT_RAW12,
> + MIPI_CSI2_DT_RAW14,
> + }
> +};
> +
> +static int viscsi2_start(struct viscsi2 *priv, struct v4l2_subdev_state *state)
> +{
> + const struct v4l2_mbus_framefmt *sink_fmt;
> + const struct viscsi2_format *cur_fmt;
> + struct media_pad *src_pad;
> + int cur_bpp, dphy_rate;
> + s64 link_freq;
> + int ret = 0;
> +
> + /* Get bpp for current format */
> + sink_fmt = v4l2_subdev_state_get_format(state, VISCSI2_PAD_SINK);
> + cur_fmt = fmt_for_mbus_code(sink_fmt->code);
> + if (!cur_fmt)
> + return -EINVAL;
> + cur_bpp = cur_fmt->bpp;
> +
> + /* Get DPHY rate [unit: Mbps]; note that the signal is DDR */
> + src_pad = &priv->remote->entity.pads[priv->remote_pad];
> + link_freq = v4l2_get_link_freq(src_pad, cur_bpp, 2 * priv->lanes);
> + if (link_freq < 0)
> + return link_freq;
> + dphy_rate = div_s64(link_freq, 500000);
> +
> + clk_prepare_enable(priv->clk_apb);
> + clk_prepare_enable(priv->clk_cfg);
> + ndelay(15);
> + reset_control_deassert(priv->rst);
> +
> + ret = viscsi2_initialize(priv, priv->lanes, dphy_rate,
> + &err_target_vc0_alldt);
> +
> + if (ret) {
> + reset_control_assert(priv->rst);
> + clk_disable_unprepare(priv->clk_cfg);
> + clk_disable_unprepare(priv->clk_apb);
> + return ret;
> + }
> +
> + priv->running = true;
> + return 0;
> +}
> +
> +static void viscsi2_stop(struct viscsi2 *priv)
> +{
> + priv->running = false;
> +
> + /* Disable interrupt by clearing bits of MSK registers */
> + viscsi2_write(priv, REG_CSI2RX_INT_MSK_PHY_FATAL, 0);
> + viscsi2_write(priv, REG_CSI2RX_INT_MSK_PKT_FATAL, 0);
> + viscsi2_write(priv, REG_CSI2RX_INT_MSK_FRAME_FATAL, 0);
> + viscsi2_write(priv, REG_CSI2RX_INT_MSK_PHY, 0);
> + viscsi2_write(priv, REG_CSI2RX_INT_MSK_PKT, 0);
> + viscsi2_write(priv, REG_CSI2RX_INT_MSK_LINE, 0);
> + /* Make sure registers cleared */
> + viscsi2_read(priv, REG_CSI2RX_INT_MSK_PHY_FATAL);
> + viscsi2_read(priv, REG_CSI2RX_INT_MSK_PKT_FATAL);
> + viscsi2_read(priv, REG_CSI2RX_INT_MSK_FRAME_FATAL);
> + viscsi2_read(priv, REG_CSI2RX_INT_MSK_PHY);
> + viscsi2_read(priv, REG_CSI2RX_INT_MSK_PKT);
> + viscsi2_read(priv, REG_CSI2RX_INT_MSK_LINE);
> + /* Wait for current handlers finish */
> + synchronize_irq(priv->irq);
> +
> + /* Shutdown hardware */
> + viscsi2_write(priv, REG_CSI2RX_PHY_SHUTDOWNZ, 0);
> + viscsi2_write(priv, REG_CSI2RX_PHY_RSTZ, 0);
> + viscsi2_write(priv, REG_CSI2RX_PHY_TESTCTRL0, 1);
> + viscsi2_write(priv, REG_CSI2RX_RESETN, 0);
> +
> + reset_control_assert(priv->rst);
> + clk_disable_unprepare(priv->clk_cfg);
> + clk_disable_unprepare(priv->clk_apb);
> +}
> +
> +static int viscsi2_enable_streams(struct v4l2_subdev *sd,
> + struct v4l2_subdev_state *state, u32 pad,
> + u64 streams_mask)
> +{
> + struct viscsi2 *priv = sd_to_csi2(sd);
> + int ret;
> +
> + /* Enabling: turn on CSI2RX -> turn on sensor */
> + ret = viscsi2_start(priv, state);
> + if (ret)
> + return ret;
> +
> + /* Currently CSI2RX supports only stream0 in source pad */
> + ret = v4l2_subdev_enable_streams(priv->remote, priv->remote_pad,
> + BIT(0));
> + if (ret) {
> + viscsi2_stop(priv);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int viscsi2_disable_streams(struct v4l2_subdev *sd,
> + struct v4l2_subdev_state *state, u32 pad,
> + u64 streams_mask)
> +{
> + struct viscsi2 *priv = sd_to_csi2(sd);
> +
> + /* Disabling: turn off sensor -> turn off CSI2RX */
> + v4l2_subdev_disable_streams(priv->remote, priv->remote_pad, BIT(0));
> + viscsi2_stop(priv);
> +
> + return 0;
> +}
> +
> +static int viscsi2_enum_mbus_code(struct v4l2_subdev *sd,
> + struct v4l2_subdev_state *sd_state,
> + struct v4l2_subdev_mbus_code_enum *code)
> +{
> + if (code->pad == VISCSI2_PAD_SRC) {
> + const struct v4l2_mbus_framefmt *sink_fmt;
> +
> + /* SRC pad supports exactly the same format as SINK pad */
> + if (code->index)
> + return -EINVAL;
> + sink_fmt = v4l2_subdev_state_get_format(sd_state,
> + VISCSI2_PAD_SINK);
> + code->code = sink_fmt->code;
> + return 0;
> + }
> +
> + if (code->index >= ARRAY_SIZE(viscsi2_formats))
> + return -EINVAL;
> + code->code = viscsi2_formats[code->index].code;
> +
> + return 0;
> +}
> +
> +static int viscsi2_enum_frame_size(struct v4l2_subdev *sd,
> + struct v4l2_subdev_state *sd_state,
> + struct v4l2_subdev_frame_size_enum *fse)
> +{
> + if (fse->index > 0)
> + return -EINVAL;
> +
> + if (!fmt_for_mbus_code(fse->code))
> + return -EINVAL;
> +
> + fse->min_width = VISCSI2_MIN_WIDTH;
> + fse->max_width = VISCSI2_MAX_WIDTH;
> + fse->min_height = VISCSI2_MIN_HEIGHT;
> + fse->max_height = VISCSI2_MAX_HEIGHT;
> +
> + return 0;
> +}
> +
> +static int viscsi2_init_state(struct v4l2_subdev *sd,
> + struct v4l2_subdev_state *sd_state)
> +{
> + struct v4l2_mbus_framefmt *sink_fmt, *src_fmt;
> +
> + sink_fmt = v4l2_subdev_state_get_format(sd_state, VISCSI2_PAD_SINK);
> + src_fmt = v4l2_subdev_state_get_format(sd_state, VISCSI2_PAD_SRC);
> +
> + sink_fmt->width = VISCSI2_DEF_WIDTH;
> + sink_fmt->height = VISCSI2_DEF_HEIGHT;
> + sink_fmt->field = V4L2_FIELD_NONE;
> + sink_fmt->code = viscsi2_formats[0].code;
> + sink_fmt->colorspace = V4L2_COLORSPACE_RAW;
> + sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
> + sink_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
> + sink_fmt->xfer_func = V4L2_XFER_FUNC_NONE;
> +
> + *src_fmt = *sink_fmt;
> +
> + return 0;
> +}
> +
> +static int viscsi2_set_pad_format(struct v4l2_subdev *sd,
> + struct v4l2_subdev_state *sd_state,
> + struct v4l2_subdev_format *fmt)
> +{
> + struct v4l2_mbus_framefmt *sink_fmt, *src_fmt;
> +
> + /* SRC PAD has the same format as SINK PAD */
> + if (fmt->pad == VISCSI2_PAD_SRC)
> + return v4l2_subdev_get_fmt(sd, sd_state, fmt);
> +
> + sink_fmt = v4l2_subdev_state_get_format(sd_state, VISCSI2_PAD_SINK);
> +
> + *sink_fmt = fmt->format;
> + sink_fmt->width = clamp_t(u32, fmt->format.width, VISCSI2_MIN_WIDTH,
> + VISCSI2_MAX_WIDTH);
> + sink_fmt->height = clamp_t(u32, fmt->format.height, VISCSI2_MIN_HEIGHT,
> + VISCSI2_MAX_HEIGHT);
> + if (!fmt_for_mbus_code(sink_fmt->code))
> + sink_fmt->code = viscsi2_formats[0].code;
> + fmt->format = *sink_fmt;
> +
> + /* Source pad should have the same format */
> + src_fmt = v4l2_subdev_state_get_format(sd_state, VISCSI2_PAD_SRC);
> + *src_fmt = *sink_fmt;
> +
> + return 0;
> +}
> +
> +static const struct media_entity_operations viscsi2_entity_ops = {
> + .link_validate = v4l2_subdev_link_validate,
> +};
> +
> +static const struct v4l2_subdev_pad_ops viscsi2_pad_ops = {
> + .enum_mbus_code = viscsi2_enum_mbus_code,
> + .enum_frame_size = viscsi2_enum_frame_size,
> + .disable_streams = viscsi2_disable_streams,
> + .enable_streams = viscsi2_enable_streams,
> + .get_fmt = v4l2_subdev_get_fmt,
> + .set_fmt = viscsi2_set_pad_format,
> +};
> +
> +static const struct v4l2_subdev_ops viscsi2_subdev_ops = {
> + .pad = &viscsi2_pad_ops,
> +};
> +
> +static const struct v4l2_subdev_internal_ops viscsi2_internal_ops = {
> + .init_state = viscsi2_init_state,
> +};
> +
> +static int viscsi2_notify_bound(struct v4l2_async_notifier *notifier,
> + struct v4l2_subdev *subdev,
> + struct v4l2_async_connection *asc)
> +{
> + struct viscsi2 *priv = notifier_to_csi2(notifier);
> + int pad;
> +
> + pad = media_entity_get_fwnode_pad(&subdev->entity, asc->match.fwnode,
> + MEDIA_PAD_FL_SOURCE);
> + if (pad < 0) {
> + dev_err(priv->dev, "Failed to find pad for %s\n", subdev->name);
> + return pad;
> + }
> +
> + priv->remote = subdev;
> + priv->remote_pad = pad;
> +
> + return media_create_pad_link(
> + &subdev->entity, pad, &priv->subdev.entity, 0,
> + MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
> +}
> +
> +static void viscsi2_notify_unbind(struct v4l2_async_notifier *notifier,
> + struct v4l2_subdev *subdev,
> + struct v4l2_async_connection *asc)
> +{
> + struct viscsi2 *priv = notifier_to_csi2(notifier);
> +
> + priv->remote = NULL;
> +}
> +
> +static const struct v4l2_async_notifier_operations viscsi2_notify_ops = {
> + .bound = viscsi2_notify_bound,
> + .unbind = viscsi2_notify_unbind,
> +};
> +
> +static int viscsi2_parse_dt(struct viscsi2 *priv)
> +{
> + struct v4l2_async_connection *asc;
> + struct fwnode_handle *fwnode;
> + struct fwnode_handle *ep;
> + struct v4l2_fwnode_endpoint v4l2_ep = {
> + .bus_type = V4L2_MBUS_CSI2_DPHY,
> + };
> + int ret;
> +
> + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(priv->dev), 0, 0, 0);
> + if (!ep) {
> + dev_err(priv->dev, "Not connected to subdevice\n");
> + return -EINVAL;
> + }
> +
> + ret = v4l2_fwnode_endpoint_parse(ep, &v4l2_ep);
> + if (ret) {
> + dev_err(priv->dev, "Could not parse v4l2 endpoint\n");
> + goto error_fwnode_handle_put;
> + }
> +
> + priv->lanes = v4l2_ep.bus.mipi_csi2.num_data_lanes;
> + if (priv->lanes != 1 && priv->lanes != 2 && priv->lanes != 4) {
> + dev_err(priv->dev,
> + "Unsupported number of data-lanes for D-PHY: %u\n",
> + priv->lanes);
> + goto error_fwnode_handle_put;
> + }
> +
> + fwnode = fwnode_graph_get_remote_endpoint(ep);
> + fwnode_handle_put(ep);
> +
> + v4l2_async_subdev_nf_init(&priv->notifier, &priv->subdev);
> + priv->notifier.ops = &viscsi2_notify_ops;
> +
> + asc = v4l2_async_nf_add_fwnode(&priv->notifier, fwnode,
> + struct v4l2_async_connection);
> + fwnode_handle_put(fwnode);
> + if (IS_ERR(asc))
> + return PTR_ERR(asc);
> +
> + ret = v4l2_async_nf_register(&priv->notifier);
> + if (ret)
> + v4l2_async_nf_cleanup(&priv->notifier);
> +
> + return ret;
> +
> +error_fwnode_handle_put:
> + fwnode_handle_put(ep);
> + return -EINVAL;
> +}
> +
> +static irqreturn_t viscsi2_irq(int irq, void *dev_id)
> +{
> + struct viscsi2 *priv = dev_id;
> + u32 event;
> +
> + event = viscsi2_read(priv, REG_CSI2RX_INT_ST_MAIN);
> + dev_err_ratelimited(priv->dev, "CSI2RX error 0x%x.\n", event);
> +
> +#ifdef CONFIG_DEBUG_FS
> + priv->debug_phy_fatal |=
> + viscsi2_read(priv, REG_CSI2RX_INT_ST_PHY_FATAL);
> + priv->debug_pkt_fatal |=
> + viscsi2_read(priv, REG_CSI2RX_INT_ST_PKT_FATAL);
> + priv->debug_frame_fatal |=
> + viscsi2_read(priv, REG_CSI2RX_INT_ST_FRAME_FATAL);
> + priv->debug_phy |= viscsi2_read(priv, REG_CSI2RX_INT_ST_PHY);
> + priv->debug_pkt |= viscsi2_read(priv, REG_CSI2RX_INT_ST_PKT);
> + priv->debug_line |= viscsi2_read(priv, REG_CSI2RX_INT_ST_LINE);
> +#endif
> +
> + return IRQ_HANDLED;
> +}
> +
> +static const struct of_device_id viscsi2_of_table[] = {
> + {
> + .compatible = "toshiba,visconti5-csi2",
> + },
> + { /* Sentinel */ }
> +};
> +
> +static int viscsi2_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct viscsi2 *priv;
> + int irq, ret;
> +
> + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> + if (!priv)
> + return -ENOMEM;
> +
> + priv->running = false;
> +
> + priv->dev = dev;
> +
> + priv->base = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(priv->base))
> + return PTR_ERR(priv->base);
> +
> + irq = platform_get_irq(pdev, 0);
> + if (irq < 0)
> + return irq;
> + ret = devm_request_irq(dev, irq, viscsi2_irq, 0, KBUILD_MODNAME, priv);
> + if (ret)
> + return dev_err_probe(dev, ret, "irq request failed");
> +
> + priv->irq = irq;
> +
> + priv->clk_cfg = devm_clk_get(dev, "cfg");
> + if (IS_ERR(priv->clk_cfg))
> + return dev_err_probe(dev, PTR_ERR(priv->clk_cfg),
> + "cannot get clock cfg");
> + priv->clk_apb = devm_clk_get(dev, "apb");
> + if (IS_ERR(priv->clk_apb))
> + return dev_err_probe(dev, PTR_ERR(priv->clk_apb),
> + "cannot get clock apb");
> + priv->rst = devm_reset_control_get_exclusive_by_index(dev, 0);
> + if (IS_ERR(priv->rst))
> + return dev_err_probe(dev, PTR_ERR(priv->rst),
> + "cannot get reset");
> +
> + platform_set_drvdata(pdev, priv);
> +
> + ret = viscsi2_parse_dt(priv);
> + if (ret)
> + return ret;
> +
> + priv->subdev.dev = &pdev->dev;
> + v4l2_subdev_init(&priv->subdev, &viscsi2_subdev_ops);
> + v4l2_set_subdevdata(&priv->subdev, &pdev->dev);
> + snprintf(priv->subdev.name, sizeof(priv->subdev.name), "%s %s",
> + KBUILD_MODNAME, dev_name(&pdev->dev));
> +
> + priv->subdev.internal_ops = &viscsi2_internal_ops;
> + priv->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
> + priv->subdev.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
> + priv->subdev.entity.ops = &viscsi2_entity_ops;
> +
> + priv->pads[VISCSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
> + priv->pads[VISCSI2_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
> +
> + ret = media_entity_pads_init(&priv->subdev.entity,
> + ARRAY_SIZE(priv->pads), priv->pads);
> + if (ret)
> + goto err_cleanup_async;
> +
> + ret = v4l2_subdev_init_finalize(&priv->subdev);
> + if (ret)
> + goto err_cleanup_media_entity;
> +
> + ret = v4l2_async_register_subdev(&priv->subdev);
> + if (ret < 0)
> + goto err_cleanup_subdev_state;
> +
> +#ifdef CONFIG_DEBUG_FS
> + viscsi2_debug_init(priv);
> +#endif
> +
> + return 0;
> +
> +err_cleanup_subdev_state:
> + v4l2_subdev_cleanup(&priv->subdev);
> +
> +err_cleanup_media_entity:
> + media_entity_cleanup(&priv->subdev.entity);
> +
> +err_cleanup_async:
> + v4l2_async_nf_unregister(&priv->notifier);
> + v4l2_async_nf_cleanup(&priv->notifier);
> +
> + return ret;
> +}
> +
> +static void viscsi2_remove(struct platform_device *pdev)
> +{
> + struct viscsi2 *priv = platform_get_drvdata(pdev);
> +
> +#ifdef CONFIG_DEBUG_FS
> + viscsi2_debug_cleanup(priv);
> +#endif
> +
> + v4l2_async_nf_unregister(&priv->notifier);
> + v4l2_async_nf_cleanup(&priv->notifier);
> + v4l2_async_unregister_subdev(&priv->subdev);
> +
> + v4l2_subdev_cleanup(&priv->subdev);
> + media_entity_cleanup(&priv->subdev.entity);
> +}
> +
> +static struct platform_driver viscsi2_driver = {
> + .probe = viscsi2_probe,
> + .remove = viscsi2_remove,
> + .driver = {
> + .name = "visconti-csi2rx",
> + .of_match_table = viscsi2_of_table,
> + },
> +};
> +
> +module_platform_driver(viscsi2_driver);
> +
> +MODULE_AUTHOR("Yuji Ishikawa <yuji2.ishikawa@toshiba.co.jp>");
> +MODULE_DESCRIPTION("Toshiba Visconti CSI-2 receiver driver");
> +MODULE_LICENSE("Dual BSD/GPL");
>
> --
> 2.34.1
>
>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v13 1/7] dt-bindings: media: platform: visconti: Add Toshiba Visconti MIPI CSI-2 Receiver
2025-10-16 2:24 ` [PATCH v13 1/7] dt-bindings: media: platform: visconti: Add Toshiba Visconti MIPI CSI-2 Receiver Yuji Ishikawa
2025-10-16 4:34 ` Frank Li
@ 2025-10-16 6:38 ` Krzysztof Kozlowski
2025-10-20 6:16 ` yuji2.ishikawa
1 sibling, 1 reply; 15+ messages in thread
From: Krzysztof Kozlowski @ 2025-10-16 6:38 UTC (permalink / raw)
To: Yuji Ishikawa, Nobuhiro Iwamatsu, Mauro Carvalho Chehab,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Philipp Zabel
Cc: linux-media, devicetree, linux-arm-kernel, linux-kernel
On 16/10/2025 04:24, Yuji Ishikawa wrote:
> Adds the Device Tree binding documentation that allows to describe
"Add".
https://elixir.bootlin.com/linux/v6.16/source/Documentation/process/submitting-patches.rst#L94
> the MIPI CSI-2 Receiver found in Toshiba Visconti SoCs.
...
> +examples:
> + - |
> + #include <dt-bindings/clock/toshiba,tmpv770x.h>
> + #include <dt-bindings/interrupt-controller/arm-gic.h>
> + #include <dt-bindings/interrupt-controller/irq.h>
> +
> + soc {
> + #address-cells = <2>;
> + #size-cells = <2>;
> +
> + csi0: csi@1c008000 {
I don't understand why the label appeared. It is not used and it wasn't
here before. I did not ask to add label, but I only asked to fix the
node name to match generic names rule.
Drop the label. With these fixes:
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v13 2/7] dt-bindings: media: platform: visconti: Add Toshiba Visconti Video Input Interface
2025-10-16 2:24 ` [PATCH v13 2/7] dt-bindings: media: platform: visconti: Add Toshiba Visconti Video Input Interface Yuji Ishikawa
@ 2025-10-16 6:39 ` Krzysztof Kozlowski
0 siblings, 0 replies; 15+ messages in thread
From: Krzysztof Kozlowski @ 2025-10-16 6:39 UTC (permalink / raw)
To: Yuji Ishikawa, Nobuhiro Iwamatsu, Mauro Carvalho Chehab,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Philipp Zabel
Cc: linux-media, devicetree, linux-arm-kernel, linux-kernel
On 16/10/2025 04:24, Yuji Ishikawa wrote:
> Adds the Device Tree binding documentation that allows to describe
> the Video Input Interface found in Toshiba Visconti SoCs.
>
> Signed-off-by: Yuji Ishikawa <yuji2.ishikawa@toshiba.co.jp>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 15+ messages in thread
* RE: [PATCH v13 1/7] dt-bindings: media: platform: visconti: Add Toshiba Visconti MIPI CSI-2 Receiver
2025-10-16 4:34 ` Frank Li
@ 2025-10-20 6:11 ` yuji2.ishikawa
0 siblings, 0 replies; 15+ messages in thread
From: yuji2.ishikawa @ 2025-10-20 6:11 UTC (permalink / raw)
To: Frank.li
Cc: nobuhiro.iwamatsu.x90, mchehab, robh, krzk+dt, conor+dt, p.zabel,
linux-media, devicetree, linux-arm-kernel, linux-kernel
Hello Frank
Thank you for review comments.
> -----Original Message-----
> From: Frank Li <Frank.li@nxp.com>
> Sent: Thursday, October 16, 2025 1:35 PM
> To: ishikawa yuji(石川 悠司 □AIDC○EA開)
> <yuji2.ishikawa@toshiba.co.jp>
> Cc: iwamatsu nobuhiro(岩松 信洋 □DITC○CPT)
> <nobuhiro.iwamatsu.x90@mail.toshiba>; Mauro Carvalho Chehab
> <mchehab@kernel.org>; Rob Herring <robh@kernel.org>; Krzysztof Kozlowski
> <krzk+dt@kernel.org>; Conor Dooley <conor+dt@kernel.org>; Philipp Zabel
> <p.zabel@pengutronix.de>; linux-media@vger.kernel.org;
> devicetree@vger.kernel.org; linux-arm-kernel@lists.infradead.org;
> linux-kernel@vger.kernel.org
> Subject: Re: [PATCH v13 1/7] dt-bindings: media: platform: visconti: Add
> Toshiba Visconti MIPI CSI-2 Receiver
>
> On Thu, Oct 16, 2025 at 11:24:38AM +0900, Yuji Ishikawa wrote:
> > Adds the Device Tree binding documentation that allows to describe the
> > MIPI CSI-2 Receiver found in Toshiba Visconti SoCs.
> >
> > Signed-off-by: Yuji Ishikawa <yuji2.ishikawa@toshiba.co.jp>
> > ---
> > Changelog v12:
> > - Newly add bindings for CSI2RX driver
> >
> > Changelog v13:
> > - add entries to MAINTAINERS file.
> > - update email address of Nobuhiro Iwamatsu in /maintainers
> > - add Yuji Ishikawa to /maintainers
> > - change /properties/compatible: toshiba,visconti5-csi2rx ->
> > toshiba,visconti5-csi2
> > - change bindings file name: toshiba,visconti5-csi2rx ->
> > toshiba,visconti5-csi2
> > - change node name in sample DTS: csi2rx -> csi
> > - remove "|-" from /description
> > - update /description
> > - add definitions of clock and reset
> > - update /properties/ports/properties/port@0/description for better
> > comment
> > - update /properties/ports/properties/port@0/$ref to specify full
> > pathname
> > - remove
> /properties/ports/properties/port@0/properties/endpoint/properties/data-la
> nes/description because the default text provides enough information.
> > - update sample dts
> > ---
> > .../bindings/media/toshiba,visconti5-csi2.yaml | 125
> +++++++++++++++++++++
> > MAINTAINERS | 7 ++
> > 2 files changed, 132 insertions(+)
> >
> > diff --git
> > a/Documentation/devicetree/bindings/media/toshiba,visconti5-csi2.yaml
> > b/Documentation/devicetree/bindings/media/toshiba,visconti5-csi2.yaml
> > new file mode 100644
> > index 000000000000..21fb46de5b6e
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/media/toshiba,visconti5-csi2.y
> > +++ aml
> > @@ -0,0 +1,125 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) %YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/media/toshiba,visconti5-csi2.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: Toshiba Visconti5 SoC MIPI CSI-2 receiver
> > +
> > +maintainers:
> > + - Nobuhiro Iwamatsu <nobuhiro.iwamatsu.x90@mail.toshiba>
> > + - Yuji Ishikawa <yuji2.ishikawa@toshiba.co.jp>
> > +
> > +description:
> > + Toshiba Visconti5 SoC MIPI CSI-2 receiver device receives MIPI
> > +CSI-2 video
> > + stream. The obtained video stream is used as input for the Visconti5 VIIF.
> > +
> > +properties:
> > + compatible:
> > + const: toshiba,visconti5-csi2
> > +
>
> ...
>
> > +
> > +examples:
> > + - |
> > + #include <dt-bindings/clock/toshiba,tmpv770x.h>
> > + #include <dt-bindings/interrupt-controller/arm-gic.h>
> > + #include <dt-bindings/interrupt-controller/irq.h>
> > +
> > + soc {
> > + #address-cells = <2>;
> > + #size-cells = <2>;
> > +
> > + csi0: csi@1c008000 {
>
> Needn't label csi0
>
I'll drop the label.
> > + compatible = "toshiba,visconti5-csi2";
> > + reg = <0 0x1c008000 0 0x400>;
> > + interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>;
> > + clocks = <&pismu TMPV770X_CLK_VIIFBS0_CFG>,
> > + <&pismu TMPV770X_CLK_VIIFBS0_APB>;
> > + clock-names = "cfg", "apb";
> > + resets = <&pismu TMPV770X_RESET_VIIFBS0_APB>;
> > +
> > + ports {
> > + #address-cells = <1>;
> > + #size-cells = <0>;
>
> need empty line here.
I'll add empty line before blocks.
> > + port@0 {
> > + reg = <0>;
>
> empty line here
>
> > + csi0_in: endpoint {
> > + data-lanes = <1 2>;
> > + remote-endpoint = <&imx219_out>;
> > + };
> > + };
>
> empty line here
>
> Frank
> > + port@1 {
> > + reg = <1>;
> > + csi0_out: endpoint {
> > + remote-endpoint = <&video0_in>;
> > + };
> > + };
> > + };
> > + };
> > + };
> > diff --git a/MAINTAINERS b/MAINTAINERS index
> > 46126ce2f968..e4634a0aad74 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -25979,6 +25979,13 @@ F:
> Documentation/devicetree/bindings/media/i2c/toshiba,tc358743.txt
> > F: drivers/media/i2c/tc358743*
> > F: include/media/i2c/tc358743.h
> >
> > +TOSHIBA VISCONTI VIIF DRIVER
> > +M: Nobuhiro Iwamatsu <nobuhiro.iwamatsu.x90@mail.toshiba>
> > +M: Yuji Ishikawa <yuji2.ishikawa@toshiba.co.jp>
> > +L: linux-media@vger.kernel.org
> > +S: Maintained
> > +F:
> Documentation/devicetree/bindings/media/toshiba,visconti5-csi2.ya
> ml
> > +
> > TOSHIBA WMI HOTKEYS DRIVER
> > M: Azael Avalos <coproscefalo@gmail.com>
> > L: platform-driver-x86@vger.kernel.org
> >
> > --
> > 2.34.1
> >
> >
Regards,
Yuji Ishikawa
^ permalink raw reply [flat|nested] 15+ messages in thread
* RE: [PATCH v13 4/7] media: platform: visconti: Add Toshiba Visconti CSI-2 Receiver driver
2025-10-16 4:45 ` Frank Li
@ 2025-10-20 6:13 ` yuji2.ishikawa
2025-10-20 16:05 ` Frank Li
0 siblings, 1 reply; 15+ messages in thread
From: yuji2.ishikawa @ 2025-10-20 6:13 UTC (permalink / raw)
To: Frank.li
Cc: nobuhiro.iwamatsu.x90, mchehab, robh, krzk+dt, conor+dt, p.zabel,
linux-media, devicetree, linux-arm-kernel, linux-kernel
Hello Frank,
Thank you for review comments.
> -----Original Message-----
> From: Frank Li <Frank.li@nxp.com>
> Sent: Thursday, October 16, 2025 1:45 PM
> To: ishikawa yuji(石川 悠司 □AIDC○EA開)
> <yuji2.ishikawa@toshiba.co.jp>
> Cc: iwamatsu nobuhiro(岩松 信洋 □DITC○CPT)
> <nobuhiro.iwamatsu.x90@mail.toshiba>; Mauro Carvalho Chehab
> <mchehab@kernel.org>; Rob Herring <robh@kernel.org>; Krzysztof Kozlowski
> <krzk+dt@kernel.org>; Conor Dooley <conor+dt@kernel.org>; Philipp Zabel
> <p.zabel@pengutronix.de>; linux-media@vger.kernel.org;
> devicetree@vger.kernel.org; linux-arm-kernel@lists.infradead.org;
> linux-kernel@vger.kernel.org
> Subject: Re: [PATCH v13 4/7] media: platform: visconti: Add Toshiba Visconti
> CSI-2 Receiver driver
>
> On Thu, Oct 16, 2025 at 11:24:41AM +0900, Yuji Ishikawa wrote:
> > Add support to MIPI CSI-2 Receiver on Toshiba Visconti ARM SoCs.
> > This driver is used with Visconti Video Input Interface driver.
> >
> > Signed-off-by: Yuji Ishikawa <yuji2.ishikawa@toshiba.co.jp>
> > ---
> > Changelog v12:
> > - Separate CSI2RX driver and made it independent driver
> > - viif_csi2rx subdevice driver (in v11 patch) was removed.
> > - dictionary order at Kconfig and Makefile
> >
> > Changelog v13:
> > - wrap one line at 80 characters
> > - change banner comment style
> > - update comment style; spacing at the start and end, capitalize first
> > letter
> > - add support for clock and reset framework
> > - add debugfs to pass debug and status information
> > - add entries to MAINTAINERS file
> > - Kconfig: add a blank line just after License Identifier.
> > - update references to header files
> > - remove redundant inline qualifier
> > - shorten function/variable names: visconti_csi2rx -> viscsi2
> > - simplify dphy_write and dphy read operations
> > - remove osc_freq_target from struct csi2rx_dphy_hs_info, which is always
> the same value.
> > - add comment about MASK register's behavior (reversed polarity)
> > - use v4l2_get_link_freq() instead of get_pixelclock()
> > - set driver name according to module name: visconti_csi2rx_dev ->
> > visconti-csi2rx
> > - check error before setting priv->irq in probe()
> > - check error at fmt_for_mbus_code()
> > - add callback for ioctl(VIDIOC_ENUM_FRAMESIZES)
> > - improve viscsi2_parse_dt() by assuming bus_type is CSI2_DPHY
> > - use dev_err_ratelimited() for irq handler
> > - bugfix on fmt_for_mbus_code(): in case unsupported mbus_code is
> > given
> > - add goto based error handling sequence to viscsi2_parse_dt()
> > - specify default value of colorspace, ycbcr_enc, quantization and
> > xfer_func of sink/src_fmt
> > - specify sensor at enable_streams() using previously set ID, instead
> > of checking remote pad every time
> > - remove U suffix on numeric value
> > - use unsigned int variable for loop index
> > - remove redundant casting
> > - use GENMASK instead of literal
> > - remove unused constants
> > - remove unused visconti_csi2rx_video_ops
> > ---
> > MAINTAINERS | 1 +
> > drivers/media/platform/Kconfig | 1 +
> > drivers/media/platform/Makefile | 1 +
> > drivers/media/platform/toshiba/Kconfig | 6 +
> > drivers/media/platform/toshiba/Makefile | 3 +
> > drivers/media/platform/toshiba/visconti/Kconfig | 17 +
> > drivers/media/platform/toshiba/visconti/Makefile | 8 +
> > .../media/platform/toshiba/visconti/csi2rx_drv.c | 954
> +++++++++++++++++++++
> > 8 files changed, 991 insertions(+)
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS index
> > c17c7ddba5af..ce973791b367 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -25986,6 +25986,7 @@ L: linux-media@vger.kernel.org
> > S: Maintained
> > F:
> Documentation/devicetree/bindings/media/toshiba,visconti5-csi2.ya
> ml
> > F:
> Documentation/devicetree/bindings/media/toshiba,visconti5-viif.yam
> l
> > +F: drivers/media/platform/toshiba/visconti/
> >
> > TOSHIBA WMI HOTKEYS DRIVER
> > M: Azael Avalos <coproscefalo@gmail.com>
> > diff --git a/drivers/media/platform/Kconfig
> > b/drivers/media/platform/Kconfig index 9287faafdce5..d5265aa16c88
> > 100644
> > --- a/drivers/media/platform/Kconfig
> > +++ b/drivers/media/platform/Kconfig
> > @@ -87,6 +87,7 @@ source "drivers/media/platform/st/Kconfig"
> > source "drivers/media/platform/sunxi/Kconfig"
> > source "drivers/media/platform/synopsys/Kconfig"
> > source "drivers/media/platform/ti/Kconfig"
> > +source "drivers/media/platform/toshiba/Kconfig"
> > source "drivers/media/platform/verisilicon/Kconfig"
> > source "drivers/media/platform/via/Kconfig"
> > source "drivers/media/platform/xilinx/Kconfig"
> > diff --git a/drivers/media/platform/Makefile
> > b/drivers/media/platform/Makefile index 6fd7db0541c7..09e67ecb9559
> > 100644
> > --- a/drivers/media/platform/Makefile
> > +++ b/drivers/media/platform/Makefile
> > @@ -30,6 +30,7 @@ obj-y += st/
> > obj-y += sunxi/
> > obj-y += synopsys/
> > obj-y += ti/
> > +obj-y += toshiba/
> > obj-y += verisilicon/
> > obj-y += via/
> > obj-y += xilinx/
> > diff --git a/drivers/media/platform/toshiba/Kconfig
> > b/drivers/media/platform/toshiba/Kconfig
> > new file mode 100644
> > index 000000000000..f02983f4fc97
> > --- /dev/null
> > +++ b/drivers/media/platform/toshiba/Kconfig
> > @@ -0,0 +1,6 @@
> > +# SPDX-License-Identifier: GPL-2.0-only
> > +
> > +comment "Toshiba media platform drivers"
> > +
> > +source "drivers/media/platform/toshiba/visconti/Kconfig"
> > +
> > diff --git a/drivers/media/platform/toshiba/Makefile
> > b/drivers/media/platform/toshiba/Makefile
> > new file mode 100644
> > index 000000000000..dd89a9a35704
> > --- /dev/null
> > +++ b/drivers/media/platform/toshiba/Makefile
> > @@ -0,0 +1,3 @@
> > +# SPDX-License-Identifier: GPL-2.0-only
> > +
> > +obj-y += visconti/
> > diff --git a/drivers/media/platform/toshiba/visconti/Kconfig
> > b/drivers/media/platform/toshiba/visconti/Kconfig
> > new file mode 100644
> > index 000000000000..aa0b63f9f008
> > --- /dev/null
> > +++ b/drivers/media/platform/toshiba/visconti/Kconfig
> > @@ -0,0 +1,17 @@
> > +# SPDX-License-Identifier: GPL-2.0-only
> > +
> > +config VIDEO_VISCONTI_CSI2RX
> > + tristate "Visconti MIPI CSI-2 Receiver driver"
> > + depends on V4L_PLATFORM_DRIVERS
> > + depends on VIDEO_DEV && OF
> > + depends on ARCH_VISCONTI || COMPILE_TEST
> > + select MEDIA_CONTROLLER
> > + select VIDEO_V4L2_SUBDEV_API
> > + select V4L2_FWNODE
> > + help
> > + Support for Toshiba Visconti MIPI CSI-2 receiver,
> > + which is used with Visconti Camera Interface driver.
> > +
> > + This driver yields 1 subdevice node for a hardware instance.
> > + To compile this driver as a module, choose M here: the
> > + module will be called visconti-csi2rx.
> > diff --git a/drivers/media/platform/toshiba/visconti/Makefile
> > b/drivers/media/platform/toshiba/visconti/Makefile
> > new file mode 100644
> > index 000000000000..62a029376134
> > --- /dev/null
> > +++ b/drivers/media/platform/toshiba/visconti/Makefile
> > @@ -0,0 +1,8 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +#
> > +# Makefile for the Visconti video input device driver #
> > +
> > +visconti-csi2rx-objs = csi2rx_drv.o
> > +
> > +obj-$(CONFIG_VIDEO_VISCONTI_CSI2RX) += visconti-csi2rx.o
> > diff --git a/drivers/media/platform/toshiba/visconti/csi2rx_drv.c
> > b/drivers/media/platform/toshiba/visconti/csi2rx_drv.c
> > new file mode 100644
> > index 000000000000..53d112432a86
> > --- /dev/null
> > +++ b/drivers/media/platform/toshiba/visconti/csi2rx_drv.c
> > @@ -0,0 +1,954 @@
> > +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
> > +/*
> > + * Toshiba Visconti Video Capture Support
> > + *
> > + * (C) Copyright 2025 TOSHIBA CORPORATION
> > + * (C) Copyright 2025 Toshiba Electronic Devices & Storage
> > +Corporation */
> > +
> > +#include <linux/clk.h>
> > +#include <linux/debugfs.h>
> > +#include <linux/delay.h>
> > +#include <linux/device.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/io.h>
> > +#include <linux/module.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/property.h>
> > +#include <linux/reset.h>
> > +#include <linux/seq_file.h>
> > +
> > +#include <media/mipi-csi2.h>
> > +#include <media/v4l2-common.h>
> > +#include <media/v4l2-ctrls.h>
> > +#include <media/v4l2-fwnode.h>
> > +#include <media/v4l2-subdev.h>
> > +
> > +/* CSI2HOST register space */
> > +#define REG_CSI2RX_NLANES 0x4
> > +#define REG_CSI2RX_RESETN 0x8
> > +#define REG_CSI2RX_INT_ST_MAIN 0xc
> > +#define REG_CSI2RX_DATA_IDS_1 0x10
> > +#define REG_CSI2RX_DATA_IDS_2 0x14
> > +#define REG_CSI2RX_PHY_SHUTDOWNZ 0x40
> > +#define REG_CSI2RX_PHY_RSTZ 0x44
> > +
> > +/* Access to dphy external registers */ #define
> > +REG_CSI2RX_PHY_TESTCTRL0 0x50
> > +#define BIT_TESTCTRL0_CLK_0 0
> > +#define BIT_TESTCTRL0_CLK_1 BIT(1)
> > +
> > +#define REG_CSI2RX_PHY_TESTCTRL1 0x54
> > +#define BIT_TESTCTRL1_ADDR BIT(16)
> > +#define MASK_TESTCTRL1_DOUT GENMASK(15, 8)
> > +
> > +#define REG_CSI2RX_INT_ST_PHY_FATAL 0xe0 #define
> > +REG_CSI2RX_INT_MSK_PHY_FATAL 0xe4
> > +#define MASK_PHY_FATAL_ALL 0x0000000f
> > +
> > +#define REG_CSI2RX_INT_ST_PKT_FATAL 0xf0 #define
> > +REG_CSI2RX_INT_MSK_PKT_FATAL 0xf4
> > +#define MASK_PKT_FATAL_ALL 0x0001000f
> > +
> > +#define REG_CSI2RX_INT_ST_FRAME_FATAL 0x100 #define
> > +REG_CSI2RX_INT_MSK_FRAME_FATAL 0x104
> > +#define MASK_FRAME_FATAL_ALL 0x000f0f0f
> > +
> > +#define REG_CSI2RX_INT_ST_PHY 0x110
> > +#define REG_CSI2RX_INT_MSK_PHY 0x114
> > +#define MASK_PHY_ERROR_ALL 0x000f000f
> > +
> > +#define REG_CSI2RX_INT_ST_PKT 0x120
> > +#define REG_CSI2RX_INT_MSK_PKT 0x124
> > +#define MASK_PKT_ERROR_ALL 0x000f000f
> > +
> > +#define REG_CSI2RX_INT_ST_LINE 0x130
> > +#define REG_CSI2RX_INT_MSK_LINE 0x134
> > +#define MASK_LINE_ERROR_ALL 0x00ff00ff
>
>
> Look like it is dwc CSI2RX controller. Can we work out a common dwc CSI2RX
> driver to avoid every duplicate the same code
>
> A attempt at
> https://lore.kernel.org/imx/20250821-95_cam-v3-20-c9286fbb34b9@nxp.com
> /
>
> The above is the base on stage's imx6. we try to find a path to workout a
> common dwc csi2rx.
>
> Frank
>
Yes, it is DWC CSI2RX controller.
It's an interesting idea to have a common dwc driver.
Let me check if CSI2RX and PHY drivers work well with Visconti's hardware.
Please let me know the base repository to apply patches.
Regards,
Yuji Ishikawa
^ permalink raw reply [flat|nested] 15+ messages in thread
* RE: [PATCH v13 1/7] dt-bindings: media: platform: visconti: Add Toshiba Visconti MIPI CSI-2 Receiver
2025-10-16 6:38 ` Krzysztof Kozlowski
@ 2025-10-20 6:16 ` yuji2.ishikawa
0 siblings, 0 replies; 15+ messages in thread
From: yuji2.ishikawa @ 2025-10-20 6:16 UTC (permalink / raw)
To: krzk, nobuhiro.iwamatsu.x90, mchehab, robh, krzk+dt, conor+dt,
p.zabel
Cc: linux-media, devicetree, linux-arm-kernel, linux-kernel
Hello Krzysztof
Thank you for review comments.
> -----Original Message-----
> From: Krzysztof Kozlowski <krzk@kernel.org>
> Sent: Thursday, October 16, 2025 3:38 PM
> To: ishikawa yuji(石川 悠司 □AIDC○EA開)
> <yuji2.ishikawa@toshiba.co.jp>; iwamatsu nobuhiro(岩松 信洋 □DITC○
> CPT) <nobuhiro.iwamatsu.x90@mail.toshiba>; Mauro Carvalho Chehab
> <mchehab@kernel.org>; Rob Herring <robh@kernel.org>; Krzysztof Kozlowski
> <krzk+dt@kernel.org>; Conor Dooley <conor+dt@kernel.org>; Philipp Zabel
> <p.zabel@pengutronix.de>
> Cc: linux-media@vger.kernel.org; devicetree@vger.kernel.org;
> linux-arm-kernel@lists.infradead.org; linux-kernel@vger.kernel.org
> Subject: Re: [PATCH v13 1/7] dt-bindings: media: platform: visconti: Add
> Toshiba Visconti MIPI CSI-2 Receiver
>
> On 16/10/2025 04:24, Yuji Ishikawa wrote:
> > Adds the Device Tree binding documentation that allows to describe
>
> "Add".
>
> https://elixir.bootlin.com/linux/v6.16/source/Documentation/process/submit
> ting-patches.rst#L94
>
I'll fix commit messages.
>
> > the MIPI CSI-2 Receiver found in Toshiba Visconti SoCs.
>
>
> ...
>
> > +examples:
> > + - |
> > + #include <dt-bindings/clock/toshiba,tmpv770x.h>
> > + #include <dt-bindings/interrupt-controller/arm-gic.h>
> > + #include <dt-bindings/interrupt-controller/irq.h>
> > +
> > + soc {
> > + #address-cells = <2>;
> > + #size-cells = <2>;
> > +
> > + csi0: csi@1c008000 {
>
> I don't understand why the label appeared. It is not used and it wasn't here
> before. I did not ask to add label, but I only asked to fix the node name to match
> generic names rule.
>
> Drop the label. With these fixes:
>
> Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
>
I'll remove the label.
>
> Best regards,
> Krzysztof
Regards,
Yuji Ishikawa
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v13 4/7] media: platform: visconti: Add Toshiba Visconti CSI-2 Receiver driver
2025-10-20 6:13 ` yuji2.ishikawa
@ 2025-10-20 16:05 ` Frank Li
0 siblings, 0 replies; 15+ messages in thread
From: Frank Li @ 2025-10-20 16:05 UTC (permalink / raw)
To: yuji2.ishikawa
Cc: nobuhiro.iwamatsu.x90, mchehab, robh, krzk+dt, conor+dt, p.zabel,
linux-media, devicetree, linux-arm-kernel, linux-kernel
On Mon, Oct 20, 2025 at 06:13:37AM +0000, yuji2.ishikawa@toshiba.co.jp wrote:
> Hello Frank,
>
> Thank you for review comments.
>
> > -----Original Message-----
> > From: Frank Li <Frank.li@nxp.com>
> > Sent: Thursday, October 16, 2025 1:45 PM
> > To: ishikawa yuji(石川 悠司 □AIDC○EA開)
> > <yuji2.ishikawa@toshiba.co.jp>
> > Cc: iwamatsu nobuhiro(岩松 信洋 □DITC○CPT)
> > <nobuhiro.iwamatsu.x90@mail.toshiba>; Mauro Carvalho Chehab
> > <mchehab@kernel.org>; Rob Herring <robh@kernel.org>; Krzysztof Kozlowski
> > <krzk+dt@kernel.org>; Conor Dooley <conor+dt@kernel.org>; Philipp Zabel
> > <p.zabel@pengutronix.de>; linux-media@vger.kernel.org;
> > devicetree@vger.kernel.org; linux-arm-kernel@lists.infradead.org;
> > linux-kernel@vger.kernel.org
> > Subject: Re: [PATCH v13 4/7] media: platform: visconti: Add Toshiba Visconti
> > CSI-2 Receiver driver
> >
> > On Thu, Oct 16, 2025 at 11:24:41AM +0900, Yuji Ishikawa wrote:
> > > Add support to MIPI CSI-2 Receiver on Toshiba Visconti ARM SoCs.
> > > This driver is used with Visconti Video Input Interface driver.
> > >
> > > Signed-off-by: Yuji Ishikawa <yuji2.ishikawa@toshiba.co.jp>
> > > ---
> > > Changelog v12:
> > > - Separate CSI2RX driver and made it independent driver
> > > - viif_csi2rx subdevice driver (in v11 patch) was removed.
> > > - dictionary order at Kconfig and Makefile
> > >
> > > Changelog v13:
> > > - wrap one line at 80 characters
> > > - change banner comment style
> > > - update comment style; spacing at the start and end, capitalize first
> > > letter
> > > - add support for clock and reset framework
> > > - add debugfs to pass debug and status information
> > > - add entries to MAINTAINERS file
> > > - Kconfig: add a blank line just after License Identifier.
> > > - update references to header files
> > > - remove redundant inline qualifier
> > > - shorten function/variable names: visconti_csi2rx -> viscsi2
> > > - simplify dphy_write and dphy read operations
> > > - remove osc_freq_target from struct csi2rx_dphy_hs_info, which is always
> > the same value.
> > > - add comment about MASK register's behavior (reversed polarity)
> > > - use v4l2_get_link_freq() instead of get_pixelclock()
> > > - set driver name according to module name: visconti_csi2rx_dev ->
> > > visconti-csi2rx
> > > - check error before setting priv->irq in probe()
> > > - check error at fmt_for_mbus_code()
> > > - add callback for ioctl(VIDIOC_ENUM_FRAMESIZES)
> > > - improve viscsi2_parse_dt() by assuming bus_type is CSI2_DPHY
> > > - use dev_err_ratelimited() for irq handler
> > > - bugfix on fmt_for_mbus_code(): in case unsupported mbus_code is
> > > given
> > > - add goto based error handling sequence to viscsi2_parse_dt()
> > > - specify default value of colorspace, ycbcr_enc, quantization and
> > > xfer_func of sink/src_fmt
> > > - specify sensor at enable_streams() using previously set ID, instead
> > > of checking remote pad every time
> > > - remove U suffix on numeric value
> > > - use unsigned int variable for loop index
> > > - remove redundant casting
> > > - use GENMASK instead of literal
> > > - remove unused constants
> > > - remove unused visconti_csi2rx_video_ops
> > > ---
> > > MAINTAINERS | 1 +
> > > drivers/media/platform/Kconfig | 1 +
> > > drivers/media/platform/Makefile | 1 +
> > > drivers/media/platform/toshiba/Kconfig | 6 +
> > > drivers/media/platform/toshiba/Makefile | 3 +
> > > drivers/media/platform/toshiba/visconti/Kconfig | 17 +
> > > drivers/media/platform/toshiba/visconti/Makefile | 8 +
> > > .../media/platform/toshiba/visconti/csi2rx_drv.c | 954
> > +++++++++++++++++++++
> > > 8 files changed, 991 insertions(+)
> > >
> > > diff --git a/MAINTAINERS b/MAINTAINERS index
> > > c17c7ddba5af..ce973791b367 100644
> > > --- a/MAINTAINERS
> > > +++ b/MAINTAINERS
> > > @@ -25986,6 +25986,7 @@ L: linux-media@vger.kernel.org
> > > S: Maintained
> > > F:
> > Documentation/devicetree/bindings/media/toshiba,visconti5-csi2.ya
> > ml
> > > F:
> > Documentation/devicetree/bindings/media/toshiba,visconti5-viif.yam
> > l
> > > +F: drivers/media/platform/toshiba/visconti/
> > >
> > > TOSHIBA WMI HOTKEYS DRIVER
> > > M: Azael Avalos <coproscefalo@gmail.com>
> > > diff --git a/drivers/media/platform/Kconfig
> > > b/drivers/media/platform/Kconfig index 9287faafdce5..d5265aa16c88
> > > 100644
> > > --- a/drivers/media/platform/Kconfig
> > > +++ b/drivers/media/platform/Kconfig
> > > @@ -87,6 +87,7 @@ source "drivers/media/platform/st/Kconfig"
> > > source "drivers/media/platform/sunxi/Kconfig"
> > > source "drivers/media/platform/synopsys/Kconfig"
> > > source "drivers/media/platform/ti/Kconfig"
> > > +source "drivers/media/platform/toshiba/Kconfig"
> > > source "drivers/media/platform/verisilicon/Kconfig"
> > > source "drivers/media/platform/via/Kconfig"
> > > source "drivers/media/platform/xilinx/Kconfig"
> > > diff --git a/drivers/media/platform/Makefile
> > > b/drivers/media/platform/Makefile index 6fd7db0541c7..09e67ecb9559
> > > 100644
> > > --- a/drivers/media/platform/Makefile
> > > +++ b/drivers/media/platform/Makefile
> > > @@ -30,6 +30,7 @@ obj-y += st/
> > > obj-y += sunxi/
> > > obj-y += synopsys/
> > > obj-y += ti/
> > > +obj-y += toshiba/
> > > obj-y += verisilicon/
> > > obj-y += via/
> > > obj-y += xilinx/
> > > diff --git a/drivers/media/platform/toshiba/Kconfig
> > > b/drivers/media/platform/toshiba/Kconfig
> > > new file mode 100644
> > > index 000000000000..f02983f4fc97
> > > --- /dev/null
> > > +++ b/drivers/media/platform/toshiba/Kconfig
> > > @@ -0,0 +1,6 @@
> > > +# SPDX-License-Identifier: GPL-2.0-only
> > > +
> > > +comment "Toshiba media platform drivers"
> > > +
> > > +source "drivers/media/platform/toshiba/visconti/Kconfig"
> > > +
> > > diff --git a/drivers/media/platform/toshiba/Makefile
> > > b/drivers/media/platform/toshiba/Makefile
> > > new file mode 100644
> > > index 000000000000..dd89a9a35704
> > > --- /dev/null
> > > +++ b/drivers/media/platform/toshiba/Makefile
> > > @@ -0,0 +1,3 @@
> > > +# SPDX-License-Identifier: GPL-2.0-only
> > > +
> > > +obj-y += visconti/
> > > diff --git a/drivers/media/platform/toshiba/visconti/Kconfig
> > > b/drivers/media/platform/toshiba/visconti/Kconfig
> > > new file mode 100644
> > > index 000000000000..aa0b63f9f008
> > > --- /dev/null
> > > +++ b/drivers/media/platform/toshiba/visconti/Kconfig
> > > @@ -0,0 +1,17 @@
> > > +# SPDX-License-Identifier: GPL-2.0-only
> > > +
> > > +config VIDEO_VISCONTI_CSI2RX
> > > + tristate "Visconti MIPI CSI-2 Receiver driver"
> > > + depends on V4L_PLATFORM_DRIVERS
> > > + depends on VIDEO_DEV && OF
> > > + depends on ARCH_VISCONTI || COMPILE_TEST
> > > + select MEDIA_CONTROLLER
> > > + select VIDEO_V4L2_SUBDEV_API
> > > + select V4L2_FWNODE
> > > + help
> > > + Support for Toshiba Visconti MIPI CSI-2 receiver,
> > > + which is used with Visconti Camera Interface driver.
> > > +
> > > + This driver yields 1 subdevice node for a hardware instance.
> > > + To compile this driver as a module, choose M here: the
> > > + module will be called visconti-csi2rx.
> > > diff --git a/drivers/media/platform/toshiba/visconti/Makefile
> > > b/drivers/media/platform/toshiba/visconti/Makefile
> > > new file mode 100644
> > > index 000000000000..62a029376134
> > > --- /dev/null
> > > +++ b/drivers/media/platform/toshiba/visconti/Makefile
> > > @@ -0,0 +1,8 @@
> > > +# SPDX-License-Identifier: GPL-2.0
> > > +#
> > > +# Makefile for the Visconti video input device driver #
> > > +
> > > +visconti-csi2rx-objs = csi2rx_drv.o
> > > +
> > > +obj-$(CONFIG_VIDEO_VISCONTI_CSI2RX) += visconti-csi2rx.o
> > > diff --git a/drivers/media/platform/toshiba/visconti/csi2rx_drv.c
> > > b/drivers/media/platform/toshiba/visconti/csi2rx_drv.c
> > > new file mode 100644
> > > index 000000000000..53d112432a86
> > > --- /dev/null
> > > +++ b/drivers/media/platform/toshiba/visconti/csi2rx_drv.c
> > > @@ -0,0 +1,954 @@
> > > +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
> > > +/*
> > > + * Toshiba Visconti Video Capture Support
> > > + *
> > > + * (C) Copyright 2025 TOSHIBA CORPORATION
> > > + * (C) Copyright 2025 Toshiba Electronic Devices & Storage
> > > +Corporation */
> > > +
> > > +#include <linux/clk.h>
> > > +#include <linux/debugfs.h>
> > > +#include <linux/delay.h>
> > > +#include <linux/device.h>
> > > +#include <linux/interrupt.h>
> > > +#include <linux/io.h>
> > > +#include <linux/module.h>
> > > +#include <linux/platform_device.h>
> > > +#include <linux/property.h>
> > > +#include <linux/reset.h>
> > > +#include <linux/seq_file.h>
> > > +
> > > +#include <media/mipi-csi2.h>
> > > +#include <media/v4l2-common.h>
> > > +#include <media/v4l2-ctrls.h>
> > > +#include <media/v4l2-fwnode.h>
> > > +#include <media/v4l2-subdev.h>
> > > +
> > > +/* CSI2HOST register space */
> > > +#define REG_CSI2RX_NLANES 0x4
> > > +#define REG_CSI2RX_RESETN 0x8
> > > +#define REG_CSI2RX_INT_ST_MAIN 0xc
> > > +#define REG_CSI2RX_DATA_IDS_1 0x10
> > > +#define REG_CSI2RX_DATA_IDS_2 0x14
> > > +#define REG_CSI2RX_PHY_SHUTDOWNZ 0x40
> > > +#define REG_CSI2RX_PHY_RSTZ 0x44
> > > +
> > > +/* Access to dphy external registers */ #define
> > > +REG_CSI2RX_PHY_TESTCTRL0 0x50
> > > +#define BIT_TESTCTRL0_CLK_0 0
> > > +#define BIT_TESTCTRL0_CLK_1 BIT(1)
> > > +
> > > +#define REG_CSI2RX_PHY_TESTCTRL1 0x54
> > > +#define BIT_TESTCTRL1_ADDR BIT(16)
> > > +#define MASK_TESTCTRL1_DOUT GENMASK(15, 8)
> > > +
> > > +#define REG_CSI2RX_INT_ST_PHY_FATAL 0xe0 #define
> > > +REG_CSI2RX_INT_MSK_PHY_FATAL 0xe4
> > > +#define MASK_PHY_FATAL_ALL 0x0000000f
> > > +
> > > +#define REG_CSI2RX_INT_ST_PKT_FATAL 0xf0 #define
> > > +REG_CSI2RX_INT_MSK_PKT_FATAL 0xf4
> > > +#define MASK_PKT_FATAL_ALL 0x0001000f
> > > +
> > > +#define REG_CSI2RX_INT_ST_FRAME_FATAL 0x100 #define
> > > +REG_CSI2RX_INT_MSK_FRAME_FATAL 0x104
> > > +#define MASK_FRAME_FATAL_ALL 0x000f0f0f
> > > +
> > > +#define REG_CSI2RX_INT_ST_PHY 0x110
> > > +#define REG_CSI2RX_INT_MSK_PHY 0x114
> > > +#define MASK_PHY_ERROR_ALL 0x000f000f
> > > +
> > > +#define REG_CSI2RX_INT_ST_PKT 0x120
> > > +#define REG_CSI2RX_INT_MSK_PKT 0x124
> > > +#define MASK_PKT_ERROR_ALL 0x000f000f
> > > +
> > > +#define REG_CSI2RX_INT_ST_LINE 0x130
> > > +#define REG_CSI2RX_INT_MSK_LINE 0x134
> > > +#define MASK_LINE_ERROR_ALL 0x00ff00ff
> >
> >
> > Look like it is dwc CSI2RX controller. Can we work out a common dwc CSI2RX
> > driver to avoid every duplicate the same code
> >
> > A attempt at
> > https://lore.kernel.org/imx/20250821-95_cam-v3-20-c9286fbb34b9@nxp.com
> > /
> >
> > The above is the base on stage's imx6. we try to find a path to workout a
> > common dwc csi2rx.
> >
> > Frank
> >
>
> Yes, it is DWC CSI2RX controller.
> It's an interesting idea to have a common dwc driver.
> Let me check if CSI2RX and PHY drivers work well with Visconti's hardware.
> Please let me know the base repository to apply patches.
It's base next-20250821.
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/tag/?h=next-20250821
Frank
>
> Regards,
> Yuji Ishikawa
>
^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2025-10-20 16:06 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-10-16 2:24 [PATCH v13 0/7] Add Toshiba Visconti Video Input Interface driver Yuji Ishikawa
2025-10-16 2:24 ` [PATCH v13 1/7] dt-bindings: media: platform: visconti: Add Toshiba Visconti MIPI CSI-2 Receiver Yuji Ishikawa
2025-10-16 4:34 ` Frank Li
2025-10-20 6:11 ` yuji2.ishikawa
2025-10-16 6:38 ` Krzysztof Kozlowski
2025-10-20 6:16 ` yuji2.ishikawa
2025-10-16 2:24 ` [PATCH v13 2/7] dt-bindings: media: platform: visconti: Add Toshiba Visconti Video Input Interface Yuji Ishikawa
2025-10-16 6:39 ` Krzysztof Kozlowski
2025-10-16 2:24 ` [PATCH v13 3/7] media: uapi: Add visconti viif meta buffer formats Yuji Ishikawa
2025-10-16 2:24 ` [PATCH v13 4/7] media: platform: visconti: Add Toshiba Visconti CSI-2 Receiver driver Yuji Ishikawa
2025-10-16 4:45 ` Frank Li
2025-10-20 6:13 ` yuji2.ishikawa
2025-10-20 16:05 ` Frank Li
2025-10-16 2:24 ` [PATCH v13 6/7] media: platform: visconti: Add streaming interface for ISP parameters and statistics Yuji Ishikawa
2025-10-16 2:24 ` [PATCH v13 7/7] documentation: media: Add documentation for Toshiba Visconti Video Input Interface driver Yuji Ishikawa
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox