diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx708.yaml b/Documentation/devicetree/bindings/media/i2c/sony,imx708.yaml index 286aad2e8c697..bc346730e1f31 100644 --- a/Documentation/devicetree/bindings/media/i2c/sony,imx708.yaml +++ b/Documentation/devicetree/bindings/media/i2c/sony,imx708.yaml @@ -4,10 +4,11 @@ $id: http://devicetree.org/schemas/media/i2c/sony,imx708.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Sony 1/2.3-Inch 12Mpixel CMOS Digital Image Sensor +title: Sony IMX708 Sensor maintainers: - Raspberry Pi Kernel Maintenance + - Jai Luthra description: |- The Sony IMX708 is a 1/2.3-inch CMOS active pixel digital image sensor @@ -26,11 +27,6 @@ properties: clocks: maxItems: 1 - clock-names: - description: Input clock (6 to 27 MHz) - items: - - const: inck - vdig-supply: description: Digital I/O voltage supply, 1.1 volts @@ -48,13 +44,12 @@ properties: Digital core voltage supply, 1.8 volts reset-gpios: - description: Sensor reset (XCLR) GPIO + description: Sensor RESETN (XCLR) GPIO maxItems: 1 port: $ref: /schemas/graph.yaml#/$defs/port-base - description: | - Video output port + additionalProperties: false properties: endpoint: @@ -63,7 +58,7 @@ properties: properties: data-lanes: - anyOf: + oneOf: - items: - const: 1 - const: 2 @@ -73,19 +68,17 @@ properties: - const: 3 - const: 4 - link-frequencies: true - required: - data-lanes - link-frequencies - additionalProperties: false + required: + - endpoint required: - compatible - reg - clocks - - clock-names - vdig-supply - vana1-supply - vana2-supply @@ -102,12 +95,11 @@ examples: #address-cells = <1>; #size-cells = <0>; - imx708: camera-sensor@1a { + camera@1a { compatible = "sony,imx708"; reg = <0x1a>; clocks = <&clk 90>; - clock-names = "inck"; vdig-supply = <&camera_vdig>; vana1-supply = <&camera_vana1>; diff --git a/MAINTAINERS b/MAINTAINERS index abb7f5e4ebbdd..b36a204e2cf89 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -25340,6 +25340,14 @@ T: git git://linuxtv.org/media_tree.git F: Documentation/devicetree/bindings/media/i2c/sony,imx708.yaml F: drivers/media/i2c/imx708.c +SONY IMX708 SENSOR DRIVER +M: Raspberry Pi Kernel Maintenance +M: Jai Luthra +L: linux-media@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/media/i2c/sony,imx708.yaml +F: drivers/media/i2c/imx708.c + SONY MEMORYSTICK SUBSYSTEM M: Maxim Levitsky M: Alex Dubov diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 32f0c5793e2ab..989b6a7d30420 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -360,6 +360,17 @@ config VIDEO_IMX708 To compile this driver as a module, choose M here: the module will be called imx708. +config VIDEO_IMX708 + tristate "Sony IMX708 sensor support" + depends on GPIOLIB + select V4L2_CCI_I2C + help + This is a Video4Linux2 sensor driver for the Sony + IMX708 camera. + + To compile this driver as a module, choose M here: the + module will be called imx708. + config VIDEO_MAX9271_LIB tristate diff --git a/drivers/media/i2c/imx708.c b/drivers/media/i2c/imx708.c index 23af2f3f08bfa..052474befc5e4 100644 --- a/drivers/media/i2c/imx708.c +++ b/drivers/media/i2c/imx708.c @@ -1,12 +1,10 @@ // SPDX-License-Identifier: GPL-2.0 /* - * A V4L2 driver for Sony IMX708 cameras. - * Copyright (C) 2022, Raspberry Pi Ltd + * A V4L2 driver for Sony IMX708 * - * Based on Sony imx477 camera driver - * Copyright (C) 2020 Raspberry Pi Ltd + * Copyright (C) 2026 Ideas on Board Oy + * Copyright (C) 2022 Raspberry Pi Ltd */ -#include #include #include #include @@ -14,79 +12,102 @@ #include #include #include +#include +#include +#include +#include #include -#include #include #include #include - -/* - * Parameter to adjust Quad Bayer re-mosaic broken line correction - * strength, used in full-resolution mode only. Set zero to disable. - */ -static int qbc_adjust = 2; -module_param(qbc_adjust, int, 0644); -MODULE_PARM_DESC(qbc_adjust, "Quad Bayer broken line correction strength [0,2-5]"); - -#define IMX708_REG_VALUE_08BIT 1 -#define IMX708_REG_VALUE_16BIT 2 +#include /* Chip ID */ -#define IMX708_REG_CHIP_ID 0x0016 +#define IMX708_REG_CHIP_ID CCI_REG16(0x0016) #define IMX708_CHIP_ID 0x0708 -#define IMX708_REG_MODE_SELECT 0x0100 +#define IMX708_REG_MODE_SELECT CCI_REG8(0x0100) #define IMX708_MODE_STANDBY 0x00 #define IMX708_MODE_STREAMING 0x01 -#define IMX708_REG_ORIENTATION 0x101 +#define IMX708_REG_ORIENTATION CCI_REG8(0x0101) #define IMX708_INCLK_FREQ 24000000 -/* Default initial pixel rate, will get updated for each mode. */ -#define IMX708_INITIAL_PIXEL_RATE 590000000 +/* Pixel rate */ +#define IMX708_PIXEL_RATE 595200000 + +/* PLL */ +#define IMX708_REG_IVT_PXCK_DIV CCI_REG8(0x0301) +#define IMX708_REG_IVT_SYCK_DIV CCI_REG8(0x0303) +#define IMX708_REG_IVT_PREPLLCK_DIV CCI_REG8(0x0305) +#define IMX708_REG_IVT_PLL_MPY CCI_REG16(0x0306) +#define IMX708_REG_IOP_SYCK_DIV CCI_REG8(0x030b) +#define IMX708_REG_IOP_PREPLLCK_DIV CCI_REG8(0x030d) +#define IMX708_REG_IOP_PLL_MPY CCI_REG16(0x030e) +#define IMX708_REG_PLL_MULT_DRIV CCI_REG8(0x0310) /* V_TIMING internal */ -#define IMX708_REG_FRAME_LENGTH 0x0340 +#define IMX708_REG_FRAME_LENGTH CCI_REG16(0x0340) #define IMX708_FRAME_LENGTH_MAX 0xffff +#define IMX708_REG_LINE_LENGTH CCI_REG16(0x0342) +#define IMX708_LINE_LENGTH 15648 +#define IMX708_VBLANK_MIN 58 + +/* Imaging area */ +#define IMX708_REG_X_ADD_STA CCI_REG16(0x0344) +#define IMX708_REG_X_ADD_END CCI_REG16(0x0348) +#define IMX708_REG_Y_ADD_STA CCI_REG16(0x0346) +#define IMX708_REG_Y_ADD_END CCI_REG16(0x034a) +#define IMX708_REG_DIG_CROP_X_OFFSET CCI_REG16(0x0408) +#define IMX708_REG_DIG_CROP_Y_OFFSET CCI_REG16(0x040a) +#define IMX708_REG_DIG_CROP_WIDTH CCI_REG16(0x040c) +#define IMX708_REG_DIG_CROP_HEIGHT CCI_REG16(0x040e) +#define IMX708_REG_X_OUTPUT_SIZE CCI_REG16(0x034c) +#define IMX708_REG_Y_OUTPUT_SIZE CCI_REG16(0x034e) + +#define IMX708_REG_ACROPLP_EN CCI_REG8(0x32df) +#define IMX708_REG_BINNING_MODE CCI_REG8(0x0900) +#define IMX708_REG_BINNING_TYPE CCI_REG8(0x0901) +#define IMX708_REG_BINNING_WEIGHT CCI_REG8(0x0902) +#define IMX708_REG_BINNING_PRIORITY_H CCI_REG8(0x3200) +#define IMX708_REG_BINNING_PRIORITY_V CCI_REG8(0x3201) /* Long exposure multiplier */ #define IMX708_LONG_EXP_SHIFT_MAX 7 -#define IMX708_LONG_EXP_SHIFT_REG 0x3100 +#define IMX708_LONG_EXP_SHIFT_REG CCI_REG8(0x3100) /* Exposure control */ -#define IMX708_REG_EXPOSURE 0x0202 +#define IMX708_REG_EXPOSURE CCI_REG16(0x0202) #define IMX708_EXPOSURE_OFFSET 48 #define IMX708_EXPOSURE_DEFAULT 0x640 #define IMX708_EXPOSURE_STEP 1 -#define IMX708_EXPOSURE_MIN 1 -#define IMX708_EXPOSURE_MAX (IMX708_FRAME_LENGTH_MAX - \ - IMX708_EXPOSURE_OFFSET) +#define IMX708_EXPOSURE_MIN 8 /* Analog gain control */ -#define IMX708_REG_ANALOG_GAIN 0x0204 -#define IMX708_ANA_GAIN_MIN 112 +#define IMX708_REG_ANALOG_GAIN CCI_REG16(0x0204) +#define IMX708_ANA_GAIN_MIN 0 #define IMX708_ANA_GAIN_MAX 960 #define IMX708_ANA_GAIN_STEP 1 #define IMX708_ANA_GAIN_DEFAULT IMX708_ANA_GAIN_MIN /* Digital gain control */ -#define IMX708_REG_DIGITAL_GAIN 0x020e +#define IMX708_REG_DIGITAL_GAIN CCI_REG16(0x020e) #define IMX708_DGTL_GAIN_MIN 0x0100 #define IMX708_DGTL_GAIN_MAX 0xffff #define IMX708_DGTL_GAIN_DEFAULT 0x0100 #define IMX708_DGTL_GAIN_STEP 1 /* Colour balance controls */ -#define IMX708_REG_COLOUR_BALANCE_RED 0x0b90 -#define IMX708_REG_COLOUR_BALANCE_BLUE 0x0b92 +#define IMX708_REG_COLOUR_BALANCE_RED CCI_REG16(0x0b90) +#define IMX708_REG_COLOUR_BALANCE_BLUE CCI_REG16(0x0b92) #define IMX708_COLOUR_BALANCE_MIN 0x01 #define IMX708_COLOUR_BALANCE_MAX 0xffff #define IMX708_COLOUR_BALANCE_STEP 0x01 #define IMX708_COLOUR_BALANCE_DEFAULT 0x100 /* Test Pattern Control */ -#define IMX708_REG_TEST_PATTERN 0x0600 +#define IMX708_REG_TEST_PATTERN CCI_REG16(0x0600) #define IMX708_TEST_PATTERN_DISABLE 0 #define IMX708_TEST_PATTERN_SOLID_COLOR 1 #define IMX708_TEST_PATTERN_COLOR_BARS 2 @@ -94,103 +115,64 @@ MODULE_PARM_DESC(qbc_adjust, "Quad Bayer broken line correction strength [0,2-5] #define IMX708_TEST_PATTERN_PN9 4 /* Test pattern colour components */ -#define IMX708_REG_TEST_PATTERN_R 0x0602 -#define IMX708_REG_TEST_PATTERN_GR 0x0604 -#define IMX708_REG_TEST_PATTERN_B 0x0606 -#define IMX708_REG_TEST_PATTERN_GB 0x0608 +#define IMX708_REG_TEST_PATTERN_R CCI_REG16(0x0602) +#define IMX708_REG_TEST_PATTERN_GR CCI_REG16(0x0604) +#define IMX708_REG_TEST_PATTERN_B CCI_REG16(0x0606) +#define IMX708_REG_TEST_PATTERN_GB CCI_REG16(0x0608) #define IMX708_TEST_PATTERN_COLOUR_MIN 0 #define IMX708_TEST_PATTERN_COLOUR_MAX 0x0fff #define IMX708_TEST_PATTERN_COLOUR_STEP 1 -#define IMX708_REG_BASE_SPC_GAINS_L 0x7b10 -#define IMX708_REG_BASE_SPC_GAINS_R 0x7c00 +#define IMX708_REG_BASE_SPC_GAINS_L CCI_REG8(0x7b10) +#define IMX708_REG_BASE_SPC_GAINS_R CCI_REG8(0x7c00) -/* HDR exposure ratio (long:med == med:short) */ -#define IMX708_HDR_EXPOSURE_RATIO 4 -#define IMX708_REG_MID_EXPOSURE 0x3116 -#define IMX708_REG_SHT_EXPOSURE 0x0224 -#define IMX708_REG_MID_ANALOG_GAIN 0x3118 -#define IMX708_REG_SHT_ANALOG_GAIN 0x0216 +/* Middle and short exposure */ +#define IMX708_REG_MID_EXPOSURE CCI_REG16(0x3116) +#define IMX708_REG_SHT_EXPOSURE CCI_REG16(0x0224) +#define IMX708_REG_MID_ANALOG_GAIN CCI_REG16(0x3118) +#define IMX708_REG_MID_DIGITAL_GAIN CCI_REG16(0x311a) +#define IMX708_REG_SHT_ANALOG_GAIN CCI_REG16(0x0216) +#define IMX708_REG_SHT_DIGITAL_GAIN CCI_REG16(0x0218) -#define IMX708_REG_CLKLANE_BLANK 0x3220 +#define IMX708_REG_CLKLANE_BLANK CCI_REG8(0x3220) #define IMX708_CLKLANE_BLANK_NONCONT BIT(0) /* QBC Re-mosaic broken line correction registers */ -#define IMX708_LPF_INTENSITY_EN 0xC428 +#define IMX708_REG_QBC_RMSC_EN CCI_REG8(0x32d5) +#define IMX708_REG_LPF_INTENSITY_EN CCI_REG8(0xc428) #define IMX708_LPF_INTENSITY_ENABLED 0x00 #define IMX708_LPF_INTENSITY_DISABLED 0x01 -#define IMX708_LPF_INTENSITY 0xC429 +#define IMX708_REG_LPF_INTENSITY CCI_REG8(0xc429) +#define IMX708_LPF_INTENSITY_DEFAULT 2 -/* - * Metadata buffer holds a variety of data, all sent with the same VC/DT (0x12). - * It comprises two scanlines (of up to 5760 bytes each, for 4608 pixels) - * of embedded data, one line of PDAF data, and two lines of AE-HIST data - * (AE histograms are valid for HDR mode and empty in non-HDR modes). - */ -#define IMX708_EMBEDDED_LINE_WIDTH (5 * 5760) -#define IMX708_NUM_EMBEDDED_LINES 1 +/* AE HIST */ +#define IMX708_REG_AEHIST_AUTO_THRESH CCI_REG16(0x3360) +#define IMX708_REG_AEHIST1_AREA_WIDTH CCI_REG16(0x3366) +#define IMX708_REG_AEHIST1_AREA_HEIGHT CCI_REG16(0x3368) enum pad_types { - IMAGE_PAD, - METADATA_PAD, - NUM_PADS + IMX708_SOURCE_PAD, + IMX708_NUM_PADS }; /* IMX708 native and active pixel array size. */ -#define IMX708_NATIVE_WIDTH 4640U -#define IMX708_NATIVE_HEIGHT 2658U -#define IMX708_PIXEL_ARRAY_LEFT 16U -#define IMX708_PIXEL_ARRAY_TOP 24U -#define IMX708_PIXEL_ARRAY_WIDTH 4608U -#define IMX708_PIXEL_ARRAY_HEIGHT 2592U - -struct imx708_reg { - u16 address; - u8 val; +static const struct v4l2_rect imx708_native_area = { + .top = 0, + .left = 0, + .width = 4640, + .height = 2658, }; -struct imx708_reg_list { - unsigned int num_of_regs; - const struct imx708_reg *regs; +static const struct v4l2_rect imx708_active_area = { + .top = 24, + .left = 16, + .width = 4608, + .height = 2592, }; -/* Mode : resolution and related config&values */ -struct imx708_mode { - /* Frame width */ - unsigned int width; - - /* Frame height */ - unsigned int height; - - /* H-timing in pixels */ - unsigned int line_length_pix; - - /* Analog crop rectangle. */ - struct v4l2_rect crop; - - /* Highest possible framerate. */ - unsigned int vblank_min; - - /* Default framerate. */ - unsigned int vblank_default; - - /* Default register values */ - struct imx708_reg_list reg_list; - - /* Not all modes have the same pixel rate. */ - u64 pixel_rate; - - /* Not all modes have the same minimum exposure. */ - u32 exposure_lines_min; - - /* Not all modes have the same exposure lines step. */ - u32 exposure_lines_step; - - /* HDR flag, used for checking if the current mode is HDR */ - bool hdr; - - /* Quad Bayer Re-mosaic flag */ - bool remosaic; +struct imx708_reg_list { + unsigned int num_of_regs; + const struct cci_reg_sequence *regs; }; /* Default PDAF pixel correction gains */ @@ -213,19 +195,16 @@ static const s64 link_freqs[] = { }; /* 450MHz is the nominal "default" link frequency */ -static const struct imx708_reg link_450Mhz_regs[] = { - {0x030E, 0x01}, - {0x030F, 0x2c}, +static const struct cci_reg_sequence link_450Mhz_regs[] = { + { IMX708_REG_IOP_PLL_MPY, 0x012c }, }; -static const struct imx708_reg link_447Mhz_regs[] = { - {0x030E, 0x01}, - {0x030F, 0x2a}, +static const struct cci_reg_sequence link_447Mhz_regs[] = { + { IMX708_REG_IOP_PLL_MPY, 0x012a }, }; -static const struct imx708_reg link_453Mhz_regs[] = { - {0x030E, 0x01}, - {0x030F, 0x2e}, +static const struct cci_reg_sequence link_453Mhz_regs[] = { + { IMX708_REG_IOP_PLL_MPY, 0x012e }, }; static const struct imx708_reg_list link_freq_regs[] = { @@ -243,533 +222,110 @@ static const struct imx708_reg_list link_freq_regs[] = { }, }; -static const struct imx708_reg mode_common_regs[] = { - {0x0100, 0x00}, - {0x0136, 0x18}, - {0x0137, 0x00}, - {0x33F0, 0x02}, - {0x33F1, 0x05}, - {0x3062, 0x00}, - {0x3063, 0x12}, - {0x3068, 0x00}, - {0x3069, 0x12}, - {0x306A, 0x00}, - {0x306B, 0x30}, - {0x3076, 0x00}, - {0x3077, 0x30}, - {0x3078, 0x00}, - {0x3079, 0x30}, - {0x5E54, 0x0C}, - {0x6E44, 0x00}, - {0xB0B6, 0x01}, - {0xE829, 0x00}, - {0xF001, 0x08}, - {0xF003, 0x08}, - {0xF00D, 0x10}, - {0xF00F, 0x10}, - {0xF031, 0x08}, - {0xF033, 0x08}, - {0xF03D, 0x10}, - {0xF03F, 0x10}, - {0x0112, 0x0A}, - {0x0113, 0x0A}, - {0x0114, 0x01}, - {0x0B8E, 0x01}, - {0x0B8F, 0x00}, - {0x0B94, 0x01}, - {0x0B95, 0x00}, - {0x3400, 0x01}, - {0x3478, 0x01}, - {0x3479, 0x1c}, - {0x3091, 0x01}, - {0x3092, 0x00}, - {0x3419, 0x00}, - {0xBCF1, 0x02}, - {0x3094, 0x01}, - {0x3095, 0x01}, - {0x3362, 0x00}, - {0x3363, 0x00}, - {0x3364, 0x00}, - {0x3365, 0x00}, - {0x0138, 0x01}, -}; - -/* 10-bit. */ -static const struct imx708_reg mode_4608x2592_regs[] = { - {0x0342, 0x3D}, - {0x0343, 0x20}, - {0x0340, 0x0A}, - {0x0341, 0x59}, - {0x0344, 0x00}, - {0x0345, 0x00}, - {0x0346, 0x00}, - {0x0347, 0x00}, - {0x0348, 0x11}, - {0x0349, 0xFF}, - {0x034A, 0X0A}, - {0x034B, 0x1F}, - {0x0220, 0x62}, - {0x0222, 0x01}, - {0x0900, 0x00}, - {0x0901, 0x11}, - {0x0902, 0x0A}, - {0x3200, 0x01}, - {0x3201, 0x01}, - {0x32D5, 0x01}, - {0x32D6, 0x00}, - {0x32DB, 0x01}, - {0x32DF, 0x00}, - {0x350C, 0x00}, - {0x350D, 0x00}, - {0x0408, 0x00}, - {0x0409, 0x00}, - {0x040A, 0x00}, - {0x040B, 0x00}, - {0x040C, 0x12}, - {0x040D, 0x00}, - {0x040E, 0x0A}, - {0x040F, 0x20}, - {0x034C, 0x12}, - {0x034D, 0x00}, - {0x034E, 0x0A}, - {0x034F, 0x20}, - {0x0301, 0x05}, - {0x0303, 0x02}, - {0x0305, 0x02}, - {0x0306, 0x00}, - {0x0307, 0x7C}, - {0x030B, 0x02}, - {0x030D, 0x04}, - {0x0310, 0x01}, - {0x3CA0, 0x00}, - {0x3CA1, 0x64}, - {0x3CA4, 0x00}, - {0x3CA5, 0x00}, - {0x3CA6, 0x00}, - {0x3CA7, 0x00}, - {0x3CAA, 0x00}, - {0x3CAB, 0x00}, - {0x3CB8, 0x00}, - {0x3CB9, 0x08}, - {0x3CBA, 0x00}, - {0x3CBB, 0x00}, - {0x3CBC, 0x00}, - {0x3CBD, 0x3C}, - {0x3CBE, 0x00}, - {0x3CBF, 0x00}, - {0x0202, 0x0A}, - {0x0203, 0x29}, - {0x0224, 0x01}, - {0x0225, 0xF4}, - {0x3116, 0x01}, - {0x3117, 0xF4}, - {0x0204, 0x00}, - {0x0205, 0x00}, - {0x0216, 0x00}, - {0x0217, 0x00}, - {0x0218, 0x01}, - {0x0219, 0x00}, - {0x020E, 0x01}, - {0x020F, 0x00}, - {0x3118, 0x00}, - {0x3119, 0x00}, - {0x311A, 0x01}, - {0x311B, 0x00}, - {0x341a, 0x00}, - {0x341b, 0x00}, - {0x341c, 0x00}, - {0x341d, 0x00}, - {0x341e, 0x01}, - {0x341f, 0x20}, - {0x3420, 0x00}, - {0x3421, 0xd8}, - {0x3366, 0x00}, - {0x3367, 0x00}, - {0x3368, 0x00}, - {0x3369, 0x00}, -}; - -static const struct imx708_reg mode_2x2binned_regs[] = { - {0x0342, 0x1E}, - {0x0343, 0x90}, - {0x0340, 0x05}, - {0x0341, 0x38}, - {0x0344, 0x00}, - {0x0345, 0x00}, - {0x0346, 0x00}, - {0x0347, 0x00}, - {0x0348, 0x11}, - {0x0349, 0xFF}, - {0x034A, 0X0A}, - {0x034B, 0x1F}, - {0x0220, 0x62}, - {0x0222, 0x01}, - {0x0900, 0x01}, - {0x0901, 0x22}, - {0x0902, 0x08}, - {0x3200, 0x41}, - {0x3201, 0x41}, - {0x32D5, 0x00}, - {0x32D6, 0x00}, - {0x32DB, 0x01}, - {0x32DF, 0x00}, - {0x350C, 0x00}, - {0x350D, 0x00}, - {0x0408, 0x00}, - {0x0409, 0x00}, - {0x040A, 0x00}, - {0x040B, 0x00}, - {0x040C, 0x09}, - {0x040D, 0x00}, - {0x040E, 0x05}, - {0x040F, 0x10}, - {0x034C, 0x09}, - {0x034D, 0x00}, - {0x034E, 0x05}, - {0x034F, 0x10}, - {0x0301, 0x05}, - {0x0303, 0x02}, - {0x0305, 0x02}, - {0x0306, 0x00}, - {0x0307, 0x7A}, - {0x030B, 0x02}, - {0x030D, 0x04}, - {0x0310, 0x01}, - {0x3CA0, 0x00}, - {0x3CA1, 0x3C}, - {0x3CA4, 0x00}, - {0x3CA5, 0x3C}, - {0x3CA6, 0x00}, - {0x3CA7, 0x00}, - {0x3CAA, 0x00}, - {0x3CAB, 0x00}, - {0x3CB8, 0x00}, - {0x3CB9, 0x1C}, - {0x3CBA, 0x00}, - {0x3CBB, 0x08}, - {0x3CBC, 0x00}, - {0x3CBD, 0x1E}, - {0x3CBE, 0x00}, - {0x3CBF, 0x0A}, - {0x0202, 0x05}, - {0x0203, 0x08}, - {0x0224, 0x01}, - {0x0225, 0xF4}, - {0x3116, 0x01}, - {0x3117, 0xF4}, - {0x0204, 0x00}, - {0x0205, 0x70}, - {0x0216, 0x00}, - {0x0217, 0x70}, - {0x0218, 0x01}, - {0x0219, 0x00}, - {0x020E, 0x01}, - {0x020F, 0x00}, - {0x3118, 0x00}, - {0x3119, 0x70}, - {0x311A, 0x01}, - {0x311B, 0x00}, - {0x341a, 0x00}, - {0x341b, 0x00}, - {0x341c, 0x00}, - {0x341d, 0x00}, - {0x341e, 0x00}, - {0x341f, 0x90}, - {0x3420, 0x00}, - {0x3421, 0x6c}, - {0x3366, 0x00}, - {0x3367, 0x00}, - {0x3368, 0x00}, - {0x3369, 0x00}, -}; - -static const struct imx708_reg mode_2x2binned_720p_regs[] = { - {0x0342, 0x14}, - {0x0343, 0x60}, - {0x0340, 0x04}, - {0x0341, 0xB6}, - {0x0344, 0x03}, - {0x0345, 0x00}, - {0x0346, 0x01}, - {0x0347, 0xB0}, - {0x0348, 0x0E}, - {0x0349, 0xFF}, - {0x034A, 0x08}, - {0x034B, 0x6F}, - {0x0220, 0x62}, - {0x0222, 0x01}, - {0x0900, 0x01}, - {0x0901, 0x22}, - {0x0902, 0x08}, - {0x3200, 0x41}, - {0x3201, 0x41}, - {0x32D5, 0x00}, - {0x32D6, 0x00}, - {0x32DB, 0x01}, - {0x32DF, 0x01}, - {0x350C, 0x00}, - {0x350D, 0x00}, - {0x0408, 0x00}, - {0x0409, 0x00}, - {0x040A, 0x00}, - {0x040B, 0x00}, - {0x040C, 0x06}, - {0x040D, 0x00}, - {0x040E, 0x03}, - {0x040F, 0x60}, - {0x034C, 0x06}, - {0x034D, 0x00}, - {0x034E, 0x03}, - {0x034F, 0x60}, - {0x0301, 0x05}, - {0x0303, 0x02}, - {0x0305, 0x02}, - {0x0306, 0x00}, - {0x0307, 0x76}, - {0x030B, 0x02}, - {0x030D, 0x04}, - {0x0310, 0x01}, - {0x3CA0, 0x00}, - {0x3CA1, 0x3C}, - {0x3CA4, 0x01}, - {0x3CA5, 0x5E}, - {0x3CA6, 0x00}, - {0x3CA7, 0x00}, - {0x3CAA, 0x00}, - {0x3CAB, 0x00}, - {0x3CB8, 0x00}, - {0x3CB9, 0x0C}, - {0x3CBA, 0x00}, - {0x3CBB, 0x04}, - {0x3CBC, 0x00}, - {0x3CBD, 0x1E}, - {0x3CBE, 0x00}, - {0x3CBF, 0x05}, - {0x0202, 0x04}, - {0x0203, 0x86}, - {0x0224, 0x01}, - {0x0225, 0xF4}, - {0x3116, 0x01}, - {0x3117, 0xF4}, - {0x0204, 0x00}, - {0x0205, 0x70}, - {0x0216, 0x00}, - {0x0217, 0x70}, - {0x0218, 0x01}, - {0x0219, 0x00}, - {0x020E, 0x01}, - {0x020F, 0x00}, - {0x3118, 0x00}, - {0x3119, 0x70}, - {0x311A, 0x01}, - {0x311B, 0x00}, - {0x341a, 0x00}, - {0x341b, 0x00}, - {0x341c, 0x00}, - {0x341d, 0x00}, - {0x341e, 0x00}, - {0x341f, 0x60}, - {0x3420, 0x00}, - {0x3421, 0x48}, - {0x3366, 0x00}, - {0x3367, 0x00}, - {0x3368, 0x00}, - {0x3369, 0x00}, -}; - -static const struct imx708_reg mode_hdr_regs[] = { - {0x0342, 0x14}, - {0x0343, 0x60}, - {0x0340, 0x0A}, - {0x0341, 0x5B}, - {0x0344, 0x00}, - {0x0345, 0x00}, - {0x0346, 0x00}, - {0x0347, 0x00}, - {0x0348, 0x11}, - {0x0349, 0xFF}, - {0x034A, 0X0A}, - {0x034B, 0x1F}, - {0x0220, 0x01}, - {0x0222, IMX708_HDR_EXPOSURE_RATIO}, - {0x0900, 0x00}, - {0x0901, 0x11}, - {0x0902, 0x0A}, - {0x3200, 0x01}, - {0x3201, 0x01}, - {0x32D5, 0x00}, - {0x32D6, 0x00}, - {0x32DB, 0x01}, - {0x32DF, 0x00}, - {0x350C, 0x00}, - {0x350D, 0x00}, - {0x0408, 0x00}, - {0x0409, 0x00}, - {0x040A, 0x00}, - {0x040B, 0x00}, - {0x040C, 0x09}, - {0x040D, 0x00}, - {0x040E, 0x05}, - {0x040F, 0x10}, - {0x034C, 0x09}, - {0x034D, 0x00}, - {0x034E, 0x05}, - {0x034F, 0x10}, - {0x0301, 0x05}, - {0x0303, 0x02}, - {0x0305, 0x02}, - {0x0306, 0x00}, - {0x0307, 0xA2}, - {0x030B, 0x02}, - {0x030D, 0x04}, - {0x0310, 0x01}, - {0x3CA0, 0x00}, - {0x3CA1, 0x00}, - {0x3CA4, 0x00}, - {0x3CA5, 0x00}, - {0x3CA6, 0x00}, - {0x3CA7, 0x28}, - {0x3CAA, 0x00}, - {0x3CAB, 0x00}, - {0x3CB8, 0x00}, - {0x3CB9, 0x30}, - {0x3CBA, 0x00}, - {0x3CBB, 0x00}, - {0x3CBC, 0x00}, - {0x3CBD, 0x32}, - {0x3CBE, 0x00}, - {0x3CBF, 0x00}, - {0x0202, 0x0A}, - {0x0203, 0x2B}, - {0x0224, 0x0A}, - {0x0225, 0x2B}, - {0x3116, 0x0A}, - {0x3117, 0x2B}, - {0x0204, 0x00}, - {0x0205, 0x00}, - {0x0216, 0x00}, - {0x0217, 0x00}, - {0x0218, 0x01}, - {0x0219, 0x00}, - {0x020E, 0x01}, - {0x020F, 0x00}, - {0x3118, 0x00}, - {0x3119, 0x00}, - {0x311A, 0x01}, - {0x311B, 0x00}, - {0x341a, 0x00}, - {0x341b, 0x00}, - {0x341c, 0x00}, - {0x341d, 0x00}, - {0x341e, 0x00}, - {0x341f, 0x90}, - {0x3420, 0x00}, - {0x3421, 0x6c}, - {0x3360, 0x01}, - {0x3361, 0x01}, - {0x3366, 0x09}, - {0x3367, 0x00}, - {0x3368, 0x05}, - {0x3369, 0x10}, -}; - -/* Mode configs. Keep separate lists for when HDR is enabled or not. */ -static const struct imx708_mode supported_modes_10bit_no_hdr[] = { - { - /* Full resolution. */ - .width = 4608, - .height = 2592, - .line_length_pix = 0x3d20, - .crop = { - .left = IMX708_PIXEL_ARRAY_LEFT, - .top = IMX708_PIXEL_ARRAY_TOP, - .width = 4608, - .height = 2592, - }, - .vblank_min = 58, - .vblank_default = 58, - .reg_list = { - .num_of_regs = ARRAY_SIZE(mode_4608x2592_regs), - .regs = mode_4608x2592_regs, - }, - .pixel_rate = 595200000, - .exposure_lines_min = 8, - .exposure_lines_step = 1, - .hdr = false, - .remosaic = true - }, - { - /* regular 2x2 binned. */ - .width = 2304, - .height = 1296, - .line_length_pix = 0x1e90, - .crop = { - .left = IMX708_PIXEL_ARRAY_LEFT, - .top = IMX708_PIXEL_ARRAY_TOP, - .width = 4608, - .height = 2592, - }, - .vblank_min = 40, - .vblank_default = 1198, - .reg_list = { - .num_of_regs = ARRAY_SIZE(mode_2x2binned_regs), - .regs = mode_2x2binned_regs, - }, - .pixel_rate = 585600000, - .exposure_lines_min = 4, - .exposure_lines_step = 2, - .hdr = false, - .remosaic = false - }, - { - /* 2x2 binned and cropped for 720p. */ - .width = 1536, - .height = 864, - .line_length_pix = 0x1460, - .crop = { - .left = IMX708_PIXEL_ARRAY_LEFT + 768, - .top = IMX708_PIXEL_ARRAY_TOP + 432, - .width = 3072, - .height = 1728, - }, - .vblank_min = 40, - .vblank_default = 2755, - .reg_list = { - .num_of_regs = ARRAY_SIZE(mode_2x2binned_720p_regs), - .regs = mode_2x2binned_720p_regs, - }, - .pixel_rate = 566400000, - .exposure_lines_min = 4, - .exposure_lines_step = 2, - .hdr = false, - .remosaic = false - }, -}; - -static const struct imx708_mode supported_modes_10bit_hdr[] = { - { - /* There's only one HDR mode, which is 2x2 downscaled */ - .width = 2304, - .height = 1296, - .line_length_pix = 0x1460, - .crop = { - .left = IMX708_PIXEL_ARRAY_LEFT, - .top = IMX708_PIXEL_ARRAY_TOP, - .width = 4608, - .height = 2592, - }, - .vblank_min = 3673, - .vblank_default = 3673, - .reg_list = { - .num_of_regs = ARRAY_SIZE(mode_hdr_regs), - .regs = mode_hdr_regs, - }, - .pixel_rate = 777600000, - .exposure_lines_min = 8 * IMX708_HDR_EXPOSURE_RATIO * IMX708_HDR_EXPOSURE_RATIO, - .exposure_lines_step = 2 * IMX708_HDR_EXPOSURE_RATIO * IMX708_HDR_EXPOSURE_RATIO, - .hdr = true, - .remosaic = false - } +static const struct cci_reg_sequence imx708_common_regs[] = { + /* Common */ + { CCI_REG8(0x0100), 0x00 }, + { CCI_REG8(0x0136), 0x18 }, + { CCI_REG8(0x0137), 0x00 }, + { CCI_REG8(0x33f0), 0x02 }, + { CCI_REG8(0x33f1), 0x05 }, + { CCI_REG8(0x3062), 0x00 }, + { CCI_REG8(0x3063), 0x12 }, + { CCI_REG8(0x3068), 0x00 }, + { CCI_REG8(0x3069), 0x12 }, + { CCI_REG8(0x306a), 0x00 }, + { CCI_REG8(0x306b), 0x30 }, + { CCI_REG8(0x3076), 0x00 }, + { CCI_REG8(0x3077), 0x30 }, + { CCI_REG8(0x3078), 0x00 }, + { CCI_REG8(0x3079), 0x30 }, + { CCI_REG8(0x5e54), 0x0c }, + { CCI_REG8(0x6e44), 0x00 }, + { CCI_REG8(0xb0b6), 0x01 }, + { CCI_REG8(0xe829), 0x00 }, + { CCI_REG8(0xf001), 0x08 }, + { CCI_REG8(0xf003), 0x08 }, + { CCI_REG8(0xf00d), 0x10 }, + { CCI_REG8(0xf00f), 0x10 }, + { CCI_REG8(0xf031), 0x08 }, + { CCI_REG8(0xf033), 0x08 }, + { CCI_REG8(0xf03d), 0x10 }, + { CCI_REG8(0xf03f), 0x10 }, + { CCI_REG8(0x0112), 0x0a }, + { CCI_REG8(0x0113), 0x0a }, + { CCI_REG8(0x0114), 0x01 }, + { CCI_REG8(0x0b8e), 0x01 }, + { CCI_REG8(0x0b8f), 0x00 }, + { CCI_REG8(0x0b94), 0x01 }, + { CCI_REG8(0x0b95), 0x00 }, + { CCI_REG8(0x3400), 0x01 }, + { CCI_REG8(0x3478), 0x01 }, + { CCI_REG8(0x3479), 0x1c }, + { CCI_REG8(0x3091), 0x01 }, + { CCI_REG8(0x3092), 0x00 }, + { CCI_REG8(0x3419), 0x00 }, + { CCI_REG8(0xbcf1), 0x02 }, + { CCI_REG8(0x3094), 0x01 }, + { CCI_REG8(0x3095), 0x01 }, + { CCI_REG8(0x3362), 0x00 }, + { CCI_REG8(0x3363), 0x00 }, + { CCI_REG8(0x3364), 0x00 }, + { CCI_REG8(0x3365), 0x00 }, + { CCI_REG8(0x0138), 0x01 }, + /* PLL config */ + { IMX708_REG_IVT_PXCK_DIV, 0x05 }, + { IMX708_REG_IVT_SYCK_DIV, 0x02 }, + { IMX708_REG_IVT_PREPLLCK_DIV, 0x02 }, + { IMX708_REG_IVT_PLL_MPY, 0x007c }, + { IMX708_REG_IOP_SYCK_DIV, 0x02 }, + { IMX708_REG_IOP_PREPLLCK_DIV, 0x04 }, + { IMX708_REG_PLL_MULT_DRIV, 0x01 }, + /* non-HDR defaults */ + { CCI_REG8(0x0220), 0x62 }, + { CCI_REG8(0x0222), 0x01 }, + { CCI_REG8(0x350c), 0x00 }, + { CCI_REG8(0x350d), 0x00 }, + { IMX708_REG_SHT_EXPOSURE, 0x01f4 }, + { IMX708_REG_MID_EXPOSURE, 0x01f4 }, + { IMX708_REG_SHT_ANALOG_GAIN, 0x0000 }, + { IMX708_REG_SHT_DIGITAL_GAIN, 0x0100 }, + { IMX708_REG_MID_ANALOG_GAIN, 0x0000 }, + { IMX708_REG_MID_DIGITAL_GAIN, 0x0100 }, + { IMX708_REG_AEHIST1_AREA_WIDTH, 0x0000 }, + { IMX708_REG_AEHIST1_AREA_HEIGHT, 0x0000 }, + /* Quad-Bayer Compensation */ + { IMX708_REG_QBC_RMSC_EN, 0x01 }, + { IMX708_REG_LPF_INTENSITY, IMX708_LPF_INTENSITY_DEFAULT }, + { IMX708_REG_LPF_INTENSITY_EN, IMX708_LPF_INTENSITY_ENABLED }, + { CCI_REG8(0x32d6), 0x00 }, + { CCI_REG8(0x32db), 0x01 }, + /* Analogue crop disabled */ + { IMX708_REG_ACROPLP_EN, 0x00 }, + /* Unknown registers */ + { CCI_REG8(0x3ca0), 0x00 }, + { CCI_REG8(0x3ca1), 0x64 }, + { CCI_REG8(0x3ca4), 0x00 }, + { CCI_REG8(0x3ca5), 0x00 }, + { CCI_REG8(0x3ca6), 0x00 }, + { CCI_REG8(0x3ca7), 0x00 }, + { CCI_REG8(0x3caa), 0x00 }, + { CCI_REG8(0x3cab), 0x00 }, + { CCI_REG8(0x3cb8), 0x00 }, + { CCI_REG8(0x3cb9), 0x08 }, + { CCI_REG8(0x3cba), 0x00 }, + { CCI_REG8(0x3cbb), 0x00 }, + { CCI_REG8(0x3cbc), 0x00 }, + { CCI_REG8(0x3cbd), 0x3c }, + { CCI_REG8(0x3cbe), 0x00 }, + { CCI_REG8(0x3cbf), 0x00 }, + { CCI_REG8(0x341a), 0x00 }, + { CCI_REG8(0x341b), 0x00 }, + { CCI_REG8(0x341c), 0x00 }, + { CCI_REG8(0x341d), 0x00 }, + { CCI_REG8(0x341e), 0x01 }, + { CCI_REG8(0x341f), 0x20 }, + { CCI_REG8(0x3420), 0x00 }, + { CCI_REG8(0x3421), 0xd8 }, }; /* @@ -827,9 +383,8 @@ static const char * const imx708_supply_name[] = { struct imx708 { struct v4l2_subdev sd; - struct media_pad pad[NUM_PADS]; - - struct v4l2_mbus_framefmt fmt; + struct media_pad pad[IMX708_NUM_PADS]; + struct regmap *cci; struct clk *inclk; u32 inclk_freq; @@ -839,141 +394,25 @@ struct imx708 { struct v4l2_ctrl_handler ctrl_handler; /* V4L2 Controls */ - struct v4l2_ctrl *pixel_rate; struct v4l2_ctrl *exposure; struct v4l2_ctrl *vblank; struct v4l2_ctrl *hblank; - struct v4l2_ctrl *hdr_mode; - struct v4l2_ctrl *link_freq; struct { struct v4l2_ctrl *hflip; struct v4l2_ctrl *vflip; }; - /* Current mode */ - const struct imx708_mode *mode; - - /* - * Mutex for serialized access: - * Protect sensor module set pad format and start/stop streaming safely. - */ - struct mutex mutex; - - /* Streaming on/off */ - bool streaming; - - /* Rewrite common registers on stream on? */ - bool common_regs_written; - /* Current long exposure factor in use. Set through V4L2_CID_VBLANK */ unsigned int long_exp_shift; - unsigned int link_freq_idx; + unsigned long link_freq_bitmap; unsigned int csi_flags; }; static inline struct imx708 *to_imx708(struct v4l2_subdev *_sd) { - return container_of(_sd, struct imx708, sd); -} - -static inline void get_mode_table(unsigned int code, - const struct imx708_mode **mode_list, - unsigned int *num_modes, - bool hdr_enable) -{ - switch (code) { - /* 10-bit */ - case MEDIA_BUS_FMT_SRGGB10_1X10: - case MEDIA_BUS_FMT_SGRBG10_1X10: - case MEDIA_BUS_FMT_SGBRG10_1X10: - case MEDIA_BUS_FMT_SBGGR10_1X10: - if (hdr_enable) { - *mode_list = supported_modes_10bit_hdr; - *num_modes = ARRAY_SIZE(supported_modes_10bit_hdr); - } else { - *mode_list = supported_modes_10bit_no_hdr; - *num_modes = ARRAY_SIZE(supported_modes_10bit_no_hdr); - } - break; - default: - *mode_list = NULL; - *num_modes = 0; - } -} - -/* Read registers up to 2 at a time */ -static int imx708_read_reg(struct imx708 *imx708, u16 reg, u32 len, u32 *val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&imx708->sd); - struct i2c_msg msgs[2]; - u8 addr_buf[2] = { reg >> 8, reg & 0xff }; - u8 data_buf[4] = { 0, }; - int ret; - - if (len > 4) - return -EINVAL; - - /* Write register address */ - msgs[0].addr = client->addr; - msgs[0].flags = 0; - msgs[0].len = ARRAY_SIZE(addr_buf); - msgs[0].buf = addr_buf; - - /* Read data from register */ - 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; -} - -/* Write registers up to 2 at a time */ -static int imx708_write_reg(struct imx708 *imx708, u16 reg, u32 len, u32 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&imx708->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; -} - -/* Write a list of registers */ -static int imx708_write_regs(struct imx708 *imx708, - const struct imx708_reg *regs, u32 len) -{ - struct i2c_client *client = v4l2_get_subdevdata(&imx708->sd); - unsigned int i; - - for (i = 0; i < len; i++) { - int ret; - - ret = imx708_write_reg(imx708, regs[i].address, 1, regs[i].val); - if (ret) { - dev_err_ratelimited(&client->dev, - "Failed to write reg 0x%4.4x. error = %d\n", - regs[i].address, ret); - - return ret; - } - } - - return 0; + return container_of_const(_sd, struct imx708, sd); } /* Get bayer order based on flip setting. */ @@ -981,94 +420,18 @@ static u32 imx708_get_format_code(struct imx708 *imx708) { unsigned int i; - lockdep_assert_held(&imx708->mutex); - i = (imx708->vflip->val ? 2 : 0) | (imx708->hflip->val ? 1 : 0); return codes[i]; } -static void imx708_set_default_format(struct imx708 *imx708) -{ - struct v4l2_mbus_framefmt *fmt = &imx708->fmt; - - /* Set default mode to max resolution */ - imx708->mode = &supported_modes_10bit_no_hdr[0]; - - /* fmt->code not set as it will always be computed based on flips */ - fmt->colorspace = V4L2_COLORSPACE_RAW; - fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); - fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, - fmt->colorspace, - fmt->ycbcr_enc); - fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); - fmt->width = imx708->mode->width; - fmt->height = imx708->mode->height; - fmt->field = V4L2_FIELD_NONE; -} - -static int imx708_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - struct imx708 *imx708 = to_imx708(sd); - struct v4l2_mbus_framefmt *try_fmt_img = - v4l2_subdev_state_get_format(fh->state, IMAGE_PAD); - struct v4l2_mbus_framefmt *try_fmt_meta = - v4l2_subdev_state_get_format(fh->state, METADATA_PAD); - struct v4l2_rect *try_crop; - - mutex_lock(&imx708->mutex); - - /* Initialize try_fmt for the image pad */ - if (imx708->hdr_mode->val) { - try_fmt_img->width = supported_modes_10bit_hdr[0].width; - try_fmt_img->height = supported_modes_10bit_hdr[0].height; - } else { - try_fmt_img->width = supported_modes_10bit_no_hdr[0].width; - try_fmt_img->height = supported_modes_10bit_no_hdr[0].height; - } - try_fmt_img->code = imx708_get_format_code(imx708); - try_fmt_img->field = V4L2_FIELD_NONE; - - /* Initialize try_fmt for the embedded metadata pad */ - try_fmt_meta->width = IMX708_EMBEDDED_LINE_WIDTH; - try_fmt_meta->height = IMX708_NUM_EMBEDDED_LINES; - try_fmt_meta->code = MEDIA_BUS_FMT_SENSOR_DATA; - try_fmt_meta->field = V4L2_FIELD_NONE; - - /* Initialize try_crop */ - try_crop = v4l2_subdev_state_get_crop(fh->state, IMAGE_PAD); - try_crop->left = IMX708_PIXEL_ARRAY_LEFT; - try_crop->top = IMX708_PIXEL_ARRAY_TOP; - try_crop->width = IMX708_PIXEL_ARRAY_WIDTH; - try_crop->height = IMX708_PIXEL_ARRAY_HEIGHT; - - mutex_unlock(&imx708->mutex); - - return 0; -} - -static int imx708_set_exposure(struct imx708 *imx708, unsigned int val) -{ - val = max(val, imx708->mode->exposure_lines_min); - val -= val % imx708->mode->exposure_lines_step; - - /* - * In HDR mode this will set the longest exposure. The sensor - * will automatically divide the medium and short ones by 4,16. - */ - return imx708_write_reg(imx708, IMX708_REG_EXPOSURE, - IMX708_REG_VALUE_16BIT, - val >> imx708->long_exp_shift); -} - -static void imx708_adjust_exposure_range(struct imx708 *imx708, - struct v4l2_ctrl *ctrl) +static void imx708_adjust_exposure_range(struct imx708 *imx708) { int exposure_max, exposure_def; /* Honour the VBLANK limits when setting exposure. */ - exposure_max = imx708->mode->height + imx708->vblank->val - + exposure_max = imx708_active_area.height + imx708->vblank->val - IMX708_EXPOSURE_OFFSET; exposure_def = min(exposure_max, imx708->exposure->val); __v4l2_ctrl_modify_range(imx708->exposure, imx708->exposure->minimum, @@ -1076,23 +439,9 @@ static void imx708_adjust_exposure_range(struct imx708 *imx708, exposure_def); } -static int imx708_set_analogue_gain(struct imx708 *imx708, unsigned int val) -{ - int ret; - - /* - * In HDR mode this will set the gain for the longest exposure, - * and by default the sensor uses the same gain for all of them. - */ - ret = imx708_write_reg(imx708, IMX708_REG_ANALOG_GAIN, - IMX708_REG_VALUE_16BIT, val); - - return ret; -} - static int imx708_set_frame_length(struct imx708 *imx708, unsigned int val) { - int ret; + int ret = 0; imx708->long_exp_shift = 0; @@ -1101,46 +450,18 @@ static int imx708_set_frame_length(struct imx708 *imx708, unsigned int val) val >>= 1; } - ret = imx708_write_reg(imx708, IMX708_REG_FRAME_LENGTH, - IMX708_REG_VALUE_16BIT, val); - if (ret) - return ret; - - return imx708_write_reg(imx708, IMX708_LONG_EXP_SHIFT_REG, - IMX708_REG_VALUE_08BIT, imx708->long_exp_shift); -} - -static void imx708_set_framing_limits(struct imx708 *imx708) -{ - const struct imx708_mode *mode = imx708->mode; - unsigned int hblank; - - __v4l2_ctrl_modify_range(imx708->pixel_rate, - mode->pixel_rate, mode->pixel_rate, - 1, mode->pixel_rate); + cci_write(imx708->cci, IMX708_REG_FRAME_LENGTH, val, &ret); + cci_write(imx708->cci, IMX708_LONG_EXP_SHIFT_REG, + imx708->long_exp_shift, &ret); - /* Update limits and set FPS to default */ - __v4l2_ctrl_modify_range(imx708->vblank, mode->vblank_min, - ((1 << IMX708_LONG_EXP_SHIFT_MAX) * - IMX708_FRAME_LENGTH_MAX) - mode->height, - 1, mode->vblank_default); - - /* - * Currently PPL is fixed to the mode specified value, so hblank - * depends on mode->width only, and is not changeable in any - * way other than changing the mode. - */ - hblank = mode->line_length_pix - mode->width; - __v4l2_ctrl_modify_range(imx708->hblank, hblank, hblank, 1, hblank); + return ret; } static int imx708_set_ctrl(struct v4l2_ctrl *ctrl) { struct imx708 *imx708 = - container_of(ctrl->handler, struct imx708, ctrl_handler); + container_of_const(ctrl->handler, struct imx708, ctrl_handler); struct i2c_client *client = v4l2_get_subdevdata(&imx708->sd); - const struct imx708_mode *mode_list; - unsigned int code, num_modes; int ret = 0; switch (ctrl->id) { @@ -1149,99 +470,86 @@ static int imx708_set_ctrl(struct v4l2_ctrl *ctrl) * The VBLANK control may change the limits of usable exposure, * so check and adjust if necessary. */ - imx708_adjust_exposure_range(imx708, ctrl); + imx708_adjust_exposure_range(imx708); break; - case V4L2_CID_WIDE_DYNAMIC_RANGE: - /* - * The WIDE_DYNAMIC_RANGE control can also be applied immediately - * as it doesn't set any registers. Don't do anything if the mode - * already matches. - */ - if (imx708->mode && imx708->mode->hdr != ctrl->val) { - code = imx708_get_format_code(imx708); - get_mode_table(code, &mode_list, &num_modes, ctrl->val); - imx708->mode = v4l2_find_nearest_size(mode_list, - num_modes, - width, height, - imx708->mode->width, - imx708->mode->height); - imx708_set_framing_limits(imx708); - } + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: { + struct v4l2_subdev_state *state = + v4l2_subdev_get_locked_active_state(&imx708->sd); + struct v4l2_mbus_framefmt *format; + + format = v4l2_subdev_state_get_format(state, IMX708_SOURCE_PAD); + format->code = imx708_get_format_code(imx708); break; } + } /* - * Applying V4L2 control value only happens - * when power is up for streaming + * Only apply control values when device is powered on (RPM ACTIVE) + * and streaming (usage count != 0) */ - if (pm_runtime_get_if_in_use(&client->dev) == 0) + if (!pm_runtime_get_if_in_use(&client->dev)) return 0; switch (ctrl->id) { case V4L2_CID_ANALOGUE_GAIN: - imx708_set_analogue_gain(imx708, ctrl->val); + cci_write(imx708->cci, IMX708_REG_ANALOG_GAIN, ctrl->val, &ret); break; + case V4L2_CID_VBLANK: + ret = imx708_set_frame_length(imx708, + imx708_active_area.height + + ctrl->val); + fallthrough; /* update exposure with new long_exp_shift */ case V4L2_CID_EXPOSURE: - ret = imx708_set_exposure(imx708, ctrl->val); + cci_write(imx708->cci, IMX708_REG_EXPOSURE, + imx708->exposure->val >> imx708->long_exp_shift, + &ret); break; case V4L2_CID_DIGITAL_GAIN: - ret = imx708_write_reg(imx708, IMX708_REG_DIGITAL_GAIN, - IMX708_REG_VALUE_16BIT, ctrl->val); + cci_write(imx708->cci, IMX708_REG_DIGITAL_GAIN, ctrl->val, &ret); break; case V4L2_CID_TEST_PATTERN: - ret = imx708_write_reg(imx708, IMX708_REG_TEST_PATTERN, - IMX708_REG_VALUE_16BIT, - imx708_test_pattern_val[ctrl->val]); + cci_write(imx708->cci, IMX708_REG_TEST_PATTERN, + imx708_test_pattern_val[ctrl->val], &ret); break; case V4L2_CID_TEST_PATTERN_RED: - ret = imx708_write_reg(imx708, IMX708_REG_TEST_PATTERN_R, - IMX708_REG_VALUE_16BIT, ctrl->val); + cci_write(imx708->cci, IMX708_REG_TEST_PATTERN_R, + ctrl->val, &ret); break; case V4L2_CID_TEST_PATTERN_GREENR: - ret = imx708_write_reg(imx708, IMX708_REG_TEST_PATTERN_GR, - IMX708_REG_VALUE_16BIT, ctrl->val); + cci_write(imx708->cci, IMX708_REG_TEST_PATTERN_GR, + ctrl->val, &ret); break; case V4L2_CID_TEST_PATTERN_BLUE: - ret = imx708_write_reg(imx708, IMX708_REG_TEST_PATTERN_B, - IMX708_REG_VALUE_16BIT, ctrl->val); + cci_write(imx708->cci, IMX708_REG_TEST_PATTERN_B, + ctrl->val, &ret); break; case V4L2_CID_TEST_PATTERN_GREENB: - ret = imx708_write_reg(imx708, IMX708_REG_TEST_PATTERN_GB, - IMX708_REG_VALUE_16BIT, ctrl->val); + cci_write(imx708->cci, IMX708_REG_TEST_PATTERN_GB, + ctrl->val, &ret); break; case V4L2_CID_HFLIP: case V4L2_CID_VFLIP: - ret = imx708_write_reg(imx708, IMX708_REG_ORIENTATION, 1, - imx708->hflip->val | - imx708->vflip->val << 1); - break; - case V4L2_CID_VBLANK: - ret = imx708_set_frame_length(imx708, - imx708->mode->height + ctrl->val); + cci_write(imx708->cci, IMX708_REG_ORIENTATION, + imx708->hflip->val | imx708->vflip->val << 1, &ret); break; case V4L2_CID_NOTIFY_GAINS: - ret = imx708_write_reg(imx708, IMX708_REG_COLOUR_BALANCE_BLUE, - IMX708_REG_VALUE_16BIT, - ctrl->p_new.p_u32[0]); + cci_write(imx708->cci, IMX708_REG_COLOUR_BALANCE_BLUE, + ctrl->p_new.p_u32[0], &ret); if (ret) break; - ret = imx708_write_reg(imx708, IMX708_REG_COLOUR_BALANCE_RED, - IMX708_REG_VALUE_16BIT, - ctrl->p_new.p_u32[3]); - break; - case V4L2_CID_WIDE_DYNAMIC_RANGE: - /* Already handled above. */ + cci_write(imx708->cci, IMX708_REG_COLOUR_BALANCE_RED, + ctrl->p_new.p_u32[3], &ret); break; default: - dev_info(&client->dev, + dev_warn(&client->dev, "ctrl(id:0x%x,val:0x%x) is not handled\n", ctrl->id, ctrl->val); ret = -EINVAL; break; } - pm_runtime_mark_last_busy(&client->dev); pm_runtime_put_autosuspend(&client->dev); return ret; @@ -1257,22 +565,13 @@ static int imx708_enum_mbus_code(struct v4l2_subdev *sd, { struct imx708 *imx708 = to_imx708(sd); - if (code->pad >= NUM_PADS) + if (code->pad >= IMX708_NUM_PADS) return -EINVAL; - if (code->pad == IMAGE_PAD) { - if (code->index >= (ARRAY_SIZE(codes) / 4)) - return -EINVAL; - - mutex_lock(&imx708->mutex); - code->code = imx708_get_format_code(imx708); - mutex_unlock(&imx708->mutex); - } else { - if (code->index > 0) - return -EINVAL; + if (code->index >= (ARRAY_SIZE(codes) / 4)) + return -EINVAL; - code->code = MEDIA_BUS_FMT_SENSOR_DATA; - } + code->code = imx708_get_format_code(imx708); return 0; } @@ -1284,202 +583,72 @@ static int imx708_enum_frame_size(struct v4l2_subdev *sd, struct imx708 *imx708 = to_imx708(sd); u32 code; - if (fse->pad >= NUM_PADS) + if (fse->pad >= IMX708_NUM_PADS) return -EINVAL; - if (fse->pad == IMAGE_PAD) { - const struct imx708_mode *mode_list; - unsigned int num_modes; - - get_mode_table(fse->code, &mode_list, &num_modes, - imx708->hdr_mode->val); - - if (fse->index >= num_modes) - return -EINVAL; - - mutex_lock(&imx708->mutex); - code = imx708_get_format_code(imx708); - mutex_unlock(&imx708->mutex); - - if (fse->code != code) - return -EINVAL; - - fse->min_width = mode_list[fse->index].width; - fse->max_width = fse->min_width; - fse->min_height = mode_list[fse->index].height; - fse->max_height = fse->min_height; - } else { - if (fse->code != MEDIA_BUS_FMT_SENSOR_DATA || fse->index > 0) - return -EINVAL; - - fse->min_width = IMX708_EMBEDDED_LINE_WIDTH; - fse->max_width = fse->min_width; - fse->min_height = IMX708_NUM_EMBEDDED_LINES; - fse->max_height = fse->min_height; - } - - return 0; -} - -static void imx708_reset_colorspace(struct v4l2_mbus_framefmt *fmt) -{ - fmt->colorspace = V4L2_COLORSPACE_RAW; - fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); - fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, - fmt->colorspace, - fmt->ycbcr_enc); - fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); -} - -static void imx708_update_image_pad_format(struct imx708 *imx708, - const struct imx708_mode *mode, - struct v4l2_subdev_format *fmt) -{ - fmt->format.width = mode->width; - fmt->format.height = mode->height; - fmt->format.field = V4L2_FIELD_NONE; - imx708_reset_colorspace(&fmt->format); -} - -static void imx708_update_metadata_pad_format(struct v4l2_subdev_format *fmt) -{ - fmt->format.width = IMX708_EMBEDDED_LINE_WIDTH; - fmt->format.height = IMX708_NUM_EMBEDDED_LINES; - fmt->format.code = MEDIA_BUS_FMT_SENSOR_DATA; - fmt->format.field = V4L2_FIELD_NONE; -} + code = imx708_get_format_code(imx708); -static int imx708_get_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct imx708 *imx708 = to_imx708(sd); + if (fse->code != code) + return -EINVAL; - if (fmt->pad >= NUM_PADS) + if (fse->index > 0) return -EINVAL; - mutex_lock(&imx708->mutex); - - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_state_get_format(sd_state, - fmt->pad); - /* update the code which could change due to vflip or hflip */ - try_fmt->code = fmt->pad == IMAGE_PAD ? - imx708_get_format_code(imx708) : - MEDIA_BUS_FMT_SENSOR_DATA; - fmt->format = *try_fmt; - } else { - if (fmt->pad == IMAGE_PAD) { - imx708_update_image_pad_format(imx708, imx708->mode, - fmt); - fmt->format.code = imx708_get_format_code(imx708); - } else { - imx708_update_metadata_pad_format(fmt); - } - } + fse->min_width = imx708_active_area.width; + fse->max_width = fse->min_width; + fse->min_height = imx708_active_area.height; + fse->max_height = fse->min_height; - mutex_unlock(&imx708->mutex); return 0; } -static int imx708_set_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) +static int imx708_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { - struct v4l2_mbus_framefmt *framefmt; - const struct imx708_mode *mode; struct imx708 *imx708 = to_imx708(sd); - - if (fmt->pad >= NUM_PADS) - return -EINVAL; - - mutex_lock(&imx708->mutex); - - if (fmt->pad == IMAGE_PAD) { - const struct imx708_mode *mode_list; - unsigned int num_modes; - - /* Bayer order varies with flips */ - fmt->format.code = imx708_get_format_code(imx708); - - get_mode_table(fmt->format.code, &mode_list, &num_modes, - imx708->hdr_mode->val); - - mode = v4l2_find_nearest_size(mode_list, - num_modes, - width, height, - fmt->format.width, - fmt->format.height); - imx708_update_image_pad_format(imx708, mode, fmt); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_state_get_format(sd_state, - fmt->pad); - *framefmt = fmt->format; - } else { - imx708->mode = mode; - imx708_set_framing_limits(imx708); - } - } else { - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_state_get_format(sd_state, - fmt->pad); - *framefmt = fmt->format; - } else { - /* Only one embedded data mode is supported */ - imx708_update_metadata_pad_format(fmt); - } - } - - mutex_unlock(&imx708->mutex); + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + + /* Initialize the image pad format. */ + format = v4l2_subdev_state_get_format(state, IMX708_SOURCE_PAD); + format->code = imx708_get_format_code(imx708); + format->width = imx708_active_area.width; + format->height = imx708_active_area.height; + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_RAW; + format->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + format->quantization = V4L2_QUANTIZATION_FULL_RANGE; + format->xfer_func = V4L2_XFER_FUNC_NONE; + + /* Initialize the image pad crop. */ + crop = v4l2_subdev_state_get_crop(state, IMX708_SOURCE_PAD); + *crop = imx708_active_area; return 0; } -static const struct v4l2_rect * -__imx708_get_pad_crop(struct imx708 *imx708, 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_state_get_crop(sd_state, pad); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &imx708->mode->crop; - } - - return NULL; -} - static int imx708_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { switch (sel->target) { case V4L2_SEL_TGT_CROP: { - struct imx708 *imx708 = to_imx708(sd); + struct v4l2_rect *crop; - mutex_lock(&imx708->mutex); - sel->r = *__imx708_get_pad_crop(imx708, sd_state, sel->pad, - sel->which); - mutex_unlock(&imx708->mutex); + crop = v4l2_subdev_state_get_crop(sd_state, sel->pad); + sel->r = *crop; return 0; } case V4L2_SEL_TGT_NATIVE_SIZE: - sel->r.left = 0; - sel->r.top = 0; - sel->r.width = IMX708_NATIVE_WIDTH; - sel->r.height = IMX708_NATIVE_HEIGHT; + sel->r = imx708_native_area; return 0; case V4L2_SEL_TGT_CROP_DEFAULT: case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.left = IMX708_PIXEL_ARRAY_LEFT; - sel->r.top = IMX708_PIXEL_ARRAY_TOP; - sel->r.width = IMX708_PIXEL_ARRAY_WIDTH; - sel->r.height = IMX708_PIXEL_ARRAY_HEIGHT; + sel->r = imx708_active_area; return 0; } @@ -1487,161 +656,172 @@ static int imx708_get_selection(struct v4l2_subdev *sd, return -EINVAL; } -/* Start streaming */ -static int imx708_start_streaming(struct imx708 *imx708) +static int imx708_program_window(struct imx708 *imx708, + struct v4l2_subdev_state *state) { - struct i2c_client *client = v4l2_get_subdevdata(&imx708->sd); - const struct imx708_reg_list *reg_list, *freq_regs; - int i, ret; - u32 val; - - if (!imx708->common_regs_written) { - ret = imx708_write_regs(imx708, mode_common_regs, - ARRAY_SIZE(mode_common_regs)); - if (ret) { - dev_err(&client->dev, "%s failed to set common settings\n", - __func__); - return ret; - } + const struct v4l2_rect *crop; + const struct v4l2_mbus_framefmt *format; + int ret = 0; + s32 x_start, y_start; + + crop = v4l2_subdev_state_get_crop(state, IMX708_SOURCE_PAD); + format = v4l2_subdev_state_get_format(state, IMX708_SOURCE_PAD); + + /* Line length */ + cci_write(imx708->cci, IMX708_REG_LINE_LENGTH, IMX708_LINE_LENGTH, + &ret); + + /* Imaging area */ + x_start = crop->left - imx708_active_area.left; + y_start = crop->top - imx708_active_area.top; + cci_write(imx708->cci, IMX708_REG_X_ADD_STA, x_start, &ret); + cci_write(imx708->cci, IMX708_REG_Y_ADD_STA, y_start, &ret); + cci_write(imx708->cci, IMX708_REG_X_ADD_END, x_start + crop->width - 1, + &ret); + cci_write(imx708->cci, IMX708_REG_Y_ADD_END, y_start + crop->height - 1, + &ret); + + /* Binning (fixed: no binning) */ + cci_write(imx708->cci, IMX708_REG_BINNING_MODE, 0x00, &ret); + cci_write(imx708->cci, IMX708_REG_BINNING_TYPE, 0x11, &ret); + cci_write(imx708->cci, IMX708_REG_BINNING_WEIGHT, 0x0a, &ret); + cci_write(imx708->cci, IMX708_REG_BINNING_PRIORITY_H, 0x01, &ret); + cci_write(imx708->cci, IMX708_REG_BINNING_PRIORITY_V, 0x01, &ret); + + /* Digital crop (fixed: no crop) */ + cci_write(imx708->cci, IMX708_REG_DIG_CROP_X_OFFSET, 0, &ret); + cci_write(imx708->cci, IMX708_REG_DIG_CROP_Y_OFFSET, 0, &ret); + cci_write(imx708->cci, IMX708_REG_DIG_CROP_WIDTH, format->width, &ret); + cci_write(imx708->cci, IMX708_REG_DIG_CROP_HEIGHT, format->height, &ret); + + /* Output size */ + cci_write(imx708->cci, IMX708_REG_X_OUTPUT_SIZE, format->width, &ret); + cci_write(imx708->cci, IMX708_REG_Y_OUTPUT_SIZE, format->height, &ret); - ret = imx708_write_reg(imx708, IMX708_REG_CLKLANE_BLANK, - IMX708_REG_VALUE_08BIT, - imx708->csi_flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK ? - IMX708_CLKLANE_BLANK_NONCONT : 0); - if (ret) { - dev_err(&client->dev, "%s failed to set clock lane mode\n", - __func__); - return ret; - } + return ret; +} - ret = imx708_read_reg(imx708, IMX708_REG_BASE_SPC_GAINS_L, - IMX708_REG_VALUE_08BIT, &val); - if (ret == 0 && val == 0x40) { - for (i = 0; i < 54 && ret == 0; i++) { - ret = imx708_write_reg(imx708, - IMX708_REG_BASE_SPC_GAINS_L + i, - IMX708_REG_VALUE_08BIT, - pdaf_gains[0][i % 9]); - } - for (i = 0; i < 54 && ret == 0; i++) { - ret = imx708_write_reg(imx708, - IMX708_REG_BASE_SPC_GAINS_R + i, - IMX708_REG_VALUE_08BIT, - pdaf_gains[1][i % 9]); - } - } - if (ret) { - dev_err(&client->dev, "%s failed to set PDAF gains\n", - __func__); - return ret; - } +static int imx708_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 mask) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct imx708 *imx708 = to_imx708(sd); + const struct imx708_reg_list *freq_regs; + int ret = 0; - imx708->common_regs_written = true; - } + ret = pm_runtime_resume_and_get(&client->dev); + if (ret < 0) + return ret; - /* Apply default values of current mode */ - reg_list = &imx708->mode->reg_list; - ret = imx708_write_regs(imx708, reg_list->regs, reg_list->num_of_regs); + /* Program windowing registers from V4L2 state */ + ret = imx708_program_window(imx708, state); if (ret) { dev_err(&client->dev, "%s failed to set mode\n", __func__); - return ret; + goto err_rpm_put; } /* Update the link frequency registers */ - freq_regs = &link_freq_regs[imx708->link_freq_idx]; - ret = imx708_write_regs(imx708, freq_regs->regs, - freq_regs->num_of_regs); + freq_regs = &link_freq_regs[__ffs(imx708->link_freq_bitmap)]; + cci_multi_reg_write(imx708->cci, freq_regs->regs, + freq_regs->num_of_regs, &ret); if (ret) { dev_err(&client->dev, "%s failed to set link frequency registers\n", __func__); - return ret; - } - - /* Quad Bayer re-mosaic adjustments (for full-resolution mode only) */ - if (imx708->mode->remosaic && qbc_adjust > 0) { - imx708_write_reg(imx708, IMX708_LPF_INTENSITY, - IMX708_REG_VALUE_08BIT, qbc_adjust); - imx708_write_reg(imx708, - IMX708_LPF_INTENSITY_EN, - IMX708_REG_VALUE_08BIT, - IMX708_LPF_INTENSITY_ENABLED); - } else { - imx708_write_reg(imx708, - IMX708_LPF_INTENSITY_EN, - IMX708_REG_VALUE_08BIT, - IMX708_LPF_INTENSITY_DISABLED); + goto err_rpm_put; } /* Apply customized values from user */ ret = __v4l2_ctrl_handler_setup(imx708->sd.ctrl_handler); if (ret) - return ret; + goto err_rpm_put; /* set stream on register */ - return imx708_write_reg(imx708, IMX708_REG_MODE_SELECT, - IMX708_REG_VALUE_08BIT, IMX708_MODE_STREAMING); + cci_write(imx708->cci, IMX708_REG_MODE_SELECT, + IMX708_MODE_STREAMING, &ret); + if (ret) { + dev_err(&client->dev, "%s failed to start streaming\n", + __func__); + goto err_rpm_put; + } + + /* vflip/hflip cannot change during streaming */ + __v4l2_ctrl_grab(imx708->vflip, true); + __v4l2_ctrl_grab(imx708->hflip, true); + + return 0; + +err_rpm_put: + pm_runtime_put_sync(&client->dev); + + return ret; } -/* Stop streaming */ -static void imx708_stop_streaming(struct imx708 *imx708) +static int imx708_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 mask) { - struct i2c_client *client = v4l2_get_subdevdata(&imx708->sd); - int ret; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct imx708 *imx708 = to_imx708(sd); + int ret = 0; /* set stream off register */ - ret = imx708_write_reg(imx708, IMX708_REG_MODE_SELECT, - IMX708_REG_VALUE_08BIT, IMX708_MODE_STANDBY); + cci_write(imx708->cci, IMX708_REG_MODE_SELECT, + IMX708_MODE_STANDBY, &ret); if (ret) dev_err(&client->dev, "%s failed to set stream\n", __func__); + + __v4l2_ctrl_grab(imx708->vflip, false); + __v4l2_ctrl_grab(imx708->hflip, false); + + pm_runtime_put_autosuspend(&client->dev); + + return ret; } -static int imx708_set_stream(struct v4l2_subdev *sd, int enable) +static int imx708_write_common(struct imx708 *imx708) { - struct imx708 *imx708 = to_imx708(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret = 0; + struct i2c_client *client = v4l2_get_subdevdata(&imx708->sd); + int i, ret = 0; + u64 val; - mutex_lock(&imx708->mutex); - if (imx708->streaming == enable) { - mutex_unlock(&imx708->mutex); - return 0; + cci_multi_reg_write(imx708->cci, imx708_common_regs, + ARRAY_SIZE(imx708_common_regs), &ret); + if (ret) { + dev_err(&client->dev, "%s failed to set common settings\n", + __func__); + return ret; } - if (enable) { - ret = pm_runtime_resume_and_get(&client->dev); - if (ret < 0) - goto err_unlock; + cci_write(imx708->cci, IMX708_REG_CLKLANE_BLANK, + imx708->csi_flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK ? + IMX708_CLKLANE_BLANK_NONCONT : 0, &ret); + if (ret) { + dev_err(&client->dev, "%s failed to set clock lane mode\n", + __func__); + return ret; + } - /* - * Apply default & customized values - * and then start streaming. - */ - ret = imx708_start_streaming(imx708); - if (ret) { - pm_runtime_put_sync(&client->dev); - goto err_unlock; + cci_read(imx708->cci, IMX708_REG_BASE_SPC_GAINS_L, &val, &ret); + if (ret == 0 && val == 0x40) { + for (i = 0; i < 54 && ret == 0; i++) { + cci_write(imx708->cci, + CCI_REG8(CCI_REG_ADDR(IMX708_REG_BASE_SPC_GAINS_L) + i), + pdaf_gains[0][i % 9], &ret); + } + for (i = 0; i < 54 && ret == 0; i++) { + cci_write(imx708->cci, + CCI_REG8(CCI_REG_ADDR(IMX708_REG_BASE_SPC_GAINS_R) + i), + pdaf_gains[1][i % 9], &ret); } - } else { - imx708_stop_streaming(imx708); - pm_runtime_mark_last_busy(&client->dev); - pm_runtime_put_autosuspend(&client->dev); + } + if (ret) { + dev_err(&client->dev, "%s failed to set PDAF gains\n", + __func__); + return ret; } - imx708->streaming = enable; - - /* vflip/hflip and hdr mode cannot change during streaming */ - __v4l2_ctrl_grab(imx708->vflip, enable); - __v4l2_ctrl_grab(imx708->hflip, enable); - __v4l2_ctrl_grab(imx708->hdr_mode, enable); - - mutex_unlock(&imx708->mutex); - - return ret; - -err_unlock: - mutex_unlock(&imx708->mutex); - - return ret; + return 0; } /* Power/clock management functions */ @@ -1667,12 +847,23 @@ static int imx708_power_on(struct device *dev) goto reg_off; } - gpiod_set_value_cansleep(imx708->reset_gpio, 1); + gpiod_set_value_cansleep(imx708->reset_gpio, 0); usleep_range(IMX708_XCLR_MIN_DELAY_US, IMX708_XCLR_MIN_DELAY_US + IMX708_XCLR_DELAY_RANGE_US); + ret = imx708_write_common(imx708); + if (ret) { + dev_err(&client->dev, "%s failed to write registers\n", + __func__); + goto clk_off; + } + return 0; +clk_off: + gpiod_set_value_cansleep(imx708->reset_gpio, 1); + clk_disable_unprepare(imx708->inclk); + reg_off: regulator_bulk_disable(ARRAY_SIZE(imx708_supply_name), imx708->supplies); @@ -1685,72 +876,22 @@ static int imx708_power_off(struct device *dev) struct v4l2_subdev *sd = i2c_get_clientdata(client); struct imx708 *imx708 = to_imx708(sd); - gpiod_set_value_cansleep(imx708->reset_gpio, 0); + gpiod_set_value_cansleep(imx708->reset_gpio, 1); regulator_bulk_disable(ARRAY_SIZE(imx708_supply_name), imx708->supplies); clk_disable_unprepare(imx708->inclk); - /* Force reprogramming of the common registers when powered up again. */ - imx708->common_regs_written = false; - return 0; } -static int __maybe_unused imx708_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct imx708 *imx708 = to_imx708(sd); - - if (imx708->streaming) - imx708_stop_streaming(imx708); - - return 0; -} - -static int __maybe_unused imx708_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct imx708 *imx708 = to_imx708(sd); - int ret; - - if (imx708->streaming) { - ret = imx708_start_streaming(imx708); - if (ret) - goto error; - } - - return 0; - -error: - imx708_stop_streaming(imx708); - imx708->streaming = 0; - return ret; -} - -static int imx708_get_regulators(struct imx708 *imx708) -{ - struct i2c_client *client = v4l2_get_subdevdata(&imx708->sd); - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(imx708_supply_name); i++) - imx708->supplies[i].supply = imx708_supply_name[i]; - - return devm_regulator_bulk_get(&client->dev, - ARRAY_SIZE(imx708_supply_name), - imx708->supplies); -} - /* Verify chip ID */ static int imx708_identify_module(struct imx708 *imx708) { struct i2c_client *client = v4l2_get_subdevdata(&imx708->sd); - int ret; - u32 val; + int ret = 0; + u64 val; - ret = imx708_read_reg(imx708, IMX708_REG_CHIP_ID, - IMX708_REG_VALUE_16BIT, &val); + cci_read(imx708->cci, IMX708_REG_CHIP_ID, &val, &ret); if (ret) { dev_err(&client->dev, "failed to read chip id %x, with error %d\n", IMX708_CHIP_ID, ret); @@ -1758,19 +899,11 @@ static int imx708_identify_module(struct imx708 *imx708) } if (val != IMX708_CHIP_ID) { - dev_err(&client->dev, "chip id mismatch: %x!=%x\n", + dev_err(&client->dev, "chip id mismatch: %x!=%llx\n", IMX708_CHIP_ID, val); return -EIO; } - ret = imx708_read_reg(imx708, 0x0000, IMX708_REG_VALUE_16BIT, &val); - if (!ret) { - dev_info(&client->dev, "camera module ID 0x%04x\n", val); - snprintf(imx708->sd.name, sizeof(imx708->sd.name), "imx708%s%s", - val & 0x02 ? "_wide" : "", - val & 0x80 ? "_noir" : ""); - } - return 0; } @@ -1780,15 +913,17 @@ static const struct v4l2_subdev_core_ops imx708_core_ops = { }; static const struct v4l2_subdev_video_ops imx708_video_ops = { - .s_stream = imx708_set_stream, + .s_stream = v4l2_subdev_s_stream_helper, }; static const struct v4l2_subdev_pad_ops imx708_pad_ops = { .enum_mbus_code = imx708_enum_mbus_code, - .get_fmt = imx708_get_pad_format, - .set_fmt = imx708_set_pad_format, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = v4l2_subdev_get_fmt, .get_selection = imx708_get_selection, .enum_frame_size = imx708_enum_frame_size, + .enable_streams = imx708_enable_streams, + .disable_streams = imx708_disable_streams, }; static const struct v4l2_subdev_ops imx708_subdev_ops = { @@ -1798,7 +933,7 @@ static const struct v4l2_subdev_ops imx708_subdev_ops = { }; static const struct v4l2_subdev_internal_ops imx708_internal_ops = { - .open = imx708_open, + .init_state = imx708_init_state, }; static const struct v4l2_ctrl_config imx708_notify_gains_ctrl = { @@ -1819,44 +954,50 @@ static int imx708_init_controls(struct imx708 *imx708) struct v4l2_ctrl_handler *ctrl_hdlr; struct i2c_client *client = v4l2_get_subdevdata(&imx708->sd); struct v4l2_fwnode_device_properties props; - struct v4l2_ctrl *ctrl; + struct v4l2_ctrl *link_freq; unsigned int i; + s32 hblank, vblank_max, exposure_max; int ret; + ret = v4l2_fwnode_device_parse(&client->dev, &props); + if (ret < 0) + return ret; + ctrl_hdlr = &imx708->ctrl_handler; - ret = v4l2_ctrl_handler_init(ctrl_hdlr, 16); + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 17); if (ret) return ret; - mutex_init(&imx708->mutex); - ctrl_hdlr->lock = &imx708->mutex; - /* By default, PIXEL_RATE is read only */ - imx708->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops, - V4L2_CID_PIXEL_RATE, - IMX708_INITIAL_PIXEL_RATE, - IMX708_INITIAL_PIXEL_RATE, 1, - IMX708_INITIAL_PIXEL_RATE); - - ctrl = v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx708_ctrl_ops, - V4L2_CID_LINK_FREQ, 0, 0, - &link_freqs[imx708->link_freq_idx]); - if (ctrl) - ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; - - /* - * Create the controls here, but mode specific limits are setup - * in the imx708_set_framing_limits() call below. - */ + v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops, V4L2_CID_PIXEL_RATE, + IMX708_PIXEL_RATE, IMX708_PIXEL_RATE, 1, + IMX708_PIXEL_RATE); + + link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx708_ctrl_ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(link_freqs) - 1, + __ffs(imx708->link_freq_bitmap), + link_freqs); + + /* Frame Time = 2^LONG_EXP_SHIFT * REG_FRAME_LENGTH */ + vblank_max = ((1 << IMX708_LONG_EXP_SHIFT_MAX) * + IMX708_FRAME_LENGTH_MAX) - imx708_active_area.height; imx708->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops, - V4L2_CID_VBLANK, 0, 0xffff, 1, 0); + V4L2_CID_VBLANK, IMX708_VBLANK_MIN, + vblank_max, 1, IMX708_VBLANK_MIN); + + hblank = IMX708_LINE_LENGTH - imx708_active_area.width; imx708->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops, - V4L2_CID_HBLANK, 0, 0xffff, 1, 0); + V4L2_CID_HBLANK, hblank, hblank, 1, + hblank); + /* Max Exposure = FRAME_LENGTH - OFFSET */ + exposure_max = (imx708_active_area.height + IMX708_VBLANK_MIN) - + IMX708_EXPOSURE_OFFSET; imx708->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops, V4L2_CID_EXPOSURE, IMX708_EXPOSURE_MIN, - IMX708_EXPOSURE_MAX, + exposure_max, IMX708_EXPOSURE_STEP, IMX708_EXPOSURE_DEFAULT); @@ -1897,48 +1038,25 @@ static int imx708_init_controls(struct imx708 *imx708) v4l2_ctrl_new_custom(ctrl_hdlr, &imx708_notify_gains_ctrl, NULL); - imx708->hdr_mode = v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops, - V4L2_CID_WIDE_DYNAMIC_RANGE, - 0, 1, 1, 0); - - ret = v4l2_fwnode_device_parse(&client->dev, &props); - if (ret) - goto error; - v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx708_ctrl_ops, &props); if (ctrl_hdlr->error) { ret = ctrl_hdlr->error; dev_err(&client->dev, "%s control init failed (%d)\n", __func__, ret); - goto error; + v4l2_ctrl_handler_free(ctrl_hdlr); + + return ret; } + link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; imx708->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; imx708->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; imx708->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; - imx708->hdr_mode->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; imx708->sd.ctrl_handler = ctrl_hdlr; - /* Setup exposure and frame/line length limits. */ - mutex_lock(&imx708->mutex); - imx708_set_framing_limits(imx708); - mutex_unlock(&imx708->mutex); - return 0; - -error: - v4l2_ctrl_handler_free(ctrl_hdlr); - mutex_destroy(&imx708->mutex); - - return ret; -} - -static void imx708_free_controls(struct imx708 *imx708) -{ - v4l2_ctrl_handler_free(imx708->sd.ctrl_handler); - mutex_destroy(&imx708->mutex); } static int imx708_check_hwcfg(struct device *dev, struct imx708 *imx708) @@ -1948,7 +1066,6 @@ static int imx708_check_hwcfg(struct device *dev, struct imx708 *imx708) .bus_type = V4L2_MBUS_CSI2_DPHY }; int ret = -EINVAL; - int i; endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); if (!endpoint) { @@ -1967,29 +1084,13 @@ static int imx708_check_hwcfg(struct device *dev, struct imx708 *imx708) goto error_out; } - /* Check the link frequency set in device tree */ - if (!ep_cfg.nr_of_link_frequencies) { - dev_err(dev, "link-frequency property not found in DT\n"); - goto error_out; - } - - for (i = 0; i < ARRAY_SIZE(link_freqs); i++) { - if (link_freqs[i] == ep_cfg.link_frequencies[0]) { - imx708->link_freq_idx = i; - break; - } - } - - if (i == ARRAY_SIZE(link_freqs)) { - dev_err(dev, "Link frequency not supported: %lld\n", - ep_cfg.link_frequencies[0]); - ret = -EINVAL; - goto error_out; - } - imx708->csi_flags = ep_cfg.bus.mipi_csi2.flags; - ret = 0; + /* Check the link frequency set in device tree */ + ret = v4l2_link_freq_to_bitmap(dev, ep_cfg.link_frequencies, + ep_cfg.nr_of_link_frequencies, + link_freqs, ARRAY_SIZE(link_freqs), + &imx708->link_freq_bitmap); error_out: v4l2_fwnode_endpoint_free(&ep_cfg); @@ -2010,12 +1111,15 @@ static int imx708_probe(struct i2c_client *client) v4l2_i2c_subdev_init(&imx708->sd, client, &imx708_subdev_ops); - /* Check the hardware configuration in device tree */ + imx708->cci = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(imx708->cci)) + return dev_err_probe(dev, PTR_ERR(imx708->cci), + "failed to init CCI\n"); + if (imx708_check_hwcfg(dev, imx708)) return -EINVAL; - /* Get system clock (inclk) */ - imx708->inclk = devm_clk_get(dev, "inclk"); + imx708->inclk = devm_v4l2_sensor_clk_get(dev, NULL); if (IS_ERR(imx708->inclk)) return dev_err_probe(dev, PTR_ERR(imx708->inclk), "failed to get inclk\n"); @@ -2026,18 +1130,21 @@ static int imx708_probe(struct i2c_client *client) "inclk frequency not supported: %d Hz\n", imx708->inclk_freq); - ret = imx708_get_regulators(imx708); + for (int i = 0; i < ARRAY_SIZE(imx708_supply_name); i++) + imx708->supplies[i].supply = imx708_supply_name[i]; + + ret = devm_regulator_bulk_get(&client->dev, + ARRAY_SIZE(imx708_supply_name), + imx708->supplies); if (ret) return dev_err_probe(dev, ret, "failed to get regulators\n"); - /* Request optional enable pin */ imx708->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(imx708->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(imx708->reset_gpio), + "failed to get reset GPIO\n"); - /* - * The sensor must be powered for imx708_identify_module() - * to be able to read the CHIP_ID register - */ ret = imx708_power_on(dev); if (ret) return ret; @@ -2046,61 +1153,57 @@ static int imx708_probe(struct i2c_client *client) if (ret) goto error_power_off; - /* Initialize default format */ - imx708_set_default_format(imx708); - - /* - * Enable runtime PM with autosuspend. As the device has been powered - * manually, mark it as active, and increase the usage count without - * resuming the device. - */ pm_runtime_set_active(dev); - pm_runtime_get_noresume(dev); pm_runtime_enable(dev); - pm_runtime_set_autosuspend_delay(dev, 5000); - pm_runtime_use_autosuspend(dev); - /* This needs the pm runtime to be registered. */ ret = imx708_init_controls(imx708); if (ret) goto error_pm_runtime; - /* Initialize subdev */ imx708->sd.internal_ops = &imx708_internal_ops; - imx708->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | - V4L2_SUBDEV_FL_HAS_EVENTS; + imx708->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; imx708->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; - /* Initialize source pads */ - imx708->pad[IMAGE_PAD].flags = MEDIA_PAD_FL_SOURCE; - imx708->pad[METADATA_PAD].flags = MEDIA_PAD_FL_SOURCE; + imx708->pad[IMX708_SOURCE_PAD].flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_pads_init(&imx708->sd.entity, NUM_PADS, imx708->pad); + ret = media_entity_pads_init(&imx708->sd.entity, IMX708_NUM_PADS, + imx708->pad); if (ret) { - dev_err(dev, "failed to init entity pads: %d\n", ret); + dev_err_probe(dev, ret, "failed to init entity pads\n"); goto error_handler_free; } + imx708->sd.state_lock = imx708->ctrl_handler.lock; + ret = v4l2_subdev_init_finalize(&imx708->sd); + if (ret) { + dev_err_probe(dev, ret, "failed to finalize subdev\n"); + goto error_media_entity; + } + ret = v4l2_async_register_subdev_sensor(&imx708->sd); if (ret < 0) { - dev_err(dev, "failed to register sensor sub-device: %d\n", ret); - goto error_media_entity; + dev_err_probe(dev, ret, "failed to register sensor sub-device\n"); + goto error_subdev_cleanup; } - pm_runtime_mark_last_busy(dev); - pm_runtime_put_autosuspend(dev); + pm_runtime_idle(dev); + pm_runtime_set_autosuspend_delay(dev, 5000); + pm_runtime_use_autosuspend(dev); return 0; +error_subdev_cleanup: + v4l2_subdev_cleanup(&imx708->sd); + error_media_entity: media_entity_cleanup(&imx708->sd.entity); error_handler_free: - imx708_free_controls(imx708); + v4l2_ctrl_handler_free(imx708->sd.ctrl_handler); error_pm_runtime: pm_runtime_disable(dev); - pm_runtime_put_noidle(dev); + pm_runtime_set_suspended(dev); error_power_off: imx708_power_off(&client->dev); @@ -2114,8 +1217,9 @@ static void imx708_remove(struct i2c_client *client) struct imx708 *imx708 = to_imx708(sd); v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(sd); media_entity_cleanup(&sd->entity); - imx708_free_controls(imx708); + v4l2_ctrl_handler_free(imx708->sd.ctrl_handler); pm_runtime_disable(&client->dev); if (!pm_runtime_status_suspended(&client->dev)) @@ -2130,7 +1234,6 @@ static const struct of_device_id imx708_dt_ids[] = { MODULE_DEVICE_TABLE(of, imx708_dt_ids); static const struct dev_pm_ops imx708_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(imx708_suspend, imx708_resume) SET_RUNTIME_PM_OPS(imx708_power_off, imx708_power_on, NULL) }; @@ -2138,7 +1241,7 @@ static struct i2c_driver imx708_i2c_driver = { .driver = { .name = "imx708", .of_match_table = imx708_dt_ids, - .pm = &imx708_pm_ops, + .pm = pm_ptr(&imx708_pm_ops), }, .probe = imx708_probe, .remove = imx708_remove, @@ -2147,5 +1250,6 @@ static struct i2c_driver imx708_i2c_driver = { module_i2c_driver(imx708_i2c_driver); MODULE_AUTHOR("David Plowman "); +MODULE_AUTHOR("Jai Luthra "); MODULE_DESCRIPTION("Sony IMX708 sensor driver"); -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL");