* [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
* 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 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 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 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
* [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
* 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
* [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
* 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 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 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
* [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
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