From b8a65a3b2a487d95717212f23119dfab2786537b Mon Sep 17 00:00:00 2001 From: Julien Massot <julien.massot@collabora.com> Date: Wed, 3 May 2023 09:54:42 +0200 Subject: [PATCH] update gmsl patches --- debian/config/arm64/config | 3 + ...edia-platform-cadence-use-asd-fwnode.patch | 28 + ...-add-maxim-GMSL2-deserializer-driver.patch | 3802 +++++++++++++---- ...ax-gmsl2-deser-report-link-frequency.patch | 113 - ...do-not-try-to-link-non-existing-pipe.patch | 23 - ...ax-gmsl2-deser-fix-pattern-generator.patch | 169 - ...change-max96714-compatible-to-max967.patch | 64 - .../0168-Add-raw8-video-formats.patch | 85 + debian/patches/series | 7 +- 9 files changed, 3001 insertions(+), 1293 deletions(-) create mode 100644 debian/patches/apertis/am62x-csi/0160-media-platform-cadence-use-asd-fwnode.patch delete mode 100644 debian/patches/apertis/maxim-gmsl2/0151-max-gmsl2-deser-report-link-frequency.patch delete mode 100644 debian/patches/apertis/maxim-gmsl2/0152-max-gmsl2-deser-do-not-try-to-link-non-existing-pipe.patch delete mode 100644 debian/patches/apertis/maxim-gmsl2/0153-drivers-media-max-gmsl2-deser-fix-pattern-generator.patch delete mode 100644 debian/patches/apertis/maxim-gmsl2/0154-max-gmsl2-deser-change-max96714-compatible-to-max967.patch create mode 100644 debian/patches/apertis/st-vgxy61/0168-Add-raw8-video-formats.patch diff --git a/debian/config/arm64/config b/debian/config/arm64/config index 47b4d8decad..f1d31c25317 100644 --- a/debian/config/arm64/config +++ b/debian/config/arm64/config @@ -791,6 +791,9 @@ CONFIG_CEC_MESON_G12A_AO=m ## file: drivers/media/i2c/Kconfig ## CONFIG_VIDEO_MAXIM_GMSL2=m +CONFIG_VIDEO_MAXIM_GMSL2_DES=m +CONFIG_VIDEO_MAXIM_GMSL2_SER=m +CONFIG_VIDEO_ST_VGXY61=m ## ## file: drivers/media/platform/amlogic/meson-ge2d/Kconfig diff --git a/debian/patches/apertis/am62x-csi/0160-media-platform-cadence-use-asd-fwnode.patch b/debian/patches/apertis/am62x-csi/0160-media-platform-cadence-use-asd-fwnode.patch new file mode 100644 index 00000000000..1d63d525127 --- /dev/null +++ b/debian/patches/apertis/am62x-csi/0160-media-platform-cadence-use-asd-fwnode.patch @@ -0,0 +1,28 @@ +From f439f422a26dad42328d8cbfca5c7e37ac1968a3 Mon Sep 17 00:00:00 2001 +From: Julien Massot <julien.massot@collabora.com> +Date: Wed, 3 May 2023 09:28:14 +0200 +Subject: [PATCH 4/6] media: platform: cadence: use asd fwnode + +On some subdev the fwnode is the device node and not the endpoint node. +Using the subdev fwnode doesn't allow to fetch the subdev +pad we are connected to. +--- + drivers/media/platform/cadence/cdns-csi2rx.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c +index 1c7541a2b6..51aa68c7cf 100644 +--- a/drivers/media/platform/cadence/cdns-csi2rx.c ++++ b/drivers/media/platform/cadence/cdns-csi2rx.c +@@ -494,7 +494,7 @@ static int csi2rx_async_bound(struct v4l2_async_notifier *notifier, + struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev); + + csi2rx->source_pad = media_entity_get_fwnode_pad(&s_subdev->entity, +- s_subdev->fwnode, ++ asd->match.fwnode, + MEDIA_PAD_FL_SOURCE); + if (csi2rx->source_pad < 0) { + dev_err(csi2rx->dev, "Couldn't find output pad for subdev %s\n", +-- +2.40.1 + diff --git a/debian/patches/apertis/maxim-gmsl2/0001-drivers-media-add-maxim-GMSL2-deserializer-driver.patch b/debian/patches/apertis/maxim-gmsl2/0001-drivers-media-add-maxim-GMSL2-deserializer-driver.patch index 43ee7b473f3..d2c9509aaac 100644 --- a/debian/patches/apertis/maxim-gmsl2/0001-drivers-media-add-maxim-GMSL2-deserializer-driver.patch +++ b/debian/patches/apertis/maxim-gmsl2/0001-drivers-media-add-maxim-GMSL2-deserializer-driver.patch @@ -1,122 +1,473 @@ From: Julien Massot <julien.massot@collabora.com> -Date: Fri, 20 Jan 2023 10:51:34 +0100 -Subject: [PATCH] drivers: media: add maxim GMSL2 deserializer driver +Date: Thu, 30 Mar 2023 09:52:16 +0200 +Subject: drivers: media: add GMSL2 deserializer This driver support MAX96914, MAX96934 and MAX96714 deserializers that are quad, dual, and one GMSL2 link deserializer. -This driver has support for: +The deserializer driver has support for: Up to 2 4-lane CSI outputs, up to 8 video pipes, and up 4 GMSL2 Link. +Power over Coax, pattern generator, gpio controller as well as +GMSL1 backward compatibility. It also supports pattern generator as well as GMSL1 backward compatibility. - -Signed-off-by: Julien Massot <julien.massot@collabora.com> -[Martyn Welch: Ported to v6.1.4] -Signed-off-by: Martyn Welch <martyn.welch@collabora.com> --- - drivers/media/i2c/Kconfig | 12 + - drivers/media/i2c/Makefile | 2 + - drivers/media/i2c/max-gmsl2-deser.c | 1592 ++++++++++++++++++++++++++++++++++ - drivers/media/i2c/max-gmsl2-patgen.c | 192 ++++ - drivers/media/i2c/max-gmsl2-patgen.h | 23 + - 5 files changed, 1821 insertions(+) + .../bindings/media/i2c/maxim,max96914.yaml | 250 ++++ + drivers/media/i2c/Kconfig | 42 + + drivers/media/i2c/Makefile | 5 + + drivers/media/i2c/gmsl2-def.h | 153 ++ + drivers/media/i2c/max-gmsl2-deser.c | 1463 ++++++++++++++++++++ + drivers/media/i2c/max-gmsl2-gpio.c | 207 +++ + drivers/media/i2c/max-gmsl2-patgen.c | 175 +++ + drivers/media/i2c/max-gmsl2-ser.c | 992 +++++++++++++ + drivers/media/i2c/max-gmsl2.c | 394 ++++++ + include/linux/gpio/max-gmsl2-gpio.h | 16 + + include/media/gmsl2/max-gmsl2-patgen.h | 45 + + 11 files changed, 3742 insertions(+) + create mode 100644 Documentation/devicetree/bindings/media/i2c/maxim,max96914.yaml + create mode 100644 drivers/media/i2c/gmsl2-def.h create mode 100644 drivers/media/i2c/max-gmsl2-deser.c + create mode 100644 drivers/media/i2c/max-gmsl2-gpio.c create mode 100644 drivers/media/i2c/max-gmsl2-patgen.c - create mode 100644 drivers/media/i2c/max-gmsl2-patgen.h + create mode 100644 drivers/media/i2c/max-gmsl2-ser.c + create mode 100644 drivers/media/i2c/max-gmsl2.c + create mode 100644 include/linux/gpio/max-gmsl2-gpio.h + create mode 100644 include/media/gmsl2/max-gmsl2-patgen.h +diff --git a/Documentation/devicetree/bindings/media/i2c/maxim,max96914.yaml b/Documentation/devicetree/bindings/media/i2c/maxim,max96914.yaml +new file mode 100644 +index 00000000..b9061d5 +--- /dev/null ++++ b/Documentation/devicetree/bindings/media/i2c/maxim,max96914.yaml +@@ -0,0 +1,250 @@ ++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/media/i2c/max,max96914.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Maxim Integrated MAX96xxx Family GMSL2 Deserializer Hubs ++ ++maintainers: ++ - Julien Massot <julien.massot@collabora.com> ++ ++description: ++ The Maxim MAX96xxx devices are GMSL2 video deserializer with I2C and GPIO ++ forwarding. ++ ++properties: ++ compatible: ++ enum: ++ - maxim,max96934 ++ reg: ++ maxItems: 1 ++ ++ enable-gpios: ++ maxItems: 1 ++ description: ++ Specifier for the GPIO connected to the PWDNB pin. ++ ++ gpio-controller: true ++ ++ '#gpio-cells': ++ const: 2 ++ ++ maxim,pipe-stream-id: ++ $ref: /schemas/types.yaml#/definitions/uint8-array ++ minItems: 1 ++ maxItems: 8 ++ items: ++ # Possible stream id [0,1,2,3] ++ maximum: 3 ++ description: ++ An array of stream identifier. Position of an entry determines ++ the pipe number, while the value of an entry indicates the given ++ stream identifier, e.g. for 8-pipes device we could have ++ "maxim,pipe-stream-id = <3 2 1 0 3 2 1 0>;" overriding ++ the default <0 1 2 3 0 1 2 3>. ++ Stream id will be used to select a particular stream for a video pipe. ++ ++ ports: ++ $ref: /schemas/graph.yaml#/properties/ports ++ ++ properties: ++ port@0: ++ $ref: /schemas/graph.yaml#/$defs/port-base ++ unevaluatedProperties: false ++ description: GMSL-Link input 0 ++ ++ properties: ++ endpoint: ++ $ref: /schemas/media/video-interfaces.yaml# ++ unevaluatedProperties: false ++ description: ++ Endpoint for GMSL-Link port. ++ ++ port@1: ++ $ref: /schemas/graph.yaml#/$defs/port-base ++ unevaluatedProperties: false ++ description: GMSL-Link input 1 ++ ++ properties: ++ endpoint: ++ $ref: /schemas/media/video-interfaces.yaml# ++ unevaluatedProperties: false ++ description: ++ Endpoint for GMSL-Link port. ++ ++ port@2: ++ $ref: /schemas/graph.yaml#/$defs/port-base ++ unevaluatedProperties: false ++ description: GMSL-Link input 2 ++ ++ properties: ++ endpoint: ++ $ref: /schemas/media/video-interfaces.yaml# ++ unevaluatedProperties: false ++ description: ++ Endpoint for GMSL-Link port. ++ ++ port@3: ++ $ref: /schemas/graph.yaml#/$defs/port-base ++ unevaluatedProperties: false ++ description: GMSL-Link input 3 ++ ++ properties: ++ endpoint: ++ $ref: /schemas/media/video-interfaces.yaml# ++ unevaluatedProperties: false ++ description: ++ Endpoint for GMSL-Link port. ++ ++ port@4: ++ $ref: /schemas/graph.yaml#/$defs/port-base ++ unevaluatedProperties: false ++ description: CSI-2 Output 0 ++ ++ properties: ++ endpoint: ++ $ref: /schemas/media/video-interfaces.yaml# ++ unevaluatedProperties: false ++ ++ properties: ++ data-lanes: ++ minItems: 1 ++ maxItems: 4 ++ link-frequencies: ++ maxItems: 1 ++ ++ required: ++ - data-lanes ++ - link-frequencies ++ ++ port@5: ++ $ref: /schemas/graph.yaml#/$defs/port-base ++ unevaluatedProperties: false ++ description: CSI-2 Output 1 ++ ++ properties: ++ endpoint: ++ $ref: /schemas/media/video-interfaces.yaml# ++ unevaluatedProperties: false ++ ++ properties: ++ data-lanes: ++ minItems: 1 ++ maxItems: 4 ++ link-frequencies: ++ maxItems: 1 ++ ++ required: ++ - data-lanes ++ - link-frequencies ++ ++ required: ++ - port@0 ++ - port@1 ++ - port@2 ++ - port@3 ++ - port@4 ++ - port@5 ++ ++patternProperties: ++ "^port[0-3]-poc-supply$": ++ description: Regulator providing Power over Coax for a particular port ++ ++required: ++ - compatible ++ - reg ++ - ports ++ ++additionalProperties: false ++ ++examples: ++ - | ++ #include <dt-bindings/gpio/gpio.h> ++ ++ i2c { ++ clock-frequency = <400000>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ deser@68 { ++ compatible = "maxim,max96914"; ++ reg = <0x68>; ++ ++ enable-gpios = <&tlmm 7 GPIO_ACTIVE_HIGH>; ++ port0-poc-supply = <&camera_poc_12v>; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ /* Port A, Camera 0 */ ++ port@0 { ++ reg = <0>; ++ ++ max96914_gmsl_a_in: endpoint { ++ remote-endpoint = <&max96971_gmsl_out>; ++ }; ++ }; ++ ++ /* Port B, unconnected */ ++ port@1 { ++ reg = <1>; ++ }; ++ ++ /* Port C, unconnected */ ++ port@2 { ++ reg = <2>; ++ }; ++ ++ /* Port D, unconnected */ ++ port@3 { ++ reg = <3>; ++ }; ++ ++ /* Port 4, CSI-2 TX */ ++ port@4 { ++ reg = <4>; ++ max96914_0_csi_out: endpoint { ++ data-lanes = <1 2 3 4>; ++ link-frequencies = /bits/ 64 <800000000>; ++ remote-endpoint = <&csi2_phy0>; ++ }; ++ }; ++ ++ /* Port 5, unconnected */ ++ port@5 { ++ reg = <5>; ++ }; ++ }; ++ ++ i2c-gate { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ serializer1: serializer@40 { ++ compatible = "maxim,max96971f"; ++ reg = <0x40>; ++ gpio-controller; ++ #gpio-cells = <2>; ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ port@0 { ++ reg = <0>; ++ max96971_csi_in: endpoint { ++ }; ++ }; ++ ++ port@1 { ++ reg = <1>; ++ max96971_gmsl_out: endpoint { ++ remote-endpoint = <&max96914_gmsl_a_in>; ++ }; ++ }; ++ }; /* End of serializer ports*/ ++ }; /* End of serializer */ ++ }; /* End of I2C gate*/ ++ }; /* End of deserializer */ ++ }; /* End of I2C */ ++... diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig -index 7dce81b..a35b2e4 100644 +index 7dce81b..8223b3e 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig -@@ -1225,6 +1225,18 @@ config VIDEO_MAX9286 +@@ -1225,6 +1225,48 @@ config VIDEO_MAX9286 To compile this driver as a module, choose M here: the module will be called max9286. +config VIDEO_MAXIM_GMSL2 -+ tristate "Maxim GMSL2 Serializer " -+ depends on I2C ++ tristate "Maxim GMSL2 Deserializer" ++ depends on OF ++ select V4L2_FWNODE ++ select REGMAP ++ help ++ Say Y here if you want support for Maxim GMSL2 ++ deserializer. ++ ++config VIDEO_MAXIM_GMSL2_DES ++ tristate "Maxim GMSL2 Deserializer" + depends on OF + select V4L2_FWNODE + select VIDEO_V4L2_SUBDEV_API + select MEDIA_CONTROLLER + select REGMAP_I2C ++ select VIDEO_MAXIM_GMSL2_GPIO ++ select VIDEO_MAXIM_GMSL2 + help + Say Y here if you want support for Maxim GMSL2 + deserializer. ++ ++config VIDEO_MAXIM_GMSL2_SER ++ tristate "Maxim GMSL2 Seserializer" ++ depends on OF ++ select V4L2_FWNODE ++ select VIDEO_V4L2_SUBDEV_API ++ select MEDIA_CONTROLLER ++ select REGMAP_I2C ++ select VIDEO_MAXIM_GMSL2_GPIO ++ select VIDEO_MAXIM_GMSL2 ++ help ++ Say Y here if you want support for Maxim GMSL2 ++ serializer. ++ ++config VIDEO_MAXIM_GMSL2_GPIO ++ tristate "Maxim GMSL2 GPIO controller" ++ depends on OF ++ select REGMAP ++ select OF_GPIO ++ + config VIDEO_ML86V7667 tristate "OKI ML86V7667 video decoder" depends on VIDEO_DEV && I2C diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile -index 0a29331..6e3ed6f 100644 +index 0a29331..af6097a 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile -@@ -57,6 +57,8 @@ obj-$(CONFIG_VIDEO_M52790) += m52790.o - obj-$(CONFIG_VIDEO_M5MOLS) += m5mols/ - obj-$(CONFIG_VIDEO_MAX9271_LIB) += max9271.o - obj-$(CONFIG_VIDEO_MAX9286) += max9286.o -+max-gmsl2-deser-driver-objs := max-gmsl2-deser.o max-gmsl2-patgen.o -+obj-$(CONFIG_VIDEO_MAXIM_GMSL2) += max-gmsl2-deser-driver.o - obj-$(CONFIG_VIDEO_ML86V7667) += ml86v7667.o - obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o - obj-$(CONFIG_VIDEO_MT9M001) += mt9m001.o -diff --git a/drivers/media/i2c/max-gmsl2-deser.c b/drivers/media/i2c/max-gmsl2-deser.c +@@ -142,3 +142,8 @@ obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o + obj-$(CONFIG_VIDEO_VS6624) += vs6624.o + obj-$(CONFIG_VIDEO_WM8739) += wm8739.o + obj-$(CONFIG_VIDEO_WM8775) += wm8775.o ++max-gmsl2-lib-objs := max-gmsl2.o max-gmsl2-patgen.o ++obj-$(CONFIG_VIDEO_MAXIM_GMSL2) += max-gmsl2-lib.o ++obj-$(CONFIG_VIDEO_MAXIM_GMSL2_DES) += max-gmsl2-deser.o ++obj-$(CONFIG_VIDEO_MAXIM_GMSL2_SER) += max-gmsl2-ser.o ++obj-$(CONFIG_VIDEO_MAXIM_GMSL2_GPIO) += max-gmsl2-gpio.o +diff --git a/drivers/media/i2c/gmsl2-def.h b/drivers/media/i2c/gmsl2-def.h new file mode 100644 -index 0000000..8a30110 +index 00000000..d67bab3 --- /dev/null -+++ b/drivers/media/i2c/max-gmsl2-deser.c -@@ -0,0 +1,1592 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * Maxim GMSL2 Deserializer Driver -+ * -+ * Copyright (C) 2022 Collabora Ltd. -+ */ -+ -+#define GMSL2_MAX_LINK 4 -+#define GMSL2_MAX_PIPE 8 -+#define GMSL2_MAX_CSI 2 -+ -+#include <linux/debugfs.h> -+#include <linux/gpio/consumer.h> -+#include <linux/i2c.h> -+#include <linux/mod_devicetable.h> -+#include <linux/module.h> -+#include <linux/of_device.h> ++++ b/drivers/media/i2c/gmsl2-def.h +@@ -0,0 +1,153 @@ ++#include <linux/device.h> +#include <linux/regmap.h> -+#include <linux/v4l2-dv-timings.h> +#include <media/v4l2-ctrls.h> -+#include <media/v4l2-device.h> -+#include <media/v4l2-fwnode.h> +#include <media/v4l2-subdev.h> ++#ifndef __LINUX_MEDIA_MAX_GMSL2_DEFS_H ++#define __LINUX_MEDIA_MAX_GMSL2_DEFS_H + -+#include "max-gmsl2-patgen.h" ++static struct regmap_config gmsl2_regmap_config = { ++ .reg_bits = 16, ++ .val_bits = 8, ++ .max_register = 0xFFFF, ++ .cache_type = REGCACHE_NONE, ++}; + -+struct max_gmsl2_compat_gmsl1 { -+ struct reg_field gmsl2_link_mode; -+ struct reg_field highimm[GMSL2_MAX_LINK]; -+ struct reg_field link_lock[GMSL2_MAX_LINK]; -+ struct reg_field forward_cc[GMSL2_MAX_LINK]; -+ u16 conv_datatype[GMSL2_MAX_LINK]; ++#define MAX9295A_DEVICE_ID 0x91 ++#define MAX96934_DEVICE_ID 0x92 ++#define MAX96914_DEVICE_ID 0xa0 ++#define MAX96724_DEVICE_ID 0xa2 ++#define MAX96717F_DEVICE_ID 0xc8 ++#define MAX96714F_DEVICE_ID 0xca ++ ++static inline const char* gmsl2_id_to_name(u8 device_id) ++{ ++ switch (device_id) { ++ case MAX9295A_DEVICE_ID: return "max9295a"; ++ case MAX96934_DEVICE_ID: return "max96934"; ++ case MAX96724_DEVICE_ID: return "max96724"; ++ case MAX96714F_DEVICE_ID: return "max96714f"; ++ case MAX96717F_DEVICE_ID: return "max96717f"; ++ default: ++ break; ++ } ++ return NULL; ++} ++ ++struct max_gmsl2_pipe { ++ struct regmap_field *enable; ++ struct regmap_field *link_sel; ++ struct regmap_field *stream_id; ++ struct regmap_field *vid_lock; ++ struct regmap_field *override_bpp_vc_dt; ++ struct regmap_field *bpp_l; ++ struct regmap_field *bpp_h; ++ struct regmap_field *dt_l; ++ struct regmap_field *dt_h; ++ struct regmap_field *dt_sel[2]; ++ struct regmap_field *vc; ++ u8 pipe_remap_idx; ++}; ++ ++struct max_gmsl2_csi { ++ struct v4l2_subdev sd; ++ struct media_pad pads[2]; ++ struct v4l2_ctrl_handler ctrl_hdl; ++ struct v4l2_ctrl *pixel_rate; ++ void *priv; ++ ++ struct regmap_field *phy_enable; ++ ++ /* CSI Tx Only */ ++ struct regmap_field *dpll_freq; ++ ++ struct regmap_field *lane_cnt; ++ struct regmap_field *lane_polarities[2]; ++ struct regmap_field *lane_map[2]; ++ struct fwnode_handle *ep_fwnode; ++ u8 id; +}; + -+struct max_gmsl2_deser_data { ++struct max_gmsl2_link { ++ struct regmap_field *locked; ++ struct regmap_field *reset_one_shot; ++ struct regmap_field *reset; ++ ++ /* deserializer only */ ++ struct v4l2_subdev *source_sd; /* Connected subdev */ ++ u16 source_sd_pad; ++ struct fwnode_handle *source_ep_fwnode; ++ struct v4l2_async_subdev asd; ++ struct regulator *vpoc; ++}; ++ ++#define GMSL2_MAX_LINK 4 ++#define GMSL2_MAX_PIPE 8 ++#define GMSL2_MAX_CSI 2 ++ ++struct max_gmsl2_data { + u8 device_id; + u8 links; + u8 pipes; + u8 csi; ++ u8 ngpios; ++ u16 gpio_base; + struct reg_field dev_rev; -+ struct reg_field csi_enable; ++ struct reg_field csi_force_enable; + struct reg_field csi_wait_frame; + struct reg_field csi_phy_enable[GMSL2_MAX_CSI]; + /* Each CSI 4 lanes use 2 phys for lane polarities and lane map + * declare the phy which provides D0-D1 first. + */ ++ struct reg_field csi_lane_cnt[GMSL2_MAX_CSI]; + struct reg_field csi_lane_polarities[GMSL2_MAX_CSI * 2]; + struct reg_field csi_lane_map[GMSL2_MAX_CSI * 2]; -+ struct reg_field csi_phy_out_freq[GMSL2_MAX_CSI * 2]; ++ struct reg_field csi_dpll_freq[GMSL2_MAX_CSI * 2]; + struct reg_field pattern_clk_freq; + u16 patgen_base_addr[2]; + struct reg_field pipe_enable[GMSL2_MAX_PIPE]; @@ -129,6 +480,7 @@ index 0000000..8a30110 + struct reg_field pipe_vc[GMSL2_MAX_PIPE]; + struct reg_field pipe_dt_l[GMSL2_MAX_PIPE]; + struct reg_field pipe_dt_h[GMSL2_MAX_PIPE]; ++ struct reg_field pipe_dt_sel[GMSL2_MAX_PIPE]; + struct reg_field pipe_vid_lock[GMSL2_MAX_PIPE]; + u8 pipe_remap_regs; + u16 pipe_remap_base_addr[GMSL2_MAX_PIPE]; @@ -137,83 +489,127 @@ index 0000000..8a30110 + struct reg_field reset_link[GMSL2_MAX_LINK]; + /* GMSL1 backward compatibility */ + const struct max_gmsl2_compat_gmsl1 *gmsl1; ++ struct reg_field tunnel_mode; +}; + -+/* VC[7:6] DT[5:0] */ -+#define PIPE_ELEMENT(vc, dt) ( vc << 6 | dt ) ++int max_gmsl2_csi_get_link_freq(struct max_gmsl2_csi *csi, s64 *link_freq); ++int max_gmsl2_csi_phy_init(struct max_gmsl2_csi *csi); ++int max_gmsl2_init_pipes_stream_id(struct max_gmsl2_pipe *pipes, u8 npipes, ++ struct device *dev); ++void max_gmsl2_pipe_init_regfield(struct device *dev, struct regmap *regmap, ++ struct max_gmsl2_pipe *pipe, ++ const struct max_gmsl2_data *data, ++ u8 id); ++void max_gmsl2_csi_init_regfield(struct device *dev, struct regmap *regmap, ++ struct max_gmsl2_csi *csi, ++ const struct max_gmsl2_data *data, ++ u8 id); ++void max_gmsl2_link_init_regfield(struct device *dev, ++ struct regmap *regmap, ++ struct max_gmsl2_link *link, ++ const struct max_gmsl2_data *data, u8 id); ++void max_gmsl2_state_show_pipe(struct device *dev, struct max_gmsl2_pipe *pipe, ++ const struct max_gmsl2_data *data, u8 pipeid); + -+struct max_gmsl2_deser_pipe { -+ struct regmap_field *enable; -+ struct regmap_field *link_sel; -+ struct regmap_field *stream_id; -+ struct regmap_field *vid_lock; -+ struct regmap_field *override_bpp_vc_dt; -+ struct regmap_field *bpp_l; -+ struct regmap_field *bpp_h; -+ struct regmap_field *dt_l; -+ struct regmap_field *dt_h; -+ struct regmap_field *vc; -+ struct v4l2_subdev sd; -+ struct media_pad pad; -+ u8 pipe_remap_idx; -+}; -+#define max_gmsl2_deser_sd_to_pipe(sd) \ -+ container_of(sd, struct max_gmsl2_deser_pipe, sd) ++/* Some registers such as bpp and dt maybe split on two regmap_field */ ++int max_gmsl2_write_h_l_val(struct regmap_field *h, struct regmap_field *l, ++ const struct reg_field *l_field, unsigned int val); ++int max_gmsl2_read_h_l_val(struct regmap_field *h, struct regmap_field *l, ++ const struct reg_field *l_field, unsigned int *val); ++#endif +diff --git a/drivers/media/i2c/max-gmsl2-deser.c b/drivers/media/i2c/max-gmsl2-deser.c +new file mode 100644 +index 00000000..de85183 +--- /dev/null ++++ b/drivers/media/i2c/max-gmsl2-deser.c +@@ -0,0 +1,1463 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Maxim GMSL2 Deserializer Driver ++ * ++ * Copyright (C) 2022 Collabora Ltd. ++ */ + -+struct max_gmsl2_deser_csi { -+ struct v4l2_subdev sd; -+ struct media_pad pads[2]; -+ struct v4l2_ctrl_handler ctrl_hdl; -+ struct v4l2_ctrl *pixel_rate; -+ struct v4l2_subdev_format format; -+ struct max_gmsl2_deser_priv *priv; -+ struct regmap_field *phy_enable; -+ struct regmap_field *phy_out_freq[2]; -+ struct regmap_field *lane_polarities[2]; -+ struct regmap_field *lane_map[2]; -+ u8 id; ++#include <linux/gpio/consumer.h> ++#include <linux/gpio/max-gmsl2-gpio.h> ++#include <linux/i2c.h> ++#include <linux/i2c-mux.h> ++#include <linux/mod_devicetable.h> ++#include <linux/module.h> ++#include <linux/of_device.h> ++#include <linux/regmap.h> ++#include <linux/v4l2-dv-timings.h> ++#include <media/v4l2-ctrls.h> ++#include <media/v4l2-device.h> ++#include <media/v4l2-fwnode.h> ++#include <media/v4l2-subdev.h> ++#include <media/mipi-csi2.h> ++ ++#include "gmsl2-def.h" ++#include <media/gmsl2/max-gmsl2-patgen.h> ++ ++struct max_gmsl2_compat_gmsl1 { ++ struct reg_field gmsl2_link_mode; ++ struct reg_field highimm[GMSL2_MAX_LINK]; ++ struct reg_field link_lock[GMSL2_MAX_LINK]; ++ struct reg_field forward_cc[GMSL2_MAX_LINK]; ++ struct reg_field shift_vid_hvd[GMSL2_MAX_LINK]; ++ u16 conv_datatype[GMSL2_MAX_LINK]; +}; + -+#define max_gmsl2_deser_sd_to_csi(sd) \ -+ container_of(sd, struct max_gmsl2_deser_csi, sd) ++/* VC[7:6] DT[5:0] */ ++#define PIPE_ELEMENT(vc, dt) ( vc << 6 | dt ) + -+struct max_gmsl2_deser_priv { ++#define GMSL2_MAX_NPORTS (GMSL2_MAX_LINK + GMSL2_MAX_CSI) ++ ++struct max_gmsl2_deser { + struct i2c_client *client; + struct regmap *regmap; + struct gpio_desc *gpiod_pwdn; + struct v4l2_ctrl_handler ctrls; -+ struct v4l2_ctrl *pixelrate; -+ struct max_gmsl2_deser_pipe pipes[GMSL2_MAX_PIPE]; -+ struct max_gmsl2_deser_csi csi[GMSL2_MAX_CSI]; -+ const struct max_gmsl2_deser_data *data; ++ struct max_gmsl2_pipe pipes[GMSL2_MAX_PIPE]; ++ struct max_gmsl2_csi csi[GMSL2_MAX_CSI]; ++ struct max_gmsl2_link links[GMSL2_MAX_LINK]; ++ struct max_gmsl2_patgen patgen; ++ struct v4l2_subdev_format format; ++ struct i2c_mux_core *mux; ++ struct gpio_chip gpio; ++ const struct max_gmsl2_data *data; + struct regmap_field *dev_rev; + u8 csi_use_cnt; -+ struct regmap_field *csi_enable; ++ struct regmap_field *csi_force_enable; + /* wait a new frame before generating a MIPI packet */ + struct regmap_field *csi_wait_frame; -+ struct regmap_field *pattern_clk_freq; -+ struct regmap_field *link_locked[GMSL2_MAX_LINK]; -+ struct regmap_field *reset_one_shot_link[GMSL2_MAX_LINK]; -+ struct regmap_field *reset_link[GMSL2_MAX_LINK]; + struct regmap_field *gmsl2_link_mode; + struct regmap_field *gmsl1_highimm[GMSL2_MAX_LINK]; + struct regmap_field *gmsl1_link_locked[GMSL2_MAX_LINK]; + struct regmap_field *gmsl1_forward_cc[GMSL2_MAX_LINK]; -+ struct dentry *dbg_dir; ++ struct regmap_field *gmsl1_shift_vid_hvd[GMSL2_MAX_LINK]; ++ struct v4l2_subdev sd; ++ struct media_pad pads[GMSL2_MAX_NPORTS]; ++ struct v4l2_ctrl_handler ctrl_handler; ++ struct v4l2_async_notifier notifier; ++ s64 tx_link_freq[1]; ++ struct regmap_field *tunnel_mode; ++ bool tunnel_mode_saved; +}; +#define GMSL2_INVALID_REG_FIELD REG_FIELD(0, 0, 0) + -+#define MAX96934_DEVICE_ID 0x92 -+const struct max_gmsl2_deser_data max96934_data = { ++#define sd_to_max_gmsl2_deser(sd) \ ++ container_of(sd, struct max_gmsl2_deser, sd) ++ ++static const struct max_gmsl2_data max96934_data = { + .device_id = MAX96934_DEVICE_ID, + .pipes = 4, + .links = 2, + .csi = 2, ++ .ngpios = 20, ++ .gpio_base = 0x2b0, + .dev_rev = REG_FIELD(0xe, 0, 3), -+ .csi_enable = REG_FIELD(0x313, 1, 1), ++ .csi_lane_cnt = {REG_FIELD(0x44a, 6, 7), REG_FIELD(0x48a, 6, 7)}, + .csi_wait_frame = REG_FIELD(0x325, 7, 7), + .csi_phy_enable = { REG_FIELD(0x332, 4, 5), REG_FIELD(0x332, 6, 7) }, -+ .csi_phy_out_freq = { REG_FIELD(0x31d, 0, 4), REG_FIELD(0x320, 0, 4), ++ .csi_dpll_freq = { REG_FIELD(0x31d, 0, 4), REG_FIELD(0x320, 0, 4), + REG_FIELD(0x323, 0, 4), REG_FIELD(0x326, 0, 4)}, + + /* phy1, phy0, phy2, phy3 in this order, because phy1 and phy2 are used @@ -257,7 +653,7 @@ index 0000000..8a30110 +}; +#define IS_MAX96934(priv) (priv->data->device_id == MAX96934_DEVICE_ID) + -+const struct max_gmsl2_compat_gmsl1 max96714_gmsl1_data = { ++static const struct max_gmsl2_compat_gmsl1 max96714f_gmsl1_data = { + .gmsl2_link_mode = REG_FIELD(0x6, 6, 6), + .highimm = { REG_FIELD(0xb06, 7, 7) }, + /* FIXME conv datatype register is a bit different @@ -267,19 +663,23 @@ index 0000000..8a30110 + */ + .conv_datatype = {0xb96 }, + .link_lock = { REG_FIELD(0xbcb, 0, 0) }, ++ .shift_vid_hvd = { REG_FIELD(0xba7, 6, 6) }, +}; + -+#define MAX96714_DEVICE_ID 0xc9 -+const struct max_gmsl2_deser_data max96714_data = { -+ .device_id = MAX96714_DEVICE_ID, ++static const struct max_gmsl2_data max96714f_data = { ++ .device_id = MAX96714F_DEVICE_ID, + .pipes = 1, + .links = 1, + .csi = 1, ++ .ngpios = 9, ++ .gpio_base = 0x2b0, + .dev_rev = REG_FIELD(0xe, 0, 3), -+ .csi_enable = REG_FIELD(0x313, 1, 1), ++ .csi_lane_cnt = { REG_FIELD(0x44a, 6, 7) }, ++ .csi_force_enable = REG_FIELD(0x330, 7, 7), + .csi_wait_frame = REG_FIELD(0x325, 7, 7), + .csi_phy_enable = { REG_FIELD(0x332, 4, 5) }, -+ .csi_phy_out_freq = { GMSL2_INVALID_REG_FIELD, REG_FIELD(0x320, 0, 4), }, ++ /* MAX96714 Can't do 2x2 MiPi, so DPHY0 freq can't be set */ ++ .csi_dpll_freq = { GMSL2_INVALID_REG_FIELD, REG_FIELD(0x320, 0, 4), }, + .csi_lane_polarities = { REG_FIELD(0x335, 0, 2), REG_FIELD(0x335, 3, 5) }, + .csi_lane_map = { REG_FIELD(0x333, 0, 3), REG_FIELD(0x333, 4, 7) }, + .pattern_clk_freq = REG_FIELD(0x38, 0, 1), @@ -300,15 +700,13 @@ index 0000000..8a30110 + .reset_one_shot_link = { REG_FIELD(0x10, 5, 5) }, + .reset_link = { REG_FIELD(0x10, 6, 6) }, + /* This device is backward compatible with GMSL1 */ -+ .gmsl1 = &max96714_gmsl1_data, ++ .gmsl1 = &max96714f_gmsl1_data, ++ .tunnel_mode = REG_FIELD(0x474, 0, 0), + +}; -+#define IS_MAX96714(priv) (priv->data->device_id == MAX96714_DEVICE_ID) ++#define IS_MAX96714F(priv) (priv->data->device_id == MAX96714F_DEVICE_ID) + -+ -+#define MAX96914_DEVICE_ID 0xa0 -+ -+const struct max_gmsl2_compat_gmsl1 max96914_gmsl1_data = { ++static const struct max_gmsl2_compat_gmsl1 max96914_gmsl1_data = { + .gmsl2_link_mode = REG_FIELD(0x6, 4, 7), + .forward_cc = {REG_FIELD(0xb04, 0, 0), REG_FIELD(0xc04, 0, 0), + REG_FIELD(0xd04, 0, 0), REG_FIELD(0xe04, 0, 0) }, @@ -316,20 +714,25 @@ index 0000000..8a30110 + REG_FIELD(0xd06, 7, 7), REG_FIELD(0xe06, 7, 7) }, + .conv_datatype = {0xb96, 0xc96, 0xd96, 0xe96}, + .link_lock = { REG_FIELD(0xbcb, 0, 0), REG_FIELD(0xccb, 0, 0), -+ REG_FIELD(0xdcb, 0, 0), REG_FIELD(0xecb, 0, 0) } ++ REG_FIELD(0xdcb, 0, 0), REG_FIELD(0xecb, 0, 0) }, ++ .shift_vid_hvd = { REG_FIELD(0xba7, 6, 6), REG_FIELD(0xca7, 6, 6), ++ REG_FIELD(0xda7, 6, 6), REG_FIELD(0xea7, 7, 7) }, +}; + -+const struct max_gmsl2_deser_data max96914_data = { -+ .device_id = MAX96914_DEVICE_ID, ++static const struct max_gmsl2_data max96914_data = { ++ .device_id = MAX96724_DEVICE_ID, + .pipes = 8, + .links = 4, + .csi = 2, ++ .ngpios = 17, ++ .gpio_base = 0x300, + .dev_rev = REG_FIELD(0x4c, 0, 3), -+ .csi_enable = REG_FIELD(0x8a0,7, 7), ++ .csi_lane_cnt = {REG_FIELD(0x94a, 6, 7), REG_FIELD(0x98a, 6, 7)}, ++ .csi_force_enable = REG_FIELD(0x8a0,7, 7), + .csi_wait_frame = GMSL2_INVALID_REG_FIELD, /*FIXME */ + + .csi_phy_enable = { REG_FIELD(0x8a2, 4, 5), REG_FIELD(0x8a2, 6, 7) }, -+ .csi_phy_out_freq = { REG_FIELD(0x415, 0, 4), REG_FIELD(0x418, 0, 4), ++ .csi_dpll_freq = { REG_FIELD(0x415, 0, 4), REG_FIELD(0x418, 0, 4), + REG_FIELD(0x41b, 0, 4), REG_FIELD(0x41e, 0, 4)}, + .csi_lane_polarities = { REG_FIELD(0x8a5, 0, 2), REG_FIELD(0x8a5, 3, 5), + REG_FIELD(0x8a6, 0, 2), REG_FIELD(0x8a6, 3, 5) }, @@ -419,14 +822,83 @@ index 0000000..8a30110 +}; +#define IS_MAX96914(priv) (priv->data->device_id == MAX96914_DEVICE_ID) + -+#define GMSL2_MIPI_DT_RGB888 0x24 -+ -+static struct regmap_config max_gmsl2_deser_regmap_config = { -+ .reg_bits = 16, -+ .val_bits = 8, -+ .max_register = 0xFFFF, -+ .cache_type = REGCACHE_NONE, ++static const struct max_gmsl2_data max96724_data = { ++ .device_id = MAX96724_DEVICE_ID, ++ .pipes = 4, ++ .csi = 2, ++ .links = 4, ++ .ngpios = 11, ++ .gpio_base = 0x300, ++ .dev_rev = REG_FIELD(0x4c, 0, 3), ++ .csi_lane_cnt = { REG_FIELD(0x90a, 6, 7), REG_FIELD(0x94a, 6, 7) }, ++ .csi_force_enable = REG_FIELD(0x8a0,7, 7), ++ .csi_phy_enable = { REG_FIELD(0x8a2, 4, 5), REG_FIELD(0x8a2, 6, 7) }, ++ .csi_dpll_freq = { REG_FIELD(0x415, 0, 4), REG_FIELD(0x418, 0, 4), ++ REG_FIELD(0x41b, 0, 4), REG_FIELD(0x41e, 0, 4)}, ++ .csi_lane_polarities = { REG_FIELD(0x8a5, 0, 2), REG_FIELD(0x8a5, 3, 5), ++ REG_FIELD(0x8a6, 0, 2), REG_FIELD(0x8a6, 3, 5) }, ++ .csi_lane_map = { REG_FIELD(0x8a3, 0, 3), REG_FIELD(0x8a3, 4, 7), ++ REG_FIELD(0x8a4, 0, 3), REG_FIELD(0x8a4, 4, 7) }, ++ .patgen_base_addr = {0x1050}, ++ .pipe_enable = { ++ REG_FIELD(0xf4, 0, 0), REG_FIELD(0xf4, 1, 1), ++ REG_FIELD(0xf4, 2, 2), REG_FIELD(0xf4, 3, 3), ++ }, ++ .pipe_link_sel = { ++ REG_FIELD(0xf0, 2, 3), REG_FIELD(0xf0, 6, 7), ++ REG_FIELD(0xf1, 2, 3), REG_FIELD(0xf1, 6, 7), ++ }, ++ .pipe_stream_id = { ++ REG_FIELD(0xf0, 0, 1), REG_FIELD(0xf0, 4, 5), ++ REG_FIELD(0xf1, 0, 1), REG_FIELD(0xf1, 4, 5), ++ }, ++ .pipe_override_bpp_vc_dt = { ++ REG_FIELD(0x415, 6, 6), REG_FIELD(0x415, 7, 7), ++ REG_FIELD(0x418, 6, 6), REG_FIELD(0x418, 7, 7), ++ }, ++ .pipe_bpp_l = { ++ REG_FIELD(0x40b, 3, 7), REG_FIELD(0x411, 0, 4), ++ REG_FIELD(0x412, 0, 1), REG_FIELD(0x412, 2, 6), ++ }, ++ .pipe_bpp_h = { ++ GMSL2_INVALID_REG_FIELD, GMSL2_INVALID_REG_FIELD, ++ REG_FIELD(0x411, 5, 7), GMSL2_INVALID_REG_FIELD, ++ }, ++ .pipe_dt_l = { ++ REG_FIELD(0x40e, 0, 5), REG_FIELD(0x40f, 0, 3), ++ REG_FIELD(0x410, 0, 1), REG_FIELD(0x410, 2, 7), ++ }, ++ .pipe_dt_h = { ++ GMSL2_INVALID_REG_FIELD, REG_FIELD(0x40e, 6, 7), ++ REG_FIELD(0x40f, 4, 7), GMSL2_INVALID_REG_FIELD, ++ }, ++ .pipe_vc = { ++ REG_FIELD(0x40c, 0, 3), REG_FIELD(0x40c, 4, 7), ++ REG_FIELD(0x40d, 0, 3), REG_FIELD(0x40d, 4, 7), ++ }, ++ .pipe_vid_lock = { ++ REG_FIELD(0x108, 6, 6), REG_FIELD(0x11a, 6, 6), ++ REG_FIELD(0x12c, 6, 6), REG_FIELD(0x13e, 6, 6), ++ }, ++ .pipe_remap_regs = 16, ++ .pipe_remap_base_addr = { ++ 0x90b, 0x94b, 0x98b, 0x9cb ++ }, ++ .link_lock = { ++ REG_FIELD(0x1a, 3, 3), REG_FIELD(0xa, 3, 3), ++ REG_FIELD(0xb, 3, 3), REG_FIELD(0xc, 3, 3) ++ }, ++ .reset_one_shot_link = { ++ REG_FIELD(0x18, 0, 0), REG_FIELD(0x18, 1, 1), ++ REG_FIELD(0x18, 2, 2), REG_FIELD(0x18, 3, 3) ++ }, ++ .reset_link = { ++ REG_FIELD(0x18, 4, 4), REG_FIELD(0x18, 5, 5), ++ REG_FIELD(0x18, 6, 6), REG_FIELD(0x18, 7, 7) ++ }, ++ .gmsl1 = &max96914_gmsl1_data, +}; ++#define IS_MAX96724(priv) (priv->data->device_id == MAX96724_DEVICE_ID) + +#define for_each_pipe(pipe_idx, priv) \ + for ( (pipe_idx) = 0; (pipe_idx) < (priv)->data->pipes; (pipe_idx)++ ) @@ -437,151 +909,63 @@ index 0000000..8a30110 +#define for_each_link(link_idx, priv) \ + for ( (link_idx) = 0; (link_idx) < (priv)->data->links; (link_idx)++ ) + -+static const char* pipe_to_str_quad(u8 pipe) { -+ switch (pipe) { -+ case 0: -+ return "pipe_0"; -+ case 1: -+ return "pipe_1"; -+ case 2: -+ return "pipe_2"; -+ case 3: -+ return "pipe_3"; -+ case 4: -+ return "pipe_4"; -+ case 5: -+ return "pipe_5"; -+ case 6: -+ return "pipe_6"; -+ case 7: -+ return "pipe_7"; -+ } -+ -+ return NULL; -+} -+ -+static const char* pipe_to_str_dual(u8 pipe) { -+ switch (pipe) { -+ case 0: -+ return "pipe_X"; -+ case 1: -+ return "pipe_Y"; -+ case 2: -+ return "pipe_Z"; -+ case 3: -+ return "pipe_U"; -+ } ++static void max_gmsl2_deser_init_patgen_regs(struct max_gmsl2_deser *priv) ++{ ++ struct device *dev = &priv->client->dev; ++ const struct max_gmsl2_data *data = priv->data; ++ struct max_gmsl2_patgen *patgen = &priv->patgen; ++ const struct reg_field *field; ++ u16 base = data->patgen_base_addr[0]; ++ struct reg_field gen_vs_hs_de = REG_FIELD(base, 5, 7); ++ struct reg_field inv_vs_hs_de = REG_FIELD(base, 2, 4); ++ struct reg_field vtg_mode = REG_FIELD(base, 0, 2); ++ struct reg_field vpg_mode_reg = REG_FIELD(base + 1, 4, 5); + -+ return NULL; -+} ++ field = &data->pattern_clk_freq; ++ if (field->reg) ++ priv->patgen.pattern_clk_freq = ++ devm_regmap_field_alloc(dev, priv->regmap, *field); + -+/* Quad and dual link deserializer have different pipes naming */ -+static const char* pipe_to_str(struct max_gmsl2_deser_priv *priv, u8 pipe) { -+ if (priv->data->links == 4) -+ return pipe_to_str_quad(pipe); -+ else -+ return pipe_to_str_dual(pipe);; ++ patgen->gen_vs_hs_de = devm_regmap_field_alloc(dev, priv->regmap, gen_vs_hs_de); ++ patgen->inv_vs_hs_de = devm_regmap_field_alloc(dev, priv->regmap, inv_vs_hs_de); ++ patgen->vtg_mode = devm_regmap_field_alloc(dev, priv->regmap, vtg_mode); ++ patgen->vpg_mode = devm_regmap_field_alloc(dev, priv->regmap, vpg_mode_reg); ++ patgen->vtg_timing = base + 2; ++ patgen->vpg_patterns = base + 0x1d; +} + -+static const char* device_id_to_name(u8 device_id) -+{ -+ switch (device_id) { -+ case MAX96934_DEVICE_ID: -+ return "max96934"; -+ case MAX96914_DEVICE_ID: -+ return "max96914"; -+ case MAX96714_DEVICE_ID: -+ return "max96714"; -+ default: -+ break; -+ } -+ return NULL; -+} + -+static int max_gmsl2_deser_init_regmap(struct max_gmsl2_deser_priv *priv) ++static int max_gmsl2_deser_init_regmap(struct max_gmsl2_deser *priv) +{ + struct device *dev = &priv->client->dev; -+ const struct max_gmsl2_deser_data *data = priv->data; -+ struct max_gmsl2_deser_pipe *pipe; -+ struct max_gmsl2_deser_csi *csi; ++ const struct max_gmsl2_data *data = priv->data; + const struct reg_field *field; + u8 i; + + priv->regmap = devm_regmap_init_i2c(priv->client, -+ &max_gmsl2_deser_regmap_config); ++ &gmsl2_regmap_config); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + -+ for_each_pipe(i, priv) { -+ pipe = &priv->pipes[i]; -+ -+ field = &data->pipe_enable[i]; -+ pipe->enable = devm_regmap_field_alloc(dev, priv->regmap, -+ *field); -+ field = &data->pipe_stream_id[i]; -+ pipe->stream_id = devm_regmap_field_alloc(dev, priv->regmap, -+ *field); -+ field = &data->pipe_link_sel[i]; -+ if (field->reg) -+ pipe->link_sel = devm_regmap_field_alloc(dev, priv->regmap, -+ *field); -+ field = &data->pipe_vid_lock[i]; -+ pipe->vid_lock = devm_regmap_field_alloc(dev, priv->regmap, *field); -+ -+ field = &data->pipe_override_bpp_vc_dt[i]; -+ pipe->override_bpp_vc_dt = devm_regmap_field_alloc(dev, priv->regmap, *field); -+ -+ field = &data->pipe_bpp_l[i]; -+ pipe->bpp_l = devm_regmap_field_alloc(dev, priv->regmap, *field); -+ field = &data->pipe_bpp_h[i]; -+ if (field->reg) -+ pipe->bpp_h = devm_regmap_field_alloc(dev, priv->regmap, *field); -+ -+ field = &data->pipe_dt_l[i]; -+ pipe->dt_l = devm_regmap_field_alloc(dev, priv->regmap, *field); -+ field = &data->pipe_dt_h[i]; -+ if (field->reg) -+ pipe->dt_h = devm_regmap_field_alloc(dev, priv->regmap, *field); -+ -+ field = &data->pipe_dt_l[i]; -+ pipe->dt_l = devm_regmap_field_alloc(dev, priv->regmap, *field); -+ -+ field = &data->pipe_vc[i]; -+ pipe->vc = devm_regmap_field_alloc(dev, priv->regmap, *field); -+ } ++ for_each_pipe(i, priv) ++ max_gmsl2_pipe_init_regfield(dev, priv->regmap, ++ &priv->pipes[i], priv->data, ++ i); ++ ++ for_each_csi(i, priv) ++ max_gmsl2_csi_init_regfield(dev, priv->regmap, ++ &priv->csi[i], priv->data, ++ i); ++ for_each_link(i, priv) ++ max_gmsl2_link_init_regfield(dev, priv->regmap, ++ &priv->links[i], priv->data, ++ i); ++ field = &data->dev_rev; ++ priv->dev_rev = devm_regmap_field_alloc(dev, priv->regmap, *field); + -+ for_each_csi(i, priv) { -+ csi = &priv->csi[i]; -+ field = &data->csi_phy_enable[i]; -+ csi->phy_enable = devm_regmap_field_alloc(dev, priv->regmap, *field); -+ -+ field = &data->csi_phy_out_freq[i*2]; -+ if (field->reg) -+ csi->phy_out_freq[0] = devm_regmap_field_alloc(dev, priv->regmap, *field); -+ -+ field = &data->csi_phy_out_freq[i * 2 + 1]; -+ if (field->reg) -+ csi->phy_out_freq[1] = devm_regmap_field_alloc(dev, priv->regmap, *field); -+ -+ field = &data->csi_lane_polarities[i * 2]; -+ csi->lane_polarities[0] = devm_regmap_field_alloc(dev, priv->regmap, *field); -+ -+ field = &data->csi_lane_polarities[i * 2 +1]; -+ csi->lane_polarities[1] = devm_regmap_field_alloc(dev, priv->regmap, *field); -+ -+ field = &data->csi_lane_map[i * 2]; -+ csi->lane_map[0] = devm_regmap_field_alloc(dev, priv->regmap, *field); -+ -+ field = &data->csi_lane_map[i * 2 + 1]; -+ csi->lane_map[1] = devm_regmap_field_alloc(dev, priv->regmap, *field); -+ } -+ -+ field = &data->dev_rev; -+ priv->dev_rev = devm_regmap_field_alloc(dev, priv->regmap, *field); -+ -+ field = &data->csi_enable; ++ field = &data->csi_force_enable; + if (field->reg) -+ priv->csi_enable = ++ priv->csi_force_enable = + devm_regmap_field_alloc(dev, priv->regmap, *field); + + field = &data->csi_wait_frame; @@ -589,25 +973,12 @@ index 0000000..8a30110 + priv->csi_wait_frame = + devm_regmap_field_alloc(dev, priv->regmap, *field); + -+ field = &data->pattern_clk_freq; ++ field = &data->tunnel_mode; + if (field->reg) -+ priv->pattern_clk_freq = ++ priv->tunnel_mode = + devm_regmap_field_alloc(dev, priv->regmap, *field); + -+ for_each_link(i, priv) { -+ field = &data->link_lock[i]; -+ if (field->reg) -+ priv->link_locked[i] = -+ devm_regmap_field_alloc(dev, priv->regmap, *field); -+ field = &data->reset_one_shot_link[i]; -+ if (field->reg) -+ priv->reset_one_shot_link[i] = -+ devm_regmap_field_alloc(dev, priv->regmap, *field); -+ field = &data->reset_link[i]; -+ if (field->reg) -+ priv->reset_link[i] = -+ devm_regmap_field_alloc(dev, priv->regmap, *field); -+ } ++ max_gmsl2_deser_init_patgen_regs(priv); + + if (data->gmsl1) { + field = &data->gmsl1->gmsl2_link_mode; @@ -626,6 +997,10 @@ index 0000000..8a30110 + priv->gmsl1_forward_cc[i] = + devm_regmap_field_alloc(dev, priv->regmap, + *field); ++ field = &data->gmsl1->shift_vid_hvd[i]; ++ priv->gmsl1_shift_vid_hvd[i] = ++ devm_regmap_field_alloc(dev, priv->regmap, ++ *field); + } + } + return 0; @@ -639,10 +1014,10 @@ index 0000000..8a30110 +#define DST_DPHY_REG(n) (0x22 + n / 4) +#define DST_DPHY_SHIFT(n) ((n % 4) * 2) +#define DST_DPHY_MASK(n) (0x3 << DST_DPHY_SHIFT(n)) -+static int pipe_remap_element(struct max_gmsl2_deser_priv *priv, u8 pipeid, ++static int pipe_remap_element(struct max_gmsl2_deser *priv, u8 pipeid, + u8 src, u8 dst, u8 csiid) +{ -+ struct max_gmsl2_deser_pipe *pipe = &priv->pipes[pipeid]; ++ struct max_gmsl2_pipe *pipe = &priv->pipes[pipeid]; + u16 base = priv->data->pipe_remap_base_addr[pipeid]; + u8 idx = pipe->pipe_remap_idx; + int ret; @@ -673,9 +1048,9 @@ index 0000000..8a30110 + return 0; +} + -+static int pipe_clear_remap(struct max_gmsl2_deser_priv *priv, u8 pipeid) ++static int pipe_clear_remap(struct max_gmsl2_deser *priv, u8 pipeid) +{ -+ struct max_gmsl2_deser_pipe *pipe = &priv->pipes[pipeid]; ++ struct max_gmsl2_pipe *pipe = &priv->pipes[pipeid]; + u16 base = priv->data->pipe_remap_base_addr[pipeid]; + int i, ret; + @@ -689,7 +1064,7 @@ index 0000000..8a30110 + return 0; +} + -+static int pipes_clear_remap(struct max_gmsl2_deser_priv *priv) ++static int pipes_clear_remap(struct max_gmsl2_deser *priv) +{ + u8 pipeid; + int ret; @@ -702,196 +1077,152 @@ index 0000000..8a30110 + return 0; +} + -+/* Some registers such as bpp and dt maybe split on two regmap_field */ -+static inline int max_gmsl2_write_h_l_val(struct regmap_field *h, struct regmap_field *l, -+ const struct reg_field *l_field, unsigned int val) -+{ -+ u8 shift; -+ int ret; -+ -+ /* if h ptr is not null then the register is splitted */ -+ if (h) { -+ shift = l_field->msb - l_field->lsb + 1; -+ ret = regmap_field_write(h, val >> shift); -+ if (ret) -+ return ret; -+ } -+ -+ /* regmap_field_write will take care of applying a mask on val -+ * to not write the h bits. -+ */ -+ return regmap_field_write(l, val); -+} -+ -+static inline int max_gmsl2_read_h_l_val(struct regmap_field *h, struct regmap_field *l, -+ const struct reg_field *l_field, unsigned int *val) -+{ -+ u8 shift; -+ unsigned int h_val; -+ int ret; -+ -+ ret = regmap_field_read(l, val); -+ if (ret) -+ return ret; -+ if (h) { -+ ret = regmap_field_read(h, &h_val); -+ if (ret) -+ return ret; -+ shift = l_field->msb - l_field->lsb + 1; -+ *val |= (h_val << shift); -+ } -+ -+ return 0; -+} -+ -+static int max_gmsl2_deser_state_show(struct seq_file *seq, void *p) ++static int max_gmsl2_deser_log_status(struct v4l2_subdev *sd) +{ -+ struct max_gmsl2_deser_priv *priv = seq->private; -+ struct max_gmsl2_deser_pipe *pipe; -+ struct max_gmsl2_deser_csi *csi; -+ unsigned int link_locked, enable, stream_id, link_sel, vid_lock, bpp, dt, vc, freq1, freq2, dphy_en, link_mode; -+ u8 link, pipeid, csiid; ++ struct max_gmsl2_deser *priv = sd_to_max_gmsl2_deser(sd); ++ struct device *dev = &priv->client->dev; ++ struct max_gmsl2_pipe *pipe; ++ struct max_gmsl2_csi *csi; ++ struct max_gmsl2_link *link; ++ unsigned int link_locked, freq, dphy_en, link_mode; ++ u8 linkid, pipeid, csiid; + int ret; + -+ seq_printf(seq, "Max GMSL2 deserializer %s id: %02x\n", -+ device_id_to_name(priv->data->device_id), priv->data->device_id); ++ dev_info(dev, "Max GMSL2 deserializer %s id: %02x\n", ++ gmsl2_id_to_name(priv->data->device_id), priv->data->device_id); + + /* MAX96934 has only one link lock flag */ + if (IS_MAX96934(priv)) { -+ ret = regmap_field_read(priv->link_locked[0], &link_locked); ++ ret = regmap_field_read(priv->links[0].locked, &link_locked); + if (!ret) -+ seq_printf(seq, "Link locked: %u\n", link_locked); ++ dev_info(dev, "Link locked: %u\n", link_locked); + } else { + /* MAX96914 with one link lock per link */ + ret = regmap_field_read(priv->gmsl2_link_mode, &link_mode); + if (ret) + return ret; -+ for_each_link(link, priv) { -+ if (link_mode & BIT(link)) { ++ for_each_link(linkid, priv) { ++ link = &priv->links[linkid]; ++ if (link_mode & BIT(linkid)) { + /* gmsl2*/ -+ ret = regmap_field_read(priv->link_locked[link], &link_locked); ++ ret = regmap_field_read(link->locked, &link_locked); + if (!ret) -+ seq_printf(seq, "GMSL2 Link %c locked: %u\n", 'A' + link, link_locked); ++ dev_info(dev, "GMSL2 Link %c locked: %u\n", 'A' + linkid, link_locked); + } else { + /* gmsl1 backward compatibility */ -+ ret = regmap_field_read(priv->gmsl1_link_locked[link], &link_locked); ++ ret = regmap_field_read(priv->gmsl1_link_locked[linkid], &link_locked); + if (!ret) -+ seq_printf(seq, "GMSL1 Link %c locked: %u\n", 'A' + link, link_locked); ++ dev_info(dev, "GMSL1 Link %c locked: %u\n", 'A' + linkid, link_locked); + } + } + } + for_each_pipe(pipeid, priv) { + pipe = &priv->pipes[pipeid]; -+ ret = regmap_field_read(pipe->enable, &enable); -+ if (ret) -+ break; -+ ret = regmap_field_read(pipe->stream_id, &stream_id); -+ if (ret) -+ break; -+ if (pipe->link_sel) { -+ ret = regmap_field_read(pipe->link_sel, &link_sel); -+ if (ret) -+ break; -+ } -+ -+ ret = regmap_field_read(pipe->vid_lock, &vid_lock); -+ if (ret) -+ break; -+ ret = max_gmsl2_read_h_l_val(pipe->bpp_h, pipe->bpp_l, -+ &priv->data->pipe_bpp_l[pipeid], -+ &bpp); -+ if (ret) -+ break; -+ ret = max_gmsl2_read_h_l_val(pipe->dt_h, pipe->dt_l, -+ &priv->data->pipe_dt_l[pipeid], -+ &dt); -+ if (ret) -+ break; -+ ret = regmap_field_read(pipe->vc, &vc); -+ if (ret) -+ break; -+ -+ seq_printf(seq, "%s: enable[%u]", -+ pipe_to_str(priv, pipeid), enable); -+ if (pipe->link_sel) -+ seq_printf(seq, " link_sel[%c]", 'A' + link_sel); -+ seq_printf(seq, " stream_id[%u]", stream_id); -+ seq_printf(seq," vid_lock[%u] bpp[%u], dt[0x%2x], vc[%u]\n", -+ vid_lock, bpp, dt, vc); ++ max_gmsl2_state_show_pipe(dev, pipe, priv->data, pipeid); + } + + for_each_csi(csiid, priv) { + csi = &priv->csi[csiid]; -+ ret = regmap_field_read(csi->phy_out_freq[0], &freq1); -+ if (ret) -+ break; -+ ret = regmap_field_read(csi->phy_out_freq[1], &freq2); ++ ret = regmap_field_read(csi->dpll_freq, &freq); + if (ret) + break; + ret = regmap_field_read(csi->phy_enable, &dphy_en); + if (ret) + break; -+ seq_printf(seq, "CSI%d: freq DPHY[%d]:%dMHz enabled:%u DPHY[%d]:%dMHz enabled:%u\n", csiid, -+ csiid * 2 , freq1 * 100, (dphy_en & BIT(0)) == BIT(0), -+ csiid * 2 + 1, freq2 * 100, (dphy_en & BIT(1)) == BIT(1)); ++ dev_info(dev, "CSI%d: DPHY%d enabled:%u DPHY%d: enabled:%u DPLL:%dMHz\n", csiid, ++ csiid * 2 , !!(dphy_en & BIT(0)), ++ csiid * 2 + 1, !!(dphy_en & BIT(1)), ++ freq * 100); + } + + return ret; +} + -+DEFINE_SHOW_ATTRIBUTE(max_gmsl2_deser_state); ++static int max_gmsl2_csi_map_pipes(struct max_gmsl2_csi *csi); + -+static void max_gmsl2_deser_debug_init(struct max_gmsl2_deser_priv *priv) ++ ++static int max_gmsl2_deser_pipe_override_format(struct max_gmsl2_deser *priv, ++ u8 pipe_id, u8 datatype, ++ u8 bpp, u8 vc) +{ -+ struct device *dev = &priv->client->dev; ++ struct max_gmsl2_pipe *pipe = &priv->pipes[pipe_id]; ++ const struct reg_field *l_field; ++ int ret; + -+ priv->dbg_dir = debugfs_create_dir(dev_name(dev), NULL); ++ ret = regmap_field_write(pipe->vc, vc); ++ if (ret) ++ return ret; + -+ debugfs_create_file("state", 0400, priv->dbg_dir, -+ priv, &max_gmsl2_deser_state_fops); -+} ++ l_field = &priv->data->pipe_dt_l[pipe_id]; ++ ret = max_gmsl2_write_h_l_val(pipe->dt_h, pipe->dt_l, ++ l_field, datatype); + -+static void max_gmsl2_deser_debug_remove(struct max_gmsl2_deser_priv *priv) -+{ -+ debugfs_remove_recursive(priv->dbg_dir); -+ priv->dbg_dir = NULL; ++ if (ret) ++ return ret; ++ ++ l_field = &priv->data->pipe_bpp_l[pipe_id]; ++ ret = max_gmsl2_write_h_l_val(pipe->bpp_h, pipe->bpp_l, ++ l_field, bpp); ++ if (ret) ++ return ret; ++ return regmap_field_write(pipe->override_bpp_vc_dt, 1); +} -+static int max_gmsl2_csi_map_pipes(struct max_gmsl2_deser_csi *csi); + +#define PATTERN_CLK_FREQ_75MHz 1 -+static int max_gmsl2_deser_csi_s_ctrl_pattern(struct max_gmsl2_deser_csi *csi, int val) ++static int max_gmsl2_deser_s_ctrl_pattern(struct max_gmsl2_deser *priv, int val) +{ -+ struct max_gmsl2_deser_priv *priv = csi->priv; ++ struct max_gmsl2_patgen *patgen = &priv->patgen; + const struct v4l2_dv_timings t_1080p = V4L2_DV_BT_CEA_1920X1080P30; + const struct v4l2_dv_timings t_720p = V4L2_DV_BT_CEA_1280X720P60; + const struct v4l2_bt_timings *bt; -+ uint16_t patgen_base = priv->data->patgen_base_addr[0]; + enum max_gmsl2_vpg_mode mode; + int ret; + u8 i; + + if (val) { -+ regmap_field_write(priv->pattern_clk_freq, ++ regmap_field_write(patgen->pattern_clk_freq, + PATTERN_CLK_FREQ_75MHz); + mode = (val == 1) ? MAX_GMSL2_VPG_CHECKERBOARD: + MAX_GMSL2_VPG_GRADIENT; + -+ ret = max_gmsl2_vpg_enable(priv->regmap, -+ patgen_base, mode); ++ ret = max_gmsl2_vpg_enable(patgen, priv->regmap, mode); + if (ret) + return ret; -+ if (csi->format.format.height == 720) ++ ++ if (priv->format.format.height == 720) + bt = &t_720p.bt; + else + bt = &t_1080p.bt; + -+ ret = max_gmsl2_vtg_enable(priv->regmap, patgen_base, bt); ++ /* Invert VS HS and DE */ ++ ret = regmap_field_write(patgen->inv_vs_hs_de, 7); ++ if (ret) ++ return ret; ++ ++ ret = max_gmsl2_vtg_enable(patgen, priv->regmap, bt); + if (ret) + return ret; + /* Pipe remapping doesn't works with pattern generator */ + ret = pipes_clear_remap(priv); + if (ret) + return ret; ++ /* Tunnel mode should be disabled for pattern generator */ ++ if (priv->tunnel_mode) { ++ ret = regmap_field_write(priv->tunnel_mode, 0); ++ if (ret) ++ return ret; ++ } ++ ++ /* FIXME: Do we really need to override the format for ++ * the pattern generator. ++ */ ++ for_each_pipe(i, priv) { ++ ret = max_gmsl2_deser_pipe_override_format(priv, i, ++ MIPI_CSI2_DT_RGB888, ++ 24, 0); ++ if (ret) ++ return ret; ++ } + } else { + /* Restore CSI to Pipe mapping */ + ret = pipes_clear_remap(priv); @@ -902,25 +1233,38 @@ index 0000000..8a30110 + if (ret) + return ret; + } -+ ret = max_gmsl2_vtg_disable(priv->regmap, patgen_base); ++ ret = max_gmsl2_vtg_disable(patgen, priv->regmap); ++ if (ret) ++ return ret; ++ ret = max_gmsl2_vpg_disable(patgen); + if (ret) + return ret; ++ /* Restore tunnel mode */ ++ if (priv->tunnel_mode && priv->tunnel_mode_saved) { ++ ret = regmap_field_write(priv->tunnel_mode, 1); ++ if (ret) ++ return ret; ++ } ++ for_each_pipe(i, priv) { ++ ret = regmap_field_write( ++ priv->pipes[i].override_bpp_vc_dt, 0); ++ if (ret) ++ return ret; ++ } + } + + + return 0; +} + -+static int max_gmsl2_deser_csi_s_ctrl(struct v4l2_ctrl *ctrl) ++static int max_gmsl2_deser_s_ctrl(struct v4l2_ctrl *ctrl) +{ -+ struct max_gmsl2_deser_csi *csi = -+ container_of(ctrl->handler, struct max_gmsl2_deser_csi, ctrl_hdl); ++ struct max_gmsl2_deser *priv = ++ container_of(ctrl->handler, struct max_gmsl2_deser, ctrl_handler); + + switch (ctrl->id) { -+ case V4L2_CID_PIXEL_RATE: -+ return 0; + case V4L2_CID_TEST_PATTERN: -+ return max_gmsl2_deser_csi_s_ctrl_pattern(csi, ctrl->val); ++ return max_gmsl2_deser_s_ctrl_pattern(priv, ctrl->val); + default: + return -EINVAL; + } @@ -928,50 +1272,42 @@ index 0000000..8a30110 + return 0; +} + -+static const struct v4l2_ctrl_ops max_gmsl2_deser_csi_ctrl_ops = { -+ .s_ctrl = max_gmsl2_deser_csi_s_ctrl, ++static const struct v4l2_ctrl_ops max_gmsl2_deser_ctrl_ops = { ++ .s_ctrl = max_gmsl2_deser_s_ctrl, +}; + -+static int max_gmsl2_deser_csi_get_format(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *sdstate, ++static int max_gmsl2_deser_get_format(struct v4l2_subdev *sd, ++ struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *sdformat) +{ -+ struct max_gmsl2_deser_csi *csi = max_gmsl2_deser_sd_to_csi(sd); ++ struct max_gmsl2_deser *priv = sd_to_max_gmsl2_deser(sd); + -+ printk("%s: sdname %s \n", __func__, sd->name); -+ sdformat->format = csi->format.format; ++ sdformat->format = priv->format.format; + + return 0; +} + -+static int max_gmsl2_deser_csi_set_format(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *sdstate, ++static int max_gmsl2_deser_set_format(struct v4l2_subdev *sd, ++ struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *sdformat) +{ -+ struct max_gmsl2_deser_csi *csi = max_gmsl2_deser_sd_to_csi(sd); -+ -+ if ((sdformat->format.width == 1920 && sdformat->format.height == 1080 -+ && sdformat->format.code == MEDIA_BUS_FMT_RGB888_1X24) || -+ (sdformat->format.width == 1080 && sdformat->format.height == 720 -+ && sdformat->format.code == MEDIA_BUS_FMT_RGB888_1X24)) -+ csi->format.format = sdformat->format; -+ else -+ sdformat->format = csi->format.format; ++ struct max_gmsl2_deser *priv = sd_to_max_gmsl2_deser(sd); ++ priv->format.format = sdformat->format; + + return 0; +} + -+static const struct v4l2_subdev_pad_ops max_gmsl2_deser_csi_pad_ops = { -+ .get_fmt = max_gmsl2_deser_csi_get_format, -+ .set_fmt = max_gmsl2_deser_csi_set_format, ++static const struct v4l2_subdev_pad_ops max_gmsl2_deser_pad_ops = { ++ .get_fmt = max_gmsl2_deser_get_format, ++ .set_fmt = max_gmsl2_deser_set_format, +}; + -+static int max_gmsl2_deser_csi_enable(struct max_gmsl2_deser_priv *priv, bool enable) ++static int max_gmsl2_deser_csi_enable(struct max_gmsl2_deser *priv, bool enable) +{ + bool enabled = !!priv->csi_use_cnt; + int ret = 0; + -+ if (!priv->csi_enable) ++ if (!priv->csi_force_enable) + return 0; + + if (enable) @@ -980,140 +1316,57 @@ index 0000000..8a30110 + priv->csi_use_cnt--; + + if (enabled && !priv->csi_use_cnt) -+ ret = regmap_field_write(priv->csi_enable, 0); ++ ret = regmap_field_write(priv->csi_force_enable, 0); + else if (!enabled && priv->csi_use_cnt) -+ ret = regmap_field_write(priv->csi_enable, 1); ++ ret = regmap_field_write(priv->csi_force_enable, 1); + + return 0; +} + -+static int max_gmsl2_deser_csi_s_stream(struct v4l2_subdev *sd, int enable) -+{ -+ struct max_gmsl2_deser_csi *csi = max_gmsl2_deser_sd_to_csi(sd); -+ -+ printk("%s csi:%u enable %d\n", __func__, csi->id, enable); -+ -+ max_gmsl2_deser_csi_enable(csi->priv, !!enable); -+ -+ /* Each CSI use 2 phy enable or disable both */ -+ return regmap_field_write(csi->phy_enable, enable ? 0x3 : 0); -+} -+ -+static const struct v4l2_subdev_video_ops max_gmsl2_deser_csi_video_ops = { -+ .s_stream = max_gmsl2_deser_csi_s_stream, -+}; -+ -+static const struct v4l2_subdev_ops max_gmsl2_deser_csi_ops = { -+ .video = &max_gmsl2_deser_csi_video_ops, -+ .pad = &max_gmsl2_deser_csi_pad_ops, -+}; -+ -+static const char * const max_gmsl2_test_pattern[] = { -+ "Disabled", -+ "Checkerboard", -+ "Gradient" -+}; -+ -+static int max_gmsl2_deser_csi_init_controls(struct max_gmsl2_deser_csi *csi) -+{ -+ -+ v4l2_ctrl_handler_init(&csi->ctrl_hdl, 2); -+ -+ csi->pixel_rate = v4l2_ctrl_new_std(&csi->ctrl_hdl, -+ &max_gmsl2_deser_csi_ctrl_ops, -+ V4L2_CID_PIXEL_RATE, 1, INT_MAX, -+ 1, 1); -+ if (csi->priv->data->patgen_base_addr[0]) -+ v4l2_ctrl_new_std_menu_items(&csi->ctrl_hdl, -+ &max_gmsl2_deser_csi_ctrl_ops, -+ V4L2_CID_TEST_PATTERN, -+ ARRAY_SIZE(max_gmsl2_test_pattern) - 1, -+ 0, 0, max_gmsl2_test_pattern); -+ -+ csi->sd.ctrl_handler = &csi->ctrl_hdl; -+ if (csi->ctrl_hdl.error) { -+ v4l2_ctrl_handler_free(&csi->ctrl_hdl); -+ return csi->ctrl_hdl.error; -+ } -+ -+ return v4l2_ctrl_handler_setup(&csi->ctrl_hdl); -+} -+ -+static int max_gmsl2_deser_csi_registered(struct v4l2_subdev *sd) ++static int max_gmsl2_deser_s_stream(struct v4l2_subdev *sd, int enable) +{ -+ struct max_gmsl2_deser_csi *csi = max_gmsl2_deser_sd_to_csi(sd); -+ struct max_gmsl2_deser_priv *priv = csi->priv; -+ struct device *dev = &priv->client->dev; -+ struct max_gmsl2_deser_pipe *pipe; -+ struct v4l2_subdev *src; ++ struct max_gmsl2_deser *priv = sd_to_max_gmsl2_deser(sd); ++ struct max_gmsl2_link *link; ++ struct max_gmsl2_csi *csi; ++ unsigned int i; + int ret; + -+ dev_info(dev, "Registered CSI%u %s", csi->id, sd->name); -+ -+ if (csi->id == 0) -+ /* Pipe Y goes to CSI0 */ -+ pipe = &priv->pipes[1]; -+ else -+ /* Pipe Z goes to CSI1 */ -+ pipe = &priv->pipes[2]; -+ -+ src = &pipe->sd; -+ -+ if (!src->v4l2_dev) -+ ret = v4l2_device_register_subdev(sd->v4l2_dev, src); ++ max_gmsl2_deser_csi_enable(priv, enable); + -+ /* Connect pipe source pad(0) to csi sink pad(0)*/ -+ ret = media_create_pad_link(&src->entity, 0, -+ &sd->entity, 0, -+ MEDIA_LNK_FL_ENABLED); -+ return ret; -+} ++ for_each_link(i, priv) { ++ link = &priv->links[i]; ++ if (link->source_sd) ++ v4l2_subdev_call(link->source_sd, video, ++ s_stream, enable); ++ } + -+static const struct v4l2_subdev_internal_ops max_gmsl2_deser_csi_internal_ops = { -+ .registered = max_gmsl2_deser_csi_registered, -+}; ++ for_each_csi(i, priv) { ++ csi = &priv->csi[i]; ++ ret = regmap_field_write(csi->phy_enable, enable ? 0x3 : 0); ++ if (ret) ++ return ret; ++ } + -+static int max_gmsl2_deser_link_setup(struct media_entity *entity, -+ const struct media_pad *local, -+ const struct media_pad *remote, u32 flags) -+{ + return 0; +} + -+static const struct media_entity_operations max_gmsl2_deser_csi_media_ops = { -+ .link_setup = max_gmsl2_deser_link_setup, -+ .link_validate = v4l2_subdev_link_validate, ++static const struct v4l2_subdev_core_ops max_gmsl2_deser_core_ops = { ++ .log_status = max_gmsl2_deser_log_status, +}; + -+static const struct media_entity_operations max_gmsl2_deser_media_ops = { -+ .link_validate = v4l2_subdev_link_validate, ++static const struct v4l2_subdev_video_ops max_gmsl2_deser_video_ops = { ++ .s_stream = max_gmsl2_deser_s_stream, +}; + -+static void max_gmsl2_deser_subdev_init(struct v4l2_subdev *sd, struct max_gmsl2_deser_priv *priv, -+ const struct v4l2_subdev_ops *ops, u32 function, -+ const char *ident) -+{ -+ struct device *dev = &priv->client->dev; -+ -+ v4l2_subdev_init(sd, ops); -+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; -+ -+ /* the owner is the same as the i2c_client's driver owner */ -+ sd->owner = dev->driver->owner; -+ sd->dev = dev; -+ -+ v4l2_set_subdevdata(sd, priv); -+ -+ /* initialize name */ -+ snprintf(sd->name, sizeof(sd->name), "%s %d-%04x %s", -+ dev->driver->name, -+ i2c_adapter_id(priv->client->adapter), -+ priv->client->addr, ident); ++static const struct v4l2_subdev_ops max_gmsl2_deser_subdev_ops = { ++ .core = &max_gmsl2_deser_core_ops, ++ .video = &max_gmsl2_deser_video_ops, ++ .pad = &max_gmsl2_deser_pad_ops, ++}; + -+ sd->entity.function = function; -+ sd->entity.ops = (function == MEDIA_ENT_F_VID_IF_BRIDGE) ? -+ &max_gmsl2_deser_csi_media_ops : &max_gmsl2_deser_media_ops; -+} ++static const struct media_entity_operations max_gmsl2_deser_entity_ops = { ++ .get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1, ++}; + +/* + * By using the default pipe to CSI controller mapping, pipe0/4 and @@ -1126,19 +1379,21 @@ index 0000000..8a30110 + * CSI controller 1, and data selected in PIPE 3 will go to the + * CSI controller 2. + */ -+static int max96914_remap_pipe(struct max_gmsl2_deser_priv *priv, u8 pipeid, ++static int max96914_remap_pipe(struct max_gmsl2_deser *priv, u8 pipeid, + u8 csi_controller) +{ -+ struct max_gmsl2_deser_pipe *pipe = &priv->pipes[pipeid]; ++ struct max_gmsl2_pipe *pipe = &priv->pipes[pipeid]; + unsigned int vc; + /* remap frame start, frame end, and rgb888 datatypes */ -+ u8 dt_to_remap[3] = {0x00, 0x01, 0x24}; ++ u8 dt_to_remap[3] = {MIPI_CSI2_DT_FS, MIPI_CSI2_DT_LE, ++ MIPI_CSI2_DT_RGB888}; + u8 pipe_elt; + int ret, i; + + ret = regmap_field_read(pipe->vc, &vc); + if (ret) + return ret; ++ + /* route dt and vc from pipe to CSI controller*/ + for (i = 0; i < ARRAY_SIZE(dt_to_remap); i++) { + pipe_elt = PIPE_ELEMENT(vc, dt_to_remap[i]); @@ -1151,9 +1406,10 @@ index 0000000..8a30110 + return ret; +} + -+static int max_gmsl2_csi_map_pipes(struct max_gmsl2_deser_csi *csi) ++static int max_gmsl2_csi_map_pipes(struct max_gmsl2_csi *csi) +{ -+ struct fwnode_handle *node = csi->sd.fwnode; ++ struct fwnode_handle *node = csi->ep_fwnode; ++ struct max_gmsl2_deser *priv = csi->priv; + u32 pipes[GMSL2_MAX_PIPE]; + int ret, num_pipes, i; + @@ -1169,7 +1425,7 @@ index 0000000..8a30110 + return ret; + + for (i = 0; i < num_pipes; i++) { -+ if (pipes[i] >= csi->priv->data->pipes) ++ if (pipes[i] >= priv->data->pipes) + return -EINVAL; + /* csi_id = 0 then it's CSI controller 1 if csi_id = 1 then it's the CSI + * controller 2. @@ -1182,55 +1438,9 @@ index 0000000..8a30110 + return 0; +} + -+static int max_gmsl2_csi_phy_init(struct max_gmsl2_deser_csi *csi, -+ struct v4l2_mbus_config_mipi_csi2 *mipi) -+{ -+ int i, ret; -+ u8 val = 0; -+ -+ /* lane polarities */ -+ for (i = 0; i < mipi->num_data_lanes + 1; i++) { -+ if (!mipi->lane_polarities[i]) -+ continue; -+ if (i == 0) -+ /* clock lane */ -+ val |= BIT(5); -+ else if (i < 3) -+ /* Lane D0 and D1 */ -+ val |= BIT(i - 1); -+ else -+ /* D2 and D3 */ -+ val |= BIT(i); -+ } -+ ret = regmap_field_write(csi->lane_polarities[0], val); -+ if (ret) -+ return ret; -+ ret = regmap_field_write(csi->lane_polarities[1], val >> 3); -+ if (ret) -+ return ret; -+ -+ /* lane mapping */ -+ val = 0; -+ for (i = 0; i < mipi->num_data_lanes; i++) -+ val |= (mipi->data_lanes[i] - 1) << (i*2); -+ -+ ret = regmap_field_write(csi->lane_map[0], val); -+ if (ret) -+ return ret; -+ ret = regmap_field_write(csi->lane_map[1], val >> 4); -+ if (ret) -+ return ret; -+ -+ return 0; -+} -+ -+static int max_gmsl2_deser_csi_init(struct max_gmsl2_deser_priv *priv, u8 csi_id) ++static int max_gmsl2_deser_csi_init(struct max_gmsl2_deser *priv, u8 csi_id) +{ -+ struct max_gmsl2_deser_csi *csi = &priv->csi[csi_id]; -+ struct v4l2_fwnode_endpoint v4l2_ep = { -+ .bus_type = V4L2_MBUS_CSI2_DPHY -+ }; -+ ++ struct max_gmsl2_csi *csi = &priv->csi[csi_id]; + struct device *dev = &priv->client->dev; + int ret; + @@ -1239,73 +1449,33 @@ index 0000000..8a30110 + if (ret) + return ret; + -+ /* FIXME: take value from dt, written value in multiple of 100MHz -+ * Only the master DPhy might need to be set. -+ */ -+ if (csi->phy_out_freq[0]) { -+ ret = regmap_field_write(csi->phy_out_freq[0], 8); -+ if (ret) -+ return ret; -+ } -+ -+ if (csi->phy_out_freq[1]) { -+ ret = regmap_field_write(csi->phy_out_freq[1], 8); -+ if (ret) -+ return ret; -+ } -+ csi->format.format.width = 1280; -+ csi->format.format.height = 720; -+ csi->format.format.code = MEDIA_BUS_FMT_RGB888_1X24; -+ csi->format.format.field = V4L2_FIELD_NONE; -+ -+ max_gmsl2_deser_subdev_init(&csi->sd, priv, &max_gmsl2_deser_csi_ops, -+ MEDIA_ENT_F_VID_IF_BRIDGE, -+ (csi_id == 0) ? "csia" : "csib"); ++ priv->format.format.width = 1280; ++ priv->format.format.height = 720; ++ priv->format.format.code = MEDIA_BUS_FMT_RGB888_1X24; ++ priv->format.format.field = V4L2_FIELD_NONE; + + /* CSI source enpoint ports start after gmsl2 link */ -+ csi->sd.fwnode = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), ++ csi->ep_fwnode = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), + priv->data->links + csi_id, + 0, 0); ++ if (!csi->ep_fwnode) ++ return -ENODEV; + -+ ret = v4l2_fwnode_endpoint_parse(csi->sd.fwnode, &v4l2_ep); -+ if (ret) { -+ dev_err(&priv->client->dev, "Could not parse v4l2 endpoint\n"); -+ return -EINVAL; -+ } -+ -+ if (v4l2_ep.bus.mipi_csi2.num_data_lanes != 4) { -+ dev_err(&priv->client->dev, "Only 4 data lanes supported\n"); -+ return -EINVAL; -+ } -+ ret = max_gmsl2_csi_phy_init(csi, &v4l2_ep.bus.mipi_csi2); ++ ret = max_gmsl2_csi_phy_init(csi); + if (ret) + return ret; + -+ /* Register internal ops for incremental subdev registration */ -+ csi->sd.internal_ops = &max_gmsl2_deser_csi_internal_ops; -+ -+ csi->pads[0].flags = MEDIA_PAD_FL_SINK; -+ csi->pads[1].flags = MEDIA_PAD_FL_SOURCE; -+ -+ csi->priv = priv; -+ csi->id = csi_id; -+ -+ ret = media_entity_pads_init(&csi->sd.entity, 2, csi->pads); ++ ret = max_gmsl2_csi_get_link_freq(csi, priv->tx_link_freq); + if (ret) + return ret; + -+ ret = max_gmsl2_deser_csi_init_controls(csi); -+ if (ret) -+ goto err_cleanup_media; ++ csi->priv = priv; ++ csi->id = csi_id; + + ret = max_gmsl2_csi_map_pipes(csi); + if (ret) + goto err_cleanup_media; + -+ ret = v4l2_async_register_subdev(&csi->sd); -+ if (ret) -+ goto err_cleanup_media; -+ + return 0; + +err_cleanup_media: @@ -1314,135 +1484,148 @@ index 0000000..8a30110 + return ret; +} + -+static int max_gmsl2_deser_pipe_s_stream(struct v4l2_subdev *sd, int enable) -+{ -+ struct max_gmsl2_deser_pipe *pipe = max_gmsl2_deser_sd_to_pipe(sd); -+ unsigned int val; -+ int ret; ++#define GMSL1_DT_RGB888_OLDI 0 ++#define CONV_GMSL1_EN BIT(1) ++/* BIT(0) RSVD 1 */ ++#define CONV_GMSL_DATATYPE(gmsl1_dt) ( gmsl1_dt << 3 | CONV_GMSL1_EN | BIT(0)) ++static int max_gmsl2_link_set_gmsl1(struct max_gmsl2_deser *priv, u8 link) ++{ ++ const struct max_gmsl2_compat_gmsl1 *gmsl1 = priv->data->gmsl1; ++ int ret; ++ ++ if (!gmsl1) { ++ dev_err(&priv->client->dev, ++ "This device doesn't support gmsl1 mode"); ++ return -EINVAL; + -+ ret = regmap_field_read(pipe->vid_lock, &val); ++ } ++ ++ /* Disable the forward control channel */ ++ ret = regmap_field_write(priv->gmsl1_forward_cc[link], 0); + if (ret) + return ret; -+ printk("starting stream vid lock: %u", val); + -+ return regmap_field_write(pipe->enable, !!enable); -+} ++ /* clear gmsl2 bit for this link */ ++ ret = regmap_field_update_bits(priv->gmsl2_link_mode, ++ BIT(link), 0); ++ if (ret) ++ return ret; + -+static int max_gmsl2_deser_pipe_g_input_status(struct v4l2_subdev *sd, u32 *status) -+{ -+ struct max_gmsl2_deser_pipe *pipe = max_gmsl2_deser_sd_to_pipe(sd); -+ int ret; -+ unsigned int vid_lock; ++ /* We only support HIM capable gmsl1 device */ ++ ret = regmap_field_write(priv->gmsl1_highimm[link], 1); ++ if (ret) ++ return ret; + -+ ret = regmap_field_read(pipe->vid_lock, &vid_lock); ++ /*HACK: DBL=0 DRS=0 BWS=0 RSV=0 HIBW=1 HVEN=0 RSV=0 PXL_CRC=0*/ ++ ret = regmap_write(priv->regmap, 0x0B07, 0x08); ++ if (ret) ++ return ret; ++ ++ /* ++ * Shift video bits to make sure that HS, VS does not appear on ++ * pixel data ++ */ ++ ret = regmap_field_write(priv->gmsl1_shift_vid_hvd[link], 1); + if (ret) + return ret; + -+ *status = vid_lock ? 0 : V4L2_IN_ST_NO_SIGNAL; ++ /* Datatype GMSL1 conversion only support RGB888 OLDI */ ++ ret = regmap_write(priv->regmap, gmsl1->conv_datatype[link], ++ CONV_GMSL_DATATYPE(GMSL1_DT_RGB888_OLDI)); ++ return ret; + -+ return 0; +} + -+static const struct v4l2_subdev_video_ops max_gmsl2_deser_pipe_video_ops = { -+ .g_input_status = max_gmsl2_deser_pipe_g_input_status, -+ .s_stream = max_gmsl2_deser_pipe_s_stream, -+}; -+ -+static const struct v4l2_subdev_pad_ops max_gmsl2_deser_pipe_pad_ops = { -+}; ++static int max_gmsl2_poll_dummy_read(struct max_gmsl2_deser *priv) ++{ ++ unsigned int dummy; ++ int ret, i; + -+static const struct v4l2_subdev_ops max_gmsl2_deser_pipe_ops = { -+ .video = &max_gmsl2_deser_pipe_video_ops, -+ .pad = &max_gmsl2_deser_pipe_pad_ops, -+}; ++ ret = regmap_read(priv->regmap, 0, &dummy); ++ for (i = 0; i < 100 && ret; i++) { ++ usleep_range(2000, 3000); ++ ret = regmap_read(priv->regmap, 0, &dummy); ++ } ++ return ret; ++} + -+static int max_gmsl2_deser_pipe_set_format(struct max_gmsl2_deser_priv *priv, u8 pipe_id, -+ u8 datatype, u8 bpp, u8 vc) ++static int max_gmsl2_link_parse_dt_poc(struct device *dev, u8 nlink) +{ -+ struct max_gmsl2_deser_pipe *pipe = &priv->pipes[pipe_id]; -+ const struct reg_field *l_field; ++ char name[10]; + int ret; + -+ ret = regmap_field_write(pipe->vc, vc); -+ if (ret) -+ return ret; -+ -+ l_field = &priv->data->pipe_dt_l[pipe_id]; -+ ret = max_gmsl2_write_h_l_val(pipe->dt_h, pipe->dt_l, -+ l_field, datatype); ++ snprintf(name, sizeof(name), "port%u-poc", nlink); + -+ if (ret) ++ ret = devm_regulator_get_enable_optional(dev, name); ++ if (ret < 0 && ret != -ENODEV) + return ret; + -+ l_field = &priv->data->pipe_bpp_l[pipe_id]; -+ ret = max_gmsl2_write_h_l_val(pipe->bpp_h, pipe->bpp_l, -+ l_field, bpp); -+ if (ret) -+ return ret; -+ return regmap_field_write(pipe->override_bpp_vc_dt, 1); ++ return 0; +} + -+static int max_gmsl2_deser_pipe_init(struct max_gmsl2_deser_priv *priv, u8 pipe_id, u8 vc) ++ ++static int max_gmsl2_init_links(struct max_gmsl2_deser *priv) +{ -+ struct max_gmsl2_deser_pipe *pipe = &priv->pipes[pipe_id]; ++ struct max_gmsl2_link *link; ++ struct device *dev = &priv->client->dev; ++ struct fwnode_handle *node; ++ bool gmsl1_mode; + int ret; ++ u8 i; + -+ /* -+ * FIXME: Only supporting RGB888 at the moment -+ * set MIPI vc=0 for each pipe -+ */ -+ ret = max_gmsl2_deser_pipe_set_format(priv, pipe_id, -+ GMSL2_MIPI_DT_RGB888, 3 * 8, vc); -+ if (ret) -+ return ret; ++ for_each_link(i, priv) { ++ link = &priv->links[i]; ++ node = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), i, 0, 0); ++ if (!node) ++ continue; + -+ /* FIXME: find an appropriate media type */ -+ max_gmsl2_deser_subdev_init(&pipe->sd, priv, &max_gmsl2_deser_pipe_ops, -+ MEDIA_ENT_F_ATV_DECODER, -+ pipe_to_str(priv, pipe_id)); ++ link->source_ep_fwnode = node; ++ ret = max_gmsl2_link_parse_dt_poc(dev, i); ++ if (ret) { ++ dev_err(dev, "Port%u: failed to enable POC supply: %d\n", ++ i, ret); ++ fwnode_handle_put(node); ++ return ret; ++ } + -+ pipe->pad.flags = MEDIA_PAD_FL_SOURCE; ++ gmsl1_mode = fwnode_property_present(node, "maxim,link-gmsl1-mode"); ++ fwnode_handle_put(node); + -+ ret = media_entity_pads_init(&pipe->sd.entity, -+ 1, &pipe->pad); -+ if (ret) -+ return ret; ++ if (!gmsl1_mode) ++ continue; + -+ ret = v4l2_async_register_subdev(&pipe->sd); -+ if (ret) -+ goto err_cleanup_media; ++ ret = max_gmsl2_link_set_gmsl1(priv, i); ++ if (ret) ++ return ret; ++ if (link->reset_one_shot) { ++ ret = regmap_field_write(link->reset_one_shot, 1); ++ if (ret) ++ return ret; ++ ret = max_gmsl2_poll_dummy_read(priv); ++ if (ret) ++ return ret; ++ } ++ } + + return 0; -+ -+err_cleanup_media: -+ media_entity_cleanup(&pipe->sd.entity); -+ -+ return ret; +} + -+static int max_gmsl2_deser_v4l2_register(struct max_gmsl2_deser_priv *priv) ++static int max_gmsl2_deser_hw_init(struct max_gmsl2_deser *priv) +{ + struct device *dev = &priv->client->dev; -+ u32 stream_id[GMSL2_MAX_PIPE]; + u32 pipe_vc[GMSL2_MAX_PIPE] = {}; + int ret = 0; + int i; + ++ ret = max_gmsl2_init_links(priv); ++ if (ret) ++ return ret; ++ + /* check if we need to override default stream id mapping */ -+ ret = of_property_read_u32_array(dev->of_node, "maxim,pipe-stream-id", -+ stream_id, priv->data->pipes); -+ if (!ret) { -+ for_each_pipe(i, priv) { -+ if (stream_id[i] >= 4) { -+ dev_err(dev, "invalid stream id: %d for pipe %d", -+ stream_id[i], i); -+ return -EINVAL; -+ } -+ ret = regmap_field_write(priv->pipes[i].stream_id, -+ stream_id[i]); -+ if (ret) -+ return ret; -+ } ++ ret = max_gmsl2_init_pipes_stream_id(priv->pipes, priv->data->pipes, dev); ++ if (ret) { ++ dev_err(dev, "fail to set pipe stream id"); ++ return ret; + } + + /* check if we need to override default pipe virtual channel */ @@ -1453,14 +1636,8 @@ index 0000000..8a30110 + return ret; + } + -+ for_each_pipe(i, priv) { -+ ret = max_gmsl2_deser_pipe_init(priv, i, pipe_vc[i]); -+ if (ret) -+ return ret; -+ } -+ -+ if (priv->csi_enable) { -+ ret = regmap_field_write(priv->csi_enable, 0); ++ if (priv->csi_force_enable) { ++ ret = regmap_field_write(priv->csi_force_enable, 0); + if (ret) + return ret; + } @@ -1470,107 +1647,228 @@ index 0000000..8a30110 + if (ret) + return ret; + } ++ + for_each_csi(i, priv) { + ret = max_gmsl2_deser_csi_init(priv, i); -+ if (ret) ++ if (ret && ret != -ENODEV) + return ret; + } + + return 0; +} + -+#define GMSL1_DT_RGB888_OLDI 0 -+#define CONV_GMSL1_EN BIT(1) -+/* BIT(0) RSVD 1 */ -+#define CONV_GMSL_DATATYPE(gmsl1_dt) ( gmsl1_dt << 3 | CONV_GMSL1_EN | BIT(0)) -+static int max_gmsl2_link_set_gmsl1(struct max_gmsl2_deser_priv *priv, u8 link) ++struct max_gmsl2_link_asd { ++ struct v4l2_async_subdev base; ++ struct max_gmsl2_link *link; ++}; ++ ++static inline struct max_gmsl2_link_asd *to_max_gmsl2_link_asd(struct v4l2_async_subdev *asd) +{ -+ const struct max_gmsl2_compat_gmsl1 *gmsl1 = priv->data->gmsl1; -+ int ret; ++ return container_of(asd, struct max_gmsl2_link_asd, base); ++} + -+ if (!gmsl1) { -+ dev_err(&priv->client->dev, -+ "This device doesn't support gmsl1 mode"); -+ return -EINVAL; ++static int max_gmsl2_deser_notify_bound(struct v4l2_async_notifier *notifier, ++ struct v4l2_subdev *source_subdev, ++ struct v4l2_async_subdev *asd) ++{ ++ struct v4l2_subdev *sd = notifier->sd; ++ struct max_gmsl2_deser *priv = sd_to_max_gmsl2_deser(sd); ++ struct max_gmsl2_link *link = to_max_gmsl2_link_asd(asd)->link; ++ struct device *dev = &priv->client->dev; ++ int ret; + -+ } -+ /* HACK disable the forward control channel */ -+ ret = regmap_field_write(priv->gmsl1_forward_cc[link], 0); -+ if (ret) -+ return ret; -+ /* clear gmsl2 bit for this link */ -+ ret = regmap_field_update_bits(priv->gmsl2_link_mode, -+ BIT(link), 0); -+ if (ret) ++ ret = media_entity_get_fwnode_pad(&source_subdev->entity, ++ source_subdev->fwnode, ++ MEDIA_PAD_FL_SOURCE); ++ if (ret < 0) + return ret; + -+ /* We only support HIM capable gmsl1 device */ -+ ret = regmap_field_write(priv->gmsl1_highimm[link], 1); -+ if (ret) -+ return ret; ++ link->source_sd = source_subdev; ++ link->source_sd_pad = ret; + -+ /*HACK: DBL=0 DRS=0 BWS=0 RSV=0 HIBW=1 HVEN=0 RSV=0 PXL_CRC=0*/ -+ ret = regmap_write(priv->regmap, 0x0B07, 0x08); -+ if (ret) ++ ret = media_create_pad_link(&source_subdev->entity, link->source_sd_pad, ++ &priv->sd.entity, 0, ++ MEDIA_LNK_FL_ENABLED | ++ MEDIA_LNK_FL_IMMUTABLE); ++ if (ret) { ++ dev_err(dev, "Unable to link %s:%u -> %s:0\n", ++ source_subdev->name, link->source_sd_pad, ++ priv->sd.name); + return ret; -+ /* Datatype GMSL1 conversion only support RGB888 OLDI */ -+ ret = regmap_write(priv->regmap, gmsl1->conv_datatype[link], -+ CONV_GMSL_DATATYPE(GMSL1_DT_RGB888_OLDI)); -+ return ret; ++ } + ++ return 0; +} + -+static int max_gmsl2_poll_dummy_read(struct max_gmsl2_deser_priv *priv) ++static void max_gmsl2_deser_notify_unbind(struct v4l2_async_notifier *notifier, ++ struct v4l2_subdev *subdev, ++ struct v4l2_async_subdev *asd) +{ -+ unsigned int dummy; -+ int ret, i; ++ struct max_gmsl2_link *link = to_max_gmsl2_link_asd(asd)->link; + -+ ret = regmap_read(priv->regmap, 0, &dummy); -+ for (i = 0; i < 100 && ret; i++) { -+ usleep_range(2000, 3000); -+ ret = regmap_read(priv->regmap, 0, &dummy); -+ } -+ return ret; ++ link->source_sd = NULL; +} + -+static int max_gmsl2_init_links(struct max_gmsl2_deser_priv *priv) ++static const struct v4l2_async_notifier_operations max_gmsl2_deser_notify_ops = ++{ ++ .bound = max_gmsl2_deser_notify_bound, ++ .unbind = max_gmsl2_deser_notify_unbind, ++}; ++ ++static int max_gmsl2_deser_v4l2_notifier_register(struct max_gmsl2_deser *priv) +{ + struct device *dev = &priv->client->dev; -+ struct fwnode_handle *node; -+ bool gmsl1_mode; ++ struct max_gmsl2_link *link; ++ struct max_gmsl2_link_asd *asd; ++ unsigned int i; + int ret; -+ u8 link; + -+ for (link = 0; link < priv->data->links; link++) { -+ node = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), link, 0, -+ FWNODE_GRAPH_DEVICE_DISABLED); -+ if (!node) ++ v4l2_async_nf_init(&priv->notifier); ++ ++ for_each_link(i, priv) { ++ link = &priv->links[i]; ++ if (!link->source_ep_fwnode) ++ /* This link is not connected */ + continue; -+ gmsl1_mode = fwnode_property_present(node, "maxim,link-gmsl1-mode"); -+ fwnode_handle_put(node); -+ if (gmsl1_mode) { -+ ret = max_gmsl2_link_set_gmsl1(priv, link); -+ if (ret) -+ return ret; -+ if (priv->reset_one_shot_link[link]) { -+ ret = regmap_field_write( -+ priv->reset_one_shot_link[link], 1); -+ if (ret) -+ return ret; -+ ret = max_gmsl2_poll_dummy_read(priv); -+ if (ret) -+ return ret; -+ } ++ ++ asd = v4l2_async_nf_add_fwnode_remote(&priv->notifier, ++ link->source_ep_fwnode, ++ struct max_gmsl2_link_asd); ++ ++ if (IS_ERR(asd)) { ++ dev_err(dev, "Fail to add subdev notifier for link %d: %pe", ++ i, asd); ++ v4l2_async_nf_cleanup(&priv->notifier); ++ return PTR_ERR(asd); ++ } ++ asd->link = link; ++ } ++ ++ priv->notifier.ops = &max_gmsl2_deser_notify_ops; ++ ++ ret = v4l2_async_subdev_nf_register(&priv->sd, &priv->notifier); ++ if (ret) { ++ v4l2_async_nf_cleanup(&priv->notifier); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void max_gmsl2_deser_v4l2_notifier_unregister(struct max_gmsl2_deser *priv) ++{ ++ v4l2_async_nf_unregister(&priv->notifier); ++ v4l2_async_nf_cleanup(&priv->notifier); ++} ++ ++static int max_gmsl2_deser_create_subdev(struct max_gmsl2_deser *priv) ++{ ++ struct device *dev = &priv->client->dev; ++ unsigned int i; ++ int ret; ++ ++ v4l2_i2c_subdev_init(&priv->sd, priv->client, &max_gmsl2_deser_subdev_ops); ++ v4l2_ctrl_handler_init(&priv->ctrl_handler, 1); ++ priv->sd.ctrl_handler = &priv->ctrl_handler; ++ ++ if (priv->data->patgen_base_addr[0]) { ++ v4l2_ctrl_new_std_menu_items(&priv->ctrl_handler, ++ &max_gmsl2_deser_ctrl_ops, ++ V4L2_CID_TEST_PATTERN, ++ ARRAY_SIZE(max_gmsl2_test_pattern) - 1, ++ 0, 0, max_gmsl2_test_pattern); ++ if (priv->ctrl_handler.error) { ++ ret = priv->ctrl_handler.error; ++ goto err_free_ctrl; + } + } + ++ v4l2_ctrl_new_int_menu(&priv->ctrl_handler, NULL, V4L2_CID_LINK_FREQ, ++ ARRAY_SIZE(priv->tx_link_freq) - 1, 0, ++ priv->tx_link_freq); ++ ++ priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ++ priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; ++ priv->sd.entity.ops = &max_gmsl2_deser_entity_ops; ++ ++ for (i = 0; i < priv->data->links + priv->data->csi; i++) { ++ priv->pads[i].flags = (i < priv->data->links) ? ++ MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; ++ } ++ ++ ret = media_entity_pads_init(&priv->sd.entity, ++ priv->data->links + ++ priv->data->csi, ++ priv->pads); ++ if (ret) ++ goto err_free_ctrl; ++ ++ ret = max_gmsl2_deser_v4l2_notifier_register(priv); ++ if (ret) { ++ dev_err(dev, "v4l2 subdev notifier register failed: %d\n", ret); ++ goto err_entity_cleanup; ++ } ++ ++ ret = v4l2_async_register_subdev(&priv->sd); ++ if (ret) { ++ dev_err(dev, "v4l2_async_register_subdev error: %d\n", ret); ++ goto err_unreg_notif; ++ } ++ ++ return 0; ++ ++err_unreg_notif: ++ v4l2_async_nf_unregister(&priv->notifier); ++err_entity_cleanup: ++ media_entity_cleanup(&priv->sd.entity); ++err_free_ctrl: ++ v4l2_ctrl_handler_free(&priv->ctrl_handler); ++ ++ return ret; ++} ++ ++static void max_gmsl2_deser_destroy_subdev(struct max_gmsl2_deser *priv) ++{ ++ max_gmsl2_deser_v4l2_notifier_unregister(priv); ++ v4l2_async_unregister_subdev(&priv->sd); ++ ++ v4l2_subdev_cleanup(&priv->sd); ++ ++ media_entity_cleanup(&priv->sd.entity); ++ v4l2_ctrl_handler_free(&priv->ctrl_handler); ++} ++ ++static int max_gmsl2_i2c_mux_select(struct i2c_mux_core *mux, u32 chan) ++{ ++ return 0; ++} ++ ++static int max_gmsl2_i2c_mux_init(struct max_gmsl2_deser *priv) ++{ ++ priv->mux = i2c_mux_alloc(priv->client->adapter, &priv->client->dev, ++ 1, 0, I2C_MUX_LOCKED | I2C_MUX_GATE, ++ max_gmsl2_i2c_mux_select, NULL); ++ if (!priv->mux) ++ return -ENOMEM; ++ ++ return i2c_mux_add_adapter(priv->mux, 0, 0, 0); ++} ++ ++static int max96724_force_6gbps(struct max_gmsl2_deser *priv) ++{ ++ /* 6 Gbps for LINK A-B-C-D */ ++ regmap_write(priv->regmap, 0x10, 0x22); ++ regmap_write(priv->regmap, 0x11, 0x22); ++ /* Reset one shot the links */ ++ regmap_write(priv->regmap, 0x18, 0xf); ++ + return 0; +} + +static int max_gmsl2_deser_probe(struct i2c_client *client) +{ -+ struct max_gmsl2_deser_priv *priv; -+ unsigned int dev_id, dev_rev; ++ struct max_gmsl2_deser *priv; ++ unsigned int dev_id, dev_rev, val; + int i; + int ret; + @@ -1620,28 +1918,56 @@ index 0000000..8a30110 + + dev_info(&client->dev, "GMSL2 deserializer device_id: 0x%02x rev: 0x%01x", dev_id, dev_rev); + i2c_set_clientdata(client, priv); -+ ret = max_gmsl2_init_links(priv); ++ ++ /* Save tunnel mode we might have to disable it later for pattern generator */ ++ if (priv->tunnel_mode) { ++ ret = regmap_field_read(priv->tunnel_mode, &val); ++ if (ret) ++ return ret; ++ ++ priv->tunnel_mode_saved = !!val; ++ } ++ ++ //HACK: either fix CFG1 or parse the link rate in the device tree ++ if (IS_MAX96724(priv)) ++ max96724_force_6gbps(priv); ++ ++ ret = max_gmsl2_gpiochip_probe(&priv->gpio, &client->dev, priv->regmap, ++ priv->data->gpio_base, ++ priv->data->ngpios); ++ if (ret) { ++ dev_err(&client->dev, "Failed to init gpiochip\n"); ++ return ret; ++ } ++ ++ ret = max_gmsl2_deser_hw_init(priv); + if (ret) + return ret; + -+ ret = max_gmsl2_deser_v4l2_register(priv); ++ ret = max_gmsl2_i2c_mux_init(priv); + if (ret) + return ret; + -+ max_gmsl2_deser_debug_init(priv); -+ return ret; -+} ++ ret = max_gmsl2_deser_create_subdev(priv); ++ if (ret) ++ return ret; ++ ++ return ret; ++} + +static void max_gmsl2_deser_remove(struct i2c_client *client) +{ -+ struct max_gmsl2_deser_priv *priv = i2c_get_clientdata(client); -+ max_gmsl2_deser_debug_remove(priv); ++ struct max_gmsl2_deser *priv = i2c_get_clientdata(client); ++ max_gmsl2_deser_destroy_subdev(priv); ++ gpiod_set_value_cansleep(priv->gpiod_pwdn, 0); ++ +}; + +static const struct of_device_id max_gmsl2_deser_of_ids[] = { -+ { .compatible = "maxim,max96934", .data = &max96934_data }, -+ { .compatible = "maxim,max96914", .data = &max96914_data }, -+ { .compatible = "maxim,max96714", .data = &max96714_data }, ++ { .compatible = "maxim,max96934", .data = &max96934_data }, ++ { .compatible = "maxim,max96914", .data = &max96914_data }, ++ { .compatible = "maxim,max96724", .data = &max96914_data }, ++ { .compatible = "maxim,max96714f", .data = &max96714f_data }, + { } +}; +MODULE_DEVICE_TABLE(of, max_gmsl2_deser_of_ids); @@ -1660,12 +1986,225 @@ index 0000000..8a30110 +MODULE_DESCRIPTION("Maxim GMSL2 Deserializer Driver"); +MODULE_AUTHOR("Julien Massot <julien.massot@collabora.com>"); +MODULE_LICENSE("GPL"); +diff --git a/drivers/media/i2c/max-gmsl2-gpio.c b/drivers/media/i2c/max-gmsl2-gpio.c +new file mode 100644 +index 00000000..ca473df +--- /dev/null ++++ b/drivers/media/i2c/max-gmsl2-gpio.c +@@ -0,0 +1,207 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Maxim GMSL2 Deserializer Driver ++ * ++ * Copyright (C) 2023 Collabora Ltd. ++ */ ++ ++#include <linux/module.h> ++#include <linux/of_device.h> ++#include <linux/regmap.h> ++#include <linux/gpio/consumer.h> ++#include <linux/gpio/driver.h> ++#include <linux/gpio/max-gmsl2-gpio.h> ++ ++#define GMSL2_MAX_GPIO 0x1f ++#define GMSL2_GPIO_OUT BIT(4) ++#define GMSL2_GPIO_IN BIT(3) ++#define GMSL2_GPIO_RX_EN BIT(2) ++#define GMSL2_GPIO_TX_EN BIT(1) ++#define GMSL2_GPIO_OUT_DIS BIT(0) ++ ++#define GMSL2_GPIO_REG_A(chip, gpio) (chip->base + gpio * 3) ++ ++struct max_gmsl2_gpiochip { ++ struct regmap *regmap; ++ u16 base; ++}; ++ ++static int max_gmsl2_gpio_set_bit(struct max_gmsl2_gpiochip *chip, ++ unsigned int offset, u8 bit, bool en) ++{ ++ return regmap_update_bits(chip->regmap, ++ GMSL2_GPIO_REG_A(chip, offset), ++ bit, en ? bit : 0); ++} ++ ++static int max_gmsl2_gpio_get_bits(struct max_gmsl2_gpiochip *chip, ++ unsigned int offset, u8 mask) ++{ ++ unsigned int val; ++ int ret; ++ ++ ret = regmap_read(chip->regmap, GMSL2_GPIO_REG_A(chip, offset), &val); ++ if (ret) ++ return ret; ++ return val & mask; ++} ++ ++static int max_gmsl2_gpiochip_rx_en(struct max_gmsl2_gpiochip *chip, ++ unsigned int offset, bool en) ++{ ++ return max_gmsl2_gpio_set_bit(chip, offset, GMSL2_GPIO_RX_EN, en); ++} ++ ++static int max_gmsl2_gpiochip_tx_en(struct max_gmsl2_gpiochip *chip, ++ unsigned int offset, bool en) ++{ ++ return max_gmsl2_gpio_set_bit(chip, offset, GMSL2_GPIO_TX_EN, en); ++} ++ ++static int max_gmsl2_gpiochip_get(struct gpio_chip *gpiochip, ++ unsigned int offset) ++{ ++ struct max_gmsl2_gpiochip *chip = gpiochip_get_data(gpiochip); ++ unsigned int val; ++ int ret; ++ ++ ret = regmap_read(chip->regmap, GMSL2_GPIO_REG_A(chip, offset), &val); ++ if (ret) ++ return ret; ++ if (val & GMSL2_GPIO_OUT_DIS) ++ return !!(val & GMSL2_GPIO_IN); ++ else ++ return !!(val & GMSL2_GPIO_OUT); ++} ++ ++static void max_gmsl2_gpiochip_set(struct gpio_chip *gpiochip, ++ unsigned offset, int value) ++{ ++ struct max_gmsl2_gpiochip *chip = gpiochip_get_data(gpiochip); ++ ++ max_gmsl2_gpio_set_bit(chip, offset, GMSL2_GPIO_OUT, !!value); ++} ++ ++static int max_gmsl2_gpio_get_direction(struct gpio_chip *gpiochip, unsigned int offset) ++{ ++ struct max_gmsl2_gpiochip *chip = gpiochip_get_data(gpiochip); ++ int ret; ++ ++ ret = max_gmsl2_gpio_get_bits(chip, offset, GMSL2_GPIO_OUT_DIS); ++ if (ret < 0) ++ return ret; ++ ++ return !!ret; ++} ++ ++static int max_gmsl2_gpio_direction_out(struct gpio_chip *gpiochip, unsigned int offset, ++ int value) ++{ ++ struct max_gmsl2_gpiochip *chip = gpiochip_get_data(gpiochip); ++ ++ return regmap_update_bits(chip->regmap, GMSL2_GPIO_REG_A(chip, offset), ++ GMSL2_GPIO_OUT_DIS | GMSL2_GPIO_OUT, ++ value ? GMSL2_GPIO_OUT : 0); ++} ++ ++static int max_gmsl2_gpio_direction_in(struct gpio_chip *gpiochip, unsigned int offset) ++{ ++ struct max_gmsl2_gpiochip *chip = gpiochip_get_data(gpiochip); ++ ++ return max_gmsl2_gpio_set_bit(chip, offset, GMSL2_GPIO_OUT_DIS, true); ++} ++ ++int max_gmsl2_gpiochip_probe(struct gpio_chip *gpio, struct device *dev, ++ struct regmap *regmap, u16 base, u8 ngpios) ++{ ++ struct max_gmsl2_gpiochip *chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); ++ struct gpio_descs *output_gpios; ++ struct gpio_descs *input_gpios; ++ u32 fw_gpio[GMSL2_MAX_GPIO]; ++ int cnt, ret, i; ++ ++ chip->regmap = regmap; ++ chip->base = base; ++ ++ gpio->label = dev_name(dev); ++ gpio->parent = dev; ++ gpio->owner = THIS_MODULE; ++ gpio->ngpio = ngpios; ++ gpio->base = -1; ++ gpio->get_direction = max_gmsl2_gpio_get_direction; ++ gpio->direction_input = max_gmsl2_gpio_direction_in; ++ gpio->direction_output = max_gmsl2_gpio_direction_out; ++ gpio->set = max_gmsl2_gpiochip_set; ++ gpio->get = max_gmsl2_gpiochip_get; ++ gpio->can_sleep = true; ++ ++ for (i = 0; i < gpio->ngpio; i++) { ++ ret = max_gmsl2_gpiochip_rx_en(chip, i, 0); ++ if (ret) ++ return ret; ++ ret = max_gmsl2_gpiochip_tx_en(chip, i, 0); ++ if (ret) ++ return ret; ++ } ++ ++ cnt = of_property_count_u32_elems(dev->of_node, "maxim,gpio-forward-rx"); ++ if (cnt > 0) { ++ ret = of_property_read_u32_array(dev->of_node, "maxim,gpio-forward-rx", ++ fw_gpio, cnt); ++ if (ret) { ++ dev_err(dev, "Fail to read maxim,gpio-forward-rx property"); ++ return ret; ++ } ++ for (i = 0; i < cnt; i++) { ++ if (fw_gpio[i] > gpio->ngpio) { ++ dev_err(dev, "GPIO %u is out of range", fw_gpio[i]); ++ return -EINVAL; ++ } ++ ++ ret = max_gmsl2_gpiochip_rx_en(chip, fw_gpio[i], true); ++ if (ret) { ++ dev_err(dev, "Fail to enable gpio rx forwarding %u", fw_gpio[i]); ++ return ret; ++ } ++ } ++ } ++ ++ cnt = of_property_count_u32_elems(dev->of_node, "maxim,gpio-forward-tx"); ++ if (cnt > 0) { ++ ret = of_property_read_u32_array(dev->of_node, "maxim,gpio-forward-tx", ++ fw_gpio, cnt); ++ if (ret) { ++ dev_err(dev, "Fail to read maxim,gpio-forward-tx property"); ++ return ret; ++ } ++ for (i = 0; i < cnt; i++) { ++ if (fw_gpio[i] > gpio->ngpio) { ++ dev_err(dev, "GPIO %u is out of range", fw_gpio[i]); ++ return -EINVAL; ++ } ++ ++ ret = max_gmsl2_gpiochip_tx_en(chip, fw_gpio[i], true); ++ if (ret) { ++ dev_err(dev, "Fail to enable gpio tx forwarding %u", fw_gpio[i]); ++ return ret; ++ } ++ } ++ } ++ ++ ret = devm_gpiochip_add_data(dev, gpio, chip); ++ if (ret) { ++ dev_err(dev, "Unable to create gpio_chip\n"); ++ return ret; ++ } ++ ++ /* If we have some gpios reserved get them here */ ++ output_gpios = devm_gpiod_get_array_optional(dev, "output", GPIOD_OUT_LOW); ++ input_gpios = devm_gpiod_get_array_optional(dev, "input", GPIOD_OUT_LOW); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(max_gmsl2_gpiochip_probe); ++ ++MODULE_DESCRIPTION("Maxim GMSL2 GPIO Driver"); ++MODULE_AUTHOR("Julien Massot <julien.massot@collabora.com>"); ++MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/max-gmsl2-patgen.c b/drivers/media/i2c/max-gmsl2-patgen.c new file mode 100644 -index 0000000..172fbde +index 00000000..92b869e --- /dev/null +++ b/drivers/media/i2c/max-gmsl2-patgen.c -@@ -0,0 +1,192 @@ +@@ -0,0 +1,175 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Maxim GMSL2 Deserializer Driver @@ -1675,7 +2214,7 @@ index 0000000..172fbde + * Copyright (C) 2022 Collabora Ltd. + */ + -+#include "max-gmsl2-patgen.h" ++#include <media/gmsl2/max-gmsl2-patgen.h> + +static int max_gmsl2_write_bulk(struct regmap *regmap, uint16_t reg, + unsigned int val, uint8_t val_count) @@ -1689,31 +2228,25 @@ index 0000000..172fbde + return regmap_bulk_write(regmap, reg, values, val_count); +} + -+#define VTG_CTRL 0 ++/* VTG control */ +#define VTG_MODE_FREE_RUNNING 0x3 -+#define VTG_DE_INV BIT(2) -+#define VTG_HS_INV BIT(3) -+#define VTG_VS_INV BIT(4) -+#define VTG_GEN_DE BIT(5) -+#define VTG_GEN_HS BIT(6) -+#define VTG_GEN_VS BIT(7) -+#define VTG_VS_DLY 0x2 -+#define VTG_VS_HIGH 0x5 -+#define VTG_VS_LOW 0x8 -+#define VTG_V2H 0xb -+#define VTG_HS_HIGH 0xe -+#define VTG_HS_LOW 0x10 -+#define VTG_HS_CNT 0x12 ++ ++#define VTG_VS_DLY(patgen) (patgen->vtg_timing) ++#define VTG_VS_HIGH(patgen) (patgen->vtg_timing + 3) ++#define VTG_VS_LOW(patgen) (patgen->vtg_timing + 6) ++#define VTG_V2H(patgen) (patgen->vtg_timing + 9) ++#define VTG_HS_HIGH(patgen) (patgen->vtg_timing + 0xc) ++#define VTG_HS_LOW(patgen) (patgen->vtg_timing + 0xe) ++#define VTG_HS_CNT(patgen) (patgen->vtg_timing + 0x10) +/* VS edge to the rising edge of the first DE in terms of PCLK cycles */ -+#define VTG_V2D 0x14 -+#define VTG_DE_HIGH 0x17 -+#define VTG_DE_LOW 0x19 -+#define VTG_DE_CNT 0x1b ++#define VTG_V2D(patgen) (patgen->vtg_timing + 0x12) ++#define VTG_DE_HIGH(patgen) (patgen->vtg_timing + 0x15) ++#define VTG_DE_LOW(patgen) (patgen->vtg_timing + 0x17) ++#define VTG_DE_CNT(patgen) (patgen->vtg_timing + 0x19) + -+int max_gmsl2_vtg_enable(struct regmap *regmap, uint16_t base_addr, ++int max_gmsl2_vtg_enable(struct max_gmsl2_patgen *patgen, struct regmap *regmap, + const struct v4l2_bt_timings *bt) +{ -+ /*1280 * 720 @ 60 fps 74.25 MHz pclk*/ + const u32 h_active = bt->width; + const u32 h_fp = bt->hfrontporch; + const u32 h_sw = bt->hsync; @@ -1728,162 +2261,1593 @@ index 0000000..172fbde + + int ret; + -+ ret = max_gmsl2_write_bulk(regmap, base_addr + VTG_VS_DLY, ++ ret = max_gmsl2_write_bulk(regmap, VTG_VS_DLY(patgen), + 0, 3); + if (ret) + return ret; + -+ ret = max_gmsl2_write_bulk(regmap, base_addr + VTG_VS_HIGH, ++ ret = max_gmsl2_write_bulk(regmap, VTG_VS_HIGH(patgen), + v_sw * h_tot, 3); + if (ret) + return ret; + -+ ret = max_gmsl2_write_bulk(regmap, base_addr + VTG_VS_LOW, ++ ret = max_gmsl2_write_bulk(regmap, VTG_VS_LOW(patgen), + (v_active + v_fp + v_bp) * h_tot, 3); + if (ret) + return ret; + -+ ret = max_gmsl2_write_bulk(regmap, base_addr + VTG_V2H, -+ 0, 3); ++ ret = max_gmsl2_write_bulk(regmap, VTG_V2H(patgen), 0, 3); + if (ret) + return ret; + -+ ret = max_gmsl2_write_bulk(regmap, base_addr + VTG_HS_HIGH, h_sw, 2); ++ ret = max_gmsl2_write_bulk(regmap, VTG_HS_HIGH(patgen), h_sw, 2); + if (ret) + return ret; + -+ ret = max_gmsl2_write_bulk(regmap, base_addr + VTG_HS_LOW, ++ ret = max_gmsl2_write_bulk(regmap, VTG_HS_LOW(patgen), + h_active + h_fp + h_bp, 2); + if (ret) + return ret; + -+ ret = max_gmsl2_write_bulk(regmap, base_addr + VTG_HS_CNT, -+ v_tot, 2); ++ ret = max_gmsl2_write_bulk(regmap, VTG_HS_CNT(patgen), v_tot, 2); + if (ret) + return ret; + -+ ret = max_gmsl2_write_bulk(regmap, base_addr + VTG_V2D, ++ ret = max_gmsl2_write_bulk(regmap, VTG_V2D(patgen), + h_tot * (v_sw + v_bp) + (h_sw + h_bp), 3); + if (ret) + return ret; + -+ ret = max_gmsl2_write_bulk(regmap, base_addr + VTG_DE_HIGH, ++ ret = max_gmsl2_write_bulk(regmap, VTG_DE_HIGH(patgen), + h_active, 2); + if (ret) + return ret; + -+ ret = max_gmsl2_write_bulk(regmap, base_addr + VTG_DE_LOW, ++ ret = max_gmsl2_write_bulk(regmap, VTG_DE_LOW(patgen), + h_fp + h_sw + h_bp, 2); + if (ret) + return ret; + -+ ret = max_gmsl2_write_bulk(regmap, base_addr + VTG_DE_CNT, ++ ret = max_gmsl2_write_bulk(regmap, VTG_DE_CNT(patgen), + v_active, 2); + if (ret) + return ret; + -+ return regmap_write(regmap, base_addr + VTG_CTRL, -+ VTG_GEN_VS | VTG_GEN_HS | VTG_GEN_DE | -+ VTG_VS_INV | VTG_HS_INV | -+ VTG_MODE_FREE_RUNNING); ++ ret = regmap_field_write(patgen->vtg_mode, VTG_MODE_FREE_RUNNING); ++ if (ret) ++ return ret; ++ ++ return regmap_field_write(patgen->gen_vs_hs_de, 7); +} ++EXPORT_SYMBOL_GPL(max_gmsl2_vtg_enable); + -+int max_gmsl2_vtg_disable(struct regmap *regmap, uint16_t base_addr) ++int max_gmsl2_vtg_disable(struct max_gmsl2_patgen *patgen, struct regmap *regmap) +{ -+ return regmap_write(regmap, base_addr + VTG_CTRL, 0); ++ return regmap_field_write(patgen->gen_vs_hs_de, 0); +} ++EXPORT_SYMBOL_GPL(max_gmsl2_vtg_disable); + -+#define VPG_CTRL 1 -+#define VPG_MODE_CHKB BIT(4) -+#define VPG_MODE_GRAD BIT(5) -+#define VPG_MODE_MASK (VPG_MODE_CHKB | VPG_MODE_GRAD) -+#define VPG_CHKB_COLOR_A 0x1e -+#define VPG_CHKB_COLOR_B 0x21 -+#define VPG_CHKB_RPT_CNT_A 0x24 -+#define VPG_CHKB_RPT_CNT_B 0x25 -+#define VPG_CHKB_RPT_LINES 0x26 -+/* Gradient mode increment amount. -+ * Increment amount is the reg value / 4. -+ */ -+#define VPG_GRAD_INCR 0x1d ++#define VPG_MODE_CHKB BIT(0) ++#define VPG_MODE_GRAD BIT(1) + -+int max_gmsl2_vpg_enable(struct regmap *regmap, uint16_t base_addr, ++#define VPG_GRAD_INCR(patgen) (patgen->vpg_patterns) ++#define VPG_CHKB_COLOR_A(patgen) (patgen->vpg_patterns + 1) ++#define VPG_CHKB_COLOR_B(patgen) (patgen->vpg_patterns + 4) ++#define VPG_CHKB_RPT_CNT_A(patgen) (patgen->vpg_patterns + 7) ++#define VPG_CHKB_RPT_CNT_B(patgen) (patgen->vpg_patterns + 8) ++#define VPG_CHKB_ALT(patgen) (patgen->vpg_patterns + 9) ++ ++int max_gmsl2_vpg_enable(struct max_gmsl2_patgen *patgen, struct regmap *regmap, + enum max_gmsl2_vpg_mode mode) +{ + int ret; + + if (mode == MAX_GMSL2_VPG_CHECKERBOARD) { + /* Set checkerboard pattern size. */ -+ ret = regmap_write(regmap, -+ base_addr + VPG_CHKB_RPT_CNT_A, 0x3c); ++ ret = regmap_write(regmap, VPG_CHKB_RPT_CNT_A(patgen), 0x3c); + if (ret) + return ret; -+ ret = regmap_write(regmap, -+ base_addr + VPG_CHKB_RPT_CNT_B, 0x3c); ++ ret = regmap_write(regmap, VPG_CHKB_RPT_CNT_B(patgen), 0x3c); + if (ret) + return ret; -+ ret = regmap_write(regmap, -+ base_addr + VPG_CHKB_RPT_LINES, 0x3c); ++ ret = regmap_write(regmap, VPG_CHKB_ALT(patgen), 0x3c); + if (ret) + return ret; + /* Set checkerboard pattern colors. */ -+ ret = max_gmsl2_write_bulk(regmap, -+ base_addr + VPG_CHKB_COLOR_A, ++ ret = max_gmsl2_write_bulk(regmap, VPG_CHKB_COLOR_A(patgen), + 0xfecc00, 3); + if (ret) + return ret; -+ ret = max_gmsl2_write_bulk(regmap, -+ base_addr + VPG_CHKB_COLOR_B, ++ ret = max_gmsl2_write_bulk(regmap, VPG_CHKB_COLOR_B(patgen), + 0x006aa7, 3); + if (ret) + return ret; -+ -+ ret = regmap_update_bits(regmap, -+ base_addr + VPG_CTRL, VPG_MODE_MASK, -+ VPG_MODE_CHKB); ++ ret = regmap_field_write(patgen->vpg_mode, VPG_MODE_CHKB); + } else { + /* Set gradient increment. */ -+ ret = regmap_write(regmap, base_addr + VPG_GRAD_INCR, 0x10); ++ ret = regmap_write(regmap, VPG_GRAD_INCR(patgen), 0x10); + if (ret) + return ret; -+ ret = regmap_update_bits(regmap, base_addr + VPG_CTRL, -+ VPG_MODE_MASK, VPG_MODE_GRAD); ++ ret = regmap_field_write(patgen->vpg_mode, VPG_MODE_GRAD); + } + + return ret; +} ++EXPORT_SYMBOL_GPL(max_gmsl2_vpg_enable); + -+int max_gmsl2_vpg_disable(struct regmap *regmap, uint16_t base_addr) ++int max_gmsl2_vpg_disable(struct max_gmsl2_patgen *patgen) +{ -+ return regmap_update_bits(regmap, base_addr + VPG_CTRL, -+ VPG_MODE_MASK, 0); ++ return regmap_field_write(patgen->vpg_mode, 0); +} -diff --git a/drivers/media/i2c/max-gmsl2-patgen.h b/drivers/media/i2c/max-gmsl2-patgen.h ++EXPORT_SYMBOL_GPL(max_gmsl2_vpg_disable); +diff --git a/drivers/media/i2c/max-gmsl2-ser.c b/drivers/media/i2c/max-gmsl2-ser.c new file mode 100644 -index 0000000..11d4b72 +index 00000000..7673e45 --- /dev/null -+++ b/drivers/media/i2c/max-gmsl2-patgen.h -@@ -0,0 +1,23 @@ ++++ b/drivers/media/i2c/max-gmsl2-ser.c +@@ -0,0 +1,992 @@ +// SPDX-License-Identifier: GPL-2.0 +/* -+ * Maxim GMSL2 Deserializer Driver ++ * Friver for Maxim GMSL2 video serializer + * -+ * Copyright (C) 2022 Collabora Ltd. ++ * Copyright (C) 2023 Collabora Ltd. + */ + ++#include <linux/clk-provider.h> ++#include <linux/debugfs.h> ++#include <linux/gpio/max-gmsl2-gpio.h> ++#include <linux/i2c-mux.h> ++#include <linux/i2c.h> ++#include <linux/mod_devicetable.h> ++#include <linux/module.h> ++#include <linux/of_device.h> +#include <linux/regmap.h> -+#include <linux/types.h> -+#include <linux/videodev2.h> ++#include <linux/v4l2-dv-timings.h> ++#include <media/mipi-csi2.h> ++#include <media/v4l2-fwnode.h> ++#include <media/v4l2-subdev.h> ++#include <media/gmsl2/max-gmsl2-patgen.h> + -+enum max_gmsl2_vpg_mode ++#include "gmsl2-def.h" ++ ++#define GMSL_SER_MAX_PIPE 4 ++#define GMSL_SER_MAX_CSI_LANES 4 ++ ++struct max_gmsl2_ser { ++ struct i2c_client *client; ++ const struct max_gmsl2_data *data; ++ struct max_gmsl2_pipe pipes[GMSL_SER_MAX_PIPE]; ++ struct max_gmsl2_csi csi; ++ struct max_gmsl2_link link; ++ struct max_gmsl2_patgen patgen; ++ struct v4l2_subdev_format format; ++ struct gpio_chip gpio; ++ struct regmap_field *dev_rev; ++ struct regmap *regmap; ++ struct dentry *dbg_dir; ++ struct v4l2_subdev sd; ++ struct media_pad pads[2]; ++ struct v4l2_async_notifier notifier; ++ struct v4l2_async_subdev asd; ++ struct v4l2_subdev *source_sd; ++ u16 source_sd_pad; ++ struct v4l2_ctrl_handler ctrl_handler; ++ struct i2c_mux_core *mux; ++ struct regmap_field *tunnel_mode; ++ struct clk_hw clk_hw; ++}; ++ ++static inline struct max_gmsl2_ser *sd_to_max_gmsl2_ser(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct max_gmsl2_ser, sd); ++} ++ ++static inline struct max_gmsl2_ser *clk_hw_to_max_gmsl2_ser(struct clk_hw *hw) ++{ ++ return container_of(hw, struct max_gmsl2_ser, clk_hw); ++} ++ ++/* Source Pad is right after sink pads */ ++#define MAX_GMSL2_SER_PAD_SINK 0 ++#define MAX_GMSL2_SER_PAD_SRC 1 ++ ++static const struct max_gmsl2_data max96717f_data = ++{ ++ .device_id = MAX96717F_DEVICE_ID, ++ .pipes = 1, ++ .csi = 1, ++ .links = 1, ++ .ngpios = 11, ++ .gpio_base = 0x2be, ++ .dev_rev = REG_FIELD(0xe, 0, 3), ++ .csi_lane_cnt = { REG_FIELD(0x331, 4, 5) }, ++ /* Phy that provides D0, D1 first*/ ++ .csi_lane_polarities = { REG_FIELD(0x335, 0, 2), REG_FIELD(0x334, 4, 6) }, ++ .csi_lane_map = { REG_FIELD(0x333, 0, 3), REG_FIELD(0x332, 4, 7) }, ++ .csi_force_enable = REG_FIELD(0x325, 7, 7) , ++ .csi_phy_enable = { REG_FIELD(0x308, 5, 5) }, ++ .pattern_clk_freq = REG_FIELD(0x24f, 1, 3), ++ .patgen_base_addr = { 0x24e }, ++ .pipe_stream_id = { REG_FIELD(0x5b, 0, 1) }, ++ .pipe_enable = { REG_FIELD(0x2, 6, 6) }, ++ .pipe_vid_lock = { REG_FIELD(0x112, 7, 7) }, ++ .link_lock = { REG_FIELD(0x13, 3, 3) }, ++ .reset_one_shot_link = { REG_FIELD(0x10, 5, 5) }, ++ .reset_link = { REG_FIELD(0x10, 6, 6) }, ++ .tunnel_mode = REG_FIELD(0x383, 7, 7), ++ .pipe_dt_sel = { ++ REG_FIELD(0x318, 0, 6), ++ REG_FIELD(0x319, 0, 6), ++ }, ++}; ++ ++const struct max_gmsl2_data max9295a_data = ++{ ++ .device_id = MAX9295A_DEVICE_ID, ++ .pipes = 4, ++ .csi = 1, ++ .links = 1, ++ .ngpios = 11, ++ .gpio_base = 0x2be, ++ .dev_rev = REG_FIELD(0xe, 0, 3), ++ .csi_lane_cnt = { REG_FIELD(0x331, 4, 5) }, ++ .csi_lane_polarities = { REG_FIELD(0x335, 0, 2), REG_FIELD(0x334, 4, 6) }, ++ .csi_lane_map = { REG_FIELD(0x333, 0, 3), REG_FIELD(0x332, 4, 7) }, ++ .csi_force_enable = REG_FIELD(0x325, 7, 7) , ++ .csi_phy_enable = { REG_FIELD(0x308, 5, 5) }, //* Port B*/ ++ .pattern_clk_freq = REG_FIELD(0x24f, 1, 3), ++ .patgen_base_addr = { 0x1f3 }, ++ .pipe_stream_id = { REG_FIELD(0x5b, 0, 1), ++ REG_FIELD(0x57, 0, 1), ++ REG_FIELD(0x5b, 0, 1), ++ REG_FIELD(0x5f, 0, 1) }, ++ .pipe_enable = { REG_FIELD(0x2, 4, 4), REG_FIELD(0x2, 5, 5), ++ REG_FIELD(0x2, 6, 6) , REG_FIELD(0x2, 7, 7) }, ++ .pipe_vid_lock = { REG_FIELD(0x102, 7, 7), REG_FIELD(0x10a, 7, 7), ++ REG_FIELD(0x112, 7, 7), REG_FIELD(0x11a, 7, 7) }, ++ .link_lock = { REG_FIELD(0x13, 3, 3) }, ++ .reset_one_shot_link = { REG_FIELD(0x10, 5, 5) }, ++ .reset_link = { REG_FIELD(0x10, 6, 6) }, ++ .pipe_dt_sel = { ++ REG_FIELD(0x314, 0, 6), ++ REG_FIELD(0x315, 0, 6), ++ REG_FIELD(0x316, 0, 6), ++ REG_FIELD(0x317, 0, 6), ++ REG_FIELD(0x318, 0, 6), ++ REG_FIELD(0x319, 0, 6), ++ REG_FIELD(0x31a, 0, 6), ++ REG_FIELD(0x31b, 0, 6), ++ }, ++}; ++#define IS_MAX9295A(priv) (priv->data->device_id == MAX9295A_DEVICE_ID) ++ ++#define for_each_pipe(pipe_idx, priv) \ ++ for ( (pipe_idx) = 0; (pipe_idx) < (priv)->data->pipes; (pipe_idx)++ ) ++ ++static void max_gmsl2_ser_init_patgen_regs(struct max_gmsl2_ser *priv) ++{ ++ struct device *dev = &priv->client->dev; ++ const struct max_gmsl2_data *data = priv->data; ++ struct max_gmsl2_patgen *patgen = &priv->patgen; ++ const struct reg_field *field; ++ u16 base = data->patgen_base_addr[0]; ++ struct reg_field gen_vs_hs_de = REG_FIELD(base, 5, 7); ++ struct reg_field inv_vs_hs_de = REG_FIELD(base, 2, 4); ++ struct reg_field vtg_mode = REG_FIELD(base, 0, 2); ++ struct reg_field vpg_mode_reg = REG_FIELD(base + 0x1c, 4, 5); ++ ++ field = &data->pattern_clk_freq; ++ if (field->reg) ++ priv->patgen.pattern_clk_freq = ++ devm_regmap_field_alloc(dev, priv->regmap, *field); ++ ++ patgen->gen_vs_hs_de = devm_regmap_field_alloc(dev, priv->regmap, gen_vs_hs_de); ++ patgen->inv_vs_hs_de = devm_regmap_field_alloc(dev, priv->regmap, inv_vs_hs_de); ++ patgen->vtg_mode = devm_regmap_field_alloc(dev, priv->regmap, vtg_mode); ++ patgen->vpg_mode = devm_regmap_field_alloc(dev, priv->regmap, vpg_mode_reg); ++ ++ patgen->vtg_timing = base + 2; ++ patgen->vpg_patterns = base + 0x1d; ++} ++ ++static int max_gmsl2_ser_init_regmap(struct max_gmsl2_ser *priv) ++{ ++ const struct max_gmsl2_data *data = priv->data; ++ struct device *dev = &priv->client->dev; ++ const struct reg_field *field; ++ int i; ++ ++ priv->regmap = devm_regmap_init_i2c(priv->client, ++ &gmsl2_regmap_config); ++ if (IS_ERR(priv->regmap)) ++ return PTR_ERR(priv->regmap); ++ ++ for_each_pipe(i, priv) ++ max_gmsl2_pipe_init_regfield(dev, priv->regmap, ++ &priv->pipes[i], priv->data, ++ i); ++ ++ max_gmsl2_csi_init_regfield(dev, priv->regmap, ++ &priv->csi, priv->data, 0); ++ ++ max_gmsl2_link_init_regfield(dev, priv->regmap, ++ &priv->link, priv->data, 0); ++ priv->dev_rev = ++ devm_regmap_field_alloc(dev, priv->regmap, data->dev_rev); ++ ++ field = &data->tunnel_mode; ++ if (field->reg) ++ priv->tunnel_mode = ++ devm_regmap_field_alloc(dev, priv->regmap, *field); ++ ++ max_gmsl2_ser_init_patgen_regs(priv); ++ ++ return 0; ++} ++ ++#define for_each_pipe(pipe_idx, priv) \ ++ for ( (pipe_idx) = 0; (pipe_idx) < (priv)->data->pipes; (pipe_idx)++ ) ++ ++static int max_gmsl2_ser_log_status(struct v4l2_subdev *sd) ++{ ++ struct max_gmsl2_ser *priv = sd_to_max_gmsl2_ser(sd); ++ struct device *dev = &priv->client->dev; ++ struct max_gmsl2_pipe *pipe; ++ unsigned int link_locked; ++ u8 pipeid; ++ int ret; ++ ++ dev_info(dev, "Max GMSL2 CSI Serializer %s id: %02x\n", ++ gmsl2_id_to_name(priv->data->device_id), priv->data->device_id); ++ ++ ret = regmap_field_read(priv->link.locked, &link_locked); ++ if (ret) ++ return ret; ++ dev_info(dev, "Link locked: %u\n", link_locked); ++ ++ for_each_pipe(pipeid, priv) { ++ pipe = &priv->pipes[pipeid]; ++ max_gmsl2_state_show_pipe(dev, pipe, priv->data, pipeid); ++ } ++ ++ return 0; ++} ++ ++#define PATTERN_CLK_FREQ_75MHz 0x101 ++static int max_gmsl2_enable_pattern(struct max_gmsl2_ser *priv, int val) ++{ ++ int ret; ++ struct max_gmsl2_patgen *patgen = &priv->patgen; ++ const struct v4l2_dv_timings t_720p = V4L2_DV_BT_CEA_1280X720P60; ++ const struct v4l2_bt_timings *bt; ++ enum max_gmsl2_vpg_mode mode; ++ ++ if (val) { ++ if (priv->patgen.pattern_clk_freq) { ++ ret = regmap_field_write(priv->patgen.pattern_clk_freq, ++ PATTERN_CLK_FREQ_75MHz); ++ if (ret) ++ return ret; ++ } ++ /* Invert VS HS and DE */ ++ ret = regmap_field_write(priv->patgen.inv_vs_hs_de, 7); ++ if (ret) ++ return ret; ++ mode = (val == 1) ? MAX_GMSL2_VPG_CHECKERBOARD: ++ MAX_GMSL2_VPG_GRADIENT; ++ ++ ret = max_gmsl2_vpg_enable(patgen, priv->regmap, mode); ++ if (ret) ++ return ret; ++ bt = &t_720p.bt; ++ ret = max_gmsl2_vtg_enable(patgen, priv->regmap, bt); ++ if (ret) ++ return ret; ++ } ++ return 0; ++} ++ ++static int max_gmsl2_ser_notify_bound(struct v4l2_async_notifier *notifier, ++ struct v4l2_subdev *source_subdev, ++ struct v4l2_async_subdev *asd) ++{ ++ struct max_gmsl2_ser *priv = sd_to_max_gmsl2_ser(notifier->sd); ++ struct device *dev = &priv->client->dev; ++ int ret; ++ ++ ret = media_entity_get_fwnode_pad(&source_subdev->entity, ++ source_subdev->fwnode, ++ MEDIA_PAD_FL_SOURCE); ++ if (ret < 0) ++ return ret; ++ ++ priv->source_sd = source_subdev; ++ priv->source_sd_pad = ret; ++ ++ ret = media_create_pad_link(&source_subdev->entity, priv->source_sd_pad, ++ &priv->sd.entity, MAX_GMSL2_SER_PAD_SINK, ++ MEDIA_LNK_FL_ENABLED | ++ MEDIA_LNK_FL_IMMUTABLE); ++ if (ret) { ++ dev_err(dev, "Unable to link %s:%u -> %s:0\n", ++ source_subdev->name, priv->source_sd_pad, ++ priv->sd.name); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static const struct v4l2_async_notifier_operations max_gmsl2_ser_notify_ops = +{ -+ MAX_GMSL2_VPG_CHECKERBOARD, -+ MAX_GMSL2_VPG_GRADIENT ++ .bound = max_gmsl2_ser_notify_bound, ++}; ++ ++static int max_gmsl2_ser_v4l2_notifier_register(struct max_gmsl2_ser *priv) ++{ ++ struct device *dev = &priv->client->dev; ++ struct v4l2_async_subdev *asd; ++ struct fwnode_handle *ep_fwnode; ++ int ret; ++ ++ ep_fwnode = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), ++ 0, 0, 0); ++ if (!ep_fwnode) ++ return -ENODEV; ++ ++ v4l2_async_nf_init(&priv->notifier); ++ ++ asd = v4l2_async_nf_add_fwnode_remote( ++ &priv->notifier, ep_fwnode, ++ struct v4l2_async_subdev); ++ ++ fwnode_handle_put(ep_fwnode); ++ ++ if (IS_ERR(asd)) { ++ dev_err(dev, "Failed to add subdev: %ld", PTR_ERR(asd)); ++ v4l2_async_nf_cleanup(&priv->notifier); ++ return ret; ++ } ++ ++ priv->notifier.ops = &max_gmsl2_ser_notify_ops; ++ ++ ret = v4l2_async_subdev_nf_register(&priv->sd, &priv->notifier); ++ if (ret) { ++ dev_err(dev, "Failed to register subdev_notifier"); ++ v4l2_async_nf_cleanup(&priv->notifier); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++struct max_gmsl2_fmt_desc { ++ u32 code; ++ u32 data_type; ++}; ++ ++static const struct max_gmsl2_fmt_desc max_gmsl2_ser_supported_codes[] = { ++ { ++ .code = MEDIA_BUS_FMT_Y8_1X8, ++ .data_type = MIPI_CSI2_DT_RAW8, ++ }, ++ { ++ .code = MEDIA_BUS_FMT_Y10_1X10, ++ .data_type = MIPI_CSI2_DT_RAW10, ++ }, ++ { ++ .code = MEDIA_BUS_FMT_Y12_1X12, ++ .data_type = MIPI_CSI2_DT_RAW12, ++ }, ++ { ++ .code = MEDIA_BUS_FMT_Y14_1X14, ++ .data_type = MIPI_CSI2_DT_RAW14, ++ }, ++ { ++ .code = MEDIA_BUS_FMT_Y16_1X16, ++ .data_type = MIPI_CSI2_DT_RAW16, ++ }, ++}; ++ ++/* ++ * Return 1 if the device is in tunnel mode, 0 if in pixel mode. ++ * Negative errno in case of error. ++ */ ++static int max_gmsl2_ser_tunnel_mode(struct max_gmsl2_ser *priv) ++{ ++ unsigned int tunnel; ++ int ret; ++ ++ if (!priv->tunnel_mode) ++ return 0; ++ ++ ret = regmap_field_read(priv->tunnel_mode, &tunnel); ++ if (ret) ++ return ret; ++ return !!tunnel; ++} ++ ++#define MAX_GMSL2_DT_SEL_EN BIT(6) ++static int max_gmsl2_pipe_set_dt_sel(struct max_gmsl2_ser *priv, u32 dt) ++{ ++ struct max_gmsl2_pipe *pipe; ++ int ret; ++ ++ ret = max_gmsl2_ser_tunnel_mode(priv); ++ if (ret < 0) ++ return ret; ++ ++ /* We don't need to select a datatype in tunnel mode */ ++ if (ret) ++ return 0; ++ ++ if (priv->data->pipes == 1) ++ pipe = &priv->pipes[0]; ++ else if (priv->data->pipes == 4) ++ pipe = &priv->pipes[2]; ++ else ++ return -EINVAL; ++ ++ return regmap_field_write(pipe->dt_sel[0], ++ dt | MAX_GMSL2_DT_SEL_EN); ++}; ++ ++static int max_gmsl2_ser_get_format(struct v4l2_subdev *sd, ++ struct v4l2_subdev_state *sd_state, ++ struct v4l2_subdev_format *sdformat) ++{ ++ struct max_gmsl2_ser *priv = sd_to_max_gmsl2_ser(sd); ++ ++ sdformat->format = priv->format.format; ++ ++ return 0; ++} ++ ++static int max_gmsl2_ser_set_format(struct v4l2_subdev *sd, ++ struct v4l2_subdev_state *sd_state, ++ struct v4l2_subdev_format *sdformat) ++{ ++ struct max_gmsl2_ser *priv = sd_to_max_gmsl2_ser(sd); ++ u32 dt; ++ int i, ret; ++ ++ /* No transcoding, source and sink formats must match. */ ++ if (sdformat->pad == MAX_GMSL2_SER_PAD_SRC) ++ return v4l2_subdev_get_fmt(sd, sd_state, sdformat); ++ ++ ret = max_gmsl2_ser_tunnel_mode(priv); ++ if (ret < 0) ++ return ret; ++ else if (ret) ++ /* If we are in tunnel mode we can accept any format */ ++ goto skip_dt_sel; ++ ++ for (i = 0; i < ARRAY_SIZE(max_gmsl2_ser_supported_codes); i++) { ++ if (max_gmsl2_ser_supported_codes[i].code == sdformat->format.code) ++ break; ++ } ++ ++ if (i == ARRAY_SIZE(max_gmsl2_ser_supported_codes)) { ++ i = 0; ++ sdformat->format.code = max_gmsl2_ser_supported_codes[i].code; ++ } ++ ++ dt = max_gmsl2_ser_supported_codes[i].data_type; ++ ret = max_gmsl2_pipe_set_dt_sel(priv, dt); ++ ++skip_dt_sel: ++ priv->format.format = sdformat->format; ++ ++ return 0; ++} ++ ++static const struct v4l2_subdev_pad_ops max_gmsl2_ser_pad_ops = { ++ .get_fmt = max_gmsl2_ser_get_format, ++ .set_fmt = max_gmsl2_ser_set_format, ++}; ++ ++static int max_gmsl2_ser_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct max_gmsl2_ser *priv = ++ container_of(ctrl->handler, struct max_gmsl2_ser, ctrl_handler); ++ int ret = 0; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_TEST_PATTERN: ++ ret = max_gmsl2_enable_pattern(priv, ctrl->val); ++ break; ++ } ++ ++ return ret; ++} ++ ++static const struct v4l2_ctrl_ops max_gmsl2_ser_ctrl_ops = { ++ .s_ctrl = max_gmsl2_ser_s_ctrl, ++}; ++ ++static const struct media_entity_operations max_gmsl2_ser_entity_ops = { ++}; ++ ++static int max_gmsl2_ser_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ struct max_gmsl2_ser *priv = sd_to_max_gmsl2_ser(sd); ++ int ret, i; ++ ++ for_each_pipe(i, priv) { ++ ret = regmap_field_write(priv->pipes[i].enable, !!enable); ++ if (ret) ++ return ret; ++ } ++ ++ ret = regmap_field_write(priv->csi.phy_enable, !!enable); ++ if (ret) ++ return ret; ++ ++ if (priv->source_sd) ++ ret = v4l2_subdev_call(priv->source_sd, video, s_stream, enable); ++ ++ return ret; ++} ++ ++static const struct v4l2_subdev_core_ops max_gmsl2_ser_core_ops = { ++ .log_status = max_gmsl2_ser_log_status, ++}; ++ ++static const struct v4l2_subdev_video_ops max_gmsl2_ser_video_ops = { ++ .s_stream = max_gmsl2_ser_s_stream, ++}; ++ ++static const struct v4l2_subdev_ops max_gmsl2_ser_subdev_ops = { ++ .core = &max_gmsl2_ser_core_ops, ++ .video = &max_gmsl2_ser_video_ops, ++ .pad = &max_gmsl2_ser_pad_ops, ++}; ++ ++static void max_gmsl2_ser_notifier_unregister(struct max_gmsl2_ser *priv) ++{ ++ v4l2_async_nf_unregister(&priv->notifier); ++ v4l2_async_nf_cleanup(&priv->notifier); ++} ++ ++static int max_gmsl2_ser_init_format(struct max_gmsl2_ser *priv) ++{ ++ priv->format.format.width = 1920; ++ priv->format.format.height = 1080; ++ priv->format.format.code = MEDIA_BUS_FMT_Y8_1X8; ++ priv->format.format.field = V4L2_FIELD_NONE; ++ ++ return max_gmsl2_pipe_set_dt_sel(priv, MIPI_CSI2_DT_RAW8); ++} ++ ++static int max_gmsl2_ser_subdev_init(struct max_gmsl2_ser *priv) ++{ ++ struct device *dev = &priv->client->dev; ++ int ret; ++ ++ ret = max_gmsl2_ser_init_format(priv); ++ if (ret) ++ return ret; ++ ++ v4l2_i2c_subdev_init(&priv->sd, priv->client, &max_gmsl2_ser_subdev_ops); ++ v4l2_ctrl_handler_init(&priv->ctrl_handler, 1); ++ priv->sd.ctrl_handler = &priv->ctrl_handler; ++ ++ if (priv->data->patgen_base_addr[0]) ++ v4l2_ctrl_new_std_menu_items( ++ &priv->ctrl_handler, ++ &max_gmsl2_ser_ctrl_ops, ++ V4L2_CID_TEST_PATTERN, ++ ARRAY_SIZE(max_gmsl2_test_pattern) - 1, ++ 0, 0, max_gmsl2_test_pattern); ++ ++ if (priv->ctrl_handler.error) { ++ ret = priv->ctrl_handler.error; ++ dev_err(dev, "Failed to set up v4l2 controls\n"); ++ goto err_remove_ctrls; ++ } ++ ++ priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ++ priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; ++ priv->sd.entity.ops = &max_gmsl2_ser_entity_ops; ++ ++ priv->pads[MAX_GMSL2_SER_PAD_SINK].flags = MEDIA_PAD_FL_SINK; ++ priv->pads[MAX_GMSL2_SER_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; ++ ++ ret = media_entity_pads_init(&priv->sd.entity, 2, priv->pads); ++ if (ret) { ++ dev_err(dev, "Failed to init pads\n"); ++ goto err_remove_ctrls; ++ } ++ ++ priv->sd.fwnode = fwnode_graph_get_endpoint_by_id( ++ dev_fwnode(dev), MAX_GMSL2_SER_PAD_SRC, ++ 0, 0); ++ if (!priv->sd.fwnode) { ++ ret = -ENODEV; ++ dev_err(dev, "Missing TX endpoint\n"); ++ goto err_remove_ctrls; ++ } ++ ++ ret = max_gmsl2_ser_v4l2_notifier_register(priv); ++ if (ret == -ENODEV) { ++ /* We can still do pattern generation */ ++ dev_warn(dev, "v4l2 subdev no camera attached to the sink port\n"); ++ } else if (ret) { ++ dev_err(dev, "v4l2 subdev notifier register failed\n"); ++ goto err_fwnode_put; ++ } ++ ++ ret = v4l2_async_register_subdev(&priv->sd); ++ if (ret) { ++ dev_err(dev, "v4l2_async_register_subdev error\n"); ++ goto err_unreg_notif; ++ } ++ ++ return 0; ++ ++err_unreg_notif: ++ max_gmsl2_ser_notifier_unregister(priv); ++err_fwnode_put: ++ fwnode_handle_put(priv->sd.fwnode); ++err_remove_ctrls: ++ v4l2_ctrl_handler_free(&priv->ctrl_handler); ++ ++ return ret; ++} ++ ++static void max_gmsl2_ser_subdev_uninit(struct max_gmsl2_ser *priv) ++{ ++ v4l2_async_unregister_subdev(&priv->sd); ++ max_gmsl2_ser_notifier_unregister(priv); ++ fwnode_handle_put(priv->sd.fwnode); ++ media_entity_cleanup(&priv->sd.entity); ++ v4l2_ctrl_handler_free(&priv->ctrl_handler); ++} ++ ++static int max_gmsl2_ser_init_pipes(struct max_gmsl2_ser *priv) ++{ ++ return max_gmsl2_init_pipes_stream_id(priv->pipes, priv->data->pipes, ++ &priv->client->dev); ++} ++ ++static int max_gmsl2_ser_init_csi_lanes(struct max_gmsl2_csi *csi) ++{ ++ int ret; ++ struct v4l2_fwnode_endpoint v4l2_ep = { ++ .bus_type = V4L2_MBUS_CSI2_DPHY ++ }; ++ struct v4l2_mbus_config_mipi_csi2 *mipi = &v4l2_ep.bus.mipi_csi2; ++ unsigned char nlanes, i; ++ u8 val = 0; ++ ++ ret = v4l2_fwnode_endpoint_parse(csi->ep_fwnode, &v4l2_ep); ++ if (ret) ++ return -EINVAL; ++ ++ nlanes = mipi->num_data_lanes; ++ if (nlanes < 1 || nlanes > GMSL_SER_MAX_CSI_LANES) ++ return -EINVAL; ++ ++ ret = regmap_field_write(csi->lane_cnt, nlanes - 1); ++ if (ret) ++ return ret; ++ ++ for (i = 0; i < nlanes + 1; i++) { ++ if (!mipi->lane_polarities[i]) ++ continue; ++ if (i == 0) ++ val |= BIT(2); ++ else if (i < 3) ++ val |= BIT(i - 1); ++ else ++ val |= BIT(i); ++ } ++ ++ ret = regmap_field_write(csi->lane_polarities[0], val); ++ if (ret) ++ return ret; ++ ret = regmap_field_write(csi->lane_polarities[1], val >> 3); ++ if (ret) ++ return ret; ++ ++ val = 0; ++ for (i = 0; i < nlanes; i++) ++ val |= (mipi->data_lanes[i] - 1) << (i*2); ++ ++ /* map unused lanes 1 to 1 */ ++ for (; i < GMSL_SER_MAX_CSI_LANES; i++) ++ val |= i << (i*2); ++ ++ ret = regmap_field_write(csi->lane_map[0], val); ++ if (ret) ++ return ret; ++ ret = regmap_field_write(csi->lane_map[1], val >> 4); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static int max_gmsl2_ser_init_csi(struct max_gmsl2_ser *priv) ++{ ++ struct max_gmsl2_csi *csi = &priv->csi; ++ struct device *dev = &priv->client->dev; ++ int ret; ++ ++ csi->priv = priv; ++ csi->ep_fwnode = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), ++ 0, 0, 0); ++ ++ ret = max_gmsl2_ser_init_csi_lanes(csi); ++ if (ret) ++ return ret; ++ ++ /* FIXME: the plck output for the sensor should be parsed from ++ * the dts. ++ */ ++ if (IS_MAX9295A(priv)) { ++ /* Enable pclk output on MFP4 */ ++ ret = regmap_write(priv->regmap, 0x3f1, 0x89); ++ if (ret) ++ return ret; ++ } ++ return 0; ++} ++ ++static int max_gmsl2_i2c_mux_select(struct i2c_mux_core *mux, u32 chan) ++{ ++ return 0; ++} ++ ++static int max_gmsl2_i2c_mux_init(struct max_gmsl2_ser *priv) ++{ ++ priv->mux = i2c_mux_alloc(priv->client->adapter, &priv->client->dev, ++ 1, 0, I2C_MUX_LOCKED | I2C_MUX_GATE, ++ max_gmsl2_i2c_mux_select, NULL); ++ if (!priv->mux) ++ return -ENOMEM; ++ ++ return i2c_mux_add_adapter(priv->mux, 0, 0, 0); ++} ++ ++/* MAX96717F */ ++#define REG3 0x3 ++#define RCLKSEL_REF_PLL 0x3 ++#define REG6 0x6 ++#define RCLKEN BIT(5) ++#define PIO_SLEW_1 0x570 ++#define REF_VTG0 0x3f0 ++#define REGGEN_PREDEF_EN BIT(6) ++#define REFGEN_PREDEF_FREQ_POS 4 ++#define REFGEN_PREDEF_FREQ_27_24_MHZ (0x1 << REFGEN_PREDEF_FREQ_POS) ++#define REFGEN_PREDEF_FREQ_ALT BIT(3) ++#define REFGEN_RST BIT(1) ++#define REFGEN_EN BIT(0) ++ ++struct max96717_pll_predef_freq { ++ unsigned long freq; ++ bool is_alt; ++ u8 val; ++}; ++ ++static const struct max96717_pll_predef_freq max96717_predef_freqs[] = { ++ { 13500000, true, 0 }, { 19200000, false, 0 }, ++ { 24000000, true, 1 }, { 27000000, false, 1 }, ++ { 37125000, false, 2 }, { 74250000, false, 3 }, ++}; ++ ++static unsigned long ++max_gmsl2_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) ++{ ++ struct max_gmsl2_ser *priv = clk_hw_to_max_gmsl2_ser(hw); ++ unsigned int i, val; ++ bool is_alt; ++ int ret; ++ ++ ret = regmap_read(priv->regmap, REF_VTG0, &val); ++ if (ret) ++ return 0; ++ ++ is_alt = !!(val & REFGEN_PREDEF_FREQ_ALT); ++ val = (val >> REFGEN_PREDEF_FREQ_POS) & 0x3; ++ ++ for (i = 0; i < ARRAY_SIZE(max96717_predef_freqs); i++) ++ if (max96717_predef_freqs[i].val == val && ++ max96717_predef_freqs[i].is_alt == is_alt) ++ return max96717_predef_freqs[i].freq; ++ ++ return 0; ++} ++ ++static long max_gmsl2_clk_round_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *parent_rate) ++{ ++ struct max_gmsl2_ser *priv = clk_hw_to_max_gmsl2_ser(hw); ++ struct device *dev = &priv->client->dev; ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(max96717_predef_freqs); i++) ++ if (rate <= max96717_predef_freqs[i].freq) ++ break; ++ ++ if (rate != max96717_predef_freqs[i].freq) ++ dev_warn(dev, "Request CLK freq:%lu, found CLK freq:%lu\n", ++ rate, max96717_predef_freqs[i].freq); ++ ++ return max96717_predef_freqs[i].freq; ++} ++ ++static int max_gmsl2_clk_set_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long parent_rate) ++{ ++ struct max_gmsl2_ser *priv = clk_hw_to_max_gmsl2_ser(hw); ++ unsigned int i; ++ int ret; ++ u8 val; ++ ++ for (i = 0; i < ARRAY_SIZE(max96717_predef_freqs); i++) ++ if (rate <= max96717_predef_freqs[i].freq) ++ break; ++ ++ val = REGGEN_PREDEF_EN | ++ ( max96717_predef_freqs[i].val << REFGEN_PREDEF_FREQ_POS); ++ ++ if (max96717_predef_freqs[i].is_alt) ++ val |= REFGEN_PREDEF_FREQ_ALT; ++ ++ val |= REFGEN_RST; ++ ++ ret = regmap_write(priv->regmap, REF_VTG0, val); ++ if (ret) ++ return ret; ++ ++ ret = regmap_update_bits(priv->regmap, REF_VTG0, REFGEN_RST| REFGEN_EN, REFGEN_EN); ++ if (ret) ++ return ret; ++ ++ return regmap_update_bits(priv->regmap, REG6, RCLKEN, RCLKEN); ++} ++ ++static int max_gmsl2_clk_enable(struct clk_hw *hw) ++{ ++ struct max_gmsl2_ser *priv = clk_hw_to_max_gmsl2_ser(hw); ++ ++ return regmap_update_bits(priv->regmap, REG6, RCLKEN, RCLKEN); ++} ++ ++static void max_gmsl2_clk_disable(struct clk_hw *hw) ++{ ++ struct max_gmsl2_ser *priv = clk_hw_to_max_gmsl2_ser(hw); ++ ++ regmap_update_bits(priv->regmap, REG6, RCLKEN, 0); ++} ++ ++static const struct clk_ops max_gmsl2_clk_ops = { ++ .enable = max_gmsl2_clk_enable, ++ .disable = max_gmsl2_clk_disable, ++ .set_rate = max_gmsl2_clk_set_rate, ++ .recalc_rate = max_gmsl2_clk_recalc_rate, ++ .round_rate = max_gmsl2_clk_round_rate, ++}; ++ ++static int max_gmsl2_ser_init_clk_provider(struct max_gmsl2_ser *priv) ++{ ++ struct device *dev = &priv->client->dev; ++ int ret; ++ ++ const struct clk_init_data init = { ++ .name = kasprintf(GFP_KERNEL, "%s.%s.clk_out", ++ gmsl2_id_to_name(priv->data->device_id), ++ dev_name(dev)), ++ .ops = &max_gmsl2_clk_ops, ++ }; ++ ++ if (!init.name) ++ return -ENOMEM; ++ ++ /* RCLKSEL Reference PLL output */ ++ ret = regmap_update_bits(priv->regmap, REG3, 0x3, RCLKSEL_REF_PLL); ++ if (ret) ++ goto free_init_name; ++ ++ /* MFP4 fastest slew rate */ ++ ret = regmap_update_bits(priv->regmap, PIO_SLEW_1, BIT(5) | BIT(4), 0); ++ if (ret) ++ goto free_init_name; ++ ++ priv->clk_hw.init = &init; ++ ++ /* Initialize to 24 MHz */ ++ ret = max_gmsl2_clk_set_rate(&priv->clk_hw, 24000000, 0); ++ if (ret < 0) ++ goto free_init_name; ++ ++ ret = devm_clk_hw_register(dev, &priv->clk_hw); ++ kfree(init.name); ++ if (ret) ++ return dev_err_probe(dev, ret, "Cannot register clock HW\n"); ++ ++ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, ++ &priv->clk_hw); ++ if (ret) ++ return dev_err_probe(dev, ret, ++ "Cannot add OF clock provider\n"); ++ ++ return 0; ++ ++free_init_name: ++ kfree(init.name); ++ return ret; ++ ++} ++ ++static int max_gmsl2_ser_probe(struct i2c_client *client) ++{ ++ struct max_gmsl2_ser *priv; ++ unsigned int dev_id, dev_rev; ++ int ret; ++ ++ priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->client = client; ++ priv->data = of_device_get_match_data(&client->dev); ++ ++ ret = max_gmsl2_ser_init_regmap(priv); ++ if (ret) ++ return ret; ++ ++ ret = regmap_read(priv->regmap, 0x0d, &dev_id); ++ if (ret) { ++ dev_err(&client->dev, "Fail to read device id"); ++ return ret; ++ } ++ ++ if ((u8)dev_id != priv->data->device_id) { ++ dev_err(&client->dev, "Wrong device id got 0x%02x expected 0x%02x", ++ dev_id, priv->data->device_id); ++ return -ENODEV; ++ } ++ ++ ret = regmap_field_read(priv->dev_rev, &dev_rev); ++ if (ret) ++ return ret; ++ ++ dev_info(&client->dev, "GMSL2 CSI Serializer device_id: 0x%02x rev: 0x%01x", dev_id, dev_rev); ++ i2c_set_clientdata(client, priv); ++ ++ ret = max_gmsl2_gpiochip_probe(&priv->gpio, &client->dev, priv->regmap, ++ priv->data->gpio_base, ++ priv->data->ngpios); ++ if (ret) { ++ dev_err(&client->dev, "Failed to init gpiochip\n"); ++ return ret; ++ } ++ ++ ret = max_gmsl2_ser_init_csi(priv); ++ if (ret) ++ return ret; ++ ++ ret = max_gmsl2_ser_init_pipes(priv); ++ if (ret) ++ return ret; ++ ++ ret = max_gmsl2_i2c_mux_init(priv); ++ if (ret) ++ return ret; ++ ++ if (device_property_present(&client->dev, "#clock-cells")) { ++ ret = max_gmsl2_ser_init_clk_provider(priv); ++ if (ret) ++ return ret; ++ } ++ ++ ret = max_gmsl2_ser_subdev_init(priv); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static void max_gmsl2_ser_remove(struct i2c_client *client) ++{ ++ struct max_gmsl2_ser *priv = i2c_get_clientdata(client); ++ ++ max_gmsl2_ser_subdev_uninit(priv); ++}; ++ ++static const struct of_device_id max_gmsl2_ser_of_ids[] = { ++ { .compatible = "maxim,max96717f", .data = &max96717f_data }, ++ { .compatible = "maxim,max9295a", .data = &max9295a_data }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, max_gmsl2_ser_of_ids); ++ ++static struct i2c_driver max_gmsl2_ser_i2c_driver = { ++ .driver = { ++ .name = "max_gmsl2_ser", ++ .of_match_table = max_gmsl2_ser_of_ids, ++ }, ++ .probe_new = max_gmsl2_ser_probe, ++ .remove = max_gmsl2_ser_remove, ++}; ++ ++module_i2c_driver(max_gmsl2_ser_i2c_driver); ++ ++MODULE_DESCRIPTION("Maxim GMSL2 Serializer Driver"); ++MODULE_AUTHOR("Julien Massot <julien.massot@collabora.com>"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/i2c/max-gmsl2.c b/drivers/media/i2c/max-gmsl2.c +new file mode 100644 +index 00000000..61db622 +--- /dev/null ++++ b/drivers/media/i2c/max-gmsl2.c +@@ -0,0 +1,394 @@ ++#include <linux/errno.h> ++#include <linux/module.h> ++#include <linux/seq_file.h> ++#include <media/v4l2-fwnode.h> ++ ++#include "gmsl2-def.h" ++ ++static int max_gmsl2_csi_lane_init(struct max_gmsl2_csi *csi, ++ struct v4l2_mbus_config_mipi_csi2 *mipi) ++{ ++ int i, ret; ++ u8 val = 0; ++ ++ if (mipi->num_data_lanes < 1 || mipi->num_data_lanes > 4) ++ return -EINVAL; ++ ++ ret = regmap_field_write(csi->lane_cnt, mipi->num_data_lanes - 1); ++ if (ret) ++ return ret; ++ ++ /* lane polarities */ ++ for (i = 0; i < mipi->num_data_lanes + 1; i++) { ++ if (!mipi->lane_polarities[i]) ++ continue; ++ if (i == 0) ++ /* clock lane */ ++ val |= BIT(5); ++ else if (i < 3) ++ /* Lane D0 and D1 */ ++ val |= BIT(i - 1); ++ else ++ /* D2 and D3 */ ++ val |= BIT(i); ++ } ++ ret = regmap_field_write(csi->lane_polarities[0], val); ++ if (ret) ++ return ret; ++ ret = regmap_field_write(csi->lane_polarities[1], val >> 3); ++ if (ret) ++ return ret; ++ ++ /* lane mapping */ ++ val = 0; ++ for (i = 0; i < mipi->num_data_lanes; i++) ++ val |= (mipi->data_lanes[i] - 1) << (i*2); ++ ++ ret = regmap_field_write(csi->lane_map[0], val); ++ if (ret) ++ return ret; ++ ret = regmap_field_write(csi->lane_map[1], val >> 4); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++/* ++ * DPHY clock frequency is half DPLL frequency. ++ */ ++ ++#define MHZ(v) ((u64)((v) * 1000000UL)) ++int max_gmsl2_csi_get_link_freq(struct max_gmsl2_csi *csi, s64 *link_freq) ++{ ++ u32 dpll_freq; ++ int ret; ++ ++ if (!csi->dpll_freq) ++ return -EINVAL; ++ ++ ret = regmap_field_read(csi->dpll_freq, &dpll_freq); ++ if (ret) ++ return ret; ++ ++ *link_freq = dpll_freq * MHZ(100) / 2; ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(max_gmsl2_csi_get_link_freq); ++ ++static int max_gmsl2_csi_set_frequency(struct max_gmsl2_csi *csi, ++ struct v4l2_fwnode_endpoint *v4l2_ep) ++{ ++ int ret; ++ u64 freq = MHZ(400); ++ u32 dpll_freq; ++ ++ if (v4l2_ep->nr_of_link_frequencies == 1) { ++ freq = *v4l2_ep->link_frequencies; ++ if (freq % MHZ(100/2) || freq < MHZ(200/2) || freq > MHZ(5700/2)) { ++ return -EINVAL; ++ } ++ } ++ ++ dpll_freq = freq * 2 / MHZ(100); ++ ++ ret = regmap_field_write(csi->dpll_freq, dpll_freq); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++int max_gmsl2_csi_phy_init(struct max_gmsl2_csi *csi) ++{ ++ int ret; ++ struct v4l2_fwnode_endpoint v4l2_ep = { ++ .bus_type = V4L2_MBUS_CSI2_DPHY ++ }; ++ ++ ret = v4l2_fwnode_endpoint_parse(csi->ep_fwnode, &v4l2_ep); ++ if (ret) ++ return -EINVAL; ++ ++ if (csi->dpll_freq) { ++ ret = max_gmsl2_csi_set_frequency(csi, &v4l2_ep); ++ if (ret) ++ return ret; ++ } ++ ++ return max_gmsl2_csi_lane_init(csi, &v4l2_ep.bus.mipi_csi2); ++} ++EXPORT_SYMBOL_GPL(max_gmsl2_csi_phy_init); ++ ++int max_gmsl2_init_pipes_stream_id(struct max_gmsl2_pipe *pipes, u8 npipes, ++ struct device *dev) ++{ ++ u8 stream_id[GMSL2_MAX_PIPE]; ++ int n_stream_id, i, ret; ++ ++ if (npipes > GMSL2_MAX_PIPE) ++ return -EINVAL; ++ ++ if (!device_property_present(dev, "maxim,pipe-stream-id")) ++ return 0; ++ ++ n_stream_id = device_property_count_u8(dev, "maxim,pipe-stream-id"); ++ if (n_stream_id > npipes) ++ return -EOVERFLOW; ++ ++ ret = device_property_read_u8_array(dev, "maxim,pipe-stream-id", ++ stream_id, ++ n_stream_id); ++ if (ret) ++ return ret; ++ ++ for (i = 0; i < n_stream_id; i++, pipes++) { ++ if (stream_id[i] > 3) { ++ dev_err(dev, "invalid stream id: %d for pipe %d", ++ stream_id[i], i); ++ return -EINVAL; ++ } ++ ret = regmap_field_write(pipes->stream_id, stream_id[i]); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(max_gmsl2_init_pipes_stream_id); ++ ++void max_gmsl2_pipe_init_regfield(struct device *dev, ++ struct regmap *regmap, ++ struct max_gmsl2_pipe *pipe, ++ const struct max_gmsl2_data *data, ++ u8 id) ++{ ++ const struct reg_field *field; ++ ++ field = &data->pipe_enable[id]; ++ pipe->enable = devm_regmap_field_alloc(dev, regmap, ++ *field); ++ field = &data->pipe_stream_id[id]; ++ pipe->stream_id = devm_regmap_field_alloc(dev, regmap, ++ *field); ++ field = &data->pipe_link_sel[id]; ++ if (field->reg) ++ pipe->link_sel = devm_regmap_field_alloc(dev, regmap, ++ *field); ++ field = &data->pipe_vid_lock[id]; ++ if (field->reg) ++ pipe->vid_lock = devm_regmap_field_alloc(dev, regmap, ++ *field); ++ ++ field = &data->pipe_override_bpp_vc_dt[id]; ++ if (field->reg) ++ pipe->override_bpp_vc_dt = ++ devm_regmap_field_alloc(dev, regmap, *field); ++ ++ field = &data->pipe_bpp_l[id]; ++ if (field->reg) ++ pipe->bpp_l = devm_regmap_field_alloc(dev, regmap, ++ *field); ++ ++ field = &data->pipe_bpp_h[id]; ++ if (field->reg) ++ pipe->bpp_h = devm_regmap_field_alloc(dev, regmap, ++ *field); ++ ++ field = &data->pipe_dt_l[id]; ++ if (field->reg) ++ pipe->dt_l = devm_regmap_field_alloc(dev, regmap, ++ *field); ++ ++ field = &data->pipe_dt_h[id]; ++ if (field->reg) ++ pipe->dt_h = devm_regmap_field_alloc(dev, regmap, ++ *field); ++ ++ field = &data->pipe_dt_l[id]; ++ if (field->reg) ++ pipe->dt_l = devm_regmap_field_alloc(dev, regmap, ++ *field); ++ ++ field = &data->pipe_vc[id]; ++ if (field->reg) ++ pipe->vc = devm_regmap_field_alloc(dev, regmap, *field); ++ ++ field = &data->pipe_dt_sel[id*2]; ++ if (field->reg) ++ pipe->dt_sel[0] = devm_regmap_field_alloc(dev, regmap, *field); ++ ++ field = &data->pipe_dt_sel[id * 2 + 1]; ++ if (field->reg) ++ pipe->dt_sel[1] = devm_regmap_field_alloc(dev, regmap, *field); ++ ++} ++EXPORT_SYMBOL_GPL(max_gmsl2_pipe_init_regfield); ++ ++void max_gmsl2_csi_init_regfield(struct device *dev, ++ struct regmap *regmap, ++ struct max_gmsl2_csi *csi, ++ const struct max_gmsl2_data *data, u8 id) ++{ ++ const struct reg_field *field; ++ ++ field = &data->csi_phy_enable[id]; ++ if (field->reg) ++ csi->phy_enable = devm_regmap_field_alloc(dev, regmap, *field); ++ ++ /* We only suport 2x4 mode, in this case the clk is provided ++ * by the master phy, phy1 for CSI port 0 and phy2 for CSI port 1. ++ */ ++ field = &data->csi_dpll_freq[id == 0 ? 1 : 2]; ++ if (field->reg) ++ csi->dpll_freq = devm_regmap_field_alloc(dev, regmap, *field); ++ ++ field = &data->csi_lane_polarities[id * 2]; ++ csi->lane_polarities[0] = devm_regmap_field_alloc(dev, regmap, *field); ++ ++ field = &data->csi_lane_polarities[id * 2 + 1]; ++ csi->lane_polarities[1] = devm_regmap_field_alloc(dev, regmap, *field); ++ ++ field = &data->csi_lane_map[id * 2]; ++ csi->lane_map[0] = devm_regmap_field_alloc(dev, regmap, *field); ++ ++ field = &data->csi_lane_map[id * 2 + 1]; ++ csi->lane_map[1] = devm_regmap_field_alloc(dev, regmap, *field); ++ ++ field = &data->csi_lane_cnt[id]; ++ csi->lane_cnt = devm_regmap_field_alloc(dev, regmap, *field); ++} ++EXPORT_SYMBOL_GPL(max_gmsl2_csi_init_regfield); ++ ++void max_gmsl2_link_init_regfield(struct device *dev, ++ struct regmap *regmap, ++ struct max_gmsl2_link *link, ++ const struct max_gmsl2_data *data, u8 id) ++{ ++ const struct reg_field *field; ++ ++ field = &data->link_lock[id]; ++ if (field->reg) ++ link->locked = ++ devm_regmap_field_alloc(dev, regmap, *field); ++ field = &data->reset_one_shot_link[id]; ++ if (field->reg) ++ link->reset_one_shot = ++ devm_regmap_field_alloc(dev, regmap, *field); ++ field = &data->reset_link[id]; ++ if (field->reg) ++ link->reset = ++ devm_regmap_field_alloc(dev, regmap, *field); ++} ++EXPORT_SYMBOL_GPL(max_gmsl2_link_init_regfield); ++/* Some registers such as bpp and dt maybe split on two regmap_field */ ++int max_gmsl2_write_h_l_val(struct regmap_field *h, struct regmap_field *l, ++ const struct reg_field *l_field, unsigned int val) ++{ ++ u8 shift; ++ int ret; ++ ++ /* if h ptr is not null then the register is splitted */ ++ if (h) { ++ shift = l_field->msb - l_field->lsb + 1; ++ ret = regmap_field_write(h, val >> shift); ++ if (ret) ++ return ret; ++ } ++ ++ /* regmap_field_write will take care of applying a mask on val ++ * to not write the h bits. ++ */ ++ return regmap_field_write(l, val); ++} ++EXPORT_SYMBOL_GPL(max_gmsl2_write_h_l_val); ++ ++int max_gmsl2_read_h_l_val(struct regmap_field *h, struct regmap_field *l, ++ const struct reg_field *l_field, unsigned int *val) ++{ ++ u8 shift; ++ unsigned int h_val; ++ int ret; ++ ++ ret = regmap_field_read(l, val); ++ if (ret) ++ return ret; ++ if (h) { ++ ret = regmap_field_read(h, &h_val); ++ if (ret) ++ return ret; ++ shift = l_field->msb - l_field->lsb + 1; ++ *val |= (h_val << shift); ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(max_gmsl2_read_h_l_val); ++ ++void max_gmsl2_state_show_pipe(struct device *dev, struct max_gmsl2_pipe *pipe, ++ const struct max_gmsl2_data *data, u8 pipeid) ++{ ++ unsigned int enable, stream_id, link_sel, vid_lock, bpp, dt, vc; ++ int ret, i; ++ ++ ret = regmap_field_read(pipe->enable, &enable); ++ if (ret) ++ return; ++ dev_info(dev, "pipe_%u: enable:%u", pipeid, enable); ++ ++ if (pipe->link_sel) { ++ ret = regmap_field_read(pipe->link_sel, &link_sel); ++ if (ret) ++ return; ++ dev_info(dev, " link_sel:%c", 'A' + link_sel); ++ } ++ ++ ret = regmap_field_read(pipe->stream_id, &stream_id); ++ if (ret) ++ return; ++ dev_info(dev, " stream_id:%u", stream_id); ++ ++ ret = regmap_field_read(pipe->vid_lock, &vid_lock); ++ if (ret) ++ return; ++ dev_info(dev," vid_lock:%u", vid_lock); ++ ++ if (pipe->override_bpp_vc_dt) { ++ ret = max_gmsl2_read_h_l_val(pipe->bpp_h, pipe->bpp_l, ++ &data->pipe_bpp_l[pipeid], ++ &bpp); ++ if (ret) ++ return; ++ ret = max_gmsl2_read_h_l_val(pipe->dt_h, pipe->dt_l, ++ &data->pipe_dt_l[pipeid], ++ &dt); ++ if (ret) ++ return; ++ ret = regmap_field_read(pipe->vc, &vc); ++ if (ret) ++ return; ++ dev_info(dev, " bpp:%u, dt:0x%2x, vc:%u", ++ bpp, dt, vc); ++ } ++ ++ if (pipe->dt_sel[0] && pipe->dt_sel[1]) { ++ for (i = 0; i < 2; i++) { ++ ret = regmap_field_read(pipe->dt_sel[i], &dt); ++ if (ret) ++ return; ++ //Enabled ++ if (dt & BIT(6)) ++ dev_info(dev," dt%d_sel:%x", i, dt & 0x3F); ++ else ++ dev_info(dev," dt%d_sel:disabled", i); ++ } ++ } ++ ++ dev_info(dev, "\n"); ++} ++EXPORT_SYMBOL_GPL(max_gmsl2_state_show_pipe); ++ ++MODULE_DESCRIPTION("Maxim GMSL2 Lib"); ++MODULE_AUTHOR("Julien Massot <julien.massot@collabora.com>"); ++MODULE_LICENSE("GPL"); +diff --git a/include/linux/gpio/max-gmsl2-gpio.h b/include/linux/gpio/max-gmsl2-gpio.h +new file mode 100644 +index 00000000..363cb68 +--- /dev/null ++++ b/include/linux/gpio/max-gmsl2-gpio.h +@@ -0,0 +1,16 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Maxim GMSL2 GPIO Driver ++ * ++ * Copyright (C) 2023 Collabora Ltd. ++ */ ++#ifndef __LINUX_GPIO_MAX_GMSL2_GPIO_H ++#define __LINUX_GPIO_MAX_GMSL2_GPIO_H ++ ++#include <linux/regmap.h> ++#include <linux/gpio/driver.h> ++ ++int max_gmsl2_gpiochip_probe(struct gpio_chip *gpio, struct device *dev, ++ struct regmap *regmap, u16 base, u8 ngpios); ++ ++#endif /* __LINUX_GPIO_MAX_GMSL2_GPIO_H */ +diff --git a/include/media/gmsl2/max-gmsl2-patgen.h b/include/media/gmsl2/max-gmsl2-patgen.h +new file mode 100644 +index 00000000..3f675d9 +--- /dev/null ++++ b/include/media/gmsl2/max-gmsl2-patgen.h +@@ -0,0 +1,45 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Maxim GMSL2 Pattern Generator ++ * ++ * Copyright (C) 2023 Collabora Ltd. ++ */ ++ ++#ifndef __LINUX_MEDIA_MAX_GMSL2_PATGEN_H ++#define __LINUX_MEDIA_MAX_GMSL2_PATGEN_H ++ ++#include <linux/regmap.h> ++#include <linux/types.h> ++#include <linux/videodev2.h> ++ ++enum max_gmsl2_vpg_mode ++{ ++ MAX_GMSL2_VPG_DISABLED = 0, ++ MAX_GMSL2_VPG_CHECKERBOARD = 1, ++ MAX_GMSL2_VPG_GRADIENT = 2, ++}; ++ ++static const char * const max_gmsl2_test_pattern[] = { ++ "Disabled", ++ "Checkerboard", ++ "Gradient" ++}; ++ ++struct max_gmsl2_patgen { ++ struct regmap_field *gen_vs_hs_de; ++ struct regmap_field *inv_vs_hs_de; ++ struct regmap_field *vtg_mode; ++ struct regmap_field *vpg_mode; ++ u16 vtg_timing; ++ u16 vpg_patterns; ++ struct regmap_field *pattern_clk_freq; +}; + -+int max_gmsl2_vtg_enable(struct regmap *regmap, uint16_t base_addr, ++int max_gmsl2_vtg_enable(struct max_gmsl2_patgen *patgen, ++ struct regmap *regmap, + const struct v4l2_bt_timings *t); -+int max_gmsl2_vtg_disable(struct regmap *regmap, uint16_t base_addr); -+int max_gmsl2_vpg_enable(struct regmap *regmap, uint16_t base_addr, ++int max_gmsl2_vtg_disable(struct max_gmsl2_patgen *patgen, struct regmap *regmap); ++int max_gmsl2_vpg_enable(struct max_gmsl2_patgen *patgen, struct regmap *regmap, + enum max_gmsl2_vpg_mode mode); -+int max_gmsl2_vpg_disable(struct regmap *regmap, uint16_t base_addr); ++int max_gmsl2_vpg_disable(struct max_gmsl2_patgen *patgen); ++#endif /* __LINUX_MEDIA_MAX_GMSL2_PATGEN_H */ diff --git a/debian/patches/apertis/maxim-gmsl2/0151-max-gmsl2-deser-report-link-frequency.patch b/debian/patches/apertis/maxim-gmsl2/0151-max-gmsl2-deser-report-link-frequency.patch deleted file mode 100644 index a7d9907553a..00000000000 --- a/debian/patches/apertis/maxim-gmsl2/0151-max-gmsl2-deser-report-link-frequency.patch +++ /dev/null @@ -1,113 +0,0 @@ -From: Julien Massot <julien.massot@collabora.com> -Date: Thu, 23 Mar 2023 12:05:47 +0100 -Subject: max-gmsl2-deser: report link frequency - -Report the link frequency by implementing the V4L2_CID_LINK_FREQ -control. ---- - drivers/media/i2c/max-gmsl2-deser.c | 40 +++++++++++++++++-------------------- - 1 file changed, 18 insertions(+), 22 deletions(-) - -diff --git a/drivers/media/i2c/max-gmsl2-deser.c b/drivers/media/i2c/max-gmsl2-deser.c -index 8a30110..79a4211 100644 ---- a/drivers/media/i2c/max-gmsl2-deser.c -+++ b/drivers/media/i2c/max-gmsl2-deser.c -@@ -98,7 +98,7 @@ struct max_gmsl2_deser_csi { - struct v4l2_subdev_format format; - struct max_gmsl2_deser_priv *priv; - struct regmap_field *phy_enable; -- struct regmap_field *phy_out_freq[2]; -+ struct regmap_field *phy_out_freq; - struct regmap_field *lane_polarities[2]; - struct regmap_field *lane_map[2]; - u8 id; -@@ -130,6 +130,7 @@ struct max_gmsl2_deser_priv { - struct regmap_field *gmsl1_link_locked[GMSL2_MAX_LINK]; - struct regmap_field *gmsl1_forward_cc[GMSL2_MAX_LINK]; - struct dentry *dbg_dir; -+ s64 tx_link_freq[1]; - }; - #define GMSL2_INVALID_REG_FIELD REG_FIELD(0, 0, 0) - -@@ -485,13 +486,9 @@ static int max_gmsl2_deser_init_regmap(struct max_gmsl2_deser_priv *priv) - field = &data->csi_phy_enable[i]; - csi->phy_enable = devm_regmap_field_alloc(dev, priv->regmap, *field); - -- field = &data->csi_phy_out_freq[i*2]; -+ field = &data->csi_phy_out_freq[i == 0 ? 1 : 2]; - if (field->reg) -- csi->phy_out_freq[0] = devm_regmap_field_alloc(dev, priv->regmap, *field); -- -- field = &data->csi_phy_out_freq[i * 2 + 1]; -- if (field->reg) -- csi->phy_out_freq[1] = devm_regmap_field_alloc(dev, priv->regmap, *field); -+ csi->phy_out_freq = devm_regmap_field_alloc(dev, priv->regmap, *field); - - field = &data->csi_lane_polarities[i * 2]; - csi->lane_polarities[0] = devm_regmap_field_alloc(dev, priv->regmap, *field); -@@ -679,7 +676,7 @@ static int max_gmsl2_deser_state_show(struct seq_file *seq, void *p) - struct max_gmsl2_deser_priv *priv = seq->private; - struct max_gmsl2_deser_pipe *pipe; - struct max_gmsl2_deser_csi *csi; -- unsigned int link_locked, enable, stream_id, link_sel, vid_lock, bpp, dt, vc, freq1, freq2, dphy_en, link_mode; -+ unsigned int link_locked, enable, stream_id, link_sel, vid_lock, bpp, dt, vc, freq, dphy_en, link_mode; - u8 link, pipeid, csiid; - int ret; - -@@ -752,18 +749,16 @@ static int max_gmsl2_deser_state_show(struct seq_file *seq, void *p) - - for_each_csi(csiid, priv) { - csi = &priv->csi[csiid]; -- ret = regmap_field_read(csi->phy_out_freq[0], &freq1); -- if (ret) -- break; -- ret = regmap_field_read(csi->phy_out_freq[1], &freq2); -+ ret = regmap_field_read(csi->phy_out_freq, &freq); - if (ret) - break; - ret = regmap_field_read(csi->phy_enable, &dphy_en); - if (ret) - break; -- seq_printf(seq, "CSI%d: freq DPHY[%d]:%dMHz enabled:%u DPHY[%d]:%dMHz enabled:%u\n", csiid, -- csiid * 2 , freq1 * 100, (dphy_en & BIT(0)) == BIT(0), -- csiid * 2 + 1, freq2 * 100, (dphy_en & BIT(1)) == BIT(1)); -+ seq_printf(seq, "CSI%d: freq:%dMHz DPHY[%d] enabled:%u DPHY[%d] enabled:%u\n", csiid, -+ freq * 100, -+ csiid * 2 , (dphy_en & BIT(0)) == BIT(0), -+ csiid * 2 + 1, (dphy_en & BIT(1)) == BIT(1)); - } - - return ret; -@@ -953,6 +948,11 @@ static int max_gmsl2_deser_csi_init_controls(struct max_gmsl2_deser_csi *csi) - &max_gmsl2_deser_csi_ctrl_ops, - V4L2_CID_PIXEL_RATE, 1, INT_MAX, - 1, 1); -+ -+ v4l2_ctrl_new_int_menu(&csi->ctrl_hdl, NULL, V4L2_CID_LINK_FREQ, -+ ARRAY_SIZE(csi->priv->tx_link_freq) - 1, 0, -+ csi->priv->tx_link_freq); -+ - if (csi->priv->data->patgen_base_addr[0]) - v4l2_ctrl_new_std_menu_items(&csi->ctrl_hdl, - &max_gmsl2_deser_csi_ctrl_ops, -@@ -1172,17 +1172,13 @@ static int max_gmsl2_deser_csi_init(struct max_gmsl2_deser_priv *priv, u8 csi_id - /* FIXME: take value from dt, written value in multiple of 100MHz - * Only the master DPhy might need to be set. - */ -- if (csi->phy_out_freq[0]) { -- ret = regmap_field_write(csi->phy_out_freq[0], 8); -+ if (csi->phy_out_freq) { -+ ret = regmap_field_write(csi->phy_out_freq, 8); - if (ret) - return ret; - } -+ priv->tx_link_freq[0] = 8 * 100000000; - -- if (csi->phy_out_freq[1]) { -- ret = regmap_field_write(csi->phy_out_freq[1], 8); -- if (ret) -- return ret; -- } - csi->format.format.width = 1280; - csi->format.format.height = 720; - csi->format.format.code = MEDIA_BUS_FMT_RGB888_1X24; diff --git a/debian/patches/apertis/maxim-gmsl2/0152-max-gmsl2-deser-do-not-try-to-link-non-existing-pipe.patch b/debian/patches/apertis/maxim-gmsl2/0152-max-gmsl2-deser-do-not-try-to-link-non-existing-pipe.patch deleted file mode 100644 index 68d17548aab..00000000000 --- a/debian/patches/apertis/maxim-gmsl2/0152-max-gmsl2-deser-do-not-try-to-link-non-existing-pipe.patch +++ /dev/null @@ -1,23 +0,0 @@ -From: Julien Massot <julien.massot@collabora.com> -Date: Thu, 23 Mar 2023 12:06:39 +0100 -Subject: max-gmsl2-deser: do not try to link non existing pipe - ---- - drivers/media/i2c/max-gmsl2-deser.c | 4 +++- - 1 file changed, 3 insertions(+), 1 deletion(-) - -diff --git a/drivers/media/i2c/max-gmsl2-deser.c b/drivers/media/i2c/max-gmsl2-deser.c -index 79a4211..fe3f724 100644 ---- a/drivers/media/i2c/max-gmsl2-deser.c -+++ b/drivers/media/i2c/max-gmsl2-deser.c -@@ -980,7 +980,9 @@ static int max_gmsl2_deser_csi_registered(struct v4l2_subdev *sd) - - dev_info(dev, "Registered CSI%u %s", csi->id, sd->name); - -- if (csi->id == 0) -+ if (priv->data->pipes == 1) -+ pipe = &priv->pipes[0]; -+ else if (csi->id == 0) - /* Pipe Y goes to CSI0 */ - pipe = &priv->pipes[1]; - else diff --git a/debian/patches/apertis/maxim-gmsl2/0153-drivers-media-max-gmsl2-deser-fix-pattern-generator.patch b/debian/patches/apertis/maxim-gmsl2/0153-drivers-media-max-gmsl2-deser-fix-pattern-generator.patch deleted file mode 100644 index e21152b09ff..00000000000 --- a/debian/patches/apertis/maxim-gmsl2/0153-drivers-media-max-gmsl2-deser-fix-pattern-generator.patch +++ /dev/null @@ -1,169 +0,0 @@ -From: Julien Massot <julien.massot@collabora.com> -Date: Thu, 20 Apr 2023 14:22:53 +0200 -Subject: drivers: media: max-gmsl2-deser: fix pattern generator - -The pattern generator needs to force the csi clock output, and -also requires that the deserializer is in pixel mode. - -Save the intial tunnel mode on device startup and switch the -device to pixel mode when pattern generator is enabled. -When pattern generator is disabled we restore the initial -settings. - -Also fix the link frequency reporting which is half the Bps -(two bytes per clock period). ---- - drivers/media/i2c/max-gmsl2-deser.c | 61 +++++++++++++++++++++++++++++++++++-- - 1 file changed, 58 insertions(+), 3 deletions(-) - -diff --git a/drivers/media/i2c/max-gmsl2-deser.c b/drivers/media/i2c/max-gmsl2-deser.c -index fe3f724..36ef82a 100644 ---- a/drivers/media/i2c/max-gmsl2-deser.c -+++ b/drivers/media/i2c/max-gmsl2-deser.c -@@ -39,6 +39,7 @@ struct max_gmsl2_deser_data { - u8 csi; - struct reg_field dev_rev; - struct reg_field csi_enable; -+ struct reg_field csi_out_force_en; - struct reg_field csi_wait_frame; - struct reg_field csi_phy_enable[GMSL2_MAX_CSI]; - /* Each CSI 4 lanes use 2 phys for lane polarities and lane map -@@ -67,6 +68,7 @@ struct max_gmsl2_deser_data { - struct reg_field reset_link[GMSL2_MAX_LINK]; - /* GMSL1 backward compatibility */ - const struct max_gmsl2_compat_gmsl1 *gmsl1; -+ struct reg_field tunnel_mode; - }; - - /* VC[7:6] DT[5:0] */ -@@ -119,6 +121,8 @@ struct max_gmsl2_deser_priv { - struct regmap_field *dev_rev; - u8 csi_use_cnt; - struct regmap_field *csi_enable; -+ /* In case of pattern generation we might have to force the csi output*/ -+ struct regmap_field *csi_out_force_en; - /* wait a new frame before generating a MIPI packet */ - struct regmap_field *csi_wait_frame; - struct regmap_field *pattern_clk_freq; -@@ -129,6 +133,8 @@ struct max_gmsl2_deser_priv { - struct regmap_field *gmsl1_highimm[GMSL2_MAX_LINK]; - struct regmap_field *gmsl1_link_locked[GMSL2_MAX_LINK]; - struct regmap_field *gmsl1_forward_cc[GMSL2_MAX_LINK]; -+ struct regmap_field *tunnel_mode; -+ bool tunnel_mode_saved; - struct dentry *dbg_dir; - s64 tx_link_freq[1]; - }; -@@ -209,6 +215,7 @@ const struct max_gmsl2_deser_data max96714_data = { - .dev_rev = REG_FIELD(0xe, 0, 3), - .csi_enable = REG_FIELD(0x313, 1, 1), - .csi_wait_frame = REG_FIELD(0x325, 7, 7), -+ .csi_out_force_en = REG_FIELD(0x330, 7, 7), - .csi_phy_enable = { REG_FIELD(0x332, 4, 5) }, - .csi_phy_out_freq = { GMSL2_INVALID_REG_FIELD, REG_FIELD(0x320, 0, 4), }, - .csi_lane_polarities = { REG_FIELD(0x335, 0, 2), REG_FIELD(0x335, 3, 5) }, -@@ -232,6 +239,7 @@ const struct max_gmsl2_deser_data max96714_data = { - .reset_link = { REG_FIELD(0x10, 6, 6) }, - /* This device is backward compatible with GMSL1 */ - .gmsl1 = &max96714_gmsl1_data, -+ .tunnel_mode = REG_FIELD(0x474, 0, 0), - - }; - #define IS_MAX96714(priv) (priv->data->device_id == MAX96714_DEVICE_ID) -@@ -511,6 +519,16 @@ static int max_gmsl2_deser_init_regmap(struct max_gmsl2_deser_priv *priv) - priv->csi_enable = - devm_regmap_field_alloc(dev, priv->regmap, *field); - -+ field = &data->csi_out_force_en; -+ if (field->reg) -+ priv->csi_out_force_en = -+ devm_regmap_field_alloc(dev, priv->regmap, *field); -+ -+ field = &data->tunnel_mode; -+ if (field->reg) -+ priv->tunnel_mode = -+ devm_regmap_field_alloc(dev, priv->regmap, *field); -+ - field = &data->csi_wait_frame; - if (field->reg) - priv->csi_wait_frame = -@@ -817,6 +835,22 @@ static int max_gmsl2_deser_csi_s_ctrl_pattern(struct max_gmsl2_deser_csi *csi, i - ret = pipes_clear_remap(priv); - if (ret) - return ret; -+ -+ /* Tunnel mode should be disabled for pattern generator */ -+ if (priv->tunnel_mode) { -+ ret = regmap_field_write(priv->tunnel_mode, 0); -+ if (ret) -+ return ret; -+ } -+ -+ if (priv->csi_out_force_en) { -+ /* Force CSI output clock for use in MIPI -+ * loopback test or pattern generator use.*/ -+ ret = regmap_field_write(priv->csi_out_force_en, 1); -+ if (ret) -+ return ret; -+ } -+ - } else { - /* Restore CSI to Pipe mapping */ - ret = pipes_clear_remap(priv); -@@ -830,9 +864,19 @@ static int max_gmsl2_deser_csi_s_ctrl_pattern(struct max_gmsl2_deser_csi *csi, i - ret = max_gmsl2_vtg_disable(priv->regmap, patgen_base); - if (ret) - return ret; -+ /* Restore tunnel mode */ -+ if (priv->tunnel_mode && priv->tunnel_mode_saved) { -+ ret = regmap_field_write(priv->tunnel_mode, 1); -+ if (ret) -+ return ret; -+ } -+ if (priv->csi_out_force_en) { -+ ret = regmap_field_write(priv->csi_out_force_en, 0); -+ if (ret) -+ return ret; -+ } - } - -- - return 0; - } - -@@ -1179,7 +1223,8 @@ static int max_gmsl2_deser_csi_init(struct max_gmsl2_deser_priv *priv, u8 csi_id - if (ret) - return ret; - } -- priv->tx_link_freq[0] = 8 * 100000000; -+ /* The link frequency is half the Bps and DPLL freq. */ -+ priv->tx_link_freq[0] = 4 * 100000000; - - csi->format.format.width = 1280; - csi->format.format.height = 720; -@@ -1498,7 +1543,7 @@ static int max_gmsl2_init_links(struct max_gmsl2_deser_priv *priv) - static int max_gmsl2_deser_probe(struct i2c_client *client) - { - struct max_gmsl2_deser_priv *priv; -- unsigned int dev_id, dev_rev; -+ unsigned int dev_id, dev_rev, val; - int i; - int ret; - -@@ -1548,6 +1593,16 @@ static int max_gmsl2_deser_probe(struct i2c_client *client) - - dev_info(&client->dev, "GMSL2 deserializer device_id: 0x%02x rev: 0x%01x", dev_id, dev_rev); - i2c_set_clientdata(client, priv); -+ -+ /* Save tunnel mode we might have to disable it later for pattern generator */ -+ if (priv->tunnel_mode) { -+ ret = regmap_field_read(priv->tunnel_mode, &val); -+ if (ret) -+ return ret; -+ -+ priv->tunnel_mode_saved = !!val; -+ } -+ - ret = max_gmsl2_init_links(priv); - if (ret) - return ret; diff --git a/debian/patches/apertis/maxim-gmsl2/0154-max-gmsl2-deser-change-max96714-compatible-to-max967.patch b/debian/patches/apertis/maxim-gmsl2/0154-max-gmsl2-deser-change-max96714-compatible-to-max967.patch deleted file mode 100644 index 1f72008fd50..00000000000 --- a/debian/patches/apertis/maxim-gmsl2/0154-max-gmsl2-deser-change-max96714-compatible-to-max967.patch +++ /dev/null @@ -1,64 +0,0 @@ -From 9be6f8a20ec2092ae1c5cb13a8ba00ad6da1a1bb Mon Sep 17 00:00:00 2001 -From: Julien Massot <julien.massot@collabora.com> -Date: Thu, 20 Apr 2023 22:15:29 +0200 -Subject: [PATCH 2/2] max-gmsl2-deser: change max96714 compatible to max96714f - -The device used for testing is a max96714f and not a max96714, -the only change in the registers is the device id: 0xca instead -of 0xc9. - -We may need to support several device id for the same registers -definition in the future. ---- - drivers/media/i2c/max-gmsl2-deser.c | 14 +++++++------- - 1 file changed, 7 insertions(+), 7 deletions(-) - -diff --git a/drivers/media/i2c/max-gmsl2-deser.c b/drivers/media/i2c/max-gmsl2-deser.c -index 36ef82a493..3df540e7ec 100644 ---- a/drivers/media/i2c/max-gmsl2-deser.c -+++ b/drivers/media/i2c/max-gmsl2-deser.c -@@ -206,9 +206,9 @@ const struct max_gmsl2_compat_gmsl1 max96714_gmsl1_data = { - .link_lock = { REG_FIELD(0xbcb, 0, 0) }, - }; - --#define MAX96714_DEVICE_ID 0xc9 --const struct max_gmsl2_deser_data max96714_data = { -- .device_id = MAX96714_DEVICE_ID, -+#define MAX96714F_DEVICE_ID 0xca -+const struct max_gmsl2_deser_data max96714f_data = { -+ .device_id = MAX96714F_DEVICE_ID, - .pipes = 1, - .links = 1, - .csi = 1, -@@ -242,7 +242,7 @@ const struct max_gmsl2_deser_data max96714_data = { - .tunnel_mode = REG_FIELD(0x474, 0, 0), - - }; --#define IS_MAX96714(priv) (priv->data->device_id == MAX96714_DEVICE_ID) -+#define IS_MAX96714F(priv) (priv->data->device_id == MAX96714F_DEVICE_ID) - - - #define MAX96914_DEVICE_ID 0xa0 -@@ -429,8 +429,8 @@ static const char* device_id_to_name(u8 device_id) - return "max96934"; - case MAX96914_DEVICE_ID: - return "max96914"; -- case MAX96714_DEVICE_ID: -- return "max96714"; -+ case MAX96714F_DEVICE_ID: -+ return "max96714f"; - default: - break; - } -@@ -1624,7 +1624,7 @@ static void max_gmsl2_deser_remove(struct i2c_client *client) - static const struct of_device_id max_gmsl2_deser_of_ids[] = { - { .compatible = "maxim,max96934", .data = &max96934_data }, - { .compatible = "maxim,max96914", .data = &max96914_data }, -- { .compatible = "maxim,max96714", .data = &max96714_data }, -+ { .compatible = "maxim,max96714f", .data = &max96714f_data }, - { } - }; - MODULE_DEVICE_TABLE(of, max_gmsl2_deser_of_ids); --- -2.39.2 - diff --git a/debian/patches/apertis/st-vgxy61/0168-Add-raw8-video-formats.patch b/debian/patches/apertis/st-vgxy61/0168-Add-raw8-video-formats.patch new file mode 100644 index 00000000000..050afaaecec --- /dev/null +++ b/debian/patches/apertis/st-vgxy61/0168-Add-raw8-video-formats.patch @@ -0,0 +1,85 @@ +From: fvz1brg <francisco.vaz@pt.bosch.com> +Date: Thu, 27 Apr 2023 09:54:40 +0000 +Subject: Add raw8 video formats + +--- + drivers/media/platform/cadence/cdns-csi2rx.c | 20 +++++++++++++++ + .../media/platform/ti/j721e-csi2rx/j721e-csi2rx.c | 30 +++++++++++++++++++--- + 2 files changed, 47 insertions(+), 3 deletions(-) + +diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c +index 7800ded..18d5db9 100644 +--- a/drivers/media/platform/cadence/cdns-csi2rx.c ++++ b/drivers/media/platform/cadence/cdns-csi2rx.c +@@ -123,6 +123,26 @@ static const struct csi2rx_fmt formats[] = { + .code = MEDIA_BUS_FMT_RGB888_1X24, + .bpp = 24, + }, ++ { ++ .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_Y8_1X8, ++ .bpp = 8, ++ }, + }; + + static const struct csi2rx_fmt *csi2rx_get_fmt_by_code(u32 code) +diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c +index 395b610..1af7b0b 100644 +--- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c ++++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c +@@ -118,13 +118,37 @@ static const struct ti_csi2rx_fmt formats[] = { + .code = MEDIA_BUS_FMT_VYUY8_1X16, + .csi_dt = MIPI_CSI2_DT_YUV422_8B, + .bpp = 16, +- }, +- { ++ }, { + .fourcc = V4L2_PIX_FMT_RGB24, + .code = MEDIA_BUS_FMT_RGB888_1X24, + .csi_dt = MIPI_CSI2_DT_RGB888, + .bpp = 24, +- }, ++ }, { ++ .fourcc = V4L2_PIX_FMT_SBGGR8, ++ .code = MEDIA_BUS_FMT_SBGGR8_1X8, ++ .csi_dt = MIPI_CSI2_DT_RAW8, ++ .bpp = 8, ++ }, { ++ .fourcc = V4L2_PIX_FMT_SGBRG8, ++ .code = MEDIA_BUS_FMT_SGBRG8_1X8, ++ .csi_dt = MIPI_CSI2_DT_RAW8, ++ .bpp = 8, ++ }, { ++ .fourcc = V4L2_PIX_FMT_SGRBG8, ++ .code = MEDIA_BUS_FMT_SGRBG8_1X8, ++ .csi_dt = MIPI_CSI2_DT_RAW8, ++ .bpp = 8, ++ }, { ++ .fourcc = V4L2_PIX_FMT_SRGGB8, ++ .code = MEDIA_BUS_FMT_SRGGB8_1X8, ++ .csi_dt = MIPI_CSI2_DT_RAW8, ++ .bpp = 8, ++ }, { ++ .fourcc = V4L2_PIX_FMT_GREY, ++ .code = MEDIA_BUS_FMT_Y8_1X8, ++ .csi_dt = MIPI_CSI2_DT_RAW8, ++ .bpp = 8, ++ }, + + /* More formats can be supported but they are not listed for now. */ + }; diff --git a/debian/patches/series b/debian/patches/series index e3f10e9a422..317a5c5b20f 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -212,14 +212,11 @@ apertis/am62x/0158-Add-memory-region-and-mailbox-configuration-for-r5f-.patch # Support for Maxim GMSL devices apertis/maxim-gmsl2/0001-drivers-media-add-maxim-GMSL2-deserializer-driver.patch -apertis/maxim-gmsl2/0151-max-gmsl2-deser-report-link-frequency.patch -apertis/maxim-gmsl2/0152-max-gmsl2-deser-do-not-try-to-link-non-existing-pipe.patch -apertis/maxim-gmsl2/0153-drivers-media-max-gmsl2-deser-fix-pattern-generator.patch -apertis/maxim-gmsl2/0154-max-gmsl2-deser-change-max96714-compatible-to-max967.patch # Additional video capture format apertis/am62x-csi/0153-csi2rx-add-RGB-format.patch apertis/am62x-csi/0159-media-platform-ti-j721e-csi2rx-add-missing-MIPI-DT-f.patch +apertis/am62x-csi/0160-media-platform-cadence-use-asd-fwnode.patch # Backport of VGXY61 camera driver apertis/st-vgxy61/0162-media-i2c-Add-driver-for-ST-VGXY61-camera-sensor.patch @@ -228,4 +225,4 @@ apertis/st-vgxy61/0164-media-i2c-st-vgxy61-Fix-smatch-warnings.patch apertis/st-vgxy61/0165-media-i2c-st-vgxy61-Use-asm-intead-of-asm-generic.patch apertis/st-vgxy61/0166-media-v4l-Add-1X16-16-bit-greyscale-media-bus-code-d.patch apertis/st-vgxy61/0167-media-v4l-ctrls-Add-a-control-for-HDR-mode.patch - +apertis/st-vgxy61/0168-Add-raw8-video-formats.patch -- GitLab