From 7973ba63f2eefce88f54968dc1fcda707483b384 Mon Sep 17 00:00:00 2001 From: Zefa Chen Date: Wed, 9 Oct 2019 20:38:05 +0800 Subject: [PATCH] media: i2c: sensor drivers synchronize with kernel 4.4 kernel 4.4 commit ends f3e1785b1893c4a7bdf543ae048b92f9696daf53 Change-Id: I666049f58cb9939e76cbe889d3f85be012ea0fc1 Signed-off-by: Zefa Chen --- drivers/media/i2c/Kconfig | 336 ++- drivers/media/i2c/Makefile | 26 + drivers/media/i2c/ar0230.c | 1688 +++++++++++ drivers/media/i2c/bf3925.c | 1498 ++++++++++ drivers/media/i2c/gc0329.c | 118 +- drivers/media/i2c/gc0403.c | 1289 +++++++++ drivers/media/i2c/gc2035.c | 118 +- drivers/media/i2c/gc2155.c | 199 +- drivers/media/i2c/gc2355.c | 144 +- drivers/media/i2c/gc2385.c | 1254 ++++++++ drivers/media/i2c/gc5025.c | 1922 +++++++++++++ drivers/media/i2c/gc5035.c | 1598 +++++++++++ drivers/media/i2c/gc8034.c | 2165 ++++++++++++++ drivers/media/i2c/imx219.c | 129 +- drivers/media/i2c/imx258.c | 2800 ++++++++++-------- drivers/media/i2c/imx258_eeprom.c | 377 +++ drivers/media/i2c/imx258_eeprom_head.h | 62 + drivers/media/i2c/imx317.c | 1364 +++++++++ drivers/media/i2c/imx323.c | 166 +- drivers/media/i2c/imx327.c | 1386 +++++++++ drivers/media/i2c/jx_h65.c | 1246 ++++++++ drivers/media/i2c/ov2680.c | 2114 +++++++------- drivers/media/i2c/ov2685.c | 451 +-- drivers/media/i2c/ov2718.c | 167 +- drivers/media/i2c/ov2735.c | 201 +- drivers/media/i2c/ov4689.c | 199 +- drivers/media/i2c/ov5648.c | 1506 ++++++++++ drivers/media/i2c/ov5670.c | 3638 ++++++++++-------------- drivers/media/i2c/ov5695.c | 473 +-- drivers/media/i2c/ov7251.c | 2326 +++++++-------- drivers/media/i2c/ov7750.c | 193 +- drivers/media/i2c/ov8858.c | 2999 +++++++++++++++++++ drivers/media/i2c/ov9281.c | 1172 ++++++++ drivers/media/i2c/ov9750.c | 1293 +++++++++ drivers/media/i2c/sc031gs.c | 195 +- drivers/media/i2c/sc132gs.c | 1191 ++++++++ 36 files changed, 31893 insertions(+), 6110 deletions(-) create mode 100644 drivers/media/i2c/ar0230.c create mode 100644 drivers/media/i2c/bf3925.c create mode 100644 drivers/media/i2c/gc0403.c create mode 100644 drivers/media/i2c/gc2385.c create mode 100644 drivers/media/i2c/gc5025.c create mode 100644 drivers/media/i2c/gc5035.c create mode 100644 drivers/media/i2c/gc8034.c create mode 100644 drivers/media/i2c/imx258_eeprom.c create mode 100644 drivers/media/i2c/imx258_eeprom_head.h create mode 100644 drivers/media/i2c/imx317.c create mode 100644 drivers/media/i2c/imx327.c create mode 100644 drivers/media/i2c/jx_h65.c create mode 100644 drivers/media/i2c/ov5648.c create mode 100644 drivers/media/i2c/ov8858.c create mode 100644 drivers/media/i2c/ov9281.c create mode 100644 drivers/media/i2c/ov9750.c create mode 100644 drivers/media/i2c/sc132gs.c diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index e103f9392be6..d6a4898317a9 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -596,6 +596,17 @@ config VIDEO_APTINA_PLL config VIDEO_SMIAPP_PLL tristate +config VIDEO_IMX219 + tristate "Sony IMX219 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + help + This is a Video4Linux2 sensor driver for the Sony + IMX219 camera. + + To compile this driver as a module, choose M here: the + module will be called imx219. + config VIDEO_IMX258 tristate "Sony IMX258 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API @@ -607,6 +618,14 @@ config VIDEO_IMX258 To compile this driver as a module, choose M here: the module will be called imx258. +config VIDEO_IMX258_EEPROM + tristate "Sony imx258 sensor otp from eeprom support" + depends on VIDEO_V4L2 && I2C + depends on VIDEO_IMX258 + select V4L2_FWNODE + help + This driver supports IMX258 OTP load from eeprom. + config VIDEO_IMX274 tristate "Sony IMX274 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API @@ -616,6 +635,39 @@ config VIDEO_IMX274 This is a V4L2 sensor driver for the Sony IMX274 CMOS image sensor. +config VIDEO_IMX317 + tristate "Sony IMX317 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + help + This is a Video4Linux2 sensor driver for the Sony + IMX317 camera. + + To compile this driver as a module, choose M here: the + module will be called imx317. + +config VIDEO_IMX323 + tristate "Sony IMX323 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + help + This is a Video4Linux2 sensor driver for the Sony + IMX323 camera. + + To compile this driver as a module, choose M here: the + module will be called imx323. + +config VIDEO_IMX327 + tristate "Sony IMX327 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + help + This is a Video4Linux2 sensor driver for the Sony + IMX327 camera. + + To compile this driver as a module, choose M here: the + module will be called imx327. + config VIDEO_OV2640 tristate "OmniVision OV2640 sensor support" depends on VIDEO_V4L2 && I2C @@ -663,6 +715,42 @@ config VIDEO_OV2685 To compile this driver as a module, choose M here: the module will be called ov2685. +config VIDEO_OV2718 + tristate "OmniVision OV2718 sensor support" + depends on VIDEO_V4L2 && I2C && MEDIA_CONTROLLER + depends on MEDIA_CAMERA_SUPPORT + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the OmniVision + OV2718 camera. + + To compile this driver as a module, choose M here: the + module will be called ov2718. + +config VIDEO_OV2735 + tristate "OmniVision OV2735 sensor support" + depends on VIDEO_V4L2 && I2C && MEDIA_CONTROLLER + depends on MEDIA_CAMERA_SUPPORT + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the OmniVision + OV2735 camera. + + To compile this driver as a module, choose M here: the + module will be called ov2735. + +config VIDEO_OV4689 + tristate "OmniVision OV4689 sensor support" + depends on VIDEO_V4L2 && I2C && MEDIA_CONTROLLER + depends on MEDIA_CAMERA_SUPPORT + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the OmniVision + OV4689 camera. + + To compile this driver as a module, choose M here: the + module will be called ov4689. + config VIDEO_OV5640 tristate "OmniVision OV5640 sensor support" depends on OF @@ -698,24 +786,24 @@ config VIDEO_OV5647 To compile this driver as a module, choose M here: the module will be called ov5647. -config VIDEO_OV6650 - tristate "OmniVision OV6650 sensor support" - depends on I2C && VIDEO_V4L2 +config VIDEO_OV5648 + tristate "OmniVision OV5648 sensor support" + depends on VIDEO_V4L2 && I2C && MEDIA_CONTROLLER depends on MEDIA_CAMERA_SUPPORT - ---help--- + select V4L2_FWNODE + help This is a Video4Linux2 sensor driver for the OmniVision - OV6650 camera. + OV5648 camera. To compile this driver as a module, choose M here: the - module will be called ov6650. + module will be called ov5648. config VIDEO_OV5670 tristate "OmniVision OV5670 sensor support" - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on VIDEO_V4L2 && I2C && MEDIA_CONTROLLER depends on MEDIA_CAMERA_SUPPORT - depends on MEDIA_CONTROLLER select V4L2_FWNODE - ---help--- + help This is a Video4Linux2 sensor driver for the OmniVision OV5670 camera. @@ -724,15 +812,27 @@ config VIDEO_OV5670 config VIDEO_OV5695 tristate "OmniVision OV5695 sensor support" - depends on I2C && VIDEO_V4L2 + depends on VIDEO_V4L2 && I2C && MEDIA_CONTROLLER depends on MEDIA_CAMERA_SUPPORT - ---help--- + select V4L2_FWNODE + help This is a Video4Linux2 sensor driver for the OmniVision OV5695 camera. To compile this driver as a module, choose M here: the module will be called ov5695. +config VIDEO_OV6650 + tristate "OmniVision OV6650 sensor support" + depends on I2C && VIDEO_V4L2 + depends on MEDIA_CAMERA_SUPPORT + help + This is a Video4Linux2 sensor driver for the OmniVision + OV6650 camera. + + To compile this driver as a module, choose M here: the + module will be called ov6650. + config VIDEO_OV7251 tristate "OmniVision OV7251 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API @@ -785,12 +885,62 @@ config VIDEO_OV7740 This is a Video4Linux2 sensor driver for the OmniVision OV7740 VGA camera sensor. -config VIDEO_OV9650 - tristate "OmniVision OV9650/OV9652 sensor support" +config VIDEO_OV7750 + tristate "OmniVision OV7750 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - ---help--- - This is a V4L2 sensor driver for the Omnivision - OV9650 and OV9652 camera sensors. + depends on MEDIA_CAMERA_SUPPORT + depends on MEDIA_CONTROLLER + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the OmniVision + OV7750 camera. + + To compile this driver as a module, choose M here: the + module will be called ov7750. + +config VIDEO_OV8858 + tristate "OmniVision OV8858 sensor support" + depends on I2C && VIDEO_V4L2 + depends on MEDIA_CAMERA_SUPPORT + help + This is a Video4Linux2 sensor driver for the OmniVision + OV8858 camera. + + To compile this driver as a module, choose M here: the + module will be called ov8858. + +config VIDEO_OV9281 + tristate "OmniVision OV9281 sensor support" + depends on I2C && VIDEO_V4L2 + depends on MEDIA_CAMERA_SUPPORT + help + This is a Video4Linux2 sensor driver for the OmniVision + OV9281 camera. + + To compile this driver as a module, choose M here: the + module will be called ov9281. + +config VIDEO_OV9650 + tristate "OmniVision OV9650 sensor support" + depends on I2C && VIDEO_V4L2 + depends on MEDIA_CAMERA_SUPPORT + help + This is a Video4Linux2 sensor driver for the OmniVision + OV9650 camera. + + To compile this driver as a module, choose M here: the + module will be called ov9650. + +config VIDEO_OV9750 + tristate "OmniVision OV9750 sensor support" + depends on I2C && VIDEO_V4L2 + depends on MEDIA_CAMERA_SUPPORT + help + This is a Video4Linux2 sensor driver for the OmniVision + OV9750 camera. + + To compile this driver as a module, choose M here: the + module will be called ov9750. config VIDEO_OV13850 tristate "OmniVision OV13850 sensor support" @@ -895,6 +1045,17 @@ config VIDEO_MT9V111 To compile this driver as a module, choose M here: the module will be called mt9v111. +config VIDEO_AR0230 + tristate "Aptina AR0230 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + select V4L2_FWNODE + help + Support for the Aptina AR0230 sensor. + + To compile this driver as a module, choose M here: the + module will be called ar0230. + config VIDEO_SR030PC30 tristate "Siliconfile SR030PC30 sensor support" depends on I2C && VIDEO_V4L2 @@ -976,6 +1137,39 @@ config VIDEO_GC0312 To compile this driver as a module, choose M here: the module will be called gc0312. +config VIDEO_GC0329 + tristate "GalaxyCore GC0329 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + select V4L2_FWNODE + help + Support for the GalaxyCore GC0329 sensor. + + To compile this driver as a module, choose M here: the + module will be called gc0329. + +config VIDEO_GC0403 + tristate "GalaxyCore GC0403 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + select V4L2_FWNODE + help + Support for the GalaxyCore GC0403 sensor. + + To compile this driver as a module, choose M here: the + module will be called gc0403. + +config VIDEO_GC2035 + tristate "GalaxyCore GC2035 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + select V4L2_FWNODE + help + Support for the GalaxyCore GC2035 sensor. + + To compile this driver as a module, choose M here: the + module will be called gc2035. + config VIDEO_GC2145 tristate "GalaxyCore GC2145 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API @@ -987,6 +1181,116 @@ config VIDEO_GC2145 To compile this driver as a module, choose M here: the module will be called gc2145. +config VIDEO_GC2155 + tristate "GalaxyCore GC2155 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + select V4L2_FWNODE + help + Support for the GalaxyCore GC2155 sensor. + + To compile this driver as a module, choose M here: the + module will be called gc2155. + +config VIDEO_GC2355 + tristate "GalaxyCore GC2355 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + select V4L2_FWNODE + help + Support for the GalaxyCore GC2355 sensor. + + To compile this driver as a module, choose M here: the + module will be called gc2355. + +config VIDEO_GC2385 + tristate "GalaxyCore GC2385 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + select V4L2_FWNODE + help + Support for the GalaxyCore GC2385 sensor. + + To compile this driver as a module, choose M here: the + module will be called gc2385. + +config VIDEO_GC5025 + tristate "GalaxyCore GC5025 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + select V4L2_FWNODE + help + Support for the GalaxyCore GC5025 sensor. + + To compile this driver as a module, choose M here: the + module will be called gc5025. + +config VIDEO_GC5035 + tristate "GalaxyCore GC5035 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + select V4L2_FWNODE + help + Support for the GalaxyCore GC5035 sensor. + + To compile this driver as a module, choose M here: the + module will be called gc5035. + +config VIDEO_GC8034 + tristate "GalaxyCore GC8034 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + select V4L2_FWNODE + help + Support for the GalaxyCore GC8034 sensor. + + To compile this driver as a module, choose M here: the + module will be called gc8034. + +config VIDEO_BF3925 + tristate "BYD BF3925 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + select V4L2_FWNODE + help + Support for the BYD BF3925 sensor. + + To compile this driver as a module, choose M here: the + module will be called bf3925. + +config VIDEO_JX_H65 + tristate "SOI JX_H65 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + select V4L2_FWNODE + help + Support for the SOI JX_H65 sensor. + + To compile this driver as a module, choose M here: the + module will be called jx_h65. + +config VIDEO_SC031GS + tristate "SmartSens SC031GS sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + select V4L2_FWNODE + help + Support for the SmartSens SC031GS sensor. + + To compile this driver as a module, choose M here: the + module will be called sc031gs. + +config VIDEO_SC132GS + tristate "SmartSens SC132GS sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + select V4L2_FWNODE + help + Support for the SmartSens SC132GS sensor. + + To compile this driver as a module, choose M here: the + module will be called sc132gs. + comment "Flash devices" config VIDEO_ADP1653 diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 8d1adc0919ff..d684e4b2b318 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -67,9 +67,13 @@ obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o obj-$(CONFIG_VIDEO_OV2640) += ov2640.o obj-$(CONFIG_VIDEO_OV2680) += ov2680.o obj-$(CONFIG_VIDEO_OV2685) += ov2685.o +obj-$(CONFIG_VIDEO_OV2718) += ov2718.o +obj-$(CONFIG_VIDEO_OV2735) += ov2735.o +obj-$(CONFIG_VIDEO_OV4689) += ov4689.o obj-$(CONFIG_VIDEO_OV5640) += ov5640.o obj-$(CONFIG_VIDEO_OV5645) += ov5645.o obj-$(CONFIG_VIDEO_OV5647) += ov5647.o +obj-$(CONFIG_VIDEO_OV5648) += ov5648.o obj-$(CONFIG_VIDEO_OV5670) += ov5670.o obj-$(CONFIG_VIDEO_OV5695) += ov5695.o obj-$(CONFIG_VIDEO_OV6650) += ov6650.o @@ -78,7 +82,10 @@ obj-$(CONFIG_VIDEO_OV7640) += ov7640.o obj-$(CONFIG_VIDEO_OV7670) += ov7670.o obj-$(CONFIG_VIDEO_OV772X) += ov772x.o obj-$(CONFIG_VIDEO_OV7740) += ov7740.o +obj-$(CONFIG_VIDEO_OV8858) += ov8858.o +obj-$(CONFIG_VIDEO_OV9281) += ov9281.o obj-$(CONFIG_VIDEO_OV9650) += ov9650.o +obj-$(CONFIG_VIDEO_OV9750) += ov9750.o obj-$(CONFIG_VIDEO_OV13850) += ov13850.o obj-$(CONFIG_VIDEO_OV13858) += ov13858.o obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o @@ -89,6 +96,7 @@ obj-$(CONFIG_VIDEO_MT9T112) += mt9t112.o obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o obj-$(CONFIG_VIDEO_MT9V111) += mt9v111.o +obj-$(CONFIG_VIDEO_AR0230) += ar0230.o obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o obj-$(CONFIG_VIDEO_RJ54N1) += rj54n1cb0c.o @@ -107,9 +115,27 @@ obj-$(CONFIG_VIDEO_I2C) += video-i2c.o obj-$(CONFIG_VIDEO_ML86V7667) += ml86v7667.o obj-$(CONFIG_VIDEO_OV2659) += ov2659.o obj-$(CONFIG_VIDEO_TC358743) += tc358743.o +obj-$(CONFIG_VIDEO_IMX219) += imx219.o obj-$(CONFIG_VIDEO_IMX258) += imx258.o +obj-$(CONFIG_VIDEO_IMX258_EEPROM) += imx258_eeprom.o obj-$(CONFIG_VIDEO_IMX274) += imx274.o +obj-$(CONFIG_VIDEO_IMX317) += imx317.o +obj-$(CONFIG_VIDEO_IMX323) += imx323.o +obj-$(CONFIG_VIDEO_IMX327) += imx327.o obj-$(CONFIG_VIDEO_GC0312) += gc0312.o +obj-$(CONFIG_VIDEO_GC0329) += gc0329.o +obj-$(CONFIG_VIDEO_GC0403) += gc0403.o +obj-$(CONFIG_VIDEO_GC2035) += gc2035.o obj-$(CONFIG_VIDEO_GC2145) += gc2145.o +obj-$(CONFIG_VIDEO_GC2155) += gc2155.o +obj-$(CONFIG_VIDEO_GC2355) += gc2355.o +obj-$(CONFIG_VIDEO_GC2385) += gc2385.o +obj-$(CONFIG_VIDEO_GC5025) += gc5025.o +obj-$(CONFIG_VIDEO_GC5035) += gc5035.o +obj-$(CONFIG_VIDEO_GC8034) += gc8034.o +obj-$(CONFIG_VIDEO_BF3925) += bf3925.o +obj-$(CONFIG_VIDEO_JX_H65) += jx_h65.o +obj-$(CONFIG_VIDEO_SC031GS) += sc031gs.o +obj-$(CONFIG_VIDEO_SC132GS) += sc132gs.o obj-$(CONFIG_SDR_MAX2175) += max2175.o diff --git a/drivers/media/i2c/ar0230.c b/drivers/media/i2c/ar0230.c new file mode 100644 index 000000000000..318da90ca478 --- /dev/null +++ b/drivers/media/i2c/ar0230.c @@ -0,0 +1,1688 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ar0230 driver + * + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x00) + +#ifndef V4L2_CID_DIGITAL_GAIN +#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN +#endif + +/* 74.25Mhz */ +#define AR0230_PIXEL_RATE (74250000) +#define AR0230_XVCLK_FREQ 24000000 + +#define CHIP_ID 0x3020 +#define AR0230_REG_CHIP_ID 0x31fc + +#define AR0230_REG_CTRL_MODE 0x301A +#define AR0230_MODE_SW_STANDBY 0x10D8 +#define AR0230_MODE_STREAMING 0x10DC + +#define AR0230_REG_EXPOSURE 0x3012 +#define AR0230_EXPOSURE_MIN 0 +#define AR0230_EXPOSURE_STEP 1 +#define AR0230_VTS_MAX 0x044A + +#define AR0230_REG_ANALOG_GAIN 0x3060 +#define ANALOG_GAIN_MIN 0x0 +#define ANALOG_GAIN_MAX 0xFB7 +#define ANALOG_GAIN_STEP 1 +#define ANALOG_GAIN_DEFAULT 0xC0 + +#define AR0230_REG_VTS 0x300a + +#define AR0230_REG_ORIENTATION 0x3040 +#define AR0230_ORIENTATION_H bit(14) +#define AR0230_ORIENTATION_V bit(15) + +#define REG_NULL 0xFFFF +#define REG_DELAY 0xFFFE + +#define AR0230_REG_VALUE_08BIT 1 +#define AR0230_REG_VALUE_16BIT 2 +#define AR0230_REG_VALUE_24BIT 3 + +#define USE_HDR_MODE + +/* h_offs 35 v_offs 14 */ +#define PIX_FORMAT MEDIA_BUS_FMT_SGRBG12_1X12 + +#define AR0230_NAME "ar0230" + +struct cam_regulator { + char name[32]; + int val; +}; + +static const struct cam_regulator ar0230_regulator[] = { + {"avdd", 2800000}, /* Analog power */ + {"dovdd", 1800000}, /* Digital I/O power */ + {"dvdd", 1800000}, /* Digital core power */ +}; + +#define AR0230_NUM_SUPPLIES ARRAY_SIZE(ar0230_regulator) + +struct regval { + u16 addr; + u16 val; +}; + +struct ar0230_mode { + u32 width; + u32 height; + u32 max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + const struct regval *reg_list; +}; + +struct ar0230 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; + struct regulator_bulk_data supplies[AR0230_NUM_SUPPLIES]; + + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *anal_gain; + struct v4l2_ctrl *digi_gain; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *test_pattern; + struct mutex mutex; + bool streaming; + bool power_on; + const struct ar0230_mode *cur_mode; + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; +}; + +#define to_ar0230(sd) container_of(sd, struct ar0230, subdev) + +/* + * Xclk 24Mhz + * Pclk 74.25Mhz + * linelength 0x469 + * framelength 0x44a + * grabwindow_width 1920 + * grabwindow_height 1080 + * max_framerate 30fps + * dvp bt601 12bit + */ +static const struct regval ar0230_regs[] = { +#ifdef USE_HDR_MODE + {0x301A, 0x0001}, + {REG_DELAY, 2000}, + {0x301A, 0x10D8}, + {REG_DELAY, 2000}, + {0x3088, 0x8000}, + {0x3086, 0x4558}, + {0x3086, 0x729B}, + {0x3086, 0x4A31}, + {0x3086, 0x4342}, + {0x3086, 0x8E03}, + {0x3086, 0x2A14}, + {0x3086, 0x4578}, + {0x3086, 0x7B3D}, + {0x3086, 0xFF3D}, + {0x3086, 0xFF3D}, + {0x3086, 0xEA2A}, + {0x3086, 0x043D}, + {0x3086, 0x102A}, + {0x3086, 0x052A}, + {0x3086, 0x1535}, + {0x3086, 0x2A05}, + {0x3086, 0x3D10}, + {0x3086, 0x4558}, + {0x3086, 0x2A04}, + {0x3086, 0x2A14}, + {0x3086, 0x3DFF}, + {0x3086, 0x3DFF}, + {0x3086, 0x3DEA}, + {0x3086, 0x2A04}, + {0x3086, 0x622A}, + {0x3086, 0x288E}, + {0x3086, 0x0036}, + {0x3086, 0x2A08}, + {0x3086, 0x3D64}, + {0x3086, 0x7A3D}, + {0x3086, 0x0444}, + {0x3086, 0x2C4B}, + {0x3086, 0x8F00}, + {0x3086, 0x430C}, + {0x3086, 0x2D63}, + {0x3086, 0x4316}, + {0x3086, 0x8E03}, + {0x3086, 0x2AFC}, + {0x3086, 0x5C1D}, + {0x3086, 0x5754}, + {0x3086, 0x495F}, + {0x3086, 0x5305}, + {0x3086, 0x5307}, + {0x3086, 0x4D2B}, + {0x3086, 0xF810}, + {0x3086, 0x164C}, + {0x3086, 0x0855}, + {0x3086, 0x562B}, + {0x3086, 0xB82B}, + {0x3086, 0x984E}, + {0x3086, 0x1129}, + {0x3086, 0x0429}, + {0x3086, 0x8429}, + {0x3086, 0x9460}, + {0x3086, 0x5C19}, + {0x3086, 0x5C1B}, + {0x3086, 0x4548}, + {0x3086, 0x4508}, + {0x3086, 0x4588}, + {0x3086, 0x29B6}, + {0x3086, 0x8E01}, + {0x3086, 0x2AF8}, + {0x3086, 0x3E02}, + {0x3086, 0x2AFA}, + {0x3086, 0x3F09}, + {0x3086, 0x5C1B}, + {0x3086, 0x29B2}, + {0x3086, 0x3F0C}, + {0x3086, 0x3E02}, + {0x3086, 0x3E13}, + {0x3086, 0x5C13}, + {0x3086, 0x3F11}, + {0x3086, 0x3E0B}, + {0x3086, 0x5F2B}, + {0x3086, 0x902A}, + {0x3086, 0xF22B}, + {0x3086, 0x803E}, + {0x3086, 0x043F}, + {0x3086, 0x0660}, + {0x3086, 0x29A2}, + {0x3086, 0x29A3}, + {0x3086, 0x5F4D}, + {0x3086, 0x192A}, + {0x3086, 0xFA29}, + {0x3086, 0x8345}, + {0x3086, 0xA83E}, + {0x3086, 0x072A}, + {0x3086, 0xFB3E}, + {0x3086, 0x2945}, + {0x3086, 0x8821}, + {0x3086, 0x3E08}, + {0x3086, 0x2AFA}, + {0x3086, 0x5D29}, + {0x3086, 0x9288}, + {0x3086, 0x102B}, + {0x3086, 0x048B}, + {0x3086, 0x1685}, + {0x3086, 0x8D48}, + {0x3086, 0x4D4E}, + {0x3086, 0x2B80}, + {0x3086, 0x4C0B}, + {0x3086, 0x603F}, + {0x3086, 0x282A}, + {0x3086, 0xF23F}, + {0x3086, 0x0F29}, + {0x3086, 0x8229}, + {0x3086, 0x8329}, + {0x3086, 0x435C}, + {0x3086, 0x155F}, + {0x3086, 0x4D19}, + {0x3086, 0x2AFA}, + {0x3086, 0x4558}, + {0x3086, 0x8E00}, + {0x3086, 0x2A98}, + {0x3086, 0x3F06}, + {0x3086, 0x1244}, + {0x3086, 0x4A04}, + {0x3086, 0x4316}, + {0x3086, 0x0543}, + {0x3086, 0x1658}, + {0x3086, 0x4316}, + {0x3086, 0x5A43}, + {0x3086, 0x1606}, + {0x3086, 0x4316}, + {0x3086, 0x0743}, + {0x3086, 0x168E}, + {0x3086, 0x032A}, + {0x3086, 0x9C45}, + {0x3086, 0x787B}, + {0x3086, 0x3F07}, + {0x3086, 0x2A9D}, + {0x3086, 0x3E2E}, + {0x3086, 0x4558}, + {0x3086, 0x253E}, + {0x3086, 0x068E}, + {0x3086, 0x012A}, + {0x3086, 0x988E}, + {0x3086, 0x0012}, + {0x3086, 0x444B}, + {0x3086, 0x0343}, + {0x3086, 0x2D46}, + {0x3086, 0x4316}, + {0x3086, 0xA343}, + {0x3086, 0x165D}, + {0x3086, 0x0D29}, + {0x3086, 0x4488}, + {0x3086, 0x102B}, + {0x3086, 0x0453}, + {0x3086, 0x0D8B}, + {0x3086, 0x1685}, + {0x3086, 0x448E}, + {0x3086, 0x032A}, + {0x3086, 0xFC5C}, + {0x3086, 0x1D8D}, + {0x3086, 0x6057}, + {0x3086, 0x5449}, + {0x3086, 0x5F53}, + {0x3086, 0x0553}, + {0x3086, 0x074D}, + {0x3086, 0x2BF8}, + {0x3086, 0x1016}, + {0x3086, 0x4C08}, + {0x3086, 0x5556}, + {0x3086, 0x2BB8}, + {0x3086, 0x2B98}, + {0x3086, 0x4E11}, + {0x3086, 0x2904}, + {0x3086, 0x2984}, + {0x3086, 0x2994}, + {0x3086, 0x605C}, + {0x3086, 0x195C}, + {0x3086, 0x1B45}, + {0x3086, 0x4845}, + {0x3086, 0x0845}, + {0x3086, 0x8829}, + {0x3086, 0xB68E}, + {0x3086, 0x012A}, + {0x3086, 0xF83E}, + {0x3086, 0x022A}, + {0x3086, 0xFA3F}, + {0x3086, 0x095C}, + {0x3086, 0x1B29}, + {0x3086, 0xB23F}, + {0x3086, 0x0C3E}, + {0x3086, 0x023E}, + {0x3086, 0x135C}, + {0x3086, 0x133F}, + {0x3086, 0x113E}, + {0x3086, 0x0B5F}, + {0x3086, 0x2B90}, + {0x3086, 0x2AF2}, + {0x3086, 0x2B80}, + {0x3086, 0x3E04}, + {0x3086, 0x3F06}, + {0x3086, 0x6029}, + {0x3086, 0xA229}, + {0x3086, 0xA35F}, + {0x3086, 0x4D1C}, + {0x3086, 0x2AFA}, + {0x3086, 0x2983}, + {0x3086, 0x45A8}, + {0x3086, 0x3E07}, + {0x3086, 0x2AFB}, + {0x3086, 0x3E29}, + {0x3086, 0x4588}, + {0x3086, 0x243E}, + {0x3086, 0x082A}, + {0x3086, 0xFA5D}, + {0x3086, 0x2992}, + {0x3086, 0x8810}, + {0x3086, 0x2B04}, + {0x3086, 0x8B16}, + {0x3086, 0x868D}, + {0x3086, 0x484D}, + {0x3086, 0x4E2B}, + {0x3086, 0x804C}, + {0x3086, 0x0B60}, + {0x3086, 0x3F28}, + {0x3086, 0x2AF2}, + {0x3086, 0x3F0F}, + {0x3086, 0x2982}, + {0x3086, 0x2983}, + {0x3086, 0x2943}, + {0x3086, 0x5C15}, + {0x3086, 0x5F4D}, + {0x3086, 0x1C2A}, + {0x3086, 0xFA45}, + {0x3086, 0x588E}, + {0x3086, 0x002A}, + {0x3086, 0x983F}, + {0x3086, 0x064A}, + {0x3086, 0x739D}, + {0x3086, 0x0A43}, + {0x3086, 0x160B}, + {0x3086, 0x4316}, + {0x3086, 0x8E03}, + {0x3086, 0x2A9C}, + {0x3086, 0x4578}, + {0x3086, 0x3F07}, + {0x3086, 0x2A9D}, + {0x3086, 0x3E12}, + {0x3086, 0x4558}, + {0x3086, 0x3F04}, + {0x3086, 0x8E01}, + {0x3086, 0x2A98}, + {0x3086, 0x8E00}, + {0x3086, 0x9176}, + {0x3086, 0x9C77}, + {0x3086, 0x9C46}, + {0x3086, 0x4416}, + {0x3086, 0x1690}, + {0x3086, 0x7A12}, + {0x3086, 0x444B}, + {0x3086, 0x4A00}, + {0x3086, 0x4316}, + {0x3086, 0x6343}, + {0x3086, 0x1608}, + {0x3086, 0x4316}, + {0x3086, 0x5043}, + {0x3086, 0x1665}, + {0x3086, 0x4316}, + {0x3086, 0x6643}, + {0x3086, 0x168E}, + {0x3086, 0x032A}, + {0x3086, 0x9C45}, + {0x3086, 0x783F}, + {0x3086, 0x072A}, + {0x3086, 0x9D5D}, + {0x3086, 0x0C29}, + {0x3086, 0x4488}, + {0x3086, 0x102B}, + {0x3086, 0x0453}, + {0x3086, 0x0D8B}, + {0x3086, 0x1686}, + {0x3086, 0x3E1F}, + {0x3086, 0x4558}, + {0x3086, 0x283E}, + {0x3086, 0x068E}, + {0x3086, 0x012A}, + {0x3086, 0x988E}, + {0x3086, 0x008D}, + {0x3086, 0x6012}, + {0x3086, 0x444B}, + {0x3086, 0x2C2C}, + {0x3086, 0x2C2C}, + {0x2436, 0x000E}, + {0x320C, 0x0180}, + {0x320E, 0x0300}, + {0x3210, 0x0500}, + {0x3204, 0x0B6D}, + {0x30FE, 0x0080}, + {0x3ED8, 0x7B99}, + {0x3EDC, 0x9BA8}, + {0x3EDA, 0x9B9B}, + {0x3092, 0x006F}, + {0x3EEC, 0x1C04}, + {0x30BA, 0x779C}, + {0x3EF6, 0xA70F}, + {0x3044, 0x0410}, + {0x3ED0, 0xFF44}, + {0x3ED4, 0x031F}, + {0x30FE, 0x0080}, + {0x3EE2, 0x8866}, + {0x3EE4, 0x6623}, + {0x3EE6, 0x2263}, + {0x30E0, 0x4283}, + {0x30F0, 0x1283}, + {0x30B0, 0x0118}, + {0x31AC, 0x100C}, + {0x3040, 0x0000}, + {0x31AE, 0x0301}, + {0x3082, 0x0008}, + {0x31E0, 0x0200}, + {0x2420, 0x0000}, + {0x2440, 0x0004}, + {0x2442, 0x0080}, + {0x301E, 0x0000}, + {0x2450, 0x0000}, + {0x320A, 0x0080}, + {0x31D0, 0x0000}, + {0x2400, 0x0002}, + {0x2410, 0x0005}, + {0x2412, 0x002D}, + {0x2444, 0xF400}, + {0x2446, 0x0001}, + {0x2438, 0x0010}, + {0x243A, 0x0012}, + {0x243C, 0xFFFF}, + {0x243E, 0x0100}, + {0x3206, 0x0B08}, + {0x3208, 0x1E13}, + {0x3202, 0x0080}, + {0x3200, 0x0002}, + {0x3190, 0x0000}, + {0x318A, 0x0E74}, + {0x318C, 0xC000}, + {0x3192, 0x0400}, + {0x3198, 0x183C}, + {0x3060, 0x000B}, + {0x3096, 0x0480}, + {0x3098, 0x0480}, + {0x3206, 0x0B08}, + {0x3208, 0x1E13}, + {0x3202, 0x0080}, + {0x3200, 0x0002}, + {0x3100, 0x0000}, + {0x30BA, 0x779C}, + {0x318E, 0x0200}, + {0x3064, 0x1982}, + {0x3064, 0x1802}, + {0x302A, 0x0008}, + {0x302C, 0x0001}, + {0x302E, 0x0008}, + {0x3030, 0x00C6}, + {0x3036, 0x0006}, + {0x3038, 0x0001}, + {0x31AE, 0x0301}, + {0x30BA, 0x769C}, + {0x3002, 0x0004}, + {0x3004, 0x000c}, + {0x3006, 0x043b}, + {0x3008, 0x078b}, + {0x300A, 0x044A}, + {0x300C, 0x0469}, + {0x3012, 0x0148}, + {0x3180, 0x0008}, + {0x3062, 0x2333}, + {0x30B0, 0x0118}, + {0x30A2, 0x0001}, + {0x30A6, 0x0001}, + {0x3082, 0x0008}, + {0x3040, 0x0000}, + {0x318E, 0x0000}, +#else + {0x301A, 0x0001}, + {REG_DELAY, 20000}, + {0x301A, 0x10D8}, + {REG_DELAY, 20000}, + {0x3088, 0x8242}, + {0x3086, 0x4558}, + {0x3086, 0x729B}, + {0x3086, 0x4A31}, + {0x3086, 0x4342}, + {0x3086, 0x8E03}, + {0x3086, 0x2A14}, + {0x3086, 0x4578}, + {0x3086, 0x7B3D}, + {0x3086, 0xFF3D}, + {0x3086, 0xFF3D}, + {0x3086, 0xEA2A}, + {0x3086, 0x043D}, + {0x3086, 0x102A}, + {0x3086, 0x052A}, + {0x3086, 0x1535}, + {0x3086, 0x2A05}, + {0x3086, 0x3D10}, + {0x3086, 0x4558}, + {0x3086, 0x2A04}, + {0x3086, 0x2A14}, + {0x3086, 0x3DFF}, + {0x3086, 0x3DFF}, + {0x3086, 0x3DEA}, + {0x3086, 0x2A04}, + {0x3086, 0x622A}, + {0x3086, 0x288E}, + {0x3086, 0x0036}, + {0x3086, 0x2A08}, + {0x3086, 0x3D64}, + {0x3086, 0x7A3D}, + {0x3086, 0x0444}, + {0x3086, 0x2C4B}, + {0x3086, 0x8F03}, + {0x3086, 0x430D}, + {0x3086, 0x2D46}, + {0x3086, 0x4316}, + {0x3086, 0x5F16}, + {0x3086, 0x530D}, + {0x3086, 0x1660}, + {0x3086, 0x3E4C}, + {0x3086, 0x2904}, + {0x3086, 0x2984}, + {0x3086, 0x8E03}, + {0x3086, 0x2AFC}, + {0x3086, 0x5C1D}, + {0x3086, 0x5754}, + {0x3086, 0x495F}, + {0x3086, 0x5305}, + {0x3086, 0x5307}, + {0x3086, 0x4D2B}, + {0x3086, 0xF810}, + {0x3086, 0x164C}, + {0x3086, 0x0955}, + {0x3086, 0x562B}, + {0x3086, 0xB82B}, + {0x3086, 0x984E}, + {0x3086, 0x1129}, + {0x3086, 0x9460}, + {0x3086, 0x5C19}, + {0x3086, 0x5C1B}, + {0x3086, 0x4548}, + {0x3086, 0x4508}, + {0x3086, 0x4588}, + {0x3086, 0x29B6}, + {0x3086, 0x8E01}, + {0x3086, 0x2AF8}, + {0x3086, 0x1702}, + {0x3086, 0x2AFA}, + {0x3086, 0x1709}, + {0x3086, 0x5C1B}, + {0x3086, 0x29B2}, + {0x3086, 0x170C}, + {0x3086, 0x1703}, + {0x3086, 0x1715}, + {0x3086, 0x5C13}, + {0x3086, 0x1711}, + {0x3086, 0x170F}, + {0x3086, 0x5F2B}, + {0x3086, 0x902A}, + {0x3086, 0xF22B}, + {0x3086, 0x8017}, + {0x3086, 0x0617}, + {0x3086, 0x0660}, + {0x3086, 0x29A2}, + {0x3086, 0x29A3}, + {0x3086, 0x5F4D}, + {0x3086, 0x1C2A}, + {0x3086, 0xFA29}, + {0x3086, 0x8345}, + {0x3086, 0xA817}, + {0x3086, 0x072A}, + {0x3086, 0xFB17}, + {0x3086, 0x2945}, + {0x3086, 0x8824}, + {0x3086, 0x1708}, + {0x3086, 0x2AFA}, + {0x3086, 0x5D29}, + {0x3086, 0x9288}, + {0x3086, 0x102B}, + {0x3086, 0x048B}, + {0x3086, 0x1686}, + {0x3086, 0x8D48}, + {0x3086, 0x4D4E}, + {0x3086, 0x2B80}, + {0x3086, 0x4C0B}, + {0x3086, 0x6017}, + {0x3086, 0x302A}, + {0x3086, 0xF217}, + {0x3086, 0x1017}, + {0x3086, 0x8F29}, + {0x3086, 0x8229}, + {0x3086, 0x8329}, + {0x3086, 0x435C}, + {0x3086, 0x155F}, + {0x3086, 0x4D1C}, + {0x3086, 0x2AFA}, + {0x3086, 0x4558}, + {0x3086, 0x8E00}, + {0x3086, 0x2A98}, + {0x3086, 0x170A}, + {0x3086, 0x4A0A}, + {0x3086, 0x4316}, + {0x3086, 0x0B43}, + {0x3086, 0x168E}, + {0x3086, 0x032A}, + {0x3086, 0x9C45}, + {0x3086, 0x7817}, + {0x3086, 0x072A}, + {0x3086, 0x9D17}, + {0x3086, 0x305D}, + {0x3086, 0x2944}, + {0x3086, 0x8810}, + {0x3086, 0x2B04}, + {0x3086, 0x530D}, + {0x3086, 0x4558}, + {0x3086, 0x1708}, + {0x3086, 0x8E01}, + {0x3086, 0x2A98}, + {0x3086, 0x8E00}, + {0x3086, 0x769C}, + {0x3086, 0x779C}, + {0x3086, 0x4644}, + {0x3086, 0x1616}, + {0x3086, 0x907A}, + {0x3086, 0x1244}, + {0x3086, 0x4B18}, + {0x3086, 0x4A04}, + {0x3086, 0x4316}, + {0x3086, 0x0643}, + {0x3086, 0x1605}, + {0x3086, 0x4316}, + {0x3086, 0x0743}, + {0x3086, 0x1658}, + {0x3086, 0x4316}, + {0x3086, 0x5A43}, + {0x3086, 0x1645}, + {0x3086, 0x588E}, + {0x3086, 0x032A}, + {0x3086, 0x9C45}, + {0x3086, 0x787B}, + {0x3086, 0x1707}, + {0x3086, 0x2A9D}, + {0x3086, 0x530D}, + {0x3086, 0x8B16}, + {0x3086, 0x8617}, + {0x3086, 0x2345}, + {0x3086, 0x5825}, + {0x3086, 0x1710}, + {0x3086, 0x8E01}, + {0x3086, 0x2A98}, + {0x3086, 0x8E00}, + {0x3086, 0x1710}, + {0x3086, 0x8D60}, + {0x3086, 0x1244}, + {0x3086, 0x4B2C}, + {0x3086, 0x2C2C}, + {0x2436, 0x000E}, + {0x320C, 0x0180}, + {0x320E, 0x0300}, + {0x3210, 0x0500}, + {0x3204, 0x0B6D}, + {0x30FE, 0x0080}, + {0x3ED8, 0x7B99}, + {0x3EDC, 0x9BA8}, + {0x3EDA, 0x9B9B}, + {0x3092, 0x006F}, + {0x3EEC, 0x1C04}, + {0x30BA, 0x779C}, + {0x3EF6, 0xA70F}, + {0x3044, 0x0410}, + {0x3ED0, 0xFF44}, + {0x3ED4, 0x031F}, + {0x30FE, 0x0080}, + {0x3EE2, 0x8866}, + {0x3EE4, 0x6623}, + {0x3EE6, 0x2263}, + {0x30E0, 0x4283}, + {0x30F0, 0x1283}, + {0x30B0, 0x0118}, + {0x31AC, 0x0C0C}, + {0x3040, 0x0000}, + {0x31AE, 0x0301}, + {0x3082, 0x0009}, + {0x30BA, 0x769C}, + {0x31E0, 0x0200}, + {0x318C, 0x0000}, + {0x3060, 0x000B}, + {0x3096, 0x0080}, + {0x3098, 0x0080}, + {0x3206, 0x0B08}, + {0x3208, 0x1E13}, + {0x3202, 0x0080}, + {0x3200, 0x0002}, + {0x3100, 0x0000}, + {0x3200, 0x0000}, + {0x31D0, 0x0000}, + {0x2400, 0x0003}, + {0x301E, 0x00A8}, + {0x2450, 0x0000}, + {0x320A, 0x0080}, + {0x3064, 0x1982}, + {0x3064, 0x1802}, + {0x302A, 0x0008}, + {0x302C, 0x0001}, + {0x302E, 0x0008}, + {0x3030, 0x00C6}, + {0x3036, 0x0006}, + {0x3038, 0x0001}, + {0x31AE, 0x0301}, + {0x30BA, 0x769C}, + {0x3002, 0x0004}, + {0x3004, 0x000C}, + {0x3006, 0x043B}, + {0x3008, 0x078B}, + {0x300A, 0x0448}, + {0x300C, 0x0469}, + {0x3012, 0x03DA}, + {0x3180, 0x0008}, + {0x3062, 0x2333}, + {0x30B0, 0x0118}, + {0x30A2, 0x0001}, + {0x30A6, 0x0001}, + {0x3082, 0x0009}, + {0x3040, 0x0000}, + {0x318E, 0x0000}, + {0x301A, 0x10D8}, +#endif + {REG_NULL, 0x00}, +}; + +static const struct ar0230_mode supported_modes[] = { + { + .width = 1920, + .height = 1080, + .max_fps = 30, + .exp_def = 0x0100, + .hts_def = 0x0469 * 2, + .vts_def = 0x044a, + .reg_list = ar0230_regs, + } +}; + +static const char * const ar0230_test_pattern_menu[] = { + "Disabled", + "Vertical Color Bar Type 1", + "Vertical Color Bar Type 2", + "Vertical Color Bar Type 3", + "Vertical Color Bar Type 4" +}; + +/* Write registers up to 4 at a time */ +static int ar0230_write_reg(struct i2c_client *client, u16 reg, + int len, u32 val) +{ + u32 buf_i, val_i; + u8 buf[6]; + u8 *val_p; + __be32 val_be; + + if (len > 4) + return -EINVAL; + + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + + val_be = cpu_to_be32(val); + val_p = (u8 *)&val_be; + buf_i = 2; + val_i = 4 - len; + + while (val_i < 4) + buf[buf_i++] = val_p[val_i++]; + //printk("czf reg = 0x%04x, value = 0x%04x\n", reg, val); + if (i2c_master_send(client, buf, len + 2) != len + 2) + return -EIO; + usleep_range(10, 20); + return 0; +} + +static int ar0230_write_array(struct i2c_client *client, + const struct regval *regs) +{ + u32 i; + int ret = 0; + + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) { + if (unlikely(regs[i].addr == REG_DELAY)) + usleep_range(regs[i].val, regs[i].val * 2); + else + ret = ar0230_write_reg(client, regs[i].addr, + AR0230_REG_VALUE_16BIT, + regs[i].val); + } + + return ret; +} + +/* Read registers up to 4 at a time */ +static int ar0230_read_reg(struct i2c_client *client, u16 reg, unsigned int len, + u32 *val) +{ + struct i2c_msg msgs[2]; + u8 *data_be_p; + __be32 data_be = 0; + __be16 reg_addr_be = cpu_to_be16(reg); + int ret; + + if (len > 4 || !len) + return -EINVAL; + + data_be_p = (u8 *)&data_be; + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 2; + msgs[0].buf = (u8 *)®_addr_be; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_be_p[4 - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = be32_to_cpu(data_be); + + return 0; +} + +static int ar0230_get_reso_dist(const struct ar0230_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); +} + +static const struct ar0230_mode * +ar0230_find_best_fit(struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *framefmt = &fmt->format; + int dist; + int cur_best_fit = 0; + int cur_best_fit_dist = -1; + u32 i; + + for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { + dist = ar0230_get_reso_dist(&supported_modes[i], framefmt); + if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { + cur_best_fit_dist = dist; + cur_best_fit = i; + } + } + + return &supported_modes[cur_best_fit]; +} + +static int ar0230_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ar0230 *ar0230 = to_ar0230(sd); + const struct ar0230_mode *mode; + s64 h_blank, vblank_def; + + mutex_lock(&ar0230->mutex); + + mode = ar0230_find_best_fit(fmt); + fmt->format.code = PIX_FORMAT; + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; +#else + mutex_unlock(&ar0230->mutex); + return -ENOTTY; +#endif + } else { + ar0230->cur_mode = mode; + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(ar0230->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(ar0230->vblank, vblank_def, + AR0230_VTS_MAX - mode->height, + 1, vblank_def); + } + + mutex_unlock(&ar0230->mutex); + + return 0; +} + +static int ar0230_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ar0230 *ar0230 = to_ar0230(sd); + const struct ar0230_mode *mode = ar0230->cur_mode; + + mutex_lock(&ar0230->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); +#else + mutex_unlock(&ar0230->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = PIX_FORMAT; + fmt->format.field = V4L2_FIELD_NONE; + } + mutex_unlock(&ar0230->mutex); + + return 0; +} + +static int ar0230_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index != 0) + return -EINVAL; + code->code = PIX_FORMAT; + + return 0; +} + +static int ar0230_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fse->code != PIX_FORMAT) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = supported_modes[fse->index].width; + fse->max_height = supported_modes[fse->index].height; + fse->min_height = supported_modes[fse->index].height; + + return 0; +} + +static int ar0230_enable_test_pattern(struct ar0230 *ar0230, u32 pattern) +{ + return 0; +} + +static void ar0230_get_module_inf(struct ar0230 *ar0230, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strlcpy(inf->base.sensor, AR0230_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, ar0230->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, ar0230->len_name, sizeof(inf->base.lens)); +} + +static long ar0230_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct ar0230 *ar0230 = to_ar0230(sd); + long ret = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + ar0230_get_module_inf(ar0230, (struct rkmodule_inf *)arg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long ar0230_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + long ret; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = ar0230_ioctl(sd, cmd, inf); + if (!ret) + ret = copy_to_user(up, inf, sizeof(*inf)); + kfree(inf); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + +static int __ar0230_start_stream(struct ar0230 *ar0230) +{ + int ret; + + ret = ar0230_write_array(ar0230->client, ar0230->cur_mode->reg_list); + if (ret) + return ret; + + /* In case these controls are set before streaming */ + mutex_unlock(&ar0230->mutex); + ret = v4l2_ctrl_handler_setup(&ar0230->ctrl_handler); + mutex_lock(&ar0230->mutex); + if (ret) + return ret; + + return ar0230_write_reg(ar0230->client, AR0230_REG_CTRL_MODE, + AR0230_REG_VALUE_16BIT, AR0230_MODE_STREAMING); +} + +static int __ar0230_stop_stream(struct ar0230 *ar0230) +{ + return ar0230_write_reg(ar0230->client, AR0230_REG_CTRL_MODE, + AR0230_REG_VALUE_16BIT, AR0230_MODE_SW_STANDBY); +} + +static int ar0230_s_stream(struct v4l2_subdev *sd, int on) +{ + struct ar0230 *ar0230 = to_ar0230(sd); + struct i2c_client *client = ar0230->client; + int ret = 0; + + mutex_lock(&ar0230->mutex); + on = !!on; + if (on == ar0230->streaming) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = __ar0230_start_stream(ar0230); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + __ar0230_stop_stream(ar0230); + pm_runtime_put(&client->dev); + } + + ar0230->streaming = on; +unlock_and_return: + mutex_unlock(&ar0230->mutex); + + return ret; +} + +static int ar0230_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct ar0230 *ar0230 = to_ar0230(sd); + const struct ar0230_mode *mode = ar0230->cur_mode; + + mutex_lock(&ar0230->mutex); + fi->interval.numerator = 10000; + fi->interval.denominator = mode->max_fps * 10000; + mutex_unlock(&ar0230->mutex); + + return 0; +} + +static int ar0230_s_power(struct v4l2_subdev *sd, int on) +{ + struct ar0230 *ar0230 = to_ar0230(sd); + struct i2c_client *client = ar0230->client; + int ret = 0; + + mutex_lock(&ar0230->mutex); + + /* If the power state is not modified - no work to do. */ + if (ar0230->power_on == !!on) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ar0230->power_on = true; + } else { + pm_runtime_put(&client->dev); + ar0230->power_on = false; + } + +unlock_and_return: + mutex_unlock(&ar0230->mutex); + + return ret; +} + +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 ar0230_cal_delay(u32 cycles) +{ + return DIV_ROUND_UP(cycles, AR0230_XVCLK_FREQ / 1000 / 1000); +} + +static int __ar0230_power_on(struct ar0230 *ar0230) +{ + int ret; + u32 i, delay_us; + struct device *dev = &ar0230->client->dev; + + ret = clk_set_rate(ar0230->xvclk, AR0230_XVCLK_FREQ); + if (ret < 0) { + dev_err(dev, "Failed to set xvclk rate (%d)\n", + AR0230_XVCLK_FREQ); + return ret; + } + if (clk_get_rate(ar0230->xvclk) != AR0230_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on %d\n", + AR0230_XVCLK_FREQ); + ret = clk_prepare_enable(ar0230->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + + if (!IS_ERR(ar0230->reset_gpio)) + gpiod_set_value_cansleep(ar0230->reset_gpio, 0); + + for (i = 0; i < AR0230_NUM_SUPPLIES; i++) + regulator_set_voltage(ar0230->supplies[i].consumer, + ar0230_regulator[i].val, + ar0230_regulator[i].val); + + ret = regulator_bulk_enable(AR0230_NUM_SUPPLIES, ar0230->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + + if (!IS_ERR(ar0230->reset_gpio)) + gpiod_set_value_cansleep(ar0230->reset_gpio, 1); + + if (!IS_ERR(ar0230->pwdn_gpio)) + gpiod_set_value_cansleep(ar0230->pwdn_gpio, 1); + + /* 8192 cycles prior to first SCCB transaction */ + delay_us = ar0230_cal_delay(92000); + usleep_range(delay_us, delay_us * 2); + + return 0; + +disable_clk: + clk_disable_unprepare(ar0230->xvclk); + + return ret; +} + +static void __ar0230_power_off(struct ar0230 *ar0230) +{ + if (!IS_ERR(ar0230->pwdn_gpio)) + gpiod_set_value_cansleep(ar0230->pwdn_gpio, 0); + clk_disable_unprepare(ar0230->xvclk); + if (!IS_ERR(ar0230->reset_gpio)) + gpiod_set_value_cansleep(ar0230->reset_gpio, 0); + regulator_bulk_disable(AR0230_NUM_SUPPLIES, ar0230->supplies); +} + +static int ar0230_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ar0230 *ar0230 = to_ar0230(sd); + + return __ar0230_power_on(ar0230); +} + +static int ar0230_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ar0230 *ar0230 = to_ar0230(sd); + + __ar0230_power_off(ar0230); + + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int ar0230_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct ar0230 *ar0230 = to_ar0230(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + const struct ar0230_mode *def_mode = &supported_modes[0]; + + mutex_lock(&ar0230->mutex); + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = PIX_FORMAT; + try_fmt->field = V4L2_FIELD_NONE; + mutex_unlock(&ar0230->mutex); + /* No crop or compose */ + + return 0; +} +#endif + +static int ar0230_g_mbus_config(struct v4l2_subdev *sd, + struct v4l2_mbus_config *config) +{ + config->type = V4L2_MBUS_PARALLEL; + config->flags = V4L2_MBUS_HSYNC_ACTIVE_HIGH | + V4L2_MBUS_VSYNC_ACTIVE_HIGH | + V4L2_MBUS_PCLK_SAMPLE_FALLING; + return 0; +} + +static const struct dev_pm_ops ar0230_pm_ops = { + SET_RUNTIME_PM_OPS(ar0230_runtime_suspend, + ar0230_runtime_resume, NULL) +}; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops ar0230_internal_ops = { + .open = ar0230_open, +}; +#endif + +static const struct v4l2_subdev_core_ops ar0230_core_ops = { + .s_power = ar0230_s_power, + .ioctl = ar0230_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = ar0230_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops ar0230_video_ops = { + .s_stream = ar0230_s_stream, + .g_mbus_config = ar0230_g_mbus_config, + .g_frame_interval = ar0230_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops ar0230_pad_ops = { + .enum_mbus_code = ar0230_enum_mbus_code, + .enum_frame_size = ar0230_enum_frame_sizes, + .get_fmt = ar0230_get_fmt, + .set_fmt = ar0230_set_fmt, +}; + +static const struct v4l2_subdev_ops ar0230_subdev_ops = { + .core = &ar0230_core_ops, + .video = &ar0230_video_ops, + .pad = &ar0230_pad_ops, +}; + +static int ar0230_set_gain(struct ar0230 *ar0230, int gain) +{ + int ret = 0; + u32 again = 0; + + if (gain < 192) + gain = 192; + if (gain < 256) { + again = (u32)(32 - (32 * 128 / gain)); + ret = ar0230_write_reg(ar0230->client, 0x3100, + AR0230_REG_VALUE_16BIT, 0); + ret |= ar0230_write_reg(ar0230->client, AR0230_REG_ANALOG_GAIN, + AR0230_REG_VALUE_16BIT, again); + } else if (gain >= 256 && gain < 345) { + again = (u32)(32 - (64 * 128 / gain)); + again |= 0x0010; + ret = ar0230_write_reg(ar0230->client, 0x3100, + AR0230_REG_VALUE_16BIT, 0); + ret |= ar0230_write_reg(ar0230->client, AR0230_REG_ANALOG_GAIN, + AR0230_REG_VALUE_16BIT, again); + } else if (gain >= 345 && gain < 691) { + again = (u32)(32 - (32 * 345 / gain)); + ret = ar0230_write_reg(ar0230->client, 0x3100, + AR0230_REG_VALUE_16BIT, 4); + ret |= ar0230_write_reg(ar0230->client, AR0230_REG_ANALOG_GAIN, + AR0230_REG_VALUE_16BIT, again); + } else if (gain >= 691 && gain < 1382) { + again = (u32)(32 - (64 * 345 / gain)); + again |= 0x0010; + ret = ar0230_write_reg(ar0230->client, 0x3100, + AR0230_REG_VALUE_16BIT, 4); + ret |= ar0230_write_reg(ar0230->client, AR0230_REG_ANALOG_GAIN, + AR0230_REG_VALUE_16BIT, again); + } else if (gain >= 1382 && gain < 2764) { + again = (u32)(32 - (128 * 345 / gain)); + again |= 0x0020; + ret = ar0230_write_reg(ar0230->client, 0x3100, + AR0230_REG_VALUE_16BIT, 4); + ret |= ar0230_write_reg(ar0230->client, AR0230_REG_ANALOG_GAIN, + AR0230_REG_VALUE_16BIT, again); + } else if (gain >= 2764 && gain < 4023) { + again = (u32)(32 - (256 * 345 / gain)); + again |= 0x0030; + ret = ar0230_write_reg(ar0230->client, 0x3100, + AR0230_REG_VALUE_16BIT, 4); + ret |= ar0230_write_reg(ar0230->client, AR0230_REG_ANALOG_GAIN, + AR0230_REG_VALUE_16BIT, again); + } + return ret; +} + +static int ar0230_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ar0230 *ar0230 = container_of(ctrl->handler, + struct ar0230, ctrl_handler); + struct i2c_client *client = ar0230->client; + int ret = 0; + + if (pm_runtime_get(&client->dev) <= 0) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + ret = ar0230_write_reg(ar0230->client, AR0230_REG_EXPOSURE, + AR0230_REG_VALUE_16BIT, ctrl->val); + break; + case V4L2_CID_ANALOGUE_GAIN: + ret = ar0230_set_gain(ar0230, ctrl->val); + break; + case V4L2_CID_VBLANK: + ret = ar0230_write_reg(ar0230->client, AR0230_REG_VTS, + AR0230_REG_VALUE_16BIT, + ctrl->val + ar0230->cur_mode->height); + break; + case V4L2_CID_TEST_PATTERN: + ret = ar0230_enable_test_pattern(ar0230, ctrl->val); + break; + default: + dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", + __func__, ctrl->id, ctrl->val); + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops ar0230_ctrl_ops = { + .s_ctrl = ar0230_set_ctrl, +}; + +static int ar0230_initialize_controls(struct ar0230 *ar0230) +{ + const struct ar0230_mode *mode; + struct v4l2_ctrl_handler *handler; + s64 exposure_max, vblank_def; + u32 h_blank; + int ret; + + handler = &ar0230->ctrl_handler; + mode = ar0230->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 8); + if (ret) + return ret; + handler->lock = &ar0230->mutex; + + v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, + 0, AR0230_PIXEL_RATE, 1, AR0230_PIXEL_RATE); + + h_blank = mode->hts_def - mode->width; + ar0230->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (ar0230->hblank) + ar0230->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = mode->vts_def - mode->height; + ar0230->vblank = v4l2_ctrl_new_std(handler, &ar0230_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + AR0230_VTS_MAX - mode->height, + 1, vblank_def); + + exposure_max = mode->vts_def - 1; + ar0230->exposure = v4l2_ctrl_new_std(handler, &ar0230_ctrl_ops, + V4L2_CID_EXPOSURE, AR0230_EXPOSURE_MIN, + exposure_max, AR0230_EXPOSURE_STEP, + mode->exp_def); + + ar0230->anal_gain = v4l2_ctrl_new_std(handler, &ar0230_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, ANALOG_GAIN_MIN, + ANALOG_GAIN_MAX, ANALOG_GAIN_STEP, + ANALOG_GAIN_DEFAULT); + + ar0230->test_pattern = v4l2_ctrl_new_std_menu_items(handler, + &ar0230_ctrl_ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ar0230_test_pattern_menu) - 1, + 0, 0, ar0230_test_pattern_menu); + + if (handler->error) { + ret = handler->error; + dev_err(&ar0230->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + ar0230->subdev.ctrl_handler = handler; + + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +static int ar0230_check_sensor_id(struct ar0230 *ar0230, + struct i2c_client *client) +{ + struct device *dev = &ar0230->client->dev; + u32 id = 0; + int ret; + + ret = ar0230_read_reg(client, AR0230_REG_CHIP_ID, + AR0230_REG_VALUE_16BIT, &id); + if (id != CHIP_ID) { + dev_err(dev, "Unexpected sensor id(%x), ret(%d)\n", id, ret); + return -ENODEV; + } + + dev_info(dev, "Detected AR0230 sensor\n"); + + return 0; +} + +static int ar0230_configure_regulators(struct ar0230 *ar0230) +{ + u32 i; + + for (i = 0; i < AR0230_NUM_SUPPLIES; i++) + ar0230->supplies[i].supply = + ar0230_regulator[i].name; + + return devm_regulator_bulk_get(&ar0230->client->dev, + AR0230_NUM_SUPPLIES, + ar0230->supplies); +} + +static int ar0230_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct ar0230 *ar0230; + struct v4l2_subdev *sd; + char facing[2]; + int ret; + + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + ar0230 = devm_kzalloc(dev, sizeof(*ar0230), GFP_KERNEL); + if (!ar0230) + return -ENOMEM; + + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &ar0230->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &ar0230->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &ar0230->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &ar0230->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + + ar0230->client = client; + ar0230->cur_mode = &supported_modes[0]; + + ar0230->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(ar0230->xvclk)) { + dev_err(dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + ar0230->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ar0230->reset_gpio)) + dev_warn(dev, "Failed to get reset-gpios\n"); + + ar0230->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(ar0230->pwdn_gpio)) + dev_warn(dev, "Failed to get pwdn-gpios\n"); + + ret = ar0230_configure_regulators(ar0230); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + + mutex_init(&ar0230->mutex); + + sd = &ar0230->subdev; + v4l2_i2c_subdev_init(sd, client, &ar0230_subdev_ops); + ret = ar0230_initialize_controls(ar0230); + if (ret) + goto err_destroy_mutex; + + ret = __ar0230_power_on(ar0230); + if (ret) + goto err_free_handler; + + ret = ar0230_check_sensor_id(ar0230, client); + if (ret) + goto err_power_off; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &ar0230_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + ar0230->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &ar0230->pad); + if (ret < 0) + goto err_power_off; +#endif + + memset(facing, 0, sizeof(facing)); + if (strcmp(ar0230->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + ar0230->module_index, facing, + AR0230_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor_common(sd); + if (ret) { + dev_err(dev, "v4l2 async register subdev failed\n"); + goto err_clean_entity; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; + +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif +err_power_off: + __ar0230_power_off(ar0230); +err_free_handler: + v4l2_ctrl_handler_free(&ar0230->ctrl_handler); +err_destroy_mutex: + mutex_destroy(&ar0230->mutex); + + return ret; +} + +static int ar0230_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ar0230 *ar0230 = to_ar0230(sd); + + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + v4l2_ctrl_handler_free(&ar0230->ctrl_handler); + mutex_destroy(&ar0230->mutex); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __ar0230_power_off(ar0230); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id ar0230_of_match[] = { + { .compatible = "aptina,ar0230" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ar0230_of_match); +#endif + +static const struct i2c_device_id ar0230_match_id[] = { + { "aptina,ar0230", 0 }, + { }, +}; + +static struct i2c_driver ar0230_i2c_driver = { + .driver = { + .name = AR0230_NAME, + .pm = &ar0230_pm_ops, + .of_match_table = of_match_ptr(ar0230_of_match), + }, + .probe = &ar0230_probe, + .remove = &ar0230_remove, + .id_table = ar0230_match_id, +}; + +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&ar0230_i2c_driver); +} + +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&ar0230_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_DESCRIPTION("Aptina ar0230 sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/bf3925.c b/drivers/media/i2c/bf3925.c new file mode 100644 index 000000000000..49e792e2714f --- /dev/null +++ b/drivers/media/i2c/bf3925.c @@ -0,0 +1,1498 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * BF3925 CMOS Image Sensor driver + * + * Copyright (C) 2015 Texas Instruments, Inc. + * + * Benoit Parrot + * Lad, Prabhakar + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x0) +#define DRIVER_NAME "bf3925" +#define BF3925_PIXEL_RATE (120 * 1000 * 1000) + +/* + * BF3925 register definitions + */ +#define REG_SOFTWARE_STANDBY 0xf2 + +#define REG_SC_CHIP_ID_H 0xfc +#define REG_SC_CHIP_ID_L 0xfd + +#define REG_NULL 0xFFFF /* Array end token */ + +#define SENSOR_ID(_msb, _lsb) ((_msb) << 8 | (_lsb)) +#define BF3925_ID 0x3925 + +struct sensor_register { + u16 addr; + u8 value; +}; + +struct bf3925_framesize { + u16 width; + u16 height; + u16 fps; + u16 max_exp_lines; + const struct sensor_register *regs; +}; + +struct bf3925_pll_ctrl { + u8 ctrl1; + u8 ctrl2; + u8 ctrl3; +}; + +struct bf3925_pixfmt { + u32 code; + /* Output format Register Value (REG_FORMAT_CTRL00) */ + struct sensor_register *format_ctrl_regs; +}; + +struct pll_ctrl_reg { + unsigned int div; + unsigned char reg; +}; + +static const char * const bf3925_supply_names[] = { + "dovdd", /* Digital I/O power */ + "avdd", /* Analog power */ + "dvdd", /* Digital core power */ +}; + +#define BF3925_NUM_SUPPLIES ARRAY_SIZE(bf3925_supply_names) + +struct bf3925 { + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_mbus_framefmt format; + unsigned int fps; + unsigned int xvclk_frequency; + struct clk *xvclk; + struct gpio_desc *power_gpio; + struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; + struct gpio_desc *pwdn2_gpio; + struct regulator_bulk_data supplies[BF3925_NUM_SUPPLIES]; + struct mutex lock; + struct i2c_client *client; + struct v4l2_ctrl_handler ctrls; + struct v4l2_ctrl *link_frequency; + const struct bf3925_framesize *frame_size; + int streaming; + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; +}; + +static const struct sensor_register bf3925_init_regs[] = { + {0xff, 0x01}, //Bit[0]: select reg page + {0xff, 0x01}, //Bit[0]: select reg page + {0x50, 0x00}, //bit[4]: digital subsample Data format selection + {0x51, 0x02}, //YUV Sequence + {0xe0, 0x00}, + {0xe2, 0x64}, + {0xe3, 0x48}, + {0xe4, 0x83}, //Drive capability //0x81 ljx + {0xe7, 0x9b}, + + //clock, dummy + {0xff, 0x01}, //Bit[0]: select reg page + {0xe9, 0x2a}, //08 PLL setting + {0xff, 0x00}, //Bit[0]: select reg page + {0x01, 0x00}, + {0x02, 0x90}, //Dummy Pixel Insert LSB + {0x03, 0x00}, ///02 //yang + {0x04, 0x00}, //Dummy line Insert LSB + {0xff, 0x01}, + {0xe5, 0x32}, + //init black + {0xff, 0x00}, + {0x3d, 0x00}, + {0x30, 0x61}, + {0x31, 0x63}, + {0x32, 0x60}, + {0x33, 0x63}, + + //resolution + {0xff, 0x00}, + {0x05, 0xa2}, ///a0 + {0x09, 0x90}, ///00 + {0x0a, 0x48}, + {0x0b, 0x60}, + {0x0c, 0x00}, + {0x0d, 0xb8}, + {0x0e, 0x40}, + {0xff, 0x01}, + {0x52, 0x01}, //Bit[1]: VSYNC option Bit[0]: HSYNC option + {0x5d, 0x02}, + {0x5a, 0x00}, + {0x5b, 0x00}, + {0x5c, 0x00}, + {0xff, 0x01}, + {0x53, 0x30}, ///60 + {0x54, 0x20}, ///40 + {0x55, 0x00}, + {0x56, 0x20}, ///40 + {0x57, 0x00}, + {0x58, 0x58}, ///b0 + {0xff, 0x01}, + {0x50, 0x00}, //bit[4]: digital subsample Data format selection + + //initial AWB and AE + {0xff, 0x00}, //Bit[0]: select reg page + {0xb2, 0x81}, //Manual AWB & AE + {0xb0, 0x16}, + {0xb1, 0x1d}, + {0xb2, 0x89}, + {0xff, 0x01}, + {0x00, 0x00}, + {0x0e, 0x0a}, + {0x0f, 0x64}, + {0x10, 0x28}, + {0x00, 0x05}, + + //black control + {0xff, 0x00}, + {0x3c, 0x97}, + + //black sun + {0xff, 0x01}, //Bit[0]: select reg page + {0xe1, 0xf8}, //28 bit[7:4]: Pixel bias current + {0xff, 0x00}, //Bit[0]: select reg page + {0x00, 0x47}, //bit[6]: black sun control bit[5:4]: mirror/flip + {0x18, 0x0c}, //PRST indoor + {0x19, 0x1a}, //PRST outdoor + + //lens shading + {0xff, 0x00}, //Bit[0]: select reg page + {0x52, 0x13}, + {0x53, 0x5c}, + {0x54, 0x24}, + {0x55, 0x13}, + {0x56, 0x5c}, + {0x57, 0x24}, + {0x58, 0xd3}, + {0x59, 0x5c}, + {0x5a, 0x24}, + {0x5b, 0x44}, ///46 lens shading gain of R + {0x5c, 0x3C}, ///43 lens shading gain of G1 + {0x5d, 0x40}, //lens shading gain of B + {0x5e, 0x3C}, /// 43lens shading gain of G0 + +#if 0 + /*gamma default */ + {0xff, 0x00}, //Bit[0]: select reg page + {0x60, 0x30}, + {0x61, 0x2a}, + {0x62, 0x24}, + {0x63, 0x1b}, + {0x64, 0x18}, + {0x65, 0x16}, + {0x66, 0x14}, + {0x67, 0x12}, + {0x68, 0x10}, + {0x69, 0x0e}, + {0x6a, 0x0d}, + {0x6b, 0x0c}, + {0x6c, 0x0a}, + {0x6d, 0x09}, + {0x6e, 0x09}, +#endif + {0x6f, 0xf0}, + {0x70, 0x20}, + {0x71, 0x60}, + {0x72, 0x24},///10 + {0x73, 0x24},///10 +#if 1 + //gamma hi-lit,nice over-ex. + {0xff, 0x00}, //Bit[0]: select reg page + {0x60, 0x33}, + {0x61, 0x2b}, + {0x62, 0x27}, + {0x63, 0x22}, + {0x64, 0x1b}, + {0x65, 0x17}, + {0x66, 0x14}, + {0x67, 0x11}, + {0x68, 0x0e}, + {0x69, 0x0c}, + {0x6a, 0x0b}, + {0x6b, 0x0a}, + {0x6c, 0x09}, + {0x6d, 0x08}, + {0x6e, 0x07}, +#else + //gamma nice color + {0xff, 0x00}, //Bit[0]: select reg page + {0x60, 0x28}, + {0x61, 0x28}, + {0x62, 0x26}, + {0x63, 0x22}, + {0x64, 0x1f}, + {0x65, 0x1c}, + {0x66, 0x18}, + {0x67, 0x13}, + {0x68, 0x10}, + {0x69, 0x0d}, + {0x6a, 0x0c}, + {0x6b, 0x0a}, + {0x6c, 0x08}, + {0x6d, 0x07}, + {0x6e, 0x06}, + + ///gamma low denoise + {0xff, 0x00}, //Bit[0]: select reg page + {0x60, 0x24}, + {0x61, 0x30}, + {0x62, 0x20}, + {0x63, 0x1a}, + {0x64, 0x16}, + {0x65, 0x13}, + {0x66, 0x11}, + {0x67, 0x0e}, + {0x68, 0x0d}, + {0x69, 0x0c}, + {0x6a, 0x0b}, + {0x6b, 0x09}, + {0x6c, 0x09}, + {0x6d, 0x08}, + {0x6e, 0x07}, +#endif + +#if 1 + //clearer + //denoise and edge enhancement + {0xff, 0x00}, //Bit[0]: select reg page + {0x80, 0x0f}, + {0x81, 0x1e}, + {0x83, 0x37}, //0x83[7:4]: de_noise threshhole; 0x83[3:0]: de_noise + {0x84, 0xe6}, + {0x85, 0x00}, + {0x86, 0xfc}, + {0x87, 0x00}, + {0x88, 0xa2}, //bit[7:6] 0 is low noise; + {0x89, 0xca}, + {0x8a, 0x44}, + {0x8b, 0x12}, + {0x91, 0x48}, + {0x92, 0x11}, + {0x93, 0x0c}, +#else + //denoise and edge enhancement + {0xff, 0x00}, //Bit[0]: select reg page + {0x80, 0x0f}, + {0x81, 0x0c}, + {0x83, 0x27}, //0x83[7:4]: de_noise threshhole; 0x83[3:0]: de_noise + {0x84, 0xe6}, + {0x85, 0x88}, + {0x86, 0xfa}, + {0x87, 0x1a}, + {0x88, 0xa2}, //bit[7:6] 0 is low noise; + {0x89, 0xca}, + {0x8b, 0x11}, //12 Bright/Dark edge enhancement + {0x91, 0x48}, //45 0x91:40 +#endif + + //AWB + {0xff, 0x00}, //Bit[0]: select reg page + {0xa2, 0x06}, //the low limit of blue gain for indoor scene + {0xa3, 0x28}, //the upper limit of blue gain for indoor scene + {0xa4, 0x0a}, //the low limit of red gain for indoor scene + {0xa5, 0x2c}, //the upper limit of red gain for indoor scene + {0xa7, 0x1b}, //Base B gain + {0xa8, 0x14}, //Base R gain + {0xa9, 0x15}, + {0xaa, 0x18}, + {0xab, 0x26}, + {0xac, 0x5c}, + {0xae, 0x47}, + {0xb2, 0x89}, + {0xb3, 0x66}, // green gain + {0xb4, 0x03}, //the offset of F light + {0xb5, 0x00}, //the offset of non-F light + {0xb6, 0xd9}, //bit[7]: outdoor control + {0xb8, 0xca}, + {0xbb, 0x0d}, + {0xbc, 0x15}, + {0xbd, 0x09}, + {0xbe, 0x24}, + {0xbf, 0x66}, + +#if 1 + // color default + {0xff, 0x00}, //Bit[0]: select reg page + {0xc0, 0x8a}, + {0xc1, 0x05}, + {0xc2, 0x84}, + {0xc3, 0x86}, + {0xc4, 0x03}, + {0xc5, 0x93}, + +#else + //color Gorgeous + {0xff, 0x00}, //Bit[0]: select reg page + {0xc0, 0x83}, + {0xc1, 0x86}, + {0xc2, 0x82}, + {0xc3, 0x8a}, + {0xc4, 0x07}, + {0xc5, 0x9f}, + + //color light + {0xff, 0x00}, //Bit[0]: select reg page + {0xc0, 0x83}, + {0xc1, 0x02}, + {0xc2, 0x84}, + {0xc3, 0x84}, + {0xc4, 0x03}, + {0xc5, 0x8d}, +#endif + + // A color + {0xff, 0x00}, //Bit[0]: select reg page + {0xc6, 0x8a}, + {0xc7, 0x82}, + {0xc8, 0x8b}, + {0xc9, 0x87}, + {0xca, 0x83}, + {0xcb, 0x91}, + + //Outdoor color + {0xff, 0x00}, //Bit[0]: select reg page + {0xd0, 0x90}, + {0xd1, 0x05}, + {0xd2, 0x82}, + {0xd3, 0x88}, + {0xd4, 0x03}, + {0xd5, 0x93}, + {0xcd, 0x30}, + {0xd6, 0x61}, + + //AE + {0xff, 0x01}, //Bit[0]: select reg page + {0x00, 0x05}, + {0x01, 0x8a}, // AE window and weight + {0x04, 0x48}, //4f AE Target//40 + {0x05, 0x48}, //4f Y target value1//48 + {0x07, 0x92}, //Bit[3:2]: the bigger, Y_AVER_MODIFY is smaller + {0x09, 0x8a}, //92 Bit[5:0]: INT_MAX//8c + {0x0a, 0xa5}, + {0x0b, 0x82}, //Bit[5:0]: INT_MIN + {0x0c, 0xb4}, //78 50hz banding + {0x0d, 0x96}, //64 60hz banding + {0x15, 0x02}, //AEC + {0x16, 0x8c}, + {0x17, 0xb5}, + {0x18, 0x50}, ///30 + {0x1b, 0x30}, ///33 minimum global gain + {0x1c, 0x58}, ///66 + {0x1d, 0x38}, ///55 + {0x1e, 0x58}, ///80 + {0x1f, 0x60}, /// c0 maximum gain//a0 + + // saturation + {0xff, 0x01}, //Bit[0]: select reg page + {0x30, 0xff}, ///e0 + {0x31, 0x48}, + {0x32, 0x60}, ///f0 + {0x34, 0xd8}, ///da Cb Saturation Coefficient low 8 bit for NF light + {0x35, 0xc8}, ///ca Cr Saturation Coefficient low 8 bit for NF light + {0x36, 0xff}, //Cb Saturation Coefficient low 8 bit for F light + {0x37, 0xd0}, //Cr Saturation Coefficient low 8 bit for F light + + //skin + {0xff, 0x01}, //Bit[0]: select reg page + {0x3b, 0x08}, + + // auto contrast + {0xff, 0x01}, //Bit[0]: select reg page + {0x3e, 0x02}, // + {0x3e, 0x82}, //do not change + {0x38, 0x40}, +//yang add start switch to 1600*1200 UXGA +//1600*1200 +//window +//yang add end switch to 1600*1200 UXGA + {REG_NULL, 0x00}, +}; + +/* Senor full resolution setting */ +static const struct sensor_register bf3925_full_regs[] = { + //1600*1200 + //window + {0xff, 0x00}, + {0x05, 0xa0}, + {0x09, 0x00}, + {0x0a, 0x48}, + {0x0b, 0x60}, + {0x0c, 0x00}, + {0x0d, 0xb8}, + {0x0e, 0x40}, + {0xff, 0x01}, + {0x52, 0x01}, //Bit[1]: VSYNC option Bit[0]: HSYNC option + {0x5d, 0x02}, + {0x5a, 0x00}, + {0x5b, 0x00}, + {0x5c, 0x00}, + {0xff, 0x01}, + {0x53, 0x60}, + {0x54, 0x40}, + {0x55, 0x00}, + {0x56, 0x40}, + {0x57, 0x00}, + {0x58, 0xb0}, + {0xff, 0x01}, + {0x50, 0x00}, + + //clock, dummy + {0xff, 0x01}, //Bit[0]: select reg page + {0x09, 0x86}, + {0xe9, 0x2a}, //08 PLL setting + {0xff, 0x00}, //Bit[0]: select reg page + {0x01, 0x00}, + {0x02, 0x00}, //Dummy Pixel Insert LSB + {0x03, 0x00}, ///02 //yang + {0x04, 0x00}, //Dummy line Insert LSB + + {REG_NULL, 0x00}, +}; + +/* Preview resolution setting*/ +static const struct sensor_register bf3925_svga_regs_15fps[] = { + //800*600 + //window + {0xff, 0x00}, + {0x05, 0xa0}, + {0x09, 0x00}, + {0x0a, 0x48}, + {0x0b, 0x60}, + {0x0c, 0x00}, + {0x0d, 0xb8}, + {0x0e, 0x40}, + {0xff, 0x01}, + {0x52, 0x01}, //Bit[1]: VSYNC option Bit[0]: HSYNC option + {0x5d, 0x02}, + {0x5a, 0x00}, + {0x5b, 0x00}, + {0x5c, 0x00}, + {0xff, 0x01}, + {0x53, 0x30}, + {0x54, 0x20}, + {0x55, 0x02}, + {0x56, 0x22}, + {0x57, 0x01}, + {0x58, 0x59}, + {0xff, 0x01}, + {0x50, 0x00}, + + //clock, dummy + {0xff, 0x01}, //Bit[0]: select reg page + {0x09, 0x86}, + {0xe9, 0x2a}, //08 PLL setting + {0xff, 0x00}, //Bit[0]: select reg page + {0x01, 0x00}, + {0x02, 0x00}, //Dummy Pixel Insert LSB + {0x03, 0x00}, ///02 //yang + {0x04, 0x00}, //Dummy line Insert LSB + + {REG_NULL, 0x00}, +}; + +/* Preview resolution setting*/ +static const struct sensor_register bf3925_svga_regs_30fps[] = { + //800*600 + {0xff, 0x00}, + {0x05, 0xa2}, + {0x09, 0x04}, + + {0x0a, 0x4c}, + {0x0b, 0x60}, + {0x0c, 0x04}, + {0x0d, 0xbc}, + {0x0e, 0x40}, + {0xff, 0x01}, + {0x52, 0x01}, + {0x5d, 0x02}, + {0x5a, 0x00}, + {0x5b, 0x00}, + {0x5c, 0x00}, + {0xff, 0x01}, + {0x09, 0x83}, + {0x53, 0x30}, + {0x54, 0x20}, + {0x55, 0x02}, + {0x56, 0x22}, + {0x57, 0x01}, + {0x58, 0x59}, + {0xff, 0x01}, + {0x50, 0x00}, + {0xe9, 0x2a}, + //clock, dummy + {0xff, 0x01}, //Bit[0]: select reg page + {0x09, 0x83}, + + /* 08 PLL setting 0x09: 1 times + * 0x1b: multiply 5/4 0x2b: 3/2 multiply + * 0x08:double 0x1a: 5/2 multiply + * 0x2a: triple 0x2a ljx 2017-6 + */ + {0xe9, 0x08}, + {0xff, 0x00}, //Bit[0]: select reg page + {0x01, 0x00}, + {0x02, 0xea}, //Dummy Pixel Insert LSB + {0x03, 0x00}, ///02 //yang + {0x04, 0x00}, //Dummy line Insert LSB + + {REG_NULL, 0x00}, +}; + +static const struct bf3925_framesize bf3925_framesizes[] = { + { /* SVGA */ + .width = 800, + .height = 600, + .fps = 15, + .regs = bf3925_svga_regs_15fps, + }, { /* SVGA */ + .width = 800, + .height = 600, + .fps = 30, + .regs = bf3925_svga_regs_30fps, + }, { /* FULL */ + .width = 1600, + .height = 1200, + .fps = 15, + .regs = bf3925_full_regs, + } +}; + +static const struct bf3925_pixfmt bf3925_formats[] = { + { + .code = MEDIA_BUS_FMT_UYVY8_2X8, + } +}; + +static inline struct bf3925 *to_bf3925(struct v4l2_subdev *sd) +{ + return container_of(sd, struct bf3925, sd); +} + +/* sensor register write */ +static int bf3925_write(struct i2c_client *client, u8 reg, u8 val) +{ + struct i2c_msg msg; + u8 buf[2]; + int ret; + + dev_dbg(&client->dev, "write reg(0x%x val:0x%x)!\n", reg, val); + buf[0] = reg & 0xFF; + buf[1] = val; + + msg.addr = client->addr; + msg.flags = client->flags; + msg.buf = buf; + msg.len = sizeof(buf); + + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret >= 0) + return 0; + + dev_err(&client->dev, + "bf3925 write reg(0x%x val:0x%x) failed !\n", reg, val); + + return ret; +} + +/* sensor register read */ +static int bf3925_read(struct i2c_client *client, u8 reg, u8 *val) +{ + struct i2c_msg msg[2]; + u8 buf[1]; + int ret; + + buf[0] = reg & 0xFF; + + msg[0].addr = client->addr; + msg[0].flags = client->flags; + msg[0].buf = buf; + msg[0].len = sizeof(buf); + + msg[1].addr = client->addr; + msg[1].flags = client->flags | I2C_M_RD; + msg[1].buf = buf; + msg[1].len = 1; + + ret = i2c_transfer(client->adapter, msg, 2); + if (ret >= 0) { + *val = buf[0]; + return 0; + } + + dev_err(&client->dev, + "bf3925 read reg:0x%x failed !\n", reg); + + return ret; +} + +static int bf3925_write_array(struct i2c_client *client, + const struct sensor_register *regs) +{ + int i, ret = 0; + + i = 0; + while (regs[i].addr != REG_NULL) { + ret = bf3925_write(client, regs[i].addr, regs[i].value); + if (ret) { + dev_err(&client->dev, "%s failed !\n", __func__); + break; + } + + i++; + } + + return ret; +} + +static void bf3925_get_default_format(struct v4l2_mbus_framefmt *format) +{ + format->width = bf3925_framesizes[0].width; + format->height = bf3925_framesizes[0].height; + format->colorspace = V4L2_COLORSPACE_SRGB; + format->code = bf3925_formats[0].code; + format->field = V4L2_FIELD_NONE; +} + +static void bf3925_set_streaming(struct bf3925 *bf3925, int on) +{ + struct i2c_client *client = bf3925->client; + int ret; + + dev_dbg(&client->dev, "%s: on: %d\n", __func__, on); + + ret = bf3925_write(client, REG_SOFTWARE_STANDBY, on); + if (ret) + dev_err(&client->dev, "bf3925 soft standby failed\n"); +} + +/* + * V4L2 subdev video and pad level operations + */ + +static int bf3925_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + dev_dbg(&client->dev, "%s:\n", __func__); + + if (code->index >= ARRAY_SIZE(bf3925_formats)) + return -EINVAL; + + code->code = bf3925_formats[code->index].code; + + return 0; +} + +static int bf3925_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int i = ARRAY_SIZE(bf3925_formats); + + dev_dbg(&client->dev, "%s:\n", __func__); + + if (fse->index >= ARRAY_SIZE(bf3925_framesizes)) + return -EINVAL; + + while (--i) + if (fse->code == bf3925_formats[i].code) + break; + + fse->code = bf3925_formats[i].code; + + fse->min_width = bf3925_framesizes[fse->index].width; + fse->max_width = fse->min_width; + fse->max_height = bf3925_framesizes[fse->index].height; + fse->min_height = fse->max_height; + + return 0; +} + +static int bf3925_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct bf3925 *bf3925 = to_bf3925(sd); + + dev_dbg(&client->dev, "%s enter\n", __func__); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + struct v4l2_mbus_framefmt *mf; + + mf = v4l2_subdev_get_try_format(sd, cfg, 0); + mutex_lock(&bf3925->lock); + fmt->format = *mf; + mutex_unlock(&bf3925->lock); + return 0; +#else + return -ENOTTY; +#endif + } + + mutex_lock(&bf3925->lock); + fmt->format = bf3925->format; + mutex_unlock(&bf3925->lock); + + dev_dbg(&client->dev, "%s: %x %dx%d\n", __func__, + bf3925->format.code, bf3925->format.width, + bf3925->format.height); + + return 0; +} + +static void __bf3925_try_frame_size_fps(struct v4l2_mbus_framefmt *mf, + const struct bf3925_framesize **size, + unsigned int fps) +{ + const struct bf3925_framesize *fsize = &bf3925_framesizes[0]; + const struct bf3925_framesize *match = NULL; + unsigned int i = ARRAY_SIZE(bf3925_framesizes); + unsigned int min_err = UINT_MAX; + + while (i--) { + unsigned int err = abs(fsize->width - mf->width) + + abs(fsize->height - mf->height); + if (err < min_err && fsize->regs[0].addr) { + min_err = err; + match = fsize; + } + fsize++; + } + + if (!match) { + match = &bf3925_framesizes[0]; + } else { + fsize = &bf3925_framesizes[0]; + for (i = 0; i < ARRAY_SIZE(bf3925_framesizes); i++) { + if (fsize->width == match->width && + fsize->height == match->height && + fps >= fsize->fps) + match = fsize; + + fsize++; + } + } + + mf->width = match->width; + mf->height = match->height; + + if (size) + *size = match; +} + +static int bf3925_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int index = ARRAY_SIZE(bf3925_formats); + struct v4l2_mbus_framefmt *mf = &fmt->format; + const struct bf3925_framesize *size = NULL; + struct bf3925 *bf3925 = to_bf3925(sd); + int ret = 0; + + dev_dbg(&client->dev, "%s enter\n", __func__); + + __bf3925_try_frame_size_fps(mf, &size, bf3925->fps); + + while (--index >= 0) + if (bf3925_formats[index].code == mf->code) + break; + + if (index < 0) + return -EINVAL; + + mf->colorspace = V4L2_COLORSPACE_SRGB; + mf->code = bf3925_formats[index].code; + mf->field = V4L2_FIELD_NONE; + + mutex_lock(&bf3925->lock); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + *mf = fmt->format; +#else + return -ENOTTY; +#endif + } else { + if (bf3925->streaming) { + mutex_unlock(&bf3925->lock); + return -EBUSY; + } + + bf3925->frame_size = size; + bf3925->format = fmt->format; + } + + mutex_unlock(&bf3925->lock); + return ret; +} + +static int bf3925_s_stream(struct v4l2_subdev *sd, int on) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct bf3925 *bf3925 = to_bf3925(sd); + int ret = 0; + + dev_info(&client->dev, "%s: on: %d, %dx%d@%d\n", __func__, on, + bf3925->frame_size->width, + bf3925->frame_size->height, + bf3925->frame_size->fps); + + mutex_lock(&bf3925->lock); + + on = !!on; + + if (bf3925->streaming == on) + goto unlock; + + if (!on) { + /* Stop Streaming Sequence */ + bf3925_set_streaming(bf3925, 0x02); + bf3925->streaming = on; + goto unlock; + } + + ret = bf3925_write_array(client, bf3925->frame_size->regs); + if (ret) + goto unlock; + + bf3925_set_streaming(bf3925, 0x00); + bf3925->streaming = on; + +unlock: + mutex_unlock(&bf3925->lock); + return ret; +} + +static int bf3925_set_test_pattern(struct bf3925 *bf3925, int value) +{ + return 0; +} + +static int bf3925_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct bf3925 *bf3925 = + container_of(ctrl->handler, struct bf3925, ctrls); + + switch (ctrl->id) { + case V4L2_CID_TEST_PATTERN: + return bf3925_set_test_pattern(bf3925, ctrl->val); + } + + return 0; +} + +static const struct v4l2_ctrl_ops bf3925_ctrl_ops = { + .s_ctrl = bf3925_s_ctrl, +}; + +static const char * const bf3925_test_pattern_menu[] = { + "Disabled", + "Vertical Color Bars", +}; + +/* ----------------------------------------------------------------------------- + * V4L2 subdev internal operations + */ + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int bf3925_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + + dev_dbg(&client->dev, "%s:\n", __func__); + + bf3925_get_default_format(format); + + return 0; +} +#endif + +static int bf3925_g_mbus_config(struct v4l2_subdev *sd, + struct v4l2_mbus_config *config) +{ + config->type = V4L2_MBUS_PARALLEL; + config->flags = V4L2_MBUS_HSYNC_ACTIVE_HIGH | + V4L2_MBUS_VSYNC_ACTIVE_LOW | + V4L2_MBUS_PCLK_SAMPLE_RISING; + + return 0; +} + +static int bf3925_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct bf3925 *bf3925 = to_bf3925(sd); + + mutex_lock(&bf3925->lock); + fi->interval.numerator = 10000; + fi->interval.denominator = bf3925->fps * 10000; + mutex_unlock(&bf3925->lock); + + return 0; +} + +static int bf3925_s_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct bf3925 *bf3925 = to_bf3925(sd); + const struct bf3925_framesize *size = NULL; + struct v4l2_mbus_framefmt mf; + unsigned int fps; + int ret = 0; + + dev_dbg(&client->dev, "Setting %d/%d frame interval\n", + fi->interval.numerator, fi->interval.denominator); + + mutex_lock(&bf3925->lock); + if (bf3925->format.width == 1600) + goto unlock; + fps = DIV_ROUND_CLOSEST(fi->interval.denominator, + fi->interval.numerator); + mf = bf3925->format; + __bf3925_try_frame_size_fps(&mf, &size, fps); + + if (bf3925->frame_size != size) { + dev_info(&client->dev, "%s match wxh@FPS is %dx%d@%d\n", + __func__, size->width, size->height, size->fps); + ret = bf3925_write_array(client, size->regs); + if (ret) + goto unlock; + bf3925->frame_size = size; + bf3925->fps = fps; + } +unlock: + mutex_unlock(&bf3925->lock); + + return ret; +} + +static void bf3925_get_module_inf(struct bf3925 *bf3925, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strlcpy(inf->base.sensor, DRIVER_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, bf3925->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, bf3925->len_name, sizeof(inf->base.lens)); +} + +static long bf3925_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct bf3925 *bf3925 = to_bf3925(sd); + long ret = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + bf3925_get_module_inf(bf3925, (struct rkmodule_inf *)arg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long bf3925_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_awb_cfg *cfg; + long ret; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = bf3925_ioctl(sd, cmd, inf); + if (!ret) + ret = copy_to_user(up, inf, sizeof(*inf)); + kfree(inf); + break; + case RKMODULE_AWB_CFG: + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(cfg, up, sizeof(*cfg)); + if (!ret) + ret = bf3925_ioctl(sd, cmd, cfg); + kfree(cfg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + +static int bf3925_init(struct v4l2_subdev *sd, u32 val) +{ + int ret; + struct bf3925 *bf3925 = to_bf3925(sd); + struct i2c_client *client = bf3925->client; + + dev_info(&client->dev, "%s(%d)\n", __func__, __LINE__); + /* soft reset */ + ret = bf3925_write(client, 0xf2, 0x03); + ret = bf3925_write_array(client, bf3925_init_regs); + return ret; +} + +static int bf3925_power(struct v4l2_subdev *sd, int on) +{ + int ret; + struct bf3925 *bf3925 = to_bf3925(sd); + struct i2c_client *client = bf3925->client; + struct device *dev = &bf3925->client->dev; + + dev_info(&client->dev, "%s(%d) on(%d)\n", __func__, __LINE__, on); + if (on) { + if (!IS_ERR(bf3925->pwdn_gpio)) { + gpiod_set_value_cansleep(bf3925->pwdn_gpio, 0); + usleep_range(2000, 5000); + } + ret = bf3925_init(sd, 0); + usleep_range(10000, 20000); + if (ret) + dev_err(dev, "init error\n"); + } else { + if (!IS_ERR(bf3925->pwdn_gpio)) { + gpiod_set_value_cansleep(bf3925->pwdn_gpio, 1); + usleep_range(2000, 5000); + } + } + return 0; +} + +static const struct v4l2_subdev_core_ops bf3925_subdev_core_ops = { + .log_status = v4l2_ctrl_subdev_log_status, + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, + .ioctl = bf3925_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = bf3925_compat_ioctl32, +#endif + .s_power = bf3925_power, +}; + +static const struct v4l2_subdev_video_ops bf3925_subdev_video_ops = { + .s_stream = bf3925_s_stream, + .g_mbus_config = bf3925_g_mbus_config, + .g_frame_interval = bf3925_g_frame_interval, + .s_frame_interval = bf3925_s_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops bf3925_subdev_pad_ops = { + .enum_mbus_code = bf3925_enum_mbus_code, + .enum_frame_size = bf3925_enum_frame_sizes, + .get_fmt = bf3925_get_fmt, + .set_fmt = bf3925_set_fmt, +}; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_ops bf3925_subdev_ops = { + .core = &bf3925_subdev_core_ops, + .video = &bf3925_subdev_video_ops, + .pad = &bf3925_subdev_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops bf3925_subdev_internal_ops = { + .open = bf3925_open, +}; +#endif + +static int bf3925_detect(struct bf3925 *bf3925) +{ + struct i2c_client *client = bf3925->client; + u8 pid, ver; + int ret; + + dev_dbg(&client->dev, "%s:\n", __func__); + + /* Check sensor revision */ + ret = bf3925_read(client, REG_SC_CHIP_ID_H, &pid); + if (!ret) + ret = bf3925_read(client, REG_SC_CHIP_ID_L, &ver); + + if (!ret) { + unsigned short id; + + id = SENSOR_ID(pid, ver); + if (id != BF3925_ID) { + ret = -1; + dev_err(&client->dev, + "Sensor detection failed (%04X, %d)\n", + id, ret); + } else { + dev_info(&client->dev, "Found BF%04X sensor\n", id); + if (!IS_ERR(bf3925->pwdn_gpio)) + gpiod_set_value_cansleep(bf3925->pwdn_gpio, 1); + } + } + + return ret; +} + +static int __bf3925_power_on(struct bf3925 *bf3925) +{ + int ret; + struct device *dev = &bf3925->client->dev; + + dev_info(dev, "power on!!!\n"); + if (!IS_ERR(bf3925->xvclk)) { + ret = clk_set_rate(bf3925->xvclk, 24000000); + if (ret < 0) + dev_info(dev, "Failed to set xvclk rate (24MHz)\n"); + } + if (!IS_ERR(bf3925->xvclk)) { + ret = clk_prepare_enable(bf3925->xvclk); + if (ret < 0) + dev_info(dev, "Failed to enable xvclk\n"); + } + usleep_range(7000, 10000); + + if (!IS_ERR(bf3925->pwdn_gpio)) { + gpiod_set_value_cansleep(bf3925->pwdn_gpio, 1); + usleep_range(2000, 5000); + } + + if (!IS_ERR(bf3925->supplies)) { + ret = regulator_bulk_enable(BF3925_NUM_SUPPLIES, + bf3925->supplies); + if (ret < 0) + dev_info(dev, "Failed to enable regulators\n"); + + usleep_range(20000, 50000); + } + + if (!IS_ERR(bf3925->pwdn2_gpio)) { + gpiod_set_value_cansleep(bf3925->pwdn2_gpio, 1); + usleep_range(2000, 5000); + } + + if (!IS_ERR(bf3925->pwdn_gpio)) { + gpiod_set_value_cansleep(bf3925->pwdn_gpio, 0); + usleep_range(2000, 5000); + } + + return 0; +} + +static void __bf3925_power_off(struct bf3925 *bf3925) +{ + if (!IS_ERR(bf3925->xvclk)) + clk_disable_unprepare(bf3925->xvclk); + if (!IS_ERR(bf3925->supplies)) + regulator_bulk_disable(BF3925_NUM_SUPPLIES, bf3925->supplies); + if (!IS_ERR(bf3925->pwdn_gpio)) + gpiod_set_value_cansleep(bf3925->pwdn_gpio, 1); +} + +static int bf3925_configure_regulators(struct bf3925 *bf3925) +{ + unsigned int i; + + for (i = 0; i < BF3925_NUM_SUPPLIES; i++) + bf3925->supplies[i].supply = bf3925_supply_names[i]; + + return devm_regulator_bulk_get(&bf3925->client->dev, + BF3925_NUM_SUPPLIES, + bf3925->supplies); +} + +static int bf3925_parse_of(struct bf3925 *bf3925) +{ + struct device *dev = &bf3925->client->dev; + struct device_node *node = dev->of_node; + struct gpio_desc *pwdn_gpio; + unsigned int pwdn = -1; + enum of_gpio_flags flags; + int ret; + + bf3925->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(bf3925->pwdn_gpio)) { + dev_info(dev, "Failed to get pwdn-gpios, maybe no use\n"); + pwdn = of_get_named_gpio_flags(node, "pwdn-gpios", 0, &flags); + pwdn_gpio = gpio_to_desc(pwdn); + if (IS_ERR(pwdn_gpio)) + dev_info(dev, "Failed to get pwdn-gpios again\n"); + else + bf3925->pwdn_gpio = pwdn_gpio; + } + + bf3925->pwdn2_gpio = devm_gpiod_get(dev, "pwdn2", GPIOD_OUT_LOW); + if (IS_ERR(bf3925->pwdn2_gpio)) { + dev_info(dev, "Failed to get pwdn2-gpios, maybe no use\n"); + pwdn = of_get_named_gpio_flags(node, "pwdn2-gpios", 0, &flags); + pwdn_gpio = gpio_to_desc(pwdn); + if (IS_ERR(pwdn_gpio)) + dev_info(dev, "Failed to get pwdn2-gpios again\n"); + else + bf3925->pwdn2_gpio = pwdn_gpio; + } + + ret = bf3925_configure_regulators(bf3925); + if (ret) + dev_info(dev, "Failed to get power regulators\n"); + + return __bf3925_power_on(bf3925); +} + +static int bf3925_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct v4l2_subdev *sd; + struct bf3925 *bf3925; + char facing[2] = "b"; + int ret; + + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + bf3925 = devm_kzalloc(&client->dev, sizeof(*bf3925), GFP_KERNEL); + if (!bf3925) + return -ENOMEM; + + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &bf3925->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &bf3925->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &bf3925->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &bf3925->len_name); + if (ret) { + dev_err(&client->dev, "could not get module information!\n"); + return -EINVAL; + } + + bf3925->client = client; + bf3925->xvclk = devm_clk_get(&client->dev, "xvclk"); + if (IS_ERR(bf3925->xvclk)) { + dev_err(&client->dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + bf3925_parse_of(bf3925); + + bf3925->xvclk_frequency = clk_get_rate(bf3925->xvclk); + if (bf3925->xvclk_frequency < 6000000 || + bf3925->xvclk_frequency > 27000000) + return -EINVAL; + + v4l2_ctrl_handler_init(&bf3925->ctrls, 2); + bf3925->link_frequency = + v4l2_ctrl_new_std(&bf3925->ctrls, &bf3925_ctrl_ops, + V4L2_CID_PIXEL_RATE, 0, + BF3925_PIXEL_RATE, 1, + BF3925_PIXEL_RATE); + + v4l2_ctrl_new_std_menu_items(&bf3925->ctrls, &bf3925_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(bf3925_test_pattern_menu) - 1, + 0, 0, bf3925_test_pattern_menu); + bf3925->sd.ctrl_handler = &bf3925->ctrls; + + if (bf3925->ctrls.error) { + dev_err(&client->dev, "%s: control initialization error %d\n", + __func__, bf3925->ctrls.error); + return bf3925->ctrls.error; + } + + sd = &bf3925->sd; + client->flags |= I2C_CLIENT_SCCB; +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + v4l2_i2c_subdev_init(sd, client, &bf3925_subdev_ops); + + sd->internal_ops = &bf3925_subdev_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; +#endif + +#if defined(CONFIG_MEDIA_CONTROLLER) + bf3925->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &bf3925->pad); + if (ret < 0) { + v4l2_ctrl_handler_free(&bf3925->ctrls); + return ret; + } +#endif + + mutex_init(&bf3925->lock); + + bf3925_get_default_format(&bf3925->format); + bf3925->frame_size = &bf3925_framesizes[0]; + bf3925->format.width = bf3925_framesizes[0].width; + bf3925->format.height = bf3925_framesizes[0].height; + bf3925->fps = bf3925_framesizes[0].fps; + + ret = bf3925_detect(bf3925); + if (ret < 0) + goto error; + + memset(facing, 0, sizeof(facing)); + if (strcmp(bf3925->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + bf3925->module_index, facing, + DRIVER_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor_common(&bf3925->sd); + if (ret) + goto error; + + dev_info(&client->dev, "%s sensor driver registered !!\n", sd->name); + + return 0; + +error: + v4l2_ctrl_handler_free(&bf3925->ctrls); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + mutex_destroy(&bf3925->lock); + __bf3925_power_off(bf3925); + return ret; +} + +static int bf3925_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct bf3925 *bf3925 = to_bf3925(sd); + + v4l2_ctrl_handler_free(&bf3925->ctrls); + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + mutex_destroy(&bf3925->lock); + + __bf3925_power_off(bf3925); + + return 0; +} + +static const struct i2c_device_id bf3925_id[] = { + { "bf3925", 0 }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(i2c, bf3925_id); + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id bf3925_of_match[] = { + { .compatible = "byd,bf3925", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, bf3925_of_match); +#endif + +static struct i2c_driver bf3925_i2c_driver = { + .driver = { + .name = DRIVER_NAME, + .of_match_table = of_match_ptr(bf3925_of_match), + }, + .probe = bf3925_probe, + .remove = bf3925_remove, + .id_table = bf3925_id, +}; + +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&bf3925_i2c_driver); +} + +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&bf3925_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_AUTHOR("Benoit Parrot "); +MODULE_DESCRIPTION("BF3925 CMOS Image Sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/gc0329.c b/drivers/media/i2c/gc0329.c index a89607e14439..3a81d629534d 100644 --- a/drivers/media/i2c/gc0329.c +++ b/drivers/media/i2c/gc0329.c @@ -22,7 +22,8 @@ #include #include #include - +#include +#include #include #include #include @@ -33,6 +34,7 @@ #include #include +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x0) #define DRIVER_NAME "gc0329" #define GC0329_PIXEL_RATE (24 * 1000 * 1000) @@ -96,6 +98,10 @@ struct gc0329 { struct v4l2_ctrl *link_frequency; const struct gc0329_framesize *frame_size; int streaming; + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; }; static const struct sensor_register gc0329_vga_regs[] = { @@ -652,6 +658,76 @@ static int gc0329_set_fmt(struct v4l2_subdev *sd, return ret; } +static void gc0329_get_module_inf(struct gc0329 *gc0329, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strlcpy(inf->base.sensor, DRIVER_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, gc0329->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, gc0329->len_name, sizeof(inf->base.lens)); +} + +static long gc0329_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct gc0329 *gc0329 = to_gc0329(sd); + long ret = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + gc0329_get_module_inf(gc0329, (struct rkmodule_inf *)arg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long gc0329_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_awb_cfg *cfg; + long ret; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = gc0329_ioctl(sd, cmd, inf); + if (!ret) + ret = copy_to_user(up, inf, sizeof(*inf)); + kfree(inf); + break; + case RKMODULE_AWB_CFG: + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(cfg, up, sizeof(*cfg)); + if (!ret) + ret = gc0329_ioctl(sd, cmd, cfg); + kfree(cfg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + static int gc0329_s_stream(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -803,6 +879,10 @@ static const struct v4l2_subdev_core_ops gc0329_subdev_core_ops = { .log_status = v4l2_ctrl_subdev_log_status, .subscribe_event = v4l2_ctrl_subdev_subscribe_event, .unsubscribe_event = v4l2_event_subdev_unsubscribe, + .ioctl = gc0329_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = gc0329_compat_ioctl32, +#endif }; static const struct v4l2_subdev_video_ops gc0329_subdev_video_ops = { @@ -942,14 +1022,35 @@ static int gc0329_parse_of(struct gc0329 *gc0329) static int gc0329_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; struct v4l2_subdev *sd; struct gc0329 *gc0329; + char facing[2]; int ret; + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + gc0329 = devm_kzalloc(&client->dev, sizeof(*gc0329), GFP_KERNEL); if (!gc0329) return -ENOMEM; + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &gc0329->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &gc0329->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &gc0329->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &gc0329->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + gc0329->client = client; gc0329->xvclk = devm_clk_get(&client->dev, "xvclk"); if (IS_ERR(gc0329->xvclk)) { @@ -995,8 +1096,8 @@ static int gc0329_probe(struct i2c_client *client, #if defined(CONFIG_MEDIA_CONTROLLER) gc0329->pad.flags = MEDIA_PAD_FL_SOURCE; - sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; - ret = media_entity_init(&sd->entity, 1, &gc0329->pad, 0); + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &gc0329->pad); if (ret < 0) { v4l2_ctrl_handler_free(&gc0329->ctrls); return ret; @@ -1015,7 +1116,16 @@ static int gc0329_probe(struct i2c_client *client, if (ret < 0) goto error; - ret = v4l2_async_register_subdev(&gc0329->sd); + memset(facing, 0, sizeof(facing)); + if (strcmp(gc0329->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + gc0329->module_index, facing, + DRIVER_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor_common(sd); if (ret) goto error; diff --git a/drivers/media/i2c/gc0403.c b/drivers/media/i2c/gc0403.c new file mode 100644 index 000000000000..53aa35f7fe39 --- /dev/null +++ b/drivers/media/i2c/gc0403.c @@ -0,0 +1,1289 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * gc0403 driver + * + * Copyright (C) 2019 Fuzhou Rockchip Electronics Co.,Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * GC0403 register definitions + */ +#define GC0403_REG_EXP_H 0x03 +#define GC0403_REG_EXP_L 0x04 +#define GC0403_REG_VBLK_H 0x07 +#define GC0403_REG_VBLK_L 0x08 +#define GC0403_REG_MIPI_EN 0x10 +#define GC0403_REG_DGAIN_H 0xb1 +#define GC0403_REG_DGAIN_L 0xb2 +#define GC0403_REG_AGAIN 0xb6 +#define GC0403_REG_CHIP_ID_H 0xf0 +#define GC0403_REG_CHIP_ID_L 0xf1 +#define PAGE_SELECT_REG 0xfe +#define REG_NULL 0xff + +#define GC0403_CHIP_ID 0x0403 +#define SENSOR_ID(_msb, _lsb) ((_msb) << 8 | (_lsb)) + +#define GC0403_ANALOG_GAIN_MIN 0 +#define GC0403_ANALOG_GAIN_MAX 0x0a +#define GC0403_ANALOG_GAIN_STP 1 +#define GC0403_ANALOG_GAIN_DFT 1 +/* gain[4:0] [0x00,0x0a]*/ +#define GC0403_FETCH_ANALOG_GAIN(VAL) ((VAL) & 0x0F) + +#ifndef V4L2_CID_DIGITAL_GAIN +#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN +#endif + +#define GC0403_DIGI_GAIN_MIN 0x40 +#define GC0403_DIGI_GAIN_MAX 0x3ff +#define GC0403_DIGI_GAIN_STP 1 +#define GC0403_DIGI_GAIN_DFT 0x40 +/* gain[9:6] */ +#define GC0403_FETCH_DIGITAL_GAIN_HIGH(VAL) (((VAL) >> 6) & 0x0F) +/* gain[5:0] */ +#define GC0403_FETCH_DIGITAL_GAIN_LOW(VAL) (((VAL) << 2) & 0xFC) + +#define GC0403_TOTAL_GAIN_MIN 100 +#define GC0403_TOTAL_GAIN_MAX 3200 +#define GC0403_TOTAL_GAIN_STEP 1 + +#define GC0403_EXPOSURE_MAX 8191 +#define GC0403_EXPOSURE_MIN 1 + +#define GC0403_NAME "gc0403" +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x01) + +#define GC0403_XVCLK_FREQ 24000000 +#define GC0403_LINK_FREQ 96000000 +#define GC0403_PIXEL_RATE (GC0403_LINK_FREQ * 2 * 1 / 10) + +static const s64 link_freq_menu_items[] = { + GC0403_LINK_FREQ +}; + +static const char * const gc0403_supply_names[] = { + "dovdd", /* Digital I/O power */ + "avdd", /* Analog power */ +}; + +#define GC0403_NUM_SUPPLIES ARRAY_SIZE(gc0403_supply_names) + +struct regval { + u8 addr; + u8 val; +}; + +struct gc0403_mode { + u32 width; + u32 height; + u32 max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + const struct regval *reg_list; +}; + +struct gc0403 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *pwdn_gpio; + struct regulator_bulk_data supplies[GC0403_NUM_SUPPLIES]; + + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *anal_gain; + struct v4l2_ctrl *digi_gain; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + /* mutex lock, protect current operation */ + struct mutex mutex; + bool streaming; + const struct gc0403_mode *cur_mode; + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; +}; + +#define to_gc0403(sd) container_of(sd, struct gc0403, subdev) + +/* + * MCLK=24Mhz + * MIPI_CLOCK=192Mbps + * Actual_window_size=640*480 + * HD=1362 + * VD=586 + * row_time=56.75us,FPS = 30fps; + */ +static const struct regval gc0403_vga_regs[] = { + /****SYS****/ + {0xfe, 0x80}, + {0xfe, 0x80}, + {0xfe, 0x80}, + {0xf2, 0x00}, /* sync_pad_io_ebi */ + {0xf6, 0x00}, /*up down */ + {0xfc, 0xc6}, + {0xf7, 0x19}, /* pll enable */ + {0xf8, 0x01}, /* Pll mode 2 */ + {0xf9, 0x3e}, /* [0] pll enable solve IOVDD large current problem */ + {0xfe, 0x03}, + {0x06, 0x80}, + {0x06, 0x00}, + {0xfe, 0x00}, + {0xf9, 0x2e}, + {0xfe, 0x00}, + {0xfa, 0x00}, /* div */ + {0xfe, 0x00}, + /**ANALOG&CISCTL**/ + {0x03, 0x02}, + {0x04, 0x55}, + {0x05, 0x01}, /* H blank */ + {0x06, 0x49}, /* H blank=bb=187 */ + {0x07, 0x00}, /* VB */ + {0x08, 0x5a}, /* VB=E8=232 */ + {0x09, 0x00}, + {0x0a, 0x2c}, + {0x0b, 0x00}, + {0x0c, 0x3c}, + {0x0d, 0x01}, /* win_height */ + {0x0e, 0xf0}, /* 496 */ + {0x0f, 0x02}, /* win_width */ + {0x10, 0x90}, /* 656 */ + {0x11, 0x23}, /* 44FPN abnormal column */ + {0x12, 0x10}, + {0x13, 0x11}, + {0x14, 0x01}, + {0x15, 0x00}, + {0x16, 0xc0}, + {0x17, 0x14}, + {0x18, 0x02}, + {0x19, 0x38}, + {0x1a, 0x11}, + {0x1b, 0x06}, + {0x1c, 0x04}, + {0x1d, 0x00}, + {0x1e, 0xfc}, + {0x1f, 0x09}, + {0x20, 0xb5}, + {0x21, 0x3f}, + {0x22, 0xe6}, + {0x23, 0x32}, + {0x24, 0x2f}, + {0x27, 0x00}, + {0x28, 0x00}, + {0x2a, 0x00}, + {0x2b, 0x00}, + {0x2c, 0x00}, + {0x2d, 0x01}, + {0x2e, 0xf0}, + {0x2f, 0x01}, + {0x25, 0xc0}, + {0x3d, 0xe0}, + {0x3e, 0x45}, + {0x3f, 0x1f}, + {0xc2, 0x17}, + {0x30, 0x22}, + {0x31, 0x23}, + {0x32, 0x02}, + {0x33, 0x03}, + {0x34, 0x04}, + {0x35, 0x05}, + {0x36, 0x06}, + {0x37, 0x07}, + {0x38, 0x0f}, + {0x39, 0x17}, + {0x3a, 0x1f}, + /****ISP****/ + {0xfe, 0x00}, + {0x8a, 0x00}, + {0x8c, 0x07}, + {0x8e, 0x02}, /* luma value not normal */ + {0x90, 0x01}, + {0x94, 0x02}, + {0x95, 0x01}, + {0x96, 0xe0}, /* 480 */ + {0x97, 0x02}, + {0x98, 0x80}, /* 640 */ + /****BLK****/ + {0xfe, 0x00}, + {0x18, 0x02}, + {0x40, 0x22}, + {0x41, 0x01}, + {0x5e, 0x00}, + {0x66, 0x20}, + /****MIPI****/ + {0xfe, 0x03}, + {0x01, 0x83}, + {0x02, 0x11}, + {0x03, 0x96}, + {0x04, 0x01}, + {0x05, 0x00}, + {0x06, 0xa4}, + {0x10, 0x90}, + {0x11, 0x2b}, + {0x12, 0x20}, + {0x13, 0x03}, + {0x15, 0x00}, + {0x21, 0x10}, + {0x22, 0x03}, + {0x23, 0x20}, + {0x24, 0x02}, + {0x25, 0x10}, + {0x26, 0x05}, + {0x21, 0x10}, + {0x29, 0x03}, + {0x2a, 0x0a}, + {0x2b, 0x04}, + {0xfe, 0x00}, + {0xb0, 0x50}, + {0xb6, 0x09}, + {REG_NULL, 0x00}, +}; + +/* + * MCLK=24Mhz + * MIPI_CLOCK=192Mbps + * Actual_window_size=768*576 + * HD=1206 + * VD=663 + * row_time=50.25us,FPS = 30fps; + */ +static const struct regval gc0403_768x576_regs[] = { + /****SYS****/ + {0xfe, 0x80}, + {0xfe, 0x80}, + {0xfe, 0x80}, + {0xf2, 0x00}, /* sync_pad_io_ebi */ + {0xf6, 0x00}, /* up down */ + {0xfc, 0xc6}, + {0xf7, 0x19}, /* pll enable */ + {0xf8, 0x01}, /* Pll mode 2 */ + {0xf9, 0x3e}, /* [0] pll enable solve IOVDD large current problem */ + {0xfe, 0x03}, + {0x06, 0x80}, + {0x06, 0x00}, + {0xfe, 0x00}, + {0xf9, 0x2e}, + {0xfe, 0x00}, + {0xfa, 0x00}, /* div */ + {0xfe, 0x00}, + /**ANALOG&CISCTL**/ + {0x03, 0x02}, + {0x04, 0x55}, + {0x05, 0x00}, /* H blank */ + {0x06, 0xbb}, /* H blank=bb=187 */ + {0x07, 0x00}, /* VB */ + {0x08, 0x46}, /* VB=E8=232 */ + {0x0c, 0x04}, + {0x0d, 0x02}, /* win_height */ + {0x0e, 0x48}, /* 584 */ + {0x0f, 0x03}, /* win_width */ + {0x10, 0x08}, /* 776 */ + {0x11, 0x23}, /* 44FPN abnormal column */ + {0x12, 0x10}, + {0x13, 0x11}, + {0x14, 0x01}, + {0x15, 0x00}, + {0x16, 0xc0}, + {0x17, 0x14}, + {0x18, 0x02}, + {0x19, 0x38}, + {0x1a, 0x11}, + {0x1b, 0x06}, + {0x1c, 0x04}, + {0x1d, 0x00}, + {0x1e, 0xfc}, + {0x1f, 0x09}, + {0x20, 0xb5}, + {0x21, 0x3f}, + {0x22, 0xe6}, + {0x23, 0x32}, + {0x24, 0x2f}, + {0x27, 0x00}, + {0x28, 0x00}, + {0x2a, 0x00}, + {0x2b, 0x00}, + {0x2c, 0x00}, + {0x2d, 0x01}, + {0x2e, 0xf0}, + {0x2f, 0x01}, + {0x25, 0xc0}, + {0x3d, 0xe0}, + {0x3e, 0x45}, + {0x3f, 0x1f}, + {0xc2, 0x17}, + {0x30, 0x22}, + {0x31, 0x23}, + {0x32, 0x02}, + {0x33, 0x03}, + {0x34, 0x04}, + {0x35, 0x05}, + {0x36, 0x06}, + {0x37, 0x07}, + {0x38, 0x0f}, + {0x39, 0x17}, + {0x3a, 0x1f}, + /****ISP****/ + {0xfe, 0x00}, + {0x8a, 0x00}, + {0x8c, 0x07}, + {0x8e, 0x02}, /* luma value not normal */ + {0x90, 0x01}, + {0x94, 0x02}, + {0x95, 0x02}, + {0x96, 0x40}, /* 576 */ + {0x97, 0x03}, + {0x98, 0x00}, /* 768 */ + /****BLK****/ + {0xfe, 0x00}, + {0x18, 0x02}, + {0x40, 0x22}, + {0x41, 0x01}, + {0x5e, 0x00}, + {0x66, 0x20}, + /****MIPI****/ + {0xfe, 0x03}, + {0x01, 0x83}, + {0x02, 0x11}, + {0x03, 0x96}, + {0x04, 0x01}, + {0x05, 0x00}, + {0x06, 0xa4}, + {0x10, 0x80}, + {0x11, 0x2b}, + {0x12, 0xc0}, + {0x13, 0x03}, + {0x15, 0x00}, + {0x21, 0x10}, + {0x22, 0x03}, + {0x23, 0x20}, + {0x24, 0x02}, + {0x25, 0x10}, + {0x26, 0x05}, + {0x21, 0x10}, + {0x29, 0x01}, + {0x2a, 0x0a}, + {0x2b, 0x04}, + {0xfe, 0x00}, + {0xb0, 0x50}, + {0xb6, 0x01}, + {REG_NULL, 0x00}, +}; + +static const struct gc0403_mode supported_modes[] = { + { + .width = 640, + .height = 480, + .max_fps = 30, + .exp_def = 500, + .hts_def = 1362, + .vts_def = 586, + .reg_list = gc0403_vga_regs, + }, + { + .width = 768, + .height = 576, + .max_fps = 30, + .exp_def = 500, + .hts_def = 1206, + .vts_def = 663, + .reg_list = gc0403_768x576_regs, + } +}; + +/* sensor register write */ +static int gc0403_write_reg(struct i2c_client *client, u8 reg, u8 val) +{ + struct i2c_msg msg; + u8 buf[2]; + int ret; + + buf[0] = reg & 0xFF; + buf[1] = val; + + msg.addr = client->addr; + msg.flags = client->flags; + msg.buf = buf; + msg.len = sizeof(buf); + + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret >= 0) + return 0; + + dev_err(&client->dev, + "gc0403 write reg(0x%x val:0x%x) failed !\n", reg, val); + + return ret; +} + +static int gc0403_write_array(struct i2c_client *client, + const struct regval *regs) +{ + int i, ret = 0; + + i = 0; + while (regs[i].addr != REG_NULL) { + ret = gc0403_write_reg(client, regs[i].addr, regs[i].val); + if (ret) { + dev_err(&client->dev, "%s failed !\n", __func__); + break; + } + i++; + } + + return ret; +} + +/* sensor register read */ +static int gc0403_read_reg(struct i2c_client *client, u8 reg, u8 *val) +{ + struct i2c_msg msg[2]; + u8 buf[1]; + int ret; + + buf[0] = reg & 0xFF; + + msg[0].addr = client->addr; + msg[0].flags = client->flags; + msg[0].buf = buf; + msg[0].len = sizeof(buf); + + msg[1].addr = client->addr; + msg[1].flags = client->flags | I2C_M_RD; + msg[1].buf = buf; + msg[1].len = 1; + + ret = i2c_transfer(client->adapter, msg, 2); + if (ret >= 0) { + *val = buf[0]; + return 0; + } + + dev_err(&client->dev, + "gc0403 read reg(0x%x val:0x%x) failed !\n", reg, *val); + + return ret; +} + +static int gc0403_get_reso_dist(const struct gc0403_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); +} + +static const struct gc0403_mode * +gc0403_find_best_fit(struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *framefmt = &fmt->format; + int dist; + int cur_best_fit = 0; + int cur_best_fit_dist = -1; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { + dist = gc0403_get_reso_dist(&supported_modes[i], framefmt); + if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { + cur_best_fit_dist = dist; + cur_best_fit = i; + } + } + + return &supported_modes[cur_best_fit]; +} + +static int gc0403_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct gc0403 *gc0403 = to_gc0403(sd); + const struct gc0403_mode *mode; + + mutex_lock(&gc0403->mutex); + + mode = gc0403_find_best_fit(fmt); + fmt->format.code = MEDIA_BUS_FMT_SRGGB10_1X10; + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; +#else + mutex_unlock(&gc0403->mutex); + return -ENOTTY; +#endif + } else { + gc0403->cur_mode = mode; + } + + mutex_unlock(&gc0403->mutex); + + return 0; +} + +static int gc0403_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct gc0403 *gc0403 = to_gc0403(sd); + const struct gc0403_mode *mode = gc0403->cur_mode; + + mutex_lock(&gc0403->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); +#else + mutex_unlock(&gc0403->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = MEDIA_BUS_FMT_SRGGB10_1X10; + fmt->format.field = V4L2_FIELD_NONE; + } + mutex_unlock(&gc0403->mutex); + + return 0; +} + +static int gc0403_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index != 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_SRGGB10_1X10; + + return 0; +} + +static int gc0403_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fse->code != MEDIA_BUS_FMT_SRGGB10_1X10) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = supported_modes[fse->index].width; + fse->max_height = supported_modes[fse->index].height; + fse->min_height = supported_modes[fse->index].height; + + return 0; +} + +static int gc0403_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct gc0403 *gc0403 = to_gc0403(sd); + const struct gc0403_mode *mode = gc0403->cur_mode; + + mutex_lock(&gc0403->mutex); + fi->interval.numerator = 10000; + fi->interval.denominator = mode->max_fps * 10000; + mutex_unlock(&gc0403->mutex); + + return 0; +} + +static void gc0403_get_module_inf(struct gc0403 *gc0403, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strlcpy(inf->base.sensor, GC0403_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, gc0403->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, gc0403->len_name, sizeof(inf->base.lens)); +} + +static long gc0403_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct gc0403 *gc0403 = to_gc0403(sd); + long ret = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + gc0403_get_module_inf(gc0403, (struct rkmodule_inf *)arg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long gc0403_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_awb_cfg *cfg; + long ret; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = gc0403_ioctl(sd, cmd, inf); + if (!ret) { + ret = copy_to_user(up, inf, sizeof(*inf)); + if (ret) { + ret = -EFAULT; + return ret; + } + } + kfree(inf); + break; + case RKMODULE_AWB_CFG: + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(cfg, up, sizeof(*cfg)); + if (!ret) + ret = gc0403_ioctl(sd, cmd, cfg); + else + ret = -EFAULT; + kfree(cfg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + +static int __gc0403_start_stream(struct gc0403 *gc0403) +{ + int ret = 0; + + /* In case these controls are set before streaming */ + mutex_unlock(&gc0403->mutex); + ret = v4l2_ctrl_handler_setup(&gc0403->ctrl_handler); + mutex_lock(&gc0403->mutex); + if (ret) + return ret; + ret = gc0403_write_reg(gc0403->client, PAGE_SELECT_REG, 0x03); + ret |= gc0403_write_reg(gc0403->client, GC0403_REG_MIPI_EN, 0x90); + ret |= gc0403_write_reg(gc0403->client, PAGE_SELECT_REG, 0x00); + + return ret; +} + +static int __gc0403_stop_stream(struct gc0403 *gc0403) +{ + int ret = 0; + + ret = gc0403_write_reg(gc0403->client, PAGE_SELECT_REG, 0x03); + ret |= gc0403_write_reg(gc0403->client, GC0403_REG_MIPI_EN, 0x80); + ret |= gc0403_write_reg(gc0403->client, PAGE_SELECT_REG, 0x00); + + return ret; +} + +static int __gc0403_power_on(struct gc0403 *gc0403); +static void __gc0403_power_off(struct gc0403 *gc0403); + +static int gc0403_s_power(struct v4l2_subdev *sd, int on) +{ + struct gc0403 *gc0403 = to_gc0403(sd); + int ret = 0; + + mutex_lock(&gc0403->mutex); + + on = !!on; + if (on) + ret = pm_runtime_get_sync(&gc0403->client->dev); + else + ret = pm_runtime_put(&gc0403->client->dev); + + mutex_unlock(&gc0403->mutex); + + return ret; +} + +static int gc0403_s_stream(struct v4l2_subdev *sd, int on) +{ + struct gc0403 *gc0403 = to_gc0403(sd); + int ret = 0; + + mutex_lock(&gc0403->mutex); + on = !!on; + if (on == gc0403->streaming) + goto unlock_and_return; + + if (on) { + ret = __gc0403_start_stream(gc0403); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + goto unlock_and_return; + } + } else { + __gc0403_stop_stream(gc0403); + usleep_range(33 * 1000, 35 * 1000); + } + + gc0403->streaming = on; + +unlock_and_return: + mutex_unlock(&gc0403->mutex); + + return ret; +} + +/* Calculate the delay in us by clock rate and clock cycles */ +static int __gc0403_power_on(struct gc0403 *gc0403) +{ + int ret; + struct device *dev = &gc0403->client->dev; + + ret = clk_set_rate(gc0403->xvclk, GC0403_XVCLK_FREQ); + if (ret < 0) { + dev_err(dev, "Failed to set xvclk rate (24MHz)\n"); + return ret; + } + if (clk_get_rate(gc0403->xvclk) != GC0403_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched,modes are based on 24MHz\n"); + ret = clk_prepare_enable(gc0403->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + + ret = regulator_bulk_enable(GC0403_NUM_SUPPLIES, gc0403->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + + if (!IS_ERR(gc0403->pwdn_gpio)) + gpiod_set_value_cansleep(gc0403->pwdn_gpio, 0); + /* here usleep at least 10~15ms,will better */ + usleep_range(10 * 1000, 15 * 1000); + + ret = gc0403_write_array(gc0403->client, gc0403->cur_mode->reg_list); + if (ret) + return ret; + usleep_range(10 * 1000, 20 * 1000); + + return 0; + +disable_clk: + clk_disable_unprepare(gc0403->xvclk); + + return ret; +} + +static void __gc0403_power_off(struct gc0403 *gc0403) +{ + if (!IS_ERR(gc0403->pwdn_gpio)) + gpiod_set_value_cansleep(gc0403->pwdn_gpio, 1); + clk_disable_unprepare(gc0403->xvclk); + regulator_bulk_disable(GC0403_NUM_SUPPLIES, gc0403->supplies); +} + +static int gc0403_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc0403 *gc0403 = to_gc0403(sd); + int ret; + + ret = __gc0403_power_on(gc0403); + if (ret) + return ret; + + if (gc0403->streaming) { + ret = gc0403_s_stream(sd, 1); + if (ret) + __gc0403_power_off(gc0403); + } + + return ret; +} + +static int gc0403_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc0403 *gc0403 = to_gc0403(sd); + + __gc0403_power_off(gc0403); + + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int gc0403_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct gc0403 *gc0403 = to_gc0403(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + const struct gc0403_mode *def_mode = &supported_modes[1]; + + mutex_lock(&gc0403->mutex); + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = MEDIA_BUS_FMT_SRGGB10_1X10; + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&gc0403->mutex); + /* No crop or compose */ + + return 0; +} +#endif + +static const struct dev_pm_ops gc0403_pm_ops = { + SET_RUNTIME_PM_OPS(gc0403_runtime_suspend, + gc0403_runtime_resume, NULL) +}; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops gc0403_internal_ops = { + .open = gc0403_open, +}; +#endif + +static struct v4l2_subdev_core_ops gc0403_core_ops = { + .s_power = gc0403_s_power, + .ioctl = gc0403_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = gc0403_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops gc0403_video_ops = { + .s_stream = gc0403_s_stream, + .g_frame_interval = gc0403_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops gc0403_pad_ops = { + .enum_mbus_code = gc0403_enum_mbus_code, + .enum_frame_size = gc0403_enum_frame_sizes, + .get_fmt = gc0403_get_fmt, + .set_fmt = gc0403_set_fmt, +}; + +static const struct v4l2_subdev_ops gc0403_subdev_ops = { + .core = &gc0403_core_ops, + .video = &gc0403_video_ops, + .pad = &gc0403_pad_ops, +}; + +static int gc0403_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gc0403 *gc0403 = container_of(ctrl->handler, + struct gc0403, ctrl_handler); + struct i2c_client *client = gc0403->client; + int ret = 0; + int analog_gain_table[] = {100, 142, 250, 354, 490, 691, 970, + 1363, 1945, 2704, 3889}; + int table_cnt = 11; + int analog_gain_reg_value = 0x00; + int digital_gain_reg_value = 0x00; + int total_gain = 0; + int analog_gain = 0; + int i = 0; + + if (pm_runtime_get(&client->dev) <= 0) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + dev_dbg(&client->dev, + "gc0403: V4L2_CID_EXPOSURE exp val = 0x%x\n", + ctrl->val); + /* 4 least significant bits of expsoure are fractional part */ + ret = gc0403_write_reg(gc0403->client, GC0403_REG_EXP_H, + (ctrl->val >> 8) & 0x1f); + ret |= gc0403_write_reg(gc0403->client, GC0403_REG_EXP_L, + ctrl->val & 0xff); + break; + case V4L2_CID_ANALOGUE_GAIN: + case V4L2_CID_DIGITAL_GAIN: + total_gain = ctrl->val; + for (i = 0; i < table_cnt; i++) { + if (total_gain < analog_gain_table[i]) + break; + } + + i = i - 1; + if (i < 0) + i = 0; + + analog_gain = analog_gain_table[i]; + analog_gain_reg_value = i; + digital_gain_reg_value = total_gain * 64 / analog_gain; + + if (analog_gain_reg_value < GC0403_ANALOG_GAIN_MIN) + analog_gain_reg_value = GC0403_ANALOG_GAIN_MIN; + if (analog_gain_reg_value > GC0403_ANALOG_GAIN_MAX) + analog_gain_reg_value = GC0403_ANALOG_GAIN_MAX; + + if (digital_gain_reg_value < GC0403_DIGI_GAIN_MIN) + digital_gain_reg_value = GC0403_DIGI_GAIN_MIN; + if (digital_gain_reg_value > GC0403_DIGI_GAIN_MAX) + digital_gain_reg_value = GC0403_DIGI_GAIN_MAX; + + ret = gc0403_write_reg(gc0403->client, + GC0403_REG_AGAIN, + GC0403_FETCH_ANALOG_GAIN + (analog_gain_reg_value)); + ret |= gc0403_write_reg(gc0403->client, + GC0403_REG_DGAIN_H, + GC0403_FETCH_DIGITAL_GAIN_HIGH + (digital_gain_reg_value)); + ret |= gc0403_write_reg(gc0403->client, + GC0403_REG_DGAIN_L, + GC0403_FETCH_DIGITAL_GAIN_LOW + (digital_gain_reg_value)); + + dev_dbg(&client->dev, "gc0403: gain: %d,a: %d,d: %d\n", + total_gain, analog_gain_reg_value, + digital_gain_reg_value); + break; + case V4L2_CID_VBLANK: + ret = gc0403_write_reg(gc0403->client, GC0403_REG_VBLK_H, + (ctrl->val) >> 8); + ret |= gc0403_write_reg(gc0403->client, GC0403_REG_VBLK_L, + (ctrl->val) & 0xff); + break; + default: + dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", + __func__, ctrl->id, ctrl->val); + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops gc0403_ctrl_ops = { + .s_ctrl = gc0403_set_ctrl, +}; + +static int gc0403_initialize_controls(struct gc0403 *gc0403) +{ + const struct gc0403_mode *mode; + struct v4l2_ctrl_handler *handler; + struct v4l2_ctrl *ctrl; + s64 vblank_def; + u32 h_blank; + int ret; + + handler = &gc0403->ctrl_handler; + mode = gc0403->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 5); + if (ret) + return ret; + handler->lock = &gc0403->mutex; + + ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, + 0, 0, link_freq_menu_items); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, + 0, GC0403_PIXEL_RATE, 1, GC0403_PIXEL_RATE); + + h_blank = mode->hts_def - mode->width; + gc0403->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (gc0403->hblank) + gc0403->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = mode->vts_def - mode->height; + gc0403->vblank = v4l2_ctrl_new_std(handler, &gc0403_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, vblank_def, + 1, vblank_def); + + gc0403->exposure = v4l2_ctrl_new_std(handler, &gc0403_ctrl_ops, + V4L2_CID_EXPOSURE, + GC0403_EXPOSURE_MIN, GC0403_EXPOSURE_MAX, + 1, mode->exp_def); + + /* Anolog gain */ + gc0403->anal_gain = v4l2_ctrl_new_std(handler, &gc0403_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, GC0403_TOTAL_GAIN_MIN, + GC0403_TOTAL_GAIN_MAX, GC0403_TOTAL_GAIN_STEP, + GC0403_TOTAL_GAIN_MIN); + + /* Digital gain */ + gc0403->digi_gain = v4l2_ctrl_new_std(handler, &gc0403_ctrl_ops, + V4L2_CID_DIGITAL_GAIN, GC0403_TOTAL_GAIN_MIN, + GC0403_TOTAL_GAIN_MAX, GC0403_TOTAL_GAIN_STEP, + GC0403_TOTAL_GAIN_MIN); + + if (handler->error) { + ret = handler->error; + dev_err(&gc0403->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + gc0403->subdev.ctrl_handler = handler; + + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +static int gc0403_check_sensor_id(struct gc0403 *gc0403, + struct i2c_client *client) +{ + struct device *dev = &gc0403->client->dev; + u8 pid = 0, ver = 0; + u16 id = 0; + int ret = 0; + + /* Check sensor revision */ + ret = gc0403_read_reg(client, GC0403_REG_CHIP_ID_H, &pid); + ret |= gc0403_read_reg(client, GC0403_REG_CHIP_ID_L, &ver); + if (ret) { + dev_err(&client->dev, "gc0403_read_reg failed (%d)\n", ret); + return ret; + } + + id = SENSOR_ID(pid, ver); + if (id != GC0403_CHIP_ID) { + dev_err(&client->dev, + "Sensor detection failed (%04X,%d)\n", + id, ret); + return -ENODEV; + } + + dev_info(dev, "Detected GC%04x sensor\n", id); + + return 0; +} + +static int gc0403_configure_regulators(struct gc0403 *gc0403) +{ + unsigned int i; + + for (i = 0; i < GC0403_NUM_SUPPLIES; i++) + gc0403->supplies[i].supply = gc0403_supply_names[i]; + + return devm_regulator_bulk_get(&gc0403->client->dev, + GC0403_NUM_SUPPLIES, + gc0403->supplies); +} + +static int gc0403_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + /* add a dev_node */ + struct device_node *node = dev->of_node; + struct gc0403 *gc0403; + struct v4l2_subdev *sd; + char facing[2]; + int ret; + + /* add info */ + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + gc0403 = devm_kzalloc(dev, sizeof(*gc0403), GFP_KERNEL); + if (!gc0403) + return -ENOMEM; + + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &gc0403->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &gc0403->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &gc0403->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &gc0403->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + + gc0403->client = client; + gc0403->cur_mode = &supported_modes[1]; + + gc0403->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(gc0403->xvclk)) { + dev_err(dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + gc0403->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(gc0403->pwdn_gpio)) + dev_warn(dev, "Failed to get pwdn-gpios\n"); + + ret = gc0403_configure_regulators(gc0403); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + + mutex_init(&gc0403->mutex); + + sd = &gc0403->subdev; + v4l2_i2c_subdev_init(sd, client, &gc0403_subdev_ops); + ret = gc0403_initialize_controls(gc0403); + if (ret) + goto err_destroy_mutex; + + ret = __gc0403_power_on(gc0403); + if (ret) + goto err_free_handler; + + ret = gc0403_check_sensor_id(gc0403, client); + if (ret) + goto err_power_off; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &gc0403_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + gc0403->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &gc0403->pad); + if (ret < 0) + goto err_power_off; +#endif + + memset(facing, 0, sizeof(facing)); + if (strcmp(gc0403->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + gc0403->module_index, facing, + GC0403_NAME, dev_name(sd->dev)); + + ret = v4l2_async_register_subdev_sensor_common(sd); + if (ret) { + dev_err(dev, "v4l2 async register subdev failed\n"); + goto err_clean_entity; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; + +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif +err_power_off: + __gc0403_power_off(gc0403); +err_free_handler: + v4l2_ctrl_handler_free(&gc0403->ctrl_handler); +err_destroy_mutex: + mutex_destroy(&gc0403->mutex); + + return ret; +} + +static int gc0403_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc0403 *gc0403 = to_gc0403(sd); + + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + v4l2_ctrl_handler_free(&gc0403->ctrl_handler); + mutex_destroy(&gc0403->mutex); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __gc0403_power_off(gc0403); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id gc0403_of_match[] = { + { .compatible = "galaxycore,gc0403" }, + {}, +}; +MODULE_DEVICE_TABLE(of, gc0403_of_match); +#endif + +static const struct i2c_device_id gc0403_match_id[] = { + { "gc0403", 0 }, + { }, +}; + +static struct i2c_driver gc0403_i2c_driver = { + .driver = { + .name = "gc0403", + .pm = &gc0403_pm_ops, + .of_match_table = of_match_ptr(gc0403_of_match), + }, + .probe = &gc0403_probe, + .remove = &gc0403_remove, + .id_table = gc0403_match_id, +}; + +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&gc0403_i2c_driver); +} + +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&gc0403_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_DESCRIPTION("Galaxycore gc0403 sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/gc2035.c b/drivers/media/i2c/gc2035.c index ebd343f45fff..5922c456decf 100644 --- a/drivers/media/i2c/gc2035.c +++ b/drivers/media/i2c/gc2035.c @@ -22,7 +22,8 @@ #include #include #include - +#include +#include #include #include #include @@ -33,6 +34,7 @@ #include #include +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x0) #define DRIVER_NAME "gc2035" #define GC2035_PIXEL_RATE (70 * 1000 * 1000) @@ -98,6 +100,10 @@ struct gc2035 { struct v4l2_ctrl *link_frequency; const struct gc2035_framesize *frame_size; int streaming; + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; }; static const struct sensor_register gc2035_init_regs[] = { @@ -1190,10 +1196,84 @@ unlock: return ret; } +static void gc2035_get_module_inf(struct gc2035 *gc2035, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strlcpy(inf->base.sensor, DRIVER_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, gc2035->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, gc2035->len_name, sizeof(inf->base.lens)); +} + +static long gc2035_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct gc2035 *gc2035 = to_gc2035(sd); + long ret = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + gc2035_get_module_inf(gc2035, (struct rkmodule_inf *)arg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long gc2035_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_awb_cfg *cfg; + long ret; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = gc2035_ioctl(sd, cmd, inf); + if (!ret) + ret = copy_to_user(up, inf, sizeof(*inf)); + kfree(inf); + break; + case RKMODULE_AWB_CFG: + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(cfg, up, sizeof(*cfg)); + if (!ret) + ret = gc2035_ioctl(sd, cmd, cfg); + kfree(cfg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + static const struct v4l2_subdev_core_ops gc2035_subdev_core_ops = { .log_status = v4l2_ctrl_subdev_log_status, .subscribe_event = v4l2_ctrl_subdev_subscribe_event, .unsubscribe_event = v4l2_event_subdev_unsubscribe, + .ioctl = gc2035_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = gc2035_compat_ioctl32, +#endif }; static const struct v4l2_subdev_video_ops gc2035_subdev_video_ops = { @@ -1334,14 +1414,35 @@ static int gc2035_parse_of(struct gc2035 *gc2035) static int gc2035_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; struct v4l2_subdev *sd; struct gc2035 *gc2035; + char facing[2]; int ret; + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + gc2035 = devm_kzalloc(&client->dev, sizeof(*gc2035), GFP_KERNEL); if (!gc2035) return -ENOMEM; + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &gc2035->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &gc2035->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &gc2035->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &gc2035->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + gc2035->client = client; gc2035->xvclk = devm_clk_get(&client->dev, "xvclk"); if (IS_ERR(gc2035->xvclk)) { @@ -1387,8 +1488,8 @@ static int gc2035_probe(struct i2c_client *client, #if defined(CONFIG_MEDIA_CONTROLLER) gc2035->pad.flags = MEDIA_PAD_FL_SOURCE; - sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; - ret = media_entity_init(&sd->entity, 1, &gc2035->pad, 0); + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &gc2035->pad); if (ret < 0) { v4l2_ctrl_handler_free(&gc2035->ctrls); return ret; @@ -1407,7 +1508,16 @@ static int gc2035_probe(struct i2c_client *client, if (ret < 0) goto error; - ret = v4l2_async_register_subdev(&gc2035->sd); + memset(facing, 0, sizeof(facing)); + if (strcmp(gc2035->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + gc2035->module_index, facing, + DRIVER_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor_common(sd); if (ret) goto error; diff --git a/drivers/media/i2c/gc2155.c b/drivers/media/i2c/gc2155.c index 927c9462ac90..d85c12359305 100644 --- a/drivers/media/i2c/gc2155.c +++ b/drivers/media/i2c/gc2155.c @@ -3,6 +3,9 @@ * gc2155 driver * * Copyright (C) 2018 Fuzhou Rockchip Electronics Co., Ltd. + * + * V0.0X01.0X01 add poweron function. + * V0.0X01.0X02 fix mclk issue when probe multiple camera. */ #include @@ -13,11 +16,16 @@ #include #include #include +#include +#include +#include #include #include #include #include +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x02) + #define REG_CHIP_ID_H 0xf0 #define REG_CHIP_ID_L 0xf1 #define CHIP_ID_H 0x21 @@ -27,6 +35,8 @@ #define GC2155_XVCLK_FREQ 24000000 +#define GC2155_NAME "gc2155" + static const char * const gc2155_supply_names[] = { "avdd", "dovdd", @@ -54,11 +64,16 @@ struct gc2155 { struct regulator_bulk_data supplies[GC2155_NUM_SUPPLIES]; bool streaming; + bool power_on; struct mutex mutex; /* lock to serialize v4l2 callback */ struct v4l2_subdev subdev; struct media_pad pad; const struct gc2155_mode *cur_mode; + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; }; #define to_gc2155(sd) container_of(sd, struct gc2155, subdev) @@ -1056,12 +1071,15 @@ static int __gc2155_power_on(struct gc2155 *gc2155) return ret; } - if (!IS_ERR(gc2155->xvclk)) { - ret = clk_prepare_enable(gc2155->xvclk); - if (ret < 0) { - dev_err(dev, "Failed to enable xvclk\n"); - return ret; - } + ret = clk_set_rate(gc2155->xvclk, GC2155_XVCLK_FREQ); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate (24MHz)\n"); + if (clk_get_rate(gc2155->xvclk) != GC2155_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); + ret = clk_prepare_enable(gc2155->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; } if (!IS_ERR(gc2155->pwdn_gpio)) @@ -1086,6 +1104,76 @@ static void __gc2155_power_off(struct gc2155 *gc2155) regulator_bulk_disable(GC2155_NUM_SUPPLIES, gc2155->supplies); } +static void gc2155_get_module_inf(struct gc2155 *gc2155, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strlcpy(inf->base.sensor, GC2155_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, gc2155->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, gc2155->len_name, sizeof(inf->base.lens)); +} + +static long gc2155_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct gc2155 *gc2155 = to_gc2155(sd); + long ret = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + gc2155_get_module_inf(gc2155, (struct rkmodule_inf *)arg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long gc2155_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_awb_cfg *cfg; + long ret; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = gc2155_ioctl(sd, cmd, inf); + if (!ret) + ret = copy_to_user(up, inf, sizeof(*inf)); + kfree(inf); + break; + case RKMODULE_AWB_CFG: + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(cfg, up, sizeof(*cfg)); + if (!ret) + ret = gc2155_ioctl(sd, cmd, cfg); + kfree(cfg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + static int gc2155_s_stream(struct v4l2_subdev *sd, int on) { struct gc2155 *gc2155 = to_gc2155(sd); @@ -1105,12 +1193,6 @@ static int gc2155_s_stream(struct v4l2_subdev *sd, int on) goto unlock_and_return; } - ret = gc2155_write_array(gc2155->client, gc2155_global_regs); - if (ret) { - pm_runtime_put(&client->dev); - goto unlock_and_return; - } - ret = gc2155_write_array(gc2155->client, gc2155->cur_mode->reg_list); if (ret) { @@ -1130,6 +1212,44 @@ unlock_and_return: return ret; } +static int gc2155_s_power(struct v4l2_subdev *sd, int on) +{ + struct gc2155 *gc2155 = to_gc2155(sd); + struct i2c_client *client = gc2155->client; + int ret = 0; + + mutex_lock(&gc2155->mutex); + + /* If the power state is not modified - no work to do. */ + if (gc2155->power_on == !!on) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = gc2155_write_array(gc2155->client, gc2155_global_regs); + if (ret) { + v4l2_err(sd, "could not set init registers\n"); + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + gc2155->power_on = true; + } else { + pm_runtime_put(&client->dev); + gc2155->power_on = false; + } + +unlock_and_return: + mutex_unlock(&gc2155->mutex); + + return ret; +} + #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API static int gc2155_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { @@ -1177,6 +1297,14 @@ static const struct dev_pm_ops gc2155_pm_ops = { gc2155_runtime_resume, NULL) }; +static const struct v4l2_subdev_core_ops gc2155_core_ops = { + .s_power = gc2155_s_power, + .ioctl = gc2155_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = gc2155_compat_ioctl32, +#endif +}; + static const struct v4l2_subdev_video_ops gc2155_video_ops = { .s_stream = gc2155_s_stream, }; @@ -1189,6 +1317,7 @@ static const struct v4l2_subdev_pad_ops gc2155_pad_ops = { }; static const struct v4l2_subdev_ops gc2155_subdev_ops = { + .core = &gc2155_core_ops, .video = &gc2155_video_ops, .pad = &gc2155_pad_ops, }; @@ -1235,13 +1364,34 @@ static int gc2155_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; + struct device_node *node = dev->of_node; struct gc2155 *gc2155; + struct v4l2_subdev *sd; + char facing[2]; int ret; + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + gc2155 = devm_kzalloc(dev, sizeof(*gc2155), GFP_KERNEL); if (!gc2155) return -ENOMEM; + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &gc2155->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &gc2155->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &gc2155->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &gc2155->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + gc2155->client = client; gc2155->cur_mode = &supported_modes[0]; @@ -1250,13 +1400,6 @@ static int gc2155_probe(struct i2c_client *client, dev_err(dev, "Failed to get xvclk\n"); return -EINVAL; } - ret = clk_set_rate(gc2155->xvclk, GC2155_XVCLK_FREQ); - if (ret < 0) { - dev_err(dev, "Failed to set xvclk rate (24MHz)\n"); - return ret; - } - if (clk_get_rate(gc2155->xvclk) != GC2155_XVCLK_FREQ) - dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); gc2155->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(gc2155->reset_gpio)) @@ -1289,13 +1432,23 @@ static int gc2155_probe(struct i2c_client *client, #endif #if defined(CONFIG_MEDIA_CONTROLLER) gc2155->pad.flags = MEDIA_PAD_FL_SOURCE; - gc2155->subdev.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; - ret = media_entity_init(&gc2155->subdev.entity, 1, &gc2155->pad, 0); + gc2155->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&gc2155->subdev.entity, 1, &gc2155->pad); if (ret < 0) goto err_power_off; #endif - ret = v4l2_async_register_subdev(&gc2155->subdev); + sd = &gc2155->subdev; + memset(facing, 0, sizeof(facing)); + if (strcmp(gc2155->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + gc2155->module_index, facing, + GC2155_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor_common(sd); if (ret) { dev_err(dev, "v4l2 async register subdev failed\n"); goto err_clean_entity; @@ -1353,7 +1506,7 @@ static const struct i2c_device_id gc2155_match_id[] = { static struct i2c_driver gc2155_i2c_driver = { .driver = { - .name = "gc2155", + .name = GC2155_NAME, .pm = &gc2155_pm_ops, .of_match_table = of_match_ptr(gc2155_of_match), }, diff --git a/drivers/media/i2c/gc2355.c b/drivers/media/i2c/gc2355.c index 1594d1b66d9a..23b4f3769eb8 100644 --- a/drivers/media/i2c/gc2355.c +++ b/drivers/media/i2c/gc2355.c @@ -3,6 +3,7 @@ * gc2355 driver * * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd. + * V0.0X01.0X02 fix mclk issue when probe multiple camera. */ #define DEBUG 1 #include @@ -14,12 +15,17 @@ #include #include #include +#include +#include +#include #include #include #include #include #include +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x2) + #ifndef V4L2_CID_DIGITAL_GAIN #define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN #endif @@ -73,6 +79,8 @@ #define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default" #define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep" +#define GC2355_NAME "gc2355" + static const char * const gc2355_supply_names[] = { "avdd", /* Analog power */ "dovdd", /* Digital I/O power */ @@ -119,6 +127,10 @@ struct gc2355 { struct mutex mutex; bool streaming; const struct gc2355_mode *cur_mode; + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; }; #define to_gc2355(sd) container_of(sd, struct gc2355, subdev) @@ -509,6 +521,76 @@ static int gc2355_g_frame_interval(struct v4l2_subdev *sd, return 0; } +static void gc2355_get_module_inf(struct gc2355 *gc2355, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strlcpy(inf->base.sensor, GC2355_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, gc2355->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, gc2355->len_name, sizeof(inf->base.lens)); +} + +static long gc2355_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct gc2355 *gc2355 = to_gc2355(sd); + long ret = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + gc2355_get_module_inf(gc2355, (struct rkmodule_inf *)arg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long gc2355_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_awb_cfg *cfg; + long ret; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = gc2355_ioctl(sd, cmd, inf); + if (!ret) + ret = copy_to_user(up, inf, sizeof(*inf)); + kfree(inf); + break; + case RKMODULE_AWB_CFG: + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(cfg, up, sizeof(*cfg)); + if (!ret) + ret = gc2355_ioctl(sd, cmd, cfg); + kfree(cfg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + static int __gc2355_start_stream(struct gc2355 *gc2355) { int ret; @@ -601,13 +683,16 @@ static int __gc2355_power_on(struct gc2355 *gc2355) if (ret < 0) dev_err(dev, "could not set pins\n"); } - + ret = clk_set_rate(gc2355->xvclk, GC2355_XVCLK_FREQ); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate (24MHz)\n"); + if (clk_get_rate(gc2355->xvclk) != GC2355_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); ret = clk_prepare_enable(gc2355->xvclk); if (ret < 0) { dev_err(dev, "Failed to enable xvclk\n"); return ret; } - if (!IS_ERR(gc2355->reset_gpio)) gpiod_set_value_cansleep(gc2355->reset_gpio, 0); @@ -708,6 +793,13 @@ static const struct v4l2_subdev_internal_ops gc2355_internal_ops = { }; #endif +static const struct v4l2_subdev_core_ops gc2355_core_ops = { + .ioctl = gc2355_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = gc2355_compat_ioctl32, +#endif +}; + static const struct v4l2_subdev_video_ops gc2355_video_ops = { .s_stream = gc2355_s_stream, .g_frame_interval = gc2355_g_frame_interval, @@ -721,6 +813,7 @@ static const struct v4l2_subdev_pad_ops gc2355_pad_ops = { }; static const struct v4l2_subdev_ops gc2355_subdev_ops = { + .core = &gc2355_core_ops, .video = &gc2355_video_ops, .pad = &gc2355_pad_ops, }; @@ -956,14 +1049,34 @@ static int gc2355_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; + struct device_node *node = dev->of_node; struct gc2355 *gc2355; struct v4l2_subdev *sd; + char facing[2]; int ret; + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + gc2355 = devm_kzalloc(dev, sizeof(*gc2355), GFP_KERNEL); if (!gc2355) return -ENOMEM; + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &gc2355->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &gc2355->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &gc2355->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &gc2355->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + gc2355->client = client; gc2355->cur_mode = &supported_modes[0]; @@ -972,13 +1085,6 @@ static int gc2355_probe(struct i2c_client *client, dev_err(dev, "Failed to get xvclk\n"); return -EINVAL; } - ret = clk_set_rate(gc2355->xvclk, GC2355_XVCLK_FREQ); - if (ret < 0) { - dev_err(dev, "Failed to set xvclk rate (24MHz)\n"); - return ret; - } - if (clk_get_rate(gc2355->xvclk) != GC2355_XVCLK_FREQ) - dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); gc2355->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(gc2355->reset_gpio)) @@ -1029,17 +1135,27 @@ static int gc2355_probe(struct i2c_client *client, #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API sd->internal_ops = &gc2355_internal_ops; - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; #endif #if defined(CONFIG_MEDIA_CONTROLLER) gc2355->pad.flags = MEDIA_PAD_FL_SOURCE; - sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; - ret = media_entity_init(&sd->entity, 1, &gc2355->pad, 0); + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &gc2355->pad); if (ret < 0) goto err_power_off; #endif - ret = v4l2_async_register_subdev(sd); + memset(facing, 0, sizeof(facing)); + if (strcmp(gc2355->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + gc2355->module_index, facing, + GC2355_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor_common(sd); if (ret) { dev_err(dev, "v4l2 async register subdev failed\n"); goto err_clean_entity; @@ -1100,7 +1216,7 @@ static const struct i2c_device_id gc2355_match_id[] = { static struct i2c_driver gc2355_i2c_driver = { .driver = { - .name = "gc2355", + .name = GC2355_NAME, .pm = &gc2355_pm_ops, .of_match_table = of_match_ptr(gc2355_of_match), }, diff --git a/drivers/media/i2c/gc2385.c b/drivers/media/i2c/gc2385.c new file mode 100644 index 000000000000..3adfa9b4feb4 --- /dev/null +++ b/drivers/media/i2c/gc2385.c @@ -0,0 +1,1254 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * gc2385 driver + * + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd. + * + * V0.0X01.0X01 add poweron function. + * V0.0X01.0X02 fix mclk issue when probe multiple camera. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x02) + +#ifndef V4L2_CID_DIGITAL_GAIN +#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN +#endif + +#define GC2385_LANES 1 +#define GC2385_BITS_PER_SAMPLE 10 +#define GC2385_LINK_FREQ_MHZ 328000000 +/* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ +#define GC2385_PIXEL_RATE (GC2385_LINK_FREQ_MHZ * 2 * 1 / 10) +#define GC2385_XVCLK_FREQ 24000000 + +#define CHIP_ID 0x2385 +#define GC2385_REG_CHIP_ID_H 0xf0 +#define GC2385_REG_CHIP_ID_L 0xf1 + +#define GC2385_REG_SET_PAGE 0xfe +#define GC2385_SET_PAGE_ONE 0x00 + +#define GC2385_REG_CTRL_MODE 0xed +#define GC2385_MODE_SW_STANDBY 0x00 +#define GC2385_MODE_STREAMING 0x90 + +#define GC2385_REG_EXPOSURE_H 0x03 +#define GC2385_REG_EXPOSURE_L 0x04 +#define GC2385_FETCH_HIGH_BYTE_EXP(VAL) (((VAL) >> 8) & 0x3F) /* 6 Bits */ +#define GC2385_FETCH_LOW_BYTE_EXP(VAL) ((VAL) & 0xFF) /* 8 Bits */ +#define GC2385_EXPOSURE_MIN 4 +#define GC2385_EXPOSURE_STEP 1 +#define GC2385_VTS_MAX 0x1fff + +#define GC2385_REG_AGAIN 0xb6 +#define GC2385_REG_DGAIN_INT 0xb1 +#define GC2385_REG_DGAIN_FRAC 0xb2 +#define GC2385_GAIN_MIN 64 +#define GC2385_GAIN_MAX 1092 +#define GC2385_GAIN_STEP 1 +#define GC2385_GAIN_DEFAULT 64 + +#define GC2385_REG_VTS_H 0x07 +#define GC2385_REG_VTS_L 0x08 + +#define REG_NULL 0xFF + +#define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default" +#define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep" + +#define GC2385_NAME "gc2385" + +static const char * const gc2385_supply_names[] = { + "avdd", /* Analog power */ + "dovdd", /* Digital I/O power */ + "dvdd", /* Digital core power */ +}; + +#define GC2385_NUM_SUPPLIES ARRAY_SIZE(gc2385_supply_names) + +struct regval { + u8 addr; + u8 val; +}; + +struct gc2385_mode { + u32 width; + u32 height; + struct v4l2_fract max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + const struct regval *reg_list; +}; + +struct gc2385 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; + struct regulator_bulk_data supplies[GC2385_NUM_SUPPLIES]; + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_sleep; + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *anal_gain; + struct v4l2_ctrl *digi_gain; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct mutex mutex; + bool streaming; + bool power_on; + const struct gc2385_mode *cur_mode; + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; +}; + +#define to_gc2385(sd) container_of(sd, struct gc2385, subdev) + +/* + * Xclk 24Mhz + */ +static const struct regval gc2385_global_regs[] = { + {REG_NULL, 0x00}, +}; + +/* + * Xclk 24Mhz + * max_framerate 30fps + * mipi_datarate per lane 656Mbps + */ +static const struct regval gc2385_1600x1200_regs[] = { + {0xfe, 0x00}, + {0xfe, 0x00}, + {0xfe, 0x00}, + {0xf2, 0x02}, + {0xf4, 0x03}, + {0xf7, 0x01}, + {0xf8, 0x28}, + {0xf9, 0x02}, + {0xfa, 0x08}, + {0xfc, 0x8e}, + {0xe7, 0xcc}, + {0x88, 0x03}, + {0x03, 0x04}, + {0x04, 0x80}, + {0x05, 0x02}, + {0x06, 0x86}, + {0x07, 0x00}, + {0x08, 0x10}, + {0x09, 0x00}, + {0x0a, 0x04}, + {0x0b, 0x00}, + {0x0c, 0x02}, + {0x17, 0xd4}, + {0x18, 0x02}, + {0x19, 0x17}, + {0x1c, 0x18}, + {0x20, 0x73}, + {0x21, 0x38}, + {0x22, 0xa2}, + {0x29, 0x20}, + {0x2f, 0x14}, + {0x3f, 0x40}, + {0xcd, 0x94}, + {0xce, 0x45}, + {0xd1, 0x0c}, + {0xd7, 0x9b}, + {0xd8, 0x99}, + {0xda, 0x3b}, + {0xd9, 0xb5}, + {0xdb, 0x75}, + {0xe3, 0x1b}, + {0xe4, 0xf8}, + {0x40, 0x22}, + {0x43, 0x07}, + {0x4e, 0x3c}, + {0x4f, 0x00}, + {0x68, 0x00}, + {0xb0, 0x46}, + {0xb1, 0x01}, + {0xb2, 0x00}, + {0xb6, 0x00}, + {0x90, 0x01}, + {0x92, 0x03}, + {0x94, 0x05}, + {0x95, 0x04}, + {0x96, 0xb0}, + {0x97, 0x06}, + {0x98, 0x40}, + {0xfe, 0x00}, + {0xed, 0x00}, + {0xfe, 0x03}, + {0x01, 0x03}, + {0x02, 0x82}, + {0x03, 0xd0}, + {0x04, 0x04}, + {0x05, 0x00}, + {0x06, 0x80}, + {0x11, 0x2b}, + {0x12, 0xd0}, + {0x13, 0x07}, + {0x15, 0x00}, + {0x1b, 0x10}, + {0x1c, 0x10}, + {0x21, 0x08}, + {0x22, 0x05}, + {0x23, 0x13}, + {0x24, 0x02}, + {0x25, 0x13}, + {0x26, 0x06}, + {0x29, 0x06}, + {0x2a, 0x08}, + {0x2b, 0x06}, + {0xfe, 0x00}, + {REG_NULL, 0x00}, +}; + +static const struct gc2385_mode supported_modes[] = { + { + .width = 1600, + .height = 1200, + .max_fps = { + .numerator = 10000, + .denominator = 304472, + }, + .exp_def = 0x0480, + .hts_def = 0x10DC, + .vts_def = 0x04E0, + .reg_list = gc2385_1600x1200_regs, + }, +}; + +static const s64 link_freq_menu_items[] = { + GC2385_LINK_FREQ_MHZ +}; + +/* Write registers up to 4 at a time */ +static int gc2385_write_reg(struct i2c_client *client, u8 reg, u8 val) +{ + struct i2c_msg msg; + u8 buf[2]; + int ret; + + buf[0] = reg & 0xFF; + buf[1] = val; + + msg.addr = client->addr; + msg.flags = client->flags; + msg.buf = buf; + msg.len = sizeof(buf); + + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret >= 0) + return 0; + + dev_err(&client->dev, + "gc2385 write reg(0x%x val:0x%x) failed !\n", reg, val); + + return ret; +} + +static int gc2385_write_array(struct i2c_client *client, + const struct regval *regs) +{ + u32 i = 0; + int ret = 0; + + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) + ret = gc2385_write_reg(client, regs[i].addr, regs[i].val); + + return ret; +} + +/* Read registers up to 4 at a time */ +static int gc2385_read_reg(struct i2c_client *client, u8 reg, u8 *val) +{ + struct i2c_msg msg[2]; + u8 buf[1]; + int ret; + + buf[0] = reg & 0xFF; + + msg[0].addr = client->addr; + msg[0].flags = client->flags; + msg[0].buf = buf; + msg[0].len = sizeof(buf); + + msg[1].addr = client->addr; + msg[1].flags = client->flags | I2C_M_RD; + msg[1].buf = buf; + msg[1].len = 1; + + ret = i2c_transfer(client->adapter, msg, 2); + if (ret >= 0) { + *val = buf[0]; + return 0; + } + + dev_err(&client->dev, + "gc2385 read reg:0x%x failed !\n", reg); + + return ret; +} + +static int gc2385_get_reso_dist(const struct gc2385_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); +} + +static const struct gc2385_mode * +gc2385_find_best_fit(struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *framefmt = &fmt->format; + int dist; + int cur_best_fit = 0; + int cur_best_fit_dist = -1; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { + dist = gc2385_get_reso_dist(&supported_modes[i], framefmt); + if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { + cur_best_fit_dist = dist; + cur_best_fit = i; + } + } + + return &supported_modes[cur_best_fit]; +} + +static int gc2385_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct gc2385 *gc2385 = to_gc2385(sd); + const struct gc2385_mode *mode; + s64 h_blank, vblank_def; + + mutex_lock(&gc2385->mutex); + + mode = gc2385_find_best_fit(fmt); + fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10; + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; +#else + mutex_unlock(&gc2385->mutex); + return -ENOTTY; +#endif + } else { + gc2385->cur_mode = mode; + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(gc2385->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(gc2385->vblank, vblank_def, + GC2385_VTS_MAX - mode->height, + 1, vblank_def); + } + + mutex_unlock(&gc2385->mutex); + + return 0; +} + +static int gc2385_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct gc2385 *gc2385 = to_gc2385(sd); + const struct gc2385_mode *mode = gc2385->cur_mode; + + mutex_lock(&gc2385->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); +#else + mutex_unlock(&gc2385->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10; + fmt->format.field = V4L2_FIELD_NONE; + } + mutex_unlock(&gc2385->mutex); + + return 0; +} + +static int gc2385_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index != 0) + return -EINVAL; + code->code = MEDIA_BUS_FMT_SBGGR10_1X10; + + return 0; +} + +static int gc2385_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fse->code != MEDIA_BUS_FMT_SBGGR10_1X10) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = supported_modes[fse->index].width; + fse->max_height = supported_modes[fse->index].height; + fse->min_height = supported_modes[fse->index].height; + + return 0; +} + +static int gc2385_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct gc2385 *gc2385 = to_gc2385(sd); + const struct gc2385_mode *mode = gc2385->cur_mode; + + mutex_lock(&gc2385->mutex); + fi->interval = mode->max_fps; + mutex_unlock(&gc2385->mutex); + + return 0; +} + +static void gc2385_get_module_inf(struct gc2385 *gc2385, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strlcpy(inf->base.sensor, GC2385_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, gc2385->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, gc2385->len_name, sizeof(inf->base.lens)); +} + +static long gc2385_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct gc2385 *gc2385 = to_gc2385(sd); + long ret = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + gc2385_get_module_inf(gc2385, (struct rkmodule_inf *)arg); + break; + default: + ret = -ENOTTY; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long gc2385_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_awb_cfg *cfg; + long ret; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = gc2385_ioctl(sd, cmd, inf); + if (!ret) + ret = copy_to_user(up, inf, sizeof(*inf)); + kfree(inf); + break; + case RKMODULE_AWB_CFG: + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(cfg, up, sizeof(*cfg)); + if (!ret) + ret = gc2385_ioctl(sd, cmd, cfg); + kfree(cfg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + +static int __gc2385_start_stream(struct gc2385 *gc2385) +{ + int ret; + + ret = gc2385_write_array(gc2385->client, gc2385->cur_mode->reg_list); + if (ret) + return ret; + + /* In case these controls are set before streaming */ + mutex_unlock(&gc2385->mutex); + ret = v4l2_ctrl_handler_setup(&gc2385->ctrl_handler); + mutex_lock(&gc2385->mutex); + if (ret) + return ret; + ret = gc2385_write_reg(gc2385->client, + GC2385_REG_SET_PAGE, + GC2385_SET_PAGE_ONE); + ret |= gc2385_write_reg(gc2385->client, + GC2385_REG_CTRL_MODE, + GC2385_MODE_STREAMING); + return ret; +} + +static int __gc2385_stop_stream(struct gc2385 *gc2385) +{ + int ret; + + ret = gc2385_write_reg(gc2385->client, + GC2385_REG_SET_PAGE, GC2385_SET_PAGE_ONE); + ret |= gc2385_write_reg(gc2385->client, + GC2385_REG_CTRL_MODE, GC2385_MODE_SW_STANDBY); + return ret; +} + +static int gc2385_s_stream(struct v4l2_subdev *sd, int on) +{ + struct gc2385 *gc2385 = to_gc2385(sd); + struct i2c_client *client = gc2385->client; + int ret = 0; + + mutex_lock(&gc2385->mutex); + on = !!on; + if (on == gc2385->streaming) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = __gc2385_start_stream(gc2385); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + __gc2385_stop_stream(gc2385); + pm_runtime_put(&client->dev); + } + + gc2385->streaming = on; + +unlock_and_return: + mutex_unlock(&gc2385->mutex); + + return ret; +} + +static int gc2385_s_power(struct v4l2_subdev *sd, int on) +{ + struct gc2385 *gc2385 = to_gc2385(sd); + struct i2c_client *client = gc2385->client; + int ret = 0; + + mutex_lock(&gc2385->mutex); + + /* If the power state is not modified - no work to do. */ + if (gc2385->power_on == !!on) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = gc2385_write_array(gc2385->client, gc2385_global_regs); + if (ret) { + v4l2_err(sd, "could not set init registers\n"); + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + gc2385->power_on = true; + } else { + pm_runtime_put(&client->dev); + gc2385->power_on = false; + } + +unlock_and_return: + mutex_unlock(&gc2385->mutex); + + return ret; +} + +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 gc2385_cal_delay(u32 cycles) +{ + return DIV_ROUND_UP(cycles, GC2385_XVCLK_FREQ / 1000 / 1000); +} + +static int __gc2385_power_on(struct gc2385 *gc2385) +{ + int ret; + u32 delay_us; + struct device *dev = &gc2385->client->dev; + + if (!IS_ERR_OR_NULL(gc2385->pins_default)) { + ret = pinctrl_select_state(gc2385->pinctrl, + gc2385->pins_default); + if (ret < 0) + dev_err(dev, "could not set pins\n"); + } + ret = clk_set_rate(gc2385->xvclk, GC2385_XVCLK_FREQ); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate (24MHz)\n"); + if (clk_get_rate(gc2385->xvclk) != GC2385_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); + ret = clk_prepare_enable(gc2385->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + if (!IS_ERR(gc2385->reset_gpio)) + gpiod_set_value_cansleep(gc2385->reset_gpio, 1); + + ret = regulator_bulk_enable(GC2385_NUM_SUPPLIES, gc2385->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + + usleep_range(1000, 1100); + if (!IS_ERR(gc2385->reset_gpio)) + gpiod_set_value_cansleep(gc2385->reset_gpio, 0); + + usleep_range(500, 1000); + if (!IS_ERR(gc2385->pwdn_gpio)) + gpiod_set_value_cansleep(gc2385->pwdn_gpio, 1); + + /* 8192 cycles prior to first SCCB transaction */ + delay_us = gc2385_cal_delay(8192); + usleep_range(delay_us, delay_us * 2); + + return 0; + +disable_clk: + clk_disable_unprepare(gc2385->xvclk); + + return ret; +} + +static void __gc2385_power_off(struct gc2385 *gc2385) +{ + int ret = 0; + + if (!IS_ERR(gc2385->pwdn_gpio)) + gpiod_set_value_cansleep(gc2385->pwdn_gpio, 0); + clk_disable_unprepare(gc2385->xvclk); + if (!IS_ERR(gc2385->reset_gpio)) + gpiod_set_value_cansleep(gc2385->reset_gpio, 1); + if (!IS_ERR_OR_NULL(gc2385->pins_sleep)) { + ret = pinctrl_select_state(gc2385->pinctrl, + gc2385->pins_sleep); + if (ret < 0) + dev_dbg(&gc2385->client->dev, "could not set pins\n"); + } + regulator_bulk_disable(GC2385_NUM_SUPPLIES, gc2385->supplies); +} + +static int gc2385_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc2385 *gc2385 = to_gc2385(sd); + + return __gc2385_power_on(gc2385); +} + +static int gc2385_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc2385 *gc2385 = to_gc2385(sd); + + __gc2385_power_off(gc2385); + + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int gc2385_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct gc2385 *gc2385 = to_gc2385(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + const struct gc2385_mode *def_mode = &supported_modes[0]; + + mutex_lock(&gc2385->mutex); + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10; + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&gc2385->mutex); + /* No crop or compose */ + + return 0; +} +#endif + +static const struct dev_pm_ops gc2385_pm_ops = { + SET_RUNTIME_PM_OPS(gc2385_runtime_suspend, + gc2385_runtime_resume, NULL) +}; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops gc2385_internal_ops = { + .open = gc2385_open, +}; +#endif + +static const struct v4l2_subdev_core_ops gc2385_core_ops = { + .s_power = gc2385_s_power, + .ioctl = gc2385_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = gc2385_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops gc2385_video_ops = { + .s_stream = gc2385_s_stream, + .g_frame_interval = gc2385_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops gc2385_pad_ops = { + .enum_mbus_code = gc2385_enum_mbus_code, + .enum_frame_size = gc2385_enum_frame_sizes, + .get_fmt = gc2385_get_fmt, + .set_fmt = gc2385_set_fmt, +}; + +static const struct v4l2_subdev_ops gc2385_subdev_ops = { + .core = &gc2385_core_ops, + .video = &gc2385_video_ops, + .pad = &gc2385_pad_ops, +}; + +#define GC2385_ANALOG_GAIN_1 64 /*1.00x*/ +#define GC2385_ANALOG_GAIN_2 92 // 1.43x +#define GC2385_ANALOG_GAIN_3 127 // 1.99x +#define GC2385_ANALOG_GAIN_4 183 // 2.86x +#define GC2385_ANALOG_GAIN_5 257 // 4.01x +#define GC2385_ANALOG_GAIN_6 369 // 5.76x +#define GC2385_ANALOG_GAIN_7 531 //8.30x +#define GC2385_ANALOG_GAIN_8 750 // 11.72x +#define GC2385_ANALOG_GAIN_9 1092 // 17.06x + +static int gc2385_set_gain_reg(struct gc2385 *gc2385, u32 a_gain) +{ + int ret = 0; + u32 temp = 0; + + ret = gc2385_write_reg(gc2385->client, + GC2385_REG_SET_PAGE, GC2385_SET_PAGE_ONE); + if (a_gain >= GC2385_ANALOG_GAIN_1 && + a_gain < GC2385_ANALOG_GAIN_2) { + ret |= gc2385_write_reg(gc2385->client, 0x20, 0x73); + ret |= gc2385_write_reg(gc2385->client, 0x22, 0xa2); + ret |= gc2385_write_reg(gc2385->client, + GC2385_REG_AGAIN, 0x0); + temp = 256 * a_gain / GC2385_ANALOG_GAIN_1; + ret |= gc2385_write_reg(gc2385->client, + GC2385_REG_DGAIN_INT, (temp >> 8) & 0xff); + ret |= gc2385_write_reg(gc2385->client, + GC2385_REG_DGAIN_FRAC, temp & 0xff); + } else if (a_gain >= GC2385_ANALOG_GAIN_2 && + a_gain < GC2385_ANALOG_GAIN_3) { + ret |= gc2385_write_reg(gc2385->client, 0x20, 0x73); + ret |= gc2385_write_reg(gc2385->client, 0x22, 0xa2); + ret |= gc2385_write_reg(gc2385->client, + GC2385_REG_AGAIN, 0x1); + temp = 256 * a_gain / GC2385_ANALOG_GAIN_2; + ret |= gc2385_write_reg(gc2385->client, + GC2385_REG_DGAIN_INT, (temp >> 8) & 0xff); + ret |= gc2385_write_reg(gc2385->client, + GC2385_REG_DGAIN_FRAC, temp & 0xff); + } else if (a_gain >= GC2385_ANALOG_GAIN_3 && + a_gain < GC2385_ANALOG_GAIN_4) { + ret |= gc2385_write_reg(gc2385->client, 0x20, 0x73); + ret |= gc2385_write_reg(gc2385->client, 0x22, 0xa2); + ret |= gc2385_write_reg(gc2385->client, + GC2385_REG_AGAIN, 0x2); + temp = 256 * a_gain / GC2385_ANALOG_GAIN_3; + ret |= gc2385_write_reg(gc2385->client, + GC2385_REG_DGAIN_INT, (temp >> 8) & 0xff); + ret |= gc2385_write_reg(gc2385->client, + GC2385_REG_DGAIN_FRAC, temp & 0xff); + } else if (a_gain >= GC2385_ANALOG_GAIN_4 && + a_gain < GC2385_ANALOG_GAIN_5) { + ret |= gc2385_write_reg(gc2385->client, 0x20, 0x73); + ret |= gc2385_write_reg(gc2385->client, 0x22, 0xa2); + ret |= gc2385_write_reg(gc2385->client, + GC2385_REG_AGAIN, 0x3); + temp = 256 * a_gain / GC2385_ANALOG_GAIN_4; + ret |= gc2385_write_reg(gc2385->client, + GC2385_REG_DGAIN_INT, (temp >> 8) & 0xff); + ret |= gc2385_write_reg(gc2385->client, + GC2385_REG_DGAIN_FRAC, temp & 0xff); + } else if (a_gain >= GC2385_ANALOG_GAIN_5 && + a_gain < GC2385_ANALOG_GAIN_6) { + ret |= gc2385_write_reg(gc2385->client, 0x20, 0x73); + ret |= gc2385_write_reg(gc2385->client, 0x22, 0xa3); + ret |= gc2385_write_reg(gc2385->client, + GC2385_REG_AGAIN, 0x4); + temp = 256 * a_gain / GC2385_ANALOG_GAIN_5; + ret |= gc2385_write_reg(gc2385->client, + GC2385_REG_DGAIN_INT, (temp >> 8) & 0xff); + ret |= gc2385_write_reg(gc2385->client, + GC2385_REG_DGAIN_FRAC, temp & 0xff); + } else if (a_gain >= GC2385_ANALOG_GAIN_6 && + a_gain < GC2385_ANALOG_GAIN_7) { + ret |= gc2385_write_reg(gc2385->client, 0x20, 0x73); + ret |= gc2385_write_reg(gc2385->client, 0x22, 0xa3); + ret |= gc2385_write_reg(gc2385->client, + GC2385_REG_AGAIN, 0x5); + temp = 256 * a_gain / GC2385_ANALOG_GAIN_6; + ret |= gc2385_write_reg(gc2385->client, + GC2385_REG_DGAIN_INT, (temp >> 8) & 0xff); + ret |= gc2385_write_reg(gc2385->client, + GC2385_REG_DGAIN_FRAC, temp & 0xff); + } else if (a_gain >= GC2385_ANALOG_GAIN_7 && + a_gain < GC2385_ANALOG_GAIN_8) { + ret |= gc2385_write_reg(gc2385->client, 0x20, 0x74); + ret |= gc2385_write_reg(gc2385->client, 0x22, 0xa3); + ret |= gc2385_write_reg(gc2385->client, + GC2385_REG_AGAIN, 0x6); + temp = 256 * a_gain / GC2385_ANALOG_GAIN_7; + ret |= gc2385_write_reg(gc2385->client, + GC2385_REG_DGAIN_INT, (temp >> 8) & 0xff); + ret |= gc2385_write_reg(gc2385->client, + GC2385_REG_DGAIN_FRAC, temp & 0xff); + } else if (a_gain >= GC2385_ANALOG_GAIN_8 && + a_gain < GC2385_ANALOG_GAIN_9) { + ret |= gc2385_write_reg(gc2385->client, 0x20, 0x74); + ret |= gc2385_write_reg(gc2385->client, 0x22, 0xa3); + ret |= gc2385_write_reg(gc2385->client, + GC2385_REG_AGAIN, 0x7); + temp = 256 * a_gain / GC2385_ANALOG_GAIN_8; + ret |= gc2385_write_reg(gc2385->client, + GC2385_REG_DGAIN_INT, (temp >> 8) & 0xff); + ret |= gc2385_write_reg(gc2385->client, + GC2385_REG_DGAIN_FRAC, temp & 0xff); + } else { + ret |= gc2385_write_reg(gc2385->client, 0x20, 0x75); + ret |= gc2385_write_reg(gc2385->client, 0x22, 0xa4); + ret |= gc2385_write_reg(gc2385->client, + GC2385_REG_AGAIN, 0x8); + temp = 256 * a_gain / GC2385_ANALOG_GAIN_9; + ret |= gc2385_write_reg(gc2385->client, + GC2385_REG_DGAIN_INT, (temp >> 8) & 0xff); + ret |= gc2385_write_reg(gc2385->client, + GC2385_REG_DGAIN_FRAC, temp & 0xff); + } + return ret; +} + +static int gc2385_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gc2385 *gc2385 = container_of(ctrl->handler, + struct gc2385, ctrl_handler); + struct i2c_client *client = gc2385->client; + s64 max; + int ret = 0; + + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ + max = gc2385->cur_mode->height + ctrl->val - 4; + __v4l2_ctrl_modify_range(gc2385->exposure, + gc2385->exposure->minimum, max, + gc2385->exposure->step, + gc2385->exposure->default_value); + break; + } + + if (pm_runtime_get(&client->dev) <= 0) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + /* 4 least significant bits of expsoure are fractional part */ + ret = gc2385_write_reg(gc2385->client, + GC2385_REG_SET_PAGE, + GC2385_SET_PAGE_ONE); + ret |= gc2385_write_reg(gc2385->client, + GC2385_REG_EXPOSURE_H, + GC2385_FETCH_HIGH_BYTE_EXP(ctrl->val)); + ret |= gc2385_write_reg(gc2385->client, + GC2385_REG_EXPOSURE_L, + GC2385_FETCH_LOW_BYTE_EXP(ctrl->val)); + break; + case V4L2_CID_ANALOGUE_GAIN: + ret = gc2385_set_gain_reg(gc2385, ctrl->val); + break; + case V4L2_CID_VBLANK: + ret = gc2385_write_reg(gc2385->client, + GC2385_REG_SET_PAGE, + GC2385_SET_PAGE_ONE); + ret |= gc2385_write_reg(gc2385->client, + GC2385_REG_VTS_H, + ((ctrl->val - 32) >> 8) & 0xff); + ret |= gc2385_write_reg(gc2385->client, + GC2385_REG_VTS_L, + (ctrl->val - 32) & 0xff); + break; + default: + dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", + __func__, ctrl->id, ctrl->val); + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops gc2385_ctrl_ops = { + .s_ctrl = gc2385_set_ctrl, +}; + +static int gc2385_initialize_controls(struct gc2385 *gc2385) +{ + const struct gc2385_mode *mode; + struct v4l2_ctrl_handler *handler; + struct v4l2_ctrl *ctrl; + s64 exposure_max, vblank_def; + u32 h_blank; + int ret; + + handler = &gc2385->ctrl_handler; + mode = gc2385->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 8); + if (ret) + return ret; + handler->lock = &gc2385->mutex; + + ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, + 0, 0, link_freq_menu_items); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, + 0, GC2385_PIXEL_RATE, 1, GC2385_PIXEL_RATE); + + h_blank = mode->hts_def - mode->width; + gc2385->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (gc2385->hblank) + gc2385->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = mode->vts_def - mode->height; + gc2385->vblank = v4l2_ctrl_new_std(handler, &gc2385_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + GC2385_VTS_MAX - mode->height, + 1, vblank_def); + + exposure_max = mode->vts_def - 4; + gc2385->exposure = v4l2_ctrl_new_std(handler, &gc2385_ctrl_ops, + V4L2_CID_EXPOSURE, GC2385_EXPOSURE_MIN, + exposure_max, GC2385_EXPOSURE_STEP, + mode->exp_def); + + gc2385->anal_gain = v4l2_ctrl_new_std(handler, &gc2385_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, GC2385_GAIN_MIN, + GC2385_GAIN_MAX, GC2385_GAIN_STEP, + GC2385_GAIN_DEFAULT); + if (handler->error) { + ret = handler->error; + dev_err(&gc2385->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + gc2385->subdev.ctrl_handler = handler; + + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +static int gc2385_check_sensor_id(struct gc2385 *gc2385, + struct i2c_client *client) +{ + struct device *dev = &gc2385->client->dev; + u16 id = 0; + u8 reg_H = 0; + u8 reg_L = 0; + int ret; + + ret = gc2385_write_reg(gc2385->client, + GC2385_REG_SET_PAGE, + GC2385_SET_PAGE_ONE); + ret |= gc2385_read_reg(client, GC2385_REG_CHIP_ID_H, ®_H); + ret |= gc2385_read_reg(client, GC2385_REG_CHIP_ID_L, ®_L); + id = ((reg_H << 8) & 0xff00) | (reg_L & 0xff); + if (id != CHIP_ID) { + dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret); + return -ENODEV; + } + return ret; +} + +static int gc2385_configure_regulators(struct gc2385 *gc2385) +{ + unsigned int i; + + for (i = 0; i < GC2385_NUM_SUPPLIES; i++) + gc2385->supplies[i].supply = gc2385_supply_names[i]; + + return devm_regulator_bulk_get(&gc2385->client->dev, + GC2385_NUM_SUPPLIES, + gc2385->supplies); +} + +static int gc2385_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct gc2385 *gc2385; + struct v4l2_subdev *sd; + char facing[2]; + int ret; + + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + gc2385 = devm_kzalloc(dev, sizeof(*gc2385), GFP_KERNEL); + if (!gc2385) + return -ENOMEM; + + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &gc2385->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &gc2385->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &gc2385->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &gc2385->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + gc2385->client = client; + gc2385->cur_mode = &supported_modes[0]; + + gc2385->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(gc2385->xvclk)) { + dev_err(dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + gc2385->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(gc2385->reset_gpio)) + dev_warn(dev, "Failed to get reset-gpios\n"); + + gc2385->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(gc2385->pwdn_gpio)) + dev_warn(dev, "Failed to get pwdn-gpios\n"); + + ret = gc2385_configure_regulators(gc2385); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + + gc2385->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR(gc2385->pinctrl)) { + gc2385->pins_default = + pinctrl_lookup_state(gc2385->pinctrl, + OF_CAMERA_PINCTRL_STATE_DEFAULT); + if (IS_ERR(gc2385->pins_default)) + dev_err(dev, "could not get default pinstate\n"); + + gc2385->pins_sleep = + pinctrl_lookup_state(gc2385->pinctrl, + OF_CAMERA_PINCTRL_STATE_SLEEP); + if (IS_ERR(gc2385->pins_sleep)) + dev_err(dev, "could not get sleep pinstate\n"); + } + + mutex_init(&gc2385->mutex); + + sd = &gc2385->subdev; + v4l2_i2c_subdev_init(sd, client, &gc2385_subdev_ops); + ret = gc2385_initialize_controls(gc2385); + if (ret) + goto err_destroy_mutex; + + ret = __gc2385_power_on(gc2385); + if (ret) + goto err_free_handler; + + ret = gc2385_check_sensor_id(gc2385, client); + if (ret) + goto err_power_off; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &gc2385_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + gc2385->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &gc2385->pad); + if (ret < 0) + goto err_power_off; +#endif + + memset(facing, 0, sizeof(facing)); + if (strcmp(gc2385->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + gc2385->module_index, facing, + GC2385_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor_common(sd); + if (ret) { + dev_err(dev, "v4l2 async register subdev failed\n"); + goto err_clean_entity; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; + +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif +err_power_off: + __gc2385_power_off(gc2385); +err_free_handler: + v4l2_ctrl_handler_free(&gc2385->ctrl_handler); +err_destroy_mutex: + mutex_destroy(&gc2385->mutex); + + return ret; +} + +static int gc2385_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc2385 *gc2385 = to_gc2385(sd); + + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + v4l2_ctrl_handler_free(&gc2385->ctrl_handler); + mutex_destroy(&gc2385->mutex); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __gc2385_power_off(gc2385); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id gc2385_of_match[] = { + { .compatible = "galaxycore,gc2385" }, + {}, +}; +MODULE_DEVICE_TABLE(of, gc2385_of_match); +#endif + +static const struct i2c_device_id gc2385_match_id[] = { + { "galaxycore,gc2385", 0 }, + { }, +}; + +static struct i2c_driver gc2385_i2c_driver = { + .driver = { + .name = GC2385_NAME, + .pm = &gc2385_pm_ops, + .of_match_table = of_match_ptr(gc2385_of_match), + }, + .probe = &gc2385_probe, + .remove = &gc2385_remove, + .id_table = gc2385_match_id, +}; + +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&gc2385_i2c_driver); +} + +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&gc2385_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_DESCRIPTION("GalaxyCore gc2385 sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/gc5025.c b/drivers/media/i2c/gc5025.c new file mode 100644 index 000000000000..8dc8318b3c25 --- /dev/null +++ b/drivers/media/i2c/gc5025.c @@ -0,0 +1,1922 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * gc5025 driver + * + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd. + * + * V0.0X01.0X01 add poweron function. + * V0.0X01.0X02 fix mclk issue when probe multiple camera. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x02) + +#ifndef V4L2_CID_DIGITAL_GAIN +#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN +#endif + +#define GC5025_LANES 2 +#define GC5025_BITS_PER_SAMPLE 10 +#define GC5025_LINK_FREQ_MHZ 432000000 +/* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ +#define GC5025_PIXEL_RATE (GC5025_LINK_FREQ_MHZ * 2 * 2 / 10) +#define GC5025_XVCLK_FREQ 24000000 + +#define CHIP_ID 0x5025 +#define GC5025_REG_CHIP_ID_H 0xf0 +#define GC5025_REG_CHIP_ID_L 0xf1 + +#define GC5025_REG_SET_PAGE 0xfe +#define GC5025_SET_PAGE_ONE 0x00 + +#define GC5025_REG_CTRL_MODE 0x3f +#define GC5025_MODE_SW_STANDBY 0x01 +#define GC5025_MODE_STREAMING 0x91 + +#define GC5025_REG_EXPOSURE_H 0x03 +#define GC5025_REG_EXPOSURE_L 0x04 +#define GC5025_EXPOSURE_MIN 4 +#define GC5025_EXPOSURE_STEP 1 +#define GC5025_VTS_MAX 0x1fff + +#define GC5025_REG_AGAIN 0xb6 +#define GC5025_REG_DGAIN_INT 0xb1 +#define GC5025_REG_DGAIN_FRAC 0xb2 +#define GC5025_GAIN_MIN 64 +#define GC5025_GAIN_MAX 1024 +#define GC5025_GAIN_STEP 1 +#define GC5025_GAIN_DEFAULT 64 + +#define GC5025_REG_VTS_H 0x07 +#define GC5025_REG_VTS_L 0x08 + +#define REG_NULL 0xFF + +#define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default" +#define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep" + +#define GC5025_NAME "gc5025" + +static const char * const gc5025_supply_names[] = { + "avdd", /* Analog power */ + "dovdd", /* Digital I/O power */ + "dvdd", /* Digital core power */ +}; + +#define GC5025_NUM_SUPPLIES ARRAY_SIZE(gc5025_supply_names) + +#define IMAGE_NORMAL_MIRROR +#define DD_PARAM_QTY_5025 200 +#define INFO_ROM_START_5025 0x08 +#define INFO_WIDTH_5025 0x08 +#define WB_ROM_START_5025 0x88 +#define WB_WIDTH_5025 0x05 +#define GOLDEN_ROM_START_5025 0xe0 +#define GOLDEN_WIDTH_5025 0x05 +#define WINDOW_WIDTH 0x0a30 +#define WINDOW_HEIGHT 0x079c + +struct gc5025_otp_info { + u32 flag; //bit[7]: info bit[6]:wb bit[3]:dd + u32 module_id; + u32 lens_id; + u16 vcm_id; + u16 vcm_driver_id; + u32 year; + u32 month; + u32 day; + u32 rg_ratio; + u32 bg_ratio; + u32 golden_rg; + u32 golden_bg; + u16 dd_param_x[DD_PARAM_QTY_5025]; + u16 dd_param_y[DD_PARAM_QTY_5025]; + u16 dd_param_type[DD_PARAM_QTY_5025]; + u16 dd_cnt; +}; + +struct gc5025_id_name { + u32 id; + char name[RKMODULE_NAME_LEN]; +}; + +static const struct gc5025_id_name gc5025_module_info[] = { + {0x0d, "CameraKing"}, + {0x00, "Unknown"} +}; + +static const struct gc5025_id_name gc5025_lens_info[] = { + {0xa9, "CK5502"}, + {0x00, "Unknown"} +}; + +struct regval { + u8 addr; + u8 val; +}; + +struct gc5025_mode { + u32 width; + u32 height; + struct v4l2_fract max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + const struct regval *reg_list; +}; + +struct gc5025 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; + struct regulator_bulk_data supplies[GC5025_NUM_SUPPLIES]; + + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_sleep; + + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *anal_gain; + struct v4l2_ctrl *digi_gain; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct mutex mutex; + bool streaming; + bool power_on; + const struct gc5025_mode *cur_mode; + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; + u32 Dgain_ratio; + bool DR_State; + struct gc5025_otp_info *otp; + struct rkmodule_inf module_inf; + struct rkmodule_awb_cfg awb_cfg; +}; + +#define to_gc5025(sd) container_of(sd, struct gc5025, subdev) + +/* + * Xclk 24Mhz + */ +static const struct regval gc5025_global_regs[] = { + {REG_NULL, 0x00}, +}; + +/* + * Xclk 24Mhz + * max_framerate 30fps + * mipi_datarate per lane 656Mbps + */ +static const struct regval gc5025_1600x1200_regs[] = { + {0xfe, 0x00}, + {0xfe, 0x00}, + {0xfe, 0x00}, + {0xf7, 0x01}, + {0xf8, 0x11}, + {0xf9, 0x00}, + {0xfa, 0xa0}, + {0xfc, 0x2a}, + {0xfe, 0x03}, + {0x01, 0x07}, + {0xfc, 0x2e}, + {0xfe, 0x00}, + {0x88, 0x03}, + {0x03, 0x07}, + {0x04, 0xC0}, + {0x05, 0x02}, + {0x06, 0x58}, + {0x08, 0x20}, + {0x0a, 0x1c}, + {0x0c, 0x04}, + {0x0d, 0x07}, + {0x0e, 0x9c}, + {0x0f, 0x0a}, + {0x10, 0x30}, + {0x17, 0xc0}, + {0x18, 0x02}, + {0x19, 0x17}, + {0x1a, 0x1a}, + {0x1e, 0x90}, + {0x1f, 0xb0}, + {0x20, 0x2b}, + {0x21, 0x2b}, + {0x26, 0x2b}, + {0x25, 0xc1}, + {0x27, 0x64}, + {0x28, 0x00}, + {0x29, 0x3f}, + {0x2b, 0x80}, + {0x30, 0x11}, + {0x31, 0x20}, + {0x32, 0xa0}, + {0x33, 0x00}, + {0x34, 0x55}, + {0x3a, 0x00}, + {0x3b, 0x00}, + {0x81, 0x60}, + {0xcb, 0x02}, + {0xcd, 0x2d}, + {0xcf, 0x50}, + {0xd0, 0xb3}, + {0xd1, 0x18}, + {0xd9, 0xaa}, + {0xdc, 0x03}, + {0xdd, 0xaa}, + {0xe0, 0x00}, + {0xe1, 0x0a}, + {0xe3, 0x2a}, + {0xe4, 0xa0}, + {0xe5, 0x06}, + {0xe6, 0x10}, + {0xe7, 0xc2}, + {0xfe, 0x10}, + {0xfe, 0x00}, + {0xfe, 0x10}, + {0xfe, 0x00}, + {0x80, 0x10}, + {0x89, 0x03}, + {0xfe, 0x01}, + {0x88, 0xf7}, + {0x8a, 0x03}, + {0x8e, 0xc7}, + {0xfe, 0x00}, + {0x40, 0x22}, + {0x41, 0x28}, + {0x42, 0x04}, + {0x4e, 0x0f}, + {0x4f, 0xf0}, + {0x67, 0x0c}, + {0xae, 0x40}, + {0xaf, 0x04}, + {0x60, 0x00}, + {0x61, 0x80}, + {0xb0, 0x58}, + {0xb1, 0x01}, + {0xb2, 0x00}, + {0xb6, 0x00}, + {0x91, 0x00}, + {0x92, 0x02}, + {0x94, 0x03}, + {0xfe, 0x03}, + {0x02, 0x03}, + {0x03, 0x8e}, + {0x06, 0x80}, + {0x15, 0x00}, + {0x16, 0x09}, + {0x18, 0x0a}, + {0x21, 0x10}, + {0x22, 0x05}, + {0x23, 0x20}, + {0x24, 0x02}, + {0x25, 0x20}, + {0x26, 0x08}, + {0x29, 0x06}, + {0x2a, 0x0a}, + {0x2b, 0x08}, + {0xfe, 0x00}, + {REG_NULL, 0x00}, +}; + +static const struct regval gc5025_doublereset_reg[] = { + {0xfe, 0x00}, + {0x1c, 0x1c}, + {0x2f, 0x4a}, + {0x38, 0x02}, + {0x39, 0x00}, + {0x3c, 0x02}, + {0x3d, 0x02}, + {0xd3, 0xcc}, + {0x43, 0x03}, + {0x1d, 0x13}, + {REG_NULL, 0x00}, +}; + +static const struct regval gc5025_disable_doublereset_reg[] = { + {0xfe, 0x00}, + {0x1c, 0x2c}, + {0x2f, 0x4d}, + {0x38, 0x04}, + {0x39, 0x02}, + {0x3c, 0x08}, + {0x3d, 0x0f}, + {0xd3, 0xc4}, + {0x43, 0x08}, + {0x1d, 0x00}, + {REG_NULL, 0x00}, +}; + +static const struct gc5025_mode supported_modes[] = { + { + .width = 2592, + .height = 1944, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x07C0, + .hts_def = 0x12C0, + .vts_def = 0x07D0, + .reg_list = gc5025_1600x1200_regs, + }, +}; + +static const s64 link_freq_menu_items[] = { + GC5025_LINK_FREQ_MHZ +}; + +/* Write registers up to 4 at a time */ +static int gc5025_write_reg(struct i2c_client *client, u8 reg, u8 val) +{ + struct i2c_msg msg; + u8 buf[2]; + int ret; + + buf[0] = reg & 0xFF; + buf[1] = val; + + msg.addr = client->addr; + msg.flags = client->flags; + msg.buf = buf; + msg.len = sizeof(buf); + + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret >= 0) + return 0; + + dev_err(&client->dev, + "gc5025 write reg(0x%x val:0x%x) failed !\n", reg, val); + + return ret; +} + +static int gc5025_write_array(struct i2c_client *client, + const struct regval *regs) +{ + u32 i = 0; + int ret = 0; + + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) + ret = gc5025_write_reg(client, regs[i].addr, regs[i].val); + + return ret; +} + +/* Read registers up to 4 at a time */ +static int gc5025_read_reg(struct i2c_client *client, u8 reg, u8 *val) +{ + struct i2c_msg msg[2]; + u8 buf[1]; + int ret; + + buf[0] = reg & 0xFF; + + msg[0].addr = client->addr; + msg[0].flags = client->flags; + msg[0].buf = buf; + msg[0].len = sizeof(buf); + + msg[1].addr = client->addr; + msg[1].flags = client->flags | I2C_M_RD; + msg[1].buf = buf; + msg[1].len = 1; + + ret = i2c_transfer(client->adapter, msg, 2); + if (ret >= 0) { + *val = buf[0]; + return 0; + } + + dev_err(&client->dev, + "gc5025 read reg:0x%x failed !\n", reg); + + return ret; +} + +static int gc5025_get_reso_dist(const struct gc5025_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); +} + +static const struct gc5025_mode * +gc5025_find_best_fit(struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *framefmt = &fmt->format; + int dist; + int cur_best_fit = 0; + int cur_best_fit_dist = -1; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { + dist = gc5025_get_reso_dist(&supported_modes[i], framefmt); + if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { + cur_best_fit_dist = dist; + cur_best_fit = i; + } + } + + return &supported_modes[cur_best_fit]; +} + +static int gc5025_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct gc5025 *gc5025 = to_gc5025(sd); + const struct gc5025_mode *mode; + s64 h_blank, vblank_def; + + mutex_lock(&gc5025->mutex); + + mode = gc5025_find_best_fit(fmt); + fmt->format.code = MEDIA_BUS_FMT_SRGGB10_1X10; + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; +#else + mutex_unlock(&gc5025->mutex); + return -ENOTTY; +#endif + } else { + gc5025->cur_mode = mode; + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(gc5025->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(gc5025->vblank, vblank_def, + GC5025_VTS_MAX - mode->height, + 1, vblank_def); + } + + mutex_unlock(&gc5025->mutex); + + return 0; +} + +static int gc5025_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct gc5025 *gc5025 = to_gc5025(sd); + const struct gc5025_mode *mode = gc5025->cur_mode; + + mutex_lock(&gc5025->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); +#else + mutex_unlock(&gc5025->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = MEDIA_BUS_FMT_SRGGB10_1X10; + fmt->format.field = V4L2_FIELD_NONE; + } + mutex_unlock(&gc5025->mutex); + + return 0; +} + +static int gc5025_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index != 0) + return -EINVAL; + code->code = MEDIA_BUS_FMT_SRGGB10_1X10; + + return 0; +} + +static int gc5025_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fse->code != MEDIA_BUS_FMT_SRGGB10_1X10) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = supported_modes[fse->index].width; + fse->max_height = supported_modes[fse->index].height; + fse->min_height = supported_modes[fse->index].height; + + return 0; +} + +static int gc5025_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct gc5025 *gc5025 = to_gc5025(sd); + const struct gc5025_mode *mode = gc5025->cur_mode; + + mutex_lock(&gc5025->mutex); + fi->interval = mode->max_fps; + mutex_unlock(&gc5025->mutex); + + return 0; +} + +static int gc5025_otp_read_reg(struct i2c_client *client, + int page, + int address) +{ + int ret = 0; + u8 val = 0; + u8 addr_high = 0; + + ret = gc5025_write_reg(client, 0xfe, 0x00); + ret |= gc5025_read_reg(client, 0xd4, &addr_high); + switch (page) { + case 0: + addr_high &= 0xfb; + break; + case 1: + addr_high |= 0x04; + break; + default: + break; + } + addr_high &= 0xfc; + addr_high |= (address & 0x300) >> 8; + ret |= gc5025_write_reg(client, 0xD4, addr_high); + ret |= gc5025_write_reg(client, 0xD5, address & 0xff); + ret |= gc5025_write_reg(client, 0xF3, 0x20); + ret |= gc5025_read_reg(client, 0xD7, &val); + if (ret != 0) + return ret; + return val; +} + +static int gc5025_otp_enable(struct gc5025 *gc5025) +{ + struct i2c_client *client = gc5025->client; + u8 otp_clk = 0; + u8 otp_en = 0; + int ret = 0; + + ret = gc5025_write_reg(client, 0xfe, 0x00); + ret |= gc5025_write_reg(client, 0xf7, 0x01); + ret |= gc5025_write_reg(client, 0xf8, 0x11); + ret |= gc5025_write_reg(client, 0xf9, 0x00); + ret |= gc5025_write_reg(client, 0xfa, 0xa0); + ret |= gc5025_write_reg(client, 0xfc, 0x2a); + ret |= gc5025_write_reg(client, 0xfe, 0x03); + ret |= gc5025_write_reg(client, 0x01, 0x07); + ret |= gc5025_write_reg(client, 0xfc, 0x2e); + ret |= gc5025_write_reg(client, 0xfe, 0x00); + usleep_range(10, 20); + ret |= gc5025_write_reg(client, 0x88, 0x03); + ret |= gc5025_write_reg(client, 0xe7, 0xcc); + ret |= gc5025_write_reg(client, 0xfc, 0x2e); + ret |= gc5025_write_reg(client, 0xfa, 0xb0); + + ret |= gc5025_read_reg(client, 0xfa, &otp_clk); + ret |= gc5025_read_reg(client, 0xd4, &otp_en); + otp_clk |= 0x10; + otp_en |= 0x80; + ret |= gc5025_write_reg(client, 0xfa, otp_clk); + ret |= gc5025_write_reg(client, 0xd4, otp_en); + usleep_range(100, 200); + return ret; +} + +static int gc5025_otp_disable(struct gc5025 *gc5025) +{ + struct i2c_client *client = gc5025->client; + u8 otp_clk = 0; + u8 otp_en = 0; + int ret = 0; + + ret = gc5025_read_reg(client, 0xfa, &otp_clk); + ret |= gc5025_read_reg(client, 0xd4, &otp_en); + otp_clk &= 0xef; + otp_en &= 0x7f; + ret |= gc5025_write_reg(client, 0xfa, otp_clk); + ret |= gc5025_write_reg(client, 0xd4, otp_en); + return ret; +} + +static int gc5025_otp_read(struct gc5025 *gc5025) +{ + int otp_flag, i, j, index, temp, tmpH, tmpL; + struct gc5025_otp_info *otp_p; + struct device *dev = &gc5025->client->dev; + struct i2c_client *client = gc5025->client; + int checksum = 0; + int page = 0; + int total_number = 0; + u8 m_DD_Otp_Value[182]; + u16 dd_rom_start, offset; + u8 info_start_add, wb_start_add, golden_start_add; + u8 check_dd_flag, type; + u8 dd0 = 0, dd1 = 0, dd2 = 0; + u16 x, y; + int cnt = 0; + + otp_p = devm_kzalloc(dev, sizeof(*otp_p), GFP_KERNEL); + if (!otp_p) + return -ENOMEM; + + /* OTP info and awb*/ + otp_flag = gc5025_otp_read_reg(client, 1, 0x00); + for (index = 0; index < 2; index++) { + switch ((otp_flag >> (4 + 2 * index)) & 0x03) { + case 0x00: + dev_err(dev, "%s GC5025_OTP_INFO group %d is Empty!\n", + __func__, index + 1); + break; + case 0x01: + dev_dbg(dev, "%s GC5025_OTP_INFO group %d is Valid!\n", + __func__, index + 1); + checksum = 0; + info_start_add = + INFO_ROM_START_5025 + 8 * index * INFO_WIDTH_5025; + otp_p->module_id = gc5025_otp_read_reg(client, + 1, info_start_add); + checksum += otp_p->module_id; + otp_p->lens_id = gc5025_otp_read_reg(client, + 1, info_start_add + 8 * 1); + checksum += otp_p->lens_id; + otp_p->vcm_driver_id = gc5025_otp_read_reg(client, + 1, info_start_add + 8 * 2); + checksum += otp_p->vcm_driver_id; + otp_p->vcm_id = gc5025_otp_read_reg(client, 1, + info_start_add + 8 * 3); + checksum += otp_p->vcm_id; + otp_p->year = gc5025_otp_read_reg(client, 1, + info_start_add + 8 * 4); + checksum += otp_p->year; + otp_p->month = gc5025_otp_read_reg(client, + 1, info_start_add + 8 * 5); + checksum += otp_p->month; + otp_p->day = gc5025_otp_read_reg(client, + 1, info_start_add + 8 * 6); + checksum += otp_p->day; + checksum = checksum % 255 + 1; + temp = gc5025_otp_read_reg(client, + 1, 0x40 + 8 * index * INFO_WIDTH_5025); + if (checksum == temp) { + otp_p->flag = 0x80; + dev_dbg(dev, "fac info: module(0x%x) lens(0x%x) time(%d_%d_%d)\n", + otp_p->module_id, + otp_p->lens_id, + otp_p->year, + otp_p->month, + otp_p->day); + } else { + dev_err(dev, "otp module info check sum error\n"); + } + break; + case 0x02: + case 0x03: + dev_err(dev, "%s GC5025_OTP_INFO group %d is Invalid !!\n", + __func__, index + 1); + break; + default: + break; + } + switch ((otp_flag >> (2 * index)) & 0x03) { + case 0x00: + dev_err(dev, "%s GC5025_OTP_WB group %d is Empty !\n", + __func__, index + 1); + break; + case 0x01: + dev_dbg(dev, "%s GC5025_OTP_WB group %d is Valid !!\n", + __func__, index + 1); + checksum = 0; + wb_start_add = + WB_ROM_START_5025 + 8 * index * WB_WIDTH_5025; + tmpH = gc5025_otp_read_reg(client, + 1, wb_start_add); + checksum += tmpH; + tmpL = gc5025_otp_read_reg(client, + 1, wb_start_add + 8 * 1); + checksum += tmpL; + otp_p->rg_ratio = (tmpH << 8) | tmpL; + tmpH = gc5025_otp_read_reg(client, + 1, wb_start_add + 8 * 2); + checksum += tmpH; + tmpL = gc5025_otp_read_reg(client, + 1, wb_start_add + 8 * 3); + checksum += tmpL; + otp_p->bg_ratio = (tmpH << 8) | tmpL; + checksum = checksum % 255 + 1; + temp = gc5025_otp_read_reg(client, + 1, 0xa8 + 8 * index * WB_WIDTH_5025); + if (checksum == temp) { + otp_p->flag = 0x40; + dev_dbg(dev, "otp:(rg_ratio 0x%x, bg_ratio 0x%x)\n", + otp_p->rg_ratio, otp_p->bg_ratio); + } + break; + case 0x02: + case 0x03: + dev_err(dev, "%s GC5025_OTP_WB group %d is Invalid !!\n", + __func__, index + 1); + break; + default: + break; + } + } + /* OTP awb golden*/ + otp_flag = gc5025_otp_read_reg(client, 1, 0xd8); + for (index = 0; index < 2; index++) { + switch ((otp_flag >> (2 * index)) & 0x03) { + case 0x00: + dev_err(dev, "%s GC5025_OTP_GOLDEN group %d is Empty !\n", + __func__, index + 1); + break; + case 0x01: + dev_dbg(dev, "%s GC5025_OTP_GOLDEN group %d is Valid !!\n", + __func__, index + 1); + checksum = 0; + golden_start_add = + GOLDEN_ROM_START_5025 + 8 * index * GOLDEN_WIDTH_5025; + tmpH = gc5025_otp_read_reg(client, + 1, golden_start_add); + checksum += tmpH; + tmpL = gc5025_otp_read_reg(client, + 1, golden_start_add + 8 * 1); + checksum += tmpL; + otp_p->golden_rg = (tmpH << 8) | tmpL; + tmpH = gc5025_otp_read_reg(client, + 1, golden_start_add + 8 * 2); + checksum += tmpH; + tmpL = gc5025_otp_read_reg(client, + 1, golden_start_add + 8 * 3); + checksum += tmpL; + otp_p->golden_bg = (tmpH << 8) | tmpL; + checksum = checksum % 255 + 1; + temp = gc5025_otp_read_reg(client, + 1, + 0x100 + 8 * index * GOLDEN_WIDTH_5025); + if (checksum == temp) { + dev_dbg(dev, "otp:(golden_rg 0x%x, golden_bg 0x%x)\n", + otp_p->golden_rg, otp_p->golden_bg); + } + break; + case 0x02: + case 0x03: + dev_err(dev, "%s GC5025_OTP_GOLDEN group %d is Invalid !!\n", + __func__, index + 1); + break; + default: + break; + } + } + + /* OTP DD calibration data */ + otp_flag = gc5025_otp_read_reg(client, 0, 0); + switch (otp_flag & 0x03) { + case 0x00: + dev_err(dev, "%s GC5025 OTP:flag_dd is EMPTY!\n", + __func__); + break; + case 0x01: + dev_dbg(dev, "%s GC5025 OTP:flag_dd is Valid!\n", + __func__); + checksum = 0; + total_number = gc5025_otp_read_reg(client, 0, 0x08) + + gc5025_otp_read_reg(client, 0, 0x10); + for (i = 0; i < 126; i++) { + m_DD_Otp_Value[i] = + gc5025_otp_read_reg(client, + 0, 0x08 + 8 * i); + checksum += m_DD_Otp_Value[i]; + } + for (i = 0; i < 56; i++) { + m_DD_Otp_Value[126 + i] = + gc5025_otp_read_reg(client, + 1, 0x148 + 8 * i); + checksum += m_DD_Otp_Value[126 + i]; + } + checksum = checksum % 255 + 1; + temp = gc5025_otp_read_reg(client, 1, 0x308); + if (checksum == temp) { + for (i = 0; i < total_number; i++) { + if (i < 31) { + page = 0; + dd_rom_start = 0x18; + offset = 0; + } else { + page = 1; + dd_rom_start = 0x148; + offset = 124;//31*4 + } + check_dd_flag = gc5025_otp_read_reg(client, + page, + dd_rom_start + 8 * (4 * i - offset + 3)); + if (check_dd_flag & 0x10) { + //Read OTP + type = check_dd_flag & 0x0f; + dd0 = gc5025_otp_read_reg(client, page, + dd_rom_start + 8 * (4 * i - offset)); + dd1 = gc5025_otp_read_reg(client, + page, + dd_rom_start + 8 * (4 * i - offset + 1)); + dd2 = gc5025_otp_read_reg(client, + page, + dd_rom_start + 8 * (4 * i - offset + 2)); + x = ((dd1 & 0x0f) << 8) + dd0; + y = (dd2 << 4) + ((dd1 & 0xf0) >> 4); + + if (type == 3) { + for (j = 0; j < 4; j++) { + otp_p->dd_param_x[cnt] = x; + otp_p->dd_param_y[cnt] = y + j; + otp_p->dd_param_type[cnt++] = 2; + } + } else if (type == 4) { + for (j = 0; j < 2; j++) { + otp_p->dd_param_x[cnt] = x; + otp_p->dd_param_y[cnt] = y + j; + otp_p->dd_param_type[cnt++] = 2; + } + } else { + otp_p->dd_param_x[cnt] = x; + otp_p->dd_param_y[cnt] = y; + otp_p->dd_param_type[cnt++] = type; + } + } else { + dev_err(dev, "%s GC5025_OTP_DD:check_id[%d] = %x,checkid error!!\n", + __func__, i, check_dd_flag); + } + } + otp_p->dd_cnt = cnt; + otp_p->flag |= 0x08; + } + break; + case 0x02: + case 0x03: + dev_err(dev, "%s GC5025 OTP:flag_dd is Invalid!\n", + __func__); + break; + default: + break; + } + + if (otp_p->flag) { + gc5025->otp = otp_p; + } else { + gc5025->otp = NULL; + devm_kfree(dev, otp_p); + } + + return 0; +} + +static void gc5025_get_otp(struct gc5025_otp_info *otp, + struct rkmodule_inf *inf) +{ + u32 i; + + /* fac */ + if (otp->flag & 0x80) { + inf->fac.flag = 1; + inf->fac.year = otp->year; + inf->fac.month = otp->month; + inf->fac.day = otp->day; + for (i = 0; i < ARRAY_SIZE(gc5025_module_info) - 1; i++) { + if (gc5025_module_info[i].id == otp->module_id) + break; + } + strlcpy(inf->fac.module, gc5025_module_info[i].name, + sizeof(inf->fac.module)); + + for (i = 0; i < ARRAY_SIZE(gc5025_lens_info) - 1; i++) { + if (gc5025_lens_info[i].id == otp->lens_id) + break; + } + strlcpy(inf->fac.lens, gc5025_lens_info[i].name, + sizeof(inf->fac.lens)); + } + /* awb */ + if (otp->flag & 0x40) { + inf->awb.flag = 1; + inf->awb.r_value = otp->rg_ratio; + inf->awb.b_value = otp->bg_ratio; + inf->awb.gr_value = 0; + inf->awb.gb_value = 0; + + inf->awb.golden_r_value = 0; + inf->awb.golden_b_value = 0; + inf->awb.golden_gr_value = 0; + inf->awb.golden_gb_value = 0; + } +} + +static void gc5025_get_module_inf(struct gc5025 *gc5025, + struct rkmodule_inf *inf) +{ + struct gc5025_otp_info *otp = gc5025->otp; + + strlcpy(inf->base.sensor, + GC5025_NAME, + sizeof(inf->base.sensor)); + strlcpy(inf->base.module, + gc5025->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, + gc5025->len_name, + sizeof(inf->base.lens)); + if (otp) + gc5025_get_otp(otp, inf); +} + +static void gc5025_set_module_inf(struct gc5025 *gc5025, + struct rkmodule_awb_cfg *cfg) +{ + mutex_lock(&gc5025->mutex); + memcpy(&gc5025->awb_cfg, cfg, sizeof(*cfg)); + mutex_unlock(&gc5025->mutex); +} + +static long gc5025_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct gc5025 *gc5025 = to_gc5025(sd); + long ret = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + gc5025_get_module_inf(gc5025, (struct rkmodule_inf *)arg); + break; + case RKMODULE_AWB_CFG: + gc5025_set_module_inf(gc5025, (struct rkmodule_awb_cfg *)arg); + break; + default: + ret = -ENOTTY; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long gc5025_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_awb_cfg *cfg; + long ret = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = gc5025_ioctl(sd, cmd, inf); + if (!ret) + ret = copy_to_user(up, inf, sizeof(*inf)); + kfree(inf); + break; + case RKMODULE_AWB_CFG: + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(cfg, up, sizeof(*cfg)); + if (!ret) + ret = gc5025_ioctl(sd, cmd, cfg); + kfree(cfg); + break; + default: + ret = -ENOTTY; + break; + } + + return ret; +} +#endif + +/*--------------------------------------------------------------------------*/ +static int gc5025_apply_otp(struct gc5025 *gc5025) +{ + int R_gain, G_gain, B_gain, base_gain; + struct i2c_client *client = gc5025->client; + struct gc5025_otp_info *otp_p = gc5025->otp; + struct rkmodule_awb_cfg *awb_cfg = &gc5025->awb_cfg; + u32 golden_bg_ratio; + u32 golden_rg_ratio; + u32 golden_g_value; + u16 i, j; + u16 temp_x = 0, temp_y = 0; + u8 temp_type = 0; + u8 temp_val0, temp_val1, temp_val2; + u16 column, ii, iii, jj; + + if (!gc5025->awb_cfg.enable) + return 0; + + golden_g_value = (awb_cfg->golden_gb_value + + awb_cfg->golden_gr_value) / 2; + golden_bg_ratio = + awb_cfg->golden_b_value * 0x400 / golden_g_value; + golden_rg_ratio = + awb_cfg->golden_r_value * 0x400 / golden_g_value; + /* apply OTP WB Calibration */ + if ((otp_p->flag & 0x40) && golden_bg_ratio && golden_rg_ratio) { + /* calculate G gain */ + R_gain = golden_rg_ratio * 1000 / otp_p->rg_ratio; + B_gain = golden_bg_ratio * 1000 / otp_p->bg_ratio; + G_gain = 1000; + base_gain = (R_gain < B_gain) ? R_gain : B_gain; + base_gain = (base_gain < G_gain) ? base_gain : G_gain; + + R_gain = 0x400 * R_gain / (base_gain); + B_gain = 0x400 * B_gain / (base_gain); + G_gain = 0x400 * G_gain / (base_gain); + + /* update sensor WB gain */ + gc5025_write_reg(client, 0xfe, 0x00); + gc5025_write_reg(client, 0xc6, + (G_gain & 0x7f8) >> 3); + gc5025_write_reg(client, 0xc7, + (R_gain & 0x7f8) >> 3); + gc5025_write_reg(client, 0xc8, + (B_gain & 0x7f8) >> 3); + gc5025_write_reg(client, 0xc9, + (G_gain & 0x7f8) >> 3); + gc5025_write_reg(client, 0xc4, + ((G_gain & 0X07) << 4) | (R_gain & 0x07)); + gc5025_write_reg(client, 0xc5, + ((B_gain & 0X07) << 4) | (G_gain & 0x07)); + dev_dbg(&client->dev, "apply awb gain: 0x%x, 0x%x, 0x%x\n", + R_gain, G_gain, B_gain); + } + + /* apply OTP DD Calibration */ + if (otp_p->flag & 0x08) { +#if defined IMAGE_NORMAL_MIRROR +#elif defined IMAGE_H_MIRROR + for (i = 0; i < otp_p->dd_cnt; i++) { + if (otp_p->dd_param_type[i] == 0) { + otp_p->dd_param_x[i] = + WINDOW_WIDTH - otp_p->dd_param_x[i] + 1; + } else if (otp_p->dd_param_type[i] == 1) { + otp_p->dd_param_x[i] = + WINDOW_WIDTH - otp_p->dd_param_x[i] - 1; + } else { + otp_p->dd_param_x[i] = + WINDOW_WIDTH - otp_p->dd_param_x[i]; + } + } +#elif defined IMAGE_V_MIRROR + for (i = 0; i < otp_p->dd_cnt; i++) { + otp_p->dd_param_y[i] = + WINDOW_HEIGHT - otp_p->dd_param_y[i] + 1; + } +#elif defined IMAGE_HV_MIRROR + for (i = 0; i < otp_p->dd_cnt; i++) { + if (otp_p->dd_param_type[i] == 0) { + otp_p->dd_param_x[i] = + WINDOW_WIDTH - otp_p->dd_param_x[i] + 1; + otp_p->dd_param_y[i] = + WINDOW_HEIGHT - otp_p->dd_param_y[i] + 1; + } else if (otp_p->dd_param_type[i] == 1) { + otp_p->dd_param_x[i] = + WINDOW_WIDTH - otp_p->dd_param_x[i] - 1; + otp_p->dd_param_y[i] = + WINDOW_HEIGHT - otp_p->dd_param_y[i] + 1; + } else { + otp_p->dd_param_x[i] = + WINDOW_WIDTH - otp_p->dd_param_x[i]; + otp_p->dd_param_y[i] = + WINDOW_HEIGHT - otp_p->dd_param_y[i] + 1; + } + } +#endif + //y + for (i = 0; i < otp_p->dd_cnt - 1; i++) { + for (j = 0; j < otp_p->dd_cnt - 1 - i; j++) { + if (otp_p->dd_param_y[j] > + otp_p->dd_param_y[j + 1]) { + temp_x = otp_p->dd_param_x[j]; + otp_p->dd_param_x[j] = + otp_p->dd_param_x[j + 1]; + otp_p->dd_param_x[j + 1] = + temp_x; + temp_y = + otp_p->dd_param_y[j]; + otp_p->dd_param_y[j] = + otp_p->dd_param_y[j + 1]; + otp_p->dd_param_y[j + 1] = + temp_y; + temp_type = + otp_p->dd_param_type[j]; + otp_p->dd_param_type[j] = + otp_p->dd_param_type[j + 1]; + otp_p->dd_param_type[j + 1] = + temp_type; + } + } + } + //x + column = 0; + for (i = 0 ; i < otp_p->dd_cnt - 1; ++i) { + if (otp_p->dd_param_y[i] == otp_p->dd_param_y[i + 1]) { + column++; + if (otp_p->dd_cnt - 2 != i) + continue; + } + if (otp_p->dd_cnt - 2 == i && + otp_p->dd_param_y[i] == otp_p->dd_param_y[i + 1]) { + i = otp_p->dd_cnt - 1; + } + iii = i - column; + for (ii = i - column; ii < i ; ++ii) { + for (jj = i - column; jj < + i - (ii - iii); ++jj) { + if (otp_p->dd_param_x[jj] > + otp_p->dd_param_x[jj + 1]) { + temp_x = otp_p->dd_param_x[jj]; + otp_p->dd_param_x[jj] = + otp_p->dd_param_x[jj + 1]; + otp_p->dd_param_x[jj + 1] = + temp_x; + temp_y = + otp_p->dd_param_y[jj]; + otp_p->dd_param_y[jj] = + otp_p->dd_param_y[jj + 1]; + otp_p->dd_param_y[jj + 1] = + temp_y; + temp_type = + otp_p->dd_param_type[jj]; + otp_p->dd_param_type[jj] = + otp_p->dd_param_type[jj + 1]; + otp_p->dd_param_type[jj + 1] = + temp_type; + } + } + } + column = 0; + } + + //write SRAM + gc5025_write_reg(client, 0xfe, 0x00); + gc5025_write_reg(client, 0x80, 0x50); + gc5025_write_reg(client, 0xfe, 0x01); + gc5025_write_reg(client, 0xa8, 0x00); + gc5025_write_reg(client, 0x9d, 0x04); + gc5025_write_reg(client, 0xbe, 0x00); + gc5025_write_reg(client, 0xa9, 0x01); + + for (i = 0; i < otp_p->dd_cnt; i++) { + temp_val0 = otp_p->dd_param_x[i] & 0x00ff; + temp_val1 = ((otp_p->dd_param_y[i] << 4) & 0x00f0) + + ((otp_p->dd_param_x[i] >> 8) & 0x000f); + temp_val2 = (otp_p->dd_param_y[i] >> 4) & 0xff; + gc5025_write_reg(client, 0xaa, i); + gc5025_write_reg(client, 0xac, temp_val0); + gc5025_write_reg(client, 0xac, temp_val1); + gc5025_write_reg(client, 0xac, temp_val2); + gc5025_write_reg(client, 0xac, + otp_p->dd_param_type[i]); + } + gc5025_write_reg(client, 0xbe, 0x01); + gc5025_write_reg(client, 0xfe, 0x00); + } + return 0; +} + +static int __gc5025_start_stream(struct gc5025 *gc5025) +{ + int ret; + + ret = gc5025_write_array(gc5025->client, gc5025->cur_mode->reg_list); + if (ret) + return ret; + + if (gc5025->DR_State) { + ret = gc5025_write_array(gc5025->client, + gc5025_doublereset_reg); + } else { + ret = gc5025_write_array(gc5025->client, + gc5025_disable_doublereset_reg); + } + if (ret) + return ret; + /* In case these controls are set before streaming */ + mutex_unlock(&gc5025->mutex); + ret = v4l2_ctrl_handler_setup(&gc5025->ctrl_handler); + mutex_lock(&gc5025->mutex); + if (ret) + return ret; + if (gc5025->otp) { + ret = gc5025_otp_enable(gc5025); + ret |= gc5025_apply_otp(gc5025); + ret |= gc5025_otp_disable(gc5025); + if (ret) + return ret; + } + ret = gc5025_write_reg(gc5025->client, + GC5025_REG_SET_PAGE, + GC5025_SET_PAGE_ONE); + ret |= gc5025_write_reg(gc5025->client, + GC5025_REG_CTRL_MODE, + GC5025_MODE_STREAMING); + return ret; +} + +static int __gc5025_stop_stream(struct gc5025 *gc5025) +{ + int ret; + + ret = gc5025_write_reg(gc5025->client, + GC5025_REG_SET_PAGE, + GC5025_SET_PAGE_ONE); + ret |= gc5025_write_reg(gc5025->client, + GC5025_REG_CTRL_MODE, + GC5025_MODE_SW_STANDBY); + return ret; +} + +static int gc5025_s_stream(struct v4l2_subdev *sd, int on) +{ + struct gc5025 *gc5025 = to_gc5025(sd); + struct i2c_client *client = gc5025->client; + int ret = 0; + + mutex_lock(&gc5025->mutex); + on = !!on; + if (on == gc5025->streaming) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = __gc5025_start_stream(gc5025); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + __gc5025_stop_stream(gc5025); + pm_runtime_put(&client->dev); + } + + gc5025->streaming = on; + +unlock_and_return: + mutex_unlock(&gc5025->mutex); + + return ret; +} + +static int gc5025_s_power(struct v4l2_subdev *sd, int on) +{ + struct gc5025 *gc5025 = to_gc5025(sd); + struct i2c_client *client = gc5025->client; + int ret = 0; + + mutex_lock(&gc5025->mutex); + + /* If the power state is not modified - no work to do. */ + if (gc5025->power_on == !!on) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = gc5025_write_array(gc5025->client, gc5025_global_regs); + if (ret) { + v4l2_err(sd, "could not set init registers\n"); + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + gc5025->power_on = true; + } else { + pm_runtime_put(&client->dev); + gc5025->power_on = false; + } + +unlock_and_return: + mutex_unlock(&gc5025->mutex); + + return ret; +} + +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 gc5025_cal_delay(u32 cycles) +{ + return DIV_ROUND_UP(cycles, GC5025_XVCLK_FREQ / 1000 / 1000); +} + +static int __gc5025_power_on(struct gc5025 *gc5025) +{ + int ret; + u32 delay_us; + struct device *dev = &gc5025->client->dev; + + if (!IS_ERR_OR_NULL(gc5025->pins_default)) { + ret = pinctrl_select_state(gc5025->pinctrl, + gc5025->pins_default); + if (ret < 0) + dev_err(dev, "could not set pins\n"); + } + ret = clk_set_rate(gc5025->xvclk, GC5025_XVCLK_FREQ); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate (24MHz)\n"); + if (clk_get_rate(gc5025->xvclk) != GC5025_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); + ret = clk_prepare_enable(gc5025->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + if (!IS_ERR(gc5025->reset_gpio)) + gpiod_set_value_cansleep(gc5025->reset_gpio, 1); + + ret = regulator_bulk_enable(GC5025_NUM_SUPPLIES, gc5025->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + + usleep_range(1000, 1100); + if (!IS_ERR(gc5025->reset_gpio)) + gpiod_set_value_cansleep(gc5025->reset_gpio, 0); + + usleep_range(500, 1000); + if (!IS_ERR(gc5025->pwdn_gpio)) + gpiod_set_value_cansleep(gc5025->pwdn_gpio, 0); + + /* 8192 cycles prior to first SCCB transaction */ + delay_us = gc5025_cal_delay(8192); + usleep_range(delay_us, delay_us * 2); + + return 0; + +disable_clk: + clk_disable_unprepare(gc5025->xvclk); + + return ret; +} + +static void __gc5025_power_off(struct gc5025 *gc5025) +{ + int ret; + + if (!IS_ERR(gc5025->pwdn_gpio)) + gpiod_set_value_cansleep(gc5025->pwdn_gpio, 1); + clk_disable_unprepare(gc5025->xvclk); + if (!IS_ERR(gc5025->reset_gpio)) + gpiod_set_value_cansleep(gc5025->reset_gpio, 1); + if (!IS_ERR_OR_NULL(gc5025->pins_sleep)) { + ret = pinctrl_select_state(gc5025->pinctrl, + gc5025->pins_sleep); + if (ret < 0) + dev_dbg(&gc5025->client->dev, "could not set pins\n"); + } + regulator_bulk_disable(GC5025_NUM_SUPPLIES, gc5025->supplies); +} + +static int gc5025_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc5025 *gc5025 = to_gc5025(sd); + + return __gc5025_power_on(gc5025); +} + +static int gc5025_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc5025 *gc5025 = to_gc5025(sd); + + __gc5025_power_off(gc5025); + + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int gc5025_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct gc5025 *gc5025 = to_gc5025(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + const struct gc5025_mode *def_mode = &supported_modes[0]; + + mutex_lock(&gc5025->mutex); + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = MEDIA_BUS_FMT_SRGGB10_1X10; + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&gc5025->mutex); + /* No crop or compose */ + + return 0; +} +#endif + +static const struct dev_pm_ops gc5025_pm_ops = { + SET_RUNTIME_PM_OPS(gc5025_runtime_suspend, + gc5025_runtime_resume, NULL) +}; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops gc5025_internal_ops = { + .open = gc5025_open, +}; +#endif + +static const struct v4l2_subdev_core_ops gc5025_core_ops = { + .s_power = gc5025_s_power, + .ioctl = gc5025_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = gc5025_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops gc5025_video_ops = { + .s_stream = gc5025_s_stream, + .g_frame_interval = gc5025_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops gc5025_pad_ops = { + .enum_mbus_code = gc5025_enum_mbus_code, + .enum_frame_size = gc5025_enum_frame_sizes, + .get_fmt = gc5025_get_fmt, + .set_fmt = gc5025_set_fmt, +}; + +static const struct v4l2_subdev_ops gc5025_subdev_ops = { + .core = &gc5025_core_ops, + .video = &gc5025_video_ops, + .pad = &gc5025_pad_ops, +}; + +static int gc5025_set_exposure_reg(struct gc5025 *gc5025, u32 exposure) +{ + u32 caltime = 0; + int ret = 0; + + caltime = exposure / 2; + caltime = caltime * 2; + gc5025->Dgain_ratio = 256 * exposure / caltime; + ret = gc5025_write_reg(gc5025->client, + GC5025_REG_SET_PAGE, + GC5025_SET_PAGE_ONE); + if (!gc5025->DR_State) { + if (caltime <= 10) + ret |= gc5025_write_reg(gc5025->client, 0xd9, 0xdd); + else + ret |= gc5025_write_reg(gc5025->client, 0xd9, 0xaa); + } + ret |= gc5025_write_reg(gc5025->client, + GC5025_REG_EXPOSURE_H, + (caltime >> 8) & 0x3F); + ret |= gc5025_write_reg(gc5025->client, + GC5025_REG_EXPOSURE_L, + caltime & 0xFF); + return ret; +} + +#define GC5025_ANALOG_GAIN_1 64 /*1.00x*/ +#define GC5025_ANALOG_GAIN_2 92 // 1.445x + +static int gc5025_set_gain_reg(struct gc5025 *gc5025, u32 a_gain) +{ + int ret = 0; + u32 temp = 0; + + if (a_gain < 0x40) + a_gain = 0x40; + ret = gc5025_write_reg(gc5025->client, + GC5025_REG_SET_PAGE, + GC5025_SET_PAGE_ONE); + if (a_gain >= GC5025_ANALOG_GAIN_1 && + a_gain < GC5025_ANALOG_GAIN_2) { + ret |= gc5025_write_reg(gc5025->client, + GC5025_REG_AGAIN, 0x0); + temp = a_gain; + } else { + ret |= gc5025_write_reg(gc5025->client, + GC5025_REG_AGAIN, 0x1); + temp = 64 * a_gain / GC5025_ANALOG_GAIN_2; + } + temp = temp * gc5025->Dgain_ratio / 256; + ret |= gc5025_write_reg(gc5025->client, + GC5025_REG_DGAIN_INT, + temp >> 6); + ret |= gc5025_write_reg(gc5025->client, + GC5025_REG_DGAIN_FRAC, + (temp << 2) & 0xfc); + return ret; +} + +static int gc5025_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gc5025 *gc5025 = container_of(ctrl->handler, + struct gc5025, ctrl_handler); + struct i2c_client *client = gc5025->client; + s64 max; + int ret = 0; + + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ + max = gc5025->cur_mode->height + ctrl->val - 4; + __v4l2_ctrl_modify_range(gc5025->exposure, + gc5025->exposure->minimum, max, + gc5025->exposure->step, + gc5025->exposure->default_value); + break; + } + + if (pm_runtime_get(&client->dev) <= 0) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + /* 4 least significant bits of expsoure are fractional part */ + ret = gc5025_set_exposure_reg(gc5025, ctrl->val); + break; + case V4L2_CID_ANALOGUE_GAIN: + ret = gc5025_set_gain_reg(gc5025, ctrl->val); + break; + case V4L2_CID_VBLANK: + ret = gc5025_write_reg(gc5025->client, + GC5025_REG_SET_PAGE, + GC5025_SET_PAGE_ONE); + ret |= gc5025_write_reg(gc5025->client, + GC5025_REG_VTS_H, + ((ctrl->val - 24) >> 8) & 0xff); + ret |= gc5025_write_reg(gc5025->client, + GC5025_REG_VTS_L, + (ctrl->val - 24) & 0xff); + break; + default: + dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", + __func__, ctrl->id, ctrl->val); + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops gc5025_ctrl_ops = { + .s_ctrl = gc5025_set_ctrl, +}; + +static int gc5025_initialize_controls(struct gc5025 *gc5025) +{ + const struct gc5025_mode *mode; + struct v4l2_ctrl_handler *handler; + struct v4l2_ctrl *ctrl; + s64 exposure_max, vblank_def; + u32 h_blank; + int ret; + + handler = &gc5025->ctrl_handler; + mode = gc5025->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 8); + if (ret) + return ret; + handler->lock = &gc5025->mutex; + + ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, + 0, 0, link_freq_menu_items); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, + 0, GC5025_PIXEL_RATE, 1, GC5025_PIXEL_RATE); + + h_blank = mode->hts_def - mode->width; + gc5025->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (gc5025->hblank) + gc5025->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = mode->vts_def - mode->height; + gc5025->vblank = v4l2_ctrl_new_std(handler, &gc5025_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + GC5025_VTS_MAX - mode->height, + 1, vblank_def); + + exposure_max = mode->vts_def - 4; + gc5025->exposure = v4l2_ctrl_new_std(handler, &gc5025_ctrl_ops, + V4L2_CID_EXPOSURE, GC5025_EXPOSURE_MIN, + exposure_max, GC5025_EXPOSURE_STEP, + mode->exp_def); + + gc5025->anal_gain = v4l2_ctrl_new_std(handler, &gc5025_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, GC5025_GAIN_MIN, + GC5025_GAIN_MAX, GC5025_GAIN_STEP, + GC5025_GAIN_DEFAULT); + + if (handler->error) { + ret = handler->error; + dev_err(&gc5025->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + gc5025->subdev.ctrl_handler = handler; + + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +static int gc5025_check_sensor_id(struct gc5025 *gc5025, + struct i2c_client *client) +{ + struct device *dev = &gc5025->client->dev; + u16 id = 0; + u8 reg_H = 0; + u8 reg_L = 0; + u8 flag_doublereset = 0; + u8 flag_GC5025A = 0; + int ret; + + ret = gc5025_read_reg(client, GC5025_REG_CHIP_ID_H, ®_H); + ret |= gc5025_read_reg(client, GC5025_REG_CHIP_ID_L, ®_L); + id = ((reg_H << 8) & 0xff00) | (reg_L & 0xff); + if (id != CHIP_ID) { + dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret); + return -ENODEV; + } + ret |= gc5025_read_reg(client, 0x26, &flag_doublereset); + ret |= gc5025_read_reg(client, 0x27, &flag_GC5025A); + if ((flag_GC5025A & 0x01) == 0x01) { + dev_warn(dev, "GC5025A sensor!\n"); + gc5025->DR_State = false; + } else { + if ((flag_doublereset & 0x03) == 0x01) { + gc5025->DR_State = false; + dev_warn(dev, "GC5025 double reset off\n"); + } else { + gc5025->DR_State = true; + dev_warn(dev, "GC5025 double reset on\n"); + } + } + return ret; +} + +static int gc5025_configure_regulators(struct gc5025 *gc5025) +{ + unsigned int i; + + for (i = 0; i < GC5025_NUM_SUPPLIES; i++) + gc5025->supplies[i].supply = gc5025_supply_names[i]; + + return devm_regulator_bulk_get(&gc5025->client->dev, + GC5025_NUM_SUPPLIES, + gc5025->supplies); +} + +static int gc5025_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct gc5025 *gc5025; + struct v4l2_subdev *sd; + char facing[2]; + int ret; + + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + gc5025 = devm_kzalloc(dev, sizeof(*gc5025), GFP_KERNEL); + if (!gc5025) + return -ENOMEM; + + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &gc5025->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &gc5025->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &gc5025->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &gc5025->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + gc5025->client = client; + gc5025->cur_mode = &supported_modes[0]; + + gc5025->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(gc5025->xvclk)) { + dev_err(dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + gc5025->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(gc5025->reset_gpio)) + dev_warn(dev, "Failed to get reset-gpios\n"); + + gc5025->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(gc5025->pwdn_gpio)) + dev_warn(dev, "Failed to get pwdn-gpios\n"); + + ret = gc5025_configure_regulators(gc5025); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + + gc5025->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR(gc5025->pinctrl)) { + gc5025->pins_default = + pinctrl_lookup_state(gc5025->pinctrl, + OF_CAMERA_PINCTRL_STATE_DEFAULT); + if (IS_ERR(gc5025->pins_default)) + dev_err(dev, "could not get default pinstate\n"); + + gc5025->pins_sleep = + pinctrl_lookup_state(gc5025->pinctrl, + OF_CAMERA_PINCTRL_STATE_SLEEP); + if (IS_ERR(gc5025->pins_sleep)) + dev_err(dev, "could not get sleep pinstate\n"); + } + + mutex_init(&gc5025->mutex); + + sd = &gc5025->subdev; + v4l2_i2c_subdev_init(sd, client, &gc5025_subdev_ops); + ret = gc5025_initialize_controls(gc5025); + if (ret) + goto err_destroy_mutex; + + ret = __gc5025_power_on(gc5025); + if (ret) + goto err_free_handler; + + ret = gc5025_check_sensor_id(gc5025, client); + if (ret) + goto err_power_off; + + gc5025_otp_enable(gc5025); + gc5025_otp_read(gc5025); + gc5025_otp_disable(gc5025); + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &gc5025_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + gc5025->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &gc5025->pad); + if (ret < 0) + goto err_power_off; +#endif + + memset(facing, 0, sizeof(facing)); + if (strcmp(gc5025->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + gc5025->module_index, facing, + GC5025_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor_common(sd); + if (ret) { + dev_err(dev, "v4l2 async register subdev failed\n"); + goto err_clean_entity; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; + +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif +err_power_off: + __gc5025_power_off(gc5025); +err_free_handler: + v4l2_ctrl_handler_free(&gc5025->ctrl_handler); +err_destroy_mutex: + mutex_destroy(&gc5025->mutex); + + return ret; +} + +static int gc5025_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc5025 *gc5025 = to_gc5025(sd); + + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + v4l2_ctrl_handler_free(&gc5025->ctrl_handler); + mutex_destroy(&gc5025->mutex); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __gc5025_power_off(gc5025); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id gc5025_of_match[] = { + { .compatible = "galaxycore,gc5025" }, + {}, +}; +MODULE_DEVICE_TABLE(of, gc5025_of_match); +#endif + +static const struct i2c_device_id gc5025_match_id[] = { + { "galaxycore,gc5025", 0 }, + { }, +}; + +static struct i2c_driver gc5025_i2c_driver = { + .driver = { + .name = GC5025_NAME, + .pm = &gc5025_pm_ops, + .of_match_table = of_match_ptr(gc5025_of_match), + }, + .probe = &gc5025_probe, + .remove = &gc5025_remove, + .id_table = gc5025_match_id, +}; + +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&gc5025_i2c_driver); +} + +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&gc5025_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_DESCRIPTION("GalaxyCore gc5025 sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/gc5035.c b/drivers/media/i2c/gc5035.c new file mode 100644 index 000000000000..ba634b20ce74 --- /dev/null +++ b/drivers/media/i2c/gc5035.c @@ -0,0 +1,1598 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * gc5035 driver + * + * Copyright (C) 2019 Fuzhou Rockchip Electronics Co., Ltd. + * + * V0.0X01.0X01 init driver. + * TODO: add OTP function. + * V0.0X01.0X02 fix mclk issue when probe multiple camera. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x02) + +#ifndef V4L2_CID_DIGITAL_GAIN +#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN +#endif + +#define GC5035_LANES 2 +#define GC5035_BITS_PER_SAMPLE 10 +#define GC5035_LINK_FREQ_MHZ 438000000LL +#define MIPI_FREQ 438000000LL + +/* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ +#define GC5035_PIXEL_RATE (MIPI_FREQ * 2LL * 2LL / 10) +#define GC5035_XVCLK_FREQ 24000000 + +#define CHIP_ID 0x5035 +#define GC5035_REG_CHIP_ID_H 0xf0 +#define GC5035_REG_CHIP_ID_L 0xf1 + +#define GC5035_REG_SET_PAGE 0xfe +#define GC5035_SET_PAGE_ONE 0x00 + +#define GC5035_REG_CTRL_MODE 0x3e +#define GC5035_MODE_SW_STANDBY 0x01 +#define GC5035_MODE_STREAMING 0x91 + +#define GC5035_REG_EXPOSURE_H 0x03 +#define GC5035_REG_EXPOSURE_L 0x04 +#define GC5035_FETCH_HIGH_BYTE_EXP(VAL) (((VAL) >> 8) & 0x0F) /* 4 Bits */ +#define GC5035_FETCH_LOW_BYTE_EXP(VAL) ((VAL) & 0xFF) /* 8 Bits */ +#define GC5035_EXPOSURE_MIN 4 +#define GC5035_EXPOSURE_STEP 1 +#define GC5035_VTS_MAX 0x1fff + +#define GC5035_REG_AGAIN 0xb6 +#define GC5035_REG_DGAIN_INT 0xb1 +#define GC5035_REG_DGAIN_FRAC 0xb2 +#define GC5035_GAIN_MIN 64 +#define GC5035_GAIN_MAX 1024 +#define GC5035_GAIN_STEP 1 +#define GC5035_GAIN_DEFAULT 64 + +#define GC5035_REG_VTS_H 0x41 +#define GC5035_REG_VTS_L 0x42 + +#define REG_NULL 0xFF + +#define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default" +#define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep" + +#define GC5035_NAME "gc5035" + +static const char * const gc5035_supply_names[] = { + "avdd", /* Analog power */ + "dovdd", /* Digital I/O power */ + "dvdd", /* Digital core power */ +}; + +#define GC5035_NUM_SUPPLIES ARRAY_SIZE(gc5035_supply_names) + +#define IMAGE_NORMAL_MIRROR +#define DD_PARAM_QTY_5035 200 +#define INFO_ROM_START_5035 0x08 +#define INFO_WIDTH_5035 0x08 +#define WB_ROM_START_5035 0x88 +#define WB_WIDTH_5035 0x05 +#define GOLDEN_ROM_START_5035 0xe0 +#define GOLDEN_WIDTH_5035 0x05 +#define WINDOW_WIDTH 0x0a30 +#define WINDOW_HEIGHT 0x079c + +/* SENSOR MIRROR FLIP INFO */ +#define GC5035_MIRROR_FLIP_ENABLE 0 +#if GC5035_MIRROR_FLIP_ENABLE +#define GC5035_MIRROR 0x83 +#define GC5035_RSTDUMMY1 0x03 +#define GC5035_RSTDUMMY2 0xfc +#else +#define GC5035_MIRROR 0x80 +#define GC5035_RSTDUMMY1 0x02 +#define GC5035_RSTDUMMY2 0x7c +#endif + +struct gc5035_otp_info { + u32 flag; //bit[7]: info bit[6]:wb bit[3]:dd + u32 module_id; + u32 lens_id; + u16 vcm_id; + u16 vcm_driver_id; + u32 year; + u32 month; + u32 day; + u32 rg_ratio; + u32 bg_ratio; + u32 golden_rg; + u32 golden_bg; + u16 dd_param_x[DD_PARAM_QTY_5035]; + u16 dd_param_y[DD_PARAM_QTY_5035]; + u16 dd_param_type[DD_PARAM_QTY_5035]; + u16 dd_cnt; +}; + +struct gc5035_id_name { + u32 id; + char name[RKMODULE_NAME_LEN]; +}; + +struct regval { + u8 addr; + u8 val; +}; + +struct gc5035_mode { + u32 width; + u32 height; + struct v4l2_fract max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + const struct regval *reg_list; +}; + +struct gc5035 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; + struct regulator_bulk_data supplies[GC5035_NUM_SUPPLIES]; + + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_sleep; + + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *anal_gain; + struct v4l2_ctrl *digi_gain; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *test_pattern; + struct mutex mutex; + bool streaming; + bool power_on; + const struct gc5035_mode *cur_mode; + unsigned int lane_num; + unsigned int cfg_num; + unsigned int pixel_rate; + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; + u32 Dgain_ratio; + struct gc5035_otp_info *otp; + struct rkmodule_inf module_inf; + struct rkmodule_awb_cfg awb_cfg; +}; + +#define to_gc5035(sd) container_of(sd, struct gc5035, subdev) + +/* + * Xclk 24Mhz + */ +static const struct regval gc5035_global_regs[] = { + /* SYSTEM */ + {0xfc, 0x01}, + {0xf4, 0x40}, + {0xf5, 0xe9}, + {0xf6, 0x14}, + {0xf8, 0x49}, + {0xf9, 0x82}, + {0xfa, 0x00}, + {0xfc, 0x81}, + {0xfe, 0x00}, + {0x36, 0x01}, + {0xd3, 0x87}, + {0x36, 0x00}, + {0x33, 0x00}, + {0xfe, 0x03}, + {0x01, 0xe7}, + {0xf7, 0x01}, + {0xfc, 0x8f}, + {0xfc, 0x8f}, + {0xfc, 0x8e}, + {0xfe, 0x00}, + {0xee, 0x30}, + {0x87, 0x18}, + {0xfe, 0x01}, + {0x8c, 0x90}, + {0xfe, 0x00}, + + /* Analog & CISCTL */ + {0xfe, 0x00}, + {0x05, 0x02}, + {0x06, 0xda}, + {0x9d, 0x0c}, + {0x09, 0x00}, + {0x0a, 0x04}, + {0x0b, 0x00}, + {0x0c, 0x03}, + {0x0d, 0x07}, + {0x0e, 0xa8}, + {0x0f, 0x0a}, + {0x10, 0x30}, + {0x11, 0x02}, + {0x17, GC5035_MIRROR}, + {0x19, 0x05}, + {0xfe, 0x02}, + {0x30, 0x03}, + {0x31, 0x03}, + {0xfe, 0x00}, + {0xd9, 0xc0}, + {0x1b, 0x20}, + {0x21, 0x48}, + {0x28, 0x22}, + {0x29, 0x58}, + {0x44, 0x20}, + {0x4b, 0x10}, + {0x4e, 0x1a}, + {0x50, 0x11}, + {0x52, 0x33}, + {0x53, 0x44}, + {0x55, 0x10}, + {0x5b, 0x11}, + {0xc5, 0x02}, + {0x8c, 0x1a}, + {0xfe, 0x02}, + {0x33, 0x05}, + {0x32, 0x38}, + {0xfe, 0x00}, + {0x91, 0x80}, + {0x92, 0x28}, + {0x93, 0x20}, + {0x95, 0xa0}, + {0x96, 0xe0}, + {0xd5, 0xfc}, + {0x97, 0x28}, + {0x16, 0x0c}, + {0x1a, 0x1a}, + {0x1f, 0x11}, + {0x20, 0x10}, + {0x46, 0xe3}, + {0x4a, 0x04}, + {0x54, GC5035_RSTDUMMY1}, + {0x62, 0x00}, + {0x72, 0xcf}, + {0x73, 0xc9}, + {0x7a, 0x05}, + {0x7d, 0xcc}, + {0x90, 0x00}, + {0xce, 0x98}, + {0xd0, 0xb2}, + {0xd2, 0x40}, + {0xe6, 0xe0}, + {0xfe, 0x02}, + {0x12, 0x01}, + {0x13, 0x01}, + {0x14, 0x01}, + {0x15, 0x02}, + {0x22, GC5035_RSTDUMMY2}, + {0x91, 0x00}, + {0x92, 0x00}, + {0x93, 0x00}, + {0x94, 0x00}, + {0xfe, 0x00}, + {0xfc, 0x88}, + {0xfe, 0x10}, + {0xfe, 0x00}, + {0xfc, 0x8e}, + {0xfe, 0x00}, + {0xfe, 0x00}, + {0xfe, 0x00}, + {0xfc, 0x88}, + {0xfe, 0x10}, + {0xfe, 0x00}, + {0xfc, 0x8e}, + + /* Gain */ + {0xfe, 0x00}, + {0xb0, 0x6e}, + {0xb1, 0x01}, + {0xb2, 0x00}, + {0xb3, 0x00}, + {0xb4, 0x00}, + {0xb6, 0x00}, + + /* ISP */ + {0xfe, 0x01}, + {0x53, 0x00}, + {0x89, 0x03}, + {0x60, 0x40}, + {0x87, 0x50}, + + /* BLK */ + {0xfe, 0x01}, + {0x42, 0x21}, + {0x49, 0x03}, + {0x4a, 0xff}, + {0x4b, 0xc0}, + {0x55, 0x00}, + + /* Anti_blooming */ + {0xfe, 0x01}, + {0x41, 0x28}, + {0x4c, 0x00}, + {0x4d, 0x00}, + {0x4e, 0x3c}, + {0x44, 0x08}, + {0x48, 0x01}, + + /* Crop */ + {0xfe, 0x01}, + {0x91, 0x00}, + {0x92, 0x08}, + {0x93, 0x00}, + {0x94, 0x07}, + {0x95, 0x07}, + {0x96, 0x98}, + {0x97, 0x0a}, + {0x98, 0x20}, + {0x99, 0x00}, + + /* MIPI */ + {0xfe, 0x03}, + {0x02, 0x57}, + {0x03, 0xb7}, + {0x15, 0x14}, + {0x18, 0x0f}, + {0x21, 0x22}, + {0x22, 0x06}, + {0x23, 0x48}, + {0x24, 0x12}, + {0x25, 0x28}, + {0x26, 0x08}, + {0x29, 0x06}, + {0x2a, 0x58}, + {0x2b, 0x08}, + {0xfe, 0x01}, + {0x8c, 0x10}, + + {0xfe, 0x00}, + {0x3e, 0x01}, + {REG_NULL, 0x00}, +}; + +/* + * Xclk 24Mhz + * max_framerate 30fps + * mipi_datarate per lane 876Mbps + */ +static const struct regval gc5035_2592x1944_regs[] = { + /* lane snap */ + {0xfc, 0x01}, + {0xf4, 0x40}, + {0xf5, 0xe9}, + {0xf6, 0x14}, + {0xf8, 0x49}, + {0xf9, 0x82}, + {0xfa, 0x00}, + {0xfc, 0x81}, + {0xfe, 0x00}, + {0x36, 0x01}, + {0xd3, 0x87}, + {0x36, 0x00}, + {0x33, 0x00}, + {0xfe, 0x03}, + {0x01, 0xe7}, + {0xf7, 0x01}, + {0xfc, 0x8f}, + {0xfc, 0x8f}, + {0xfc, 0x8e}, + {0xfe, 0x00}, + {0xee, 0x30}, + {0x87, 0x18}, + {0xfe, 0x01}, + {0x8c, 0x90}, + {0xfe, 0x00}, + + /*Analog & CISCTL*/ + {0xfe, 0x00}, + {0x05, 0x02}, + {0x06, 0xda}, + {0x9d, 0x0c}, + {0x09, 0x00}, + {0x0a, 0x04}, + {0x0b, 0x00}, + {0x0c, 0x03}, + {0x0d, 0x07}, + {0x0e, 0xa8}, + {0x0f, 0x0a}, + {0x10, 0x30}, + {0xd9, 0xc0}, + {0x21, 0x48}, + {0x29, 0x58}, + {0x44, 0x20}, + {0x4e, 0x1a}, + {0x8c, 0x1a}, + {0x91, 0x80}, + {0x92, 0x28}, + {0x93, 0x20}, + {0x95, 0xa0}, + {0x96, 0xe0}, + {0xd5, 0xfc}, + {0x97, 0x28}, + {0x1f, 0x11}, + {0xce, 0x98}, + {0xd0, 0xb2}, + {0xfe, 0x02}, + {0x14, 0x01}, + {0x15, 0x02}, + {0x91, 0x00}, + {0x92, 0x00}, + {0xfe, 0x00}, + {0xfc, 0x88}, + {0xfe, 0x10}, + {0xfe, 0x00}, + {0xfc, 0x8e}, + {0xfe, 0x00}, + {0xfe, 0x00}, + {0xfe, 0x00}, + {0xfc, 0x88}, + {0xfe, 0x10}, + {0xfe, 0x00}, + {0xfc, 0x8e}, + /*BLK*/ + {0xfe, 0x01}, + {0x49, 0x03}, + {0x4a, 0xff}, + {0x4b, 0xc0}, + /*anti_blooming*/ + {0xfe, 0x01}, + {0x4e, 0x3c}, + {0x44, 0x08}, + /*CROP*/ + {0xfe, 0x01}, + {0x91, 0x00}, + {0x92, 0x08}, + {0x93, 0x00}, + {0x94, 0x07}, + {0x95, 0x07}, + {0x96, 0x98}, + {0x97, 0x0a}, + {0x98, 0x20}, + {0x99, 0x00}, + /*MIPI*/ + {0xfe, 0x03}, + {0x02, 0x57}, + {0x22, 0x06}, + {0x26, 0x08}, + {0x29, 0x06}, + {0x2b, 0x08}, + {0xfe, 0x01}, + {0x8c, 0x10}, + {0xfe, 0x00}, + {REG_NULL, 0x00}, +}; + +static const struct gc5035_mode supported_modes_2lane[] = { + { + .width = 2592, + .height = 1944, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x07C0, + .hts_def = 0x0B68, + .vts_def = 0x07D0, + .reg_list = gc5035_2592x1944_regs, + }, +}; + +static const struct gc5035_mode *supported_modes; + +static const s64 link_freq_menu_items[] = { + GC5035_LINK_FREQ_MHZ +}; + +/* Write registers up to 4 at a time */ +static int gc5035_write_reg(struct i2c_client *client, u8 reg, u8 val) +{ + struct i2c_msg msg; + u8 buf[2]; + int ret; + + dev_dbg(&client->dev, "write reg(0x%x val:0x%x)!\n", reg, val); + buf[0] = reg & 0xFF; + buf[1] = val; + + msg.addr = client->addr; + msg.flags = client->flags; + msg.buf = buf; + msg.len = sizeof(buf); + + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret >= 0) + return 0; + + dev_err(&client->dev, + "gc5035 write reg(0x%x val:0x%x) failed !\n", reg, val); + + return ret; +} + +static int gc5035_write_array(struct i2c_client *client, + const struct regval *regs) +{ + u32 i = 0; + int ret = 0; + + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) + ret = gc5035_write_reg(client, regs[i].addr, regs[i].val); + + return ret; +} + +/* Read registers up to 4 at a time */ +static int gc5035_read_reg(struct i2c_client *client, u8 reg, u8 *val) +{ + struct i2c_msg msg[2]; + u8 buf[1]; + int ret; + + buf[0] = reg & 0xFF; + + msg[0].addr = client->addr; + msg[0].flags = client->flags; + msg[0].buf = buf; + msg[0].len = sizeof(buf); + + msg[1].addr = client->addr; + msg[1].flags = client->flags | I2C_M_RD; + msg[1].buf = buf; + msg[1].len = 1; + + ret = i2c_transfer(client->adapter, msg, 2); + if (ret >= 0) { + *val = buf[0]; + return 0; + } + + dev_err(&client->dev, + "gc5035 read reg:0x%x failed !\n", reg); + + return ret; +} + +static int gc5035_get_reso_dist(const struct gc5035_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); +} + +static const struct gc5035_mode * +gc5035_find_best_fit(struct gc5035 *gc5035, + struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *framefmt = &fmt->format; + int dist; + int cur_best_fit = 0; + int cur_best_fit_dist = -1; + unsigned int i; + + for (i = 0; i < gc5035->cfg_num; i++) { + dist = gc5035_get_reso_dist(&supported_modes[i], framefmt); + if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { + cur_best_fit_dist = dist; + cur_best_fit = i; + } + } + + return &supported_modes[cur_best_fit]; +} + +static int gc5035_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct gc5035 *gc5035 = to_gc5035(sd); + const struct gc5035_mode *mode; + s64 h_blank, vblank_def; + + mutex_lock(&gc5035->mutex); + + mode = gc5035_find_best_fit(gc5035, fmt); + fmt->format.code = MEDIA_BUS_FMT_SRGGB10_1X10; + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; +#else + mutex_unlock(&gc5035->mutex); + return -ENOTTY; +#endif + } else { + gc5035->cur_mode = mode; + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(gc5035->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(gc5035->vblank, vblank_def, + GC5035_VTS_MAX - mode->height, + 1, vblank_def); + } + + mutex_unlock(&gc5035->mutex); + + return 0; +} + +static int gc5035_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct gc5035 *gc5035 = to_gc5035(sd); + const struct gc5035_mode *mode = gc5035->cur_mode; + + mutex_lock(&gc5035->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); +#else + mutex_unlock(&gc5035->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = MEDIA_BUS_FMT_SRGGB10_1X10; + fmt->format.field = V4L2_FIELD_NONE; + } + mutex_unlock(&gc5035->mutex); + + return 0; +} + +static int gc5035_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index != 0) + return -EINVAL; + code->code = MEDIA_BUS_FMT_SRGGB10_1X10; + + return 0; +} + +static int gc5035_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct gc5035 *gc5035 = to_gc5035(sd); + + if (fse->index >= gc5035->cfg_num) + return -EINVAL; + + if (fse->code != MEDIA_BUS_FMT_SRGGB10_1X10) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = supported_modes[fse->index].width; + fse->max_height = supported_modes[fse->index].height; + fse->min_height = supported_modes[fse->index].height; + + return 0; +} + +static int gc5035_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct gc5035 *gc5035 = to_gc5035(sd); + const struct gc5035_mode *mode = gc5035->cur_mode; + + mutex_lock(&gc5035->mutex); + fi->interval = mode->max_fps; + mutex_unlock(&gc5035->mutex); + + return 0; +} + +static void gc5035_get_module_inf(struct gc5035 *gc5035, + struct rkmodule_inf *inf) +{ + strlcpy(inf->base.sensor, + GC5035_NAME, + sizeof(inf->base.sensor)); + strlcpy(inf->base.module, + gc5035->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, + gc5035->len_name, + sizeof(inf->base.lens)); +} + +static void gc5035_set_module_inf(struct gc5035 *gc5035, + struct rkmodule_awb_cfg *cfg) +{ + mutex_lock(&gc5035->mutex); + memcpy(&gc5035->awb_cfg, cfg, sizeof(*cfg)); + mutex_unlock(&gc5035->mutex); +} + +static long gc5035_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct gc5035 *gc5035 = to_gc5035(sd); + long ret = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + gc5035_get_module_inf(gc5035, (struct rkmodule_inf *)arg); + break; + case RKMODULE_AWB_CFG: + gc5035_set_module_inf(gc5035, (struct rkmodule_awb_cfg *)arg); + break; + default: + ret = -ENOTTY; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long gc5035_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_awb_cfg *cfg; + long ret = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = gc5035_ioctl(sd, cmd, inf); + if (!ret) + ret = copy_to_user(up, inf, sizeof(*inf)); + kfree(inf); + break; + case RKMODULE_AWB_CFG: + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(cfg, up, sizeof(*cfg)); + if (!ret) + ret = gc5035_ioctl(sd, cmd, cfg); + kfree(cfg); + break; + default: + ret = -ENOTTY; + break; + } + + return ret; +} +#endif + +static int __gc5035_start_stream(struct gc5035 *gc5035) +{ + int ret; + + ret = gc5035_write_array(gc5035->client, gc5035->cur_mode->reg_list); + if (ret) + return ret; + + /* In case these controls are set before streaming */ + mutex_unlock(&gc5035->mutex); + ret = v4l2_ctrl_handler_setup(&gc5035->ctrl_handler); + mutex_lock(&gc5035->mutex); + if (ret) + return ret; + ret = gc5035_write_reg(gc5035->client, + GC5035_REG_SET_PAGE, + GC5035_SET_PAGE_ONE); + ret |= gc5035_write_reg(gc5035->client, + GC5035_REG_CTRL_MODE, + GC5035_MODE_STREAMING); + return ret; +} + +static int __gc5035_stop_stream(struct gc5035 *gc5035) +{ + int ret; + + ret = gc5035_write_reg(gc5035->client, + GC5035_REG_SET_PAGE, + GC5035_SET_PAGE_ONE); + ret |= gc5035_write_reg(gc5035->client, + GC5035_REG_CTRL_MODE, + GC5035_MODE_SW_STANDBY); + return ret; +} + +static int gc5035_s_stream(struct v4l2_subdev *sd, int on) +{ + struct gc5035 *gc5035 = to_gc5035(sd); + struct i2c_client *client = gc5035->client; + int ret = 0; + + mutex_lock(&gc5035->mutex); + on = !!on; + if (on == gc5035->streaming) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = __gc5035_start_stream(gc5035); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + __gc5035_stop_stream(gc5035); + pm_runtime_put(&client->dev); + } + + gc5035->streaming = on; + +unlock_and_return: + mutex_unlock(&gc5035->mutex); + + return ret; +} + +static int gc5035_s_power(struct v4l2_subdev *sd, int on) +{ + struct gc5035 *gc5035 = to_gc5035(sd); + struct i2c_client *client = gc5035->client; + int ret = 0; + + mutex_lock(&gc5035->mutex); + + /* If the power state is not modified - no work to do. */ + if (gc5035->power_on == !!on) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = gc5035_write_array(gc5035->client, gc5035_global_regs); + if (ret) { + v4l2_err(sd, "could not set init registers\n"); + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + gc5035->power_on = true; + } else { + pm_runtime_put(&client->dev); + gc5035->power_on = false; + } + +unlock_and_return: + mutex_unlock(&gc5035->mutex); + + return ret; +} + +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 gc5035_cal_delay(u32 cycles) +{ + return DIV_ROUND_UP(cycles, GC5035_XVCLK_FREQ / 1000 / 1000); +} + +static int __gc5035_power_on(struct gc5035 *gc5035) +{ + int ret; + u32 delay_us; + struct device *dev = &gc5035->client->dev; + + if (!IS_ERR_OR_NULL(gc5035->pins_default)) { + ret = pinctrl_select_state(gc5035->pinctrl, + gc5035->pins_default); + if (ret < 0) + dev_err(dev, "could not set pins\n"); + } + ret = clk_set_rate(gc5035->xvclk, GC5035_XVCLK_FREQ); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate (24MHz)\n"); + if (clk_get_rate(gc5035->xvclk) != GC5035_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); + ret = clk_prepare_enable(gc5035->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + if (!IS_ERR(gc5035->reset_gpio)) + gpiod_set_value_cansleep(gc5035->reset_gpio, 0); + + ret = regulator_bulk_enable(GC5035_NUM_SUPPLIES, gc5035->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + + usleep_range(1000, 1100); + if (!IS_ERR(gc5035->reset_gpio)) + gpiod_set_value_cansleep(gc5035->reset_gpio, 1); + + usleep_range(500, 1000); + if (!IS_ERR(gc5035->pwdn_gpio)) + gpiod_set_value_cansleep(gc5035->pwdn_gpio, 1); + + /* 8192 cycles prior to first SCCB transaction */ + delay_us = gc5035_cal_delay(8192); + usleep_range(delay_us, delay_us * 2); + + return 0; + +disable_clk: + clk_disable_unprepare(gc5035->xvclk); + + return ret; +} + +static void __gc5035_power_off(struct gc5035 *gc5035) +{ + int ret; + + if (!IS_ERR(gc5035->pwdn_gpio)) + gpiod_set_value_cansleep(gc5035->pwdn_gpio, 0); + clk_disable_unprepare(gc5035->xvclk); + if (!IS_ERR(gc5035->reset_gpio)) + gpiod_set_value_cansleep(gc5035->reset_gpio, 0); + if (!IS_ERR_OR_NULL(gc5035->pins_sleep)) { + ret = pinctrl_select_state(gc5035->pinctrl, + gc5035->pins_sleep); + if (ret < 0) + dev_dbg(&gc5035->client->dev, "could not set pins\n"); + } + regulator_bulk_disable(GC5035_NUM_SUPPLIES, gc5035->supplies); +} + +static int gc5035_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc5035 *gc5035 = to_gc5035(sd); + + return __gc5035_power_on(gc5035); +} + +static int gc5035_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc5035 *gc5035 = to_gc5035(sd); + + __gc5035_power_off(gc5035); + + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int gc5035_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct gc5035 *gc5035 = to_gc5035(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + const struct gc5035_mode *def_mode = &supported_modes[0]; + + mutex_lock(&gc5035->mutex); + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = MEDIA_BUS_FMT_SRGGB10_1X10; + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&gc5035->mutex); + /* No crop or compose */ + + return 0; +} +#endif + +static int sensor_g_mbus_config(struct v4l2_subdev *sd, + struct v4l2_mbus_config *config) +{ + struct gc5035 *sensor = to_gc5035(sd); + struct device *dev = &sensor->client->dev; + + dev_info(dev, "%s(%d) enter!\n", __func__, __LINE__); + + if (2 == sensor->lane_num) { + config->type = V4L2_MBUS_CSI2; + config->flags = V4L2_MBUS_CSI2_2_LANE | + V4L2_MBUS_CSI2_CHANNEL_0 | + V4L2_MBUS_CSI2_CHANNEL_1 | + V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; + } else { + dev_err(&sensor->client->dev, + "unsupported lane_num(%d)\n", sensor->lane_num); + } + return 0; +} + +static const struct dev_pm_ops gc5035_pm_ops = { + SET_RUNTIME_PM_OPS(gc5035_runtime_suspend, + gc5035_runtime_resume, NULL) +}; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops gc5035_internal_ops = { + .open = gc5035_open, +}; +#endif + +static const struct v4l2_subdev_core_ops gc5035_core_ops = { + .s_power = gc5035_s_power, + .ioctl = gc5035_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = gc5035_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops gc5035_video_ops = { + .g_mbus_config = sensor_g_mbus_config, + .s_stream = gc5035_s_stream, + .g_frame_interval = gc5035_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops gc5035_pad_ops = { + .enum_mbus_code = gc5035_enum_mbus_code, + .enum_frame_size = gc5035_enum_frame_sizes, + .get_fmt = gc5035_get_fmt, + .set_fmt = gc5035_set_fmt, +}; + +static const struct v4l2_subdev_ops gc5035_subdev_ops = { + .core = &gc5035_core_ops, + .video = &gc5035_video_ops, + .pad = &gc5035_pad_ops, +}; + +static int gc5035_set_test_pattern(struct gc5035 *gc5035, int value) +{ + int ret = 0; + + dev_info(&gc5035->client->dev, "Test Pattern!!\n"); + ret = gc5035_write_reg(gc5035->client, 0xfe, 0x01); + ret = gc5035_write_reg(gc5035->client, 0x8c, value); + ret = gc5035_write_reg(gc5035->client, 0xfe, 0x00); + return ret; +} + +static const char * const gc5035_test_pattern_menu[] = { + "Disabled", + "Vertical Color Bar Type 1", + "Vertical Color Bar Type 2", + "Vertical Color Bar Type 3", + "Vertical Color Bar Type 4" +}; + +static int gc5035_set_exposure_reg(struct gc5035 *gc5035, u32 exposure) +{ + u32 caltime = 0; + int ret = 0; + + caltime = exposure / 2; + caltime = caltime * 2; + gc5035->Dgain_ratio = 256 * exposure / caltime; + ret = gc5035_write_reg(gc5035->client, + GC5035_REG_SET_PAGE, + GC5035_SET_PAGE_ONE); + + ret |= gc5035_write_reg(gc5035->client, + GC5035_REG_EXPOSURE_H, + (caltime >> 8) & 0x3F); + ret |= gc5035_write_reg(gc5035->client, + GC5035_REG_EXPOSURE_L, + caltime & 0xFF); + + return ret; +} + +static u32 GC5035_AGC_Param[17][2] = { + {64, 0}, + {76, 1}, + {90, 2}, + {106, 3}, + {126, 8}, + {150, 9}, + {179, 10}, + {211, 11}, + {250, 12}, + {301, 13}, + {358, 14}, + {427, 15}, + {499, 16}, + {589, 17}, + {704, 18}, + {830, 19}, + {998, 20}, +}; + +static int gc5035_set_gain_reg(struct gc5035 *gc5035, u32 a_gain) +{ + struct device *dev = &gc5035->client->dev; + int ret = 0, i = 0; + u32 temp_gain = 0; + + dev_info(dev, "%s(%d) a_gain(0x%08x)!\n", __func__, __LINE__, a_gain); + if (a_gain < 0x40) + a_gain = 0x40; + else if (a_gain > 0x400) + a_gain = 0x400; + for (i = 16; i >= 0; i--) { + if (a_gain >= GC5035_AGC_Param[i][0]) + break; + } + + ret = gc5035_write_reg(gc5035->client, + GC5035_REG_SET_PAGE, + GC5035_SET_PAGE_ONE); + ret |= gc5035_write_reg(gc5035->client, + GC5035_REG_AGAIN, GC5035_AGC_Param[i][1]); + temp_gain = a_gain; + temp_gain = temp_gain * gc5035->Dgain_ratio / GC5035_AGC_Param[i][0]; + + dev_info(dev, "AGC_Param[%d][0](%d) temp_gain is(0x%08x)!\n", + i, GC5035_AGC_Param[i][0], temp_gain); + ret |= gc5035_write_reg(gc5035->client, + GC5035_REG_DGAIN_INT, + temp_gain >> 6); + ret |= gc5035_write_reg(gc5035->client, + GC5035_REG_DGAIN_FRAC, + (temp_gain << 2) & 0xfc); + return ret; +} + +static int gc5035_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gc5035 *gc5035 = container_of(ctrl->handler, + struct gc5035, ctrl_handler); + struct i2c_client *client = gc5035->client; + s64 max; + int ret = 0; + + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ + max = gc5035->cur_mode->height + ctrl->val - 4; + __v4l2_ctrl_modify_range(gc5035->exposure, + gc5035->exposure->minimum, max, + gc5035->exposure->step, + gc5035->exposure->default_value); + break; + } + + if (pm_runtime_get(&client->dev) <= 0) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + /* 4 least significant bits of expsoure are fractional part */ + ret = gc5035_set_exposure_reg(gc5035, ctrl->val); + break; + case V4L2_CID_ANALOGUE_GAIN: + ret = gc5035_set_gain_reg(gc5035, ctrl->val); + break; + case V4L2_CID_VBLANK: + ctrl->val = ctrl->val + gc5035->cur_mode->height; + ret = gc5035_write_reg(gc5035->client, + GC5035_REG_SET_PAGE, + GC5035_SET_PAGE_ONE); + ret |= gc5035_write_reg(gc5035->client, + GC5035_REG_VTS_H, + ((ctrl->val) >> 8) & 0xff); + ret |= gc5035_write_reg(gc5035->client, + GC5035_REG_VTS_L, + (ctrl->val) & 0xff); + break; + case V4L2_CID_TEST_PATTERN: + ret = gc5035_set_test_pattern(gc5035, ctrl->val); + break; + default: + dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", + __func__, ctrl->id, ctrl->val); + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops gc5035_ctrl_ops = { + .s_ctrl = gc5035_set_ctrl, +}; + +static int gc5035_initialize_controls(struct gc5035 *gc5035) +{ + const struct gc5035_mode *mode; + struct v4l2_ctrl_handler *handler; + struct v4l2_ctrl *ctrl; + s64 exposure_max, vblank_def; + u32 h_blank; + int ret; + + handler = &gc5035->ctrl_handler; + mode = gc5035->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 8); + if (ret) + return ret; + handler->lock = &gc5035->mutex; + + ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, + 0, 0, link_freq_menu_items); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, + 0, GC5035_PIXEL_RATE, 1, GC5035_PIXEL_RATE); + + h_blank = mode->hts_def - mode->width; + gc5035->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (gc5035->hblank) + gc5035->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = mode->vts_def - mode->height; + gc5035->vblank = v4l2_ctrl_new_std(handler, &gc5035_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + GC5035_VTS_MAX - mode->height, + 1, vblank_def); + + exposure_max = mode->vts_def - 4; + gc5035->exposure = v4l2_ctrl_new_std(handler, &gc5035_ctrl_ops, + V4L2_CID_EXPOSURE, GC5035_EXPOSURE_MIN, + exposure_max, GC5035_EXPOSURE_STEP, + mode->exp_def); + + gc5035->anal_gain = v4l2_ctrl_new_std(handler, &gc5035_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, GC5035_GAIN_MIN, + GC5035_GAIN_MAX, GC5035_GAIN_STEP, + GC5035_GAIN_DEFAULT); + + gc5035->test_pattern = v4l2_ctrl_new_std_menu_items(handler, + &gc5035_ctrl_ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(gc5035_test_pattern_menu) - 1, + 0, 0, gc5035_test_pattern_menu); + + if (handler->error) { + ret = handler->error; + dev_err(&gc5035->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + gc5035->subdev.ctrl_handler = handler; + + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +static int gc5035_check_sensor_id(struct gc5035 *gc5035, + struct i2c_client *client) +{ + struct device *dev = &gc5035->client->dev; + u16 id = 0; + u8 reg_H = 0; + u8 reg_L = 0; + int ret; + + ret = gc5035_read_reg(client, GC5035_REG_CHIP_ID_H, ®_H); + ret |= gc5035_read_reg(client, GC5035_REG_CHIP_ID_L, ®_L); + id = ((reg_H << 8) & 0xff00) | (reg_L & 0xff); + if (id != CHIP_ID) { + dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret); + return -ENODEV; + } + dev_info(dev, "detected gc%04x sensor\n", id); + return ret; +} + +static int gc5035_configure_regulators(struct gc5035 *gc5035) +{ + unsigned int i; + + for (i = 0; i < GC5035_NUM_SUPPLIES; i++) + gc5035->supplies[i].supply = gc5035_supply_names[i]; + + return devm_regulator_bulk_get(&gc5035->client->dev, + GC5035_NUM_SUPPLIES, + gc5035->supplies); +} + +static void free_gpio(struct gc5035 *sensor) +{ + struct device *dev = &sensor->client->dev; + unsigned int temp_gpio = -1; + + dev_info(dev, "%s(%d) enter!\n", __func__, __LINE__); + if (!IS_ERR(sensor->reset_gpio)) { + temp_gpio = desc_to_gpio(sensor->reset_gpio); + dev_info(dev, "free gpio(%d)!\n", temp_gpio); + gpio_free(temp_gpio); + } + + if (!IS_ERR(sensor->pwdn_gpio)) { + temp_gpio = desc_to_gpio(sensor->pwdn_gpio); + dev_info(dev, "free gpio(%d)!\n", temp_gpio); + gpio_free(temp_gpio); + } +} + +static int gc5035_parse_of(struct gc5035 *gc5035) +{ + struct device *dev = &gc5035->client->dev; + struct device_node *endpoint; + struct fwnode_handle *fwnode; + int rval; + + endpoint = of_graph_get_next_endpoint(dev->of_node, NULL); + if (!endpoint) { + dev_err(dev, "Failed to get endpoint\n"); + return -EINVAL; + } + fwnode = of_fwnode_handle(endpoint); + rval = fwnode_property_read_u32_array(fwnode, "data-lanes", NULL, 0); + if (rval <= 0) { + dev_warn(dev, " Get mipi lane num failed!\n"); + return -1; + } + + gc5035->lane_num = rval; + if (2 == gc5035->lane_num) { + gc5035->cur_mode = &supported_modes_2lane[0]; + supported_modes = supported_modes_2lane; + gc5035->cfg_num = ARRAY_SIZE(supported_modes_2lane); + + /* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ + gc5035->pixel_rate = MIPI_FREQ * 2U * gc5035->lane_num / 10U; + dev_info(dev, "lane_num(%d) pixel_rate(%u)\n", + gc5035->lane_num, gc5035->pixel_rate); + } else { + dev_err(dev, "unsupported lane_num(%d)\n", gc5035->lane_num); + return -1; + } + return 0; +} + +static int gc5035_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct gc5035 *gc5035; + struct v4l2_subdev *sd; + char facing[2]; + int ret; + + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + gc5035 = devm_kzalloc(dev, sizeof(*gc5035), GFP_KERNEL); + if (!gc5035) + return -ENOMEM; + + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &gc5035->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &gc5035->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &gc5035->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &gc5035->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + gc5035->client = client; + + gc5035->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(gc5035->xvclk)) { + dev_err(dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + gc5035->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(gc5035->reset_gpio)) + dev_warn(dev, "Failed to get reset-gpios\n"); + + gc5035->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(gc5035->pwdn_gpio)) + dev_warn(dev, "Failed to get pwdn-gpios\n"); + + ret = gc5035_configure_regulators(gc5035); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + + ret = gc5035_parse_of(gc5035); + if (ret != 0) + return -EINVAL; + + gc5035->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR(gc5035->pinctrl)) { + gc5035->pins_default = + pinctrl_lookup_state(gc5035->pinctrl, + OF_CAMERA_PINCTRL_STATE_DEFAULT); + if (IS_ERR(gc5035->pins_default)) + dev_err(dev, "could not get default pinstate\n"); + + gc5035->pins_sleep = + pinctrl_lookup_state(gc5035->pinctrl, + OF_CAMERA_PINCTRL_STATE_SLEEP); + if (IS_ERR(gc5035->pins_sleep)) + dev_err(dev, "could not get sleep pinstate\n"); + } + + mutex_init(&gc5035->mutex); + + sd = &gc5035->subdev; + v4l2_i2c_subdev_init(sd, client, &gc5035_subdev_ops); + ret = gc5035_initialize_controls(gc5035); + if (ret) + goto err_destroy_mutex; + + ret = __gc5035_power_on(gc5035); + if (ret) + goto err_free_handler; + + ret = gc5035_check_sensor_id(gc5035, client); + if (ret) + goto err_power_off; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &gc5035_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + gc5035->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &gc5035->pad); + if (ret < 0) + goto err_power_off; +#endif + + memset(facing, 0, sizeof(facing)); + if (strcmp(gc5035->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + gc5035->module_index, facing, + GC5035_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor_common(sd); + if (ret) { + dev_err(dev, "v4l2 async register subdev failed\n"); + goto err_clean_entity; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; + +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif +err_power_off: + __gc5035_power_off(gc5035); + free_gpio(gc5035); +err_free_handler: + v4l2_ctrl_handler_free(&gc5035->ctrl_handler); +err_destroy_mutex: + mutex_destroy(&gc5035->mutex); + + return ret; +} + +static int gc5035_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc5035 *gc5035 = to_gc5035(sd); + + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + v4l2_ctrl_handler_free(&gc5035->ctrl_handler); + mutex_destroy(&gc5035->mutex); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __gc5035_power_off(gc5035); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id gc5035_of_match[] = { + { .compatible = "galaxycore,gc5035" }, + {}, +}; +MODULE_DEVICE_TABLE(of, gc5035_of_match); +#endif + +static const struct i2c_device_id gc5035_match_id[] = { + { "galaxycore,gc5035", 0 }, + { }, +}; + +static struct i2c_driver gc5035_i2c_driver = { + .driver = { + .name = GC5035_NAME, + .pm = &gc5035_pm_ops, + .of_match_table = of_match_ptr(gc5035_of_match), + }, + .probe = &gc5035_probe, + .remove = &gc5035_remove, + .id_table = gc5035_match_id, +}; + +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&gc5035_i2c_driver); +} + +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&gc5035_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_DESCRIPTION("GalaxyCore gc5035 sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/gc8034.c b/drivers/media/i2c/gc8034.c new file mode 100644 index 000000000000..f45d65a780f6 --- /dev/null +++ b/drivers/media/i2c/gc8034.c @@ -0,0 +1,2165 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * gc8034 driver + * + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd. + * + * V0.0X01.0X01 add poweron function. + * V0.0X01.0X02 fix mclk issue when probe multiple camera. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x02) + +#ifndef V4L2_CID_DIGITAL_GAIN +#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN +#endif + +#define GC8034_LANES 4 +#define GC8034_BITS_PER_SAMPLE 10 +#define GC8034_LINK_FREQ_MHZ 336000000 +/* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ +#define GC8034_PIXEL_RATE 80000000 +#define GC8034_XVCLK_FREQ 24000000 + +#define CHIP_ID 0x8044 +#define GC8034_REG_CHIP_ID_H 0xf0 +#define GC8034_REG_CHIP_ID_L 0xf1 + +#define GC8034_REG_SET_PAGE 0xfe +#define GC8034_SET_PAGE_ZERO 0x00 + +#define GC8034_REG_CTRL_MODE 0x00 +#define GC8034_MODE_SW_STANDBY 0x00 +#define GC8034_MODE_STREAMING 0x01 + +#define GC8034_REG_EXPOSURE_H 0x03 +#define GC8034_REG_EXPOSURE_L 0x04 +#define GC8034_FETCH_HIGH_BYTE_EXP(VAL) (((VAL) >> 8) & 0x7F) /* 4 Bits */ +#define GC8034_FETCH_LOW_BYTE_EXP(VAL) ((VAL) & 0xFF) /* 8 Bits */ +#define GC8034_EXPOSURE_MIN 4 +#define GC8034_EXPOSURE_STEP 1 +#define GC8034_VTS_MAX 0x1fff + +#define GC8034_REG_AGAIN 0xb6 +#define GC8034_REG_DGAIN_INT 0xb1 +#define GC8034_REG_DGAIN_FRAC 0xb2 +#define GC8034_GAIN_MIN 64 +#define GC8034_GAIN_MAX 1092 +#define GC8034_GAIN_STEP 1 +#define GC8034_GAIN_DEFAULT 64 + +#define GC8034_REG_VTS_H 0x07 +#define GC8034_REG_VTS_L 0x08 + +#define REG_NULL 0xFF + +#define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default" +#define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep" + +#define GC8034_NAME "gc8034" + +static const char * const gc8034_supply_names[] = { + "avdd", /* Analog power */ + "dovdd", /* Digital I/O power */ + "dvdd", /* Digital core power */ +}; + +#define GC8034_NUM_SUPPLIES ARRAY_SIZE(gc8034_supply_names) + +struct gc8034_dd { + u16 x; + u16 y; + u16 t; +}; + +struct gc8034_otp_info { + int flag; //bit[7]: info bit[6]:wb bit[5]:vcm bit[4]:lenc + //bit[3] dd bit[2] chip version + u32 module_id; + u32 lens_id; + u32 year; + u32 month; + u32 day; + u32 rg_ratio; + u32 bg_ratio; + u32 golden_rg; + u32 golden_bg; + u8 lsc[396]; + u32 vcm_start; + u32 vcm_end; + u32 vcm_dir; + u32 dd_cnt; + struct gc8034_dd dd_param[160]; + u16 reg_page[5]; + u16 reg_addr[5]; + u16 reg_value[5]; + u16 reg_num; +}; + +struct gc8034_id_name { + u32 id; + char name[RKMODULE_NAME_LEN]; +}; + +static const struct gc8034_id_name gc8034_module_info[] = { + {0x0d, "CameraKing"}, + {0x00, "Unknown"} +}; + +static const struct gc8034_id_name gc8034_lens_info[] = { + {0xd0, "CK8401"}, + {0x00, "Unknown"} +}; + +struct regval { + u8 addr; + u8 val; +}; + +struct gc8034_mode { + u32 width; + u32 height; + struct v4l2_fract max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + const struct regval *reg_list; +}; + +struct gc8034 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; + struct regulator_bulk_data supplies[GC8034_NUM_SUPPLIES]; + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_sleep; + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *anal_gain; + struct v4l2_ctrl *digi_gain; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct mutex mutex; + bool streaming; + bool power_on; + const struct gc8034_mode *cur_mode; + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; + u32 Dgain_ratio; + struct gc8034_otp_info *otp; + struct rkmodule_inf module_inf; + struct rkmodule_awb_cfg awb_cfg; +}; + +#define to_gc8034(sd) container_of(sd, struct gc8034, subdev) + +/* + * Xclk 24Mhz + */ +static const struct regval gc8034_global_regs[] = { + /*SYS*/ + {0xf2, 0x00}, + {0xf4, 0x80}, + {0xf5, 0x19}, + {0xf6, 0x44}, + {0xf8, 0x63}, + {0xfa, 0x45}, + {0xf9, 0x00}, + {0xf7, 0x9d}, + {0xfc, 0x00}, + {0xfc, 0x00}, + {0xfc, 0xea}, + {0xfe, 0x03}, + {0x03, 0x9a}, + {0x18, 0x07}, + {0x01, 0x07}, + {0xfc, 0xee}, + /*Cisctl&Analog*/ + {0xfe, 0x00}, + {0x03, 0x08}, + {0x04, 0xc6}, + {0x05, 0x02}, + {0x06, 0x16}, + {0x07, 0x00}, + {0x08, 0x10}, + {0x0a, 0x3a}, + {0x0b, 0x00}, + {0x0c, 0x04}, + {0x0d, 0x09}, + {0x0e, 0xa0}, + {0x0f, 0x0c}, + {0x10, 0xd4}, + {0x17, 0xc0}, + {0x18, 0x02}, + {0x19, 0x17}, + {0x1e, 0x50}, + {0x1f, 0x80}, + {0x21, 0x4c}, + {0x25, 0x00}, + {0x28, 0x4a}, + {0x2d, 0x89}, + {0xca, 0x02}, + {0xcb, 0x00}, + {0xcc, 0x39}, + {0xce, 0xd0}, + {0xcf, 0x93}, + {0xd0, 0x19}, + {0xd1, 0xaa}, + {0xd2, 0xcb}, + {0xd8, 0x40}, + {0xd9, 0xff}, + {0xda, 0x0e}, + {0xdb, 0xb0}, + {0xdc, 0x0e}, + {0xde, 0x08}, + {0xe4, 0xc6}, + {0xe5, 0x08}, + {0xe6, 0x10}, + {0xed, 0x2a}, + {0xfe, 0x02}, + {0x59, 0x02}, + {0x5a, 0x04}, + {0x5b, 0x08}, + {0x5c, 0x20}, + {0xfe, 0x00}, + {0x1a, 0x09}, + {0x1d, 0x13}, + {0xfe, 0x10}, + {0xfe, 0x00}, + {0xfe, 0x10}, + {0xfe, 0x00}, + /*Gamma*/ + {0xfe, 0x00}, + {0x20, 0x55}, + {0x33, 0x83}, + {0xfe, 0x01}, + {0xdf, 0x06}, + {0xe7, 0x18}, + {0xe8, 0x20}, + {0xe9, 0x16}, + {0xea, 0x17}, + {0xeb, 0x50}, + {0xec, 0x6c}, + {0xed, 0x9b}, + {0xee, 0xd8}, + /*ISP*/ + {0xfe, 0x00}, + {0x80, 0x10}, + {0x84, 0x01}, + {0x88, 0x03}, + {0x89, 0x03}, + {0x8d, 0x03}, + {0x8f, 0x14}, + {0xad, 0x30}, + {0x66, 0x2c}, + {0xbc, 0x49}, + {0xc2, 0x7f}, + {0xc3, 0xff}, + /*Crop window*/ + {0x90, 0x01}, + {0x92, 0x08}, + {0x94, 0x09}, + {0x95, 0x04}, + {0x96, 0xc8}, + {0x97, 0x06}, + {0x98, 0x60}, + /*Gain*/ + {0xb0, 0x90}, + {0xb1, 0x01}, + {0xb2, 0x00}, + {0xb6, 0x00}, + /*BLK*/ + {0xfe, 0x00}, + {0x40, 0x22}, + {0x41, 0x20}, + {0x42, 0x02}, + {0x43, 0x08}, + {0x4e, 0x0f}, + {0x4f, 0xf0}, + {0x58, 0x80}, + {0x59, 0x80}, + {0x5a, 0x80}, + {0x5b, 0x80}, + {0x5c, 0x00}, + {0x5d, 0x00}, + {0x5e, 0x00}, + {0x5f, 0x00}, + {0x6b, 0x01}, + {0x6c, 0x00}, + {0x6d, 0x0c}, + /*WB offset*/ + {0xfe, 0x01}, + {0xbf, 0x40}, + /*Dark Sun*/ + {0xfe, 0x01}, + {0x68, 0x77}, + /*DPC*/ + {0xfe, 0x01}, + {0x60, 0x00}, + {0x61, 0x10}, + {0x62, 0x28}, + {0x63, 0x10}, + {0x64, 0x02}, + /*LSC*/ + {0xfe, 0x01}, + {0xa8, 0x60}, + {0xa2, 0xd1}, + {0xc8, 0x57}, + {0xa1, 0xb8}, + {0xa3, 0x91}, + {0xc0, 0x50}, + {0xd0, 0x05}, + {0xd1, 0xb2}, + {0xd2, 0x1f}, + {0xd3, 0x00}, + {0xd4, 0x00}, + {0xd5, 0x00}, + {0xd6, 0x00}, + {0xd7, 0x00}, + {0xd8, 0x00}, + {0xd9, 0x00}, + {0xa4, 0x10}, + {0xa5, 0x20}, + {0xa6, 0x60}, + {0xa7, 0x80}, + {0xab, 0x18}, + {0xc7, 0xc0}, + /*ABB*/ + {0xfe, 0x01}, + {0x20, 0x02}, + {0x21, 0x02}, + {0x23, 0x42}, + /*MIPI*/ + {0xfe, 0x03}, + {0x02, 0x03}, + {0x04, 0x80}, + {0x11, 0x2b}, + {0x12, 0xf8}, + {0x13, 0x07}, + {0x15, 0x10}, + {0x16, 0x29}, + {0x17, 0xff}, + {0x19, 0xaa}, + {0x1a, 0x02}, + {0x21, 0x02}, + {0x22, 0x03}, + {0x23, 0x0a}, + {0x24, 0x00}, + {0x25, 0x12}, + {0x26, 0x04}, + {0x29, 0x04}, + {0x2a, 0x02}, + {0x2b, 0x04}, + {0xfe, 0x00}, + {0x3f, 0x00}, + {REG_NULL, 0x00}, +}; + +/* + * Xclk 24Mhz + * max_framerate 30fps + * mipi_datarate per lane 656Mbps + */ +static const struct regval gc8034_3264x2448_regs[] = { + /*SYS*/ + {0xf2, 0x00}, + {0xf4, 0x80}, + {0xf5, 0x19}, + {0xf6, 0x44}, + {0xf8, 0x63}, + {0xfa, 0x45}, + {0xf9, 0x00}, + {0xf7, 0x95}, + {0xfc, 0x00}, + {0xfc, 0x00}, + {0xfc, 0xea}, + {0xfe, 0x03}, + {0x03, 0x9a}, + {0x18, 0x07}, + {0x01, 0x07}, + {0xfc, 0xee}, + /*ISP*/ + {0xfe, 0x00}, + {0x80, 0x13}, + {0xad, 0x00}, + /*Crop window*/ + {0x90, 0x01}, + {0x92, 0x08}, + {0x94, 0x09}, + {0x95, 0x09}, + {0x96, 0x90}, + {0x97, 0x0c}, + {0x98, 0xc0}, + /*DPC*/ + {0xfe, 0x01}, + {0x62, 0x60}, + {0x63, 0x48}, + /*MIPI*/ + {0xfe, 0x03}, + {0x02, 0x03}, + {0x04, 0x80}, + {0x11, 0x2b}, + {0x12, 0xf0}, + {0x13, 0x0f}, + {0x15, 0x10}, + {0x16, 0x29}, + {0x17, 0xff}, + {0x19, 0xaa}, + {0x1a, 0x02}, + {0x21, 0x05}, + {0x22, 0x06}, + {0x23, 0x2b}, + {0x24, 0x00}, + {0x25, 0x12}, + {0x26, 0x07}, + {0x29, 0x07}, + {0x2a, 0x12}, + {0x2b, 0x07}, + {0xfe, 0x00}, + {0x3f, 0xd0}, + {REG_NULL, 0x00}, +}; + +static const struct gc8034_mode supported_modes[] = { + { + .width = 3264, + .height = 2448, + .max_fps = { + .numerator = 10000, + .denominator = 299625, + }, + .exp_def = 0x08c6, + .hts_def = 0x10b0, + .vts_def = 0x09c4, + .reg_list = gc8034_3264x2448_regs, + }, +}; + +static const s64 link_freq_menu_items[] = { + GC8034_LINK_FREQ_MHZ +}; + +/* Write registers up to 4 at a time */ +static int gc8034_write_reg(struct i2c_client *client, u8 reg, u8 val) +{ + struct i2c_msg msg; + u8 buf[2]; + int ret; + + buf[0] = reg & 0xFF; + buf[1] = val; + + msg.addr = client->addr; + msg.flags = client->flags; + msg.buf = buf; + msg.len = sizeof(buf); + + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret >= 0) + return 0; + + dev_err(&client->dev, + "gc8034 write reg(0x%x val:0x%x) failed !\n", reg, val); + + return ret; +} + +static int gc8034_write_array(struct i2c_client *client, + const struct regval *regs) +{ + u32 i = 0; + int ret = 0; + + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) + ret = gc8034_write_reg(client, regs[i].addr, regs[i].val); + + return ret; +} + +/* Read registers up to 4 at a time */ +static int gc8034_read_reg(struct i2c_client *client, u8 reg, u8 *val) +{ + struct i2c_msg msg[2]; + u8 buf[1]; + int ret; + + buf[0] = reg & 0xFF; + + msg[0].addr = client->addr; + msg[0].flags = client->flags; + msg[0].buf = buf; + msg[0].len = sizeof(buf); + + msg[1].addr = client->addr; + msg[1].flags = client->flags | I2C_M_RD; + msg[1].buf = buf; + msg[1].len = 1; + + ret = i2c_transfer(client->adapter, msg, 2); + if (ret >= 0) { + *val = buf[0]; + return 0; + } + + dev_err(&client->dev, + "gc8034 read reg:0x%x failed !\n", reg); + + return ret; +} + +static int gc8034_get_reso_dist(const struct gc8034_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); +} + +static const struct gc8034_mode * +gc8034_find_best_fit(struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *framefmt = &fmt->format; + int dist; + int cur_best_fit = 0; + int cur_best_fit_dist = -1; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { + dist = gc8034_get_reso_dist(&supported_modes[i], framefmt); + if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { + cur_best_fit_dist = dist; + cur_best_fit = i; + } + } + + return &supported_modes[cur_best_fit]; +} + +static int gc8034_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct gc8034 *gc8034 = to_gc8034(sd); + const struct gc8034_mode *mode; + s64 h_blank, vblank_def; + + mutex_lock(&gc8034->mutex); + + mode = gc8034_find_best_fit(fmt); + fmt->format.code = MEDIA_BUS_FMT_SRGGB10_1X10; + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; +#else + mutex_unlock(&gc8034->mutex); + return -ENOTTY; +#endif + } else { + gc8034->cur_mode = mode; + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(gc8034->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(gc8034->vblank, vblank_def, + GC8034_VTS_MAX - mode->height, + 1, vblank_def); + } + + mutex_unlock(&gc8034->mutex); + + return 0; +} + +static int gc8034_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct gc8034 *gc8034 = to_gc8034(sd); + const struct gc8034_mode *mode = gc8034->cur_mode; + + mutex_lock(&gc8034->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); +#else + mutex_unlock(&gc8034->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = MEDIA_BUS_FMT_SRGGB10_1X10; + fmt->format.field = V4L2_FIELD_NONE; + } + mutex_unlock(&gc8034->mutex); + + return 0; +} + +static int gc8034_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index != 0) + return -EINVAL; + code->code = MEDIA_BUS_FMT_SRGGB10_1X10; + + return 0; +} + +static int gc8034_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fse->code != MEDIA_BUS_FMT_SRGGB10_1X10) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = supported_modes[fse->index].width; + fse->max_height = supported_modes[fse->index].height; + fse->min_height = supported_modes[fse->index].height; + + return 0; +} + +static int gc8034_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct gc8034 *gc8034 = to_gc8034(sd); + const struct gc8034_mode *mode = gc8034->cur_mode; + + mutex_lock(&gc8034->mutex); + fi->interval = mode->max_fps; + mutex_unlock(&gc8034->mutex); + + return 0; +} + +#define GC8034_MIRROR_NORMAL +#define DD_WIDTH 3284 +#define DD_HEIGHT 2464 + +#define DD_PARAM_QTY 350 +#define WINDOW_WIDTH 0x0cd4//3284 max effective pixels +#define WINDOW_HEIGHT 0x09a0//2462 +#define REG_ROM_START 0x4e +#define INFO_ROM_START 0x70 +#define INFO_WIDTH 0x08 +#define WB_ROM_START 0x5f +#define WB_WIDTH 0x04 +#define GOLDEN_ROM_START 0x67//golden R/G ratio +#define GOLDEN_WIDTH 0x04 +#define LSC_NUM 99//0x63 //(7+2)*(9+2) +#define VCM_START 0x3B +#define VCM_WIDTH 0x04 + +static int gc8034_otp_read_reg(struct i2c_client *client, + int page, int address) +{ + int ret = 0; + u8 val = 0; + + ret = gc8034_write_reg(client, 0xfe, 0x00); + ret |= gc8034_write_reg(client, 0xD4, + ((page << 2) & 0x3c) + ((address >> 5) & 0x03)); + ret |= gc8034_write_reg(client, 0xD5, + (address << 3) & 0xff); + ret |= gc8034_write_reg(client, 0xF3, + 0x20); + ret |= gc8034_read_reg(client, 0xD7, &val); + if (ret != 0) + return ret; + return val; +} + +static int gc8034_otp_read_group(struct i2c_client *client, + int page, int address, u8 *buf, int size) +{ + int i = 0; + int val = 0; + + for (i = 0; i < size; i++) { + if ((address % 0x80) == 0) { + page += 1; + address = 0; + } + val = gc8034_otp_read_reg(client, page, address); + if (val >= 0) + buf[i] = val; + else + return val; + address += 1; + } + return 0; +} + +static int gc8034_otp_enable(struct gc8034 *gc8034) +{ + struct i2c_client *client = gc8034->client; + u8 otp_clk = 0; + u8 otp_en = 0; + int ret = 0; + + ret = gc8034_write_reg(client, 0xf2, 0x00); + ret |= gc8034_write_reg(client, 0xf4, 0x80); + ret |= gc8034_write_reg(client, 0xfc, 0x00); + ret |= gc8034_write_reg(client, 0xf7, 0x97); + ret |= gc8034_write_reg(client, 0xfc, 0x00); + ret |= gc8034_write_reg(client, 0xfc, 0x00); + ret |= gc8034_write_reg(client, 0xfc, 0xee); + ret |= gc8034_read_reg(client, 0xF2, &otp_clk); + ret |= gc8034_read_reg(client, 0xF4, &otp_en); + otp_clk |= 0x01; + otp_en |= 0x08; + ret |= gc8034_write_reg(client, 0xF2, otp_clk); + ret |= gc8034_write_reg(client, 0xF4, otp_en); + usleep_range(100, 200); + return ret; +} + +static int gc8034_otp_disable(struct gc8034 *gc8034) +{ + struct i2c_client *client = gc8034->client; + u8 otp_clk = 0; + u8 otp_en = 0; + int ret = 0; + + ret = gc8034_read_reg(client, 0xF2, &otp_clk); + ret |= gc8034_read_reg(client, 0xF4, &otp_en); + otp_clk &= 0xFE; + otp_en &= 0xF7; + ret |= gc8034_write_reg(client, 0xF2, otp_clk); + ret |= gc8034_write_reg(client, 0xF4, otp_en); + ret |= gc8034_write_reg(client, 0xfc, 0x00); + ret |= gc8034_write_reg(client, 0xf7, 0x95); + ret |= gc8034_write_reg(client, 0xfc, 0x00); + ret |= gc8034_write_reg(client, 0xfc, 0x00); + ret |= gc8034_write_reg(client, 0xfc, 0xee); + return ret; +} + +static void gc8034_check_prsel(struct gc8034 *gc8034) +{ + struct i2c_client *client = gc8034->client; + u8 product_level = 0; + + gc8034_write_reg(client, 0xfe, 0x02); + gc8034_read_reg(client, 0x68, &product_level); + product_level &= 0x07; + + if (product_level == 0x00 || product_level == 0x01) { + gc8034_write_reg(client, 0xfe, 0x00); + gc8034_write_reg(client, 0xd2, 0xcb); + } else { + gc8034_write_reg(client, 0xfe, 0x00); + gc8034_write_reg(client, 0xd2, 0xc3); + } +} + +static int gc8034_otp_read(struct gc8034 *gc8034) +{ + int otp_flag, i, j, index, temp; + struct gc8034_otp_info *otp_ptr; + struct device *dev = &gc8034->client->dev; + struct i2c_client *client = gc8034->client; + int ret = 0; + int cnt = 0; + int checksum = 0; + u8 info[8] = {0}; + u8 wb[4] = {0}; + u8 vcm[4] = {0}; + u8 golden[4] = {0}; + int total_number = 0; + u8 ddtempbuff[4 * 80] = { 0 }; + + otp_ptr = devm_kzalloc(dev, sizeof(*otp_ptr), GFP_KERNEL); + if (!otp_ptr) + return -ENOMEM; + + /* OTP base information*/ + otp_flag = gc8034_otp_read_reg(client, 9, 0x6f); + for (index = 0; index < 2; index++) { + switch ((otp_flag << (2 * index)) & 0x0c) { + case 0x00: + dev_err(dev, "%s GC8034_OTP_INFO group %d is Empty!\n", + __func__, index + 1); + break; + case 0x04: + dev_err(dev, "%s GC8034_OTP_INFO group %d is Valid!\n", + __func__, index + 1); + checksum = 0; + ret |= gc8034_otp_read_group(client, 9, + (INFO_ROM_START + index * INFO_WIDTH), + &info[0], INFO_WIDTH); + if (ret < 0) { + dev_err(dev, "%s read otp error!\n", __func__); + return ret; + } + for (i = 0; i < INFO_WIDTH - 1; i++) + checksum += info[i]; + if ((checksum % 255 + 1) == info[INFO_WIDTH - 1]) { + otp_ptr->flag = 0x80; + otp_ptr->module_id = info[0]; + otp_ptr->lens_id = info[1]; + otp_ptr->year = info[4]; + otp_ptr->month = info[5]; + otp_ptr->day = info[6]; + dev_err(dev, "fac info: module(0x%x) lens(0x%x) time(%d_%d_%d)!\n", + otp_ptr->module_id, + otp_ptr->lens_id, + otp_ptr->year, + otp_ptr->month, + otp_ptr->day); + } else { + dev_err(dev, "%s GC8034_OTP_INFO Check sum %d Error!\n", + __func__, index + 1); + } + break; + case 0x08: + case 0x0c: + dev_err(dev, "%s GC8034_OTP_INFO group %d is Invalid !!\n", + __func__, index + 1); + break; + default: + break; + } + } + + /* OTP WB calibration data */ + otp_flag = gc8034_otp_read_reg(client, 9, 0x5e); + for (index = 0; index < 2; index++) { + switch ((otp_flag << (2 * index)) & 0x0c) { + case 0x00: + dev_err(dev, "%s GC8034_OTP_WB group %d is Empty !\n", + __func__, index + 1); + break; + case 0x04: + dev_err(dev, "%s GC8034_OTP_WB group %d is Valid !!\n", + __func__, index + 1); + checksum = 0; + ret |= gc8034_otp_read_group(client, + 9, + (WB_ROM_START + index * WB_WIDTH), + &wb[0], + WB_WIDTH); + if (ret < 0) { + dev_err(dev, "%s read otp error!\n", __func__); + return ret; + } + for (i = 0; i < WB_WIDTH - 1; i++) + checksum += wb[i]; + if ((checksum % 255 + 1) == wb[WB_WIDTH - 1]) { + otp_ptr->flag |= 0x40; /* valid AWB in OTP */ + otp_ptr->rg_ratio = + ((wb[1] & 0xf0) << 4) | wb[0]; + otp_ptr->bg_ratio = + ((wb[1] & 0x0f) << 8) | wb[2]; + dev_err(dev, "otp:(rg_ratio 0x%x, bg_ratio 0x%x)\n", + otp_ptr->rg_ratio, otp_ptr->bg_ratio); + } else { + dev_err(dev, "%s GC8034_OTP_WB Check sum %d Error !!\n", + __func__, index + 1); + } + break; + case 0x08: + case 0x0c: + dev_err(dev, "%s GC8034_OTP_WB group %d is Invalid !!\n", + __func__, index + 1); + break; + default: + break; + } + switch ((otp_flag << (2 * index)) & 0xc0) { + case 0x00: + dev_err(dev, "%s GC8034_OTP_GOLDEN group %d is Empty!\n", + __func__, index + 1); + break; + case 0x40: + dev_err(dev, "%s GC8034_OTP_GOLDEN group %d is Valid !!\n", + __func__, index + 1); + checksum = 0; + ret = gc8034_otp_read_group(client, 9, + (GOLDEN_ROM_START + index * GOLDEN_WIDTH), + &golden[0], GOLDEN_WIDTH); + if (ret < 0) { + dev_err(dev, "%s read otp error!\n", __func__); + return ret; + } + for (i = 0; i < GOLDEN_WIDTH - 1; i++) + checksum += golden[i]; + if ((checksum % 255 + 1) == golden[GOLDEN_WIDTH - 1]) { + otp_ptr->golden_rg = + golden[0] | ((golden[1] & 0xf0) << 4); + otp_ptr->golden_bg = + ((golden[1] & 0x0f) << 8) | golden[2]; + dev_err(dev, "otp:(golden_rg 0x%x, golden_bg 0x%x)\n", + otp_ptr->golden_rg, otp_ptr->golden_bg); + } else { + dev_err(dev, "%s GC8034_OTP_GOLDEN Check sum %d Error !!\n", + __func__, index + 1); + } + break; + case 0x80: + case 0xc0: + dev_err(dev, "%s GC8034_OTP_GOLDEN group %d is Invalid !!\n", + __func__, index + 1); + break; + default: + break; + } + } + + /* OTP VCM calibration data */ + otp_flag = gc8034_otp_read_reg(client, 3, 0x3A); + for (index = 0; index < 2; index++) { + switch ((otp_flag << (2 * index)) & 0x0c) { + case 0x00: + dev_err(dev, "%s GC8034_OTP_VCM group %d is Empty !\n", + __func__, index + 1); + break; + case 0x04: + dev_err(dev, "%s GC8034_OTP_VCM group %d is Valid !\n", + __func__, index + 1); + ret |= gc8034_otp_read_group(client, + 3, + (VCM_START + index * VCM_WIDTH), + &vcm[0], + VCM_WIDTH); + if (ret < 0) { + dev_err(dev, "%s read otp error!\n", __func__); + return ret; + } + checksum = 0; + for (i = 0; i < 3; i++) + checksum += vcm[i]; + if ((checksum % 255 + 1) == vcm[3]) { + otp_ptr->flag |= 0x20; /* valid LSC in OTP */ + otp_ptr->vcm_dir = 0;//not dir register + otp_ptr->vcm_start = + ((vcm[0] & 0x0f) << 8) + vcm[2]; + otp_ptr->vcm_end = + ((vcm[0] & 0xf0) << 4) + vcm[1]; + dev_err(dev, "%s GC8034_OTP_VCM check sum success\n", + __func__); + dev_err(dev, "vcm_info: 0x%x, 0x%x, 0x%x!\n", + otp_ptr->vcm_start, + otp_ptr->vcm_end, + otp_ptr->vcm_dir); + } else { + dev_err(dev, "VCM check sum read: 0x%x, calculate:0x%x\n", + vcm[3], checksum % 255 + 1); + } + break; + case 0x08: + case 0x0c: + dev_err(dev, "%s GC8034_OTP_VCM group %d is Invalid !\n", + __func__, index + 1); + break; + default: + break; + } + } + + /* OTP LSC calibration data */ + otp_flag = gc8034_otp_read_reg(client, 3, 0x43); + for (index = 0; index < 2; index++) { + switch ((otp_flag << (2 * index)) & 0x0c) { + case 0x00: + dev_err(dev, "%s GC8034_OTP_LSC group %d is Empty !\n", + __func__, index + 1); + break; + case 0x04: + dev_err(dev, "%s GC8034_OTP_LSC group %d is Valid !\n", + __func__, index + 1); + if (index == 0) { + ret |= gc8034_otp_read_group(client, + 3, 0x44, otp_ptr->lsc, 396); + temp = gc8034_otp_read_reg(client, 6, 0x50); + } else { + ret |= gc8034_otp_read_group(client, + 6, 0x51, otp_ptr->lsc, 396); + temp = gc8034_otp_read_reg(client, 9, 0x5d); + } + checksum = 0; + for (i = 0; i < 396; i++) { + checksum += otp_ptr->lsc[i]; + usleep_range(100, 200); + dev_err(dev, "otp lsc[%d] = %d\n", + i, otp_ptr->lsc[i]); + } + if ((checksum % 255 + 1) == temp) { + otp_ptr->flag |= 0x10; /* valid LSC in OTP */ + dev_err(dev, "%s GC8034_OTP_LSC check sum success\n", + __func__); + } else { + dev_err(dev, "LSC check sum read: 0x%x, calculate:0x%x\n", + temp, checksum % 255 + 1); + } + break; + case 0x08: + case 0x0c: + dev_err(dev, "%s GC8034_OTP_LSC group %d is Invalid !\n", + __func__, index + 1); + break; + default: + break; + } + } + /* OTP DD calibration data */ + otp_flag = gc8034_otp_read_reg(client, 0, 0x0b); + for (index = 0; index < 2; index++) { + switch (otp_flag & 0x03) { + case 0x00: + dev_err(dev, "%s GC8034 OTP:flag_dd is EMPTY!\n", + __func__); + break; + case 0x04: + dev_err(dev, "%s GC8034_OTP_DD group %d is valid!\n", + __func__, index + 1); + total_number = gc8034_otp_read_reg(client, 0, 0x0c) + + gc8034_otp_read_reg(client, 0, 0x0d); + ret |= gc8034_otp_read_group(client, 0, 0x0e, + &ddtempbuff[0], 4 * total_number); + for (i = 0; i < total_number; i++) { + if ((ddtempbuff[4 * i + 3] & 0x80) == 0x80) { + if ((ddtempbuff[4 * i + 3] & 0x03) == 0x03) { + otp_ptr->dd_param[cnt].x = + (((u16)ddtempbuff[4 * i + 1] & 0x0f) << 8) + + ddtempbuff[4 * i]; + otp_ptr->dd_param[cnt].y = + ((u16)ddtempbuff[4 * i + 2] << 4) + + ((ddtempbuff[4 * i + 1] & 0xf0) >> 4); + otp_ptr->dd_param[cnt++].t = 2; + otp_ptr->dd_param[cnt].x = + (((u16)ddtempbuff[4 * i + 1] & 0x0f) << 8) + + ddtempbuff[4 * i]; + otp_ptr->dd_param[cnt].y = + ((u16)ddtempbuff[4 * i + 2] << 4) + + ((ddtempbuff[4 * i + 1] & 0xf0) >> 4) + 1; + otp_ptr->dd_param[cnt++].t = 2; + } else { + otp_ptr->dd_param[cnt].x = + (((u16)ddtempbuff[4 * i + 1] & 0x0f) << 8) + + ddtempbuff[4 * i]; + otp_ptr->dd_param[cnt].y = + ((u16)ddtempbuff[4 * i + 2] << 4) + + ((ddtempbuff[4 * i + 1] & 0xf0) >> 4); + otp_ptr->dd_param[cnt++].t = + ddtempbuff[4 * i + 3] & 0x03; + } + } + } + otp_ptr->dd_cnt = total_number; + otp_ptr->flag |= 0x08; + dev_err(dev, "%s GC8034 OTP:total_number = %d!\n", + __func__, total_number); + break; + case 0x08: + case 0x0c: + dev_err(dev, "%s GC8034_OTP_DD group %d is Invalid!\n", + __func__, index + 1); + break; + default: + break; + } + } + /* OTP Chip Register*/ + otp_flag = gc8034_otp_read_reg(client, 2, 0x4e); + if (otp_flag == 1) { + for (i = 0; i < 5; i++) { + dev_err(dev, "%s GC8034 reg is valid!\n", __func__); + temp = gc8034_otp_read_reg(client, 2, (0x4f + 5 * i)); + for (j = 0; j < 2; j++) { + if (((temp >> (4 * j + 3)) & 0x01) == 0x01) { + otp_ptr->reg_page[otp_ptr->reg_num] = + (temp >> (4 * j)) & 0x03; + otp_ptr->reg_addr[otp_ptr->reg_num] = + gc8034_otp_read_reg(client, + 2, + 0x50 + 5 * i + 2 * j); + otp_ptr->reg_value[otp_ptr->reg_num] = + gc8034_otp_read_reg(client, + 2, + 0x50 + 5 * i + 2 * j + 1); + otp_ptr->reg_num++; + } + } + } + otp_ptr->flag |= 0x04; + } + + if (otp_ptr->flag) { + gc8034->otp = otp_ptr; + } else { + gc8034->otp = NULL; + devm_kfree(dev, otp_ptr); + } + + return 0; +} + +static void gc8034_get_otp(struct gc8034_otp_info *otp, + struct rkmodule_inf *inf) +{ + u32 i; + + /* fac */ + if (otp->flag & 0x80) { + inf->fac.flag = 1; + inf->fac.year = otp->year; + inf->fac.month = otp->month; + inf->fac.day = otp->day; + for (i = 0; i < ARRAY_SIZE(gc8034_module_info) - 1; i++) { + if (gc8034_module_info[i].id == otp->module_id) + break; + } + strlcpy(inf->fac.module, gc8034_module_info[i].name, + sizeof(inf->fac.module)); + + for (i = 0; i < ARRAY_SIZE(gc8034_lens_info) - 1; i++) { + if (gc8034_lens_info[i].id == otp->lens_id) + break; + } + strlcpy(inf->fac.lens, gc8034_lens_info[i].name, + sizeof(inf->fac.lens)); + } + /* awb */ + if (otp->flag & 0x40) { + inf->awb.flag = 1; + inf->awb.r_value = otp->rg_ratio; + inf->awb.b_value = otp->bg_ratio; + inf->awb.gr_value = 0; + inf->awb.gb_value = 0; + + inf->awb.golden_r_value = 0; + inf->awb.golden_b_value = 0; + inf->awb.golden_gr_value = 0; + inf->awb.golden_gb_value = 0; + } + /* af */ + if (otp->flag & 0x20) { + inf->af.flag = 1; + inf->af.vcm_start = otp->vcm_start; + inf->af.vcm_end = otp->vcm_end; + inf->af.vcm_dir = otp->vcm_dir; + } +} + +static void gc8034_get_module_inf(struct gc8034 *gc8034, + struct rkmodule_inf *inf) +{ + struct gc8034_otp_info *otp = gc8034->otp; + + strlcpy(inf->base.sensor, + GC8034_NAME, + sizeof(inf->base.sensor)); + strlcpy(inf->base.module, + gc8034->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, + gc8034->len_name, + sizeof(inf->base.lens)); + if (otp) + gc8034_get_otp(otp, inf); +} + +static void gc8034_set_module_inf(struct gc8034 *gc8034, + struct rkmodule_awb_cfg *cfg) +{ + mutex_lock(&gc8034->mutex); + memcpy(&gc8034->awb_cfg, cfg, sizeof(*cfg)); + mutex_unlock(&gc8034->mutex); +} + +static long gc8034_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct gc8034 *gc8034 = to_gc8034(sd); + long ret = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + gc8034_get_module_inf(gc8034, (struct rkmodule_inf *)arg); + break; + case RKMODULE_AWB_CFG: + gc8034_set_module_inf(gc8034, (struct rkmodule_awb_cfg *)arg); + break; + default: + ret = -ENOTTY; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long gc8034_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_awb_cfg *cfg; + long ret = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = gc8034_ioctl(sd, cmd, inf); + if (!ret) + ret = copy_to_user(up, inf, sizeof(*inf)); + kfree(inf); + break; + case RKMODULE_AWB_CFG: + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(cfg, up, sizeof(*cfg)); + if (!ret) + ret = gc8034_ioctl(sd, cmd, cfg); + kfree(cfg); + break; + default: + ret = -ENOTTY; + break; + } + + return ret; +} +#endif + +/*--------------------------------------------------------------------------*/ +static int gc8034_apply_otp(struct gc8034 *gc8034) +{ + int R_gain, G_gain, B_gain, base_gain; + struct i2c_client *client = gc8034->client; + struct gc8034_otp_info *otp_ptr = gc8034->otp; + struct rkmodule_awb_cfg *awb_cfg = &gc8034->awb_cfg; + u32 golden_bg_ratio; + u32 golden_rg_ratio; + u32 golden_g_value; + u32 i, j; + u16 base = 0; + u32 dd_cnt = 0; + u8 temp_val0 = 0; + u8 temp_val1 = 0; + u8 temp_val2 = 0; + struct gc8034_dd dd_temp = {0, 0, 0}; + + if (!gc8034->awb_cfg.enable) + return 0; + + golden_g_value = (awb_cfg->golden_gb_value + + awb_cfg->golden_gr_value) / 2; + golden_bg_ratio = awb_cfg->golden_b_value * 0x400 / golden_g_value; + golden_rg_ratio = awb_cfg->golden_r_value * 0x400 / golden_g_value; + /* apply OTP WB Calibration */ + if ((otp_ptr->flag & 0x40) && golden_bg_ratio && golden_rg_ratio) { + /* calculate G gain */ + R_gain = golden_rg_ratio * 1000 / otp_ptr->rg_ratio; + B_gain = golden_bg_ratio * 1000 / otp_ptr->bg_ratio; + G_gain = 1000; + base_gain = (R_gain < B_gain) ? R_gain : B_gain; + base_gain = (base_gain < G_gain) ? base_gain : G_gain; + + R_gain = 0x400 * R_gain / (base_gain); + B_gain = 0x400 * B_gain / (base_gain); + G_gain = 0x400 * G_gain / (base_gain); + + /* update sensor WB gain */ + gc8034_write_reg(client, 0xfe, 0x01); + + gc8034_write_reg(client, 0x84, G_gain >> 3); + gc8034_write_reg(client, 0x85, R_gain >> 3); + gc8034_write_reg(client, 0x86, B_gain >> 3); + gc8034_write_reg(client, 0x87, G_gain >> 3); + gc8034_write_reg(client, 0x88, + ((G_gain & 0X07) << 4) + (R_gain & 0x07)); + gc8034_write_reg(client, 0x89, + ((B_gain & 0X07) << 4) + (G_gain & 0x07)); + + gc8034_write_reg(client, 0xfe, 0x00); + + dev_dbg(&client->dev, "apply awb gain: 0x%x, 0x%x, 0x%x\n", + R_gain, G_gain, B_gain); + } + /* apply OTP Lenc Calibration */ + if (otp_ptr->flag & 0x10) { + gc8034_write_reg(client, 0xfe, 0x01); + gc8034_write_reg(client, 0xcf, 0x00); + gc8034_write_reg(client, 0xc9, 0x01); + for (i = 0; i < 9; i++) { + gc8034_write_reg(client, 0xca, i * 0x0c); + for (j = 0; j < 11; j++) { +#if defined(GC8034_MIRROR_NORMAL) + base = 4 * (11 * i + j); +#elif defined(GC8034_MIRROR_H) + base = 4 * (11 * i + 10 - j); +#elif defined(GC8034_MIRROR_V) + base = 4 * (11 * (8 - i) + j); +#elif defined(GC8034_MIRROR_HV) + base = 4 * (11 * (8 - i) + 10 - j); +#endif + gc8034_write_reg(client, 0xcc, + otp_ptr->lsc[base + 0]); + gc8034_write_reg(client, 0xcc, + otp_ptr->lsc[base + 1]); + gc8034_write_reg(client, 0xcc, + otp_ptr->lsc[base + 2]); + gc8034_write_reg(client, 0xcc, + otp_ptr->lsc[base + 3]); + dev_dbg(&client->dev, + "apply lsc otp_ptr->lsc[%d]=%d\n", + base + 0, + otp_ptr->lsc[base + 0]); + dev_dbg(&client->dev, + "apply lsc otp_ptr->lsc[%d]=%d\n", + base + 1, + otp_ptr->lsc[base + 1]); + dev_dbg(&client->dev, + "apply lsc otp_ptr->lsc[%d]=%d\n", + base + 2, + otp_ptr->lsc[base + 2]); + dev_dbg(&client->dev, + "apply lsc otp_ptr->lsc[%d]=%d\n", + base + 3, + otp_ptr->lsc[base + 3]); + } + } + gc8034_write_reg(client, 0xcf, 0x01); + gc8034_write_reg(client, 0xa0, 0x13); + gc8034_write_reg(client, 0xfe, 0x00); + dev_err(&client->dev, "apply lsc\n"); + } + /* apply OTP DD Calibration */ + if (otp_ptr->flag & 0x08) { + dd_cnt = otp_ptr->dd_cnt; + for (i = 0; i < dd_cnt; i++) { +#if defined(GC8034_MIRROR_H) || defined(GC8034_MIRROR_HV) + switch (otp_ptr->dd_param[i].t) { + case 0: + otp_ptr->dd_param[i].x = + DD_WIDTH - otp_ptr->dd_param[i].x + 1; + break; + case 1: + otp_ptr->dd_param[i].x = + DD_WIDTH - otp_ptr->dd_param[i].x - 1; + break; + default: + otp_ptr->dd_param[i].x = + DD_WIDTH - otp_ptr->dd_param[i].x; + break; + } +#endif +#if defined(GC8034_MIRROR_V) || defined(GC8034_MIRROR_HV) + otp_ptr->dd_param[i].y = + DD_HEIGHT - otp_ptr->dd_param[i].y + 1; +#endif + } + for (i = 0; i < dd_cnt - 1; i++) { + for (j = i + 1; j < dd_cnt; j++) { + if (otp_ptr->dd_param[i].y * + DD_WIDTH + otp_ptr->dd_param[i].x > + otp_ptr->dd_param[j].y * DD_WIDTH + + otp_ptr->dd_param[j].x) { + dd_temp.x = otp_ptr->dd_param[i].x; + dd_temp.y = otp_ptr->dd_param[i].y; + dd_temp.t = otp_ptr->dd_param[i].t; + otp_ptr->dd_param[i].x = + otp_ptr->dd_param[j].x; + otp_ptr->dd_param[i].y = + otp_ptr->dd_param[j].y; + otp_ptr->dd_param[i].t = + otp_ptr->dd_param[j].t; + otp_ptr->dd_param[j].x = dd_temp.x; + otp_ptr->dd_param[j].y = dd_temp.y; + otp_ptr->dd_param[j].t = dd_temp.t; + } + } + } + gc8034_write_reg(client, 0xfe, 0x01); + gc8034_write_reg(client, 0xbe, 0x00); + gc8034_write_reg(client, 0xa9, 0x01); + for (i = 0; i < dd_cnt; i++) { + temp_val0 = otp_ptr->dd_param[i].x & 0x00ff; + temp_val1 = ((otp_ptr->dd_param[i].y & 0x000f) << 4) + + ((otp_ptr->dd_param[i].x & 0x0f00) >> 8); + temp_val2 = (otp_ptr->dd_param[i].y & 0x0ff0) >> 4; + gc8034_write_reg(client, 0xaa, i); + gc8034_write_reg(client, 0xac, temp_val0); + gc8034_write_reg(client, 0xac, temp_val1); + gc8034_write_reg(client, 0xac, temp_val2); + gc8034_write_reg(client, 0xac, otp_ptr->dd_param[i].t); + } + gc8034_write_reg(client, 0xbe, 0x01); + gc8034_write_reg(client, 0xfe, 0x00); + dev_err(&client->dev, "apply dd\n"); + } + + if (otp_ptr->flag & 0x04) { + gc8034_write_reg(client, 0xfe, 0x00); + for (i = 0; i < otp_ptr->reg_num; i++) { + gc8034_write_reg(client, 0xfe, otp_ptr->reg_page[i]); + gc8034_write_reg(client, + otp_ptr->reg_addr[i], + otp_ptr->reg_value[i]); + } + dev_err(&client->dev, "apply chip reg\n"); + } + return 0; +} + +static int __gc8034_start_stream(struct gc8034 *gc8034) +{ + int ret; + + if (gc8034->otp) { + ret = gc8034_otp_enable(gc8034); + gc8034_check_prsel(gc8034); + ret |= gc8034_apply_otp(gc8034); + usleep_range(1000, 2000); + ret |= gc8034_otp_disable(gc8034); + if (ret) + return ret; + } + ret = gc8034_write_array(gc8034->client, gc8034->cur_mode->reg_list); + if (ret) + return ret; + + /* In case these controls are set before streaming */ + mutex_unlock(&gc8034->mutex); + ret = v4l2_ctrl_handler_setup(&gc8034->ctrl_handler); + mutex_lock(&gc8034->mutex); + return ret; +} + +static int __gc8034_stop_stream(struct gc8034 *gc8034) +{ + return 0; +} + +static int gc8034_s_stream(struct v4l2_subdev *sd, int on) +{ + struct gc8034 *gc8034 = to_gc8034(sd); + struct i2c_client *client = gc8034->client; + int ret = 0; + + mutex_lock(&gc8034->mutex); + on = !!on; + if (on == gc8034->streaming) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = __gc8034_start_stream(gc8034); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + __gc8034_stop_stream(gc8034); + pm_runtime_put(&client->dev); + } + + gc8034->streaming = on; + +unlock_and_return: + mutex_unlock(&gc8034->mutex); + + return ret; +} + +static int gc8034_s_power(struct v4l2_subdev *sd, int on) +{ + struct gc8034 *gc8034 = to_gc8034(sd); + struct i2c_client *client = gc8034->client; + int ret = 0; + + mutex_lock(&gc8034->mutex); + + /* If the power state is not modified - no work to do. */ + if (gc8034->power_on == !!on) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = gc8034_write_array(gc8034->client, gc8034_global_regs); + if (ret) { + v4l2_err(sd, "could not set init registers\n"); + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + gc8034->power_on = true; + } else { + pm_runtime_put(&client->dev); + gc8034->power_on = false; + } + +unlock_and_return: + mutex_unlock(&gc8034->mutex); + + return ret; +} + +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 gc8034_cal_delay(u32 cycles) +{ + return DIV_ROUND_UP(cycles, GC8034_XVCLK_FREQ / 1000 / 1000); +} + +static int __gc8034_power_on(struct gc8034 *gc8034) +{ + int ret; + u32 delay_us; + struct device *dev = &gc8034->client->dev; + + if (!IS_ERR_OR_NULL(gc8034->pins_default)) { + ret = pinctrl_select_state(gc8034->pinctrl, + gc8034->pins_default); + if (ret < 0) + dev_err(dev, "could not set pins\n"); + } + ret = clk_set_rate(gc8034->xvclk, GC8034_XVCLK_FREQ); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate (24MHz)\n"); + if (clk_get_rate(gc8034->xvclk) != GC8034_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); + ret = clk_prepare_enable(gc8034->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + if (!IS_ERR(gc8034->reset_gpio)) + gpiod_set_value_cansleep(gc8034->reset_gpio, 1); + + ret = regulator_bulk_enable(GC8034_NUM_SUPPLIES, gc8034->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + + usleep_range(1000, 1100); + if (!IS_ERR(gc8034->reset_gpio)) + gpiod_set_value_cansleep(gc8034->reset_gpio, 0); + + usleep_range(500, 1000); + if (!IS_ERR(gc8034->pwdn_gpio)) + gpiod_set_value_cansleep(gc8034->pwdn_gpio, 0); + + /* 8192 cycles prior to first SCCB transaction */ + delay_us = gc8034_cal_delay(8192); + usleep_range(delay_us, delay_us * 2); + + return 0; + +disable_clk: + clk_disable_unprepare(gc8034->xvclk); + + return ret; +} + +static void __gc8034_power_off(struct gc8034 *gc8034) +{ + int ret; + + if (!IS_ERR(gc8034->pwdn_gpio)) + gpiod_set_value_cansleep(gc8034->pwdn_gpio, 1); + clk_disable_unprepare(gc8034->xvclk); + if (!IS_ERR(gc8034->reset_gpio)) + gpiod_set_value_cansleep(gc8034->reset_gpio, 1); + if (!IS_ERR_OR_NULL(gc8034->pins_sleep)) { + ret = pinctrl_select_state(gc8034->pinctrl, + gc8034->pins_sleep); + if (ret < 0) + dev_dbg(&gc8034->client->dev, "could not set pins\n"); + } + regulator_bulk_disable(GC8034_NUM_SUPPLIES, gc8034->supplies); +} + +static int gc8034_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc8034 *gc8034 = to_gc8034(sd); + + return __gc8034_power_on(gc8034); +} + +static int gc8034_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc8034 *gc8034 = to_gc8034(sd); + + __gc8034_power_off(gc8034); + + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int gc8034_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct gc8034 *gc8034 = to_gc8034(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + const struct gc8034_mode *def_mode = &supported_modes[0]; + + mutex_lock(&gc8034->mutex); + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = MEDIA_BUS_FMT_SRGGB10_1X10; + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&gc8034->mutex); + /* No crop or compose */ + + return 0; +} +#endif + +static const struct dev_pm_ops gc8034_pm_ops = { + SET_RUNTIME_PM_OPS(gc8034_runtime_suspend, + gc8034_runtime_resume, NULL) +}; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops gc8034_internal_ops = { + .open = gc8034_open, +}; +#endif + +static const struct v4l2_subdev_core_ops gc8034_core_ops = { + .s_power = gc8034_s_power, + .ioctl = gc8034_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = gc8034_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops gc8034_video_ops = { + .s_stream = gc8034_s_stream, + .g_frame_interval = gc8034_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops gc8034_pad_ops = { + .enum_mbus_code = gc8034_enum_mbus_code, + .enum_frame_size = gc8034_enum_frame_sizes, + .get_fmt = gc8034_get_fmt, + .set_fmt = gc8034_set_fmt, +}; + +static const struct v4l2_subdev_ops gc8034_subdev_ops = { + .core = &gc8034_core_ops, + .video = &gc8034_video_ops, + .pad = &gc8034_pad_ops, +}; + +static int gc8034_set_exposure_reg(struct gc8034 *gc8034, u32 exposure) +{ + int ret = 0; + u32 cal_shutter = 0; + + cal_shutter = exposure >> 1; + cal_shutter = cal_shutter << 1; + + gc8034->Dgain_ratio = 256 * exposure / cal_shutter; + ret = gc8034_write_reg(gc8034->client, + GC8034_REG_SET_PAGE, GC8034_SET_PAGE_ZERO); + ret |= gc8034_write_reg(gc8034->client, + GC8034_REG_EXPOSURE_H, + GC8034_FETCH_HIGH_BYTE_EXP(cal_shutter)); + ret |= gc8034_write_reg(gc8034->client, + GC8034_REG_EXPOSURE_L, + GC8034_FETCH_LOW_BYTE_EXP(cal_shutter)); + return ret; +} + +#define MAX_AG_INDEX 9 +#define AGC_REG_NUM 14 +#define MEAG_INDEX 7 + +u16 gain_level[MAX_AG_INDEX] = { + 0x0040, /* 1.000*/ + 0x0058, /* 1.375*/ + 0x007d, /* 1.950*/ + 0x00ad, /* 2.700*/ + 0x00f3, /* 3.800*/ + 0x0159, /* 5.400*/ + 0x01ea, /* 7.660*/ + 0x02ac, /*10.688*/ + 0x03c2, /*15.030*/ +}; + +u8 agc_register[MAX_AG_INDEX][AGC_REG_NUM] = { + /* fullsize */ + { 0x00, 0x55, 0x83, 0x01, 0x06, 0x18, 0x20, + 0x16, 0x17, 0x50, 0x6c, 0x9b, 0xd8, 0x00 }, + { 0x00, 0x55, 0x83, 0x01, 0x06, 0x18, 0x20, + 0x16, 0x17, 0x50, 0x6c, 0x9b, 0xd8, 0x00 }, + { 0x00, 0x4e, 0x84, 0x01, 0x0c, 0x2e, 0x2d, + 0x15, 0x19, 0x47, 0x70, 0x9f, 0xd8, 0x00 }, + { 0x00, 0x51, 0x80, 0x01, 0x07, 0x28, 0x32, + 0x22, 0x20, 0x49, 0x70, 0x91, 0xd9, 0x00 }, + { 0x00, 0x4d, 0x83, 0x01, 0x0f, 0x3b, 0x3b, + 0x1c, 0x1f, 0x47, 0x6f, 0x9b, 0xd3, 0x00 }, + { 0x00, 0x50, 0x83, 0x01, 0x08, 0x35, 0x46, + 0x1e, 0x22, 0x4c, 0x70, 0x9a, 0xd2, 0x00 }, + { 0x00, 0x52, 0x80, 0x01, 0x0c, 0x35, 0x3a, + 0x2b, 0x2d, 0x4c, 0x67, 0x8d, 0xc0, 0x00 }, + { 0x00, 0x52, 0x80, 0x01, 0x0c, 0x35, 0x3a, + 0x2b, 0x2d, 0x4c, 0x67, 0x8d, 0xc0, 0x00 }, + { 0x00, 0x52, 0x80, 0x01, 0x0c, 0x35, 0x3a, + 0x2b, 0x2d, 0x4c, 0x67, 0x8d, 0xc0, 0x00 } +}; + +static int gc8034_set_gain_reg(struct gc8034 *gc8034, u32 a_gain) +{ + int ret = 0; + u32 temp_gain = 0; + int gain_index = 0; + u32 Dgain_ratio = 0; + + Dgain_ratio = gc8034->Dgain_ratio; + for (gain_index = MEAG_INDEX - 1; gain_index >= 0; gain_index--) { + if (a_gain >= gain_level[gain_index]) { + ret = gc8034_write_reg(gc8034->client, + GC8034_REG_SET_PAGE, GC8034_SET_PAGE_ZERO); + ret |= gc8034_write_reg(gc8034->client, + 0xb6, gain_index); + temp_gain = 256 * a_gain / gain_level[gain_index]; + temp_gain = temp_gain * Dgain_ratio / 256; + ret |= gc8034_write_reg(gc8034->client, + 0xb1, temp_gain >> 8); + ret |= gc8034_write_reg(gc8034->client, + 0xb2, temp_gain & 0xff); + + ret |= gc8034_write_reg(gc8034->client, 0xfe, + agc_register[gain_index][0]); + ret |= gc8034_write_reg(gc8034->client, 0x20, + agc_register[gain_index][1]); + ret |= gc8034_write_reg(gc8034->client, 0x33, + agc_register[gain_index][2]); + ret |= gc8034_write_reg(gc8034->client, 0xfe, + agc_register[gain_index][3]); + ret |= gc8034_write_reg(gc8034->client, 0xdf, + agc_register[gain_index][4]); + ret |= gc8034_write_reg(gc8034->client, 0xe7, + agc_register[gain_index][5]); + ret |= gc8034_write_reg(gc8034->client, 0xe8, + agc_register[gain_index][6]); + ret |= gc8034_write_reg(gc8034->client, 0xe9, + agc_register[gain_index][7]); + ret |= gc8034_write_reg(gc8034->client, 0xea, + agc_register[gain_index][8]); + ret |= gc8034_write_reg(gc8034->client, 0xeb, + agc_register[gain_index][9]); + ret |= gc8034_write_reg(gc8034->client, 0xec, + agc_register[gain_index][10]); + ret |= gc8034_write_reg(gc8034->client, 0xed, + agc_register[gain_index][11]); + ret |= gc8034_write_reg(gc8034->client, 0xee, + agc_register[gain_index][12]); + ret |= gc8034_write_reg(gc8034->client, 0xfe, + agc_register[gain_index][13]); + break; + } + } + return ret; +} + +static int gc8034_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gc8034 *gc8034 = container_of(ctrl->handler, + struct gc8034, ctrl_handler); + struct i2c_client *client = gc8034->client; + s64 max; + int ret = 0; + + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ + max = gc8034->cur_mode->height + ctrl->val - 4; + __v4l2_ctrl_modify_range(gc8034->exposure, + gc8034->exposure->minimum, max, + gc8034->exposure->step, + gc8034->exposure->default_value); + break; + } + + if (pm_runtime_get(&client->dev) <= 0) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + /* 4 least significant bits of expsoure are fractional part */ + ret = gc8034_set_exposure_reg(gc8034, ctrl->val); + break; + case V4L2_CID_ANALOGUE_GAIN: + ret = gc8034_set_gain_reg(gc8034, ctrl->val); + break; + case V4L2_CID_VBLANK: + ret = gc8034_write_reg(gc8034->client, + GC8034_REG_SET_PAGE, + GC8034_SET_PAGE_ZERO); + ret |= gc8034_write_reg(gc8034->client, + GC8034_REG_VTS_H, + ((ctrl->val - 36) >> 8) & 0xff); + ret |= gc8034_write_reg(gc8034->client, + GC8034_REG_VTS_L, + (ctrl->val - 36) & 0xff); + break; + default: + dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", + __func__, ctrl->id, ctrl->val); + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops gc8034_ctrl_ops = { + .s_ctrl = gc8034_set_ctrl, +}; + +static int gc8034_initialize_controls(struct gc8034 *gc8034) +{ + const struct gc8034_mode *mode; + struct v4l2_ctrl_handler *handler; + struct v4l2_ctrl *ctrl; + s64 exposure_max, vblank_def; + u32 h_blank; + int ret; + + handler = &gc8034->ctrl_handler; + mode = gc8034->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 8); + if (ret) + return ret; + handler->lock = &gc8034->mutex; + + ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, + 0, 0, link_freq_menu_items); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, + 0, GC8034_PIXEL_RATE, 1, GC8034_PIXEL_RATE); + + h_blank = mode->hts_def - mode->width; + gc8034->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (gc8034->hblank) + gc8034->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = mode->vts_def - mode->height; + gc8034->vblank = v4l2_ctrl_new_std(handler, &gc8034_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + GC8034_VTS_MAX - mode->height, + 1, vblank_def); + + exposure_max = mode->vts_def - 4; + gc8034->exposure = v4l2_ctrl_new_std(handler, &gc8034_ctrl_ops, + V4L2_CID_EXPOSURE, GC8034_EXPOSURE_MIN, + exposure_max, GC8034_EXPOSURE_STEP, + mode->exp_def); + + gc8034->anal_gain = v4l2_ctrl_new_std(handler, &gc8034_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, GC8034_GAIN_MIN, + GC8034_GAIN_MAX, GC8034_GAIN_STEP, + GC8034_GAIN_DEFAULT); + if (handler->error) { + ret = handler->error; + dev_err(&gc8034->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + gc8034->subdev.ctrl_handler = handler; + + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +static int gc8034_check_sensor_id(struct gc8034 *gc8034, + struct i2c_client *client) +{ + struct device *dev = &gc8034->client->dev; + u16 id = 0; + u8 reg_H = 0; + u8 reg_L = 0; + int ret; + + ret = gc8034_read_reg(client, GC8034_REG_CHIP_ID_H, ®_H); + ret |= gc8034_read_reg(client, GC8034_REG_CHIP_ID_L, ®_L); + id = ((reg_H << 8) & 0xff00) | (reg_L & 0xff); + if (id != CHIP_ID) { + dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret); + return -ENODEV; + } + + return ret; +} + +static int gc8034_configure_regulators(struct gc8034 *gc8034) +{ + unsigned int i; + + for (i = 0; i < GC8034_NUM_SUPPLIES; i++) + gc8034->supplies[i].supply = gc8034_supply_names[i]; + + return devm_regulator_bulk_get(&gc8034->client->dev, + GC8034_NUM_SUPPLIES, + gc8034->supplies); +} + +static int gc8034_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct gc8034 *gc8034; + struct v4l2_subdev *sd; + char facing[2]; + int ret; + + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + gc8034 = devm_kzalloc(dev, sizeof(*gc8034), GFP_KERNEL); + if (!gc8034) + return -ENOMEM; + + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &gc8034->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &gc8034->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &gc8034->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &gc8034->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + gc8034->client = client; + gc8034->cur_mode = &supported_modes[0]; + + gc8034->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(gc8034->xvclk)) { + dev_err(dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + gc8034->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(gc8034->reset_gpio)) + dev_warn(dev, "Failed to get reset-gpios\n"); + + gc8034->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(gc8034->pwdn_gpio)) + dev_warn(dev, "Failed to get pwdn-gpios\n"); + + ret = gc8034_configure_regulators(gc8034); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + + gc8034->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR(gc8034->pinctrl)) { + gc8034->pins_default = + pinctrl_lookup_state(gc8034->pinctrl, + OF_CAMERA_PINCTRL_STATE_DEFAULT); + if (IS_ERR(gc8034->pins_default)) + dev_err(dev, "could not get default pinstate\n"); + + gc8034->pins_sleep = + pinctrl_lookup_state(gc8034->pinctrl, + OF_CAMERA_PINCTRL_STATE_SLEEP); + if (IS_ERR(gc8034->pins_sleep)) + dev_err(dev, "could not get sleep pinstate\n"); + } + + mutex_init(&gc8034->mutex); + + sd = &gc8034->subdev; + v4l2_i2c_subdev_init(sd, client, &gc8034_subdev_ops); + ret = gc8034_initialize_controls(gc8034); + if (ret) + goto err_destroy_mutex; + + ret = __gc8034_power_on(gc8034); + if (ret) + goto err_free_handler; + + ret = gc8034_check_sensor_id(gc8034, client); + if (ret) + goto err_power_off; + + gc8034_otp_enable(gc8034); + gc8034_otp_read(gc8034); + gc8034_otp_disable(gc8034); + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &gc8034_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + gc8034->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &gc8034->pad); + if (ret < 0) + goto err_power_off; +#endif + + memset(facing, 0, sizeof(facing)); + if (strcmp(gc8034->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + gc8034->module_index, facing, + GC8034_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor_common(sd); + if (ret) { + dev_err(dev, "v4l2 async register subdev failed\n"); + goto err_clean_entity; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; + +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif +err_power_off: + __gc8034_power_off(gc8034); +err_free_handler: + v4l2_ctrl_handler_free(&gc8034->ctrl_handler); +err_destroy_mutex: + mutex_destroy(&gc8034->mutex); + + return ret; +} + +static int gc8034_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc8034 *gc8034 = to_gc8034(sd); + + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + v4l2_ctrl_handler_free(&gc8034->ctrl_handler); + mutex_destroy(&gc8034->mutex); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __gc8034_power_off(gc8034); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id gc8034_of_match[] = { + { .compatible = "galaxycore,gc8034" }, + {}, +}; +MODULE_DEVICE_TABLE(of, gc8034_of_match); +#endif + +static const struct i2c_device_id gc8034_match_id[] = { + { "galaxycore,gc8034", 0}, + { }, +}; + +static struct i2c_driver gc8034_i2c_driver = { + .driver = { + .name = GC8034_NAME, + .pm = &gc8034_pm_ops, + .of_match_table = of_match_ptr(gc8034_of_match), + }, + .probe = &gc8034_probe, + .remove = &gc8034_remove, + .id_table = gc8034_match_id, +}; + +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&gc8034_i2c_driver); +} + +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&gc8034_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_DESCRIPTION("GalaxyCore gc8034 sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index b7338a8b746d..affa61261e1c 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -16,12 +16,16 @@ #include #include #include +#include +#include #include #include #include #include #include +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x0) + /* IMX219 supported geometry */ #define IMX219_TABLE_END 0xffff #define IMX219_ANALOGUE_GAIN_MULTIPLIER 256 @@ -40,6 +44,8 @@ #define IMX219_EXP_LINES_MARGIN 4 +#define IMX219_NAME "imx219" + static const s64 link_freq_menu_items[] = { 456000000, }; @@ -231,6 +237,10 @@ struct imx219 { struct v4l2_ctrl *pixel_rate; const struct imx219_mode *cur_mode; u16 cur_vts; + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; }; static const struct imx219_mode supported_modes[] = { @@ -711,6 +721,77 @@ static int imx219_get_fmt(struct v4l2_subdev *sd, return 0; } +static void imx219_get_module_inf(struct imx219 *imx219, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strlcpy(inf->base.sensor, IMX219_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, imx219->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, imx219->len_name, sizeof(inf->base.lens)); +} + +static long imx219_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct imx219 *imx219 = to_imx219(client); + long ret = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + imx219_get_module_inf(imx219, (struct rkmodule_inf *)arg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long imx219_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_awb_cfg *cfg; + long ret; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = imx219_ioctl(sd, cmd, inf); + if (!ret) + ret = copy_to_user(up, inf, sizeof(*inf)); + kfree(inf); + break; + case RKMODULE_AWB_CFG: + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(cfg, up, sizeof(*cfg)); + if (!ret) + ret = imx219_ioctl(sd, cmd, cfg); + kfree(cfg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + /* Various V4L2 operations tables */ static struct v4l2_subdev_video_ops imx219_subdev_video_ops = { .s_stream = imx219_s_stream, @@ -719,6 +800,10 @@ static struct v4l2_subdev_video_ops imx219_subdev_video_ops = { static struct v4l2_subdev_core_ops imx219_subdev_core_ops = { .s_power = imx219_s_power, + .ioctl = imx219_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = imx219_compat_ioctl32, +#endif }; static const struct v4l2_subdev_pad_ops imx219_subdev_pad_ops = { @@ -889,8 +974,17 @@ static int imx219_probe(struct i2c_client *client, { struct imx219 *priv; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct v4l2_subdev *sd; + char facing[2]; int ret; + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { dev_warn(&adapter->dev, "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE\n"); @@ -900,6 +994,19 @@ static int imx219_probe(struct i2c_client *client, if (!priv) return -ENOMEM; + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &priv->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &priv->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &priv->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &priv->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + priv->clk = devm_clk_get(&client->dev, NULL); if (IS_ERR(priv->clk)) { dev_info(&client->dev, "Error %ld getting clock\n", @@ -923,14 +1030,26 @@ static int imx219_probe(struct i2c_client *client, if (ret < 0) return ret; - priv->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + priv->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; + priv->pad.flags = MEDIA_PAD_FL_SOURCE; - priv->subdev.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; - ret = media_entity_init(&priv->subdev.entity, 1, &priv->pad, 0); + priv->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&priv->subdev.entity, 1, &priv->pad); if (ret < 0) return ret; - ret = v4l2_async_register_subdev(&priv->subdev); + sd = &priv->subdev; + memset(facing, 0, sizeof(facing)); + if (strcmp(priv->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + priv->module_index, facing, + IMX219_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor_common(sd); if (ret < 0) return ret; @@ -963,7 +1082,7 @@ MODULE_DEVICE_TABLE(i2c, imx219_id); static struct i2c_driver imx219_i2c_driver = { .driver = { .of_match_table = of_match_ptr(imx219_of_match), - .name = "imx219", + .name = IMX219_NAME, }, .probe = imx219_probe, .remove = imx219_remove, diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c index 31a1e2294843..c0999a402fd6 100644 --- a/drivers/media/i2c/imx258.c +++ b/drivers/media/i2c/imx258.c @@ -1,797 +1,1500 @@ // SPDX-License-Identifier: GPL-2.0 -// Copyright (C) 2018 Intel Corporation +/* + * imx258 driver + * + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd. + * + * V0.0X01.0X01 add poweron function. + * V0.0X01.0X02 fix mclk issue when probe multiple camera. + */ -#include +#include +#include #include +#include #include #include #include +#include +#include +#include +#include +#include +#include #include -#include -#include +#include +#include +#include "imx258_eeprom_head.h" + +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x02) + +#ifndef V4L2_CID_DIGITAL_GAIN +#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN +#endif + +#define IMX258_LANES 4 +#define IMX258_BITS_PER_SAMPLE 10 +#define IMX258_LINK_FREQ_498MHZ 498000000 +#define IMX258_LINK_FREQ_399MHZ 399000000 +/* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ +#define IMX258_PIXEL_RATE_FULL_SIZE 398400000 +#define IMX258_PIXEL_RATE_BINNING 319200000 +#define IMX258_XVCLK_FREQ 24000000 + +#define CHIP_ID 0x0258 +#define IMX258_REG_CHIP_ID 0x0016 + +#define IMX258_REG_CTRL_MODE 0x0100 +#define IMX258_MODE_SW_STANDBY 0x0 +#define IMX258_MODE_STREAMING BIT(0) + +#define IMX258_REG_EXPOSURE 0x0202 +#define IMX258_EXPOSURE_MIN 4 +#define IMX258_EXPOSURE_STEP 1 +#define IMX258_VTS_MAX 0xffff + +#define IMX258_REG_GAIN_H 0x0204 +#define IMX258_REG_GAIN_L 0x0205 +#define IMX258_GAIN_MIN 0 +#define IMX258_GAIN_MAX 0x1fff +#define IMX258_GAIN_STEP 1 +#define IMX258_GAIN_DEFAULT 0x0 + +#define IMX258_REG_TEST_PATTERN 0x0600 +#define IMX258_TEST_PATTERN_ENABLE 0x80 +#define IMX258_TEST_PATTERN_DISABLE 0x0 + +#define IMX258_REG_VTS 0x0340 + +#define REG_NULL 0xFFFF #define IMX258_REG_VALUE_08BIT 1 #define IMX258_REG_VALUE_16BIT 2 +#define IMX258_REG_VALUE_24BIT 3 -#define IMX258_REG_MODE_SELECT 0x0100 -#define IMX258_MODE_STANDBY 0x00 -#define IMX258_MODE_STREAMING 0x01 +#define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default" +#define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep" -/* Chip ID */ -#define IMX258_REG_CHIP_ID 0x0016 -#define IMX258_CHIP_ID 0x0258 +#define IMX258_NAME "imx258" -/* V_TIMING internal */ -#define IMX258_VTS_30FPS 0x0c98 -#define IMX258_VTS_30FPS_2K 0x0638 -#define IMX258_VTS_30FPS_VGA 0x034c -#define IMX258_VTS_MAX 0xffff +static const char * const imx258_supply_names[] = { + "avdd", /* Analog power */ + "dovdd", /* Digital I/O power */ + "dvdd", /* Digital core power */ +}; -/*Frame Length Line*/ -#define IMX258_FLL_MIN 0x08a6 -#define IMX258_FLL_MAX 0xffff -#define IMX258_FLL_STEP 1 -#define IMX258_FLL_DEFAULT 0x0c98 +#define IMX258_NUM_SUPPLIES ARRAY_SIZE(imx258_supply_names) -/* HBLANK control - read only */ -#define IMX258_PPL_DEFAULT 5352 - -/* Exposure control */ -#define IMX258_REG_EXPOSURE 0x0202 -#define IMX258_EXPOSURE_MIN 4 -#define IMX258_EXPOSURE_STEP 1 -#define IMX258_EXPOSURE_DEFAULT 0x640 -#define IMX258_EXPOSURE_MAX 65535 - -/* Analog gain control */ -#define IMX258_REG_ANALOG_GAIN 0x0204 -#define IMX258_ANA_GAIN_MIN 0 -#define IMX258_ANA_GAIN_MAX 0x1fff -#define IMX258_ANA_GAIN_STEP 1 -#define IMX258_ANA_GAIN_DEFAULT 0x0 - -/* Digital gain control */ -#define IMX258_REG_GR_DIGITAL_GAIN 0x020e -#define IMX258_REG_R_DIGITAL_GAIN 0x0210 -#define IMX258_REG_B_DIGITAL_GAIN 0x0212 -#define IMX258_REG_GB_DIGITAL_GAIN 0x0214 -#define IMX258_DGTL_GAIN_MIN 0 -#define IMX258_DGTL_GAIN_MAX 4096 /* Max = 0xFFF */ -#define IMX258_DGTL_GAIN_DEFAULT 1024 -#define IMX258_DGTL_GAIN_STEP 1 - -/* Test Pattern Control */ -#define IMX258_REG_TEST_PATTERN 0x0600 -#define IMX258_TEST_PATTERN_DISABLE 0 -#define IMX258_TEST_PATTERN_SOLID_COLOR 1 -#define IMX258_TEST_PATTERN_COLOR_BARS 2 -#define IMX258_TEST_PATTERN_GREY_COLOR 3 -#define IMX258_TEST_PATTERN_PN9 4 - -/* Orientation */ -#define REG_MIRROR_FLIP_CONTROL 0x0101 -#define REG_CONFIG_MIRROR_FLIP 0x03 -#define REG_CONFIG_FLIP_TEST_PATTERN 0x02 - -struct imx258_reg { - u16 address; +struct regval { + u16 addr; u8 val; }; -struct imx258_reg_list { - u32 num_of_regs; - const struct imx258_reg *regs; -}; - -/* Link frequency config */ -struct imx258_link_freq_config { - u32 pixels_per_line; - - /* PLL registers for this link frequency */ - struct imx258_reg_list reg_list; -}; - -/* Mode : resolution and related config&values */ struct imx258_mode { - /* Frame width */ u32 width; - /* Frame height */ u32 height; - - /* V-timing */ + struct v4l2_fract max_fps; + u32 hts_def; u32 vts_def; - u32 vts_min; - - /* Index of Link frequency config to be used */ - u32 link_freq_index; - /* Default register values */ - struct imx258_reg_list reg_list; + u32 exp_def; + const struct regval *reg_list; }; -/* 4208x3118 needs 1267Mbps/lane, 4 lanes */ -static const struct imx258_reg mipi_data_rate_1267mbps[] = { - { 0x0301, 0x05 }, - { 0x0303, 0x02 }, - { 0x0305, 0x03 }, - { 0x0306, 0x00 }, - { 0x0307, 0xC6 }, - { 0x0309, 0x0A }, - { 0x030B, 0x01 }, - { 0x030D, 0x02 }, - { 0x030E, 0x00 }, - { 0x030F, 0xD8 }, - { 0x0310, 0x00 }, - { 0x0820, 0x13 }, - { 0x0821, 0x4C }, - { 0x0822, 0xCC }, - { 0x0823, 0xCC }, +struct imx258 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; + struct regulator_bulk_data supplies[IMX258_NUM_SUPPLIES]; + + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_sleep; + + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *anal_gain; + struct v4l2_ctrl *digi_gain; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *test_pattern; + struct mutex mutex; + bool streaming; + bool power_on; + const struct imx258_mode *cur_mode; + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate; + struct imx258_otp_info *otp; + struct rkmodule_inf module_inf; + struct rkmodule_awb_cfg awb_cfg; + struct rkmodule_lsc_cfg lsc_cfg; }; -static const struct imx258_reg mipi_data_rate_640mbps[] = { - { 0x0301, 0x05 }, - { 0x0303, 0x02 }, - { 0x0305, 0x03 }, - { 0x0306, 0x00 }, - { 0x0307, 0x64 }, - { 0x0309, 0x0A }, - { 0x030B, 0x01 }, - { 0x030D, 0x02 }, - { 0x030E, 0x00 }, - { 0x030F, 0xD8 }, - { 0x0310, 0x00 }, - { 0x0820, 0x0A }, - { 0x0821, 0x00 }, - { 0x0822, 0x00 }, - { 0x0823, 0x00 }, +#define to_imx258(sd) container_of(sd, struct imx258, subdev) + +struct imx258_id_name { + u32 id; + char name[RKMODULE_NAME_LEN]; }; -static const struct imx258_reg mode_4208x3118_regs[] = { - { 0x0136, 0x13 }, - { 0x0137, 0x33 }, - { 0x3051, 0x00 }, - { 0x3052, 0x00 }, - { 0x4E21, 0x14 }, - { 0x6B11, 0xCF }, - { 0x7FF0, 0x08 }, - { 0x7FF1, 0x0F }, - { 0x7FF2, 0x08 }, - { 0x7FF3, 0x1B }, - { 0x7FF4, 0x23 }, - { 0x7FF5, 0x60 }, - { 0x7FF6, 0x00 }, - { 0x7FF7, 0x01 }, - { 0x7FF8, 0x00 }, - { 0x7FF9, 0x78 }, - { 0x7FFA, 0x00 }, - { 0x7FFB, 0x00 }, - { 0x7FFC, 0x00 }, - { 0x7FFD, 0x00 }, - { 0x7FFE, 0x00 }, - { 0x7FFF, 0x03 }, - { 0x7F76, 0x03 }, - { 0x7F77, 0xFE }, - { 0x7FA8, 0x03 }, - { 0x7FA9, 0xFE }, - { 0x7B24, 0x81 }, - { 0x7B25, 0x00 }, - { 0x6564, 0x07 }, - { 0x6B0D, 0x41 }, - { 0x653D, 0x04 }, - { 0x6B05, 0x8C }, - { 0x6B06, 0xF9 }, - { 0x6B08, 0x65 }, - { 0x6B09, 0xFC }, - { 0x6B0A, 0xCF }, - { 0x6B0B, 0xD2 }, - { 0x6700, 0x0E }, - { 0x6707, 0x0E }, - { 0x9104, 0x00 }, - { 0x4648, 0x7F }, - { 0x7420, 0x00 }, - { 0x7421, 0x1C }, - { 0x7422, 0x00 }, - { 0x7423, 0xD7 }, - { 0x5F04, 0x00 }, - { 0x5F05, 0xED }, - { 0x0112, 0x0A }, - { 0x0113, 0x0A }, - { 0x0114, 0x03 }, - { 0x0342, 0x14 }, - { 0x0343, 0xE8 }, - { 0x0340, 0x0C }, - { 0x0341, 0x50 }, - { 0x0344, 0x00 }, - { 0x0345, 0x00 }, - { 0x0346, 0x00 }, - { 0x0347, 0x00 }, - { 0x0348, 0x10 }, - { 0x0349, 0x6F }, - { 0x034A, 0x0C }, - { 0x034B, 0x2E }, - { 0x0381, 0x01 }, - { 0x0383, 0x01 }, - { 0x0385, 0x01 }, - { 0x0387, 0x01 }, - { 0x0900, 0x00 }, - { 0x0901, 0x11 }, - { 0x0401, 0x00 }, - { 0x0404, 0x00 }, - { 0x0405, 0x10 }, - { 0x0408, 0x00 }, - { 0x0409, 0x00 }, - { 0x040A, 0x00 }, - { 0x040B, 0x00 }, - { 0x040C, 0x10 }, - { 0x040D, 0x70 }, - { 0x040E, 0x0C }, - { 0x040F, 0x30 }, - { 0x3038, 0x00 }, - { 0x303A, 0x00 }, - { 0x303B, 0x10 }, - { 0x300D, 0x00 }, - { 0x034C, 0x10 }, - { 0x034D, 0x70 }, - { 0x034E, 0x0C }, - { 0x034F, 0x30 }, - { 0x0350, 0x01 }, - { 0x0202, 0x0C }, - { 0x0203, 0x46 }, - { 0x0204, 0x00 }, - { 0x0205, 0x00 }, - { 0x020E, 0x01 }, - { 0x020F, 0x00 }, - { 0x0210, 0x01 }, - { 0x0211, 0x00 }, - { 0x0212, 0x01 }, - { 0x0213, 0x00 }, - { 0x0214, 0x01 }, - { 0x0215, 0x00 }, - { 0x7BCD, 0x00 }, - { 0x94DC, 0x20 }, - { 0x94DD, 0x20 }, - { 0x94DE, 0x20 }, - { 0x95DC, 0x20 }, - { 0x95DD, 0x20 }, - { 0x95DE, 0x20 }, - { 0x7FB0, 0x00 }, - { 0x9010, 0x3E }, - { 0x9419, 0x50 }, - { 0x941B, 0x50 }, - { 0x9519, 0x50 }, - { 0x951B, 0x50 }, - { 0x3030, 0x00 }, - { 0x3032, 0x00 }, - { 0x0220, 0x00 }, +static const struct imx258_id_name imx258_module_info[] = { + {0x36, "GuangDongLiteArray"}, + {0x0d, "CameraKing"}, + {0x00, "Unknown"} }; -static const struct imx258_reg mode_2104_1560_regs[] = { - { 0x0136, 0x13 }, - { 0x0137, 0x33 }, - { 0x3051, 0x00 }, - { 0x3052, 0x00 }, - { 0x4E21, 0x14 }, - { 0x6B11, 0xCF }, - { 0x7FF0, 0x08 }, - { 0x7FF1, 0x0F }, - { 0x7FF2, 0x08 }, - { 0x7FF3, 0x1B }, - { 0x7FF4, 0x23 }, - { 0x7FF5, 0x60 }, - { 0x7FF6, 0x00 }, - { 0x7FF7, 0x01 }, - { 0x7FF8, 0x00 }, - { 0x7FF9, 0x78 }, - { 0x7FFA, 0x00 }, - { 0x7FFB, 0x00 }, - { 0x7FFC, 0x00 }, - { 0x7FFD, 0x00 }, - { 0x7FFE, 0x00 }, - { 0x7FFF, 0x03 }, - { 0x7F76, 0x03 }, - { 0x7F77, 0xFE }, - { 0x7FA8, 0x03 }, - { 0x7FA9, 0xFE }, - { 0x7B24, 0x81 }, - { 0x7B25, 0x00 }, - { 0x6564, 0x07 }, - { 0x6B0D, 0x41 }, - { 0x653D, 0x04 }, - { 0x6B05, 0x8C }, - { 0x6B06, 0xF9 }, - { 0x6B08, 0x65 }, - { 0x6B09, 0xFC }, - { 0x6B0A, 0xCF }, - { 0x6B0B, 0xD2 }, - { 0x6700, 0x0E }, - { 0x6707, 0x0E }, - { 0x9104, 0x00 }, - { 0x4648, 0x7F }, - { 0x7420, 0x00 }, - { 0x7421, 0x1C }, - { 0x7422, 0x00 }, - { 0x7423, 0xD7 }, - { 0x5F04, 0x00 }, - { 0x5F05, 0xED }, - { 0x0112, 0x0A }, - { 0x0113, 0x0A }, - { 0x0114, 0x03 }, - { 0x0342, 0x14 }, - { 0x0343, 0xE8 }, - { 0x0340, 0x06 }, - { 0x0341, 0x38 }, - { 0x0344, 0x00 }, - { 0x0345, 0x00 }, - { 0x0346, 0x00 }, - { 0x0347, 0x00 }, - { 0x0348, 0x10 }, - { 0x0349, 0x6F }, - { 0x034A, 0x0C }, - { 0x034B, 0x2E }, - { 0x0381, 0x01 }, - { 0x0383, 0x01 }, - { 0x0385, 0x01 }, - { 0x0387, 0x01 }, - { 0x0900, 0x01 }, - { 0x0901, 0x12 }, - { 0x0401, 0x01 }, - { 0x0404, 0x00 }, - { 0x0405, 0x20 }, - { 0x0408, 0x00 }, - { 0x0409, 0x02 }, - { 0x040A, 0x00 }, - { 0x040B, 0x00 }, - { 0x040C, 0x10 }, - { 0x040D, 0x6A }, - { 0x040E, 0x06 }, - { 0x040F, 0x18 }, - { 0x3038, 0x00 }, - { 0x303A, 0x00 }, - { 0x303B, 0x10 }, - { 0x300D, 0x00 }, - { 0x034C, 0x08 }, - { 0x034D, 0x38 }, - { 0x034E, 0x06 }, - { 0x034F, 0x18 }, - { 0x0350, 0x01 }, - { 0x0202, 0x06 }, - { 0x0203, 0x2E }, - { 0x0204, 0x00 }, - { 0x0205, 0x00 }, - { 0x020E, 0x01 }, - { 0x020F, 0x00 }, - { 0x0210, 0x01 }, - { 0x0211, 0x00 }, - { 0x0212, 0x01 }, - { 0x0213, 0x00 }, - { 0x0214, 0x01 }, - { 0x0215, 0x00 }, - { 0x7BCD, 0x01 }, - { 0x94DC, 0x20 }, - { 0x94DD, 0x20 }, - { 0x94DE, 0x20 }, - { 0x95DC, 0x20 }, - { 0x95DD, 0x20 }, - { 0x95DE, 0x20 }, - { 0x7FB0, 0x00 }, - { 0x9010, 0x3E }, - { 0x9419, 0x50 }, - { 0x941B, 0x50 }, - { 0x9519, 0x50 }, - { 0x951B, 0x50 }, - { 0x3030, 0x00 }, - { 0x3032, 0x00 }, - { 0x0220, 0x00 }, +static const struct imx258_id_name imx258_lens_info[] = { + {0x47, "Sunny 3923C"}, + {0x07, "Largen 9611A6"}, + {0x00, "Unknown"} }; -static const struct imx258_reg mode_1048_780_regs[] = { - { 0x0136, 0x13 }, - { 0x0137, 0x33 }, - { 0x3051, 0x00 }, - { 0x3052, 0x00 }, - { 0x4E21, 0x14 }, - { 0x6B11, 0xCF }, - { 0x7FF0, 0x08 }, - { 0x7FF1, 0x0F }, - { 0x7FF2, 0x08 }, - { 0x7FF3, 0x1B }, - { 0x7FF4, 0x23 }, - { 0x7FF5, 0x60 }, - { 0x7FF6, 0x00 }, - { 0x7FF7, 0x01 }, - { 0x7FF8, 0x00 }, - { 0x7FF9, 0x78 }, - { 0x7FFA, 0x00 }, - { 0x7FFB, 0x00 }, - { 0x7FFC, 0x00 }, - { 0x7FFD, 0x00 }, - { 0x7FFE, 0x00 }, - { 0x7FFF, 0x03 }, - { 0x7F76, 0x03 }, - { 0x7F77, 0xFE }, - { 0x7FA8, 0x03 }, - { 0x7FA9, 0xFE }, - { 0x7B24, 0x81 }, - { 0x7B25, 0x00 }, - { 0x6564, 0x07 }, - { 0x6B0D, 0x41 }, - { 0x653D, 0x04 }, - { 0x6B05, 0x8C }, - { 0x6B06, 0xF9 }, - { 0x6B08, 0x65 }, - { 0x6B09, 0xFC }, - { 0x6B0A, 0xCF }, - { 0x6B0B, 0xD2 }, - { 0x6700, 0x0E }, - { 0x6707, 0x0E }, - { 0x9104, 0x00 }, - { 0x4648, 0x7F }, - { 0x7420, 0x00 }, - { 0x7421, 0x1C }, - { 0x7422, 0x00 }, - { 0x7423, 0xD7 }, - { 0x5F04, 0x00 }, - { 0x5F05, 0xED }, - { 0x0112, 0x0A }, - { 0x0113, 0x0A }, - { 0x0114, 0x03 }, - { 0x0342, 0x14 }, - { 0x0343, 0xE8 }, - { 0x0340, 0x03 }, - { 0x0341, 0x4C }, - { 0x0344, 0x00 }, - { 0x0345, 0x00 }, - { 0x0346, 0x00 }, - { 0x0347, 0x00 }, - { 0x0348, 0x10 }, - { 0x0349, 0x6F }, - { 0x034A, 0x0C }, - { 0x034B, 0x2E }, - { 0x0381, 0x01 }, - { 0x0383, 0x01 }, - { 0x0385, 0x01 }, - { 0x0387, 0x01 }, - { 0x0900, 0x01 }, - { 0x0901, 0x14 }, - { 0x0401, 0x01 }, - { 0x0404, 0x00 }, - { 0x0405, 0x40 }, - { 0x0408, 0x00 }, - { 0x0409, 0x06 }, - { 0x040A, 0x00 }, - { 0x040B, 0x00 }, - { 0x040C, 0x10 }, - { 0x040D, 0x64 }, - { 0x040E, 0x03 }, - { 0x040F, 0x0C }, - { 0x3038, 0x00 }, - { 0x303A, 0x00 }, - { 0x303B, 0x10 }, - { 0x300D, 0x00 }, - { 0x034C, 0x04 }, - { 0x034D, 0x18 }, - { 0x034E, 0x03 }, - { 0x034F, 0x0C }, - { 0x0350, 0x01 }, - { 0x0202, 0x03 }, - { 0x0203, 0x42 }, - { 0x0204, 0x00 }, - { 0x0205, 0x00 }, - { 0x020E, 0x01 }, - { 0x020F, 0x00 }, - { 0x0210, 0x01 }, - { 0x0211, 0x00 }, - { 0x0212, 0x01 }, - { 0x0213, 0x00 }, - { 0x0214, 0x01 }, - { 0x0215, 0x00 }, - { 0x7BCD, 0x00 }, - { 0x94DC, 0x20 }, - { 0x94DD, 0x20 }, - { 0x94DE, 0x20 }, - { 0x95DC, 0x20 }, - { 0x95DD, 0x20 }, - { 0x95DE, 0x20 }, - { 0x7FB0, 0x00 }, - { 0x9010, 0x3E }, - { 0x9419, 0x50 }, - { 0x941B, 0x50 }, - { 0x9519, 0x50 }, - { 0x951B, 0x50 }, - { 0x3030, 0x00 }, - { 0x3032, 0x00 }, - { 0x0220, 0x00 }, +/* + * Xclk 24Mhz + */ +static const struct regval imx258_global_regs[] = { + {0x0136, 0x18}, + {0x0137, 0x00}, + {0x3051, 0x00}, + {0x6b11, 0xcf}, + {0x7ff0, 0x08}, + {0x7ff1, 0x0f}, + {0x7ff2, 0x08}, + {0x7ff3, 0x1b}, + {0x7ff4, 0x23}, + {0x7ff5, 0x60}, + {0x7ff6, 0x00}, + {0x7ff7, 0x01}, + {0x7ff8, 0x00}, + {0x7ff9, 0x78}, + {0x7ffa, 0x01}, + {0x7ffb, 0x00}, + {0x7ffc, 0x00}, + {0x7ffd, 0x00}, + {0x7ffe, 0x00}, + {0x7fff, 0x03}, + {0x7f76, 0x03}, + {0x7f77, 0xfe}, + {0x7fa8, 0x03}, + {0x7fa9, 0xfe}, + {0x7b24, 0x81}, + {0x7b25, 0x01}, + {0x6564, 0x07}, + {0x6b0d, 0x41}, + {0x653d, 0x04}, + {0x6b05, 0x8c}, + {0x6b06, 0xf9}, + {0x6b08, 0x65}, + {0x6b09, 0xfc}, + {0x6b0a, 0xcf}, + {0x6b0b, 0xd2}, + {0x6700, 0x0e}, + {0x6707, 0x0e}, + {0x5f04, 0x00}, + {0x5f05, 0xed}, + {0x94c7, 0xff}, + {0x94c8, 0xff}, + {0x94c9, 0xff}, + {0x95c7, 0xff}, + {0x95c8, 0xff}, + {0x95c9, 0xff}, + {0x94c4, 0x3f}, + {0x94c5, 0x3f}, + {0x94c6, 0x3f}, + {0x95c4, 0x3f}, + {0x95c5, 0x3f}, + {0x95c6, 0x3f}, + {0x94c1, 0x02}, + {0x94c2, 0x02}, + {0x94c3, 0x02}, + {0x95c1, 0x02}, + {0x95c2, 0x02}, + {0x95c3, 0x02}, + {0x94be, 0x0c}, + {0x94bf, 0x0c}, + {0x94c0, 0x0c}, + {0x95be, 0x0c}, + {0x95bf, 0x0c}, + {0x95c0, 0x0c}, + {0x94d0, 0x74}, + {0x94d1, 0x74}, + {0x94d2, 0x74}, + {0x95d0, 0x74}, + {0x95d1, 0x74}, + {0x95d2, 0x74}, + {0x94cd, 0x2e}, + {0x94ce, 0x2e}, + {0x94cf, 0x2e}, + {0x95cd, 0x2e}, + {0x95ce, 0x2e}, + {0x95cf, 0x2e}, + {0x94ca, 0x4c}, + {0x94cb, 0x4c}, + {0x94cc, 0x4c}, + {0x95ca, 0x4c}, + {0x95cb, 0x4c}, + {0x95cc, 0x4c}, + {0x900e, 0x32}, + {0x94e2, 0xff}, + {0x94e3, 0xff}, + {0x94e4, 0xff}, + {0x95e2, 0xff}, + {0x95e3, 0xff}, + {0x95e4, 0xff}, + {0x94df, 0x6e}, + {0x94e0, 0x6e}, + {0x94e1, 0x6e}, + {0x95df, 0x6e}, + {0x95e0, 0x6e}, + {0x95e1, 0x6e}, + {0x7fcc, 0x01}, + {0x7b78, 0x00}, + {0x9401, 0x35}, + {0x9403, 0x23}, + {0x9405, 0x23}, + {0x9406, 0x00}, + {0x9407, 0x31}, + {0x9408, 0x00}, + {0x9409, 0x1b}, + {0x940a, 0x00}, + {0x940b, 0x15}, + {0x940d, 0x3f}, + {0x940f, 0x3f}, + {0x9411, 0x3f}, + {0x9413, 0x64}, + {0x9415, 0x64}, + {0x9417, 0x64}, + {0x941d, 0x34}, + {0x941f, 0x01}, + {0x9421, 0x01}, + {0x9423, 0x01}, + {0x9425, 0x23}, + {0x9427, 0x23}, + {0x9429, 0x23}, + {0x942b, 0x2f}, + {0x942d, 0x1a}, + {0x942f, 0x14}, + {0x9431, 0x3f}, + {0x9433, 0x3f}, + {0x9435, 0x3f}, + {0x9437, 0x6b}, + {0x9439, 0x7c}, + {0x943b, 0x81}, + {0x9443, 0x0f}, + {0x9445, 0x0f}, + {0x9447, 0x0f}, + {0x9449, 0x0f}, + {0x944b, 0x0f}, + {0x944d, 0x0f}, + {0x944f, 0x1e}, + {0x9451, 0x0f}, + {0x9453, 0x0b}, + {0x9455, 0x28}, + {0x9457, 0x13}, + {0x9459, 0x0c}, + {0x945d, 0x00}, + {0x945e, 0x00}, + {0x945f, 0x00}, + {0x946d, 0x00}, + {0x946f, 0x10}, + {0x9471, 0x10}, + {0x9473, 0x40}, + {0x9475, 0x2e}, + {0x9477, 0x10}, + {0x9478, 0x0a}, + {0x947b, 0xe0}, + {0x947c, 0xe0}, + {0x947d, 0xe0}, + {0x947e, 0xe0}, + {0x947f, 0xe0}, + {0x9480, 0xe0}, + {0x9483, 0x14}, + {0x9485, 0x14}, + {0x9487, 0x14}, + {0x9501, 0x35}, + {0x9503, 0x14}, + {0x9505, 0x14}, + {0x9507, 0x31}, + {0x9509, 0x1b}, + {0x950b, 0x15}, + {0x950d, 0x1e}, + {0x950f, 0x1e}, + {0x9511, 0x1e}, + {0x9513, 0x64}, + {0x9515, 0x64}, + {0x9517, 0x64}, + {0x951d, 0x34}, + {0x951f, 0x01}, + {0x9521, 0x01}, + {0x9523, 0x01}, + {0x9525, 0x14}, + {0x9527, 0x14}, + {0x9529, 0x14}, + {0x952b, 0x2f}, + {0x952d, 0x1a}, + {0x952f, 0x14}, + {0x9531, 0x1e}, + {0x9533, 0x1e}, + {0x9535, 0x1e}, + {0x9537, 0x6b}, + {0x9539, 0x7c}, + {0x953b, 0x81}, + {0x9543, 0x0f}, + {0x9545, 0x0f}, + {0x9547, 0x0f}, + {0x9549, 0x0f}, + {0x954b, 0x0f}, + {0x954d, 0x0f}, + {0x954f, 0x15}, + {0x9551, 0x0b}, + {0x9553, 0x08}, + {0x9555, 0x1c}, + {0x9557, 0x0d}, + {0x9559, 0x08}, + {0x955d, 0x00}, + {0x955e, 0x00}, + {0x955f, 0x00}, + {0x956d, 0x00}, + {0x956f, 0x10}, + {0x9571, 0x10}, + {0x9573, 0x40}, + {0x9575, 0x2e}, + {0x9577, 0x10}, + {0x9578, 0x0a}, + {0x957b, 0xe0}, + {0x957c, 0xe0}, + {0x957d, 0xe0}, + {0x957e, 0xe0}, + {0x957f, 0xe0}, + {0x9580, 0xe0}, + {0x9583, 0x14}, + {0x9585, 0x14}, + {0x9587, 0x14}, + {0x7f78, 0x00}, + {0x7f89, 0x00}, + {0x7f93, 0x00}, + {0x924b, 0x1b}, + {0x924c, 0x0a}, + {0x9304, 0x04}, + {0x9315, 0x04}, + {0x9250, 0x50}, + {0x9251, 0x3c}, + {0x9252, 0x14}, + {0x0112, 0x0a}, + {0x0113, 0x0a}, + {0x0114, 0x03}, + {0x0301, 0x05}, + {0x0303, 0x02}, + {0x0305, 0x04}, + {0x0306, 0x00}, + {0x0307, 0xa6}, + {0x0309, 0x0a}, + {0x030b, 0x01}, + {0x030d, 0x02}, + {0x030e, 0x00}, + {0x030f, 0xd8}, + {0x0310, 0x00}, + {0x0820, 0x0f}, + {0x0821, 0x90}, + {0x0822, 0x00}, + {0x0823, 0x00}, + {0x4648, 0x7f}, + {0x7420, 0x00}, + {0x7421, 0x1c}, + {0x7422, 0x00}, + {0x7423, 0xd7}, + {0x9104, 0x00}, + {0x0342, 0x14}, + {0x0343, 0xe8}, + {0x0340, 0x0e}, + {0x0341, 0x88}, + {0x0344, 0x00}, + {0x0345, 0x00}, + {0x0346, 0x00}, + {0x0347, 0x00}, + {0x0348, 0x10}, + {0x0349, 0x6f}, + {0x034a, 0x0c}, + {0x034b, 0x2f}, + {0x0381, 0x01}, + {0x0383, 0x01}, + {0x0385, 0x01}, + {0x0387, 0x01}, + {0x0900, 0x00}, + {0x0901, 0x11}, + {0x0401, 0x00}, + {0x0404, 0x00}, + {0x0405, 0x10}, + {0x0408, 0x00}, + {0x0409, 0x00}, + {0x040a, 0x00}, + {0x040b, 0x00}, + {0x040c, 0x10}, + {0x040d, 0x70}, + {0x040e, 0x0c}, + {0x040f, 0x30}, + {0x3038, 0x00}, + {0x303a, 0x00}, + {0x034c, 0x10}, + {0x034d, 0x70}, + {0x034e, 0x0c}, + {0x034f, 0x30}, + {0x0202, 0x0e}, + {0x0203, 0x7e}, + {0x0204, 0x00}, + {0x0205, 0x00}, + {0x020e, 0x01}, + {0x020f, 0x00}, + {0x0210, 0x01}, + {0x0211, 0x00}, + {0x0212, 0x01}, + {0x0213, 0x00}, + {0x0214, 0x01}, + {0x0215, 0x00}, + {0x7bcd, 0x00}, + {0x94dc, 0x20}, + {0x94dd, 0x20}, + {0x94de, 0x20}, + {0x95dc, 0x20}, + {0x95dd, 0x20}, + {0x95de, 0x20}, + {0x7fb0, 0x00}, + {0x9010, 0x3e}, + {0x9419, 0x50}, + {0x941b, 0x50}, + {0x9519, 0x50}, + {0x951b, 0x50}, + {0x3030, 0x00}, + {0x3032, 0x00}, + {0x0100, 0x00}, + {REG_NULL, 0x00}, +}; + +/* + * Xclk 24Mhz + * max_framerate 30fps + * mipi_datarate per lane 600Mbps + */ +static const struct regval imx258_2096x1560_regs[] = { + {0x0112, 0x0a}, + {0x0113, 0x0a}, + {0x0114, 0x03}, + {0x0301, 0x05}, + {0x0303, 0x02}, + {0x0305, 0x04}, + {0x0306, 0x00}, + {0x0307, 0x85}, + {0x0309, 0x0a}, + {0x030b, 0x01}, + {0x030d, 0x02}, + {0x030e, 0x00}, + {0x030f, 0xd8}, + {0x0310, 0x00}, + {0x0820, 0x0c}, + {0x0821, 0x78}, + {0x0822, 0x00}, + {0x0823, 0x00}, + {0x4648, 0x7f}, + {0x7420, 0x00}, + {0x7421, 0x1c}, + {0x7422, 0x00}, + {0x7423, 0xd7}, + {0x9104, 0x00}, + {0x0342, 0x14}, + {0x0343, 0xe8}, + {0x0340, 0x07}, + {0x0341, 0xc4}, + {0x0344, 0x00}, + {0x0345, 0x00}, + {0x0346, 0x00}, + {0x0347, 0x00}, + {0x0348, 0x10}, + {0x0349, 0x6f}, + {0x034a, 0x0c}, + {0x034b, 0x2f}, + {0x0381, 0x01}, + {0x0383, 0x01}, + {0x0385, 0x01}, + {0x0387, 0x01}, + {0x0900, 0x01}, + {0x0901, 0x12}, + {0x0401, 0x01}, + {0x0404, 0x00}, + {0x0405, 0x20}, + {0x0408, 0x00}, + {0x0409, 0x06}, + {0x040a, 0x00}, + {0x040b, 0x00}, + {0x040c, 0x10}, + {0x040d, 0x62}, + {0x040e, 0x06}, + {0x040f, 0x18}, + {0x3038, 0x00}, + {0x303a, 0x00}, + {0x303b, 0x10}, + {0x300d, 0x00}, + {0x034c, 0x08}, + {0x034d, 0x30}, + {0x034e, 0x06}, + {0x034f, 0x18}, + {0x0100, 0x00}, + {REG_NULL, 0x00}, +}; + +/* + * Xclk 24Mhz + * max_framerate 7fps + * mipi_datarate per lane 600Mbps + */ +static const struct regval imx258_4208x3120_regs[] = { + {0x0112, 0x0a}, + {0x0113, 0x0a}, + {0x0114, 0x03}, + {0x0301, 0x05}, + {0x0303, 0x02}, + {0x0305, 0x04}, + {0x0306, 0x00}, + {0x0307, 0xa6}, + {0x0309, 0x0a}, + {0x030b, 0x01}, + {0x030d, 0x02}, + {0x030e, 0x00}, + {0x030f, 0xd8}, + {0x0310, 0x00}, + {0x0820, 0x0f}, + {0x0821, 0x90}, + {0x0822, 0x00}, + {0x0823, 0x00}, + {0x4648, 0x7f}, + {0x7420, 0x00}, + {0x7421, 0x1c}, + {0x7422, 0x00}, + {0x7423, 0xd7}, + {0x9104, 0x00}, + {0x0342, 0x14}, + {0x0343, 0xe8}, + {0x0340, 0x0e}, + {0x0341, 0x88}, + {0x0344, 0x00}, + {0x0345, 0x00}, + {0x0346, 0x00}, + {0x0347, 0x00}, + {0x0348, 0x10}, + {0x0349, 0x6f}, + {0x034a, 0x0c}, + {0x034b, 0x2f}, + {0x0381, 0x01}, + {0x0383, 0x01}, + {0x0385, 0x01}, + {0x0387, 0x01}, + {0x0900, 0x00}, + {0x0901, 0x11}, + {0x0401, 0x00}, + {0x0404, 0x00}, + {0x0405, 0x10}, + {0x0408, 0x00}, + {0x0409, 0x00}, + {0x040a, 0x00}, + {0x040b, 0x00}, + {0x040c, 0x10}, + {0x040d, 0x70}, + {0x040e, 0x0c}, + {0x040f, 0x30}, + {0x3038, 0x00}, + {0x303a, 0x00}, + {0x303b, 0x10}, + {0x300d, 0x00}, + {0x034c, 0x10}, + {0x034d, 0x70}, + {0x034e, 0x0c}, + {0x034f, 0x30}, + {0x0100, 0x00}, + {REG_NULL, 0x00}, +}; + +static const struct imx258_mode supported_modes[] = { + { + .width = 4208, + .height = 3120, + .max_fps = { + .numerator = 10000, + .denominator = 200000, + }, + .exp_def = 0x0E7E, + .hts_def = 0x14E8, + .vts_def = 0x0E88, + .reg_list = imx258_4208x3120_regs, + }, + { + .width = 2096, + .height = 1560, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x07BA, + .hts_def = 0x14E8, + .vts_def = 0x07C4, + .reg_list = imx258_2096x1560_regs, + }, +}; + +static const s64 link_freq_menu_items[] = { + IMX258_LINK_FREQ_498MHZ, + IMX258_LINK_FREQ_399MHZ }; static const char * const imx258_test_pattern_menu[] = { "Disabled", - "Color Bars", - "Solid Color", - "Grey Color Bars", - "PN9" + "Vertical Color Bar Type 1", + "Vertical Color Bar Type 2", + "Vertical Color Bar Type 3", + "Vertical Color Bar Type 4" }; -static const int imx258_test_pattern_val[] = { - IMX258_TEST_PATTERN_DISABLE, - IMX258_TEST_PATTERN_COLOR_BARS, - IMX258_TEST_PATTERN_SOLID_COLOR, - IMX258_TEST_PATTERN_GREY_COLOR, - IMX258_TEST_PATTERN_PN9, -}; - -/* Configurations for supported link frequencies */ -#define IMX258_LINK_FREQ_634MHZ 633600000ULL -#define IMX258_LINK_FREQ_320MHZ 320000000ULL - -enum { - IMX258_LINK_FREQ_1267MBPS, - IMX258_LINK_FREQ_640MBPS, -}; - -/* - * pixel_rate = link_freq * data-rate * nr_of_lanes / bits_per_sample - * data rate => double data rate; number of lanes => 4; bits per pixel => 10 - */ -static u64 link_freq_to_pixel_rate(u64 f) +/* Write registers up to 4 at a time */ +static int imx258_write_reg(struct i2c_client *client, u16 reg, + int len, u32 val) { - f *= 2 * 4; - do_div(f, 10); - - return f; -} - -/* Menu items for LINK_FREQ V4L2 control */ -static const s64 link_freq_menu_items[] = { - IMX258_LINK_FREQ_634MHZ, - IMX258_LINK_FREQ_320MHZ, -}; - -/* Link frequency configs */ -static const struct imx258_link_freq_config link_freq_configs[] = { - [IMX258_LINK_FREQ_1267MBPS] = { - .pixels_per_line = IMX258_PPL_DEFAULT, - .reg_list = { - .num_of_regs = ARRAY_SIZE(mipi_data_rate_1267mbps), - .regs = mipi_data_rate_1267mbps, - } - }, - [IMX258_LINK_FREQ_640MBPS] = { - .pixels_per_line = IMX258_PPL_DEFAULT, - .reg_list = { - .num_of_regs = ARRAY_SIZE(mipi_data_rate_640mbps), - .regs = mipi_data_rate_640mbps, - } - }, -}; - -/* Mode configs */ -static const struct imx258_mode supported_modes[] = { - { - .width = 4208, - .height = 3118, - .vts_def = IMX258_VTS_30FPS, - .vts_min = IMX258_VTS_30FPS, - .reg_list = { - .num_of_regs = ARRAY_SIZE(mode_4208x3118_regs), - .regs = mode_4208x3118_regs, - }, - .link_freq_index = IMX258_LINK_FREQ_1267MBPS, - }, - { - .width = 2104, - .height = 1560, - .vts_def = IMX258_VTS_30FPS_2K, - .vts_min = IMX258_VTS_30FPS_2K, - .reg_list = { - .num_of_regs = ARRAY_SIZE(mode_2104_1560_regs), - .regs = mode_2104_1560_regs, - }, - .link_freq_index = IMX258_LINK_FREQ_640MBPS, - }, - { - .width = 1048, - .height = 780, - .vts_def = IMX258_VTS_30FPS_VGA, - .vts_min = IMX258_VTS_30FPS_VGA, - .reg_list = { - .num_of_regs = ARRAY_SIZE(mode_1048_780_regs), - .regs = mode_1048_780_regs, - }, - .link_freq_index = IMX258_LINK_FREQ_640MBPS, - }, -}; - -struct imx258 { - struct v4l2_subdev sd; - struct media_pad pad; - - struct v4l2_ctrl_handler ctrl_handler; - /* V4L2 Controls */ - struct v4l2_ctrl *link_freq; - struct v4l2_ctrl *pixel_rate; - struct v4l2_ctrl *vblank; - struct v4l2_ctrl *hblank; - struct v4l2_ctrl *exposure; - - /* Current mode */ - const struct imx258_mode *cur_mode; - - /* - * Mutex for serialized access: - * Protect sensor module set pad format and start/stop streaming safely. - */ - struct mutex mutex; - - /* Streaming on/off */ - bool streaming; -}; - -static inline struct imx258 *to_imx258(struct v4l2_subdev *_sd) -{ - return container_of(_sd, struct imx258, sd); -} - -/* Read registers up to 2 at a time */ -static int imx258_read_reg(struct imx258 *imx258, u16 reg, u32 len, u32 *val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&imx258->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 imx258_write_reg(struct imx258 *imx258, u16 reg, u32 len, u32 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); + u32 buf_i, val_i; u8 buf[6]; + u8 *val_p; + __be32 val_be; if (len > 4) return -EINVAL; - put_unaligned_be16(reg, buf); - put_unaligned_be32(val << (8 * (4 - len)), buf + 2); + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + + val_be = cpu_to_be32(val); + val_p = (u8 *)&val_be; + buf_i = 2; + val_i = 4 - len; + + while (val_i < 4) + buf[buf_i++] = val_p[val_i++]; + if (i2c_master_send(client, buf, len + 2) != len + 2) return -EIO; return 0; } -/* Write a list of registers */ -static int imx258_write_regs(struct imx258 *imx258, - const struct imx258_reg *regs, u32 len) +static int imx258_write_array(struct i2c_client *client, + const struct regval *regs) { - struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); - unsigned int i; + u32 i; + int ret = 0; + + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) + ret = imx258_write_reg(client, regs[i].addr, + IMX258_REG_VALUE_08BIT, + regs[i].val); + return ret; +} + +/* Read registers up to 4 at a time */ +static int imx258_read_reg(struct i2c_client *client, u16 reg, + unsigned int len, u32 *val) +{ + struct i2c_msg msgs[2]; + u8 *data_be_p; + __be32 data_be = 0; + __be16 reg_addr_be = cpu_to_be16(reg); int ret; - for (i = 0; i < len; i++) { - ret = imx258_write_reg(imx258, 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); + if (len > 4 || !len) + return -EINVAL; - return ret; + data_be_p = (u8 *)&data_be; + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 2; + msgs[0].buf = (u8 *)®_addr_be; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_be_p[4 - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = be32_to_cpu(data_be); + + return 0; +} + +static int imx258_get_reso_dist(const struct imx258_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); +} + +static const struct imx258_mode * + imx258_find_best_fit(struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *framefmt = &fmt->format; + int dist; + int cur_best_fit = 0; + int cur_best_fit_dist = -1; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { + dist = imx258_get_reso_dist(&supported_modes[i], framefmt); + if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { + cur_best_fit_dist = dist; + cur_best_fit = i; } } - return 0; + return &supported_modes[cur_best_fit]; } -/* Open sub-device */ -static int imx258_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +static int imx258_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) { - struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, fh->pad, 0); + struct imx258 *imx258 = to_imx258(sd); + const struct imx258_mode *mode; + s64 h_blank, vblank_def; - /* Initialize try_fmt */ - try_fmt->width = supported_modes[0].width; - try_fmt->height = supported_modes[0].height; - try_fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; - try_fmt->field = V4L2_FIELD_NONE; + mutex_lock(&imx258->mutex); + + mode = imx258_find_best_fit(fmt); + fmt->format.code = MEDIA_BUS_FMT_SRGGB10_1X10; + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; +#else + mutex_unlock(&imx258->mutex); + return -ENOTTY; +#endif + } else { + imx258->cur_mode = mode; + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(imx258->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(imx258->vblank, vblank_def, + IMX258_VTS_MAX - mode->height, + 1, vblank_def); + if (mode->width == 2096 && mode->height == 1560) { + __v4l2_ctrl_s_ctrl(imx258->link_freq, + link_freq_menu_items[1]); + __v4l2_ctrl_s_ctrl_int64(imx258->pixel_rate, + IMX258_PIXEL_RATE_BINNING); + } else { + __v4l2_ctrl_s_ctrl(imx258->link_freq, + link_freq_menu_items[0]); + __v4l2_ctrl_s_ctrl_int64(imx258->pixel_rate, + IMX258_PIXEL_RATE_FULL_SIZE); + } + } + mutex_unlock(&imx258->mutex); return 0; } -static int imx258_update_digital_gain(struct imx258 *imx258, u32 len, u32 val) +static int imx258_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct imx258 *imx258 = to_imx258(sd); + const struct imx258_mode *mode = imx258->cur_mode; + + mutex_lock(&imx258->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); +#else + mutex_unlock(&imx258->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = MEDIA_BUS_FMT_SRGGB10_1X10; + fmt->format.field = V4L2_FIELD_NONE; + } + mutex_unlock(&imx258->mutex); + + return 0; +} + +static int imx258_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index != 0) + return -EINVAL; + code->code = MEDIA_BUS_FMT_SRGGB10_1X10; + + return 0; +} + +static int imx258_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fse->code != MEDIA_BUS_FMT_SRGGB10_1X10) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = supported_modes[fse->index].width; + fse->max_height = supported_modes[fse->index].height; + fse->min_height = supported_modes[fse->index].height; + + return 0; +} + +static int imx258_enable_test_pattern(struct imx258 *imx258, u32 pattern) +{ + u32 val; + + if (pattern) + val = (pattern - 1) | IMX258_TEST_PATTERN_ENABLE; + else + val = IMX258_TEST_PATTERN_DISABLE; + + return imx258_write_reg(imx258->client, + IMX258_REG_TEST_PATTERN, + IMX258_REG_VALUE_08BIT, + val); +} + +static int imx258_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct imx258 *imx258 = to_imx258(sd); + const struct imx258_mode *mode = imx258->cur_mode; + + mutex_lock(&imx258->mutex); + fi->interval = mode->max_fps; + mutex_unlock(&imx258->mutex); + + return 0; +} + +static void imx258_get_otp(struct imx258_otp_info *otp, + struct rkmodule_inf *inf) +{ + u32 i; + + /* fac */ + if (otp->flag & 0x80) { + inf->fac.flag = 1; + inf->fac.year = otp->year; + inf->fac.month = otp->month; + inf->fac.day = otp->day; + for (i = 0; i < ARRAY_SIZE(imx258_module_info) - 1; i++) { + if (imx258_module_info[i].id == otp->module_id) + break; + } + strlcpy(inf->fac.module, imx258_module_info[i].name, + sizeof(inf->fac.module)); + + for (i = 0; i < ARRAY_SIZE(imx258_lens_info) - 1; i++) { + if (imx258_lens_info[i].id == otp->lens_id) + break; + } + strlcpy(inf->fac.lens, imx258_lens_info[i].name, + sizeof(inf->fac.lens)); + } + /* awb */ + if (otp->flag & 0x40) { + inf->awb.flag = 1; + inf->awb.r_value = otp->rg_ratio; + inf->awb.b_value = otp->bg_ratio; + inf->awb.gr_value = 0x400; + inf->awb.gb_value = 0x400; + + inf->awb.golden_r_value = 0; + inf->awb.golden_b_value = 0; + inf->awb.golden_gr_value = 0; + inf->awb.golden_gb_value = 0; + } + /* af */ + if (otp->flag & 0x20) { + inf->af.flag = 1; + inf->af.vcm_start = otp->vcm_start; + inf->af.vcm_end = otp->vcm_end; + inf->af.vcm_dir = otp->vcm_dir; + } + /* lsc */ + if (otp->flag & 0x10) { + inf->lsc.flag = 1; + inf->lsc.decimal_bits = 0; + inf->lsc.lsc_w = 9; + inf->lsc.lsc_h = 14; + + for (i = 0; i < 126; i++) { + inf->lsc.lsc_r[i] = otp->lenc[i]; + inf->lsc.lsc_gr[i] = otp->lenc[i + 126]; + inf->lsc.lsc_gb[i] = otp->lenc[i + 252]; + inf->lsc.lsc_b[i] = otp->lenc[i + 378]; + } + } +} + +static void imx258_get_module_inf(struct imx258 *imx258, + struct rkmodule_inf *inf) +{ + struct imx258_otp_info *otp = imx258->otp; + + strlcpy(inf->base.sensor, IMX258_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, + imx258->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, imx258->len_name, sizeof(inf->base.lens)); + if (otp) + imx258_get_otp(otp, inf); +} + +static void imx258_set_awb_cfg(struct imx258 *imx258, + struct rkmodule_awb_cfg *cfg) +{ + mutex_lock(&imx258->mutex); + memcpy(&imx258->awb_cfg, cfg, sizeof(*cfg)); + mutex_unlock(&imx258->mutex); +} + +static void imx258_set_lsc_cfg(struct imx258 *imx258, + struct rkmodule_lsc_cfg *cfg) +{ + mutex_lock(&imx258->mutex); + memcpy(&imx258->lsc_cfg, cfg, sizeof(*cfg)); + mutex_unlock(&imx258->mutex); +} + +static long imx258_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct imx258 *imx258 = to_imx258(sd); + long ret = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + imx258_get_module_inf(imx258, (struct rkmodule_inf *)arg); + break; + case RKMODULE_AWB_CFG: + imx258_set_awb_cfg(imx258, (struct rkmodule_awb_cfg *)arg); + break; + case RKMODULE_LSC_CFG: + imx258_set_lsc_cfg(imx258, (struct rkmodule_lsc_cfg *)arg); + break; + default: + ret = -ENOTTY; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long imx258_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_awb_cfg *awb_cfg; + struct rkmodule_lsc_cfg *lsc_cfg; + long ret = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = imx258_ioctl(sd, cmd, inf); + if (!ret) + ret = copy_to_user(up, inf, sizeof(*inf)); + kfree(inf); + break; + case RKMODULE_AWB_CFG: + awb_cfg = kzalloc(sizeof(*awb_cfg), GFP_KERNEL); + if (!awb_cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(awb_cfg, up, sizeof(*awb_cfg)); + if (!ret) + ret = imx258_ioctl(sd, cmd, awb_cfg); + kfree(awb_cfg); + break; + case RKMODULE_LSC_CFG: + lsc_cfg = kzalloc(sizeof(*lsc_cfg), GFP_KERNEL); + if (!lsc_cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(lsc_cfg, up, sizeof(*lsc_cfg)); + if (!ret) + ret = imx258_ioctl(sd, cmd, lsc_cfg); + kfree(lsc_cfg); + break; + default: + ret = -ENOTTY; + break; + } + return ret; +} +#endif + +/*--------------------------------------------------------------------------*/ +static int imx258_apply_otp(struct imx258 *imx258) +{ + int R_gain, G_gain, B_gain, base_gain; + struct i2c_client *client = imx258->client; + struct imx258_otp_info *otp_ptr = imx258->otp; + struct rkmodule_awb_cfg *awb_cfg = &imx258->awb_cfg; + struct rkmodule_lsc_cfg *lsc_cfg = &imx258->lsc_cfg; + u32 golden_bg_ratio = 0; + u32 golden_rg_ratio = 0; + u32 golden_g_value = 0; + u32 bg_ratio; + u32 rg_ratio; + //u32 g_value; + u32 i; + + if (awb_cfg->enable) { + golden_g_value = (awb_cfg->golden_gb_value + + awb_cfg->golden_gr_value) / 2; + golden_bg_ratio = awb_cfg->golden_b_value * 0x400 / golden_g_value; + golden_rg_ratio = awb_cfg->golden_r_value * 0x400 / golden_g_value; + } + /* apply OTP WB Calibration */ + if ((otp_ptr->flag & 0x40) && golden_bg_ratio && golden_rg_ratio) { + rg_ratio = otp_ptr->rg_ratio; + bg_ratio = otp_ptr->bg_ratio; + dev_dbg(&client->dev, "rg:0x%x,bg:0x%x,gol rg:0x%x,bg:0x%x\n", + rg_ratio, bg_ratio, golden_rg_ratio, golden_bg_ratio); + /* calculate G gain */ + R_gain = golden_rg_ratio * 1000 / rg_ratio; + B_gain = golden_bg_ratio * 1000 / bg_ratio; + G_gain = 1000; + if (R_gain < 1000 || B_gain < 1000) { + if (R_gain < B_gain) + base_gain = R_gain; + else + base_gain = B_gain; + } else { + base_gain = G_gain; + } + R_gain = 0x100 * R_gain / (base_gain); + B_gain = 0x100 * B_gain / (base_gain); + G_gain = 0x100 * G_gain / (base_gain); + /* update sensor WB gain */ + if (R_gain > 0x100) { + imx258_write_reg(client, 0x0210, + IMX258_REG_VALUE_08BIT, R_gain >> 8); + imx258_write_reg(client, 0x0211, + IMX258_REG_VALUE_08BIT, R_gain & 0x00ff); + } + if (G_gain > 0x100) { + imx258_write_reg(client, 0x020e, + IMX258_REG_VALUE_08BIT, G_gain >> 8); + imx258_write_reg(client, 0x020f, + IMX258_REG_VALUE_08BIT, G_gain & 0x00ff); + imx258_write_reg(client, 0x0214, + IMX258_REG_VALUE_08BIT, G_gain >> 8); + imx258_write_reg(client, 0x0215, + IMX258_REG_VALUE_08BIT, G_gain & 0x00ff); + } + if (B_gain > 0x100) { + imx258_write_reg(client, 0x0212, + IMX258_REG_VALUE_08BIT, B_gain >> 8); + imx258_write_reg(client, 0x0213, + IMX258_REG_VALUE_08BIT, B_gain & 0x00ff); + } + dev_dbg(&client->dev, "apply awb gain: 0x%x, 0x%x, 0x%x\n", + R_gain, G_gain, B_gain); + } + + /* apply OTP Lenc Calibration */ + if ((otp_ptr->flag & 0x10) && lsc_cfg->enable) { + for (i = 0; i < 504; i++) { + imx258_write_reg(client, 0xA300 + i, + IMX258_REG_VALUE_08BIT, otp_ptr->lenc[i]); + dev_dbg(&client->dev, "apply lenc[%d]: 0x%x\n", + i, otp_ptr->lenc[i]); + } + usleep_range(1000, 2000); + //choose lsc table 1 + imx258_write_reg(client, 0x3021, + IMX258_REG_VALUE_08BIT, 0x01); + //enable lsc + imx258_write_reg(client, 0x0B00, + IMX258_REG_VALUE_08BIT, 0x01); + } + + /* apply OTP SPC Calibration */ + if (otp_ptr->flag & 0x08) { + for (i = 0; i < 63; i++) { + imx258_write_reg(client, 0xD04C + i, + IMX258_REG_VALUE_08BIT, otp_ptr->spc[i]); + dev_dbg(&client->dev, "apply spc[%d]: 0x%x\n", + i, otp_ptr->spc[i]); + imx258_write_reg(client, 0xD08C + i, + IMX258_REG_VALUE_08BIT, otp_ptr->spc[i + 63]); + dev_dbg(&client->dev, "apply spc[%d]: 0x%x\n", + i + 63, otp_ptr->spc[i + 63]); + } + //enable spc + imx258_write_reg(client, 0x7BC8, + IMX258_REG_VALUE_08BIT, 0x01); + } + return 0; +} + +static int __imx258_start_stream(struct imx258 *imx258) { int ret; - ret = imx258_write_reg(imx258, IMX258_REG_GR_DIGITAL_GAIN, - IMX258_REG_VALUE_16BIT, - val); + ret = imx258_write_array(imx258->client, imx258->cur_mode->reg_list); if (ret) return ret; - ret = imx258_write_reg(imx258, IMX258_REG_GB_DIGITAL_GAIN, - IMX258_REG_VALUE_16BIT, - val); + + /* In case these controls are set before streaming */ + mutex_unlock(&imx258->mutex); + ret = v4l2_ctrl_handler_setup(&imx258->ctrl_handler); + mutex_lock(&imx258->mutex); if (ret) return ret; - ret = imx258_write_reg(imx258, IMX258_REG_R_DIGITAL_GAIN, - IMX258_REG_VALUE_16BIT, - val); - if (ret) - return ret; - ret = imx258_write_reg(imx258, IMX258_REG_B_DIGITAL_GAIN, - IMX258_REG_VALUE_16BIT, - val); - if (ret) + if (imx258->otp) { + ret = imx258_apply_otp(imx258); + if (ret) + return ret; + } + return imx258_write_reg(imx258->client, + IMX258_REG_CTRL_MODE, + IMX258_REG_VALUE_08BIT, + IMX258_MODE_STREAMING); +} + +static int __imx258_stop_stream(struct imx258 *imx258) +{ + return imx258_write_reg(imx258->client, + IMX258_REG_CTRL_MODE, + IMX258_REG_VALUE_08BIT, + IMX258_MODE_SW_STANDBY); +} + +static int imx258_s_stream(struct v4l2_subdev *sd, int on) +{ + struct imx258 *imx258 = to_imx258(sd); + struct i2c_client *client = imx258->client; + int ret = 0; + + mutex_lock(&imx258->mutex); + on = !!on; + if (on == imx258->streaming) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = __imx258_start_stream(imx258); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + __imx258_stop_stream(imx258); + pm_runtime_put(&client->dev); + } + + imx258->streaming = on; + +unlock_and_return: + mutex_unlock(&imx258->mutex); + + return ret; +} + +static int imx258_s_power(struct v4l2_subdev *sd, int on) +{ + struct imx258 *imx258 = to_imx258(sd); + struct i2c_client *client = imx258->client; + int ret = 0; + + mutex_lock(&imx258->mutex); + + /* If the power state is not modified - no work to do. */ + if (imx258->power_on == !!on) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = imx258_write_array(imx258->client, imx258_global_regs); + if (ret) { + v4l2_err(sd, "could not set init registers\n"); + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + imx258->power_on = true; + } else { + pm_runtime_put(&client->dev); + imx258->power_on = false; + } + +unlock_and_return: + mutex_unlock(&imx258->mutex); + + return ret; +} + +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 imx258_cal_delay(u32 cycles) +{ + return DIV_ROUND_UP(cycles, IMX258_XVCLK_FREQ / 1000 / 1000); +} + +static int __imx258_power_on(struct imx258 *imx258) +{ + int ret; + u32 delay_us; + struct device *dev = &imx258->client->dev; + + if (!IS_ERR_OR_NULL(imx258->pins_default)) { + ret = pinctrl_select_state(imx258->pinctrl, + imx258->pins_default); + if (ret < 0) + dev_err(dev, "could not set pins\n"); + } + ret = clk_set_rate(imx258->xvclk, IMX258_XVCLK_FREQ); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate (24MHz)\n"); + if (clk_get_rate(imx258->xvclk) != IMX258_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); + ret = clk_prepare_enable(imx258->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); return ret; + } + if (!IS_ERR(imx258->reset_gpio)) + gpiod_set_value_cansleep(imx258->reset_gpio, 1); + + ret = regulator_bulk_enable(IMX258_NUM_SUPPLIES, imx258->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + + if (!IS_ERR(imx258->reset_gpio)) + gpiod_set_value_cansleep(imx258->reset_gpio, 0); + + usleep_range(500, 1000); + if (!IS_ERR(imx258->pwdn_gpio)) + gpiod_set_value_cansleep(imx258->pwdn_gpio, 0); + + /* 8192 cycles prior to first SCCB transaction */ + delay_us = imx258_cal_delay(8192); + usleep_range(delay_us, delay_us * 2); + return 0; + +disable_clk: + clk_disable_unprepare(imx258->xvclk); + + return ret; +} + +static void __imx258_power_off(struct imx258 *imx258) +{ + int ret; + + if (!IS_ERR(imx258->pwdn_gpio)) + gpiod_set_value_cansleep(imx258->pwdn_gpio, 1); + clk_disable_unprepare(imx258->xvclk); + if (!IS_ERR(imx258->reset_gpio)) + gpiod_set_value_cansleep(imx258->reset_gpio, 1); + if (!IS_ERR_OR_NULL(imx258->pins_sleep)) { + ret = pinctrl_select_state(imx258->pinctrl, + imx258->pins_sleep); + if (ret < 0) + dev_dbg(&imx258->client->dev, "could not set pins\n"); + } + regulator_bulk_disable(IMX258_NUM_SUPPLIES, imx258->supplies); +} + +static int imx258_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx258 *imx258 = to_imx258(sd); + + return __imx258_power_on(imx258); +} + +static int imx258_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx258 *imx258 = to_imx258(sd); + + __imx258_power_off(imx258); + + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int imx258_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct imx258 *imx258 = to_imx258(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + const struct imx258_mode *def_mode = &supported_modes[0]; + + mutex_lock(&imx258->mutex); + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = MEDIA_BUS_FMT_SRGGB10_1X10; + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&imx258->mutex); + /* No crop or compose */ + + return 0; +} +#endif + +static const struct dev_pm_ops imx258_pm_ops = { + SET_RUNTIME_PM_OPS(imx258_runtime_suspend, + imx258_runtime_resume, NULL) +}; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops imx258_internal_ops = { + .open = imx258_open, +}; +#endif + +static const struct v4l2_subdev_core_ops imx258_core_ops = { + .s_power = imx258_s_power, + .ioctl = imx258_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = imx258_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops imx258_video_ops = { + .s_stream = imx258_s_stream, + .g_frame_interval = imx258_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops imx258_pad_ops = { + .enum_mbus_code = imx258_enum_mbus_code, + .enum_frame_size = imx258_enum_frame_sizes, + .get_fmt = imx258_get_fmt, + .set_fmt = imx258_set_fmt, +}; + +static const struct v4l2_subdev_ops imx258_subdev_ops = { + .core = &imx258_core_ops, + .video = &imx258_video_ops, + .pad = &imx258_pad_ops, +}; + +static int imx258_set_gain_reg(struct imx258 *imx258, u32 a_gain) +{ + int ret = 0; + u32 gain_reg = 0; + + gain_reg = (512 - (512 * 512 / a_gain)); + if (gain_reg > 480) + gain_reg = 480; + + ret = imx258_write_reg(imx258->client, + IMX258_REG_GAIN_H, + IMX258_REG_VALUE_08BIT, + ((gain_reg & 0x100) >> 8)); + ret |= imx258_write_reg(imx258->client, + IMX258_REG_GAIN_L, + IMX258_REG_VALUE_08BIT, + (gain_reg & 0xff)); + return ret; } static int imx258_set_ctrl(struct v4l2_ctrl *ctrl) { - struct imx258 *imx258 = - container_of(ctrl->handler, struct imx258, ctrl_handler); - struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); + struct imx258 *imx258 = container_of(ctrl->handler, + struct imx258, ctrl_handler); + struct i2c_client *client = imx258->client; + s64 max; int ret = 0; - /* - * Applying V4L2 control value only happens - * when power is up for streaming - */ - if (pm_runtime_get_if_in_use(&client->dev) == 0) + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ + max = imx258->cur_mode->height + ctrl->val - 4; + __v4l2_ctrl_modify_range(imx258->exposure, + imx258->exposure->minimum, max, + imx258->exposure->step, + imx258->exposure->default_value); + break; + } + + if (pm_runtime_get(&client->dev) <= 0) return 0; switch (ctrl->id) { - case V4L2_CID_ANALOGUE_GAIN: - ret = imx258_write_reg(imx258, IMX258_REG_ANALOG_GAIN, - IMX258_REG_VALUE_16BIT, - ctrl->val); - break; case V4L2_CID_EXPOSURE: - ret = imx258_write_reg(imx258, IMX258_REG_EXPOSURE, - IMX258_REG_VALUE_16BIT, - ctrl->val); + /* 4 least significant bits of expsoure are fractional part */ + ret = imx258_write_reg(imx258->client, + IMX258_REG_EXPOSURE, + IMX258_REG_VALUE_16BIT, + ctrl->val); break; - case V4L2_CID_DIGITAL_GAIN: - ret = imx258_update_digital_gain(imx258, IMX258_REG_VALUE_16BIT, - ctrl->val); + case V4L2_CID_ANALOGUE_GAIN: + ret = imx258_set_gain_reg(imx258, ctrl->val); + break; + case V4L2_CID_VBLANK: + ret = imx258_write_reg(imx258->client, + IMX258_REG_VTS, + IMX258_REG_VALUE_16BIT, + ctrl->val + imx258->cur_mode->height); break; case V4L2_CID_TEST_PATTERN: - ret = imx258_write_reg(imx258, IMX258_REG_TEST_PATTERN, - IMX258_REG_VALUE_16BIT, - imx258_test_pattern_val[ctrl->val]); - - ret = imx258_write_reg(imx258, REG_MIRROR_FLIP_CONTROL, - IMX258_REG_VALUE_08BIT, - ctrl->val == imx258_test_pattern_val - [IMX258_TEST_PATTERN_DISABLE] ? - REG_CONFIG_MIRROR_FLIP : - REG_CONFIG_FLIP_TEST_PATTERN); + ret = imx258_enable_test_pattern(imx258, ctrl->val); break; default: - dev_info(&client->dev, - "ctrl(id:0x%x,val:0x%x) is not handled\n", - ctrl->id, ctrl->val); - ret = -EINVAL; + dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", + __func__, ctrl->id, ctrl->val); break; } @@ -804,477 +1507,266 @@ static const struct v4l2_ctrl_ops imx258_ctrl_ops = { .s_ctrl = imx258_set_ctrl, }; -static int imx258_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) +static int imx258_initialize_controls(struct imx258 *imx258) { - /* Only one bayer order(GRBG) is supported */ - if (code->index > 0) - return -EINVAL; - - code->code = MEDIA_BUS_FMT_SGRBG10_1X10; - - return 0; -} - -static int imx258_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_frame_size_enum *fse) -{ - if (fse->index >= ARRAY_SIZE(supported_modes)) - return -EINVAL; - - if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) - return -EINVAL; - - fse->min_width = supported_modes[fse->index].width; - fse->max_width = fse->min_width; - fse->min_height = supported_modes[fse->index].height; - fse->max_height = fse->min_height; - - return 0; -} - -static void imx258_update_pad_format(const struct imx258_mode *mode, - struct v4l2_subdev_format *fmt) -{ - fmt->format.width = mode->width; - fmt->format.height = mode->height; - fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10; - fmt->format.field = V4L2_FIELD_NONE; -} - -static int __imx258_get_pad_format(struct imx258 *imx258, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt) -{ - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&imx258->sd, cfg, - fmt->pad); - else - imx258_update_pad_format(imx258->cur_mode, fmt); - - return 0; -} - -static int imx258_get_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt) -{ - struct imx258 *imx258 = to_imx258(sd); - int ret; - - mutex_lock(&imx258->mutex); - ret = __imx258_get_pad_format(imx258, cfg, fmt); - mutex_unlock(&imx258->mutex); - - return ret; -} - -static int imx258_set_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt) -{ - struct imx258 *imx258 = to_imx258(sd); const struct imx258_mode *mode; - struct v4l2_mbus_framefmt *framefmt; - s32 vblank_def; - s32 vblank_min; - s64 h_blank; - s64 pixel_rate; - s64 link_freq; + struct v4l2_ctrl_handler *handler; + s64 exposure_max, vblank_def; + u32 h_blank; + int ret; - mutex_lock(&imx258->mutex); - - /* Only one raw bayer(GBRG) order is supported */ - fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10; - - mode = v4l2_find_nearest_size(supported_modes, - ARRAY_SIZE(supported_modes), width, height, - fmt->format.width, fmt->format.height); - imx258_update_pad_format(mode, fmt); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); - *framefmt = fmt->format; - } else { - imx258->cur_mode = mode; - __v4l2_ctrl_s_ctrl(imx258->link_freq, mode->link_freq_index); - - link_freq = link_freq_menu_items[mode->link_freq_index]; - pixel_rate = link_freq_to_pixel_rate(link_freq); - __v4l2_ctrl_s_ctrl_int64(imx258->pixel_rate, pixel_rate); - /* Update limits and set FPS to default */ - vblank_def = imx258->cur_mode->vts_def - - imx258->cur_mode->height; - vblank_min = imx258->cur_mode->vts_min - - imx258->cur_mode->height; - __v4l2_ctrl_modify_range( - imx258->vblank, vblank_min, - IMX258_VTS_MAX - imx258->cur_mode->height, 1, - vblank_def); - __v4l2_ctrl_s_ctrl(imx258->vblank, vblank_def); - h_blank = - link_freq_configs[mode->link_freq_index].pixels_per_line - - imx258->cur_mode->width; - __v4l2_ctrl_modify_range(imx258->hblank, h_blank, - h_blank, 1, h_blank); - } - - mutex_unlock(&imx258->mutex); - - return 0; -} - -/* Start streaming */ -static int imx258_start_streaming(struct imx258 *imx258) -{ - struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); - const struct imx258_reg_list *reg_list; - int ret, link_freq_index; - - /* Setup PLL */ - link_freq_index = imx258->cur_mode->link_freq_index; - reg_list = &link_freq_configs[link_freq_index].reg_list; - ret = imx258_write_regs(imx258, reg_list->regs, reg_list->num_of_regs); - if (ret) { - dev_err(&client->dev, "%s failed to set plls\n", __func__); - return ret; - } - - /* Apply default values of current mode */ - reg_list = &imx258->cur_mode->reg_list; - ret = imx258_write_regs(imx258, reg_list->regs, reg_list->num_of_regs); - if (ret) { - dev_err(&client->dev, "%s failed to set mode\n", __func__); - return ret; - } - - /* Set Orientation be 180 degree */ - ret = imx258_write_reg(imx258, REG_MIRROR_FLIP_CONTROL, - IMX258_REG_VALUE_08BIT, REG_CONFIG_MIRROR_FLIP); - if (ret) { - dev_err(&client->dev, "%s failed to set orientation\n", - __func__); - return ret; - } - - /* Apply customized values from user */ - ret = __v4l2_ctrl_handler_setup(imx258->sd.ctrl_handler); + handler = &imx258->ctrl_handler; + mode = imx258->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 8); if (ret) return ret; + handler->lock = &imx258->mutex; - /* set stream on register */ - return imx258_write_reg(imx258, IMX258_REG_MODE_SELECT, - IMX258_REG_VALUE_08BIT, - IMX258_MODE_STREAMING); -} + imx258->link_freq = v4l2_ctrl_new_int_menu(handler, NULL, + V4L2_CID_LINK_FREQ, 1, 0, + link_freq_menu_items); -/* Stop streaming */ -static int imx258_stop_streaming(struct imx258 *imx258) -{ - struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); - int ret; - - /* set stream off register */ - ret = imx258_write_reg(imx258, IMX258_REG_MODE_SELECT, - IMX258_REG_VALUE_08BIT, IMX258_MODE_STANDBY); - if (ret) - dev_err(&client->dev, "%s failed to set stream\n", __func__); - - /* - * Return success even if it was an error, as there is nothing the - * caller can do about it. - */ - return 0; -} - -static int imx258_set_stream(struct v4l2_subdev *sd, int enable) -{ - struct imx258 *imx258 = to_imx258(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret = 0; - - mutex_lock(&imx258->mutex); - if (imx258->streaming == enable) { - mutex_unlock(&imx258->mutex); - return 0; - } - - if (enable) { - ret = pm_runtime_get_sync(&client->dev); - if (ret < 0) { - pm_runtime_put_noidle(&client->dev); - goto err_unlock; - } - - /* - * Apply default & customized values - * and then start streaming. - */ - ret = imx258_start_streaming(imx258); - if (ret) - goto err_rpm_put; - } else { - imx258_stop_streaming(imx258); - pm_runtime_put(&client->dev); - } - - imx258->streaming = enable; - mutex_unlock(&imx258->mutex); - - return ret; - -err_rpm_put: - pm_runtime_put(&client->dev); -err_unlock: - mutex_unlock(&imx258->mutex); - - return ret; -} - -static int __maybe_unused imx258_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct imx258 *imx258 = to_imx258(sd); - - if (imx258->streaming) - imx258_stop_streaming(imx258); - - return 0; -} - -static int __maybe_unused imx258_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct imx258 *imx258 = to_imx258(sd); - int ret; - - if (imx258->streaming) { - ret = imx258_start_streaming(imx258); - if (ret) - goto error; - } - - return 0; - -error: - imx258_stop_streaming(imx258); - imx258->streaming = 0; - return ret; -} - -/* Verify chip ID */ -static int imx258_identify_module(struct imx258 *imx258) -{ - struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); - int ret; - u32 val; - - ret = imx258_read_reg(imx258, IMX258_REG_CHIP_ID, - IMX258_REG_VALUE_16BIT, &val); - if (ret) { - dev_err(&client->dev, "failed to read chip id %x\n", - IMX258_CHIP_ID); - return ret; - } - - if (val != IMX258_CHIP_ID) { - dev_err(&client->dev, "chip id mismatch: %x!=%x\n", - IMX258_CHIP_ID, val); - return -EIO; - } - - return 0; -} - -static const struct v4l2_subdev_video_ops imx258_video_ops = { - .s_stream = imx258_set_stream, -}; - -static const struct v4l2_subdev_pad_ops imx258_pad_ops = { - .enum_mbus_code = imx258_enum_mbus_code, - .get_fmt = imx258_get_pad_format, - .set_fmt = imx258_set_pad_format, - .enum_frame_size = imx258_enum_frame_size, -}; - -static const struct v4l2_subdev_ops imx258_subdev_ops = { - .video = &imx258_video_ops, - .pad = &imx258_pad_ops, -}; - -static const struct v4l2_subdev_internal_ops imx258_internal_ops = { - .open = imx258_open, -}; - -/* Initialize control handlers */ -static int imx258_init_controls(struct imx258 *imx258) -{ - struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); - struct v4l2_ctrl_handler *ctrl_hdlr; - s64 vblank_def; - s64 vblank_min; - s64 pixel_rate_min; - s64 pixel_rate_max; - int ret; - - ctrl_hdlr = &imx258->ctrl_handler; - ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8); - if (ret) - return ret; - - mutex_init(&imx258->mutex); - ctrl_hdlr->lock = &imx258->mutex; - imx258->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, - &imx258_ctrl_ops, - V4L2_CID_LINK_FREQ, - ARRAY_SIZE(link_freq_menu_items) - 1, - 0, - link_freq_menu_items); - - if (imx258->link_freq) - imx258->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; - - pixel_rate_max = link_freq_to_pixel_rate(link_freq_menu_items[0]); - pixel_rate_min = link_freq_to_pixel_rate(link_freq_menu_items[1]); - /* By default, PIXEL_RATE is read only */ - imx258->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops, - V4L2_CID_PIXEL_RATE, - pixel_rate_min, pixel_rate_max, - 1, pixel_rate_max); - - - vblank_def = imx258->cur_mode->vts_def - imx258->cur_mode->height; - vblank_min = imx258->cur_mode->vts_min - imx258->cur_mode->height; - imx258->vblank = v4l2_ctrl_new_std( - ctrl_hdlr, &imx258_ctrl_ops, V4L2_CID_VBLANK, - vblank_min, - IMX258_VTS_MAX - imx258->cur_mode->height, 1, - vblank_def); - - if (imx258->vblank) - imx258->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; - - imx258->hblank = v4l2_ctrl_new_std( - ctrl_hdlr, &imx258_ctrl_ops, V4L2_CID_HBLANK, - IMX258_PPL_DEFAULT - imx258->cur_mode->width, - IMX258_PPL_DEFAULT - imx258->cur_mode->width, - 1, - IMX258_PPL_DEFAULT - imx258->cur_mode->width); + imx258->pixel_rate = v4l2_ctrl_new_std(handler, NULL, + V4L2_CID_PIXEL_RATE, 0, IMX258_PIXEL_RATE_FULL_SIZE, + 1, IMX258_PIXEL_RATE_FULL_SIZE); + h_blank = mode->hts_def - mode->width; + imx258->hblank = v4l2_ctrl_new_std(handler, NULL, + V4L2_CID_HBLANK, h_blank, h_blank, 1, h_blank); if (imx258->hblank) imx258->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; - imx258->exposure = v4l2_ctrl_new_std( - ctrl_hdlr, &imx258_ctrl_ops, - V4L2_CID_EXPOSURE, IMX258_EXPOSURE_MIN, - IMX258_EXPOSURE_MAX, IMX258_EXPOSURE_STEP, - IMX258_EXPOSURE_DEFAULT); + vblank_def = mode->vts_def - mode->height; + imx258->vblank = v4l2_ctrl_new_std(handler, &imx258_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + IMX258_VTS_MAX - mode->height, + 1, vblank_def); - v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, - IMX258_ANA_GAIN_MIN, IMX258_ANA_GAIN_MAX, - IMX258_ANA_GAIN_STEP, IMX258_ANA_GAIN_DEFAULT); + exposure_max = mode->vts_def - 4; + imx258->exposure = v4l2_ctrl_new_std(handler, &imx258_ctrl_ops, + V4L2_CID_EXPOSURE, IMX258_EXPOSURE_MIN, + exposure_max, IMX258_EXPOSURE_STEP, + mode->exp_def); - v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops, V4L2_CID_DIGITAL_GAIN, - IMX258_DGTL_GAIN_MIN, IMX258_DGTL_GAIN_MAX, - IMX258_DGTL_GAIN_STEP, - IMX258_DGTL_GAIN_DEFAULT); + imx258->anal_gain = v4l2_ctrl_new_std(handler, &imx258_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, IMX258_GAIN_MIN, + IMX258_GAIN_MAX, IMX258_GAIN_STEP, + IMX258_GAIN_DEFAULT); - v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx258_ctrl_ops, - V4L2_CID_TEST_PATTERN, - ARRAY_SIZE(imx258_test_pattern_menu) - 1, - 0, 0, imx258_test_pattern_menu); + imx258->test_pattern = v4l2_ctrl_new_std_menu_items(handler, + &imx258_ctrl_ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(imx258_test_pattern_menu) - 1, + 0, 0, imx258_test_pattern_menu); - if (ctrl_hdlr->error) { - ret = ctrl_hdlr->error; - dev_err(&client->dev, "%s control init failed (%d)\n", - __func__, ret); - goto error; + if (handler->error) { + ret = handler->error; + dev_err(&imx258->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; } - imx258->sd.ctrl_handler = ctrl_hdlr; + imx258->subdev.ctrl_handler = handler; return 0; -error: - v4l2_ctrl_handler_free(ctrl_hdlr); - mutex_destroy(&imx258->mutex); +err_free_handler: + v4l2_ctrl_handler_free(handler); return ret; } -static void imx258_free_controls(struct imx258 *imx258) +static int imx258_check_sensor_id(struct imx258 *imx258, + struct i2c_client *client) { - v4l2_ctrl_handler_free(imx258->sd.ctrl_handler); - mutex_destroy(&imx258->mutex); + struct device *dev = &imx258->client->dev; + int ret = 0; + u32 id = 0; + + ret = imx258_read_reg(client, IMX258_REG_CHIP_ID, + IMX258_REG_VALUE_16BIT, &id); + if (id != CHIP_ID) { + dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret); + return -ENODEV; + } + + return 0; } -static int imx258_probe(struct i2c_client *client) +static int imx258_configure_regulators(struct imx258 *imx258) { + unsigned int i; + + for (i = 0; i < IMX258_NUM_SUPPLIES; i++) + imx258->supplies[i].supply = imx258_supply_names[i]; + + return devm_regulator_bulk_get(&imx258->client->dev, + IMX258_NUM_SUPPLIES, + imx258->supplies); +} + +static int imx258_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; struct imx258 *imx258; + struct v4l2_subdev *sd; + char facing[2]; + struct device_node *eeprom_ctrl_node; + struct i2c_client *eeprom_ctrl_client; + struct v4l2_subdev *eeprom_ctrl; + struct imx258_otp_info *otp_ptr; int ret; - u32 val = 0; - device_property_read_u32(&client->dev, "clock-frequency", &val); - if (val != 19200000) - return -EINVAL; + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); - /* - * Check that the device is mounted upside down. The driver only - * supports a single pixel order right now. - */ - ret = device_property_read_u32(&client->dev, "rotation", &val); - if (ret || val != 180) - return -EINVAL; - - imx258 = devm_kzalloc(&client->dev, sizeof(*imx258), GFP_KERNEL); + imx258 = devm_kzalloc(dev, sizeof(*imx258), GFP_KERNEL); if (!imx258) return -ENOMEM; - /* Initialize subdev */ - v4l2_i2c_subdev_init(&imx258->sd, client, &imx258_subdev_ops); + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &imx258->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &imx258->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &imx258->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &imx258->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } - /* Check module identity */ - ret = imx258_identify_module(imx258); - if (ret) - return ret; - - /* Set default mode to max resolution */ + imx258->client = client; imx258->cur_mode = &supported_modes[0]; - ret = imx258_init_controls(imx258); - if (ret) + imx258->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(imx258->xvclk)) { + dev_err(dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + imx258->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(imx258->reset_gpio)) + dev_warn(dev, "Failed to get reset-gpios\n"); + + imx258->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(imx258->pwdn_gpio)) + dev_warn(dev, "Failed to get pwdn-gpios\n"); + + ret = imx258_configure_regulators(imx258); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); return ret; + } - /* Initialize subdev */ - imx258->sd.internal_ops = &imx258_internal_ops; - imx258->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - imx258->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + imx258->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR(imx258->pinctrl)) { + imx258->pins_default = + pinctrl_lookup_state(imx258->pinctrl, + OF_CAMERA_PINCTRL_STATE_DEFAULT); + if (IS_ERR(imx258->pins_default)) + dev_err(dev, "could not get default pinstate\n"); - /* Initialize source pad */ - imx258->pad.flags = MEDIA_PAD_FL_SOURCE; + imx258->pins_sleep = + pinctrl_lookup_state(imx258->pinctrl, + OF_CAMERA_PINCTRL_STATE_SLEEP); + if (IS_ERR(imx258->pins_sleep)) + dev_err(dev, "could not get sleep pinstate\n"); + } - ret = media_entity_pads_init(&imx258->sd.entity, 1, &imx258->pad); + mutex_init(&imx258->mutex); + + sd = &imx258->subdev; + v4l2_i2c_subdev_init(sd, client, &imx258_subdev_ops); + ret = imx258_initialize_controls(imx258); if (ret) - goto error_handler_free; + goto err_destroy_mutex; - ret = v4l2_async_register_subdev_sensor_common(&imx258->sd); + ret = __imx258_power_on(imx258); + if (ret) + goto err_free_handler; + + ret = imx258_check_sensor_id(imx258, client); + if (ret) + goto err_power_off; + + eeprom_ctrl_node = of_parse_phandle(node, "eeprom-ctrl", 0); + if (eeprom_ctrl_node) { + eeprom_ctrl_client = + of_find_i2c_device_by_node(eeprom_ctrl_node); + of_node_put(eeprom_ctrl_node); + if (IS_ERR_OR_NULL(eeprom_ctrl_client)) { + dev_err(dev, "can not get node\n"); + goto continue_probe; + } + eeprom_ctrl = i2c_get_clientdata(eeprom_ctrl_client); + if (IS_ERR_OR_NULL(eeprom_ctrl)) { + dev_err(dev, "can not get eeprom i2c client\n"); + } else { + otp_ptr = devm_kzalloc(dev, sizeof(*otp_ptr), GFP_KERNEL); + if (!otp_ptr) + return -ENOMEM; + ret = v4l2_subdev_call(eeprom_ctrl, + core, ioctl, 0, otp_ptr); + if (!ret) { + imx258->otp = otp_ptr; + } else { + imx258->otp = NULL; + devm_kfree(dev, otp_ptr); + } + } + } + +continue_probe: + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &imx258_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + imx258->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &imx258->pad); if (ret < 0) - goto error_media_entity; + goto err_power_off; +#endif - pm_runtime_set_active(&client->dev); - pm_runtime_enable(&client->dev); - pm_runtime_idle(&client->dev); + memset(facing, 0, sizeof(facing)); + if (strcmp(imx258->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + imx258->module_index, facing, + IMX258_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor_common(sd); + if (ret) { + dev_err(dev, "v4l2 async register subdev failed\n"); + goto err_clean_entity; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); return 0; -error_media_entity: - media_entity_cleanup(&imx258->sd.entity); - -error_handler_free: - imx258_free_controls(imx258); +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif +err_power_off: + __imx258_power_off(imx258); +err_free_handler: + v4l2_ctrl_handler_free(&imx258->ctrl_handler); +err_destroy_mutex: + mutex_destroy(&imx258->mutex); return ret; } @@ -1285,42 +1777,56 @@ static int imx258_remove(struct i2c_client *client) struct imx258 *imx258 = to_imx258(sd); v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) media_entity_cleanup(&sd->entity); - imx258_free_controls(imx258); +#endif + v4l2_ctrl_handler_free(&imx258->ctrl_handler); + mutex_destroy(&imx258->mutex); pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __imx258_power_off(imx258); pm_runtime_set_suspended(&client->dev); return 0; } -static const struct dev_pm_ops imx258_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(imx258_suspend, imx258_resume) +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id imx258_of_match[] = { + { .compatible = "sony,imx258" }, + {}, }; - -#ifdef CONFIG_ACPI -static const struct acpi_device_id imx258_acpi_ids[] = { - { "SONY258A" }, - { /* sentinel */ } -}; - -MODULE_DEVICE_TABLE(acpi, imx258_acpi_ids); +MODULE_DEVICE_TABLE(of, imx258_of_match); #endif +static const struct i2c_device_id imx258_match_id[] = { + { "sony,imx258", 0 }, + { }, +}; + static struct i2c_driver imx258_i2c_driver = { .driver = { - .name = "imx258", + .name = IMX258_NAME, .pm = &imx258_pm_ops, - .acpi_match_table = ACPI_PTR(imx258_acpi_ids), + .of_match_table = of_match_ptr(imx258_of_match), }, - .probe_new = imx258_probe, - .remove = imx258_remove, + .probe = &imx258_probe, + .remove = &imx258_remove, + .id_table = imx258_match_id, }; -module_i2c_driver(imx258_i2c_driver); +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&imx258_i2c_driver); +} -MODULE_AUTHOR("Yeh, Andy "); -MODULE_AUTHOR("Chiang, Alan "); -MODULE_AUTHOR("Chen, Jason "); -MODULE_DESCRIPTION("Sony IMX258 sensor driver"); +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&imx258_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_DESCRIPTION("Sony imx258 sensor driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/imx258_eeprom.c b/drivers/media/i2c/imx258_eeprom.c new file mode 100644 index 000000000000..94fd4094d329 --- /dev/null +++ b/drivers/media/i2c/imx258_eeprom.c @@ -0,0 +1,377 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd. + +#include +#include +#include +#include +#include +#include +#include +#include "imx258_eeprom_head.h" + +#define DEVICE_NAME "imx258_eeprom" + +static inline struct imx258_eeprom_device + *sd_to_imx258_eeprom(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct imx258_eeprom_device, sd); +} + +/* Read registers up to 4 at a time */ +static int imx258_read_reg_otp(struct i2c_client *client, u16 reg, + unsigned int len, u32 *val) +{ + struct i2c_msg msgs[2]; + u8 *data_be_p; + __be32 data_be = 0; + __be16 reg_addr_be = cpu_to_be16(reg); + int ret; + + if (len > 4 || !len) + return -EINVAL; + + data_be_p = (u8 *)&data_be; + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 2; + msgs[0].buf = (u8 *)®_addr_be; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_be_p[4 - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = be32_to_cpu(data_be); + + return 0; +} + +static u8 get_vendor_flag(struct i2c_client *client) +{ + u8 vendor_flag = 0; + + if (client->addr == SLAVE_ADDRESS_GZ) + vendor_flag |= 0x80; + return vendor_flag; +} + +static int imx258_otp_read_gz(struct imx258_eeprom_device *imx258_eeprom_dev) +{ + struct i2c_client *client = imx258_eeprom_dev->client; + int otp_flag, i; + struct imx258_otp_info *otp_ptr; + struct device *dev = &imx258_eeprom_dev->client->dev; + int ret = 0; + u32 r_value, gr_value, gb_value, b_value; + u32 temp = 0; + u32 checksum = 0; + + otp_ptr = kzalloc(sizeof(*otp_ptr), GFP_KERNEL); + if (!otp_ptr) + return -ENOMEM; + + otp_flag = 0; + /* OTP base information*/ + ret = imx258_read_reg_otp(client, GZ_INFO_FLAG_REG, + 1, &otp_flag); + if (otp_flag == 0x01) { + otp_ptr->flag = 0x80; /* valid INFO in OTP */ + ret |= imx258_read_reg_otp(client, GZ_ID_REG, + 1, &otp_ptr->module_id); + ret |= imx258_read_reg_otp(client, GZ_LENS_ID_REG, + 1, &otp_ptr->lens_id); + ret |= imx258_read_reg_otp(client, GZ_PRODUCT_YEAR_REG, + 1, &otp_ptr->year); + ret |= imx258_read_reg_otp(client, GZ_PRODUCT_MONTH_REG, + 1, &otp_ptr->month); + ret |= imx258_read_reg_otp(client, GZ_PRODUCT_DAY_REG, + 1, &otp_ptr->day); + dev_dbg(dev, "fac info: module(0x%x) lens(0x%x) time(%d_%d_%d)!\n", + otp_ptr->module_id, + otp_ptr->lens_id, + otp_ptr->year, + otp_ptr->month, + otp_ptr->day); + if (ret) + goto err; + } + + /* OTP WB calibration data */ + ret = imx258_read_reg_otp(client, GZ_AWB_FLAG_REG, + 1, &otp_flag); + if (otp_flag == 0x01) { + otp_ptr->flag |= 0x40; /* valid AWB in OTP */ + ret |= imx258_read_reg_otp(client, GZ_CUR_R_REG, + 1, &r_value); + checksum += r_value; + ret |= imx258_read_reg_otp(client, GZ_CUR_GR_REG, + 1, &gr_value); + checksum += gr_value; + ret |= imx258_read_reg_otp(client, GZ_CUR_GB_REG, + 1, &gb_value); + checksum += gb_value; + ret |= imx258_read_reg_otp(client, GZ_CUR_B_REG, + 1, &b_value); + checksum += b_value; + otp_ptr->rg_ratio = + r_value * 1024 / ((gr_value + gb_value) / 2); + otp_ptr->bg_ratio = + b_value * 1024 / ((gr_value + gb_value) / 2); + ret |= imx258_read_reg_otp(client, GZ_GOLDEN_R_REG, + 1, &r_value); + checksum += r_value; + ret |= imx258_read_reg_otp(client, GZ_GOLDEN_GR_REG, + 1, &gr_value); + checksum += gr_value; + ret |= imx258_read_reg_otp(client, GZ_GOLDEN_GB_REG, + 1, &gb_value); + checksum += gb_value; + ret |= imx258_read_reg_otp(client, GZ_GOLDEN_B_REG, + 1, &b_value); + checksum += b_value; + otp_ptr->rg_golden = + r_value * 1024 / ((gr_value + gb_value) / 2); + otp_ptr->bg_golden = + b_value * 1024 / ((gr_value + gb_value) / 2); + ret |= imx258_read_reg_otp(client, GZ_AWB_CHECKSUM_REG, + 1, &temp); + if (ret != 0 || (checksum % 0xff) != temp) { + dev_err(dev, "otp awb info: check sum (%d,%d),ret = %d !\n", + checksum, + temp, + ret); + goto err; + } + dev_dbg(dev, "awb cur:(rg 0x%x, bg 0x%x,)\n", + otp_ptr->rg_ratio, otp_ptr->bg_ratio); + dev_dbg(dev, "awb gol:(rg 0x%x, bg 0x%x)\n", + otp_ptr->rg_golden, otp_ptr->bg_golden); + } + + checksum = 0; + /* OTP LSC calibration data */ + ret = imx258_read_reg_otp(client, GZ_LSC_FLAG_REG, + 1, &otp_flag); + if (otp_flag == 0x01) { + otp_ptr->flag |= 0x10; /* valid LSC in OTP */ + for (i = 0; i < 504; i++) { + ret |= imx258_read_reg_otp(client, + GZ_LSC_DATA_START_REG + i, + 1, &temp); + otp_ptr->lenc[i] = temp; + checksum += temp; + dev_dbg(dev, + "otp read lsc addr = 0x%04x, lenc[%d] = %d\n", + GZ_LSC_DATA_START_REG + i, i, temp); + } + ret |= imx258_read_reg_otp(client, GZ_LSC_CHECKSUM_REG, + 1, &temp); + if (ret != 0 || (checksum % 0xff) != temp) { + dev_err(dev, + "otp lsc info: check sum (%d,%d),ret = %d !\n", + checksum, temp, ret); + goto err; + } + } + + checksum = 0; + /* OTP VCM calibration data */ + ret = imx258_read_reg_otp(client, GZ_VCM_FLAG_REG, + 1, &otp_flag); + if (otp_flag == 0x01) { + otp_ptr->flag |= 0x20; /* valid VCM in OTP */ + ret |= imx258_read_reg_otp(client, GZ_VCM_DIR_REG, + 1, &otp_ptr->vcm_dir); + checksum += otp_ptr->vcm_dir; + ret |= imx258_read_reg_otp(client, GZ_VCM_START_REG, + 1, &temp); + checksum += temp; + ret |= imx258_read_reg_otp(client, GZ_VCM_START_REG + 1, + 1, &otp_ptr->vcm_start); + checksum += otp_ptr->vcm_start; + otp_ptr->vcm_start |= (temp << 8); + ret |= imx258_read_reg_otp(client, GZ_VCM_END_REG, + 1, &temp); + checksum += temp; + ret |= imx258_read_reg_otp(client, GZ_VCM_END_REG + 1, + 1, &otp_ptr->vcm_end); + checksum += otp_ptr->vcm_end; + otp_ptr->vcm_end |= (temp << 8); + ret |= imx258_read_reg_otp(client, GZ_VCM_CHECKSUM_REG, + 1, &temp); + if (ret != 0 || (checksum % 0xff) != temp) { + dev_err(dev, + "otp VCM info: check sum (%d,%d),ret = %d !\n", + checksum, temp, ret); + goto err; + } + dev_dbg(dev, "vcm_info: 0x%x, 0x%x, 0x%x!\n", + otp_ptr->vcm_start, + otp_ptr->vcm_end, + otp_ptr->vcm_dir); + } + + checksum = 0; + /* OTP SPC calibration data */ + ret = imx258_read_reg_otp(client, GZ_SPC_FLAG_REG, + 1, &otp_flag); + if (otp_flag == 0x01) { + otp_ptr->flag |= 0x08; /* valid LSC in OTP */ + for (i = 0; i < 126; i++) { + ret |= imx258_read_reg_otp(client, + GZ_SPC_DATA_START_REG + i, + 1, &temp); + otp_ptr->spc[i] = (uint8_t)temp; + checksum += temp; + dev_dbg(dev, + "otp read spc addr = 0x%04x, spc[%d] = %d\n", + GZ_SPC_DATA_START_REG + i, i, temp); + } + ret |= imx258_read_reg_otp(client, GZ_SPC_CHECKSUM_REG, + 1, &temp); + if (ret != 0 || (checksum % 0xff) != temp) { + dev_err(dev, + "otp spc info: check sum (%d,%d),ret = %d !\n", + checksum, temp, ret); + goto err; + } + } + + if (otp_ptr->flag) { + imx258_eeprom_dev->otp = otp_ptr; + } else { + imx258_eeprom_dev->otp = NULL; + kfree(otp_ptr); + } + + return 0; +err: + imx258_eeprom_dev->otp = NULL; + kfree(otp_ptr); + return -EINVAL; +} + +static int imx258_otp_read(struct imx258_eeprom_device *imx258_eeprom_dev) +{ + u8 vendor_flag = 0; + struct i2c_client *client = imx258_eeprom_dev->client; + + vendor_flag = get_vendor_flag(client); + if (vendor_flag == 0x80) + imx258_otp_read_gz(imx258_eeprom_dev); + return 0; +} + +static long imx258_eeprom_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, void *arg) +{ + struct imx258_eeprom_device *imx258_eeprom_dev = + sd_to_imx258_eeprom(sd); + imx258_otp_read(imx258_eeprom_dev); + if (arg && imx258_eeprom_dev->otp) + memcpy(arg, imx258_eeprom_dev->otp, + sizeof(struct imx258_otp_info)); + return 0; +} + +static const struct v4l2_subdev_core_ops imx258_eeprom_core_ops = { + .ioctl = imx258_eeprom_ioctl, +}; + +static const struct v4l2_subdev_ops imx258_eeprom_ops = { + .core = &imx258_eeprom_core_ops, +}; + +static void imx258_eeprom_subdev_cleanup(struct imx258_eeprom_device *dev) +{ + v4l2_device_unregister_subdev(&dev->sd); + media_entity_cleanup(&dev->sd.entity); +} + +static int imx258_eeprom_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct imx258_eeprom_device *imx258_eeprom_dev; + + dev_info(&client->dev, "probing...\n"); + imx258_eeprom_dev = devm_kzalloc(&client->dev, + sizeof(*imx258_eeprom_dev), + GFP_KERNEL); + + if (imx258_eeprom_dev == NULL) { + dev_err(&client->dev, "Probe failed\n"); + return -ENOMEM; + } + v4l2_i2c_subdev_init(&imx258_eeprom_dev->sd, + client, &imx258_eeprom_ops); + imx258_eeprom_dev->client = client; + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + pm_runtime_idle(&client->dev); + + dev_info(&client->dev, "probing successful\n"); + + return 0; +} + +static int imx258_eeprom_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx258_eeprom_device *imx258_eeprom_dev = + sd_to_imx258_eeprom(sd); + kfree(imx258_eeprom_dev->otp); + pm_runtime_disable(&client->dev); + imx258_eeprom_subdev_cleanup(imx258_eeprom_dev); + + return 0; +} + +static int __maybe_unused imx258_eeprom_suspend(struct device *dev) +{ + return 0; +} + +static int __maybe_unused imx258_eeprom_resume(struct device *dev) +{ + return 0; +} + +static const struct i2c_device_id imx258_eeprom_id_table[] = { + { DEVICE_NAME, 0 }, + { { 0 } } +}; +MODULE_DEVICE_TABLE(i2c, imx258_eeprom_id_table); + +static const struct of_device_id imx258_eeprom_of_table[] = { + { .compatible = "sony,imx258_eeprom" }, + { { 0 } } +}; +MODULE_DEVICE_TABLE(of, imx258_eeprom_of_table); + +static const struct dev_pm_ops imx258_eeprom_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(imx258_eeprom_suspend, imx258_eeprom_resume) + SET_RUNTIME_PM_OPS(imx258_eeprom_suspend, imx258_eeprom_resume, NULL) +}; + +static struct i2c_driver imx258_eeprom_i2c_driver = { + .driver = { + .name = DEVICE_NAME, + .pm = &imx258_eeprom_pm_ops, + .of_match_table = imx258_eeprom_of_table, + }, + .probe = &imx258_eeprom_probe, + .remove = &imx258_eeprom_remove, + .id_table = imx258_eeprom_id_table, +}; + +module_i2c_driver(imx258_eeprom_i2c_driver); + +MODULE_DESCRIPTION("IMX258 OTP driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/imx258_eeprom_head.h b/drivers/media/i2c/imx258_eeprom_head.h new file mode 100644 index 000000000000..9678e20df2c2 --- /dev/null +++ b/drivers/media/i2c/imx258_eeprom_head.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd. */ + +#ifndef IMX258_EEPROM_HEAD_H +#define IMX258_EEPROM_HEAD_H + +#define SLAVE_ADDRESS_GZ 0x50 +#define GZ_INFO_FLAG_REG 0X0000 +#define GZ_ID_REG 0X0005 +#define GZ_LENS_ID_REG 0X0006 +#define GZ_PRODUCT_YEAR_REG 0X000A +#define GZ_PRODUCT_MONTH_REG 0X000B +#define GZ_PRODUCT_DAY_REG 0X000C +#define GZ_AWB_FLAG_REG 0x001c +#define GZ_CUR_R_REG 0x001d +#define GZ_CUR_GR_REG 0x001e +#define GZ_CUR_GB_REG 0x001f +#define GZ_CUR_B_REG 0x0020 +#define GZ_GOLDEN_R_REG 0x0021 +#define GZ_GOLDEN_GR_REG 0x0022 +#define GZ_GOLDEN_GB_REG 0x0023 +#define GZ_GOLDEN_B_REG 0x0024 +#define GZ_AWB_CHECKSUM_REG 0x0025 +#define GZ_LSC_FLAG_REG 0X003A +#define GZ_LSC_DATA_START_REG 0x003B +#define GZ_LSC_CHECKSUM_REG 0x0233 +#define GZ_VCM_FLAG_REG 0X0788 +#define GZ_VCM_DIR_REG 0X0789 +#define GZ_VCM_START_REG 0X078C +#define GZ_VCM_END_REG 0X078A +#define GZ_VCM_CHECKSUM_REG 0x0790 +#define GZ_SPC_FLAG_REG 0X0CE1 +#define GZ_SPC_DATA_START_REG 0x0CE2 +#define GZ_SPC_CHECKSUM_REG 0x0d60 + +struct imx258_otp_info { + u32 flag; //bit[7]: info bit[6]:wb bit[5]:vcm bit[4]:lenc bit[3]:spc + u32 module_id; + u32 lens_id; + u32 year; + u32 month; + u32 day; + u32 rg_ratio; + u32 bg_ratio; + u32 rg_golden; + u32 bg_golden; + int vcm_start; + int vcm_end; + int vcm_dir; + u8 lenc[504]; + u8 spc[126]; +}; + +/* imx258_eeprom device structure */ +struct imx258_eeprom_device { + struct v4l2_subdev sd; + struct i2c_client *client; + struct imx258_otp_info *otp; +}; + +#endif /* IMX258_EEPROM_HEAD_H */ + diff --git a/drivers/media/i2c/imx317.c b/drivers/media/i2c/imx317.c new file mode 100644 index 000000000000..320293d9c575 --- /dev/null +++ b/drivers/media/i2c/imx317.c @@ -0,0 +1,1364 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * imx317 driver + * + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd. + * + * V0.0X01.0X01 add poweron function. + * V0.0X01.0X02 fix mclk issue when probe multiple camera. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x02) + +#ifndef V4L2_CID_DIGITAL_GAIN +#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN +#endif + +#define IMX317_LINK_FREQ_360MHZ 360000000 +/* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ +#define IMX317_PIXEL_RATE (IMX317_LINK_FREQ_360MHZ * 2 * 2 / 10) +#define IMX317_XVCLK_FREQ 24000000 + +#define CHIP_ID 0x03 +#define IMX317_REG_CHIP_ID 0x3004 + +#define IMX317_REG_CTRL_MODE 0x3000 +#define IMX317_MODE_SW_STANDBY 0x12 +#define IMX317_MODE_STREAMING 0x00 + +#define IMX317_REG_EXPOSURE_H 0x300D +#define IMX317_REG_EXPOSURE_L 0x300C +#define IMX317_EXPOSURE_MIN 12 +#define IMX317_EXPOSURE_STEP 1 +#define IMX317_VTS_MAX 0x7fff + +#define IMX317_REG_GAIN_H 0x300B +#define IMX317_REG_GAIN_L 0x300A +#define IMX317_GAIN_H_MASK 0x07 +#define IMX317_GAIN_H_SHIFT 8 +#define IMX317_GAIN_L_MASK 0xFF +#define IMX317_GAIN_MIN 0x0800 +#define IMX317_GAIN_MAX 0xFFFF +#define IMX317_GAIN_STEP 1 +#define IMX317_GAIN_DEFAULT 0x0BDF + +#define IMX317_REG_VTS_H 0x30FA +#define IMX317_REG_VTS_M 0x30F9 +#define IMX317_REG_VTS_L 0x30F8 + +#define REG_NULL 0xFFFF +#define REG_DELAY 0xFFFE + +#define IMX317_REG_VALUE_08BIT 1 +#define IMX317_REG_VALUE_16BIT 2 +#define IMX317_REG_VALUE_24BIT 3 + +#define IMX317_LANES 2 +#define IMX317_BITS_PER_SAMPLE 10 + +#define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default" +#define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep" + +#define IMX317_NAME "imx317" + +static const char * const imx317_supply_names[] = { + "avdd", /* Analog power */ + "dovdd", /* Digital I/O power */ + "dvdd", /* Digital core power */ +}; + +#define IMX317_NUM_SUPPLIES ARRAY_SIZE(imx317_supply_names) + +struct regval { + u16 addr; + u8 val; +}; + +struct imx317_mode { + u32 width; + u32 height; + u32 max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + const struct regval *reg_list; +}; + +struct imx317 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; + struct regulator_bulk_data supplies[IMX317_NUM_SUPPLIES]; + + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_sleep; + + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *anal_gain; + struct v4l2_ctrl *digi_gain; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *test_pattern; + struct mutex mutex; + bool streaming; + bool power_on; + const struct imx317_mode *cur_mode; + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; +}; + +#define to_imx317(sd) container_of(sd, struct imx317, subdev) + +/* + * Xclk 24Mhz + */ +static const struct regval imx317_global_regs[] = { + {REG_NULL, 0x00}, +}; + +/* + * Xclk 24Mhz + * max_framerate 30fps + * mipi_datarate per lane 720Mbps + * 4 lane + */ +static const struct regval imx317_1932x1094_regs[] = { + {0x3000, 0x1f}, + {0x303E, 0x02}, + {0x3120, 0xF0}, + {0x3121, 0x00}, + {0x3122, 0x02}, + {0x3123, 0x01}, + {0x3129, 0x9C}, + {0x312A, 0x02}, + {0x312D, 0x02}, + {0x3AC4, 0x01}, + {0x310B, 0x00}, + {0x30EE, 0x01}, + {0x3304, 0x32}, + {0x3306, 0x32}, + {0x3590, 0x32}, + {0x3686, 0x32}, + {0x3045, 0x32}, + {0x301A, 0x00}, + {0x304C, 0x00}, + {0x304D, 0x03}, + {0x331C, 0x1A}, + {0x3502, 0x02}, + {0x3529, 0x0E}, + {0x352A, 0x0E}, + {0x352B, 0x0E}, + {0x3538, 0x0E}, + {0x3539, 0x0E}, + {0x3553, 0x00}, + {0x357D, 0x05}, + {0x357F, 0x05}, + {0x3581, 0x04}, + {0x3583, 0x76}, + {0x3587, 0x01}, + {0x35BB, 0x0E}, + {0x35BC, 0x0E}, + {0x35BD, 0x0E}, + {0x35BE, 0x0E}, + {0x35BF, 0x0E}, + {0x366E, 0x00}, + {0x366F, 0x00}, + {0x3670, 0x00}, + {0x3671, 0x00}, + {0x3004, 0x02}, + {0x3005, 0x21}, + {0x3006, 0x00}, + {0x3007, 0x11}, + {0x300E, 0x00}, + {0x300F, 0x00}, + {0x3037, 0x00}, + {0x3038, 0x00}, + {0x3039, 0x00}, + {0x303A, 0x00}, + {0x303B, 0x00}, + {0x30DD, 0x00}, + {0x30DE, 0x00}, + {0x30DF, 0x00}, + {0x30E0, 0x00}, + {0x30E1, 0x00}, + {0x30E2, 0x02}, + {0x30F6, 0x1e}, + {0x30F7, 0x01}, + {0x30F8, 0xD0}, + {0x30F9, 0x20}, + {0x30FA, 0x00}, + {0x3130, 0x4e}, + {0x3131, 0x04}, + {0x3132, 0x46}, + {0x3133, 0x04}, + {0x3a54, 0x8c}, + {0x3a55, 0x07}, + {0x3342, 0x0a}, + {0x3343, 0x00}, + {0x3344, 0x1a}, + {0x3345, 0x00}, + {0x3528, 0x0E}, + {0x3554, 0x00}, + {0x3555, 0x01}, + {0x3556, 0x01}, + {0x3557, 0x01}, + {0x3558, 0x01}, + {0x3559, 0x00}, + {0x355A, 0x00}, + {0x35BA, 0x0E}, + {0x366A, 0x1B}, + {0x366B, 0x1A}, + {0x366C, 0x19}, + {0x366D, 0x17}, + {0x33A6, 0x01}, + {0x306B, 0x05}, + {0x3A41, 0x08}, + {0x3134, 0x77}, + {0x3135, 0x00}, + {0x3136, 0x67}, + {0x3137, 0x00}, + {0x3138, 0x37}, + {0x3139, 0x00}, + {0x313A, 0x37}, + {0x313B, 0x00}, + {0x313C, 0x37}, + {0x313D, 0x00}, + {0x313E, 0xDF}, + {0x313F, 0x00}, + {0x3140, 0x37}, + {0x3141, 0x00}, + {0x3142, 0x2F}, + {0x3143, 0x00}, + {0x3144, 0x0F}, + {0x3145, 0x00}, + {0x3A85, 0x03}, + {0x3A86, 0x47}, + {0x3A87, 0x00}, + {REG_DELAY, 0x10}, + {0x303E, 0x02}, + {REG_DELAY, 0x07}, + {0x30F4, 0x00}, + {0x3018, 0xA2}, + {0x300a, 0x9c}, + {0x300b, 0x02}, + {0x300c, 0x0c}, + {0x300d, 0x00}, + {0x3001, 0x10}, + {REG_NULL, 0x00}, +}; + +/* + * Xclk 24Mhz + * max_framerate 30fps + * mipi_datarate per lane 720Mbps + * 4 lane + */ +static const struct regval imx317_3864x2174_regs[] = { + {0x3000, 0x1f}, + {0x303E, 0x02}, + {0x3120, 0xF0}, + {0x3121, 0x00}, + {0x3122, 0x02}, + {0x3123, 0x01}, + {0x3129, 0x9C}, + {0x312A, 0x02}, + {0x312D, 0x02}, + {0x3AC4, 0x01}, + {0x310B, 0x00}, + {0x30EE, 0x01}, + {0x3304, 0x32}, + {0x3306, 0x32}, + {0x3590, 0x32}, + {0x3686, 0x32}, + {0x3045, 0x32}, + {0x301A, 0x00}, + {0x304C, 0x00}, + {0x304D, 0x03}, + {0x331C, 0x1A}, + {0x3502, 0x02}, + {0x3529, 0x0E}, + {0x352A, 0x0E}, + {0x352B, 0x0E}, + {0x3538, 0x0E}, + {0x3539, 0x0E}, + {0x3553, 0x00}, + {0x357D, 0x05}, + {0x357F, 0x05}, + {0x3581, 0x04}, + {0x3583, 0x76}, + {0x3587, 0x01}, + {0x35BB, 0x0E}, + {0x35BC, 0x0E}, + {0x35BD, 0x0E}, + {0x35BE, 0x0E}, + {0x35BF, 0x0E}, + {0x366E, 0x00}, + {0x366F, 0x00}, + {0x3670, 0x00}, + {0x3671, 0x00}, + {0x3004, 0x01}, + {0x3005, 0x01}, + {0x3006, 0x00}, + {0x3007, 0x02}, + {0x300E, 0x00}, + {0x300F, 0x00}, + {0x3037, 0x00}, + {0x3038, 0x00}, + {0x3039, 0x00}, + {0x303A, 0x00}, + {0x303B, 0x00}, + {0x30DD, 0x00}, + {0x30DE, 0x00}, + {0x30DF, 0x00}, + {0x30E0, 0x00}, + {0x30E1, 0x00}, + {0x30E2, 0x01}, + {0x30F6, 0x10}, + {0x30F7, 0x02}, + {0x30F8, 0xc6}, + {0x30F9, 0x11}, + {0x30FA, 0x00}, + {0x3130, 0x86}, + {0x3131, 0x08}, + {0x3132, 0x7E}, + {0x3133, 0x08}, + {0x3A54, 0x18}, + {0x3A55, 0x0F}, + {0x3342, 0x0A}, + {0x3343, 0x00}, + {0x3344, 0x16}, + {0x3345, 0x00}, + {0x3528, 0x0E}, + {0x3554, 0x1F}, + {0x3555, 0x01}, + {0x3556, 0x01}, + {0x3557, 0x01}, + {0x3558, 0x01}, + {0x3559, 0x00}, + {0x355A, 0x00}, + {0x35BA, 0x0E}, + {0x366A, 0x1B}, + {0x366B, 0x1A}, + {0x366C, 0x19}, + {0x366D, 0x17}, + {0x33A6, 0x01}, + {0x306B, 0x05}, + {0x3A41, 0x08}, + {0x3134, 0x77}, + {0x3135, 0x00}, + {0x3136, 0x67}, + {0x3137, 0x00}, + {0x3138, 0x37}, + {0x3139, 0x00}, + {0x313A, 0x37}, + {0x313B, 0x00}, + {0x313C, 0x37}, + {0x313D, 0x00}, + {0x313E, 0xDF}, + {0x313F, 0x00}, + {0x3140, 0x37}, + {0x3141, 0x00}, + {0x3142, 0x2F}, + {0x3143, 0x00}, + {0x3144, 0x0F}, + {0x3145, 0x00}, + {0x3A85, 0x03}, + {0x3A86, 0x47}, + {0x3A87, 0x00}, + {0x3A43, 0x01}, + {REG_DELAY, 0x10}, + {0x303E, 0x02}, + {REG_DELAY, 0x07}, + {0x30F4, 0x00}, + {0x3018, 0xA2}, + {0x300a, 0x9c}, + {0x300b, 0x02}, + {0x300c, 0x0c}, + {0x300d, 0x00}, + {0x3001, 0x10}, + {REG_NULL, 0x00}, +}; + +static const struct imx317_mode supported_modes[] = { + { + .width = 1932, + .height = 1094, + .max_fps = 30, + .exp_def = 0x000C, + .hts_def = 0x011E, + .vts_def = 0x20D0, + .reg_list = imx317_1932x1094_regs, + }, + { + .width = 3864, + .height = 2174, + .max_fps = 30, + .exp_def = 0x000C, + .hts_def = 0x0210, + .vts_def = 0x11C6, + .reg_list = imx317_3864x2174_regs, + }, +}; + +static const s64 link_freq_menu_items[] = { + IMX317_LINK_FREQ_360MHZ +}; + +static const char * const imx317_test_pattern_menu[] = { + "Disabled", + "Vertical Color Bar Type 1", + "Vertical Color Bar Type 2", + "Vertical Color Bar Type 3", + "Vertical Color Bar Type 4" +}; + +/* Write registers up to 4 at a time */ +static int imx317_write_reg(struct i2c_client *client, u16 reg, + u32 len, u32 val) +{ + u32 buf_i, val_i; + u8 buf[6]; + u8 *val_p; + __be32 val_be; + + if (len > 4) + return -EINVAL; + + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + + val_be = cpu_to_be32(val); + val_p = (u8 *)&val_be; + buf_i = 2; + val_i = 4 - len; + + while (val_i < 4) + buf[buf_i++] = val_p[val_i++]; + + if (i2c_master_send(client, buf, len + 2) != len + 2) + return -EIO; + + return 0; +} + +static int imx317_write_array(struct i2c_client *client, + const struct regval *regs) +{ + u32 i; + int ret = 0; + + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) { + if (regs[i].addr == REG_DELAY) { + usleep_range(regs[i].val, 2 * regs[i].val); + } else { + ret = imx317_write_reg(client, regs[i].addr, + IMX317_REG_VALUE_08BIT, regs[i].val); + } + } + + return ret; +} + +/* Read registers up to 4 at a time */ +static int imx317_read_reg(struct i2c_client *client, u16 reg, unsigned int len, + u32 *val) +{ + struct i2c_msg msgs[2]; + u8 *data_be_p; + __be32 data_be = 0; + __be16 reg_addr_be = cpu_to_be16(reg); + int ret; + + if (len > 4 || !len) + return -EINVAL; + + data_be_p = (u8 *)&data_be; + + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 2; + msgs[0].buf = (u8 *)®_addr_be; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_be_p[4 - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = be32_to_cpu(data_be); + + return 0; +} + +static int imx317_get_reso_dist(const struct imx317_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); +} + +static const struct imx317_mode * +imx317_find_best_fit(struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *framefmt = &fmt->format; + int dist; + int cur_best_fit = 0; + int cur_best_fit_dist = -1; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { + dist = imx317_get_reso_dist(&supported_modes[i], framefmt); + if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { + cur_best_fit_dist = dist; + cur_best_fit = i; + } + } + + return &supported_modes[cur_best_fit]; +} + +static int imx317_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct imx317 *imx317 = to_imx317(sd); + const struct imx317_mode *mode; + s64 h_blank, vblank_def; + + mutex_lock(&imx317->mutex); + + mode = imx317_find_best_fit(fmt); + fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10; + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; +#else + mutex_unlock(&imx317->mutex); + return -ENOTTY; +#endif + } else { + imx317->cur_mode = mode; + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(imx317->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(imx317->vblank, vblank_def, + IMX317_VTS_MAX - mode->height, + 1, vblank_def); + } + + mutex_unlock(&imx317->mutex); + + return 0; +} + +static int imx317_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct imx317 *imx317 = to_imx317(sd); + const struct imx317_mode *mode = imx317->cur_mode; + + mutex_lock(&imx317->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); +#else + mutex_unlock(&imx317->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10; + fmt->format.field = V4L2_FIELD_NONE; + } + mutex_unlock(&imx317->mutex); + + return 0; +} + +static int imx317_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index != 0) + return -EINVAL; + code->code = MEDIA_BUS_FMT_SBGGR10_1X10; + + return 0; +} + +static int imx317_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fse->code != MEDIA_BUS_FMT_SBGGR10_1X10) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = supported_modes[fse->index].width; + fse->max_height = supported_modes[fse->index].height; + fse->min_height = supported_modes[fse->index].height; + + return 0; +} + +static int imx317_enable_test_pattern(struct imx317 *imx317, u32 pattern) +{ + return 0; +} + +static int imx317_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct imx317 *imx317 = to_imx317(sd); + const struct imx317_mode *mode = imx317->cur_mode; + + mutex_lock(&imx317->mutex); + fi->interval.numerator = 10000; + fi->interval.denominator = mode->max_fps * 10000; + mutex_unlock(&imx317->mutex); + + return 0; +} + +static void imx317_get_module_inf(struct imx317 *imx317, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strlcpy(inf->base.sensor, IMX317_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, imx317->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, imx317->len_name, sizeof(inf->base.lens)); +} + +static long imx317_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct imx317 *imx317 = to_imx317(sd); + long ret = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + imx317_get_module_inf(imx317, (struct rkmodule_inf *)arg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long imx317_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_awb_cfg *cfg; + long ret; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = imx317_ioctl(sd, cmd, inf); + if (!ret) + ret = copy_to_user(up, inf, sizeof(*inf)); + kfree(inf); + break; + case RKMODULE_AWB_CFG: + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(cfg, up, sizeof(*cfg)); + if (!ret) + ret = imx317_ioctl(sd, cmd, cfg); + kfree(cfg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + +static int __imx317_start_stream(struct imx317 *imx317) +{ + int ret; + + ret = imx317_write_array(imx317->client, imx317->cur_mode->reg_list); + if (ret) + return ret; + + /* In case these controls are set before streaming */ + mutex_unlock(&imx317->mutex); + ret = v4l2_ctrl_handler_setup(&imx317->ctrl_handler); + mutex_lock(&imx317->mutex); + if (ret) + return ret; + + return imx317_write_reg(imx317->client, IMX317_REG_CTRL_MODE, + IMX317_REG_VALUE_08BIT, IMX317_MODE_STREAMING); +} + +static int __imx317_stop_stream(struct imx317 *imx317) +{ + return imx317_write_reg(imx317->client, IMX317_REG_CTRL_MODE, + IMX317_REG_VALUE_08BIT, IMX317_MODE_SW_STANDBY); +} + +static int imx317_s_stream(struct v4l2_subdev *sd, int on) +{ + struct imx317 *imx317 = to_imx317(sd); + struct i2c_client *client = imx317->client; + int ret = 0; + + mutex_lock(&imx317->mutex); + on = !!on; + if (on == imx317->streaming) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = __imx317_start_stream(imx317); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + __imx317_stop_stream(imx317); + pm_runtime_put(&client->dev); + } + + imx317->streaming = on; + +unlock_and_return: + mutex_unlock(&imx317->mutex); + + return ret; +} + +static int imx317_s_power(struct v4l2_subdev *sd, int on) +{ + struct imx317 *imx317 = to_imx317(sd); + struct i2c_client *client = imx317->client; + int ret = 0; + + mutex_lock(&imx317->mutex); + + /* If the power state is not modified - no work to do. */ + if (imx317->power_on == !!on) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = imx317_write_array(imx317->client, imx317_global_regs); + if (ret) { + v4l2_err(sd, "could not set init registers\n"); + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + imx317->power_on = true; + } else { + pm_runtime_put(&client->dev); + imx317->power_on = false; + } + +unlock_and_return: + mutex_unlock(&imx317->mutex); + + return ret; +} + +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 imx317_cal_delay(u32 cycles) +{ + return DIV_ROUND_UP(cycles, IMX317_XVCLK_FREQ / 1000 / 1000); +} + +static int __imx317_power_on(struct imx317 *imx317) +{ + int ret; + u32 delay_us; + struct device *dev = &imx317->client->dev; + + if (!IS_ERR_OR_NULL(imx317->pins_default)) { + ret = pinctrl_select_state(imx317->pinctrl, + imx317->pins_default); + if (ret < 0) + dev_err(dev, "could not set pins\n"); + } + ret = clk_set_rate(imx317->xvclk, IMX317_XVCLK_FREQ); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate (24MHz)\n"); + if (clk_get_rate(imx317->xvclk) != IMX317_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); + ret = clk_prepare_enable(imx317->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + if (!IS_ERR(imx317->reset_gpio)) + gpiod_set_value_cansleep(imx317->reset_gpio, 0); + + ret = regulator_bulk_enable(IMX317_NUM_SUPPLIES, imx317->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + + if (!IS_ERR(imx317->reset_gpio)) + gpiod_set_value_cansleep(imx317->reset_gpio, 1); + + usleep_range(500, 1000); + if (!IS_ERR(imx317->pwdn_gpio)) + gpiod_set_value_cansleep(imx317->pwdn_gpio, 1); + + /* 8192 cycles prior to first SCCB transaction */ + delay_us = imx317_cal_delay(8192); + usleep_range(delay_us, delay_us * 2); + + return 0; + +disable_clk: + clk_disable_unprepare(imx317->xvclk); + + return ret; +} + +static void __imx317_power_off(struct imx317 *imx317) +{ + int ret; + struct device *dev = &imx317->client->dev; + + if (!IS_ERR(imx317->pwdn_gpio)) + gpiod_set_value_cansleep(imx317->pwdn_gpio, 0); + clk_disable_unprepare(imx317->xvclk); + if (!IS_ERR(imx317->reset_gpio)) + gpiod_set_value_cansleep(imx317->reset_gpio, 0); + if (!IS_ERR_OR_NULL(imx317->pins_sleep)) { + ret = pinctrl_select_state(imx317->pinctrl, + imx317->pins_sleep); + if (ret < 0) + dev_dbg(dev, "could not set pins\n"); + } + regulator_bulk_disable(IMX317_NUM_SUPPLIES, imx317->supplies); +} + +static int imx317_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx317 *imx317 = to_imx317(sd); + + return __imx317_power_on(imx317); +} + +static int imx317_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx317 *imx317 = to_imx317(sd); + + __imx317_power_off(imx317); + + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int imx317_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct imx317 *imx317 = to_imx317(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + const struct imx317_mode *def_mode = &supported_modes[0]; + + mutex_lock(&imx317->mutex); + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10; + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&imx317->mutex); + /* No crop or compose */ + + return 0; +} +#endif + +static const struct dev_pm_ops imx317_pm_ops = { + SET_RUNTIME_PM_OPS(imx317_runtime_suspend, + imx317_runtime_resume, NULL) +}; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops imx317_internal_ops = { + .open = imx317_open, +}; +#endif + +static const struct v4l2_subdev_core_ops imx317_core_ops = { + .s_power = imx317_s_power, + .ioctl = imx317_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = imx317_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops imx317_video_ops = { + .s_stream = imx317_s_stream, + .g_frame_interval = imx317_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops imx317_pad_ops = { + .enum_mbus_code = imx317_enum_mbus_code, + .enum_frame_size = imx317_enum_frame_sizes, + .get_fmt = imx317_get_fmt, + .set_fmt = imx317_set_fmt, +}; + +static const struct v4l2_subdev_ops imx317_subdev_ops = { + .core = &imx317_core_ops, + .video = &imx317_video_ops, + .pad = &imx317_pad_ops, +}; + +static int imx317_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct imx317 *imx317 = container_of(ctrl->handler, + struct imx317, ctrl_handler); + struct i2c_client *client = imx317->client; + s64 max; + u32 val = 0; + int ret = 0; + + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ + max = imx317->cur_mode->height + ctrl->val - 4; + __v4l2_ctrl_modify_range(imx317->exposure, + imx317->exposure->minimum, max, + imx317->exposure->step, + imx317->exposure->default_value); + break; + } + + if (pm_runtime_get(&client->dev) <= 0) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + /* 4 least significant bits of expsoure are fractional part */ + ret = imx317_write_reg(imx317->client, + IMX317_REG_EXPOSURE_H, + IMX317_REG_VALUE_08BIT, + (ctrl->val & 0xFF00) >> 8); + ret |= imx317_write_reg(imx317->client, + IMX317_REG_EXPOSURE_L, + IMX317_REG_VALUE_08BIT, + ctrl->val & 0xFF); + break; + case V4L2_CID_ANALOGUE_GAIN: + val = 2048 * 2048; + do_div(val, ctrl->val); + if (val <= 2048) + val = 2048 - val; + else + val = 0; + + if (val > 0x7A5) + val = 0x7A5; + + ret = imx317_write_reg(imx317->client, IMX317_REG_GAIN_H, + IMX317_REG_VALUE_08BIT, + (val >> IMX317_GAIN_H_SHIFT) & IMX317_GAIN_H_MASK); + ret |= imx317_write_reg(imx317->client, IMX317_REG_GAIN_L, + IMX317_REG_VALUE_08BIT, + val & IMX317_GAIN_L_MASK); + break; + case V4L2_CID_VBLANK: + val = ctrl->val + imx317->cur_mode->height; + ret = imx317_write_reg(imx317->client, IMX317_REG_VTS_H, + IMX317_REG_VALUE_08BIT, + (val & 0x0F0000) >> 16); + ret |= imx317_write_reg(imx317->client, IMX317_REG_VTS_M, + IMX317_REG_VALUE_08BIT, + (val & 0x00FF00) >> 8); + ret |= imx317_write_reg(imx317->client, IMX317_REG_VTS_L, + IMX317_REG_VALUE_08BIT, + val & 0x0000FF); + break; + case V4L2_CID_TEST_PATTERN: + ret = imx317_enable_test_pattern(imx317, ctrl->val); + break; + default: + dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", + __func__, ctrl->id, ctrl->val); + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops imx317_ctrl_ops = { + .s_ctrl = imx317_set_ctrl, +}; + +static int imx317_initialize_controls(struct imx317 *imx317) +{ + const struct imx317_mode *mode; + struct v4l2_ctrl_handler *handler; + struct v4l2_ctrl *ctrl; + s64 exposure_max, vblank_def; + u32 h_blank; + int ret; + + handler = &imx317->ctrl_handler; + mode = imx317->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 8); + if (ret) + return ret; + handler->lock = &imx317->mutex; + + ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, + 0, 0, link_freq_menu_items); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, + 0, IMX317_PIXEL_RATE, 1, IMX317_PIXEL_RATE); + + h_blank = mode->hts_def - mode->width; + imx317->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (imx317->hblank) + imx317->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = mode->vts_def - mode->height; + imx317->vblank = v4l2_ctrl_new_std(handler, &imx317_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + IMX317_VTS_MAX - mode->height, + 1, vblank_def); + + exposure_max = mode->vts_def - 4; + imx317->exposure = v4l2_ctrl_new_std(handler, &imx317_ctrl_ops, + V4L2_CID_EXPOSURE, IMX317_EXPOSURE_MIN, + exposure_max, IMX317_EXPOSURE_STEP, + mode->exp_def); + + imx317->anal_gain = v4l2_ctrl_new_std(handler, &imx317_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, IMX317_GAIN_MIN, + IMX317_GAIN_MAX, IMX317_GAIN_STEP, + IMX317_GAIN_DEFAULT); + + imx317->test_pattern = v4l2_ctrl_new_std_menu_items(handler, + &imx317_ctrl_ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(imx317_test_pattern_menu) - 1, + 0, 0, imx317_test_pattern_menu); + + if (handler->error) { + ret = handler->error; + dev_err(&imx317->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + imx317->subdev.ctrl_handler = handler; + + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +static int imx317_check_sensor_id(struct imx317 *imx317, + struct i2c_client *client) +{ + struct device *dev = &imx317->client->dev; + u32 id = 0; + int ret; + + ret = imx317_read_reg(client, IMX317_REG_CHIP_ID, + IMX317_REG_VALUE_08BIT, &id); + if (id != CHIP_ID) { + dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret); + return -ENODEV; + } + + dev_info(dev, "Detected OV%06x sensor\n", CHIP_ID); + + return 0; +} + +static int imx317_configure_regulators(struct imx317 *imx317) +{ + unsigned int i; + + for (i = 0; i < IMX317_NUM_SUPPLIES; i++) + imx317->supplies[i].supply = imx317_supply_names[i]; + + return devm_regulator_bulk_get(&imx317->client->dev, + IMX317_NUM_SUPPLIES, + imx317->supplies); +} + +static int imx317_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct imx317 *imx317; + struct v4l2_subdev *sd; + char facing[2]; + int ret; + + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + imx317 = devm_kzalloc(dev, sizeof(*imx317), GFP_KERNEL); + if (!imx317) + return -ENOMEM; + + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &imx317->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &imx317->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &imx317->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &imx317->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + + imx317->client = client; + imx317->cur_mode = &supported_modes[0]; + + imx317->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(imx317->xvclk)) { + dev_err(dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + imx317->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(imx317->reset_gpio)) + dev_warn(dev, "Failed to get reset-gpios\n"); + + imx317->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(imx317->pwdn_gpio)) + dev_warn(dev, "Failed to get pwdn-gpios\n"); + + imx317->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR(imx317->pinctrl)) { + imx317->pins_default = + pinctrl_lookup_state(imx317->pinctrl, + OF_CAMERA_PINCTRL_STATE_DEFAULT); + if (IS_ERR(imx317->pins_default)) + dev_err(dev, "could not get default pinstate\n"); + + imx317->pins_sleep = + pinctrl_lookup_state(imx317->pinctrl, + OF_CAMERA_PINCTRL_STATE_SLEEP); + if (IS_ERR(imx317->pins_sleep)) + dev_err(dev, "could not get sleep pinstate\n"); + } else { + dev_err(dev, "no pinctrl\n"); + } + + ret = imx317_configure_regulators(imx317); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + + mutex_init(&imx317->mutex); + + sd = &imx317->subdev; + v4l2_i2c_subdev_init(sd, client, &imx317_subdev_ops); + ret = imx317_initialize_controls(imx317); + if (ret) + goto err_destroy_mutex; + + ret = __imx317_power_on(imx317); + if (ret) + goto err_free_handler; + + ret = imx317_check_sensor_id(imx317, client); + if (ret) + goto err_power_off; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &imx317_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + imx317->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &imx317->pad); + if (ret < 0) + goto err_power_off; +#endif + + memset(facing, 0, sizeof(facing)); + if (strcmp(imx317->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + imx317->module_index, facing, + IMX317_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor_common(sd); + if (ret) { + dev_err(dev, "v4l2 async register subdev failed\n"); + goto err_clean_entity; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; + +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif +err_power_off: + __imx317_power_off(imx317); +err_free_handler: + v4l2_ctrl_handler_free(&imx317->ctrl_handler); +err_destroy_mutex: + mutex_destroy(&imx317->mutex); + + return ret; +} + +static int imx317_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx317 *imx317 = to_imx317(sd); + + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + v4l2_ctrl_handler_free(&imx317->ctrl_handler); + mutex_destroy(&imx317->mutex); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __imx317_power_off(imx317); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id imx317_of_match[] = { + { .compatible = "sony,imx317" }, + {}, +}; +MODULE_DEVICE_TABLE(of, imx317_of_match); +#endif + +static const struct i2c_device_id imx317_match_id[] = { + { "sony,imx317", 0 }, + { }, +}; + +static struct i2c_driver imx317_i2c_driver = { + .driver = { + .name = IMX317_NAME, + .pm = &imx317_pm_ops, + .of_match_table = of_match_ptr(imx317_of_match), + }, + .probe = &imx317_probe, + .remove = &imx317_remove, + .id_table = imx317_match_id, +}; + +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&imx317_i2c_driver); +} + +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&imx317_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_DESCRIPTION("OmniVision imx317 sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/imx323.c b/drivers/media/i2c/imx323.c index 837c87104e88..f82eb08a9ad0 100644 --- a/drivers/media/i2c/imx323.c +++ b/drivers/media/i2c/imx323.c @@ -3,6 +3,8 @@ * imx323 driver * * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd. + * + * V0.0X01.0X01 add poweron function. */ #include @@ -13,12 +15,17 @@ #include #include #include +#include #include +#include +#include #include #include #include #include +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x01) + #ifndef V4L2_CID_DIGITAL_GAIN #define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN #endif @@ -60,6 +67,8 @@ /* h_offs 35 v_offs 14 */ #define PIX_FORMAT MEDIA_BUS_FMT_SBGGR10_1X10 +#define IMX323_NAME "imx323" + struct cam_regulator { char name[32]; int val; @@ -106,7 +115,12 @@ struct imx323 { struct v4l2_ctrl *test_pattern; struct mutex mutex; bool streaming; + bool power_on; const struct imx323_mode *cur_mode; + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; }; #define to_imx323(sd) container_of(sd, struct imx323, subdev) @@ -370,6 +384,76 @@ static int imx323_enable_test_pattern(struct imx323 *imx323, u32 pattern) return 0; } +static void imx323_get_module_inf(struct imx323 *imx323, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strlcpy(inf->base.sensor, IMX323_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, imx323->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, imx323->len_name, sizeof(inf->base.lens)); +} + +static long imx323_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct imx323 *imx323 = to_imx323(sd); + long ret = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + imx323_get_module_inf(imx323, (struct rkmodule_inf *)arg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long imx323_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_awb_cfg *cfg; + long ret; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = imx323_ioctl(sd, cmd, inf); + if (!ret) + ret = copy_to_user(up, inf, sizeof(*inf)); + kfree(inf); + break; + case RKMODULE_AWB_CFG: + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(cfg, up, sizeof(*cfg)); + if (!ret) + ret = imx323_ioctl(sd, cmd, cfg); + kfree(cfg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + static int __imx323_start_stream(struct imx323 *imx323) { int ret; @@ -445,6 +529,37 @@ static int imx323_g_frame_interval(struct v4l2_subdev *sd, return 0; } +static int imx323_s_power(struct v4l2_subdev *sd, int on) +{ + struct imx323 *imx323 = to_imx323(sd); + struct i2c_client *client = imx323->client; + int ret = 0; + + mutex_lock(&imx323->mutex); + + /* If the power state is not modified - no work to do. */ + if (imx323->power_on == !!on) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + imx323->power_on = true; + } else { + pm_runtime_put(&client->dev); + imx323->power_on = false; + } + +unlock_and_return: + mutex_unlock(&imx323->mutex); + + return ret; +} + /* Calculate the delay in us by clock rate and clock cycles */ static inline u32 imx323_cal_delay(u32 cycles) { @@ -576,6 +691,14 @@ static const struct v4l2_subdev_internal_ops imx323_internal_ops = { }; #endif +static const struct v4l2_subdev_core_ops imx323_core_ops = { + .s_power = imx323_s_power, + .ioctl = imx323_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = imx323_compat_ioctl32, +#endif +}; + static const struct v4l2_subdev_video_ops imx323_video_ops = { .s_stream = imx323_s_stream, .g_mbus_config = imx323_g_mbus_config, @@ -590,6 +713,7 @@ static const struct v4l2_subdev_pad_ops imx323_pad_ops = { }; static const struct v4l2_subdev_ops imx323_subdev_ops = { + .core = &imx323_core_ops, .video = &imx323_video_ops, .pad = &imx323_pad_ops, }; @@ -710,7 +834,7 @@ static int imx323_check_sensor_id(struct imx323 *imx323, IMX323_REG_VALUE_08BIT, &id); if (id != CHIP_ID) { dev_err(dev, "Unexpected sensor id(%x), ret(%d)\n", id, ret); - return ret; + return -ENODEV; } dev_info(dev, "Detected IMX323 sensor\n"); @@ -735,14 +859,34 @@ static int imx323_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; + struct device_node *node = dev->of_node; struct imx323 *imx323; struct v4l2_subdev *sd; + char facing[2]; int ret; + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + imx323 = devm_kzalloc(dev, sizeof(*imx323), GFP_KERNEL); if (!imx323) return -ENOMEM; + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &imx323->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &imx323->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &imx323->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &imx323->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + imx323->client = client; imx323->cur_mode = &supported_modes[0]; @@ -784,17 +928,27 @@ static int imx323_probe(struct i2c_client *client, #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API sd->internal_ops = &imx323_internal_ops; - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; #endif #if defined(CONFIG_MEDIA_CONTROLLER) imx323->pad.flags = MEDIA_PAD_FL_SOURCE; - sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; - ret = media_entity_init(&sd->entity, 1, &imx323->pad, 0); + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &imx323->pad); if (ret < 0) goto err_power_off; #endif - ret = v4l2_async_register_subdev(sd); + memset(facing, 0, sizeof(facing)); + if (strcmp(imx323->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + imx323->module_index, facing, + IMX323_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor_common(sd); if (ret) { dev_err(dev, "v4l2 async register subdev failed\n"); goto err_clean_entity; @@ -855,7 +1009,7 @@ static const struct i2c_device_id imx323_match_id[] = { static struct i2c_driver imx323_i2c_driver = { .driver = { - .name = "imx323", + .name = IMX323_NAME, .pm = &imx323_pm_ops, .of_match_table = of_match_ptr(imx323_of_match), }, diff --git a/drivers/media/i2c/imx327.c b/drivers/media/i2c/imx327.c new file mode 100644 index 000000000000..40b38e6cc739 --- /dev/null +++ b/drivers/media/i2c/imx327.c @@ -0,0 +1,1386 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * imx327 driver + * + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x02) + +#ifndef V4L2_CID_DIGITAL_GAIN +#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN +#endif + +#define IMX327_LINK_FREQ 222750000 +#define IMX327_HDR_LANES 4 +#define IMX327_LANES 2 +#define IMX327_BITS_PER_SAMPLE 12 + +/* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ +#define IMX327_PIXEL_RATE \ +(IMX327_LINK_FREQ * 2 * IMX327_LANES / IMX327_BITS_PER_SAMPLE) + +#define IMX327_PIXEL_HDR_RATE \ +(IMX327_LINK_FREQ * 2 * IMX327_HDR_LANES / IMX327_BITS_PER_SAMPLE) + +#define IMX327_XVCLK_FREQ 37125000 + +#define CHIP_ID 0xa0 +#define IMX327_REG_CHIP_ID 0x3008 + +#define IMX327_REG_CTRL_MODE 0x3000 +#define IMX327_MODE_SW_STANDBY 0x1 +#define IMX327_MODE_STREAMING 0x0 + +#define IMX327_REG_EXPOSURE_H 0x3022 +#define IMX327_REG_EXPOSURE_M 0x3021 +#define IMX327_REG_EXPOSURE_L 0x3020 + +#define IMX327_FETCH_HIGH_BYTE_EXP(VAL) (((VAL) >> 16) & 0x03) +#define IMX327_FETCH_MID_BYTE_EXP(VAL) (((VAL) >> 8) & 0xFF) +#define IMX327_FETCH_LOW_BYTE_EXP(VAL) ((VAL) & 0xFF) + +#define IMX327_EXPOSURE_MIN 2 +#define IMX327_EXPOSURE_STEP 1 +#define IMX327_VTS_MAX 0x7fff + +#define IMX327_REG_GAIN 0x3014 +#define IMX327_GAIN_MIN 0x00 +#define IMX327_GAIN_MAX 0xee +#define IMX327_GAIN_STEP 1 +#define IMX327_GAIN_DEFAULT 0x00 + +#define IMX327_REG_TEST_PATTERN 0x5e00 +#define IMX327_TEST_PATTERN_ENABLE 0x80 +#define IMX327_TEST_PATTERN_DISABLE 0x0 + +#define IMX327_REG_VTS_H 0x301a +#define IMX327_REG_VTS_M 0x3019 +#define IMX327_REG_VTS_L 0x3018 +#define IMX327_FETCH_HIGH_BYTE_VTS(VAL) (((VAL) >> 16) & 0x03) +#define IMX327_FETCH_MID_BYTE_VTS(VAL) (((VAL) >> 8) & 0xFF) +#define IMX327_FETCH_LOW_BYTE_VTS(VAL) ((VAL) & 0xFF) + +#define REG_DELAY 0xFFFE +#define REG_NULL 0xFFFF + +#define IMX327_REG_VALUE_08BIT 1 +#define IMX327_REG_VALUE_16BIT 2 +#define IMX327_REG_VALUE_24BIT 3 + +#define IMX327_NAME "imx327" + +#define IMX327_RHS1 0xe1 +static bool g_isHCG; + +static const char * const imx327_supply_names[] = { + "avdd", /* Analog power */ + "dovdd", /* Digital I/O power */ + "dvdd", /* Digital core power */ +}; + +#define IMX327_NUM_SUPPLIES ARRAY_SIZE(imx327_supply_names) + +struct regval { + u16 addr; + u8 val; +}; + +struct imx327_mode { + u32 bus_fmt; + u32 width; + u32 height; + u32 max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + const struct regval *reg_list; +}; + +struct imx327 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; + struct regulator_bulk_data supplies[IMX327_NUM_SUPPLIES]; + + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *anal_gain; + struct v4l2_ctrl *digi_gain; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *test_pattern; + struct mutex mutex; + bool streaming; + bool power_on; + bool has_devnode; + const struct imx327_mode *cur_mode; + const struct imx327_mode *support_modes; + u32 support_modes_num; + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; +}; + +#define to_imx327(sd) container_of(sd, struct imx327, subdev) + +/* + * Xclk 37.125Mhz + * max_framerate 30fps + * mipi_datarate per lane 445Mbps + */ +static const struct regval imx327_1920x1080_regs[] = { + {0x3003, 0x01}, + {REG_DELAY, 0x10}, + {0x3000, 0x01}, + {0x3001, 0x00}, + {0x3002, 0x00}, + {0x3005, 0x01}, + {0x3007, 0x00}, + {0x3009, 0x02}, + {0x300a, 0xf0}, + {0x300b, 0x00}, + {0x3011, 0x0a}, + {0x3012, 0x64}, + {0x3014, 0x00}, + {0x3018, 0x65},/* vMAX L */ + {0x3019, 0x04},/* VMAX M */ + {0x301a, 0x00},/* VMAX H */ + {0x301c, 0x30},/* HMAX L */ + {0x301d, 0x11},/* HMAX H */ + {0x3020, 0xfe}, + {0x3021, 0x03}, + {0x3022, 0x00}, + {0x3046, 0x01}, + {0x3048, 0x00}, + {0x3049, 0x08}, + {0x304b, 0x0a}, + {0x305c, 0x18}, + {0x305d, 0x03}, + {0x305e, 0x20}, + {0x305f, 0x01}, + {0x309e, 0x4a}, + {0x309f, 0x4a}, + {0x30d2, 0x19}, + {0x30d7, 0x03}, + {0x3128, 0x04}, + {0x3129, 0x00}, + {0x313b, 0x41}, + {0x315e, 0x1a}, + {0x3164, 0x1a}, + {0x317c, 0x00}, + {0x31ec, 0x0e}, + {0x3405, 0x10}, + {0x3407, 0x01}, + {0x3414, 0x0a}, + {0x3418, 0x38}, + {0x3419, 0x04}, + {0x3441, 0x0c}, + {0x3442, 0x0c}, + {0x3443, 0x01}, + {0x3444, 0x20}, + {0x3445, 0x25}, + {0x3446, 0x57}, + {0x3447, 0x00}, + {0x3448, 0x55}, + {0x3449, 0x00}, + {0x344a, 0x1f}, + {0x344b, 0x00}, + {0x344c, 0x1f}, + {0x344d, 0x00}, + {0x344e, 0x1f}, + {0x344f, 0x00}, + {0x3450, 0x77}, + {0x3451, 0x00}, + {0x3452, 0x1f}, + {0x3453, 0x00}, + {0x3454, 0x17}, + {0x3455, 0x00}, + {0x3472, 0x80}, + {0x3473, 0x07}, + {0x3480, 0x49}, + {REG_NULL, 0x00}, +}; + +static const struct regval imx327_hdr_1920x1080_regs[] = { + //10bit hdr2 + {0x3003, 0x01}, + {REG_DELAY, 0x10}, + {0x3000, 0x01}, + {0x3001, 0x00}, + {0x3002, 0x00}, + {0x3005, 0x00}, + {0x3007, 0x40}, + {0x3009, 0x01}, + {0x300a, 0x3c}, + {0x300c, 0x11}, //hdr+ + {0x3011, 0x02}, + {0x3018, 0xb8},/* VMAX L */ + {0x3019, 0x05},/* VMAX M */ + {0x301a, 0x00}, + {0x301c, 0xEc},/* HMAX L */ + {0x301d, 0x07},/* HMAX H */ + {0x3045, 0x05},//hdr+ + {0x3046, 0x00}, + {0x304b, 0x0a}, + {0x305c, 0x18}, + {0x305d, 0x03}, + {0x305e, 0x20}, + {0x305f, 0x01}, + {0x309e, 0x4a}, + {0x309f, 0x4a}, + {0x30d2, 0x19}, + {0x30d7, 0x03}, + {0x3106, 0x11},//hdr+ + {0x3129, 0x1d}, + {0x313b, 0x61}, + {0x315e, 0x1a}, + {0x3164, 0x1a}, + {0x317c, 0x12}, + {0x31ec, 0x37}, + {0x3405, 0x10}, + {0x3407, 0x03}, + {0x3414, 0x00}, + {0x3415, 0x00},//hdr+ + {0x3418, 0x7A}, + {0x3419, 0x09}, + {0x3441, 0x0a}, + {0x3442, 0x0a}, + {0x3443, 0x03}, + {0x3444, 0x20}, + {0x3445, 0x25}, + {0x3446, 0x57}, + {0x3447, 0x00}, + {0x3448, 0x37},//37? + {0x3449, 0x00}, + {0x344a, 0x1f}, + {0x344b, 0x00}, + {0x344c, 0x1f}, + {0x344d, 0x00}, + {0x344e, 0x1f}, + {0x344f, 0x00}, + {0x3450, 0x77}, + {0x3451, 0x00}, + {0x3452, 0x1f}, + {0x3453, 0x00}, + {0x3454, 0x17}, + {0x3455, 0x00}, + {0x3472, 0xa0}, + {0x3473, 0x07}, + {0x347b, 0x23}, + {0x3480, 0x49}, + + {0x31a0, 0xb4},//hdr+ + {0x31a1, 0x02},//hdr+ + + {0x3020, 0x02},//hdr+ shs1 l short + {0x3021, 0x00},//hdr+ shs1 m + {0x3022, 0x00},//hdr+ shs1 h + {0x3030, 0xe1},//hdr+ IMX327_RHS1 + {0x3031, 0x00},//hdr+IMX327_RHS1 + {0x3032, 0x00},//hdr+ + + {0x31A0, 0xe8},//hdr+ HBLANK1 + {0x31A1, 0x01},//hdr+ + {0x303C, 0x04},//hdr+ + {0x303D, 0x00},//hdr+ + {0x303E, 0x41},//hdr+ + {0x303F, 0x04},//hdr+ + {0x303A, 0x08},//hdr+ + + {0x3024, 0xc9},//hdr+ shs2 l + {0x3025, 0x06},//hdr+ shs2 m + {0x3026, 0x00},//hdr+ shs2 h + + {0x3010, 0x61},//hdr+ gain 1frame FPGC + {0x3014, 0x00},//hdr+ gain 1frame long + {0x30F0, 0x64},//hdr+ gain 2frame FPGC + {0x30f2, 0x00},//hdr+ gain 2frame short + {REG_NULL, 0x00}, +}; + +static const struct imx327_mode supported_hdr_modes[] = { + { + .bus_fmt = MEDIA_BUS_FMT_SBGGR10_1X10, + .width = 1920, + .height = 1080, + .max_fps = 50, + .exp_def = 0x0002, + .hts_def = 0x07ec, + .vts_def = 0x05b8, + .reg_list = imx327_hdr_1920x1080_regs, + } +}; + +static const struct imx327_mode supported_linear_modes[] = { + { + .bus_fmt = MEDIA_BUS_FMT_SRGGB12_1X12, + .width = 1920, + .height = 1080, + .max_fps = 30, + .exp_def = 0x0300, + .hts_def = 0x1130, + .vts_def = 0x0465, + .reg_list = imx327_1920x1080_regs, + }, +}; + +static const s64 link_freq_menu_items[] = { + IMX327_LINK_FREQ +}; + +static const char * const imx327_test_pattern_menu[] = { + "Disabled", + "Vertical Color Bar Type 1", + "Vertical Color Bar Type 2", + "Vertical Color Bar Type 3", + "Vertical Color Bar Type 4" +}; + +/* Write registers up to 4 at a time */ +static int imx327_write_reg(struct i2c_client *client, u16 reg, + int len, u32 val) +{ + u32 buf_i, val_i; + u8 buf[6]; + u8 *val_p; + __be32 val_be; + + if (len > 4) + return -EINVAL; + + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + + val_be = cpu_to_be32(val); + val_p = (u8 *)&val_be; + buf_i = 2; + val_i = 4 - len; + + while (val_i < 4) + buf[buf_i++] = val_p[val_i++]; + + if (i2c_master_send(client, buf, len + 2) != len + 2) + return -EIO; + + return 0; +} + +static int imx327_write_array(struct i2c_client *client, + const struct regval *regs) +{ + u32 i; + int ret = 0; + + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) + if (unlikely(regs[i].addr == REG_DELAY)) + usleep_range(regs[i].val, regs[i].val * 2); + else + ret = imx327_write_reg(client, regs[i].addr, + IMX327_REG_VALUE_08BIT, + regs[i].val); + + return ret; +} + +/* Read registers up to 4 at a time */ +static int imx327_read_reg(struct i2c_client *client, u16 reg, unsigned int len, + u32 *val) +{ + struct i2c_msg msgs[2]; + u8 *data_be_p; + __be32 data_be = 0; + __be16 reg_addr_be = cpu_to_be16(reg); + int ret, i; + + if (len > 4 || !len) + return -EINVAL; + + data_be_p = (u8 *)&data_be; + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 2; + msgs[0].buf = (u8 *)®_addr_be; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_be_p[4 - len]; + + for (i = 0; i < 3; i++) { + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret == ARRAY_SIZE(msgs)) + break; + } + if (ret != ARRAY_SIZE(msgs) && i == 3) + return -EIO; + + *val = be32_to_cpu(data_be); + + return 0; +} + +static int imx327_get_reso_dist(const struct imx327_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); +} + +static const struct imx327_mode * +imx327_find_best_fit(struct v4l2_subdev *sd, + struct v4l2_subdev_format *fmt) +{ + struct imx327 *imx327 = to_imx327(sd); + struct v4l2_mbus_framefmt *framefmt = &fmt->format; + int dist; + int cur_best_fit = 0; + int cur_best_fit_dist = -1; + unsigned int i; + + for (i = 0; i < imx327->support_modes_num; i++) { + dist = imx327_get_reso_dist(&imx327->support_modes[i], + framefmt); + if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { + cur_best_fit_dist = dist; + cur_best_fit = i; + } + } + + return &imx327->support_modes[cur_best_fit]; +} + +static int imx327_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct imx327 *imx327 = to_imx327(sd); + const struct imx327_mode *mode; + s64 h_blank, vblank_def; + + mutex_lock(&imx327->mutex); + + mode = imx327_find_best_fit(sd, fmt); + fmt->format.code = mode->bus_fmt; + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; +#else + mutex_unlock(&imx327->mutex); + return -ENOTTY; +#endif + } else { + imx327->cur_mode = mode; + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(imx327->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(imx327->vblank, vblank_def, + IMX327_VTS_MAX - mode->height, + 1, vblank_def); + } + + mutex_unlock(&imx327->mutex); + + return 0; +} + +static int imx327_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct imx327 *imx327 = to_imx327(sd); + const struct imx327_mode *mode = imx327->cur_mode; + + mutex_lock(&imx327->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); +#else + mutex_unlock(&imx327->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = mode->bus_fmt; + fmt->format.field = V4L2_FIELD_NONE; + } + mutex_unlock(&imx327->mutex); + return 0; +} + +static int imx327_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct imx327 *imx327 = to_imx327(sd); + if (code->index != 0) + return -EINVAL; + code->code = imx327->support_modes[0].bus_fmt; + + return 0; +} + +static int imx327_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct imx327 *imx327 = to_imx327(sd); + + if (fse->index >= imx327->support_modes_num) + return -EINVAL; + + if (fse->code != imx327->support_modes[0].bus_fmt) + return -EINVAL; + + fse->min_width = imx327->support_modes[fse->index].width; + fse->max_width = imx327->support_modes[fse->index].width; + fse->max_height = imx327->support_modes[fse->index].height; + fse->min_height = imx327->support_modes[fse->index].height; + + return 0; +} + +static int imx327_enable_test_pattern(struct imx327 *imx327, u32 pattern) +{ + u32 val; + + if (pattern) + val = (pattern - 1) | IMX327_TEST_PATTERN_ENABLE; + else + val = IMX327_TEST_PATTERN_DISABLE; + + return imx327_write_reg(imx327->client, + IMX327_REG_TEST_PATTERN, + IMX327_REG_VALUE_08BIT, + val); +} + +static int imx327_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct imx327 *imx327 = to_imx327(sd); + const struct imx327_mode *mode = imx327->cur_mode; + + mutex_lock(&imx327->mutex); + fi->interval.numerator = 10000; + fi->interval.denominator = mode->max_fps * 10000; + mutex_unlock(&imx327->mutex); + + return 0; +} + +static void imx327_get_module_inf(struct imx327 *imx327, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strlcpy(inf->base.sensor, IMX327_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, imx327->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, imx327->len_name, sizeof(inf->base.lens)); +} + +static long imx327_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct imx327 *imx327 = to_imx327(sd); + struct i2c_client *client = imx327->client; + struct preisp_hdrae_exp_s *hdrae_exp; + long ret = 0; + u32 gain_switch = 0; + s32 l_gain, s_gain, shs1, fsc, shs2; + + switch (cmd) { + case PREISP_CMD_SET_HDRAE_EXP: + hdrae_exp = (struct preisp_hdrae_exp_s *)arg; + dev_info(&client->dev, + "rev exp req: L_time=%d, gain=%d, S_time=%d, gain=%d\n", + hdrae_exp->long_exp_reg, + hdrae_exp->long_gain_reg, + hdrae_exp->short_exp_reg, + hdrae_exp->short_gain_reg); + + //long gain and short gain + l_gain = hdrae_exp->long_gain_reg; + s_gain = hdrae_exp->short_gain_reg; + if (l_gain > 100) { + l_gain -= 27; //8.1db + s_gain = (s_gain < 27) ? 0 : (s_gain - 27); + if (!g_isHCG) { + gain_switch = 0x11; + g_isHCG = true; + } + } else if (l_gain < 44) { + if (g_isHCG) { + gain_switch = 0x1; + g_isHCG = false; + } + } else { + if (g_isHCG) { + l_gain -= 27; + s_gain = (s_gain < 27) ? 0 : (s_gain - 27); + } + } + imx327_write_reg(imx327->client, 0x3014, + IMX327_REG_VALUE_08BIT, l_gain); + imx327_write_reg(imx327->client, 0x30f2, + IMX327_REG_VALUE_08BIT, s_gain); + if (gain_switch) + imx327_write_reg(imx327->client, 0x3009, + IMX327_REG_VALUE_08BIT, gain_switch); + + //long exposure and short exposure + fsc = imx327->cur_mode->vts_def * 2; + shs1 = IMX327_RHS1 - hdrae_exp->short_exp_reg - 1; + shs2 = fsc - hdrae_exp->long_exp_reg - 1; + if (shs1 < 2) + shs1 = 2; + else if (shs1 > (IMX327_RHS1 - 2)) + shs1 = IMX327_RHS1 - 2; + if (shs2 < (IMX327_RHS1 + 2)) + shs2 = IMX327_RHS1 + 2; + else if (shs2 > (fsc - 2)) + shs2 = fsc - 2; + + imx327_write_reg(imx327->client, 0x3020, + IMX327_REG_VALUE_08BIT, + IMX327_FETCH_LOW_BYTE_EXP(shs1)); + imx327_write_reg(imx327->client, 0x3021, + IMX327_REG_VALUE_08BIT, + IMX327_FETCH_MID_BYTE_EXP(shs1)); + imx327_write_reg(imx327->client, 0x3022, + IMX327_REG_VALUE_08BIT, + IMX327_FETCH_HIGH_BYTE_EXP(shs1)); + + imx327_write_reg(imx327->client, 0x3024, + IMX327_REG_VALUE_08BIT, + IMX327_FETCH_LOW_BYTE_EXP(shs2)); + imx327_write_reg(imx327->client, 0x3025, + IMX327_REG_VALUE_08BIT, + IMX327_FETCH_MID_BYTE_EXP(shs2)); + imx327_write_reg(imx327->client, 0x3026, + IMX327_REG_VALUE_08BIT, + IMX327_FETCH_HIGH_BYTE_EXP(shs2)); + + dev_info(&client->dev, + "set l_gain:0x%x s_gain:0x%x shs2:0x%x shs1:0x%x\n", + l_gain, s_gain, shs2, shs1); + break; + case RKMODULE_GET_MODULE_INFO: + imx327_get_module_inf(imx327, (struct rkmodule_inf *)arg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long imx327_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_awb_cfg *cfg; + long ret; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = imx327_ioctl(sd, cmd, inf); + if (!ret) + ret = copy_to_user(up, inf, sizeof(*inf)); + kfree(inf); + break; + case RKMODULE_AWB_CFG: + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(cfg, up, sizeof(*cfg)); + if (!ret) + ret = imx327_ioctl(sd, cmd, cfg); + kfree(cfg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + +static int __imx327_start_stream(struct imx327 *imx327) +{ + int ret; + + ret = imx327_write_array(imx327->client, imx327->cur_mode->reg_list); + if (ret) + return ret; + + /* In case these controls are set before streaming */ + mutex_unlock(&imx327->mutex); + ret = v4l2_ctrl_handler_setup(&imx327->ctrl_handler); + mutex_lock(&imx327->mutex); + if (ret) + return ret; + + return imx327_write_reg(imx327->client, IMX327_REG_CTRL_MODE, + IMX327_REG_VALUE_08BIT, 0); +} + +static int __imx327_stop_stream(struct imx327 *imx327) +{ + return imx327_write_reg(imx327->client, IMX327_REG_CTRL_MODE, + IMX327_REG_VALUE_08BIT, 1); +} + +static int imx327_s_stream(struct v4l2_subdev *sd, int on) +{ + struct imx327 *imx327 = to_imx327(sd); + struct i2c_client *client = imx327->client; + int ret = 0; + + mutex_lock(&imx327->mutex); + on = !!on; + if (on == imx327->streaming) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = __imx327_start_stream(imx327); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + __imx327_stop_stream(imx327); + pm_runtime_put(&client->dev); + } + + imx327->streaming = on; + +unlock_and_return: + mutex_unlock(&imx327->mutex); + + return ret; +} + +static int imx327_s_power(struct v4l2_subdev *sd, int on) +{ + struct imx327 *imx327 = to_imx327(sd); + struct i2c_client *client = imx327->client; + int ret = 0; + + mutex_lock(&imx327->mutex); + + /* If the power state is not modified - no work to do. */ + if (imx327->power_on == !!on) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + imx327->power_on = true; + } else { + pm_runtime_put(&client->dev); + imx327->power_on = false; + } + +unlock_and_return: + mutex_unlock(&imx327->mutex); + + return ret; +} + +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 imx327_cal_delay(u32 cycles) +{ + return DIV_ROUND_UP(cycles, IMX327_XVCLK_FREQ / 1000 / 1000); +} + +static int __imx327_power_on(struct imx327 *imx327) +{ + int ret; + u32 delay_us; + struct device *dev = &imx327->client->dev; + + ret = clk_set_rate(imx327->xvclk, IMX327_XVCLK_FREQ); + if (ret < 0) { + dev_err(dev, "Failed to set xvclk rate (24MHz)\n"); + return ret; + } + if (clk_get_rate(imx327->xvclk) != IMX327_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 37.125MHz\n"); + ret = clk_prepare_enable(imx327->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + + if (!IS_ERR(imx327->reset_gpio)) + gpiod_set_value_cansleep(imx327->reset_gpio, 0); + + ret = regulator_bulk_enable(IMX327_NUM_SUPPLIES, imx327->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + + if (!IS_ERR(imx327->reset_gpio)) + gpiod_set_value_cansleep(imx327->reset_gpio, 1); + + usleep_range(500, 1000); + if (!IS_ERR(imx327->pwdn_gpio)) + gpiod_set_value_cansleep(imx327->pwdn_gpio, 1); + + /* 8192 cycles prior to first SCCB transaction */ + delay_us = imx327_cal_delay(8192); + usleep_range(delay_us, delay_us * 2); + + return 0; + +disable_clk: + clk_disable_unprepare(imx327->xvclk); + + return ret; +} + +static void __imx327_power_off(struct imx327 *imx327) +{ + if (!IS_ERR(imx327->pwdn_gpio)) + gpiod_set_value_cansleep(imx327->pwdn_gpio, 0); + clk_disable_unprepare(imx327->xvclk); + if (!IS_ERR(imx327->reset_gpio)) + gpiod_set_value_cansleep(imx327->reset_gpio, 0); + regulator_bulk_disable(IMX327_NUM_SUPPLIES, imx327->supplies); +} + +static int imx327_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx327 *imx327 = to_imx327(sd); + + return __imx327_power_on(imx327); +} + +static int imx327_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx327 *imx327 = to_imx327(sd); + + __imx327_power_off(imx327); + + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int imx327_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct imx327 *imx327 = to_imx327(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + const struct imx327_mode *def_mode = &imx327->support_modes[0]; + + mutex_lock(&imx327->mutex); + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = def_mode->bus_fmt; + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&imx327->mutex); + /* No crop or compose */ + + return 0; +} +#endif + +static const struct dev_pm_ops imx327_pm_ops = { + SET_RUNTIME_PM_OPS(imx327_runtime_suspend, + imx327_runtime_resume, NULL) +}; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops imx327_internal_ops = { + .open = imx327_open, +}; +#endif + +static const struct v4l2_subdev_core_ops imx327_core_ops = { + .s_power = imx327_s_power, + .ioctl = imx327_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = imx327_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops imx327_video_ops = { + .s_stream = imx327_s_stream, + .g_frame_interval = imx327_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops imx327_pad_ops = { + .enum_mbus_code = imx327_enum_mbus_code, + .enum_frame_size = imx327_enum_frame_sizes, + .get_fmt = imx327_get_fmt, + .set_fmt = imx327_set_fmt, +}; + +static const struct v4l2_subdev_ops imx327_subdev_ops = { + .core = &imx327_core_ops, + .video = &imx327_video_ops, + .pad = &imx327_pad_ops, +}; + +static int imx327_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct imx327 *imx327 = container_of(ctrl->handler, + struct imx327, ctrl_handler); + struct i2c_client *client = imx327->client; + s64 max; + int ret = 0; + + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ + max = imx327->cur_mode->height + ctrl->val - 4; + __v4l2_ctrl_modify_range(imx327->exposure, + imx327->exposure->minimum, max, + imx327->exposure->step, + imx327->exposure->default_value); + break; + } + + if (pm_runtime_get(&client->dev) <= 0) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + /* 4 least significant bits of expsoure are fractional part */ + ret = imx327_write_reg(imx327->client, + IMX327_REG_EXPOSURE_H, + IMX327_REG_VALUE_08BIT, + IMX327_FETCH_HIGH_BYTE_EXP(ctrl->val)); + ret |= imx327_write_reg(imx327->client, + IMX327_REG_EXPOSURE_M, + IMX327_REG_VALUE_08BIT, + IMX327_FETCH_MID_BYTE_EXP(ctrl->val)); + ret |= imx327_write_reg(imx327->client, + IMX327_REG_EXPOSURE_L, + IMX327_REG_VALUE_08BIT, + IMX327_FETCH_LOW_BYTE_EXP(ctrl->val)); + break; + case V4L2_CID_ANALOGUE_GAIN: + ret = imx327_write_reg(imx327->client, + IMX327_REG_GAIN, + IMX327_REG_VALUE_08BIT, + ctrl->val); + break; + case V4L2_CID_VBLANK: + ret = imx327_write_reg(imx327->client, + IMX327_REG_VTS_H, + IMX327_REG_VALUE_08BIT, + IMX327_FETCH_HIGH_BYTE_VTS(ctrl->val + imx327->cur_mode->height)); + ret |= imx327_write_reg(imx327->client, + IMX327_REG_VTS_M, + IMX327_REG_VALUE_08BIT, + IMX327_FETCH_MID_BYTE_VTS(ctrl->val + imx327->cur_mode->height)); + ret |= imx327_write_reg(imx327->client, + IMX327_REG_VTS_L, + IMX327_REG_VALUE_08BIT, + IMX327_FETCH_LOW_BYTE_VTS(ctrl->val + imx327->cur_mode->height)); + break; + case V4L2_CID_TEST_PATTERN: + ret = imx327_enable_test_pattern(imx327, ctrl->val); + break; + default: + dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", + __func__, ctrl->id, ctrl->val); + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops imx327_ctrl_ops = { + .s_ctrl = imx327_set_ctrl, +}; + +static int imx327_initialize_controls(struct imx327 *imx327) +{ + const struct imx327_mode *mode; + struct v4l2_ctrl_handler *handler; + struct v4l2_ctrl *ctrl; + s64 exposure_max, vblank_def; + u32 h_blank; + int ret; + + handler = &imx327->ctrl_handler; + mode = imx327->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 8); + if (ret) + return ret; + handler->lock = &imx327->mutex; + + ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, + 0, 0, link_freq_menu_items); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + if (imx327->has_devnode) { + v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, + 0, IMX327_PIXEL_RATE, 1, IMX327_PIXEL_RATE); + } else { + v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, + 0, IMX327_PIXEL_HDR_RATE, + 1, IMX327_PIXEL_HDR_RATE); + } + + h_blank = mode->hts_def - mode->width; + imx327->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (imx327->hblank) + imx327->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = mode->vts_def - mode->height; + imx327->vblank = v4l2_ctrl_new_std(handler, &imx327_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + IMX327_VTS_MAX - mode->height, + 1, vblank_def); + + exposure_max = mode->vts_def - 4; + imx327->exposure = v4l2_ctrl_new_std(handler, &imx327_ctrl_ops, + V4L2_CID_EXPOSURE, IMX327_EXPOSURE_MIN, + exposure_max, IMX327_EXPOSURE_STEP, + mode->exp_def); + + imx327->anal_gain = v4l2_ctrl_new_std(handler, &imx327_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, IMX327_GAIN_MIN, + IMX327_GAIN_MAX, IMX327_GAIN_STEP, + IMX327_GAIN_DEFAULT); + + imx327->test_pattern = v4l2_ctrl_new_std_menu_items(handler, + &imx327_ctrl_ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(imx327_test_pattern_menu) - 1, + 0, 0, imx327_test_pattern_menu); + if (handler->error) { + ret = handler->error; + dev_err(&imx327->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + imx327->subdev.ctrl_handler = handler; + + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +static int imx327_check_sensor_id(struct imx327 *imx327, + struct i2c_client *client) +{ + struct device *dev = &imx327->client->dev; + u32 id = 0; + int ret; + + ret = imx327_read_reg(client, IMX327_REG_CHIP_ID, + IMX327_REG_VALUE_08BIT, &id); + if (id != CHIP_ID) { + dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret); + return -ENODEV; + } + + dev_info(dev, "Detected imx327 id:%06x\n", CHIP_ID); + + return 0; +} + +static int imx327_check_remote_dev(struct imx327 *imx327) +{ + struct device *dev = &imx327->client->dev; + struct device_node *parent = dev->of_node; + struct device_node *remote = NULL; + int ret = 0; + + imx327->has_devnode = true; + remote = of_graph_get_remote_node(parent, 0, 0); + if (!remote) { + dev_err(dev, "Invalid remote device!\n"); + return -EINVAL; + } + + if (strstr(of_node_full_name(remote), "spi_rk1608")) + imx327->has_devnode = false; + + of_node_put(remote); + + return ret; +} + +static int imx327_configure_regulators(struct imx327 *imx327) +{ + unsigned int i; + + for (i = 0; i < IMX327_NUM_SUPPLIES; i++) + imx327->supplies[i].supply = imx327_supply_names[i]; + + return devm_regulator_bulk_get(&imx327->client->dev, + IMX327_NUM_SUPPLIES, + imx327->supplies); +} + +static int imx327_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct imx327 *imx327; + struct v4l2_subdev *sd; + char facing[2]; + int ret; + + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + imx327 = devm_kzalloc(dev, sizeof(*imx327), GFP_KERNEL); + if (!imx327) + return -ENOMEM; + + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &imx327->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &imx327->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &imx327->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &imx327->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + + imx327->client = client; + imx327->cur_mode = &imx327->support_modes[0]; + + imx327->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(imx327->xvclk)) { + dev_err(dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + imx327->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(imx327->reset_gpio)) + dev_warn(dev, "Failed to get reset-gpios\n"); + + imx327->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(imx327->pwdn_gpio)) + dev_warn(dev, "Failed to get pwdn-gpios\n"); + + ret = imx327_configure_regulators(imx327); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + + mutex_init(&imx327->mutex); + + sd = &imx327->subdev; + v4l2_i2c_subdev_init(sd, client, &imx327_subdev_ops); + imx327_check_remote_dev(imx327); + if (imx327->has_devnode) { + imx327->support_modes = supported_linear_modes; + imx327->support_modes_num = ARRAY_SIZE(supported_linear_modes); + dev_err(dev, "linear mode"); + } else { + imx327->support_modes = supported_hdr_modes; + imx327->support_modes_num = ARRAY_SIZE(supported_hdr_modes); + dev_err(dev, "hdr mode"); + } + imx327->cur_mode = &imx327->support_modes[0]; + + ret = imx327_initialize_controls(imx327); + if (ret) + goto err_destroy_mutex; + + ret = __imx327_power_on(imx327); + if (ret) + goto err_free_handler; + + ret = imx327_check_sensor_id(imx327, client); + if (ret) + goto err_power_off; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &imx327_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + imx327->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &imx327->pad); + if (ret < 0) + goto err_power_off; +#endif + + memset(facing, 0, sizeof(facing)); + if (strcmp(imx327->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + imx327->module_index, facing, + IMX327_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor_common(sd); + if (ret) { + dev_err(dev, "v4l2 async register subdev failed\n"); + goto err_clean_entity; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; + +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif +err_power_off: + __imx327_power_off(imx327); +err_free_handler: + v4l2_ctrl_handler_free(&imx327->ctrl_handler); +err_destroy_mutex: + mutex_destroy(&imx327->mutex); + + return ret; +} + +static int imx327_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx327 *imx327 = to_imx327(sd); + + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + v4l2_ctrl_handler_free(&imx327->ctrl_handler); + mutex_destroy(&imx327->mutex); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __imx327_power_off(imx327); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id imx327_of_match[] = { + { .compatible = "sony,imx327" }, + {}, +}; +MODULE_DEVICE_TABLE(of, imx327_of_match); +#endif + +static const struct i2c_device_id imx327_match_id[] = { + { "sony,imx327", 0 }, + { }, +}; + +static struct i2c_driver imx327_i2c_driver = { + .driver = { + .name = IMX327_NAME, + .pm = &imx327_pm_ops, + .of_match_table = of_match_ptr(imx327_of_match), + }, + .probe = &imx327_probe, + .remove = &imx327_remove, + .id_table = imx327_match_id, +}; + +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&imx327_i2c_driver); +} + +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&imx327_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_DESCRIPTION("Sony imx327 sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/jx_h65.c b/drivers/media/i2c/jx_h65.c new file mode 100644 index 000000000000..afc9d3c41416 --- /dev/null +++ b/drivers/media/i2c/jx_h65.c @@ -0,0 +1,1246 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * jx_h65 driver + * + * Copyright (C) 2019 Fuzhou Rockchip Electronics Co., Ltd. + * + * V0.0X01.0X01 add poweron function. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x01) + +#ifndef V4L2_CID_DIGITAL_GAIN +#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN +#endif + +#define JX_H65_XVCLK_FREQ 24000000 + +#define CHIP_ID_H 0x0A +#define CHIP_ID_L 0x65 +#define JX_H65_PIDH_ADDR 0x0a +#define JX_H65_PIDL_ADDR 0x0b + +#define JX_H65_REG_CTRL_MODE 0x12 +#define JX_H65_MODE_SW_STANDBY 0x40 +#define JX_H65_MODE_STREAMING 0x00 + +#define JX_H65_AEC_PK_LONG_EXPO_HIGH_REG 0x02 /* Exposure Bits 8-15 */ +#define JX_H65_AEC_PK_LONG_EXPO_LOW_REG 0x01 /* Exposure Bits 0-7 */ +#define JX_H65_FETCH_HIGH_BYTE_EXP(VAL) (((VAL) >> 8) & 0xFF) /* 8-15 Bits */ +#define JX_H65_FETCH_LOW_BYTE_EXP(VAL) ((VAL) & 0xFF) /* 0-7 Bits */ +#define JX_H65_EXPOSURE_MIN 4 +#define JX_H65_EXPOSURE_STEP 1 +#define JX_H65_VTS_MAX 0xffff + +#define JX_H65_AEC_PK_LONG_GAIN_REG 0x00 /* Bits 0 -7 */ +#define ANALOG_GAIN_MIN 0x00 +#define ANALOG_GAIN_MAX 0x7f +#define ANALOG_GAIN_STEP 1 +#define ANALOG_GAIN_DEFAULT 0x0 + +#define JX_H65_DIGI_GAIN_L_MASK 0x3f +#define JX_H65_DIGI_GAIN_H_SHIFT 6 +#define JX_H65_DIGI_GAIN_MIN 0 +#define JX_H65_DIGI_GAIN_MAX (0x4000 - 1) +#define JX_H65_DIGI_GAIN_STEP 1 +#define JX_H65_DIGI_GAIN_DEFAULT 1024 + +#define JX_H65_REG_TEST_PATTERN 0x0c +#define JX_H65_TEST_PATTERN_ENABLE 0x80 +#define JX_H65_TEST_PATTERN_DISABLE 0x0 + +#define JX_H65_REG_HIGH_VTS 0x23 +#define JX_H65_REG_LOW_VTS 0X22 +#define JX_H65_FETCH_HIGH_BYTE_VTS(VAL) (((VAL) >> 8) & 0xFF) /* 8-15 Bits */ +#define JX_H65_FETCH_LOW_BYTE_VTS(VAL) ((VAL) & 0xFF) /* 0-7 Bits */ + +#define REG_NULL 0xFF +#define REG_DELAY 0xFE + +#define JX_H65_NAME "jx_h65" + +static const char * const jx_h65_supply_names[] = { + "vcc2v8_dvp", /* Analog power */ + "vcc1v8_dvp", /* Digital I/O power */ + "vdd1v5_dvp", /* Digital core power */ +}; + +#define JX_H65_NUM_SUPPLIES ARRAY_SIZE(jx_h65_supply_names) + +struct regval { + u16 addr; + u8 val; +}; + +struct jx_h65_mode { + u32 width; + u32 height; + u32 max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + const struct regval *reg_list; +}; + +struct jx_h65 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; + struct regulator_bulk_data supplies[JX_H65_NUM_SUPPLIES]; + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *anal_gain; + struct v4l2_ctrl *digi_gain; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *test_pattern; + struct mutex mutex; + bool streaming; + bool power_on; + const struct jx_h65_mode *cur_mode; + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; +}; + +#define to_jx_h65(sd) container_of(sd, struct jx_h65, subdev) + +/* + * Xclk 24Mhz + * Pclk 45Mhz + * linelength 672(0x2a0) + * framelength 2232(0x8b8) + * grabwindow_width 1280 + * grabwindow_height 720 + * max_framerate 30fps + * mipi_datarate per lane 216Mbps + */ + +static const struct regval jx_h65_1280x720_regs[] = { + {0x12, 0x40}, + {0x0E, 0x11}, + {0x0F, 0x04}, + {0x10, 0x24}, + {0x11, 0x80}, + {0x5F, 0x01}, + {0x60, 0x10}, + {0x19, 0x64}, + {0x48, 0x25}, + {0x20, 0xD0}, + {0x21, 0x02}, + {0x22, 0xE8}, + {0x23, 0x03}, + {0x24, 0x80}, + {0x25, 0xD0}, + {0x26, 0x22}, + {0x27, 0x5C}, + {0x28, 0x1A}, + {0x29, 0x01}, + {0x2A, 0x48}, + {0x2B, 0x25}, + {0x2C, 0x00}, + {0x2D, 0x1F}, + {0x2E, 0xF9}, + {0x2F, 0x40}, + {0x41, 0x90}, + {0x42, 0x12}, + {0x39, 0x90}, + {0x1D, 0x00}, + {0x1E, 0x04}, + {0x6C, 0x40}, + {0x70, 0x89}, + {0x71, 0x8A}, + {0x72, 0x68}, + {0x73, 0x33}, + {0x74, 0x52}, + {0x75, 0x2B}, + {0x76, 0x40}, + {0x77, 0x06}, + {0x78, 0x0E}, + {0x6E, 0x2C}, + {0x1F, 0x10}, + {0x31, 0x0C}, + {0x32, 0x20}, + {0x33, 0x0C}, + {0x34, 0x4F}, + {0x36, 0x06}, + {0x38, 0x39}, + {0x3A, 0x08}, + {0x3B, 0x50}, + {0x3C, 0xA0}, + {0x3D, 0x00}, + {0x3E, 0x01}, + {0x3F, 0x00}, + {0x40, 0x00}, + {0x0D, 0x50}, + {0x5A, 0x43}, + {0x5B, 0xB3}, + {0x5C, 0x0C}, + {0x5D, 0x7E}, + {0x5E, 0x24}, + {0x62, 0x40}, + {0x67, 0x48}, + {0x6A, 0x11}, + {0x68, 0x00}, + {0x8F, 0x9F}, + {0x0C, 0x00}, + {0x59, 0x97}, + {0x4A, 0x05}, + {0x50, 0x03}, + {0x47, 0x62}, + {0x7E, 0xCD}, + {0x8D, 0x87}, + {0x49, 0x10}, + {0x7F, 0x52}, + {0x8E, 0x00}, + {0x8C, 0xFF}, + {0x8B, 0x01}, + {0x57, 0x02}, + {0x94, 0x00}, + {0x95, 0x00}, + {0x63, 0x80}, + {0x7B, 0x46}, + {0x7C, 0x2D}, + {0x90, 0x00}, + {0x79, 0x00}, + {0x13, 0x81}, + {0x45, 0x89}, + {0x93, 0x68}, + {REG_DELAY, 0x00}, + {0x45, 0x19}, + {0x1F, 0x11}, + {0x17, 0x00}, + {0x16, 0x77}, + {REG_NULL, 0x00} +}; + +static const struct regval jx_h65_1280x960_regs[] = { + {0x12, 0x40}, + {0x0E, 0x11}, + {0x0F, 0x04}, + {0x10, 0x24}, + {0x11, 0x80}, + {0x5F, 0x01}, + {0x60, 0x10}, + {0x19, 0x64}, + {0x48, 0x25}, + {0x20, 0xD0}, + {0x21, 0x02}, + {0x22, 0xE8}, + {0x23, 0x03}, + {0x24, 0x80}, + {0x25, 0xC0}, + {0x26, 0x32}, + {0x27, 0x5C}, + {0x28, 0x1C}, + {0x29, 0x01}, + {0x2A, 0x48}, + {0x2B, 0x25}, + {0x2C, 0x00}, + {0x2D, 0x00}, + {0x2E, 0xF9}, + {0x2F, 0x40}, + {0x41, 0x90}, + {0x42, 0x12}, + {0x39, 0x90}, + {0x1D, 0x00}, + {0x1E, 0x04}, + {0x6C, 0x40}, + {0x70, 0x89}, + {0x71, 0x8A}, + {0x72, 0x68}, + {0x73, 0x33}, + {0x74, 0x52}, + {0x75, 0x2B}, + {0x76, 0x40}, + {0x77, 0x06}, + {0x78, 0x0E}, + {0x6E, 0x2C}, + {0x1F, 0x10}, + {0x31, 0x0C}, + {0x32, 0x20}, + {0x33, 0x0C}, + {0x34, 0x4F}, + {0x36, 0x06}, + {0x38, 0x39}, + {0x3A, 0x08}, + {0x3B, 0x50}, + {0x3C, 0xA0}, + {0x3D, 0x00}, + {0x3E, 0x01}, + {0x3F, 0x00}, + {0x40, 0x00}, + {0x0D, 0x50}, + {0x5A, 0x43}, + {0x5B, 0xB3}, + {0x5C, 0x0C}, + {0x5D, 0x7E}, + {0x5E, 0x24}, + {0x62, 0x40}, + {0x67, 0x48}, + {0x6A, 0x11}, + {0x68, 0x00}, + {0x8F, 0x9F}, + {0x0C, 0x00}, + {0x59, 0x97}, + {0x4A, 0x05}, + {0x50, 0x03}, + {0x47, 0x62}, + {0x7E, 0xCD}, + {0x8D, 0x87}, + {0x49, 0x10}, + {0x7F, 0x52}, + {0x8E, 0x00}, + {0x8C, 0xFF}, + {0x8B, 0x01}, + {0x57, 0x02}, + {0x94, 0x00}, + {0x95, 0x00}, + {0x63, 0x80}, + {0x7B, 0x46}, + {0x7C, 0x2D}, + {0x90, 0x00}, + {0x79, 0x00}, + {0x13, 0x81}, + {0x45, 0x89}, + {0x93, 0x68}, + {REG_DELAY, 0x00}, + {0x45, 0x19}, + {0x1F, 0x11}, + {REG_NULL, 0x00} +}; + +static const struct jx_h65_mode supported_modes[] = { + { + .width = 1280, + .height = 960, + .max_fps = 30, + .exp_def = 0x0384, + .hts_def = 0x02d0, + .vts_def = 0x03e8, + .reg_list = jx_h65_1280x960_regs, + }, + { + .width = 1280, + .height = 720, + .max_fps = 30, + .exp_def = 0x0384, + .hts_def = 0x02d0, + .vts_def = 0x03e8, + .reg_list = jx_h65_1280x720_regs, + } +}; + +#define JX_H65_LINK_FREQ_420MHZ 216000000 +#define JX_H65_PIXEL_RATE (JX_H65_LINK_FREQ_420MHZ * 2 * 1 / 10) +static const s64 link_freq_menu_items[] = { + JX_H65_LINK_FREQ_420MHZ +}; + +static const char * const jx_h65_test_pattern_menu[] = { + "Disabled", + "Vertical Color Bar Type 1", + "Vertical Color Bar Type 2", + "Vertical Color Bar Type 3", + "Vertical Color Bar Type 4" +}; + +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 jx_h65_cal_delay(u32 cycles) +{ + return DIV_ROUND_UP(cycles, JX_H65_XVCLK_FREQ / 1000 / 1000); +} + +static int jx_h65_write_reg(struct i2c_client *client, u8 reg, u8 val) +{ + struct i2c_msg msg; + u8 buf[2]; + int ret; + + buf[0] = reg & 0xFF; + buf[1] = val; + + msg.addr = client->addr; + msg.flags = client->flags; + msg.buf = buf; + msg.len = sizeof(buf); + + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret >= 0) + return 0; + + dev_err(&client->dev, + "jx_h65 write reg(0x%x val:0x%x) failed !\n", reg, val); + + return ret; +} + +static int jx_h65_write_array(struct i2c_client *client, + const struct regval *regs) +{ + u32 i, delay_us; + int ret = 0; + + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) { + if (regs[i].addr == REG_DELAY) { + delay_us = jx_h65_cal_delay(500 * 1000); + usleep_range(delay_us, delay_us * 2); + } else { + ret = jx_h65_write_reg(client, + regs[i].addr, regs[i].val); + } + } + + return ret; +} + +static int jx_h65_read_reg(struct i2c_client *client, u8 reg, u8 *val) +{ + struct i2c_msg msg[2]; + u8 buf[1]; + int ret; + + buf[0] = reg & 0xFF; + + msg[0].addr = client->addr; + msg[0].flags = client->flags; + msg[0].buf = buf; + msg[0].len = sizeof(buf); + + msg[1].addr = client->addr; + msg[1].flags = client->flags | I2C_M_RD; + msg[1].buf = buf; + msg[1].len = 1; + + ret = i2c_transfer(client->adapter, msg, 2); + if (ret >= 0) { + *val = buf[0]; + return 0; + } + + dev_err(&client->dev, + "jx_h65 read reg:0x%x failed !\n", reg); + + return ret; +} + +static int jx_h65_get_reso_dist(const struct jx_h65_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); +} + +static const struct jx_h65_mode * +jx_h65_find_best_fit(struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *framefmt = &fmt->format; + int dist; + int cur_best_fit = 0; + int cur_best_fit_dist = -1; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { + dist = jx_h65_get_reso_dist(&supported_modes[i], framefmt); + if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { + cur_best_fit_dist = dist; + cur_best_fit = i; + } + } + + return &supported_modes[cur_best_fit]; +} + +static int jx_h65_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct jx_h65 *jx_h65 = to_jx_h65(sd); + const struct jx_h65_mode *mode; + s64 h_blank, vblank_def; + + mutex_lock(&jx_h65->mutex); + + mode = jx_h65_find_best_fit(fmt); + fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10; + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; +#else + mutex_unlock(&jx_h65->mutex); + return -ENOTTY; +#endif + } else { + jx_h65->cur_mode = mode; + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(jx_h65->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(jx_h65->vblank, vblank_def, + JX_H65_VTS_MAX - mode->height, + 1, vblank_def); + } + + mutex_unlock(&jx_h65->mutex); + + return 0; +} + +static int jx_h65_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct jx_h65 *jx_h65 = to_jx_h65(sd); + const struct jx_h65_mode *mode = jx_h65->cur_mode; + + mutex_lock(&jx_h65->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); +#else + mutex_unlock(&jx_h65->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10; + fmt->format.field = V4L2_FIELD_NONE; + } + mutex_unlock(&jx_h65->mutex); + + return 0; +} + +static int jx_h65_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index != 0) + return -EINVAL; + code->code = MEDIA_BUS_FMT_SBGGR10_1X10; + + return 0; +} + +static int jx_h65_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fse->code != MEDIA_BUS_FMT_SBGGR10_1X10) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = supported_modes[fse->index].width; + fse->max_height = supported_modes[fse->index].height; + fse->min_height = supported_modes[fse->index].height; + + return 0; +} + +static int jx_h65_enable_test_pattern(struct jx_h65 *jx_h65, u32 pattern) +{ + u32 val; + + if (pattern) + val = (pattern - 1) | JX_H65_TEST_PATTERN_ENABLE; + else + val = JX_H65_TEST_PATTERN_DISABLE; + + return jx_h65_write_reg(jx_h65->client, JX_H65_REG_TEST_PATTERN, val); +} + +static void jx_h65_get_module_inf(struct jx_h65 *jx_h65, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strlcpy(inf->base.sensor, JX_H65_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, jx_h65->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, jx_h65->len_name, sizeof(inf->base.lens)); +} + +static long jx_h65_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct jx_h65 *jx_h65 = to_jx_h65(sd); + long ret = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + jx_h65_get_module_inf(jx_h65, (struct rkmodule_inf *)arg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long jx_h65_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_awb_cfg *cfg; + long ret; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = jx_h65_ioctl(sd, cmd, inf); + if (!ret) + ret = copy_to_user(up, inf, sizeof(*inf)); + kfree(inf); + break; + case RKMODULE_AWB_CFG: + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(cfg, up, sizeof(*cfg)); + if (!ret) + ret = jx_h65_ioctl(sd, cmd, cfg); + kfree(cfg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + +static int jx_h65_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct jx_h65 *jx_h65 = to_jx_h65(sd); + const struct jx_h65_mode *mode = jx_h65->cur_mode; + + mutex_lock(&jx_h65->mutex); + fi->interval.numerator = 10000; + fi->interval.denominator = mode->max_fps * 10000; + mutex_unlock(&jx_h65->mutex); + + return 0; +} + +static int __jx_h65_start_stream(struct jx_h65 *jx_h65) +{ + return jx_h65_write_reg(jx_h65->client, JX_H65_REG_CTRL_MODE, + JX_H65_MODE_STREAMING); +} + +static int __jx_h65_stop_stream(struct jx_h65 *jx_h65) +{ + return jx_h65_write_reg(jx_h65->client, JX_H65_REG_CTRL_MODE, + JX_H65_MODE_SW_STANDBY); +} + +static int jx_h65_s_stream(struct v4l2_subdev *sd, int on) +{ + struct jx_h65 *jx_h65 = to_jx_h65(sd); + struct i2c_client *client = jx_h65->client; + int ret = 0; + + mutex_lock(&jx_h65->mutex); + on = !!on; + if (on == jx_h65->streaming) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = __jx_h65_start_stream(jx_h65); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + __jx_h65_stop_stream(jx_h65); + pm_runtime_put(&client->dev); + } + + jx_h65->streaming = on; + +unlock_and_return: + mutex_unlock(&jx_h65->mutex); + + return ret; +} + +static int jx_h65_s_power(struct v4l2_subdev *sd, int on) +{ + struct jx_h65 *jx_h65 = to_jx_h65(sd); + struct i2c_client *client = jx_h65->client; + int ret = 0; + + mutex_lock(&jx_h65->mutex); + + /* If the power state is not modified - no work to do. */ + if (jx_h65->power_on == !!on) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = jx_h65_write_array(jx_h65->client, + jx_h65->cur_mode->reg_list); + if (ret) + goto unlock_and_return; + + mutex_unlock(&jx_h65->mutex); + /* In case these controls are set before streaming */ + ret = v4l2_ctrl_handler_setup(&jx_h65->ctrl_handler); + if (ret) + return ret; + mutex_lock(&jx_h65->mutex); + + jx_h65->power_on = true; + } else { + pm_runtime_put(&client->dev); + jx_h65->power_on = false; + } + +unlock_and_return: + mutex_unlock(&jx_h65->mutex); + + return ret; +} + +static int __jx_h65_power_on(struct jx_h65 *jx_h65) +{ + int ret; + u32 delay_us; + struct device *dev = &jx_h65->client->dev; + + ret = clk_set_rate(jx_h65->xvclk, JX_H65_XVCLK_FREQ); + if (ret < 0) { + dev_err(dev, "Failed to set xvclk rate (24MHz)\n"); + return ret; + } + if (clk_get_rate(jx_h65->xvclk) != JX_H65_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); + ret = clk_prepare_enable(jx_h65->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + + if (!IS_ERR(jx_h65->reset_gpio)) + gpiod_set_value_cansleep(jx_h65->reset_gpio, 1); + + ret = regulator_bulk_enable(JX_H65_NUM_SUPPLIES, jx_h65->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + + /* According to datasheet, at least 10ms for reset duration */ + usleep_range(10 * 1000, 15 * 1000); + + if (!IS_ERR(jx_h65->reset_gpio)) + gpiod_set_value_cansleep(jx_h65->reset_gpio, 0); + + if (!IS_ERR(jx_h65->pwdn_gpio)) + gpiod_set_value_cansleep(jx_h65->pwdn_gpio, 0); + + /* 8192 cycles prior to first SCCB transaction */ + delay_us = jx_h65_cal_delay(8192); + usleep_range(delay_us, delay_us * 2); + + return 0; + +disable_clk: + clk_disable_unprepare(jx_h65->xvclk); + + return ret; +} + +static void __jx_h65_power_off(struct jx_h65 *jx_h65) +{ + if (!IS_ERR(jx_h65->pwdn_gpio)) + gpiod_set_value_cansleep(jx_h65->pwdn_gpio, 1); + clk_disable_unprepare(jx_h65->xvclk); + if (!IS_ERR(jx_h65->reset_gpio)) + gpiod_set_value_cansleep(jx_h65->reset_gpio, 1); + regulator_bulk_disable(JX_H65_NUM_SUPPLIES, jx_h65->supplies); +} + +static int jx_h65_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct jx_h65 *jx_h65 = to_jx_h65(sd); + + return __jx_h65_power_on(jx_h65); +} + +static int jx_h65_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct jx_h65 *jx_h65 = to_jx_h65(sd); + + __jx_h65_power_off(jx_h65); + + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int jx_h65_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct jx_h65 *jx_h65 = to_jx_h65(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + const struct jx_h65_mode *def_mode = &supported_modes[0]; + + mutex_lock(&jx_h65->mutex); + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10; + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&jx_h65->mutex); + /* No crop or compose */ + + return 0; +} +#endif + +static const struct dev_pm_ops jx_h65_pm_ops = { + SET_RUNTIME_PM_OPS(jx_h65_runtime_suspend, + jx_h65_runtime_resume, NULL) +}; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops jx_h65_internal_ops = { + .open = jx_h65_open, +}; +#endif + +static const struct v4l2_subdev_core_ops jx_h65_core_ops = { + .s_power = jx_h65_s_power, + .ioctl = jx_h65_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = jx_h65_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops jx_h65_video_ops = { + .s_stream = jx_h65_s_stream, + .g_frame_interval = jx_h65_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops jx_h65_pad_ops = { + .enum_mbus_code = jx_h65_enum_mbus_code, + .enum_frame_size = jx_h65_enum_frame_sizes, + .get_fmt = jx_h65_get_fmt, + .set_fmt = jx_h65_set_fmt, +}; + +static const struct v4l2_subdev_ops jx_h65_subdev_ops = { + .core = &jx_h65_core_ops, + .video = &jx_h65_video_ops, + .pad = &jx_h65_pad_ops, +}; + +static int jx_h65_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct jx_h65 *jx_h65 = container_of(ctrl->handler, + struct jx_h65, ctrl_handler); + struct i2c_client *client = jx_h65->client; + s64 max; + int ret = 0; + + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ + max = jx_h65->cur_mode->height + ctrl->val; + __v4l2_ctrl_modify_range(jx_h65->exposure, + jx_h65->exposure->minimum, max, + jx_h65->exposure->step, + jx_h65->exposure->default_value); + break; + } + + if (pm_runtime_get(&client->dev) <= 0) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + dev_dbg(&client->dev, "set expo: val: %d\n", ctrl->val); + /* 4 least significant bits of expsoure are fractional part */ + ret = jx_h65_write_reg(jx_h65->client, + JX_H65_AEC_PK_LONG_EXPO_HIGH_REG, + JX_H65_FETCH_HIGH_BYTE_EXP(ctrl->val)); + ret |= jx_h65_write_reg(jx_h65->client, + JX_H65_AEC_PK_LONG_EXPO_LOW_REG, + JX_H65_FETCH_LOW_BYTE_EXP(ctrl->val)); + break; + case V4L2_CID_ANALOGUE_GAIN: + dev_dbg(&client->dev, "set a-gain: val: %d\n", ctrl->val); + ret |= jx_h65_write_reg(jx_h65->client, + JX_H65_AEC_PK_LONG_GAIN_REG, ctrl->val); + break; + case V4L2_CID_DIGITAL_GAIN: + break; + case V4L2_CID_VBLANK: + dev_dbg(&client->dev, "set vblank: val: %d\n", ctrl->val); + ret |= jx_h65_write_reg(jx_h65->client, JX_H65_REG_HIGH_VTS, + JX_H65_FETCH_HIGH_BYTE_VTS((ctrl->val + jx_h65->cur_mode->height))); + ret |= jx_h65_write_reg(jx_h65->client, JX_H65_REG_LOW_VTS, + JX_H65_FETCH_LOW_BYTE_VTS((ctrl->val + jx_h65->cur_mode->height))); + break; + case V4L2_CID_TEST_PATTERN: + ret = jx_h65_enable_test_pattern(jx_h65, ctrl->val); + break; + default: + dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", + __func__, ctrl->id, ctrl->val); + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops jx_h65_ctrl_ops = { + .s_ctrl = jx_h65_set_ctrl, +}; + +static int jx_h65_initialize_controls(struct jx_h65 *jx_h65) +{ + const struct jx_h65_mode *mode; + struct v4l2_ctrl_handler *handler; + struct v4l2_ctrl *ctrl; + s64 exposure_max, vblank_def; + u32 h_blank; + int ret; + + handler = &jx_h65->ctrl_handler; + mode = jx_h65->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 8); + if (ret) + return ret; + handler->lock = &jx_h65->mutex; + + ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, + 0, 0, link_freq_menu_items); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, + 0, JX_H65_PIXEL_RATE, 1, JX_H65_PIXEL_RATE); + + h_blank = mode->hts_def - mode->width; + jx_h65->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (jx_h65->hblank) + jx_h65->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = mode->vts_def - mode->height; + jx_h65->vblank = v4l2_ctrl_new_std(handler, &jx_h65_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + JX_H65_VTS_MAX - mode->height, + 1, vblank_def); + + exposure_max = mode->vts_def; + jx_h65->exposure = v4l2_ctrl_new_std(handler, &jx_h65_ctrl_ops, + V4L2_CID_EXPOSURE, JX_H65_EXPOSURE_MIN, + exposure_max, JX_H65_EXPOSURE_STEP, + mode->exp_def); + + jx_h65->anal_gain = v4l2_ctrl_new_std(handler, &jx_h65_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, ANALOG_GAIN_MIN, + ANALOG_GAIN_MAX, ANALOG_GAIN_STEP, + ANALOG_GAIN_DEFAULT); + + /* Digital gain */ + jx_h65->digi_gain = v4l2_ctrl_new_std(handler, &jx_h65_ctrl_ops, + V4L2_CID_DIGITAL_GAIN, JX_H65_DIGI_GAIN_MIN, + JX_H65_DIGI_GAIN_MAX, JX_H65_DIGI_GAIN_STEP, + JX_H65_DIGI_GAIN_DEFAULT); + + jx_h65->test_pattern = v4l2_ctrl_new_std_menu_items(handler, + &jx_h65_ctrl_ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(jx_h65_test_pattern_menu) - 1, + 0, 0, jx_h65_test_pattern_menu); + + if (handler->error) { + ret = handler->error; + dev_err(&jx_h65->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + jx_h65->subdev.ctrl_handler = handler; + + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +static int jx_h65_check_sensor_id(struct jx_h65 *jx_h65, + struct i2c_client *client) +{ + struct device *dev = &jx_h65->client->dev; + u8 id_h = 0; + u8 id_l = 0; + int ret; + + ret = jx_h65_read_reg(client, JX_H65_PIDH_ADDR, &id_h); + ret |= jx_h65_read_reg(client, JX_H65_PIDL_ADDR, &id_l); + if (id_h != CHIP_ID_H && id_l != CHIP_ID_L) { + dev_err(dev, "Wrong camera sensor id(0x%02x%02x)\n", + id_h, id_l); + return -EINVAL; + } + + dev_info(dev, "Detected jx_h65 (0x%02x%02x) sensor\n", + id_h, id_l); + + return ret; +} + +static int jx_h65_configure_regulators(struct jx_h65 *jx_h65) +{ + unsigned int i; + + for (i = 0; i < JX_H65_NUM_SUPPLIES; i++) + jx_h65->supplies[i].supply = jx_h65_supply_names[i]; + + return devm_regulator_bulk_get(&jx_h65->client->dev, + JX_H65_NUM_SUPPLIES, + jx_h65->supplies); +} + +static int jx_h65_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct jx_h65 *jx_h65; + struct v4l2_subdev *sd; + char facing[2]; + int ret; + + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + jx_h65 = devm_kzalloc(dev, sizeof(*jx_h65), GFP_KERNEL); + if (!jx_h65) + return -ENOMEM; + + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &jx_h65->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &jx_h65->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &jx_h65->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &jx_h65->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + + jx_h65->client = client; + jx_h65->cur_mode = &supported_modes[0]; + + jx_h65->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(jx_h65->xvclk)) { + dev_err(dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + jx_h65->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(jx_h65->reset_gpio)) + dev_warn(dev, "Failed to get reset-gpios\n"); + + jx_h65->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(jx_h65->pwdn_gpio)) + dev_warn(dev, "Failed to get pwdn-gpios\n"); + + ret = jx_h65_configure_regulators(jx_h65); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + + mutex_init(&jx_h65->mutex); + + sd = &jx_h65->subdev; + v4l2_i2c_subdev_init(sd, client, &jx_h65_subdev_ops); + ret = jx_h65_initialize_controls(jx_h65); + if (ret) + goto err_destroy_mutex; + + ret = __jx_h65_power_on(jx_h65); + if (ret) + goto err_free_handler; + + ret = jx_h65_check_sensor_id(jx_h65, client); + if (ret) + goto err_power_off; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &jx_h65_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + jx_h65->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &jx_h65->pad); + if (ret < 0) + goto err_power_off; +#endif + + memset(facing, 0, sizeof(facing)); + if (strcmp(jx_h65->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + jx_h65->module_index, facing, + JX_H65_NAME, dev_name(sd->dev)); + + ret = v4l2_async_register_subdev(sd); + if (ret) { + dev_err(dev, "v4l2 async register subdev failed\n"); + goto err_clean_entity; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; + +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif +err_power_off: + __jx_h65_power_off(jx_h65); +err_free_handler: + v4l2_ctrl_handler_free(&jx_h65->ctrl_handler); +err_destroy_mutex: + mutex_destroy(&jx_h65->mutex); + + return ret; +} + +static int jx_h65_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct jx_h65 *jx_h65 = to_jx_h65(sd); + + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + v4l2_ctrl_handler_free(&jx_h65->ctrl_handler); + mutex_destroy(&jx_h65->mutex); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __jx_h65_power_off(jx_h65); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id jx_h65_of_match[] = { + { .compatible = "soi,jx_h65" }, + {}, +}; +MODULE_DEVICE_TABLE(of, jx_h65_of_match); +#endif + +static const struct i2c_device_id jx_h65_match_id[] = { + { "soi,jx_h65", 0 }, + { }, +}; + +static struct i2c_driver jx_h65_i2c_driver = { + .driver = { + .name = JX_H65_NAME, + .pm = &jx_h65_pm_ops, + .of_match_table = of_match_ptr(jx_h65_of_match), + }, + .probe = &jx_h65_probe, + .remove = &jx_h65_remove, + .id_table = jx_h65_match_id, +}; + +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&jx_h65_i2c_driver); +} + +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&jx_h65_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_DESCRIPTION("SOI jx_h65 sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ov2680.c b/drivers/media/i2c/ov2680.c index f753a1c333ef..1c1b6e568c36 100644 --- a/drivers/media/i2c/ov2680.c +++ b/drivers/media/i2c/ov2680.c @@ -1,900 +1,909 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Omnivision OV2680 CMOS Image Sensor driver - * - * Copyright (C) 2018 Linaro Ltd - * - * Based on OV5640 Sensor Driver - * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved. - * Copyright (C) 2014-2017 Mentor Graphics Inc. + * ov2680 driver * + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd. + * V0.0X01.0X02 fix mclk issue when probe multiple camera. */ -#include #include +#include #include -#include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include #include - +#include +#include +#include +#include +#include #include #include +#include +#include +#include +#include +#include #include -#define OV2680_XVCLK_VALUE 24000000 +#include -#define OV2680_CHIP_ID 0x2680 +/* verify default register values */ +//#define CHECK_REG_VALUE -#define OV2680_REG_STREAM_CTRL 0x0100 -#define OV2680_REG_SOFT_RESET 0x0103 +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x2) -#define OV2680_REG_CHIP_ID_HIGH 0x300a -#define OV2680_REG_CHIP_ID_LOW 0x300b +#ifndef V4L2_CID_DIGITAL_GAIN +#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN +#endif -#define OV2680_REG_R_MANUAL 0x3503 -#define OV2680_REG_GAIN_PK 0x350a -#define OV2680_REG_EXPOSURE_PK_HIGH 0x3500 -#define OV2680_REG_TIMING_HTS 0x380c -#define OV2680_REG_TIMING_VTS 0x380e -#define OV2680_REG_FORMAT1 0x3820 -#define OV2680_REG_FORMAT2 0x3821 +/* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ +#define MIPI_FREQ 330000000U +#define OV2680_PIXEL_RATE (330000000LL * 1LL * 2LL / 10) +#define OV2680_XVCLK_FREQ 24000000 -#define OV2680_REG_ISP_CTRL00 0x5080 +#define CHIP_ID 0x2680 +#define OV2680_REG_CHIP_ID 0x300a -#define OV2680_FRAME_RATE 30 +#define OV2680_REG_CTRL_MODE 0x0100 +#define OV2680_MODE_SW_STANDBY 0x00 +#define OV2680_MODE_STREAMING 0x01 -#define OV2680_REG_VALUE_8BIT 1 +#define OV2680_REG_EXPOSURE 0x3500 +#define OV2680_EXPOSURE_MIN 4 +#define OV2680_EXPOSURE_STEP 1 +#define OV2680_VTS_MAX 0x7fff + +#define OV2680_REG_ANALOG_GAIN 0x350a +#define ANALOG_GAIN_MIN 0x10 +#define ANALOG_GAIN_MAX 0xf8 +#define ANALOG_GAIN_STEP 1 +#define ANALOG_GAIN_DEFAULT 0xf8 +#define OV2680_REG_GAIN_H 0x350a +#define OV2680_REG_GAIN_L 0x350b +#define OV2680_GAIN_L_MASK 0xff +#define OV2680_GAIN_H_MASK 0x07 +#define OV2680_DIGI_GAIN_H_SHIFT 8 + +#define OV2680_DIGI_GAIN_MIN 0 +#define OV2680_DIGI_GAIN_MAX (0x4000 - 1) +#define OV2680_DIGI_GAIN_STEP 1 +#define OV2680_DIGI_GAIN_DEFAULT 1024 + +#define OV2680_REG_TEST_PATTERN 0x5080 +#define OV2680_TEST_PATTERN_ENABLE 0x80 +#define OV2680_TEST_PATTERN_DISABLE 0x0 + +#define OV2680_REG_VTS 0x380e + +#define REG_NULL 0xFFFF + +#define OV2680_REG_VALUE_08BIT 1 #define OV2680_REG_VALUE_16BIT 2 #define OV2680_REG_VALUE_24BIT 3 -#define OV2680_WIDTH_MAX 1600 -#define OV2680_HEIGHT_MAX 1200 +#define OV2680_LANES 1 +#define OV2680_BITS_PER_SAMPLE 10 -enum ov2680_mode_id { - OV2680_MODE_QUXGA_800_600, - OV2680_MODE_720P_1280_720, - OV2680_MODE_UXGA_1600_1200, - OV2680_MODE_MAX, +#define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default" +#define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep" + +#define OV2680_NAME "ov2680" + +static const char * const ov2680_supply_names[] = { + "avdd", /* Analog power */ + "dovdd", /* Digital I/O power */ + "dvdd", /* Digital core power */ }; -struct reg_value { - u16 reg_addr; +#define OV2680_NUM_SUPPLIES ARRAY_SIZE(ov2680_supply_names) + +struct regval { + u16 addr; u8 val; }; -static const char * const ov2680_supply_name[] = { - "DOVDD", - "DVDD", - "AVDD", -}; - -#define OV2680_NUM_SUPPLIES ARRAY_SIZE(ov2680_supply_name) - -struct ov2680_mode_info { - const char *name; - enum ov2680_mode_id id; +struct ov2680_mode { u32 width; u32 height; - const struct reg_value *reg_data; - u32 reg_data_size; + struct v4l2_fract max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + const struct regval *reg_list; }; -struct ov2680_ctrls { - struct v4l2_ctrl_handler handler; - struct { - struct v4l2_ctrl *auto_exp; - struct v4l2_ctrl *exposure; - }; - struct { - struct v4l2_ctrl *auto_gain; - struct v4l2_ctrl *gain; - }; +struct ov2680 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *power_gpio; + struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; + struct regulator_bulk_data supplies[OV2680_NUM_SUPPLIES]; - struct v4l2_ctrl *hflip; - struct v4l2_ctrl *vflip; - struct v4l2_ctrl *test_pattern; + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_sleep; + + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *anal_gain; + struct v4l2_ctrl *digi_gain; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *test_pattern; + struct mutex mutex; + bool streaming; + const struct ov2680_mode *cur_mode; + unsigned int lane_num; + unsigned int cfg_num; + unsigned int pixel_rate; + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; }; -struct ov2680_dev { - struct i2c_client *i2c_client; - struct v4l2_subdev sd; +#define to_ov2680(sd) container_of(sd, struct ov2680, subdev) - struct media_pad pad; - struct clk *xvclk; - u32 xvclk_freq; - struct regulator_bulk_data supplies[OV2680_NUM_SUPPLIES]; +/* + * Xclk 24Mhz + * Pclk 66Mhz + * linelength 1700(0x6a4) + * framelength 1294(0x50e) + * grabwindow_width 1600 + * grabwindow_height 1200 + * max_framerate 30fps + * mipi_datarate per lane 660Mbps + */ +static const struct regval ov2680_global_regs[] = { + {0x0100, 0x00}, + {0x0103, 0x01}, + {0x3002, 0x00}, + {0x3016, 0x1c}, + {0x3018, 0x44}, + {0x3020, 0x00}, + {0x3080, 0x02}, + {0x3082, 0x37}, + {0x3084, 0x09}, + {0x3085, 0x04}, + {0x3086, 0x01}, + {0x3501, 0x26}, + {0x3502, 0x40}, + {0x3503, 0x03}, + {0x350b, 0x36}, + {0x3600, 0xb4}, + {0x3603, 0x35}, + {0x3604, 0x24}, + {0x3605, 0x00}, + {0x3620, 0x26}, + {0x3621, 0x37}, + {0x3622, 0x04}, + {0x3628, 0x00}, + {0x3705, 0x3c}, + {0x370c, 0x50}, + {0x370d, 0xc0}, + {0x3718, 0x88}, + {0x3720, 0x00}, + {0x3721, 0x00}, + {0x3722, 0x00}, + {0x3723, 0x00}, + {0x3738, 0x00}, + {0x370a, 0x23}, + {0x3717, 0x58}, + {0x3781, 0x80}, + {0x3784, 0x0c}, + {0x3789, 0x60}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x00}, + {0x3804, 0x06}, + {0x3805, 0x4f}, + {0x3806, 0x04}, + {0x3807, 0xbf}, + {0x3808, 0x03}, + {0x3809, 0x20}, + {0x380a, 0x02}, + {0x380b, 0x58}, + {0x380c, 0x06}, + {0x380d, 0xac}, + {0x380e, 0x02}, + {0x380f, 0x84}, + {0x3810, 0x00}, + {0x3811, 0x04}, + {0x3812, 0x00}, + {0x3813, 0x04}, + {0x3814, 0x31}, + {0x3815, 0x31}, + {0x3819, 0x04}, + {0x3820, 0xc2}, + {0x3821, 0x01}, + {0x4000, 0x81}, + {0x4001, 0x40}, + {0x4008, 0x00}, + {0x4009, 0x03}, + {0x4602, 0x02}, + {0x481f, 0x36}, + {0x4825, 0x36}, + {0x4837, 0x30}, + {0x5002, 0x30}, + {0x5080, 0x00}, + {0x5081, 0x41}, + {REG_NULL, 0x00}, + //{0x0100, 0x01}, - struct gpio_desc *reset_gpio; - struct mutex lock; /* protect members */ - - bool mode_pending_changes; - bool is_enabled; - bool is_streaming; - - struct ov2680_ctrls ctrls; - struct v4l2_mbus_framefmt fmt; - struct v4l2_fract frame_interval; - - const struct ov2680_mode_info *current_mode; + {REG_NULL, 0x00}, }; -static const char * const test_pattern_menu[] = { +/* + * Xclk 24Mhz + * Pclk 66Mhz + * linelength 1700(0x6a4) + * framelength 1294(0x50e) + * grabwindow_width 1600 + * grabwindow_height 1200 + * max_framerate 30fps + * mipi_datarate per lane 660Mbps + */ +static const struct regval ov2680_1600x1200_regs[] = { + // 1600x1200 30fps 1 lane MIPI 660Mbps/lane + {0x3086, 0x00}, + {0x3620, 0x26}, + {0x3621, 0x37}, + {0x3622, 0x04}, + {0x370a, 0x21}, + {0x370d, 0xc0}, + {0x3718, 0x88}, + {0x3721, 0x00}, + {0x3722, 0x00}, + {0x3723, 0x00}, + {0x3738, 0x00}, + {0x3803, 0x00}, + {0x3807, 0xbf}, + {0x3808, 0x06}, + {0x3809, 0x40}, + {0x380a, 0x04}, + {0x380b, 0xb0}, + {0x380c, 0x06}, + {0x380d, 0xa4}, + {0x380e, 0x05}, + {0x380f, 0x0e}, + {0x3811, 0x08}, + {0x3813, 0x08}, + {0x3814, 0x11}, + {0x3815, 0x11}, + {0x3820, 0xc0}, + {0x3821, 0x00}, + {0x4008, 0x02}, + {0x4009, 0x09}, + {0x4837, 0x18}, + //{0x0100, 0x01}, + + {REG_NULL, 0x00}, +}; + +static const struct ov2680_mode supported_modes_1lane[] = { + { + .width = 1600, + .height = 1200, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x0500, + .hts_def = 0x06a4, + .vts_def = 0x050e, + .reg_list = ov2680_1600x1200_regs, + }, +}; + +static const struct ov2680_mode *supported_modes; + +static const s64 link_freq_menu_items[] = { + MIPI_FREQ +}; + +static const char * const ov2680_test_pattern_menu[] = { "Disabled", - "Color Bars", - "Random Data", - "Square", - "Black Image", + "Vertical Color Bar Type 1", + "Vertical Color Bar Type 2", + "Vertical Color Bar Type 3", + "Vertical Color Bar Type 4" }; -static const int ov2680_hv_flip_bayer_order[] = { - MEDIA_BUS_FMT_SBGGR10_1X10, - MEDIA_BUS_FMT_SGRBG10_1X10, - MEDIA_BUS_FMT_SGBRG10_1X10, - MEDIA_BUS_FMT_SRGGB10_1X10, -}; - -static const struct reg_value ov2680_setting_30fps_QUXGA_800_600[] = { - {0x3086, 0x01}, {0x370a, 0x23}, {0x3808, 0x03}, {0x3809, 0x20}, - {0x380a, 0x02}, {0x380b, 0x58}, {0x380c, 0x06}, {0x380d, 0xac}, - {0x380e, 0x02}, {0x380f, 0x84}, {0x3811, 0x04}, {0x3813, 0x04}, - {0x3814, 0x31}, {0x3815, 0x31}, {0x3820, 0xc0}, {0x4008, 0x00}, - {0x4009, 0x03}, {0x4837, 0x1e}, {0x3501, 0x4e}, {0x3502, 0xe0}, -}; - -static const struct reg_value ov2680_setting_30fps_720P_1280_720[] = { - {0x3086, 0x00}, {0x3808, 0x05}, {0x3809, 0x00}, {0x380a, 0x02}, - {0x380b, 0xd0}, {0x380c, 0x06}, {0x380d, 0xa8}, {0x380e, 0x05}, - {0x380f, 0x0e}, {0x3811, 0x08}, {0x3813, 0x06}, {0x3814, 0x11}, - {0x3815, 0x11}, {0x3820, 0xc0}, {0x4008, 0x00}, -}; - -static const struct reg_value ov2680_setting_30fps_UXGA_1600_1200[] = { - {0x3086, 0x00}, {0x3501, 0x4e}, {0x3502, 0xe0}, {0x3808, 0x06}, - {0x3809, 0x40}, {0x380a, 0x04}, {0x380b, 0xb0}, {0x380c, 0x06}, - {0x380d, 0xa8}, {0x380e, 0x05}, {0x380f, 0x0e}, {0x3811, 0x00}, - {0x3813, 0x00}, {0x3814, 0x11}, {0x3815, 0x11}, {0x3820, 0xc0}, - {0x4008, 0x00}, {0x4837, 0x18} -}; - -static const struct ov2680_mode_info ov2680_mode_init_data = { - "mode_quxga_800_600", OV2680_MODE_QUXGA_800_600, 800, 600, - ov2680_setting_30fps_QUXGA_800_600, - ARRAY_SIZE(ov2680_setting_30fps_QUXGA_800_600), -}; - -static const struct ov2680_mode_info ov2680_mode_data[OV2680_MODE_MAX] = { - {"mode_quxga_800_600", OV2680_MODE_QUXGA_800_600, - 800, 600, ov2680_setting_30fps_QUXGA_800_600, - ARRAY_SIZE(ov2680_setting_30fps_QUXGA_800_600)}, - {"mode_720p_1280_720", OV2680_MODE_720P_1280_720, - 1280, 720, ov2680_setting_30fps_720P_1280_720, - ARRAY_SIZE(ov2680_setting_30fps_720P_1280_720)}, - {"mode_uxga_1600_1200", OV2680_MODE_UXGA_1600_1200, - 1600, 1200, ov2680_setting_30fps_UXGA_1600_1200, - ARRAY_SIZE(ov2680_setting_30fps_UXGA_1600_1200)}, -}; - -static struct ov2680_dev *to_ov2680_dev(struct v4l2_subdev *sd) +/* Write registers up to 4 at a time */ +static int ov2680_write_reg(struct i2c_client *client, u16 reg, + u32 len, u32 val) { - return container_of(sd, struct ov2680_dev, sd); -} - -static struct device *ov2680_to_dev(struct ov2680_dev *sensor) -{ - return &sensor->i2c_client->dev; -} - -static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct ov2680_dev, - ctrls.handler)->sd; -} - -static int __ov2680_write_reg(struct ov2680_dev *sensor, u16 reg, - unsigned int len, u32 val) -{ - struct i2c_client *client = sensor->i2c_client; + u32 buf_i, val_i; u8 buf[6]; - int ret; + u8 *val_p; + __be32 val_be; + + dev_dbg(&client->dev, "write reg(0x%x val:0x%06x)!\n", reg, val); if (len > 4) return -EINVAL; - put_unaligned_be16(reg, buf); - put_unaligned_be32(val << (8 * (4 - len)), buf + 2); - ret = i2c_master_send(client, buf, len + 2); - if (ret != len + 2) { - dev_err(&client->dev, "write error: reg=0x%4x: %d\n", reg, ret); + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + + val_be = cpu_to_be32(val); + val_p = (u8 *)&val_be; + buf_i = 2; + val_i = 4 - len; + + while (val_i < 4) + buf[buf_i++] = val_p[val_i++]; + + if (i2c_master_send(client, buf, len + 2) != len + 2) { + dev_err(&client->dev, + "write reg(0x%x val:0x%x)failed !\n", reg, val); return -EIO; } - return 0; } -#define ov2680_write_reg(s, r, v) \ - __ov2680_write_reg(s, r, OV2680_REG_VALUE_8BIT, v) - -#define ov2680_write_reg16(s, r, v) \ - __ov2680_write_reg(s, r, OV2680_REG_VALUE_16BIT, v) - -#define ov2680_write_reg24(s, r, v) \ - __ov2680_write_reg(s, r, OV2680_REG_VALUE_24BIT, v) - -static int __ov2680_read_reg(struct ov2680_dev *sensor, u16 reg, - unsigned int len, u32 *val) +static int ov2680_write_array(struct i2c_client *client, + const struct regval *regs) +{ + u32 i; + int ret = 0; + + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) + ret = ov2680_write_reg(client, regs[i].addr, + OV2680_REG_VALUE_08BIT, regs[i].val); + + return ret; +} + +/* Read registers up to 4 at a time */ +static int ov2680_read_reg(struct i2c_client *client, u16 reg, + unsigned int len, u32 *val) { - struct i2c_client *client = sensor->i2c_client; struct i2c_msg msgs[2]; - u8 addr_buf[2] = { reg >> 8, reg & 0xff }; - u8 data_buf[4] = { 0, }; + u8 *data_be_p; + __be32 data_be = 0; + __be16 reg_addr_be = cpu_to_be16(reg); int ret; - if (len > 4) + if (len > 4 || !len) return -EINVAL; + data_be_p = (u8 *)&data_be; + /* Write register address */ msgs[0].addr = client->addr; msgs[0].flags = 0; - msgs[0].len = ARRAY_SIZE(addr_buf); - msgs[0].buf = addr_buf; + msgs[0].len = 2; + msgs[0].buf = (u8 *)®_addr_be; + /* 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]; + msgs[1].buf = &data_be_p[4 - len]; ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); - if (ret != ARRAY_SIZE(msgs)) { - dev_err(&client->dev, "read error: reg=0x%4x: %d\n", reg, ret); + if (ret != ARRAY_SIZE(msgs)) return -EIO; - } - *val = get_unaligned_be32(data_buf); + *val = be32_to_cpu(data_be); return 0; } -#define ov2680_read_reg(s, r, v) \ - __ov2680_read_reg(s, r, OV2680_REG_VALUE_8BIT, v) - -#define ov2680_read_reg16(s, r, v) \ - __ov2680_read_reg(s, r, OV2680_REG_VALUE_16BIT, v) - -#define ov2680_read_reg24(s, r, v) \ - __ov2680_read_reg(s, r, OV2680_REG_VALUE_24BIT, v) - -static int ov2680_mod_reg(struct ov2680_dev *sensor, u16 reg, u8 mask, u8 val) +/* Check Register value */ +#ifdef CHECK_REG_VALUE +static int ov2680_reg_verify(struct i2c_client *client, + const struct regval *regs) { - u32 readval; - int ret; - - ret = ov2680_read_reg(sensor, reg, &readval); - if (ret < 0) - return ret; - - readval &= ~mask; - val &= mask; - val |= readval; - - return ov2680_write_reg(sensor, reg, val); -} - -static int ov2680_load_regs(struct ov2680_dev *sensor, - const struct ov2680_mode_info *mode) -{ - const struct reg_value *regs = mode->reg_data; - unsigned int i; + u32 i; int ret = 0; - u16 reg_addr; - u8 val; - - for (i = 0; i < mode->reg_data_size; ++i, ++regs) { - reg_addr = regs->reg_addr; - val = regs->val; - - ret = ov2680_write_reg(sensor, reg_addr, val); - if (ret) - break; - } - - return ret; -} - -static void ov2680_power_up(struct ov2680_dev *sensor) -{ - if (!sensor->reset_gpio) - return; - - gpiod_set_value(sensor->reset_gpio, 0); - usleep_range(5000, 10000); -} - -static void ov2680_power_down(struct ov2680_dev *sensor) -{ - if (!sensor->reset_gpio) - return; - - gpiod_set_value(sensor->reset_gpio, 1); - usleep_range(5000, 10000); -} - -static int ov2680_bayer_order(struct ov2680_dev *sensor) -{ - u32 format1; - u32 format2; - u32 hv_flip; - int ret; - - ret = ov2680_read_reg(sensor, OV2680_REG_FORMAT1, &format1); - if (ret < 0) - return ret; - - ret = ov2680_read_reg(sensor, OV2680_REG_FORMAT2, &format2); - if (ret < 0) - return ret; - - hv_flip = (format2 & BIT(2) << 1) | (format1 & BIT(2)); - - sensor->fmt.code = ov2680_hv_flip_bayer_order[hv_flip]; - - return 0; -} - -static int ov2680_vflip_enable(struct ov2680_dev *sensor) -{ - int ret; - - ret = ov2680_mod_reg(sensor, OV2680_REG_FORMAT1, BIT(2), BIT(2)); - if (ret < 0) - return ret; - - return ov2680_bayer_order(sensor); -} - -static int ov2680_vflip_disable(struct ov2680_dev *sensor) -{ - int ret; - - ret = ov2680_mod_reg(sensor, OV2680_REG_FORMAT1, BIT(2), BIT(0)); - if (ret < 0) - return ret; - - return ov2680_bayer_order(sensor); -} - -static int ov2680_hflip_enable(struct ov2680_dev *sensor) -{ - int ret; - - ret = ov2680_mod_reg(sensor, OV2680_REG_FORMAT2, BIT(2), BIT(2)); - if (ret < 0) - return ret; - - return ov2680_bayer_order(sensor); -} - -static int ov2680_hflip_disable(struct ov2680_dev *sensor) -{ - int ret; - - ret = ov2680_mod_reg(sensor, OV2680_REG_FORMAT2, BIT(2), BIT(0)); - if (ret < 0) - return ret; - - return ov2680_bayer_order(sensor); -} - -static int ov2680_test_pattern_set(struct ov2680_dev *sensor, int value) -{ - int ret; - - if (!value) - return ov2680_mod_reg(sensor, OV2680_REG_ISP_CTRL00, BIT(7), 0); - - ret = ov2680_mod_reg(sensor, OV2680_REG_ISP_CTRL00, 0x03, value - 1); - if (ret < 0) - return ret; - - ret = ov2680_mod_reg(sensor, OV2680_REG_ISP_CTRL00, BIT(7), BIT(7)); - if (ret < 0) - return ret; - - return 0; -} - -static int ov2680_gain_set(struct ov2680_dev *sensor, bool auto_gain) -{ - struct ov2680_ctrls *ctrls = &sensor->ctrls; - u32 gain; - int ret; - - ret = ov2680_mod_reg(sensor, OV2680_REG_R_MANUAL, BIT(1), - auto_gain ? 0 : BIT(1)); - if (ret < 0) - return ret; - - if (auto_gain || !ctrls->gain->is_new) - return 0; - - gain = ctrls->gain->val; - - ret = ov2680_write_reg16(sensor, OV2680_REG_GAIN_PK, gain); - - return 0; -} - -static int ov2680_gain_get(struct ov2680_dev *sensor) -{ - u32 gain; - int ret; - - ret = ov2680_read_reg16(sensor, OV2680_REG_GAIN_PK, &gain); - if (ret) - return ret; - - return gain; -} - -static int ov2680_exposure_set(struct ov2680_dev *sensor, bool auto_exp) -{ - struct ov2680_ctrls *ctrls = &sensor->ctrls; - u32 exp; - int ret; - - ret = ov2680_mod_reg(sensor, OV2680_REG_R_MANUAL, BIT(0), - auto_exp ? 0 : BIT(0)); - if (ret < 0) - return ret; - - if (auto_exp || !ctrls->exposure->is_new) - return 0; - - exp = (u32)ctrls->exposure->val; - exp <<= 4; - - return ov2680_write_reg24(sensor, OV2680_REG_EXPOSURE_PK_HIGH, exp); -} - -static int ov2680_exposure_get(struct ov2680_dev *sensor) -{ - int ret; - u32 exp; - - ret = ov2680_read_reg24(sensor, OV2680_REG_EXPOSURE_PK_HIGH, &exp); - if (ret) - return ret; - - return exp >> 4; -} - -static int ov2680_stream_enable(struct ov2680_dev *sensor) -{ - return ov2680_write_reg(sensor, OV2680_REG_STREAM_CTRL, 1); -} - -static int ov2680_stream_disable(struct ov2680_dev *sensor) -{ - return ov2680_write_reg(sensor, OV2680_REG_STREAM_CTRL, 0); -} - -static int ov2680_mode_set(struct ov2680_dev *sensor) -{ - struct ov2680_ctrls *ctrls = &sensor->ctrls; - int ret; - - ret = ov2680_gain_set(sensor, false); - if (ret < 0) - return ret; - - ret = ov2680_exposure_set(sensor, false); - if (ret < 0) - return ret; - - ret = ov2680_load_regs(sensor, sensor->current_mode); - if (ret < 0) - return ret; - - if (ctrls->auto_gain->val) { - ret = ov2680_gain_set(sensor, true); - if (ret < 0) - return ret; - } - - if (ctrls->auto_exp->val == V4L2_EXPOSURE_AUTO) { - ret = ov2680_exposure_set(sensor, true); - if (ret < 0) - return ret; - } - - sensor->mode_pending_changes = false; - - return 0; -} - -static int ov2680_mode_restore(struct ov2680_dev *sensor) -{ - int ret; - - ret = ov2680_load_regs(sensor, &ov2680_mode_init_data); - if (ret < 0) - return ret; - - return ov2680_mode_set(sensor); -} - -static int ov2680_power_off(struct ov2680_dev *sensor) -{ - if (!sensor->is_enabled) - return 0; - - clk_disable_unprepare(sensor->xvclk); - ov2680_power_down(sensor); - regulator_bulk_disable(OV2680_NUM_SUPPLIES, sensor->supplies); - sensor->is_enabled = false; - - return 0; -} - -static int ov2680_power_on(struct ov2680_dev *sensor) -{ - struct device *dev = ov2680_to_dev(sensor); - int ret; - - if (sensor->is_enabled) - return 0; - - ret = regulator_bulk_enable(OV2680_NUM_SUPPLIES, sensor->supplies); - if (ret < 0) { - dev_err(dev, "failed to enable regulators: %d\n", ret); - return ret; - } - - if (!sensor->reset_gpio) { - ret = ov2680_write_reg(sensor, OV2680_REG_SOFT_RESET, 0x01); - if (ret != 0) { - dev_err(dev, "sensor soft reset failed\n"); - return ret; + u32 value; + + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) { + ret = ov2680_read_reg(client, regs[i].addr, + OV2680_REG_VALUE_08BIT, &value); + if (value != regs[i].val) { + dev_info(&client->dev, "%s:0x%04x is 0x%08x instead of 0x%08x\n", + __func__, regs[i].addr, value, regs[i].val); } - usleep_range(1000, 2000); + } + return ret; +} +#endif + +static int ov2680_get_reso_dist(const struct ov2680_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); +} + +static const struct ov2680_mode * +ov2680_find_best_fit(struct ov2680 *ov2680, + struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *framefmt = &fmt->format; + int dist; + int cur_best_fit = 0; + int cur_best_fit_dist = -1; + unsigned int i; + + for (i = 0; i < ov2680->cfg_num; i++) { + dist = ov2680_get_reso_dist(&supported_modes[i], framefmt); + if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { + cur_best_fit_dist = dist; + cur_best_fit = i; + } + } + + return &supported_modes[cur_best_fit]; +} + +static int ov2680_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ov2680 *ov2680 = to_ov2680(sd); + const struct ov2680_mode *mode; + s64 h_blank, vblank_def; + + mutex_lock(&ov2680->mutex); + + mode = ov2680_find_best_fit(ov2680, fmt); + fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10; + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; +#else + mutex_unlock(&ov2680->mutex); + return -ENOTTY; +#endif } else { - ov2680_power_down(sensor); - ov2680_power_up(sensor); + ov2680->cur_mode = mode; + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(ov2680->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(ov2680->vblank, vblank_def, + OV2680_VTS_MAX - mode->height, + 1, vblank_def); } - ret = clk_prepare_enable(sensor->xvclk); - if (ret < 0) - return ret; - - ret = ov2680_mode_restore(sensor); - if (ret < 0) - goto disable; - - sensor->is_enabled = true; - - /* Set clock lane into LP-11 state */ - ov2680_stream_enable(sensor); - usleep_range(1000, 2000); - ov2680_stream_disable(sensor); - - return 0; - -disable: - dev_err(dev, "failed to enable sensor: %d\n", ret); - ov2680_power_off(sensor); - - return ret; -} - -static int ov2680_s_power(struct v4l2_subdev *sd, int on) -{ - struct ov2680_dev *sensor = to_ov2680_dev(sd); - int ret = 0; - - mutex_lock(&sensor->lock); - - if (on) - ret = ov2680_power_on(sensor); - else - ret = ov2680_power_off(sensor); - - mutex_unlock(&sensor->lock); - - if (on && ret == 0) { - ret = v4l2_ctrl_handler_setup(&sensor->ctrls.handler); - if (ret < 0) - return ret; - } - - return ret; -} - -static int ov2680_s_g_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *fi) -{ - struct ov2680_dev *sensor = to_ov2680_dev(sd); - - mutex_lock(&sensor->lock); - fi->interval = sensor->frame_interval; - mutex_unlock(&sensor->lock); - - return 0; -} - -static int ov2680_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct ov2680_dev *sensor = to_ov2680_dev(sd); - int ret = 0; - - mutex_lock(&sensor->lock); - - if (sensor->is_streaming == !!enable) - goto unlock; - - if (enable && sensor->mode_pending_changes) { - ret = ov2680_mode_set(sensor); - if (ret < 0) - goto unlock; - } - - if (enable) - ret = ov2680_stream_enable(sensor); - else - ret = ov2680_stream_disable(sensor); - - sensor->is_streaming = !!enable; - -unlock: - mutex_unlock(&sensor->lock); - - return ret; -} - -static int ov2680_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - struct ov2680_dev *sensor = to_ov2680_dev(sd); - - if (code->pad != 0 || code->index != 0) - return -EINVAL; - - code->code = sensor->fmt.code; + mutex_unlock(&ov2680->mutex); return 0; } static int ov2680_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) + struct v4l2_subdev_format *fmt) { - struct ov2680_dev *sensor = to_ov2680_dev(sd); - struct v4l2_mbus_framefmt *fmt = NULL; - int ret = 0; + struct ov2680 *ov2680 = to_ov2680(sd); + const struct ov2680_mode *mode = ov2680->cur_mode; - if (format->pad != 0) - return -EINVAL; - - mutex_lock(&sensor->lock); - - if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + mutex_lock(&ov2680->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API - fmt = v4l2_subdev_get_try_format(&sensor->sd, cfg, format->pad); + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); #else - ret = -ENOTTY; + mutex_unlock(&ov2680->mutex); + return -ENOTTY; #endif } else { - fmt = &sensor->fmt; + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10; + fmt->format.field = V4L2_FIELD_NONE; } + mutex_unlock(&ov2680->mutex); - if (fmt) - format->format = *fmt; + return 0; +} - mutex_unlock(&sensor->lock); +static int ov2680_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index != 0) + return -EINVAL; + code->code = MEDIA_BUS_FMT_SBGGR10_1X10; + + return 0; +} + +static int ov2680_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct ov2680 *ov2680 = to_ov2680(sd); + + if (fse->index >= ov2680->cfg_num) + return -EINVAL; + + if (fse->code != MEDIA_BUS_FMT_SBGGR10_1X10) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = supported_modes[fse->index].width; + fse->max_height = supported_modes[fse->index].height; + fse->min_height = supported_modes[fse->index].height; + + return 0; +} + +static int ov2680_enable_test_pattern(struct ov2680 *ov2680, u32 pattern) +{ + u32 val; + + if (pattern) + val = (pattern - 1) | OV2680_TEST_PATTERN_ENABLE; + else + val = OV2680_TEST_PATTERN_DISABLE; + + return ov2680_write_reg(ov2680->client, OV2680_REG_TEST_PATTERN, + OV2680_REG_VALUE_08BIT, val); +} + +static int ov2680_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct ov2680 *ov2680 = to_ov2680(sd); + const struct ov2680_mode *mode = ov2680->cur_mode; + + mutex_lock(&ov2680->mutex); + fi->interval = mode->max_fps; + mutex_unlock(&ov2680->mutex); + + return 0; +} + +static void ov2680_get_module_inf(struct ov2680 *ov2680, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strlcpy(inf->base.sensor, OV2680_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, ov2680->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, ov2680->len_name, sizeof(inf->base.lens)); +} + +static long ov2680_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct ov2680 *ov2680 = to_ov2680(sd); + long ret = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + ov2680_get_module_inf(ov2680, (struct rkmodule_inf *)arg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } return ret; } -static int ov2680_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) +#ifdef CONFIG_COMPAT +static long ov2680_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) { - struct ov2680_dev *sensor = to_ov2680_dev(sd); - struct v4l2_mbus_framefmt *fmt = &format->format; -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API - struct v4l2_mbus_framefmt *try_fmt; + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_awb_cfg *cfg; + long ret; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = ov2680_ioctl(sd, cmd, inf); + if (!ret) + ret = copy_to_user(up, inf, sizeof(*inf)); + kfree(inf); + break; + case RKMODULE_AWB_CFG: + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(cfg, up, sizeof(*cfg)); + if (!ret) + ret = ov2680_ioctl(sd, cmd, cfg); + kfree(cfg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} #endif - const struct ov2680_mode_info *mode; + +static int __ov2680_start_stream(struct ov2680 *ov2680) +{ + int ret; + + ret = ov2680_write_array(ov2680->client, ov2680->cur_mode->reg_list); + if (ret) + return ret; + +#ifdef CHECK_REG_VALUE + usleep_range(10000, 20000); + /* verify default values to make sure everything has */ + /* been written correctly as expected */ + dev_info(&ov2680->client->dev, "%s:Check register value!\n", + __func__); + ret = ov2680_reg_verify(ov2680->client, ov2680_global_regs); + if (ret) + return ret; + + ret = ov2680_reg_verify(ov2680->client, ov2680->cur_mode->reg_list); + if (ret) + return ret; +#endif + + /* In case these controls are set before streaming */ + mutex_unlock(&ov2680->mutex); + ret = v4l2_ctrl_handler_setup(&ov2680->ctrl_handler); + mutex_lock(&ov2680->mutex); + if (ret) + return ret; + ret = ov2680_write_reg(ov2680->client, OV2680_REG_CTRL_MODE, + OV2680_REG_VALUE_08BIT, OV2680_MODE_STREAMING); + return ret; +} + +static int __ov2680_stop_stream(struct ov2680 *ov2680) +{ + return ov2680_write_reg(ov2680->client, OV2680_REG_CTRL_MODE, + OV2680_REG_VALUE_08BIT, OV2680_MODE_SW_STANDBY); +} + +static int ov2680_s_stream(struct v4l2_subdev *sd, int on) +{ + struct ov2680 *ov2680 = to_ov2680(sd); + struct i2c_client *client = ov2680->client; int ret = 0; - if (format->pad != 0) - return -EINVAL; + dev_info(&client->dev, "%s: on: %d, %dx%d@%d\n", __func__, on, + ov2680->cur_mode->width, + ov2680->cur_mode->height, + ov2680->cur_mode->max_fps.denominator); - mutex_lock(&sensor->lock); + mutex_lock(&ov2680->mutex); + on = !!on; + if (on == ov2680->streaming) + goto unlock_and_return; - if (sensor->is_streaming) { - ret = -EBUSY; - goto unlock; + if (on) { + dev_info(&client->dev, "stream on!!!\n"); + ret = __ov2680_start_stream(ov2680); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + dev_info(&client->dev, "stream off!!!\n"); + __ov2680_stop_stream(ov2680); } - mode = v4l2_find_nearest_size(ov2680_mode_data, - ARRAY_SIZE(ov2680_mode_data), width, - height, fmt->width, fmt->height); - if (!mode) { - ret = -EINVAL; - goto unlock; - } + ov2680->streaming = on; - if (format->which == V4L2_SUBDEV_FORMAT_TRY) { -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API - try_fmt = v4l2_subdev_get_try_format(sd, cfg, 0); - format->format = *try_fmt; -#else - ret = -ENOTTY; -#endif - - goto unlock; - } - - fmt->width = mode->width; - fmt->height = mode->height; - fmt->code = sensor->fmt.code; - fmt->colorspace = sensor->fmt.colorspace; - - sensor->current_mode = mode; - sensor->fmt = format->format; - sensor->mode_pending_changes = true; - -unlock: - mutex_unlock(&sensor->lock); +unlock_and_return: + mutex_unlock(&ov2680->mutex); return ret; } -static int ov2680_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg) +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 ov2680_cal_delay(u32 cycles) { - struct v4l2_subdev_format fmt = { - .which = cfg ? V4L2_SUBDEV_FORMAT_TRY - : V4L2_SUBDEV_FORMAT_ACTIVE, - .format = { - .width = 800, - .height = 600, + return DIV_ROUND_UP(cycles, OV2680_XVCLK_FREQ / 1000 / 1000); +} + +static int __ov2680_power_on(struct ov2680 *ov2680) +{ + int ret; + u32 delay_us; + struct device *dev = &ov2680->client->dev; + + if (!IS_ERR(ov2680->power_gpio)) + gpiod_set_value_cansleep(ov2680->power_gpio, 1); + + usleep_range(1000, 2000); + + if (!IS_ERR_OR_NULL(ov2680->pins_default)) { + ret = pinctrl_select_state(ov2680->pinctrl, + ov2680->pins_default); + if (ret < 0) + dev_err(dev, "could not set pins\n"); + } + ret = clk_set_rate(ov2680->xvclk, OV2680_XVCLK_FREQ); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate (24MHz)\n"); + if (clk_get_rate(ov2680->xvclk) != OV2680_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); + ret = clk_prepare_enable(ov2680->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + if (!IS_ERR(ov2680->reset_gpio)) + gpiod_set_value_cansleep(ov2680->reset_gpio, 1); + + ret = regulator_bulk_enable(OV2680_NUM_SUPPLIES, ov2680->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + + if (!IS_ERR(ov2680->reset_gpio)) + gpiod_set_value_cansleep(ov2680->reset_gpio, 0); + + if (!IS_ERR(ov2680->pwdn_gpio)) + gpiod_set_value_cansleep(ov2680->pwdn_gpio, 1); + + /* 8192 cycles prior to first SCCB transaction */ + delay_us = ov2680_cal_delay(8192); + usleep_range(delay_us, delay_us * 2); + + return 0; + +disable_clk: + clk_disable_unprepare(ov2680->xvclk); + + return ret; +} + +static void __ov2680_power_off(struct ov2680 *ov2680) +{ + int ret; + struct device *dev = &ov2680->client->dev; + + if (!IS_ERR(ov2680->pwdn_gpio)) + gpiod_set_value_cansleep(ov2680->pwdn_gpio, 0); + clk_disable_unprepare(ov2680->xvclk); + if (!IS_ERR(ov2680->reset_gpio)) + gpiod_set_value_cansleep(ov2680->reset_gpio, 1); + if (!IS_ERR_OR_NULL(ov2680->pins_sleep)) { + ret = pinctrl_select_state(ov2680->pinctrl, + ov2680->pins_sleep); + if (ret < 0) + dev_dbg(dev, "could not set pins\n"); + } + if (!IS_ERR(ov2680->power_gpio)) + gpiod_set_value_cansleep(ov2680->power_gpio, 0); + + regulator_bulk_disable(OV2680_NUM_SUPPLIES, ov2680->supplies); +} + +static int ov2680_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov2680 *ov2680 = to_ov2680(sd); + + return __ov2680_power_on(ov2680); +} + +static int ov2680_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov2680 *ov2680 = to_ov2680(sd); + + __ov2680_power_off(ov2680); + + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int ov2680_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct ov2680 *ov2680 = to_ov2680(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + const struct ov2680_mode *def_mode = &supported_modes[0]; + + mutex_lock(&ov2680->mutex); + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10; + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&ov2680->mutex); + /* No crop or compose */ + + return 0; +} +#endif + +static int ov2680_init(struct v4l2_subdev *sd) +{ + int ret; + struct ov2680 *ov2680 = to_ov2680(sd); + struct i2c_client *client = ov2680->client; + + dev_info(&client->dev, "%s(%d)\n", __func__, __LINE__); + ret = ov2680_write_array(ov2680->client, ov2680_global_regs); + return ret; +} + +static int ov2680_power(struct v4l2_subdev *sd, int on) +{ + int ret; + struct ov2680 *ov2680 = to_ov2680(sd); + struct i2c_client *client = ov2680->client; + struct device *dev = &ov2680->client->dev; + + dev_info(&client->dev, "%s(%d) on(%d)\n", __func__, __LINE__, on); + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + return ret; } - }; - - return ov2680_set_fmt(sd, cfg, &fmt); -} - -static int ov2680_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_frame_size_enum *fse) -{ - int index = fse->index; - - if (index >= OV2680_MODE_MAX || index < 0) - return -EINVAL; - - fse->min_width = ov2680_mode_data[index].width; - fse->min_height = ov2680_mode_data[index].height; - fse->max_width = ov2680_mode_data[index].width; - fse->max_height = ov2680_mode_data[index].height; - - return 0; -} - -static int ov2680_enum_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_frame_interval_enum *fie) -{ - struct v4l2_fract tpf; - - if (fie->index >= OV2680_MODE_MAX || fie->width > OV2680_WIDTH_MAX || - fie->height > OV2680_HEIGHT_MAX || - fie->which > V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - tpf.denominator = OV2680_FRAME_RATE; - tpf.numerator = 1; - - fie->interval = tpf; - - return 0; -} - -static int ov2680_g_volatile_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = ctrl_to_sd(ctrl); - struct ov2680_dev *sensor = to_ov2680_dev(sd); - struct ov2680_ctrls *ctrls = &sensor->ctrls; - int val; - - if (!sensor->is_enabled) - return 0; - - switch (ctrl->id) { - case V4L2_CID_GAIN: - val = ov2680_gain_get(sensor); - if (val < 0) - return val; - ctrls->gain->val = val; - break; - case V4L2_CID_EXPOSURE: - val = ov2680_exposure_get(sensor); - if (val < 0) - return val; - ctrls->exposure->val = val; - break; + ret = ov2680_init(sd); + usleep_range(5000, 10000); + if (ret) + dev_err(dev, "init error\n"); + } else { + pm_runtime_put(&client->dev); } - return 0; } -static int ov2680_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = ctrl_to_sd(ctrl); - struct ov2680_dev *sensor = to_ov2680_dev(sd); - struct ov2680_ctrls *ctrls = &sensor->ctrls; - - if (!sensor->is_enabled) - return 0; - - switch (ctrl->id) { - case V4L2_CID_AUTOGAIN: - return ov2680_gain_set(sensor, !!ctrl->val); - case V4L2_CID_GAIN: - return ov2680_gain_set(sensor, !!ctrls->auto_gain->val); - case V4L2_CID_EXPOSURE_AUTO: - return ov2680_exposure_set(sensor, !!ctrl->val); - case V4L2_CID_EXPOSURE: - return ov2680_exposure_set(sensor, !!ctrls->auto_exp->val); - case V4L2_CID_VFLIP: - if (sensor->is_streaming) - return -EBUSY; - if (ctrl->val) - return ov2680_vflip_enable(sensor); - else - return ov2680_vflip_disable(sensor); - case V4L2_CID_HFLIP: - if (sensor->is_streaming) - return -EBUSY; - if (ctrl->val) - return ov2680_hflip_enable(sensor); - else - return ov2680_hflip_disable(sensor); - case V4L2_CID_TEST_PATTERN: - return ov2680_test_pattern_set(sensor, ctrl->val); - default: - break; - } - - return -EINVAL; -} - -static const struct v4l2_ctrl_ops ov2680_ctrl_ops = { - .g_volatile_ctrl = ov2680_g_volatile_ctrl, - .s_ctrl = ov2680_s_ctrl, +static const struct dev_pm_ops ov2680_pm_ops = { + SET_RUNTIME_PM_OPS(ov2680_runtime_suspend, + ov2680_runtime_resume, NULL) }; +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops ov2680_internal_ops = { + .open = ov2680_open, +}; +#endif + static const struct v4l2_subdev_core_ops ov2680_core_ops = { - .s_power = ov2680_s_power, + .ioctl = ov2680_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = ov2680_compat_ioctl32, +#endif + .s_power = ov2680_power, }; static const struct v4l2_subdev_video_ops ov2680_video_ops = { - .g_frame_interval = ov2680_s_g_frame_interval, - .s_frame_interval = ov2680_s_g_frame_interval, - .s_stream = ov2680_s_stream, + .s_stream = ov2680_s_stream, + .g_frame_interval = ov2680_g_frame_interval, }; static const struct v4l2_subdev_pad_ops ov2680_pad_ops = { - .init_cfg = ov2680_init_cfg, - .enum_mbus_code = ov2680_enum_mbus_code, - .get_fmt = ov2680_get_fmt, - .set_fmt = ov2680_set_fmt, - .enum_frame_size = ov2680_enum_frame_size, - .enum_frame_interval = ov2680_enum_frame_interval, + .enum_mbus_code = ov2680_enum_mbus_code, + .enum_frame_size = ov2680_enum_frame_sizes, + .get_fmt = ov2680_get_fmt, + .set_fmt = ov2680_set_fmt, }; static const struct v4l2_subdev_ops ov2680_subdev_ops = { @@ -903,212 +912,359 @@ static const struct v4l2_subdev_ops ov2680_subdev_ops = { .pad = &ov2680_pad_ops, }; -static int ov2680_mode_init(struct ov2680_dev *sensor) +static int ov2680_set_ctrl(struct v4l2_ctrl *ctrl) { - const struct ov2680_mode_info *init_mode; - - /* set initial mode */ - sensor->fmt.code = MEDIA_BUS_FMT_SBGGR10_1X10; - sensor->fmt.width = 800; - sensor->fmt.height = 600; - sensor->fmt.field = V4L2_FIELD_NONE; - sensor->fmt.colorspace = V4L2_COLORSPACE_SRGB; - - sensor->frame_interval.denominator = OV2680_FRAME_RATE; - sensor->frame_interval.numerator = 1; - - init_mode = &ov2680_mode_init_data; - - sensor->current_mode = init_mode; - - sensor->mode_pending_changes = true; - - return 0; -} - -static int ov2680_v4l2_init(struct ov2680_dev *sensor) -{ - const struct v4l2_ctrl_ops *ops = &ov2680_ctrl_ops; - struct ov2680_ctrls *ctrls = &sensor->ctrls; - struct v4l2_ctrl_handler *hdl = &ctrls->handler; + struct ov2680 *ov2680 = container_of(ctrl->handler, + struct ov2680, ctrl_handler); + struct i2c_client *client = ov2680->client; + s64 max; int ret = 0; - v4l2_i2c_subdev_init(&sensor->sd, sensor->i2c_client, - &ov2680_subdev_ops); - -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API - sensor->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; -#endif - sensor->pad.flags = MEDIA_PAD_FL_SOURCE; - sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; - - ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad); - if (ret < 0) - return ret; - - v4l2_ctrl_handler_init(hdl, 7); - - hdl->lock = &sensor->lock; - - ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0); - ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0); - - ctrls->test_pattern = v4l2_ctrl_new_std_menu_items(hdl, - &ov2680_ctrl_ops, V4L2_CID_TEST_PATTERN, - ARRAY_SIZE(test_pattern_menu) - 1, - 0, 0, test_pattern_menu); - - ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops, - V4L2_CID_EXPOSURE_AUTO, - V4L2_EXPOSURE_MANUAL, 0, - V4L2_EXPOSURE_AUTO); - - ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, - 0, 32767, 1, 0); - - ctrls->auto_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTOGAIN, - 0, 1, 1, 1); - ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN, 0, 2047, 1, 0); - - if (hdl->error) { - ret = hdl->error; - goto cleanup_entity; + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ + max = ov2680->cur_mode->height + ctrl->val - 4; + __v4l2_ctrl_modify_range(ov2680->exposure, + ov2680->exposure->minimum, max, + ov2680->exposure->step, + ov2680->exposure->default_value); + break; } - ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE; - ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE; + if (pm_runtime_get(&client->dev) <= 0) + return 0; - v4l2_ctrl_auto_cluster(2, &ctrls->auto_gain, 0, true); - v4l2_ctrl_auto_cluster(2, &ctrls->auto_exp, 1, true); + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + /* 4 least significant bits of expsoure are fractional part */ - sensor->sd.ctrl_handler = hdl; + ret = ov2680_write_reg(ov2680->client, OV2680_REG_EXPOSURE, + OV2680_REG_VALUE_24BIT, ctrl->val << 4); - ret = v4l2_async_register_subdev(&sensor->sd); - if (ret < 0) - goto cleanup_entity; + break; + case V4L2_CID_ANALOGUE_GAIN: + ret = ov2680_write_reg(ov2680->client, OV2680_REG_GAIN_L, + OV2680_REG_VALUE_08BIT, + ctrl->val & OV2680_GAIN_L_MASK); + ret |= ov2680_write_reg(ov2680->client, OV2680_REG_GAIN_H, + OV2680_REG_VALUE_08BIT, + (ctrl->val >> OV2680_DIGI_GAIN_H_SHIFT) & + OV2680_GAIN_H_MASK); + break; + case V4L2_CID_VBLANK: - return 0; + ret = ov2680_write_reg(ov2680->client, OV2680_REG_VTS, + OV2680_REG_VALUE_16BIT, + ctrl->val + ov2680->cur_mode->height); + break; + case V4L2_CID_TEST_PATTERN: + ret = ov2680_enable_test_pattern(ov2680, ctrl->val); + break; + default: + dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", + __func__, ctrl->id, ctrl->val); + break; + } -cleanup_entity: - media_entity_cleanup(&sensor->sd.entity); - v4l2_ctrl_handler_free(hdl); + pm_runtime_put(&client->dev); return ret; } -static int ov2680_get_regulators(struct ov2680_dev *sensor) +static const struct v4l2_ctrl_ops ov2680_ctrl_ops = { + .s_ctrl = ov2680_set_ctrl, +}; + +static int ov2680_initialize_controls(struct ov2680 *ov2680) { - int i; + const struct ov2680_mode *mode; + struct v4l2_ctrl_handler *handler; + struct v4l2_ctrl *ctrl; + s64 exposure_max, vblank_def; + u32 h_blank; + int ret; + + handler = &ov2680->ctrl_handler; + mode = ov2680->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 8); + if (ret) + return ret; + handler->lock = &ov2680->mutex; + + ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, + 0, 0, link_freq_menu_items); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, + 0, ov2680->pixel_rate, 1, ov2680->pixel_rate); + + h_blank = mode->hts_def - mode->width; + ov2680->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (ov2680->hblank) + ov2680->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = mode->vts_def - mode->height; + ov2680->vblank = v4l2_ctrl_new_std(handler, &ov2680_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + OV2680_VTS_MAX - mode->height, + 1, vblank_def); + + exposure_max = mode->vts_def - 4; + ov2680->exposure = v4l2_ctrl_new_std(handler, &ov2680_ctrl_ops, + V4L2_CID_EXPOSURE, OV2680_EXPOSURE_MIN, + exposure_max, OV2680_EXPOSURE_STEP, + mode->exp_def); + + ov2680->anal_gain = v4l2_ctrl_new_std(handler, &ov2680_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, ANALOG_GAIN_MIN, + ANALOG_GAIN_MAX, ANALOG_GAIN_STEP, + ANALOG_GAIN_DEFAULT); + + /* Digital gain */ + ov2680->digi_gain = v4l2_ctrl_new_std(handler, &ov2680_ctrl_ops, + V4L2_CID_DIGITAL_GAIN, OV2680_DIGI_GAIN_MIN, + OV2680_DIGI_GAIN_MAX, OV2680_DIGI_GAIN_STEP, + OV2680_DIGI_GAIN_DEFAULT); + + ov2680->test_pattern = v4l2_ctrl_new_std_menu_items(handler, + &ov2680_ctrl_ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov2680_test_pattern_menu) - 1, + 0, 0, ov2680_test_pattern_menu); + + if (handler->error) { + ret = handler->error; + dev_err(&ov2680->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + ov2680->subdev.ctrl_handler = handler; + + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +static int ov2680_check_sensor_id(struct ov2680 *ov2680, + struct i2c_client *client) +{ + struct device *dev = &ov2680->client->dev; + u32 id = 0; + int ret; + + ret = ov2680_read_reg(client, OV2680_REG_CHIP_ID, + OV2680_REG_VALUE_16BIT, &id); + if (id != CHIP_ID) { + dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret); + return -ENODEV; + } + + dev_info(dev, "Detected OV%06x sensor\n", CHIP_ID); + + return 0; +} + +static int ov2680_configure_regulators(struct ov2680 *ov2680) +{ + unsigned int i; for (i = 0; i < OV2680_NUM_SUPPLIES; i++) - sensor->supplies[i].supply = ov2680_supply_name[i]; + ov2680->supplies[i].supply = ov2680_supply_names[i]; - return devm_regulator_bulk_get(&sensor->i2c_client->dev, + return devm_regulator_bulk_get(&ov2680->client->dev, OV2680_NUM_SUPPLIES, - sensor->supplies); + ov2680->supplies); } -static int ov2680_check_id(struct ov2680_dev *sensor) +static int ov2680_parse_of(struct ov2680 *ov2680) { - struct device *dev = ov2680_to_dev(sensor); - u32 chip_id; - int ret; + struct device *dev = &ov2680->client->dev; + struct device_node *endpoint; + struct fwnode_handle *fwnode; + int rval; - ov2680_power_on(sensor); - - ret = ov2680_read_reg16(sensor, OV2680_REG_CHIP_ID_HIGH, &chip_id); - if (ret < 0) { - dev_err(dev, "failed to read chip id high\n"); - return -ENODEV; - } - - if (chip_id != OV2680_CHIP_ID) { - dev_err(dev, "chip id: 0x%04x does not match expected 0x%04x\n", - chip_id, OV2680_CHIP_ID); - return -ENODEV; - } - - return 0; -} - -static int ov2860_parse_dt(struct ov2680_dev *sensor) -{ - struct device *dev = ov2680_to_dev(sensor); - int ret; - - sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset", - GPIOD_OUT_HIGH); - ret = PTR_ERR_OR_ZERO(sensor->reset_gpio); - if (ret < 0) { - dev_dbg(dev, "error while getting reset gpio: %d\n", ret); - return ret; - } - - sensor->xvclk = devm_clk_get(dev, "xvclk"); - if (IS_ERR(sensor->xvclk)) { - dev_err(dev, "xvclk clock missing or invalid\n"); - return PTR_ERR(sensor->xvclk); - } - - sensor->xvclk_freq = clk_get_rate(sensor->xvclk); - if (sensor->xvclk_freq != OV2680_XVCLK_VALUE) { - dev_err(dev, "wrong xvclk frequency %d HZ, expected: %d Hz\n", - sensor->xvclk_freq, OV2680_XVCLK_VALUE); + endpoint = of_graph_get_next_endpoint(dev->of_node, NULL); + if (!endpoint) { + dev_err(dev, "Failed to get endpoint\n"); return -EINVAL; } + fwnode = of_fwnode_handle(endpoint); + rval = fwnode_property_read_u32_array(fwnode, "data-lanes", NULL, 0); + if (rval <= 0) { + dev_warn(dev, " Get mipi lane num failed!\n"); + return -1; + } + ov2680->lane_num = rval; + if (1 == ov2680->lane_num) { + ov2680->cur_mode = &supported_modes_1lane[0]; + supported_modes = supported_modes_1lane; + ov2680->cfg_num = ARRAY_SIZE(supported_modes_1lane); + + /* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ + ov2680->pixel_rate = MIPI_FREQ * 2U * ov2680->lane_num / 10U; + dev_info(dev, "lane_num(%d) pixel_rate(%u)\n", + ov2680->lane_num, ov2680->pixel_rate); + } else { + dev_err(dev, "unsupported lane_num(%d)\n", ov2680->lane_num); + return -1; + } return 0; } -static int ov2680_probe(struct i2c_client *client) +static int ov2680_probe(struct i2c_client *client, + const struct i2c_device_id *id) { struct device *dev = &client->dev; - struct ov2680_dev *sensor; + struct device_node *node = dev->of_node; + struct ov2680 *ov2680; + struct v4l2_subdev *sd; + char facing[2] = "b"; int ret; - sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); - if (!sensor) + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + ov2680 = devm_kzalloc(dev, sizeof(*ov2680), GFP_KERNEL); + if (!ov2680) return -ENOMEM; - sensor->i2c_client = client; - - ret = ov2860_parse_dt(sensor); - if (ret < 0) - return -EINVAL; - - ret = ov2680_mode_init(sensor); - if (ret < 0) - return ret; - - ret = ov2680_get_regulators(sensor); - if (ret < 0) { - dev_err(dev, "failed to get regulators\n"); - return ret; + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &ov2680->module_index); + if (ret) { + dev_warn(dev, "could not get module index!\n"); + ov2680->module_index = 0; } - mutex_init(&sensor->lock); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &ov2680->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &ov2680->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &ov2680->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } - ret = ov2680_v4l2_init(sensor); + ov2680->client = client; + + ov2680->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(ov2680->xvclk)) { + dev_err(dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + ov2680->power_gpio = devm_gpiod_get(dev, "power", GPIOD_OUT_LOW); + if (IS_ERR(ov2680->power_gpio)) + dev_warn(dev, "Failed to get power-gpios, maybe no use\n"); + + ov2680->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ov2680->reset_gpio)) + dev_warn(dev, "Failed to get reset-gpios, maybe no use\n"); + + ov2680->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(ov2680->pwdn_gpio)) + dev_warn(dev, "Failed to get pwdn-gpios\n"); + + ret = ov2680_configure_regulators(ov2680); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + ret = ov2680_parse_of(ov2680); + if (ret != 0) + return -EINVAL; + + ov2680->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR(ov2680->pinctrl)) { + ov2680->pins_default = + pinctrl_lookup_state(ov2680->pinctrl, + OF_CAMERA_PINCTRL_STATE_DEFAULT); + if (IS_ERR(ov2680->pins_default)) + dev_err(dev, "could not get default pinstate\n"); + + ov2680->pins_sleep = + pinctrl_lookup_state(ov2680->pinctrl, + OF_CAMERA_PINCTRL_STATE_SLEEP); + if (IS_ERR(ov2680->pins_sleep)) + dev_err(dev, "could not get sleep pinstate\n"); + } + + mutex_init(&ov2680->mutex); + + sd = &ov2680->subdev; + v4l2_i2c_subdev_init(sd, client, &ov2680_subdev_ops); + ret = ov2680_initialize_controls(ov2680); + if (ret) + goto err_destroy_mutex; + + ret = __ov2680_power_on(ov2680); + if (ret) + goto err_free_handler; + + ret = ov2680_check_sensor_id(ov2680, client); + if (ret < 0) { + dev_info(&client->dev, "%s(%d) Check id failed\n" + "check following information:\n" + "Power/PowerDown/Reset/Mclk/I2cBus !!\n", + __func__, __LINE__); + goto err_power_off; + } + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &ov2680_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + ov2680->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &ov2680->pad); if (ret < 0) - goto lock_destroy; + goto err_power_off; +#endif - ret = ov2680_check_id(sensor); - if (ret < 0) - goto error_cleanup; + memset(facing, 0, sizeof(facing)); + if (strcmp(ov2680->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; - dev_info(dev, "ov2680 init correctly\n"); + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + ov2680->module_index, facing, + OV2680_NAME, dev_name(sd->dev)); + + ret = v4l2_async_register_subdev_sensor_common(sd); + if (ret) { + dev_err(dev, "v4l2 async register subdev failed\n"); + goto err_clean_entity; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); return 0; -error_cleanup: - dev_err(dev, "ov2680 init fail: %d\n", ret); - - media_entity_cleanup(&sensor->sd.entity); - v4l2_async_unregister_subdev(&sensor->sd); - v4l2_ctrl_handler_free(&sensor->ctrls.handler); - -lock_destroy: - mutex_destroy(&sensor->lock); +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif +err_power_off: + __ov2680_power_off(ov2680); +err_free_handler: + v4l2_ctrl_handler_free(&ov2680->ctrl_handler); +err_destroy_mutex: + mutex_destroy(&ov2680->mutex); return ret; } @@ -1116,71 +1272,59 @@ lock_destroy: static int ov2680_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct ov2680_dev *sensor = to_ov2680_dev(sd); + struct ov2680 *ov2680 = to_ov2680(sd); - v4l2_async_unregister_subdev(&sensor->sd); - mutex_destroy(&sensor->lock); - media_entity_cleanup(&sensor->sd.entity); - v4l2_ctrl_handler_free(&sensor->ctrls.handler); + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + v4l2_ctrl_handler_free(&ov2680->ctrl_handler); + mutex_destroy(&ov2680->mutex); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __ov2680_power_off(ov2680); + pm_runtime_set_suspended(&client->dev); return 0; } -static int __maybe_unused ov2680_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct ov2680_dev *sensor = to_ov2680_dev(sd); - - if (sensor->is_streaming) - ov2680_stream_disable(sensor); - - return 0; -} - -static int __maybe_unused ov2680_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct ov2680_dev *sensor = to_ov2680_dev(sd); - int ret; - - if (sensor->is_streaming) { - ret = ov2680_stream_enable(sensor); - if (ret < 0) - goto stream_disable; - } - - return 0; - -stream_disable: - ov2680_stream_disable(sensor); - sensor->is_streaming = false; - - return ret; -} - -static const struct dev_pm_ops ov2680_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(ov2680_suspend, ov2680_resume) -}; - -static const struct of_device_id ov2680_dt_ids[] = { +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id ov2680_of_match[] = { { .compatible = "ovti,ov2680" }, - { /* sentinel */ }, + {}, +}; +MODULE_DEVICE_TABLE(of, ov2680_of_match); +#endif + +static const struct i2c_device_id ov2680_match_id[] = { + { "ovti,ov2680", 0 }, + { }, }; -MODULE_DEVICE_TABLE(of, ov2680_dt_ids); static struct i2c_driver ov2680_i2c_driver = { .driver = { - .name = "ov2680", + .name = OV2680_NAME, .pm = &ov2680_pm_ops, - .of_match_table = of_match_ptr(ov2680_dt_ids), + .of_match_table = of_match_ptr(ov2680_of_match), }, - .probe_new = ov2680_probe, - .remove = ov2680_remove, + .probe = &ov2680_probe, + .remove = &ov2680_remove, + .id_table = ov2680_match_id, }; -module_i2c_driver(ov2680_i2c_driver); -MODULE_AUTHOR("Rui Miguel Silva "); -MODULE_DESCRIPTION("OV2680 CMOS Image Sensor driver"); +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&ov2680_i2c_driver); +} + +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&ov2680_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_DESCRIPTION("OmniVision ov2680 sensor driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ov2685.c b/drivers/media/i2c/ov2685.c index 385c1886a947..1da7e5a282a2 100644 --- a/drivers/media/i2c/ov2685.c +++ b/drivers/media/i2c/ov2685.c @@ -1,8 +1,12 @@ -// SPDX-License-Identifier: GPL-2.0 /* * ov2685 driver * * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. */ #include @@ -14,16 +18,19 @@ #include #include #include +#include +#include +#include #include #include #include #include +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x0) + #define CHIP_ID 0x2685 #define OV2685_REG_CHIP_ID 0x300a -#define OV2685_XVCLK_FREQ 24000000 - #define REG_SC_CTRL_MODE 0x0100 #define SC_CTRL_MODE_STANDBY 0x0 #define SC_CTRL_MODE_STREAMING BIT(0) @@ -44,7 +51,7 @@ #define OV2685_REG_TEST_PATTERN 0x5080 #define OV2685_TEST_PATTERN_DISABLED 0x00 #define OV2685_TEST_PATTERN_COLOR_BAR 0x80 -#define OV2685_TEST_PATTERN_RANDOM 0x81 +#define OV2685_TEST_PATTERN_RND 0x81 #define OV2685_TEST_PATTERN_COLOR_BAR_FADE 0x88 #define OV2685_TEST_PATTERN_BW_SQUARE 0x92 #define OV2685_TEST_PATTERN_COLOR_SQUARE 0x82 @@ -58,13 +65,7 @@ #define OV2685_LANES 1 #define OV2685_BITS_PER_SAMPLE 10 -static const char * const ov2685_supply_names[] = { - "avdd", /* Analog power */ - "dovdd", /* Digital I/O power */ - "dvdd", /* Digital core power */ -}; - -#define OV2685_NUM_SUPPLIES ARRAY_SIZE(ov2685_supply_names) +#define OV2685_NAME "ov2685" struct regval { u16 addr; @@ -83,8 +84,10 @@ struct ov2685_mode { struct ov2685 { struct i2c_client *client; struct clk *xvclk; + struct regulator *avdd_regulator; /* Analog power */ + struct regulator *dovdd_regulator; /* Digital I/O power */ + /* use internal DVDD power */ struct gpio_desc *reset_gpio; - struct regulator_bulk_data supplies[OV2685_NUM_SUPPLIES]; bool streaming; struct mutex mutex; @@ -98,8 +101,11 @@ struct ov2685 { struct v4l2_ctrl_handler ctrl_handler; const struct ov2685_mode *cur_mode; + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; }; - #define to_ov2685(sd) container_of(sd, struct ov2685, subdev) /* PLL settings bases on 24M xvclk */ @@ -119,7 +125,7 @@ static struct regval ov2685_1600x1200_regs[] = { {0x3087, 0x00}, {0x3501, 0x4e}, {0x3502, 0xe0}, - {0x3503, 0x27}, + {0x3503, 0x07}, {0x350b, 0x36}, {0x3600, 0xb4}, {0x3603, 0x35}, @@ -215,17 +221,17 @@ static const s64 link_freq_menu_items[] = { static const char * const ov2685_test_pattern_menu[] = { "Disabled", "Color Bar", + "RND PATTERN", "Color Bar FADE", - "Random Data", - "Black White Square", - "Color Square" + "BW SQUARE", + "COLOR SQUARE" }; static const int ov2685_test_pattern_val[] = { OV2685_TEST_PATTERN_DISABLED, OV2685_TEST_PATTERN_COLOR_BAR, + OV2685_TEST_PATTERN_RND, OV2685_TEST_PATTERN_COLOR_BAR_FADE, - OV2685_TEST_PATTERN_RANDOM, OV2685_TEST_PATTERN_BW_SQUARE, OV2685_TEST_PATTERN_COLOR_SQUARE, }; @@ -243,9 +249,10 @@ static const struct ov2685_mode supported_modes[] = { /* Write registers up to 4 at a time */ static int ov2685_write_reg(struct i2c_client *client, u16 reg, - u32 len, u32 val) + unsigned int len, u32 val) { - u32 val_i, buf_i; + int buf_i; + int val_i; u8 buf[6]; u8 *val_p; __be32 val_be; @@ -273,8 +280,7 @@ static int ov2685_write_reg(struct i2c_client *client, u16 reg, static int ov2685_write_array(struct i2c_client *client, const struct regval *regs) { - int ret = 0; - u32 i; + int i, ret = 0; for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) ret = ov2685_write_reg(client, regs[i].addr, @@ -285,7 +291,7 @@ static int ov2685_write_array(struct i2c_client *client, /* Read registers up to 4 at a time */ static int ov2685_read_reg(struct i2c_client *client, u16 reg, - u32 len, u32 *val) + unsigned int len, u32 *val) { struct i2c_msg msgs[2]; u8 *data_be_p; @@ -318,12 +324,12 @@ static int ov2685_read_reg(struct i2c_client *client, u16 reg, return 0; } -static void ov2685_fill_fmt(const struct ov2685_mode *mode, +static void ov2685_fill_fmt(struct ov2685 *ov2685, struct v4l2_mbus_framefmt *fmt) { fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10; - fmt->width = mode->width; - fmt->height = mode->height; + fmt->width = ov2685->cur_mode->width; + fmt->height = ov2685->cur_mode->height; fmt->field = V4L2_FIELD_NONE; } @@ -334,8 +340,7 @@ static int ov2685_set_fmt(struct v4l2_subdev *sd, struct ov2685 *ov2685 = to_ov2685(sd); struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format; - /* only one mode supported for now */ - ov2685_fill_fmt(ov2685->cur_mode, mbus_fmt); + ov2685_fill_fmt(ov2685, mbus_fmt); return 0; } @@ -347,7 +352,7 @@ static int ov2685_get_fmt(struct v4l2_subdev *sd, struct ov2685 *ov2685 = to_ov2685(sd); struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format; - ov2685_fill_fmt(ov2685->cur_mode, mbus_fmt); + ov2685_fill_fmt(ov2685, mbus_fmt); return 0; } @@ -360,7 +365,6 @@ static int ov2685_enum_mbus_code(struct v4l2_subdev *sd, return -EINVAL; code->code = MEDIA_BUS_FMT_SBGGR10_1X10; - return 0; } @@ -383,16 +387,34 @@ static int ov2685_enum_frame_sizes(struct v4l2_subdev *sd, return 0; } -/* Calculate the delay in us by clock rate and clock cycles */ -static inline u32 ov2685_cal_delay(u32 cycles) +static inline void ov2685_set_exposure(struct ov2685 *ov2685, s32 val) { - return DIV_ROUND_UP(cycles, OV2685_XVCLK_FREQ / 1000 / 1000); + ov2685_write_reg(ov2685->client, OV2685_REG_EXPOSURE, + OV2685_REG_VALUE_24BIT, val << 4); +} + +static inline void ov2685_set_gain(struct ov2685 *ov2685, s32 val) +{ + ov2685_write_reg(ov2685->client, OV2685_REG_GAIN, + OV2685_REG_VALUE_16BIT, val & OV2685_GAIN_MAX); +} + +static inline void ov2685_set_vts(struct ov2685 *ov2685, s32 val) +{ + val += ov2685->cur_mode->height; + ov2685_write_reg(ov2685->client, OV2685_REG_VTS, + OV2685_REG_VALUE_16BIT, val); +} + +static inline void ov2685_enable_test_pattern(struct ov2685 *ov2685, u32 pat) +{ + ov2685_write_reg(ov2685->client, OV2685_REG_TEST_PATTERN, + OV2685_REG_VALUE_08BIT, ov2685_test_pattern_val[pat]); } static int __ov2685_power_on(struct ov2685 *ov2685) { int ret; - u32 delay_us; struct device *dev = &ov2685->client->dev; ret = clk_prepare_enable(ov2685->xvclk); @@ -400,33 +422,37 @@ static int __ov2685_power_on(struct ov2685 *ov2685) dev_err(dev, "Failed to enable xvclk\n"); return ret; } + clk_set_rate(ov2685->xvclk, 24000000); gpiod_set_value_cansleep(ov2685->reset_gpio, 1); - - ret = regulator_bulk_enable(OV2685_NUM_SUPPLIES, ov2685->supplies); + /* AVDD and DOVDD may rise in any order */ + ret = regulator_enable(ov2685->avdd_regulator); if (ret < 0) { - dev_err(dev, "Failed to enable regulators\n"); - goto disable_clk; + dev_err(dev, "Failed to enable AVDD regulator\n"); + goto disable_xvclk; } - - /* The minimum delay between power supplies and reset rising can be 0 */ + ret = regulator_enable(ov2685->dovdd_regulator); + if (ret < 0) { + dev_err(dev, "Failed to enable DOVDD regulator\n"); + goto disable_avdd; + } + /* The minimum delay between AVDD and reset rising can be 0 */ gpiod_set_value_cansleep(ov2685->reset_gpio, 0); - /* 8192 xvclk cycles prior to the first SCCB transaction */ - delay_us = ov2685_cal_delay(8192); - usleep_range(delay_us, delay_us * 2); + /* 8192 xvclk cycles prior to the first SCCB transaction. + * NOTE: An additional 1ms must be added to wait for + * SCCB to become stable when using internal DVDD. + */ + usleep_range(1350, 1500); /* HACK: ov2685 would output messy data after reset(R0103), * writing register before .s_stream() as a workaround */ ret = ov2685_write_array(ov2685->client, ov2685->cur_mode->reg_list); - if (ret) - goto disable_supplies; - return 0; - -disable_supplies: - regulator_bulk_disable(OV2685_NUM_SUPPLIES, ov2685->supplies); -disable_clk: + return ret; +disable_avdd: + regulator_disable(ov2685->avdd_regulator); +disable_xvclk: clk_disable_unprepare(ov2685->xvclk); return ret; @@ -435,14 +461,100 @@ disable_clk: static void __ov2685_power_off(struct ov2685 *ov2685) { /* 512 xvclk cycles after the last SCCB transaction or MIPI frame end */ - u32 delay_us = ov2685_cal_delay(512); - - usleep_range(delay_us, delay_us * 2); + usleep_range(30, 50); clk_disable_unprepare(ov2685->xvclk); gpiod_set_value_cansleep(ov2685->reset_gpio, 1); - regulator_bulk_disable(OV2685_NUM_SUPPLIES, ov2685->supplies); + regulator_disable(ov2685->dovdd_regulator); + regulator_disable(ov2685->avdd_regulator); } +static int ov2685_s_power(struct v4l2_subdev *sd, int on) +{ + struct ov2685 *ov2685 = to_ov2685(sd); + int ret = 0; + + mutex_lock(&ov2685->mutex); + + if (on) + ret = pm_runtime_get_sync(&ov2685->client->dev); + else + ret = pm_runtime_put(&ov2685->client->dev); + + mutex_unlock(&ov2685->mutex); + + return ret; +} + +static void ov2685_get_module_inf(struct ov2685 *ov2685, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strlcpy(inf->base.sensor, OV2685_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, ov2685->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, ov2685->len_name, sizeof(inf->base.lens)); +} + +static long ov2685_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct ov2685 *ov2685 = to_ov2685(sd); + long ret = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + ov2685_get_module_inf(ov2685, (struct rkmodule_inf *)arg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long ov2685_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_awb_cfg *cfg; + long ret; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = ov2685_ioctl(sd, cmd, inf); + if (!ret) + ret = copy_to_user(up, inf, sizeof(*inf)); + kfree(inf); + break; + case RKMODULE_AWB_CFG: + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(cfg, up, sizeof(*cfg)); + if (!ret) + ret = ov2685_ioctl(sd, cmd, cfg); + kfree(cfg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + static int ov2685_s_stream(struct v4l2_subdev *sd, int on) { struct ov2685 *ov2685 = to_ov2685(sd); @@ -456,33 +568,27 @@ static int ov2685_s_stream(struct v4l2_subdev *sd, int on) goto unlock_and_return; if (on) { - ret = pm_runtime_get_sync(&ov2685->client->dev); - if (ret < 0) { - pm_runtime_put_noidle(&client->dev); - goto unlock_and_return; - } - ret = __v4l2_ctrl_handler_setup(&ov2685->ctrl_handler); - if (ret) { - pm_runtime_put(&client->dev); - goto unlock_and_return; - } + /* In case these controls are set before streaming */ + ov2685_set_exposure(ov2685, ov2685->exposure->val); + ov2685_set_gain(ov2685, ov2685->anal_gain->val); + ov2685_set_vts(ov2685, ov2685->vblank->val); + ov2685_enable_test_pattern(ov2685, ov2685->test_pattern->val); + ret = ov2685_write_reg(client, REG_SC_CTRL_MODE, OV2685_REG_VALUE_08BIT, SC_CTRL_MODE_STREAMING); - if (ret) { - pm_runtime_put(&client->dev); + if (ret) goto unlock_and_return; - } } else { - ov2685_write_reg(client, REG_SC_CTRL_MODE, + ret = ov2685_write_reg(client, REG_SC_CTRL_MODE, OV2685_REG_VALUE_08BIT, SC_CTRL_MODE_STANDBY); - pm_runtime_put(&ov2685->client->dev); + if (ret) + goto unlock_and_return; } ov2685->streaming = on; unlock_and_return: mutex_unlock(&ov2685->mutex); - return ret; } @@ -496,7 +602,7 @@ static int ov2685_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) try_fmt = v4l2_subdev_get_try_format(sd, fh->pad, 0); /* Initialize try_fmt */ - ov2685_fill_fmt(&supported_modes[0], try_fmt); + ov2685_fill_fmt(ov2685, try_fmt); mutex_unlock(&ov2685->mutex); @@ -504,16 +610,27 @@ static int ov2685_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) } #endif -static int __maybe_unused ov2685_runtime_resume(struct device *dev) +static int ov2685_runtime_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct v4l2_subdev *sd = i2c_get_clientdata(client); struct ov2685 *ov2685 = to_ov2685(sd); + int ret; - return __ov2685_power_on(ov2685); + ret = __ov2685_power_on(ov2685); + if (ret) + return ret; + + if (ov2685->streaming) { + ret = ov2685_s_stream(sd, 1); + if (ret) + __ov2685_power_off(ov2685); + } + + return ret; } -static int __maybe_unused ov2685_runtime_suspend(struct device *dev) +static int ov2685_runtime_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct v4l2_subdev *sd = i2c_get_clientdata(client); @@ -534,67 +651,66 @@ static int ov2685_set_ctrl(struct v4l2_ctrl *ctrl) struct ov2685 *ov2685 = container_of(ctrl->handler, struct ov2685, ctrl_handler); struct i2c_client *client = ov2685->client; - s64 max_expo; - int ret; + s64 max; + int ret = 0; /* Propagate change of current control to all related controls */ switch (ctrl->id) { case V4L2_CID_VBLANK: /* Update max exposure while meeting expected vblanking */ - max_expo = ov2685->cur_mode->height + ctrl->val - 4; + max = ov2685->cur_mode->height + ctrl->val - 4; __v4l2_ctrl_modify_range(ov2685->exposure, - ov2685->exposure->minimum, max_expo, + ov2685->exposure->minimum, max, ov2685->exposure->step, ov2685->exposure->default_value); break; } - if (pm_runtime_get_if_in_use(&client->dev) <= 0) - return 0; - + pm_runtime_get_sync(&client->dev); switch (ctrl->id) { case V4L2_CID_EXPOSURE: - ret = ov2685_write_reg(ov2685->client, OV2685_REG_EXPOSURE, - OV2685_REG_VALUE_24BIT, ctrl->val << 4); + ov2685_set_exposure(ov2685, ctrl->val); break; case V4L2_CID_ANALOGUE_GAIN: - ret = ov2685_write_reg(ov2685->client, OV2685_REG_GAIN, - OV2685_REG_VALUE_16BIT, ctrl->val); + ov2685_set_gain(ov2685, ctrl->val); break; case V4L2_CID_VBLANK: - ret = ov2685_write_reg(ov2685->client, OV2685_REG_VTS, - OV2685_REG_VALUE_16BIT, - ctrl->val + ov2685->cur_mode->height); + ov2685_set_vts(ov2685, ctrl->val); break; case V4L2_CID_TEST_PATTERN: - ret = ov2685_write_reg(ov2685->client, OV2685_REG_TEST_PATTERN, - OV2685_REG_VALUE_08BIT, - ov2685_test_pattern_val[ctrl->val]); + ov2685_enable_test_pattern(ov2685, ctrl->val); break; default: dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", __func__, ctrl->id, ctrl->val); - ret = -EINVAL; break; }; - pm_runtime_put(&client->dev); return ret; } -static const struct v4l2_subdev_video_ops ov2685_video_ops = { +static struct v4l2_subdev_core_ops ov2685_core_ops = { + .s_power = ov2685_s_power, + .ioctl = ov2685_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = ov2685_compat_ioctl32, +#endif +}; + +static struct v4l2_subdev_video_ops ov2685_video_ops = { .s_stream = ov2685_s_stream, }; -static const struct v4l2_subdev_pad_ops ov2685_pad_ops = { +static struct v4l2_subdev_pad_ops ov2685_pad_ops = { .enum_mbus_code = ov2685_enum_mbus_code, .enum_frame_size = ov2685_enum_frame_sizes, .get_fmt = ov2685_get_fmt, .set_fmt = ov2685_set_fmt, }; -static const struct v4l2_subdev_ops ov2685_subdev_ops = { +static struct v4l2_subdev_ops ov2685_subdev_ops = { + .core = &ov2685_core_ops, .video = &ov2685_video_ops, .pad = &ov2685_pad_ops, }; @@ -620,7 +736,7 @@ static int ov2685_initialize_controls(struct ov2685 *ov2685) handler = &ov2685->ctrl_handler; mode = ov2685->cur_mode; - ret = v4l2_ctrl_handler_init(handler, 8); + ret = v4l2_ctrl_handler_init(handler, 1); if (ret) return ret; handler->lock = &ov2685->mutex; @@ -663,34 +779,31 @@ static int ov2685_initialize_controls(struct ov2685 *ov2685) 0, 0, ov2685_test_pattern_menu); if (handler->error) { - ret = handler->error; - dev_err(&ov2685->client->dev, - "Failed to init controls(%d)\n", ret); - goto err_free_handler; + v4l2_ctrl_handler_free(handler); + return handler->error; } ov2685->subdev.ctrl_handler = handler; return 0; - -err_free_handler: - v4l2_ctrl_handler_free(handler); - - return ret; } static int ov2685_check_sensor_id(struct ov2685 *ov2685, struct i2c_client *client) { struct device *dev = &ov2685->client->dev; - int ret; - u32 id = 0; + int id, ret; - ret = ov2685_read_reg(client, OV2685_REG_CHIP_ID, - OV2685_REG_VALUE_16BIT, &id); - if (id != CHIP_ID) { - dev_err(dev, "Unexpected sensor id(%04x), ret(%d)\n", id, ret); + ret = __ov2685_power_on(ov2685); + if (ret) return ret; + ov2685_read_reg(client, OV2685_REG_CHIP_ID, + OV2685_REG_VALUE_16BIT, &id); + __ov2685_power_off(ov2685); + + if (id != CHIP_ID) { + dev_err(dev, "Wrong camera sensor id(%04x)\n", id); + return -EINVAL; } dev_info(dev, "Detected OV%04x sensor\n", CHIP_ID); @@ -698,29 +811,38 @@ static int ov2685_check_sensor_id(struct ov2685 *ov2685, return 0; } -static int ov2685_configure_regulators(struct ov2685 *ov2685) -{ - int i; - - for (i = 0; i < OV2685_NUM_SUPPLIES; i++) - ov2685->supplies[i].supply = ov2685_supply_names[i]; - - return devm_regulator_bulk_get(&ov2685->client->dev, - OV2685_NUM_SUPPLIES, - ov2685->supplies); -} - static int ov2685_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; + struct device_node *node = dev->of_node; struct ov2685 *ov2685; + struct v4l2_subdev *sd; + char facing[2]; int ret; + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + ov2685 = devm_kzalloc(dev, sizeof(*ov2685), GFP_KERNEL); if (!ov2685) return -ENOMEM; + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &ov2685->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &ov2685->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &ov2685->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &ov2685->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + ov2685->client = client; ov2685->cur_mode = &supported_modes[0]; @@ -729,13 +851,6 @@ static int ov2685_probe(struct i2c_client *client, dev_err(dev, "Failed to get xvclk\n"); return -EINVAL; } - ret = clk_set_rate(ov2685->xvclk, OV2685_XVCLK_FREQ); - if (ret < 0) { - dev_err(dev, "Failed to set xvclk rate (24MHz)\n"); - return ret; - } - if (clk_get_rate(ov2685->xvclk) != OV2685_XVCLK_FREQ) - dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); ov2685->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(ov2685->reset_gpio)) { @@ -743,59 +858,66 @@ static int ov2685_probe(struct i2c_client *client, return -EINVAL; } - ret = ov2685_configure_regulators(ov2685); - if (ret) { - dev_err(dev, "Failed to get power regulators\n"); - return ret; + ov2685->avdd_regulator = devm_regulator_get(dev, "avdd"); + if (IS_ERR(ov2685->avdd_regulator)) { + dev_err(dev, "Failed to get avdd-supply\n"); + return -EINVAL; + } + ov2685->dovdd_regulator = devm_regulator_get(dev, "dovdd"); + if (IS_ERR(ov2685->dovdd_regulator)) { + dev_err(dev, "Failed to get dovdd-supply\n"); + return -EINVAL; } mutex_init(&ov2685->mutex); v4l2_i2c_subdev_init(&ov2685->subdev, client, &ov2685_subdev_ops); ret = ov2685_initialize_controls(ov2685); if (ret) - goto err_destroy_mutex; - - ret = __ov2685_power_on(ov2685); - if (ret) - goto err_free_handler; + goto destroy_mutex; ret = ov2685_check_sensor_id(ov2685, client); if (ret) - goto err_power_off; + return ret; #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API ov2685->subdev.internal_ops = &ov2685_internal_ops; - ov2685->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; #endif #if defined(CONFIG_MEDIA_CONTROLLER) ov2685->pad.flags = MEDIA_PAD_FL_SOURCE; + ov2685->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; ov2685->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR; ret = media_entity_pads_init(&ov2685->subdev.entity, 1, &ov2685->pad); if (ret < 0) - goto err_power_off; + goto free_ctrl_handler; #endif - ret = v4l2_async_register_subdev(&ov2685->subdev); + sd = &ov2685->subdev; + memset(facing, 0, sizeof(facing)); + if (strcmp(ov2685->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + ov2685->module_index, facing, + OV2685_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor_common(sd); if (ret) { dev_err(dev, "v4l2 async register subdev failed\n"); - goto err_clean_entity; + goto clean_entity; } - pm_runtime_set_active(dev); pm_runtime_enable(dev); - pm_runtime_idle(dev); - return 0; -err_clean_entity: +clean_entity: #if defined(CONFIG_MEDIA_CONTROLLER) media_entity_cleanup(&ov2685->subdev.entity); #endif -err_power_off: - __ov2685_power_off(ov2685); -err_free_handler: +free_ctrl_handler: v4l2_ctrl_handler_free(&ov2685->ctrl_handler); -err_destroy_mutex: +destroy_mutex: mutex_destroy(&ov2685->mutex); return ret; @@ -803,37 +925,28 @@ err_destroy_mutex: static int ov2685_remove(struct i2c_client *client) { - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct ov2685 *ov2685 = to_ov2685(sd); + struct ov2685 *ov2685 = i2c_get_clientdata(client); - v4l2_async_unregister_subdev(sd); -#if defined(CONFIG_MEDIA_CONTROLLER) - media_entity_cleanup(&sd->entity); -#endif + __ov2685_power_off(ov2685); + v4l2_async_unregister_subdev(&ov2685->subdev); + media_entity_cleanup(&ov2685->subdev.entity); v4l2_ctrl_handler_free(&ov2685->ctrl_handler); mutex_destroy(&ov2685->mutex); - pm_runtime_disable(&client->dev); - if (!pm_runtime_status_suspended(&client->dev)) - __ov2685_power_off(ov2685); - pm_runtime_set_suspended(&client->dev); - return 0; } -#if IS_ENABLED(CONFIG_OF) static const struct of_device_id ov2685_of_match[] = { { .compatible = "ovti,ov2685" }, {}, }; -MODULE_DEVICE_TABLE(of, ov2685_of_match); -#endif static struct i2c_driver ov2685_i2c_driver = { .driver = { - .name = "ov2685", + .name = OV2685_NAME, + .owner = THIS_MODULE, .pm = &ov2685_pm_ops, - .of_match_table = of_match_ptr(ov2685_of_match), + .of_match_table = ov2685_of_match }, .probe = &ov2685_probe, .remove = &ov2685_remove, diff --git a/drivers/media/i2c/ov2718.c b/drivers/media/i2c/ov2718.c index 5eb44cc3a706..4ce432bd4a7e 100644 --- a/drivers/media/i2c/ov2718.c +++ b/drivers/media/i2c/ov2718.c @@ -3,6 +3,9 @@ * ov2718 driver * * Copyright (C) 2018 Fuzhou Rockchip Electronics Co., Ltd. + * + * V0.0X01.0X01 add poweron function. + * V0.0X01.0X02 fix mclk issue when probe multiple camera. */ #include @@ -14,6 +17,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -28,6 +34,8 @@ #include #include +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x02) + #ifndef V4L2_CID_DIGITAL_GAIN #define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN #endif @@ -89,6 +97,8 @@ #define OF_CAMERA_MODULE_REGULATORS "rockchip,regulator-names" #define OF_CAMERA_MODULE_REGULATOR_VOLTAGES "rockchip,regulator-voltages" +#define OV2718_NAME "ov2718" + struct ov2718_gpio { int pltfrm_gpio; const char *label; @@ -144,11 +154,16 @@ struct ov2718 { struct v4l2_ctrl *test_pattern; struct mutex mutex; bool streaming; + bool power_on; bool has_devnode; const struct ov2718_mode *cur_mode; const struct ov2718_mode *support_modes; u32 support_modes_num; struct ov2718_regulators regulators; + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; }; #define to_ov2718(sd) container_of(sd, struct ov2718, subdev) @@ -4275,6 +4290,16 @@ static void ov2718_get_hcg_reg(u32 gain, u32 *again_reg, u32 *dgain_reg) } } +static void ov2718_get_module_inf(struct ov2718 *ov2718, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strlcpy(inf->base.sensor, OV2718_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, ov2718->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, ov2718->len_name, sizeof(inf->base.lens)); +} + static long ov2718_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { struct ov2718 *ov2718 = to_ov2718(sd); @@ -4373,13 +4398,90 @@ static long ov2718_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) l_dgain, s_dgain); break; + case RKMODULE_GET_MODULE_INFO: + ov2718_get_module_inf(ov2718, (struct rkmodule_inf *)arg); + break; default: - return -ENOTTY; + return -ENOIOCTLCMD; } return ret; } +#ifdef CONFIG_COMPAT +static long ov2718_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_awb_cfg *cfg; + long ret; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = ov2718_ioctl(sd, cmd, inf); + if (!ret) + ret = copy_to_user(up, inf, sizeof(*inf)); + kfree(inf); + break; + case RKMODULE_AWB_CFG: + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(cfg, up, sizeof(*cfg)); + if (!ret) + ret = ov2718_ioctl(sd, cmd, cfg); + kfree(cfg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + +static int ov2718_s_power(struct v4l2_subdev *sd, int on) +{ + struct ov2718 *ov2718 = to_ov2718(sd); + struct i2c_client *client = ov2718->client; + int ret = 0; + + mutex_lock(&ov2718->mutex); + + /* If the power state is not modified - no work to do. */ + if (ov2718->power_on == !!on) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ov2718->power_on = true; + } else { + pm_runtime_put(&client->dev); + ov2718->power_on = false; + } + +unlock_and_return: + mutex_unlock(&ov2718->mutex); + + return ret; +} + /* Calculate the delay in us by clock rate and clock cycles */ static inline u32 ov2718_cal_delay(u32 cycles) { @@ -4400,13 +4502,16 @@ static int __ov2718_power_on(struct ov2718 *ov2718) if (ret < 0) dev_err(dev, "could not set pins\n"); } - + ret = clk_set_rate(ov2718->xvclk, OV2718_XVCLK_FREQ); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate (24MHz)\n"); + if (clk_get_rate(ov2718->xvclk) != OV2718_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); ret = clk_prepare_enable(ov2718->xvclk); if (ret < 0) { dev_err(dev, "Failed to enable xvclk\n"); return ret; } - if (ov2718->regulators.regulator) { for (i = 0; i < ov2718->regulators.cnt; i++) { regulator = ov2718->regulators.regulator + i; @@ -4554,7 +4659,11 @@ static const struct v4l2_subdev_pad_ops ov2718_pad_ops = { }; static const struct v4l2_subdev_core_ops ov2718_core_ops = { + .s_power = ov2718_s_power, .ioctl = ov2718_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = ov2718_compat_ioctl32, +#endif }; static const struct v4l2_subdev_ops ov2718_subdev_ops = { @@ -4785,6 +4894,10 @@ static int ov2718_check_sensor_id(struct ov2718 *ov2718, usleep_range(1000, 2000); continue; } + + if (id != CHIP_ID) + return -ENODEV; + dev_info(dev, "Detected OV%06x sensor\n", CHIP_ID); return 0; @@ -4792,7 +4905,6 @@ static int ov2718_check_sensor_id(struct ov2718 *ov2718, static int ov2718_analyze_dts(struct ov2718 *ov2718) { - int ret; int elem_size, elem_index; const char *str = ""; struct property *prop; @@ -4805,13 +4917,6 @@ static int ov2718_analyze_dts(struct ov2718 *ov2718) dev_err(dev, "Failed to get xvclk\n"); return -EINVAL; } - ret = clk_set_rate(ov2718->xvclk, OV2718_XVCLK_FREQ); - if (ret < 0) { - dev_err(dev, "Failed to set xvclk rate (24MHz)\n"); - return ret; - } - if (clk_get_rate(ov2718->xvclk) != OV2718_XVCLK_FREQ) - dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); ov2718->pinctrl = devm_pinctrl_get(dev); if (!IS_ERR(ov2718->pinctrl)) { @@ -4918,14 +5023,34 @@ static int ov2718_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; + struct device_node *node = dev->of_node; struct ov2718 *ov2718; struct v4l2_subdev *sd; + char facing[2]; int ret; + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + ov2718 = devm_kzalloc(dev, sizeof(*ov2718), GFP_KERNEL); if (!ov2718) return -ENOMEM; + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &ov2718->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &ov2718->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &ov2718->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &ov2718->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + ov2718->client = client; ret = ov2718_analyze_dts(ov2718); @@ -4963,18 +5088,28 @@ static int ov2718_probe(struct i2c_client *client, #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API sd->internal_ops = &ov2718_internal_ops; if (ov2718->has_devnode) - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; #endif #if defined(CONFIG_MEDIA_CONTROLLER) ov2718->pad.flags = MEDIA_PAD_FL_SOURCE; - sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; - ret = media_entity_init(&sd->entity, 1, &ov2718->pad, 0); + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &ov2718->pad); if (ret < 0) goto err_power_off; #endif if (ov2718->has_devnode) { - ret = v4l2_async_register_subdev(sd); + memset(facing, 0, sizeof(facing)); + if (strcmp(ov2718->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + ov2718->module_index, facing, + OV2718_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor_common(sd); if (ret) { dev_err(dev, "v4l2 async register subdev failed\n"); goto err_clean_entity; @@ -5037,7 +5172,7 @@ static const struct i2c_device_id ov2718_match_id[] = { static struct i2c_driver ov2718_i2c_driver = { .driver = { - .name = "ov2718", + .name = OV2718_NAME, .pm = &ov2718_pm_ops, .of_match_table = of_match_ptr(ov2718_of_match), }, diff --git a/drivers/media/i2c/ov2735.c b/drivers/media/i2c/ov2735.c index 4efe874c089b..977b10d24555 100644 --- a/drivers/media/i2c/ov2735.c +++ b/drivers/media/i2c/ov2735.c @@ -3,6 +3,9 @@ * ov2735 driver * * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd. + * + * V0.0X01.0X01 add poweron function. + * V0.0X01.0X02 fix mclk issue when probe multiple camera. */ #include @@ -14,11 +17,16 @@ #include #include #include +#include +#include +#include #include #include #include #include +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x02) + #ifndef V4L2_CID_DIGITAL_GAIN #define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN #endif @@ -85,6 +93,8 @@ #define ANALOG_GAIN_STEP 1 #define ANALOG_GAIN_DEFAULT 0x10 +#define OV2735_NAME "ov2735" + static const char * const ov2735_supply_names[] = { "avdd", /* Analog power */ "dovdd", /* Digital I/O power */ @@ -126,7 +136,12 @@ struct ov2735 { struct v4l2_ctrl *test_pattern; struct mutex mutex; bool streaming; + bool power_on; const struct ov2735_mode *cur_mode; + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; }; #define to_ov2735(sd) container_of(sd, struct ov2735, subdev) @@ -504,14 +519,80 @@ static int ov2735_enable_test_pattern(struct ov2735 *ov2735, u32 pattern) val); } +static void ov2735_get_module_inf(struct ov2735 *ov2735, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strlcpy(inf->base.sensor, OV2735_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, ov2735->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, ov2735->len_name, sizeof(inf->base.lens)); +} + +static long ov2735_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct ov2735 *ov2735 = to_ov2735(sd); + long ret = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + ov2735_get_module_inf(ov2735, (struct rkmodule_inf *)arg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long ov2735_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_awb_cfg *cfg; + long ret; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = ov2735_ioctl(sd, cmd, inf); + if (!ret) + ret = copy_to_user(up, inf, sizeof(*inf)); + kfree(inf); + break; + case RKMODULE_AWB_CFG: + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(cfg, up, sizeof(*cfg)); + if (!ret) + ret = ov2735_ioctl(sd, cmd, cfg); + kfree(cfg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + static int __ov2735_start_stream(struct ov2735 *ov2735) { int ret; - ret = ov2735_write_array(ov2735->client, ov2735_global_regs); - if (ret) - return ret; - mdelay(3); ret = ov2735_write_array(ov2735->client, ov2735->cur_mode->reg_list); if (ret) return ret; @@ -577,6 +658,44 @@ unlock_and_return: return ret; } +static int ov2735_s_power(struct v4l2_subdev *sd, int on) +{ + struct ov2735 *ov2735 = to_ov2735(sd); + struct i2c_client *client = ov2735->client; + int ret = 0; + + mutex_lock(&ov2735->mutex); + + /* If the power state is not modified - no work to do. */ + if (ov2735->power_on == !!on) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = ov2735_write_array(ov2735->client, ov2735_global_regs); + if (ret) { + v4l2_err(sd, "could not set init registers\n"); + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ov2735->power_on = true; + } else { + pm_runtime_put(&client->dev); + ov2735->power_on = false; + } + +unlock_and_return: + mutex_unlock(&ov2735->mutex); + + return ret; +} + /* Calculate the delay in us by clock rate and clock cycles */ static inline u32 ov2735_cal_delay(u32 cycles) { @@ -610,11 +729,14 @@ static int __ov2735_power_on(struct ov2735 *ov2735) gpiod_set_value_cansleep(ov2735->reset_gpio, 1); usleep_range(2000, 5000); } - if (!IS_ERR(ov2735->xvclk)) { - ret = clk_prepare_enable(ov2735->xvclk); - if (ret < 0) - dev_info(dev, "Failed to enable xvclk\n"); - } + ret = clk_set_rate(ov2735->xvclk, OV2735_XVCLK_FREQ); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate (24MHz)\n"); + if (clk_get_rate(ov2735->xvclk) != OV2735_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); + ret = clk_prepare_enable(ov2735->xvclk); + if (ret < 0) + dev_info(dev, "Failed to enable xvclk\n"); /* 8192 cycles prior to first SCCB transaction */ delay_us = ov2735_cal_delay(8192); @@ -691,6 +813,14 @@ static const struct v4l2_subdev_internal_ops ov2735_internal_ops = { }; #endif +static const struct v4l2_subdev_core_ops ov2735_core_ops = { + .s_power = ov2735_s_power, + .ioctl = ov2735_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = ov2735_compat_ioctl32, +#endif +}; + static const struct v4l2_subdev_video_ops ov2735_video_ops = { .s_stream = ov2735_s_stream, }; @@ -703,6 +833,7 @@ static const struct v4l2_subdev_pad_ops ov2735_pad_ops = { }; static const struct v4l2_subdev_ops ov2735_subdev_ops = { + .core = &ov2735_core_ops, .video = &ov2735_video_ops, .pad = &ov2735_pad_ops, }; @@ -852,7 +983,7 @@ static int ov2735_check_sensor_id(struct ov2735 *ov2735, ret = ov2735_write_reg(ov2735->client, PAGE_SELECT_REG, PAGE_ZERO); ret |= ov2735_read_reg(ov2735->client, OV2735_PIDH_ADDR, &pidh); ret |= ov2735_read_reg(ov2735->client, OV2735_PIDL_ADDR, &pidl); - if (IS_ERR_VALUE(ret)) { + if (ret) { dev_err(dev, "register read failed, camera module powered off?\n"); goto err; @@ -891,14 +1022,34 @@ static int ov2735_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; + struct device_node *node = dev->of_node; struct ov2735 *ov2735; struct v4l2_subdev *sd; + char facing[2]; int ret; + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + ov2735 = devm_kzalloc(dev, sizeof(*ov2735), GFP_KERNEL); if (!ov2735) return -ENOMEM; + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &ov2735->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &ov2735->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &ov2735->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &ov2735->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + ov2735->client = client; ov2735->cur_mode = &supported_modes[0]; @@ -907,13 +1058,6 @@ static int ov2735_probe(struct i2c_client *client, dev_err(dev, "Failed to get xvclk\n"); return -EINVAL; } - ret = clk_set_rate(ov2735->xvclk, OV2735_XVCLK_FREQ); - if (ret < 0) { - dev_err(dev, "Failed to set xvclk rate (24MHz)\n"); - return ret; - } - if (clk_get_rate(ov2735->xvclk) != OV2735_XVCLK_FREQ) - dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); ov2735->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(ov2735->reset_gpio)) @@ -947,17 +1091,27 @@ static int ov2735_probe(struct i2c_client *client, #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API sd->internal_ops = &ov2735_internal_ops; - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; #endif #if defined(CONFIG_MEDIA_CONTROLLER) ov2735->pad.flags = MEDIA_PAD_FL_SOURCE; - sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; - ret = media_entity_init(&sd->entity, 1, &ov2735->pad, 0); + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &ov2735->pad); if (ret < 0) goto err_power_off; #endif - ret = v4l2_async_register_subdev(sd); + memset(facing, 0, sizeof(facing)); + if (strcmp(ov2735->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + ov2735->module_index, facing, + OV2735_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor_common(sd); if (ret) { dev_err(dev, "v4l2 async register subdev failed\n"); goto err_clean_entity; @@ -1018,7 +1172,7 @@ static const struct i2c_device_id ov2735_match_id[] = { static struct i2c_driver ov2735_i2c_driver = { .driver = { - .name = "ov2735", + .name = OV2735_NAME, .pm = &ov2735_pm_ops, .of_match_table = of_match_ptr(ov2735_of_match), }, @@ -1041,4 +1195,5 @@ device_initcall_sync(sensor_mod_init); module_exit(sensor_mod_exit); MODULE_DESCRIPTION("OmniVision ov2735 sensor driver"); -MODULE_LICENSE("GPL v2"); \ No newline at end of file +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/media/i2c/ov4689.c b/drivers/media/i2c/ov4689.c index a87650f645de..d9fdb744b729 100644 --- a/drivers/media/i2c/ov4689.c +++ b/drivers/media/i2c/ov4689.c @@ -3,6 +3,10 @@ * ov4689 driver * * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd. + * + * V0.0X01.0X01 add poweron function. + * V0.0X01.0X02 fix mclk issue when probe multiple camera. + * V0.0X01.0X03 fix gain range. */ #include @@ -14,12 +18,17 @@ #include #include #include +#include +#include +#include #include #include #include #include #include +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x03) + #ifndef V4L2_CID_DIGITAL_GAIN #define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN #endif @@ -46,10 +55,10 @@ #define OV4689_GAIN_H_MASK 0x07 #define OV4689_GAIN_H_SHIFT 8 #define OV4689_GAIN_L_MASK 0xff -#define OV4689_GAIN_MIN 0x10 -#define OV4689_GAIN_MAX 0xf8 +#define OV4689_GAIN_MIN 0x80 +#define OV4689_GAIN_MAX 0x7f8 #define OV4689_GAIN_STEP 1 -#define OV4689_GAIN_DEFAULT 0x10 +#define OV4689_GAIN_DEFAULT 0x80 #define OV4689_REG_TEST_PATTERN 0x5040 #define OV4689_TEST_PATTERN_ENABLE 0x80 @@ -69,6 +78,8 @@ #define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default" #define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep" +#define OV4689_NAME "ov4689" + static const char * const ov4689_supply_names[] = { "avdd", /* Analog power */ "dovdd", /* Digital I/O power */ @@ -114,7 +125,12 @@ struct ov4689 { struct v4l2_ctrl *test_pattern; struct mutex mutex; bool streaming; + bool power_on; const struct ov4689_mode *cur_mode; + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; }; #define to_ov4689(sd) container_of(sd, struct ov4689, subdev) @@ -635,14 +651,80 @@ static int ov4689_g_frame_interval(struct v4l2_subdev *sd, return 0; } +static void ov4689_get_module_inf(struct ov4689 *ov4689, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strlcpy(inf->base.sensor, OV4689_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, ov4689->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, ov4689->len_name, sizeof(inf->base.lens)); +} + +static long ov4689_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct ov4689 *ov4689 = to_ov4689(sd); + long ret = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + ov4689_get_module_inf(ov4689, (struct rkmodule_inf *)arg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long ov4689_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_awb_cfg *cfg; + long ret; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = ov4689_ioctl(sd, cmd, inf); + if (!ret) + ret = copy_to_user(up, inf, sizeof(*inf)); + kfree(inf); + break; + case RKMODULE_AWB_CFG: + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(cfg, up, sizeof(*cfg)); + if (!ret) + ret = ov4689_ioctl(sd, cmd, cfg); + kfree(cfg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + static int __ov4689_start_stream(struct ov4689 *ov4689) { int ret; - ret = ov4689_write_array(ov4689->client, ov4689_global_regs); - if (ret) - return ret; - ret = ov4689_write_array(ov4689->client, ov4689->cur_mode->reg_list); if (ret) return ret; @@ -701,6 +783,44 @@ unlock_and_return: return ret; } +static int ov4689_s_power(struct v4l2_subdev *sd, int on) +{ + struct ov4689 *ov4689 = to_ov4689(sd); + struct i2c_client *client = ov4689->client; + int ret = 0; + + mutex_lock(&ov4689->mutex); + + /* If the power state is not modified - no work to do. */ + if (ov4689->power_on == !!on) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = ov4689_write_array(ov4689->client, ov4689_global_regs); + if (ret) { + v4l2_err(sd, "could not set init registers\n"); + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ov4689->power_on = true; + } else { + pm_runtime_put(&client->dev); + ov4689->power_on = false; + } + +unlock_and_return: + mutex_unlock(&ov4689->mutex); + + return ret; +} + /* Calculate the delay in us by clock rate and clock cycles */ static inline u32 ov4689_cal_delay(u32 cycles) { @@ -719,13 +839,16 @@ static int __ov4689_power_on(struct ov4689 *ov4689) if (ret < 0) dev_err(dev, "could not set pins\n"); } - + ret = clk_set_rate(ov4689->xvclk, OV4689_XVCLK_FREQ); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate (24MHz)\n"); + if (clk_get_rate(ov4689->xvclk) != OV4689_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); ret = clk_prepare_enable(ov4689->xvclk); if (ret < 0) { dev_err(dev, "Failed to enable xvclk\n"); return ret; } - if (!IS_ERR(ov4689->reset_gpio)) gpiod_set_value_cansleep(ov4689->reset_gpio, 0); @@ -826,6 +949,14 @@ static const struct v4l2_subdev_internal_ops ov4689_internal_ops = { }; #endif +static const struct v4l2_subdev_core_ops ov4689_core_ops = { + .s_power = ov4689_s_power, + .ioctl = ov4689_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = ov4689_compat_ioctl32, +#endif +}; + static const struct v4l2_subdev_video_ops ov4689_video_ops = { .s_stream = ov4689_s_stream, .g_frame_interval = ov4689_g_frame_interval, @@ -839,6 +970,7 @@ static const struct v4l2_subdev_pad_ops ov4689_pad_ops = { }; static const struct v4l2_subdev_ops ov4689_subdev_ops = { + .core = &ov4689_core_ops, .video = &ov4689_video_ops, .pad = &ov4689_pad_ops, }; @@ -983,7 +1115,7 @@ static int ov4689_check_sensor_id(struct ov4689 *ov4689, OV4689_REG_VALUE_16BIT, &id); if (id != CHIP_ID) { dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret); - return ret; + return -ENODEV; } dev_info(dev, "Detected OV%06x sensor\n", CHIP_ID); @@ -1007,14 +1139,34 @@ static int ov4689_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; + struct device_node *node = dev->of_node; struct ov4689 *ov4689; struct v4l2_subdev *sd; + char facing[2]; int ret; + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + ov4689 = devm_kzalloc(dev, sizeof(*ov4689), GFP_KERNEL); if (!ov4689) return -ENOMEM; + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &ov4689->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &ov4689->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &ov4689->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &ov4689->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + ov4689->client = client; ov4689->cur_mode = &supported_modes[0]; @@ -1023,13 +1175,6 @@ static int ov4689_probe(struct i2c_client *client, dev_err(dev, "Failed to get xvclk\n"); return -EINVAL; } - ret = clk_set_rate(ov4689->xvclk, OV4689_XVCLK_FREQ); - if (ret < 0) { - dev_err(dev, "Failed to set xvclk rate (24MHz)\n"); - return ret; - } - if (clk_get_rate(ov4689->xvclk) != OV4689_XVCLK_FREQ) - dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); ov4689->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(ov4689->reset_gpio)) @@ -1080,17 +1225,27 @@ static int ov4689_probe(struct i2c_client *client, #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API sd->internal_ops = &ov4689_internal_ops; - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; #endif #if defined(CONFIG_MEDIA_CONTROLLER) ov4689->pad.flags = MEDIA_PAD_FL_SOURCE; - sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; - ret = media_entity_init(&sd->entity, 1, &ov4689->pad, 0); + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &ov4689->pad); if (ret < 0) goto err_power_off; #endif - ret = v4l2_async_register_subdev(sd); + memset(facing, 0, sizeof(facing)); + if (strcmp(ov4689->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + ov4689->module_index, facing, + OV4689_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor_common(sd); if (ret) { dev_err(dev, "v4l2 async register subdev failed\n"); goto err_clean_entity; @@ -1151,7 +1306,7 @@ static const struct i2c_device_id ov4689_match_id[] = { static struct i2c_driver ov4689_i2c_driver = { .driver = { - .name = "ov4689", + .name = OV4689_NAME, .pm = &ov4689_pm_ops, .of_match_table = of_match_ptr(ov4689_of_match), }, diff --git a/drivers/media/i2c/ov5648.c b/drivers/media/i2c/ov5648.c new file mode 100644 index 000000000000..b71576d47444 --- /dev/null +++ b/drivers/media/i2c/ov5648.c @@ -0,0 +1,1506 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ov5648 driver + * + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd. + * + * V0.0X01.0X01 add poweron function. + * V0.0X01.0X02 fix mclk issue when probe multiple camera. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* verify default register values */ +//#define CHECK_REG_VALUE + +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x02) + +#ifndef V4L2_CID_DIGITAL_GAIN +#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN +#endif + +/* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ +#define MIPI_FREQ 210000000U +#define OV5648_PIXEL_RATE (210000000LL * 2LL * 2LL / 10) +#define OV5648_XVCLK_FREQ 24000000 + +#define CHIP_ID 0x5648 +#define OV5648_REG_CHIP_ID 0x300a + +#define OV5648_REG_CTRL_MODE 0x0100 +#define OV5648_MODE_SW_STANDBY 0x00 +#define OV5648_MODE_STREAMING 0x01 + +#define OV5648_REG_EXPOSURE 0x3500 +#define OV5648_EXPOSURE_MIN 4 +#define OV5648_EXPOSURE_STEP 1 +#define OV5648_VTS_MAX 0x7fff + +#define OV5648_REG_ANALOG_GAIN 0x3509 +#define ANALOG_GAIN_MIN 0x10 +#define ANALOG_GAIN_MAX 0xf8 +#define ANALOG_GAIN_STEP 1 +#define ANALOG_GAIN_DEFAULT 0xf8 + +#define OV5648_REG_GAIN_H 0x350a +#define OV5648_REG_GAIN_L 0x350b +#define OV5648_GAIN_L_MASK 0xff +#define OV5648_GAIN_H_MASK 0x03 +#define OV5648_DIGI_GAIN_H_SHIFT 8 +#define OV5648_DIGI_GAIN_MIN 0 +#define OV5648_DIGI_GAIN_MAX (0x4000 - 1) +#define OV5648_DIGI_GAIN_STEP 1 +#define OV5648_DIGI_GAIN_DEFAULT 1024 + +#define OV5648_REG_TEST_PATTERN 0x503d +#define OV5648_TEST_PATTERN_ENABLE 0x80 +#define OV5648_TEST_PATTERN_DISABLE 0x0 + +#define OV5648_REG_VTS 0x380e + +#define REG_NULL 0xFFFF + +#define OV5648_REG_VALUE_08BIT 1 +#define OV5648_REG_VALUE_16BIT 2 +#define OV5648_REG_VALUE_24BIT 3 + +#define OV5648_LANES 2 +#define OV5648_BITS_PER_SAMPLE 10 + +#define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default" +#define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep" + +#define OV5648_NAME "ov5648" + +static const char * const ov5648_supply_names[] = { + "avdd", /* Analog power */ + "dovdd", /* Digital I/O power */ + "dvdd", /* Digital core power */ +}; + +#define OV5648_NUM_SUPPLIES ARRAY_SIZE(ov5648_supply_names) + +struct regval { + u16 addr; + u8 val; +}; + +struct ov5648_mode { + u32 width; + u32 height; + struct v4l2_fract max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + const struct regval *reg_list; +}; + +struct ov5648 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *power_gpio; + struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; + struct regulator_bulk_data supplies[OV5648_NUM_SUPPLIES]; + + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_sleep; + + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *anal_gain; + struct v4l2_ctrl *digi_gain; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *test_pattern; + struct mutex mutex; + bool streaming; + bool power_on; + const struct ov5648_mode *cur_mode; + unsigned int lane_num; + unsigned int cfg_num; + unsigned int pixel_rate; + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; +}; + +#define to_ov5648(sd) container_of(sd, struct ov5648, subdev) + +/* + * Xclk 24Mhz + * Pclk 84Mhz + * linelength 2816(0xb00) + * framelength 1984(0x7c0) + * grabwindow_width 2592 + * grabwindow_height 1944 + * max_framerate 15fps + * mipi_datarate per lane 420Mbps + */ +static const struct regval ov5648_global_regs[] = { + {0x0100, 0x00}, + {0x3001, 0x00}, + {0x3002, 0x00}, + {0x3011, 0x02}, + {0x3017, 0x05}, + {0x3018, 0x4c}, //bit[7:5] 001: 1lane;010: 2lane + {0x301c, 0xd2}, + {0x3022, 0x00}, + {0x3034, 0x1a}, + {0x3035, 0x21}, + {0x3036, 0x69}, + {0x3037, 0x03}, + {0x3038, 0x00}, + {0x3039, 0x00}, + {0x303a, 0x00}, + {0x303b, 0x19}, + {0x303c, 0x11}, + {0x303d, 0x30}, + {0x3105, 0x11}, + {0x3106, 0x05}, + {0x3304, 0x28}, + {0x3305, 0x41}, + {0x3306, 0x30}, + {0x3308, 0x00}, + {0x3309, 0xc8}, + {0x330a, 0x01}, + {0x330b, 0x90}, + {0x330c, 0x02}, + {0x330d, 0x58}, + {0x330e, 0x03}, + {0x330f, 0x20}, + {0x3300, 0x00}, + {0x3500, 0x00}, + {0x3501, 0x3d}, + {0x3502, 0x00}, + {0x3503, 0x07}, + {0x350a, 0x00}, + {0x350b, 0x40}, + {0x3601, 0x33}, + {0x3602, 0x00}, + {0x3611, 0x0e}, + {0x3612, 0x2b}, + {0x3614, 0x50}, + + {0x3620, 0x33}, + {0x3622, 0x00}, + {0x3630, 0xad}, + {0x3631, 0x00}, + {0x3632, 0x94}, + {0x3633, 0x17}, + {0x3634, 0x14}, + {0x3704, 0xc0}, + {0x3705, 0x2a}, + {0x3708, 0x66}, + {0x3709, 0x52}, + {0x370b, 0x23}, + {0x370c, 0xcf}, + {0x370d, 0x00}, + {0x370e, 0x00}, + {0x371c, 0x07}, + {0x3739, 0xd2}, + {0x373c, 0x00}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x00}, + {0x3804, 0x0a}, + {0x3805, 0x3f}, + {0x3806, 0x07}, + {0x3807, 0xa3}, + {0x3808, 0x05}, + {0x3809, 0x10}, + {0x380a, 0x03}, + {0x380b, 0xcc}, + {0x380c, 0x0b}, + {0x380d, 0x00}, + {0x380e, 0x03}, + {0x380f, 0xe0}, + {0x3810, 0x00}, + {0x3811, 0x08}, + {0x3812, 0x00}, + {0x3813, 0x04}, + {0x3814, 0x31}, + {0x3815, 0x31}, + {0x3817, 0x00}, + {0x3820, 0x08}, + {0x3821, 0x07}, + {0x3826, 0x03}, + {0x3829, 0x00}, + {0x382b, 0x0b}, + {0x3830, 0x00}, + {0x3836, 0x00}, + {0x3837, 0x00}, + {0x3838, 0x00}, + {0x3839, 0x04}, + {0x383a, 0x00}, + {0x383b, 0x01}, + {0x3b00, 0x00}, + {0x3b02, 0x08}, + {0x3b03, 0x00}, + {0x3b04, 0x04}, + + {0x3b05, 0x00}, + {0x3b06, 0x04}, + {0x3b07, 0x08}, + {0x3b08, 0x00}, + {0x3b09, 0x02}, + {0x3b0a, 0x04}, + {0x3b0b, 0x00}, + {0x3b0c, 0x3d}, + {0x3f01, 0x0d}, + {0x3f0f, 0xf5}, + {0x4000, 0x89}, + {0x4001, 0x02}, + {0x4002, 0x45}, + {0x4004, 0x02}, + {0x4005, 0x18}, + {0x4006, 0x08}, + {0x4007, 0x10}, + {0x4008, 0x00}, + {0x4050, 0x6e}, + {0x4051, 0x8f}, + {0x4300, 0xf8}, + {0x4303, 0xff}, + {0x4304, 0x00}, + {0x4307, 0xff}, + {0x4520, 0x00}, + {0x4521, 0x00}, + {0x4511, 0x22}, + {0x4801, 0x0f}, + {0x4814, 0x2a}, + {0x481f, 0x3c}, + {0x4823, 0x3c}, + {0x4826, 0x00}, + {0x481b, 0x3c}, + {0x4827, 0x32}, + {0x4837, 0x18}, + {0x4b00, 0x06}, + {0x4b01, 0x0a}, + {0x4b04, 0x10}, + {0x5000, 0xff}, + {0x5001, 0x00}, + {0x5002, 0x41}, + {0x5003, 0x0a}, + {0x5004, 0x00}, + {0x5043, 0x00}, + {0x5013, 0x00}, + {0x501f, 0x03}, + {0x503d, 0x00}, + {0x5780, 0xfc}, + {0x5781, 0x1f}, + {0x5782, 0x03}, + {0x5786, 0x20}, + {0x5787, 0x40}, + {0x5788, 0x08}, + {0x5789, 0x08}, + {0x578a, 0x02}, + {0x578b, 0x01}, + {0x578c, 0x01}, + + {0x578d, 0x0c}, + {0x578e, 0x02}, + {0x578f, 0x01}, + {0x5790, 0x01}, + {0x5a00, 0x08}, + {0x5b00, 0x01}, + {0x5b01, 0x40}, + {0x5b02, 0x00}, + {0x5b03, 0xf0}, + //{0x0100, 0x01}, + + {REG_NULL, 0x00}, +}; + +/* + * Xclk 24Mhz + * Pclk 84Mhz + * linelength 2816(0xb00) + * framelength 1984(0x7c0) + * grabwindow_width 2592 + * grabwindow_height 1944 + * max_framerate 15fps + * mipi_datarate per lane 420Mbps + */ +static const struct regval ov5648_2592x1944_regs[] = { + // 2592x1944 15fps 2 lane MIPI 420Mbps/lane + {0x0100, 0x00}, + {0x3501, 0x7b}, // exposure + {0x2502, 0x00}, // exposure + {0x3708, 0x63}, + {0x3709, 0x12}, + {0x370c, 0xcc}, // changed by AM05d + {0x3800, 0x00}, // xstart = 0 + {0x3801, 0x00}, // xstart + {0x3802, 0x00}, // ystart = 0 + {0x3803, 0x00}, // ystart + {0x3804, 0x0a}, // xend = 2623 + {0x3805, 0x3f}, // xend + {0x3806, 0x07}, // yend = 1955 + {0x3807, 0xa3}, // yend + {0x3808, 0x0a}, // x output size = 2592 + {0x3809, 0x20}, // x output size + {0x380a, 0x07}, // y output size = 1944 + {0x380b, 0x98}, // y output size + + {0x380c, 0x0b}, // hts = 2816 + {0x380d, 0x00}, // hts + {0x380e, 0x07}, // vts = 1984 + {0x380f, 0xc0}, // vts + {0x3810, 0x00}, // isp x win = 16 + {0x3811, 0x10}, // isp x win + {0x3812, 0x00}, // isp y win = 6 + {0x3813, 0x06}, // isp y win + {0x3814, 0x11}, // x inc + {0x3815, 0x11}, // y inc + {0x3817, 0x00}, // hsync start + {0x3820, 0x40}, // flip off, v bin off + {0x3821, 0x06}, // mirror on, v bin off + {0x4004, 0x04}, // black line number + {0x4005, 0x1a}, // blc always update + {0x350b, 0x40}, // gain = 4x + {0x4837, 0x17}, // MIPI global timing + //{0x0100, 0x01}, + + {REG_NULL, 0x00}, +}; + +/* + * Xclk 24Mhz + * Pclk 84Mhz + * linelength 2816(0xb00) + * framelength 992(0x3e0) + * grabwindow_width 1296 + * grabwindow_height 972 + * max_framerate 30fps + * mipi_datarate per lane 420Mbps + */ +static const struct regval ov5648_1296x972_regs[] = { + // 1296x972 30fps 2 lane MIPI 420Mbps/lane + {0x0100, 0x00}, + {0x3501, 0x3d}, // exposure + {0x3502, 0x00}, // exposure + {0x3708, 0x66}, + {0x3709, 0x52}, + {0x370c, 0xcf}, + {0x3800, 0x00}, // xstart = 0 + {0x3801, 0x00}, // x start + {0x3802, 0x00}, // y start = 0 + {0x3803, 0x00}, // y start + {0x3804, 0x0a}, // xend = 2623 + {0x3805, 0x3f}, // xend + {0x3806, 0x07}, // yend = 1955 + {0x3807, 0xa3}, // yend + {0x3808, 0x05}, // x output size = 1296 + {0x3809, 0x10}, // x output size + {0x380a, 0x03}, // y output size = 972 + {0x380b, 0xcc}, // y output size + + {0x380c, 0x0b}, // hts = 2816 + {0x380d, 0x00}, // hts + {0x380e, 0x03}, // vts = 992 + {0x380f, 0xe0}, // vts + {0x3810, 0x00}, // isp x win = 8 + {0x3811, 0x08}, // isp x win + {0x3812, 0x00}, // isp y win = 4 + {0x3813, 0x04}, // isp y win + {0x3814, 0x31}, // x inc + {0x3815, 0x31}, // y inc + {0x3817, 0x00}, // hsync start + {0x3820, 0x08}, // flip off, v bin off + {0x3821, 0x07}, // mirror on, h bin on + {0x4004, 0x02}, // black line number + {0x4005, 0x18}, // blc level trigger + {0x350b, 0x80}, // gain = 8x + {0x4837, 0x17}, // MIPI global timing + //{0x0100, 0x01}, + + {REG_NULL, 0x00} +}; + +static const struct ov5648_mode supported_modes_2lane[] = { + { + .width = 2592, + .height = 1944, + .max_fps = { + .numerator = 10000, + .denominator = 150000, + }, + .exp_def = 0x0450, + .hts_def = 0x0b00, + .vts_def = 0x07c0, + .reg_list = ov5648_2592x1944_regs, + }, + { + .width = 1296, + .height = 972, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x03d0, + .hts_def = 0x0b00, + .vts_def = 0x03e0, + .reg_list = ov5648_1296x972_regs, + }, +}; + +static const struct ov5648_mode *supported_modes; + +static const s64 link_freq_menu_items[] = { + MIPI_FREQ +}; + +static const char * const ov5648_test_pattern_menu[] = { + "Disabled", + "Vertical Color Bar Type 1", + "Vertical Color Bar Type 2", + "Vertical Color Bar Type 3", + "Vertical Color Bar Type 4" +}; + +/* Write registers up to 4 at a time */ +static int ov5648_write_reg(struct i2c_client *client, u16 reg, + u32 len, u32 val) +{ + u32 buf_i, val_i; + u8 buf[6]; + u8 *val_p; + __be32 val_be; + + //dev_info(&client->dev, "%s(%d) enter!\n", __func__, __LINE__); + //dev_info(&client->dev, "write reg(0x%x val:0x%x)!\n", reg, val); + + if (len > 4) + return -EINVAL; + + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + + val_be = cpu_to_be32(val); + val_p = (u8 *)&val_be; + buf_i = 2; + val_i = 4 - len; + + while (val_i < 4) + buf[buf_i++] = val_p[val_i++]; + + if (i2c_master_send(client, buf, len + 2) != len + 2) { + dev_err(&client->dev, + "write reg(0x%x val:0x%x)failed !\n", reg, val); + return -EIO; + } + return 0; +} + +static int ov5648_write_array(struct i2c_client *client, + const struct regval *regs) +{ + u32 i; + int ret = 0; + + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) + ret = ov5648_write_reg(client, regs[i].addr, + OV5648_REG_VALUE_08BIT, regs[i].val); + + return ret; +} + +/* Read registers up to 4 at a time */ +static int ov5648_read_reg(struct i2c_client *client, u16 reg, + unsigned int len, u32 *val) +{ + struct i2c_msg msgs[2]; + u8 *data_be_p; + __be32 data_be = 0; + __be16 reg_addr_be = cpu_to_be16(reg); + int ret; + + if (len > 4 || !len) + return -EINVAL; + + data_be_p = (u8 *)&data_be; + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 2; + msgs[0].buf = (u8 *)®_addr_be; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_be_p[4 - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = be32_to_cpu(data_be); + + return 0; +} + +/* Check Register value */ +#ifdef CHECK_REG_VALUE +static int ov5648_reg_verify(struct i2c_client *client, + const struct regval *regs) +{ + u32 i; + int ret = 0; + u32 value; + + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) { + ret = ov5648_read_reg(client, regs[i].addr, + OV5648_REG_VALUE_08BIT, &value); + if (value != regs[i].val) { + dev_info(&client->dev, "%s:0x%04x is 0x%08x \ + instead of 0x%08x\n", __func__, + regs[i].addr, value, regs[i].val); + } + } + return ret; +} +#endif + +static int ov5648_get_reso_dist(const struct ov5648_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); +} + +static const struct ov5648_mode * +ov5648_find_best_fit(struct ov5648 *ov5648, + struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *framefmt = &fmt->format; + int dist; + int cur_best_fit = 0; + int cur_best_fit_dist = -1; + int i; + + for (i = 0; i < ov5648->cfg_num; i++) { + dist = ov5648_get_reso_dist(&supported_modes[i], framefmt); + if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { + cur_best_fit_dist = dist; + cur_best_fit = i; + } + } + + return &supported_modes[cur_best_fit]; +} + +static int ov5648_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ov5648 *ov5648 = to_ov5648(sd); + const struct ov5648_mode *mode; + s64 h_blank, vblank_def; + + mutex_lock(&ov5648->mutex); + + mode = ov5648_find_best_fit(ov5648, fmt); + fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10; + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; +#else + mutex_unlock(&ov5648->mutex); + return -ENOTTY; +#endif + } else { + ov5648->cur_mode = mode; + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(ov5648->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(ov5648->vblank, vblank_def, + OV5648_VTS_MAX - mode->height, + 1, vblank_def); + } + + mutex_unlock(&ov5648->mutex); + + return 0; +} + +static int ov5648_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ov5648 *ov5648 = to_ov5648(sd); + const struct ov5648_mode *mode = ov5648->cur_mode; + + mutex_lock(&ov5648->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); +#else + mutex_unlock(&ov5648->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10; + fmt->format.field = V4L2_FIELD_NONE; + } + mutex_unlock(&ov5648->mutex); + + return 0; +} + +static int ov5648_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index != 0) + return -EINVAL; + code->code = MEDIA_BUS_FMT_SBGGR10_1X10; + + return 0; +} + +static int ov5648_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct ov5648 *ov5648 = to_ov5648(sd); + + if (fse->index >= ov5648->cfg_num) + return -EINVAL; + + if (fse->code != MEDIA_BUS_FMT_SBGGR10_1X10) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = supported_modes[fse->index].width; + fse->max_height = supported_modes[fse->index].height; + fse->min_height = supported_modes[fse->index].height; + + return 0; +} + +static int ov5648_enable_test_pattern(struct ov5648 *ov5648, u32 pattern) +{ + u32 val; + + if (pattern) + val = (pattern - 1) | OV5648_TEST_PATTERN_ENABLE; + else + val = OV5648_TEST_PATTERN_DISABLE; + + return ov5648_write_reg(ov5648->client, OV5648_REG_TEST_PATTERN, + OV5648_REG_VALUE_08BIT, val); +} + +static int ov5648_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct ov5648 *ov5648 = to_ov5648(sd); + const struct ov5648_mode *mode = ov5648->cur_mode; + + mutex_lock(&ov5648->mutex); + fi->interval = mode->max_fps; + mutex_unlock(&ov5648->mutex); + + return 0; +} + +static void ov5648_get_module_inf(struct ov5648 *ov5648, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strlcpy(inf->base.sensor, OV5648_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, ov5648->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, ov5648->len_name, sizeof(inf->base.lens)); +} + +static long ov5648_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct ov5648 *ov5648 = to_ov5648(sd); + long ret = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + ov5648_get_module_inf(ov5648, (struct rkmodule_inf *)arg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long ov5648_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_awb_cfg *cfg; + long ret; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = ov5648_ioctl(sd, cmd, inf); + if (!ret) + ret = copy_to_user(up, inf, sizeof(*inf)); + kfree(inf); + break; + case RKMODULE_AWB_CFG: + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(cfg, up, sizeof(*cfg)); + if (!ret) + ret = ov5648_ioctl(sd, cmd, cfg); + kfree(cfg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + +static int __ov5648_start_stream(struct ov5648 *ov5648) +{ + int ret; + + ret = ov5648_write_array(ov5648->client, ov5648->cur_mode->reg_list); + if (ret) + return ret; + +#ifdef CHECK_REG_VALUE + usleep_range(10000, 20000); + /* verify default values to make sure everything has */ + /* been written correctly as expected */ + dev_info(&ov5648->client->dev, "%s:Check register value!\n", + __func__); + ret = ov5648_reg_verify(ov5648->client, ov5648_global_regs); + if (ret) + return ret; + + ret = ov5648_reg_verify(ov5648->client, ov5648->cur_mode->reg_list); + if (ret) + return ret; +#endif + + /* In case these controls are set before streaming */ + mutex_unlock(&ov5648->mutex); + ret = v4l2_ctrl_handler_setup(&ov5648->ctrl_handler); + mutex_lock(&ov5648->mutex); + if (ret) + return ret; + ret = ov5648_write_reg(ov5648->client, OV5648_REG_CTRL_MODE, + OV5648_REG_VALUE_08BIT, OV5648_MODE_STREAMING); + return ret; +} + +static int __ov5648_stop_stream(struct ov5648 *ov5648) +{ + return ov5648_write_reg(ov5648->client, OV5648_REG_CTRL_MODE, + OV5648_REG_VALUE_08BIT, OV5648_MODE_SW_STANDBY); +} + +static int ov5648_s_stream(struct v4l2_subdev *sd, int on) +{ + struct ov5648 *ov5648 = to_ov5648(sd); + struct i2c_client *client = ov5648->client; + int ret = 0; + + dev_info(&client->dev, "%s(%d) enter!\n", __func__, __LINE__); + mutex_lock(&ov5648->mutex); + on = !!on; + if (on == ov5648->streaming) + goto unlock_and_return; + + if (on) { + dev_info(&client->dev, "stream on!!!\n"); + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = __ov5648_start_stream(ov5648); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + dev_info(&client->dev, "stream off!!!\n"); + __ov5648_stop_stream(ov5648); + pm_runtime_put(&client->dev); + } + + ov5648->streaming = on; + +unlock_and_return: + mutex_unlock(&ov5648->mutex); + + return ret; +} + +static int ov5648_s_power(struct v4l2_subdev *sd, int on) +{ + struct ov5648 *ov5648 = to_ov5648(sd); + struct i2c_client *client = ov5648->client; + int ret = 0; + + mutex_lock(&ov5648->mutex); + + /* If the power state is not modified - no work to do. */ + if (ov5648->power_on == !!on) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = ov5648_write_array(ov5648->client, ov5648_global_regs); + if (ret) { + v4l2_err(sd, "could not set init registers\n"); + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ov5648->power_on = true; + } else { + pm_runtime_put(&client->dev); + ov5648->power_on = false; + } + +unlock_and_return: + mutex_unlock(&ov5648->mutex); + + return ret; +} + +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 ov5648_cal_delay(u32 cycles) +{ + return DIV_ROUND_UP(cycles, OV5648_XVCLK_FREQ / 1000 / 1000); +} + +static int __ov5648_power_on(struct ov5648 *ov5648) +{ + int ret; + u32 delay_us; + struct device *dev = &ov5648->client->dev; + + if (!IS_ERR(ov5648->power_gpio)) + gpiod_set_value_cansleep(ov5648->power_gpio, 1); + + usleep_range(1000, 2000); + + if (!IS_ERR_OR_NULL(ov5648->pins_default)) { + ret = pinctrl_select_state(ov5648->pinctrl, + ov5648->pins_default); + if (ret < 0) + dev_err(dev, "could not set pins\n"); + } + ret = clk_set_rate(ov5648->xvclk, OV5648_XVCLK_FREQ); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate (24MHz)\n"); + if (clk_get_rate(ov5648->xvclk) != OV5648_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); + ret = clk_prepare_enable(ov5648->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + if (!IS_ERR(ov5648->reset_gpio)) + gpiod_set_value_cansleep(ov5648->reset_gpio, 1); + + ret = regulator_bulk_enable(OV5648_NUM_SUPPLIES, ov5648->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + + if (!IS_ERR(ov5648->reset_gpio)) + gpiod_set_value_cansleep(ov5648->reset_gpio, 0); + + if (!IS_ERR(ov5648->pwdn_gpio)) + gpiod_set_value_cansleep(ov5648->pwdn_gpio, 1); + + /* 8192 cycles prior to first SCCB transaction */ + delay_us = ov5648_cal_delay(8192); + usleep_range(delay_us, delay_us * 2); + + return 0; + +disable_clk: + clk_disable_unprepare(ov5648->xvclk); + + return ret; +} + +static void __ov5648_power_off(struct ov5648 *ov5648) +{ + int ret; + struct device *dev = &ov5648->client->dev; + + if (!IS_ERR(ov5648->pwdn_gpio)) + gpiod_set_value_cansleep(ov5648->pwdn_gpio, 0); + clk_disable_unprepare(ov5648->xvclk); + if (!IS_ERR(ov5648->reset_gpio)) + gpiod_set_value_cansleep(ov5648->reset_gpio, 1); + if (!IS_ERR_OR_NULL(ov5648->pins_sleep)) { + ret = pinctrl_select_state(ov5648->pinctrl, + ov5648->pins_sleep); + if (ret < 0) + dev_dbg(dev, "could not set pins\n"); + } + if (!IS_ERR(ov5648->power_gpio)) + gpiod_set_value_cansleep(ov5648->power_gpio, 0); + + regulator_bulk_disable(OV5648_NUM_SUPPLIES, ov5648->supplies); +} + +static int ov5648_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov5648 *ov5648 = to_ov5648(sd); + + return __ov5648_power_on(ov5648); +} + +static int ov5648_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov5648 *ov5648 = to_ov5648(sd); + + __ov5648_power_off(ov5648); + + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int ov5648_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct ov5648 *ov5648 = to_ov5648(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + const struct ov5648_mode *def_mode = &supported_modes[0]; + + mutex_lock(&ov5648->mutex); + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10; + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&ov5648->mutex); + /* No crop or compose */ + + return 0; +} +#endif + +static const struct dev_pm_ops ov5648_pm_ops = { + SET_RUNTIME_PM_OPS(ov5648_runtime_suspend, + ov5648_runtime_resume, NULL) +}; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops ov5648_internal_ops = { + .open = ov5648_open, +}; +#endif + +static const struct v4l2_subdev_core_ops ov5648_core_ops = { + .s_power = ov5648_s_power, + .ioctl = ov5648_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = ov5648_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops ov5648_video_ops = { + .s_stream = ov5648_s_stream, + .g_frame_interval = ov5648_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops ov5648_pad_ops = { + .enum_mbus_code = ov5648_enum_mbus_code, + .enum_frame_size = ov5648_enum_frame_sizes, + .get_fmt = ov5648_get_fmt, + .set_fmt = ov5648_set_fmt, +}; + +static const struct v4l2_subdev_ops ov5648_subdev_ops = { + .core = &ov5648_core_ops, + .video = &ov5648_video_ops, + .pad = &ov5648_pad_ops, +}; + +static int ov5648_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov5648 *ov5648 = container_of(ctrl->handler, + struct ov5648, ctrl_handler); + struct i2c_client *client = ov5648->client; + s64 max; + int ret = 0; + + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ + max = ov5648->cur_mode->height + ctrl->val - 4; + __v4l2_ctrl_modify_range(ov5648->exposure, + ov5648->exposure->minimum, max, + ov5648->exposure->step, + ov5648->exposure->default_value); + break; + } + + if (pm_runtime_get(&client->dev) <= 0) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + /* 4 least significant bits of expsoure are fractional part */ + + ret = ov5648_write_reg(ov5648->client, OV5648_REG_EXPOSURE, + OV5648_REG_VALUE_24BIT, ctrl->val << 4); + + break; + case V4L2_CID_ANALOGUE_GAIN: + ret = ov5648_write_reg(ov5648->client, OV5648_REG_GAIN_L, + OV5648_REG_VALUE_08BIT, + ctrl->val & OV5648_GAIN_L_MASK); + ret |= ov5648_write_reg(ov5648->client, OV5648_REG_GAIN_H, + OV5648_REG_VALUE_08BIT, + (ctrl->val >> OV5648_DIGI_GAIN_H_SHIFT) & + OV5648_GAIN_H_MASK); + break; + case V4L2_CID_VBLANK: + + ret = ov5648_write_reg(ov5648->client, OV5648_REG_VTS, + OV5648_REG_VALUE_16BIT, + ctrl->val + ov5648->cur_mode->height); + break; + case V4L2_CID_TEST_PATTERN: + ret = ov5648_enable_test_pattern(ov5648, ctrl->val); + break; + default: + dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", + __func__, ctrl->id, ctrl->val); + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops ov5648_ctrl_ops = { + .s_ctrl = ov5648_set_ctrl, +}; + +static int ov5648_initialize_controls(struct ov5648 *ov5648) +{ + const struct ov5648_mode *mode; + struct v4l2_ctrl_handler *handler; + struct v4l2_ctrl *ctrl; + s64 exposure_max, vblank_def; + u32 h_blank; + int ret; + + handler = &ov5648->ctrl_handler; + mode = ov5648->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 8); + if (ret) + return ret; + handler->lock = &ov5648->mutex; + + ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, + 0, 0, link_freq_menu_items); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, + 0, ov5648->pixel_rate, 1, ov5648->pixel_rate); + + h_blank = mode->hts_def - mode->width; + ov5648->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (ov5648->hblank) + ov5648->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = mode->vts_def - mode->height; + ov5648->vblank = v4l2_ctrl_new_std(handler, &ov5648_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + OV5648_VTS_MAX - mode->height, + 1, vblank_def); + + exposure_max = mode->vts_def - 4; + ov5648->exposure = v4l2_ctrl_new_std(handler, &ov5648_ctrl_ops, + V4L2_CID_EXPOSURE, OV5648_EXPOSURE_MIN, + exposure_max, OV5648_EXPOSURE_STEP, + mode->exp_def); + + ov5648->anal_gain = v4l2_ctrl_new_std(handler, &ov5648_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, ANALOG_GAIN_MIN, + ANALOG_GAIN_MAX, ANALOG_GAIN_STEP, + ANALOG_GAIN_DEFAULT); + + /* Digital gain */ + ov5648->digi_gain = v4l2_ctrl_new_std(handler, &ov5648_ctrl_ops, + V4L2_CID_DIGITAL_GAIN, OV5648_DIGI_GAIN_MIN, + OV5648_DIGI_GAIN_MAX, OV5648_DIGI_GAIN_STEP, + OV5648_DIGI_GAIN_DEFAULT); + + ov5648->test_pattern = v4l2_ctrl_new_std_menu_items(handler, + &ov5648_ctrl_ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov5648_test_pattern_menu) - 1, + 0, 0, ov5648_test_pattern_menu); + + if (handler->error) { + ret = handler->error; + dev_err(&ov5648->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + ov5648->subdev.ctrl_handler = handler; + + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +static int ov5648_check_sensor_id(struct ov5648 *ov5648, + struct i2c_client *client) +{ + struct device *dev = &ov5648->client->dev; + u32 id = 0; + int ret; + + ret = ov5648_read_reg(client, OV5648_REG_CHIP_ID, + OV5648_REG_VALUE_16BIT, &id); + if (id != CHIP_ID) { + dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret); + return -ENODEV; + } + + dev_info(dev, "Detected OV%06x sensor\n", CHIP_ID); + + return 0; +} + +static int ov5648_configure_regulators(struct ov5648 *ov5648) +{ + int i; + + for (i = 0; i < OV5648_NUM_SUPPLIES; i++) + ov5648->supplies[i].supply = ov5648_supply_names[i]; + + return devm_regulator_bulk_get(&ov5648->client->dev, + OV5648_NUM_SUPPLIES, + ov5648->supplies); +} + +static int ov5648_parse_of(struct ov5648 *ov5648) +{ + struct device *dev = &ov5648->client->dev; + struct device_node *endpoint; + struct fwnode_handle *fwnode; + int rval; + + endpoint = of_graph_get_next_endpoint(dev->of_node, NULL); + if (!endpoint) { + dev_err(dev, "Failed to get endpoint\n"); + return -EINVAL; + } + fwnode = of_fwnode_handle(endpoint); + rval = fwnode_property_read_u32_array(fwnode, "data-lanes", NULL, 0); + if (rval <= 0) { + dev_warn(dev, " Get mipi lane num failed!\n"); + return -1; + } + + ov5648->lane_num = rval; + if (2 == ov5648->lane_num) { + ov5648->cur_mode = &supported_modes_2lane[0]; + supported_modes = supported_modes_2lane; + ov5648->cfg_num = ARRAY_SIZE(supported_modes_2lane); + + /* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ + ov5648->pixel_rate = MIPI_FREQ * 2U * ov5648->lane_num / 10U; + dev_info(dev, "lane_num(%d) pixel_rate(%u)\n", + ov5648->lane_num, ov5648->pixel_rate); + } else { + dev_err(dev, "unsupported lane_num(%d)\n", ov5648->lane_num); + return -1; + } + return 0; +} + +static int ov5648_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct ov5648 *ov5648; + struct v4l2_subdev *sd; + char facing[2] = "b"; + int ret; + + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + ov5648 = devm_kzalloc(dev, sizeof(*ov5648), GFP_KERNEL); + if (!ov5648) + return -ENOMEM; + + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &ov5648->module_index); + if (ret) { + dev_warn(dev, "could not get module index!\n"); + ov5648->module_index = 0; + } + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &ov5648->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &ov5648->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &ov5648->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + + ov5648->client = client; + + ov5648->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(ov5648->xvclk)) { + dev_err(dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + ov5648->power_gpio = devm_gpiod_get(dev, "power", GPIOD_OUT_LOW); + if (IS_ERR(ov5648->power_gpio)) + dev_warn(dev, "Failed to get power-gpios, maybe no use\n"); + + ov5648->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ov5648->reset_gpio)) + dev_warn(dev, "Failed to get reset-gpios, maybe no use\n"); + + ov5648->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(ov5648->pwdn_gpio)) + dev_warn(dev, "Failed to get pwdn-gpios\n"); + + ret = ov5648_configure_regulators(ov5648); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + ret = ov5648_parse_of(ov5648); + if (ret != 0) + return -EINVAL; + + ov5648->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR(ov5648->pinctrl)) { + ov5648->pins_default = + pinctrl_lookup_state(ov5648->pinctrl, + OF_CAMERA_PINCTRL_STATE_DEFAULT); + if (IS_ERR(ov5648->pins_default)) + dev_err(dev, "could not get default pinstate\n"); + + ov5648->pins_sleep = + pinctrl_lookup_state(ov5648->pinctrl, + OF_CAMERA_PINCTRL_STATE_SLEEP); + if (IS_ERR(ov5648->pins_sleep)) + dev_err(dev, "could not get sleep pinstate\n"); + } + + mutex_init(&ov5648->mutex); + + sd = &ov5648->subdev; + v4l2_i2c_subdev_init(sd, client, &ov5648_subdev_ops); + ret = ov5648_initialize_controls(ov5648); + if (ret) + goto err_destroy_mutex; + + ret = __ov5648_power_on(ov5648); + if (ret) + goto err_free_handler; + + ret = ov5648_check_sensor_id(ov5648, client); + if (ret < 0) { + dev_info(&client->dev, "%s(%d) Check id failed\n" + "check following information:\n" + "Power/PowerDown/Reset/Mclk/I2cBus !!\n", + __func__, __LINE__); + goto err_power_off; + } + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &ov5648_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + ov5648->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &ov5648->pad); + if (ret < 0) + goto err_power_off; +#endif + + memset(facing, 0, sizeof(facing)); + if (strcmp(ov5648->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + ov5648->module_index, facing, + OV5648_NAME, dev_name(sd->dev)); + + ret = v4l2_async_register_subdev_sensor_common(sd); + if (ret) { + dev_err(dev, "v4l2 async register subdev failed\n"); + goto err_clean_entity; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; + +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif +err_power_off: + __ov5648_power_off(ov5648); +err_free_handler: + v4l2_ctrl_handler_free(&ov5648->ctrl_handler); +err_destroy_mutex: + mutex_destroy(&ov5648->mutex); + + return ret; +} + +static int ov5648_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov5648 *ov5648 = to_ov5648(sd); + + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + v4l2_ctrl_handler_free(&ov5648->ctrl_handler); + mutex_destroy(&ov5648->mutex); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __ov5648_power_off(ov5648); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id ov5648_of_match[] = { + { .compatible = "ovti,ov5648" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ov5648_of_match); +#endif + +static const struct i2c_device_id ov5648_match_id[] = { + { "ovti,ov5648", 0 }, + { }, +}; + +static struct i2c_driver ov5648_i2c_driver = { + .driver = { + .name = OV5648_NAME, + .pm = &ov5648_pm_ops, + .of_match_table = of_match_ptr(ov5648_of_match), + }, + .probe = &ov5648_probe, + .remove = &ov5648_remove, + .id_table = ov5648_match_id, +}; + +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&ov5648_i2c_driver); +} + +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&ov5648_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_DESCRIPTION("OmniVision ov5648 sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ov5670.c b/drivers/media/i2c/ov5670.c index 7b7c74d77370..b8f6b938f127 100644 --- a/drivers/media/i2c/ov5670.c +++ b/drivers/media/i2c/ov5670.c @@ -1,110 +1,261 @@ // SPDX-License-Identifier: GPL-2.0 -// Copyright (c) 2017 Intel Corporation. +/* + * ov5670 driver + * + * Copyright (C) 2019 Fuzhou Rockchip Electronics Co., Ltd. + * + * V0.0X01.0X01 add poweron function. + * V0.0X01.0X02 fix mclk issue when probe multiple camera. + * V0.0X01.0X03 add otp function. + */ -#include +#include +#include +#include +#include #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include +#include +#include +#include +#include +#include -#define OV5670_REG_CHIP_ID 0x300a -#define OV5670_CHIP_ID 0x005670 +#include -#define OV5670_REG_MODE_SELECT 0x0100 -#define OV5670_MODE_STANDBY 0x00 +/* verify default register values */ +//#define CHECK_REG_VALUE + +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x03) + +#ifndef V4L2_CID_DIGITAL_GAIN +#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN +#endif + +/* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ +#define MIPI_FREQ 420000000U +#define OV5670_PIXEL_RATE (420000000LL * 2LL * 2LL / 10) +#define OV5670_XVCLK_FREQ 24000000 + +#define CHIP_ID 0x5670 +#define OV5670_REG_CHIP_ID 0x300b + +#define OV5670_REG_CTRL_MODE 0x0100 +#define OV5670_MODE_SW_STANDBY 0x00 #define OV5670_MODE_STREAMING 0x01 -#define OV5670_REG_SOFTWARE_RST 0x0103 -#define OV5670_SOFTWARE_RST 0x01 - -/* vertical-timings from sensor */ -#define OV5670_REG_VTS 0x380e -#define OV5670_VTS_30FPS 0x0808 /* default for 30 fps */ -#define OV5670_VTS_MAX 0xffff - -/* horizontal-timings from sensor */ -#define OV5670_REG_HTS 0x380c - -/* - * Pixels-per-line(PPL) = Time-per-line * pixel-rate - * In OV5670, Time-per-line = HTS/SCLK. - * HTS is fixed for all resolutions, not recommended to change. - */ -#define OV5670_FIXED_PPL 2724 /* Pixels per line */ - -/* Exposure controls from sensor */ #define OV5670_REG_EXPOSURE 0x3500 #define OV5670_EXPOSURE_MIN 4 #define OV5670_EXPOSURE_STEP 1 +#define OV5670_VTS_MAX 0x7fff -/* Analog gain controls from sensor */ -#define OV5670_REG_ANALOG_GAIN 0x3508 -#define ANALOG_GAIN_MIN 0 -#define ANALOG_GAIN_MAX 8191 +#define OV5670_REG_GAIN_H 0x3508 +#define OV5670_REG_GAIN_L 0x3509 +#define OV5670_GAIN_L_MASK 0xff +#define OV5670_GAIN_H_MASK 0x1f +#define OV5670_GAIN_H_SHIFT 8 +#define ANALOG_GAIN_MIN 0x80 +#define ANALOG_GAIN_MAX 0x400 #define ANALOG_GAIN_STEP 1 -#define ANALOG_GAIN_DEFAULT 128 +#define ANALOG_GAIN_DEFAULT 1024 -/* Digital gain controls from sensor */ -#define OV5670_REG_R_DGTL_GAIN 0x5032 -#define OV5670_REG_G_DGTL_GAIN 0x5034 -#define OV5670_REG_B_DGTL_GAIN 0x5036 -#define OV5670_DGTL_GAIN_MIN 0 -#define OV5670_DGTL_GAIN_MAX 4095 -#define OV5670_DGTL_GAIN_STEP 1 -#define OV5670_DGTL_GAIN_DEFAULT 1024 +#define OV5670_REG_GROUP 0x3208 -/* Test Pattern Control */ #define OV5670_REG_TEST_PATTERN 0x4303 -#define OV5670_TEST_PATTERN_ENABLE BIT(3) -#define OV5670_REG_TEST_PATTERN_CTRL 0x4320 +#define OV5670_TEST_PATTERN_ENABLE 0x08 +#define OV5670_TEST_PATTERN_DISABLE 0x0 + +#define OV5670_REG_VTS 0x380e + +#define REG_NULL 0xFFFF +#define DELAY_MS 0xEEEE /* Array delay token */ #define OV5670_REG_VALUE_08BIT 1 #define OV5670_REG_VALUE_16BIT 2 #define OV5670_REG_VALUE_24BIT 3 -/* Initial number of frames to skip to avoid possible garbage */ -#define OV5670_NUM_OF_SKIP_FRAMES 2 +#define OV5670_LANES 2 +#define OV5670_BITS_PER_SAMPLE 10 -struct ov5670_reg { - u16 address; +#define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default" +#define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep" + +#define OV5670_NAME "ov5670" + +#define RG_Ratio_Typical_Default (0x16f) +#define BG_Ratio_Typical_Default (0x16f) + +#define ov5670_write_1byte(client, reg, val) \ + ov5670_write_reg((client), (reg), OV5670_REG_VALUE_08BIT, (val)) + +#define ov5670_read_1byte(client, reg, val) \ + ov5670_read_reg((client), (reg), OV5670_REG_VALUE_08BIT, (val)) + +struct ov5670_otp_info { + int flag; // bit[7]: info, bit[6]:wb + int module_id; + int lens_id; + int year; + int month; + int day; + int rg_ratio; + int bg_ratio; +}; + +static const char * const ov5670_supply_names[] = { + "avdd", /* Analog power */ + "dovdd", /* Digital I/O power */ + "dvdd", /* Digital core power */ +}; + +#define OV5670_NUM_SUPPLIES ARRAY_SIZE(ov5670_supply_names) + +struct regval { + u16 addr; u8 val; }; -struct ov5670_reg_list { - u32 num_of_regs; - const struct ov5670_reg *regs; -}; - -struct ov5670_link_freq_config { - u32 pixel_rate; - const struct ov5670_reg_list reg_list; -}; - struct ov5670_mode { - /* Frame width in pixels */ u32 width; - - /* Frame height in pixels */ u32 height; - - /* Default vertical timining size */ + struct v4l2_fract max_fps; + u32 hts_def; u32 vts_def; - - /* Min vertical timining size */ - u32 vts_min; - - /* Link frequency needed for this resolution */ - u32 link_freq_index; - - /* Sensor register settings for this resolution */ - const struct ov5670_reg_list reg_list; + u32 exp_def; + const struct regval *reg_list; }; -static const struct ov5670_reg mipi_data_rate_840mbps[] = { - {0x0300, 0x04}, +struct ov5670 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *power_gpio; + struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; + struct regulator_bulk_data supplies[OV5670_NUM_SUPPLIES]; + + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_sleep; + + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *anal_gain; + struct v4l2_ctrl *digi_gain; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *test_pattern; + struct mutex mutex; + bool streaming; + bool power_on; + const struct ov5670_mode *cur_mode; + unsigned int lane_num; + unsigned int cfg_num; + unsigned int pixel_rate; + u32 module_index; + struct ov5670_otp_info *otp; + const char *module_facing; + const char *module_name; + const char *len_name; + struct rkmodule_awb_cfg awb_cfg; +}; + +#define to_ov5670(sd) container_of(sd, struct ov5670, subdev) + +struct ov5670_id_name { + int id; + char name[RKMODULE_NAME_LEN]; +}; + +static const struct ov5670_id_name ov5670_module_info[] = { + {0x01, "Sunny"}, + {0x02, "Truly"}, + {0x03, "A-kerr"}, + {0x04, "LiteArray"}, + {0x05, "Darling"}, + {0x06, "Qtech"}, + {0x07, "OFlim"}, + {0x08, "Huaquan/Kingcom"}, + {0x09, "Booyi"}, + {0x0a, "Laimu"}, + {0x0b, "WDSEN"}, + {0x0c, "Sunrise"}, + {0x0d, "CameraKing"}, + {0x0e, "Sunniness/Riyong"}, + {0x0f, "Tongju"}, + {0x10, "Seasons/Sijichun"}, + {0x11, "Foxconn"}, + {0x12, "Importek"}, + {0x13, "Altek"}, + {0x14, "ABICO/Ability"}, + {0x15, "Lite-on"}, + {0x16, "Chicony"}, + {0x17, "Primax"}, + {0x18, "AVC"}, + {0x19, "Suyin"}, + {0x21, "Sharp"}, + {0x31, "MCNEX"}, + {0x32, "SEMCO"}, + {0x33, "Partron"}, + {0x41, "Reach/Zhongliancheng"}, + {0x42, "BYD"}, + {0x43, "OSTEC(AoShunChuang)"}, + {0x44, "Chengli"}, + {0x45, "Jiali"}, + {0x46, "Chippack"}, + {0x47, "RongSheng"}, + {0x48, "ShineTech/ShenTai"}, + {0x49, "Brodsands"}, + {0x50, "Others"}, + {0x00, "Unknown"} +}; + +static const struct ov5670_id_name ov5670_lens_info[] = { + {0x01, "Largan 40010A2"}, + {0x10, "Largan 30048A1"}, + {0x11, "Largan 30031A1B"}, + {0x12, "Largan 40010A1"}, + {0x30, "Sunny 3531A"}, + {0x31, "Sunny 3531B"}, + {0x32, "Sunny 3533A"}, + {0x90, "Kinko 3956AH"}, + {0xa0, "E-pin D517"}, + {0xc0, "XuYe XA-0502B"}, + {0xc8, "XuYe XA-0502A"}, + {0xc9, "XuYe E009A"}, + {0x00, "Unknown"} +}; + +/* + * Xclk 24Mhz + * Pclk 84Mhz + * linelength 2816(0xb00) + * framelength 1984(0x7c0) + * grabwindow_width 2592 + * grabwindow_height 1944 + * max_framerate 30fps + * mipi_datarate per lane 840Mbps + */ +static const struct regval ov5670_global_regs[] = { + {0x0103, 0x01}, //software reset + {DELAY_MS, 5}, + {0x0100, 0x00}, //software standby + {0x0300, 0x04}, //PLL {0x0301, 0x00}, - {0x0302, 0x84}, + {0x0302, 0x69}, //MIPI bit rate 840Mbps/lane {0x0303, 0x00}, {0x0304, 0x03}, {0x0305, 0x01}, @@ -112,47 +263,44 @@ static const struct ov5670_reg mipi_data_rate_840mbps[] = { {0x030a, 0x00}, {0x030b, 0x00}, {0x030c, 0x00}, - {0x030d, 0x26}, + {0x030d, 0x1e}, {0x030e, 0x00}, {0x030f, 0x06}, - {0x0312, 0x01}, - {0x3031, 0x0a}, -}; - -static const struct ov5670_reg mode_2592x1944_regs[] = { - {0x3000, 0x00}, - {0x3002, 0x21}, - {0x3005, 0xf0}, + {0x0312, 0x01}, //PLL + {0x3000, 0x00}, //Fsin/Vsync input + {0x3002, 0x21}, //ULPM output + {0x3005, 0xf0}, //sclk_psram on, sclk_syncfifo on {0x3007, 0x00}, - {0x3015, 0x0f}, - {0x3018, 0x32}, - {0x301a, 0xf0}, - {0x301b, 0xf0}, - {0x301c, 0xf0}, - {0x301d, 0xf0}, - {0x301e, 0xf0}, - {0x3030, 0x00}, - {0x3031, 0x0a}, - {0x303c, 0xff}, - {0x303e, 0xff}, - {0x3040, 0xf0}, - {0x3041, 0x00}, - {0x3042, 0xf0}, - {0x3106, 0x11}, - {0x3500, 0x00}, - {0x3501, 0x80}, - {0x3502, 0x00}, - {0x3503, 0x04}, - {0x3504, 0x03}, - {0x3505, 0x83}, - {0x3508, 0x04}, - {0x3509, 0x00}, - {0x350e, 0x04}, - {0x350f, 0x00}, - {0x3510, 0x00}, - {0x3511, 0x02}, - {0x3512, 0x00}, - {0x3601, 0xc8}, + {0x3015, 0x0f}, //npump clock div = 1, disable Ppumu_clk + {0x3018, 0x32}, //MIPI 2 lane + + {0x301a, 0xf0}, //sclk_stb on, sclk_ac on, slck_tc on + {0x301b, 0xf0}, //sclk_blc/isp/testmode/vfifo on + {0x301c, 0xf0}, //sclk_mipi on, sclk_dpcm on, sclk_otp on + {0x301d, 0xf0}, //sclk_asram_tst on, sclk_grp on, sclk_bist on, + {0x301e, 0xf0}, //sclk_ilpwm/lvds/vfifo/mipi on + {0x3030, 0x00}, //sclk normal, pclk normal + {0x3031, 0x0a}, //10-bit mode + {0x303c, 0xff}, //reserved + {0x303e, 0xff}, //reserved + {0x3040, 0xf0}, //sclk_isp_fc_en, sclk_fc-en, sclk_tpm_en, sclk_fmt_en + {0x3041, 0x00}, //reserved + {0x3042, 0xf0}, //reserved + {0x3106, 0x11}, //sclk_div = 1, sclk_pre_div = 1 + {0x3500, 0x00}, //exposure H + {0x3501, 0x3d}, //exposure M + {0x3502, 0x00}, //exposure L + {0x3503, 0x04}, //gain no delay, use sensor gain + {0x3504, 0x03}, //exposure manual, gain manual + {0x3505, 0x83}, //sensor gain fixed bit + {0x3508, 0x04}, //gain H + {0x3509, 0x00}, //gain L + {0x350e, 0x04}, //short digital gain H + {0x350f, 0x00}, //short digital gain L + {0x3510, 0x00}, //short exposure H + {0x3511, 0x02}, //short exposure M + {0x3512, 0x00}, //short exposure L + {0x3601, 0xc8}, //analog control {0x3610, 0x88}, {0x3612, 0x48}, {0x3614, 0x5b}, @@ -171,279 +319,14 @@ static const struct ov5670_reg mode_2592x1944_regs[] = { {0x3655, 0x20}, {0x3656, 0xff}, {0x365a, 0xff}, - {0x365e, 0xff}, - {0x3668, 0x00}, - {0x366a, 0x07}, - {0x366e, 0x10}, - {0x366d, 0x00}, - {0x366f, 0x80}, - {0x3700, 0x28}, - {0x3701, 0x10}, - {0x3702, 0x3a}, - {0x3703, 0x19}, - {0x3704, 0x10}, - {0x3705, 0x00}, - {0x3706, 0x66}, - {0x3707, 0x08}, - {0x3708, 0x34}, - {0x3709, 0x40}, - {0x370a, 0x01}, - {0x370b, 0x1b}, - {0x3714, 0x24}, - {0x371a, 0x3e}, - {0x3733, 0x00}, - {0x3734, 0x00}, - {0x373a, 0x05}, - {0x373b, 0x06}, - {0x373c, 0x0a}, - {0x373f, 0xa0}, - {0x3755, 0x00}, - {0x3758, 0x00}, - {0x375b, 0x0e}, - {0x3766, 0x5f}, - {0x3768, 0x00}, - {0x3769, 0x22}, - {0x3773, 0x08}, - {0x3774, 0x1f}, - {0x3776, 0x06}, - {0x37a0, 0x88}, - {0x37a1, 0x5c}, - {0x37a7, 0x88}, - {0x37a8, 0x70}, - {0x37aa, 0x88}, - {0x37ab, 0x48}, - {0x37b3, 0x66}, - {0x37c2, 0x04}, - {0x37c5, 0x00}, - {0x37c8, 0x00}, - {0x3800, 0x00}, - {0x3801, 0x0c}, - {0x3802, 0x00}, - {0x3803, 0x04}, - {0x3804, 0x0a}, - {0x3805, 0x33}, - {0x3806, 0x07}, - {0x3807, 0xa3}, - {0x3808, 0x0a}, - {0x3809, 0x20}, - {0x380a, 0x07}, - {0x380b, 0x98}, - {0x380c, 0x06}, - {0x380d, 0x90}, - {0x380e, 0x08}, - {0x380f, 0x08}, - {0x3811, 0x04}, - {0x3813, 0x02}, - {0x3814, 0x01}, - {0x3815, 0x01}, - {0x3816, 0x00}, - {0x3817, 0x00}, - {0x3818, 0x00}, - {0x3819, 0x00}, - {0x3820, 0x84}, - {0x3821, 0x46}, - {0x3822, 0x48}, - {0x3826, 0x00}, - {0x3827, 0x08}, - {0x382a, 0x01}, - {0x382b, 0x01}, - {0x3830, 0x08}, - {0x3836, 0x02}, - {0x3837, 0x00}, - {0x3838, 0x10}, - {0x3841, 0xff}, - {0x3846, 0x48}, - {0x3861, 0x00}, - {0x3862, 0x04}, - {0x3863, 0x06}, - {0x3a11, 0x01}, - {0x3a12, 0x78}, - {0x3b00, 0x00}, - {0x3b02, 0x00}, - {0x3b03, 0x00}, - {0x3b04, 0x00}, - {0x3b05, 0x00}, - {0x3c00, 0x89}, - {0x3c01, 0xab}, - {0x3c02, 0x01}, - {0x3c03, 0x00}, - {0x3c04, 0x00}, - {0x3c05, 0x03}, - {0x3c06, 0x00}, - {0x3c07, 0x05}, - {0x3c0c, 0x00}, - {0x3c0d, 0x00}, - {0x3c0e, 0x00}, - {0x3c0f, 0x00}, - {0x3c40, 0x00}, - {0x3c41, 0xa3}, - {0x3c43, 0x7d}, - {0x3c45, 0xd7}, - {0x3c47, 0xfc}, - {0x3c50, 0x05}, - {0x3c52, 0xaa}, - {0x3c54, 0x71}, - {0x3c56, 0x80}, - {0x3d85, 0x17}, - {0x3f03, 0x00}, - {0x3f0a, 0x00}, - {0x3f0b, 0x00}, - {0x4001, 0x60}, - {0x4009, 0x0d}, - {0x4020, 0x00}, - {0x4021, 0x00}, - {0x4022, 0x00}, - {0x4023, 0x00}, - {0x4024, 0x00}, - {0x4025, 0x00}, - {0x4026, 0x00}, - {0x4027, 0x00}, - {0x4028, 0x00}, - {0x4029, 0x00}, - {0x402a, 0x00}, - {0x402b, 0x00}, - {0x402c, 0x00}, - {0x402d, 0x00}, - {0x402e, 0x00}, - {0x402f, 0x00}, - {0x4040, 0x00}, - {0x4041, 0x03}, - {0x4042, 0x00}, - {0x4043, 0x7A}, - {0x4044, 0x00}, - {0x4045, 0x7A}, - {0x4046, 0x00}, - {0x4047, 0x7A}, - {0x4048, 0x00}, - {0x4049, 0x7A}, - {0x4307, 0x30}, - {0x4500, 0x58}, - {0x4501, 0x04}, - {0x4502, 0x40}, - {0x4503, 0x10}, - {0x4508, 0xaa}, - {0x4509, 0xaa}, - {0x450a, 0x00}, - {0x450b, 0x00}, - {0x4600, 0x01}, - {0x4601, 0x03}, - {0x4700, 0xa4}, - {0x4800, 0x4c}, - {0x4816, 0x53}, - {0x481f, 0x40}, - {0x4837, 0x13}, - {0x5000, 0x56}, - {0x5001, 0x01}, - {0x5002, 0x28}, - {0x5004, 0x0c}, - {0x5006, 0x0c}, - {0x5007, 0xe0}, - {0x5008, 0x01}, - {0x5009, 0xb0}, - {0x5901, 0x00}, - {0x5a01, 0x00}, - {0x5a03, 0x00}, - {0x5a04, 0x0c}, - {0x5a05, 0xe0}, - {0x5a06, 0x09}, - {0x5a07, 0xb0}, - {0x5a08, 0x06}, - {0x5e00, 0x00}, - {0x3734, 0x40}, - {0x5b00, 0x01}, - {0x5b01, 0x10}, - {0x5b02, 0x01}, - {0x5b03, 0xdb}, - {0x3d8c, 0x71}, - {0x3d8d, 0xea}, - {0x4017, 0x08}, - {0x3618, 0x2a}, - {0x5780, 0x3e}, - {0x5781, 0x0f}, - {0x5782, 0x44}, - {0x5783, 0x02}, - {0x5784, 0x01}, - {0x5785, 0x01}, - {0x5786, 0x00}, - {0x5787, 0x04}, - {0x5788, 0x02}, - {0x5789, 0x0f}, - {0x578a, 0xfd}, - {0x578b, 0xf5}, - {0x578c, 0xf5}, - {0x578d, 0x03}, - {0x578e, 0x08}, - {0x578f, 0x0c}, - {0x5790, 0x08}, - {0x5791, 0x06}, - {0x5792, 0x00}, - {0x5793, 0x52}, - {0x5794, 0xa3}, - {0x3503, 0x00}, - {0x5045, 0x05}, - {0x4003, 0x40}, - {0x5048, 0x40} -}; -static const struct ov5670_reg mode_1296x972_regs[] = { - {0x3000, 0x00}, - {0x3002, 0x21}, - {0x3005, 0xf0}, - {0x3007, 0x00}, - {0x3015, 0x0f}, - {0x3018, 0x32}, - {0x301a, 0xf0}, - {0x301b, 0xf0}, - {0x301c, 0xf0}, - {0x301d, 0xf0}, - {0x301e, 0xf0}, - {0x3030, 0x00}, - {0x3031, 0x0a}, - {0x303c, 0xff}, - {0x303e, 0xff}, - {0x3040, 0xf0}, - {0x3041, 0x00}, - {0x3042, 0xf0}, - {0x3106, 0x11}, - {0x3500, 0x00}, - {0x3501, 0x80}, - {0x3502, 0x00}, - {0x3503, 0x04}, - {0x3504, 0x03}, - {0x3505, 0x83}, - {0x3508, 0x07}, - {0x3509, 0x80}, - {0x350e, 0x04}, - {0x350f, 0x00}, - {0x3510, 0x00}, - {0x3511, 0x02}, - {0x3512, 0x00}, - {0x3601, 0xc8}, - {0x3610, 0x88}, - {0x3612, 0x48}, - {0x3614, 0x5b}, - {0x3615, 0x96}, - {0x3621, 0xd0}, - {0x3622, 0x00}, - {0x3623, 0x00}, - {0x3633, 0x13}, - {0x3634, 0x13}, - {0x3635, 0x13}, - {0x3636, 0x13}, - {0x3645, 0x13}, - {0x3646, 0x82}, - {0x3650, 0x00}, - {0x3652, 0xff}, - {0x3655, 0x20}, - {0x3656, 0xff}, - {0x365a, 0xff}, {0x365e, 0xff}, {0x3668, 0x00}, {0x366a, 0x07}, {0x366e, 0x08}, {0x366d, 0x00}, - {0x366f, 0x80}, - {0x3700, 0x28}, + {0x366f, 0x80}, //analog control + {0x3700, 0x28}, //sensor control {0x3701, 0x10}, {0x3702, 0x3a}, {0x3703, 0x19}, @@ -481,38 +364,39 @@ static const struct ov5670_reg mode_1296x972_regs[] = { {0x37b3, 0x66}, {0x37c2, 0x04}, {0x37c5, 0x00}, - {0x37c8, 0x00}, - {0x3800, 0x00}, - {0x3801, 0x0c}, - {0x3802, 0x00}, - {0x3803, 0x04}, - {0x3804, 0x0a}, - {0x3805, 0x33}, - {0x3806, 0x07}, - {0x3807, 0xa3}, - {0x3808, 0x05}, - {0x3809, 0x10}, - {0x380a, 0x03}, - {0x380b, 0xcc}, - {0x380c, 0x06}, - {0x380d, 0x90}, - {0x380e, 0x08}, - {0x380f, 0x08}, - {0x3811, 0x04}, - {0x3813, 0x04}, - {0x3814, 0x03}, - {0x3815, 0x01}, - {0x3816, 0x00}, - {0x3817, 0x00}, - {0x3818, 0x00}, - {0x3819, 0x00}, - {0x3820, 0x94}, - {0x3821, 0x47}, - {0x3822, 0x48}, - {0x3826, 0x00}, - {0x3827, 0x08}, - {0x382a, 0x03}, - {0x382b, 0x01}, + {0x37c8, 0x00}, //sensor control + + {0x3800, 0x00}, //x addr start H + {0x3801, 0x0c}, //x addr start L + {0x3802, 0x00}, //y addr start H + {0x3803, 0x04}, //y addr start L + {0x3804, 0x0a}, //x addr end H + {0x3805, 0x33}, //x addr end L + {0x3806, 0x07}, //y addr end H + {0x3807, 0xa3}, //y addr end L + {0x3808, 0x05}, //x output size H + {0x3809, 0x10}, //x output size L + {0x380a, 0x03}, //y output size H + {0x380b, 0xc0}, //y output size L + {0x380c, 0x06}, //HTS H + {0x380d, 0x90}, //HTS L + {0x380e, 0x03}, //VTS H + {0x380f, 0xfc}, //VTS L + {0x3811, 0x04}, //ISP x win L + {0x3813, 0x02}, //ISP y win L + {0x3814, 0x03}, //x inc odd + {0x3815, 0x01}, //x inc even + {0x3816, 0x00}, //vsync start H + {0x3817, 0x00}, //vsync star L + {0x3818, 0x00}, //vsync end H + {0x3819, 0x00}, //vsync end L + {0x3820, 0x90}, //vsyn48_blc on, vflip off + {0x3821, 0x47}, //hsync_en_o, mirror on, dig_bin on + {0x3822, 0x48}, //addr0_num[3:1]=0x02, ablc_num[5:1]=0x08 + {0x3826, 0x00}, //r_rst_fsin H + {0x3827, 0x08}, //r_rst_fsin L + {0x382a, 0x03}, //y inc odd + {0x382b, 0x01}, //y inc even {0x3830, 0x08}, {0x3836, 0x02}, {0x3837, 0x00}, @@ -524,11 +408,12 @@ static const struct ov5670_reg mode_1296x972_regs[] = { {0x3863, 0x06}, {0x3a11, 0x01}, {0x3a12, 0x78}, - {0x3b00, 0x00}, + {0x3b00, 0x00}, //strobe {0x3b02, 0x00}, {0x3b03, 0x00}, + {0x3b04, 0x00}, - {0x3b05, 0x00}, + {0x3b05, 0x00}, //strobe {0x3c00, 0x89}, {0x3c01, 0xab}, {0x3c02, 0x01}, @@ -551,13 +436,13 @@ static const struct ov5670_reg mode_1296x972_regs[] = { {0x3c54, 0x71}, {0x3c56, 0x80}, {0x3d85, 0x17}, - {0x3f03, 0x00}, + {0x3f03, 0x00}, //PSRAM {0x3f0a, 0x00}, - {0x3f0b, 0x00}, - {0x4001, 0x60}, - {0x4009, 0x05}, - {0x4020, 0x00}, - {0x4021, 0x00}, + {0x3f0b, 0x00}, //PSRAM + {0x4001, 0x60}, //BLC, K enable + {0x4009, 0x05}, //BLC, black line end line + {0x4020, 0x00}, //BLC, offset compensation th000 + {0x4021, 0x00}, //BLC, offset compensation K000 {0x4022, 0x00}, {0x4023, 0x00}, {0x4024, 0x00}, @@ -572,16 +457,18 @@ static const struct ov5670_reg mode_1296x972_regs[] = { {0x402d, 0x00}, {0x402e, 0x00}, {0x402f, 0x00}, + {0x4040, 0x00}, {0x4041, 0x03}, {0x4042, 0x00}, - {0x4043, 0x7A}, + {0x4043, 0x7A}, //1/1.05 x (0x80) {0x4044, 0x00}, {0x4045, 0x7A}, {0x4046, 0x00}, {0x4047, 0x7A}, - {0x4048, 0x00}, - {0x4049, 0x7A}, + {0x4048, 0x00}, //BLC, kcoef_r_man H + {0x4049, 0x7A}, //BLC, kcoef_r_man L + {0x4303, 0x00}, {0x4307, 0x30}, {0x4500, 0x58}, {0x4501, 0x04}, @@ -594,301 +481,36 @@ static const struct ov5670_reg mode_1296x972_regs[] = { {0x4600, 0x00}, {0x4601, 0x81}, {0x4700, 0xa4}, - {0x4800, 0x4c}, - {0x4816, 0x53}, - {0x481f, 0x40}, - {0x4837, 0x13}, - {0x5000, 0x56}, - {0x5001, 0x01}, - {0x5002, 0x28}, - {0x5004, 0x0c}, + {0x4800, 0x4c}, //MIPI control + {0x4816, 0x53}, //emb_dt + {0x481f, 0x40}, //clock_prepare_min + {0x4837, 0x13}, //MIPI global timing + {0x5000, 0x56}, //dcblc_en, awb_gain_en, bc_en, wc_en + {0x5001, 0x01}, //blc_en + {0x5002, 0x28}, //otp_dpc_en + {0x5004, 0x0c}, //ISP size auto control enable {0x5006, 0x0c}, {0x5007, 0xe0}, {0x5008, 0x01}, {0x5009, 0xb0}, - {0x5901, 0x00}, - {0x5a01, 0x00}, - {0x5a03, 0x00}, - {0x5a04, 0x0c}, - {0x5a05, 0xe0}, - {0x5a06, 0x09}, - {0x5a07, 0xb0}, - {0x5a08, 0x06}, - {0x5e00, 0x00}, - {0x3734, 0x40}, - {0x5b00, 0x01}, - {0x5b01, 0x10}, - {0x5b02, 0x01}, - {0x5b03, 0xdb}, - {0x3d8c, 0x71}, - {0x3d8d, 0xea}, - {0x4017, 0x10}, - {0x3618, 0x2a}, - {0x5780, 0x3e}, - {0x5781, 0x0f}, - {0x5782, 0x44}, - {0x5783, 0x02}, - {0x5784, 0x01}, - {0x5785, 0x01}, - {0x5786, 0x00}, - {0x5787, 0x04}, - {0x5788, 0x02}, - {0x5789, 0x0f}, - {0x578a, 0xfd}, - {0x578b, 0xf5}, - {0x578c, 0xf5}, - {0x578d, 0x03}, - {0x578e, 0x08}, - {0x578f, 0x0c}, - {0x5790, 0x08}, - {0x5791, 0x04}, - {0x5792, 0x00}, - {0x5793, 0x52}, - {0x5794, 0xa3}, - {0x3503, 0x00}, - {0x5045, 0x05}, - {0x4003, 0x40}, - {0x5048, 0x40} -}; + {0x5901, 0x00}, //VAP + {0x5a01, 0x00}, //WINC x start offset H + {0x5a03, 0x00}, //WINC x start offset L + {0x5a04, 0x0c}, //WINC y start offset H + {0x5a05, 0xe0}, //WINC y start offset L + {0x5a06, 0x09}, //WINC window width H + {0x5a07, 0xb0}, //WINC window width L + {0x5a08, 0x06}, //WINC window height H + {0x5e00, 0x00}, //WINC window height L + {0x3734, 0x40}, //Improve HFPN -static const struct ov5670_reg mode_648x486_regs[] = { - {0x3000, 0x00}, - {0x3002, 0x21}, - {0x3005, 0xf0}, - {0x3007, 0x00}, - {0x3015, 0x0f}, - {0x3018, 0x32}, - {0x301a, 0xf0}, - {0x301b, 0xf0}, - {0x301c, 0xf0}, - {0x301d, 0xf0}, - {0x301e, 0xf0}, - {0x3030, 0x00}, - {0x3031, 0x0a}, - {0x303c, 0xff}, - {0x303e, 0xff}, - {0x3040, 0xf0}, - {0x3041, 0x00}, - {0x3042, 0xf0}, - {0x3106, 0x11}, - {0x3500, 0x00}, - {0x3501, 0x80}, - {0x3502, 0x00}, - {0x3503, 0x04}, - {0x3504, 0x03}, - {0x3505, 0x83}, - {0x3508, 0x04}, - {0x3509, 0x00}, - {0x350e, 0x04}, - {0x350f, 0x00}, - {0x3510, 0x00}, - {0x3511, 0x02}, - {0x3512, 0x00}, - {0x3601, 0xc8}, - {0x3610, 0x88}, - {0x3612, 0x48}, - {0x3614, 0x5b}, - {0x3615, 0x96}, - {0x3621, 0xd0}, - {0x3622, 0x00}, - {0x3623, 0x04}, - {0x3633, 0x13}, - {0x3634, 0x13}, - {0x3635, 0x13}, - {0x3636, 0x13}, - {0x3645, 0x13}, - {0x3646, 0x82}, - {0x3650, 0x00}, - {0x3652, 0xff}, - {0x3655, 0x20}, - {0x3656, 0xff}, - {0x365a, 0xff}, - {0x365e, 0xff}, - {0x3668, 0x00}, - {0x366a, 0x07}, - {0x366e, 0x08}, - {0x366d, 0x00}, - {0x366f, 0x80}, - {0x3700, 0x28}, - {0x3701, 0x10}, - {0x3702, 0x3a}, - {0x3703, 0x19}, - {0x3704, 0x10}, - {0x3705, 0x00}, - {0x3706, 0x66}, - {0x3707, 0x08}, - {0x3708, 0x34}, - {0x3709, 0x40}, - {0x370a, 0x01}, - {0x370b, 0x1b}, - {0x3714, 0x24}, - {0x371a, 0x3e}, - {0x3733, 0x00}, - {0x3734, 0x00}, - {0x373a, 0x05}, - {0x373b, 0x06}, - {0x373c, 0x0a}, - {0x373f, 0xa0}, - {0x3755, 0x00}, - {0x3758, 0x00}, - {0x375b, 0x0e}, - {0x3766, 0x5f}, - {0x3768, 0x00}, - {0x3769, 0x22}, - {0x3773, 0x08}, - {0x3774, 0x1f}, - {0x3776, 0x06}, - {0x37a0, 0x88}, - {0x37a1, 0x5c}, - {0x37a7, 0x88}, - {0x37a8, 0x70}, - {0x37aa, 0x88}, - {0x37ab, 0x48}, - {0x37b3, 0x66}, - {0x37c2, 0x04}, - {0x37c5, 0x00}, - {0x37c8, 0x00}, - {0x3800, 0x00}, - {0x3801, 0x0c}, - {0x3802, 0x00}, - {0x3803, 0x04}, - {0x3804, 0x0a}, - {0x3805, 0x33}, - {0x3806, 0x07}, - {0x3807, 0xa3}, - {0x3808, 0x02}, - {0x3809, 0x88}, - {0x380a, 0x01}, - {0x380b, 0xe6}, - {0x380c, 0x06}, - {0x380d, 0x90}, - {0x380e, 0x08}, - {0x380f, 0x08}, - {0x3811, 0x04}, - {0x3813, 0x02}, - {0x3814, 0x07}, - {0x3815, 0x01}, - {0x3816, 0x00}, - {0x3817, 0x00}, - {0x3818, 0x00}, - {0x3819, 0x00}, - {0x3820, 0x94}, - {0x3821, 0xc6}, - {0x3822, 0x48}, - {0x3826, 0x00}, - {0x3827, 0x08}, - {0x382a, 0x07}, - {0x382b, 0x01}, - {0x3830, 0x08}, - {0x3836, 0x02}, - {0x3837, 0x00}, - {0x3838, 0x10}, - {0x3841, 0xff}, - {0x3846, 0x48}, - {0x3861, 0x00}, - {0x3862, 0x04}, - {0x3863, 0x06}, - {0x3a11, 0x01}, - {0x3a12, 0x78}, - {0x3b00, 0x00}, - {0x3b02, 0x00}, - {0x3b03, 0x00}, - {0x3b04, 0x00}, - {0x3b05, 0x00}, - {0x3c00, 0x89}, - {0x3c01, 0xab}, - {0x3c02, 0x01}, - {0x3c03, 0x00}, - {0x3c04, 0x00}, - {0x3c05, 0x03}, - {0x3c06, 0x00}, - {0x3c07, 0x05}, - {0x3c0c, 0x00}, - {0x3c0d, 0x00}, - {0x3c0e, 0x00}, - {0x3c0f, 0x00}, - {0x3c40, 0x00}, - {0x3c41, 0xa3}, - {0x3c43, 0x7d}, - {0x3c45, 0xd7}, - {0x3c47, 0xfc}, - {0x3c50, 0x05}, - {0x3c52, 0xaa}, - {0x3c54, 0x71}, - {0x3c56, 0x80}, - {0x3d85, 0x17}, - {0x3f03, 0x00}, - {0x3f0a, 0x00}, - {0x3f0b, 0x00}, - {0x4001, 0x60}, - {0x4009, 0x05}, - {0x4020, 0x00}, - {0x4021, 0x00}, - {0x4022, 0x00}, - {0x4023, 0x00}, - {0x4024, 0x00}, - {0x4025, 0x00}, - {0x4026, 0x00}, - {0x4027, 0x00}, - {0x4028, 0x00}, - {0x4029, 0x00}, - {0x402a, 0x00}, - {0x402b, 0x00}, - {0x402c, 0x00}, - {0x402d, 0x00}, - {0x402e, 0x00}, - {0x402f, 0x00}, - {0x4040, 0x00}, - {0x4041, 0x03}, - {0x4042, 0x00}, - {0x4043, 0x7A}, - {0x4044, 0x00}, - {0x4045, 0x7A}, - {0x4046, 0x00}, - {0x4047, 0x7A}, - {0x4048, 0x00}, - {0x4049, 0x7A}, - {0x4307, 0x30}, - {0x4500, 0x58}, - {0x4501, 0x04}, - {0x4502, 0x40}, - {0x4503, 0x10}, - {0x4508, 0x55}, - {0x4509, 0x55}, - {0x450a, 0x02}, - {0x450b, 0x00}, - {0x4600, 0x00}, - {0x4601, 0x40}, - {0x4700, 0xa4}, - {0x4800, 0x4c}, - {0x4816, 0x53}, - {0x481f, 0x40}, - {0x4837, 0x13}, - {0x5000, 0x56}, - {0x5001, 0x01}, - {0x5002, 0x28}, - {0x5004, 0x0c}, - {0x5006, 0x0c}, - {0x5007, 0xe0}, - {0x5008, 0x01}, - {0x5009, 0xb0}, - {0x5901, 0x00}, - {0x5a01, 0x00}, - {0x5a03, 0x00}, - {0x5a04, 0x0c}, - {0x5a05, 0xe0}, - {0x5a06, 0x09}, - {0x5a07, 0xb0}, - {0x5a08, 0x06}, - {0x5e00, 0x00}, - {0x3734, 0x40}, - {0x5b00, 0x01}, - {0x5b01, 0x10}, - {0x5b02, 0x01}, - {0x5b03, 0xdb}, - {0x3d8c, 0x71}, - {0x3d8d, 0xea}, - {0x4017, 0x10}, + {0x5b00, 0x01}, //[2:0] otp start addr[10:8] + {0x5b01, 0x10}, //[7:0] otp start addr[7:0] + {0x5b02, 0x01}, //[2:0] otp end addr[10:8] + {0x5b03, 0xdb}, //[7:0] otp end addr[7:0] + {0x3d8c, 0x71}, //Header address high byte + {0x3d8d, 0xea}, //Header address low byte + {0x4017, 0x10}, //threshold = 4LSB for Binning sum format. {0x3618, 0x2a}, {0x5780, 0x3e}, {0x5781, 0x0f}, @@ -911,807 +533,132 @@ static const struct ov5670_reg mode_648x486_regs[] = { {0x5792, 0x00}, {0x5793, 0x52}, {0x5794, 0xa3}, - {0x3503, 0x00}, - {0x5045, 0x05}, - {0x4003, 0x40}, - {0x5048, 0x40} + {0x3503, 0x30}, //exposure gain/exposure delay not used + {0x3002, 0x61}, //[6]ULPM output enable + {0x3010, 0x40}, //[6]enable ULPM as GPIO controlled by register + {0x300d, 0x00}, //[6]ULPM output low (if 1=> high) + {0x5045, 0x05}, //[2] enable MWB manual bias + {0x5048, 0x10}, //MWB manual bias be the same with 0x4003 BLC target. + //{0x0100, 0x01}, + + {REG_NULL, 0x00}, }; -static const struct ov5670_reg mode_2560x1440_regs[] = { - {0x3000, 0x00}, - {0x3002, 0x21}, - {0x3005, 0xf0}, - {0x3007, 0x00}, - {0x3015, 0x0f}, - {0x3018, 0x32}, - {0x301a, 0xf0}, - {0x301b, 0xf0}, - {0x301c, 0xf0}, - {0x301d, 0xf0}, - {0x301e, 0xf0}, - {0x3030, 0x00}, - {0x3031, 0x0a}, - {0x303c, 0xff}, - {0x303e, 0xff}, - {0x3040, 0xf0}, - {0x3041, 0x00}, - {0x3042, 0xf0}, - {0x3106, 0x11}, - {0x3500, 0x00}, - {0x3501, 0x80}, - {0x3502, 0x00}, - {0x3503, 0x04}, - {0x3504, 0x03}, - {0x3505, 0x83}, - {0x3508, 0x04}, - {0x3509, 0x00}, - {0x350e, 0x04}, - {0x350f, 0x00}, - {0x3510, 0x00}, - {0x3511, 0x02}, - {0x3512, 0x00}, - {0x3601, 0xc8}, - {0x3610, 0x88}, - {0x3612, 0x48}, - {0x3614, 0x5b}, - {0x3615, 0x96}, - {0x3621, 0xd0}, - {0x3622, 0x00}, - {0x3623, 0x00}, - {0x3633, 0x13}, - {0x3634, 0x13}, - {0x3635, 0x13}, - {0x3636, 0x13}, - {0x3645, 0x13}, - {0x3646, 0x82}, - {0x3650, 0x00}, - {0x3652, 0xff}, - {0x3655, 0x20}, - {0x3656, 0xff}, - {0x365a, 0xff}, - {0x365e, 0xff}, - {0x3668, 0x00}, - {0x366a, 0x07}, - {0x366e, 0x10}, - {0x366d, 0x00}, - {0x366f, 0x80}, - {0x3700, 0x28}, - {0x3701, 0x10}, - {0x3702, 0x3a}, - {0x3703, 0x19}, - {0x3704, 0x10}, - {0x3705, 0x00}, - {0x3706, 0x66}, - {0x3707, 0x08}, - {0x3708, 0x34}, - {0x3709, 0x40}, - {0x370a, 0x01}, - {0x370b, 0x1b}, - {0x3714, 0x24}, - {0x371a, 0x3e}, - {0x3733, 0x00}, - {0x3734, 0x00}, - {0x373a, 0x05}, - {0x373b, 0x06}, - {0x373c, 0x0a}, - {0x373f, 0xa0}, - {0x3755, 0x00}, - {0x3758, 0x00}, - {0x375b, 0x0e}, - {0x3766, 0x5f}, - {0x3768, 0x00}, - {0x3769, 0x22}, - {0x3773, 0x08}, - {0x3774, 0x1f}, - {0x3776, 0x06}, - {0x37a0, 0x88}, - {0x37a1, 0x5c}, - {0x37a7, 0x88}, - {0x37a8, 0x70}, - {0x37aa, 0x88}, - {0x37ab, 0x48}, - {0x37b3, 0x66}, - {0x37c2, 0x04}, - {0x37c5, 0x00}, - {0x37c8, 0x00}, - {0x3800, 0x00}, - {0x3801, 0x0c}, - {0x3802, 0x00}, - {0x3803, 0x04}, - {0x3804, 0x0a}, - {0x3805, 0x33}, - {0x3806, 0x07}, - {0x3807, 0xa3}, - {0x3808, 0x0a}, - {0x3809, 0x00}, - {0x380a, 0x05}, - {0x380b, 0xa0}, - {0x380c, 0x06}, - {0x380d, 0x90}, - {0x380e, 0x08}, - {0x380f, 0x08}, - {0x3811, 0x04}, - {0x3813, 0x02}, - {0x3814, 0x01}, - {0x3815, 0x01}, - {0x3816, 0x00}, - {0x3817, 0x00}, - {0x3818, 0x00}, - {0x3819, 0x00}, - {0x3820, 0x84}, - {0x3821, 0x46}, - {0x3822, 0x48}, - {0x3826, 0x00}, - {0x3827, 0x08}, - {0x382a, 0x01}, - {0x382b, 0x01}, - {0x3830, 0x08}, - {0x3836, 0x02}, - {0x3837, 0x00}, - {0x3838, 0x10}, - {0x3841, 0xff}, - {0x3846, 0x48}, - {0x3861, 0x00}, - {0x3862, 0x04}, - {0x3863, 0x06}, - {0x3a11, 0x01}, - {0x3a12, 0x78}, - {0x3b00, 0x00}, - {0x3b02, 0x00}, - {0x3b03, 0x00}, - {0x3b04, 0x00}, - {0x3b05, 0x00}, - {0x3c00, 0x89}, - {0x3c01, 0xab}, - {0x3c02, 0x01}, - {0x3c03, 0x00}, - {0x3c04, 0x00}, - {0x3c05, 0x03}, - {0x3c06, 0x00}, - {0x3c07, 0x05}, - {0x3c0c, 0x00}, - {0x3c0d, 0x00}, - {0x3c0e, 0x00}, - {0x3c0f, 0x00}, - {0x3c40, 0x00}, - {0x3c41, 0xa3}, - {0x3c43, 0x7d}, - {0x3c45, 0xd7}, - {0x3c47, 0xfc}, - {0x3c50, 0x05}, - {0x3c52, 0xaa}, - {0x3c54, 0x71}, - {0x3c56, 0x80}, - {0x3d85, 0x17}, - {0x3f03, 0x00}, - {0x3f0a, 0x00}, - {0x3f0b, 0x00}, - {0x4001, 0x60}, - {0x4009, 0x0d}, - {0x4020, 0x00}, - {0x4021, 0x00}, - {0x4022, 0x00}, - {0x4023, 0x00}, - {0x4024, 0x00}, - {0x4025, 0x00}, - {0x4026, 0x00}, - {0x4027, 0x00}, - {0x4028, 0x00}, - {0x4029, 0x00}, - {0x402a, 0x00}, - {0x402b, 0x00}, - {0x402c, 0x00}, - {0x402d, 0x00}, - {0x402e, 0x00}, - {0x402f, 0x00}, - {0x4040, 0x00}, - {0x4041, 0x03}, - {0x4042, 0x00}, - {0x4043, 0x7A}, - {0x4044, 0x00}, - {0x4045, 0x7A}, - {0x4046, 0x00}, - {0x4047, 0x7A}, - {0x4048, 0x00}, - {0x4049, 0x7A}, - {0x4307, 0x30}, - {0x4500, 0x58}, - {0x4501, 0x04}, +/* + * Xclk 24Mhz + * Pclk 210Mhz + * linelength 3360(0xd20 + * framelength 2038(0x7f6) + * grabwindow_width 2592 + * grabwindow_height 1944 + * max_framerate 30fps + * mipi_datarate per lane 840Mbps + */ +static const struct regval ov5670_2592x1944_regs_2lane[] = { + // 2592x1944 30fps 2 lane MIPI 840Mbps/lane + {0x0100, 0x00}, + {0x3501, 0x7b}, //exposure M + {0x3623, 0x00}, //analog control + {0x366e, 0x10}, //analog control + {0x370b, 0x1b}, //sensor control + {0x3808, 0x0a}, //x output size H + {0x3809, 0x20}, //x output size L + {0x380a, 0x07}, //y output size H + {0x380b, 0x98}, //y output size L + {0x380c, 0x06}, //HTS H + {0x380d, 0x90}, //HTS L + {0x380e, 0x07}, //VTS H + {0x380f, 0xf6}, //VTS L + {0x3814, 0x01}, //x inc odd + {0x3820, 0x80}, //vsyn48_blc on, vflip off + {0x3821, 0x46}, //hsync_en_o, mirror on, dig_bin on + {0x382a, 0x01}, //y inc odd + {0x4009, 0x0d}, //BLC, black line end line {0x4502, 0x40}, - {0x4503, 0x10}, {0x4508, 0xaa}, {0x4509, 0xaa}, {0x450a, 0x00}, - {0x450b, 0x00}, {0x4600, 0x01}, - {0x4601, 0x00}, - {0x4700, 0xa4}, - {0x4800, 0x4c}, - {0x4816, 0x53}, - {0x481f, 0x40}, - {0x4837, 0x13}, - {0x5000, 0x56}, - {0x5001, 0x01}, - {0x5002, 0x28}, - {0x5004, 0x0c}, - {0x5006, 0x0c}, - {0x5007, 0xe0}, - {0x5008, 0x01}, - {0x5009, 0xb0}, - {0x5901, 0x00}, - {0x5a01, 0x00}, - {0x5a03, 0x00}, - {0x5a04, 0x0c}, - {0x5a05, 0xe0}, - {0x5a06, 0x09}, - {0x5a07, 0xb0}, - {0x5a08, 0x06}, - {0x5e00, 0x00}, - {0x3734, 0x40}, - {0x5b00, 0x01}, - {0x5b01, 0x10}, - {0x5b02, 0x01}, - {0x5b03, 0xdb}, - {0x3d8c, 0x71}, - {0x3d8d, 0xea}, - {0x4017, 0x08}, - {0x3618, 0x2a}, - {0x5780, 0x3e}, - {0x5781, 0x0f}, - {0x5782, 0x44}, - {0x5783, 0x02}, - {0x5784, 0x01}, - {0x5785, 0x01}, - {0x5786, 0x00}, - {0x5787, 0x04}, - {0x5788, 0x02}, - {0x5789, 0x0f}, - {0x578a, 0xfd}, - {0x578b, 0xf5}, - {0x578c, 0xf5}, - {0x578d, 0x03}, - {0x578e, 0x08}, - {0x578f, 0x0c}, - {0x5790, 0x08}, - {0x5791, 0x06}, - {0x5792, 0x00}, - {0x5793, 0x52}, - {0x5794, 0xa3}, - {0x5045, 0x05}, - {0x4003, 0x40}, - {0x5048, 0x40} + {0x4601, 0x03}, + {0x4017, 0x08}, //BLC, offset trigger threshold + //{0x0100, 0x01}, + + {REG_NULL, 0x00}, }; -static const struct ov5670_reg mode_1280x720_regs[] = { - {0x3000, 0x00}, - {0x3002, 0x21}, - {0x3005, 0xf0}, - {0x3007, 0x00}, - {0x3015, 0x0f}, - {0x3018, 0x32}, - {0x301a, 0xf0}, - {0x301b, 0xf0}, - {0x301c, 0xf0}, - {0x301d, 0xf0}, - {0x301e, 0xf0}, - {0x3030, 0x00}, - {0x3031, 0x0a}, - {0x303c, 0xff}, - {0x303e, 0xff}, - {0x3040, 0xf0}, - {0x3041, 0x00}, - {0x3042, 0xf0}, - {0x3106, 0x11}, - {0x3500, 0x00}, - {0x3501, 0x80}, - {0x3502, 0x00}, - {0x3503, 0x04}, - {0x3504, 0x03}, - {0x3505, 0x83}, - {0x3508, 0x04}, - {0x3509, 0x00}, - {0x350e, 0x04}, - {0x350f, 0x00}, - {0x3510, 0x00}, - {0x3511, 0x02}, - {0x3512, 0x00}, - {0x3601, 0xc8}, - {0x3610, 0x88}, - {0x3612, 0x48}, - {0x3614, 0x5b}, - {0x3615, 0x96}, - {0x3621, 0xd0}, - {0x3622, 0x00}, - {0x3623, 0x00}, - {0x3633, 0x13}, - {0x3634, 0x13}, - {0x3635, 0x13}, - {0x3636, 0x13}, - {0x3645, 0x13}, - {0x3646, 0x82}, - {0x3650, 0x00}, - {0x3652, 0xff}, - {0x3655, 0x20}, - {0x3656, 0xff}, - {0x365a, 0xff}, - {0x365e, 0xff}, - {0x3668, 0x00}, - {0x366a, 0x07}, - {0x366e, 0x08}, - {0x366d, 0x00}, - {0x366f, 0x80}, - {0x3700, 0x28}, - {0x3701, 0x10}, - {0x3702, 0x3a}, - {0x3703, 0x19}, - {0x3704, 0x10}, - {0x3705, 0x00}, - {0x3706, 0x66}, - {0x3707, 0x08}, - {0x3708, 0x34}, - {0x3709, 0x40}, - {0x370a, 0x01}, - {0x370b, 0x1b}, - {0x3714, 0x24}, - {0x371a, 0x3e}, - {0x3733, 0x00}, - {0x3734, 0x00}, - {0x373a, 0x05}, - {0x373b, 0x06}, - {0x373c, 0x0a}, - {0x373f, 0xa0}, - {0x3755, 0x00}, - {0x3758, 0x00}, - {0x375b, 0x0e}, - {0x3766, 0x5f}, - {0x3768, 0x00}, - {0x3769, 0x22}, - {0x3773, 0x08}, - {0x3774, 0x1f}, - {0x3776, 0x06}, - {0x37a0, 0x88}, - {0x37a1, 0x5c}, - {0x37a7, 0x88}, - {0x37a8, 0x70}, - {0x37aa, 0x88}, - {0x37ab, 0x48}, - {0x37b3, 0x66}, - {0x37c2, 0x04}, - {0x37c5, 0x00}, - {0x37c8, 0x00}, - {0x3800, 0x00}, - {0x3801, 0x0c}, - {0x3802, 0x00}, - {0x3803, 0x04}, - {0x3804, 0x0a}, - {0x3805, 0x33}, - {0x3806, 0x07}, - {0x3807, 0xa3}, - {0x3808, 0x05}, - {0x3809, 0x00}, - {0x380a, 0x02}, - {0x380b, 0xd0}, - {0x380c, 0x06}, - {0x380d, 0x90}, - {0x380e, 0x08}, - {0x380f, 0x08}, - {0x3811, 0x04}, - {0x3813, 0x02}, - {0x3814, 0x03}, - {0x3815, 0x01}, - {0x3816, 0x00}, - {0x3817, 0x00}, - {0x3818, 0x00}, - {0x3819, 0x00}, - {0x3820, 0x94}, - {0x3821, 0x47}, - {0x3822, 0x48}, - {0x3826, 0x00}, - {0x3827, 0x08}, - {0x382a, 0x03}, - {0x382b, 0x01}, - {0x3830, 0x08}, - {0x3836, 0x02}, - {0x3837, 0x00}, - {0x3838, 0x10}, - {0x3841, 0xff}, - {0x3846, 0x48}, - {0x3861, 0x00}, - {0x3862, 0x04}, - {0x3863, 0x06}, - {0x3a11, 0x01}, - {0x3a12, 0x78}, - {0x3b00, 0x00}, - {0x3b02, 0x00}, - {0x3b03, 0x00}, - {0x3b04, 0x00}, - {0x3b05, 0x00}, - {0x3c00, 0x89}, - {0x3c01, 0xab}, - {0x3c02, 0x01}, - {0x3c03, 0x00}, - {0x3c04, 0x00}, - {0x3c05, 0x03}, - {0x3c06, 0x00}, - {0x3c07, 0x05}, - {0x3c0c, 0x00}, - {0x3c0d, 0x00}, - {0x3c0e, 0x00}, - {0x3c0f, 0x00}, - {0x3c40, 0x00}, - {0x3c41, 0xa3}, - {0x3c43, 0x7d}, - {0x3c45, 0xd7}, - {0x3c47, 0xfc}, - {0x3c50, 0x05}, - {0x3c52, 0xaa}, - {0x3c54, 0x71}, - {0x3c56, 0x80}, - {0x3d85, 0x17}, - {0x3f03, 0x00}, - {0x3f0a, 0x00}, - {0x3f0b, 0x00}, - {0x4001, 0x60}, - {0x4009, 0x05}, - {0x4020, 0x00}, - {0x4021, 0x00}, - {0x4022, 0x00}, - {0x4023, 0x00}, - {0x4024, 0x00}, - {0x4025, 0x00}, - {0x4026, 0x00}, - {0x4027, 0x00}, - {0x4028, 0x00}, - {0x4029, 0x00}, - {0x402a, 0x00}, - {0x402b, 0x00}, - {0x402c, 0x00}, - {0x402d, 0x00}, - {0x402e, 0x00}, - {0x402f, 0x00}, - {0x4040, 0x00}, - {0x4041, 0x03}, - {0x4042, 0x00}, - {0x4043, 0x7A}, - {0x4044, 0x00}, - {0x4045, 0x7A}, - {0x4046, 0x00}, - {0x4047, 0x7A}, - {0x4048, 0x00}, - {0x4049, 0x7A}, - {0x4307, 0x30}, - {0x4500, 0x58}, - {0x4501, 0x04}, +/* + * Xclk 24Mhz + * Pclk 210Mhz + * linelength 3360(0xd20 + * framelength 2038(0x7f6) + * grabwindow_width 1296 + * grabwindow_height 960 + * max_framerate 30fps + * mipi_datarate per lane 840Mbps + */ +static const struct regval ov5670_1296x960_regs_2lane[] = { + // 1296x960 30fps 2 lane MIPI 840Mbps/lane + {0x0100, 0x00}, + {0x3501, 0x3d}, //exposure M + {0x3623, 0x00}, //analog control + {0x366e, 0x08}, //analog control + {0x370b, 0x1b}, //sensor control + {0x3808, 0x05}, //x output size H + {0x3809, 0x10}, //x output size L + {0x380a, 0x03}, //y output size H + {0x380b, 0xc0}, //y output size L + {0x380c, 0x06}, //HTS H + {0x380d, 0x90}, //HTS L + {0x380e, 0x07}, //VTS H + {0x380f, 0xf6}, //VTS L + {0x3814, 0x03}, //x inc odd + {0x3820, 0x90}, //vsyn48_blc on, vflip off + {0x3821, 0x47}, //hsync_en_o, mirror on, dig_bin on + {0x382a, 0x03}, //y inc odd + {0x4009, 0x05}, //BLC, black line end line {0x4502, 0x48}, - {0x4503, 0x10}, {0x4508, 0x55}, {0x4509, 0x55}, {0x450a, 0x00}, - {0x450b, 0x00}, {0x4600, 0x00}, - {0x4601, 0x80}, - {0x4700, 0xa4}, - {0x4800, 0x4c}, - {0x4816, 0x53}, - {0x481f, 0x40}, - {0x4837, 0x13}, - {0x5000, 0x56}, - {0x5001, 0x01}, - {0x5002, 0x28}, - {0x5004, 0x0c}, - {0x5006, 0x0c}, - {0x5007, 0xe0}, - {0x5008, 0x01}, - {0x5009, 0xb0}, - {0x5901, 0x00}, - {0x5a01, 0x00}, - {0x5a03, 0x00}, - {0x5a04, 0x0c}, - {0x5a05, 0xe0}, - {0x5a06, 0x09}, - {0x5a07, 0xb0}, - {0x5a08, 0x06}, - {0x5e00, 0x00}, - {0x3734, 0x40}, - {0x5b00, 0x01}, - {0x5b01, 0x10}, - {0x5b02, 0x01}, - {0x5b03, 0xdb}, - {0x3d8c, 0x71}, - {0x3d8d, 0xea}, - {0x4017, 0x10}, - {0x3618, 0x2a}, - {0x5780, 0x3e}, - {0x5781, 0x0f}, - {0x5782, 0x44}, - {0x5783, 0x02}, - {0x5784, 0x01}, - {0x5785, 0x01}, - {0x5786, 0x00}, - {0x5787, 0x04}, - {0x5788, 0x02}, - {0x5789, 0x0f}, - {0x578a, 0xfd}, - {0x578b, 0xf5}, - {0x578c, 0xf5}, - {0x578d, 0x03}, - {0x578e, 0x08}, - {0x578f, 0x0c}, - {0x5790, 0x08}, - {0x5791, 0x06}, - {0x5792, 0x00}, - {0x5793, 0x52}, - {0x5794, 0xa3}, - {0x3503, 0x00}, - {0x5045, 0x05}, - {0x4003, 0x40}, - {0x5048, 0x40} + {0x4601, 0x81}, + {0x4017, 0x10}, //BLC, offset trigger threshold + //{0x0100, 0x01}, + + {REG_NULL, 0x00} }; -static const struct ov5670_reg mode_640x360_regs[] = { - {0x3000, 0x00}, - {0x3002, 0x21}, - {0x3005, 0xf0}, - {0x3007, 0x00}, - {0x3015, 0x0f}, - {0x3018, 0x32}, - {0x301a, 0xf0}, - {0x301b, 0xf0}, - {0x301c, 0xf0}, - {0x301d, 0xf0}, - {0x301e, 0xf0}, - {0x3030, 0x00}, - {0x3031, 0x0a}, - {0x303c, 0xff}, - {0x303e, 0xff}, - {0x3040, 0xf0}, - {0x3041, 0x00}, - {0x3042, 0xf0}, - {0x3106, 0x11}, - {0x3500, 0x00}, - {0x3501, 0x80}, - {0x3502, 0x00}, - {0x3503, 0x04}, - {0x3504, 0x03}, - {0x3505, 0x83}, - {0x3508, 0x04}, - {0x3509, 0x00}, - {0x350e, 0x04}, - {0x350f, 0x00}, - {0x3510, 0x00}, - {0x3511, 0x02}, - {0x3512, 0x00}, - {0x3601, 0xc8}, - {0x3610, 0x88}, - {0x3612, 0x48}, - {0x3614, 0x5b}, - {0x3615, 0x96}, - {0x3621, 0xd0}, - {0x3622, 0x00}, - {0x3623, 0x04}, - {0x3633, 0x13}, - {0x3634, 0x13}, - {0x3635, 0x13}, - {0x3636, 0x13}, - {0x3645, 0x13}, - {0x3646, 0x82}, - {0x3650, 0x00}, - {0x3652, 0xff}, - {0x3655, 0x20}, - {0x3656, 0xff}, - {0x365a, 0xff}, - {0x365e, 0xff}, - {0x3668, 0x00}, - {0x366a, 0x07}, - {0x366e, 0x08}, - {0x366d, 0x00}, - {0x366f, 0x80}, - {0x3700, 0x28}, - {0x3701, 0x10}, - {0x3702, 0x3a}, - {0x3703, 0x19}, - {0x3704, 0x10}, - {0x3705, 0x00}, - {0x3706, 0x66}, - {0x3707, 0x08}, - {0x3708, 0x34}, - {0x3709, 0x40}, - {0x370a, 0x01}, - {0x370b, 0x1b}, - {0x3714, 0x24}, - {0x371a, 0x3e}, - {0x3733, 0x00}, - {0x3734, 0x00}, - {0x373a, 0x05}, - {0x373b, 0x06}, - {0x373c, 0x0a}, - {0x373f, 0xa0}, - {0x3755, 0x00}, - {0x3758, 0x00}, - {0x375b, 0x0e}, - {0x3766, 0x5f}, - {0x3768, 0x00}, - {0x3769, 0x22}, - {0x3773, 0x08}, - {0x3774, 0x1f}, - {0x3776, 0x06}, - {0x37a0, 0x88}, - {0x37a1, 0x5c}, - {0x37a7, 0x88}, - {0x37a8, 0x70}, - {0x37aa, 0x88}, - {0x37ab, 0x48}, - {0x37b3, 0x66}, - {0x37c2, 0x04}, - {0x37c5, 0x00}, - {0x37c8, 0x00}, - {0x3800, 0x00}, - {0x3801, 0x0c}, - {0x3802, 0x00}, - {0x3803, 0x04}, - {0x3804, 0x0a}, - {0x3805, 0x33}, - {0x3806, 0x07}, - {0x3807, 0xa3}, - {0x3808, 0x02}, - {0x3809, 0x80}, - {0x380a, 0x01}, - {0x380b, 0x68}, - {0x380c, 0x06}, - {0x380d, 0x90}, - {0x380e, 0x08}, - {0x380f, 0x08}, - {0x3811, 0x04}, - {0x3813, 0x02}, - {0x3814, 0x07}, - {0x3815, 0x01}, - {0x3816, 0x00}, - {0x3817, 0x00}, - {0x3818, 0x00}, - {0x3819, 0x00}, - {0x3820, 0x94}, - {0x3821, 0xc6}, - {0x3822, 0x48}, - {0x3826, 0x00}, - {0x3827, 0x08}, - {0x382a, 0x07}, - {0x382b, 0x01}, - {0x3830, 0x08}, - {0x3836, 0x02}, - {0x3837, 0x00}, - {0x3838, 0x10}, - {0x3841, 0xff}, - {0x3846, 0x48}, - {0x3861, 0x00}, - {0x3862, 0x04}, - {0x3863, 0x06}, - {0x3a11, 0x01}, - {0x3a12, 0x78}, - {0x3b00, 0x00}, - {0x3b02, 0x00}, - {0x3b03, 0x00}, - {0x3b04, 0x00}, - {0x3b05, 0x00}, - {0x3c00, 0x89}, - {0x3c01, 0xab}, - {0x3c02, 0x01}, - {0x3c03, 0x00}, - {0x3c04, 0x00}, - {0x3c05, 0x03}, - {0x3c06, 0x00}, - {0x3c07, 0x05}, - {0x3c0c, 0x00}, - {0x3c0d, 0x00}, - {0x3c0e, 0x00}, - {0x3c0f, 0x00}, - {0x3c40, 0x00}, - {0x3c41, 0xa3}, - {0x3c43, 0x7d}, - {0x3c45, 0xd7}, - {0x3c47, 0xfc}, - {0x3c50, 0x05}, - {0x3c52, 0xaa}, - {0x3c54, 0x71}, - {0x3c56, 0x80}, - {0x3d85, 0x17}, - {0x3f03, 0x00}, - {0x3f0a, 0x00}, - {0x3f0b, 0x00}, - {0x4001, 0x60}, - {0x4009, 0x05}, - {0x4020, 0x00}, - {0x4021, 0x00}, - {0x4022, 0x00}, - {0x4023, 0x00}, - {0x4024, 0x00}, - {0x4025, 0x00}, - {0x4026, 0x00}, - {0x4027, 0x00}, - {0x4028, 0x00}, - {0x4029, 0x00}, - {0x402a, 0x00}, - {0x402b, 0x00}, - {0x402c, 0x00}, - {0x402d, 0x00}, - {0x402e, 0x00}, - {0x402f, 0x00}, - {0x4040, 0x00}, - {0x4041, 0x03}, - {0x4042, 0x00}, - {0x4043, 0x7A}, - {0x4044, 0x00}, - {0x4045, 0x7A}, - {0x4046, 0x00}, - {0x4047, 0x7A}, - {0x4048, 0x00}, - {0x4049, 0x7A}, - {0x4307, 0x30}, - {0x4500, 0x58}, - {0x4501, 0x04}, - {0x4502, 0x40}, - {0x4503, 0x10}, - {0x4508, 0x55}, - {0x4509, 0x55}, - {0x450a, 0x02}, - {0x450b, 0x00}, - {0x4600, 0x00}, - {0x4601, 0x40}, - {0x4700, 0xa4}, - {0x4800, 0x4c}, - {0x4816, 0x53}, - {0x481f, 0x40}, - {0x4837, 0x13}, - {0x5000, 0x56}, - {0x5001, 0x01}, - {0x5002, 0x28}, - {0x5004, 0x0c}, - {0x5006, 0x0c}, - {0x5007, 0xe0}, - {0x5008, 0x01}, - {0x5009, 0xb0}, - {0x5901, 0x00}, - {0x5a01, 0x00}, - {0x5a03, 0x00}, - {0x5a04, 0x0c}, - {0x5a05, 0xe0}, - {0x5a06, 0x09}, - {0x5a07, 0xb0}, - {0x5a08, 0x06}, - {0x5e00, 0x00}, - {0x3734, 0x40}, - {0x5b00, 0x01}, - {0x5b01, 0x10}, - {0x5b02, 0x01}, - {0x5b03, 0xdb}, - {0x3d8c, 0x71}, - {0x3d8d, 0xea}, - {0x4017, 0x10}, - {0x3618, 0x2a}, - {0x5780, 0x3e}, - {0x5781, 0x0f}, - {0x5782, 0x44}, - {0x5783, 0x02}, - {0x5784, 0x01}, - {0x5785, 0x01}, - {0x5786, 0x00}, - {0x5787, 0x04}, - {0x5788, 0x02}, - {0x5789, 0x0f}, - {0x578a, 0xfd}, - {0x578b, 0xf5}, - {0x578c, 0xf5}, - {0x578d, 0x03}, - {0x578e, 0x08}, - {0x578f, 0x0c}, - {0x5790, 0x08}, - {0x5791, 0x06}, - {0x5792, 0x00}, - {0x5793, 0x52}, - {0x5794, 0xa3}, - {0x3503, 0x00}, - {0x5045, 0x05}, - {0x4003, 0x40}, - {0x5048, 0x40} +static const struct ov5670_mode supported_modes_2lane[] = { + { + .width = 2592, + .height = 1944, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x07d0, + .hts_def = 0x0d20, + .vts_def = 0x07f6, + .reg_list = ov5670_2592x1944_regs_2lane, + }, + { + .width = 1296, + .height = 960, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x03d0, + .hts_def = 0x0d20, + .vts_def = 0x07f6, + .reg_list = ov5670_1296x960_regs_2lane, + }, +}; + +static const struct ov5670_mode *supported_modes; + +static const s64 link_freq_menu_items[] = { + MIPI_FREQ }; static const char * const ov5670_test_pattern_menu[] = { @@ -1719,134 +666,71 @@ static const char * const ov5670_test_pattern_menu[] = { "Vertical Color Bar Type 1", }; -/* Supported link frequencies */ -#define OV5670_LINK_FREQ_422MHZ 422400000 -#define OV5670_LINK_FREQ_422MHZ_INDEX 0 -static const struct ov5670_link_freq_config link_freq_configs[] = { - { - /* pixel_rate = link_freq * 2 * nr_of_lanes / bits_per_sample */ - .pixel_rate = (OV5670_LINK_FREQ_422MHZ * 2 * 2) / 10, - .reg_list = { - .num_of_regs = ARRAY_SIZE(mipi_data_rate_840mbps), - .regs = mipi_data_rate_840mbps, +/* Write registers up to 4 at a time */ +static int ov5670_write_reg(struct i2c_client *client, u16 reg, + u32 len, u32 val) +{ + u32 buf_i, val_i; + u8 buf[6]; + u8 *val_p; + __be32 val_be; + + dev_dbg(&client->dev, "%s(%d) enter!\n", __func__, __LINE__); + dev_dbg(&client->dev, "write reg(0x%x val:0x%x)!\n", reg, val); + + if (len > 4) + return -EINVAL; + + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + + val_be = cpu_to_be32(val); + val_p = (u8 *)&val_be; + buf_i = 2; + val_i = 4 - len; + + while (val_i < 4) + buf[buf_i++] = val_p[val_i++]; + + if (i2c_master_send(client, buf, len + 2) != len + 2) { + dev_err(&client->dev, + "write reg(0x%x val:0x%x)failed !\n", reg, val); + return -EIO; + } + return 0; +} + +static int ov5670_write_array(struct i2c_client *client, + const struct regval *regs) +{ + int i, delay_ms, ret = 0; + + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) { + if (regs[i].addr == DELAY_MS) { + delay_ms = regs[i].val; + dev_info(&client->dev, "delay(%d) ms !\n", delay_ms); + usleep_range(1000 * delay_ms, 1000 * delay_ms + 100); + continue; } + ret = ov5670_write_reg(client, regs[i].addr, + OV5670_REG_VALUE_08BIT, regs[i].val); + if (ret) + dev_err(&client->dev, "%s failed !\n", __func__); } -}; - -static const s64 link_freq_menu_items[] = { - OV5670_LINK_FREQ_422MHZ -}; - -/* - * OV5670 sensor supports following resolutions with full FOV: - * 4:3 ==> {2592x1944, 1296x972, 648x486} - * 16:9 ==> {2560x1440, 1280x720, 640x360} - */ -static const struct ov5670_mode supported_modes[] = { - { - .width = 2592, - .height = 1944, - .vts_def = OV5670_VTS_30FPS, - .vts_min = OV5670_VTS_30FPS, - .reg_list = { - .num_of_regs = ARRAY_SIZE(mode_2592x1944_regs), - .regs = mode_2592x1944_regs, - }, - .link_freq_index = OV5670_LINK_FREQ_422MHZ_INDEX, - }, - { - .width = 1296, - .height = 972, - .vts_def = OV5670_VTS_30FPS, - .vts_min = 996, - .reg_list = { - .num_of_regs = ARRAY_SIZE(mode_1296x972_regs), - .regs = mode_1296x972_regs, - }, - .link_freq_index = OV5670_LINK_FREQ_422MHZ_INDEX, - }, - { - .width = 648, - .height = 486, - .vts_def = OV5670_VTS_30FPS, - .vts_min = 516, - .reg_list = { - .num_of_regs = ARRAY_SIZE(mode_648x486_regs), - .regs = mode_648x486_regs, - }, - .link_freq_index = OV5670_LINK_FREQ_422MHZ_INDEX, - }, - { - .width = 2560, - .height = 1440, - .vts_def = OV5670_VTS_30FPS, - .vts_min = OV5670_VTS_30FPS, - .reg_list = { - .num_of_regs = ARRAY_SIZE(mode_2560x1440_regs), - .regs = mode_2560x1440_regs, - }, - .link_freq_index = OV5670_LINK_FREQ_422MHZ_INDEX, - }, - { - .width = 1280, - .height = 720, - .vts_def = OV5670_VTS_30FPS, - .vts_min = 1020, - .reg_list = { - .num_of_regs = ARRAY_SIZE(mode_1280x720_regs), - .regs = mode_1280x720_regs, - }, - .link_freq_index = OV5670_LINK_FREQ_422MHZ_INDEX, - }, - { - .width = 640, - .height = 360, - .vts_def = OV5670_VTS_30FPS, - .vts_min = 510, - .reg_list = { - .num_of_regs = ARRAY_SIZE(mode_640x360_regs), - .regs = mode_640x360_regs, - }, - .link_freq_index = OV5670_LINK_FREQ_422MHZ_INDEX, - } -}; - -struct ov5670 { - struct v4l2_subdev sd; - struct media_pad pad; - - struct v4l2_ctrl_handler ctrl_handler; - /* V4L2 Controls */ - struct v4l2_ctrl *link_freq; - struct v4l2_ctrl *pixel_rate; - struct v4l2_ctrl *vblank; - struct v4l2_ctrl *hblank; - struct v4l2_ctrl *exposure; - - /* Current mode */ - const struct ov5670_mode *cur_mode; - - /* To serialize asynchronus callbacks */ - struct mutex mutex; - - /* Streaming on/off */ - bool streaming; -}; - -#define to_ov5670(_sd) container_of(_sd, struct ov5670, sd) + return ret; +} /* Read registers up to 4 at a time */ -static int ov5670_read_reg(struct ov5670 *ov5670, u16 reg, unsigned int len, - u32 *val) +static int ov5670_read_reg(struct i2c_client *client, u16 reg, + unsigned int len, u32 *val) { - struct i2c_client *client = v4l2_get_subdevdata(&ov5670->sd); struct i2c_msg msgs[2]; u8 *data_be_p; __be32 data_be = 0; __be16 reg_addr_be = cpu_to_be16(reg); int ret; - if (len > 4) + if (len > 4 || !len) return -EINVAL; data_be_p = (u8 *)&data_be; @@ -1871,136 +755,676 @@ static int ov5670_read_reg(struct ov5670 *ov5670, u16 reg, unsigned int len, return 0; } -/* Write registers up to 4 at a time */ -static int ov5670_write_reg(struct ov5670 *ov5670, u16 reg, unsigned int len, - u32 val) +/* Check Register value */ +#ifdef CHECK_REG_VALUE +static int ov5670_reg_verify(struct i2c_client *client, + const struct regval *regs) { - struct i2c_client *client = v4l2_get_subdevdata(&ov5670->sd); - int buf_i; - int val_i; - u8 buf[6]; - u8 *val_p; - __be32 tmp; + u32 i; + int ret = 0; + u32 value; - if (len > 4) - return -EINVAL; + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) { + ret = ov5670_read_reg(client, regs[i].addr, + OV5670_REG_VALUE_08BIT, &value); + if (value != regs[i].val) { + dev_info(&client->dev, "%s: 0x%04x is 0x%x instead of 0x%x\n", + __func__, regs[i].addr, value, regs[i].val); + } + } + return ret; +} +#endif - buf[0] = reg >> 8; - buf[1] = reg & 0xff; - - tmp = cpu_to_be32(val); - val_p = (u8 *)&tmp; - buf_i = 2; - val_i = 4 - len; - - while (val_i < 4) - buf[buf_i++] = val_p[val_i++]; - - if (i2c_master_send(client, buf, len + 2) != len + 2) - return -EIO; - - return 0; +static int ov5670_get_reso_dist(const struct ov5670_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); } -/* Write a list of registers */ -static int ov5670_write_regs(struct ov5670 *ov5670, - const struct ov5670_reg *regs, unsigned int len) +static const struct ov5670_mode * +ov5670_find_best_fit(struct ov5670 *ov5670, + struct v4l2_subdev_format *fmt) { - struct i2c_client *client = v4l2_get_subdevdata(&ov5670->sd); + struct v4l2_mbus_framefmt *framefmt = &fmt->format; + int dist; + int cur_best_fit = 0; + int cur_best_fit_dist = -1; unsigned int i; - int ret; - for (i = 0; i < len; i++) { - ret = ov5670_write_reg(ov5670, 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; + for (i = 0; i < ov5670->cfg_num; i++) { + dist = ov5670_get_reso_dist(&supported_modes[i], framefmt); + if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { + cur_best_fit_dist = dist; + cur_best_fit = i; } } - return 0; + return &supported_modes[cur_best_fit]; } -static int ov5670_write_reg_list(struct ov5670 *ov5670, - const struct ov5670_reg_list *r_list) -{ - return ov5670_write_regs(ov5670, r_list->regs, r_list->num_of_regs); -} - -/* Open sub-device */ -static int ov5670_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +static int ov5670_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) { struct ov5670 *ov5670 = to_ov5670(sd); - struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, fh->pad, 0); + const struct ov5670_mode *mode; + s64 h_blank, vblank_def; mutex_lock(&ov5670->mutex); - /* Initialize try_fmt */ - try_fmt->width = ov5670->cur_mode->width; - try_fmt->height = ov5670->cur_mode->height; - try_fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; - try_fmt->field = V4L2_FIELD_NONE; + mode = ov5670_find_best_fit(ov5670, fmt); + fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10; + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; +#else + mutex_unlock(&ov5670->mutex); + return -ENOTTY; +#endif + } else { + ov5670->cur_mode = mode; + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(ov5670->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(ov5670->vblank, vblank_def, + OV5670_VTS_MAX - mode->height, + 1, vblank_def); + } - /* No crop or compose */ mutex_unlock(&ov5670->mutex); return 0; } -static int ov5670_update_digital_gain(struct ov5670 *ov5670, u32 d_gain) +static int ov5670_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) { - int ret; + struct ov5670 *ov5670 = to_ov5670(sd); + const struct ov5670_mode *mode = ov5670->cur_mode; - ret = ov5670_write_reg(ov5670, OV5670_REG_R_DGTL_GAIN, - OV5670_REG_VALUE_16BIT, d_gain); - if (ret) - return ret; + mutex_lock(&ov5670->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); +#else + mutex_unlock(&ov5670->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10; + fmt->format.field = V4L2_FIELD_NONE; + } + mutex_unlock(&ov5670->mutex); - ret = ov5670_write_reg(ov5670, OV5670_REG_G_DGTL_GAIN, - OV5670_REG_VALUE_16BIT, d_gain); - if (ret) - return ret; + return 0; +} - return ov5670_write_reg(ov5670, OV5670_REG_B_DGTL_GAIN, - OV5670_REG_VALUE_16BIT, d_gain); +static int ov5670_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index != 0) + return -EINVAL; + code->code = MEDIA_BUS_FMT_SBGGR10_1X10; + + return 0; +} + +static int ov5670_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct ov5670 *ov5670 = to_ov5670(sd); + + if (fse->index >= ov5670->cfg_num) + return -EINVAL; + + if (fse->code != MEDIA_BUS_FMT_SBGGR10_1X10) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = supported_modes[fse->index].width; + fse->max_height = supported_modes[fse->index].height; + fse->min_height = supported_modes[fse->index].height; + + return 0; } static int ov5670_enable_test_pattern(struct ov5670 *ov5670, u32 pattern) { u32 val; - int ret; - - /* Set the bayer order that we support */ - ret = ov5670_write_reg(ov5670, OV5670_REG_TEST_PATTERN_CTRL, - OV5670_REG_VALUE_08BIT, 0); - if (ret) - return ret; - - ret = ov5670_read_reg(ov5670, OV5670_REG_TEST_PATTERN, - OV5670_REG_VALUE_08BIT, &val); - if (ret) - return ret; if (pattern) - val |= OV5670_TEST_PATTERN_ENABLE; + val = (pattern - 1) | OV5670_TEST_PATTERN_ENABLE; else - val &= ~OV5670_TEST_PATTERN_ENABLE; + val = OV5670_TEST_PATTERN_DISABLE; - return ov5670_write_reg(ov5670, OV5670_REG_TEST_PATTERN, + return ov5670_write_reg(ov5670->client, OV5670_REG_TEST_PATTERN, OV5670_REG_VALUE_08BIT, val); } -/* Initialize control handlers */ +static void ov5670_get_otp(struct ov5670_otp_info *otp, + struct rkmodule_inf *inf) +{ + u32 i; + int rg, bg; + + /* fac */ + if (otp->flag & 0x80) { + inf->fac.flag = 1; + inf->fac.year = otp->year; + inf->fac.month = otp->month; + inf->fac.day = otp->day; + + for (i = 0; i < ARRAY_SIZE(ov5670_module_info) - 1; i++) { + if (ov5670_module_info[i].id == otp->module_id) + break; + } + strlcpy(inf->fac.module, ov5670_module_info[i].name, + sizeof(inf->fac.module)); + + for (i = 0; i < ARRAY_SIZE(ov5670_lens_info) - 1; i++) { + if (ov5670_lens_info[i].id == otp->lens_id) + break; + } + strlcpy(inf->fac.lens, ov5670_lens_info[i].name, + sizeof(inf->fac.lens)); + } + + /* awb */ + if (otp->flag & 0x40) { + rg = otp->rg_ratio; + bg = otp->bg_ratio; + + inf->awb.flag = 1; + inf->awb.r_value = rg; + inf->awb.b_value = bg; + inf->awb.gr_value = 0x200; + inf->awb.gb_value = 0x200; + + inf->awb.golden_r_value = 0; + inf->awb.golden_b_value = 0; + inf->awb.golden_gr_value = 0; + inf->awb.golden_gb_value = 0; + } +} + +static int ov5670_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct ov5670 *ov5670 = to_ov5670(sd); + const struct ov5670_mode *mode = ov5670->cur_mode; + + mutex_lock(&ov5670->mutex); + fi->interval = mode->max_fps; + mutex_unlock(&ov5670->mutex); + + return 0; +} + +static void ov5670_get_module_inf(struct ov5670 *ov5670, + struct rkmodule_inf *inf) +{ + struct ov5670_otp_info *otp = ov5670->otp; + memset(inf, 0, sizeof(*inf)); + strlcpy(inf->base.sensor, OV5670_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, ov5670->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, ov5670->len_name, sizeof(inf->base.lens)); + if (otp) + ov5670_get_otp(otp, inf); +} + +static void ov5670_set_awb_cfg(struct ov5670 *ov5670, + struct rkmodule_awb_cfg *cfg) +{ + mutex_lock(&ov5670->mutex); + memcpy(&ov5670->awb_cfg, cfg, sizeof(*cfg)); + mutex_unlock(&ov5670->mutex); +} + +static long ov5670_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct ov5670 *ov5670 = to_ov5670(sd); + long ret = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + ov5670_get_module_inf(ov5670, (struct rkmodule_inf *)arg); + break; + case RKMODULE_AWB_CFG: + ov5670_set_awb_cfg(ov5670, (struct rkmodule_awb_cfg *)arg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long ov5670_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_awb_cfg *awb_cfg; + long ret; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = ov5670_ioctl(sd, cmd, inf); + if (!ret) + ret = copy_to_user(up, inf, sizeof(*inf)); + kfree(inf); + break; + case RKMODULE_AWB_CFG: + awb_cfg = kzalloc(sizeof(*awb_cfg), GFP_KERNEL); + if (!awb_cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(awb_cfg, up, sizeof(*awb_cfg)); + if (!ret) + ret = ov5670_ioctl(sd, cmd, awb_cfg); + kfree(awb_cfg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif +/*--------------------------------------------------------------------------*/ +static int ov5670_apply_otp(struct ov5670 *ov5670) +{ + int rg, bg, R_gain, G_gain, B_gain, base_gain; + struct i2c_client *client = ov5670->client; + struct ov5670_otp_info *otp_ptr = ov5670->otp; + struct rkmodule_awb_cfg *awb_cfg = &ov5670->awb_cfg; + u32 golden_bg_ratio = 0; + u32 golden_rg_ratio = 0; + u32 golden_g_value = 0; + + if (awb_cfg->enable) { + golden_g_value = (awb_cfg->golden_gb_value + + awb_cfg->golden_gr_value) / 2; + if (golden_g_value != 0) { + golden_rg_ratio = awb_cfg->golden_r_value * 0x200 + / golden_g_value; + golden_bg_ratio = awb_cfg->golden_b_value * 0x200 + / golden_g_value; + } else { + golden_rg_ratio = RG_Ratio_Typical_Default; + golden_bg_ratio = BG_Ratio_Typical_Default; + } + } + + /* apply OTP WB Calibration */ + if (otp_ptr->flag & 0x40) { + rg = otp_ptr->rg_ratio; + bg = otp_ptr->bg_ratio; + + /* calculate G gain */ + R_gain = (golden_rg_ratio * 1000) / rg; + B_gain = (golden_bg_ratio * 1000) / bg; + G_gain = 1000; + if (R_gain < 1000 || B_gain < 1000) { + if (R_gain < B_gain) + base_gain = R_gain; + else + base_gain = B_gain; + } else { + base_gain = G_gain; + } + R_gain = 0x400 * R_gain / (base_gain); + B_gain = 0x400 * B_gain / (base_gain); + G_gain = 0x400 * G_gain / (base_gain); + + /* update sensor WB gain */ + if (R_gain > 0x400) { + ov5670_write_1byte(client, 0x5032, R_gain >> 8); + ov5670_write_1byte(client, 0x5033, R_gain & 0x00ff); + } + if (G_gain > 0x400) { + ov5670_write_1byte(client, 0x5034, G_gain >> 8); + ov5670_write_1byte(client, 0x5035, G_gain & 0x00ff); + } + if (B_gain > 0x400) { + ov5670_write_1byte(client, 0x5036, B_gain >> 8); + ov5670_write_1byte(client, 0x5037, B_gain & 0x00ff); + } + + dev_info(&client->dev, "apply awb gain: 0x%x, 0x%x, 0x%x\n", + R_gain, G_gain, B_gain); + } + return 0; +} + +static int __ov5670_start_stream(struct ov5670 *ov5670) +{ + int ret; + + ret = ov5670_write_array(ov5670->client, ov5670->cur_mode->reg_list); + if (ret) + return ret; + +#ifdef CHECK_REG_VALUE + usleep_range(10000, 20000); + /* verify default values to make sure everything has */ + /* been written correctly as expected */ + dev_info(&ov5670->client->dev, "%s:Check register value!\n", + __func__); + ret = ov5670_reg_verify(ov5670->client, ov5670_global_regs); + if (ret) + return ret; + + ret = ov5670_reg_verify(ov5670->client, ov5670->cur_mode->reg_list); + if (ret) + return ret; +#endif + + /* In case these controls are set before streaming */ + mutex_unlock(&ov5670->mutex); + ret = v4l2_ctrl_handler_setup(&ov5670->ctrl_handler); + mutex_lock(&ov5670->mutex); + if (ret) + return ret; + + if (ov5670->otp) + ret = ov5670_apply_otp(ov5670); + + if (ret) + dev_info(&ov5670->client->dev, "APPly otp failed!\n"); + + ret = ov5670_write_reg(ov5670->client, OV5670_REG_CTRL_MODE, + OV5670_REG_VALUE_08BIT, OV5670_MODE_STREAMING); + return ret; +} + +static int __ov5670_stop_stream(struct ov5670 *ov5670) +{ + return ov5670_write_reg(ov5670->client, OV5670_REG_CTRL_MODE, + OV5670_REG_VALUE_08BIT, OV5670_MODE_SW_STANDBY); +} + +static int ov5670_s_stream(struct v4l2_subdev *sd, int on) +{ + struct ov5670 *ov5670 = to_ov5670(sd); + struct i2c_client *client = ov5670->client; + int ret = 0; + + dev_info(&client->dev, "%s: on: %d, %dx%d@%d\n", __func__, on, + ov5670->cur_mode->width, + ov5670->cur_mode->height, + ov5670->cur_mode->max_fps.denominator); + + mutex_lock(&ov5670->mutex); + on = !!on; + if (on == ov5670->streaming) + goto unlock_and_return; + + if (on) { + dev_info(&client->dev, "stream on!!!\n"); + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = __ov5670_start_stream(ov5670); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + dev_info(&client->dev, "stream off!!!\n"); + __ov5670_stop_stream(ov5670); + pm_runtime_put(&client->dev); + } + + ov5670->streaming = on; + +unlock_and_return: + mutex_unlock(&ov5670->mutex); + + return ret; +} + +static int ov5670_s_power(struct v4l2_subdev *sd, int on) +{ + struct ov5670 *ov5670 = to_ov5670(sd); + struct i2c_client *client = ov5670->client; + int ret = 0; + + dev_info(&client->dev, "%s(%d) on(%d)\n", __func__, __LINE__, on); + mutex_lock(&ov5670->mutex); + + /* If the power state is not modified - no work to do. */ + if (ov5670->power_on == !!on) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = ov5670_write_array(ov5670->client, ov5670_global_regs); + if (ret) { + v4l2_err(sd, "could not set init registers\n"); + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ov5670->power_on = true; + /* export gpio */ + if (!IS_ERR(ov5670->reset_gpio)) + gpiod_export(ov5670->reset_gpio, false); + if (!IS_ERR(ov5670->pwdn_gpio)) + gpiod_export(ov5670->pwdn_gpio, false); + } else { + pm_runtime_put(&client->dev); + ov5670->power_on = false; + } + +unlock_and_return: + mutex_unlock(&ov5670->mutex); + + return ret; +} + +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 ov5670_cal_delay(u32 cycles) +{ + return DIV_ROUND_UP(cycles, OV5670_XVCLK_FREQ / 1000 / 1000); +} + +static int __ov5670_power_on(struct ov5670 *ov5670) +{ + int ret; + u32 delay_us; + struct device *dev = &ov5670->client->dev; + + if (!IS_ERR(ov5670->power_gpio)) + gpiod_set_value_cansleep(ov5670->power_gpio, 1); + + usleep_range(1000, 2000); + + if (!IS_ERR_OR_NULL(ov5670->pins_default)) { + ret = pinctrl_select_state(ov5670->pinctrl, + ov5670->pins_default); + if (ret < 0) + dev_err(dev, "could not set pins\n"); + } + ret = clk_set_rate(ov5670->xvclk, OV5670_XVCLK_FREQ); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate (24MHz)\n"); + if (clk_get_rate(ov5670->xvclk) != OV5670_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); + ret = clk_prepare_enable(ov5670->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + + ret = regulator_bulk_enable(OV5670_NUM_SUPPLIES, ov5670->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + + if (!IS_ERR(ov5670->reset_gpio)) + gpiod_set_value_cansleep(ov5670->reset_gpio, 1); + + if (!IS_ERR(ov5670->pwdn_gpio)) + gpiod_set_value_cansleep(ov5670->pwdn_gpio, 1); + + /* export gpio */ + if (!IS_ERR(ov5670->reset_gpio)) + gpiod_export(ov5670->reset_gpio, false); + if (!IS_ERR(ov5670->pwdn_gpio)) + gpiod_export(ov5670->pwdn_gpio, false); + + /* 8192 cycles prior to first SCCB transaction */ + delay_us = ov5670_cal_delay(8192); + usleep_range(delay_us, delay_us * 2); + usleep_range(10000, 20000); + return 0; + +disable_clk: + clk_disable_unprepare(ov5670->xvclk); + + return ret; +} + +static void __ov5670_power_off(struct ov5670 *ov5670) +{ + int ret; + struct device *dev = &ov5670->client->dev; + + if (!IS_ERR(ov5670->pwdn_gpio)) + gpiod_set_value_cansleep(ov5670->pwdn_gpio, 0); + clk_disable_unprepare(ov5670->xvclk); + if (!IS_ERR(ov5670->reset_gpio)) + gpiod_set_value_cansleep(ov5670->reset_gpio, 0); + if (!IS_ERR_OR_NULL(ov5670->pins_sleep)) { + ret = pinctrl_select_state(ov5670->pinctrl, + ov5670->pins_sleep); + if (ret < 0) + dev_dbg(dev, "could not set pins\n"); + } + if (!IS_ERR(ov5670->power_gpio)) + gpiod_set_value_cansleep(ov5670->power_gpio, 0); + + regulator_bulk_disable(OV5670_NUM_SUPPLIES, ov5670->supplies); +} + +static int ov5670_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov5670 *ov5670 = to_ov5670(sd); + + return __ov5670_power_on(ov5670); +} + +static int ov5670_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov5670 *ov5670 = to_ov5670(sd); + + __ov5670_power_off(ov5670); + + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int ov5670_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct ov5670 *ov5670 = to_ov5670(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + const struct ov5670_mode *def_mode = &supported_modes[0]; + + mutex_lock(&ov5670->mutex); + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10; + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&ov5670->mutex); + /* No crop or compose */ + + return 0; +} +#endif + +static const struct dev_pm_ops ov5670_pm_ops = { + SET_RUNTIME_PM_OPS(ov5670_runtime_suspend, + ov5670_runtime_resume, NULL) +}; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops ov5670_internal_ops = { + .open = ov5670_open, +}; +#endif + +static const struct v4l2_subdev_core_ops ov5670_core_ops = { + .s_power = ov5670_s_power, + .ioctl = ov5670_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = ov5670_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops ov5670_video_ops = { + .s_stream = ov5670_s_stream, + .g_frame_interval = ov5670_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops ov5670_pad_ops = { + .enum_mbus_code = ov5670_enum_mbus_code, + .enum_frame_size = ov5670_enum_frame_sizes, + .get_fmt = ov5670_get_fmt, + .set_fmt = ov5670_set_fmt, +}; + +static const struct v4l2_subdev_ops ov5670_subdev_ops = { + .core = &ov5670_core_ops, + .video = &ov5670_video_ops, + .pad = &ov5670_pad_ops, +}; + static int ov5670_set_ctrl(struct v4l2_ctrl *ctrl) { struct ov5670 *ov5670 = container_of(ctrl->handler, struct ov5670, ctrl_handler); - struct i2c_client *client = v4l2_get_subdevdata(&ov5670->sd); + struct i2c_client *client = ov5670->client; s64 max; int ret = 0; @@ -2008,41 +1432,59 @@ static int ov5670_set_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_VBLANK: /* Update max exposure while meeting expected vblanking */ - max = ov5670->cur_mode->height + ctrl->val - 8; + max = ov5670->cur_mode->height + ctrl->val - 4; __v4l2_ctrl_modify_range(ov5670->exposure, ov5670->exposure->minimum, max, - ov5670->exposure->step, max); + ov5670->exposure->step, + ov5670->exposure->default_value); break; } - /* V4L2 controls values will be applied only when power is already up */ - if (pm_runtime_get_if_in_use(&client->dev) <= 0) + if (pm_runtime_get(&client->dev) <= 0) return 0; switch (ctrl->id) { - case V4L2_CID_ANALOGUE_GAIN: - ret = ov5670_write_reg(ov5670, OV5670_REG_ANALOG_GAIN, - OV5670_REG_VALUE_16BIT, ctrl->val); - break; - case V4L2_CID_DIGITAL_GAIN: - ret = ov5670_update_digital_gain(ov5670, ctrl->val); - break; case V4L2_CID_EXPOSURE: /* 4 least significant bits of expsoure are fractional part */ - ret = ov5670_write_reg(ov5670, OV5670_REG_EXPOSURE, + /*group 0*/ + ret = ov5670_write_reg(ov5670->client, OV5670_REG_GROUP, + OV5670_REG_VALUE_08BIT, 0x00); + ret = ov5670_write_reg(ov5670->client, OV5670_REG_EXPOSURE, OV5670_REG_VALUE_24BIT, ctrl->val << 4); + ret = ov5670_write_reg(ov5670->client, OV5670_REG_GROUP, + OV5670_REG_VALUE_08BIT, 0x10); + ret = ov5670_write_reg(ov5670->client, OV5670_REG_GROUP, + OV5670_REG_VALUE_08BIT, 0xa0); + + break; + case V4L2_CID_ANALOGUE_GAIN: + /*group 1*/ + ret = ov5670_write_reg(ov5670->client, OV5670_REG_GROUP, + OV5670_REG_VALUE_08BIT, 0x01); + + ret = ov5670_write_reg(ov5670->client, OV5670_REG_GAIN_L, + OV5670_REG_VALUE_08BIT, + ctrl->val & OV5670_GAIN_L_MASK); + ret |= ov5670_write_reg(ov5670->client, OV5670_REG_GAIN_H, + OV5670_REG_VALUE_08BIT, + (ctrl->val >> OV5670_GAIN_H_SHIFT) & + OV5670_GAIN_H_MASK); + ret = ov5670_write_reg(ov5670->client, OV5670_REG_GROUP, + OV5670_REG_VALUE_08BIT, 0x11); + ret = ov5670_write_reg(ov5670->client, OV5670_REG_GROUP, + OV5670_REG_VALUE_08BIT, 0xa1); break; case V4L2_CID_VBLANK: - /* Update VTS that meets expected vertical blanking */ - ret = ov5670_write_reg(ov5670, OV5670_REG_VTS, + + ret = ov5670_write_reg(ov5670->client, OV5670_REG_VTS, OV5670_REG_VALUE_16BIT, - ov5670->cur_mode->height + ctrl->val); + ctrl->val + ov5670->cur_mode->height); break; case V4L2_CID_TEST_PATTERN: ret = ov5670_enable_test_pattern(ov5670, ctrl->val); break; default: - dev_info(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", + dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", __func__, ctrl->id, ctrl->val); break; } @@ -2056,473 +1498,421 @@ static const struct v4l2_ctrl_ops ov5670_ctrl_ops = { .s_ctrl = ov5670_set_ctrl, }; -/* Initialize control handlers */ -static int ov5670_init_controls(struct ov5670 *ov5670) +static int ov5670_initialize_controls(struct ov5670 *ov5670) { - struct v4l2_ctrl_handler *ctrl_hdlr; - s64 vblank_max; - s64 vblank_def; - s64 vblank_min; - s64 exposure_max; + const struct ov5670_mode *mode; + struct v4l2_ctrl_handler *handler; + struct v4l2_ctrl *ctrl; + s64 exposure_max, vblank_def; + u32 h_blank; int ret; - ctrl_hdlr = &ov5670->ctrl_handler; - ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8); + handler = &ov5670->ctrl_handler; + mode = ov5670->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 8); if (ret) return ret; + handler->lock = &ov5670->mutex; - ctrl_hdlr->lock = &ov5670->mutex; - ov5670->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, - &ov5670_ctrl_ops, - V4L2_CID_LINK_FREQ, - 0, 0, link_freq_menu_items); - if (ov5670->link_freq) - ov5670->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, + 0, 0, link_freq_menu_items); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; - /* By default, V4L2_CID_PIXEL_RATE is read only */ - ov5670->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov5670_ctrl_ops, - V4L2_CID_PIXEL_RATE, 0, - link_freq_configs[0].pixel_rate, - 1, - link_freq_configs[0].pixel_rate); + v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, + 0, ov5670->pixel_rate, 1, ov5670->pixel_rate); - vblank_max = OV5670_VTS_MAX - ov5670->cur_mode->height; - vblank_def = ov5670->cur_mode->vts_def - ov5670->cur_mode->height; - vblank_min = ov5670->cur_mode->vts_min - ov5670->cur_mode->height; - ov5670->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov5670_ctrl_ops, - V4L2_CID_VBLANK, vblank_min, - vblank_max, 1, vblank_def); - - ov5670->hblank = v4l2_ctrl_new_std( - ctrl_hdlr, &ov5670_ctrl_ops, V4L2_CID_HBLANK, - OV5670_FIXED_PPL - ov5670->cur_mode->width, - OV5670_FIXED_PPL - ov5670->cur_mode->width, 1, - OV5670_FIXED_PPL - ov5670->cur_mode->width); + h_blank = mode->hts_def - mode->width; + ov5670->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); if (ov5670->hblank) ov5670->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; - /* Get min, max, step, default from sensor */ - v4l2_ctrl_new_std(ctrl_hdlr, &ov5670_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, - ANALOG_GAIN_MIN, ANALOG_GAIN_MAX, ANALOG_GAIN_STEP, - ANALOG_GAIN_DEFAULT); + vblank_def = mode->vts_def - mode->height; + ov5670->vblank = v4l2_ctrl_new_std(handler, &ov5670_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + OV5670_VTS_MAX - mode->height, + 1, vblank_def); - /* Digital gain */ - v4l2_ctrl_new_std(ctrl_hdlr, &ov5670_ctrl_ops, V4L2_CID_DIGITAL_GAIN, - OV5670_DGTL_GAIN_MIN, OV5670_DGTL_GAIN_MAX, - OV5670_DGTL_GAIN_STEP, OV5670_DGTL_GAIN_DEFAULT); + exposure_max = mode->vts_def - 4; + ov5670->exposure = v4l2_ctrl_new_std(handler, &ov5670_ctrl_ops, + V4L2_CID_EXPOSURE, OV5670_EXPOSURE_MIN, + exposure_max, OV5670_EXPOSURE_STEP, + mode->exp_def); - /* Get min, max, step, default from sensor */ - exposure_max = ov5670->cur_mode->vts_def - 8; - ov5670->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov5670_ctrl_ops, - V4L2_CID_EXPOSURE, - OV5670_EXPOSURE_MIN, - exposure_max, OV5670_EXPOSURE_STEP, - exposure_max); + ov5670->anal_gain = v4l2_ctrl_new_std(handler, &ov5670_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, ANALOG_GAIN_MIN, + ANALOG_GAIN_MAX, ANALOG_GAIN_STEP, + ANALOG_GAIN_DEFAULT); - v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &ov5670_ctrl_ops, - V4L2_CID_TEST_PATTERN, - ARRAY_SIZE(ov5670_test_pattern_menu) - 1, - 0, 0, ov5670_test_pattern_menu); + ov5670->test_pattern = v4l2_ctrl_new_std_menu_items(handler, + &ov5670_ctrl_ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov5670_test_pattern_menu) - 1, + 0, 0, ov5670_test_pattern_menu); - if (ctrl_hdlr->error) { - ret = ctrl_hdlr->error; - goto error; + if (handler->error) { + ret = handler->error; + dev_err(&ov5670->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; } - ov5670->sd.ctrl_handler = ctrl_hdlr; + ov5670->subdev.ctrl_handler = handler; return 0; -error: - v4l2_ctrl_handler_free(ctrl_hdlr); +err_free_handler: + v4l2_ctrl_handler_free(handler); return ret; } -static int ov5670_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) +static int ov5670_otp_read(struct ov5670 *ov5670) { - /* Only one bayer order GRBG is supported */ - if (code->index > 0) - return -EINVAL; + int otp_flag, addr, temp, i; + struct ov5670_otp_info *otp_ptr; + struct device *dev = &ov5670->client->dev; + struct i2c_client *client = ov5670->client; - code->code = MEDIA_BUS_FMT_SGRBG10_1X10; + otp_ptr = devm_kzalloc(dev, sizeof(*otp_ptr), GFP_KERNEL); + if (!otp_ptr) + return -ENOMEM; - return 0; -} - -static int ov5670_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_frame_size_enum *fse) -{ - if (fse->index >= ARRAY_SIZE(supported_modes)) - return -EINVAL; - - if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) - return -EINVAL; - - fse->min_width = supported_modes[fse->index].width; - fse->max_width = fse->min_width; - fse->min_height = supported_modes[fse->index].height; - fse->max_height = fse->min_height; - - return 0; -} - -static void ov5670_update_pad_format(const struct ov5670_mode *mode, - struct v4l2_subdev_format *fmt) -{ - fmt->format.width = mode->width; - fmt->format.height = mode->height; - fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10; - fmt->format.field = V4L2_FIELD_NONE; -} - -static int ov5670_do_get_pad_format(struct ov5670 *ov5670, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt) -{ - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&ov5670->sd, cfg, - fmt->pad); + otp_flag = 0; + ov5670_read_1byte(client, 0x7010, &otp_flag); + if ((otp_flag & 0xc0) == 0x40) + addr = 0x7011; /* base address of info group 1 */ + else if ((otp_flag & 0x30) == 0x10) + addr = 0x7016; /* base address of info group 2 */ + else if ((otp_flag & 0x0c) == 0x04) + addr = 0x701b; /* base address of info group 3 */ else - ov5670_update_pad_format(ov5670->cur_mode, fmt); + addr = 0; - return 0; -} - -static int ov5670_get_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt) -{ - struct ov5670 *ov5670 = to_ov5670(sd); - int ret; - - mutex_lock(&ov5670->mutex); - ret = ov5670_do_get_pad_format(ov5670, cfg, fmt); - mutex_unlock(&ov5670->mutex); - - return ret; -} - -static int ov5670_set_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt) -{ - struct ov5670 *ov5670 = to_ov5670(sd); - const struct ov5670_mode *mode; - s32 vblank_def; - s32 h_blank; - - mutex_lock(&ov5670->mutex); - - fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10; - - mode = v4l2_find_nearest_size(supported_modes, - ARRAY_SIZE(supported_modes), - width, height, - fmt->format.width, fmt->format.height); - ov5670_update_pad_format(mode, fmt); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; + if (addr != 0) { + otp_ptr->flag = 0x80; /* valid info in OTP */ + ov5670_read_1byte(client, addr, &otp_ptr->module_id); + ov5670_read_1byte(client, addr + 1, &otp_ptr->lens_id); + ov5670_read_1byte(client, addr + 2, &otp_ptr->year); + ov5670_read_1byte(client, addr + 3, &otp_ptr->month); + ov5670_read_1byte(client, addr + 4, &otp_ptr->day); + dev_info(dev, "fac info: module(0x%x) lens(0x%x) time(%d_%d_%d)!\n", + otp_ptr->module_id, + otp_ptr->lens_id, + otp_ptr->year, + otp_ptr->month, + otp_ptr->day); } else { - ov5670->cur_mode = mode; - __v4l2_ctrl_s_ctrl(ov5670->link_freq, mode->link_freq_index); - __v4l2_ctrl_s_ctrl_int64( - ov5670->pixel_rate, - link_freq_configs[mode->link_freq_index].pixel_rate); - /* Update limits and set FPS to default */ - vblank_def = ov5670->cur_mode->vts_def - - ov5670->cur_mode->height; - __v4l2_ctrl_modify_range( - ov5670->vblank, - ov5670->cur_mode->vts_min - ov5670->cur_mode->height, - OV5670_VTS_MAX - ov5670->cur_mode->height, 1, - vblank_def); - __v4l2_ctrl_s_ctrl(ov5670->vblank, vblank_def); - h_blank = OV5670_FIXED_PPL - ov5670->cur_mode->width; - __v4l2_ctrl_modify_range(ov5670->hblank, h_blank, h_blank, 1, - h_blank); + otp_ptr->flag = 0x00; /* not info in OTP */ + otp_ptr->module_id = 0x00; + otp_ptr->lens_id = 0x00; + otp_ptr->year = 0x00; + otp_ptr->month = 0x00; + otp_ptr->day = 0x00; + dev_warn(dev, "fac info: module(0x%x) lens(0x%x) time(%d_%d_%d)!\n", + otp_ptr->module_id, + otp_ptr->lens_id, + otp_ptr->year, + otp_ptr->month, + otp_ptr->day); } - mutex_unlock(&ov5670->mutex); + /* OTP base information and WB calibration data */ + ov5670_read_1byte(client, 0x7020, &otp_flag); + if ((otp_flag & 0xc0) == 0x40) + addr = 0x7021; /* base address of info group 1 */ + else if ((otp_flag & 0x30) == 0x10) + addr = 0x7024; /* base address of info group 2 */ + else if ((otp_flag & 0x0c) == 0x04) + addr = 0x7027; /* base address of info group 3 */ + else + addr = 0; - return 0; -} - -static int ov5670_get_skip_frames(struct v4l2_subdev *sd, u32 *frames) -{ - *frames = OV5670_NUM_OF_SKIP_FRAMES; - - return 0; -} - -/* Prepare streaming by writing default values and customized values */ -static int ov5670_start_streaming(struct ov5670 *ov5670) -{ - struct i2c_client *client = v4l2_get_subdevdata(&ov5670->sd); - const struct ov5670_reg_list *reg_list; - int link_freq_index; - int ret; - - /* Get out of from software reset */ - ret = ov5670_write_reg(ov5670, OV5670_REG_SOFTWARE_RST, - OV5670_REG_VALUE_08BIT, OV5670_SOFTWARE_RST); - if (ret) { - dev_err(&client->dev, "%s failed to set powerup registers\n", - __func__); - return ret; + if (addr != 0) { + otp_ptr->flag |= 0x40; /* valid info and AWB in OTP */ + ov5670_read_1byte(client, addr + 2, &temp); + ov5670_read_1byte(client, addr, &otp_ptr->rg_ratio); + otp_ptr->rg_ratio = (otp_ptr->rg_ratio << 2) + + ((temp >> 6) & 0x03); + ov5670_read_1byte(client, addr + 1, &otp_ptr->bg_ratio); + otp_ptr->bg_ratio = (otp_ptr->bg_ratio << 2) + + ((temp >> 4) & 0x03); + dev_info(dev, "awb info: (0x%x, 0x%x)!\n", + otp_ptr->rg_ratio, otp_ptr->bg_ratio); + } else { + otp_ptr->rg_ratio = 0x00; + otp_ptr->bg_ratio = 0x00; + dev_warn(dev, "awb info: (0x%x, 0x%x)!\n", + otp_ptr->rg_ratio, otp_ptr->bg_ratio); } - /* Setup PLL */ - link_freq_index = ov5670->cur_mode->link_freq_index; - reg_list = &link_freq_configs[link_freq_index].reg_list; - ret = ov5670_write_reg_list(ov5670, reg_list); - if (ret) { - dev_err(&client->dev, "%s failed to set plls\n", __func__); - return ret; - } + for (i = 0x7010; i <= 0x7029; i++) + ov5670_write_1byte(client, i, 0); /* clear OTP buffer */ - /* Apply default values of current mode */ - reg_list = &ov5670->cur_mode->reg_list; - ret = ov5670_write_reg_list(ov5670, reg_list); - if (ret) { - dev_err(&client->dev, "%s failed to set mode\n", __func__); - return ret; - } - - ret = __v4l2_ctrl_handler_setup(ov5670->sd.ctrl_handler); - if (ret) - return ret; - - /* Write stream on list */ - ret = ov5670_write_reg(ov5670, OV5670_REG_MODE_SELECT, - OV5670_REG_VALUE_08BIT, OV5670_MODE_STREAMING); - if (ret) { - dev_err(&client->dev, "%s failed to set stream\n", __func__); - return ret; + if (otp_ptr->flag) { + ov5670->otp = otp_ptr; + } else { + ov5670->otp = NULL; + dev_info(dev, "otp is null!\n"); + devm_kfree(dev, otp_ptr); } return 0; } -static int ov5670_stop_streaming(struct ov5670 *ov5670) +static int ov5670_otp_check_read(struct ov5670 *ov5670) { - struct i2c_client *client = v4l2_get_subdevdata(&ov5670->sd); - int ret; - - ret = ov5670_write_reg(ov5670, OV5670_REG_MODE_SELECT, - OV5670_REG_VALUE_08BIT, OV5670_MODE_STANDBY); - if (ret) - dev_err(&client->dev, "%s failed to set stream\n", __func__); - - /* Return success even if it was an error, as there is nothing the - * caller can do about it. - */ - return 0; -} - -static int ov5670_set_stream(struct v4l2_subdev *sd, int enable) -{ - struct ov5670 *ov5670 = to_ov5670(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); + int temp = 0; int ret = 0; + struct i2c_client *client = ov5670->client; - mutex_lock(&ov5670->mutex); - if (ov5670->streaming == enable) - goto unlock_and_return; + /* stream on */ + ov5670_write_1byte(client, + OV5670_REG_CTRL_MODE, + OV5670_MODE_STREAMING); - if (enable) { - ret = pm_runtime_get_sync(&client->dev); - if (ret < 0) { - pm_runtime_put_noidle(&client->dev); - goto unlock_and_return; - } + ov5670_read_1byte(client, 0x5002, &temp); + ov5670_write_1byte(client, 0x5002, (temp & (~0x08))); - ret = ov5670_start_streaming(ov5670); - if (ret) - goto error; - } else { - ret = ov5670_stop_streaming(ov5670); - pm_runtime_put(&client->dev); - } - ov5670->streaming = enable; - goto unlock_and_return; + /* read OTP into buffer */ + ov5670_write_1byte(client, 0x3d84, 0xC0); + ov5670_write_1byte(client, 0x3d88, 0x70); /* OTP start address */ + ov5670_write_1byte(client, 0x3d89, 0x10); + ov5670_write_1byte(client, 0x3d8A, 0x70); /* OTP end address */ + ov5670_write_1byte(client, 0x3d8B, 0x29); + ov5670_write_1byte(client, 0x3d81, 0x01); /* load otp into buffer */ + usleep_range(10000, 20000); -error: - pm_runtime_put(&client->dev); + ret = ov5670_otp_read(ov5670); -unlock_and_return: - mutex_unlock(&ov5670->mutex); + /* set 0x5002[3] to "1" */ + ov5670_read_1byte(client, 0x5002, &temp); + ov5670_write_1byte(client, 0x5002, 0x08 | (temp & (~0x08))); + + /* stream off */ + ov5670_write_1byte(client, + OV5670_REG_CTRL_MODE, + OV5670_MODE_SW_STANDBY); return ret; } -static int __maybe_unused ov5670_suspend(struct device *dev) +static int ov5670_check_sensor_id(struct ov5670 *ov5670, + struct i2c_client *client) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct ov5670 *ov5670 = to_ov5670(sd); + struct device *dev = &ov5670->client->dev; + u32 id = 0; + int ret; - if (ov5670->streaming) - ov5670_stop_streaming(ov5670); + ret = ov5670_read_reg(client, OV5670_REG_CHIP_ID, + OV5670_REG_VALUE_16BIT, &id); + if (id != CHIP_ID) { + dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret); + return -ENODEV; + } + + dev_info(dev, "Detected OV%06x sensor\n", CHIP_ID); return 0; } -static int __maybe_unused ov5670_resume(struct device *dev) +static int ov5670_configure_regulators(struct ov5670 *ov5670) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct ov5670 *ov5670 = to_ov5670(sd); - int ret; + unsigned int i; - if (ov5670->streaming) { - ret = ov5670_start_streaming(ov5670); - if (ret) { - ov5670_stop_streaming(ov5670); - return ret; - } + for (i = 0; i < OV5670_NUM_SUPPLIES; i++) + ov5670->supplies[i].supply = ov5670_supply_names[i]; + + return devm_regulator_bulk_get(&ov5670->client->dev, + OV5670_NUM_SUPPLIES, + ov5670->supplies); +} + +static int ov5670_parse_of(struct ov5670 *ov5670) +{ + struct device *dev = &ov5670->client->dev; + struct device_node *endpoint; + struct fwnode_handle *fwnode; + int rval; + + endpoint = of_graph_get_next_endpoint(dev->of_node, NULL); + if (!endpoint) { + dev_err(dev, "Failed to get endpoint\n"); + return -EINVAL; + } + fwnode = of_fwnode_handle(endpoint); + rval = fwnode_property_read_u32_array(fwnode, "data-lanes", NULL, 0); + if (rval <= 0) { + dev_warn(dev, " Get mipi lane num failed!\n"); + return -1; + } + + ov5670->lane_num = rval; + if (2 == ov5670->lane_num) { + ov5670->cur_mode = &supported_modes_2lane[0]; + supported_modes = supported_modes_2lane; + ov5670->cfg_num = ARRAY_SIZE(supported_modes_2lane); + + /* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ + ov5670->pixel_rate = MIPI_FREQ * 2U * ov5670->lane_num / 8U; + dev_info(dev, "lane_num(%d) pixel_rate(%u)\n", + ov5670->lane_num, ov5670->pixel_rate); + } else { + dev_err(dev, "unsupported lane_num(%d)\n", ov5670->lane_num); + return -1; } return 0; } -/* Verify chip ID */ -static int ov5670_identify_module(struct ov5670 *ov5670) -{ - struct i2c_client *client = v4l2_get_subdevdata(&ov5670->sd); - int ret; - u32 val; - - ret = ov5670_read_reg(ov5670, OV5670_REG_CHIP_ID, - OV5670_REG_VALUE_24BIT, &val); - if (ret) - return ret; - - if (val != OV5670_CHIP_ID) { - dev_err(&client->dev, "chip id mismatch: %x!=%x\n", - OV5670_CHIP_ID, val); - return -ENXIO; - } - - return 0; -} - -static const struct v4l2_subdev_video_ops ov5670_video_ops = { - .s_stream = ov5670_set_stream, -}; - -static const struct v4l2_subdev_pad_ops ov5670_pad_ops = { - .enum_mbus_code = ov5670_enum_mbus_code, - .get_fmt = ov5670_get_pad_format, - .set_fmt = ov5670_set_pad_format, - .enum_frame_size = ov5670_enum_frame_size, -}; - -static const struct v4l2_subdev_sensor_ops ov5670_sensor_ops = { - .g_skip_frames = ov5670_get_skip_frames, -}; - -static const struct v4l2_subdev_ops ov5670_subdev_ops = { - .video = &ov5670_video_ops, - .pad = &ov5670_pad_ops, - .sensor = &ov5670_sensor_ops, -}; - -static const struct media_entity_operations ov5670_subdev_entity_ops = { - .link_validate = v4l2_subdev_link_validate, -}; - -static const struct v4l2_subdev_internal_ops ov5670_internal_ops = { - .open = ov5670_open, -}; - -static int ov5670_probe(struct i2c_client *client) +static int ov5670_probe(struct i2c_client *client, + const struct i2c_device_id *id) { + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; struct ov5670 *ov5670; - const char *err_msg; - u32 input_clk = 0; + struct v4l2_subdev *sd; + char facing[2] = "b"; int ret; - device_property_read_u32(&client->dev, "clock-frequency", &input_clk); - if (input_clk != 19200000) + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + ov5670 = devm_kzalloc(dev, sizeof(*ov5670), GFP_KERNEL); + if (!ov5670) + return -ENOMEM; + + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &ov5670->module_index); + if (ret) { + dev_warn(dev, "could not get module index!\n"); + ov5670->module_index = 0; + } + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &ov5670->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &ov5670->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &ov5670->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + + ov5670->client = client; + + ov5670->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(ov5670->xvclk)) { + dev_err(dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + ov5670->power_gpio = devm_gpiod_get(dev, "power", GPIOD_OUT_LOW); + if (IS_ERR(ov5670->power_gpio)) + dev_warn(dev, "Failed to get power-gpios, maybe no use\n"); + + ov5670->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ov5670->reset_gpio)) + dev_warn(dev, "Failed to get reset-gpios, maybe no use\n"); + + ov5670->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(ov5670->pwdn_gpio)) + dev_warn(dev, "Failed to get pwdn-gpios\n"); + + ret = ov5670_configure_regulators(ov5670); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + ret = ov5670_parse_of(ov5670); + if (ret != 0) return -EINVAL; - ov5670 = devm_kzalloc(&client->dev, sizeof(*ov5670), GFP_KERNEL); - if (!ov5670) { - ret = -ENOMEM; - err_msg = "devm_kzalloc() error"; - goto error_print; - } + ov5670->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR(ov5670->pinctrl)) { + ov5670->pins_default = + pinctrl_lookup_state(ov5670->pinctrl, + OF_CAMERA_PINCTRL_STATE_DEFAULT); + if (IS_ERR(ov5670->pins_default)) + dev_err(dev, "could not get default pinstate\n"); - /* Initialize subdev */ - v4l2_i2c_subdev_init(&ov5670->sd, client, &ov5670_subdev_ops); - - /* Check module identity */ - ret = ov5670_identify_module(ov5670); - if (ret) { - err_msg = "ov5670_identify_module() error"; - goto error_print; + ov5670->pins_sleep = + pinctrl_lookup_state(ov5670->pinctrl, + OF_CAMERA_PINCTRL_STATE_SLEEP); + if (IS_ERR(ov5670->pins_sleep)) + dev_err(dev, "could not get sleep pinstate\n"); } mutex_init(&ov5670->mutex); - /* Set default mode to max resolution */ - ov5670->cur_mode = &supported_modes[0]; + sd = &ov5670->subdev; + v4l2_i2c_subdev_init(sd, client, &ov5670_subdev_ops); + ret = ov5670_initialize_controls(ov5670); + if (ret) + goto err_destroy_mutex; - ret = ov5670_init_controls(ov5670); - if (ret) { - err_msg = "ov5670_init_controls() error"; - goto error_mutex_destroy; - } + ret = __ov5670_power_on(ov5670); + if (ret) + goto err_free_handler; - ov5670->sd.internal_ops = &ov5670_internal_ops; - ov5670->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - ov5670->sd.entity.ops = &ov5670_subdev_entity_ops; - ov5670->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; - - /* Source pad initialization */ - ov5670->pad.flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_pads_init(&ov5670->sd.entity, 1, &ov5670->pad); - if (ret) { - err_msg = "media_entity_pads_init() error"; - goto error_handler_free; - } - - /* Async register for subdev */ - ret = v4l2_async_register_subdev_sensor_common(&ov5670->sd); + ret = ov5670_check_sensor_id(ov5670, client); if (ret < 0) { - err_msg = "v4l2_async_register_subdev() error"; - goto error_entity_cleanup; + dev_info(&client->dev, "%s(%d) Check id failed\n" + "check following information:\n" + "Power/PowerDown/Reset/Mclk/I2cBus !!\n", + __func__, __LINE__); + goto err_power_off; + } + ov5670_otp_check_read(ov5670); + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &ov5670_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + ov5670->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &ov5670->pad); + if (ret < 0) + goto err_power_off; +#endif + + memset(facing, 0, sizeof(facing)); + if (strcmp(ov5670->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + ov5670->module_index, facing, + OV5670_NAME, dev_name(sd->dev)); + + ret = v4l2_async_register_subdev_sensor_common(sd); + if (ret) { + dev_err(dev, "v4l2 async register subdev failed\n"); + goto err_clean_entity; } - ov5670->streaming = false; - - /* - * Device is already turned on by i2c-core with ACPI domain PM. - * Enable runtime PM and turn off the device. - */ - pm_runtime_get_noresume(&client->dev); - pm_runtime_set_active(&client->dev); - pm_runtime_enable(&client->dev); - pm_runtime_put(&client->dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); return 0; -error_entity_cleanup: - media_entity_cleanup(&ov5670->sd.entity); - -error_handler_free: - v4l2_ctrl_handler_free(ov5670->sd.ctrl_handler); - -error_mutex_destroy: +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif +err_power_off: + __ov5670_power_off(ov5670); +err_free_handler: + v4l2_ctrl_handler_free(&ov5670->ctrl_handler); +err_destroy_mutex: mutex_destroy(&ov5670->mutex); -error_print: - dev_err(&client->dev, "%s: %s %d\n", __func__, err_msg, ret); - return ret; } @@ -2532,48 +1922,56 @@ static int ov5670_remove(struct i2c_client *client) struct ov5670 *ov5670 = to_ov5670(sd); v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) media_entity_cleanup(&sd->entity); - v4l2_ctrl_handler_free(sd->ctrl_handler); +#endif + v4l2_ctrl_handler_free(&ov5670->ctrl_handler); mutex_destroy(&ov5670->mutex); - /* - * Disable runtime PM but keep the device turned on. - * i2c-core with ACPI domain PM will turn off the device. - */ - pm_runtime_get_sync(&client->dev); pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __ov5670_power_off(ov5670); pm_runtime_set_suspended(&client->dev); - pm_runtime_put_noidle(&client->dev); return 0; } -static const struct dev_pm_ops ov5670_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(ov5670_suspend, ov5670_resume) +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id ov5670_of_match[] = { + { .compatible = "ovti,ov5670" }, + {}, }; - -#ifdef CONFIG_ACPI -static const struct acpi_device_id ov5670_acpi_ids[] = { - {"INT3479"}, - { /* sentinel */ } -}; - -MODULE_DEVICE_TABLE(acpi, ov5670_acpi_ids); +MODULE_DEVICE_TABLE(of, ov5670_of_match); #endif +static const struct i2c_device_id ov5670_match_id[] = { + { "ovti,ov5670", 0 }, + { }, +}; + static struct i2c_driver ov5670_i2c_driver = { .driver = { - .name = "ov5670", + .name = OV5670_NAME, .pm = &ov5670_pm_ops, - .acpi_match_table = ACPI_PTR(ov5670_acpi_ids), + .of_match_table = of_match_ptr(ov5670_of_match), }, - .probe_new = ov5670_probe, - .remove = ov5670_remove, + .probe = &ov5670_probe, + .remove = &ov5670_remove, + .id_table = ov5670_match_id, }; -module_i2c_driver(ov5670_i2c_driver); +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&ov5670_i2c_driver); +} -MODULE_AUTHOR("Rapolu, Chiranjeevi "); -MODULE_AUTHOR("Yang, Hyungwoo "); -MODULE_DESCRIPTION("Omnivision ov5670 sensor driver"); +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&ov5670_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_DESCRIPTION("OmniVision ov5670 sensor driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ov5695.c b/drivers/media/i2c/ov5695.c index 9a80decd93d3..64ad4a130ac2 100644 --- a/drivers/media/i2c/ov5695.c +++ b/drivers/media/i2c/ov5695.c @@ -3,6 +3,8 @@ * ov5695 driver * * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd. + * + * V0.0X01.0X01 add poweron function. */ #include @@ -14,11 +16,16 @@ #include #include #include +#include +#include +#include #include #include #include #include +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x01) + #ifndef V4L2_CID_DIGITAL_GAIN #define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN #endif @@ -69,6 +76,12 @@ #define OV5695_LANES 2 #define OV5695_BITS_PER_SAMPLE 10 +#define I2C_M_WR 0 +#define I2C_MSG_MAX 300 +#define I2C_DATA_MAX (I2C_MSG_MAX * 3) + +#define OV5695_NAME "ov5695" + static const char * const ov5695_supply_names[] = { "avdd", /* Analog power */ "dovdd", /* Digital I/O power */ @@ -96,6 +109,7 @@ struct ov5695 { struct i2c_client *client; struct clk *xvclk; struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; struct regulator_bulk_data supplies[OV5695_NUM_SUPPLIES]; struct v4l2_subdev subdev; @@ -109,7 +123,12 @@ struct ov5695 { struct v4l2_ctrl *test_pattern; struct mutex mutex; bool streaming; + bool power_on; const struct ov5695_mode *cur_mode; + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; }; #define to_ov5695(sd) container_of(sd, struct ov5695, subdev) @@ -384,175 +403,18 @@ static const struct regval ov5695_1920x1080_regs[] = { * mipi_datarate per lane 840Mbps */ static const struct regval ov5695_1296x972_regs[] = { - {0x0103, 0x01}, - {0x0100, 0x00}, - {0x0300, 0x04}, - {0x0301, 0x00}, - {0x0302, 0x69}, - {0x0303, 0x00}, - {0x0304, 0x00}, - {0x0305, 0x01}, - {0x0307, 0x00}, - {0x030b, 0x00}, - {0x030c, 0x00}, - {0x030d, 0x1e}, - {0x030e, 0x04}, - {0x030f, 0x03}, - {0x0312, 0x01}, - {0x3000, 0x00}, - {0x3002, 0xa1}, - {0x3008, 0x00}, - {0x3010, 0x00}, - {0x3016, 0x32}, - {0x3022, 0x51}, - {0x3106, 0x15}, - {0x3107, 0x01}, - {0x3108, 0x05}, - {0x3500, 0x00}, {0x3501, 0x3e}, - {0x3502, 0x00}, - {0x3503, 0x08}, - {0x3504, 0x03}, - {0x3505, 0x8c}, - {0x3507, 0x03}, - {0x3508, 0x00}, - {0x3509, 0x10}, - {0x350c, 0x00}, - {0x350d, 0x80}, - {0x3510, 0x00}, - {0x3511, 0x02}, - {0x3512, 0x00}, - {0x3601, 0x55}, - {0x3602, 0x58}, {0x3611, 0x58}, - {0x3614, 0x30}, - {0x3615, 0x77}, - {0x3621, 0x08}, - {0x3624, 0x40}, - {0x3633, 0x0c}, - {0x3634, 0x0c}, - {0x3635, 0x0c}, - {0x3636, 0x0c}, - {0x3638, 0x00}, - {0x3639, 0x00}, - {0x363a, 0x00}, - {0x363b, 0x00}, - {0x363c, 0xff}, - {0x363d, 0xfa}, - {0x3650, 0x44}, - {0x3651, 0x44}, - {0x3652, 0x44}, - {0x3653, 0x44}, - {0x3654, 0x44}, - {0x3655, 0x44}, - {0x3656, 0x44}, - {0x3657, 0x44}, - {0x3660, 0x00}, - {0x3661, 0x00}, - {0x3662, 0x00}, - {0x366a, 0x00}, - {0x366e, 0x0c}, - {0x3673, 0x04}, - {0x3700, 0x14}, - {0x3703, 0x0c}, {0x3706, 0x24}, {0x3714, 0x27}, - {0x3715, 0x01}, {0x3716, 0x00}, {0x3717, 0x02}, - {0x3733, 0x10}, - {0x3734, 0x40}, - {0x373f, 0xa0}, - {0x3765, 0x20}, - {0x37a1, 0x1d}, - {0x37a8, 0x26}, - {0x37ab, 0x14}, - {0x37c2, 0x04}, {0x37c3, 0xf0}, - {0x37cb, 0x09}, - {0x37cc, 0x13}, - {0x37cd, 0x1f}, - {0x37ce, 0x1f}, - {0x3800, 0x00}, - {0x3801, 0x00}, - {0x3802, 0x00}, - {0x3803, 0x00}, - {0x3804, 0x0a}, - {0x3805, 0x3f}, - {0x3806, 0x07}, - {0x3807, 0xaf}, - {0x3808, 0x05}, - {0x3809, 0x10}, - {0x380a, 0x03}, - {0x380b, 0xcc}, - {0x380c, 0x02}, {0x380d, 0xe4}, {0x380e, 0x03}, {0x380f, 0xf4}, - {0x3810, 0x00}, {0x3811, 0x00}, - {0x3812, 0x00}, - {0x3813, 0x06}, - {0x3814, 0x03}, - {0x3815, 0x01}, - {0x3816, 0x03}, - {0x3817, 0x01}, - {0x3818, 0x00}, - {0x3819, 0x00}, - {0x381a, 0x00}, - {0x381b, 0x01}, - {0x3820, 0x8b}, - {0x3821, 0x01}, - {0x3c80, 0x08}, - {0x3c82, 0x00}, - {0x3c83, 0x00}, - {0x3c88, 0x00}, - {0x3d85, 0x14}, - {0x3f02, 0x08}, - {0x3f03, 0x10}, - {0x4008, 0x02}, - {0x4009, 0x09}, - {0x404e, 0x20}, - {0x4501, 0x00}, - {0x4502, 0x10}, - {0x4800, 0x00}, - {0x481f, 0x2a}, - {0x4837, 0x13}, {0x5000, 0x13}, - {0x5780, 0x3e}, - {0x5781, 0x0f}, - {0x5782, 0x44}, - {0x5783, 0x02}, - {0x5784, 0x01}, - {0x5785, 0x01}, - {0x5786, 0x00}, - {0x5787, 0x04}, - {0x5788, 0x02}, - {0x5789, 0x0f}, - {0x578a, 0xfd}, - {0x578b, 0xf5}, - {0x578c, 0xf5}, - {0x578d, 0x03}, - {0x578e, 0x08}, - {0x578f, 0x0c}, - {0x5790, 0x08}, - {0x5791, 0x06}, - {0x5792, 0x00}, - {0x5793, 0x52}, - {0x5794, 0xa3}, - {0x5b00, 0x00}, - {0x5b01, 0x1c}, - {0x5b02, 0x00}, - {0x5b03, 0x7f}, - {0x5b05, 0x6c}, - {0x5e10, 0xfc}, - {0x4010, 0xf1}, - {0x3503, 0x08}, - {0x3505, 0x8c}, - {0x3507, 0x03}, - {0x3508, 0x00}, - {0x3509, 0xf8}, - {0x0100, 0x01}, {REG_NULL, 0x00} }; @@ -733,14 +595,58 @@ static int ov5695_write_reg(struct i2c_client *client, u16 reg, static int ov5695_write_array(struct i2c_client *client, const struct regval *regs) { - u32 i; + u8 *data; + u32 i, j = 0, k = 0; int ret = 0; + struct i2c_msg *msg; - for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) - ret = ov5695_write_reg(client, regs[i].addr, - OV5695_REG_VALUE_08BIT, regs[i].val); + msg = kmalloc((sizeof(struct i2c_msg) * I2C_MSG_MAX), + GFP_KERNEL); + if (!msg) + return -ENOMEM; - return ret; + data = kmalloc((sizeof(unsigned char) * I2C_DATA_MAX), + GFP_KERNEL); + if (!data) { + kfree(msg); + return -ENOMEM; + } + + for (i = 0; regs[i].addr != REG_NULL; i++) { + (msg + j)->addr = client->addr; + (msg + j)->flags = I2C_M_WR; + (msg + j)->buf = (data + k); + + data[k + 0] = (u8)(regs[i].addr >> 8); + data[k + 1] = (u8)(regs[i].addr & 0xFF); + data[k + 2] = (u8)(regs[i].val & 0xFF); + k = k + 3; + (msg + j)->len = 3; + + if (j++ == (I2C_MSG_MAX - 1)) { + ret = i2c_transfer(client->adapter, msg, j); + if (ret < 0) { + kfree(msg); + kfree(data); + return ret; + } + j = 0; + k = 0; + } + } + + if (j != 0) { + ret = i2c_transfer(client->adapter, msg, j); + if (ret < 0) { + kfree(msg); + kfree(data); + return ret; + } + } + kfree(msg); + kfree(data); + + return 0; } /* Read registers up to 4 at a time */ @@ -753,7 +659,7 @@ static int ov5695_read_reg(struct i2c_client *client, u16 reg, unsigned int len, __be16 reg_addr_be = cpu_to_be16(reg); int ret; - if (len > 4) + if (len > 4 || !len) return -EINVAL; data_be_p = (u8 *)&data_be; @@ -911,19 +817,102 @@ static int ov5695_enable_test_pattern(struct ov5695 *ov5695, u32 pattern) OV5695_REG_VALUE_08BIT, val); } +static int ov5695_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct ov5695 *ov5695 = to_ov5695(sd); + const struct ov5695_mode *mode = ov5695->cur_mode; + + mutex_lock(&ov5695->mutex); + fi->interval.numerator = 10000; + fi->interval.denominator = mode->max_fps * 10000; + mutex_unlock(&ov5695->mutex); + + return 0; +} + +static void ov5695_get_module_inf(struct ov5695 *ov5695, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strlcpy(inf->base.sensor, OV5695_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, ov5695->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, ov5695->len_name, sizeof(inf->base.lens)); +} + +static long ov5695_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct ov5695 *ov5695 = to_ov5695(sd); + long ret = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + ov5695_get_module_inf(ov5695, (struct rkmodule_inf *)arg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long ov5695_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_awb_cfg *cfg; + long ret; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = ov5695_ioctl(sd, cmd, inf); + if (!ret) + ret = copy_to_user(up, inf, sizeof(*inf)); + kfree(inf); + break; + case RKMODULE_AWB_CFG: + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(cfg, up, sizeof(*cfg)); + if (!ret) + ret = ov5695_ioctl(sd, cmd, cfg); + kfree(cfg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + static int __ov5695_start_stream(struct ov5695 *ov5695) { int ret; - ret = ov5695_write_array(ov5695->client, ov5695_global_regs); - if (ret) - return ret; ret = ov5695_write_array(ov5695->client, ov5695->cur_mode->reg_list); if (ret) return ret; /* In case these controls are set before streaming */ - ret = __v4l2_ctrl_handler_setup(&ov5695->ctrl_handler); + mutex_unlock(&ov5695->mutex); + ret = v4l2_ctrl_handler_setup(&ov5695->ctrl_handler); + mutex_lock(&ov5695->mutex); if (ret) return ret; @@ -974,6 +963,44 @@ unlock_and_return: return ret; } +static int ov5695_s_power(struct v4l2_subdev *sd, int on) +{ + struct ov5695 *ov5695 = to_ov5695(sd); + struct i2c_client *client = ov5695->client; + int ret = 0; + + mutex_lock(&ov5695->mutex); + + /* If the power state is not modified - no work to do. */ + if (ov5695->power_on == !!on) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = ov5695_write_array(ov5695->client, ov5695_global_regs); + if (ret) { + v4l2_err(sd, "could not set init registers\n"); + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ov5695->power_on = true; + } else { + pm_runtime_put(&client->dev); + ov5695->power_on = false; + } + +unlock_and_return: + mutex_unlock(&ov5695->mutex); + + return ret; +} + /* Calculate the delay in us by clock rate and clock cycles */ static inline u32 ov5695_cal_delay(u32 cycles) { @@ -986,13 +1013,21 @@ static int __ov5695_power_on(struct ov5695 *ov5695) u32 delay_us; struct device *dev = &ov5695->client->dev; + ret = clk_set_rate(ov5695->xvclk, OV5695_XVCLK_FREQ); + if (ret < 0) { + dev_err(dev, "Failed to set xvclk rate (24MHz)\n"); + return ret; + } + if (clk_get_rate(ov5695->xvclk) != OV5695_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); ret = clk_prepare_enable(ov5695->xvclk); if (ret < 0) { dev_err(dev, "Failed to enable xvclk\n"); return ret; } - gpiod_set_value_cansleep(ov5695->reset_gpio, 1); + if (!IS_ERR(ov5695->reset_gpio)) + gpiod_set_value_cansleep(ov5695->reset_gpio, 1); ret = regulator_bulk_enable(OV5695_NUM_SUPPLIES, ov5695->supplies); if (ret < 0) { @@ -1000,7 +1035,11 @@ static int __ov5695_power_on(struct ov5695 *ov5695) goto disable_clk; } - gpiod_set_value_cansleep(ov5695->reset_gpio, 0); + if (!IS_ERR(ov5695->reset_gpio)) + gpiod_set_value_cansleep(ov5695->reset_gpio, 0); + + if (!IS_ERR(ov5695->pwdn_gpio)) + gpiod_set_value_cansleep(ov5695->pwdn_gpio, 1); /* 8192 cycles prior to first SCCB transaction */ delay_us = ov5695_cal_delay(8192); @@ -1016,12 +1055,15 @@ disable_clk: static void __ov5695_power_off(struct ov5695 *ov5695) { + if (!IS_ERR(ov5695->pwdn_gpio)) + gpiod_set_value_cansleep(ov5695->pwdn_gpio, 0); clk_disable_unprepare(ov5695->xvclk); - gpiod_set_value_cansleep(ov5695->reset_gpio, 1); + if (!IS_ERR(ov5695->reset_gpio)) + gpiod_set_value_cansleep(ov5695->reset_gpio, 1); regulator_bulk_disable(OV5695_NUM_SUPPLIES, ov5695->supplies); } -static int __maybe_unused ov5695_runtime_resume(struct device *dev) +static int ov5695_runtime_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct v4l2_subdev *sd = i2c_get_clientdata(client); @@ -1030,7 +1072,7 @@ static int __maybe_unused ov5695_runtime_resume(struct device *dev) return __ov5695_power_on(ov5695); } -static int __maybe_unused ov5695_runtime_suspend(struct device *dev) +static int ov5695_runtime_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct v4l2_subdev *sd = i2c_get_clientdata(client); @@ -1074,8 +1116,17 @@ static const struct v4l2_subdev_internal_ops ov5695_internal_ops = { }; #endif +static const struct v4l2_subdev_core_ops ov5695_core_ops = { + .s_power = ov5695_s_power, + .ioctl = ov5695_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = ov5695_compat_ioctl32, +#endif +}; + static const struct v4l2_subdev_video_ops ov5695_video_ops = { .s_stream = ov5695_s_stream, + .g_frame_interval = ov5695_g_frame_interval, }; static const struct v4l2_subdev_pad_ops ov5695_pad_ops = { @@ -1086,6 +1137,7 @@ static const struct v4l2_subdev_pad_ops ov5695_pad_ops = { }; static const struct v4l2_subdev_ops ov5695_subdev_ops = { + .core = &ov5695_core_ops, .video = &ov5695_video_ops, .pad = &ov5695_pad_ops, }; @@ -1110,7 +1162,7 @@ static int ov5695_set_ctrl(struct v4l2_ctrl *ctrl) break; } - if (pm_runtime_get_if_in_use(&client->dev) <= 0) + if (pm_runtime_get(&client->dev) <= 0) return 0; switch (ctrl->id) { @@ -1127,7 +1179,7 @@ static int ov5695_set_ctrl(struct v4l2_ctrl *ctrl) ret = ov5695_write_reg(ov5695->client, OV5695_REG_DIGI_GAIN_L, OV5695_REG_VALUE_08BIT, ctrl->val & OV5695_DIGI_GAIN_L_MASK); - ret = ov5695_write_reg(ov5695->client, OV5695_REG_DIGI_GAIN_H, + ret |= ov5695_write_reg(ov5695->client, OV5695_REG_DIGI_GAIN_H, OV5695_REG_VALUE_08BIT, ctrl->val >> OV5695_DIGI_GAIN_H_SHIFT); break; @@ -1143,7 +1195,7 @@ static int ov5695_set_ctrl(struct v4l2_ctrl *ctrl) dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", __func__, ctrl->id, ctrl->val); break; - }; + } pm_runtime_put(&client->dev); @@ -1240,7 +1292,7 @@ static int ov5695_check_sensor_id(struct ov5695 *ov5695, OV5695_REG_VALUE_24BIT, &id); if (id != CHIP_ID) { dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret); - return ret; + return -ENODEV; } dev_info(dev, "Detected OV%06x sensor\n", CHIP_ID); @@ -1264,14 +1316,34 @@ static int ov5695_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; + struct device_node *node = dev->of_node; struct ov5695 *ov5695; struct v4l2_subdev *sd; + char facing[2]; int ret; + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + ov5695 = devm_kzalloc(dev, sizeof(*ov5695), GFP_KERNEL); if (!ov5695) return -ENOMEM; + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &ov5695->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &ov5695->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &ov5695->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &ov5695->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + ov5695->client = client; ov5695->cur_mode = &supported_modes[0]; @@ -1280,20 +1352,16 @@ static int ov5695_probe(struct i2c_client *client, dev_err(dev, "Failed to get xvclk\n"); return -EINVAL; } - ret = clk_set_rate(ov5695->xvclk, OV5695_XVCLK_FREQ); - if (ret < 0) { - dev_err(dev, "Failed to set xvclk rate (24MHz)\n"); - return ret; - } - if (clk_get_rate(ov5695->xvclk) != OV5695_XVCLK_FREQ) - dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); ov5695->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(ov5695->reset_gpio)) { - dev_err(dev, "Failed to get reset-gpios\n"); - return -EINVAL; + dev_warn(dev, "Failed to get reset-gpios\n"); } + ov5695->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(ov5695->pwdn_gpio)) + dev_warn(dev, "Failed to get pwdn-gpios\n"); + ret = ov5695_configure_regulators(ov5695); if (ret) { dev_err(dev, "Failed to get power regulators\n"); @@ -1318,7 +1386,8 @@ static int ov5695_probe(struct i2c_client *client, #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API sd->internal_ops = &ov5695_internal_ops; - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; #endif #if defined(CONFIG_MEDIA_CONTROLLER) ov5695->pad.flags = MEDIA_PAD_FL_SOURCE; @@ -1328,7 +1397,16 @@ static int ov5695_probe(struct i2c_client *client, goto err_power_off; #endif - ret = v4l2_async_register_subdev(sd); + memset(facing, 0, sizeof(facing)); + if (strcmp(ov5695->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + ov5695->module_index, facing, + OV5695_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor_common(sd); if (ret) { dev_err(dev, "v4l2 async register subdev failed\n"); goto err_clean_entity; @@ -1382,17 +1460,34 @@ static const struct of_device_id ov5695_of_match[] = { MODULE_DEVICE_TABLE(of, ov5695_of_match); #endif +static const struct i2c_device_id ov5695_match_id[] = { + { "ovti,ov5695", 0 }, + { }, +}; + static struct i2c_driver ov5695_i2c_driver = { .driver = { - .name = "ov5695", + .name = OV5695_NAME, .pm = &ov5695_pm_ops, .of_match_table = of_match_ptr(ov5695_of_match), }, .probe = &ov5695_probe, .remove = &ov5695_remove, + .id_table = ov5695_match_id, }; -module_i2c_driver(ov5695_i2c_driver); +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&ov5695_i2c_driver); +} + +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&ov5695_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); MODULE_DESCRIPTION("OmniVision ov5695 sensor driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ov7251.c b/drivers/media/i2c/ov7251.c index d3ebb7529fca..f1470953d104 100644 --- a/drivers/media/i2c/ov7251.c +++ b/drivers/media/i2c/ov7251.c @@ -1,1469 +1,1107 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Driver for the OV7251 camera sensor. + * ov7251 driver * - * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. - * Copyright (c) 2017-2018, Linaro Ltd. + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd. + * + * V0.0X01.0X01 add poweron function. + * V0.0X01.0X02 fix mclk issue when probe multiple camera. */ -#include #include -#include #include +#include #include #include -#include #include +#include #include +#include #include -#include +#include +#include +#include +#include #include -#include #include -#define OV7251_SC_MODE_SELECT 0x0100 -#define OV7251_SC_MODE_SELECT_SW_STANDBY 0x0 -#define OV7251_SC_MODE_SELECT_STREAMING 0x1 +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x02) -#define OV7251_CHIP_ID_HIGH 0x300a -#define OV7251_CHIP_ID_HIGH_BYTE 0x77 -#define OV7251_CHIP_ID_LOW 0x300b -#define OV7251_CHIP_ID_LOW_BYTE 0x50 -#define OV7251_SC_GP_IO_IN1 0x3029 -#define OV7251_AEC_EXPO_0 0x3500 -#define OV7251_AEC_EXPO_1 0x3501 -#define OV7251_AEC_EXPO_2 0x3502 -#define OV7251_AEC_AGC_ADJ_0 0x350a -#define OV7251_AEC_AGC_ADJ_1 0x350b -#define OV7251_TIMING_FORMAT1 0x3820 -#define OV7251_TIMING_FORMAT1_VFLIP BIT(2) -#define OV7251_TIMING_FORMAT2 0x3821 -#define OV7251_TIMING_FORMAT2_MIRROR BIT(2) -#define OV7251_PRE_ISP_00 0x5e00 -#define OV7251_PRE_ISP_00_TEST_PATTERN BIT(7) +#ifndef V4L2_CID_DIGITAL_GAIN +#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN +#endif -struct reg_value { - u16 reg; +/* 48Mhz */ +#define OV7251_PIXEL_RATE (48 * 1000 * 1000) +#define OV7251_XVCLK_FREQ 24000000 + +#define CHIP_ID 0x007750 +#define OV7251_REG_CHIP_ID 0x300a +#define OV7251_REG_MOD_VENDOR_ID 0x3d10 +#define OV7251_REG_OPT_LOAD_CTRL 0x3d81 + +#define OV7251_REG_CTRL_MODE 0x0100 +#define OV7251_MODE_SW_STANDBY 0x0 +#define OV7251_MODE_STREAMING BIT(0) + +#define OV7251_REG_EXPOSURE 0x3500 +#define OV7251_EXPOSURE_MIN 4 +#define OV7251_EXPOSURE_STEP 0xf +#define OV7251_VTS_MAX 0xffff + +#define OV7251_REG_ANALOG_GAIN 0x350a +#define ANALOG_GAIN_MASK 0x3ff +#define ANALOG_GAIN_MIN 0x10 +#define ANALOG_GAIN_MAX 0x3e0 +#define ANALOG_GAIN_STEP 1 +#define ANALOG_GAIN_DEFAULT 0x20 + +#define OV7251_REG_TEST_PATTERN 0x5e00 +#define OV7251_TEST_PATTERN_ENABLE 0x80 +#define OV7251_TEST_PATTERN_DISABLE 0x0 + +#define OV7251_REG_VTS 0x380e + +#define REG_NULL 0xFFFF + +#define OV7251_REG_VALUE_08BIT 1 +#define OV7251_REG_VALUE_16BIT 2 +#define OV7251_REG_VALUE_24BIT 3 + +#define OV7251_NAME "ov7251" + +static const char * const ov7251_supply_names[] = { + "avdd", /* Analog power */ + "dovdd", /* Digital I/O power */ + "dvdd", /* Digital core power not needed*/ +}; + +#define OV7251_NUM_SUPPLIES ARRAY_SIZE(ov7251_supply_names) + +struct regval { + u16 addr; u8 val; }; -struct ov7251_mode_info { +struct ov7251_mode { u32 width; u32 height; - const struct reg_value *data; - u32 data_size; - u32 pixel_clock; - u32 link_freq; - u16 exposure_max; - u16 exposure_def; - struct v4l2_fract timeperframe; + u32 max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + const struct regval *reg_list; }; struct ov7251 { - struct i2c_client *i2c_client; - struct device *dev; - struct v4l2_subdev sd; - struct media_pad pad; - struct v4l2_fwnode_endpoint ep; - struct v4l2_mbus_framefmt fmt; - struct v4l2_rect crop; - struct clk *xclk; - u32 xclk_freq; + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; + struct regulator_bulk_data supplies[OV7251_NUM_SUPPLIES]; - struct regulator *io_regulator; - struct regulator *core_regulator; - struct regulator *analog_regulator; - - const struct ov7251_mode_info *current_mode; - - struct v4l2_ctrl_handler ctrls; - struct v4l2_ctrl *pixel_clock; - struct v4l2_ctrl *link_freq; - struct v4l2_ctrl *exposure; - struct v4l2_ctrl *gain; - - /* Cached register values */ - u8 aec_pk_manual; - u8 pre_isp_00; - u8 timing_format1; - u8 timing_format2; - - struct mutex lock; /* lock to protect power state, ctrls and mode */ - bool power_on; - - struct gpio_desc *enable_gpio; + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *anal_gain; + struct v4l2_ctrl *digi_gain; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *test_pattern; + struct mutex mutex; + bool streaming; + bool power_on; + const struct ov7251_mode *cur_mode; + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; }; -static inline struct ov7251 *to_ov7251(struct v4l2_subdev *sd) -{ - return container_of(sd, struct ov7251, sd); -} +#define to_ov7251(sd) container_of(sd, struct ov7251, subdev) -static const struct reg_value ov7251_global_init_setting[] = { - { 0x0103, 0x01 }, - { 0x303b, 0x02 }, +/* + * Xclk 24Mhz + * Pclk 48Mhz + * PCLK = HTS * VTS * FPS + * linelength 928(0x3a0) + * framelength 1720(0x6b8) + * grabwindow_width 640 + * grabwindow_height 480 + * max_framerate 30fps + * mipi_datarate per lane 640Mbps + */ +static const struct regval ov7251_640x480_regs[] = { + {0x0100, 0x00}, + {0x0103, 0x01}, + + {0x3001, 0x62}, + {0x3005, 0x00}, + {0x3012, 0xc0}, + {0x3013, 0xd2}, + {0x3014, 0x04}, + {0x3016, 0x10}, + {0x3017, 0x00}, + {0x3018, 0x00}, + {0x301a, 0x00}, + {0x301b, 0x00}, + {0x301c, 0x20}, + {0x3023, 0x05}, + {0x3037, 0xf0}, + {0x3098, 0x04}, + {0x3099, 0x28}, + {0x309a, 0x05}, + {0x309b, 0x04}, + {0x30b0, 0x0a}, + {0x30b1, 0x01}, + {0x30b3, 0x64}, + {0x30b4, 0x03}, + {0x30b5, 0x05}, + {0x3106, 0xda}, + {0x3500, 0x00}, + {0x3501, 0x1f}, + {0x3502, 0x80}, + {0x3503, 0x07}, + {0x3509, 0x10}, + {0x350b, 0x10}, + {0x3600, 0x1c}, + {0x3602, 0x62}, + {0x3620, 0xb7}, + {0x3622, 0x04}, + {0x3626, 0x21}, + {0x3627, 0x30}, + {0x3630, 0x44}, + {0x3631, 0x35}, + {0x3634, 0x60}, + {0x3636, 0x00}, + {0x3662, 0x01}, + {0x3663, 0x70}, + {0x3664, 0xf0}, + {0x3666, 0x0a}, + {0x3669, 0x1a}, + {0x366a, 0x00}, + {0x366b, 0x50}, + {0x3673, 0x01}, + {0x3674, 0xff}, + {0x3675, 0x03}, + {0x3705, 0xc1}, + {0x3709, 0x40}, + {0x373c, 0x08}, + {0x3742, 0x00}, + {0x3757, 0xb3}, + {0x3788, 0x00}, + {0x37a8, 0x01}, + {0x37a9, 0xc0}, + {0x3800, 0x00}, + {0x3801, 0x04}, + {0x3802, 0x00}, + {0x3803, 0x04}, + {0x3804, 0x02}, + {0x3805, 0x8b}, + {0x3806, 0x01}, + {0x3807, 0xeb}, + {0x3808, 0x02}, + {0x3809, 0x80}, + {0x380a, 0x01}, + {0x380b, 0xe0}, + {0x380c, 0x03}, + {0x380d, 0xa0}, + {0x380e, 0x06}, + {0x380f, 0xb8}, + {0x3810, 0x00}, + {0x3811, 0x02}, + {0x3812, 0x00}, + {0x3813, 0x02}, + {0x3814, 0x11}, + {0x3815, 0x11}, + {0x3820, 0x40}, + {0x3821, 0x00}, + {0x382f, 0x0e}, + {0x3832, 0x00}, + {0x3833, 0x05}, + {0x3834, 0x00}, + {0x3835, 0x0c}, + {0x3837, 0x00}, + {0x3b80, 0x00}, + {0x3b81, 0xa5}, + {0x3b82, 0x10}, + {0x3b83, 0x00}, + {0x3b84, 0x08}, + {0x3b85, 0x00}, + {0x3b86, 0x01}, + {0x3b87, 0x00}, + {0x3b88, 0x00}, + {0x3b89, 0x00}, + {0x3b8a, 0x00}, + {0x3b8b, 0x05}, + {0x3b8c, 0x00}, + {0x3b8d, 0x00}, + {0x3b8e, 0x00}, + {0x3b8f, 0x1a}, + {0x3b94, 0x05}, + {0x3b95, 0xf2}, + {0x3b96, 0x40}, + {0x3c00, 0x89}, + {0x3c01, 0x63}, + {0x3c02, 0x01}, + {0x3c03, 0x00}, + {0x3c04, 0x00}, + {0x3c05, 0x03}, + {0x3c06, 0x00}, + {0x3c07, 0x06}, + {0x3c0c, 0x01}, + {0x3c0d, 0xd0}, + {0x3c0e, 0x02}, + {0x3c0f, 0x04}, + {0x4001, 0x42}, + {0x4004, 0x04}, + {0x4005, 0x00}, + {0x404e, 0x01}, + {0x4241, 0x00}, + {0x4242, 0x00}, + {0x4300, 0xff}, + {0x4301, 0x00}, + {0x4501, 0x48}, + {0x4600, 0x00}, + {0x4601, 0x4e}, + {0x4801, 0x0f}, + {0x4806, 0x0f}, + {0x4819, 0xaa}, + {0x4823, 0x3e}, + {0x4837, 0x19}, + {0x4a0d, 0x00}, + {0x4a47, 0x7f}, + {0x4a49, 0xf0}, + {0x4a4b, 0x30}, + {0x5000, 0x85}, + {0x5001, 0x80}, + {REG_NULL, 0x00}, }; -static const struct reg_value ov7251_setting_vga_30fps[] = { - { 0x3005, 0x00 }, - { 0x3012, 0xc0 }, - { 0x3013, 0xd2 }, - { 0x3014, 0x04 }, - { 0x3016, 0xf0 }, - { 0x3017, 0xf0 }, - { 0x3018, 0xf0 }, - { 0x301a, 0xf0 }, - { 0x301b, 0xf0 }, - { 0x301c, 0xf0 }, - { 0x3023, 0x05 }, - { 0x3037, 0xf0 }, - { 0x3098, 0x04 }, /* pll2 pre divider */ - { 0x3099, 0x28 }, /* pll2 multiplier */ - { 0x309a, 0x05 }, /* pll2 sys divider */ - { 0x309b, 0x04 }, /* pll2 adc divider */ - { 0x309d, 0x00 }, /* pll2 divider */ - { 0x30b0, 0x0a }, /* pll1 pix divider */ - { 0x30b1, 0x01 }, /* pll1 divider */ - { 0x30b3, 0x64 }, /* pll1 multiplier */ - { 0x30b4, 0x03 }, /* pll1 pre divider */ - { 0x30b5, 0x05 }, /* pll1 mipi divider */ - { 0x3106, 0xda }, - { 0x3503, 0x07 }, - { 0x3509, 0x10 }, - { 0x3600, 0x1c }, - { 0x3602, 0x62 }, - { 0x3620, 0xb7 }, - { 0x3622, 0x04 }, - { 0x3626, 0x21 }, - { 0x3627, 0x30 }, - { 0x3630, 0x44 }, - { 0x3631, 0x35 }, - { 0x3634, 0x60 }, - { 0x3636, 0x00 }, - { 0x3662, 0x01 }, - { 0x3663, 0x70 }, - { 0x3664, 0x50 }, - { 0x3666, 0x0a }, - { 0x3669, 0x1a }, - { 0x366a, 0x00 }, - { 0x366b, 0x50 }, - { 0x3673, 0x01 }, - { 0x3674, 0xff }, - { 0x3675, 0x03 }, - { 0x3705, 0xc1 }, - { 0x3709, 0x40 }, - { 0x373c, 0x08 }, - { 0x3742, 0x00 }, - { 0x3757, 0xb3 }, - { 0x3788, 0x00 }, - { 0x37a8, 0x01 }, - { 0x37a9, 0xc0 }, - { 0x3800, 0x00 }, - { 0x3801, 0x04 }, - { 0x3802, 0x00 }, - { 0x3803, 0x04 }, - { 0x3804, 0x02 }, - { 0x3805, 0x8b }, - { 0x3806, 0x01 }, - { 0x3807, 0xeb }, - { 0x3808, 0x02 }, /* width high */ - { 0x3809, 0x80 }, /* width low */ - { 0x380a, 0x01 }, /* height high */ - { 0x380b, 0xe0 }, /* height low */ - { 0x380c, 0x03 }, /* total horiz timing high */ - { 0x380d, 0xa0 }, /* total horiz timing low */ - { 0x380e, 0x06 }, /* total vertical timing high */ - { 0x380f, 0xbc }, /* total vertical timing low */ - { 0x3810, 0x00 }, - { 0x3811, 0x04 }, - { 0x3812, 0x00 }, - { 0x3813, 0x05 }, - { 0x3814, 0x11 }, - { 0x3815, 0x11 }, - { 0x3820, 0x40 }, - { 0x3821, 0x00 }, - { 0x382f, 0x0e }, - { 0x3832, 0x00 }, - { 0x3833, 0x05 }, - { 0x3834, 0x00 }, - { 0x3835, 0x0c }, - { 0x3837, 0x00 }, - { 0x3b80, 0x00 }, - { 0x3b81, 0xa5 }, - { 0x3b82, 0x10 }, - { 0x3b83, 0x00 }, - { 0x3b84, 0x08 }, - { 0x3b85, 0x00 }, - { 0x3b86, 0x01 }, - { 0x3b87, 0x00 }, - { 0x3b88, 0x00 }, - { 0x3b89, 0x00 }, - { 0x3b8a, 0x00 }, - { 0x3b8b, 0x05 }, - { 0x3b8c, 0x00 }, - { 0x3b8d, 0x00 }, - { 0x3b8e, 0x00 }, - { 0x3b8f, 0x1a }, - { 0x3b94, 0x05 }, - { 0x3b95, 0xf2 }, - { 0x3b96, 0x40 }, - { 0x3c00, 0x89 }, - { 0x3c01, 0x63 }, - { 0x3c02, 0x01 }, - { 0x3c03, 0x00 }, - { 0x3c04, 0x00 }, - { 0x3c05, 0x03 }, - { 0x3c06, 0x00 }, - { 0x3c07, 0x06 }, - { 0x3c0c, 0x01 }, - { 0x3c0d, 0xd0 }, - { 0x3c0e, 0x02 }, - { 0x3c0f, 0x0a }, - { 0x4001, 0x42 }, - { 0x4004, 0x04 }, - { 0x4005, 0x00 }, - { 0x404e, 0x01 }, - { 0x4300, 0xff }, - { 0x4301, 0x00 }, - { 0x4315, 0x00 }, - { 0x4501, 0x48 }, - { 0x4600, 0x00 }, - { 0x4601, 0x4e }, - { 0x4801, 0x0f }, - { 0x4806, 0x0f }, - { 0x4819, 0xaa }, - { 0x4823, 0x3e }, - { 0x4837, 0x19 }, - { 0x4a0d, 0x00 }, - { 0x4a47, 0x7f }, - { 0x4a49, 0xf0 }, - { 0x4a4b, 0x30 }, - { 0x5000, 0x85 }, - { 0x5001, 0x80 }, -}; - -static const struct reg_value ov7251_setting_vga_60fps[] = { - { 0x3005, 0x00 }, - { 0x3012, 0xc0 }, - { 0x3013, 0xd2 }, - { 0x3014, 0x04 }, - { 0x3016, 0x10 }, - { 0x3017, 0x00 }, - { 0x3018, 0x00 }, - { 0x301a, 0x00 }, - { 0x301b, 0x00 }, - { 0x301c, 0x00 }, - { 0x3023, 0x05 }, - { 0x3037, 0xf0 }, - { 0x3098, 0x04 }, /* pll2 pre divider */ - { 0x3099, 0x28 }, /* pll2 multiplier */ - { 0x309a, 0x05 }, /* pll2 sys divider */ - { 0x309b, 0x04 }, /* pll2 adc divider */ - { 0x309d, 0x00 }, /* pll2 divider */ - { 0x30b0, 0x0a }, /* pll1 pix divider */ - { 0x30b1, 0x01 }, /* pll1 divider */ - { 0x30b3, 0x64 }, /* pll1 multiplier */ - { 0x30b4, 0x03 }, /* pll1 pre divider */ - { 0x30b5, 0x05 }, /* pll1 mipi divider */ - { 0x3106, 0xda }, - { 0x3503, 0x07 }, - { 0x3509, 0x10 }, - { 0x3600, 0x1c }, - { 0x3602, 0x62 }, - { 0x3620, 0xb7 }, - { 0x3622, 0x04 }, - { 0x3626, 0x21 }, - { 0x3627, 0x30 }, - { 0x3630, 0x44 }, - { 0x3631, 0x35 }, - { 0x3634, 0x60 }, - { 0x3636, 0x00 }, - { 0x3662, 0x01 }, - { 0x3663, 0x70 }, - { 0x3664, 0x50 }, - { 0x3666, 0x0a }, - { 0x3669, 0x1a }, - { 0x366a, 0x00 }, - { 0x366b, 0x50 }, - { 0x3673, 0x01 }, - { 0x3674, 0xff }, - { 0x3675, 0x03 }, - { 0x3705, 0xc1 }, - { 0x3709, 0x40 }, - { 0x373c, 0x08 }, - { 0x3742, 0x00 }, - { 0x3757, 0xb3 }, - { 0x3788, 0x00 }, - { 0x37a8, 0x01 }, - { 0x37a9, 0xc0 }, - { 0x3800, 0x00 }, - { 0x3801, 0x04 }, - { 0x3802, 0x00 }, - { 0x3803, 0x04 }, - { 0x3804, 0x02 }, - { 0x3805, 0x8b }, - { 0x3806, 0x01 }, - { 0x3807, 0xeb }, - { 0x3808, 0x02 }, /* width high */ - { 0x3809, 0x80 }, /* width low */ - { 0x380a, 0x01 }, /* height high */ - { 0x380b, 0xe0 }, /* height low */ - { 0x380c, 0x03 }, /* total horiz timing high */ - { 0x380d, 0xa0 }, /* total horiz timing low */ - { 0x380e, 0x03 }, /* total vertical timing high */ - { 0x380f, 0x5c }, /* total vertical timing low */ - { 0x3810, 0x00 }, - { 0x3811, 0x04 }, - { 0x3812, 0x00 }, - { 0x3813, 0x05 }, - { 0x3814, 0x11 }, - { 0x3815, 0x11 }, - { 0x3820, 0x40 }, - { 0x3821, 0x00 }, - { 0x382f, 0x0e }, - { 0x3832, 0x00 }, - { 0x3833, 0x05 }, - { 0x3834, 0x00 }, - { 0x3835, 0x0c }, - { 0x3837, 0x00 }, - { 0x3b80, 0x00 }, - { 0x3b81, 0xa5 }, - { 0x3b82, 0x10 }, - { 0x3b83, 0x00 }, - { 0x3b84, 0x08 }, - { 0x3b85, 0x00 }, - { 0x3b86, 0x01 }, - { 0x3b87, 0x00 }, - { 0x3b88, 0x00 }, - { 0x3b89, 0x00 }, - { 0x3b8a, 0x00 }, - { 0x3b8b, 0x05 }, - { 0x3b8c, 0x00 }, - { 0x3b8d, 0x00 }, - { 0x3b8e, 0x00 }, - { 0x3b8f, 0x1a }, - { 0x3b94, 0x05 }, - { 0x3b95, 0xf2 }, - { 0x3b96, 0x40 }, - { 0x3c00, 0x89 }, - { 0x3c01, 0x63 }, - { 0x3c02, 0x01 }, - { 0x3c03, 0x00 }, - { 0x3c04, 0x00 }, - { 0x3c05, 0x03 }, - { 0x3c06, 0x00 }, - { 0x3c07, 0x06 }, - { 0x3c0c, 0x01 }, - { 0x3c0d, 0xd0 }, - { 0x3c0e, 0x02 }, - { 0x3c0f, 0x0a }, - { 0x4001, 0x42 }, - { 0x4004, 0x04 }, - { 0x4005, 0x00 }, - { 0x404e, 0x01 }, - { 0x4300, 0xff }, - { 0x4301, 0x00 }, - { 0x4315, 0x00 }, - { 0x4501, 0x48 }, - { 0x4600, 0x00 }, - { 0x4601, 0x4e }, - { 0x4801, 0x0f }, - { 0x4806, 0x0f }, - { 0x4819, 0xaa }, - { 0x4823, 0x3e }, - { 0x4837, 0x19 }, - { 0x4a0d, 0x00 }, - { 0x4a47, 0x7f }, - { 0x4a49, 0xf0 }, - { 0x4a4b, 0x30 }, - { 0x5000, 0x85 }, - { 0x5001, 0x80 }, -}; - -static const struct reg_value ov7251_setting_vga_90fps[] = { - { 0x3005, 0x00 }, - { 0x3012, 0xc0 }, - { 0x3013, 0xd2 }, - { 0x3014, 0x04 }, - { 0x3016, 0x10 }, - { 0x3017, 0x00 }, - { 0x3018, 0x00 }, - { 0x301a, 0x00 }, - { 0x301b, 0x00 }, - { 0x301c, 0x00 }, - { 0x3023, 0x05 }, - { 0x3037, 0xf0 }, - { 0x3098, 0x04 }, /* pll2 pre divider */ - { 0x3099, 0x28 }, /* pll2 multiplier */ - { 0x309a, 0x05 }, /* pll2 sys divider */ - { 0x309b, 0x04 }, /* pll2 adc divider */ - { 0x309d, 0x00 }, /* pll2 divider */ - { 0x30b0, 0x0a }, /* pll1 pix divider */ - { 0x30b1, 0x01 }, /* pll1 divider */ - { 0x30b3, 0x64 }, /* pll1 multiplier */ - { 0x30b4, 0x03 }, /* pll1 pre divider */ - { 0x30b5, 0x05 }, /* pll1 mipi divider */ - { 0x3106, 0xda }, - { 0x3503, 0x07 }, - { 0x3509, 0x10 }, - { 0x3600, 0x1c }, - { 0x3602, 0x62 }, - { 0x3620, 0xb7 }, - { 0x3622, 0x04 }, - { 0x3626, 0x21 }, - { 0x3627, 0x30 }, - { 0x3630, 0x44 }, - { 0x3631, 0x35 }, - { 0x3634, 0x60 }, - { 0x3636, 0x00 }, - { 0x3662, 0x01 }, - { 0x3663, 0x70 }, - { 0x3664, 0x50 }, - { 0x3666, 0x0a }, - { 0x3669, 0x1a }, - { 0x366a, 0x00 }, - { 0x366b, 0x50 }, - { 0x3673, 0x01 }, - { 0x3674, 0xff }, - { 0x3675, 0x03 }, - { 0x3705, 0xc1 }, - { 0x3709, 0x40 }, - { 0x373c, 0x08 }, - { 0x3742, 0x00 }, - { 0x3757, 0xb3 }, - { 0x3788, 0x00 }, - { 0x37a8, 0x01 }, - { 0x37a9, 0xc0 }, - { 0x3800, 0x00 }, - { 0x3801, 0x04 }, - { 0x3802, 0x00 }, - { 0x3803, 0x04 }, - { 0x3804, 0x02 }, - { 0x3805, 0x8b }, - { 0x3806, 0x01 }, - { 0x3807, 0xeb }, - { 0x3808, 0x02 }, /* width high */ - { 0x3809, 0x80 }, /* width low */ - { 0x380a, 0x01 }, /* height high */ - { 0x380b, 0xe0 }, /* height low */ - { 0x380c, 0x03 }, /* total horiz timing high */ - { 0x380d, 0xa0 }, /* total horiz timing low */ - { 0x380e, 0x02 }, /* total vertical timing high */ - { 0x380f, 0x3c }, /* total vertical timing low */ - { 0x3810, 0x00 }, - { 0x3811, 0x04 }, - { 0x3812, 0x00 }, - { 0x3813, 0x05 }, - { 0x3814, 0x11 }, - { 0x3815, 0x11 }, - { 0x3820, 0x40 }, - { 0x3821, 0x00 }, - { 0x382f, 0x0e }, - { 0x3832, 0x00 }, - { 0x3833, 0x05 }, - { 0x3834, 0x00 }, - { 0x3835, 0x0c }, - { 0x3837, 0x00 }, - { 0x3b80, 0x00 }, - { 0x3b81, 0xa5 }, - { 0x3b82, 0x10 }, - { 0x3b83, 0x00 }, - { 0x3b84, 0x08 }, - { 0x3b85, 0x00 }, - { 0x3b86, 0x01 }, - { 0x3b87, 0x00 }, - { 0x3b88, 0x00 }, - { 0x3b89, 0x00 }, - { 0x3b8a, 0x00 }, - { 0x3b8b, 0x05 }, - { 0x3b8c, 0x00 }, - { 0x3b8d, 0x00 }, - { 0x3b8e, 0x00 }, - { 0x3b8f, 0x1a }, - { 0x3b94, 0x05 }, - { 0x3b95, 0xf2 }, - { 0x3b96, 0x40 }, - { 0x3c00, 0x89 }, - { 0x3c01, 0x63 }, - { 0x3c02, 0x01 }, - { 0x3c03, 0x00 }, - { 0x3c04, 0x00 }, - { 0x3c05, 0x03 }, - { 0x3c06, 0x00 }, - { 0x3c07, 0x06 }, - { 0x3c0c, 0x01 }, - { 0x3c0d, 0xd0 }, - { 0x3c0e, 0x02 }, - { 0x3c0f, 0x0a }, - { 0x4001, 0x42 }, - { 0x4004, 0x04 }, - { 0x4005, 0x00 }, - { 0x404e, 0x01 }, - { 0x4300, 0xff }, - { 0x4301, 0x00 }, - { 0x4315, 0x00 }, - { 0x4501, 0x48 }, - { 0x4600, 0x00 }, - { 0x4601, 0x4e }, - { 0x4801, 0x0f }, - { 0x4806, 0x0f }, - { 0x4819, 0xaa }, - { 0x4823, 0x3e }, - { 0x4837, 0x19 }, - { 0x4a0d, 0x00 }, - { 0x4a47, 0x7f }, - { 0x4a49, 0xf0 }, - { 0x4a4b, 0x30 }, - { 0x5000, 0x85 }, - { 0x5001, 0x80 }, -}; - -static const s64 link_freq[] = { - 240000000, -}; - -static const struct ov7251_mode_info ov7251_mode_info_data[] = { +static const struct ov7251_mode supported_modes[] = { { .width = 640, .height = 480, - .data = ov7251_setting_vga_30fps, - .data_size = ARRAY_SIZE(ov7251_setting_vga_30fps), - .pixel_clock = 48000000, - .link_freq = 0, /* an index in link_freq[] */ - .exposure_max = 1704, - .exposure_def = 504, - .timeperframe = { - .numerator = 100, - .denominator = 3000 - } - }, - { - .width = 640, - .height = 480, - .data = ov7251_setting_vga_60fps, - .data_size = ARRAY_SIZE(ov7251_setting_vga_60fps), - .pixel_clock = 48000000, - .link_freq = 0, /* an index in link_freq[] */ - .exposure_max = 840, - .exposure_def = 504, - .timeperframe = { - .numerator = 100, - .denominator = 6014 - } - }, - { - .width = 640, - .height = 480, - .data = ov7251_setting_vga_90fps, - .data_size = ARRAY_SIZE(ov7251_setting_vga_90fps), - .pixel_clock = 48000000, - .link_freq = 0, /* an index in link_freq[] */ - .exposure_max = 552, - .exposure_def = 504, - .timeperframe = { - .numerator = 100, - .denominator = 9043 - } + .max_fps = 30, + .exp_def = 0x061c, + .hts_def = 0x03a0, + .vts_def = 0x06b8, + .reg_list = ov7251_640x480_regs, }, }; -static int ov7251_regulators_enable(struct ov7251 *ov7251) -{ - int ret; - - /* OV7251 power up sequence requires core regulator - * to be enabled not earlier than io regulator - */ - - ret = regulator_enable(ov7251->io_regulator); - if (ret < 0) { - dev_err(ov7251->dev, "set io voltage failed\n"); - return ret; - } - - ret = regulator_enable(ov7251->analog_regulator); - if (ret) { - dev_err(ov7251->dev, "set analog voltage failed\n"); - goto err_disable_io; - } - - ret = regulator_enable(ov7251->core_regulator); - if (ret) { - dev_err(ov7251->dev, "set core voltage failed\n"); - goto err_disable_analog; - } - - return 0; - -err_disable_analog: - regulator_disable(ov7251->analog_regulator); - -err_disable_io: - regulator_disable(ov7251->io_regulator); - - return ret; -} - -static void ov7251_regulators_disable(struct ov7251 *ov7251) -{ - int ret; - - ret = regulator_disable(ov7251->core_regulator); - if (ret < 0) - dev_err(ov7251->dev, "core regulator disable failed\n"); - - ret = regulator_disable(ov7251->analog_regulator); - if (ret < 0) - dev_err(ov7251->dev, "analog regulator disable failed\n"); - - ret = regulator_disable(ov7251->io_regulator); - if (ret < 0) - dev_err(ov7251->dev, "io regulator disable failed\n"); -} - -static int ov7251_write_reg(struct ov7251 *ov7251, u16 reg, u8 val) -{ - u8 regbuf[3]; - int ret; - - regbuf[0] = reg >> 8; - regbuf[1] = reg & 0xff; - regbuf[2] = val; - - ret = i2c_master_send(ov7251->i2c_client, regbuf, 3); - if (ret < 0) { - dev_err(ov7251->dev, "%s: write reg error %d: reg=%x, val=%x\n", - __func__, ret, reg, val); - return ret; - } - - return 0; -} - -static int ov7251_write_seq_regs(struct ov7251 *ov7251, u16 reg, u8 *val, - u8 num) -{ - u8 regbuf[5]; - u8 nregbuf = sizeof(reg) + num * sizeof(*val); - int ret = 0; - - if (nregbuf > sizeof(regbuf)) - return -EINVAL; - - regbuf[0] = reg >> 8; - regbuf[1] = reg & 0xff; - - memcpy(regbuf + 2, val, num); - - ret = i2c_master_send(ov7251->i2c_client, regbuf, nregbuf); - if (ret < 0) { - dev_err(ov7251->dev, - "%s: write seq regs error %d: first reg=%x\n", - __func__, ret, reg); - return ret; - } - - return 0; -} - -static int ov7251_read_reg(struct ov7251 *ov7251, u16 reg, u8 *val) -{ - u8 regbuf[2]; - int ret; - - regbuf[0] = reg >> 8; - regbuf[1] = reg & 0xff; - - ret = i2c_master_send(ov7251->i2c_client, regbuf, 2); - if (ret < 0) { - dev_err(ov7251->dev, "%s: write reg error %d: reg=%x\n", - __func__, ret, reg); - return ret; - } - - ret = i2c_master_recv(ov7251->i2c_client, val, 1); - if (ret < 0) { - dev_err(ov7251->dev, "%s: read reg error %d: reg=%x\n", - __func__, ret, reg); - return ret; - } - - return 0; -} - -static int ov7251_set_exposure(struct ov7251 *ov7251, s32 exposure) -{ - u16 reg; - u8 val[3]; - - reg = OV7251_AEC_EXPO_0; - val[0] = (exposure & 0xf000) >> 12; /* goes to OV7251_AEC_EXPO_0 */ - val[1] = (exposure & 0x0ff0) >> 4; /* goes to OV7251_AEC_EXPO_1 */ - val[2] = (exposure & 0x000f) << 4; /* goes to OV7251_AEC_EXPO_2 */ - - return ov7251_write_seq_regs(ov7251, reg, val, 3); -} - -static int ov7251_set_gain(struct ov7251 *ov7251, s32 gain) -{ - u16 reg; - u8 val[2]; - - reg = OV7251_AEC_AGC_ADJ_0; - val[0] = (gain & 0x0300) >> 8; /* goes to OV7251_AEC_AGC_ADJ_0 */ - val[1] = gain & 0xff; /* goes to OV7251_AEC_AGC_ADJ_1 */ - - return ov7251_write_seq_regs(ov7251, reg, val, 2); -} - -static int ov7251_set_register_array(struct ov7251 *ov7251, - const struct reg_value *settings, - unsigned int num_settings) -{ - unsigned int i; - int ret; - - for (i = 0; i < num_settings; ++i, ++settings) { - ret = ov7251_write_reg(ov7251, settings->reg, settings->val); - if (ret < 0) - return ret; - } - - return 0; -} - -static int ov7251_set_power_on(struct ov7251 *ov7251) -{ - int ret; - u32 wait_us; - - ret = ov7251_regulators_enable(ov7251); - if (ret < 0) - return ret; - - ret = clk_prepare_enable(ov7251->xclk); - if (ret < 0) { - dev_err(ov7251->dev, "clk prepare enable failed\n"); - ov7251_regulators_disable(ov7251); - return ret; - } - - gpiod_set_value_cansleep(ov7251->enable_gpio, 1); - - /* wait at least 65536 external clock cycles */ - wait_us = DIV_ROUND_UP(65536 * 1000, - DIV_ROUND_UP(ov7251->xclk_freq, 1000)); - usleep_range(wait_us, wait_us + 1000); - - return 0; -} - -static void ov7251_set_power_off(struct ov7251 *ov7251) -{ - clk_disable_unprepare(ov7251->xclk); - gpiod_set_value_cansleep(ov7251->enable_gpio, 0); - ov7251_regulators_disable(ov7251); -} - -static int ov7251_s_power(struct v4l2_subdev *sd, int on) -{ - struct ov7251 *ov7251 = to_ov7251(sd); - int ret = 0; - - mutex_lock(&ov7251->lock); - - /* If the power state is not modified - no work to do. */ - if (ov7251->power_on == !!on) - goto exit; - - if (on) { - ret = ov7251_set_power_on(ov7251); - if (ret < 0) - goto exit; - - ret = ov7251_set_register_array(ov7251, - ov7251_global_init_setting, - ARRAY_SIZE(ov7251_global_init_setting)); - if (ret < 0) { - dev_err(ov7251->dev, "could not set init registers\n"); - ov7251_set_power_off(ov7251); - goto exit; - } - - ov7251->power_on = true; - } else { - ov7251_set_power_off(ov7251); - ov7251->power_on = false; - } - -exit: - mutex_unlock(&ov7251->lock); - - return ret; -} - -static int ov7251_set_hflip(struct ov7251 *ov7251, s32 value) -{ - u8 val = ov7251->timing_format2; - int ret; - - if (value) - val |= OV7251_TIMING_FORMAT2_MIRROR; - else - val &= ~OV7251_TIMING_FORMAT2_MIRROR; - - ret = ov7251_write_reg(ov7251, OV7251_TIMING_FORMAT2, val); - if (!ret) - ov7251->timing_format2 = val; - - return ret; -} - -static int ov7251_set_vflip(struct ov7251 *ov7251, s32 value) -{ - u8 val = ov7251->timing_format1; - int ret; - - if (value) - val |= OV7251_TIMING_FORMAT1_VFLIP; - else - val &= ~OV7251_TIMING_FORMAT1_VFLIP; - - ret = ov7251_write_reg(ov7251, OV7251_TIMING_FORMAT1, val); - if (!ret) - ov7251->timing_format1 = val; - - return ret; -} - -static int ov7251_set_test_pattern(struct ov7251 *ov7251, s32 value) -{ - u8 val = ov7251->pre_isp_00; - int ret; - - if (value) - val |= OV7251_PRE_ISP_00_TEST_PATTERN; - else - val &= ~OV7251_PRE_ISP_00_TEST_PATTERN; - - ret = ov7251_write_reg(ov7251, OV7251_PRE_ISP_00, val); - if (!ret) - ov7251->pre_isp_00 = val; - - return ret; -} +#define OV7251_LINK_FREQ_320MHZ 320000000 +static const s64 link_freq_menu_items[] = { + OV7251_LINK_FREQ_320MHZ +}; static const char * const ov7251_test_pattern_menu[] = { "Disabled", - "Vertical Pattern Bars", + "Vertical Color Bar", }; -static int ov7251_s_ctrl(struct v4l2_ctrl *ctrl) +/* Write registers up to 4 at a time */ +static int ov7251_write_reg(struct i2c_client *client, u16 reg, + int len, u32 val) { - struct ov7251 *ov7251 = container_of(ctrl->handler, - struct ov7251, ctrls); - int ret; + u32 buf_i, val_i; + u8 buf[6]; + u8 *val_p; + __be32 val_be; - /* v4l2_ctrl_lock() locks our mutex */ + usleep_range(1500, 1600); + if (len > 4) + return -EINVAL; - if (!ov7251->power_on) - return 0; + buf[0] = reg >> 8; + buf[1] = reg & 0xff; - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - ret = ov7251_set_exposure(ov7251, ctrl->val); - break; - case V4L2_CID_GAIN: - ret = ov7251_set_gain(ov7251, ctrl->val); - break; - case V4L2_CID_TEST_PATTERN: - ret = ov7251_set_test_pattern(ov7251, ctrl->val); - break; - case V4L2_CID_HFLIP: - ret = ov7251_set_hflip(ov7251, ctrl->val); - break; - case V4L2_CID_VFLIP: - ret = ov7251_set_vflip(ov7251, ctrl->val); - break; - default: - ret = -EINVAL; - break; - } + val_be = cpu_to_be32(val); + val_p = (u8 *)&val_be; + buf_i = 2; + val_i = 4 - len; + + while (val_i < 4) + buf[buf_i++] = val_p[val_i++]; + + if (i2c_master_send(client, buf, len + 2) != len + 2) + return -EIO; + + return 0; +} + +static int ov7251_write_array(struct i2c_client *client, + const struct regval *regs) +{ + u32 i; + int ret = 0; + + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) + ret = ov7251_write_reg(client, regs[i].addr, + OV7251_REG_VALUE_08BIT, regs[i].val); return ret; } -static const struct v4l2_ctrl_ops ov7251_ctrl_ops = { - .s_ctrl = ov7251_s_ctrl, -}; +/* Read registers up to 4 at a time */ +static int ov7251_read_reg(struct i2c_client *client, u16 reg, unsigned int len, + u32 *val) +{ + struct i2c_msg msgs[2]; + u8 *data_be_p; + __be32 data_be = 0; + __be16 reg_addr_be = cpu_to_be16(reg); + int ret; + + if (len > 4 || !len) + return -EINVAL; + + data_be_p = (u8 *)&data_be; + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 2; + msgs[0].buf = (u8 *)®_addr_be; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_be_p[4 - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = be32_to_cpu(data_be); + + return 0; +} + +static int ov7251_get_reso_dist(const struct ov7251_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); +} + +static const struct ov7251_mode * +ov7251_find_best_fit(struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *framefmt = &fmt->format; + int dist; + int cur_best_fit = 0; + int cur_best_fit_dist = -1; + size_t i; + + for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { + dist = ov7251_get_reso_dist(&supported_modes[i], framefmt); + if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { + cur_best_fit_dist = dist; + cur_best_fit = i; + } + } + + return &supported_modes[cur_best_fit]; +} + +static int ov7251_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ov7251 *ov7251 = to_ov7251(sd); + const struct ov7251_mode *mode; + s64 h_blank, vblank_def; + + mutex_lock(&ov7251->mutex); + + mode = ov7251_find_best_fit(fmt); + fmt->format.code = MEDIA_BUS_FMT_Y10_1X10; + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; +#else + mutex_unlock(&ov7251->mutex); + return -ENOTTY; +#endif + } else { + ov7251->cur_mode = mode; + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(ov7251->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(ov7251->vblank, vblank_def, + OV7251_VTS_MAX - mode->height, + 1, vblank_def); + } + + mutex_unlock(&ov7251->mutex); + + return 0; +} + +static int ov7251_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ov7251 *ov7251 = to_ov7251(sd); + const struct ov7251_mode *mode = ov7251->cur_mode; + + mutex_lock(&ov7251->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); +#else + mutex_unlock(&ov7251->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = MEDIA_BUS_FMT_Y10_1X10; + fmt->format.field = V4L2_FIELD_NONE; + } + mutex_unlock(&ov7251->mutex); + + return 0; +} static int ov7251_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { - if (code->index > 0) + if (code->index != 0) return -EINVAL; - code->code = MEDIA_BUS_FMT_Y10_1X10; return 0; } -static int ov7251_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_frame_size_enum *fse) +static int ov7251_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) { + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + if (fse->code != MEDIA_BUS_FMT_Y10_1X10) return -EINVAL; - if (fse->index >= ARRAY_SIZE(ov7251_mode_info_data)) - return -EINVAL; - - fse->min_width = ov7251_mode_info_data[fse->index].width; - fse->max_width = ov7251_mode_info_data[fse->index].width; - fse->min_height = ov7251_mode_info_data[fse->index].height; - fse->max_height = ov7251_mode_info_data[fse->index].height; + fse->min_width = supported_modes[fse->index].width; + fse->max_width = supported_modes[fse->index].width; + fse->max_height = supported_modes[fse->index].height; + fse->min_height = supported_modes[fse->index].height; return 0; } -static int ov7251_enum_frame_ival(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_frame_interval_enum *fie) +static int ov7251_enable_test_pattern(struct ov7251 *ov7251, u32 pattern) { - unsigned int index = fie->index; - unsigned int i; + u32 val; - for (i = 0; i < ARRAY_SIZE(ov7251_mode_info_data); i++) { - if (fie->width != ov7251_mode_info_data[i].width || - fie->height != ov7251_mode_info_data[i].height) - continue; + if (pattern) + val = (pattern - 1) | OV7251_TEST_PATTERN_ENABLE; + else + val = OV7251_TEST_PATTERN_DISABLE; - if (index-- == 0) { - fie->interval = ov7251_mode_info_data[i].timeperframe; - return 0; - } - } - - return -EINVAL; + return ov7251_write_reg(ov7251->client, OV7251_REG_TEST_PATTERN, + OV7251_REG_VALUE_08BIT, val); } -static struct v4l2_mbus_framefmt * -__ov7251_get_pad_format(struct ov7251 *ov7251, - struct v4l2_subdev_pad_config *cfg, - unsigned int pad, - enum v4l2_subdev_format_whence which) -{ - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&ov7251->sd, cfg, pad); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &ov7251->fmt; - default: - return NULL; - } -} - -static int ov7251_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) +static int ov7251_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) { struct ov7251 *ov7251 = to_ov7251(sd); + const struct ov7251_mode *mode = ov7251->cur_mode; - mutex_lock(&ov7251->lock); - format->format = *__ov7251_get_pad_format(ov7251, cfg, format->pad, - format->which); - mutex_unlock(&ov7251->lock); + mutex_lock(&ov7251->mutex); + fi->interval.numerator = 10000; + fi->interval.denominator = mode->max_fps * 10000; + mutex_unlock(&ov7251->mutex); return 0; } -static struct v4l2_rect * -__ov7251_get_pad_crop(struct ov7251 *ov7251, struct v4l2_subdev_pad_config *cfg, - unsigned int pad, enum v4l2_subdev_format_whence which) +static void ov7251_get_module_inf(struct ov7251 *ov7251, + struct rkmodule_inf *inf) { - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&ov7251->sd, cfg, pad); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &ov7251->crop; - default: - return NULL; - } + memset(inf, 0, sizeof(*inf)); + strlcpy(inf->base.sensor, OV7251_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, ov7251->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, ov7251->len_name, sizeof(inf->base.lens)); } -static inline u32 avg_fps(const struct v4l2_fract *t) -{ - return (t->denominator + (t->numerator >> 1)) / t->numerator; -} - -static const struct ov7251_mode_info * -ov7251_find_mode_by_ival(struct ov7251 *ov7251, struct v4l2_fract *timeperframe) -{ - const struct ov7251_mode_info *mode = ov7251->current_mode; - unsigned int fps_req = avg_fps(timeperframe); - unsigned int max_dist_match = (unsigned int) -1; - unsigned int i, n = 0; - - for (i = 0; i < ARRAY_SIZE(ov7251_mode_info_data); i++) { - unsigned int dist; - unsigned int fps_tmp; - - if (mode->width != ov7251_mode_info_data[i].width || - mode->height != ov7251_mode_info_data[i].height) - continue; - - fps_tmp = avg_fps(&ov7251_mode_info_data[i].timeperframe); - - dist = abs(fps_req - fps_tmp); - - if (dist < max_dist_match) { - n = i; - max_dist_match = dist; - } - } - - return &ov7251_mode_info_data[n]; -} - -static int ov7251_set_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) +static long ov7251_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { struct ov7251 *ov7251 = to_ov7251(sd); - struct v4l2_mbus_framefmt *__format; - struct v4l2_rect *__crop; - const struct ov7251_mode_info *new_mode; - int ret = 0; + long ret = 0; - mutex_lock(&ov7251->lock); - - __crop = __ov7251_get_pad_crop(ov7251, cfg, format->pad, format->which); - - new_mode = v4l2_find_nearest_size(ov7251_mode_info_data, - ARRAY_SIZE(ov7251_mode_info_data), - width, height, - format->format.width, format->format.height); - - __crop->width = new_mode->width; - __crop->height = new_mode->height; - - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - ret = __v4l2_ctrl_s_ctrl_int64(ov7251->pixel_clock, - new_mode->pixel_clock); - if (ret < 0) - goto exit; - - ret = __v4l2_ctrl_s_ctrl(ov7251->link_freq, - new_mode->link_freq); - if (ret < 0) - goto exit; - - ret = __v4l2_ctrl_modify_range(ov7251->exposure, - 1, new_mode->exposure_max, - 1, new_mode->exposure_def); - if (ret < 0) - goto exit; - - ret = __v4l2_ctrl_s_ctrl(ov7251->exposure, - new_mode->exposure_def); - if (ret < 0) - goto exit; - - ret = __v4l2_ctrl_s_ctrl(ov7251->gain, 16); - if (ret < 0) - goto exit; - - ov7251->current_mode = new_mode; + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + ov7251_get_module_inf(ov7251, (struct rkmodule_inf *)arg); + break; + default: + ret = -ENOTTY; + break; } - __format = __ov7251_get_pad_format(ov7251, cfg, format->pad, - format->which); - __format->width = __crop->width; - __format->height = __crop->height; - __format->code = MEDIA_BUS_FMT_Y10_1X10; - __format->field = V4L2_FIELD_NONE; - __format->colorspace = V4L2_COLORSPACE_SRGB; - __format->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(__format->colorspace); - __format->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, - __format->colorspace, __format->ycbcr_enc); - __format->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(__format->colorspace); - - format->format = *__format; - -exit: - mutex_unlock(&ov7251->lock); - return ret; } -static int ov7251_entity_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg) +#ifdef CONFIG_COMPAT +static long ov7251_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) { - struct v4l2_subdev_format fmt = { - .which = cfg ? V4L2_SUBDEV_FORMAT_TRY - : V4L2_SUBDEV_FORMAT_ACTIVE, - .format = { - .width = 640, - .height = 480 + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_awb_cfg *cfg; + long ret; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; } - }; - ov7251_set_format(subdev, cfg, &fmt); + ret = ov7251_ioctl(sd, cmd, inf); + if (!ret) + ret = copy_to_user(up, inf, sizeof(*inf)); + kfree(inf); + break; + case RKMODULE_AWB_CFG: + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) { + ret = -ENOMEM; + return ret; + } - return 0; + ret = copy_from_user(cfg, up, sizeof(*cfg)); + if (!ret) + ret = ov7251_ioctl(sd, cmd, cfg); + kfree(cfg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; } +#endif -static int ov7251_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) +static int __ov7251_start_stream(struct ov7251 *ov7251) { - struct ov7251 *ov7251 = to_ov7251(sd); - - if (sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; - - mutex_lock(&ov7251->lock); - sel->r = *__ov7251_get_pad_crop(ov7251, cfg, sel->pad, - sel->which); - mutex_unlock(&ov7251->lock); - - return 0; -} - -static int ov7251_s_stream(struct v4l2_subdev *subdev, int enable) -{ - struct ov7251 *ov7251 = to_ov7251(subdev); int ret; - mutex_lock(&ov7251->lock); + ret = ov7251_write_array(ov7251->client, ov7251->cur_mode->reg_list); + if (ret) + return ret; - if (enable) { - ret = ov7251_set_register_array(ov7251, - ov7251->current_mode->data, - ov7251->current_mode->data_size); + /* In case these controls are set before streaming */ + mutex_unlock(&ov7251->mutex); + ret = v4l2_ctrl_handler_setup(&ov7251->ctrl_handler); + mutex_lock(&ov7251->mutex); + if (ret) + return ret; + + return ov7251_write_reg(ov7251->client, OV7251_REG_CTRL_MODE, + OV7251_REG_VALUE_08BIT, OV7251_MODE_STREAMING); +} + +static int __ov7251_stop_stream(struct ov7251 *ov7251) +{ + return ov7251_write_reg(ov7251->client, OV7251_REG_CTRL_MODE, + OV7251_REG_VALUE_08BIT, OV7251_MODE_SW_STANDBY); +} + +static int ov7251_s_stream(struct v4l2_subdev *sd, int on) +{ + struct ov7251 *ov7251 = to_ov7251(sd); + struct i2c_client *client = ov7251->client; + int ret = 0; + + mutex_lock(&ov7251->mutex); + on = !!on; + if (on == ov7251->streaming) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); if (ret < 0) { - dev_err(ov7251->dev, "could not set mode %dx%d\n", - ov7251->current_mode->width, - ov7251->current_mode->height); - goto exit; + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; } - ret = __v4l2_ctrl_handler_setup(&ov7251->ctrls); - if (ret < 0) { - dev_err(ov7251->dev, "could not sync v4l2 controls\n"); - goto exit; + ret = __ov7251_start_stream(ov7251); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; } - ret = ov7251_write_reg(ov7251, OV7251_SC_MODE_SELECT, - OV7251_SC_MODE_SELECT_STREAMING); + usleep_range(10 * 1000, 12 * 1000); } else { - ret = ov7251_write_reg(ov7251, OV7251_SC_MODE_SELECT, - OV7251_SC_MODE_SELECT_SW_STANDBY); + __ov7251_stop_stream(ov7251); + pm_runtime_put(&client->dev); } -exit: - mutex_unlock(&ov7251->lock); + ov7251->streaming = on; + +unlock_and_return: + mutex_unlock(&ov7251->mutex); return ret; } -static int ov7251_get_frame_interval(struct v4l2_subdev *subdev, - struct v4l2_subdev_frame_interval *fi) +static int ov7251_s_power(struct v4l2_subdev *sd, int on) { - struct ov7251 *ov7251 = to_ov7251(subdev); + struct ov7251 *ov7251 = to_ov7251(sd); + struct i2c_client *client = ov7251->client; + int ret = 0; - mutex_lock(&ov7251->lock); - fi->interval = ov7251->current_mode->timeperframe; - mutex_unlock(&ov7251->lock); + mutex_lock(&ov7251->mutex); + + /* If the power state is not modified - no work to do. */ + if (ov7251->power_on == !!on) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ov7251->power_on = true; + } else { + pm_runtime_put(&client->dev); + ov7251->power_on = false; + } + +unlock_and_return: + mutex_unlock(&ov7251->mutex); + + return ret; +} + +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 ov7251_cal_delay(u32 cycles) +{ + return DIV_ROUND_UP(cycles, OV7251_XVCLK_FREQ / 1000 / 1000); +} + +static int __ov7251_power_on(struct ov7251 *ov7251) +{ + int ret; + u32 delay_us; + struct device *dev = &ov7251->client->dev; + + ret = clk_set_rate(ov7251->xvclk, OV7251_XVCLK_FREQ); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate (24MHz)\n"); + if (clk_get_rate(ov7251->xvclk) != OV7251_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); + ret = clk_prepare_enable(ov7251->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + + if (!IS_ERR(ov7251->reset_gpio)) + gpiod_set_value_cansleep(ov7251->reset_gpio, 1); + + ret = regulator_bulk_enable(OV7251_NUM_SUPPLIES, ov7251->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + + usleep_range(1000, 1100); + + if (!IS_ERR(ov7251->reset_gpio)) + gpiod_set_value_cansleep(ov7251->reset_gpio, 0); + + if (!IS_ERR(ov7251->pwdn_gpio)) + gpiod_set_value_cansleep(ov7251->pwdn_gpio, 1); + + /* 8192 cycles prior to first SCCB transaction */ + delay_us = ov7251_cal_delay(8192); + usleep_range(delay_us, delay_us * 2); + + return 0; + +disable_clk: + clk_disable_unprepare(ov7251->xvclk); + + return ret; +} + +static void __ov7251_power_off(struct ov7251 *ov7251) +{ + if (!IS_ERR(ov7251->pwdn_gpio)) + gpiod_set_value_cansleep(ov7251->pwdn_gpio, 0); + clk_disable_unprepare(ov7251->xvclk); + if (!IS_ERR(ov7251->reset_gpio)) + gpiod_set_value_cansleep(ov7251->reset_gpio, 1); + regulator_bulk_disable(OV7251_NUM_SUPPLIES, ov7251->supplies); +} + +static int ov7251_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov7251 *ov7251 = to_ov7251(sd); + + return __ov7251_power_on(ov7251); +} + +static int ov7251_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov7251 *ov7251 = to_ov7251(sd); + + __ov7251_power_off(ov7251); return 0; } -static int ov7251_set_frame_interval(struct v4l2_subdev *subdev, - struct v4l2_subdev_frame_interval *fi) +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int ov7251_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { - struct ov7251 *ov7251 = to_ov7251(subdev); - const struct ov7251_mode_info *new_mode; - int ret = 0; + struct ov7251 *ov7251 = to_ov7251(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + const struct ov7251_mode *def_mode = &supported_modes[0]; - mutex_lock(&ov7251->lock); - new_mode = ov7251_find_mode_by_ival(ov7251, &fi->interval); + mutex_lock(&ov7251->mutex); + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = MEDIA_BUS_FMT_Y10_1X10; + try_fmt->field = V4L2_FIELD_NONE; - if (new_mode != ov7251->current_mode) { - ret = __v4l2_ctrl_s_ctrl_int64(ov7251->pixel_clock, - new_mode->pixel_clock); - if (ret < 0) - goto exit; + mutex_unlock(&ov7251->mutex); + /* No crop or compose */ - ret = __v4l2_ctrl_s_ctrl(ov7251->link_freq, - new_mode->link_freq); - if (ret < 0) - goto exit; - - ret = __v4l2_ctrl_modify_range(ov7251->exposure, - 1, new_mode->exposure_max, - 1, new_mode->exposure_def); - if (ret < 0) - goto exit; - - ret = __v4l2_ctrl_s_ctrl(ov7251->exposure, - new_mode->exposure_def); - if (ret < 0) - goto exit; - - ret = __v4l2_ctrl_s_ctrl(ov7251->gain, 16); - if (ret < 0) - goto exit; - - ov7251->current_mode = new_mode; - } - - fi->interval = ov7251->current_mode->timeperframe; - -exit: - mutex_unlock(&ov7251->lock); - - return ret; + return 0; } +#endif + +static const struct dev_pm_ops ov7251_pm_ops = { + SET_RUNTIME_PM_OPS(ov7251_runtime_suspend, + ov7251_runtime_resume, NULL) +}; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops ov7251_internal_ops = { + .open = ov7251_open, +}; +#endif static const struct v4l2_subdev_core_ops ov7251_core_ops = { .s_power = ov7251_s_power, + .ioctl = ov7251_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = ov7251_compat_ioctl32, +#endif }; static const struct v4l2_subdev_video_ops ov7251_video_ops = { .s_stream = ov7251_s_stream, - .g_frame_interval = ov7251_get_frame_interval, - .s_frame_interval = ov7251_set_frame_interval, + .g_frame_interval = ov7251_g_frame_interval, }; -static const struct v4l2_subdev_pad_ops ov7251_subdev_pad_ops = { - .init_cfg = ov7251_entity_init_cfg, +static const struct v4l2_subdev_pad_ops ov7251_pad_ops = { .enum_mbus_code = ov7251_enum_mbus_code, - .enum_frame_size = ov7251_enum_frame_size, - .enum_frame_interval = ov7251_enum_frame_ival, - .get_fmt = ov7251_get_format, - .set_fmt = ov7251_set_format, - .get_selection = ov7251_get_selection, + .enum_frame_size = ov7251_enum_frame_sizes, + .get_fmt = ov7251_get_fmt, + .set_fmt = ov7251_set_fmt, }; static const struct v4l2_subdev_ops ov7251_subdev_ops = { - .core = &ov7251_core_ops, - .video = &ov7251_video_ops, - .pad = &ov7251_subdev_pad_ops, + .core = &ov7251_core_ops, + .video = &ov7251_video_ops, + .pad = &ov7251_pad_ops, }; -static int ov7251_probe(struct i2c_client *client) +static int ov7251_set_ctrl(struct v4l2_ctrl *ctrl) { - struct device *dev = &client->dev; - struct fwnode_handle *endpoint; - struct ov7251 *ov7251; - u8 chip_id_high, chip_id_low, chip_rev; + struct ov7251 *ov7251 = container_of(ctrl->handler, + struct ov7251, ctrl_handler); + struct i2c_client *client = ov7251->client; + s64 max; + int ret = 0; + + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ + max = ov7251->cur_mode->height + ctrl->val - 20; + __v4l2_ctrl_modify_range(ov7251->exposure, + ov7251->exposure->minimum, max, + ov7251->exposure->step, + ov7251->exposure->default_value); + break; + } + + if (pm_runtime_get(&client->dev) <= 0) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + /* 4 least significant bits of expsoure are fractional part */ + ret = ov7251_write_reg(ov7251->client, OV7251_REG_EXPOSURE, + OV7251_REG_VALUE_24BIT, ctrl->val << 4); + break; + case V4L2_CID_ANALOGUE_GAIN: + ret = ov7251_write_reg(ov7251->client, OV7251_REG_ANALOG_GAIN, + OV7251_REG_VALUE_16BIT, + ctrl->val & ANALOG_GAIN_MASK); + break; + case V4L2_CID_VBLANK: + ret = ov7251_write_reg(ov7251->client, OV7251_REG_VTS, + OV7251_REG_VALUE_16BIT, + ctrl->val + ov7251->cur_mode->height); + break; + case V4L2_CID_TEST_PATTERN: + ret = ov7251_enable_test_pattern(ov7251, ctrl->val); + break; + default: + dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", + __func__, ctrl->id, ctrl->val); + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops ov7251_ctrl_ops = { + .s_ctrl = ov7251_set_ctrl, +}; + +static int ov7251_initialize_controls(struct ov7251 *ov7251) +{ + const struct ov7251_mode *mode; + struct v4l2_ctrl_handler *handler; + struct v4l2_ctrl *ctrl; + s64 exposure_max, vblank_def; + u32 h_blank; int ret; - ov7251 = devm_kzalloc(dev, sizeof(struct ov7251), GFP_KERNEL); - if (!ov7251) - return -ENOMEM; - - ov7251->i2c_client = client; - ov7251->dev = dev; - - endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); - if (!endpoint) { - dev_err(dev, "endpoint node not found\n"); - return -EINVAL; - } - - ret = v4l2_fwnode_endpoint_parse(endpoint, &ov7251->ep); - fwnode_handle_put(endpoint); - if (ret < 0) { - dev_err(dev, "parsing endpoint node failed\n"); + handler = &ov7251->ctrl_handler; + mode = ov7251->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 8); + if (ret) return ret; + handler->lock = &ov7251->mutex; + + ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, + 0, 0, link_freq_menu_items); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, + 0, OV7251_PIXEL_RATE, 1, OV7251_PIXEL_RATE); + + h_blank = mode->hts_def - mode->width; + ov7251->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (ov7251->hblank) + ov7251->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = mode->vts_def - mode->height; + ov7251->vblank = v4l2_ctrl_new_std(handler, &ov7251_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + OV7251_VTS_MAX - mode->height, + 1, vblank_def); + + exposure_max = mode->vts_def - 20; + ov7251->exposure = v4l2_ctrl_new_std(handler, &ov7251_ctrl_ops, + V4L2_CID_EXPOSURE, OV7251_EXPOSURE_MIN, + exposure_max, OV7251_EXPOSURE_STEP, + mode->exp_def); + + ov7251->anal_gain = v4l2_ctrl_new_std(handler, &ov7251_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, ANALOG_GAIN_MIN, + ANALOG_GAIN_MAX, ANALOG_GAIN_STEP, + ANALOG_GAIN_DEFAULT); + + ov7251->test_pattern = v4l2_ctrl_new_std_menu_items(handler, + &ov7251_ctrl_ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov7251_test_pattern_menu) - 1, + 0, 0, ov7251_test_pattern_menu); + + if (handler->error) { + ret = handler->error; + dev_err(&ov7251->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; } - if (ov7251->ep.bus_type != V4L2_MBUS_CSI2) { - dev_err(dev, "invalid bus type (%u), must be CSI2 (%u)\n", - ov7251->ep.bus_type, V4L2_MBUS_CSI2); - return -EINVAL; - } - - /* get system clock (xclk) */ - ov7251->xclk = devm_clk_get(dev, "xclk"); - if (IS_ERR(ov7251->xclk)) { - dev_err(dev, "could not get xclk"); - return PTR_ERR(ov7251->xclk); - } - - ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", - &ov7251->xclk_freq); - if (ret) { - dev_err(dev, "could not get xclk frequency\n"); - return ret; - } - - /* external clock must be 24MHz, allow 1% tolerance */ - if (ov7251->xclk_freq < 23760000 || ov7251->xclk_freq > 24240000) { - dev_err(dev, "external clock frequency %u is not supported\n", - ov7251->xclk_freq); - return -EINVAL; - } - - ret = clk_set_rate(ov7251->xclk, ov7251->xclk_freq); - if (ret) { - dev_err(dev, "could not set xclk frequency\n"); - return ret; - } - - ov7251->io_regulator = devm_regulator_get(dev, "vdddo"); - if (IS_ERR(ov7251->io_regulator)) { - dev_err(dev, "cannot get io regulator\n"); - return PTR_ERR(ov7251->io_regulator); - } - - ov7251->core_regulator = devm_regulator_get(dev, "vddd"); - if (IS_ERR(ov7251->core_regulator)) { - dev_err(dev, "cannot get core regulator\n"); - return PTR_ERR(ov7251->core_regulator); - } - - ov7251->analog_regulator = devm_regulator_get(dev, "vdda"); - if (IS_ERR(ov7251->analog_regulator)) { - dev_err(dev, "cannot get analog regulator\n"); - return PTR_ERR(ov7251->analog_regulator); - } - - ov7251->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_HIGH); - if (IS_ERR(ov7251->enable_gpio)) { - dev_err(dev, "cannot get enable gpio\n"); - return PTR_ERR(ov7251->enable_gpio); - } - - mutex_init(&ov7251->lock); - - v4l2_ctrl_handler_init(&ov7251->ctrls, 7); - ov7251->ctrls.lock = &ov7251->lock; - - v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - ov7251->exposure = v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops, - V4L2_CID_EXPOSURE, 1, 32, 1, 32); - ov7251->gain = v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops, - V4L2_CID_GAIN, 16, 1023, 1, 16); - v4l2_ctrl_new_std_menu_items(&ov7251->ctrls, &ov7251_ctrl_ops, - V4L2_CID_TEST_PATTERN, - ARRAY_SIZE(ov7251_test_pattern_menu) - 1, - 0, 0, ov7251_test_pattern_menu); - ov7251->pixel_clock = v4l2_ctrl_new_std(&ov7251->ctrls, - &ov7251_ctrl_ops, - V4L2_CID_PIXEL_RATE, - 1, INT_MAX, 1, 1); - ov7251->link_freq = v4l2_ctrl_new_int_menu(&ov7251->ctrls, - &ov7251_ctrl_ops, - V4L2_CID_LINK_FREQ, - ARRAY_SIZE(link_freq) - 1, - 0, link_freq); - if (ov7251->link_freq) - ov7251->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; - - ov7251->sd.ctrl_handler = &ov7251->ctrls; - - if (ov7251->ctrls.error) { - dev_err(dev, "%s: control initialization error %d\n", - __func__, ov7251->ctrls.error); - ret = ov7251->ctrls.error; - goto free_ctrl; - } - - v4l2_i2c_subdev_init(&ov7251->sd, client, &ov7251_subdev_ops); - ov7251->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - ov7251->pad.flags = MEDIA_PAD_FL_SOURCE; - ov7251->sd.dev = &client->dev; - ov7251->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; - - ret = media_entity_pads_init(&ov7251->sd.entity, 1, &ov7251->pad); - if (ret < 0) { - dev_err(dev, "could not register media entity\n"); - goto free_ctrl; - } - - ret = ov7251_s_power(&ov7251->sd, true); - if (ret < 0) { - dev_err(dev, "could not power up OV7251\n"); - goto free_entity; - } - - ret = ov7251_read_reg(ov7251, OV7251_CHIP_ID_HIGH, &chip_id_high); - if (ret < 0 || chip_id_high != OV7251_CHIP_ID_HIGH_BYTE) { - dev_err(dev, "could not read ID high\n"); - ret = -ENODEV; - goto power_down; - } - ret = ov7251_read_reg(ov7251, OV7251_CHIP_ID_LOW, &chip_id_low); - if (ret < 0 || chip_id_low != OV7251_CHIP_ID_LOW_BYTE) { - dev_err(dev, "could not read ID low\n"); - ret = -ENODEV; - goto power_down; - } - - ret = ov7251_read_reg(ov7251, OV7251_SC_GP_IO_IN1, &chip_rev); - if (ret < 0) { - dev_err(dev, "could not read revision\n"); - ret = -ENODEV; - goto power_down; - } - chip_rev >>= 4; - - dev_info(dev, "OV7251 revision %x (%s) detected at address 0x%02x\n", - chip_rev, - chip_rev == 0x4 ? "1A / 1B" : - chip_rev == 0x5 ? "1C / 1D" : - chip_rev == 0x6 ? "1E" : - chip_rev == 0x7 ? "1F" : "unknown", - client->addr); - - ret = ov7251_read_reg(ov7251, OV7251_PRE_ISP_00, - &ov7251->pre_isp_00); - if (ret < 0) { - dev_err(dev, "could not read test pattern value\n"); - ret = -ENODEV; - goto power_down; - } - - ret = ov7251_read_reg(ov7251, OV7251_TIMING_FORMAT1, - &ov7251->timing_format1); - if (ret < 0) { - dev_err(dev, "could not read vflip value\n"); - ret = -ENODEV; - goto power_down; - } - - ret = ov7251_read_reg(ov7251, OV7251_TIMING_FORMAT2, - &ov7251->timing_format2); - if (ret < 0) { - dev_err(dev, "could not read hflip value\n"); - ret = -ENODEV; - goto power_down; - } - - ov7251_s_power(&ov7251->sd, false); - - ret = v4l2_async_register_subdev(&ov7251->sd); - if (ret < 0) { - dev_err(dev, "could not register v4l2 device\n"); - goto free_entity; - } - - ov7251_entity_init_cfg(&ov7251->sd, NULL); + ov7251->subdev.ctrl_handler = handler; return 0; -power_down: - ov7251_s_power(&ov7251->sd, false); -free_entity: - media_entity_cleanup(&ov7251->sd.entity); -free_ctrl: - v4l2_ctrl_handler_free(&ov7251->ctrls); - mutex_destroy(&ov7251->lock); +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +static int ov7251_check_sensor_id(struct ov7251 *ov7251, + struct i2c_client *client) +{ + struct device *dev = &ov7251->client->dev; + u32 id = 0; + int ret; + + ret = ov7251_read_reg(client, OV7251_REG_CHIP_ID, + OV7251_REG_VALUE_16BIT, &id); + if (id != CHIP_ID) { + dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret); + return -ENODEV; + } + + dev_info(dev, "Detected OV%06x sensor\n", CHIP_ID); + + return 0; +} + +static int ov7251_configure_regulators(struct ov7251 *ov7251) +{ + size_t i; + + for (i = 0; i < OV7251_NUM_SUPPLIES; i++) + ov7251->supplies[i].supply = ov7251_supply_names[i]; + + return devm_regulator_bulk_get(&ov7251->client->dev, + OV7251_NUM_SUPPLIES, + ov7251->supplies); +} + +static int ov7251_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct ov7251 *ov7251; + struct v4l2_subdev *sd; + char facing[2]; + int ret; + + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + ov7251 = devm_kzalloc(dev, sizeof(*ov7251), GFP_KERNEL); + if (!ov7251) + return -ENOMEM; + + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &ov7251->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &ov7251->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &ov7251->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &ov7251->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + + ov7251->client = client; + ov7251->cur_mode = &supported_modes[0]; + + ov7251->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(ov7251->xvclk)) { + dev_err(dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + ov7251->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ov7251->reset_gpio)) + dev_warn(dev, "Failed to get reset-gpios\n"); + + ov7251->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(ov7251->pwdn_gpio)) + dev_warn(dev, "Failed to get pwdn-gpios\n"); + + ret = ov7251_configure_regulators(ov7251); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + + mutex_init(&ov7251->mutex); + + sd = &ov7251->subdev; + v4l2_i2c_subdev_init(sd, client, &ov7251_subdev_ops); + ret = ov7251_initialize_controls(ov7251); + if (ret) + goto err_destroy_mutex; + + ret = __ov7251_power_on(ov7251); + if (ret) + goto err_free_handler; + + ret = ov7251_check_sensor_id(ov7251, client); + if (ret) + goto err_power_off; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &ov7251_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + ov7251->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &ov7251->pad); + if (ret < 0) + goto err_power_off; +#endif + + memset(facing, 0, sizeof(facing)); + if (strcmp(ov7251->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + ov7251->module_index, facing, + OV7251_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor_common(sd); + if (ret) { + dev_err(dev, "v4l2 async register subdev failed\n"); + goto err_clean_entity; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; + +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif +err_power_off: + __ov7251_power_off(ov7251); +err_free_handler: + v4l2_ctrl_handler_free(&ov7251->ctrl_handler); +err_destroy_mutex: + mutex_destroy(&ov7251->mutex); return ret; } @@ -1473,31 +1111,57 @@ static int ov7251_remove(struct i2c_client *client) struct v4l2_subdev *sd = i2c_get_clientdata(client); struct ov7251 *ov7251 = to_ov7251(sd); - v4l2_async_unregister_subdev(&ov7251->sd); - media_entity_cleanup(&ov7251->sd.entity); - v4l2_ctrl_handler_free(&ov7251->ctrls); - mutex_destroy(&ov7251->lock); + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + v4l2_ctrl_handler_free(&ov7251->ctrl_handler); + mutex_destroy(&ov7251->mutex); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __ov7251_power_off(ov7251); + pm_runtime_set_suspended(&client->dev); return 0; } +#if IS_ENABLED(CONFIG_OF) static const struct of_device_id ov7251_of_match[] = { { .compatible = "ovti,ov7251" }, - { /* sentinel */ } + {}, }; MODULE_DEVICE_TABLE(of, ov7251_of_match); +#endif + +static const struct i2c_device_id ov7251_match_id[] = { + { "ovti,ov7251", 0 }, + { }, +}; static struct i2c_driver ov7251_i2c_driver = { .driver = { - .of_match_table = ov7251_of_match, - .name = "ov7251", + .name = OV7251_NAME, + .pm = &ov7251_pm_ops, + .of_match_table = of_match_ptr(ov7251_of_match), }, - .probe_new = ov7251_probe, - .remove = ov7251_remove, + .probe = &ov7251_probe, + .remove = &ov7251_remove, + .id_table = ov7251_match_id, }; -module_i2c_driver(ov7251_i2c_driver); +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&ov7251_i2c_driver); +} -MODULE_DESCRIPTION("Omnivision OV7251 Camera Driver"); -MODULE_AUTHOR("Todor Tomov "); +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&ov7251_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_DESCRIPTION("OmniVision ov7251 sensor driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ov7750.c b/drivers/media/i2c/ov7750.c index f4dbd3175ed0..31c3b6e4ba2c 100644 --- a/drivers/media/i2c/ov7750.c +++ b/drivers/media/i2c/ov7750.c @@ -3,6 +3,9 @@ * ov7750 driver * * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd. + * + * V0.0X01.0X01 add poweron function. + * V0.0X01.0X02 fix mclk issue when probe multiple camera. */ #include @@ -14,12 +17,17 @@ #include #include #include +#include +#include +#include #include #include #include #include #include +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x02) + #ifndef V4L2_CID_DIGITAL_GAIN #define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN #endif @@ -72,6 +80,8 @@ #define OF_CAMERA_PINCTRL_DEFAULT "rockchip,camera_default" #define OF_CAMERA_PINCTRL_SLEEP "rockchip,camera_sleep" +#define OV7750_NAME "ov7750" + static const struct regval *ov7750_global_regs; static const char * const ov7750_supply_names[] = { @@ -119,7 +129,12 @@ struct ov7750 { struct v4l2_ctrl *test_pattern; struct mutex mutex; bool streaming; + bool power_on; const struct ov7750_mode *cur_mode; + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; }; #define to_ov7750(sd) container_of(sd, struct ov7750, subdev) @@ -597,14 +612,80 @@ static int ov7750_enable_test_pattern(struct ov7750 *ov7750, u32 pattern) val); } +static void ov7750_get_module_inf(struct ov7750 *ov7750, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strlcpy(inf->base.sensor, OV7750_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, ov7750->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, ov7750->len_name, sizeof(inf->base.lens)); +} + +static long ov7750_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct ov7750 *ov7750 = to_ov7750(sd); + long ret = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + ov7750_get_module_inf(ov7750, (struct rkmodule_inf *)arg); + break; + default: + ret = -ENOTTY; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long ov7750_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_awb_cfg *cfg; + long ret; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = ov7750_ioctl(sd, cmd, inf); + if (!ret) + ret = copy_to_user(up, inf, sizeof(*inf)); + kfree(inf); + break; + case RKMODULE_AWB_CFG: + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(cfg, up, sizeof(*cfg)); + if (!ret) + ret = ov7750_ioctl(sd, cmd, cfg); + kfree(cfg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + static int __ov7750_start_stream(struct ov7750 *ov7750) { int ret; - ret = ov7750_write_array(ov7750->client, ov7750_global_regs); - if (ret) - return ret; - ret = ov7750_write_array(ov7750->client, ov7750->cur_mode->reg_list); if (ret) return ret; @@ -667,6 +748,44 @@ unlock_and_return: return ret; } +static int ov7750_s_power(struct v4l2_subdev *sd, int on) +{ + struct ov7750 *ov7750 = to_ov7750(sd); + struct i2c_client *client = ov7750->client; + int ret = 0; + + mutex_lock(&ov7750->mutex); + + /* If the power state is not modified - no work to do. */ + if (ov7750->power_on == !!on) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = ov7750_write_array(ov7750->client, ov7750_global_regs); + if (ret) { + v4l2_err(sd, "could not set init registers\n"); + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ov7750->power_on = true; + } else { + pm_runtime_put(&client->dev); + ov7750->power_on = false; + } + +unlock_and_return: + mutex_unlock(&ov7750->mutex); + + return ret; +} + /* Calculate the delay in us by clock rate and clock cycles */ static inline u32 ov7750_cal_delay(u32 cycles) { @@ -686,6 +805,11 @@ static int __ov7750_power_on(struct ov7750 *ov7750) dev_err(dev, "could not set pins\n"); } + ret = clk_set_rate(ov7750->xvclk, OV7750_XVCLK_FREQ); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate (24MHz)\n"); + if (clk_get_rate(ov7750->xvclk) != OV7750_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); ret = clk_prepare_enable(ov7750->xvclk); if (ret < 0) { dev_err(dev, "Failed to enable xvclk\n"); @@ -792,6 +916,14 @@ static const struct v4l2_subdev_internal_ops ov7750_internal_ops = { }; #endif +static const struct v4l2_subdev_core_ops ov7750_core_ops = { + .s_power = ov7750_s_power, + .ioctl = ov7750_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = ov7750_compat_ioctl32, +#endif +}; + static const struct v4l2_subdev_video_ops ov7750_video_ops = { .s_stream = ov7750_s_stream, }; @@ -804,6 +936,7 @@ static const struct v4l2_subdev_pad_ops ov7750_pad_ops = { }; static const struct v4l2_subdev_ops ov7750_subdev_ops = { + .core = &ov7750_core_ops, .video = &ov7750_video_ops, .pad = &ov7750_pad_ops, }; @@ -955,7 +1088,7 @@ static int ov7750_check_sensor_id(struct ov7750 *ov7750, OV7750_REG_VALUE_16BIT, &id); if (id != CHIP_ID) { dev_err(dev, "Unexpected sensor id(%04x), ret(%d)\n", id, ret); - return ret; + return -ENODEV; } ret = ov7750_read_reg(client, OV7750_CHIP_REVISION_REG, @@ -990,14 +1123,34 @@ static int ov7750_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; + struct device_node *node = dev->of_node; struct ov7750 *ov7750; struct v4l2_subdev *sd; + char facing[2]; int ret; + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + ov7750 = devm_kzalloc(dev, sizeof(*ov7750), GFP_KERNEL); if (!ov7750) return -ENOMEM; + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &ov7750->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &ov7750->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &ov7750->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &ov7750->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + ov7750->client = client; ov7750->cur_mode = &supported_modes[0]; @@ -1006,13 +1159,6 @@ static int ov7750_probe(struct i2c_client *client, dev_err(dev, "Failed to get xvclk\n"); return -EINVAL; } - ret = clk_set_rate(ov7750->xvclk, OV7750_XVCLK_FREQ); - if (ret < 0) { - dev_err(dev, "Failed to set xvclk rate (24MHz)\n"); - return ret; - } - if (clk_get_rate(ov7750->xvclk) != OV7750_XVCLK_FREQ) - dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); ov7750->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(ov7750->reset_gpio)) @@ -1061,17 +1207,27 @@ static int ov7750_probe(struct i2c_client *client, #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API sd->internal_ops = &ov7750_internal_ops; - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; #endif #if defined(CONFIG_MEDIA_CONTROLLER) ov7750->pad.flags = MEDIA_PAD_FL_SOURCE; - sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; - ret = media_entity_init(&sd->entity, 1, &ov7750->pad, 0); + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &ov7750->pad); if (ret < 0) goto err_power_off; #endif - ret = v4l2_async_register_subdev(sd); + memset(facing, 0, sizeof(facing)); + if (strcmp(ov7750->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + ov7750->module_index, facing, + OV7750_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor_common(sd); if (ret) { dev_err(dev, "v4l2 async register subdev failed\n"); goto err_clean_entity; @@ -1132,7 +1288,7 @@ static const struct i2c_device_id ov7750_match_id[] = { static struct i2c_driver ov7750_i2c_driver = { .driver = { - .name = "ov7750", + .name = OV7750_NAME, .pm = &ov7750_pm_ops, .of_match_table = of_match_ptr(ov7750_of_match), }, @@ -1155,4 +1311,5 @@ device_initcall_sync(sensor_mod_init); module_exit(sensor_mod_exit); MODULE_DESCRIPTION("OmniVision ov7750 sensor driver"); -MODULE_LICENSE("GPL v2"); \ No newline at end of file +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/media/i2c/ov8858.c b/drivers/media/i2c/ov8858.c new file mode 100644 index 000000000000..91d599dc54e7 --- /dev/null +++ b/drivers/media/i2c/ov8858.c @@ -0,0 +1,2999 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ov8858 driver + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd. + * v0.1.0x00 : 1. create file. + * V0.0X01.0X02 fix mclk issue when probe multiple camera. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x02) + +#ifndef V4L2_CID_DIGITAL_GAIN +#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN +#endif +#define OV8858_PIXEL_RATE (360000000LL * 2LL * 2LL / 10LL) + +#define MIPI_FREQ 360000000U +#define OV8858_XVCLK_FREQ 24000000 + +#define CHIP_ID 0x008858 +#define OV8858_REG_CHIP_ID 0x300a + +#define OV8858_REG_CTRL_MODE 0x0100 +#define OV8858_MODE_SW_STANDBY 0x0 +#define OV8858_MODE_STREAMING 0x1 + +#define OV8858_REG_EXPOSURE 0x3500 +#define OV8858_EXPOSURE_MIN 4 +#define OV8858_EXPOSURE_STEP 1 +#define OV8858_VTS_MAX 0x7fff + +#define OV8858_REG_GAIN_H 0x3508 +#define OV8858_REG_GAIN_L 0x3509 +#define OV8858_GAIN_H_MASK 0x07 +#define OV8858_GAIN_H_SHIFT 8 +#define OV8858_GAIN_L_MASK 0xff +#define OV8858_GAIN_MIN 0x80 +#define OV8858_GAIN_MAX 0x400 +#define OV8858_GAIN_STEP 1 +#define OV8858_GAIN_DEFAULT 0x80 + +#define OV8858_REG_TEST_PATTERN 0x5e00 +#define OV8858_TEST_PATTERN_ENABLE 0x80 +#define OV8858_TEST_PATTERN_DISABLE 0x0 + +#define OV8858_REG_VTS 0x380e + +#define REG_NULL 0xFFFF + +#define OV8858_REG_VALUE_08BIT 1 +#define OV8858_REG_VALUE_16BIT 2 +#define OV8858_REG_VALUE_24BIT 3 + +#define OV8858_LANES 2 +#define OV8858_BITS_PER_SAMPLE 10 + +#define OV8858_CHIP_REVISION_REG 0x302A +#define OV8858_R1A 0xb0 +#define OV8858_R2A 0xb2 + +#define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default" +#define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep" + +#define OV8858_NAME "ov8858" +#define OV8858_MEDIA_BUS_FMT MEDIA_BUS_FMT_SBGGR10_1X10 + +#define ov8858_write_1byte(client, reg, val) \ + ov8858_write_reg((client), (reg), OV8858_REG_VALUE_08BIT, (val)) + +#define ov8858_read_1byte(client, reg, val) \ + ov8858_read_reg((client), (reg), OV8858_REG_VALUE_08BIT, (val)) + +static const struct regval *ov8858_global_regs; + +struct ov8858_otp_info_r1a { + int flag; // bit[7]: info, bit[6]:wb, bit[5]:vcm, bit[4]:lenc + int module_id; + int lens_id; + int year; + int month; + int day; + int rg_ratio; + int bg_ratio; + int light_rg; + int light_bg; + int lenc[110]; + int vcm_start; + int vcm_end; + int vcm_dir; +}; + +struct ov8858_otp_info_r2a { + int flag; // bit[7]: info, bit[6]:wb, bit[5]:vcm, bit[4]:lenc + int module_id; + int lens_id; + int year; + int month; + int day; + int rg_ratio; + int bg_ratio; + int lenc[240]; + int checksum; + int vcm_start; + int vcm_end; + int vcm_dir; +}; + +static const char * const ov8858_supply_names[] = { + "avdd", /* Analog power */ + "dovdd", /* Digital I/O power */ + "dvdd", /* Digital core power */ +}; + +#define OV8858_NUM_SUPPLIES ARRAY_SIZE(ov8858_supply_names) + +struct regval { + u16 addr; + u8 val; +}; + +struct ov8858_mode { + u32 width; + u32 height; + struct v4l2_fract max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + const struct regval *reg_list; +}; + +struct ov8858 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *power_gpio; + struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; + struct regulator_bulk_data supplies[OV8858_NUM_SUPPLIES]; + + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_sleep; + + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *anal_gain; + struct v4l2_ctrl *digi_gain; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *test_pattern; + struct mutex mutex; + bool streaming; + const struct ov8858_mode *cur_mode; + bool is_r2a; + unsigned int lane_num; + unsigned int cfg_num; + unsigned int pixel_rate; + + struct ov8858_otp_info_r1a *otp_r1a; + struct ov8858_otp_info_r2a *otp_r2a; + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; + struct rkmodule_inf module_inf; + struct rkmodule_awb_cfg awb_cfg; + struct rkmodule_lsc_cfg lsc_cfg; +}; + +#define to_ov8858(sd) container_of(sd, struct ov8858, subdev) + +struct ov8858_id_name { + u32 id; + char name[RKMODULE_NAME_LEN]; +}; + +static const struct ov8858_id_name ov8858_module_info[] = { + {0x01, "Sunny"}, + {0x02, "Truly"}, + {0x03, "A-kerr"}, + {0x04, "LiteArray"}, + {0x05, "Darling"}, + {0x06, "Qtech"}, + {0x07, "OFlim"}, + {0x08, "Huaquan/Kingcom"}, + {0x09, "Booyi"}, + {0x0a, "Laimu"}, + {0x0b, "WDSEN"}, + {0x0c, "Sunrise"}, + {0x0d, "CameraKing"}, + {0x0e, "Sunniness/Riyong"}, + {0x0f, "Tongju"}, + {0x10, "Seasons/Sijichun"}, + {0x11, "Foxconn"}, + {0x12, "Importek"}, + {0x13, "Altek"}, + {0x14, "ABICO/Ability"}, + {0x15, "Lite-on"}, + {0x16, "Chicony"}, + {0x17, "Primax"}, + {0x18, "AVC"}, + {0x19, "Suyin"}, + {0x21, "Sharp"}, + {0x31, "MCNEX"}, + {0x32, "SEMCO"}, + {0x33, "Partron"}, + {0x41, "Reach/Zhongliancheng"}, + {0x42, "BYD"}, + {0x43, "OSTEC(AoShunChuang)"}, + {0x44, "Chengli"}, + {0x45, "Jiali"}, + {0x46, "Chippack"}, + {0x47, "RongSheng"}, + {0x48, "ShineTech/ShenTai"}, + {0x49, "Brodsands"}, + {0x50, "Others"}, + {0x51, "Method"}, + {0x52, "Sunwin"}, + {0x53, "LG"}, + {0x54, "Goertek"}, + {0x00, "Unknown"} +}; + +static const struct ov8858_id_name ov8858_lens_info[] = { + {0x10, "Largan 9565A1"}, + {0x11, "Largan 9570A/A1"}, + {0x12, "Largan 9569A2/A3"}, + {0x13, "Largan 40108/A1"}, + {0x14, "Largan 50030A1"}, + {0x15, "Largan 40109A1"}, + {0x16, "Largan 40100/A1"}, + {0x17, "Largan 40112/A1"}, + {0x30, "Sunny 3813A"}, + {0x50, "Kantatsu R5AV08/BV"}, + {0x51, "Kantatsu S5AE08"}, + {0x52, "Kantatsu S5AE08"}, + {0x78, "GSEO 8738"}, + {0x79, "GSEO 8744"}, + {0x7a, "GSEO 8742"}, + {0x80, "Foxconn 8028"}, + {0xd8, "XinXu DS-8335"}, + {0xd9, "XinXu DS-8341"}, + {0x00, "Unknown"} +}; + +/* + * Xclk 24Mhz + */ +static const struct regval ov8858_global_regs_r1a[] = { + //@@5.1.1.1 Initialization (Global Setting) + //; Slave_ID=0x6c; + //{0x0103 ,0x01 }, software reset + {0x0100, 0x00}, + {0x0100, 0x00}, + {0x0100, 0x00}, + {0x0100, 0x00}, + {0x0302, 0x1e}, + {0x0303, 0x00}, + {0x0304, 0x03}, + {0x030e, 0x00}, + {0x030f, 0x09}, + {0x0312, 0x01}, + {0x031e, 0x0c}, + {0x3600, 0x00}, + {0x3601, 0x00}, + {0x3602, 0x00}, + {0x3603, 0x00}, + {0x3604, 0x22}, + {0x3605, 0x30}, + {0x3606, 0x00}, + {0x3607, 0x20}, + {0x3608, 0x11}, + {0x3609, 0x28}, + {0x360a, 0x00}, + {0x360b, 0x06}, + {0x360c, 0xdc}, + {0x360d, 0x40}, + {0x360e, 0x0c}, + {0x360f, 0x20}, + {0x3610, 0x07}, + {0x3611, 0x20}, + {0x3612, 0x88}, + {0x3613, 0x80}, + {0x3614, 0x58}, + {0x3615, 0x00}, + {0x3616, 0x4a}, + {0x3617, 0xb0}, + {0x3618, 0x56}, + {0x3619, 0x70}, + {0x361a, 0x99}, + {0x361b, 0x00}, + {0x361c, 0x07}, + {0x361d, 0x00}, + {0x361e, 0x00}, + {0x361f, 0x00}, + {0x3638, 0xff}, + {0x3633, 0x0c}, + {0x3634, 0x0c}, + {0x3635, 0x0c}, + {0x3636, 0x0c}, + {0x3645, 0x13}, + {0x3646, 0x83}, + {0x364a, 0x07}, + {0x3015, 0x01}, + {0x3018, 0x32}, + {0x3020, 0x93}, + {0x3022, 0x01}, + {0x3031, 0x0a}, + {0x3034, 0x00}, + {0x3106, 0x01}, + {0x3305, 0xf1}, + {0x3308, 0x00}, + {0x3309, 0x28}, + {0x330a, 0x00}, + {0x330b, 0x20}, + {0x330c, 0x00}, + {0x330d, 0x00}, + {0x330e, 0x00}, + {0x330f, 0x40}, + {0x3307, 0x04}, + {0x3500, 0x00}, + {0x3501, 0x4d}, + {0x3502, 0x40}, + {0x3503, 0x00}, + {0x3505, 0x80}, + {0x3508, 0x04}, + {0x3509, 0x00}, + {0x350c, 0x00}, + {0x350d, 0x80}, + {0x3510, 0x00}, + {0x3511, 0x02}, + {0x3512, 0x00}, + {0x3700, 0x18}, + {0x3701, 0x0c}, + {0x3702, 0x28}, + {0x3703, 0x19}, + {0x3704, 0x14}, + {0x3705, 0x00}, + {0x3706, 0x35}, + {0x3707, 0x04}, + {0x3708, 0x24}, + {0x3709, 0x33}, + {0x370a, 0x00}, + {0x370b, 0xb5}, + {0x370c, 0x04}, + {0x3718, 0x12}, + {0x3719, 0x31}, + {0x3712, 0x42}, + {0x3714, 0x24}, + {0x371e, 0x19}, + {0x371f, 0x40}, + {0x3720, 0x05}, + {0x3721, 0x05}, + {0x3724, 0x06}, + {0x3725, 0x01}, + {0x3726, 0x06}, + {0x3728, 0x05}, + {0x3729, 0x02}, + {0x372a, 0x03}, + {0x372b, 0x53}, + {0x372c, 0xa3}, + {0x372d, 0x53}, + {0x372e, 0x06}, + {0x372f, 0x10}, + {0x3730, 0x01}, + {0x3731, 0x06}, + {0x3732, 0x14}, + {0x3733, 0x10}, + {0x3734, 0x40}, + {0x3736, 0x20}, + {0x373a, 0x05}, + {0x373b, 0x06}, + {0x373c, 0x0a}, + {0x373e, 0x03}, + {0x3755, 0x10}, + {0x3758, 0x00}, + {0x3759, 0x4c}, + {0x375a, 0x06}, + {0x375b, 0x13}, + {0x375c, 0x20}, + {0x375d, 0x02}, + {0x375e, 0x00}, + {0x375f, 0x14}, + {0x3768, 0x22}, + {0x3769, 0x44}, + {0x376a, 0x44}, + {0x3761, 0x00}, + {0x3762, 0x00}, + {0x3763, 0x00}, + {0x3766, 0xff}, + {0x376b, 0x00}, + {0x3772, 0x23}, + {0x3773, 0x02}, + {0x3774, 0x16}, + {0x3775, 0x12}, + {0x3776, 0x04}, + {0x3777, 0x00}, + {0x3778, 0x1b}, + {0x37a0, 0x44}, + {0x37a1, 0x3d}, + {0x37a2, 0x3d}, + {0x37a3, 0x00}, + {0x37a4, 0x00}, + {0x37a5, 0x00}, + {0x37a6, 0x00}, + {0x37a7, 0x44}, + {0x37a8, 0x4c}, + {0x37a9, 0x4c}, + {0x3760, 0x00}, + {0x376f, 0x01}, + {0x37aa, 0x44}, + {0x37ab, 0x2e}, + {0x37ac, 0x2e}, + {0x37ad, 0x33}, + {0x37ae, 0x0d}, + {0x37af, 0x0d}, + {0x37b0, 0x00}, + {0x37b1, 0x00}, + {0x37b2, 0x00}, + {0x37b3, 0x42}, + {0x37b4, 0x42}, + {0x37b5, 0x33}, + {0x37b6, 0x00}, + {0x37b7, 0x00}, + {0x37b8, 0x00}, + {0x37b9, 0xff}, + {0x3800, 0x00}, + {0x3801, 0x0c}, + {0x3802, 0x00}, + {0x3803, 0x0c}, + {0x3804, 0x0c}, + {0x3805, 0xd3}, + {0x3806, 0x09}, + {0x3807, 0xa3}, + {0x3808, 0x06}, + {0x3809, 0x60}, + {0x380a, 0x04}, + {0x380b, 0xc8}, + {0x380c, 0x07}, + {0x380d, 0x88}, + {0x380e, 0x04}, + {0x380f, 0xdc}, + {0x3810, 0x00}, + {0x3811, 0x04}, + {0x3813, 0x02}, + {0x3814, 0x03}, + {0x3815, 0x01}, + {0x3820, 0x00}, + {0x3821, 0x67}, + {0x382a, 0x03}, + {0x382b, 0x01}, + {0x3830, 0x08}, + {0x3836, 0x02}, + {0x3837, 0x18}, + {0x3841, 0xff}, + {0x3846, 0x48}, + {0x3d85, 0x14}, + {0x3f08, 0x08}, + {0x3f0a, 0x80}, + {0x4000, 0xf1}, + {0x4001, 0x10}, + {0x4005, 0x10}, + {0x4002, 0x27}, + {0x4009, 0x81}, + {0x400b, 0x0c}, + {0x401b, 0x00}, + {0x401d, 0x00}, + {0x4020, 0x00}, + {0x4021, 0x04}, + {0x4022, 0x04}, + {0x4023, 0xb9}, + {0x4024, 0x05}, + {0x4025, 0x2a}, + {0x4026, 0x05}, + {0x4027, 0x2b}, + {0x4028, 0x00}, + {0x4029, 0x02}, + {0x402a, 0x04}, + {0x402b, 0x04}, + {0x402c, 0x02}, + {0x402d, 0x02}, + {0x402e, 0x08}, + {0x402f, 0x02}, + {0x401f, 0x00}, + {0x4034, 0x3f}, + {0x403d, 0x04}, + {0x4300, 0xff}, + {0x4301, 0x00}, + {0x4302, 0x0f}, + {0x4316, 0x00}, + {0x4500, 0x38}, + {0x4503, 0x18}, + {0x4600, 0x00}, + {0x4601, 0xcb}, + {0x481f, 0x32}, + {0x4837, 0x16}, + {0x4850, 0x10}, + {0x4851, 0x32}, + {0x4b00, 0x2a}, + {0x4b0d, 0x00}, + {0x4d00, 0x04}, + {0x4d01, 0x18}, + {0x4d02, 0xc3}, + {0x4d03, 0xff}, + {0x4d04, 0xff}, + {0x4d05, 0xff}, + {0x5000, 0x7e}, + {0x5001, 0x01}, + {0x5002, 0x08}, + {0x5003, 0x20}, + {0x5046, 0x12}, + {0x5901, 0x00}, + {0x5e00, 0x00}, + {0x5e01, 0x41}, + {0x382d, 0x7f}, + {0x4825, 0x3a}, + {0x4826, 0x40}, + {0x4808, 0x25}, + //{0x0100, 0x01}, + {REG_NULL, 0x00}, +}; + +/* + * Xclk 24Mhz + */ +static const struct regval ov8858_global_regs_r2a_2lane[] = { + // MIPI=720Mbps, SysClk=144Mhz,Dac Clock=360Mhz. + // + // + // v00_01_00 (05/29/2014) : initial setting + // + // AM19 : 3617 <- 0xC0 + // + // AM20 : change FWC_6K_EN to be default 0x3618=0x5a + {0x0103, 0x01},// software reset for OVTATool only + {0x0103, 0x01},// software reset + {0x0100, 0x00},// software standby + {0x0302, 0x1e},// pll1_multi + {0x0303, 0x00},// pll1_divm + {0x0304, 0x03},// pll1_div_mipi + {0x030e, 0x02},// pll2_rdiv + {0x030f, 0x04},// pll2_divsp + {0x0312, 0x03},// pll2_pre_div0, pll2_r_divdac + {0x031e, 0x0c},// pll1_no_lat + {0x3600, 0x00}, + {0x3601, 0x00}, + {0x3602, 0x00}, + {0x3603, 0x00}, + {0x3604, 0x22}, + {0x3605, 0x20}, + {0x3606, 0x00}, + {0x3607, 0x20}, + {0x3608, 0x11}, + {0x3609, 0x28}, + {0x360a, 0x00}, + {0x360b, 0x05}, + {0x360c, 0xd4}, + {0x360d, 0x40}, + {0x360e, 0x0c}, + {0x360f, 0x20}, + {0x3610, 0x07}, + {0x3611, 0x20}, + {0x3612, 0x88}, + {0x3613, 0x80}, + {0x3614, 0x58}, + {0x3615, 0x00}, + {0x3616, 0x4a}, + {0x3617, 0x90}, + {0x3618, 0x5a}, + {0x3619, 0x70}, + {0x361a, 0x99}, + {0x361b, 0x0a}, + {0x361c, 0x07}, + {0x361d, 0x00}, + {0x361e, 0x00}, + {0x361f, 0x00}, + {0x3638, 0xff}, + {0x3633, 0x0f}, + {0x3634, 0x0f}, + {0x3635, 0x0f}, + {0x3636, 0x12}, + {0x3645, 0x13}, + {0x3646, 0x83}, + {0x364a, 0x07}, + {0x3015, 0x00}, + {0x3018, 0x32}, // MIPI 2 lane + {0x3020, 0x93}, // Clock switch output normal, pclk_div =/1 + {0x3022, 0x01}, // pd_mipi enable when rst_sync + {0x3031, 0x0a}, // MIPI 10-bit mode + {0x3034, 0x00}, // + {0x3106, 0x01}, // sclk_div, sclk_pre_div + {0x3305, 0xf1}, + {0x3308, 0x00}, + {0x3309, 0x28}, + {0x330a, 0x00}, + {0x330b, 0x20}, + {0x330c, 0x00}, + {0x330d, 0x00}, + {0x330e, 0x00}, + {0x330f, 0x40}, + {0x3307, 0x04}, + {0x3500, 0x00}, // exposure H + {0x3501, 0x4d}, // exposure M + {0x3502, 0x40}, // exposure L + {0x3503, 0x80}, // gain delay ?, exposure delay 1 frame, real gain + {0x3505, 0x80}, // gain option + {0x3508, 0x02}, // gain H + {0x3509, 0x00}, // gain L + {0x350c, 0x00}, // short gain H + {0x350d, 0x80}, // short gain L + {0x3510, 0x00}, // short exposure H + {0x3511, 0x02}, // short exposure M + {0x3512, 0x00}, // short exposure L + {0x3700, 0x18}, + {0x3701, 0x0c}, + {0x3702, 0x28}, + {0x3703, 0x19}, + {0x3704, 0x14}, + {0x3705, 0x00}, + {0x3706, 0x82}, + {0x3707, 0x04}, + {0x3708, 0x24}, + {0x3709, 0x33}, + {0x370a, 0x01}, + {0x370b, 0x82}, + {0x370c, 0x04}, + {0x3718, 0x12}, + {0x3719, 0x31}, + {0x3712, 0x42}, + {0x3714, 0x24}, + {0x371e, 0x19}, + {0x371f, 0x40}, + {0x3720, 0x05}, + {0x3721, 0x05}, + {0x3724, 0x06}, + {0x3725, 0x01}, + {0x3726, 0x06}, + {0x3728, 0x05}, + {0x3729, 0x02}, + {0x372a, 0x03}, + {0x372b, 0x53}, + {0x372c, 0xa3}, + {0x372d, 0x53}, + {0x372e, 0x06}, + {0x372f, 0x10}, + {0x3730, 0x01}, + {0x3731, 0x06}, + {0x3732, 0x14}, + {0x3733, 0x10}, + {0x3734, 0x40}, + {0x3736, 0x20}, + {0x373a, 0x05}, + {0x373b, 0x06}, + {0x373c, 0x0a}, + {0x373e, 0x03}, + {0x3750, 0x0a}, + {0x3751, 0x0e}, + {0x3755, 0x10}, + {0x3758, 0x00}, + {0x3759, 0x4c}, + {0x375a, 0x06}, + {0x375b, 0x13}, + {0x375c, 0x20}, + {0x375d, 0x02}, + {0x375e, 0x00}, + {0x375f, 0x14}, + {0x3768, 0x22}, + {0x3769, 0x44}, + {0x376a, 0x44}, + {0x3761, 0x00}, + {0x3762, 0x00}, + {0x3763, 0x00}, + {0x3766, 0xff}, + {0x376b, 0x00}, + {0x3772, 0x23}, + {0x3773, 0x02}, + {0x3774, 0x16}, + {0x3775, 0x12}, + {0x3776, 0x04}, + {0x3777, 0x00}, + {0x3778, 0x17}, + {0x37a0, 0x44}, + {0x37a1, 0x3d}, + {0x37a2, 0x3d}, + {0x37a3, 0x00}, + {0x37a4, 0x00}, + {0x37a5, 0x00}, + {0x37a6, 0x00}, + {0x37a7, 0x44}, + {0x37a8, 0x4c}, + {0x37a9, 0x4c}, + {0x3760, 0x00}, + {0x376f, 0x01}, + {0x37aa, 0x44}, + {0x37ab, 0x2e}, + {0x37ac, 0x2e}, + {0x37ad, 0x33}, + {0x37ae, 0x0d}, + {0x37af, 0x0d}, + {0x37b0, 0x00}, + {0x37b1, 0x00}, + {0x37b2, 0x00}, + {0x37b3, 0x42}, + {0x37b4, 0x42}, + {0x37b5, 0x31}, + {0x37b6, 0x00}, + {0x37b7, 0x00}, + {0x37b8, 0x00}, + {0x37b9, 0xff}, + {0x3800, 0x00}, // x start H + {0x3801, 0x0c}, // x start L + {0x3802, 0x00}, // y start H + {0x3803, 0x0c}, // y start L + {0x3804, 0x0c}, // x end H + {0x3805, 0xd3}, // x end L + {0x3806, 0x09}, // y end H + {0x3807, 0xa3}, // y end L + {0x3808, 0x06}, // x output size H + {0x3809, 0x60}, // x output size L + {0x380a, 0x04}, // y output size H + {0x380b, 0xc8}, // y output size L + {0x380c, 0x07}, // HTS H + {0x380d, 0x88}, // HTS L + {0x380e, 0x04}, // VTS H + {0x380f, 0xdc}, // VTS L + {0x3810, 0x00}, // ISP x win H + {0x3811, 0x04}, // ISP x win L + {0x3813, 0x02}, // ISP y win L + {0x3814, 0x03}, // x odd inc + {0x3815, 0x01}, // x even inc + {0x3820, 0x00}, // vflip off + {0x3821, 0x67}, // mirror on, bin on + {0x382a, 0x03}, // y odd inc + {0x382b, 0x01}, // y even inc + {0x3830, 0x08}, // + {0x3836, 0x02}, // + {0x3837, 0x18}, // + {0x3841, 0xff}, // window auto size enable + {0x3846, 0x48}, // + {0x3d85, 0x16}, // OTP power up load data enable + {0x3d8c, 0x73}, // OTP setting start High + {0x3d8d, 0xde}, // OTP setting start Low + {0x3f08, 0x08}, // + {0x3f0a, 0x00}, // + {0x4000, 0xf1}, // out_range_trig, format_chg_trig + {0x4001, 0x10}, // total 128 black column + {0x4005, 0x10}, // BLC target L + {0x4002, 0x27}, // value used to limit BLC offset + {0x4009, 0x81}, // final BLC offset limitation enable + {0x400b, 0x0c}, // DCBLC on, DCBLC manual mode on + {0x401b, 0x00}, // zero line R coefficient + {0x401d, 0x00}, // zoro line T coefficient + {0x4020, 0x00}, // Anchor left start H + {0x4021, 0x04}, // Anchor left start L + {0x4022, 0x06}, // Anchor left end H + {0x4023, 0x00}, // Anchor left end L + {0x4024, 0x0f}, // Anchor right start H + {0x4025, 0x2a}, // Anchor right start L + {0x4026, 0x0f}, // Anchor right end H + {0x4027, 0x2b}, // Anchor right end L + {0x4028, 0x00}, // top zero line start + {0x4029, 0x02}, // top zero line number + {0x402a, 0x04}, // top black line start + {0x402b, 0x04}, // top black line number + {0x402c, 0x00}, // bottom zero line start + {0x402d, 0x02}, // bottom zoro line number + {0x402e, 0x04}, // bottom black line start + {0x402f, 0x04}, // bottom black line number + {0x401f, 0x00}, // interpolation x/y disable, Anchor one disable + {0x4034, 0x3f}, // + {0x403d, 0x04}, // md_precision_en + {0x4300, 0xff}, // clip max H + {0x4301, 0x00}, // clip min H + {0x4302, 0x0f}, // clip min L, clip max L + {0x4316, 0x00}, // + {0x4500, 0x58}, // + {0x4503, 0x18}, // + {0x4600, 0x00}, // + {0x4601, 0xcb}, // + {0x481f, 0x32}, // clk prepare min + {0x4837, 0x16}, // global timing + {0x4850, 0x10}, // lane 1 = 1, lane 0 = 0 + {0x4851, 0x32}, // lane 3 = 3, lane 2 = 2 + {0x4b00, 0x2a}, // + {0x4b0d, 0x00}, // + {0x4d00, 0x04}, // temperature sensor + {0x4d01, 0x18}, // + {0x4d02, 0xc3}, // + {0x4d03, 0xff}, // + {0x4d04, 0xff}, // + {0x4d05, 0xff}, // temperature sensor + {0x5000, 0xfe}, // lenc on, slave/master AWB gain/statistics enable + {0x5001, 0x01}, // BLC on + {0x5002, 0x08}, // H scale off, WBMATCH off, OTP_DPC + {0x5003, 0x20}, // DPC_DBC buffer control enable, WB + {0x5046, 0x12}, // + {0x5780, 0x3e}, // DPC + {0x5781, 0x0f}, // + {0x5782, 0x44}, // + {0x5783, 0x02}, // + {0x5784, 0x01}, // + {0x5785, 0x00}, // + {0x5786, 0x00}, // + {0x5787, 0x04}, // + {0x5788, 0x02}, // + {0x5789, 0x0f}, // + {0x578a, 0xfd}, // + {0x578b, 0xf5}, // + {0x578c, 0xf5}, // + {0x578d, 0x03}, // + {0x578e, 0x08}, // + {0x578f, 0x0c}, // + {0x5790, 0x08}, // + {0x5791, 0x04}, // + {0x5792, 0x00}, // + {0x5793, 0x52}, // + {0x5794, 0xa3}, // DPC + {0x5871, 0x0d}, // Lenc + {0x5870, 0x18}, // + {0x586e, 0x10}, // + {0x586f, 0x08}, // + {0x58f7, 0x01}, // + {0x58f8, 0x3d}, // Lenc + {0x5901, 0x00}, // H skip off, V skip off + {0x5b00, 0x02}, // OTP DPC start address + {0x5b01, 0x10}, // OTP DPC start address + {0x5b02, 0x03}, // OTP DPC end address + {0x5b03, 0xcf}, // OTP DPC end address + {0x5b05, 0x6c}, // recover method = 2b11, + {0x5e00, 0x00}, // use 0x3ff to test pattern off + {0x5e01, 0x41}, // window cut enable + {0x382d, 0x7f}, // + {0x4825, 0x3a}, // lpx_p_min + {0x4826, 0x40}, // hs_prepare_min + {0x4808, 0x25}, // wake up delay in 1/1024 s + {0x3763, 0x18}, // + {0x3768, 0xcc}, // + {0x470b, 0x28}, // + {0x4202, 0x00}, // + {0x400d, 0x10}, // BLC offset trigger L + {0x4040, 0x04}, // BLC gain th2 + {0x403e, 0x04}, // BLC gain th1 + {0x4041, 0xc6}, // BLC + {0x3007, 0x80}, + {0x400a, 0x01}, + {REG_NULL, 0x00}, +}; + +/* + * Xclk 24Mhz + * max_framerate 30fps + * mipi_datarate per lane 720Mbps + */ +static const struct regval ov8858_1632x1224_regs_2lane[] = { + // MIPI=720Mbps, SysClk=144Mhz,Dac Clock=360Mhz. + // + // MIPI=720Mbps, SysClk=144Mhz,Dac Clock=360Mhz. + // + // + // v00_01_00 (05/29/2014) : initial setting + // + // AM19 : 3617 <- 0xC0 + // + // AM20 : change FWC_6K_EN to be default 0x3618=0x5a + {0x0100, 0x00}, + {0x3501, 0x4d}, // exposure M + {0x3502, 0x40}, // exposure L + {0x3778, 0x17}, // + {0x3808, 0x06}, // x output size H + {0x3809, 0x60}, // x output size L + {0x380a, 0x04}, // y output size H + {0x380b, 0xc8}, // y output size L + {0x380c, 0x07}, // HTS H + {0x380d, 0x88}, // HTS L + {0x380e, 0x04}, // VTS H + {0x380f, 0xdc}, // VTS L + {0x3814, 0x03}, // x odd inc + {0x3821, 0x67}, // mirror on, bin on + {0x382a, 0x03}, // y odd inc + {0x3830, 0x08}, + {0x3836, 0x02}, + {0x3f0a, 0x00}, + {0x4001, 0x10}, // total 128 black column + {0x4022, 0x06}, // Anchor left end H + {0x4023, 0x00}, // Anchor left end L + {0x4025, 0x2a}, // Anchor right start L + {0x4027, 0x2b}, // Anchor right end L + {0x402b, 0x04}, // top black line number + {0x402f, 0x04}, // bottom black line number + {0x4500, 0x58}, + {0x4600, 0x00}, + {0x4601, 0xcb}, + {0x382d, 0x7f}, + {0x0100, 0x01}, + {REG_NULL, 0x00}, +}; + +/* + * Xclk 24Mhz + * max_framerate 15fps + * mipi_datarate per lane 720Mbps + */ +static const struct regval ov8858_3264x2448_regs_2lane[] = { + {0x0100, 0x00}, + {0x3501, 0x9a},// exposure M + {0x3502, 0x20},// exposure L + {0x3778, 0x1a},// + {0x3808, 0x0c},// x output size H + {0x3809, 0xc0},// x output size L + {0x380a, 0x09},// y output size H + {0x380b, 0x90},// y output size L + {0x380c, 0x07},// HTS H + {0x380d, 0x94},// HTS L + {0x380e, 0x09},// VTS H + {0x380f, 0xaa},// VTS L + {0x3814, 0x01},// x odd inc + {0x3821, 0x46},// mirror on, bin off + {0x382a, 0x01},// y odd inc + {0x3830, 0x06}, + {0x3836, 0x01}, + {0x3f0a, 0x00}, + {0x4001, 0x00},// total 256 black column + {0x4022, 0x0c},// Anchor left end H + {0x4023, 0x60},// Anchor left end L + {0x4025, 0x36},// Anchor right start L + {0x4027, 0x37},// Anchor right end L + {0x402b, 0x08},// top black line number + {0x402f, 0x08},// bottom black line number + {0x4500, 0x58}, + {0x4600, 0x01}, + {0x4601, 0x97}, + {0x382d, 0xff}, + {REG_NULL, 0x00}, +}; + +/* + * Xclk 24Mhz + */ +static const struct regval ov8858_global_regs_r2a_4lane[] = { + // + // MIPI=720Mbps, SysClk=144Mhz,Dac Clock=360Mhz. + // + // v00_01_00 (05/29/2014) : initial setting + // + // AM19 : 3617 <- 0xC0 + // + // AM20 : change FWC_6K_EN to be default 0x3618=0x5a + {0x0103, 0x01}, // software reset for OVTATool only + {0x0103, 0x01}, // software reset + {0x0100, 0x00}, // software standby + {0x0302, 0x1e}, // pll1_multi + {0x0303, 0x00}, // pll1_divm + {0x0304, 0x03}, // pll1_div_mipi + {0x030e, 0x00}, // pll2_rdiv + {0x030f, 0x04}, // pll2_divsp + {0x0312, 0x01}, // pll2_pre_div0, pll2_r_divdac + {0x031e, 0x0c}, // pll1_no_lat + {0x3600, 0x00}, + {0x3601, 0x00}, + {0x3602, 0x00}, + {0x3603, 0x00}, + {0x3604, 0x22}, + {0x3605, 0x20}, + {0x3606, 0x00}, + {0x3607, 0x20}, + {0x3608, 0x11}, + {0x3609, 0x28}, + {0x360a, 0x00}, + {0x360b, 0x05}, + {0x360c, 0xd4}, + {0x360d, 0x40}, + {0x360e, 0x0c}, + {0x360f, 0x20}, + {0x3610, 0x07}, + {0x3611, 0x20}, + {0x3612, 0x88}, + {0x3613, 0x80}, + {0x3614, 0x58}, + {0x3615, 0x00}, + {0x3616, 0x4a}, + {0x3617, 0x90}, + {0x3618, 0x5a}, + {0x3619, 0x70}, + {0x361a, 0x99}, + {0x361b, 0x0a}, + {0x361c, 0x07}, + {0x361d, 0x00}, + {0x361e, 0x00}, + {0x361f, 0x00}, + {0x3638, 0xff}, + {0x3633, 0x0f}, + {0x3634, 0x0f}, + {0x3635, 0x0f}, + {0x3636, 0x12}, + {0x3645, 0x13}, + {0x3646, 0x83}, + {0x364a, 0x07}, + {0x3015, 0x01}, // + {0x3018, 0x72}, // MIPI 4 lane + {0x3020, 0x93}, // Clock switch output normal, pclk_div =/1 + {0x3022, 0x01}, // pd_mipi enable when rst_sync + {0x3031, 0x0a}, // MIPI 10-bit mode + {0x3034, 0x00}, // + {0x3106, 0x01}, // sclk_div, sclk_pre_div + {0x3305, 0xf1}, + {0x3308, 0x00}, + {0x3309, 0x28}, + {0x330a, 0x00}, + {0x330b, 0x20}, + {0x330c, 0x00}, + {0x330d, 0x00}, + {0x330e, 0x00}, + {0x330f, 0x40}, + {0x3307, 0x04}, + {0x3500, 0x00}, // exposure H + {0x3501, 0x4d}, // exposure M + {0x3502, 0x40}, // exposure L + {0x3503, 0x80}, // gain delay ?, exposure delay 1 frame, real gain + {0x3505, 0x80}, // gain option + {0x3508, 0x04}, // gain H + {0x3509, 0x00}, // gain L + {0x350c, 0x00}, // short gain H + {0x350d, 0x80}, // short gain L + {0x3510, 0x00}, // short exposure H + {0x3511, 0x02}, // short exposure M + {0x3512, 0x00}, // short exposure L + {0x3700, 0x30}, + {0x3701, 0x18}, + {0x3702, 0x50}, + {0x3703, 0x32}, + {0x3704, 0x28}, + {0x3705, 0x00}, + {0x3706, 0x82}, + {0x3707, 0x08}, + {0x3708, 0x48}, + {0x3709, 0x66}, + {0x370a, 0x01}, + {0x370b, 0x82}, + {0x370c, 0x07}, + {0x3718, 0x14}, + {0x3719, 0x31}, + {0x3712, 0x44}, + {0x3714, 0x24}, + {0x371e, 0x31}, + {0x371f, 0x7f}, + {0x3720, 0x0a}, + {0x3721, 0x0a}, + {0x3724, 0x0c}, + {0x3725, 0x02}, + {0x3726, 0x0c}, + {0x3728, 0x0a}, + {0x3729, 0x03}, + {0x372a, 0x06}, + {0x372b, 0xa6}, + {0x372c, 0xa6}, + {0x372d, 0xa6}, + {0x372e, 0x0c}, + {0x372f, 0x20}, + {0x3730, 0x02}, + {0x3731, 0x0c}, + {0x3732, 0x28}, + {0x3733, 0x10}, + {0x3734, 0x40}, + {0x3736, 0x30}, + {0x373a, 0x0a}, + {0x373b, 0x0b}, + {0x373c, 0x14}, + {0x373e, 0x06}, + {0x3750, 0x0a}, + {0x3751, 0x0e}, + {0x3755, 0x10}, + {0x3758, 0x00}, + {0x3759, 0x4c}, + {0x375a, 0x0c}, + {0x375b, 0x26}, + {0x375c, 0x20}, + {0x375d, 0x04}, + {0x375e, 0x00}, + {0x375f, 0x28}, + {0x3768, 0x22}, + {0x3769, 0x44}, + {0x376a, 0x44}, + {0x3761, 0x00}, + {0x3762, 0x00}, + {0x3763, 0x00}, + {0x3766, 0xff}, + {0x376b, 0x00}, + {0x3772, 0x46}, + {0x3773, 0x04}, + {0x3774, 0x2c}, + {0x3775, 0x13}, + {0x3776, 0x08}, + {0x3777, 0x00}, + {0x3778, 0x17}, + {0x37a0, 0x88}, + {0x37a1, 0x7a}, + {0x37a2, 0x7a}, + {0x37a3, 0x00}, + {0x37a4, 0x00}, + {0x37a5, 0x00}, + {0x37a6, 0x00}, + {0x37a7, 0x88}, + {0x37a8, 0x98}, + {0x37a9, 0x98}, + {0x3760, 0x00}, + {0x376f, 0x01}, + {0x37aa, 0x88}, + {0x37ab, 0x5c}, + {0x37ac, 0x5c}, + {0x37ad, 0x55}, + {0x37ae, 0x19}, + {0x37af, 0x19}, + {0x37b0, 0x00}, + {0x37b1, 0x00}, + {0x37b2, 0x00}, + {0x37b3, 0x84}, + {0x37b4, 0x84}, + {0x37b5, 0x60}, + {0x37b6, 0x00}, + {0x37b7, 0x00}, + {0x37b8, 0x00}, + {0x37b9, 0xff}, + {0x3800, 0x00}, // x start H + {0x3801, 0x0c}, // x start L + {0x3802, 0x00}, // y start H + {0x3803, 0x0c}, // y start L + {0x3804, 0x0c}, // x end H + {0x3805, 0xd3}, // x end L + {0x3806, 0x09}, // y end H + {0x3807, 0xa3}, // y end L + {0x3808, 0x06}, // x output size H + {0x3809, 0x60}, // x output size L + {0x380a, 0x04}, // y output size H + {0x380b, 0xc8}, // y output size L + {0x380c, 0x07}, // HTS H + {0x380d, 0x88}, // HTS L + {0x380e, 0x04}, // VTS H + {0x380f, 0xdc}, // VTS L + {0x3810, 0x00}, // ISP x win H + {0x3811, 0x04}, // ISP x win L + {0x3813, 0x02}, // ISP y win L + {0x3814, 0x03}, // x odd inc + {0x3815, 0x01}, // x even inc + {0x3820, 0x00}, // vflip off + {0x3821, 0x67}, // mirror on, bin o + {0x382a, 0x03}, // y odd inc + {0x382b, 0x01}, // y even inc + {0x3830, 0x08}, + {0x3836, 0x02}, + {0x3837, 0x18}, + {0x3841, 0xff}, // window auto size enable + {0x3846, 0x48}, // + {0x3d85, 0x16}, // OTP power up load data/setting enable enable + {0x3d8c, 0x73}, // OTP setting start High + {0x3d8d, 0xde}, // OTP setting start Low + {0x3f08, 0x10}, // + {0x3f0a, 0x00}, // + {0x4000, 0xf1}, // out_range/format_chg/gain/exp_chg trig enable + {0x4001, 0x10}, // total 128 black column + {0x4005, 0x10}, // BLC target L + {0x4002, 0x27}, // value used to limit BLC offset + {0x4009, 0x81}, // final BLC offset limitation enable + {0x400b, 0x0c}, // DCBLC on, DCBLC manual mode on + {0x401b, 0x00}, // zero line R coefficient + {0x401d, 0x00}, // zoro line T coefficient + {0x4020, 0x00}, // Anchor left start H + {0x4021, 0x04}, // Anchor left start L + {0x4022, 0x06}, // Anchor left end H + {0x4023, 0x00}, // Anchor left end L + {0x4024, 0x0f}, // Anchor right start H + {0x4025, 0x2a}, // Anchor right start L + {0x4026, 0x0f}, // Anchor right end H + {0x4027, 0x2b}, // Anchor right end L + {0x4028, 0x00}, // top zero line start + {0x4029, 0x02}, // top zero line number + {0x402a, 0x04}, // top black line start + {0x402b, 0x04}, // top black line number + {0x402c, 0x00}, // bottom zero line start + {0x402d, 0x02}, // bottom zoro line number + {0x402e, 0x04}, // bottom black line start + {0x402f, 0x04}, // bottom black line number + {0x401f, 0x00}, // interpolation x/y disable, Anchor one disable + {0x4034, 0x3f}, + {0x403d, 0x04}, // md_precision_en + {0x4300, 0xff}, // clip max H + {0x4301, 0x00}, // clip min H + {0x4302, 0x0f}, // clip min L, clip max L + {0x4316, 0x00}, + {0x4500, 0x58}, + {0x4503, 0x18}, + {0x4600, 0x00}, + {0x4601, 0xcb}, + {0x481f, 0x32}, // clk prepare min + {0x4837, 0x16}, // global timing + {0x4850, 0x10}, // lane 1 = 1, lane 0 = 0 + {0x4851, 0x32}, // lane 3 = 3, lane 2 = 2 + {0x4b00, 0x2a}, + {0x4b0d, 0x00}, + {0x4d00, 0x04}, // temperature sensor + {0x4d01, 0x18}, // + {0x4d02, 0xc3}, // + {0x4d03, 0xff}, // + {0x4d04, 0xff}, // + {0x4d05, 0xff}, // temperature sensor + {0x5000, 0xfe}, // lenc on, slave/master AWB gain/statistics enable + {0x5001, 0x01}, // BLC on + {0x5002, 0x08}, // WBMATCH sensor's gain, H scale/WBMATCH/OTP_DPC off + {0x5003, 0x20}, // DPC_DBC buffer control enable, WB + {0x5046, 0x12}, // + {0x5780, 0x3e}, // DPC + {0x5781, 0x0f}, // + {0x5782, 0x44}, // + {0x5783, 0x02}, // + {0x5784, 0x01}, // + {0x5785, 0x00}, // + {0x5786, 0x00}, // + {0x5787, 0x04}, // + {0x5788, 0x02}, // + {0x5789, 0x0f}, // + {0x578a, 0xfd}, // + {0x578b, 0xf5}, // + {0x578c, 0xf5}, // + {0x578d, 0x03}, // + {0x578e, 0x08}, // + {0x578f, 0x0c}, // + {0x5790, 0x08}, // + {0x5791, 0x04}, // + {0x5792, 0x00}, // + {0x5793, 0x52}, // + {0x5794, 0xa3}, // DPC + {0x5871, 0x0d}, // Lenc + {0x5870, 0x18}, // + {0x586e, 0x10}, // + {0x586f, 0x08}, // + {0x58f7, 0x01}, // + {0x58f8, 0x3d}, // Lenc + {0x5901, 0x00}, // H skip off, V skip off + {0x5b00, 0x02}, // OTP DPC start address + {0x5b01, 0x10}, // OTP DPC start address + {0x5b02, 0x03}, // OTP DPC end address + {0x5b03, 0xcf}, // OTP DPC end address + {0x5b05, 0x6c}, // recover method = 2b11 + {0x5e00, 0x00}, // use 0x3ff to test pattern off + {0x5e01, 0x41}, // window cut enable + {0x382d, 0x7f}, // + {0x4825, 0x3a}, // lpx_p_min + {0x4826, 0x40}, // hs_prepare_min + {0x4808, 0x25}, // wake up delay in 1/1024 s + {0x3763, 0x18}, + {0x3768, 0xcc}, + {0x470b, 0x28}, + {0x4202, 0x00}, + {0x400d, 0x10}, // BLC offset trigger L + {0x4040, 0x04}, // BLC gain th2 + {0x403e, 0x04}, // BLC gain th1 + {0x4041, 0xc6}, // BLC + {0x3007, 0x80}, + {0x400a, 0x01}, + {REG_NULL, 0x00}, +}; + +/* + * Xclk 24Mhz + * max_framerate 30fps + * mipi_datarate per lane 720Mbps + */ +static const struct regval ov8858_3264x2448_regs_4lane[] = { + {0x0100, 0x00}, + {0x3501, 0x9a}, // exposure M + {0x3502, 0x20}, // exposure L + {0x3508, 0x02}, // gain H + {0x3808, 0x0c}, // x output size H + {0x3809, 0xc0}, // x output size L + {0x380a, 0x09}, // y output size H + {0x380b, 0x90}, // y output size L + {0x380c, 0x07}, // HTS H + {0x380d, 0x94}, // HTS L + {0x380e, 0x09}, // VTS H + {0x380f, 0xaa}, // VTS L + {0x3814, 0x01}, // x odd inc + {0x3821, 0x46}, // mirror on, bin off + {0x382a, 0x01}, // y odd inc + {0x3830, 0x06}, + {0x3836, 0x01}, + {0x3f0a, 0x00}, + {0x4001, 0x00}, // total 256 black column + {0x4022, 0x0c}, // Anchor left end H + {0x4023, 0x60}, // Anchor left end L + {0x4025, 0x36}, // Anchor right start L + {0x4027, 0x37}, // Anchor right end L + {0x402b, 0x08}, // top black line number + {0x402f, 0x08}, // interpolation x/y disable, Anchor one disable + {0x4500, 0x58}, + {0x4600, 0x01}, + {0x4601, 0x97}, + {0x382d, 0xff}, + {REG_NULL, 0x00}, +}; + +static const struct ov8858_mode supported_modes_2lane[] = { + { + .width = 3264, + .height = 2448, + .max_fps = { + .numerator = 10000, + .denominator = 150000, + }, + .exp_def = 0x09a0, + .hts_def = 0x0794, + .vts_def = 0x09aa, + .reg_list = ov8858_3264x2448_regs_2lane, + }, + { + .width = 1632, + .height = 1224, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x04d0, + .hts_def = 0x0788, + .vts_def = 0x04dc, + .reg_list = ov8858_1632x1224_regs_2lane, + }, +}; + +static const struct ov8858_mode supported_modes_4lane[] = { + { + .width = 3264, + .height = 2448, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x09a0, + .hts_def = 0x0794, + .vts_def = 0x09aa, + .reg_list = ov8858_3264x2448_regs_4lane, + }, +}; + +static const struct ov8858_mode *supported_modes; + +static const s64 link_freq_menu_items[] = { + MIPI_FREQ +}; + +static const char * const ov8858_test_pattern_menu[] = { + "Disabled", + "Vertical Color Bar Type 1", + "Vertical Color Bar Type 2", + "Vertical Color Bar Type 3", + "Vertical Color Bar Type 4" +}; + +/* Write registers up to 4 at a time */ +static int ov8858_write_reg(struct i2c_client *client, u16 reg, + u32 len, u32 val) +{ + u32 buf_i, val_i; + u8 buf[6]; + u8 *val_p; + __be32 val_be; + + if (len > 4) + return -EINVAL; + + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + + val_be = cpu_to_be32(val); + val_p = (u8 *)&val_be; + buf_i = 2; + val_i = 4 - len; + + while (val_i < 4) + buf[buf_i++] = val_p[val_i++]; + + if (i2c_master_send(client, buf, len + 2) != len + 2) + return -EIO; + + return 0; +} + +static int ov8858_write_array(struct i2c_client *client, + const struct regval *regs) +{ + u32 i; + int ret = 0; + + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) + ret = ov8858_write_reg(client, regs[i].addr, + OV8858_REG_VALUE_08BIT, + regs[i].val); + + return ret; +} + +/* Read registers up to 4 at a time */ +static int ov8858_read_reg(struct i2c_client *client, u16 reg, + unsigned int len, u32 *val) +{ + struct i2c_msg msgs[2]; + u8 *data_be_p; + __be32 data_be = 0; + __be16 reg_addr_be = cpu_to_be16(reg); + int ret; + + if (len > 4 || !len) + return -EINVAL; + + data_be_p = (u8 *)&data_be; + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 2; + msgs[0].buf = (u8 *)®_addr_be; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_be_p[4 - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = be32_to_cpu(data_be); + + return 0; +} + +static int ov8858_get_reso_dist(const struct ov8858_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); +} + +static const struct ov8858_mode * +ov8858_find_best_fit(struct ov8858 *ov8858, + struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *framefmt = &fmt->format; + int dist; + int cur_best_fit = 0; + int cur_best_fit_dist = -1; + unsigned int i; + + for (i = 0; i < ov8858->cfg_num; i++) { + dist = ov8858_get_reso_dist(&supported_modes[i], framefmt); + if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { + cur_best_fit_dist = dist; + cur_best_fit = i; + } + } + + return &supported_modes[cur_best_fit]; +} + +static int ov8858_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ov8858 *ov8858 = to_ov8858(sd); + const struct ov8858_mode *mode; + s64 h_blank, vblank_def; + + mutex_lock(&ov8858->mutex); + + mode = ov8858_find_best_fit(ov8858, fmt); + fmt->format.code = OV8858_MEDIA_BUS_FMT; + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; +#else + mutex_unlock(&ov8858->mutex); + return -ENOTTY; +#endif + } else { + ov8858->cur_mode = mode; + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(ov8858->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(ov8858->vblank, vblank_def, + OV8858_VTS_MAX - mode->height, + 1, vblank_def); + } + + mutex_unlock(&ov8858->mutex); + + return 0; +} + +static int ov8858_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ov8858 *ov8858 = to_ov8858(sd); + const struct ov8858_mode *mode = ov8858->cur_mode; + + mutex_lock(&ov8858->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); +#else + mutex_unlock(&ov8858->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = OV8858_MEDIA_BUS_FMT; + fmt->format.field = V4L2_FIELD_NONE; + } + mutex_unlock(&ov8858->mutex); + + return 0; +} + +static int ov8858_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index != 0) + return -EINVAL; + code->code = OV8858_MEDIA_BUS_FMT; + + return 0; +} + +static int ov8858_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct ov8858 *ov8858 = to_ov8858(sd); + + if (fse->index >= ov8858->cfg_num) + return -EINVAL; + + if (fse->code != OV8858_MEDIA_BUS_FMT) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = supported_modes[fse->index].width; + fse->max_height = supported_modes[fse->index].height; + fse->min_height = supported_modes[fse->index].height; + + return 0; +} + +static int ov8858_enable_test_pattern(struct ov8858 *ov8858, u32 pattern) +{ + u32 val; + + if (pattern) + val = (pattern - 1) | OV8858_TEST_PATTERN_ENABLE; + else + val = OV8858_TEST_PATTERN_DISABLE; + + return ov8858_write_reg(ov8858->client, + OV8858_REG_TEST_PATTERN, + OV8858_REG_VALUE_08BIT, + val); +} + +static int ov8858_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct ov8858 *ov8858 = to_ov8858(sd); + const struct ov8858_mode *mode = ov8858->cur_mode; + + mutex_lock(&ov8858->mutex); + fi->interval = mode->max_fps; + mutex_unlock(&ov8858->mutex); + + return 0; +} + +static void ov8858_get_r1a_otp(struct ov8858_otp_info_r1a *otp_r1a, + struct rkmodule_inf *inf) +{ + u32 i, j; + int rg, bg; + + /* fac */ + if (otp_r1a->flag & 0x80) { + inf->fac.flag = 1; + inf->fac.year = otp_r1a->year; + inf->fac.month = otp_r1a->month; + inf->fac.day = otp_r1a->day; + + for (i = 0; i < ARRAY_SIZE(ov8858_module_info) - 1; i++) { + if (ov8858_module_info[i].id == otp_r1a->module_id) + break; + } + strlcpy(inf->fac.module, ov8858_module_info[i].name, + sizeof(inf->fac.module)); + + for (i = 0; i < ARRAY_SIZE(ov8858_lens_info) - 1; i++) { + if (ov8858_lens_info[i].id == otp_r1a->lens_id) + break; + } + strlcpy(inf->fac.lens, ov8858_lens_info[i].name, + sizeof(inf->fac.lens)); + } + + /* awb */ + if (otp_r1a->flag & 0x40) { + if (otp_r1a->light_rg == 0) + /* no light source information in OTP ,light factor = 1 */ + rg = otp_r1a->rg_ratio; + else + rg = otp_r1a->rg_ratio * (otp_r1a->light_rg + 512) / 1024; + + if (otp_r1a->light_bg == 0) + /* no light source information in OTP ,light factor = 1 */ + bg = otp_r1a->bg_ratio; + else + bg = otp_r1a->bg_ratio * (otp_r1a->light_bg + 512) / 1024; + + inf->awb.flag = 1; + inf->awb.r_value = rg; + inf->awb.b_value = bg; + inf->awb.gr_value = 0x200; + inf->awb.gb_value = 0x200; + + inf->awb.golden_r_value = 0; + inf->awb.golden_b_value = 0; + inf->awb.golden_gr_value = 0; + inf->awb.golden_gb_value = 0; + } + + /* af */ + if (otp_r1a->flag & 0x20) { + inf->af.flag = 1; + inf->af.vcm_start = otp_r1a->vcm_start; + inf->af.vcm_end = otp_r1a->vcm_end; + inf->af.vcm_dir = otp_r1a->vcm_dir; + } + + /* lsc */ + if (otp_r1a->flag & 0x10) { + inf->lsc.flag = 1; + inf->lsc.decimal_bits = 0; + inf->lsc.lsc_w = 6; + inf->lsc.lsc_h = 6; + + j = 0; + for (i = 0; i < 36; i++) { + inf->lsc.lsc_gr[i] = otp_r1a->lenc[j++]; + inf->lsc.lsc_gb[i] = inf->lsc.lsc_gr[i]; + } + for (i = 0; i < 36; i++) + inf->lsc.lsc_b[i] = otp_r1a->lenc[j++] + otp_r1a->lenc[108]; + for (i = 0; i < 36; i++) + inf->lsc.lsc_r[i] = otp_r1a->lenc[j++] + otp_r1a->lenc[109]; + } +} + +static void ov8858_get_r2a_otp(struct ov8858_otp_info_r2a *otp_r2a, + struct rkmodule_inf *inf) +{ + unsigned int i, j; + int rg, bg; + + /* fac / awb */ + if (otp_r2a->flag & 0xC0) { + inf->fac.flag = 1; + inf->fac.year = otp_r2a->year; + inf->fac.month = otp_r2a->month; + inf->fac.day = otp_r2a->day; + + for (i = 0; i < ARRAY_SIZE(ov8858_module_info) - 1; i++) { + if (ov8858_module_info[i].id == otp_r2a->module_id) + break; + } + strlcpy(inf->fac.module, ov8858_module_info[i].name, + sizeof(inf->fac.module)); + + for (i = 0; i < ARRAY_SIZE(ov8858_lens_info) - 1; i++) { + if (ov8858_lens_info[i].id == otp_r2a->lens_id) + break; + } + strlcpy(inf->fac.lens, ov8858_lens_info[i].name, + sizeof(inf->fac.lens)); + + rg = otp_r2a->rg_ratio; + bg = otp_r2a->bg_ratio; + + inf->awb.flag = 1; + inf->awb.r_value = rg; + inf->awb.b_value = bg; + inf->awb.gr_value = 0x200; + inf->awb.gb_value = 0x200; + + inf->awb.golden_r_value = 0; + inf->awb.golden_b_value = 0; + inf->awb.golden_gr_value = 0; + inf->awb.golden_gb_value = 0; + } + + /* af */ + if (otp_r2a->flag & 0x20) { + inf->af.flag = 1; + inf->af.vcm_start = otp_r2a->vcm_start; + inf->af.vcm_end = otp_r2a->vcm_end; + inf->af.vcm_dir = otp_r2a->vcm_dir; + } + + /* lsc */ + if (otp_r2a->flag & 0x10) { + inf->lsc.flag = 1; + inf->lsc.decimal_bits = 0; + inf->lsc.lsc_w = 8; + inf->lsc.lsc_h = 10; + + j = 0; + for (i = 0; i < 80; i++) { + inf->lsc.lsc_gr[i] = otp_r2a->lenc[j++]; + inf->lsc.lsc_gb[i] = inf->lsc.lsc_gr[i]; + } + for (i = 0; i < 80; i++) + inf->lsc.lsc_b[i] = otp_r2a->lenc[j++]; + for (i = 0; i < 80; i++) + inf->lsc.lsc_r[i] = otp_r2a->lenc[j++]; + } +} + +static void ov8858_get_module_inf(struct ov8858 *ov8858, + struct rkmodule_inf *inf) +{ + struct ov8858_otp_info_r1a *otp_r1a = ov8858->otp_r1a; + struct ov8858_otp_info_r2a *otp_r2a = ov8858->otp_r2a; + + strlcpy(inf->base.sensor, OV8858_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, ov8858->module_name, sizeof(inf->base.module)); + strlcpy(inf->base.lens, ov8858->len_name, sizeof(inf->base.lens)); + + if (ov8858->is_r2a) { + if (otp_r2a) + ov8858_get_r2a_otp(otp_r2a, inf); + } else { + if (otp_r1a) + ov8858_get_r1a_otp(otp_r1a, inf); + } +} + +static void ov8858_set_awb_cfg(struct ov8858 *ov8858, + struct rkmodule_awb_cfg *cfg) +{ + mutex_lock(&ov8858->mutex); + memcpy(&ov8858->awb_cfg, cfg, sizeof(*cfg)); + mutex_unlock(&ov8858->mutex); +} + +static void ov8858_set_lsc_cfg(struct ov8858 *ov8858, + struct rkmodule_lsc_cfg *cfg) +{ + mutex_lock(&ov8858->mutex); + memcpy(&ov8858->lsc_cfg, cfg, sizeof(*cfg)); + mutex_unlock(&ov8858->mutex); +} + +static long ov8858_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct ov8858 *ov8858 = to_ov8858(sd); + long ret = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + ov8858_get_module_inf(ov8858, (struct rkmodule_inf *)arg); + break; + case RKMODULE_AWB_CFG: + ov8858_set_awb_cfg(ov8858, (struct rkmodule_awb_cfg *)arg); + break; + case RKMODULE_LSC_CFG: + ov8858_set_lsc_cfg(ov8858, (struct rkmodule_lsc_cfg *)arg); + break; + default: + ret = -ENOTTY; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long ov8858_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_awb_cfg *awb_cfg; + struct rkmodule_lsc_cfg *lsc_cfg; + long ret = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = ov8858_ioctl(sd, cmd, inf); + if (!ret) + ret = copy_to_user(up, inf, sizeof(*inf)); + kfree(inf); + break; + case RKMODULE_AWB_CFG: + awb_cfg = kzalloc(sizeof(*awb_cfg), GFP_KERNEL); + if (!awb_cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(awb_cfg, up, sizeof(*awb_cfg)); + if (!ret) + ret = ov8858_ioctl(sd, cmd, awb_cfg); + kfree(awb_cfg); + break; + case RKMODULE_LSC_CFG: + lsc_cfg = kzalloc(sizeof(*lsc_cfg), GFP_KERNEL); + if (!lsc_cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(lsc_cfg, up, sizeof(*lsc_cfg)); + if (!ret) + ret = ov8858_ioctl(sd, cmd, lsc_cfg); + kfree(lsc_cfg); + break; + default: + ret = -ENOTTY; + break; + } + + return ret; +} +#endif + +/*--------------------------------------------------------------------------*/ +static int ov8858_apply_otp_r1a(struct ov8858 *ov8858) +{ + int rg, bg, R_gain, G_gain, B_gain, base_gain, temp; + struct i2c_client *client = ov8858->client; + struct ov8858_otp_info_r1a *otp_ptr = ov8858->otp_r1a; + struct rkmodule_awb_cfg *awb_cfg = &ov8858->awb_cfg; + struct rkmodule_lsc_cfg *lsc_cfg = &ov8858->lsc_cfg; + u32 golden_bg_ratio = 0; + u32 golden_rg_ratio = 0; + u32 golden_g_value = 0; + u32 i; + + if (awb_cfg->enable) { + golden_g_value = (awb_cfg->golden_gb_value + + awb_cfg->golden_gr_value) / 2; + golden_bg_ratio = awb_cfg->golden_b_value * 0x200 / golden_g_value; + golden_rg_ratio = awb_cfg->golden_r_value * 0x200 / golden_g_value; + } + + /* apply OTP WB Calibration */ + if ((otp_ptr->flag & 0x40) && golden_bg_ratio && golden_rg_ratio) { + if (otp_ptr->light_rg == 0) + /* + * no light source information in OTP, + * light factor = 1 + */ + rg = otp_ptr->rg_ratio; + else + rg = otp_ptr->rg_ratio * + (otp_ptr->light_rg + 512) / 1024; + + if (otp_ptr->light_bg == 0) + /* + * no light source information in OTP, + * light factor = 1 + */ + bg = otp_ptr->bg_ratio; + else + bg = otp_ptr->bg_ratio * + (otp_ptr->light_bg + 512) / 1024; + + /* calculate G gain */ + R_gain = (golden_rg_ratio * 1000) / rg; + B_gain = (golden_bg_ratio * 1000) / bg; + G_gain = 1000; + if (R_gain < 1000 || B_gain < 1000) { + if (R_gain < B_gain) + base_gain = R_gain; + else + base_gain = B_gain; + } else { + base_gain = G_gain; + } + R_gain = 0x400 * R_gain / (base_gain); + B_gain = 0x400 * B_gain / (base_gain); + G_gain = 0x400 * G_gain / (base_gain); + + /* update sensor WB gain */ + if (R_gain > 0x400) { + ov8858_write_1byte(client, 0x5032, R_gain >> 8); + ov8858_write_1byte(client, 0x5033, R_gain & 0x00ff); + } + if (G_gain > 0x400) { + ov8858_write_1byte(client, 0x5034, G_gain >> 8); + ov8858_write_1byte(client, 0x5035, G_gain & 0x00ff); + } + if (B_gain > 0x400) { + ov8858_write_1byte(client, 0x5036, B_gain >> 8); + ov8858_write_1byte(client, 0x5037, B_gain & 0x00ff); + } + + dev_dbg(&client->dev, "apply awb gain: 0x%x, 0x%x, 0x%x\n", + R_gain, G_gain, B_gain); + } + + /* apply OTP Lenc Calibration */ + if ((otp_ptr->flag & 0x10) && lsc_cfg->enable) { + ov8858_read_1byte(client, 0x5000, &temp); + temp = 0x80 | temp; + ov8858_write_1byte(client, 0x5000, temp); + for (i = 0; i < ARRAY_SIZE(otp_ptr->lenc); i++) { + ov8858_write_1byte(client, 0x5800 + i, + otp_ptr->lenc[i]); + dev_dbg(&client->dev, "apply lenc[%d]: 0x%x\n", + i, otp_ptr->lenc[i]); + } + } + + return 0; +} + +static int ov8858_apply_otp_r2a(struct ov8858 *ov8858) +{ + int rg, bg, R_gain, G_gain, B_gain, base_gain, temp; + struct i2c_client *client = ov8858->client; + struct ov8858_otp_info_r2a *otp_ptr = ov8858->otp_r2a; + struct rkmodule_awb_cfg *awb_cfg = &ov8858->awb_cfg; + struct rkmodule_lsc_cfg *lsc_cfg = &ov8858->lsc_cfg; + u32 golden_bg_ratio = 0; + u32 golden_rg_ratio = 0; + u32 golden_g_value = 0; + u32 i; + + if (awb_cfg->enable) { + golden_g_value = (awb_cfg->golden_gb_value + + awb_cfg->golden_gr_value) / 2; + golden_bg_ratio = awb_cfg->golden_b_value * 0x200 / golden_g_value; + golden_rg_ratio = awb_cfg->golden_r_value * 0x200 / golden_g_value; + } + + /* apply OTP WB Calibration */ + if ((otp_ptr->flag & 0xC0) && golden_bg_ratio && golden_rg_ratio) { + rg = otp_ptr->rg_ratio; + bg = otp_ptr->bg_ratio; + /* calculate G gain */ + R_gain = (golden_rg_ratio * 1000) / rg; + B_gain = (golden_bg_ratio * 1000) / bg; + G_gain = 1000; + if (R_gain < 1000 || B_gain < 1000) { + if (R_gain < B_gain) + base_gain = R_gain; + else + base_gain = B_gain; + } else { + base_gain = G_gain; + } + R_gain = 0x400 * R_gain / (base_gain); + B_gain = 0x400 * B_gain / (base_gain); + G_gain = 0x400 * G_gain / (base_gain); + + /* update sensor WB gain */ + if (R_gain > 0x400) { + ov8858_write_1byte(client, 0x5032, R_gain >> 8); + ov8858_write_1byte(client, 0x5033, R_gain & 0x00ff); + } + if (G_gain > 0x400) { + ov8858_write_1byte(client, 0x5034, G_gain >> 8); + ov8858_write_1byte(client, 0x5035, G_gain & 0x00ff); + } + if (B_gain > 0x400) { + ov8858_write_1byte(client, 0x5036, B_gain >> 8); + ov8858_write_1byte(client, 0x5037, B_gain & 0x00ff); + } + + dev_dbg(&client->dev, "apply awb gain: 0x%x, 0x%x, 0x%x\n", + R_gain, G_gain, B_gain); + } + + /* apply OTP Lenc Calibration */ + if ((otp_ptr->flag & 0x10) && lsc_cfg->enable) { + ov8858_read_1byte(client, 0x5000, &temp); + temp = 0x80 | temp; + ov8858_write_1byte(client, 0x5000, temp); + for (i = 0; i < ARRAY_SIZE(otp_ptr->lenc); i++) { + ov8858_write_1byte(client, 0x5800 + i, + otp_ptr->lenc[i]); + dev_dbg(&client->dev, "apply lenc[%d]: 0x%x\n", + i, otp_ptr->lenc[i]); + } + } + + return 0; +} + +static int ov8858_apply_otp(struct ov8858 *ov8858) +{ + int ret = 0; + + if (ov8858->is_r2a && ov8858->otp_r2a) + ret = ov8858_apply_otp_r2a(ov8858); + else if (ov8858->otp_r1a) + ret = ov8858_apply_otp_r1a(ov8858); + + return ret; +} + +static int __ov8858_start_stream(struct ov8858 *ov8858) +{ + int ret; + + ret = ov8858_write_array(ov8858->client, ov8858_global_regs); + if (ret) + return ret; + + ret = ov8858_write_array(ov8858->client, ov8858->cur_mode->reg_list); + if (ret) + return ret; + + /* In case these controls are set before streaming */ + mutex_unlock(&ov8858->mutex); + ret = v4l2_ctrl_handler_setup(&ov8858->ctrl_handler); + mutex_lock(&ov8858->mutex); + if (ret) + return ret; + + ret = ov8858_apply_otp(ov8858); + if (ret) + return ret; + + return ov8858_write_reg(ov8858->client, + OV8858_REG_CTRL_MODE, + OV8858_REG_VALUE_08BIT, + OV8858_MODE_STREAMING); +} + +static int __ov8858_stop_stream(struct ov8858 *ov8858) +{ + return ov8858_write_reg(ov8858->client, + OV8858_REG_CTRL_MODE, + OV8858_REG_VALUE_08BIT, + OV8858_MODE_SW_STANDBY); +} + +static int ov8858_s_stream(struct v4l2_subdev *sd, int on) +{ + struct ov8858 *ov8858 = to_ov8858(sd); + struct i2c_client *client = ov8858->client; + int ret = 0; + + mutex_lock(&ov8858->mutex); + on = !!on; + if (on == ov8858->streaming) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = __ov8858_start_stream(ov8858); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + __ov8858_stop_stream(ov8858); + pm_runtime_put(&client->dev); + } + + ov8858->streaming = on; + +unlock_and_return: + mutex_unlock(&ov8858->mutex); + + return ret; +} + +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 ov8858_cal_delay(u32 cycles) +{ + return DIV_ROUND_UP(cycles, OV8858_XVCLK_FREQ / 1000 / 1000); +} + +static int __ov8858_power_on(struct ov8858 *ov8858) +{ + int ret; + u32 delay_us; + struct device *dev = &ov8858->client->dev; + + if (!IS_ERR(ov8858->power_gpio)) + gpiod_set_value_cansleep(ov8858->power_gpio, 1); + + usleep_range(1000, 2000); + + if (!IS_ERR_OR_NULL(ov8858->pins_default)) { + ret = pinctrl_select_state(ov8858->pinctrl, + ov8858->pins_default); + if (ret < 0) + dev_err(dev, "could not set pins\n"); + } + + ret = clk_set_rate(ov8858->xvclk, OV8858_XVCLK_FREQ); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate (24MHz)\n"); + if (clk_get_rate(ov8858->xvclk) != OV8858_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); + ret = clk_prepare_enable(ov8858->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + + if (!IS_ERR(ov8858->reset_gpio)) + gpiod_set_value_cansleep(ov8858->reset_gpio, 0); + + ret = regulator_bulk_enable(OV8858_NUM_SUPPLIES, ov8858->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + + if (!IS_ERR(ov8858->reset_gpio)) + gpiod_set_value_cansleep(ov8858->reset_gpio, 1); + + usleep_range(1000, 2000); + if (!IS_ERR(ov8858->pwdn_gpio)) + gpiod_set_value_cansleep(ov8858->pwdn_gpio, 1); + + /* 8192 cycles prior to first SCCB transaction */ + delay_us = ov8858_cal_delay(8192); + usleep_range(delay_us, delay_us * 2); + + return 0; + +disable_clk: + clk_disable_unprepare(ov8858->xvclk); + + return ret; +} + +static void __ov8858_power_off(struct ov8858 *ov8858) +{ + int ret; + struct device *dev = &ov8858->client->dev; + + if (!IS_ERR(ov8858->pwdn_gpio)) + gpiod_set_value_cansleep(ov8858->pwdn_gpio, 0); + clk_disable_unprepare(ov8858->xvclk); + if (!IS_ERR(ov8858->reset_gpio)) + gpiod_set_value_cansleep(ov8858->reset_gpio, 0); + if (!IS_ERR_OR_NULL(ov8858->pins_sleep)) { + ret = pinctrl_select_state(ov8858->pinctrl, + ov8858->pins_sleep); + if (ret < 0) + dev_dbg(dev, "could not set pins\n"); + } + + //if (!IS_ERR(ov8858->power_gpio)) + //gpiod_set_value_cansleep(ov8858->power_gpio, 0); + + regulator_bulk_disable(OV8858_NUM_SUPPLIES, ov8858->supplies); +} + +static int ov8858_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov8858 *ov8858 = to_ov8858(sd); + + return __ov8858_power_on(ov8858); +} + +static int ov8858_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov8858 *ov8858 = to_ov8858(sd); + + __ov8858_power_off(ov8858); + + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int ov8858_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct ov8858 *ov8858 = to_ov8858(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + const struct ov8858_mode *def_mode = &supported_modes[0]; + + mutex_lock(&ov8858->mutex); + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = OV8858_MEDIA_BUS_FMT; + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&ov8858->mutex); + /* No crop or compose */ + + return 0; +} +#endif + +static const struct dev_pm_ops ov8858_pm_ops = { + SET_RUNTIME_PM_OPS(ov8858_runtime_suspend, + ov8858_runtime_resume, NULL) +}; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops ov8858_internal_ops = { + .open = ov8858_open, +}; +#endif + +static const struct v4l2_subdev_core_ops ov8858_core_ops = { + .ioctl = ov8858_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = ov8858_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops ov8858_video_ops = { + .s_stream = ov8858_s_stream, + .g_frame_interval = ov8858_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops ov8858_pad_ops = { + .enum_mbus_code = ov8858_enum_mbus_code, + .enum_frame_size = ov8858_enum_frame_sizes, + .get_fmt = ov8858_get_fmt, + .set_fmt = ov8858_set_fmt, +}; + +static const struct v4l2_subdev_ops ov8858_subdev_ops = { + .core = &ov8858_core_ops, + .video = &ov8858_video_ops, + .pad = &ov8858_pad_ops, +}; + +static int ov8858_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov8858 *ov8858 = container_of(ctrl->handler, + struct ov8858, ctrl_handler); + struct i2c_client *client = ov8858->client; + s64 max; + int ret = 0; + + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ + max = ov8858->cur_mode->height + ctrl->val - 4; + __v4l2_ctrl_modify_range(ov8858->exposure, + ov8858->exposure->minimum, max, + ov8858->exposure->step, + ov8858->exposure->default_value); + break; + } + + if (pm_runtime_get(&client->dev) <= 0) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + /* 4 least significant bits of expsoure are fractional part */ + ret = ov8858_write_reg(ov8858->client, + OV8858_REG_EXPOSURE, + OV8858_REG_VALUE_24BIT, + ctrl->val << 4); + break; + case V4L2_CID_ANALOGUE_GAIN: + ret = ov8858_write_reg(ov8858->client, + OV8858_REG_GAIN_H, + OV8858_REG_VALUE_08BIT, + (ctrl->val >> OV8858_GAIN_H_SHIFT) & + OV8858_GAIN_H_MASK); + ret |= ov8858_write_reg(ov8858->client, + OV8858_REG_GAIN_L, + OV8858_REG_VALUE_08BIT, + ctrl->val & OV8858_GAIN_L_MASK); + break; + case V4L2_CID_VBLANK: + ret = ov8858_write_reg(ov8858->client, + OV8858_REG_VTS, + OV8858_REG_VALUE_16BIT, + ctrl->val + ov8858->cur_mode->height); + break; + case V4L2_CID_TEST_PATTERN: + ret = ov8858_enable_test_pattern(ov8858, ctrl->val); + break; + default: + dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", + __func__, ctrl->id, ctrl->val); + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops ov8858_ctrl_ops = { + .s_ctrl = ov8858_set_ctrl, +}; + +static int ov8858_initialize_controls(struct ov8858 *ov8858) +{ + const struct ov8858_mode *mode; + struct v4l2_ctrl_handler *handler; + struct v4l2_ctrl *ctrl; + s64 exposure_max, vblank_def; + u32 h_blank; + int ret; + + handler = &ov8858->ctrl_handler; + mode = ov8858->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 8); + if (ret) + return ret; + handler->lock = &ov8858->mutex; + + ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, + 0, 0, link_freq_menu_items); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, + 0, ov8858->pixel_rate, 1, ov8858->pixel_rate); + + h_blank = mode->hts_def - mode->width; + ov8858->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (ov8858->hblank) + ov8858->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = mode->vts_def - mode->height; + ov8858->vblank = v4l2_ctrl_new_std(handler, &ov8858_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + OV8858_VTS_MAX - mode->height, + 1, vblank_def); + + exposure_max = mode->vts_def - 4; + ov8858->exposure = v4l2_ctrl_new_std(handler, &ov8858_ctrl_ops, + V4L2_CID_EXPOSURE, OV8858_EXPOSURE_MIN, + exposure_max, OV8858_EXPOSURE_STEP, + mode->exp_def); + + ov8858->anal_gain = v4l2_ctrl_new_std(handler, &ov8858_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, OV8858_GAIN_MIN, + OV8858_GAIN_MAX, OV8858_GAIN_STEP, + OV8858_GAIN_DEFAULT); + + ov8858->test_pattern = v4l2_ctrl_new_std_menu_items(handler, + &ov8858_ctrl_ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov8858_test_pattern_menu) - 1, + 0, 0, ov8858_test_pattern_menu); + + if (handler->error) { + ret = handler->error; + dev_err(&ov8858->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + ov8858->subdev.ctrl_handler = handler; + + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +static int ov8858_otp_read_r1a(struct ov8858 *ov8858) +{ + int otp_flag, addr, temp, i; + struct ov8858_otp_info_r1a *otp_ptr; + struct device *dev = &ov8858->client->dev; + struct i2c_client *client = ov8858->client; + + otp_ptr = kzalloc(sizeof(*otp_ptr), GFP_KERNEL); + if (!otp_ptr) + return -ENOMEM; + + otp_flag = 0; + ov8858_read_1byte(client, 0x7010, &otp_flag); + if ((otp_flag & 0xc0) == 0x40) + addr = 0x7011; /* base address of info group 1 */ + else if ((otp_flag & 0x30) == 0x10) + addr = 0x7016; /* base address of info group 2 */ + else if ((otp_flag & 0x0c) == 0x04) + addr = 0x701b; /* base address of info group 3 */ + else + addr = 0; + + if (addr != 0) { + otp_ptr->flag = 0x80; /* valid info in OTP */ + ov8858_read_1byte(client, addr, &otp_ptr->module_id); + ov8858_read_1byte(client, addr + 1, &otp_ptr->lens_id); + ov8858_read_1byte(client, addr + 2, &otp_ptr->year); + ov8858_read_1byte(client, addr + 3, &otp_ptr->month); + ov8858_read_1byte(client, addr + 4, &otp_ptr->day); + dev_dbg(dev, "fac info: module(0x%x) lens(0x%x) time(%d_%d_%d)!\n", + otp_ptr->module_id, + otp_ptr->lens_id, + otp_ptr->year, + otp_ptr->month, + otp_ptr->day); + } + + /* OTP base information and WB calibration data */ + ov8858_read_1byte(client, 0x7020, &otp_flag); + if ((otp_flag & 0xc0) == 0x40) + addr = 0x7021; /* base address of info group 1 */ + else if ((otp_flag & 0x30) == 0x10) + addr = 0x7026; /* base address of info group 2 */ + else if ((otp_flag & 0x0c) == 0x04) + addr = 0x702b; /* base address of info group 3 */ + else + addr = 0; + + if (addr != 0) { + otp_ptr->flag |= 0x40; /* valid info and AWB in OTP */ + ov8858_read_1byte(client, addr + 4, &temp); + ov8858_read_1byte(client, addr, &otp_ptr->rg_ratio); + otp_ptr->rg_ratio = (otp_ptr->rg_ratio << 2) + + ((temp >> 6) & 0x03); + ov8858_read_1byte(client, addr + 1, &otp_ptr->bg_ratio); + otp_ptr->bg_ratio = (otp_ptr->bg_ratio << 2) + + ((temp >> 4) & 0x03); + ov8858_read_1byte(client, addr + 2, &otp_ptr->light_rg); + otp_ptr->light_rg = (otp_ptr->light_rg << 2) + + ((temp >> 2) & 0x03); + ov8858_read_1byte(client, addr + 3, &otp_ptr->light_bg); + otp_ptr->light_bg = (otp_ptr->light_bg << 2) + + ((temp) & 0x03); + dev_dbg(dev, "awb info: (0x%x, 0x%x, 0x%x, 0x%x)!\n", + otp_ptr->rg_ratio, otp_ptr->bg_ratio, + otp_ptr->light_rg, otp_ptr->light_bg); + } + + /* OTP VCM Calibration */ + ov8858_read_1byte(client, 0x7030, &otp_flag); + if ((otp_flag & 0xc0) == 0x40) + addr = 0x7031; /* base address of VCM Calibration group 1 */ + else if ((otp_flag & 0x30) == 0x10) + addr = 0x7034; /* base address of VCM Calibration group 2 */ + else if ((otp_flag & 0x0c) == 0x04) + addr = 0x7037; /* base address of VCM Calibration group 3 */ + else + addr = 0; + if (addr != 0) { + otp_ptr->flag |= 0x20; + ov8858_read_1byte(client, addr + 2, &temp); + ov8858_read_1byte(client, addr, &otp_ptr->vcm_start); + otp_ptr->vcm_start = (otp_ptr->vcm_start << 2) | + ((temp >> 6) & 0x03); + ov8858_read_1byte(client, addr + 1, &otp_ptr->vcm_end); + otp_ptr->vcm_end = (otp_ptr->vcm_end << 2) | + ((temp >> 4) & 0x03); + otp_ptr->vcm_dir = (temp >> 2) & 0x03; + dev_dbg(dev, "vcm_info: 0x%x, 0x%x, 0x%x!\n", + otp_ptr->vcm_start, + otp_ptr->vcm_end, + otp_ptr->vcm_dir); + } + + /* OTP Lenc Calibration */ + ov8858_read_1byte(client, 0x703a, &otp_flag); + if ((otp_flag & 0xc0) == 0x40) + addr = 0x703b; /* base address of Lenc Calibration group 1 */ + else if ((otp_flag & 0x30) == 0x10) + addr = 0x70a9; /* base address of Lenc Calibration group 2 */ + else if ((otp_flag & 0x0c) == 0x04) + addr = 0x7117; /* base address of Lenc Calibration group 3 */ + else + addr = 0; + if (addr != 0) { + otp_ptr->flag |= 0x10; + for (i = 0; i < 110; i++) { + ov8858_read_1byte(client, addr + i, &otp_ptr->lenc[i]); + dev_dbg(dev, "lsc 0x%x!\n", otp_ptr->lenc[i]); + } + } + + for (i = 0x7010; i <= 0x7184; i++) + ov8858_write_1byte(client, i, 0); /* clear OTP buffer */ + + if (otp_ptr->flag) { + ov8858->otp_r1a = otp_ptr; + } else { + ov8858->otp_r1a = NULL; + dev_info(dev, "otp_r1a is null!\n"); + kfree(otp_ptr); + } + + return 0; +} + +static int ov8858_otp_read_r2a(struct ov8858 *ov8858) +{ + struct ov8858_otp_info_r2a *otp_ptr; + int otp_flag, addr, temp, checksum, i; + struct device *dev = &ov8858->client->dev; + struct i2c_client *client = ov8858->client; + + otp_ptr = kzalloc(sizeof(*otp_ptr), GFP_KERNEL); + if (!otp_ptr) + return -ENOMEM; + + /* OTP base information and WB calibration data */ + otp_flag = 0; + ov8858_read_1byte(client, 0x7010, &otp_flag); + if ((otp_flag & 0xc0) == 0x40) + addr = 0x7011; /* base address of info group 1 */ + else if ((otp_flag & 0x30) == 0x10) + addr = 0x7019; /* base address of info group 2 */ + else + addr = 0; + + if (addr != 0) { + otp_ptr->flag = 0xC0; /* valid info and AWB in OTP */ + ov8858_read_1byte(client, addr, &otp_ptr->module_id); + ov8858_read_1byte(client, addr + 1, &otp_ptr->lens_id); + ov8858_read_1byte(client, addr + 2, &otp_ptr->year); + ov8858_read_1byte(client, addr + 3, &otp_ptr->month); + ov8858_read_1byte(client, addr + 4, &otp_ptr->day); + ov8858_read_1byte(client, addr + 7, &temp); + ov8858_read_1byte(client, addr + 5, &otp_ptr->rg_ratio); + otp_ptr->rg_ratio = (otp_ptr->rg_ratio << 2) + + ((temp >> 6) & 0x03); + ov8858_read_1byte(client, addr + 6, &otp_ptr->bg_ratio); + otp_ptr->bg_ratio = (otp_ptr->bg_ratio << 2) + + ((temp >> 4) & 0x03); + + dev_dbg(dev, "fac info: module(0x%x) lens(0x%x) time(%d_%d_%d) !\n", + otp_ptr->module_id, + otp_ptr->lens_id, + otp_ptr->year, + otp_ptr->month, + otp_ptr->day); + dev_dbg(dev, "awb info: (0x%x, 0x%x)!\n", + otp_ptr->rg_ratio, + otp_ptr->bg_ratio); + } + + /* OTP VCM Calibration */ + ov8858_read_1byte(client, 0x7021, &otp_flag); + if ((otp_flag & 0xc0) == 0x40) + addr = 0x7022; /* base address of VCM Calibration group 1 */ + else if ((otp_flag & 0x30) == 0x10) + addr = 0x7025; /* base address of VCM Calibration group 2 */ + else + addr = 0; + + if (addr != 0) { + otp_ptr->flag |= 0x20; + ov8858_read_1byte(client, addr + 2, &temp); + ov8858_read_1byte(client, addr, &otp_ptr->vcm_start); + otp_ptr->vcm_start = (otp_ptr->vcm_start << 2) | + ((temp >> 6) & 0x03); + ov8858_read_1byte(client, addr + 1, &otp_ptr->vcm_end); + otp_ptr->vcm_end = (otp_ptr->vcm_end << 2) | + ((temp >> 4) & 0x03); + otp_ptr->vcm_dir = (temp >> 2) & 0x03; + } + + /* OTP Lenc Calibration */ + ov8858_read_1byte(client, 0x7028, &otp_flag); + if ((otp_flag & 0xc0) == 0x40) + addr = 0x7029; /* base address of Lenc Calibration group 1 */ + else if ((otp_flag & 0x30) == 0x10) + addr = 0x711a; /* base address of Lenc Calibration group 2 */ + else + addr = 0; + + if (addr != 0) { + checksum = 0; + for (i = 0; i < 240; i++) { + ov8858_read_1byte(client, addr + i, &otp_ptr->lenc[i]); + checksum += otp_ptr->lenc[i]; + dev_dbg(dev, "lsc_info: 0x%x!\n", otp_ptr->lenc[i]); + } + checksum = (checksum) % 255 + 1; + ov8858_read_1byte(client, addr + 240, &otp_ptr->checksum); + if (otp_ptr->checksum == checksum) + otp_ptr->flag |= 0x10; + } + + for (i = 0x7010; i <= 0x720a; i++) + ov8858_write_1byte(client, i, 0); /* clear OTP buffer */ + + if (otp_ptr->flag) { + ov8858->otp_r2a = otp_ptr; + } else { + ov8858->otp_r2a = NULL; + dev_info(dev, "otp_r2a is null!\n"); + kfree(otp_ptr); + } + + return 0; +} + +static int ov8858_otp_read(struct ov8858 *ov8858) +{ + int temp = 0; + int ret = 0; + struct i2c_client *client = ov8858->client; + + /* stream on */ + ov8858_write_1byte(client, + OV8858_REG_CTRL_MODE, + OV8858_MODE_STREAMING); + + ov8858_read_1byte(client, 0x5002, &temp); + ov8858_write_1byte(client, 0x5002, (temp & (~0x08))); + + /* read OTP into buffer */ + ov8858_write_1byte(client, 0x3d84, 0xC0); + ov8858_write_1byte(client, 0x3d88, 0x70); /* OTP start address */ + ov8858_write_1byte(client, 0x3d89, 0x10); + if (ov8858->is_r2a) { + ov8858_write_1byte(client, 0x3d8A, 0x72); /* OTP end address */ + ov8858_write_1byte(client, 0x3d8B, 0x0a); + } else { + ov8858_write_1byte(client, 0x3d8A, 0x71); /* OTP end address */ + ov8858_write_1byte(client, 0x3d8B, 0x84); + } + ov8858_write_1byte(client, 0x3d81, 0x01); /* load otp into buffer */ + usleep_range(10000, 20000); + + if (ov8858->is_r2a) + ret = ov8858_otp_read_r2a(ov8858); + else + ret = ov8858_otp_read_r1a(ov8858); + + /* set 0x5002[3] to "1" */ + ov8858_read_1byte(client, 0x5002, &temp); + ov8858_write_1byte(client, 0x5002, 0x08 | (temp & (~0x08))); + + /* stream off */ + ov8858_write_1byte(client, + OV8858_REG_CTRL_MODE, + OV8858_MODE_SW_STANDBY); + + return ret; +} + +static int ov8858_check_sensor_id(struct ov8858 *ov8858, + struct i2c_client *client) +{ + struct device *dev = &ov8858->client->dev; + u32 id = 0; + int ret; + + ret = ov8858_read_reg(client, OV8858_REG_CHIP_ID, + OV8858_REG_VALUE_24BIT, &id); + if (id != CHIP_ID) { + dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret); + return ret; + } + + ret = ov8858_read_reg(client, OV8858_CHIP_REVISION_REG, + OV8858_REG_VALUE_08BIT, &id); + if (ret) { + dev_err(dev, "Read chip revision register error\n"); + return ret; + } + dev_info(dev, "Detected OV%06x sensor, REVISION 0x%x\n", CHIP_ID, id); + + if (id == OV8858_R2A) { + if (4 == ov8858->lane_num) { + ov8858_global_regs = ov8858_global_regs_r2a_4lane; + } else { + ov8858_global_regs = ov8858_global_regs_r2a_2lane; + } + + ov8858->is_r2a = true; + } else { + ov8858_global_regs = ov8858_global_regs_r1a; + ov8858->is_r2a = false; + dev_warn(dev, "R1A may not work well current!\n"); + } + + return 0; +} + +static int ov8858_configure_regulators(struct ov8858 *ov8858) +{ + unsigned int i; + + for (i = 0; i < OV8858_NUM_SUPPLIES; i++) + ov8858->supplies[i].supply = ov8858_supply_names[i]; + + return devm_regulator_bulk_get(&ov8858->client->dev, + OV8858_NUM_SUPPLIES, + ov8858->supplies); +} + +static int ov8858_parse_of(struct ov8858 *ov8858) +{ + struct device *dev = &ov8858->client->dev; + struct device_node *endpoint; + struct fwnode_handle *fwnode; + int rval; + + endpoint = of_graph_get_next_endpoint(dev->of_node, NULL); + if (!endpoint) { + dev_err(dev, "Failed to get endpoint\n"); + return -EINVAL; + } + fwnode = of_fwnode_handle(endpoint); + rval = fwnode_property_read_u32_array(fwnode, "data-lanes", NULL, 0); + if (rval <= 0) { + dev_warn(dev, " Get mipi lane num failed!\n"); + return -1; + } + + ov8858->lane_num = rval; + if (4 == ov8858->lane_num) { + ov8858->cur_mode = &supported_modes_4lane[0]; + supported_modes = supported_modes_4lane; + ov8858->cfg_num = ARRAY_SIZE(supported_modes_4lane); + + /* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ + ov8858->pixel_rate = MIPI_FREQ * 2U * ov8858->lane_num / 10U; + dev_info(dev, "lane_num(%d) pixel_rate(%u)\n", + ov8858->lane_num, ov8858->pixel_rate); + } else { + ov8858->cur_mode = &supported_modes_2lane[0]; + supported_modes = supported_modes_2lane; + ov8858->cfg_num = ARRAY_SIZE(supported_modes_2lane); + + /*pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ + ov8858->pixel_rate = MIPI_FREQ * 2U * (ov8858->lane_num) / 10U; + dev_info(dev, "lane_num(%d) pixel_rate(%u)\n", + ov8858->lane_num, ov8858->pixel_rate); + } + return 0; +} + +static int ov8858_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct ov8858 *ov8858; + struct v4l2_subdev *sd; + char facing[2]; + int ret; + + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + ov8858 = devm_kzalloc(dev, sizeof(*ov8858), GFP_KERNEL); + if (!ov8858) + return -ENOMEM; + + ov8858->client = client; + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &ov8858->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &ov8858->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &ov8858->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &ov8858->len_name); + if (ret) { + dev_err(dev, + "could not get module information!\n"); + return -EINVAL; + } + + ov8858->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(ov8858->xvclk)) { + dev_err(dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + ov8858->power_gpio = devm_gpiod_get(dev, "power", GPIOD_OUT_LOW); + if (IS_ERR(ov8858->power_gpio)) + dev_warn(dev, "Failed to get power-gpios, maybe no use\n"); + + ov8858->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ov8858->reset_gpio)) + dev_warn(dev, "Failed to get reset-gpios, maybe no use\n"); + + ov8858->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(ov8858->pwdn_gpio)) + dev_warn(dev, "Failed to get pwdn-gpios, maybe no use\n"); + + ret = ov8858_configure_regulators(ov8858); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + + ret = ov8858_parse_of(ov8858); + if (ret != 0) + return -EINVAL; + + ov8858->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR(ov8858->pinctrl)) { + ov8858->pins_default = + pinctrl_lookup_state(ov8858->pinctrl, + OF_CAMERA_PINCTRL_STATE_DEFAULT); + if (IS_ERR(ov8858->pins_default)) + dev_err(dev, "could not get default pinstate\n"); + + ov8858->pins_sleep = + pinctrl_lookup_state(ov8858->pinctrl, + OF_CAMERA_PINCTRL_STATE_SLEEP); + if (IS_ERR(ov8858->pins_sleep)) + dev_err(dev, "could not get sleep pinstate\n"); + } + + mutex_init(&ov8858->mutex); + + sd = &ov8858->subdev; + v4l2_i2c_subdev_init(sd, client, &ov8858_subdev_ops); + ret = ov8858_initialize_controls(ov8858); + if (ret) + goto err_destroy_mutex; + + ret = __ov8858_power_on(ov8858); + if (ret) + goto err_free_handler; + + ret = ov8858_check_sensor_id(ov8858, client); + if (ret) + goto err_power_off; + + ov8858_otp_read(ov8858); + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &ov8858_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + ov8858->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &ov8858->pad); + if (ret < 0) + goto err_power_off; +#endif + + memset(facing, 0, sizeof(facing)); + if (strcmp(ov8858->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + ov8858->module_index, facing, + OV8858_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor_common(sd); + if (ret) { + dev_err(dev, "v4l2 async register subdev failed\n"); + goto err_clean_entity; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; + +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif +err_power_off: + __ov8858_power_off(ov8858); +err_free_handler: + v4l2_ctrl_handler_free(&ov8858->ctrl_handler); +err_destroy_mutex: + mutex_destroy(&ov8858->mutex); + + return ret; +} + +static int ov8858_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov8858 *ov8858 = to_ov8858(sd); + + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + v4l2_ctrl_handler_free(&ov8858->ctrl_handler); + if (ov8858->otp_r2a) + kfree(ov8858->otp_r2a); + if (ov8858->otp_r1a) + kfree(ov8858->otp_r1a); + mutex_destroy(&ov8858->mutex); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __ov8858_power_off(ov8858); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id ov8858_of_match[] = { + { .compatible = "ovti,ov8858" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ov8858_of_match); +#endif + +static const struct i2c_device_id ov8858_match_id[] = { + { "ovti,ov8858", 0 }, + { }, +}; + +static struct i2c_driver ov8858_i2c_driver = { + .driver = { + .name = OV8858_NAME, + .pm = &ov8858_pm_ops, + .of_match_table = of_match_ptr(ov8858_of_match), + }, + .probe = &ov8858_probe, + .remove = &ov8858_remove, + .id_table = ov8858_match_id, +}; + +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&ov8858_i2c_driver); +} + +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&ov8858_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_DESCRIPTION("OmniVision ov8858 sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ov9281.c b/drivers/media/i2c/ov9281.c new file mode 100644 index 000000000000..7fbc1f160c66 --- /dev/null +++ b/drivers/media/i2c/ov9281.c @@ -0,0 +1,1172 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ov9281 driver + * + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd. + * V0.0X01.0X02 fix mclk issue when probe multiple camera. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x2) + +#ifndef V4L2_CID_DIGITAL_GAIN +#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN +#endif + +#define OV9281_LINK_FREQ_400MHZ 400000000 +/* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ +#define OV9281_PIXEL_RATE (OV9281_LINK_FREQ_400MHZ * 2 * 2 / 10) +#define OV9281_XVCLK_FREQ 24000000 + +#define CHIP_ID 0x9281 +#define OV9281_REG_CHIP_ID 0x300a + +#define OV9281_REG_CTRL_MODE 0x0100 +#define OV9281_MODE_SW_STANDBY 0x0 +#define OV9281_MODE_STREAMING BIT(0) + +#define OV9281_REG_EXPOSURE 0x3500 +#define OV9281_EXPOSURE_MIN 4 +#define OV9281_EXPOSURE_STEP 1 +#define OV9281_VTS_MAX 0x7fff + +#define OV9281_REG_GAIN_H 0x3508 +#define OV9281_REG_GAIN_L 0x3509 +#define OV9281_GAIN_H_MASK 0x07 +#define OV9281_GAIN_H_SHIFT 8 +#define OV9281_GAIN_L_MASK 0xff +#define OV9281_GAIN_MIN 0x10 +#define OV9281_GAIN_MAX 0xf8 +#define OV9281_GAIN_STEP 1 +#define OV9281_GAIN_DEFAULT 0x10 + +#define OV9281_REG_TEST_PATTERN 0x5e00 +#define OV9281_TEST_PATTERN_ENABLE 0x80 +#define OV9281_TEST_PATTERN_DISABLE 0x0 + +#define OV9281_REG_VTS 0x380e + +#define REG_NULL 0xFFFF + +#define OV9281_REG_VALUE_08BIT 1 +#define OV9281_REG_VALUE_16BIT 2 +#define OV9281_REG_VALUE_24BIT 3 + +#define OV9281_LANES 2 +#define OV9281_BITS_PER_SAMPLE 10 + +#define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default" +#define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep" + +#define OV9281_NAME "ov9281" + +static const char * const ov9281_supply_names[] = { + "avdd", /* Analog power */ + "dovdd", /* Digital I/O power */ + "dvdd", /* Digital core power */ +}; + +#define OV9281_NUM_SUPPLIES ARRAY_SIZE(ov9281_supply_names) + +struct regval { + u16 addr; + u8 val; +}; + +struct ov9281_mode { + u32 width; + u32 height; + u32 max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + const struct regval *reg_list; +}; + +struct ov9281 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; + struct regulator_bulk_data supplies[OV9281_NUM_SUPPLIES]; + + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_sleep; + + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *anal_gain; + struct v4l2_ctrl *digi_gain; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *test_pattern; + struct mutex mutex; + bool streaming; + bool power_on; + const struct ov9281_mode *cur_mode; + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; +}; + +#define to_ov9281(sd) container_of(sd, struct ov9281, subdev) + +/* + * Xclk 24Mhz + */ +static const struct regval ov9281_global_regs[] = { + {REG_NULL, 0x00}, +}; + +/* + * Xclk 24Mhz + * max_framerate 120fps + * mipi_datarate per lane 800Mbps + */ +static const struct regval ov9281_1280x800_regs[] = { + {0x0103, 0x01}, + {0x0302, 0x32}, + {0x030d, 0x50}, + {0x030e, 0x02}, + {0x3001, 0x00}, + {0x3004, 0x00}, + {0x3005, 0x00}, + {0x3006, 0x04}, + {0x3011, 0x0a}, + {0x3013, 0x18}, + {0x3022, 0x01}, + {0x3023, 0x00}, + {0x302c, 0x00}, + {0x302f, 0x00}, + {0x3030, 0x04}, + {0x3039, 0x32}, + {0x303a, 0x00}, + {0x303f, 0x01}, + {0x3500, 0x00}, + {0x3501, 0x2a}, + {0x3502, 0x90}, + {0x3503, 0x08}, + {0x3505, 0x8c}, + {0x3507, 0x03}, + {0x3508, 0x00}, + {0x3509, 0x10}, + {0x3610, 0x80}, + {0x3611, 0xa0}, + {0x3620, 0x6f}, + {0x3632, 0x56}, + {0x3633, 0x78}, + {0x3662, 0x05}, + {0x3666, 0x00}, + {0x366f, 0x5a}, + {0x3680, 0x84}, + {0x3712, 0x80}, + {0x372d, 0x22}, + {0x3731, 0x80}, + {0x3732, 0x30}, + {0x3778, 0x00}, + {0x377d, 0x22}, + {0x3788, 0x02}, + {0x3789, 0xa4}, + {0x378a, 0x00}, + {0x378b, 0x4a}, + {0x3799, 0x20}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x00}, + {0x3804, 0x05}, + {0x3805, 0x0f}, + {0x3806, 0x03}, + {0x3807, 0x2f}, + {0x3808, 0x05}, + {0x3809, 0x00}, + {0x380a, 0x03}, + {0x380b, 0x20}, + {0x380c, 0x02}, + {0x380d, 0xd8}, + {0x380e, 0x03}, + {0x380f, 0x8e}, + {0x3810, 0x00}, + {0x3811, 0x08}, + {0x3812, 0x00}, + {0x3813, 0x08}, + {0x3814, 0x11}, + {0x3815, 0x11}, + {0x3820, 0x40}, + {0x3821, 0x00}, + {0x3881, 0x42}, + {0x38b1, 0x00}, + {0x3920, 0xff}, + {0x4003, 0x40}, + {0x4008, 0x04}, + {0x4009, 0x0b}, + {0x400c, 0x00}, + {0x400d, 0x07}, + {0x4010, 0x40}, + {0x4043, 0x40}, + {0x4307, 0x30}, + {0x4317, 0x00}, + {0x4501, 0x00}, + {0x4507, 0x00}, + {0x4509, 0x00}, + {0x450a, 0x08}, + {0x4601, 0x04}, + {0x470f, 0x00}, + {0x4f07, 0x00}, + {0x4800, 0x00}, + {0x5000, 0x9f}, + {0x5001, 0x00}, + {0x5e00, 0x00}, + {0x5d00, 0x07}, + {0x5d01, 0x00}, + {REG_NULL, 0x00}, +}; + +static const struct ov9281_mode supported_modes[] = { + { + .width = 1280, + .height = 800, + .max_fps = 120, + .exp_def = 0x0320, + .hts_def = 0x0b60,//0x2d8*4 + .vts_def = 0x038e, + .reg_list = ov9281_1280x800_regs, + }, +}; + +static const s64 link_freq_menu_items[] = { + OV9281_LINK_FREQ_400MHZ +}; + +static const char * const ov9281_test_pattern_menu[] = { + "Disabled", + "Vertical Color Bar Type 1", + "Vertical Color Bar Type 2", + "Vertical Color Bar Type 3", + "Vertical Color Bar Type 4" +}; + +/* Write registers up to 4 at a time */ +static int ov9281_write_reg(struct i2c_client *client, u16 reg, + u32 len, u32 val) +{ + u32 buf_i, val_i; + u8 buf[6]; + u8 *val_p; + __be32 val_be; + + if (len > 4) + return -EINVAL; + + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + + val_be = cpu_to_be32(val); + val_p = (u8 *)&val_be; + buf_i = 2; + val_i = 4 - len; + + while (val_i < 4) + buf[buf_i++] = val_p[val_i++]; + + if (i2c_master_send(client, buf, len + 2) != len + 2) + return -EIO; + + return 0; +} + +static int ov9281_write_array(struct i2c_client *client, + const struct regval *regs) +{ + u32 i; + int ret = 0; + + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) + ret = ov9281_write_reg(client, regs[i].addr, + OV9281_REG_VALUE_08BIT, regs[i].val); + + return ret; +} + +/* Read registers up to 4 at a time */ +static int ov9281_read_reg(struct i2c_client *client, u16 reg, unsigned int len, + u32 *val) +{ + struct i2c_msg msgs[2]; + u8 *data_be_p; + __be32 data_be = 0; + __be16 reg_addr_be = cpu_to_be16(reg); + int ret; + + if (len > 4 || !len) + return -EINVAL; + + data_be_p = (u8 *)&data_be; + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 2; + msgs[0].buf = (u8 *)®_addr_be; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_be_p[4 - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = be32_to_cpu(data_be); + + return 0; +} + +static int ov9281_get_reso_dist(const struct ov9281_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); +} + +static const struct ov9281_mode * +ov9281_find_best_fit(struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *framefmt = &fmt->format; + int dist; + int cur_best_fit = 0; + int cur_best_fit_dist = -1; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { + dist = ov9281_get_reso_dist(&supported_modes[i], framefmt); + if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { + cur_best_fit_dist = dist; + cur_best_fit = i; + } + } + + return &supported_modes[cur_best_fit]; +} + +static int ov9281_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ov9281 *ov9281 = to_ov9281(sd); + const struct ov9281_mode *mode; + s64 h_blank, vblank_def; + + mutex_lock(&ov9281->mutex); + + mode = ov9281_find_best_fit(fmt); + fmt->format.code = MEDIA_BUS_FMT_Y10_1X10; + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; +#else + mutex_unlock(&ov9281->mutex); + return -ENOTTY; +#endif + } else { + ov9281->cur_mode = mode; + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(ov9281->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(ov9281->vblank, vblank_def, + OV9281_VTS_MAX - mode->height, + 1, vblank_def); + } + + mutex_unlock(&ov9281->mutex); + + return 0; +} + +static int ov9281_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ov9281 *ov9281 = to_ov9281(sd); + const struct ov9281_mode *mode = ov9281->cur_mode; + + mutex_lock(&ov9281->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); +#else + mutex_unlock(&ov9281->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = MEDIA_BUS_FMT_Y10_1X10; + fmt->format.field = V4L2_FIELD_NONE; + } + mutex_unlock(&ov9281->mutex); + + return 0; +} + +static int ov9281_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index != 0) + return -EINVAL; + code->code = MEDIA_BUS_FMT_Y10_1X10; + + return 0; +} + +static int ov9281_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fse->code != MEDIA_BUS_FMT_Y10_1X10) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = supported_modes[fse->index].width; + fse->max_height = supported_modes[fse->index].height; + fse->min_height = supported_modes[fse->index].height; + + return 0; +} + +static int ov9281_enable_test_pattern(struct ov9281 *ov9281, u32 pattern) +{ + u32 val; + + if (pattern) + val = (pattern - 1) | OV9281_TEST_PATTERN_ENABLE; + else + val = OV9281_TEST_PATTERN_DISABLE; + + return ov9281_write_reg(ov9281->client, OV9281_REG_TEST_PATTERN, + OV9281_REG_VALUE_08BIT, val); +} + +static int OV9281_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct ov9281 *ov9281 = to_ov9281(sd); + const struct ov9281_mode *mode = ov9281->cur_mode; + + mutex_lock(&ov9281->mutex); + fi->interval.numerator = 10000; + fi->interval.denominator = mode->max_fps * 10000; + mutex_unlock(&ov9281->mutex); + + return 0; +} + +static void ov9281_get_module_inf(struct ov9281 *ov9281, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strlcpy(inf->base.sensor, OV9281_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, ov9281->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, ov9281->len_name, sizeof(inf->base.lens)); +} + +static long ov9281_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct ov9281 *ov9281 = to_ov9281(sd); + long ret = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + ov9281_get_module_inf(ov9281, (struct rkmodule_inf *)arg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long ov9281_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_awb_cfg *cfg; + long ret; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = ov9281_ioctl(sd, cmd, inf); + if (!ret) + ret = copy_to_user(up, inf, sizeof(*inf)); + kfree(inf); + break; + case RKMODULE_AWB_CFG: + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(cfg, up, sizeof(*cfg)); + if (!ret) + ret = ov9281_ioctl(sd, cmd, cfg); + kfree(cfg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + +static int __ov9281_start_stream(struct ov9281 *ov9281) +{ + int ret; + + ret = ov9281_write_array(ov9281->client, ov9281->cur_mode->reg_list); + if (ret) + return ret; + + /* In case these controls are set before streaming */ + mutex_unlock(&ov9281->mutex); + ret = v4l2_ctrl_handler_setup(&ov9281->ctrl_handler); + mutex_lock(&ov9281->mutex); + if (ret) + return ret; + + return ov9281_write_reg(ov9281->client, OV9281_REG_CTRL_MODE, + OV9281_REG_VALUE_08BIT, OV9281_MODE_STREAMING); +} + +static int __ov9281_stop_stream(struct ov9281 *ov9281) +{ + return ov9281_write_reg(ov9281->client, OV9281_REG_CTRL_MODE, + OV9281_REG_VALUE_08BIT, OV9281_MODE_SW_STANDBY); +} + +static int ov9281_s_stream(struct v4l2_subdev *sd, int on) +{ + struct ov9281 *ov9281 = to_ov9281(sd); + struct i2c_client *client = ov9281->client; + int ret = 0; + + mutex_lock(&ov9281->mutex); + on = !!on; + if (on == ov9281->streaming) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = __ov9281_start_stream(ov9281); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + __ov9281_stop_stream(ov9281); + pm_runtime_put(&client->dev); + } + + ov9281->streaming = on; + +unlock_and_return: + mutex_unlock(&ov9281->mutex); + + return ret; +} + +static int ov9281_s_power(struct v4l2_subdev *sd, int on) +{ + struct ov9281 *ov9281 = to_ov9281(sd); + struct i2c_client *client = ov9281->client; + int ret = 0; + + mutex_lock(&ov9281->mutex); + + /* If the power state is not modified - no work to do. */ + if (ov9281->power_on == !!on) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + ret = ov9281_write_array(ov9281->client, ov9281_global_regs); + if (ret) { + v4l2_err(sd, "could not set init registers\n"); + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + ov9281->power_on = true; + } else { + pm_runtime_put(&client->dev); + ov9281->power_on = false; + } + +unlock_and_return: + mutex_unlock(&ov9281->mutex); + + return ret; +} + +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 ov9281_cal_delay(u32 cycles) +{ + return DIV_ROUND_UP(cycles, OV9281_XVCLK_FREQ / 1000 / 1000); +} + +static int __ov9281_power_on(struct ov9281 *ov9281) +{ + int ret; + u32 delay_us; + struct device *dev = &ov9281->client->dev; + + if (!IS_ERR_OR_NULL(ov9281->pins_default)) { + ret = pinctrl_select_state(ov9281->pinctrl, + ov9281->pins_default); + if (ret < 0) + dev_err(dev, "could not set pins\n"); + } + + ret = clk_set_rate(ov9281->xvclk, OV9281_XVCLK_FREQ); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate (24MHz)\n"); + if (clk_get_rate(ov9281->xvclk) != OV9281_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); + ret = clk_prepare_enable(ov9281->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + + if (!IS_ERR(ov9281->reset_gpio)) + gpiod_set_value_cansleep(ov9281->reset_gpio, 0); + + ret = regulator_bulk_enable(OV9281_NUM_SUPPLIES, ov9281->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + + if (!IS_ERR(ov9281->reset_gpio)) + gpiod_set_value_cansleep(ov9281->reset_gpio, 1); + + usleep_range(500, 1000); + if (!IS_ERR(ov9281->pwdn_gpio)) + gpiod_set_value_cansleep(ov9281->pwdn_gpio, 1); + + /* 8192 cycles prior to first SCCB transaction */ + delay_us = ov9281_cal_delay(8192); + usleep_range(delay_us, delay_us * 2); + + return 0; + +disable_clk: + clk_disable_unprepare(ov9281->xvclk); + + return ret; +} + +static void __ov9281_power_off(struct ov9281 *ov9281) +{ + int ret; + struct device *dev = &ov9281->client->dev; + + if (!IS_ERR(ov9281->pwdn_gpio)) + gpiod_set_value_cansleep(ov9281->pwdn_gpio, 0); + clk_disable_unprepare(ov9281->xvclk); + if (!IS_ERR(ov9281->reset_gpio)) + gpiod_set_value_cansleep(ov9281->reset_gpio, 0); + if (!IS_ERR_OR_NULL(ov9281->pins_sleep)) { + ret = pinctrl_select_state(ov9281->pinctrl, + ov9281->pins_sleep); + if (ret < 0) + dev_dbg(dev, "could not set pins\n"); + } + regulator_bulk_disable(OV9281_NUM_SUPPLIES, ov9281->supplies); +} + +static int ov9281_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov9281 *ov9281 = to_ov9281(sd); + + return __ov9281_power_on(ov9281); +} + +static int ov9281_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov9281 *ov9281 = to_ov9281(sd); + + __ov9281_power_off(ov9281); + + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int ov9281_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct ov9281 *ov9281 = to_ov9281(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + const struct ov9281_mode *def_mode = &supported_modes[0]; + + mutex_lock(&ov9281->mutex); + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = MEDIA_BUS_FMT_Y10_1X10; + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&ov9281->mutex); + /* No crop or compose */ + + return 0; +} +#endif + +static const struct dev_pm_ops ov9281_pm_ops = { + SET_RUNTIME_PM_OPS(ov9281_runtime_suspend, + ov9281_runtime_resume, NULL) +}; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops ov9281_internal_ops = { + .open = ov9281_open, +}; +#endif + +static const struct v4l2_subdev_core_ops ov9281_core_ops = { + .s_power = ov9281_s_power, + .ioctl = ov9281_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = ov9281_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops ov9281_video_ops = { + .s_stream = ov9281_s_stream, + .g_frame_interval = OV9281_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops ov9281_pad_ops = { + .enum_mbus_code = ov9281_enum_mbus_code, + .enum_frame_size = ov9281_enum_frame_sizes, + .get_fmt = ov9281_get_fmt, + .set_fmt = ov9281_set_fmt, +}; + +static const struct v4l2_subdev_ops ov9281_subdev_ops = { + .core = &ov9281_core_ops, + .video = &ov9281_video_ops, + .pad = &ov9281_pad_ops, +}; + +static int ov9281_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov9281 *ov9281 = container_of(ctrl->handler, + struct ov9281, ctrl_handler); + struct i2c_client *client = ov9281->client; + s64 max; + int ret = 0; + + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ + max = ov9281->cur_mode->height + ctrl->val - 4; + __v4l2_ctrl_modify_range(ov9281->exposure, + ov9281->exposure->minimum, max, + ov9281->exposure->step, + ov9281->exposure->default_value); + break; + } + + if (pm_runtime_get(&client->dev) <= 0) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + /* 4 least significant bits of expsoure are fractional part */ + ret = ov9281_write_reg(ov9281->client, OV9281_REG_EXPOSURE, + OV9281_REG_VALUE_24BIT, ctrl->val << 4); + break; + case V4L2_CID_ANALOGUE_GAIN: + ret = ov9281_write_reg(ov9281->client, OV9281_REG_GAIN_H, + OV9281_REG_VALUE_08BIT, + (ctrl->val >> OV9281_GAIN_H_SHIFT) & OV9281_GAIN_H_MASK); + ret |= ov9281_write_reg(ov9281->client, OV9281_REG_GAIN_L, + OV9281_REG_VALUE_08BIT, + ctrl->val & OV9281_GAIN_L_MASK); + break; + case V4L2_CID_VBLANK: + ret = ov9281_write_reg(ov9281->client, OV9281_REG_VTS, + OV9281_REG_VALUE_16BIT, + ctrl->val + ov9281->cur_mode->height); + break; + case V4L2_CID_TEST_PATTERN: + ret = ov9281_enable_test_pattern(ov9281, ctrl->val); + break; + default: + dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", + __func__, ctrl->id, ctrl->val); + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops ov9281_ctrl_ops = { + .s_ctrl = ov9281_set_ctrl, +}; + +static int ov9281_initialize_controls(struct ov9281 *ov9281) +{ + const struct ov9281_mode *mode; + struct v4l2_ctrl_handler *handler; + struct v4l2_ctrl *ctrl; + s64 exposure_max, vblank_def; + u32 h_blank; + int ret; + + handler = &ov9281->ctrl_handler; + mode = ov9281->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 8); + if (ret) + return ret; + handler->lock = &ov9281->mutex; + + ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, + 0, 0, link_freq_menu_items); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, + 0, OV9281_PIXEL_RATE, 1, OV9281_PIXEL_RATE); + + h_blank = mode->hts_def - mode->width; + ov9281->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (ov9281->hblank) + ov9281->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = mode->vts_def - mode->height; + ov9281->vblank = v4l2_ctrl_new_std(handler, &ov9281_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + OV9281_VTS_MAX - mode->height, + 1, vblank_def); + + exposure_max = mode->vts_def - 4; + ov9281->exposure = v4l2_ctrl_new_std(handler, &ov9281_ctrl_ops, + V4L2_CID_EXPOSURE, OV9281_EXPOSURE_MIN, + exposure_max, OV9281_EXPOSURE_STEP, + mode->exp_def); + + ov9281->anal_gain = v4l2_ctrl_new_std(handler, &ov9281_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, OV9281_GAIN_MIN, + OV9281_GAIN_MAX, OV9281_GAIN_STEP, + OV9281_GAIN_DEFAULT); + + ov9281->test_pattern = v4l2_ctrl_new_std_menu_items(handler, + &ov9281_ctrl_ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov9281_test_pattern_menu) - 1, + 0, 0, ov9281_test_pattern_menu); + + if (handler->error) { + ret = handler->error; + dev_err(&ov9281->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + ov9281->subdev.ctrl_handler = handler; + + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +static int ov9281_check_sensor_id(struct ov9281 *ov9281, + struct i2c_client *client) +{ + struct device *dev = &ov9281->client->dev; + u32 id = 0; + int ret; + + ret = ov9281_read_reg(client, OV9281_REG_CHIP_ID, + OV9281_REG_VALUE_16BIT, &id); + if (id != CHIP_ID) { + dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret); + return -ENODEV; + } + + dev_info(dev, "Detected OV%06x sensor\n", CHIP_ID); + + return 0; +} + +static int ov9281_configure_regulators(struct ov9281 *ov9281) +{ + unsigned int i; + + for (i = 0; i < OV9281_NUM_SUPPLIES; i++) + ov9281->supplies[i].supply = ov9281_supply_names[i]; + + return devm_regulator_bulk_get(&ov9281->client->dev, + OV9281_NUM_SUPPLIES, + ov9281->supplies); +} + +static int ov9281_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct ov9281 *ov9281; + struct v4l2_subdev *sd; + char facing[2]; + int ret; + + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + ov9281 = devm_kzalloc(dev, sizeof(*ov9281), GFP_KERNEL); + if (!ov9281) + return -ENOMEM; + + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &ov9281->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &ov9281->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &ov9281->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &ov9281->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + + ov9281->client = client; + ov9281->cur_mode = &supported_modes[0]; + + ov9281->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(ov9281->xvclk)) { + dev_err(dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + ov9281->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ov9281->reset_gpio)) + dev_warn(dev, "Failed to get reset-gpios\n"); + + ov9281->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(ov9281->pwdn_gpio)) + dev_warn(dev, "Failed to get pwdn-gpios\n"); + + ov9281->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR(ov9281->pinctrl)) { + ov9281->pins_default = + pinctrl_lookup_state(ov9281->pinctrl, + OF_CAMERA_PINCTRL_STATE_DEFAULT); + if (IS_ERR(ov9281->pins_default)) + dev_err(dev, "could not get default pinstate\n"); + + ov9281->pins_sleep = + pinctrl_lookup_state(ov9281->pinctrl, + OF_CAMERA_PINCTRL_STATE_SLEEP); + if (IS_ERR(ov9281->pins_sleep)) + dev_err(dev, "could not get sleep pinstate\n"); + } else { + dev_err(dev, "no pinctrl\n"); + } + + ret = ov9281_configure_regulators(ov9281); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + + mutex_init(&ov9281->mutex); + + sd = &ov9281->subdev; + v4l2_i2c_subdev_init(sd, client, &ov9281_subdev_ops); + ret = ov9281_initialize_controls(ov9281); + if (ret) + goto err_destroy_mutex; + + ret = __ov9281_power_on(ov9281); + if (ret) + goto err_free_handler; + + ret = ov9281_check_sensor_id(ov9281, client); + if (ret) + goto err_power_off; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &ov9281_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + ov9281->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &ov9281->pad); + if (ret < 0) + goto err_power_off; +#endif + + memset(facing, 0, sizeof(facing)); + if (strcmp(ov9281->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + ov9281->module_index, facing, + OV9281_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor_common(sd); + if (ret) { + dev_err(dev, "v4l2 async register subdev failed\n"); + goto err_clean_entity; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; + +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif +err_power_off: + __ov9281_power_off(ov9281); +err_free_handler: + v4l2_ctrl_handler_free(&ov9281->ctrl_handler); +err_destroy_mutex: + mutex_destroy(&ov9281->mutex); + + return ret; +} + +static int ov9281_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov9281 *ov9281 = to_ov9281(sd); + + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + v4l2_ctrl_handler_free(&ov9281->ctrl_handler); + mutex_destroy(&ov9281->mutex); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __ov9281_power_off(ov9281); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id ov9281_of_match[] = { + { .compatible = "ovti,ov9281" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ov9281_of_match); +#endif + +static const struct i2c_device_id ov9281_match_id[] = { + { "ovti,ov9281", 0 }, + { }, +}; + +static struct i2c_driver ov9281_i2c_driver = { + .driver = { + .name = OV9281_NAME, + .pm = &ov9281_pm_ops, + .of_match_table = of_match_ptr(ov9281_of_match), + }, + .probe = &ov9281_probe, + .remove = &ov9281_remove, + .id_table = ov9281_match_id, +}; + +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&ov9281_i2c_driver); +} + +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&ov9281_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_DESCRIPTION("OmniVision ov9281 sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ov9750.c b/drivers/media/i2c/ov9750.c new file mode 100644 index 000000000000..c7e61a5e4d76 --- /dev/null +++ b/drivers/media/i2c/ov9750.c @@ -0,0 +1,1293 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ov9750 driver + * + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd. + * V0.0X01.0X02 fix mclk issue when probe multiple camera. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x2) + +#ifndef V4L2_CID_DIGITAL_GAIN +#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN +#endif + +#define OV9750_LINK_FREQ_400MHZ 400000000 +/* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ +#define OV9750_PIXEL_RATE (OV9750_LINK_FREQ_400MHZ * 2 * 2 / 10) +#define OV9750_XVCLK_FREQ 24000000 + +#define CHIP_ID 0x9750 +#define OV9750_REG_CHIP_ID 0x300B + +#define OV9750_REG_CTRL_MODE 0x0100 +#define OV9750_MODE_SW_STANDBY 0x0 +#define OV9750_MODE_STREAMING BIT(0) + +#define OV9750_REG_EXPOSURE 0x3500 +#define OV9750_EXPOSURE_MIN 4 +#define OV9750_EXPOSURE_STEP 1 +#define OV9750_VTS_MAX 0x7fff + +#define OV9750_REG_GAIN_H 0x3508 +#define OV9750_REG_GAIN_L 0x3509 +#define OV9750_GAIN_H_MASK 0x1f +#define OV9750_GAIN_L_MASK 0xff +#define OV9750_GAIN_MIN 0x0080 +#define OV9750_GAIN_MAX 0x1000 +#define OV9750_GAIN_STEP 1 +#define OV9750_GAIN_DEFAULT 0x0080 + +#define OV9750_REG_TEST_PATTERN 0x5e00 +#define OV9750_TEST_PATTERN_ENABLE 0x80 +#define OV9750_TEST_PATTERN_DISABLE 0x0 + +#define OV9750_REG_VTS 0x380e + +#define REG_NULL 0xFFFF +#define REG_DELAY 0xFFFE + +#define OV9750_REG_VALUE_08BIT 1 +#define OV9750_REG_VALUE_16BIT 2 +#define OV9750_REG_VALUE_24BIT 3 + +#define OV9750_LANES 2 +#define OV9750_BITS_PER_SAMPLE 10 + +#define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default" +#define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep" + +#define OV9750_NAME "ov9750" + +static const char * const ov9750_supply_names[] = { + "avdd", /* Analog power */ + "dovdd", /* Digital I/O power */ + "dvdd", /* Digital core power */ +}; + +#define OV9750_NUM_SUPPLIES ARRAY_SIZE(ov9750_supply_names) + +struct regval { + u16 addr; + u8 val; +}; + +struct ov9750_mode { + u32 width; + u32 height; + struct v4l2_fract max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + const struct regval *reg_list; +}; + +struct ov9750 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; + struct regulator_bulk_data supplies[OV9750_NUM_SUPPLIES]; + + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_sleep; + + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *anal_gain; + struct v4l2_ctrl *digi_gain; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *test_pattern; + struct mutex mutex; + bool streaming; + bool power_on; + const struct ov9750_mode *cur_mode; + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; +}; + +#define to_ov9750(sd) container_of(sd, struct ov9750, subdev) + +/* + * Xclk 24Mhz + */ +static const struct regval ov9750_global_regs[] = { + {0x0103, 0x01}, + {REG_DELAY, 0x10}, + {0x0100, 0x00}, + {REG_DELAY, 0x10}, + {0x0300, 0x04}, + {0x0302, 0x64}, + {0x0303, 0x00}, + {0x0304, 0x03}, + {0x0305, 0x01}, + {0x0306, 0x01}, + {0x030a, 0x00}, + {0x030b, 0x00}, + {0x030d, 0x1e}, + {0x030e, 0x01}, + {0x030f, 0x04}, + {0x0312, 0x01}, + {0x031e, 0x04}, + {0x3000, 0x00}, + {0x3001, 0x00}, + {0x3002, 0x21}, + {0x3005, 0xf0}, + {0x3011, 0x00}, + {0x3016, 0x53}, + {0x3018, 0x32}, + {0x301a, 0xf0}, + {0x301b, 0xf0}, + {0x301c, 0xf0}, + {0x301d, 0xf0}, + {0x301e, 0xf0}, + {0x3022, 0x01}, + {0x3031, 0x0a}, + {0x3032, 0x80}, + {0x303c, 0xff}, + {0x303e, 0xff}, + {0x3040, 0xf0}, + {0x3041, 0x00}, + {0x3042, 0xf0}, + {0x3104, 0x01}, + {0x3106, 0x15}, + {0x3107, 0x01}, + {0x3500, 0x00}, + {0x3501, 0x3d}, + {0x3502, 0x00}, + {0x3503, 0x08}, + {0x3504, 0x03}, + {0x3505, 0x83}, + {0x3508, 0x02}, + {0x3509, 0x80}, + {0x3600, 0x65}, + {0x3601, 0x60}, + {0x3602, 0x22}, + {0x3610, 0xe8}, + {0x3611, 0x56}, + {0x3612, 0x48}, + {0x3613, 0x5a}, + {0x3614, 0x91}, + {0x3615, 0x79}, + {0x3617, 0x57}, + {0x3621, 0x90}, + {0x3622, 0x00}, + {0x3623, 0x00}, + {0x3625, 0x07}, + {0x3633, 0x10}, + {0x3634, 0x10}, + {0x3635, 0x14}, + {0x3636, 0x13}, + {0x3650, 0x00}, + {0x3652, 0xff}, + {0x3654, 0x00}, + {0x3653, 0x34}, + {0x3655, 0x20}, + {0x3656, 0xff}, + {0x3657, 0xc4}, + {0x365a, 0xff}, + {0x365b, 0xff}, + {0x365e, 0xff}, + {0x365f, 0x00}, + {0x3668, 0x00}, + {0x366a, 0x07}, + {0x366d, 0x00}, + {0x366e, 0x10}, + {0x3702, 0x1d}, + {0x3703, 0x10}, + {0x3704, 0x14}, + {0x3705, 0x00}, + {0x3706, 0x27}, + {0x3709, 0x24}, + {0x370a, 0x00}, + {0x370b, 0x7d}, + {0x3714, 0x24}, + {0x371a, 0x5e}, + {0x3730, 0x82}, + {0x3733, 0x10}, + {0x373e, 0x18}, + {0x3755, 0x00}, + {0x3758, 0x00}, + {0x375b, 0x13}, + {0x3772, 0x23}, + {0x3773, 0x05}, + {0x3774, 0x16}, + {0x3775, 0x12}, + {0x3776, 0x08}, + {0x37a8, 0x38}, + {0x37b5, 0x36}, + {0x37c2, 0x04}, + {0x37c5, 0x00}, + {0x37c7, 0x38}, + {0x37c8, 0x00}, + {0x37d1, 0x13}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x04}, + {0x3804, 0x05}, + {0x3805, 0x0f}, + {0x3806, 0x03}, + {0x3807, 0xcb}, + {0x3808, 0x05}, + {0x3809, 0x00}, + {0x380a, 0x03}, + {0x380b, 0xc0}, + {0x380c, 0x03}, + {0x380d, 0x2a}, + {0x380e, 0x03}, + {0x380f, 0xdc}, + {0x3810, 0x00}, + {0x3811, 0x08}, + {0x3812, 0x00}, + {0x3813, 0x04}, + {0x3814, 0x01}, + {0x3815, 0x01}, + {0x3816, 0x00}, + {0x3817, 0x00}, + {0x3818, 0x00}, + {0x3819, 0x00}, + {0x3820, 0x80}, + {0x3821, 0x40}, + {0x3826, 0x00}, + {0x3827, 0x08}, + {0x382a, 0x01}, + {0x382b, 0x01}, + {0x3836, 0x02}, + {0x3838, 0x10}, + {0x3861, 0x00}, + {0x3862, 0x00}, + {0x3863, 0x02}, + {0x3b00, 0x00}, + {0x3c00, 0x89}, + {0x3c01, 0xab}, + {0x3c02, 0x01}, + {0x3c03, 0x00}, + {0x3c04, 0x00}, + {0x3c05, 0x03}, + {0x3c06, 0x00}, + {0x3c07, 0x05}, + {0x3c0c, 0x00}, + {0x3c0d, 0x00}, + {0x3c0e, 0x00}, + {0x3c0f, 0x00}, + {0x3c40, 0x00}, + {0x3c41, 0xa3}, + {0x3c43, 0x7d}, + {0x3c56, 0x80}, + {0x3c80, 0x08}, + {0x3c82, 0x01}, + {0x3c83, 0x61}, + {0x3d85, 0x17}, + {0x3f08, 0x08}, + {0x3f0a, 0x00}, + {0x3f0b, 0x30}, + {0x4000, 0xcd}, + {0x4003, 0x40}, + {0x4009, 0x0d}, + {0x4010, 0xf0}, + {0x4011, 0x70}, + {0x4017, 0x10}, + {0x4040, 0x00}, + {0x4041, 0x00}, + {0x4303, 0x00}, + {0x4307, 0x30}, + {0x4500, 0x30}, + {0x4502, 0x40}, + {0x4503, 0x06}, + {0x4508, 0xaa}, + {0x450b, 0x00}, + {0x450c, 0x00}, + {0x4600, 0x00}, + {0x4601, 0x80}, + {0x4700, 0x04}, + {0x4704, 0x00}, + {0x4705, 0x04}, + {0x4837, 0x14}, + {0x484a, 0x3f}, + {0x5000, 0x10}, + {0x5001, 0x01}, + {0x5002, 0x28}, + {0x5004, 0x0c}, + {0x5006, 0x0c}, + {0x5007, 0xe0}, + {0x5008, 0x01}, + {0x5009, 0xb0}, + {0x502a, 0x18}, + {0x5901, 0x00}, + {0x5a01, 0x00}, + {0x5a03, 0x00}, + {0x5a04, 0x0c}, + {0x5a05, 0xe0}, + {0x5a06, 0x09}, + {0x5a07, 0xb0}, + {0x5a08, 0x06}, + {0x5e00, 0x00}, + {0x5e10, 0xfc}, + {0x300f, 0x00}, + {0x3733, 0x10}, + {0x3610, 0xe8}, + {0x3611, 0x56}, + {0x3635, 0x14}, + {0x3636, 0x13}, + {0x3620, 0x84}, + {0x3614, 0x96}, + {0x481f, 0x30}, + {0x3788, 0x00}, + {0x3789, 0x04}, + {0x378a, 0x01}, + {0x378b, 0x60}, + {0x3799, 0x27}, + {REG_NULL, 0x00}, +}; + +/* + * Xclk 24Mhz + * max_framerate 60fps + * mipi_datarate per lane 800Mbps + */ +static const struct regval ov9750_1280x960_regs[] = { + {REG_NULL, 0x00}, +}; + +static const struct ov9750_mode supported_modes[] = { + { + .width = 1280, + .height = 960, + .max_fps = { + .numerator = 10000, + .denominator = 600000, + }, + .exp_def = 0x03D0, + .hts_def = 0x0654,//0x32A*2 + .vts_def = 0x03DC, + .reg_list = ov9750_1280x960_regs, + }, +}; + +static const s64 link_freq_menu_items[] = { + OV9750_LINK_FREQ_400MHZ +}; + +static const char * const ov9750_test_pattern_menu[] = { + "Disabled", + "Vertical Color Bar Type 1", + "Vertical Color Bar Type 2", + "Vertical Color Bar Type 3", + "Vertical Color Bar Type 4" +}; + +/* Write registers up to 4 at a time */ +static int ov9750_write_reg(struct i2c_client *client, u16 reg, + u32 len, u32 val) +{ + u32 buf_i, val_i; + u8 buf[6]; + u8 *val_p; + __be32 val_be; + + if (len > 4) + return -EINVAL; + + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + + val_be = cpu_to_be32(val); + val_p = (u8 *)&val_be; + buf_i = 2; + val_i = 4 - len; + + while (val_i < 4) + buf[buf_i++] = val_p[val_i++]; + + if (i2c_master_send(client, buf, len + 2) != len + 2) + return -EIO; + + return 0; +} + +static int ov9750_write_array(struct i2c_client *client, + const struct regval *regs) +{ + u32 i; + int ret = 0; + + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) { + if (unlikely(regs[i].addr == REG_DELAY)) + usleep_range(regs[i].val, regs[i].val * 2); + else + ret = ov9750_write_reg(client, regs[i].addr, + OV9750_REG_VALUE_08BIT, regs[i].val); + } + return ret; +} + +/* Read registers up to 4 at a time */ +static int ov9750_read_reg(struct i2c_client *client, u16 reg, unsigned int len, + u32 *val) +{ + struct i2c_msg msgs[2]; + u8 *data_be_p; + __be32 data_be = 0; + __be16 reg_addr_be = cpu_to_be16(reg); + int ret; + + if (len > 4 || !len) + return -EINVAL; + + data_be_p = (u8 *)&data_be; + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 2; + msgs[0].buf = (u8 *)®_addr_be; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_be_p[4 - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = be32_to_cpu(data_be); + + return 0; +} + +static int ov9750_get_reso_dist(const struct ov9750_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); +} + +static const struct ov9750_mode * +ov9750_find_best_fit(struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *framefmt = &fmt->format; + int dist; + int cur_best_fit = 0; + int cur_best_fit_dist = -1; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { + dist = ov9750_get_reso_dist(&supported_modes[i], framefmt); + if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { + cur_best_fit_dist = dist; + cur_best_fit = i; + } + } + + return &supported_modes[cur_best_fit]; +} + +static int ov9750_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ov9750 *ov9750 = to_ov9750(sd); + const struct ov9750_mode *mode; + s64 h_blank, vblank_def; + + mutex_lock(&ov9750->mutex); + + mode = ov9750_find_best_fit(fmt); + fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10; + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; +#else + mutex_unlock(&ov9750->mutex); + return -ENOTTY; +#endif + } else { + ov9750->cur_mode = mode; + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(ov9750->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(ov9750->vblank, vblank_def, + OV9750_VTS_MAX - mode->height, + 1, vblank_def); + } + + mutex_unlock(&ov9750->mutex); + + return 0; +} + +static int ov9750_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ov9750 *ov9750 = to_ov9750(sd); + const struct ov9750_mode *mode = ov9750->cur_mode; + + mutex_lock(&ov9750->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); +#else + mutex_unlock(&ov9750->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10; + fmt->format.field = V4L2_FIELD_NONE; + } + mutex_unlock(&ov9750->mutex); + + return 0; +} + +static int ov9750_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index != 0) + return -EINVAL; + code->code = MEDIA_BUS_FMT_SBGGR10_1X10; + + return 0; +} + +static int ov9750_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fse->code != MEDIA_BUS_FMT_SBGGR10_1X10) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = supported_modes[fse->index].width; + fse->max_height = supported_modes[fse->index].height; + fse->min_height = supported_modes[fse->index].height; + + return 0; +} + +static int ov9750_enable_test_pattern(struct ov9750 *ov9750, u32 pattern) +{ + u32 val; + + if (pattern) + val = ((pattern - 1) < 2) | OV9750_TEST_PATTERN_ENABLE; + else + val = OV9750_TEST_PATTERN_DISABLE; + + return ov9750_write_reg(ov9750->client, OV9750_REG_TEST_PATTERN, + OV9750_REG_VALUE_08BIT, val); +} + +static int OV9750_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct ov9750 *ov9750 = to_ov9750(sd); + const struct ov9750_mode *mode = ov9750->cur_mode; + + mutex_lock(&ov9750->mutex); + fi->interval = mode->max_fps; + mutex_unlock(&ov9750->mutex); + + return 0; +} + +static void ov9750_get_module_inf(struct ov9750 *ov9750, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strlcpy(inf->base.sensor, OV9750_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, ov9750->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, ov9750->len_name, sizeof(inf->base.lens)); +} + +static long ov9750_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct ov9750 *ov9750 = to_ov9750(sd); + long ret = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + ov9750_get_module_inf(ov9750, (struct rkmodule_inf *)arg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long ov9750_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + long ret; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + ret = ov9750_ioctl(sd, cmd, inf); + if (!ret) + ret = copy_to_user(up, inf, sizeof(*inf)); + kfree(inf); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + +static int __ov9750_start_stream(struct ov9750 *ov9750) +{ + int ret; + + ret = ov9750_write_array(ov9750->client, ov9750->cur_mode->reg_list); + if (ret) + return ret; + + /* In case these controls are set before streaming */ + mutex_unlock(&ov9750->mutex); + ret = v4l2_ctrl_handler_setup(&ov9750->ctrl_handler); + mutex_lock(&ov9750->mutex); + if (ret) + return ret; + + return ov9750_write_reg(ov9750->client, OV9750_REG_CTRL_MODE, + OV9750_REG_VALUE_08BIT, OV9750_MODE_STREAMING); +} + +static int __ov9750_stop_stream(struct ov9750 *ov9750) +{ + return ov9750_write_reg(ov9750->client, OV9750_REG_CTRL_MODE, + OV9750_REG_VALUE_08BIT, OV9750_MODE_SW_STANDBY); +} + +static int ov9750_s_stream(struct v4l2_subdev *sd, int on) +{ + struct ov9750 *ov9750 = to_ov9750(sd); + struct i2c_client *client = ov9750->client; + int ret = 0; + + mutex_lock(&ov9750->mutex); + on = !!on; + if (on == ov9750->streaming) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = __ov9750_start_stream(ov9750); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + __ov9750_stop_stream(ov9750); + pm_runtime_put(&client->dev); + } + + ov9750->streaming = on; + +unlock_and_return: + mutex_unlock(&ov9750->mutex); + + return ret; +} + +static int ov9750_s_power(struct v4l2_subdev *sd, int on) +{ + struct ov9750 *ov9750 = to_ov9750(sd); + struct i2c_client *client = ov9750->client; + int ret = 0; + + mutex_lock(&ov9750->mutex); + + /* If the power state is not modified - no work to do. */ + if (ov9750->power_on == !!on) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + ret = ov9750_write_array(ov9750->client, ov9750_global_regs); + if (ret) { + v4l2_err(sd, "could not set init registers\n"); + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + ov9750->power_on = true; + } else { + pm_runtime_put(&client->dev); + ov9750->power_on = false; + } + +unlock_and_return: + mutex_unlock(&ov9750->mutex); + + return ret; +} + +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 ov9750_cal_delay(u32 cycles) +{ + return DIV_ROUND_UP(cycles, OV9750_XVCLK_FREQ / 1000 / 1000); +} + +static int __ov9750_power_on(struct ov9750 *ov9750) +{ + int ret; + u32 delay_us; + struct device *dev = &ov9750->client->dev; + + if (!IS_ERR_OR_NULL(ov9750->pins_default)) { + ret = pinctrl_select_state(ov9750->pinctrl, + ov9750->pins_default); + if (ret < 0) + dev_err(dev, "could not set pins\n"); + } + + ret = clk_set_rate(ov9750->xvclk, OV9750_XVCLK_FREQ); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate (24MHz)\n"); + if (clk_get_rate(ov9750->xvclk) != OV9750_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); + ret = clk_prepare_enable(ov9750->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + + if (!IS_ERR(ov9750->reset_gpio)) + gpiod_set_value_cansleep(ov9750->reset_gpio, 0); + + ret = regulator_bulk_enable(OV9750_NUM_SUPPLIES, ov9750->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + + if (!IS_ERR(ov9750->reset_gpio)) + gpiod_set_value_cansleep(ov9750->reset_gpio, 1); + + usleep_range(500, 1000); + if (!IS_ERR(ov9750->pwdn_gpio)) + gpiod_set_value_cansleep(ov9750->pwdn_gpio, 1); + + /* 8192 cycles prior to first SCCB transaction */ + delay_us = ov9750_cal_delay(8192); + usleep_range(delay_us, delay_us * 2); + + return 0; + +disable_clk: + clk_disable_unprepare(ov9750->xvclk); + + return ret; +} + +static void __ov9750_power_off(struct ov9750 *ov9750) +{ + int ret; + + if (!IS_ERR(ov9750->pwdn_gpio)) + gpiod_set_value_cansleep(ov9750->pwdn_gpio, 0); + clk_disable_unprepare(ov9750->xvclk); + if (!IS_ERR(ov9750->reset_gpio)) + gpiod_set_value_cansleep(ov9750->reset_gpio, 0); + if (!IS_ERR_OR_NULL(ov9750->pins_sleep)) { + ret = pinctrl_select_state(ov9750->pinctrl, + ov9750->pins_sleep); + if (ret < 0) + dev_dbg(&ov9750->client->dev, "could not set pins\n"); + } + regulator_bulk_disable(OV9750_NUM_SUPPLIES, ov9750->supplies); +} + +static int ov9750_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov9750 *ov9750 = to_ov9750(sd); + + return __ov9750_power_on(ov9750); +} + +static int ov9750_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov9750 *ov9750 = to_ov9750(sd); + + __ov9750_power_off(ov9750); + + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int ov9750_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct ov9750 *ov9750 = to_ov9750(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + const struct ov9750_mode *def_mode = &supported_modes[0]; + + mutex_lock(&ov9750->mutex); + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10; + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&ov9750->mutex); + /* No crop or compose */ + + return 0; +} +#endif + +static const struct dev_pm_ops ov9750_pm_ops = { + SET_RUNTIME_PM_OPS(ov9750_runtime_suspend, + ov9750_runtime_resume, NULL) +}; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops ov9750_internal_ops = { + .open = ov9750_open, +}; +#endif + +static const struct v4l2_subdev_core_ops ov9750_core_ops = { + .s_power = ov9750_s_power, + .ioctl = ov9750_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = ov9750_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops ov9750_video_ops = { + .s_stream = ov9750_s_stream, + .g_frame_interval = OV9750_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops ov9750_pad_ops = { + .enum_mbus_code = ov9750_enum_mbus_code, + .enum_frame_size = ov9750_enum_frame_sizes, + .get_fmt = ov9750_get_fmt, + .set_fmt = ov9750_set_fmt, +}; + +static const struct v4l2_subdev_ops ov9750_subdev_ops = { + .core = &ov9750_core_ops, + .video = &ov9750_video_ops, + .pad = &ov9750_pad_ops, +}; + +static int ov9750_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov9750 *ov9750 = container_of(ctrl->handler, + struct ov9750, ctrl_handler); + struct i2c_client *client = ov9750->client; + s64 max; + int ret = 0; + + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ + max = ov9750->cur_mode->height + ctrl->val - 4; + __v4l2_ctrl_modify_range(ov9750->exposure, + ov9750->exposure->minimum, max, + ov9750->exposure->step, + ov9750->exposure->default_value); + break; + } + + if (pm_runtime_get(&client->dev) <= 0) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + /* 4 least significant bits of expsoure are fractional part */ + ret = ov9750_write_reg(ov9750->client, OV9750_REG_EXPOSURE, + OV9750_REG_VALUE_24BIT, ctrl->val << 4); + break; + case V4L2_CID_ANALOGUE_GAIN: + ret = ov9750_write_reg(ov9750->client, OV9750_REG_GAIN_H, + OV9750_REG_VALUE_08BIT, + (ctrl->val >> 8) & OV9750_GAIN_H_MASK); + ret |= ov9750_write_reg(ov9750->client, OV9750_REG_GAIN_L, + OV9750_REG_VALUE_08BIT, + ctrl->val & OV9750_GAIN_L_MASK); + break; + case V4L2_CID_VBLANK: + ret = ov9750_write_reg(ov9750->client, OV9750_REG_VTS, + OV9750_REG_VALUE_16BIT, + ctrl->val + ov9750->cur_mode->height); + break; + case V4L2_CID_TEST_PATTERN: + ret = ov9750_enable_test_pattern(ov9750, ctrl->val); + break; + default: + dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", + __func__, ctrl->id, ctrl->val); + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops ov9750_ctrl_ops = { + .s_ctrl = ov9750_set_ctrl, +}; + +static int ov9750_initialize_controls(struct ov9750 *ov9750) +{ + const struct ov9750_mode *mode; + struct v4l2_ctrl_handler *handler; + struct v4l2_ctrl *ctrl; + s64 exposure_max, vblank_def; + u32 h_blank; + int ret; + + handler = &ov9750->ctrl_handler; + mode = ov9750->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 8); + if (ret) + return ret; + handler->lock = &ov9750->mutex; + + ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, + 0, 0, link_freq_menu_items); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, + 0, OV9750_PIXEL_RATE, 1, OV9750_PIXEL_RATE); + + h_blank = mode->hts_def - mode->width; + ov9750->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (ov9750->hblank) + ov9750->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = mode->vts_def - mode->height; + ov9750->vblank = v4l2_ctrl_new_std(handler, &ov9750_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + OV9750_VTS_MAX - mode->height, + 1, vblank_def); + + exposure_max = mode->vts_def - 4; + ov9750->exposure = v4l2_ctrl_new_std(handler, &ov9750_ctrl_ops, + V4L2_CID_EXPOSURE, OV9750_EXPOSURE_MIN, + exposure_max, OV9750_EXPOSURE_STEP, + mode->exp_def); + + ov9750->anal_gain = v4l2_ctrl_new_std(handler, &ov9750_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, OV9750_GAIN_MIN, + OV9750_GAIN_MAX, OV9750_GAIN_STEP, + OV9750_GAIN_DEFAULT); + + ov9750->test_pattern = v4l2_ctrl_new_std_menu_items(handler, + &ov9750_ctrl_ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov9750_test_pattern_menu) - 1, + 0, 0, ov9750_test_pattern_menu); + + if (handler->error) { + ret = handler->error; + dev_err(&ov9750->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + ov9750->subdev.ctrl_handler = handler; + + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +static int ov9750_check_sensor_id(struct ov9750 *ov9750, + struct i2c_client *client) +{ + struct device *dev = &ov9750->client->dev; + u32 id = 0; + int ret; + + ret = ov9750_read_reg(client, OV9750_REG_CHIP_ID, + OV9750_REG_VALUE_16BIT, &id); + if (id != CHIP_ID) { + dev_err(dev, "Unexpected sensor id(%04x), ret(%d)\n", id, ret); + return -ENODEV; + } + + dev_info(dev, "Detected OV%04x sensor\n", id); + + return 0; +} + +static int ov9750_configure_regulators(struct ov9750 *ov9750) +{ + unsigned int i; + + for (i = 0; i < OV9750_NUM_SUPPLIES; i++) + ov9750->supplies[i].supply = ov9750_supply_names[i]; + + return devm_regulator_bulk_get(&ov9750->client->dev, + OV9750_NUM_SUPPLIES, + ov9750->supplies); +} + +static int ov9750_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct ov9750 *ov9750; + struct v4l2_subdev *sd; + char facing[2]; + int ret; + + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + ov9750 = devm_kzalloc(dev, sizeof(*ov9750), GFP_KERNEL); + if (!ov9750) + return -ENOMEM; + + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &ov9750->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &ov9750->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &ov9750->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &ov9750->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + + ov9750->client = client; + ov9750->cur_mode = &supported_modes[0]; + + ov9750->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(ov9750->xvclk)) { + dev_err(dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + ov9750->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ov9750->reset_gpio)) + dev_warn(dev, "Failed to get reset-gpios\n"); + + ov9750->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(ov9750->pwdn_gpio)) + dev_warn(dev, "Failed to get pwdn-gpios\n"); + + ov9750->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR(ov9750->pinctrl)) { + ov9750->pins_default = + pinctrl_lookup_state(ov9750->pinctrl, + OF_CAMERA_PINCTRL_STATE_DEFAULT); + if (IS_ERR(ov9750->pins_default)) + dev_err(dev, "could not get default pinstate\n"); + + ov9750->pins_sleep = + pinctrl_lookup_state(ov9750->pinctrl, + OF_CAMERA_PINCTRL_STATE_SLEEP); + if (IS_ERR(ov9750->pins_sleep)) + dev_err(dev, "could not get sleep pinstate\n"); + } else { + dev_err(dev, "no pinctrl\n"); + } + + ret = ov9750_configure_regulators(ov9750); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + + mutex_init(&ov9750->mutex); + + sd = &ov9750->subdev; + v4l2_i2c_subdev_init(sd, client, &ov9750_subdev_ops); + ret = ov9750_initialize_controls(ov9750); + if (ret) + goto err_destroy_mutex; + + ret = __ov9750_power_on(ov9750); + if (ret) + goto err_free_handler; + + ret = ov9750_check_sensor_id(ov9750, client); + if (ret) + goto err_power_off; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &ov9750_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + ov9750->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &ov9750->pad); + if (ret < 0) + goto err_power_off; +#endif + + memset(facing, 0, sizeof(facing)); + if (strcmp(ov9750->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + ov9750->module_index, facing, + OV9750_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor_common(sd); + if (ret) { + dev_err(dev, "v4l2 async register subdev failed\n"); + goto err_clean_entity; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; + +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif +err_power_off: + __ov9750_power_off(ov9750); +err_free_handler: + v4l2_ctrl_handler_free(&ov9750->ctrl_handler); +err_destroy_mutex: + mutex_destroy(&ov9750->mutex); + + return ret; +} + +static int ov9750_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov9750 *ov9750 = to_ov9750(sd); + + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + v4l2_ctrl_handler_free(&ov9750->ctrl_handler); + mutex_destroy(&ov9750->mutex); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __ov9750_power_off(ov9750); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id ov9750_of_match[] = { + { .compatible = "ovti,ov9750" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ov9750_of_match); +#endif + +static const struct i2c_device_id ov9750_match_id[] = { + { "ovti,ov9750", 0 }, + { }, +}; + +static struct i2c_driver ov9750_i2c_driver = { + .driver = { + .name = OV9750_NAME, + .pm = &ov9750_pm_ops, + .of_match_table = of_match_ptr(ov9750_of_match), + }, + .probe = &ov9750_probe, + .remove = &ov9750_remove, + .id_table = ov9750_match_id, +}; + +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&ov9750_i2c_driver); +} + +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&ov9750_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_DESCRIPTION("OmniVision ov9750 sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/sc031gs.c b/drivers/media/i2c/sc031gs.c index eaa7f26c53f0..abba702259f2 100644 --- a/drivers/media/i2c/sc031gs.c +++ b/drivers/media/i2c/sc031gs.c @@ -3,6 +3,9 @@ * sc031gs driver * * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd. + * + * V0.0X01.0X01 add poweron function. + * V0.0X01.0X02 fix mclk issue when probe multiple camera. */ #include @@ -14,11 +17,16 @@ #include #include #include +#include +#include +#include #include #include #include #include +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x02) + #ifndef V4L2_CID_DIGITAL_GAIN #define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN #endif @@ -66,6 +74,8 @@ #define SC031GS_BITS_PER_SAMPLE 10 #endif +#define SC031GS_NAME "sc031gs" + static const char * const sc031gs_supply_names[] = { "avdd", /* Analog power */ "dovdd", /* Digital I/O power */ @@ -106,7 +116,12 @@ struct sc031gs { struct v4l2_ctrl *test_pattern; struct mutex mutex; bool streaming; + bool power_on; const struct sc031gs_mode *cur_mode; + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; }; #define to_sc031gs(sd) container_of(sd, struct sc031gs, subdev) @@ -128,6 +143,7 @@ static const struct regval sc031gs_global_regs[] = { {0x3018, 0x1f}, {0x3019, 0xff}, {0x301c, 0xb4}, + {0x3028, 0x82}, {0x320c, 0x03}, {0x320d, 0x6e}, // {0x320e, 0x02}, //120fps @@ -197,9 +213,10 @@ static const struct regval sc031gs_global_regs[] = { {0x4500, 0x59}, {0x4501, 0xc4}, {0x5011, 0x00}, -// {0x0100, 0x01}, + {0x0100, 0x01}, {0x4418, 0x08}, - {0x4419, 0x8a}, + {0x4419, 0x8e}, + {0x0100, 0x00}, // test pattern // {0x4501, 0xac}, // {0x5011, 0x01}, @@ -282,8 +299,10 @@ static const struct regval sc031gs_global_regs[] = { {0x4501, 0xc4}, {0x4603, 0x00}, {0x5011, 0x00}, + {0x0100, 0x01}, {0x4418, 0x08}, - {0x4419, 0x8a}, + {0x4419, 0x8e}, + {0x0100, 0x00}, {REG_NULL, 0x00}, #endif }; @@ -353,6 +372,8 @@ static int sc031gs_write_array(struct i2c_client *client, for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) { ret = sc031gs_write_reg(client, regs[i].addr, SC031GS_REG_VALUE_08BIT, regs[i].val); + if (regs[i].addr == 0x0100 && regs[i].val == 0x01) + msleep(10); } return ret; @@ -526,6 +547,76 @@ static int sc031gs_enable_test_pattern(struct sc031gs *sc031gs, u32 pattern) SC031GS_REG_VALUE_08BIT, val); } +static void sc031gs_get_module_inf(struct sc031gs *sc031gs, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strlcpy(inf->base.sensor, SC031GS_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, sc031gs->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, sc031gs->len_name, sizeof(inf->base.lens)); +} + +static long sc031gs_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct sc031gs *sc031gs = to_sc031gs(sd); + long ret = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + sc031gs_get_module_inf(sc031gs, (struct rkmodule_inf *)arg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long sc031gs_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_awb_cfg *cfg; + long ret; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = sc031gs_ioctl(sd, cmd, inf); + if (!ret) + ret = copy_to_user(up, inf, sizeof(*inf)); + kfree(inf); + break; + case RKMODULE_AWB_CFG: + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(cfg, up, sizeof(*cfg)); + if (!ret) + ret = sc031gs_ioctl(sd, cmd, cfg); + kfree(cfg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + static int sc031gs_set_ctrl_gain(struct sc031gs *sc031gs, u32 a_gain) { int ret = 0; @@ -559,12 +650,12 @@ static int sc031gs_set_ctrl_gain(struct sc031gs *sc031gs, u32 a_gain) if (a_gain < 0x20) { ret |= sc031gs_write_reg(sc031gs->client, 0x3314, - SC031GS_REG_VALUE_08BIT, 0x3a); + SC031GS_REG_VALUE_08BIT, 0x42); ret |= sc031gs_write_reg(sc031gs->client, 0x3317, SC031GS_REG_VALUE_08BIT, 0x20); } else { ret |= sc031gs_write_reg(sc031gs->client, 0x3314, - SC031GS_REG_VALUE_08BIT, 0x44); + SC031GS_REG_VALUE_08BIT, 0x4f); ret |= sc031gs_write_reg(sc031gs->client, 0x3317, SC031GS_REG_VALUE_08BIT, 0x0f); } @@ -659,6 +750,37 @@ static int sc031gs_g_frame_interval(struct v4l2_subdev *sd, return 0; } +static int sc031gs_s_power(struct v4l2_subdev *sd, int on) +{ + struct sc031gs *sc031gs = to_sc031gs(sd); + struct i2c_client *client = sc031gs->client; + int ret = 0; + + mutex_lock(&sc031gs->mutex); + + /* If the power state is not modified - no work to do. */ + if (sc031gs->power_on == !!on) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + sc031gs->power_on = true; + } else { + pm_runtime_put(&client->dev); + sc031gs->power_on = false; + } + +unlock_and_return: + mutex_unlock(&sc031gs->mutex); + + return ret; +} + /* Calculate the delay in us by clock rate and clock cycles */ static inline u32 sc031gs_cal_delay(u32 cycles) { @@ -671,6 +793,11 @@ static int __sc031gs_power_on(struct sc031gs *sc031gs) u32 delay_us; struct device *dev = &sc031gs->client->dev; + ret = clk_set_rate(sc031gs->xvclk, SC031GS_XVCLK_FREQ); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate (24MHz)\n"); + if (clk_get_rate(sc031gs->xvclk) != SC031GS_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); ret = clk_prepare_enable(sc031gs->xvclk); if (ret < 0) { dev_err(dev, "Failed to enable xvclk\n"); @@ -772,6 +899,14 @@ static const struct v4l2_subdev_internal_ops sc031gs_internal_ops = { }; #endif +static const struct v4l2_subdev_core_ops sc031gs_core_ops = { + .s_power = sc031gs_s_power, + .ioctl = sc031gs_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = sc031gs_compat_ioctl32, +#endif +}; + static const struct v4l2_subdev_video_ops sc031gs_video_ops = { .s_stream = sc031gs_s_stream, .g_frame_interval = sc031gs_g_frame_interval, @@ -788,6 +923,7 @@ static const struct v4l2_subdev_pad_ops sc031gs_pad_ops = { }; static const struct v4l2_subdev_ops sc031gs_subdev_ops = { + .core = &sc031gs_core_ops, .video = &sc031gs_video_ops, .pad = &sc031gs_pad_ops, }; @@ -927,7 +1063,7 @@ static int sc031gs_check_sensor_id(struct sc031gs *sc031gs, SC031GS_REG_VALUE_16BIT, &id); if (id != CHIP_ID) { dev_err(dev, "Unexpected sensor id(%04x), ret(%d)\n", id, ret); - return ret; + return -ENODEV; } dev_info(dev, "Detected SC031GS CHIP ID = 0x%04x sensor\n", CHIP_ID); @@ -951,14 +1087,34 @@ static int sc031gs_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; + struct device_node *node = dev->of_node; struct sc031gs *sc031gs; struct v4l2_subdev *sd; + char facing[2]; int ret; + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + sc031gs = devm_kzalloc(dev, sizeof(*sc031gs), GFP_KERNEL); if (!sc031gs) return -ENOMEM; + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &sc031gs->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &sc031gs->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &sc031gs->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &sc031gs->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + sc031gs->client = client; sc031gs->cur_mode = &supported_modes[0]; @@ -967,13 +1123,6 @@ static int sc031gs_probe(struct i2c_client *client, dev_err(dev, "Failed to get xvclk\n"); return -EINVAL; } - ret = clk_set_rate(sc031gs->xvclk, SC031GS_XVCLK_FREQ); - if (ret < 0) { - dev_err(dev, "Failed to set xvclk rate (24MHz)\n"); - return ret; - } - if (clk_get_rate(sc031gs->xvclk) != SC031GS_XVCLK_FREQ) - dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); sc031gs->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(sc031gs->reset_gpio)) @@ -1006,17 +1155,27 @@ static int sc031gs_probe(struct i2c_client *client, #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API sd->internal_ops = &sc031gs_internal_ops; - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; #endif #if defined(CONFIG_MEDIA_CONTROLLER) sc031gs->pad.flags = MEDIA_PAD_FL_SOURCE; - sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; - ret = media_entity_init(&sd->entity, 1, &sc031gs->pad, 0); + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &sc031gs->pad); if (ret < 0) goto err_power_off; #endif - ret = v4l2_async_register_subdev(sd); + memset(facing, 0, sizeof(facing)); + if (strcmp(sc031gs->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + sc031gs->module_index, facing, + SC031GS_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor_common(sd); if (ret) { dev_err(dev, "v4l2 async register subdev failed\n"); goto err_clean_entity; @@ -1077,7 +1236,7 @@ static const struct i2c_device_id sc031gs_match_id[] = { static struct i2c_driver sc031gs_i2c_driver = { .driver = { - .name = "sc031gs", + .name = SC031GS_NAME, .pm = &sc031gs_pm_ops, .of_match_table = of_match_ptr(sc031gs_of_match), }, diff --git a/drivers/media/i2c/sc132gs.c b/drivers/media/i2c/sc132gs.c new file mode 100644 index 000000000000..40097acb7df6 --- /dev/null +++ b/drivers/media/i2c/sc132gs.c @@ -0,0 +1,1191 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * sc132gs driver + * + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd. + * V0.1.0: MIPI is ok. + * V0.0X01.0X02 fix mclk issue when probe multiple camera. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x02) +#ifndef V4L2_CID_DIGITAL_GAIN +#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN +#endif + +#define SC132GS_PIXEL_RATE (72 * 1000 * 1000) +#define SC132GS_XVCLK_FREQ 24000000 + +#define CHIP_ID 0x0132 +#define SC132GS_REG_CHIP_ID 0x3107 + +#define SC132GS_REG_CTRL_MODE 0x0100 +#define SC132GS_MODE_SW_STANDBY 0x0 +#define SC132GS_MODE_STREAMING BIT(0) + +#define SC132GS_REG_EXPOSURE 0x3e01 +#define SC132GS_EXPOSURE_MIN 6 +#define SC132GS_EXPOSURE_STEP 1 +#define SC132GS_VTS_MAX 0xffff + +#define SC132GS_REG_COARSE_AGAIN 0x3e08 +#define SC132GS_REG_FINE_AGAIN 0x3e09 +#define ANALOG_GAIN_MIN 0x20 +#define ANALOG_GAIN_MAX 0x391 +#define ANALOG_GAIN_STEP 1 +#define ANALOG_GAIN_DEFAULT 0x20 + +#define SC132GS_REG_TEST_PATTERN 0x4501 +#define SC132GS_TEST_PATTERN_ENABLE 0xcc +#define SC132GS_TEST_PATTERN_DISABLE 0xc4 + +#define SC132GS_REG_VTS 0x320e + +#define REG_NULL 0xFFFF + +#define SC132GS_REG_VALUE_08BIT 1 +#define SC132GS_REG_VALUE_16BIT 2 +#define SC132GS_REG_VALUE_24BIT 3 + +#define SC132GS_NAME "sc132gs" + +#define PIX_FORMAT MEDIA_BUS_FMT_Y8_1X8 + +#define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default" +#define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep" + +#define SC132GS_LANES 1 +#define SC132GS_BITS_PER_SAMPLE 8 + +static const char * const sc132gs_supply_names[] = { + "avdd", /* Analog power */ + "dovdd", /* Digital I/O power */ + "dvdd", /* Digital core power */ +}; + +#define SC132GS_NUM_SUPPLIES ARRAY_SIZE(sc132gs_supply_names) + +struct regval { + u16 addr; + u8 val; +}; + +struct sc132gs_mode { + u32 width; + u32 height; + u32 max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + const struct regval *reg_list; +}; + +struct sc132gs { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *pwdn_gpio; + struct regulator_bulk_data supplies[SC132GS_NUM_SUPPLIES]; + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_sleep; + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *anal_gain; + struct v4l2_ctrl *digi_gain; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *test_pattern; + struct mutex mutex; + bool streaming; + bool power_on; + const struct sc132gs_mode *cur_mode; + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; +}; + +#define to_sc132gs(sd) container_of(sd, struct sc132gs, subdev) + +/* + * Xclk 24Mhz + * Pclk 72Mhz + * linelength 1696(0x06a0) + * framelength 2122(0x084a) + * grabwindow_width 1080 + * grabwindow_height 1280 + * mipi 1 lane + * max_framerate 30fps + * mipi_datarate per lane 720Mbps + */ +static const struct regval sc132gs_global_regs[] = { + {0x0103, 0x01}, + {0x0100, 0x00}, + + //PLL bypass + {0x36e9, 0x80}, + {0x36f9, 0x80}, + + {0x3018, 0x12}, + {0x3019, 0x0e}, + {0x301a, 0xb4}, + {0x3031, 0x08}, + {0x3032, 0x60}, + {0x3038, 0x44}, + {0x3207, 0x17}, + {0x320c, 0x06}, + {0x320d, 0xa0}, + {0x320e, 0x08}, + {0x320f, 0x4a}, + {0x3250, 0xcc}, + {0x3251, 0x02}, + {0x3252, 0x08}, + {0x3253, 0x45}, + {0x3254, 0x05}, + {0x3255, 0x3b}, + {0x3306, 0x78}, + {0x330a, 0x00}, + {0x330b, 0xc8}, + {0x330f, 0x24}, + {0x3314, 0x80}, + {0x3315, 0x40}, + {0x3317, 0xf0}, + {0x331f, 0x12}, + {0x3364, 0x00}, + {0x3385, 0x41}, + {0x3387, 0x41}, + {0x3389, 0x09}, + {0x33ab, 0x00}, + {0x33ac, 0x00}, + {0x33b1, 0x03}, + {0x33b2, 0x12}, + {0x33f8, 0x02}, + {0x33fa, 0x01}, + {0x3409, 0x08}, + {0x34f0, 0xc0}, + {0x34f1, 0x20}, + {0x34f2, 0x03}, + {0x3622, 0xf5}, + {0x3630, 0x5c}, + {0x3631, 0x80}, + {0x3632, 0xc8}, + {0x3633, 0x32}, + {0x3638, 0x2a}, + {0x3639, 0x07}, + {0x363b, 0x48}, + {0x363c, 0x83}, + {0x363d, 0x10}, + {0x36ea, 0x3a}, + {0x36fa, 0x25}, + {0x36fb, 0x05}, + {0x36fd, 0x04}, + {0x3900, 0x11}, + {0x3901, 0x05}, + {0x3902, 0xc5}, + {0x3904, 0x04}, + {0x3908, 0x91}, + {0x391e, 0x00}, + {0x3e01, 0x53}, + {0x3e02, 0xe0}, + {0x3e09, 0x20}, + {0x3e0e, 0xd2}, + {0x3e14, 0xb0}, + {0x3e1e, 0x7c}, + {0x3e26, 0x20}, + {0x4418, 0x38}, + {0x4503, 0x10}, + {0x4837, 0x14}, + {0x5000, 0x0e}, + {0x540c, 0x51}, + {0x550f, 0x38}, + {0x5780, 0x67}, + {0x5784, 0x10}, + {0x5785, 0x06}, + {0x5787, 0x02}, + {0x5788, 0x00}, + {0x5789, 0x00}, + {0x578a, 0x02}, + {0x578b, 0x00}, + {0x578c, 0x00}, + {0x5790, 0x00}, + {0x5791, 0x00}, + {0x5792, 0x00}, + {0x5793, 0x00}, + {0x5794, 0x00}, + {0x5795, 0x00}, + {0x5799, 0x04}, + + {0x3037, 0x00}, + + //PLL set + {0x36e9, 0x24}, + {0x36f9, 0x24}, + + {0x0100, 0x01}, + {REG_NULL, 0x00}, +}; + +static const struct sc132gs_mode supported_modes[] = { + { + .width = 1080, + .height = 1280, + .max_fps = 30, + .exp_def = 0x0148, + .hts_def = 0x06a0, + .vts_def = 0x084a, + .reg_list = sc132gs_global_regs, + }, +}; + +static const char * const sc132gs_test_pattern_menu[] = { + "Disabled", + "Vertical Color Bar Type 1", + "Vertical Color Bar Type 2", + "Vertical Color Bar Type 3", + "Vertical Color Bar Type 4" +}; + +#define SC132GS_LINK_FREQ_360MHZ (360 * 1000 * 1000) + +static const s64 link_freq_menu_items[] = { + SC132GS_LINK_FREQ_360MHZ +}; + +/* Write registers up to 4 at a time */ +static int sc132gs_write_reg(struct i2c_client *client, + u16 reg, u32 len, u32 val) +{ + u32 buf_i, val_i; + u8 buf[6]; + u8 *val_p; + __be32 val_be; + u32 ret; + + if (len > 4) + return -EINVAL; + + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + + val_be = cpu_to_be32(val); + val_p = (u8 *)&val_be; + buf_i = 2; + val_i = 4 - len; + + while (val_i < 4) + buf[buf_i++] = val_p[val_i++]; + + ret = i2c_master_send(client, buf, len + 2); + if (ret != len + 2) + return -EIO; + + return 0; +} + +static int sc132gs_write_array(struct i2c_client *client, + const struct regval *regs) +{ + u32 i; + int ret = 0; + + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) { + ret = sc132gs_write_reg(client, regs[i].addr, + SC132GS_REG_VALUE_08BIT, regs[i].val); + } + + return ret; +} + +/* Read registers up to 4 at a time */ +static int sc132gs_read_reg(struct i2c_client *client, + u16 reg, unsigned int len, u32 *val) +{ + struct i2c_msg msgs[2]; + u8 *data_be_p; + __be32 data_be = 0; + __be16 reg_addr_be = cpu_to_be16(reg); + int ret; + + if (len > 4 || !len) + return -EINVAL; + + data_be_p = (u8 *)&data_be; + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 2; + msgs[0].buf = (u8 *)®_addr_be; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_be_p[4 - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = be32_to_cpu(data_be); + + return 0; +} + +static int sc132gs_get_reso_dist(const struct sc132gs_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); +} + +static const struct sc132gs_mode * + sc132gs_find_best_fit(struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *framefmt = &fmt->format; + int dist; + int cur_best_fit = 0; + int cur_best_fit_dist = -1; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { + dist = sc132gs_get_reso_dist(&supported_modes[i], framefmt); + if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { + cur_best_fit_dist = dist; + cur_best_fit = i; + } + } + return &supported_modes[cur_best_fit]; +} + +static int sc132gs_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct sc132gs *sc132gs = to_sc132gs(sd); + const struct sc132gs_mode *mode; + s64 h_blank, vblank_def; + + mutex_lock(&sc132gs->mutex); + + mode = sc132gs_find_best_fit(fmt); + fmt->format.code = PIX_FORMAT; + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; +#else + mutex_unlock(&sc132gs->mutex); + return -ENOTTY; +#endif + } else { + sc132gs->cur_mode = mode; + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(sc132gs->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(sc132gs->vblank, vblank_def, + SC132GS_VTS_MAX - mode->height, + 1, vblank_def); + } + + mutex_unlock(&sc132gs->mutex); + + return 0; +} + +static int sc132gs_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct sc132gs *sc132gs = to_sc132gs(sd); + const struct sc132gs_mode *mode = sc132gs->cur_mode; + + mutex_lock(&sc132gs->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); +#else + mutex_unlock(&sc132gs->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = PIX_FORMAT; + fmt->format.field = V4L2_FIELD_NONE; + } + mutex_unlock(&sc132gs->mutex); + + return 0; +} + +static int sc132gs_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index != 0) + return -EINVAL; + code->code = PIX_FORMAT; + + return 0; +} + +static int sc132gs_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fse->code != PIX_FORMAT) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = supported_modes[fse->index].width; + fse->max_height = supported_modes[fse->index].height; + fse->min_height = supported_modes[fse->index].height; + + return 0; +} + +static int sc132gs_enable_test_pattern(struct sc132gs *sc132gs, u32 pattern) +{ + u32 val; + + if (pattern) + val = (pattern - 1) | SC132GS_TEST_PATTERN_ENABLE; + else + val = SC132GS_TEST_PATTERN_DISABLE; + + return sc132gs_write_reg(sc132gs->client, SC132GS_REG_TEST_PATTERN, + SC132GS_REG_VALUE_08BIT, val); +} + +static void sc132gs_get_module_inf(struct sc132gs *sc132gs, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strlcpy(inf->base.sensor, SC132GS_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, sc132gs->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, sc132gs->len_name, sizeof(inf->base.lens)); +} + +static long sc132gs_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct sc132gs *sc132gs = to_sc132gs(sd); + long ret = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + sc132gs_get_module_inf(sc132gs, (struct rkmodule_inf *)arg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long sc132gs_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + long ret; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = sc132gs_ioctl(sd, cmd, inf); + if (!ret) + ret = copy_to_user(up, inf, sizeof(*inf)); + kfree(inf); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + +static int sc132gs_set_ctrl_gain(struct sc132gs *sc132gs, u32 a_gain) +{ + int ret = 0; + u32 coarse_again, fine_again, fine_again_reg, coarse_again_reg; + + if (a_gain < 0x20) + a_gain = 0x20; + if (a_gain > 0x391) + a_gain = 0x391; + + if (a_gain < 0x3a) {/*1x~1.813*/ + fine_again = a_gain; + coarse_again = 0x03; + fine_again_reg = fine_again & 0x3f; + coarse_again_reg = coarse_again & 0x3F; + if (fine_again_reg >= 0x39) + fine_again_reg = 0x39; + } else if (a_gain < 0x72) {/*1.813~3.568x*/ + fine_again = (a_gain - 0x3a) * 1000 / 1755 + 0x20; + coarse_again = 0x23; + if (fine_again > 0x3f) + fine_again = 0x3f; + fine_again_reg = fine_again & 0x3f; + coarse_again_reg = coarse_again & 0x3F; + } else if (a_gain < 0xe8) { /*3.568x~7.250x*/ + fine_again = (a_gain - 0x72) * 1000 / 3682 + 0x20; + coarse_again = 0x27; + if (fine_again > 0x3f) + fine_again = 0x3f; + fine_again_reg = fine_again & 0x3f; + coarse_again_reg = coarse_again & 0x3F; + } else if (a_gain < 0x1d0) { /*7.250x~14.5x*/ + fine_again = (a_gain - 0xe8) * 100 / 725 + 0x20; + coarse_again = 0x2f; + if (fine_again > 0x3f) + fine_again = 0x3f; + fine_again_reg = fine_again & 0x3f; + coarse_again_reg = coarse_again & 0x3F; + } else { /*14.5x~28.547*/ + fine_again = (a_gain - 0x1d0) * 1000 / 14047 + 0x20; + coarse_again = 0x3f; + if (fine_again > 0x3f) + fine_again = 0x3f; + fine_again_reg = fine_again & 0x3f; + coarse_again_reg = coarse_again & 0x3F; + } + ret |= sc132gs_write_reg(sc132gs->client, + SC132GS_REG_COARSE_AGAIN, + SC132GS_REG_VALUE_08BIT, + coarse_again_reg); + ret |= sc132gs_write_reg(sc132gs->client, + SC132GS_REG_FINE_AGAIN, + SC132GS_REG_VALUE_08BIT, + fine_again_reg); + return ret; +} + +static int __sc132gs_start_stream(struct sc132gs *sc132gs) +{ + int ret; + + ret = sc132gs_write_array(sc132gs->client, sc132gs->cur_mode->reg_list); + if (ret) + return ret; + + /* In case these controls are set before streaming */ + mutex_unlock(&sc132gs->mutex); + ret = v4l2_ctrl_handler_setup(&sc132gs->ctrl_handler); + mutex_lock(&sc132gs->mutex); + if (ret) + return ret; + + return sc132gs_write_reg(sc132gs->client, SC132GS_REG_CTRL_MODE, + SC132GS_REG_VALUE_08BIT, SC132GS_MODE_STREAMING); +} + +static int __sc132gs_stop_stream(struct sc132gs *sc132gs) +{ + return sc132gs_write_reg(sc132gs->client, SC132GS_REG_CTRL_MODE, + SC132GS_REG_VALUE_08BIT, SC132GS_MODE_SW_STANDBY); +} + +static int sc132gs_s_stream(struct v4l2_subdev *sd, int on) +{ + struct sc132gs *sc132gs = to_sc132gs(sd); + struct i2c_client *client = sc132gs->client; + int ret = 0; + + mutex_lock(&sc132gs->mutex); + on = !!on; + if (on == sc132gs->streaming) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = __sc132gs_start_stream(sc132gs); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + __sc132gs_stop_stream(sc132gs); + pm_runtime_put(&client->dev); + } + + sc132gs->streaming = on; + +unlock_and_return: + mutex_unlock(&sc132gs->mutex); + + return ret; +} + +static int sc132gs_s_power(struct v4l2_subdev *sd, int on) +{ + struct sc132gs *sc132gs = to_sc132gs(sd); + struct i2c_client *client = sc132gs->client; + int ret = 0; + + mutex_lock(&sc132gs->mutex); + + /* If the power state is not modified - no work to do. */ + if (sc132gs->power_on == !!on) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + sc132gs->power_on = true; + } else { + pm_runtime_put(&client->dev); + sc132gs->power_on = false; + } + +unlock_and_return: + mutex_unlock(&sc132gs->mutex); + + return ret; +} + +static int sc132gs_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct sc132gs *sc132gs = to_sc132gs(sd); + const struct sc132gs_mode *mode = sc132gs->cur_mode; + + mutex_lock(&sc132gs->mutex); + fi->interval.numerator = 10000; + fi->interval.denominator = mode->max_fps * 10000; + mutex_unlock(&sc132gs->mutex); + + return 0; +} + +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 sc132gs_cal_delay(u32 cycles) +{ + return DIV_ROUND_UP(cycles, SC132GS_XVCLK_FREQ / 1000 / 1000); +} + +static int __sc132gs_power_on(struct sc132gs *sc132gs) +{ + int ret; + u32 delay_us; + struct device *dev = &sc132gs->client->dev; + + if (!IS_ERR_OR_NULL(sc132gs->pins_default)) { + ret = pinctrl_select_state(sc132gs->pinctrl, + sc132gs->pins_default); + if (ret < 0) + dev_err(dev, "could not set pins\n"); + } + + ret = clk_set_rate(sc132gs->xvclk, SC132GS_XVCLK_FREQ); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate (24MHz)\n"); + if (clk_get_rate(sc132gs->xvclk) != SC132GS_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); + ret = clk_prepare_enable(sc132gs->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + + ret = regulator_bulk_enable(SC132GS_NUM_SUPPLIES, sc132gs->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + + if (!IS_ERR(sc132gs->pwdn_gpio)) + gpiod_set_value_cansleep(sc132gs->pwdn_gpio, 1); + + /* 8192 cycles prior to first SCCB transaction */ + delay_us = sc132gs_cal_delay(8192); + usleep_range(delay_us, delay_us * 2); + + return 0; + +disable_clk: + clk_disable_unprepare(sc132gs->xvclk); + + return ret; +} + +static void __sc132gs_power_off(struct sc132gs *sc132gs) +{ + int ret; + + if (!IS_ERR(sc132gs->pwdn_gpio)) + gpiod_set_value_cansleep(sc132gs->pwdn_gpio, 0); + clk_disable_unprepare(sc132gs->xvclk); + if (!IS_ERR_OR_NULL(sc132gs->pins_sleep)) { + ret = pinctrl_select_state(sc132gs->pinctrl, + sc132gs->pins_sleep); + if (ret < 0) + dev_dbg(&sc132gs->client->dev, "could not set pins\n"); + } + regulator_bulk_disable(SC132GS_NUM_SUPPLIES, sc132gs->supplies); +} + +static int sc132gs_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct sc132gs *sc132gs = to_sc132gs(sd); + + return __sc132gs_power_on(sc132gs); +} + +static int sc132gs_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct sc132gs *sc132gs = to_sc132gs(sd); + + __sc132gs_power_off(sc132gs); + + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int sc132gs_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct sc132gs *sc132gs = to_sc132gs(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + const struct sc132gs_mode *def_mode = &supported_modes[0]; + + mutex_lock(&sc132gs->mutex); + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = PIX_FORMAT; + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&sc132gs->mutex); + /* No crop or compose */ + + return 0; +} +#endif + +static const struct dev_pm_ops sc132gs_pm_ops = { + SET_RUNTIME_PM_OPS(sc132gs_runtime_suspend, + sc132gs_runtime_resume, NULL) +}; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops sc132gs_internal_ops = { + .open = sc132gs_open, +}; +#endif + +static const struct v4l2_subdev_core_ops sc132gs_core_ops = { + .s_power = sc132gs_s_power, + .ioctl = sc132gs_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = sc132gs_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops sc132gs_video_ops = { + .s_stream = sc132gs_s_stream, + .g_frame_interval = sc132gs_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops sc132gs_pad_ops = { + .enum_mbus_code = sc132gs_enum_mbus_code, + .enum_frame_size = sc132gs_enum_frame_sizes, + .get_fmt = sc132gs_get_fmt, + .set_fmt = sc132gs_set_fmt, +}; + +static const struct v4l2_subdev_ops sc132gs_subdev_ops = { + .core = &sc132gs_core_ops, + .video = &sc132gs_video_ops, + .pad = &sc132gs_pad_ops, +}; + +static int sc132gs_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct sc132gs *sc132gs = container_of(ctrl->handler, + struct sc132gs, ctrl_handler); + struct i2c_client *client = sc132gs->client; + s64 max; + int ret = 0; + + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ + max = sc132gs->cur_mode->height + ctrl->val - 6; + __v4l2_ctrl_modify_range(sc132gs->exposure, + sc132gs->exposure->minimum, max, + sc132gs->exposure->step, + sc132gs->exposure->default_value); + break; + } + + if (pm_runtime_get(&client->dev) <= 0) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + /* 4 least significant bits of expsoure are fractional part */ + ret = sc132gs_write_reg(sc132gs->client, SC132GS_REG_EXPOSURE, + SC132GS_REG_VALUE_16BIT, ctrl->val << 4); + break; + case V4L2_CID_ANALOGUE_GAIN: + ret = sc132gs_set_ctrl_gain(sc132gs, ctrl->val); + break; + case V4L2_CID_VBLANK: + ret = sc132gs_write_reg(sc132gs->client, SC132GS_REG_VTS, + SC132GS_REG_VALUE_16BIT, + ctrl->val + sc132gs->cur_mode->height); + break; + case V4L2_CID_TEST_PATTERN: + ret = sc132gs_enable_test_pattern(sc132gs, ctrl->val); + break; + default: + dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", + __func__, ctrl->id, ctrl->val); + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops sc132gs_ctrl_ops = { + .s_ctrl = sc132gs_set_ctrl, +}; + +static int sc132gs_initialize_controls(struct sc132gs *sc132gs) +{ + const struct sc132gs_mode *mode; + struct v4l2_ctrl_handler *handler; + struct v4l2_ctrl *ctrl; + s64 exposure_max, vblank_def; + u32 h_blank; + int ret; + + handler = &sc132gs->ctrl_handler; + mode = sc132gs->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 8); + if (ret) + return ret; + handler->lock = &sc132gs->mutex; + + ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, + 0, 0, link_freq_menu_items); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, + 0, SC132GS_PIXEL_RATE, 1, SC132GS_PIXEL_RATE); + + h_blank = mode->hts_def - mode->width; + sc132gs->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (sc132gs->hblank) + sc132gs->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = mode->vts_def - mode->height; + sc132gs->vblank = v4l2_ctrl_new_std(handler, &sc132gs_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + SC132GS_VTS_MAX - mode->height, + 1, vblank_def); + + exposure_max = mode->vts_def - 6; + sc132gs->exposure = v4l2_ctrl_new_std(handler, &sc132gs_ctrl_ops, + V4L2_CID_EXPOSURE, SC132GS_EXPOSURE_MIN, + exposure_max, SC132GS_EXPOSURE_STEP, + mode->exp_def); + + sc132gs->anal_gain = v4l2_ctrl_new_std(handler, &sc132gs_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, ANALOG_GAIN_MIN, + ANALOG_GAIN_MAX, ANALOG_GAIN_STEP, + ANALOG_GAIN_DEFAULT); + + sc132gs->test_pattern = v4l2_ctrl_new_std_menu_items(handler, + &sc132gs_ctrl_ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(sc132gs_test_pattern_menu) - 1, + 0, 0, sc132gs_test_pattern_menu); + + if (handler->error) { + ret = handler->error; + dev_err(&sc132gs->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + sc132gs->subdev.ctrl_handler = handler; + + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +static int sc132gs_check_sensor_id(struct sc132gs *sc132gs, + struct i2c_client *client) +{ + struct device *dev = &sc132gs->client->dev; + u32 id = 0; + int ret; + + ret = sc132gs_read_reg(client, SC132GS_REG_CHIP_ID, + SC132GS_REG_VALUE_16BIT, &id); + if (id != CHIP_ID) { + dev_err(dev, "Unexpected sensor id(%04x), ret(%d)\n", id, ret); + return -ENODEV; + } + + dev_info(dev, "Detected SC132GS CHIP ID = 0x%04x sensor\n", CHIP_ID); + + return 0; +} + +static int sc132gs_configure_regulators(struct sc132gs *sc132gs) +{ + unsigned int i; + + for (i = 0; i < SC132GS_NUM_SUPPLIES; i++) + sc132gs->supplies[i].supply = sc132gs_supply_names[i]; + + return devm_regulator_bulk_get(&sc132gs->client->dev, + SC132GS_NUM_SUPPLIES, + sc132gs->supplies); +} + +static int sc132gs_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct sc132gs *sc132gs; + struct v4l2_subdev *sd; + char facing[2]; + int ret; + + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + sc132gs = devm_kzalloc(dev, sizeof(*sc132gs), GFP_KERNEL); + if (!sc132gs) + return -ENOMEM; + + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &sc132gs->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &sc132gs->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &sc132gs->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &sc132gs->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + sc132gs->client = client; + sc132gs->cur_mode = &supported_modes[0]; + + sc132gs->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(sc132gs->xvclk)) { + dev_err(dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + sc132gs->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(sc132gs->pwdn_gpio)) + dev_warn(dev, "Failed to get pwdn-gpios\n"); + ret = sc132gs_configure_regulators(sc132gs); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + + sc132gs->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR(sc132gs->pinctrl)) { + sc132gs->pins_default = + pinctrl_lookup_state(sc132gs->pinctrl, + OF_CAMERA_PINCTRL_STATE_DEFAULT); + if (IS_ERR(sc132gs->pins_default)) + dev_err(dev, "could not get default pinstate\n"); + + sc132gs->pins_sleep = + pinctrl_lookup_state(sc132gs->pinctrl, + OF_CAMERA_PINCTRL_STATE_SLEEP); + if (IS_ERR(sc132gs->pins_sleep)) + dev_err(dev, "could not get sleep pinstate\n"); + } + mutex_init(&sc132gs->mutex); + + sd = &sc132gs->subdev; + v4l2_i2c_subdev_init(sd, client, &sc132gs_subdev_ops); + ret = sc132gs_initialize_controls(sc132gs); + if (ret) + goto err_destroy_mutex; + + ret = __sc132gs_power_on(sc132gs); + if (ret) + goto err_free_handler; + + ret = sc132gs_check_sensor_id(sc132gs, client); + if (ret) + goto err_power_off; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &sc132gs_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + sc132gs->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &sc132gs->pad); + if (ret < 0) + goto err_power_off; +#endif + + memset(facing, 0, sizeof(facing)); + if (strcmp(sc132gs->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + sc132gs->module_index, facing, + SC132GS_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor_common(sd); + if (ret) { + dev_err(dev, "v4l2 async register subdev failed\n"); + goto err_clean_entity; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; + +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif +err_power_off: + __sc132gs_power_off(sc132gs); +err_free_handler: + v4l2_ctrl_handler_free(&sc132gs->ctrl_handler); +err_destroy_mutex: + mutex_destroy(&sc132gs->mutex); + + return ret; +} + +static int sc132gs_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct sc132gs *sc132gs = to_sc132gs(sd); + + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + v4l2_ctrl_handler_free(&sc132gs->ctrl_handler); + mutex_destroy(&sc132gs->mutex); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __sc132gs_power_off(sc132gs); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id sc132gs_of_match[] = { + { .compatible = "smartsens,sc132gs" }, + {}, +}; +MODULE_DEVICE_TABLE(of, sc132gs_of_match); +#endif + +static const struct i2c_device_id sc132gs_match_id[] = { + { "smartsens,sc132gs", 0 }, + { }, +}; + +static struct i2c_driver sc132gs_i2c_driver = { + .driver = { + .name = SC132GS_NAME, + .pm = &sc132gs_pm_ops, + .of_match_table = of_match_ptr(sc132gs_of_match), + }, + .probe = &sc132gs_probe, + .remove = &sc132gs_remove, + .id_table = sc132gs_match_id, +}; + +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&sc132gs_i2c_driver); +} + +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&sc132gs_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_DESCRIPTION("Smartsens sc132gs sensor driver"); +MODULE_LICENSE("GPL v2");