From f9b1e0ffdd9b5134d9356d7e09e9c1a065fdfa13 Mon Sep 17 00:00:00 2001 From: Richard Oliver Date: Thu, 27 Jun 2024 10:11:44 +0100 Subject: [PATCH 1244/1350] media: i2c: Add driver for Sony IMX500 sensor The Sony IMX500 is a stacked 1/2.3-inch CMOS digital image sensor and inbuilt AI processor with an active array CNN (Convolutional Neural Network) inference engine. The native sensor size is 4056H x 3040V, and the module also contains an in-built ISP for the CNN. The module is programmable through an I2C interface with firmware and neural network uploads being made over SPI. This driver supports imaging only. Signed-off-by: Richard Oliver --- MAINTAINERS | 1 + drivers/media/i2c/Kconfig | 12 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/imx500.c | 1610 ++++++++++++++++++++++++++++++++++++ 4 files changed, 1624 insertions(+) create mode 100644 drivers/media/i2c/imx500.c --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20133,6 +20133,7 @@ L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media_tree.git F: Documentation/devicetree/bindings/media/i2c/sony,imx500.yaml +F: drivers/media/i2c/imx500.c SONY IMX519 SENSOR DRIVER M: Arducam Kernel Maintenance --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -236,6 +236,18 @@ config VIDEO_IMX477 To compile this driver as a module, choose M here: the module will be called imx477. +config VIDEO_IMX500 + tristate "Sony IMX500 sensor support" + depends on I2C && VIDEO_DEV + select VIDEO_V4L2_SUBDEV_API + select V4L2_CCI_I2C + help + This is a Video4Linux2 sensor driver for the Sony + IMX500 camera. + + To compile this driver as a module, choose M here: the + module will be called IMX500. + config VIDEO_IMX519 tristate "Arducam IMX519 sensor support" depends on I2C && VIDEO_DEV --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -58,6 +58,7 @@ obj-$(CONFIG_VIDEO_IMX355) += imx355.o obj-$(CONFIG_VIDEO_IMX412) += imx412.o obj-$(CONFIG_VIDEO_IMX415) += imx415.o obj-$(CONFIG_VIDEO_IMX477) += imx477.o +obj-$(CONFIG_VIDEO_IMX500) += imx500.o obj-$(CONFIG_VIDEO_IMX519) += imx519.o obj-$(CONFIG_VIDEO_IMX708) += imx708.o obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o --- /dev/null +++ b/drivers/media/i2c/imx500.c @@ -0,0 +1,1610 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * A V4L2 driver for Sony IMX500 cameras. + * Copyright (C) 2024, Raspberry Pi Ltd + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Chip ID */ +#define IMX500_REG_CHIP_ID CCI_REG16(0x0016) +#define IMX500_CHIP_ID 0x0500 + +#define IMX500_REG_MODE_SELECT CCI_REG8(0x0100) +#define IMX500_MODE_STANDBY 0x00 +#define IMX500_MODE_STREAMING 0x01 + +#define IMX500_REG_IMAGE_ONLY_MODE CCI_REG8(0xa700) +#define IMX500_IMAGE_ONLY_FALSE 0x00 +#define IMX500_IMAGE_ONLY_TRUE 0x01 + +#define IMX500_REG_ORIENTATION CCI_REG8(0x101) + +#define IMX500_XCLK_FREQ 24000000 + +#define IMX500_DEFAULT_LINK_FREQ 444000000 + +#define IMX500_PIXEL_RATE 744000000 + +/* V_TIMING internal */ +#define IMX500_REG_FRAME_LENGTH CCI_REG16(0x0340) +#define IMX500_FRAME_LENGTH_MAX 0xffdc +#define IMX500_VBLANK_MIN 4 + +/* H_TIMING internal */ +#define IMX500_REG_LINE_LENGTH CCI_REG16(0x0342) +#define IMX500_LINE_LENGTH_MAX 0xfff0 + +/* Long exposure multiplier */ +#define IMX500_LONG_EXP_SHIFT_MAX 7 +#define IMX500_LONG_EXP_SHIFT_REG CCI_REG8(0x3210) + +/* Exposure control */ +#define IMX500_REG_EXPOSURE CCI_REG16(0x0202) +#define IMX500_EXPOSURE_OFFSET 22 +#define IMX500_EXPOSURE_MIN 8 +#define IMX500_EXPOSURE_STEP 1 +#define IMX500_EXPOSURE_DEFAULT 0x640 +#define IMX500_EXPOSURE_MAX (IMX500_FRAME_LENGTH_MAX - IMX500_EXPOSURE_OFFSET) + +/* Analog gain control */ +#define IMX500_REG_ANALOG_GAIN CCI_REG16(0x0204) +#define IMX500_ANA_GAIN_MIN 0 +#define IMX500_ANA_GAIN_MAX 978 +#define IMX500_ANA_GAIN_STEP 1 +#define IMX500_ANA_GAIN_DEFAULT 0x0 + +/* Colour balance controls */ +#define IMX500_REG_COLOUR_BALANCE_R CCI_REG16(0xd804) +#define IMX500_REG_COLOUR_BALANCE_GR CCI_REG16(0xd806) +#define IMX500_REG_COLOUR_BALANCE_GB CCI_REG16(0xd808) +#define IMX500_REG_COLOUR_BALANCE_B CCI_REG16(0xd80a) +#define IMX500_COLOUR_BALANCE_MIN 0x0001 +#define IMX500_COLOUR_BALANCE_MAX 0x0fff +#define IMX500_COLOUR_BALANCE_STEP 0x0001 +#define IMX500_COLOUR_BALANCE_DEFAULT 0x0100 + +/* Embedded sizes */ +#define IMX500_MAX_EMBEDDED_SIZE \ + (2 * ((((IMX500_PIXEL_ARRAY_WIDTH * 10) >> 3) + 15) & ~15)) + +/* IMX500 native and active pixel array size. */ +#define IMX500_NATIVE_WIDTH 4072U +#define IMX500_NATIVE_HEIGHT 3176U +#define IMX500_PIXEL_ARRAY_LEFT 8U +#define IMX500_PIXEL_ARRAY_TOP 16U +#define IMX500_PIXEL_ARRAY_WIDTH 4056U +#define IMX500_PIXEL_ARRAY_HEIGHT 3040U + +#define NUM_PADS 1 + +/* regulator supplies */ +static const char *const imx500_supply_name[] = { + /* Supplies can be enabled in any order */ + "vana", /* Analog (2.7V) supply */ + "vdig", /* Digital Core (0.84V) supply */ + "vif", /* Interface (1.8V) supply */ +}; + +#define IMX500_NUM_SUPPLIES ARRAY_SIZE(imx500_supply_name) + +struct imx500_reg_list { + unsigned int num_of_regs; + const struct cci_reg_sequence *regs; +}; + +/* Mode : resolution and related config&values */ +struct imx500_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; + + /* Default framerate. */ + unsigned int framerate_default; + + /* Default register values */ + struct imx500_reg_list reg_list; +}; + +static const struct cci_reg_sequence mode_common_regs[] = { + { CCI_REG8(0x0305), 0x02 }, + { CCI_REG8(0x0306), 0x00 }, + { CCI_REG8(0x030d), 0x02 }, + { CCI_REG8(0x030e), 0x00 }, + { CCI_REG8(0x0106), 0x01 }, /* FAST_STANDBY_CTL */ + { CCI_REG8(0x0136), 0x1b }, /* EXCLK_FREQ */ + { CCI_REG8(0x0137), 0x00 }, + { CCI_REG8(0x0138), 0x01 }, /* TEMP_SENS_CTL */ + { CCI_REG8(0x0112), 0x0a }, + { CCI_REG8(0x0113), 0x0a }, + { CCI_REG8(0x0114), 0x01 }, /* CSI_LANE_MODE */ +}; + +/* 12 mpix 15fps */ +static const struct cci_reg_sequence mode_4056x3040_regs[] = { + { CCI_REG8(0x0340), 0x12 }, + { CCI_REG8(0x0341), 0x42 }, + { CCI_REG8(0x0342), 0x45 }, + { CCI_REG8(0x0343), 0xec }, + { CCI_REG8(0x3210), 0x00 }, + { CCI_REG8(0x0344), 0x00 }, + { CCI_REG8(0x0345), 0x00 }, + { CCI_REG8(0x0346), 0x00 }, + { CCI_REG8(0x0347), 0x00 }, + { CCI_REG8(0x0348), 0x0f }, + { CCI_REG8(0x0349), 0xd7 }, + { CCI_REG8(0x0350), 0x00 }, + { CCI_REG8(0x034a), 0x0b }, + { CCI_REG8(0x034b), 0xdf }, + { CCI_REG8(0x3f58), 0x01 }, + { CCI_REG8(0x0381), 0x01 }, + { CCI_REG8(0x0383), 0x01 }, + { CCI_REG8(0x0385), 0x01 }, + { CCI_REG8(0x0387), 0x01 }, + { CCI_REG8(0x0900), 0x00 }, + { CCI_REG8(0x0901), 0x11 }, + { CCI_REG8(0x0902), 0x00 }, + { CCI_REG8(0x3241), 0x11 }, + { CCI_REG8(0x3242), 0x01 }, + { CCI_REG8(0x3250), 0x00 }, + { CCI_REG8(0x3f0f), 0x00 }, + { CCI_REG8(0x3f40), 0x00 }, + { CCI_REG8(0x3f41), 0x00 }, + { CCI_REG8(0x3f42), 0x00 }, + { CCI_REG8(0x3f43), 0x00 }, + { CCI_REG8(0xb34e), 0x00 }, + { CCI_REG8(0xb351), 0x20 }, + { CCI_REG8(0xb35c), 0x00 }, + { CCI_REG8(0xb35e), 0x08 }, + { CCI_REG8(0x0401), 0x00 }, + { CCI_REG8(0x0404), 0x00 }, + { CCI_REG8(0x0405), 0x10 }, + { CCI_REG8(0x0408), 0x00 }, + { CCI_REG8(0x0409), 0x00 }, + { CCI_REG8(0x040a), 0x00 }, + { CCI_REG8(0x040b), 0x00 }, + { CCI_REG8(0x040c), 0x0f }, + { CCI_REG8(0x040d), 0xd8 }, + { CCI_REG8(0x040e), 0x0b }, + { CCI_REG8(0x040f), 0xe0 }, + { CCI_REG8(0x034c), 0x0f }, + { CCI_REG8(0x034d), 0xd8 }, + { CCI_REG8(0x034e), 0x0b }, + { CCI_REG8(0x034f), 0xe0 }, + { CCI_REG8(0x0301), 0x05 }, + { CCI_REG8(0x0303), 0x02 }, + { CCI_REG8(0x0307), 0x9b }, + { CCI_REG8(0x0309), 0x0a }, + { CCI_REG8(0x030b), 0x01 }, + { CCI_REG8(0x030f), 0x4a }, + { CCI_REG8(0x0310), 0x01 }, + { CCI_REG8(0x0820), 0x07 }, + { CCI_REG8(0x0821), 0xce }, + { CCI_REG8(0x0822), 0x00 }, + { CCI_REG8(0x0823), 0x00 }, + { CCI_REG8(0x3e20), 0x01 }, + { CCI_REG8(0x3e35), 0x01 }, + { CCI_REG8(0x3e36), 0x01 }, + { CCI_REG8(0x3e37), 0x00 }, + { CCI_REG8(0x3e3a), 0x01 }, + { CCI_REG8(0x3e3b), 0x00 }, + { CCI_REG8(0x00e3), 0x00 }, + { CCI_REG8(0x00e4), 0x00 }, + { CCI_REG8(0x00e6), 0x00 }, + { CCI_REG8(0x00e7), 0x00 }, + { CCI_REG8(0x00e8), 0x00 }, + { CCI_REG8(0x00e9), 0x00 }, + { CCI_REG8(0x3f50), 0x00 }, + { CCI_REG8(0x3f56), 0x02 }, + { CCI_REG8(0x3f57), 0x42 }, + { CCI_REG8(0x3606), 0x01 }, + { CCI_REG8(0x3607), 0x01 }, + { CCI_REG8(0x3f26), 0x00 }, + { CCI_REG8(0x3f4a), 0x00 }, + { CCI_REG8(0x3f4b), 0x00 }, + { CCI_REG8(0x4bc0), 0x16 }, + { CCI_REG8(0x7ba8), 0x00 }, + { CCI_REG8(0x7ba9), 0x00 }, + { CCI_REG8(0x886b), 0x00 }, + { CCI_REG8(0x579a), 0x00 }, + { CCI_REG8(0x579b), 0x0a }, + { CCI_REG8(0x579c), 0x01 }, + { CCI_REG8(0x579d), 0x2a }, + { CCI_REG8(0x57ac), 0x00 }, + { CCI_REG8(0x57ad), 0x00 }, + { CCI_REG8(0x57ae), 0x00 }, + { CCI_REG8(0x57af), 0x81 }, + { CCI_REG8(0x57be), 0x00 }, + { CCI_REG8(0x57bf), 0x00 }, + { CCI_REG8(0x57c0), 0x00 }, + { CCI_REG8(0x57c1), 0x81 }, + { CCI_REG8(0x57d0), 0x00 }, + { CCI_REG8(0x57d1), 0x00 }, + { CCI_REG8(0x57d2), 0x00 }, + { CCI_REG8(0x57d3), 0x81 }, + { CCI_REG8(0x5324), 0x00 }, + { CCI_REG8(0x5325), 0x26 }, + { CCI_REG8(0x5326), 0x00 }, + { CCI_REG8(0x5327), 0x6b }, + { CCI_REG8(0xbca7), 0x00 }, + { CCI_REG8(0x5fcc), 0x28 }, + { CCI_REG8(0x5fd7), 0x2d }, + { CCI_REG8(0x5fe2), 0x2d }, + { CCI_REG8(0x5fed), 0x2d }, + { CCI_REG8(0x5ff8), 0x2d }, + { CCI_REG8(0x6003), 0x2d }, + { CCI_REG8(0x5d0b), 0x01 }, + { CCI_REG8(0x6f6d), 0x00 }, + { CCI_REG8(0x61c9), 0x00 }, + { CCI_REG8(0x5352), 0x00 }, + { CCI_REG8(0x5353), 0x49 }, + { CCI_REG8(0x5356), 0x00 }, + { CCI_REG8(0x5357), 0x30 }, + { CCI_REG8(0x5358), 0x00 }, + { CCI_REG8(0x5359), 0x3b }, + { CCI_REG8(0x535c), 0x00 }, + { CCI_REG8(0x535d), 0xb0 }, + { CCI_REG8(0x6187), 0x18 }, + { CCI_REG8(0x6189), 0x18 }, + { CCI_REG8(0x618b), 0x18 }, + { CCI_REG8(0x618d), 0x1d }, + { CCI_REG8(0x618f), 0x1d }, + { CCI_REG8(0x5414), 0x01 }, + { CCI_REG8(0x5415), 0x0c }, + { CCI_REG8(0xbca8), 0x0a }, + { CCI_REG8(0x5fcf), 0x1e }, + { CCI_REG8(0x5fda), 0x1e }, + { CCI_REG8(0x5fe5), 0x1e }, + { CCI_REG8(0x5ff0), 0x1e }, + { CCI_REG8(0x5ffb), 0x1e }, + { CCI_REG8(0x6006), 0x1e }, + { CCI_REG8(0x616e), 0x04 }, + { CCI_REG8(0x616f), 0x04 }, + { CCI_REG8(0x6170), 0x04 }, + { CCI_REG8(0x6171), 0x06 }, + { CCI_REG8(0x6172), 0x06 }, + { CCI_REG8(0x6173), 0x0c }, + { CCI_REG8(0x6174), 0x0c }, + { CCI_REG8(0x6175), 0x0c }, + { CCI_REG8(0x6176), 0x00 }, + { CCI_REG8(0x6177), 0x10 }, + { CCI_REG8(0x6178), 0x00 }, + { CCI_REG8(0x6179), 0x1a }, + { CCI_REG8(0x617a), 0x00 }, + { CCI_REG8(0x617b), 0x1a }, + { CCI_REG8(0x617c), 0x00 }, + { CCI_REG8(0x617d), 0x27 }, + { CCI_REG8(0x617e), 0x00 }, + { CCI_REG8(0x617f), 0x27 }, + { CCI_REG8(0x6180), 0x00 }, + { CCI_REG8(0x6181), 0x44 }, + { CCI_REG8(0x6182), 0x00 }, + { CCI_REG8(0x6183), 0x44 }, + { CCI_REG8(0x6184), 0x00 }, + { CCI_REG8(0x6185), 0x44 }, + { CCI_REG8(0x5dfc), 0x0a }, + { CCI_REG8(0x5e00), 0x0a }, + { CCI_REG8(0x5e04), 0x0a }, + { CCI_REG8(0x5e08), 0x0a }, + { CCI_REG8(0x5dfd), 0x0a }, + { CCI_REG8(0x5e01), 0x0a }, + { CCI_REG8(0x5e05), 0x0a }, + { CCI_REG8(0x5e09), 0x0a }, + { CCI_REG8(0x5dfe), 0x0a }, + { CCI_REG8(0x5e02), 0x0a }, + { CCI_REG8(0x5e06), 0x0a }, + { CCI_REG8(0x5e0a), 0x0a }, + { CCI_REG8(0x5dff), 0x0a }, + { CCI_REG8(0x5e03), 0x0a }, + { CCI_REG8(0x5e07), 0x0a }, + { CCI_REG8(0x5e0b), 0x0a }, + { CCI_REG8(0x5dec), 0x12 }, + { CCI_REG8(0x5df0), 0x12 }, + { CCI_REG8(0x5df4), 0x21 }, + { CCI_REG8(0x5df8), 0x31 }, + { CCI_REG8(0x5ded), 0x12 }, + { CCI_REG8(0x5df1), 0x12 }, + { CCI_REG8(0x5df5), 0x21 }, + { CCI_REG8(0x5df9), 0x31 }, + { CCI_REG8(0x5dee), 0x12 }, + { CCI_REG8(0x5df2), 0x12 }, + { CCI_REG8(0x5df6), 0x21 }, + { CCI_REG8(0x5dfa), 0x31 }, + { CCI_REG8(0x5def), 0x12 }, + { CCI_REG8(0x5df3), 0x12 }, + { CCI_REG8(0x5df7), 0x21 }, + { CCI_REG8(0x5dfb), 0x31 }, + { CCI_REG8(0x5ddc), 0x0d }, + { CCI_REG8(0x5de0), 0x0d }, + { CCI_REG8(0x5de4), 0x0d }, + { CCI_REG8(0x5de8), 0x0d }, + { CCI_REG8(0x5ddd), 0x0d }, + { CCI_REG8(0x5de1), 0x0d }, + { CCI_REG8(0x5de5), 0x0d }, + { CCI_REG8(0x5de9), 0x0d }, + { CCI_REG8(0x5dde), 0x0d }, + { CCI_REG8(0x5de2), 0x0d }, + { CCI_REG8(0x5de6), 0x0d }, + { CCI_REG8(0x5dea), 0x0d }, + { CCI_REG8(0x5ddf), 0x0d }, + { CCI_REG8(0x5de3), 0x0d }, + { CCI_REG8(0x5de7), 0x0d }, + { CCI_REG8(0x5deb), 0x0d }, + { CCI_REG8(0x5dcc), 0x55 }, + { CCI_REG8(0x5dd0), 0x50 }, + { CCI_REG8(0x5dd4), 0x4b }, + { CCI_REG8(0x5dd8), 0x4b }, + { CCI_REG8(0x5dcd), 0x55 }, + { CCI_REG8(0x5dd1), 0x50 }, + { CCI_REG8(0x5dd5), 0x4b }, + { CCI_REG8(0x5dd9), 0x4b }, + { CCI_REG8(0x5dce), 0x55 }, + { CCI_REG8(0x5dd2), 0x50 }, + { CCI_REG8(0x5dd6), 0x4b }, + { CCI_REG8(0x5dda), 0x4b }, + { CCI_REG8(0x5dcf), 0x55 }, + { CCI_REG8(0x5dd3), 0x50 }, + { CCI_REG8(0x5dd7), 0x4b }, + { CCI_REG8(0x5ddb), 0x4b }, + { CCI_REG8(0x0202), 0x12 }, + { CCI_REG8(0x0203), 0x2c }, + { CCI_REG8(0x0204), 0x00 }, + { CCI_REG8(0x0205), 0x00 }, + { CCI_REG8(0x020e), 0x01 }, + { CCI_REG8(0x020f), 0x00 }, + { CCI_REG8(0x0210), 0x01 }, + { CCI_REG8(0x0211), 0x00 }, + { CCI_REG8(0x0212), 0x01 }, + { CCI_REG8(0x0213), 0x00 }, + { CCI_REG8(0x0214), 0x01 }, + { CCI_REG8(0x0215), 0x00 }, +}; + +/* 2x2 binned. 56fps */ +static const struct cci_reg_sequence mode_2028x1520_regs[] = { + { CCI_REG8(0x0112), 0x0a }, + { CCI_REG8(0x0113), 0x0a }, + { CCI_REG8(0x0114), 0x01 }, + { CCI_REG8(0x0342), 0x24 }, + { CCI_REG8(0x0343), 0xb6 }, + { CCI_REG8(0x0340), 0x0b }, + { CCI_REG8(0x0341), 0x9c }, + { CCI_REG8(0x3210), 0x00 }, + { CCI_REG8(0x0344), 0x00 }, + { CCI_REG8(0x0345), 0x00 }, + { CCI_REG8(0x0346), 0x00 }, + { CCI_REG8(0x0347), 0x00 }, + { CCI_REG8(0x0348), 0x0f }, + { CCI_REG8(0x0349), 0xd7 }, + { CCI_REG8(0x0350), 0x00 }, + { CCI_REG8(0x034a), 0x0b }, + { CCI_REG8(0x034b), 0xdf }, + { CCI_REG8(0x3f58), 0x01 }, + { CCI_REG8(0x0381), 0x01 }, + { CCI_REG8(0x0383), 0x01 }, + { CCI_REG8(0x0385), 0x01 }, + { CCI_REG8(0x0387), 0x01 }, + { CCI_REG8(0x0900), 0x01 }, + { CCI_REG8(0x0901), 0x22 }, + { CCI_REG8(0x0902), 0x02 }, + { CCI_REG8(0x3241), 0x11 }, + { CCI_REG8(0x3242), 0x01 }, + { CCI_REG8(0x3250), 0x03 }, + { CCI_REG8(0x3f0f), 0x00 }, + { CCI_REG8(0x3f40), 0x00 }, + { CCI_REG8(0x3f41), 0x00 }, + { CCI_REG8(0x3f42), 0x00 }, + { CCI_REG8(0x3f43), 0x00 }, + { CCI_REG8(0xb34e), 0x00 }, + { CCI_REG8(0xb351), 0x20 }, + { CCI_REG8(0xb35c), 0x00 }, + { CCI_REG8(0xb35e), 0x08 }, + { CCI_REG8(0x0401), 0x00 }, + { CCI_REG8(0x0404), 0x00 }, + { CCI_REG8(0x0405), 0x10 }, + { CCI_REG8(0x0408), 0x00 }, + { CCI_REG8(0x0409), 0x00 }, + { CCI_REG8(0x040a), 0x00 }, + { CCI_REG8(0x040b), 0x00 }, + { CCI_REG8(0x040c), 0x07 }, + { CCI_REG8(0x040d), 0xec }, + { CCI_REG8(0x040e), 0x05 }, + { CCI_REG8(0x040f), 0xf0 }, + { CCI_REG8(0x034c), 0x07 }, + { CCI_REG8(0x034d), 0xec }, + { CCI_REG8(0x034e), 0x05 }, + { CCI_REG8(0x034f), 0xf0 }, + { CCI_REG8(0x0301), 0x05 }, + { CCI_REG8(0x0303), 0x02 }, + { CCI_REG8(0x0307), 0x9b }, + { CCI_REG8(0x0309), 0x0a }, + { CCI_REG8(0x030b), 0x01 }, + { CCI_REG8(0x030f), 0x4a }, + { CCI_REG8(0x0310), 0x01 }, + { CCI_REG8(0x0820), 0x07 }, + { CCI_REG8(0x0821), 0xce }, + { CCI_REG8(0x0822), 0x00 }, + { CCI_REG8(0x0823), 0x00 }, + { CCI_REG8(0x3e20), 0x01 }, + { CCI_REG8(0x3e35), 0x01 }, + { CCI_REG8(0x3e36), 0x01 }, + { CCI_REG8(0x3e37), 0x00 }, + { CCI_REG8(0x3e3a), 0x01 }, + { CCI_REG8(0x3e3b), 0x00 }, + { CCI_REG8(0x00e3), 0x00 }, + { CCI_REG8(0x00e4), 0x00 }, + { CCI_REG8(0x00e6), 0x00 }, + { CCI_REG8(0x00e7), 0x00 }, + { CCI_REG8(0x00e8), 0x00 }, + { CCI_REG8(0x00e9), 0x00 }, + { CCI_REG8(0x3f50), 0x00 }, + { CCI_REG8(0x3f56), 0x01 }, + { CCI_REG8(0x3f57), 0x30 }, + { CCI_REG8(0x3606), 0x01 }, + { CCI_REG8(0x3607), 0x01 }, + { CCI_REG8(0x3f26), 0x00 }, + { CCI_REG8(0x3f4a), 0x00 }, + { CCI_REG8(0x3f4b), 0x00 }, + { CCI_REG8(0x4bc0), 0x16 }, + { CCI_REG8(0x7ba8), 0x00 }, + { CCI_REG8(0x7ba9), 0x00 }, + { CCI_REG8(0x886b), 0x00 }, + { CCI_REG8(0x579a), 0x00 }, + { CCI_REG8(0x579b), 0x0a }, + { CCI_REG8(0x579c), 0x01 }, + { CCI_REG8(0x579d), 0x2a }, + { CCI_REG8(0x57ac), 0x00 }, + { CCI_REG8(0x57ad), 0x00 }, + { CCI_REG8(0x57ae), 0x00 }, + { CCI_REG8(0x57af), 0x81 }, + { CCI_REG8(0x57be), 0x00 }, + { CCI_REG8(0x57bf), 0x00 }, + { CCI_REG8(0x57c0), 0x00 }, + { CCI_REG8(0x57c1), 0x81 }, + { CCI_REG8(0x57d0), 0x00 }, + { CCI_REG8(0x57d1), 0x00 }, + { CCI_REG8(0x57d2), 0x00 }, + { CCI_REG8(0x57d3), 0x81 }, + { CCI_REG8(0x5324), 0x00 }, + { CCI_REG8(0x5325), 0x31 }, + { CCI_REG8(0x5326), 0x00 }, + { CCI_REG8(0x5327), 0x60 }, + { CCI_REG8(0xbca7), 0x08 }, + { CCI_REG8(0x5fcc), 0x1e }, + { CCI_REG8(0x5fd7), 0x1e }, + { CCI_REG8(0x5fe2), 0x1e }, + { CCI_REG8(0x5fed), 0x1e }, + { CCI_REG8(0x5ff8), 0x1e }, + { CCI_REG8(0x6003), 0x1e }, + { CCI_REG8(0x5d0b), 0x02 }, + { CCI_REG8(0x6f6d), 0x01 }, + { CCI_REG8(0x61c9), 0x68 }, + { CCI_REG8(0x5352), 0x00 }, + { CCI_REG8(0x5353), 0x3f }, + { CCI_REG8(0x5356), 0x00 }, + { CCI_REG8(0x5357), 0x1c }, + { CCI_REG8(0x5358), 0x00 }, + { CCI_REG8(0x5359), 0x3d }, + { CCI_REG8(0x535c), 0x00 }, + { CCI_REG8(0x535d), 0xa6 }, + { CCI_REG8(0x6187), 0x1d }, + { CCI_REG8(0x6189), 0x1d }, + { CCI_REG8(0x618b), 0x1d }, + { CCI_REG8(0x618d), 0x23 }, + { CCI_REG8(0x618f), 0x23 }, + { CCI_REG8(0x5414), 0x01 }, + { CCI_REG8(0x5415), 0x12 }, + { CCI_REG8(0xbca8), 0x00 }, + { CCI_REG8(0x5fcf), 0x28 }, + { CCI_REG8(0x5fda), 0x2d }, + { CCI_REG8(0x5fe5), 0x2d }, + { CCI_REG8(0x5ff0), 0x2d }, + { CCI_REG8(0x5ffb), 0x2d }, + { CCI_REG8(0x6006), 0x2d }, + { CCI_REG8(0x616e), 0x04 }, + { CCI_REG8(0x616f), 0x04 }, + { CCI_REG8(0x6170), 0x04 }, + { CCI_REG8(0x6171), 0x06 }, + { CCI_REG8(0x6172), 0x06 }, + { CCI_REG8(0x6173), 0x0c }, + { CCI_REG8(0x6174), 0x0c }, + { CCI_REG8(0x6175), 0x0c }, + { CCI_REG8(0x6176), 0x00 }, + { CCI_REG8(0x6177), 0x10 }, + { CCI_REG8(0x6178), 0x00 }, + { CCI_REG8(0x6179), 0x1a }, + { CCI_REG8(0x617a), 0x00 }, + { CCI_REG8(0x617b), 0x1a }, + { CCI_REG8(0x617c), 0x00 }, + { CCI_REG8(0x617d), 0x27 }, + { CCI_REG8(0x617e), 0x00 }, + { CCI_REG8(0x617f), 0x27 }, + { CCI_REG8(0x6180), 0x00 }, + { CCI_REG8(0x6181), 0x44 }, + { CCI_REG8(0x6182), 0x00 }, + { CCI_REG8(0x6183), 0x44 }, + { CCI_REG8(0x6184), 0x00 }, + { CCI_REG8(0x6185), 0x44 }, + { CCI_REG8(0x5dfc), 0x0a }, + { CCI_REG8(0x5e00), 0x0a }, + { CCI_REG8(0x5e04), 0x0a }, + { CCI_REG8(0x5e08), 0x0a }, + { CCI_REG8(0x5dfd), 0x0a }, + { CCI_REG8(0x5e01), 0x0a }, + { CCI_REG8(0x5e05), 0x0a }, + { CCI_REG8(0x5e09), 0x0a }, + { CCI_REG8(0x5dfe), 0x0a }, + { CCI_REG8(0x5e02), 0x0a }, + { CCI_REG8(0x5e06), 0x0a }, + { CCI_REG8(0x5e0a), 0x0a }, + { CCI_REG8(0x5dff), 0x0a }, + { CCI_REG8(0x5e03), 0x0a }, + { CCI_REG8(0x5e07), 0x0a }, + { CCI_REG8(0x5e0b), 0x0a }, + { CCI_REG8(0x5dec), 0x12 }, + { CCI_REG8(0x5df0), 0x12 }, + { CCI_REG8(0x5df4), 0x21 }, + { CCI_REG8(0x5df8), 0x31 }, + { CCI_REG8(0x5ded), 0x12 }, + { CCI_REG8(0x5df1), 0x12 }, + { CCI_REG8(0x5df5), 0x21 }, + { CCI_REG8(0x5df9), 0x31 }, + { CCI_REG8(0x5dee), 0x12 }, + { CCI_REG8(0x5df2), 0x12 }, + { CCI_REG8(0x5df6), 0x21 }, + { CCI_REG8(0x5dfa), 0x31 }, + { CCI_REG8(0x5def), 0x12 }, + { CCI_REG8(0x5df3), 0x12 }, + { CCI_REG8(0x5df7), 0x21 }, + { CCI_REG8(0x5dfb), 0x31 }, + { CCI_REG8(0x5ddc), 0x0d }, + { CCI_REG8(0x5de0), 0x0d }, + { CCI_REG8(0x5de4), 0x0d }, + { CCI_REG8(0x5de8), 0x0d }, + { CCI_REG8(0x5ddd), 0x0d }, + { CCI_REG8(0x5de1), 0x0d }, + { CCI_REG8(0x5de5), 0x0d }, + { CCI_REG8(0x5de9), 0x0d }, + { CCI_REG8(0x5dde), 0x0d }, + { CCI_REG8(0x5de2), 0x0d }, + { CCI_REG8(0x5de6), 0x0d }, + { CCI_REG8(0x5dea), 0x0d }, + { CCI_REG8(0x5ddf), 0x0d }, + { CCI_REG8(0x5de3), 0x0d }, + { CCI_REG8(0x5de7), 0x0d }, + { CCI_REG8(0x5deb), 0x0d }, + { CCI_REG8(0x5dcc), 0x55 }, + { CCI_REG8(0x5dd0), 0x50 }, + { CCI_REG8(0x5dd4), 0x4b }, + { CCI_REG8(0x5dd8), 0x4b }, + { CCI_REG8(0x5dcd), 0x55 }, + { CCI_REG8(0x5dd1), 0x50 }, + { CCI_REG8(0x5dd5), 0x4b }, + { CCI_REG8(0x5dd9), 0x4b }, + { CCI_REG8(0x5dce), 0x55 }, + { CCI_REG8(0x5dd2), 0x50 }, + { CCI_REG8(0x5dd6), 0x4b }, + { CCI_REG8(0x5dda), 0x4b }, + { CCI_REG8(0x5dcf), 0x55 }, + { CCI_REG8(0x5dd3), 0x50 }, + { CCI_REG8(0x5dd7), 0x4b }, + { CCI_REG8(0x5ddb), 0x4b }, + { CCI_REG8(0x0202), 0x0b }, + { CCI_REG8(0x0203), 0x86 }, + { CCI_REG8(0x0204), 0x00 }, + { CCI_REG8(0x0205), 0x00 }, + { CCI_REG8(0x020e), 0x01 }, + { CCI_REG8(0x020f), 0x00 }, + { CCI_REG8(0x0210), 0x01 }, + { CCI_REG8(0x0211), 0x00 }, + { CCI_REG8(0x0212), 0x01 }, + { CCI_REG8(0x0213), 0x00 }, + { CCI_REG8(0x0214), 0x01 }, + { CCI_REG8(0x0215), 0x00 }, +}; + +/* Mode configs */ +static const struct imx500_mode imx500_supported_modes[] = { + { + /* 12MPix 10fps mode */ + .width = 4056, + .height = 3040, + .line_length_pix = 17900, + .crop = { + .left = IMX500_PIXEL_ARRAY_LEFT, + .top = IMX500_PIXEL_ARRAY_TOP, + .width = 4056, + .height = 3040, + }, + .framerate_default = 10, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_4056x3040_regs), + .regs = mode_4056x3040_regs, + }, + }, + { + /* 2x2 binned 40fps mode */ + .width = 2028, + .height = 1520, + .line_length_pix = 9398, + .crop = { + .left = IMX500_PIXEL_ARRAY_LEFT, + .top = IMX500_PIXEL_ARRAY_TOP, + .width = 4056, + .height = 3040, + }, + .framerate_default = 40, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_2028x1520_regs), + .regs = mode_2028x1520_regs, + }, + }, +}; + +/* + * The supported formats. + * This table MUST contain 4 entries per format, to cover the various flip + * combinations in the order + * - no flip + * - h flip + * - v flip + * - h&v flips + */ +static const u32 codes[] = { + /* 10-bit modes. */ + MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SBGGR10_1X10, +}; + +struct imx500 { + struct v4l2_subdev sd; + struct media_pad pad; + struct regmap *regmap; + + unsigned int fmt_code; + + struct clk *xclk; + u32 xclk_freq; + + struct gpio_desc *reset_gpio; + struct regulator_bulk_data supplies[IMX500_NUM_SUPPLIES]; + + struct v4l2_ctrl_handler ctrl_handler; + /* V4L2 Controls */ + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + + /* Current mode */ + const struct imx500_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; +}; + +static inline struct imx500 *to_imx500(struct v4l2_subdev *_sd) +{ + return container_of(_sd, struct imx500, sd); +} + +/* Get bayer order based on flip setting. */ +static u32 imx500_get_format_code(struct imx500 *imx500) +{ + unsigned int i; + + lockdep_assert_held(&imx500->mutex); + + i = (imx500->vflip->val ? 2 : 0) | (imx500->hflip->val ? 1 : 0); + + return codes[i]; +} + +static void imx500_set_default_format(struct imx500 *imx500) +{ + /* Set default mode to max resolution */ + imx500->mode = &imx500_supported_modes[0]; + imx500->fmt_code = MEDIA_BUS_FMT_SRGGB10_1X10; +} + +static void imx500_adjust_exposure_range(struct imx500 *imx500) +{ + int exposure_max, exposure_def; + + /* Honour the VBLANK limits when setting exposure. */ + exposure_max = imx500->mode->height + imx500->vblank->val - + IMX500_EXPOSURE_OFFSET; + exposure_def = min(exposure_max, imx500->exposure->val); + __v4l2_ctrl_modify_range(imx500->exposure, imx500->exposure->minimum, + exposure_max, imx500->exposure->step, + exposure_def); +} + +static int imx500_set_frame_length(struct imx500 *imx500, unsigned int val) +{ + int ret = 0; + + imx500->long_exp_shift = 0; + + while (val > IMX500_FRAME_LENGTH_MAX) { + imx500->long_exp_shift++; + val >>= 1; + } + + ret = cci_write(imx500->regmap, IMX500_REG_FRAME_LENGTH, val, NULL); + if (ret) + return ret; + + return cci_write(imx500->regmap, IMX500_LONG_EXP_SHIFT_REG, + imx500->long_exp_shift, NULL); +} + +static int imx500_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct imx500 *imx500 = + container_of(ctrl->handler, struct imx500, ctrl_handler); + struct i2c_client *client = v4l2_get_subdevdata(&imx500->sd); + int ret = 0; + + /* + * The VBLANK control may change the limits of usable exposure, so check + * and adjust if necessary. + */ + if (ctrl->id == V4L2_CID_VBLANK) + imx500_adjust_exposure_range(imx500); + + /* + * Applying V4L2 control value only happens + * when power is up for streaming + */ + if (pm_runtime_get_if_in_use(&client->dev) == 0) + return 0; + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + ret = cci_write(imx500->regmap, IMX500_REG_ANALOG_GAIN, + ctrl->val, NULL); + break; + case V4L2_CID_EXPOSURE: + ret = cci_write(imx500->regmap, IMX500_REG_EXPOSURE, + ctrl->val >> imx500->long_exp_shift, NULL); + break; + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + ret = cci_write(imx500->regmap, IMX500_REG_ORIENTATION, + imx500->hflip->val | imx500->vflip->val << 1, + NULL); + break; + case V4L2_CID_VBLANK: + ret = imx500_set_frame_length(imx500, + imx500->mode->height + ctrl->val); + break; + case V4L2_CID_HBLANK: + ret = cci_write(imx500->regmap, IMX500_REG_LINE_LENGTH, + imx500->mode->width + ctrl->val, NULL); + break; + case V4L2_CID_NOTIFY_GAINS: + ret = cci_write(imx500->regmap, IMX500_REG_COLOUR_BALANCE_B, + ctrl->p_new.p_u32[0], NULL); + cci_write(imx500->regmap, IMX500_REG_COLOUR_BALANCE_GB, + ctrl->p_new.p_u32[1], &ret); + cci_write(imx500->regmap, IMX500_REG_COLOUR_BALANCE_GR, + ctrl->p_new.p_u32[2], &ret); + cci_write(imx500->regmap, IMX500_REG_COLOUR_BALANCE_R, + ctrl->p_new.p_u32[3], &ret); + break; + default: + dev_info(&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; +} + +static const struct v4l2_ctrl_ops imx500_ctrl_ops = { + .s_ctrl = imx500_set_ctrl, +}; + +static const struct v4l2_ctrl_config imx500_notify_gains_ctrl = { + .ops = &imx500_ctrl_ops, + .id = V4L2_CID_NOTIFY_GAINS, + .type = V4L2_CTRL_TYPE_U32, + .min = IMX500_COLOUR_BALANCE_MIN, + .max = IMX500_COLOUR_BALANCE_MAX, + .step = IMX500_COLOUR_BALANCE_STEP, + .def = IMX500_COLOUR_BALANCE_DEFAULT, + .dims = { 4 }, + .elem_size = sizeof(u32), +}; + +static int imx500_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct imx500 *imx500 = to_imx500(sd); + + if (code->pad >= NUM_PADS) + return -EINVAL; + + if (code->index != 0) + return -EINVAL; + + code->code = imx500_get_format_code(imx500); + + return 0; +} + +static int imx500_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct imx500 *imx500 = to_imx500(sd); + + if (fse->pad >= NUM_PADS) + return -EINVAL; + + const struct imx500_mode *mode_list = imx500_supported_modes; + unsigned int num_modes = ARRAY_SIZE(imx500_supported_modes); + + if (fse->index >= num_modes) + return -EINVAL; + + if (fse->code != imx500_get_format_code(imx500)) + 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; + + return 0; +} + +static void imx500_update_image_pad_format(struct imx500 *imx500, + const struct imx500_mode *mode, + struct v4l2_subdev_format *fmt) +{ + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + fmt->format.colorspace = V4L2_COLORSPACE_RAW; + fmt->format.ycbcr_enc = + V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->format.colorspace); + fmt->format.quantization = V4L2_MAP_QUANTIZATION_DEFAULT( + true, fmt->format.colorspace, fmt->format.ycbcr_enc); + fmt->format.xfer_func = + V4L2_MAP_XFER_FUNC_DEFAULT(fmt->format.colorspace); +} + +static int imx500_get_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct imx500 *imx500 = to_imx500(sd); + + if (fmt->pad >= NUM_PADS) + return -EINVAL; + + mutex_lock(&imx500->mutex); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_get_try_format( + &imx500->sd, sd_state, fmt->pad); + /* update the code which could change due to vflip or hflip */ + try_fmt->code = imx500_get_format_code(imx500); + fmt->format = *try_fmt; + } else { + imx500_update_image_pad_format(imx500, imx500->mode, fmt); + fmt->format.code = imx500_get_format_code(imx500); + } + + mutex_unlock(&imx500->mutex); + return 0; +} + +static unsigned int imx500_get_frame_length(const struct imx500_mode *mode, + unsigned int framerate_default) +{ + u64 frame_length; + + frame_length = IMX500_PIXEL_RATE; + do_div(frame_length, (u64)framerate_default * mode->line_length_pix); + + if (WARN_ON(frame_length > IMX500_FRAME_LENGTH_MAX)) + frame_length = IMX500_FRAME_LENGTH_MAX; + + return max_t(unsigned int, frame_length, mode->height); +} + +static void imx500_set_framing_limits(struct imx500 *imx500) +{ + unsigned int frm_length_default, hblank_min; + const struct imx500_mode *mode = imx500->mode; + + frm_length_default = + imx500_get_frame_length(mode, mode->framerate_default); + + /* Default to no long exposure multiplier. */ + imx500->long_exp_shift = 0; + + /* Update limits and set FPS to default */ + __v4l2_ctrl_modify_range( + imx500->vblank, 1, + ((1 << IMX500_LONG_EXP_SHIFT_MAX) * IMX500_FRAME_LENGTH_MAX) - + mode->height, + IMX500_VBLANK_MIN, frm_length_default - mode->height); + + /* Setting this will adjust the exposure limits as well. */ + __v4l2_ctrl_s_ctrl(imx500->vblank, frm_length_default - mode->height); + + hblank_min = mode->line_length_pix - mode->width; + __v4l2_ctrl_modify_range(imx500->hblank, hblank_min, hblank_min, 1, + hblank_min); + __v4l2_ctrl_s_ctrl(imx500->hblank, hblank_min); +} + +static int imx500_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *framefmt; + const struct imx500_mode *mode; + struct imx500 *imx500 = to_imx500(sd); + + if (fmt->pad >= NUM_PADS) + return -EINVAL; + + mutex_lock(&imx500->mutex); + + const struct imx500_mode *mode_list = imx500_supported_modes; + unsigned int num_modes = ARRAY_SIZE(imx500_supported_modes); + + /* Bayer order varies with flips */ + fmt->format.code = imx500_get_format_code(imx500); + + mode = v4l2_find_nearest_size(mode_list, num_modes, width, height, + fmt->format.width, fmt->format.height); + imx500_update_image_pad_format(imx500, mode, fmt); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + *framefmt = fmt->format; + } else if (imx500->mode != mode) { + imx500->mode = mode; + imx500->fmt_code = fmt->format.code; + imx500_set_framing_limits(imx500); + } + + mutex_unlock(&imx500->mutex); + + return 0; +} + +static const struct v4l2_rect * +__imx500_get_pad_crop(struct imx500 *imx500, 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(&imx500->sd, sd_state, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &imx500->mode->crop; + } + + return NULL; +} + +static int imx500_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 imx500 *imx500 = to_imx500(sd); + + mutex_lock(&imx500->mutex); + sel->r = *__imx500_get_pad_crop(imx500, sd_state, sel->pad, + sel->which); + mutex_unlock(&imx500->mutex); + + return 0; + } + + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = IMX500_NATIVE_WIDTH; + sel->r.height = IMX500_NATIVE_HEIGHT; + + return 0; + + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.left = IMX500_PIXEL_ARRAY_LEFT; + sel->r.top = IMX500_PIXEL_ARRAY_TOP; + sel->r.width = IMX500_PIXEL_ARRAY_WIDTH; + sel->r.height = IMX500_PIXEL_ARRAY_HEIGHT; + + return 0; + } + + return -EINVAL; +} + +/* Start streaming */ +static int imx500_start_streaming(struct imx500 *imx500) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx500->sd); + const struct imx500_reg_list *reg_list; + int ret; + + ret = pm_runtime_resume_and_get(&client->dev); + if (ret < 0) + return ret; + + ret = cci_write(imx500->regmap, IMX500_REG_IMAGE_ONLY_MODE, + IMX500_IMAGE_ONLY_TRUE, + NULL); + if (ret) { + dev_err(&client->dev, "%s failed to set image mode\n", + __func__); + return ret; + } + + if (!imx500->common_regs_written) { + ret = cci_multi_reg_write(imx500->regmap, mode_common_regs, + ARRAY_SIZE(mode_common_regs), NULL); + + if (ret) { + dev_err(&client->dev, + "%s failed to set common settings\n", __func__); + return ret; + } + + imx500->common_regs_written = true; + } + + /* Apply default values of current mode */ + reg_list = &imx500->mode->reg_list; + ret = cci_multi_reg_write(imx500->regmap, reg_list->regs, + reg_list->num_of_regs, NULL); + if (ret) { + dev_err(&client->dev, "%s failed to set mode\n", __func__); + return ret; + } + + /* Apply customized values from user */ + ret = __v4l2_ctrl_handler_setup(imx500->sd.ctrl_handler); + + /* Disable any sensor startup frame drops. This must be written here! */ + cci_write(imx500->regmap, CCI_REG8(0xD405), 0, &ret); + + /* set stream on register */ + cci_write(imx500->regmap, IMX500_REG_MODE_SELECT, IMX500_MODE_STREAMING, + &ret); + + return ret; +} + +/* Stop streaming */ +static void imx500_stop_streaming(struct imx500 *imx500) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx500->sd); + int ret; + + /* set stream off register */ + ret = cci_write(imx500->regmap, IMX500_REG_MODE_SELECT, + IMX500_MODE_STANDBY, NULL); + if (ret) + dev_err(&client->dev, "%s failed to set stream\n", __func__); + + pm_runtime_mark_last_busy(&client->dev); + pm_runtime_put_autosuspend(&client->dev); +} + +static int imx500_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct imx500 *imx500 = to_imx500(sd); + int ret = 0; + + mutex_lock(&imx500->mutex); + if (imx500->streaming == enable) { + mutex_unlock(&imx500->mutex); + return 0; + } + + if (enable) { + /* + * Apply default & customized values + * and then start streaming. + */ + ret = imx500_start_streaming(imx500); + if (ret) + goto err_start_streaming; + } else { + imx500_stop_streaming(imx500); + } + + imx500->streaming = enable; + + /* vflip and hflip cannot change during streaming */ + __v4l2_ctrl_grab(imx500->vflip, enable); + __v4l2_ctrl_grab(imx500->hflip, enable); + + mutex_unlock(&imx500->mutex); + + return ret; + +err_start_streaming: + mutex_unlock(&imx500->mutex); + + return ret; +} + +/* Power/clock management functions */ +static int imx500_power_on(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx500 *imx500 = to_imx500(sd); + int ret; + + ret = regulator_bulk_enable(IMX500_NUM_SUPPLIES, imx500->supplies); + if (ret) { + dev_err(&client->dev, "%s: failed to enable regulators\n", + __func__); + return ret; + } + + /* T4 - 1us + * Ambiguous: Regulators rising to INCK start is specified by the datasheet + * but also "Presence of INCK during Power off is acceptable" + */ + udelay(2); + + ret = clk_prepare_enable(imx500->xclk); + if (ret) { + dev_err(&client->dev, "%s: failed to enable clock\n", __func__); + goto reg_off; + } + + /* T5 - 0ms + * Ambiguous: Regulators rising to XCLR rising is specified by the datasheet + * as 0ms but also "XCLR pin should be set to 'High' after INCK supplied.". + * T4 and T5 are shown as overlapping. + */ + gpiod_set_value_cansleep(imx500->reset_gpio, 1); + + /* T7 - 9ms + * "INCK start and CXLR rising till Send Streaming Command wait time" + */ + usleep_range(9000, 12000); + + return 0; + +reg_off: + regulator_bulk_disable(IMX500_NUM_SUPPLIES, imx500->supplies); + return ret; +} + +static int imx500_power_off(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx500 *imx500 = to_imx500(sd); + + /* Datasheet specifies power down sequence as INCK disable, XCLR low, + * regulator disable. T1 (XCLR neg-edge to regulator disable) is specified + * as 0us. + * + * Note, this is not the reverse order of power up. + */ + clk_disable_unprepare(imx500->xclk); + gpiod_set_value_cansleep(imx500->reset_gpio, 0); + regulator_bulk_disable(IMX500_NUM_SUPPLIES, imx500->supplies); + + /* Force reprogramming of the common registers when powered up again. */ + imx500->common_regs_written = false; + + return 0; +} + +static int imx500_get_regulators(struct imx500 *imx500) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx500->sd); + unsigned int i; + + for (i = 0; i < IMX500_NUM_SUPPLIES; i++) + imx500->supplies[i].supply = imx500_supply_name[i]; + + return devm_regulator_bulk_get(&client->dev, IMX500_NUM_SUPPLIES, + imx500->supplies); +} + +/* Verify chip ID */ +static int imx500_identify_module(struct imx500 *imx500) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx500->sd); + int ret; + u64 val; + + ret = cci_read(imx500->regmap, IMX500_REG_CHIP_ID, &val, NULL); + if (ret) { + dev_err(&client->dev, + "failed to read chip id %x, with error %d\n", + IMX500_CHIP_ID, ret); + return ret; + } + + if (val != IMX500_CHIP_ID) { + dev_err(&client->dev, "chip id mismatch: %x!=%llx\n", + IMX500_CHIP_ID, val); + return -EIO; + } + + dev_info(&client->dev, "Device found is imx%llx\n", val); + + return 0; +} + +static const struct v4l2_subdev_core_ops imx500_core_ops = { + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_video_ops imx500_video_ops = { + .s_stream = imx500_set_stream, +}; + +static const struct v4l2_subdev_pad_ops imx500_pad_ops = { + .enum_mbus_code = imx500_enum_mbus_code, + .get_fmt = imx500_get_pad_format, + .set_fmt = imx500_set_pad_format, + .get_selection = imx500_get_selection, + .enum_frame_size = imx500_enum_frame_size, +}; + +static const struct v4l2_subdev_ops imx500_subdev_ops = { + .core = &imx500_core_ops, + .video = &imx500_video_ops, + .pad = &imx500_pad_ops, +}; + +static const s64 imx500_link_freq_menu[] = { + IMX500_DEFAULT_LINK_FREQ, +}; + +/* Initialize control handlers */ +static int imx500_init_controls(struct imx500 *imx500) +{ + struct v4l2_ctrl_handler *ctrl_hdlr; + struct i2c_client *client = v4l2_get_subdevdata(&imx500->sd); + struct v4l2_fwnode_device_properties props; + int ret; + + ctrl_hdlr = &imx500->ctrl_handler; + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 16); + if (ret) + return ret; + + mutex_init(&imx500->mutex); + ctrl_hdlr->lock = &imx500->mutex; + + /* By default, PIXEL_RATE is read only */ + imx500->pixel_rate = v4l2_ctrl_new_std( + ctrl_hdlr, &imx500_ctrl_ops, V4L2_CID_PIXEL_RATE, + IMX500_PIXEL_RATE, IMX500_PIXEL_RATE, 1, IMX500_PIXEL_RATE); + + /* LINK_FREQ is also read only */ + imx500->link_freq = + v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx500_ctrl_ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(imx500_link_freq_menu) - 1, 0, + imx500_link_freq_menu); + if (imx500->link_freq) + imx500->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + /* + * Create the controls here, but mode specific limits are setup + * in the imx500_set_framing_limits() call below. + */ + imx500->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx500_ctrl_ops, + V4L2_CID_VBLANK, 0, 0xffff, 1, 0); + imx500->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx500_ctrl_ops, + V4L2_CID_HBLANK, 0, 0xffff, 1, 0); + + imx500->exposure = v4l2_ctrl_new_std( + ctrl_hdlr, &imx500_ctrl_ops, V4L2_CID_EXPOSURE, + IMX500_EXPOSURE_MIN, IMX500_EXPOSURE_MAX, IMX500_EXPOSURE_STEP, + IMX500_EXPOSURE_DEFAULT); + + v4l2_ctrl_new_std(ctrl_hdlr, &imx500_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + IMX500_ANA_GAIN_MIN, IMX500_ANA_GAIN_MAX, + IMX500_ANA_GAIN_STEP, IMX500_ANA_GAIN_DEFAULT); + + imx500->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx500_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + if (imx500->hflip) + imx500->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + imx500->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx500_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + if (imx500->vflip) + imx500->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + v4l2_ctrl_new_custom(ctrl_hdlr, &imx500_notify_gains_ctrl, NULL); + + if (ctrl_hdlr->error) { + ret = ctrl_hdlr->error; + dev_err(&client->dev, "%s control init failed (%d)\n", __func__, + ret); + goto error; + } + + ret = v4l2_fwnode_device_parse(&client->dev, &props); + if (ret) + goto error; + + ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx500_ctrl_ops, + &props); + if (ret) + goto error; + + imx500->sd.ctrl_handler = ctrl_hdlr; + + /* Setup exposure and frame/line length limits. */ + imx500_set_framing_limits(imx500); + + return 0; + +error: + v4l2_ctrl_handler_free(ctrl_hdlr); + mutex_destroy(&imx500->mutex); + + return ret; +} + +static void imx500_free_controls(struct imx500 *imx500) +{ + v4l2_ctrl_handler_free(imx500->sd.ctrl_handler); + mutex_destroy(&imx500->mutex); +} + +static int imx500_check_hwcfg(struct device *dev) +{ + struct fwnode_handle *endpoint; + struct v4l2_fwnode_endpoint ep_cfg = { .bus_type = + V4L2_MBUS_CSI2_DPHY }; + int ret = -EINVAL; + + endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); + if (!endpoint) { + dev_err(dev, "endpoint node not found\n"); + return -EINVAL; + } + + if (v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep_cfg)) { + dev_err(dev, "could not parse endpoint\n"); + goto error_out; + } + + /* Check the number of MIPI CSI2 data lanes */ + if (ep_cfg.bus.mipi_csi2.num_data_lanes != 2) { + dev_err(dev, "only 2 data lanes are currently supported\n"); + 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; + } + + if (ep_cfg.nr_of_link_frequencies != 1 || + ep_cfg.link_frequencies[0] != IMX500_DEFAULT_LINK_FREQ) { + dev_err(dev, "Link frequency not supported: %lld\n", + ep_cfg.link_frequencies[0]); + goto error_out; + } + + ret = 0; + +error_out: + v4l2_fwnode_endpoint_free(&ep_cfg); + fwnode_handle_put(endpoint); + + return ret; +} + +static int imx500_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct imx500 *imx500; + int ret; + + imx500 = devm_kzalloc(&client->dev, sizeof(*imx500), GFP_KERNEL); + if (!imx500) + return -ENOMEM; + + imx500->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(imx500->regmap)) + return dev_err_probe(dev, PTR_ERR(imx500->regmap), + "failed to initialise CCI\n"); + + v4l2_i2c_subdev_init(&imx500->sd, client, &imx500_subdev_ops); + + /* Check the hardware configuration in device tree */ + if (imx500_check_hwcfg(dev)) + return -EINVAL; + + /* Get system clock (xclk) */ + imx500->xclk = devm_clk_get(dev, NULL); + if (IS_ERR(imx500->xclk)) + return dev_err_probe(dev, PTR_ERR(imx500->xclk), + "failed to get xclk\n"); + + imx500->xclk_freq = clk_get_rate(imx500->xclk); + if (imx500->xclk_freq != IMX500_XCLK_FREQ) { + dev_err(dev, "xclk frequency not supported: %d Hz\n", + imx500->xclk_freq); + return -EINVAL; + } + + ret = imx500_get_regulators(imx500); + if (ret) { + dev_err(dev, "failed to get regulators\n"); + return ret; + } + + imx500->reset_gpio = + devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + + /* + * The sensor must be powered for imx500_identify_module() + * to be able to read the CHIP_ID register + */ + ret = imx500_power_on(dev); + if (ret) + return ret; + + 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); + + ret = imx500_identify_module(imx500); + if (ret) + goto error_power_off; + + /* Initialize default format */ + imx500_set_default_format(imx500); + + /* This needs the pm runtime to be registered. */ + ret = imx500_init_controls(imx500); + if (ret) + goto error_power_off; + + /* Initialize subdev */ + imx500->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; + imx500->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + /* Initialize source pads */ + imx500->pad.flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&imx500->sd.entity, NUM_PADS, + &imx500->pad); + if (ret) { + dev_err(dev, "failed to init entity pads: %d\n", ret); + goto error_handler_free; + } + + ret = v4l2_async_register_subdev_sensor(&imx500->sd); + if (ret < 0) { + dev_err(dev, "failed to register sensor sub-device: %d\n", ret); + goto error_media_entity; + } + + pm_runtime_mark_last_busy(&client->dev); + pm_runtime_put_autosuspend(&client->dev); + + return 0; + +error_media_entity: + media_entity_cleanup(&imx500->sd.entity); + +error_handler_free: + imx500_free_controls(imx500); + +error_power_off: + pm_runtime_disable(&client->dev); + pm_runtime_put_noidle(&client->dev); + imx500_power_off(&client->dev); + + return ret; +} + +static void imx500_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx500 *imx500 = to_imx500(sd); + + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + imx500_free_controls(imx500); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + imx500_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); +} + +static const struct of_device_id imx500_dt_ids[] = { + { .compatible = "sony,imx500" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, imx500_dt_ids); + +static const struct dev_pm_ops imx500_pm_ops = { SET_RUNTIME_PM_OPS( + imx500_power_off, imx500_power_on, NULL) }; + +static struct i2c_driver imx500_i2c_driver = { + .driver = { + .name = "imx500", + .of_match_table = imx500_dt_ids, + .pm = &imx500_pm_ops, + }, + .probe = imx500_probe, + .remove = imx500_remove, +}; + +module_i2c_driver(imx500_i2c_driver); + +MODULE_AUTHOR("Naushir Patuck "); +MODULE_DESCRIPTION("Sony IMX500 sensor driver"); +MODULE_LICENSE("GPL");