2023-11-23 11:52:00

by Zhi Mao (毛智)

[permalink] [raw]
Subject: [PATCH 0/2] media: i2c: Add support for GC08A3 sensor

This series adds YAML DT binding and V4L2 sub-device driver for Galaxycore's
GC08A3 8-megapixel 10-bit RAW CMOS 1/4" sensor, with an MIPI CSI-2 image data
interface and the I2C control bus.

The driver is implemented with V4L2 framework.
- Async registered as a V4L2 sub-device.
- As the first component of camera system including Seninf, ISP pipeline.
- A media entity that provides one source pad in common.
- Used in camera features on ChromeOS application.

Also this driver supports following features:
- manual exposure and analog gain control support
- vertical blanking control support
- test pattern support
- media controller support
- runtime PM support
- support resolution: 3264x2448@30fps, 1920x1080@60fps

Thanks

Zhi Mao (2):
media: dt-bindings: media: i2c: Document GC08A3 bindings
media: i2c: Add GC08A3 image sensor driver

.../bindings/media/i2c/galaxycore,gc08a3.yaml | 128 ++
.../devicetree/bindings/vendor-prefixes.yaml | 2 +
drivers/media/i2c/Kconfig | 14 +
drivers/media/i2c/Makefile | 1 +
drivers/media/i2c/gc08a3.c | 2046 +++++++++++++++++
5 files changed, 2191 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/i2c/galaxycore,gc08a3.yaml
create mode 100644 drivers/media/i2c/gc08a3.c

--
2.25.1



2023-11-23 11:52:54

by Zhi Mao (毛智)

[permalink] [raw]
Subject: [PATCH 1/2] media: dt-bindings: media: i2c: Document GC08A3 bindings

Add YAML device tree binding for GC08A3 CMOS image sensor,
and the relevant MAINTAINERS entries.

Signed-off-by: Zhi Mao <[email protected]>
---
.../bindings/media/i2c/galaxycore,gc08a3.yaml | 128 ++++++++++++++++++
.../devicetree/bindings/vendor-prefixes.yaml | 2 +
2 files changed, 130 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/i2c/galaxycore,gc08a3.yaml

diff --git a/Documentation/devicetree/bindings/media/i2c/galaxycore,gc08a3.yaml b/Documentation/devicetree/bindings/media/i2c/galaxycore,gc08a3.yaml
new file mode 100644
index 000000000000..089ea321da91
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/galaxycore,gc08a3.yaml
@@ -0,0 +1,128 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/i2c/galaxycore,gc08a3.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: GalaxyCore gc08a3 1/4" 8M Pixel MIPI CSI-2 sensor
+
+maintainers:
+ - Zhi Mao <[email protected]>
+
+description: |-
+ The gc08a3 is a raw image sensor with an MIPI CSI-2 image data
+ interface and CCI (I2C compatible) control bus. The output format
+ is raw Bayer.
+
+properties:
+ compatible:
+ const: GalaxyCore,gc08a3
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: Reference to the mclk clock.
+
+ assigned-clocks:
+ maxItems: 1
+
+ assigned-clock-rates:
+ maxItems: 1
+
+ enable-gpios:
+ description: Reference to the GPIO connected to the RESETB pin. Active low.
+ maxItems: 1
+
+ vddio-supply:
+ description: Definition of the regulator used for the VDDIO power supply.
+
+ vdda-supply:
+ description: Definition of the regulator used for the VDDA power supply.
+
+ vddd-supply:
+ description: Definition of the regulator used for the VDDD power supply.
+
+ port:
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ unevaluatedProperties: false
+
+ properties:
+ endpoint:
+ $ref: /schemas/media/video-interfaces.yaml#
+ unevaluatedProperties: false
+
+ properties:
+ data-lanes:
+ oneOf:
+ - items:
+ - const: 1
+ - const: 2
+ - const: 3
+ - const: 4
+ - items:
+ - const: 1
+ - const: 2
+
+ link-frequencies: true
+
+ required:
+ - data-lanes
+ - link-frequencies
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - assigned-clocks
+ - assigned-clock-rates
+ - vddio-supply
+ - vdda-supply
+ - vddd-supply
+ - port
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ sensor0@2 {
+ status = "okay";
+ compatible = "GalaxyCore,gc08a3";
+ reg = <0x31>;
+
+ clocks = <&topckgen CLK_TOP_CAMTG>,
+ <&topckgen CLK_TOP_UNIVPLL_192M_D8>;
+ clock-names = "xvclk", "freq_mux";
+ clock-frequency = <24000000>;
+
+ assigned-clocks = <&topckgen CLK_TOP_CAMTG>,
+ <&topckgen CLK_TOP_UNIVPLL_192M_D8>;
+ assigned-clock-parents = <&topckgen CLK_TOP_UNIVPLL_192M_D8>;
+ assigned-clock-rates = <0>, <24000000>;
+
+ enable-gpios = <&pio 19 GPIO_ACTIVE_HIGH>;
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&camera_pins_cam0>;
+
+ avdd-supply = <&mt6359_vfe28_ldo_reg>;
+
+ port {
+ sensor0_out_2: endpoint {
+ data-lanes = <1 2 3 4>;
+ link-frequencies = /bits/ 64 <336000000 207000000>;
+ remote-endpoint = <&seninf_csi_port_0_in_2>;
+ };
+ };
+ };
+
+ };
+
+...
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index 309b94c328c8..a0bbec0bfee2 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -502,6 +502,8 @@ patternProperties:
description: Fujitsu Ltd.
"^fxtec,.*":
description: FX Technology Ltd.
+ "^GalaxyCore,.*":
+ description: GalaxyCore Inc.
"^gardena,.*":
description: GARDENA GmbH
"^gateway,.*":
--
2.25.1

2023-11-23 11:53:42

by Zhi Mao (毛智)

[permalink] [raw]
Subject: [PATCH 2/2] media: i2c: Add GC08A3 image sensor driver

Add a V4L2 sub-device driver for Galaxycore GC08A3 image sensor.

Signed-off-by: Zhi Mao <[email protected]>
---
drivers/media/i2c/Kconfig | 14 +
drivers/media/i2c/Makefile | 1 +
drivers/media/i2c/gc08a3.c | 2046 ++++++++++++++++++++++++++++++++++++
3 files changed, 2061 insertions(+)
create mode 100644 drivers/media/i2c/gc08a3.c

diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 59ee0ca2c978..2e37be537690 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -1451,6 +1451,20 @@ config VIDEO_THS7303
To compile this driver as a module, choose M here: the
module will be called ths7303.

+config VIDEO_GC08A3
+ tristate "GalaxyCore gc08a3 sensor support"
+ depends on GPIOLIB && I2C && VIDEO_DEV
+ select V4L2_FWNODE
+ select MEDIA_CONTROLLER
+ select VIDEO_V4L2_SUBDEV_API
+ select REGMAP_I2C
+ help
+ This is a Video4Linux2 sensor driver for the GalaxyCore gc08a3
+ camera.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gc08a3.
+
endmenu

#
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index f5010f80a21f..ec40dbd75e7a 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -36,6 +36,7 @@ obj-$(CONFIG_VIDEO_DW9719) += dw9719.o
obj-$(CONFIG_VIDEO_DW9768) += dw9768.o
obj-$(CONFIG_VIDEO_DW9807_VCM) += dw9807-vcm.o
obj-$(CONFIG_VIDEO_ET8EK8) += et8ek8/
+obj-$(CONFIG_VIDEO_GC08A3) += gc08a3.o
obj-$(CONFIG_VIDEO_HI556) += hi556.o
obj-$(CONFIG_VIDEO_HI846) += hi846.o
obj-$(CONFIG_VIDEO_HI847) += hi847.o
diff --git a/drivers/media/i2c/gc08a3.c b/drivers/media/i2c/gc08a3.c
new file mode 100644
index 000000000000..d3cf62b65c2e
--- /dev/null
+++ b/drivers/media/i2c/gc08a3.c
@@ -0,0 +1,2046 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * gc08a3.c - gc08a3 sensor driver
+ *
+ * Copyright 2023 Mediatek
+ *
+ * Zhi Mao <[email protected]>
+ */
+
+#include <asm/unaligned.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <media/media-entity.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+//=========================================
+#define GC08A3_REG_VALUE_08BIT 1
+#define GC08A3_REG_VALUE_16BIT 2
+#define GC08A3_REG_VALUE_24BIT 3
+
+#define GC08A3_REG_CHIP_ID 0x03f0
+#define GC08A3_CHIP_ID 0x08a3
+
+#define GC08A3_NATIVE_WIDTH 3264
+#define GC08A3_NATIVE_HEIGHT 2448
+
+#define GC08A3_REG_TEST_PATTERN_EN 0x008c
+#define GC08A3_REG_TEST_PATTERN_IDX 0x008d
+#define GC08A3_TEST_PATTERN_EN 0x01
+
+#define GC08A3_STRAEMING_REG 0x0100
+
+#define GC08A3_SCLK 280000000LL
+
+#define GC08A3_DEFAULT_CLK_FREQ 24000000
+#define GC08A3_FPS 30
+#define GC08A3_MBUS_CODE MEDIA_BUS_FMT_SRGGB10_1X10
+#define GC08A3_DATA_LANES 4
+
+//for 1920*1080
+#define GC08A3_LINK_FREQ_207MHZ 207000000ULL
+//for 3264*2448
+#define GC08A3_LINK_FREQ_336MHZ 336000000ULL
+
+#define GC08A3_RGB_DEPTH 10
+
+//frame length
+#define GC08A3_FL_REG 0x0340
+#define GC08A3_VTS_30FPS 2548
+#define GC08A3_VTS_30FPS_MIN 2548
+#define GC08A3_VTS_60FPS 1276
+#define GC08A3_VTS_60FPS_MIN 1276
+#define GC08A3_VTS_MAX 0xfff0
+#define GC08A3_FL_MARGIN 48
+
+// line length
+#define GC08A3_LL_REG 0x0342
+#define GC08A3_HTS_30FPS 3640
+#define GC08A3_HTS_60FPS 3640
+
+#define GC08A3_EXP_REG 0x0202
+#define GC08A3_EXP_MARGIN 16
+#define GC08A3_EXP_MIN 4
+#define GC08A3_EXP_STEP 1
+
+#define GC08A3_FLIP_REG 0x0101
+#define GC08A3_FLIP_H_MASK 0x1
+#define GC08A3_FLIP_V_MASK 0x2
+
+#define GC08A3_AGAIN_REG 0x0204
+#define GC08A3_AGAIN_MIN 1024
+#define GC08A3_AGAIN_MAX (1024 * 16)
+#define GC08A3_AGAIN_STEP 1
+
+#define GC08A3_DGAIN_REG 0x020e
+#define GC08A3_DGAIN_MIN 1024
+#define GC08A3_DGAIN_MAX 1024
+#define GC08A3_DGAIN_STEP 1
+//============================================================
+
+//============================================================
+static const char *const gc08a3_test_pattern_menu[] = {
+ "No Pattern", "Solid Black", "Solid White", "Solid Red",
+ "Solid Green", "Solid Blue", "Solid Yellow", "Colour Bar",
+};
+
+enum {
+ GC08A3_LINK_FREQ_336MHZ_CFG,
+ GC08A3_LINK_FREQ_207MHZ_CFG,
+};
+
+static const s64 link_freq_menu_items[] = {
+ GC08A3_LINK_FREQ_336MHZ,
+ GC08A3_LINK_FREQ_207MHZ,
+};
+
+static const char *const gc08a3_supply_name[] = {
+ "avdd",
+ "dvdd",
+ "dovdd",
+};
+
+#define GC08A3_NUM_SUPPLIES ARRAY_SIZE(gc08a3_supply_name)
+
+struct gc08a3 {
+ struct device *dev;
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+ struct v4l2_mbus_framefmt fmt;
+ struct i2c_client *client;
+
+ struct v4l2_rect crop;
+
+ struct clk *xclk;
+ struct regulator_bulk_data supplies[GC08A3_NUM_SUPPLIES];
+ struct gpio_desc *enable_gpio;
+
+ struct regmap *regmap;
+
+ struct v4l2_ctrl_handler ctrls;
+ struct v4l2_ctrl *pixel_rate;
+ struct v4l2_ctrl *link_freq;
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *vblank;
+ struct v4l2_ctrl *hblank;
+
+ /*
+ * Serialize control access, get/set format, get selection
+ * and start streaming.
+ */
+ struct mutex mutex;
+
+ bool streaming;
+
+ /* Current mode */
+ const struct gc08a3_mode *cur_mode;
+};
+
+struct reg_8 {
+ u16 address;
+ u8 val;
+};
+
+struct gc08a3_reg_list {
+ u32 num_of_regs;
+ const struct reg_8 *regs;
+};
+
+struct gc08a3_link_freq_config {
+ const struct gc08a3_reg_list reg_list;
+};
+
+enum {
+ GC08A3_TABLE_WAIT_MS = 0,
+ GC08A3_TABLE_END,
+};
+
+/*From gc08a3_mode_tbls.h*/
+static const struct reg_8 mode_3264x2448[] = {
+ /*system*/
+ { 0x031c, 0x60 },
+ { 0x0337, 0x04 },
+ { 0x0335, 0x51 },
+ { 0x0336, 0x70 },
+ { 0x0383, 0xbb },
+ { 0x031a, 0x00 },
+ { 0x0321, 0x10 },
+ { 0x0327, 0x03 },
+ { 0x0325, 0x40 },
+ { 0x0326, 0x23 },
+ { 0x0314, 0x11 },
+ { 0x0315, 0xd6 },
+ { 0x0316, 0x01 },
+ { 0x0334, 0x40 },
+ { 0x0324, 0x42 },
+ { 0x031c, 0x00 },
+ { 0x031c, 0x9f },
+ { 0x0344, 0x00 },
+ { 0x0345, 0x06 },
+ { 0x0346, 0x00 },
+ { 0x0347, 0x04 },
+ { 0x0348, 0x0c },
+ { 0x0349, 0xd0 }, //3280
+ { 0x034a, 0x09 },
+ { 0x034b, 0x9c }, //2460
+ { 0x0202, 0x09 },
+ { 0x0203, 0x04 }, //Exp
+ { 0x0340, 0x09 },
+ { 0x0341, 0xf4 }, //FL
+ { 0x0342, 0x07 },
+ { 0x0343, 0x1c }, //LineLength
+
+ { 0x0226, 0x00 }, //min vb[15:8]
+ { 0x0227, 0x28 }, //min vb[7:0]
+ { 0x0e38, 0x49 },
+ { 0x0210, 0x13 },
+ { 0x0218, 0x00 },
+ { 0x0241, 0x88 },
+ { 0x0392, 0x60 },
+
+ /*ISP*/
+ { 0x031c, 0x80 },
+ { 0x03fe, 0x10 }, //CISCTL_rst
+ { 0x03fe, 0x00 },
+ { 0x031c, 0x9f },
+ { 0x03fe, 0x00 },
+ { 0x03fe, 0x00 },
+ { 0x03fe, 0x00 },
+ { 0x03fe, 0x00 },
+ { 0x031c, 0x80 },
+ { 0x03fe, 0x10 },
+ { 0x03fe, 0x00 },
+ { 0x031c, 0x9f },
+ { 0x00a2, 0x00 },
+ { 0x00a3, 0x00 },
+ { 0x00ab, 0x00 },
+ { 0x00ac, 0x00 },
+ { 0x05a0, 0x82 },
+ { 0x05ac, 0x00 },
+ { 0x05ad, 0x01 },
+ { 0x05ae, 0x00 },
+ { 0x0800, 0x0a },
+ { 0x0801, 0x14 },
+ { 0x0802, 0x28 },
+ { 0x0803, 0x34 },
+ { 0x0804, 0x0e },
+ { 0x0805, 0x33 },
+ { 0x0806, 0x03 },
+ { 0x0807, 0x8a },
+ { 0x0808, 0x50 },
+ { 0x0809, 0x00 },
+ { 0x080a, 0x34 },
+ { 0x080b, 0x03 },
+ { 0x080c, 0x26 },
+ { 0x080d, 0x03 },
+ { 0x080e, 0x18 },
+ { 0x080f, 0x03 },
+ { 0x0810, 0x10 },
+ { 0x0811, 0x03 },
+ { 0x0812, 0x00 },
+ { 0x0813, 0x00 },
+ { 0x0814, 0x01 },
+ { 0x0815, 0x00 },
+ { 0x0816, 0x01 },
+ { 0x0817, 0x00 },
+ { 0x0818, 0x00 },
+ { 0x0819, 0x0a },
+ { 0x081a, 0x01 },
+ { 0x081b, 0x6c },
+ { 0x081c, 0x00 },
+ { 0x081d, 0x0b },
+ { 0x081e, 0x02 },
+ { 0x081f, 0x00 },
+ { 0x0820, 0x00 },
+ { 0x0821, 0x0c },
+ { 0x0822, 0x02 },
+ { 0x0823, 0xd9 },
+ { 0x0824, 0x00 },
+ { 0x0825, 0x0d },
+ { 0x0826, 0x03 },
+ { 0x0827, 0xf0 },
+ { 0x0828, 0x00 },
+ { 0x0829, 0x0e },
+ { 0x082a, 0x05 },
+ { 0x082b, 0x94 },
+ { 0x082c, 0x09 },
+ { 0x082d, 0x6e },
+ { 0x082e, 0x07 },
+ { 0x082f, 0xe6 },
+ { 0x0830, 0x10 },
+ { 0x0831, 0x0e },
+ { 0x0832, 0x0b },
+ { 0x0833, 0x2c },
+ { 0x0834, 0x14 },
+ { 0x0835, 0xae },
+ { 0x0836, 0x0f },
+ { 0x0837, 0xc4 },
+ { 0x0838, 0x18 },
+ { 0x0839, 0x0e },
+ { 0x05ac, 0x01 },
+ { 0x059a, 0x00 },
+ { 0x059b, 0x00 },
+ { 0x059c, 0x01 },
+ { 0x0598, 0x00 },
+ { 0x0597, 0x14 },
+ { 0x05ab, 0x09 },
+ { 0x05a4, 0x02 },
+ { 0x05a3, 0x05 },
+ { 0x05a0, 0xc2 },
+ { 0x0207, 0xc4 },
+
+ /*GAIN*/
+ { 0x0204, 0x04 },
+ { 0x0205, 0x00 },
+ { 0x0050, 0x5c },
+ { 0x0051, 0x44 },
+
+ /*out window*/
+ { 0x009a, 0x66 },
+ { 0x0351, 0x00 },
+ { 0x0352, 0x06 }, //out_win_y1
+ { 0x0353, 0x00 },
+ { 0x0354, 0x08 }, //out_win_x1
+ { 0x034c, 0x0c },
+ { 0x034d, 0xc0 }, //3264
+ { 0x034e, 0x09 },
+ { 0x034f, 0x90 }, //2448
+
+ /*MIPI*/
+ { 0x0114, 0x03 }, //0:1lane 1:2lane 3:4lane
+ { 0x0180, 0x65 }, //[3:0]dphy_mipi_diff
+ { 0x0181, 0xf0 },
+ { 0x0185, 0x01 },
+ { 0x0115, 0x30 },
+ { 0x011b, 0x12 },
+ { 0x011c, 0x12 },
+ { 0x0121, 0x06 }, //T_LPX
+ { 0x0122, 0x06 }, //T_CLK_HS_PREPARE
+ { 0x0123, 0x15 }, //T_CLK_zero
+ { 0x0124, 0x01 }, //T_CLK_PRE
+ { 0x0125, 0x0b }, //T_CLK_POST
+ { 0x0126, 0x08 }, //T_CLK_TRAIL
+ { 0x0129, 0x06 }, //T_HS_PREPARE
+ { 0x012a, 0x08 }, //T_HS_Zero
+ { 0x012b, 0x08 }, //T_HS_TRAIL
+
+ { 0x0a73, 0x60 },
+ { 0x0a70, 0x11 },
+ { 0x0313, 0x80 },
+ { 0x0aff, 0x00 },
+ { 0x0aff, 0x00 },
+ { 0x0aff, 0x00 },
+ { 0x0aff, 0x00 },
+ { 0x0aff, 0x00 },
+ { 0x0aff, 0x00 },
+ { 0x0aff, 0x00 },
+ { 0x0aff, 0x00 },
+ { 0x0a70, 0x00 },
+ { 0x00a4, 0x80 },
+ { 0x0316, 0x01 },
+ { 0x0a67, 0x00 },
+ { 0x0084, 0x10 },
+ { 0x0102, 0x09 },
+
+ { GC08A3_TABLE_END, 0x00 }
+};
+
+static const struct reg_8 mode_1920x1080[] = {
+ /*system*/
+ { 0x031c, 0x60 },
+ { 0x0337, 0x04 },
+ { 0x0335, 0x51 },
+ { 0x0336, 0x45 },
+ { 0x0383, 0x8b },
+ { 0x031a, 0x00 },
+ { 0x0321, 0x10 },
+ { 0x0327, 0x03 },
+ { 0x0325, 0x40 },
+ { 0x0326, 0x23 },
+ { 0x0314, 0x11 },
+ { 0x0315, 0xd6 },
+ { 0x0316, 0x01 },
+ { 0x0334, 0x40 },
+ { 0x0324, 0x42 },
+ { 0x031c, 0x00 },
+ { 0x031c, 0x9f },
+ { 0x0344, 0x02 },
+ { 0x0345, 0xa6 },
+ { 0x0346, 0x02 },
+ { 0x0347, 0xb0 },
+ { 0x0348, 0x07 },
+ { 0x0349, 0x90 }, //1936
+ { 0x034a, 0x04 },
+ { 0x034b, 0x44 }, //1092
+ { 0x0202, 0x03 },
+ { 0x0203, 0x00 }, //Exp
+ { 0x0340, 0x04 },
+ { 0x0341, 0xfc }, //FL
+ { 0x0342, 0x07 },
+ { 0x0343, 0x1c }, //LineLength
+
+ { 0x0226, 0x00 }, //min vb[15:8]
+ { 0x0227, 0x88 }, //min vb[7:0]
+ { 0x0e38, 0x49 },
+ { 0x0210, 0x13 },
+ { 0x0218, 0x00 },
+ { 0x0241, 0x88 },
+ { 0x0392, 0x60 },
+
+ /*ISP*/
+ { 0x031c, 0x80 },
+ { 0x03fe, 0x10 }, //CISCTL_rst
+ { 0x03fe, 0x00 },
+ { 0x031c, 0x9f },
+ { 0x03fe, 0x00 },
+ { 0x03fe, 0x00 },
+ { 0x03fe, 0x00 },
+ { 0x03fe, 0x00 },
+ { 0x031c, 0x80 },
+ { 0x03fe, 0x10 },
+ { 0x03fe, 0x00 },
+ { 0x031c, 0x9f },
+ { 0x00a2, 0xac },
+ { 0x00a3, 0x02 },
+ { 0x00ab, 0xa0 },
+ { 0x00ac, 0x02 },
+ { 0x05a0, 0x82 },
+ { 0x05ac, 0x00 },
+ { 0x05ad, 0x01 },
+ { 0x05ae, 0x00 },
+ { 0x0800, 0x0a },
+ { 0x0801, 0x14 },
+ { 0x0802, 0x28 },
+ { 0x0803, 0x34 },
+ { 0x0804, 0x0e },
+ { 0x0805, 0x33 },
+ { 0x0806, 0x03 },
+ { 0x0807, 0x8a },
+ { 0x0808, 0x50 },
+ { 0x0809, 0x00 },
+ { 0x080a, 0x34 },
+ { 0x080b, 0x03 },
+ { 0x080c, 0x26 },
+ { 0x080d, 0x03 },
+ { 0x080e, 0x18 },
+ { 0x080f, 0x03 },
+ { 0x0810, 0x10 },
+ { 0x0811, 0x03 },
+ { 0x0812, 0x00 },
+ { 0x0813, 0x00 },
+ { 0x0814, 0x01 },
+ { 0x0815, 0x00 },
+ { 0x0816, 0x01 },
+ { 0x0817, 0x00 },
+ { 0x0818, 0x00 },
+ { 0x0819, 0x0a },
+ { 0x081a, 0x01 },
+ { 0x081b, 0x6c },
+ { 0x081c, 0x00 },
+ { 0x081d, 0x0b },
+ { 0x081e, 0x02 },
+ { 0x081f, 0x00 },
+ { 0x0820, 0x00 },
+ { 0x0821, 0x0c },
+ { 0x0822, 0x02 },
+ { 0x0823, 0xd9 },
+ { 0x0824, 0x00 },
+ { 0x0825, 0x0d },
+ { 0x0826, 0x03 },
+ { 0x0827, 0xf0 },
+ { 0x0828, 0x00 },
+ { 0x0829, 0x0e },
+ { 0x082a, 0x05 },
+ { 0x082b, 0x94 },
+ { 0x082c, 0x09 },
+ { 0x082d, 0x6e },
+ { 0x082e, 0x07 },
+ { 0x082f, 0xe6 },
+ { 0x0830, 0x10 },
+ { 0x0831, 0x0e },
+ { 0x0832, 0x0b },
+ { 0x0833, 0x2c },
+ { 0x0834, 0x14 },
+ { 0x0835, 0xae },
+ { 0x0836, 0x0f },
+ { 0x0837, 0xc4 },
+ { 0x0838, 0x18 },
+ { 0x0839, 0x0e },
+ { 0x05ac, 0x01 },
+ { 0x059a, 0x00 },
+ { 0x059b, 0x00 },
+ { 0x059c, 0x01 },
+ { 0x0598, 0x00 },
+ { 0x0597, 0x14 },
+ { 0x05ab, 0x09 },
+ { 0x05a4, 0x02 },
+ { 0x05a3, 0x05 },
+ { 0x05a0, 0xc2 },
+ { 0x0207, 0xc4 },
+
+ /*GAIN*/
+ { 0x0204, 0x04 },
+ { 0x0205, 0x00 },
+ { 0x0050, 0x38 },
+ { 0x0051, 0x20 },
+
+ /*out window*/
+ { 0x009a, 0x66 },
+ { 0x0351, 0x00 },
+ { 0x0352, 0x06 }, //out_win_y1
+ { 0x0353, 0x00 },
+ { 0x0354, 0x08 }, //out_win_x1
+ { 0x034c, 0x07 },
+ { 0x034d, 0x80 }, //1920
+ { 0x034e, 0x04 },
+ { 0x034f, 0x38 }, //1080
+
+ /*MIPI*/
+ { 0x0114, 0x03 }, //0:1lane 1:2lane 3:4lane
+ { 0x0180, 0x65 }, //[3:0]dphy_mipi_diff
+ { 0x0181, 0xf0 },
+ { 0x0185, 0x01 },
+ { 0x0115, 0x30 },
+ { 0x011b, 0x12 },
+ { 0x011c, 0x12 },
+ { 0x0121, 0x02 }, //T_LPX
+ { 0x0122, 0x03 }, //T_CLK_HS_PREPARE
+ { 0x0123, 0x0c }, //T_CLK_zero
+ { 0x0124, 0x00 }, //T_CLK_PRE
+ { 0x0125, 0x09 }, //T_CLK_POST
+ { 0x0126, 0x06 }, //T_CLK_TRAIL
+ { 0x0129, 0x04 }, //T_HS_PREPARE
+ { 0x012a, 0x03 }, //T_HS_Zero
+ { 0x012b, 0x06 }, //T_HS_TRAIL
+
+ { 0x0a73, 0x60 },
+ { 0x0a70, 0x11 },
+ { 0x0313, 0x80 },
+ { 0x0aff, 0x00 },
+ { 0x0aff, 0x00 },
+ { 0x0aff, 0x00 },
+ { 0x0aff, 0x00 },
+ { 0x0aff, 0x00 },
+ { 0x0aff, 0x00 },
+ { 0x0aff, 0x00 },
+ { 0x0aff, 0x00 },
+ { 0x0a70, 0x00 },
+ { 0x00a4, 0x80 },
+ { 0x0316, 0x01 },
+ { 0x0a67, 0x00 },
+ { 0x0084, 0x10 },
+ { 0x0102, 0x09 },
+
+ { GC08A3_TABLE_END, 0x00 }
+};
+
+static const struct reg_8 mode_table_common[] = {
+ { GC08A3_STRAEMING_REG, 0x00 },
+ /*system*/
+ { 0x031c, 0x60 },
+ { 0x0337, 0x04 },
+ { 0x0335, 0x51 },
+ { 0x0336, 0x70 },
+ { 0x0383, 0xbb },
+ { 0x031a, 0x00 },
+ { 0x0321, 0x10 },
+ { 0x0327, 0x03 },
+ { 0x0325, 0x40 },
+ { 0x0326, 0x23 },
+ { 0x0314, 0x11 },
+ { 0x0315, 0xd6 },
+ { 0x0316, 0x01 },
+ { 0x0334, 0x40 },
+ { 0x0324, 0x42 },
+ { 0x031c, 0x00 },
+ { 0x031c, 0x9f },
+ { 0x039a, 0x13 },
+ { 0x0084, 0x30 },
+ { 0x02b3, 0x08 },
+ { 0x0057, 0x0c },
+ { 0x05c3, 0x50 },
+ { 0x0311, 0x90 },
+ { 0x05a0, 0x02 },
+ { 0x0074, 0x0a },
+ { 0x0059, 0x11 },
+ { 0x0070, 0x05 },
+ { 0x0101, 0x00 }, //[1]updown [0]mirror
+
+ /*analog*/
+ { 0x0344, 0x00 },
+ { 0x0345, 0x06 },
+ { 0x0346, 0x00 },
+ { 0x0347, 0x04 },
+ { 0x0348, 0x0c },
+ { 0x0349, 0xd0 }, //3280
+ { 0x034a, 0x09 },
+ { 0x034b, 0x9c }, //2460
+ { 0x0202, 0x09 },
+ { 0x0203, 0x04 }, //Exp
+
+ { 0x0219, 0x05 }, //[4]FL_depend_exp
+ { 0x0226, 0x00 }, //min vb[15:8]
+ { 0x0227, 0x28 }, //min vb[7:0]
+ { 0x0e0a, 0x00 },
+ { 0x0e0b, 0x00 },
+ { 0x0e24, 0x04 },
+ { 0x0e25, 0x04 },
+ { 0x0e26, 0x00 },
+ { 0x0e27, 0x10 },
+ { 0x0e01, 0x74 },
+ { 0x0e03, 0x47 },
+ { 0x0e04, 0x33 },
+ { 0x0e05, 0x44 },
+ { 0x0e06, 0x44 },
+ { 0x0e0c, 0x1e },
+ { 0x0e17, 0x3a },
+ { 0x0e18, 0x3c },
+ { 0x0e19, 0x40 },
+ { 0x0e1a, 0x42 },
+ { 0x0e28, 0x21 },
+ { 0x0e2b, 0x68 },
+ { 0x0e2c, 0x0d },
+ { 0x0e2d, 0x08 },
+ { 0x0e34, 0xf4 },
+ { 0x0e35, 0x44 },
+ { 0x0e36, 0x07 },
+ { 0x0e38, 0x49 },
+ { 0x0210, 0x13 },
+ { 0x0218, 0x00 },
+ { 0x0241, 0x88 },
+ { 0x0e32, 0x00 },
+ { 0x0e33, 0x18 },
+ { 0x0e42, 0x03 },
+ { 0x0e43, 0x80 },
+ { 0x0e44, 0x04 },
+ { 0x0e45, 0x00 },
+ { 0x0e4f, 0x04 },
+ { 0x057a, 0x20 },
+ { 0x0381, 0x7c },
+ { 0x0382, 0x9b },
+ { 0x0384, 0xfb },
+ { 0x0389, 0x38 },
+ { 0x038a, 0x03 },
+ { 0x0390, 0x6a },
+ { 0x0391, 0x0b },
+ { 0x0392, 0x60 },
+ { 0x0393, 0xc1 },
+ { 0x0396, 0xff },
+ { 0x0398, 0x62 },
+
+ /*cisctl reset*/
+ { 0x031c, 0x80 },
+ { 0x03fe, 0x10 }, //CISCTL_rst
+ { 0x03fe, 0x00 },
+ { 0x031c, 0x9f },
+ { 0x03fe, 0x00 },
+ { 0x03fe, 0x00 },
+ { 0x03fe, 0x00 },
+ { 0x03fe, 0x00 },
+ { 0x031c, 0x80 },
+ { 0x03fe, 0x10 }, //CISCTL_rst
+ { 0x03fe, 0x00 },
+ { 0x031c, 0x9f },
+ { 0x0360, 0x01 },
+ { 0x0360, 0x00 },
+ { 0x0316, 0x09 },
+ { 0x0a67, 0x80 },
+ { 0x0313, 0x00 },
+ { 0x0a53, 0x0e },
+ { 0x0a65, 0x17 },
+ { 0x0a68, 0xa1 },
+ { 0x0a58, 0x00 },
+ { 0x0ace, 0x0c },
+ { 0x00a4, 0x00 },
+ { 0x00a5, 0x01 },
+ { 0x00a7, 0x09 },
+ { 0x00a8, 0x9c },
+ { 0x00a9, 0x0c },
+ { 0x00aa, 0xd0 },
+ { 0x0a8a, 0x00 },
+ { 0x0a8b, 0xe0 },
+ { 0x0a8c, 0x13 },
+ { 0x0a8d, 0xe8 },
+ { 0x0a90, 0x0a },
+ { 0x0a91, 0x10 },
+ { 0x0a92, 0xf8 },
+ { 0x0a71, 0xf2 },
+ { 0x0a72, 0x12 },
+ { 0x0a73, 0x64 },
+ { 0x0a75, 0x41 },
+ { 0x0a70, 0x07 },
+ { 0x0313, 0x80 },
+
+ /*ISP*/
+ { 0x00a0, 0x01 },
+ { 0x0080, 0xd2 },
+ { 0x0081, 0x3f },
+ { 0x0087, 0x51 },
+ { 0x0089, 0x03 },
+ { 0x009b, 0x40 },
+ { 0x05a0, 0x82 },
+ { 0x05ac, 0x00 },
+ { 0x05ad, 0x01 },
+ { 0x05ae, 0x00 },
+ { 0x0800, 0x0a },
+ { 0x0801, 0x14 },
+ { 0x0802, 0x28 },
+ { 0x0803, 0x34 },
+ { 0x0804, 0x0e },
+ { 0x0805, 0x33 },
+ { 0x0806, 0x03 },
+ { 0x0807, 0x8a },
+ { 0x0808, 0x50 },
+ { 0x0809, 0x00 },
+ { 0x080a, 0x34 },
+ { 0x080b, 0x03 },
+ { 0x080c, 0x26 },
+ { 0x080d, 0x03 },
+ { 0x080e, 0x18 },
+ { 0x080f, 0x03 },
+ { 0x0810, 0x10 },
+ { 0x0811, 0x03 },
+ { 0x0812, 0x00 },
+ { 0x0813, 0x00 },
+ { 0x0814, 0x01 },
+ { 0x0815, 0x00 },
+ { 0x0816, 0x01 },
+ { 0x0817, 0x00 },
+ { 0x0818, 0x00 },
+ { 0x0819, 0x0a },
+ { 0x081a, 0x01 },
+ { 0x081b, 0x6c },
+ { 0x081c, 0x00 },
+ { 0x081d, 0x0b },
+ { 0x081e, 0x02 },
+ { 0x081f, 0x00 },
+ { 0x0820, 0x00 },
+ { 0x0821, 0x0c },
+ { 0x0822, 0x02 },
+ { 0x0823, 0xd9 },
+ { 0x0824, 0x00 },
+ { 0x0825, 0x0d },
+ { 0x0826, 0x03 },
+ { 0x0827, 0xf0 },
+ { 0x0828, 0x00 },
+ { 0x0829, 0x0e },
+ { 0x082a, 0x05 },
+ { 0x082b, 0x94 },
+ { 0x082c, 0x09 },
+ { 0x082d, 0x6e },
+ { 0x082e, 0x07 },
+ { 0x082f, 0xe6 },
+ { 0x0830, 0x10 },
+ { 0x0831, 0x0e },
+ { 0x0832, 0x0b },
+ { 0x0833, 0x2c },
+ { 0x0834, 0x14 },
+ { 0x0835, 0xae },
+ { 0x0836, 0x0f },
+ { 0x0837, 0xc4 },
+ { 0x0838, 0x18 },
+ { 0x0839, 0x0e },
+ { 0x05ac, 0x01 },
+ { 0x059a, 0x00 },
+ { 0x059b, 0x00 },
+ { 0x059c, 0x01 },
+ { 0x0598, 0x00 },
+ { 0x0597, 0x14 },
+ { 0x05ab, 0x09 },
+ { 0x05a4, 0x02 },
+ { 0x05a3, 0x05 },
+ { 0x05a0, 0xc2 },
+ { 0x0207, 0xc4 },
+
+ /*GAIN*/
+ { 0x0208, 0x01 },
+ { 0x0209, 0x72 },
+ { 0x0204, 0x04 },
+ { 0x0205, 0x00 },
+
+ { 0x0040, 0x22 },
+ { 0x0041, 0x20 },
+ { 0x0043, 0x10 },
+ { 0x0044, 0x00 },
+ { 0x0046, 0x08 },
+ { 0x0047, 0xf0 },
+ { 0x0048, 0x0f },
+ { 0x004b, 0x0f },
+ { 0x004c, 0x00 },
+ { 0x0050, 0x5c },
+ { 0x0051, 0x44 },
+ { 0x005b, 0x03 },
+ { 0x00c0, 0x00 },
+ { 0x00c1, 0x80 },
+ { 0x00c2, 0x31 },
+ { 0x00c3, 0x00 },
+ { 0x0460, 0x04 },
+ { 0x0462, 0x08 },
+ { 0x0464, 0x0e },
+ { 0x0466, 0x0a },
+ { 0x0468, 0x12 },
+ { 0x046a, 0x12 },
+ { 0x046c, 0x10 },
+ { 0x046e, 0x0c },
+ { 0x0461, 0x03 },
+ { 0x0463, 0x03 },
+ { 0x0465, 0x03 },
+ { 0x0467, 0x03 },
+ { 0x0469, 0x04 },
+ { 0x046b, 0x04 },
+ { 0x046d, 0x04 },
+ { 0x046f, 0x04 },
+ { 0x0470, 0x04 },
+ { 0x0472, 0x10 },
+ { 0x0474, 0x26 },
+ { 0x0476, 0x38 },
+ { 0x0478, 0x20 },
+ { 0x047a, 0x30 },
+ { 0x047c, 0x38 },
+ { 0x047e, 0x60 },
+ { 0x0471, 0x05 },
+ { 0x0473, 0x05 },
+ { 0x0475, 0x05 },
+ { 0x0477, 0x05 },
+ { 0x0479, 0x04 },
+ { 0x047b, 0x04 },
+ { 0x047d, 0x04 },
+ { 0x047f, 0x04 },
+
+ { GC08A3_TABLE_END, 0x00 }
+};
+
+static const struct gc08a3_link_freq_config link_freq_configs[] = {
+ [GC08A3_LINK_FREQ_336MHZ_CFG] = {
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_table_common),
+ .regs = mode_table_common,
+ }
+ },
+ [GC08A3_LINK_FREQ_207MHZ_CFG] = {
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_table_common),
+ .regs = mode_table_common,
+ }
+ },
+
+};
+
+enum {
+ GC08A3_PREV,
+ GC08A3_HS,
+};
+
+/*
+ * Declare modes in order, from biggest
+ * to smallest height.
+ */
+static const struct gc08a3_mode {
+ u8 mode_id;
+ u32 width;
+ u32 height;
+ const struct gc08a3_reg_list reg_list;
+
+ u32 hts; /* Horizontal timining size */
+ u32 vts_def; /* Default vertical timining size */
+ u32 vts_min; /* Min vertical timining size */
+ u32 link_freq_index; /* Link frequency needed for this resolution */
+ u32 max_framerate;
+
+} gc08a3_modes[] = {
+ {
+ .mode_id = GC08A3_PREV,
+ .width = 3264,
+ .height = 2448,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_3264x2448),
+ .regs = mode_3264x2448,
+ },
+ .link_freq_index = GC08A3_LINK_FREQ_336MHZ_CFG,
+
+ .hts = GC08A3_HTS_30FPS,
+ .vts_def = GC08A3_VTS_30FPS,
+ .vts_min = GC08A3_VTS_30FPS_MIN,
+ .max_framerate = 300,
+ },
+ {
+ .mode_id = GC08A3_HS,
+ .width = 1920,
+ .height = 1080,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_1920x1080),
+ .regs = mode_1920x1080,
+ },
+ .link_freq_index = GC08A3_LINK_FREQ_207MHZ_CFG,
+
+ .hts = GC08A3_HTS_60FPS,
+ .vts_def = GC08A3_VTS_60FPS,
+ .vts_min = GC08A3_VTS_60FPS_MIN,
+ .max_framerate = 600,
+ },
+};
+
+static u64 to_pixel_rate(u32 f_index)
+{
+ u64 pixel_rate = link_freq_menu_items[f_index] * 2 * GC08A3_DATA_LANES;
+
+ do_div(pixel_rate, GC08A3_RGB_DEPTH);
+
+ return pixel_rate;
+}
+
+static u64 __maybe_unused to_pixels_per_line(u32 hts, u32 f_index)
+{
+ u64 ppl = hts * to_pixel_rate(f_index);
+
+ do_div(ppl, GC08A3_SCLK);
+
+ return ppl;
+}
+
+static int gc08a3_read_reg(struct gc08a3 *gc08a3, u16 reg, u16 len, u32 *val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&gc08a3->sd);
+ struct i2c_msg msgs[2];
+ u8 addr_buf[2];
+ u8 data_buf[4] = { 0 };
+ int ret;
+
+ if (len > 4)
+ return -EINVAL;
+
+ put_unaligned_be16(reg, addr_buf);
+ msgs[0].addr = client->addr;
+ msgs[0].flags = 0;
+ msgs[0].len = sizeof(addr_buf);
+ msgs[0].buf = addr_buf;
+ msgs[1].addr = client->addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = len;
+ msgs[1].buf = &data_buf[4 - len];
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret != ARRAY_SIZE(msgs))
+ return -EIO;
+
+ *val = get_unaligned_be32(data_buf);
+
+ return 0;
+}
+
+static int gc08a3_write_reg(struct gc08a3 *gc08a3, u16 reg, u16 len, u32 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&gc08a3->sd);
+ u8 buf[6];
+
+ if (len > 4)
+ return -EINVAL;
+
+ put_unaligned_be16(reg, buf);
+ put_unaligned_be32(val << 8 * (4 - len), buf + 2);
+ if (i2c_master_send(client, buf, len + 2) != len + 2)
+ return -EIO;
+
+ return 0;
+}
+
+static int gc08a3_write_reg_list(struct gc08a3 *gc08a3,
+ const struct gc08a3_reg_list *r_list)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&gc08a3->sd);
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < r_list->num_of_regs; i++) {
+ if (r_list->regs[i].address == GC08A3_TABLE_WAIT_MS) {
+ usleep_range(r_list->regs[i].val * 1000,
+ r_list->regs[i].val * 1000 + 500);
+ continue;
+ }
+
+ if (r_list->regs[i].address == GC08A3_TABLE_END)
+ break;
+
+ ret = gc08a3_write_reg(gc08a3, r_list->regs[i].address,
+ GC08A3_REG_VALUE_08BIT,
+ r_list->regs[i].val);
+ if (ret) {
+ dev_err_ratelimited(&client->dev,
+ "failed to write reg 0x%4.4x. error = %d",
+ r_list->regs[i].address, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int gc08a3_identify_module(struct gc08a3 *gc08a3)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&gc08a3->sd);
+ u32 val = 0;
+
+ gc08a3_read_reg(gc08a3, GC08A3_REG_CHIP_ID, GC08A3_REG_VALUE_16BIT,
+ &val);
+
+ if (val != GC08A3_CHIP_ID) {
+ dev_err(&client->dev, "chip id mismatch: 0x%x!=0x%x",
+ GC08A3_CHIP_ID, val);
+ return -ENXIO;
+ }
+
+ dev_info(&client->dev, "sensor_id: 0x%04x\n", val);
+ return 0;
+}
+
+static inline struct gc08a3 *to_gc08a3(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct gc08a3, sd);
+}
+
+static int __maybe_unused gc08a3_power_on(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct gc08a3 *gc08a3 = to_gc08a3(sd);
+ int ret;
+
+ dev_info(dev, "--- %s +", __func__);
+
+ gpiod_set_value_cansleep(gc08a3->enable_gpio, 0);
+ usleep_range(2000, 3000);
+
+ ret = regulator_bulk_enable(GC08A3_NUM_SUPPLIES, gc08a3->supplies);
+ if (ret < 0) {
+ dev_err(gc08a3->dev, "failed to enable regulators: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(gc08a3->xclk);
+ if (ret < 0) {
+ regulator_bulk_disable(GC08A3_NUM_SUPPLIES, gc08a3->supplies);
+ dev_err(gc08a3->dev, "clk prepare enable failed\n");
+ return ret;
+ }
+
+ usleep_range(2000, 3000);
+
+ gpiod_set_value_cansleep(gc08a3->enable_gpio, 1);
+ usleep_range(12000, 15000);
+
+ dev_info(dev, "--- %s -", __func__);
+
+ return 0;
+}
+
+static int __maybe_unused gc08a3_power_off(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct gc08a3 *gc08a3 = to_gc08a3(sd);
+
+ dev_info(dev, "--- %s +", __func__);
+
+ gpiod_set_value_cansleep(gc08a3->enable_gpio, 0);
+
+ clk_disable_unprepare(gc08a3->xclk);
+
+ regulator_bulk_disable(GC08A3_NUM_SUPPLIES, gc08a3->supplies);
+ usleep_range(10, 20);
+
+ dev_info(dev, "--- %s -", __func__);
+
+ return 0;
+}
+
+static int gc08a3_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index > 0)
+ return -EINVAL;
+
+ code->code = GC08A3_MBUS_CODE;
+
+ return 0;
+}
+
+static int gc08a3_enum_frame_size(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ if (fse->code != GC08A3_MBUS_CODE)
+ return -EINVAL;
+
+ if (fse->index >= ARRAY_SIZE(gc08a3_modes))
+ return -EINVAL;
+
+ fse->min_width = gc08a3_modes[fse->index].width;
+ fse->max_width = gc08a3_modes[fse->index].width;
+ fse->min_height = gc08a3_modes[fse->index].height;
+ fse->max_height = gc08a3_modes[fse->index].height;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int gc08a3_g_register(struct v4l2_subdev *subdev,
+ struct v4l2_dbg_register *reg)
+{
+ int ret;
+ u32 val;
+ struct gc08a3 *gc08a3 = container_of(subdev, struct gc08a3, sd);
+
+ ret = gc08a3_read_reg(gc08a3, reg->reg, GC08A3_REG_VALUE_08BIT, &val);
+ if (ret < 0)
+ return ret;
+
+ reg->val = val;
+ reg->size = 1;
+
+ return 0;
+}
+
+static int gc08a3_s_register(struct v4l2_subdev *subdev,
+ const struct v4l2_dbg_register *reg)
+{
+ struct gc08a3 *gc08a3 = container_of(subdev, struct gc08a3, sd);
+
+ return gc08a3_write_reg(gc08a3, reg->reg, GC08A3_REG_VALUE_08BIT,
+ reg->val & 0xff);
+}
+
+#endif
+
+static const struct v4l2_subdev_core_ops gc08a3_core_ops = {
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = gc08a3_g_register,
+ .s_register = gc08a3_s_register,
+#endif
+};
+
+static struct v4l2_mbus_framefmt *
+__gc08a3_get_pad_format(struct gc08a3 *gc08a3,
+ struct v4l2_subdev_state *sd_state, unsigned int pad,
+ enum v4l2_subdev_format_whence which)
+{
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_format(&gc08a3->sd, sd_state, pad);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &gc08a3->fmt;
+ default:
+ return NULL;
+ }
+}
+
+static int gc08a3_get_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *format)
+{
+ struct gc08a3 *gc08a3 = to_gc08a3(sd);
+
+ mutex_lock(&gc08a3->mutex);
+ format->format = *__gc08a3_get_pad_format(gc08a3, sd_state, format->pad,
+ format->which);
+ mutex_unlock(&gc08a3->mutex);
+
+ return 0;
+}
+
+static struct v4l2_rect *
+__gc08a3_get_pad_crop(struct gc08a3 *gc08a3, struct v4l2_subdev_state *sd_state,
+ unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_crop(&gc08a3->sd, sd_state, pad);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &gc08a3->crop;
+ default:
+ return NULL;
+ }
+}
+
+static int gc08a3_update_cur_mode_controls(struct gc08a3 *gc08a3)
+{
+ s64 exposure_max, h_blank;
+ int ret = 0;
+
+ ret = __v4l2_ctrl_modify_range(gc08a3->vblank,
+ gc08a3->cur_mode->vts_min - gc08a3->cur_mode->height,
+ GC08A3_VTS_MAX - gc08a3->cur_mode->height, 1,
+ gc08a3->cur_mode->vts_def - gc08a3->cur_mode->height);
+ if (ret)
+ dev_err(gc08a3->dev, "VB ctrl range update failed\n");
+
+ h_blank = gc08a3->cur_mode->hts - gc08a3->cur_mode->width;
+ ret = __v4l2_ctrl_modify_range(gc08a3->hblank, h_blank, h_blank, 1,
+ h_blank);
+ if (ret)
+ dev_err(gc08a3->dev, "HB ctrl range update failed\n");
+
+ exposure_max = gc08a3->cur_mode->vts_def - GC08A3_EXP_MARGIN;
+ ret = __v4l2_ctrl_modify_range(gc08a3->exposure, GC08A3_EXP_MIN,
+ exposure_max, GC08A3_EXP_STEP,
+ exposure_max);
+ if (ret)
+ dev_err(gc08a3->dev, "exposure ctrl range update failed\n");
+
+ return ret;
+}
+
+static int gc08a3_set_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *format)
+{
+ struct gc08a3 *gc08a3 = to_gc08a3(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(&gc08a3->sd);
+ struct device *dev = &client->dev;
+ struct v4l2_mbus_framefmt *__format;
+ struct v4l2_rect *__crop;
+ const struct gc08a3_mode *mode;
+
+ mutex_lock(&gc08a3->mutex);
+
+ dev_dbg(dev, "---- %s, format(W x H:%d x %d) +", __func__,
+ format->format.width, format->format.height);
+
+ __crop = __gc08a3_get_pad_crop(gc08a3, sd_state, format->pad,
+ format->which);
+
+ mode = v4l2_find_nearest_size(gc08a3_modes, ARRAY_SIZE(gc08a3_modes),
+ width, height, format->format.width,
+ format->format.height);
+
+ dev_dbg(dev, "----nearest mode(W x H:%d x %d)", mode->width,
+ mode->height);
+
+ __crop->width = mode->width;
+ __crop->height = mode->height;
+
+ __format = __gc08a3_get_pad_format(gc08a3, sd_state, format->pad,
+ format->which);
+ __format->width = __crop->width;
+ __format->height = __crop->height;
+ __format->code = GC08A3_MBUS_CODE;
+ __format->field = V4L2_FIELD_NONE;
+ __format->colorspace = V4L2_COLORSPACE_SRGB;
+ __format->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(__format->colorspace);
+ __format->quantization =
+ V4L2_MAP_QUANTIZATION_DEFAULT(true,
+ __format->colorspace,
+ __format->ycbcr_enc);
+ __format->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(__format->colorspace);
+
+ format->format = *__format;
+
+ gc08a3->cur_mode = mode;
+ gc08a3_update_cur_mode_controls(gc08a3);
+
+ mutex_unlock(&gc08a3->mutex);
+
+ return 0;
+}
+
+static int gc08a3_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_selection *sel)
+{
+ struct gc08a3 *gc08a3 = to_gc08a3(sd);
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP:
+ mutex_lock(&gc08a3->mutex);
+ sel->r = *__gc08a3_get_pad_crop(gc08a3, sd_state, sel->pad, sel->which);
+ mutex_unlock(&gc08a3->mutex);
+ break;
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ sel->r.top = 0;
+ sel->r.left = 0;
+ sel->r.width = GC08A3_NATIVE_WIDTH;
+ sel->r.height = GC08A3_NATIVE_HEIGHT;
+ break;
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ if (gc08a3->cur_mode->width == GC08A3_NATIVE_WIDTH) {
+ sel->r.top = 0;
+ sel->r.left = 0;
+ sel->r.width = GC08A3_NATIVE_WIDTH;
+ sel->r.height = GC08A3_NATIVE_HEIGHT;
+ } else {
+ sel->r.top = 0;
+ sel->r.left = 0;
+ sel->r.width = 1920;
+ sel->r.height = 1080;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int gc08a3_entity_init_cfg(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_state *sd_state)
+{
+ struct v4l2_subdev_format fmt = {};
+
+ fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY :
+ V4L2_SUBDEV_FORMAT_ACTIVE;
+ fmt.format.width = gc08a3_modes[0].width;
+ fmt.format.height = gc08a3_modes[0].height;
+
+ gc08a3_set_format(subdev, sd_state, &fmt);
+
+ return 0;
+}
+
+static int gc08a3_set_ctrl_hflip(struct gc08a3 *gc08a3, u32 ctrl_val)
+{
+ int ret;
+ u32 val;
+
+ ret = gc08a3_read_reg(gc08a3, GC08A3_FLIP_REG, GC08A3_REG_VALUE_08BIT,
+ &val);
+ if (ret) {
+ dev_err(gc08a3->dev, "read hflip register failed: %d\n", ret);
+ return ret;
+ }
+
+ val = (ctrl_val) ? (val | GC08A3_FLIP_H_MASK) :
+ (val & ~GC08A3_FLIP_H_MASK);
+ ret = gc08a3_write_reg(gc08a3, GC08A3_FLIP_REG, GC08A3_REG_VALUE_08BIT,
+ val);
+ if (ret < 0)
+ dev_err(gc08a3->dev, "Error %d\n", ret);
+
+ return ret;
+}
+
+static int gc08a3_set_ctrl_vflip(struct gc08a3 *gc08a3, u32 ctrl_val)
+{
+ int ret;
+ u32 val;
+
+ ret = gc08a3_read_reg(gc08a3, GC08A3_FLIP_REG, GC08A3_REG_VALUE_08BIT,
+ &val);
+ if (ret) {
+ dev_err(gc08a3->dev, "read vflip register failed: %d\n", ret);
+ return ret;
+ }
+
+ val = (ctrl_val) ? (val | GC08A3_FLIP_V_MASK) :
+ (val & ~GC08A3_FLIP_V_MASK);
+ ret = gc08a3_write_reg(gc08a3, GC08A3_FLIP_REG, GC08A3_REG_VALUE_08BIT,
+ val);
+ if (ret < 0)
+ dev_err(gc08a3->dev, "Error %d\n", ret);
+
+ return ret;
+}
+
+static int gc08a3_test_pattern(struct gc08a3 *gc08a3, u32 pattern_menu)
+{
+ int ret = 0;
+ u32 pattern = 0;
+
+ if (pattern_menu) {
+ // write bit16:0x0110 -> color bar
+ switch (pattern_menu) {
+ case 1:
+ pattern = 0x00;
+ break;
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ pattern = pattern_menu + 2;
+ break;
+ case 7:
+ pattern = 0x10;
+ break;
+
+ default:
+ dev_dbg(gc08a3->dev, "invalid pattern menu!\n");
+ break;
+ }
+
+ ret = gc08a3_write_reg(gc08a3, GC08A3_REG_TEST_PATTERN_EN,
+ GC08A3_REG_VALUE_08BIT,
+ GC08A3_TEST_PATTERN_EN);
+ ret = gc08a3_write_reg(gc08a3, GC08A3_REG_TEST_PATTERN_IDX,
+ GC08A3_REG_VALUE_08BIT, pattern);
+
+ } else {
+ ret = gc08a3_write_reg(gc08a3, GC08A3_REG_TEST_PATTERN_EN,
+ GC08A3_REG_VALUE_08BIT, 0x00);
+ }
+
+ return ret;
+}
+
+static int gc08a3_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct gc08a3 *gc08a3 =
+ container_of(ctrl->handler, struct gc08a3, ctrls);
+ int ret = 0;
+ s64 exposure_max;
+
+ if (ctrl->id == V4L2_CID_VBLANK) {
+ /* Update max exposure while meeting expected vblanking */
+ exposure_max = gc08a3->cur_mode->height + ctrl->val -
+ GC08A3_EXP_MARGIN;
+ __v4l2_ctrl_modify_range(gc08a3->exposure,
+ gc08a3->exposure->minimum,
+ exposure_max, gc08a3->exposure->step,
+ exposure_max);
+ }
+
+ /*
+ * Applying V4L2 control value only happens
+ * when power is up for streaming
+ */
+ if (!pm_runtime_get_if_in_use(gc08a3->dev))
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ ret = gc08a3_write_reg(gc08a3, GC08A3_EXP_REG,
+ GC08A3_REG_VALUE_16BIT, ctrl->val);
+ break;
+
+ case V4L2_CID_ANALOGUE_GAIN:
+ ret = gc08a3_write_reg(gc08a3, GC08A3_AGAIN_REG,
+ GC08A3_REG_VALUE_16BIT, ctrl->val);
+ break;
+
+ case V4L2_CID_VBLANK:
+ dev_info(gc08a3->dev, "V4L2_CID_VBLANK:height:%d, V_BLNK:%d\n",
+ gc08a3->cur_mode->height, ctrl->val);
+ ret = gc08a3_write_reg(gc08a3, GC08A3_FL_REG,
+ GC08A3_REG_VALUE_16BIT,
+ gc08a3->cur_mode->height + ctrl->val);
+ break;
+
+ case V4L2_CID_HFLIP:
+ gc08a3_set_ctrl_hflip(gc08a3, ctrl->val);
+ break;
+
+ case V4L2_CID_VFLIP:
+ gc08a3_set_ctrl_vflip(gc08a3, ctrl->val);
+ break;
+
+ case V4L2_CID_TEST_PATTERN:
+ ret = gc08a3_test_pattern(gc08a3, ctrl->val);
+ break;
+
+ default:
+ break;
+ }
+
+ pm_runtime_put(gc08a3->dev);
+
+ return ret;
+}
+
+static int get_volatile_ext_ctrl(struct v4l2_ctrl *ctrl)
+{
+ int ret = 0;
+ struct gc08a3 *gc08a3 =
+ container_of(ctrl->handler, struct gc08a3, ctrls);
+ struct i2c_client *client = v4l2_get_subdevdata(&gc08a3->sd);
+
+ dev_dbg(&client->dev, "---- %s, CMD:0x%x\n", __func__, ctrl->id);
+ switch (ctrl->id) {
+ default:
+ dev_info(&client->dev, "[gc08a3] %s, un-support CMD: 0x%x\n",
+ __func__, ctrl->id);
+ break;
+ }
+
+ return ret;
+}
+
+static int gc08a3_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ int ret = 0;
+
+ switch (ctrl->id) {
+ default:
+ ret = get_volatile_ext_ctrl(ctrl);
+ break;
+ }
+
+ return ret;
+}
+
+static int try_ext_ctrl(struct v4l2_ctrl *ctrl)
+{
+ int ret = 0;
+ struct gc08a3 *gc08a3 =
+ container_of(ctrl->handler, struct gc08a3, ctrls);
+ struct i2c_client *client = v4l2_get_subdevdata(&gc08a3->sd);
+
+ switch (ctrl->id) {
+ default:
+ dev_dbg(&client->dev,
+ "[gc08a3] un-handle CMD: 0x%x (%s : %d)\n", ctrl->id,
+ __func__, __LINE__);
+ break;
+ }
+
+ return ret;
+}
+
+static int gc08a3_try_ctrl(struct v4l2_ctrl *ctrl)
+{
+ int ret = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ case V4L2_CID_ANALOGUE_GAIN:
+ case V4L2_CID_VBLANK:
+ case V4L2_CID_HFLIP:
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_TEST_PATTERN:
+ return 0;
+
+ default:
+ ret = try_ext_ctrl(ctrl);
+ break;
+ }
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops gc08a3_ctrl_ops = {
+ .g_volatile_ctrl = gc08a3_g_volatile_ctrl,
+ .try_ctrl = gc08a3_try_ctrl,
+ .s_ctrl = gc08a3_set_ctrl,
+};
+
+static int gc08a3_start_streaming(struct gc08a3 *gc08a3)
+{
+ const struct gc08a3_mode *mode;
+ const struct gc08a3_reg_list *reg_list;
+ int link_freq_index;
+ int ret;
+
+ dev_info(gc08a3->dev, "%s ++\n", __func__);
+
+ mutex_lock(&gc08a3->mutex);
+
+ link_freq_index = gc08a3->cur_mode->link_freq_index;
+ dev_info(gc08a3->dev, "----link_freq_index = %d ", link_freq_index);
+
+ reg_list = &link_freq_configs[link_freq_index].reg_list;
+ ret = gc08a3_write_reg_list(gc08a3, reg_list);
+ if (ret) {
+ dev_err(gc08a3->dev, "could not sent common table %d\n", ret);
+ goto error;
+ }
+
+ mode = gc08a3->cur_mode;
+ dev_info(gc08a3->dev, "----write regtbl: mode(id:%d, WxH:%dx%d)",
+ mode->mode_id, mode->width, mode->height);
+ reg_list = &mode->reg_list;
+
+ ret = gc08a3_write_reg_list(gc08a3, reg_list);
+ if (ret < 0) {
+ dev_err(gc08a3->dev, "could not sent mode table %d\n", ret);
+ goto error;
+ }
+ ret = __v4l2_ctrl_handler_setup(&gc08a3->ctrls);
+ if (ret < 0) {
+ dev_err(gc08a3->dev, "could not sync v4l2 controls\n");
+ goto error;
+ }
+
+ ret = gc08a3_write_reg(gc08a3, GC08A3_STRAEMING_REG,
+ GC08A3_REG_VALUE_08BIT, 1);
+ if (ret < 0) {
+ dev_err(gc08a3->dev, "write STRAEMING_REG failed: %d\n", ret);
+ goto error;
+ }
+
+ mutex_unlock(&gc08a3->mutex);
+
+ dev_info(gc08a3->dev, "%s --\n", __func__);
+
+ return 0;
+
+error:
+ mutex_unlock(&gc08a3->mutex);
+ return ret;
+}
+
+static int gc08a3_stop_streaming(struct gc08a3 *gc08a3)
+{
+ int ret;
+
+ dev_info(gc08a3->dev, "%s ++\n", __func__);
+
+ ret = gc08a3_write_reg(gc08a3, GC08A3_STRAEMING_REG,
+ GC08A3_REG_VALUE_08BIT, 0);
+ if (ret < 0)
+ dev_err(gc08a3->dev, "could not sent stop streaming %d\n", ret);
+
+ dev_info(gc08a3->dev, "%s --\n", __func__);
+
+ return ret;
+}
+
+static int gc08a3_s_stream(struct v4l2_subdev *subdev, int enable)
+{
+ struct gc08a3 *gc08a3 = to_gc08a3(subdev);
+ int ret;
+
+ if (gc08a3->streaming == enable)
+ return 0;
+
+ if (enable) {
+ ret = pm_runtime_resume_and_get(gc08a3->dev);
+ if (ret < 0)
+ return ret;
+
+ ret = gc08a3_start_streaming(gc08a3);
+ if (ret < 0)
+ goto err_rpm_put;
+ } else {
+ ret = gc08a3_stop_streaming(gc08a3);
+ if (ret < 0)
+ goto err_rpm_put;
+ pm_runtime_put(gc08a3->dev);
+ }
+
+ gc08a3->streaming = enable;
+ return 0;
+
+err_rpm_put:
+ pm_runtime_put(gc08a3->dev);
+ return ret;
+}
+
+static int gc08a3_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad,
+ struct v4l2_mbus_config *config)
+{
+ config->type = V4L2_MBUS_CSI2_DPHY;
+ config->bus.mipi_csi2.num_data_lanes = 4;
+ config->bus.mipi_csi2.flags = 0;
+ return 0;
+}
+
+static int gc08a3_g_frame_interval(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_frame_interval *fival)
+{
+ struct gc08a3 *gc08a3 = to_gc08a3(subdev);
+
+ fival->interval.numerator = 1;
+ fival->interval.denominator = gc08a3->cur_mode->max_framerate / 10;
+
+ return 0;
+}
+
+static int
+gc08a3_enum_frame_interval(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_frame_interval_enum *fie)
+{
+ const struct gc08a3_mode *mode;
+
+ if (fie->index != 0)
+ return -EINVAL;
+
+ mode = v4l2_find_nearest_size(gc08a3_modes, ARRAY_SIZE(gc08a3_modes),
+ width, height, fie->width, fie->height);
+
+ fie->code = GC08A3_MBUS_CODE;
+ fie->width = mode->width;
+ fie->height = mode->height;
+ fie->interval.numerator = 1;
+ fie->interval.denominator = mode->max_framerate / 10;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_video_ops gc08a3_video_ops = {
+ .s_stream = gc08a3_s_stream,
+ .g_frame_interval = gc08a3_g_frame_interval,
+ .s_frame_interval = gc08a3_g_frame_interval,
+};
+
+static const struct v4l2_subdev_pad_ops gc08a3_subdev_pad_ops = {
+ .enum_mbus_code = gc08a3_enum_mbus_code,
+ .enum_frame_size = gc08a3_enum_frame_size,
+ .enum_frame_interval = gc08a3_enum_frame_interval,
+ .get_fmt = gc08a3_get_format,
+ .set_fmt = gc08a3_set_format,
+ .get_selection = gc08a3_get_selection,
+ .init_cfg = gc08a3_entity_init_cfg,
+ .get_mbus_config = gc08a3_g_mbus_config,
+};
+
+static const struct v4l2_subdev_ops gc08a3_subdev_ops = {
+ .core = &gc08a3_core_ops,
+ .video = &gc08a3_video_ops,
+ .pad = &gc08a3_subdev_pad_ops,
+};
+
+static const struct regmap_config sensor_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int gc08a3_get_regulators(struct device *dev, struct gc08a3 *gc08a3)
+{
+ unsigned int i;
+
+ for (i = 0; i < GC08A3_NUM_SUPPLIES; i++)
+ gc08a3->supplies[i].supply = gc08a3_supply_name[i];
+
+ return devm_regulator_bulk_get(dev, GC08A3_NUM_SUPPLIES,
+ gc08a3->supplies);
+}
+
+static int gc08a3_parse_fwnode(struct device *dev)
+{
+ struct fwnode_handle *endpoint;
+ struct v4l2_fwnode_endpoint bus_cfg = {
+ .bus_type = V4L2_MBUS_CSI2_DPHY,
+ };
+ unsigned int i, j;
+ int ret;
+
+ endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
+ if (!endpoint) {
+ dev_err(dev, "endpoint node not found\n");
+ return -EINVAL;
+ }
+
+ ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg);
+ if (ret) {
+ dev_err(dev, "parsing endpoint node failed\n");
+ goto done;
+ }
+
+ if (!bus_cfg.nr_of_link_frequencies) {
+ dev_err(dev, "no link frequencies defined");
+ ret = -EINVAL;
+ goto done;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(link_freq_menu_items); i++) {
+ for (j = 0; j < bus_cfg.nr_of_link_frequencies; j++) {
+ if (link_freq_menu_items[i] ==
+ bus_cfg.link_frequencies[j])
+ break;
+ }
+
+ if (j == bus_cfg.nr_of_link_frequencies) {
+ dev_err(dev,
+ "no link frequency %lld supported, please check DT",
+ link_freq_menu_items[i]);
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+
+done:
+ v4l2_fwnode_endpoint_free(&bus_cfg);
+ fwnode_handle_put(endpoint);
+ return ret;
+}
+
+static int __maybe_unused gc08a3_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct gc08a3 *gc08a3 = to_gc08a3(sd);
+
+ if (gc08a3->streaming)
+ gc08a3_stop_streaming(gc08a3);
+
+ return 0;
+}
+
+static int __maybe_unused gc08a3_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct gc08a3 *gc08a3 = to_gc08a3(sd);
+ int ret;
+
+ if (gc08a3->streaming) {
+ ret = gc08a3_start_streaming(gc08a3);
+ if (ret)
+ goto error;
+ }
+
+ return 0;
+
+error:
+ gc08a3_stop_streaming(gc08a3);
+ gc08a3->streaming = 0;
+ return ret;
+}
+
+static int gc08a3_init_controls(struct gc08a3 *gc08a3)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&gc08a3->sd);
+ const struct v4l2_ctrl_ops *ops = &gc08a3_ctrl_ops;
+ struct v4l2_fwnode_device_properties props;
+ struct v4l2_ctrl_handler *ctrl_hdlr;
+ s64 exposure_max, h_blank;
+ int ret;
+
+ ctrl_hdlr = &gc08a3->ctrls;
+ ret = v4l2_ctrl_handler_init(ctrl_hdlr, 10);
+ if (ret)
+ return ret;
+
+ ctrl_hdlr->lock = &gc08a3->mutex;
+
+ gc08a3->link_freq =
+ v4l2_ctrl_new_int_menu(ctrl_hdlr,
+ &gc08a3_ctrl_ops,
+ V4L2_CID_LINK_FREQ,
+ ARRAY_SIZE(link_freq_menu_items) - 1,
+ 0, link_freq_menu_items);
+ if (gc08a3->link_freq)
+ gc08a3->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ gc08a3->pixel_rate =
+ v4l2_ctrl_new_std(ctrl_hdlr,
+ &gc08a3_ctrl_ops,
+ V4L2_CID_PIXEL_RATE, 0,
+ to_pixel_rate(GC08A3_LINK_FREQ_336MHZ_CFG), 1,
+ to_pixel_rate(GC08A3_LINK_FREQ_336MHZ_CFG));
+
+ gc08a3->vblank =
+ v4l2_ctrl_new_std(ctrl_hdlr,
+ &gc08a3_ctrl_ops, V4L2_CID_VBLANK,
+ gc08a3->cur_mode->vts_min - gc08a3->cur_mode->height,
+ GC08A3_VTS_MAX - gc08a3->cur_mode->height, 1,
+ gc08a3->cur_mode->vts_def - gc08a3->cur_mode->height);
+
+ h_blank = gc08a3->cur_mode->hts - gc08a3->cur_mode->width;
+ gc08a3->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &gc08a3_ctrl_ops,
+ V4L2_CID_HBLANK, h_blank, h_blank, 1,
+ h_blank);
+ if (gc08a3->hblank)
+ gc08a3->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ v4l2_ctrl_new_std(ctrl_hdlr, &gc08a3_ctrl_ops,
+ V4L2_CID_ANALOGUE_GAIN, GC08A3_AGAIN_MIN,
+ GC08A3_AGAIN_MAX, GC08A3_AGAIN_STEP,
+ GC08A3_AGAIN_MIN);
+
+ exposure_max = gc08a3->cur_mode->vts_def - GC08A3_EXP_MARGIN;
+ gc08a3->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &gc08a3_ctrl_ops,
+ V4L2_CID_EXPOSURE, GC08A3_EXP_MIN,
+ exposure_max, GC08A3_EXP_STEP,
+ exposure_max);
+
+ v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &gc08a3_ctrl_ops,
+ V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(gc08a3_test_pattern_menu) - 1,
+ 0, 0, gc08a3_test_pattern_menu);
+
+ v4l2_ctrl_new_std(ctrl_hdlr, &gc08a3_ctrl_ops, V4L2_CID_HFLIP, 0,
+ 1, 1, 0);
+
+ v4l2_ctrl_new_std(ctrl_hdlr, &gc08a3_ctrl_ops, V4L2_CID_VFLIP, 0,
+ 1, 1, 0);
+
+ /* register properties to fwnode (e.g. rotation, orientation) */
+ ret = v4l2_fwnode_device_parse(&client->dev, &props);
+ if (ret)
+ goto error_ctrls;
+
+ ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, ops, &props);
+ if (ret)
+ goto error_ctrls;
+
+ if (ctrl_hdlr->error) {
+ ret = ctrl_hdlr->error;
+ goto error_ctrls;
+ }
+
+ gc08a3->sd.ctrl_handler = ctrl_hdlr;
+
+ return 0;
+
+error_ctrls:
+ v4l2_ctrl_handler_free(ctrl_hdlr);
+
+ return ret;
+}
+
+static int gc08a3_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct gc08a3 *gc08a3;
+ int ret;
+
+ dev_info(dev, "--- %s +", __func__);
+
+ ret = gc08a3_parse_fwnode(dev);
+ if (ret)
+ return ret;
+
+ gc08a3 = devm_kzalloc(dev, sizeof(*gc08a3), GFP_KERNEL);
+ if (!gc08a3)
+ return -ENOMEM;
+
+ gc08a3->dev = dev;
+
+ gc08a3->xclk = devm_clk_get(dev, NULL);
+ if (IS_ERR(gc08a3->xclk)) {
+ dev_err(dev, "could not get xclk\n");
+ return PTR_ERR(gc08a3->xclk);
+ }
+
+ ret = clk_set_rate(gc08a3->xclk, GC08A3_DEFAULT_CLK_FREQ);
+ if (ret) {
+ dev_err(dev, "could not set xclk frequency\n");
+ return ret;
+ }
+
+ ret = gc08a3_get_regulators(dev, gc08a3);
+ if (ret < 0) {
+ dev_err(dev, "cannot get regulators\n");
+ return ret;
+ }
+
+ gc08a3->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
+ if (IS_ERR(gc08a3->enable_gpio)) {
+ dev_err(dev, "cannot get enable gpio\n");
+ return PTR_ERR(gc08a3->enable_gpio);
+ }
+
+ gc08a3->regmap = devm_regmap_init_i2c(client, &sensor_regmap_config);
+ if (IS_ERR(gc08a3->regmap)) {
+ dev_err(dev, "regmap init failed\n");
+ return PTR_ERR(gc08a3->regmap);
+ }
+
+ v4l2_i2c_subdev_init(&gc08a3->sd, client, &gc08a3_subdev_ops);
+
+ gc08a3_power_on(gc08a3->dev);
+
+ ret = gc08a3_identify_module(gc08a3);
+ if (ret) {
+ dev_err(&client->dev, "failed to find sensor: %d\n", ret);
+ gc08a3_power_off(gc08a3->dev);
+ return ret;
+ }
+
+ mutex_init(&gc08a3->mutex);
+ gc08a3->cur_mode = &gc08a3_modes[0];
+
+ ret = gc08a3_init_controls(gc08a3);
+ if (ret) {
+ dev_err(&client->dev, "failed to init controls: %d", ret);
+ goto free_ctrl;
+ }
+
+ gc08a3->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ gc08a3->pad.flags = MEDIA_PAD_FL_SOURCE;
+ gc08a3->sd.dev = &client->dev;
+ gc08a3->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+
+ dev_dbg(&client->dev, "gc08a3->sd.name: %s, dev->of_node->name: %s\n",
+ gc08a3->sd.name, dev->of_node->name);
+ if (V4L2_SUBDEV_NAME_SIZE - strlen(gc08a3->sd.name) - 2 <
+ strlen(dev->of_node->name)) {
+ dev_err(&client->dev,
+ "the string length of (sd.name + of_node->name) is too long.\n");
+ return -EINVAL;
+ }
+ strncat(gc08a3->sd.name, " ", 1);
+ strncat(gc08a3->sd.name, dev->of_node->name,
+ V4L2_SUBDEV_NAME_SIZE - strlen(gc08a3->sd.name) - 2);
+ dev_dbg(&client->dev, "after: gc08a3->sd.name: %s\n", gc08a3->sd.name);
+
+ ret = media_entity_pads_init(&gc08a3->sd.entity, 1, &gc08a3->pad);
+ if (ret < 0) {
+ dev_err(dev, "could not register media entity\n");
+ goto free_ctrl;
+ }
+
+ ret = v4l2_async_register_subdev_sensor(&gc08a3->sd);
+ if (ret < 0) {
+ dev_err(dev, "could not register v4l2 device\n");
+ goto free_entity;
+ }
+
+ pm_runtime_set_active(gc08a3->dev);
+ pm_runtime_enable(gc08a3->dev);
+ pm_runtime_idle(gc08a3->dev);
+
+ dev_info(dev, "--- %s -", __func__);
+
+ return 0;
+
+free_entity:
+ media_entity_cleanup(&gc08a3->sd.entity);
+free_ctrl:
+ mutex_destroy(&gc08a3->mutex);
+ v4l2_ctrl_handler_free(&gc08a3->ctrls);
+ pm_runtime_disable(gc08a3->dev);
+
+ return ret;
+}
+
+static void gc08a3_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct gc08a3 *gc08a3 = to_gc08a3(sd);
+
+ v4l2_async_unregister_subdev(&gc08a3->sd);
+ media_entity_cleanup(&gc08a3->sd.entity);
+ v4l2_ctrl_handler_free(&gc08a3->ctrls);
+
+ pm_runtime_disable(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+
+ mutex_destroy(&gc08a3->mutex);
+}
+
+static const struct of_device_id gc08a3_of_match[] = {
+ { .compatible = "GalaxyCore,gc08a3" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, gc08a3_of_match);
+
+static const struct dev_pm_ops gc08a3_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(gc08a3_suspend, gc08a3_resume)
+ SET_RUNTIME_PM_OPS(gc08a3_power_off, gc08a3_power_on, NULL)
+};
+
+static struct i2c_driver gc08a3_i2c_driver = {
+ .driver = {
+ .of_match_table = gc08a3_of_match,
+ .pm = &gc08a3_pm_ops,
+ .name = "gc08a3",
+ },
+ .probe_new = gc08a3_probe,
+ .remove = gc08a3_remove,
+};
+
+module_i2c_driver(gc08a3_i2c_driver);
+
+MODULE_DESCRIPTION("GalaxyCore gc08a3 Camera driver");
+MODULE_AUTHOR("Zhi Mao <[email protected]>");
+MODULE_LICENSE("GPL");
--
2.25.1

2023-11-23 12:16:56

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH 1/2] media: dt-bindings: media: i2c: Document GC08A3 bindings


On Thu, 23 Nov 2023 19:51:03 +0800, Zhi Mao wrote:
> Add YAML device tree binding for GC08A3 CMOS image sensor,
> and the relevant MAINTAINERS entries.
>
> Signed-off-by: Zhi Mao <[email protected]>
> ---
> .../bindings/media/i2c/galaxycore,gc08a3.yaml | 128 ++++++++++++++++++
> .../devicetree/bindings/vendor-prefixes.yaml | 2 +
> 2 files changed, 130 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/media/i2c/galaxycore,gc08a3.yaml
>

My bot found errors running 'make DT_CHECKER_FLAGS=-m dt_binding_check'
on your patch (DT_CHECKER_FLAGS is new in v5.13):

yamllint warnings/errors:

dtschema/dtc warnings/errors:
Error: Documentation/devicetree/bindings/media/i2c/galaxycore,gc08a3.example.dts:29.37-38 syntax error
FATAL ERROR: Unable to parse input tree
make[2]: *** [scripts/Makefile.lib:419: Documentation/devicetree/bindings/media/i2c/galaxycore,gc08a3.example.dtb] Error 1
make[2]: *** Waiting for unfinished jobs....
make[1]: *** [/builds/robherring/dt-review-ci/linux/Makefile:1424: dt_binding_check] Error 2
make: *** [Makefile:234: __sub-make] Error 2

doc reference errors (make refcheckdocs):

See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/[email protected]

The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.

If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:

pip3 install dtschema --upgrade

Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.

2023-11-23 17:32:05

by Conor Dooley

[permalink] [raw]
Subject: Re: [PATCH 1/2] media: dt-bindings: media: i2c: Document GC08A3 bindings

On Thu, Nov 23, 2023 at 07:51:03PM +0800, Zhi Mao wrote:
> Add YAML device tree binding for GC08A3 CMOS image sensor,
> and the relevant MAINTAINERS entries.
>
> Signed-off-by: Zhi Mao <[email protected]>

Please test your bindings.

> ---
> .../bindings/media/i2c/galaxycore,gc08a3.yaml | 128 ++++++++++++++++++
> .../devicetree/bindings/vendor-prefixes.yaml | 2 +
> 2 files changed, 130 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/media/i2c/galaxycore,gc08a3.yaml
>
> diff --git a/Documentation/devicetree/bindings/media/i2c/galaxycore,gc08a3.yaml b/Documentation/devicetree/bindings/media/i2c/galaxycore,gc08a3.yaml
> new file mode 100644
> index 000000000000..089ea321da91
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/i2c/galaxycore,gc08a3.yaml
> @@ -0,0 +1,128 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/media/i2c/galaxycore,gc08a3.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: GalaxyCore gc08a3 1/4" 8M Pixel MIPI CSI-2 sensor
> +
> +maintainers:
> + - Zhi Mao <[email protected]>
> +
> +description: |-

The |- is not needed, you have no formatting to preserve.

> + The gc08a3 is a raw image sensor with an MIPI CSI-2 image data
> + interface and CCI (I2C compatible) control bus. The output format
> + is raw Bayer.
> +
> +properties:
> + compatible:
> + const: GalaxyCore,gc08a3

Please remove the capitals.

> +
> + reg:
> + maxItems: 1
> +
> + clocks:
> + items:
> + - description: Reference to the mclk clock.

Pointless, just use maxItems: 1.

> +
> + assigned-clocks:
> + maxItems: 1
> +
> + assigned-clock-rates:
> + maxItems: 1
> +
> + enable-gpios:
> + description: Reference to the GPIO connected to the RESETB pin. Active low.
> + maxItems: 1
> +

> + vddio-supply:
> + description: Definition of the regulator used for the VDDIO power supply.
> +
> + vdda-supply:
> + description: Definition of the regulator used for the VDDA power supply.
> +
> + vddd-supply:
> + description: Definition of the regulator used for the VDDD power supply.

These descriptions can all be replaced with "foo-supply: true" IMO.

> + port:
> + $ref: /schemas/graph.yaml#/$defs/port-base
> + unevaluatedProperties: false
> +
> + properties:
> + endpoint:
> + $ref: /schemas/media/video-interfaces.yaml#
> + unevaluatedProperties: false
> +
> + properties:
> + data-lanes:
> + oneOf:
> + - items:
> + - const: 1
> + - const: 2
> + - const: 3
> + - const: 4
> + - items:
> + - const: 1
> + - const: 2
> +
> + link-frequencies: true
> +
> + required:
> + - data-lanes
> + - link-frequencies
> +
> +required:
> + - compatible
> + - reg
> + - clocks

> + - assigned-clocks
> + - assigned-clock-rates

Why are these required?

> + - vddio-supply
> + - vdda-supply
> + - vddd-supply
> + - port
> +
> +additionalProperties: false
> +
> +examples:
> + - |
> + #include <dt-bindings/gpio/gpio.h>
> +
> + i2c {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + sensor0@2 {
> + status = "okay";
> + compatible = "GalaxyCore,gc08a3";
> + reg = <0x31>;
> +
> + clocks = <&topckgen CLK_TOP_CAMTG>,
> + <&topckgen CLK_TOP_UNIVPLL_192M_D8>;
> + clock-names = "xvclk", "freq_mux";
> + clock-frequency = <24000000>;
> +
> + assigned-clocks = <&topckgen CLK_TOP_CAMTG>,
> + <&topckgen CLK_TOP_UNIVPLL_192M_D8>;
> + assigned-clock-parents = <&topckgen CLK_TOP_UNIVPLL_192M_D8>;
> + assigned-clock-rates = <0>, <24000000>;
> +
> + enable-gpios = <&pio 19 GPIO_ACTIVE_HIGH>;
> +
> + pinctrl-names = "default";
> + pinctrl-0 = <&camera_pins_cam0>;
> +
> + avdd-supply = <&mt6359_vfe28_ldo_reg>;
> +
> + port {
> + sensor0_out_2: endpoint {
> + data-lanes = <1 2 3 4>;
> + link-frequencies = /bits/ 64 <336000000 207000000>;
> + remote-endpoint = <&seninf_csi_port_0_in_2>;
> + };
> + };
> + };
> +
> + };
> +
> +...
> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
> index 309b94c328c8..a0bbec0bfee2 100644
> --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
> +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
> @@ -502,6 +502,8 @@ patternProperties:
> description: Fujitsu Ltd.
> "^fxtec,.*":
> description: FX Technology Ltd.
> + "^GalaxyCore,.*":
> + description: GalaxyCore Inc.
> "^gardena,.*":
> description: GARDENA GmbH
> "^gateway,.*":
> --
> 2.25.1
>


Attachments:
(No filename) (5.10 kB)
signature.asc (235.00 B)
Download all attachments

2023-11-24 07:58:30

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH 2/2] media: i2c: Add GC08A3 image sensor driver

Hi Zhi,

kernel test robot noticed the following build warnings:

[auto build test WARNING on robh/for-next]
[also build test WARNING on linuxtv-media-stage/master sailus-media-tree/streams linus/master v6.7-rc2 next-20231124]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url: https://github.com/intel-lab-lkp/linux/commits/Zhi-Mao/media-dt-bindings-media-i2c-Document-GC08A3-bindings/20231123-203838
base: https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next
patch link: https://lore.kernel.org/r/20231123115104.32094-3-zhi.mao%40mediatek.com
patch subject: [PATCH 2/2] media: i2c: Add GC08A3 image sensor driver
config: parisc-allmodconfig (https://download.01.org/0day-ci/archive/20231124/[email protected]/config)
compiler: hppa-linux-gcc (GCC) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231124/[email protected]/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/

All warnings (new ones prefixed by >>):

drivers/media/i2c/gc08a3.c: In function 'gc08a3_probe':
drivers/media/i2c/gc08a3.c:1965:13: error: 'V4L2_SUBDEV_NAME_SIZE' undeclared (first use in this function); did you mean 'V4L2_SUBDEV_FL_IS_I2C'?
1965 | if (V4L2_SUBDEV_NAME_SIZE - strlen(gc08a3->sd.name) - 2 <
| ^~~~~~~~~~~~~~~~~~~~~
| V4L2_SUBDEV_FL_IS_I2C
drivers/media/i2c/gc08a3.c:1965:13: note: each undeclared identifier is reported only once for each function it appears in
drivers/media/i2c/gc08a3.c: At top level:
drivers/media/i2c/gc08a3.c:2038:10: error: 'struct i2c_driver' has no member named 'probe_new'
2038 | .probe_new = gc08a3_probe,
| ^~~~~~~~~
drivers/media/i2c/gc08a3.c:2038:23: error: initialization of 'const struct i2c_device_id *' from incompatible pointer type 'int (*)(struct i2c_client *)' [-Werror=incompatible-pointer-types]
2038 | .probe_new = gc08a3_probe,
| ^~~~~~~~~~~~
drivers/media/i2c/gc08a3.c:2038:23: note: (near initialization for 'gc08a3_i2c_driver.id_table')
drivers/media/i2c/gc08a3.c: In function 'gc08a3_probe':
>> drivers/media/i2c/gc08a3.c:1971:9: warning: 'strncat' specified bound 1 equals source length [-Wstringop-overflow=]
1971 | strncat(gc08a3->sd.name, " ", 1);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cc1: some warnings being treated as errors


vim +/strncat +1971 drivers/media/i2c/gc08a3.c

1889
1890 static int gc08a3_probe(struct i2c_client *client)
1891 {
1892 struct device *dev = &client->dev;
1893 struct gc08a3 *gc08a3;
1894 int ret;
1895
1896 dev_info(dev, "--- %s +", __func__);
1897
1898 ret = gc08a3_parse_fwnode(dev);
1899 if (ret)
1900 return ret;
1901
1902 gc08a3 = devm_kzalloc(dev, sizeof(*gc08a3), GFP_KERNEL);
1903 if (!gc08a3)
1904 return -ENOMEM;
1905
1906 gc08a3->dev = dev;
1907
1908 gc08a3->xclk = devm_clk_get(dev, NULL);
1909 if (IS_ERR(gc08a3->xclk)) {
1910 dev_err(dev, "could not get xclk\n");
1911 return PTR_ERR(gc08a3->xclk);
1912 }
1913
1914 ret = clk_set_rate(gc08a3->xclk, GC08A3_DEFAULT_CLK_FREQ);
1915 if (ret) {
1916 dev_err(dev, "could not set xclk frequency\n");
1917 return ret;
1918 }
1919
1920 ret = gc08a3_get_regulators(dev, gc08a3);
1921 if (ret < 0) {
1922 dev_err(dev, "cannot get regulators\n");
1923 return ret;
1924 }
1925
1926 gc08a3->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
1927 if (IS_ERR(gc08a3->enable_gpio)) {
1928 dev_err(dev, "cannot get enable gpio\n");
1929 return PTR_ERR(gc08a3->enable_gpio);
1930 }
1931
1932 gc08a3->regmap = devm_regmap_init_i2c(client, &sensor_regmap_config);
1933 if (IS_ERR(gc08a3->regmap)) {
1934 dev_err(dev, "regmap init failed\n");
1935 return PTR_ERR(gc08a3->regmap);
1936 }
1937
1938 v4l2_i2c_subdev_init(&gc08a3->sd, client, &gc08a3_subdev_ops);
1939
1940 gc08a3_power_on(gc08a3->dev);
1941
1942 ret = gc08a3_identify_module(gc08a3);
1943 if (ret) {
1944 dev_err(&client->dev, "failed to find sensor: %d\n", ret);
1945 gc08a3_power_off(gc08a3->dev);
1946 return ret;
1947 }
1948
1949 mutex_init(&gc08a3->mutex);
1950 gc08a3->cur_mode = &gc08a3_modes[0];
1951
1952 ret = gc08a3_init_controls(gc08a3);
1953 if (ret) {
1954 dev_err(&client->dev, "failed to init controls: %d", ret);
1955 goto free_ctrl;
1956 }
1957
1958 gc08a3->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
1959 gc08a3->pad.flags = MEDIA_PAD_FL_SOURCE;
1960 gc08a3->sd.dev = &client->dev;
1961 gc08a3->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
1962
1963 dev_dbg(&client->dev, "gc08a3->sd.name: %s, dev->of_node->name: %s\n",
1964 gc08a3->sd.name, dev->of_node->name);
1965 if (V4L2_SUBDEV_NAME_SIZE - strlen(gc08a3->sd.name) - 2 <
1966 strlen(dev->of_node->name)) {
1967 dev_err(&client->dev,
1968 "the string length of (sd.name + of_node->name) is too long.\n");
1969 return -EINVAL;
1970 }
> 1971 strncat(gc08a3->sd.name, " ", 1);
1972 strncat(gc08a3->sd.name, dev->of_node->name,
1973 V4L2_SUBDEV_NAME_SIZE - strlen(gc08a3->sd.name) - 2);
1974 dev_dbg(&client->dev, "after: gc08a3->sd.name: %s\n", gc08a3->sd.name);
1975
1976 ret = media_entity_pads_init(&gc08a3->sd.entity, 1, &gc08a3->pad);
1977 if (ret < 0) {
1978 dev_err(dev, "could not register media entity\n");
1979 goto free_ctrl;
1980 }
1981
1982 ret = v4l2_async_register_subdev_sensor(&gc08a3->sd);
1983 if (ret < 0) {
1984 dev_err(dev, "could not register v4l2 device\n");
1985 goto free_entity;
1986 }
1987
1988 pm_runtime_set_active(gc08a3->dev);
1989 pm_runtime_enable(gc08a3->dev);
1990 pm_runtime_idle(gc08a3->dev);
1991
1992 dev_info(dev, "--- %s -", __func__);
1993
1994 return 0;
1995
1996 free_entity:
1997 media_entity_cleanup(&gc08a3->sd.entity);
1998 free_ctrl:
1999 mutex_destroy(&gc08a3->mutex);
2000 v4l2_ctrl_handler_free(&gc08a3->ctrls);
2001 pm_runtime_disable(gc08a3->dev);
2002
2003 return ret;
2004 }
2005

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

2023-11-24 08:10:29

by Krzysztof Kozlowski

[permalink] [raw]
Subject: Re: [PATCH 2/2] media: i2c: Add GC08A3 image sensor driver

On 23/11/2023 12:51, Zhi Mao wrote:
> Add a V4L2 sub-device driver for Galaxycore GC08A3 image sensor.
>
> Signed-off-by: Zhi Mao <[email protected]>
> ---
> drivers/media/i2c/Kconfig | 14 +
> drivers/media/i2c/Makefile | 1 +
> drivers/media/i2c/gc08a3.c | 2046 ++++++++++++++++++++++++++++++++++++
> 3 files changed, 2061 insertions(+)
> create mode 100644 drivers/media/i2c/gc08a3.c


> +static int gc08a3_start_streaming(struct gc08a3 *gc08a3)
> +{
> + const struct gc08a3_mode *mode;
> + const struct gc08a3_reg_list *reg_list;
> + int link_freq_index;
> + int ret;
> +
> + dev_info(gc08a3->dev, "%s ++\n", __func__);

Drop

> +
> + mutex_lock(&gc08a3->mutex);
> +
> + link_freq_index = gc08a3->cur_mode->link_freq_index;
> + dev_info(gc08a3->dev, "----link_freq_index = %d ", link_freq_index);
> +
> + reg_list = &link_freq_configs[link_freq_index].reg_list;
> + ret = gc08a3_write_reg_list(gc08a3, reg_list);
> + if (ret) {
> + dev_err(gc08a3->dev, "could not sent common table %d\n", ret);
> + goto error;
> + }
> +
> + mode = gc08a3->cur_mode;
> + dev_info(gc08a3->dev, "----write regtbl: mode(id:%d, WxH:%dx%d)",
> + mode->mode_id, mode->width, mode->height);
> + reg_list = &mode->reg_list;
> +
> + ret = gc08a3_write_reg_list(gc08a3, reg_list);
> + if (ret < 0) {
> + dev_err(gc08a3->dev, "could not sent mode table %d\n", ret);
> + goto error;
> + }
> + ret = __v4l2_ctrl_handler_setup(&gc08a3->ctrls);
> + if (ret < 0) {
> + dev_err(gc08a3->dev, "could not sync v4l2 controls\n");
> + goto error;
> + }
> +
> + ret = gc08a3_write_reg(gc08a3, GC08A3_STRAEMING_REG,
> + GC08A3_REG_VALUE_08BIT, 1);
> + if (ret < 0) {
> + dev_err(gc08a3->dev, "write STRAEMING_REG failed: %d\n", ret);
> + goto error;
> + }
> +
> + mutex_unlock(&gc08a3->mutex);
> +
> + dev_info(gc08a3->dev, "%s --\n", __func__);

Drop


> +
> + return 0;
> +
> +error:
> + mutex_unlock(&gc08a3->mutex);
> + return ret;
> +}
> +
> +static int gc08a3_stop_streaming(struct gc08a3 *gc08a3)
> +{
> + int ret;
> +
> + dev_info(gc08a3->dev, "%s ++\n", __func__);

Drop

> +
> + ret = gc08a3_write_reg(gc08a3, GC08A3_STRAEMING_REG,
> + GC08A3_REG_VALUE_08BIT, 0);
> + if (ret < 0)
> + dev_err(gc08a3->dev, "could not sent stop streaming %d\n", ret);
> +
> + dev_info(gc08a3->dev, "%s --\n", __func__);
> +

Drop

...

> +
> +static int gc08a3_probe(struct i2c_client *client)
> +{
> + struct device *dev = &client->dev;
> + struct gc08a3 *gc08a3;
> + int ret;
> +
> + dev_info(dev, "--- %s +", __func__);


No, drop such silly debug messages. Everywhere. Really everywhere.

> +
> + ret = gc08a3_parse_fwnode(dev);
> + if (ret)
> + return ret;
> +
> + gc08a3 = devm_kzalloc(dev, sizeof(*gc08a3), GFP_KERNEL);
> + if (!gc08a3)
> + return -ENOMEM;
> +
> + gc08a3->dev = dev;
> +
> + gc08a3->xclk = devm_clk_get(dev, NULL);
> + if (IS_ERR(gc08a3->xclk)) {
> + dev_err(dev, "could not get xclk\n");

Syntax is:
return dev_err_probe()

> + return PTR_ERR(gc08a3->xclk);
> + }
> +
> + ret = clk_set_rate(gc08a3->xclk, GC08A3_DEFAULT_CLK_FREQ);
> + if (ret) {
> + dev_err(dev, "could not set xclk frequency\n");
> + return ret;
> + }
> +
> + ret = gc08a3_get_regulators(dev, gc08a3);
> + if (ret < 0) {
> + dev_err(dev, "cannot get regulators\n");

return dev_err_probe()

> + return ret;
> + }
> +
> + gc08a3->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
> + if (IS_ERR(gc08a3->enable_gpio)) {
> + dev_err(dev, "cannot get enable gpio\n");

return dev_err_probe()

> + return PTR_ERR(gc08a3->enable_gpio);
> + }
> +
> + gc08a3->regmap = devm_regmap_init_i2c(client, &sensor_regmap_config);
> + if (IS_ERR(gc08a3->regmap)) {
> + dev_err(dev, "regmap init failed\n");

return dev_err_probe()

> + return PTR_ERR(gc08a3->regmap);
> + }
> +
> + v4l2_i2c_subdev_init(&gc08a3->sd, client, &gc08a3_subdev_ops);
> +
> + gc08a3_power_on(gc08a3->dev);
> +
> + ret = gc08a3_identify_module(gc08a3);
> + if (ret) {
> + dev_err(&client->dev, "failed to find sensor: %d\n", ret);
> + gc08a3_power_off(gc08a3->dev);
> + return ret;
> + }
> +
> + mutex_init(&gc08a3->mutex);
> + gc08a3->cur_mode = &gc08a3_modes[0];
> +
> + ret = gc08a3_init_controls(gc08a3);
> + if (ret) {
> + dev_err(&client->dev, "failed to init controls: %d", ret);

No power off?

> + goto free_ctrl;
> + }
> +
> + gc08a3->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> + gc08a3->pad.flags = MEDIA_PAD_FL_SOURCE;
> + gc08a3->sd.dev = &client->dev;
> + gc08a3->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
> +
> + dev_dbg(&client->dev, "gc08a3->sd.name: %s, dev->of_node->name: %s\n",
> + gc08a3->sd.name, dev->of_node->name);
> + if (V4L2_SUBDEV_NAME_SIZE - strlen(gc08a3->sd.name) - 2 <
> + strlen(dev->of_node->name)) {
> + dev_err(&client->dev,
> + "the string length of (sd.name + of_node->name) is too long.\n");

This looks like random error handling. You had goto in previous cases.

> + return -EINVAL;
> + }
> + strncat(gc08a3->sd.name, " ", 1);
> + strncat(gc08a3->sd.name, dev->of_node->name,
> + V4L2_SUBDEV_NAME_SIZE - strlen(gc08a3->sd.name) - 2);
> + dev_dbg(&client->dev, "after: gc08a3->sd.name: %s\n", gc08a3->sd.name);
> +
> + ret = media_entity_pads_init(&gc08a3->sd.entity, 1, &gc08a3->pad);
> + if (ret < 0) {
> + dev_err(dev, "could not register media entity\n");
> + goto free_ctrl;
> + }
> +
> + ret = v4l2_async_register_subdev_sensor(&gc08a3->sd);
> + if (ret < 0) {
> + dev_err(dev, "could not register v4l2 device\n");
> + goto free_entity;
> + }
> +
> + pm_runtime_set_active(gc08a3->dev);
> + pm_runtime_enable(gc08a3->dev);
> + pm_runtime_idle(gc08a3->dev);
> +
> + dev_info(dev, "--- %s -", __func__);

No, drop such silly debug messages.

> +
> + return 0;
> +
> +free_entity:
> + media_entity_cleanup(&gc08a3->sd.entity);
> +free_ctrl:
> + mutex_destroy(&gc08a3->mutex);
> + v4l2_ctrl_handler_free(&gc08a3->ctrls);
> + pm_runtime_disable(gc08a3->dev);
> +
> + return ret;
> +}
> +
> +static void gc08a3_remove(struct i2c_client *client)
> +{
> + struct v4l2_subdev *sd = i2c_get_clientdata(client);
> + struct gc08a3 *gc08a3 = to_gc08a3(sd);
> +
> + v4l2_async_unregister_subdev(&gc08a3->sd);
> + media_entity_cleanup(&gc08a3->sd.entity);
> + v4l2_ctrl_handler_free(&gc08a3->ctrls);
> +
> + pm_runtime_disable(&client->dev);
> + pm_runtime_set_suspended(&client->dev);
> +
> + mutex_destroy(&gc08a3->mutex);
> +}
> +
> +static const struct of_device_id gc08a3_of_match[] = {
> + { .compatible = "GalaxyCore,gc08a3" },
> + {}
> +};
> +MODULE_DEVICE_TABLE(of, gc08a3_of_match);
> +
> +static const struct dev_pm_ops gc08a3_pm_ops = {
> + SET_SYSTEM_SLEEP_PM_OPS(gc08a3_suspend, gc08a3_resume)
> + SET_RUNTIME_PM_OPS(gc08a3_power_off, gc08a3_power_on, NULL)

Fix indentation.

This code has very pooro quality. I suggest you to do first internal
review to avoid commenting on trivial errors and using community
resources for this.


Best regards,
Krzysztof

2023-12-07 10:25:17

by Laurent Pinchart

[permalink] [raw]
Subject: Re: [PATCH 1/2] media: dt-bindings: media: i2c: Document GC08A3 bindings

On Thu, Nov 23, 2023 at 05:31:35PM +0000, Conor Dooley wrote:
> On Thu, Nov 23, 2023 at 07:51:03PM +0800, Zhi Mao wrote:
> > Add YAML device tree binding for GC08A3 CMOS image sensor,
> > and the relevant MAINTAINERS entries.
> >
> > Signed-off-by: Zhi Mao <[email protected]>
>
> Please test your bindings.
>
> > ---
> > .../bindings/media/i2c/galaxycore,gc08a3.yaml | 128 ++++++++++++++++++
> > .../devicetree/bindings/vendor-prefixes.yaml | 2 +
> > 2 files changed, 130 insertions(+)
> > create mode 100644 Documentation/devicetree/bindings/media/i2c/galaxycore,gc08a3.yaml
> >
> > diff --git a/Documentation/devicetree/bindings/media/i2c/galaxycore,gc08a3.yaml b/Documentation/devicetree/bindings/media/i2c/galaxycore,gc08a3.yaml
> > new file mode 100644
> > index 000000000000..089ea321da91
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/media/i2c/galaxycore,gc08a3.yaml
> > @@ -0,0 +1,128 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/media/i2c/galaxycore,gc08a3.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: GalaxyCore gc08a3 1/4" 8M Pixel MIPI CSI-2 sensor
> > +
> > +maintainers:
> > + - Zhi Mao <[email protected]>
> > +
> > +description: |-
>
> The |- is not needed, you have no formatting to preserve.
>
> > + The gc08a3 is a raw image sensor with an MIPI CSI-2 image data
> > + interface and CCI (I2C compatible) control bus. The output format
> > + is raw Bayer.
> > +
> > +properties:
> > + compatible:
> > + const: GalaxyCore,gc08a3
>
> Please remove the capitals.
>
> > +
> > + reg:
> > + maxItems: 1
> > +
> > + clocks:
> > + items:
> > + - description: Reference to the mclk clock.
>
> Pointless, just use maxItems: 1.
>
> > +
> > + assigned-clocks:
> > + maxItems: 1
> > +
> > + assigned-clock-rates:
> > + maxItems: 1
> > +
> > + enable-gpios:
> > + description: Reference to the GPIO connected to the RESETB pin. Active low.
> > + maxItems: 1
> > +

If that's the RESETB pin, it should be reset-gpios.

> > + vddio-supply:
> > + description: Definition of the regulator used for the VDDIO power supply.
> > +
> > + vdda-supply:
> > + description: Definition of the regulator used for the VDDA power supply.
> > +
> > + vddd-supply:
> > + description: Definition of the regulator used for the VDDD power supply.
>
> These descriptions can all be replaced with "foo-supply: true" IMO.
>
> > + port:
> > + $ref: /schemas/graph.yaml#/$defs/port-base
> > + unevaluatedProperties: false
> > +
> > + properties:
> > + endpoint:
> > + $ref: /schemas/media/video-interfaces.yaml#
> > + unevaluatedProperties: false
> > +
> > + properties:
> > + data-lanes:
> > + oneOf:
> > + - items:
> > + - const: 1
> > + - const: 2
> > + - const: 3
> > + - const: 4
> > + - items:
> > + - const: 1
> > + - const: 2
> > +
> > + link-frequencies: true
> > +
> > + required:
> > + - data-lanes
> > + - link-frequencies
> > +
> > +required:
> > + - compatible
> > + - reg
> > + - clocks
>
> > + - assigned-clocks
> > + - assigned-clock-rates
>
> Why are these required?
>
> > + - vddio-supply
> > + - vdda-supply
> > + - vddd-supply
> > + - port
> > +
> > +additionalProperties: false
> > +
> > +examples:
> > + - |
> > + #include <dt-bindings/gpio/gpio.h>
> > +
> > + i2c {
> > + #address-cells = <1>;
> > + #size-cells = <0>;
> > +
> > + sensor0@2 {
> > + status = "okay";
> > + compatible = "GalaxyCore,gc08a3";
> > + reg = <0x31>;
> > +
> > + clocks = <&topckgen CLK_TOP_CAMTG>,
> > + <&topckgen CLK_TOP_UNIVPLL_192M_D8>;
> > + clock-names = "xvclk", "freq_mux";
> > + clock-frequency = <24000000>;
> > +
> > + assigned-clocks = <&topckgen CLK_TOP_CAMTG>,
> > + <&topckgen CLK_TOP_UNIVPLL_192M_D8>;
> > + assigned-clock-parents = <&topckgen CLK_TOP_UNIVPLL_192M_D8>;
> > + assigned-clock-rates = <0>, <24000000>;
> > +
> > + enable-gpios = <&pio 19 GPIO_ACTIVE_HIGH>;
> > +
> > + pinctrl-names = "default";
> > + pinctrl-0 = <&camera_pins_cam0>;
> > +
> > + avdd-supply = <&mt6359_vfe28_ldo_reg>;
> > +
> > + port {
> > + sensor0_out_2: endpoint {
> > + data-lanes = <1 2 3 4>;
> > + link-frequencies = /bits/ 64 <336000000 207000000>;
> > + remote-endpoint = <&seninf_csi_port_0_in_2>;
> > + };
> > + };
> > + };
> > +
> > + };
> > +
> > +...
> > diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
> > index 309b94c328c8..a0bbec0bfee2 100644
> > --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
> > +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
> > @@ -502,6 +502,8 @@ patternProperties:
> > description: Fujitsu Ltd.
> > "^fxtec,.*":
> > description: FX Technology Ltd.
> > + "^GalaxyCore,.*":
> > + description: GalaxyCore Inc.
> > "^gardena,.*":
> > description: GARDENA GmbH
> > "^gateway,.*":

--
Regards,

Laurent Pinchart

2023-12-07 11:07:13

by Laurent Pinchart

[permalink] [raw]
Subject: Re: [PATCH 2/2] media: i2c: Add GC08A3 image sensor driver

Hi Zhi Mao,

Thank you for the patch.

This is a partial review, as lots of things will change already for v2.
I'll review the next version in more details.

First of all, please rebase the driver on top of the master branch of
git://linuxtv.org/media_stage.git. Quite a few in-kernel APIs have
changed.

On Thu, Nov 23, 2023 at 07:51:04PM +0800, Zhi Mao wrote:
> Add a V4L2 sub-device driver for Galaxycore GC08A3 image sensor.
>
> Signed-off-by: Zhi Mao <[email protected]>
> ---
> drivers/media/i2c/Kconfig | 14 +
> drivers/media/i2c/Makefile | 1 +
> drivers/media/i2c/gc08a3.c | 2046 ++++++++++++++++++++++++++++++++++++
> 3 files changed, 2061 insertions(+)
> create mode 100644 drivers/media/i2c/gc08a3.c
>
> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
> index 59ee0ca2c978..2e37be537690 100644
> --- a/drivers/media/i2c/Kconfig
> +++ b/drivers/media/i2c/Kconfig
> @@ -1451,6 +1451,20 @@ config VIDEO_THS7303
> To compile this driver as a module, choose M here: the
> module will be called ths7303.
>
> +config VIDEO_GC08A3

Alphabetical order please.

> + tristate "GalaxyCore gc08a3 sensor support"
> + depends on GPIOLIB && I2C && VIDEO_DEV
> + select V4L2_FWNODE
> + select MEDIA_CONTROLLER
> + select VIDEO_V4L2_SUBDEV_API

You can drop all of the above except

depends on GPIOLIB

The dependencies on I2C and VIDEO_DEV and the selection of V4L2_FWNODE,
MEDIA_CONTROLLER and VIDEIO_V4L2_SUBDEV_API are now handled
automatically for all drivers in the VIDEO_CAMERA_SENSOR menu.

> + select REGMAP_I2C
> + help
> + This is a Video4Linux2 sensor driver for the GalaxyCore gc08a3
> + camera.
> +
> + To compile this driver as a module, choose M here: the
> + module will be called gc08a3.
> +
> endmenu
>
> #
> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
> index f5010f80a21f..ec40dbd75e7a 100644
> --- a/drivers/media/i2c/Makefile
> +++ b/drivers/media/i2c/Makefile
> @@ -36,6 +36,7 @@ obj-$(CONFIG_VIDEO_DW9719) += dw9719.o
> obj-$(CONFIG_VIDEO_DW9768) += dw9768.o
> obj-$(CONFIG_VIDEO_DW9807_VCM) += dw9807-vcm.o
> obj-$(CONFIG_VIDEO_ET8EK8) += et8ek8/
> +obj-$(CONFIG_VIDEO_GC08A3) += gc08a3.o
> obj-$(CONFIG_VIDEO_HI556) += hi556.o
> obj-$(CONFIG_VIDEO_HI846) += hi846.o
> obj-$(CONFIG_VIDEO_HI847) += hi847.o
> diff --git a/drivers/media/i2c/gc08a3.c b/drivers/media/i2c/gc08a3.c
> new file mode 100644
> index 000000000000..d3cf62b65c2e
> --- /dev/null
> +++ b/drivers/media/i2c/gc08a3.c
> @@ -0,0 +1,2046 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * gc08a3.c - gc08a3 sensor driver
> + *
> + * Copyright 2023 Mediatek
> + *
> + * Zhi Mao <[email protected]>
> + */
> +
> +#include <asm/unaligned.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/i2c.h>
> +#include <linux/module.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/regulator/consumer.h>
> +#include <media/media-entity.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-fwnode.h>
> +#include <media/v4l2-subdev.h>
> +
> +//=========================================

V4L2 uses C-style comments.

> +#define GC08A3_REG_VALUE_08BIT 1
> +#define GC08A3_REG_VALUE_16BIT 2
> +#define GC08A3_REG_VALUE_24BIT 3

Please use the v4l2-cci helpers for register access.

> +
> +#define GC08A3_REG_CHIP_ID 0x03f0
> +#define GC08A3_CHIP_ID 0x08a3
> +
> +#define GC08A3_NATIVE_WIDTH 3264
> +#define GC08A3_NATIVE_HEIGHT 2448
> +
> +#define GC08A3_REG_TEST_PATTERN_EN 0x008c
> +#define GC08A3_REG_TEST_PATTERN_IDX 0x008d
> +#define GC08A3_TEST_PATTERN_EN 0x01
> +
> +#define GC08A3_STRAEMING_REG 0x0100
> +
> +#define GC08A3_SCLK 280000000LL
> +
> +#define GC08A3_DEFAULT_CLK_FREQ 24000000
> +#define GC08A3_FPS 30
> +#define GC08A3_MBUS_CODE MEDIA_BUS_FMT_SRGGB10_1X10
> +#define GC08A3_DATA_LANES 4
> +
> +//for 1920*1080
> +#define GC08A3_LINK_FREQ_207MHZ 207000000ULL
> +//for 3264*2448
> +#define GC08A3_LINK_FREQ_336MHZ 336000000ULL

Macros don't help readibility, simply use the numerical values in the
link_freq_menu_items array below.

> +
> +#define GC08A3_RGB_DEPTH 10
> +
> +//frame length
> +#define GC08A3_FL_REG 0x0340
> +#define GC08A3_VTS_30FPS 2548
> +#define GC08A3_VTS_30FPS_MIN 2548
> +#define GC08A3_VTS_60FPS 1276
> +#define GC08A3_VTS_60FPS_MIN 1276
> +#define GC08A3_VTS_MAX 0xfff0
> +#define GC08A3_FL_MARGIN 48
> +
> +// line length
> +#define GC08A3_LL_REG 0x0342
> +#define GC08A3_HTS_30FPS 3640
> +#define GC08A3_HTS_60FPS 3640
> +
> +#define GC08A3_EXP_REG 0x0202
> +#define GC08A3_EXP_MARGIN 16
> +#define GC08A3_EXP_MIN 4
> +#define GC08A3_EXP_STEP 1
> +
> +#define GC08A3_FLIP_REG 0x0101
> +#define GC08A3_FLIP_H_MASK 0x1
> +#define GC08A3_FLIP_V_MASK 0x2
> +
> +#define GC08A3_AGAIN_REG 0x0204
> +#define GC08A3_AGAIN_MIN 1024
> +#define GC08A3_AGAIN_MAX (1024 * 16)
> +#define GC08A3_AGAIN_STEP 1
> +
> +#define GC08A3_DGAIN_REG 0x020e
> +#define GC08A3_DGAIN_MIN 1024
> +#define GC08A3_DGAIN_MAX 1024
> +#define GC08A3_DGAIN_STEP 1
> +//============================================================
> +
> +//============================================================
> +static const char *const gc08a3_test_pattern_menu[] = {
> + "No Pattern", "Solid Black", "Solid White", "Solid Red",
> + "Solid Green", "Solid Blue", "Solid Yellow", "Colour Bar",
> +};
> +
> +enum {
> + GC08A3_LINK_FREQ_336MHZ_CFG,
> + GC08A3_LINK_FREQ_207MHZ_CFG,
> +};
> +
> +static const s64 link_freq_menu_items[] = {
> + GC08A3_LINK_FREQ_336MHZ,
> + GC08A3_LINK_FREQ_207MHZ,
> +};
> +
> +static const char *const gc08a3_supply_name[] = {
> + "avdd",
> + "dvdd",
> + "dovdd",
> +};
> +
> +#define GC08A3_NUM_SUPPLIES ARRAY_SIZE(gc08a3_supply_name)
> +
> +struct gc08a3 {
> + struct device *dev;
> + struct v4l2_subdev sd;
> + struct media_pad pad;
> + struct v4l2_mbus_framefmt fmt;

Please use the subdev active state to store the active format. See
commit e8a5b1df000e ("media: i2c: imx219: Use subdev active state") as
an example of how to do so. There's also documentation in
Documentation/driver-api/media/v4l2-subdev.rst (please read the most
up-to-date version in the master branch of the media_stage tree).

> + struct i2c_client *client;
> +
> + struct v4l2_rect crop;

This should be stored in the subdev active state too.

> +
> + struct clk *xclk;
> + struct regulator_bulk_data supplies[GC08A3_NUM_SUPPLIES];
> + struct gpio_desc *enable_gpio;
> +
> + struct regmap *regmap;
> +
> + struct v4l2_ctrl_handler ctrls;
> + struct v4l2_ctrl *pixel_rate;
> + struct v4l2_ctrl *link_freq;
> + struct v4l2_ctrl *exposure;
> + struct v4l2_ctrl *vblank;
> + struct v4l2_ctrl *hblank;
> +
> + /*
> + * Serialize control access, get/set format, get selection
> + * and start streaming.
> + */
> + struct mutex mutex;

The mutex won't be needed anymore once you use the subdev active state.

> +
> + bool streaming;
> +
> + /* Current mode */
> + const struct gc08a3_mode *cur_mode;
> +};
> +
> +struct reg_8 {
> + u16 address;
> + u8 val;
> +};
> +
> +struct gc08a3_reg_list {
> + u32 num_of_regs;
> + const struct reg_8 *regs;
> +};
> +
> +struct gc08a3_link_freq_config {
> + const struct gc08a3_reg_list reg_list;
> +};
> +
> +enum {
> + GC08A3_TABLE_WAIT_MS = 0,
> + GC08A3_TABLE_END,
> +};
> +
> +/*From gc08a3_mode_tbls.h*/
> +static const struct reg_8 mode_3264x2448[] = {
> + /*system*/
> + { 0x031c, 0x60 },
> + { 0x0337, 0x04 },
> + { 0x0335, 0x51 },
> + { 0x0336, 0x70 },
> + { 0x0383, 0xbb },
> + { 0x031a, 0x00 },
> + { 0x0321, 0x10 },
> + { 0x0327, 0x03 },
> + { 0x0325, 0x40 },
> + { 0x0326, 0x23 },
> + { 0x0314, 0x11 },
> + { 0x0315, 0xd6 },
> + { 0x0316, 0x01 },
> + { 0x0334, 0x40 },
> + { 0x0324, 0x42 },
> + { 0x031c, 0x00 },
> + { 0x031c, 0x9f },
> + { 0x0344, 0x00 },
> + { 0x0345, 0x06 },
> + { 0x0346, 0x00 },
> + { 0x0347, 0x04 },
> + { 0x0348, 0x0c },
> + { 0x0349, 0xd0 }, //3280
> + { 0x034a, 0x09 },
> + { 0x034b, 0x9c }, //2460
> + { 0x0202, 0x09 },
> + { 0x0203, 0x04 }, //Exp
> + { 0x0340, 0x09 },
> + { 0x0341, 0xf4 }, //FL
> + { 0x0342, 0x07 },
> + { 0x0343, 0x1c }, //LineLength
> +
> + { 0x0226, 0x00 }, //min vb[15:8]
> + { 0x0227, 0x28 }, //min vb[7:0]
> + { 0x0e38, 0x49 },
> + { 0x0210, 0x13 },
> + { 0x0218, 0x00 },
> + { 0x0241, 0x88 },
> + { 0x0392, 0x60 },
> +
> + /*ISP*/
> + { 0x031c, 0x80 },
> + { 0x03fe, 0x10 }, //CISCTL_rst
> + { 0x03fe, 0x00 },
> + { 0x031c, 0x9f },
> + { 0x03fe, 0x00 },
> + { 0x03fe, 0x00 },
> + { 0x03fe, 0x00 },
> + { 0x03fe, 0x00 },
> + { 0x031c, 0x80 },
> + { 0x03fe, 0x10 },
> + { 0x03fe, 0x00 },
> + { 0x031c, 0x9f },
> + { 0x00a2, 0x00 },
> + { 0x00a3, 0x00 },
> + { 0x00ab, 0x00 },
> + { 0x00ac, 0x00 },
> + { 0x05a0, 0x82 },
> + { 0x05ac, 0x00 },
> + { 0x05ad, 0x01 },
> + { 0x05ae, 0x00 },
> + { 0x0800, 0x0a },
> + { 0x0801, 0x14 },
> + { 0x0802, 0x28 },
> + { 0x0803, 0x34 },
> + { 0x0804, 0x0e },
> + { 0x0805, 0x33 },
> + { 0x0806, 0x03 },
> + { 0x0807, 0x8a },
> + { 0x0808, 0x50 },
> + { 0x0809, 0x00 },
> + { 0x080a, 0x34 },
> + { 0x080b, 0x03 },
> + { 0x080c, 0x26 },
> + { 0x080d, 0x03 },
> + { 0x080e, 0x18 },
> + { 0x080f, 0x03 },
> + { 0x0810, 0x10 },
> + { 0x0811, 0x03 },
> + { 0x0812, 0x00 },
> + { 0x0813, 0x00 },
> + { 0x0814, 0x01 },
> + { 0x0815, 0x00 },
> + { 0x0816, 0x01 },
> + { 0x0817, 0x00 },
> + { 0x0818, 0x00 },
> + { 0x0819, 0x0a },
> + { 0x081a, 0x01 },
> + { 0x081b, 0x6c },
> + { 0x081c, 0x00 },
> + { 0x081d, 0x0b },
> + { 0x081e, 0x02 },
> + { 0x081f, 0x00 },
> + { 0x0820, 0x00 },
> + { 0x0821, 0x0c },
> + { 0x0822, 0x02 },
> + { 0x0823, 0xd9 },
> + { 0x0824, 0x00 },
> + { 0x0825, 0x0d },
> + { 0x0826, 0x03 },
> + { 0x0827, 0xf0 },
> + { 0x0828, 0x00 },
> + { 0x0829, 0x0e },
> + { 0x082a, 0x05 },
> + { 0x082b, 0x94 },
> + { 0x082c, 0x09 },
> + { 0x082d, 0x6e },
> + { 0x082e, 0x07 },
> + { 0x082f, 0xe6 },
> + { 0x0830, 0x10 },
> + { 0x0831, 0x0e },
> + { 0x0832, 0x0b },
> + { 0x0833, 0x2c },
> + { 0x0834, 0x14 },
> + { 0x0835, 0xae },
> + { 0x0836, 0x0f },
> + { 0x0837, 0xc4 },
> + { 0x0838, 0x18 },
> + { 0x0839, 0x0e },
> + { 0x05ac, 0x01 },
> + { 0x059a, 0x00 },
> + { 0x059b, 0x00 },
> + { 0x059c, 0x01 },
> + { 0x0598, 0x00 },
> + { 0x0597, 0x14 },
> + { 0x05ab, 0x09 },
> + { 0x05a4, 0x02 },
> + { 0x05a3, 0x05 },
> + { 0x05a0, 0xc2 },
> + { 0x0207, 0xc4 },
> +
> + /*GAIN*/
> + { 0x0204, 0x04 },
> + { 0x0205, 0x00 },
> + { 0x0050, 0x5c },
> + { 0x0051, 0x44 },
> +
> + /*out window*/
> + { 0x009a, 0x66 },
> + { 0x0351, 0x00 },
> + { 0x0352, 0x06 }, //out_win_y1
> + { 0x0353, 0x00 },
> + { 0x0354, 0x08 }, //out_win_x1
> + { 0x034c, 0x0c },
> + { 0x034d, 0xc0 }, //3264
> + { 0x034e, 0x09 },
> + { 0x034f, 0x90 }, //2448
> +
> + /*MIPI*/
> + { 0x0114, 0x03 }, //0:1lane 1:2lane 3:4lane
> + { 0x0180, 0x65 }, //[3:0]dphy_mipi_diff
> + { 0x0181, 0xf0 },
> + { 0x0185, 0x01 },
> + { 0x0115, 0x30 },
> + { 0x011b, 0x12 },
> + { 0x011c, 0x12 },
> + { 0x0121, 0x06 }, //T_LPX
> + { 0x0122, 0x06 }, //T_CLK_HS_PREPARE
> + { 0x0123, 0x15 }, //T_CLK_zero
> + { 0x0124, 0x01 }, //T_CLK_PRE
> + { 0x0125, 0x0b }, //T_CLK_POST
> + { 0x0126, 0x08 }, //T_CLK_TRAIL
> + { 0x0129, 0x06 }, //T_HS_PREPARE
> + { 0x012a, 0x08 }, //T_HS_Zero
> + { 0x012b, 0x08 }, //T_HS_TRAIL
> +
> + { 0x0a73, 0x60 },
> + { 0x0a70, 0x11 },
> + { 0x0313, 0x80 },
> + { 0x0aff, 0x00 },
> + { 0x0aff, 0x00 },
> + { 0x0aff, 0x00 },
> + { 0x0aff, 0x00 },
> + { 0x0aff, 0x00 },
> + { 0x0aff, 0x00 },
> + { 0x0aff, 0x00 },
> + { 0x0aff, 0x00 },
> + { 0x0a70, 0x00 },
> + { 0x00a4, 0x80 },
> + { 0x0316, 0x01 },
> + { 0x0a67, 0x00 },
> + { 0x0084, 0x10 },
> + { 0x0102, 0x09 },
> +
> + { GC08A3_TABLE_END, 0x00 }
> +};
> +
> +static const struct reg_8 mode_1920x1080[] = {
> + /*system*/
> + { 0x031c, 0x60 },
> + { 0x0337, 0x04 },
> + { 0x0335, 0x51 },
> + { 0x0336, 0x45 },
> + { 0x0383, 0x8b },
> + { 0x031a, 0x00 },
> + { 0x0321, 0x10 },
> + { 0x0327, 0x03 },
> + { 0x0325, 0x40 },
> + { 0x0326, 0x23 },
> + { 0x0314, 0x11 },
> + { 0x0315, 0xd6 },
> + { 0x0316, 0x01 },
> + { 0x0334, 0x40 },
> + { 0x0324, 0x42 },
> + { 0x031c, 0x00 },
> + { 0x031c, 0x9f },
> + { 0x0344, 0x02 },
> + { 0x0345, 0xa6 },
> + { 0x0346, 0x02 },
> + { 0x0347, 0xb0 },
> + { 0x0348, 0x07 },
> + { 0x0349, 0x90 }, //1936
> + { 0x034a, 0x04 },
> + { 0x034b, 0x44 }, //1092
> + { 0x0202, 0x03 },
> + { 0x0203, 0x00 }, //Exp
> + { 0x0340, 0x04 },
> + { 0x0341, 0xfc }, //FL
> + { 0x0342, 0x07 },
> + { 0x0343, 0x1c }, //LineLength
> +
> + { 0x0226, 0x00 }, //min vb[15:8]
> + { 0x0227, 0x88 }, //min vb[7:0]
> + { 0x0e38, 0x49 },
> + { 0x0210, 0x13 },
> + { 0x0218, 0x00 },
> + { 0x0241, 0x88 },
> + { 0x0392, 0x60 },
> +
> + /*ISP*/
> + { 0x031c, 0x80 },
> + { 0x03fe, 0x10 }, //CISCTL_rst
> + { 0x03fe, 0x00 },
> + { 0x031c, 0x9f },
> + { 0x03fe, 0x00 },
> + { 0x03fe, 0x00 },
> + { 0x03fe, 0x00 },
> + { 0x03fe, 0x00 },
> + { 0x031c, 0x80 },
> + { 0x03fe, 0x10 },
> + { 0x03fe, 0x00 },
> + { 0x031c, 0x9f },
> + { 0x00a2, 0xac },
> + { 0x00a3, 0x02 },
> + { 0x00ab, 0xa0 },
> + { 0x00ac, 0x02 },
> + { 0x05a0, 0x82 },
> + { 0x05ac, 0x00 },
> + { 0x05ad, 0x01 },
> + { 0x05ae, 0x00 },
> + { 0x0800, 0x0a },
> + { 0x0801, 0x14 },
> + { 0x0802, 0x28 },
> + { 0x0803, 0x34 },
> + { 0x0804, 0x0e },
> + { 0x0805, 0x33 },
> + { 0x0806, 0x03 },
> + { 0x0807, 0x8a },
> + { 0x0808, 0x50 },
> + { 0x0809, 0x00 },
> + { 0x080a, 0x34 },
> + { 0x080b, 0x03 },
> + { 0x080c, 0x26 },
> + { 0x080d, 0x03 },
> + { 0x080e, 0x18 },
> + { 0x080f, 0x03 },
> + { 0x0810, 0x10 },
> + { 0x0811, 0x03 },
> + { 0x0812, 0x00 },
> + { 0x0813, 0x00 },
> + { 0x0814, 0x01 },
> + { 0x0815, 0x00 },
> + { 0x0816, 0x01 },
> + { 0x0817, 0x00 },
> + { 0x0818, 0x00 },
> + { 0x0819, 0x0a },
> + { 0x081a, 0x01 },
> + { 0x081b, 0x6c },
> + { 0x081c, 0x00 },
> + { 0x081d, 0x0b },
> + { 0x081e, 0x02 },
> + { 0x081f, 0x00 },
> + { 0x0820, 0x00 },
> + { 0x0821, 0x0c },
> + { 0x0822, 0x02 },
> + { 0x0823, 0xd9 },
> + { 0x0824, 0x00 },
> + { 0x0825, 0x0d },
> + { 0x0826, 0x03 },
> + { 0x0827, 0xf0 },
> + { 0x0828, 0x00 },
> + { 0x0829, 0x0e },
> + { 0x082a, 0x05 },
> + { 0x082b, 0x94 },
> + { 0x082c, 0x09 },
> + { 0x082d, 0x6e },
> + { 0x082e, 0x07 },
> + { 0x082f, 0xe6 },
> + { 0x0830, 0x10 },
> + { 0x0831, 0x0e },
> + { 0x0832, 0x0b },
> + { 0x0833, 0x2c },
> + { 0x0834, 0x14 },
> + { 0x0835, 0xae },
> + { 0x0836, 0x0f },
> + { 0x0837, 0xc4 },
> + { 0x0838, 0x18 },
> + { 0x0839, 0x0e },
> + { 0x05ac, 0x01 },
> + { 0x059a, 0x00 },
> + { 0x059b, 0x00 },
> + { 0x059c, 0x01 },
> + { 0x0598, 0x00 },
> + { 0x0597, 0x14 },
> + { 0x05ab, 0x09 },
> + { 0x05a4, 0x02 },
> + { 0x05a3, 0x05 },
> + { 0x05a0, 0xc2 },
> + { 0x0207, 0xc4 },
> +
> + /*GAIN*/
> + { 0x0204, 0x04 },
> + { 0x0205, 0x00 },
> + { 0x0050, 0x38 },
> + { 0x0051, 0x20 },
> +
> + /*out window*/
> + { 0x009a, 0x66 },
> + { 0x0351, 0x00 },
> + { 0x0352, 0x06 }, //out_win_y1
> + { 0x0353, 0x00 },
> + { 0x0354, 0x08 }, //out_win_x1
> + { 0x034c, 0x07 },
> + { 0x034d, 0x80 }, //1920
> + { 0x034e, 0x04 },
> + { 0x034f, 0x38 }, //1080
> +
> + /*MIPI*/
> + { 0x0114, 0x03 }, //0:1lane 1:2lane 3:4lane
> + { 0x0180, 0x65 }, //[3:0]dphy_mipi_diff
> + { 0x0181, 0xf0 },
> + { 0x0185, 0x01 },
> + { 0x0115, 0x30 },
> + { 0x011b, 0x12 },
> + { 0x011c, 0x12 },
> + { 0x0121, 0x02 }, //T_LPX
> + { 0x0122, 0x03 }, //T_CLK_HS_PREPARE
> + { 0x0123, 0x0c }, //T_CLK_zero
> + { 0x0124, 0x00 }, //T_CLK_PRE
> + { 0x0125, 0x09 }, //T_CLK_POST
> + { 0x0126, 0x06 }, //T_CLK_TRAIL
> + { 0x0129, 0x04 }, //T_HS_PREPARE
> + { 0x012a, 0x03 }, //T_HS_Zero
> + { 0x012b, 0x06 }, //T_HS_TRAIL
> +
> + { 0x0a73, 0x60 },
> + { 0x0a70, 0x11 },
> + { 0x0313, 0x80 },
> + { 0x0aff, 0x00 },
> + { 0x0aff, 0x00 },
> + { 0x0aff, 0x00 },
> + { 0x0aff, 0x00 },
> + { 0x0aff, 0x00 },
> + { 0x0aff, 0x00 },
> + { 0x0aff, 0x00 },
> + { 0x0aff, 0x00 },
> + { 0x0a70, 0x00 },
> + { 0x00a4, 0x80 },
> + { 0x0316, 0x01 },
> + { 0x0a67, 0x00 },
> + { 0x0084, 0x10 },
> + { 0x0102, 0x09 },
> +
> + { GC08A3_TABLE_END, 0x00 }
> +};
> +
> +static const struct reg_8 mode_table_common[] = {
> + { GC08A3_STRAEMING_REG, 0x00 },
> + /*system*/
> + { 0x031c, 0x60 },
> + { 0x0337, 0x04 },
> + { 0x0335, 0x51 },
> + { 0x0336, 0x70 },
> + { 0x0383, 0xbb },
> + { 0x031a, 0x00 },
> + { 0x0321, 0x10 },
> + { 0x0327, 0x03 },
> + { 0x0325, 0x40 },
> + { 0x0326, 0x23 },
> + { 0x0314, 0x11 },
> + { 0x0315, 0xd6 },
> + { 0x0316, 0x01 },
> + { 0x0334, 0x40 },
> + { 0x0324, 0x42 },
> + { 0x031c, 0x00 },
> + { 0x031c, 0x9f },
> + { 0x039a, 0x13 },
> + { 0x0084, 0x30 },
> + { 0x02b3, 0x08 },
> + { 0x0057, 0x0c },
> + { 0x05c3, 0x50 },
> + { 0x0311, 0x90 },
> + { 0x05a0, 0x02 },
> + { 0x0074, 0x0a },
> + { 0x0059, 0x11 },
> + { 0x0070, 0x05 },
> + { 0x0101, 0x00 }, //[1]updown [0]mirror
> +
> + /*analog*/
> + { 0x0344, 0x00 },
> + { 0x0345, 0x06 },
> + { 0x0346, 0x00 },
> + { 0x0347, 0x04 },
> + { 0x0348, 0x0c },
> + { 0x0349, 0xd0 }, //3280
> + { 0x034a, 0x09 },
> + { 0x034b, 0x9c }, //2460
> + { 0x0202, 0x09 },
> + { 0x0203, 0x04 }, //Exp
> +
> + { 0x0219, 0x05 }, //[4]FL_depend_exp
> + { 0x0226, 0x00 }, //min vb[15:8]
> + { 0x0227, 0x28 }, //min vb[7:0]
> + { 0x0e0a, 0x00 },
> + { 0x0e0b, 0x00 },
> + { 0x0e24, 0x04 },
> + { 0x0e25, 0x04 },
> + { 0x0e26, 0x00 },
> + { 0x0e27, 0x10 },
> + { 0x0e01, 0x74 },
> + { 0x0e03, 0x47 },
> + { 0x0e04, 0x33 },
> + { 0x0e05, 0x44 },
> + { 0x0e06, 0x44 },
> + { 0x0e0c, 0x1e },
> + { 0x0e17, 0x3a },
> + { 0x0e18, 0x3c },
> + { 0x0e19, 0x40 },
> + { 0x0e1a, 0x42 },
> + { 0x0e28, 0x21 },
> + { 0x0e2b, 0x68 },
> + { 0x0e2c, 0x0d },
> + { 0x0e2d, 0x08 },
> + { 0x0e34, 0xf4 },
> + { 0x0e35, 0x44 },
> + { 0x0e36, 0x07 },
> + { 0x0e38, 0x49 },
> + { 0x0210, 0x13 },
> + { 0x0218, 0x00 },
> + { 0x0241, 0x88 },
> + { 0x0e32, 0x00 },
> + { 0x0e33, 0x18 },
> + { 0x0e42, 0x03 },
> + { 0x0e43, 0x80 },
> + { 0x0e44, 0x04 },
> + { 0x0e45, 0x00 },
> + { 0x0e4f, 0x04 },
> + { 0x057a, 0x20 },
> + { 0x0381, 0x7c },
> + { 0x0382, 0x9b },
> + { 0x0384, 0xfb },
> + { 0x0389, 0x38 },
> + { 0x038a, 0x03 },
> + { 0x0390, 0x6a },
> + { 0x0391, 0x0b },
> + { 0x0392, 0x60 },
> + { 0x0393, 0xc1 },
> + { 0x0396, 0xff },
> + { 0x0398, 0x62 },
> +
> + /*cisctl reset*/
> + { 0x031c, 0x80 },
> + { 0x03fe, 0x10 }, //CISCTL_rst
> + { 0x03fe, 0x00 },
> + { 0x031c, 0x9f },
> + { 0x03fe, 0x00 },
> + { 0x03fe, 0x00 },
> + { 0x03fe, 0x00 },
> + { 0x03fe, 0x00 },
> + { 0x031c, 0x80 },
> + { 0x03fe, 0x10 }, //CISCTL_rst
> + { 0x03fe, 0x00 },
> + { 0x031c, 0x9f },
> + { 0x0360, 0x01 },
> + { 0x0360, 0x00 },
> + { 0x0316, 0x09 },
> + { 0x0a67, 0x80 },
> + { 0x0313, 0x00 },
> + { 0x0a53, 0x0e },
> + { 0x0a65, 0x17 },
> + { 0x0a68, 0xa1 },
> + { 0x0a58, 0x00 },
> + { 0x0ace, 0x0c },
> + { 0x00a4, 0x00 },
> + { 0x00a5, 0x01 },
> + { 0x00a7, 0x09 },
> + { 0x00a8, 0x9c },
> + { 0x00a9, 0x0c },
> + { 0x00aa, 0xd0 },
> + { 0x0a8a, 0x00 },
> + { 0x0a8b, 0xe0 },
> + { 0x0a8c, 0x13 },
> + { 0x0a8d, 0xe8 },
> + { 0x0a90, 0x0a },
> + { 0x0a91, 0x10 },
> + { 0x0a92, 0xf8 },
> + { 0x0a71, 0xf2 },
> + { 0x0a72, 0x12 },
> + { 0x0a73, 0x64 },
> + { 0x0a75, 0x41 },
> + { 0x0a70, 0x07 },
> + { 0x0313, 0x80 },
> +
> + /*ISP*/
> + { 0x00a0, 0x01 },
> + { 0x0080, 0xd2 },
> + { 0x0081, 0x3f },
> + { 0x0087, 0x51 },
> + { 0x0089, 0x03 },
> + { 0x009b, 0x40 },
> + { 0x05a0, 0x82 },
> + { 0x05ac, 0x00 },
> + { 0x05ad, 0x01 },
> + { 0x05ae, 0x00 },
> + { 0x0800, 0x0a },
> + { 0x0801, 0x14 },
> + { 0x0802, 0x28 },
> + { 0x0803, 0x34 },
> + { 0x0804, 0x0e },
> + { 0x0805, 0x33 },
> + { 0x0806, 0x03 },
> + { 0x0807, 0x8a },
> + { 0x0808, 0x50 },
> + { 0x0809, 0x00 },
> + { 0x080a, 0x34 },
> + { 0x080b, 0x03 },
> + { 0x080c, 0x26 },
> + { 0x080d, 0x03 },
> + { 0x080e, 0x18 },
> + { 0x080f, 0x03 },
> + { 0x0810, 0x10 },
> + { 0x0811, 0x03 },
> + { 0x0812, 0x00 },
> + { 0x0813, 0x00 },
> + { 0x0814, 0x01 },
> + { 0x0815, 0x00 },
> + { 0x0816, 0x01 },
> + { 0x0817, 0x00 },
> + { 0x0818, 0x00 },
> + { 0x0819, 0x0a },
> + { 0x081a, 0x01 },
> + { 0x081b, 0x6c },
> + { 0x081c, 0x00 },
> + { 0x081d, 0x0b },
> + { 0x081e, 0x02 },
> + { 0x081f, 0x00 },
> + { 0x0820, 0x00 },
> + { 0x0821, 0x0c },
> + { 0x0822, 0x02 },
> + { 0x0823, 0xd9 },
> + { 0x0824, 0x00 },
> + { 0x0825, 0x0d },
> + { 0x0826, 0x03 },
> + { 0x0827, 0xf0 },
> + { 0x0828, 0x00 },
> + { 0x0829, 0x0e },
> + { 0x082a, 0x05 },
> + { 0x082b, 0x94 },
> + { 0x082c, 0x09 },
> + { 0x082d, 0x6e },
> + { 0x082e, 0x07 },
> + { 0x082f, 0xe6 },
> + { 0x0830, 0x10 },
> + { 0x0831, 0x0e },
> + { 0x0832, 0x0b },
> + { 0x0833, 0x2c },
> + { 0x0834, 0x14 },
> + { 0x0835, 0xae },
> + { 0x0836, 0x0f },
> + { 0x0837, 0xc4 },
> + { 0x0838, 0x18 },
> + { 0x0839, 0x0e },
> + { 0x05ac, 0x01 },
> + { 0x059a, 0x00 },
> + { 0x059b, 0x00 },
> + { 0x059c, 0x01 },
> + { 0x0598, 0x00 },
> + { 0x0597, 0x14 },
> + { 0x05ab, 0x09 },
> + { 0x05a4, 0x02 },
> + { 0x05a3, 0x05 },
> + { 0x05a0, 0xc2 },
> + { 0x0207, 0xc4 },
> +
> + /*GAIN*/
> + { 0x0208, 0x01 },
> + { 0x0209, 0x72 },
> + { 0x0204, 0x04 },
> + { 0x0205, 0x00 },
> +
> + { 0x0040, 0x22 },
> + { 0x0041, 0x20 },
> + { 0x0043, 0x10 },
> + { 0x0044, 0x00 },
> + { 0x0046, 0x08 },
> + { 0x0047, 0xf0 },
> + { 0x0048, 0x0f },
> + { 0x004b, 0x0f },
> + { 0x004c, 0x00 },
> + { 0x0050, 0x5c },
> + { 0x0051, 0x44 },
> + { 0x005b, 0x03 },
> + { 0x00c0, 0x00 },
> + { 0x00c1, 0x80 },
> + { 0x00c2, 0x31 },
> + { 0x00c3, 0x00 },
> + { 0x0460, 0x04 },
> + { 0x0462, 0x08 },
> + { 0x0464, 0x0e },
> + { 0x0466, 0x0a },
> + { 0x0468, 0x12 },
> + { 0x046a, 0x12 },
> + { 0x046c, 0x10 },
> + { 0x046e, 0x0c },
> + { 0x0461, 0x03 },
> + { 0x0463, 0x03 },
> + { 0x0465, 0x03 },
> + { 0x0467, 0x03 },
> + { 0x0469, 0x04 },
> + { 0x046b, 0x04 },
> + { 0x046d, 0x04 },
> + { 0x046f, 0x04 },
> + { 0x0470, 0x04 },
> + { 0x0472, 0x10 },
> + { 0x0474, 0x26 },
> + { 0x0476, 0x38 },
> + { 0x0478, 0x20 },
> + { 0x047a, 0x30 },
> + { 0x047c, 0x38 },
> + { 0x047e, 0x60 },
> + { 0x0471, 0x05 },
> + { 0x0473, 0x05 },
> + { 0x0475, 0x05 },
> + { 0x0477, 0x05 },
> + { 0x0479, 0x04 },
> + { 0x047b, 0x04 },
> + { 0x047d, 0x04 },
> + { 0x047f, 0x04 },
> +
> + { GC08A3_TABLE_END, 0x00 }
> +};
> +
> +static const struct gc08a3_link_freq_config link_freq_configs[] = {
> + [GC08A3_LINK_FREQ_336MHZ_CFG] = {
> + .reg_list = {
> + .num_of_regs = ARRAY_SIZE(mode_table_common),
> + .regs = mode_table_common,
> + }
> + },
> + [GC08A3_LINK_FREQ_207MHZ_CFG] = {
> + .reg_list = {
> + .num_of_regs = ARRAY_SIZE(mode_table_common),
> + .regs = mode_table_common,
> + }
> + },
> +

Extra blank line.

> +};
> +
> +enum {
> + GC08A3_PREV,
> + GC08A3_HS,
> +};
> +
> +/*
> + * Declare modes in order, from biggest
> + * to smallest height.
> + */
> +static const struct gc08a3_mode {
> + u8 mode_id;
> + u32 width;
> + u32 height;
> + const struct gc08a3_reg_list reg_list;
> +
> + u32 hts; /* Horizontal timining size */
> + u32 vts_def; /* Default vertical timining size */
> + u32 vts_min; /* Min vertical timining size */
> + u32 link_freq_index; /* Link frequency needed for this resolution */
> + u32 max_framerate;
> +

Extra blank line.

> +} gc08a3_modes[] = {
> + {
> + .mode_id = GC08A3_PREV,
> + .width = 3264,
> + .height = 2448,
> + .reg_list = {
> + .num_of_regs = ARRAY_SIZE(mode_3264x2448),
> + .regs = mode_3264x2448,
> + },
> + .link_freq_index = GC08A3_LINK_FREQ_336MHZ_CFG,
> +
> + .hts = GC08A3_HTS_30FPS,
> + .vts_def = GC08A3_VTS_30FPS,
> + .vts_min = GC08A3_VTS_30FPS_MIN,
> + .max_framerate = 300,
> + },
> + {
> + .mode_id = GC08A3_HS,
> + .width = 1920,
> + .height = 1080,
> + .reg_list = {
> + .num_of_regs = ARRAY_SIZE(mode_1920x1080),
> + .regs = mode_1920x1080,
> + },
> + .link_freq_index = GC08A3_LINK_FREQ_207MHZ_CFG,
> +
> + .hts = GC08A3_HTS_60FPS,
> + .vts_def = GC08A3_VTS_60FPS,
> + .vts_min = GC08A3_VTS_60FPS_MIN,
> + .max_framerate = 600,
> + },
> +};
> +
> +static u64 to_pixel_rate(u32 f_index)
> +{
> + u64 pixel_rate = link_freq_menu_items[f_index] * 2 * GC08A3_DATA_LANES;
> +
> + do_div(pixel_rate, GC08A3_RGB_DEPTH);
> +
> + return pixel_rate;
> +}
> +
> +static u64 __maybe_unused to_pixels_per_line(u32 hts, u32 f_index)
> +{
> + u64 ppl = hts * to_pixel_rate(f_index);
> +
> + do_div(ppl, GC08A3_SCLK);
> +
> + return ppl;
> +}
> +
> +static int gc08a3_read_reg(struct gc08a3 *gc08a3, u16 reg, u16 len, u32 *val)
> +{
> + struct i2c_client *client = v4l2_get_subdevdata(&gc08a3->sd);
> + struct i2c_msg msgs[2];
> + u8 addr_buf[2];
> + u8 data_buf[4] = { 0 };
> + int ret;
> +
> + if (len > 4)
> + return -EINVAL;
> +
> + put_unaligned_be16(reg, addr_buf);
> + msgs[0].addr = client->addr;
> + msgs[0].flags = 0;
> + msgs[0].len = sizeof(addr_buf);
> + msgs[0].buf = addr_buf;
> + msgs[1].addr = client->addr;
> + msgs[1].flags = I2C_M_RD;
> + msgs[1].len = len;
> + msgs[1].buf = &data_buf[4 - len];
> +
> + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
> + if (ret != ARRAY_SIZE(msgs))
> + return -EIO;
> +
> + *val = get_unaligned_be32(data_buf);
> +
> + return 0;
> +}
> +
> +static int gc08a3_write_reg(struct gc08a3 *gc08a3, u16 reg, u16 len, u32 val)
> +{
> + struct i2c_client *client = v4l2_get_subdevdata(&gc08a3->sd);
> + u8 buf[6];
> +
> + if (len > 4)
> + return -EINVAL;
> +
> + put_unaligned_be16(reg, buf);
> + put_unaligned_be32(val << 8 * (4 - len), buf + 2);
> + if (i2c_master_send(client, buf, len + 2) != len + 2)
> + return -EIO;
> +
> + return 0;
> +}
> +
> +static int gc08a3_write_reg_list(struct gc08a3 *gc08a3,
> + const struct gc08a3_reg_list *r_list)
> +{
> + struct i2c_client *client = v4l2_get_subdevdata(&gc08a3->sd);
> + unsigned int i;
> + int ret;
> +
> + for (i = 0; i < r_list->num_of_regs; i++) {
> + if (r_list->regs[i].address == GC08A3_TABLE_WAIT_MS) {
> + usleep_range(r_list->regs[i].val * 1000,
> + r_list->regs[i].val * 1000 + 500);
> + continue;
> + }
> +
> + if (r_list->regs[i].address == GC08A3_TABLE_END)
> + break;
> +
> + ret = gc08a3_write_reg(gc08a3, r_list->regs[i].address,
> + GC08A3_REG_VALUE_08BIT,
> + r_list->regs[i].val);
> + if (ret) {
> + dev_err_ratelimited(&client->dev,
> + "failed to write reg 0x%4.4x. error = %d",
> + r_list->regs[i].address, ret);
> + return ret;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int gc08a3_identify_module(struct gc08a3 *gc08a3)
> +{
> + struct i2c_client *client = v4l2_get_subdevdata(&gc08a3->sd);
> + u32 val = 0;
> +
> + gc08a3_read_reg(gc08a3, GC08A3_REG_CHIP_ID, GC08A3_REG_VALUE_16BIT,
> + &val);
> +
> + if (val != GC08A3_CHIP_ID) {
> + dev_err(&client->dev, "chip id mismatch: 0x%x!=0x%x",
> + GC08A3_CHIP_ID, val);
> + return -ENXIO;
> + }
> +
> + dev_info(&client->dev, "sensor_id: 0x%04x\n", val);
> + return 0;
> +}
> +
> +static inline struct gc08a3 *to_gc08a3(struct v4l2_subdev *sd)
> +{
> + return container_of(sd, struct gc08a3, sd);
> +}
> +
> +static int __maybe_unused gc08a3_power_on(struct device *dev)
> +{
> + struct i2c_client *client = to_i2c_client(dev);
> + struct v4l2_subdev *sd = i2c_get_clientdata(client);
> + struct gc08a3 *gc08a3 = to_gc08a3(sd);
> + int ret;
> +
> + dev_info(dev, "--- %s +", __func__);

Drop this. Tracing is done with ftrace. Same comment below where
applicable.

> +
> + gpiod_set_value_cansleep(gc08a3->enable_gpio, 0);
> + usleep_range(2000, 3000);
> +
> + ret = regulator_bulk_enable(GC08A3_NUM_SUPPLIES, gc08a3->supplies);
> + if (ret < 0) {
> + dev_err(gc08a3->dev, "failed to enable regulators: %d\n", ret);
> + return ret;
> + }
> +
> + ret = clk_prepare_enable(gc08a3->xclk);
> + if (ret < 0) {
> + regulator_bulk_disable(GC08A3_NUM_SUPPLIES, gc08a3->supplies);
> + dev_err(gc08a3->dev, "clk prepare enable failed\n");
> + return ret;
> + }
> +
> + usleep_range(2000, 3000);
> +
> + gpiod_set_value_cansleep(gc08a3->enable_gpio, 1);
> + usleep_range(12000, 15000);
> +
> + dev_info(dev, "--- %s -", __func__);
> +
> + return 0;
> +}
> +
> +static int __maybe_unused gc08a3_power_off(struct device *dev)
> +{
> + struct i2c_client *client = to_i2c_client(dev);
> + struct v4l2_subdev *sd = i2c_get_clientdata(client);
> + struct gc08a3 *gc08a3 = to_gc08a3(sd);
> +
> + dev_info(dev, "--- %s +", __func__);
> +
> + gpiod_set_value_cansleep(gc08a3->enable_gpio, 0);
> +
> + clk_disable_unprepare(gc08a3->xclk);
> +
> + regulator_bulk_disable(GC08A3_NUM_SUPPLIES, gc08a3->supplies);
> + usleep_range(10, 20);
> +
> + dev_info(dev, "--- %s -", __func__);
> +
> + return 0;
> +}
> +
> +static int gc08a3_enum_mbus_code(struct v4l2_subdev *sd,
> + struct v4l2_subdev_state *sd_state,
> + struct v4l2_subdev_mbus_code_enum *code)
> +{
> + if (code->index > 0)
> + return -EINVAL;
> +
> + code->code = GC08A3_MBUS_CODE;
> +
> + return 0;
> +}
> +
> +static int gc08a3_enum_frame_size(struct v4l2_subdev *subdev,
> + struct v4l2_subdev_state *sd_state,
> + struct v4l2_subdev_frame_size_enum *fse)
> +{
> + if (fse->code != GC08A3_MBUS_CODE)
> + return -EINVAL;
> +
> + if (fse->index >= ARRAY_SIZE(gc08a3_modes))
> + return -EINVAL;
> +
> + fse->min_width = gc08a3_modes[fse->index].width;
> + fse->max_width = gc08a3_modes[fse->index].width;
> + fse->min_height = gc08a3_modes[fse->index].height;
> + fse->max_height = gc08a3_modes[fse->index].height;
> +
> + return 0;
> +}
> +
> +#ifdef CONFIG_VIDEO_ADV_DEBUG
> +static int gc08a3_g_register(struct v4l2_subdev *subdev,
> + struct v4l2_dbg_register *reg)
> +{
> + int ret;
> + u32 val;
> + struct gc08a3 *gc08a3 = container_of(subdev, struct gc08a3, sd);
> +
> + ret = gc08a3_read_reg(gc08a3, reg->reg, GC08A3_REG_VALUE_08BIT, &val);
> + if (ret < 0)
> + return ret;
> +
> + reg->val = val;
> + reg->size = 1;
> +
> + return 0;
> +}
> +
> +static int gc08a3_s_register(struct v4l2_subdev *subdev,
> + const struct v4l2_dbg_register *reg)
> +{
> + struct gc08a3 *gc08a3 = container_of(subdev, struct gc08a3, sd);
> +
> + return gc08a3_write_reg(gc08a3, reg->reg, GC08A3_REG_VALUE_08BIT,
> + reg->val & 0xff);
> +}
> +
> +#endif
> +
> +static const struct v4l2_subdev_core_ops gc08a3_core_ops = {
> +#ifdef CONFIG_VIDEO_ADV_DEBUG
> + .g_register = gc08a3_g_register,
> + .s_register = gc08a3_s_register,
> +#endif

Drop these too. If you need to access registers directly from userspace
for development purpose, you can do so through i2cdev.

> +};
> +
> +static struct v4l2_mbus_framefmt *
> +__gc08a3_get_pad_format(struct gc08a3 *gc08a3,
> + struct v4l2_subdev_state *sd_state, unsigned int pad,
> + enum v4l2_subdev_format_whence which)
> +{
> + switch (which) {
> + case V4L2_SUBDEV_FORMAT_TRY:
> + return v4l2_subdev_get_try_format(&gc08a3->sd, sd_state, pad);
> + case V4L2_SUBDEV_FORMAT_ACTIVE:
> + return &gc08a3->fmt;
> + default:
> + return NULL;
> + }
> +}
> +
> +static int gc08a3_get_format(struct v4l2_subdev *sd,
> + struct v4l2_subdev_state *sd_state,
> + struct v4l2_subdev_format *format)
> +{
> + struct gc08a3 *gc08a3 = to_gc08a3(sd);
> +
> + mutex_lock(&gc08a3->mutex);
> + format->format = *__gc08a3_get_pad_format(gc08a3, sd_state, format->pad,
> + format->which);
> + mutex_unlock(&gc08a3->mutex);
> +
> + return 0;
> +}
> +
> +static struct v4l2_rect *
> +__gc08a3_get_pad_crop(struct gc08a3 *gc08a3, struct v4l2_subdev_state *sd_state,
> + unsigned int pad, enum v4l2_subdev_format_whence which)
> +{
> + switch (which) {
> + case V4L2_SUBDEV_FORMAT_TRY:
> + return v4l2_subdev_get_try_crop(&gc08a3->sd, sd_state, pad);
> + case V4L2_SUBDEV_FORMAT_ACTIVE:
> + return &gc08a3->crop;
> + default:
> + return NULL;
> + }
> +}
> +
> +static int gc08a3_update_cur_mode_controls(struct gc08a3 *gc08a3)
> +{
> + s64 exposure_max, h_blank;
> + int ret = 0;
> +
> + ret = __v4l2_ctrl_modify_range(gc08a3->vblank,
> + gc08a3->cur_mode->vts_min - gc08a3->cur_mode->height,
> + GC08A3_VTS_MAX - gc08a3->cur_mode->height, 1,
> + gc08a3->cur_mode->vts_def - gc08a3->cur_mode->height);
> + if (ret)
> + dev_err(gc08a3->dev, "VB ctrl range update failed\n");
> +
> + h_blank = gc08a3->cur_mode->hts - gc08a3->cur_mode->width;
> + ret = __v4l2_ctrl_modify_range(gc08a3->hblank, h_blank, h_blank, 1,
> + h_blank);
> + if (ret)
> + dev_err(gc08a3->dev, "HB ctrl range update failed\n");
> +
> + exposure_max = gc08a3->cur_mode->vts_def - GC08A3_EXP_MARGIN;
> + ret = __v4l2_ctrl_modify_range(gc08a3->exposure, GC08A3_EXP_MIN,
> + exposure_max, GC08A3_EXP_STEP,
> + exposure_max);
> + if (ret)
> + dev_err(gc08a3->dev, "exposure ctrl range update failed\n");
> +
> + return ret;
> +}
> +
> +static int gc08a3_set_format(struct v4l2_subdev *sd,
> + struct v4l2_subdev_state *sd_state,
> + struct v4l2_subdev_format *format)
> +{
> + struct gc08a3 *gc08a3 = to_gc08a3(sd);
> + struct i2c_client *client = v4l2_get_subdevdata(&gc08a3->sd);
> + struct device *dev = &client->dev;
> + struct v4l2_mbus_framefmt *__format;
> + struct v4l2_rect *__crop;
> + const struct gc08a3_mode *mode;
> +
> + mutex_lock(&gc08a3->mutex);
> +
> + dev_dbg(dev, "---- %s, format(W x H:%d x %d) +", __func__,
> + format->format.width, format->format.height);
> +
> + __crop = __gc08a3_get_pad_crop(gc08a3, sd_state, format->pad,
> + format->which);
> +
> + mode = v4l2_find_nearest_size(gc08a3_modes, ARRAY_SIZE(gc08a3_modes),
> + width, height, format->format.width,
> + format->format.height);
> +
> + dev_dbg(dev, "----nearest mode(W x H:%d x %d)", mode->width,
> + mode->height);
> +
> + __crop->width = mode->width;
> + __crop->height = mode->height;
> +
> + __format = __gc08a3_get_pad_format(gc08a3, sd_state, format->pad,
> + format->which);
> + __format->width = __crop->width;
> + __format->height = __crop->height;
> + __format->code = GC08A3_MBUS_CODE;
> + __format->field = V4L2_FIELD_NONE;
> + __format->colorspace = V4L2_COLORSPACE_SRGB;
> + __format->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(__format->colorspace);
> + __format->quantization =
> + V4L2_MAP_QUANTIZATION_DEFAULT(true,
> + __format->colorspace,
> + __format->ycbcr_enc);
> + __format->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(__format->colorspace);
> +
> + format->format = *__format;
> +
> + gc08a3->cur_mode = mode;
> + gc08a3_update_cur_mode_controls(gc08a3);
> +
> + mutex_unlock(&gc08a3->mutex);
> +
> + return 0;
> +}
> +
> +static int gc08a3_get_selection(struct v4l2_subdev *sd,
> + struct v4l2_subdev_state *sd_state,
> + struct v4l2_subdev_selection *sel)
> +{
> + struct gc08a3 *gc08a3 = to_gc08a3(sd);
> +
> + switch (sel->target) {
> + case V4L2_SEL_TGT_CROP:
> + mutex_lock(&gc08a3->mutex);
> + sel->r = *__gc08a3_get_pad_crop(gc08a3, sd_state, sel->pad, sel->which);
> + mutex_unlock(&gc08a3->mutex);
> + break;
> + case V4L2_SEL_TGT_CROP_BOUNDS:
> + sel->r.top = 0;
> + sel->r.left = 0;
> + sel->r.width = GC08A3_NATIVE_WIDTH;
> + sel->r.height = GC08A3_NATIVE_HEIGHT;
> + break;
> + case V4L2_SEL_TGT_CROP_DEFAULT:
> + if (gc08a3->cur_mode->width == GC08A3_NATIVE_WIDTH) {
> + sel->r.top = 0;
> + sel->r.left = 0;
> + sel->r.width = GC08A3_NATIVE_WIDTH;
> + sel->r.height = GC08A3_NATIVE_HEIGHT;
> + } else {
> + sel->r.top = 0;
> + sel->r.left = 0;
> + sel->r.width = 1920;
> + sel->r.height = 1080;
> + }
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int gc08a3_entity_init_cfg(struct v4l2_subdev *subdev,
> + struct v4l2_subdev_state *sd_state)
> +{
> + struct v4l2_subdev_format fmt = {};
> +
> + fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY :
> + V4L2_SUBDEV_FORMAT_ACTIVE;
> + fmt.format.width = gc08a3_modes[0].width;
> + fmt.format.height = gc08a3_modes[0].height;
> +
> + gc08a3_set_format(subdev, sd_state, &fmt);
> +
> + return 0;
> +}
> +
> +static int gc08a3_set_ctrl_hflip(struct gc08a3 *gc08a3, u32 ctrl_val)
> +{
> + int ret;
> + u32 val;
> +
> + ret = gc08a3_read_reg(gc08a3, GC08A3_FLIP_REG, GC08A3_REG_VALUE_08BIT,
> + &val);
> + if (ret) {
> + dev_err(gc08a3->dev, "read hflip register failed: %d\n", ret);
> + return ret;
> + }
> +
> + val = (ctrl_val) ? (val | GC08A3_FLIP_H_MASK) :
> + (val & ~GC08A3_FLIP_H_MASK);
> + ret = gc08a3_write_reg(gc08a3, GC08A3_FLIP_REG, GC08A3_REG_VALUE_08BIT,
> + val);
> + if (ret < 0)
> + dev_err(gc08a3->dev, "Error %d\n", ret);
> +
> + return ret;
> +}
> +
> +static int gc08a3_set_ctrl_vflip(struct gc08a3 *gc08a3, u32 ctrl_val)
> +{
> + int ret;
> + u32 val;
> +
> + ret = gc08a3_read_reg(gc08a3, GC08A3_FLIP_REG, GC08A3_REG_VALUE_08BIT,
> + &val);
> + if (ret) {
> + dev_err(gc08a3->dev, "read vflip register failed: %d\n", ret);
> + return ret;
> + }
> +
> + val = (ctrl_val) ? (val | GC08A3_FLIP_V_MASK) :
> + (val & ~GC08A3_FLIP_V_MASK);
> + ret = gc08a3_write_reg(gc08a3, GC08A3_FLIP_REG, GC08A3_REG_VALUE_08BIT,
> + val);
> + if (ret < 0)
> + dev_err(gc08a3->dev, "Error %d\n", ret);
> +
> + return ret;
> +}
> +
> +static int gc08a3_test_pattern(struct gc08a3 *gc08a3, u32 pattern_menu)
> +{
> + int ret = 0;
> + u32 pattern = 0;
> +
> + if (pattern_menu) {
> + // write bit16:0x0110 -> color bar
> + switch (pattern_menu) {
> + case 1:
> + pattern = 0x00;
> + break;
> + case 2:
> + case 3:
> + case 4:
> + case 5:
> + case 6:
> + pattern = pattern_menu + 2;
> + break;
> + case 7:
> + pattern = 0x10;
> + break;
> +
> + default:
> + dev_dbg(gc08a3->dev, "invalid pattern menu!\n");
> + break;
> + }
> +
> + ret = gc08a3_write_reg(gc08a3, GC08A3_REG_TEST_PATTERN_EN,
> + GC08A3_REG_VALUE_08BIT,
> + GC08A3_TEST_PATTERN_EN);
> + ret = gc08a3_write_reg(gc08a3, GC08A3_REG_TEST_PATTERN_IDX,
> + GC08A3_REG_VALUE_08BIT, pattern);
> +
> + } else {
> + ret = gc08a3_write_reg(gc08a3, GC08A3_REG_TEST_PATTERN_EN,
> + GC08A3_REG_VALUE_08BIT, 0x00);
> + }
> +
> + return ret;
> +}
> +
> +static int gc08a3_set_ctrl(struct v4l2_ctrl *ctrl)
> +{
> + struct gc08a3 *gc08a3 =
> + container_of(ctrl->handler, struct gc08a3, ctrls);
> + int ret = 0;
> + s64 exposure_max;
> +
> + if (ctrl->id == V4L2_CID_VBLANK) {
> + /* Update max exposure while meeting expected vblanking */
> + exposure_max = gc08a3->cur_mode->height + ctrl->val -
> + GC08A3_EXP_MARGIN;
> + __v4l2_ctrl_modify_range(gc08a3->exposure,
> + gc08a3->exposure->minimum,
> + exposure_max, gc08a3->exposure->step,
> + exposure_max);
> + }
> +
> + /*
> + * Applying V4L2 control value only happens
> + * when power is up for streaming
> + */
> + if (!pm_runtime_get_if_in_use(gc08a3->dev))
> + return 0;
> +
> + switch (ctrl->id) {
> + case V4L2_CID_EXPOSURE:
> + ret = gc08a3_write_reg(gc08a3, GC08A3_EXP_REG,
> + GC08A3_REG_VALUE_16BIT, ctrl->val);
> + break;
> +
> + case V4L2_CID_ANALOGUE_GAIN:
> + ret = gc08a3_write_reg(gc08a3, GC08A3_AGAIN_REG,
> + GC08A3_REG_VALUE_16BIT, ctrl->val);
> + break;
> +
> + case V4L2_CID_VBLANK:
> + dev_info(gc08a3->dev, "V4L2_CID_VBLANK:height:%d, V_BLNK:%d\n",
> + gc08a3->cur_mode->height, ctrl->val);

I would drop this too, or at the very least convert it to dev_dbg().
There should be no message but debug messages printed by the driver to
the kernel log during normal operation.

> + ret = gc08a3_write_reg(gc08a3, GC08A3_FL_REG,
> + GC08A3_REG_VALUE_16BIT,
> + gc08a3->cur_mode->height + ctrl->val);
> + break;
> +
> + case V4L2_CID_HFLIP:
> + gc08a3_set_ctrl_hflip(gc08a3, ctrl->val);
> + break;
> +
> + case V4L2_CID_VFLIP:
> + gc08a3_set_ctrl_vflip(gc08a3, ctrl->val);
> + break;
> +
> + case V4L2_CID_TEST_PATTERN:
> + ret = gc08a3_test_pattern(gc08a3, ctrl->val);
> + break;
> +
> + default:
> + break;
> + }
> +
> + pm_runtime_put(gc08a3->dev);
> +
> + return ret;
> +}
> +
> +static int get_volatile_ext_ctrl(struct v4l2_ctrl *ctrl)
> +{
> + int ret = 0;
> + struct gc08a3 *gc08a3 =
> + container_of(ctrl->handler, struct gc08a3, ctrls);
> + struct i2c_client *client = v4l2_get_subdevdata(&gc08a3->sd);
> +
> + dev_dbg(&client->dev, "---- %s, CMD:0x%x\n", __func__, ctrl->id);
> + switch (ctrl->id) {
> + default:
> + dev_info(&client->dev, "[gc08a3] %s, un-support CMD: 0x%x\n",
> + __func__, ctrl->id);
> + break;
> + }
> +
> + return ret;
> +}
> +
> +static int gc08a3_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
> +{
> + int ret = 0;
> +
> + switch (ctrl->id) {
> + default:
> + ret = get_volatile_ext_ctrl(ctrl);
> + break;
> + }
> +
> + return ret;
> +}
> +
> +static int try_ext_ctrl(struct v4l2_ctrl *ctrl)
> +{
> + int ret = 0;
> + struct gc08a3 *gc08a3 =
> + container_of(ctrl->handler, struct gc08a3, ctrls);
> + struct i2c_client *client = v4l2_get_subdevdata(&gc08a3->sd);
> +
> + switch (ctrl->id) {
> + default:
> + dev_dbg(&client->dev,
> + "[gc08a3] un-handle CMD: 0x%x (%s : %d)\n", ctrl->id,
> + __func__, __LINE__);
> + break;
> + }
> +
> + return ret;
> +}
> +
> +static int gc08a3_try_ctrl(struct v4l2_ctrl *ctrl)
> +{
> + int ret = 0;
> +
> + switch (ctrl->id) {
> + case V4L2_CID_EXPOSURE:
> + case V4L2_CID_ANALOGUE_GAIN:
> + case V4L2_CID_VBLANK:
> + case V4L2_CID_HFLIP:
> + case V4L2_CID_VFLIP:
> + case V4L2_CID_TEST_PATTERN:
> + return 0;
> +
> + default:
> + ret = try_ext_ctrl(ctrl);
> + break;
> + }
> +
> + return ret;
> +}
> +
> +static const struct v4l2_ctrl_ops gc08a3_ctrl_ops = {
> + .g_volatile_ctrl = gc08a3_g_volatile_ctrl,
> + .try_ctrl = gc08a3_try_ctrl,

Drop the .g_volatile_ctrl() and .try_ctrl() implementations, they don't
do anything, and the operations are optional.

> + .s_ctrl = gc08a3_set_ctrl,
> +};
> +
> +static int gc08a3_start_streaming(struct gc08a3 *gc08a3)
> +{
> + const struct gc08a3_mode *mode;
> + const struct gc08a3_reg_list *reg_list;
> + int link_freq_index;
> + int ret;
> +
> + dev_info(gc08a3->dev, "%s ++\n", __func__);
> +
> + mutex_lock(&gc08a3->mutex);
> +
> + link_freq_index = gc08a3->cur_mode->link_freq_index;
> + dev_info(gc08a3->dev, "----link_freq_index = %d ", link_freq_index);
> +
> + reg_list = &link_freq_configs[link_freq_index].reg_list;
> + ret = gc08a3_write_reg_list(gc08a3, reg_list);
> + if (ret) {
> + dev_err(gc08a3->dev, "could not sent common table %d\n", ret);
> + goto error;
> + }
> +
> + mode = gc08a3->cur_mode;
> + dev_info(gc08a3->dev, "----write regtbl: mode(id:%d, WxH:%dx%d)",
> + mode->mode_id, mode->width, mode->height);
> + reg_list = &mode->reg_list;
> +
> + ret = gc08a3_write_reg_list(gc08a3, reg_list);
> + if (ret < 0) {
> + dev_err(gc08a3->dev, "could not sent mode table %d\n", ret);
> + goto error;
> + }
> + ret = __v4l2_ctrl_handler_setup(&gc08a3->ctrls);
> + if (ret < 0) {
> + dev_err(gc08a3->dev, "could not sync v4l2 controls\n");
> + goto error;
> + }
> +
> + ret = gc08a3_write_reg(gc08a3, GC08A3_STRAEMING_REG,
> + GC08A3_REG_VALUE_08BIT, 1);
> + if (ret < 0) {
> + dev_err(gc08a3->dev, "write STRAEMING_REG failed: %d\n", ret);
> + goto error;
> + }
> +
> + mutex_unlock(&gc08a3->mutex);
> +
> + dev_info(gc08a3->dev, "%s --\n", __func__);
> +
> + return 0;
> +
> +error:
> + mutex_unlock(&gc08a3->mutex);
> + return ret;
> +}
> +
> +static int gc08a3_stop_streaming(struct gc08a3 *gc08a3)
> +{
> + int ret;
> +
> + dev_info(gc08a3->dev, "%s ++\n", __func__);
> +
> + ret = gc08a3_write_reg(gc08a3, GC08A3_STRAEMING_REG,
> + GC08A3_REG_VALUE_08BIT, 0);
> + if (ret < 0)
> + dev_err(gc08a3->dev, "could not sent stop streaming %d\n", ret);
> +
> + dev_info(gc08a3->dev, "%s --\n", __func__);
> +
> + return ret;
> +}
> +
> +static int gc08a3_s_stream(struct v4l2_subdev *subdev, int enable)
> +{
> + struct gc08a3 *gc08a3 = to_gc08a3(subdev);
> + int ret;
> +
> + if (gc08a3->streaming == enable)
> + return 0;
> +
> + if (enable) {
> + ret = pm_runtime_resume_and_get(gc08a3->dev);
> + if (ret < 0)
> + return ret;
> +
> + ret = gc08a3_start_streaming(gc08a3);
> + if (ret < 0)
> + goto err_rpm_put;
> + } else {
> + ret = gc08a3_stop_streaming(gc08a3);
> + if (ret < 0)
> + goto err_rpm_put;
> + pm_runtime_put(gc08a3->dev);
> + }
> +
> + gc08a3->streaming = enable;
> + return 0;
> +
> +err_rpm_put:
> + pm_runtime_put(gc08a3->dev);
> + return ret;
> +}
> +
> +static int gc08a3_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad,
> + struct v4l2_mbus_config *config)
> +{
> + config->type = V4L2_MBUS_CSI2_DPHY;
> + config->bus.mipi_csi2.num_data_lanes = 4;
> + config->bus.mipi_csi2.flags = 0;
> + return 0;
> +}
> +
> +static int gc08a3_g_frame_interval(struct v4l2_subdev *subdev,
> + struct v4l2_subdev_frame_interval *fival)
> +{
> + struct gc08a3 *gc08a3 = to_gc08a3(subdev);
> +
> + fival->interval.numerator = 1;
> + fival->interval.denominator = gc08a3->cur_mode->max_framerate / 10;
> +
> + return 0;
> +}
> +
> +static int
> +gc08a3_enum_frame_interval(struct v4l2_subdev *subdev,
> + struct v4l2_subdev_state *sd_state,
> + struct v4l2_subdev_frame_interval_enum *fie)
> +{
> + const struct gc08a3_mode *mode;
> +
> + if (fie->index != 0)
> + return -EINVAL;
> +
> + mode = v4l2_find_nearest_size(gc08a3_modes, ARRAY_SIZE(gc08a3_modes),
> + width, height, fie->width, fie->height);
> +
> + fie->code = GC08A3_MBUS_CODE;
> + fie->width = mode->width;
> + fie->height = mode->height;
> + fie->interval.numerator = 1;
> + fie->interval.denominator = mode->max_framerate / 10;
> +
> + return 0;
> +}
> +
> +static const struct v4l2_subdev_video_ops gc08a3_video_ops = {
> + .s_stream = gc08a3_s_stream,
> + .g_frame_interval = gc08a3_g_frame_interval,
> + .s_frame_interval = gc08a3_g_frame_interval,
> +};
> +
> +static const struct v4l2_subdev_pad_ops gc08a3_subdev_pad_ops = {
> + .enum_mbus_code = gc08a3_enum_mbus_code,
> + .enum_frame_size = gc08a3_enum_frame_size,
> + .enum_frame_interval = gc08a3_enum_frame_interval,
> + .get_fmt = gc08a3_get_format,
> + .set_fmt = gc08a3_set_format,
> + .get_selection = gc08a3_get_selection,
> + .init_cfg = gc08a3_entity_init_cfg,
> + .get_mbus_config = gc08a3_g_mbus_config,
> +};
> +
> +static const struct v4l2_subdev_ops gc08a3_subdev_ops = {
> + .core = &gc08a3_core_ops,
> + .video = &gc08a3_video_ops,
> + .pad = &gc08a3_subdev_pad_ops,
> +};
> +
> +static const struct regmap_config sensor_regmap_config = {
> + .reg_bits = 16,
> + .val_bits = 8,
> + .cache_type = REGCACHE_RBTREE,
> +};
> +
> +static int gc08a3_get_regulators(struct device *dev, struct gc08a3 *gc08a3)
> +{
> + unsigned int i;
> +
> + for (i = 0; i < GC08A3_NUM_SUPPLIES; i++)
> + gc08a3->supplies[i].supply = gc08a3_supply_name[i];
> +
> + return devm_regulator_bulk_get(dev, GC08A3_NUM_SUPPLIES,
> + gc08a3->supplies);
> +}
> +
> +static int gc08a3_parse_fwnode(struct device *dev)
> +{
> + struct fwnode_handle *endpoint;
> + struct v4l2_fwnode_endpoint bus_cfg = {
> + .bus_type = V4L2_MBUS_CSI2_DPHY,
> + };
> + unsigned int i, j;
> + int ret;
> +
> + endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
> + if (!endpoint) {
> + dev_err(dev, "endpoint node not found\n");
> + return -EINVAL;
> + }
> +
> + ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg);
> + if (ret) {
> + dev_err(dev, "parsing endpoint node failed\n");
> + goto done;
> + }
> +
> + if (!bus_cfg.nr_of_link_frequencies) {
> + dev_err(dev, "no link frequencies defined");
> + ret = -EINVAL;
> + goto done;
> + }
> +
> + for (i = 0; i < ARRAY_SIZE(link_freq_menu_items); i++) {
> + for (j = 0; j < bus_cfg.nr_of_link_frequencies; j++) {
> + if (link_freq_menu_items[i] ==
> + bus_cfg.link_frequencies[j])
> + break;
> + }
> +
> + if (j == bus_cfg.nr_of_link_frequencies) {
> + dev_err(dev,
> + "no link frequency %lld supported, please check DT",
> + link_freq_menu_items[i]);
> + ret = -EINVAL;
> + goto done;
> + }
> + }
> +
> +done:
> + v4l2_fwnode_endpoint_free(&bus_cfg);
> + fwnode_handle_put(endpoint);
> + return ret;
> +}
> +
> +static int __maybe_unused gc08a3_suspend(struct device *dev)
> +{
> + struct i2c_client *client = to_i2c_client(dev);
> + struct v4l2_subdev *sd = i2c_get_clientdata(client);
> + struct gc08a3 *gc08a3 = to_gc08a3(sd);
> +
> + if (gc08a3->streaming)
> + gc08a3_stop_streaming(gc08a3);
> +
> + return 0;
> +}
> +
> +static int __maybe_unused gc08a3_resume(struct device *dev)
> +{
> + struct i2c_client *client = to_i2c_client(dev);
> + struct v4l2_subdev *sd = i2c_get_clientdata(client);
> + struct gc08a3 *gc08a3 = to_gc08a3(sd);
> + int ret;
> +
> + if (gc08a3->streaming) {
> + ret = gc08a3_start_streaming(gc08a3);
> + if (ret)
> + goto error;
> + }
> +
> + return 0;
> +
> +error:
> + gc08a3_stop_streaming(gc08a3);
> + gc08a3->streaming = 0;
> + return ret;
> +}

No need for system suspend/resume operations. Please read
Documentation/driver-api/media/camera-sensor.rst in the master branch of
git://linuxtv.org/media_stage.git for an explanation.

> +
> +static int gc08a3_init_controls(struct gc08a3 *gc08a3)
> +{
> + struct i2c_client *client = v4l2_get_subdevdata(&gc08a3->sd);
> + const struct v4l2_ctrl_ops *ops = &gc08a3_ctrl_ops;
> + struct v4l2_fwnode_device_properties props;
> + struct v4l2_ctrl_handler *ctrl_hdlr;
> + s64 exposure_max, h_blank;
> + int ret;
> +
> + ctrl_hdlr = &gc08a3->ctrls;
> + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 10);
> + if (ret)
> + return ret;
> +
> + ctrl_hdlr->lock = &gc08a3->mutex;
> +
> + gc08a3->link_freq =
> + v4l2_ctrl_new_int_menu(ctrl_hdlr,
> + &gc08a3_ctrl_ops,
> + V4L2_CID_LINK_FREQ,
> + ARRAY_SIZE(link_freq_menu_items) - 1,
> + 0, link_freq_menu_items);
> + if (gc08a3->link_freq)
> + gc08a3->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> +
> + gc08a3->pixel_rate =
> + v4l2_ctrl_new_std(ctrl_hdlr,
> + &gc08a3_ctrl_ops,
> + V4L2_CID_PIXEL_RATE, 0,
> + to_pixel_rate(GC08A3_LINK_FREQ_336MHZ_CFG), 1,
> + to_pixel_rate(GC08A3_LINK_FREQ_336MHZ_CFG));
> +
> + gc08a3->vblank =
> + v4l2_ctrl_new_std(ctrl_hdlr,
> + &gc08a3_ctrl_ops, V4L2_CID_VBLANK,
> + gc08a3->cur_mode->vts_min - gc08a3->cur_mode->height,
> + GC08A3_VTS_MAX - gc08a3->cur_mode->height, 1,
> + gc08a3->cur_mode->vts_def - gc08a3->cur_mode->height);
> +
> + h_blank = gc08a3->cur_mode->hts - gc08a3->cur_mode->width;
> + gc08a3->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &gc08a3_ctrl_ops,
> + V4L2_CID_HBLANK, h_blank, h_blank, 1,
> + h_blank);
> + if (gc08a3->hblank)
> + gc08a3->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> +
> + v4l2_ctrl_new_std(ctrl_hdlr, &gc08a3_ctrl_ops,
> + V4L2_CID_ANALOGUE_GAIN, GC08A3_AGAIN_MIN,
> + GC08A3_AGAIN_MAX, GC08A3_AGAIN_STEP,
> + GC08A3_AGAIN_MIN);
> +
> + exposure_max = gc08a3->cur_mode->vts_def - GC08A3_EXP_MARGIN;
> + gc08a3->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &gc08a3_ctrl_ops,
> + V4L2_CID_EXPOSURE, GC08A3_EXP_MIN,
> + exposure_max, GC08A3_EXP_STEP,
> + exposure_max);
> +
> + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &gc08a3_ctrl_ops,
> + V4L2_CID_TEST_PATTERN,
> + ARRAY_SIZE(gc08a3_test_pattern_menu) - 1,
> + 0, 0, gc08a3_test_pattern_menu);
> +
> + v4l2_ctrl_new_std(ctrl_hdlr, &gc08a3_ctrl_ops, V4L2_CID_HFLIP, 0,
> + 1, 1, 0);
> +
> + v4l2_ctrl_new_std(ctrl_hdlr, &gc08a3_ctrl_ops, V4L2_CID_VFLIP, 0,
> + 1, 1, 0);
> +
> + /* register properties to fwnode (e.g. rotation, orientation) */
> + ret = v4l2_fwnode_device_parse(&client->dev, &props);
> + if (ret)
> + goto error_ctrls;
> +
> + ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, ops, &props);
> + if (ret)
> + goto error_ctrls;
> +
> + if (ctrl_hdlr->error) {
> + ret = ctrl_hdlr->error;
> + goto error_ctrls;
> + }
> +
> + gc08a3->sd.ctrl_handler = ctrl_hdlr;
> +
> + return 0;
> +
> +error_ctrls:
> + v4l2_ctrl_handler_free(ctrl_hdlr);
> +
> + return ret;
> +}
> +
> +static int gc08a3_probe(struct i2c_client *client)
> +{
> + struct device *dev = &client->dev;
> + struct gc08a3 *gc08a3;
> + int ret;
> +
> + dev_info(dev, "--- %s +", __func__);
> +
> + ret = gc08a3_parse_fwnode(dev);
> + if (ret)
> + return ret;
> +
> + gc08a3 = devm_kzalloc(dev, sizeof(*gc08a3), GFP_KERNEL);
> + if (!gc08a3)
> + return -ENOMEM;
> +
> + gc08a3->dev = dev;
> +
> + gc08a3->xclk = devm_clk_get(dev, NULL);
> + if (IS_ERR(gc08a3->xclk)) {
> + dev_err(dev, "could not get xclk\n");
> + return PTR_ERR(gc08a3->xclk);
> + }
> +
> + ret = clk_set_rate(gc08a3->xclk, GC08A3_DEFAULT_CLK_FREQ);
> + if (ret) {
> + dev_err(dev, "could not set xclk frequency\n");
> + return ret;
> + }
> +
> + ret = gc08a3_get_regulators(dev, gc08a3);
> + if (ret < 0) {
> + dev_err(dev, "cannot get regulators\n");
> + return ret;
> + }
> +
> + gc08a3->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
> + if (IS_ERR(gc08a3->enable_gpio)) {
> + dev_err(dev, "cannot get enable gpio\n");
> + return PTR_ERR(gc08a3->enable_gpio);
> + }
> +
> + gc08a3->regmap = devm_regmap_init_i2c(client, &sensor_regmap_config);
> + if (IS_ERR(gc08a3->regmap)) {
> + dev_err(dev, "regmap init failed\n");
> + return PTR_ERR(gc08a3->regmap);
> + }
> +
> + v4l2_i2c_subdev_init(&gc08a3->sd, client, &gc08a3_subdev_ops);
> +
> + gc08a3_power_on(gc08a3->dev);
> +
> + ret = gc08a3_identify_module(gc08a3);
> + if (ret) {
> + dev_err(&client->dev, "failed to find sensor: %d\n", ret);
> + gc08a3_power_off(gc08a3->dev);
> + return ret;
> + }
> +
> + mutex_init(&gc08a3->mutex);
> + gc08a3->cur_mode = &gc08a3_modes[0];
> +
> + ret = gc08a3_init_controls(gc08a3);
> + if (ret) {
> + dev_err(&client->dev, "failed to init controls: %d", ret);
> + goto free_ctrl;
> + }
> +
> + gc08a3->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> + gc08a3->pad.flags = MEDIA_PAD_FL_SOURCE;
> + gc08a3->sd.dev = &client->dev;
> + gc08a3->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
> +
> + dev_dbg(&client->dev, "gc08a3->sd.name: %s, dev->of_node->name: %s\n",
> + gc08a3->sd.name, dev->of_node->name);
> + if (V4L2_SUBDEV_NAME_SIZE - strlen(gc08a3->sd.name) - 2 <
> + strlen(dev->of_node->name)) {
> + dev_err(&client->dev,
> + "the string length of (sd.name + of_node->name) is too long.\n");
> + return -EINVAL;
> + }
> + strncat(gc08a3->sd.name, " ", 1);
> + strncat(gc08a3->sd.name, dev->of_node->name,
> + V4L2_SUBDEV_NAME_SIZE - strlen(gc08a3->sd.name) - 2);
> + dev_dbg(&client->dev, "after: gc08a3->sd.name: %s\n", gc08a3->sd.name);
> +
> + ret = media_entity_pads_init(&gc08a3->sd.entity, 1, &gc08a3->pad);
> + if (ret < 0) {
> + dev_err(dev, "could not register media entity\n");
> + goto free_ctrl;
> + }
> +
> + ret = v4l2_async_register_subdev_sensor(&gc08a3->sd);
> + if (ret < 0) {
> + dev_err(dev, "could not register v4l2 device\n");
> + goto free_entity;
> + }
> +
> + pm_runtime_set_active(gc08a3->dev);
> + pm_runtime_enable(gc08a3->dev);
> + pm_runtime_idle(gc08a3->dev);

Please see the imx219 driver for an example of how to correctly
implement runtime PM support. Please also use PM runtime autosuspend.

> +
> + dev_info(dev, "--- %s -", __func__);
> +
> + return 0;
> +
> +free_entity:
> + media_entity_cleanup(&gc08a3->sd.entity);
> +free_ctrl:
> + mutex_destroy(&gc08a3->mutex);
> + v4l2_ctrl_handler_free(&gc08a3->ctrls);
> + pm_runtime_disable(gc08a3->dev);
> +
> + return ret;
> +}
> +
> +static void gc08a3_remove(struct i2c_client *client)
> +{
> + struct v4l2_subdev *sd = i2c_get_clientdata(client);
> + struct gc08a3 *gc08a3 = to_gc08a3(sd);
> +
> + v4l2_async_unregister_subdev(&gc08a3->sd);
> + media_entity_cleanup(&gc08a3->sd.entity);
> + v4l2_ctrl_handler_free(&gc08a3->ctrls);
> +
> + pm_runtime_disable(&client->dev);
> + pm_runtime_set_suspended(&client->dev);
> +
> + mutex_destroy(&gc08a3->mutex);
> +}
> +
> +static const struct of_device_id gc08a3_of_match[] = {
> + { .compatible = "GalaxyCore,gc08a3" },
> + {}
> +};
> +MODULE_DEVICE_TABLE(of, gc08a3_of_match);
> +
> +static const struct dev_pm_ops gc08a3_pm_ops = {
> + SET_SYSTEM_SLEEP_PM_OPS(gc08a3_suspend, gc08a3_resume)
> + SET_RUNTIME_PM_OPS(gc08a3_power_off, gc08a3_power_on, NULL)
> +};
> +
> +static struct i2c_driver gc08a3_i2c_driver = {
> + .driver = {
> + .of_match_table = gc08a3_of_match,
> + .pm = &gc08a3_pm_ops,
> + .name = "gc08a3",
> + },
> + .probe_new = gc08a3_probe,
> + .remove = gc08a3_remove,
> +};
> +
> +module_i2c_driver(gc08a3_i2c_driver);
> +
> +MODULE_DESCRIPTION("GalaxyCore gc08a3 Camera driver");
> +MODULE_AUTHOR("Zhi Mao <[email protected]>");
> +MODULE_LICENSE("GPL");

--
Regards,

Laurent Pinchart

2024-01-09 09:11:59

by Zhi Mao (毛智)

[permalink] [raw]
Subject: Re: [PATCH 1/2] media: dt-bindings: media: i2c: Document GC08A3 bindings

On Thu, 2023-12-07 at 12:25 +0200, Laurent Pinchart wrote:
>
> External email : Please do not click links or open attachments until
> you have verified the sender or the content.
> On Thu, Nov 23, 2023 at 05:31:35PM +0000, Conor Dooley wrote:
> > On Thu, Nov 23, 2023 at 07:51:03PM +0800, Zhi Mao wrote:
> > > Add YAML device tree binding for GC08A3 CMOS image sensor,
> > > and the relevant MAINTAINERS entries.
> > >
> > > Signed-off-by: Zhi Mao <[email protected]>
> >
> > Please test your bindings.
> >
> > > ---
> > > .../bindings/media/i2c/galaxycore,gc08a3.yaml | 128
> ++++++++++++++++++
> > > .../devicetree/bindings/vendor-prefixes.yaml | 2 +
> > > 2 files changed, 130 insertions(+)
> > > create mode 100644
> Documentation/devicetree/bindings/media/i2c/galaxycore,gc08a3.yaml
> > >
> > > diff --git
> a/Documentation/devicetree/bindings/media/i2c/galaxycore,gc08a3.yaml
> b/Documentation/devicetree/bindings/media/i2c/galaxycore,gc08a3.yaml
> > > new file mode 100644
> > > index 000000000000..089ea321da91
> > > --- /dev/null
> > > +++
> b/Documentation/devicetree/bindings/media/i2c/galaxycore,gc08a3.yaml
> > > @@ -0,0 +1,128 @@
> > > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > > +%YAML 1.2
> > > +---
> > > +$id:
> http://devicetree.org/schemas/media/i2c/galaxycore,gc08a3.yaml#
> > > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > > +
> > > +title: GalaxyCore gc08a3 1/4" 8M Pixel MIPI CSI-2 sensor
> > > +
> > > +maintainers:
> > > + - Zhi Mao <[email protected]>
> > > +
> > > +description: |-
> >
> > The |- is not needed, you have no formatting to preserve.
> >
> > > + The gc08a3 is a raw image sensor with an MIPI CSI-2 image data
> > > + interface and CCI (I2C compatible) control bus. The output
> format
> > > + is raw Bayer.
> > > +
> > > +properties:
> > > + compatible:
> > > + const: GalaxyCore,gc08a3
> >
> > Please remove the capitals.
> >
> > > +
> > > + reg:
> > > + maxItems: 1
> > > +
> > > + clocks:
> > > + items:
> > > + - description: Reference to the mclk clock.
> >
> > Pointless, just use maxItems: 1.
> >
> > > +
> > > + assigned-clocks:
> > > + maxItems: 1
> > > +
> > > + assigned-clock-rates:
> > > + maxItems: 1
> > > +
> > > + enable-gpios:
> > > + description: Reference to the GPIO connected to the RESETB
> pin. Active low.
> > > + maxItems: 1
> > > +
>
> If that's the RESETB pin, it should be reset-gpios.
[mtk]: yes, it's reset-pin, fixed in patch:v3.
and some other comments also fixed, please review patch:v3.

https://lore.kernel.org/linux-media/[email protected]/
>
> > > + vddio-supply:
> > > + description: Definition of the regulator used for the VDDIO
> power supply.
> > > +
> > > + vdda-supply:
> > > + description: Definition of the regulator used for the VDDA
> power supply.
> > > +
> > > + vddd-supply:
> > > + description: Definition of the regulator used for the VDDD
> power supply.
> >
> > These descriptions can all be replaced with "foo-supply: true" IMO.
> >
> > > + port:
> > > + $ref: /schemas/graph.yaml#/$defs/port-base
> > > + unevaluatedProperties: false
> > > +
> > > + properties:
> > > + endpoint:
> > > + $ref: /schemas/media/video-interfaces.yaml#
> > > + unevaluatedProperties: false
> > > +
> > > + properties:
> > > + data-lanes:
> > > + oneOf:
> > > + - items:
> > > + - const: 1
> > > + - const: 2
> > > + - const: 3
> > > + - const: 4
> > > + - items:
> > > + - const: 1
> > > + - const: 2
> > > +
> > > + link-frequencies: true
> > > +
> > > + required:
> > > + - data-lanes
> > > + - link-frequencies
> > > +
> > > +required:
> > > + - compatible
> > > + - reg
> > > + - clocks
> >
> > > + - assigned-clocks
> > > + - assigned-clock-rates
> >
> > Why are these required?
> >
> > > + - vddio-supply
> > > + - vdda-supply
> > > + - vddd-supply
> > > + - port
> > > +
> > > +additionalProperties: false
> > > +
> > > +examples:
> > > + - |
> > > + #include <dt-bindings/gpio/gpio.h>
> > > +
> > > + i2c {
> > > + #address-cells = <1>;
> > > + #size-cells = <0>;
> > > +
> > > + sensor0@2 {
> > > + status = "okay";
> > > + compatible = "GalaxyCore,gc08a3";
> > > + reg = <0x31>;
> > > +
> > > + clocks = <&topckgen CLK_TOP_CAMTG>,
> > > + <&topckgen CLK_TOP_UNIVPLL_192M_D8>;
> > > + clock-names = "xvclk", "freq_mux";
> > > + clock-frequency = <24000000>;
> > > +
> > > + assigned-clocks = <&topckgen CLK_TOP_CAMTG>,
> > > + <&topckgen CLK_TOP_UNIVPLL_192M_D8>;
> > > + assigned-clock-parents = <&topckgen
> CLK_TOP_UNIVPLL_192M_D8>;
> > > + assigned-clock-rates = <0>, <24000000>;
> > > +
> > > + enable-gpios = <&pio 19 GPIO_ACTIVE_HIGH>;
> > > +
> > > + pinctrl-names = "default";
> > > + pinctrl-0 = <&camera_pins_cam0>;
> > > +
> > > + avdd-supply = <&mt6359_vfe28_ldo_reg>;
> > > +
> > > + port {
> > > + sensor0_out_2: endpoint {
> > > + data-lanes = <1 2 3 4>;
> > > + link-frequencies = /bits/ 64 <336000000
> 207000000>;
> > > + remote-endpoint = <&seninf_csi_port_0_in_2>;
> > > + };
> > > + };
> > > + };
> > > +
> > > + };
> > > +
> > > +...
> > > diff --git a/Documentation/devicetree/bindings/vendor-
> prefixes.yaml b/Documentation/devicetree/bindings/vendor-
> prefixes.yaml
> > > index 309b94c328c8..a0bbec0bfee2 100644
> > > --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
> > > +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
> > > @@ -502,6 +502,8 @@ patternProperties:
> > > description: Fujitsu Ltd.
> > > "^fxtec,.*":
> > > description: FX Technology Ltd.
> > > + "^GalaxyCore,.*":
> > > + description: GalaxyCore Inc.
> > > "^gardena,.*":
> > > description: GARDENA GmbH
> > > "^gateway,.*":
>
> --
> Regards,
>
> Laurent Pinchart

2024-01-09 09:20:20

by Laurent Pinchart

[permalink] [raw]
Subject: Re: [PATCH 1/2] media: dt-bindings: media: i2c: Document GC08A3 bindings

Hi Zhi,

Your e-mail still came as HTML, with the same footer. Please fix this
first, I'm afraid we can't communicate properly on mailing lists
otherwise.

On Tue, Jan 09, 2024 at 09:11:32AM +0000, Zhi Mao (毛智) wrote:
> On Thu, 2023-12-07 at 12:25 +0200, Laurent Pinchart wrote:
> >
> > External email : Please do not click links or open attachments until
> > you have verified the sender or the content.
> > On Thu, Nov 23, 2023 at 05:31:35PM +0000, Conor Dooley wrote:
> > > On Thu, Nov 23, 2023 at 07:51:03PM +0800, Zhi Mao wrote:
> > > > Add YAML device tree binding for GC08A3 CMOS image sensor,
> > > > and the relevant MAINTAINERS entries.
> > > >
> > > > Signed-off-by: Zhi Mao <[email protected]>
> > >
> > > Please test your bindings.
> > >
> > > > ---
> > > > .../bindings/media/i2c/galaxycore,gc08a3.yaml | 128
> > ++++++++++++++++++
> > > > .../devicetree/bindings/vendor-prefixes.yaml | 2 +
> > > > 2 files changed, 130 insertions(+)
> > > > create mode 100644
> > Documentation/devicetree/bindings/media/i2c/galaxycore,gc08a3.yaml
> > > >
> > > > diff --git
> > a/Documentation/devicetree/bindings/media/i2c/galaxycore,gc08a3.yaml
> > b/Documentation/devicetree/bindings/media/i2c/galaxycore,gc08a3.yaml
> > > > new file mode 100644
> > > > index 000000000000..089ea321da91
> > > > --- /dev/null
> > > > +++
> > b/Documentation/devicetree/bindings/media/i2c/galaxycore,gc08a3.yaml
> > > > @@ -0,0 +1,128 @@
> > > > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > > > +%YAML 1.2
> > > > +---
> > > > +$id:
> > http://devicetree.org/schemas/media/i2c/galaxycore,gc08a3.yaml#
> > > > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > > > +
> > > > +title: GalaxyCore gc08a3 1/4" 8M Pixel MIPI CSI-2 sensor
> > > > +
> > > > +maintainers:
> > > > + - Zhi Mao <[email protected]>
> > > > +
> > > > +description: |-
> > >
> > > The |- is not needed, you have no formatting to preserve.
> > >
> > > > + The gc08a3 is a raw image sensor with an MIPI CSI-2 image data
> > > > + interface and CCI (I2C compatible) control bus. The output
> > format
> > > > + is raw Bayer.
> > > > +
> > > > +properties:
> > > > + compatible:
> > > > + const: GalaxyCore,gc08a3
> > >
> > > Please remove the capitals.
> > >
> > > > +
> > > > + reg:
> > > > + maxItems: 1
> > > > +
> > > > + clocks:
> > > > + items:
> > > > + - description: Reference to the mclk clock.
> > >
> > > Pointless, just use maxItems: 1.
> > >
> > > > +
> > > > + assigned-clocks:
> > > > + maxItems: 1
> > > > +
> > > > + assigned-clock-rates:
> > > > + maxItems: 1
> > > > +
> > > > + enable-gpios:
> > > > + description: Reference to the GPIO connected to the RESETB
> > pin. Active low.
> > > > + maxItems: 1
> > > > +
> >
> > If that's the RESETB pin, it should be reset-gpios.
> [mtk]: yes, it's reset-pin, fixed in patch:v3.
> and some other comments also fixed, please review patch:v3.
>
> https://lore.kernel.org/linux-media/[email protected]/
> >
> > > > + vddio-supply:
> > > > + description: Definition of the regulator used for the VDDIO
> > power supply.
> > > > +
> > > > + vdda-supply:
> > > > + description: Definition of the regulator used for the VDDA
> > power supply.
> > > > +
> > > > + vddd-supply:
> > > > + description: Definition of the regulator used for the VDDD
> > power supply.
> > >
> > > These descriptions can all be replaced with "foo-supply: true" IMO.
> > >
> > > > + port:
> > > > + $ref: /schemas/graph.yaml#/$defs/port-base
> > > > + unevaluatedProperties: false
> > > > +
> > > > + properties:
> > > > + endpoint:
> > > > + $ref: /schemas/media/video-interfaces.yaml#
> > > > + unevaluatedProperties: false
> > > > +
> > > > + properties:
> > > > + data-lanes:
> > > > + oneOf:
> > > > + - items:
> > > > + - const: 1
> > > > + - const: 2
> > > > + - const: 3
> > > > + - const: 4
> > > > + - items:
> > > > + - const: 1
> > > > + - const: 2
> > > > +
> > > > + link-frequencies: true
> > > > +
> > > > + required:
> > > > + - data-lanes
> > > > + - link-frequencies
> > > > +
> > > > +required:
> > > > + - compatible
> > > > + - reg
> > > > + - clocks
> > >
> > > > + - assigned-clocks
> > > > + - assigned-clock-rates
> > >
> > > Why are these required?
> > >
> > > > + - vddio-supply
> > > > + - vdda-supply
> > > > + - vddd-supply
> > > > + - port
> > > > +
> > > > +additionalProperties: false
> > > > +
> > > > +examples:
> > > > + - |
> > > > + #include <dt-bindings/gpio/gpio.h>
> > > > +
> > > > + i2c {
> > > > + #address-cells = <1>;
> > > > + #size-cells = <0>;
> > > > +
> > > > + sensor0@2 {
> > > > + status = "okay";
> > > > + compatible = "GalaxyCore,gc08a3";
> > > > + reg = <0x31>;
> > > > +
> > > > + clocks = <&topckgen CLK_TOP_CAMTG>,
> > > > + <&topckgen CLK_TOP_UNIVPLL_192M_D8>;
> > > > + clock-names = "xvclk", "freq_mux";
> > > > + clock-frequency = <24000000>;
> > > > +
> > > > + assigned-clocks = <&topckgen CLK_TOP_CAMTG>,
> > > > + <&topckgen CLK_TOP_UNIVPLL_192M_D8>;
> > > > + assigned-clock-parents = <&topckgen
> > CLK_TOP_UNIVPLL_192M_D8>;
> > > > + assigned-clock-rates = <0>, <24000000>;
> > > > +
> > > > + enable-gpios = <&pio 19 GPIO_ACTIVE_HIGH>;
> > > > +
> > > > + pinctrl-names = "default";
> > > > + pinctrl-0 = <&camera_pins_cam0>;
> > > > +
> > > > + avdd-supply = <&mt6359_vfe28_ldo_reg>;
> > > > +
> > > > + port {
> > > > + sensor0_out_2: endpoint {
> > > > + data-lanes = <1 2 3 4>;
> > > > + link-frequencies = /bits/ 64 <336000000
> > 207000000>;
> > > > + remote-endpoint = <&seninf_csi_port_0_in_2>;
> > > > + };
> > > > + };
> > > > + };
> > > > +
> > > > + };
> > > > +
> > > > +...
> > > > diff --git a/Documentation/devicetree/bindings/vendor-
> > prefixes.yaml b/Documentation/devicetree/bindings/vendor-
> > prefixes.yaml
> > > > index 309b94c328c8..a0bbec0bfee2 100644
> > > > --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
> > > > +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
> > > > @@ -502,6 +502,8 @@ patternProperties:
> > > > description: Fujitsu Ltd.
> > > > "^fxtec,.*":
> > > > description: FX Technology Ltd.
> > > > + "^GalaxyCore,.*":
> > > > + description: GalaxyCore Inc.
> > > > "^gardena,.*":
> > > > description: GARDENA GmbH
> > > > "^gateway,.*":
> >
> > --
> > Regards,
> >
> > Laurent Pinchart
>
>
> ************* MEDIATEK Confidentiality Notice ********************
> The information contained in this e-mail message (including any
> attachments) may be confidential, proprietary, privileged, or otherwise
> exempt from disclosure under applicable laws. It is intended to be
> conveyed only to the designated recipient(s). Any use, dissemination,
> distribution, printing, retaining or copying of this e-mail (including its
> attachments) by unintended recipient(s) is strictly prohibited and may
> be unlawful. If you are not an intended recipient of this e-mail, or believe
> that you have received this e-mail in error, please notify the sender
> immediately (by replying to this e-mail), delete any and all copies of
> this e-mail (including any attachments) from your system, and do not
> disclose the content of this e-mail to any other person. Thank you!
>

--
Regards,

Laurent Pinchart

2024-01-09 09:26:40

by Zhi Mao (毛智)

[permalink] [raw]
Subject: Re: [PATCH 2/2] media: i2c: Add GC08A3 image sensor driver

On Thu, 2023-12-07 at 13:06 +0200, Laurent Pinchart wrote:
>
> External email : Please do not click links or open attachments until
> you have verified the sender or the content.
> Hi Zhi Mao,
>
> Thank you for the patch.
>
> This is a partial review, as lots of things will change already for
> v2.
> I'll review the next version in more details.
>
> First of all, please rebase the driver on top of the master branch of
> git://linuxtv.org/media_stage.git. Quite a few in-kernel APIs have
> changed.
>
> On Thu, Nov 23, 2023 at 07:51:04PM +0800, Zhi Mao wrote:
> > Add a V4L2 sub-device driver for Galaxycore GC08A3 image sensor.
> >
> > Signed-off-by: Zhi Mao <[email protected]>
> > ---
> > drivers/media/i2c/Kconfig | 14 +
> > drivers/media/i2c/Makefile | 1 +
> > drivers/media/i2c/gc08a3.c | 2046
> ++++++++++++++++++++++++++++++++++++
> > 3 files changed, 2061 insertions(+)
> > create mode 100644 drivers/media/i2c/gc08a3.c
> >
> > diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
> > index 59ee0ca2c978..2e37be537690 100644
> > --- a/drivers/media/i2c/Kconfig
> > +++ b/drivers/media/i2c/Kconfig
> > @@ -1451,6 +1451,20 @@ config VIDEO_THS7303
> > To compile this driver as a module, choose M here: the
> > module will be called ths7303.
> >
> > +config VIDEO_GC08A3
>
> Alphabetical order please.
[mtk]: fixed in patch:v3
>
> > +tristate "GalaxyCore gc08a3 sensor support"
> > +depends on GPIOLIB && I2C && VIDEO_DEV
> > +select V4L2_FWNODE
> > +select MEDIA_CONTROLLER
> > +select VIDEO_V4L2_SUBDEV_API
>
> You can drop all of the above except
>
> depends on GPIOLIB
>
> The dependencies on I2C and VIDEO_DEV and the selection of
> V4L2_FWNODE,
> MEDIA_CONTROLLER and VIDEIO_V4L2_SUBDEV_API are now handled
> automatically for all drivers in the VIDEO_CAMERA_SENSOR menu.
[mtk]: fixed in patch:v3
>
> > +select REGMAP_I2C
> > +help
> > + This is a Video4Linux2 sensor driver for the GalaxyCore gc08a3
> > + camera.
> > +
> > + To compile this driver as a module, choose M here: the
> > + module will be called gc08a3.
> > +
> > endmenu
> >
> > #
> > diff --git a/drivers/media/i2c/Makefile
> b/drivers/media/i2c/Makefile
> > index f5010f80a21f..ec40dbd75e7a 100644
> > --- a/drivers/media/i2c/Makefile
> > +++ b/drivers/media/i2c/Makefile
> > @@ -36,6 +36,7 @@ obj-$(CONFIG_VIDEO_DW9719) += dw9719.o
> > obj-$(CONFIG_VIDEO_DW9768) += dw9768.o
> > obj-$(CONFIG_VIDEO_DW9807_VCM) += dw9807-vcm.o
> > obj-$(CONFIG_VIDEO_ET8EK8) += et8ek8/
> > +obj-$(CONFIG_VIDEO_GC08A3) += gc08a3.o
> > obj-$(CONFIG_VIDEO_HI556) += hi556.o
> > obj-$(CONFIG_VIDEO_HI846) += hi846.o
> > obj-$(CONFIG_VIDEO_HI847) += hi847.o
> > diff --git a/drivers/media/i2c/gc08a3.c
> b/drivers/media/i2c/gc08a3.c
> > new file mode 100644
> > index 000000000000..d3cf62b65c2e
> > --- /dev/null
> > +++ b/drivers/media/i2c/gc08a3.c
> > @@ -0,0 +1,2046 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * gc08a3.c - gc08a3 sensor driver
> > + *
> > + * Copyright 2023 Mediatek
> > + *
> > + * Zhi Mao <[email protected]>
> > + */
> > +
> > +#include <asm/unaligned.h>
> > +#include <linux/clk.h>
> > +#include <linux/delay.h>
> > +#include <linux/gpio/consumer.h>
> > +#include <linux/i2c.h>
> > +#include <linux/module.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/regmap.h>
> > +#include <linux/regulator/consumer.h>
> > +#include <media/media-entity.h>
> > +#include <media/v4l2-ctrls.h>
> > +#include <media/v4l2-fwnode.h>
> > +#include <media/v4l2-subdev.h>
> > +
> > +//=========================================
>
> V4L2 uses C-style comments.
[mtk]: fixed in patch:v3
>
> > +#define GC08A3_REG_VALUE_08BIT 1
> > +#define GC08A3_REG_VALUE_16BIT 2
> > +#define GC08A3_REG_VALUE_24BIT 3
>
> Please use the v4l2-cci helpers for register access.
[mtk]: fixed in patch:v3
>
> > +
> > +#define GC08A3_REG_CHIP_ID 0x03f0
> > +#define GC08A3_CHIP_ID 0x08a3
> > +
> > +#define GC08A3_NATIVE_WIDTH3264
> > +#define GC08A3_NATIVE_HEIGHT2448
> > +
> > +#define GC08A3_REG_TEST_PATTERN_EN 0x008c
> > +#define GC08A3_REG_TEST_PATTERN_IDX 0x008d
> > +#define GC08A3_TEST_PATTERN_EN 0x01
> > +
> > +#define GC08A3_STRAEMING_REG 0x0100
> > +
> > +#define GC08A3_SCLK 280000000LL
> > +
> > +#define GC08A3_DEFAULT_CLK_FREQ 24000000
> > +#define GC08A3_FPS 30
> > +#define GC08A3_MBUS_CODE MEDIA_BUS_FMT_SRGGB10_1X10
> > +#define GC08A3_DATA_LANES 4
> > +
> > +//for 1920*1080
> > +#define GC08A3_LINK_FREQ_207MHZ 207000000ULL
> > +//for 3264*2448
> > +#define GC08A3_LINK_FREQ_336MHZ 336000000ULL
>
> Macros don't help readibility, simply use the numerical values in the
> link_freq_menu_items array below.
[mtk]: Using magic number may not improve code readability,
and I find some other sensor driver also use macros in
link_freq_menu_items. Can we keep this useage in gc08a driver?
>
> > +
> > +#define GC08A3_RGB_DEPTH 10
> > +
> > +//frame length
> > +#define GC08A3_FL_REG 0x0340
> > +#define GC08A3_VTS_30FPS 2548
> > +#define GC08A3_VTS_30FPS_MIN 2548
> > +#define GC08A3_VTS_60FPS 1276
> > +#define GC08A3_VTS_60FPS_MIN 1276
> > +#define GC08A3_VTS_MAX 0xfff0
> > +#define GC08A3_FL_MARGIN 48
> > +
> > +// line length
> > +#define GC08A3_LL_REG 0x0342
> > +#define GC08A3_HTS_30FPS 3640
> > +#define GC08A3_HTS_60FPS 3640
> > +
> > +#define GC08A3_EXP_REG 0x0202
> > +#define GC08A3_EXP_MARGIN 16
> > +#define GC08A3_EXP_MIN 4
> > +#define GC08A3_EXP_STEP 1
> > +
> > +#define GC08A3_FLIP_REG 0x0101
> > +#define GC08A3_FLIP_H_MASK 0x1
> > +#define GC08A3_FLIP_V_MASK 0x2
> > +
> > +#define GC08A3_AGAIN_REG 0x0204
> > +#define GC08A3_AGAIN_MIN 1024
> > +#define GC08A3_AGAIN_MAX (1024 * 16)
> > +#define GC08A3_AGAIN_STEP 1
> > +
> > +#define GC08A3_DGAIN_REG 0x020e
> > +#define GC08A3_DGAIN_MIN 1024
> > +#define GC08A3_DGAIN_MAX 1024
> > +#define GC08A3_DGAIN_STEP 1
> > +//============================================================
> > +
> > +//============================================================
> > +static const char *const gc08a3_test_pattern_menu[] = {
> > +"No Pattern", "Solid Black", "Solid White", "Solid Red",
> > +"Solid Green", "Solid Blue", "Solid Yellow", "Colour Bar",
> > +};
> > +
> > +enum {
> > +GC08A3_LINK_FREQ_336MHZ_CFG,
> > +GC08A3_LINK_FREQ_207MHZ_CFG,
> > +};
> > +
> > +static const s64 link_freq_menu_items[] = {
> > +GC08A3_LINK_FREQ_336MHZ,
> > +GC08A3_LINK_FREQ_207MHZ,
> > +};
> > +
> > +static const char *const gc08a3_supply_name[] = {
> > +"avdd",
> > +"dvdd",
> > +"dovdd",
> > +};
> > +
> > +#define GC08A3_NUM_SUPPLIES ARRAY_SIZE(gc08a3_supply_name)
> > +
> > +struct gc08a3 {
> > +struct device *dev;
> > +struct v4l2_subdev sd;
> > +struct media_pad pad;
> > +struct v4l2_mbus_framefmt fmt;
>
> Please use the subdev active state to store the active format. See
> commit e8a5b1df000e ("media: i2c: imx219: Use subdev active state")
> as
> an example of how to do so. There's also documentation in
> Documentation/driver-api/media/v4l2-subdev.rst (please read the most
> up-to-date version in the master branch of the media_stage tree).
>
[mtk]: fixed in patch:v3

> > +struct i2c_client *client;
> > +
> > +struct v4l2_rect crop;
>
> This should be stored in the subdev active state too.
>
[mtk]: fixed in patch:v3

> > +
> > +struct clk *xclk;
> > +struct regulator_bulk_data supplies[GC08A3_NUM_SUPPLIES];
> > +struct gpio_desc *enable_gpio;
> > +
> > +struct regmap *regmap;
> > +
> > +struct v4l2_ctrl_handler ctrls;
> > +struct v4l2_ctrl *pixel_rate;
> > +struct v4l2_ctrl *link_freq;
> > +struct v4l2_ctrl *exposure;
> > +struct v4l2_ctrl *vblank;
> > +struct v4l2_ctrl *hblank;
> > +
> > +/*
> > + * Serialize control access, get/set format, get selection
> > + * and start streaming.
> > + */
> > +struct mutex mutex;
>
> The mutex won't be needed anymore once you use the subdev active
> state.
>
[mtk]: fixed in patch:v3

> > +
> > +bool streaming;
> > +
> > +/* Current mode */
> > +const struct gc08a3_mode *cur_mode;
> > +};
> > +
> > +struct reg_8 {
> > +u16 address;
> > +u8 val;
> > +};
> > +
> > +struct gc08a3_reg_list {
> > +u32 num_of_regs;
> > +const struct reg_8 *regs;
> > +};
> > +
> > +struct gc08a3_link_freq_config {
> > +const struct gc08a3_reg_list reg_list;
> > +};
> > +
> > +enum {
> > +GC08A3_TABLE_WAIT_MS = 0,
> > +GC08A3_TABLE_END,
> > +};
> > +
> > +/*From gc08a3_mode_tbls.h*/
> > +static const struct reg_8 mode_3264x2448[] = {
> > +/*system*/
> > +{ 0x031c, 0x60 },
> > +{ 0x0337, 0x04 },
> > +{ 0x0335, 0x51 },
> > +{ 0x0336, 0x70 },
> > +{ 0x0383, 0xbb },
> > +{ 0x031a, 0x00 },
> > +{ 0x0321, 0x10 },
> > +{ 0x0327, 0x03 },
> > +{ 0x0325, 0x40 },
> > +{ 0x0326, 0x23 },
> > +{ 0x0314, 0x11 },
> > +{ 0x0315, 0xd6 },
> > +{ 0x0316, 0x01 },
> > +{ 0x0334, 0x40 },
> > +{ 0x0324, 0x42 },
> > +{ 0x031c, 0x00 },
> > +{ 0x031c, 0x9f },
> > +{ 0x0344, 0x00 },
> > +{ 0x0345, 0x06 },
> > +{ 0x0346, 0x00 },
> > +{ 0x0347, 0x04 },
> > +{ 0x0348, 0x0c },
> > +{ 0x0349, 0xd0 }, //3280
> > +{ 0x034a, 0x09 },
> > +{ 0x034b, 0x9c }, //2460
> > +{ 0x0202, 0x09 },
> > +{ 0x0203, 0x04 }, //Exp
> > +{ 0x0340, 0x09 },
> > +{ 0x0341, 0xf4 }, //FL
> > +{ 0x0342, 0x07 },
> > +{ 0x0343, 0x1c }, //LineLength
> > +
> > +{ 0x0226, 0x00 }, //min vb[15:8]
> > +{ 0x0227, 0x28 }, //min vb[7:0]
> > +{ 0x0e38, 0x49 },
> > +{ 0x0210, 0x13 },
> > +{ 0x0218, 0x00 },
> > +{ 0x0241, 0x88 },
> > +{ 0x0392, 0x60 },
> > +
> > +/*ISP*/
> > +{ 0x031c, 0x80 },
> > +{ 0x03fe, 0x10 }, //CISCTL_rst
> > +{ 0x03fe, 0x00 },
> > +{ 0x031c, 0x9f },
> > +{ 0x03fe, 0x00 },
> > +{ 0x03fe, 0x00 },
> > +{ 0x03fe, 0x00 },
> > +{ 0x03fe, 0x00 },
> > +{ 0x031c, 0x80 },
> > +{ 0x03fe, 0x10 },
> > +{ 0x03fe, 0x00 },
> > +{ 0x031c, 0x9f },
> > +{ 0x00a2, 0x00 },
> > +{ 0x00a3, 0x00 },
> > +{ 0x00ab, 0x00 },
> > +{ 0x00ac, 0x00 },
> > +{ 0x05a0, 0x82 },
> > +{ 0x05ac, 0x00 },
> > +{ 0x05ad, 0x01 },
> > +{ 0x05ae, 0x00 },
> > +{ 0x0800, 0x0a },
> > +{ 0x0801, 0x14 },
> > +{ 0x0802, 0x28 },
> > +{ 0x0803, 0x34 },
> > +{ 0x0804, 0x0e },
> > +{ 0x0805, 0x33 },
> > +{ 0x0806, 0x03 },
> > +{ 0x0807, 0x8a },
> > +{ 0x0808, 0x50 },
> > +{ 0x0809, 0x00 },
> > +{ 0x080a, 0x34 },
> > +{ 0x080b, 0x03 },
> > +{ 0x080c, 0x26 },
> > +{ 0x080d, 0x03 },
> > +{ 0x080e, 0x18 },
> > +{ 0x080f, 0x03 },
> > +{ 0x0810, 0x10 },
> > +{ 0x0811, 0x03 },
> > +{ 0x0812, 0x00 },
> > +{ 0x0813, 0x00 },
> > +{ 0x0814, 0x01 },
> > +{ 0x0815, 0x00 },
> > +{ 0x0816, 0x01 },
> > +{ 0x0817, 0x00 },
> > +{ 0x0818, 0x00 },
> > +{ 0x0819, 0x0a },
> > +{ 0x081a, 0x01 },
> > +{ 0x081b, 0x6c },
> > +{ 0x081c, 0x00 },
> > +{ 0x081d, 0x0b },
> > +{ 0x081e, 0x02 },
> > +{ 0x081f, 0x00 },
> > +{ 0x0820, 0x00 },
> > +{ 0x0821, 0x0c },
> > +{ 0x0822, 0x02 },
> > +{ 0x0823, 0xd9 },
> > +{ 0x0824, 0x00 },
> > +{ 0x0825, 0x0d },
> > +{ 0x0826, 0x03 },
> > +{ 0x0827, 0xf0 },
> > +{ 0x0828, 0x00 },
> > +{ 0x0829, 0x0e },
> > +{ 0x082a, 0x05 },
> > +{ 0x082b, 0x94 },
> > +{ 0x082c, 0x09 },
> > +{ 0x082d, 0x6e },
> > +{ 0x082e, 0x07 },
> > +{ 0x082f, 0xe6 },
> > +{ 0x0830, 0x10 },
> > +{ 0x0831, 0x0e },
> > +{ 0x0832, 0x0b },
> > +{ 0x0833, 0x2c },
> > +{ 0x0834, 0x14 },
> > +{ 0x0835, 0xae },
> > +{ 0x0836, 0x0f },
> > +{ 0x0837, 0xc4 },
> > +{ 0x0838, 0x18 },
> > +{ 0x0839, 0x0e },
> > +{ 0x05ac, 0x01 },
> > +{ 0x059a, 0x00 },
> > +{ 0x059b, 0x00 },
> > +{ 0x059c, 0x01 },
> > +{ 0x0598, 0x00 },
> > +{ 0x0597, 0x14 },
> > +{ 0x05ab, 0x09 },
> > +{ 0x05a4, 0x02 },
> > +{ 0x05a3, 0x05 },
> > +{ 0x05a0, 0xc2 },
> > +{ 0x0207, 0xc4 },
> > +
> > +/*GAIN*/
> > +{ 0x0204, 0x04 },
> > +{ 0x0205, 0x00 },
> > +{ 0x0050, 0x5c },
> > +{ 0x0051, 0x44 },
> > +
> > +/*out window*/
> > +{ 0x009a, 0x66 },
> > +{ 0x0351, 0x00 },
> > +{ 0x0352, 0x06 }, //out_win_y1
> > +{ 0x0353, 0x00 },
> > +{ 0x0354, 0x08 }, //out_win_x1
> > +{ 0x034c, 0x0c },
> > +{ 0x034d, 0xc0 }, //3264
> > +{ 0x034e, 0x09 },
> > +{ 0x034f, 0x90 }, //2448
> > +
> > +/*MIPI*/
> > +{ 0x0114, 0x03 }, //0:1lane 1:2lane 3:4lane
> > +{ 0x0180, 0x65 }, //[3:0]dphy_mipi_diff
> > +{ 0x0181, 0xf0 },
> > +{ 0x0185, 0x01 },
> > +{ 0x0115, 0x30 },
> > +{ 0x011b, 0x12 },
> > +{ 0x011c, 0x12 },
> > +{ 0x0121, 0x06 }, //T_LPX
> > +{ 0x0122, 0x06 }, //T_CLK_HS_PREPARE
> > +{ 0x0123, 0x15 }, //T_CLK_zero
> > +{ 0x0124, 0x01 }, //T_CLK_PRE
> > +{ 0x0125, 0x0b }, //T_CLK_POST
> > +{ 0x0126, 0x08 }, //T_CLK_TRAIL
> > +{ 0x0129, 0x06 }, //T_HS_PREPARE
> > +{ 0x012a, 0x08 }, //T_HS_Zero
> > +{ 0x012b, 0x08 }, //T_HS_TRAIL
> > +
> > +{ 0x0a73, 0x60 },
> > +{ 0x0a70, 0x11 },
> > +{ 0x0313, 0x80 },
> > +{ 0x0aff, 0x00 },
> > +{ 0x0aff, 0x00 },
> > +{ 0x0aff, 0x00 },
> > +{ 0x0aff, 0x00 },
> > +{ 0x0aff, 0x00 },
> > +{ 0x0aff, 0x00 },
> > +{ 0x0aff, 0x00 },
> > +{ 0x0aff, 0x00 },
> > +{ 0x0a70, 0x00 },
> > +{ 0x00a4, 0x80 },
> > +{ 0x0316, 0x01 },
> > +{ 0x0a67, 0x00 },
> > +{ 0x0084, 0x10 },
> > +{ 0x0102, 0x09 },
> > +
> > +{ GC08A3_TABLE_END, 0x00 }
> > +};
> > +
> > +static const struct reg_8 mode_1920x1080[] = {
> > +/*system*/
> > +{ 0x031c, 0x60 },
> > +{ 0x0337, 0x04 },
> > +{ 0x0335, 0x51 },
> > +{ 0x0336, 0x45 },
> > +{ 0x0383, 0x8b },
> > +{ 0x031a, 0x00 },
> > +{ 0x0321, 0x10 },
> > +{ 0x0327, 0x03 },
> > +{ 0x0325, 0x40 },
> > +{ 0x0326, 0x23 },
> > +{ 0x0314, 0x11 },
> > +{ 0x0315, 0xd6 },
> > +{ 0x0316, 0x01 },
> > +{ 0x0334, 0x40 },
> > +{ 0x0324, 0x42 },
> > +{ 0x031c, 0x00 },
> > +{ 0x031c, 0x9f },
> > +{ 0x0344, 0x02 },
> > +{ 0x0345, 0xa6 },
> > +{ 0x0346, 0x02 },
> > +{ 0x0347, 0xb0 },
> > +{ 0x0348, 0x07 },
> > +{ 0x0349, 0x90 }, //1936
> > +{ 0x034a, 0x04 },
> > +{ 0x034b, 0x44 }, //1092
> > +{ 0x0202, 0x03 },
> > +{ 0x0203, 0x00 }, //Exp
> > +{ 0x0340, 0x04 },
> > +{ 0x0341, 0xfc }, //FL
> > +{ 0x0342, 0x07 },
> > +{ 0x0343, 0x1c }, //LineLength
> > +
> > +{ 0x0226, 0x00 }, //min vb[15:8]
> > +{ 0x0227, 0x88 }, //min vb[7:0]
> > +{ 0x0e38, 0x49 },
> > +{ 0x0210, 0x13 },
> > +{ 0x0218, 0x00 },
> > +{ 0x0241, 0x88 },
> > +{ 0x0392, 0x60 },
> > +
> > +/*ISP*/
> > +{ 0x031c, 0x80 },
> > +{ 0x03fe, 0x10 }, //CISCTL_rst
> > +{ 0x03fe, 0x00 },
> > +{ 0x031c, 0x9f },
> > +{ 0x03fe, 0x00 },
> > +{ 0x03fe, 0x00 },
> > +{ 0x03fe, 0x00 },
> > +{ 0x03fe, 0x00 },
> > +{ 0x031c, 0x80 },
> > +{ 0x03fe, 0x10 },
> > +{ 0x03fe, 0x00 },
> > +{ 0x031c, 0x9f },
> > +{ 0x00a2, 0xac },
> > +{ 0x00a3, 0x02 },
> > +{ 0x00ab, 0xa0 },
> > +{ 0x00ac, 0x02 },
> > +{ 0x05a0, 0x82 },
> > +{ 0x05ac, 0x00 },
> > +{ 0x05ad, 0x01 },
> > +{ 0x05ae, 0x00 },
> > +{ 0x0800, 0x0a },
> > +{ 0x0801, 0x14 },
> > +{ 0x0802, 0x28 },
> > +{ 0x0803, 0x34 },
> > +{ 0x0804, 0x0e },
> > +{ 0x0805, 0x33 },
> > +{ 0x0806, 0x03 },
> > +{ 0x0807, 0x8a },
> > +{ 0x0808, 0x50 },
> > +{ 0x0809, 0x00 },
> > +{ 0x080a, 0x34 },
> > +{ 0x080b, 0x03 },
> > +{ 0x080c, 0x26 },
> > +{ 0x080d, 0x03 },
> > +{ 0x080e, 0x18 },
> > +{ 0x080f, 0x03 },
> > +{ 0x0810, 0x10 },
> > +{ 0x0811, 0x03 },
> > +{ 0x0812, 0x00 },
> > +{ 0x0813, 0x00 },
> > +{ 0x0814, 0x01 },
> > +{ 0x0815, 0x00 },
> > +{ 0x0816, 0x01 },
> > +{ 0x0817, 0x00 },
> > +{ 0x0818, 0x00 },
> > +{ 0x0819, 0x0a },
> > +{ 0x081a, 0x01 },
> > +{ 0x081b, 0x6c },
> > +{ 0x081c, 0x00 },
> > +{ 0x081d, 0x0b },
> > +{ 0x081e, 0x02 },
> > +{ 0x081f, 0x00 },
> > +{ 0x0820, 0x00 },
> > +{ 0x0821, 0x0c },
> > +{ 0x0822, 0x02 },
> > +{ 0x0823, 0xd9 },
> > +{ 0x0824, 0x00 },
> > +{ 0x0825, 0x0d },
> > +{ 0x0826, 0x03 },
> > +{ 0x0827, 0xf0 },
> > +{ 0x0828, 0x00 },
> > +{ 0x0829, 0x0e },
> > +{ 0x082a, 0x05 },
> > +{ 0x082b, 0x94 },
> > +{ 0x082c, 0x09 },
> > +{ 0x082d, 0x6e },
> > +{ 0x082e, 0x07 },
> > +{ 0x082f, 0xe6 },
> > +{ 0x0830, 0x10 },
> > +{ 0x0831, 0x0e },
> > +{ 0x0832, 0x0b },
> > +{ 0x0833, 0x2c },
> > +{ 0x0834, 0x14 },
> > +{ 0x0835, 0xae },
> > +{ 0x0836, 0x0f },
> > +{ 0x0837, 0xc4 },
> > +{ 0x0838, 0x18 },
> > +{ 0x0839, 0x0e },
> > +{ 0x05ac, 0x01 },
> > +{ 0x059a, 0x00 },
> > +{ 0x059b, 0x00 },
> > +{ 0x059c, 0x01 },
> > +{ 0x0598, 0x00 },
> > +{ 0x0597, 0x14 },
> > +{ 0x05ab, 0x09 },
> > +{ 0x05a4, 0x02 },
> > +{ 0x05a3, 0x05 },
> > +{ 0x05a0, 0xc2 },
> > +{ 0x0207, 0xc4 },
> > +
> > +/*GAIN*/
> > +{ 0x0204, 0x04 },
> > +{ 0x0205, 0x00 },
> > +{ 0x0050, 0x38 },
> > +{ 0x0051, 0x20 },
> > +
> > +/*out window*/
> > +{ 0x009a, 0x66 },
> > +{ 0x0351, 0x00 },
> > +{ 0x0352, 0x06 }, //out_win_y1
> > +{ 0x0353, 0x00 },
> > +{ 0x0354, 0x08 }, //out_win_x1
> > +{ 0x034c, 0x07 },
> > +{ 0x034d, 0x80 }, //1920
> > +{ 0x034e, 0x04 },
> > +{ 0x034f, 0x38 }, //1080
> > +
> > +/*MIPI*/
> > +{ 0x0114, 0x03 }, //0:1lane 1:2lane 3:4lane
> > +{ 0x0180, 0x65 }, //[3:0]dphy_mipi_diff
> > +{ 0x0181, 0xf0 },
> > +{ 0x0185, 0x01 },
> > +{ 0x0115, 0x30 },
> > +{ 0x011b, 0x12 },
> > +{ 0x011c, 0x12 },
> > +{ 0x0121, 0x02 }, //T_LPX
> > +{ 0x0122, 0x03 }, //T_CLK_HS_PREPARE
> > +{ 0x0123, 0x0c }, //T_CLK_zero
> > +{ 0x0124, 0x00 }, //T_CLK_PRE
> > +{ 0x0125, 0x09 }, //T_CLK_POST
> > +{ 0x0126, 0x06 }, //T_CLK_TRAIL
> > +{ 0x0129, 0x04 }, //T_HS_PREPARE
> > +{ 0x012a, 0x03 }, //T_HS_Zero
> > +{ 0x012b, 0x06 }, //T_HS_TRAIL
> > +
> > +{ 0x0a73, 0x60 },
> > +{ 0x0a70, 0x11 },
> > +{ 0x0313, 0x80 },
> > +{ 0x0aff, 0x00 },
> > +{ 0x0aff, 0x00 },
> > +{ 0x0aff, 0x00 },
> > +{ 0x0aff, 0x00 },
> > +{ 0x0aff, 0x00 },
> > +{ 0x0aff, 0x00 },
> > +{ 0x0aff, 0x00 },
> > +{ 0x0aff, 0x00 },
> > +{ 0x0a70, 0x00 },
> > +{ 0x00a4, 0x80 },
> > +{ 0x0316, 0x01 },
> > +{ 0x0a67, 0x00 },
> > +{ 0x0084, 0x10 },
> > +{ 0x0102, 0x09 },
> > +
> > +{ GC08A3_TABLE_END, 0x00 }
> > +};
> > +
> > +static const struct reg_8 mode_table_common[] = {
> > +{ GC08A3_STRAEMING_REG, 0x00 },
> > +/*system*/
> > +{ 0x031c, 0x60 },
> > +{ 0x0337, 0x04 },
> > +{ 0x0335, 0x51 },
> > +{ 0x0336, 0x70 },
> > +{ 0x0383, 0xbb },
> > +{ 0x031a, 0x00 },
> > +{ 0x0321, 0x10 },
> > +{ 0x0327, 0x03 },
> > +{ 0x0325, 0x40 },
> > +{ 0x0326, 0x23 },
> > +{ 0x0314, 0x11 },
> > +{ 0x0315, 0xd6 },
> > +{ 0x0316, 0x01 },
> > +{ 0x0334, 0x40 },
> > +{ 0x0324, 0x42 },
> > +{ 0x031c, 0x00 },
> > +{ 0x031c, 0x9f },
> > +{ 0x039a, 0x13 },
> > +{ 0x0084, 0x30 },
> > +{ 0x02b3, 0x08 },
> > +{ 0x0057, 0x0c },
> > +{ 0x05c3, 0x50 },
> > +{ 0x0311, 0x90 },
> > +{ 0x05a0, 0x02 },
> > +{ 0x0074, 0x0a },
> > +{ 0x0059, 0x11 },
> > +{ 0x0070, 0x05 },
> > +{ 0x0101, 0x00 }, //[1]updown [0]mirror
> > +
> > +/*analog*/
> > +{ 0x0344, 0x00 },
> > +{ 0x0345, 0x06 },
> > +{ 0x0346, 0x00 },
> > +{ 0x0347, 0x04 },
> > +{ 0x0348, 0x0c },
> > +{ 0x0349, 0xd0 }, //3280
> > +{ 0x034a, 0x09 },
> > +{ 0x034b, 0x9c }, //2460
> > +{ 0x0202, 0x09 },
> > +{ 0x0203, 0x04 }, //Exp
> > +
> > +{ 0x0219, 0x05 }, //[4]FL_depend_exp
> > +{ 0x0226, 0x00 }, //min vb[15:8]
> > +{ 0x0227, 0x28 }, //min vb[7:0]
> > +{ 0x0e0a, 0x00 },
> > +{ 0x0e0b, 0x00 },
> > +{ 0x0e24, 0x04 },
> > +{ 0x0e25, 0x04 },
> > +{ 0x0e26, 0x00 },
> > +{ 0x0e27, 0x10 },
> > +{ 0x0e01, 0x74 },
> > +{ 0x0e03, 0x47 },
> > +{ 0x0e04, 0x33 },
> > +{ 0x0e05, 0x44 },
> > +{ 0x0e06, 0x44 },
> > +{ 0x0e0c, 0x1e },
> > +{ 0x0e17, 0x3a },
> > +{ 0x0e18, 0x3c },
> > +{ 0x0e19, 0x40 },
> > +{ 0x0e1a, 0x42 },
> > +{ 0x0e28, 0x21 },
> > +{ 0x0e2b, 0x68 },
> > +{ 0x0e2c, 0x0d },
> > +{ 0x0e2d, 0x08 },
> > +{ 0x0e34, 0xf4 },
> > +{ 0x0e35, 0x44 },
> > +{ 0x0e36, 0x07 },
> > +{ 0x0e38, 0x49 },
> > +{ 0x0210, 0x13 },
> > +{ 0x0218, 0x00 },
> > +{ 0x0241, 0x88 },
> > +{ 0x0e32, 0x00 },
> > +{ 0x0e33, 0x18 },
> > +{ 0x0e42, 0x03 },
> > +{ 0x0e43, 0x80 },
> > +{ 0x0e44, 0x04 },
> > +{ 0x0e45, 0x00 },
> > +{ 0x0e4f, 0x04 },
> > +{ 0x057a, 0x20 },
> > +{ 0x0381, 0x7c },
> > +{ 0x0382, 0x9b },
> > +{ 0x0384, 0xfb },
> > +{ 0x0389, 0x38 },
> > +{ 0x038a, 0x03 },
> > +{ 0x0390, 0x6a },
> > +{ 0x0391, 0x0b },
> > +{ 0x0392, 0x60 },
> > +{ 0x0393, 0xc1 },
> > +{ 0x0396, 0xff },
> > +{ 0x0398, 0x62 },
> > +
> > +/*cisctl reset*/
> > +{ 0x031c, 0x80 },
> > +{ 0x03fe, 0x10 }, //CISCTL_rst
> > +{ 0x03fe, 0x00 },
> > +{ 0x031c, 0x9f },
> > +{ 0x03fe, 0x00 },
> > +{ 0x03fe, 0x00 },
> > +{ 0x03fe, 0x00 },
> > +{ 0x03fe, 0x00 },
> > +{ 0x031c, 0x80 },
> > +{ 0x03fe, 0x10 }, //CISCTL_rst
> > +{ 0x03fe, 0x00 },
> > +{ 0x031c, 0x9f },
> > +{ 0x0360, 0x01 },
> > +{ 0x0360, 0x00 },
> > +{ 0x0316, 0x09 },
> > +{ 0x0a67, 0x80 },
> > +{ 0x0313, 0x00 },
> > +{ 0x0a53, 0x0e },
> > +{ 0x0a65, 0x17 },
> > +{ 0x0a68, 0xa1 },
> > +{ 0x0a58, 0x00 },
> > +{ 0x0ace, 0x0c },
> > +{ 0x00a4, 0x00 },
> > +{ 0x00a5, 0x01 },
> > +{ 0x00a7, 0x09 },
> > +{ 0x00a8, 0x9c },
> > +{ 0x00a9, 0x0c },
> > +{ 0x00aa, 0xd0 },
> > +{ 0x0a8a, 0x00 },
> > +{ 0x0a8b, 0xe0 },
> > +{ 0x0a8c, 0x13 },
> > +{ 0x0a8d, 0xe8 },
> > +{ 0x0a90, 0x0a },
> > +{ 0x0a91, 0x10 },
> > +{ 0x0a92, 0xf8 },
> > +{ 0x0a71, 0xf2 },
> > +{ 0x0a72, 0x12 },
> > +{ 0x0a73, 0x64 },
> > +{ 0x0a75, 0x41 },
> > +{ 0x0a70, 0x07 },
> > +{ 0x0313, 0x80 },
> > +
> > +/*ISP*/
> > +{ 0x00a0, 0x01 },
> > +{ 0x0080, 0xd2 },
> > +{ 0x0081, 0x3f },
> > +{ 0x0087, 0x51 },
> > +{ 0x0089, 0x03 },
> > +{ 0x009b, 0x40 },
> > +{ 0x05a0, 0x82 },
> > +{ 0x05ac, 0x00 },
> > +{ 0x05ad, 0x01 },
> > +{ 0x05ae, 0x00 },
> > +{ 0x0800, 0x0a },
> > +{ 0x0801, 0x14 },
> > +{ 0x0802, 0x28 },
> > +{ 0x0803, 0x34 },
> > +{ 0x0804, 0x0e },
> > +{ 0x0805, 0x33 },
> > +{ 0x0806, 0x03 },
> > +{ 0x0807, 0x8a },
> > +{ 0x0808, 0x50 },
> > +{ 0x0809, 0x00 },
> > +{ 0x080a, 0x34 },
> > +{ 0x080b, 0x03 },
> > +{ 0x080c, 0x26 },
> > +{ 0x080d, 0x03 },
> > +{ 0x080e, 0x18 },
> > +{ 0x080f, 0x03 },
> > +{ 0x0810, 0x10 },
> > +{ 0x0811, 0x03 },
> > +{ 0x0812, 0x00 },
> > +{ 0x0813, 0x00 },
> > +{ 0x0814, 0x01 },
> > +{ 0x0815, 0x00 },
> > +{ 0x0816, 0x01 },
> > +{ 0x0817, 0x00 },
> > +{ 0x0818, 0x00 },
> > +{ 0x0819, 0x0a },
> > +{ 0x081a, 0x01 },
> > +{ 0x081b, 0x6c },
> > +{ 0x081c, 0x00 },
> > +{ 0x081d, 0x0b },
> > +{ 0x081e, 0x02 },
> > +{ 0x081f, 0x00 },
> > +{ 0x0820, 0x00 },
> > +{ 0x0821, 0x0c },
> > +{ 0x0822, 0x02 },
> > +{ 0x0823, 0xd9 },
> > +{ 0x0824, 0x00 },
> > +{ 0x0825, 0x0d },
> > +{ 0x0826, 0x03 },
> > +{ 0x0827, 0xf0 },
> > +{ 0x0828, 0x00 },
> > +{ 0x0829, 0x0e },
> > +{ 0x082a, 0x05 },
> > +{ 0x082b, 0x94 },
> > +{ 0x082c, 0x09 },
> > +{ 0x082d, 0x6e },
> > +{ 0x082e, 0x07 },
> > +{ 0x082f, 0xe6 },
> > +{ 0x0830, 0x10 },
> > +{ 0x0831, 0x0e },
> > +{ 0x0832, 0x0b },
> > +{ 0x0833, 0x2c },
> > +{ 0x0834, 0x14 },
> > +{ 0x0835, 0xae },
> > +{ 0x0836, 0x0f },
> > +{ 0x0837, 0xc4 },
> > +{ 0x0838, 0x18 },
> > +{ 0x0839, 0x0e },
> > +{ 0x05ac, 0x01 },
> > +{ 0x059a, 0x00 },
> > +{ 0x059b, 0x00 },
> > +{ 0x059c, 0x01 },
> > +{ 0x0598, 0x00 },
> > +{ 0x0597, 0x14 },
> > +{ 0x05ab, 0x09 },
> > +{ 0x05a4, 0x02 },
> > +{ 0x05a3, 0x05 },
> > +{ 0x05a0, 0xc2 },
> > +{ 0x0207, 0xc4 },
> > +
> > +/*GAIN*/
> > +{ 0x0208, 0x01 },
> > +{ 0x0209, 0x72 },
> > +{ 0x0204, 0x04 },
> > +{ 0x0205, 0x00 },
> > +
> > +{ 0x0040, 0x22 },
> > +{ 0x0041, 0x20 },
> > +{ 0x0043, 0x10 },
> > +{ 0x0044, 0x00 },
> > +{ 0x0046, 0x08 },
> > +{ 0x0047, 0xf0 },
> > +{ 0x0048, 0x0f },
> > +{ 0x004b, 0x0f },
> > +{ 0x004c, 0x00 },
> > +{ 0x0050, 0x5c },
> > +{ 0x0051, 0x44 },
> > +{ 0x005b, 0x03 },
> > +{ 0x00c0, 0x00 },
> > +{ 0x00c1, 0x80 },
> > +{ 0x00c2, 0x31 },
> > +{ 0x00c3, 0x00 },
> > +{ 0x0460, 0x04 },
> > +{ 0x0462, 0x08 },
> > +{ 0x0464, 0x0e },
> > +{ 0x0466, 0x0a },
> > +{ 0x0468, 0x12 },
> > +{ 0x046a, 0x12 },
> > +{ 0x046c, 0x10 },
> > +{ 0x046e, 0x0c },
> > +{ 0x0461, 0x03 },
> > +{ 0x0463, 0x03 },
> > +{ 0x0465, 0x03 },
> > +{ 0x0467, 0x03 },
> > +{ 0x0469, 0x04 },
> > +{ 0x046b, 0x04 },
> > +{ 0x046d, 0x04 },
> > +{ 0x046f, 0x04 },
> > +{ 0x0470, 0x04 },
> > +{ 0x0472, 0x10 },
> > +{ 0x0474, 0x26 },
> > +{ 0x0476, 0x38 },
> > +{ 0x0478, 0x20 },
> > +{ 0x047a, 0x30 },
> > +{ 0x047c, 0x38 },
> > +{ 0x047e, 0x60 },
> > +{ 0x0471, 0x05 },
> > +{ 0x0473, 0x05 },
> > +{ 0x0475, 0x05 },
> > +{ 0x0477, 0x05 },
> > +{ 0x0479, 0x04 },
> > +{ 0x047b, 0x04 },
> > +{ 0x047d, 0x04 },
> > +{ 0x047f, 0x04 },
> > +
> > +{ GC08A3_TABLE_END, 0x00 }
> > +};
> > +
> > +static const struct gc08a3_link_freq_config link_freq_configs[] =
> {
> > +[GC08A3_LINK_FREQ_336MHZ_CFG] = {
> > +.reg_list = {
> > +.num_of_regs = ARRAY_SIZE(mode_table_common),
> > +.regs = mode_table_common,
> > +}
> > +},
> > +[GC08A3_LINK_FREQ_207MHZ_CFG] = {
> > +.reg_list = {
> > +.num_of_regs = ARRAY_SIZE(mode_table_common),
> > +.regs = mode_table_common,
> > +}
> > +},
> > +
>
> Extra blank line.
>
[mtk]: fixed in patch:v3

> > +};
> > +
> > +enum {
> > +GC08A3_PREV,
> > +GC08A3_HS,
> > +};
> > +
> > +/*
> > + * Declare modes in order, from biggest
> > + * to smallest height.
> > + */
> > +static const struct gc08a3_mode {
> > +u8 mode_id;
> > +u32 width;
> > +u32 height;
> > +const struct gc08a3_reg_list reg_list;
> > +
> > +u32 hts; /* Horizontal timining size */
> > +u32 vts_def; /* Default vertical timining size */
> > +u32 vts_min; /* Min vertical timining size */
> > +u32 link_freq_index; /* Link frequency needed for this resolution
> */
> > +u32 max_framerate;
> > +
>
> Extra blank line.
>
[mtk]: fixed in patch:v3

> > +} gc08a3_modes[] = {
> > +{
> > +.mode_id = GC08A3_PREV,
> > +.width = 3264,
> > +.height = 2448,
> > +.reg_list = {
> > +.num_of_regs = ARRAY_SIZE(mode_3264x2448),
> > +.regs = mode_3264x2448,
> > +},
> > +.link_freq_index = GC08A3_LINK_FREQ_336MHZ_CFG,
> > +
> > +.hts = GC08A3_HTS_30FPS,
> > +.vts_def = GC08A3_VTS_30FPS,
> > +.vts_min = GC08A3_VTS_30FPS_MIN,
> > +.max_framerate = 300,
> > +},
> > +{
> > +.mode_id = GC08A3_HS,
> > +.width = 1920,
> > +.height = 1080,
> > +.reg_list = {
> > +.num_of_regs = ARRAY_SIZE(mode_1920x1080),
> > +.regs = mode_1920x1080,
> > +},
> > +.link_freq_index = GC08A3_LINK_FREQ_207MHZ_CFG,
> > +
> > +.hts = GC08A3_HTS_60FPS,
> > +.vts_def = GC08A3_VTS_60FPS,
> > +.vts_min = GC08A3_VTS_60FPS_MIN,
> > +.max_framerate = 600,
> > +},
> > +};
> > +
> > +static u64 to_pixel_rate(u32 f_index)
> > +{
> > +u64 pixel_rate = link_freq_menu_items[f_index] * 2 *
> GC08A3_DATA_LANES;
> > +
> > +do_div(pixel_rate, GC08A3_RGB_DEPTH);
> > +
> > +return pixel_rate;
> > +}
> > +
> > +static u64 __maybe_unused to_pixels_per_line(u32 hts, u32 f_index)
> > +{
> > +u64 ppl = hts * to_pixel_rate(f_index);
> > +
> > +do_div(ppl, GC08A3_SCLK);
> > +
> > +return ppl;
> > +}
> > +
> > +static int gc08a3_read_reg(struct gc08a3 *gc08a3, u16 reg, u16
> len, u32 *val)
> > +{
> > +struct i2c_client *client = v4l2_get_subdevdata(&gc08a3->sd);
> > +struct i2c_msg msgs[2];
> > +u8 addr_buf[2];
> > +u8 data_buf[4] = { 0 };
> > +int ret;
> > +
> > +if (len > 4)
> > +return -EINVAL;
> > +
> > +put_unaligned_be16(reg, addr_buf);
> > +msgs[0].addr = client->addr;
> > +msgs[0].flags = 0;
> > +msgs[0].len = sizeof(addr_buf);
> > +msgs[0].buf = addr_buf;
> > +msgs[1].addr = client->addr;
> > +msgs[1].flags = I2C_M_RD;
> > +msgs[1].len = len;
> > +msgs[1].buf = &data_buf[4 - len];
> > +
> > +ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
> > +if (ret != ARRAY_SIZE(msgs))
> > +return -EIO;
> > +
> > +*val = get_unaligned_be32(data_buf);
> > +
> > +return 0;
> > +}
> > +
> > +static int gc08a3_write_reg(struct gc08a3 *gc08a3, u16 reg, u16
> len, u32 val)
> > +{
> > +struct i2c_client *client = v4l2_get_subdevdata(&gc08a3->sd);
> > +u8 buf[6];
> > +
> > +if (len > 4)
> > +return -EINVAL;
> > +
> > +put_unaligned_be16(reg, buf);
> > +put_unaligned_be32(val << 8 * (4 - len), buf + 2);
> > +if (i2c_master_send(client, buf, len + 2) != len + 2)
> > +return -EIO;
> > +
> > +return 0;
> > +}
> > +
> > +static int gc08a3_write_reg_list(struct gc08a3 *gc08a3,
> > + const struct gc08a3_reg_list *r_list)
> > +{
> > +struct i2c_client *client = v4l2_get_subdevdata(&gc08a3->sd);
> > +unsigned int i;
> > +int ret;
> > +
> > +for (i = 0; i < r_list->num_of_regs; i++) {
> > +if (r_list->regs[i].address == GC08A3_TABLE_WAIT_MS) {
> > +usleep_range(r_list->regs[i].val * 1000,
> > + r_list->regs[i].val * 1000 + 500);
> > +continue;
> > +}
> > +
> > +if (r_list->regs[i].address == GC08A3_TABLE_END)
> > +break;
> > +
> > +ret = gc08a3_write_reg(gc08a3, r_list->regs[i].address,
> > + GC08A3_REG_VALUE_08BIT,
> > + r_list->regs[i].val);
> > +if (ret) {
> > +dev_err_ratelimited(&client->dev,
> > + "failed to write reg 0x%4.4x. error = %d",
> > + r_list->regs[i].address, ret);
> > +return ret;
> > +}
> > +}
> > +
> > +return 0;
> > +}
> > +
> > +static int gc08a3_identify_module(struct gc08a3 *gc08a3)
> > +{
> > +struct i2c_client *client = v4l2_get_subdevdata(&gc08a3->sd);
> > +u32 val = 0;
> > +
> > +gc08a3_read_reg(gc08a3, GC08A3_REG_CHIP_ID,
> GC08A3_REG_VALUE_16BIT,
> > +&val);
> > +
> > +if (val != GC08A3_CHIP_ID) {
> > +dev_err(&client->dev, "chip id mismatch: 0x%x!=0x%x",
> > +GC08A3_CHIP_ID, val);
> > +return -ENXIO;
> > +}
> > +
> > +dev_info(&client->dev, "sensor_id: 0x%04x\n", val);
> > +return 0;
> > +}
> > +
> > +static inline struct gc08a3 *to_gc08a3(struct v4l2_subdev *sd)
> > +{
> > +return container_of(sd, struct gc08a3, sd);
> > +}
> > +
> > +static int __maybe_unused gc08a3_power_on(struct device *dev)
> > +{
> > +struct i2c_client *client = to_i2c_client(dev);
> > +struct v4l2_subdev *sd = i2c_get_clientdata(client);
> > +struct gc08a3 *gc08a3 = to_gc08a3(sd);
> > +int ret;
> > +
> > +dev_info(dev, "--- %s +", __func__);
>
> Drop this. Tracing is done with ftrace. Same comment below where
> applicable.
>
[mtk]: fixed in patch:v3

> > +
> > +gpiod_set_value_cansleep(gc08a3->enable_gpio, 0);
> > +usleep_range(2000, 3000);
> > +
> > +ret = regulator_bulk_enable(GC08A3_NUM_SUPPLIES, gc08a3-
> >supplies);
> > +if (ret < 0) {
> > +dev_err(gc08a3->dev, "failed to enable regulators: %d\n", ret);
> > +return ret;
> > +}
> > +
> > +ret = clk_prepare_enable(gc08a3->xclk);
> > +if (ret < 0) {
> > +regulator_bulk_disable(GC08A3_NUM_SUPPLIES, gc08a3->supplies);
> > +dev_err(gc08a3->dev, "clk prepare enable failed\n");
> > +return ret;
> > +}
> > +
> > +usleep_range(2000, 3000);
> > +
> > +gpiod_set_value_cansleep(gc08a3->enable_gpio, 1);
> > +usleep_range(12000, 15000);
> > +
> > +dev_info(dev, "--- %s -", __func__);
> > +
> > +return 0;
> > +}
> > +
> > +static int __maybe_unused gc08a3_power_off(struct device *dev)
> > +{
> > +struct i2c_client *client = to_i2c_client(dev);
> > +struct v4l2_subdev *sd = i2c_get_clientdata(client);
> > +struct gc08a3 *gc08a3 = to_gc08a3(sd);
> > +
> > +dev_info(dev, "--- %s +", __func__);
> > +
> > +gpiod_set_value_cansleep(gc08a3->enable_gpio, 0);
> > +
> > +clk_disable_unprepare(gc08a3->xclk);
> > +
> > +regulator_bulk_disable(GC08A3_NUM_SUPPLIES, gc08a3->supplies);
> > +usleep_range(10, 20);
> > +
> > +dev_info(dev, "--- %s -", __func__);
> > +
> > +return 0;
> > +}
> > +
> > +static int gc08a3_enum_mbus_code(struct v4l2_subdev *sd,
> > + struct v4l2_subdev_state *sd_state,
> > + struct v4l2_subdev_mbus_code_enum *code)
> > +{
> > +if (code->index > 0)
> > +return -EINVAL;
> > +
> > +code->code = GC08A3_MBUS_CODE;
> > +
> > +return 0;
> > +}
> > +
> > +static int gc08a3_enum_frame_size(struct v4l2_subdev *subdev,
> > + struct v4l2_subdev_state *sd_state,
> > + struct v4l2_subdev_frame_size_enum *fse)
> > +{
> > +if (fse->code != GC08A3_MBUS_CODE)
> > +return -EINVAL;
> > +
> > +if (fse->index >= ARRAY_SIZE(gc08a3_modes))
> > +return -EINVAL;
> > +
> > +fse->min_width = gc08a3_modes[fse->index].width;
> > +fse->max_width = gc08a3_modes[fse->index].width;
> > +fse->min_height = gc08a3_modes[fse->index].height;
> > +fse->max_height = gc08a3_modes[fse->index].height;
> > +
> > +return 0;
> > +}
> > +
> > +#ifdef CONFIG_VIDEO_ADV_DEBUG
> > +static int gc08a3_g_register(struct v4l2_subdev *subdev,
> > + struct v4l2_dbg_register *reg)
> > +{
> > +int ret;
> > +u32 val;
> > +struct gc08a3 *gc08a3 = container_of(subdev, struct gc08a3, sd);
> > +
> > +ret = gc08a3_read_reg(gc08a3, reg->reg, GC08A3_REG_VALUE_08BIT,
> &val);
> > +if (ret < 0)
> > +return ret;
> > +
> > +reg->val = val;
> > +reg->size = 1;
> > +
> > +return 0;
> > +}
> > +
> > +static int gc08a3_s_register(struct v4l2_subdev *subdev,
> > + const struct v4l2_dbg_register *reg)
> > +{
> > +struct gc08a3 *gc08a3 = container_of(subdev, struct gc08a3, sd);
> > +
> > +return gc08a3_write_reg(gc08a3, reg->reg, GC08A3_REG_VALUE_08BIT,
> > +reg->val & 0xff);
> > +}
> > +
> > +#endif
> > +
> > +static const struct v4l2_subdev_core_ops gc08a3_core_ops = {
> > +#ifdef CONFIG_VIDEO_ADV_DEBUG
> > +.g_register = gc08a3_g_register,
> > +.s_register = gc08a3_s_register,
> > +#endif
>
> Drop these too. If you need to access registers directly from
> userspace
> for development purpose, you can do so through i2cdev.
>
[mtk]: fixed in patch:v3

> > +};
> > +
> > +static struct v4l2_mbus_framefmt *
> > +__gc08a3_get_pad_format(struct gc08a3 *gc08a3,
> > +struct v4l2_subdev_state *sd_state, unsigned int pad,
> > +enum v4l2_subdev_format_whence which)
> > +{
> > +switch (which) {
> > +case V4L2_SUBDEV_FORMAT_TRY:
> > +return v4l2_subdev_get_try_format(&gc08a3->sd, sd_state, pad);
> > +case V4L2_SUBDEV_FORMAT_ACTIVE:
> > +return &gc08a3->fmt;
> > +default:
> > +return NULL;
> > +}
> > +}
> > +
> > +static int gc08a3_get_format(struct v4l2_subdev *sd,
> > + struct v4l2_subdev_state *sd_state,
> > + struct v4l2_subdev_format *format)
> > +{
> > +struct gc08a3 *gc08a3 = to_gc08a3(sd);
> > +
> > +mutex_lock(&gc08a3->mutex);
> > +format->format = *__gc08a3_get_pad_format(gc08a3, sd_state,
> format->pad,
> > + format->which);
> > +mutex_unlock(&gc08a3->mutex);
> > +
> > +return 0;
> > +}
> > +
> > +static struct v4l2_rect *
> > +__gc08a3_get_pad_crop(struct gc08a3 *gc08a3, struct
> v4l2_subdev_state *sd_state,
> > + unsigned int pad, enum v4l2_subdev_format_whence which)
> > +{
> > +switch (which) {
> > +case V4L2_SUBDEV_FORMAT_TRY:
> > +return v4l2_subdev_get_try_crop(&gc08a3->sd, sd_state, pad);
> > +case V4L2_SUBDEV_FORMAT_ACTIVE:
> > +return &gc08a3->crop;
> > +default:
> > +return NULL;
> > +}
> > +}
> > +
> > +static int gc08a3_update_cur_mode_controls(struct gc08a3 *gc08a3)
> > +{
> > +s64 exposure_max, h_blank;
> > +int ret = 0;
> > +
> > +ret = __v4l2_ctrl_modify_range(gc08a3->vblank,
> > + gc08a3->cur_mode->vts_min - gc08a3->cur_mode->height,
> > + GC08A3_VTS_MAX - gc08a3->cur_mode->height, 1,
> > + gc08a3->cur_mode->vts_def - gc08a3->cur_mode->height);
> > +if (ret)
> > +dev_err(gc08a3->dev, "VB ctrl range update failed\n");
> > +
> > +h_blank = gc08a3->cur_mode->hts - gc08a3->cur_mode->width;
> > +ret = __v4l2_ctrl_modify_range(gc08a3->hblank, h_blank, h_blank,
> 1,
> > + h_blank);
> > +if (ret)
> > +dev_err(gc08a3->dev, "HB ctrl range update failed\n");
> > +
> > +exposure_max = gc08a3->cur_mode->vts_def - GC08A3_EXP_MARGIN;
> > +ret = __v4l2_ctrl_modify_range(gc08a3->exposure, GC08A3_EXP_MIN,
> > + exposure_max, GC08A3_EXP_STEP,
> > + exposure_max);
> > +if (ret)
> > +dev_err(gc08a3->dev, "exposure ctrl range update failed\n");
> > +
> > +return ret;
> > +}
> > +
> > +static int gc08a3_set_format(struct v4l2_subdev *sd,
> > + struct v4l2_subdev_state *sd_state,
> > + struct v4l2_subdev_format *format)
> > +{
> > +struct gc08a3 *gc08a3 = to_gc08a3(sd);
> > +struct i2c_client *client = v4l2_get_subdevdata(&gc08a3->sd);
> > +struct device *dev = &client->dev;
> > +struct v4l2_mbus_framefmt *__format;
> > +struct v4l2_rect *__crop;
> > +const struct gc08a3_mode *mode;
> > +
> > +mutex_lock(&gc08a3->mutex);
> > +
> > +dev_dbg(dev, "---- %s, format(W x H:%d x %d) +", __func__,
> > +format->format.width, format->format.height);
> > +
> > +__crop = __gc08a3_get_pad_crop(gc08a3, sd_state, format->pad,
> > + format->which);
> > +
> > +mode = v4l2_find_nearest_size(gc08a3_modes,
> ARRAY_SIZE(gc08a3_modes),
> > + width, height, format->format.width,
> > + format->format.height);
> > +
> > +dev_dbg(dev, "----nearest mode(W x H:%d x %d)", mode->width,
> > +mode->height);
> > +
> > +__crop->width = mode->width;
> > +__crop->height = mode->height;
> > +
> > +__format = __gc08a3_get_pad_format(gc08a3, sd_state, format->pad,
> > + format->which);
> > +__format->width = __crop->width;
> > +__format->height = __crop->height;
> > +__format->code = GC08A3_MBUS_CODE;
> > +__format->field = V4L2_FIELD_NONE;
> > +__format->colorspace = V4L2_COLORSPACE_SRGB;
> > +__format->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(__format-
> >colorspace);
> > +__format->quantization =
> > +V4L2_MAP_QUANTIZATION_DEFAULT(true,
> > + __format->colorspace,
> > + __format->ycbcr_enc);
> > +__format->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(__format-
> >colorspace);
> > +
> > +format->format = *__format;
> > +
> > +gc08a3->cur_mode = mode;
> > +gc08a3_update_cur_mode_controls(gc08a3);
> > +
> > +mutex_unlock(&gc08a3->mutex);
> > +
> > +return 0;
> > +}
> > +
> > +static int gc08a3_get_selection(struct v4l2_subdev *sd,
> > +struct v4l2_subdev_state *sd_state,
> > +struct v4l2_subdev_selection *sel)
> > +{
> > +struct gc08a3 *gc08a3 = to_gc08a3(sd);
> > +
> > +switch (sel->target) {
> > +case V4L2_SEL_TGT_CROP:
> > +mutex_lock(&gc08a3->mutex);
> > +sel->r = *__gc08a3_get_pad_crop(gc08a3, sd_state, sel->pad, sel-
> >which);
> > +mutex_unlock(&gc08a3->mutex);
> > +break;
> > +case V4L2_SEL_TGT_CROP_BOUNDS:
> > +sel->r.top = 0;
> > +sel->r.left = 0;
> > +sel->r.width = GC08A3_NATIVE_WIDTH;
> > +sel->r.height = GC08A3_NATIVE_HEIGHT;
> > +break;
> > +case V4L2_SEL_TGT_CROP_DEFAULT:
> > +if (gc08a3->cur_mode->width == GC08A3_NATIVE_WIDTH) {
> > +sel->r.top = 0;
> > +sel->r.left = 0;
> > +sel->r.width = GC08A3_NATIVE_WIDTH;
> > +sel->r.height = GC08A3_NATIVE_HEIGHT;
> > +} else {
> > +sel->r.top = 0;
> > +sel->r.left = 0;
> > +sel->r.width = 1920;
> > +sel->r.height = 1080;
> > +}
> > +break;
> > +default:
> > +return -EINVAL;
> > +}
> > +
> > +return 0;
> > +}
> > +
> > +static int gc08a3_entity_init_cfg(struct v4l2_subdev *subdev,
> > + struct v4l2_subdev_state *sd_state)
> > +{
> > +struct v4l2_subdev_format fmt = {};
> > +
> > +fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY :
> > + V4L2_SUBDEV_FORMAT_ACTIVE;
> > +fmt.format.width = gc08a3_modes[0].width;
> > +fmt.format.height = gc08a3_modes[0].height;
> > +
> > +gc08a3_set_format(subdev, sd_state, &fmt);
> > +
> > +return 0;
> > +}
> > +
> > +static int gc08a3_set_ctrl_hflip(struct gc08a3 *gc08a3, u32
> ctrl_val)
> > +{
> > +int ret;
> > +u32 val;
> > +
> > +ret = gc08a3_read_reg(gc08a3, GC08A3_FLIP_REG,
> GC08A3_REG_VALUE_08BIT,
> > + &val);
> > +if (ret) {
> > +dev_err(gc08a3->dev, "read hflip register failed: %d\n", ret);
> > +return ret;
> > +}
> > +
> > +val = (ctrl_val) ? (val | GC08A3_FLIP_H_MASK) :
> > + (val & ~GC08A3_FLIP_H_MASK);
> > +ret = gc08a3_write_reg(gc08a3, GC08A3_FLIP_REG,
> GC08A3_REG_VALUE_08BIT,
> > + val);
> > +if (ret < 0)
> > +dev_err(gc08a3->dev, "Error %d\n", ret);
> > +
> > +return ret;
> > +}
> > +
> > +static int gc08a3_set_ctrl_vflip(struct gc08a3 *gc08a3, u32
> ctrl_val)
> > +{
> > +int ret;
> > +u32 val;
> > +
> > +ret = gc08a3_read_reg(gc08a3, GC08A3_FLIP_REG,
> GC08A3_REG_VALUE_08BIT,
> > + &val);
> > +if (ret) {
> > +dev_err(gc08a3->dev, "read vflip register failed: %d\n", ret);
> > +return ret;
> > +}
> > +
> > +val = (ctrl_val) ? (val | GC08A3_FLIP_V_MASK) :
> > + (val & ~GC08A3_FLIP_V_MASK);
> > +ret = gc08a3_write_reg(gc08a3, GC08A3_FLIP_REG,
> GC08A3_REG_VALUE_08BIT,
> > + val);
> > +if (ret < 0)
> > +dev_err(gc08a3->dev, "Error %d\n", ret);
> > +
> > +return ret;
> > +}
> > +
> > +static int gc08a3_test_pattern(struct gc08a3 *gc08a3, u32
> pattern_menu)
> > +{
> > +int ret = 0;
> > +u32 pattern = 0;
> > +
> > +if (pattern_menu) {
> > +// write bit16:0x0110 -> color bar
> > +switch (pattern_menu) {
> > +case 1:
> > +pattern = 0x00;
> > +break;
> > +case 2:
> > +case 3:
> > +case 4:
> > +case 5:
> > +case 6:
> > +pattern = pattern_menu + 2;
> > +break;
> > +case 7:
> > +pattern = 0x10;
> > +break;
> > +
> > +default:
> > +dev_dbg(gc08a3->dev, "invalid pattern menu!\n");
> > +break;
> > +}
> > +
> > +ret = gc08a3_write_reg(gc08a3, GC08A3_REG_TEST_PATTERN_EN,
> > + GC08A3_REG_VALUE_08BIT,
> > + GC08A3_TEST_PATTERN_EN);
> > +ret = gc08a3_write_reg(gc08a3, GC08A3_REG_TEST_PATTERN_IDX,
> > + GC08A3_REG_VALUE_08BIT, pattern);
> > +
> > +} else {
> > +ret = gc08a3_write_reg(gc08a3, GC08A3_REG_TEST_PATTERN_EN,
> > + GC08A3_REG_VALUE_08BIT, 0x00);
> > +}
> > +
> > +return ret;
> > +}
> > +
> > +static int gc08a3_set_ctrl(struct v4l2_ctrl *ctrl)
> > +{
> > +struct gc08a3 *gc08a3 =
> > +container_of(ctrl->handler, struct gc08a3, ctrls);
> > +int ret = 0;
> > +s64 exposure_max;
> > +
> > +if (ctrl->id == V4L2_CID_VBLANK) {
> > +/* Update max exposure while meeting expected vblanking */
> > +exposure_max = gc08a3->cur_mode->height + ctrl->val -
> > + GC08A3_EXP_MARGIN;
> > +__v4l2_ctrl_modify_range(gc08a3->exposure,
> > + gc08a3->exposure->minimum,
> > + exposure_max, gc08a3->exposure->step,
> > + exposure_max);
> > +}
> > +
> > +/*
> > + * Applying V4L2 control value only happens
> > + * when power is up for streaming
> > + */
> > +if (!pm_runtime_get_if_in_use(gc08a3->dev))
> > +return 0;
> > +
> > +switch (ctrl->id) {
> > +case V4L2_CID_EXPOSURE:
> > +ret = gc08a3_write_reg(gc08a3, GC08A3_EXP_REG,
> > + GC08A3_REG_VALUE_16BIT, ctrl->val);
> > +break;
> > +
> > +case V4L2_CID_ANALOGUE_GAIN:
> > +ret = gc08a3_write_reg(gc08a3, GC08A3_AGAIN_REG,
> > + GC08A3_REG_VALUE_16BIT, ctrl->val);
> > +break;
> > +
> > +case V4L2_CID_VBLANK:
> > +dev_info(gc08a3->dev, "V4L2_CID_VBLANK:height:%d, V_BLNK:%d\n",
> > + gc08a3->cur_mode->height, ctrl->val);
>
> I would drop this too, or at the very least convert it to dev_dbg().
> There should be no message but debug messages printed by the driver
> to
> the kernel log during normal operation.
>
[mtk]: fixed in patch:v3

> > +ret = gc08a3_write_reg(gc08a3, GC08A3_FL_REG,
> > + GC08A3_REG_VALUE_16BIT,
> > + gc08a3->cur_mode->height + ctrl->val);
> > +break;
> > +
> > +case V4L2_CID_HFLIP:
> > +gc08a3_set_ctrl_hflip(gc08a3, ctrl->val);
> > +break;
> > +
> > +case V4L2_CID_VFLIP:
> > +gc08a3_set_ctrl_vflip(gc08a3, ctrl->val);
> > +break;
> > +
> > +case V4L2_CID_TEST_PATTERN:
> > +ret = gc08a3_test_pattern(gc08a3, ctrl->val);
> > +break;
> > +
> > +default:
> > +break;
> > +}
> > +
> > +pm_runtime_put(gc08a3->dev);
> > +
> > +return ret;
> > +}
> > +
> > +static int get_volatile_ext_ctrl(struct v4l2_ctrl *ctrl)
> > +{
> > +int ret = 0;
> > +struct gc08a3 *gc08a3 =
> > +container_of(ctrl->handler, struct gc08a3, ctrls);
> > +struct i2c_client *client = v4l2_get_subdevdata(&gc08a3->sd);
> > +
> > +dev_dbg(&client->dev, "---- %s, CMD:0x%x\n", __func__, ctrl->id);
> > +switch (ctrl->id) {
> > +default:
> > +dev_info(&client->dev, "[gc08a3] %s, un-support CMD: 0x%x\n",
> > + __func__, ctrl->id);
> > +break;
> > +}
> > +
> > +return ret;
> > +}
> > +
> > +static int gc08a3_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
> > +{
> > +int ret = 0;
> > +
> > +switch (ctrl->id) {
> > +default:
> > +ret = get_volatile_ext_ctrl(ctrl);
> > +break;
> > +}
> > +
> > +return ret;
> > +}
> > +
> > +static int try_ext_ctrl(struct v4l2_ctrl *ctrl)
> > +{
> > +int ret = 0;
> > +struct gc08a3 *gc08a3 =
> > +container_of(ctrl->handler, struct gc08a3, ctrls);
> > +struct i2c_client *client = v4l2_get_subdevdata(&gc08a3->sd);
> > +
> > +switch (ctrl->id) {
> > +default:
> > +dev_dbg(&client->dev,
> > +"[gc08a3] un-handle CMD: 0x%x (%s : %d)\n", ctrl->id,
> > +__func__, __LINE__);
> > +break;
> > +}
> > +
> > +return ret;
> > +}
> > +
> > +static int gc08a3_try_ctrl(struct v4l2_ctrl *ctrl)
> > +{
> > +int ret = 0;
> > +
> > +switch (ctrl->id) {
> > +case V4L2_CID_EXPOSURE:
> > +case V4L2_CID_ANALOGUE_GAIN:
> > +case V4L2_CID_VBLANK:
> > +case V4L2_CID_HFLIP:
> > +case V4L2_CID_VFLIP:
> > +case V4L2_CID_TEST_PATTERN:
> > +return 0;
> > +
> > +default:
> > +ret = try_ext_ctrl(ctrl);
> > +break;
> > +}
> > +
> > +return ret;
> > +}
> > +
> > +static const struct v4l2_ctrl_ops gc08a3_ctrl_ops = {
> > +.g_volatile_ctrl = gc08a3_g_volatile_ctrl,
> > +.try_ctrl = gc08a3_try_ctrl,
>
> Drop the .g_volatile_ctrl() and .try_ctrl() implementations, they
> don't
> do anything, and the operations are optional.
>
[mtk]: fixed in patch:v3

> > +.s_ctrl = gc08a3_set_ctrl,
> > +};
> > +
> > +static int gc08a3_start_streaming(struct gc08a3 *gc08a3)
> > +{
> > +const struct gc08a3_mode *mode;
> > +const struct gc08a3_reg_list *reg_list;
> > +int link_freq_index;
> > +int ret;
> > +
> > +dev_info(gc08a3->dev, "%s ++\n", __func__);
> > +
> > +mutex_lock(&gc08a3->mutex);
> > +
> > +link_freq_index = gc08a3->cur_mode->link_freq_index;
> > +dev_info(gc08a3->dev, "----link_freq_index = %d ",
> link_freq_index);
> > +
> > +reg_list = &link_freq_configs[link_freq_index].reg_list;
> > +ret = gc08a3_write_reg_list(gc08a3, reg_list);
> > +if (ret) {
> > +dev_err(gc08a3->dev, "could not sent common table %d\n", ret);
> > +goto error;
> > +}
> > +
> > +mode = gc08a3->cur_mode;
> > +dev_info(gc08a3->dev, "----write regtbl: mode(id:%d, WxH:%dx%d)",
> > + mode->mode_id, mode->width, mode->height);
> > +reg_list = &mode->reg_list;
> > +
> > +ret = gc08a3_write_reg_list(gc08a3, reg_list);
> > +if (ret < 0) {
> > +dev_err(gc08a3->dev, "could not sent mode table %d\n", ret);
> > +goto error;
> > +}
> > +ret = __v4l2_ctrl_handler_setup(&gc08a3->ctrls);
> > +if (ret < 0) {
> > +dev_err(gc08a3->dev, "could not sync v4l2 controls\n");
> > +goto error;
> > +}
> > +
> > +ret = gc08a3_write_reg(gc08a3, GC08A3_STRAEMING_REG,
> > + GC08A3_REG_VALUE_08BIT, 1);
> > +if (ret < 0) {
> > +dev_err(gc08a3->dev, "write STRAEMING_REG failed: %d\n", ret);
> > +goto error;
> > +}
> > +
> > +mutex_unlock(&gc08a3->mutex);
> > +
> > +dev_info(gc08a3->dev, "%s --\n", __func__);
> > +
> > +return 0;
> > +
> > +error:
> > +mutex_unlock(&gc08a3->mutex);
> > +return ret;
> > +}
> > +
> > +static int gc08a3_stop_streaming(struct gc08a3 *gc08a3)
> > +{
> > +int ret;
> > +
> > +dev_info(gc08a3->dev, "%s ++\n", __func__);
> > +
> > +ret = gc08a3_write_reg(gc08a3, GC08A3_STRAEMING_REG,
> > + GC08A3_REG_VALUE_08BIT, 0);
> > +if (ret < 0)
> > +dev_err(gc08a3->dev, "could not sent stop streaming %d\n", ret);
> > +
> > +dev_info(gc08a3->dev, "%s --\n", __func__);
> > +
> > +return ret;
> > +}
> > +
> > +static int gc08a3_s_stream(struct v4l2_subdev *subdev, int enable)
> > +{
> > +struct gc08a3 *gc08a3 = to_gc08a3(subdev);
> > +int ret;
> > +
> > +if (gc08a3->streaming == enable)
> > +return 0;
> > +
> > +if (enable) {
> > +ret = pm_runtime_resume_and_get(gc08a3->dev);
> > +if (ret < 0)
> > +return ret;
> > +
> > +ret = gc08a3_start_streaming(gc08a3);
> > +if (ret < 0)
> > +goto err_rpm_put;
> > +} else {
> > +ret = gc08a3_stop_streaming(gc08a3);
> > +if (ret < 0)
> > +goto err_rpm_put;
> > +pm_runtime_put(gc08a3->dev);
> > +}
> > +
> > +gc08a3->streaming = enable;
> > +return 0;
> > +
> > +err_rpm_put:
> > +pm_runtime_put(gc08a3->dev);
> > +return ret;
> > +}
> > +
> > +static int gc08a3_g_mbus_config(struct v4l2_subdev *sd, unsigned
> int pad,
> > +struct v4l2_mbus_config *config)
> > +{
> > +config->type = V4L2_MBUS_CSI2_DPHY;
> > +config->bus.mipi_csi2.num_data_lanes = 4;
> > +config->bus.mipi_csi2.flags = 0;
> > +return 0;
> > +}
> > +
> > +static int gc08a3_g_frame_interval(struct v4l2_subdev *subdev,
> > + struct v4l2_subdev_frame_interval *fival)
> > +{
> > +struct gc08a3 *gc08a3 = to_gc08a3(subdev);
> > +
> > +fival->interval.numerator = 1;
> > +fival->interval.denominator = gc08a3->cur_mode->max_framerate /
> 10;
> > +
> > +return 0;
> > +}
> > +
> > +static int
> > +gc08a3_enum_frame_interval(struct v4l2_subdev *subdev,
> > + struct v4l2_subdev_state *sd_state,
> > + struct v4l2_subdev_frame_interval_enum *fie)
> > +{
> > +const struct gc08a3_mode *mode;
> > +
> > +if (fie->index != 0)
> > +return -EINVAL;
> > +
> > +mode = v4l2_find_nearest_size(gc08a3_modes,
> ARRAY_SIZE(gc08a3_modes),
> > + width, height, fie->width, fie->height);
> > +
> > +fie->code = GC08A3_MBUS_CODE;
> > +fie->width = mode->width;
> > +fie->height = mode->height;
> > +fie->interval.numerator = 1;
> > +fie->interval.denominator = mode->max_framerate / 10;
> > +
> > +return 0;
> > +}
> > +
> > +static const struct v4l2_subdev_video_ops gc08a3_video_ops = {
> > +.s_stream = gc08a3_s_stream,
> > +.g_frame_interval = gc08a3_g_frame_interval,
> > +.s_frame_interval = gc08a3_g_frame_interval,
> > +};
> > +
> > +static const struct v4l2_subdev_pad_ops gc08a3_subdev_pad_ops = {
> > +.enum_mbus_code = gc08a3_enum_mbus_code,
> > +.enum_frame_size = gc08a3_enum_frame_size,
> > +.enum_frame_interval = gc08a3_enum_frame_interval,
> > +.get_fmt = gc08a3_get_format,
> > +.set_fmt = gc08a3_set_format,
> > +.get_selection = gc08a3_get_selection,
> > +.init_cfg = gc08a3_entity_init_cfg,
> > +.get_mbus_config = gc08a3_g_mbus_config,
> > +};
> > +
> > +static const struct v4l2_subdev_ops gc08a3_subdev_ops = {
> > +.core = &gc08a3_core_ops,
> > +.video = &gc08a3_video_ops,
> > +.pad = &gc08a3_subdev_pad_ops,
> > +};
> > +
> > +static const struct regmap_config sensor_regmap_config = {
> > +.reg_bits = 16,
> > +.val_bits = 8,
> > +.cache_type = REGCACHE_RBTREE,
> > +};
> > +
> > +static int gc08a3_get_regulators(struct device *dev, struct gc08a3
> *gc08a3)
> > +{
> > +unsigned int i;
> > +
> > +for (i = 0; i < GC08A3_NUM_SUPPLIES; i++)
> > +gc08a3->supplies[i].supply = gc08a3_supply_name[i];
> > +
> > +return devm_regulator_bulk_get(dev, GC08A3_NUM_SUPPLIES,
> > + gc08a3->supplies);
> > +}
> > +
> > +static int gc08a3_parse_fwnode(struct device *dev)
> > +{
> > +struct fwnode_handle *endpoint;
> > +struct v4l2_fwnode_endpoint bus_cfg = {
> > +.bus_type = V4L2_MBUS_CSI2_DPHY,
> > +};
> > +unsigned int i, j;
> > +int ret;
> > +
> > +endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
> > +if (!endpoint) {
> > +dev_err(dev, "endpoint node not found\n");
> > +return -EINVAL;
> > +}
> > +
> > +ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg);
> > +if (ret) {
> > +dev_err(dev, "parsing endpoint node failed\n");
> > +goto done;
> > +}
> > +
> > +if (!bus_cfg.nr_of_link_frequencies) {
> > +dev_err(dev, "no link frequencies defined");
> > +ret = -EINVAL;
> > +goto done;
> > +}
> > +
> > +for (i = 0; i < ARRAY_SIZE(link_freq_menu_items); i++) {
> > +for (j = 0; j < bus_cfg.nr_of_link_frequencies; j++) {
> > +if (link_freq_menu_items[i] ==
> > + bus_cfg.link_frequencies[j])
> > +break;
> > +}
> > +
> > +if (j == bus_cfg.nr_of_link_frequencies) {
> > +dev_err(dev,
> > +"no link frequency %lld supported, please check DT",
> > +link_freq_menu_items[i]);
> > +ret = -EINVAL;
> > +goto done;
> > +}
> > +}
> > +
> > +done:
> > +v4l2_fwnode_endpoint_free(&bus_cfg);
> > +fwnode_handle_put(endpoint);
> > +return ret;
> > +}
> > +
> > +static int __maybe_unused gc08a3_suspend(struct device *dev)
> > +{
> > +struct i2c_client *client = to_i2c_client(dev);
> > +struct v4l2_subdev *sd = i2c_get_clientdata(client);
> > +struct gc08a3 *gc08a3 = to_gc08a3(sd);
> > +
> > +if (gc08a3->streaming)
> > +gc08a3_stop_streaming(gc08a3);
> > +
> > +return 0;
> > +}
> > +
> > +static int __maybe_unused gc08a3_resume(struct device *dev)
> > +{
> > +struct i2c_client *client = to_i2c_client(dev);
> > +struct v4l2_subdev *sd = i2c_get_clientdata(client);
> > +struct gc08a3 *gc08a3 = to_gc08a3(sd);
> > +int ret;
> > +
> > +if (gc08a3->streaming) {
> > +ret = gc08a3_start_streaming(gc08a3);
> > +if (ret)
> > +goto error;
> > +}
> > +
> > +return 0;
> > +
> > +error:
> > +gc08a3_stop_streaming(gc08a3);
> > +gc08a3->streaming = 0;
> > +return ret;
> > +}
>
> No need for system suspend/resume operations. Please read
> Documentation/driver-api/media/camera-sensor.rst in the master branch
> of
> git://linuxtv.org/media_stage.git for an explanation.
>
[mtk]: fixed in patch:v3

> > +
> > +static int gc08a3_init_controls(struct gc08a3 *gc08a3)
> > +{
> > +struct i2c_client *client = v4l2_get_subdevdata(&gc08a3->sd);
> > +const struct v4l2_ctrl_ops *ops = &gc08a3_ctrl_ops;
> > +struct v4l2_fwnode_device_properties props;
> > +struct v4l2_ctrl_handler *ctrl_hdlr;
> > +s64 exposure_max, h_blank;
> > +int ret;
> > +
> > +ctrl_hdlr = &gc08a3->ctrls;
> > +ret = v4l2_ctrl_handler_init(ctrl_hdlr, 10);
> > +if (ret)
> > +return ret;
> > +
> > +ctrl_hdlr->lock = &gc08a3->mutex;
> > +
> > +gc08a3->link_freq =
> > +v4l2_ctrl_new_int_menu(ctrl_hdlr,
> > + &gc08a3_ctrl_ops,
> > + V4L2_CID_LINK_FREQ,
> > + ARRAY_SIZE(link_freq_menu_items) - 1,
> > + 0, link_freq_menu_items);
> > +if (gc08a3->link_freq)
> > +gc08a3->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> > +
> > +gc08a3->pixel_rate =
> > +v4l2_ctrl_new_std(ctrl_hdlr,
> > + &gc08a3_ctrl_ops,
> > + V4L2_CID_PIXEL_RATE, 0,
> > + to_pixel_rate(GC08A3_LINK_FREQ_336MHZ_CFG), 1,
> > + to_pixel_rate(GC08A3_LINK_FREQ_336MHZ_CFG));
> > +
> > +gc08a3->vblank =
> > +v4l2_ctrl_new_std(ctrl_hdlr,
> > + &gc08a3_ctrl_ops, V4L2_CID_VBLANK,
> > + gc08a3->cur_mode->vts_min - gc08a3->cur_mode->height,
> > + GC08A3_VTS_MAX - gc08a3->cur_mode->height, 1,
> > + gc08a3->cur_mode->vts_def - gc08a3->cur_mode->height);
> > +
> > +h_blank = gc08a3->cur_mode->hts - gc08a3->cur_mode->width;
> > +gc08a3->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &gc08a3_ctrl_ops,
> > + V4L2_CID_HBLANK, h_blank, h_blank, 1,
> > + h_blank);
> > +if (gc08a3->hblank)
> > +gc08a3->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> > +
> > +v4l2_ctrl_new_std(ctrl_hdlr, &gc08a3_ctrl_ops,
> > + V4L2_CID_ANALOGUE_GAIN, GC08A3_AGAIN_MIN,
> > + GC08A3_AGAIN_MAX, GC08A3_AGAIN_STEP,
> > + GC08A3_AGAIN_MIN);
> > +
> > +exposure_max = gc08a3->cur_mode->vts_def - GC08A3_EXP_MARGIN;
> > +gc08a3->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &gc08a3_ctrl_ops,
> > + V4L2_CID_EXPOSURE, GC08A3_EXP_MIN,
> > + exposure_max, GC08A3_EXP_STEP,
> > + exposure_max);
> > +
> > +v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &gc08a3_ctrl_ops,
> > + V4L2_CID_TEST_PATTERN,
> > + ARRAY_SIZE(gc08a3_test_pattern_menu) - 1,
> > + 0, 0, gc08a3_test_pattern_menu);
> > +
> > +v4l2_ctrl_new_std(ctrl_hdlr, &gc08a3_ctrl_ops, V4L2_CID_HFLIP, 0,
> > + 1, 1, 0);
> > +
> > +v4l2_ctrl_new_std(ctrl_hdlr, &gc08a3_ctrl_ops, V4L2_CID_VFLIP, 0,
> > + 1, 1, 0);
> > +
> > +/* register properties to fwnode (e.g. rotation, orientation) */
> > +ret = v4l2_fwnode_device_parse(&client->dev, &props);
> > +if (ret)
> > +goto error_ctrls;
> > +
> > +ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, ops, &props);
> > +if (ret)
> > +goto error_ctrls;
> > +
> > +if (ctrl_hdlr->error) {
> > +ret = ctrl_hdlr->error;
> > +goto error_ctrls;
> > +}
> > +
> > +gc08a3->sd.ctrl_handler = ctrl_hdlr;
> > +
> > +return 0;
> > +
> > +error_ctrls:
> > +v4l2_ctrl_handler_free(ctrl_hdlr);
> > +
> > +return ret;
> > +}
> > +
> > +static int gc08a3_probe(struct i2c_client *client)
> > +{
> > +struct device *dev = &client->dev;
> > +struct gc08a3 *gc08a3;
> > +int ret;
> > +
> > +dev_info(dev, "--- %s +", __func__);
> > +
> > +ret = gc08a3_parse_fwnode(dev);
> > +if (ret)
> > +return ret;
> > +
> > +gc08a3 = devm_kzalloc(dev, sizeof(*gc08a3), GFP_KERNEL);
> > +if (!gc08a3)
> > +return -ENOMEM;
> > +
> > +gc08a3->dev = dev;
> > +
> > +gc08a3->xclk = devm_clk_get(dev, NULL);
> > +if (IS_ERR(gc08a3->xclk)) {
> > +dev_err(dev, "could not get xclk\n");
> > +return PTR_ERR(gc08a3->xclk);
> > +}
> > +
> > +ret = clk_set_rate(gc08a3->xclk, GC08A3_DEFAULT_CLK_FREQ);
> > +if (ret) {
> > +dev_err(dev, "could not set xclk frequency\n");
> > +return ret;
> > +}
> > +
> > +ret = gc08a3_get_regulators(dev, gc08a3);
> > +if (ret < 0) {
> > +dev_err(dev, "cannot get regulators\n");
> > +return ret;
> > +}
> > +
> > +gc08a3->enable_gpio = devm_gpiod_get(dev, "enable",
> GPIOD_OUT_LOW);
> > +if (IS_ERR(gc08a3->enable_gpio)) {
> > +dev_err(dev, "cannot get enable gpio\n");
> > +return PTR_ERR(gc08a3->enable_gpio);
> > +}
> > +
> > +gc08a3->regmap = devm_regmap_init_i2c(client,
> &sensor_regmap_config);
> > +if (IS_ERR(gc08a3->regmap)) {
> > +dev_err(dev, "regmap init failed\n");
> > +return PTR_ERR(gc08a3->regmap);
> > +}
> > +
> > +v4l2_i2c_subdev_init(&gc08a3->sd, client, &gc08a3_subdev_ops);
> > +
> > +gc08a3_power_on(gc08a3->dev);
> > +
> > +ret = gc08a3_identify_module(gc08a3);
> > +if (ret) {
> > +dev_err(&client->dev, "failed to find sensor: %d\n", ret);
> > +gc08a3_power_off(gc08a3->dev);
> > +return ret;
> > +}
> > +
> > +mutex_init(&gc08a3->mutex);
> > +gc08a3->cur_mode = &gc08a3_modes[0];
> > +
> > +ret = gc08a3_init_controls(gc08a3);
> > +if (ret) {
> > +dev_err(&client->dev, "failed to init controls: %d", ret);
> > +goto free_ctrl;
> > +}
> > +
> > +gc08a3->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> > +gc08a3->pad.flags = MEDIA_PAD_FL_SOURCE;
> > +gc08a3->sd.dev = &client->dev;
> > +gc08a3->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
> > +
> > +dev_dbg(&client->dev, "gc08a3->sd.name: %s, dev->of_node->name:
> %s\n",
> > +gc08a3->sd.name, dev->of_node->name);
> > +if (V4L2_SUBDEV_NAME_SIZE - strlen(gc08a3->sd.name) - 2 <
> > + strlen(dev->of_node->name)) {
> > +dev_err(&client->dev,
> > +"the string length of (sd.name + of_node->name) is too long.\n");
> > +return -EINVAL;
> > +}
> > +strncat(gc08a3->sd.name, " ", 1);
> > +strncat(gc08a3->sd.name, dev->of_node->name,
> > +V4L2_SUBDEV_NAME_SIZE - strlen(gc08a3->sd.name) - 2);
> > +dev_dbg(&client->dev, "after: gc08a3->sd.name: %s\n", gc08a3-
> >sd.name);
> > +
> > +ret = media_entity_pads_init(&gc08a3->sd.entity, 1, &gc08a3->pad);
> > +if (ret < 0) {
> > +dev_err(dev, "could not register media entity\n");
> > +goto free_ctrl;
> > +}
> > +
> > +ret = v4l2_async_register_subdev_sensor(&gc08a3->sd);
> > +if (ret < 0) {
> > +dev_err(dev, "could not register v4l2 device\n");
> > +goto free_entity;
> > +}
> > +
> > +pm_runtime_set_active(gc08a3->dev);
> > +pm_runtime_enable(gc08a3->dev);
> > +pm_runtime_idle(gc08a3->dev);
>
> Please see the imx219 driver for an example of how to correctly
> implement runtime PM support. Please also use PM runtime autosuspend.
>
[mtk]: fixed in patch:v3

> > +
> > +dev_info(dev, "--- %s -", __func__);
> > +
> > +return 0;
> > +
> > +free_entity:
> > +media_entity_cleanup(&gc08a3->sd.entity);
> > +free_ctrl:
> > +mutex_destroy(&gc08a3->mutex);
> > +v4l2_ctrl_handler_free(&gc08a3->ctrls);
> > +pm_runtime_disable(gc08a3->dev);
> > +
> > +return ret;
> > +}
> > +
> > +static void gc08a3_remove(struct i2c_client *client)
> > +{
> > +struct v4l2_subdev *sd = i2c_get_clientdata(client);
> > +struct gc08a3 *gc08a3 = to_gc08a3(sd);
> > +
> > +v4l2_async_unregister_subdev(&gc08a3->sd);
> > +media_entity_cleanup(&gc08a3->sd.entity);
> > +v4l2_ctrl_handler_free(&gc08a3->ctrls);
> > +
> > +pm_runtime_disable(&client->dev);
> > +pm_runtime_set_suspended(&client->dev);
> > +
> > +mutex_destroy(&gc08a3->mutex);
> > +}
> > +
> > +static const struct of_device_id gc08a3_of_match[] = {
> > +{ .compatible = "GalaxyCore,gc08a3" },
> > +{}
> > +};
> > +MODULE_DEVICE_TABLE(of, gc08a3_of_match);
> > +
> > +static const struct dev_pm_ops gc08a3_pm_ops = {
> > +SET_SYSTEM_SLEEP_PM_OPS(gc08a3_suspend, gc08a3_resume)
> > +SET_RUNTIME_PM_OPS(gc08a3_power_off, gc08a3_power_on, NULL)
> > +};
> > +
> > +static struct i2c_driver gc08a3_i2c_driver = {
> > +.driver = {
> > +.of_match_table = gc08a3_of_match,
> > +.pm = &gc08a3_pm_ops,
> > +.name = "gc08a3",
> > +},
> > +.probe_new = gc08a3_probe,
> > +.remove = gc08a3_remove,
> > +};
> > +
> > +module_i2c_driver(gc08a3_i2c_driver);
> > +
> > +MODULE_DESCRIPTION("GalaxyCore gc08a3 Camera driver");
> > +MODULE_AUTHOR("Zhi Mao <[email protected]>");
> > +MODULE_LICENSE("GPL");
>
> --
> Regards,
>
> Laurent Pinchart