diff options
Diffstat (limited to 'drivers/media/i2c')
132 files changed, 19069 insertions, 6481 deletions
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 8ba096b8ebca..4b4db8c4f496 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -27,7 +27,7 @@ config VIDEO_IR_I2C menuconfig VIDEO_CAMERA_SENSOR bool "Camera sensor devices" - depends on MEDIA_CAMERA_SUPPORT && I2C + depends on MEDIA_CAMERA_SUPPORT && I2C && HAVE_CLK select MEDIA_CONTROLLER select V4L2_FWNODE select VIDEO_V4L2_SUBDEV_API @@ -70,6 +70,16 @@ config VIDEO_GC0308 To compile this driver as a module, choose M here: the module will be called gc0308. +config VIDEO_GC0310 + tristate "GalaxyCore GC0310 sensor support" + select V4L2_CCI_I2C + help + This is a Video4Linux2 sensor-level driver for the Galaxycore + GC0310 0.3MP sensor. + + To compile this driver as a module, choose M here: the + module will be called gc0310. + config VIDEO_GC05A2 tristate "GalaxyCore gc05a2 sensor support" select V4L2_CCI_I2C @@ -127,6 +137,16 @@ config VIDEO_HI847 To compile this driver as a module, choose M here: the module will be called hi847. +config VIDEO_IMX111 + tristate "Sony IMX111 sensor support" + select V4L2_CCI_I2C + help + This is a V4L2 sensor driver for the Sony IMX111 camera + sensors. + + To compile this driver as a module, choose M here: the + module will be called imx111. + config VIDEO_IMX208 tristate "Sony IMX208 sensor support" help @@ -140,6 +160,8 @@ config VIDEO_IMX214 tristate "Sony IMX214 sensor support" depends on GPIOLIB select REGMAP_I2C + select V4L2_CCI_I2C + select VIDEO_CCS_PLL help This is a Video4Linux2 sensor driver for the Sony IMX214 camera. @@ -216,6 +238,7 @@ config VIDEO_IMX319 config VIDEO_IMX334 tristate "Sony IMX334 sensor support" depends on OF_GPIO + select V4L2_CCI_I2C help This is a Video4Linux2 sensor driver for the Sony IMX334 camera. @@ -314,6 +337,7 @@ config VIDEO_MT9V011 config VIDEO_MT9V032 tristate "Micron MT9V032 sensor support" + depends on OF select REGMAP_I2C help This is a Video4Linux2 sensor driver for the Micron @@ -337,6 +361,16 @@ config VIDEO_OG01A1B To compile this driver as a module, choose M here: the module will be called og01a1b. +config VIDEO_OG0VE1B + tristate "OmniVision OG0VE1B sensor support" + select V4L2_CCI_I2C + help + This is a Video4Linux2 sensor driver for the OmniVision + OG0VE1B camera. + + To compile this driver as a module, choose M here: the + module will be called og0ve1b. + config VIDEO_OV01A10 tristate "OmniVision OV01A10 sensor support" help @@ -355,6 +389,26 @@ config VIDEO_OV02A10 To compile this driver as a module, choose M here: the module will be called ov02a10. +config VIDEO_OV02E10 + tristate "OmniVision OV02E10 sensor support" + select V4L2_CCI_I2C + help + This is a Video4Linux2 sensor driver for the OmniVision + OV02E10 camera. + + To compile this driver as a module, choose M here: the + module will be called ov02e10. + +config VIDEO_OV02C10 + tristate "OmniVision OV02C10 sensor support" + select V4L2_CCI_I2C + help + This is a Video4Linux2 sensor driver for the OmniVision + OV02C10 camera. + + To compile this driver as a module, choose M here: the + module will be called ov02c10. + config VIDEO_OV08D10 tristate "OmniVision OV08D10 sensor support" help @@ -423,6 +477,16 @@ config VIDEO_OV2685 To compile this driver as a module, choose M here: the module will be called ov2685. +config VIDEO_OV2735 + tristate "OmniVision OV2735 sensor support" + select V4L2_CCI_I2C + 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_OV2740 tristate "OmniVision OV2740 sensor support" depends on ACPI || COMPILE_TEST @@ -519,24 +583,25 @@ config VIDEO_OV5695 To compile this driver as a module, choose M here: the module will be called ov5695. -config VIDEO_OV64A40 - tristate "OmniVision OV64A40 sensor support" +config VIDEO_OV6211 + tristate "OmniVision OV6211 sensor support" select V4L2_CCI_I2C help This is a Video4Linux2 sensor driver for the OmniVision - OV64A40 camera. + OV6211 camera. To compile this driver as a module, choose M here: the - module will be called ov64a40. + module will be called ov6211. -config VIDEO_OV6650 - tristate "OmniVision OV6650 sensor support" +config VIDEO_OV64A40 + tristate "OmniVision OV64A40 sensor support" + select V4L2_CCI_I2C help This is a Video4Linux2 sensor driver for the OmniVision - OV6650 camera. + OV64A40 camera. To compile this driver as a module, choose M here: the - module will be called ov6650. + module will be called ov64a40. config VIDEO_OV7251 tristate "OmniVision OV7251 sensor support" @@ -690,6 +755,28 @@ config VIDEO_S5K6A3 This is a V4L2 sensor driver for Samsung S5K6A3 raw camera sensor. +config VIDEO_VD55G1 + tristate "ST VD55G1 sensor support" + select V4L2_CCI_I2C + depends on GPIOLIB + help + This is a Video4Linux2 sensor driver for the ST VD55G1 + camera sensor. + + To compile this driver as a module, choose M here: the + module will be called vd55g1. + +config VIDEO_VD56G3 + tristate "ST VD56G3 sensor support" + select V4L2_CCI_I2C + depends on GPIOLIB + help + This is a Video4Linux2 sensor driver for the ST VD56G3 + camera sensor. + + To compile this driver as a module, choose M here: the + module will be called vd56g3. + config VIDEO_VGXY61 tristate "ST VGXY61 sensor support" select V4L2_CCI_I2C @@ -721,24 +808,25 @@ config VIDEO_THP7312 endmenu -menu "Lens drivers" - visible if MEDIA_CAMERA_SUPPORT +menuconfig VIDEO_CAMERA_LENS + bool "Lens drivers" + depends on MEDIA_CAMERA_SUPPORT && I2C + select MEDIA_CONTROLLER + select V4L2_FWNODE + select VIDEO_V4L2_SUBDEV_API + default y + +if VIDEO_CAMERA_LENS config VIDEO_AD5820 tristate "AD5820 lens voice coil support" - depends on GPIOLIB && I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select V4L2_ASYNC + depends on GPIOLIB help This is a driver for the AD5820 camera lens voice coil. It is used for example in Nokia N900 (RX-51). config VIDEO_AK7375 tristate "AK7375 lens voice coil support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_ASYNC help This is a driver for the AK7375 camera lens voice coil. AK7375 is a 12 bit DAC with 120mA output current sink @@ -747,10 +835,7 @@ config VIDEO_AK7375 config VIDEO_DW9714 tristate "DW9714 lens voice coil support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_ASYNC + depends on GPIOLIB help This is a driver for the DW9714 camera lens voice coil. DW9714 is a 10 bit DAC with 120mA output current sink @@ -759,10 +844,6 @@ config VIDEO_DW9714 config VIDEO_DW9719 tristate "DW9719 lens voice coil support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_ASYNC select V4L2_CCI_I2C help This is a driver for the DW9719 camera lens voice coil. @@ -771,10 +852,6 @@ config VIDEO_DW9719 config VIDEO_DW9768 tristate "DW9768 lens voice coil support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a driver for the DW9768 camera lens voice coil. DW9768 is a 10 bit DAC with 100mA output current sink @@ -783,17 +860,13 @@ config VIDEO_DW9768 config VIDEO_DW9807_VCM tristate "DW9807 lens voice coil support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_ASYNC help This is a driver for the DW9807 camera lens voice coil. DW9807 is a 10 bit DAC with 100mA output current sink capability. This is designed for linear control of voice coil motors, controlled via I2C serial interface. -endmenu +endif menu "Flash devices" visible if MEDIA_CAMERA_SUPPORT @@ -1146,6 +1219,20 @@ config VIDEO_ISL7998X Support for Intersil ISL7998x analog to MIPI-CSI2 or BT.656 decoder. +config VIDEO_LT6911UXE + tristate "Lontium LT6911UXE decoder" + depends on ACPI && VIDEO_DEV && I2C + select V4L2_FWNODE + select V4L2_CCI_I2C + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + help + This is a Video4Linux2 sensor-level driver for the Lontium + LT6911UXE HDMI to MIPI CSI-2 bridge. + + To compile this driver as a module, choose M here: the + module will be called lt6911uxe. + config VIDEO_KS0127 tristate "KS0127 video decoder" depends on VIDEO_DEV && I2C @@ -1626,7 +1713,7 @@ config VIDEO_MAX96714 config VIDEO_MAX96717 tristate "Maxim MAX96717 GMSL2 Serializer support" - depends on OF && I2C && VIDEO_DEV && COMMON_CLK + depends on I2C && VIDEO_DEV && COMMON_CLK select I2C_MUX select MEDIA_CONTROLLER select GPIOLIB diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index fbb988bd067a..c5f17602454f 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_VIDEO_DW9768) += dw9768.o obj-$(CONFIG_VIDEO_DW9807_VCM) += dw9807-vcm.o obj-$(CONFIG_VIDEO_ET8EK8) += et8ek8/ obj-$(CONFIG_VIDEO_GC0308) += gc0308.o +obj-$(CONFIG_VIDEO_GC0310) += gc0310.o obj-$(CONFIG_VIDEO_GC05A2) += gc05a2.o obj-$(CONFIG_VIDEO_GC08A3) += gc08a3.o obj-$(CONFIG_VIDEO_GC2145) += gc2145.o @@ -45,6 +46,7 @@ obj-$(CONFIG_VIDEO_HI556) += hi556.o obj-$(CONFIG_VIDEO_HI846) += hi846.o obj-$(CONFIG_VIDEO_HI847) += hi847.o obj-$(CONFIG_VIDEO_I2C) += video-i2c.o +obj-$(CONFIG_VIDEO_IMX111) += imx111.o obj-$(CONFIG_VIDEO_IMX208) += imx208.o obj-$(CONFIG_VIDEO_IMX214) += imx214.o obj-$(CONFIG_VIDEO_IMX219) += imx219.o @@ -64,6 +66,7 @@ obj-$(CONFIG_VIDEO_ISL7998X) += isl7998x.o obj-$(CONFIG_VIDEO_KS0127) += ks0127.o obj-$(CONFIG_VIDEO_LM3560) += lm3560.o obj-$(CONFIG_VIDEO_LM3646) += lm3646.o +obj-$(CONFIG_VIDEO_LT6911UXE) += lt6911uxe.o obj-$(CONFIG_VIDEO_M52790) += m52790.o obj-$(CONFIG_VIDEO_MAX9271_LIB) += max9271.o obj-$(CONFIG_VIDEO_MAX9286) += max9286.o @@ -80,8 +83,11 @@ obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o obj-$(CONFIG_VIDEO_MT9V111) += mt9v111.o obj-$(CONFIG_VIDEO_OG01A1B) += og01a1b.o +obj-$(CONFIG_VIDEO_OG0VE1B) += og0ve1b.o obj-$(CONFIG_VIDEO_OV01A10) += ov01a10.o obj-$(CONFIG_VIDEO_OV02A10) += ov02a10.o +obj-$(CONFIG_VIDEO_OV02C10) += ov02c10.o +obj-$(CONFIG_VIDEO_OV02E10) += ov02e10.o obj-$(CONFIG_VIDEO_OV08D10) += ov08d10.o obj-$(CONFIG_VIDEO_OV08X40) += ov08x40.o obj-$(CONFIG_VIDEO_OV13858) += ov13858.o @@ -90,6 +96,7 @@ obj-$(CONFIG_VIDEO_OV2640) += ov2640.o obj-$(CONFIG_VIDEO_OV2659) += ov2659.o obj-$(CONFIG_VIDEO_OV2680) += ov2680.o obj-$(CONFIG_VIDEO_OV2685) += ov2685.o +obj-$(CONFIG_VIDEO_OV2735) += ov2735.o obj-$(CONFIG_VIDEO_OV2740) += ov2740.o obj-$(CONFIG_VIDEO_OV4689) += ov4689.o obj-$(CONFIG_VIDEO_OV5640) += ov5640.o @@ -100,8 +107,8 @@ obj-$(CONFIG_VIDEO_OV5670) += ov5670.o obj-$(CONFIG_VIDEO_OV5675) += ov5675.o obj-$(CONFIG_VIDEO_OV5693) += ov5693.o obj-$(CONFIG_VIDEO_OV5695) += ov5695.o +obj-$(CONFIG_VIDEO_OV6211) += ov6211.o obj-$(CONFIG_VIDEO_OV64A40) += ov64a40.o -obj-$(CONFIG_VIDEO_OV6650) += ov6650.o obj-$(CONFIG_VIDEO_OV7251) += ov7251.o obj-$(CONFIG_VIDEO_OV7640) += ov7640.o obj-$(CONFIG_VIDEO_OV7670) += ov7670.o @@ -152,6 +159,8 @@ obj-$(CONFIG_VIDEO_TW9910) += tw9910.o obj-$(CONFIG_VIDEO_UDA1342) += uda1342.o obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o +obj-$(CONFIG_VIDEO_VD55G1) += vd55g1.o +obj-$(CONFIG_VIDEO_VD56G3) += vd56g3.o obj-$(CONFIG_VIDEO_VGXY61) += vgxy61.o obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index ff7dfa0278a7..378f4e6af12c 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -195,6 +195,7 @@ struct adv7180_state; #define ADV7180_FLAG_V2 BIT(1) #define ADV7180_FLAG_MIPI_CSI2 BIT(2) #define ADV7180_FLAG_I2P BIT(3) +#define ADV7180_FLAG_TEST_PATTERN BIT(4) struct adv7180_chip_info { unsigned int flags; @@ -213,7 +214,6 @@ struct adv7180_state { struct gpio_desc *pwdn_gpio; struct gpio_desc *rst_gpio; v4l2_std_id curr_norm; - bool powered; bool streaming; u8 input; @@ -273,6 +273,38 @@ static int adv7180_vpp_write(struct adv7180_state *state, unsigned int reg, return i2c_smbus_write_byte_data(state->vpp_client, reg, value); } +static int adv7180_set_power(struct adv7180_state *state, bool on) +{ + u8 val; + int ret; + + if (on) + val = ADV7180_PWR_MAN_ON; + else + val = ADV7180_PWR_MAN_OFF; + + ret = adv7180_write(state, ADV7180_REG_PWR_MAN, val); + if (ret) + return ret; + + if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) { + if (on) { + adv7180_csi_write(state, 0xDE, 0x02); + adv7180_csi_write(state, 0xD2, 0xF7); + adv7180_csi_write(state, 0xD8, 0x65); + adv7180_csi_write(state, 0xE0, 0x09); + adv7180_csi_write(state, 0x2C, 0x00); + if (state->field == V4L2_FIELD_NONE) + adv7180_csi_write(state, 0x1D, 0x80); + adv7180_csi_write(state, 0x00, 0x00); + } else { + adv7180_csi_write(state, 0x00, 0x80); + } + } + + return 0; +} + static v4l2_std_id adv7180_std_to_v4l2(u8 status1) { /* in case V4L2_IN_ST_NO_SIGNAL */ @@ -356,32 +388,27 @@ static inline struct adv7180_state *to_state(struct v4l2_subdev *sd) static int adv7180_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) { struct adv7180_state *state = to_state(sd); - int err = mutex_lock_interruptible(&state->mutex); - if (err) - return err; - - if (state->streaming) { - err = -EBUSY; - goto unlock; - } + int ret; - err = adv7180_set_video_standard(state, - ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM); - if (err) - goto unlock; + guard(mutex)(&state->mutex); - msleep(100); - __adv7180_status(state, NULL, std); + /* + * We can't sample the standard if the device is streaming as that would + * interfere with the capture session as the VID_SEL reg is touched. + */ + if (state->streaming) + return -EBUSY; - err = v4l2_std_to_adv7180(state->curr_norm); - if (err < 0) - goto unlock; + /* Set the standard to autodetect PAL B/G/H/I/D, NTSC J or SECAM */ + ret = adv7180_set_video_standard(state, + ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM); + if (ret) + return ret; - err = adv7180_set_video_standard(state, err); + /* Allow some time for the autodetection to run. */ + msleep(100); -unlock: - mutex_unlock(&state->mutex); - return err; + return __adv7180_status(state, NULL, std); } static int adv7180_s_routing(struct v4l2_subdev *sd, u32 input, @@ -436,22 +463,18 @@ static int adv7180_program_std(struct adv7180_state *state) static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std) { struct adv7180_state *state = to_state(sd); - int ret = mutex_lock_interruptible(&state->mutex); + int ret; - if (ret) - return ret; + guard(mutex)(&state->mutex); /* Make sure we can support this std */ ret = v4l2_std_to_adv7180(std); if (ret < 0) - goto out; + return ret; state->curr_norm = std; - ret = adv7180_program_std(state); -out: - mutex_unlock(&state->mutex); - return ret; + return 0; } static int adv7180_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm) @@ -513,55 +536,6 @@ static void adv7180_set_reset_pin(struct adv7180_state *state, bool on) } } -static int adv7180_set_power(struct adv7180_state *state, bool on) -{ - u8 val; - int ret; - - if (on) - val = ADV7180_PWR_MAN_ON; - else - val = ADV7180_PWR_MAN_OFF; - - ret = adv7180_write(state, ADV7180_REG_PWR_MAN, val); - if (ret) - return ret; - - if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) { - if (on) { - adv7180_csi_write(state, 0xDE, 0x02); - adv7180_csi_write(state, 0xD2, 0xF7); - adv7180_csi_write(state, 0xD8, 0x65); - adv7180_csi_write(state, 0xE0, 0x09); - adv7180_csi_write(state, 0x2C, 0x00); - if (state->field == V4L2_FIELD_NONE) - adv7180_csi_write(state, 0x1D, 0x80); - adv7180_csi_write(state, 0x00, 0x00); - } else { - adv7180_csi_write(state, 0x00, 0x80); - } - } - - return 0; -} - -static int adv7180_s_power(struct v4l2_subdev *sd, int on) -{ - struct adv7180_state *state = to_state(sd); - int ret; - - ret = mutex_lock_interruptible(&state->mutex); - if (ret) - return ret; - - ret = adv7180_set_power(state, on); - if (ret == 0) - state->powered = on; - - mutex_unlock(&state->mutex); - return ret; -} - static const char * const test_pattern_menu[] = { "Single color", "Color bars", @@ -600,11 +574,11 @@ static int adv7180_s_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = to_adv7180_sd(ctrl); struct adv7180_state *state = to_state(sd); - int ret = mutex_lock_interruptible(&state->mutex); + int ret = 0; int val; - if (ret) - return ret; + lockdep_assert_held(&state->mutex); + val = ctrl->val; switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: @@ -646,7 +620,6 @@ static int adv7180_s_ctrl(struct v4l2_ctrl *ctrl) ret = -EINVAL; } - mutex_unlock(&state->mutex); return ret; } @@ -667,6 +640,7 @@ static const struct v4l2_ctrl_config adv7180_ctrl_fast_switch = { static int adv7180_init_controls(struct adv7180_state *state) { v4l2_ctrl_handler_init(&state->ctrl_hdl, 4); + state->ctrl_hdl.lock = &state->mutex; v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops, V4L2_CID_BRIGHTNESS, ADV7180_BRI_MIN, @@ -682,11 +656,15 @@ static int adv7180_init_controls(struct adv7180_state *state) ADV7180_HUE_MAX, 1, ADV7180_HUE_DEF); v4l2_ctrl_new_custom(&state->ctrl_hdl, &adv7180_ctrl_fast_switch, NULL); - v4l2_ctrl_new_std_menu_items(&state->ctrl_hdl, &adv7180_ctrl_ops, - V4L2_CID_TEST_PATTERN, - ARRAY_SIZE(test_pattern_menu) - 1, - 0, ARRAY_SIZE(test_pattern_menu) - 1, - test_pattern_menu); + if (state->chip_info->flags & ADV7180_FLAG_TEST_PATTERN) { + v4l2_ctrl_new_std_menu_items(&state->ctrl_hdl, + &adv7180_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(test_pattern_menu) - 1, + 0, + ARRAY_SIZE(test_pattern_menu) - 1, + test_pattern_menu); + } state->sd.ctrl_handler = &state->ctrl_hdl; if (state->ctrl_hdl.error) { @@ -695,7 +673,6 @@ static int adv7180_init_controls(struct adv7180_state *state) v4l2_ctrl_handler_free(&state->ctrl_hdl); return err; } - v4l2_ctrl_handler_setup(&state->ctrl_hdl); return 0; } @@ -807,12 +784,7 @@ static int adv7180_set_pad_format(struct v4l2_subdev *sd, ret = adv7180_mbus_fmt(sd, &format->format); if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - if (state->field != format->format.field) { - state->field = format->format.field; - adv7180_set_power(state, false); - adv7180_set_field_mode(state); - adv7180_set_power(state, true); - } + state->field = format->format.field; } else { framefmt = v4l2_subdev_state_get_format(sd_state, 0); *framefmt = format->format; @@ -863,24 +835,92 @@ static int adv7180_get_skip_frames(struct v4l2_subdev *sd, u32 *frames) return 0; } -static int adv7180_g_pixelaspect(struct v4l2_subdev *sd, struct v4l2_fract *aspect) +static int adv7180_g_tvnorms(struct v4l2_subdev *sd, v4l2_std_id *norm) { - struct adv7180_state *state = to_state(sd); + *norm = V4L2_STD_ALL; + return 0; +} - if (state->curr_norm & V4L2_STD_525_60) { - aspect->numerator = 11; - aspect->denominator = 10; - } else { - aspect->numerator = 54; - aspect->denominator = 59; - } +static int init_device(struct adv7180_state *state) +{ + int ret; - return 0; + lockdep_assert_held(&state->mutex); + + ret = adv7180_program_std(state); + if (ret) + return ret; + + adv7180_set_field_mode(state); + + __v4l2_ctrl_handler_setup(&state->ctrl_hdl); + + return ret; } -static int adv7180_g_tvnorms(struct v4l2_subdev *sd, v4l2_std_id *norm) +static int adv7180_reset_device(struct adv7180_state *state) { - *norm = V4L2_STD_ALL; + int ret; + + lockdep_assert_held(&state->mutex); + + adv7180_set_power_pin(state, true); + adv7180_set_reset_pin(state, false); + + adv7180_write(state, ADV7180_REG_PWR_MAN, ADV7180_PWR_MAN_RES); + usleep_range(5000, 10000); + + /* + * If the devices decoder is power on after reset, power off so the + * device can be configured. + */ + if (state->chip_info->flags & ADV7180_FLAG_RESET_POWERED) + adv7180_set_power(state, false); + + ret = state->chip_info->init(state); + if (ret) + return ret; + + ret = init_device(state); + if (ret) + return ret; + + /* register for interrupts */ + if (state->irq > 0) { + /* config the Interrupt pin to be active low */ + ret = adv7180_write(state, ADV7180_REG_ICONF1, + ADV7180_ICONF1_ACTIVE_LOW | + ADV7180_ICONF1_PSYNC_ONLY); + if (ret < 0) + return ret; + + ret = adv7180_write(state, ADV7180_REG_IMR1, 0); + if (ret < 0) + return ret; + + ret = adv7180_write(state, ADV7180_REG_IMR2, 0); + if (ret < 0) + return ret; + + /* enable AD change interrupts */ + ret = adv7180_write(state, ADV7180_REG_IMR3, + ADV7180_IRQ3_AD_CHANGE); + if (ret < 0) + return ret; + + ret = adv7180_write(state, ADV7180_REG_IMR4, 0); + if (ret < 0) + return ret; + } + + /* + * If the devices decoder is power on after reset, restore the power + * after configuration. This is to preserve the behavior of the driver, + * not doing this result in the first 35+ frames captured being garbage. + */ + if (state->chip_info->flags & ADV7180_FLAG_RESET_POWERED) + adv7180_set_power(state, true); + return 0; } @@ -889,18 +929,29 @@ static int adv7180_s_stream(struct v4l2_subdev *sd, int enable) struct adv7180_state *state = to_state(sd); int ret; - /* It's always safe to stop streaming, no need to take the lock */ - if (!enable) { - state->streaming = enable; - return 0; - } - /* Must wait until querystd released the lock */ - ret = mutex_lock_interruptible(&state->mutex); + guard(mutex)(&state->mutex); + + /* + * Always power off the decoder even if streaming is to be enabled, the + * decoder needs to be off for the device to be configured. + */ + ret = adv7180_set_power(state, false); if (ret) return ret; + + if (enable) { + ret = init_device(state); + if (ret) + return ret; + + ret = adv7180_set_power(state, true); + if (ret) + return ret; + } + state->streaming = enable; - mutex_unlock(&state->mutex); + return 0; } @@ -924,13 +975,11 @@ static const struct v4l2_subdev_video_ops adv7180_video_ops = { .querystd = adv7180_querystd, .g_input_status = adv7180_g_input_status, .s_routing = adv7180_s_routing, - .g_pixelaspect = adv7180_g_pixelaspect, .g_tvnorms = adv7180_g_tvnorms, .s_stream = adv7180_s_stream, }; static const struct v4l2_subdev_core_ops adv7180_core_ops = { - .s_power = adv7180_s_power, .subscribe_event = adv7180_subscribe_event, .unsubscribe_event = v4l2_event_subdev_unsubscribe, }; @@ -1221,7 +1270,7 @@ static const struct adv7180_chip_info adv7182_info = { }; static const struct adv7180_chip_info adv7280_info = { - .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_I2P, + .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_I2P | ADV7180_FLAG_TEST_PATTERN, .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) | BIT(ADV7182_INPUT_CVBS_AIN2) | BIT(ADV7182_INPUT_CVBS_AIN3) | @@ -1235,7 +1284,8 @@ static const struct adv7180_chip_info adv7280_info = { }; static const struct adv7180_chip_info adv7280_m_info = { - .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2 | ADV7180_FLAG_I2P, + .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2 | ADV7180_FLAG_I2P | + ADV7180_FLAG_TEST_PATTERN, .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) | BIT(ADV7182_INPUT_CVBS_AIN2) | BIT(ADV7182_INPUT_CVBS_AIN3) | @@ -1256,7 +1306,8 @@ static const struct adv7180_chip_info adv7280_m_info = { }; static const struct adv7180_chip_info adv7281_info = { - .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2, + .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2 | + ADV7180_FLAG_TEST_PATTERN, .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) | BIT(ADV7182_INPUT_CVBS_AIN2) | BIT(ADV7182_INPUT_CVBS_AIN7) | @@ -1271,7 +1322,8 @@ static const struct adv7180_chip_info adv7281_info = { }; static const struct adv7180_chip_info adv7281_m_info = { - .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2, + .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2 | + ADV7180_FLAG_TEST_PATTERN, .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) | BIT(ADV7182_INPUT_CVBS_AIN2) | BIT(ADV7182_INPUT_CVBS_AIN3) | @@ -1291,7 +1343,8 @@ static const struct adv7180_chip_info adv7281_m_info = { }; static const struct adv7180_chip_info adv7281_ma_info = { - .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2, + .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2 | + ADV7180_FLAG_TEST_PATTERN, .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) | BIT(ADV7182_INPUT_CVBS_AIN2) | BIT(ADV7182_INPUT_CVBS_AIN3) | @@ -1316,7 +1369,7 @@ static const struct adv7180_chip_info adv7281_ma_info = { }; static const struct adv7180_chip_info adv7282_info = { - .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_I2P, + .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_I2P | ADV7180_FLAG_TEST_PATTERN, .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) | BIT(ADV7182_INPUT_CVBS_AIN2) | BIT(ADV7182_INPUT_CVBS_AIN7) | @@ -1331,7 +1384,8 @@ static const struct adv7180_chip_info adv7282_info = { }; static const struct adv7180_chip_info adv7282_m_info = { - .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2 | ADV7180_FLAG_I2P, + .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2 | ADV7180_FLAG_I2P | + ADV7180_FLAG_TEST_PATTERN, .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) | BIT(ADV7182_INPUT_CVBS_AIN2) | BIT(ADV7182_INPUT_CVBS_AIN3) | @@ -1349,62 +1403,6 @@ static const struct adv7180_chip_info adv7282_m_info = { .select_input = adv7182_select_input, }; -static int init_device(struct adv7180_state *state) -{ - int ret; - - mutex_lock(&state->mutex); - - adv7180_set_power_pin(state, true); - adv7180_set_reset_pin(state, false); - - adv7180_write(state, ADV7180_REG_PWR_MAN, ADV7180_PWR_MAN_RES); - usleep_range(5000, 10000); - - ret = state->chip_info->init(state); - if (ret) - goto out_unlock; - - ret = adv7180_program_std(state); - if (ret) - goto out_unlock; - - adv7180_set_field_mode(state); - - /* register for interrupts */ - if (state->irq > 0) { - /* config the Interrupt pin to be active low */ - ret = adv7180_write(state, ADV7180_REG_ICONF1, - ADV7180_ICONF1_ACTIVE_LOW | - ADV7180_ICONF1_PSYNC_ONLY); - if (ret < 0) - goto out_unlock; - - ret = adv7180_write(state, ADV7180_REG_IMR1, 0); - if (ret < 0) - goto out_unlock; - - ret = adv7180_write(state, ADV7180_REG_IMR2, 0); - if (ret < 0) - goto out_unlock; - - /* enable AD change interrupts interrupts */ - ret = adv7180_write(state, ADV7180_REG_IMR3, - ADV7180_IRQ3_AD_CHANGE); - if (ret < 0) - goto out_unlock; - - ret = adv7180_write(state, ADV7180_REG_IMR4, 0); - if (ret < 0) - goto out_unlock; - } - -out_unlock: - mutex_unlock(&state->mutex); - - return ret; -} - static int adv7180_probe(struct i2c_client *client) { struct device_node *np = client->dev.of_node; @@ -1463,10 +1461,7 @@ static int adv7180_probe(struct i2c_client *client) state->irq = client->irq; mutex_init(&state->mutex); state->curr_norm = V4L2_STD_NTSC; - if (state->chip_info->flags & ADV7180_FLAG_RESET_POWERED) - state->powered = true; - else - state->powered = false; + state->input = 0; sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &adv7180_ops); @@ -1483,7 +1478,9 @@ static int adv7180_probe(struct i2c_client *client) if (ret) goto err_free_ctrl; - ret = init_device(state); + mutex_lock(&state->mutex); + ret = adv7180_reset_device(state); + mutex_unlock(&state->mutex); if (ret) goto err_media_entity_cleanup; @@ -1555,6 +1552,8 @@ static int adv7180_suspend(struct device *dev) struct v4l2_subdev *sd = dev_get_drvdata(dev); struct adv7180_state *state = to_state(sd); + guard(mutex)(&state->mutex); + return adv7180_set_power(state, false); } @@ -1564,13 +1563,18 @@ static int adv7180_resume(struct device *dev) struct adv7180_state *state = to_state(sd); int ret; - ret = init_device(state); + guard(mutex)(&state->mutex); + + ret = adv7180_reset_device(state); if (ret < 0) return ret; - ret = adv7180_set_power(state, state->powered); - if (ret) - return ret; + /* If we were streaming when suspending, start decoder. */ + if (state->streaming) { + ret = adv7180_set_power(state, true); + if (ret) + return ret; + } return 0; } diff --git a/drivers/media/i2c/adv748x/adv748x-afe.c b/drivers/media/i2c/adv748x/adv748x-afe.c index 5edb3295dc58..678199196b84 100644 --- a/drivers/media/i2c/adv748x/adv748x-afe.c +++ b/drivers/media/i2c/adv748x/adv748x-afe.c @@ -161,22 +161,6 @@ int adv748x_afe_s_input(struct adv748x_afe *afe, unsigned int input) return sdp_write(state, ADV748X_SDP_INSEL, input); } -static int adv748x_afe_g_pixelaspect(struct v4l2_subdev *sd, - struct v4l2_fract *aspect) -{ - struct adv748x_afe *afe = adv748x_sd_to_afe(sd); - - if (afe->curr_norm & V4L2_STD_525_60) { - aspect->numerator = 11; - aspect->denominator = 10; - } else { - aspect->numerator = 54; - aspect->denominator = 59; - } - - return 0; -} - /* ----------------------------------------------------------------------------- * v4l2_subdev_video_ops */ @@ -307,7 +291,6 @@ static const struct v4l2_subdev_video_ops adv748x_afe_video_ops = { .g_tvnorms = adv748x_afe_g_tvnorms, .g_input_status = adv748x_afe_g_input_status, .s_stream = adv748x_afe_s_stream, - .g_pixelaspect = adv748x_afe_g_pixelaspect, }; /* ----------------------------------------------------------------------------- diff --git a/drivers/media/i2c/adv748x/adv748x-hdmi.c b/drivers/media/i2c/adv748x/adv748x-hdmi.c index a4db9bae5f79..b154dea29ba2 100644 --- a/drivers/media/i2c/adv748x/adv748x-hdmi.c +++ b/drivers/media/i2c/adv748x/adv748x-hdmi.c @@ -382,19 +382,9 @@ done: return ret; } -static int adv748x_hdmi_g_pixelaspect(struct v4l2_subdev *sd, - struct v4l2_fract *aspect) -{ - aspect->numerator = 1; - aspect->denominator = 1; - - return 0; -} - static const struct v4l2_subdev_video_ops adv748x_video_ops_hdmi = { .g_input_status = adv748x_hdmi_g_input_status, .s_stream = adv748x_hdmi_s_stream, - .g_pixelaspect = adv748x_hdmi_g_pixelaspect, }; /* ----------------------------------------------------------------------------- diff --git a/drivers/media/i2c/adv748x/adv748x.h b/drivers/media/i2c/adv748x/adv748x.h index 9bc0121d0eff..2c1db5968af8 100644 --- a/drivers/media/i2c/adv748x/adv748x.h +++ b/drivers/media/i2c/adv748x/adv748x.h @@ -320,7 +320,7 @@ struct adv748x_state { /* Free run pattern select */ #define ADV748X_SDP_FRP 0x14 -#define ADV748X_SDP_FRP_MASK GENMASK(3, 1) +#define ADV748X_SDP_FRP_MASK GENMASK(2, 0) /* Saturation */ #define ADV748X_SDP_SD_SAT_U 0xe3 /* user_map_rw_reg_e3 */ diff --git a/drivers/media/i2c/adv7511-v4l2.c b/drivers/media/i2c/adv7511-v4l2.c index 4036972af3a6..853c7806de92 100644 --- a/drivers/media/i2c/adv7511-v4l2.c +++ b/drivers/media/i2c/adv7511-v4l2.c @@ -1370,9 +1370,9 @@ static int adv7511_set_fmt(struct v4l2_subdev *sd, case V4L2_COLORSPACE_BT2020: c = HDMI_COLORIMETRY_EXTENDED; if (y && format->format.ycbcr_enc == V4L2_YCBCR_ENC_BT2020_CONST_LUM) - ec = 5; /* Not yet available in hdmi.h */ + ec = HDMI_EXTENDED_COLORIMETRY_BT2020_CONST_LUM; else - ec = 6; /* Not yet available in hdmi.h */ + ec = HDMI_EXTENDED_COLORIMETRY_BT2020; break; default: break; @@ -1664,7 +1664,9 @@ static bool adv7511_check_edid_status(struct v4l2_subdev *sd) if (!err) { adv7511_dbg_dump_edid(2, debug, sd, segment, &state->edid.data[segment * 256]); if (segment == 0) { - state->edid.blocks = state->edid.data[0x7e] + 1; + state->edid.blocks = + v4l2_num_edid_blocks(state->edid.data, + EDID_MAX_SEGM * 2); v4l2_dbg(1, debug, sd, "%s: %d blocks in total\n", __func__, state->edid.blocks); } @@ -1682,7 +1684,7 @@ static bool adv7511_check_edid_status(struct v4l2_subdev *sd) /* one more segment read ok */ state->edid.segments = segment + 1; v4l2_ctrl_s_ctrl(state->have_edid0_ctrl, 0x1); - if (((state->edid.data[0x7e] >> 1) + 1) > state->edid.segments) { + if (state->edid.blocks > state->edid.segments * 2) { /* Request next EDID segment */ v4l2_dbg(1, debug, sd, "%s: request segment %d\n", __func__, state->edid.segments); adv7511_wr(sd, 0xc9, 0xf); diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index e271782b7b70..516553fb17e9 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -42,7 +42,7 @@ module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "debug level (0-2)"); MODULE_DESCRIPTION("Analog Devices ADV7604/10/11/12 video decoder driver"); -MODULE_AUTHOR("Hans Verkuil <hansverk@cisco.com>"); +MODULE_AUTHOR("Hans Verkuil <hverkuil@kernel.org>"); MODULE_AUTHOR("Mats Randgaard <mats.randgaard@cisco.com>"); MODULE_LICENSE("GPL"); @@ -2448,8 +2448,8 @@ static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) } cec_s_phys_addr(state->cec_adap, parent_pa, false); - /* enable hotplug after 100 ms */ - schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 10); + /* enable hotplug after 143 ms */ + schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 7); return 0; } @@ -3670,7 +3670,7 @@ static int adv76xx_probe(struct i2c_client *client) err = media_entity_pads_init(&sd->entity, state->source_pad + 1, state->pads); if (err) - goto err_work_queues; + goto err_i2c; /* Configure regmaps */ err = configure_regmaps(state); @@ -3711,8 +3711,6 @@ static int adv76xx_probe(struct i2c_client *client) err_entity: media_entity_cleanup(&sd->entity); -err_work_queues: - cancel_delayed_work(&state->delayed_work_enable_hotplug); err_i2c: adv76xx_unregister_clients(state); err_hdl: diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index 5545cd23e113..ea6966c0605e 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -38,7 +38,7 @@ module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "debug level (0-2)"); MODULE_DESCRIPTION("Analog Devices ADV7842 video decoder driver"); -MODULE_AUTHOR("Hans Verkuil <hansverk@cisco.com>"); +MODULE_AUTHOR("Hans Verkuil <hverkuil@kernel.org>"); MODULE_AUTHOR("Martin Bugge <marbugge@cisco.com>"); MODULE_LICENSE("GPL"); @@ -2699,6 +2699,7 @@ static int adv7842_cp_log_status(struct v4l2_subdev *sd) /* CP block */ struct adv7842_state *state = to_state(sd); struct v4l2_dv_timings timings; + int temp; u8 reg_io_0x02 = io_read(sd, 0x02); u8 reg_io_0x21 = io_read(sd, 0x21); u8 reg_rep_0x77 = rep_read(sd, 0x77); @@ -2821,8 +2822,9 @@ static int adv7842_cp_log_status(struct v4l2_subdev *sd) (((reg_io_0x02 >> 2) & 0x01) ^ (reg_io_0x02 & 0x01)) ? "(16-235)" : "(0-255)", (reg_io_0x02 & 0x08) ? "enabled" : "disabled"); + temp = cp_read(sd, 0xf4) >> 4; v4l2_info(sd, "Color space conversion: %s\n", - csc_coeff_sel_rb[cp_read(sd, 0xf4) >> 4]); + temp < 0 ? "" : csc_coeff_sel_rb[temp]); if (!is_digital_input(sd)) return 0; @@ -2852,8 +2854,9 @@ static int adv7842_cp_log_status(struct v4l2_subdev *sd) hdmi_read(sd, 0x5f)); v4l2_info(sd, "AV Mute: %s\n", (hdmi_read(sd, 0x04) & 0x40) ? "on" : "off"); + temp = hdmi_read(sd, 0x0b) >> 6; v4l2_info(sd, "Deep color mode: %s\n", - deep_color_mode_txt[hdmi_read(sd, 0x0b) >> 6]); + temp < 0 ? "" : deep_color_mode_txt[temp]); adv7842_log_infoframes(sd); @@ -3466,8 +3469,8 @@ static struct i2c_client *adv7842_dummy_client(struct v4l2_subdev *sd, const cha cp = i2c_new_dummy_device(client->adapter, io_read(sd, io_reg) >> 1); if (IS_ERR(cp)) { - v4l2_err(sd, "register %s on i2c addr 0x%x failed with %ld\n", - desc, addr, PTR_ERR(cp)); + v4l2_err(sd, "register %s on i2c addr 0x%x failed with %pe\n", + desc, addr, cp); cp = NULL; } @@ -3626,7 +3629,7 @@ static int adv7842_probe(struct i2c_client *client) err = media_entity_pads_init(&sd->entity, ADV7842_PAD_SOURCE + 1, state->pads); if (err) - goto err_work_queues; + goto err_i2c; err = adv7842_core_init(sd); if (err) @@ -3647,8 +3650,6 @@ static int adv7842_probe(struct i2c_client *client) err_entity: media_entity_cleanup(&sd->entity); -err_work_queues: - cancel_delayed_work(&state->delayed_work_enable_hotplug); err_i2c: adv7842_unregister_clients(sd); err_hdl: diff --git a/drivers/media/i2c/alvium-csi2.c b/drivers/media/i2c/alvium-csi2.c index 05b708bd0a64..1f088acecf36 100644 --- a/drivers/media/i2c/alvium-csi2.c +++ b/drivers/media/i2c/alvium-csi2.c @@ -1841,7 +1841,6 @@ static int alvium_s_stream(struct v4l2_subdev *sd, int enable) } else { alvium_set_stream_mipi(alvium, enable); - pm_runtime_mark_last_busy(&client->dev); pm_runtime_put_autosuspend(&client->dev); } diff --git a/drivers/media/i2c/ar0521.c b/drivers/media/i2c/ar0521.c index 24873149096c..f156058500e3 100644 --- a/drivers/media/i2c/ar0521.c +++ b/drivers/media/i2c/ar0521.c @@ -1077,11 +1077,10 @@ static int ar0521_probe(struct i2c_client *client) } /* Get master clock (extclk) */ - sensor->extclk = devm_clk_get(dev, "extclk"); - if (IS_ERR(sensor->extclk)) { - dev_err(dev, "failed to get extclk\n"); - return PTR_ERR(sensor->extclk); - } + sensor->extclk = devm_v4l2_sensor_clk_get(dev, "extclk"); + if (IS_ERR(sensor->extclk)) + return dev_err_probe(dev, PTR_ERR(sensor->extclk), + "failed to get extclk\n"); sensor->extclk_freq = clk_get_rate(sensor->extclk); @@ -1110,8 +1109,8 @@ static int ar0521_probe(struct i2c_client *client) ar0521_supply_names[cnt]); if (IS_ERR(supply)) { - dev_info(dev, "no %s regulator found: %li\n", - ar0521_supply_names[cnt], PTR_ERR(supply)); + dev_info(dev, "no %s regulator found: %pe\n", + ar0521_supply_names[cnt], supply); return PTR_ERR(supply); } sensor->supplies[cnt] = supply; diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c index cf8858cb13d4..4eb83636e102 100644 --- a/drivers/media/i2c/ccs-pll.c +++ b/drivers/media/i2c/ccs-pll.c @@ -75,11 +75,11 @@ static const char *pll_string(unsigned int which) #define PLL_FL(f) CCS_PLL_FLAG_##f -static void print_pll(struct device *dev, struct ccs_pll *pll) +static void print_pll(struct device *dev, const struct ccs_pll *pll) { const struct { - struct ccs_pll_branch_fr *fr; - struct ccs_pll_branch_bk *bk; + const struct ccs_pll_branch_fr *fr; + const struct ccs_pll_branch_bk *bk; unsigned int which; } branches[] = { { &pll->vt_fr, &pll->vt_bk, PLL_VT }, @@ -123,10 +123,15 @@ static void print_pll(struct device *dev, struct ccs_pll *pll) pll->pixel_rate_pixel_array); dev_dbg(dev, "pixel rate on CSI-2 bus:\t%u\n", pll->pixel_rate_csi); +} - dev_dbg(dev, "flags%s%s%s%s%s%s%s%s%s\n", +static void print_pll_flags(struct device *dev, struct ccs_pll *pll) +{ + dev_dbg(dev, "PLL flags%s%s%s%s%s%s%s%s%s%s%s\n", + pll->flags & PLL_FL(OP_PIX_CLOCK_PER_LANE) ? " op-pix-clock-per-lane" : "", + pll->flags & PLL_FL(EVEN_PLL_MULTIPLIER) ? " even-pll-multiplier" : "", + pll->flags & PLL_FL(NO_OP_CLOCKS) ? " no-op-clocks" : "", pll->flags & PLL_FL(LANE_SPEED_MODEL) ? " lane-speed" : "", - pll->flags & PLL_FL(LINK_DECOUPLED) ? " link-decoupled" : "", pll->flags & PLL_FL(EXT_IP_PLL_DIVIDER) ? " ext-ip-pll-divider" : "", pll->flags & PLL_FL(FLEXIBLE_OP_PIX_CLK_DIV) ? @@ -150,10 +155,10 @@ static u32 op_pix_ddr(u32 flags) static int check_fr_bounds(struct device *dev, const struct ccs_pll_limits *lim, - struct ccs_pll *pll, unsigned int which) + const struct ccs_pll *pll, unsigned int which) { const struct ccs_pll_branch_limits_fr *lim_fr; - struct ccs_pll_branch_fr *pll_fr; + const struct ccs_pll_branch_fr *pll_fr; const char *s = pll_string(which); int rval; @@ -190,10 +195,10 @@ static int check_fr_bounds(struct device *dev, static int check_bk_bounds(struct device *dev, const struct ccs_pll_limits *lim, - struct ccs_pll *pll, unsigned int which) + const struct ccs_pll *pll, unsigned int which) { const struct ccs_pll_branch_limits_bk *lim_bk; - struct ccs_pll_branch_bk *pll_bk; + const struct ccs_pll_branch_bk *pll_bk; const char *s = pll_string(which); int rval; @@ -230,7 +235,7 @@ static int check_bk_bounds(struct device *dev, return rval; } -static int check_ext_bounds(struct device *dev, struct ccs_pll *pll) +static int check_ext_bounds(struct device *dev, const struct ccs_pll *pll) { if (!(pll->flags & CCS_PLL_FLAG_FIFO_DERATING) && pll->pixel_rate_pixel_array > pll->pixel_rate_csi) { @@ -311,14 +316,24 @@ __ccs_pll_calculate_vt_tree(struct device *dev, more_mul *= DIV_ROUND_UP(lim_fr->min_pll_multiplier, mul * more_mul); dev_dbg(dev, "more_mul2: %u\n", more_mul); - pll_fr->pll_multiplier = mul * more_mul; + if (pll->flags & CCS_PLL_FLAG_EVEN_PLL_MULTIPLIER && + (mul & 1) && (more_mul & 1)) + more_mul <<= 1; - if (pll_fr->pll_multiplier * pll_fr->pll_ip_clk_freq_hz > - lim_fr->max_pll_op_clk_freq_hz) + pll_fr->pll_multiplier = mul * more_mul; + if (pll_fr->pll_multiplier > lim_fr->max_pll_multiplier) { + dev_dbg(dev, "pll multiplier %u too high\n", + pll_fr->pll_multiplier); return -EINVAL; + } pll_fr->pll_op_clk_freq_hz = pll_fr->pll_ip_clk_freq_hz * pll_fr->pll_multiplier; + if (pll_fr->pll_op_clk_freq_hz > lim_fr->max_pll_op_clk_freq_hz) { + dev_dbg(dev, "too high OP clock %u\n", + pll_fr->pll_op_clk_freq_hz); + return -EINVAL; + } vt_div = div * more_mul; @@ -397,6 +412,8 @@ static int ccs_pll_calculate_vt_tree(struct device *dev, min_pre_pll_clk_div = max_t(u16, min_pre_pll_clk_div, pll->ext_clk_freq_hz / lim_fr->max_pll_ip_clk_freq_hz); + if (!(pll->flags & CCS_PLL_FLAG_EXT_IP_PLL_DIVIDER)) + min_pre_pll_clk_div = clk_div_even(min_pre_pll_clk_div); dev_dbg(dev, "vt min/max_pre_pll_clk_div: %u,%u\n", min_pre_pll_clk_div, max_pre_pll_clk_div); @@ -432,10 +449,11 @@ static int ccs_pll_calculate_vt_tree(struct device *dev, return 0; } + dev_dbg(dev, "unable to compute VT pre_pll divisor\n"); return -EINVAL; } -static void +static int ccs_pll_calculate_vt(struct device *dev, const struct ccs_pll_limits *lim, const struct ccs_pll_branch_limits_bk *op_lim_bk, struct ccs_pll *pll, struct ccs_pll_branch_fr *pll_fr, @@ -558,6 +576,8 @@ ccs_pll_calculate_vt(struct device *dev, const struct ccs_pll_limits *lim, if (best_pix_div < SHRT_MAX >> 1) break; } + if (best_pix_div == SHRT_MAX >> 1) + return -EINVAL; pll->vt_bk.sys_clk_div = DIV_ROUND_UP(vt_div, best_pix_div); pll->vt_bk.pix_clk_div = best_pix_div; @@ -570,6 +590,8 @@ ccs_pll_calculate_vt(struct device *dev, const struct ccs_pll_limits *lim, out_calc_pixel_rate: pll->pixel_rate_pixel_array = pll->vt_bk.pix_clk_freq_hz * pll->vt_lanes; + + return 0; } /* @@ -659,6 +681,10 @@ ccs_pll_calculate_op(struct device *dev, const struct ccs_pll_limits *lim, if (!is_one_or_even(i)) i <<= 1; + if (pll->flags & CCS_PLL_FLAG_EVEN_PLL_MULTIPLIER && + mul & 1 && i & 1) + i <<= 1; + dev_dbg(dev, "final more_mul: %u\n", i); if (i > more_mul_max) { dev_dbg(dev, "final more_mul is bad, max %u\n", more_mul_max); @@ -716,6 +742,8 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim, u32 i; int rval = -EINVAL; + print_pll_flags(dev, pll); + if (!(pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL)) { pll->op_lanes = 1; pll->vt_lanes = 1; @@ -792,7 +820,7 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim, op_lim_fr->min_pre_pll_clk_div, op_lim_fr->max_pre_pll_clk_div); max_op_pre_pll_clk_div = min_t(u16, op_lim_fr->max_pre_pll_clk_div, - clk_div_even(pll->ext_clk_freq_hz / + DIV_ROUND_UP(pll->ext_clk_freq_hz, op_lim_fr->min_pll_ip_clk_freq_hz)); min_op_pre_pll_clk_div = max_t(u16, op_lim_fr->min_pre_pll_clk_div, @@ -815,6 +843,8 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim, one_or_more( DIV_ROUND_UP(op_lim_fr->max_pll_op_clk_freq_hz, pll->ext_clk_freq_hz)))); + if (!(pll->flags & CCS_PLL_FLAG_EXT_IP_PLL_DIVIDER)) + min_op_pre_pll_clk_div = clk_div_even(min_op_pre_pll_clk_div); dev_dbg(dev, "pll_op check: min / max op_pre_pll_clk_div: %u / %u\n", min_op_pre_pll_clk_div, max_op_pre_pll_clk_div); @@ -843,8 +873,10 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim, if (pll->flags & CCS_PLL_FLAG_DUAL_PLL) break; - ccs_pll_calculate_vt(dev, lim, op_lim_bk, pll, op_pll_fr, - op_pll_bk, cphy, phy_const); + rval = ccs_pll_calculate_vt(dev, lim, op_lim_bk, pll, op_pll_fr, + op_pll_bk, cphy, phy_const); + if (rval) + continue; rval = check_bk_bounds(dev, lim, pll, PLL_VT); if (rval) @@ -857,8 +889,7 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim, } if (rval) { - dev_dbg(dev, "unable to compute pre_pll divisor\n"); - + dev_dbg(dev, "unable to compute OP pre_pll divisor\n"); return rval; } diff --git a/drivers/media/i2c/ccs-pll.h b/drivers/media/i2c/ccs-pll.h index 6eb1b1c68e1e..e22903931e72 100644 --- a/drivers/media/i2c/ccs-pll.h +++ b/drivers/media/i2c/ccs-pll.h @@ -18,19 +18,40 @@ #define CCS_PLL_BUS_TYPE_CSI2_DPHY 0x00 #define CCS_PLL_BUS_TYPE_CSI2_CPHY 0x01 -/* Old SMIA and implementation specific flags */ -/* op pix clock is for all lanes in total normally */ +/* Old SMIA and implementation specific flags. */ +/* OP PIX clock is for all lanes in total normally. */ #define CCS_PLL_FLAG_OP_PIX_CLOCK_PER_LANE BIT(0) -#define CCS_PLL_FLAG_NO_OP_CLOCKS BIT(1) +/* If set, the PLL multipliers are required to be even. */ +#define CCS_PLL_FLAG_EVEN_PLL_MULTIPLIER BIT(3) + /* CCS PLL flags */ + +/* The sensor doesn't have OP clocks at all. */ +#define CCS_PLL_FLAG_NO_OP_CLOCKS BIT(1) +/* System speed model if this flag is unset. */ #define CCS_PLL_FLAG_LANE_SPEED_MODEL BIT(2) -#define CCS_PLL_FLAG_LINK_DECOUPLED BIT(3) +/* If set, the pre-PLL divider may have odd values, too. */ #define CCS_PLL_FLAG_EXT_IP_PLL_DIVIDER BIT(4) +/* + * If set, the OP PIX clock doesn't have to exactly match with data rate, it may + * be higher. See "OP Domain Formulas" in MIPI CCS 1.1 spec. + */ #define CCS_PLL_FLAG_FLEXIBLE_OP_PIX_CLK_DIV BIT(5) +/* If set, the VT domain may run faster than the OP domain. */ #define CCS_PLL_FLAG_FIFO_DERATING BIT(6) +/* If set, the VT domain may run slower than the OP domain. */ #define CCS_PLL_FLAG_FIFO_OVERRATING BIT(7) +/* If set, the PLL tree has two PLLs instead of one. */ #define CCS_PLL_FLAG_DUAL_PLL BIT(8) +/* + * If set, the OP SYS clock is a dual data rate clock, transferring two bits per + * cycle instead of one. + */ #define CCS_PLL_FLAG_OP_SYS_DDR BIT(9) +/* + * If set, the OP PIX clock is a dual data rate clock, transferring two pixels + * per cycle instead of one. + */ #define CCS_PLL_FLAG_OP_PIX_DDR BIT(10) /** diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index e1ae0f9fad43..f8523140784c 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -787,10 +787,8 @@ static int ccs_set_ctrl(struct v4l2_ctrl *ctrl) rval = -EINVAL; } - if (pm_status > 0) { - pm_runtime_mark_last_busy(&client->dev); + if (pm_status > 0) pm_runtime_put_autosuspend(&client->dev); - } return rval; } @@ -1354,8 +1352,10 @@ static int ccs_change_cci_addr(struct ccs_sensor *sensor) client->addr = sensor->hwcfg.i2c_addr_dfl; - rval = ccs_write(sensor, CCI_ADDRESS_CTRL, - sensor->hwcfg.i2c_addr_alt << 1); + rval = read_poll_timeout(ccs_write, rval, !rval, CCS_RESET_DELAY_US, + CCS_RESET_TIMEOUT_US, false, sensor, + CCI_ADDRESS_CTRL, + sensor->hwcfg.i2c_addr_alt << 1); if (rval) return rval; @@ -1575,44 +1575,38 @@ static int ccs_power_on(struct device *dev) if (ccsdev->flags & CCS_DEVICE_FLAG_IS_SMIA) sleep = SMIAPP_RESET_DELAY(sensor->hwcfg.ext_clk); else - sleep = 5000; + sleep = CCS_RESET_DELAY_US; usleep_range(sleep, sleep); } /* - * Failures to respond to the address change command have been noticed. - * Those failures seem to be caused by the sensor requiring a longer - * boot time than advertised. An additional 10ms delay seems to work - * around the issue, but the SMIA++ I2C write retry hack makes the delay - * unnecessary. The failures need to be investigated to find a proper - * fix, and a delay will likely need to be added here if the I2C write - * retry hack is reverted before the root cause of the boot time issue - * is found. + * Some devices take longer than the spec-defined time to respond + * after reset. Try until some time has passed before flagging it + * an error. */ - if (!sensor->reset && !sensor->xshutdown) { - u8 retry = 100; u32 reset; - rval = ccs_write(sensor, SOFTWARE_RESET, CCS_SOFTWARE_RESET_ON); + rval = read_poll_timeout(ccs_write, rval, !rval, + CCS_RESET_DELAY_US, + CCS_RESET_TIMEOUT_US, + false, sensor, SOFTWARE_RESET, + CCS_SOFTWARE_RESET_ON); if (rval < 0) { dev_err(dev, "software reset failed\n"); goto out_cci_addr_fail; } - do { - rval = ccs_read(sensor, SOFTWARE_RESET, &reset); - reset = !rval && reset == CCS_SOFTWARE_RESET_OFF; - if (reset) - break; - - usleep_range(1000, 2000); - } while (--retry); - - if (!reset) { - dev_err(dev, "software reset failed\n"); - rval = -EIO; + rval = read_poll_timeout(ccs_read, rval, + !rval && + reset == CCS_SOFTWARE_RESET_OFF, + CCS_RESET_DELAY_US, + CCS_RESET_TIMEOUT_US, false, sensor, + SOFTWARE_RESET, &reset); + if (rval < 0) { + dev_err_probe(dev, rval, + "failed to respond after reset\n"); goto out_cci_addr_fail; } } @@ -1918,7 +1912,6 @@ static int ccs_set_stream(struct v4l2_subdev *subdev, int enable) if (!enable) { ccs_stop_streaming(sensor); sensor->streaming = false; - pm_runtime_mark_last_busy(&client->dev); pm_runtime_put_autosuspend(&client->dev); return 0; @@ -1933,7 +1926,6 @@ static int ccs_set_stream(struct v4l2_subdev *subdev, int enable) rval = ccs_start_streaming(sensor); if (rval < 0) { sensor->streaming = false; - pm_runtime_mark_last_busy(&client->dev); pm_runtime_put_autosuspend(&client->dev); } @@ -2681,7 +2673,6 @@ nvm_show(struct device *dev, struct device_attribute *attr, char *buf) return -ENODEV; } - pm_runtime_mark_last_busy(&client->dev); pm_runtime_put_autosuspend(&client->dev); /* @@ -2857,10 +2848,6 @@ static int ccs_identify_module(struct ccs_sensor *sensor) break; } - if (i >= ARRAY_SIZE(ccs_module_idents)) - dev_warn(&client->dev, - "no quirks for this module; let's hope it's fully compliant\n"); - dev_dbg(&client->dev, "the sensor is called %s\n", minfo->name); return 0; @@ -3131,8 +3118,6 @@ static int ccs_get_hwconfig(struct ccs_sensor *sensor, struct device *dev) rval = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", &hwcfg->ext_clk); - if (rval) - dev_info(dev, "can't get clock-frequency\n"); dev_dbg(dev, "clk %u, mode %u\n", hwcfg->ext_clk, hwcfg->csi_signalling_mode); @@ -3252,8 +3237,8 @@ static int ccs_probe(struct i2c_client *client) dev_info(&client->dev, "no clock defined, continuing...\n"); sensor->ext_clk = NULL; } else if (IS_ERR(sensor->ext_clk)) { - dev_err(&client->dev, "could not get clock (%ld)\n", - PTR_ERR(sensor->ext_clk)); + dev_err(&client->dev, "could not get clock (%pe)\n", + sensor->ext_clk); return -EPROBE_DEFER; } @@ -3309,8 +3294,8 @@ static int ccs_probe(struct i2c_client *client) sensor->regmap = devm_cci_regmap_init_i2c(client, 16); if (IS_ERR(sensor->regmap)) { - dev_err(&client->dev, "can't initialise CCI (%ld)\n", - PTR_ERR(sensor->regmap)); + dev_err(&client->dev, "can't initialise CCI (%pe)\n", + sensor->regmap); return PTR_ERR(sensor->regmap); } @@ -3335,9 +3320,11 @@ static int ccs_probe(struct i2c_client *client) rval = request_firmware(&fw, filename, &client->dev); if (!rval) { - ccs_data_parse(&sensor->sdata, fw->data, fw->size, &client->dev, - true); + rval = ccs_data_parse(&sensor->sdata, fw->data, fw->size, + &client->dev, true); release_firmware(fw); + if (rval) + goto out_power_off; } if (!(ccsdev->flags & CCS_DEVICE_FLAG_IS_SMIA) || @@ -3351,9 +3338,11 @@ static int ccs_probe(struct i2c_client *client) rval = request_firmware(&fw, filename, &client->dev); if (!rval) { - ccs_data_parse(&sensor->mdata, fw->data, fw->size, - &client->dev, true); + rval = ccs_data_parse(&sensor->mdata, fw->data, + fw->size, &client->dev, true); release_firmware(fw); + if (rval) + goto out_release_sdata; } } @@ -3447,7 +3436,6 @@ static int ccs_probe(struct i2c_client *client) CCS_LIM(sensor, NUM_OF_VT_LANES) + 1; sensor->pll.op_lanes = CCS_LIM(sensor, NUM_OF_OP_LANES) + 1; - sensor->pll.flags |= CCS_PLL_FLAG_LINK_DECOUPLED; } else { sensor->pll.vt_lanes = sensor->pll.csi2.lanes; sensor->pll.op_lanes = sensor->pll.csi2.lanes; @@ -3562,19 +3550,20 @@ static int ccs_probe(struct i2c_client *client) out_disable_runtime_pm: pm_runtime_put_noidle(&client->dev); pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); out_cleanup: ccs_cleanup(sensor); +out_free_ccs_limits: + kfree(sensor->ccs_limits); + out_release_mdata: kvfree(sensor->mdata.backing); out_release_sdata: kvfree(sensor->sdata.backing); -out_free_ccs_limits: - kfree(sensor->ccs_limits); - out_power_off: ccs_power_off(&client->dev); mutex_destroy(&sensor->mutex); @@ -3591,9 +3580,10 @@ static void ccs_remove(struct i2c_client *client) v4l2_async_unregister_subdev(subdev); pm_runtime_disable(&client->dev); - if (!pm_runtime_status_suspended(&client->dev)) + if (!pm_runtime_status_suspended(&client->dev)) { ccs_power_off(&client->dev); - pm_runtime_set_suspended(&client->dev); + pm_runtime_set_suspended(&client->dev); + } for (i = 0; i < sensor->ssds_used; i++) v4l2_device_unregister_subdev(&sensor->ssds[i].sd); diff --git a/drivers/media/i2c/ccs/ccs-data.c b/drivers/media/i2c/ccs/ccs-data.c index 08400edf77ce..f469afcea680 100644 --- a/drivers/media/i2c/ccs/ccs-data.c +++ b/drivers/media/i2c/ccs/ccs-data.c @@ -10,6 +10,7 @@ #include <linux/limits.h> #include <linux/mm.h> #include <linux/slab.h> +#include <linux/string.h> #include "ccs-data-defs.h" @@ -97,7 +98,7 @@ ccs_data_parse_length_specifier(const struct __ccs_data_length_specifier *__len, plen = ((size_t) (__len3->length[0] & ((1 << CCS_DATA_LENGTH_SPECIFIER_SIZE_SHIFT) - 1)) - << 16) + (__len3->length[0] << 8) + __len3->length[1]; + << 16) + (__len3->length[1] << 8) + __len3->length[2]; break; } default: @@ -948,15 +949,15 @@ int ccs_data_parse(struct ccs_data_container *ccsdata, const void *data, rval = __ccs_data_parse(&bin, ccsdata, data, len, dev, verbose); if (rval) - return rval; + goto out_cleanup; rval = bin_backing_alloc(&bin); if (rval) - return rval; + goto out_cleanup; rval = __ccs_data_parse(&bin, ccsdata, data, len, dev, false); if (rval) - goto out_free; + goto out_cleanup; if (verbose && ccsdata->version) print_ccs_data_version(dev, ccsdata->version); @@ -965,15 +966,17 @@ int ccs_data_parse(struct ccs_data_container *ccsdata, const void *data, rval = -EPROTO; dev_dbg(dev, "parsing mismatch; base %p; now %p; end %p\n", bin.base, bin.now, bin.end); - goto out_free; + goto out_cleanup; } ccsdata->backing = bin.base; return 0; -out_free: +out_cleanup: kvfree(bin.base); + memset(ccsdata, 0, sizeof(*ccsdata)); + dev_warn(dev, "failed to parse CCS static data: %d\n", rval); return rval; } diff --git a/drivers/media/i2c/ccs/ccs-quirk.c b/drivers/media/i2c/ccs/ccs-quirk.c index e3d4c7a275bc..e48a4fa1f5dd 100644 --- a/drivers/media/i2c/ccs/ccs-quirk.c +++ b/drivers/media/i2c/ccs/ccs-quirk.c @@ -190,8 +190,7 @@ static int jt8ev1_post_streamoff(struct ccs_sensor *sensor) static int jt8ev1_init(struct ccs_sensor *sensor) { - sensor->pll.flags |= CCS_PLL_FLAG_LANE_SPEED_MODEL | - CCS_PLL_FLAG_LINK_DECOUPLED; + sensor->pll.flags |= CCS_PLL_FLAG_LANE_SPEED_MODEL; sensor->pll.vt_lanes = 1; sensor->pll.op_lanes = sensor->pll.csi2.lanes; diff --git a/drivers/media/i2c/ccs/ccs-reg-access.c b/drivers/media/i2c/ccs/ccs-reg-access.c index a696a0ec8ff5..fd36889ccc1d 100644 --- a/drivers/media/i2c/ccs/ccs-reg-access.c +++ b/drivers/media/i2c/ccs/ccs-reg-access.c @@ -210,7 +210,6 @@ int ccs_read_addr_noconv(struct ccs_sensor *sensor, u32 reg, u32 *val) */ int ccs_write_addr(struct ccs_sensor *sensor, u32 reg, u32 val) { - unsigned int retries = 10; int rval; rval = ccs_call_quirk(sensor, reg_access, true, ®, &val); @@ -219,13 +218,7 @@ int ccs_write_addr(struct ccs_sensor *sensor, u32 reg, u32 val) if (rval < 0) return rval; - rval = 0; - do { - if (cci_write(sensor->regmap, reg, val, &rval)) - fsleep(1000); - } while (rval && --retries); - - return rval; + return cci_write(sensor->regmap, reg, val, NULL); } #define MAX_WRITE_LEN 32U diff --git a/drivers/media/i2c/ccs/ccs.h b/drivers/media/i2c/ccs/ccs.h index 096573845a10..0726c4687f0f 100644 --- a/drivers/media/i2c/ccs/ccs.h +++ b/drivers/media/i2c/ccs/ccs.h @@ -43,6 +43,8 @@ #define SMIAPP_RESET_DELAY(clk) \ (1000 + (SMIAPP_RESET_DELAY_CLOCKS * 1000 \ + (clk) / 1000 - 1) / ((clk) / 1000)) +#define CCS_RESET_DELAY_US 5000 +#define CCS_RESET_TIMEOUT_US 1000000 #define CCS_COLOUR_COMPONENTS 4 diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c index a90a9e5705a0..a86306304330 100644 --- a/drivers/media/i2c/cx25840/cx25840-core.c +++ b/drivers/media/i2c/cx25840/cx25840-core.c @@ -9,10 +9,10 @@ * Changes by Tyler Trafford <tatrafford@comcast.net> * - cleanup/rewrite for V4L2 API (2005) * - * VBI support by Hans Verkuil <hverkuil@xs4all.nl>. + * VBI support by Hans Verkuil <hverkuil@kernel.org>. * * NTSC sliced VBI support by Christopher Neufeld <television@cneufeld.ca> - * with additional fixes by Hans Verkuil <hverkuil@xs4all.nl>. + * with additional fixes by Hans Verkuil <hverkuil@kernel.org>. * * CX23885 support by Steven Toth <stoth@linuxtv.org>. * diff --git a/drivers/media/i2c/ds90ub913.c b/drivers/media/i2c/ds90ub913.c index 8eed4a200fd8..e97e499b04e6 100644 --- a/drivers/media/i2c/ds90ub913.c +++ b/drivers/media/i2c/ds90ub913.c @@ -8,10 +8,10 @@ * Copyright (c) 2023 Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> */ +#include <linux/bitfield.h> #include <linux/clk-provider.h> #include <linux/clk.h> #include <linux/delay.h> -#include <linux/fwnode.h> #include <linux/gpio/driver.h> #include <linux/i2c-atr.h> #include <linux/i2c.h> @@ -118,31 +118,66 @@ static const struct ub913_format_info *ub913_find_format(u32 incode) return NULL; } -static int ub913_read(const struct ub913_data *priv, u8 reg, u8 *val) +static int ub913_read(const struct ub913_data *priv, u8 reg, u8 *val, + int *err) { unsigned int v; int ret; + if (err && *err) + return *err; + ret = regmap_read(priv->regmap, reg, &v); - if (ret < 0) { + if (ret) { dev_err(&priv->client->dev, "Cannot read register 0x%02x: %d!\n", reg, ret); - return ret; + goto out; } *val = v; - return 0; + +out: + if (ret && err) + *err = ret; + + return ret; } -static int ub913_write(const struct ub913_data *priv, u8 reg, u8 val) +static int ub913_write(const struct ub913_data *priv, u8 reg, u8 val, + int *err) { int ret; + if (err && *err) + return *err; + ret = regmap_write(priv->regmap, reg, val); if (ret < 0) dev_err(&priv->client->dev, "Cannot write register 0x%02x: %d!\n", reg, ret); + if (ret && err) + *err = ret; + + return ret; +} + +static int ub913_update_bits(const struct ub913_data *priv, u8 reg, u8 mask, + u8 val, int *err) +{ + int ret; + + if (err && *err) + return *err; + + ret = regmap_update_bits(priv->regmap, reg, mask, val); + if (ret < 0) + dev_err(&priv->client->dev, + "Cannot update register 0x%02x %d!\n", reg, ret); + + if (ret && err) + *err = ret; + return ret; } @@ -168,9 +203,9 @@ static int ub913_gpio_direction_out(struct gpio_chip *gc, unsigned int offset, 0)); } -static void ub913_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) +static int ub913_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) { - ub913_gpio_direction_out(gc, offset, value); + return ub913_gpio_direction_out(gc, offset, value); } static int ub913_gpio_of_xlate(struct gpio_chip *gc, @@ -190,7 +225,7 @@ static int ub913_gpiochip_probe(struct ub913_data *priv) int ret; /* Initialize GPIOs 0 and 1 to local control, tri-state */ - ub913_write(priv, UB913_REG_GPIO_CFG(0), 0); + ub913_write(priv, UB913_REG_GPIO_CFG(0), 0, NULL); gc->label = dev_name(dev); gc->parent = dev; @@ -298,18 +333,9 @@ static int _ub913_set_routing(struct v4l2_subdev *sd, .quantization = V4L2_QUANTIZATION_LIM_RANGE, .xfer_func = V4L2_XFER_FUNC_SRGB, }; - struct v4l2_subdev_stream_configs *stream_configs; - unsigned int i; + struct v4l2_subdev_route *route; int ret; - /* - * Note: we can only support up to V4L2_FRAME_DESC_ENTRY_MAX, until - * frame desc is made dynamically allocated. - */ - - if (routing->num_routes > V4L2_FRAME_DESC_ENTRY_MAX) - return -EINVAL; - ret = v4l2_subdev_routing_validate(sd, routing, V4L2_SUBDEV_ROUTING_ONLY_1_TO_1); if (ret) @@ -319,13 +345,15 @@ static int _ub913_set_routing(struct v4l2_subdev *sd, if (ret) return ret; - stream_configs = &state->stream_configs; + for_each_active_route(&state->routing, route) { + struct v4l2_mbus_framefmt *fmt; - for (i = 0; i < stream_configs->num_configs; i++) { - if (stream_configs->configs[i].pad == UB913_PAD_SINK) - stream_configs->configs[i].fmt = in_format; - else - stream_configs->configs[i].fmt = out_format; + fmt = v4l2_subdev_state_get_format(state, route->sink_pad, + route->sink_stream); + *fmt = in_format; + fmt = v4l2_subdev_state_get_format(state, route->source_pad, + route->source_stream); + *fmt = out_format; } return 0; @@ -436,10 +464,10 @@ static int ub913_set_fmt(struct v4l2_subdev *sd, if (!fmt) return -EINVAL; - format->format.code = finfo->outcode; - *fmt = format->format; + fmt->code = finfo->outcode; + return 0; } @@ -468,25 +496,41 @@ static int ub913_log_status(struct v4l2_subdev *sd) { struct ub913_data *priv = sd_to_ub913(sd); struct device *dev = &priv->client->dev; - u8 v = 0, v1 = 0, v2 = 0; + u8 v, v1, v2; + int ret; + + ret = ub913_read(priv, UB913_REG_MODE_SEL, &v, NULL); + if (ret) + return ret; - ub913_read(priv, UB913_REG_MODE_SEL, &v); dev_info(dev, "MODE_SEL %#02x\n", v); - ub913_read(priv, UB913_REG_CRC_ERRORS_LSB, &v1); - ub913_read(priv, UB913_REG_CRC_ERRORS_MSB, &v2); + ub913_read(priv, UB913_REG_CRC_ERRORS_LSB, &v1, &ret); + ub913_read(priv, UB913_REG_CRC_ERRORS_MSB, &v2, &ret); + if (ret) + return ret; + dev_info(dev, "CRC errors %u\n", v1 | (v2 << 8)); /* clear CRC errors */ - ub913_read(priv, UB913_REG_GENERAL_CFG, &v); + ub913_read(priv, UB913_REG_GENERAL_CFG, &v, &ret); ub913_write(priv, UB913_REG_GENERAL_CFG, - v | UB913_REG_GENERAL_CFG_CRC_ERR_RESET); - ub913_write(priv, UB913_REG_GENERAL_CFG, v); + v | UB913_REG_GENERAL_CFG_CRC_ERR_RESET, &ret); + ub913_write(priv, UB913_REG_GENERAL_CFG, v, &ret); + + if (ret) + return ret; + + ret = ub913_read(priv, UB913_REG_GENERAL_STATUS, &v, NULL); + if (ret) + return ret; - ub913_read(priv, UB913_REG_GENERAL_STATUS, &v); dev_info(dev, "GENERAL_STATUS %#02x\n", v); - ub913_read(priv, UB913_REG_PLL_OVR, &v); + ret = ub913_read(priv, UB913_REG_PLL_OVR, &v, NULL); + if (ret) + return ret; + dev_info(dev, "PLL_OVR %#02x\n", v); return 0; @@ -578,7 +622,7 @@ static int ub913_v4l2_notifier_register(struct ub913_data *priv) fwnode_handle_put(ep_fwnode); if (IS_ERR(asd)) { - dev_err(dev, "Failed to add subdev: %ld", PTR_ERR(asd)); + dev_err(dev, "Failed to add subdev: %pe", asd); v4l2_async_nf_cleanup(&priv->notifier); return PTR_ERR(asd); } @@ -642,11 +686,11 @@ static int ub913_i2c_master_init(struct ub913_data *priv) scl_high = div64_u64((u64)scl_high * ref, 1000000000); scl_low = div64_u64((u64)scl_low * ref, 1000000000); - ret = ub913_write(priv, UB913_REG_SCL_HIGH_TIME, scl_high); + ret = ub913_write(priv, UB913_REG_SCL_HIGH_TIME, scl_high, NULL); if (ret) return ret; - ret = ub913_write(priv, UB913_REG_SCL_LOW_TIME, scl_low); + ret = ub913_write(priv, UB913_REG_SCL_LOW_TIME, scl_low, NULL); if (ret) return ret; @@ -656,6 +700,7 @@ static int ub913_i2c_master_init(struct ub913_data *priv) static int ub913_add_i2c_adapter(struct ub913_data *priv) { struct device *dev = &priv->client->dev; + struct i2c_atr_adap_desc desc = { }; struct fwnode_handle *i2c_handle; int ret; @@ -663,8 +708,12 @@ static int ub913_add_i2c_adapter(struct ub913_data *priv) if (!i2c_handle) return 0; - ret = i2c_atr_add_adapter(priv->plat_data->atr, priv->plat_data->port, - dev, i2c_handle); + desc.chan_id = priv->plat_data->port; + desc.parent = dev; + desc.bus_handle = i2c_handle; + desc.num_aliases = 0; + + ret = i2c_atr_add_adapter(priv->plat_data->atr, &desc); fwnode_handle_put(i2c_handle); @@ -715,7 +764,7 @@ static int ub913_hw_init(struct ub913_data *priv) int ret; u8 v; - ret = ub913_read(priv, UB913_REG_MODE_SEL, &v); + ret = ub913_read(priv, UB913_REG_MODE_SEL, &v, NULL); if (ret) return ret; @@ -733,10 +782,13 @@ static int ub913_hw_init(struct ub913_data *priv) if (ret) return dev_err_probe(dev, ret, "i2c master init failed\n"); - ub913_read(priv, UB913_REG_GENERAL_CFG, &v); - v &= ~UB913_REG_GENERAL_CFG_PCLK_RISING; - v |= priv->pclk_polarity_rising ? UB913_REG_GENERAL_CFG_PCLK_RISING : 0; - ub913_write(priv, UB913_REG_GENERAL_CFG, v); + ret = ub913_update_bits(priv, UB913_REG_GENERAL_CFG, + UB913_REG_GENERAL_CFG_PCLK_RISING, + FIELD_PREP(UB913_REG_GENERAL_CFG_PCLK_RISING, + priv->pclk_polarity_rising), NULL); + + if (ret) + return ret; return 0; } @@ -793,7 +845,6 @@ static void ub913_subdev_uninit(struct ub913_data *priv) v4l2_async_unregister_subdev(&priv->sd); ub913_v4l2_nf_unregister(priv); v4l2_subdev_cleanup(&priv->sd); - fwnode_handle_put(priv->sd.fwnode); media_entity_cleanup(&priv->sd.entity); } @@ -904,4 +955,4 @@ MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Texas Instruments DS90UB913 FPD-Link III Serializer Driver"); MODULE_AUTHOR("Luca Ceresoli <luca@lucaceresoli.net>"); MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>"); -MODULE_IMPORT_NS(I2C_ATR); +MODULE_IMPORT_NS("I2C_ATR"); diff --git a/drivers/media/i2c/ds90ub953.c b/drivers/media/i2c/ds90ub953.c index 8b028a84f5bc..daefdb108fbf 100644 --- a/drivers/media/i2c/ds90ub953.c +++ b/drivers/media/i2c/ds90ub953.c @@ -11,7 +11,6 @@ #include <linux/clk-provider.h> #include <linux/clk.h> #include <linux/delay.h> -#include <linux/fwnode.h> #include <linux/gpio/driver.h> #include <linux/i2c-atr.h> #include <linux/i2c.h> @@ -28,6 +27,8 @@ #include <media/v4l2-mediabus.h> #include <media/v4l2-subdev.h> +#include "ds90ub953.h" + #define UB953_PAD_SINK 0 #define UB953_PAD_SOURCE 1 @@ -35,86 +36,6 @@ #define UB953_DEFAULT_CLKOUT_RATE 25000000UL -#define UB953_REG_RESET_CTL 0x01 -#define UB953_REG_RESET_CTL_DIGITAL_RESET_1 BIT(1) -#define UB953_REG_RESET_CTL_DIGITAL_RESET_0 BIT(0) - -#define UB953_REG_GENERAL_CFG 0x02 -#define UB953_REG_GENERAL_CFG_CONT_CLK BIT(6) -#define UB953_REG_GENERAL_CFG_CSI_LANE_SEL_SHIFT 4 -#define UB953_REG_GENERAL_CFG_CSI_LANE_SEL_MASK GENMASK(5, 4) -#define UB953_REG_GENERAL_CFG_CRC_TX_GEN_ENABLE BIT(1) -#define UB953_REG_GENERAL_CFG_I2C_STRAP_MODE BIT(0) - -#define UB953_REG_MODE_SEL 0x03 -#define UB953_REG_MODE_SEL_MODE_DONE BIT(3) -#define UB953_REG_MODE_SEL_MODE_OVERRIDE BIT(4) -#define UB953_REG_MODE_SEL_MODE_MASK GENMASK(2, 0) - -#define UB953_REG_CLKOUT_CTRL0 0x06 -#define UB953_REG_CLKOUT_CTRL1 0x07 - -#define UB953_REG_SCL_HIGH_TIME 0x0b -#define UB953_REG_SCL_LOW_TIME 0x0c - -#define UB953_REG_LOCAL_GPIO_DATA 0x0d -#define UB953_REG_LOCAL_GPIO_DATA_GPIO_RMTEN(n) BIT(4 + (n)) -#define UB953_REG_LOCAL_GPIO_DATA_GPIO_OUT_SRC(n) BIT(0 + (n)) - -#define UB953_REG_GPIO_INPUT_CTRL 0x0e -#define UB953_REG_GPIO_INPUT_CTRL_OUT_EN(n) BIT(4 + (n)) -#define UB953_REG_GPIO_INPUT_CTRL_INPUT_EN(n) BIT(0 + (n)) - -#define UB953_REG_REV_MASK_ID 0x50 -#define UB953_REG_GENERAL_STATUS 0x52 - -#define UB953_REG_GPIO_PIN_STS 0x53 -#define UB953_REG_GPIO_PIN_STS_GPIO_STS(n) BIT(0 + (n)) - -#define UB953_REG_BIST_ERR_CNT 0x54 -#define UB953_REG_CRC_ERR_CNT1 0x55 -#define UB953_REG_CRC_ERR_CNT2 0x56 - -#define UB953_REG_CSI_ERR_CNT 0x5c -#define UB953_REG_CSI_ERR_STATUS 0x5d -#define UB953_REG_CSI_ERR_DLANE01 0x5e -#define UB953_REG_CSI_ERR_DLANE23 0x5f -#define UB953_REG_CSI_ERR_CLK_LANE 0x60 -#define UB953_REG_CSI_PKT_HDR_VC_ID 0x61 -#define UB953_REG_PKT_HDR_WC_LSB 0x62 -#define UB953_REG_PKT_HDR_WC_MSB 0x63 -#define UB953_REG_CSI_ECC 0x64 - -#define UB953_REG_IND_ACC_CTL 0xb0 -#define UB953_REG_IND_ACC_ADDR 0xb1 -#define UB953_REG_IND_ACC_DATA 0xb2 - -#define UB953_REG_FPD3_RX_ID(n) (0xf0 + (n)) -#define UB953_REG_FPD3_RX_ID_LEN 6 - -/* Indirect register blocks */ -#define UB953_IND_TARGET_PAT_GEN 0x00 -#define UB953_IND_TARGET_FPD3_TX 0x01 -#define UB953_IND_TARGET_DIE_ID 0x02 - -#define UB953_IND_PGEN_CTL 0x01 -#define UB953_IND_PGEN_CTL_PGEN_ENABLE BIT(0) -#define UB953_IND_PGEN_CFG 0x02 -#define UB953_IND_PGEN_CSI_DI 0x03 -#define UB953_IND_PGEN_LINE_SIZE1 0x04 -#define UB953_IND_PGEN_LINE_SIZE0 0x05 -#define UB953_IND_PGEN_BAR_SIZE1 0x06 -#define UB953_IND_PGEN_BAR_SIZE0 0x07 -#define UB953_IND_PGEN_ACT_LPF1 0x08 -#define UB953_IND_PGEN_ACT_LPF0 0x09 -#define UB953_IND_PGEN_TOT_LPF1 0x0a -#define UB953_IND_PGEN_TOT_LPF0 0x0b -#define UB953_IND_PGEN_LINE_PD1 0x0c -#define UB953_IND_PGEN_LINE_PD0 0x0d -#define UB953_IND_PGEN_VBP 0x0e -#define UB953_IND_PGEN_VFP 0x0f -#define UB953_IND_PGEN_COLOR(n) (0x10 + (n)) /* n <= 15 */ - /* Note: Only sync mode supported for now */ enum ub953_mode { /* FPD-Link III CSI-2 synchronous mode */ @@ -182,11 +103,14 @@ static inline struct ub953_data *sd_to_ub953(struct v4l2_subdev *sd) * HW Access */ -static int ub953_read(struct ub953_data *priv, u8 reg, u8 *val) +static int ub953_read(struct ub953_data *priv, u8 reg, u8 *val, int *err) { unsigned int v; int ret; + if (err && *err) + return *err; + mutex_lock(&priv->reg_lock); ret = regmap_read(priv->regmap, reg, &v); @@ -201,13 +125,19 @@ static int ub953_read(struct ub953_data *priv, u8 reg, u8 *val) out_unlock: mutex_unlock(&priv->reg_lock); + if (ret && err) + *err = ret; + return ret; } -static int ub953_write(struct ub953_data *priv, u8 reg, u8 val) +static int ub953_write(struct ub953_data *priv, u8 reg, u8 val, int *err) { int ret; + if (err && *err) + return *err; + mutex_lock(&priv->reg_lock); ret = regmap_write(priv->regmap, reg, val); @@ -217,6 +147,9 @@ static int ub953_write(struct ub953_data *priv, u8 reg, u8 val) mutex_unlock(&priv->reg_lock); + if (ret && err) + *err = ret; + return ret; } @@ -241,11 +174,15 @@ static int ub953_select_ind_reg_block(struct ub953_data *priv, u8 block) } __maybe_unused -static int ub953_read_ind(struct ub953_data *priv, u8 block, u8 reg, u8 *val) +static int ub953_read_ind(struct ub953_data *priv, u8 block, u8 reg, u8 *val, + int *err) { unsigned int v; int ret; + if (err && *err) + return *err; + mutex_lock(&priv->reg_lock); ret = ub953_select_ind_reg_block(priv, block); @@ -255,7 +192,7 @@ static int ub953_read_ind(struct ub953_data *priv, u8 block, u8 reg, u8 *val) ret = regmap_write(priv->regmap, UB953_REG_IND_ACC_ADDR, reg); if (ret) { dev_err(&priv->client->dev, - "Write to IND_ACC_ADDR failed when reading %u:%x02x: %d\n", + "Write to IND_ACC_ADDR failed when reading %u:0x%02x: %d\n", block, reg, ret); goto out_unlock; } @@ -263,7 +200,7 @@ static int ub953_read_ind(struct ub953_data *priv, u8 block, u8 reg, u8 *val) ret = regmap_read(priv->regmap, UB953_REG_IND_ACC_DATA, &v); if (ret) { dev_err(&priv->client->dev, - "Write to IND_ACC_DATA failed when reading %u:%x02x: %d\n", + "Write to IND_ACC_DATA failed when reading %u:0x%02x: %d\n", block, reg, ret); goto out_unlock; } @@ -273,14 +210,21 @@ static int ub953_read_ind(struct ub953_data *priv, u8 block, u8 reg, u8 *val) out_unlock: mutex_unlock(&priv->reg_lock); + if (ret && err) + *err = ret; + return ret; } __maybe_unused -static int ub953_write_ind(struct ub953_data *priv, u8 block, u8 reg, u8 val) +static int ub953_write_ind(struct ub953_data *priv, u8 block, u8 reg, u8 val, + int *err) { int ret; + if (err && *err) + return *err; + mutex_lock(&priv->reg_lock); ret = ub953_select_ind_reg_block(priv, block); @@ -290,7 +234,7 @@ static int ub953_write_ind(struct ub953_data *priv, u8 block, u8 reg, u8 val) ret = regmap_write(priv->regmap, UB953_REG_IND_ACC_ADDR, reg); if (ret) { dev_err(&priv->client->dev, - "Write to IND_ACC_ADDR failed when writing %u:%x02x: %d\n", + "Write to IND_ACC_ADDR failed when writing %u:0x%02x: %d\n", block, reg, ret); goto out_unlock; } @@ -298,13 +242,16 @@ static int ub953_write_ind(struct ub953_data *priv, u8 block, u8 reg, u8 val) ret = regmap_write(priv->regmap, UB953_REG_IND_ACC_DATA, val); if (ret) { dev_err(&priv->client->dev, - "Write to IND_ACC_DATA failed when writing %u:%x02x\n: %d\n", + "Write to IND_ACC_DATA failed when writing %u:0x%02x: %d\n", block, reg, ret); } out_unlock: mutex_unlock(&priv->reg_lock); + if (ret && err) + *err = ret; + return ret; } @@ -317,7 +264,7 @@ static int ub953_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) int ret; u8 v; - ret = ub953_read(priv, UB953_REG_GPIO_INPUT_CTRL, &v); + ret = ub953_read(priv, UB953_REG_GPIO_INPUT_CTRL, &v, NULL); if (ret) return ret; @@ -363,21 +310,20 @@ static int ub953_gpio_get(struct gpio_chip *gc, unsigned int offset) int ret; u8 v; - ret = ub953_read(priv, UB953_REG_GPIO_PIN_STS, &v); + ret = ub953_read(priv, UB953_REG_GPIO_PIN_STS, &v, NULL); if (ret) return ret; return !!(v & UB953_REG_GPIO_PIN_STS_GPIO_STS(offset)); } -static void ub953_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) +static int ub953_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) { struct ub953_data *priv = gpiochip_get_data(gc); - regmap_update_bits(priv->regmap, UB953_REG_LOCAL_GPIO_DATA, - UB953_REG_LOCAL_GPIO_DATA_GPIO_OUT_SRC(offset), - value ? UB953_REG_LOCAL_GPIO_DATA_GPIO_OUT_SRC(offset) : - 0); + return regmap_update_bits(priv->regmap, UB953_REG_LOCAL_GPIO_DATA, + UB953_REG_LOCAL_GPIO_DATA_GPIO_OUT_SRC(offset), + value ? UB953_REG_LOCAL_GPIO_DATA_GPIO_OUT_SRC(offset) : 0); } static int ub953_gpio_of_xlate(struct gpio_chip *gc, @@ -397,8 +343,13 @@ static int ub953_gpiochip_probe(struct ub953_data *priv) int ret; /* Set all GPIOs to local input mode */ - ub953_write(priv, UB953_REG_LOCAL_GPIO_DATA, 0); - ub953_write(priv, UB953_REG_GPIO_INPUT_CTRL, 0xf); + ret = ub953_write(priv, UB953_REG_LOCAL_GPIO_DATA, 0, NULL); + if (ret) + return ret; + + ret = ub953_write(priv, UB953_REG_GPIO_INPUT_CTRL, 0xf, NULL); + if (ret) + return ret; gc->label = dev_name(dev); gc->parent = dev; @@ -448,14 +399,6 @@ static int _ub953_set_routing(struct v4l2_subdev *sd, }; int ret; - /* - * Note: we can only support up to V4L2_FRAME_DESC_ENTRY_MAX, until - * frame desc is made dynamically allocated. - */ - - if (routing->num_routes > V4L2_FRAME_DESC_ENTRY_MAX) - return -EINVAL; - ret = v4l2_subdev_routing_validate(sd, routing, V4L2_SUBDEV_ROUTING_ONLY_1_TO_1); if (ret) @@ -599,53 +542,95 @@ static int ub953_log_status(struct v4l2_subdev *sd) { struct ub953_data *priv = sd_to_ub953(sd); struct device *dev = &priv->client->dev; - u8 v = 0, v1 = 0, v2 = 0; - unsigned int i; char id[UB953_REG_FPD3_RX_ID_LEN]; - u8 gpio_local_data = 0; - u8 gpio_input_ctrl = 0; - u8 gpio_pin_sts = 0; + u8 gpio_local_data; + u8 gpio_input_ctrl; + u8 gpio_pin_sts; + unsigned int i; + u8 v, v1, v2; + int ret; - for (i = 0; i < sizeof(id); i++) - ub953_read(priv, UB953_REG_FPD3_RX_ID(i), &id[i]); + for (i = 0; i < sizeof(id); i++) { + ret = ub953_read(priv, UB953_REG_FPD3_RX_ID(i), &id[i], NULL); + if (ret) + return ret; + } dev_info(dev, "ID '%.*s'\n", (int)sizeof(id), id); - ub953_read(priv, UB953_REG_GENERAL_STATUS, &v); + ret = ub953_read(priv, UB953_REG_GENERAL_STATUS, &v, NULL); + if (ret) + return ret; + dev_info(dev, "GENERAL_STATUS %#02x\n", v); - ub953_read(priv, UB953_REG_CRC_ERR_CNT1, &v1); - ub953_read(priv, UB953_REG_CRC_ERR_CNT2, &v2); + ub953_read(priv, UB953_REG_CRC_ERR_CNT1, &v1, &ret); + ub953_read(priv, UB953_REG_CRC_ERR_CNT2, &v2, &ret); + if (ret) + return ret; + dev_info(dev, "CRC error count %u\n", v1 | (v2 << 8)); - ub953_read(priv, UB953_REG_CSI_ERR_CNT, &v); + /* Clear CRC error counter */ + if (v1 || v2) + regmap_update_bits(priv->regmap, UB953_REG_BC_CTRL, + UB953_REG_BC_CTRL_CRC_ERR_CLR, + UB953_REG_BC_CTRL_CRC_ERR_CLR); + + ret = ub953_read(priv, UB953_REG_CSI_ERR_CNT, &v, NULL); + if (ret) + return ret; + dev_info(dev, "CSI error count %u\n", v); - ub953_read(priv, UB953_REG_CSI_ERR_STATUS, &v); + ret = ub953_read(priv, UB953_REG_CSI_ERR_STATUS, &v, NULL); + if (ret) + return ret; + dev_info(dev, "CSI_ERR_STATUS %#02x\n", v); - ub953_read(priv, UB953_REG_CSI_ERR_DLANE01, &v); + ret = ub953_read(priv, UB953_REG_CSI_ERR_DLANE01, &v, NULL); + if (ret) + return ret; + dev_info(dev, "CSI_ERR_DLANE01 %#02x\n", v); - ub953_read(priv, UB953_REG_CSI_ERR_DLANE23, &v); + ret = ub953_read(priv, UB953_REG_CSI_ERR_DLANE23, &v, NULL); + if (ret) + return ret; + dev_info(dev, "CSI_ERR_DLANE23 %#02x\n", v); - ub953_read(priv, UB953_REG_CSI_ERR_CLK_LANE, &v); + ret = ub953_read(priv, UB953_REG_CSI_ERR_CLK_LANE, &v, NULL); + if (ret) + return ret; + dev_info(dev, "CSI_ERR_CLK_LANE %#02x\n", v); - ub953_read(priv, UB953_REG_CSI_PKT_HDR_VC_ID, &v); + ret = ub953_read(priv, UB953_REG_CSI_PKT_HDR_VC_ID, &v, NULL); + if (ret) + return ret; + dev_info(dev, "CSI packet header VC %u ID %u\n", v >> 6, v & 0x3f); - ub953_read(priv, UB953_REG_PKT_HDR_WC_LSB, &v1); - ub953_read(priv, UB953_REG_PKT_HDR_WC_MSB, &v2); + ub953_read(priv, UB953_REG_PKT_HDR_WC_LSB, &v1, &ret); + ub953_read(priv, UB953_REG_PKT_HDR_WC_MSB, &v2, &ret); + if (ret) + return ret; + dev_info(dev, "CSI packet header WC %u\n", (v2 << 8) | v1); - ub953_read(priv, UB953_REG_CSI_ECC, &v); + ret = ub953_read(priv, UB953_REG_CSI_ECC, &v, NULL); + if (ret) + return ret; + dev_info(dev, "CSI ECC %#02x\n", v); - ub953_read(priv, UB953_REG_LOCAL_GPIO_DATA, &gpio_local_data); - ub953_read(priv, UB953_REG_GPIO_INPUT_CTRL, &gpio_input_ctrl); - ub953_read(priv, UB953_REG_GPIO_PIN_STS, &gpio_pin_sts); + ub953_read(priv, UB953_REG_LOCAL_GPIO_DATA, &gpio_local_data, &ret); + ub953_read(priv, UB953_REG_GPIO_INPUT_CTRL, &gpio_input_ctrl, &ret); + ub953_read(priv, UB953_REG_GPIO_PIN_STS, &gpio_pin_sts, &ret); + if (ret) + return ret; for (i = 0; i < UB953_NUM_GPIOS; i++) { dev_info(dev, @@ -791,7 +776,7 @@ static int ub953_v4l2_notifier_register(struct ub953_data *priv) fwnode_handle_put(ep_fwnode); if (IS_ERR(asd)) { - dev_err(dev, "Failed to add subdev: %ld", PTR_ERR(asd)); + dev_err(dev, "Failed to add subdev: %pe", asd); v4l2_async_nf_cleanup(&priv->notifier); return PTR_ERR(asd); } @@ -829,11 +814,11 @@ static int ub953_i2c_master_init(struct ub953_data *priv) scl_high = div64_u64((u64)scl_high * ref, 1000000000) - 5; scl_low = div64_u64((u64)scl_low * ref, 1000000000) - 5; - ret = ub953_write(priv, UB953_REG_SCL_HIGH_TIME, scl_high); + ret = ub953_write(priv, UB953_REG_SCL_HIGH_TIME, scl_high, NULL); if (ret) return ret; - ret = ub953_write(priv, UB953_REG_SCL_LOW_TIME, scl_low); + ret = ub953_write(priv, UB953_REG_SCL_LOW_TIME, scl_low, NULL); if (ret) return ret; @@ -958,10 +943,11 @@ static void ub953_calc_clkout_params(struct ub953_data *priv, clkout_data->rate = clkout_rate; } -static void ub953_write_clkout_regs(struct ub953_data *priv, - const struct ub953_clkout_data *clkout_data) +static int ub953_write_clkout_regs(struct ub953_data *priv, + const struct ub953_clkout_data *clkout_data) { u8 clkout_ctrl0, clkout_ctrl1; + int ret; if (priv->hw_data->is_ub971) clkout_ctrl0 = clkout_data->m; @@ -971,8 +957,15 @@ static void ub953_write_clkout_regs(struct ub953_data *priv, clkout_ctrl1 = clkout_data->n; - ub953_write(priv, UB953_REG_CLKOUT_CTRL0, clkout_ctrl0); - ub953_write(priv, UB953_REG_CLKOUT_CTRL1, clkout_ctrl1); + ret = ub953_write(priv, UB953_REG_CLKOUT_CTRL0, clkout_ctrl0, NULL); + if (ret) + return ret; + + ret = ub953_write(priv, UB953_REG_CLKOUT_CTRL1, clkout_ctrl1, NULL); + if (ret) + return ret; + + return 0; } static unsigned long ub953_clkout_recalc_rate(struct clk_hw *hw, @@ -987,13 +980,13 @@ static unsigned long ub953_clkout_recalc_rate(struct clk_hw *hw, u64 rate; int ret; - ret = ub953_read(priv, UB953_REG_CLKOUT_CTRL0, &ctrl0); + ret = ub953_read(priv, UB953_REG_CLKOUT_CTRL0, &ctrl0, NULL); if (ret) { dev_err(dev, "Failed to read CLKOUT_CTRL0: %d\n", ret); return 0; } - ret = ub953_read(priv, UB953_REG_CLKOUT_CTRL1, &ctrl1); + ret = ub953_read(priv, UB953_REG_CLKOUT_CTRL1, &ctrl1, NULL); if (ret) { dev_err(dev, "Failed to read CLKOUT_CTRL1: %d\n", ret); return 0; @@ -1030,15 +1023,17 @@ static unsigned long ub953_clkout_recalc_rate(struct clk_hw *hw, return rate; } -static long ub953_clkout_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int ub953_clkout_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct ub953_data *priv = container_of(hw, struct ub953_data, clkout_clk_hw); struct ub953_clkout_data clkout_data; - ub953_calc_clkout_params(priv, rate, &clkout_data); + ub953_calc_clkout_params(priv, req->rate, &clkout_data); + + req->rate = clkout_data.rate; - return clkout_data.rate; + return 0; } static int ub953_clkout_set_rate(struct clk_hw *hw, unsigned long rate, @@ -1052,14 +1047,12 @@ static int ub953_clkout_set_rate(struct clk_hw *hw, unsigned long rate, dev_dbg(&priv->client->dev, "%s %lu (requested %lu)\n", __func__, clkout_data.rate, rate); - ub953_write_clkout_regs(priv, &clkout_data); - - return 0; + return ub953_write_clkout_regs(priv, &clkout_data); } static const struct clk_ops ub953_clkout_ops = { .recalc_rate = ub953_clkout_recalc_rate, - .round_rate = ub953_clkout_round_rate, + .determine_rate = ub953_clkout_determine_rate, .set_rate = ub953_clkout_set_rate, }; @@ -1079,7 +1072,9 @@ static int ub953_register_clkout(struct ub953_data *priv) /* Initialize clkout to 25MHz by default */ ub953_calc_clkout_params(priv, UB953_DEFAULT_CLKOUT_RATE, &clkout_data); - ub953_write_clkout_regs(priv, &clkout_data); + ret = ub953_write_clkout_regs(priv, &clkout_data); + if (ret) + return ret; priv->clkout_clk_hw.init = &init; @@ -1100,6 +1095,7 @@ static int ub953_register_clkout(struct ub953_data *priv) static int ub953_add_i2c_adapter(struct ub953_data *priv) { struct device *dev = &priv->client->dev; + struct i2c_atr_adap_desc desc = { }; struct fwnode_handle *i2c_handle; int ret; @@ -1107,8 +1103,12 @@ static int ub953_add_i2c_adapter(struct ub953_data *priv) if (!i2c_handle) return 0; - ret = i2c_atr_add_adapter(priv->plat_data->atr, priv->plat_data->port, - dev, i2c_handle); + desc.chan_id = priv->plat_data->port; + desc.parent = dev; + desc.bus_handle = i2c_handle; + desc.num_aliases = 0; + + ret = i2c_atr_add_adapter(priv->plat_data->atr, &desc); fwnode_handle_put(i2c_handle); @@ -1169,7 +1169,7 @@ static int ub953_hw_init(struct ub953_data *priv) int ret; u8 v; - ret = ub953_read(priv, UB953_REG_MODE_SEL, &v); + ret = ub953_read(priv, UB953_REG_MODE_SEL, &v, NULL); if (ret) return ret; @@ -1209,13 +1209,13 @@ static int ub953_hw_init(struct ub953_data *priv) return dev_err_probe(dev, -EINVAL, "clkin required for non-sync ext mode\n"); - ret = ub953_read(priv, UB953_REG_REV_MASK_ID, &v); + ret = ub953_read(priv, UB953_REG_REV_MASK_ID, &v, NULL); if (ret) return dev_err_probe(dev, ret, "Failed to read revision"); dev_info(dev, "Found %s rev/mask %#04x\n", priv->hw_data->model, v); - ret = ub953_read(priv, UB953_REG_GENERAL_CFG, &v); + ret = ub953_read(priv, UB953_REG_GENERAL_CFG, &v, NULL); if (ret) return ret; @@ -1226,12 +1226,22 @@ static int ub953_hw_init(struct ub953_data *priv) if (ret) return dev_err_probe(dev, ret, "i2c init failed\n"); - ub953_write(priv, UB953_REG_GENERAL_CFG, - (priv->non_continous_clk ? 0 : UB953_REG_GENERAL_CFG_CONT_CLK) | - ((priv->num_data_lanes - 1) << UB953_REG_GENERAL_CFG_CSI_LANE_SEL_SHIFT) | - UB953_REG_GENERAL_CFG_CRC_TX_GEN_ENABLE); + v = 0; + v |= priv->non_continous_clk ? 0 : UB953_REG_GENERAL_CFG_CONT_CLK; + v |= (priv->num_data_lanes - 1) << + UB953_REG_GENERAL_CFG_CSI_LANE_SEL_SHIFT; + v |= UB953_REG_GENERAL_CFG_CRC_TX_GEN_ENABLE; - return 0; + ret = ub953_write(priv, UB953_REG_GENERAL_CFG, v, NULL); + if (ret) + return ret; + + v = 1U << UB953_REG_I2C_CONTROL2_SDA_OUTPUT_SETUP_SHIFT; + v |= UB953_REG_I2C_CONTROL2_BUS_SPEEDUP; + + ret = ub953_write(priv, UB953_REG_I2C_CONTROL2, v, NULL); + + return ret; } static int ub953_subdev_init(struct ub953_data *priv) @@ -1288,7 +1298,6 @@ static void ub953_subdev_uninit(struct ub953_data *priv) v4l2_async_unregister_subdev(&priv->sd); ub953_v4l2_notifier_unregister(priv); v4l2_subdev_cleanup(&priv->sd); - fwnode_handle_put(priv->sd.fwnode); media_entity_cleanup(&priv->sd.entity); } @@ -1425,4 +1434,4 @@ MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Texas Instruments FPD-Link III/IV CSI-2 Serializers Driver"); MODULE_AUTHOR("Luca Ceresoli <luca@lucaceresoli.net>"); MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>"); -MODULE_IMPORT_NS(I2C_ATR); +MODULE_IMPORT_NS("I2C_ATR"); diff --git a/drivers/media/i2c/ds90ub953.h b/drivers/media/i2c/ds90ub953.h new file mode 100644 index 000000000000..97a6b3af326e --- /dev/null +++ b/drivers/media/i2c/ds90ub953.h @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __MEDIA_I2C_DS90UB953_H__ +#define __MEDIA_I2C_DS90UB953_H__ + +#include <linux/types.h> + +#define UB953_REG_RESET_CTL 0x01 +#define UB953_REG_RESET_CTL_DIGITAL_RESET_1 BIT(1) +#define UB953_REG_RESET_CTL_DIGITAL_RESET_0 BIT(0) + +#define UB953_REG_GENERAL_CFG 0x02 +#define UB953_REG_GENERAL_CFG_CONT_CLK BIT(6) +#define UB953_REG_GENERAL_CFG_CSI_LANE_SEL_SHIFT 4 +#define UB953_REG_GENERAL_CFG_CSI_LANE_SEL_MASK GENMASK(5, 4) +#define UB953_REG_GENERAL_CFG_CRC_TX_GEN_ENABLE BIT(1) +#define UB953_REG_GENERAL_CFG_I2C_STRAP_MODE BIT(0) + +#define UB953_REG_MODE_SEL 0x03 +#define UB953_REG_MODE_SEL_MODE_DONE BIT(3) +#define UB953_REG_MODE_SEL_MODE_OVERRIDE BIT(4) +#define UB953_REG_MODE_SEL_MODE_MASK GENMASK(2, 0) + +#define UB953_REG_CLKOUT_CTRL0 0x06 +#define UB953_REG_CLKOUT_CTRL1 0x07 + +#define UB953_REG_I2C_CONTROL2 0x0a +#define UB953_REG_I2C_CONTROL2_SDA_OUTPUT_SETUP_SHIFT 4 +#define UB953_REG_I2C_CONTROL2_BUS_SPEEDUP BIT(1) + +#define UB953_REG_SCL_HIGH_TIME 0x0b +#define UB953_REG_SCL_LOW_TIME 0x0c + +#define UB953_REG_LOCAL_GPIO_DATA 0x0d +#define UB953_REG_LOCAL_GPIO_DATA_GPIO_RMTEN(n) BIT(4 + (n)) +#define UB953_REG_LOCAL_GPIO_DATA_GPIO_OUT_SRC(n) BIT(0 + (n)) + +#define UB953_REG_GPIO_INPUT_CTRL 0x0e +#define UB953_REG_GPIO_INPUT_CTRL_OUT_EN(n) BIT(4 + (n)) +#define UB953_REG_GPIO_INPUT_CTRL_INPUT_EN(n) BIT(0 + (n)) + +#define UB953_REG_BC_CTRL 0x49 +#define UB953_REG_BC_CTRL_CRC_ERR_CLR BIT(3) + +#define UB953_REG_REV_MASK_ID 0x50 +#define UB953_REG_GENERAL_STATUS 0x52 + +#define UB953_REG_GPIO_PIN_STS 0x53 +#define UB953_REG_GPIO_PIN_STS_GPIO_STS(n) BIT(0 + (n)) + +#define UB953_REG_BIST_ERR_CNT 0x54 +#define UB953_REG_CRC_ERR_CNT1 0x55 +#define UB953_REG_CRC_ERR_CNT2 0x56 + +#define UB953_REG_CSI_ERR_CNT 0x5c +#define UB953_REG_CSI_ERR_STATUS 0x5d +#define UB953_REG_CSI_ERR_DLANE01 0x5e +#define UB953_REG_CSI_ERR_DLANE23 0x5f +#define UB953_REG_CSI_ERR_CLK_LANE 0x60 +#define UB953_REG_CSI_PKT_HDR_VC_ID 0x61 +#define UB953_REG_PKT_HDR_WC_LSB 0x62 +#define UB953_REG_PKT_HDR_WC_MSB 0x63 +#define UB953_REG_CSI_ECC 0x64 + +#define UB953_REG_IND_ACC_CTL 0xb0 +#define UB953_REG_IND_ACC_ADDR 0xb1 +#define UB953_REG_IND_ACC_DATA 0xb2 + +#define UB953_REG_FPD3_RX_ID(n) (0xf0 + (n)) +#define UB953_REG_FPD3_RX_ID_LEN 6 + +/* Indirect register blocks */ +#define UB953_IND_TARGET_PAT_GEN 0x00 +#define UB953_IND_TARGET_ANALOG 0x01 +#define UB953_IND_TARGET_DIE_ID 0x02 + +#define UB953_IND_PGEN_CTL 0x01 +#define UB953_IND_PGEN_CTL_PGEN_ENABLE BIT(0) +#define UB953_IND_PGEN_CFG 0x02 +#define UB953_IND_PGEN_CSI_DI 0x03 +#define UB953_IND_PGEN_LINE_SIZE1 0x04 +#define UB953_IND_PGEN_LINE_SIZE0 0x05 +#define UB953_IND_PGEN_BAR_SIZE1 0x06 +#define UB953_IND_PGEN_BAR_SIZE0 0x07 +#define UB953_IND_PGEN_ACT_LPF1 0x08 +#define UB953_IND_PGEN_ACT_LPF0 0x09 +#define UB953_IND_PGEN_TOT_LPF1 0x0a +#define UB953_IND_PGEN_TOT_LPF0 0x0b +#define UB953_IND_PGEN_LINE_PD1 0x0c +#define UB953_IND_PGEN_LINE_PD0 0x0d +#define UB953_IND_PGEN_VBP 0x0e +#define UB953_IND_PGEN_VFP 0x0f +#define UB953_IND_PGEN_COLOR(n) (0x10 + (n)) /* n <= 15 */ + +#define UB953_IND_ANA_TEMP_DYNAMIC_CFG 0x4b +#define UB953_IND_ANA_TEMP_DYNAMIC_CFG_OV BIT(5) +#define UB953_IND_ANA_TEMP_STATIC_CFG 0x4c +#define UB953_IND_ANA_TEMP_STATIC_CFG_MASK GENMASK(6, 4) + +/* UB971 Registers */ + +#define UB971_ENH_BC_CHK 0x4b + +#endif /* __MEDIA_I2C_DS90UB953_H__ */ diff --git a/drivers/media/i2c/ds90ub960.c b/drivers/media/i2c/ds90ub960.c index 33f362a00875..3156f6d6c6de 100644 --- a/drivers/media/i2c/ds90ub960.c +++ b/drivers/media/i2c/ds90ub960.c @@ -27,6 +27,7 @@ */ #include <linux/bitops.h> +#include <linux/cleanup.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/fwnode.h> @@ -43,6 +44,7 @@ #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> +#include <linux/units.h> #include <linux/workqueue.h> #include <media/i2c/ds90ub9xx.h> @@ -51,7 +53,18 @@ #include <media/v4l2-fwnode.h> #include <media/v4l2-subdev.h> -#define MHZ(v) ((u32)((v) * 1000000U)) +#include "ds90ub953.h" + +#define MHZ(v) ((u32)((v) * HZ_PER_MHZ)) + +/* + * If this is defined, the i2c addresses from UB960_DEBUG_I2C_RX_ID to + * UB960_DEBUG_I2C_RX_ID + 3 can be used to access the paged RX port registers + * directly. + * + * Only for debug purposes. + */ +/* #define UB960_DEBUG_I2C_RX_ID 0x40 */ #define UB960_POLL_TIME_MS 500 @@ -233,13 +246,17 @@ #define UB960_RR_BIST_ERR_COUNT 0x57 #define UB960_RR_BCC_CONFIG 0x58 +#define UB960_RR_BCC_CONFIG_BC_ALWAYS_ON BIT(4) +#define UB960_RR_BCC_CONFIG_AUTO_ACK_ALL BIT(5) #define UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH BIT(6) #define UB960_RR_BCC_CONFIG_BC_FREQ_SEL_MASK GENMASK(2, 0) #define UB960_RR_DATAPATH_CTL1 0x59 #define UB960_RR_DATAPATH_CTL2 0x5a #define UB960_RR_SER_ID 0x5b +#define UB960_RR_SER_ID_FREEZE_DEVICE_ID BIT(0) #define UB960_RR_SER_ALIAS_ID 0x5c +#define UB960_RR_SER_ALIAS_ID_AUTO_ACK BIT(0) /* For these two register sets: n < UB960_MAX_PORT_ALIASES */ #define UB960_RR_SLAVE_ID(n) (0x5d + (n)) @@ -297,8 +314,6 @@ #define UB960_XR_REFCLK_FREQ 0xa5 /* UB960 */ -#define UB960_RR_VC_ID_MAP(x) (0xa0 + (x)) /* UB9702 */ - #define UB960_SR_IND_ACC_CTL 0xb0 #define UB960_SR_IND_ACC_CTL_IA_AUTO_INC BIT(1) @@ -311,9 +326,6 @@ #define UB960_SR_FV_MIN_TIME 0xbc #define UB960_SR_GPIO_PD_CTL 0xbe -#define UB960_SR_FPD_RATE_CFG 0xc2 /* UB9702 */ -#define UB960_SR_CSI_PLL_DIV 0xc9 /* UB9702 */ - #define UB960_RR_PORT_DEBUG 0xd0 #define UB960_RR_AEQ_CTL2 0xd2 #define UB960_RR_AEQ_CTL2_SET_AEQ_FLOOR BIT(2) @@ -344,17 +356,15 @@ #define UB960_RR_SEN_INT_RISE_STS 0xde #define UB960_RR_SEN_INT_FALL_STS 0xdf -#define UB960_RR_CHANNEL_MODE 0xe4 /* UB9702 */ #define UB960_SR_FPD3_RX_ID(n) (0xf0 + (n)) #define UB960_SR_FPD3_RX_ID_LEN 6 -#define UB960_SR_I2C_RX_ID(n) (0xf8 + (n)) /* < UB960_FPD_RX_NPORTS */ +#define UB960_SR_I2C_RX_ID(n) (0xf8 + (n)) /* Indirect register blocks */ #define UB960_IND_TARGET_PAT_GEN 0x00 #define UB960_IND_TARGET_RX_ANA(n) (0x01 + (n)) -#define UB960_IND_TARGET_CSI_CSIPLL_REG_1 0x92 /* UB9702 */ #define UB960_IND_TARGET_CSI_ANA 0x07 /* UB960_IR_PGEN_*: Indirect Registers for Test Pattern Generator */ @@ -386,6 +396,49 @@ #define UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY BIT(3) #define UB960_IR_RX_ANA_STROBE_SET_DATA_DELAY_MASK GENMASK(2, 0) +/* UB9702 Registers */ + +#define UB9702_SR_CSI_EXCLUSIVE_FWD2 0x3c +#define UB9702_SR_REFCLK_FREQ 0x3d +#define UB9702_RR_RX_CTL_1 0x80 +#define UB9702_RR_RX_CTL_2 0x87 +#define UB9702_RR_VC_ID_MAP(x) (0xa0 + (x)) +#define UB9702_SR_FPD_RATE_CFG 0xc2 +#define UB9702_SR_CSI_PLL_DIV 0xc9 +#define UB9702_RR_RX_SM_SEL_2 0xd4 +#define UB9702_RR_CHANNEL_MODE 0xe4 + +#define UB9702_IND_TARGET_SAR_ADC 0x0a + +#define UB9702_IR_RX_ANA_FPD_BC_CTL0 0x04 +#define UB9702_IR_RX_ANA_FPD_BC_CTL1 0x0d +#define UB9702_IR_RX_ANA_FPD_BC_CTL2 0x1b +#define UB9702_IR_RX_ANA_SYSTEM_INIT_REG0 0x21 +#define UB9702_IR_RX_ANA_AEQ_ALP_SEL6 0x27 +#define UB9702_IR_RX_ANA_AEQ_ALP_SEL7 0x28 +#define UB9702_IR_RX_ANA_AEQ_ALP_SEL10 0x2b +#define UB9702_IR_RX_ANA_AEQ_ALP_SEL11 0x2c +#define UB9702_IR_RX_ANA_EQ_ADAPT_CTRL 0x2e +#define UB9702_IR_RX_ANA_AEQ_CFG_1 0x34 +#define UB9702_IR_RX_ANA_AEQ_CFG_2 0x4d +#define UB9702_IR_RX_ANA_GAIN_CTRL_0 0x71 +#define UB9702_IR_RX_ANA_GAIN_CTRL_0 0x71 +#define UB9702_IR_RX_ANA_VGA_CTRL_SEL_1 0x72 +#define UB9702_IR_RX_ANA_VGA_CTRL_SEL_2 0x73 +#define UB9702_IR_RX_ANA_VGA_CTRL_SEL_3 0x74 +#define UB9702_IR_RX_ANA_VGA_CTRL_SEL_6 0x77 +#define UB9702_IR_RX_ANA_AEQ_CFG_3 0x79 +#define UB9702_IR_RX_ANA_AEQ_CFG_4 0x85 +#define UB9702_IR_RX_ANA_EQ_CTRL_SEL_15 0x87 +#define UB9702_IR_RX_ANA_EQ_CTRL_SEL_24 0x90 +#define UB9702_IR_RX_ANA_EQ_CTRL_SEL_38 0x9e +#define UB9702_IR_RX_ANA_FPD3_CDR_CTRL_SEL_5 0xa5 +#define UB9702_IR_RX_ANA_FPD3_AEQ_CTRL_SEL_1 0xa8 +#define UB9702_IR_RX_ANA_EQ_OVERRIDE_CTRL 0xf0 +#define UB9702_IR_RX_ANA_VGA_CTRL_SEL_8 0xf1 + +#define UB9702_IR_CSI_ANA_CSIPLL_REG_1 0x92 + /* EQ related */ #define UB960_MIN_AEQ_STROBE_POS -7 @@ -439,7 +492,9 @@ struct ub960_rxport { struct fwnode_handle *fwnode; struct i2c_client *client; unsigned short alias; /* I2C alias (lower 7 bits) */ + short addr; /* Local I2C address (lower 7 bits) */ struct ds90ub9xx_platform_data pdata; + struct regmap *regmap; } ser; enum ub960_rxport_mode rx_mode; @@ -467,7 +522,9 @@ struct ub960_rxport { }; } eq; - const struct i2c_client *aliased_clients[UB960_MAX_PORT_ALIASES]; + /* lock for aliased_addrs and associated registers */ + struct mutex aliased_addrs_lock; + u16 aliased_addrs[UB960_MAX_PORT_ALIASES]; }; struct ub960_asd { @@ -568,11 +625,23 @@ struct ub960_format_info { }; static const struct ub960_format_info ub960_formats[] = { + { .code = MEDIA_BUS_FMT_RGB888_1X24, .bpp = 24, .datatype = MIPI_CSI2_DT_RGB888, }, + { .code = MEDIA_BUS_FMT_YUYV8_1X16, .bpp = 16, .datatype = MIPI_CSI2_DT_YUV422_8B, }, { .code = MEDIA_BUS_FMT_UYVY8_1X16, .bpp = 16, .datatype = MIPI_CSI2_DT_YUV422_8B, }, { .code = MEDIA_BUS_FMT_VYUY8_1X16, .bpp = 16, .datatype = MIPI_CSI2_DT_YUV422_8B, }, { .code = MEDIA_BUS_FMT_YVYU8_1X16, .bpp = 16, .datatype = MIPI_CSI2_DT_YUV422_8B, }, + { .code = MEDIA_BUS_FMT_SBGGR8_1X8, .bpp = 8, .datatype = MIPI_CSI2_DT_RAW8, }, + { .code = MEDIA_BUS_FMT_SGBRG8_1X8, .bpp = 8, .datatype = MIPI_CSI2_DT_RAW8, }, + { .code = MEDIA_BUS_FMT_SGRBG8_1X8, .bpp = 8, .datatype = MIPI_CSI2_DT_RAW8, }, + { .code = MEDIA_BUS_FMT_SRGGB8_1X8, .bpp = 8, .datatype = MIPI_CSI2_DT_RAW8, }, + + { .code = MEDIA_BUS_FMT_SBGGR10_1X10, .bpp = 10, .datatype = MIPI_CSI2_DT_RAW10, }, + { .code = MEDIA_BUS_FMT_SGBRG10_1X10, .bpp = 10, .datatype = MIPI_CSI2_DT_RAW10, }, + { .code = MEDIA_BUS_FMT_SGRBG10_1X10, .bpp = 10, .datatype = MIPI_CSI2_DT_RAW10, }, + { .code = MEDIA_BUS_FMT_SRGGB10_1X10, .bpp = 10, .datatype = MIPI_CSI2_DT_RAW10, }, + { .code = MEDIA_BUS_FMT_SBGGR12_1X12, .bpp = 12, .datatype = MIPI_CSI2_DT_RAW12, }, { .code = MEDIA_BUS_FMT_SGBRG12_1X12, .bpp = 12, .datatype = MIPI_CSI2_DT_RAW12, }, { .code = MEDIA_BUS_FMT_SGRBG12_1X12, .bpp = 12, .datatype = MIPI_CSI2_DT_RAW12, }, @@ -591,16 +660,76 @@ static const struct ub960_format_info *ub960_find_format(u32 code) return NULL; } +struct ub960_rxport_iter { + unsigned int nport; + struct ub960_rxport *rxport; +}; + +enum ub960_iter_flags { + UB960_ITER_ACTIVE_ONLY = BIT(0), + UB960_ITER_FPD4_ONLY = BIT(1), +}; + +static struct ub960_rxport_iter ub960_iter_rxport(struct ub960_data *priv, + struct ub960_rxport_iter it, + enum ub960_iter_flags flags) +{ + for (; it.nport < priv->hw_data->num_rxports; it.nport++) { + it.rxport = priv->rxports[it.nport]; + + if ((flags & UB960_ITER_ACTIVE_ONLY) && !it.rxport) + continue; + + if ((flags & UB960_ITER_FPD4_ONLY) && + it.rxport->cdr_mode != RXPORT_CDR_FPD4) + continue; + + return it; + } + + it.rxport = NULL; + + return it; +} + +#define for_each_rxport(priv, it) \ + for (struct ub960_rxport_iter it = \ + ub960_iter_rxport(priv, (struct ub960_rxport_iter){ 0 }, \ + 0); \ + it.nport < (priv)->hw_data->num_rxports; \ + it.nport++, it = ub960_iter_rxport(priv, it, 0)) + +#define for_each_active_rxport(priv, it) \ + for (struct ub960_rxport_iter it = \ + ub960_iter_rxport(priv, (struct ub960_rxport_iter){ 0 }, \ + UB960_ITER_ACTIVE_ONLY); \ + it.nport < (priv)->hw_data->num_rxports; \ + it.nport++, it = ub960_iter_rxport(priv, it, \ + UB960_ITER_ACTIVE_ONLY)) + +#define for_each_active_rxport_fpd4(priv, it) \ + for (struct ub960_rxport_iter it = \ + ub960_iter_rxport(priv, (struct ub960_rxport_iter){ 0 }, \ + UB960_ITER_ACTIVE_ONLY | \ + UB960_ITER_FPD4_ONLY); \ + it.nport < (priv)->hw_data->num_rxports; \ + it.nport++, it = ub960_iter_rxport(priv, it, \ + UB960_ITER_ACTIVE_ONLY | \ + UB960_ITER_FPD4_ONLY)) + /* ----------------------------------------------------------------------------- * Basic device access */ -static int ub960_read(struct ub960_data *priv, u8 reg, u8 *val) +static int ub960_read(struct ub960_data *priv, u8 reg, u8 *val, int *err) { struct device *dev = &priv->client->dev; unsigned int v; int ret; + if (err && *err) + return *err; + mutex_lock(&priv->reg_lock); ret = regmap_read(priv->regmap, reg, &v); @@ -615,14 +744,20 @@ static int ub960_read(struct ub960_data *priv, u8 reg, u8 *val) out_unlock: mutex_unlock(&priv->reg_lock); + if (ret && err) + *err = ret; + return ret; } -static int ub960_write(struct ub960_data *priv, u8 reg, u8 val) +static int ub960_write(struct ub960_data *priv, u8 reg, u8 val, int *err) { struct device *dev = &priv->client->dev; int ret; + if (err && *err) + return *err; + mutex_lock(&priv->reg_lock); ret = regmap_write(priv->regmap, reg, val); @@ -632,14 +767,21 @@ static int ub960_write(struct ub960_data *priv, u8 reg, u8 val) mutex_unlock(&priv->reg_lock); + if (ret && err) + *err = ret; + return ret; } -static int ub960_update_bits(struct ub960_data *priv, u8 reg, u8 mask, u8 val) +static int ub960_update_bits(struct ub960_data *priv, u8 reg, u8 mask, u8 val, + int *err) { struct device *dev = &priv->client->dev; int ret; + if (err && *err) + return *err; + mutex_lock(&priv->reg_lock); ret = regmap_update_bits(priv->regmap, reg, mask, val); @@ -649,15 +791,21 @@ static int ub960_update_bits(struct ub960_data *priv, u8 reg, u8 mask, u8 val) mutex_unlock(&priv->reg_lock); + if (ret && err) + *err = ret; + return ret; } -static int ub960_read16(struct ub960_data *priv, u8 reg, u16 *val) +static int ub960_read16(struct ub960_data *priv, u8 reg, u16 *val, int *err) { struct device *dev = &priv->client->dev; __be16 __v; int ret; + if (err && *err) + return *err; + mutex_lock(&priv->reg_lock); ret = regmap_bulk_read(priv->regmap, reg, &__v, sizeof(__v)); @@ -672,6 +820,9 @@ static int ub960_read16(struct ub960_data *priv, u8 reg, u16 *val) out_unlock: mutex_unlock(&priv->reg_lock); + if (ret && err) + *err = ret; + return ret; } @@ -698,12 +849,16 @@ static int ub960_rxport_select(struct ub960_data *priv, u8 nport) return 0; } -static int ub960_rxport_read(struct ub960_data *priv, u8 nport, u8 reg, u8 *val) +static int ub960_rxport_read(struct ub960_data *priv, u8 nport, u8 reg, + u8 *val, int *err) { struct device *dev = &priv->client->dev; unsigned int v; int ret; + if (err && *err) + return *err; + mutex_lock(&priv->reg_lock); ret = ub960_rxport_select(priv, nport); @@ -722,14 +877,21 @@ static int ub960_rxport_read(struct ub960_data *priv, u8 nport, u8 reg, u8 *val) out_unlock: mutex_unlock(&priv->reg_lock); + if (ret && err) + *err = ret; + return ret; } -static int ub960_rxport_write(struct ub960_data *priv, u8 nport, u8 reg, u8 val) +static int ub960_rxport_write(struct ub960_data *priv, u8 nport, u8 reg, + u8 val, int *err) { struct device *dev = &priv->client->dev; int ret; + if (err && *err) + return *err; + mutex_lock(&priv->reg_lock); ret = ub960_rxport_select(priv, nport); @@ -744,15 +906,21 @@ static int ub960_rxport_write(struct ub960_data *priv, u8 nport, u8 reg, u8 val) out_unlock: mutex_unlock(&priv->reg_lock); + if (ret && err) + *err = ret; + return ret; } static int ub960_rxport_update_bits(struct ub960_data *priv, u8 nport, u8 reg, - u8 mask, u8 val) + u8 mask, u8 val, int *err) { struct device *dev = &priv->client->dev; int ret; + if (err && *err) + return *err; + mutex_lock(&priv->reg_lock); ret = ub960_rxport_select(priv, nport); @@ -767,16 +935,22 @@ static int ub960_rxport_update_bits(struct ub960_data *priv, u8 nport, u8 reg, out_unlock: mutex_unlock(&priv->reg_lock); + if (ret && err) + *err = ret; + return ret; } static int ub960_rxport_read16(struct ub960_data *priv, u8 nport, u8 reg, - u16 *val) + u16 *val, int *err) { struct device *dev = &priv->client->dev; __be16 __v; int ret; + if (err && *err) + return *err; + mutex_lock(&priv->reg_lock); ret = ub960_rxport_select(priv, nport); @@ -795,6 +969,9 @@ static int ub960_rxport_read16(struct ub960_data *priv, u8 nport, u8 reg, out_unlock: mutex_unlock(&priv->reg_lock); + if (ret && err) + *err = ret; + return ret; } @@ -821,12 +998,16 @@ static int ub960_txport_select(struct ub960_data *priv, u8 nport) return 0; } -static int ub960_txport_read(struct ub960_data *priv, u8 nport, u8 reg, u8 *val) +static int ub960_txport_read(struct ub960_data *priv, u8 nport, u8 reg, + u8 *val, int *err) { struct device *dev = &priv->client->dev; unsigned int v; int ret; + if (err && *err) + return *err; + mutex_lock(&priv->reg_lock); ret = ub960_txport_select(priv, nport); @@ -845,14 +1026,21 @@ static int ub960_txport_read(struct ub960_data *priv, u8 nport, u8 reg, u8 *val) out_unlock: mutex_unlock(&priv->reg_lock); + if (ret && err) + *err = ret; + return ret; } -static int ub960_txport_write(struct ub960_data *priv, u8 nport, u8 reg, u8 val) +static int ub960_txport_write(struct ub960_data *priv, u8 nport, u8 reg, + u8 val, int *err) { struct device *dev = &priv->client->dev; int ret; + if (err && *err) + return *err; + mutex_lock(&priv->reg_lock); ret = ub960_txport_select(priv, nport); @@ -867,15 +1055,21 @@ static int ub960_txport_write(struct ub960_data *priv, u8 nport, u8 reg, u8 val) out_unlock: mutex_unlock(&priv->reg_lock); + if (ret && err) + *err = ret; + return ret; } static int ub960_txport_update_bits(struct ub960_data *priv, u8 nport, u8 reg, - u8 mask, u8 val) + u8 mask, u8 val, int *err) { struct device *dev = &priv->client->dev; int ret; + if (err && *err) + return *err; + mutex_lock(&priv->reg_lock); ret = ub960_txport_select(priv, nport); @@ -890,6 +1084,9 @@ static int ub960_txport_update_bits(struct ub960_data *priv, u8 nport, u8 reg, out_unlock: mutex_unlock(&priv->reg_lock); + if (ret && err) + *err = ret; + return ret; } @@ -915,12 +1112,16 @@ static int ub960_select_ind_reg_block(struct ub960_data *priv, u8 block) return 0; } -static int ub960_read_ind(struct ub960_data *priv, u8 block, u8 reg, u8 *val) +static int ub960_read_ind(struct ub960_data *priv, u8 block, u8 reg, u8 *val, + int *err) { struct device *dev = &priv->client->dev; unsigned int v; int ret; + if (err && *err) + return *err; + mutex_lock(&priv->reg_lock); ret = ub960_select_ind_reg_block(priv, block); @@ -948,14 +1149,21 @@ static int ub960_read_ind(struct ub960_data *priv, u8 block, u8 reg, u8 *val) out_unlock: mutex_unlock(&priv->reg_lock); + if (ret && err) + *err = ret; + return ret; } -static int ub960_write_ind(struct ub960_data *priv, u8 block, u8 reg, u8 val) +static int ub960_write_ind(struct ub960_data *priv, u8 block, u8 reg, u8 val, + int *err) { struct device *dev = &priv->client->dev; int ret; + if (err && *err) + return *err; + mutex_lock(&priv->reg_lock); ret = ub960_select_ind_reg_block(priv, block); @@ -981,15 +1189,21 @@ static int ub960_write_ind(struct ub960_data *priv, u8 block, u8 reg, u8 val) out_unlock: mutex_unlock(&priv->reg_lock); + if (ret && err) + *err = ret; + return ret; } static int ub960_ind_update_bits(struct ub960_data *priv, u8 block, u8 reg, - u8 mask, u8 val) + u8 mask, u8 val, int *err) { struct device *dev = &priv->client->dev; int ret; + if (err && *err) + return *err; + mutex_lock(&priv->reg_lock); ret = ub960_select_ind_reg_block(priv, block); @@ -1016,6 +1230,36 @@ static int ub960_ind_update_bits(struct ub960_data *priv, u8 block, u8 reg, out_unlock: mutex_unlock(&priv->reg_lock); + if (ret && err) + *err = ret; + + return ret; +} + +static int ub960_reset(struct ub960_data *priv, bool reset_regs) +{ + struct device *dev = &priv->client->dev; + unsigned int v; + int ret; + u8 bit; + + bit = reset_regs ? UB960_SR_RESET_DIGITAL_RESET1 : + UB960_SR_RESET_DIGITAL_RESET0; + + ret = ub960_write(priv, UB960_SR_RESET, bit, NULL); + if (ret) + return ret; + + mutex_lock(&priv->reg_lock); + + ret = regmap_read_poll_timeout(priv->regmap, UB960_SR_RESET, v, + (v & bit) == 0, 2000, 100000); + + mutex_unlock(&priv->reg_lock); + + if (ret) + dev_err(dev, "reset failed: %d\n", ret); + return ret; } @@ -1023,67 +1267,82 @@ out_unlock: * I2C-ATR (address translator) */ -static int ub960_atr_attach_client(struct i2c_atr *atr, u32 chan_id, - const struct i2c_client *client, u16 alias) +static int ub960_atr_attach_addr(struct i2c_atr *atr, u32 chan_id, + u16 addr, u16 alias) { struct ub960_data *priv = i2c_atr_get_driver_data(atr); struct ub960_rxport *rxport = priv->rxports[chan_id]; struct device *dev = &priv->client->dev; unsigned int reg_idx; + int ret = 0; - for (reg_idx = 0; reg_idx < ARRAY_SIZE(rxport->aliased_clients); reg_idx++) { - if (!rxport->aliased_clients[reg_idx]) + guard(mutex)(&rxport->aliased_addrs_lock); + + for (reg_idx = 0; reg_idx < ARRAY_SIZE(rxport->aliased_addrs); reg_idx++) { + if (!rxport->aliased_addrs[reg_idx]) break; } - if (reg_idx == ARRAY_SIZE(rxport->aliased_clients)) { + if (reg_idx == ARRAY_SIZE(rxport->aliased_addrs)) { dev_err(dev, "rx%u: alias pool exhausted\n", rxport->nport); return -EADDRNOTAVAIL; } - rxport->aliased_clients[reg_idx] = client; + rxport->aliased_addrs[reg_idx] = addr; ub960_rxport_write(priv, chan_id, UB960_RR_SLAVE_ID(reg_idx), - client->addr << 1); + addr << 1, &ret); ub960_rxport_write(priv, chan_id, UB960_RR_SLAVE_ALIAS(reg_idx), - alias << 1); + alias << 1, &ret); + + if (ret) + return ret; dev_dbg(dev, "rx%u: client 0x%02x assigned alias 0x%02x at slot %u\n", - rxport->nport, client->addr, alias, reg_idx); + rxport->nport, addr, alias, reg_idx); return 0; } -static void ub960_atr_detach_client(struct i2c_atr *atr, u32 chan_id, - const struct i2c_client *client) +static void ub960_atr_detach_addr(struct i2c_atr *atr, u32 chan_id, + u16 addr) { struct ub960_data *priv = i2c_atr_get_driver_data(atr); struct ub960_rxport *rxport = priv->rxports[chan_id]; struct device *dev = &priv->client->dev; unsigned int reg_idx; + int ret; + + guard(mutex)(&rxport->aliased_addrs_lock); - for (reg_idx = 0; reg_idx < ARRAY_SIZE(rxport->aliased_clients); reg_idx++) { - if (rxport->aliased_clients[reg_idx] == client) + for (reg_idx = 0; reg_idx < ARRAY_SIZE(rxport->aliased_addrs); reg_idx++) { + if (rxport->aliased_addrs[reg_idx] == addr) break; } - if (reg_idx == ARRAY_SIZE(rxport->aliased_clients)) { + if (reg_idx == ARRAY_SIZE(rxport->aliased_addrs)) { dev_err(dev, "rx%u: client 0x%02x is not mapped!\n", - rxport->nport, client->addr); + rxport->nport, addr); return; } - rxport->aliased_clients[reg_idx] = NULL; + rxport->aliased_addrs[reg_idx] = 0; - ub960_rxport_write(priv, chan_id, UB960_RR_SLAVE_ALIAS(reg_idx), 0); + ret = ub960_rxport_write(priv, chan_id, UB960_RR_SLAVE_ALIAS(reg_idx), + 0, NULL); + if (ret) { + dev_err(dev, "rx%u: unable to fully unmap client 0x%02x: %d\n", + rxport->nport, addr, ret); + return; + } dev_dbg(dev, "rx%u: client 0x%02x released at slot %u\n", rxport->nport, - client->addr, reg_idx); + addr, reg_idx); } static const struct i2c_atr_ops ub960_atr_ops = { - .attach_client = ub960_atr_attach_client, - .detach_client = ub960_atr_detach_client, + .attach_addr = ub960_atr_attach_addr, + .detach_addr = ub960_atr_detach_addr, }; static int ub960_init_atr(struct ub960_data *priv) @@ -1092,7 +1351,7 @@ static int ub960_init_atr(struct ub960_data *priv) struct i2c_adapter *parent_adap = priv->client->adapter; priv->atr = i2c_atr_new(parent_adap, dev, &ub960_atr_ops, - priv->hw_data->num_rxports); + priv->hw_data->num_rxports, 0); if (IS_ERR(priv->atr)) return PTR_ERR(priv->atr); @@ -1170,21 +1429,24 @@ err_free_txport: return ret; } -static void ub960_csi_handle_events(struct ub960_data *priv, u8 nport) +static int ub960_csi_handle_events(struct ub960_data *priv, u8 nport) { struct device *dev = &priv->client->dev; u8 csi_tx_isr; int ret; - ret = ub960_txport_read(priv, nport, UB960_TR_CSI_TX_ISR, &csi_tx_isr); + ret = ub960_txport_read(priv, nport, UB960_TR_CSI_TX_ISR, &csi_tx_isr, + NULL); if (ret) - return; + return ret; if (csi_tx_isr & UB960_TR_CSI_TX_ISR_IS_CSI_SYNC_ERROR) dev_warn(dev, "TX%u: CSI_SYNC_ERROR\n", nport); if (csi_tx_isr & UB960_TR_CSI_TX_ISR_IS_CSI_PASS_ERROR) dev_warn(dev, "TX%u: CSI_PASS_ERROR\n", nport); + + return 0; } /* ----------------------------------------------------------------------------- @@ -1193,25 +1455,25 @@ static void ub960_csi_handle_events(struct ub960_data *priv, u8 nport) static int ub960_rxport_enable_vpocs(struct ub960_data *priv) { - unsigned int nport; + unsigned int failed_nport; int ret; - for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { - struct ub960_rxport *rxport = priv->rxports[nport]; - - if (!rxport || !rxport->vpoc) + for_each_active_rxport(priv, it) { + if (!it.rxport->vpoc) continue; - ret = regulator_enable(rxport->vpoc); - if (ret) + ret = regulator_enable(it.rxport->vpoc); + if (ret) { + failed_nport = it.nport; goto err_disable_vpocs; + } } return 0; err_disable_vpocs: - while (nport--) { - struct ub960_rxport *rxport = priv->rxports[nport]; + while (failed_nport--) { + struct ub960_rxport *rxport = priv->rxports[failed_nport]; if (!rxport || !rxport->vpoc) continue; @@ -1224,40 +1486,44 @@ err_disable_vpocs: static void ub960_rxport_disable_vpocs(struct ub960_data *priv) { - unsigned int nport; - - for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { - struct ub960_rxport *rxport = priv->rxports[nport]; - - if (!rxport || !rxport->vpoc) + for_each_active_rxport(priv, it) { + if (!it.rxport->vpoc) continue; - regulator_disable(rxport->vpoc); + regulator_disable(it.rxport->vpoc); } } -static void ub960_rxport_clear_errors(struct ub960_data *priv, - unsigned int nport) +static int ub960_rxport_clear_errors(struct ub960_data *priv, + unsigned int nport) { + int ret = 0; u8 v; - ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS1, &v); - ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS2, &v); - ub960_rxport_read(priv, nport, UB960_RR_CSI_RX_STS, &v); - ub960_rxport_read(priv, nport, UB960_RR_BCC_STATUS, &v); + ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS1, &v, &ret); + ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS2, &v, &ret); + ub960_rxport_read(priv, nport, UB960_RR_CSI_RX_STS, &v, &ret); + ub960_rxport_read(priv, nport, UB960_RR_BCC_STATUS, &v, &ret); + + ub960_rxport_read(priv, nport, UB960_RR_RX_PAR_ERR_HI, &v, &ret); + ub960_rxport_read(priv, nport, UB960_RR_RX_PAR_ERR_LO, &v, &ret); - ub960_rxport_read(priv, nport, UB960_RR_RX_PAR_ERR_HI, &v); - ub960_rxport_read(priv, nport, UB960_RR_RX_PAR_ERR_LO, &v); + ub960_rxport_read(priv, nport, UB960_RR_CSI_ERR_COUNTER, &v, &ret); - ub960_rxport_read(priv, nport, UB960_RR_CSI_ERR_COUNTER, &v); + return ret; } -static void ub960_clear_rx_errors(struct ub960_data *priv) +static int ub960_clear_rx_errors(struct ub960_data *priv) { - unsigned int nport; + int ret; - for (nport = 0; nport < priv->hw_data->num_rxports; nport++) - ub960_rxport_clear_errors(priv, nport); + for_each_rxport(priv, it) { + ret = ub960_rxport_clear_errors(priv, it.nport); + if (ret) + return ret; + } + + return 0; } static int ub960_rxport_get_strobe_pos(struct ub960_data *priv, @@ -1267,25 +1533,29 @@ static int ub960_rxport_get_strobe_pos(struct ub960_data *priv, u8 clk_delay, data_delay; int ret; - ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport), - UB960_IR_RX_ANA_STROBE_SET_CLK, &v); + ret = ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB960_IR_RX_ANA_STROBE_SET_CLK, &v, NULL); + if (ret) + return ret; clk_delay = (v & UB960_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY) ? 0 : UB960_MANUAL_STROBE_EXTRA_DELAY; - ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport), - UB960_IR_RX_ANA_STROBE_SET_DATA, &v); + ret = ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB960_IR_RX_ANA_STROBE_SET_DATA, &v, NULL); + if (ret) + return ret; data_delay = (v & UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY) ? 0 : UB960_MANUAL_STROBE_EXTRA_DELAY; - ret = ub960_rxport_read(priv, nport, UB960_RR_SFILTER_STS_0, &v); + ret = ub960_rxport_read(priv, nport, UB960_RR_SFILTER_STS_0, &v, NULL); if (ret) return ret; clk_delay += v & UB960_IR_RX_ANA_STROBE_SET_CLK_DELAY_MASK; - ret = ub960_rxport_read(priv, nport, UB960_RR_SFILTER_STS_1, &v); + ret = ub960_rxport_read(priv, nport, UB960_RR_SFILTER_STS_1, &v, NULL); if (ret) return ret; @@ -1296,10 +1566,11 @@ static int ub960_rxport_get_strobe_pos(struct ub960_data *priv, return 0; } -static void ub960_rxport_set_strobe_pos(struct ub960_data *priv, - unsigned int nport, s8 strobe_pos) +static int ub960_rxport_set_strobe_pos(struct ub960_data *priv, + unsigned int nport, s8 strobe_pos) { u8 clk_delay, data_delay; + int ret = 0; clk_delay = UB960_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY; data_delay = UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY; @@ -1314,22 +1585,25 @@ static void ub960_rxport_set_strobe_pos(struct ub960_data *priv, data_delay = strobe_pos | UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY; ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), - UB960_IR_RX_ANA_STROBE_SET_CLK, clk_delay); + UB960_IR_RX_ANA_STROBE_SET_CLK, clk_delay, &ret); ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), - UB960_IR_RX_ANA_STROBE_SET_DATA, data_delay); + UB960_IR_RX_ANA_STROBE_SET_DATA, data_delay, &ret); + + return ret; } -static void ub960_rxport_set_strobe_range(struct ub960_data *priv, - s8 strobe_min, s8 strobe_max) +static int ub960_rxport_set_strobe_range(struct ub960_data *priv, s8 strobe_min, + s8 strobe_max) { /* Convert the signed strobe pos to positive zero based value */ strobe_min -= UB960_MIN_AEQ_STROBE_POS; strobe_max -= UB960_MIN_AEQ_STROBE_POS; - ub960_write(priv, UB960_XR_SFILTER_CFG, - ((u8)strobe_min << UB960_XR_SFILTER_CFG_SFILTER_MIN_SHIFT) | - ((u8)strobe_max << UB960_XR_SFILTER_CFG_SFILTER_MAX_SHIFT)); + return ub960_write(priv, UB960_XR_SFILTER_CFG, + ((u8)strobe_min << UB960_XR_SFILTER_CFG_SFILTER_MIN_SHIFT) | + ((u8)strobe_max << UB960_XR_SFILTER_CFG_SFILTER_MAX_SHIFT), + NULL); } static int ub960_rxport_get_eq_level(struct ub960_data *priv, @@ -1338,7 +1612,7 @@ static int ub960_rxport_get_eq_level(struct ub960_data *priv, int ret; u8 v; - ret = ub960_rxport_read(priv, nport, UB960_RR_AEQ_STATUS, &v); + ret = ub960_rxport_read(priv, nport, UB960_RR_AEQ_STATUS, &v, NULL); if (ret) return ret; @@ -1348,11 +1622,12 @@ static int ub960_rxport_get_eq_level(struct ub960_data *priv, return 0; } -static void ub960_rxport_set_eq_level(struct ub960_data *priv, - unsigned int nport, u8 eq_level) +static int ub960_rxport_set_eq_level(struct ub960_data *priv, + unsigned int nport, u8 eq_level) { u8 eq_stage_1_select_value, eq_stage_2_select_value; const unsigned int eq_stage_max = 7; + int ret; u8 v; if (eq_level <= eq_stage_max) { @@ -1363,7 +1638,9 @@ static void ub960_rxport_set_eq_level(struct ub960_data *priv, eq_stage_2_select_value = eq_level - eq_stage_max; } - ub960_rxport_read(priv, nport, UB960_RR_AEQ_BYPASS, &v); + ret = ub960_rxport_read(priv, nport, UB960_RR_AEQ_BYPASS, &v, NULL); + if (ret) + return ret; v &= ~(UB960_RR_AEQ_BYPASS_EQ_STAGE1_VALUE_MASK | UB960_RR_AEQ_BYPASS_EQ_STAGE2_VALUE_MASK); @@ -1371,67 +1648,102 @@ static void ub960_rxport_set_eq_level(struct ub960_data *priv, v |= eq_stage_2_select_value << UB960_RR_AEQ_BYPASS_EQ_STAGE2_VALUE_SHIFT; v |= UB960_RR_AEQ_BYPASS_ENABLE; - ub960_rxport_write(priv, nport, UB960_RR_AEQ_BYPASS, v); + ret = ub960_rxport_write(priv, nport, UB960_RR_AEQ_BYPASS, v, NULL); + if (ret) + return ret; + + return 0; } -static void ub960_rxport_set_eq_range(struct ub960_data *priv, - unsigned int nport, u8 eq_min, u8 eq_max) +static int ub960_rxport_set_eq_range(struct ub960_data *priv, + unsigned int nport, u8 eq_min, u8 eq_max) { + int ret = 0; + ub960_rxport_write(priv, nport, UB960_RR_AEQ_MIN_MAX, (eq_min << UB960_RR_AEQ_MIN_MAX_AEQ_FLOOR_SHIFT) | - (eq_max << UB960_RR_AEQ_MIN_MAX_AEQ_MAX_SHIFT)); + (eq_max << UB960_RR_AEQ_MIN_MAX_AEQ_MAX_SHIFT), + &ret); /* Enable AEQ min setting */ ub960_rxport_update_bits(priv, nport, UB960_RR_AEQ_CTL2, UB960_RR_AEQ_CTL2_SET_AEQ_FLOOR, - UB960_RR_AEQ_CTL2_SET_AEQ_FLOOR); + UB960_RR_AEQ_CTL2_SET_AEQ_FLOOR, &ret); + + return ret; } -static void ub960_rxport_config_eq(struct ub960_data *priv, unsigned int nport) +static int ub960_rxport_config_eq(struct ub960_data *priv, unsigned int nport) { struct ub960_rxport *rxport = priv->rxports[nport]; + int ret; /* We also set common settings here. Should be moved elsewhere. */ if (priv->strobe.manual) { /* Disable AEQ_SFILTER_EN */ - ub960_update_bits(priv, UB960_XR_AEQ_CTL1, - UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN, 0); + ret = ub960_update_bits(priv, UB960_XR_AEQ_CTL1, + UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN, 0, + NULL); + if (ret) + return ret; } else { /* Enable SFILTER and error control */ - ub960_write(priv, UB960_XR_AEQ_CTL1, - UB960_XR_AEQ_CTL1_AEQ_ERR_CTL_MASK | - UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN); + ret = ub960_write(priv, UB960_XR_AEQ_CTL1, + UB960_XR_AEQ_CTL1_AEQ_ERR_CTL_MASK | + UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN, + NULL); + + if (ret) + return ret; /* Set AEQ strobe range */ - ub960_rxport_set_strobe_range(priv, priv->strobe.min, - priv->strobe.max); + ret = ub960_rxport_set_strobe_range(priv, priv->strobe.min, + priv->strobe.max); + if (ret) + return ret; } /* The rest are port specific */ if (priv->strobe.manual) - ub960_rxport_set_strobe_pos(priv, nport, rxport->eq.strobe_pos); + ret = ub960_rxport_set_strobe_pos(priv, nport, + rxport->eq.strobe_pos); else - ub960_rxport_set_strobe_pos(priv, nport, 0); + ret = ub960_rxport_set_strobe_pos(priv, nport, 0); + + if (ret) + return ret; if (rxport->eq.manual_eq) { - ub960_rxport_set_eq_level(priv, nport, - rxport->eq.manual.eq_level); + ret = ub960_rxport_set_eq_level(priv, nport, + rxport->eq.manual.eq_level); + if (ret) + return ret; /* Enable AEQ Bypass */ - ub960_rxport_update_bits(priv, nport, UB960_RR_AEQ_BYPASS, - UB960_RR_AEQ_BYPASS_ENABLE, - UB960_RR_AEQ_BYPASS_ENABLE); + ret = ub960_rxport_update_bits(priv, nport, UB960_RR_AEQ_BYPASS, + UB960_RR_AEQ_BYPASS_ENABLE, + UB960_RR_AEQ_BYPASS_ENABLE, + NULL); + if (ret) + return ret; } else { - ub960_rxport_set_eq_range(priv, nport, - rxport->eq.aeq.eq_level_min, - rxport->eq.aeq.eq_level_max); + ret = ub960_rxport_set_eq_range(priv, nport, + rxport->eq.aeq.eq_level_min, + rxport->eq.aeq.eq_level_max); + if (ret) + return ret; /* Disable AEQ Bypass */ - ub960_rxport_update_bits(priv, nport, UB960_RR_AEQ_BYPASS, - UB960_RR_AEQ_BYPASS_ENABLE, 0); + ret = ub960_rxport_update_bits(priv, nport, UB960_RR_AEQ_BYPASS, + UB960_RR_AEQ_BYPASS_ENABLE, 0, + NULL); + if (ret) + return ret; } + + return 0; } static int ub960_rxport_link_ok(struct ub960_data *priv, unsigned int nport, @@ -1446,7 +1758,7 @@ static int ub960_rxport_link_ok(struct ub960_data *priv, unsigned int nport, bool errors; ret = ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS1, - &rx_port_sts1); + &rx_port_sts1, NULL); if (ret) return ret; @@ -1456,25 +1768,27 @@ static int ub960_rxport_link_ok(struct ub960_data *priv, unsigned int nport, } ret = ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS2, - &rx_port_sts2); + &rx_port_sts2, NULL); if (ret) return ret; - ret = ub960_rxport_read(priv, nport, UB960_RR_CSI_RX_STS, &csi_rx_sts); + ret = ub960_rxport_read(priv, nport, UB960_RR_CSI_RX_STS, &csi_rx_sts, + NULL); if (ret) return ret; ret = ub960_rxport_read(priv, nport, UB960_RR_CSI_ERR_COUNTER, - &csi_err_cnt); + &csi_err_cnt, NULL); if (ret) return ret; - ret = ub960_rxport_read(priv, nport, UB960_RR_BCC_STATUS, &bcc_sts); + ret = ub960_rxport_read(priv, nport, UB960_RR_BCC_STATUS, &bcc_sts, + NULL); if (ret) return ret; ret = ub960_rxport_read16(priv, nport, UB960_RR_RX_PAR_ERR_HI, - &parity_errors); + &parity_errors, NULL); if (ret) return ret; @@ -1489,6 +1803,23 @@ static int ub960_rxport_link_ok(struct ub960_data *priv, unsigned int nport, return 0; } +static int ub960_rxport_lockup_wa_ub9702(struct ub960_data *priv) +{ + int ret; + + /* Toggle PI_MODE to avoid possible FPD RX lockup */ + + ret = ub960_update_bits(priv, UB9702_RR_CHANNEL_MODE, GENMASK(4, 3), + 2 << 3, NULL); + if (ret) + return ret; + + usleep_range(1000, 5000); + + return ub960_update_bits(priv, UB9702_RR_CHANNEL_MODE, GENMASK(4, 3), + 0, NULL); +} + /* * Wait for the RX ports to lock, have no errors and have stable strobe position * and EQ level. @@ -1519,6 +1850,7 @@ static int ub960_rxport_wait_locks(struct ub960_data *priv, link_ok_mask = 0; while (time_before(jiffies, timeout)) { + bool fpd4_wa = false; missing = 0; for_each_set_bit(nport, &port_mask, @@ -1533,6 +1865,9 @@ static int ub960_rxport_wait_locks(struct ub960_data *priv, if (ret) return ret; + if (!ok && rxport->cdr_mode == RXPORT_CDR_FPD4) + fpd4_wa = true; + /* * We want the link to be ok for two consecutive loops, * as a link could get established just before our test @@ -1552,7 +1887,18 @@ static int ub960_rxport_wait_locks(struct ub960_data *priv, if (missing == 0) break; - msleep(50); + if (fpd4_wa) { + ret = ub960_rxport_lockup_wa_ub9702(priv); + if (ret) + return ret; + } + + /* + * The sleep time of 10 ms was found by testing to give a lock + * with a few iterations. It can be decreased if on some setups + * the lock can be achieved much faster. + */ + fsleep(10 * USEC_PER_MSEC); } if (lock_mask) @@ -1572,18 +1918,30 @@ static int ub960_rxport_wait_locks(struct ub960_data *priv, continue; } - ub960_rxport_read16(priv, nport, UB960_RR_RX_FREQ_HIGH, &v); + ret = ub960_rxport_read16(priv, nport, UB960_RR_RX_FREQ_HIGH, + &v, NULL); - ret = ub960_rxport_get_strobe_pos(priv, nport, &strobe_pos); if (ret) return ret; - ret = ub960_rxport_get_eq_level(priv, nport, &eq_level); - if (ret) - return ret; + if (priv->hw_data->is_ub9702) { + dev_dbg(dev, "\trx%u: locked, freq %llu Hz\n", + nport, ((u64)v * HZ_PER_MHZ) >> 8); + } else { + ret = ub960_rxport_get_strobe_pos(priv, nport, + &strobe_pos); + if (ret) + return ret; - dev_dbg(dev, "\trx%u: locked, SP: %d, EQ: %u, freq %llu Hz\n", - nport, strobe_pos, eq_level, (v * 1000000ULL) >> 8); + ret = ub960_rxport_get_eq_level(priv, nport, &eq_level); + if (ret) + return ret; + + dev_dbg(dev, + "\trx%u: locked, SP: %d, EQ: %u, freq %llu Hz\n", + nport, strobe_pos, eq_level, + ((u64)v * HZ_PER_MHZ) >> 8); + } } return 0; @@ -1640,13 +1998,188 @@ static unsigned long ub960_calc_bc_clk_rate_ub9702(struct ub960_data *priv, } } +static int ub960_rxport_serializer_write(struct ub960_rxport *rxport, u8 reg, + u8 val, int *err) +{ + struct ub960_data *priv = rxport->priv; + struct device *dev = &priv->client->dev; + union i2c_smbus_data data; + int ret; + + if (err && *err) + return *err; + + data.byte = val; + + ret = i2c_smbus_xfer(priv->client->adapter, rxport->ser.alias, 0, + I2C_SMBUS_WRITE, reg, I2C_SMBUS_BYTE_DATA, &data); + if (ret) + dev_err(dev, + "rx%u: cannot write serializer register 0x%02x (%d)!\n", + rxport->nport, reg, ret); + + if (ret && err) + *err = ret; + + return ret; +} + +static int ub960_rxport_serializer_read(struct ub960_rxport *rxport, u8 reg, + u8 *val, int *err) +{ + struct ub960_data *priv = rxport->priv; + struct device *dev = &priv->client->dev; + union i2c_smbus_data data = { 0 }; + int ret; + + if (err && *err) + return *err; + + ret = i2c_smbus_xfer(priv->client->adapter, rxport->ser.alias, + priv->client->flags, I2C_SMBUS_READ, reg, + I2C_SMBUS_BYTE_DATA, &data); + if (ret) + dev_err(dev, + "rx%u: cannot read serializer register 0x%02x (%d)!\n", + rxport->nport, reg, ret); + else + *val = data.byte; + + if (ret && err) + *err = ret; + + return ret; +} + +static int ub960_serializer_temp_ramp(struct ub960_rxport *rxport) +{ + struct ub960_data *priv = rxport->priv; + short temp_dynamic_offset[] = {-1, -1, 0, 0, 1, 1, 1, 3}; + u8 temp_dynamic_cfg; + u8 nport = rxport->nport; + u8 ser_temp_code; + int ret = 0; + + /* Configure temp ramp only on UB953 */ + if (!fwnode_device_is_compatible(rxport->ser.fwnode, "ti,ds90ub953-q1")) + return 0; + + /* Read current serializer die temperature */ + ub960_rxport_read(priv, nport, UB960_RR_SENSOR_STS_2, &ser_temp_code, + &ret); + + /* Enable I2C passthrough on back channel */ + ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, + UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH, + UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH, &ret); + + if (ret) + return ret; + + /* Select indirect page for analog regs on the serializer */ + ub960_rxport_serializer_write(rxport, UB953_REG_IND_ACC_CTL, + UB953_IND_TARGET_ANALOG << 2, &ret); + + /* Set temperature ramp dynamic and static config */ + ub960_rxport_serializer_write(rxport, UB953_REG_IND_ACC_ADDR, + UB953_IND_ANA_TEMP_DYNAMIC_CFG, &ret); + ub960_rxport_serializer_read(rxport, UB953_REG_IND_ACC_DATA, + &temp_dynamic_cfg, &ret); + + if (ret) + return ret; + + temp_dynamic_cfg |= UB953_IND_ANA_TEMP_DYNAMIC_CFG_OV; + temp_dynamic_cfg += temp_dynamic_offset[ser_temp_code]; + + /* Update temp static config */ + ub960_rxport_serializer_write(rxport, UB953_REG_IND_ACC_ADDR, + UB953_IND_ANA_TEMP_STATIC_CFG, &ret); + ub960_rxport_serializer_write(rxport, UB953_REG_IND_ACC_DATA, + UB953_IND_ANA_TEMP_STATIC_CFG_MASK, &ret); + + /* Update temperature ramp dynamic config */ + ub960_rxport_serializer_write(rxport, UB953_REG_IND_ACC_ADDR, + UB953_IND_ANA_TEMP_DYNAMIC_CFG, &ret); + + /* Enable I2C auto ack on BC before we set dynamic cfg and reset */ + ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, + UB960_RR_BCC_CONFIG_AUTO_ACK_ALL, + UB960_RR_BCC_CONFIG_AUTO_ACK_ALL, &ret); + + ub960_rxport_serializer_write(rxport, UB953_REG_IND_ACC_DATA, + temp_dynamic_cfg, &ret); + + if (ret) + return ret; + + /* Soft reset to apply PLL updates */ + ub960_rxport_serializer_write(rxport, UB953_REG_RESET_CTL, + UB953_REG_RESET_CTL_DIGITAL_RESET_0, + &ret); + msleep(20); + + /* Disable I2C passthrough and auto-ack on BC */ + ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, + UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH | + UB960_RR_BCC_CONFIG_AUTO_ACK_ALL, + 0x0, &ret); + + return ret; +} + +static int ub960_rxport_bc_ser_config(struct ub960_rxport *rxport) +{ + struct ub960_data *priv = rxport->priv; + struct device *dev = &priv->client->dev; + u8 nport = rxport->nport; + int ret = 0; + + /* Skip port if serializer's address is not known */ + if (rxport->ser.addr < 0) { + dev_dbg(dev, + "rx%u: serializer address missing, skip configuration\n", + nport); + return 0; + } + + /* + * Note: the code here probably only works for CSI-2 serializers in + * sync mode. To support other serializers the BC related configuration + * should be done before calling this function. + */ + + /* Enable I2C passthrough and auto-ack on BC */ + ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, + UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH | + UB960_RR_BCC_CONFIG_AUTO_ACK_ALL, + UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH | + UB960_RR_BCC_CONFIG_AUTO_ACK_ALL, + &ret); + + if (ret) + return ret; + + /* Disable BC alternate mode auto detect */ + ub960_rxport_serializer_write(rxport, UB971_ENH_BC_CHK, 0x02, &ret); + /* Decrease link detect timer */ + ub960_rxport_serializer_write(rxport, UB953_REG_BC_CTRL, 0x06, &ret); + + /* Disable I2C passthrough and auto-ack on BC */ + ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, + UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH | + UB960_RR_BCC_CONFIG_AUTO_ACK_ALL, + 0x0, &ret); + + return ret; +} + static int ub960_rxport_add_serializer(struct ub960_data *priv, u8 nport) { struct ub960_rxport *rxport = priv->rxports[nport]; struct device *dev = &priv->client->dev; struct ds90ub9xx_platform_data *ser_pdata = &rxport->ser.pdata; struct i2c_board_info ser_info = { - .of_node = to_of_node(rxport->ser.fwnode), .fwnode = rxport->ser.fwnode, .platform_data = ser_pdata, }; @@ -1690,30 +2223,27 @@ static void ub960_rxport_remove_serializer(struct ub960_data *priv, u8 nport) /* Add serializer i2c devices for all initialized ports */ static int ub960_rxport_add_serializers(struct ub960_data *priv) { - unsigned int nport; + unsigned int failed_nport; int ret; - for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { - struct ub960_rxport *rxport = priv->rxports[nport]; - - if (!rxport) - continue; - - ret = ub960_rxport_add_serializer(priv, nport); - if (ret) + for_each_active_rxport(priv, it) { + ret = ub960_rxport_add_serializer(priv, it.nport); + if (ret) { + failed_nport = it.nport; goto err_remove_sers; + } } return 0; err_remove_sers: - while (nport--) { - struct ub960_rxport *rxport = priv->rxports[nport]; + while (failed_nport--) { + struct ub960_rxport *rxport = priv->rxports[failed_nport]; if (!rxport) continue; - ub960_rxport_remove_serializer(priv, nport); + ub960_rxport_remove_serializer(priv, failed_nport); } return ret; @@ -1721,20 +2251,12 @@ err_remove_sers: static void ub960_rxport_remove_serializers(struct ub960_data *priv) { - unsigned int nport; - - for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { - struct ub960_rxport *rxport = priv->rxports[nport]; - - if (!rxport) - continue; - - ub960_rxport_remove_serializer(priv, nport); - } + for_each_active_rxport(priv, it) + ub960_rxport_remove_serializer(priv, it.nport); } -static void ub960_init_tx_port(struct ub960_data *priv, - struct ub960_txport *txport) +static int ub960_init_tx_port(struct ub960_data *priv, + struct ub960_txport *txport) { unsigned int nport = txport->nport; u8 csi_ctl = 0; @@ -1751,76 +2273,114 @@ static void ub960_init_tx_port(struct ub960_data *priv, if (!txport->non_continous_clk) csi_ctl |= UB960_TR_CSI_CTL_CSI_CONTS_CLOCK; - ub960_txport_write(priv, nport, UB960_TR_CSI_CTL, csi_ctl); + return ub960_txport_write(priv, nport, UB960_TR_CSI_CTL, csi_ctl, NULL); } -static int ub960_init_tx_ports(struct ub960_data *priv) +static int ub960_init_tx_ports_ub960(struct ub960_data *priv) { - unsigned int nport; u8 speed_select; - u8 pll_div; - - /* TX ports */ switch (priv->tx_data_rate) { + case MHZ(400): + speed_select = 3; + break; + case MHZ(800): + speed_select = 2; + break; + case MHZ(1200): + speed_select = 1; + break; case MHZ(1600): default: speed_select = 0; + break; + } + + return ub960_write(priv, UB960_SR_CSI_PLL_CTL, speed_select, NULL); +} + +static int ub960_init_tx_ports_ub9702(struct ub960_data *priv) +{ + u8 speed_select; + u8 ana_pll_div; + u8 pll_div; + int ret = 0; + + switch (priv->tx_data_rate) { + case MHZ(400): + speed_select = 3; pll_div = 0x10; + ana_pll_div = 0xa2; + break; + case MHZ(800): + speed_select = 2; + pll_div = 0x10; + ana_pll_div = 0x92; break; case MHZ(1200): speed_select = 1; pll_div = 0x18; + ana_pll_div = 0x90; break; - case MHZ(800): - speed_select = 2; - pll_div = 0x10; + case MHZ(1500): + speed_select = 0; + pll_div = 0x0f; + ana_pll_div = 0x82; break; - case MHZ(400): - speed_select = 3; + case MHZ(1600): + default: + speed_select = 0; pll_div = 0x10; + ana_pll_div = 0x82; + break; + case MHZ(2500): + speed_select = 0x10; + pll_div = 0x19; + ana_pll_div = 0x80; break; } - ub960_write(priv, UB960_SR_CSI_PLL_CTL, speed_select); + ub960_write(priv, UB960_SR_CSI_PLL_CTL, speed_select, &ret); + ub960_write(priv, UB9702_SR_CSI_PLL_DIV, pll_div, &ret); + ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, + UB9702_IR_CSI_ANA_CSIPLL_REG_1, ana_pll_div, &ret); - if (priv->hw_data->is_ub9702) { - ub960_write(priv, UB960_SR_CSI_PLL_DIV, pll_div); + return ret; +} - switch (priv->tx_data_rate) { - case MHZ(1600): - default: - ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x92, 0x80); - ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x4b, 0x2a); - break; - case MHZ(800): - ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x92, 0x90); - ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x4f, 0x2a); - ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x4b, 0x2a); - break; - case MHZ(400): - ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x92, 0xa0); - break; - } - } +static int ub960_init_tx_ports(struct ub960_data *priv) +{ + int ret; - for (nport = 0; nport < priv->hw_data->num_txports; nport++) { + if (priv->hw_data->is_ub9702) + ret = ub960_init_tx_ports_ub9702(priv); + else + ret = ub960_init_tx_ports_ub960(priv); + + if (ret) + return ret; + + for (unsigned int nport = 0; nport < priv->hw_data->num_txports; + nport++) { struct ub960_txport *txport = priv->txports[nport]; if (!txport) continue; - ub960_init_tx_port(priv, txport); + ret = ub960_init_tx_port(priv, txport); + if (ret) + return ret; } return 0; } -static void ub960_init_rx_port_ub960(struct ub960_data *priv, - struct ub960_rxport *rxport) +static int ub960_init_rx_port_ub960(struct ub960_data *priv, + struct ub960_rxport *rxport) { unsigned int nport = rxport->nport; u32 bc_freq_val; + int ret = 0; /* * Back channel frequency select. @@ -1849,306 +2409,870 @@ static void ub960_init_rx_port_ub960(struct ub960_data *priv, break; default: - return; + return -EINVAL; } ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, UB960_RR_BCC_CONFIG_BC_FREQ_SEL_MASK, - bc_freq_val); + bc_freq_val, &ret); switch (rxport->rx_mode) { case RXPORT_MODE_RAW10: /* FPD3_MODE = RAW10 Mode (DS90UB913A-Q1 / DS90UB933-Q1 compatible) */ ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG, UB960_RR_PORT_CONFIG_FPD3_MODE_MASK, - 0x3); + 0x3, &ret); /* * RAW10_8BIT_CTL = 0b10 : 8-bit processing using upper 8 bits */ ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG2, UB960_RR_PORT_CONFIG2_RAW10_8BIT_CTL_MASK, - 0x2 << UB960_RR_PORT_CONFIG2_RAW10_8BIT_CTL_SHIFT); + 0x2 << UB960_RR_PORT_CONFIG2_RAW10_8BIT_CTL_SHIFT, + &ret); break; case RXPORT_MODE_RAW12_HF: case RXPORT_MODE_RAW12_LF: /* Not implemented */ - return; + return -EINVAL; case RXPORT_MODE_CSI2_SYNC: case RXPORT_MODE_CSI2_NONSYNC: /* CSI-2 Mode (DS90UB953-Q1 compatible) */ ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG, 0x3, - 0x0); + 0x0, &ret); break; } /* LV_POLARITY & FV_POLARITY */ ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG2, 0x3, - rxport->lv_fv_pol); + rxport->lv_fv_pol, &ret); /* Enable all interrupt sources from this port */ - ub960_rxport_write(priv, nport, UB960_RR_PORT_ICR_HI, 0x07); - ub960_rxport_write(priv, nport, UB960_RR_PORT_ICR_LO, 0x7f); + ub960_rxport_write(priv, nport, UB960_RR_PORT_ICR_HI, 0x07, &ret); + ub960_rxport_write(priv, nport, UB960_RR_PORT_ICR_LO, 0x7f, &ret); /* Enable I2C_PASS_THROUGH */ ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH, - UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH); + UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH, &ret); /* Enable I2C communication to the serializer via the alias addr */ ub960_rxport_write(priv, nport, UB960_RR_SER_ALIAS_ID, - rxport->ser.alias << 1); + rxport->ser.alias << 1, &ret); /* Configure EQ related settings */ ub960_rxport_config_eq(priv, nport); /* Enable RX port */ - ub960_update_bits(priv, UB960_SR_RX_PORT_CTL, BIT(nport), BIT(nport)); + ub960_update_bits(priv, UB960_SR_RX_PORT_CTL, BIT(nport), BIT(nport), + &ret); + + return ret; } -static void ub960_init_rx_port_ub9702_fpd3(struct ub960_data *priv, - struct ub960_rxport *rxport) +static int ub960_init_rx_ports_ub960(struct ub960_data *priv) { - unsigned int nport = rxport->nport; - u8 bc_freq_val; - u8 fpd_func_mode; + struct device *dev = &priv->client->dev; + unsigned int port_lock_mask; + unsigned int port_mask; + int ret; - switch (rxport->rx_mode) { - case RXPORT_MODE_RAW10: - bc_freq_val = 0; - fpd_func_mode = 5; - break; + for_each_active_rxport(priv, it) { + ret = ub960_init_rx_port_ub960(priv, it.rxport); + if (ret) + return ret; + } - case RXPORT_MODE_RAW12_HF: - bc_freq_val = 0; - fpd_func_mode = 4; - break; + ret = ub960_reset(priv, false); + if (ret) + return ret; - case RXPORT_MODE_RAW12_LF: - bc_freq_val = 0; - fpd_func_mode = 6; - break; + port_mask = 0; - case RXPORT_MODE_CSI2_SYNC: - bc_freq_val = 6; - fpd_func_mode = 2; - break; + for_each_active_rxport(priv, it) + port_mask |= BIT(it.nport); - case RXPORT_MODE_CSI2_NONSYNC: - bc_freq_val = 2; - fpd_func_mode = 2; - break; + ret = ub960_rxport_wait_locks(priv, port_mask, &port_lock_mask); + if (ret) + return ret; - default: - return; + if (port_mask != port_lock_mask) { + ret = -EIO; + dev_err_probe(dev, ret, "Failed to lock all RX ports\n"); + return ret; + } + + /* Set temperature ramp on serializer */ + for_each_active_rxport(priv, it) { + ret = ub960_serializer_temp_ramp(it.rxport); + if (ret) + return ret; + + ub960_rxport_update_bits(priv, it.nport, UB960_RR_BCC_CONFIG, + UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH, + UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH, + &ret); + if (ret) + return ret; } - ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, 0x7, - bc_freq_val); - ub960_rxport_write(priv, nport, UB960_RR_CHANNEL_MODE, fpd_func_mode); + /* + * Clear any errors caused by switching the RX port settings while + * probing. + */ + ret = ub960_clear_rx_errors(priv); + if (ret) + return ret; - /* set serdes_eq_mode = 1 */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0xa8, 0x80); + return 0; +} - /* enable serdes driver */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x0d, 0x7f); +/* + * UB9702 specific initial RX port configuration + */ - /* set serdes_eq_offset=4 */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x2b, 0x04); +static int ub960_turn_off_rxport_ub9702(struct ub960_data *priv, + unsigned int nport) +{ + int ret = 0; - /* init default serdes_eq_max in 0xa9 */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0xa9, 0x23); + /* Disable RX port */ + ub960_update_bits(priv, UB960_SR_RX_PORT_CTL, BIT(nport), 0, &ret); - /* init serdes_eq_min in 0xaa */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0xaa, 0); + /* Disable FPD Rx and FPD BC CMR */ + ub960_rxport_write(priv, nport, UB9702_RR_RX_CTL_2, 0x1b, &ret); - /* serdes_driver_ctl2 control: DS90UB953-Q1/DS90UB933-Q1/DS90UB913A-Q1 */ - ub960_ind_update_bits(priv, UB960_IND_TARGET_RX_ANA(nport), 0x1b, - BIT(3), BIT(3)); + /* Disable FPD BC Tx */ + ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, BIT(4), 0, + &ret); - /* RX port to half-rate */ - ub960_update_bits(priv, UB960_SR_FPD_RATE_CFG, 0x3 << (nport * 2), - BIT(nport * 2)); + /* Disable internal RX blocks */ + ub960_rxport_write(priv, nport, UB9702_RR_RX_CTL_1, 0x15, &ret); + + /* Disable AEQ */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_AEQ_CFG_2, 0x03, &ret); + + /* PI disabled and oDAC disabled */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_AEQ_CFG_4, 0x09, &ret); + + /* AEQ configured for disabled link */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_AEQ_CFG_1, 0x20, &ret); + + /* disable AEQ clock and DFE */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_AEQ_CFG_3, 0x45, &ret); + + /* Powerdown FPD3 CDR */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_FPD3_CDR_CTRL_SEL_5, 0x82, &ret); + + return ret; } -static void ub960_init_rx_port_ub9702_fpd4_aeq(struct ub960_data *priv, - struct ub960_rxport *rxport) +static int ub960_set_bc_drv_config_ub9702(struct ub960_data *priv, + unsigned int nport) { - unsigned int nport = rxport->nport; - bool first_time_power_up = true; - - if (first_time_power_up) { - u8 v; + u8 fpd_bc_ctl0; + u8 fpd_bc_ctl1; + u8 fpd_bc_ctl2; + int ret = 0; - /* AEQ init */ - ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x2c, &v); + if (priv->rxports[nport]->cdr_mode == RXPORT_CDR_FPD4) { + /* Set FPD PBC drv into FPD IV mode */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x27, v); - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x28, v + 1); + fpd_bc_ctl0 = 0; + fpd_bc_ctl1 = 0; + fpd_bc_ctl2 = 0; + } else { + /* Set FPD PBC drv into FPD III mode */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x2b, 0x00); + fpd_bc_ctl0 = 2; + fpd_bc_ctl1 = 1; + fpd_bc_ctl2 = 5; } - /* enable serdes_eq_ctl2 */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x9e, 0x00); + ub960_ind_update_bits(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_FPD_BC_CTL0, GENMASK(7, 5), + fpd_bc_ctl0 << 5, &ret); + + ub960_ind_update_bits(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_FPD_BC_CTL1, BIT(6), + fpd_bc_ctl1 << 6, &ret); - /* enable serdes_eq_ctl1 */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x90, 0x40); + ub960_ind_update_bits(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_FPD_BC_CTL2, GENMASK(6, 3), + fpd_bc_ctl2 << 3, &ret); + + return ret; +} - /* enable serdes_eq_en */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x2e, 0x40); +static int ub960_set_fpd4_sync_mode_ub9702(struct ub960_data *priv, + unsigned int nport) +{ + int ret = 0; - /* disable serdes_eq_override */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0xf0, 0x00); + /* FPD4 Sync Mode */ + ub960_rxport_write(priv, nport, UB9702_RR_CHANNEL_MODE, 0x0, &ret); - /* disable serdes_gain_override */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x71, 0x00); + /* BC_FREQ_SELECT = (PLL_FREQ/3200) Mbps */ + ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, + UB960_RR_BCC_CONFIG_BC_FREQ_SEL_MASK, 6, &ret); + + if (ret) + return ret; + + ret = ub960_set_bc_drv_config_ub9702(priv, nport); + if (ret) + return ret; + + /* Set AEQ timer to 400us/step */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_SYSTEM_INIT_REG0, 0x2f, &ret); + + /* Disable FPD4 Auto Recovery */ + ub960_update_bits(priv, UB9702_SR_CSI_EXCLUSIVE_FWD2, GENMASK(5, 4), 0, + &ret); + + /* Enable RX port */ + ub960_update_bits(priv, UB960_SR_RX_PORT_CTL, BIT(nport), BIT(nport), + &ret); + + /* Enable FPD4 Auto Recovery */ + ub960_update_bits(priv, UB9702_SR_CSI_EXCLUSIVE_FWD2, GENMASK(5, 4), + BIT(4), &ret); + + return ret; } -static void ub960_init_rx_port_ub9702_fpd4(struct ub960_data *priv, - struct ub960_rxport *rxport) +static int ub960_set_fpd4_async_mode_ub9702(struct ub960_data *priv, + unsigned int nport) { - unsigned int nport = rxport->nport; - u8 bc_freq_val; + int ret = 0; - switch (rxport->rx_mode) { - case RXPORT_MODE_RAW10: - bc_freq_val = 0; - break; + /* FPD4 ASync Mode */ + ub960_rxport_write(priv, nport, UB9702_RR_CHANNEL_MODE, 0x1, &ret); - case RXPORT_MODE_RAW12_HF: - bc_freq_val = 0; - break; + /* 10Mbps w/ BC enabled */ + /* BC_FREQ_SELECT=(PLL_FREQ/3200) Mbps */ + ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, + UB960_RR_BCC_CONFIG_BC_FREQ_SEL_MASK, 2, &ret); - case RXPORT_MODE_RAW12_LF: - bc_freq_val = 0; - break; + if (ret) + return ret; - case RXPORT_MODE_CSI2_SYNC: - bc_freq_val = 6; - break; + ret = ub960_set_bc_drv_config_ub9702(priv, nport); + if (ret) + return ret; - case RXPORT_MODE_CSI2_NONSYNC: - bc_freq_val = 2; - break; + /* Set AEQ timer to 400us/step */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_SYSTEM_INIT_REG0, 0x2f, &ret); - default: - return; - } + /* Disable FPD4 Auto Recover */ + ub960_update_bits(priv, UB9702_SR_CSI_EXCLUSIVE_FWD2, GENMASK(5, 4), 0, + &ret); - ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, 0x7, - bc_freq_val); + /* Enable RX port */ + ub960_update_bits(priv, UB960_SR_RX_PORT_CTL, BIT(nport), BIT(nport), + &ret); - /* FPD4 Sync Mode */ - ub960_rxport_write(priv, nport, UB960_RR_CHANNEL_MODE, 0); + /* Enable FPD4 Auto Recovery */ + ub960_update_bits(priv, UB9702_SR_CSI_EXCLUSIVE_FWD2, GENMASK(5, 4), + BIT(4), &ret); + + return ret; +} - /* add serdes_eq_offset of 4 */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x2b, 0x04); +static int ub960_set_fpd3_sync_mode_ub9702(struct ub960_data *priv, + unsigned int nport) +{ + int ret = 0; - /* FPD4 serdes_start_eq in 0x27: assign default */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x27, 0x0); - /* FPD4 serdes_end_eq in 0x28: assign default */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x28, 0x23); + /* FPD3 Sync Mode */ + ub960_rxport_write(priv, nport, UB9702_RR_CHANNEL_MODE, 0x2, &ret); - /* set serdes_driver_mode into FPD IV mode */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x04, 0x00); - /* set FPD PBC drv into FPD IV mode */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x1b, 0x00); + /* BC_FREQ_SELECT=(PLL_FREQ/3200) Mbps */ + ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, + UB960_RR_BCC_CONFIG_BC_FREQ_SEL_MASK, 6, &ret); - /* set serdes_system_init to 0x2f */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x21, 0x2f); - /* set serdes_system_rst in reset mode */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x25, 0xc1); + /* Set AEQ_LOCK_MODE = 1 */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_FPD3_AEQ_CTRL_SEL_1, BIT(7), &ret); - /* RX port to 7.55G mode */ - ub960_update_bits(priv, UB960_SR_FPD_RATE_CFG, 0x3 << (nport * 2), - 0 << (nport * 2)); + if (ret) + return ret; - ub960_init_rx_port_ub9702_fpd4_aeq(priv, rxport); + ret = ub960_set_bc_drv_config_ub9702(priv, nport); + if (ret) + return ret; + + /* Enable RX port */ + ub960_update_bits(priv, UB960_SR_RX_PORT_CTL, BIT(nport), BIT(nport), + &ret); + + return ret; } -static void ub960_init_rx_port_ub9702(struct ub960_data *priv, - struct ub960_rxport *rxport) +static int ub960_set_raw10_dvp_mode_ub9702(struct ub960_data *priv, + unsigned int nport) { - unsigned int nport = rxport->nport; + int ret = 0; - if (rxport->cdr_mode == RXPORT_CDR_FPD3) - ub960_init_rx_port_ub9702_fpd3(priv, rxport); - else /* RXPORT_CDR_FPD4 */ - ub960_init_rx_port_ub9702_fpd4(priv, rxport); + /* FPD3 RAW10 Mode */ + ub960_rxport_write(priv, nport, UB9702_RR_CHANNEL_MODE, 0x5, &ret); - switch (rxport->rx_mode) { - case RXPORT_MODE_RAW10: - /* - * RAW10_8BIT_CTL = 0b11 : 8-bit processing using lower 8 bits - * 0b10 : 8-bit processing using upper 8 bits - */ - ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG2, - 0x3 << 6, 0x2 << 6); + ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, + UB960_RR_BCC_CONFIG_BC_FREQ_SEL_MASK, 0, &ret); + /* Set AEQ_LOCK_MODE = 1 */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_FPD3_AEQ_CTRL_SEL_1, BIT(7), &ret); + + /* + * RAW10_8BIT_CTL = 0b11 : 8-bit processing using lower 8 bits + * 0b10 : 8-bit processing using upper 8 bits + */ + ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG2, 0x3 << 6, + 0x2 << 6, &ret); + + /* LV_POLARITY & FV_POLARITY */ + ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG2, 0x3, + priv->rxports[nport]->lv_fv_pol, &ret); + + if (ret) + return ret; + + ret = ub960_set_bc_drv_config_ub9702(priv, nport); + if (ret) + return ret; + + /* Enable RX port */ + ub960_update_bits(priv, UB960_SR_RX_PORT_CTL, BIT(nport), BIT(nport), + &ret); + + return ret; +} + +static int ub960_configure_rx_port_ub9702(struct ub960_data *priv, + unsigned int nport) +{ + struct device *dev = &priv->client->dev; + struct ub960_rxport *rxport = priv->rxports[nport]; + int ret; + + if (!rxport) { + ret = ub960_turn_off_rxport_ub9702(priv, nport); + if (ret) + return ret; + + dev_dbg(dev, "rx%u: disabled\n", nport); + return 0; + } + + switch (rxport->cdr_mode) { + case RXPORT_CDR_FPD4: + switch (rxport->rx_mode) { + case RXPORT_MODE_CSI2_SYNC: + ret = ub960_set_fpd4_sync_mode_ub9702(priv, nport); + if (ret) + return ret; + + dev_dbg(dev, "rx%u: FPD-Link IV SYNC mode\n", nport); + break; + case RXPORT_MODE_CSI2_NONSYNC: + ret = ub960_set_fpd4_async_mode_ub9702(priv, nport); + if (ret) + return ret; + + dev_dbg(dev, "rx%u: FPD-Link IV ASYNC mode\n", nport); + break; + default: + dev_err(dev, "rx%u: unsupported FPD4 mode %u\n", nport, + rxport->rx_mode); + return -EINVAL; + } break; - case RXPORT_MODE_RAW12_HF: - case RXPORT_MODE_RAW12_LF: - /* Not implemented */ - return; + case RXPORT_CDR_FPD3: + switch (rxport->rx_mode) { + case RXPORT_MODE_CSI2_SYNC: + ret = ub960_set_fpd3_sync_mode_ub9702(priv, nport); + if (ret) + return ret; - case RXPORT_MODE_CSI2_SYNC: - case RXPORT_MODE_CSI2_NONSYNC: + dev_dbg(dev, "rx%u: FPD-Link III SYNC mode\n", nport); + break; + case RXPORT_MODE_RAW10: + ret = ub960_set_raw10_dvp_mode_ub9702(priv, nport); + if (ret) + return ret; + dev_dbg(dev, "rx%u: FPD-Link III RAW10 DVP mode\n", + nport); + break; + default: + dev_err(&priv->client->dev, + "rx%u: unsupported FPD3 mode %u\n", nport, + rxport->rx_mode); + return -EINVAL; + } break; + + default: + dev_err(&priv->client->dev, "rx%u: unsupported CDR mode %u\n", + nport, rxport->cdr_mode); + return -EINVAL; } - /* LV_POLARITY & FV_POLARITY */ - ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG2, 0x3, - rxport->lv_fv_pol); + return 0; +} - /* Enable all interrupt sources from this port */ - ub960_rxport_write(priv, nport, UB960_RR_PORT_ICR_HI, 0x07); - ub960_rxport_write(priv, nport, UB960_RR_PORT_ICR_LO, 0x7f); +static int ub960_lock_recovery_ub9702(struct ub960_data *priv, + unsigned int nport) +{ + struct device *dev = &priv->client->dev; + /* Assumption that max AEQ should be under 16 */ + const u8 rx_aeq_limit = 16; + u8 prev_aeq = 0xff; + bool rx_lock; + + for (unsigned int retry = 0; retry < 3; ++retry) { + u8 port_sts1; + u8 rx_aeq; + int ret; - /* Enable I2C_PASS_THROUGH */ - ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, - UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH, - UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH); + ret = ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS1, + &port_sts1, NULL); + if (ret) + return ret; - /* Enable I2C communication to the serializer via the alias addr */ - ub960_rxport_write(priv, nport, UB960_RR_SER_ALIAS_ID, - rxport->ser.alias << 1); + rx_lock = port_sts1 & UB960_RR_RX_PORT_STS1_PORT_PASS; - /* Enable RX port */ - ub960_update_bits(priv, UB960_SR_RX_PORT_CTL, BIT(nport), BIT(nport)); + if (!rx_lock) { + ret = ub960_rxport_lockup_wa_ub9702(priv); + if (ret) + return ret; + + /* Restart AEQ by changing max to 0 --> 0x23 */ + ret = ub960_write_ind(priv, + UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_AEQ_ALP_SEL7, 0, + NULL); + if (ret) + return ret; + + msleep(20); + + /* AEQ Restart */ + ret = ub960_write_ind(priv, + UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_AEQ_ALP_SEL7, + 0x23, NULL); + + if (ret) + return ret; - if (rxport->cdr_mode == RXPORT_CDR_FPD4) { - /* unreset 960 AEQ */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x25, 0x41); + msleep(20); + dev_dbg(dev, "rx%u: no lock, retry = %u\n", nport, + retry); + + continue; + } + + ret = ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_AEQ_ALP_SEL11, &rx_aeq, + NULL); + if (ret) + return ret; + + if (rx_aeq < rx_aeq_limit) { + dev_dbg(dev, + "rx%u: locked and AEQ normal before setting AEQ window\n", + nport); + return 0; + } + + if (rx_aeq != prev_aeq) { + ret = ub960_rxport_lockup_wa_ub9702(priv); + if (ret) + return ret; + + /* Restart AEQ by changing max to 0 --> 0x23 */ + ret = ub960_write_ind(priv, + UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_AEQ_ALP_SEL7, + 0, NULL); + if (ret) + return ret; + + msleep(20); + + /* AEQ Restart */ + ret = ub960_write_ind(priv, + UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_AEQ_ALP_SEL7, + 0x23, NULL); + if (ret) + return ret; + + msleep(20); + + dev_dbg(dev, + "rx%u: high AEQ at initial check recovery loop, retry=%u\n", + nport, retry); + + prev_aeq = rx_aeq; + } else { + dev_dbg(dev, + "rx%u: lossy cable detected, RX_AEQ %#x, RX_AEQ_LIMIT %#x, retry %u\n", + nport, rx_aeq, rx_aeq_limit, retry); + dev_dbg(dev, + "rx%u: will continue with initiation sequence but high AEQ\n", + nport); + return 0; + } } + + dev_err(dev, "rx%u: max number of retries: %s\n", nport, + rx_lock ? "unstable AEQ" : "no lock"); + + return -EIO; } -static int ub960_init_rx_ports(struct ub960_data *priv) +static int ub960_enable_aeq_lms_ub9702(struct ub960_data *priv, + unsigned int nport) { - unsigned int nport; + struct device *dev = &priv->client->dev; + u8 read_aeq_init; + int ret; - for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { - struct ub960_rxport *rxport = priv->rxports[nport]; + ret = ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_AEQ_ALP_SEL11, &read_aeq_init, + NULL); + if (ret) + return ret; - if (!rxport) - continue; + dev_dbg(dev, "rx%u: initial AEQ = %#x\n", nport, read_aeq_init); - if (priv->hw_data->is_ub9702) - ub960_init_rx_port_ub9702(priv, rxport); - else - ub960_init_rx_port_ub960(priv, rxport); + /* Set AEQ Min */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_AEQ_ALP_SEL6, read_aeq_init, &ret); + /* Set AEQ Max */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_AEQ_ALP_SEL7, read_aeq_init + 1, &ret); + /* Set AEQ offset to 0 */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_AEQ_ALP_SEL10, 0x0, &ret); + + /* Enable AEQ tap2 */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_EQ_CTRL_SEL_38, 0x00, &ret); + /* Set VGA Gain 1 Gain 2 override to 0 */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_VGA_CTRL_SEL_8, 0x00, &ret); + /* Set VGA Initial Sweep Gain to 0 */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_VGA_CTRL_SEL_6, 0x80, &ret); + /* Set VGA_Adapt (VGA Gain) override to 0 (thermometer encoded) */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_VGA_CTRL_SEL_3, 0x00, &ret); + /* Enable VGA_SWEEP */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_EQ_ADAPT_CTRL, 0x40, &ret); + /* Disable VGA_SWEEP_GAIN_OV, disable VGA_TUNE_OV */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_EQ_OVERRIDE_CTRL, 0x00, &ret); + + /* Set VGA HIGH Threshold to 43 */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_VGA_CTRL_SEL_1, 0x2b, &ret); + /* Set VGA LOW Threshold to 18 */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_VGA_CTRL_SEL_2, 0x12, &ret); + /* Set vga_sweep_th to 32 */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_EQ_CTRL_SEL_15, 0x20, &ret); + /* Set AEQ timer to 400us/step and parity threshold to 7 */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_SYSTEM_INIT_REG0, 0xef, &ret); + + if (ret) + return ret; + + dev_dbg(dev, "rx%u: enable FPD-Link IV AEQ LMS\n", nport); + + return 0; +} + +static int ub960_enable_dfe_lms_ub9702(struct ub960_data *priv, + unsigned int nport) +{ + struct device *dev = &priv->client->dev; + int ret = 0; + + /* Enable DFE LMS */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_EQ_CTRL_SEL_24, 0x40, &ret); + /* Disable VGA Gain1 override */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_GAIN_CTRL_0, 0x20, &ret); + + if (ret) + return ret; + + usleep_range(1000, 5000); + + /* Disable VGA Gain2 override */ + ret = ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_GAIN_CTRL_0, 0x00, NULL); + if (ret) + return ret; + + dev_dbg(dev, "rx%u: enabled FPD-Link IV DFE LMS", nport); + + return 0; +} + +static int ub960_init_rx_ports_ub9702(struct ub960_data *priv) +{ + struct device *dev = &priv->client->dev; + unsigned int port_lock_mask; + unsigned int port_mask = 0; + bool have_fpd4 = false; + int ret; + + for_each_active_rxport(priv, it) { + ret = ub960_rxport_update_bits(priv, it.nport, + UB960_RR_BCC_CONFIG, + UB960_RR_BCC_CONFIG_BC_ALWAYS_ON, + UB960_RR_BCC_CONFIG_BC_ALWAYS_ON, + NULL); + if (ret) + return ret; + } + + /* Disable FPD4 Auto Recovery */ + ret = ub960_write(priv, UB9702_SR_CSI_EXCLUSIVE_FWD2, 0x0f, NULL); + if (ret) + return ret; + + for_each_active_rxport(priv, it) { + if (it.rxport->ser.addr >= 0) { + /* + * Set serializer's I2C address if set in the dts file, + * and freeze it to prevent updates from the FC. + */ + ub960_rxport_write(priv, it.nport, UB960_RR_SER_ID, + it.rxport->ser.addr << 1 | + UB960_RR_SER_ID_FREEZE_DEVICE_ID, + &ret); + } + + /* Set serializer I2C alias with auto-ack */ + ub960_rxport_write(priv, it.nport, UB960_RR_SER_ALIAS_ID, + it.rxport->ser.alias << 1 | + UB960_RR_SER_ALIAS_ID_AUTO_ACK, &ret); + + if (ret) + return ret; + } + + for_each_active_rxport(priv, it) { + if (fwnode_device_is_compatible(it.rxport->ser.fwnode, + "ti,ds90ub971-q1")) { + ret = ub960_rxport_bc_ser_config(it.rxport); + if (ret) + return ret; + } + } + + for_each_active_rxport_fpd4(priv, it) { + /* Hold state machine in reset */ + ub960_rxport_write(priv, it.nport, UB9702_RR_RX_SM_SEL_2, 0x10, + &ret); + + /* Set AEQ max to 0 */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(it.nport), + UB9702_IR_RX_ANA_AEQ_ALP_SEL7, 0, &ret); + + if (ret) + return ret; + + dev_dbg(dev, + "rx%u: holding state machine and adjusting AEQ max to 0", + it.nport); + } + + for_each_active_rxport(priv, it) { + port_mask |= BIT(it.nport); + + if (it.rxport->cdr_mode == RXPORT_CDR_FPD4) + have_fpd4 = true; + } + + for_each_rxport(priv, it) { + ret = ub960_configure_rx_port_ub9702(priv, it.nport); + if (ret) + return ret; + } + + ret = ub960_reset(priv, false); + if (ret) + return ret; + + if (have_fpd4) { + for_each_active_rxport_fpd4(priv, it) { + /* Release state machine */ + ret = ub960_rxport_write(priv, it.nport, + UB9702_RR_RX_SM_SEL_2, 0x0, + NULL); + if (ret) + return ret; + + dev_dbg(dev, "rx%u: state machine released\n", + it.nport); + } + + /* Wait for SM to resume */ + fsleep(5000); + + for_each_active_rxport_fpd4(priv, it) { + ret = ub960_write_ind(priv, + UB960_IND_TARGET_RX_ANA(it.nport), + UB9702_IR_RX_ANA_AEQ_ALP_SEL7, + 0x23, NULL); + if (ret) + return ret; + + dev_dbg(dev, "rx%u: AEQ restart\n", it.nport); + } + + /* Wait for lock */ + fsleep(20000); + + for_each_active_rxport_fpd4(priv, it) { + ret = ub960_lock_recovery_ub9702(priv, it.nport); + if (ret) + return ret; + } + + for_each_active_rxport_fpd4(priv, it) { + ret = ub960_enable_aeq_lms_ub9702(priv, it.nport); + if (ret) + return ret; + } + + for_each_active_rxport_fpd4(priv, it) { + /* Hold state machine in reset */ + ret = ub960_rxport_write(priv, it.nport, + UB9702_RR_RX_SM_SEL_2, 0x10, + NULL); + if (ret) + return ret; + } + + ret = ub960_reset(priv, false); + if (ret) + return ret; + + for_each_active_rxport_fpd4(priv, it) { + /* Release state machine */ + ret = ub960_rxport_write(priv, it.nport, + UB9702_RR_RX_SM_SEL_2, 0, + NULL); + if (ret) + return ret; + } + } + + /* Wait time for stable lock */ + fsleep(15000); + + /* Set temperature ramp on serializer */ + for_each_active_rxport(priv, it) { + ret = ub960_serializer_temp_ramp(it.rxport); + if (ret) + return ret; + } + + for_each_active_rxport_fpd4(priv, it) { + ret = ub960_enable_dfe_lms_ub9702(priv, it.nport); + if (ret) + return ret; + } + + /* Wait for DFE and LMS to adapt */ + fsleep(5000); + + ret = ub960_rxport_wait_locks(priv, port_mask, &port_lock_mask); + if (ret) + return ret; + + if (port_mask != port_lock_mask) { + ret = -EIO; + dev_err_probe(dev, ret, "Failed to lock all RX ports\n"); + return ret; + } + + for_each_active_rxport(priv, it) { + /* Enable all interrupt sources from this port */ + ub960_rxport_write(priv, it.nport, UB960_RR_PORT_ICR_HI, 0x07, + &ret); + ub960_rxport_write(priv, it.nport, UB960_RR_PORT_ICR_LO, 0x7f, + &ret); + + /* Clear serializer I2C alias auto-ack */ + ub960_rxport_update_bits(priv, it.nport, UB960_RR_SER_ALIAS_ID, + UB960_RR_SER_ALIAS_ID_AUTO_ACK, 0, + &ret); + + /* Enable I2C_PASS_THROUGH */ + ub960_rxport_update_bits(priv, it.nport, UB960_RR_BCC_CONFIG, + UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH, + UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH, + &ret); + + if (ret) + return ret; + } + + /* Enable FPD4 Auto Recovery, Recovery loop active */ + ret = ub960_write(priv, UB9702_SR_CSI_EXCLUSIVE_FWD2, 0x18, NULL); + if (ret) + return ret; + + for_each_active_rxport_fpd4(priv, it) { + u8 final_aeq; + + ret = ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(it.nport), + UB9702_IR_RX_ANA_AEQ_ALP_SEL11, &final_aeq, + NULL); + if (ret) + return ret; + + dev_dbg(dev, "rx%u: final AEQ = %#x\n", it.nport, final_aeq); } + /* + * Clear any errors caused by switching the RX port settings while + * probing. + */ + + ret = ub960_clear_rx_errors(priv); + if (ret) + return ret; + return 0; } -static void ub960_rxport_handle_events(struct ub960_data *priv, u8 nport) +static int ub960_rxport_handle_events(struct ub960_data *priv, u8 nport) { struct device *dev = &priv->client->dev; u8 rx_port_sts1; @@ -2158,27 +3282,21 @@ static void ub960_rxport_handle_events(struct ub960_data *priv, u8 nport) int ret = 0; /* Read interrupts (also clears most of them) */ - if (!ret) - ret = ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS1, - &rx_port_sts1); - if (!ret) - ret = ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS2, - &rx_port_sts2); - if (!ret) - ret = ub960_rxport_read(priv, nport, UB960_RR_CSI_RX_STS, - &csi_rx_sts); - if (!ret) - ret = ub960_rxport_read(priv, nport, UB960_RR_BCC_STATUS, - &bcc_sts); + ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS1, &rx_port_sts1, + &ret); + ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS2, &rx_port_sts2, + &ret); + ub960_rxport_read(priv, nport, UB960_RR_CSI_RX_STS, &csi_rx_sts, &ret); + ub960_rxport_read(priv, nport, UB960_RR_BCC_STATUS, &bcc_sts, &ret); if (ret) - return; + return ret; if (rx_port_sts1 & UB960_RR_RX_PORT_STS1_PARITY_ERROR) { u16 v; ret = ub960_rxport_read16(priv, nport, UB960_RR_RX_PAR_ERR_HI, - &v); + &v, NULL); if (!ret) dev_err(dev, "rx%u parity errors: %u\n", nport, v); } @@ -2237,7 +3355,8 @@ static void ub960_rxport_handle_events(struct ub960_data *priv, u8 nport) if (rx_port_sts2 & UB960_RR_RX_PORT_STS2_LINE_LEN_CHG) { u16 v; - ret = ub960_rxport_read16(priv, nport, UB960_RR_LINE_LEN_1, &v); + ret = ub960_rxport_read16(priv, nport, UB960_RR_LINE_LEN_1, + &v, NULL); if (!ret) dev_dbg(dev, "rx%u line len changed: %u\n", nport, v); } @@ -2246,7 +3365,7 @@ static void ub960_rxport_handle_events(struct ub960_data *priv, u8 nport) u16 v; ret = ub960_rxport_read16(priv, nport, UB960_RR_LINE_COUNT_HI, - &v); + &v, NULL); if (!ret) dev_dbg(dev, "rx%u line count changed: %u\n", nport, v); } @@ -2266,6 +3385,8 @@ static void ub960_rxport_handle_events(struct ub960_data *priv, u8 nport) "stable freq" : "unstable freq"); } + + return 0; } /* ----------------------------------------------------------------------------- @@ -2318,17 +3439,17 @@ static int ub960_enable_tx_port(struct ub960_data *priv, unsigned int nport) return ub960_txport_update_bits(priv, nport, UB960_TR_CSI_CTL, UB960_TR_CSI_CTL_CSI_ENABLE, - UB960_TR_CSI_CTL_CSI_ENABLE); + UB960_TR_CSI_CTL_CSI_ENABLE, NULL); } -static void ub960_disable_tx_port(struct ub960_data *priv, unsigned int nport) +static int ub960_disable_tx_port(struct ub960_data *priv, unsigned int nport) { struct device *dev = &priv->client->dev; dev_dbg(dev, "disable TX port %u\n", nport); - ub960_txport_update_bits(priv, nport, UB960_TR_CSI_CTL, - UB960_TR_CSI_CTL_CSI_ENABLE, 0); + return ub960_txport_update_bits(priv, nport, UB960_TR_CSI_CTL, + UB960_TR_CSI_CTL_CSI_ENABLE, 0, NULL); } static int ub960_enable_rx_port(struct ub960_data *priv, unsigned int nport) @@ -2339,19 +3460,19 @@ static int ub960_enable_rx_port(struct ub960_data *priv, unsigned int nport) /* Enable forwarding */ return ub960_update_bits(priv, UB960_SR_FWD_CTL1, - UB960_SR_FWD_CTL1_PORT_DIS(nport), 0); + UB960_SR_FWD_CTL1_PORT_DIS(nport), 0, NULL); } -static void ub960_disable_rx_port(struct ub960_data *priv, unsigned int nport) +static int ub960_disable_rx_port(struct ub960_data *priv, unsigned int nport) { struct device *dev = &priv->client->dev; dev_dbg(dev, "disable RX port %u\n", nport); /* Disable forwarding */ - ub960_update_bits(priv, UB960_SR_FWD_CTL1, - UB960_SR_FWD_CTL1_PORT_DIS(nport), - UB960_SR_FWD_CTL1_PORT_DIS(nport)); + return ub960_update_bits(priv, UB960_SR_FWD_CTL1, + UB960_SR_FWD_CTL1_PORT_DIS(nport), + UB960_SR_FWD_CTL1_PORT_DIS(nport), NULL); } /* @@ -2360,20 +3481,14 @@ static void ub960_disable_rx_port(struct ub960_data *priv, unsigned int nport) */ static int ub960_validate_stream_vcs(struct ub960_data *priv) { - unsigned int nport; - unsigned int i; - - for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { - struct ub960_rxport *rxport = priv->rxports[nport]; + for_each_active_rxport(priv, it) { struct v4l2_mbus_frame_desc desc; int ret; u8 vc; - if (!rxport) - continue; - - ret = v4l2_subdev_call(rxport->source.sd, pad, get_frame_desc, - rxport->source.pad, &desc); + ret = v4l2_subdev_call(it.rxport->source.sd, pad, + get_frame_desc, it.rxport->source.pad, + &desc); if (ret) return ret; @@ -2385,13 +3500,13 @@ static int ub960_validate_stream_vcs(struct ub960_data *priv) vc = desc.entry[0].bus.csi2.vc; - for (i = 1; i < desc.num_entries; i++) { + for (unsigned int i = 1; i < desc.num_entries; i++) { if (vc == desc.entry[i].bus.csi2.vc) continue; dev_err(&priv->client->dev, "rx%u: source with multiple virtual-channels is not supported\n", - nport); + it.nport); return -ENODEV; } } @@ -2412,7 +3527,6 @@ static int ub960_configure_ports_for_streaming(struct ub960_data *priv, } rx_data[UB960_MAX_RX_NPORTS] = {}; u8 vc_map[UB960_MAX_RX_NPORTS] = {}; struct v4l2_subdev_route *route; - unsigned int nport; int ret; ret = ub960_validate_stream_vcs(priv); @@ -2482,22 +3596,24 @@ static int ub960_configure_ports_for_streaming(struct ub960_data *priv, */ fwd_ctl = GENMASK(7, 4); - for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { - struct ub960_rxport *rxport = priv->rxports[nport]; + for_each_active_rxport(priv, it) { + unsigned long nport = it.nport; + u8 vc = vc_map[nport]; if (rx_data[nport].num_streams == 0) continue; - switch (rxport->rx_mode) { + switch (it.rxport->rx_mode) { case RXPORT_MODE_RAW10: ub960_rxport_write(priv, nport, UB960_RR_RAW10_ID, - rx_data[nport].pixel_dt | (vc << UB960_RR_RAW10_ID_VC_SHIFT)); + rx_data[nport].pixel_dt | (vc << UB960_RR_RAW10_ID_VC_SHIFT), + &ret); - ub960_rxport_write(priv, rxport->nport, + ub960_rxport_write(priv, nport, UB960_RR_RAW_EMBED_DTYPE, (rx_data[nport].meta_lines << UB960_RR_RAW_EMBED_DTYPE_LINES_SHIFT) | - rx_data[nport].meta_dt); + rx_data[nport].meta_dt, &ret); break; @@ -2514,15 +3630,17 @@ static int ub960_configure_ports_for_streaming(struct ub960_data *priv, (vc << UB960_RR_CSI_VC_MAP_SHIFT(3)) | (vc << UB960_RR_CSI_VC_MAP_SHIFT(2)) | (vc << UB960_RR_CSI_VC_MAP_SHIFT(1)) | - (vc << UB960_RR_CSI_VC_MAP_SHIFT(0))); + (vc << UB960_RR_CSI_VC_MAP_SHIFT(0)), + &ret); } else { unsigned int i; /* Map all VCs from this port to VC(nport) */ for (i = 0; i < 8; i++) ub960_rxport_write(priv, nport, - UB960_RR_VC_ID_MAP(i), - nport); + UB9702_RR_VC_ID_MAP(i), + (nport << 4) | nport, + &ret); } break; @@ -2534,9 +3652,9 @@ static int ub960_configure_ports_for_streaming(struct ub960_data *priv, fwd_ctl &= ~BIT(nport); /* forward to TX0 */ } - ub960_write(priv, UB960_SR_FWD_CTL1, fwd_ctl); + ub960_write(priv, UB960_SR_FWD_CTL1, fwd_ctl, &ret); - return 0; + return ret; } static void ub960_update_streaming_status(struct ub960_data *priv) @@ -2560,7 +3678,6 @@ static int ub960_enable_streams(struct v4l2_subdev *sd, u64 sink_streams[UB960_MAX_RX_NPORTS] = {}; struct v4l2_subdev_route *route; unsigned int failed_port; - unsigned int nport; int ret; if (!priv->streaming) { @@ -2582,6 +3699,8 @@ static int ub960_enable_streams(struct v4l2_subdev *sd, /* Collect sink streams per pad which we need to enable */ for_each_active_route(&state->routing, route) { + unsigned int nport; + if (route->source_pad != source_pad) continue; @@ -2593,7 +3712,9 @@ static int ub960_enable_streams(struct v4l2_subdev *sd, sink_streams[nport] |= BIT_ULL(route->sink_stream); } - for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { + for_each_rxport(priv, it) { + unsigned int nport = it.nport; + if (!sink_streams[nport]) continue; @@ -2631,7 +3752,7 @@ static int ub960_enable_streams(struct v4l2_subdev *sd, return 0; err: - for (nport = 0; nport < failed_port; nport++) { + for (unsigned int nport = 0; nport < failed_port; nport++) { if (!sink_streams[nport]) continue; @@ -2671,11 +3792,12 @@ static int ub960_disable_streams(struct v4l2_subdev *sd, struct device *dev = &priv->client->dev; u64 sink_streams[UB960_MAX_RX_NPORTS] = {}; struct v4l2_subdev_route *route; - unsigned int nport; int ret; /* Collect sink streams per pad which we need to disable */ for_each_active_route(&state->routing, route) { + unsigned int nport; + if (route->source_pad != source_pad) continue; @@ -2687,7 +3809,9 @@ static int ub960_disable_streams(struct v4l2_subdev *sd, sink_streams[nport] |= BIT_ULL(route->sink_stream); } - for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { + for_each_rxport(priv, it) { + unsigned int nport = it.nport; + if (!sink_streams[nport]) continue; @@ -2737,14 +3861,6 @@ static int _ub960_set_routing(struct v4l2_subdev *sd, }; int ret; - /* - * Note: we can only support up to V4L2_FRAME_DESC_ENTRY_MAX, until - * frame desc is made dynamically allocated. - */ - - if (routing->num_routes > V4L2_FRAME_DESC_ENTRY_MAX) - return -E2BIG; - ret = v4l2_subdev_routing_validate(sd, routing, V4L2_SUBDEV_ROUTING_ONLY_1_TO_1 | V4L2_SUBDEV_ROUTING_NO_SINK_STREAM_MIX); @@ -2939,25 +4055,93 @@ static const struct v4l2_subdev_pad_ops ub960_pad_ops = { .set_fmt = ub960_set_fmt, }; +static int ub960_log_status_ub960_sp_eq(struct ub960_data *priv, + unsigned int nport) +{ + struct device *dev = &priv->client->dev; + u8 eq_level; + s8 strobe_pos; + int ret; + u8 v; + + /* Strobe */ + + ret = ub960_read(priv, UB960_XR_AEQ_CTL1, &v, NULL); + if (ret) + return ret; + + dev_info(dev, "\t%s strobe\n", + (v & UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN) ? "Adaptive" : + "Manual"); + + if (v & UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN) { + ret = ub960_read(priv, UB960_XR_SFILTER_CFG, &v, NULL); + if (ret) + return ret; + + dev_info(dev, "\tStrobe range [%d, %d]\n", + ((v >> UB960_XR_SFILTER_CFG_SFILTER_MIN_SHIFT) & 0xf) - 7, + ((v >> UB960_XR_SFILTER_CFG_SFILTER_MAX_SHIFT) & 0xf) - 7); + } + + ret = ub960_rxport_get_strobe_pos(priv, nport, &strobe_pos); + if (ret) + return ret; + + dev_info(dev, "\tStrobe pos %d\n", strobe_pos); + + /* EQ */ + + ret = ub960_rxport_read(priv, nport, UB960_RR_AEQ_BYPASS, &v, NULL); + if (ret) + return ret; + + dev_info(dev, "\t%s EQ\n", + (v & UB960_RR_AEQ_BYPASS_ENABLE) ? "Manual" : + "Adaptive"); + + if (!(v & UB960_RR_AEQ_BYPASS_ENABLE)) { + ret = ub960_rxport_read(priv, nport, UB960_RR_AEQ_MIN_MAX, &v, + NULL); + if (ret) + return ret; + + dev_info(dev, "\tEQ range [%u, %u]\n", + (v >> UB960_RR_AEQ_MIN_MAX_AEQ_FLOOR_SHIFT) & 0xf, + (v >> UB960_RR_AEQ_MIN_MAX_AEQ_MAX_SHIFT) & 0xf); + } + + ret = ub960_rxport_get_eq_level(priv, nport, &eq_level); + if (ret) + return ret; + + dev_info(dev, "\tEQ level %u\n", eq_level); + + return 0; +} + static int ub960_log_status(struct v4l2_subdev *sd) { struct ub960_data *priv = sd_to_ub960(sd); struct device *dev = &priv->client->dev; struct v4l2_subdev_state *state; - unsigned int nport; - unsigned int i; u16 v16 = 0; u8 v = 0; u8 id[UB960_SR_FPD3_RX_ID_LEN]; + int ret = 0; state = v4l2_subdev_lock_and_get_active_state(sd); - for (i = 0; i < sizeof(id); i++) - ub960_read(priv, UB960_SR_FPD3_RX_ID(i), &id[i]); + for (unsigned int i = 0; i < sizeof(id); i++) { + ret = ub960_read(priv, UB960_SR_FPD3_RX_ID(i), &id[i], NULL); + if (ret) + return ret; + } dev_info(dev, "ID '%.*s'\n", (int)sizeof(id), id); - for (nport = 0; nport < priv->hw_data->num_txports; nport++) { + for (unsigned int nport = 0; nport < priv->hw_data->num_txports; + nport++) { struct ub960_txport *txport = priv->txports[nport]; dev_info(dev, "TX %u\n", nport); @@ -2967,37 +4151,56 @@ static int ub960_log_status(struct v4l2_subdev *sd) continue; } - ub960_txport_read(priv, nport, UB960_TR_CSI_STS, &v); + ret = ub960_txport_read(priv, nport, UB960_TR_CSI_STS, &v, NULL); + if (ret) + return ret; + dev_info(dev, "\tsync %u, pass %u\n", v & (u8)BIT(1), v & (u8)BIT(0)); - ub960_read16(priv, UB960_SR_CSI_FRAME_COUNT_HI(nport), &v16); + ret = ub960_read16(priv, UB960_SR_CSI_FRAME_COUNT_HI(nport), + &v16, NULL); + if (ret) + return ret; + dev_info(dev, "\tframe counter %u\n", v16); - ub960_read16(priv, UB960_SR_CSI_FRAME_ERR_COUNT_HI(nport), &v16); + ret = ub960_read16(priv, UB960_SR_CSI_FRAME_ERR_COUNT_HI(nport), + &v16, NULL); + if (ret) + return ret; + dev_info(dev, "\tframe error counter %u\n", v16); - ub960_read16(priv, UB960_SR_CSI_LINE_COUNT_HI(nport), &v16); + ret = ub960_read16(priv, UB960_SR_CSI_LINE_COUNT_HI(nport), + &v16, NULL); + if (ret) + return ret; + dev_info(dev, "\tline counter %u\n", v16); - ub960_read16(priv, UB960_SR_CSI_LINE_ERR_COUNT_HI(nport), &v16); + ret = ub960_read16(priv, UB960_SR_CSI_LINE_ERR_COUNT_HI(nport), + &v16, NULL); + if (ret) + return ret; + dev_info(dev, "\tline error counter %u\n", v16); } - for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { - struct ub960_rxport *rxport = priv->rxports[nport]; - u8 eq_level; - s8 strobe_pos; - unsigned int i; + for_each_rxport(priv, it) { + unsigned int nport = it.nport; dev_info(dev, "RX %u\n", nport); - if (!rxport) { + if (!it.rxport) { dev_info(dev, "\tNot initialized\n"); continue; } - ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS1, &v); + ret = ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS1, &v, + NULL); + if (ret) + return ret; if (v & UB960_RR_RX_PORT_STS1_LOCK_STS) dev_info(dev, "\tLocked\n"); @@ -3005,72 +4208,65 @@ static int ub960_log_status(struct v4l2_subdev *sd) dev_info(dev, "\tNot locked\n"); dev_info(dev, "\trx_port_sts1 %#02x\n", v); - ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS2, &v); - dev_info(dev, "\trx_port_sts2 %#02x\n", v); - - ub960_rxport_read16(priv, nport, UB960_RR_RX_FREQ_HIGH, &v16); - dev_info(dev, "\tlink freq %llu Hz\n", (v16 * 1000000ULL) >> 8); - - ub960_rxport_read16(priv, nport, UB960_RR_RX_PAR_ERR_HI, &v16); - dev_info(dev, "\tparity errors %u\n", v16); - - ub960_rxport_read16(priv, nport, UB960_RR_LINE_COUNT_HI, &v16); - dev_info(dev, "\tlines per frame %u\n", v16); - - ub960_rxport_read16(priv, nport, UB960_RR_LINE_LEN_1, &v16); - dev_info(dev, "\tbytes per line %u\n", v16); - - ub960_rxport_read(priv, nport, UB960_RR_CSI_ERR_COUNTER, &v); - dev_info(dev, "\tcsi_err_counter %u\n", v); + ret = ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS2, &v, + NULL); + if (ret) + return ret; - /* Strobe */ + dev_info(dev, "\trx_port_sts2 %#02x\n", v); - ub960_read(priv, UB960_XR_AEQ_CTL1, &v); + ret = ub960_rxport_read16(priv, nport, UB960_RR_RX_FREQ_HIGH, + &v16, NULL); + if (ret) + return ret; - dev_info(dev, "\t%s strobe\n", - (v & UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN) ? "Adaptive" : - "Manual"); + dev_info(dev, "\tlink freq %llu Hz\n", ((u64)v16 * HZ_PER_MHZ) >> 8); - if (v & UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN) { - ub960_read(priv, UB960_XR_SFILTER_CFG, &v); + ret = ub960_rxport_read16(priv, nport, UB960_RR_RX_PAR_ERR_HI, + &v16, NULL); + if (ret) + return ret; - dev_info(dev, "\tStrobe range [%d, %d]\n", - ((v >> UB960_XR_SFILTER_CFG_SFILTER_MIN_SHIFT) & 0xf) - 7, - ((v >> UB960_XR_SFILTER_CFG_SFILTER_MAX_SHIFT) & 0xf) - 7); - } + dev_info(dev, "\tparity errors %u\n", v16); - ub960_rxport_get_strobe_pos(priv, nport, &strobe_pos); + ret = ub960_rxport_read16(priv, nport, UB960_RR_LINE_COUNT_HI, + &v16, NULL); + if (ret) + return ret; - dev_info(dev, "\tStrobe pos %d\n", strobe_pos); + dev_info(dev, "\tlines per frame %u\n", v16); - /* EQ */ + ret = ub960_rxport_read16(priv, nport, UB960_RR_LINE_LEN_1, + &v16, NULL); + if (ret) + return ret; - ub960_rxport_read(priv, nport, UB960_RR_AEQ_BYPASS, &v); + dev_info(dev, "\tbytes per line %u\n", v16); - dev_info(dev, "\t%s EQ\n", - (v & UB960_RR_AEQ_BYPASS_ENABLE) ? "Manual" : - "Adaptive"); + ret = ub960_rxport_read(priv, nport, UB960_RR_CSI_ERR_COUNTER, + &v, NULL); + if (ret) + return ret; - if (!(v & UB960_RR_AEQ_BYPASS_ENABLE)) { - ub960_rxport_read(priv, nport, UB960_RR_AEQ_MIN_MAX, &v); + dev_info(dev, "\tcsi_err_counter %u\n", v); - dev_info(dev, "\tEQ range [%u, %u]\n", - (v >> UB960_RR_AEQ_MIN_MAX_AEQ_FLOOR_SHIFT) & 0xf, - (v >> UB960_RR_AEQ_MIN_MAX_AEQ_MAX_SHIFT) & 0xf); + if (!priv->hw_data->is_ub9702) { + ret = ub960_log_status_ub960_sp_eq(priv, nport); + if (ret) + return ret; } - if (ub960_rxport_get_eq_level(priv, nport, &eq_level) == 0) - dev_info(dev, "\tEQ level %u\n", eq_level); - /* GPIOs */ - for (i = 0; i < UB960_NUM_BC_GPIOS; i++) { + for (unsigned int i = 0; i < UB960_NUM_BC_GPIOS; i++) { u8 ctl_reg; u8 ctl_shift; ctl_reg = UB960_RR_BC_GPIO_CTL(i / 2); ctl_shift = (i % 2) * 4; - ub960_rxport_read(priv, nport, ctl_reg, &v); + ret = ub960_rxport_read(priv, nport, ctl_reg, &v, NULL); + if (ret) + return ret; dev_info(dev, "\tGPIO%u: mode %u\n", i, (v >> ctl_shift) & 0xf); @@ -3108,34 +4304,36 @@ static const struct media_entity_operations ub960_entity_ops = { static irqreturn_t ub960_handle_events(int irq, void *arg) { struct ub960_data *priv = arg; - unsigned int i; u8 int_sts; u8 fwd_sts; int ret; - ret = ub960_read(priv, UB960_SR_INTERRUPT_STS, &int_sts); + ret = ub960_read(priv, UB960_SR_INTERRUPT_STS, &int_sts, NULL); if (ret || !int_sts) return IRQ_NONE; dev_dbg(&priv->client->dev, "INTERRUPT_STS %x\n", int_sts); - ret = ub960_read(priv, UB960_SR_FWD_STS, &fwd_sts); + ret = ub960_read(priv, UB960_SR_FWD_STS, &fwd_sts, NULL); if (ret) return IRQ_NONE; dev_dbg(&priv->client->dev, "FWD_STS %#02x\n", fwd_sts); - for (i = 0; i < priv->hw_data->num_txports; i++) { - if (int_sts & UB960_SR_INTERRUPT_STS_IS_CSI_TX(i)) - ub960_csi_handle_events(priv, i); + for (unsigned int i = 0; i < priv->hw_data->num_txports; i++) { + if (int_sts & UB960_SR_INTERRUPT_STS_IS_CSI_TX(i)) { + ret = ub960_csi_handle_events(priv, i); + if (ret) + return IRQ_NONE; + } } - for (i = 0; i < priv->hw_data->num_rxports; i++) { - if (!priv->rxports[i]) - continue; - - if (int_sts & UB960_SR_INTERRUPT_STS_IS_RX(i)) - ub960_rxport_handle_events(priv, i); + for_each_active_rxport(priv, it) { + if (int_sts & UB960_SR_INTERRUPT_STS_IS_RX(it.nport)) { + ret = ub960_rxport_handle_events(priv, it.nport); + if (ret) + return IRQ_NONE; + } } return IRQ_HANDLED; @@ -3170,19 +4368,14 @@ static void ub960_txport_free_ports(struct ub960_data *priv) static void ub960_rxport_free_ports(struct ub960_data *priv) { - unsigned int nport; + for_each_active_rxport(priv, it) { + fwnode_handle_put(it.rxport->source.ep_fwnode); + fwnode_handle_put(it.rxport->ser.fwnode); - for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { - struct ub960_rxport *rxport = priv->rxports[nport]; + mutex_destroy(&it.rxport->aliased_addrs_lock); - if (!rxport) - continue; - - fwnode_handle_put(rxport->source.ep_fwnode); - fwnode_handle_put(rxport->ser.fwnode); - - kfree(rxport); - priv->rxports[nport] = NULL; + kfree(it.rxport); + priv->rxports[it.nport] = NULL; } } @@ -3198,6 +4391,7 @@ ub960_parse_dt_rxport_link_properties(struct ub960_data *priv, s32 strobe_pos; u32 eq_level; u32 ser_i2c_alias; + u32 ser_i2c_addr; int ret; cdr_mode = RXPORT_CDR_FPD3; @@ -3309,6 +4503,13 @@ ub960_parse_dt_rxport_link_properties(struct ub960_data *priv, return -EINVAL; } + ret = fwnode_property_read_u32(rxport->ser.fwnode, "reg", + &ser_i2c_addr); + if (ret) + rxport->ser.addr = -EINVAL; + else + rxport->ser.addr = ser_i2c_addr; + return 0; } @@ -3401,6 +4602,8 @@ static int ub960_parse_dt_rxport(struct ub960_data *priv, unsigned int nport, if (ret) goto err_put_remote_fwnode; + mutex_init(&rxport->aliased_addrs_lock); + return 0; err_put_remote_fwnode: @@ -3441,7 +4644,6 @@ static int ub960_parse_dt_rxports(struct ub960_data *priv) { struct device *dev = &priv->client->dev; struct fwnode_handle *links_fwnode; - unsigned int nport; int ret; links_fwnode = fwnode_get_named_child_node(dev_fwnode(dev), "links"); @@ -3456,9 +4658,10 @@ static int ub960_parse_dt_rxports(struct ub960_data *priv) priv->strobe.manual = fwnode_property_read_bool(links_fwnode, "ti,manual-strobe"); - for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { + for_each_rxport(priv, it) { struct fwnode_handle *link_fwnode; struct fwnode_handle *ep_fwnode; + unsigned int nport = it.nport; link_fwnode = ub960_fwnode_get_link_by_regs(links_fwnode, nport); if (!link_fwnode) @@ -3547,7 +4750,6 @@ static int ub960_notify_bound(struct v4l2_async_notifier *notifier, struct ub960_rxport *rxport = to_ub960_asd(asd)->rxport; struct device *dev = &priv->client->dev; u8 nport = rxport->nport; - unsigned int i; int ret; ret = media_entity_get_fwnode_pad(&subdev->entity, @@ -3572,8 +4774,8 @@ static int ub960_notify_bound(struct v4l2_async_notifier *notifier, return ret; } - for (i = 0; i < priv->hw_data->num_rxports; i++) { - if (priv->rxports[i] && !priv->rxports[i]->source.sd) { + for_each_active_rxport(priv, it) { + if (!it.rxport->source.sd) { dev_dbg(dev, "Waiting for more subdevs to be bound\n"); return 0; } @@ -3599,29 +4801,24 @@ static const struct v4l2_async_notifier_operations ub960_notify_ops = { static int ub960_v4l2_notifier_register(struct ub960_data *priv) { struct device *dev = &priv->client->dev; - unsigned int i; int ret; v4l2_async_subdev_nf_init(&priv->notifier, &priv->sd); - for (i = 0; i < priv->hw_data->num_rxports; i++) { - struct ub960_rxport *rxport = priv->rxports[i]; + for_each_active_rxport(priv, it) { struct ub960_asd *asd; - if (!rxport) - continue; - asd = v4l2_async_nf_add_fwnode(&priv->notifier, - rxport->source.ep_fwnode, + it.rxport->source.ep_fwnode, struct ub960_asd); if (IS_ERR(asd)) { dev_err(dev, "Failed to add subdev for source %u: %pe", - i, asd); + it.nport, asd); v4l2_async_nf_cleanup(&priv->notifier); return PTR_ERR(asd); } - asd->rxport = rxport; + asd->rxport = it.rxport; } priv->notifier.ops = &ub960_notify_ops; @@ -3739,29 +4936,6 @@ static const struct regmap_config ub960_regmap_config = { .disable_locking = true, }; -static void ub960_reset(struct ub960_data *priv, bool reset_regs) -{ - struct device *dev = &priv->client->dev; - unsigned int v; - int ret; - u8 bit; - - bit = reset_regs ? UB960_SR_RESET_DIGITAL_RESET1 : - UB960_SR_RESET_DIGITAL_RESET0; - - ub960_write(priv, UB960_SR_RESET, bit); - - mutex_lock(&priv->reg_lock); - - ret = regmap_read_poll_timeout(priv->regmap, UB960_SR_RESET, v, - (v & bit) == 0, 2000, 100000); - - mutex_unlock(&priv->reg_lock); - - if (ret) - dev_err(dev, "reset failed: %d\n", ret); -} - static int ub960_get_hw_resources(struct ub960_data *priv) { struct device *dev = &priv->client->dev; @@ -3818,10 +4992,12 @@ static int ub960_enable_core_hw(struct ub960_data *priv) fsleep(2000); } - ub960_reset(priv, true); + ret = ub960_reset(priv, true); + if (ret) + goto err_pd_gpio; /* Runtime check register accessibility */ - ret = ub960_read(priv, UB960_SR_REV_MASK, &rev_mask); + ret = ub960_read(priv, UB960_SR_REV_MASK, &rev_mask, NULL); if (ret) { dev_err_probe(dev, ret, "Cannot read first register, abort\n"); goto err_pd_gpio; @@ -3830,20 +5006,25 @@ static int ub960_enable_core_hw(struct ub960_data *priv) dev_dbg(dev, "Found %s (rev/mask %#04x)\n", priv->hw_data->model, rev_mask); - ret = ub960_read(priv, UB960_SR_DEVICE_STS, &dev_sts); + ret = ub960_read(priv, UB960_SR_DEVICE_STS, &dev_sts, NULL); if (ret) goto err_pd_gpio; - ret = ub960_read(priv, UB960_XR_REFCLK_FREQ, &refclk_freq); + if (priv->hw_data->is_ub9702) + ret = ub960_read(priv, UB9702_SR_REFCLK_FREQ, &refclk_freq, + NULL); + else + ret = ub960_read(priv, UB960_XR_REFCLK_FREQ, &refclk_freq, + NULL); if (ret) goto err_pd_gpio; dev_dbg(dev, "refclk valid %u freq %u MHz (clk fw freq %lu MHz)\n", !!(dev_sts & BIT(4)), refclk_freq, - clk_get_rate(priv->refclk) / 1000000); + clk_get_rate(priv->refclk) / HZ_PER_MHZ); /* Disable all RX ports by default */ - ret = ub960_write(priv, UB960_SR_RX_PORT_CTL, 0); + ret = ub960_write(priv, UB960_SR_RX_PORT_CTL, 0, NULL); if (ret) goto err_pd_gpio; @@ -3851,7 +5032,8 @@ static int ub960_enable_core_hw(struct ub960_data *priv) if (priv->hw_data->is_ub9702) { ret = ub960_update_bits(priv, UB960_SR_RESET, UB960_SR_RESET_GPIO_LOCK_RELEASE, - UB960_SR_RESET_GPIO_LOCK_RELEASE); + UB960_SR_RESET_GPIO_LOCK_RELEASE, + NULL); if (ret) goto err_pd_gpio; } @@ -3878,9 +5060,6 @@ static int ub960_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct ub960_data *priv; - unsigned int port_lock_mask; - unsigned int port_mask; - unsigned int nport; int ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -3923,39 +5102,14 @@ static int ub960_probe(struct i2c_client *client) if (ret) goto err_free_ports; - ret = ub960_init_rx_ports(priv); - if (ret) - goto err_disable_vpocs; - - ub960_reset(priv, false); - - port_mask = 0; - - for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { - struct ub960_rxport *rxport = priv->rxports[nport]; - - if (!rxport) - continue; - - port_mask |= BIT(nport); - } + if (priv->hw_data->is_ub9702) + ret = ub960_init_rx_ports_ub9702(priv); + else + ret = ub960_init_rx_ports_ub960(priv); - ret = ub960_rxport_wait_locks(priv, port_mask, &port_lock_mask); if (ret) goto err_disable_vpocs; - if (port_mask != port_lock_mask) { - ret = -EIO; - dev_err_probe(dev, ret, "Failed to lock all RX ports\n"); - goto err_disable_vpocs; - } - - /* - * Clear any errors caused by switching the RX port settings while - * probing. - */ - ub960_clear_rx_errors(priv); - ret = ub960_init_atr(priv); if (ret) goto err_disable_vpocs; @@ -3974,6 +5128,12 @@ static int ub960_probe(struct i2c_client *client) schedule_delayed_work(&priv->poll_work, msecs_to_jiffies(UB960_POLL_TIME_MS)); +#ifdef UB960_DEBUG_I2C_RX_ID + for_each_rxport(priv, it) + ub960_write(priv, UB960_SR_I2C_RX_ID(it.nport), + (UB960_DEBUG_I2C_RX_ID + it.nport) << 1, NULL); +#endif + return 0; err_free_sers: @@ -4052,4 +5212,4 @@ MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Texas Instruments FPD-Link III/IV Deserializers Driver"); MODULE_AUTHOR("Luca Ceresoli <luca@lucaceresoli.net>"); MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>"); -MODULE_IMPORT_NS(I2C_ATR); +MODULE_IMPORT_NS("I2C_ATR"); diff --git a/drivers/media/i2c/dw9714.c b/drivers/media/i2c/dw9714.c index 2ddd7daa79e2..1e7ad355a388 100644 --- a/drivers/media/i2c/dw9714.c +++ b/drivers/media/i2c/dw9714.c @@ -2,6 +2,7 @@ // Copyright (c) 2015--2017 Intel Corporation. #include <linux/delay.h> +#include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/module.h> #include <linux/pm_runtime.h> @@ -38,6 +39,7 @@ struct dw9714_device { struct v4l2_subdev sd; u16 current_val; struct regulator *vcc; + struct gpio_desc *powerdown_gpio; }; static inline struct dw9714_device *to_dw9714_vcm(struct v4l2_ctrl *ctrl) @@ -137,6 +139,28 @@ static int dw9714_init_controls(struct dw9714_device *dev_vcm) return hdl->error; } +static int dw9714_power_up(struct dw9714_device *dw9714_dev) +{ + int ret; + + ret = regulator_enable(dw9714_dev->vcc); + if (ret) + return ret; + + gpiod_set_value_cansleep(dw9714_dev->powerdown_gpio, 0); + + usleep_range(1000, 2000); + + return 0; +} + +static int dw9714_power_down(struct dw9714_device *dw9714_dev) +{ + gpiod_set_value_cansleep(dw9714_dev->powerdown_gpio, 1); + + return regulator_disable(dw9714_dev->vcc); +} + static int dw9714_probe(struct i2c_client *client) { struct dw9714_device *dw9714_dev; @@ -144,20 +168,25 @@ static int dw9714_probe(struct i2c_client *client) dw9714_dev = devm_kzalloc(&client->dev, sizeof(*dw9714_dev), GFP_KERNEL); - if (dw9714_dev == NULL) + if (!dw9714_dev) return -ENOMEM; dw9714_dev->vcc = devm_regulator_get(&client->dev, "vcc"); if (IS_ERR(dw9714_dev->vcc)) return PTR_ERR(dw9714_dev->vcc); - rval = regulator_enable(dw9714_dev->vcc); - if (rval < 0) { - dev_err(&client->dev, "failed to enable vcc: %d\n", rval); - return rval; - } + dw9714_dev->powerdown_gpio = devm_gpiod_get_optional(&client->dev, + "powerdown", + GPIOD_OUT_HIGH); + if (IS_ERR(dw9714_dev->powerdown_gpio)) + return dev_err_probe(&client->dev, + PTR_ERR(dw9714_dev->powerdown_gpio), + "could not get powerdown gpio\n"); - usleep_range(1000, 2000); + rval = dw9714_power_up(dw9714_dev); + if (rval) + return dev_err_probe(&client->dev, rval, + "failed to power up: %d\n", rval); v4l2_i2c_subdev_init(&dw9714_dev->sd, client, &dw9714_ops); dw9714_dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | @@ -185,7 +214,7 @@ static int dw9714_probe(struct i2c_client *client) return 0; err_cleanup: - regulator_disable(dw9714_dev->vcc); + dw9714_power_down(dw9714_dev); v4l2_ctrl_handler_free(&dw9714_dev->ctrls_vcm); media_entity_cleanup(&dw9714_dev->sd.entity); @@ -200,10 +229,10 @@ static void dw9714_remove(struct i2c_client *client) pm_runtime_disable(&client->dev); if (!pm_runtime_status_suspended(&client->dev)) { - ret = regulator_disable(dw9714_dev->vcc); + ret = dw9714_power_down(dw9714_dev); if (ret) { dev_err(&client->dev, - "Failed to disable vcc: %d\n", ret); + "Failed to power down: %d\n", ret); } } pm_runtime_set_suspended(&client->dev); @@ -234,9 +263,9 @@ static int __maybe_unused dw9714_vcm_suspend(struct device *dev) usleep_range(DW9714_CTRL_DELAY_US, DW9714_CTRL_DELAY_US + 10); } - ret = regulator_disable(dw9714_dev->vcc); + ret = dw9714_power_down(dw9714_dev); if (ret) - dev_err(dev, "Failed to disable vcc: %d\n", ret); + dev_err(dev, "Failed to power down: %d\n", ret); return ret; } @@ -247,7 +276,7 @@ static int __maybe_unused dw9714_vcm_suspend(struct device *dev) * The lens position is gradually moved in units of DW9714_CTRL_STEPS, * to make the movements smoothly. */ -static int __maybe_unused dw9714_vcm_resume(struct device *dev) +static int __maybe_unused dw9714_vcm_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct v4l2_subdev *sd = i2c_get_clientdata(client); @@ -257,12 +286,11 @@ static int __maybe_unused dw9714_vcm_resume(struct device *dev) if (pm_runtime_suspended(&client->dev)) return 0; - ret = regulator_enable(dw9714_dev->vcc); + ret = dw9714_power_up(dw9714_dev); if (ret) { - dev_err(dev, "Failed to enable vcc: %d\n", ret); + dev_err(dev, "Failed to power up: %d\n", ret); return ret; } - usleep_range(1000, 2000); for (val = dw9714_dev->current_val % DW9714_CTRL_STEPS; val < dw9714_dev->current_val + DW9714_CTRL_STEPS - 1; @@ -271,7 +299,7 @@ static int __maybe_unused dw9714_vcm_resume(struct device *dev) DW9714_VAL(val, DW9714_DEFAULT_S)); if (ret) dev_err_ratelimited(dev, "%s I2C failure: %d", - __func__, ret); + __func__, ret); usleep_range(DW9714_CTRL_DELAY_US, DW9714_CTRL_DELAY_US + 10); } diff --git a/drivers/media/i2c/dw9719.c b/drivers/media/i2c/dw9719.c index c626ed845928..59558335989e 100644 --- a/drivers/media/i2c/dw9719.c +++ b/drivers/media/i2c/dw9719.c @@ -2,8 +2,10 @@ // Copyright (c) 2012 Intel Corporation /* - * Based on linux/modules/camera/drivers/media/i2c/imx/dw9719.c in this repo: - * https://github.com/ZenfoneArea/android_kernel_asus_zenfone5 + * Based on linux/modules/camera/drivers/media/i2c/imx/dw9719.c from: + * https://github.com/ZenfoneArea/android_kernel_asus_zenfone5 and + * latte-l-oss/drivers/external_drivers/camera/drivers/media/i2c/micam/dw9761.c + * from: https://github.com/MiCode/Xiaomi_Kernel_OpenSource/ */ #include <linux/delay.h> @@ -21,28 +23,71 @@ #define DW9719_CTRL_STEPS 16 #define DW9719_CTRL_DELAY_US 1000 +#define DW9718S_PD CCI_REG8(0) + +#define DW9718S_CONTROL CCI_REG8(1) +#define DW9718S_CONTROL_SW_LINEAR BIT(0) +#define DW9718S_CONTROL_SAC_SHIFT 1 +#define DW9718S_CONTROL_SAC_MASK 0x7 +#define DW9718S_CONTROL_OCP_DISABLE BIT(4) +#define DW9718S_CONTROL_UVLO_DISABLE BIT(5) +#define DW9718S_DEFAULT_SAC 4 + +#define DW9718S_VCM_CURRENT CCI_REG16(2) + +#define DW9718S_SW CCI_REG8(4) +#define DW9718S_SW_VCM_FREQ_MASK 0xF +#define DW9718S_DEFAULT_VCM_FREQ 0 + +#define DW9718S_SACT CCI_REG8(5) +#define DW9718S_SACT_PERIOD_8_8MS 0x19 + #define DW9719_INFO CCI_REG8(0) #define DW9719_ID 0xF1 +#define DW9761_ID 0xF4 #define DW9719_CONTROL CCI_REG8(2) +#define DW9719_STANDBY 0x00 +#define DW9719_SHUTDOWN 0x01 #define DW9719_ENABLE_RINGING 0x02 #define DW9719_VCM_CURRENT CCI_REG16(3) +#define DW9719_STATUS CCI_REG16(5) +#define DW9719_STATUS_BUSY BIT(0) + #define DW9719_MODE CCI_REG8(6) #define DW9719_MODE_SAC_SHIFT 4 -#define DW9719_MODE_SAC3 4 +#define DW9719_DEFAULT_SAC 4 +#define DW9761_DEFAULT_SAC 6 #define DW9719_VCM_FREQ CCI_REG8(7) #define DW9719_DEFAULT_VCM_FREQ 0x60 +#define DW9761_DEFAULT_VCM_FREQ 0x3E + +#define DW9761_VCM_PRELOAD CCI_REG8(8) +#define DW9761_DEFAULT_VCM_PRELOAD 0x73 + +#define DW9800K_DEFAULT_SAC 1 +#define DW9800K_MODE_SAC_SHIFT 6 +#define DW9800K_DEFAULT_VCM_FREQ 0x10 #define to_dw9719_device(x) container_of(x, struct dw9719_device, sd) +enum dw9719_model { + DW9718S, + DW9719, + DW9761, + DW9800K, +}; + struct dw9719_device { struct v4l2_subdev sd; struct device *dev; struct regmap *regmap; struct regulator *regulator; + enum dw9719_model model; + u32 mode_low_bits; u32 sac_mode; u32 vcm_freq; @@ -52,46 +97,126 @@ struct dw9719_device { } ctrls; }; -static int dw9719_detect(struct dw9719_device *dw9719) -{ - int ret; - u64 val; - - ret = cci_read(dw9719->regmap, DW9719_INFO, &val, NULL); - if (ret < 0) - return ret; - - if (val != DW9719_ID) { - dev_err(dw9719->dev, "Failed to detect correct id\n"); - return -ENXIO; - } - - return 0; -} - static int dw9719_power_down(struct dw9719_device *dw9719) { + u32 reg_pwr = dw9719->model == DW9718S ? DW9718S_PD : DW9719_CONTROL; + + /* + * Worth engaging the internal SHUTDOWN mode especially due to the + * regulator being potentially shared with other devices. + */ + if (cci_write(dw9719->regmap, reg_pwr, DW9719_SHUTDOWN, NULL)) + dev_err(dw9719->dev, "Error writing to power register\n"); return regulator_disable(dw9719->regulator); } -static int dw9719_power_up(struct dw9719_device *dw9719) +static int dw9719_power_up(struct dw9719_device *dw9719, bool detect) { + u32 reg_pwr = dw9719->model == DW9718S ? DW9718S_PD : DW9719_CONTROL; + u64 val; int ret; + int err; ret = regulator_enable(dw9719->regulator); if (ret) return ret; - /* Jiggle SCL pin to wake up device */ - cci_write(dw9719->regmap, DW9719_CONTROL, 1, &ret); + /* + * Need 100us to transition from SHUTDOWN to STANDBY. + * Jiggle the SCL pin to wake up the device (even when the regulator is + * shared) and wait double the time to be sure, as 100us is not enough + * at least on the DW9718S as found on the motorola-nora smartphone, + * then retry the write. + */ + cci_write(dw9719->regmap, reg_pwr, DW9719_STANDBY, NULL); + /* the jiggle is expected to fail, don't even log that as error */ + fsleep(200); + cci_write(dw9719->regmap, reg_pwr, DW9719_STANDBY, &ret); + + if (detect) { + /* These models do not have an INFO register */ + switch (dw9719->model) { + case DW9718S: + dw9719->sac_mode = DW9718S_DEFAULT_SAC; + dw9719->vcm_freq = DW9718S_DEFAULT_VCM_FREQ; + goto props; + case DW9800K: + dw9719->sac_mode = DW9800K_DEFAULT_SAC; + dw9719->vcm_freq = DW9800K_DEFAULT_VCM_FREQ; + goto props; + default: + break; + } + + ret = cci_read(dw9719->regmap, DW9719_INFO, &val, NULL); + if (ret < 0) + return ret; - /* Need 100us to transit from SHUTDOWN to STANDBY */ - fsleep(100); + switch (val) { + case DW9719_ID: + dw9719->model = DW9719; + dw9719->mode_low_bits = 0x00; + dw9719->sac_mode = DW9719_DEFAULT_SAC; + dw9719->vcm_freq = DW9719_DEFAULT_VCM_FREQ; + break; + case DW9761_ID: + dw9719->model = DW9761; + dw9719->mode_low_bits = 0x01; + dw9719->sac_mode = DW9761_DEFAULT_SAC; + dw9719->vcm_freq = DW9761_DEFAULT_VCM_FREQ; + break; + default: + dev_err(dw9719->dev, + "Error unknown device id 0x%02llx\n", val); + return -ENXIO; + } + +props: + /* Optional indication of SAC mode select */ + device_property_read_u32(dw9719->dev, "dongwoon,sac-mode", + &dw9719->sac_mode); + + /* Optional indication of VCM frequency */ + err = device_property_read_u32(dw9719->dev, "dongwoon,vcm-freq", + &dw9719->vcm_freq); + if (err == 0) + dev_warn(dw9719->dev, "dongwoon,vcm-freq property is deprecated, please use dongwoon,vcm-prescale\n"); + + /* Optional indication of VCM prescale */ + device_property_read_u32(dw9719->dev, "dongwoon,vcm-prescale", + &dw9719->vcm_freq); + } - cci_write(dw9719->regmap, DW9719_CONTROL, DW9719_ENABLE_RINGING, &ret); - cci_write(dw9719->regmap, DW9719_MODE, - dw9719->sac_mode << DW9719_MODE_SAC_SHIFT, &ret); - cci_write(dw9719->regmap, DW9719_VCM_FREQ, dw9719->vcm_freq, &ret); + switch (dw9719->model) { + case DW9800K: + cci_write(dw9719->regmap, DW9719_CONTROL, DW9719_ENABLE_RINGING, &ret); + cci_write(dw9719->regmap, DW9719_MODE, + dw9719->sac_mode << DW9800K_MODE_SAC_SHIFT, &ret); + cci_write(dw9719->regmap, DW9719_VCM_FREQ, dw9719->vcm_freq, &ret); + break; + case DW9718S: + /* Datasheet says [OCP/UVLO] should be disabled below 2.5V */ + dw9719->sac_mode &= DW9718S_CONTROL_SAC_MASK; + cci_write(dw9719->regmap, DW9718S_CONTROL, + DW9718S_CONTROL_SW_LINEAR | + (dw9719->sac_mode << DW9718S_CONTROL_SAC_SHIFT) | + DW9718S_CONTROL_OCP_DISABLE | + DW9718S_CONTROL_UVLO_DISABLE, &ret); + cci_write(dw9719->regmap, DW9718S_SACT, + DW9718S_SACT_PERIOD_8_8MS, &ret); + cci_write(dw9719->regmap, DW9718S_SW, + dw9719->vcm_freq & DW9718S_SW_VCM_FREQ_MASK, &ret); + break; + case DW9761: + cci_write(dw9719->regmap, DW9761_VCM_PRELOAD, + DW9761_DEFAULT_VCM_PRELOAD, &ret); + fallthrough; + case DW9719: + cci_write(dw9719->regmap, DW9719_CONTROL, DW9719_ENABLE_RINGING, &ret); + cci_write(dw9719->regmap, DW9719_MODE, dw9719->mode_low_bits | + (dw9719->sac_mode << DW9719_MODE_SAC_SHIFT), &ret); + cci_write(dw9719->regmap, DW9719_VCM_FREQ, dw9719->vcm_freq, &ret); + } if (ret) dw9719_power_down(dw9719); @@ -101,7 +226,9 @@ static int dw9719_power_up(struct dw9719_device *dw9719) static int dw9719_t_focus_abs(struct dw9719_device *dw9719, s32 value) { - return cci_write(dw9719->regmap, DW9719_VCM_CURRENT, value, NULL); + u32 reg = dw9719->model == DW9718S ? DW9718S_VCM_CURRENT + : DW9719_VCM_CURRENT; + return cci_write(dw9719->regmap, reg, value, NULL); } static int dw9719_set_ctrl(struct v4l2_ctrl *ctrl) @@ -159,7 +286,7 @@ static int dw9719_resume(struct device *dev) int ret; int val; - ret = dw9719_power_up(dw9719); + ret = dw9719_power_up(dw9719, false); if (ret) return ret; @@ -186,7 +313,7 @@ static int dw9719_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) static int dw9719_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { - pm_runtime_put(sd->dev); + pm_runtime_put_autosuspend(sd->dev); return 0; } @@ -232,21 +359,13 @@ static int dw9719_probe(struct i2c_client *client) if (!dw9719) return -ENOMEM; + dw9719->model = (enum dw9719_model)(uintptr_t)i2c_get_match_data(client); + dw9719->regmap = devm_cci_regmap_init_i2c(client, 8); if (IS_ERR(dw9719->regmap)) return PTR_ERR(dw9719->regmap); dw9719->dev = &client->dev; - dw9719->sac_mode = DW9719_MODE_SAC3; - dw9719->vcm_freq = DW9719_DEFAULT_VCM_FREQ; - - /* Optional indication of SAC mode select */ - device_property_read_u32(&client->dev, "dongwoon,sac-mode", - &dw9719->sac_mode); - - /* Optional indication of VCM frequency */ - device_property_read_u32(&client->dev, "dongwoon,vcm-freq", - &dw9719->vcm_freq); dw9719->regulator = devm_regulator_get(&client->dev, "vdd"); if (IS_ERR(dw9719->regulator)) @@ -274,14 +393,10 @@ static int dw9719_probe(struct i2c_client *client) * will work. */ - ret = dw9719_power_up(dw9719); + ret = dw9719_power_up(dw9719, true); if (ret) goto err_cleanup_media; - ret = dw9719_detect(dw9719); - if (ret) - goto err_powerdown; - pm_runtime_set_active(&client->dev); pm_runtime_get_noresume(&client->dev); pm_runtime_enable(&client->dev); @@ -299,7 +414,6 @@ static int dw9719_probe(struct i2c_client *client) err_pm_runtime: pm_runtime_disable(&client->dev); pm_runtime_put_noidle(&client->dev); -err_powerdown: dw9719_power_down(dw9719); err_cleanup_media: media_entity_cleanup(&dw9719->sd.entity); @@ -325,11 +439,14 @@ static void dw9719_remove(struct i2c_client *client) pm_runtime_set_suspended(&client->dev); } -static const struct i2c_device_id dw9719_id_table[] = { - { "dw9719" }, +static const struct of_device_id dw9719_of_table[] = { + { .compatible = "dongwoon,dw9718s", .data = (const void *)DW9718S }, + { .compatible = "dongwoon,dw9719", .data = (const void *)DW9719 }, + { .compatible = "dongwoon,dw9761", .data = (const void *)DW9761 }, + { .compatible = "dongwoon,dw9800k", .data = (const void *)DW9800K }, { } }; -MODULE_DEVICE_TABLE(i2c, dw9719_id_table); +MODULE_DEVICE_TABLE(of, dw9719_of_table); static DEFINE_RUNTIME_DEV_PM_OPS(dw9719_pm_ops, dw9719_suspend, dw9719_resume, NULL); @@ -338,10 +455,10 @@ static struct i2c_driver dw9719_i2c_driver = { .driver = { .name = "dw9719", .pm = pm_sleep_ptr(&dw9719_pm_ops), + .of_match_table = dw9719_of_table, }, .probe = dw9719_probe, .remove = dw9719_remove, - .id_table = dw9719_id_table, }; module_i2c_driver(dw9719_i2c_driver); diff --git a/drivers/media/i2c/dw9768.c b/drivers/media/i2c/dw9768.c index 3a4d100b9199..d434721ba8ed 100644 --- a/drivers/media/i2c/dw9768.c +++ b/drivers/media/i2c/dw9768.c @@ -374,7 +374,6 @@ static int dw9768_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) static int dw9768_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { - pm_runtime_mark_last_busy(sd->dev); pm_runtime_put_autosuspend(sd->dev); return 0; diff --git a/drivers/media/i2c/et8ek8/et8ek8_driver.c b/drivers/media/i2c/et8ek8/et8ek8_driver.c index 7519863d77b1..2cb7b718782b 100644 --- a/drivers/media/i2c/et8ek8/et8ek8_driver.c +++ b/drivers/media/i2c/et8ek8/et8ek8_driver.c @@ -816,7 +816,6 @@ static int et8ek8_power_on(struct et8ek8_sensor *sensor) { struct v4l2_subdev *subdev = &sensor->subdev; struct i2c_client *client = v4l2_get_subdevdata(subdev); - unsigned int xclk_freq; int val, rval; rval = regulator_enable(sensor->vana); @@ -825,17 +824,6 @@ static int et8ek8_power_on(struct et8ek8_sensor *sensor) return rval; } - if (sensor->current_reglist) - xclk_freq = sensor->current_reglist->mode.ext_clock; - else - xclk_freq = sensor->xclk_freq; - - rval = clk_set_rate(sensor->ext_clk, xclk_freq); - if (rval < 0) { - dev_err(&client->dev, "unable to set extclk clock freq to %u\n", - xclk_freq); - goto out; - } rval = clk_prepare_enable(sensor->ext_clk); if (rval < 0) { dev_err(&client->dev, "failed to enable extclk\n"); @@ -849,7 +837,7 @@ static int et8ek8_power_on(struct et8ek8_sensor *sensor) gpiod_set_value(sensor->reset, 1); - msleep(5000 * 1000 / xclk_freq + 1); /* Wait 5000 cycles */ + msleep(5000 * 1000 / sensor->xclk_freq + 1); /* Wait 5000 cycles */ rval = et8ek8_i2c_reglist_find_write(client, &meta_reglist, ET8EK8_REGLIST_POWERON); @@ -1085,9 +1073,6 @@ static int et8ek8_set_frame_interval(struct v4l2_subdev *subdev, if (!reglist) return -EINVAL; - if (sensor->current_reglist->mode.ext_clock != reglist->mode.ext_clock) - return -EINVAL; - sensor->current_reglist = reglist; et8ek8_update_controls(sensor); @@ -1433,18 +1418,13 @@ static int et8ek8_probe(struct i2c_client *client) return PTR_ERR(sensor->vana); } - sensor->ext_clk = devm_clk_get(dev, NULL); - if (IS_ERR(sensor->ext_clk)) { - dev_err(&client->dev, "could not get clock\n"); - return PTR_ERR(sensor->ext_clk); - } + sensor->ext_clk = devm_v4l2_sensor_clk_get_legacy(dev, NULL, true, + 9600000); + if (IS_ERR(sensor->ext_clk)) + return dev_err_probe(&client->dev, PTR_ERR(sensor->ext_clk), + "could not get clock\n"); - ret = of_property_read_u32(dev->of_node, "clock-frequency", - &sensor->xclk_freq); - if (ret) { - dev_warn(dev, "can't get clock-frequency\n"); - return ret; - } + sensor->xclk_freq = clk_get_rate(sensor->ext_clk); mutex_init(&sensor->power_lock); diff --git a/drivers/media/i2c/et8ek8/et8ek8_mode.c b/drivers/media/i2c/et8ek8/et8ek8_mode.c index c9088eb0a812..914be1007099 100644 --- a/drivers/media/i2c/et8ek8/et8ek8_mode.c +++ b/drivers/media/i2c/et8ek8/et8ek8_mode.c @@ -44,7 +44,6 @@ static struct et8ek8_reglist mode1_poweron_mode2_16vga_2592x1968_12_07fps = { .window_width = 2592, .window_height = 1968, .pixel_clock = 80000000, - .ext_clock = 9600000, .timeperframe = { .numerator = 100, .denominator = 1207 @@ -145,7 +144,6 @@ static struct et8ek8_reglist mode1_16vga_2592x1968_13_12fps_dpcm10_8 = { .window_width = 2592, .window_height = 1968, .pixel_clock = 80000000, - .ext_clock = 9600000, .timeperframe = { .numerator = 100, .denominator = 1292 @@ -201,7 +199,6 @@ static struct et8ek8_reglist mode3_4vga_1296x984_29_99fps_dpcm10_8 = { .window_width = 1296, .window_height = 984, .pixel_clock = 96533333, - .ext_clock = 9600000, .timeperframe = { .numerator = 100, .denominator = 3000 @@ -257,7 +254,6 @@ static struct et8ek8_reglist mode4_svga_864x656_29_88fps = { .window_width = 864, .window_height = 656, .pixel_clock = 80000000, - .ext_clock = 9600000, .timeperframe = { .numerator = 100, .denominator = 2988 @@ -313,7 +309,6 @@ static struct et8ek8_reglist mode5_vga_648x492_29_93fps = { .window_width = 648, .window_height = 492, .pixel_clock = 80000000, - .ext_clock = 9600000, .timeperframe = { .numerator = 100, .denominator = 2993 @@ -369,7 +364,6 @@ static struct et8ek8_reglist mode2_16vga_2592x1968_3_99fps = { .window_width = 2592, .window_height = 1968, .pixel_clock = 80000000, - .ext_clock = 9600000, .timeperframe = { .numerator = 100, .denominator = 399 @@ -424,7 +418,6 @@ static struct et8ek8_reglist mode_648x492_5fps = { .window_width = 648, .window_height = 492, .pixel_clock = 13333333, - .ext_clock = 9600000, .timeperframe = { .numerator = 100, .denominator = 499 @@ -480,7 +473,6 @@ static struct et8ek8_reglist mode3_4vga_1296x984_5fps = { .window_width = 1296, .window_height = 984, .pixel_clock = 49400000, - .ext_clock = 9600000, .timeperframe = { .numerator = 100, .denominator = 501 @@ -536,7 +528,6 @@ static struct et8ek8_reglist mode_4vga_1296x984_25fps_dpcm10_8 = { .window_width = 1296, .window_height = 984, .pixel_clock = 84266667, - .ext_clock = 9600000, .timeperframe = { .numerator = 100, .denominator = 2500 diff --git a/drivers/media/i2c/et8ek8/et8ek8_reg.h b/drivers/media/i2c/et8ek8/et8ek8_reg.h index c90e74935f12..3305986c7c9c 100644 --- a/drivers/media/i2c/et8ek8/et8ek8_reg.h +++ b/drivers/media/i2c/et8ek8/et8ek8_reg.h @@ -37,7 +37,6 @@ struct et8ek8_mode { u16 window_height; u32 pixel_clock; /* in Hz */ - u32 ext_clock; /* in Hz */ struct v4l2_fract timeperframe; u32 max_exp; /* Maximum exposure value */ u32 bus_format; /* MEDIA_BUS_FMT_ */ diff --git a/drivers/media/i2c/gc0308.c b/drivers/media/i2c/gc0308.c index 069f42785b3c..cbcda0e18ff1 100644 --- a/drivers/media/i2c/gc0308.c +++ b/drivers/media/i2c/gc0308.c @@ -974,7 +974,6 @@ static int gc0308_s_ctrl(struct v4l2_ctrl *ctrl) if (ret) dev_err(gc0308->dev, "failed to set control: %d\n", ret); - pm_runtime_mark_last_busy(gc0308->dev); pm_runtime_put_autosuspend(gc0308->dev); return ret; @@ -1157,14 +1156,12 @@ static int gc0308_start_stream(struct gc0308 *gc0308) return 0; disable_pm: - pm_runtime_mark_last_busy(gc0308->dev); pm_runtime_put_autosuspend(gc0308->dev); return ret; } static int gc0308_stop_stream(struct gc0308 *gc0308) { - pm_runtime_mark_last_busy(gc0308->dev); pm_runtime_put_autosuspend(gc0308->dev); return 0; } diff --git a/drivers/media/i2c/gc0310.c b/drivers/media/i2c/gc0310.c new file mode 100644 index 000000000000..7af4d66f42a0 --- /dev/null +++ b/drivers/media/i2c/gc0310.c @@ -0,0 +1,783 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Support for GalaxyCore GC0310 VGA camera sensor. + * + * Copyright (c) 2013 Intel Corporation. All Rights Reserved. + * Copyright (c) 2023-2025 Hans de Goede <hansg@kernel.org> + */ + +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/string.h> +#include <linux/types.h> + +#include <media/v4l2-cci.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> + +#define GC0310_NATIVE_WIDTH 656 +#define GC0310_NATIVE_HEIGHT 496 + +/* + * The actual PLL output rate is unknown, the datasheet + * says that the formula for the frame-time in pixels is: + * rowtime = win-width + hblank + sh-delay + 4 + * frametime = rowtime * (win-height + vblank) + * Filling this in and multiplying by 30 fps gives: + * pixelrate = (660 + 178 + 42 + 4) * (498 + 27) * 30 = 13923000 + */ +#define GC0310_PIXELRATE 13923000 +/* single lane, bus-format is 8 bpp, CSI-2 is double data rate */ +#define GC0310_LINK_FREQ (GC0310_PIXELRATE * 8 / 2) +#define GC0310_MCLK_FREQ 19200000 +#define GC0310_FPS 30 +#define GC0310_SKIP_FRAMES 3 + +#define GC0310_ID 0xa310 + +#define GC0310_RESET_RELATED_REG CCI_REG8(0xfe) +#define GC0310_REGISTER_PAGE_0 0x0 +#define GC0310_REGISTER_PAGE_3 0x3 + +/* + * GC0310 System control registers + */ +#define GC0310_SW_STREAM_REG CCI_REG8(0x10) + +#define GC0310_START_STREAMING 0x94 /* 8-bit enable */ +#define GC0310_STOP_STREAMING 0x0 /* 8-bit disable */ + +#define GC0310_SC_CMMN_CHIP_ID_REG CCI_REG16(0xf0) + +#define GC0310_AEC_PK_EXPO_REG CCI_REG16(0x03) +#define GC0310_AGC_ADJ_REG CCI_REG8(0x48) +#define GC0310_DGC_ADJ_REG CCI_REG8(0x71) + +#define GC0310_H_CROP_START_REG CCI_REG16(0x09) +#define GC0310_V_CROP_START_REG CCI_REG16(0x0b) +#define GC0310_H_OUTSIZE_REG CCI_REG16(0x0f) +#define GC0310_V_OUTSIZE_REG CCI_REG16(0x0d) + +#define GC0310_H_BLANKING_REG CCI_REG16(0x05) +/* Hblank-register + sh-delay + H-crop + 4 (from hw) */ +#define GC0310_H_BLANK_DEFAULT (178 + 42 + 4 + 4) + +#define GC0310_V_BLANKING_REG CCI_REG16(0x07) +/* Vblank needs an offset compensate for the small V-crop done */ +#define GC0310_V_BLANK_OFFSET 2 +/* Vsync start time + 1 row vsync + vsync end time + offset */ +#define GC0310_V_BLANK_MIN (9 + 1 + 4 + GC0310_V_BLANK_OFFSET) +#define GC0310_V_BLANK_DEFAULT (27 + GC0310_V_BLANK_OFFSET) +#define GC0310_V_BLANK_MAX (4095 - GC0310_NATIVE_HEIGHT) + +#define GC0310_SH_DELAY_REG CCI_REG8(0x11) +#define GC0310_VS_START_TIME_REG CCI_REG8(0x12) +#define GC0310_VS_END_TIME_REG CCI_REG8(0x13) + +#define to_gc0310_sensor(x) container_of(x, struct gc0310_device, sd) + +struct gc0310_device { + struct v4l2_subdev sd; + struct media_pad pad; + + struct regmap *regmap; + struct gpio_desc *reset; + struct gpio_desc *powerdown; + + struct gc0310_ctrls { + struct v4l2_ctrl_handler handler; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *gain; + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + } ctrls; +}; + +struct gc0310_reg { + u8 reg; + u8 val; +}; + +static const struct reg_sequence gc0310_reset_register[] = { + /* System registers */ + { 0xfe, 0xf0 }, + { 0xfe, 0xf0 }, + { 0xfe, 0x00 }, + + { 0xfc, 0x0e }, /* 4e */ + { 0xfc, 0x0e }, /* 16//4e // [0]apwd [6]regf_clk_gate */ + { 0xf2, 0x80 }, /* sync output */ + { 0xf3, 0x00 }, /* 1f//01 data output */ + { 0xf7, 0x33 }, /* f9 */ + { 0xf8, 0x05 }, /* 00 */ + { 0xf9, 0x0e }, /* 0x8e //0f */ + { 0xfa, 0x11 }, + + /* MIPI */ + { 0xfe, 0x03 }, + { 0x01, 0x03 }, /* mipi 1lane */ + { 0x02, 0x22 }, /* 0x33 */ + { 0x03, 0x94 }, + { 0x04, 0x01 }, /* fifo_prog */ + { 0x05, 0x00 }, /* fifo_prog */ + { 0x06, 0x80 }, /* b0 //YUV ISP data */ + { 0x11, 0x2a }, /* 1e //LDI set YUV422 */ + { 0x12, 0x90 }, /* 00 //04 //00 //04//00 //LWC[7:0] */ + { 0x13, 0x02 }, /* 05 //05 //LWC[15:8] */ + { 0x15, 0x12 }, /* 0x10 //DPHYY_MODE read_ready */ + { 0x17, 0x01 }, + { 0x40, 0x08 }, + { 0x41, 0x00 }, + { 0x42, 0x00 }, + { 0x43, 0x00 }, + { 0x21, 0x02 }, /* 0x01 */ + { 0x22, 0x02 }, /* 0x01 */ + { 0x23, 0x01 }, /* 0x05 //Nor:0x05 DOU:0x06 */ + { 0x29, 0x00 }, + { 0x2A, 0x25 }, /* 0x05 //data zero 0x7a de */ + { 0x2B, 0x02 }, + + { 0xfe, 0x00 }, + + /* CISCTL */ + { 0x00, 0x2f }, /* 2f//0f//02//01 */ + { 0x01, 0x0f }, /* 06 */ + { 0x02, 0x04 }, + { 0x4f, 0x00 }, /* AEC 0FF */ + { 0x03, 0x01 }, /* 0x03 //04 */ + { 0x04, 0xc0 }, /* 0xe8 //58 */ + { 0x05, 0x00 }, + { 0x06, 0xb2 }, /* 0x0a //HB */ + /* Vblank (reg 0x07 + 0x08) gets set by the vblank ctrl */ + { 0x09, 0x00 }, /* row start */ + { 0x0a, 0x00 }, + { 0x0b, 0x00 }, /* col start */ + { 0x0c, 0x00 }, + { 0x0d, 0x01 }, /* height */ + { 0x0e, 0xf2 }, /* 0xf7 //height */ + { 0x0f, 0x02 }, /* width */ + { 0x10, 0x94 }, /* 0xa0 //height */ + { 0x17, 0x14 }, + { 0x18, 0x1a }, /* 0a//[4]double reset */ + { 0x19, 0x14 }, /* AD pipeline */ + { 0x1b, 0x48 }, + { 0x1e, 0x6b }, /* 3b//col bias */ + { 0x1f, 0x28 }, /* 20//00//08//txlow */ + { 0x20, 0x89 }, /* 88//0c//[3:2]DA15 */ + { 0x21, 0x49 }, /* 48//[3] txhigh */ + { 0x22, 0xb0 }, + { 0x23, 0x04 }, /* [1:0]vcm_r */ + { 0x24, 0x16 }, /* 15 */ + { 0x34, 0x20 }, /* [6:4] rsg high//range */ + + /* BLK */ + { 0x26, 0x23 }, /* [1]dark_current_en [0]offset_en */ + { 0x28, 0xff }, /* BLK_limie_value */ + { 0x29, 0x00 }, /* global offset */ + { 0x33, 0x18 }, /* offset_ratio */ + { 0x37, 0x20 }, /* dark_current_ratio */ + { 0x2a, 0x00 }, + { 0x2b, 0x00 }, + { 0x2c, 0x00 }, + { 0x2d, 0x00 }, + { 0x2e, 0x00 }, + { 0x2f, 0x00 }, + { 0x30, 0x00 }, + { 0x31, 0x00 }, + { 0x47, 0x80 }, /* a7 */ + { 0x4e, 0x66 }, /* select_row */ + { 0xa8, 0x02 }, /* win_width_dark, same with crop_win_width */ + { 0xa9, 0x80 }, + + /* ISP */ + { 0x40, 0x06 }, /* 0xff //ff //48 */ + { 0x41, 0x00 }, /* 0x21 //00//[0]curve_en */ + { 0x42, 0x04 }, /* 0xcf //0a//[1]awn_en */ + { 0x44, 0x18 }, /* 0x18 //02 */ + { 0x46, 0x02 }, /* 0x03 //sync */ + { 0x49, 0x03 }, + { 0x4c, 0x20 }, /* 00[5]pretect exp */ + { 0x50, 0x01 }, /* crop enable */ + { 0x51, 0x00 }, + { 0x52, 0x00 }, + { 0x53, 0x00 }, + { 0x54, 0x01 }, + { 0x55, 0x01 }, /* crop window height */ + { 0x56, 0xf0 }, + { 0x57, 0x02 }, /* crop window width */ + { 0x58, 0x90 }, + + /* Gain */ + { 0x70, 0x70 }, /* 70 //80//global gain */ + { 0x71, 0x20 }, /* pregain gain */ + { 0x72, 0x40 }, /* post gain */ + { 0x5a, 0x84 }, /* 84//analog gain 0 */ + { 0x5b, 0xc9 }, /* c9 */ + { 0x5c, 0xed }, /* ed//not use pga gain highest level */ + { 0x77, 0x40 }, /* R gain 0x74 //awb gain */ + { 0x78, 0x40 }, /* G gain */ + { 0x79, 0x40 }, /* B gain 0x5f */ + + { 0x48, 0x00 }, + { 0xfe, 0x01 }, + { 0x0a, 0x45 }, /* [7]col gain mode */ + + { 0x3e, 0x40 }, + { 0x3f, 0x5c }, + { 0x40, 0x7b }, + { 0x41, 0xbd }, + { 0x42, 0xf6 }, + { 0x43, 0x63 }, + { 0x03, 0x60 }, + { 0x44, 0x03 }, + + /* Dark / Sun mode related */ + { 0xfe, 0x01 }, + { 0x45, 0xa4 }, /* 0xf7 */ + { 0x46, 0xf0 }, /* 0xff //f0//sun value th */ + { 0x48, 0x03 }, /* sun mode */ + { 0x4f, 0x60 }, /* sun_clamp */ + { 0xfe, 0x00 }, +}; + +static const struct reg_sequence gc0310_VGA_30fps[] = { + { 0xfe, 0x00 }, + { 0x0d, 0x01 }, /* height */ + { 0x0e, 0xf2 }, /* 0xf7 //height */ + { 0x0f, 0x02 }, /* width */ + { 0x10, 0x94 }, /* 0xa0 //height */ + + { 0x50, 0x01 }, /* crop enable */ + { 0x51, 0x00 }, + { 0x52, 0x00 }, + { 0x53, 0x00 }, + { 0x54, 0x01 }, + { 0x55, 0x01 }, /* crop window height */ + { 0x56, 0xf0 }, + { 0x57, 0x02 }, /* crop window width */ + { 0x58, 0x90 }, + + { 0xfe, 0x03 }, + { 0x12, 0x90 }, /* 00 //04 //00 //04//00 //LWC[7:0] */ + { 0x13, 0x02 }, /* 05 //05 //LWC[15:8] */ + + { 0xfe, 0x00 }, +}; + +static const s64 link_freq_menu_items[] = { + GC0310_LINK_FREQ, +}; + +static int gc0310_gain_set(struct gc0310_device *sensor, u32 gain) +{ + u8 again, dgain; + int ret = 0; + + /* Taken from original driver, this never sets dgain lower then 32? */ + + /* Change 0 - 95 to 32 - 127 */ + gain += 32; + + if (gain < 64) { + again = 0x0; /* sqrt(2) */ + dgain = gain; + } else { + again = 0x2; /* 2 * sqrt(2) */ + dgain = gain / 2; + } + + cci_write(sensor->regmap, GC0310_AGC_ADJ_REG, again, &ret); + cci_write(sensor->regmap, GC0310_DGC_ADJ_REG, dgain, &ret); + return ret; +} + +static int gc0310_exposure_update_range(struct gc0310_device *sensor) +{ + int exp_max = GC0310_NATIVE_HEIGHT + sensor->ctrls.vblank->val; + + return __v4l2_ctrl_modify_range(sensor->ctrls.exposure, 0, exp_max, + 1, exp_max); +} + +static int gc0310_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gc0310_device *sensor = + container_of(ctrl->handler, struct gc0310_device, ctrls.handler); + int ret; + + /* Update exposure range on vblank changes */ + if (ctrl->id == V4L2_CID_VBLANK) { + ret = gc0310_exposure_update_range(sensor); + if (ret) + return ret; + } + + /* Only apply changes to the controls if the device is powered up */ + if (!pm_runtime_get_if_in_use(sensor->sd.dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + ret = cci_write(sensor->regmap, GC0310_AEC_PK_EXPO_REG, + ctrl->val, NULL); + break; + case V4L2_CID_ANALOGUE_GAIN: + ret = gc0310_gain_set(sensor, ctrl->val); + break; + case V4L2_CID_VBLANK: + ret = cci_write(sensor->regmap, GC0310_V_BLANKING_REG, + ctrl->val - GC0310_V_BLANK_OFFSET, + NULL); + break; + default: + ret = -EINVAL; + break; + } + + pm_runtime_put(sensor->sd.dev); + return ret; +} + +static const struct v4l2_ctrl_ops ctrl_ops = { + .s_ctrl = gc0310_s_ctrl, +}; + +/* The GC0310 currently only supports 1 fixed fmt */ +static void gc0310_fill_format(struct v4l2_mbus_framefmt *fmt) +{ + memset(fmt, 0, sizeof(*fmt)); + fmt->width = GC0310_NATIVE_WIDTH; + fmt->height = GC0310_NATIVE_HEIGHT; + fmt->field = V4L2_FIELD_NONE; + fmt->code = MEDIA_BUS_FMT_SGRBG8_1X8; +} + +static int gc0310_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + /* Only the single fixed 656x496 mode is supported, without croping */ + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = GC0310_NATIVE_WIDTH; + sel->r.height = GC0310_NATIVE_HEIGHT; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int gc0310_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct gc0310_device *sensor = to_gc0310_sensor(sd); + + gpiod_set_value_cansleep(sensor->powerdown, 1); + gpiod_set_value_cansleep(sensor->reset, 1); + return 0; +} + +static int gc0310_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct gc0310_device *sensor = to_gc0310_sensor(sd); + + fsleep(10 * USEC_PER_MSEC); + gpiod_set_value_cansleep(sensor->reset, 0); + fsleep(10 * USEC_PER_MSEC); + gpiod_set_value_cansleep(sensor->powerdown, 0); + fsleep(10 * USEC_PER_MSEC); + + return 0; +} + +static int gc0310_detect(struct gc0310_device *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd); + u64 val; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + ret = cci_read(sensor->regmap, GC0310_SC_CMMN_CHIP_ID_REG, &val, NULL); + if (ret < 0) { + dev_err(&client->dev, "read sensor_id failed: %d\n", ret); + return -ENODEV; + } + + dev_dbg(&client->dev, "sensor ID = 0x%llx\n", val); + + if (val != GC0310_ID) { + dev_err(&client->dev, "sensor ID error, read id = 0x%llx, target id = 0x%x\n", + val, GC0310_ID); + return -ENODEV; + } + + dev_dbg(&client->dev, "detect gc0310 success\n"); + + return 0; +} + +static int gc0310_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct gc0310_device *sensor = to_gc0310_sensor(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; + + ret = pm_runtime_resume_and_get(&client->dev); + if (ret) + return ret; + + ret = regmap_multi_reg_write(sensor->regmap, + gc0310_reset_register, + ARRAY_SIZE(gc0310_reset_register)); + if (ret) + goto error_power_down; + + ret = regmap_multi_reg_write(sensor->regmap, + gc0310_VGA_30fps, + ARRAY_SIZE(gc0310_VGA_30fps)); + if (ret) + goto error_power_down; + + /* restore value of all ctrls */ + ret = __v4l2_ctrl_handler_setup(&sensor->ctrls.handler); + + /* enable per frame MIPI and sensor ctrl reset */ + cci_write(sensor->regmap, GC0310_RESET_RELATED_REG, 0x30, &ret); + + cci_write(sensor->regmap, GC0310_RESET_RELATED_REG, + GC0310_REGISTER_PAGE_3, &ret); + cci_write(sensor->regmap, GC0310_SW_STREAM_REG, + GC0310_START_STREAMING, &ret); + cci_write(sensor->regmap, GC0310_RESET_RELATED_REG, + GC0310_REGISTER_PAGE_0, &ret); + +error_power_down: + if (ret) + pm_runtime_put(&client->dev); + + return ret; +} + +static int gc0310_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct gc0310_device *sensor = to_gc0310_sensor(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = 0; + + cci_write(sensor->regmap, GC0310_RESET_RELATED_REG, + GC0310_REGISTER_PAGE_3, &ret); + cci_write(sensor->regmap, GC0310_SW_STREAM_REG, + GC0310_STOP_STREAMING, &ret); + cci_write(sensor->regmap, GC0310_RESET_RELATED_REG, + GC0310_REGISTER_PAGE_0, &ret); + + pm_runtime_put(&client->dev); + return ret; +} + +static int gc0310_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + /* We support only a single format */ + if (code->index) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_SGRBG8_1X8; + return 0; +} + +static int gc0310_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + /* We support only a single resolution */ + if (fse->index) + return -EINVAL; + + fse->min_width = GC0310_NATIVE_WIDTH; + fse->max_width = GC0310_NATIVE_WIDTH; + fse->min_height = GC0310_NATIVE_HEIGHT; + fse->max_height = GC0310_NATIVE_HEIGHT; + + return 0; +} + +static const struct v4l2_subdev_video_ops gc0310_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_pad_ops gc0310_pad_ops = { + .enum_mbus_code = gc0310_enum_mbus_code, + .enum_frame_size = gc0310_enum_frame_size, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = v4l2_subdev_get_fmt, /* Only 1 fixed mode supported */ + .get_selection = gc0310_get_selection, + .set_selection = gc0310_get_selection, + .enable_streams = gc0310_enable_streams, + .disable_streams = gc0310_disable_streams, +}; + +static const struct v4l2_subdev_ops gc0310_ops = { + .video = &gc0310_video_ops, + .pad = &gc0310_pad_ops, +}; + +static int gc0310_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + gc0310_fill_format(v4l2_subdev_state_get_format(sd_state, 0)); + return 0; +} + +static const struct v4l2_subdev_internal_ops gc0310_internal_ops = { + .init_state = gc0310_init_state, +}; + +static int gc0310_init_controls(struct gc0310_device *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd); + struct v4l2_ctrl_handler *hdl = &sensor->ctrls.handler; + struct v4l2_fwnode_device_properties props; + int exp_max, ret; + + v4l2_ctrl_handler_init(hdl, 8); + + /* Use the same lock for controls as for everything else */ + sensor->sd.ctrl_handler = hdl; + + exp_max = GC0310_NATIVE_HEIGHT + GC0310_V_BLANK_DEFAULT; + sensor->ctrls.exposure = + v4l2_ctrl_new_std(hdl, &ctrl_ops, V4L2_CID_EXPOSURE, 0, + exp_max, 1, exp_max); + + /* 32 steps at base gain 1 + 64 half steps at base gain 2 */ + sensor->ctrls.gain = + v4l2_ctrl_new_std(hdl, &ctrl_ops, V4L2_CID_ANALOGUE_GAIN, 0, 95, 1, 31); + + sensor->ctrls.link_freq = + v4l2_ctrl_new_int_menu(hdl, NULL, V4L2_CID_LINK_FREQ, + 0, 0, link_freq_menu_items); + sensor->ctrls.pixel_rate = + v4l2_ctrl_new_std(hdl, NULL, V4L2_CID_PIXEL_RATE, 0, + GC0310_PIXELRATE, 1, GC0310_PIXELRATE); + + sensor->ctrls.vblank = + v4l2_ctrl_new_std(hdl, &ctrl_ops, V4L2_CID_VBLANK, + GC0310_V_BLANK_MIN, + GC0310_V_BLANK_MAX, 1, + GC0310_V_BLANK_DEFAULT); + + sensor->ctrls.hblank = + v4l2_ctrl_new_std(hdl, NULL, V4L2_CID_HBLANK, + GC0310_H_BLANK_DEFAULT, + GC0310_H_BLANK_DEFAULT, 1, + GC0310_H_BLANK_DEFAULT); + + ret = v4l2_fwnode_device_parse(&client->dev, &props); + if (ret) + return ret; + + v4l2_ctrl_new_fwnode_properties(hdl, &ctrl_ops, &props); + + if (hdl->error) + return hdl->error; + + sensor->ctrls.pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY; + sensor->ctrls.link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + sensor->ctrls.hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + return 0; +} + +static void gc0310_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc0310_device *sensor = to_gc0310_sensor(sd); + + v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(sd); + media_entity_cleanup(&sensor->sd.entity); + v4l2_ctrl_handler_free(&sensor->ctrls.handler); + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) { + gc0310_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); + } +} + +static int gc0310_check_hwcfg(struct device *dev) +{ + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; + struct fwnode_handle *ep_fwnode; + unsigned long link_freq_bitmap; + u32 mclk; + int ret; + + /* + * Sometimes the fwnode graph is initialized by the bridge driver. + * Bridge drivers doing this may also add GPIO mappings, wait for this. + */ + ep_fwnode = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0); + if (!ep_fwnode) + return dev_err_probe(dev, -EPROBE_DEFER, + "waiting for fwnode graph endpoint\n"); + + ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", + &mclk); + if (ret) { + fwnode_handle_put(ep_fwnode); + return dev_err_probe(dev, ret, + "reading clock-frequency property\n"); + } + + if (mclk != GC0310_MCLK_FREQ) { + fwnode_handle_put(ep_fwnode); + return dev_err_probe(dev, -EINVAL, + "external clock %u is not supported\n", + mclk); + } + + ret = v4l2_fwnode_endpoint_alloc_parse(ep_fwnode, &bus_cfg); + fwnode_handle_put(ep_fwnode); + if (ret) + return dev_err_probe(dev, ret, "parsing endpoint failed\n"); + + ret = v4l2_link_freq_to_bitmap(dev, bus_cfg.link_frequencies, + bus_cfg.nr_of_link_frequencies, + link_freq_menu_items, + ARRAY_SIZE(link_freq_menu_items), + &link_freq_bitmap); + + if (ret == 0 && bus_cfg.bus.mipi_csi2.num_data_lanes != 1) + ret = dev_err_probe(dev, -EINVAL, + "number of CSI2 data lanes %u is not supported\n", + bus_cfg.bus.mipi_csi2.num_data_lanes); + + v4l2_fwnode_endpoint_free(&bus_cfg); + return ret; +} + +static int gc0310_probe(struct i2c_client *client) +{ + struct gc0310_device *sensor; + int ret; + + ret = gc0310_check_hwcfg(&client->dev); + if (ret) + return ret; + + sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL); + if (!sensor) + return -ENOMEM; + + sensor->reset = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(sensor->reset)) { + return dev_err_probe(&client->dev, PTR_ERR(sensor->reset), + "getting reset GPIO\n"); + } + + sensor->powerdown = devm_gpiod_get(&client->dev, "powerdown", GPIOD_OUT_HIGH); + if (IS_ERR(sensor->powerdown)) { + return dev_err_probe(&client->dev, PTR_ERR(sensor->powerdown), + "getting powerdown GPIO\n"); + } + + v4l2_i2c_subdev_init(&sensor->sd, client, &gc0310_ops); + + sensor->regmap = devm_cci_regmap_init_i2c(client, 8); + if (IS_ERR(sensor->regmap)) + return PTR_ERR(sensor->regmap); + + gc0310_power_on(&client->dev); + + pm_runtime_set_active(&client->dev); + pm_runtime_get_noresume(&client->dev); + pm_runtime_enable(&client->dev); + + ret = gc0310_detect(sensor); + if (ret) + goto err_power_down; + + sensor->sd.internal_ops = &gc0310_internal_ops; + sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sensor->pad.flags = MEDIA_PAD_FL_SOURCE; + sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + ret = gc0310_init_controls(sensor); + if (ret) + goto err_power_down; + + ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad); + if (ret) + goto err_power_down; + + sensor->sd.state_lock = sensor->ctrls.handler.lock; + ret = v4l2_subdev_init_finalize(&sensor->sd); + if (ret) + goto err_power_down; + + ret = v4l2_async_register_subdev_sensor(&sensor->sd); + if (ret) + goto err_power_down; + + pm_runtime_set_autosuspend_delay(&client->dev, 1000); + pm_runtime_use_autosuspend(&client->dev); + pm_runtime_put_autosuspend(&client->dev); + + return 0; + +err_power_down: + pm_runtime_put_noidle(&client->dev); + gc0310_remove(client); + return ret; +} + +static DEFINE_RUNTIME_DEV_PM_OPS(gc0310_pm_ops, + gc0310_power_off, gc0310_power_on, NULL); + +static const struct acpi_device_id gc0310_acpi_match[] = { + {"INT0310"}, + {}, +}; +MODULE_DEVICE_TABLE(acpi, gc0310_acpi_match); + +static struct i2c_driver gc0310_driver = { + .driver = { + .name = "gc0310", + .pm = pm_sleep_ptr(&gc0310_pm_ops), + .acpi_match_table = gc0310_acpi_match, + }, + .probe = gc0310_probe, + .remove = gc0310_remove, +}; +module_i2c_driver(gc0310_driver); + +MODULE_AUTHOR("Lai, Angie <angie.lai@intel.com>"); +MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>"); +MODULE_DESCRIPTION("A low-level driver for GalaxyCore GC0310 sensors"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/gc05a2.c b/drivers/media/i2c/gc05a2.c index 3f7f3d5abeeb..8ba17f80fffe 100644 --- a/drivers/media/i2c/gc05a2.c +++ b/drivers/media/i2c/gc05a2.c @@ -1235,16 +1235,12 @@ static int gc05a2_probe(struct i2c_client *client) return dev_err_probe(dev, PTR_ERR(gc05a2->regmap), "failed to init CCI\n"); - gc05a2->xclk = devm_clk_get(dev, NULL); + gc05a2->xclk = devm_v4l2_sensor_clk_get_legacy(dev, NULL, true, + GC05A2_DEFAULT_CLK_FREQ); if (IS_ERR(gc05a2->xclk)) return dev_err_probe(dev, PTR_ERR(gc05a2->xclk), "failed to get xclk\n"); - ret = clk_set_rate(gc05a2->xclk, GC05A2_DEFAULT_CLK_FREQ); - if (ret) - return dev_err_probe(dev, ret, - "failed to set xclk frequency\n"); - ret = gc05a2_get_regulators(dev, gc05a2); if (ret < 0) return dev_err_probe(dev, ret, diff --git a/drivers/media/i2c/gc08a3.c b/drivers/media/i2c/gc08a3.c index 938709a677b6..11fd936db9c3 100644 --- a/drivers/media/i2c/gc08a3.c +++ b/drivers/media/i2c/gc08a3.c @@ -1199,16 +1199,12 @@ static int gc08a3_probe(struct i2c_client *client) return dev_err_probe(dev, PTR_ERR(gc08a3->regmap), "failed to init CCI\n"); - gc08a3->xclk = devm_clk_get(dev, NULL); + gc08a3->xclk = devm_v4l2_sensor_clk_get_legacy(dev, NULL, true, + GC08A3_DEFAULT_CLK_FREQ); if (IS_ERR(gc08a3->xclk)) return dev_err_probe(dev, PTR_ERR(gc08a3->xclk), "failed to get xclk\n"); - ret = clk_set_rate(gc08a3->xclk, GC08A3_DEFAULT_CLK_FREQ); - if (ret) - return dev_err_probe(dev, ret, - "failed to set xclk frequency\n"); - ret = gc08a3_get_regulators(dev, gc08a3); if (ret < 0) return dev_err_probe(dev, ret, diff --git a/drivers/media/i2c/gc2145.c b/drivers/media/i2c/gc2145.c index ba02161d46e7..b215963a2648 100644 --- a/drivers/media/i2c/gc2145.c +++ b/drivers/media/i2c/gc2145.c @@ -963,7 +963,6 @@ static int gc2145_enable_streams(struct v4l2_subdev *sd, return 0; err_rpm_put: - pm_runtime_mark_last_busy(&client->dev); pm_runtime_put_autosuspend(&client->dev); return ret; } @@ -985,7 +984,6 @@ static int gc2145_disable_streams(struct v4l2_subdev *sd, if (ret) dev_err(&client->dev, "%s failed to write regs\n", __func__); - pm_runtime_mark_last_busy(&client->dev); pm_runtime_put_autosuspend(&client->dev); return ret; @@ -1193,7 +1191,6 @@ static int gc2145_s_ctrl(struct v4l2_ctrl *ctrl) break; } - pm_runtime_mark_last_busy(&client->dev); pm_runtime_put_autosuspend(&client->dev); return ret; @@ -1334,7 +1331,7 @@ static int gc2145_probe(struct i2c_client *client) return -EINVAL; /* Get system clock (xclk) */ - gc2145->xclk = devm_clk_get(dev, NULL); + gc2145->xclk = devm_v4l2_sensor_clk_get(dev, NULL); if (IS_ERR(gc2145->xclk)) return dev_err_probe(dev, PTR_ERR(gc2145->xclk), "failed to get xclk\n"); diff --git a/drivers/media/i2c/hi556.c b/drivers/media/i2c/hi556.c index 3ac42d1ab8b4..de573cee4451 100644 --- a/drivers/media/i2c/hi556.c +++ b/drivers/media/i2c/hi556.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2019 Intel Corporation. -#include <linux/unaligned.h> #include <linux/acpi.h> #include <linux/clk.h> #include <linux/delay.h> @@ -10,6 +9,8 @@ #include <linux/module.h> #include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> +#include <linux/unaligned.h> + #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-fwnode.h> @@ -624,7 +625,15 @@ static const struct hi556_mode supported_modes[] = { }, }; +static const char * const hi556_supply_names[] = { + "dovdd", /* Digital I/O power */ + "avdd", /* Analog power */ + "dvdd", /* Digital core power */ +}; + struct hi556 { + struct device *dev; + struct v4l2_subdev sd; struct media_pad pad; struct v4l2_ctrl_handler ctrl_handler; @@ -639,7 +648,7 @@ struct hi556 { /* GPIOs, clocks, etc. */ struct gpio_desc *reset_gpio; struct clk *clk; - struct regulator *avdd; + struct regulator_bulk_data supplies[ARRAY_SIZE(hi556_supply_names)]; /* Current mode */ const struct hi556_mode *cur_mode; @@ -709,7 +718,6 @@ static int hi556_write_reg(struct hi556 *hi556, u16 reg, u16 len, u32 val) static int hi556_write_reg_list(struct hi556 *hi556, const struct hi556_reg_list *r_list) { - struct i2c_client *client = v4l2_get_subdevdata(&hi556->sd); unsigned int i; int ret; @@ -718,8 +726,8 @@ static int hi556_write_reg_list(struct hi556 *hi556, HI556_REG_VALUE_16BIT, r_list->regs[i].val); if (ret) { - dev_err_ratelimited(&client->dev, - "failed to write reg 0x%4.4x. error = %d", + dev_err_ratelimited(hi556->dev, + "failed to write reg 0x%4.4x. error = %d\n", r_list->regs[i].address, ret); return ret; } @@ -756,28 +764,29 @@ static int hi556_test_pattern(struct hi556 *hi556, u32 pattern) int ret; u32 val; - if (pattern) { - ret = hi556_read_reg(hi556, HI556_REG_ISP, - HI556_REG_VALUE_08BIT, &val); - if (ret) - return ret; + ret = hi556_read_reg(hi556, HI556_REG_ISP, + HI556_REG_VALUE_08BIT, &val); + if (ret) + return ret; - ret = hi556_write_reg(hi556, HI556_REG_ISP, - HI556_REG_VALUE_08BIT, - val | HI556_REG_ISP_TPG_EN); - if (ret) - return ret; - } + val = pattern ? (val | HI556_REG_ISP_TPG_EN) : + (val & ~HI556_REG_ISP_TPG_EN); + + ret = hi556_write_reg(hi556, HI556_REG_ISP, + HI556_REG_VALUE_08BIT, val); + if (ret) + return ret; + + val = pattern ? BIT(pattern - 1) : 0; return hi556_write_reg(hi556, HI556_REG_TEST_PATTERN, - HI556_REG_VALUE_08BIT, pattern); + HI556_REG_VALUE_08BIT, val); } static int hi556_set_ctrl(struct v4l2_ctrl *ctrl) { struct hi556 *hi556 = container_of(ctrl->handler, struct hi556, ctrl_handler); - struct i2c_client *client = v4l2_get_subdevdata(&hi556->sd); s64 exposure_max; int ret = 0; @@ -793,7 +802,7 @@ static int hi556_set_ctrl(struct v4l2_ctrl *ctrl) } /* V4L2 controls values will be applied only when power is already up */ - if (!pm_runtime_get_if_in_use(&client->dev)) + if (!pm_runtime_get_if_in_use(hi556->dev)) return 0; switch (ctrl->id) { @@ -827,7 +836,7 @@ static int hi556_set_ctrl(struct v4l2_ctrl *ctrl) break; } - pm_runtime_put(&client->dev); + pm_runtime_put(hi556->dev); return ret; } @@ -913,7 +922,6 @@ static void hi556_assign_pad_format(const struct hi556_mode *mode, static int hi556_identify_module(struct hi556 *hi556) { - struct i2c_client *client = v4l2_get_subdevdata(&hi556->sd); int ret; u32 val; @@ -926,7 +934,7 @@ static int hi556_identify_module(struct hi556 *hi556) return ret; if (val != HI556_CHIP_ID) { - dev_err(&client->dev, "chip id mismatch: %x!=%x", + dev_err(hi556->dev, "chip id mismatch: %x!=%x\n", HI556_CHIP_ID, val); return -ENXIO; } @@ -990,7 +998,6 @@ static int hi556_get_selection(struct v4l2_subdev *sd, static int hi556_start_streaming(struct hi556 *hi556) { - struct i2c_client *client = v4l2_get_subdevdata(&hi556->sd); const struct hi556_reg_list *reg_list; int link_freq_index, ret; @@ -1002,14 +1009,14 @@ static int hi556_start_streaming(struct hi556 *hi556) reg_list = &link_freq_configs[link_freq_index].reg_list; ret = hi556_write_reg_list(hi556, reg_list); if (ret) { - dev_err(&client->dev, "failed to set plls"); + dev_err(hi556->dev, "failed to set plls\n"); return ret; } reg_list = &hi556->cur_mode->reg_list; ret = hi556_write_reg_list(hi556, reg_list); if (ret) { - dev_err(&client->dev, "failed to set mode"); + dev_err(hi556->dev, "failed to set mode\n"); return ret; } @@ -1021,7 +1028,7 @@ static int hi556_start_streaming(struct hi556 *hi556) HI556_REG_VALUE_16BIT, HI556_MODE_STREAMING); if (ret) { - dev_err(&client->dev, "failed to set stream"); + dev_err(hi556->dev, "failed to set stream\n"); return ret; } @@ -1030,22 +1037,19 @@ static int hi556_start_streaming(struct hi556 *hi556) static void hi556_stop_streaming(struct hi556 *hi556) { - struct i2c_client *client = v4l2_get_subdevdata(&hi556->sd); - if (hi556_write_reg(hi556, HI556_REG_MODE_SELECT, HI556_REG_VALUE_16BIT, HI556_MODE_STANDBY)) - dev_err(&client->dev, "failed to set stream"); + dev_err(hi556->dev, "failed to set stream\n"); } static int hi556_set_stream(struct v4l2_subdev *sd, int enable) { struct hi556 *hi556 = to_hi556(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); int ret = 0; mutex_lock(&hi556->mutex); if (enable) { - ret = pm_runtime_resume_and_get(&client->dev); + ret = pm_runtime_resume_and_get(hi556->dev); if (ret < 0) { mutex_unlock(&hi556->mutex); return ret; @@ -1053,13 +1057,12 @@ static int hi556_set_stream(struct v4l2_subdev *sd, int enable) ret = hi556_start_streaming(hi556); if (ret) { - enable = 0; hi556_stop_streaming(hi556); - pm_runtime_put(&client->dev); + pm_runtime_put(hi556->dev); } } else { hi556_stop_streaming(hi556); - pm_runtime_put(&client->dev); + pm_runtime_put(hi556->dev); } mutex_unlock(&hi556->mutex); @@ -1210,7 +1213,6 @@ static int hi556_check_hwcfg(struct device *dev) struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = V4L2_MBUS_CSI2_DPHY }; - u32 mclk; int ret = 0; unsigned int i, j; @@ -1220,33 +1222,23 @@ static int hi556_check_hwcfg(struct device *dev) */ ep = fwnode_graph_get_next_endpoint(fwnode, NULL); if (!ep) - return -EPROBE_DEFER; + return dev_err_probe(dev, -EPROBE_DEFER, + "waiting for fwnode graph endpoint\n"); ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); fwnode_handle_put(ep); if (ret) - return ret; - - ret = fwnode_property_read_u32(fwnode, "clock-frequency", &mclk); - if (ret) { - dev_err(dev, "can't get clock frequency"); - return ret; - } - - if (mclk != HI556_MCLK) { - dev_err(dev, "external clock %d is not supported", mclk); - return -EINVAL; - } + return dev_err_probe(dev, ret, "parsing endpoint failed\n"); if (bus_cfg.bus.mipi_csi2.num_data_lanes != 2) { - dev_err(dev, "number of CSI2 data lanes %d is not supported", + dev_err(dev, "number of CSI2 data lanes %d is not supported\n", bus_cfg.bus.mipi_csi2.num_data_lanes); ret = -EINVAL; goto check_hwcfg_error; } if (!bus_cfg.nr_of_link_frequencies) { - dev_err(dev, "no link frequencies defined"); + dev_err(dev, "no link frequencies defined\n"); ret = -EINVAL; goto check_hwcfg_error; } @@ -1259,7 +1251,7 @@ static int hi556_check_hwcfg(struct device *dev) } if (j == bus_cfg.nr_of_link_frequencies) { - dev_err(dev, "no link frequency %lld supported", + dev_err(dev, "no link frequency %lld supported\n", link_freq_menu_items[i]); ret = -EINVAL; goto check_hwcfg_error; @@ -1280,7 +1272,7 @@ static void hi556_remove(struct i2c_client *client) v4l2_async_unregister_subdev(sd); media_entity_cleanup(&sd->entity); v4l2_ctrl_handler_free(sd->ctrl_handler); - pm_runtime_disable(&client->dev); + pm_runtime_disable(hi556->dev); mutex_destroy(&hi556->mutex); } @@ -1288,17 +1280,10 @@ static int hi556_suspend(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); struct hi556 *hi556 = to_hi556(sd); - int ret; gpiod_set_value_cansleep(hi556->reset_gpio, 1); - - ret = regulator_disable(hi556->avdd); - if (ret) { - dev_err(dev, "failed to disable avdd: %d\n", ret); - gpiod_set_value_cansleep(hi556->reset_gpio, 0); - return ret; - } - + regulator_bulk_disable(ARRAY_SIZE(hi556_supply_names), + hi556->supplies); clk_disable_unprepare(hi556->clk); return 0; } @@ -1313,14 +1298,20 @@ static int hi556_resume(struct device *dev) if (ret) return ret; - ret = regulator_enable(hi556->avdd); + ret = regulator_bulk_enable(ARRAY_SIZE(hi556_supply_names), + hi556->supplies); if (ret) { - dev_err(dev, "failed to enable avdd: %d\n", ret); + dev_err(dev, "failed to enable regulators: %d", ret); clk_disable_unprepare(hi556->clk); return ret; } - gpiod_set_value_cansleep(hi556->reset_gpio, 0); + if (hi556->reset_gpio) { + /* Assert reset for at least 2ms on back to back off-on */ + usleep_range(2000, 2200); + gpiod_set_value_cansleep(hi556->reset_gpio, 0); + } + usleep_range(5000, 5500); return 0; } @@ -1328,50 +1319,60 @@ static int hi556_resume(struct device *dev) static int hi556_probe(struct i2c_client *client) { struct hi556 *hi556; + unsigned long freq; bool full_power; - int ret; + int i, ret; ret = hi556_check_hwcfg(&client->dev); - if (ret) { - dev_err(&client->dev, "failed to check HW configuration: %d", - ret); + if (ret) return ret; - } hi556 = devm_kzalloc(&client->dev, sizeof(*hi556), GFP_KERNEL); if (!hi556) return -ENOMEM; + hi556->dev = &client->dev; + v4l2_i2c_subdev_init(&hi556->sd, client, &hi556_subdev_ops); - hi556->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", + hi556->reset_gpio = devm_gpiod_get_optional(hi556->dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(hi556->reset_gpio)) - return dev_err_probe(&client->dev, PTR_ERR(hi556->reset_gpio), + return dev_err_probe(hi556->dev, PTR_ERR(hi556->reset_gpio), "failed to get reset GPIO\n"); - hi556->clk = devm_clk_get_optional(&client->dev, "clk"); + hi556->clk = devm_v4l2_sensor_clk_get(hi556->dev, "clk"); if (IS_ERR(hi556->clk)) - return dev_err_probe(&client->dev, PTR_ERR(hi556->clk), + return dev_err_probe(hi556->dev, PTR_ERR(hi556->clk), "failed to get clock\n"); - /* The regulator core will provide a "dummy" regulator if necessary */ - hi556->avdd = devm_regulator_get(&client->dev, "avdd"); - if (IS_ERR(hi556->avdd)) - return dev_err_probe(&client->dev, PTR_ERR(hi556->avdd), - "failed to get avdd regulator\n"); + freq = clk_get_rate(hi556->clk); + if (freq != HI556_MCLK) + return dev_err_probe(hi556->dev, -EINVAL, + "external clock %lu is not supported\n", + freq); + + for (i = 0; i < ARRAY_SIZE(hi556_supply_names); i++) + hi556->supplies[i].supply = hi556_supply_names[i]; + + ret = devm_regulator_bulk_get(hi556->dev, + ARRAY_SIZE(hi556_supply_names), + hi556->supplies); + if (ret) + return dev_err_probe(hi556->dev, ret, + "failed to get regulators\n"); - full_power = acpi_dev_state_d0(&client->dev); + full_power = acpi_dev_state_d0(hi556->dev); if (full_power) { /* Ensure non ACPI managed resources are enabled */ - ret = hi556_resume(&client->dev); + ret = hi556_resume(hi556->dev); if (ret) - return dev_err_probe(&client->dev, ret, + return dev_err_probe(hi556->dev, ret, "failed to power on sensor\n"); ret = hi556_identify_module(hi556); if (ret) { - dev_err(&client->dev, "failed to find sensor: %d", ret); + dev_err(hi556->dev, "failed to find sensor: %d\n", ret); goto probe_error_power_off; } } @@ -1380,7 +1381,7 @@ static int hi556_probe(struct i2c_client *client) hi556->cur_mode = &supported_modes[0]; ret = hi556_init_controls(hi556); if (ret) { - dev_err(&client->dev, "failed to init controls: %d", ret); + dev_err(hi556->dev, "failed to init controls: %d\n", ret); goto probe_error_v4l2_ctrl_handler_free; } @@ -1391,22 +1392,22 @@ static int hi556_probe(struct i2c_client *client) hi556->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&hi556->sd.entity, 1, &hi556->pad); if (ret) { - dev_err(&client->dev, "failed to init entity pads: %d", ret); + dev_err(hi556->dev, "failed to init entity pads: %d\n", ret); goto probe_error_v4l2_ctrl_handler_free; } ret = v4l2_async_register_subdev_sensor(&hi556->sd); if (ret < 0) { - dev_err(&client->dev, "failed to register V4L2 subdev: %d", + dev_err(hi556->dev, "failed to register V4L2 subdev: %d\n", ret); goto probe_error_media_entity_cleanup; } /* Set the device's state to active if it's in D0 state. */ if (full_power) - pm_runtime_set_active(&client->dev); - pm_runtime_enable(&client->dev); - pm_runtime_idle(&client->dev); + pm_runtime_set_active(hi556->dev); + pm_runtime_enable(hi556->dev); + pm_runtime_idle(hi556->dev); return 0; @@ -1419,7 +1420,7 @@ probe_error_v4l2_ctrl_handler_free: probe_error_power_off: if (full_power) - hi556_suspend(&client->dev); + hi556_suspend(hi556->dev); return ret; } diff --git a/drivers/media/i2c/hi846.c b/drivers/media/i2c/hi846.c index 172772decd3d..a3f77b8434ca 100644 --- a/drivers/media/i2c/hi846.c +++ b/drivers/media/i2c/hi846.c @@ -2052,12 +2052,11 @@ static int hi846_probe(struct i2c_client *client) return ret; } - hi846->clock = devm_clk_get(&client->dev, NULL); - if (IS_ERR(hi846->clock)) { - dev_err(&client->dev, "failed to get clock: %pe\n", - hi846->clock); - return PTR_ERR(hi846->clock); - } + hi846->clock = devm_v4l2_sensor_clk_get(&client->dev, NULL); + if (IS_ERR(hi846->clock)) + return dev_err_probe(&client->dev, PTR_ERR(hi846->clock), + "failed to get clock: %pe\n", + hi846->clock); mclk_freq = clk_get_rate(hi846->clock); if (mclk_freq != 25000000) diff --git a/drivers/media/i2c/hi847.c b/drivers/media/i2c/hi847.c index 546833f5b5f5..def01aa07b2f 100644 --- a/drivers/media/i2c/hi847.c +++ b/drivers/media/i2c/hi847.c @@ -1,12 +1,14 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2022 Intel Corporation. -#include <linux/unaligned.h> #include <linux/acpi.h> +#include <linux/clk.h> #include <linux/delay.h> #include <linux/i2c.h> #include <linux/module.h> #include <linux/pm_runtime.h> +#include <linux/unaligned.h> + #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-fwnode.h> @@ -2166,6 +2168,9 @@ static const struct hi847_mode supported_modes[] = { }; struct hi847 { + struct device *dev; + struct clk *clk; + struct v4l2_subdev sd; struct media_pad pad; struct v4l2_ctrl_handler ctrl_handler; @@ -2244,7 +2249,6 @@ static int hi847_write_reg(struct hi847 *hi847, u16 reg, u16 len, u32 val) static int hi847_write_reg_list(struct hi847 *hi847, const struct hi847_reg_list *r_list) { - struct i2c_client *client = v4l2_get_subdevdata(&hi847->sd); unsigned int i; int ret; @@ -2253,7 +2257,7 @@ static int hi847_write_reg_list(struct hi847 *hi847, HI847_REG_VALUE_16BIT, r_list->regs[i].val); if (ret) { - dev_err_ratelimited(&client->dev, + dev_err_ratelimited(hi847->dev, "failed to write reg 0x%4.4x. error = %d", r_list->regs[i].address, ret); return ret; @@ -2408,7 +2412,6 @@ static int hi847_set_ctrl(struct v4l2_ctrl *ctrl) { struct hi847 *hi847 = container_of(ctrl->handler, struct hi847, ctrl_handler); - struct i2c_client *client = v4l2_get_subdevdata(&hi847->sd); s64 exposure_max; int ret = 0; @@ -2424,7 +2427,7 @@ static int hi847_set_ctrl(struct v4l2_ctrl *ctrl) } /* V4L2 controls values will be applied only when power is already up */ - if (!pm_runtime_get_if_in_use(&client->dev)) + if (!pm_runtime_get_if_in_use(hi847->dev)) return 0; switch (ctrl->id) { @@ -2466,7 +2469,7 @@ static int hi847_set_ctrl(struct v4l2_ctrl *ctrl) break; } - pm_runtime_put(&client->dev); + pm_runtime_put(hi847->dev); return ret; } @@ -2557,7 +2560,6 @@ static void hi847_assign_pad_format(const struct hi847_mode *mode, static int hi847_start_streaming(struct hi847 *hi847) { - struct i2c_client *client = v4l2_get_subdevdata(&hi847->sd); const struct hi847_reg_list *reg_list; int link_freq_index, ret; @@ -2565,14 +2567,14 @@ static int hi847_start_streaming(struct hi847 *hi847) reg_list = &link_freq_configs[link_freq_index].reg_list; ret = hi847_write_reg_list(hi847, reg_list); if (ret) { - dev_err(&client->dev, "failed to set plls"); + dev_err(hi847->dev, "failed to set plls"); return ret; } reg_list = &hi847->cur_mode->reg_list; ret = hi847_write_reg_list(hi847, reg_list); if (ret) { - dev_err(&client->dev, "failed to set mode"); + dev_err(hi847->dev, "failed to set mode"); return ret; } @@ -2587,7 +2589,7 @@ static int hi847_start_streaming(struct hi847 *hi847) HI847_REG_VALUE_16BIT, HI847_MODE_STREAMING); if (ret) { - dev_err(&client->dev, "failed to set stream"); + dev_err(hi847->dev, "failed to set stream"); return ret; } @@ -2596,28 +2598,25 @@ static int hi847_start_streaming(struct hi847 *hi847) static void hi847_stop_streaming(struct hi847 *hi847) { - struct i2c_client *client = v4l2_get_subdevdata(&hi847->sd); - if (hi847_write_reg(hi847, HI847_REG_MODE_TG, HI847_REG_VALUE_16BIT, HI847_REG_MODE_TG_DISABLE)) - dev_err(&client->dev, "failed to set stream 0x%x", + dev_err(hi847->dev, "failed to set stream 0x%x", HI847_REG_MODE_TG); if (hi847_write_reg(hi847, HI847_REG_MODE_SELECT, HI847_REG_VALUE_16BIT, HI847_MODE_STANDBY)) - dev_err(&client->dev, "failed to set stream 0x%x", + dev_err(hi847->dev, "failed to set stream 0x%x", HI847_REG_MODE_SELECT); } static int hi847_set_stream(struct v4l2_subdev *sd, int enable) { struct hi847 *hi847 = to_hi847(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); int ret = 0; mutex_lock(&hi847->mutex); if (enable) { - ret = pm_runtime_resume_and_get(&client->dev); + ret = pm_runtime_resume_and_get(hi847->dev); if (ret) { mutex_unlock(&hi847->mutex); return ret; @@ -2627,11 +2626,11 @@ static int hi847_set_stream(struct v4l2_subdev *sd, int enable) if (ret) { enable = 0; hi847_stop_streaming(hi847); - pm_runtime_put(&client->dev); + pm_runtime_put(hi847->dev); } } else { hi847_stop_streaming(hi847); - pm_runtime_put(&client->dev); + pm_runtime_put(hi847->dev); } mutex_unlock(&hi847->mutex); @@ -2768,7 +2767,6 @@ static const struct v4l2_subdev_internal_ops hi847_internal_ops = { static int hi847_identify_module(struct hi847 *hi847) { - struct i2c_client *client = v4l2_get_subdevdata(&hi847->sd); int ret; u32 val; @@ -2778,7 +2776,7 @@ static int hi847_identify_module(struct hi847 *hi847) return ret; if (val != HI847_CHIP_ID) { - dev_err(&client->dev, "chip id mismatch: %x!=%x", + dev_err(hi847->dev, "chip id mismatch: %x!=%x", HI847_CHIP_ID, val); return -ENXIO; } @@ -2793,24 +2791,12 @@ static int hi847_check_hwcfg(struct device *dev) struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = V4L2_MBUS_CSI2_DPHY }; - u32 mclk; int ret; unsigned int i, j; if (!fwnode) return -ENXIO; - ret = fwnode_property_read_u32(fwnode, "clock-frequency", &mclk); - if (ret) { - dev_err(dev, "can't get clock frequency"); - return ret; - } - - if (mclk != HI847_MCLK) { - dev_err(dev, "external clock %d is not supported", mclk); - return -EINVAL; - } - ep = fwnode_graph_get_next_endpoint(fwnode, NULL); if (!ep) return -ENXIO; @@ -2862,22 +2848,36 @@ static void hi847_remove(struct i2c_client *client) v4l2_async_unregister_subdev(sd); media_entity_cleanup(&sd->entity); v4l2_ctrl_handler_free(sd->ctrl_handler); - pm_runtime_disable(&client->dev); + pm_runtime_disable(hi847->dev); mutex_destroy(&hi847->mutex); } static int hi847_probe(struct i2c_client *client) { struct hi847 *hi847; + unsigned long freq; int ret; hi847 = devm_kzalloc(&client->dev, sizeof(*hi847), GFP_KERNEL); if (!hi847) return -ENOMEM; - ret = hi847_check_hwcfg(&client->dev); + hi847->dev = &client->dev; + + hi847->clk = devm_v4l2_sensor_clk_get(hi847->dev, NULL); + if (IS_ERR(hi847->clk)) + return dev_err_probe(hi847->dev, PTR_ERR(hi847->clk), + "failed to get clock\n"); + + freq = clk_get_rate(hi847->clk); + if (freq != HI847_MCLK) + return dev_err_probe(hi847->dev, -EINVAL, + "external clock %lu is not supported\n", + freq); + + ret = hi847_check_hwcfg(hi847->dev); if (ret) { - dev_err(&client->dev, "failed to get HW configuration: %d", + dev_err(hi847->dev, "failed to get HW configuration: %d", ret); return ret; } @@ -2885,7 +2885,7 @@ static int hi847_probe(struct i2c_client *client) v4l2_i2c_subdev_init(&hi847->sd, client, &hi847_subdev_ops); ret = hi847_identify_module(hi847); if (ret) { - dev_err(&client->dev, "failed to find sensor: %d", ret); + dev_err(hi847->dev, "failed to find sensor: %d", ret); return ret; } @@ -2893,7 +2893,7 @@ static int hi847_probe(struct i2c_client *client) hi847->cur_mode = &supported_modes[0]; ret = hi847_init_controls(hi847); if (ret) { - dev_err(&client->dev, "failed to init controls: %d", ret); + dev_err(hi847->dev, "failed to init controls: %d", ret); goto probe_error_v4l2_ctrl_handler_free; } @@ -2904,20 +2904,20 @@ static int hi847_probe(struct i2c_client *client) hi847->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&hi847->sd.entity, 1, &hi847->pad); if (ret) { - dev_err(&client->dev, "failed to init entity pads: %d", ret); + dev_err(hi847->dev, "failed to init entity pads: %d", ret); goto probe_error_v4l2_ctrl_handler_free; } ret = v4l2_async_register_subdev_sensor(&hi847->sd); if (ret < 0) { - dev_err(&client->dev, "failed to register V4L2 subdev: %d", + dev_err(hi847->dev, "failed to register V4L2 subdev: %d", ret); goto probe_error_media_entity_cleanup; } - pm_runtime_set_active(&client->dev); - pm_runtime_enable(&client->dev); - pm_runtime_idle(&client->dev); + pm_runtime_set_active(hi847->dev); + pm_runtime_enable(hi847->dev); + pm_runtime_idle(hi847->dev); return 0; diff --git a/drivers/media/i2c/imx111.c b/drivers/media/i2c/imx111.c new file mode 100644 index 000000000000..8eb919788ef7 --- /dev/null +++ b/drivers/media/i2c/imx111.c @@ -0,0 +1,1610 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/media.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/types.h> +#include <linux/videodev2.h> +#include <linux/units.h> + +#include <media/media-entity.h> +#include <media/v4l2-async.h> +#include <media/v4l2-cci.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-subdev.h> +#include <media/v4l2-mediabus.h> + +/* product information registers */ +#define IMX111_PRODUCT_ID CCI_REG16(0x0000) +#define IMX111_CHIP_ID 0x111 +#define IMX111_REVISION CCI_REG8(0x0002) +#define IMX111_MANUFACTURER_ID CCI_REG8(0x0003) +#define IMX111_FRAME_COUNTER CCI_REG8(0x0005) +#define IMX111_PIXEL_ORDER CCI_REG8(0x0006) + +/* general configuration registers */ +#define IMX111_STREAMING_MODE CCI_REG8(0x0100) +#define IMX111_MODE_STANDBY 0 +#define IMX111_MODE_STREAMING 1 +#define IMX111_IMAGE_ORIENTATION CCI_REG8(0x0101) +#define IMX111_IMAGE_HFLIP BIT(0) +#define IMX111_IMAGE_VFLIP BIT(1) +#define IMX111_SOFTWARE_RESET CCI_REG8(0x0103) +#define IMX111_RESET_ON 1 +#define IMX111_GROUP_WRITE CCI_REG8(0x0104) +#define IMX111_GROUP_WRITE_ON 1 +#define IMX111_FRAME_DROP CCI_REG8(0x0105) +#define IMX111_FRAME_DROP_ON 1 +#define IMX111_CHANNEL_ID CCI_REG8(0x0110) +#define IMX111_SIGNALLING_MODE CCI_REG8(0x0111) +#define IMX111_DATA_DEPTH CCI_REG16(0x0112) +#define IMX111_DATA_DEPTH_RAW8 0x08 +#define IMX111_DATA_DEPTH_RAW10 0x0a + +/* integration time registers */ +#define IMX111_INTEGRATION_TIME CCI_REG16(0x0202) +#define IMX111_INTEGRATION_TIME_MIN 0x1 +#define IMX111_INTEGRATION_TIME_MAX 0xffff +#define IMX111_INTEGRATION_TIME_STEP 1 +#define IMX111_INTEGRATION_TIME_OFFSET 5 + +/* analog gain control */ +#define IMX111_REG_ANALOG_GAIN CCI_REG8(0x0205) +#define IMX111_ANA_GAIN_MIN 0 +#define IMX111_ANA_GAIN_MAX 240 +#define IMX111_ANA_GAIN_STEP 1 +#define IMX111_ANA_GAIN_DEFAULT 0 + +/* digital gain control */ +#define IMX111_REG_DIG_GAIN_GREENR CCI_REG16(0x020e) +#define IMX111_REG_DIG_GAIN_RED CCI_REG16(0x0210) +#define IMX111_REG_DIG_GAIN_BLUE CCI_REG16(0x0212) +#define IMX111_REG_DIG_GAIN_GREENB CCI_REG16(0x0214) +#define IMX111_DGTL_GAIN_MIN 0x0100 +#define IMX111_DGTL_GAIN_MAX 0x0fff +#define IMX111_DGTL_GAIN_DEFAULT 0x0100 +#define IMX111_DGTL_GAIN_STEP 1 + +/* clock configuration registers */ +#define IMX111_PIXEL_CLK_DIVIDER_PLL1 CCI_REG8(0x0301) +#define IMX111_SYSTEM_CLK_DIVIDER_PLL1 CCI_REG8(0x0303) +#define IMX111_PRE_PLL_CLK_DIVIDER_PLL1 CCI_REG8(0x0305) +#define IMX111_PLL_MULTIPLIER_PLL1 CCI_REG8(0x0307) +#define IMX111_PLL_SETTLING_TIME CCI_REG8(0x303c) +#define IMX111_PLL_SETTLING_TIME_DEFAULT 200 +#define IMX111_POST_DIVIDER CCI_REG8(0x30a4) +#define IMX111_POST_DIVIDER_DIV1 2 +#define IMX111_POST_DIVIDER_DIV2 0 +#define IMX111_POST_DIVIDER_DIV4 1 + +/* frame timing registers */ +#define IMX111_VERTICAL_TOTAL_LENGTH CCI_REG16(0x0340) +#define IMX111_VTL_MAX 0x09d8 +#define IMX111_VBLANK_MIN 16 +#define IMX111_HORIZONTAL_TOTAL_LENGTH CCI_REG16(0x0342) +#define IMX111_HTL_MAX 0x0dd0 +#define IMX111_HBLANK_MIN 16 + +/* image size registers */ +#define IMX111_HORIZONTAL_START CCI_REG16(0x0344) +#define IMX111_VERTICAL_START CCI_REG16(0x0346) +#define IMX111_HORIZONTAL_END CCI_REG16(0x0348) +#define IMX111_VERTICAL_END CCI_REG16(0x034a) +#define IMX111_IMAGE_WIDTH CCI_REG16(0x034c) +#define IMX111_IMAGE_HEIGHT CCI_REG16(0x034e) +#define IMX111_H_EVEN_INC CCI_REG8(0x0381) +#define IMX111_H_ODD_INC CCI_REG8(0x0383) +#define IMX111_W_EVEN_INC CCI_REG8(0x0385) +#define IMX111_W_ODD_INC CCI_REG8(0x0387) + +/* test pattern registers */ +#define IMX111_TEST_PATTERN CCI_REG8(0x0601) +#define IMX111_TEST_PATTERN_NONE 0 +#define IMX111_TEST_PATTERN_SOLID 1 +#define IMX111_TEST_PATTERN_BARS 2 +#define IMX111_TEST_PATTERN_FADE 3 +#define IMX111_TEST_PATTERN_PN9 4 +#define IMX111_SOLID_COLOR_RED CCI_REG16(0x0602) +#define IMX111_SOLID_COLOR_GR CCI_REG16(0x0604) +#define IMX111_SOLID_COLOR_BLUE CCI_REG16(0x0606) +#define IMX111_SOLID_COLOR_GB CCI_REG16(0x0608) +#define IMX111_TESTP_COLOUR_MIN 0 +#define IMX111_TESTP_COLOUR_MAX 0x03ff +#define IMX111_TESTP_COLOUR_STEP 1 + +#define IMX111_FRAME_RATE_STEP 5 + +#define IMX111_PIXEL_ARRAY_WIDTH 3280U +#define IMX111_PIXEL_ARRAY_HEIGHT 2464U + +enum { + IMX111_MODE_3280x2464, + IMX111_MODE_3280x1848, + IMX111_MODE_3280x1098, + IMX111_MODE_2100x1200, + IMX111_MODE_1952x1098, + IMX111_MODE_1920x1080, + IMX111_MODE_1640x1232, + IMX111_MODE_1440x1080, + IMX111_MODE_1640x924, + IMX111_MODE_1308x736, + IMX111_MODE_1280x720, + IMX111_MODE_820x614, + IMX111_MODE_640x480, +}; + +static const struct regulator_bulk_data imx111_supplies[] = { + { .supply = "iovdd" }, + { .supply = "dvdd" }, + { .supply = "avdd" }, +}; + +struct imx111_mode { + u32 width; + u32 height; + + /* Default vertical and horizontal total length */ + u32 vtl_def; + u32 htl_def; + + struct { + const struct cci_reg_sequence *regs; + u32 num_of_regs; + } reg_list; +}; + +struct imx111_pll { + u64 extclk_rate; + u8 pre_div; + u8 mult; +}; + +struct imx111 { + struct regmap *regmap; + + struct clk *extclk; + struct gpio_desc *reset; + struct regulator_bulk_data *supplies; + + struct v4l2_fwnode_endpoint bus_cfg; + struct v4l2_subdev sd; + struct media_pad pad; + + /* V4L2 Controls */ + struct v4l2_ctrl_handler hdl; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + + /* Current mode */ + const struct imx111_mode *cur_mode; + const struct imx111_pll *pll; + u32 data_depth; + + u64 pixel_clk_raw; + s64 default_link_freq; +}; + +static const struct imx111_pll imx111_pll[] = { + { .extclk_rate = 6000000, .pre_div = 1, .mult = 113, }, + { .extclk_rate = 12000000, .pre_div = 2, .mult = 113, }, + { .extclk_rate = 13500000, .pre_div = 1, .mult = 50, }, + { .extclk_rate = 18000000, .pre_div = 2, .mult = 75, }, + { .extclk_rate = 24000000, .pre_div = 4, .mult = 113, }, + { .extclk_rate = 27000000, .pre_div = 2, .mult = 50, }, + { .extclk_rate = 36000000, .pre_div = 4, .mult = 75, }, + { .extclk_rate = 54000000, .pre_div = 4, .mult = 50, }, +}; + +/* + * This table MUST contain 4 entries per format, to cover the various flip + * combinations in the order + * - no flip + * - h flip + * - v flip + * - h&v flips + */ +static const u32 imx111_mbus_formats[] = { + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SBGGR10_1X10, + MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10, + + MEDIA_BUS_FMT_SGBRG8_1X8, + MEDIA_BUS_FMT_SBGGR8_1X8, + MEDIA_BUS_FMT_SRGGB8_1X8, + MEDIA_BUS_FMT_SGRBG8_1X8, +}; + +static const struct cci_reg_sequence imx111_global_init[] = { + { CCI_REG8(0x3080), 0x50 }, + { CCI_REG8(0x3087), 0x53 }, + { CCI_REG8(0x309d), 0x94 }, + { CCI_REG8(0x30b1), 0x03 }, + { CCI_REG8(0x30c6), 0x00 }, + { CCI_REG8(0x30c7), 0x00 }, + { CCI_REG8(0x3115), 0x0b }, + { CCI_REG8(0x3118), 0x30 }, + { CCI_REG8(0x311d), 0x25 }, + { CCI_REG8(0x3121), 0x0a }, + { CCI_REG8(0x3212), 0xf2 }, + { CCI_REG8(0x3213), 0x0f }, + { CCI_REG8(0x3215), 0x0f }, + { CCI_REG8(0x3217), 0x0b }, + { CCI_REG8(0x3219), 0x0b }, + { CCI_REG8(0x321b), 0x0d }, + { CCI_REG8(0x321d), 0x0d }, + { CCI_REG8(0x32aa), 0x11 }, + { CCI_REG8(0x3032), 0x40 }, +}; + +static const struct cci_reg_sequence mode_820x614[] = { + { IMX111_GROUP_WRITE, 1 }, + { IMX111_HORIZONTAL_START, 0x0008 }, { IMX111_VERTICAL_START, 0x0034 }, + { IMX111_HORIZONTAL_END, 0x0cd7 }, { IMX111_VERTICAL_END, 0x09cb }, + { IMX111_IMAGE_WIDTH, 0x0334 }, { IMX111_IMAGE_HEIGHT, 0x0266 }, + { IMX111_GROUP_WRITE, 0 }, + { IMX111_H_EVEN_INC, 0x05 }, { IMX111_H_ODD_INC, 0x03 }, + { IMX111_W_EVEN_INC, 0x05 }, { IMX111_W_ODD_INC, 0x03 }, + { CCI_REG8(0x3033), 0x00 }, { CCI_REG8(0x303d), 0x10 }, + { CCI_REG8(0x303e), 0x40 }, { CCI_REG8(0x3040), 0x08 }, + { CCI_REG8(0x3041), 0x97 }, { CCI_REG8(0x3048), 0x01 }, + { CCI_REG8(0x304c), 0x6f }, { CCI_REG8(0x304d), 0x03 }, + { CCI_REG8(0x3064), 0x12 }, { CCI_REG8(0x3073), 0x00 }, + { CCI_REG8(0x3074), 0x11 }, { CCI_REG8(0x3075), 0x11 }, + { CCI_REG8(0x3076), 0x11 }, { CCI_REG8(0x3077), 0x11 }, + { CCI_REG8(0x3079), 0x00 }, { CCI_REG8(0x307a), 0x00 }, + { CCI_REG8(0x309b), 0x28 }, { CCI_REG8(0x309c), 0x13 }, + { CCI_REG8(0x309e), 0x00 }, { CCI_REG8(0x30a0), 0x14 }, + { CCI_REG8(0x30a1), 0x09 }, { CCI_REG8(0x30aa), 0x03 }, + { CCI_REG8(0x30b2), 0x03 }, { CCI_REG8(0x30d5), 0x09 }, + { CCI_REG8(0x30d6), 0x00 }, { CCI_REG8(0x30d7), 0x00 }, + { CCI_REG8(0x30d8), 0x00 }, { CCI_REG8(0x30d9), 0x00 }, + { CCI_REG8(0x30de), 0x04 }, { CCI_REG8(0x30df), 0x20 }, + { CCI_REG8(0x3102), 0x08 }, { CCI_REG8(0x3103), 0x22 }, + { CCI_REG8(0x3104), 0x20 }, { CCI_REG8(0x3105), 0x00 }, + { CCI_REG8(0x3106), 0x87 }, { CCI_REG8(0x3107), 0x00 }, + { CCI_REG8(0x3108), 0x03 }, { CCI_REG8(0x3109), 0x02 }, + { CCI_REG8(0x310a), 0x03 }, { CCI_REG8(0x315c), 0x9c }, + { CCI_REG8(0x315d), 0x9b }, { CCI_REG8(0x316e), 0x9d }, + { CCI_REG8(0x316f), 0x9c }, { CCI_REG8(0x3318), 0x7a }, + { CCI_REG8(0x3348), 0xe0 }, +}; + +static const struct cci_reg_sequence mode_1308x736[] = { + { IMX111_GROUP_WRITE, 1 }, + { IMX111_HORIZONTAL_START, 0x0154 }, { IMX111_VERTICAL_START, 0x0220 }, + { IMX111_HORIZONTAL_END, 0x0b8b }, { IMX111_VERTICAL_END, 0x07df }, + { IMX111_IMAGE_WIDTH, 0x051c }, { IMX111_IMAGE_HEIGHT, 0x02e0 }, + { IMX111_GROUP_WRITE, 0 }, + { IMX111_H_EVEN_INC, 0x01 }, { IMX111_H_ODD_INC, 0x01 }, + { IMX111_W_EVEN_INC, 0x01 }, { IMX111_W_ODD_INC, 0x03 }, + { CCI_REG8(0x3033), 0x84 }, { CCI_REG8(0x303d), 0x10 }, + { CCI_REG8(0x303e), 0x40 }, { CCI_REG8(0x3040), 0x08 }, + { CCI_REG8(0x3041), 0x97 }, { CCI_REG8(0x3048), 0x01 }, + { CCI_REG8(0x304c), 0xd7 }, { CCI_REG8(0x304d), 0x01 }, + { CCI_REG8(0x3064), 0x12 }, { CCI_REG8(0x3073), 0x00 }, + { CCI_REG8(0x3074), 0x11 }, { CCI_REG8(0x3075), 0x11 }, + { CCI_REG8(0x3076), 0x11 }, { CCI_REG8(0x3077), 0x11 }, + { CCI_REG8(0x3079), 0x00 }, { CCI_REG8(0x307a), 0x00 }, + { CCI_REG8(0x309b), 0x48 }, { CCI_REG8(0x309c), 0x12 }, + { CCI_REG8(0x309e), 0x04 }, { CCI_REG8(0x30a0), 0x14 }, + { CCI_REG8(0x30a1), 0x0a }, { CCI_REG8(0x30aa), 0x01 }, + { CCI_REG8(0x30b2), 0x05 }, { CCI_REG8(0x30d5), 0x04 }, + { CCI_REG8(0x30d6), 0x85 }, { CCI_REG8(0x30d7), 0x2a }, + { CCI_REG8(0x30d8), 0x64 }, { CCI_REG8(0x30d9), 0x89 }, + { CCI_REG8(0x30de), 0x00 }, { CCI_REG8(0x30df), 0x20 }, + { CCI_REG8(0x3102), 0x08 }, { CCI_REG8(0x3103), 0x22 }, + { CCI_REG8(0x3104), 0x20 }, { CCI_REG8(0x3105), 0x00 }, + { CCI_REG8(0x3106), 0x87 }, { CCI_REG8(0x3107), 0x00 }, + { CCI_REG8(0x3108), 0x03 }, { CCI_REG8(0x3109), 0x02 }, + { CCI_REG8(0x310a), 0x03 }, { CCI_REG8(0x315c), 0x42 }, + { CCI_REG8(0x315d), 0x41 }, { CCI_REG8(0x316e), 0x43 }, + { CCI_REG8(0x316f), 0x42 }, { CCI_REG8(0x3318), 0x62 }, + { CCI_REG8(0x3348), 0xe0 }, +}; + +static const struct cci_reg_sequence mode_1640x924[] = { + { IMX111_GROUP_WRITE, 1 }, + { IMX111_HORIZONTAL_START, 0x0008 }, { IMX111_VERTICAL_START, 0x0164 }, + { IMX111_HORIZONTAL_END, 0x0cd7 }, { IMX111_VERTICAL_END, 0x089b }, + { IMX111_IMAGE_WIDTH, 0x0668 }, { IMX111_IMAGE_HEIGHT, 0x039c }, + { IMX111_GROUP_WRITE, 0 }, + { IMX111_H_EVEN_INC, 0x01 }, { IMX111_H_ODD_INC, 0x03 }, + { IMX111_W_EVEN_INC, 0x01 }, { IMX111_W_ODD_INC, 0x03 }, + { CCI_REG8(0x3033), 0x00 }, { CCI_REG8(0x303d), 0x10 }, + { CCI_REG8(0x303e), 0x40 }, { CCI_REG8(0x3040), 0x08 }, + { CCI_REG8(0x3041), 0x97 }, { CCI_REG8(0x3048), 0x01 }, + { CCI_REG8(0x304c), 0x6f }, { CCI_REG8(0x304d), 0x03 }, + { CCI_REG8(0x3064), 0x12 }, { CCI_REG8(0x3073), 0x00 }, + { CCI_REG8(0x3074), 0x11 }, { CCI_REG8(0x3075), 0x11 }, + { CCI_REG8(0x3076), 0x11 }, { CCI_REG8(0x3077), 0x11 }, + { CCI_REG8(0x3079), 0x00 }, { CCI_REG8(0x307a), 0x00 }, + { CCI_REG8(0x309b), 0x28 }, { CCI_REG8(0x309c), 0x13 }, + { CCI_REG8(0x309e), 0x00 }, { CCI_REG8(0x30a0), 0x14 }, + { CCI_REG8(0x30a1), 0x09 }, { CCI_REG8(0x30aa), 0x03 }, + { CCI_REG8(0x30b2), 0x05 }, { CCI_REG8(0x30d5), 0x09 }, + { CCI_REG8(0x30d6), 0x01 }, { CCI_REG8(0x30d7), 0x01 }, + { CCI_REG8(0x30d8), 0x64 }, { CCI_REG8(0x30d9), 0x89 }, + { CCI_REG8(0x30de), 0x02 }, { CCI_REG8(0x30df), 0x20 }, + { CCI_REG8(0x3102), 0x08 }, { CCI_REG8(0x3103), 0x22 }, + { CCI_REG8(0x3104), 0x20 }, { CCI_REG8(0x3105), 0x00 }, + { CCI_REG8(0x3106), 0x87 }, { CCI_REG8(0x3107), 0x00 }, + { CCI_REG8(0x3108), 0x03 }, { CCI_REG8(0x3109), 0x02 }, + { CCI_REG8(0x310a), 0x03 }, { CCI_REG8(0x315c), 0x9c }, + { CCI_REG8(0x315d), 0x9b }, { CCI_REG8(0x316e), 0x9d }, + { CCI_REG8(0x316f), 0x9c }, { CCI_REG8(0x3318), 0x72 }, + { CCI_REG8(0x3348), 0xe0 }, +}; + +static const struct cci_reg_sequence mode_1640x1232[] = { + { IMX111_GROUP_WRITE, 1 }, + { IMX111_HORIZONTAL_START, 0x0008 }, { IMX111_VERTICAL_START, 0x0030 }, + { IMX111_HORIZONTAL_END, 0x0cd7 }, { IMX111_VERTICAL_END, 0x09cf }, + { IMX111_IMAGE_WIDTH, 0x0668 }, { IMX111_IMAGE_HEIGHT, 0x04d0 }, + { IMX111_GROUP_WRITE, 0 }, + { IMX111_H_EVEN_INC, 0x01 }, { IMX111_H_ODD_INC, 0x03 }, + { IMX111_W_EVEN_INC, 0x01 }, { IMX111_W_ODD_INC, 0x03 }, + { CCI_REG8(0x3033), 0x00 }, { CCI_REG8(0x303d), 0x10 }, + { CCI_REG8(0x303e), 0x40 }, { CCI_REG8(0x3040), 0x08 }, + { CCI_REG8(0x3041), 0x97 }, { CCI_REG8(0x3048), 0x01 }, + { CCI_REG8(0x304c), 0x6f }, { CCI_REG8(0x304d), 0x03 }, + { CCI_REG8(0x3064), 0x12 }, { CCI_REG8(0x3073), 0x00 }, + { CCI_REG8(0x3074), 0x11 }, { CCI_REG8(0x3075), 0x11 }, + { CCI_REG8(0x3076), 0x11 }, { CCI_REG8(0x3077), 0x11 }, + { CCI_REG8(0x3079), 0x00 }, { CCI_REG8(0x307a), 0x00 }, + { CCI_REG8(0x309b), 0x28 }, { CCI_REG8(0x309c), 0x13 }, + { CCI_REG8(0x309e), 0x00 }, { CCI_REG8(0x30a0), 0x14 }, + { CCI_REG8(0x30a1), 0x09 }, { CCI_REG8(0x30aa), 0x03 }, + { CCI_REG8(0x30b2), 0x05 }, { CCI_REG8(0x30d5), 0x09 }, + { CCI_REG8(0x30d6), 0x01 }, { CCI_REG8(0x30d7), 0x01 }, + { CCI_REG8(0x30d8), 0x64 }, { CCI_REG8(0x30d9), 0x89 }, + { CCI_REG8(0x30de), 0x02 }, { CCI_REG8(0x30df), 0x20 }, + { CCI_REG8(0x3102), 0x08 }, { CCI_REG8(0x3103), 0x22 }, + { CCI_REG8(0x3104), 0x20 }, { CCI_REG8(0x3105), 0x00 }, + { CCI_REG8(0x3106), 0x87 }, { CCI_REG8(0x3107), 0x00 }, + { CCI_REG8(0x3108), 0x03 }, { CCI_REG8(0x3109), 0x02 }, + { CCI_REG8(0x310a), 0x03 }, { CCI_REG8(0x315c), 0x9c }, + { CCI_REG8(0x315d), 0x9b }, { CCI_REG8(0x316e), 0x9d }, + { CCI_REG8(0x316f), 0x9c }, { CCI_REG8(0x3318), 0x72 }, + { CCI_REG8(0x3348), 0xe0 }, +}; + +static const struct cci_reg_sequence mode_1952x1098[] = { + { IMX111_GROUP_WRITE, 1 }, + { IMX111_HORIZONTAL_START, 0x0016 }, { IMX111_VERTICAL_START, 0x016e }, + { IMX111_HORIZONTAL_END, 0x0ccb }, { IMX111_VERTICAL_END, 0x0893 }, + { IMX111_IMAGE_WIDTH, 0x07a0 }, { IMX111_IMAGE_HEIGHT, 0x044a }, + { IMX111_GROUP_WRITE, 0 }, + { IMX111_H_EVEN_INC, 0x01 }, { IMX111_H_ODD_INC, 0x01 }, + { IMX111_W_EVEN_INC, 0x01 }, { IMX111_W_ODD_INC, 0x01 }, + { CCI_REG8(0x3033), 0x00 }, { CCI_REG8(0x303d), 0x10 }, + { CCI_REG8(0x303e), 0x00 }, { CCI_REG8(0x3040), 0x08 }, + { CCI_REG8(0x3041), 0x91 }, { CCI_REG8(0x3048), 0x00 }, + { CCI_REG8(0x304c), 0x67 }, { CCI_REG8(0x304d), 0x03 }, + { CCI_REG8(0x3064), 0x10 }, { CCI_REG8(0x3073), 0xa0 }, + { CCI_REG8(0x3074), 0x12 }, { CCI_REG8(0x3075), 0x12 }, + { CCI_REG8(0x3076), 0x12 }, { CCI_REG8(0x3077), 0x11 }, + { CCI_REG8(0x3079), 0x0a }, { CCI_REG8(0x307a), 0x0a }, + { CCI_REG8(0x309b), 0x60 }, { CCI_REG8(0x309e), 0x04 }, + { CCI_REG8(0x30a0), 0x15 }, { CCI_REG8(0x30a1), 0x08 }, + { CCI_REG8(0x30aa), 0x03 }, { CCI_REG8(0x30b2), 0x05 }, + { CCI_REG8(0x30d5), 0x20 }, { CCI_REG8(0x30d6), 0x85 }, + { CCI_REG8(0x30d7), 0x2a }, { CCI_REG8(0x30d8), 0x64 }, + { CCI_REG8(0x30d9), 0x89 }, { CCI_REG8(0x30de), 0x00 }, + { CCI_REG8(0x30df), 0x21 }, { CCI_REG8(0x3102), 0x08 }, + { CCI_REG8(0x3103), 0x1d }, { CCI_REG8(0x3104), 0x1e }, + { CCI_REG8(0x3105), 0x00 }, { CCI_REG8(0x3106), 0x74 }, + { CCI_REG8(0x3107), 0x00 }, { CCI_REG8(0x3108), 0x03 }, + { CCI_REG8(0x3109), 0x02 }, { CCI_REG8(0x310a), 0x03 }, + { CCI_REG8(0x315c), 0x37 }, { CCI_REG8(0x315d), 0x36 }, + { CCI_REG8(0x316e), 0x38 }, { CCI_REG8(0x316f), 0x37 }, + { CCI_REG8(0x3318), 0x63 }, { CCI_REG8(0x3348), 0xA0 }, +}; + +static const struct cci_reg_sequence mode_2100x1200[] = { + { IMX111_GROUP_WRITE, 1 }, + { IMX111_HORIZONTAL_START, 0x0256 }, { IMX111_VERTICAL_START, 0x02a8 }, + { IMX111_HORIZONTAL_END, 0x0a89 }, { IMX111_VERTICAL_END, 0x0757 }, + { IMX111_IMAGE_WIDTH, 0x0834 }, { IMX111_IMAGE_HEIGHT, 0x04b0 }, + { IMX111_GROUP_WRITE, 0 }, + { IMX111_H_EVEN_INC, 0x01 }, { IMX111_H_ODD_INC, 0x01 }, + { IMX111_W_EVEN_INC, 0x01 }, { IMX111_W_ODD_INC, 0x01 }, + { CCI_REG8(0x3033), 0x00 }, { CCI_REG8(0x303d), 0x10 }, + { CCI_REG8(0x303e), 0x40 }, { CCI_REG8(0x3040), 0x08 }, + { CCI_REG8(0x3041), 0x97 }, { CCI_REG8(0x3048), 0x00 }, + { CCI_REG8(0x304c), 0x6f }, { CCI_REG8(0x304d), 0x03 }, + { CCI_REG8(0x3064), 0x12 }, { CCI_REG8(0x3073), 0x00 }, + { CCI_REG8(0x3074), 0x11 }, { CCI_REG8(0x3075), 0x11 }, + { CCI_REG8(0x3076), 0x11 }, { CCI_REG8(0x3077), 0x11 }, + { CCI_REG8(0x3079), 0x00 }, { CCI_REG8(0x307a), 0x00 }, + { CCI_REG8(0x309b), 0x20 }, { CCI_REG8(0x309c), 0x13 }, + { CCI_REG8(0x309e), 0x00 }, { CCI_REG8(0x30a0), 0x14 }, + { CCI_REG8(0x30a1), 0x08 }, { CCI_REG8(0x30aa), 0x03 }, + { CCI_REG8(0x30b2), 0x07 }, { CCI_REG8(0x30d5), 0x00 }, + { CCI_REG8(0x30d6), 0x85 }, { CCI_REG8(0x30d7), 0x2a }, + { CCI_REG8(0x30d8), 0x64 }, { CCI_REG8(0x30d9), 0x89 }, + { CCI_REG8(0x30de), 0x00 }, { CCI_REG8(0x30df), 0x20 }, + { CCI_REG8(0x3102), 0x08 }, { CCI_REG8(0x3103), 0x22 }, + { CCI_REG8(0x3104), 0x20 }, { CCI_REG8(0x3105), 0x00 }, + { CCI_REG8(0x3106), 0x87 }, { CCI_REG8(0x3107), 0x00 }, + { CCI_REG8(0x3108), 0x03 }, { CCI_REG8(0x3109), 0x02 }, + { CCI_REG8(0x310a), 0x03 }, { CCI_REG8(0x315c), 0x9c }, + { CCI_REG8(0x315d), 0x9b }, { CCI_REG8(0x316e), 0x9d }, + { CCI_REG8(0x316f), 0x9c }, { CCI_REG8(0x3318), 0x62 }, + { CCI_REG8(0x3348), 0xe0 }, +}; + +static const struct cci_reg_sequence mode_3280x1098[] = { + { IMX111_GROUP_WRITE, 1 }, + { IMX111_HORIZONTAL_START, 0x0008 }, { IMX111_VERTICAL_START, 0x01f6 }, + { IMX111_HORIZONTAL_END, 0x0cd7 }, { IMX111_VERTICAL_END, 0x080b }, + { IMX111_IMAGE_WIDTH, 0x0cd0 }, { IMX111_IMAGE_HEIGHT, 0x044a }, + { IMX111_GROUP_WRITE, 0 }, + { IMX111_H_EVEN_INC, 0x01 }, { IMX111_H_ODD_INC, 0x01 }, + { IMX111_W_EVEN_INC, 0x01 }, { IMX111_W_ODD_INC, 0x01 }, + { CCI_REG8(0x3033), 0x00 }, { CCI_REG8(0x303d), 0x10 }, + { CCI_REG8(0x303e), 0x40 }, { CCI_REG8(0x3040), 0x08 }, + { CCI_REG8(0x3041), 0x93 }, { CCI_REG8(0x3048), 0x00 }, + { CCI_REG8(0x304c), 0x67 }, { CCI_REG8(0x304d), 0x03 }, + { CCI_REG8(0x3064), 0x12 }, { CCI_REG8(0x3073), 0xe0 }, + { CCI_REG8(0x3074), 0x12 }, { CCI_REG8(0x3075), 0x12 }, + { CCI_REG8(0x3076), 0x12 }, { CCI_REG8(0x3077), 0x12 }, + { CCI_REG8(0x3079), 0x2a }, { CCI_REG8(0x307a), 0x0a }, + { CCI_REG8(0x309b), 0x60 }, { CCI_REG8(0x309e), 0x04 }, + { CCI_REG8(0x30a0), 0x15 }, { CCI_REG8(0x30a1), 0x08 }, + { CCI_REG8(0x30aa), 0x03 }, { CCI_REG8(0x30b2), 0x05 }, + { CCI_REG8(0x30d5), 0x00 }, { CCI_REG8(0x30d6), 0x85 }, + { CCI_REG8(0x30d7), 0x2a }, { CCI_REG8(0x30d8), 0x64 }, + { CCI_REG8(0x30d9), 0x89 }, { CCI_REG8(0x30de), 0x00 }, + { CCI_REG8(0x30df), 0x20 }, { CCI_REG8(0x3102), 0x08 }, + { CCI_REG8(0x3103), 0x1d }, { CCI_REG8(0x3104), 0x1e }, + { CCI_REG8(0x3105), 0x00 }, { CCI_REG8(0x3106), 0x74 }, + { CCI_REG8(0x3107), 0x00 }, { CCI_REG8(0x3108), 0x03 }, + { CCI_REG8(0x3109), 0x02 }, { CCI_REG8(0x310a), 0x03 }, + { CCI_REG8(0x315c), 0x37 }, { CCI_REG8(0x315d), 0x36 }, + { CCI_REG8(0x316e), 0x38 }, { CCI_REG8(0x316f), 0x37 }, + { CCI_REG8(0x3318), 0x63 }, { CCI_REG8(0x3348), 0xe0 }, +}; + +static const struct cci_reg_sequence mode_3280x1848[] = { + { IMX111_GROUP_WRITE, 1 }, + { IMX111_HORIZONTAL_START, 0x0008 }, { IMX111_VERTICAL_START, 0x0164 }, + { IMX111_HORIZONTAL_END, 0x0cd7 }, { IMX111_VERTICAL_END, 0x089b }, + { IMX111_IMAGE_WIDTH, 0x0cd0 }, { IMX111_IMAGE_HEIGHT, 0x0738 }, + { IMX111_GROUP_WRITE, 0 }, + { IMX111_H_EVEN_INC, 0x01 }, { IMX111_H_ODD_INC, 0x01 }, + { IMX111_W_EVEN_INC, 0x01 }, { IMX111_W_ODD_INC, 0x01 }, + { CCI_REG8(0x3033), 0x00 }, { CCI_REG8(0x303d), 0x00 }, + { CCI_REG8(0x303e), 0x41 }, { CCI_REG8(0x3040), 0x08 }, + { CCI_REG8(0x3041), 0x97 }, { CCI_REG8(0x3048), 0x00 }, + { CCI_REG8(0x304c), 0x6f }, { CCI_REG8(0x304d), 0x03 }, + { CCI_REG8(0x3064), 0x12 }, { CCI_REG8(0x3073), 0x00 }, + { CCI_REG8(0x3074), 0x11 }, { CCI_REG8(0x3075), 0x11 }, + { CCI_REG8(0x3076), 0x11 }, { CCI_REG8(0x3077), 0x11 }, + { CCI_REG8(0x3079), 0x00 }, { CCI_REG8(0x307a), 0x00 }, + { CCI_REG8(0x309b), 0x20 }, { CCI_REG8(0x309c), 0x13 }, + { CCI_REG8(0x309e), 0x00 }, { CCI_REG8(0x30a0), 0x14 }, + { CCI_REG8(0x30a1), 0x08 }, { CCI_REG8(0x30aa), 0x03 }, + { CCI_REG8(0x30b2), 0x07 }, { CCI_REG8(0x30d5), 0x00 }, + { CCI_REG8(0x30d6), 0x85 }, { CCI_REG8(0x30d7), 0x2a }, + { CCI_REG8(0x30d8), 0x64 }, { CCI_REG8(0x30d9), 0x89 }, + { CCI_REG8(0x30de), 0x00 }, { CCI_REG8(0x30df), 0x20 }, + { CCI_REG8(0x3102), 0x10 }, { CCI_REG8(0x3103), 0x44 }, + { CCI_REG8(0x3104), 0x40 }, { CCI_REG8(0x3105), 0x00 }, + { CCI_REG8(0x3106), 0x0d }, { CCI_REG8(0x3107), 0x01 }, + { CCI_REG8(0x3108), 0x09 }, { CCI_REG8(0x3109), 0x08 }, + { CCI_REG8(0x310a), 0x0f }, { CCI_REG8(0x315c), 0x5d }, + { CCI_REG8(0x315d), 0x5c }, { CCI_REG8(0x316e), 0x5e }, + { CCI_REG8(0x316f), 0x5d }, { CCI_REG8(0x3318), 0x60 }, + { CCI_REG8(0x3348), 0xe0 }, +}; + +static const struct cci_reg_sequence mode_3280x2464[] = { + { IMX111_GROUP_WRITE, 1 }, + { IMX111_HORIZONTAL_START, 0x0008 }, { IMX111_VERTICAL_START, 0x0030 }, + { IMX111_HORIZONTAL_END, 0x0cd7 }, { IMX111_VERTICAL_END, 0x09cf }, + { IMX111_IMAGE_WIDTH, 0x0cd0 }, { IMX111_IMAGE_HEIGHT, 0x09a0 }, + { IMX111_GROUP_WRITE, 0 }, + { IMX111_H_EVEN_INC, 0x01 }, { IMX111_H_ODD_INC, 0x01 }, + { IMX111_W_EVEN_INC, 0x01 }, { IMX111_W_ODD_INC, 0x01 }, + { CCI_REG8(0x3033), 0x00 }, { CCI_REG8(0x303d), 0x00 }, + { CCI_REG8(0x303e), 0x41 }, { CCI_REG8(0x3040), 0x08 }, + { CCI_REG8(0x3041), 0x97 }, { CCI_REG8(0x3048), 0x00 }, + { CCI_REG8(0x304c), 0x6f }, { CCI_REG8(0x304d), 0x03 }, + { CCI_REG8(0x3064), 0x12 }, { CCI_REG8(0x3073), 0x00 }, + { CCI_REG8(0x3074), 0x11 }, { CCI_REG8(0x3075), 0x11 }, + { CCI_REG8(0x3076), 0x11 }, { CCI_REG8(0x3077), 0x11 }, + { CCI_REG8(0x3079), 0x00 }, { CCI_REG8(0x307a), 0x00 }, + { CCI_REG8(0x309b), 0x20 }, { CCI_REG8(0x309c), 0x13 }, + { CCI_REG8(0x309e), 0x00 }, { CCI_REG8(0x30a0), 0x14 }, + { CCI_REG8(0x30a1), 0x08 }, { CCI_REG8(0x30aa), 0x03 }, + { CCI_REG8(0x30b2), 0x07 }, { CCI_REG8(0x30d5), 0x00 }, + { CCI_REG8(0x30d6), 0x85 }, { CCI_REG8(0x30d7), 0x2a }, + { CCI_REG8(0x30d8), 0x64 }, { CCI_REG8(0x30d9), 0x89 }, + { CCI_REG8(0x30de), 0x00 }, { CCI_REG8(0x30df), 0x20 }, + { CCI_REG8(0x3102), 0x10 }, { CCI_REG8(0x3103), 0x44 }, + { CCI_REG8(0x3104), 0x40 }, { CCI_REG8(0x3105), 0x00 }, + { CCI_REG8(0x3106), 0x0d }, { CCI_REG8(0x3107), 0x01 }, + { CCI_REG8(0x3108), 0x09 }, { CCI_REG8(0x3109), 0x08 }, + { CCI_REG8(0x310a), 0x0f }, { CCI_REG8(0x315c), 0x5d }, + { CCI_REG8(0x315d), 0x5c }, { CCI_REG8(0x316e), 0x5e }, + { CCI_REG8(0x316f), 0x5d }, { CCI_REG8(0x3318), 0x60 }, + { CCI_REG8(0x3348), 0xe0 }, +}; + +static const struct imx111_mode imx111_modes[] = { + [IMX111_MODE_3280x2464] = { + .width = 3280, + .height = 2464, + .vtl_def = 2490, + .htl_def = 3536, + .reg_list = { + .regs = mode_3280x2464, + .num_of_regs = ARRAY_SIZE(mode_3280x2464), + }, + }, + [IMX111_MODE_3280x1848] = { + .width = 3280, + .height = 1848, + .vtl_def = 1874, + .htl_def = 3536, + .reg_list = { + .regs = mode_3280x1848, + .num_of_regs = ARRAY_SIZE(mode_3280x1848), + }, + }, + [IMX111_MODE_3280x1098] = { + .width = 3280, + .height = 1098, + .vtl_def = 1130, + .htl_def = 3500, + .reg_list = { + .regs = mode_3280x1098, + .num_of_regs = ARRAY_SIZE(mode_3280x1098), + }, + }, + [IMX111_MODE_2100x1200] = { + .width = 2100, + .height = 1200, + .vtl_def = 1260, + .htl_def = 3536, + .reg_list = { + .regs = mode_2100x1200, + .num_of_regs = ARRAY_SIZE(mode_2100x1200), + }, + }, + [IMX111_MODE_1952x1098] = { + .width = 1952, + .height = 1098, + .vtl_def = 1884, + .htl_def = 3500, + .reg_list = { + .regs = mode_1952x1098, + .num_of_regs = ARRAY_SIZE(mode_1952x1098), + }, + }, + [IMX111_MODE_1920x1080] = { + .width = 1920, + .height = 1080, + .vtl_def = 1884, + .htl_def = 3500, + .reg_list = { + .regs = mode_1952x1098, + .num_of_regs = ARRAY_SIZE(mode_1952x1098), + }, + }, + [IMX111_MODE_1640x1232] = { + .width = 1640, + .height = 1232, + .vtl_def = 1254, + .htl_def = 3536, + .reg_list = { + .regs = mode_1640x1232, + .num_of_regs = ARRAY_SIZE(mode_1640x1232), + }, + }, + [IMX111_MODE_1440x1080] = { + .width = 1440, + .height = 1080, + .vtl_def = 1254, + .htl_def = 3536, + .reg_list = { + .regs = mode_1640x1232, + .num_of_regs = ARRAY_SIZE(mode_1640x1232), + }, + }, + [IMX111_MODE_1640x924] = { + .width = 1640, + .height = 924, + .vtl_def = 946, + .htl_def = 3536, + .reg_list = { + .regs = mode_1640x924, + .num_of_regs = ARRAY_SIZE(mode_1640x924), + }, + }, + [IMX111_MODE_1308x736] = { + .width = 1308, + .height = 736, + .vtl_def = 2369, + .htl_def = 1896, + .reg_list = { + .regs = mode_1308x736, + .num_of_regs = ARRAY_SIZE(mode_1308x736), + }, + }, + [IMX111_MODE_1280x720] = { + .width = 1280, + .height = 720, + .vtl_def = 2369, + .htl_def = 1896, + .reg_list = { + .regs = mode_1308x736, + .num_of_regs = ARRAY_SIZE(mode_1308x736), + }, + }, + [IMX111_MODE_820x614] = { + .width = 820, + .height = 614, + .vtl_def = 1260, + .htl_def = 3536, + .reg_list = { + .regs = mode_820x614, + .num_of_regs = ARRAY_SIZE(mode_820x614), + }, + }, + [IMX111_MODE_640x480] = { + .width = 640, + .height = 480, + .vtl_def = 1260, + .htl_def = 3536, + .reg_list = { + .regs = mode_820x614, + .num_of_regs = ARRAY_SIZE(mode_820x614), + }, + }, +}; + +static inline struct imx111 *sd_to_imx111(struct v4l2_subdev *sd) +{ + return container_of_const(sd, struct imx111, sd); +} + +static inline struct imx111 *ctrl_to_imx111(struct v4l2_ctrl *ctrl) +{ + return container_of_const(ctrl->handler, struct imx111, hdl); +} + +static u8 to_settle_delay(u64 extclk_rate) +{ + u64 extclk_mhz = div_u64(extclk_rate, MEGA); + + return DIV_ROUND_UP(IMX111_PLL_SETTLING_TIME_DEFAULT * extclk_mhz - 63, + 64); +} + +static u32 imx111_get_format_code(struct imx111 *sensor, u32 code, bool test) +{ + u32 i; + + for (i = 0; i < ARRAY_SIZE(imx111_mbus_formats); i++) + if (imx111_mbus_formats[i] == code) + break; + + if (i >= ARRAY_SIZE(imx111_mbus_formats)) + i = 0; + + if (test) + return imx111_mbus_formats[i]; + + i = (i & ~3) | (sensor->vflip->val ? 2 : 0) | + (sensor->hflip->val ? 1 : 0); + + return imx111_mbus_formats[i]; +} + +static u32 imx111_get_format_bpp(const struct v4l2_mbus_framefmt *format) +{ + switch (format->code) { + case MEDIA_BUS_FMT_SRGGB8_1X8: + case MEDIA_BUS_FMT_SGRBG8_1X8: + case MEDIA_BUS_FMT_SGBRG8_1X8: + case MEDIA_BUS_FMT_SBGGR8_1X8: + return 8; + + case MEDIA_BUS_FMT_SRGGB10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SBGGR10_1X10: + default: + return 10; + } +} + +static int imx111_update_digital_gain(struct imx111 *sensor, u32 val) +{ + int ret = 0; + + cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, + IMX111_GROUP_WRITE_ON, + IMX111_GROUP_WRITE_ON, &ret); + + cci_write(sensor->regmap, IMX111_REG_DIG_GAIN_GREENR, val, &ret); + cci_write(sensor->regmap, IMX111_REG_DIG_GAIN_RED, val, &ret); + cci_write(sensor->regmap, IMX111_REG_DIG_GAIN_BLUE, val, &ret); + cci_write(sensor->regmap, IMX111_REG_DIG_GAIN_GREENB, val, &ret); + + cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, + IMX111_GROUP_WRITE_ON, 0, &ret); + + return ret; +} + +static int imx111_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct imx111 *sensor = ctrl_to_imx111(ctrl); + struct device *dev = regmap_get_device(sensor->regmap); + int ret = 0; + + if (ctrl->id == V4L2_CID_VBLANK) { + s64 max = sensor->cur_mode->height + ctrl->val - + IMX111_INTEGRATION_TIME_OFFSET; + + ret = __v4l2_ctrl_modify_range(sensor->exposure, + sensor->exposure->minimum, + max, sensor->exposure->step, + max); + if (ret) + return ret; + } + + /* + * Applying V4L2 control value only happens + * when power is up for streaming + */ + if (!pm_runtime_get_if_in_use(dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + cci_write(sensor->regmap, IMX111_REG_ANALOG_GAIN, ctrl->val, + &ret); + break; + case V4L2_CID_DIGITAL_GAIN: + ret = imx111_update_digital_gain(sensor, ctrl->val); + break; + case V4L2_CID_EXPOSURE: + cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, + IMX111_GROUP_WRITE_ON, + IMX111_GROUP_WRITE_ON, &ret); + cci_write(sensor->regmap, IMX111_INTEGRATION_TIME, ctrl->val, + &ret); + cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, + IMX111_GROUP_WRITE_ON, 0, &ret); + break; + case V4L2_CID_HBLANK: + cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, + IMX111_GROUP_WRITE_ON, + IMX111_GROUP_WRITE_ON, &ret); + cci_write(sensor->regmap, IMX111_HORIZONTAL_TOTAL_LENGTH, + sensor->cur_mode->width + ctrl->val, &ret); + cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, + IMX111_GROUP_WRITE_ON, 0, &ret); + break; + case V4L2_CID_VBLANK: + cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, + IMX111_GROUP_WRITE_ON, + IMX111_GROUP_WRITE_ON, &ret); + cci_write(sensor->regmap, IMX111_VERTICAL_TOTAL_LENGTH, + sensor->cur_mode->height + ctrl->val, &ret); + cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, + IMX111_GROUP_WRITE_ON, 0, &ret); + break; + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + cci_write(sensor->regmap, IMX111_IMAGE_ORIENTATION, + sensor->hflip->val | sensor->vflip->val << 1, &ret); + break; + case V4L2_CID_TEST_PATTERN: + cci_write(sensor->regmap, IMX111_TEST_PATTERN, ctrl->val, + &ret); + break; + case V4L2_CID_TEST_PATTERN_RED: + cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, + IMX111_GROUP_WRITE_ON, + IMX111_GROUP_WRITE_ON, &ret); + cci_write(sensor->regmap, IMX111_SOLID_COLOR_RED, ctrl->val, + &ret); + cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, + IMX111_GROUP_WRITE_ON, 0, &ret); + break; + case V4L2_CID_TEST_PATTERN_GREENR: + cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, + IMX111_GROUP_WRITE_ON, + IMX111_GROUP_WRITE_ON, &ret); + cci_write(sensor->regmap, IMX111_SOLID_COLOR_GR, ctrl->val, + &ret); + cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, + IMX111_GROUP_WRITE_ON, 0, &ret); + break; + case V4L2_CID_TEST_PATTERN_BLUE: + cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, + IMX111_GROUP_WRITE_ON, + IMX111_GROUP_WRITE_ON, &ret); + cci_write(sensor->regmap, IMX111_SOLID_COLOR_BLUE, ctrl->val, + &ret); + cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, + IMX111_GROUP_WRITE_ON, 0, &ret); + break; + case V4L2_CID_TEST_PATTERN_GREENB: + cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, + IMX111_GROUP_WRITE_ON, + IMX111_GROUP_WRITE_ON, &ret); + cci_write(sensor->regmap, IMX111_SOLID_COLOR_GB, ctrl->val, + &ret); + cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, + IMX111_GROUP_WRITE_ON, 0, &ret); + break; + default: + ret = -EINVAL; + } + + pm_runtime_put(dev); + + return ret; +} + +static const struct v4l2_ctrl_ops imx111_ctrl_ops = { + .s_ctrl = imx111_set_ctrl, +}; + +static const char * const test_pattern_menu[] = { + "Disabled", + "Solid Color Fill", + "Standard Color Bars", + "Fade To Grey Color Bars", + "Pseudorandom data", +}; + +static int imx111_init_controls(struct imx111 *sensor) +{ + const struct v4l2_ctrl_ops *ops = &imx111_ctrl_ops; + struct device *dev = regmap_get_device(sensor->regmap); + const struct imx111_mode *mode = sensor->cur_mode; + struct v4l2_fwnode_device_properties props; + struct v4l2_ctrl_handler *hdl = &sensor->hdl; + s64 pixel_rate_min, pixel_rate_max; + int i, ret; + + ret = v4l2_fwnode_device_parse(dev, &props); + if (ret < 0) + return ret; + + v4l2_ctrl_handler_init(hdl, 15); + + pixel_rate_min = div_u64(sensor->pixel_clk_raw, + 2 * IMX111_DATA_DEPTH_RAW10); + pixel_rate_max = div_u64(sensor->pixel_clk_raw, + 2 * IMX111_DATA_DEPTH_RAW8); + sensor->pixel_rate = v4l2_ctrl_new_std(hdl, NULL, V4L2_CID_PIXEL_RATE, + pixel_rate_min, pixel_rate_max, + 1, + div_u64(sensor->pixel_clk_raw, + 2 * + sensor->data_depth)); + + sensor->link_freq = v4l2_ctrl_new_int_menu(hdl, NULL, + V4L2_CID_LINK_FREQ, 0, 0, + &sensor->default_link_freq); + if (sensor->link_freq) + sensor->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN, + IMX111_ANA_GAIN_MIN, IMX111_ANA_GAIN_MAX, + IMX111_ANA_GAIN_STEP, IMX111_ANA_GAIN_DEFAULT); + + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_DIGITAL_GAIN, + IMX111_DGTL_GAIN_MIN, IMX111_DGTL_GAIN_MAX, + IMX111_DGTL_GAIN_STEP, IMX111_DGTL_GAIN_DEFAULT); + + sensor->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, + 0); + if (sensor->hflip) + sensor->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + sensor->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, + 0); + if (sensor->vflip) + sensor->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + sensor->vblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK, + IMX111_VBLANK_MIN, + IMX111_VTL_MAX - mode->height, 1, + mode->vtl_def - mode->height); + sensor->hblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HBLANK, + IMX111_HBLANK_MIN, + IMX111_HTL_MAX - mode->width, 1, + mode->htl_def - mode->width); + + /* + * The maximum coarse integration time is the frame length in lines + * minus five. + */ + sensor->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, + IMX111_INTEGRATION_TIME_MIN, + IMX111_PIXEL_ARRAY_HEIGHT - + IMX111_INTEGRATION_TIME_OFFSET, + IMX111_INTEGRATION_TIME_STEP, + IMX111_PIXEL_ARRAY_HEIGHT - + IMX111_INTEGRATION_TIME_OFFSET); + + v4l2_ctrl_new_fwnode_properties(hdl, ops, &props); + + v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(test_pattern_menu) - 1, 0, 0, + test_pattern_menu); + for (i = 0; i < 4; i++) { + /* + * The assumption is that + * TEST_PATTERN_GREENR == TEST_PATTERN_RED + 1 + * TEST_PATTERN_BLUE == TEST_PATTERN_RED + 2 + * TEST_PATTERN_GREENB == TEST_PATTERN_RED + 3 + */ + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_TEST_PATTERN_RED + i, + IMX111_TESTP_COLOUR_MIN, + IMX111_TESTP_COLOUR_MAX, + IMX111_TESTP_COLOUR_STEP, + IMX111_TESTP_COLOUR_MAX); + /* The "Solid color" pattern is white by default */ + } + + if (hdl->error) + return hdl->error; + + sensor->sd.ctrl_handler = hdl; + + return 0; +}; + +static int imx111_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct imx111 *sensor = sd_to_imx111(sd); + struct device *dev = regmap_get_device(sensor->regmap); + const struct imx111_mode *mode = sensor->cur_mode; + int ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + /* Apply default values of current mode */ + ret = cci_multi_reg_write(sensor->regmap, mode->reg_list.regs, + mode->reg_list.num_of_regs, NULL); + if (ret < 0) { + dev_err(dev, "Failed to initialize the sensor\n"); + goto err_rpm_put; + } + + cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, + IMX111_GROUP_WRITE_ON, + IMX111_GROUP_WRITE_ON, &ret); + cci_write(sensor->regmap, IMX111_DATA_DEPTH, + sensor->data_depth | sensor->data_depth << 8, &ret); + cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, + IMX111_GROUP_WRITE_ON, 0, &ret); + + if (ret) + goto err_rpm_put; + + ret = __v4l2_ctrl_handler_setup(&sensor->hdl); + if (ret) + goto err_rpm_put; + + ret = cci_write(sensor->regmap, IMX111_STREAMING_MODE, + IMX111_MODE_STREAMING, NULL); + if (ret) + dev_err(dev, "failed to start stream"); + + /* vflip and hflip cannot change during streaming */ + __v4l2_ctrl_grab(sensor->vflip, true); + __v4l2_ctrl_grab(sensor->hflip, true); + + msleep(30); + + return 0; + +err_rpm_put: + pm_runtime_put_autosuspend(dev); + return ret; +} + +static int imx111_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct imx111 *sensor = sd_to_imx111(sd); + struct device *dev = regmap_get_device(sensor->regmap); + int ret; + + ret = cci_write(sensor->regmap, IMX111_STREAMING_MODE, + IMX111_MODE_STANDBY, NULL); + if (ret) + dev_err(dev, "failed to stop stream\n"); + + __v4l2_ctrl_grab(sensor->vflip, false); + __v4l2_ctrl_grab(sensor->hflip, false); + + pm_runtime_put_autosuspend(dev); + + return ret; +} + +static int imx111_initialize(struct imx111 *sensor) +{ + struct device *dev = regmap_get_device(sensor->regmap); + int ret = 0; + + /* Configure the PLL. */ + cci_write(sensor->regmap, IMX111_PRE_PLL_CLK_DIVIDER_PLL1, + sensor->pll->pre_div, &ret); + cci_write(sensor->regmap, IMX111_PLL_MULTIPLIER_PLL1, + sensor->pll->mult, &ret); + cci_write(sensor->regmap, IMX111_POST_DIVIDER, + IMX111_POST_DIVIDER_DIV1, &ret); + cci_write(sensor->regmap, IMX111_PLL_SETTLING_TIME, + to_settle_delay(sensor->pll->extclk_rate), &ret); + + cci_multi_reg_write(sensor->regmap, imx111_global_init, + ARRAY_SIZE(imx111_global_init), &ret); + if (ret < 0) { + dev_err(dev, "Failed to initialize the sensor\n"); + return ret; + } + + return 0; +} + +/* ---------------------------------------------------------------------------- + * IMX111 Pad Subdev Init and Operations + */ +static int imx111_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct imx111 *sensor = sd_to_imx111(sd); + + if (code->index >= ARRAY_SIZE(imx111_mbus_formats) / 4) + return -EINVAL; + + code->code = imx111_get_format_code(sensor, + imx111_mbus_formats[code->index * + 4], false); + + return 0; +} + +static int imx111_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct imx111 *sensor = sd_to_imx111(sd); + u32 code; + + if (fse->index >= ARRAY_SIZE(imx111_modes)) + return -EINVAL; + + code = imx111_get_format_code(sensor, fse->code, true); + if (fse->code != code) + return -EINVAL; + + fse->min_width = imx111_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = imx111_modes[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static int imx111_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct imx111 *sensor = sd_to_imx111(sd); + struct v4l2_mbus_framefmt *mbus_fmt = &format->format; + struct v4l2_mbus_framefmt *fmt; + const struct imx111_mode *mode; + + mode = v4l2_find_nearest_size(imx111_modes, ARRAY_SIZE(imx111_modes), + width, height, + mbus_fmt->width, mbus_fmt->height); + + fmt = v4l2_subdev_state_get_format(state, format->pad); + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + int ret; + + sensor->cur_mode = mode; + sensor->data_depth = imx111_get_format_bpp(fmt); + + ret = __v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate, + div_u64(sensor->pixel_clk_raw, + 2 * + sensor->data_depth)); + if (ret) + return ret; + + ret = __v4l2_ctrl_modify_range(sensor->vblank, + IMX111_VBLANK_MIN, + IMX111_VTL_MAX - mode->height, + 1, + mode->vtl_def - mode->height); + if (ret) + return ret; + + ret = __v4l2_ctrl_s_ctrl(sensor->vblank, mode->vtl_def - + mode->height); + if (ret) + return ret; + + ret = __v4l2_ctrl_modify_range(sensor->hblank, + IMX111_HBLANK_MIN, + IMX111_HTL_MAX - mode->width, + 1, + mode->htl_def - mode->width); + if (ret) + return ret; + + ret = __v4l2_ctrl_s_ctrl(sensor->hblank, mode->htl_def - + mode->width); + if (ret) + return ret; + } + + fmt->code = imx111_get_format_code(sensor, mbus_fmt->code, false); + fmt->width = mode->width; + fmt->height = mode->height; + fmt->colorspace = V4L2_COLORSPACE_RAW; + + *mbus_fmt = *fmt; + + return 0; +} + +static int imx111_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + struct imx111 *sensor = sd_to_imx111(sd); + const struct imx111_mode *mode = sensor->cur_mode; + struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_subdev_state_get_format(sd_state, 0); + + fmt->code = MEDIA_BUS_FMT_SGBRG10_1X10; + fmt->width = mode->width; + fmt->height = mode->height; + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_RAW; + fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; + fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; + fmt->xfer_func = V4L2_XFER_FUNC_NONE; + + return 0; +} + +static const struct v4l2_subdev_video_ops imx111_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_pad_ops imx111_pad_ops = { + .enum_mbus_code = imx111_enum_mbus_code, + .enum_frame_size = imx111_enum_frame_size, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = imx111_set_format, + .enable_streams = imx111_enable_streams, + .disable_streams = imx111_disable_streams, +}; + +static const struct v4l2_subdev_ops imx111_subdev_ops = { + .video = &imx111_video_ops, + .pad = &imx111_pad_ops, +}; + +static const struct media_entity_operations imx111_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_internal_ops imx111_internal_ops = { + .init_state = imx111_init_state, +}; + +static int imx111_init_subdev(struct imx111 *sensor, struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct v4l2_subdev *sd = &sensor->sd; + struct media_pad *pad = &sensor->pad; + struct v4l2_ctrl_handler *hdl = &sensor->hdl; + int ret; + + /* Initialize the subdev. */ + v4l2_i2c_subdev_init(sd, client, &imx111_subdev_ops); + + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->internal_ops = &imx111_internal_ops; + + /* Initialize the media entity. */ + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + sd->entity.ops = &imx111_subdev_entity_ops; + pad->flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&sd->entity, 1, pad); + if (ret < 0) { + dev_err(dev, "failed to init entity pads: %d", ret); + return ret; + } + + /* Initialize the control handler. */ + ret = imx111_init_controls(sensor); + if (ret) + goto error; + + return 0; +error: + v4l2_ctrl_handler_free(hdl); + media_entity_cleanup(&sd->entity); + return ret; +}; + +/* ---------------------------------------------------------------------------- + * Power Management + */ + +static int imx111_power_on(struct imx111 *sensor) +{ + int ret; + + if (sensor->reset) + gpiod_set_value(sensor->reset, 1); + + ret = regulator_bulk_enable(ARRAY_SIZE(imx111_supplies), + sensor->supplies); + if (ret < 0) + return ret; + + usleep_range(500, 600); + + if (sensor->reset) + gpiod_set_value(sensor->reset, 0); + + usleep_range(200, 250); + + ret = clk_prepare_enable(sensor->extclk); + if (ret < 0) + goto error_regulator; + + usleep_range(200, 250); + + return 0; + +error_regulator: + regulator_bulk_disable(ARRAY_SIZE(imx111_supplies), sensor->supplies); + return ret; +} + +static void imx111_power_off(struct imx111 *sensor) +{ + if (sensor->reset) + gpiod_set_value(sensor->reset, 1); + usleep_range(1000, 2000); + + clk_disable_unprepare(sensor->extclk); + regulator_bulk_disable(ARRAY_SIZE(imx111_supplies), sensor->supplies); +} + +static int __maybe_unused imx111_pm_runtime_resume(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct imx111 *sensor = sd_to_imx111(sd); + int ret; + + ret = imx111_power_on(sensor); + if (ret) + return ret; + + ret = imx111_initialize(sensor); + if (ret) { + imx111_power_off(sensor); + return ret; + } + + return 0; +} + +static int __maybe_unused imx111_pm_runtime_suspend(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct imx111 *sensor = sd_to_imx111(sd); + + imx111_power_off(sensor); + + return 0; +} + +static const struct dev_pm_ops imx111_pm_ops = { + SET_RUNTIME_PM_OPS(imx111_pm_runtime_suspend, + imx111_pm_runtime_resume, NULL) +}; + +/* ---------------------------------------------------------------------------- + * Probe & Remove + */ + +static int imx111_identify_module(struct imx111 *sensor) +{ + struct device *dev = regmap_get_device(sensor->regmap); + u64 value, revision, manufacturer; + int ret = 0; + + ret = cci_read(sensor->regmap, IMX111_PRODUCT_ID, &value, NULL); + if (ret) + return ret; + + if (value != IMX111_CHIP_ID) { + dev_err(dev, "chip id mismatch: %x!=%04llx", IMX111_CHIP_ID, + value); + return -ENXIO; + } + + cci_read(sensor->regmap, IMX111_REVISION, &revision, &ret); + cci_read(sensor->regmap, IMX111_MANUFACTURER_ID, &manufacturer, &ret); + + dev_dbg(dev, "module IMX%03llx rev. %llu manufacturer %llu\n", + value, revision, manufacturer); + + return ret; +} + +static int imx111_clk_init(struct imx111 *sensor) +{ + struct device *dev = regmap_get_device(sensor->regmap); + u32 ndata_lanes = sensor->bus_cfg.bus.mipi_csi2.num_data_lanes; + u64 extclk_rate, system_clk; + unsigned int i; + + extclk_rate = clk_get_rate(sensor->extclk); + if (!extclk_rate) + return dev_err_probe(dev, -EINVAL, "EXTCLK rate unknown\n"); + + for (i = 0; i < ARRAY_SIZE(imx111_pll); i++) { + if (clk_get_rate(sensor->extclk) == + imx111_pll[i].extclk_rate) { + sensor->pll = &imx111_pll[i]; + break; + } + } + if (!sensor->pll) + return dev_err_probe(dev, -EINVAL, + "Unsupported EXTCLK rate %llu\n", + extclk_rate); + + system_clk = div_u64(extclk_rate, sensor->pll->pre_div) * + sensor->pll->mult; + + /* + * Pixel clock or Logic clock is used for internal image processing is + * generated by dividing into 1/10 or 1/8 frequency according to the + * word length of the CSI2 interface. This clock is designating the + * pixel rate and used as the base of integration time, frame rate etc. + */ + sensor->pixel_clk_raw = system_clk * ndata_lanes; + + /* + * The CSI-2 bus is clocked for 16-bit per pixel, transmitted in DDR + * over n lanes for RAW10 default format. + */ + sensor->default_link_freq = div_u64(sensor->pixel_clk_raw * 8, + 2 * IMX111_DATA_DEPTH_RAW10); + + if (sensor->bus_cfg.nr_of_link_frequencies != 1 || + sensor->bus_cfg.link_frequencies[0] != sensor->default_link_freq) + return dev_err_probe(dev, -EINVAL, + "Invalid link-frequency, expected %llu\n", + sensor->default_link_freq); + + return 0; +} + +static int imx111_parse_dt(struct imx111 *sensor) +{ + struct device *dev = regmap_get_device(sensor->regmap); + struct fwnode_handle *fwnode = dev_fwnode(dev); + struct fwnode_handle *ep; + int ret; + + ep = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!ep) { + dev_err(dev, "No endpoint found\n"); + return -EINVAL; + } + + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &sensor->bus_cfg); + fwnode_handle_put(ep); + if (ret < 0) { + dev_err(dev, "Failed to parse endpoint\n"); + goto error; + } + + sensor->bus_cfg.bus_type = V4L2_MBUS_CSI2_DPHY; + + /* Check the number of MIPI CSI2 data lanes */ + if (sensor->bus_cfg.bus.mipi_csi2.num_data_lanes > 2) { + dev_err(dev, "number of lanes is more than 2\n"); + ret = -EINVAL; + goto error; + } + + return 0; + +error: + v4l2_fwnode_endpoint_free(&sensor->bus_cfg); + return ret; +} + +static int imx111_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct imx111 *sensor; + int ret; + + sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); + if (!sensor) + return -ENOMEM; + + sensor->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(sensor->regmap)) + return dev_err_probe(dev, PTR_ERR(sensor->regmap), + "Failed to allocate register map\n"); + + sensor->extclk = devm_v4l2_sensor_clk_get(dev, NULL); + if (IS_ERR(sensor->extclk)) + return dev_err_probe(dev, PTR_ERR(sensor->extclk), + "Failed to get clock\n"); + + sensor->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(sensor->reset)) + return dev_err_probe(dev, PTR_ERR(sensor->reset), + "Failed to get reset GPIO\n"); + + ret = devm_regulator_bulk_get_const(dev, ARRAY_SIZE(imx111_supplies), + imx111_supplies, + &sensor->supplies); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to get regulators\n"); + + ret = imx111_parse_dt(sensor); + if (ret < 0) + return ret; + + ret = imx111_clk_init(sensor); + if (ret < 0) + goto error_ep_free; + + ret = imx111_power_on(sensor); + if (ret < 0) { + dev_err_probe(dev, ret, "Could not power on the device\n"); + goto error_ep_free; + } + + ret = imx111_identify_module(sensor); + if (ret < 0) { + dev_err_probe(dev, ret, "Could not identify module\n"); + goto error_power_off; + } + + sensor->cur_mode = &imx111_modes[IMX111_MODE_3280x2464]; + sensor->data_depth = IMX111_DATA_DEPTH_RAW10; + + ret = imx111_initialize(sensor); + if (ret < 0) + goto error_power_off; + + ret = imx111_init_subdev(sensor, client); + if (ret < 0) { + dev_err(dev, "failed to init controls: %d", ret); + goto error_v4l2_ctrl_handler_free; + } + + ret = v4l2_subdev_init_finalize(&sensor->sd); + if (ret) + goto error_v4l2_ctrl_handler_free; + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + ret = v4l2_async_register_subdev_sensor(&sensor->sd); + if (ret < 0) { + dev_err(dev, "failed to register V4L2 subdev: %d", ret); + goto error_pm; + } + + pm_runtime_set_autosuspend_delay(dev, 1000); + pm_runtime_use_autosuspend(dev); + pm_runtime_idle(dev); + + return 0; + +error_pm: + v4l2_subdev_cleanup(&sensor->sd); + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + +error_v4l2_ctrl_handler_free: + v4l2_ctrl_handler_free(&sensor->hdl); + media_entity_cleanup(&sensor->sd.entity); + +error_power_off: + imx111_power_off(sensor); + +error_ep_free: + v4l2_fwnode_endpoint_free(&sensor->bus_cfg); + + return ret; +} + +static void imx111_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx111 *sensor = sd_to_imx111(sd); + + v4l2_async_unregister_subdev(&sensor->sd); + v4l2_subdev_cleanup(sd); + media_entity_cleanup(&sensor->sd.entity); + v4l2_ctrl_handler_free(&sensor->hdl); + v4l2_fwnode_endpoint_free(&sensor->bus_cfg); + + /* + * Disable runtime PM. In case runtime PM is disabled in the kernel, + * make sure to turn power off manually. + */ + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) { + imx111_power_off(sensor); + pm_runtime_set_suspended(&client->dev); + } +} + +static const struct of_device_id imx111_of_match[] = { + { .compatible = "sony,imx111" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx111_of_match); + +static struct i2c_driver imx111_i2c_driver = { + .driver = { + .name = "imx111", + .of_match_table = imx111_of_match, + .pm = &imx111_pm_ops, + }, + .probe = imx111_probe, + .remove = imx111_remove, +}; +module_i2c_driver(imx111_i2c_driver); + +MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>"); +MODULE_DESCRIPTION("Sony IMX111 CMOS Image Sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/imx208.c b/drivers/media/i2c/imx208.c index 2184c90f7864..d5350bb46f14 100644 --- a/drivers/media/i2c/imx208.c +++ b/drivers/media/i2c/imx208.c @@ -2,13 +2,15 @@ // Copyright (C) 2021 Intel Corporation #include <linux/acpi.h> +#include <linux/clk.h> #include <linux/delay.h> #include <linux/i2c.h> #include <linux/module.h> #include <linux/pm_runtime.h> +#include <linux/unaligned.h> + #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> -#include <linux/unaligned.h> #define IMX208_REG_MODE_SELECT 0x0100 #define IMX208_MODE_STANDBY 0x00 @@ -268,6 +270,9 @@ static const struct imx208_mode supported_modes[] = { }; struct imx208 { + struct device *dev; + struct clk *clk; + struct v4l2_subdev sd; struct media_pad pad; @@ -372,7 +377,6 @@ static int imx208_write_reg(struct imx208 *imx208, u16 reg, u32 len, u32 val) static int imx208_write_regs(struct imx208 *imx208, const struct imx208_reg *regs, u32 len) { - struct i2c_client *client = v4l2_get_subdevdata(&imx208->sd); unsigned int i; int ret; @@ -380,7 +384,7 @@ static int imx208_write_regs(struct imx208 *imx208, ret = imx208_write_reg(imx208, regs[i].address, 1, regs[i].val); if (ret) { - dev_err_ratelimited(&client->dev, + dev_err_ratelimited(imx208->dev, "Failed to write reg 0x%4.4x. error = %d\n", regs[i].address, ret); @@ -431,14 +435,13 @@ static int imx208_set_ctrl(struct v4l2_ctrl *ctrl) { struct imx208 *imx208 = container_of(ctrl->handler, struct imx208, ctrl_handler); - struct i2c_client *client = v4l2_get_subdevdata(&imx208->sd); int ret; /* * Applying V4L2 control value only happens * when power is up for streaming */ - if (!pm_runtime_get_if_in_use(&client->dev)) + if (!pm_runtime_get_if_in_use(imx208->dev)) return 0; switch (ctrl->id) { @@ -471,13 +474,13 @@ static int imx208_set_ctrl(struct v4l2_ctrl *ctrl) break; default: ret = -EINVAL; - dev_err(&client->dev, + dev_err(imx208->dev, "ctrl(id:0x%x,val:0x%x) is not handled\n", ctrl->id, ctrl->val); break; } - pm_runtime_put(&client->dev); + pm_runtime_put(imx208->dev); return ret; } @@ -620,7 +623,6 @@ static int imx208_set_pad_format(struct v4l2_subdev *sd, static int imx208_identify_module(struct imx208 *imx208) { - struct i2c_client *client = v4l2_get_subdevdata(&imx208->sd); int ret; u32 val; @@ -630,13 +632,13 @@ static int imx208_identify_module(struct imx208 *imx208) ret = imx208_read_reg(imx208, IMX208_REG_CHIP_ID, 2, &val); if (ret) { - dev_err(&client->dev, "failed to read chip id %x\n", + dev_err(imx208->dev, "failed to read chip id %x\n", IMX208_CHIP_ID); return ret; } if (val != IMX208_CHIP_ID) { - dev_err(&client->dev, "chip id mismatch: %x!=%x\n", + dev_err(imx208->dev, "chip id mismatch: %x!=%x\n", IMX208_CHIP_ID, val); return -EIO; } @@ -649,7 +651,6 @@ static int imx208_identify_module(struct imx208 *imx208) /* Start streaming */ static int imx208_start_streaming(struct imx208 *imx208) { - struct i2c_client *client = v4l2_get_subdevdata(&imx208->sd); const struct imx208_reg_list *reg_list; int ret, link_freq_index; @@ -662,7 +663,7 @@ static int imx208_start_streaming(struct imx208 *imx208) reg_list = &link_freq_configs[link_freq_index].reg_list; ret = imx208_write_regs(imx208, reg_list->regs, reg_list->num_of_regs); if (ret) { - dev_err(&client->dev, "%s failed to set plls\n", __func__); + dev_err(imx208->dev, "%s failed to set plls\n", __func__); return ret; } @@ -670,7 +671,7 @@ static int imx208_start_streaming(struct imx208 *imx208) reg_list = &imx208->cur_mode->reg_list; ret = imx208_write_regs(imx208, reg_list->regs, reg_list->num_of_regs); if (ret) { - dev_err(&client->dev, "%s failed to set mode\n", __func__); + dev_err(imx208->dev, "%s failed to set mode\n", __func__); return ret; } @@ -687,14 +688,13 @@ static int imx208_start_streaming(struct imx208 *imx208) /* Stop streaming */ static int imx208_stop_streaming(struct imx208 *imx208) { - struct i2c_client *client = v4l2_get_subdevdata(&imx208->sd); int ret; /* set stream off register */ ret = imx208_write_reg(imx208, IMX208_REG_MODE_SELECT, 1, IMX208_MODE_STANDBY); if (ret) - dev_err(&client->dev, "%s failed to set stream\n", __func__); + dev_err(imx208->dev, "%s failed to set stream\n", __func__); /* * Return success even if it was an error, as there is nothing the @@ -706,13 +706,12 @@ static int imx208_stop_streaming(struct imx208 *imx208) static int imx208_set_stream(struct v4l2_subdev *sd, int enable) { struct imx208 *imx208 = to_imx208(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); int ret = 0; mutex_lock(&imx208->imx208_mx); if (enable) { - ret = pm_runtime_resume_and_get(&client->dev); + ret = pm_runtime_resume_and_get(imx208->dev); if (ret) { mutex_unlock(&imx208->imx208_mx); return ret; @@ -727,7 +726,7 @@ static int imx208_set_stream(struct v4l2_subdev *sd, int enable) goto err_rpm_put; } else { imx208_stop_streaming(imx208); - pm_runtime_put(&client->dev); + pm_runtime_put(imx208->dev); } mutex_unlock(&imx208->imx208_mx); @@ -739,7 +738,7 @@ static int imx208_set_stream(struct v4l2_subdev *sd, int enable) return ret; err_rpm_put: - pm_runtime_put(&client->dev); + pm_runtime_put(imx208->dev); mutex_unlock(&imx208->imx208_mx); return ret; @@ -778,7 +777,7 @@ static int imx208_read_otp(struct imx208 *imx208) if (imx208->otp_read) goto out_unlock; - ret = pm_runtime_resume_and_get(&client->dev); + ret = pm_runtime_resume_and_get(imx208->dev); if (ret) goto out_unlock; @@ -805,7 +804,7 @@ static int imx208_read_otp(struct imx208 *imx208) } out_pm_put: - pm_runtime_put(&client->dev); + pm_runtime_put(imx208->dev); out_unlock: mutex_unlock(&imx208->imx208_mx); @@ -814,7 +813,7 @@ out_unlock: } static ssize_t otp_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct i2c_client *client = to_i2c_client(kobj_to_dev(kobj)); @@ -835,7 +834,6 @@ static const BIN_ATTR_RO(otp, IMX208_OTP_SIZE); /* Initialize control handlers */ static int imx208_init_controls(struct imx208 *imx208) { - struct i2c_client *client = v4l2_get_subdevdata(&imx208->sd); struct v4l2_ctrl_handler *ctrl_hdlr = &imx208->ctrl_handler; s64 exposure_max; s64 vblank_def; @@ -914,7 +912,7 @@ static int imx208_init_controls(struct imx208 *imx208) if (ctrl_hdlr->error) { ret = ctrl_hdlr->error; - dev_err(&client->dev, "%s control init failed (%d)\n", + dev_err(imx208->dev, "%s control init failed (%d)\n", __func__, ret); goto error; } @@ -938,31 +936,36 @@ static void imx208_free_controls(struct imx208 *imx208) static int imx208_probe(struct i2c_client *client) { struct imx208 *imx208; + unsigned long freq; int ret; bool full_power; - u32 val = 0; - - device_property_read_u32(&client->dev, "clock-frequency", &val); - if (val != 19200000) { - dev_err(&client->dev, - "Unsupported clock-frequency %u. Expected 19200000.\n", - val); - return -EINVAL; - } imx208 = devm_kzalloc(&client->dev, sizeof(*imx208), GFP_KERNEL); if (!imx208) return -ENOMEM; + imx208->dev = &client->dev; + + imx208->clk = devm_v4l2_sensor_clk_get(imx208->dev, NULL); + if (IS_ERR(imx208->clk)) + return dev_err_probe(imx208->dev, PTR_ERR(imx208->clk), + "failed to get clock\n"); + + freq = clk_get_rate(imx208->clk); + if (freq != 19200000) + return dev_err_probe(imx208->dev, -EINVAL, + "external clock %lu is not supported\n", + freq); + /* Initialize subdev */ v4l2_i2c_subdev_init(&imx208->sd, client, &imx208_subdev_ops); - full_power = acpi_dev_state_d0(&client->dev); + full_power = acpi_dev_state_d0(imx208->dev); if (full_power) { /* Check module identity */ ret = imx208_identify_module(imx208); if (ret) { - dev_err(&client->dev, "failed to find sensor: %d", ret); + dev_err(imx208->dev, "failed to find sensor: %d", ret); goto error_probe; } } @@ -972,7 +975,7 @@ static int imx208_probe(struct i2c_client *client) ret = imx208_init_controls(imx208); if (ret) { - dev_err(&client->dev, "failed to init controls: %d", ret); + dev_err(imx208->dev, "failed to init controls: %d", ret); goto error_probe; } @@ -985,7 +988,7 @@ static int imx208_probe(struct i2c_client *client) imx208->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&imx208->sd.entity, 1, &imx208->pad); if (ret) { - dev_err(&client->dev, "%s failed:%d\n", __func__, ret); + dev_err(imx208->dev, "%s failed:%d\n", __func__, ret); goto error_handler_free; } @@ -993,17 +996,17 @@ static int imx208_probe(struct i2c_client *client) if (ret < 0) goto error_media_entity; - ret = device_create_bin_file(&client->dev, &bin_attr_otp); + ret = device_create_bin_file(imx208->dev, &bin_attr_otp); if (ret) { - dev_err(&client->dev, "sysfs otp creation failed\n"); + dev_err(imx208->dev, "sysfs otp creation failed\n"); goto error_async_subdev; } /* Set the device's state to active if it's in D0 state. */ if (full_power) - pm_runtime_set_active(&client->dev); - pm_runtime_enable(&client->dev); - pm_runtime_idle(&client->dev); + pm_runtime_set_active(imx208->dev); + pm_runtime_enable(imx208->dev); + pm_runtime_idle(imx208->dev); return 0; @@ -1027,13 +1030,13 @@ static void imx208_remove(struct i2c_client *client) struct v4l2_subdev *sd = i2c_get_clientdata(client); struct imx208 *imx208 = to_imx208(sd); - device_remove_bin_file(&client->dev, &bin_attr_otp); + device_remove_bin_file(imx208->dev, &bin_attr_otp); v4l2_async_unregister_subdev(sd); media_entity_cleanup(&sd->entity); imx208_free_controls(imx208); - pm_runtime_disable(&client->dev); - pm_runtime_set_suspended(&client->dev); + pm_runtime_disable(imx208->dev); + pm_runtime_set_suspended(imx208->dev); mutex_destroy(&imx208->imx208_mx); } diff --git a/drivers/media/i2c/imx214.c b/drivers/media/i2c/imx214.c index 4962cfe7c83d..d4945b192776 100644 --- a/drivers/media/i2c/imx214.c +++ b/drivers/media/i2c/imx214.c @@ -15,26 +15,187 @@ #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <media/media-entity.h> +#include <media/v4l2-cci.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-fwnode.h> #include <media/v4l2-subdev.h> -#define IMX214_REG_MODE_SELECT 0x0100 +#include "ccs-pll.h" + +/* Chip ID */ +#define IMX214_REG_CHIP_ID CCI_REG16(0x0016) +#define IMX214_CHIP_ID 0x0214 + +#define IMX214_REG_MODE_SELECT CCI_REG8(0x0100) #define IMX214_MODE_STANDBY 0x00 #define IMX214_MODE_STREAMING 0x01 -#define IMX214_DEFAULT_CLK_FREQ 24000000 -#define IMX214_DEFAULT_LINK_FREQ 480000000 -#define IMX214_DEFAULT_PIXEL_RATE ((IMX214_DEFAULT_LINK_FREQ * 8LL) / 10) +#define IMX214_REG_FAST_STANDBY_CTRL CCI_REG8(0x0106) + +#define IMX214_DEFAULT_LINK_FREQ 600000000 +/* Keep wrong link frequency for backward compatibility */ +#define IMX214_DEFAULT_LINK_FREQ_LEGACY 480000000 #define IMX214_FPS 30 -#define IMX214_MBUS_CODE MEDIA_BUS_FMT_SRGGB10_1X10 + +/* V-TIMING internal */ +#define IMX214_REG_FRM_LENGTH_LINES CCI_REG16(0x0340) +#define IMX214_VTS_MAX 0xffff + +#define IMX214_VBLANK_MIN 890 + +/* HBLANK control - read only */ +#define IMX214_PPL_DEFAULT 5008 /* Exposure control */ -#define IMX214_REG_EXPOSURE 0x0202 -#define IMX214_EXPOSURE_MIN 0 -#define IMX214_EXPOSURE_MAX 3184 +#define IMX214_REG_EXPOSURE CCI_REG16(0x0202) +#define IMX214_EXPOSURE_OFFSET 10 +#define IMX214_EXPOSURE_MIN 1 #define IMX214_EXPOSURE_STEP 1 #define IMX214_EXPOSURE_DEFAULT 3184 +#define IMX214_REG_EXPOSURE_RATIO CCI_REG8(0x0222) +#define IMX214_REG_SHORT_EXPOSURE CCI_REG16(0x0224) + +/* Analog gain control */ +#define IMX214_REG_ANALOG_GAIN CCI_REG16(0x0204) +#define IMX214_REG_SHORT_ANALOG_GAIN CCI_REG16(0x0216) +#define IMX214_ANA_GAIN_MIN 0 +#define IMX214_ANA_GAIN_MAX 448 +#define IMX214_ANA_GAIN_STEP 1 +#define IMX214_ANA_GAIN_DEFAULT 0x0 + +/* Digital gain control */ +#define IMX214_REG_DIG_GAIN_GREENR CCI_REG16(0x020e) +#define IMX214_REG_DIG_GAIN_RED CCI_REG16(0x0210) +#define IMX214_REG_DIG_GAIN_BLUE CCI_REG16(0x0212) +#define IMX214_REG_DIG_GAIN_GREENB CCI_REG16(0x0214) +#define IMX214_DGTL_GAIN_MIN 0x0100 +#define IMX214_DGTL_GAIN_MAX 0x0fff +#define IMX214_DGTL_GAIN_DEFAULT 0x0100 +#define IMX214_DGTL_GAIN_STEP 1 + +#define IMX214_REG_ORIENTATION CCI_REG8(0x0101) + +#define IMX214_REG_MASK_CORR_FRAMES CCI_REG8(0x0105) +#define IMX214_CORR_FRAMES_TRANSMIT 0 +#define IMX214_CORR_FRAMES_MASK 1 + +#define IMX214_REG_CSI_DATA_FORMAT CCI_REG16(0x0112) +#define IMX214_CSI_DATA_FORMAT_RAW8 0x0808 +#define IMX214_CSI_DATA_FORMAT_RAW10 0x0A0A +#define IMX214_CSI_DATA_FORMAT_COMP6 0x0A06 +#define IMX214_CSI_DATA_FORMAT_COMP8 0x0A08 +#define IMX214_BITS_PER_PIXEL_MASK 0xFF + +#define IMX214_REG_CSI_LANE_MODE CCI_REG8(0x0114) +#define IMX214_CSI_2_LANE_MODE 1 +#define IMX214_CSI_4_LANE_MODE 3 + +#define IMX214_REG_EXCK_FREQ CCI_REG16(0x0136) +#define IMX214_EXCK_FREQ(n) ((n) * 256) /* n expressed in MHz */ + +#define IMX214_REG_TEMP_SENSOR_CONTROL CCI_REG8(0x0138) + +#define IMX214_REG_HDR_MODE CCI_REG8(0x0220) +#define IMX214_HDR_MODE_OFF 0 +#define IMX214_HDR_MODE_ON 1 + +#define IMX214_REG_HDR_RES_REDUCTION CCI_REG8(0x0221) +#define IMX214_HDR_RES_REDU_THROUGH 0x11 +#define IMX214_HDR_RES_REDU_2_BINNING 0x22 + +/* PLL settings */ +#define IMX214_REG_VTPXCK_DIV CCI_REG8(0x0301) +#define IMX214_REG_VTSYCK_DIV CCI_REG8(0x0303) +#define IMX214_REG_PREPLLCK_VT_DIV CCI_REG8(0x0305) +#define IMX214_REG_PLL_VT_MPY CCI_REG16(0x0306) +#define IMX214_REG_OPPXCK_DIV CCI_REG8(0x0309) +#define IMX214_REG_OPSYCK_DIV CCI_REG8(0x030b) +#define IMX214_REG_PLL_MULT_DRIV CCI_REG8(0x0310) +#define IMX214_PLL_SINGLE 0 +#define IMX214_PLL_DUAL 1 + +#define IMX214_REG_LINE_LENGTH_PCK CCI_REG16(0x0342) +#define IMX214_REG_X_ADD_STA CCI_REG16(0x0344) +#define IMX214_REG_Y_ADD_STA CCI_REG16(0x0346) +#define IMX214_REG_X_ADD_END CCI_REG16(0x0348) +#define IMX214_REG_Y_ADD_END CCI_REG16(0x034a) +#define IMX214_REG_X_OUTPUT_SIZE CCI_REG16(0x034c) +#define IMX214_REG_Y_OUTPUT_SIZE CCI_REG16(0x034e) +#define IMX214_REG_X_EVEN_INC CCI_REG8(0x0381) +#define IMX214_REG_X_ODD_INC CCI_REG8(0x0383) +#define IMX214_REG_Y_EVEN_INC CCI_REG8(0x0385) +#define IMX214_REG_Y_ODD_INC CCI_REG8(0x0387) + +#define IMX214_REG_SCALE_MODE CCI_REG8(0x0401) +#define IMX214_SCALE_NONE 0 +#define IMX214_SCALE_HORIZONTAL 1 +#define IMX214_SCALE_FULL 2 +#define IMX214_REG_SCALE_M CCI_REG16(0x0404) + +#define IMX214_REG_DIG_CROP_X_OFFSET CCI_REG16(0x0408) +#define IMX214_REG_DIG_CROP_Y_OFFSET CCI_REG16(0x040a) +#define IMX214_REG_DIG_CROP_WIDTH CCI_REG16(0x040c) +#define IMX214_REG_DIG_CROP_HEIGHT CCI_REG16(0x040e) + +#define IMX214_REG_REQ_LINK_BIT_RATE CCI_REG32(0x0820) +#define IMX214_LINK_BIT_RATE_MBPS(n) ((n) << 16) + +/* Binning mode */ +#define IMX214_REG_BINNING_MODE CCI_REG8(0x0900) +#define IMX214_BINNING_NONE 0 +#define IMX214_BINNING_ENABLE 1 +#define IMX214_REG_BINNING_TYPE CCI_REG8(0x0901) +#define IMX214_REG_BINNING_WEIGHTING CCI_REG8(0x0902) +#define IMX214_BINNING_AVERAGE 0x00 +#define IMX214_BINNING_SUMMED 0x01 +#define IMX214_BINNING_BAYER 0x02 + +#define IMX214_REG_SING_DEF_CORR_EN CCI_REG8(0x0b06) +#define IMX214_SING_DEF_CORR_OFF 0 +#define IMX214_SING_DEF_CORR_ON 1 + +/* AWB control */ +#define IMX214_REG_ABS_GAIN_GREENR CCI_REG16(0x0b8e) +#define IMX214_REG_ABS_GAIN_RED CCI_REG16(0x0b90) +#define IMX214_REG_ABS_GAIN_BLUE CCI_REG16(0x0b92) +#define IMX214_REG_ABS_GAIN_GREENB CCI_REG16(0x0b94) + +#define IMX214_REG_RMSC_NR_MODE CCI_REG8(0x3001) +#define IMX214_REG_STATS_OUT_EN CCI_REG8(0x3013) +#define IMX214_STATS_OUT_OFF 0 +#define IMX214_STATS_OUT_ON 1 + +/* Chroma noise reduction */ +#define IMX214_REG_NML_NR_EN CCI_REG8(0x30a2) +#define IMX214_NML_NR_OFF 0 +#define IMX214_NML_NR_ON 1 + +#define IMX214_REG_EBD_SIZE_V CCI_REG8(0x5041) +#define IMX214_EBD_NO 0 +#define IMX214_EBD_4_LINE 4 + +#define IMX214_REG_RG_STATS_LMT CCI_REG16(0x6d12) +#define IMX214_RG_STATS_LMT_10_BIT 0x03FF +#define IMX214_RG_STATS_LMT_14_BIT 0x3FFF + +#define IMX214_REG_ATR_FAST_MOVE CCI_REG8(0x9300) + +/* Test Pattern Control */ +#define IMX214_REG_TEST_PATTERN CCI_REG16(0x0600) +#define IMX214_TEST_PATTERN_DISABLE 0 +#define IMX214_TEST_PATTERN_SOLID_COLOR 1 +#define IMX214_TEST_PATTERN_COLOR_BARS 2 +#define IMX214_TEST_PATTERN_GREY_COLOR 3 +#define IMX214_TEST_PATTERN_PN9 4 + +/* Test pattern colour components */ +#define IMX214_REG_TESTP_RED CCI_REG16(0x0602) +#define IMX214_REG_TESTP_GREENR CCI_REG16(0x0604) +#define IMX214_REG_TESTP_BLUE CCI_REG16(0x0606) +#define IMX214_REG_TESTP_GREENB CCI_REG16(0x0608) +#define IMX214_TESTP_COLOUR_MIN 0 +#define IMX214_TESTP_COLOUR_MAX 0x03ff +#define IMX214_TESTP_COLOUR_STEP 1 /* IMX214 native and active pixel array size */ #define IMX214_NATIVE_WIDTH 4224U @@ -52,372 +213,282 @@ static const char * const imx214_supply_name[] = { #define IMX214_NUM_SUPPLIES ARRAY_SIZE(imx214_supply_name) +/* + * The supported formats. + * This table MUST contain 4 entries per format, to cover the various flip + * combinations in the order + * - no flip + * - h flip + * - v flip + * - h&v flips + */ +static const u32 imx214_mbus_formats[] = { + MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SBGGR10_1X10, +}; + +static const char * const imx214_test_pattern_menu[] = { + "Disabled", + "Color Bars", + "Solid Color", + "Grey Color Bars", + "PN9" +}; + +static const int imx214_test_pattern_val[] = { + IMX214_TEST_PATTERN_DISABLE, + IMX214_TEST_PATTERN_COLOR_BARS, + IMX214_TEST_PATTERN_SOLID_COLOR, + IMX214_TEST_PATTERN_GREY_COLOR, + IMX214_TEST_PATTERN_PN9, +}; + struct imx214 { struct device *dev; struct clk *xclk; struct regmap *regmap; + struct ccs_pll pll; + + struct v4l2_fwnode_endpoint bus_cfg; + struct v4l2_subdev sd; struct media_pad pad; - struct v4l2_mbus_framefmt fmt; - struct v4l2_rect crop; struct v4l2_ctrl_handler ctrls; struct v4l2_ctrl *pixel_rate; struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; struct v4l2_ctrl *exposure; struct v4l2_ctrl *unit_size; + struct { + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + }; struct regulator_bulk_data supplies[IMX214_NUM_SUPPLIES]; struct gpio_desc *enable_gpio; - - /* - * Serialize control access, get/set format, get selection - * and start streaming. - */ - struct mutex mutex; -}; - -struct reg_8 { - u16 addr; - u8 val; -}; - -enum { - IMX214_TABLE_WAIT_MS = 0, - IMX214_TABLE_END, - IMX214_MAX_RETRIES, - IMX214_WAIT_MS }; /*From imx214_mode_tbls.h*/ -static const struct reg_8 mode_4096x2304[] = { - {0x0114, 0x03}, - {0x0220, 0x00}, - {0x0221, 0x11}, - {0x0222, 0x01}, - {0x0340, 0x0C}, - {0x0341, 0x7A}, - {0x0342, 0x13}, - {0x0343, 0x90}, - {0x0344, 0x00}, - {0x0345, 0x38}, - {0x0346, 0x01}, - {0x0347, 0x98}, - {0x0348, 0x10}, - {0x0349, 0x37}, - {0x034A, 0x0A}, - {0x034B, 0x97}, - {0x0381, 0x01}, - {0x0383, 0x01}, - {0x0385, 0x01}, - {0x0387, 0x01}, - {0x0900, 0x00}, - {0x0901, 0x00}, - {0x0902, 0x00}, - {0x3000, 0x35}, - {0x3054, 0x01}, - {0x305C, 0x11}, - - {0x0112, 0x0A}, - {0x0113, 0x0A}, - {0x034C, 0x10}, - {0x034D, 0x00}, - {0x034E, 0x09}, - {0x034F, 0x00}, - {0x0401, 0x00}, - {0x0404, 0x00}, - {0x0405, 0x10}, - {0x0408, 0x00}, - {0x0409, 0x00}, - {0x040A, 0x00}, - {0x040B, 0x00}, - {0x040C, 0x10}, - {0x040D, 0x00}, - {0x040E, 0x09}, - {0x040F, 0x00}, - - {0x0301, 0x05}, - {0x0303, 0x02}, - {0x0305, 0x03}, - {0x0306, 0x00}, - {0x0307, 0x96}, - {0x0309, 0x0A}, - {0x030B, 0x01}, - {0x0310, 0x00}, - - {0x0820, 0x12}, - {0x0821, 0xC0}, - {0x0822, 0x00}, - {0x0823, 0x00}, - - {0x3A03, 0x09}, - {0x3A04, 0x50}, - {0x3A05, 0x01}, - - {0x0B06, 0x01}, - {0x30A2, 0x00}, - - {0x30B4, 0x00}, - - {0x3A02, 0xFF}, - - {0x3011, 0x00}, - {0x3013, 0x01}, - - {0x0202, 0x0C}, - {0x0203, 0x70}, - {0x0224, 0x01}, - {0x0225, 0xF4}, - - {0x0204, 0x00}, - {0x0205, 0x00}, - {0x020E, 0x01}, - {0x020F, 0x00}, - {0x0210, 0x01}, - {0x0211, 0x00}, - {0x0212, 0x01}, - {0x0213, 0x00}, - {0x0214, 0x01}, - {0x0215, 0x00}, - {0x0216, 0x00}, - {0x0217, 0x00}, - - {0x4170, 0x00}, - {0x4171, 0x10}, - {0x4176, 0x00}, - {0x4177, 0x3C}, - {0xAE20, 0x04}, - {0xAE21, 0x5C}, - - {IMX214_TABLE_WAIT_MS, 10}, - {0x0138, 0x01}, - {IMX214_TABLE_END, 0x00} +static const struct cci_reg_sequence mode_4096x2304[] = { + { IMX214_REG_HDR_MODE, IMX214_HDR_MODE_OFF }, + { IMX214_REG_HDR_RES_REDUCTION, IMX214_HDR_RES_REDU_THROUGH }, + { IMX214_REG_EXPOSURE_RATIO, 1 }, + { IMX214_REG_X_ADD_STA, 56 }, + { IMX214_REG_Y_ADD_STA, 408 }, + { IMX214_REG_X_ADD_END, 4151 }, + { IMX214_REG_Y_ADD_END, 2711 }, + { IMX214_REG_X_EVEN_INC, 1 }, + { IMX214_REG_X_ODD_INC, 1 }, + { IMX214_REG_Y_EVEN_INC, 1 }, + { IMX214_REG_Y_ODD_INC, 1 }, + { IMX214_REG_BINNING_MODE, IMX214_BINNING_NONE }, + { IMX214_REG_BINNING_TYPE, 0 }, + { IMX214_REG_BINNING_WEIGHTING, IMX214_BINNING_AVERAGE }, + { CCI_REG8(0x3000), 0x35 }, + { CCI_REG8(0x3054), 0x01 }, + { CCI_REG8(0x305C), 0x11 }, + + { IMX214_REG_CSI_DATA_FORMAT, IMX214_CSI_DATA_FORMAT_RAW10 }, + { IMX214_REG_X_OUTPUT_SIZE, 4096 }, + { IMX214_REG_Y_OUTPUT_SIZE, 2304 }, + { IMX214_REG_SCALE_MODE, IMX214_SCALE_NONE }, + { IMX214_REG_SCALE_M, 2 }, + { IMX214_REG_DIG_CROP_X_OFFSET, 0 }, + { IMX214_REG_DIG_CROP_Y_OFFSET, 0 }, + { IMX214_REG_DIG_CROP_WIDTH, 4096 }, + { IMX214_REG_DIG_CROP_HEIGHT, 2304 }, + + { CCI_REG8(0x3A03), 0x09 }, + { CCI_REG8(0x3A04), 0x50 }, + { CCI_REG8(0x3A05), 0x01 }, + + { IMX214_REG_SING_DEF_CORR_EN, IMX214_SING_DEF_CORR_ON }, + { IMX214_REG_NML_NR_EN, IMX214_NML_NR_OFF }, + + { CCI_REG8(0x30B4), 0x00 }, + + { CCI_REG8(0x3A02), 0xFF }, + + { CCI_REG8(0x3011), 0x00 }, + { IMX214_REG_STATS_OUT_EN, IMX214_STATS_OUT_ON }, + + { IMX214_REG_SHORT_EXPOSURE, 500 }, + + { CCI_REG8(0x4170), 0x00 }, + { CCI_REG8(0x4171), 0x10 }, + { CCI_REG8(0x4176), 0x00 }, + { CCI_REG8(0x4177), 0x3C }, + { CCI_REG8(0xAE20), 0x04 }, + { CCI_REG8(0xAE21), 0x5C }, }; -static const struct reg_8 mode_1920x1080[] = { - {0x0114, 0x03}, - {0x0220, 0x00}, - {0x0221, 0x11}, - {0x0222, 0x01}, - {0x0340, 0x0C}, - {0x0341, 0x7A}, - {0x0342, 0x13}, - {0x0343, 0x90}, - {0x0344, 0x04}, - {0x0345, 0x78}, - {0x0346, 0x03}, - {0x0347, 0xFC}, - {0x0348, 0x0B}, - {0x0349, 0xF7}, - {0x034A, 0x08}, - {0x034B, 0x33}, - {0x0381, 0x01}, - {0x0383, 0x01}, - {0x0385, 0x01}, - {0x0387, 0x01}, - {0x0900, 0x00}, - {0x0901, 0x00}, - {0x0902, 0x00}, - {0x3000, 0x35}, - {0x3054, 0x01}, - {0x305C, 0x11}, - - {0x0112, 0x0A}, - {0x0113, 0x0A}, - {0x034C, 0x07}, - {0x034D, 0x80}, - {0x034E, 0x04}, - {0x034F, 0x38}, - {0x0401, 0x00}, - {0x0404, 0x00}, - {0x0405, 0x10}, - {0x0408, 0x00}, - {0x0409, 0x00}, - {0x040A, 0x00}, - {0x040B, 0x00}, - {0x040C, 0x07}, - {0x040D, 0x80}, - {0x040E, 0x04}, - {0x040F, 0x38}, - - {0x0301, 0x05}, - {0x0303, 0x02}, - {0x0305, 0x03}, - {0x0306, 0x00}, - {0x0307, 0x96}, - {0x0309, 0x0A}, - {0x030B, 0x01}, - {0x0310, 0x00}, - - {0x0820, 0x12}, - {0x0821, 0xC0}, - {0x0822, 0x00}, - {0x0823, 0x00}, - - {0x3A03, 0x04}, - {0x3A04, 0xF8}, - {0x3A05, 0x02}, - - {0x0B06, 0x01}, - {0x30A2, 0x00}, - - {0x30B4, 0x00}, - - {0x3A02, 0xFF}, - - {0x3011, 0x00}, - {0x3013, 0x01}, - - {0x0202, 0x0C}, - {0x0203, 0x70}, - {0x0224, 0x01}, - {0x0225, 0xF4}, - - {0x0204, 0x00}, - {0x0205, 0x00}, - {0x020E, 0x01}, - {0x020F, 0x00}, - {0x0210, 0x01}, - {0x0211, 0x00}, - {0x0212, 0x01}, - {0x0213, 0x00}, - {0x0214, 0x01}, - {0x0215, 0x00}, - {0x0216, 0x00}, - {0x0217, 0x00}, - - {0x4170, 0x00}, - {0x4171, 0x10}, - {0x4176, 0x00}, - {0x4177, 0x3C}, - {0xAE20, 0x04}, - {0xAE21, 0x5C}, - - {IMX214_TABLE_WAIT_MS, 10}, - {0x0138, 0x01}, - {IMX214_TABLE_END, 0x00} +static const struct cci_reg_sequence mode_1920x1080[] = { + { IMX214_REG_HDR_MODE, IMX214_HDR_MODE_OFF }, + { IMX214_REG_HDR_RES_REDUCTION, IMX214_HDR_RES_REDU_THROUGH }, + { IMX214_REG_EXPOSURE_RATIO, 1 }, + { IMX214_REG_X_ADD_STA, 1144 }, + { IMX214_REG_Y_ADD_STA, 1020 }, + { IMX214_REG_X_ADD_END, 3063 }, + { IMX214_REG_Y_ADD_END, 2099 }, + { IMX214_REG_X_EVEN_INC, 1 }, + { IMX214_REG_X_ODD_INC, 1 }, + { IMX214_REG_Y_EVEN_INC, 1 }, + { IMX214_REG_Y_ODD_INC, 1 }, + { IMX214_REG_BINNING_MODE, IMX214_BINNING_NONE }, + { IMX214_REG_BINNING_TYPE, 0 }, + { IMX214_REG_BINNING_WEIGHTING, IMX214_BINNING_AVERAGE }, + { CCI_REG8(0x3000), 0x35 }, + { CCI_REG8(0x3054), 0x01 }, + { CCI_REG8(0x305C), 0x11 }, + + { IMX214_REG_CSI_DATA_FORMAT, IMX214_CSI_DATA_FORMAT_RAW10 }, + { IMX214_REG_X_OUTPUT_SIZE, 1920 }, + { IMX214_REG_Y_OUTPUT_SIZE, 1080 }, + { IMX214_REG_SCALE_MODE, IMX214_SCALE_NONE }, + { IMX214_REG_SCALE_M, 2 }, + { IMX214_REG_DIG_CROP_X_OFFSET, 0 }, + { IMX214_REG_DIG_CROP_Y_OFFSET, 0 }, + { IMX214_REG_DIG_CROP_WIDTH, 1920 }, + { IMX214_REG_DIG_CROP_HEIGHT, 1080 }, + + { CCI_REG8(0x3A03), 0x04 }, + { CCI_REG8(0x3A04), 0xF8 }, + { CCI_REG8(0x3A05), 0x02 }, + + { IMX214_REG_SING_DEF_CORR_EN, IMX214_SING_DEF_CORR_ON }, + { IMX214_REG_NML_NR_EN, IMX214_NML_NR_OFF }, + + { CCI_REG8(0x30B4), 0x00 }, + + { CCI_REG8(0x3A02), 0xFF }, + + { CCI_REG8(0x3011), 0x00 }, + { IMX214_REG_STATS_OUT_EN, IMX214_STATS_OUT_ON }, + + { IMX214_REG_SHORT_EXPOSURE, 500 }, + + { CCI_REG8(0x4170), 0x00 }, + { CCI_REG8(0x4171), 0x10 }, + { CCI_REG8(0x4176), 0x00 }, + { CCI_REG8(0x4177), 0x3C }, + { CCI_REG8(0xAE20), 0x04 }, + { CCI_REG8(0xAE21), 0x5C }, }; -static const struct reg_8 mode_table_common[] = { +static const struct cci_reg_sequence mode_table_common[] = { /* software reset */ /* software standby settings */ - {0x0100, 0x00}, + { IMX214_REG_MODE_SELECT, IMX214_MODE_STANDBY }, /* ATR setting */ - {0x9300, 0x02}, - - /* external clock setting */ - {0x0136, 0x18}, - {0x0137, 0x00}, + { IMX214_REG_ATR_FAST_MOVE, 2 }, /* global setting */ /* basic config */ - {0x0101, 0x00}, - {0x0105, 0x01}, - {0x0106, 0x01}, - {0x4550, 0x02}, - {0x4601, 0x00}, - {0x4642, 0x05}, - {0x6227, 0x11}, - {0x6276, 0x00}, - {0x900E, 0x06}, - {0xA802, 0x90}, - {0xA803, 0x11}, - {0xA804, 0x62}, - {0xA805, 0x77}, - {0xA806, 0xAE}, - {0xA807, 0x34}, - {0xA808, 0xAE}, - {0xA809, 0x35}, - {0xA80A, 0x62}, - {0xA80B, 0x83}, - {0xAE33, 0x00}, + { IMX214_REG_MASK_CORR_FRAMES, IMX214_CORR_FRAMES_MASK }, + { IMX214_REG_FAST_STANDBY_CTRL, 1 }, + { IMX214_REG_LINE_LENGTH_PCK, IMX214_PPL_DEFAULT }, + { CCI_REG8(0x4550), 0x02 }, + { CCI_REG8(0x4601), 0x00 }, + { CCI_REG8(0x4642), 0x05 }, + { CCI_REG8(0x6227), 0x11 }, + { CCI_REG8(0x6276), 0x00 }, + { CCI_REG8(0x900E), 0x06 }, + { CCI_REG8(0xA802), 0x90 }, + { CCI_REG8(0xA803), 0x11 }, + { CCI_REG8(0xA804), 0x62 }, + { CCI_REG8(0xA805), 0x77 }, + { CCI_REG8(0xA806), 0xAE }, + { CCI_REG8(0xA807), 0x34 }, + { CCI_REG8(0xA808), 0xAE }, + { CCI_REG8(0xA809), 0x35 }, + { CCI_REG8(0xA80A), 0x62 }, + { CCI_REG8(0xA80B), 0x83 }, + { CCI_REG8(0xAE33), 0x00 }, /* analog setting */ - {0x4174, 0x00}, - {0x4175, 0x11}, - {0x4612, 0x29}, - {0x461B, 0x12}, - {0x461F, 0x06}, - {0x4635, 0x07}, - {0x4637, 0x30}, - {0x463F, 0x18}, - {0x4641, 0x0D}, - {0x465B, 0x12}, - {0x465F, 0x11}, - {0x4663, 0x11}, - {0x4667, 0x0F}, - {0x466F, 0x0F}, - {0x470E, 0x09}, - {0x4909, 0xAB}, - {0x490B, 0x95}, - {0x4915, 0x5D}, - {0x4A5F, 0xFF}, - {0x4A61, 0xFF}, - {0x4A73, 0x62}, - {0x4A85, 0x00}, - {0x4A87, 0xFF}, + { CCI_REG8(0x4174), 0x00 }, + { CCI_REG8(0x4175), 0x11 }, + { CCI_REG8(0x4612), 0x29 }, + { CCI_REG8(0x461B), 0x12 }, + { CCI_REG8(0x461F), 0x06 }, + { CCI_REG8(0x4635), 0x07 }, + { CCI_REG8(0x4637), 0x30 }, + { CCI_REG8(0x463F), 0x18 }, + { CCI_REG8(0x4641), 0x0D }, + { CCI_REG8(0x465B), 0x12 }, + { CCI_REG8(0x465F), 0x11 }, + { CCI_REG8(0x4663), 0x11 }, + { CCI_REG8(0x4667), 0x0F }, + { CCI_REG8(0x466F), 0x0F }, + { CCI_REG8(0x470E), 0x09 }, + { CCI_REG8(0x4909), 0xAB }, + { CCI_REG8(0x490B), 0x95 }, + { CCI_REG8(0x4915), 0x5D }, + { CCI_REG8(0x4A5F), 0xFF }, + { CCI_REG8(0x4A61), 0xFF }, + { CCI_REG8(0x4A73), 0x62 }, + { CCI_REG8(0x4A85), 0x00 }, + { CCI_REG8(0x4A87), 0xFF }, /* embedded data */ - {0x5041, 0x04}, - {0x583C, 0x04}, - {0x620E, 0x04}, - {0x6EB2, 0x01}, - {0x6EB3, 0x00}, - {0x9300, 0x02}, + { IMX214_REG_EBD_SIZE_V, IMX214_EBD_4_LINE }, + { CCI_REG8(0x583C), 0x04 }, + { CCI_REG8(0x620E), 0x04 }, + { CCI_REG8(0x6EB2), 0x01 }, + { CCI_REG8(0x6EB3), 0x00 }, + { IMX214_REG_ATR_FAST_MOVE, 2 }, /* imagequality */ /* HDR setting */ - {0x3001, 0x07}, - {0x6D12, 0x3F}, - {0x6D13, 0xFF}, - {0x9344, 0x03}, - {0x9706, 0x10}, - {0x9707, 0x03}, - {0x9708, 0x03}, - {0x9E04, 0x01}, - {0x9E05, 0x00}, - {0x9E0C, 0x01}, - {0x9E0D, 0x02}, - {0x9E24, 0x00}, - {0x9E25, 0x8C}, - {0x9E26, 0x00}, - {0x9E27, 0x94}, - {0x9E28, 0x00}, - {0x9E29, 0x96}, + { IMX214_REG_RMSC_NR_MODE, 0x07 }, + { IMX214_REG_RG_STATS_LMT, IMX214_RG_STATS_LMT_14_BIT }, + { CCI_REG8(0x9344), 0x03 }, + { CCI_REG8(0x9706), 0x10 }, + { CCI_REG8(0x9707), 0x03 }, + { CCI_REG8(0x9708), 0x03 }, + { CCI_REG8(0x9E04), 0x01 }, + { CCI_REG8(0x9E05), 0x00 }, + { CCI_REG8(0x9E0C), 0x01 }, + { CCI_REG8(0x9E0D), 0x02 }, + { CCI_REG8(0x9E24), 0x00 }, + { CCI_REG8(0x9E25), 0x8C }, + { CCI_REG8(0x9E26), 0x00 }, + { CCI_REG8(0x9E27), 0x94 }, + { CCI_REG8(0x9E28), 0x00 }, + { CCI_REG8(0x9E29), 0x96 }, /* CNR parameter setting */ - {0x69DB, 0x01}, + { CCI_REG8(0x69DB), 0x01 }, /* Moire reduction */ - {0x6957, 0x01}, + { CCI_REG8(0x6957), 0x01 }, /* image enhancement */ - {0x6987, 0x17}, - {0x698A, 0x03}, - {0x698B, 0x03}, + { CCI_REG8(0x6987), 0x17 }, + { CCI_REG8(0x698A), 0x03 }, + { CCI_REG8(0x698B), 0x03 }, /* white balanace */ - {0x0B8E, 0x01}, - {0x0B8F, 0x00}, - {0x0B90, 0x01}, - {0x0B91, 0x00}, - {0x0B92, 0x01}, - {0x0B93, 0x00}, - {0x0B94, 0x01}, - {0x0B95, 0x00}, + { IMX214_REG_ABS_GAIN_GREENR, 0x0100 }, + { IMX214_REG_ABS_GAIN_RED, 0x0100 }, + { IMX214_REG_ABS_GAIN_BLUE, 0x0100 }, + { IMX214_REG_ABS_GAIN_GREENB, 0x0100 }, /* ATR setting */ - {0x6E50, 0x00}, - {0x6E51, 0x32}, - {0x9340, 0x00}, - {0x9341, 0x3C}, - {0x9342, 0x03}, - {0x9343, 0xFF}, - {IMX214_TABLE_END, 0x00} + { CCI_REG8(0x6E50), 0x00 }, + { CCI_REG8(0x6E51), 0x32 }, + { CCI_REG8(0x9340), 0x00 }, + { CCI_REG8(0x9341), 0x3C }, + { CCI_REG8(0x9342), 0x03 }, + { CCI_REG8(0x9343), 0xFF }, }; /* @@ -427,16 +498,25 @@ static const struct reg_8 mode_table_common[] = { static const struct imx214_mode { u32 width; u32 height; - const struct reg_8 *reg_table; + + /* V-timing */ + unsigned int vts_def; + + unsigned int num_of_regs; + const struct cci_reg_sequence *reg_table; } imx214_modes[] = { { .width = 4096, .height = 2304, + .vts_def = 3194, + .num_of_regs = ARRAY_SIZE(mode_4096x2304), .reg_table = mode_4096x2304, }, { .width = 1920, .height = 1080, + .vts_def = 3194, + .num_of_regs = ARRAY_SIZE(mode_1920x1080), .reg_table = mode_1920x1080, }, }; @@ -490,14 +570,42 @@ static int __maybe_unused imx214_power_off(struct device *dev) return 0; } +/* Get bayer order based on flip setting. */ +static u32 imx214_get_format_code(struct imx214 *imx214) +{ + unsigned int i; + + i = (imx214->vflip->val ? 2 : 0) | (imx214->hflip->val ? 1 : 0); + + return imx214_mbus_formats[i]; +} + +static void imx214_update_pad_format(struct imx214 *imx214, + const struct imx214_mode *mode, + struct v4l2_mbus_framefmt *fmt, u32 code) +{ + fmt->code = imx214_get_format_code(imx214); + fmt->width = mode->width; + fmt->height = mode->height; + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); + fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, + fmt->colorspace, + fmt->ycbcr_enc); + fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); +} + static int imx214_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { - if (code->index > 0) + struct imx214 *imx214 = to_imx214(sd); + + if (code->index >= (ARRAY_SIZE(imx214_mbus_formats) / 4)) return -EINVAL; - code->code = IMX214_MBUS_CODE; + code->code = imx214_get_format_code(imx214); return 0; } @@ -506,7 +614,11 @@ static int imx214_enum_frame_size(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { - if (fse->code != IMX214_MBUS_CODE) + struct imx214 *imx214 = to_imx214(subdev); + u32 code; + + code = imx214_get_format_code(imx214); + if (fse->code != code) return -EINVAL; if (fse->index >= ARRAY_SIZE(imx214_modes)) @@ -549,52 +661,6 @@ static const struct v4l2_subdev_core_ops imx214_core_ops = { #endif }; -static struct v4l2_mbus_framefmt * -__imx214_get_pad_format(struct imx214 *imx214, - struct v4l2_subdev_state *sd_state, - unsigned int pad, - enum v4l2_subdev_format_whence which) -{ - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_state_get_format(sd_state, pad); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &imx214->fmt; - default: - return NULL; - } -} - -static int imx214_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *format) -{ - struct imx214 *imx214 = to_imx214(sd); - - mutex_lock(&imx214->mutex); - format->format = *__imx214_get_pad_format(imx214, sd_state, - format->pad, - format->which); - mutex_unlock(&imx214->mutex); - - return 0; -} - -static struct v4l2_rect * -__imx214_get_pad_crop(struct imx214 *imx214, - struct v4l2_subdev_state *sd_state, - unsigned int pad, enum v4l2_subdev_format_whence which) -{ - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_state_get_crop(sd_state, pad); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &imx214->crop; - default: - return NULL; - } -} - static int imx214_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) @@ -604,34 +670,48 @@ static int imx214_set_format(struct v4l2_subdev *sd, struct v4l2_rect *__crop; const struct imx214_mode *mode; - mutex_lock(&imx214->mutex); - - __crop = __imx214_get_pad_crop(imx214, sd_state, format->pad, - format->which); - mode = v4l2_find_nearest_size(imx214_modes, ARRAY_SIZE(imx214_modes), width, height, format->format.width, format->format.height); - __crop->width = mode->width; - __crop->height = mode->height; + imx214_update_pad_format(imx214, mode, &format->format, + format->format.code); + __format = v4l2_subdev_state_get_format(sd_state, 0); - __format = __imx214_get_pad_format(imx214, sd_state, format->pad, - format->which); - __format->width = __crop->width; - __format->height = __crop->height; - __format->code = IMX214_MBUS_CODE; - __format->field = V4L2_FIELD_NONE; - __format->colorspace = V4L2_COLORSPACE_SRGB; - __format->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(__format->colorspace); - __format->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, - __format->colorspace, __format->ycbcr_enc); - __format->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(__format->colorspace); + *__format = format->format; - format->format = *__format; + __crop = v4l2_subdev_state_get_crop(sd_state, 0); + __crop->width = mode->width; + __crop->height = mode->height; - mutex_unlock(&imx214->mutex); + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + int exposure_max; + int exposure_def; + int hblank; + + /* Update blank limits */ + __v4l2_ctrl_modify_range(imx214->vblank, IMX214_VBLANK_MIN, + IMX214_VTS_MAX - mode->height, 2, + mode->vts_def - mode->height); + + /* Update max exposure while meeting expected vblanking */ + exposure_max = mode->vts_def - IMX214_EXPOSURE_OFFSET; + exposure_def = min(exposure_max, IMX214_EXPOSURE_DEFAULT); + __v4l2_ctrl_modify_range(imx214->exposure, + imx214->exposure->minimum, + exposure_max, imx214->exposure->step, + exposure_def); + + /* + * Currently PPL is fixed to IMX214_PPL_DEFAULT, so hblank + * depends on mode->width only, and is not changeable in any + * way other than changing the mode. + */ + hblank = IMX214_PPL_DEFAULT - mode->width; + __v4l2_ctrl_modify_range(imx214->hblank, hblank, hblank, 1, + hblank); + } return 0; } @@ -640,14 +720,9 @@ static int imx214_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { - struct imx214 *imx214 = to_imx214(sd); - switch (sel->target) { case V4L2_SEL_TGT_CROP: - mutex_lock(&imx214->mutex); - sel->r = *__imx214_get_pad_crop(imx214, sd_state, sel->pad, - sel->which); - mutex_unlock(&imx214->mutex); + sel->r = *v4l2_subdev_state_get_crop(sd_state, 0); return 0; case V4L2_SEL_TGT_NATIVE_SIZE: @@ -675,6 +750,7 @@ static int imx214_entity_init_state(struct v4l2_subdev *subdev, struct v4l2_subdev_format fmt = { }; fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + fmt.format.code = MEDIA_BUS_FMT_SRGGB10_1X10; fmt.format.width = imx214_modes[0].width; fmt.format.height = imx214_modes[0].height; @@ -683,12 +759,65 @@ static int imx214_entity_init_state(struct v4l2_subdev *subdev, return 0; } +static int imx214_configure_pll(struct imx214 *imx214) +{ + int ret = 0; + + cci_write(imx214->regmap, IMX214_REG_VTPXCK_DIV, + imx214->pll.vt_bk.pix_clk_div, &ret); + cci_write(imx214->regmap, IMX214_REG_VTSYCK_DIV, + imx214->pll.vt_bk.sys_clk_div, &ret); + cci_write(imx214->regmap, IMX214_REG_PREPLLCK_VT_DIV, + imx214->pll.vt_fr.pre_pll_clk_div, &ret); + cci_write(imx214->regmap, IMX214_REG_PLL_VT_MPY, + imx214->pll.vt_fr.pll_multiplier, &ret); + cci_write(imx214->regmap, IMX214_REG_OPPXCK_DIV, + imx214->pll.op_bk.pix_clk_div, &ret); + cci_write(imx214->regmap, IMX214_REG_OPSYCK_DIV, + imx214->pll.op_bk.sys_clk_div, &ret); + cci_write(imx214->regmap, IMX214_REG_PLL_MULT_DRIV, + IMX214_PLL_SINGLE, &ret); + cci_write(imx214->regmap, IMX214_REG_EXCK_FREQ, + IMX214_EXCK_FREQ(imx214->pll.ext_clk_freq_hz / 1000000), &ret); + + return ret; +} + +static int imx214_update_digital_gain(struct imx214 *imx214, u32 val) +{ + int ret = 0; + + cci_write(imx214->regmap, IMX214_REG_DIG_GAIN_GREENR, val, &ret); + cci_write(imx214->regmap, IMX214_REG_DIG_GAIN_RED, val, &ret); + cci_write(imx214->regmap, IMX214_REG_DIG_GAIN_BLUE, val, &ret); + cci_write(imx214->regmap, IMX214_REG_DIG_GAIN_GREENB, val, &ret); + + return ret; +} + static int imx214_set_ctrl(struct v4l2_ctrl *ctrl) { struct imx214 *imx214 = container_of(ctrl->handler, struct imx214, ctrls); - u8 vals[2]; - int ret; + const struct v4l2_mbus_framefmt *format = NULL; + struct v4l2_subdev_state *state; + int ret = 0; + + if (ctrl->id == V4L2_CID_VBLANK) { + int exposure_max, exposure_def; + + state = v4l2_subdev_get_locked_active_state(&imx214->sd); + format = v4l2_subdev_state_get_format(state, 0); + + /* Update max exposure while meeting expected vblanking */ + exposure_max = + format->height + ctrl->val - IMX214_EXPOSURE_OFFSET; + exposure_def = min(exposure_max, IMX214_EXPOSURE_DEFAULT); + __v4l2_ctrl_modify_range(imx214->exposure, + imx214->exposure->minimum, + exposure_max, imx214->exposure->step, + exposure_def); + } /* * Applying V4L2 control value only happens @@ -698,15 +827,47 @@ static int imx214_set_ctrl(struct v4l2_ctrl *ctrl) return 0; switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + cci_write(imx214->regmap, IMX214_REG_ANALOG_GAIN, + ctrl->val, &ret); + cci_write(imx214->regmap, IMX214_REG_SHORT_ANALOG_GAIN, + ctrl->val, &ret); + break; + case V4L2_CID_DIGITAL_GAIN: + ret = imx214_update_digital_gain(imx214, ctrl->val); + break; case V4L2_CID_EXPOSURE: - vals[1] = ctrl->val; - vals[0] = ctrl->val >> 8; - ret = regmap_bulk_write(imx214->regmap, IMX214_REG_EXPOSURE, vals, 2); - if (ret < 0) - dev_err(imx214->dev, "Error %d\n", ret); - ret = 0; + cci_write(imx214->regmap, IMX214_REG_EXPOSURE, ctrl->val, &ret); + break; + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + cci_write(imx214->regmap, IMX214_REG_ORIENTATION, + imx214->hflip->val | imx214->vflip->val << 1, &ret); + break; + case V4L2_CID_VBLANK: + cci_write(imx214->regmap, IMX214_REG_FRM_LENGTH_LINES, + format->height + ctrl->val, &ret); + break; + case V4L2_CID_TEST_PATTERN: + cci_write(imx214->regmap, IMX214_REG_TEST_PATTERN, + imx214_test_pattern_val[ctrl->val], &ret); + break; + case V4L2_CID_TEST_PATTERN_RED: + cci_write(imx214->regmap, IMX214_REG_TESTP_RED, + ctrl->val, &ret); + break; + case V4L2_CID_TEST_PATTERN_GREENR: + cci_write(imx214->regmap, IMX214_REG_TESTP_GREENR, + ctrl->val, &ret); + break; + case V4L2_CID_TEST_PATTERN_BLUE: + cci_write(imx214->regmap, IMX214_REG_TESTP_BLUE, + ctrl->val, &ret); + break; + case V4L2_CID_TEST_PATTERN_GREENB: + cci_write(imx214->regmap, IMX214_REG_TESTP_GREENB, + ctrl->val, &ret); break; - default: ret = -EINVAL; } @@ -720,39 +881,139 @@ static const struct v4l2_ctrl_ops imx214_ctrl_ops = { .s_ctrl = imx214_set_ctrl, }; -static int imx214_ctrls_init(struct imx214 *imx214) +static int imx214_pll_calculate(struct imx214 *imx214, struct ccs_pll *pll, + unsigned int link_freq) { - static const s64 link_freq[] = { - IMX214_DEFAULT_LINK_FREQ + struct ccs_pll_limits limits = { + .min_ext_clk_freq_hz = 6000000, + .max_ext_clk_freq_hz = 27000000, + + .vt_fr = { + .min_pre_pll_clk_div = 1, + .max_pre_pll_clk_div = 15, + /* Value is educated guess as we don't have a spec */ + .min_pll_ip_clk_freq_hz = 6000000, + /* Value is educated guess as we don't have a spec */ + .max_pll_ip_clk_freq_hz = 12000000, + .min_pll_multiplier = 12, + .max_pll_multiplier = 1200, + .min_pll_op_clk_freq_hz = 338000000, + .max_pll_op_clk_freq_hz = 1200000000, + }, + .vt_bk = { + .min_sys_clk_div = 2, + .max_sys_clk_div = 4, + .min_pix_clk_div = 5, + .max_pix_clk_div = 10, + .min_pix_clk_freq_hz = 30000000, + .max_pix_clk_freq_hz = 120000000, + }, + .op_bk = { + .min_sys_clk_div = 1, + .max_sys_clk_div = 2, + .min_pix_clk_div = 6, + .max_pix_clk_div = 10, + .min_pix_clk_freq_hz = 30000000, + .max_pix_clk_freq_hz = 120000000, + }, + + .min_line_length_pck_bin = IMX214_PPL_DEFAULT, + .min_line_length_pck = IMX214_PPL_DEFAULT, }; + unsigned int num_lanes = imx214->bus_cfg.bus.mipi_csi2.num_data_lanes; + + /* + * There are no documented constraints on the sys clock frequency, for + * either branch. Recover them based on the PLL output clock frequency + * and sys_clk_div limits on one hand, and the pix clock frequency and + * the pix_clk_div limits on the other hand. + */ + limits.vt_bk.min_sys_clk_freq_hz = + max(limits.vt_fr.min_pll_op_clk_freq_hz / limits.vt_bk.max_sys_clk_div, + limits.vt_bk.min_pix_clk_freq_hz * limits.vt_bk.min_pix_clk_div); + limits.vt_bk.max_sys_clk_freq_hz = + min(limits.vt_fr.max_pll_op_clk_freq_hz / limits.vt_bk.min_sys_clk_div, + limits.vt_bk.max_pix_clk_freq_hz * limits.vt_bk.max_pix_clk_div); + + limits.op_bk.min_sys_clk_freq_hz = + max(limits.vt_fr.min_pll_op_clk_freq_hz / limits.op_bk.max_sys_clk_div, + limits.op_bk.min_pix_clk_freq_hz * limits.op_bk.min_pix_clk_div); + limits.op_bk.max_sys_clk_freq_hz = + min(limits.vt_fr.max_pll_op_clk_freq_hz / limits.op_bk.min_sys_clk_div, + limits.op_bk.max_pix_clk_freq_hz * limits.op_bk.max_pix_clk_div); + + memset(pll, 0, sizeof(*pll)); + + pll->bus_type = CCS_PLL_BUS_TYPE_CSI2_DPHY; + pll->op_lanes = num_lanes; + pll->vt_lanes = num_lanes; + pll->csi2.lanes = num_lanes; + + pll->binning_horizontal = 1; + pll->binning_vertical = 1; + pll->scale_m = 1; + pll->scale_n = 1; + pll->bits_per_pixel = + IMX214_CSI_DATA_FORMAT_RAW10 & IMX214_BITS_PER_PIXEL_MASK; + pll->flags = CCS_PLL_FLAG_LANE_SPEED_MODEL; + pll->link_freq = link_freq; + pll->ext_clk_freq_hz = clk_get_rate(imx214->xclk); + + return ccs_pll_calculate(imx214->dev, &limits, pll); +} + +static int imx214_pll_update(struct imx214 *imx214) +{ + u64 link_freq; + int ret; + + link_freq = imx214->bus_cfg.link_frequencies[imx214->link_freq->val]; + ret = imx214_pll_calculate(imx214, &imx214->pll, link_freq); + if (ret) { + dev_err(imx214->dev, "PLL calculations failed: %d\n", ret); + return ret; + } + + ret = v4l2_ctrl_s_ctrl_int64(imx214->pixel_rate, + imx214->pll.pixel_rate_pixel_array); + if (ret) { + dev_err(imx214->dev, "failed to set pixel rate\n"); + return ret; + } + + return 0; +} + +static int imx214_ctrls_init(struct imx214 *imx214) +{ static const struct v4l2_area unit_size = { .width = 1120, .height = 1120, }; + const struct imx214_mode *mode = &imx214_modes[0]; struct v4l2_fwnode_device_properties props; struct v4l2_ctrl_handler *ctrl_hdlr; - int ret; + int exposure_max, exposure_def; + int hblank; + int i, ret; ret = v4l2_fwnode_device_parse(imx214->dev, &props); if (ret < 0) return ret; ctrl_hdlr = &imx214->ctrls; - ret = v4l2_ctrl_handler_init(&imx214->ctrls, 6); + ret = v4l2_ctrl_handler_init(&imx214->ctrls, 13); if (ret) return ret; - imx214->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, NULL, - V4L2_CID_PIXEL_RATE, 0, - IMX214_DEFAULT_PIXEL_RATE, 1, - IMX214_DEFAULT_PIXEL_RATE); + imx214->pixel_rate = + v4l2_ctrl_new_std(ctrl_hdlr, NULL, V4L2_CID_PIXEL_RATE, 1, + INT_MAX, 1, 1); imx214->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, NULL, V4L2_CID_LINK_FREQ, - ARRAY_SIZE(link_freq) - 1, - 0, link_freq); - if (imx214->link_freq) - imx214->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + imx214->bus_cfg.nr_of_link_frequencies - 1, + 0, imx214->bus_cfg.link_frequencies); /* * WARNING! @@ -764,17 +1025,68 @@ static int imx214_ctrls_init(struct imx214 *imx214) * * Yours sincerely, Ricardo. */ + + /* Initial vblank/hblank/exposure parameters based on current mode */ + imx214->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx214_ctrl_ops, + V4L2_CID_VBLANK, IMX214_VBLANK_MIN, + IMX214_VTS_MAX - mode->height, 2, + mode->vts_def - mode->height); + + hblank = IMX214_PPL_DEFAULT - mode->width; + imx214->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx214_ctrl_ops, + V4L2_CID_HBLANK, hblank, hblank, + 1, hblank); + exposure_max = mode->vts_def - IMX214_EXPOSURE_OFFSET; + exposure_def = min(exposure_max, IMX214_EXPOSURE_DEFAULT); imx214->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx214_ctrl_ops, V4L2_CID_EXPOSURE, IMX214_EXPOSURE_MIN, - IMX214_EXPOSURE_MAX, + exposure_max, IMX214_EXPOSURE_STEP, - IMX214_EXPOSURE_DEFAULT); + exposure_def); + + v4l2_ctrl_new_std(ctrl_hdlr, &imx214_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + IMX214_ANA_GAIN_MIN, IMX214_ANA_GAIN_MAX, + IMX214_ANA_GAIN_STEP, IMX214_ANA_GAIN_DEFAULT); + + v4l2_ctrl_new_std(ctrl_hdlr, &imx214_ctrl_ops, V4L2_CID_DIGITAL_GAIN, + IMX214_DGTL_GAIN_MIN, IMX214_DGTL_GAIN_MAX, + IMX214_DGTL_GAIN_STEP, IMX214_DGTL_GAIN_DEFAULT); + + imx214->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx214_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + + imx214->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx214_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + + v4l2_ctrl_cluster(2, &imx214->hflip); + + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx214_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(imx214_test_pattern_menu) - 1, + 0, 0, imx214_test_pattern_menu); + for (i = 0; i < 4; i++) { + /* + * The assumption is that + * V4L2_CID_TEST_PATTERN_GREENR == V4L2_CID_TEST_PATTERN_RED + 1 + * V4L2_CID_TEST_PATTERN_BLUE == V4L2_CID_TEST_PATTERN_RED + 2 + * V4L2_CID_TEST_PATTERN_GREENB == V4L2_CID_TEST_PATTERN_RED + 3 + */ + v4l2_ctrl_new_std(ctrl_hdlr, &imx214_ctrl_ops, + V4L2_CID_TEST_PATTERN_RED + i, + IMX214_TESTP_COLOUR_MIN, + IMX214_TESTP_COLOUR_MAX, + IMX214_TESTP_COLOUR_STEP, + IMX214_TESTP_COLOUR_MAX); + /* The "Solid color" pattern is white by default */ + } imx214->unit_size = v4l2_ctrl_new_std_compound(ctrl_hdlr, NULL, V4L2_CID_UNIT_CELL_SIZE, - v4l2_ctrl_ptr_create((void *)&unit_size)); + v4l2_ctrl_ptr_create((void *)&unit_size), + v4l2_ctrl_ptr_create(NULL), + v4l2_ctrl_ptr_create(NULL)); v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx214_ctrl_ops, &props); @@ -785,81 +1097,86 @@ static int imx214_ctrls_init(struct imx214 *imx214) return ret; } - imx214->sd.ctrl_handler = ctrl_hdlr; + /* Now that the controls have been properly created, set their flags. */ + imx214->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + imx214->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + imx214->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + imx214->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; - return 0; -}; - -#define MAX_CMD 4 -static int imx214_write_table(struct imx214 *imx214, - const struct reg_8 table[]) -{ - u8 vals[MAX_CMD]; - int i; - int ret; - - for (; table->addr != IMX214_TABLE_END ; table++) { - if (table->addr == IMX214_TABLE_WAIT_MS) { - usleep_range(table->val * 1000, - table->val * 1000 + 500); - continue; - } - - for (i = 0; i < MAX_CMD; i++) { - if (table[i].addr != (table[0].addr + i)) - break; - vals[i] = table[i].val; - } - - ret = regmap_bulk_write(imx214->regmap, table->addr, vals, i); - - if (ret) { - dev_err(imx214->dev, "write_table error: %d\n", ret); - return ret; - } - - table += i - 1; + ret = imx214_pll_update(imx214); + if (ret < 0) { + v4l2_ctrl_handler_free(ctrl_hdlr); + dev_err(imx214->dev, "failed to update PLL\n"); + return ret; } + imx214->sd.ctrl_handler = ctrl_hdlr; + return 0; -} +}; static int imx214_start_streaming(struct imx214 *imx214) { + const struct v4l2_mbus_framefmt *fmt; + struct v4l2_subdev_state *state; const struct imx214_mode *mode; + int bit_rate_mbps; int ret; - mutex_lock(&imx214->mutex); - ret = imx214_write_table(imx214, mode_table_common); + ret = cci_multi_reg_write(imx214->regmap, mode_table_common, + ARRAY_SIZE(mode_table_common), NULL); if (ret < 0) { dev_err(imx214->dev, "could not sent common table %d\n", ret); - goto error; + return ret; } - mode = v4l2_find_nearest_size(imx214_modes, - ARRAY_SIZE(imx214_modes), width, height, - imx214->fmt.width, imx214->fmt.height); - ret = imx214_write_table(imx214, mode->reg_table); + ret = imx214_configure_pll(imx214); + if (ret) { + dev_err(imx214->dev, "failed to configure PLL: %d\n", ret); + return ret; + } + + bit_rate_mbps = imx214->pll.pixel_rate_csi / 1000000 + * imx214->pll.bits_per_pixel; + ret = cci_write(imx214->regmap, IMX214_REG_REQ_LINK_BIT_RATE, + IMX214_LINK_BIT_RATE_MBPS(bit_rate_mbps), NULL); + if (ret) { + dev_err(imx214->dev, "failed to configure link bit rate\n"); + return ret; + } + + ret = cci_write(imx214->regmap, IMX214_REG_CSI_LANE_MODE, + IMX214_CSI_4_LANE_MODE, NULL); + if (ret) { + dev_err(imx214->dev, "failed to configure lanes\n"); + return ret; + } + + state = v4l2_subdev_get_locked_active_state(&imx214->sd); + fmt = v4l2_subdev_state_get_format(state, 0); + mode = v4l2_find_nearest_size(imx214_modes, ARRAY_SIZE(imx214_modes), + width, height, fmt->width, fmt->height); + ret = cci_multi_reg_write(imx214->regmap, mode->reg_table, + mode->num_of_regs, NULL); if (ret < 0) { dev_err(imx214->dev, "could not sent mode table %d\n", ret); - goto error; + return ret; } + + usleep_range(10000, 10500); + + cci_write(imx214->regmap, IMX214_REG_TEMP_SENSOR_CONTROL, 0x01, NULL); + ret = __v4l2_ctrl_handler_setup(&imx214->ctrls); if (ret < 0) { dev_err(imx214->dev, "could not sync v4l2 controls\n"); - goto error; + return ret; } - ret = regmap_write(imx214->regmap, IMX214_REG_MODE_SELECT, IMX214_MODE_STREAMING); - if (ret < 0) { + ret = cci_write(imx214->regmap, IMX214_REG_MODE_SELECT, + IMX214_MODE_STREAMING, NULL); + if (ret < 0) dev_err(imx214->dev, "could not sent start table %d\n", ret); - goto error; - } - mutex_unlock(&imx214->mutex); - return 0; - -error: - mutex_unlock(&imx214->mutex); return ret; } @@ -867,7 +1184,8 @@ static int imx214_stop_streaming(struct imx214 *imx214) { int ret; - ret = regmap_write(imx214->regmap, IMX214_REG_MODE_SELECT, IMX214_MODE_STANDBY); + ret = cci_write(imx214->regmap, IMX214_REG_MODE_SELECT, + IMX214_MODE_STANDBY, NULL); if (ret < 0) dev_err(imx214->dev, "could not sent stop table %d\n", ret); @@ -877,6 +1195,7 @@ static int imx214_stop_streaming(struct imx214 *imx214) static int imx214_s_stream(struct v4l2_subdev *subdev, int enable) { struct imx214 *imx214 = to_imx214(subdev); + struct v4l2_subdev_state *state; int ret; if (enable) { @@ -884,7 +1203,9 @@ static int imx214_s_stream(struct v4l2_subdev *subdev, int enable) if (ret < 0) return ret; + state = v4l2_subdev_lock_and_get_active_state(subdev); ret = imx214_start_streaming(imx214); + v4l2_subdev_unlock_state(state); if (ret < 0) goto err_rpm_put; } else { @@ -918,12 +1239,22 @@ static int imx214_get_frame_interval(struct v4l2_subdev *subdev, return 0; } +/* + * Raw sensors should be using the VBLANK and HBLANK controls to determine + * the frame rate. However this driver was initially added using the + * [S|G|ENUM]_FRAME_INTERVAL ioctls with a fixed rate of 30fps. + * Retain the frame_interval ops for backwards compatibility, but they do + * nothing. + */ static int imx214_enum_frame_interval(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_interval_enum *fie) { + struct imx214 *imx214 = to_imx214(subdev); const struct imx214_mode *mode; + dev_warn_once(imx214->dev, "frame_interval functions return an unreliable value for compatibility reasons. Use the VBLANK and HBLANK controls to determine the correct frame rate.\n"); + if (fie->index != 0) return -EINVAL; @@ -931,7 +1262,7 @@ static int imx214_enum_frame_interval(struct v4l2_subdev *subdev, ARRAY_SIZE(imx214_modes), width, height, fie->width, fie->height); - fie->code = IMX214_MBUS_CODE; + fie->code = imx214_get_format_code(imx214); fie->width = mode->width; fie->height = mode->height; fie->interval.numerator = 1; @@ -948,7 +1279,7 @@ static const struct v4l2_subdev_pad_ops imx214_subdev_pad_ops = { .enum_mbus_code = imx214_enum_mbus_code, .enum_frame_size = imx214_enum_frame_size, .enum_frame_interval = imx214_enum_frame_interval, - .get_fmt = imx214_get_format, + .get_fmt = v4l2_subdev_get_fmt, .set_fmt = imx214_set_format, .get_selection = imx214_get_selection, .get_frame_interval = imx214_get_frame_interval, @@ -965,12 +1296,6 @@ static const struct v4l2_subdev_internal_ops imx214_internal_ops = { .init_state = imx214_entity_init_state, }; -static const struct regmap_config sensor_regmap_config = { - .reg_bits = 16, - .val_bits = 8, - .cache_type = REGCACHE_MAPLE, -}; - static int imx214_get_regulators(struct device *dev, struct imx214 *imx214) { unsigned int i; @@ -982,41 +1307,80 @@ static int imx214_get_regulators(struct device *dev, struct imx214 *imx214) imx214->supplies); } -static int imx214_parse_fwnode(struct device *dev) +/* Verify chip ID */ +static int imx214_identify_module(struct imx214 *imx214) { - struct fwnode_handle *endpoint; - struct v4l2_fwnode_endpoint bus_cfg = { - .bus_type = V4L2_MBUS_CSI2_DPHY, - }; + struct i2c_client *client = v4l2_get_subdevdata(&imx214->sd); + int ret; + u64 val; + + ret = cci_read(imx214->regmap, IMX214_REG_CHIP_ID, &val, NULL); + if (ret) + return dev_err_probe(&client->dev, ret, + "failed to read chip id %x\n", + IMX214_CHIP_ID); + + if (val != IMX214_CHIP_ID) + return dev_err_probe(&client->dev, -EIO, + "chip id mismatch: %x!=%llx\n", + IMX214_CHIP_ID, val); + + return 0; +} + +static int imx214_parse_fwnode(struct imx214 *imx214) +{ + struct fwnode_handle *endpoint __free(fwnode_handle) = NULL; + struct v4l2_fwnode_endpoint *bus_cfg = &imx214->bus_cfg; + struct device *dev = imx214->dev; unsigned int i; int ret; endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); - if (!endpoint) { - dev_err(dev, "endpoint node not found\n"); - return -EINVAL; - } + if (!endpoint) + return dev_err_probe(dev, -EINVAL, "endpoint node not found\n"); - ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg); - if (ret) { - dev_err(dev, "parsing endpoint node failed\n"); - goto done; + bus_cfg->bus_type = V4L2_MBUS_CSI2_DPHY; + ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, bus_cfg); + if (ret) + return dev_err_probe(dev, ret, "parsing endpoint node failed\n"); + + /* Check the number of MIPI CSI2 data lanes */ + if (bus_cfg->bus.mipi_csi2.num_data_lanes != 4) { + ret = dev_err_probe(dev, -EINVAL, + "only 4 data lanes are currently supported\n"); + goto error; } - for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++) - if (bus_cfg.link_frequencies[i] == IMX214_DEFAULT_LINK_FREQ) - break; + if (bus_cfg->nr_of_link_frequencies != 1) + dev_warn(dev, "Only one link-frequency supported, please review your DT. Continuing anyway\n"); - if (i == bus_cfg.nr_of_link_frequencies) { - dev_err(dev, "link-frequencies %d not supported, Please review your DT\n", - IMX214_DEFAULT_LINK_FREQ); - ret = -EINVAL; - goto done; + for (i = 0; i < bus_cfg->nr_of_link_frequencies; i++) { + u64 freq = bus_cfg->link_frequencies[i]; + struct ccs_pll pll; + + if (freq == IMX214_DEFAULT_LINK_FREQ_LEGACY) { + dev_warn(dev, + "link-frequencies %d not supported, please review your DT. Continuing anyway\n", + IMX214_DEFAULT_LINK_FREQ); + freq = IMX214_DEFAULT_LINK_FREQ; + bus_cfg->link_frequencies[i] = freq; + } + + if (!imx214_pll_calculate(imx214, &pll, freq)) + break; } -done: - v4l2_fwnode_endpoint_free(&bus_cfg); - fwnode_handle_put(endpoint); + if (i == bus_cfg->nr_of_link_frequencies) + ret = dev_err_probe(dev, -EINVAL, + "link-frequencies %lld not supported, please review your DT\n", + bus_cfg->nr_of_link_frequencies ? + bus_cfg->link_frequencies[0] : 0); + + return 0; + +error: + v4l2_fwnode_endpoint_free(&imx214->bus_cfg); return ret; } @@ -1026,45 +1390,34 @@ static int imx214_probe(struct i2c_client *client) struct imx214 *imx214; int ret; - ret = imx214_parse_fwnode(dev); - if (ret) - return ret; - imx214 = devm_kzalloc(dev, sizeof(*imx214), GFP_KERNEL); if (!imx214) return -ENOMEM; imx214->dev = dev; - imx214->xclk = devm_clk_get(dev, NULL); - if (IS_ERR(imx214->xclk)) { - dev_err(dev, "could not get xclk"); - return PTR_ERR(imx214->xclk); - } - - ret = clk_set_rate(imx214->xclk, IMX214_DEFAULT_CLK_FREQ); - if (ret) { - dev_err(dev, "could not set xclk frequency\n"); - return ret; - } + imx214->xclk = devm_v4l2_sensor_clk_get(dev, NULL); + if (IS_ERR(imx214->xclk)) + return dev_err_probe(dev, PTR_ERR(imx214->xclk), + "failed to get xclk\n"); ret = imx214_get_regulators(dev, imx214); - if (ret < 0) { - dev_err(dev, "cannot get regulators\n"); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "failed to get regulators\n"); imx214->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); - if (IS_ERR(imx214->enable_gpio)) { - dev_err(dev, "cannot get enable gpio\n"); - return PTR_ERR(imx214->enable_gpio); - } + if (IS_ERR(imx214->enable_gpio)) + return dev_err_probe(dev, PTR_ERR(imx214->enable_gpio), + "failed to get enable gpio\n"); - imx214->regmap = devm_regmap_init_i2c(client, &sensor_regmap_config); - if (IS_ERR(imx214->regmap)) { - dev_err(dev, "regmap init failed\n"); - return PTR_ERR(imx214->regmap); - } + imx214->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(imx214->regmap)) + return dev_err_probe(dev, PTR_ERR(imx214->regmap), + "failed to initialize CCI\n"); + + ret = imx214_parse_fwnode(imx214); + if (ret) + return ret; v4l2_i2c_subdev_init(&imx214->sd, client, &imx214_subdev_ops); imx214->sd.internal_ops = &imx214_internal_ops; @@ -1073,19 +1426,18 @@ static int imx214_probe(struct i2c_client *client) * Enable power initially, to avoid warnings * from clk_disable on power_off */ - imx214_power_on(imx214->dev); + ret = imx214_power_on(imx214->dev); + if (ret < 0) + goto error_fwnode; - pm_runtime_set_active(imx214->dev); - pm_runtime_enable(imx214->dev); - pm_runtime_idle(imx214->dev); + ret = imx214_identify_module(imx214); + if (ret) + goto error_power_off; ret = imx214_ctrls_init(imx214); if (ret < 0) goto error_power_off; - mutex_init(&imx214->mutex); - imx214->ctrls.lock = &imx214->mutex; - imx214->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; imx214->pad.flags = MEDIA_PAD_FL_SOURCE; imx214->sd.dev = &client->dev; @@ -1093,28 +1445,47 @@ static int imx214_probe(struct i2c_client *client) ret = media_entity_pads_init(&imx214->sd.entity, 1, &imx214->pad); if (ret < 0) { - dev_err(dev, "could not register media entity\n"); + dev_err_probe(dev, ret, "failed to init entity pads\n"); goto free_ctrl; } - imx214_entity_init_state(&imx214->sd, NULL); + imx214->sd.state_lock = imx214->ctrls.lock; + ret = v4l2_subdev_init_finalize(&imx214->sd); + if (ret < 0) { + dev_err_probe(dev, ret, "subdev init error\n"); + goto free_entity; + } + + pm_runtime_set_active(imx214->dev); + pm_runtime_enable(imx214->dev); ret = v4l2_async_register_subdev_sensor(&imx214->sd); if (ret < 0) { - dev_err(dev, "could not register v4l2 device\n"); - goto free_entity; + dev_err_probe(dev, ret, + "failed to register sensor sub-device\n"); + goto error_subdev_cleanup; } + pm_runtime_idle(imx214->dev); + return 0; +error_subdev_cleanup: + pm_runtime_disable(imx214->dev); + pm_runtime_set_suspended(&client->dev); + v4l2_subdev_cleanup(&imx214->sd); + free_entity: media_entity_cleanup(&imx214->sd.entity); + free_ctrl: - mutex_destroy(&imx214->mutex); v4l2_ctrl_handler_free(&imx214->ctrls); + error_power_off: - pm_runtime_disable(imx214->dev); - regulator_bulk_disable(IMX214_NUM_SUPPLIES, imx214->supplies); + imx214_power_off(imx214->dev); + +error_fwnode: + v4l2_fwnode_endpoint_free(&imx214->bus_cfg); return ret; } @@ -1125,13 +1496,16 @@ static void imx214_remove(struct i2c_client *client) struct imx214 *imx214 = to_imx214(sd); v4l2_async_unregister_subdev(&imx214->sd); + v4l2_subdev_cleanup(sd); media_entity_cleanup(&imx214->sd.entity); v4l2_ctrl_handler_free(&imx214->ctrls); + v4l2_fwnode_endpoint_free(&imx214->bus_cfg); pm_runtime_disable(&client->dev); - pm_runtime_set_suspended(&client->dev); - - mutex_destroy(&imx214->mutex); + if (!pm_runtime_status_suspended(&client->dev)) { + imx214_power_off(imx214->dev); + pm_runtime_set_suspended(&client->dev); + } } static const struct of_device_id imx214_of_match[] = { diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 2d54cea113e1..bc55fe2a93b4 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -68,17 +68,17 @@ #define IMX219_EXPOSURE_STEP 1 #define IMX219_EXPOSURE_DEFAULT 0x640 #define IMX219_EXPOSURE_MAX 65535 +#define IMX219_EXPOSURE_OFFSET 4 /* V_TIMING internal */ -#define IMX219_REG_VTS CCI_REG16(0x0160) -#define IMX219_VTS_MAX 0xffff - -#define IMX219_VBLANK_MIN 4 - -/* HBLANK control - read only */ -#define IMX219_PPL_DEFAULT 3448 - +#define IMX219_REG_FRM_LENGTH_A CCI_REG16(0x0160) +#define IMX219_FLL_MAX 0xffff +#define IMX219_VBLANK_MIN 32 #define IMX219_REG_LINE_LENGTH_A CCI_REG16(0x0162) +#define IMX219_LLP_MIN 0x0d78 +#define IMX219_BINNED_LLP_MIN 0x0de8 +#define IMX219_LLP_MAX 0x7ff0 + #define IMX219_REG_X_ADD_STA_A CCI_REG16(0x0164) #define IMX219_REG_X_ADD_END_A CCI_REG16(0x0166) #define IMX219_REG_Y_ADD_STA_A CCI_REG16(0x0168) @@ -133,10 +133,11 @@ /* Pixel rate is fixed for all the modes */ #define IMX219_PIXEL_RATE 182400000 -#define IMX219_PIXEL_RATE_4LANE 280800000 +#define IMX219_PIXEL_RATE_4LANE 281600000 #define IMX219_DEFAULT_LINK_FREQ 456000000 -#define IMX219_DEFAULT_LINK_FREQ_4LANE 363000000 +#define IMX219_DEFAULT_LINK_FREQ_4LANE_UNSUPPORTED 363000000 +#define IMX219_DEFAULT_LINK_FREQ_4LANE 364000000 /* IMX219 native and active pixel array size. */ #define IMX219_NATIVE_WIDTH 3296U @@ -154,7 +155,7 @@ struct imx219_mode { unsigned int height; /* V-timing */ - unsigned int vts_def; + unsigned int fll_def; }; static const struct cci_reg_sequence imx219_common_regs[] = { @@ -168,15 +169,6 @@ static const struct cci_reg_sequence imx219_common_regs[] = { { CCI_REG8(0x30eb), 0x05 }, { CCI_REG8(0x30eb), 0x09 }, - /* PLL Clock Table */ - { IMX219_REG_VTPXCK_DIV, 5 }, - { IMX219_REG_VTSYCK_DIV, 1 }, - { IMX219_REG_PREPLLCK_VT_DIV, 3 }, /* 0x03 = AUTO set */ - { IMX219_REG_PREPLLCK_OP_DIV, 3 }, /* 0x03 = AUTO set */ - { IMX219_REG_PLL_VT_MPY, 57 }, - { IMX219_REG_OPSYCK_DIV, 1 }, - { IMX219_REG_PLL_OP_MPY, 114 }, - /* Undocumented registers */ { CCI_REG8(0x455e), 0x00 }, { CCI_REG8(0x471e), 0x4b }, @@ -192,7 +184,6 @@ static const struct cci_reg_sequence imx219_common_regs[] = { { CCI_REG8(0x479b), 0x0e }, /* Frame Bank Register Group "A" */ - { IMX219_REG_LINE_LENGTH_A, 3448 }, { IMX219_REG_X_ODD_INC_A, 1 }, { IMX219_REG_Y_ODD_INC_A, 1 }, @@ -201,12 +192,45 @@ static const struct cci_reg_sequence imx219_common_regs[] = { { IMX219_REG_EXCK_FREQ, IMX219_EXCK_FREQ(IMX219_XCLK_FREQ / 1000000) }, }; +static const struct cci_reg_sequence imx219_2lane_regs[] = { + /* PLL Clock Table */ + { IMX219_REG_VTPXCK_DIV, 5 }, + { IMX219_REG_VTSYCK_DIV, 1 }, + { IMX219_REG_PREPLLCK_VT_DIV, 3 }, /* 0x03 = AUTO set */ + { IMX219_REG_PREPLLCK_OP_DIV, 3 }, /* 0x03 = AUTO set */ + { IMX219_REG_PLL_VT_MPY, 57 }, + { IMX219_REG_OPSYCK_DIV, 1 }, + { IMX219_REG_PLL_OP_MPY, 114 }, + + /* 2-Lane CSI Mode */ + { IMX219_REG_CSI_LANE_MODE, IMX219_CSI_2_LANE_MODE }, +}; + +static const struct cci_reg_sequence imx219_4lane_regs[] = { + /* PLL Clock Table */ + { IMX219_REG_VTPXCK_DIV, 5 }, + { IMX219_REG_VTSYCK_DIV, 1 }, + { IMX219_REG_PREPLLCK_VT_DIV, 3 }, /* 0x03 = AUTO set */ + { IMX219_REG_PREPLLCK_OP_DIV, 3 }, /* 0x03 = AUTO set */ + { IMX219_REG_PLL_VT_MPY, 88 }, + { IMX219_REG_OPSYCK_DIV, 1 }, + { IMX219_REG_PLL_OP_MPY, 91 }, + + /* 4-Lane CSI Mode */ + { IMX219_REG_CSI_LANE_MODE, IMX219_CSI_4_LANE_MODE }, +}; + static const s64 imx219_link_freq_menu[] = { IMX219_DEFAULT_LINK_FREQ, }; static const s64 imx219_link_freq_4lane_menu[] = { IMX219_DEFAULT_LINK_FREQ_4LANE, + /* + * This will never be advertised to userspace, but will be used for + * v4l2_link_freq_to_bitmap + */ + IMX219_DEFAULT_LINK_FREQ_4LANE_UNSUPPORTED, }; static const char * const imx219_test_pattern_menu[] = { @@ -289,25 +313,25 @@ static const struct imx219_mode supported_modes[] = { /* 8MPix 15fps mode */ .width = 3280, .height = 2464, - .vts_def = 3526, + .fll_def = 3526, }, { /* 1080P 30fps cropped */ .width = 1920, .height = 1080, - .vts_def = 1763, + .fll_def = 1763, }, { - /* 2x2 binned 30fps mode */ + /* 2x2 binned 60fps mode */ .width = 1640, .height = 1232, - .vts_def = 1763, + .fll_def = 1707, }, { - /* 640x480 30fps mode */ + /* 640x480 60fps mode */ .width = 640, .height = 480, - .vts_def = 1763, + .fll_def = 1707, }, }; @@ -359,6 +383,52 @@ static u32 imx219_get_format_code(struct imx219 *imx219, u32 code) return imx219_mbus_formats[i]; } +static u32 imx219_get_format_bpp(const struct v4l2_mbus_framefmt *format) +{ + switch (format->code) { + case MEDIA_BUS_FMT_SRGGB8_1X8: + case MEDIA_BUS_FMT_SGRBG8_1X8: + case MEDIA_BUS_FMT_SGBRG8_1X8: + case MEDIA_BUS_FMT_SBGGR8_1X8: + return 8; + + case MEDIA_BUS_FMT_SRGGB10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SBGGR10_1X10: + default: + return 10; + } +} + +static void imx219_get_binning(struct v4l2_subdev_state *state, u8 *bin_h, + u8 *bin_v) +{ + const struct v4l2_mbus_framefmt *format = + v4l2_subdev_state_get_format(state, 0); + const struct v4l2_rect *crop = v4l2_subdev_state_get_crop(state, 0); + u32 hbin = crop->width / format->width; + u32 vbin = crop->height / format->height; + + if (hbin == 2 && vbin == 2) { + *bin_h = IMX219_BINNING_X2_ANALOG; + *bin_v = IMX219_BINNING_X2_ANALOG; + } else { + *bin_h = IMX219_BINNING_NONE; + *bin_v = IMX219_BINNING_NONE; + } + +} + +static inline u32 imx219_get_rate_factor(struct v4l2_subdev_state *state) +{ + u8 bin_h, bin_v; + + imx219_get_binning(state, &bin_h, &bin_v); + + return (bin_h & bin_v) == IMX219_BINNING_X2_ANALOG ? 2 : 1; +} + /* ----------------------------------------------------------------------------- * Controls */ @@ -370,22 +440,28 @@ static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); const struct v4l2_mbus_framefmt *format; struct v4l2_subdev_state *state; + u32 rate_factor; int ret = 0; state = v4l2_subdev_get_locked_active_state(&imx219->sd); format = v4l2_subdev_state_get_format(state, 0); + rate_factor = imx219_get_rate_factor(state); if (ctrl->id == V4L2_CID_VBLANK) { int exposure_max, exposure_def; /* Update max exposure while meeting expected vblanking */ - exposure_max = format->height + ctrl->val - 4; + exposure_max = format->height + ctrl->val - IMX219_EXPOSURE_OFFSET; exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ? - exposure_max : IMX219_EXPOSURE_DEFAULT; - __v4l2_ctrl_modify_range(imx219->exposure, - imx219->exposure->minimum, - exposure_max, imx219->exposure->step, - exposure_def); + exposure_max : IMX219_EXPOSURE_DEFAULT; + ret = __v4l2_ctrl_modify_range(imx219->exposure, + imx219->exposure->minimum, + exposure_max, + imx219->exposure->step, + exposure_def); + if (ret) + return ret; + } /* @@ -402,7 +478,7 @@ static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_EXPOSURE: cci_write(imx219->regmap, IMX219_REG_EXPOSURE, - ctrl->val, &ret); + ctrl->val / rate_factor, &ret); break; case V4L2_CID_DIGITAL_GAIN: cci_write(imx219->regmap, IMX219_REG_DIGITAL_GAIN, @@ -418,8 +494,12 @@ static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) imx219->hflip->val | imx219->vflip->val << 1, &ret); break; case V4L2_CID_VBLANK: - cci_write(imx219->regmap, IMX219_REG_VTS, - format->height + ctrl->val, &ret); + cci_write(imx219->regmap, IMX219_REG_FRM_LENGTH_A, + (format->height + ctrl->val) / rate_factor, &ret); + break; + case V4L2_CID_HBLANK: + cci_write(imx219->regmap, IMX219_REG_LINE_LENGTH_A, + format->width + ctrl->val, &ret); break; case V4L2_CID_TEST_PATTERN_RED: cci_write(imx219->regmap, IMX219_REG_TESTP_RED, @@ -466,7 +546,7 @@ static int imx219_init_controls(struct imx219 *imx219) const struct imx219_mode *mode = &supported_modes[0]; struct v4l2_ctrl_handler *ctrl_hdlr; struct v4l2_fwnode_device_properties props; - int exposure_max, exposure_def, hblank; + int exposure_max, exposure_def; int i, ret; ctrl_hdlr = &imx219->ctrl_handler; @@ -490,20 +570,19 @@ static int imx219_init_controls(struct imx219 *imx219) if (imx219->link_freq) imx219->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; - /* Initial vblank/hblank/exposure parameters based on current mode */ + /* Initial blanking and exposure. Limits are updated during set_fmt */ imx219->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_VBLANK, IMX219_VBLANK_MIN, - IMX219_VTS_MAX - mode->height, 1, - mode->vts_def - mode->height); - hblank = IMX219_PPL_DEFAULT - mode->width; + IMX219_FLL_MAX - mode->height, 1, + mode->fll_def - mode->height); imx219->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, - V4L2_CID_HBLANK, hblank, hblank, - 1, hblank); - if (imx219->hblank) - imx219->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; - exposure_max = mode->vts_def - 4; + V4L2_CID_HBLANK, + IMX219_LLP_MIN - mode->width, + IMX219_LLP_MAX - mode->width, 1, + IMX219_LLP_MIN - mode->width); + exposure_max = mode->fll_def - IMX219_EXPOSURE_OFFSET; exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ? - exposure_max : IMX219_EXPOSURE_DEFAULT; + exposure_max : IMX219_EXPOSURE_DEFAULT; imx219->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_EXPOSURE, IMX219_EXPOSURE_MIN, exposure_max, @@ -587,29 +666,13 @@ static int imx219_set_framefmt(struct imx219 *imx219, { const struct v4l2_mbus_framefmt *format; const struct v4l2_rect *crop; - unsigned int bpp; - u64 bin_h, bin_v; + u8 bin_h, bin_v; + u32 bpp; int ret = 0; format = v4l2_subdev_state_get_format(state, 0); crop = v4l2_subdev_state_get_crop(state, 0); - - switch (format->code) { - case MEDIA_BUS_FMT_SRGGB8_1X8: - case MEDIA_BUS_FMT_SGRBG8_1X8: - case MEDIA_BUS_FMT_SGBRG8_1X8: - case MEDIA_BUS_FMT_SBGGR8_1X8: - bpp = 8; - break; - - case MEDIA_BUS_FMT_SRGGB10_1X10: - case MEDIA_BUS_FMT_SGRBG10_1X10: - case MEDIA_BUS_FMT_SGBRG10_1X10: - case MEDIA_BUS_FMT_SBGGR10_1X10: - default: - bpp = 10; - break; - } + bpp = imx219_get_format_bpp(format); cci_write(imx219->regmap, IMX219_REG_X_ADD_STA_A, crop->left - IMX219_PIXEL_ARRAY_LEFT, &ret); @@ -620,26 +683,7 @@ static int imx219_set_framefmt(struct imx219 *imx219, cci_write(imx219->regmap, IMX219_REG_Y_ADD_END_A, crop->top - IMX219_PIXEL_ARRAY_TOP + crop->height - 1, &ret); - switch (crop->width / format->width) { - case 1: - default: - bin_h = IMX219_BINNING_NONE; - break; - case 2: - bin_h = bpp == 8 ? IMX219_BINNING_X2_ANALOG : IMX219_BINNING_X2; - break; - } - - switch (crop->height / format->height) { - case 1: - default: - bin_v = IMX219_BINNING_NONE; - break; - case 2: - bin_v = bpp == 8 ? IMX219_BINNING_X2_ANALOG : IMX219_BINNING_X2; - break; - } - + imx219_get_binning(state, &bin_h, &bin_v); cci_write(imx219->regmap, IMX219_REG_BINNING_MODE_H, bin_h, &ret); cci_write(imx219->regmap, IMX219_REG_BINNING_MODE_V, bin_v, &ret); @@ -662,14 +706,18 @@ static int imx219_set_framefmt(struct imx219 *imx219, static int imx219_configure_lanes(struct imx219 *imx219) { - return cci_write(imx219->regmap, IMX219_REG_CSI_LANE_MODE, - imx219->lanes == 2 ? IMX219_CSI_2_LANE_MODE : - IMX219_CSI_4_LANE_MODE, NULL); + /* Write the appropriate PLL settings for the number of MIPI lanes */ + return cci_multi_reg_write(imx219->regmap, + imx219->lanes == 2 ? imx219_2lane_regs : imx219_4lane_regs, + imx219->lanes == 2 ? ARRAY_SIZE(imx219_2lane_regs) : + ARRAY_SIZE(imx219_4lane_regs), NULL); }; -static int imx219_start_streaming(struct imx219 *imx219, - struct v4l2_subdev_state *state) +static int imx219_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) { + struct imx219 *imx219 = to_imx219(sd); struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); int ret; @@ -718,12 +766,15 @@ static int imx219_start_streaming(struct imx219 *imx219, return 0; err_rpm_put: - pm_runtime_put(&client->dev); + pm_runtime_put_autosuspend(&client->dev); return ret; } -static void imx219_stop_streaming(struct imx219 *imx219) +static int imx219_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) { + struct imx219 *imx219 = to_imx219(sd); struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); int ret; @@ -736,23 +787,8 @@ static void imx219_stop_streaming(struct imx219 *imx219) __v4l2_ctrl_grab(imx219->vflip, false); __v4l2_ctrl_grab(imx219->hflip, false); - pm_runtime_put(&client->dev); -} - -static int imx219_set_stream(struct v4l2_subdev *sd, int enable) -{ - struct imx219 *imx219 = to_imx219(sd); - struct v4l2_subdev_state *state; - int ret = 0; + pm_runtime_put_autosuspend(&client->dev); - state = v4l2_subdev_lock_and_get_active_state(sd); - - if (enable) - ret = imx219_start_streaming(imx219, state); - else - imx219_stop_streaming(imx219); - - v4l2_subdev_unlock_state(state); return ret; } @@ -815,7 +851,12 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, const struct imx219_mode *mode; struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; - unsigned int bin_h, bin_v; + u8 bin_h, bin_v, binning; + u32 prev_line_len; + int ret; + + format = v4l2_subdev_state_get_format(state, 0); + prev_line_len = format->width + imx219->hblank->val; mode = v4l2_find_nearest_size(supported_modes, ARRAY_SIZE(supported_modes), @@ -823,8 +864,6 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, fmt->format.width, fmt->format.height); imx219_update_pad_format(imx219, mode, &fmt->format, fmt->format.code); - - format = v4l2_subdev_state_get_format(state, 0); *format = fmt->format; /* @@ -834,39 +873,79 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, bin_h = min(IMX219_PIXEL_ARRAY_WIDTH / format->width, 2U); bin_v = min(IMX219_PIXEL_ARRAY_HEIGHT / format->height, 2U); + /* Ensure bin_h and bin_v are same to avoid 1:2 or 2:1 stretching */ + binning = min(bin_h, bin_v); + crop = v4l2_subdev_state_get_crop(state, 0); - crop->width = format->width * bin_h; - crop->height = format->height * bin_v; + crop->width = format->width * binning; + crop->height = format->height * binning; crop->left = (IMX219_NATIVE_WIDTH - crop->width) / 2; crop->top = (IMX219_NATIVE_HEIGHT - crop->height) / 2; if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { int exposure_max; int exposure_def; - int hblank; + int hblank, llp_min; + int pixel_rate; /* Update limits and set FPS to default */ - __v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN, - IMX219_VTS_MAX - mode->height, 1, - mode->vts_def - mode->height); - __v4l2_ctrl_s_ctrl(imx219->vblank, - mode->vts_def - mode->height); + ret = __v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN, + IMX219_FLL_MAX - mode->height, 1, + mode->fll_def - mode->height); + if (ret) + return ret; + + ret = __v4l2_ctrl_s_ctrl(imx219->vblank, + mode->fll_def - mode->height); + if (ret) + return ret; + /* Update max exposure while meeting expected vblanking */ - exposure_max = mode->vts_def - 4; + exposure_max = mode->fll_def - IMX219_EXPOSURE_OFFSET; exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ? - exposure_max : IMX219_EXPOSURE_DEFAULT; - __v4l2_ctrl_modify_range(imx219->exposure, - imx219->exposure->minimum, - exposure_max, imx219->exposure->step, - exposure_def); + exposure_max : IMX219_EXPOSURE_DEFAULT; + ret = __v4l2_ctrl_modify_range(imx219->exposure, + imx219->exposure->minimum, + exposure_max, + imx219->exposure->step, + exposure_def); + if (ret) + return ret; + + /* + * With analog binning the default minimum line length of 3448 + * can cause artefacts with RAW10 formats, because the ADC + * operates on two lines together. So we switch to a higher + * minimum of 3560. + */ + imx219_get_binning(state, &bin_h, &bin_v); + llp_min = (bin_h & bin_v) == IMX219_BINNING_X2_ANALOG ? + IMX219_BINNED_LLP_MIN : IMX219_LLP_MIN; + ret = __v4l2_ctrl_modify_range(imx219->hblank, + llp_min - mode->width, + IMX219_LLP_MAX - mode->width, 1, + llp_min - mode->width); + if (ret) + return ret; /* - * Currently PPL is fixed to IMX219_PPL_DEFAULT, so hblank - * depends on mode->width only, and is not changeble in any - * way other than changing the mode. + * Retain PPL setting from previous mode so that the + * line time does not change on a mode change. + * Limits have to be recomputed as the controls define + * the blanking only, so PPL values need to have the + * mode width subtracted. */ - hblank = IMX219_PPL_DEFAULT - mode->width; - __v4l2_ctrl_modify_range(imx219->hblank, hblank, hblank, 1, - hblank); + hblank = prev_line_len - mode->width; + ret = __v4l2_ctrl_s_ctrl(imx219->hblank, hblank); + if (ret) + return ret; + + /* Scale the pixel rate based on the mode specific factor */ + pixel_rate = imx219_get_pixel_rate(imx219) * + imx219_get_rate_factor(state); + ret = __v4l2_ctrl_modify_range(imx219->pixel_rate, pixel_rate, + pixel_rate, 1, pixel_rate); + if (ret) + return ret; } return 0; @@ -877,10 +956,9 @@ static int imx219_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_selection *sel) { switch (sel->target) { - case V4L2_SEL_TGT_CROP: { + case V4L2_SEL_TGT_CROP: sel->r = *v4l2_subdev_state_get_crop(state, 0); return 0; - } case V4L2_SEL_TGT_NATIVE_SIZE: sel->r.top = 0; @@ -916,13 +994,11 @@ static int imx219_init_state(struct v4l2_subdev *sd, }, }; - imx219_set_pad_format(sd, state, &fmt); - - return 0; + return imx219_set_pad_format(sd, state, &fmt); } static const struct v4l2_subdev_video_ops imx219_video_ops = { - .s_stream = imx219_set_stream, + .s_stream = v4l2_subdev_s_stream_helper, }; static const struct v4l2_subdev_pad_ops imx219_pad_ops = { @@ -931,6 +1007,8 @@ static const struct v4l2_subdev_pad_ops imx219_pad_ops = { .set_fmt = imx219_set_pad_format, .get_selection = imx219_get_selection, .enum_frame_size = imx219_enum_frame_size, + .enable_streams = imx219_enable_streams, + .disable_streams = imx219_disable_streams, }; static const struct v4l2_subdev_ops imx219_subdev_ops = { @@ -967,6 +1045,10 @@ static int imx219_power_on(struct device *dev) goto reg_off; } + /* + * Note: Misinterpretation of reset assertion - do not re-use this code. + * XCLR pin is using incorrect (for reset signal) logical level. + */ gpiod_set_value_cansleep(imx219->reset_gpio, 1); usleep_range(IMX219_XCLR_MIN_DELAY_US, IMX219_XCLR_MIN_DELAY_US + IMX219_XCLR_DELAY_RANGE_US); @@ -1035,6 +1117,7 @@ static int imx219_check_hwcfg(struct device *dev, struct imx219 *imx219) struct v4l2_fwnode_endpoint ep_cfg = { .bus_type = V4L2_MBUS_CSI2_DPHY }; + unsigned long link_freq_bitmap; int ret = -EINVAL; endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); @@ -1056,23 +1139,40 @@ static int imx219_check_hwcfg(struct device *dev, struct imx219 *imx219) imx219->lanes = ep_cfg.bus.mipi_csi2.num_data_lanes; /* Check the link frequency set in device tree */ - if (!ep_cfg.nr_of_link_frequencies) { - dev_err_probe(dev, -EINVAL, - "link-frequency property not found in DT\n"); - goto error_out; + switch (imx219->lanes) { + case 2: + ret = v4l2_link_freq_to_bitmap(dev, + ep_cfg.link_frequencies, + ep_cfg.nr_of_link_frequencies, + imx219_link_freq_menu, + ARRAY_SIZE(imx219_link_freq_menu), + &link_freq_bitmap); + break; + case 4: + ret = v4l2_link_freq_to_bitmap(dev, + ep_cfg.link_frequencies, + ep_cfg.nr_of_link_frequencies, + imx219_link_freq_4lane_menu, + ARRAY_SIZE(imx219_link_freq_4lane_menu), + &link_freq_bitmap); + + if (!ret && (link_freq_bitmap & BIT(1))) { + dev_warn(dev, "Link frequency of %d not supported, but has been incorrectly advertised previously\n", + IMX219_DEFAULT_LINK_FREQ_4LANE_UNSUPPORTED); + dev_warn(dev, "Using link frequency of %d\n", + IMX219_DEFAULT_LINK_FREQ_4LANE); + link_freq_bitmap |= BIT(0); + } + break; } - if (ep_cfg.nr_of_link_frequencies != 1 || - (ep_cfg.link_frequencies[0] != ((imx219->lanes == 2) ? - IMX219_DEFAULT_LINK_FREQ : IMX219_DEFAULT_LINK_FREQ_4LANE))) { + if (ret || !(link_freq_bitmap & BIT(0))) { + ret = -EINVAL; dev_err_probe(dev, -EINVAL, "Link frequency not supported: %lld\n", ep_cfg.link_frequencies[0]); - goto error_out; } - ret = 0; - error_out: v4l2_fwnode_endpoint_free(&ep_cfg); fwnode_handle_put(endpoint); @@ -1103,7 +1203,7 @@ static int imx219_probe(struct i2c_client *client) "failed to initialize CCI\n"); /* Get system clock (xclk) */ - imx219->xclk = devm_clk_get(dev, NULL); + imx219->xclk = devm_v4l2_sensor_clk_get(dev, NULL); if (IS_ERR(imx219->xclk)) return dev_err_probe(dev, PTR_ERR(imx219->xclk), "failed to get xclk\n"); @@ -1178,6 +1278,9 @@ static int imx219_probe(struct i2c_client *client) goto error_media_entity; } + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + ret = v4l2_async_register_subdev_sensor(&imx219->sd); if (ret < 0) { dev_err_probe(dev, ret, @@ -1185,15 +1288,16 @@ static int imx219_probe(struct i2c_client *client) goto error_subdev_cleanup; } - /* Enable runtime PM and turn off the device */ - pm_runtime_set_active(dev); - pm_runtime_enable(dev); pm_runtime_idle(dev); + pm_runtime_set_autosuspend_delay(dev, 1000); + pm_runtime_use_autosuspend(dev); return 0; error_subdev_cleanup: v4l2_subdev_cleanup(&imx219->sd); + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); error_media_entity: media_entity_cleanup(&imx219->sd.entity); @@ -1218,9 +1322,10 @@ static void imx219_remove(struct i2c_client *client) imx219_free_controls(imx219); pm_runtime_disable(&client->dev); - if (!pm_runtime_status_suspended(&client->dev)) + if (!pm_runtime_status_suspended(&client->dev)) { imx219_power_off(&client->dev); - pm_runtime_set_suspended(&client->dev); + pm_runtime_set_suspended(&client->dev); + } } static const struct of_device_id imx219_dt_ids[] = { diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c index 9e30fce1f223..e50dcfd830f5 100644 --- a/drivers/media/i2c/imx258.c +++ b/drivers/media/i2c/imx258.c @@ -8,11 +8,12 @@ #include <linux/module.h> #include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> +#include <linux/unaligned.h> + #include <media/v4l2-cci.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-fwnode.h> -#include <linux/unaligned.h> #define IMX258_REG_MODE_SELECT CCI_REG8(0x0100) #define IMX258_MODE_STANDBY 0x00 @@ -645,6 +646,8 @@ static const struct imx258_mode supported_modes[] = { }; struct imx258 { + struct device *dev; + struct v4l2_subdev sd; struct media_pad pad; struct regmap *regmap; @@ -751,7 +754,6 @@ 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); int ret = 0; /* @@ -765,7 +767,7 @@ static int imx258_set_ctrl(struct v4l2_ctrl *ctrl) * Applying V4L2 control value only happens * when power is up for streaming */ - if (pm_runtime_get_if_in_use(&client->dev) == 0) + if (pm_runtime_get_if_in_use(imx258->dev) == 0) return 0; switch (ctrl->id) { @@ -811,14 +813,14 @@ static int imx258_set_ctrl(struct v4l2_ctrl *ctrl) NULL); break; default: - dev_info(&client->dev, + dev_info(imx258->dev, "ctrl(id:0x%x,val:0x%x) is not handled\n", ctrl->id, ctrl->val); ret = -EINVAL; break; } - pm_runtime_put(&client->dev); + pm_runtime_put(imx258->dev); return ret; } @@ -1013,14 +1015,13 @@ static int imx258_get_selection(struct v4l2_subdev *sd, /* 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; const struct imx258_link_freq_config *link_freq_cfg; int ret, link_freq_index; ret = cci_write(imx258->regmap, IMX258_REG_RESET, 0x01, NULL); if (ret) { - dev_err(&client->dev, "%s failed to reset sensor\n", __func__); + dev_err(imx258->dev, "%s failed to reset sensor\n", __func__); return ret; } @@ -1034,21 +1035,21 @@ static int imx258_start_streaming(struct imx258 *imx258) reg_list = &link_freq_cfg->link_cfg[imx258->lane_mode_idx].reg_list; ret = cci_multi_reg_write(imx258->regmap, reg_list->regs, reg_list->num_of_regs, NULL); if (ret) { - dev_err(&client->dev, "%s failed to set plls\n", __func__); + dev_err(imx258->dev, "%s failed to set plls\n", __func__); return ret; } ret = cci_multi_reg_write(imx258->regmap, mode_common_regs, ARRAY_SIZE(mode_common_regs), NULL); if (ret) { - dev_err(&client->dev, "%s failed to set common regs\n", __func__); + dev_err(imx258->dev, "%s failed to set common regs\n", __func__); return ret; } ret = cci_multi_reg_write(imx258->regmap, imx258->variant_cfg->regs, imx258->variant_cfg->num_regs, NULL); if (ret) { - dev_err(&client->dev, "%s failed to set variant config\n", + dev_err(imx258->dev, "%s failed to set variant config\n", __func__); return ret; } @@ -1057,7 +1058,7 @@ static int imx258_start_streaming(struct imx258 *imx258) !!(imx258->csi2_flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK), NULL); if (ret) { - dev_err(&client->dev, "%s failed to set clock lane mode\n", __func__); + dev_err(imx258->dev, "%s failed to set clock lane mode\n", __func__); return ret; } @@ -1065,7 +1066,7 @@ static int imx258_start_streaming(struct imx258 *imx258) reg_list = &imx258->cur_mode->reg_list; ret = cci_multi_reg_write(imx258->regmap, reg_list->regs, reg_list->num_of_regs, NULL); if (ret) { - dev_err(&client->dev, "%s failed to set mode\n", __func__); + dev_err(imx258->dev, "%s failed to set mode\n", __func__); return ret; } @@ -1082,14 +1083,13 @@ static int imx258_start_streaming(struct imx258 *imx258) /* 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 = cci_write(imx258->regmap, IMX258_REG_MODE_SELECT, IMX258_MODE_STANDBY, NULL); if (ret) - dev_err(&client->dev, "%s failed to set stream\n", __func__); + dev_err(imx258->dev, "%s failed to set stream\n", __func__); /* * Return success even if it was an error, as there is nothing the @@ -1135,13 +1135,12 @@ static int imx258_power_off(struct device *dev) 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 (enable) { - ret = pm_runtime_resume_and_get(&client->dev); + ret = pm_runtime_resume_and_get(imx258->dev); if (ret < 0) goto err_unlock; @@ -1154,7 +1153,7 @@ static int imx258_set_stream(struct v4l2_subdev *sd, int enable) goto err_rpm_put; } else { imx258_stop_streaming(imx258); - pm_runtime_put(&client->dev); + pm_runtime_put(imx258->dev); } mutex_unlock(&imx258->mutex); @@ -1162,7 +1161,7 @@ static int imx258_set_stream(struct v4l2_subdev *sd, int enable) return ret; err_rpm_put: - pm_runtime_put(&client->dev); + pm_runtime_put(imx258->dev); err_unlock: mutex_unlock(&imx258->mutex); @@ -1172,20 +1171,19 @@ err_unlock: /* Verify chip ID */ static int imx258_identify_module(struct imx258 *imx258) { - struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); int ret; u64 val; ret = cci_read(imx258->regmap, IMX258_REG_CHIP_ID, &val, NULL); if (ret) { - dev_err(&client->dev, "failed to read chip id %x\n", + dev_err(imx258->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!=%llx\n", + dev_err(imx258->dev, "chip id mismatch: %x!=%llx\n", IMX258_CHIP_ID, val); return -EIO; } @@ -1217,7 +1215,6 @@ static const struct v4l2_subdev_internal_ops imx258_internal_ops = { /* Initialize control handlers */ static int imx258_init_controls(struct imx258 *imx258) { - struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); const struct imx258_link_freq_config *link_freq_cfgs; struct v4l2_fwnode_device_properties props; struct v4l2_ctrl_handler *ctrl_hdlr; @@ -1308,12 +1305,12 @@ static int imx258_init_controls(struct imx258 *imx258) if (ctrl_hdlr->error) { ret = ctrl_hdlr->error; - dev_err(&client->dev, "%s control init failed (%d)\n", + dev_err(imx258->dev, "%s control init failed (%d)\n", __func__, ret); goto error; } - ret = v4l2_fwnode_device_parse(&client->dev, &props); + ret = v4l2_fwnode_device_parse(imx258->dev, &props); if (ret) goto error; @@ -1339,15 +1336,14 @@ static void imx258_free_controls(struct imx258 *imx258) mutex_destroy(&imx258->mutex); } -static int imx258_get_regulators(struct imx258 *imx258, - struct i2c_client *client) +static int imx258_get_regulators(struct imx258 *imx258) { unsigned int i; for (i = 0; i < IMX258_NUM_SUPPLIES; i++) imx258->supplies[i].supply = imx258_supply_name[i]; - return devm_regulator_bulk_get(&client->dev, + return devm_regulator_bulk_get(imx258->dev, IMX258_NUM_SUPPLIES, imx258->supplies); } @@ -1365,30 +1361,27 @@ static int imx258_probe(struct i2c_client *client) if (!imx258) return -ENOMEM; + imx258->dev = &client->dev; + imx258->regmap = devm_cci_regmap_init_i2c(client, 16); if (IS_ERR(imx258->regmap)) { ret = PTR_ERR(imx258->regmap); - dev_err(&client->dev, "failed to initialize CCI: %d\n", ret); + dev_err(imx258->dev, "failed to initialize CCI: %d\n", ret); return ret; } - ret = imx258_get_regulators(imx258, client); + ret = imx258_get_regulators(imx258); if (ret) - return dev_err_probe(&client->dev, ret, + return dev_err_probe(imx258->dev, ret, "failed to get regulators\n"); - imx258->clk = devm_clk_get_optional(&client->dev, NULL); + imx258->clk = devm_v4l2_sensor_clk_get_legacy(imx258->dev, NULL, false, + 0); if (IS_ERR(imx258->clk)) - return dev_err_probe(&client->dev, PTR_ERR(imx258->clk), + return dev_err_probe(imx258->dev, PTR_ERR(imx258->clk), "error getting clock\n"); - if (!imx258->clk) { - dev_dbg(&client->dev, - "no clock provided, using clock-frequency property\n"); - device_property_read_u32(&client->dev, "clock-frequency", &val); - } else { - val = clk_get_rate(imx258->clk); - } + val = clk_get_rate(imx258->clk); switch (val) { case 19200000: @@ -1400,32 +1393,32 @@ static int imx258_probe(struct i2c_client *client) imx258->link_freq_menu_items = link_freq_menu_items_24; break; default: - dev_err(&client->dev, "input clock frequency of %u not supported\n", + dev_err(imx258->dev, "input clock frequency of %u not supported\n", val); return -EINVAL; } - endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL); + endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(imx258->dev), NULL); if (!endpoint) { - dev_err(&client->dev, "Endpoint node not found\n"); + dev_err(imx258->dev, "Endpoint node not found\n"); return -EINVAL; } ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep); fwnode_handle_put(endpoint); if (ret) { - dev_err(&client->dev, "Parsing endpoint node failed\n"); + dev_err(imx258->dev, "Parsing endpoint node failed\n"); return ret; } - ret = v4l2_link_freq_to_bitmap(&client->dev, + ret = v4l2_link_freq_to_bitmap(imx258->dev, ep.link_frequencies, ep.nr_of_link_frequencies, imx258->link_freq_menu_items, ARRAY_SIZE(link_freq_menu_items_19_2), &imx258->link_freq_bitmap); if (ret) { - dev_err(&client->dev, "Link frequency not supported\n"); + dev_err(imx258->dev, "Link frequency not supported\n"); goto error_endpoint_free; } @@ -1438,7 +1431,7 @@ static int imx258_probe(struct i2c_client *client) imx258->lane_mode_idx = IMX258_4_LANE_MODE; break; default: - dev_err(&client->dev, "Invalid data lanes: %u\n", + dev_err(imx258->dev, "Invalid data lanes: %u\n", ep.bus.mipi_csi2.num_data_lanes); ret = -EINVAL; goto error_endpoint_free; @@ -1446,7 +1439,7 @@ static int imx258_probe(struct i2c_client *client) imx258->csi2_flags = ep.bus.mipi_csi2.flags; - imx258->variant_cfg = device_get_match_data(&client->dev); + imx258->variant_cfg = device_get_match_data(imx258->dev); if (!imx258->variant_cfg) imx258->variant_cfg = &imx258_cfg; @@ -1454,7 +1447,7 @@ static int imx258_probe(struct i2c_client *client) v4l2_i2c_subdev_init(&imx258->sd, client, &imx258_subdev_ops); /* Will be powered off via pm_runtime_idle */ - ret = imx258_power_on(&client->dev); + ret = imx258_power_on(imx258->dev); if (ret) goto error_endpoint_free; @@ -1486,9 +1479,9 @@ static int imx258_probe(struct i2c_client *client) if (ret < 0) goto error_media_entity; - pm_runtime_set_active(&client->dev); - pm_runtime_enable(&client->dev); - pm_runtime_idle(&client->dev); + pm_runtime_set_active(imx258->dev); + pm_runtime_enable(imx258->dev); + pm_runtime_idle(imx258->dev); v4l2_fwnode_endpoint_free(&ep); return 0; @@ -1500,7 +1493,7 @@ error_handler_free: imx258_free_controls(imx258); error_identify: - imx258_power_off(&client->dev); + imx258_power_off(imx258->dev); error_endpoint_free: v4l2_fwnode_endpoint_free(&ep); @@ -1517,10 +1510,10 @@ static void imx258_remove(struct i2c_client *client) media_entity_cleanup(&sd->entity); imx258_free_controls(imx258); - pm_runtime_disable(&client->dev); - if (!pm_runtime_status_suspended(&client->dev)) - imx258_power_off(&client->dev); - pm_runtime_set_suspended(&client->dev); + pm_runtime_disable(imx258->dev); + if (!pm_runtime_status_suspended(imx258->dev)) + imx258_power_off(imx258->dev); + pm_runtime_set_suspended(imx258->dev); } static const struct dev_pm_ops imx258_pm_ops = { diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c index a2b824986027..8ec78b60bea6 100644 --- a/drivers/media/i2c/imx274.c +++ b/drivers/media/i2c/imx274.c @@ -826,6 +826,8 @@ static int imx274_start_stream(struct stimx274 *priv) * if rst = 0, keep it in reset; * if rst = 1, bring it out of reset. * + * Note: Misinterpretation of reset assertion - do not re-use this code. + * XCLR pin is using incorrect (for reset signal) logical level. */ static void imx274_reset(struct stimx274 *priv, int rst) { @@ -2032,8 +2034,7 @@ static int imx274_probe(struct i2c_client *client) /* initialize regmap */ imx274->regmap = devm_regmap_init_i2c(client, &imx274_regmap_config); if (IS_ERR(imx274->regmap)) { - dev_err(dev, - "regmap init failed: %ld\n", PTR_ERR(imx274->regmap)); + dev_err(dev, "regmap init failed: %pe\n", imx274->regmap); ret = -ENODEV; goto err_regmap; } diff --git a/drivers/media/i2c/imx283.c b/drivers/media/i2c/imx283.c index f676faf4b301..8ab63ad8f385 100644 --- a/drivers/media/i2c/imx283.c +++ b/drivers/media/i2c/imx283.c @@ -1082,7 +1082,7 @@ static int imx283_start_streaming(struct imx283 *imx283, cci_write(imx283->cci, IMX283_REG_SVR, 0x00, &ret); dev_dbg(imx283->dev, "Mode: Size %d x %d\n", mode->width, mode->height); - dev_dbg(imx283->dev, "Analogue Crop (in the mode) %d,%d %dx%d\n", + dev_dbg(imx283->dev, "Analogue Crop (in the mode) (%d,%d)/%ux%u\n", mode->crop.left, mode->crop.top, mode->crop.width, @@ -1143,7 +1143,6 @@ static int imx283_enable_streams(struct v4l2_subdev *sd, return 0; err_rpm_put: - pm_runtime_mark_last_busy(imx283->dev); pm_runtime_put_autosuspend(imx283->dev); return ret; @@ -1163,15 +1162,16 @@ static int imx283_disable_streams(struct v4l2_subdev *sd, if (ret) dev_err(imx283->dev, "Failed to stop stream\n"); - pm_runtime_mark_last_busy(imx283->dev); pm_runtime_put_autosuspend(imx283->dev); return ret; } /* Power/clock management functions */ -static int imx283_power_on(struct imx283 *imx283) +static int imx283_power_on(struct device *dev) { + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct imx283 *imx283 = to_imx283(sd); int ret; ret = regulator_bulk_enable(ARRAY_SIZE(imx283_supply_name), @@ -1199,29 +1199,14 @@ reg_off: return ret; } -static int imx283_power_off(struct imx283 *imx283) -{ - gpiod_set_value_cansleep(imx283->reset_gpio, 1); - regulator_bulk_disable(ARRAY_SIZE(imx283_supply_name), imx283->supplies); - clk_disable_unprepare(imx283->xclk); - - return 0; -} - -static int imx283_runtime_resume(struct device *dev) +static int imx283_power_off(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); struct imx283 *imx283 = to_imx283(sd); - return imx283_power_on(imx283); -} - -static int imx283_runtime_suspend(struct device *dev) -{ - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct imx283 *imx283 = to_imx283(sd); - - imx283_power_off(imx283); + gpiod_set_value_cansleep(imx283->reset_gpio, 1); + regulator_bulk_disable(ARRAY_SIZE(imx283_supply_name), imx283->supplies); + clk_disable_unprepare(imx283->xclk); return 0; } @@ -1475,11 +1460,10 @@ static int imx283_probe(struct i2c_client *client) } /* Get system clock (xclk) */ - imx283->xclk = devm_clk_get(imx283->dev, NULL); - if (IS_ERR(imx283->xclk)) { + imx283->xclk = devm_v4l2_sensor_clk_get(imx283->dev, NULL); + if (IS_ERR(imx283->xclk)) return dev_err_probe(imx283->dev, PTR_ERR(imx283->xclk), "failed to get xclk\n"); - } xclk_freq = clk_get_rate(imx283->xclk); for (i = 0; i < ARRAY_SIZE(imx283_frequencies); i++) { @@ -1516,7 +1500,7 @@ static int imx283_probe(struct i2c_client *client) * The sensor must be powered for imx283_identify_module() * to be able to read the CHIP_ID register */ - ret = imx283_power_on(imx283); + ret = imx283_power_on(imx283->dev); if (ret) return ret; @@ -1571,7 +1555,6 @@ static int imx283_probe(struct i2c_client *client) * Decrease the PM usage count. The device will get suspended after the * autosuspend delay, turning the power off. */ - pm_runtime_mark_last_busy(imx283->dev); pm_runtime_put_autosuspend(imx283->dev); return 0; @@ -1589,7 +1572,7 @@ error_pm: pm_runtime_disable(imx283->dev); pm_runtime_set_suspended(imx283->dev); error_power_off: - imx283_power_off(imx283); + imx283_power_off(imx283->dev); return ret; } @@ -1606,12 +1589,12 @@ static void imx283_remove(struct i2c_client *client) pm_runtime_disable(imx283->dev); if (!pm_runtime_status_suspended(imx283->dev)) - imx283_power_off(imx283); + imx283_power_off(imx283->dev); pm_runtime_set_suspended(imx283->dev); } -static DEFINE_RUNTIME_DEV_PM_OPS(imx283_pm_ops, imx283_runtime_suspend, - imx283_runtime_resume, NULL); +static DEFINE_RUNTIME_DEV_PM_OPS(imx283_pm_ops, imx283_power_off, + imx283_power_on, NULL); static const struct of_device_id imx283_dt_ids[] = { { .compatible = "sony,imx283" }, diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index f5ee6bd3b52d..21cbc81cb2ed 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -170,12 +170,15 @@ enum imx290_model { IMX290_MODEL_IMX290LQR, IMX290_MODEL_IMX290LLR, IMX290_MODEL_IMX327LQR, + IMX290_MODEL_IMX462LQR, + IMX290_MODEL_IMX462LLR, }; struct imx290_model_info { enum imx290_colour_variant colour_variant; const struct cci_reg_sequence *init_regs; size_t init_regs_num; + unsigned int max_analog_gain; const char *name; }; @@ -267,7 +270,6 @@ static const struct cci_reg_sequence imx290_global_init_settings[] = { { IMX290_WINWV, 1097 }, { IMX290_XSOUTSEL, IMX290_XSOUTSEL_XVSOUTSEL_VSYNC | IMX290_XSOUTSEL_XHSOUTSEL_HSYNC }, - { CCI_REG8(0x3011), 0x02 }, { CCI_REG8(0x3012), 0x64 }, { CCI_REG8(0x3013), 0x00 }, }; @@ -275,6 +277,51 @@ static const struct cci_reg_sequence imx290_global_init_settings[] = { static const struct cci_reg_sequence imx290_global_init_settings_290[] = { { CCI_REG8(0x300f), 0x00 }, { CCI_REG8(0x3010), 0x21 }, + { CCI_REG8(0x3011), 0x00 }, + { CCI_REG8(0x3016), 0x09 }, + { CCI_REG8(0x3070), 0x02 }, + { CCI_REG8(0x3071), 0x11 }, + { CCI_REG8(0x309b), 0x10 }, + { CCI_REG8(0x309c), 0x22 }, + { CCI_REG8(0x30a2), 0x02 }, + { CCI_REG8(0x30a6), 0x20 }, + { CCI_REG8(0x30a8), 0x20 }, + { CCI_REG8(0x30aa), 0x20 }, + { CCI_REG8(0x30ac), 0x20 }, + { CCI_REG8(0x30b0), 0x43 }, + { CCI_REG8(0x3119), 0x9e }, + { CCI_REG8(0x311c), 0x1e }, + { CCI_REG8(0x311e), 0x08 }, + { CCI_REG8(0x3128), 0x05 }, + { CCI_REG8(0x313d), 0x83 }, + { CCI_REG8(0x3150), 0x03 }, + { CCI_REG8(0x317e), 0x00 }, + { CCI_REG8(0x32b8), 0x50 }, + { CCI_REG8(0x32b9), 0x10 }, + { CCI_REG8(0x32ba), 0x00 }, + { CCI_REG8(0x32bb), 0x04 }, + { CCI_REG8(0x32c8), 0x50 }, + { CCI_REG8(0x32c9), 0x10 }, + { CCI_REG8(0x32ca), 0x00 }, + { CCI_REG8(0x32cb), 0x04 }, + { CCI_REG8(0x332c), 0xd3 }, + { CCI_REG8(0x332d), 0x10 }, + { CCI_REG8(0x332e), 0x0d }, + { CCI_REG8(0x3358), 0x06 }, + { CCI_REG8(0x3359), 0xe1 }, + { CCI_REG8(0x335a), 0x11 }, + { CCI_REG8(0x3360), 0x1e }, + { CCI_REG8(0x3361), 0x61 }, + { CCI_REG8(0x3362), 0x10 }, + { CCI_REG8(0x33b0), 0x50 }, + { CCI_REG8(0x33b2), 0x1a }, + { CCI_REG8(0x33b3), 0x04 }, +}; + +static const struct cci_reg_sequence imx290_global_init_settings_462[] = { + { CCI_REG8(0x300f), 0x00 }, + { CCI_REG8(0x3010), 0x21 }, + { CCI_REG8(0x3011), 0x02 }, { CCI_REG8(0x3016), 0x09 }, { CCI_REG8(0x3070), 0x02 }, { CCI_REG8(0x3071), 0x11 }, @@ -328,6 +375,7 @@ static const struct cci_reg_sequence xclk_regs[][IMX290_NUM_CLK_REGS] = { }; static const struct cci_reg_sequence imx290_global_init_settings_327[] = { + { CCI_REG8(0x3011), 0x02 }, { CCI_REG8(0x309e), 0x4A }, { CCI_REG8(0x309f), 0x4A }, { CCI_REG8(0x313b), 0x61 }, @@ -821,7 +869,6 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) break; } - pm_runtime_mark_last_busy(imx290->dev); pm_runtime_put_autosuspend(imx290->dev); return ret; @@ -876,14 +923,10 @@ static int imx290_ctrl_init(struct imx290 *imx290) * up to 72.0dB (240) add further digital gain. Limit the range to * analog gain only, support for digital gain can be added separately * if needed. - * - * The IMX327 and IMX462 are largely compatible with the IMX290, but - * have an analog gain range of 0.0dB to 29.4dB and 42dB of digital - * gain. When support for those sensors gets added to the driver, the - * gain control should be adjusted accordingly. */ v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, - V4L2_CID_ANALOGUE_GAIN, 0, 100, 1, 0); + V4L2_CID_ANALOGUE_GAIN, 0, + imx290->model->max_analog_gain, 1, 0); /* * Correct range will be determined through imx290_ctrl_update setting @@ -1055,7 +1098,6 @@ static int imx290_set_stream(struct v4l2_subdev *sd, int enable) } } else { imx290_stop_streaming(imx290); - pm_runtime_mark_last_busy(imx290->dev); pm_runtime_put_autosuspend(imx290->dev); } @@ -1250,8 +1292,6 @@ static int imx290_subdev_init(struct imx290 *imx290) * will already be prevented even before the delay. */ v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops); - imx290->sd.dev = imx290->dev; - pm_runtime_mark_last_busy(imx290->dev); pm_runtime_put_autosuspend(imx290->dev); imx290->sd.internal_ops = &imx290_internal_ops; @@ -1382,14 +1422,14 @@ static int imx290_get_regulators(struct device *dev, struct imx290 *imx290) static int imx290_init_clk(struct imx290 *imx290) { u32 xclk_freq; - int ret; - ret = device_property_read_u32(imx290->dev, "clock-frequency", - &xclk_freq); - if (ret) { - dev_err(imx290->dev, "Could not get xclk frequency\n"); - return ret; - } + imx290->xclk = devm_v4l2_sensor_clk_get_legacy(imx290->dev, "xclk", + false, 0); + if (IS_ERR(imx290->xclk)) + return dev_err_probe(imx290->dev, PTR_ERR(imx290->xclk), + "Could not get xclk\n"); + + xclk_freq = clk_get_rate(imx290->xclk); /* external clock must be 37.125 MHz or 74.25MHz */ switch (xclk_freq) { @@ -1405,12 +1445,6 @@ static int imx290_init_clk(struct imx290 *imx290) return -EINVAL; } - ret = clk_set_rate(imx290->xclk, xclk_freq); - if (ret) { - dev_err(imx290->dev, "Could not set xclk frequency\n"); - return ret; - } - return 0; } @@ -1441,20 +1475,37 @@ static const struct imx290_model_info imx290_models[] = { .colour_variant = IMX290_VARIANT_COLOUR, .init_regs = imx290_global_init_settings_290, .init_regs_num = ARRAY_SIZE(imx290_global_init_settings_290), + .max_analog_gain = 100, .name = "imx290", }, [IMX290_MODEL_IMX290LLR] = { .colour_variant = IMX290_VARIANT_MONO, .init_regs = imx290_global_init_settings_290, .init_regs_num = ARRAY_SIZE(imx290_global_init_settings_290), + .max_analog_gain = 100, .name = "imx290", }, [IMX290_MODEL_IMX327LQR] = { .colour_variant = IMX290_VARIANT_COLOUR, .init_regs = imx290_global_init_settings_327, .init_regs_num = ARRAY_SIZE(imx290_global_init_settings_327), + .max_analog_gain = 98, .name = "imx327", }, + [IMX290_MODEL_IMX462LQR] = { + .colour_variant = IMX290_VARIANT_COLOUR, + .init_regs = imx290_global_init_settings_462, + .init_regs_num = ARRAY_SIZE(imx290_global_init_settings_462), + .max_analog_gain = 98, + .name = "imx462", + }, + [IMX290_MODEL_IMX462LLR] = { + .colour_variant = IMX290_VARIANT_MONO, + .init_regs = imx290_global_init_settings_462, + .init_regs_num = ARRAY_SIZE(imx290_global_init_settings_462), + .max_analog_gain = 98, + .name = "imx462", + }, }; static int imx290_parse_dt(struct imx290 *imx290) @@ -1539,11 +1590,6 @@ static int imx290_probe(struct i2c_client *client) return ret; /* Acquire resources. */ - imx290->xclk = devm_clk_get(dev, "xclk"); - if (IS_ERR(imx290->xclk)) - return dev_err_probe(dev, PTR_ERR(imx290->xclk), - "Could not get xclk\n"); - ret = imx290_get_regulators(dev, imx290); if (ret < 0) return dev_err_probe(dev, ret, "Cannot get regulators\n"); @@ -1554,7 +1600,7 @@ static int imx290_probe(struct i2c_client *client) return dev_err_probe(dev, PTR_ERR(imx290->rst_gpio), "Cannot get reset gpio\n"); - /* Initialize external clock frequency. */ + /* Initialize external clock. */ ret = imx290_init_clk(imx290); if (ret) return ret; @@ -1653,6 +1699,12 @@ static const struct of_device_id imx290_of_match[] = { }, { .compatible = "sony,imx327lqr", .data = &imx290_models[IMX290_MODEL_IMX327LQR], + }, { + .compatible = "sony,imx462lqr", + .data = &imx290_models[IMX290_MODEL_IMX462LQR], + }, { + .compatible = "sony,imx462llr", + .data = &imx290_models[IMX290_MODEL_IMX462LLR], }, { /* sentinel */ }, }; diff --git a/drivers/media/i2c/imx296.c b/drivers/media/i2c/imx296.c index 83149fa729c4..69636db11a2b 100644 --- a/drivers/media/i2c/imx296.c +++ b/drivers/media/i2c/imx296.c @@ -604,7 +604,6 @@ static int imx296_s_stream(struct v4l2_subdev *sd, int enable) if (!enable) { ret = imx296_stream_off(sensor); - pm_runtime_mark_last_busy(sensor->dev); pm_runtime_put_autosuspend(sensor->dev); goto unlock; @@ -922,7 +921,7 @@ static int imx296_read_temperature(struct imx296 *sensor, int *temp) tmdout &= IMX296_TMDOUT_MASK; - /* T(°C) = 246.312 - 0.304 * TMDOUT */; + /* T(°C) = 246.312 - 0.304 * TMDOUT */ *temp = 246312 - 304 * tmdout; return imx296_write(sensor, IMX296_TMDCTRL, 0, NULL); @@ -954,6 +953,8 @@ static int imx296_identify_model(struct imx296 *sensor) return ret; } + usleep_range(2000, 5000); + ret = imx296_read(sensor, IMX296_SENSOR_INFO); if (ret < 0) { dev_err(sensor->dev, "failed to read sensor information (%d)\n", @@ -1042,7 +1043,7 @@ static int imx296_probe(struct i2c_client *client) return dev_err_probe(sensor->dev, PTR_ERR(sensor->reset), "failed to get reset GPIO\n"); - sensor->clk = devm_clk_get(sensor->dev, "inck"); + sensor->clk = devm_v4l2_sensor_clk_get(sensor->dev, "inck"); if (IS_ERR(sensor->clk)) return dev_err_probe(sensor->dev, PTR_ERR(sensor->clk), "failed to get clock\n"); diff --git a/drivers/media/i2c/imx319.c b/drivers/media/i2c/imx319.c index dd1b4ff983dc..953310ef3046 100644 --- a/drivers/media/i2c/imx319.c +++ b/drivers/media/i2c/imx319.c @@ -1,11 +1,13 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2018 Intel Corporation -#include <linux/unaligned.h> #include <linux/acpi.h> +#include <linux/clk.h> #include <linux/i2c.h> #include <linux/module.h> #include <linux/pm_runtime.h> +#include <linux/unaligned.h> + #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-event.h> @@ -106,11 +108,12 @@ struct imx319_mode { }; struct imx319_hwcfg { - u32 ext_clk; /* sensor external clk */ unsigned long link_freq_bitmap; }; struct imx319 { + struct device *dev; + struct v4l2_subdev sd; struct media_pad pad; @@ -1839,14 +1842,13 @@ static int imx319_write_reg(struct imx319 *imx319, u16 reg, u32 len, u32 val) static int imx319_write_regs(struct imx319 *imx319, const struct imx319_reg *regs, u32 len) { - struct i2c_client *client = v4l2_get_subdevdata(&imx319->sd); int ret; u32 i; for (i = 0; i < len; i++) { ret = imx319_write_reg(imx319, regs[i].address, 1, regs[i].val); if (ret) { - dev_err_ratelimited(&client->dev, + dev_err_ratelimited(imx319->dev, "write reg 0x%4.4x return err %d", regs[i].address, ret); return ret; @@ -1880,7 +1882,6 @@ static int imx319_set_ctrl(struct v4l2_ctrl *ctrl) { struct imx319 *imx319 = container_of(ctrl->handler, struct imx319, ctrl_handler); - struct i2c_client *client = v4l2_get_subdevdata(&imx319->sd); s64 max; int ret; @@ -1899,7 +1900,7 @@ static int imx319_set_ctrl(struct v4l2_ctrl *ctrl) * Applying V4L2 control value only happens * when power is up for streaming */ - if (!pm_runtime_get_if_in_use(&client->dev)) + if (!pm_runtime_get_if_in_use(imx319->dev)) return 0; switch (ctrl->id) { @@ -1933,12 +1934,12 @@ static int imx319_set_ctrl(struct v4l2_ctrl *ctrl) break; default: ret = -EINVAL; - dev_info(&client->dev, "ctrl(id:0x%x,val:0x%x) is not handled", + dev_info(imx319->dev, "ctrl(id:0x%x,val:0x%x) is not handled", ctrl->id, ctrl->val); break; } - pm_runtime_put(&client->dev); + pm_runtime_put(imx319->dev); return ret; } @@ -2087,7 +2088,6 @@ imx319_set_pad_format(struct v4l2_subdev *sd, /* Verify chip ID */ static int imx319_identify_module(struct imx319 *imx319) { - struct i2c_client *client = v4l2_get_subdevdata(&imx319->sd); int ret; u32 val; @@ -2099,7 +2099,7 @@ static int imx319_identify_module(struct imx319 *imx319) return ret; if (val != IMX319_CHIP_ID) { - dev_err(&client->dev, "chip id mismatch: %x!=%x", + dev_err(imx319->dev, "chip id mismatch: %x!=%x", IMX319_CHIP_ID, val); return -EIO; } @@ -2112,7 +2112,6 @@ static int imx319_identify_module(struct imx319 *imx319) /* Start streaming */ static int imx319_start_streaming(struct imx319 *imx319) { - struct i2c_client *client = v4l2_get_subdevdata(&imx319->sd); const struct imx319_reg_list *reg_list; int ret; @@ -2124,7 +2123,7 @@ static int imx319_start_streaming(struct imx319 *imx319) reg_list = &imx319_global_setting; ret = imx319_write_regs(imx319, reg_list->regs, reg_list->num_of_regs); if (ret) { - dev_err(&client->dev, "failed to set global settings"); + dev_err(imx319->dev, "failed to set global settings"); return ret; } @@ -2132,7 +2131,7 @@ static int imx319_start_streaming(struct imx319 *imx319) reg_list = &imx319->cur_mode->reg_list; ret = imx319_write_regs(imx319, reg_list->regs, reg_list->num_of_regs); if (ret) { - dev_err(&client->dev, "failed to set mode"); + dev_err(imx319->dev, "failed to set mode"); return ret; } @@ -2160,13 +2159,12 @@ static int imx319_stop_streaming(struct imx319 *imx319) static int imx319_set_stream(struct v4l2_subdev *sd, int enable) { struct imx319 *imx319 = to_imx319(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); int ret = 0; mutex_lock(&imx319->mutex); if (enable) { - ret = pm_runtime_resume_and_get(&client->dev); + ret = pm_runtime_resume_and_get(imx319->dev); if (ret < 0) goto err_unlock; @@ -2179,7 +2177,7 @@ static int imx319_set_stream(struct v4l2_subdev *sd, int enable) goto err_rpm_put; } else { imx319_stop_streaming(imx319); - pm_runtime_put(&client->dev); + pm_runtime_put(imx319->dev); } /* vflip and hflip cannot change during streaming */ @@ -2191,7 +2189,7 @@ static int imx319_set_stream(struct v4l2_subdev *sd, int enable) return ret; err_rpm_put: - pm_runtime_put(&client->dev); + pm_runtime_put(imx319->dev); err_unlock: mutex_unlock(&imx319->mutex); @@ -2231,7 +2229,6 @@ static const struct v4l2_subdev_internal_ops imx319_internal_ops = { /* Initialize control handlers */ static int imx319_init_controls(struct imx319 *imx319) { - struct i2c_client *client = v4l2_get_subdevdata(&imx319->sd); struct v4l2_ctrl_handler *ctrl_hdlr; s64 exposure_max; s64 vblank_def; @@ -2311,7 +2308,7 @@ static int imx319_init_controls(struct imx319 *imx319) 0, 0, imx319_test_pattern_menu); if (ctrl_hdlr->error) { ret = ctrl_hdlr->error; - dev_err(&client->dev, "control init failed: %d", ret); + dev_err(imx319->dev, "control init failed: %d", ret); goto error; } @@ -2350,20 +2347,6 @@ static struct imx319_hwcfg *imx319_get_hwcfg(struct device *dev) if (!cfg) goto out_err; - ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", - &cfg->ext_clk); - if (ret) { - dev_err(dev, "can't get clock frequency"); - goto out_err; - } - - dev_dbg(dev, "ext clk: %d", cfg->ext_clk); - if (cfg->ext_clk != IMX319_EXT_CLK) { - dev_err(dev, "external clock %d is not supported", - cfg->ext_clk); - goto out_err; - } - ret = v4l2_link_freq_to_bitmap(dev, bus_cfg.link_frequencies, bus_cfg.nr_of_link_frequencies, link_freq_menu_items, @@ -2385,6 +2368,8 @@ out_err: static int imx319_probe(struct i2c_client *client) { struct imx319 *imx319; + unsigned long freq; + struct clk *clk; bool full_power; int ret; @@ -2392,24 +2377,37 @@ static int imx319_probe(struct i2c_client *client) if (!imx319) return -ENOMEM; + imx319->dev = &client->dev; + mutex_init(&imx319->mutex); + clk = devm_v4l2_sensor_clk_get(imx319->dev, NULL); + if (IS_ERR(clk)) + return dev_err_probe(imx319->dev, PTR_ERR(clk), + "failed to acquire clock\n"); + + freq = clk_get_rate(clk); + if (freq != IMX319_EXT_CLK) + return dev_err_probe(imx319->dev, -EINVAL, + "external clock %lu is not supported", + freq); + /* Initialize subdev */ v4l2_i2c_subdev_init(&imx319->sd, client, &imx319_subdev_ops); - full_power = acpi_dev_state_d0(&client->dev); + full_power = acpi_dev_state_d0(imx319->dev); if (full_power) { /* Check module identity */ ret = imx319_identify_module(imx319); if (ret) { - dev_err(&client->dev, "failed to find sensor: %d", ret); + dev_err(imx319->dev, "failed to find sensor: %d", ret); goto error_probe; } } - imx319->hwcfg = imx319_get_hwcfg(&client->dev); + imx319->hwcfg = imx319_get_hwcfg(imx319->dev); if (!imx319->hwcfg) { - dev_err(&client->dev, "failed to get hwcfg"); + dev_err(imx319->dev, "failed to get hwcfg"); ret = -ENODEV; goto error_probe; } @@ -2419,7 +2417,7 @@ static int imx319_probe(struct i2c_client *client) ret = imx319_init_controls(imx319); if (ret) { - dev_err(&client->dev, "failed to init controls: %d", ret); + dev_err(imx319->dev, "failed to init controls: %d", ret); goto error_probe; } @@ -2434,25 +2432,27 @@ static int imx319_probe(struct i2c_client *client) imx319->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&imx319->sd.entity, 1, &imx319->pad); if (ret) { - dev_err(&client->dev, "failed to init entity pads: %d", ret); + dev_err(imx319->dev, "failed to init entity pads: %d", ret); goto error_handler_free; } /* Set the device's state to active if it's in D0 state. */ if (full_power) - pm_runtime_set_active(&client->dev); - pm_runtime_enable(&client->dev); - pm_runtime_idle(&client->dev); + pm_runtime_set_active(imx319->dev); + pm_runtime_enable(imx319->dev); ret = v4l2_async_register_subdev_sensor(&imx319->sd); if (ret < 0) goto error_media_entity_pm; + pm_runtime_idle(imx319->dev); + return 0; error_media_entity_pm: - pm_runtime_disable(&client->dev); - pm_runtime_set_suspended(&client->dev); + pm_runtime_disable(imx319->dev); + if (full_power) + pm_runtime_set_suspended(imx319->dev); media_entity_cleanup(&imx319->sd.entity); error_handler_free: @@ -2473,8 +2473,9 @@ static void imx319_remove(struct i2c_client *client) media_entity_cleanup(&sd->entity); v4l2_ctrl_handler_free(sd->ctrl_handler); - pm_runtime_disable(&client->dev); - pm_runtime_set_suspended(&client->dev); + pm_runtime_disable(imx319->dev); + if (!pm_runtime_status_suspended(imx319->dev)) + pm_runtime_set_suspended(imx319->dev); mutex_destroy(&imx319->mutex); } diff --git a/drivers/media/i2c/imx334.c b/drivers/media/i2c/imx334.c index a544fc3df39c..9654f9268056 100644 --- a/drivers/media/i2c/imx334.c +++ b/drivers/media/i2c/imx334.c @@ -12,77 +12,128 @@ #include <linux/module.h> #include <linux/pm_runtime.h> +#include <media/v4l2-cci.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-fwnode.h> #include <media/v4l2-subdev.h> /* Streaming Mode */ -#define IMX334_REG_MODE_SELECT 0x3000 -#define IMX334_MODE_STANDBY 0x01 -#define IMX334_MODE_STREAMING 0x00 +#define IMX334_REG_MODE_SELECT CCI_REG8(0x3000) +#define IMX334_MODE_STANDBY 0x01 +#define IMX334_MODE_STREAMING 0x00 /* Lines per frame */ -#define IMX334_REG_LPFR 0x3030 +#define IMX334_REG_VMAX CCI_REG24_LE(0x3030) + +#define IMX334_REG_HMAX CCI_REG16_LE(0x3034) + +#define IMX334_REG_OPB_SIZE_V CCI_REG8(0x304c) +#define IMX334_REG_ADBIT CCI_REG8(0x3050) +#define IMX334_REG_MDBIT CCI_REG8(0x319d) +#define IMX334_REG_ADBIT1 CCI_REG16_LE(0x341c) +#define IMX334_REG_Y_OUT_SIZE CCI_REG16_LE(0x3308) +#define IMX334_REG_XVS_XHS_OUTSEL CCI_REG8(0x31a0) +#define IMX334_REG_XVS_XHS_DRV CCI_REG8(0x31a1) /* Chip ID */ -#define IMX334_REG_ID 0x3044 -#define IMX334_ID 0x1e +#define IMX334_REG_ID CCI_REG8(0x3044) +#define IMX334_ID 0x1e /* Exposure control */ -#define IMX334_REG_SHUTTER 0x3058 -#define IMX334_EXPOSURE_MIN 1 -#define IMX334_EXPOSURE_OFFSET 5 -#define IMX334_EXPOSURE_STEP 1 -#define IMX334_EXPOSURE_DEFAULT 0x0648 +#define IMX334_REG_SHUTTER CCI_REG24_LE(0x3058) +#define IMX334_EXPOSURE_MIN 1 +#define IMX334_EXPOSURE_OFFSET 5 +#define IMX334_EXPOSURE_STEP 1 +#define IMX334_EXPOSURE_DEFAULT 0x0648 + +#define IMX334_REG_LANEMODE CCI_REG8(0x3a01) +#define IMX334_CSI_4_LANE_MODE 3 +#define IMX334_CSI_8_LANE_MODE 7 + +/* Window cropping Settings */ +#define IMX334_REG_AREA3_ST_ADR_1 CCI_REG16_LE(0x3074) +#define IMX334_REG_AREA3_ST_ADR_2 CCI_REG16_LE(0x308e) +#define IMX334_REG_UNREAD_PARAM5 CCI_REG16_LE(0x30b6) +#define IMX334_REG_AREA3_WIDTH_1 CCI_REG16_LE(0x3076) +#define IMX334_REG_AREA3_WIDTH_2 CCI_REG16_LE(0x3090) +#define IMX334_REG_BLACK_OFSET_ADR CCI_REG16_LE(0x30c6) +#define IMX334_REG_UNRD_LINE_MAX CCI_REG16_LE(0x30ce) +#define IMX334_REG_UNREAD_ED_ADR CCI_REG16_LE(0x30d8) +#define IMX334_REG_UNREAD_PARAM6 CCI_REG16_LE(0x3116) + +#define IMX334_REG_VREVERSE CCI_REG8(0x304f) +#define IMX334_REG_HREVERSE CCI_REG8(0x304e) + +/* Binning Settings */ +#define IMX334_REG_HADD_VADD CCI_REG8(0x3199) +#define IMX334_REG_VALID_EXPAND CCI_REG8(0x31dd) +#define IMX334_REG_TCYCLE CCI_REG8(0x3300) /* Analog gain control */ -#define IMX334_REG_AGAIN 0x30e8 -#define IMX334_AGAIN_MIN 0 -#define IMX334_AGAIN_MAX 240 -#define IMX334_AGAIN_STEP 1 -#define IMX334_AGAIN_DEFAULT 0 +#define IMX334_REG_AGAIN CCI_REG16_LE(0x30e8) +#define IMX334_AGAIN_MIN 0 +#define IMX334_AGAIN_MAX 240 +#define IMX334_AGAIN_STEP 1 +#define IMX334_AGAIN_DEFAULT 0 /* Group hold register */ -#define IMX334_REG_HOLD 0x3001 +#define IMX334_REG_HOLD CCI_REG8(0x3001) + +#define IMX334_REG_MASTER_MODE CCI_REG8(0x3002) +#define IMX334_REG_WINMODE CCI_REG8(0x3018) +#define IMX334_REG_HTRIMMING_START CCI_REG16_LE(0x302c) +#define IMX334_REG_HNUM CCI_REG16_LE(0x302e) /* Input clock rate */ -#define IMX334_INCLK_RATE 24000000 +#define IMX334_INCLK_RATE 24000000 + +/* INCK Setting Register */ +#define IMX334_REG_BCWAIT_TIME CCI_REG8(0x300c) +#define IMX334_REG_CPWAIT_TIME CCI_REG8(0x300d) +#define IMX334_REG_INCKSEL1 CCI_REG16_LE(0x314c) +#define IMX334_REG_INCKSEL2 CCI_REG8(0x315a) +#define IMX334_REG_INCKSEL3 CCI_REG8(0x3168) +#define IMX334_REG_INCKSEL4 CCI_REG8(0x316a) +#define IMX334_REG_SYS_MODE CCI_REG8(0x319e) + +#define IMX334_REG_TCLKPOST CCI_REG16_LE(0x3a18) +#define IMX334_REG_TCLKPREPARE CCI_REG16_LE(0x3a1a) +#define IMX334_REG_TCLKTRAIL CCI_REG16_LE(0x3a1c) +#define IMX334_REG_TCLKZERO CCI_REG16_LE(0x3a1e) +#define IMX334_REG_THSPREPARE CCI_REG16_LE(0x3a20) +#define IMX334_REG_THSZERO CCI_REG16_LE(0x3a22) +#define IMX334_REG_THSTRAIL CCI_REG16_LE(0x3a24) +#define IMX334_REG_THSEXIT CCI_REG16_LE(0x3a26) +#define IMX334_REG_TPLX CCI_REG16_LE(0x3a28) /* CSI2 HW configuration */ -#define IMX334_LINK_FREQ_891M 891000000 -#define IMX334_LINK_FREQ_445M 445500000 -#define IMX334_NUM_DATA_LANES 4 +#define IMX334_LINK_FREQ_891M 891000000 +#define IMX334_LINK_FREQ_445M 445500000 +#define IMX334_NUM_DATA_LANES 4 -#define IMX334_REG_MIN 0x00 -#define IMX334_REG_MAX 0xfffff +#define IMX334_REG_MIN 0x00 +#define IMX334_REG_MAX 0xfffff /* Test Pattern Control */ -#define IMX334_REG_TP 0x329e -#define IMX334_TP_COLOR_HBARS 0xA -#define IMX334_TP_COLOR_VBARS 0xB +#define IMX334_REG_TP CCI_REG8(0x329e) +#define IMX334_TP_COLOR_HBARS 0xa +#define IMX334_TP_COLOR_VBARS 0xb +#define IMX334_TP_BLACK 0x0 +#define IMX334_TP_WHITE 0x1 +#define IMX334_TP_BLACK_GREY 0xc -#define IMX334_TPG_EN_DOUT 0x329c -#define IMX334_TP_ENABLE 0x1 -#define IMX334_TP_DISABLE 0x0 +#define IMX334_TPG_EN_DOUT CCI_REG8(0x329c) +#define IMX334_TP_ENABLE 0x1 +#define IMX334_TP_DISABLE 0x0 -#define IMX334_TPG_COLORW 0x32a0 -#define IMX334_TPG_COLORW_120P 0x13 +#define IMX334_TPG_COLORW CCI_REG8(0x32a0) +#define IMX334_TPG_COLORW_120P 0x13 -#define IMX334_TP_CLK_EN 0x3148 -#define IMX334_TP_CLK_EN_VAL 0x10 -#define IMX334_TP_CLK_DIS_VAL 0x0 +#define IMX334_TP_CLK_EN CCI_REG8(0x3148) +#define IMX334_TP_CLK_EN_VAL 0x10 +#define IMX334_TP_CLK_DIS_VAL 0x0 -#define IMX334_DIG_CLP_MODE 0x3280 - -/** - * struct imx334_reg - imx334 sensor register - * @address: Register address - * @val: Register value - */ -struct imx334_reg { - u16 address; - u8 val; -}; +#define IMX334_DIG_CLP_MODE CCI_REG8(0x3280) /** * struct imx334_reg_list - imx334 sensor register list @@ -91,7 +142,7 @@ struct imx334_reg { */ struct imx334_reg_list { u32 num_of_regs; - const struct imx334_reg *regs; + const struct cci_reg_sequence *regs; }; /** @@ -121,6 +172,7 @@ struct imx334_mode { /** * struct imx334 - imx334 sensor device structure * @dev: Pointer to generic device + * @cci: CCI register map * @client: Pointer to i2c client * @sd: V4L2 sub-device * @pad: Media pad. Only one pad supported @@ -135,12 +187,12 @@ struct imx334_mode { * @again_ctrl: Pointer to analog gain control * @vblank: Vertical blanking in lines * @cur_mode: Pointer to current selected sensor mode - * @mutex: Mutex for serializing sensor controls * @link_freq_bitmap: Menu bitmap for link_freq_ctrl * @cur_code: current selected format code */ struct imx334 { struct device *dev; + struct regmap *cci; struct i2c_client *client; struct v4l2_subdev sd; struct media_pad pad; @@ -157,7 +209,6 @@ struct imx334 { }; u32 vblank; const struct imx334_mode *cur_mode; - struct mutex mutex; unsigned long link_freq_bitmap; u32 cur_code; }; @@ -167,309 +218,213 @@ static const s64 link_freq[] = { IMX334_LINK_FREQ_445M, }; +/* Sensor common mode registers values */ +static const struct cci_reg_sequence common_mode_regs[] = { + { IMX334_REG_MODE_SELECT, IMX334_MODE_STANDBY }, + { IMX334_REG_WINMODE, 0x04 }, + { IMX334_REG_VMAX, 0x0008ca }, + { IMX334_REG_HMAX, 0x044c }, + { IMX334_REG_BLACK_OFSET_ADR, 0x0000 }, + { IMX334_REG_UNRD_LINE_MAX, 0x0000 }, + { IMX334_REG_OPB_SIZE_V, 0x00 }, + { IMX334_REG_HREVERSE, 0x00 }, + { IMX334_REG_VREVERSE, 0x00 }, + { IMX334_REG_UNREAD_PARAM5, 0x0000 }, + { IMX334_REG_UNREAD_PARAM6, 0x0008 }, + { IMX334_REG_XVS_XHS_OUTSEL, 0x20 }, + { IMX334_REG_XVS_XHS_DRV, 0x0f }, + { IMX334_REG_BCWAIT_TIME, 0x3b }, + { IMX334_REG_CPWAIT_TIME, 0x2a }, + { IMX334_REG_INCKSEL1, 0x0129 }, + { IMX334_REG_INCKSEL2, 0x06 }, + { IMX334_REG_INCKSEL3, 0xa0 }, + { IMX334_REG_INCKSEL4, 0x7e }, + { IMX334_REG_SYS_MODE, 0x02 }, + { IMX334_REG_HADD_VADD, 0x00 }, + { IMX334_REG_VALID_EXPAND, 0x03 }, + { IMX334_REG_TCYCLE, 0x00 }, + { IMX334_REG_TCLKPOST, 0x007f }, + { IMX334_REG_TCLKPREPARE, 0x0037 }, + { IMX334_REG_TCLKTRAIL, 0x0037 }, + { IMX334_REG_TCLKZERO, 0xf7 }, + { IMX334_REG_THSPREPARE, 0x002f }, + { CCI_REG8(0x3078), 0x02 }, + { CCI_REG8(0x3079), 0x00 }, + { CCI_REG8(0x307a), 0x00 }, + { CCI_REG8(0x307b), 0x00 }, + { CCI_REG8(0x3080), 0x02 }, + { CCI_REG8(0x3081), 0x00 }, + { CCI_REG8(0x3082), 0x00 }, + { CCI_REG8(0x3083), 0x00 }, + { CCI_REG8(0x3088), 0x02 }, + { CCI_REG8(0x3094), 0x00 }, + { CCI_REG8(0x3095), 0x00 }, + { CCI_REG8(0x3096), 0x00 }, + { CCI_REG8(0x309b), 0x02 }, + { CCI_REG8(0x309c), 0x00 }, + { CCI_REG8(0x309d), 0x00 }, + { CCI_REG8(0x309e), 0x00 }, + { CCI_REG8(0x30a4), 0x00 }, + { CCI_REG8(0x30a5), 0x00 }, + { CCI_REG8(0x3288), 0x21 }, + { CCI_REG8(0x328a), 0x02 }, + { CCI_REG8(0x3414), 0x05 }, + { CCI_REG8(0x3416), 0x18 }, + { CCI_REG8(0x35Ac), 0x0e }, + { CCI_REG8(0x3648), 0x01 }, + { CCI_REG8(0x364a), 0x04 }, + { CCI_REG8(0x364c), 0x04 }, + { CCI_REG8(0x3678), 0x01 }, + { CCI_REG8(0x367c), 0x31 }, + { CCI_REG8(0x367e), 0x31 }, + { CCI_REG8(0x3708), 0x02 }, + { CCI_REG8(0x3714), 0x01 }, + { CCI_REG8(0x3715), 0x02 }, + { CCI_REG8(0x3716), 0x02 }, + { CCI_REG8(0x3717), 0x02 }, + { CCI_REG8(0x371c), 0x3d }, + { CCI_REG8(0x371d), 0x3f }, + { CCI_REG8(0x372c), 0x00 }, + { CCI_REG8(0x372d), 0x00 }, + { CCI_REG8(0x372e), 0x46 }, + { CCI_REG8(0x372f), 0x00 }, + { CCI_REG8(0x3730), 0x89 }, + { CCI_REG8(0x3731), 0x00 }, + { CCI_REG8(0x3732), 0x08 }, + { CCI_REG8(0x3733), 0x01 }, + { CCI_REG8(0x3734), 0xfe }, + { CCI_REG8(0x3735), 0x05 }, + { CCI_REG8(0x375d), 0x00 }, + { CCI_REG8(0x375e), 0x00 }, + { CCI_REG8(0x375f), 0x61 }, + { CCI_REG8(0x3760), 0x06 }, + { CCI_REG8(0x3768), 0x1b }, + { CCI_REG8(0x3769), 0x1b }, + { CCI_REG8(0x376a), 0x1a }, + { CCI_REG8(0x376b), 0x19 }, + { CCI_REG8(0x376c), 0x18 }, + { CCI_REG8(0x376d), 0x14 }, + { CCI_REG8(0x376e), 0x0f }, + { CCI_REG8(0x3776), 0x00 }, + { CCI_REG8(0x3777), 0x00 }, + { CCI_REG8(0x3778), 0x46 }, + { CCI_REG8(0x3779), 0x00 }, + { CCI_REG8(0x377a), 0x08 }, + { CCI_REG8(0x377b), 0x01 }, + { CCI_REG8(0x377c), 0x45 }, + { CCI_REG8(0x377d), 0x01 }, + { CCI_REG8(0x377e), 0x23 }, + { CCI_REG8(0x377f), 0x02 }, + { CCI_REG8(0x3780), 0xd9 }, + { CCI_REG8(0x3781), 0x03 }, + { CCI_REG8(0x3782), 0xf5 }, + { CCI_REG8(0x3783), 0x06 }, + { CCI_REG8(0x3784), 0xa5 }, + { CCI_REG8(0x3788), 0x0f }, + { CCI_REG8(0x378a), 0xd9 }, + { CCI_REG8(0x378b), 0x03 }, + { CCI_REG8(0x378c), 0xeb }, + { CCI_REG8(0x378d), 0x05 }, + { CCI_REG8(0x378e), 0x87 }, + { CCI_REG8(0x378f), 0x06 }, + { CCI_REG8(0x3790), 0xf5 }, + { CCI_REG8(0x3792), 0x43 }, + { CCI_REG8(0x3794), 0x7a }, + { CCI_REG8(0x3796), 0xa1 }, + { CCI_REG8(0x37b0), 0x37 }, + { CCI_REG8(0x3e04), 0x0e }, + { IMX334_REG_AGAIN, 0x0050 }, + { IMX334_REG_MASTER_MODE, 0x00 }, +}; + +/* Sensor mode registers for 640x480@30fps */ +static const struct cci_reg_sequence mode_640x480_regs[] = { + { IMX334_REG_HTRIMMING_START, 0x0670 }, + { IMX334_REG_HNUM, 0x0280 }, + { IMX334_REG_AREA3_ST_ADR_1, 0x0748 }, + { IMX334_REG_AREA3_ST_ADR_2, 0x0749 }, + { IMX334_REG_AREA3_WIDTH_1, 0x01e0 }, + { IMX334_REG_AREA3_WIDTH_2, 0x01e0 }, + { IMX334_REG_Y_OUT_SIZE, 0x01e0 }, + { IMX334_REG_UNREAD_ED_ADR, 0x0b30 }, +}; + +/* Sensor mode registers for 1280x720@30fps */ +static const struct cci_reg_sequence mode_1280x720_regs[] = { + { IMX334_REG_HTRIMMING_START, 0x0530 }, + { IMX334_REG_HNUM, 0x0500 }, + { IMX334_REG_AREA3_ST_ADR_1, 0x0384 }, + { IMX334_REG_AREA3_ST_ADR_2, 0x0385 }, + { IMX334_REG_AREA3_WIDTH_1, 0x02d0 }, + { IMX334_REG_AREA3_WIDTH_2, 0x02d0 }, + { IMX334_REG_Y_OUT_SIZE, 0x02d0 }, + { IMX334_REG_UNREAD_ED_ADR, 0x0b30 }, +}; + /* Sensor mode registers for 1920x1080@30fps */ -static const struct imx334_reg mode_1920x1080_regs[] = { - {0x3000, 0x01}, - {0x3018, 0x04}, - {0x3030, 0xca}, - {0x3031, 0x08}, - {0x3032, 0x00}, - {0x3034, 0x4c}, - {0x3035, 0x04}, - {0x302c, 0xf0}, - {0x302d, 0x03}, - {0x302e, 0x80}, - {0x302f, 0x07}, - {0x3074, 0xcc}, - {0x3075, 0x02}, - {0x308e, 0xcd}, - {0x308f, 0x02}, - {0x3076, 0x38}, - {0x3077, 0x04}, - {0x3090, 0x38}, - {0x3091, 0x04}, - {0x3308, 0x38}, - {0x3309, 0x04}, - {0x30C6, 0x00}, - {0x30c7, 0x00}, - {0x30ce, 0x00}, - {0x30cf, 0x00}, - {0x30d8, 0x18}, - {0x30d9, 0x0a}, - {0x304c, 0x00}, - {0x304e, 0x00}, - {0x304f, 0x00}, - {0x3050, 0x00}, - {0x30b6, 0x00}, - {0x30b7, 0x00}, - {0x3116, 0x08}, - {0x3117, 0x00}, - {0x31a0, 0x20}, - {0x31a1, 0x0f}, - {0x300c, 0x3b}, - {0x300d, 0x29}, - {0x314c, 0x29}, - {0x314d, 0x01}, - {0x315a, 0x06}, - {0x3168, 0xa0}, - {0x316a, 0x7e}, - {0x319e, 0x02}, - {0x3199, 0x00}, - {0x319d, 0x00}, - {0x31dd, 0x03}, - {0x3300, 0x00}, - {0x341c, 0xff}, - {0x341d, 0x01}, - {0x3a01, 0x03}, - {0x3a18, 0x7f}, - {0x3a19, 0x00}, - {0x3a1a, 0x37}, - {0x3a1b, 0x00}, - {0x3a1c, 0x37}, - {0x3a1d, 0x00}, - {0x3a1e, 0xf7}, - {0x3a1f, 0x00}, - {0x3a20, 0x3f}, - {0x3a21, 0x00}, - {0x3a20, 0x6f}, - {0x3a21, 0x00}, - {0x3a20, 0x3f}, - {0x3a21, 0x00}, - {0x3a20, 0x5f}, - {0x3a21, 0x00}, - {0x3a20, 0x2f}, - {0x3a21, 0x00}, - {0x3078, 0x02}, - {0x3079, 0x00}, - {0x307a, 0x00}, - {0x307b, 0x00}, - {0x3080, 0x02}, - {0x3081, 0x00}, - {0x3082, 0x00}, - {0x3083, 0x00}, - {0x3088, 0x02}, - {0x3094, 0x00}, - {0x3095, 0x00}, - {0x3096, 0x00}, - {0x309b, 0x02}, - {0x309c, 0x00}, - {0x309d, 0x00}, - {0x309e, 0x00}, - {0x30a4, 0x00}, - {0x30a5, 0x00}, - {0x3288, 0x21}, - {0x328a, 0x02}, - {0x3414, 0x05}, - {0x3416, 0x18}, - {0x35Ac, 0x0e}, - {0x3648, 0x01}, - {0x364a, 0x04}, - {0x364c, 0x04}, - {0x3678, 0x01}, - {0x367c, 0x31}, - {0x367e, 0x31}, - {0x3708, 0x02}, - {0x3714, 0x01}, - {0x3715, 0x02}, - {0x3716, 0x02}, - {0x3717, 0x02}, - {0x371c, 0x3d}, - {0x371d, 0x3f}, - {0x372c, 0x00}, - {0x372d, 0x00}, - {0x372e, 0x46}, - {0x372f, 0x00}, - {0x3730, 0x89}, - {0x3731, 0x00}, - {0x3732, 0x08}, - {0x3733, 0x01}, - {0x3734, 0xfe}, - {0x3735, 0x05}, - {0x375d, 0x00}, - {0x375e, 0x00}, - {0x375f, 0x61}, - {0x3760, 0x06}, - {0x3768, 0x1b}, - {0x3769, 0x1b}, - {0x376a, 0x1a}, - {0x376b, 0x19}, - {0x376c, 0x18}, - {0x376d, 0x14}, - {0x376e, 0x0f}, - {0x3776, 0x00}, - {0x3777, 0x00}, - {0x3778, 0x46}, - {0x3779, 0x00}, - {0x377a, 0x08}, - {0x377b, 0x01}, - {0x377c, 0x45}, - {0x377d, 0x01}, - {0x377e, 0x23}, - {0x377f, 0x02}, - {0x3780, 0xd9}, - {0x3781, 0x03}, - {0x3782, 0xf5}, - {0x3783, 0x06}, - {0x3784, 0xa5}, - {0x3788, 0x0f}, - {0x378a, 0xd9}, - {0x378b, 0x03}, - {0x378c, 0xeb}, - {0x378d, 0x05}, - {0x378e, 0x87}, - {0x378f, 0x06}, - {0x3790, 0xf5}, - {0x3792, 0x43}, - {0x3794, 0x7a}, - {0x3796, 0xa1}, - {0x37b0, 0x37}, - {0x3e04, 0x0e}, - {0x30e8, 0x50}, - {0x30e9, 0x00}, - {0x3e04, 0x0e}, - {0x3002, 0x00}, +static const struct cci_reg_sequence mode_1920x1080_regs[] = { + { IMX334_REG_HTRIMMING_START, 0x03f0 }, + { IMX334_REG_HNUM, 0x0780 }, + { IMX334_REG_AREA3_ST_ADR_1, 0x02cc }, + { IMX334_REG_AREA3_ST_ADR_2, 0x02cd }, + { IMX334_REG_AREA3_WIDTH_1, 0x0438 }, + { IMX334_REG_AREA3_WIDTH_2, 0x0438 }, + { IMX334_REG_Y_OUT_SIZE, 0x0438 }, + { IMX334_REG_UNREAD_ED_ADR, 0x0a18 }, }; /* Sensor mode registers for 3840x2160@30fps */ -static const struct imx334_reg mode_3840x2160_regs[] = { - {0x3000, 0x01}, - {0x3002, 0x00}, - {0x3018, 0x04}, - {0x37b0, 0x36}, - {0x304c, 0x00}, - {0x300c, 0x3b}, - {0x300d, 0x2a}, - {0x3034, 0x26}, - {0x3035, 0x02}, - {0x314c, 0x29}, - {0x314d, 0x01}, - {0x315a, 0x02}, - {0x3168, 0xa0}, - {0x316a, 0x7e}, - {0x3288, 0x21}, - {0x328a, 0x02}, - {0x302c, 0x3c}, - {0x302d, 0x00}, - {0x302e, 0x00}, - {0x302f, 0x0f}, - {0x3076, 0x70}, - {0x3077, 0x08}, - {0x3090, 0x70}, - {0x3091, 0x08}, - {0x30d8, 0x20}, - {0x30d9, 0x12}, - {0x3308, 0x70}, - {0x3309, 0x08}, - {0x3414, 0x05}, - {0x3416, 0x18}, - {0x35ac, 0x0e}, - {0x3648, 0x01}, - {0x364a, 0x04}, - {0x364c, 0x04}, - {0x3678, 0x01}, - {0x367c, 0x31}, - {0x367e, 0x31}, - {0x3708, 0x02}, - {0x3714, 0x01}, - {0x3715, 0x02}, - {0x3716, 0x02}, - {0x3717, 0x02}, - {0x371c, 0x3d}, - {0x371d, 0x3f}, - {0x372c, 0x00}, - {0x372d, 0x00}, - {0x372e, 0x46}, - {0x372f, 0x00}, - {0x3730, 0x89}, - {0x3731, 0x00}, - {0x3732, 0x08}, - {0x3733, 0x01}, - {0x3734, 0xfe}, - {0x3735, 0x05}, - {0x375d, 0x00}, - {0x375e, 0x00}, - {0x375f, 0x61}, - {0x3760, 0x06}, - {0x3768, 0x1b}, - {0x3769, 0x1b}, - {0x376a, 0x1a}, - {0x376b, 0x19}, - {0x376c, 0x18}, - {0x376d, 0x14}, - {0x376e, 0x0f}, - {0x3776, 0x00}, - {0x3777, 0x00}, - {0x3778, 0x46}, - {0x3779, 0x00}, - {0x377a, 0x08}, - {0x377b, 0x01}, - {0x377c, 0x45}, - {0x377d, 0x01}, - {0x377e, 0x23}, - {0x377f, 0x02}, - {0x3780, 0xd9}, - {0x3781, 0x03}, - {0x3782, 0xf5}, - {0x3783, 0x06}, - {0x3784, 0xa5}, - {0x3788, 0x0f}, - {0x378a, 0xd9}, - {0x378b, 0x03}, - {0x378c, 0xeb}, - {0x378d, 0x05}, - {0x378e, 0x87}, - {0x378f, 0x06}, - {0x3790, 0xf5}, - {0x3792, 0x43}, - {0x3794, 0x7a}, - {0x3796, 0xa1}, - {0x3e04, 0x0e}, - {0x319e, 0x00}, - {0x3a00, 0x01}, - {0x3a18, 0xbf}, - {0x3a19, 0x00}, - {0x3a1a, 0x67}, - {0x3a1b, 0x00}, - {0x3a1c, 0x6f}, - {0x3a1d, 0x00}, - {0x3a1e, 0xd7}, - {0x3a1f, 0x01}, - {0x3a20, 0x6f}, - {0x3a21, 0x00}, - {0x3a22, 0xcf}, - {0x3a23, 0x00}, - {0x3a24, 0x6f}, - {0x3a25, 0x00}, - {0x3a26, 0xb7}, - {0x3a27, 0x00}, - {0x3a28, 0x5f}, - {0x3a29, 0x00}, +static const struct cci_reg_sequence mode_3840x2160_regs[] = { + { IMX334_REG_HMAX, 0x0226 }, + { IMX334_REG_INCKSEL2, 0x02 }, + { IMX334_REG_HTRIMMING_START, 0x003c }, + { IMX334_REG_HNUM, 0x0f00 }, + { IMX334_REG_AREA3_ST_ADR_1, 0x00b0 }, + { IMX334_REG_AREA3_ST_ADR_2, 0x00b1 }, + { IMX334_REG_UNREAD_ED_ADR, 0x1220 }, + { IMX334_REG_AREA3_WIDTH_1, 0x0870 }, + { IMX334_REG_AREA3_WIDTH_2, 0x0870 }, + { IMX334_REG_Y_OUT_SIZE, 0x0870 }, + { IMX334_REG_SYS_MODE, 0x0100 }, + { IMX334_REG_TCLKPOST, 0x00bf }, + { IMX334_REG_TCLKPREPARE, 0x0067 }, + { IMX334_REG_TCLKTRAIL, 0x006f }, + { IMX334_REG_TCLKZERO, 0x1d7 }, + { IMX334_REG_THSPREPARE, 0x006f }, + { IMX334_REG_THSZERO, 0x00cf }, + { IMX334_REG_THSTRAIL, 0x006f }, + { IMX334_REG_THSEXIT, 0x00b7 }, + { IMX334_REG_TPLX, 0x005f }, }; static const char * const imx334_test_pattern_menu[] = { "Disabled", "Vertical Color Bars", "Horizontal Color Bars", + "Black and Grey Bars", + "Black Color", + "White Color", }; static const int imx334_test_pattern_val[] = { IMX334_TP_DISABLE, IMX334_TP_COLOR_HBARS, IMX334_TP_COLOR_VBARS, + IMX334_TP_BLACK_GREY, + IMX334_TP_BLACK, + IMX334_TP_WHITE, }; -static const struct imx334_reg raw10_framefmt_regs[] = { - {0x3050, 0x00}, - {0x319d, 0x00}, - {0x341c, 0xff}, - {0x341d, 0x01}, +static const struct cci_reg_sequence raw10_framefmt_regs[] = { + { IMX334_REG_ADBIT, 0x00 }, + { IMX334_REG_MDBIT, 0x00 }, + { IMX334_REG_ADBIT1, 0x01ff }, }; -static const struct imx334_reg raw12_framefmt_regs[] = { - {0x3050, 0x01}, - {0x319d, 0x01}, - {0x341c, 0x47}, - {0x341d, 0x00}, +static const struct cci_reg_sequence raw12_framefmt_regs[] = { + { IMX334_REG_ADBIT, 0x01 }, + { IMX334_REG_MDBIT, 0x01 }, + { IMX334_REG_ADBIT1, 0x0047 }, }; static const u32 imx334_mbus_codes[] = { @@ -505,6 +460,32 @@ static const struct imx334_mode supported_modes[] = { .num_of_regs = ARRAY_SIZE(mode_1920x1080_regs), .regs = mode_1920x1080_regs, }, + }, { + .width = 1280, + .height = 720, + .hblank = 2480, + .vblank = 1170, + .vblank_min = 45, + .vblank_max = 132840, + .pclk = 297000000, + .link_freq_idx = 1, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1280x720_regs), + .regs = mode_1280x720_regs, + }, + }, { + .width = 640, + .height = 480, + .hblank = 2480, + .vblank = 1170, + .vblank_min = 45, + .vblank_max = 132840, + .pclk = 297000000, + .link_freq_idx = 1, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_640x480_regs), + .regs = mode_640x480_regs, + }, }, }; @@ -520,101 +501,6 @@ static inline struct imx334 *to_imx334(struct v4l2_subdev *subdev) } /** - * imx334_read_reg() - Read registers. - * @imx334: pointer to imx334 device - * @reg: register address - * @len: length of bytes to read. Max supported bytes is 4 - * @val: pointer to register value to be filled. - * - * Big endian register addresses with little endian values. - * - * Return: 0 if successful, error code otherwise. - */ -static int imx334_read_reg(struct imx334 *imx334, u16 reg, u32 len, u32 *val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&imx334->sd); - struct i2c_msg msgs[2] = {0}; - u8 addr_buf[2] = {0}; - u8 data_buf[4] = {0}; - int ret; - - if (WARN_ON(len > 4)) - return -EINVAL; - - put_unaligned_be16(reg, addr_buf); - - /* 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; - - ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); - if (ret != ARRAY_SIZE(msgs)) - return -EIO; - - *val = get_unaligned_le32(data_buf); - - return 0; -} - -/** - * imx334_write_reg() - Write register - * @imx334: pointer to imx334 device - * @reg: register address - * @len: length of bytes. Max supported bytes is 4 - * @val: register value - * - * Big endian register addresses with little endian values. - * - * Return: 0 if successful, error code otherwise. - */ -static int imx334_write_reg(struct imx334 *imx334, u16 reg, u32 len, u32 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&imx334->sd); - u8 buf[6] = {0}; - - if (WARN_ON(len > 4)) - return -EINVAL; - - put_unaligned_be16(reg, buf); - put_unaligned_le32(val, buf + 2); - if (i2c_master_send(client, buf, len + 2) != len + 2) - return -EIO; - - return 0; -} - -/** - * imx334_write_regs() - Write a list of registers - * @imx334: pointer to imx334 device - * @regs: list of registers to be written - * @len: length of registers array - * - * Return: 0 if successful, error code otherwise. - */ -static int imx334_write_regs(struct imx334 *imx334, - const struct imx334_reg *regs, u32 len) -{ - unsigned int i; - int ret; - - for (i = 0; i < len; i++) { - ret = imx334_write_reg(imx334, regs[i].address, 1, regs[i].val); - if (ret) - return ret; - } - - return 0; -} - -/** * imx334_update_controls() - Update control ranges based on streaming mode * @imx334: pointer to imx334 device * @mode: pointer to imx334_mode sensor mode @@ -659,30 +545,23 @@ static int imx334_update_controls(struct imx334 *imx334, static int imx334_update_exp_gain(struct imx334 *imx334, u32 exposure, u32 gain) { u32 lpfr, shutter; - int ret; + int ret_hold; + int ret = 0; lpfr = imx334->vblank + imx334->cur_mode->height; shutter = lpfr - exposure; - dev_dbg(imx334->dev, "Set long exp %u analog gain %u sh0 %u lpfr %u", + dev_dbg(imx334->dev, "Set long exp %u analog gain %u sh0 %u lpfr %u\n", exposure, gain, shutter, lpfr); - ret = imx334_write_reg(imx334, IMX334_REG_HOLD, 1, 1); - if (ret) - return ret; - - ret = imx334_write_reg(imx334, IMX334_REG_LPFR, 3, lpfr); - if (ret) - goto error_release_group_hold; - - ret = imx334_write_reg(imx334, IMX334_REG_SHUTTER, 3, shutter); - if (ret) - goto error_release_group_hold; - - ret = imx334_write_reg(imx334, IMX334_REG_AGAIN, 1, gain); + cci_write(imx334->cci, IMX334_REG_HOLD, 1, &ret); + cci_write(imx334->cci, IMX334_REG_VMAX, lpfr, &ret); + cci_write(imx334->cci, IMX334_REG_SHUTTER, shutter, &ret); + cci_write(imx334->cci, IMX334_REG_AGAIN, gain, &ret); -error_release_group_hold: - imx334_write_reg(imx334, IMX334_REG_HOLD, 1, 0); + ret_hold = cci_write(imx334->cci, IMX334_REG_HOLD, 0, NULL); + if (ret_hold) + return ret_hold; return ret; } @@ -707,11 +586,10 @@ static int imx334_set_ctrl(struct v4l2_ctrl *ctrl) u32 exposure; int ret; - switch (ctrl->id) { - case V4L2_CID_VBLANK: + if (ctrl->id == V4L2_CID_VBLANK) { imx334->vblank = imx334->vblank_ctrl->val; - dev_dbg(imx334->dev, "Received vblank %u, new lpfr %u", + dev_dbg(imx334->dev, "Received vblank %u, new lpfr %u\n", imx334->vblank, imx334->vblank + imx334->cur_mode->height); @@ -721,23 +599,32 @@ static int imx334_set_ctrl(struct v4l2_ctrl *ctrl) imx334->cur_mode->height - IMX334_EXPOSURE_OFFSET, 1, IMX334_EXPOSURE_DEFAULT); + if (ret) + return ret; + } + + /* Set controls only if sensor is in power on state */ + if (!pm_runtime_get_if_in_use(imx334->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_VBLANK: + exposure = imx334->exp_ctrl->val; + analog_gain = imx334->again_ctrl->val; + + ret = imx334_update_exp_gain(imx334, exposure, analog_gain); + break; case V4L2_CID_EXPOSURE: - /* Set controls only if sensor is in power on state */ - if (!pm_runtime_get_if_in_use(imx334->dev)) - return 0; - exposure = ctrl->val; analog_gain = imx334->again_ctrl->val; - dev_dbg(imx334->dev, "Received exp %u analog gain %u", + dev_dbg(imx334->dev, "Received exp %u analog gain %u\n", exposure, analog_gain); ret = imx334_update_exp_gain(imx334, exposure, analog_gain); - pm_runtime_put(imx334->dev); - break; case V4L2_CID_PIXEL_RATE: case V4L2_CID_LINK_FREQ: @@ -746,29 +633,31 @@ static int imx334_set_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_TEST_PATTERN: if (ctrl->val) { - imx334_write_reg(imx334, IMX334_TP_CLK_EN, 1, - IMX334_TP_CLK_EN_VAL); - imx334_write_reg(imx334, IMX334_DIG_CLP_MODE, 1, 0x0); - imx334_write_reg(imx334, IMX334_TPG_COLORW, 1, - IMX334_TPG_COLORW_120P); - imx334_write_reg(imx334, IMX334_REG_TP, 1, - imx334_test_pattern_val[ctrl->val]); - imx334_write_reg(imx334, IMX334_TPG_EN_DOUT, 1, - IMX334_TP_ENABLE); + cci_write(imx334->cci, IMX334_TP_CLK_EN, + IMX334_TP_CLK_EN_VAL, NULL); + cci_write(imx334->cci, IMX334_DIG_CLP_MODE, 0x0, NULL); + cci_write(imx334->cci, IMX334_TPG_COLORW, + IMX334_TPG_COLORW_120P, NULL); + cci_write(imx334->cci, IMX334_REG_TP, + imx334_test_pattern_val[ctrl->val], NULL); + cci_write(imx334->cci, IMX334_TPG_EN_DOUT, + IMX334_TP_ENABLE, NULL); } else { - imx334_write_reg(imx334, IMX334_DIG_CLP_MODE, 1, 0x1); - imx334_write_reg(imx334, IMX334_TP_CLK_EN, 1, - IMX334_TP_CLK_DIS_VAL); - imx334_write_reg(imx334, IMX334_TPG_EN_DOUT, 1, - IMX334_TP_DISABLE); + cci_write(imx334->cci, IMX334_DIG_CLP_MODE, 0x1, NULL); + cci_write(imx334->cci, IMX334_TP_CLK_EN, + IMX334_TP_CLK_DIS_VAL, NULL); + cci_write(imx334->cci, IMX334_TPG_EN_DOUT, + IMX334_TP_DISABLE, NULL); } ret = 0; break; default: - dev_err(imx334->dev, "Invalid control %d", ctrl->id); + dev_err(imx334->dev, "Invalid control %d\n", ctrl->id); ret = -EINVAL; } + pm_runtime_put(imx334->dev); + return ret; } @@ -874,8 +763,6 @@ static int imx334_get_pad_format(struct v4l2_subdev *sd, { struct imx334 *imx334 = to_imx334(sd); - mutex_lock(&imx334->mutex); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *framefmt; @@ -886,8 +773,6 @@ static int imx334_get_pad_format(struct v4l2_subdev *sd, imx334_fill_pad_format(imx334, imx334->cur_mode, fmt); } - mutex_unlock(&imx334->mutex); - return 0; } @@ -907,8 +792,6 @@ static int imx334_set_pad_format(struct v4l2_subdev *sd, const struct imx334_mode *mode; int ret = 0; - mutex_lock(&imx334->mutex); - mode = v4l2_find_nearest_size(supported_modes, ARRAY_SIZE(supported_modes), width, height, @@ -929,8 +812,6 @@ static int imx334_set_pad_format(struct v4l2_subdev *sd, imx334->cur_mode = mode; } - mutex_unlock(&imx334->mutex); - return ret; } @@ -949,8 +830,6 @@ static int imx334_init_state(struct v4l2_subdev *sd, fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; - mutex_lock(&imx334->mutex); - imx334_fill_pad_format(imx334, imx334->cur_mode, &fmt); __v4l2_ctrl_modify_range(imx334->link_freq_ctrl, 0, @@ -958,8 +837,6 @@ static int imx334_init_state(struct v4l2_subdev *sd, ~(imx334->link_freq_bitmap), __ffs(imx334->link_freq_bitmap)); - mutex_unlock(&imx334->mutex); - return imx334_set_pad_format(sd, sd_state, &fmt); } @@ -967,109 +844,113 @@ static int imx334_set_framefmt(struct imx334 *imx334) { switch (imx334->cur_code) { case MEDIA_BUS_FMT_SRGGB10_1X10: - return imx334_write_regs(imx334, raw10_framefmt_regs, - ARRAY_SIZE(raw10_framefmt_regs)); + return cci_multi_reg_write(imx334->cci, raw10_framefmt_regs, + ARRAY_SIZE(raw10_framefmt_regs), NULL); + case MEDIA_BUS_FMT_SRGGB12_1X12: - return imx334_write_regs(imx334, raw12_framefmt_regs, - ARRAY_SIZE(raw12_framefmt_regs)); + return cci_multi_reg_write(imx334->cci, raw12_framefmt_regs, + ARRAY_SIZE(raw12_framefmt_regs), NULL); } return -EINVAL; } /** - * imx334_start_streaming() - Start sensor stream - * @imx334: pointer to imx334 device + * imx334_enable_streams() - Enable specified streams for the sensor + * @sd: pointer to the V4L2 subdevice + * @state: pointer to the subdevice state + * @pad: pad number for which streams are enabled + * @streams_mask: bitmask specifying the streams to enable * * Return: 0 if successful, error code otherwise. */ -static int imx334_start_streaming(struct imx334 *imx334) +static int imx334_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) { + struct imx334 *imx334 = to_imx334(sd); const struct imx334_reg_list *reg_list; int ret; + ret = pm_runtime_resume_and_get(imx334->dev); + if (ret < 0) + return ret; + + ret = cci_multi_reg_write(imx334->cci, common_mode_regs, + ARRAY_SIZE(common_mode_regs), NULL); + if (ret) { + dev_err(imx334->dev, "fail to write common registers\n"); + goto err_rpm_put; + } + /* Write sensor mode registers */ reg_list = &imx334->cur_mode->reg_list; - ret = imx334_write_regs(imx334, reg_list->regs, - reg_list->num_of_regs); + ret = cci_multi_reg_write(imx334->cci, reg_list->regs, + reg_list->num_of_regs, NULL); if (ret) { - dev_err(imx334->dev, "fail to write initial registers"); - return ret; + dev_err(imx334->dev, "fail to write initial registers\n"); + goto err_rpm_put; + } + + ret = cci_write(imx334->cci, IMX334_REG_LANEMODE, + IMX334_CSI_4_LANE_MODE, NULL); + if (ret) { + dev_err(imx334->dev, "failed to configure lanes\n"); + goto err_rpm_put; } ret = imx334_set_framefmt(imx334); if (ret) { dev_err(imx334->dev, "%s failed to set frame format: %d\n", __func__, ret); - return ret; + goto err_rpm_put; } /* Setup handler will write actual exposure and gain */ ret = __v4l2_ctrl_handler_setup(imx334->sd.ctrl_handler); if (ret) { - dev_err(imx334->dev, "fail to setup handler"); - return ret; + dev_err(imx334->dev, "fail to setup handler\n"); + goto err_rpm_put; } /* Start streaming */ - ret = imx334_write_reg(imx334, IMX334_REG_MODE_SELECT, - 1, IMX334_MODE_STREAMING); + ret = cci_write(imx334->cci, IMX334_REG_MODE_SELECT, + IMX334_MODE_STREAMING, NULL); if (ret) { - dev_err(imx334->dev, "fail to start streaming"); - return ret; + dev_err(imx334->dev, "fail to start streaming\n"); + goto err_rpm_put; } return 0; -} -/** - * imx334_stop_streaming() - Stop sensor stream - * @imx334: pointer to imx334 device - * - * Return: 0 if successful, error code otherwise. - */ -static int imx334_stop_streaming(struct imx334 *imx334) -{ - return imx334_write_reg(imx334, IMX334_REG_MODE_SELECT, - 1, IMX334_MODE_STANDBY); +err_rpm_put: + pm_runtime_put(imx334->dev); + return ret; } /** - * imx334_set_stream() - Enable sensor streaming - * @sd: pointer to imx334 subdevice - * @enable: set to enable sensor streaming + * imx334_disable_streams() - Enable specified streams for the sensor + * @sd: pointer to the V4L2 subdevice + * @state: pointer to the subdevice state + * @pad: pad number for which streams are disabled + * @streams_mask: bitmask specifying the streams to disable * * Return: 0 if successful, error code otherwise. */ -static int imx334_set_stream(struct v4l2_subdev *sd, int enable) +static int imx334_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) { struct imx334 *imx334 = to_imx334(sd); int ret; - mutex_lock(&imx334->mutex); - - if (enable) { - ret = pm_runtime_resume_and_get(imx334->dev); - if (ret < 0) - goto error_unlock; - - ret = imx334_start_streaming(imx334); - if (ret) - goto error_power_off; - } else { - imx334_stop_streaming(imx334); - pm_runtime_put(imx334->dev); - } - - mutex_unlock(&imx334->mutex); - - return 0; + ret = cci_write(imx334->cci, IMX334_REG_MODE_SELECT, + IMX334_MODE_STANDBY, NULL); + if (ret) + dev_err(imx334->dev, "%s failed to stop stream\n", __func__); -error_power_off: pm_runtime_put(imx334->dev); -error_unlock: - mutex_unlock(&imx334->mutex); return ret; } @@ -1083,14 +964,14 @@ error_unlock: static int imx334_detect(struct imx334 *imx334) { int ret; - u32 val; + u64 val; - ret = imx334_read_reg(imx334, IMX334_REG_ID, 2, &val); + ret = cci_read(imx334->cci, IMX334_REG_ID, &val, NULL); if (ret) return ret; if (val != IMX334_ID) { - dev_err(imx334->dev, "chip id mismatch: %x!=%x", + dev_err(imx334->dev, "chip id mismatch: %x!=%llx\n", IMX334_ID, val); return -ENXIO; } @@ -1120,24 +1001,20 @@ static int imx334_parse_hw_config(struct imx334 *imx334) /* Request optional reset pin */ imx334->reset_gpio = devm_gpiod_get_optional(imx334->dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(imx334->reset_gpio)) { - dev_err(imx334->dev, "failed to get reset gpio %ld", - PTR_ERR(imx334->reset_gpio)); - return PTR_ERR(imx334->reset_gpio); - } + if (IS_ERR(imx334->reset_gpio)) + return dev_err_probe(imx334->dev, PTR_ERR(imx334->reset_gpio), + "failed to get reset gpio\n"); /* Get sensor input clock */ - imx334->inclk = devm_clk_get(imx334->dev, NULL); - if (IS_ERR(imx334->inclk)) { - dev_err(imx334->dev, "could not get inclk"); - return PTR_ERR(imx334->inclk); - } + imx334->inclk = devm_v4l2_sensor_clk_get(imx334->dev, NULL); + if (IS_ERR(imx334->inclk)) + return dev_err_probe(imx334->dev, PTR_ERR(imx334->inclk), + "could not get inclk\n"); rate = clk_get_rate(imx334->inclk); - if (rate != IMX334_INCLK_RATE) { - dev_err(imx334->dev, "inclk frequency mismatch"); - return -EINVAL; - } + if (rate != IMX334_INCLK_RATE) + return dev_err_probe(imx334->dev, -EINVAL, + "inclk frequency mismatch\n"); ep = fwnode_graph_get_next_endpoint(fwnode, NULL); if (!ep) @@ -1150,7 +1027,7 @@ static int imx334_parse_hw_config(struct imx334 *imx334) if (bus_cfg.bus.mipi_csi2.num_data_lanes != IMX334_NUM_DATA_LANES) { dev_err(imx334->dev, - "number of CSI2 data lanes %d is not supported", + "number of CSI2 data lanes %d is not supported\n", bus_cfg.bus.mipi_csi2.num_data_lanes); ret = -EINVAL; goto done_endpoint_free; @@ -1169,7 +1046,7 @@ done_endpoint_free: /* V4l2 subdevice ops */ static const struct v4l2_subdev_video_ops imx334_video_ops = { - .s_stream = imx334_set_stream, + .s_stream = v4l2_subdev_s_stream_helper, }; static const struct v4l2_subdev_pad_ops imx334_pad_ops = { @@ -1177,6 +1054,8 @@ static const struct v4l2_subdev_pad_ops imx334_pad_ops = { .enum_frame_size = imx334_enum_frame_size, .get_fmt = imx334_get_pad_format, .set_fmt = imx334_set_pad_format, + .enable_streams = imx334_enable_streams, + .disable_streams = imx334_disable_streams, }; static const struct v4l2_subdev_ops imx334_subdev_ops = { @@ -1200,11 +1079,15 @@ static int imx334_power_on(struct device *dev) struct imx334 *imx334 = to_imx334(sd); int ret; + /* + * Note: Misinterpretation of reset assertion - do not re-use this code. + * XCLR pin is using incorrect (for reset signal) logical level. + */ gpiod_set_value_cansleep(imx334->reset_gpio, 1); ret = clk_prepare_enable(imx334->inclk); if (ret) { - dev_err(imx334->dev, "fail to enable inclk"); + dev_err(imx334->dev, "fail to enable inclk\n"); goto error_reset; } @@ -1253,9 +1136,6 @@ static int imx334_init_controls(struct imx334 *imx334) if (ret) return ret; - /* Serialize controls with sensor device */ - ctrl_hdlr->lock = &imx334->mutex; - /* Initialize exposure and gain */ lpfr = mode->vblank + mode->height; imx334->exp_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, @@ -1342,29 +1222,31 @@ static int imx334_probe(struct i2c_client *client) return -ENOMEM; imx334->dev = &client->dev; + imx334->cci = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(imx334->cci)) { + dev_err(imx334->dev, "Unable to initialize I2C\n"); + return -ENODEV; + } /* Initialize subdev */ v4l2_i2c_subdev_init(&imx334->sd, client, &imx334_subdev_ops); imx334->sd.internal_ops = &imx334_internal_ops; ret = imx334_parse_hw_config(imx334); - if (ret) { - dev_err(imx334->dev, "HW configuration is not supported"); - return ret; - } - - mutex_init(&imx334->mutex); + if (ret) + return dev_err_probe(imx334->dev, ret, + "HW configuration is not supported\n"); ret = imx334_power_on(imx334->dev); if (ret) { - dev_err(imx334->dev, "failed to power-on the sensor"); - goto error_mutex_destroy; + dev_err_probe(imx334->dev, ret, "failed to power-on the sensor\n"); + return ret; } /* Check module identity */ ret = imx334_detect(imx334); if (ret) { - dev_err(imx334->dev, "failed to find sensor: %d", ret); + dev_err(imx334->dev, "failed to find sensor: %d\n", ret); goto error_power_off; } @@ -1375,7 +1257,7 @@ static int imx334_probe(struct i2c_client *client) ret = imx334_init_controls(imx334); if (ret) { - dev_err(imx334->dev, "failed to init controls: %d", ret); + dev_err(imx334->dev, "failed to init controls: %d\n", ret); goto error_power_off; } @@ -1387,31 +1269,44 @@ static int imx334_probe(struct i2c_client *client) imx334->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&imx334->sd.entity, 1, &imx334->pad); if (ret) { - dev_err(imx334->dev, "failed to init entity pads: %d", ret); + dev_err(imx334->dev, "failed to init entity pads: %d\n", ret); goto error_handler_free; } - ret = v4l2_async_register_subdev_sensor(&imx334->sd); + imx334->sd.state_lock = imx334->ctrl_handler.lock; + ret = v4l2_subdev_init_finalize(&imx334->sd); if (ret < 0) { - dev_err(imx334->dev, - "failed to register async subdev: %d", ret); + dev_err(imx334->dev, "subdev init error: %d\n", ret); goto error_media_entity; } pm_runtime_set_active(imx334->dev); pm_runtime_enable(imx334->dev); + + ret = v4l2_async_register_subdev_sensor(&imx334->sd); + if (ret < 0) { + dev_err(imx334->dev, + "failed to register async subdev: %d\n", ret); + goto error_subdev_cleanup; + } + pm_runtime_idle(imx334->dev); return 0; +error_subdev_cleanup: + v4l2_subdev_cleanup(&imx334->sd); + pm_runtime_disable(imx334->dev); + pm_runtime_set_suspended(imx334->dev); + error_media_entity: media_entity_cleanup(&imx334->sd.entity); + error_handler_free: v4l2_ctrl_handler_free(imx334->sd.ctrl_handler); + error_power_off: imx334_power_off(imx334->dev); -error_mutex_destroy: - mutex_destroy(&imx334->mutex); return ret; } @@ -1425,16 +1320,17 @@ error_mutex_destroy: static void imx334_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct imx334 *imx334 = to_imx334(sd); v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(sd); media_entity_cleanup(&sd->entity); v4l2_ctrl_handler_free(sd->ctrl_handler); pm_runtime_disable(&client->dev); - pm_runtime_suspended(&client->dev); - - mutex_destroy(&imx334->mutex); + if (!pm_runtime_status_suspended(&client->dev)) { + imx334_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); + } } static const struct dev_pm_ops imx334_pm_ops = { diff --git a/drivers/media/i2c/imx335.c b/drivers/media/i2c/imx335.c index fcfd1d851bd4..5790aa4fabeb 100644 --- a/drivers/media/i2c/imx335.c +++ b/drivers/media/i2c/imx335.c @@ -31,10 +31,11 @@ #define IMX335_REG_CPWAIT_TIME CCI_REG8(0x300d) #define IMX335_REG_WINMODE CCI_REG8(0x3018) #define IMX335_REG_HTRIMMING_START CCI_REG16_LE(0x302c) -#define IMX335_REG_HNUM CCI_REG8(0x302e) +#define IMX335_REG_HNUM CCI_REG16_LE(0x302e) /* Lines per frame */ #define IMX335_REG_VMAX CCI_REG24_LE(0x3030) +#define IMX335_REG_HMAX CCI_REG16_LE(0x3034) #define IMX335_REG_OPB_SIZE_V CCI_REG8(0x304c) #define IMX335_REG_ADBIT CCI_REG8(0x3050) @@ -42,10 +43,13 @@ #define IMX335_REG_SHUTTER CCI_REG24_LE(0x3058) #define IMX335_EXPOSURE_MIN 1 -#define IMX335_EXPOSURE_OFFSET 9 +#define IMX335_SHUTTER_MIN 9 +#define IMX335_SHUTTER_MIN_BINNED 17 #define IMX335_EXPOSURE_STEP 1 #define IMX335_EXPOSURE_DEFAULT 0x0648 +#define IMX335_REG_AREA2_WIDTH_1 CCI_REG16_LE(0x3072) + #define IMX335_REG_AREA3_ST_ADR_1 CCI_REG16_LE(0x3074) #define IMX335_REG_AREA3_WIDTH_1 CCI_REG16_LE(0x3076) @@ -56,6 +60,9 @@ #define IMX335_AGAIN_STEP 1 #define IMX335_AGAIN_DEFAULT 0 +/* Vertical flip */ +#define IMX335_REG_VREVERSE CCI_REG8(0x304f) + #define IMX335_REG_TPG_TESTCLKEN CCI_REG8(0x3148) #define IMX335_REG_INCLKSEL1 CCI_REG16_LE(0x314c) @@ -121,12 +128,19 @@ #define IMX335_NUM_DATA_LANES 4 /* IMX335 native and active pixel array size. */ -#define IMX335_NATIVE_WIDTH 2616U -#define IMX335_NATIVE_HEIGHT 1964U -#define IMX335_PIXEL_ARRAY_LEFT 12U -#define IMX335_PIXEL_ARRAY_TOP 12U -#define IMX335_PIXEL_ARRAY_WIDTH 2592U -#define IMX335_PIXEL_ARRAY_HEIGHT 1944U +static const struct v4l2_rect imx335_native_area = { + .top = 0, + .left = 0, + .width = 2696, + .height = 2044, +}; + +static const struct v4l2_rect imx335_active_area = { + .top = 50, + .left = 36, + .width = 2624, + .height = 1944, +}; /** * struct imx335_reg_list - imx335 sensor register list @@ -144,8 +158,14 @@ static const char * const imx335_supply_name[] = { "dvdd", /* Digital Core (1.2V) supply */ }; +enum imx335_scan_mode { + IMX335_ALL_PIXEL, + IMX335_2_2_BINNING, +}; + /** * struct imx335_mode - imx335 sensor mode structure + * @scan_mode: Configuration scan mode (All pixel / 2x2Binning) * @width: Frame width * @height: Frame height * @code: Format code @@ -155,8 +175,11 @@ static const char * const imx335_supply_name[] = { * @vblank_max: Maximum vertical blanking in lines * @pclk: Sensor pixel clock * @reg_list: Register list for sensor mode + * @vflip_normal: Register list vflip (normal readout) + * @vflip_inverted: Register list vflip (inverted readout) */ struct imx335_mode { + enum imx335_scan_mode scan_mode; u32 width; u32 height; u32 code; @@ -166,6 +189,8 @@ struct imx335_mode { u32 vblank_max; u64 pclk; struct imx335_reg_list reg_list; + struct imx335_reg_list vflip_normal; + struct imx335_reg_list vflip_inverted; }; /** @@ -183,12 +208,12 @@ struct imx335_mode { * @pclk_ctrl: Pointer to pixel clock control * @hblank_ctrl: Pointer to horizontal blanking control * @vblank_ctrl: Pointer to vertical blanking control + * @vflip: Pointer to vertical flip control * @exp_ctrl: Pointer to exposure control * @again_ctrl: Pointer to analog gain control * @vblank: Vertical blanking in lines * @lane_mode: Mode for number of connected data lanes * @cur_mode: Pointer to current selected sensor mode - * @mutex: Mutex for serializing sensor controls * @link_freq_bitmap: Menu bitmap for link_freq_ctrl * @cur_mbus_code: Currently selected media bus format code */ @@ -207,6 +232,7 @@ struct imx335 { struct v4l2_ctrl *pclk_ctrl; struct v4l2_ctrl *hblank_ctrl; struct v4l2_ctrl *vblank_ctrl; + struct v4l2_ctrl *vflip; struct { struct v4l2_ctrl *exp_ctrl; struct v4l2_ctrl *again_ctrl; @@ -214,7 +240,6 @@ struct imx335 { u32 vblank; u32 lane_mode; const struct imx335_mode *cur_mode; - struct mutex mutex; unsigned long link_freq_bitmap; u32 cur_mbus_code; }; @@ -252,17 +277,37 @@ static const int imx335_tpg_val[] = { }; /* Sensor mode registers */ -static const struct cci_reg_sequence mode_2592x1940_regs[] = { +static const struct cci_reg_sequence mode_2592x1944_regs[] = { { IMX335_REG_MODE_SELECT, IMX335_MODE_STANDBY }, { IMX335_REG_MASTER_MODE, 0x00 }, { IMX335_REG_WINMODE, 0x04 }, + { IMX335_REG_HMAX, 550 }, { IMX335_REG_HTRIMMING_START, 48 }, { IMX335_REG_HNUM, 2592 }, { IMX335_REG_Y_OUT_SIZE, 1944 }, - { IMX335_REG_AREA3_ST_ADR_1, 176 }, + { IMX335_REG_AREA2_WIDTH_1, 40 }, { IMX335_REG_AREA3_WIDTH_1, 3928 }, { IMX335_REG_OPB_SIZE_V, 0 }, { IMX335_REG_XVS_XHS_DRV, 0x00 }, +}; + +static const struct cci_reg_sequence mode_1312x972_regs[] = { + { IMX335_REG_MODE_SELECT, IMX335_MODE_STANDBY }, + { IMX335_REG_MASTER_MODE, 0x00 }, + { IMX335_REG_WINMODE, 0x01 }, + { IMX335_REG_HMAX, 275 }, + { IMX335_REG_HTRIMMING_START, 48 }, + { IMX335_REG_HNUM, 2600 }, + { IMX335_REG_Y_OUT_SIZE, 972 }, + { IMX335_REG_AREA2_WIDTH_1, 48 }, + { IMX335_REG_AREA3_WIDTH_1, 3936 }, + { IMX335_REG_OPB_SIZE_V, 0 }, + { IMX335_REG_XVS_XHS_DRV, 0x00 }, + { CCI_REG8(0x3300), 1 }, /* TCYCLE */ + { CCI_REG8(0x3199), 0x30 }, /* HADD/VADD */ +}; + +static const struct cci_reg_sequence imx335_common_regs[] = { { CCI_REG8(0x3288), 0x21 }, { CCI_REG8(0x328a), 0x02 }, { CCI_REG8(0x3414), 0x05 }, @@ -333,16 +378,92 @@ static const struct cci_reg_sequence mode_2592x1940_regs[] = { { CCI_REG8(0x3a00), 0x00 }, }; -static const struct cci_reg_sequence raw10_framefmt_regs[] = { - { IMX335_REG_ADBIT, 0x00 }, - { IMX335_REG_MDBIT, 0x00 }, - { IMX335_REG_ADBIT1, 0x1ff }, +static const struct cci_reg_sequence mode_2592x1944_vflip_normal[] = { + { IMX335_REG_AREA3_ST_ADR_1, 176 }, + + /* Undocumented V-Flip related registers on Page 55 of datasheet. */ + { CCI_REG8(0x3081), 0x02, }, + { CCI_REG8(0x3083), 0x02, }, + { CCI_REG16_LE(0x30b6), 0x00 }, + { CCI_REG16_LE(0x3116), 0x08 }, +}; + +static const struct cci_reg_sequence mode_2592x1944_vflip_inverted[] = { + { IMX335_REG_AREA3_ST_ADR_1, 4112 }, + + /* Undocumented V-Flip related registers on Page 55 of datasheet. */ + { CCI_REG8(0x3081), 0xfe, }, + { CCI_REG8(0x3083), 0xfe, }, + { CCI_REG16_LE(0x30b6), 0x1fa }, + { CCI_REG16_LE(0x3116), 0x002 }, +}; + +static const struct cci_reg_sequence mode_1312x972_vflip_normal[] = { + { IMX335_REG_AREA3_ST_ADR_1, 176 }, + + /* Undocumented */ + { CCI_REG8(0x3078), 0x04 }, + { CCI_REG8(0x3079), 0xfd }, + { CCI_REG8(0x307a), 0x04 }, + { CCI_REG8(0x307b), 0xfe }, + { CCI_REG8(0x307c), 0x04 }, + { CCI_REG8(0x307d), 0xfb }, + { CCI_REG8(0x307e), 0x04 }, + { CCI_REG8(0x307f), 0x02 }, + { CCI_REG8(0x3080), 0x04 }, + { CCI_REG8(0x3081), 0xfd }, + { CCI_REG8(0x3082), 0x04 }, + { CCI_REG8(0x3083), 0xfe }, + { CCI_REG8(0x3084), 0x04 }, + { CCI_REG8(0x3085), 0xfb }, + { CCI_REG8(0x3086), 0x04 }, + { CCI_REG8(0x3087), 0x02 }, + { CCI_REG8(0x30a4), 0x77 }, + { CCI_REG8(0x30a8), 0x20 }, + { CCI_REG8(0x30a9), 0x00 }, + { CCI_REG8(0x30ac), 0x08 }, + { CCI_REG8(0x30ad), 0x08 }, + { CCI_REG8(0x30b0), 0x20 }, + { CCI_REG8(0x30b1), 0x00 }, + { CCI_REG8(0x30b4), 0x10 }, + { CCI_REG8(0x30b5), 0x10 }, + { CCI_REG16_LE(0x30b6), 0x00 }, + { CCI_REG16_LE(0x3112), 0x10 }, + { CCI_REG16_LE(0x3116), 0x10 }, }; -static const struct cci_reg_sequence raw12_framefmt_regs[] = { - { IMX335_REG_ADBIT, 0x01 }, - { IMX335_REG_MDBIT, 0x01 }, - { IMX335_REG_ADBIT1, 0x47 }, +static const struct cci_reg_sequence mode_1312x972_vflip_inverted[] = { + { IMX335_REG_AREA3_ST_ADR_1, 4112 }, + + /* Undocumented */ + { CCI_REG8(0x3078), 0x04 }, + { CCI_REG8(0x3079), 0xfd }, + { CCI_REG8(0x307a), 0x04 }, + { CCI_REG8(0x307b), 0xfe }, + { CCI_REG8(0x307c), 0x04 }, + { CCI_REG8(0x307d), 0xfb }, + { CCI_REG8(0x307e), 0x04 }, + { CCI_REG8(0x307f), 0x02 }, + { CCI_REG8(0x3080), 0xfc }, + { CCI_REG8(0x3081), 0x05 }, + { CCI_REG8(0x3082), 0xfc }, + { CCI_REG8(0x3083), 0x02 }, + { CCI_REG8(0x3084), 0xfc }, + { CCI_REG8(0x3085), 0x03 }, + { CCI_REG8(0x3086), 0xfc }, + { CCI_REG8(0x3087), 0xfe }, + { CCI_REG8(0x30a4), 0x77 }, + { CCI_REG8(0x30a8), 0x20 }, + { CCI_REG8(0x30a9), 0x00 }, + { CCI_REG8(0x30ac), 0x08 }, + { CCI_REG8(0x30ad), 0x78 }, + { CCI_REG8(0x30b0), 0x20 }, + { CCI_REG8(0x30b1), 0x00 }, + { CCI_REG8(0x30b4), 0x10 }, + { CCI_REG8(0x30b5), 0x70 }, + { CCI_REG16_LE(0x30b6), 0x01f2 }, + { CCI_REG16_LE(0x3112), 0x10 }, + { CCI_REG16_LE(0x3116), 0x02 }, }; static const struct cci_reg_sequence mipi_data_rate_1188Mbps[] = { @@ -407,17 +528,49 @@ static const u32 imx335_mbus_codes[] = { }; /* Supported sensor mode configurations */ -static const struct imx335_mode supported_mode = { - .width = 2592, - .height = 1944, - .hblank = 342, - .vblank = 2556, - .vblank_min = 2556, - .vblank_max = 133060, - .pclk = 396000000, - .reg_list = { - .num_of_regs = ARRAY_SIZE(mode_2592x1940_regs), - .regs = mode_2592x1940_regs, +static const struct imx335_mode supported_modes[] = { + { + .scan_mode = IMX335_ALL_PIXEL, + .width = 2592, + .height = 1944, + .hblank = 342, + .vblank = 2556, + .vblank_min = 2556, + .vblank_max = 133060, + .pclk = 396000000, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_2592x1944_regs), + .regs = mode_2592x1944_regs, + }, + .vflip_normal = { + .num_of_regs = ARRAY_SIZE(mode_2592x1944_vflip_normal), + .regs = mode_2592x1944_vflip_normal, + }, + .vflip_inverted = { + .num_of_regs = ARRAY_SIZE(mode_2592x1944_vflip_inverted), + .regs = mode_2592x1944_vflip_inverted, + } + }, { + .scan_mode = IMX335_2_2_BINNING, + .width = 1312, + .height = 972, + .hblank = 155, + .vblank = 3528, + .vblank_min = 3528, + .vblank_max = 133060, + .pclk = 396000000, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1312x972_regs), + .regs = mode_1312x972_regs, + }, + .vflip_normal = { + .num_of_regs = ARRAY_SIZE(mode_1312x972_vflip_normal), + .regs = mode_1312x972_vflip_normal, + }, + .vflip_inverted = { + .num_of_regs = ARRAY_SIZE(mode_1312x972_vflip_inverted), + .regs = mode_1312x972_vflip_inverted, + }, }, }; @@ -449,7 +602,8 @@ static int imx335_update_controls(struct imx335 *imx335, if (ret) return ret; - ret = __v4l2_ctrl_s_ctrl(imx335->hblank_ctrl, mode->hblank); + ret = __v4l2_ctrl_modify_range(imx335->hblank_ctrl, mode->hblank, + mode->hblank, 1, mode->hblank); if (ret) return ret; @@ -492,6 +646,19 @@ static int imx335_update_exp_gain(struct imx335 *imx335, u32 exposure, u32 gain) return ret; } +static int imx335_update_vertical_flip(struct imx335 *imx335, u32 vflip) +{ + const struct imx335_reg_list * const vflip_regs = + vflip ? &imx335->cur_mode->vflip_inverted : + &imx335->cur_mode->vflip_normal; + int ret = 0; + + cci_multi_reg_write(imx335->cci, vflip_regs->regs, + vflip_regs->num_of_regs, &ret); + + return cci_write(imx335->cci, IMX335_REG_VREVERSE, vflip, &ret); +} + static int imx335_update_test_pattern(struct imx335 *imx335, u32 pattern_index) { int ret = 0; @@ -553,18 +720,24 @@ static int imx335_set_ctrl(struct v4l2_ctrl *ctrl) /* Propagate change of current control to all related controls */ if (ctrl->id == V4L2_CID_VBLANK) { + u32 shutter_min = IMX335_SHUTTER_MIN; + u32 lpfr; + imx335->vblank = imx335->vblank_ctrl->val; + lpfr = imx335->vblank + imx335->cur_mode->height; dev_dbg(imx335->dev, "Received vblank %u, new lpfr %u\n", - imx335->vblank, - imx335->vblank + imx335->cur_mode->height); - - return __v4l2_ctrl_modify_range(imx335->exp_ctrl, - IMX335_EXPOSURE_MIN, - imx335->vblank + - imx335->cur_mode->height - - IMX335_EXPOSURE_OFFSET, - 1, IMX335_EXPOSURE_DEFAULT); + imx335->vblank, lpfr); + + if (imx335->cur_mode->scan_mode == IMX335_2_2_BINNING) + shutter_min = IMX335_SHUTTER_MIN_BINNED; + + ret = __v4l2_ctrl_modify_range(imx335->exp_ctrl, + IMX335_EXPOSURE_MIN, + lpfr - shutter_min, 1, + IMX335_EXPOSURE_DEFAULT); + if (ret) + return ret; } /* @@ -575,6 +748,13 @@ static int imx335_set_ctrl(struct v4l2_ctrl *ctrl) return 0; switch (ctrl->id) { + case V4L2_CID_VBLANK: + exposure = imx335->exp_ctrl->val; + analog_gain = imx335->again_ctrl->val; + + ret = imx335_update_exp_gain(imx335, exposure, analog_gain); + + break; case V4L2_CID_EXPOSURE: exposure = ctrl->val; analog_gain = imx335->again_ctrl->val; @@ -585,6 +765,10 @@ static int imx335_set_ctrl(struct v4l2_ctrl *ctrl) ret = imx335_update_exp_gain(imx335, exposure, analog_gain); break; + case V4L2_CID_VFLIP: + ret = imx335_update_vertical_flip(imx335, ctrl->val); + + break; case V4L2_CID_TEST_PATTERN: ret = imx335_update_test_pattern(imx335, ctrl->val); @@ -651,16 +835,16 @@ static int imx335_enum_frame_size(struct v4l2_subdev *sd, struct imx335 *imx335 = to_imx335(sd); u32 code; - if (fsize->index > ARRAY_SIZE(imx335_mbus_codes)) + if (fsize->index >= ARRAY_SIZE(supported_modes)) return -EINVAL; code = imx335_get_format_code(imx335, fsize->code); if (fsize->code != code) return -EINVAL; - fsize->min_width = supported_mode.width; + fsize->min_width = supported_modes[fsize->index].width; fsize->max_width = fsize->min_width; - fsize->min_height = supported_mode.height; + fsize->min_height = supported_modes[fsize->index].height; fsize->max_height = fsize->min_height; return 0; @@ -688,36 +872,6 @@ static void imx335_fill_pad_format(struct imx335 *imx335, } /** - * imx335_get_pad_format() - Get subdevice pad format - * @sd: pointer to imx335 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * @fmt: V4L2 sub-device format need to be set - * - * Return: 0 if successful, error code otherwise. - */ -static int imx335_get_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct imx335 *imx335 = to_imx335(sd); - - mutex_lock(&imx335->mutex); - - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - struct v4l2_mbus_framefmt *framefmt; - - framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); - fmt->format = *framefmt; - } else { - imx335_fill_pad_format(imx335, imx335->cur_mode, fmt); - } - - mutex_unlock(&imx335->mutex); - - return 0; -} - -/** * imx335_set_pad_format() - Set subdevice pad format * @sd: pointer to imx335 V4L2 sub-device structure * @sd_state: V4L2 sub-device configuration @@ -730,12 +884,16 @@ static int imx335_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_format *fmt) { struct imx335 *imx335 = to_imx335(sd); + struct v4l2_mbus_framefmt *format; const struct imx335_mode *mode; + struct v4l2_rect *crop; int i, ret = 0; - mutex_lock(&imx335->mutex); + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), + width, height, + fmt->format.width, fmt->format.height); - mode = &supported_mode; for (i = 0; i < ARRAY_SIZE(imx335_mbus_codes); i++) { if (imx335_mbus_codes[i] == fmt->format.code) imx335->cur_mbus_code = imx335_mbus_codes[i]; @@ -743,19 +901,25 @@ static int imx335_set_pad_format(struct v4l2_subdev *sd, imx335_fill_pad_format(imx335, mode, fmt); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - struct v4l2_mbus_framefmt *framefmt; + format = v4l2_subdev_state_get_format(sd_state, fmt->pad); + *format = fmt->format; - framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); - *framefmt = fmt->format; - } else { + crop = v4l2_subdev_state_get_crop(sd_state, fmt->pad); + crop->width = fmt->format.width; + crop->height = fmt->format.height; + if (mode->scan_mode == IMX335_2_2_BINNING) { + crop->width *= 2; + crop->height *= 2; + } + crop->left = (imx335_native_area.width - crop->width) / 2; + crop->top = (imx335_native_area.height - crop->height) / 2; + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { ret = imx335_update_controls(imx335, mode); if (!ret) imx335->cur_mode = mode; } - mutex_unlock(&imx335->mutex); - return ret; } @@ -773,14 +937,12 @@ static int imx335_init_state(struct v4l2_subdev *sd, struct v4l2_subdev_format fmt = { 0 }; fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; - imx335_fill_pad_format(imx335, &supported_mode, &fmt); + imx335_fill_pad_format(imx335, &supported_modes[0], &fmt); - mutex_lock(&imx335->mutex); __v4l2_ctrl_modify_range(imx335->link_freq_ctrl, 0, __fls(imx335->link_freq_bitmap), ~(imx335->link_freq_bitmap), __ffs(imx335->link_freq_bitmap)); - mutex_unlock(&imx335->mutex); return imx335_set_pad_format(sd, sd_state, &fmt); } @@ -798,22 +960,18 @@ static int imx335_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_selection *sel) { switch (sel->target) { - case V4L2_SEL_TGT_NATIVE_SIZE: - sel->r.top = 0; - sel->r.left = 0; - sel->r.width = IMX335_NATIVE_WIDTH; - sel->r.height = IMX335_NATIVE_HEIGHT; + case V4L2_SEL_TGT_CROP: + sel->r = *v4l2_subdev_state_get_crop(sd_state, 0); return 0; - case V4L2_SEL_TGT_CROP: + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r = imx335_native_area; + return 0; + case V4L2_SEL_TGT_CROP_DEFAULT: case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.top = IMX335_PIXEL_ARRAY_TOP; - sel->r.left = IMX335_PIXEL_ARRAY_LEFT; - sel->r.width = IMX335_PIXEL_ARRAY_WIDTH; - sel->r.height = IMX335_PIXEL_ARRAY_HEIGHT; - + sel->r = imx335_active_area; return 0; } @@ -822,39 +980,65 @@ static int imx335_get_selection(struct v4l2_subdev *sd, static int imx335_set_framefmt(struct imx335 *imx335) { - switch (imx335->cur_mbus_code) { - case MEDIA_BUS_FMT_SRGGB10_1X10: - return cci_multi_reg_write(imx335->cci, raw10_framefmt_regs, - ARRAY_SIZE(raw10_framefmt_regs), - NULL); - - case MEDIA_BUS_FMT_SRGGB12_1X12: - return cci_multi_reg_write(imx335->cci, raw12_framefmt_regs, - ARRAY_SIZE(raw12_framefmt_regs), - NULL); + /* + * In the all-pixel scan mode the AD conversion shall match the output + * bit width requested. + * + * However, when 2/2 binning is enabled, the AD conversion is always + * 10-bit, so we ensure ADBIT is clear and ADBIT1 is assigned 0x1ff. + * That's as much as the documentation gives us... + */ + int ret = 0; + u8 bpp = imx335->cur_mbus_code == MEDIA_BUS_FMT_SRGGB10_1X10 ? 10 : 12; + u8 ad_conv = bpp; + + /* Start with the output mode */ + cci_write(imx335->cci, IMX335_REG_MDBIT, bpp == 12, &ret); + + /* Enforce 10 bit AD on binning modes */ + if (imx335->cur_mode->scan_mode == IMX335_2_2_BINNING) + ad_conv = 10; + + /* AD Conversion configuration */ + if (ad_conv == 10) { + cci_write(imx335->cci, IMX335_REG_ADBIT, 0x00, &ret); + cci_write(imx335->cci, IMX335_REG_ADBIT1, 0x1ff, &ret); + } else { /* 12 bit AD Conversion */ + cci_write(imx335->cci, IMX335_REG_ADBIT, 0x01, &ret); + cci_write(imx335->cci, IMX335_REG_ADBIT1, 0x47, &ret); } - return -EINVAL; + return ret; } /** - * imx335_start_streaming() - Start sensor stream - * @imx335: pointer to imx335 device + * imx335_enable_streams() - Enable sensor streams + * @sd: V4L2 subdevice + * @state: V4L2 subdevice state + * @pad: The pad to enable + * @streams_mask: Bitmask of streams to enable * * Return: 0 if successful, error code otherwise. */ -static int imx335_start_streaming(struct imx335 *imx335) +static int imx335_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) { + struct imx335 *imx335 = to_imx335(sd); const struct imx335_reg_list *reg_list; int ret; + ret = pm_runtime_resume_and_get(imx335->dev); + if (ret < 0) + return ret; + /* Setup PLL */ reg_list = &link_freq_reglist[__ffs(imx335->link_freq_bitmap)]; ret = cci_multi_reg_write(imx335->cci, reg_list->regs, reg_list->num_of_regs, NULL); if (ret) { dev_err(imx335->dev, "%s failed to set plls\n", __func__); - return ret; + goto err_rpm_put; } /* Write sensor mode registers */ @@ -863,27 +1047,35 @@ static int imx335_start_streaming(struct imx335 *imx335) reg_list->num_of_regs, NULL); if (ret) { dev_err(imx335->dev, "fail to write initial registers\n"); - return ret; + goto err_rpm_put; + } + + /* Write sensor common registers */ + ret = cci_multi_reg_write(imx335->cci, imx335_common_regs, + ARRAY_SIZE(imx335_common_regs), NULL); + if (ret) { + dev_err(imx335->dev, "fail to write initial registers\n"); + goto err_rpm_put; } ret = imx335_set_framefmt(imx335); if (ret) { dev_err(imx335->dev, "%s failed to set frame format: %d\n", __func__, ret); - return ret; + goto err_rpm_put; } /* Configure lanes */ ret = cci_write(imx335->cci, IMX335_REG_LANEMODE, imx335->lane_mode, NULL); if (ret) - return ret; + goto err_rpm_put; /* Setup handler will write actual exposure and gain */ ret = __v4l2_ctrl_handler_setup(imx335->sd.ctrl_handler); if (ret) { dev_err(imx335->dev, "fail to setup handler\n"); - return ret; + goto err_rpm_put; } /* Start streaming */ @@ -891,62 +1083,39 @@ static int imx335_start_streaming(struct imx335 *imx335) IMX335_MODE_STREAMING, NULL); if (ret) { dev_err(imx335->dev, "fail to start streaming\n"); - return ret; + goto err_rpm_put; } /* Initial regulator stabilization period */ usleep_range(18000, 20000); return 0; -} -/** - * imx335_stop_streaming() - Stop sensor stream - * @imx335: pointer to imx335 device - * - * Return: 0 if successful, error code otherwise. - */ -static int imx335_stop_streaming(struct imx335 *imx335) -{ - return cci_write(imx335->cci, IMX335_REG_MODE_SELECT, - IMX335_MODE_STANDBY, NULL); +err_rpm_put: + pm_runtime_put(imx335->dev); + + return ret; } /** - * imx335_set_stream() - Enable sensor streaming - * @sd: pointer to imx335 subdevice - * @enable: set to enable sensor streaming + * imx335_disable_streams() - Disable sensor streams + * @sd: V4L2 subdevice + * @state: V4L2 subdevice state + * @pad: The pad to disable + * @streams_mask: Bitmask of streams to disable * * Return: 0 if successful, error code otherwise. */ -static int imx335_set_stream(struct v4l2_subdev *sd, int enable) +static int imx335_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) { struct imx335 *imx335 = to_imx335(sd); int ret; - mutex_lock(&imx335->mutex); - - if (enable) { - ret = pm_runtime_resume_and_get(imx335->dev); - if (ret) - goto error_unlock; - - ret = imx335_start_streaming(imx335); - if (ret) - goto error_power_off; - } else { - imx335_stop_streaming(imx335); - pm_runtime_put(imx335->dev); - } - - mutex_unlock(&imx335->mutex); - - return 0; - -error_power_off: + ret = cci_write(imx335->cci, IMX335_REG_MODE_SELECT, + IMX335_MODE_STANDBY, NULL); pm_runtime_put(imx335->dev); -error_unlock: - mutex_unlock(&imx335->mutex); return ret; } @@ -999,8 +1168,8 @@ static int imx335_parse_hw_config(struct imx335 *imx335) imx335->reset_gpio = devm_gpiod_get_optional(imx335->dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(imx335->reset_gpio)) { - dev_err(imx335->dev, "failed to get reset gpio %ld\n", - PTR_ERR(imx335->reset_gpio)); + dev_err(imx335->dev, "failed to get reset gpio %pe\n", + imx335->reset_gpio); return PTR_ERR(imx335->reset_gpio); } @@ -1016,11 +1185,10 @@ static int imx335_parse_hw_config(struct imx335 *imx335) } /* Get sensor input clock */ - imx335->inclk = devm_clk_get(imx335->dev, NULL); - if (IS_ERR(imx335->inclk)) { - dev_err(imx335->dev, "could not get inclk\n"); - return PTR_ERR(imx335->inclk); - } + imx335->inclk = devm_v4l2_sensor_clk_get(imx335->dev, NULL); + if (IS_ERR(imx335->inclk)) + return dev_err_probe(imx335->dev, PTR_ERR(imx335->inclk), + "could not get inclk\n"); rate = clk_get_rate(imx335->inclk); if (rate != IMX335_INCLK_RATE) { @@ -1067,7 +1235,7 @@ done_endpoint_free: /* V4l2 subdevice ops */ static const struct v4l2_subdev_video_ops imx335_video_ops = { - .s_stream = imx335_set_stream, + .s_stream = v4l2_subdev_s_stream_helper, }; static const struct v4l2_subdev_pad_ops imx335_pad_ops = { @@ -1075,8 +1243,10 @@ static const struct v4l2_subdev_pad_ops imx335_pad_ops = { .enum_frame_size = imx335_enum_frame_size, .get_selection = imx335_get_selection, .set_selection = imx335_get_selection, - .get_fmt = imx335_get_pad_format, + .get_fmt = v4l2_subdev_get_fmt, .set_fmt = imx335_set_pad_format, + .enable_streams = imx335_enable_streams, + .disable_streams = imx335_disable_streams, }; static const struct v4l2_subdev_ops imx335_subdev_ops = { @@ -1158,7 +1328,7 @@ static int imx335_init_controls(struct imx335 *imx335) struct v4l2_ctrl_handler *ctrl_hdlr = &imx335->ctrl_handler; const struct imx335_mode *mode = imx335->cur_mode; struct v4l2_fwnode_device_properties props; - u32 lpfr; + u32 lpfr, shutter_min; int ret; ret = v4l2_fwnode_device_parse(imx335->dev, &props); @@ -1166,20 +1336,20 @@ static int imx335_init_controls(struct imx335 *imx335) return ret; /* v4l2_fwnode_device_properties can add two more controls */ - ret = v4l2_ctrl_handler_init(ctrl_hdlr, 9); + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 10); if (ret) return ret; - /* Serialize controls with sensor device */ - ctrl_hdlr->lock = &imx335->mutex; - /* Initialize exposure and gain */ lpfr = mode->vblank + mode->height; + shutter_min = IMX335_SHUTTER_MIN; + if (mode->scan_mode == IMX335_2_2_BINNING) + shutter_min = IMX335_SHUTTER_MIN_BINNED; imx335->exp_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, &imx335_ctrl_ops, V4L2_CID_EXPOSURE, IMX335_EXPOSURE_MIN, - lpfr - IMX335_EXPOSURE_OFFSET, + lpfr - shutter_min, IMX335_EXPOSURE_STEP, IMX335_EXPOSURE_DEFAULT); @@ -1201,6 +1371,13 @@ static int imx335_init_controls(struct imx335 *imx335) v4l2_ctrl_cluster(2, &imx335->exp_ctrl); + imx335->vflip = v4l2_ctrl_new_std(ctrl_hdlr, + &imx335_ctrl_ops, + V4L2_CID_VFLIP, + 0, 1, 1, 0); + if (imx335->vflip) + imx335->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + imx335->vblank_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, &imx335_ctrl_ops, V4L2_CID_VBLANK, @@ -1285,12 +1462,10 @@ static int imx335_probe(struct i2c_client *client) return ret; } - mutex_init(&imx335->mutex); - ret = imx335_power_on(imx335->dev); if (ret) { dev_err(imx335->dev, "failed to power-on the sensor\n"); - goto error_mutex_destroy; + return ret; } /* Check module identity */ @@ -1301,7 +1476,7 @@ static int imx335_probe(struct i2c_client *client) } /* Set default mode to max resolution */ - imx335->cur_mode = &supported_mode; + imx335->cur_mode = &supported_modes[0]; imx335->cur_mbus_code = imx335_mbus_codes[0]; imx335->vblank = imx335->cur_mode->vblank; @@ -1323,11 +1498,18 @@ static int imx335_probe(struct i2c_client *client) goto error_handler_free; } + imx335->sd.state_lock = imx335->ctrl_handler.lock; + ret = v4l2_subdev_init_finalize(&imx335->sd); + if (ret < 0) { + dev_err(imx335->dev, "subdev init error\n"); + goto error_media_entity; + } + ret = v4l2_async_register_subdev_sensor(&imx335->sd); if (ret < 0) { dev_err(imx335->dev, "failed to register async subdev: %d\n", ret); - goto error_media_entity; + goto error_subdev_cleanup; } pm_runtime_set_active(imx335->dev); @@ -1336,14 +1518,14 @@ static int imx335_probe(struct i2c_client *client) return 0; +error_subdev_cleanup: + v4l2_subdev_cleanup(&imx335->sd); error_media_entity: media_entity_cleanup(&imx335->sd.entity); error_handler_free: v4l2_ctrl_handler_free(imx335->sd.ctrl_handler); error_power_off: imx335_power_off(imx335->dev); -error_mutex_destroy: - mutex_destroy(&imx335->mutex); return ret; } @@ -1357,9 +1539,9 @@ error_mutex_destroy: static void imx335_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct imx335 *imx335 = to_imx335(sd); v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(sd); media_entity_cleanup(&sd->entity); v4l2_ctrl_handler_free(sd->ctrl_handler); @@ -1367,8 +1549,6 @@ static void imx335_remove(struct i2c_client *client) if (!pm_runtime_status_suspended(&client->dev)) imx335_power_off(&client->dev); pm_runtime_set_suspended(&client->dev); - - mutex_destroy(&imx335->mutex); } static const struct dev_pm_ops imx335_pm_ops = { diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index b2dce67c0b6b..776107efe386 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -1,11 +1,13 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2018 Intel Corporation -#include <linux/unaligned.h> #include <linux/acpi.h> +#include <linux/clk.h> #include <linux/i2c.h> #include <linux/module.h> #include <linux/pm_runtime.h> +#include <linux/unaligned.h> + #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-event.h> @@ -92,11 +94,13 @@ struct imx355_mode { }; struct imx355_hwcfg { - u32 ext_clk; /* sensor external clk */ unsigned long link_freq_bitmap; }; struct imx355 { + struct device *dev; + struct clk *clk; + struct v4l2_subdev sd; struct media_pad pad; @@ -1136,14 +1140,13 @@ static int imx355_write_reg(struct imx355 *imx355, u16 reg, u32 len, u32 val) static int imx355_write_regs(struct imx355 *imx355, const struct imx355_reg *regs, u32 len) { - struct i2c_client *client = v4l2_get_subdevdata(&imx355->sd); int ret; u32 i; for (i = 0; i < len; i++) { ret = imx355_write_reg(imx355, regs[i].address, 1, regs[i].val); if (ret) { - dev_err_ratelimited(&client->dev, + dev_err_ratelimited(imx355->dev, "write reg 0x%4.4x return err %d", regs[i].address, ret); @@ -1178,7 +1181,6 @@ static int imx355_set_ctrl(struct v4l2_ctrl *ctrl) { struct imx355 *imx355 = container_of(ctrl->handler, struct imx355, ctrl_handler); - struct i2c_client *client = v4l2_get_subdevdata(&imx355->sd); s64 max; int ret; @@ -1197,7 +1199,7 @@ static int imx355_set_ctrl(struct v4l2_ctrl *ctrl) * Applying V4L2 control value only happens * when power is up for streaming */ - if (!pm_runtime_get_if_in_use(&client->dev)) + if (!pm_runtime_get_if_in_use(imx355->dev)) return 0; switch (ctrl->id) { @@ -1231,12 +1233,12 @@ static int imx355_set_ctrl(struct v4l2_ctrl *ctrl) break; default: ret = -EINVAL; - dev_info(&client->dev, "ctrl(id:0x%x,val:0x%x) is not handled", + dev_info(imx355->dev, "ctrl(id:0x%x,val:0x%x) is not handled", ctrl->id, ctrl->val); break; } - pm_runtime_put(&client->dev); + pm_runtime_put(imx355->dev); return ret; } @@ -1385,7 +1387,6 @@ imx355_set_pad_format(struct v4l2_subdev *sd, /* Start streaming */ static int imx355_start_streaming(struct imx355 *imx355) { - struct i2c_client *client = v4l2_get_subdevdata(&imx355->sd); const struct imx355_reg_list *reg_list; int ret; @@ -1393,7 +1394,7 @@ static int imx355_start_streaming(struct imx355 *imx355) reg_list = &imx355_global_setting; ret = imx355_write_regs(imx355, reg_list->regs, reg_list->num_of_regs); if (ret) { - dev_err(&client->dev, "failed to set global settings"); + dev_err(imx355->dev, "failed to set global settings"); return ret; } @@ -1401,7 +1402,7 @@ static int imx355_start_streaming(struct imx355 *imx355) reg_list = &imx355->cur_mode->reg_list; ret = imx355_write_regs(imx355, reg_list->regs, reg_list->num_of_regs); if (ret) { - dev_err(&client->dev, "failed to set mode"); + dev_err(imx355->dev, "failed to set mode"); return ret; } @@ -1429,13 +1430,12 @@ static int imx355_stop_streaming(struct imx355 *imx355) static int imx355_set_stream(struct v4l2_subdev *sd, int enable) { struct imx355 *imx355 = to_imx355(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); int ret = 0; mutex_lock(&imx355->mutex); if (enable) { - ret = pm_runtime_resume_and_get(&client->dev); + ret = pm_runtime_resume_and_get(imx355->dev); if (ret < 0) goto err_unlock; @@ -1448,7 +1448,7 @@ static int imx355_set_stream(struct v4l2_subdev *sd, int enable) goto err_rpm_put; } else { imx355_stop_streaming(imx355); - pm_runtime_put(&client->dev); + pm_runtime_put(imx355->dev); } /* vflip and hflip cannot change during streaming */ @@ -1460,7 +1460,7 @@ static int imx355_set_stream(struct v4l2_subdev *sd, int enable) return ret; err_rpm_put: - pm_runtime_put(&client->dev); + pm_runtime_put(imx355->dev); err_unlock: mutex_unlock(&imx355->mutex); @@ -1470,7 +1470,6 @@ err_unlock: /* Verify chip ID */ static int imx355_identify_module(struct imx355 *imx355) { - struct i2c_client *client = v4l2_get_subdevdata(&imx355->sd); int ret; u32 val; @@ -1479,7 +1478,7 @@ static int imx355_identify_module(struct imx355 *imx355) return ret; if (val != IMX355_CHIP_ID) { - dev_err(&client->dev, "chip id mismatch: %x!=%x", + dev_err(imx355->dev, "chip id mismatch: %x!=%x", IMX355_CHIP_ID, val); return -EIO; } @@ -1519,7 +1518,6 @@ static const struct v4l2_subdev_internal_ops imx355_internal_ops = { /* Initialize control handlers */ static int imx355_init_controls(struct imx355 *imx355) { - struct i2c_client *client = v4l2_get_subdevdata(&imx355->sd); struct v4l2_fwnode_device_properties props; struct v4l2_ctrl_handler *ctrl_hdlr; s64 exposure_max; @@ -1600,11 +1598,11 @@ static int imx355_init_controls(struct imx355 *imx355) 0, 0, imx355_test_pattern_menu); if (ctrl_hdlr->error) { ret = ctrl_hdlr->error; - dev_err(&client->dev, "control init failed: %d", ret); + dev_err(imx355->dev, "control init failed: %d", ret); goto error; } - ret = v4l2_fwnode_device_parse(&client->dev, &props); + ret = v4l2_fwnode_device_parse(imx355->dev, &props); if (ret) goto error; @@ -1648,20 +1646,6 @@ static struct imx355_hwcfg *imx355_get_hwcfg(struct device *dev) if (!cfg) goto out_err; - ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", - &cfg->ext_clk); - if (ret) { - dev_err(dev, "can't get clock frequency"); - goto out_err; - } - - dev_dbg(dev, "ext clk: %d", cfg->ext_clk); - if (cfg->ext_clk != IMX355_EXT_CLK) { - dev_err(dev, "external clock %d is not supported", - cfg->ext_clk); - goto out_err; - } - ret = v4l2_link_freq_to_bitmap(dev, bus_cfg.link_frequencies, bus_cfg.nr_of_link_frequencies, link_freq_menu_items, @@ -1683,27 +1667,41 @@ out_err: static int imx355_probe(struct i2c_client *client) { struct imx355 *imx355; + unsigned long freq; int ret; imx355 = devm_kzalloc(&client->dev, sizeof(*imx355), GFP_KERNEL); if (!imx355) return -ENOMEM; + imx355->dev = &client->dev; + mutex_init(&imx355->mutex); + imx355->clk = devm_v4l2_sensor_clk_get(imx355->dev, NULL); + if (IS_ERR(imx355->clk)) + return dev_err_probe(imx355->dev, PTR_ERR(imx355->clk), + "failed to get clock\n"); + + freq = clk_get_rate(imx355->clk); + if (freq != IMX355_EXT_CLK) + return dev_err_probe(imx355->dev, -EINVAL, + "external clock %lu is not supported\n", + freq); + /* Initialize subdev */ v4l2_i2c_subdev_init(&imx355->sd, client, &imx355_subdev_ops); /* Check module identity */ ret = imx355_identify_module(imx355); if (ret) { - dev_err(&client->dev, "failed to find sensor: %d", ret); + dev_err(imx355->dev, "failed to find sensor: %d", ret); goto error_probe; } - imx355->hwcfg = imx355_get_hwcfg(&client->dev); + imx355->hwcfg = imx355_get_hwcfg(imx355->dev); if (!imx355->hwcfg) { - dev_err(&client->dev, "failed to get hwcfg"); + dev_err(imx355->dev, "failed to get hwcfg"); ret = -ENODEV; goto error_probe; } @@ -1713,7 +1711,7 @@ static int imx355_probe(struct i2c_client *client) ret = imx355_init_controls(imx355); if (ret) { - dev_err(&client->dev, "failed to init controls: %d", ret); + dev_err(imx355->dev, "failed to init controls: %d", ret); goto error_probe; } @@ -1728,7 +1726,7 @@ static int imx355_probe(struct i2c_client *client) imx355->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&imx355->sd.entity, 1, &imx355->pad); if (ret) { - dev_err(&client->dev, "failed to init entity pads: %d", ret); + dev_err(imx355->dev, "failed to init entity pads: %d", ret); goto error_handler_free; } @@ -1736,9 +1734,9 @@ static int imx355_probe(struct i2c_client *client) * Device is already turned on by i2c-core with ACPI domain PM. * Enable runtime PM and turn off the device. */ - pm_runtime_set_active(&client->dev); - pm_runtime_enable(&client->dev); - pm_runtime_idle(&client->dev); + pm_runtime_set_active(imx355->dev); + pm_runtime_enable(imx355->dev); + pm_runtime_idle(imx355->dev); ret = v4l2_async_register_subdev_sensor(&imx355->sd); if (ret < 0) @@ -1747,8 +1745,8 @@ static int imx355_probe(struct i2c_client *client) return 0; error_media_entity_runtime_pm: - pm_runtime_disable(&client->dev); - pm_runtime_set_suspended(&client->dev); + pm_runtime_disable(imx355->dev); + pm_runtime_set_suspended(imx355->dev); media_entity_cleanup(&imx355->sd.entity); error_handler_free: @@ -1769,8 +1767,8 @@ static void imx355_remove(struct i2c_client *client) media_entity_cleanup(&sd->entity); v4l2_ctrl_handler_free(sd->ctrl_handler); - pm_runtime_disable(&client->dev); - pm_runtime_set_suspended(&client->dev); + pm_runtime_disable(imx355->dev); + pm_runtime_set_suspended(imx355->dev); mutex_destroy(&imx355->mutex); } diff --git a/drivers/media/i2c/imx412.c b/drivers/media/i2c/imx412.c index 0bfe3046fcc8..b3826f803547 100644 --- a/drivers/media/i2c/imx412.c +++ b/drivers/media/i2c/imx412.c @@ -547,7 +547,7 @@ static int imx412_update_exp_gain(struct imx412 *imx412, u32 exposure, u32 gain) lpfr = imx412->vblank + imx412->cur_mode->height; - dev_dbg(imx412->dev, "Set exp %u, analog gain %u, lpfr %u", + dev_dbg(imx412->dev, "Set exp %u, analog gain %u, lpfr %u\n", exposure, gain, lpfr); ret = imx412_write_reg(imx412, IMX412_REG_HOLD, 1, 1); @@ -594,7 +594,7 @@ static int imx412_set_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_VBLANK: imx412->vblank = imx412->vblank_ctrl->val; - dev_dbg(imx412->dev, "Received vblank %u, new lpfr %u", + dev_dbg(imx412->dev, "Received vblank %u, new lpfr %u\n", imx412->vblank, imx412->vblank + imx412->cur_mode->height); @@ -613,7 +613,7 @@ static int imx412_set_ctrl(struct v4l2_ctrl *ctrl) exposure = ctrl->val; analog_gain = imx412->again_ctrl->val; - dev_dbg(imx412->dev, "Received exp %u, analog gain %u", + dev_dbg(imx412->dev, "Received exp %u, analog gain %u\n", exposure, analog_gain); ret = imx412_update_exp_gain(imx412, exposure, analog_gain); @@ -622,7 +622,7 @@ static int imx412_set_ctrl(struct v4l2_ctrl *ctrl) break; default: - dev_err(imx412->dev, "Invalid control %d", ctrl->id); + dev_err(imx412->dev, "Invalid control %d\n", ctrl->id); ret = -EINVAL; } @@ -803,14 +803,14 @@ static int imx412_start_streaming(struct imx412 *imx412) ret = imx412_write_regs(imx412, reg_list->regs, reg_list->num_of_regs); if (ret) { - dev_err(imx412->dev, "fail to write initial registers"); + dev_err(imx412->dev, "fail to write initial registers\n"); return ret; } /* Setup handler will write actual exposure and gain */ ret = __v4l2_ctrl_handler_setup(imx412->sd.ctrl_handler); if (ret) { - dev_err(imx412->dev, "fail to setup handler"); + dev_err(imx412->dev, "fail to setup handler\n"); return ret; } @@ -821,7 +821,7 @@ static int imx412_start_streaming(struct imx412 *imx412) ret = imx412_write_reg(imx412, IMX412_REG_MODE_SELECT, 1, IMX412_MODE_STREAMING); if (ret) { - dev_err(imx412->dev, "fail to start streaming"); + dev_err(imx412->dev, "fail to start streaming\n"); return ret; } @@ -895,7 +895,7 @@ static int imx412_detect(struct imx412 *imx412) return ret; if (val != IMX412_ID) { - dev_err(imx412->dev, "chip id mismatch: %x!=%x", + dev_err(imx412->dev, "chip id mismatch: %x!=%x\n", IMX412_ID, val); return -ENXIO; } @@ -927,21 +927,20 @@ static int imx412_parse_hw_config(struct imx412 *imx412) imx412->reset_gpio = devm_gpiod_get_optional(imx412->dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(imx412->reset_gpio)) { - dev_err(imx412->dev, "failed to get reset gpio %ld", - PTR_ERR(imx412->reset_gpio)); + dev_err(imx412->dev, "failed to get reset gpio %pe\n", + imx412->reset_gpio); return PTR_ERR(imx412->reset_gpio); } /* Get sensor input clock */ - imx412->inclk = devm_clk_get(imx412->dev, NULL); - if (IS_ERR(imx412->inclk)) { - dev_err(imx412->dev, "could not get inclk"); - return PTR_ERR(imx412->inclk); - } + imx412->inclk = devm_v4l2_sensor_clk_get(imx412->dev, NULL); + if (IS_ERR(imx412->inclk)) + return dev_err_probe(imx412->dev, PTR_ERR(imx412->inclk), + "could not get inclk\n"); rate = clk_get_rate(imx412->inclk); if (rate != IMX412_INCLK_RATE) { - dev_err(imx412->dev, "inclk frequency mismatch"); + dev_err(imx412->dev, "inclk frequency mismatch\n"); return -EINVAL; } @@ -966,14 +965,14 @@ static int imx412_parse_hw_config(struct imx412 *imx412) if (bus_cfg.bus.mipi_csi2.num_data_lanes != IMX412_NUM_DATA_LANES) { dev_err(imx412->dev, - "number of CSI2 data lanes %d is not supported", + "number of CSI2 data lanes %d is not supported\n", bus_cfg.bus.mipi_csi2.num_data_lanes); ret = -EINVAL; goto done_endpoint_free; } if (!bus_cfg.nr_of_link_frequencies) { - dev_err(imx412->dev, "no link frequencies defined"); + dev_err(imx412->dev, "no link frequencies defined\n"); ret = -EINVAL; goto done_endpoint_free; } @@ -1034,7 +1033,7 @@ static int imx412_power_on(struct device *dev) ret = clk_prepare_enable(imx412->inclk); if (ret) { - dev_err(imx412->dev, "fail to enable inclk"); + dev_err(imx412->dev, "fail to enable inclk\n"); goto error_reset; } @@ -1145,7 +1144,7 @@ static int imx412_init_controls(struct imx412 *imx412) imx412->hblank_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; if (ctrl_hdlr->error) { - dev_err(imx412->dev, "control init failed: %d", + dev_err(imx412->dev, "control init failed: %d\n", ctrl_hdlr->error); v4l2_ctrl_handler_free(ctrl_hdlr); return ctrl_hdlr->error; @@ -1183,7 +1182,7 @@ static int imx412_probe(struct i2c_client *client) ret = imx412_parse_hw_config(imx412); if (ret) { - dev_err(imx412->dev, "HW configuration is not supported"); + dev_err(imx412->dev, "HW configuration is not supported\n"); return ret; } @@ -1191,14 +1190,14 @@ static int imx412_probe(struct i2c_client *client) ret = imx412_power_on(imx412->dev); if (ret) { - dev_err(imx412->dev, "failed to power-on the sensor"); + dev_err(imx412->dev, "failed to power-on the sensor\n"); goto error_mutex_destroy; } /* Check module identity */ ret = imx412_detect(imx412); if (ret) { - dev_err(imx412->dev, "failed to find sensor: %d", ret); + dev_err(imx412->dev, "failed to find sensor: %d\n", ret); goto error_power_off; } @@ -1208,7 +1207,7 @@ static int imx412_probe(struct i2c_client *client) ret = imx412_init_controls(imx412); if (ret) { - dev_err(imx412->dev, "failed to init controls: %d", ret); + dev_err(imx412->dev, "failed to init controls: %d\n", ret); goto error_power_off; } @@ -1222,14 +1221,14 @@ static int imx412_probe(struct i2c_client *client) imx412->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&imx412->sd.entity, 1, &imx412->pad); if (ret) { - dev_err(imx412->dev, "failed to init entity pads: %d", ret); + dev_err(imx412->dev, "failed to init entity pads: %d\n", ret); goto error_handler_free; } ret = v4l2_async_register_subdev_sensor(&imx412->sd); if (ret < 0) { dev_err(imx412->dev, - "failed to register async subdev: %d", ret); + "failed to register async subdev: %d\n", ret); goto error_media_entity; } diff --git a/drivers/media/i2c/imx415.c b/drivers/media/i2c/imx415.c index 3f7924aa1bd3..0b424c17e880 100644 --- a/drivers/media/i2c/imx415.c +++ b/drivers/media/i2c/imx415.c @@ -26,6 +26,10 @@ #define IMX415_PIXEL_ARRAY_WIDTH 3864 #define IMX415_PIXEL_ARRAY_HEIGHT 2192 #define IMX415_PIXEL_ARRAY_VBLANK 58 +#define IMX415_EXPOSURE_OFFSET 8 + +#define IMX415_PIXEL_RATE_74_25MHZ 891000000 +#define IMX415_PIXEL_RATE_72MHZ 864000000 #define IMX415_NUM_CLK_PARAM_REGS 11 @@ -51,7 +55,10 @@ #define IMX415_OUTSEL CCI_REG8(0x30c0) #define IMX415_DRV CCI_REG8(0x30c1) #define IMX415_VMAX CCI_REG24_LE(0x3024) +#define IMX415_VMAX_MAX 0xfffff #define IMX415_HMAX CCI_REG16_LE(0x3028) +#define IMX415_HMAX_MAX 0xffff +#define IMX415_HMAX_MULTIPLIER 12 #define IMX415_SHR0 CCI_REG24_LE(0x3050) #define IMX415_GAIN_PCG_0 CCI_REG16_LE(0x3090) #define IMX415_AGAIN_MIN 0 @@ -445,11 +452,8 @@ static const struct imx415_clk_params imx415_clk_params[] = { }, }; -/* all-pixel 2-lane 720 Mbps 15.74 Hz mode */ -static const struct cci_reg_sequence imx415_mode_2_720[] = { - { IMX415_VMAX, 0x08CA }, - { IMX415_HMAX, 0x07F0 }, - { IMX415_LANEMODE, IMX415_LANEMODE_2 }, +/* 720 Mbps CSI configuration */ +static const struct cci_reg_sequence imx415_linkrate_720mbps[] = { { IMX415_TCLKPOST, 0x006F }, { IMX415_TCLKPREPARE, 0x002F }, { IMX415_TCLKTRAIL, 0x002F }, @@ -461,11 +465,8 @@ static const struct cci_reg_sequence imx415_mode_2_720[] = { { IMX415_TLPX, 0x0027 }, }; -/* all-pixel 2-lane 1440 Mbps 30.01 Hz mode */ -static const struct cci_reg_sequence imx415_mode_2_1440[] = { - { IMX415_VMAX, 0x08CA }, - { IMX415_HMAX, 0x042A }, - { IMX415_LANEMODE, IMX415_LANEMODE_2 }, +/* 1440 Mbps CSI configuration */ +static const struct cci_reg_sequence imx415_linkrate_1440mbps[] = { { IMX415_TCLKPOST, 0x009F }, { IMX415_TCLKPREPARE, 0x0057 }, { IMX415_TCLKTRAIL, 0x0057 }, @@ -477,11 +478,8 @@ static const struct cci_reg_sequence imx415_mode_2_1440[] = { { IMX415_TLPX, 0x004F }, }; -/* all-pixel 4-lane 891 Mbps 30 Hz mode */ -static const struct cci_reg_sequence imx415_mode_4_891[] = { - { IMX415_VMAX, 0x08CA }, - { IMX415_HMAX, 0x044C }, - { IMX415_LANEMODE, IMX415_LANEMODE_4 }, +/* 891 Mbps CSI configuration */ +static const struct cci_reg_sequence imx415_linkrate_891mbps[] = { { IMX415_TCLKPOST, 0x007F }, { IMX415_TCLKPREPARE, 0x0037 }, { IMX415_TCLKTRAIL, 0x0037 }, @@ -498,39 +496,9 @@ struct imx415_mode_reg_list { const struct cci_reg_sequence *regs; }; -/* - * Mode : number of lanes, lane rate and frame rate dependent settings - * - * pixel_rate and hmax_pix are needed to calculate hblank for the v4l2 ctrl - * interface. These values can not be found in the data sheet and should be - * treated as virtual values. Use following table when adding new modes. - * - * lane_rate lanes fps hmax_pix pixel_rate - * - * 594 2 10.000 4400 99000000 - * 891 2 15.000 4400 148500000 - * 720 2 15.748 4064 144000000 - * 1782 2 30.000 4400 297000000 - * 2079 2 30.000 4400 297000000 - * 1440 2 30.019 4510 304615385 - * - * 594 4 20.000 5500 247500000 - * 594 4 25.000 4400 247500000 - * 720 4 25.000 4400 247500000 - * 720 4 30.019 4510 304615385 - * 891 4 30.000 4400 297000000 - * 1440 4 30.019 4510 304615385 - * 1440 4 60.038 4510 609230769 - * 1485 4 60.000 4400 594000000 - * 1782 4 60.000 4400 594000000 - * 2079 4 60.000 4400 594000000 - * 2376 4 90.164 4392 891000000 - */ struct imx415_mode { u64 lane_rate; - u32 lanes; - u32 hmax_pix; - u64 pixel_rate; + u32 hmax_min[2]; struct imx415_mode_reg_list reg_list; }; @@ -538,32 +506,26 @@ struct imx415_mode { static const struct imx415_mode supported_modes[] = { { .lane_rate = 720000000, - .lanes = 2, - .hmax_pix = 4064, - .pixel_rate = 144000000, + .hmax_min = { 2032, 1066 }, .reg_list = { - .num_of_regs = ARRAY_SIZE(imx415_mode_2_720), - .regs = imx415_mode_2_720, + .num_of_regs = ARRAY_SIZE(imx415_linkrate_720mbps), + .regs = imx415_linkrate_720mbps, }, }, { .lane_rate = 1440000000, - .lanes = 2, - .hmax_pix = 4510, - .pixel_rate = 304615385, + .hmax_min = { 1066, 533 }, .reg_list = { - .num_of_regs = ARRAY_SIZE(imx415_mode_2_1440), - .regs = imx415_mode_2_1440, + .num_of_regs = ARRAY_SIZE(imx415_linkrate_1440mbps), + .regs = imx415_linkrate_1440mbps, }, }, { .lane_rate = 891000000, - .lanes = 4, - .hmax_pix = 4400, - .pixel_rate = 297000000, + .hmax_min = { 2200, 1100 }, .reg_list = { - .num_of_regs = ARRAY_SIZE(imx415_mode_4_891), - .regs = imx415_mode_4_891, + .num_of_regs = ARRAY_SIZE(imx415_linkrate_891mbps), + .regs = imx415_linkrate_891mbps, }, }, }; @@ -587,6 +549,7 @@ static const char *const imx415_test_pattern_menu[] = { struct imx415 { struct device *dev; struct clk *clk; + unsigned long pixel_rate; struct regulator_bulk_data supplies[ARRAY_SIZE(imx415_supply_names)]; struct gpio_desc *reset; struct regmap *regmap; @@ -598,8 +561,10 @@ struct imx415 { struct v4l2_ctrl_handler ctrls; struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; struct v4l2_ctrl *hflip; struct v4l2_ctrl *vflip; + struct v4l2_ctrl *exposure; unsigned int cur_mode; unsigned int num_data_lanes; @@ -730,17 +695,38 @@ static int imx415_s_ctrl(struct v4l2_ctrl *ctrl) ctrls); const struct v4l2_mbus_framefmt *format; struct v4l2_subdev_state *state; + u32 exposure_max; unsigned int vmax; unsigned int flip; int ret; - if (!pm_runtime_get_if_in_use(sensor->dev)) - return 0; - state = v4l2_subdev_get_locked_active_state(&sensor->subdev); format = v4l2_subdev_state_get_format(state, 0); + if (ctrl->id == V4L2_CID_VBLANK) { + exposure_max = format->height + ctrl->val - + IMX415_EXPOSURE_OFFSET; + __v4l2_ctrl_modify_range(sensor->exposure, + sensor->exposure->minimum, + exposure_max, sensor->exposure->step, + sensor->exposure->default_value); + } + + if (!pm_runtime_get_if_in_use(sensor->dev)) + return 0; + switch (ctrl->id) { + case V4L2_CID_VBLANK: + ret = cci_write(sensor->regmap, IMX415_VMAX, + format->height + ctrl->val, NULL); + if (ret) + return ret; + /* + * Exposure is set based on VMAX which has just changed, so + * program exposure register as well + */ + ctrl = sensor->exposure; + fallthrough; case V4L2_CID_EXPOSURE: /* clamp the exposure value to VMAX. */ vmax = format->height + sensor->vblank->cur.val; @@ -766,6 +752,13 @@ static int imx415_s_ctrl(struct v4l2_ctrl *ctrl) ret = imx415_set_testpattern(sensor, ctrl->val); break; + case V4L2_CID_HBLANK: + ret = cci_write(sensor->regmap, IMX415_HMAX, + (format->width + ctrl->val) / + IMX415_HMAX_MULTIPLIER, + NULL); + break; + default: ret = -EINVAL; break; @@ -784,11 +777,12 @@ static int imx415_ctrls_init(struct imx415 *sensor) { struct v4l2_fwnode_device_properties props; struct v4l2_ctrl *ctrl; - u64 pixel_rate = supported_modes[sensor->cur_mode].pixel_rate; - u64 lane_rate = supported_modes[sensor->cur_mode].lane_rate; + const struct imx415_mode *cur_mode = &supported_modes[sensor->cur_mode]; + u64 lane_rate = cur_mode->lane_rate; u32 exposure_max = IMX415_PIXEL_ARRAY_HEIGHT + - IMX415_PIXEL_ARRAY_VBLANK - 8; - u32 hblank; + IMX415_PIXEL_ARRAY_VBLANK - + IMX415_EXPOSURE_OFFSET; + u32 hblank_min, hblank_max; unsigned int i; int ret; @@ -816,36 +810,33 @@ static int imx415_ctrls_init(struct imx415 *sensor) if (ctrl) ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; - v4l2_ctrl_new_std(&sensor->ctrls, &imx415_ctrl_ops, V4L2_CID_EXPOSURE, - 4, exposure_max, 1, exposure_max); + sensor->exposure = v4l2_ctrl_new_std(&sensor->ctrls, &imx415_ctrl_ops, + V4L2_CID_EXPOSURE, 4, + exposure_max, 1, exposure_max); v4l2_ctrl_new_std(&sensor->ctrls, &imx415_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, IMX415_AGAIN_MIN, IMX415_AGAIN_MAX, IMX415_AGAIN_STEP, IMX415_AGAIN_MIN); - hblank = supported_modes[sensor->cur_mode].hmax_pix - - IMX415_PIXEL_ARRAY_WIDTH; + hblank_min = (cur_mode->hmax_min[sensor->num_data_lanes == 2 ? 0 : 1] * + IMX415_HMAX_MULTIPLIER) - IMX415_PIXEL_ARRAY_WIDTH; + hblank_max = (IMX415_HMAX_MAX * IMX415_HMAX_MULTIPLIER) - + IMX415_PIXEL_ARRAY_WIDTH; ctrl = v4l2_ctrl_new_std(&sensor->ctrls, &imx415_ctrl_ops, - V4L2_CID_HBLANK, hblank, hblank, 1, hblank); - if (ctrl) - ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + V4L2_CID_HBLANK, hblank_min, + hblank_max, IMX415_HMAX_MULTIPLIER, + hblank_min); sensor->vblank = v4l2_ctrl_new_std(&sensor->ctrls, &imx415_ctrl_ops, V4L2_CID_VBLANK, IMX415_PIXEL_ARRAY_VBLANK, - IMX415_PIXEL_ARRAY_VBLANK, 1, - IMX415_PIXEL_ARRAY_VBLANK); - if (sensor->vblank) - sensor->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + IMX415_VMAX_MAX - IMX415_PIXEL_ARRAY_HEIGHT, + 1, IMX415_PIXEL_ARRAY_VBLANK); - /* - * The pixel rate used here is a virtual value and can be used for - * calculating the frame rate together with hblank. It may not - * necessarily be the physically correct pixel clock. - */ - v4l2_ctrl_new_std(&sensor->ctrls, NULL, V4L2_CID_PIXEL_RATE, pixel_rate, - pixel_rate, 1, pixel_rate); + v4l2_ctrl_new_std(&sensor->ctrls, NULL, V4L2_CID_PIXEL_RATE, + sensor->pixel_rate, sensor->pixel_rate, 1, + sensor->pixel_rate); sensor->hflip = v4l2_ctrl_new_std(&sensor->ctrls, &imx415_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0); @@ -890,7 +881,12 @@ static int imx415_set_mode(struct imx415 *sensor, int mode) IMX415_NUM_CLK_PARAM_REGS, &ret); - return 0; + ret = cci_write(sensor->regmap, IMX415_LANEMODE, + sensor->num_data_lanes == 2 ? IMX415_LANEMODE_2 : + IMX415_LANEMODE_4, + NULL); + + return ret; } static int imx415_setup(struct imx415 *sensor, struct v4l2_subdev_state *state) @@ -956,7 +952,6 @@ static int imx415_s_stream(struct v4l2_subdev *sd, int enable) if (!enable) { ret = imx415_stream_off(sensor); - pm_runtime_mark_last_busy(sensor->dev); pm_runtime_put_autosuspend(sensor->dev); goto unlock; @@ -1255,7 +1250,7 @@ static int imx415_parse_hw_config(struct imx415 *sensor) return dev_err_probe(sensor->dev, PTR_ERR(sensor->reset), "failed to get reset GPIO\n"); - sensor->clk = devm_clk_get(sensor->dev, "inck"); + sensor->clk = devm_v4l2_sensor_clk_get(sensor->dev, NULL); if (IS_ERR(sensor->clk)) return dev_err_probe(sensor->dev, PTR_ERR(sensor->clk), "failed to get clock\n"); @@ -1301,8 +1296,6 @@ static int imx415_parse_hw_config(struct imx415 *sensor) } for (j = 0; j < ARRAY_SIZE(supported_modes); ++j) { - if (sensor->num_data_lanes != supported_modes[j].lanes) - continue; if (bus_cfg.link_frequencies[i] * 2 != supported_modes[j].lane_rate) continue; @@ -1317,6 +1310,17 @@ static int imx415_parse_hw_config(struct imx415 *sensor) "no valid sensor mode defined\n"); goto done_endpoint_free; } + switch (inck) { + case 27000000: + case 37125000: + case 74250000: + sensor->pixel_rate = IMX415_PIXEL_RATE_74_25MHZ; + break; + case 24000000: + case 72000000: + sensor->pixel_rate = IMX415_PIXEL_RATE_72MHZ; + break; + } lane_rate = supported_modes[sensor->cur_mode].lane_rate; for (i = 0; i < ARRAY_SIZE(imx415_clk_params); ++i) { diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c index c84e1e0e6109..5588cdd7ec20 100644 --- a/drivers/media/i2c/ir-kbd-i2c.c +++ b/drivers/media/i2c/ir-kbd-i2c.c @@ -321,9 +321,9 @@ static int get_key_avermedia_cardbus(struct IR_i2c *ir, enum rc_proto *protocol, static int ir_key_poll(struct IR_i2c *ir) { - enum rc_proto protocol; - u32 scancode; - u8 toggle; + enum rc_proto protocol = 0; + u32 scancode = 0; + u8 toggle = 0; int rc; dev_dbg(&ir->rc->dev, "%s\n", __func__); diff --git a/drivers/media/i2c/lt6911uxe.c b/drivers/media/i2c/lt6911uxe.c new file mode 100644 index 000000000000..bdefdd157e69 --- /dev/null +++ b/drivers/media/i2c/lt6911uxe.c @@ -0,0 +1,707 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2023 - 2025 Intel Corporation. + +#include <linux/acpi.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/v4l2-dv-timings.h> + +#include <media/v4l2-cci.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-dv-timings.h> +#include <media/v4l2-event.h> +#include <media/v4l2-fwnode.h> + +#define LT6911UXE_CHIP_ID 0x2102 +#define REG_CHIP_ID CCI_REG16(0xe100) + +#define REG_ENABLE_I2C CCI_REG8(0xe0ee) +#define REG_HALF_PIX_CLK CCI_REG24(0xe085) +#define REG_BYTE_CLK CCI_REG24(0xe092) +#define REG_HALF_H_TOTAL CCI_REG16(0xe088) +#define REG_V_TOTAL CCI_REG16(0xe08a) +#define REG_HALF_H_ACTIVE CCI_REG16(0xe08c) +#define REG_V_ACTIVE CCI_REG16(0xe08e) +#define REG_MIPI_FORMAT CCI_REG8(0xe096) +#define REG_MIPI_TX_CTRL CCI_REG8(0xe0b0) + +/* Interrupts */ +#define REG_INT_HDMI CCI_REG8(0xe084) +#define INT_VIDEO_DISAPPEAR 0x0 +#define INT_VIDEO_READY 0x1 + +#define LT6911UXE_DEFAULT_LANES 4 +#define LT6911_PAGE_CONTROL 0xff +#define YUV422_8_BIT 0x7 + +static const struct v4l2_dv_timings_cap lt6911uxe_timings_cap_4kp30 = { + .type = V4L2_DV_BT_656_1120, + /* keep this initialization for compatibility with CLANG */ + .reserved = { 0 }, + /* Pixel clock from REF_01 p. 20. Min/max height/width are unknown */ + V4L2_INIT_BT_TIMINGS(160, 3840, /* min/max width */ + 120, 2160, /* min/max height */ + 50000000, 594000000, /* min/max pixelclock */ + V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | + V4L2_DV_BT_STD_CVT, + V4L2_DV_BT_CAP_PROGRESSIVE | + V4L2_DV_BT_CAP_CUSTOM | + V4L2_DV_BT_CAP_REDUCED_BLANKING) +}; + +static const struct regmap_range_cfg lt6911uxe_ranges[] = { + { + .name = "register_range", + .range_min = 0, + .range_max = 0xffff, + .selector_reg = LT6911_PAGE_CONTROL, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0, + .window_len = 0x100, + }, +}; + +static const struct regmap_config lt6911uxe_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xffff, + .ranges = lt6911uxe_ranges, + .num_ranges = ARRAY_SIZE(lt6911uxe_ranges), +}; + +struct lt6911uxe_mode { + u32 width; + u32 height; + u32 htotal; + u32 vtotal; + u32 code; + u32 fps; + u32 lanes; + s64 link_freq; + u64 pixel_clk; +}; + +struct lt6911uxe { + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *pixel_rate; + struct v4l2_dv_timings timings; + struct lt6911uxe_mode cur_mode; + struct regmap *regmap; + struct gpio_desc *reset_gpio; + struct gpio_desc *irq_gpio; +}; + +static const struct v4l2_event lt6911uxe_ev_source_change = { + .type = V4L2_EVENT_SOURCE_CHANGE, + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, +}; + +static inline struct lt6911uxe *to_lt6911uxe(struct v4l2_subdev *sd) +{ + return container_of(sd, struct lt6911uxe, sd); +} + +static s64 get_pixel_rate(struct lt6911uxe *lt6911uxe) +{ + s64 pixel_rate; + + pixel_rate = (s64)lt6911uxe->cur_mode.width * + lt6911uxe->cur_mode.height * + lt6911uxe->cur_mode.fps * 16; + do_div(pixel_rate, lt6911uxe->cur_mode.lanes); + + return pixel_rate; +} + +static int lt6911uxe_get_detected_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) +{ + struct lt6911uxe *lt6911uxe = to_lt6911uxe(sd); + struct v4l2_bt_timings *bt = &timings->bt; + + memset(timings, 0, sizeof(struct v4l2_dv_timings)); + + timings->type = V4L2_DV_BT_656_1120; + + bt->width = lt6911uxe->cur_mode.width; + bt->height = lt6911uxe->cur_mode.height; + bt->vsync = lt6911uxe->cur_mode.vtotal - lt6911uxe->cur_mode.height; + bt->hsync = lt6911uxe->cur_mode.htotal - lt6911uxe->cur_mode.width; + bt->pixelclock = lt6911uxe->cur_mode.pixel_clk; + + return 0; +} + +static int lt6911uxe_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_dv_timings *timings) +{ + struct lt6911uxe *lt6911uxe = to_lt6911uxe(sd); + struct v4l2_subdev_state *state; + + state = v4l2_subdev_lock_and_get_active_state(sd); + if (v4l2_match_dv_timings(<6911uxe->timings, timings, 0, false)) { + v4l2_subdev_unlock_state(state); + return 0; + } + + if (!v4l2_valid_dv_timings(timings, <6911uxe_timings_cap_4kp30, + NULL, NULL)) { + v4l2_subdev_unlock_state(state); + return -ERANGE; + } + lt6911uxe->timings = *timings; + v4l2_subdev_unlock_state(state); + + return 0; +} + +static int lt6911uxe_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_dv_timings *timings) +{ + struct lt6911uxe *lt6911uxe = to_lt6911uxe(sd); + struct v4l2_subdev_state *state; + + state = v4l2_subdev_lock_and_get_active_state(sd); + + *timings = lt6911uxe->timings; + v4l2_subdev_unlock_state(state); + + return 0; +} + +static int lt6911uxe_query_dv_timings(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_dv_timings *timings) +{ + struct v4l2_subdev_state *state; + int ret; + + state = v4l2_subdev_lock_and_get_active_state(sd); + ret = lt6911uxe_get_detected_timings(sd, timings); + if (ret) { + v4l2_subdev_unlock_state(state); + return ret; + } + + if (!v4l2_valid_dv_timings(timings, <6911uxe_timings_cap_4kp30, + NULL, NULL)) { + v4l2_subdev_unlock_state(state); + return -ERANGE; + } + + v4l2_subdev_unlock_state(state); + return 0; +} + +static int lt6911uxe_enum_dv_timings(struct v4l2_subdev *sd, + struct v4l2_enum_dv_timings *timings) +{ + return v4l2_enum_dv_timings_cap(timings, + <6911uxe_timings_cap_4kp30, NULL, NULL); +} + +static int lt6911uxe_dv_timings_cap(struct v4l2_subdev *sd, + struct v4l2_dv_timings_cap *cap) +{ + *cap = lt6911uxe_timings_cap_4kp30; + return 0; +} + +static int lt6911uxe_status_update(struct lt6911uxe *lt6911uxe) +{ + struct i2c_client *client = v4l2_get_subdevdata(<6911uxe->sd); + u64 int_event; + u64 byte_clk, half_pix_clk, fps, format; + u64 half_htotal, vtotal, half_width, height; + int ret = 0; + + /* Read interrupt event */ + cci_read(lt6911uxe->regmap, REG_INT_HDMI, &int_event, &ret); + if (ret) { + dev_err(&client->dev, "failed to read interrupt event: %d\n", + ret); + return ret; + } + + switch (int_event) { + case INT_VIDEO_READY: + cci_read(lt6911uxe->regmap, REG_BYTE_CLK, &byte_clk, &ret); + byte_clk *= 1000; + cci_read(lt6911uxe->regmap, REG_HALF_PIX_CLK, + &half_pix_clk, &ret); + half_pix_clk *= 1000; + + if (ret || byte_clk == 0 || half_pix_clk == 0) { + dev_dbg(&client->dev, + "invalid ByteClock or PixelClock\n"); + return -EINVAL; + } + + cci_read(lt6911uxe->regmap, REG_HALF_H_TOTAL, + &half_htotal, &ret); + cci_read(lt6911uxe->regmap, REG_V_TOTAL, &vtotal, &ret); + if (ret || half_htotal == 0 || vtotal == 0) { + dev_dbg(&client->dev, "invalid htotal or vtotal\n"); + return -EINVAL; + } + + fps = div_u64(half_pix_clk, half_htotal * vtotal); + if (fps > 60) { + dev_dbg(&client->dev, + "max fps is 60, current fps: %llu\n", fps); + return -EINVAL; + } + + cci_read(lt6911uxe->regmap, REG_HALF_H_ACTIVE, + &half_width, &ret); + cci_read(lt6911uxe->regmap, REG_V_ACTIVE, &height, &ret); + if (ret || half_width == 0 || half_width * 2 > 3840 || + height == 0 || height > 2160) { + dev_dbg(&client->dev, "invalid width or height\n"); + return -EINVAL; + } + + /* + * Get MIPI format, YUV422_8_BIT is expected in lt6911uxe + */ + cci_read(lt6911uxe->regmap, REG_MIPI_FORMAT, &format, &ret); + if (format != YUV422_8_BIT) { + dev_dbg(&client->dev, "invalid MIPI format\n"); + return -EINVAL; + } + + lt6911uxe->cur_mode.height = height; + lt6911uxe->cur_mode.width = half_width * 2; + lt6911uxe->cur_mode.fps = fps; + /* MIPI Clock Rate = ByteClock × 4, defined in lt6911uxe spec */ + lt6911uxe->cur_mode.link_freq = byte_clk * 4; + lt6911uxe->cur_mode.pixel_clk = half_pix_clk * 2; + lt6911uxe->cur_mode.vtotal = vtotal; + lt6911uxe->cur_mode.htotal = half_htotal * 2; + break; + + case INT_VIDEO_DISAPPEAR: + cci_write(lt6911uxe->regmap, REG_MIPI_TX_CTRL, 0x0, &ret); + lt6911uxe->cur_mode.height = 0; + lt6911uxe->cur_mode.width = 0; + lt6911uxe->cur_mode.fps = 0; + lt6911uxe->cur_mode.link_freq = 0; + break; + + default: + ret = -ENOLINK; + } + v4l2_subdev_notify_event(<6911uxe->sd, <6911uxe_ev_source_change); + return ret; +} + +static int lt6911uxe_init_controls(struct lt6911uxe *lt6911uxe) +{ + struct v4l2_ctrl_handler *ctrl_hdlr; + s64 pixel_rate; + int ret; + + ctrl_hdlr = <6911uxe->ctrl_handler; + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8); + if (ret) + return ret; + + pixel_rate = get_pixel_rate(lt6911uxe); + lt6911uxe->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, NULL, + V4L2_CID_PIXEL_RATE, + pixel_rate, pixel_rate, 1, + pixel_rate); + + if (ctrl_hdlr->error) { + ret = ctrl_hdlr->error; + goto hdlr_free; + } + lt6911uxe->sd.ctrl_handler = ctrl_hdlr; + + return 0; + +hdlr_free: + v4l2_ctrl_handler_free(ctrl_hdlr); + return ret; +} + +static void lt6911uxe_update_pad_format(const struct lt6911uxe_mode *mode, + struct v4l2_mbus_framefmt *fmt) +{ + fmt->width = mode->width; + fmt->height = mode->height; + fmt->code = mode->code; + fmt->field = V4L2_FIELD_NONE; +} + +static int lt6911uxe_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct lt6911uxe *lt6911uxe = to_lt6911uxe(sd); + int ret; + + ret = pm_runtime_resume_and_get(&client->dev); + if (ret < 0) + return ret; + + cci_write(lt6911uxe->regmap, REG_MIPI_TX_CTRL, 0x1, &ret); + if (ret) { + dev_err(&client->dev, "failed to start stream: %d\n", ret); + goto err_rpm_put; + } + + return 0; + +err_rpm_put: + pm_runtime_put(&client->dev); + return ret; +} + +static int lt6911uxe_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct lt6911uxe *lt6911uxe = to_lt6911uxe(sd); + struct i2c_client *client = v4l2_get_subdevdata(<6911uxe->sd); + int ret; + + ret = cci_write(lt6911uxe->regmap, REG_MIPI_TX_CTRL, 0x0, NULL); + if (ret) + dev_err(&client->dev, "failed to stop stream: %d\n", ret); + + pm_runtime_put(&client->dev); + return 0; +} + +static int lt6911uxe_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct lt6911uxe *lt6911uxe = to_lt6911uxe(sd); + u64 pixel_rate; + + lt6911uxe_update_pad_format(<6911uxe->cur_mode, &fmt->format); + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + pixel_rate = get_pixel_rate(lt6911uxe); + __v4l2_ctrl_modify_range(lt6911uxe->pixel_rate, pixel_rate, + pixel_rate, 1, pixel_rate); + + return 0; +} + +static int lt6911uxe_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct lt6911uxe *lt6911uxe = to_lt6911uxe(sd); + + if (code->index) + return -EINVAL; + + code->code = lt6911uxe->cur_mode.code; + + return 0; +} + +static int lt6911uxe_get_mbus_config(struct v4l2_subdev *sd, + unsigned int pad, + struct v4l2_mbus_config *cfg) +{ + struct lt6911uxe *lt6911uxe = to_lt6911uxe(sd); + struct v4l2_subdev_state *state; + + state = v4l2_subdev_lock_and_get_active_state(sd); + cfg->type = V4L2_MBUS_CSI2_DPHY; + cfg->link_freq = lt6911uxe->cur_mode.link_freq; + v4l2_subdev_unlock_state(state); + + return 0; +} + +static int lt6911uxe_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + struct v4l2_subdev_format fmt = { + .which = sd_state ? V4L2_SUBDEV_FORMAT_TRY + : V4L2_SUBDEV_FORMAT_ACTIVE, + }; + + return lt6911uxe_set_format(sd, sd_state, &fmt); +} + +static const struct v4l2_subdev_video_ops lt6911uxe_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +/* + * lt6911uxe provides editable EDID for customers, but only can be edited like + * updating flash. Due to this limitation, it is not possible to implement + * EDID support. + */ +static const struct v4l2_subdev_pad_ops lt6911uxe_pad_ops = { + .set_fmt = lt6911uxe_set_format, + .get_fmt = v4l2_subdev_get_fmt, + .enable_streams = lt6911uxe_enable_streams, + .disable_streams = lt6911uxe_disable_streams, + .enum_mbus_code = lt6911uxe_enum_mbus_code, + .get_frame_interval = v4l2_subdev_get_frame_interval, + .s_dv_timings = lt6911uxe_s_dv_timings, + .g_dv_timings = lt6911uxe_g_dv_timings, + .query_dv_timings = lt6911uxe_query_dv_timings, + .enum_dv_timings = lt6911uxe_enum_dv_timings, + .dv_timings_cap = lt6911uxe_dv_timings_cap, + .get_mbus_config = lt6911uxe_get_mbus_config, +}; + +static const struct v4l2_subdev_core_ops lt6911uxe_subdev_core_ops = { + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_ops lt6911uxe_subdev_ops = { + .core = <6911uxe_subdev_core_ops, + .video = <6911uxe_video_ops, + .pad = <6911uxe_pad_ops, +}; + +static const struct media_entity_operations lt6911uxe_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_internal_ops lt6911uxe_internal_ops = { + .init_state = lt6911uxe_init_state, +}; + +static int lt6911uxe_fwnode_parse(struct lt6911uxe *lt6911uxe, + struct device *dev) +{ + struct fwnode_handle *endpoint; + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; + int ret; + + endpoint = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); + if (!endpoint) + return dev_err_probe(dev, -EPROBE_DEFER, + "endpoint node not found\n"); + + ret = v4l2_fwnode_endpoint_parse(endpoint, &bus_cfg); + fwnode_handle_put(endpoint); + if (ret) { + dev_err(dev, "failed to parse endpoint node: %d\n", ret); + goto out_err; + } + + /* + * Check the number of MIPI CSI2 data lanes, + * lt6911uxe only support 4 lanes. + */ + if (bus_cfg.bus.mipi_csi2.num_data_lanes != LT6911UXE_DEFAULT_LANES) { + dev_err(dev, "only 4 data lanes are currently supported\n"); + ret = -EINVAL; + goto out_err; + } + lt6911uxe->cur_mode.lanes = bus_cfg.bus.mipi_csi2.num_data_lanes; + lt6911uxe->cur_mode.code = MEDIA_BUS_FMT_UYVY8_1X16; + + return 0; + +out_err: + v4l2_fwnode_endpoint_free(&bus_cfg); + return ret; +} + +static int lt6911uxe_identify_module(struct lt6911uxe *lt6911uxe, + struct device *dev) +{ + u64 val; + int ret = 0; + + /* Chip ID should be confirmed when the I2C slave is active */ + cci_write(lt6911uxe->regmap, REG_ENABLE_I2C, 0x1, &ret); + cci_read(lt6911uxe->regmap, REG_CHIP_ID, &val, &ret); + cci_write(lt6911uxe->regmap, REG_ENABLE_I2C, 0x0, &ret); + if (ret) + return dev_err_probe(dev, ret, "fail to read chip id\n"); + + if (val != LT6911UXE_CHIP_ID) { + return dev_err_probe(dev, -ENXIO, "chip id mismatch: %x!=%x\n", + LT6911UXE_CHIP_ID, (u16)val); + } + + return 0; +} + +static irqreturn_t lt6911uxe_threaded_irq_fn(int irq, void *dev_id) +{ + struct v4l2_subdev *sd = dev_id; + struct lt6911uxe *lt6911uxe = to_lt6911uxe(sd); + struct v4l2_subdev_state *state; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE + }; + + lt6911uxe_status_update(lt6911uxe); + state = v4l2_subdev_lock_and_get_active_state(sd); + /* + * As a HDMI to CSI2 bridge, it needs to update the format in time + * when the HDMI source changes. + */ + lt6911uxe_set_format(sd, state, &fmt); + v4l2_subdev_unlock_state(state); + + return IRQ_HANDLED; +} + +static void lt6911uxe_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct lt6911uxe *lt6911uxe = to_lt6911uxe(sd); + + free_irq(gpiod_to_irq(lt6911uxe->irq_gpio), lt6911uxe); + v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(<6911uxe->ctrl_handler); + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); +} + +static int lt6911uxe_probe(struct i2c_client *client) +{ + struct lt6911uxe *lt6911uxe; + struct device *dev = &client->dev; + int ret; + + lt6911uxe = devm_kzalloc(dev, sizeof(*lt6911uxe), GFP_KERNEL); + if (!lt6911uxe) + return -ENOMEM; + + lt6911uxe->regmap = devm_regmap_init_i2c(client, + <6911uxe_regmap_config); + if (IS_ERR(lt6911uxe->regmap)) + return dev_err_probe(dev, PTR_ERR(lt6911uxe->regmap), + "failed to init CCI\n"); + + v4l2_i2c_subdev_init(<6911uxe->sd, client, <6911uxe_subdev_ops); + + lt6911uxe->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(lt6911uxe->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(lt6911uxe->reset_gpio), + "failed to get reset gpio\n"); + + lt6911uxe->irq_gpio = devm_gpiod_get(dev, "hpd", GPIOD_IN); + if (IS_ERR(lt6911uxe->irq_gpio)) + return dev_err_probe(dev, PTR_ERR(lt6911uxe->irq_gpio), + "failed to get hpd gpio\n"); + + ret = lt6911uxe_fwnode_parse(lt6911uxe, dev); + if (ret) + return ret; + + usleep_range(10000, 10500); + + ret = lt6911uxe_identify_module(lt6911uxe, dev); + if (ret) + return dev_err_probe(dev, ret, "failed to find chip\n"); + + ret = lt6911uxe_init_controls(lt6911uxe); + if (ret) + return dev_err_probe(dev, ret, "failed to init control\n"); + + lt6911uxe->sd.dev = dev; + lt6911uxe->sd.internal_ops = <6911uxe_internal_ops; + lt6911uxe->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + lt6911uxe->sd.entity.ops = <6911uxe_subdev_entity_ops; + lt6911uxe->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + lt6911uxe->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(<6911uxe->sd.entity, 1, <6911uxe->pad); + if (ret) { + dev_err(dev, "failed to init entity pads: %d\n", ret); + goto v4l2_ctrl_handler_free; + } + + /* + * Device is already turned on by i2c-core with ACPI domain PM. + * Enable runtime PM and turn off the device. + */ + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + ret = v4l2_subdev_init_finalize(<6911uxe->sd); + if (ret) { + dev_err(dev, "failed to init v4l2 subdev: %d\n", ret); + goto media_entity_cleanup; + } + + /* Setting irq */ + ret = request_threaded_irq(gpiod_to_irq(lt6911uxe->irq_gpio), NULL, + lt6911uxe_threaded_irq_fn, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, NULL, lt6911uxe); + if (ret) { + dev_err(dev, "failed to request IRQ: %d\n", ret); + goto subdev_cleanup; + } + + ret = v4l2_async_register_subdev_sensor(<6911uxe->sd); + if (ret) { + dev_err(dev, "failed to register V4L2 subdev: %d\n", ret); + goto free_irq; + } + + return 0; + +free_irq: + free_irq(gpiod_to_irq(lt6911uxe->irq_gpio), lt6911uxe); + +subdev_cleanup: + v4l2_subdev_cleanup(<6911uxe->sd); + +media_entity_cleanup: + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + media_entity_cleanup(<6911uxe->sd.entity); + +v4l2_ctrl_handler_free: + v4l2_ctrl_handler_free(lt6911uxe->sd.ctrl_handler); + + return ret; +} + +static const struct acpi_device_id lt6911uxe_acpi_ids[] = { + { "INTC10C5" }, + {} +}; +MODULE_DEVICE_TABLE(acpi, lt6911uxe_acpi_ids); + +static struct i2c_driver lt6911uxe_i2c_driver = { + .driver = { + .name = "lt6911uxe", + .acpi_match_table = ACPI_PTR(lt6911uxe_acpi_ids), + }, + .probe = lt6911uxe_probe, + .remove = lt6911uxe_remove, +}; + +module_i2c_driver(lt6911uxe_i2c_driver); + +MODULE_AUTHOR("Yan Dongcheng <dongcheng.yan@intel.com>"); +MODULE_DESCRIPTION("Lontium lt6911uxe HDMI to MIPI Bridge Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c index 9fc4e130a273..e6e214f8294b 100644 --- a/drivers/media/i2c/max9286.c +++ b/drivers/media/i2c/max9286.c @@ -751,8 +751,8 @@ static int max9286_v4l2_notifier_register(struct max9286_priv *priv) mas = v4l2_async_nf_add_fwnode(&priv->notifier, source->fwnode, struct max9286_asd); if (IS_ERR(mas)) { - dev_err(dev, "Failed to add subdev for source %u: %ld", - i, PTR_ERR(mas)); + dev_err(dev, "Failed to add subdev for source %u: %pe", + i, mas); v4l2_async_nf_cleanup(&priv->notifier); return PTR_ERR(mas); } @@ -1193,12 +1193,12 @@ static int max9286_gpio_set(struct max9286_priv *priv, unsigned int offset, MAX9286_0X0F_RESERVED | priv->gpio_state); } -static void max9286_gpiochip_set(struct gpio_chip *chip, - unsigned int offset, int value) +static int max9286_gpiochip_set(struct gpio_chip *chip, + unsigned int offset, int value) { struct max9286_priv *priv = gpiochip_get_data(chip); - max9286_gpio_set(priv, offset, value); + return max9286_gpio_set(priv, offset, value); } static int max9286_gpiochip_get(struct gpio_chip *chip, unsigned int offset) diff --git a/drivers/media/i2c/max96714.c b/drivers/media/i2c/max96714.c index 159753b13777..e3e625e6f11a 100644 --- a/drivers/media/i2c/max96714.c +++ b/drivers/media/i2c/max96714.c @@ -7,11 +7,11 @@ #include <linux/bitfield.h> #include <linux/bitops.h> -#include <linux/fwnode.h> #include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/i2c-mux.h> #include <linux/module.h> +#include <linux/property.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> @@ -370,13 +370,6 @@ static int _max96714_set_routing(struct v4l2_subdev *sd, }; int ret; - /* - * Note: we can only support up to V4L2_FRAME_DESC_ENTRY_MAX, until - * frame desc is made dynamically allocated. - */ - if (routing->num_routes > V4L2_FRAME_DESC_ENTRY_MAX) - return -EINVAL; - ret = v4l2_subdev_routing_validate(sd, routing, V4L2_SUBDEV_ROUTING_ONLY_1_TO_1); if (ret) diff --git a/drivers/media/i2c/max96717.c b/drivers/media/i2c/max96717.c index 9259d58ba734..72f021b1a7b9 100644 --- a/drivers/media/i2c/max96717.c +++ b/drivers/media/i2c/max96717.c @@ -9,10 +9,10 @@ #include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/delay.h> -#include <linux/fwnode.h> #include <linux/gpio/driver.h> #include <linux/i2c-mux.h> #include <linux/i2c.h> +#include <linux/property.h> #include <linux/regmap.h> #include <media/v4l2-cci.h> @@ -297,13 +297,13 @@ static int max96717_gpiochip_get(struct gpio_chip *gpiochip, return !!(val & MAX96717_GPIO_OUT); } -static void max96717_gpiochip_set(struct gpio_chip *gpiochip, - unsigned int offset, int value) +static int max96717_gpiochip_set(struct gpio_chip *gpiochip, + unsigned int offset, int value) { struct max96717_priv *priv = gpiochip_get_data(gpiochip); - cci_update_bits(priv->regmap, MAX96717_GPIO_REG_A(offset), - MAX96717_GPIO_OUT, MAX96717_GPIO_OUT, NULL); + return cci_update_bits(priv->regmap, MAX96717_GPIO_REG_A(offset), + MAX96717_GPIO_OUT, MAX96717_GPIO_OUT, NULL); } static int max96717_gpio_get_direction(struct gpio_chip *gpiochip, @@ -357,7 +357,6 @@ static int max96717_gpiochip_probe(struct max96717_priv *priv) gc->direction_output = max96717_gpio_direction_out; gc->set = max96717_gpiochip_set; gc->get = max96717_gpiochip_get; - gc->of_gpio_n_cells = 2; /* Disable GPIO forwarding */ for (i = 0; i < gc->ngpio; i++) @@ -651,7 +650,7 @@ static int max96717_v4l2_notifier_register(struct max96717_priv *priv) fwnode_handle_put(ep_fwnode); if (IS_ERR(asd)) { - dev_err(dev, "Failed to add subdev: %ld", PTR_ERR(asd)); + dev_err(dev, "Failed to add subdev: %pe", asd); v4l2_async_nf_cleanup(&priv->notifier); return PTR_ERR(asd); } @@ -783,21 +782,23 @@ static unsigned int max96717_clk_find_best_index(struct max96717_priv *priv, return idx; } -static long max96717_clk_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int max96717_clk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct max96717_priv *priv = clk_hw_to_max96717(hw); struct device *dev = &priv->client->dev; unsigned int idx; - idx = max96717_clk_find_best_index(priv, rate); + idx = max96717_clk_find_best_index(priv, req->rate); - if (rate != max96717_predef_freqs[idx].freq) { + if (req->rate != max96717_predef_freqs[idx].freq) { dev_warn(dev, "Request CLK freq:%lu, found CLK freq:%lu\n", - rate, max96717_predef_freqs[idx].freq); + req->rate, max96717_predef_freqs[idx].freq); } - return max96717_predef_freqs[idx].freq; + req->rate = max96717_predef_freqs[idx].freq; + + return 0; } static int max96717_clk_set_rate(struct clk_hw *hw, unsigned long rate, @@ -848,7 +849,7 @@ static const struct clk_ops max96717_clk_ops = { .unprepare = max96717_clk_unprepare, .set_rate = max96717_clk_set_rate, .recalc_rate = max96717_clk_recalc_rate, - .round_rate = max96717_clk_round_rate, + .determine_rate = max96717_clk_determine_rate, }; static int max96717_register_clkout(struct max96717_priv *priv) diff --git a/drivers/media/i2c/msp3400-kthreads.c b/drivers/media/i2c/msp3400-kthreads.c index ecabc0e1d32e..1d9f41dd7c21 100644 --- a/drivers/media/i2c/msp3400-kthreads.c +++ b/drivers/media/i2c/msp3400-kthreads.c @@ -596,6 +596,8 @@ restart: "carrier2 val: %5d / %s\n", val, cd[i].name); } + if (max1 < 0 || max1 > 3) + goto restart; /* program the msp3400 according to the results */ state->main = msp3400c_carrier_detect_main[max1].cdo; switch (max1) { diff --git a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c index 12d3e86bdc0f..7a6114d18dfc 100644 --- a/drivers/media/i2c/mt9m001.c +++ b/drivers/media/i2c/mt9m001.c @@ -743,9 +743,10 @@ static int mt9m001_probe(struct i2c_client *client) if (!mt9m001) return -ENOMEM; - mt9m001->clk = devm_clk_get(&client->dev, NULL); + mt9m001->clk = devm_v4l2_sensor_clk_get(&client->dev, NULL); if (IS_ERR(mt9m001->clk)) - return PTR_ERR(mt9m001->clk); + return dev_err_probe(&client->dev, PTR_ERR(mt9m001->clk), + "failed to get the clock\n"); mt9m001->standby_gpio = devm_gpiod_get_optional(&client->dev, "standby", GPIOD_OUT_LOW); diff --git a/drivers/media/i2c/mt9m111.c b/drivers/media/i2c/mt9m111.c index 9aa5dcda3805..3532c7c38bec 100644 --- a/drivers/media/i2c/mt9m111.c +++ b/drivers/media/i2c/mt9m111.c @@ -1279,14 +1279,15 @@ static int mt9m111_probe(struct i2c_client *client) return ret; } - mt9m111->clk = devm_clk_get(&client->dev, "mclk"); + mt9m111->clk = devm_v4l2_sensor_clk_get(&client->dev, "mclk"); if (IS_ERR(mt9m111->clk)) - return PTR_ERR(mt9m111->clk); + return dev_err_probe(&client->dev, PTR_ERR(mt9m111->clk), + "failed to get mclk\n"); mt9m111->regulator = devm_regulator_get(&client->dev, "vdd"); if (IS_ERR(mt9m111->regulator)) { - dev_err(&client->dev, "regulator not found: %ld\n", - PTR_ERR(mt9m111->regulator)); + dev_err(&client->dev, "regulator not found: %pe\n", + mt9m111->regulator); return PTR_ERR(mt9m111->regulator); } diff --git a/drivers/media/i2c/mt9m114.c b/drivers/media/i2c/mt9m114.c index 5f0b0ad8f885..51ebbe7ae996 100644 --- a/drivers/media/i2c/mt9m114.c +++ b/drivers/media/i2c/mt9m114.c @@ -18,6 +18,7 @@ #include <linux/module.h> #include <linux/mutex.h> #include <linux/pm_runtime.h> +#include <linux/property.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/types.h> @@ -42,6 +43,9 @@ #define MT9M114_RESET_AND_MISC_CONTROL CCI_REG16(0x001a) #define MT9M114_RESET_SOC BIT(0) #define MT9M114_PAD_SLEW CCI_REG16(0x001e) +#define MT9M114_PAD_SLEW_MIN 0 +#define MT9M114_PAD_SLEW_MAX 7 +#define MT9M114_PAD_SLEW_DEFAULT 7 #define MT9M114_PAD_CONTROL CCI_REG16(0x0032) /* XDMA registers */ @@ -261,6 +265,7 @@ #define MT9M114_CAM_PGA_PGA_CONTROL CCI_REG16(0xc95e) #define MT9M114_CAM_SYSCTL_PLL_ENABLE CCI_REG8(0xc97e) #define MT9M114_CAM_SYSCTL_PLL_ENABLE_VALUE BIT(0) +#define MT9M114_CAM_SYSCTL_PLL_DISABLE_VALUE 0x00 #define MT9M114_CAM_SYSCTL_PLL_DIVIDER_M_N CCI_REG16(0xc980) #define MT9M114_CAM_SYSCTL_PLL_DIVIDER_VALUE(m, n) (((n) << 8) | (m)) #define MT9M114_CAM_SYSCTL_PLL_DIVIDER_P CCI_REG16(0xc982) @@ -377,6 +382,7 @@ struct mt9m114 { struct gpio_desc *reset; struct regulator_bulk_data supplies[3]; struct v4l2_fwnode_endpoint bus_cfg; + bool bypass_pll; struct { unsigned int m; @@ -386,6 +392,7 @@ struct mt9m114 { unsigned int pixrate; bool streaming; + u32 pad_slew_rate; /* Pixel Array */ struct { @@ -643,9 +650,6 @@ static const struct cci_reg_sequence mt9m114_init[] = { { MT9M114_CAM_SENSOR_CFG_FINE_INTEG_TIME_MAX, 1459 }, { MT9M114_CAM_SENSOR_CFG_FINE_CORRECTION, 96 }, { MT9M114_CAM_SENSOR_CFG_REG_0_DATA, 32 }, - - /* Miscellaneous settings */ - { MT9M114_PAD_SLEW, 0x0777 }, }; /* ----------------------------------------------------------------------------- @@ -743,14 +747,21 @@ static int mt9m114_initialize(struct mt9m114 *sensor) } /* Configure the PLL. */ - cci_write(sensor->regmap, MT9M114_CAM_SYSCTL_PLL_ENABLE, - MT9M114_CAM_SYSCTL_PLL_ENABLE_VALUE, &ret); - cci_write(sensor->regmap, MT9M114_CAM_SYSCTL_PLL_DIVIDER_M_N, - MT9M114_CAM_SYSCTL_PLL_DIVIDER_VALUE(sensor->pll.m, - sensor->pll.n), - &ret); - cci_write(sensor->regmap, MT9M114_CAM_SYSCTL_PLL_DIVIDER_P, - MT9M114_CAM_SYSCTL_PLL_DIVIDER_P_VALUE(sensor->pll.p), &ret); + if (sensor->bypass_pll) { + cci_write(sensor->regmap, MT9M114_CAM_SYSCTL_PLL_ENABLE, + MT9M114_CAM_SYSCTL_PLL_DISABLE_VALUE, &ret); + } else { + cci_write(sensor->regmap, MT9M114_CAM_SYSCTL_PLL_ENABLE, + MT9M114_CAM_SYSCTL_PLL_ENABLE_VALUE, &ret); + cci_write(sensor->regmap, MT9M114_CAM_SYSCTL_PLL_DIVIDER_M_N, + MT9M114_CAM_SYSCTL_PLL_DIVIDER_VALUE(sensor->pll.m, + sensor->pll.n), + &ret); + cci_write(sensor->regmap, MT9M114_CAM_SYSCTL_PLL_DIVIDER_P, + MT9M114_CAM_SYSCTL_PLL_DIVIDER_P_VALUE(sensor->pll.p), + &ret); + } + cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CFG_PIXCLK, sensor->pixrate, &ret); @@ -770,6 +781,13 @@ static int mt9m114_initialize(struct mt9m114 *sensor) if (ret < 0) return ret; + value = sensor->pad_slew_rate + | sensor->pad_slew_rate << 4 + | sensor->pad_slew_rate << 8; + cci_write(sensor->regmap, MT9M114_PAD_SLEW, value, &ret); + if (ret < 0) + return ret; + ret = mt9m114_set_state(sensor, MT9M114_SYS_STATE_ENTER_CONFIG_CHANGE); if (ret < 0) return ret; @@ -781,41 +799,25 @@ static int mt9m114_initialize(struct mt9m114 *sensor) return 0; } -static int mt9m114_configure(struct mt9m114 *sensor, - struct v4l2_subdev_state *pa_state, - struct v4l2_subdev_state *ifp_state) +static int mt9m114_configure_pa(struct mt9m114 *sensor, + struct v4l2_subdev_state *state) { - const struct v4l2_mbus_framefmt *pa_format; - const struct v4l2_rect *pa_crop; - const struct mt9m114_format_info *ifp_info; - const struct v4l2_mbus_framefmt *ifp_format; - const struct v4l2_rect *ifp_crop; - const struct v4l2_rect *ifp_compose; + const struct v4l2_mbus_framefmt *format; + const struct v4l2_rect *crop; unsigned int hratio, vratio; - u64 output_format; u64 read_mode; - int ret = 0; - - pa_format = v4l2_subdev_state_get_format(pa_state, 0); - pa_crop = v4l2_subdev_state_get_crop(pa_state, 0); + int ret; - ifp_format = v4l2_subdev_state_get_format(ifp_state, 1); - ifp_info = mt9m114_format_info(sensor, 1, ifp_format->code); - ifp_crop = v4l2_subdev_state_get_crop(ifp_state, 0); - ifp_compose = v4l2_subdev_state_get_compose(ifp_state, 0); + format = v4l2_subdev_state_get_format(state, 0); + crop = v4l2_subdev_state_get_crop(state, 0); ret = cci_read(sensor->regmap, MT9M114_CAM_SENSOR_CONTROL_READ_MODE, &read_mode, NULL); if (ret < 0) return ret; - ret = cci_read(sensor->regmap, MT9M114_CAM_OUTPUT_FORMAT, - &output_format, NULL); - if (ret < 0) - return ret; - - hratio = pa_crop->width / pa_format->width; - vratio = pa_crop->height / pa_format->height; + hratio = crop->width / format->width; + vratio = crop->height / format->height; /* * Pixel array crop and binning. The CAM_SENSOR_CFG_CPIPE_LAST_ROW @@ -824,15 +826,15 @@ static int mt9m114_configure(struct mt9m114 *sensor, * example sensor modes. */ cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CFG_X_ADDR_START, - pa_crop->left, &ret); + crop->left, &ret); cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CFG_Y_ADDR_START, - pa_crop->top, &ret); + crop->top, &ret); cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CFG_X_ADDR_END, - pa_crop->width + pa_crop->left - 1, &ret); + crop->width + crop->left - 1, &ret); cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CFG_Y_ADDR_END, - pa_crop->height + pa_crop->top - 1, &ret); + crop->height + crop->top - 1, &ret); cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CFG_CPIPE_LAST_ROW, - (pa_crop->height - 4) / vratio - 1, &ret); + (crop->height - 4) / vratio - 1, &ret); read_mode &= ~(MT9M114_CAM_SENSOR_CONTROL_X_READ_OUT_MASK | MT9M114_CAM_SENSOR_CONTROL_Y_READ_OUT_MASK); @@ -845,6 +847,29 @@ static int mt9m114_configure(struct mt9m114 *sensor, cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CONTROL_READ_MODE, read_mode, &ret); + return ret; +} + +static int mt9m114_configure_ifp(struct mt9m114 *sensor, + struct v4l2_subdev_state *state) +{ + const struct mt9m114_format_info *info; + const struct v4l2_mbus_framefmt *format; + const struct v4l2_rect *crop; + const struct v4l2_rect *compose; + u64 output_format; + int ret = 0; + + format = v4l2_subdev_state_get_format(state, 1); + info = mt9m114_format_info(sensor, 1, format->code); + crop = v4l2_subdev_state_get_crop(state, 0); + compose = v4l2_subdev_state_get_compose(state, 0); + + ret = cci_read(sensor->regmap, MT9M114_CAM_OUTPUT_FORMAT, + &output_format, NULL); + if (ret < 0) + return ret; + /* * Color pipeline (IFP) cropping and scaling. Subtract 4 from the left * and top coordinates to compensate for the lines and columns removed @@ -852,18 +877,18 @@ static int mt9m114_configure(struct mt9m114 *sensor, * not in the hardware. */ cci_write(sensor->regmap, MT9M114_CAM_CROP_WINDOW_XOFFSET, - ifp_crop->left - 4, &ret); + crop->left - 4, &ret); cci_write(sensor->regmap, MT9M114_CAM_CROP_WINDOW_YOFFSET, - ifp_crop->top - 4, &ret); + crop->top - 4, &ret); cci_write(sensor->regmap, MT9M114_CAM_CROP_WINDOW_WIDTH, - ifp_crop->width, &ret); + crop->width, &ret); cci_write(sensor->regmap, MT9M114_CAM_CROP_WINDOW_HEIGHT, - ifp_crop->height, &ret); + crop->height, &ret); cci_write(sensor->regmap, MT9M114_CAM_OUTPUT_WIDTH, - ifp_compose->width, &ret); + compose->width, &ret); cci_write(sensor->regmap, MT9M114_CAM_OUTPUT_HEIGHT, - ifp_compose->height, &ret); + compose->height, &ret); /* AWB and AE windows, use the full frame. */ cci_write(sensor->regmap, MT9M114_CAM_STAT_AWB_CLIP_WINDOW_XSTART, @@ -871,18 +896,18 @@ static int mt9m114_configure(struct mt9m114 *sensor, cci_write(sensor->regmap, MT9M114_CAM_STAT_AWB_CLIP_WINDOW_YSTART, 0, &ret); cci_write(sensor->regmap, MT9M114_CAM_STAT_AWB_CLIP_WINDOW_XEND, - ifp_compose->width - 1, &ret); + compose->width - 1, &ret); cci_write(sensor->regmap, MT9M114_CAM_STAT_AWB_CLIP_WINDOW_YEND, - ifp_compose->height - 1, &ret); + compose->height - 1, &ret); cci_write(sensor->regmap, MT9M114_CAM_STAT_AE_INITIAL_WINDOW_XSTART, 0, &ret); cci_write(sensor->regmap, MT9M114_CAM_STAT_AE_INITIAL_WINDOW_YSTART, 0, &ret); cci_write(sensor->regmap, MT9M114_CAM_STAT_AE_INITIAL_WINDOW_XEND, - ifp_compose->width / 5 - 1, &ret); + compose->width / 5 - 1, &ret); cci_write(sensor->regmap, MT9M114_CAM_STAT_AE_INITIAL_WINDOW_YEND, - ifp_compose->height / 5 - 1, &ret); + compose->height / 5 - 1, &ret); cci_write(sensor->regmap, MT9M114_CAM_CROP_CROPMODE, MT9M114_CAM_CROP_MODE_AWB_AUTO_CROP_EN | @@ -894,7 +919,7 @@ static int mt9m114_configure(struct mt9m114 *sensor, MT9M114_CAM_OUTPUT_FORMAT_FORMAT_MASK | MT9M114_CAM_OUTPUT_FORMAT_SWAP_BYTES | MT9M114_CAM_OUTPUT_FORMAT_SWAP_RED_BLUE); - output_format |= ifp_info->output_format; + output_format |= info->output_format; cci_write(sensor->regmap, MT9M114_CAM_OUTPUT_FORMAT, output_format, &ret); @@ -925,7 +950,11 @@ static int mt9m114_start_streaming(struct mt9m114 *sensor, if (ret) return ret; - ret = mt9m114_configure(sensor, pa_state, ifp_state); + ret = mt9m114_configure_ifp(sensor, ifp_state); + if (ret) + goto error; + + ret = mt9m114_configure_pa(sensor, pa_state); if (ret) goto error; @@ -954,7 +983,6 @@ static int mt9m114_start_streaming(struct mt9m114 *sensor, return 0; error: - pm_runtime_mark_last_busy(&sensor->client->dev); pm_runtime_put_autosuspend(&sensor->client->dev); return ret; @@ -968,7 +996,6 @@ static int mt9m114_stop_streaming(struct mt9m114 *sensor) ret = mt9m114_set_state(sensor, MT9M114_SYS_STATE_ENTER_SUSPEND); - pm_runtime_mark_last_busy(&sensor->client->dev); pm_runtime_put_autosuspend(&sensor->client->dev); return ret; @@ -1026,7 +1053,6 @@ static int mt9m114_pa_g_ctrl(struct v4l2_ctrl *ctrl) break; } - pm_runtime_mark_last_busy(&sensor->client->dev); pm_runtime_put_autosuspend(&sensor->client->dev); return ret; @@ -1093,7 +1119,6 @@ static int mt9m114_pa_s_ctrl(struct v4l2_ctrl *ctrl) break; } - pm_runtime_mark_last_busy(&sensor->client->dev); pm_runtime_put_autosuspend(&sensor->client->dev); return ret; @@ -1267,6 +1292,7 @@ static int mt9m114_pa_set_selection(struct v4l2_subdev *sd, struct mt9m114 *sensor = pa_to_mt9m114(sd); struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; + int ret = 0; if (sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; @@ -1282,25 +1308,41 @@ static int mt9m114_pa_set_selection(struct v4l2_subdev *sd, * binning, but binning is configured after setting the selection, so * we can't know tell here if it will be used. */ - crop->left = ALIGN(sel->r.left, 4); - crop->top = ALIGN(sel->r.top, 2); - crop->width = clamp_t(unsigned int, ALIGN(sel->r.width, 4), - MT9M114_PIXEL_ARRAY_MIN_OUTPUT_WIDTH, - MT9M114_PIXEL_ARRAY_WIDTH - crop->left); - crop->height = clamp_t(unsigned int, ALIGN(sel->r.height, 2), - MT9M114_PIXEL_ARRAY_MIN_OUTPUT_HEIGHT, - MT9M114_PIXEL_ARRAY_HEIGHT - crop->top); - - sel->r = *crop; + sel->r.left = ALIGN(sel->r.left, 4); + sel->r.top = ALIGN(sel->r.top, 2); + sel->r.width = clamp_t(unsigned int, ALIGN(sel->r.width, 4), + MT9M114_PIXEL_ARRAY_MIN_OUTPUT_WIDTH, + MT9M114_PIXEL_ARRAY_WIDTH - sel->r.left); + sel->r.height = clamp_t(unsigned int, ALIGN(sel->r.height, 2), + MT9M114_PIXEL_ARRAY_MIN_OUTPUT_HEIGHT, + MT9M114_PIXEL_ARRAY_HEIGHT - sel->r.top); + + /* Changing the selection size is not allowed in streaming state. */ + if (sensor->streaming && + (sel->r.height != crop->height || sel->r.width != crop->width)) + return -EBUSY; + + *crop = sel->r; /* Reset the format. */ format->width = crop->width; format->height = crop->height; - if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) - mt9m114_pa_ctrl_update_blanking(sensor, format); + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return ret; - return 0; + mt9m114_pa_ctrl_update_blanking(sensor, format); + + /* Apply values immediately if streaming. */ + if (sensor->streaming) { + ret = mt9m114_configure_pa(sensor, state); + if (ret) + return ret; + /* Changing the cropping config requires a CONFIG_CHANGE. */ + ret = mt9m114_set_state(sensor, + MT9M114_SYS_STATE_ENTER_CONFIG_CHANGE); + } + return ret; } static const struct v4l2_subdev_pad_ops mt9m114_pa_pad_ops = { @@ -1545,7 +1587,6 @@ static int mt9m114_ifp_s_ctrl(struct v4l2_ctrl *ctrl) break; } - pm_runtime_mark_last_busy(&sensor->client->dev); pm_runtime_put_autosuspend(&sensor->client->dev); return ret; @@ -1599,13 +1640,9 @@ static int mt9m114_ifp_get_frame_interval(struct v4l2_subdev *sd, if (interval->which != V4L2_SUBDEV_FORMAT_ACTIVE) return -EINVAL; - mutex_lock(sensor->ifp.hdl.lock); - ival->numerator = 1; ival->denominator = sensor->ifp.frame_rate; - mutex_unlock(sensor->ifp.hdl.lock); - return 0; } @@ -1624,8 +1661,6 @@ static int mt9m114_ifp_set_frame_interval(struct v4l2_subdev *sd, if (interval->which != V4L2_SUBDEV_FORMAT_ACTIVE) return -EINVAL; - mutex_lock(sensor->ifp.hdl.lock); - if (ival->numerator != 0 && ival->denominator != 0) sensor->ifp.frame_rate = min_t(unsigned int, ival->denominator / ival->numerator, @@ -1639,8 +1674,6 @@ static int mt9m114_ifp_set_frame_interval(struct v4l2_subdev *sd, if (sensor->streaming) ret = mt9m114_set_frame_rate(sensor); - mutex_unlock(sensor->ifp.hdl.lock); - return ret; } @@ -2235,9 +2268,22 @@ static const struct dev_pm_ops mt9m114_pm_ops = { * Probe & Remove */ +static int mt9m114_verify_link_frequency(struct mt9m114 *sensor, + unsigned int pixrate) +{ + unsigned int link_freq = sensor->bus_cfg.bus_type == V4L2_MBUS_CSI2_DPHY + ? pixrate * 8 : pixrate * 2; + + if (sensor->bus_cfg.nr_of_link_frequencies != 1 || + sensor->bus_cfg.link_frequencies[0] != link_freq) + return -EINVAL; + + return 0; +} + static int mt9m114_clk_init(struct mt9m114 *sensor) { - unsigned int link_freq; + unsigned int pixrate; /* Hardcode the PLL multiplier and dividers to default settings. */ sensor->pll.m = 32; @@ -2249,19 +2295,29 @@ static int mt9m114_clk_init(struct mt9m114 *sensor) * for 16-bit per pixel, transmitted in DDR over a single lane. For * parallel mode, the sensor ouputs one pixel in two PIXCLK cycles. */ - sensor->pixrate = clk_get_rate(sensor->clk) * sensor->pll.m - / ((sensor->pll.n + 1) * (sensor->pll.p + 1)); - link_freq = sensor->bus_cfg.bus_type == V4L2_MBUS_CSI2_DPHY - ? sensor->pixrate * 8 : sensor->pixrate * 2; + /* + * Check if EXTCLK fits the configured link frequency. Bypass the PLL + * in this case. + */ + pixrate = clk_get_rate(sensor->clk) / 2; + if (mt9m114_verify_link_frequency(sensor, pixrate) == 0) { + sensor->pixrate = pixrate; + sensor->bypass_pll = true; + return 0; + } - if (sensor->bus_cfg.nr_of_link_frequencies != 1 || - sensor->bus_cfg.link_frequencies[0] != link_freq) { - dev_err(&sensor->client->dev, "Unsupported DT link-frequencies\n"); - return -EINVAL; + /* Check if the PLL configuration fits the configured link frequency. */ + pixrate = clk_get_rate(sensor->clk) * sensor->pll.m + / ((sensor->pll.n + 1) * (sensor->pll.p + 1)); + if (mt9m114_verify_link_frequency(sensor, pixrate) == 0) { + sensor->pixrate = pixrate; + sensor->bypass_pll = false; + return 0; } - return 0; + dev_err(&sensor->client->dev, "Unsupported DT link-frequencies\n"); + return -EINVAL; } static int mt9m114_identify(struct mt9m114 *sensor) @@ -2330,6 +2386,17 @@ static int mt9m114_parse_dt(struct mt9m114 *sensor) goto error; } + sensor->pad_slew_rate = MT9M114_PAD_SLEW_DEFAULT; + device_property_read_u32(&sensor->client->dev, "slew-rate", + &sensor->pad_slew_rate); + + if (sensor->pad_slew_rate < MT9M114_PAD_SLEW_MIN || + sensor->pad_slew_rate > MT9M114_PAD_SLEW_MAX) { + dev_err(&sensor->client->dev, "Invalid slew-rate %u\n", + sensor->pad_slew_rate); + return -EINVAL; + } + return 0; error: @@ -2360,10 +2427,10 @@ static int mt9m114_probe(struct i2c_client *client) return ret; /* Acquire clocks, GPIOs and regulators. */ - sensor->clk = devm_clk_get(dev, NULL); + sensor->clk = devm_v4l2_sensor_clk_get(dev, NULL); if (IS_ERR(sensor->clk)) { - ret = PTR_ERR(sensor->clk); - dev_err_probe(dev, ret, "Failed to get clock\n"); + ret = dev_err_probe(dev, PTR_ERR(sensor->clk), + "Failed to get clock\n"); goto error_ep_free; } @@ -2437,7 +2504,6 @@ static int mt9m114_probe(struct i2c_client *client) * Decrease the PM usage count. The device will get suspended after the * autosuspend delay, turning the power off. */ - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return 0; diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c index 4ef5fb06131d..1500ee4db47e 100644 --- a/drivers/media/i2c/mt9p031.c +++ b/drivers/media/i2c/mt9p031.c @@ -233,9 +233,10 @@ static int mt9p031_clk_setup(struct mt9p031 *mt9p031) unsigned long ext_freq; int ret; - mt9p031->clk = devm_clk_get(&client->dev, NULL); + mt9p031->clk = devm_v4l2_sensor_clk_get(&client->dev, NULL); if (IS_ERR(mt9p031->clk)) - return PTR_ERR(mt9p031->clk); + return dev_err_probe(&client->dev, PTR_ERR(mt9p031->clk), + "failed to get the clock\n"); ret = clk_set_rate(mt9p031->clk, mt9p031->ext_freq); if (ret < 0) @@ -1092,6 +1093,7 @@ static int mt9p031_parse_properties(struct mt9p031 *mt9p031, struct device *dev) static int mt9p031_probe(struct i2c_client *client) { struct i2c_adapter *adapter = client->adapter; + const struct mt9p031_model_info *info; struct mt9p031 *mt9p031; unsigned int i; int ret; @@ -1112,7 +1114,8 @@ static int mt9p031_probe(struct i2c_client *client) mt9p031->output_control = MT9P031_OUTPUT_CONTROL_DEF; mt9p031->mode2 = MT9P031_READ_MODE_2_ROW_BLC; - mt9p031->code = (uintptr_t)device_get_match_data(&client->dev); + info = device_get_match_data(&client->dev); + mt9p031->code = info->code; mt9p031->regulators[0].supply = "vdd"; mt9p031->regulators[1].supply = "vdd_io"; diff --git a/drivers/media/i2c/mt9t112.c b/drivers/media/i2c/mt9t112.c index 878dff9b7577..2d2c840fc002 100644 --- a/drivers/media/i2c/mt9t112.c +++ b/drivers/media/i2c/mt9t112.c @@ -1078,13 +1078,12 @@ static int mt9t112_probe(struct i2c_client *client) v4l2_i2c_subdev_init(&priv->subdev, client, &mt9t112_subdev_ops); - priv->clk = devm_clk_get(&client->dev, "extclk"); - if (PTR_ERR(priv->clk) == -ENOENT) { + priv->clk = devm_v4l2_sensor_clk_get(&client->dev, "extclk"); + if (PTR_ERR(priv->clk) == -ENOENT) priv->clk = NULL; - } else if (IS_ERR(priv->clk)) { - dev_err(&client->dev, "Unable to get clock \"extclk\"\n"); - return PTR_ERR(priv->clk); - } + else if (IS_ERR(priv->clk)) + return dev_err_probe(&client->dev, PTR_ERR(priv->clk), + "Unable to get clock \"extclk\"\n"); priv->standby_gpio = devm_gpiod_get_optional(&client->dev, "standby", GPIOD_OUT_HIGH); diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c index 302120ff125e..d4359d5b92bb 100644 --- a/drivers/media/i2c/mt9v032.c +++ b/drivers/media/i2c/mt9v032.c @@ -15,16 +15,15 @@ #include <linux/i2c.h> #include <linux/log2.h> #include <linux/mod_devicetable.h> +#include <linux/module.h> #include <linux/mutex.h> #include <linux/of.h> #include <linux/of_graph.h> #include <linux/regmap.h> #include <linux/slab.h> -#include <linux/videodev2.h> #include <linux/v4l2-mediabus.h> -#include <linux/module.h> +#include <linux/videodev2.h> -#include <media/i2c/mt9v032.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-fwnode.h> @@ -182,7 +181,16 @@ static const struct mt9v032_model_version mt9v032_versions[] = { { MT9V034_CHIP_ID_REV1, "MT9V024/MT9V034 rev1" }, }; +struct mt9v032_platform_data { + unsigned int clk_pol:1; + + const s64 *link_freqs; + s64 link_def_freq; +}; + struct mt9v032 { + struct device *dev; + struct v4l2_subdev subdev; struct media_pad pad; @@ -205,7 +213,7 @@ struct mt9v032 { struct gpio_desc *reset_gpio; struct gpio_desc *standby_gpio; - struct mt9v032_platform_data *pdata; + struct mt9v032_platform_data pdata; const struct mt9v032_model_info *model; const struct mt9v032_model_version *version; @@ -330,7 +338,7 @@ static int __mt9v032_set_power(struct mt9v032 *mt9v032, bool on) return ret; /* Configure the pixel clock polarity */ - if (mt9v032->pdata && mt9v032->pdata->clk_pol) { + if (mt9v032->pdata.clk_pol) { ret = regmap_write(map, mt9v032->model->data->pclk_reg, MT9V032_PIXEL_CLOCK_INV_PXL_CLK); if (ret < 0) @@ -473,13 +481,12 @@ static int mt9v032_get_format(struct v4l2_subdev *subdev, static void mt9v032_configure_pixel_rate(struct mt9v032 *mt9v032) { - struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); int ret; ret = v4l2_ctrl_s_ctrl_int64(mt9v032->pixel_rate, mt9v032->sysclk / mt9v032->hratio); if (ret < 0) - dev_warn(&client->dev, "failed to set pixel rate (%d)\n", ret); + dev_warn(mt9v032->dev, "failed to set pixel rate (%d)\n", ret); } static unsigned int mt9v032_calc_ratio(unsigned int input, unsigned int output) @@ -682,7 +689,7 @@ static int mt9v032_s_ctrl(struct v4l2_ctrl *ctrl) if (mt9v032->link_freq == NULL) break; - freq = mt9v032->pdata->link_freqs[mt9v032->link_freq->val]; + freq = mt9v032->pdata.link_freqs[mt9v032->link_freq->val]; *mt9v032->pixel_rate->p_new.p_s64 = freq; mt9v032->sysclk = freq; break; @@ -883,12 +890,12 @@ static int mt9v032_registered(struct v4l2_subdev *subdev) u32 version; int ret; - dev_info(&client->dev, "Probing MT9V032 at address 0x%02x\n", + dev_info(mt9v032->dev, "Probing MT9V032 at address 0x%02x\n", client->addr); ret = mt9v032_power_on(mt9v032); if (ret < 0) { - dev_err(&client->dev, "MT9V032 power up failed\n"); + dev_err(mt9v032->dev, "MT9V032 power up failed\n"); return ret; } @@ -898,7 +905,7 @@ static int mt9v032_registered(struct v4l2_subdev *subdev) mt9v032_power_off(mt9v032); if (ret < 0) { - dev_err(&client->dev, "Failed reading chip version\n"); + dev_err(mt9v032->dev, "Failed reading chip version\n"); return ret; } @@ -910,12 +917,12 @@ static int mt9v032_registered(struct v4l2_subdev *subdev) } if (mt9v032->version == NULL) { - dev_err(&client->dev, "Unsupported chip version 0x%04x\n", + dev_err(mt9v032->dev, "Unsupported chip version 0x%04x\n", version); return -ENODEV; } - dev_info(&client->dev, "%s detected at address 0x%02x\n", + dev_info(mt9v032->dev, "%s detected at address 0x%02x\n", mt9v032->version->name, client->addr); mt9v032_configure_pixel_rate(mt9v032); @@ -995,41 +1002,33 @@ static const struct regmap_config mt9v032_regmap_config = { * Driver initialization and probing */ -static struct mt9v032_platform_data * -mt9v032_get_pdata(struct i2c_client *client) +static int mt9v032_get_pdata(struct mt9v032 *mt9v032) { - struct mt9v032_platform_data *pdata = NULL; + struct mt9v032_platform_data *pdata = &mt9v032->pdata; struct v4l2_fwnode_endpoint endpoint = { .bus_type = 0 }; - struct device_node *np; + struct device_node *np __free(device_node) = NULL; struct property *prop; - if (!IS_ENABLED(CONFIG_OF) || !client->dev.of_node) - return client->dev.platform_data; - - np = of_graph_get_endpoint_by_regs(client->dev.of_node, 0, -1); + np = of_graph_get_endpoint_by_regs(mt9v032->dev->of_node, 0, -1); if (!np) - return NULL; + return -EINVAL; if (v4l2_fwnode_endpoint_parse(of_fwnode_handle(np), &endpoint) < 0) - goto done; - - pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - goto done; + return -EINVAL; prop = of_find_property(np, "link-frequencies", NULL); if (prop) { u64 *link_freqs; size_t size = prop->length / sizeof(*link_freqs); - link_freqs = devm_kcalloc(&client->dev, size, + link_freqs = devm_kcalloc(mt9v032->dev, size, sizeof(*link_freqs), GFP_KERNEL); if (!link_freqs) - goto done; + return -EINVAL; if (of_property_read_u64_array(np, "link-frequencies", link_freqs, size) < 0) - goto done; + return -EINVAL; pdata->link_freqs = link_freqs; pdata->link_def_freq = link_freqs[0]; @@ -1038,14 +1037,11 @@ mt9v032_get_pdata(struct i2c_client *client) pdata->clk_pol = !!(endpoint.bus.parallel.flags & V4L2_MBUS_PCLK_SAMPLE_RISING); -done: - of_node_put(np); - return pdata; + return 0; } static int mt9v032_probe(struct i2c_client *client) { - struct mt9v032_platform_data *pdata = mt9v032_get_pdata(client); struct mt9v032 *mt9v032; unsigned int i; int ret; @@ -1054,27 +1050,35 @@ static int mt9v032_probe(struct i2c_client *client) if (!mt9v032) return -ENOMEM; + mt9v032->dev = &client->dev; + mt9v032->regmap = devm_regmap_init_i2c(client, &mt9v032_regmap_config); if (IS_ERR(mt9v032->regmap)) return PTR_ERR(mt9v032->regmap); - mt9v032->clk = devm_clk_get(&client->dev, NULL); + mt9v032->clk = devm_v4l2_sensor_clk_get(mt9v032->dev, NULL); if (IS_ERR(mt9v032->clk)) - return PTR_ERR(mt9v032->clk); + return dev_err_probe(mt9v032->dev, PTR_ERR(mt9v032->clk), + "failed to get the clock\n"); - mt9v032->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", + mt9v032->reset_gpio = devm_gpiod_get_optional(mt9v032->dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(mt9v032->reset_gpio)) return PTR_ERR(mt9v032->reset_gpio); - mt9v032->standby_gpio = devm_gpiod_get_optional(&client->dev, "standby", + mt9v032->standby_gpio = devm_gpiod_get_optional(mt9v032->dev, "standby", GPIOD_OUT_LOW); if (IS_ERR(mt9v032->standby_gpio)) return PTR_ERR(mt9v032->standby_gpio); mutex_init(&mt9v032->power_lock); - mt9v032->pdata = pdata; - mt9v032->model = i2c_get_match_data(client); + + ret = mt9v032_get_pdata(mt9v032); + if (ret) + return dev_err_probe(mt9v032->dev, -EINVAL, + "Failed to parse DT properties\n"); + + mt9v032->model = device_get_match_data(mt9v032->dev); v4l2_ctrl_handler_init(&mt9v032->ctrls, 11 + ARRAY_SIZE(mt9v032_aegc_controls)); @@ -1119,7 +1123,8 @@ static int mt9v032_probe(struct i2c_client *client) v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, V4L2_CID_PIXEL_RATE, 1, INT_MAX, 1, 1); - if (pdata && pdata->link_freqs) { + if (mt9v032->pdata.link_freqs) { + const struct mt9v032_platform_data *pdata = &mt9v032->pdata; unsigned int def = 0; for (i = 0; pdata->link_freqs[i]; ++i) { @@ -1139,7 +1144,7 @@ static int mt9v032_probe(struct i2c_client *client) mt9v032->subdev.ctrl_handler = &mt9v032->ctrls; if (mt9v032->ctrls.error) { - dev_err(&client->dev, "control initialization error %d\n", + dev_err(mt9v032->dev, "control initialization error %d\n", mt9v032->ctrls.error); ret = mt9v032->ctrls.error; goto err; @@ -1177,7 +1182,7 @@ static int mt9v032_probe(struct i2c_client *client) if (ret < 0) goto err; - mt9v032->subdev.dev = &client->dev; + mt9v032->subdev.dev = mt9v032->dev; ret = v4l2_async_register_subdev(&mt9v032->subdev); if (ret < 0) goto err; @@ -1261,19 +1266,6 @@ static const struct mt9v032_model_info mt9v032_models[] = { }, }; -static const struct i2c_device_id mt9v032_id[] = { - { "mt9v022", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V022_COLOR] }, - { "mt9v022m", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V022_MONO] }, - { "mt9v024", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V024_COLOR] }, - { "mt9v024m", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V024_MONO] }, - { "mt9v032", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V032_COLOR] }, - { "mt9v032m", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V032_MONO] }, - { "mt9v034", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V034_COLOR] }, - { "mt9v034m", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V034_MONO] }, - { /* Sentinel */ } -}; -MODULE_DEVICE_TABLE(i2c, mt9v032_id); - static const struct of_device_id mt9v032_of_match[] = { { .compatible = "aptina,mt9v022", .data = &mt9v032_models[MT9V032_MODEL_V022_COLOR] }, { .compatible = "aptina,mt9v022m", .data = &mt9v032_models[MT9V032_MODEL_V022_MONO] }, @@ -1294,7 +1286,6 @@ static struct i2c_driver mt9v032_driver = { }, .probe = mt9v032_probe, .remove = mt9v032_remove, - .id_table = mt9v032_id, }; module_i2c_driver(mt9v032_driver); diff --git a/drivers/media/i2c/mt9v111.c b/drivers/media/i2c/mt9v111.c index 723fe138e7bc..64a758c95ab7 100644 --- a/drivers/media/i2c/mt9v111.c +++ b/drivers/media/i2c/mt9v111.c @@ -365,8 +365,6 @@ static int __mt9v111_power_on(struct v4l2_subdev *sd) if (ret) return ret; - clk_set_rate(mt9v111->clk, mt9v111->sysclk); - gpiod_set_value(mt9v111->standby, 0); usleep_range(500, 1000); @@ -532,8 +530,8 @@ static int mt9v111_calc_frame_rate(struct mt9v111_dev *mt9v111, static int mt9v111_hw_config(struct mt9v111_dev *mt9v111) { struct i2c_client *c = mt9v111->client; - unsigned int ret; u16 outfmtctrl2; + int ret; /* Force device reset. */ ret = __mt9v111_hw_reset(mt9v111); @@ -1129,9 +1127,10 @@ static int mt9v111_probe(struct i2c_client *client) mt9v111->dev = &client->dev; mt9v111->client = client; - mt9v111->clk = devm_clk_get(&client->dev, NULL); + mt9v111->clk = devm_v4l2_sensor_clk_get(&client->dev, NULL); if (IS_ERR(mt9v111->clk)) - return PTR_ERR(mt9v111->clk); + return dev_err_probe(&client->dev, PTR_ERR(mt9v111->clk), + "failed to get the clock\n"); mt9v111->sysclk = clk_get_rate(mt9v111->clk); if (mt9v111->sysclk > MT9V111_MAX_CLKIN) @@ -1140,24 +1139,24 @@ static int mt9v111_probe(struct i2c_client *client) mt9v111->oe = devm_gpiod_get_optional(&client->dev, "enable", GPIOD_OUT_LOW); if (IS_ERR(mt9v111->oe)) { - dev_err(&client->dev, "Unable to get GPIO \"enable\": %ld\n", - PTR_ERR(mt9v111->oe)); + dev_err(&client->dev, "Unable to get GPIO \"enable\": %pe\n", + mt9v111->oe); return PTR_ERR(mt9v111->oe); } mt9v111->standby = devm_gpiod_get_optional(&client->dev, "standby", GPIOD_OUT_HIGH); if (IS_ERR(mt9v111->standby)) { - dev_err(&client->dev, "Unable to get GPIO \"standby\": %ld\n", - PTR_ERR(mt9v111->standby)); + dev_err(&client->dev, "Unable to get GPIO \"standby\": %pe\n", + mt9v111->standby); return PTR_ERR(mt9v111->standby); } mt9v111->reset = devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(mt9v111->reset)) { - dev_err(&client->dev, "Unable to get GPIO \"reset\": %ld\n", - PTR_ERR(mt9v111->reset)); + dev_err(&client->dev, "Unable to get GPIO \"reset\": %pe\n", + mt9v111->reset); return PTR_ERR(mt9v111->reset); } diff --git a/drivers/media/i2c/og01a1b.c b/drivers/media/i2c/og01a1b.c index 78d5d406e4b7..c7184de6251a 100644 --- a/drivers/media/i2c/og01a1b.c +++ b/drivers/media/i2c/og01a1b.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2022 Intel Corporation. -#include <linux/unaligned.h> #include <linux/acpi.h> #include <linux/clk.h> #include <linux/delay.h> @@ -10,6 +9,8 @@ #include <linux/module.h> #include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> +#include <linux/unaligned.h> + #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-fwnode.h> @@ -421,6 +422,7 @@ static const struct og01a1b_mode supported_modes[] = { }; struct og01a1b { + struct device *dev; struct clk *xvclk; struct gpio_desc *reset_gpio; struct regulator *avdd; @@ -512,7 +514,6 @@ static int og01a1b_write_reg(struct og01a1b *og01a1b, u16 reg, u16 len, u32 val) static int og01a1b_write_reg_list(struct og01a1b *og01a1b, const struct og01a1b_reg_list *r_list) { - struct i2c_client *client = v4l2_get_subdevdata(&og01a1b->sd); unsigned int i; int ret; @@ -520,7 +521,7 @@ static int og01a1b_write_reg_list(struct og01a1b *og01a1b, ret = og01a1b_write_reg(og01a1b, r_list->regs[i].address, 1, r_list->regs[i].val); if (ret) { - dev_err_ratelimited(&client->dev, + dev_err_ratelimited(og01a1b->dev, "failed to write reg 0x%4.4x. error = %d", r_list->regs[i].address, ret); return ret; @@ -544,7 +545,6 @@ static int og01a1b_set_ctrl(struct v4l2_ctrl *ctrl) { struct og01a1b *og01a1b = container_of(ctrl->handler, struct og01a1b, ctrl_handler); - struct i2c_client *client = v4l2_get_subdevdata(&og01a1b->sd); s64 exposure_max; int ret = 0; @@ -560,7 +560,7 @@ static int og01a1b_set_ctrl(struct v4l2_ctrl *ctrl) } /* V4L2 controls values will be applied only when power is already up */ - if (!pm_runtime_get_if_in_use(&client->dev)) + if (!pm_runtime_get_if_in_use(og01a1b->dev)) return 0; switch (ctrl->id) { @@ -596,7 +596,7 @@ static int og01a1b_set_ctrl(struct v4l2_ctrl *ctrl) break; } - pm_runtime_put(&client->dev); + pm_runtime_put(og01a1b->dev); return ret; } @@ -682,13 +682,12 @@ static void og01a1b_update_pad_format(const struct og01a1b_mode *mode, { fmt->width = mode->width; fmt->height = mode->height; - fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; + fmt->code = MEDIA_BUS_FMT_Y10_1X10; fmt->field = V4L2_FIELD_NONE; } static int og01a1b_start_streaming(struct og01a1b *og01a1b) { - struct i2c_client *client = v4l2_get_subdevdata(&og01a1b->sd); const struct og01a1b_reg_list *reg_list; int link_freq_index, ret; @@ -697,14 +696,14 @@ static int og01a1b_start_streaming(struct og01a1b *og01a1b) ret = og01a1b_write_reg_list(og01a1b, reg_list); if (ret) { - dev_err(&client->dev, "failed to set plls"); + dev_err(og01a1b->dev, "failed to set plls"); return ret; } reg_list = &og01a1b->cur_mode->reg_list; ret = og01a1b_write_reg_list(og01a1b, reg_list); if (ret) { - dev_err(&client->dev, "failed to set mode"); + dev_err(og01a1b->dev, "failed to set mode"); return ret; } @@ -716,7 +715,7 @@ static int og01a1b_start_streaming(struct og01a1b *og01a1b) OG01A1B_REG_VALUE_08BIT, OG01A1B_MODE_STREAMING); if (ret) { - dev_err(&client->dev, "failed to set stream"); + dev_err(og01a1b->dev, "failed to set stream"); return ret; } @@ -725,22 +724,19 @@ static int og01a1b_start_streaming(struct og01a1b *og01a1b) static void og01a1b_stop_streaming(struct og01a1b *og01a1b) { - struct i2c_client *client = v4l2_get_subdevdata(&og01a1b->sd); - if (og01a1b_write_reg(og01a1b, OG01A1B_REG_MODE_SELECT, OG01A1B_REG_VALUE_08BIT, OG01A1B_MODE_STANDBY)) - dev_err(&client->dev, "failed to set stream"); + dev_err(og01a1b->dev, "failed to set stream"); } static int og01a1b_set_stream(struct v4l2_subdev *sd, int enable) { struct og01a1b *og01a1b = to_og01a1b(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); int ret = 0; mutex_lock(&og01a1b->mutex); if (enable) { - ret = pm_runtime_resume_and_get(&client->dev); + ret = pm_runtime_resume_and_get(og01a1b->dev); if (ret) { mutex_unlock(&og01a1b->mutex); return ret; @@ -750,11 +746,11 @@ static int og01a1b_set_stream(struct v4l2_subdev *sd, int enable) if (ret) { enable = 0; og01a1b_stop_streaming(og01a1b); - pm_runtime_put(&client->dev); + pm_runtime_put(og01a1b->dev); } } else { og01a1b_stop_streaming(og01a1b); - pm_runtime_put(&client->dev); + pm_runtime_put(og01a1b->dev); } mutex_unlock(&og01a1b->mutex); @@ -828,7 +824,7 @@ static int og01a1b_enum_mbus_code(struct v4l2_subdev *sd, if (code->index > 0) return -EINVAL; - code->code = MEDIA_BUS_FMT_SGRBG10_1X10; + code->code = MEDIA_BUS_FMT_Y10_1X10; return 0; } @@ -840,7 +836,7 @@ static int og01a1b_enum_frame_size(struct v4l2_subdev *sd, if (fse->index >= ARRAY_SIZE(supported_modes)) return -EINVAL; - if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) + if (fse->code != MEDIA_BUS_FMT_Y10_1X10) return -EINVAL; fse->min_width = supported_modes[fse->index].width; @@ -889,7 +885,6 @@ static const struct v4l2_subdev_internal_ops og01a1b_internal_ops = { static int og01a1b_identify_module(struct og01a1b *og01a1b) { - struct i2c_client *client = v4l2_get_subdevdata(&og01a1b->sd); int ret; u32 val; @@ -899,7 +894,7 @@ static int og01a1b_identify_module(struct og01a1b *og01a1b) return ret; if (val != OG01A1B_CHIP_ID) { - dev_err(&client->dev, "chip id mismatch: %x!=%x", + dev_err(og01a1b->dev, "chip id mismatch: %x!=%x", OG01A1B_CHIP_ID, val); return -ENXIO; } @@ -909,35 +904,18 @@ static int og01a1b_identify_module(struct og01a1b *og01a1b) static int og01a1b_check_hwcfg(struct og01a1b *og01a1b) { - struct i2c_client *client = v4l2_get_subdevdata(&og01a1b->sd); - struct device *dev = &client->dev; + struct device *dev = og01a1b->dev; struct fwnode_handle *ep; struct fwnode_handle *fwnode = dev_fwnode(dev); struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = V4L2_MBUS_CSI2_DPHY }; - u32 mclk; int ret; unsigned int i, j; if (!fwnode) return -ENXIO; - ret = fwnode_property_read_u32(fwnode, "clock-frequency", &mclk); - if (ret) { - if (!og01a1b->xvclk) { - dev_err(dev, "can't get clock frequency"); - return ret; - } - - mclk = clk_get_rate(og01a1b->xvclk); - } - - if (mclk != OG01A1B_MCLK) { - dev_err(dev, "external clock %d is not supported", mclk); - return -EINVAL; - } - ep = fwnode_graph_get_next_endpoint(fwnode, NULL); if (!ep) return -ENXIO; @@ -1066,47 +1044,54 @@ static void og01a1b_remove(struct i2c_client *client) v4l2_async_unregister_subdev(sd); media_entity_cleanup(&sd->entity); v4l2_ctrl_handler_free(sd->ctrl_handler); - pm_runtime_disable(&client->dev); + pm_runtime_disable(og01a1b->dev); mutex_destroy(&og01a1b->mutex); } static int og01a1b_probe(struct i2c_client *client) { struct og01a1b *og01a1b; + unsigned long freq; int ret; og01a1b = devm_kzalloc(&client->dev, sizeof(*og01a1b), GFP_KERNEL); if (!og01a1b) return -ENOMEM; + og01a1b->dev = &client->dev; + v4l2_i2c_subdev_init(&og01a1b->sd, client, &og01a1b_subdev_ops); - og01a1b->xvclk = devm_clk_get_optional(&client->dev, NULL); - if (IS_ERR(og01a1b->xvclk)) { - ret = PTR_ERR(og01a1b->xvclk); - dev_err(&client->dev, "failed to get xvclk clock: %d\n", ret); - return ret; - } + og01a1b->xvclk = devm_v4l2_sensor_clk_get(og01a1b->dev, NULL); + if (IS_ERR(og01a1b->xvclk)) + return dev_err_probe(og01a1b->dev, PTR_ERR(og01a1b->xvclk), + "failed to get xvclk clock\n"); + + freq = clk_get_rate(og01a1b->xvclk); + if (freq != OG01A1B_MCLK) + return dev_err_probe(og01a1b->dev, -EINVAL, + "external clock %lu is not supported", + freq); ret = og01a1b_check_hwcfg(og01a1b); if (ret) { - dev_err(&client->dev, "failed to check HW configuration: %d", + dev_err(og01a1b->dev, "failed to check HW configuration: %d", ret); return ret; } - og01a1b->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", + og01a1b->reset_gpio = devm_gpiod_get_optional(og01a1b->dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(og01a1b->reset_gpio)) { - dev_err(&client->dev, "cannot get reset GPIO\n"); + dev_err(og01a1b->dev, "cannot get reset GPIO\n"); return PTR_ERR(og01a1b->reset_gpio); } - og01a1b->avdd = devm_regulator_get_optional(&client->dev, "avdd"); + og01a1b->avdd = devm_regulator_get_optional(og01a1b->dev, "avdd"); if (IS_ERR(og01a1b->avdd)) { ret = PTR_ERR(og01a1b->avdd); if (ret != -ENODEV) { - dev_err_probe(&client->dev, ret, + dev_err_probe(og01a1b->dev, ret, "Failed to get 'avdd' regulator\n"); return ret; } @@ -1114,11 +1099,11 @@ static int og01a1b_probe(struct i2c_client *client) og01a1b->avdd = NULL; } - og01a1b->dovdd = devm_regulator_get_optional(&client->dev, "dovdd"); + og01a1b->dovdd = devm_regulator_get_optional(og01a1b->dev, "dovdd"); if (IS_ERR(og01a1b->dovdd)) { ret = PTR_ERR(og01a1b->dovdd); if (ret != -ENODEV) { - dev_err_probe(&client->dev, ret, + dev_err_probe(og01a1b->dev, ret, "Failed to get 'dovdd' regulator\n"); return ret; } @@ -1126,11 +1111,11 @@ static int og01a1b_probe(struct i2c_client *client) og01a1b->dovdd = NULL; } - og01a1b->dvdd = devm_regulator_get_optional(&client->dev, "dvdd"); + og01a1b->dvdd = devm_regulator_get_optional(og01a1b->dev, "dvdd"); if (IS_ERR(og01a1b->dvdd)) { ret = PTR_ERR(og01a1b->dvdd); if (ret != -ENODEV) { - dev_err_probe(&client->dev, ret, + dev_err_probe(og01a1b->dev, ret, "Failed to get 'dvdd' regulator\n"); return ret; } @@ -1139,13 +1124,13 @@ static int og01a1b_probe(struct i2c_client *client) } /* The sensor must be powered on to read the CHIP_ID register */ - ret = og01a1b_power_on(&client->dev); + ret = og01a1b_power_on(og01a1b->dev); if (ret) return ret; ret = og01a1b_identify_module(og01a1b); if (ret) { - dev_err(&client->dev, "failed to find sensor: %d", ret); + dev_err(og01a1b->dev, "failed to find sensor: %d", ret); goto power_off; } @@ -1153,7 +1138,7 @@ static int og01a1b_probe(struct i2c_client *client) og01a1b->cur_mode = &supported_modes[0]; ret = og01a1b_init_controls(og01a1b); if (ret) { - dev_err(&client->dev, "failed to init controls: %d", ret); + dev_err(og01a1b->dev, "failed to init controls: %d", ret); goto probe_error_v4l2_ctrl_handler_free; } @@ -1164,21 +1149,21 @@ static int og01a1b_probe(struct i2c_client *client) og01a1b->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&og01a1b->sd.entity, 1, &og01a1b->pad); if (ret) { - dev_err(&client->dev, "failed to init entity pads: %d", ret); + dev_err(og01a1b->dev, "failed to init entity pads: %d", ret); goto probe_error_v4l2_ctrl_handler_free; } ret = v4l2_async_register_subdev_sensor(&og01a1b->sd); if (ret < 0) { - dev_err(&client->dev, "failed to register V4L2 subdev: %d", + dev_err(og01a1b->dev, "failed to register V4L2 subdev: %d", ret); goto probe_error_media_entity_cleanup; } /* Enable runtime PM and turn off the device */ - pm_runtime_set_active(&client->dev); - pm_runtime_enable(&client->dev); - pm_runtime_idle(&client->dev); + pm_runtime_set_active(og01a1b->dev); + pm_runtime_enable(og01a1b->dev); + pm_runtime_idle(og01a1b->dev); return 0; @@ -1190,7 +1175,7 @@ probe_error_v4l2_ctrl_handler_free: mutex_destroy(&og01a1b->mutex); power_off: - og01a1b_power_off(&client->dev); + og01a1b_power_off(og01a1b->dev); return ret; } diff --git a/drivers/media/i2c/og0ve1b.c b/drivers/media/i2c/og0ve1b.c new file mode 100644 index 000000000000..262d9df766fe --- /dev/null +++ b/drivers/media/i2c/og0ve1b.c @@ -0,0 +1,816 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2024-2025 Linaro Ltd + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> +#include <linux/units.h> +#include <media/v4l2-cci.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> + +#define OG0VE1B_LINK_FREQ_500MHZ (500 * HZ_PER_MHZ) +#define OG0VE1B_MCLK_FREQ_24MHZ (24 * HZ_PER_MHZ) + +#define OG0VE1B_REG_CHIP_ID CCI_REG24(0x300a) +#define OG0VE1B_CHIP_ID 0xc75645 + +#define OG0VE1B_REG_MODE_SELECT CCI_REG8(0x0100) +#define OG0VE1B_MODE_STANDBY 0x00 +#define OG0VE1B_MODE_STREAMING BIT(0) + +#define OG0VE1B_REG_SOFTWARE_RST CCI_REG8(0x0103) +#define OG0VE1B_SOFTWARE_RST BIT(0) + +/* Exposure controls from sensor */ +#define OG0VE1B_REG_EXPOSURE CCI_REG24(0x3500) +#define OG0VE1B_EXPOSURE_MIN 1 +#define OG0VE1B_EXPOSURE_MAX_MARGIN 14 +#define OG0VE1B_EXPOSURE_STEP 1 +#define OG0VE1B_EXPOSURE_DEFAULT 554 + +/* Analogue gain controls from sensor */ +#define OG0VE1B_REG_ANALOGUE_GAIN CCI_REG16(0x350a) +#define OG0VE1B_ANALOGUE_GAIN_MIN 1 +#define OG0VE1B_ANALOGUE_GAIN_MAX 0x1ff +#define OG0VE1B_ANALOGUE_GAIN_STEP 1 +#define OG0VE1B_ANALOGUE_GAIN_DEFAULT 16 + +/* Test pattern */ +#define OG0VE1B_REG_PRE_ISP CCI_REG8(0x5e00) +#define OG0VE1B_TEST_PATTERN_ENABLE BIT(7) + +#define to_og0ve1b(_sd) container_of(_sd, struct og0ve1b, sd) + +static const s64 og0ve1b_link_freq_menu[] = { + OG0VE1B_LINK_FREQ_500MHZ, +}; + +struct og0ve1b_reg_list { + const struct cci_reg_sequence *regs; + unsigned int num_regs; +}; + +struct og0ve1b_mode { + u32 width; /* Frame width in pixels */ + u32 height; /* Frame height in pixels */ + u32 hts; /* Horizontal timing size */ + u32 vts; /* Default vertical timing size */ + u32 bpp; /* Bits per pixel */ + + const struct og0ve1b_reg_list reg_list; /* Sensor register setting */ +}; + +static const char * const og0ve1b_test_pattern_menu[] = { + "Disabled", + "Vertical Colour Bars", +}; + +static const char * const og0ve1b_supply_names[] = { + "avdd", /* Analog power */ + "dovdd", /* Digital I/O power */ + "dvdd", /* Digital core power */ +}; + +#define OG0VE1B_NUM_SUPPLIES ARRAY_SIZE(og0ve1b_supply_names) + +struct og0ve1b { + struct device *dev; + struct regmap *regmap; + struct clk *xvclk; + struct gpio_desc *reset_gpio; + struct regulator_bulk_data supplies[OG0VE1B_NUM_SUPPLIES]; + + struct v4l2_subdev sd; + struct media_pad pad; + + struct v4l2_ctrl_handler ctrl_handler; + + /* Saved register value */ + u64 pre_isp; +}; + +static const struct cci_reg_sequence og0ve1b_640x480_120fps_mode[] = { + { CCI_REG8(0x30a0), 0x02 }, + { CCI_REG8(0x30a1), 0x00 }, + { CCI_REG8(0x30a2), 0x48 }, + { CCI_REG8(0x30a3), 0x34 }, + { CCI_REG8(0x30a4), 0xf7 }, + { CCI_REG8(0x30a5), 0x00 }, + { CCI_REG8(0x3082), 0x32 }, + { CCI_REG8(0x3083), 0x01 }, + { CCI_REG8(0x301c), 0xf0 }, + { CCI_REG8(0x301e), 0x0b }, + { CCI_REG8(0x3106), 0x10 }, + { CCI_REG8(0x3708), 0x77 }, + { CCI_REG8(0x3709), 0xf8 }, + { CCI_REG8(0x3717), 0x00 }, + { CCI_REG8(0x3782), 0x00 }, + { CCI_REG8(0x3783), 0x47 }, + { CCI_REG8(0x37a2), 0x00 }, + { CCI_REG8(0x3503), 0x07 }, + { CCI_REG8(0x3509), 0x10 }, + { CCI_REG8(0x3600), 0x83 }, + { CCI_REG8(0x3601), 0x21 }, + { CCI_REG8(0x3602), 0xf1 }, + { CCI_REG8(0x360a), 0x18 }, + { CCI_REG8(0x360e), 0xb3 }, + { CCI_REG8(0x3613), 0x20 }, + { CCI_REG8(0x366a), 0x78 }, + { CCI_REG8(0x3706), 0x63 }, + { CCI_REG8(0x3713), 0x00 }, + { CCI_REG8(0x3716), 0xb0 }, + { CCI_REG8(0x37a1), 0x38 }, + { CCI_REG8(0x3800), 0x00 }, + { CCI_REG8(0x3801), 0x04 }, + { CCI_REG8(0x3802), 0x00 }, + { CCI_REG8(0x3803), 0x04 }, + { CCI_REG8(0x3804), 0x02 }, + { CCI_REG8(0x3805), 0x8b }, + { CCI_REG8(0x3806), 0x01 }, + { CCI_REG8(0x3807), 0xeb }, + { CCI_REG8(0x3808), 0x02 }, /* output width */ + { CCI_REG8(0x3809), 0x80 }, + { CCI_REG8(0x380a), 0x01 }, /* output height */ + { CCI_REG8(0x380b), 0xe0 }, + { CCI_REG8(0x380c), 0x03 }, /* horizontal timing size */ + { CCI_REG8(0x380d), 0x18 }, + { CCI_REG8(0x380e), 0x02 }, /* vertical timing size */ + { CCI_REG8(0x380f), 0x38 }, + { CCI_REG8(0x3811), 0x04 }, + { CCI_REG8(0x3813), 0x04 }, + { CCI_REG8(0x3814), 0x11 }, + { CCI_REG8(0x3815), 0x11 }, + { CCI_REG8(0x3820), 0x00 }, + { CCI_REG8(0x3821), 0x00 }, + { CCI_REG8(0x3823), 0x04 }, + { CCI_REG8(0x382a), 0x00 }, + { CCI_REG8(0x382b), 0x03 }, + { CCI_REG8(0x3840), 0x00 }, + { CCI_REG8(0x389e), 0x00 }, + { CCI_REG8(0x3c05), 0x08 }, + { CCI_REG8(0x3c26), 0x02 }, + { CCI_REG8(0x3c27), 0xc0 }, + { CCI_REG8(0x3c28), 0x00 }, + { CCI_REG8(0x3c29), 0x40 }, + { CCI_REG8(0x3c2c), 0x00 }, + { CCI_REG8(0x3c2d), 0x50 }, + { CCI_REG8(0x3c2e), 0x02 }, + { CCI_REG8(0x3c2f), 0x66 }, + { CCI_REG8(0x3c33), 0x08 }, + { CCI_REG8(0x3c35), 0x00 }, + { CCI_REG8(0x3c36), 0x00 }, + { CCI_REG8(0x3c37), 0x00 }, + { CCI_REG8(0x3f52), 0x9b }, + { CCI_REG8(0x4001), 0x42 }, + { CCI_REG8(0x4004), 0x08 }, + { CCI_REG8(0x4005), 0x00 }, + { CCI_REG8(0x4007), 0x28 }, + { CCI_REG8(0x4009), 0x40 }, + { CCI_REG8(0x4307), 0x30 }, + { CCI_REG8(0x4500), 0x80 }, + { CCI_REG8(0x4501), 0x02 }, + { CCI_REG8(0x4502), 0x47 }, + { CCI_REG8(0x4504), 0x7f }, + { CCI_REG8(0x4601), 0x48 }, + { CCI_REG8(0x4800), 0x64 }, + { CCI_REG8(0x4801), 0x0f }, + { CCI_REG8(0x4806), 0x2f }, + { CCI_REG8(0x4819), 0xaa }, + { CCI_REG8(0x4823), 0x3e }, + { CCI_REG8(0x5000), 0x85 }, + { CCI_REG8(0x5e00), 0x0c }, + { CCI_REG8(0x3899), 0x09 }, + { CCI_REG8(0x4f00), 0x64 }, + { CCI_REG8(0x4f02), 0x0a }, + { CCI_REG8(0x4f05), 0x0e }, + { CCI_REG8(0x4f06), 0x11 }, + { CCI_REG8(0x4f08), 0x0b }, + { CCI_REG8(0x4f0a), 0xc4 }, + { CCI_REG8(0x4f20), 0x1f }, + { CCI_REG8(0x4f25), 0x10 }, + { CCI_REG8(0x3016), 0x10 }, + { CCI_REG8(0x3017), 0x00 }, + { CCI_REG8(0x3018), 0x00 }, + { CCI_REG8(0x3019), 0x00 }, + { CCI_REG8(0x301a), 0x00 }, + { CCI_REG8(0x301b), 0x00 }, + { CCI_REG8(0x301c), 0x72 }, + { CCI_REG8(0x3037), 0x40 }, + { CCI_REG8(0x4f2c), 0x00 }, + { CCI_REG8(0x4f21), 0x00 }, + { CCI_REG8(0x4f23), 0x00 }, + { CCI_REG8(0x4f2a), 0x00 }, + { CCI_REG8(0x3665), 0xe7 }, + { CCI_REG8(0x3668), 0x48 }, + { CCI_REG8(0x3671), 0x3c }, + { CCI_REG8(0x389a), 0x02 }, + { CCI_REG8(0x389b), 0x00 }, + { CCI_REG8(0x303c), 0xa0 }, + { CCI_REG8(0x300f), 0xf0 }, + { CCI_REG8(0x304b), 0x0f }, + { CCI_REG8(0x3662), 0x24 }, + { CCI_REG8(0x3006), 0x40 }, + { CCI_REG8(0x4f26), 0x45 }, + { CCI_REG8(0x3607), 0x34 }, + { CCI_REG8(0x3608), 0x01 }, + { CCI_REG8(0x360a), 0x0c }, + { CCI_REG8(0x360b), 0x86 }, + { CCI_REG8(0x360c), 0xcc }, + { CCI_REG8(0x3013), 0x00 }, + { CCI_REG8(0x3083), 0x02 }, + { CCI_REG8(0x3084), 0x12 }, + { CCI_REG8(0x4601), 0x38 }, + { CCI_REG8(0x366f), 0x3a }, + { CCI_REG8(0x3713), 0x19 }, + { CCI_REG8(0x37a2), 0x00 }, + { CCI_REG8(0x3f43), 0x27 }, + { CCI_REG8(0x3f45), 0x27 }, + { CCI_REG8(0x3f47), 0x32 }, + { CCI_REG8(0x3f49), 0x3e }, + { CCI_REG8(0x3f4b), 0x20 }, + { CCI_REG8(0x3f4d), 0x30 }, + { CCI_REG8(0x4300), 0x3f }, + { CCI_REG8(0x4009), 0x10 }, + { CCI_REG8(0x3f02), 0x68 }, + { CCI_REG8(0x3700), 0x8c }, + { CCI_REG8(0x370b), 0x7e }, + { CCI_REG8(0x3f47), 0x35 }, +}; + +static const struct og0ve1b_mode supported_modes[] = { + { + .width = 640, + .height = 480, + .hts = 792, + .vts = 568, + .bpp = 8, + .reg_list = { + .regs = og0ve1b_640x480_120fps_mode, + .num_regs = ARRAY_SIZE(og0ve1b_640x480_120fps_mode), + }, + }, +}; + +static int og0ve1b_enable_test_pattern(struct og0ve1b *og0ve1b, u32 pattern) +{ + u64 val = og0ve1b->pre_isp; + + if (pattern) + val |= OG0VE1B_TEST_PATTERN_ENABLE; + else + val &= ~OG0VE1B_TEST_PATTERN_ENABLE; + + return cci_write(og0ve1b->regmap, OG0VE1B_REG_PRE_ISP, val, NULL); +} + +static int og0ve1b_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct og0ve1b *og0ve1b = container_of(ctrl->handler, struct og0ve1b, + ctrl_handler); + int ret; + + /* V4L2 controls are applied, when sensor is powered up for streaming */ + if (!pm_runtime_get_if_active(og0ve1b->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + ret = cci_write(og0ve1b->regmap, OG0VE1B_REG_ANALOGUE_GAIN, + ctrl->val, NULL); + break; + case V4L2_CID_EXPOSURE: + ret = cci_write(og0ve1b->regmap, OG0VE1B_REG_EXPOSURE, + ctrl->val << 4, NULL); + break; + case V4L2_CID_TEST_PATTERN: + ret = og0ve1b_enable_test_pattern(og0ve1b, ctrl->val); + break; + default: + ret = -EINVAL; + break; + } + + pm_runtime_put(og0ve1b->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops og0ve1b_ctrl_ops = { + .s_ctrl = og0ve1b_set_ctrl, +}; + +static int og0ve1b_init_controls(struct og0ve1b *og0ve1b) +{ + struct v4l2_ctrl_handler *ctrl_hdlr = &og0ve1b->ctrl_handler; + const struct og0ve1b_mode *mode = &supported_modes[0]; + struct v4l2_fwnode_device_properties props; + s64 exposure_max, pixel_rate, h_blank; + struct v4l2_ctrl *ctrl; + int ret; + + v4l2_ctrl_handler_init(ctrl_hdlr, 9); + + ctrl = v4l2_ctrl_new_int_menu(ctrl_hdlr, &og0ve1b_ctrl_ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(og0ve1b_link_freq_menu) - 1, + 0, og0ve1b_link_freq_menu); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + pixel_rate = og0ve1b_link_freq_menu[0] / mode->bpp; + v4l2_ctrl_new_std(ctrl_hdlr, &og0ve1b_ctrl_ops, V4L2_CID_PIXEL_RATE, + 0, pixel_rate, 1, pixel_rate); + + h_blank = mode->hts - mode->width; + ctrl = v4l2_ctrl_new_std(ctrl_hdlr, &og0ve1b_ctrl_ops, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + ctrl = v4l2_ctrl_new_std(ctrl_hdlr, &og0ve1b_ctrl_ops, V4L2_CID_VBLANK, + mode->vts - mode->height, + mode->vts - mode->height, 1, + mode->vts - mode->height); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(ctrl_hdlr, &og0ve1b_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + OG0VE1B_ANALOGUE_GAIN_MIN, OG0VE1B_ANALOGUE_GAIN_MAX, + OG0VE1B_ANALOGUE_GAIN_STEP, + OG0VE1B_ANALOGUE_GAIN_DEFAULT); + + exposure_max = (mode->vts - OG0VE1B_EXPOSURE_MAX_MARGIN); + v4l2_ctrl_new_std(ctrl_hdlr, &og0ve1b_ctrl_ops, + V4L2_CID_EXPOSURE, + OG0VE1B_EXPOSURE_MIN, exposure_max, + OG0VE1B_EXPOSURE_STEP, + OG0VE1B_EXPOSURE_DEFAULT); + + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &og0ve1b_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(og0ve1b_test_pattern_menu) - 1, + 0, 0, og0ve1b_test_pattern_menu); + + if (ctrl_hdlr->error) + return ctrl_hdlr->error; + + ret = v4l2_fwnode_device_parse(og0ve1b->dev, &props); + if (ret) + goto error_free_hdlr; + + ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &og0ve1b_ctrl_ops, + &props); + if (ret) + goto error_free_hdlr; + + og0ve1b->sd.ctrl_handler = ctrl_hdlr; + + return 0; + +error_free_hdlr: + v4l2_ctrl_handler_free(ctrl_hdlr); + + return ret; +} + +static void og0ve1b_update_pad_format(const struct og0ve1b_mode *mode, + struct v4l2_mbus_framefmt *fmt) +{ + fmt->code = MEDIA_BUS_FMT_Y8_1X8; + fmt->width = mode->width; + fmt->height = mode->height; + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_RAW; + fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; + fmt->xfer_func = V4L2_XFER_FUNC_NONE; +} + +static int og0ve1b_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + const struct og0ve1b_reg_list *reg_list = &supported_modes[0].reg_list; + struct og0ve1b *og0ve1b = to_og0ve1b(sd); + int ret; + + ret = pm_runtime_resume_and_get(og0ve1b->dev); + if (ret) + return ret; + + /* Skip a step of explicit entering into the standby mode */ + ret = cci_write(og0ve1b->regmap, OG0VE1B_REG_SOFTWARE_RST, + OG0VE1B_SOFTWARE_RST, NULL); + if (ret) { + dev_err(og0ve1b->dev, "failed to software reset: %d\n", ret); + goto error; + } + + ret = cci_multi_reg_write(og0ve1b->regmap, reg_list->regs, + reg_list->num_regs, NULL); + if (ret) { + dev_err(og0ve1b->dev, "failed to set mode: %d\n", ret); + goto error; + } + + ret = __v4l2_ctrl_handler_setup(og0ve1b->sd.ctrl_handler); + if (ret) + goto error; + + ret = cci_write(og0ve1b->regmap, OG0VE1B_REG_MODE_SELECT, + OG0VE1B_MODE_STREAMING, NULL); + if (ret) { + dev_err(og0ve1b->dev, "failed to start streaming: %d\n", ret); + goto error; + } + + return 0; + +error: + pm_runtime_put_autosuspend(og0ve1b->dev); + + return ret; +} + +static int og0ve1b_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct og0ve1b *og0ve1b = to_og0ve1b(sd); + int ret; + + ret = cci_write(og0ve1b->regmap, OG0VE1B_REG_MODE_SELECT, + OG0VE1B_MODE_STANDBY, NULL); + if (ret) + dev_err(og0ve1b->dev, "failed to stop streaming: %d\n", ret); + + pm_runtime_put_autosuspend(og0ve1b->dev); + + return ret; +} + +static int og0ve1b_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *format; + const struct og0ve1b_mode *mode; + + format = v4l2_subdev_state_get_format(state, 0); + + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), + width, height, + fmt->format.width, + fmt->format.height); + + og0ve1b_update_pad_format(mode, &fmt->format); + *format = fmt->format; + + return 0; +} + +static int og0ve1b_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_Y8_1X8; + + return 0; +} + +static int og0ve1b_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fse->code != MEDIA_BUS_FMT_Y8_1X8) + 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 int og0ve1b_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_TRY, + .pad = 0, + .format = { + .code = MEDIA_BUS_FMT_Y8_1X8, + .width = supported_modes[0].width, + .height = supported_modes[0].height, + }, + }; + + og0ve1b_set_pad_format(sd, state, &fmt); + + return 0; +} + +static const struct v4l2_subdev_video_ops og0ve1b_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_pad_ops og0ve1b_pad_ops = { + .set_fmt = og0ve1b_set_pad_format, + .get_fmt = v4l2_subdev_get_fmt, + .enum_mbus_code = og0ve1b_enum_mbus_code, + .enum_frame_size = og0ve1b_enum_frame_size, + .enable_streams = og0ve1b_enable_streams, + .disable_streams = og0ve1b_disable_streams, +}; + +static const struct v4l2_subdev_ops og0ve1b_subdev_ops = { + .video = &og0ve1b_video_ops, + .pad = &og0ve1b_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops og0ve1b_internal_ops = { + .init_state = og0ve1b_init_state, +}; + +static const struct media_entity_operations og0ve1b_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static int og0ve1b_identify_sensor(struct og0ve1b *og0ve1b) +{ + u64 val; + int ret; + + ret = cci_read(og0ve1b->regmap, OG0VE1B_REG_CHIP_ID, &val, NULL); + if (ret) { + dev_err(og0ve1b->dev, "failed to read chip id: %d\n", ret); + return ret; + } + + if (val != OG0VE1B_CHIP_ID) { + dev_err(og0ve1b->dev, "chip id mismatch: %x!=%llx\n", + OG0VE1B_CHIP_ID, val); + return -ENODEV; + } + + ret = cci_read(og0ve1b->regmap, OG0VE1B_REG_PRE_ISP, + &og0ve1b->pre_isp, NULL); + if (ret) + dev_err(og0ve1b->dev, "failed to read pre_isp: %d\n", ret); + + return ret; +} + +static int og0ve1b_check_hwcfg(struct og0ve1b *og0ve1b) +{ + struct fwnode_handle *fwnode = dev_fwnode(og0ve1b->dev), *ep; + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; + unsigned long freq_bitmap; + int ret; + + if (!fwnode) + return -ENODEV; + + ep = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!ep) + return -EINVAL; + + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + fwnode_handle_put(ep); + if (ret) + return ret; + + ret = v4l2_link_freq_to_bitmap(og0ve1b->dev, + bus_cfg.link_frequencies, + bus_cfg.nr_of_link_frequencies, + og0ve1b_link_freq_menu, + ARRAY_SIZE(og0ve1b_link_freq_menu), + &freq_bitmap); + + v4l2_fwnode_endpoint_free(&bus_cfg); + + return ret; +} + +static int og0ve1b_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct og0ve1b *og0ve1b = to_og0ve1b(sd); + int ret; + + ret = regulator_bulk_enable(OG0VE1B_NUM_SUPPLIES, og0ve1b->supplies); + if (ret) + return ret; + + gpiod_set_value_cansleep(og0ve1b->reset_gpio, 0); + usleep_range(10 * USEC_PER_MSEC, 15 * USEC_PER_MSEC); + + ret = clk_prepare_enable(og0ve1b->xvclk); + if (ret) + goto reset_gpio; + + return 0; + +reset_gpio: + gpiod_set_value_cansleep(og0ve1b->reset_gpio, 1); + + regulator_bulk_disable(OG0VE1B_NUM_SUPPLIES, og0ve1b->supplies); + + return ret; +} + +static int og0ve1b_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct og0ve1b *og0ve1b = to_og0ve1b(sd); + + clk_disable_unprepare(og0ve1b->xvclk); + + gpiod_set_value_cansleep(og0ve1b->reset_gpio, 1); + + regulator_bulk_disable(OG0VE1B_NUM_SUPPLIES, og0ve1b->supplies); + + return 0; +} + +static int og0ve1b_probe(struct i2c_client *client) +{ + struct og0ve1b *og0ve1b; + unsigned long freq; + unsigned int i; + int ret; + + og0ve1b = devm_kzalloc(&client->dev, sizeof(*og0ve1b), GFP_KERNEL); + if (!og0ve1b) + return -ENOMEM; + + og0ve1b->dev = &client->dev; + + v4l2_i2c_subdev_init(&og0ve1b->sd, client, &og0ve1b_subdev_ops); + + og0ve1b->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(og0ve1b->regmap)) + return dev_err_probe(og0ve1b->dev, PTR_ERR(og0ve1b->regmap), + "failed to init CCI\n"); + + og0ve1b->xvclk = devm_v4l2_sensor_clk_get(og0ve1b->dev, NULL); + if (IS_ERR(og0ve1b->xvclk)) + return dev_err_probe(og0ve1b->dev, PTR_ERR(og0ve1b->xvclk), + "failed to get XVCLK clock\n"); + + freq = clk_get_rate(og0ve1b->xvclk); + if (freq && freq != OG0VE1B_MCLK_FREQ_24MHZ) + return dev_err_probe(og0ve1b->dev, -EINVAL, + "XVCLK clock frequency %lu is not supported\n", + freq); + + ret = og0ve1b_check_hwcfg(og0ve1b); + if (ret) + return dev_err_probe(og0ve1b->dev, ret, + "failed to check HW configuration\n"); + + og0ve1b->reset_gpio = devm_gpiod_get_optional(og0ve1b->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(og0ve1b->reset_gpio)) + return dev_err_probe(og0ve1b->dev, PTR_ERR(og0ve1b->reset_gpio), + "cannot get reset GPIO\n"); + + for (i = 0; i < OG0VE1B_NUM_SUPPLIES; i++) + og0ve1b->supplies[i].supply = og0ve1b_supply_names[i]; + + ret = devm_regulator_bulk_get(og0ve1b->dev, OG0VE1B_NUM_SUPPLIES, + og0ve1b->supplies); + if (ret) + return dev_err_probe(og0ve1b->dev, ret, + "failed to get supply regulators\n"); + + /* The sensor must be powered on to read the CHIP_ID register */ + ret = og0ve1b_power_on(og0ve1b->dev); + if (ret) + return ret; + + ret = og0ve1b_identify_sensor(og0ve1b); + if (ret) { + dev_err_probe(og0ve1b->dev, ret, "failed to find sensor\n"); + goto power_off; + } + + ret = og0ve1b_init_controls(og0ve1b); + if (ret) { + dev_err_probe(og0ve1b->dev, ret, "failed to init controls\n"); + goto power_off; + } + + og0ve1b->sd.state_lock = og0ve1b->ctrl_handler.lock; + og0ve1b->sd.internal_ops = &og0ve1b_internal_ops; + og0ve1b->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + og0ve1b->sd.entity.ops = &og0ve1b_subdev_entity_ops; + og0ve1b->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + og0ve1b->pad.flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&og0ve1b->sd.entity, 1, &og0ve1b->pad); + if (ret) { + dev_err_probe(og0ve1b->dev, ret, + "failed to init media entity pads\n"); + goto v4l2_ctrl_handler_free; + } + + ret = v4l2_subdev_init_finalize(&og0ve1b->sd); + if (ret < 0) { + dev_err_probe(og0ve1b->dev, ret, + "failed to init media entity pads\n"); + goto media_entity_cleanup; + } + + pm_runtime_set_active(og0ve1b->dev); + pm_runtime_enable(og0ve1b->dev); + + ret = v4l2_async_register_subdev_sensor(&og0ve1b->sd); + if (ret < 0) { + dev_err_probe(og0ve1b->dev, ret, + "failed to register V4L2 subdev\n"); + goto subdev_cleanup; + } + + /* Enable runtime PM and turn off the device */ + pm_runtime_idle(og0ve1b->dev); + pm_runtime_set_autosuspend_delay(og0ve1b->dev, 1000); + pm_runtime_use_autosuspend(og0ve1b->dev); + + return 0; + +subdev_cleanup: + v4l2_subdev_cleanup(&og0ve1b->sd); + pm_runtime_disable(og0ve1b->dev); + pm_runtime_set_suspended(og0ve1b->dev); + +media_entity_cleanup: + media_entity_cleanup(&og0ve1b->sd.entity); + +v4l2_ctrl_handler_free: + v4l2_ctrl_handler_free(og0ve1b->sd.ctrl_handler); + +power_off: + og0ve1b_power_off(og0ve1b->dev); + + return ret; +} + +static void og0ve1b_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct og0ve1b *og0ve1b = to_og0ve1b(sd); + + v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(sd->ctrl_handler); + pm_runtime_disable(og0ve1b->dev); + + if (!pm_runtime_status_suspended(og0ve1b->dev)) { + og0ve1b_power_off(og0ve1b->dev); + pm_runtime_set_suspended(og0ve1b->dev); + } +} + +static const struct dev_pm_ops og0ve1b_pm_ops = { + SET_RUNTIME_PM_OPS(og0ve1b_power_off, og0ve1b_power_on, NULL) +}; + +static const struct of_device_id og0ve1b_of_match[] = { + { .compatible = "ovti,og0ve1b" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, og0ve1b_of_match); + +static struct i2c_driver og0ve1b_i2c_driver = { + .driver = { + .name = "og0ve1b", + .pm = &og0ve1b_pm_ops, + .of_match_table = og0ve1b_of_match, + }, + .probe = og0ve1b_probe, + .remove = og0ve1b_remove, +}; + +module_i2c_driver(og0ve1b_i2c_driver); + +MODULE_AUTHOR("Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org>"); +MODULE_DESCRIPTION("OmniVision OG0VE1B sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/ov02a10.c b/drivers/media/i2c/ov02a10.c index 6c30e1a0d814..70d9d7c43f18 100644 --- a/drivers/media/i2c/ov02a10.c +++ b/drivers/media/i2c/ov02a10.c @@ -100,7 +100,8 @@ struct ov02a10_mode { }; struct ov02a10 { - u32 eclk_freq; + struct device *dev; + /* Indication of MIPI transmission speed select */ u32 mipi_clock_voltage; @@ -392,7 +393,7 @@ static int ov02a10_check_sensor_id(struct ov02a10 *ov02a10) chip_id = le16_to_cpu((__force __le16)ret); if ((chip_id & OV02A10_ID_MASK) != OV02A10_ID) { - dev_err(&client->dev, "unexpected sensor id(0x%04x)\n", chip_id); + dev_err(ov02a10->dev, "unexpected sensor id(0x%04x)\n", chip_id); return -EINVAL; } @@ -481,7 +482,7 @@ static int __ov02a10_start_stream(struct ov02a10 *ov02a10) ret = i2c_smbus_write_byte_data(client, REG_MIRROR_FLIP_CONTROL, REG_MIRROR_FLIP_ENABLE); if (ret < 0) { - dev_err(&client->dev, "failed to set orientation\n"); + dev_err(ov02a10->dev, "failed to set orientation\n"); return ret; } ret = i2c_smbus_write_byte_data(client, REG_GLOBAL_EFFECTIVE, @@ -530,7 +531,6 @@ static int ov02a10_init_state(struct v4l2_subdev *sd, static int ov02a10_s_stream(struct v4l2_subdev *sd, int on) { struct ov02a10 *ov02a10 = to_ov02a10(sd); - struct i2c_client *client = v4l2_get_subdevdata(&ov02a10->subdev); int ret; mutex_lock(&ov02a10->mutex); @@ -541,7 +541,7 @@ static int ov02a10_s_stream(struct v4l2_subdev *sd, int on) } if (on) { - ret = pm_runtime_resume_and_get(&client->dev); + ret = pm_runtime_resume_and_get(ov02a10->dev); if (ret < 0) goto unlock_and_return; @@ -553,7 +553,7 @@ static int ov02a10_s_stream(struct v4l2_subdev *sd, int on) } } else { __ov02a10_stop_stream(ov02a10); - pm_runtime_put(&client->dev); + pm_runtime_put(ov02a10->dev); } ov02a10->streaming = on; @@ -562,7 +562,7 @@ static int ov02a10_s_stream(struct v4l2_subdev *sd, int on) return 0; err_rpm_put: - pm_runtime_put(&client->dev); + pm_runtime_put(ov02a10->dev); unlock_and_return: mutex_unlock(&ov02a10->mutex); @@ -662,7 +662,6 @@ static int ov02a10_set_ctrl(struct v4l2_ctrl *ctrl) { struct ov02a10 *ov02a10 = container_of(ctrl->handler, struct ov02a10, ctrl_handler); - struct i2c_client *client = v4l2_get_subdevdata(&ov02a10->subdev); s64 max_expo; int ret; @@ -678,7 +677,7 @@ static int ov02a10_set_ctrl(struct v4l2_ctrl *ctrl) } /* V4L2 controls values will be applied only when power is already up */ - if (!pm_runtime_get_if_in_use(&client->dev)) + if (!pm_runtime_get_if_in_use(ov02a10->dev)) return 0; switch (ctrl->id) { @@ -699,7 +698,7 @@ static int ov02a10_set_ctrl(struct v4l2_ctrl *ctrl) break; } - pm_runtime_put(&client->dev); + pm_runtime_put(ov02a10->dev); return ret; } @@ -734,7 +733,6 @@ static const struct v4l2_ctrl_ops ov02a10_ctrl_ops = { static int ov02a10_initialize_controls(struct ov02a10 *ov02a10) { - struct i2c_client *client = v4l2_get_subdevdata(&ov02a10->subdev); const struct ov02a10_mode *mode; struct v4l2_ctrl_handler *handler; struct v4l2_ctrl *ctrl; @@ -790,7 +788,7 @@ static int ov02a10_initialize_controls(struct ov02a10 *ov02a10) if (handler->error) { ret = handler->error; - dev_err(&client->dev, "failed to init controls(%d)\n", ret); + dev_err(ov02a10->dev, "failed to init controls(%d)\n", ret); goto err_free_handler; } @@ -866,6 +864,8 @@ static int ov02a10_probe(struct i2c_client *client) if (!ov02a10) return -ENOMEM; + ov02a10->dev = dev; + ret = ov02a10_check_hwcfg(dev, ov02a10); if (ret) return dev_err_probe(dev, ret, @@ -885,22 +885,11 @@ static int ov02a10_probe(struct i2c_client *client) ov02a10->fmt.code = MEDIA_BUS_FMT_SRGGB10_1X10; } - ov02a10->eclk = devm_clk_get(dev, "eclk"); + ov02a10->eclk = devm_v4l2_sensor_clk_get_legacy(dev, "eclk", false, 0); if (IS_ERR(ov02a10->eclk)) return dev_err_probe(dev, PTR_ERR(ov02a10->eclk), "failed to get eclk\n"); - ret = device_property_read_u32(dev, "clock-frequency", - &ov02a10->eclk_freq); - if (ret < 0) - return dev_err_probe(dev, ret, - "failed to get eclk frequency\n"); - - ret = clk_set_rate(ov02a10->eclk, ov02a10->eclk_freq); - if (ret < 0) - return dev_err_probe(dev, ret, - "failed to set eclk frequency (24MHz)\n"); - if (clk_get_rate(ov02a10->eclk) != OV02A10_ECLK_FREQ) dev_warn(dev, "eclk mismatched, mode is based on 24MHz\n"); @@ -985,10 +974,10 @@ static void ov02a10_remove(struct i2c_client *client) v4l2_async_unregister_subdev(sd); media_entity_cleanup(&sd->entity); v4l2_ctrl_handler_free(sd->ctrl_handler); - pm_runtime_disable(&client->dev); - if (!pm_runtime_status_suspended(&client->dev)) - ov02a10_power_off(&client->dev); - pm_runtime_set_suspended(&client->dev); + pm_runtime_disable(ov02a10->dev); + if (!pm_runtime_status_suspended(ov02a10->dev)) + ov02a10_power_off(ov02a10->dev); + pm_runtime_set_suspended(ov02a10->dev); mutex_destroy(&ov02a10->mutex); } diff --git a/drivers/media/i2c/ov02c10.c b/drivers/media/i2c/ov02c10.c new file mode 100644 index 000000000000..b1e540eb8326 --- /dev/null +++ b/drivers/media/i2c/ov02c10.c @@ -0,0 +1,1022 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2022 Intel Corporation. + +#include <linux/acpi.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <media/v4l2-cci.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> + +#define OV02C10_LINK_FREQ_400MHZ 400000000ULL +#define OV02C10_MCLK 19200000 +#define OV02C10_RGB_DEPTH 10 + +#define OV02C10_REG_CHIP_ID CCI_REG16(0x300a) +#define OV02C10_CHIP_ID 0x5602 + +#define OV02C10_REG_STREAM_CONTROL CCI_REG8(0x0100) + +#define OV02C10_REG_HTS CCI_REG16(0x380c) + +/* vertical-timings from sensor */ +#define OV02C10_REG_VTS CCI_REG16(0x380e) +#define OV02C10_VTS_MAX 0xffff + +/* Exposure controls from sensor */ +#define OV02C10_REG_EXPOSURE CCI_REG16(0x3501) +#define OV02C10_EXPOSURE_MIN 4 +#define OV02C10_EXPOSURE_MAX_MARGIN 8 +#define OV02C10_EXPOSURE_STEP 1 + +/* Analog gain controls from sensor */ +#define OV02C10_REG_ANALOG_GAIN CCI_REG16(0x3508) +#define OV02C10_ANAL_GAIN_MIN 0x10 +#define OV02C10_ANAL_GAIN_MAX 0xf8 +#define OV02C10_ANAL_GAIN_STEP 1 +#define OV02C10_ANAL_GAIN_DEFAULT 0x10 + +/* Digital gain controls from sensor */ +#define OV02C10_REG_DIGITAL_GAIN CCI_REG24(0x350a) +#define OV02C10_DGTL_GAIN_MIN 0x0400 +#define OV02C10_DGTL_GAIN_MAX 0x3fff +#define OV02C10_DGTL_GAIN_STEP 1 +#define OV02C10_DGTL_GAIN_DEFAULT 0x0400 + +/* Rotate */ +#define OV02C10_ROTATE_CONTROL CCI_REG8(0x3820) +#define OV02C10_ISP_X_WIN_CONTROL CCI_REG16(0x3810) +#define OV02C10_ISP_Y_WIN_CONTROL CCI_REG16(0x3812) +#define OV02C10_CONFIG_ROTATE 0x18 + +/* Test Pattern Control */ +#define OV02C10_REG_TEST_PATTERN CCI_REG8(0x4503) +#define OV02C10_TEST_PATTERN_ENABLE BIT(7) + +struct ov02c10_mode { + /* Frame width in pixels */ + u32 width; + + /* Frame height in pixels */ + u32 height; + + /* Horizontal timining size */ + u32 hts; + + /* Min vertical timining size */ + u32 vts_min; + + /* Sensor register settings for this resolution */ + const struct reg_sequence *reg_sequence; + const int sequence_length; + /* Sensor register settings for 1 or 2 lane config */ + const struct reg_sequence *lane_settings[2]; + const int lane_settings_length[2]; +}; + +static const struct reg_sequence sensor_1928x1092_30fps_setting[] = { + {0x0301, 0x08}, + {0x0303, 0x06}, + {0x0304, 0x01}, + {0x0305, 0xe0}, + {0x0313, 0x40}, + {0x031c, 0x4f}, + {0x3020, 0x97}, + {0x3022, 0x01}, + {0x3026, 0xb4}, + {0x303b, 0x00}, + {0x303c, 0x4f}, + {0x303d, 0xe6}, + {0x303e, 0x00}, + {0x303f, 0x03}, + {0x3021, 0x23}, + {0x3501, 0x04}, + {0x3502, 0x6c}, + {0x3504, 0x0c}, + {0x3507, 0x00}, + {0x3508, 0x08}, + {0x3509, 0x00}, + {0x350a, 0x01}, + {0x350b, 0x00}, + {0x350c, 0x41}, + {0x3600, 0x84}, + {0x3603, 0x08}, + {0x3610, 0x57}, + {0x3611, 0x1b}, + {0x3613, 0x78}, + {0x3623, 0x00}, + {0x3632, 0xa0}, + {0x3642, 0xe8}, + {0x364c, 0x70}, + {0x365f, 0x0f}, + {0x3708, 0x30}, + {0x3714, 0x24}, + {0x3725, 0x02}, + {0x3737, 0x08}, + {0x3739, 0x28}, + {0x3749, 0x32}, + {0x374a, 0x32}, + {0x374b, 0x32}, + {0x374c, 0x32}, + {0x374d, 0x81}, + {0x374e, 0x81}, + {0x374f, 0x81}, + {0x3752, 0x36}, + {0x3753, 0x36}, + {0x3754, 0x36}, + {0x3761, 0x00}, + {0x376c, 0x81}, + {0x3774, 0x18}, + {0x3776, 0x08}, + {0x377c, 0x81}, + {0x377d, 0x81}, + {0x377e, 0x81}, + {0x37a0, 0x44}, + {0x37a6, 0x44}, + {0x37aa, 0x0d}, + {0x37ae, 0x00}, + {0x37cb, 0x03}, + {0x37cc, 0x01}, + {0x37d8, 0x02}, + {0x37d9, 0x10}, + {0x37e1, 0x10}, + {0x37e2, 0x18}, + {0x37e3, 0x08}, + {0x37e4, 0x08}, + {0x37e5, 0x02}, + {0x37e6, 0x08}, + + /* 1928x1092 */ + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x00}, + {0x3804, 0x07}, + {0x3805, 0x8f}, + {0x3806, 0x04}, + {0x3807, 0x47}, + {0x3808, 0x07}, + {0x3809, 0x88}, + {0x380a, 0x04}, + {0x380b, 0x44}, + {0x3810, 0x00}, + {0x3811, 0x02}, + {0x3812, 0x00}, + {0x3813, 0x02}, + {0x3814, 0x01}, + {0x3815, 0x01}, + {0x3816, 0x01}, + {0x3817, 0x01}, + + {0x3820, 0xa0}, + {0x3821, 0x00}, + {0x3822, 0x80}, + {0x3823, 0x08}, + {0x3824, 0x00}, + {0x3825, 0x20}, + {0x3826, 0x00}, + {0x3827, 0x08}, + {0x382a, 0x00}, + {0x382b, 0x08}, + {0x382d, 0x00}, + {0x382e, 0x00}, + {0x382f, 0x23}, + {0x3834, 0x00}, + {0x3839, 0x00}, + {0x383a, 0xd1}, + {0x383e, 0x03}, + {0x393d, 0x29}, + {0x393f, 0x6e}, + {0x394b, 0x06}, + {0x394c, 0x06}, + {0x394d, 0x08}, + {0x394f, 0x01}, + {0x3950, 0x01}, + {0x3951, 0x01}, + {0x3952, 0x01}, + {0x3953, 0x01}, + {0x3954, 0x01}, + {0x3955, 0x01}, + {0x3956, 0x01}, + {0x3957, 0x0e}, + {0x3958, 0x08}, + {0x3959, 0x08}, + {0x395a, 0x08}, + {0x395b, 0x13}, + {0x395c, 0x09}, + {0x395d, 0x05}, + {0x395e, 0x02}, + {0x395f, 0x00}, + {0x395f, 0x00}, + {0x3960, 0x00}, + {0x3961, 0x00}, + {0x3962, 0x00}, + {0x3963, 0x00}, + {0x3964, 0x00}, + {0x3965, 0x00}, + {0x3966, 0x00}, + {0x3967, 0x00}, + {0x3968, 0x01}, + {0x3969, 0x01}, + {0x396a, 0x01}, + {0x396b, 0x01}, + {0x396c, 0x10}, + {0x396d, 0xf0}, + {0x396e, 0x11}, + {0x396f, 0x00}, + {0x3970, 0x37}, + {0x3971, 0x37}, + {0x3972, 0x37}, + {0x3973, 0x37}, + {0x3974, 0x00}, + {0x3975, 0x3c}, + {0x3976, 0x3c}, + {0x3977, 0x3c}, + {0x3978, 0x3c}, + {0x3c00, 0x0f}, + {0x3c20, 0x01}, + {0x3c21, 0x08}, + {0x3f00, 0x8b}, + {0x3f02, 0x0f}, + {0x4000, 0xc3}, + {0x4001, 0xe0}, + {0x4002, 0x00}, + {0x4003, 0x40}, + {0x4008, 0x04}, + {0x4009, 0x23}, + {0x400a, 0x04}, + {0x400b, 0x01}, + {0x4077, 0x06}, + {0x4078, 0x00}, + {0x4079, 0x1a}, + {0x407a, 0x7f}, + {0x407b, 0x01}, + {0x4080, 0x03}, + {0x4081, 0x84}, + {0x4308, 0x03}, + {0x4309, 0xff}, + {0x430d, 0x00}, + {0x4806, 0x00}, + {0x4813, 0x00}, + {0x4837, 0x10}, + {0x4857, 0x05}, + {0x4500, 0x07}, + {0x4501, 0x00}, + {0x4503, 0x00}, + {0x450a, 0x04}, + {0x450e, 0x00}, + {0x450f, 0x00}, + {0x4900, 0x00}, + {0x4901, 0x00}, + {0x4902, 0x01}, + {0x5001, 0x50}, + {0x5006, 0x00}, + {0x5080, 0x40}, + {0x5181, 0x2b}, + {0x5202, 0xa3}, + {0x5206, 0x01}, + {0x5207, 0x00}, + {0x520a, 0x01}, + {0x520b, 0x00}, + {0x365d, 0x00}, + {0x4815, 0x40}, + {0x4816, 0x12}, + {0x4f00, 0x01}, +}; + +static const struct reg_sequence sensor_1928x1092_30fps_1lane_setting[] = { + {0x301b, 0xd2}, + {0x3027, 0xe1}, + {0x380c, 0x08}, + {0x380d, 0xe8}, + {0x380e, 0x04}, + {0x380f, 0x8c}, + {0x394e, 0x0b}, + {0x4800, 0x24}, + {0x5000, 0xf5}, + /* plls */ + {0x0303, 0x05}, + {0x0305, 0x90}, + {0x0316, 0x90}, + {0x3016, 0x12}, +}; + +static const struct reg_sequence sensor_1928x1092_30fps_2lane_setting[] = { + {0x301b, 0xf0}, + {0x3027, 0xf1}, + {0x380c, 0x04}, + {0x380d, 0x74}, + {0x380e, 0x09}, + {0x380f, 0x18}, + {0x394e, 0x0a}, + {0x4041, 0x20}, + {0x4884, 0x04}, + {0x4800, 0x64}, + {0x4d00, 0x03}, + {0x4d01, 0xd8}, + {0x4d02, 0xba}, + {0x4d03, 0xa0}, + {0x4d04, 0xb7}, + {0x4d05, 0x34}, + {0x4d0d, 0x00}, + {0x5000, 0xfd}, + {0x481f, 0x30}, + /* plls */ + {0x0303, 0x05}, + {0x0305, 0x90}, + {0x0316, 0x90}, + {0x3016, 0x32}, +}; + +static const char * const ov02c10_test_pattern_menu[] = { + "Disabled", + "Color Bar", + "Top-Bottom Darker Color Bar", + "Right-Left Darker Color Bar", + "Color Bar type 4", +}; + +static const s64 link_freq_menu_items[] = { + OV02C10_LINK_FREQ_400MHZ, +}; + +static const struct ov02c10_mode supported_modes[] = { + { + .width = 1928, + .height = 1092, + .hts = 2280, + .vts_min = 1164, + .reg_sequence = sensor_1928x1092_30fps_setting, + .sequence_length = ARRAY_SIZE(sensor_1928x1092_30fps_setting), + .lane_settings = { + sensor_1928x1092_30fps_1lane_setting, + sensor_1928x1092_30fps_2lane_setting + }, + .lane_settings_length = { + ARRAY_SIZE(sensor_1928x1092_30fps_1lane_setting), + ARRAY_SIZE(sensor_1928x1092_30fps_2lane_setting), + }, + }, +}; + +static const char * const ov02c10_supply_names[] = { + "dovdd", /* Digital I/O power */ + "avdd", /* Analog power */ + "dvdd", /* Digital core power */ +}; + +struct ov02c10 { + struct device *dev; + + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + struct regmap *regmap; + + /* V4L2 Controls */ + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + + struct clk *img_clk; + struct gpio_desc *reset; + struct regulator_bulk_data supplies[ARRAY_SIZE(ov02c10_supply_names)]; + + /* MIPI lane info */ + u32 link_freq_index; + u8 mipi_lanes; +}; + +static inline struct ov02c10 *to_ov02c10(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct ov02c10, sd); +} + +static int ov02c10_test_pattern(struct ov02c10 *ov02c10, int pattern) +{ + int ret = 0; + + if (!pattern) + return cci_update_bits(ov02c10->regmap, OV02C10_REG_TEST_PATTERN, + BIT(7), 0, NULL); + + cci_update_bits(ov02c10->regmap, OV02C10_REG_TEST_PATTERN, + 0x03, pattern - 1, &ret); + cci_update_bits(ov02c10->regmap, OV02C10_REG_TEST_PATTERN, + BIT(7), OV02C10_TEST_PATTERN_ENABLE, &ret); + return ret; +} + +static int ov02c10_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov02c10 *ov02c10 = container_of(ctrl->handler, + struct ov02c10, ctrl_handler); + const u32 height = supported_modes[0].height; + s64 exposure_max; + int ret = 0; + + /* Propagate change of current control to all related controls */ + if (ctrl->id == V4L2_CID_VBLANK) { + /* Update max exposure while meeting expected vblanking */ + exposure_max = height + ctrl->val - OV02C10_EXPOSURE_MAX_MARGIN; + __v4l2_ctrl_modify_range(ov02c10->exposure, + ov02c10->exposure->minimum, + exposure_max, ov02c10->exposure->step, + exposure_max); + } + + /* V4L2 controls values will be applied only when power is already up */ + if (!pm_runtime_get_if_in_use(ov02c10->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + cci_write(ov02c10->regmap, OV02C10_REG_ANALOG_GAIN, + ctrl->val << 4, &ret); + break; + + case V4L2_CID_DIGITAL_GAIN: + cci_write(ov02c10->regmap, OV02C10_REG_DIGITAL_GAIN, + ctrl->val << 6, &ret); + break; + + case V4L2_CID_EXPOSURE: + cci_write(ov02c10->regmap, OV02C10_REG_EXPOSURE, + ctrl->val, &ret); + break; + + case V4L2_CID_VBLANK: + cci_write(ov02c10->regmap, OV02C10_REG_VTS, height + ctrl->val, + &ret); + break; + + case V4L2_CID_TEST_PATTERN: + ret = ov02c10_test_pattern(ov02c10, ctrl->val); + break; + + case V4L2_CID_HFLIP: + cci_update_bits(ov02c10->regmap, OV02C10_ROTATE_CONTROL, + BIT(3), ov02c10->hflip->val << 3, &ret); + break; + + case V4L2_CID_VFLIP: + cci_update_bits(ov02c10->regmap, OV02C10_ROTATE_CONTROL, + BIT(4), ov02c10->vflip->val << 4, &ret); + break; + + default: + ret = -EINVAL; + break; + } + + pm_runtime_put(ov02c10->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops ov02c10_ctrl_ops = { + .s_ctrl = ov02c10_set_ctrl, +}; + +static int ov02c10_init_controls(struct ov02c10 *ov02c10) +{ + struct v4l2_ctrl_handler *ctrl_hdlr = &ov02c10->ctrl_handler; + const struct ov02c10_mode *mode = &supported_modes[0]; + u32 vblank_min, vblank_max, vblank_default, vts_def; + struct v4l2_fwnode_device_properties props; + s64 exposure_max, h_blank, pixel_rate; + int ret; + + v4l2_ctrl_handler_init(ctrl_hdlr, 12); + + ov02c10->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, + &ov02c10_ctrl_ops, + V4L2_CID_LINK_FREQ, + ov02c10->link_freq_index, 0, + link_freq_menu_items); + if (ov02c10->link_freq) + ov02c10->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + /* MIPI lanes are DDR -> use link-freq * 2 */ + pixel_rate = div_u64(link_freq_menu_items[ov02c10->link_freq_index] * + 2 * ov02c10->mipi_lanes, OV02C10_RGB_DEPTH); + + ov02c10->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov02c10_ctrl_ops, + V4L2_CID_PIXEL_RATE, 0, + pixel_rate, 1, pixel_rate); + + /* + * For default multiple min by number of lanes to keep the default + * FPS the same indepenedent of the lane count. + */ + vts_def = mode->vts_min * ov02c10->mipi_lanes; + + vblank_min = mode->vts_min - mode->height; + vblank_max = OV02C10_VTS_MAX - mode->height; + vblank_default = vts_def - mode->height; + ov02c10->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov02c10_ctrl_ops, + V4L2_CID_VBLANK, vblank_min, + vblank_max, 1, vblank_default); + + h_blank = mode->hts - mode->width; + ov02c10->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov02c10_ctrl_ops, + V4L2_CID_HBLANK, h_blank, h_blank, + 1, h_blank); + if (ov02c10->hblank) + ov02c10->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(ctrl_hdlr, &ov02c10_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + OV02C10_ANAL_GAIN_MIN, OV02C10_ANAL_GAIN_MAX, + OV02C10_ANAL_GAIN_STEP, OV02C10_ANAL_GAIN_DEFAULT); + v4l2_ctrl_new_std(ctrl_hdlr, &ov02c10_ctrl_ops, V4L2_CID_DIGITAL_GAIN, + OV02C10_DGTL_GAIN_MIN, OV02C10_DGTL_GAIN_MAX, + OV02C10_DGTL_GAIN_STEP, OV02C10_DGTL_GAIN_DEFAULT); + exposure_max = vts_def - OV02C10_EXPOSURE_MAX_MARGIN; + ov02c10->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov02c10_ctrl_ops, + V4L2_CID_EXPOSURE, + OV02C10_EXPOSURE_MIN, + exposure_max, + OV02C10_EXPOSURE_STEP, + exposure_max); + + ov02c10->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &ov02c10_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + if (ov02c10->hflip) + ov02c10->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + ov02c10->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &ov02c10_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + if (ov02c10->vflip) + ov02c10->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &ov02c10_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov02c10_test_pattern_menu) - 1, + 0, 0, ov02c10_test_pattern_menu); + + ret = v4l2_fwnode_device_parse(ov02c10->dev, &props); + if (ret) + return ret; + + v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &ov02c10_ctrl_ops, &props); + + if (ctrl_hdlr->error) + return ctrl_hdlr->error; + + ov02c10->sd.ctrl_handler = ctrl_hdlr; + + return 0; +} + +static void ov02c10_update_pad_format(const struct ov02c10_mode *mode, + struct v4l2_mbus_framefmt *fmt) +{ + fmt->width = mode->width; + fmt->height = mode->height; + fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; + fmt->field = V4L2_FIELD_NONE; +} + +static int ov02c10_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + const struct ov02c10_mode *mode = &supported_modes[0]; + struct ov02c10 *ov02c10 = to_ov02c10(sd); + const struct reg_sequence *reg_sequence; + int ret, sequence_length; + + ret = pm_runtime_resume_and_get(ov02c10->dev); + if (ret) + return ret; + + reg_sequence = mode->reg_sequence; + sequence_length = mode->sequence_length; + ret = regmap_multi_reg_write(ov02c10->regmap, + reg_sequence, sequence_length); + if (ret) { + dev_err(ov02c10->dev, "failed to set mode\n"); + goto out; + } + + reg_sequence = mode->lane_settings[ov02c10->mipi_lanes - 1]; + sequence_length = mode->lane_settings_length[ov02c10->mipi_lanes - 1]; + ret = regmap_multi_reg_write(ov02c10->regmap, + reg_sequence, sequence_length); + if (ret) { + dev_err(ov02c10->dev, "failed to write lane settings\n"); + goto out; + } + + ret = __v4l2_ctrl_handler_setup(ov02c10->sd.ctrl_handler); + if (ret) + goto out; + + ret = cci_write(ov02c10->regmap, OV02C10_REG_STREAM_CONTROL, 1, NULL); +out: + if (ret) + pm_runtime_put(ov02c10->dev); + + return ret; +} + +static int ov02c10_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct ov02c10 *ov02c10 = to_ov02c10(sd); + + cci_write(ov02c10->regmap, OV02C10_REG_STREAM_CONTROL, 0, NULL); + pm_runtime_put(ov02c10->dev); + + return 0; +} + +/* This function tries to get power control resources */ +static int ov02c10_get_pm_resources(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov02c10 *ov02c10 = to_ov02c10(sd); + int i; + + ov02c10->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(ov02c10->reset)) + return dev_err_probe(dev, PTR_ERR(ov02c10->reset), + "failed to get reset gpio\n"); + + for (i = 0; i < ARRAY_SIZE(ov02c10_supply_names); i++) + ov02c10->supplies[i].supply = ov02c10_supply_names[i]; + + return devm_regulator_bulk_get(dev, ARRAY_SIZE(ov02c10_supply_names), + ov02c10->supplies); +} + +static int ov02c10_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov02c10 *ov02c10 = to_ov02c10(sd); + + gpiod_set_value_cansleep(ov02c10->reset, 1); + + regulator_bulk_disable(ARRAY_SIZE(ov02c10_supply_names), + ov02c10->supplies); + + clk_disable_unprepare(ov02c10->img_clk); + + return 0; +} + +static int ov02c10_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov02c10 *ov02c10 = to_ov02c10(sd); + int ret; + + ret = clk_prepare_enable(ov02c10->img_clk); + if (ret < 0) { + dev_err(dev, "failed to enable imaging clock: %d", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(ov02c10_supply_names), + ov02c10->supplies); + if (ret < 0) { + dev_err(dev, "failed to enable regulators: %d", ret); + clk_disable_unprepare(ov02c10->img_clk); + return ret; + } + + if (ov02c10->reset) { + /* Assert reset for at least 2ms on back to back off-on */ + usleep_range(2000, 2200); + gpiod_set_value_cansleep(ov02c10->reset, 0); + usleep_range(5000, 5100); + } + + return 0; +} + +static int ov02c10_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + const struct ov02c10_mode *mode = &supported_modes[0]; + struct ov02c10 *ov02c10 = to_ov02c10(sd); + s32 vblank_def, h_blank; + + ov02c10_update_pad_format(mode, &fmt->format); + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + /* Update limits and set FPS to default */ + vblank_def = mode->vts_min * ov02c10->mipi_lanes - mode->height; + __v4l2_ctrl_modify_range(ov02c10->vblank, mode->vts_min - mode->height, + OV02C10_VTS_MAX - mode->height, 1, vblank_def); + __v4l2_ctrl_s_ctrl(ov02c10->vblank, vblank_def); + h_blank = mode->hts - mode->width; + __v4l2_ctrl_modify_range(ov02c10->hblank, h_blank, h_blank, 1, h_blank); + + return 0; +} + +static int ov02c10_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_SGRBG10_1X10; + + return 0; +} + +static int ov02c10_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + 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 int ov02c10_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + ov02c10_update_pad_format(&supported_modes[0], + v4l2_subdev_state_get_format(sd_state, 0)); + + return 0; +} + +static const struct v4l2_subdev_video_ops ov02c10_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_pad_ops ov02c10_pad_ops = { + .set_fmt = ov02c10_set_format, + .get_fmt = v4l2_subdev_get_fmt, + .enum_mbus_code = ov02c10_enum_mbus_code, + .enum_frame_size = ov02c10_enum_frame_size, + .enable_streams = ov02c10_enable_streams, + .disable_streams = ov02c10_disable_streams, +}; + +static const struct v4l2_subdev_ops ov02c10_subdev_ops = { + .video = &ov02c10_video_ops, + .pad = &ov02c10_pad_ops, +}; + +static const struct media_entity_operations ov02c10_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_internal_ops ov02c10_internal_ops = { + .init_state = ov02c10_init_state, +}; + +static int ov02c10_identify_module(struct ov02c10 *ov02c10) +{ + u64 chip_id; + int ret; + + ret = cci_read(ov02c10->regmap, OV02C10_REG_CHIP_ID, &chip_id, NULL); + if (ret) + return ret; + + if (chip_id != OV02C10_CHIP_ID) { + dev_err(ov02c10->dev, "chip id mismatch: %x!=%llx", + OV02C10_CHIP_ID, chip_id); + return -ENXIO; + } + + return 0; +} + +static int ov02c10_check_hwcfg(struct ov02c10 *ov02c10) +{ + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + struct device *dev = ov02c10->dev; + struct fwnode_handle *ep, *fwnode = dev_fwnode(dev); + unsigned long link_freq_bitmap; + int ret; + + /* + * Sometimes the fwnode graph is initialized by the bridge driver, + * wait for this. + */ + ep = fwnode_graph_get_endpoint_by_id(fwnode, 0, 0, 0); + if (!ep) + return dev_err_probe(dev, -EPROBE_DEFER, + "waiting for fwnode graph endpoint\n"); + + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + fwnode_handle_put(ep); + if (ret) + return dev_err_probe(dev, ret, "parsing endpoint failed\n"); + + ret = v4l2_link_freq_to_bitmap(dev, bus_cfg.link_frequencies, + bus_cfg.nr_of_link_frequencies, + link_freq_menu_items, + ARRAY_SIZE(link_freq_menu_items), + &link_freq_bitmap); + if (ret) + goto check_hwcfg_error; + + /* v4l2_link_freq_to_bitmap() guarantees at least 1 bit is set */ + ov02c10->link_freq_index = ffs(link_freq_bitmap) - 1; + + if (bus_cfg.bus.mipi_csi2.num_data_lanes != 1 && + bus_cfg.bus.mipi_csi2.num_data_lanes != 2) { + ret = dev_err_probe(dev, -EINVAL, + "number of CSI2 data lanes %u is not supported\n", + bus_cfg.bus.mipi_csi2.num_data_lanes); + goto check_hwcfg_error; + } + + ov02c10->mipi_lanes = bus_cfg.bus.mipi_csi2.num_data_lanes; + +check_hwcfg_error: + v4l2_fwnode_endpoint_free(&bus_cfg); + return ret; +} + +static void ov02c10_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov02c10 *ov02c10 = to_ov02c10(sd); + + v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(sd->ctrl_handler); + pm_runtime_disable(ov02c10->dev); + if (!pm_runtime_status_suspended(ov02c10->dev)) { + ov02c10_power_off(ov02c10->dev); + pm_runtime_set_suspended(ov02c10->dev); + } +} + +static int ov02c10_probe(struct i2c_client *client) +{ + struct ov02c10 *ov02c10; + unsigned long freq; + int ret; + + ov02c10 = devm_kzalloc(&client->dev, sizeof(*ov02c10), GFP_KERNEL); + if (!ov02c10) + return -ENOMEM; + + ov02c10->dev = &client->dev; + + ov02c10->img_clk = devm_v4l2_sensor_clk_get(ov02c10->dev, NULL); + if (IS_ERR(ov02c10->img_clk)) + return dev_err_probe(ov02c10->dev, PTR_ERR(ov02c10->img_clk), + "failed to get imaging clock\n"); + + freq = clk_get_rate(ov02c10->img_clk); + if (freq != OV02C10_MCLK) + return dev_err_probe(ov02c10->dev, -EINVAL, + "external clock %lu is not supported", + freq); + + v4l2_i2c_subdev_init(&ov02c10->sd, client, &ov02c10_subdev_ops); + + /* Check HW config */ + ret = ov02c10_check_hwcfg(ov02c10); + if (ret) + return ret; + + ret = ov02c10_get_pm_resources(ov02c10->dev); + if (ret) + return ret; + + ov02c10->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(ov02c10->regmap)) + return PTR_ERR(ov02c10->regmap); + + ret = ov02c10_power_on(ov02c10->dev); + if (ret) { + dev_err_probe(ov02c10->dev, ret, "failed to power on\n"); + return ret; + } + + ret = ov02c10_identify_module(ov02c10); + if (ret) { + dev_err(ov02c10->dev, "failed to find sensor: %d", ret); + goto probe_error_power_off; + } + + ret = ov02c10_init_controls(ov02c10); + if (ret) { + dev_err(ov02c10->dev, "failed to init controls: %d", ret); + goto probe_error_v4l2_ctrl_handler_free; + } + + ov02c10->sd.internal_ops = &ov02c10_internal_ops; + ov02c10->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + ov02c10->sd.entity.ops = &ov02c10_subdev_entity_ops; + ov02c10->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + ov02c10->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&ov02c10->sd.entity, 1, &ov02c10->pad); + if (ret) { + dev_err(ov02c10->dev, "failed to init entity pads: %d", ret); + goto probe_error_v4l2_ctrl_handler_free; + } + + ov02c10->sd.state_lock = ov02c10->ctrl_handler.lock; + ret = v4l2_subdev_init_finalize(&ov02c10->sd); + if (ret < 0) { + dev_err(ov02c10->dev, "failed to init subdev: %d", ret); + goto probe_error_media_entity_cleanup; + } + + pm_runtime_set_active(ov02c10->dev); + pm_runtime_enable(ov02c10->dev); + + ret = v4l2_async_register_subdev_sensor(&ov02c10->sd); + if (ret < 0) { + dev_err(ov02c10->dev, "failed to register V4L2 subdev: %d", + ret); + goto probe_error_v4l2_subdev_cleanup; + } + + pm_runtime_idle(ov02c10->dev); + return 0; + +probe_error_v4l2_subdev_cleanup: + pm_runtime_disable(ov02c10->dev); + pm_runtime_set_suspended(ov02c10->dev); + v4l2_subdev_cleanup(&ov02c10->sd); + +probe_error_media_entity_cleanup: + media_entity_cleanup(&ov02c10->sd.entity); + +probe_error_v4l2_ctrl_handler_free: + v4l2_ctrl_handler_free(ov02c10->sd.ctrl_handler); + +probe_error_power_off: + ov02c10_power_off(ov02c10->dev); + + return ret; +} + +static DEFINE_RUNTIME_DEV_PM_OPS(ov02c10_pm_ops, ov02c10_power_off, + ov02c10_power_on, NULL); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id ov02c10_acpi_ids[] = { + { "OVTI02C1" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(acpi, ov02c10_acpi_ids); +#endif + +static const struct of_device_id ov02c10_of_match[] = { + { .compatible = "ovti,ov02c10" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ov02c10_of_match); + +static struct i2c_driver ov02c10_i2c_driver = { + .driver = { + .name = "ov02c10", + .pm = pm_sleep_ptr(&ov02c10_pm_ops), + .acpi_match_table = ACPI_PTR(ov02c10_acpi_ids), + .of_match_table = ov02c10_of_match, + }, + .probe = ov02c10_probe, + .remove = ov02c10_remove, +}; + +module_i2c_driver(ov02c10_i2c_driver); + +MODULE_AUTHOR("Hao Yao <hao.yao@intel.com>"); +MODULE_AUTHOR("Heimir Thor Sverrisson <heimir.sverrisson@gmail.com>"); +MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>"); +MODULE_DESCRIPTION("OmniVision OV02C10 sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/ov02e10.c b/drivers/media/i2c/ov02e10.c new file mode 100644 index 000000000000..4a64cba99991 --- /dev/null +++ b/drivers/media/i2c/ov02e10.c @@ -0,0 +1,956 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2023 Intel Corporation. + +#include <linux/acpi.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <media/v4l2-cci.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> + +#define OV02E10_LINK_FREQ_360MHZ 360000000ULL +#define OV02E10_SCLK 36000000LL +#define OV02E10_MCLK 19200000 +#define OV02E10_DATA_LANES 2 +#define OV02E10_RGB_DEPTH 10 + +#define OV02E10_REG_PAGE_FLAG CCI_REG8(0xfd) +#define OV02E10_PAGE_0 0x0 +#define OV02E10_PAGE_1 0x1 +#define OV02E10_PAGE_2 0x2 +#define OV02E10_PAGE_3 0x3 +#define OV02E10_PAGE_5 0x4 +#define OV02E10_PAGE_7 0x5 +#define OV02E10_PAGE_8 0x6 +#define OV02E10_PAGE_9 0xF +#define OV02E10_PAGE_D 0x8 +#define OV02E10_PAGE_E 0x9 +#define OV02E10_PAGE_F 0xA + +#define OV02E10_REG_CHIP_ID CCI_REG32(0x00) +#define OV02E10_CHIP_ID 0x45025610 + +/* Horizontal and vertical flip */ +#define OV02E10_REG_ORIENTATION CCI_REG8(0x32) + +/* vertical-timings from sensor */ +#define OV02E10_REG_VTS CCI_REG16(0x35) +#define OV02E10_VTS_DEF 2244 +#define OV02E10_VTS_MIN 2244 +#define OV02E10_VTS_MAX 0x7fff + +/* horizontal-timings from sensor */ +#define OV02E10_REG_HTS CCI_REG16(0x37) + +/* Exposure controls from sensor */ +#define OV02E10_REG_EXPOSURE CCI_REG16(0x03) +#define OV02E10_EXPOSURE_MIN 1 +#define OV02E10_EXPOSURE_MAX_MARGIN 2 +#define OV02E10_EXPOSURE_STEP 1 + +/* Analog gain controls from sensor */ +#define OV02E10_REG_ANALOG_GAIN CCI_REG8(0x24) +#define OV02E10_ANAL_GAIN_MIN 0x10 +#define OV02E10_ANAL_GAIN_MAX 0xf8 +#define OV02E10_ANAL_GAIN_STEP 1 + +/* Digital gain controls from sensor */ +#define OV02E10_REG_DIGITAL_GAIN CCI_REG16(0x21) +#define OV02E10_DGTL_GAIN_MIN 256 +#define OV02E10_DGTL_GAIN_MAX 1020 +#define OV02E10_DGTL_GAIN_STEP 1 +#define OV02E10_DGTL_GAIN_DEFAULT 256 + +/* Register update control */ +#define OV02E10_REG_COMMAND_UPDATE CCI_REG8(0xE7) +#define OV02E10_COMMAND_UPDATE 0x00 +#define OV02E10_COMMAND_HOLD 0x01 + +/* Test Pattern Control */ +#define OV02E10_REG_TEST_PATTERN CCI_REG8(0x12) +#define OV02E10_TEST_PATTERN_ENABLE BIT(0) +#define OV02E10_TEST_PATTERN_BAR_SHIFT 1 + +struct reg_sequence_list { + u32 num_regs; + const struct reg_sequence *regs; +}; + +struct ov02e10_mode { + /* Frame width in pixels */ + u32 width; + + /* Frame height in pixels */ + u32 height; + + /* Horizontal timining size */ + u32 hts; + + /* Default vertical timing */ + u32 vts_def; + + /* Min vertical timining size */ + u32 vts_min; + + /* Sensor register settings for this resolution */ + const struct reg_sequence_list reg_list; +}; + +static const struct reg_sequence mode_1928x1088_30fps_2lane[] = { + { 0xfd, 0x00 }, + { 0x20, 0x00 }, + { 0x20, 0x0b }, + { 0x21, 0x02 }, + { 0x10, 0x23 }, + { 0xc5, 0x04 }, + { 0x21, 0x00 }, + { 0x14, 0x96 }, + { 0x17, 0x01 }, + { 0xfd, 0x01 }, + { 0x03, 0x00 }, + { 0x04, 0x04 }, + { 0x05, 0x04 }, + { 0x06, 0x62 }, + { 0x07, 0x01 }, + { 0x22, 0x80 }, + { 0x24, 0xff }, + { 0x40, 0xc6 }, + { 0x41, 0x18 }, + { 0x45, 0x3f }, + { 0x48, 0x0c }, + { 0x4c, 0x08 }, + { 0x51, 0x12 }, + { 0x52, 0x10 }, + { 0x57, 0x98 }, + { 0x59, 0x06 }, + { 0x5a, 0x04 }, + { 0x5c, 0x38 }, + { 0x5e, 0x10 }, + { 0x67, 0x11 }, + { 0x7b, 0x04 }, + { 0x81, 0x12 }, + { 0x90, 0x51 }, + { 0x91, 0x09 }, + { 0x92, 0x21 }, + { 0x93, 0x28 }, + { 0x95, 0x54 }, + { 0x9d, 0x20 }, + { 0x9e, 0x04 }, + { 0xb1, 0x9a }, + { 0xb2, 0x86 }, + { 0xb6, 0x3f }, + { 0xb9, 0x30 }, + { 0xc1, 0x01 }, + { 0xc5, 0xa0 }, + { 0xc6, 0x73 }, + { 0xc7, 0x04 }, + { 0xc8, 0x25 }, + { 0xc9, 0x05 }, + { 0xca, 0x28 }, + { 0xcb, 0x00 }, + { 0xcf, 0x16 }, + { 0xd2, 0xd0 }, + { 0xd7, 0x3f }, + { 0xd8, 0x40 }, + { 0xd9, 0x40 }, + { 0xda, 0x44 }, + { 0xdb, 0x3d }, + { 0xdc, 0x3d }, + { 0xdd, 0x3d }, + { 0xde, 0x3d }, + { 0xdf, 0xf0 }, + { 0xea, 0x0f }, + { 0xeb, 0x04 }, + { 0xec, 0x29 }, + { 0xee, 0x47 }, + { 0xfd, 0x01 }, + { 0x31, 0x01 }, + { 0x27, 0x00 }, + { 0x2f, 0x41 }, + { 0xfd, 0x02 }, + { 0xa1, 0x01 }, + { 0xfd, 0x02 }, + { 0x9a, 0x03 }, + { 0xfd, 0x03 }, + { 0x9d, 0x0f }, + { 0xfd, 0x07 }, + { 0x42, 0x00 }, + { 0x43, 0xad }, + { 0x44, 0x00 }, + { 0x45, 0xa8 }, + { 0x46, 0x00 }, + { 0x47, 0xa8 }, + { 0x48, 0x00 }, + { 0x49, 0xad }, + { 0xfd, 0x00 }, + { 0xc4, 0x01 }, + { 0xfd, 0x01 }, + { 0x33, 0x03 }, + { 0xfd, 0x00 }, + { 0x20, 0x1f }, +}; + +static const char *const ov02e10_test_pattern_menu[] = { + "Disabled", + "Color Bar", +}; + +static const s64 link_freq_menu_items[] = { + OV02E10_LINK_FREQ_360MHZ, +}; + +static const struct ov02e10_mode supported_modes[] = { + { + .width = 1928, + .height = 1088, + .hts = 534, + .vts_def = 2244, + .vts_min = 2244, + .reg_list = { + .num_regs = ARRAY_SIZE(mode_1928x1088_30fps_2lane), + .regs = mode_1928x1088_30fps_2lane, + }, + }, +}; + +static const char * const ov02e10_supply_names[] = { + "dovdd", /* Digital I/O power */ + "avdd", /* Analog power */ + "dvdd", /* Digital core power */ +}; + +struct ov02e10 { + struct device *dev; + + struct regmap *regmap; + 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; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *hflip; + + struct clk *img_clk; + struct regulator_bulk_data supplies[ARRAY_SIZE(ov02e10_supply_names)]; + struct gpio_desc *reset; + + /* Current mode */ + const struct ov02e10_mode *cur_mode; + + /* MIPI lanes info */ + u32 link_freq_index; + u8 mipi_lanes; +}; + +static inline struct ov02e10 *to_ov02e10(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct ov02e10, sd); +} + +static u64 to_pixel_rate(u32 f_index) +{ + u64 pixel_rate = link_freq_menu_items[f_index] * 2 * OV02E10_DATA_LANES; + + do_div(pixel_rate, OV02E10_RGB_DEPTH); + + return pixel_rate; +} + +static u64 to_pixels_per_line(u32 hts, u32 f_index) +{ + u64 ppl = hts * to_pixel_rate(f_index); + + do_div(ppl, OV02E10_SCLK); + + return ppl; +} + +static void ov02e10_test_pattern(struct ov02e10 *ov02e10, u32 pattern, int *pret) +{ + if (pattern) + pattern = pattern << OV02E10_TEST_PATTERN_BAR_SHIFT | + OV02E10_TEST_PATTERN_ENABLE; + + cci_write(ov02e10->regmap, OV02E10_REG_TEST_PATTERN, pattern, pret); +} + +static int ov02e10_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov02e10 *ov02e10 = container_of(ctrl->handler, + struct ov02e10, ctrl_handler); + s64 exposure_max; + int ret; + + /* Propagate change of current control to all related controls */ + if (ctrl->id == V4L2_CID_VBLANK) { + /* Update max exposure while meeting expected vblanking */ + exposure_max = ov02e10->cur_mode->height + ctrl->val - + OV02E10_EXPOSURE_MAX_MARGIN; + ret = __v4l2_ctrl_modify_range(ov02e10->exposure, + ov02e10->exposure->minimum, + exposure_max, + ov02e10->exposure->step, + exposure_max); + if (ret) + return ret; + } + + /* V4L2 controls values will be applied only when power is already up */ + if (!pm_runtime_get_if_in_use(ov02e10->dev)) + return 0; + + ret = cci_write(ov02e10->regmap, OV02E10_REG_COMMAND_UPDATE, + OV02E10_COMMAND_HOLD, NULL); + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + cci_write(ov02e10->regmap, OV02E10_REG_PAGE_FLAG, + OV02E10_PAGE_1, &ret); + cci_write(ov02e10->regmap, OV02E10_REG_ANALOG_GAIN, + ctrl->val, &ret); + break; + + case V4L2_CID_DIGITAL_GAIN: + cci_write(ov02e10->regmap, OV02E10_REG_PAGE_FLAG, + OV02E10_PAGE_1, &ret); + cci_write(ov02e10->regmap, OV02E10_REG_DIGITAL_GAIN, + ctrl->val, &ret); + break; + + case V4L2_CID_EXPOSURE: + cci_write(ov02e10->regmap, OV02E10_REG_PAGE_FLAG, + OV02E10_PAGE_1, &ret); + cci_write(ov02e10->regmap, OV02E10_REG_EXPOSURE, + ctrl->val, &ret); + break; + + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + cci_write(ov02e10->regmap, OV02E10_REG_PAGE_FLAG, + OV02E10_PAGE_1, &ret); + cci_write(ov02e10->regmap, OV02E10_REG_ORIENTATION, + ov02e10->hflip->val | ov02e10->vflip->val << 1, &ret); + break; + case V4L2_CID_VBLANK: + cci_write(ov02e10->regmap, OV02E10_REG_PAGE_FLAG, + OV02E10_PAGE_1, &ret); + cci_write(ov02e10->regmap, OV02E10_REG_VTS, + ov02e10->cur_mode->height + ctrl->val, &ret); + break; + + case V4L2_CID_TEST_PATTERN: + cci_write(ov02e10->regmap, OV02E10_REG_PAGE_FLAG, + OV02E10_PAGE_1, &ret); + ov02e10_test_pattern(ov02e10, ctrl->val, &ret); + break; + + default: + ret = -EINVAL; + break; + } + + cci_write(ov02e10->regmap, OV02E10_REG_COMMAND_UPDATE, + OV02E10_COMMAND_UPDATE, &ret); + + pm_runtime_put(ov02e10->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops ov02e10_ctrl_ops = { + .s_ctrl = ov02e10_set_ctrl, +}; + +static int ov02e10_init_controls(struct ov02e10 *ov02e10) +{ + struct v4l2_ctrl_handler *ctrl_hdlr = &ov02e10->ctrl_handler; + const struct ov02e10_mode *mode = ov02e10->cur_mode; + u32 vblank_min, vblank_max, vblank_def; + struct v4l2_fwnode_device_properties props; + s64 exposure_max, h_blank, pixel_rate; + int ret; + + v4l2_ctrl_handler_init(ctrl_hdlr, 12); + + ov02e10->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, + &ov02e10_ctrl_ops, + V4L2_CID_LINK_FREQ, + ov02e10->link_freq_index, + 0, link_freq_menu_items); + if (ov02e10->link_freq) + ov02e10->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + pixel_rate = to_pixel_rate(ov02e10->link_freq_index); + ov02e10->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov02e10_ctrl_ops, + V4L2_CID_PIXEL_RATE, 0, + pixel_rate, 1, pixel_rate); + + vblank_min = mode->vts_min - mode->height; + vblank_max = OV02E10_VTS_MAX - mode->height; + vblank_def = mode->vts_def - mode->height; + ov02e10->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov02e10_ctrl_ops, + V4L2_CID_VBLANK, vblank_min, + vblank_max, 1, vblank_def); + + h_blank = mode->hts - mode->width; + ov02e10->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov02e10_ctrl_ops, + V4L2_CID_HBLANK, h_blank, h_blank, + 1, h_blank); + if (ov02e10->hblank) + ov02e10->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(ctrl_hdlr, &ov02e10_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + OV02E10_ANAL_GAIN_MIN, OV02E10_ANAL_GAIN_MAX, + OV02E10_ANAL_GAIN_STEP, OV02E10_ANAL_GAIN_MIN); + + v4l2_ctrl_new_std(ctrl_hdlr, &ov02e10_ctrl_ops, V4L2_CID_DIGITAL_GAIN, + OV02E10_DGTL_GAIN_MIN, OV02E10_DGTL_GAIN_MAX, + OV02E10_DGTL_GAIN_STEP, OV02E10_DGTL_GAIN_DEFAULT); + + exposure_max = mode->vts_def - OV02E10_EXPOSURE_MAX_MARGIN; + ov02e10->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov02e10_ctrl_ops, + V4L2_CID_EXPOSURE, + OV02E10_EXPOSURE_MIN, + exposure_max, + OV02E10_EXPOSURE_STEP, + exposure_max); + + ov02e10->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &ov02e10_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + if (ov02e10->hflip) + ov02e10->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + ov02e10->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &ov02e10_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + if (ov02e10->vflip) + ov02e10->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &ov02e10_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov02e10_test_pattern_menu) - 1, + 0, 0, ov02e10_test_pattern_menu); + + ret = v4l2_fwnode_device_parse(ov02e10->dev, &props); + if (ret) + return ret; + + v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &ov02e10_ctrl_ops, &props); + + if (ctrl_hdlr->error) + return ctrl_hdlr->error; + + ov02e10->sd.ctrl_handler = ctrl_hdlr; + + return 0; +} + +static void ov02e10_update_pad_format(const struct ov02e10_mode *mode, + struct v4l2_mbus_framefmt *fmt) +{ + fmt->width = mode->width; + fmt->height = mode->height; + fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; + fmt->field = V4L2_FIELD_NONE; +} + +static int ov02e10_set_stream_mode(struct ov02e10 *ov02e10, u8 val) +{ + int ret = 0; + + cci_write(ov02e10->regmap, OV02E10_REG_PAGE_FLAG, OV02E10_PAGE_0, &ret); + cci_write(ov02e10->regmap, CCI_REG8(0xa0), val, &ret); + cci_write(ov02e10->regmap, OV02E10_REG_PAGE_FLAG, OV02E10_PAGE_1, &ret); + cci_write(ov02e10->regmap, CCI_REG8(0x01), 0x02, &ret); + + return ret; +} + +static int ov02e10_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct ov02e10 *ov02e10 = to_ov02e10(sd); + const struct reg_sequence_list *reg_list; + int ret; + + ret = pm_runtime_resume_and_get(ov02e10->dev); + if (ret) + return ret; + + reg_list = &ov02e10->cur_mode->reg_list; + ret = regmap_multi_reg_write(ov02e10->regmap, reg_list->regs, + reg_list->num_regs); + if (ret) { + dev_err(ov02e10->dev, "failed to set mode\n"); + goto out; + } + + ret = __v4l2_ctrl_handler_setup(ov02e10->sd.ctrl_handler); + if (ret) + goto out; + + ret = ov02e10_set_stream_mode(ov02e10, 1); + +out: + if (ret) + pm_runtime_put(ov02e10->dev); + + return ret; +} + +static int ov02e10_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct ov02e10 *ov02e10 = to_ov02e10(sd); + + ov02e10_set_stream_mode(ov02e10, 0); + pm_runtime_put(ov02e10->dev); + + return 0; +} + +static int ov02e10_get_pm_resources(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov02e10 *ov02e10 = to_ov02e10(sd); + int i; + + ov02e10->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(ov02e10->reset)) + return dev_err_probe(dev, PTR_ERR(ov02e10->reset), + "failed to get reset gpio\n"); + + for (i = 0; i < ARRAY_SIZE(ov02e10_supply_names); i++) + ov02e10->supplies[i].supply = ov02e10_supply_names[i]; + + return devm_regulator_bulk_get(dev, ARRAY_SIZE(ov02e10_supply_names), + ov02e10->supplies); +} + +static int ov02e10_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov02e10 *ov02e10 = to_ov02e10(sd); + + if (ov02e10->reset) + gpiod_set_value_cansleep(ov02e10->reset, 1); + + regulator_bulk_disable(ARRAY_SIZE(ov02e10_supply_names), + ov02e10->supplies); + + clk_disable_unprepare(ov02e10->img_clk); + + return 0; +} + +static int ov02e10_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov02e10 *ov02e10 = to_ov02e10(sd); + int ret; + + ret = clk_prepare_enable(ov02e10->img_clk); + if (ret < 0) { + dev_err(dev, "failed to enable imaging clock: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(ov02e10_supply_names), + ov02e10->supplies); + if (ret < 0) { + dev_err(dev, "failed to enable regulators\n"); + goto disable_clk; + } + + if (ov02e10->reset) { + usleep_range(5000, 5100); + gpiod_set_value_cansleep(ov02e10->reset, 0); + usleep_range(8000, 8100); + } + + return 0; + +disable_clk: + clk_disable_unprepare(ov02e10->img_clk); + + return ret; +} + +static int ov02e10_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct ov02e10 *ov02e10 = to_ov02e10(sd); + const struct ov02e10_mode *mode; + s32 vblank_def, h_blank; + int ret = 0; + + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), + width, height, fmt->format.width, + fmt->format.height); + + ov02e10_update_pad_format(mode, &fmt->format); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; + } else { + ov02e10->cur_mode = mode; + ret = __v4l2_ctrl_s_ctrl(ov02e10->link_freq, + ov02e10->link_freq_index); + if (ret) + return ret; + + ret = __v4l2_ctrl_s_ctrl_int64(ov02e10->pixel_rate, + to_pixel_rate(ov02e10->link_freq_index)); + if (ret) + return ret; + + /* Update limits and set FPS to default */ + vblank_def = mode->vts_def - mode->height; + ret = __v4l2_ctrl_modify_range(ov02e10->vblank, + mode->vts_min - mode->height, + OV02E10_VTS_MAX - mode->height, + 1, vblank_def); + if (ret) + return ret; + + ret = __v4l2_ctrl_s_ctrl(ov02e10->vblank, vblank_def); + if (ret) + return ret; + + h_blank = to_pixels_per_line(mode->hts, ov02e10->link_freq_index); + h_blank -= mode->width; + ret = __v4l2_ctrl_modify_range(ov02e10->hblank, h_blank, + h_blank, 1, h_blank); + } + + return ret; +} + +static int ov02e10_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct ov02e10 *ov02e10 = to_ov02e10(sd); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + fmt->format = *v4l2_subdev_state_get_format(sd_state, fmt->pad); + else + ov02e10_update_pad_format(ov02e10->cur_mode, &fmt->format); + + return 0; +} + +static int ov02e10_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_SGRBG10_1X10; + + return 0; +} + +static int ov02e10_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + 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 int ov02e10_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + ov02e10_update_pad_format(&supported_modes[0], + v4l2_subdev_state_get_format(sd_state, 0)); + + return 0; +} + +static const struct v4l2_subdev_video_ops ov02e10_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_pad_ops ov02e10_pad_ops = { + .set_fmt = ov02e10_set_format, + .get_fmt = ov02e10_get_format, + .enum_mbus_code = ov02e10_enum_mbus_code, + .enum_frame_size = ov02e10_enum_frame_size, + .enable_streams = ov02e10_enable_streams, + .disable_streams = ov02e10_disable_streams, +}; + +static const struct v4l2_subdev_ops ov02e10_subdev_ops = { + .video = &ov02e10_video_ops, + .pad = &ov02e10_pad_ops, +}; + +static const struct media_entity_operations ov02e10_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_internal_ops ov02e10_internal_ops = { + .init_state = ov02e10_init_state, +}; + +static int ov02e10_identify_module(struct ov02e10 *ov02e10) +{ + int ret; + u64 val; + + ret = cci_write(ov02e10->regmap, OV02E10_REG_PAGE_FLAG, + OV02E10_PAGE_0, NULL); + cci_read(ov02e10->regmap, OV02E10_REG_CHIP_ID, &val, &ret); + if (ret) + return ret; + + if (val != OV02E10_CHIP_ID) { + dev_err(ov02e10->dev, "chip id mismatch: %x!=%x\n", + OV02E10_CHIP_ID, (u32)val); + return -ENXIO; + } + + return 0; +} + +static int ov02e10_check_hwcfg(struct ov02e10 *ov02e10) +{ + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + struct device *dev = ov02e10->dev; + struct fwnode_handle *ep; + struct fwnode_handle *fwnode = dev_fwnode(dev); + unsigned long link_freq_bitmap; + int ret; + + ep = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!ep) + return dev_err_probe(dev, -EPROBE_DEFER, + "waiting for fwnode graph endpoint\n"); + + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + fwnode_handle_put(ep); + if (ret) + return dev_err_probe(dev, ret, "parsing endpoint failed\n"); + + if (bus_cfg.bus.mipi_csi2.num_data_lanes != OV02E10_DATA_LANES) { + dev_err(dev, "number of CSI2 data lanes %d is not supported\n", + bus_cfg.bus.mipi_csi2.num_data_lanes); + ret = -EINVAL; + goto out_err; + } + + if (!bus_cfg.nr_of_link_frequencies) { + dev_err(dev, "no link frequencies defined\n"); + ret = -EINVAL; + goto out_err; + } + + ret = v4l2_link_freq_to_bitmap(dev, bus_cfg.link_frequencies, + bus_cfg.nr_of_link_frequencies, + link_freq_menu_items, + ARRAY_SIZE(link_freq_menu_items), + &link_freq_bitmap); + if (ret) + goto out_err; + + /* v4l2_link_freq_to_bitmap() guarantees at least 1 bit is set */ + ov02e10->link_freq_index = ffs(link_freq_bitmap) - 1; + ov02e10->mipi_lanes = bus_cfg.bus.mipi_csi2.num_data_lanes; + +out_err: + v4l2_fwnode_endpoint_free(&bus_cfg); + + return ret; +} + +static void ov02e10_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov02e10 *ov02e10 = to_ov02e10(sd); + + v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(sd->ctrl_handler); + pm_runtime_disable(ov02e10->dev); + + if (!pm_runtime_status_suspended(ov02e10->dev)) { + ov02e10_power_off(ov02e10->dev); + pm_runtime_set_suspended(ov02e10->dev); + } +} + +static int ov02e10_probe(struct i2c_client *client) +{ + struct ov02e10 *ov02e10; + unsigned long freq; + int ret; + + ov02e10 = devm_kzalloc(&client->dev, sizeof(*ov02e10), GFP_KERNEL); + if (!ov02e10) + return -ENOMEM; + + ov02e10->dev = &client->dev; + + ov02e10->img_clk = devm_v4l2_sensor_clk_get(ov02e10->dev, NULL); + if (IS_ERR(ov02e10->img_clk)) + return dev_err_probe(ov02e10->dev, PTR_ERR(ov02e10->img_clk), + "failed to get imaging clock\n"); + + freq = clk_get_rate(ov02e10->img_clk); + if (freq != OV02E10_MCLK) + return dev_err_probe(ov02e10->dev, -EINVAL, + "external clock %lu is not supported", + freq); + + v4l2_i2c_subdev_init(&ov02e10->sd, client, &ov02e10_subdev_ops); + + /* Check HW config */ + ret = ov02e10_check_hwcfg(ov02e10); + if (ret) + return ret; + + /* Initialize subdev */ + ov02e10->regmap = devm_cci_regmap_init_i2c(client, 8); + if (IS_ERR(ov02e10->regmap)) + return PTR_ERR(ov02e10->regmap); + + ret = ov02e10_get_pm_resources(ov02e10->dev); + if (ret) + return ret; + + ret = ov02e10_power_on(ov02e10->dev); + if (ret) { + dev_err_probe(ov02e10->dev, ret, "failed to power on\n"); + return ret; + } + + /* Check module identity */ + ret = ov02e10_identify_module(ov02e10); + if (ret) { + dev_err(ov02e10->dev, "failed to find sensor: %d\n", ret); + goto probe_error_power_off; + } + + ov02e10->cur_mode = &supported_modes[0]; + ret = ov02e10_init_controls(ov02e10); + if (ret) { + dev_err(ov02e10->dev, "failed to init controls: %d\n", ret); + goto probe_error_v4l2_ctrl_handler_free; + } + + /* Initialize subdev */ + ov02e10->sd.internal_ops = &ov02e10_internal_ops; + ov02e10->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + ov02e10->sd.entity.ops = &ov02e10_subdev_entity_ops; + ov02e10->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + /* Initialize source pad */ + ov02e10->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&ov02e10->sd.entity, 1, &ov02e10->pad); + if (ret) { + dev_err(ov02e10->dev, "failed to init entity pads: %d", ret); + goto probe_error_v4l2_ctrl_handler_free; + } + + ov02e10->sd.state_lock = ov02e10->ctrl_handler.lock; + ret = v4l2_subdev_init_finalize(&ov02e10->sd); + if (ret < 0) { + dev_err(ov02e10->dev, "failed to init subdev: %d", ret); + goto probe_error_media_entity_cleanup; + } + + pm_runtime_set_active(ov02e10->dev); + pm_runtime_enable(ov02e10->dev); + + ret = v4l2_async_register_subdev_sensor(&ov02e10->sd); + if (ret < 0) { + dev_err(ov02e10->dev, "failed to register V4L2 subdev: %d", + ret); + goto probe_error_v4l2_subdev_cleanup; + } + + pm_runtime_idle(ov02e10->dev); + return 0; + +probe_error_v4l2_subdev_cleanup: + pm_runtime_disable(ov02e10->dev); + pm_runtime_set_suspended(ov02e10->dev); + v4l2_subdev_cleanup(&ov02e10->sd); + +probe_error_media_entity_cleanup: + media_entity_cleanup(&ov02e10->sd.entity); + +probe_error_v4l2_ctrl_handler_free: + v4l2_ctrl_handler_free(ov02e10->sd.ctrl_handler); + +probe_error_power_off: + ov02e10_power_off(ov02e10->dev); + + return ret; +} + +static DEFINE_RUNTIME_DEV_PM_OPS(ov02e10_pm_ops, ov02e10_power_off, + ov02e10_power_on, NULL); + +static const struct acpi_device_id ov02e10_acpi_ids[] = { + { "OVTI02E1" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(acpi, ov02e10_acpi_ids); + +static const struct of_device_id ov02e10_of_match[] = { + { .compatible = "ovti,ov02e10" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ov02e10_of_match); + +static struct i2c_driver ov02e10_i2c_driver = { + .driver = { + .name = "ov02e10", + .pm = pm_sleep_ptr(&ov02e10_pm_ops), + .acpi_match_table = ov02e10_acpi_ids, + .of_match_table = ov02e10_of_match, + }, + .probe = ov02e10_probe, + .remove = ov02e10_remove, +}; + +module_i2c_driver(ov02e10_i2c_driver); + +MODULE_AUTHOR("Jingjing Xiong"); +MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); +MODULE_AUTHOR("Alan Stern <stern@rowland.harvard.edu>"); +MODULE_AUTHOR("Bryan O'Donoghue <bryan.odonoghue@linaro.org>"); +MODULE_DESCRIPTION("OmniVision OV02E10 sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/ov08d10.c b/drivers/media/i2c/ov08d10.c index 1bacbdfa4298..43ec2a1f2fcf 100644 --- a/drivers/media/i2c/ov08d10.c +++ b/drivers/media/i2c/ov08d10.c @@ -515,12 +515,13 @@ static const char * const ov08d10_test_pattern_menu[] = { }; struct ov08d10 { + struct device *dev; + struct clk *clk; + struct v4l2_subdev sd; struct media_pad pad; struct v4l2_ctrl_handler ctrl_handler; - struct clk *xvclk; - /* V4L2 Controls */ struct v4l2_ctrl *link_freq; struct v4l2_ctrl *pixel_rate; @@ -663,7 +664,7 @@ static int ov08d10_write_reg_list(struct ov08d10 *ov08d10, ret = i2c_smbus_write_byte_data(client, r_list->regs[i].address, r_list->regs[i].val); if (ret) { - dev_err_ratelimited(&client->dev, + dev_err_ratelimited(ov08d10->dev, "failed to write reg 0x%2.2x. error = %d", r_list->regs[i].address, ret); return ret; @@ -849,7 +850,6 @@ static int ov08d10_set_ctrl(struct v4l2_ctrl *ctrl) { struct ov08d10 *ov08d10 = container_of(ctrl->handler, struct ov08d10, ctrl_handler); - struct i2c_client *client = v4l2_get_subdevdata(&ov08d10->sd); s64 exposure_max; int ret; @@ -865,7 +865,7 @@ static int ov08d10_set_ctrl(struct v4l2_ctrl *ctrl) } /* V4L2 controls values will be applied only when power is already up */ - if (!pm_runtime_get_if_in_use(&client->dev)) + if (!pm_runtime_get_if_in_use(ov08d10->dev)) return 0; switch (ctrl->id) { @@ -901,7 +901,7 @@ static int ov08d10_set_ctrl(struct v4l2_ctrl *ctrl) break; } - pm_runtime_put(&client->dev); + pm_runtime_put(ov08d10->dev); return ret; } @@ -1025,32 +1025,32 @@ static int ov08d10_start_streaming(struct ov08d10 *ov08d10) /* soft reset */ ret = i2c_smbus_write_byte_data(client, OV08D10_REG_PAGE, 0x00); if (ret < 0) { - dev_err(&client->dev, "failed to reset sensor"); + dev_err(ov08d10->dev, "failed to reset sensor"); return ret; } ret = i2c_smbus_write_byte_data(client, 0x20, 0x0e); if (ret < 0) { - dev_err(&client->dev, "failed to reset sensor"); + dev_err(ov08d10->dev, "failed to reset sensor"); return ret; } usleep_range(3000, 4000); ret = i2c_smbus_write_byte_data(client, 0x20, 0x0b); if (ret < 0) { - dev_err(&client->dev, "failed to reset sensor"); + dev_err(ov08d10->dev, "failed to reset sensor"); return ret; } /* update sensor setting */ ret = ov08d10_write_reg_list(ov08d10, reg_list); if (ret) { - dev_err(&client->dev, "failed to set plls"); + dev_err(ov08d10->dev, "failed to set plls"); return ret; } reg_list = &ov08d10->cur_mode->reg_list; ret = ov08d10_write_reg_list(ov08d10, reg_list); if (ret) { - dev_err(&client->dev, "failed to set mode"); + dev_err(ov08d10->dev, "failed to set mode"); return ret; } @@ -1077,19 +1077,19 @@ static void ov08d10_stop_streaming(struct ov08d10 *ov08d10) ret = i2c_smbus_write_byte_data(client, OV08D10_REG_PAGE, 0x00); if (ret < 0) { - dev_err(&client->dev, "failed to stop streaming"); + dev_err(ov08d10->dev, "failed to stop streaming"); return; } ret = i2c_smbus_write_byte_data(client, OV08D10_REG_MODE_SELECT, OV08D10_MODE_STANDBY); if (ret < 0) { - dev_err(&client->dev, "failed to stop streaming"); + dev_err(ov08d10->dev, "failed to stop streaming"); return; } ret = i2c_smbus_write_byte_data(client, OV08D10_REG_PAGE, 0x01); if (ret < 0) { - dev_err(&client->dev, "failed to stop streaming"); + dev_err(ov08d10->dev, "failed to stop streaming"); return; } } @@ -1097,12 +1097,11 @@ static void ov08d10_stop_streaming(struct ov08d10 *ov08d10) static int ov08d10_set_stream(struct v4l2_subdev *sd, int enable) { struct ov08d10 *ov08d10 = to_ov08d10(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); int ret = 0; mutex_lock(&ov08d10->mutex); if (enable) { - ret = pm_runtime_resume_and_get(&client->dev); + ret = pm_runtime_resume_and_get(ov08d10->dev); if (ret < 0) { mutex_unlock(&ov08d10->mutex); return ret; @@ -1112,11 +1111,11 @@ static int ov08d10_set_stream(struct v4l2_subdev *sd, int enable) if (ret) { enable = 0; ov08d10_stop_streaming(ov08d10); - pm_runtime_put(&client->dev); + pm_runtime_put(ov08d10->dev); } } else { ov08d10_stop_streaming(ov08d10); - pm_runtime_put(&client->dev); + pm_runtime_put(ov08d10->dev); } /* vflip and hflip cannot change during streaming */ @@ -1293,7 +1292,7 @@ static int ov08d10_identify_module(struct ov08d10 *ov08d10) chip_id = val | ret; if ((chip_id & OV08D10_ID_MASK) != OV08D10_CHIP_ID) { - dev_err(&client->dev, "unexpected sensor id(0x%04x)\n", + dev_err(ov08d10->dev, "unexpected sensor id(0x%04x)\n", chip_id); return -EINVAL; } @@ -1301,28 +1300,20 @@ static int ov08d10_identify_module(struct ov08d10 *ov08d10) return 0; } -static int ov08d10_get_hwcfg(struct ov08d10 *ov08d10, struct device *dev) +static int ov08d10_get_hwcfg(struct ov08d10 *ov08d10) { + struct device *dev = ov08d10->dev; struct fwnode_handle *ep; struct fwnode_handle *fwnode = dev_fwnode(dev); struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = V4L2_MBUS_CSI2_DPHY }; - u32 xvclk_rate; unsigned int i, j; int ret; if (!fwnode) return -ENXIO; - ret = fwnode_property_read_u32(fwnode, "clock-frequency", &xvclk_rate); - if (ret) - return ret; - - if (xvclk_rate != OV08D10_XVCLK_19_2) - dev_warn(dev, "external clock rate %u is unsupported", - xvclk_rate); - ep = fwnode_graph_get_next_endpoint(fwnode, NULL); if (!ep) return -ENXIO; @@ -1380,22 +1371,35 @@ static void ov08d10_remove(struct i2c_client *client) v4l2_async_unregister_subdev(sd); media_entity_cleanup(&sd->entity); v4l2_ctrl_handler_free(sd->ctrl_handler); - pm_runtime_disable(&client->dev); + pm_runtime_disable(ov08d10->dev); mutex_destroy(&ov08d10->mutex); } static int ov08d10_probe(struct i2c_client *client) { struct ov08d10 *ov08d10; + unsigned long freq; int ret; ov08d10 = devm_kzalloc(&client->dev, sizeof(*ov08d10), GFP_KERNEL); if (!ov08d10) return -ENOMEM; - ret = ov08d10_get_hwcfg(ov08d10, &client->dev); + ov08d10->dev = &client->dev; + + ov08d10->clk = devm_v4l2_sensor_clk_get(ov08d10->dev, NULL); + if (IS_ERR(ov08d10->clk)) + return dev_err_probe(ov08d10->dev, PTR_ERR(ov08d10->clk), + "failed to get clock\n"); + + freq = clk_get_rate(ov08d10->clk); + if (freq != OV08D10_XVCLK_19_2) + dev_warn(ov08d10->dev, + "external clock rate %lu is not supported\n", freq); + + ret = ov08d10_get_hwcfg(ov08d10); if (ret) { - dev_err(&client->dev, "failed to get HW configuration: %d", + dev_err(ov08d10->dev, "failed to get HW configuration: %d", ret); return ret; } @@ -1404,7 +1408,7 @@ static int ov08d10_probe(struct i2c_client *client) ret = ov08d10_identify_module(ov08d10); if (ret) { - dev_err(&client->dev, "failed to find sensor: %d", ret); + dev_err(ov08d10->dev, "failed to find sensor: %d", ret); return ret; } @@ -1412,7 +1416,7 @@ static int ov08d10_probe(struct i2c_client *client) ov08d10->cur_mode = &ov08d10->priv_lane->sp_modes[0]; ret = ov08d10_init_controls(ov08d10); if (ret) { - dev_err(&client->dev, "failed to init controls: %d", ret); + dev_err(ov08d10->dev, "failed to init controls: %d", ret); goto probe_error_v4l2_ctrl_handler_free; } @@ -1422,13 +1426,13 @@ static int ov08d10_probe(struct i2c_client *client) ov08d10->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&ov08d10->sd.entity, 1, &ov08d10->pad); if (ret) { - dev_err(&client->dev, "failed to init entity pads: %d", ret); + dev_err(ov08d10->dev, "failed to init entity pads: %d", ret); goto probe_error_v4l2_ctrl_handler_free; } ret = v4l2_async_register_subdev_sensor(&ov08d10->sd); if (ret < 0) { - dev_err(&client->dev, "failed to register V4L2 subdev: %d", + dev_err(ov08d10->dev, "failed to register V4L2 subdev: %d", ret); goto probe_error_media_entity_cleanup; } @@ -1437,9 +1441,9 @@ static int ov08d10_probe(struct i2c_client *client) * Device is already turned on by i2c-core with ACPI domain PM. * Enable runtime PM and turn off the device. */ - pm_runtime_set_active(&client->dev); - pm_runtime_enable(&client->dev); - pm_runtime_idle(&client->dev); + pm_runtime_set_active(ov08d10->dev); + pm_runtime_enable(ov08d10->dev); + pm_runtime_idle(ov08d10->dev); return 0; diff --git a/drivers/media/i2c/ov08x40.c b/drivers/media/i2c/ov08x40.c index b9682264e2f5..5eaf454f4763 100644 --- a/drivers/media/i2c/ov08x40.c +++ b/drivers/media/i2c/ov08x40.c @@ -1,15 +1,17 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2022 Intel Corporation. -#include <linux/unaligned.h> #include <linux/acpi.h> #include <linux/clk.h> -#include <linux/i2c.h> +#include <linux/delay.h> #include <linux/gpio/consumer.h> +#include <linux/i2c.h> #include <linux/module.h> -#include <linux/delay.h> #include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> +#include <linux/unaligned.h> + +#include <media/v4l2-common.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-fwnode.h> @@ -107,6 +109,7 @@ enum { OV08X40_LINK_FREQ_400MHZ_INDEX, + OV08X40_LINK_FREQ_749MHZ_INDEX, }; struct ov08x40_reg { @@ -150,67 +153,7 @@ struct ov08x40_mode { u16 exposure_shift; }; -static const struct ov08x40_reg mipi_data_rate_800mbps[] = { - {0x0103, 0x01}, - {0x1000, 0x00}, - {0x1601, 0xd0}, - {0x1001, 0x04}, - {0x5004, 0x53}, - {0x5110, 0x00}, - {0x5111, 0x14}, - {0x5112, 0x01}, - {0x5113, 0x7b}, - {0x5114, 0x00}, - {0x5152, 0xa3}, - {0x5a52, 0x1f}, - {0x5a1a, 0x0e}, - {0x5a1b, 0x10}, - {0x5a1f, 0x0e}, - {0x5a27, 0x0e}, - {0x6002, 0x2e}, -}; - -static const struct ov08x40_reg mode_3856x2416_regs[] = { - {0x5000, 0x5d}, - {0x5001, 0x20}, - {0x5008, 0xb0}, - {0x50c1, 0x00}, - {0x53c1, 0x00}, - {0x5f40, 0x00}, - {0x5f41, 0x40}, - {0x0300, 0x3a}, - {0x0301, 0xc8}, - {0x0302, 0x31}, - {0x0303, 0x03}, - {0x0304, 0x01}, - {0x0305, 0xa1}, - {0x0306, 0x04}, - {0x0307, 0x01}, - {0x0308, 0x03}, - {0x0309, 0x03}, - {0x0310, 0x0a}, - {0x0311, 0x02}, - {0x0312, 0x01}, - {0x0313, 0x08}, - {0x0314, 0x66}, - {0x0315, 0x00}, - {0x0316, 0x34}, - {0x0320, 0x02}, - {0x0321, 0x03}, - {0x0323, 0x05}, - {0x0324, 0x01}, - {0x0325, 0xb8}, - {0x0326, 0x4a}, - {0x0327, 0x04}, - {0x0329, 0x00}, - {0x032a, 0x05}, - {0x032b, 0x00}, - {0x032c, 0x00}, - {0x032d, 0x00}, - {0x032e, 0x02}, - {0x032f, 0xa0}, - {0x0350, 0x00}, - {0x0360, 0x01}, +static const struct ov08x40_reg ov08x40_global_regs[] = { {0x1216, 0x60}, {0x1217, 0x5b}, {0x1218, 0x00}, @@ -220,7 +163,6 @@ static const struct ov08x40_reg mode_3856x2416_regs[] = { {0x198e, 0x00}, {0x198f, 0x01}, {0x3009, 0x04}, - {0x3012, 0x41}, {0x3015, 0x00}, {0x3016, 0xb0}, {0x3017, 0xf0}, @@ -245,11 +187,10 @@ static const struct ov08x40_reg mode_3856x2416_regs[] = { {0x3058, 0x80}, {0x3059, 0x00}, {0x3107, 0x86}, - {0x3400, 0x1c}, {0x3401, 0x80}, {0x3402, 0x8c}, - {0x3419, 0x13}, - {0x341a, 0x89}, + {0x3404, 0x01}, + {0x3407, 0x01}, {0x341b, 0x30}, {0x3420, 0x00}, {0x3421, 0x00}, @@ -257,7 +198,7 @@ static const struct ov08x40_reg mode_3856x2416_regs[] = { {0x3423, 0x00}, {0x3424, 0x00}, {0x3425, 0x00}, - {0x3426, 0x00}, + {0x3426, 0x10}, {0x3427, 0x00}, {0x3428, 0x0f}, {0x3429, 0x00}, @@ -286,27 +227,28 @@ static const struct ov08x40_reg mode_3856x2416_regs[] = { {0x3455, 0x80}, {0x3456, 0x08}, {0x3500, 0x00}, - {0x3501, 0x02}, - {0x3502, 0x00}, + {0x3502, 0x10}, {0x3504, 0x4c}, {0x3506, 0x30}, {0x3507, 0x00}, - {0x3508, 0x01}, - {0x3509, 0x00}, {0x350a, 0x01}, {0x350b, 0x00}, {0x350c, 0x00}, {0x3540, 0x00}, - {0x3541, 0x01}, - {0x3542, 0x00}, {0x3544, 0x4c}, {0x3546, 0x30}, {0x3547, 0x00}, - {0x3548, 0x01}, {0x3549, 0x00}, {0x354a, 0x01}, {0x354b, 0x00}, {0x354c, 0x00}, + {0x3601, 0x40}, + {0x3602, 0x90}, + {0x3608, 0x0a}, + {0x3609, 0x08}, + {0x360f, 0x99}, + {0x3680, 0xa4}, + {0x3682, 0x80}, {0x3688, 0x02}, {0x368a, 0x2e}, {0x368e, 0x71}, @@ -316,9 +258,7 @@ static const struct ov08x40_reg mode_3856x2416_regs[] = { {0x36a4, 0x00}, {0x36a6, 0x00}, {0x3711, 0x00}, - {0x3712, 0x51}, {0x3713, 0x00}, - {0x3714, 0x24}, {0x3716, 0x00}, {0x3718, 0x07}, {0x371a, 0x1c}, @@ -327,7 +267,6 @@ static const struct ov08x40_reg mode_3856x2416_regs[] = { {0x3725, 0x32}, {0x3727, 0x05}, {0x3760, 0x02}, - {0x3761, 0x17}, {0x3762, 0x02}, {0x3763, 0x02}, {0x3764, 0x02}, @@ -337,41 +276,19 @@ static const struct ov08x40_reg mode_3856x2416_regs[] = { {0x3768, 0x02}, {0x3769, 0x00}, {0x376b, 0x20}, - {0x376e, 0x03}, - {0x37b0, 0x00}, - {0x37b1, 0xab}, {0x37b2, 0x01}, - {0x37b3, 0x82}, - {0x37b4, 0x00}, - {0x37b5, 0xe4}, - {0x37b6, 0x01}, - {0x37b7, 0xee}, {0x3800, 0x00}, {0x3801, 0x00}, {0x3802, 0x00}, - {0x3803, 0x00}, {0x3804, 0x0f}, {0x3805, 0x1f}, {0x3806, 0x09}, - {0x3807, 0x7f}, - {0x3808, 0x0f}, - {0x3809, 0x10}, - {0x380a, 0x09}, - {0x380b, 0x70}, {0x380c, 0x02}, - {0x380d, 0x80}, - {0x380e, 0x13}, - {0x380f, 0x88}, {0x3810, 0x00}, - {0x3811, 0x08}, {0x3812, 0x00}, - {0x3813, 0x07}, {0x3814, 0x11}, {0x3815, 0x11}, - {0x3820, 0x00}, - {0x3821, 0x04}, {0x3822, 0x00}, - {0x3823, 0x04}, {0x3828, 0x0f}, {0x382a, 0x80}, {0x382e, 0x41}, @@ -386,7 +303,6 @@ static const struct ov08x40_reg mode_3856x2416_regs[] = { {0x3847, 0x00}, {0x384a, 0x00}, {0x384c, 0x02}, - {0x384d, 0x80}, {0x3856, 0x50}, {0x3857, 0x30}, {0x3858, 0x80}, @@ -400,9 +316,45 @@ static const struct ov08x40_reg mode_3856x2416_regs[] = { {0x388d, 0x00}, {0x388e, 0x00}, {0x388f, 0x00}, - {0x3894, 0x00}, {0x3895, 0x00}, + {0x3911, 0x90}, + {0x3913, 0x90}, + {0x3921, 0x0f}, + {0x3928, 0x15}, + {0x3929, 0x2a}, + {0x392c, 0x02}, + {0x392e, 0x04}, + {0x392f, 0x03}, + {0x3931, 0x07}, + {0x3932, 0x10}, + {0x3938, 0x09}, + {0x3a1f, 0x8a}, + {0x3a22, 0x91}, + {0x3a23, 0x15}, + {0x3a25, 0x96}, + {0x3a28, 0xb4}, + {0x3a29, 0x26}, + {0x3a2b, 0xba}, + {0x3a2e, 0xbf}, + {0x3a2f, 0x18}, + {0x3a31, 0xc1}, + {0x3a74, 0x84}, + {0x3a99, 0x84}, + {0x3ab9, 0xa6}, + {0x3aba, 0xba}, + {0x3b0a, 0x01}, + {0x3b0b, 0x00}, + {0x3b0e, 0x01}, + {0x3b0f, 0x00}, + {0x3b12, 0x84}, + {0x3b14, 0xbb}, + {0x3b15, 0xbf}, + {0x3b1b, 0xc9}, + {0x3b21, 0xc9}, + {0x3b3f, 0x9d}, + {0x3b45, 0x9d}, {0x3c84, 0x00}, + {0x3d84, 0x04}, {0x3d85, 0x8b}, {0x3daa, 0x80}, {0x3dab, 0x14}, @@ -424,44 +376,21 @@ static const struct ov08x40_reg mode_3856x2416_regs[] = { {0x4008, 0x00}, {0x4009, 0x05}, {0x400a, 0x00}, - {0x400b, 0x08}, {0x400c, 0x00}, - {0x400d, 0x08}, {0x400e, 0x14}, {0x4010, 0xf4}, {0x4011, 0x03}, {0x4012, 0x55}, {0x4015, 0x00}, - {0x4016, 0x2d}, {0x4017, 0x00}, {0x4018, 0x0f}, + {0x4019, 0x00}, + {0x401a, 0x40}, {0x401b, 0x08}, {0x401c, 0x00}, {0x401d, 0x10}, {0x401e, 0x02}, {0x401f, 0x00}, - {0x4050, 0x06}, - {0x4051, 0xff}, - {0x4052, 0xff}, - {0x4053, 0xff}, - {0x4054, 0xff}, - {0x4055, 0xff}, - {0x4056, 0xff}, - {0x4057, 0x7f}, - {0x4058, 0x00}, - {0x4059, 0x00}, - {0x405a, 0x00}, - {0x405b, 0x00}, - {0x405c, 0x07}, - {0x405d, 0xff}, - {0x405e, 0x07}, - {0x405f, 0xff}, - {0x4080, 0x78}, - {0x4081, 0x78}, - {0x4082, 0x78}, - {0x4083, 0x78}, - {0x4019, 0x00}, - {0x401a, 0x40}, {0x4020, 0x04}, {0x4021, 0x00}, {0x4022, 0x04}, @@ -486,6 +415,22 @@ static const struct ov08x40_reg mode_3856x2416_regs[] = { {0x4045, 0x80}, {0x4046, 0x00}, {0x4047, 0x80}, + {0x4050, 0x06}, + {0x4051, 0xff}, + {0x4052, 0xff}, + {0x4053, 0xff}, + {0x4054, 0xff}, + {0x4055, 0xff}, + {0x4056, 0xff}, + {0x4057, 0x7f}, + {0x4058, 0x00}, + {0x4059, 0x00}, + {0x405a, 0x00}, + {0x405b, 0x00}, + {0x405c, 0x07}, + {0x405d, 0xff}, + {0x405e, 0x07}, + {0x405f, 0xff}, {0x4060, 0x00}, {0x4061, 0x00}, {0x4062, 0x00}, @@ -518,6 +463,10 @@ static const struct ov08x40_reg mode_3856x2416_regs[] = { {0x407d, 0x00}, {0x407e, 0x00}, {0x407f, 0x00}, + {0x4080, 0x78}, + {0x4081, 0x78}, + {0x4082, 0x78}, + {0x4083, 0x78}, {0x40e0, 0x00}, {0x40e1, 0x00}, {0x40e2, 0x00}, @@ -560,7 +509,6 @@ static const struct ov08x40_reg mode_3856x2416_regs[] = { {0x431b, 0x00}, {0x431c, 0x00}, {0x4500, 0x07}, - {0x4501, 0x00}, {0x4502, 0x00}, {0x4503, 0x0f}, {0x4504, 0x80}, @@ -573,7 +521,6 @@ static const struct ov08x40_reg mode_3856x2416_regs[] = { {0x4510, 0x00}, {0x4523, 0x00}, {0x4526, 0x00}, - {0x4542, 0x00}, {0x4543, 0x00}, {0x4544, 0x00}, {0x4545, 0x00}, @@ -592,8 +539,6 @@ static const struct ov08x40_reg mode_3856x2416_regs[] = { {0x480c, 0x80}, {0x480f, 0x32}, {0x4813, 0xe4}, - {0x4837, 0x14}, - {0x4850, 0x42}, {0x4884, 0x04}, {0x4c00, 0xf8}, {0x4c01, 0x44}, @@ -604,93 +549,37 @@ static const struct ov08x40_reg mode_3856x2416_regs[] = { {0x4d05, 0x00}, {0x4d06, 0x0c}, {0x4d07, 0x00}, - {0x3d84, 0x04}, - {0x3680, 0xa4}, - {0x3682, 0x80}, - {0x3601, 0x40}, - {0x3602, 0x90}, - {0x3608, 0x0a}, - {0x3938, 0x09}, - {0x3a74, 0x84}, - {0x3a99, 0x84}, - {0x3ab9, 0xa6}, - {0x3aba, 0xba}, - {0x3b12, 0x84}, - {0x3b14, 0xbb}, - {0x3b15, 0xbf}, - {0x3a29, 0x26}, - {0x3a1f, 0x8a}, - {0x3a22, 0x91}, - {0x3a25, 0x96}, - {0x3a28, 0xb4}, - {0x3a2b, 0xba}, - {0x3a2e, 0xbf}, - {0x3a31, 0xc1}, - {0x3a20, 0x00}, - {0x3939, 0x9d}, - {0x3902, 0x0e}, - {0x3903, 0x0e}, - {0x3904, 0x0e}, - {0x3905, 0x0e}, - {0x3906, 0x07}, - {0x3907, 0x0d}, - {0x3908, 0x11}, - {0x3909, 0x12}, - {0x360f, 0x99}, - {0x390c, 0x33}, - {0x390d, 0x66}, - {0x390e, 0xaa}, - {0x3911, 0x90}, - {0x3913, 0x90}, - {0x3915, 0x90}, - {0x3917, 0x90}, - {0x3b3f, 0x9d}, - {0x3b45, 0x9d}, - {0x3b1b, 0xc9}, - {0x3b21, 0xc9}, - {0x3440, 0xa4}, - {0x3a23, 0x15}, - {0x3a26, 0x1d}, - {0x3a2c, 0x4a}, - {0x3a2f, 0x18}, - {0x3a32, 0x55}, - {0x3b0a, 0x01}, - {0x3b0b, 0x00}, - {0x3b0e, 0x01}, - {0x3b0f, 0x00}, - {0x392c, 0x02}, - {0x392d, 0x02}, - {0x392e, 0x04}, - {0x392f, 0x03}, - {0x3930, 0x08}, - {0x3931, 0x07}, - {0x3932, 0x10}, - {0x3933, 0x0c}, - {0x3609, 0x08}, - {0x3921, 0x0f}, - {0x3928, 0x15}, - {0x3929, 0x2a}, - {0x392a, 0x54}, - {0x392b, 0xa8}, - {0x3426, 0x10}, - {0x3407, 0x01}, - {0x3404, 0x01}, - {0x3500, 0x00}, - {0x3501, 0x10}, - {0x3502, 0x10}, - {0x3508, 0x0f}, - {0x3509, 0x80}, -}; - -static const struct ov08x40_reg mode_1928x1208_regs[] = { - {0x5000, 0x55}, - {0x5001, 0x00}, {0x5008, 0xb0}, {0x50c1, 0x00}, {0x53c1, 0x00}, {0x5f40, 0x00}, {0x5f41, 0x40}, - {0x0300, 0x3a}, +}; + +static const struct ov08x40_reg_list ov08x40_global_setting = { + .num_of_regs = ARRAY_SIZE(ov08x40_global_regs), + .regs = ov08x40_global_regs, +}; + +static const struct ov08x40_reg mipi_data_rate_800mbps[] = { + {0x0103, 0x01}, + {0x1000, 0x00}, + {0x1601, 0xd0}, + {0x1001, 0x04}, + {0x5004, 0x53}, + {0x5110, 0x00}, + {0x5111, 0x14}, + {0x5112, 0x01}, + {0x5113, 0x7b}, + {0x5114, 0x00}, + {0x5152, 0xa3}, + {0x5a52, 0x1f}, + {0x5a1a, 0x0e}, + {0x5a1b, 0x10}, + {0x5a1f, 0x0e}, + {0x5a27, 0x0e}, + {0x6002, 0x2e}, + {0x0300, 0x3a}, /* PLL CTRL */ {0x0301, 0xc8}, {0x0302, 0x31}, {0x0303, 0x03}, @@ -723,421 +612,522 @@ static const struct ov08x40_reg mode_1928x1208_regs[] = { {0x032f, 0xa0}, {0x0350, 0x00}, {0x0360, 0x01}, - {0x1216, 0x60}, - {0x1217, 0x5b}, - {0x1218, 0x00}, - {0x1220, 0x24}, - {0x198a, 0x00}, - {0x198b, 0x01}, - {0x198e, 0x00}, - {0x198f, 0x01}, - {0x3009, 0x04}, + {0x3012, 0x41}, /* MIPI SC Lanes */ +}; + +static const struct ov08x40_reg mipi_data_rate_1500mbps[] = { + {0x0103, 0x01}, + {0x1000, 0x00}, + {0x1601, 0xd0}, + {0x1001, 0x04}, + {0x5004, 0x53}, + {0x5110, 0x00}, + {0x5111, 0x14}, + {0x5112, 0x01}, + {0x5113, 0x7b}, + {0x5114, 0x00}, + {0x5152, 0xa3}, + {0x5a52, 0x1f}, + {0x5a1a, 0x0e}, + {0x5a1b, 0x10}, + {0x5a1f, 0x0e}, + {0x5a27, 0x0e}, + {0x6002, 0x2e}, + {0x0300, 0x3a}, /* PLL */ + {0x0301, 0x88}, + {0x0302, 0x31}, + {0x0303, 0x05}, + {0x0304, 0x01}, + {0x0305, 0x38}, + {0x0306, 0x04}, + {0x0307, 0x00}, + {0x0308, 0x03}, + {0x0309, 0x02}, + {0x0310, 0x0a}, + {0x0311, 0x02}, + {0x0312, 0x01}, + {0x0313, 0x08}, + {0x0314, 0x00}, + {0x0315, 0x00}, + {0x0316, 0x2c}, + {0x0320, 0x02}, + {0x0321, 0x03}, + {0x0323, 0x05}, + {0x0324, 0x01}, + {0x0325, 0xb8}, + {0x0326, 0x4a}, + {0x0327, 0x04}, + {0x0329, 0x00}, + {0x032a, 0x05}, + {0x032b, 0x00}, + {0x032c, 0x00}, + {0x032d, 0x00}, + {0x032e, 0x02}, + {0x032f, 0xa0}, + {0x0350, 0x00}, + {0x0360, 0x01}, + {0x3012, 0x21}, /* MIPI SC Lanes */ +}; + +static const struct ov08x40_reg mode_3856x2176_regs_800mbps[] = { + {0x5000, 0x5d}, + {0x5001, 0x20}, + {0x3012, 0x41}, + {0x3400, 0x1c}, + {0x3419, 0x13}, + {0x341a, 0x89}, + {0x3426, 0x00}, + {0x3501, 0x02}, + {0x3502, 0x00}, + {0x3508, 0x01}, + {0x3509, 0x00}, + {0x3541, 0x01}, + {0x3542, 0x00}, + {0x3548, 0x01}, + {0x3712, 0x51}, + {0x3714, 0x24}, + {0x3761, 0x17}, + {0x376e, 0x03}, + {0x37b0, 0x00}, + {0x37b1, 0xab}, + {0x37b3, 0x82}, + {0x37b4, 0x00}, + {0x37b5, 0xe4}, + {0x37b6, 0x01}, + {0x37b7, 0xee}, + {0x3820, 0x00}, + {0x3821, 0x04}, + {0x3823, 0x04}, + {0x384d, 0x80}, + {0x3894, 0x00}, + {0x400b, 0x08}, + {0x400d, 0x08}, + {0x4016, 0x2d}, + {0x4501, 0x00}, + {0x4542, 0x00}, + {0x4837, 0x14}, + {0x4850, 0x42}, + {0x3a20, 0x00}, + {0x3939, 0x9d}, + {0x3902, 0x0e}, + {0x3903, 0x0e}, + {0x3904, 0x0e}, + {0x3905, 0x0e}, + {0x3906, 0x07}, + {0x3907, 0x0d}, + {0x3908, 0x11}, + {0x3909, 0x12}, + {0x390c, 0x33}, + {0x390d, 0x66}, + {0x390e, 0xaa}, + {0x3915, 0x90}, + {0x3917, 0x90}, + {0x3440, 0xa4}, + {0x3a26, 0x1d}, + {0x3a2c, 0x4a}, + {0x3a32, 0x55}, + {0x392d, 0x02}, + {0x3930, 0x08}, + {0x3933, 0x0c}, + {0x392a, 0x54}, + {0x392b, 0xa8}, + {0x380d, 0x80}, + {0x380e, 0x13}, + {0x380f, 0x88}, + {0x3803, 0x70}, + {0x3807, 0x0f}, + {0x3808, 0x0f}, + {0x3809, 0x10}, + {0x380a, 0x08}, + {0x380b, 0x80}, + {0x3811, 0x08}, + {0x3813, 0x10}, + {0x3501, 0x10}, + {0x3508, 0x0f}, + {0x3509, 0x80}, + {0x3813, 0x0f}, +}; + +/* OV08X 1C 3856x2176_DPHY1500M-2L */ +static const struct ov08x40_reg mode_3856x2176_regs_1500mbps[] = { + {0x5000, 0x5d}, + {0x5001, 0x20}, + {0x3012, 0x21}, + {0x3400, 0x1c}, + {0x3419, 0x12}, + {0x341a, 0x99}, + {0x3426, 0x00}, + {0x3501, 0x02}, + {0x3502, 0x00}, + {0x3508, 0x01}, + {0x3509, 0x00}, + {0x3541, 0x01}, + {0x3542, 0x00}, + {0x3548, 0x01}, + {0x3712, 0x51}, + {0x3714, 0x24}, + {0x3761, 0x17}, + {0x376e, 0x03}, + {0x37b0, 0x00}, + {0x37b1, 0xab}, + {0x37b3, 0x82}, + {0x37b4, 0x00}, + {0x37b5, 0xe4}, + {0x37b6, 0x01}, + {0x37b7, 0xee}, + {0x3803, 0x70}, + {0x3807, 0x0f}, + {0x3808, 0x0f}, + {0x3809, 0x10}, + {0x380a, 0x08}, + {0x380b, 0x80}, + {0x380d, 0xa0}, + {0x380e, 0x12}, + {0x380f, 0x98}, + {0x3811, 0x08}, + {0x3813, 0x10}, + {0x3820, 0x00}, + {0x3821, 0x04}, + {0x3823, 0x04}, + {0x384d, 0xa0}, + {0x3894, 0x00}, + {0x400b, 0x08}, + {0x400d, 0x08}, + {0x4016, 0x2d}, + {0x4501, 0x00}, + {0x4542, 0x00}, + {0x4837, 0x0a}, + {0x4850, 0x47}, + {0x3a20, 0x00}, + {0x3939, 0x9d}, + {0x3902, 0x0e}, + {0x3903, 0x0e}, + {0x3904, 0x0e}, + {0x3905, 0x0e}, + {0x3906, 0x07}, + {0x3907, 0x0d}, + {0x3908, 0x11}, + {0x3909, 0x12}, + {0x390c, 0x33}, + {0x390d, 0x66}, + {0x390e, 0xaa}, + {0x3915, 0x90}, + {0x3917, 0x90}, + {0x3440, 0xa4}, + {0x3a26, 0x1d}, + {0x3a2c, 0x4a}, + {0x3a32, 0x55}, + {0x392d, 0x02}, + {0x3930, 0x08}, + {0x3933, 0x0c}, + {0x392a, 0x54}, + {0x392b, 0xa8}, + {0x3501, 0x10}, + {0x3508, 0x0f}, + {0x3509, 0x80}, + {0x3813, 0x0f}, +}; + +/* OV08X 4C1stg 1928x1088_DPHY1500M-2L 30fps */ +static const struct ov08x40_reg mode_1928x1088_regs_1500mbps[] = { + {0x5000, 0x55}, + {0x5001, 0x00}, + {0x3012, 0x21}, + {0x3400, 0x30}, + {0x3419, 0x08}, + {0x341a, 0x4f}, + {0x3426, 0x00}, + {0x3501, 0x02}, + {0x3502, 0x00}, + {0x3508, 0x01}, + {0x3509, 0x00}, + {0x3541, 0x01}, + {0x3542, 0x00}, + {0x3548, 0x01}, + {0x3712, 0x50}, + {0x3714, 0x21}, + {0x3761, 0x28}, + {0x376e, 0x07}, + {0x37b0, 0x01}, + {0x37b1, 0x0f}, + {0x37b3, 0xd6}, + {0x37b4, 0x01}, + {0x37b5, 0x48}, + {0x37b6, 0x02}, + {0x37b7, 0x40}, + {0x3803, 0x78}, + {0x3807, 0x07}, + {0x3808, 0x07}, + {0x3809, 0x88}, + {0x380a, 0x04}, + {0x380b, 0x40}, + {0x380d, 0xf0}, + {0x380e, 0x08}, + {0x380f, 0x4e}, + {0x3811, 0x04}, + {0x3813, 0x03}, + {0x3820, 0x02}, + {0x3821, 0x14}, + {0x3823, 0x84}, + {0x384d, 0xf0}, + {0x3894, 0x03}, + {0x400b, 0x04}, + {0x400d, 0x04}, + {0x4016, 0x27}, + {0x4501, 0x10}, + {0x4542, 0x01}, + {0x4837, 0x0a}, + {0x4850, 0x47}, + {0x4911, 0x00}, + {0x4919, 0x00}, + {0x491a, 0x40}, + {0x4920, 0x04}, + {0x4921, 0x00}, + {0x4922, 0x04}, + {0x4923, 0x00}, + {0x4924, 0x04}, + {0x4925, 0x00}, + {0x4926, 0x04}, + {0x4927, 0x00}, + {0x4930, 0x00}, + {0x4931, 0x00}, + {0x4932, 0x00}, + {0x4933, 0x00}, + {0x4934, 0x00}, + {0x4935, 0x00}, + {0x4936, 0x00}, + {0x4937, 0x00}, + {0x4940, 0x00}, + {0x4941, 0x80}, + {0x4942, 0x00}, + {0x4943, 0x80}, + {0x4944, 0x00}, + {0x4945, 0x80}, + {0x4946, 0x00}, + {0x4947, 0x80}, + {0x4960, 0x00}, + {0x4961, 0x00}, + {0x4962, 0x00}, + {0x4963, 0x00}, + {0x4964, 0x00}, + {0x4965, 0x00}, + {0x4966, 0x00}, + {0x4967, 0x00}, + {0x4968, 0x00}, + {0x4969, 0x00}, + {0x496a, 0x00}, + {0x496b, 0x00}, + {0x496c, 0x00}, + {0x496d, 0x00}, + {0x496e, 0x00}, + {0x496f, 0x00}, + {0x4970, 0x00}, + {0x4971, 0x00}, + {0x4972, 0x00}, + {0x4973, 0x00}, + {0x4974, 0x00}, + {0x4975, 0x00}, + {0x4976, 0x00}, + {0x4977, 0x00}, + {0x4978, 0x00}, + {0x4979, 0x00}, + {0x497a, 0x00}, + {0x497b, 0x00}, + {0x497c, 0x00}, + {0x497d, 0x00}, + {0x497e, 0x00}, + {0x497f, 0x00}, + {0x49e0, 0x00}, + {0x49e1, 0x00}, + {0x49e2, 0x00}, + {0x49e3, 0x00}, + {0x49e4, 0x00}, + {0x49e5, 0x00}, + {0x49e6, 0x00}, + {0x49e7, 0x00}, + {0x49e8, 0x00}, + {0x49e9, 0x80}, + {0x49ea, 0x00}, + {0x49eb, 0x80}, + {0x49ec, 0x00}, + {0x49ed, 0x80}, + {0x49ee, 0x00}, + {0x49ef, 0x80}, + {0x49f0, 0x02}, + {0x49f1, 0x04}, + {0x3a20, 0x05}, + {0x3939, 0x6b}, + {0x3902, 0x10}, + {0x3903, 0x10}, + {0x3904, 0x10}, + {0x3905, 0x10}, + {0x3906, 0x01}, + {0x3907, 0x0b}, + {0x3908, 0x10}, + {0x3909, 0x13}, + {0x390b, 0x11}, + {0x390c, 0x21}, + {0x390d, 0x32}, + {0x390e, 0x76}, + {0x3a1a, 0x1c}, + {0x3a26, 0x17}, + {0x3a2c, 0x50}, + {0x3a32, 0x4f}, + {0x3ace, 0x01}, + {0x3ad2, 0x01}, + {0x3ad6, 0x01}, + {0x3ada, 0x01}, + {0x3ade, 0x01}, + {0x3ae2, 0x01}, + {0x3aee, 0x01}, + {0x3af2, 0x01}, + {0x3af6, 0x01}, + {0x3afa, 0x01}, + {0x3afe, 0x01}, + {0x3b02, 0x01}, + {0x3b06, 0x01}, + {0x392d, 0x01}, + {0x3930, 0x09}, + {0x3933, 0x0d}, + {0x392a, 0x52}, + {0x392b, 0xa3}, + {0x340b, 0x1b}, + {0x3501, 0x01}, + {0x3508, 0x0f}, + {0x3509, 0x00}, + {0x3541, 0x00}, + {0x3542, 0x80}, + {0x3548, 0x0f}, + {0x3813, 0x03}, +}; + +static const struct ov08x40_reg mode_3856x2416_regs[] = { + {0x5000, 0x5d}, + {0x5001, 0x20}, + {0x3012, 0x41}, + {0x3400, 0x1c}, + {0x3419, 0x13}, + {0x341a, 0x89}, + {0x3426, 0x00}, + {0x3501, 0x02}, + {0x3502, 0x00}, + {0x3508, 0x01}, + {0x3509, 0x00}, + {0x3541, 0x01}, + {0x3542, 0x00}, + {0x3548, 0x01}, + {0x3712, 0x51}, + {0x3714, 0x24}, + {0x3761, 0x17}, + {0x376e, 0x03}, + {0x37b0, 0x00}, + {0x37b1, 0xab}, + {0x37b3, 0x82}, + {0x37b4, 0x00}, + {0x37b5, 0xe4}, + {0x37b6, 0x01}, + {0x37b7, 0xee}, + {0x3803, 0x00}, + {0x3807, 0x7f}, + {0x3808, 0x0f}, + {0x3809, 0x10}, + {0x380a, 0x09}, + {0x380b, 0x70}, + {0x380d, 0x80}, + {0x380e, 0x13}, + {0x380f, 0x88}, + {0x3811, 0x08}, + {0x3813, 0x07}, + {0x3820, 0x00}, + {0x3821, 0x04}, + {0x3823, 0x04}, + {0x384d, 0x80}, + {0x3894, 0x00}, + {0x400b, 0x08}, + {0x400d, 0x08}, + {0x4016, 0x2d}, + {0x4501, 0x00}, + {0x4542, 0x00}, + {0x4837, 0x14}, + {0x4850, 0x42}, + {0x3a20, 0x00}, + {0x3939, 0x9d}, + {0x3902, 0x0e}, + {0x3903, 0x0e}, + {0x3904, 0x0e}, + {0x3905, 0x0e}, + {0x3906, 0x07}, + {0x3907, 0x0d}, + {0x3908, 0x11}, + {0x3909, 0x12}, + {0x390c, 0x33}, + {0x390d, 0x66}, + {0x390e, 0xaa}, + {0x3915, 0x90}, + {0x3917, 0x90}, + {0x3440, 0xa4}, + {0x3a26, 0x1d}, + {0x3a2c, 0x4a}, + {0x3a32, 0x55}, + {0x392d, 0x02}, + {0x3930, 0x08}, + {0x3933, 0x0c}, + {0x392a, 0x54}, + {0x392b, 0xa8}, + {0x3501, 0x10}, + {0x3508, 0x0f}, + {0x3509, 0x80}, +}; + +static const struct ov08x40_reg mode_1928x1208_regs[] = { + {0x5000, 0x55}, + {0x5001, 0x00}, {0x3012, 0x41}, - {0x3015, 0x00}, - {0x3016, 0xb0}, - {0x3017, 0xf0}, - {0x3018, 0xf0}, - {0x3019, 0xd2}, - {0x301a, 0xb0}, - {0x301c, 0x81}, - {0x301d, 0x02}, - {0x301e, 0x80}, - {0x3022, 0xf0}, - {0x3025, 0x89}, - {0x3030, 0x03}, - {0x3044, 0xc2}, - {0x3050, 0x35}, - {0x3051, 0x60}, - {0x3052, 0x25}, - {0x3053, 0x00}, - {0x3054, 0x00}, - {0x3055, 0x02}, - {0x3056, 0x80}, - {0x3057, 0x80}, - {0x3058, 0x80}, - {0x3059, 0x00}, - {0x3107, 0x86}, {0x3400, 0x1c}, - {0x3401, 0x80}, - {0x3402, 0x8c}, {0x3419, 0x08}, {0x341a, 0xaf}, - {0x341b, 0x30}, - {0x3420, 0x00}, - {0x3421, 0x00}, - {0x3422, 0x00}, - {0x3423, 0x00}, - {0x3424, 0x00}, - {0x3425, 0x00}, {0x3426, 0x00}, - {0x3427, 0x00}, - {0x3428, 0x0f}, - {0x3429, 0x00}, - {0x342a, 0x00}, - {0x342b, 0x00}, - {0x342c, 0x00}, - {0x342d, 0x00}, - {0x342e, 0x00}, - {0x342f, 0x11}, - {0x3430, 0x11}, - {0x3431, 0x10}, - {0x3432, 0x00}, - {0x3433, 0x00}, - {0x3434, 0x00}, - {0x3435, 0x00}, - {0x3436, 0x00}, - {0x3437, 0x00}, - {0x3442, 0x02}, - {0x3443, 0x02}, - {0x3444, 0x07}, - {0x3450, 0x00}, - {0x3451, 0x00}, - {0x3452, 0x18}, - {0x3453, 0x18}, - {0x3454, 0x00}, - {0x3455, 0x80}, - {0x3456, 0x08}, - {0x3500, 0x00}, {0x3501, 0x02}, {0x3502, 0x00}, - {0x3504, 0x4c}, - {0x3506, 0x30}, - {0x3507, 0x00}, {0x3508, 0x01}, {0x3509, 0x00}, - {0x350a, 0x01}, - {0x350b, 0x00}, - {0x350c, 0x00}, - {0x3540, 0x00}, {0x3541, 0x01}, {0x3542, 0x00}, - {0x3544, 0x4c}, - {0x3546, 0x30}, - {0x3547, 0x00}, {0x3548, 0x01}, - {0x3549, 0x00}, - {0x354a, 0x01}, - {0x354b, 0x00}, - {0x354c, 0x00}, - {0x3688, 0x02}, - {0x368a, 0x2e}, - {0x368e, 0x71}, - {0x3696, 0xd1}, - {0x3699, 0x00}, - {0x369a, 0x00}, - {0x36a4, 0x00}, - {0x36a6, 0x00}, - {0x3711, 0x00}, {0x3712, 0x50}, - {0x3713, 0x00}, {0x3714, 0x21}, - {0x3716, 0x00}, - {0x3718, 0x07}, - {0x371a, 0x1c}, - {0x371b, 0x00}, - {0x3720, 0x08}, - {0x3725, 0x32}, - {0x3727, 0x05}, - {0x3760, 0x02}, {0x3761, 0x28}, - {0x3762, 0x02}, - {0x3763, 0x02}, - {0x3764, 0x02}, - {0x3765, 0x2c}, - {0x3766, 0x04}, - {0x3767, 0x2c}, - {0x3768, 0x02}, - {0x3769, 0x00}, - {0x376b, 0x20}, {0x376e, 0x07}, {0x37b0, 0x01}, {0x37b1, 0x0f}, - {0x37b2, 0x01}, {0x37b3, 0xd6}, {0x37b4, 0x01}, {0x37b5, 0x48}, {0x37b6, 0x02}, {0x37b7, 0x40}, - {0x3800, 0x00}, - {0x3801, 0x00}, - {0x3802, 0x00}, {0x3803, 0x00}, - {0x3804, 0x0f}, - {0x3805, 0x1f}, - {0x3806, 0x09}, {0x3807, 0x7f}, {0x3808, 0x07}, {0x3809, 0x88}, {0x380a, 0x04}, {0x380b, 0xb8}, - {0x380c, 0x02}, {0x380d, 0xd0}, {0x380e, 0x11}, {0x380f, 0x5c}, - {0x3810, 0x00}, {0x3811, 0x04}, - {0x3812, 0x00}, {0x3813, 0x03}, - {0x3814, 0x11}, - {0x3815, 0x11}, {0x3820, 0x02}, {0x3821, 0x14}, - {0x3822, 0x00}, {0x3823, 0x04}, - {0x3828, 0x0f}, - {0x382a, 0x80}, - {0x382e, 0x41}, - {0x3837, 0x08}, - {0x383a, 0x81}, - {0x383b, 0x81}, - {0x383c, 0x11}, - {0x383d, 0x11}, - {0x383e, 0x00}, - {0x383f, 0x38}, - {0x3840, 0x00}, - {0x3847, 0x00}, - {0x384a, 0x00}, - {0x384c, 0x02}, {0x384d, 0xd0}, - {0x3856, 0x50}, - {0x3857, 0x30}, - {0x3858, 0x80}, - {0x3859, 0x40}, - {0x3860, 0x00}, - {0x3888, 0x00}, - {0x3889, 0x00}, - {0x388a, 0x00}, - {0x388b, 0x00}, - {0x388c, 0x00}, - {0x388d, 0x00}, - {0x388e, 0x00}, - {0x388f, 0x00}, {0x3894, 0x00}, - {0x3895, 0x00}, - {0x3c84, 0x00}, - {0x3d85, 0x8b}, - {0x3daa, 0x80}, - {0x3dab, 0x14}, - {0x3dac, 0x80}, - {0x3dad, 0xc8}, - {0x3dae, 0x81}, - {0x3daf, 0x7b}, - {0x3f00, 0x10}, - {0x3f01, 0x11}, - {0x3f06, 0x0d}, - {0x3f07, 0x0b}, - {0x3f08, 0x0d}, - {0x3f09, 0x0b}, - {0x3f0a, 0x01}, - {0x3f0b, 0x11}, - {0x3f0c, 0x33}, - {0x4001, 0x07}, - {0x4007, 0x20}, - {0x4008, 0x00}, - {0x4009, 0x05}, - {0x400a, 0x00}, {0x400b, 0x04}, - {0x400c, 0x00}, {0x400d, 0x04}, - {0x400e, 0x14}, - {0x4010, 0xf4}, - {0x4011, 0x03}, - {0x4012, 0x55}, - {0x4015, 0x00}, {0x4016, 0x27}, - {0x4017, 0x00}, - {0x4018, 0x0f}, - {0x401b, 0x08}, - {0x401c, 0x00}, - {0x401d, 0x10}, - {0x401e, 0x02}, - {0x401f, 0x00}, - {0x4050, 0x06}, - {0x4051, 0xff}, - {0x4052, 0xff}, - {0x4053, 0xff}, - {0x4054, 0xff}, - {0x4055, 0xff}, - {0x4056, 0xff}, - {0x4057, 0x7f}, - {0x4058, 0x00}, - {0x4059, 0x00}, - {0x405a, 0x00}, - {0x405b, 0x00}, - {0x405c, 0x07}, - {0x405d, 0xff}, - {0x405e, 0x07}, - {0x405f, 0xff}, - {0x4080, 0x78}, - {0x4081, 0x78}, - {0x4082, 0x78}, - {0x4083, 0x78}, - {0x4019, 0x00}, - {0x401a, 0x40}, - {0x4020, 0x04}, - {0x4021, 0x00}, - {0x4022, 0x04}, - {0x4023, 0x00}, - {0x4024, 0x04}, - {0x4025, 0x00}, - {0x4026, 0x04}, - {0x4027, 0x00}, - {0x4030, 0x00}, - {0x4031, 0x00}, - {0x4032, 0x00}, - {0x4033, 0x00}, - {0x4034, 0x00}, - {0x4035, 0x00}, - {0x4036, 0x00}, - {0x4037, 0x00}, - {0x4040, 0x00}, - {0x4041, 0x80}, - {0x4042, 0x00}, - {0x4043, 0x80}, - {0x4044, 0x00}, - {0x4045, 0x80}, - {0x4046, 0x00}, - {0x4047, 0x80}, - {0x4060, 0x00}, - {0x4061, 0x00}, - {0x4062, 0x00}, - {0x4063, 0x00}, - {0x4064, 0x00}, - {0x4065, 0x00}, - {0x4066, 0x00}, - {0x4067, 0x00}, - {0x4068, 0x00}, - {0x4069, 0x00}, - {0x406a, 0x00}, - {0x406b, 0x00}, - {0x406c, 0x00}, - {0x406d, 0x00}, - {0x406e, 0x00}, - {0x406f, 0x00}, - {0x4070, 0x00}, - {0x4071, 0x00}, - {0x4072, 0x00}, - {0x4073, 0x00}, - {0x4074, 0x00}, - {0x4075, 0x00}, - {0x4076, 0x00}, - {0x4077, 0x00}, - {0x4078, 0x00}, - {0x4079, 0x00}, - {0x407a, 0x00}, - {0x407b, 0x00}, - {0x407c, 0x00}, - {0x407d, 0x00}, - {0x407e, 0x00}, - {0x407f, 0x00}, - {0x40e0, 0x00}, - {0x40e1, 0x00}, - {0x40e2, 0x00}, - {0x40e3, 0x00}, - {0x40e4, 0x00}, - {0x40e5, 0x00}, - {0x40e6, 0x00}, - {0x40e7, 0x00}, - {0x40e8, 0x00}, - {0x40e9, 0x80}, - {0x40ea, 0x00}, - {0x40eb, 0x80}, - {0x40ec, 0x00}, - {0x40ed, 0x80}, - {0x40ee, 0x00}, - {0x40ef, 0x80}, - {0x40f0, 0x02}, - {0x40f1, 0x04}, - {0x4300, 0x00}, - {0x4301, 0x00}, - {0x4302, 0x00}, - {0x4303, 0x00}, - {0x4304, 0x00}, - {0x4305, 0x00}, - {0x4306, 0x00}, - {0x4307, 0x00}, - {0x4308, 0x00}, - {0x4309, 0x00}, - {0x430a, 0x00}, - {0x430b, 0xff}, - {0x430c, 0xff}, - {0x430d, 0x00}, - {0x430e, 0x00}, - {0x4315, 0x00}, - {0x4316, 0x00}, - {0x4317, 0x00}, - {0x4318, 0x00}, - {0x4319, 0x00}, - {0x431a, 0x00}, - {0x431b, 0x00}, - {0x431c, 0x00}, - {0x4500, 0x07}, {0x4501, 0x10}, - {0x4502, 0x00}, - {0x4503, 0x0f}, - {0x4504, 0x80}, - {0x4506, 0x01}, - {0x4509, 0x05}, - {0x450c, 0x00}, - {0x450d, 0x20}, - {0x450e, 0x00}, - {0x450f, 0x00}, - {0x4510, 0x00}, - {0x4523, 0x00}, - {0x4526, 0x00}, {0x4542, 0x00}, - {0x4543, 0x00}, - {0x4544, 0x00}, - {0x4545, 0x00}, - {0x4546, 0x00}, - {0x4547, 0x10}, - {0x4602, 0x00}, - {0x4603, 0x15}, - {0x460b, 0x07}, - {0x4680, 0x11}, - {0x4686, 0x00}, - {0x4687, 0x00}, - {0x4700, 0x00}, - {0x4800, 0x64}, - {0x4806, 0x40}, - {0x480b, 0x10}, - {0x480c, 0x80}, - {0x480f, 0x32}, - {0x4813, 0xe4}, {0x4837, 0x14}, {0x4850, 0x42}, - {0x4884, 0x04}, - {0x4c00, 0xf8}, - {0x4c01, 0x44}, - {0x4c03, 0x00}, - {0x4d00, 0x00}, - {0x4d01, 0x16}, - {0x4d04, 0x10}, - {0x4d05, 0x00}, - {0x4d06, 0x0c}, - {0x4d07, 0x00}, - {0x3d84, 0x04}, - {0x3680, 0xa4}, - {0x3682, 0x80}, - {0x3601, 0x40}, - {0x3602, 0x90}, - {0x3608, 0x0a}, - {0x3938, 0x09}, - {0x3a74, 0x84}, - {0x3a99, 0x84}, - {0x3ab9, 0xa6}, - {0x3aba, 0xba}, - {0x3b12, 0x84}, - {0x3b14, 0xbb}, - {0x3b15, 0xbf}, - {0x3a29, 0x26}, - {0x3a1f, 0x8a}, - {0x3a22, 0x91}, - {0x3a25, 0x96}, - {0x3a28, 0xb4}, - {0x3a2b, 0xba}, - {0x3a2e, 0xbf}, - {0x3a31, 0xc1}, {0x3a20, 0x05}, {0x3939, 0x6b}, {0x3902, 0x10}, @@ -1148,22 +1138,13 @@ static const struct ov08x40_reg mode_1928x1208_regs[] = { {0x3907, 0x0b}, {0x3908, 0x10}, {0x3909, 0x13}, - {0x360f, 0x99}, {0x390b, 0x11}, {0x390c, 0x21}, {0x390d, 0x32}, {0x390e, 0x76}, - {0x3911, 0x90}, - {0x3913, 0x90}, - {0x3b3f, 0x9d}, - {0x3b45, 0x9d}, - {0x3b1b, 0xc9}, - {0x3b21, 0xc9}, {0x3a1a, 0x1c}, - {0x3a23, 0x15}, {0x3a26, 0x17}, {0x3a2c, 0x50}, - {0x3a2f, 0x18}, {0x3a32, 0x4f}, {0x3ace, 0x01}, {0x3ad2, 0x01}, @@ -1178,31 +1159,13 @@ static const struct ov08x40_reg mode_1928x1208_regs[] = { {0x3afe, 0x01}, {0x3b02, 0x01}, {0x3b06, 0x01}, - {0x3b0a, 0x01}, - {0x3b0b, 0x00}, - {0x3b0e, 0x01}, - {0x3b0f, 0x00}, - {0x392c, 0x02}, {0x392d, 0x01}, - {0x392e, 0x04}, - {0x392f, 0x03}, {0x3930, 0x09}, - {0x3931, 0x07}, - {0x3932, 0x10}, {0x3933, 0x0d}, - {0x3609, 0x08}, - {0x3921, 0x0f}, - {0x3928, 0x15}, - {0x3929, 0x2a}, {0x392a, 0x52}, {0x392b, 0xa3}, {0x340b, 0x1b}, - {0x3426, 0x10}, - {0x3407, 0x01}, - {0x3404, 0x01}, - {0x3500, 0x00}, {0x3501, 0x08}, - {0x3502, 0x10}, {0x3508, 0x04}, {0x3509, 0x00}, }; @@ -1217,6 +1180,7 @@ static const char * const ov08x40_test_pattern_menu[] = { /* Configurations for supported link frequencies */ #define OV08X40_LINK_FREQ_400MHZ 400000000ULL +#define OV08X40_LINK_FREQ_749MHZ 749000000ULL #define OV08X40_SCLK_96MHZ 96000000ULL #define OV08X40_XVCLK 19200000 #define OV08X40_DATA_LANES 4 @@ -1236,6 +1200,7 @@ static u64 link_freq_to_pixel_rate(u64 f) /* Menu items for LINK_FREQ V4L2 control */ static const s64 link_freq_menu_items[] = { OV08X40_LINK_FREQ_400MHZ, + OV08X40_LINK_FREQ_749MHZ, }; /* Link frequency configs */ @@ -1246,6 +1211,12 @@ static const struct ov08x40_link_freq_config link_freq_configs[] = { .regs = mipi_data_rate_800mbps, } }, + [OV08X40_LINK_FREQ_749MHZ_INDEX] = { + .reg_list = { + .num_of_regs = ARRAY_SIZE(mipi_data_rate_1500mbps), + .regs = mipi_data_rate_1500mbps, + } + }, }; /* Mode configs */ @@ -1266,6 +1237,22 @@ static const struct ov08x40_mode supported_modes[] = { .exposure_margin = OV08X40_EXPOSURE_MAX_MARGIN, }, { + .width = 3856, + .height = 2176, + .vts_def = OV08X40_VTS_30FPS, + .vts_min = OV08X40_VTS_30FPS, + .llp = 0x10aa, /* in normal mode, tline time = 2 * HTS / SCLK */ + .lanes = 4, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_3856x2176_regs_800mbps), + .regs = mode_3856x2176_regs_800mbps, + }, + .link_freq_index = OV08X40_LINK_FREQ_400MHZ_INDEX, + .exposure_shift = 1, + .exposure_margin = OV08X40_EXPOSURE_MAX_MARGIN, + }, + + { .width = 1928, .height = 1208, .vts_def = OV08X40_VTS_BIN_30FPS, @@ -1280,6 +1267,36 @@ static const struct ov08x40_mode supported_modes[] = { .exposure_shift = 0, .exposure_margin = OV08X40_EXPOSURE_BIN_MAX_MARGIN, }, + { + .width = 3856, + .height = 2176, + .vts_def = OV08X40_VTS_30FPS, + .vts_min = OV08X40_VTS_30FPS, + .llp = 0x10aa, /* in normal mode, tline time = 2 * HTS / SCLK */ + .lanes = 2, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_3856x2176_regs_1500mbps), + .regs = mode_3856x2176_regs_1500mbps, + }, + .link_freq_index = OV08X40_LINK_FREQ_749MHZ_INDEX, + .exposure_shift = 1, + .exposure_margin = OV08X40_EXPOSURE_MAX_MARGIN, + }, + { + .width = 1928, + .height = 1088, + .vts_def = OV08X40_VTS_BIN_30FPS, + .vts_min = OV08X40_VTS_BIN_30FPS, + .llp = 0x960, + .lanes = 2, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1928x1088_regs_1500mbps), + .regs = mode_1928x1088_regs_1500mbps, + }, + .link_freq_index = OV08X40_LINK_FREQ_749MHZ_INDEX, + .exposure_shift = 0, + .exposure_margin = OV08X40_EXPOSURE_MAX_MARGIN, + }, }; static const char * const ov08x40_supply_names[] = { @@ -1289,6 +1306,8 @@ static const char * const ov08x40_supply_names[] = { }; struct ov08x40 { + struct device *dev; + struct v4l2_subdev sd; struct media_pad pad; @@ -1310,8 +1329,13 @@ struct ov08x40 { /* Mutex for serialized access */ struct mutex mutex; + /* data lanes */ + u8 mipi_lanes; + /* True if the device has been identified */ bool identified; + + unsigned long link_freq_bitmap; }; #define to_ov08x40(_sd) container_of(_sd, struct ov08x40, sd) @@ -1322,9 +1346,6 @@ static int ov08x40_power_on(struct device *dev) struct ov08x40 *ov08x = to_ov08x40(sd); int ret; - if (is_acpi_node(dev_fwnode(dev))) - return 0; - ret = clk_prepare_enable(ov08x->xvclk); if (ret < 0) { dev_err(dev, "failed to enable xvclk\n"); @@ -1344,7 +1365,7 @@ static int ov08x40_power_on(struct device *dev) } gpiod_set_value_cansleep(ov08x->reset_gpio, 0); - usleep_range(1500, 1800); + usleep_range(5000, 5500); return 0; @@ -1360,9 +1381,6 @@ static int ov08x40_power_off(struct device *dev) struct v4l2_subdev *sd = dev_get_drvdata(dev); struct ov08x40 *ov08x = to_ov08x40(sd); - if (is_acpi_node(dev_fwnode(dev))) - return 0; - gpiod_set_value_cansleep(ov08x->reset_gpio, 1); regulator_bulk_disable(ARRAY_SIZE(ov08x40_supply_names), ov08x->supplies); @@ -1400,7 +1418,7 @@ static int ov08x40_read_reg(struct ov08x40 *ov08x, ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); if (ret != ARRAY_SIZE(msgs)) - return -EIO; + return ret < 0 ? ret : -EIO; *val = be32_to_cpu(data_be); @@ -1469,7 +1487,7 @@ static int ov08x40_write_reg(struct ov08x40 *ov08x, u16 reg, u32 len, u32 __val) { struct i2c_client *client = v4l2_get_subdevdata(&ov08x->sd); - int buf_i, val_i; + int buf_i, val_i, ret; u8 buf[6], *val_p; __be32 val; @@ -1487,8 +1505,9 @@ static int ov08x40_write_reg(struct ov08x40 *ov08x, while (val_i < 4) buf[buf_i++] = val_p[val_i++]; - if (i2c_master_send(client, buf, len + 2) != len + 2) - return -EIO; + ret = i2c_master_send(client, buf, len + 2); + if (ret != len + 2) + return ret < 0 ? ret : -EIO; return 0; } @@ -1497,7 +1516,6 @@ static int ov08x40_write_reg(struct ov08x40 *ov08x, static int ov08x40_write_regs(struct ov08x40 *ov08x, const struct ov08x40_reg *regs, u32 len) { - struct i2c_client *client = v4l2_get_subdevdata(&ov08x->sd); int ret; u32 i; @@ -1506,7 +1524,7 @@ static int ov08x40_write_regs(struct ov08x40 *ov08x, regs[i].val); if (ret) { - dev_err_ratelimited(&client->dev, + dev_err_ratelimited(ov08x->dev, "Failed to write reg 0x%4.4x. error = %d\n", regs[i].address, ret); @@ -1632,7 +1650,7 @@ static int ov08x40_set_ctrl_hflip(struct ov08x40 *ov08x, u32 ctrl_val) return ov08x40_write_reg(ov08x, OV08X40_REG_MIRROR, OV08X40_REG_VALUE_08BIT, - ctrl_val ? val | BIT(2) : val & ~BIT(2)); + ctrl_val ? val & ~BIT(2) : val | BIT(2)); } static int ov08x40_set_ctrl_vflip(struct ov08x40 *ov08x, u32 ctrl_val) @@ -1654,7 +1672,6 @@ static int ov08x40_set_ctrl(struct v4l2_ctrl *ctrl) { struct ov08x40 *ov08x = container_of(ctrl->handler, struct ov08x40, ctrl_handler); - struct i2c_client *client = v4l2_get_subdevdata(&ov08x->sd); s64 max; int exp; int fll; @@ -1683,7 +1700,7 @@ static int ov08x40_set_ctrl(struct v4l2_ctrl *ctrl) * Applying V4L2 control value only happens * when power is up for streaming */ - if (!pm_runtime_get_if_in_use(&client->dev)) + if (!pm_runtime_get_if_in_use(ov08x->dev)) return 0; switch (ctrl->id) { @@ -1721,17 +1738,26 @@ static int ov08x40_set_ctrl(struct v4l2_ctrl *ctrl) ov08x40_set_ctrl_vflip(ov08x, ctrl->val); break; default: - dev_info(&client->dev, + dev_info(ov08x->dev, "ctrl(id:0x%x,val:0x%x) is not handled\n", ctrl->id, ctrl->val); break; } - pm_runtime_put(&client->dev); + pm_runtime_put(ov08x->dev); return ret; } +static bool filter_by_mipi_lanes(const void *array, size_t index, + const void *context) +{ + const struct ov08x40_mode *mode = array; + const struct ov08x40 *ov08x = context; + + return mode->lanes == ov08x->mipi_lanes; +} + static const struct v4l2_ctrl_ops ov08x40_ctrl_ops = { .s_ctrl = ov08x40_set_ctrl, }; @@ -1753,18 +1779,28 @@ static int ov08x40_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { - if (fse->index >= ARRAY_SIZE(supported_modes)) - return -EINVAL; + struct ov08x40 *ov08x = to_ov08x40(sd); + size_t i, count = 0; 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; + for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { + if (!filter_by_mipi_lanes(&supported_modes[i], i, ov08x)) + continue; - return 0; + if (count == fse->index) { + fse->min_width = supported_modes[i].width; + fse->max_width = fse->min_width; + fse->min_height = supported_modes[i].height; + fse->max_height = fse->min_height; + return 0; + } + + count++; + } + + return -EINVAL; } static void ov08x40_update_pad_format(const struct ov08x40_mode *mode, @@ -1827,10 +1863,13 @@ ov08x40_set_pad_format(struct v4l2_subdev *sd, if (fmt->format.code != MEDIA_BUS_FMT_SGRBG10_1X10) 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); + mode = v4l2_find_nearest_size_conditional(supported_modes, + ARRAY_SIZE(supported_modes), + width, height, + fmt->format.width, + fmt->format.height, + filter_by_mipi_lanes, + ov08x); ov08x40_update_pad_format(mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); @@ -1874,7 +1913,6 @@ ov08x40_set_pad_format(struct v4l2_subdev *sd, static int ov08x40_start_streaming(struct ov08x40 *ov08x) { - struct i2c_client *client = v4l2_get_subdevdata(&ov08x->sd); const struct ov08x40_reg_list *reg_list; int ret, link_freq_index; @@ -1882,7 +1920,7 @@ static int ov08x40_start_streaming(struct ov08x40 *ov08x) ret = ov08x40_write_reg(ov08x, OV08X40_REG_SOFTWARE_RST, OV08X40_REG_VALUE_08BIT, OV08X40_SOFTWARE_RST); if (ret) { - dev_err(&client->dev, "%s failed to set powerup registers\n", + dev_err(ov08x->dev, "%s failed to set powerup registers\n", __func__); return ret; } @@ -1892,7 +1930,15 @@ static int ov08x40_start_streaming(struct ov08x40 *ov08x) ret = ov08x40_write_reg_list(ov08x, reg_list); if (ret) { - dev_err(&client->dev, "%s failed to set plls\n", __func__); + dev_err(ov08x->dev, "%s failed to set plls\n", __func__); + return ret; + } + + reg_list = &ov08x40_global_setting; + ret = ov08x40_write_reg_list(ov08x, reg_list); + if (ret) { + dev_err(ov08x->dev, "%s failed to set global setting\n", + __func__); return ret; } @@ -1900,7 +1946,7 @@ static int ov08x40_start_streaming(struct ov08x40 *ov08x) reg_list = &ov08x->cur_mode->reg_list; ret = ov08x40_write_reg_list(ov08x, reg_list); if (ret) { - dev_err(&client->dev, "%s failed to set mode\n", __func__); + dev_err(ov08x->dev, "%s failed to set mode\n", __func__); return ret; } @@ -1916,7 +1962,7 @@ static int ov08x40_start_streaming(struct ov08x40 *ov08x) } if (ret) { - dev_err(&client->dev, "%s failed to set regs\n", __func__); + dev_err(ov08x->dev, "%s failed to set regs\n", __func__); return ret; } @@ -1937,19 +1983,50 @@ static int ov08x40_stop_streaming(struct ov08x40 *ov08x) OV08X40_REG_VALUE_08BIT, OV08X40_MODE_STANDBY); } +/* Verify chip ID */ +static int ov08x40_identify_module(struct ov08x40 *ov08x) +{ + int ret; + u32 val; + + if (ov08x->identified) + return 0; + + ret = ov08x40_read_reg(ov08x, OV08X40_REG_CHIP_ID, + OV08X40_REG_VALUE_24BIT, &val); + if (ret) { + dev_err(ov08x->dev, "error reading chip-id register: %d\n", ret); + return ret; + } + + if (val != OV08X40_CHIP_ID) { + dev_err(ov08x->dev, "chip id mismatch: %x!=%x\n", + OV08X40_CHIP_ID, val); + return -ENXIO; + } + + dev_dbg(ov08x->dev, "chip id 0x%x\n", val); + ov08x->identified = true; + + return 0; +} + static int ov08x40_set_stream(struct v4l2_subdev *sd, int enable) { struct ov08x40 *ov08x = to_ov08x40(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); int ret = 0; mutex_lock(&ov08x->mutex); if (enable) { - ret = pm_runtime_resume_and_get(&client->dev); + ret = pm_runtime_resume_and_get(ov08x->dev); if (ret < 0) goto err_unlock; + ret = ov08x40_identify_module(ov08x); + if (ret) + goto err_rpm_put; + /* * Apply default & customized values * and then start streaming. @@ -1959,7 +2036,7 @@ static int ov08x40_set_stream(struct v4l2_subdev *sd, int enable) goto err_rpm_put; } else { ov08x40_stop_streaming(ov08x); - pm_runtime_put(&client->dev); + pm_runtime_put(ov08x->dev); } mutex_unlock(&ov08x->mutex); @@ -1967,39 +2044,13 @@ static int ov08x40_set_stream(struct v4l2_subdev *sd, int enable) return ret; err_rpm_put: - pm_runtime_put(&client->dev); + pm_runtime_put(ov08x->dev); err_unlock: mutex_unlock(&ov08x->mutex); return ret; } -/* Verify chip ID */ -static int ov08x40_identify_module(struct ov08x40 *ov08x) -{ - struct i2c_client *client = v4l2_get_subdevdata(&ov08x->sd); - int ret; - u32 val; - - if (ov08x->identified) - return 0; - - ret = ov08x40_read_reg(ov08x, OV08X40_REG_CHIP_ID, - OV08X40_REG_VALUE_24BIT, &val); - if (ret) - return ret; - - if (val != OV08X40_CHIP_ID) { - dev_err(&client->dev, "chip id mismatch: %x!=%x\n", - OV08X40_CHIP_ID, val); - return -ENXIO; - } - - ov08x->identified = true; - - return 0; -} - static const struct v4l2_subdev_video_ops ov08x40_video_ops = { .s_stream = ov08x40_set_stream, }; @@ -2026,7 +2077,6 @@ static const struct v4l2_subdev_internal_ops ov08x40_internal_ops = { static int ov08x40_init_controls(struct ov08x40 *ov08x) { - struct i2c_client *client = v4l2_get_subdevdata(&ov08x->sd); struct v4l2_fwnode_device_properties props; struct v4l2_ctrl_handler *ctrl_hdlr; s64 exposure_max; @@ -2036,7 +2086,6 @@ static int ov08x40_init_controls(struct ov08x40 *ov08x) s64 pixel_rate_min; s64 pixel_rate_max; const struct ov08x40_mode *mode; - u32 max; int ret; ctrl_hdlr = &ov08x->ctrl_handler; @@ -2046,12 +2095,11 @@ static int ov08x40_init_controls(struct ov08x40 *ov08x) mutex_init(&ov08x->mutex); ctrl_hdlr->lock = &ov08x->mutex; - max = ARRAY_SIZE(link_freq_menu_items) - 1; ov08x->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &ov08x40_ctrl_ops, V4L2_CID_LINK_FREQ, - max, - 0, + __fls(ov08x->link_freq_bitmap), + __ffs(ov08x->link_freq_bitmap), link_freq_menu_items); if (ov08x->link_freq) ov08x->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; @@ -2109,12 +2157,12 @@ static int ov08x40_init_controls(struct ov08x40 *ov08x) if (ctrl_hdlr->error) { ret = ctrl_hdlr->error; - dev_err(&client->dev, "%s control init failed (%d)\n", + dev_err(ov08x->dev, "%s control init failed (%d)\n", __func__, ret); goto error; } - ret = v4l2_fwnode_device_parse(&client->dev, &props); + ret = v4l2_fwnode_device_parse(ov08x->dev, &props); if (ret) goto error; @@ -2140,94 +2188,85 @@ static void ov08x40_free_controls(struct ov08x40 *ov08x) mutex_destroy(&ov08x->mutex); } -static int ov08x40_check_hwcfg(struct ov08x40 *ov08x, struct device *dev) +static int ov08x40_check_hwcfg(struct ov08x40 *ov08x) { struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = V4L2_MBUS_CSI2_DPHY }; + struct device *dev = ov08x->dev; struct fwnode_handle *ep; struct fwnode_handle *fwnode = dev_fwnode(dev); - unsigned int i, j; + unsigned int i; int ret; u32 xvclk_rate; - if (!fwnode) - return -ENXIO; + /* + * Sometimes the fwnode graph is initialized by the bridge driver. + * Bridge drivers doing this also add sensor properties, wait for this. + */ + ep = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!ep) + return dev_err_probe(dev, -EPROBE_DEFER, + "waiting for fwnode graph endpoint\n"); - if (!is_acpi_node(fwnode)) { - ov08x->xvclk = devm_clk_get(dev, NULL); - if (IS_ERR(ov08x->xvclk)) { - dev_err(dev, "could not get xvclk clock (%pe)\n", - ov08x->xvclk); - return PTR_ERR(ov08x->xvclk); - } + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + fwnode_handle_put(ep); + if (ret) + return dev_err_probe(dev, ret, "parsing endpoint failed\n"); - xvclk_rate = clk_get_rate(ov08x->xvclk); + ov08x->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(ov08x->reset_gpio)) { + ret = dev_err_probe(dev, PTR_ERR(ov08x->reset_gpio), + "getting reset GPIO\n"); + goto out_err; + } - ov08x->reset_gpio = devm_gpiod_get_optional(dev, "reset", - GPIOD_OUT_LOW); - if (IS_ERR(ov08x->reset_gpio)) - return PTR_ERR(ov08x->reset_gpio); + for (i = 0; i < ARRAY_SIZE(ov08x40_supply_names); i++) + ov08x->supplies[i].supply = ov08x40_supply_names[i]; - for (i = 0; i < ARRAY_SIZE(ov08x40_supply_names); i++) - ov08x->supplies[i].supply = ov08x40_supply_names[i]; + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ov08x40_supply_names), + ov08x->supplies); + if (ret) + goto out_err; - ret = devm_regulator_bulk_get(dev, - ARRAY_SIZE(ov08x40_supply_names), - ov08x->supplies); - if (ret) - return ret; - } else { - ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", - &xvclk_rate); - if (ret) { - dev_err(dev, "can't get clock frequency"); - return ret; - } + ov08x->xvclk = devm_v4l2_sensor_clk_get(dev, NULL); + if (IS_ERR(ov08x->xvclk)) { + ret = dev_err_probe(dev, PTR_ERR(ov08x->xvclk), + "getting xvclk\n"); + goto out_err; } + xvclk_rate = clk_get_rate(ov08x->xvclk); if (xvclk_rate != OV08X40_XVCLK) { - dev_err(dev, "external clock %d is not supported", + dev_err(dev, "external clock %d is not supported\n", xvclk_rate); - return -EINVAL; + ret = -EINVAL; + goto out_err; } - ep = fwnode_graph_get_next_endpoint(fwnode, NULL); - if (!ep) - return -ENXIO; - - ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); - fwnode_handle_put(ep); - if (ret) - return ret; - - if (bus_cfg.bus.mipi_csi2.num_data_lanes != OV08X40_DATA_LANES) { - dev_err(dev, "number of CSI2 data lanes %d is not supported", + switch (bus_cfg.bus.mipi_csi2.num_data_lanes) { + case 2: + case 4: + ov08x->mipi_lanes = bus_cfg.bus.mipi_csi2.num_data_lanes; + break; + default: + dev_err(dev, "number of CSI2 data lanes %d is not supported\n", bus_cfg.bus.mipi_csi2.num_data_lanes); ret = -EINVAL; goto out_err; } if (!bus_cfg.nr_of_link_frequencies) { - dev_err(dev, "no link frequencies defined"); + dev_err(dev, "no link frequencies defined\n"); ret = -EINVAL; goto out_err; } - - for (i = 0; i < ARRAY_SIZE(link_freq_menu_items); i++) { - for (j = 0; j < bus_cfg.nr_of_link_frequencies; j++) { - if (link_freq_menu_items[i] == - bus_cfg.link_frequencies[j]) - break; - } - - if (j == bus_cfg.nr_of_link_frequencies) { - dev_err(dev, "no link frequency %lld supported", - link_freq_menu_items[i]); - ret = -EINVAL; - goto out_err; - } - } + ret = v4l2_link_freq_to_bitmap(dev, bus_cfg.link_frequencies, + bus_cfg.nr_of_link_frequencies, + link_freq_menu_items, + ARRAY_SIZE(link_freq_menu_items), + &ov08x->link_freq_bitmap); out_err: v4l2_fwnode_endpoint_free(&bus_cfg); @@ -2244,30 +2283,28 @@ static int ov08x40_probe(struct i2c_client *client) if (!ov08x) return -ENOMEM; + ov08x->dev = &client->dev; + /* Check HW config */ - ret = ov08x40_check_hwcfg(ov08x, &client->dev); - if (ret) { - dev_err(&client->dev, "failed to check hwcfg: %d", ret); + ret = ov08x40_check_hwcfg(ov08x); + if (ret) return ret; - } /* Initialize subdev */ v4l2_i2c_subdev_init(&ov08x->sd, client, &ov08x40_subdev_ops); - full_power = acpi_dev_state_d0(&client->dev); + full_power = acpi_dev_state_d0(ov08x->dev); if (full_power) { - ret = ov08x40_power_on(&client->dev); + ret = ov08x40_power_on(ov08x->dev); if (ret) { - dev_err(&client->dev, "failed to power on\n"); + dev_err(ov08x->dev, "failed to power on\n"); return ret; } /* Check module identity */ ret = ov08x40_identify_module(ov08x); - if (ret) { - dev_err(&client->dev, "failed to find sensor: %d\n", ret); + if (ret) goto probe_power_off; - } } /* Set default mode to max resolution */ @@ -2287,7 +2324,7 @@ static int ov08x40_probe(struct i2c_client *client) ov08x->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&ov08x->sd.entity, 1, &ov08x->pad); if (ret) { - dev_err(&client->dev, "%s failed:%d\n", __func__, ret); + dev_err(ov08x->dev, "%s failed:%d\n", __func__, ret); goto error_handler_free; } @@ -2296,9 +2333,9 @@ static int ov08x40_probe(struct i2c_client *client) goto error_media_entity; if (full_power) - pm_runtime_set_active(&client->dev); - pm_runtime_enable(&client->dev); - pm_runtime_idle(&client->dev); + pm_runtime_set_active(ov08x->dev); + pm_runtime_enable(ov08x->dev); + pm_runtime_idle(ov08x->dev); return 0; @@ -2309,7 +2346,7 @@ error_handler_free: ov08x40_free_controls(ov08x); probe_power_off: - ov08x40_power_off(&client->dev); + ov08x40_power_off(ov08x->dev); return ret; } @@ -2323,12 +2360,15 @@ static void ov08x40_remove(struct i2c_client *client) media_entity_cleanup(&sd->entity); ov08x40_free_controls(ov08x); - pm_runtime_disable(&client->dev); - pm_runtime_set_suspended(&client->dev); - - ov08x40_power_off(&client->dev); + pm_runtime_disable(ov08x->dev); + if (!pm_runtime_status_suspended(ov08x->dev)) + ov08x40_power_off(ov08x->dev); + pm_runtime_set_suspended(ov08x->dev); } +static DEFINE_RUNTIME_DEV_PM_OPS(ov08x40_pm_ops, ov08x40_power_off, + ov08x40_power_on, NULL); + #ifdef CONFIG_ACPI static const struct acpi_device_id ov08x40_acpi_ids[] = { {"OVTI08F4"}, @@ -2349,6 +2389,7 @@ static struct i2c_driver ov08x40_i2c_driver = { .name = "ov08x40", .acpi_match_table = ACPI_PTR(ov08x40_acpi_ids), .of_match_table = ov08x40_of_match, + .pm = pm_sleep_ptr(&ov08x40_pm_ops), }, .probe = ov08x40_probe, .remove = ov08x40_remove, diff --git a/drivers/media/i2c/ov13858.c b/drivers/media/i2c/ov13858.c index 7a3fc1d28514..162b49046990 100644 --- a/drivers/media/i2c/ov13858.c +++ b/drivers/media/i2c/ov13858.c @@ -2,6 +2,7 @@ // Copyright (c) 2017 Intel Corporation. #include <linux/acpi.h> +#include <linux/clk.h> #include <linux/i2c.h> #include <linux/module.h> #include <linux/pm_runtime.h> @@ -1028,6 +1029,9 @@ static const struct ov13858_mode supported_modes[] = { }; struct ov13858 { + struct device *dev; + struct clk *clk; + struct v4l2_subdev sd; struct media_pad pad; @@ -1117,7 +1121,6 @@ static int ov13858_write_reg(struct ov13858 *ov13858, u16 reg, u32 len, static int ov13858_write_regs(struct ov13858 *ov13858, const struct ov13858_reg *regs, u32 len) { - struct i2c_client *client = v4l2_get_subdevdata(&ov13858->sd); int ret; u32 i; @@ -1126,7 +1129,7 @@ static int ov13858_write_regs(struct ov13858 *ov13858, regs[i].val); if (ret) { dev_err_ratelimited( - &client->dev, + ov13858->dev, "Failed to write reg 0x%4.4x. error = %d\n", regs[i].address, ret); @@ -1209,7 +1212,6 @@ static int ov13858_set_ctrl(struct v4l2_ctrl *ctrl) { struct ov13858 *ov13858 = container_of(ctrl->handler, struct ov13858, ctrl_handler); - struct i2c_client *client = v4l2_get_subdevdata(&ov13858->sd); s64 max; int ret; @@ -1228,7 +1230,7 @@ static int ov13858_set_ctrl(struct v4l2_ctrl *ctrl) * Applying V4L2 control value only happens * when power is up for streaming */ - if (!pm_runtime_get_if_in_use(&client->dev)) + if (!pm_runtime_get_if_in_use(ov13858->dev)) return 0; ret = 0; @@ -1256,13 +1258,13 @@ static int ov13858_set_ctrl(struct v4l2_ctrl *ctrl) ret = ov13858_enable_test_pattern(ov13858, ctrl->val); break; default: - dev_info(&client->dev, + dev_info(ov13858->dev, "ctrl(id:0x%x,val:0x%x) is not handled\n", ctrl->id, ctrl->val); break; } - pm_runtime_put(&client->dev); + pm_runtime_put(ov13858->dev); return ret; } @@ -1408,7 +1410,6 @@ static int ov13858_get_skip_frames(struct v4l2_subdev *sd, u32 *frames) /* Start streaming */ static int ov13858_start_streaming(struct ov13858 *ov13858) { - struct i2c_client *client = v4l2_get_subdevdata(&ov13858->sd); const struct ov13858_reg_list *reg_list; int ret, link_freq_index; @@ -1416,7 +1417,7 @@ static int ov13858_start_streaming(struct ov13858 *ov13858) ret = ov13858_write_reg(ov13858, OV13858_REG_SOFTWARE_RST, OV13858_REG_VALUE_08BIT, OV13858_SOFTWARE_RST); if (ret) { - dev_err(&client->dev, "%s failed to set powerup registers\n", + dev_err(ov13858->dev, "%s failed to set powerup registers\n", __func__); return ret; } @@ -1426,7 +1427,7 @@ static int ov13858_start_streaming(struct ov13858 *ov13858) reg_list = &link_freq_configs[link_freq_index].reg_list; ret = ov13858_write_reg_list(ov13858, reg_list); if (ret) { - dev_err(&client->dev, "%s failed to set plls\n", __func__); + dev_err(ov13858->dev, "%s failed to set plls\n", __func__); return ret; } @@ -1434,7 +1435,7 @@ static int ov13858_start_streaming(struct ov13858 *ov13858) reg_list = &ov13858->cur_mode->reg_list; ret = ov13858_write_reg_list(ov13858, reg_list); if (ret) { - dev_err(&client->dev, "%s failed to set mode\n", __func__); + dev_err(ov13858->dev, "%s failed to set mode\n", __func__); return ret; } @@ -1458,13 +1459,12 @@ static int ov13858_stop_streaming(struct ov13858 *ov13858) static int ov13858_set_stream(struct v4l2_subdev *sd, int enable) { struct ov13858 *ov13858 = to_ov13858(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); int ret = 0; mutex_lock(&ov13858->mutex); if (enable) { - ret = pm_runtime_resume_and_get(&client->dev); + ret = pm_runtime_resume_and_get(ov13858->dev); if (ret < 0) goto err_unlock; @@ -1477,7 +1477,7 @@ static int ov13858_set_stream(struct v4l2_subdev *sd, int enable) goto err_rpm_put; } else { ov13858_stop_streaming(ov13858); - pm_runtime_put(&client->dev); + pm_runtime_put(ov13858->dev); } mutex_unlock(&ov13858->mutex); @@ -1485,7 +1485,7 @@ static int ov13858_set_stream(struct v4l2_subdev *sd, int enable) return ret; err_rpm_put: - pm_runtime_put(&client->dev); + pm_runtime_put(ov13858->dev); err_unlock: mutex_unlock(&ov13858->mutex); @@ -1495,7 +1495,6 @@ err_unlock: /* Verify chip ID */ static int ov13858_identify_module(struct ov13858 *ov13858) { - struct i2c_client *client = v4l2_get_subdevdata(&ov13858->sd); int ret; u32 val; @@ -1505,7 +1504,7 @@ static int ov13858_identify_module(struct ov13858 *ov13858) return ret; if (val != OV13858_CHIP_ID) { - dev_err(&client->dev, "chip id mismatch: %x!=%x\n", + dev_err(ov13858->dev, "chip id mismatch: %x!=%x\n", OV13858_CHIP_ID, val); return -EIO; } @@ -1552,7 +1551,6 @@ static const struct v4l2_subdev_internal_ops ov13858_internal_ops = { /* Initialize control handlers */ static int ov13858_init_controls(struct ov13858 *ov13858) { - struct i2c_client *client = v4l2_get_subdevdata(&ov13858->sd); struct v4l2_fwnode_device_properties props; struct v4l2_ctrl_handler *ctrl_hdlr; s64 exposure_max; @@ -1626,12 +1624,12 @@ static int ov13858_init_controls(struct ov13858 *ov13858) 0, 0, ov13858_test_pattern_menu); if (ctrl_hdlr->error) { ret = ctrl_hdlr->error; - dev_err(&client->dev, "%s control init failed (%d)\n", + dev_err(ov13858->dev, "%s control init failed (%d)\n", __func__, ret); goto error; } - ret = v4l2_fwnode_device_parse(&client->dev, &props); + ret = v4l2_fwnode_device_parse(ov13858->dev, &props); if (ret) goto error; @@ -1660,24 +1658,33 @@ static void ov13858_free_controls(struct ov13858 *ov13858) static int ov13858_probe(struct i2c_client *client) { struct ov13858 *ov13858; + unsigned long freq; int ret; - u32 val = 0; - - device_property_read_u32(&client->dev, "clock-frequency", &val); - if (val != 19200000) - return -EINVAL; ov13858 = devm_kzalloc(&client->dev, sizeof(*ov13858), GFP_KERNEL); if (!ov13858) return -ENOMEM; + ov13858->dev = &client->dev; + + ov13858->clk = devm_v4l2_sensor_clk_get(ov13858->dev, NULL); + if (IS_ERR(ov13858->clk)) + return dev_err_probe(ov13858->dev, PTR_ERR(ov13858->clk), + "failed to get clock\n"); + + freq = clk_get_rate(ov13858->clk); + if (freq != 19200000) + return dev_err_probe(ov13858->dev, -EINVAL, + "external clock %lu is not supported\n", + freq); + /* Initialize subdev */ v4l2_i2c_subdev_init(&ov13858->sd, client, &ov13858_subdev_ops); /* Check module identity */ ret = ov13858_identify_module(ov13858); if (ret) { - dev_err(&client->dev, "failed to find sensor: %d\n", ret); + dev_err(ov13858->dev, "failed to find sensor: %d\n", ret); return ret; } @@ -1699,7 +1706,7 @@ static int ov13858_probe(struct i2c_client *client) ov13858->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&ov13858->sd.entity, 1, &ov13858->pad); if (ret) { - dev_err(&client->dev, "%s failed:%d\n", __func__, ret); + dev_err(ov13858->dev, "%s failed:%d\n", __func__, ret); goto error_handler_free; } @@ -1711,9 +1718,9 @@ static int ov13858_probe(struct i2c_client *client) * Device is already turned on by i2c-core with ACPI domain PM. * Enable runtime PM and turn off the device. */ - pm_runtime_set_active(&client->dev); - pm_runtime_enable(&client->dev); - pm_runtime_idle(&client->dev); + pm_runtime_set_active(ov13858->dev); + pm_runtime_enable(ov13858->dev); + pm_runtime_idle(ov13858->dev); return 0; @@ -1722,7 +1729,7 @@ error_media_entity: error_handler_free: ov13858_free_controls(ov13858); - dev_err(&client->dev, "%s failed:%d\n", __func__, ret); + dev_err(ov13858->dev, "%s failed:%d\n", __func__, ret); return ret; } @@ -1736,7 +1743,7 @@ static void ov13858_remove(struct i2c_client *client) media_entity_cleanup(&sd->entity); ov13858_free_controls(ov13858); - pm_runtime_disable(&client->dev); + pm_runtime_disable(ov13858->dev); } static const struct i2c_device_id ov13858_id_table[] = { diff --git a/drivers/media/i2c/ov13b10.c b/drivers/media/i2c/ov13b10.c index 73c844aa5697..5421874732bc 100644 --- a/drivers/media/i2c/ov13b10.c +++ b/drivers/media/i2c/ov13b10.c @@ -34,9 +34,6 @@ #define OV13B10_VTS_120FPS 0x0320 #define OV13B10_VTS_MAX 0x7fff -/* HBLANK control - read only */ -#define OV13B10_PPL_560MHZ 4704 - /* Exposure control */ #define OV13B10_REG_EXPOSURE 0x3500 #define OV13B10_EXPOSURE_MIN 4 @@ -95,7 +92,7 @@ struct ov13b10_reg_list { /* Link frequency config */ struct ov13b10_link_freq_config { - u32 pixels_per_line; + u64 link_freq; /* registers for this link frequency */ struct ov13b10_reg_list reg_list; @@ -114,6 +111,10 @@ struct ov13b10_mode { /* Index of Link frequency config to be used */ u32 link_freq_index; + + /* Pixels per line in current mode */ + u32 ppl; + /* Default register values */ struct ov13b10_reg_list reg_list; }; @@ -513,6 +514,52 @@ static const struct ov13b10_reg mode_1364x768_120fps_regs[] = { {0x5001, 0x0d}, }; +static const struct ov13b10_reg mode_2lanes_2104x1560_60fps_regs[] = { + {0x3016, 0x32}, + {0x3106, 0x29}, + {0x0305, 0xaf}, + {0x3501, 0x06}, + {0x3662, 0x88}, + {0x3714, 0x28}, + {0x3739, 0x10}, + {0x37c2, 0x14}, + {0x37d9, 0x06}, + {0x37e2, 0x0c}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x08}, + {0x3804, 0x10}, + {0x3805, 0x8f}, + {0x3806, 0x0c}, + {0x3807, 0x47}, + {0x3808, 0x08}, + {0x3809, 0x38}, + {0x380a, 0x06}, + {0x380b, 0x18}, + {0x380c, 0x04}, + {0x380d, 0x98}, + {0x380e, 0x06}, + {0x380f, 0x3e}, + {0x3810, 0x00}, + {0x3811, 0x07}, + {0x3812, 0x00}, + {0x3813, 0x05}, + {0x3814, 0x03}, + {0x3816, 0x03}, + {0x3820, 0x8b}, + {0x3c8c, 0x18}, + {0x4008, 0x00}, + {0x4009, 0x05}, + {0x4050, 0x00}, + {0x4051, 0x05}, + {0x4501, 0x08}, + {0x4505, 0x00}, + {0x4837, 0x0e}, + {0x5000, 0xfd}, + {0x5001, 0x0d}, +}; + static const char * const ov13b10_test_pattern_menu[] = { "Disabled", "Vertical Color Bar Type 1", @@ -526,15 +573,16 @@ static const char * const ov13b10_test_pattern_menu[] = { #define OV13B10_LINK_FREQ_INDEX_0 0 #define OV13B10_EXT_CLK 19200000 -#define OV13B10_DATA_LANES 4 +#define OV13B10_4_DATA_LANES 4 +#define OV13B10_2_DATA_LANES 2 /* - * 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 + * pixel_rate = data_rate * nr_of_lanes / bits_per_pixel + * data_rate => link_freq * 2; number of lanes => 4 or 2; bits per pixel => 10 */ -static u64 link_freq_to_pixel_rate(u64 f) +static u64 link_freq_to_pixel_rate(u64 f, u8 lanes) { - f *= 2 * OV13B10_DATA_LANES; + f *= 2 * lanes; do_div(f, 10); return f; @@ -549,7 +597,7 @@ static const s64 link_freq_menu_items[] = { static const struct ov13b10_link_freq_config link_freq_configs[] = { { - .pixels_per_line = OV13B10_PPL_560MHZ, + .link_freq = OV13B10_LINK_FREQ_560MHZ, .reg_list = { .num_of_regs = ARRAY_SIZE(mipi_data_rate_1120mbps), .regs = mipi_data_rate_1120mbps, @@ -558,12 +606,14 @@ static const struct ov13b10_link_freq_config }; /* Mode configs */ -static const struct ov13b10_mode supported_modes[] = { +static const struct ov13b10_mode supported_4_lanes_modes[] = { + /* 4 data lanes */ { .width = 4208, .height = 3120, .vts_def = OV13B10_VTS_30FPS, .vts_min = OV13B10_VTS_30FPS, + .ppl = 4704, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_4208x3120_regs), .regs = mode_4208x3120_regs, @@ -575,6 +625,7 @@ static const struct ov13b10_mode supported_modes[] = { .height = 3120, .vts_def = OV13B10_VTS_30FPS, .vts_min = OV13B10_VTS_30FPS, + .ppl = 4704, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_4160x3120_regs), .regs = mode_4160x3120_regs, @@ -586,6 +637,7 @@ static const struct ov13b10_mode supported_modes[] = { .height = 2340, .vts_def = OV13B10_VTS_30FPS, .vts_min = OV13B10_VTS_30FPS, + .ppl = 4704, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_4160x2340_regs), .regs = mode_4160x2340_regs, @@ -597,6 +649,7 @@ static const struct ov13b10_mode supported_modes[] = { .height = 1560, .vts_def = OV13B10_VTS_60FPS, .vts_min = OV13B10_VTS_60FPS, + .ppl = 4704, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_2104x1560_regs), .regs = mode_2104x1560_regs, @@ -608,6 +661,7 @@ static const struct ov13b10_mode supported_modes[] = { .height = 1170, .vts_def = OV13B10_VTS_60FPS, .vts_min = OV13B10_VTS_60FPS, + .ppl = 4704, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_2080x1170_regs), .regs = mode_2080x1170_regs, @@ -620,6 +674,7 @@ static const struct ov13b10_mode supported_modes[] = { .vts_def = OV13B10_VTS_120FPS, .vts_min = OV13B10_VTS_120FPS, .link_freq_index = OV13B10_LINK_FREQ_INDEX_0, + .ppl = 4664, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1364x768_120fps_regs), .regs = mode_1364x768_120fps_regs, @@ -627,7 +682,26 @@ static const struct ov13b10_mode supported_modes[] = { }, }; +static const struct ov13b10_mode supported_2_lanes_modes[] = { + /* 2 data lanes */ + { + .width = 2104, + .height = 1560, + .vts_def = OV13B10_VTS_60FPS, + .vts_min = OV13B10_VTS_60FPS, + .link_freq_index = OV13B10_LINK_FREQ_INDEX_0, + .ppl = 2352, + .reg_list = { + .num_of_regs = + ARRAY_SIZE(mode_2lanes_2104x1560_60fps_regs), + .regs = mode_2lanes_2104x1560_60fps_regs, + }, + }, +}; + struct ov13b10 { + struct device *dev; + struct v4l2_subdev sd; struct media_pad pad; @@ -644,12 +718,20 @@ struct ov13b10 { struct v4l2_ctrl *hblank; struct v4l2_ctrl *exposure; + /* Supported modes */ + const struct ov13b10_mode *supported_modes; + /* Current mode */ const struct ov13b10_mode *cur_mode; /* Mutex for serialized access */ struct mutex mutex; + u8 supported_modes_num; + + /* Data lanes used */ + u8 data_lanes; + /* True if the device has been identified */ bool identified; }; @@ -725,7 +807,6 @@ static int ov13b10_write_reg(struct ov13b10 *ov13b, static int ov13b10_write_regs(struct ov13b10 *ov13b, const struct ov13b10_reg *regs, u32 len) { - struct i2c_client *client = v4l2_get_subdevdata(&ov13b->sd); int ret; u32 i; @@ -733,7 +814,7 @@ static int ov13b10_write_regs(struct ov13b10 *ov13b, ret = ov13b10_write_reg(ov13b, regs[i].address, 1, regs[i].val); if (ret) { - dev_err_ratelimited(&client->dev, + dev_err_ratelimited(ov13b->dev, "Failed to write reg 0x%4.4x. error = %d\n", regs[i].address, ret); @@ -753,8 +834,8 @@ static int ov13b10_write_reg_list(struct ov13b10 *ov13b, /* Open sub-device */ static int ov13b10_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { - const struct ov13b10_mode *default_mode = &supported_modes[0]; struct ov13b10 *ov13b = to_ov13b10(sd); + const struct ov13b10_mode *default_mode = ov13b->supported_modes; struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_state_get_format(fh->state, 0); @@ -888,7 +969,6 @@ static int ov13b10_set_ctrl(struct v4l2_ctrl *ctrl) { struct ov13b10 *ov13b = container_of(ctrl->handler, struct ov13b10, ctrl_handler); - struct i2c_client *client = v4l2_get_subdevdata(&ov13b->sd); s64 max; int ret; @@ -907,7 +987,7 @@ static int ov13b10_set_ctrl(struct v4l2_ctrl *ctrl) * Applying V4L2 control value only happens * when power is up for streaming */ - if (!pm_runtime_get_if_in_use(&client->dev)) + if (!pm_runtime_get_if_in_use(ov13b->dev)) return 0; ret = 0; @@ -941,13 +1021,13 @@ static int ov13b10_set_ctrl(struct v4l2_ctrl *ctrl) ov13b10_set_ctrl_vflip(ov13b, ctrl->val); break; default: - dev_info(&client->dev, + dev_info(ov13b->dev, "ctrl(id:0x%x,val:0x%x) is not handled\n", ctrl->id, ctrl->val); break; } - pm_runtime_put(&client->dev); + pm_runtime_put(ov13b->dev); return ret; } @@ -973,7 +1053,10 @@ static int ov13b10_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { - if (fse->index >= ARRAY_SIZE(supported_modes)) + struct ov13b10 *ov13b = to_ov13b10(sd); + const struct ov13b10_mode *supported_modes = ov13b->supported_modes; + + if (fse->index >= ov13b->supported_modes_num) return -EINVAL; if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) @@ -1033,6 +1116,7 @@ ov13b10_set_pad_format(struct v4l2_subdev *sd, { struct ov13b10 *ov13b = to_ov13b10(sd); const struct ov13b10_mode *mode; + const struct ov13b10_mode *supported_modes = ov13b->supported_modes; struct v4l2_mbus_framefmt *framefmt; s32 vblank_def; s32 vblank_min; @@ -1047,7 +1131,7 @@ ov13b10_set_pad_format(struct v4l2_subdev *sd, fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10; mode = v4l2_find_nearest_size(supported_modes, - ARRAY_SIZE(supported_modes), + ov13b->supported_modes_num, width, height, fmt->format.width, fmt->format.height); ov13b10_update_pad_format(mode, fmt); @@ -1058,23 +1142,18 @@ ov13b10_set_pad_format(struct v4l2_subdev *sd, ov13b->cur_mode = mode; __v4l2_ctrl_s_ctrl(ov13b->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); + pixel_rate = link_freq_to_pixel_rate(link_freq, + ov13b->data_lanes); __v4l2_ctrl_s_ctrl_int64(ov13b->pixel_rate, pixel_rate); /* Update limits and set FPS to default */ - vblank_def = ov13b->cur_mode->vts_def - - ov13b->cur_mode->height; - vblank_min = ov13b->cur_mode->vts_min - - ov13b->cur_mode->height; + vblank_def = mode->vts_def - mode->height; + vblank_min = mode->vts_min - mode->height; __v4l2_ctrl_modify_range(ov13b->vblank, vblank_min, - OV13B10_VTS_MAX - - ov13b->cur_mode->height, - 1, - vblank_def); + OV13B10_VTS_MAX - mode->height, + 1, vblank_def); __v4l2_ctrl_s_ctrl(ov13b->vblank, vblank_def); - h_blank = - link_freq_configs[mode->link_freq_index].pixels_per_line - - ov13b->cur_mode->width; + h_blank = mode->ppl - mode->width; __v4l2_ctrl_modify_range(ov13b->hblank, h_blank, h_blank, 1, h_blank); } @@ -1087,7 +1166,6 @@ ov13b10_set_pad_format(struct v4l2_subdev *sd, /* Verify chip ID */ static int ov13b10_identify_module(struct ov13b10 *ov13b) { - struct i2c_client *client = v4l2_get_subdevdata(&ov13b->sd); int ret; u32 val; @@ -1100,7 +1178,7 @@ static int ov13b10_identify_module(struct ov13b10 *ov13b) return ret; if (val != OV13B10_CHIP_ID) { - dev_err(&client->dev, "chip id mismatch: %x!=%x\n", + dev_err(ov13b->dev, "chip id mismatch: %x!=%x\n", OV13B10_CHIP_ID, val); return -EIO; } @@ -1155,7 +1233,6 @@ static int ov13b10_power_on(struct device *dev) static int ov13b10_start_streaming(struct ov13b10 *ov13b) { - struct i2c_client *client = v4l2_get_subdevdata(&ov13b->sd); const struct ov13b10_reg_list *reg_list; int ret, link_freq_index; @@ -1167,7 +1244,7 @@ static int ov13b10_start_streaming(struct ov13b10 *ov13b) ret = ov13b10_write_reg(ov13b, OV13B10_REG_SOFTWARE_RST, OV13B10_REG_VALUE_08BIT, OV13B10_SOFTWARE_RST); if (ret) { - dev_err(&client->dev, "%s failed to set powerup registers\n", + dev_err(ov13b->dev, "%s failed to set powerup registers\n", __func__); return ret; } @@ -1176,7 +1253,7 @@ static int ov13b10_start_streaming(struct ov13b10 *ov13b) reg_list = &link_freq_configs[link_freq_index].reg_list; ret = ov13b10_write_reg_list(ov13b, reg_list); if (ret) { - dev_err(&client->dev, "%s failed to set plls\n", __func__); + dev_err(ov13b->dev, "%s failed to set plls\n", __func__); return ret; } @@ -1184,7 +1261,7 @@ static int ov13b10_start_streaming(struct ov13b10 *ov13b) reg_list = &ov13b->cur_mode->reg_list; ret = ov13b10_write_reg_list(ov13b, reg_list); if (ret) { - dev_err(&client->dev, "%s failed to set mode\n", __func__); + dev_err(ov13b->dev, "%s failed to set mode\n", __func__); return ret; } @@ -1208,13 +1285,12 @@ static int ov13b10_stop_streaming(struct ov13b10 *ov13b) static int ov13b10_set_stream(struct v4l2_subdev *sd, int enable) { struct ov13b10 *ov13b = to_ov13b10(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); int ret = 0; mutex_lock(&ov13b->mutex); if (enable) { - ret = pm_runtime_resume_and_get(&client->dev); + ret = pm_runtime_resume_and_get(ov13b->dev); if (ret < 0) goto err_unlock; @@ -1227,7 +1303,7 @@ static int ov13b10_set_stream(struct v4l2_subdev *sd, int enable) goto err_rpm_put; } else { ov13b10_stop_streaming(ov13b); - pm_runtime_put(&client->dev); + pm_runtime_put(ov13b->dev); } mutex_unlock(&ov13b->mutex); @@ -1235,7 +1311,7 @@ static int ov13b10_set_stream(struct v4l2_subdev *sd, int enable) return ret; err_rpm_put: - pm_runtime_put(&client->dev); + pm_runtime_put(ov13b->dev); err_unlock: mutex_unlock(&ov13b->mutex); @@ -1281,7 +1357,6 @@ static const struct v4l2_subdev_internal_ops ov13b10_internal_ops = { /* Initialize control handlers */ static int ov13b10_init_controls(struct ov13b10 *ov13b) { - struct i2c_client *client = v4l2_get_subdevdata(&ov13b->sd); struct v4l2_fwnode_device_properties props; struct v4l2_ctrl_handler *ctrl_hdlr; s64 exposure_max; @@ -1311,7 +1386,8 @@ static int ov13b10_init_controls(struct ov13b10 *ov13b) if (ov13b->link_freq) ov13b->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; - pixel_rate_max = link_freq_to_pixel_rate(link_freq_menu_items[0]); + pixel_rate_max = link_freq_to_pixel_rate(link_freq_menu_items[0], + ov13b->data_lanes); pixel_rate_min = 0; /* By default, PIXEL_RATE is read only */ ov13b->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov13b10_ctrl_ops, @@ -1328,8 +1404,7 @@ static int ov13b10_init_controls(struct ov13b10 *ov13b) OV13B10_VTS_MAX - mode->height, 1, vblank_def); - hblank = link_freq_configs[mode->link_freq_index].pixels_per_line - - mode->width; + hblank = mode->ppl - mode->width; ov13b->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov13b10_ctrl_ops, V4L2_CID_HBLANK, hblank, hblank, 1, hblank); @@ -1364,12 +1439,12 @@ static int ov13b10_init_controls(struct ov13b10 *ov13b) if (ctrl_hdlr->error) { ret = ctrl_hdlr->error; - dev_err(&client->dev, "%s control init failed (%d)\n", + dev_err(ov13b->dev, "%s control init failed (%d)\n", __func__, ret); goto error; } - ret = v4l2_fwnode_device_parse(&client->dev, &props); + ret = v4l2_fwnode_device_parse(ov13b->dev, &props); if (ret) goto error; @@ -1395,44 +1470,50 @@ static void ov13b10_free_controls(struct ov13b10 *ov13b) mutex_destroy(&ov13b->mutex); } -static int ov13b10_get_pm_resources(struct device *dev) +static int ov13b10_get_pm_resources(struct ov13b10 *ov13b) { - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct ov13b10 *ov13b = to_ov13b10(sd); + unsigned long freq; int ret; - ov13b->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + ov13b->reset = devm_gpiod_get_optional(ov13b->dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(ov13b->reset)) - return dev_err_probe(dev, PTR_ERR(ov13b->reset), + return dev_err_probe(ov13b->dev, PTR_ERR(ov13b->reset), "failed to get reset gpio\n"); - ov13b->img_clk = devm_clk_get_optional(dev, NULL); + ov13b->img_clk = devm_v4l2_sensor_clk_get(ov13b->dev, NULL); if (IS_ERR(ov13b->img_clk)) - return dev_err_probe(dev, PTR_ERR(ov13b->img_clk), + return dev_err_probe(ov13b->dev, PTR_ERR(ov13b->img_clk), "failed to get imaging clock\n"); - ov13b->avdd = devm_regulator_get_optional(dev, "avdd"); + freq = clk_get_rate(ov13b->img_clk); + if (freq != OV13B10_EXT_CLK) + return dev_err_probe(ov13b->dev, -EINVAL, + "external clock %lu is not supported\n", + freq); + + ov13b->avdd = devm_regulator_get_optional(ov13b->dev, "avdd"); if (IS_ERR(ov13b->avdd)) { ret = PTR_ERR(ov13b->avdd); ov13b->avdd = NULL; if (ret != -ENODEV) - return dev_err_probe(dev, ret, + return dev_err_probe(ov13b->dev, ret, "failed to get avdd regulator\n"); } return 0; } -static int ov13b10_check_hwcfg(struct device *dev) +static int ov13b10_check_hwcfg(struct ov13b10 *ov13b) { struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = V4L2_MBUS_CSI2_DPHY }; + struct device *dev = ov13b->dev; struct fwnode_handle *ep; struct fwnode_handle *fwnode = dev_fwnode(dev); unsigned int i, j; int ret; - u32 ext_clk; + u8 dlane; if (!fwnode) return -ENXIO; @@ -1441,31 +1522,37 @@ static int ov13b10_check_hwcfg(struct device *dev) if (!ep) return -EPROBE_DEFER; - ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", - &ext_clk); - if (ret) { - dev_err(dev, "can't get clock frequency"); - return ret; - } - - if (ext_clk != OV13B10_EXT_CLK) { - dev_err(dev, "external clock %d is not supported", - ext_clk); - return -EINVAL; - } - ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); fwnode_handle_put(ep); if (ret) return ret; - if (bus_cfg.bus.mipi_csi2.num_data_lanes != OV13B10_DATA_LANES) { + dlane = bus_cfg.bus.mipi_csi2.num_data_lanes; + switch (dlane) { + case OV13B10_4_DATA_LANES: + ov13b->supported_modes = supported_4_lanes_modes; + ov13b->supported_modes_num = + ARRAY_SIZE(supported_4_lanes_modes); + break; + + case OV13B10_2_DATA_LANES: + ov13b->supported_modes = supported_2_lanes_modes; + ov13b->supported_modes_num = + ARRAY_SIZE(supported_2_lanes_modes); + break; + + default: dev_err(dev, "number of CSI2 data lanes %d is not supported", - bus_cfg.bus.mipi_csi2.num_data_lanes); + dlane); ret = -EINVAL; goto out_err; } + ov13b->data_lanes = dlane; + ov13b->cur_mode = ov13b->supported_modes; + dev_dbg(dev, "%u lanes with %u modes selected\n", + ov13b->data_lanes, ov13b->supported_modes_num); + if (!bus_cfg.nr_of_link_frequencies) { dev_err(dev, "no link frequencies defined"); ret = -EINVAL; @@ -1499,43 +1586,42 @@ static int ov13b10_probe(struct i2c_client *client) bool full_power; int ret; + ov13b = devm_kzalloc(&client->dev, sizeof(*ov13b), GFP_KERNEL); + if (!ov13b) + return -ENOMEM; + + ov13b->dev = &client->dev; + /* Check HW config */ - ret = ov13b10_check_hwcfg(&client->dev); + ret = ov13b10_check_hwcfg(ov13b); if (ret) { - dev_err(&client->dev, "failed to check hwcfg: %d", ret); + dev_err(ov13b->dev, "failed to check hwcfg: %d", ret); return ret; } - ov13b = devm_kzalloc(&client->dev, sizeof(*ov13b), GFP_KERNEL); - if (!ov13b) - return -ENOMEM; - /* Initialize subdev */ v4l2_i2c_subdev_init(&ov13b->sd, client, &ov13b10_subdev_ops); - ret = ov13b10_get_pm_resources(&client->dev); + ret = ov13b10_get_pm_resources(ov13b); if (ret) return ret; - full_power = acpi_dev_state_d0(&client->dev); + full_power = acpi_dev_state_d0(ov13b->dev); if (full_power) { - ret = ov13b10_power_on(&client->dev); + ret = ov13b10_power_on(ov13b->dev); if (ret) { - dev_err(&client->dev, "failed to power on\n"); + dev_err(ov13b->dev, "failed to power on\n"); return ret; } /* Check module identity */ ret = ov13b10_identify_module(ov13b); if (ret) { - dev_err(&client->dev, "failed to find sensor: %d\n", ret); + dev_err(ov13b->dev, "failed to find sensor: %d\n", ret); goto error_power_off; } } - /* Set default mode to max resolution */ - ov13b->cur_mode = &supported_modes[0]; - ret = ov13b10_init_controls(ov13b); if (ret) goto error_power_off; @@ -1550,7 +1636,7 @@ static int ov13b10_probe(struct i2c_client *client) ov13b->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&ov13b->sd.entity, 1, &ov13b->pad); if (ret) { - dev_err(&client->dev, "%s failed:%d\n", __func__, ret); + dev_err(ov13b->dev, "%s failed:%d\n", __func__, ret); goto error_handler_free; } @@ -1561,9 +1647,9 @@ static int ov13b10_probe(struct i2c_client *client) */ /* Set the device's state to active if it's in D0 state. */ if (full_power) - pm_runtime_set_active(&client->dev); - pm_runtime_enable(&client->dev); - pm_runtime_idle(&client->dev); + pm_runtime_set_active(ov13b->dev); + pm_runtime_enable(ov13b->dev); + pm_runtime_idle(ov13b->dev); ret = v4l2_async_register_subdev_sensor(&ov13b->sd); if (ret < 0) @@ -1572,17 +1658,17 @@ static int ov13b10_probe(struct i2c_client *client) return 0; error_media_entity_runtime_pm: - pm_runtime_disable(&client->dev); + pm_runtime_disable(ov13b->dev); if (full_power) - pm_runtime_set_suspended(&client->dev); + pm_runtime_set_suspended(ov13b->dev); media_entity_cleanup(&ov13b->sd.entity); error_handler_free: ov13b10_free_controls(ov13b); - dev_err(&client->dev, "%s failed:%d\n", __func__, ret); + dev_err(ov13b->dev, "%s failed:%d\n", __func__, ret); error_power_off: - ov13b10_power_off(&client->dev); + ov13b10_power_off(ov13b->dev); return ret; } @@ -1596,8 +1682,8 @@ static void ov13b10_remove(struct i2c_client *client) media_entity_cleanup(&sd->entity); ov13b10_free_controls(ov13b); - pm_runtime_disable(&client->dev); - pm_runtime_set_suspended(&client->dev); + pm_runtime_disable(ov13b->dev); + pm_runtime_set_suspended(ov13b->dev); } static DEFINE_RUNTIME_DEV_PM_OPS(ov13b10_pm_ops, ov13b10_suspend, @@ -1607,6 +1693,7 @@ static DEFINE_RUNTIME_DEV_PM_OPS(ov13b10_pm_ops, ov13b10_suspend, static const struct acpi_device_id ov13b10_acpi_ids[] = { {"OVTIDB10"}, {"OVTI13B1"}, + {"OMNI13B1"}, /* ASUS ROG Flow Z13 (GZ302) uses this ACPI ID */ { /* sentinel */ } }; diff --git a/drivers/media/i2c/ov2659.c b/drivers/media/i2c/ov2659.c index 06b7896c3eaf..061401b020fc 100644 --- a/drivers/media/i2c/ov2659.c +++ b/drivers/media/i2c/ov2659.c @@ -1437,9 +1437,10 @@ static int ov2659_probe(struct i2c_client *client) ov2659->pdata = pdata; ov2659->client = client; - ov2659->clk = devm_clk_get(&client->dev, "xvclk"); + ov2659->clk = devm_v4l2_sensor_clk_get(&client->dev, "xvclk"); if (IS_ERR(ov2659->clk)) - return PTR_ERR(ov2659->clk); + return dev_err_probe(&client->dev, PTR_ERR(ov2659->clk), + "failed to get xvclk\n"); ov2659->xvclk_frequency = clk_get_rate(ov2659->clk); if (ov2659->xvclk_frequency < 6000000 || @@ -1469,14 +1470,15 @@ static int ov2659_probe(struct i2c_client *client) V4L2_CID_TEST_PATTERN, ARRAY_SIZE(ov2659_test_pattern_menu) - 1, 0, 0, ov2659_test_pattern_menu); - ov2659->sd.ctrl_handler = &ov2659->ctrls; if (ov2659->ctrls.error) { dev_err(&client->dev, "%s: control initialization error %d\n", __func__, ov2659->ctrls.error); + v4l2_ctrl_handler_free(&ov2659->ctrls); return ov2659->ctrls.error; } + ov2659->sd.ctrl_handler = &ov2659->ctrls; sd = &ov2659->sd; client->flags |= I2C_CLIENT_SCCB; diff --git a/drivers/media/i2c/ov2680.c b/drivers/media/i2c/ov2680.c index 7237fb27ecd0..78e63bd1b35b 100644 --- a/drivers/media/i2c/ov2680.c +++ b/drivers/media/i2c/ov2680.c @@ -1079,7 +1079,6 @@ static int ov2680_parse_dt(struct ov2680_dev *sensor) struct device *dev = sensor->dev; struct fwnode_handle *ep_fwnode; struct gpio_desc *gpio; - unsigned int rate = 0; int i, ret; /* @@ -1114,38 +1113,14 @@ static int ov2680_parse_dt(struct ov2680_dev *sensor) sensor->pwdn_gpio = gpio; - sensor->xvclk = devm_clk_get_optional(dev, "xvclk"); + sensor->xvclk = devm_v4l2_sensor_clk_get(dev, "xvclk"); if (IS_ERR(sensor->xvclk)) { ret = dev_err_probe(dev, PTR_ERR(sensor->xvclk), "xvclk clock missing or invalid\n"); goto out_free_bus_cfg; } - /* - * We could have either a 24MHz or 19.2MHz clock rate from either DT or - * ACPI... but we also need to support the weird IPU3 case which will - * have an external clock AND a clock-frequency property. Check for the - * clock-frequency property and if found, set that rate if we managed - * to acquire a clock. This should cover the ACPI case. If the system - * uses devicetree then the configured rate should already be set, so - * we can just read it. - */ - ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", - &rate); - if (ret && !sensor->xvclk) { - dev_err_probe(dev, ret, "invalid clock config\n"); - goto out_free_bus_cfg; - } - - if (!ret && sensor->xvclk) { - ret = clk_set_rate(sensor->xvclk, rate); - if (ret) { - dev_err_probe(dev, ret, "failed to set clock rate\n"); - goto out_free_bus_cfg; - } - } - - sensor->xvclk_freq = rate ?: clk_get_rate(sensor->xvclk); + sensor->xvclk_freq = clk_get_rate(sensor->xvclk); for (i = 0; i < ARRAY_SIZE(ov2680_xvclk_freqs); i++) { if (sensor->xvclk_freq == ov2680_xvclk_freqs[i]) diff --git a/drivers/media/i2c/ov2685.c b/drivers/media/i2c/ov2685.c index 9b8481b8dcd4..4911a4eea126 100644 --- a/drivers/media/i2c/ov2685.c +++ b/drivers/media/i2c/ov2685.c @@ -783,16 +783,12 @@ static int ov2685_probe(struct i2c_client *client) ov2685->client = client; ov2685->cur_mode = &supported_modes[0]; - ov2685->xvclk = devm_clk_get(dev, "xvclk"); - if (IS_ERR(ov2685->xvclk)) { - 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; - } + ov2685->xvclk = devm_v4l2_sensor_clk_get_legacy(dev, "xvclk", true, + OV2685_XVCLK_FREQ); + if (IS_ERR(ov2685->xvclk)) + return dev_err_probe(dev, PTR_ERR(ov2685->xvclk), + "Failed to get xvclk\n"); + if (clk_get_rate(ov2685->xvclk) != OV2685_XVCLK_FREQ) dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); diff --git a/drivers/media/i2c/ov2735.c b/drivers/media/i2c/ov2735.c new file mode 100644 index 000000000000..b96600204141 --- /dev/null +++ b/drivers/media/i2c/ov2735.c @@ -0,0 +1,1109 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * V4L2 Support for the OV2735 + * + * Copyright (C) 2025 Silicon Signals Pvt. Ltd. + * + * Based on Rockchip ov2735 Camera Driver + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd. + * + * Inspired from ov8858, imx219, imx283 camera drivers. + */ + +#include <linux/array_size.h> +#include <linux/bitops.h> +#include <linux/cleanup.h> +#include <linux/clk.h> +#include <linux/container_of.h> +#include <linux/delay.h> +#include <linux/device/devres.h> +#include <linux/err.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/pm_runtime.h> +#include <linux/property.h> +#include <linux/regulator/consumer.h> +#include <linux/units.h> +#include <linux/types.h> +#include <linux/time.h> + +#include <media/v4l2-cci.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-mediabus.h> + +#define OV2735_XCLK_FREQ (24 * HZ_PER_MHZ) + +/* Add page number in CCI private bits [31:28] of the register address */ +#define OV2735_PAGE_REG8(p, x) (((p) << CCI_REG_PRIVATE_SHIFT) | CCI_REG8(x)) +#define OV2735_PAGE_REG16(p, x) (((p) << CCI_REG_PRIVATE_SHIFT) | CCI_REG16(x)) + +#define OV2735_REG_PAGE_SELECT CCI_REG8(0xfd) + +/* Page 0 */ +#define OV2735_REG_CHIPID OV2735_PAGE_REG16(0x00, 0x02) +#define OV2735_CHIPID 0x2735 + +#define OV2735_REG_SOFT_RESET OV2735_PAGE_REG8(0x00, 0x20) + +/* Clock Settings */ +#define OV2735_REG_PLL_CTRL OV2735_PAGE_REG8(0x00, 0x2f) +#define OV2735_PLL_CTRL_ENABLE 0x7f +#define OV2735_REG_PLL_OUTDIV OV2735_PAGE_REG8(0x00, 0x34) +#define OV2735_REG_CLK_MODE OV2735_PAGE_REG8(0x00, 0x30) +#define OV2735_REG_CLOCK_REG1 OV2735_PAGE_REG8(0x00, 0x33) +#define OV2735_REG_CLOCK_REG2 OV2735_PAGE_REG8(0x00, 0x35) + +/* Page 1 */ +#define OV2735_REG_STREAM_CTRL OV2735_PAGE_REG8(0x01, 0xa0) +#define OV2735_STREAM_CTRL_ON 0x01 +#define OV2735_STREAM_CTRL_OFF 0x00 + +#define OV2735_REG_UPDOWN_MIRROR OV2735_PAGE_REG8(0x01, 0x3f) +#define OV2735_REG_BINNING_DAC_CODE_MODE OV2735_PAGE_REG8(0x01, 0x30) +#define OV2735_REG_FRAME_LENGTH OV2735_PAGE_REG16(0x01, 0x0e) +#define OV2735_FRAME_LENGTH_MAX 0x0fff +#define OV2735_REG_FRAME_EXP_SEPERATE_EN OV2735_PAGE_REG8(0x01, 0x0d) +#define OV2735_FRAME_EXP_SEPERATE_EN 0x10 +#define OV2735_REG_FRAME_SYNC OV2735_PAGE_REG8(0x01, 0x01) + +#define OV2735_REG_HBLANK OV2735_PAGE_REG16(0x01, 0x09) + +#define OV2735_REG_HS_MIPI OV2735_PAGE_REG8(0x01, 0xb1) +#define OV2735_REG_MIPI_CTRL1 OV2735_PAGE_REG8(0x01, 0x92) +#define OV2735_REG_MIPI_CTRL2 OV2735_PAGE_REG8(0x01, 0x94) +#define OV2735_REG_MIPI_CTRL3 OV2735_PAGE_REG8(0x01, 0xa1) +#define OV2735_REG_MIPI_CTRL4 OV2735_PAGE_REG8(0x01, 0xb2) +#define OV2735_REG_MIPI_CTRL5 OV2735_PAGE_REG8(0x01, 0xb3) +#define OV2735_REG_MIPI_CTRL6 OV2735_PAGE_REG8(0x01, 0xb4) +#define OV2735_REG_MIPI_CTRL7 OV2735_PAGE_REG8(0x01, 0xb5) +#define OV2735_REG_HIGH_SPEED OV2735_PAGE_REG8(0x01, 0x9d) +#define OV2735_REG_PREPARE OV2735_PAGE_REG8(0x01, 0x95) +#define OV2735_REG_R_HS_ZERO OV2735_PAGE_REG8(0x01, 0x96) +#define OV2735_REG_TRAIL OV2735_PAGE_REG8(0x01, 0x98) +#define OV2735_REG_R_CLK_ZERO OV2735_PAGE_REG8(0x01, 0x9c) +#define OV2735_REG_MIPI_COLOMN_NUMBER OV2735_PAGE_REG16(0x01, 0x8e) +#define OV2735_REG_MIPI_LINE_NUMBER OV2735_PAGE_REG16(0x01, 0x90) + +/* Timing control registers */ +#define OV2735_REG_TIMING_CTRL2 OV2735_PAGE_REG8(0x01, 0x1a) +#define OV2735_REG_TIMING_CTRL3 OV2735_PAGE_REG8(0x01, 0x1c) +#define OV2735_REG_TIMING_CTRL1 OV2735_PAGE_REG8(0x01, 0x16) +#define OV2735_REG_RST_NUM OV2735_PAGE_REG16(0x01, 0x10) +#define OV2735_REG_RST_NUM2 OV2735_PAGE_REG16(0x01, 0x32) +#define OV2735_REG_BOOST_EN OV2735_PAGE_REG8(0x01, 0xd0) +#define OV2735_REG_B2_NUM OV2735_PAGE_REG16(0x01, 0xd1) +#define OV2735_REG_B4_NUM OV2735_PAGE_REG16(0x01, 0xd3) +#define OV2735_REG_PIXEL_CYCLE_P0 OV2735_PAGE_REG8(0x01, 0x50) +#define OV2735_REG_PIXEL_CYCLE_P1 OV2735_PAGE_REG8(0x01, 0x51) +#define OV2735_REG_PIXEL_CYCLE_P2 OV2735_PAGE_REG8(0x01, 0x52) +#define OV2735_REG_PIXEL_CYCLE_P3 OV2735_PAGE_REG8(0x01, 0x53) +#define OV2735_REG_PIXEL_CYCLE_P5 OV2735_PAGE_REG8(0x01, 0x55) +#define OV2735_REG_PIXEL_CYCLE_P7 OV2735_PAGE_REG16(0x01, 0x57) +#define OV2735_REG_PIXEL_CYCLE_P9 OV2735_PAGE_REG8(0x01, 0x5a) +#define OV2735_REG_PIXEL_CYCLE_P10 OV2735_PAGE_REG8(0x01, 0x5b) +#define OV2735_REG_PIXEL_CYCLE_P12 OV2735_PAGE_REG8(0x01, 0x5d) +#define OV2735_REG_PIXEL_CYCLE_P18 OV2735_PAGE_REG8(0x01, 0x64) +#define OV2735_REG_PIXEL_CYCLE_P20 OV2735_PAGE_REG8(0x01, 0x66) +#define OV2735_REG_PIXEL_CYCLE_P22 OV2735_PAGE_REG8(0x01, 0x68) +#define OV2735_REG_PIXEL_CYCLE_P33 OV2735_PAGE_REG16(0x01, 0x74) +#define OV2735_REG_PIXEL_CYCLE_P34 OV2735_PAGE_REG8(0x01, 0x76) +#define OV2735_REG_PIXEL_CYCLE_P35_P36 OV2735_PAGE_REG8(0x01, 0x77) +#define OV2735_REG_PIXEL_CYCLE_P37_P38 OV2735_PAGE_REG8(0x01, 0x78) +#define OV2735_REG_PIXEL_CYCLE_P31 OV2735_PAGE_REG8(0x01, 0x72) +#define OV2735_REG_PIXEL_CYCLE_P32 OV2735_PAGE_REG8(0x01, 0x73) +#define OV2735_REG_PIXEL_CYCLE_P44 OV2735_PAGE_REG8(0x01, 0x7d) +#define OV2735_REG_PIXEL_CYCLE_P45 OV2735_PAGE_REG8(0x01, 0x7e) +#define OV2735_REG_PIXEL_BIAS_CTRL_RH_RL OV2735_PAGE_REG8(0x01, 0x8a) +#define OV2735_REG_PIXEL_BIAS_CTRL_SH_SL OV2735_PAGE_REG8(0x01, 0x8b) + +/* Analog Control registers */ +#define OV2735_REG_ICOMP OV2735_PAGE_REG8(0x01, 0x19) +#define OV2735_REG_PCP_RST_SEL OV2735_PAGE_REG8(0x01, 0x21) +#define OV2735_REG_VNCP OV2735_PAGE_REG8(0x01, 0x20) +#define OV2735_REG_ANALOG_CTRL3 OV2735_PAGE_REG8(0x01, 0x25) +#define OV2735_REG_ANALOG_CTRL4 OV2735_PAGE_REG8(0x01, 0x26) +#define OV2735_REG_ANALOG_CTRL5 OV2735_PAGE_REG8(0x01, 0x29) +#define OV2735_REG_ANALOG_CTRL6 OV2735_PAGE_REG8(0x01, 0x2a) +#define OV2735_REG_ANALOG_CTRL8 OV2735_PAGE_REG8(0x01, 0x2c) + +/* BLC registers */ +#define OV2735_REG_BLC_GAIN_BLUE OV2735_PAGE_REG8(0x01, 0x86) +#define OV2735_REG_BLC_GAIN_RED OV2735_PAGE_REG8(0x01, 0x87) +#define OV2735_REG_BLC_GAIN_GR OV2735_PAGE_REG8(0x01, 0x88) +#define OV2735_REG_BLC_GAIN_GB OV2735_PAGE_REG8(0x01, 0x89) +#define OV2735_REG_GB_SUBOFFSET OV2735_PAGE_REG8(0x01, 0xf0) +#define OV2735_REG_BLUE_SUBOFFSET OV2735_PAGE_REG8(0x01, 0xf1) +#define OV2735_REG_RED_SUBOFFSET OV2735_PAGE_REG8(0x01, 0xf2) +#define OV2735_REG_GR_SUBOFFSET OV2735_PAGE_REG8(0x01, 0xf3) +#define OV2735_REG_BLC_BPC_TH_P OV2735_PAGE_REG8(0x01, 0xfc) +#define OV2735_REG_BLC_BPC_TH_N OV2735_PAGE_REG8(0x01, 0xfe) +#define OV2735_REG_ABL OV2735_PAGE_REG8(0x01, 0xfb) + +#define OV2735_REG_TEST_PATTERN OV2735_PAGE_REG8(0x01, 0xb2) +#define OV2735_TEST_PATTERN_ENABLE 0x01 +#define OV2735_TEST_PATTERN_DISABLE 0xfe + +#define OV2735_REG_LONG_EXPOSURE OV2735_PAGE_REG16(0x01, 0x03) +#define OV2735_EXPOSURE_MIN 4 +#define OV2735_EXPOSURE_STEP 1 +#define OV2735_EXPOSURE_MARGIN 4 + +#define OV2735_REG_ANALOG_GAIN OV2735_PAGE_REG8(0x01, 0x24) +#define OV2735_ANALOG_GAIN_MIN 0x10 +#define OV2735_ANALOG_GAIN_MAX 0xff +#define OV2735_ANALOG_GAIN_STEP 1 +#define OV2735_ANALOG_GAIN_DEFAULT 0x10 + +/* Page 2 */ +#define OV2735_REG_V_START OV2735_PAGE_REG16(0x02, 0xa0) +#define OV2735_REG_V_SIZE OV2735_PAGE_REG16(0x02, 0xa2) +#define OV2735_REG_H_START OV2735_PAGE_REG16(0x02, 0xa4) +#define OV2735_REG_H_SIZE OV2735_PAGE_REG16(0x02, 0xa6) + +#define OV2735_LINK_FREQ_420MHZ (420 * HZ_PER_MHZ) +#define OV2735_PIXEL_RATE (168 * HZ_PER_MHZ) + +/* OV2735 native and active pixel array size */ +static const struct v4l2_rect ov2735_native_area = { + .top = 0, + .left = 0, + .width = 1936, + .height = 1096, +}; + +static const struct v4l2_rect ov2735_active_area = { + .top = 8, + .left = 8, + .width = 1920, + .height = 1080, +}; + +static const char * const ov2735_supply_name[] = { + "avdd", /* Analog power */ + "dovdd", /* Digital I/O power */ + "dvdd", /* Digital core power */ +}; + +/* PLL_OUT = [PLL_IN * (pll_nc +3)] / [(pll_mc + 1) * (pll_outdiv + 1)] */ +struct ov2735_pll_parameters { + u8 pll_nc; + u8 pll_mc; + u8 pll_outdiv; +}; + +struct ov2735 { + struct device *dev; + struct regmap *cci; + struct v4l2_subdev sd; + struct media_pad pad; + struct clk *xclk; + struct gpio_desc *reset_gpio; + struct gpio_desc *enable_gpio; + struct regulator_bulk_data supplies[ARRAY_SIZE(ov2735_supply_name)]; + + /* V4L2 Controls */ + struct v4l2_ctrl_handler handler; + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *gain; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *test_pattern; + + u32 link_freq_index; + + u8 current_page; + struct mutex page_lock; +}; + +struct ov2735_mode { + u32 width; + u32 height; + u32 hts_def; + u32 vts_def; + u32 exp_def; + struct v4l2_rect crop; +}; + +static const struct cci_reg_sequence ov2735_common_regs[] = { + { OV2735_REG_CLK_MODE, 0x15 }, + { OV2735_REG_CLOCK_REG1, 0x01 }, + { OV2735_REG_CLOCK_REG2, 0x20 }, + { OV2735_REG_BINNING_DAC_CODE_MODE, 0x00 }, + { OV2735_REG_ABL, 0x73 }, + { OV2735_REG_FRAME_SYNC, 0x01 }, + + /* Timing ctrl */ + { OV2735_REG_TIMING_CTRL2, 0x6b }, + { OV2735_REG_TIMING_CTRL3, 0xea }, + { OV2735_REG_TIMING_CTRL1, 0x0c }, + { OV2735_REG_RST_NUM, 0x0063 }, + { OV2735_REG_RST_NUM2, 0x006f }, + { OV2735_REG_BOOST_EN, 0x02 }, + { OV2735_REG_B2_NUM, 0x0120 }, + { OV2735_REG_B4_NUM, 0x042a }, + { OV2735_REG_PIXEL_CYCLE_P0, 0x00 }, + { OV2735_REG_PIXEL_CYCLE_P1, 0x2c }, + { OV2735_REG_PIXEL_CYCLE_P2, 0x29 }, + { OV2735_REG_PIXEL_CYCLE_P3, 0x00 }, + { OV2735_REG_PIXEL_CYCLE_P5, 0x44 }, + { OV2735_REG_PIXEL_CYCLE_P7, 0x0029 }, + { OV2735_REG_PIXEL_CYCLE_P9, 0x00 }, + { OV2735_REG_PIXEL_CYCLE_P10, 0x00 }, + { OV2735_REG_PIXEL_CYCLE_P12, 0x00 }, + { OV2735_REG_PIXEL_CYCLE_P18, 0x2f }, + { OV2735_REG_PIXEL_CYCLE_P20, 0x62 }, + { OV2735_REG_PIXEL_CYCLE_P22, 0x5b }, + { OV2735_REG_PIXEL_CYCLE_P33, 0x0046 }, + { OV2735_REG_PIXEL_CYCLE_P34, 0x36 }, + { OV2735_REG_PIXEL_CYCLE_P35_P36, 0x4f }, + { OV2735_REG_PIXEL_CYCLE_P37_P38, 0xef }, + { OV2735_REG_PIXEL_CYCLE_P31, 0xcf }, + { OV2735_REG_PIXEL_CYCLE_P32, 0x36 }, + { OV2735_REG_PIXEL_CYCLE_P44, 0x0d }, + { OV2735_REG_PIXEL_CYCLE_P45, 0x0d }, + { OV2735_REG_PIXEL_BIAS_CTRL_RH_RL, 0x77 }, + { OV2735_REG_PIXEL_BIAS_CTRL_SH_SL, 0x77 }, + + /* Analog ctrl */ + { OV2735_REG_ANALOG_CTRL4, 0x5a }, + { OV2735_REG_ANALOG_CTRL5, 0x01 }, + { OV2735_REG_ANALOG_CTRL6, 0xd2 }, + { OV2735_REG_ANALOG_CTRL8, 0x40 }, + { OV2735_REG_PCP_RST_SEL, 0x00 }, + { OV2735_REG_ICOMP, 0xc3 }, + + { OV2735_REG_HS_MIPI, 0x83 }, + { OV2735_REG_MIPI_CTRL5, 0x0b }, + { OV2735_REG_MIPI_CTRL6, 0x14 }, + { OV2735_REG_HIGH_SPEED, 0x40 }, + { OV2735_REG_MIPI_CTRL3, 0x05 }, + { OV2735_REG_MIPI_CTRL2, 0x44 }, + { OV2735_REG_PREPARE, 0x33 }, + { OV2735_REG_R_HS_ZERO, 0x1f }, + { OV2735_REG_TRAIL, 0x45 }, + { OV2735_REG_R_CLK_ZERO, 0x10 }, + { OV2735_REG_MIPI_CTRL7, 0x70 }, + { OV2735_REG_ANALOG_CTRL3, 0xe0 }, + { OV2735_REG_VNCP, 0x7b }, + + /* BLC */ + { OV2735_REG_BLC_GAIN_BLUE, 0x77 }, + { OV2735_REG_BLC_GAIN_GB, 0x77 }, + { OV2735_REG_BLC_GAIN_RED, 0x74 }, + { OV2735_REG_BLC_GAIN_GR, 0x74 }, + { OV2735_REG_BLC_BPC_TH_P, 0xe0 }, + { OV2735_REG_BLC_BPC_TH_N, 0xe0 }, + { OV2735_REG_GB_SUBOFFSET, 0x40 }, + { OV2735_REG_BLUE_SUBOFFSET, 0x40 }, + { OV2735_REG_RED_SUBOFFSET, 0x40 }, + { OV2735_REG_GR_SUBOFFSET, 0x40 }, +}; + +static const struct ov2735_mode supported_modes[] = { + { + .width = 1920, + .height = 1080, + .exp_def = 399, + .hts_def = 2200, + .vts_def = 2545, + .crop = { + .top = 8, + .left = 8, + .width = 1920, + .height = 1080, + }, + }, +}; + +static const s64 link_freq_menu_items[] = { + OV2735_LINK_FREQ_420MHZ, +}; + +static const struct ov2735_pll_parameters pll_configs[] = { + /* For 420MHz pll_configs */ + { + .pll_nc = 4, + .pll_mc = 0, + .pll_outdiv = 1, + }, +}; + +static const char * const ov2735_test_pattern_menu[] = { + "Disabled", + "Vertical Color", +}; + +static int ov2735_page_access(struct ov2735 *ov2735, u32 reg, int *err) +{ + u8 page = reg >> CCI_REG_PRIVATE_SHIFT; + int ret = 0; + + if (err && *err) + return *err; + + guard(mutex)(&ov2735->page_lock); + + /* Perform page access before read/write */ + if (ov2735->current_page == page) + return ret; + + ret = cci_write(ov2735->cci, OV2735_REG_PAGE_SELECT, page, err); + if (!ret) + ov2735->current_page = page; + + return ret; +} + +static int ov2735_read(struct ov2735 *ov2735, u32 reg, u64 *val, int *err) +{ + u32 addr = reg & ~CCI_REG_PRIVATE_MASK; + int ret; + + ret = ov2735_page_access(ov2735, reg, err); + if (ret) + return ret; + + return cci_read(ov2735->cci, addr, val, err); +} + +static int ov2735_write(struct ov2735 *ov2735, u32 reg, u64 val, int *err) +{ + u32 addr = reg & ~CCI_REG_PRIVATE_MASK; + int ret; + + ret = ov2735_page_access(ov2735, reg, err); + if (ret) + return ret; + + return cci_write(ov2735->cci, addr, val, err); +} + +static int ov2735_multi_reg_write(struct ov2735 *ov2735, + const struct cci_reg_sequence *regs, + unsigned int num_regs, int *err) +{ + unsigned int i; + int ret; + + for (i = 0; i < num_regs; i++) { + ret = ov2735_write(ov2735, regs[i].reg, regs[i].val, err); + if (ret) + return ret; + } + + return 0; +} + +static inline struct ov2735 *to_ov2735(struct v4l2_subdev *_sd) +{ + return container_of_const(_sd, struct ov2735, sd); +} + +static int ov2735_enable_test_pattern(struct ov2735 *ov2735, u32 pattern) +{ + int ret; + u64 val; + + ret = ov2735_read(ov2735, OV2735_REG_TEST_PATTERN, &val, NULL); + if (ret) + return ret; + + switch (pattern) { + case 0: + val &= ~OV2735_TEST_PATTERN_ENABLE; + break; + case 1: + val |= OV2735_TEST_PATTERN_ENABLE; + break; + } + + return ov2735_write(ov2735, OV2735_REG_TEST_PATTERN, val, NULL); +} + +static int ov2735_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov2735 *ov2735 = + container_of_const(ctrl->handler, struct ov2735, handler); + struct v4l2_mbus_framefmt *fmt; + struct v4l2_subdev_state *state; + u64 vts; + int ret = 0; + + state = v4l2_subdev_get_locked_active_state(&ov2735->sd); + fmt = v4l2_subdev_state_get_format(state, 0); + + if (ctrl->id == V4L2_CID_VBLANK) { + /* Honour the VBLANK limits when setting exposure */ + s64 max = fmt->height + ctrl->val - OV2735_EXPOSURE_MARGIN; + + ret = __v4l2_ctrl_modify_range(ov2735->exposure, + ov2735->exposure->minimum, max, + ov2735->exposure->step, + ov2735->exposure->default_value); + if (ret) + return ret; + } + + if (pm_runtime_get_if_in_use(ov2735->dev) == 0) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + ov2735_write(ov2735, OV2735_REG_LONG_EXPOSURE, ctrl->val, &ret); + break; + case V4L2_CID_ANALOGUE_GAIN: + ov2735_write(ov2735, OV2735_REG_ANALOG_GAIN, ctrl->val, &ret); + break; + case V4L2_CID_HBLANK: + ov2735_write(ov2735, OV2735_REG_HBLANK, ctrl->val, &ret); + break; + case V4L2_CID_VBLANK: + vts = ctrl->val + fmt->height; + ov2735_write(ov2735, OV2735_REG_FRAME_EXP_SEPERATE_EN, + OV2735_FRAME_EXP_SEPERATE_EN, &ret); + ov2735_write(ov2735, OV2735_REG_FRAME_LENGTH, vts, &ret); + break; + case V4L2_CID_TEST_PATTERN: + ret = ov2735_enable_test_pattern(ov2735, ctrl->val); + break; + default: + ret = -EINVAL; + break; + } + ov2735_write(ov2735, OV2735_REG_FRAME_SYNC, 0x01, &ret); + + pm_runtime_put(ov2735->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops ov2735_ctrl_ops = { + .s_ctrl = ov2735_set_ctrl, +}; + +static int ov2735_init_controls(struct ov2735 *ov2735) +{ + struct v4l2_ctrl_handler *ctrl_hdlr; + struct v4l2_fwnode_device_properties props; + const struct ov2735_mode *mode = &supported_modes[0]; + u64 hblank_def, vblank_def, exp_max; + int ret; + + ctrl_hdlr = &ov2735->handler; + v4l2_ctrl_handler_init(ctrl_hdlr, 9); + + ov2735->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov2735_ctrl_ops, + V4L2_CID_PIXEL_RATE, 0, + OV2735_PIXEL_RATE, 1, + OV2735_PIXEL_RATE); + + ov2735->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &ov2735_ctrl_ops, + V4L2_CID_LINK_FREQ, + ov2735->link_freq_index, + 0, link_freq_menu_items); + if (ov2735->link_freq) + ov2735->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + hblank_def = mode->hts_def - mode->width; + ov2735->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov2735_ctrl_ops, + V4L2_CID_HBLANK, hblank_def, + hblank_def, 1, hblank_def); + + vblank_def = mode->vts_def - mode->height; + ov2735->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov2735_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + OV2735_FRAME_LENGTH_MAX - mode->height, + 1, vblank_def); + + exp_max = mode->vts_def - OV2735_EXPOSURE_MARGIN; + ov2735->exposure = + v4l2_ctrl_new_std(ctrl_hdlr, &ov2735_ctrl_ops, + V4L2_CID_EXPOSURE, + OV2735_EXPOSURE_MIN, exp_max, + OV2735_EXPOSURE_STEP, mode->exp_def); + + ov2735->gain = + v4l2_ctrl_new_std(ctrl_hdlr, &ov2735_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, OV2735_ANALOG_GAIN_MIN, + OV2735_ANALOG_GAIN_MAX, OV2735_ANALOG_GAIN_STEP, + OV2735_ANALOG_GAIN_DEFAULT); + + ov2735->test_pattern = + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &ov2735_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov2735_test_pattern_menu) - 1, + 0, 0, ov2735_test_pattern_menu); + + if (ctrl_hdlr->error) { + ret = ctrl_hdlr->error; + dev_err(ov2735->dev, "control init failed (%d)\n", ret); + goto err_handler_free; + } + + ret = v4l2_fwnode_device_parse(ov2735->dev, &props); + if (ret) + goto err_handler_free; + + ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, + &ov2735_ctrl_ops, &props); + if (ret) + goto err_handler_free; + + ov2735->sd.ctrl_handler = ctrl_hdlr; + + return 0; + +err_handler_free: + v4l2_ctrl_handler_free(ctrl_hdlr); + + return ret; +} + +static int ov2735_set_pll_ctrl(struct ov2735 *ov2735) +{ + const struct ov2735_pll_parameters *pll_parameters; + u8 pll_ctrl; + u8 pll_outdiv; + int ret = 0; + + pll_parameters = &pll_configs[ov2735->link_freq_index]; + + /* BIT[7]: pll_clk_sel, BIT[6:2]: pll_nc, BIT[1:0]: pll_mc */ + pll_ctrl = ((pll_parameters->pll_nc << 2) | (pll_parameters->pll_mc << 0)) & + OV2735_PLL_CTRL_ENABLE; + + pll_outdiv = pll_parameters->pll_outdiv; + + ov2735_write(ov2735, OV2735_REG_PLL_CTRL, pll_ctrl, &ret); + ov2735_write(ov2735, OV2735_REG_PLL_OUTDIV, pll_outdiv, &ret); + + return ret; +} + +static int ov2735_set_framefmt(struct ov2735 *ov2735, + struct v4l2_subdev_state *state) +{ + const struct v4l2_mbus_framefmt *format; + const struct v4l2_rect *crop; + int ret = 0; + + format = v4l2_subdev_state_get_format(state, 0); + crop = v4l2_subdev_state_get_crop(state, 0); + + ov2735_write(ov2735, OV2735_REG_V_START, crop->top, &ret); + ov2735_write(ov2735, OV2735_REG_V_SIZE, format->height, &ret); + ov2735_write(ov2735, OV2735_REG_MIPI_LINE_NUMBER, format->height, &ret); + ov2735_write(ov2735, OV2735_REG_H_START, crop->left, &ret); + /* OV2735_REG_H_SIZE: Image half horizontal size */ + ov2735_write(ov2735, OV2735_REG_H_SIZE, (format->width / 2), &ret); + ov2735_write(ov2735, OV2735_REG_MIPI_COLOMN_NUMBER, format->width, &ret); + + return ret; +} + +static int ov2735_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct ov2735 *ov2735 = to_ov2735(sd); + int ret; + + ret = pm_runtime_resume_and_get(ov2735->dev); + if (ret < 0) + return ret; + + /* Apply pll settings */ + ret = ov2735_set_pll_ctrl(ov2735); + if (ret) { + dev_err(ov2735->dev, "failed to set frame format: %d\n", ret); + goto err_rpm_put; + } + + ret = ov2735_multi_reg_write(ov2735, ov2735_common_regs, + ARRAY_SIZE(ov2735_common_regs), NULL); + if (ret) { + dev_err(ov2735->dev, "failed to write common registers\n"); + goto err_rpm_put; + } + + /* Apply format settings */ + ret = ov2735_set_framefmt(ov2735, state); + if (ret) { + dev_err(ov2735->dev, "failed to set frame format: %d\n", ret); + goto err_rpm_put; + } + + /* Apply customized values from user */ + ret = __v4l2_ctrl_handler_setup(ov2735->sd.ctrl_handler); + if (ret) + goto err_rpm_put; + + ret = ov2735_write(ov2735, OV2735_REG_STREAM_CTRL, + OV2735_STREAM_CTRL_ON, NULL); + if (ret) + goto err_rpm_put; + + return 0; + +err_rpm_put: + pm_runtime_put(ov2735->dev); + return ret; +} + +static int ov2735_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct ov2735 *ov2735 = to_ov2735(sd); + int ret; + + ret = ov2735_write(ov2735, OV2735_REG_STREAM_CTRL, + OV2735_STREAM_CTRL_OFF, NULL); + if (ret) + dev_err(ov2735->dev, "%s failed to set stream\n", __func__); + + pm_runtime_put(ov2735->dev); + + return ret; +} + +static int ov2735_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = *v4l2_subdev_state_get_crop(sd_state, 0); + return 0; + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r = ov2735_native_area; + return 0; + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r = ov2735_active_area; + return 0; + default: + return -EINVAL; + } +} + +static int ov2735_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_SGRBG10_1X10; + + return 0; +} + +static int ov2735_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + 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 int ov2735_set_framing_limits(struct ov2735 *ov2735, + const struct ov2735_mode *mode) +{ + u32 hblank, vblank_def; + int ret; + + hblank = mode->hts_def - mode->width; + ret = __v4l2_ctrl_modify_range(ov2735->hblank, hblank, hblank, 1, + hblank); + if (ret) + return ret; + + vblank_def = mode->vts_def - mode->height; + return __v4l2_ctrl_modify_range(ov2735->vblank, vblank_def, + OV2735_FRAME_LENGTH_MAX - mode->height, + 1, vblank_def); +} + +static int ov2735_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *format; + const struct ov2735_mode *mode; + struct v4l2_rect *crop; + struct ov2735 *ov2735 = to_ov2735(sd); + int ret; + + format = v4l2_subdev_state_get_format(sd_state, 0); + + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), + width, height, + fmt->format.width, fmt->format.height); + + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + fmt->format.colorspace = V4L2_COLORSPACE_RAW; + fmt->format.quantization = V4L2_QUANTIZATION_FULL_RANGE; + fmt->format.xfer_func = V4L2_XFER_FUNC_NONE; + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + ret = ov2735_set_framing_limits(ov2735, mode); + if (ret) + return ret; + } + + *format = fmt->format; + + /* Initialize crop rectangle */ + crop = v4l2_subdev_state_get_crop(sd_state, 0); + *crop = mode->crop; + + return 0; +} + +static int ov2735_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_TRY, + .format = { + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .width = supported_modes[0].width, + .height = supported_modes[0].height, + }, + }; + + ov2735_set_pad_format(sd, state, &fmt); + + return 0; +} + +static const struct v4l2_subdev_video_ops ov2735_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_pad_ops ov2735_pad_ops = { + .enum_mbus_code = ov2735_enum_mbus_code, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = ov2735_set_pad_format, + .get_selection = ov2735_get_selection, + .enum_frame_size = ov2735_enum_frame_size, + .enable_streams = ov2735_enable_streams, + .disable_streams = ov2735_disable_streams, +}; + +static const struct v4l2_subdev_ops ov2735_subdev_ops = { + .video = &ov2735_video_ops, + .pad = &ov2735_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops ov2735_internal_ops = { + .init_state = ov2735_init_state, +}; + +static int ov2735_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov2735 *ov2735 = to_ov2735(sd); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(ov2735_supply_name), + ov2735->supplies); + if (ret) { + dev_err(ov2735->dev, "failed to enable regulators\n"); + return ret; + } + + gpiod_set_value_cansleep(ov2735->enable_gpio, 1); + /* T4: delay from PWDN pulling low to RSTB pulling high */ + fsleep(4 * USEC_PER_MSEC); + + ret = clk_prepare_enable(ov2735->xclk); + if (ret) { + dev_err(ov2735->dev, "failed to enable clock\n"); + goto err_regulator_off; + } + + gpiod_set_value_cansleep(ov2735->reset_gpio, 0); + /* T5: delay from RSTB pulling high to first I2C command */ + fsleep(5 * USEC_PER_MSEC); + + return 0; + +err_regulator_off: + regulator_bulk_disable(ARRAY_SIZE(ov2735_supply_name), ov2735->supplies); + return ret; +} + +static int ov2735_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov2735 *ov2735 = to_ov2735(sd); + + gpiod_set_value_cansleep(ov2735->enable_gpio, 0); + clk_disable_unprepare(ov2735->xclk); + gpiod_set_value_cansleep(ov2735->reset_gpio, 1); + regulator_bulk_disable(ARRAY_SIZE(ov2735_supply_name), ov2735->supplies); + + return 0; +} + +static int ov2735_get_regulators(struct ov2735 *ov2735) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(ov2735_supply_name); i++) + ov2735->supplies[i].supply = ov2735_supply_name[i]; + + return devm_regulator_bulk_get(ov2735->dev, + ARRAY_SIZE(ov2735_supply_name), + ov2735->supplies); +} + +static int ov2735_identify_module(struct ov2735 *ov2735) +{ + u64 chip_id; + int ret; + + ret = ov2735_read(ov2735, OV2735_REG_CHIPID, &chip_id, NULL); + if (ret) + return dev_err_probe(ov2735->dev, ret, + "failed to read chip id %x\n", + OV2735_CHIPID); + + if (chip_id != OV2735_CHIPID) + return dev_err_probe(ov2735->dev, -EIO, + "chip id mismatch: %x!=%llx\n", + OV2735_CHIPID, chip_id); + + return 0; +} + +static int ov2735_parse_endpoint(struct ov2735 *ov2735) +{ + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; + struct fwnode_handle *ep; + unsigned long link_freq_bitmap; + int ret; + + ep = fwnode_graph_get_next_endpoint(dev_fwnode(ov2735->dev), NULL); + if (!ep) + return dev_err_probe(ov2735->dev, -ENXIO, + "Failed to get next endpoint\n"); + + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + fwnode_handle_put(ep); + if (ret) + return ret; + + if (bus_cfg.bus.mipi_csi2.num_data_lanes != 2) { + ret = dev_err_probe(ov2735->dev, -EINVAL, + "only 2 data lanes are supported\n"); + goto error_out; + } + + ret = v4l2_link_freq_to_bitmap(ov2735->dev, bus_cfg.link_frequencies, + bus_cfg.nr_of_link_frequencies, + link_freq_menu_items, + ARRAY_SIZE(link_freq_menu_items), + &link_freq_bitmap); + if (ret) { + ret = dev_err_probe(ov2735->dev, -EINVAL, + "only 420MHz frequency is available\n"); + goto error_out; + } + + ov2735->link_freq_index = __ffs(link_freq_bitmap); + +error_out: + v4l2_fwnode_endpoint_free(&bus_cfg); + + return ret; +}; + +static int ov2735_probe(struct i2c_client *client) +{ + struct ov2735 *ov2735; + unsigned int xclk_freq; + int ret; + + ov2735 = devm_kzalloc(&client->dev, sizeof(*ov2735), GFP_KERNEL); + if (!ov2735) + return -ENOMEM; + + ov2735->dev = &client->dev; + + v4l2_i2c_subdev_init(&ov2735->sd, client, &ov2735_subdev_ops); + ov2735->sd.internal_ops = &ov2735_internal_ops; + + ov2735->cci = devm_cci_regmap_init_i2c(client, 8); + if (IS_ERR(ov2735->cci)) + return dev_err_probe(ov2735->dev, PTR_ERR(ov2735->cci), + "failed to initialize CCI\n"); + + /* Set Current page to 0 */ + ov2735->current_page = 0; + + ret = devm_mutex_init(ov2735->dev, &ov2735->page_lock); + if (ret) + return dev_err_probe(ov2735->dev, ret, + "Failed to initialize lock\n"); + + /* Get system clock (xvclk) */ + ov2735->xclk = devm_v4l2_sensor_clk_get(ov2735->dev, NULL); + if (IS_ERR(ov2735->xclk)) + return dev_err_probe(ov2735->dev, PTR_ERR(ov2735->xclk), + "failed to get xclk\n"); + + xclk_freq = clk_get_rate(ov2735->xclk); + if (xclk_freq != OV2735_XCLK_FREQ) + return dev_err_probe(ov2735->dev, -EINVAL, + "xclk frequency not supported: %u Hz\n", + xclk_freq); + + ret = ov2735_get_regulators(ov2735); + if (ret) + return dev_err_probe(ov2735->dev, ret, + "failed to get regulators\n"); + + ret = ov2735_parse_endpoint(ov2735); + if (ret) + return dev_err_probe(ov2735->dev, ret, + "failed to parse endpoint configuration\n"); + + ov2735->reset_gpio = devm_gpiod_get_optional(ov2735->dev, + "reset", GPIOD_OUT_LOW); + if (IS_ERR(ov2735->reset_gpio)) + return dev_err_probe(ov2735->dev, PTR_ERR(ov2735->reset_gpio), + "failed to get reset GPIO\n"); + + ov2735->enable_gpio = devm_gpiod_get_optional(ov2735->dev, + "enable", GPIOD_OUT_LOW); + if (IS_ERR(ov2735->enable_gpio)) + return dev_err_probe(ov2735->dev, PTR_ERR(ov2735->enable_gpio), + "failed to get enable GPIO\n"); + + ret = ov2735_power_on(ov2735->dev); + if (ret) + return ret; + + ret = ov2735_identify_module(ov2735); + if (ret) + goto error_power_off; + + ret = ov2735_init_controls(ov2735); + if (ret) + goto error_power_off; + + /* Initialize subdev */ + ov2735->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + ov2735->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + ov2735->pad.flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&ov2735->sd.entity, 1, &ov2735->pad); + if (ret) { + dev_err_probe(ov2735->dev, ret, "failed to init entity pads\n"); + goto error_handler_free; + } + + ov2735->sd.state_lock = ov2735->handler.lock; + ret = v4l2_subdev_init_finalize(&ov2735->sd); + if (ret) { + dev_err_probe(ov2735->dev, ret, "subdev init error\n"); + goto error_media_entity; + } + + ret = devm_pm_runtime_get_noresume(ov2735->dev); + if (ret) { + dev_err_probe(ov2735->dev, ret, + "failed to get runtime PM noresume\n"); + goto error_subdev_cleanup; + } + + ret = devm_pm_runtime_set_active_enabled(ov2735->dev); + if (ret) { + dev_err_probe(ov2735->dev, ret, + "failed to set runtime PM active+enabled\n"); + goto error_subdev_cleanup; + } + + ret = v4l2_async_register_subdev_sensor(&ov2735->sd); + if (ret) { + dev_err_probe(ov2735->dev, ret, + "failed to register ov2735 sub-device\n"); + goto error_subdev_cleanup; + } + + return 0; + +error_subdev_cleanup: + v4l2_subdev_cleanup(&ov2735->sd); + +error_media_entity: + media_entity_cleanup(&ov2735->sd.entity); + +error_handler_free: + v4l2_ctrl_handler_free(ov2735->sd.ctrl_handler); + +error_power_off: + ov2735_power_off(ov2735->dev); + + return ret; +} + +static void ov2735_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov2735 *ov2735 = to_ov2735(sd); + + v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(&ov2735->sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(ov2735->sd.ctrl_handler); +} + +static DEFINE_RUNTIME_DEV_PM_OPS(ov2735_pm_ops, + ov2735_power_off, ov2735_power_on, NULL); + +static const struct of_device_id ov2735_id[] = { + { .compatible = "ovti,ov2735" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ov2735_id); + +static struct i2c_driver ov2735_driver = { + .driver = { + .name = "ov2735", + .pm = pm_ptr(&ov2735_pm_ops), + .of_match_table = ov2735_id, + }, + .probe = ov2735_probe, + .remove = ov2735_remove, +}; +module_i2c_driver(ov2735_driver); + +MODULE_DESCRIPTION("OV2735 Camera Sensor Driver"); +MODULE_AUTHOR("Hardevsinh Palaniya <hardevsinh.palaniya@siliconsignals.io>"); +MODULE_AUTHOR("Himanshu Bhavani <himanshu.bhavani@siliconsignals.io>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c index c484b753a718..fb590dfadda1 100644 --- a/drivers/media/i2c/ov2740.c +++ b/drivers/media/i2c/ov2740.c @@ -1,16 +1,17 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2020 Intel Corporation. -#include <linux/unaligned.h> #include <linux/acpi.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/module.h> -#include <linux/pm_runtime.h> #include <linux/nvmem-provider.h> +#include <linux/pm_runtime.h> #include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/unaligned.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-fwnode.h> @@ -76,6 +77,12 @@ /* OTP registers from sensor */ #define OV2740_REG_OTP_CUSTOMER 0x7010 +static const char * const ov2740_supply_name[] = { + "AVDD", + "DOVDD", + "DVDD", +}; + struct nvm_data { struct nvmem_device *nvmem; struct regmap *regmap; @@ -512,6 +519,8 @@ static const struct ov2740_mode supported_modes_180mhz[] = { }; struct ov2740 { + struct device *dev; + struct v4l2_subdev sd; struct media_pad pad; struct v4l2_ctrl_handler ctrl_handler; @@ -523,9 +532,11 @@ struct ov2740 { struct v4l2_ctrl *hblank; struct v4l2_ctrl *exposure; - /* GPIOs, clocks */ + /* GPIOs, clocks, regulators */ struct gpio_desc *reset_gpio; + struct gpio_desc *powerdown_gpio; struct clk *clk; + struct regulator_bulk_data supplies[ARRAY_SIZE(ov2740_supply_name)]; /* Current mode */ const struct ov2740_mode *cur_mode; @@ -607,7 +618,6 @@ static int ov2740_write_reg(struct ov2740 *ov2740, u16 reg, u16 len, u32 val) static int ov2740_write_reg_list(struct ov2740 *ov2740, const struct ov2740_reg_list *r_list) { - struct i2c_client *client = v4l2_get_subdevdata(&ov2740->sd); unsigned int i; int ret; @@ -615,7 +625,7 @@ static int ov2740_write_reg_list(struct ov2740 *ov2740, ret = ov2740_write_reg(ov2740, r_list->regs[i].address, 1, r_list->regs[i].val); if (ret) { - dev_err_ratelimited(&client->dev, + dev_err_ratelimited(ov2740->dev, "write reg 0x%4.4x return err = %d\n", r_list->regs[i].address, ret); return ret; @@ -627,7 +637,6 @@ static int ov2740_write_reg_list(struct ov2740 *ov2740, static int ov2740_identify_module(struct ov2740 *ov2740) { - struct i2c_client *client = v4l2_get_subdevdata(&ov2740->sd); int ret; u32 val; @@ -639,11 +648,13 @@ static int ov2740_identify_module(struct ov2740 *ov2740) return ret; if (val != OV2740_CHIP_ID) { - dev_err(&client->dev, "chip id mismatch: %x != %x\n", + dev_err(ov2740->dev, "chip id mismatch: %x != %x\n", OV2740_CHIP_ID, val); return -ENXIO; } + dev_dbg(ov2740->dev, "chip id: 0x%x\n", val); + ov2740->identified = true; return 0; @@ -693,7 +704,6 @@ static int ov2740_set_ctrl(struct v4l2_ctrl *ctrl) { struct ov2740 *ov2740 = container_of(ctrl->handler, struct ov2740, ctrl_handler); - struct i2c_client *client = v4l2_get_subdevdata(&ov2740->sd); s64 exposure_max; int ret; @@ -709,7 +719,7 @@ static int ov2740_set_ctrl(struct v4l2_ctrl *ctrl) } /* V4L2 controls values will be applied only when power is already up */ - if (!pm_runtime_get_if_in_use(&client->dev)) + if (!pm_runtime_get_if_in_use(ov2740->dev)) return 0; switch (ctrl->id) { @@ -742,7 +752,7 @@ static int ov2740_set_ctrl(struct v4l2_ctrl *ctrl) break; } - pm_runtime_put(&client->dev); + pm_runtime_put(ov2740->dev); return ret; } @@ -754,23 +764,20 @@ static const struct v4l2_ctrl_ops ov2740_ctrl_ops = { static int ov2740_init_controls(struct ov2740 *ov2740) { struct v4l2_ctrl_handler *ctrl_hdlr; - const struct ov2740_mode *cur_mode; s64 exposure_max, h_blank, pixel_rate; u32 vblank_min, vblank_max, vblank_default; - int size; + struct v4l2_fwnode_device_properties props; int ret; ctrl_hdlr = &ov2740->ctrl_handler; - ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8); + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 10); if (ret) return ret; - cur_mode = ov2740->cur_mode; - size = ARRAY_SIZE(link_freq_menu_items); - ov2740->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &ov2740_ctrl_ops, - V4L2_CID_LINK_FREQ, size - 1, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(link_freq_menu_items) - 1, ov2740->supported_modes->link_freq_index, link_freq_menu_items); if (ov2740->link_freq) @@ -781,14 +788,14 @@ static int ov2740_init_controls(struct ov2740 *ov2740) V4L2_CID_PIXEL_RATE, 0, pixel_rate, 1, pixel_rate); - vblank_min = cur_mode->vts_min - cur_mode->height; - vblank_max = cur_mode->vts_max - cur_mode->height; - vblank_default = cur_mode->vts_def - cur_mode->height; + vblank_min = ov2740->cur_mode->vts_min - ov2740->cur_mode->height; + vblank_max = ov2740->cur_mode->vts_max - ov2740->cur_mode->height; + vblank_default = ov2740->cur_mode->vts_def - ov2740->cur_mode->height; ov2740->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops, V4L2_CID_VBLANK, vblank_min, vblank_max, 1, vblank_default); - h_blank = cur_mode->hts - cur_mode->width; + h_blank = ov2740->cur_mode->hts - ov2740->cur_mode->width; ov2740->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops, V4L2_CID_HBLANK, h_blank, h_blank, 1, h_blank); @@ -801,7 +808,7 @@ static int ov2740_init_controls(struct ov2740 *ov2740) v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops, V4L2_CID_DIGITAL_GAIN, OV2740_DGTL_GAIN_MIN, OV2740_DGTL_GAIN_MAX, OV2740_DGTL_GAIN_STEP, OV2740_DGTL_GAIN_DEFAULT); - exposure_max = cur_mode->vts_def - OV2740_EXPOSURE_MAX_MARGIN; + exposure_max = ov2740->cur_mode->vts_def - OV2740_EXPOSURE_MAX_MARGIN; ov2740->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops, V4L2_CID_EXPOSURE, OV2740_EXPOSURE_MIN, exposure_max, @@ -811,6 +818,15 @@ static int ov2740_init_controls(struct ov2740 *ov2740) V4L2_CID_TEST_PATTERN, ARRAY_SIZE(ov2740_test_pattern_menu) - 1, 0, 0, ov2740_test_pattern_menu); + + ret = v4l2_fwnode_device_parse(ov2740->dev, &props); + if (ret) { + v4l2_ctrl_handler_free(ctrl_hdlr); + return ret; + } + + v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &ov2740_ctrl_ops, &props); + if (ctrl_hdlr->error) { v4l2_ctrl_handler_free(ctrl_hdlr); return ctrl_hdlr->error; @@ -922,7 +938,6 @@ err: static int ov2740_start_streaming(struct ov2740 *ov2740) { - struct i2c_client *client = v4l2_get_subdevdata(&ov2740->sd); const struct ov2740_reg_list *reg_list; int link_freq_index; int ret; @@ -937,7 +952,7 @@ static int ov2740_start_streaming(struct ov2740 *ov2740) /* Reset the sensor */ ret = ov2740_write_reg(ov2740, 0x0103, 1, 0x01); if (ret) { - dev_err(&client->dev, "failed to reset\n"); + dev_err(ov2740->dev, "failed to reset\n"); return ret; } @@ -947,14 +962,14 @@ static int ov2740_start_streaming(struct ov2740 *ov2740) reg_list = &link_freq_configs[link_freq_index].reg_list; ret = ov2740_write_reg_list(ov2740, reg_list); if (ret) { - dev_err(&client->dev, "failed to set plls\n"); + dev_err(ov2740->dev, "failed to set plls\n"); return ret; } reg_list = &ov2740->cur_mode->reg_list; ret = ov2740_write_reg_list(ov2740, reg_list); if (ret) { - dev_err(&client->dev, "failed to set mode\n"); + dev_err(ov2740->dev, "failed to set mode\n"); return ret; } @@ -965,31 +980,28 @@ static int ov2740_start_streaming(struct ov2740 *ov2740) ret = ov2740_write_reg(ov2740, OV2740_REG_MODE_SELECT, 1, OV2740_MODE_STREAMING); if (ret) - dev_err(&client->dev, "failed to start streaming\n"); + dev_err(ov2740->dev, "failed to start streaming\n"); return ret; } static void ov2740_stop_streaming(struct ov2740 *ov2740) { - struct i2c_client *client = v4l2_get_subdevdata(&ov2740->sd); - if (ov2740_write_reg(ov2740, OV2740_REG_MODE_SELECT, 1, OV2740_MODE_STANDBY)) - dev_err(&client->dev, "failed to stop streaming\n"); + dev_err(ov2740->dev, "failed to stop streaming\n"); } static int ov2740_set_stream(struct v4l2_subdev *sd, int enable) { struct ov2740 *ov2740 = to_ov2740(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); struct v4l2_subdev_state *sd_state; int ret = 0; sd_state = v4l2_subdev_lock_and_get_active_state(&ov2740->sd); if (enable) { - ret = pm_runtime_resume_and_get(&client->dev); + ret = pm_runtime_resume_and_get(ov2740->dev); if (ret < 0) goto out_unlock; @@ -997,11 +1009,11 @@ static int ov2740_set_stream(struct v4l2_subdev *sd, int enable) if (ret) { enable = 0; ov2740_stop_streaming(ov2740); - pm_runtime_put(&client->dev); + pm_runtime_put(ov2740->dev); } } else { ov2740_stop_streaming(ov2740); - pm_runtime_put(&client->dev); + pm_runtime_put(ov2740->dev); } out_unlock: @@ -1113,16 +1125,14 @@ static const struct media_entity_operations ov2740_subdev_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; -static int ov2740_check_hwcfg(struct device *dev) +static int ov2740_check_hwcfg(struct ov2740 *ov2740) { - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct ov2740 *ov2740 = to_ov2740(sd); + struct device *dev = ov2740->dev; struct fwnode_handle *ep; struct fwnode_handle *fwnode = dev_fwnode(dev); struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = V4L2_MBUS_CSI2_DPHY }; - u32 mclk; int ret; unsigned int i, j; @@ -1135,20 +1145,6 @@ static int ov2740_check_hwcfg(struct device *dev) return dev_err_probe(dev, -EPROBE_DEFER, "waiting for fwnode graph endpoint\n"); - ret = fwnode_property_read_u32(fwnode, "clock-frequency", &mclk); - if (ret) { - fwnode_handle_put(ep); - return dev_err_probe(dev, ret, - "reading clock-frequency property\n"); - } - - if (mclk != OV2740_MCLK) { - fwnode_handle_put(ep); - return dev_err_probe(dev, -EINVAL, - "external clock %d is not supported\n", - mclk); - } - ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); fwnode_handle_put(ep); if (ret) @@ -1252,7 +1248,7 @@ static int ov2740_register_nvmem(struct i2c_client *client, struct regmap_config regmap_config = { }; struct nvmem_config nvmem_config = { }; struct regmap *regmap; - struct device *dev = &client->dev; + struct device *dev = ov2740->dev; nvm = devm_kzalloc(dev, sizeof(*nvm), GFP_KERNEL); if (!nvm) @@ -1295,7 +1291,10 @@ static int ov2740_suspend(struct device *dev) struct ov2740 *ov2740 = to_ov2740(sd); gpiod_set_value_cansleep(ov2740->reset_gpio, 1); + gpiod_set_value_cansleep(ov2740->powerdown_gpio, 1); clk_disable_unprepare(ov2740->clk); + regulator_bulk_disable(ARRAY_SIZE(ov2740_supply_name), + ov2740->supplies); return 0; } @@ -1305,10 +1304,19 @@ static int ov2740_resume(struct device *dev) struct ov2740 *ov2740 = to_ov2740(sd); int ret; - ret = clk_prepare_enable(ov2740->clk); + ret = regulator_bulk_enable(ARRAY_SIZE(ov2740_supply_name), + ov2740->supplies); if (ret) return ret; + ret = clk_prepare_enable(ov2740->clk); + if (ret) { + regulator_bulk_disable(ARRAY_SIZE(ov2740_supply_name), + ov2740->supplies); + return ret; + } + + gpiod_set_value_cansleep(ov2740->powerdown_gpio, 0); gpiod_set_value_cansleep(ov2740->reset_gpio, 0); msleep(20); @@ -1319,17 +1327,21 @@ static int ov2740_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct ov2740 *ov2740; + unsigned long freq; bool full_power; + unsigned int i; int ret; ov2740 = devm_kzalloc(&client->dev, sizeof(*ov2740), GFP_KERNEL); if (!ov2740) return -ENOMEM; + ov2740->dev = &client->dev; + v4l2_i2c_subdev_init(&ov2740->sd, client, &ov2740_subdev_ops); ov2740->sd.internal_ops = &ov2740_internal_ops; - ret = ov2740_check_hwcfg(dev); + ret = ov2740_check_hwcfg(ov2740); if (ret) return ret; @@ -1337,20 +1349,42 @@ static int ov2740_probe(struct i2c_client *client) if (IS_ERR(ov2740->reset_gpio)) { return dev_err_probe(dev, PTR_ERR(ov2740->reset_gpio), "failed to get reset GPIO\n"); - } else if (ov2740->reset_gpio) { + } + + ov2740->powerdown_gpio = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_HIGH); + if (IS_ERR(ov2740->powerdown_gpio)) { + return dev_err_probe(dev, PTR_ERR(ov2740->powerdown_gpio), + "failed to get powerdown GPIO\n"); + } + + if (ov2740->reset_gpio || ov2740->powerdown_gpio) { /* - * Ensure reset is asserted for at least 20 ms before + * Ensure reset/powerdown is asserted for at least 20 ms before * ov2740_resume() deasserts it. */ msleep(20); } - ov2740->clk = devm_clk_get_optional(dev, "clk"); + ov2740->clk = devm_v4l2_sensor_clk_get(dev, "clk"); if (IS_ERR(ov2740->clk)) return dev_err_probe(dev, PTR_ERR(ov2740->clk), "failed to get clock\n"); - full_power = acpi_dev_state_d0(&client->dev); + freq = clk_get_rate(ov2740->clk); + if (freq != OV2740_MCLK) + return dev_err_probe(dev, -EINVAL, + "external clock %lu is not supported\n", + freq); + + for (i = 0; i < ARRAY_SIZE(ov2740_supply_name); i++) + ov2740->supplies[i].supply = ov2740_supply_name[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ov2740_supply_name), + ov2740->supplies); + if (ret) + return dev_err_probe(dev, ret, "failed to get regulators\n"); + + full_power = acpi_dev_state_d0(ov2740->dev); if (full_power) { /* ACPI does not always clear the reset GPIO / enable the clock */ ret = ov2740_resume(dev); @@ -1388,9 +1422,9 @@ static int ov2740_probe(struct i2c_client *client) /* Set the device's state to active if it's in D0 state. */ if (full_power) - pm_runtime_set_active(&client->dev); - pm_runtime_enable(&client->dev); - pm_runtime_idle(&client->dev); + pm_runtime_set_active(ov2740->dev); + pm_runtime_enable(ov2740->dev); + pm_runtime_idle(ov2740->dev); ret = v4l2_async_register_subdev_sensor(&ov2740->sd); if (ret < 0) { @@ -1400,17 +1434,17 @@ static int ov2740_probe(struct i2c_client *client) ret = ov2740_register_nvmem(client, ov2740); if (ret) - dev_warn(&client->dev, "register nvmem failed, ret %d\n", ret); + dev_warn(ov2740->dev, "register nvmem failed, ret %d\n", ret); return 0; probe_error_v4l2_subdev_cleanup: + pm_runtime_disable(ov2740->dev); + pm_runtime_set_suspended(ov2740->dev); v4l2_subdev_cleanup(&ov2740->sd); probe_error_media_entity_cleanup: media_entity_cleanup(&ov2740->sd.entity); - pm_runtime_disable(&client->dev); - pm_runtime_set_suspended(&client->dev); probe_error_v4l2_ctrl_handler_free: v4l2_ctrl_handler_free(ov2740->sd.ctrl_handler); diff --git a/drivers/media/i2c/ov4689.c b/drivers/media/i2c/ov4689.c index 1c3a449f9354..a59d25b09b5b 100644 --- a/drivers/media/i2c/ov4689.c +++ b/drivers/media/i2c/ov4689.c @@ -497,7 +497,6 @@ static int ov4689_s_stream(struct v4l2_subdev *sd, int on) } else { cci_write(ov4689->regmap, OV4689_REG_CTRL_MODE, OV4689_MODE_SW_STANDBY, NULL); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); } @@ -702,7 +701,6 @@ static int ov4689_set_ctrl(struct v4l2_ctrl *ctrl) break; } - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return ret; @@ -909,20 +907,12 @@ static int ov4689_probe(struct i2c_client *client) ov4689->cur_mode = &supported_modes[OV4689_MODE_2688_1520]; - ov4689->xvclk = devm_clk_get_optional(dev, NULL); + ov4689->xvclk = devm_v4l2_sensor_clk_get(dev, NULL); if (IS_ERR(ov4689->xvclk)) return dev_err_probe(dev, PTR_ERR(ov4689->xvclk), "Failed to get external clock\n"); - if (!ov4689->xvclk) { - dev_dbg(dev, - "No clock provided, using clock-frequency property\n"); - device_property_read_u32(dev, "clock-frequency", - &ov4689->clock_rate); - } else { - ov4689->clock_rate = clk_get_rate(ov4689->xvclk); - } - + ov4689->clock_rate = clk_get_rate(ov4689->xvclk); if (ov4689->clock_rate != OV4689_XVCLK_FREQ) { dev_err(dev, "External clock rate mismatch: got %d Hz, expected %d Hz\n", @@ -999,7 +989,6 @@ static int ov4689_probe(struct i2c_client *client) goto err_clean_subdev_pm; } - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return 0; diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index da5cb5f45a4f..85ecc23b3587 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -1982,6 +1982,7 @@ static int ov5640_get_light_freq(struct ov5640_dev *sensor) light_freq = 50; } else { /* 60Hz */ + light_freq = 60; } } @@ -3340,7 +3341,6 @@ static int ov5640_g_volatile_ctrl(struct v4l2_ctrl *ctrl) break; } - pm_runtime_mark_last_busy(&sensor->i2c_client->dev); pm_runtime_put_autosuspend(&sensor->i2c_client->dev); return 0; @@ -3416,7 +3416,6 @@ static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl) break; } - pm_runtime_mark_last_busy(&sensor->i2c_client->dev); pm_runtime_put_autosuspend(&sensor->i2c_client->dev); return ret; @@ -3753,7 +3752,6 @@ out: mutex_unlock(&sensor->lock); if (!enable || ret) { - pm_runtime_mark_last_busy(&sensor->i2c_client->dev); pm_runtime_put_autosuspend(&sensor->i2c_client->dev); } @@ -3897,11 +3895,10 @@ static int ov5640_probe(struct i2c_client *client) ov5640_dvp_default_fmt; /* get system clock (xclk) */ - sensor->xclk = devm_clk_get(dev, "xclk"); - if (IS_ERR(sensor->xclk)) { - dev_err(dev, "failed to get xclk\n"); - return PTR_ERR(sensor->xclk); - } + sensor->xclk = devm_v4l2_sensor_clk_get(dev, "xclk"); + if (IS_ERR(sensor->xclk)) + return dev_err_probe(dev, PTR_ERR(sensor->xclk), + "failed to get xclk\n"); sensor->xclk_freq = clk_get_rate(sensor->xclk); if (sensor->xclk_freq < OV5640_XCLK_MIN || @@ -3964,7 +3961,6 @@ static int ov5640_probe(struct i2c_client *client) pm_runtime_set_autosuspend_delay(dev, 1000); pm_runtime_use_autosuspend(dev); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return 0; diff --git a/drivers/media/i2c/ov5645.c b/drivers/media/i2c/ov5645.c index 004d0ee5c3f5..b10d408034a1 100644 --- a/drivers/media/i2c/ov5645.c +++ b/drivers/media/i2c/ov5645.c @@ -808,7 +808,6 @@ static int ov5645_s_ctrl(struct v4l2_ctrl *ctrl) break; } - pm_runtime_mark_last_busy(ov5645->dev); pm_runtime_put_autosuspend(ov5645->dev); return ret; @@ -979,7 +978,6 @@ static int ov5645_disable_streams(struct v4l2_subdev *sd, OV5645_SYSTEM_CTRL0_STOP); rpm_put: - pm_runtime_mark_last_busy(ov5645->dev); pm_runtime_put_autosuspend(ov5645->dev); return ret; @@ -1044,27 +1042,18 @@ static int ov5645_probe(struct i2c_client *client) "invalid bus type, must be CSI2\n"); /* get system clock (xclk) */ - ov5645->xclk = devm_clk_get(dev, NULL); + ov5645->xclk = devm_v4l2_sensor_clk_get_legacy(dev, NULL, false, 0); if (IS_ERR(ov5645->xclk)) return dev_err_probe(dev, PTR_ERR(ov5645->xclk), "could not get xclk"); - ret = of_property_read_u32(dev->of_node, "clock-frequency", &xclk_freq); - if (ret) - return dev_err_probe(dev, ret, - "could not get xclk frequency\n"); - /* external clock must be 24MHz, allow 1% tolerance */ + xclk_freq = clk_get_rate(ov5645->xclk); if (xclk_freq < 23760000 || xclk_freq > 24240000) return dev_err_probe(dev, -EINVAL, "unsupported xclk frequency %u\n", xclk_freq); - ret = clk_set_rate(ov5645->xclk, xclk_freq); - if (ret) - return dev_err_probe(dev, ret, - "could not set xclk frequency\n"); - for (i = 0; i < OV5645_NUM_SUPPLIES; i++) ov5645->supplies[i].supply = ov5645_supply_name[i]; @@ -1196,7 +1185,6 @@ static int ov5645_probe(struct i2c_client *client) pm_runtime_set_autosuspend_delay(dev, 1000); pm_runtime_use_autosuspend(dev); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return 0; diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index a727beb9d57e..e193fef4fced 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -1398,11 +1398,10 @@ static int ov5647_probe(struct i2c_client *client) } } - sensor->xclk = devm_clk_get(dev, NULL); - if (IS_ERR(sensor->xclk)) { - dev_err(dev, "could not get xclk"); - return PTR_ERR(sensor->xclk); - } + sensor->xclk = devm_v4l2_sensor_clk_get(dev, NULL); + if (IS_ERR(sensor->xclk)) + return dev_err_probe(dev, PTR_ERR(sensor->xclk), + "could not get xclk\n"); xclk_freq = clk_get_rate(sensor->xclk); if (xclk_freq != 25000000) { diff --git a/drivers/media/i2c/ov5648.c b/drivers/media/i2c/ov5648.c index 4b86d2631bd1..f0b839cd65f1 100644 --- a/drivers/media/i2c/ov5648.c +++ b/drivers/media/i2c/ov5648.c @@ -1061,8 +1061,8 @@ static int ov5648_sw_standby(struct ov5648_sensor *sensor, int standby) static int ov5648_chip_id_check(struct ov5648_sensor *sensor) { - u16 regs[] = { OV5648_CHIP_ID_H_REG, OV5648_CHIP_ID_L_REG }; - u8 values[] = { OV5648_CHIP_ID_H_VALUE, OV5648_CHIP_ID_L_VALUE }; + static const u16 regs[] = { OV5648_CHIP_ID_H_REG, OV5648_CHIP_ID_L_REG }; + static const u8 values[] = { OV5648_CHIP_ID_H_VALUE, OV5648_CHIP_ID_L_VALUE }; unsigned int i; u8 value; int ret; @@ -2521,10 +2521,10 @@ static int ov5648_probe(struct i2c_client *client) /* External Clock */ - sensor->xvclk = devm_clk_get(dev, NULL); + sensor->xvclk = devm_v4l2_sensor_clk_get(dev, NULL); if (IS_ERR(sensor->xvclk)) { - dev_err(dev, "failed to get external clock\n"); - ret = PTR_ERR(sensor->xvclk); + ret = dev_err_probe(dev, PTR_ERR(sensor->xvclk), + "failed to get external clock\n"); goto error_endpoint; } diff --git a/drivers/media/i2c/ov5670.c b/drivers/media/i2c/ov5670.c index c54bbc207189..04b3183b7bcb 100644 --- a/drivers/media/i2c/ov5670.c +++ b/drivers/media/i2c/ov5670.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2017 Intel Corporation. -#include <linux/unaligned.h> #include <linux/acpi.h> #include <linux/clk.h> #include <linux/delay.h> @@ -12,6 +11,8 @@ #include <linux/of.h> #include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> +#include <linux/unaligned.h> + #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-event.h> @@ -1854,6 +1855,8 @@ static const struct ov5670_mode supported_modes[] = { }; struct ov5670 { + struct device *dev; + struct v4l2_subdev sd; struct media_pad pad; struct v4l2_fwnode_endpoint endpoint; @@ -1959,7 +1962,6 @@ static int ov5670_write_reg(struct ov5670 *ov5670, u16 reg, unsigned int len, static int ov5670_write_regs(struct ov5670 *ov5670, const struct ov5670_reg *regs, unsigned int len) { - struct i2c_client *client = v4l2_get_subdevdata(&ov5670->sd); unsigned int i; int ret; @@ -1967,7 +1969,7 @@ static int ov5670_write_regs(struct ov5670 *ov5670, ret = ov5670_write_reg(ov5670, regs[i].address, 1, regs[i].val); if (ret) { dev_err_ratelimited( - &client->dev, + ov5670->dev, "Failed to write reg 0x%4.4x. error = %d\n", regs[i].address, ret); @@ -2032,7 +2034,6 @@ 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); s64 max; int ret; @@ -2048,7 +2049,7 @@ static int ov5670_set_ctrl(struct v4l2_ctrl *ctrl) } /* V4L2 controls values will be applied only when power is already up */ - if (!pm_runtime_get_if_in_use(&client->dev)) + if (!pm_runtime_get_if_in_use(ov5670->dev)) return 0; switch (ctrl->id) { @@ -2080,12 +2081,12 @@ static int ov5670_set_ctrl(struct v4l2_ctrl *ctrl) break; default: ret = -EINVAL; - dev_info(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", + dev_info(ov5670->dev, "%s Unhandled id:0x%x, val:0x%x\n", __func__, ctrl->id, ctrl->val); break; } - pm_runtime_put(&client->dev); + pm_runtime_put(ov5670->dev); return ret; } @@ -2099,7 +2100,6 @@ static int ov5670_init_controls(struct ov5670 *ov5670) { struct v4l2_mbus_config_mipi_csi2 *bus_mipi_csi2 = &ov5670->endpoint.bus.mipi_csi2; - struct i2c_client *client = v4l2_get_subdevdata(&ov5670->sd); struct v4l2_fwnode_device_properties props; struct v4l2_ctrl_handler *ctrl_hdlr; unsigned int lanes_count; @@ -2177,7 +2177,7 @@ static int ov5670_init_controls(struct ov5670 *ov5670) goto error; } - ret = v4l2_fwnode_device_parse(&client->dev, &props); + ret = v4l2_fwnode_device_parse(ov5670->dev, &props); if (ret) goto error; @@ -2350,7 +2350,6 @@ static int ov5670_get_skip_frames(struct v4l2_subdev *sd, u32 *frames) /* Verify chip ID */ static int ov5670_identify_module(struct ov5670 *ov5670) { - struct i2c_client *client = v4l2_get_subdevdata(&ov5670->sd); int ret; u32 val; @@ -2363,7 +2362,7 @@ static int ov5670_identify_module(struct ov5670 *ov5670) return ret; if (val != OV5670_CHIP_ID) { - dev_err(&client->dev, "chip id mismatch: %x!=%x\n", + dev_err(ov5670->dev, "chip id mismatch: %x!=%x\n", OV5670_CHIP_ID, val); return -ENXIO; } @@ -2389,7 +2388,6 @@ static int ov5670_mipi_configure(struct ov5670 *ov5670) /* 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; @@ -2402,7 +2400,7 @@ static int ov5670_start_streaming(struct ov5670 *ov5670) 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", + dev_err(ov5670->dev, "%s failed to set powerup registers\n", __func__); return ret; } @@ -2412,7 +2410,7 @@ static int ov5670_start_streaming(struct ov5670 *ov5670) 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__); + dev_err(ov5670->dev, "%s failed to set plls\n", __func__); return ret; } @@ -2420,13 +2418,13 @@ static int ov5670_start_streaming(struct ov5670 *ov5670) 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__); + dev_err(ov5670->dev, "%s failed to set mode\n", __func__); return ret; } ret = ov5670_mipi_configure(ov5670); if (ret) { - dev_err(&client->dev, "%s failed to configure MIPI\n", __func__); + dev_err(ov5670->dev, "%s failed to configure MIPI\n", __func__); return ret; } @@ -2438,7 +2436,7 @@ static int ov5670_start_streaming(struct ov5670 *ov5670) 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__); + dev_err(ov5670->dev, "%s failed to set stream\n", __func__); return ret; } @@ -2447,13 +2445,12 @@ static int ov5670_start_streaming(struct ov5670 *ov5670) static int ov5670_stop_streaming(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__); + dev_err(ov5670->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. @@ -2464,13 +2461,12 @@ static int ov5670_stop_streaming(struct ov5670 *ov5670) 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 ret = 0; mutex_lock(&ov5670->mutex); if (enable) { - ret = pm_runtime_resume_and_get(&client->dev); + ret = pm_runtime_resume_and_get(ov5670->dev); if (ret < 0) goto unlock_and_return; @@ -2479,12 +2475,12 @@ static int ov5670_set_stream(struct v4l2_subdev *sd, int enable) goto error; } else { ret = ov5670_stop_streaming(ov5670); - pm_runtime_put(&client->dev); + pm_runtime_put(ov5670->dev); } goto unlock_and_return; error: - pm_runtime_put(&client->dev); + pm_runtime_put(ov5670->dev); unlock_and_return: mutex_unlock(&ov5670->mutex); @@ -2621,26 +2617,23 @@ static const struct media_entity_operations ov5670_subdev_entity_ops = { static int ov5670_regulators_probe(struct ov5670 *ov5670) { - struct i2c_client *client = v4l2_get_subdevdata(&ov5670->sd); unsigned int i; for (i = 0; i < OV5670_NUM_SUPPLIES; i++) ov5670->supplies[i].supply = ov5670_supply_names[i]; - return devm_regulator_bulk_get(&client->dev, OV5670_NUM_SUPPLIES, + return devm_regulator_bulk_get(ov5670->dev, OV5670_NUM_SUPPLIES, ov5670->supplies); } static int ov5670_gpio_probe(struct ov5670 *ov5670) { - struct i2c_client *client = v4l2_get_subdevdata(&ov5670->sd); - - ov5670->pwdn_gpio = devm_gpiod_get_optional(&client->dev, "powerdown", + ov5670->pwdn_gpio = devm_gpiod_get_optional(ov5670->dev, "powerdown", GPIOD_OUT_LOW); if (IS_ERR(ov5670->pwdn_gpio)) return PTR_ERR(ov5670->pwdn_gpio); - ov5670->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", + ov5670->reset_gpio = devm_gpiod_get_optional(ov5670->dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(ov5670->reset_gpio)) return PTR_ERR(ov5670->reset_gpio); @@ -2660,18 +2653,16 @@ static int ov5670_probe(struct i2c_client *client) if (!ov5670) return -ENOMEM; - ov5670->xvclk = devm_clk_get_optional(&client->dev, NULL); - if (!IS_ERR_OR_NULL(ov5670->xvclk)) - input_clk = clk_get_rate(ov5670->xvclk); - else if (!ov5670->xvclk || PTR_ERR(ov5670->xvclk) == -ENOENT) - device_property_read_u32(&client->dev, "clock-frequency", - &input_clk); - else - return dev_err_probe(&client->dev, PTR_ERR(ov5670->xvclk), + ov5670->dev = &client->dev; + + ov5670->xvclk = devm_v4l2_sensor_clk_get(ov5670->dev, NULL); + if (IS_ERR(ov5670->xvclk)) + return dev_err_probe(ov5670->dev, PTR_ERR(ov5670->xvclk), "error getting clock\n"); + input_clk = clk_get_rate(ov5670->xvclk); if (input_clk != OV5670_XVCLK_FREQ) { - dev_err(&client->dev, + dev_err(ov5670->dev, "Unsupported clock frequency %u\n", input_clk); return -EINVAL; } @@ -2682,16 +2673,21 @@ static int ov5670_probe(struct i2c_client *client) ret = ov5670_regulators_probe(ov5670); if (ret) - return dev_err_probe(&client->dev, ret, "Regulators probe failed\n"); + return dev_err_probe(ov5670->dev, ret, "Regulators probe failed\n"); ret = ov5670_gpio_probe(ov5670); if (ret) - return dev_err_probe(&client->dev, ret, "GPIO probe failed\n"); + return dev_err_probe(ov5670->dev, ret, "GPIO probe failed\n"); - /* Graph Endpoint */ - handle = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL); + /* + * Graph Endpoint. If it's missing we defer rather than fail, as this + * sensor is known to co-exist on systems with the IPU3 and so it might + * be created by the ipu-bridge. + */ + handle = fwnode_graph_get_next_endpoint(dev_fwnode(ov5670->dev), NULL); if (!handle) - return dev_err_probe(&client->dev, -ENXIO, "Endpoint for node get failed\n"); + return dev_err_probe(ov5670->dev, -EPROBE_DEFER, + "Endpoint for node get failed\n"); ov5670->endpoint.bus_type = V4L2_MBUS_CSI2_DPHY; ov5670->endpoint.bus.mipi_csi2.num_data_lanes = 2; @@ -2699,20 +2695,20 @@ static int ov5670_probe(struct i2c_client *client) ret = v4l2_fwnode_endpoint_alloc_parse(handle, &ov5670->endpoint); fwnode_handle_put(handle); if (ret) - return dev_err_probe(&client->dev, ret, "Endpoint parse failed\n"); + return dev_err_probe(ov5670->dev, ret, "Endpoint parse failed\n"); - full_power = acpi_dev_state_d0(&client->dev); + full_power = acpi_dev_state_d0(ov5670->dev); if (full_power) { - ret = ov5670_runtime_resume(&client->dev); + ret = ov5670_runtime_resume(ov5670->dev); if (ret) { - dev_err_probe(&client->dev, ret, "Power up failed\n"); + dev_err_probe(ov5670->dev, ret, "Power up failed\n"); goto error_endpoint; } /* Check module identity */ ret = ov5670_identify_module(ov5670); if (ret) { - dev_err_probe(&client->dev, ret, "ov5670_identify_module() error\n"); + dev_err_probe(ov5670->dev, ret, "ov5670_identify_module() error\n"); goto error_power_off; } } @@ -2724,7 +2720,7 @@ static int ov5670_probe(struct i2c_client *client) ret = ov5670_init_controls(ov5670); if (ret) { - dev_err_probe(&client->dev, ret, "ov5670_init_controls() error\n"); + dev_err_probe(ov5670->dev, ret, "ov5670_init_controls() error\n"); goto error_mutex_destroy; } @@ -2737,28 +2733,28 @@ static int ov5670_probe(struct i2c_client *client) ov5670->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&ov5670->sd.entity, 1, &ov5670->pad); if (ret) { - dev_err_probe(&client->dev, ret, "media_entity_pads_init() error\n"); + dev_err_probe(ov5670->dev, ret, "media_entity_pads_init() error\n"); goto error_handler_free; } /* Set the device's state to active if it's in D0 state. */ if (full_power) - pm_runtime_set_active(&client->dev); - pm_runtime_enable(&client->dev); + pm_runtime_set_active(ov5670->dev); + pm_runtime_enable(ov5670->dev); /* Async register for subdev */ ret = v4l2_async_register_subdev_sensor(&ov5670->sd); if (ret < 0) { - dev_err_probe(&client->dev, ret, "v4l2_async_register_subdev() error\n"); + dev_err_probe(ov5670->dev, ret, "v4l2_async_register_subdev() error\n"); goto error_pm_disable; } - pm_runtime_idle(&client->dev); + pm_runtime_idle(ov5670->dev); return 0; error_pm_disable: - pm_runtime_disable(&client->dev); + pm_runtime_disable(ov5670->dev); media_entity_cleanup(&ov5670->sd.entity); @@ -2770,7 +2766,7 @@ error_mutex_destroy: error_power_off: if (full_power) - ov5670_runtime_suspend(&client->dev); + ov5670_runtime_suspend(ov5670->dev); error_endpoint: v4l2_fwnode_endpoint_free(&ov5670->endpoint); @@ -2788,8 +2784,8 @@ static void ov5670_remove(struct i2c_client *client) v4l2_ctrl_handler_free(sd->ctrl_handler); mutex_destroy(&ov5670->mutex); - pm_runtime_disable(&client->dev); - ov5670_runtime_suspend(&client->dev); + pm_runtime_disable(ov5670->dev); + ov5670_runtime_suspend(ov5670->dev); v4l2_fwnode_endpoint_free(&ov5670->endpoint); } diff --git a/drivers/media/i2c/ov5675.c b/drivers/media/i2c/ov5675.c index c1081deffc2f..ea26df328189 100644 --- a/drivers/media/i2c/ov5675.c +++ b/drivers/media/i2c/ov5675.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2019 Intel Corporation. -#include <linux/unaligned.h> #include <linux/acpi.h> #include <linux/clk.h> #include <linux/delay.h> @@ -11,6 +10,8 @@ #include <linux/module.h> #include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> +#include <linux/unaligned.h> + #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-fwnode.h> @@ -493,6 +494,8 @@ static const struct ov5675_mode supported_modes[] = { }; struct ov5675 { + struct device *dev; + struct v4l2_subdev sd; struct media_pad pad; struct v4l2_ctrl_handler ctrl_handler; @@ -584,7 +587,6 @@ static int ov5675_write_reg(struct ov5675 *ov5675, u16 reg, u16 len, u32 val) static int ov5675_write_reg_list(struct ov5675 *ov5675, const struct ov5675_reg_list *r_list) { - struct i2c_client *client = v4l2_get_subdevdata(&ov5675->sd); unsigned int i; int ret; @@ -592,7 +594,7 @@ static int ov5675_write_reg_list(struct ov5675 *ov5675, ret = ov5675_write_reg(ov5675, r_list->regs[i].address, 1, r_list->regs[i].val); if (ret) { - dev_err_ratelimited(&client->dev, + dev_err_ratelimited(ov5675->dev, "failed to write reg 0x%4.4x. error = %d", r_list->regs[i].address, ret); return ret; @@ -700,7 +702,6 @@ static int ov5675_set_ctrl(struct v4l2_ctrl *ctrl) { struct ov5675 *ov5675 = container_of(ctrl->handler, struct ov5675, ctrl_handler); - struct i2c_client *client = v4l2_get_subdevdata(&ov5675->sd); s64 exposure_max; int ret = 0; @@ -716,7 +717,7 @@ static int ov5675_set_ctrl(struct v4l2_ctrl *ctrl) } /* V4L2 controls values will be applied only when power is already up */ - if (!pm_runtime_get_if_in_use(&client->dev)) + if (!pm_runtime_get_if_in_use(ov5675->dev)) return 0; switch (ctrl->id) { @@ -765,7 +766,7 @@ static int ov5675_set_ctrl(struct v4l2_ctrl *ctrl) break; } - pm_runtime_put(&client->dev); + pm_runtime_put(ov5675->dev); return ret; } @@ -776,7 +777,6 @@ static const struct v4l2_ctrl_ops ov5675_ctrl_ops = { static int ov5675_init_controls(struct ov5675 *ov5675) { - struct i2c_client *client = v4l2_get_subdevdata(&ov5675->sd); struct v4l2_fwnode_device_properties props; struct v4l2_ctrl_handler *ctrl_hdlr; s64 exposure_max, h_blank; @@ -839,7 +839,7 @@ static int ov5675_init_controls(struct ov5675 *ov5675) return ctrl_hdlr->error; } - ret = v4l2_fwnode_device_parse(&client->dev, &props); + ret = v4l2_fwnode_device_parse(ov5675->dev, &props); if (ret) goto error; @@ -869,7 +869,6 @@ static void ov5675_update_pad_format(const struct ov5675_mode *mode, static int ov5675_identify_module(struct ov5675 *ov5675) { - struct i2c_client *client = v4l2_get_subdevdata(&ov5675->sd); int ret; u32 val; @@ -882,7 +881,7 @@ static int ov5675_identify_module(struct ov5675 *ov5675) return ret; if (val != OV5675_CHIP_ID) { - dev_err(&client->dev, "chip id mismatch: %x!=%x", + dev_err(ov5675->dev, "chip id mismatch: %x!=%x", OV5675_CHIP_ID, val); return -ENXIO; } @@ -894,7 +893,6 @@ static int ov5675_identify_module(struct ov5675 *ov5675) static int ov5675_start_streaming(struct ov5675 *ov5675) { - struct i2c_client *client = v4l2_get_subdevdata(&ov5675->sd); const struct ov5675_reg_list *reg_list; int link_freq_index, ret; @@ -906,14 +904,14 @@ static int ov5675_start_streaming(struct ov5675 *ov5675) reg_list = &link_freq_configs[link_freq_index].reg_list; ret = ov5675_write_reg_list(ov5675, reg_list); if (ret) { - dev_err(&client->dev, "failed to set plls"); + dev_err(ov5675->dev, "failed to set plls"); return ret; } reg_list = &ov5675->cur_mode->reg_list; ret = ov5675_write_reg_list(ov5675, reg_list); if (ret) { - dev_err(&client->dev, "failed to set mode"); + dev_err(ov5675->dev, "failed to set mode"); return ret; } @@ -924,7 +922,7 @@ static int ov5675_start_streaming(struct ov5675 *ov5675) ret = ov5675_write_reg(ov5675, OV5675_REG_MODE_SELECT, OV5675_REG_VALUE_08BIT, OV5675_MODE_STREAMING); if (ret) { - dev_err(&client->dev, "failed to set stream"); + dev_err(ov5675->dev, "failed to set stream"); return ret; } @@ -933,22 +931,19 @@ static int ov5675_start_streaming(struct ov5675 *ov5675) static void ov5675_stop_streaming(struct ov5675 *ov5675) { - struct i2c_client *client = v4l2_get_subdevdata(&ov5675->sd); - if (ov5675_write_reg(ov5675, OV5675_REG_MODE_SELECT, OV5675_REG_VALUE_08BIT, OV5675_MODE_STANDBY)) - dev_err(&client->dev, "failed to set stream"); + dev_err(ov5675->dev, "failed to set stream"); } static int ov5675_set_stream(struct v4l2_subdev *sd, int enable) { struct ov5675 *ov5675 = to_ov5675(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); int ret = 0; mutex_lock(&ov5675->mutex); if (enable) { - ret = pm_runtime_resume_and_get(&client->dev); + ret = pm_runtime_resume_and_get(ov5675->dev); if (ret < 0) { mutex_unlock(&ov5675->mutex); return ret; @@ -958,11 +953,11 @@ static int ov5675_set_stream(struct v4l2_subdev *sd, int enable) if (ret) { enable = 0; ov5675_stop_streaming(ov5675); - pm_runtime_put(&client->dev); + pm_runtime_put(ov5675->dev); } } else { ov5675_stop_streaming(ov5675); - pm_runtime_put(&client->dev); + pm_runtime_put(ov5675->dev); } mutex_unlock(&ov5675->mutex); @@ -1171,8 +1166,9 @@ static const struct v4l2_subdev_internal_ops ov5675_internal_ops = { .open = ov5675_open, }; -static int ov5675_get_hwcfg(struct ov5675 *ov5675, struct device *dev) +static int ov5675_get_hwcfg(struct ov5675 *ov5675) { + struct device *dev = ov5675->dev; struct fwnode_handle *ep; struct fwnode_handle *fwnode = dev_fwnode(dev); struct v4l2_fwnode_endpoint bus_cfg = { @@ -1185,24 +1181,13 @@ static int ov5675_get_hwcfg(struct ov5675 *ov5675, struct device *dev) if (!fwnode) return -ENXIO; - ov5675->xvclk = devm_clk_get_optional(dev, NULL); + ov5675->xvclk = devm_v4l2_sensor_clk_get(dev, NULL); if (IS_ERR(ov5675->xvclk)) return dev_err_probe(dev, PTR_ERR(ov5675->xvclk), - "failed to get xvclk: %ld\n", - PTR_ERR(ov5675->xvclk)); - - if (ov5675->xvclk) { - xvclk_rate = clk_get_rate(ov5675->xvclk); - } else { - ret = fwnode_property_read_u32(fwnode, "clock-frequency", - &xvclk_rate); - - if (ret) { - dev_err(dev, "can't get clock frequency"); - return ret; - } - } + "failed to get xvclk: %pe\n", + ov5675->xvclk); + xvclk_rate = clk_get_rate(ov5675->xvclk); if (xvclk_rate != OV5675_XVCLK_19_2) { dev_err(dev, "external clock rate %u is unsupported", xvclk_rate); @@ -1276,12 +1261,12 @@ static void ov5675_remove(struct i2c_client *client) v4l2_async_unregister_subdev(sd); media_entity_cleanup(&sd->entity); v4l2_ctrl_handler_free(sd->ctrl_handler); - pm_runtime_disable(&client->dev); + pm_runtime_disable(ov5675->dev); mutex_destroy(&ov5675->mutex); - if (!pm_runtime_status_suspended(&client->dev)) - ov5675_power_off(&client->dev); - pm_runtime_set_suspended(&client->dev); + if (!pm_runtime_status_suspended(ov5675->dev)) + ov5675_power_off(ov5675->dev); + pm_runtime_set_suspended(ov5675->dev); } static int ov5675_probe(struct i2c_client *client) @@ -1294,26 +1279,25 @@ static int ov5675_probe(struct i2c_client *client) if (!ov5675) return -ENOMEM; - ret = ov5675_get_hwcfg(ov5675, &client->dev); - if (ret) { - dev_err(&client->dev, "failed to get HW configuration: %d", - ret); + ov5675->dev = &client->dev; + + ret = ov5675_get_hwcfg(ov5675); + if (ret) return ret; - } v4l2_i2c_subdev_init(&ov5675->sd, client, &ov5675_subdev_ops); - ret = ov5675_power_on(&client->dev); + ret = ov5675_power_on(ov5675->dev); if (ret) { - dev_err(&client->dev, "failed to power on: %d\n", ret); + dev_err(ov5675->dev, "failed to power on: %d\n", ret); return ret; } - full_power = acpi_dev_state_d0(&client->dev); + full_power = acpi_dev_state_d0(ov5675->dev); if (full_power) { ret = ov5675_identify_module(ov5675); if (ret) { - dev_err(&client->dev, "failed to find sensor: %d", ret); + dev_err(ov5675->dev, "failed to find sensor: %d", ret); goto probe_power_off; } } @@ -1322,7 +1306,7 @@ static int ov5675_probe(struct i2c_client *client) ov5675->cur_mode = &supported_modes[0]; ret = ov5675_init_controls(ov5675); if (ret) { - dev_err(&client->dev, "failed to init controls: %d", ret); + dev_err(ov5675->dev, "failed to init controls: %d", ret); goto probe_error_v4l2_ctrl_handler_free; } @@ -1333,22 +1317,22 @@ static int ov5675_probe(struct i2c_client *client) ov5675->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&ov5675->sd.entity, 1, &ov5675->pad); if (ret) { - dev_err(&client->dev, "failed to init entity pads: %d", ret); + dev_err(ov5675->dev, "failed to init entity pads: %d", ret); goto probe_error_v4l2_ctrl_handler_free; } ret = v4l2_async_register_subdev_sensor(&ov5675->sd); if (ret < 0) { - dev_err(&client->dev, "failed to register V4L2 subdev: %d", + dev_err(ov5675->dev, "failed to register V4L2 subdev: %d", ret); goto probe_error_media_entity_cleanup; } /* Set the device's state to active if it's in D0 state. */ if (full_power) - pm_runtime_set_active(&client->dev); - pm_runtime_enable(&client->dev); - pm_runtime_idle(&client->dev); + pm_runtime_set_active(ov5675->dev); + pm_runtime_enable(ov5675->dev); + pm_runtime_idle(ov5675->dev); return 0; @@ -1359,7 +1343,7 @@ probe_error_v4l2_ctrl_handler_free: v4l2_ctrl_handler_free(ov5675->sd.ctrl_handler); mutex_destroy(&ov5675->mutex); probe_power_off: - ov5675_power_off(&client->dev); + ov5675_power_off(ov5675->dev); return ret; } diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c index 46b9ce111676..4cc796bbee92 100644 --- a/drivers/media/i2c/ov5693.c +++ b/drivers/media/i2c/ov5693.c @@ -1222,9 +1222,14 @@ static int ov5693_check_hwcfg(struct ov5693_device *ov5693) unsigned int i; int ret; + /* + * Sometimes the fwnode graph is initialized by the bridge driver + * Bridge drivers doing this may also add GPIO mappings, wait for this. + */ endpoint = fwnode_graph_get_next_endpoint(fwnode, NULL); if (!endpoint) - return -EPROBE_DEFER; /* Could be provided by cio2-bridge */ + return dev_err_probe(ov5693->dev, -EPROBE_DEFER, + "waiting for fwnode graph endpoint\n"); ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg); fwnode_handle_put(endpoint); @@ -1284,25 +1289,13 @@ static int ov5693_probe(struct i2c_client *client) v4l2_i2c_subdev_init(&ov5693->sd, client, &ov5693_ops); - ov5693->xvclk = devm_clk_get_optional(&client->dev, "xvclk"); + ov5693->xvclk = devm_v4l2_sensor_clk_get(&client->dev, "xvclk"); if (IS_ERR(ov5693->xvclk)) return dev_err_probe(&client->dev, PTR_ERR(ov5693->xvclk), - "failed to get xvclk: %ld\n", - PTR_ERR(ov5693->xvclk)); - - if (ov5693->xvclk) { - xvclk_rate = clk_get_rate(ov5693->xvclk); - } else { - ret = fwnode_property_read_u32(dev_fwnode(&client->dev), - "clock-frequency", - &xvclk_rate); - - if (ret) { - dev_err(&client->dev, "can't get clock frequency"); - return ret; - } - } + "failed to get xvclk: %pe\n", + ov5693->xvclk); + xvclk_rate = clk_get_rate(ov5693->xvclk); if (xvclk_rate != OV5693_XVCLK_FREQ) dev_warn(&client->dev, "Found clk freq %u, expected %u\n", xvclk_rate, OV5693_XVCLK_FREQ); diff --git a/drivers/media/i2c/ov5695.c b/drivers/media/i2c/ov5695.c index 663eccdfea6a..5bb6ce7b3237 100644 --- a/drivers/media/i2c/ov5695.c +++ b/drivers/media/i2c/ov5695.c @@ -1264,16 +1264,12 @@ static int ov5695_probe(struct i2c_client *client) ov5695->client = client; ov5695->cur_mode = &supported_modes[0]; - ov5695->xvclk = devm_clk_get(dev, "xvclk"); - if (IS_ERR(ov5695->xvclk)) { - 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; - } + ov5695->xvclk = devm_v4l2_sensor_clk_get_legacy(dev, "xvclk", true, + OV5695_XVCLK_FREQ); + if (IS_ERR(ov5695->xvclk)) + return dev_err_probe(dev, PTR_ERR(ov5695->xvclk), + "Failed to get xvclk\n"); + if (clk_get_rate(ov5695->xvclk) != OV5695_XVCLK_FREQ) dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); diff --git a/drivers/media/i2c/ov6211.c b/drivers/media/i2c/ov6211.c new file mode 100644 index 000000000000..e3ac5ecf27d1 --- /dev/null +++ b/drivers/media/i2c/ov6211.c @@ -0,0 +1,793 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2024-2025 Linaro Ltd + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> +#include <linux/units.h> +#include <media/v4l2-cci.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> + +#define OV6211_LINK_FREQ_480MHZ (480 * HZ_PER_MHZ) +#define OV6211_MCLK_FREQ_24MHZ (24 * HZ_PER_MHZ) + +#define OV6211_REG_CHIP_ID CCI_REG16(0x300a) +#define OV6211_CHIP_ID 0x6710 + +#define OV6211_REG_MODE_SELECT CCI_REG8(0x0100) +#define OV6211_MODE_STANDBY 0x00 +#define OV6211_MODE_STREAMING BIT(0) + +#define OV6211_REG_SOFTWARE_RST CCI_REG8(0x0103) +#define OV6211_SOFTWARE_RST BIT(0) + +/* Exposure controls from sensor */ +#define OV6211_REG_EXPOSURE CCI_REG24(0x3500) +#define OV6211_EXPOSURE_MIN 1 +#define OV6211_EXPOSURE_MAX_MARGIN 4 +#define OV6211_EXPOSURE_STEP 1 +#define OV6211_EXPOSURE_DEFAULT 210 + +/* Analogue gain controls from sensor */ +#define OV6211_REG_ANALOGUE_GAIN CCI_REG16(0x350a) +#define OV6211_ANALOGUE_GAIN_MIN 1 +#define OV6211_ANALOGUE_GAIN_MAX 0x3ff +#define OV6211_ANALOGUE_GAIN_STEP 1 +#define OV6211_ANALOGUE_GAIN_DEFAULT 160 + +/* Test pattern */ +#define OV6211_REG_PRE_ISP CCI_REG8(0x5e00) +#define OV6211_TEST_PATTERN_ENABLE BIT(7) + +#define to_ov6211(_sd) container_of(_sd, struct ov6211, sd) + +static const s64 ov6211_link_freq_menu[] = { + OV6211_LINK_FREQ_480MHZ, +}; + +struct ov6211_reg_list { + const struct cci_reg_sequence *regs; + unsigned int num_regs; +}; + +struct ov6211_mode { + u32 width; /* Frame width in pixels */ + u32 height; /* Frame height in pixels */ + u32 hts; /* Horizontal timing size */ + u32 vts; /* Default vertical timing size */ + u32 bpp; /* Bits per pixel */ + + const struct ov6211_reg_list reg_list; /* Sensor register setting */ +}; + +static const char * const ov6211_test_pattern_menu[] = { + "Disabled", + "Vertical Colour Bars", +}; + +static const char * const ov6211_supply_names[] = { + "avdd", /* Analog power */ + "dovdd", /* Digital I/O power */ + "dvdd", /* Digital core power */ +}; + +#define OV6211_NUM_SUPPLIES ARRAY_SIZE(ov6211_supply_names) + +struct ov6211 { + struct device *dev; + struct regmap *regmap; + struct clk *xvclk; + struct gpio_desc *reset_gpio; + struct regulator_bulk_data supplies[OV6211_NUM_SUPPLIES]; + + struct v4l2_subdev sd; + struct media_pad pad; + + struct v4l2_ctrl_handler ctrl_handler; + + /* Saved register values */ + u64 pre_isp; +}; + +static const struct cci_reg_sequence ov6211_400x400_120fps_mode[] = { + { CCI_REG8(0x3005), 0x00 }, + { CCI_REG8(0x3013), 0x12 }, + { CCI_REG8(0x3014), 0x04 }, + { CCI_REG8(0x3016), 0x10 }, + { CCI_REG8(0x3017), 0x00 }, + { CCI_REG8(0x3018), 0x00 }, + { CCI_REG8(0x301a), 0x00 }, + { CCI_REG8(0x301b), 0x00 }, + { CCI_REG8(0x301c), 0x00 }, + { CCI_REG8(0x3037), 0xf0 }, + { CCI_REG8(0x3080), 0x01 }, + { CCI_REG8(0x3081), 0x00 }, + { CCI_REG8(0x3082), 0x01 }, + { CCI_REG8(0x3098), 0x04 }, + { CCI_REG8(0x3099), 0x28 }, + { CCI_REG8(0x309a), 0x06 }, + { CCI_REG8(0x309b), 0x04 }, + { CCI_REG8(0x309c), 0x00 }, + { CCI_REG8(0x309d), 0x00 }, + { CCI_REG8(0x309e), 0x01 }, + { CCI_REG8(0x309f), 0x00 }, + { CCI_REG8(0x30b0), 0x08 }, + { CCI_REG8(0x30b1), 0x02 }, + { CCI_REG8(0x30b2), 0x00 }, + { CCI_REG8(0x30b3), 0x28 }, + { CCI_REG8(0x30b4), 0x02 }, + { CCI_REG8(0x30b5), 0x00 }, + { CCI_REG8(0x3106), 0xd9 }, + { CCI_REG8(0x3503), 0x07 }, + { CCI_REG8(0x3509), 0x10 }, + { CCI_REG8(0x3600), 0xfc }, + { CCI_REG8(0x3620), 0xb7 }, + { CCI_REG8(0x3621), 0x05 }, + { CCI_REG8(0x3626), 0x31 }, + { CCI_REG8(0x3627), 0x40 }, + { CCI_REG8(0x3632), 0xa3 }, + { CCI_REG8(0x3633), 0x34 }, + { CCI_REG8(0x3634), 0x40 }, + { CCI_REG8(0x3636), 0x00 }, + { CCI_REG8(0x3660), 0x80 }, + { CCI_REG8(0x3662), 0x03 }, + { CCI_REG8(0x3664), 0xf0 }, + { CCI_REG8(0x366a), 0x10 }, + { CCI_REG8(0x366b), 0x06 }, + { CCI_REG8(0x3680), 0xf4 }, + { CCI_REG8(0x3681), 0x50 }, + { CCI_REG8(0x3682), 0x00 }, + { CCI_REG8(0x3708), 0x20 }, + { CCI_REG8(0x3709), 0x40 }, + { CCI_REG8(0x370d), 0x03 }, + { CCI_REG8(0x373b), 0x02 }, + { CCI_REG8(0x373c), 0x08 }, + { CCI_REG8(0x3742), 0x00 }, + { CCI_REG8(0x3744), 0x16 }, + { CCI_REG8(0x3745), 0x08 }, + { CCI_REG8(0x3781), 0xfc }, + { CCI_REG8(0x3788), 0x00 }, + { CCI_REG8(0x3800), 0x00 }, + { CCI_REG8(0x3801), 0x04 }, + { CCI_REG8(0x3802), 0x00 }, + { CCI_REG8(0x3803), 0x04 }, + { CCI_REG8(0x3804), 0x01 }, + { CCI_REG8(0x3805), 0x9b }, + { CCI_REG8(0x3806), 0x01 }, + { CCI_REG8(0x3807), 0x9b }, + { CCI_REG8(0x3808), 0x01 }, /* output width */ + { CCI_REG8(0x3809), 0x90 }, + { CCI_REG8(0x380a), 0x01 }, /* output height */ + { CCI_REG8(0x380b), 0x90 }, + { CCI_REG8(0x380c), 0x05 }, /* horizontal timing size */ + { CCI_REG8(0x380d), 0xf2 }, + { CCI_REG8(0x380e), 0x01 }, /* vertical timing size */ + { CCI_REG8(0x380f), 0xb6 }, + { CCI_REG8(0x3810), 0x00 }, + { CCI_REG8(0x3811), 0x04 }, + { CCI_REG8(0x3812), 0x00 }, + { CCI_REG8(0x3813), 0x04 }, + { CCI_REG8(0x3814), 0x11 }, + { CCI_REG8(0x3815), 0x11 }, + { CCI_REG8(0x3820), 0x00 }, + { CCI_REG8(0x3821), 0x00 }, + { CCI_REG8(0x382b), 0xfa }, + { CCI_REG8(0x382f), 0x04 }, + { CCI_REG8(0x3832), 0x00 }, + { CCI_REG8(0x3833), 0x05 }, + { CCI_REG8(0x3834), 0x00 }, + { CCI_REG8(0x3835), 0x05 }, + { CCI_REG8(0x3882), 0x04 }, + { CCI_REG8(0x3883), 0x00 }, + { CCI_REG8(0x38a4), 0x10 }, + { CCI_REG8(0x38a5), 0x00 }, + { CCI_REG8(0x38b1), 0x03 }, + { CCI_REG8(0x3b80), 0x00 }, + { CCI_REG8(0x3b81), 0xff }, + { CCI_REG8(0x3b82), 0x10 }, + { CCI_REG8(0x3b83), 0x00 }, + { CCI_REG8(0x3b84), 0x08 }, + { CCI_REG8(0x3b85), 0x00 }, + { CCI_REG8(0x3b86), 0x01 }, + { CCI_REG8(0x3b87), 0x00 }, + { CCI_REG8(0x3b88), 0x00 }, + { CCI_REG8(0x3b89), 0x00 }, + { CCI_REG8(0x3b8a), 0x00 }, + { CCI_REG8(0x3b8b), 0x05 }, + { CCI_REG8(0x3b8c), 0x00 }, + { CCI_REG8(0x3b8d), 0x00 }, + { CCI_REG8(0x3b8e), 0x01 }, + { CCI_REG8(0x3b8f), 0xb2 }, + { CCI_REG8(0x3b94), 0x05 }, + { CCI_REG8(0x3b95), 0xf2 }, + { CCI_REG8(0x3b96), 0xc0 }, + { CCI_REG8(0x4004), 0x04 }, + { CCI_REG8(0x404e), 0x01 }, + { CCI_REG8(0x4801), 0x0f }, + { CCI_REG8(0x4806), 0x0f }, + { CCI_REG8(0x4837), 0x43 }, + { CCI_REG8(0x5a08), 0x00 }, + { CCI_REG8(0x5a01), 0x00 }, + { CCI_REG8(0x5a03), 0x00 }, + { CCI_REG8(0x5a04), 0x10 }, + { CCI_REG8(0x5a05), 0xa0 }, + { CCI_REG8(0x5a06), 0x0c }, + { CCI_REG8(0x5a07), 0x78 }, +}; + +static const struct ov6211_mode supported_modes[] = { + { + .width = 400, + .height = 400, + .hts = 1522, + .vts = 438, + .bpp = 8, + .reg_list = { + .regs = ov6211_400x400_120fps_mode, + .num_regs = ARRAY_SIZE(ov6211_400x400_120fps_mode), + }, + }, +}; + +static int ov6211_set_test_pattern(struct ov6211 *ov6211, u32 pattern) +{ + u64 val = ov6211->pre_isp; + + if (pattern) + val |= OV6211_TEST_PATTERN_ENABLE; + else + val &= ~OV6211_TEST_PATTERN_ENABLE; + + return cci_write(ov6211->regmap, OV6211_REG_PRE_ISP, val, NULL); +} + +static int ov6211_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov6211 *ov6211 = container_of(ctrl->handler, struct ov6211, + ctrl_handler); + int ret; + + /* V4L2 controls are applied, when sensor is powered up for streaming */ + if (!pm_runtime_get_if_active(ov6211->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + ret = cci_write(ov6211->regmap, OV6211_REG_ANALOGUE_GAIN, + ctrl->val, NULL); + break; + case V4L2_CID_EXPOSURE: + ret = cci_write(ov6211->regmap, OV6211_REG_EXPOSURE, + ctrl->val << 4, NULL); + break; + case V4L2_CID_TEST_PATTERN: + ret = ov6211_set_test_pattern(ov6211, ctrl->val); + break; + default: + ret = -EINVAL; + break; + } + + pm_runtime_put(ov6211->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops ov6211_ctrl_ops = { + .s_ctrl = ov6211_set_ctrl, +}; + +static int ov6211_init_controls(struct ov6211 *ov6211) +{ + struct v4l2_ctrl_handler *ctrl_hdlr = &ov6211->ctrl_handler; + const struct ov6211_mode *mode = &supported_modes[0]; + struct v4l2_fwnode_device_properties props; + s64 exposure_max, pixel_rate, h_blank; + struct v4l2_ctrl *ctrl; + int ret; + + v4l2_ctrl_handler_init(ctrl_hdlr, 9); + + ctrl = v4l2_ctrl_new_int_menu(ctrl_hdlr, &ov6211_ctrl_ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(ov6211_link_freq_menu) - 1, + 0, ov6211_link_freq_menu); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + pixel_rate = ov6211_link_freq_menu[0] / mode->bpp; + v4l2_ctrl_new_std(ctrl_hdlr, &ov6211_ctrl_ops, V4L2_CID_PIXEL_RATE, + 0, pixel_rate, 1, pixel_rate); + + h_blank = mode->hts - mode->width; + ctrl = v4l2_ctrl_new_std(ctrl_hdlr, &ov6211_ctrl_ops, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + ctrl = v4l2_ctrl_new_std(ctrl_hdlr, &ov6211_ctrl_ops, V4L2_CID_VBLANK, + mode->vts - mode->height, + mode->vts - mode->height, 1, + mode->vts - mode->height); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(ctrl_hdlr, &ov6211_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + OV6211_ANALOGUE_GAIN_MIN, OV6211_ANALOGUE_GAIN_MAX, + OV6211_ANALOGUE_GAIN_STEP, + OV6211_ANALOGUE_GAIN_DEFAULT); + + exposure_max = (mode->vts - OV6211_EXPOSURE_MAX_MARGIN); + v4l2_ctrl_new_std(ctrl_hdlr, &ov6211_ctrl_ops, + V4L2_CID_EXPOSURE, + OV6211_EXPOSURE_MIN, exposure_max, + OV6211_EXPOSURE_STEP, + OV6211_EXPOSURE_DEFAULT); + + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &ov6211_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov6211_test_pattern_menu) - 1, + 0, 0, ov6211_test_pattern_menu); + + if (ctrl_hdlr->error) + return ctrl_hdlr->error; + + ret = v4l2_fwnode_device_parse(ov6211->dev, &props); + if (ret) + goto error_free_hdlr; + + ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &ov6211_ctrl_ops, + &props); + if (ret) + goto error_free_hdlr; + + ov6211->sd.ctrl_handler = ctrl_hdlr; + + return 0; + +error_free_hdlr: + v4l2_ctrl_handler_free(ctrl_hdlr); + + return ret; +} + +static void ov6211_update_pad_format(const struct ov6211_mode *mode, + struct v4l2_mbus_framefmt *fmt) +{ + fmt->code = MEDIA_BUS_FMT_Y8_1X8; + fmt->width = mode->width; + fmt->height = mode->height; + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_RAW; + fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; + fmt->xfer_func = V4L2_XFER_FUNC_NONE; +} + +static int ov6211_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + const struct ov6211_reg_list *reg_list = &supported_modes[0].reg_list; + struct ov6211 *ov6211 = to_ov6211(sd); + int ret; + + ret = pm_runtime_resume_and_get(ov6211->dev); + if (ret) + return ret; + + /* Skip a step of explicit entering into the standby mode */ + ret = cci_write(ov6211->regmap, OV6211_REG_SOFTWARE_RST, + OV6211_SOFTWARE_RST, NULL); + if (ret) { + dev_err(ov6211->dev, "failed to software reset: %d\n", ret); + goto error; + } + + ret = cci_multi_reg_write(ov6211->regmap, reg_list->regs, + reg_list->num_regs, NULL); + if (ret) { + dev_err(ov6211->dev, "failed to set mode: %d\n", ret); + goto error; + } + + ret = __v4l2_ctrl_handler_setup(ov6211->sd.ctrl_handler); + if (ret) + goto error; + + ret = cci_write(ov6211->regmap, OV6211_REG_MODE_SELECT, + OV6211_MODE_STREAMING, NULL); + if (ret) { + dev_err(ov6211->dev, "failed to start streaming: %d\n", ret); + goto error; + } + + return 0; + +error: + pm_runtime_put_autosuspend(ov6211->dev); + + return ret; +} + +static int ov6211_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct ov6211 *ov6211 = to_ov6211(sd); + int ret; + + ret = cci_write(ov6211->regmap, OV6211_REG_MODE_SELECT, + OV6211_MODE_STANDBY, NULL); + if (ret) + dev_err(ov6211->dev, "failed to stop streaming: %d\n", ret); + + pm_runtime_put_autosuspend(ov6211->dev); + + return ret; +} + +static int ov6211_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *format; + const struct ov6211_mode *mode; + + format = v4l2_subdev_state_get_format(state, 0); + + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), + width, height, + fmt->format.width, + fmt->format.height); + + ov6211_update_pad_format(mode, &fmt->format); + *format = fmt->format; + + return 0; +} + +static int ov6211_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_Y8_1X8; + + return 0; +} + +static int ov6211_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fse->code != MEDIA_BUS_FMT_Y8_1X8) + 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 int ov6211_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_TRY, + .pad = 0, + .format = { + .code = MEDIA_BUS_FMT_Y8_1X8, + .width = supported_modes[0].width, + .height = supported_modes[0].height, + }, + }; + + ov6211_set_pad_format(sd, state, &fmt); + + return 0; +} + +static const struct v4l2_subdev_video_ops ov6211_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_pad_ops ov6211_pad_ops = { + .set_fmt = ov6211_set_pad_format, + .get_fmt = v4l2_subdev_get_fmt, + .enum_mbus_code = ov6211_enum_mbus_code, + .enum_frame_size = ov6211_enum_frame_size, + .enable_streams = ov6211_enable_streams, + .disable_streams = ov6211_disable_streams, +}; + +static const struct v4l2_subdev_ops ov6211_subdev_ops = { + .video = &ov6211_video_ops, + .pad = &ov6211_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops ov6211_internal_ops = { + .init_state = ov6211_init_state, +}; + +static const struct media_entity_operations ov6211_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static int ov6211_identify_sensor(struct ov6211 *ov6211) +{ + u64 val; + int ret; + + ret = cci_read(ov6211->regmap, OV6211_REG_CHIP_ID, &val, NULL); + if (ret) { + dev_err(ov6211->dev, "failed to read chip id: %d\n", ret); + return ret; + } + + if (val != OV6211_CHIP_ID) { + dev_err(ov6211->dev, "chip id mismatch: %x!=%llx\n", + OV6211_CHIP_ID, val); + return -ENODEV; + } + + ret = cci_read(ov6211->regmap, OV6211_REG_PRE_ISP, + &ov6211->pre_isp, NULL); + if (ret) + dev_err(ov6211->dev, "failed to read pre_isp: %d\n", ret); + + return ret; +} + +static int ov6211_check_hwcfg(struct ov6211 *ov6211) +{ + struct fwnode_handle *fwnode = dev_fwnode(ov6211->dev), *ep; + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; + unsigned long freq_bitmap; + int ret; + + if (!fwnode) + return -ENODEV; + + ep = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!ep) + return -EINVAL; + + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + fwnode_handle_put(ep); + if (ret) + return ret; + + ret = v4l2_link_freq_to_bitmap(ov6211->dev, bus_cfg.link_frequencies, + bus_cfg.nr_of_link_frequencies, + ov6211_link_freq_menu, + ARRAY_SIZE(ov6211_link_freq_menu), + &freq_bitmap); + + v4l2_fwnode_endpoint_free(&bus_cfg); + + return ret; +} + +static int ov6211_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov6211 *ov6211 = to_ov6211(sd); + int ret; + + ret = regulator_bulk_enable(OV6211_NUM_SUPPLIES, ov6211->supplies); + if (ret) + return ret; + + gpiod_set_value_cansleep(ov6211->reset_gpio, 0); + usleep_range(10 * USEC_PER_MSEC, 15 * USEC_PER_MSEC); + + ret = clk_prepare_enable(ov6211->xvclk); + if (ret) + goto reset_gpio; + + return 0; + +reset_gpio: + gpiod_set_value_cansleep(ov6211->reset_gpio, 1); + + regulator_bulk_disable(OV6211_NUM_SUPPLIES, ov6211->supplies); + + return ret; +} + +static int ov6211_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov6211 *ov6211 = to_ov6211(sd); + + clk_disable_unprepare(ov6211->xvclk); + + gpiod_set_value_cansleep(ov6211->reset_gpio, 1); + + regulator_bulk_disable(OV6211_NUM_SUPPLIES, ov6211->supplies); + + return 0; +} + +static int ov6211_probe(struct i2c_client *client) +{ + struct ov6211 *ov6211; + unsigned long freq; + unsigned int i; + int ret; + + ov6211 = devm_kzalloc(&client->dev, sizeof(*ov6211), GFP_KERNEL); + if (!ov6211) + return -ENOMEM; + + ov6211->dev = &client->dev; + + v4l2_i2c_subdev_init(&ov6211->sd, client, &ov6211_subdev_ops); + + ov6211->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(ov6211->regmap)) + return dev_err_probe(ov6211->dev, PTR_ERR(ov6211->regmap), + "failed to init CCI\n"); + + ov6211->xvclk = devm_v4l2_sensor_clk_get(ov6211->dev, NULL); + if (IS_ERR(ov6211->xvclk)) + return dev_err_probe(ov6211->dev, PTR_ERR(ov6211->xvclk), + "failed to get XVCLK clock\n"); + + freq = clk_get_rate(ov6211->xvclk); + if (freq && freq != OV6211_MCLK_FREQ_24MHZ) + return dev_err_probe(ov6211->dev, -EINVAL, + "XVCLK clock frequency %lu is not supported\n", + freq); + + ret = ov6211_check_hwcfg(ov6211); + if (ret) + return dev_err_probe(ov6211->dev, ret, + "failed to check HW configuration\n"); + + ov6211->reset_gpio = devm_gpiod_get_optional(ov6211->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(ov6211->reset_gpio)) + return dev_err_probe(ov6211->dev, PTR_ERR(ov6211->reset_gpio), + "cannot get reset GPIO\n"); + + for (i = 0; i < OV6211_NUM_SUPPLIES; i++) + ov6211->supplies[i].supply = ov6211_supply_names[i]; + + ret = devm_regulator_bulk_get(ov6211->dev, OV6211_NUM_SUPPLIES, + ov6211->supplies); + if (ret) + return dev_err_probe(ov6211->dev, ret, + "failed to get supply regulators\n"); + + /* The sensor must be powered on to read the CHIP_ID register */ + ret = ov6211_power_on(ov6211->dev); + if (ret) + return ret; + + ret = ov6211_identify_sensor(ov6211); + if (ret) { + dev_err_probe(ov6211->dev, ret, "failed to find sensor\n"); + goto power_off; + } + + ret = ov6211_init_controls(ov6211); + if (ret) { + dev_err_probe(ov6211->dev, ret, "failed to init controls\n"); + goto power_off; + } + + ov6211->sd.state_lock = ov6211->ctrl_handler.lock; + ov6211->sd.internal_ops = &ov6211_internal_ops; + ov6211->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + ov6211->sd.entity.ops = &ov6211_subdev_entity_ops; + ov6211->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + ov6211->pad.flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&ov6211->sd.entity, 1, &ov6211->pad); + if (ret) { + dev_err_probe(ov6211->dev, ret, + "failed to init media entity pads\n"); + goto v4l2_ctrl_handler_free; + } + + ret = v4l2_subdev_init_finalize(&ov6211->sd); + if (ret < 0) { + dev_err_probe(ov6211->dev, ret, + "failed to init media entity pads\n"); + goto media_entity_cleanup; + } + + pm_runtime_set_active(ov6211->dev); + pm_runtime_enable(ov6211->dev); + + ret = v4l2_async_register_subdev_sensor(&ov6211->sd); + if (ret < 0) { + dev_err_probe(ov6211->dev, ret, + "failed to register V4L2 subdev\n"); + goto subdev_cleanup; + } + + /* Enable runtime PM and turn off the device */ + pm_runtime_idle(ov6211->dev); + pm_runtime_set_autosuspend_delay(ov6211->dev, 1000); + pm_runtime_use_autosuspend(ov6211->dev); + + return 0; + +subdev_cleanup: + v4l2_subdev_cleanup(&ov6211->sd); + pm_runtime_disable(ov6211->dev); + pm_runtime_set_suspended(ov6211->dev); + +media_entity_cleanup: + media_entity_cleanup(&ov6211->sd.entity); + +v4l2_ctrl_handler_free: + v4l2_ctrl_handler_free(ov6211->sd.ctrl_handler); + +power_off: + ov6211_power_off(ov6211->dev); + + return ret; +} + +static void ov6211_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov6211 *ov6211 = to_ov6211(sd); + + v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(sd->ctrl_handler); + pm_runtime_disable(ov6211->dev); + + if (!pm_runtime_status_suspended(ov6211->dev)) { + ov6211_power_off(ov6211->dev); + pm_runtime_set_suspended(ov6211->dev); + } +} + +static const struct dev_pm_ops ov6211_pm_ops = { + SET_RUNTIME_PM_OPS(ov6211_power_off, ov6211_power_on, NULL) +}; + +static const struct of_device_id ov6211_of_match[] = { + { .compatible = "ovti,ov6211" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ov6211_of_match); + +static struct i2c_driver ov6211_i2c_driver = { + .driver = { + .name = "ov6211", + .pm = &ov6211_pm_ops, + .of_match_table = ov6211_of_match, + }, + .probe = ov6211_probe, + .remove = ov6211_remove, +}; + +module_i2c_driver(ov6211_i2c_driver); + +MODULE_AUTHOR("Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org>"); +MODULE_DESCRIPTION("OmniVision OV6211 sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/ov64a40.c b/drivers/media/i2c/ov64a40.c index a5da4fe47e0b..78b62c169b99 100644 --- a/drivers/media/i2c/ov64a40.c +++ b/drivers/media/i2c/ov64a40.c @@ -2990,7 +2990,6 @@ static int ov64a40_start_streaming(struct ov64a40 *ov64a40, return 0; error_power_off: - pm_runtime_mark_last_busy(ov64a40->dev); pm_runtime_put_autosuspend(ov64a40->dev); return ret; @@ -3000,7 +2999,6 @@ static int ov64a40_stop_streaming(struct ov64a40 *ov64a40, struct v4l2_subdev_state *state) { cci_update_bits(ov64a40->cci, OV64A40_REG_SMIA, BIT(0), 0, NULL); - pm_runtime_mark_last_busy(ov64a40->dev); pm_runtime_put_autosuspend(ov64a40->dev); __v4l2_ctrl_grab(ov64a40->link_freq, false); @@ -3329,10 +3327,8 @@ static int ov64a40_set_ctrl(struct v4l2_ctrl *ctrl) break; } - if (pm_status > 0) { - pm_runtime_mark_last_busy(ov64a40->dev); + if (pm_status > 0) pm_runtime_put_autosuspend(ov64a40->dev); - } return ret; } @@ -3550,7 +3546,7 @@ static int ov64a40_probe(struct i2c_client *client) return PTR_ERR(ov64a40->cci); } - ov64a40->xclk = devm_clk_get(&client->dev, NULL); + ov64a40->xclk = devm_v4l2_sensor_clk_get(&client->dev, NULL); if (IS_ERR(ov64a40->xclk)) return dev_err_probe(&client->dev, PTR_ERR(ov64a40->xclk), "Failed to get clock\n"); @@ -3622,7 +3618,6 @@ static int ov64a40_probe(struct i2c_client *client) goto error_subdev_cleanup; } - pm_runtime_mark_last_busy(&client->dev); pm_runtime_put_autosuspend(&client->dev); return 0; diff --git a/drivers/media/i2c/ov6650.c b/drivers/media/i2c/ov6650.c deleted file mode 100644 index 9c7627161142..000000000000 --- a/drivers/media/i2c/ov6650.c +++ /dev/null @@ -1,1149 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * V4L2 subdevice driver for OmniVision OV6650 Camera Sensor - * - * Copyright (C) 2010 Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> - * - * Based on OmniVision OV96xx Camera Driver - * Copyright (C) 2009 Marek Vasut <marek.vasut@gmail.com> - * - * Based on ov772x camera driver: - * Copyright (C) 2008 Renesas Solutions Corp. - * Kuninori Morimoto <morimoto.kuninori@renesas.com> - * - * Based on ov7670 and soc_camera_platform driver, - * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net> - * Copyright (C) 2008 Magnus Damm - * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> - * - * Hardware specific bits initially based on former work by Matt Callow - * drivers/media/video/omap/sensor_ov6650.c - * Copyright (C) 2006 Matt Callow - */ - -#include <linux/bitops.h> -#include <linux/clk.h> -#include <linux/delay.h> -#include <linux/i2c.h> -#include <linux/slab.h> -#include <linux/v4l2-mediabus.h> -#include <linux/module.h> - -#include <media/v4l2-ctrls.h> -#include <media/v4l2-device.h> - -/* Register definitions */ -#define REG_GAIN 0x00 /* range 00 - 3F */ -#define REG_BLUE 0x01 -#define REG_RED 0x02 -#define REG_SAT 0x03 /* [7:4] saturation [0:3] reserved */ -#define REG_HUE 0x04 /* [7:6] rsrvd [5] hue en [4:0] hue */ - -#define REG_BRT 0x06 - -#define REG_PIDH 0x0a -#define REG_PIDL 0x0b - -#define REG_AECH 0x10 -#define REG_CLKRC 0x11 /* Data Format and Internal Clock */ - /* [7:6] Input system clock (MHz)*/ - /* 00=8, 01=12, 10=16, 11=24 */ - /* [5:0]: Internal Clock Pre-Scaler */ -#define REG_COMA 0x12 /* [7] Reset */ -#define REG_COMB 0x13 -#define REG_COMC 0x14 -#define REG_COMD 0x15 -#define REG_COML 0x16 -#define REG_HSTRT 0x17 -#define REG_HSTOP 0x18 -#define REG_VSTRT 0x19 -#define REG_VSTOP 0x1a -#define REG_PSHFT 0x1b -#define REG_MIDH 0x1c -#define REG_MIDL 0x1d -#define REG_HSYNS 0x1e -#define REG_HSYNE 0x1f -#define REG_COME 0x20 -#define REG_YOFF 0x21 -#define REG_UOFF 0x22 -#define REG_VOFF 0x23 -#define REG_AEW 0x24 -#define REG_AEB 0x25 -#define REG_COMF 0x26 -#define REG_COMG 0x27 -#define REG_COMH 0x28 -#define REG_COMI 0x29 - -#define REG_FRARL 0x2b -#define REG_COMJ 0x2c -#define REG_COMK 0x2d -#define REG_AVGY 0x2e -#define REG_REF0 0x2f -#define REG_REF1 0x30 -#define REG_REF2 0x31 -#define REG_FRAJH 0x32 -#define REG_FRAJL 0x33 -#define REG_FACT 0x34 -#define REG_L1AEC 0x35 -#define REG_AVGU 0x36 -#define REG_AVGV 0x37 - -#define REG_SPCB 0x60 -#define REG_SPCC 0x61 -#define REG_GAM1 0x62 -#define REG_GAM2 0x63 -#define REG_GAM3 0x64 -#define REG_SPCD 0x65 - -#define REG_SPCE 0x68 -#define REG_ADCL 0x69 - -#define REG_RMCO 0x6c -#define REG_GMCO 0x6d -#define REG_BMCO 0x6e - - -/* Register bits, values, etc. */ -#define OV6650_PIDH 0x66 /* high byte of product ID number */ -#define OV6650_PIDL 0x50 /* low byte of product ID number */ -#define OV6650_MIDH 0x7F /* high byte of mfg ID */ -#define OV6650_MIDL 0xA2 /* low byte of mfg ID */ - -#define DEF_GAIN 0x00 -#define DEF_BLUE 0x80 -#define DEF_RED 0x80 - -#define SAT_SHIFT 4 -#define SAT_MASK (0xf << SAT_SHIFT) -#define SET_SAT(x) (((x) << SAT_SHIFT) & SAT_MASK) - -#define HUE_EN BIT(5) -#define HUE_MASK 0x1f -#define DEF_HUE 0x10 -#define SET_HUE(x) (HUE_EN | ((x) & HUE_MASK)) - -#define DEF_AECH 0x4D - -#define CLKRC_8MHz 0x00 -#define CLKRC_12MHz 0x40 -#define CLKRC_16MHz 0x80 -#define CLKRC_24MHz 0xc0 -#define CLKRC_DIV_MASK 0x3f -#define GET_CLKRC_DIV(x) (((x) & CLKRC_DIV_MASK) + 1) -#define DEF_CLKRC 0x00 - -#define COMA_RESET BIT(7) -#define COMA_QCIF BIT(5) -#define COMA_RAW_RGB BIT(4) -#define COMA_RGB BIT(3) -#define COMA_BW BIT(2) -#define COMA_WORD_SWAP BIT(1) -#define COMA_BYTE_SWAP BIT(0) -#define DEF_COMA 0x00 - -#define COMB_FLIP_V BIT(7) -#define COMB_FLIP_H BIT(5) -#define COMB_BAND_FILTER BIT(4) -#define COMB_AWB BIT(2) -#define COMB_AGC BIT(1) -#define COMB_AEC BIT(0) -#define DEF_COMB 0x5f - -#define COML_ONE_CHANNEL BIT(7) - -#define DEF_HSTRT 0x24 -#define DEF_HSTOP 0xd4 -#define DEF_VSTRT 0x04 -#define DEF_VSTOP 0x94 - -#define COMF_HREF_LOW BIT(4) - -#define COMJ_PCLK_RISING BIT(4) -#define COMJ_VSYNC_HIGH BIT(0) - -/* supported resolutions */ -#define W_QCIF (DEF_HSTOP - DEF_HSTRT) -#define W_CIF (W_QCIF << 1) -#define H_QCIF (DEF_VSTOP - DEF_VSTRT) -#define H_CIF (H_QCIF << 1) - -#define FRAME_RATE_MAX 30 - - -struct ov6650_reg { - u8 reg; - u8 val; -}; - -struct ov6650 { - struct v4l2_subdev subdev; - struct v4l2_ctrl_handler hdl; - struct { - /* exposure/autoexposure cluster */ - struct v4l2_ctrl *autoexposure; - struct v4l2_ctrl *exposure; - }; - struct { - /* gain/autogain cluster */ - struct v4l2_ctrl *autogain; - struct v4l2_ctrl *gain; - }; - struct { - /* blue/red/autowhitebalance cluster */ - struct v4l2_ctrl *autowb; - struct v4l2_ctrl *blue; - struct v4l2_ctrl *red; - }; - struct clk *clk; - bool half_scale; /* scale down output by 2 */ - struct v4l2_rect rect; /* sensor cropping window */ - struct v4l2_fract tpf; /* as requested with set_frame_interval */ - u32 code; -}; - -struct ov6650_xclk { - unsigned long rate; - u8 clkrc; -}; - -static const struct ov6650_xclk ov6650_xclk[] = { -{ - .rate = 8000000, - .clkrc = CLKRC_8MHz, -}, -{ - .rate = 12000000, - .clkrc = CLKRC_12MHz, -}, -{ - .rate = 16000000, - .clkrc = CLKRC_16MHz, -}, -{ - .rate = 24000000, - .clkrc = CLKRC_24MHz, -}, -}; - -static u32 ov6650_codes[] = { - MEDIA_BUS_FMT_YUYV8_2X8, - MEDIA_BUS_FMT_UYVY8_2X8, - MEDIA_BUS_FMT_YVYU8_2X8, - MEDIA_BUS_FMT_VYUY8_2X8, - MEDIA_BUS_FMT_SBGGR8_1X8, - MEDIA_BUS_FMT_Y8_1X8, -}; - -static const struct v4l2_mbus_framefmt ov6650_def_fmt = { - .width = W_CIF, - .height = H_CIF, - .code = MEDIA_BUS_FMT_SBGGR8_1X8, - .colorspace = V4L2_COLORSPACE_SRGB, - .field = V4L2_FIELD_NONE, - .ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT, - .quantization = V4L2_QUANTIZATION_DEFAULT, - .xfer_func = V4L2_XFER_FUNC_DEFAULT, -}; - -/* read a register */ -static int ov6650_reg_read(struct i2c_client *client, u8 reg, u8 *val) -{ - int ret; - u8 data = reg; - struct i2c_msg msg = { - .addr = client->addr, - .flags = 0, - .len = 1, - .buf = &data, - }; - - ret = i2c_transfer(client->adapter, &msg, 1); - if (ret < 0) - goto err; - - msg.flags = I2C_M_RD; - ret = i2c_transfer(client->adapter, &msg, 1); - if (ret < 0) - goto err; - - *val = data; - return 0; - -err: - dev_err(&client->dev, "Failed reading register 0x%02x!\n", reg); - return ret; -} - -/* write a register */ -static int ov6650_reg_write(struct i2c_client *client, u8 reg, u8 val) -{ - int ret; - unsigned char data[2] = { reg, val }; - struct i2c_msg msg = { - .addr = client->addr, - .flags = 0, - .len = 2, - .buf = data, - }; - - ret = i2c_transfer(client->adapter, &msg, 1); - udelay(100); - - if (ret < 0) { - dev_err(&client->dev, "Failed writing register 0x%02x!\n", reg); - return ret; - } - return 0; -} - - -/* Read a register, alter its bits, write it back */ -static int ov6650_reg_rmw(struct i2c_client *client, u8 reg, u8 set, u8 mask) -{ - u8 val; - int ret; - - ret = ov6650_reg_read(client, reg, &val); - if (ret) { - dev_err(&client->dev, - "[Read]-Modify-Write of register 0x%02x failed!\n", - reg); - return ret; - } - - val &= ~mask; - val |= set; - - ret = ov6650_reg_write(client, reg, val); - if (ret) - dev_err(&client->dev, - "Read-Modify-[Write] of register 0x%02x failed!\n", - reg); - - return ret; -} - -static struct ov6650 *to_ov6650(const struct i2c_client *client) -{ - return container_of(i2c_get_clientdata(client), struct ov6650, subdev); -} - -/* Start/Stop streaming from the device */ -static int ov6650_s_stream(struct v4l2_subdev *sd, int enable) -{ - return 0; -} - -/* Get status of additional camera capabilities */ -static int ov6550_g_volatile_ctrl(struct v4l2_ctrl *ctrl) -{ - struct ov6650 *priv = container_of(ctrl->handler, struct ov6650, hdl); - struct v4l2_subdev *sd = &priv->subdev; - struct i2c_client *client = v4l2_get_subdevdata(sd); - uint8_t reg, reg2; - int ret; - - switch (ctrl->id) { - case V4L2_CID_AUTOGAIN: - ret = ov6650_reg_read(client, REG_GAIN, ®); - if (!ret) - priv->gain->val = reg; - return ret; - case V4L2_CID_AUTO_WHITE_BALANCE: - ret = ov6650_reg_read(client, REG_BLUE, ®); - if (!ret) - ret = ov6650_reg_read(client, REG_RED, ®2); - if (!ret) { - priv->blue->val = reg; - priv->red->val = reg2; - } - return ret; - case V4L2_CID_EXPOSURE_AUTO: - ret = ov6650_reg_read(client, REG_AECH, ®); - if (!ret) - priv->exposure->val = reg; - return ret; - } - return -EINVAL; -} - -/* Set status of additional camera capabilities */ -static int ov6550_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct ov6650 *priv = container_of(ctrl->handler, struct ov6650, hdl); - struct v4l2_subdev *sd = &priv->subdev; - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - - switch (ctrl->id) { - case V4L2_CID_AUTOGAIN: - ret = ov6650_reg_rmw(client, REG_COMB, - ctrl->val ? COMB_AGC : 0, COMB_AGC); - if (!ret && !ctrl->val) - ret = ov6650_reg_write(client, REG_GAIN, priv->gain->val); - return ret; - case V4L2_CID_AUTO_WHITE_BALANCE: - ret = ov6650_reg_rmw(client, REG_COMB, - ctrl->val ? COMB_AWB : 0, COMB_AWB); - if (!ret && !ctrl->val) { - ret = ov6650_reg_write(client, REG_BLUE, priv->blue->val); - if (!ret) - ret = ov6650_reg_write(client, REG_RED, - priv->red->val); - } - return ret; - case V4L2_CID_SATURATION: - return ov6650_reg_rmw(client, REG_SAT, SET_SAT(ctrl->val), - SAT_MASK); - case V4L2_CID_HUE: - return ov6650_reg_rmw(client, REG_HUE, SET_HUE(ctrl->val), - HUE_MASK); - case V4L2_CID_BRIGHTNESS: - return ov6650_reg_write(client, REG_BRT, ctrl->val); - case V4L2_CID_EXPOSURE_AUTO: - ret = ov6650_reg_rmw(client, REG_COMB, ctrl->val == - V4L2_EXPOSURE_AUTO ? COMB_AEC : 0, COMB_AEC); - if (!ret && ctrl->val == V4L2_EXPOSURE_MANUAL) - ret = ov6650_reg_write(client, REG_AECH, - priv->exposure->val); - return ret; - case V4L2_CID_GAMMA: - return ov6650_reg_write(client, REG_GAM1, ctrl->val); - case V4L2_CID_VFLIP: - return ov6650_reg_rmw(client, REG_COMB, - ctrl->val ? COMB_FLIP_V : 0, COMB_FLIP_V); - case V4L2_CID_HFLIP: - return ov6650_reg_rmw(client, REG_COMB, - ctrl->val ? COMB_FLIP_H : 0, COMB_FLIP_H); - } - - return -EINVAL; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int ov6650_get_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - u8 val; - - if (reg->reg & ~0xff) - return -EINVAL; - - reg->size = 1; - - ret = ov6650_reg_read(client, reg->reg, &val); - if (!ret) - reg->val = (__u64)val; - - return ret; -} - -static int ov6650_set_register(struct v4l2_subdev *sd, - const struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg & ~0xff || reg->val & ~0xff) - return -EINVAL; - - return ov6650_reg_write(client, reg->reg, reg->val); -} -#endif - -static int ov6650_s_power(struct v4l2_subdev *sd, int on) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov6650 *priv = to_ov6650(client); - int ret = 0; - - if (on) - ret = clk_prepare_enable(priv->clk); - else - clk_disable_unprepare(priv->clk); - - return ret; -} - -static int ov6650_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_selection *sel) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov6650 *priv = to_ov6650(client); - struct v4l2_rect *rect; - - if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - /* pre-select try crop rectangle */ - rect = v4l2_subdev_state_get_crop(sd_state, 0); - - } else { - /* pre-select active crop rectangle */ - rect = &priv->rect; - } - - switch (sel->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.left = DEF_HSTRT << 1; - sel->r.top = DEF_VSTRT << 1; - sel->r.width = W_CIF; - sel->r.height = H_CIF; - return 0; - - case V4L2_SEL_TGT_CROP: - /* use selected crop rectangle */ - sel->r = *rect; - return 0; - - default: - return -EINVAL; - } -} - -static bool is_unscaled_ok(int width, int height, struct v4l2_rect *rect) -{ - return width > rect->width >> 1 || height > rect->height >> 1; -} - -static void ov6650_bind_align_crop_rectangle(struct v4l2_rect *rect) -{ - v4l_bound_align_image(&rect->width, 2, W_CIF, 1, - &rect->height, 2, H_CIF, 1, 0); - v4l_bound_align_image(&rect->left, DEF_HSTRT << 1, - (DEF_HSTRT << 1) + W_CIF - (__s32)rect->width, 1, - &rect->top, DEF_VSTRT << 1, - (DEF_VSTRT << 1) + H_CIF - (__s32)rect->height, - 1, 0); -} - -static int ov6650_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_selection *sel) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov6650 *priv = to_ov6650(client); - int ret; - - if (sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; - - ov6650_bind_align_crop_rectangle(&sel->r); - - if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - struct v4l2_rect *crop = - v4l2_subdev_state_get_crop(sd_state, 0); - struct v4l2_mbus_framefmt *mf = - v4l2_subdev_state_get_format(sd_state, 0); - /* detect current pad config scaling factor */ - bool half_scale = !is_unscaled_ok(mf->width, mf->height, crop); - - /* store new crop rectangle */ - *crop = sel->r; - - /* adjust frame size */ - mf->width = crop->width >> half_scale; - mf->height = crop->height >> half_scale; - - return 0; - } - - /* V4L2_SUBDEV_FORMAT_ACTIVE */ - - /* apply new crop rectangle */ - ret = ov6650_reg_write(client, REG_HSTRT, sel->r.left >> 1); - if (!ret) { - priv->rect.width += priv->rect.left - sel->r.left; - priv->rect.left = sel->r.left; - ret = ov6650_reg_write(client, REG_HSTOP, - (sel->r.left + sel->r.width) >> 1); - } - if (!ret) { - priv->rect.width = sel->r.width; - ret = ov6650_reg_write(client, REG_VSTRT, sel->r.top >> 1); - } - if (!ret) { - priv->rect.height += priv->rect.top - sel->r.top; - priv->rect.top = sel->r.top; - ret = ov6650_reg_write(client, REG_VSTOP, - (sel->r.top + sel->r.height) >> 1); - } - if (!ret) - priv->rect.height = sel->r.height; - - return ret; -} - -static int ov6650_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov6650 *priv = to_ov6650(client); - - if (format->pad) - return -EINVAL; - - /* initialize response with default media bus frame format */ - *mf = ov6650_def_fmt; - - /* update media bus format code and frame size */ - if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_state_get_format(sd_state, 0); - - mf->width = try_fmt->width; - mf->height = try_fmt->height; - mf->code = try_fmt->code; - - } else { - mf->width = priv->rect.width >> priv->half_scale; - mf->height = priv->rect.height >> priv->half_scale; - mf->code = priv->code; - } - return 0; -} - -#define to_clkrc(div) ((div) - 1) - -/* set the format we will capture in */ -static int ov6650_s_fmt(struct v4l2_subdev *sd, u32 code, bool half_scale) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov6650 *priv = to_ov6650(client); - u8 coma_set = 0, coma_mask = 0, coml_set, coml_mask; - int ret; - - /* select color matrix configuration for given color encoding */ - switch (code) { - case MEDIA_BUS_FMT_Y8_1X8: - dev_dbg(&client->dev, "pixel format GREY8_1X8\n"); - coma_mask |= COMA_RGB | COMA_WORD_SWAP | COMA_BYTE_SWAP; - coma_set |= COMA_BW; - break; - case MEDIA_BUS_FMT_YUYV8_2X8: - dev_dbg(&client->dev, "pixel format YUYV8_2X8_LE\n"); - coma_mask |= COMA_RGB | COMA_BW | COMA_BYTE_SWAP; - coma_set |= COMA_WORD_SWAP; - break; - case MEDIA_BUS_FMT_YVYU8_2X8: - dev_dbg(&client->dev, "pixel format YVYU8_2X8_LE (untested)\n"); - coma_mask |= COMA_RGB | COMA_BW | COMA_WORD_SWAP | - COMA_BYTE_SWAP; - break; - case MEDIA_BUS_FMT_UYVY8_2X8: - dev_dbg(&client->dev, "pixel format YUYV8_2X8_BE\n"); - if (half_scale) { - coma_mask |= COMA_RGB | COMA_BW | COMA_WORD_SWAP; - coma_set |= COMA_BYTE_SWAP; - } else { - coma_mask |= COMA_RGB | COMA_BW; - coma_set |= COMA_BYTE_SWAP | COMA_WORD_SWAP; - } - break; - case MEDIA_BUS_FMT_VYUY8_2X8: - dev_dbg(&client->dev, "pixel format YVYU8_2X8_BE (untested)\n"); - if (half_scale) { - coma_mask |= COMA_RGB | COMA_BW; - coma_set |= COMA_BYTE_SWAP | COMA_WORD_SWAP; - } else { - coma_mask |= COMA_RGB | COMA_BW | COMA_WORD_SWAP; - coma_set |= COMA_BYTE_SWAP; - } - break; - case MEDIA_BUS_FMT_SBGGR8_1X8: - dev_dbg(&client->dev, "pixel format SBGGR8_1X8 (untested)\n"); - coma_mask |= COMA_BW | COMA_BYTE_SWAP | COMA_WORD_SWAP; - coma_set |= COMA_RAW_RGB | COMA_RGB; - break; - default: - dev_err(&client->dev, "Pixel format not handled: 0x%x\n", code); - return -EINVAL; - } - - if (code == MEDIA_BUS_FMT_Y8_1X8 || - code == MEDIA_BUS_FMT_SBGGR8_1X8) { - coml_mask = COML_ONE_CHANNEL; - coml_set = 0; - } else { - coml_mask = 0; - coml_set = COML_ONE_CHANNEL; - } - - if (half_scale) { - dev_dbg(&client->dev, "max resolution: QCIF\n"); - coma_set |= COMA_QCIF; - } else { - dev_dbg(&client->dev, "max resolution: CIF\n"); - coma_mask |= COMA_QCIF; - } - - ret = ov6650_reg_rmw(client, REG_COMA, coma_set, coma_mask); - if (!ret) { - priv->half_scale = half_scale; - - ret = ov6650_reg_rmw(client, REG_COML, coml_set, coml_mask); - } - if (!ret) - priv->code = code; - - return ret; -} - -static int ov6650_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov6650 *priv = to_ov6650(client); - struct v4l2_rect *crop; - bool half_scale; - - if (format->pad) - return -EINVAL; - - switch (mf->code) { - case MEDIA_BUS_FMT_Y10_1X10: - mf->code = MEDIA_BUS_FMT_Y8_1X8; - fallthrough; - case MEDIA_BUS_FMT_Y8_1X8: - case MEDIA_BUS_FMT_YVYU8_2X8: - case MEDIA_BUS_FMT_YUYV8_2X8: - case MEDIA_BUS_FMT_VYUY8_2X8: - case MEDIA_BUS_FMT_UYVY8_2X8: - break; - default: - mf->code = MEDIA_BUS_FMT_SBGGR8_1X8; - fallthrough; - case MEDIA_BUS_FMT_SBGGR8_1X8: - break; - } - - if (format->which == V4L2_SUBDEV_FORMAT_TRY) - crop = v4l2_subdev_state_get_crop(sd_state, 0); - else - crop = &priv->rect; - - half_scale = !is_unscaled_ok(mf->width, mf->height, crop); - - if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_state_get_format(sd_state, 0); - - /* store new mbus frame format code and size in pad config */ - try_fmt->width = crop->width >> half_scale; - try_fmt->height = crop->height >> half_scale; - try_fmt->code = mf->code; - - /* return default mbus frame format updated with pad config */ - *mf = ov6650_def_fmt; - mf->width = try_fmt->width; - mf->height = try_fmt->height; - mf->code = try_fmt->code; - - } else { - int ret = 0; - - /* apply new media bus frame format and scaling if changed */ - if (mf->code != priv->code || half_scale != priv->half_scale) - ret = ov6650_s_fmt(sd, mf->code, half_scale); - if (ret) - return ret; - - /* return default format updated with active size and code */ - *mf = ov6650_def_fmt; - mf->width = priv->rect.width >> priv->half_scale; - mf->height = priv->rect.height >> priv->half_scale; - mf->code = priv->code; - } - return 0; -} - -static int ov6650_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->pad || code->index >= ARRAY_SIZE(ov6650_codes)) - return -EINVAL; - - code->code = ov6650_codes[code->index]; - return 0; -} - -static int ov6650_enum_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_frame_interval_enum *fie) -{ - int i; - - /* enumerate supported frame intervals not exceeding 1 second */ - if (fie->index > CLKRC_DIV_MASK || - GET_CLKRC_DIV(fie->index) > FRAME_RATE_MAX) - return -EINVAL; - - for (i = 0; i < ARRAY_SIZE(ov6650_codes); i++) - if (fie->code == ov6650_codes[i]) - break; - if (i == ARRAY_SIZE(ov6650_codes)) - return -EINVAL; - - if (!fie->width || fie->width > W_CIF || - !fie->height || fie->height > H_CIF) - return -EINVAL; - - fie->interval.numerator = GET_CLKRC_DIV(fie->index); - fie->interval.denominator = FRAME_RATE_MAX; - - return 0; -} - -static int ov6650_get_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_frame_interval *ival) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov6650 *priv = to_ov6650(client); - - /* - * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 - * subdev active state API. - */ - if (ival->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - ival->interval = priv->tpf; - - dev_dbg(&client->dev, "Frame interval: %u/%u s\n", - ival->interval.numerator, ival->interval.denominator); - - return 0; -} - -static int ov6650_set_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_frame_interval *ival) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov6650 *priv = to_ov6650(client); - struct v4l2_fract *tpf = &ival->interval; - int div, ret; - - /* - * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 - * subdev active state API. - */ - if (ival->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - if (tpf->numerator == 0 || tpf->denominator == 0) - div = 1; /* Reset to full rate */ - else - div = (tpf->numerator * FRAME_RATE_MAX) / tpf->denominator; - - if (div == 0) - div = 1; - else if (div > GET_CLKRC_DIV(CLKRC_DIV_MASK)) - div = GET_CLKRC_DIV(CLKRC_DIV_MASK); - - ret = ov6650_reg_rmw(client, REG_CLKRC, to_clkrc(div), CLKRC_DIV_MASK); - if (!ret) { - priv->tpf.numerator = div; - priv->tpf.denominator = FRAME_RATE_MAX; - - *tpf = priv->tpf; - } - - return ret; -} - -/* Soft reset the camera. This has nothing to do with the RESET pin! */ -static int ov6650_reset(struct i2c_client *client) -{ - int ret; - - dev_dbg(&client->dev, "reset\n"); - - ret = ov6650_reg_rmw(client, REG_COMA, COMA_RESET, 0); - if (ret) - dev_err(&client->dev, - "An error occurred while entering soft reset!\n"); - - return ret; -} - -/* program default register values */ -static int ov6650_prog_dflt(struct i2c_client *client, u8 clkrc) -{ - int ret; - - dev_dbg(&client->dev, "initializing\n"); - - ret = ov6650_reg_write(client, REG_COMA, 0); /* ~COMA_RESET */ - if (!ret) - ret = ov6650_reg_write(client, REG_CLKRC, clkrc); - if (!ret) - ret = ov6650_reg_rmw(client, REG_COMB, 0, COMB_BAND_FILTER); - - return ret; -} - -static int ov6650_video_probe(struct v4l2_subdev *sd) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov6650 *priv = to_ov6650(client); - const struct ov6650_xclk *xclk = NULL; - unsigned long rate; - u8 pidh, pidl, midh, midl; - int i, ret = 0; - - priv->clk = devm_clk_get(&client->dev, NULL); - if (IS_ERR(priv->clk)) { - ret = PTR_ERR(priv->clk); - dev_err(&client->dev, "clk request err: %d\n", ret); - return ret; - } - - rate = clk_get_rate(priv->clk); - for (i = 0; rate && i < ARRAY_SIZE(ov6650_xclk); i++) { - if (rate != ov6650_xclk[i].rate) - continue; - - xclk = &ov6650_xclk[i]; - dev_info(&client->dev, "using host default clock rate %lukHz\n", - rate / 1000); - break; - } - for (i = 0; !xclk && i < ARRAY_SIZE(ov6650_xclk); i++) { - ret = clk_set_rate(priv->clk, ov6650_xclk[i].rate); - if (ret || clk_get_rate(priv->clk) != ov6650_xclk[i].rate) - continue; - - xclk = &ov6650_xclk[i]; - dev_info(&client->dev, "using negotiated clock rate %lukHz\n", - xclk->rate / 1000); - break; - } - if (!xclk) { - dev_err(&client->dev, "unable to get supported clock rate\n"); - if (!ret) - ret = -EINVAL; - return ret; - } - - ret = ov6650_s_power(sd, 1); - if (ret < 0) - return ret; - - msleep(20); - - /* - * check and show product ID and manufacturer ID - */ - ret = ov6650_reg_read(client, REG_PIDH, &pidh); - if (!ret) - ret = ov6650_reg_read(client, REG_PIDL, &pidl); - if (!ret) - ret = ov6650_reg_read(client, REG_MIDH, &midh); - if (!ret) - ret = ov6650_reg_read(client, REG_MIDL, &midl); - - if (ret) - goto done; - - if ((pidh != OV6650_PIDH) || (pidl != OV6650_PIDL)) { - dev_err(&client->dev, "Product ID error 0x%02x:0x%02x\n", - pidh, pidl); - ret = -ENODEV; - goto done; - } - - dev_info(&client->dev, - "ov6650 Product ID 0x%02x:0x%02x Manufacturer ID 0x%02x:0x%02x\n", - pidh, pidl, midh, midl); - - ret = ov6650_reset(client); - if (!ret) - ret = ov6650_prog_dflt(client, xclk->clkrc); - if (!ret) { - /* driver default frame format, no scaling */ - ret = ov6650_s_fmt(sd, ov6650_def_fmt.code, false); - } - if (!ret) - ret = v4l2_ctrl_handler_setup(&priv->hdl); - -done: - ov6650_s_power(sd, 0); - return ret; -} - -static const struct v4l2_ctrl_ops ov6550_ctrl_ops = { - .g_volatile_ctrl = ov6550_g_volatile_ctrl, - .s_ctrl = ov6550_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops ov6650_core_ops = { -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = ov6650_get_register, - .s_register = ov6650_set_register, -#endif - .s_power = ov6650_s_power, -}; - -/* Request bus settings on camera side */ -static int ov6650_get_mbus_config(struct v4l2_subdev *sd, - unsigned int pad, - struct v4l2_mbus_config *cfg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - u8 comj, comf; - int ret; - - ret = ov6650_reg_read(client, REG_COMJ, &comj); - if (ret) - return ret; - - ret = ov6650_reg_read(client, REG_COMF, &comf); - if (ret) - return ret; - - cfg->type = V4L2_MBUS_PARALLEL; - - cfg->bus.parallel.flags = V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH - | ((comj & COMJ_VSYNC_HIGH) ? V4L2_MBUS_VSYNC_ACTIVE_HIGH - : V4L2_MBUS_VSYNC_ACTIVE_LOW) - | ((comf & COMF_HREF_LOW) ? V4L2_MBUS_HSYNC_ACTIVE_LOW - : V4L2_MBUS_HSYNC_ACTIVE_HIGH) - | ((comj & COMJ_PCLK_RISING) ? V4L2_MBUS_PCLK_SAMPLE_RISING - : V4L2_MBUS_PCLK_SAMPLE_FALLING); - return 0; -} - -static const struct v4l2_subdev_video_ops ov6650_video_ops = { - .s_stream = ov6650_s_stream, -}; - -static const struct v4l2_subdev_pad_ops ov6650_pad_ops = { - .enum_mbus_code = ov6650_enum_mbus_code, - .enum_frame_interval = ov6650_enum_frame_interval, - .get_selection = ov6650_get_selection, - .set_selection = ov6650_set_selection, - .get_fmt = ov6650_get_fmt, - .set_fmt = ov6650_set_fmt, - .get_frame_interval = ov6650_get_frame_interval, - .set_frame_interval = ov6650_set_frame_interval, - .get_mbus_config = ov6650_get_mbus_config, -}; - -static const struct v4l2_subdev_ops ov6650_subdev_ops = { - .core = &ov6650_core_ops, - .video = &ov6650_video_ops, - .pad = &ov6650_pad_ops, -}; - -static const struct v4l2_subdev_internal_ops ov6650_internal_ops = { - .registered = ov6650_video_probe, -}; - -/* - * i2c_driver function - */ -static int ov6650_probe(struct i2c_client *client) -{ - struct ov6650 *priv; - int ret; - - priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - v4l2_i2c_subdev_init(&priv->subdev, client, &ov6650_subdev_ops); - v4l2_ctrl_handler_init(&priv->hdl, 13); - v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - priv->autogain = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops, - V4L2_CID_AUTOGAIN, 0, 1, 1, 1); - priv->gain = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops, - V4L2_CID_GAIN, 0, 0x3f, 1, DEF_GAIN); - priv->autowb = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops, - V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); - priv->blue = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops, - V4L2_CID_BLUE_BALANCE, 0, 0xff, 1, DEF_BLUE); - priv->red = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops, - V4L2_CID_RED_BALANCE, 0, 0xff, 1, DEF_RED); - v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops, - V4L2_CID_SATURATION, 0, 0xf, 1, 0x8); - v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops, - V4L2_CID_HUE, 0, HUE_MASK, 1, DEF_HUE); - v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 0xff, 1, 0x80); - priv->autoexposure = v4l2_ctrl_new_std_menu(&priv->hdl, - &ov6550_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, - V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO); - priv->exposure = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops, - V4L2_CID_EXPOSURE, 0, 0xff, 1, DEF_AECH); - v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops, - V4L2_CID_GAMMA, 0, 0xff, 1, 0x12); - - priv->subdev.ctrl_handler = &priv->hdl; - if (priv->hdl.error) { - ret = priv->hdl.error; - goto ectlhdlfree; - } - - v4l2_ctrl_auto_cluster(2, &priv->autogain, 0, true); - v4l2_ctrl_auto_cluster(3, &priv->autowb, 0, true); - v4l2_ctrl_auto_cluster(2, &priv->autoexposure, - V4L2_EXPOSURE_MANUAL, true); - - priv->rect.left = DEF_HSTRT << 1; - priv->rect.top = DEF_VSTRT << 1; - priv->rect.width = W_CIF; - priv->rect.height = H_CIF; - - /* Hardware default frame interval */ - priv->tpf.numerator = GET_CLKRC_DIV(DEF_CLKRC); - priv->tpf.denominator = FRAME_RATE_MAX; - - priv->subdev.internal_ops = &ov6650_internal_ops; - - ret = v4l2_async_register_subdev(&priv->subdev); - if (!ret) - return 0; -ectlhdlfree: - v4l2_ctrl_handler_free(&priv->hdl); - - return ret; -} - -static void ov6650_remove(struct i2c_client *client) -{ - struct ov6650 *priv = to_ov6650(client); - - v4l2_async_unregister_subdev(&priv->subdev); - v4l2_ctrl_handler_free(&priv->hdl); -} - -static const struct i2c_device_id ov6650_id[] = { - { "ov6650" }, - { } -}; -MODULE_DEVICE_TABLE(i2c, ov6650_id); - -static struct i2c_driver ov6650_i2c_driver = { - .driver = { - .name = "ov6650", - }, - .probe = ov6650_probe, - .remove = ov6650_remove, - .id_table = ov6650_id, -}; - -module_i2c_driver(ov6650_i2c_driver); - -MODULE_DESCRIPTION("V4L2 subdevice driver for OmniVision OV6650 camera sensor"); -MODULE_AUTHOR("Janusz Krzysztofik <jmkrzyszt@gmail.com"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ov7251.c b/drivers/media/i2c/ov7251.c index 30f61e04ecaf..27afc3fc0175 100644 --- a/drivers/media/i2c/ov7251.c +++ b/drivers/media/i2c/ov7251.c @@ -922,6 +922,8 @@ static int ov7251_set_power_on(struct device *dev) return ret; } + usleep_range(1000, 1100); + gpiod_set_value_cansleep(ov7251->enable_gpio, 1); /* wait at least 65536 external clock cycles */ @@ -1484,9 +1486,14 @@ static int ov7251_check_hwcfg(struct ov7251 *ov7251) unsigned int i, j; int ret; + /* + * Sometimes the fwnode graph is initialized by the bridge driver + * Bridge drivers doing this may also add GPIO mappings, wait for this. + */ endpoint = fwnode_graph_get_next_endpoint(fwnode, NULL); if (!endpoint) - return -EPROBE_DEFER; /* could be provided by cio2-bridge */ + return dev_err_probe(ov7251->dev, -EPROBE_DEFER, + "waiting for fwnode graph endpoint\n"); ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg); fwnode_handle_put(endpoint); @@ -1623,7 +1630,6 @@ static int ov7251_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct ov7251 *ov7251; - unsigned int rate = 0, clk_rate = 0; int ret; int i; @@ -1639,33 +1645,12 @@ static int ov7251_probe(struct i2c_client *client) return ret; /* get system clock (xclk) */ - ov7251->xclk = devm_clk_get_optional(dev, NULL); + ov7251->xclk = devm_v4l2_sensor_clk_get(dev, NULL); if (IS_ERR(ov7251->xclk)) return dev_err_probe(dev, PTR_ERR(ov7251->xclk), "could not get xclk"); - /* - * We could have either a 24MHz or 19.2MHz clock rate from either DT or - * ACPI. We also need to support the IPU3 case which will have both an - * external clock AND a clock-frequency property. - */ - ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", - &rate); - if (ret && !ov7251->xclk) - return dev_err_probe(dev, ret, "invalid clock config\n"); - - clk_rate = clk_get_rate(ov7251->xclk); - ov7251->xclk_freq = clk_rate ? clk_rate : rate; - - if (ov7251->xclk_freq == 0) - return dev_err_probe(dev, -EINVAL, "invalid clock frequency\n"); - - if (!ret && ov7251->xclk) { - ret = clk_set_rate(ov7251->xclk, rate); - if (ret) - return dev_err_probe(dev, ret, - "failed to set clock rate\n"); - } + ov7251->xclk_freq = clk_get_rate(ov7251->xclk); for (i = 0; i < ARRAY_SIZE(supported_xclk_rates); i++) if (ov7251->xclk_freq == supported_xclk_rates[i]) @@ -1696,7 +1681,7 @@ static int ov7251_probe(struct i2c_client *client) return PTR_ERR(ov7251->analog_regulator); } - ov7251->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_HIGH); + ov7251->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); if (IS_ERR(ov7251->enable_gpio)) { dev_err(dev, "cannot get enable gpio\n"); return PTR_ERR(ov7251->enable_gpio); diff --git a/drivers/media/i2c/ov7740.c b/drivers/media/i2c/ov7740.c index 1f1c0de8e510..632fb80469be 100644 --- a/drivers/media/i2c/ov7740.c +++ b/drivers/media/i2c/ov7740.c @@ -1036,13 +1036,10 @@ static int ov7740_probe(struct i2c_client *client) if (!ov7740) return -ENOMEM; - ov7740->xvclk = devm_clk_get(&client->dev, "xvclk"); - if (IS_ERR(ov7740->xvclk)) { - ret = PTR_ERR(ov7740->xvclk); - dev_err(&client->dev, - "OV7740: fail to get xvclk: %d\n", ret); - return ret; - } + ov7740->xvclk = devm_v4l2_sensor_clk_get(&client->dev, "xvclk"); + if (IS_ERR(ov7740->xvclk)) + return dev_err_probe(&client->dev, PTR_ERR(ov7740->xvclk), + "OV7740: fail to get xvclk\n"); ret = ov7740_probe_dt(client, ov7740); if (ret) diff --git a/drivers/media/i2c/ov8856.c b/drivers/media/i2c/ov8856.c index e6704d018248..e2998cfa0d18 100644 --- a/drivers/media/i2c/ov8856.c +++ b/drivers/media/i2c/ov8856.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2019 Intel Corporation. -#include <linux/unaligned.h> #include <linux/acpi.h> #include <linux/clk.h> #include <linux/delay.h> @@ -10,6 +9,8 @@ #include <linux/module.h> #include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> +#include <linux/unaligned.h> + #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-fwnode.h> @@ -1414,6 +1415,8 @@ static const struct ov8856_reg_list bayer_offset_configs[] = { }; struct ov8856 { + struct device *dev; + struct v4l2_subdev sd; struct media_pad pad; struct v4l2_ctrl_handler ctrl_handler; @@ -1668,7 +1671,6 @@ static int ov8856_write_reg(struct ov8856 *ov8856, u16 reg, u16 len, u32 val) static int ov8856_write_reg_list(struct ov8856 *ov8856, const struct ov8856_reg_list *r_list) { - struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd); unsigned int i; int ret; @@ -1676,7 +1678,7 @@ static int ov8856_write_reg_list(struct ov8856 *ov8856, ret = ov8856_write_reg(ov8856, r_list->regs[i].address, 1, r_list->regs[i].val); if (ret) { - dev_err_ratelimited(&client->dev, + dev_err_ratelimited(ov8856->dev, "failed to write reg 0x%4.4x. error = %d", r_list->regs[i].address, ret); return ret; @@ -1688,7 +1690,6 @@ static int ov8856_write_reg_list(struct ov8856 *ov8856, static int ov8856_identify_module(struct ov8856 *ov8856) { - struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd); int ret; u32 val; @@ -1701,7 +1702,7 @@ static int ov8856_identify_module(struct ov8856 *ov8856) return ret; if (val != OV8856_CHIP_ID) { - dev_err(&client->dev, "chip id mismatch: %x!=%x", + dev_err(ov8856->dev, "chip id mismatch: %x!=%x", OV8856_CHIP_ID, val); return -ENXIO; } @@ -1818,7 +1819,6 @@ static int ov8856_set_ctrl(struct v4l2_ctrl *ctrl) { struct ov8856 *ov8856 = container_of(ctrl->handler, struct ov8856, ctrl_handler); - struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd); s64 exposure_max; int ret = 0; @@ -1834,7 +1834,7 @@ static int ov8856_set_ctrl(struct v4l2_ctrl *ctrl) } /* V4L2 controls values will be applied only when power is already up */ - if (!pm_runtime_get_if_in_use(&client->dev)) + if (!pm_runtime_get_if_in_use(ov8856->dev)) return 0; switch (ctrl->id) { @@ -1876,7 +1876,7 @@ static int ov8856_set_ctrl(struct v4l2_ctrl *ctrl) break; } - pm_runtime_put(&client->dev); + pm_runtime_put(ov8856->dev); return ret; } @@ -1979,7 +1979,6 @@ static void ov8856_update_pad_format(struct ov8856 *ov8856, static int ov8856_start_streaming(struct ov8856 *ov8856) { - struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd); const struct ov8856_reg_list *reg_list; int link_freq_index, ret; @@ -1992,21 +1991,21 @@ static int ov8856_start_streaming(struct ov8856 *ov8856) ret = ov8856_write_reg_list(ov8856, reg_list); if (ret) { - dev_err(&client->dev, "failed to set plls"); + dev_err(ov8856->dev, "failed to set plls"); return ret; } reg_list = &ov8856->cur_mode->reg_list; ret = ov8856_write_reg_list(ov8856, reg_list); if (ret) { - dev_err(&client->dev, "failed to set mode"); + dev_err(ov8856->dev, "failed to set mode"); return ret; } reg_list = &bayer_offset_configs[ov8856->cur_mbus_index]; ret = ov8856_write_reg_list(ov8856, reg_list); if (ret) { - dev_err(&client->dev, "failed to set mbus format"); + dev_err(ov8856->dev, "failed to set mbus format"); return ret; } @@ -2017,7 +2016,7 @@ static int ov8856_start_streaming(struct ov8856 *ov8856) ret = ov8856_write_reg(ov8856, OV8856_REG_MODE_SELECT, OV8856_REG_VALUE_08BIT, OV8856_MODE_STREAMING); if (ret) { - dev_err(&client->dev, "failed to set stream"); + dev_err(ov8856->dev, "failed to set stream"); return ret; } @@ -2026,22 +2025,19 @@ static int ov8856_start_streaming(struct ov8856 *ov8856) static void ov8856_stop_streaming(struct ov8856 *ov8856) { - struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd); - if (ov8856_write_reg(ov8856, OV8856_REG_MODE_SELECT, OV8856_REG_VALUE_08BIT, OV8856_MODE_STANDBY)) - dev_err(&client->dev, "failed to set stream"); + dev_err(ov8856->dev, "failed to set stream"); } static int ov8856_set_stream(struct v4l2_subdev *sd, int enable) { struct ov8856 *ov8856 = to_ov8856(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); int ret = 0; mutex_lock(&ov8856->mutex); if (enable) { - ret = pm_runtime_resume_and_get(&client->dev); + ret = pm_runtime_resume_and_get(ov8856->dev); if (ret < 0) { mutex_unlock(&ov8856->mutex); return ret; @@ -2051,11 +2047,11 @@ static int ov8856_set_stream(struct v4l2_subdev *sd, int enable) if (ret) { enable = 0; ov8856_stop_streaming(ov8856); - pm_runtime_put(&client->dev); + pm_runtime_put(ov8856->dev); } } else { ov8856_stop_streaming(ov8856); - pm_runtime_put(&client->dev); + pm_runtime_put(ov8856->dev); } mutex_unlock(&ov8856->mutex); @@ -2255,8 +2251,9 @@ static const struct v4l2_subdev_internal_ops ov8856_internal_ops = { }; -static int ov8856_get_hwcfg(struct ov8856 *ov8856, struct device *dev) +static int ov8856_get_hwcfg(struct ov8856 *ov8856) { + struct device *dev = ov8856->dev; struct fwnode_handle *ep; struct fwnode_handle *fwnode = dev_fwnode(dev); struct v4l2_fwnode_endpoint bus_cfg = { @@ -2269,21 +2266,17 @@ static int ov8856_get_hwcfg(struct ov8856 *ov8856, struct device *dev) if (!fwnode) return -ENXIO; - ret = fwnode_property_read_u32(fwnode, "clock-frequency", &xvclk_rate); - if (ret) - return ret; - - if (!is_acpi_node(fwnode)) { - ov8856->xvclk = devm_clk_get(dev, "xvclk"); - if (IS_ERR(ov8856->xvclk)) { - dev_err(dev, "could not get xvclk clock (%pe)\n", - ov8856->xvclk); - return PTR_ERR(ov8856->xvclk); - } + ov8856->xvclk = devm_v4l2_sensor_clk_get_legacy(dev, "xvclk", false, 0); + if (IS_ERR(ov8856->xvclk)) + return dev_err_probe(dev, PTR_ERR(ov8856->xvclk), + "could not get xvclk clock\n"); - clk_set_rate(ov8856->xvclk, xvclk_rate); - xvclk_rate = clk_get_rate(ov8856->xvclk); + xvclk_rate = clk_get_rate(ov8856->xvclk); + if (xvclk_rate != OV8856_XVCLK_19_2) + dev_warn(dev, "external clock rate %u is unsupported", + xvclk_rate); + if (!is_acpi_node(fwnode)) { ov8856->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(ov8856->reset_gpio)) @@ -2299,10 +2292,6 @@ static int ov8856_get_hwcfg(struct ov8856 *ov8856, struct device *dev) return ret; } - if (xvclk_rate != OV8856_XVCLK_19_2) - dev_warn(dev, "external clock rate %u is unsupported", - xvclk_rate); - ep = fwnode_graph_get_next_endpoint(fwnode, NULL); if (!ep) return -ENXIO; @@ -2365,10 +2354,10 @@ static void ov8856_remove(struct i2c_client *client) v4l2_async_unregister_subdev(sd); media_entity_cleanup(&sd->entity); v4l2_ctrl_handler_free(sd->ctrl_handler); - pm_runtime_disable(&client->dev); + pm_runtime_disable(ov8856->dev); mutex_destroy(&ov8856->mutex); - ov8856_power_off(&client->dev); + ov8856_power_off(ov8856->dev); } static int ov8856_probe(struct i2c_client *client) @@ -2381,26 +2370,25 @@ static int ov8856_probe(struct i2c_client *client) if (!ov8856) return -ENOMEM; - ret = ov8856_get_hwcfg(ov8856, &client->dev); - if (ret) { - dev_err(&client->dev, "failed to get HW configuration: %d", - ret); + ov8856->dev = &client->dev; + + ret = ov8856_get_hwcfg(ov8856); + if (ret) return ret; - } v4l2_i2c_subdev_init(&ov8856->sd, client, &ov8856_subdev_ops); - full_power = acpi_dev_state_d0(&client->dev); + full_power = acpi_dev_state_d0(ov8856->dev); if (full_power) { - ret = ov8856_power_on(&client->dev); + ret = ov8856_power_on(ov8856->dev); if (ret) { - dev_err(&client->dev, "failed to power on\n"); + dev_err(ov8856->dev, "failed to power on\n"); return ret; } ret = ov8856_identify_module(ov8856); if (ret) { - dev_err(&client->dev, "failed to find sensor: %d", ret); + dev_err(ov8856->dev, "failed to find sensor: %d", ret); goto probe_power_off; } } @@ -2410,7 +2398,7 @@ static int ov8856_probe(struct i2c_client *client) ov8856->cur_mbus_index = ov8856->cur_mode->default_mbus_index; ret = ov8856_init_controls(ov8856); if (ret) { - dev_err(&client->dev, "failed to init controls: %d", ret); + dev_err(ov8856->dev, "failed to init controls: %d", ret); goto probe_error_v4l2_ctrl_handler_free; } @@ -2421,22 +2409,22 @@ static int ov8856_probe(struct i2c_client *client) ov8856->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&ov8856->sd.entity, 1, &ov8856->pad); if (ret) { - dev_err(&client->dev, "failed to init entity pads: %d", ret); + dev_err(ov8856->dev, "failed to init entity pads: %d", ret); goto probe_error_v4l2_ctrl_handler_free; } ret = v4l2_async_register_subdev_sensor(&ov8856->sd); if (ret < 0) { - dev_err(&client->dev, "failed to register V4L2 subdev: %d", + dev_err(ov8856->dev, "failed to register V4L2 subdev: %d", ret); goto probe_error_media_entity_cleanup; } /* Set the device's state to active if it's in D0 state. */ if (full_power) - pm_runtime_set_active(&client->dev); - pm_runtime_enable(&client->dev); - pm_runtime_idle(&client->dev); + pm_runtime_set_active(ov8856->dev); + pm_runtime_enable(ov8856->dev); + pm_runtime_idle(ov8856->dev); return 0; @@ -2448,7 +2436,7 @@ probe_error_v4l2_ctrl_handler_free: mutex_destroy(&ov8856->mutex); probe_power_off: - ov8856_power_off(&client->dev); + ov8856_power_off(ov8856->dev); return ret; } diff --git a/drivers/media/i2c/ov8858.c b/drivers/media/i2c/ov8858.c index 95f9ae794846..3f45f7fab833 100644 --- a/drivers/media/i2c/ov8858.c +++ b/drivers/media/i2c/ov8858.c @@ -1391,7 +1391,6 @@ static int ov8858_s_stream(struct v4l2_subdev *sd, int on) } } else { ov8858_stop_stream(ov8858); - pm_runtime_mark_last_busy(&client->dev); pm_runtime_put_autosuspend(&client->dev); } @@ -1877,7 +1876,7 @@ static int ov8858_probe(struct i2c_client *client) if (!ov8858) return -ENOMEM; - ov8858->xvclk = devm_clk_get(dev, "xvclk"); + ov8858->xvclk = devm_v4l2_sensor_clk_get(dev, "xvclk"); if (IS_ERR(ov8858->xvclk)) return dev_err_probe(dev, PTR_ERR(ov8858->xvclk), "Failed to get xvclk\n"); @@ -1945,7 +1944,6 @@ static int ov8858_probe(struct i2c_client *client) goto err_power_off; } - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return 0; diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c index 95ffe7536aa6..a8586df14f77 100644 --- a/drivers/media/i2c/ov8865.c +++ b/drivers/media/i2c/ov8865.c @@ -2304,14 +2304,6 @@ static int ov8865_state_configure(struct ov8865_sensor *sensor, if (sensor->state.streaming) return -EBUSY; - /* State will be configured at first power on otherwise. */ - if (pm_runtime_enabled(sensor->dev) && - !pm_runtime_suspended(sensor->dev)) { - ret = ov8865_mode_configure(sensor, mode, mbus_code); - if (ret) - return ret; - } - ret = ov8865_state_mipi_configure(sensor, mode, mbus_code); if (ret) return ret; @@ -2384,10 +2376,10 @@ static int ov8865_sensor_init(struct ov8865_sensor *sensor) } /* Configure current mode. */ - ret = ov8865_state_configure(sensor, sensor->state.mode, - sensor->state.mbus_code); + ret = ov8865_mode_configure(sensor, sensor->state.mode, + sensor->state.mbus_code); if (ret) { - dev_err(sensor->dev, "failed to configure state\n"); + dev_err(sensor->dev, "failed to configure mode\n"); return ret; } @@ -2956,7 +2948,6 @@ static int ov8865_probe(struct i2c_client *client) struct ov8865_sensor *sensor; struct v4l2_subdev *subdev; struct media_pad *pad; - unsigned int rate = 0; unsigned int i; int ret; @@ -2991,7 +2982,8 @@ static int ov8865_probe(struct i2c_client *client) handle = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); if (!handle) - return -EPROBE_DEFER; + return dev_err_probe(dev, -EPROBE_DEFER, + "waiting for fwnode graph endpoint\n"); sensor->endpoint.bus_type = V4L2_MBUS_CSI2_DPHY; @@ -3019,39 +3011,14 @@ static int ov8865_probe(struct i2c_client *client) /* External Clock */ - sensor->extclk = devm_clk_get(dev, NULL); - if (PTR_ERR(sensor->extclk) == -ENOENT) { - dev_info(dev, "no external clock found, continuing...\n"); - sensor->extclk = NULL; - } else if (IS_ERR(sensor->extclk)) { - dev_err(dev, "failed to get external clock\n"); - ret = PTR_ERR(sensor->extclk); - goto error_endpoint; - } - - /* - * We could have either a 24MHz or 19.2MHz clock rate from either dt or - * ACPI...but we also need to support the weird IPU3 case which will - * have an external clock AND a clock-frequency property. Check for the - * clock-frequency property and if found, set that rate if we managed - * to acquire a clock. This should cover the ACPI case. If the system - * uses devicetree then the configured rate should already be set, so - * we can just read it. - */ - ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", - &rate); - if (!ret && sensor->extclk) { - ret = clk_set_rate(sensor->extclk, rate); - if (ret) { - dev_err_probe(dev, ret, "failed to set clock rate\n"); - goto error_endpoint; - } - } else if (ret && !sensor->extclk) { - dev_err_probe(dev, ret, "invalid clock config\n"); + sensor->extclk = devm_v4l2_sensor_clk_get(dev, NULL); + if (IS_ERR(sensor->extclk)) { + ret = dev_err_probe(dev, PTR_ERR(sensor->extclk), + "failed to get external clock\n"); goto error_endpoint; } - sensor->extclk_rate = rate ? rate : clk_get_rate(sensor->extclk); + sensor->extclk_rate = clk_get_rate(sensor->extclk); for (i = 0; i < ARRAY_SIZE(supported_extclk_rates); i++) { if (sensor->extclk_rate == supported_extclk_rates[i]) diff --git a/drivers/media/i2c/ov9282.c b/drivers/media/i2c/ov9282.c index 9f52af6f047f..3e24d88f603c 100644 --- a/drivers/media/i2c/ov9282.c +++ b/drivers/media/i2c/ov9282.c @@ -40,10 +40,19 @@ /* Exposure control */ #define OV9282_REG_EXPOSURE 0x3500 #define OV9282_EXPOSURE_MIN 1 -#define OV9282_EXPOSURE_OFFSET 12 +#define OV9282_EXPOSURE_OFFSET 25 #define OV9282_EXPOSURE_STEP 1 #define OV9282_EXPOSURE_DEFAULT 0x0282 +/* AEC/AGC manual */ +#define OV9282_REG_AEC_MANUAL 0x3503 +#define OV9282_DIGFRAC_GAIN_DELAY BIT(6) +#define OV9282_GAIN_CHANGE_DELAY BIT(5) +#define OV9282_GAIN_DELAY BIT(4) +#define OV9282_GAIN_PREC16_EN BIT(3) +#define OV9282_GAIN_MANUAL_AS_SENSGAIN BIT(2) +#define OV9282_AEC_MANUAL_DEFAULT 0x00 + /* Analog gain control */ #define OV9282_REG_AGAIN 0x3509 #define OV9282_AGAIN_MIN 0x10 @@ -214,7 +223,7 @@ static const struct ov9282_reg common_regs[] = { {0x3030, 0x10}, {0x3039, 0x32}, {0x303a, 0x00}, - {0x3503, 0x08}, + {OV9282_REG_AEC_MANUAL, OV9282_GAIN_PREC16_EN}, {0x3505, 0x8c}, {0x3507, 0x03}, {0x3508, 0x00}, @@ -296,8 +305,8 @@ static const struct ov9282_reg mode_1280x800_regs[] = { {0x3813, 0x08}, {0x3814, 0x11}, {0x3815, 0x11}, - {0x3820, 0x40}, - {0x3821, 0x00}, + {OV9282_REG_TIMING_FORMAT_1, 0x40}, + {OV9282_REG_TIMING_FORMAT_2, 0x00}, {0x4003, 0x40}, {0x4008, 0x04}, {0x4009, 0x0b}, @@ -327,8 +336,8 @@ static const struct ov9282_reg mode_1280x720_regs[] = { {0x3813, 0x08}, {0x3814, 0x11}, {0x3815, 0x11}, - {0x3820, 0x3c}, - {0x3821, 0x84}, + {OV9282_REG_TIMING_FORMAT_1, 0x3c}, + {OV9282_REG_TIMING_FORMAT_2, 0x84}, {0x4003, 0x40}, {0x4008, 0x02}, {0x4009, 0x05}, @@ -358,8 +367,8 @@ static const struct ov9282_reg mode_640x400_regs[] = { {0x3813, 0x04}, {0x3814, 0x31}, {0x3815, 0x22}, - {0x3820, 0x60}, - {0x3821, 0x01}, + {OV9282_REG_TIMING_FORMAT_1, 0x60}, + {OV9282_REG_TIMING_FORMAT_2, 0x01}, {0x4008, 0x02}, {0x4009, 0x05}, {0x400c, 0x00}, @@ -1120,17 +1129,16 @@ static int ov9282_parse_hw_config(struct ov9282 *ov9282) ov9282->reset_gpio = devm_gpiod_get_optional(ov9282->dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(ov9282->reset_gpio)) { - dev_err(ov9282->dev, "failed to get reset gpio %ld", - PTR_ERR(ov9282->reset_gpio)); + dev_err(ov9282->dev, "failed to get reset gpio %pe", + ov9282->reset_gpio); return PTR_ERR(ov9282->reset_gpio); } /* Get sensor input clock */ - ov9282->inclk = devm_clk_get(ov9282->dev, NULL); - if (IS_ERR(ov9282->inclk)) { - dev_err(ov9282->dev, "could not get inclk"); - return PTR_ERR(ov9282->inclk); - } + ov9282->inclk = devm_v4l2_sensor_clk_get(ov9282->dev, NULL); + if (IS_ERR(ov9282->inclk)) + return dev_err_probe(ov9282->dev, PTR_ERR(ov9282->inclk), + "could not get inclk\n"); ret = ov9282_configure_regulators(ov9282); if (ret) diff --git a/drivers/media/i2c/ov9640.c b/drivers/media/i2c/ov9640.c index 01dbc0ba89c8..2190c52b1433 100644 --- a/drivers/media/i2c/ov9640.c +++ b/drivers/media/i2c/ov9640.c @@ -718,9 +718,10 @@ static int ov9640_probe(struct i2c_client *client) priv->subdev.ctrl_handler = &priv->hdl; - priv->clk = devm_clk_get(&client->dev, "mclk"); + priv->clk = devm_v4l2_sensor_clk_get(&client->dev, "mclk"); if (IS_ERR(priv->clk)) { - ret = PTR_ERR(priv->clk); + ret = dev_err_probe(&client->dev, PTR_ERR(priv->clk), + "failed to get mclk\n"); goto ectrlinit; } diff --git a/drivers/media/i2c/ov9650.c b/drivers/media/i2c/ov9650.c index 026ea34d6291..c94e8fe29f22 100644 --- a/drivers/media/i2c/ov9650.c +++ b/drivers/media/i2c/ov9650.c @@ -1494,9 +1494,10 @@ static int ov965x_probe(struct i2c_client *client) } if (dev_fwnode(&client->dev)) { - ov965x->clk = devm_clk_get(&client->dev, NULL); + ov965x->clk = devm_v4l2_sensor_clk_get(&client->dev, NULL); if (IS_ERR(ov965x->clk)) - return PTR_ERR(ov965x->clk); + return dev_err_probe(&client->dev, PTR_ERR(ov965x->clk), + "failed to get the clock\n"); ov965x->mclk_frequency = clk_get_rate(ov965x->clk); ret = ov965x_configure_gpios(ov965x); diff --git a/drivers/media/i2c/ov9734.c b/drivers/media/i2c/ov9734.c index cae3aeefb616..0eaf33807fc9 100644 --- a/drivers/media/i2c/ov9734.c +++ b/drivers/media/i2c/ov9734.c @@ -1,12 +1,14 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2020 Intel Corporation. -#include <linux/unaligned.h> #include <linux/acpi.h> +#include <linux/clk.h> #include <linux/delay.h> #include <linux/i2c.h> #include <linux/module.h> #include <linux/pm_runtime.h> +#include <linux/unaligned.h> + #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-fwnode.h> @@ -321,6 +323,9 @@ static const struct ov9734_mode supported_modes[] = { }; struct ov9734 { + struct device *dev; + struct clk *clk; + struct v4l2_subdev sd; struct media_pad pad; struct v4l2_ctrl_handler ctrl_handler; @@ -414,7 +419,6 @@ static int ov9734_write_reg(struct ov9734 *ov9734, u16 reg, u16 len, u32 val) static int ov9734_write_reg_list(struct ov9734 *ov9734, const struct ov9734_reg_list *r_list) { - struct i2c_client *client = v4l2_get_subdevdata(&ov9734->sd); unsigned int i; int ret; @@ -422,7 +426,7 @@ static int ov9734_write_reg_list(struct ov9734 *ov9734, ret = ov9734_write_reg(ov9734, r_list->regs[i].address, 1, r_list->regs[i].val); if (ret) { - dev_err_ratelimited(&client->dev, + dev_err_ratelimited(ov9734->dev, "write reg 0x%4.4x return err = %d", r_list->regs[i].address, ret); return ret; @@ -476,7 +480,6 @@ static int ov9734_set_ctrl(struct v4l2_ctrl *ctrl) { struct ov9734 *ov9734 = container_of(ctrl->handler, struct ov9734, ctrl_handler); - struct i2c_client *client = v4l2_get_subdevdata(&ov9734->sd); s64 exposure_max; int ret = 0; @@ -492,7 +495,7 @@ static int ov9734_set_ctrl(struct v4l2_ctrl *ctrl) } /* V4L2 controls values will be applied only when power is already up */ - if (!pm_runtime_get_if_in_use(&client->dev)) + if (!pm_runtime_get_if_in_use(ov9734->dev)) return 0; switch (ctrl->id) { @@ -525,7 +528,7 @@ static int ov9734_set_ctrl(struct v4l2_ctrl *ctrl) break; } - pm_runtime_put(&client->dev); + pm_runtime_put(ov9734->dev); return ret; } @@ -610,7 +613,6 @@ static void ov9734_update_pad_format(const struct ov9734_mode *mode, static int ov9734_start_streaming(struct ov9734 *ov9734) { - struct i2c_client *client = v4l2_get_subdevdata(&ov9734->sd); const struct ov9734_reg_list *reg_list; int link_freq_index, ret; @@ -618,14 +620,14 @@ static int ov9734_start_streaming(struct ov9734 *ov9734) reg_list = &link_freq_configs[link_freq_index].reg_list; ret = ov9734_write_reg_list(ov9734, reg_list); if (ret) { - dev_err(&client->dev, "failed to set plls"); + dev_err(ov9734->dev, "failed to set plls"); return ret; } reg_list = &ov9734->cur_mode->reg_list; ret = ov9734_write_reg_list(ov9734, reg_list); if (ret) { - dev_err(&client->dev, "failed to set mode"); + dev_err(ov9734->dev, "failed to set mode"); return ret; } @@ -636,30 +638,27 @@ static int ov9734_start_streaming(struct ov9734 *ov9734) ret = ov9734_write_reg(ov9734, OV9734_REG_MODE_SELECT, 1, OV9734_MODE_STREAMING); if (ret) - dev_err(&client->dev, "failed to start stream"); + dev_err(ov9734->dev, "failed to start stream"); return ret; } static void ov9734_stop_streaming(struct ov9734 *ov9734) { - struct i2c_client *client = v4l2_get_subdevdata(&ov9734->sd); - if (ov9734_write_reg(ov9734, OV9734_REG_MODE_SELECT, 1, OV9734_MODE_STANDBY)) - dev_err(&client->dev, "failed to stop stream"); + dev_err(ov9734->dev, "failed to stop stream"); } static int ov9734_set_stream(struct v4l2_subdev *sd, int enable) { struct ov9734 *ov9734 = to_ov9734(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); int ret = 0; mutex_lock(&ov9734->mutex); if (enable) { - ret = pm_runtime_resume_and_get(&client->dev); + ret = pm_runtime_resume_and_get(ov9734->dev); if (ret < 0) { mutex_unlock(&ov9734->mutex); return ret; @@ -669,11 +668,11 @@ static int ov9734_set_stream(struct v4l2_subdev *sd, int enable) if (ret) { enable = 0; ov9734_stop_streaming(ov9734); - pm_runtime_put(&client->dev); + pm_runtime_put(ov9734->dev); } } else { ov9734_stop_streaming(ov9734); - pm_runtime_put(&client->dev); + pm_runtime_put(ov9734->dev); } mutex_unlock(&ov9734->mutex); @@ -808,7 +807,6 @@ static const struct v4l2_subdev_internal_ops ov9734_internal_ops = { static int ov9734_identify_module(struct ov9734 *ov9734) { - struct i2c_client *client = v4l2_get_subdevdata(&ov9734->sd); int ret; u32 val; @@ -817,7 +815,7 @@ static int ov9734_identify_module(struct ov9734 *ov9734) return ret; if (val != OV9734_CHIP_ID) { - dev_err(&client->dev, "chip id mismatch: %x!=%x", + dev_err(ov9734->dev, "chip id mismatch: %x!=%x", OV9734_CHIP_ID, val); return -ENXIO; } @@ -832,22 +830,12 @@ static int ov9734_check_hwcfg(struct device *dev) struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = V4L2_MBUS_CSI2_DPHY }; - u32 mclk; int ret; unsigned int i, j; if (!fwnode) return -ENXIO; - ret = fwnode_property_read_u32(fwnode, "clock-frequency", &mclk); - if (ret) - return ret; - - if (mclk != OV9734_MCLK) { - dev_err(dev, "external clock %d is not supported", mclk); - return -EINVAL; - } - ep = fwnode_graph_get_next_endpoint(fwnode, NULL); if (!ep) return -ENXIO; @@ -892,14 +880,15 @@ static void ov9734_remove(struct i2c_client *client) v4l2_async_unregister_subdev(sd); media_entity_cleanup(&sd->entity); v4l2_ctrl_handler_free(sd->ctrl_handler); - pm_runtime_disable(&client->dev); - pm_runtime_set_suspended(&client->dev); + pm_runtime_disable(ov9734->dev); + pm_runtime_set_suspended(ov9734->dev); mutex_destroy(&ov9734->mutex); } static int ov9734_probe(struct i2c_client *client) { struct ov9734 *ov9734; + unsigned long freq; int ret; ret = ov9734_check_hwcfg(&client->dev); @@ -913,10 +902,23 @@ static int ov9734_probe(struct i2c_client *client) if (!ov9734) return -ENOMEM; + ov9734->dev = &client->dev; + + ov9734->clk = devm_v4l2_sensor_clk_get(ov9734->dev, NULL); + if (IS_ERR(ov9734->clk)) + return dev_err_probe(ov9734->dev, PTR_ERR(ov9734->clk), + "failed to get clock\n"); + + freq = clk_get_rate(ov9734->clk); + if (freq != OV9734_MCLK) + return dev_err_probe(ov9734->dev, -EINVAL, + "external clock %lu is not supported", + freq); + v4l2_i2c_subdev_init(&ov9734->sd, client, &ov9734_subdev_ops); ret = ov9734_identify_module(ov9734); if (ret) { - dev_err(&client->dev, "failed to find sensor: %d", ret); + dev_err(ov9734->dev, "failed to find sensor: %d", ret); return ret; } @@ -924,7 +926,7 @@ static int ov9734_probe(struct i2c_client *client) ov9734->cur_mode = &supported_modes[0]; ret = ov9734_init_controls(ov9734); if (ret) { - dev_err(&client->dev, "failed to init controls: %d", ret); + dev_err(ov9734->dev, "failed to init controls: %d", ret); goto probe_error_v4l2_ctrl_handler_free; } @@ -935,7 +937,7 @@ static int ov9734_probe(struct i2c_client *client) ov9734->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&ov9734->sd.entity, 1, &ov9734->pad); if (ret) { - dev_err(&client->dev, "failed to init entity pads: %d", ret); + dev_err(ov9734->dev, "failed to init entity pads: %d", ret); goto probe_error_v4l2_ctrl_handler_free; } @@ -943,13 +945,13 @@ static int ov9734_probe(struct i2c_client *client) * Device is already turned on by i2c-core with ACPI domain PM. * Enable runtime PM and turn off the device. */ - pm_runtime_set_active(&client->dev); - pm_runtime_enable(&client->dev); - pm_runtime_idle(&client->dev); + pm_runtime_set_active(ov9734->dev); + pm_runtime_enable(ov9734->dev); + pm_runtime_idle(ov9734->dev); ret = v4l2_async_register_subdev_sensor(&ov9734->sd); if (ret < 0) { - dev_err(&client->dev, "failed to register V4L2 subdev: %d", + dev_err(ov9734->dev, "failed to register V4L2 subdev: %d", ret); goto probe_error_media_entity_cleanup_pm; } @@ -957,8 +959,8 @@ static int ov9734_probe(struct i2c_client *client) return 0; probe_error_media_entity_cleanup_pm: - pm_runtime_disable(&client->dev); - pm_runtime_set_suspended(&client->dev); + pm_runtime_disable(ov9734->dev); + pm_runtime_set_suspended(ov9734->dev); media_entity_cleanup(&ov9734->sd.entity); probe_error_v4l2_ctrl_handler_free: diff --git a/drivers/media/i2c/rdacm20.c b/drivers/media/i2c/rdacm20.c index b8bd8354d100..52e8e2620b4d 100644 --- a/drivers/media/i2c/rdacm20.c +++ b/drivers/media/i2c/rdacm20.c @@ -16,10 +16,10 @@ */ #include <linux/delay.h> -#include <linux/fwnode.h> #include <linux/init.h> #include <linux/i2c.h> #include <linux/module.h> +#include <linux/property.h> #include <linux/slab.h> #include <linux/videodev2.h> @@ -575,10 +575,9 @@ static int rdacm20_probe(struct i2c_client *client) dev->dev = &client->dev; dev->serializer.client = client; - ret = of_property_read_u32_array(client->dev.of_node, "reg", - dev->addrs, 2); + ret = device_property_read_u32_array(dev->dev, "reg", dev->addrs, 2); if (ret < 0) { - dev_err(dev->dev, "Invalid DT reg property: %d\n", ret); + dev_err(dev->dev, "Invalid FW reg property: %d\n", ret); return -EINVAL; } diff --git a/drivers/media/i2c/rdacm21.c b/drivers/media/i2c/rdacm21.c index 3e22df36354f..bcab462708c7 100644 --- a/drivers/media/i2c/rdacm21.c +++ b/drivers/media/i2c/rdacm21.c @@ -11,10 +11,10 @@ */ #include <linux/delay.h> -#include <linux/fwnode.h> #include <linux/init.h> #include <linux/i2c.h> #include <linux/module.h> +#include <linux/property.h> #include <linux/slab.h> #include <linux/videodev2.h> @@ -551,10 +551,9 @@ static int rdacm21_probe(struct i2c_client *client) dev->dev = &client->dev; dev->serializer.client = client; - ret = of_property_read_u32_array(client->dev.of_node, "reg", - dev->addrs, 2); + ret = device_property_read_u32_array(dev->dev, "reg", dev->addrs, 2); if (ret < 0) { - dev_err(dev->dev, "Invalid DT reg property: %d\n", ret); + dev_err(dev->dev, "Invalid FW reg property: %d\n", ret); return -EINVAL; } diff --git a/drivers/media/i2c/rj54n1cb0c.c b/drivers/media/i2c/rj54n1cb0c.c index b7ca39f63dba..e95342d706c3 100644 --- a/drivers/media/i2c/rj54n1cb0c.c +++ b/drivers/media/i2c/rj54n1cb0c.c @@ -1329,10 +1329,13 @@ static int rj54n1_probe(struct i2c_client *client) V4L2_CID_GAIN, 0, 127, 1, 66); v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops, V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); - rj54n1->subdev.ctrl_handler = &rj54n1->hdl; - if (rj54n1->hdl.error) - return rj54n1->hdl.error; + if (rj54n1->hdl.error) { + ret = rj54n1->hdl.error; + goto err_free_ctrl; + } + + rj54n1->subdev.ctrl_handler = &rj54n1->hdl; rj54n1->clk_div = clk_div; rj54n1->rect.left = RJ54N1_COLUMN_SKIP; rj54n1->rect.top = RJ54N1_ROW_SKIP; @@ -1354,8 +1357,8 @@ static int rj54n1_probe(struct i2c_client *client) rj54n1->pwup_gpio = gpiod_get_optional(&client->dev, "powerup", GPIOD_OUT_LOW); if (IS_ERR(rj54n1->pwup_gpio)) { - dev_info(&client->dev, "Unable to get GPIO \"powerup\": %ld\n", - PTR_ERR(rj54n1->pwup_gpio)); + dev_info(&client->dev, "Unable to get GPIO \"powerup\": %pe\n", + rj54n1->pwup_gpio); ret = PTR_ERR(rj54n1->pwup_gpio); goto err_clk_put; } @@ -1363,8 +1366,8 @@ static int rj54n1_probe(struct i2c_client *client) rj54n1->enable_gpio = gpiod_get_optional(&client->dev, "enable", GPIOD_OUT_LOW); if (IS_ERR(rj54n1->enable_gpio)) { - dev_info(&client->dev, "Unable to get GPIO \"enable\": %ld\n", - PTR_ERR(rj54n1->enable_gpio)); + dev_info(&client->dev, "Unable to get GPIO \"enable\": %pe\n", + rj54n1->enable_gpio); ret = PTR_ERR(rj54n1->enable_gpio); goto err_gpio_put; } diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c index 7716dfe2b8c9..ab31ee2b596b 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c @@ -1368,10 +1368,6 @@ static int __s5c73m3_power_on(struct s5c73m3 *state) goto err_reg_dis; } - ret = clk_set_rate(state->clock, state->mclk_frequency); - if (ret < 0) - goto err_reg_dis; - ret = clk_prepare_enable(state->clock); if (ret < 0) goto err_reg_dis; @@ -1556,16 +1552,13 @@ static int s5c73m3_get_dt_data(struct s5c73m3 *state) if (!node) return -EINVAL; - state->clock = devm_clk_get(dev, S5C73M3_CLK_NAME); + state->clock = devm_v4l2_sensor_clk_get_legacy(dev, S5C73M3_CLK_NAME, + false, + S5C73M3_DEFAULT_MCLK_FREQ); if (IS_ERR(state->clock)) - return PTR_ERR(state->clock); - - if (of_property_read_u32(node, "clock-frequency", - &state->mclk_frequency)) { - state->mclk_frequency = S5C73M3_DEFAULT_MCLK_FREQ; - dev_info(dev, "using default %u Hz clock frequency\n", - state->mclk_frequency); - } + return dev_err_probe(dev, PTR_ERR(state->clock), + "Failed to get the clock %s\n", + S5C73M3_CLK_NAME); /* Request GPIO lines asserted */ state->stby = devm_gpiod_get(dev, "standby", GPIOD_OUT_HIGH); diff --git a/drivers/media/i2c/s5c73m3/s5c73m3.h b/drivers/media/i2c/s5c73m3/s5c73m3.h index 627e80cf5b72..68a19c2c8db8 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3.h +++ b/drivers/media/i2c/s5c73m3/s5c73m3.h @@ -382,8 +382,6 @@ struct s5c73m3 { struct clk *clock; - /* External master clock frequency */ - u32 mclk_frequency; /* Video bus type - MIPI-CSI2/parallel */ enum v4l2_mbus_type bus_type; diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c index 24f399cd2124..d1d00eca8708 100644 --- a/drivers/media/i2c/s5k5baf.c +++ b/drivers/media/i2c/s5k5baf.c @@ -284,7 +284,6 @@ struct s5k5baf { struct regulator_bulk_data supplies[S5K5BAF_NUM_SUPPLIES]; struct clk *clock; - u32 mclk_frequency; struct s5k5baf_fw *fw; @@ -576,7 +575,7 @@ static void s5k5baf_hw_patch(struct s5k5baf *state) static void s5k5baf_hw_set_clocks(struct s5k5baf *state) { - unsigned long mclk = state->mclk_frequency / 1000; + unsigned long mclk = clk_get_rate(state->clock) / 1000; u16 status; static const u16 nseq_clk_cfg[] = { NSEQ(REG_I_USE_NPVI_CLOCKS, @@ -946,10 +945,6 @@ static int s5k5baf_power_on(struct s5k5baf *state) if (ret < 0) goto err; - ret = clk_set_rate(state->clock, state->mclk_frequency); - if (ret < 0) - goto err_reg_dis; - ret = clk_prepare_enable(state->clock); if (ret < 0) goto err_reg_dis; @@ -1841,14 +1836,6 @@ static int s5k5baf_parse_device_node(struct s5k5baf *state, struct device *dev) return -EINVAL; } - ret = of_property_read_u32(node, "clock-frequency", - &state->mclk_frequency); - if (ret < 0) { - state->mclk_frequency = S5K5BAF_DEFAULT_MCLK_FREQ; - dev_info(dev, "using default %u Hz clock frequency\n", - state->mclk_frequency); - } - node_ep = of_graph_get_endpoint_by_regs(node, 0, -1); if (!node_ep) { dev_err(dev, "no endpoint defined at node %pOF\n", node); @@ -1967,9 +1954,11 @@ static int s5k5baf_probe(struct i2c_client *c) if (ret < 0) goto err_me; - state->clock = devm_clk_get(state->sd.dev, S5K5BAF_CLK_NAME); + state->clock = devm_v4l2_sensor_clk_get_legacy(state->sd.dev, + S5K5BAF_CLK_NAME, false, + S5K5BAF_DEFAULT_MCLK_FREQ); if (IS_ERR(state->clock)) { - ret = -EPROBE_DEFER; + ret = PTR_ERR(state->clock); goto err_me; } diff --git a/drivers/media/i2c/s5k6a3.c b/drivers/media/i2c/s5k6a3.c index 0c2674115b7b..ba6477e88da3 100644 --- a/drivers/media/i2c/s5k6a3.c +++ b/drivers/media/i2c/s5k6a3.c @@ -51,7 +51,6 @@ enum { * @lock: mutex protecting the structure's members below * @format: media bus format at the sensor's source pad * @clock: pointer to &struct clk. - * @clock_frequency: clock frequency * @power_count: stores state if device is powered */ struct s5k6a3 { @@ -63,7 +62,6 @@ struct s5k6a3 { struct mutex lock; struct v4l2_mbus_framefmt format; struct clk *clock; - u32 clock_frequency; int power_count; }; @@ -192,10 +190,6 @@ static int __s5k6a3_power_on(struct s5k6a3 *sensor) int i = S5K6A3_SUPP_VDDA; int ret; - ret = clk_set_rate(sensor->clock, sensor->clock_frequency); - if (ret < 0) - return ret; - ret = pm_runtime_get(sensor->dev); if (ret < 0) goto error_rpm_put; @@ -292,22 +286,18 @@ static int s5k6a3_probe(struct i2c_client *client) mutex_init(&sensor->lock); sensor->dev = dev; - sensor->clock = devm_clk_get(sensor->dev, S5K6A3_CLK_NAME); + sensor->clock = devm_v4l2_sensor_clk_get_legacy(sensor->dev, + S5K6A3_CLK_NAME, false, + S5K6A3_DEFAULT_CLK_FREQ); if (IS_ERR(sensor->clock)) - return PTR_ERR(sensor->clock); + return dev_err_probe(sensor->dev, PTR_ERR(sensor->clock), + "failed to get extclk\n"); sensor->gpio_reset = devm_gpiod_get(dev, NULL, GPIOD_OUT_HIGH); ret = PTR_ERR_OR_ZERO(sensor->gpio_reset); if (ret) return ret; - if (of_property_read_u32(dev->of_node, "clock-frequency", - &sensor->clock_frequency)) { - sensor->clock_frequency = S5K6A3_DEFAULT_CLK_FREQ; - dev_info(dev, "using default %u Hz clock frequency\n", - sensor->clock_frequency); - } - for (i = 0; i < S5K6A3_NUM_SUPPLIES; i++) sensor->supplies[i].supply = s5k6a3_supply_names[i]; diff --git a/drivers/media/i2c/saa6752hs.c b/drivers/media/i2c/saa6752hs.c index 1ed8b5edb3fb..1c0031ba43b4 100644 --- a/drivers/media/i2c/saa6752hs.c +++ b/drivers/media/i2c/saa6752hs.c @@ -6,7 +6,7 @@ AC-3 support: - Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl> + Copyright (C) 2008 Hans Verkuil <hverkuil@kernel.org> */ diff --git a/drivers/media/i2c/saa7115.c b/drivers/media/i2c/saa7115.c index a1c71187e773..48d6730d9271 100644 --- a/drivers/media/i2c/saa7115.c +++ b/drivers/media/i2c/saa7115.c @@ -18,13 +18,14 @@ // Added saa7115 support by Kevin Thayer <nufan_wfk at yahoo.com> // (2/17/2003) // -// VBI support (2004) and cleanups (2005) by Hans Verkuil <hverkuil@xs4all.nl> +// VBI support (2004) and cleanups (2005) by Hans Verkuil <hverkuil@kernel.org> // // Copyright (c) 2005-2006 Mauro Carvalho Chehab <mchehab@kernel.org> // SAA7111, SAA7113 and SAA7118 support #include "saa711x_regs.h" +#include <linux/bitops.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> @@ -664,15 +665,6 @@ static const unsigned char saa7115_init_misc[] = { 0x00, 0x00 }; -static int saa711x_odd_parity(u8 c) -{ - c ^= (c >> 4); - c ^= (c >> 2); - c ^= (c >> 1); - - return c & 1; -} - static int saa711x_decode_vps(u8 *dst, u8 *p) { static const u8 biphase_tbl[] = { @@ -1227,7 +1219,7 @@ static int saa711x_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vb vbi->type = V4L2_SLICED_TELETEXT_B; break; case 4: - if (!saa711x_odd_parity(p[0]) || !saa711x_odd_parity(p[1])) + if (!parity8(p[0]) || !parity8(p[1])) return 0; vbi->type = V4L2_SLICED_CAPTION_525; break; diff --git a/drivers/media/i2c/saa7127.c b/drivers/media/i2c/saa7127.c index 818ed19cf37b..a42a7ffe3768 100644 --- a/drivers/media/i2c/saa7127.c +++ b/drivers/media/i2c/saa7127.c @@ -25,7 +25,7 @@ * Copyright (C) 2004 Chris Kennedy <c@groovy.org> * * VBI additions & cleanup: - * Copyright (C) 2004, 2005 Hans Verkuil <hverkuil@xs4all.nl> + * Copyright (C) 2004, 2005 Hans Verkuil <hverkuil@kernel.org> * * Note: the saa7126 is identical to the saa7127, and the saa7128 is * identical to the saa7129, except that the saa7126 and saa7128 have diff --git a/drivers/media/i2c/saa717x.c b/drivers/media/i2c/saa717x.c index b0793bb0c02a..713331be947c 100644 --- a/drivers/media/i2c/saa717x.c +++ b/drivers/media/i2c/saa717x.c @@ -10,7 +10,7 @@ * Changes by T.Adachi (tadachi@tadachi-net.com) * - support audio, video scaler etc, and checked the initialize sequence. * - * Cleaned up by Hans Verkuil <hverkuil@xs4all.nl> + * Cleaned up by Hans Verkuil <hverkuil@kernel.org> * * Note: this is a reversed engineered driver based on captures from * the I2C bus under Windows. This chip is very similar to the saa7134, diff --git a/drivers/media/i2c/st-mipid02.c b/drivers/media/i2c/st-mipid02.c index f08db3cfe076..4675181af5fb 100644 --- a/drivers/media/i2c/st-mipid02.c +++ b/drivers/media/i2c/st-mipid02.c @@ -301,8 +301,9 @@ static int mipid02_detect(struct mipid02_dev *bridge) static int mipid02_configure_from_rx_speed(struct mipid02_dev *bridge, struct v4l2_mbus_framefmt *fmt) { + struct media_pad *remote = + &bridge->s_subdev->entity.pads[bridge->s_subdev_pad_id]; struct i2c_client *client = bridge->i2c_client; - struct v4l2_subdev *subdev = bridge->s_subdev; struct v4l2_fwnode_endpoint *ep = &bridge->rx; u32 bpp = bpp_from_code(fmt->code); /* @@ -312,7 +313,7 @@ static int mipid02_configure_from_rx_speed(struct mipid02_dev *bridge, u64 ui_4 = 2000000000; s64 link_freq; - link_freq = v4l2_get_link_freq(subdev->ctrl_handler, bpp, + link_freq = v4l2_get_link_freq(remote, bpp, 2 * ep->bus.mipi_csi2.num_data_lanes); if (link_freq < 0) { dev_err(&client->dev, "Failed to get link frequency"); @@ -464,7 +465,6 @@ static int mipid02_disable_streams(struct v4l2_subdev *sd, if (ret) goto error; - pm_runtime_mark_last_busy(&client->dev); pm_runtime_put_autosuspend(&client->dev); error: @@ -541,7 +541,6 @@ error: cci_write(bridge->regmap, MIPID02_DATA_LANE0_REG1, 0, &ret); cci_write(bridge->regmap, MIPID02_DATA_LANE1_REG1, 0, &ret); - pm_runtime_mark_last_busy(&client->dev); pm_runtime_put_autosuspend(&client->dev); return ret; } @@ -748,8 +747,8 @@ static int mipid02_parse_rx_ep(struct mipid02_dev *bridge) of_node_put(ep_node); if (IS_ERR(asd)) { - dev_err(&client->dev, "fail to register asd to notifier %ld", - PTR_ERR(asd)); + dev_err(&client->dev, "fail to register asd to notifier %pe", + asd); return PTR_ERR(asd); } bridge->notifier.ops = &mipid02_notifier_ops; diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index c50d4e85dfd1..a0ca19359c43 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -38,7 +38,21 @@ static int debug; module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "debug level (0-3)"); +MODULE_PARM_DESC(debug, " debug level (0-3)"); + +static int packet_type = 0x87; +module_param(packet_type, int, 0644); +MODULE_PARM_DESC(packet_type, + " Programmable Packet Type. Possible values:\n" + "\t\t 0x87: DRM InfoFrame (Default).\n" + "\t\t 0x01: Audio Clock Regeneration Packet\n" + "\t\t 0x02: Audio Sample Packet\n" + "\t\t 0x03: General Control Packet\n" + "\t\t 0x04: ACP Packet\n" + "\t\t 0x07: One Bit Audio Sample Packet\n" + "\t\t 0x08: DST Audio Packet\n" + "\t\t 0x09: High Bitrate Audio Stream Packet\n" + "\t\t 0x0a: Gamut Metadata Packet\n"); MODULE_DESCRIPTION("Toshiba TC358743 HDMI to CSI-2 bridge driver"); MODULE_AUTHOR("Ramakrishnan Muthukrishnan <ram@rkrishnan.org>"); @@ -114,7 +128,7 @@ static inline struct tc358743_state *to_state(struct v4l2_subdev *sd) /* --------------- I2C --------------- */ -static void i2c_rd(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n) +static int i2c_rd(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n) { struct tc358743_state *state = to_state(sd); struct i2c_client *client = state->i2c_client; @@ -140,6 +154,7 @@ static void i2c_rd(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n) v4l2_err(sd, "%s: reading register 0x%x from 0x%x failed: %d\n", __func__, reg, client->addr, err); } + return err != ARRAY_SIZE(msgs); } static void i2c_wr(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n) @@ -196,15 +211,24 @@ static void i2c_wr(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n) } } -static noinline u32 i2c_rdreg(struct v4l2_subdev *sd, u16 reg, u32 n) +static noinline u32 i2c_rdreg_err(struct v4l2_subdev *sd, u16 reg, u32 n, + int *err) { + int error; __le32 val = 0; - i2c_rd(sd, reg, (u8 __force *)&val, n); + error = i2c_rd(sd, reg, (u8 __force *)&val, n); + if (err) + *err = error; return le32_to_cpu(val); } +static inline u32 i2c_rdreg(struct v4l2_subdev *sd, u16 reg, u32 n) +{ + return i2c_rdreg_err(sd, reg, n, NULL); +} + static noinline void i2c_wrreg(struct v4l2_subdev *sd, u16 reg, u32 val, u32 n) { __le32 raw = cpu_to_le32(val); @@ -233,6 +257,13 @@ static u16 i2c_rd16(struct v4l2_subdev *sd, u16 reg) return i2c_rdreg(sd, reg, 2); } +static int i2c_rd16_err(struct v4l2_subdev *sd, u16 reg, u16 *value) +{ + int err; + *value = i2c_rdreg_err(sd, reg, 2, &err); + return err; +} + static void i2c_wr16(struct v4l2_subdev *sd, u16 reg, u16 val) { i2c_wrreg(sd, reg, val, 2); @@ -313,6 +344,10 @@ static int tc358743_get_detected_timings(struct v4l2_subdev *sd, memset(timings, 0, sizeof(struct v4l2_dv_timings)); + /* if HPD is low, ignore any video */ + if (!(i2c_rd8(sd, HPD_CTL) & MASK_HPD_OUT0)) + return -ENOLINK; + if (no_signal(sd)) { v4l2_dbg(1, debug, sd, "%s: no valid signal\n", __func__); return -ENOLINK; @@ -416,9 +451,9 @@ static void tc358743_enable_edid(struct v4l2_subdev *sd) v4l2_dbg(2, debug, sd, "%s:\n", __func__); - /* Enable hotplug after 100 ms. DDC access to EDID is also enabled when + /* Enable hotplug after 143 ms. DDC access to EDID is also enabled when * hotplug is enabled. See register DDC_CTL */ - schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 10); + schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 7); tc358743_enable_interrupts(sd, true); tc358743_s_ctrl_detect_tx_5v(sd); @@ -445,10 +480,29 @@ tc358743_debugfs_if_read(u32 type, void *priv, struct file *filp, if (!is_hdmi(sd)) return 0; - if (type != V4L2_DEBUGFS_IF_AVI) + switch (type) { + case V4L2_DEBUGFS_IF_AVI: + i2c_rd(sd, PK_AVI_0HEAD, buf, PK_AVI_LEN); + break; + case V4L2_DEBUGFS_IF_AUDIO: + i2c_rd(sd, PK_AUD_0HEAD, buf, PK_AUD_LEN); + break; + case V4L2_DEBUGFS_IF_SPD: + i2c_rd(sd, PK_SPD_0HEAD, buf, PK_SPD_LEN); + break; + case V4L2_DEBUGFS_IF_HDMI: + i2c_rd(sd, PK_VS_0HEAD, buf, PK_VS_LEN); + break; + case V4L2_DEBUGFS_IF_DRM: + i2c_rd(sd, PK_ACP_0HEAD, buf, PK_ACP_LEN); + break; + default: return 0; + } + + if (!buf[2]) + return -ENOENT; - i2c_rd(sd, PK_AVI_0HEAD, buf, PK_AVI_16BYTE - PK_AVI_0HEAD + 1); len = buf[2] + 4; if (len > V4L2_DEBUGFS_IF_MAX_LEN) len = -ENOENT; @@ -457,26 +511,69 @@ tc358743_debugfs_if_read(u32 type, void *priv, struct file *filp, return len < 0 ? 0 : len; } -static void print_avi_infoframe(struct v4l2_subdev *sd) +static void print_infoframes(struct v4l2_subdev *sd) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct device *dev = &client->dev; union hdmi_infoframe frame; - u8 buffer[HDMI_INFOFRAME_SIZE(AVI)] = {}; + u8 buffer[V4L2_DEBUGFS_IF_MAX_LEN] = {}; + + /* + * Updating the ACP TYPE here allows for dynamically + * changing the type you want to monitor, without having + * to reload the driver with a new packet_type module option value. + * + * Instead you can set it with the new value, then call + * VIDIOC_LOG_STATUS. + */ + i2c_wr8(sd, TYP_ACP_SET, packet_type); if (!is_hdmi(sd)) { - v4l2_info(sd, "DVI-D signal - AVI infoframe not supported\n"); + v4l2_info(sd, "DVI-D signal - InfoFrames not supported\n"); return; } - i2c_rd(sd, PK_AVI_0HEAD, buffer, HDMI_INFOFRAME_SIZE(AVI)); + i2c_rd(sd, PK_AVI_0HEAD, buffer, PK_AVI_LEN); + if (hdmi_infoframe_unpack(&frame, buffer, sizeof(buffer)) >= 0) + hdmi_infoframe_log(KERN_INFO, dev, &frame); - if (hdmi_infoframe_unpack(&frame, buffer, sizeof(buffer)) < 0) { - v4l2_err(sd, "%s: unpack of AVI infoframe failed\n", __func__); - return; + i2c_rd(sd, PK_VS_0HEAD, buffer, PK_VS_LEN); + if (hdmi_infoframe_unpack(&frame, buffer, sizeof(buffer)) >= 0) + hdmi_infoframe_log(KERN_INFO, dev, &frame); + + i2c_rd(sd, PK_AUD_0HEAD, buffer, PK_AUD_LEN); + if (hdmi_infoframe_unpack(&frame, buffer, sizeof(buffer)) >= 0) + hdmi_infoframe_log(KERN_INFO, dev, &frame); + + i2c_rd(sd, PK_SPD_0HEAD, buffer, PK_SPD_LEN); + if (hdmi_infoframe_unpack(&frame, buffer, sizeof(buffer)) >= 0) + hdmi_infoframe_log(KERN_INFO, dev, &frame); + + i2c_rd(sd, PK_ACP_0HEAD, buffer, PK_ACP_LEN); + if (buffer[0] == packet_type) { + if (packet_type < 0x80) + v4l2_info(sd, "Packet: %*ph\n", PK_ACP_LEN, buffer); + else if (packet_type != 0x87) + v4l2_info(sd, "InfoFrame: %*ph\n", PK_ACP_LEN, buffer); + else if (hdmi_infoframe_unpack(&frame, buffer, + sizeof(buffer)) >= 0) + hdmi_infoframe_log(KERN_INFO, dev, &frame); } - hdmi_infoframe_log(KERN_INFO, dev, &frame); + i2c_rd(sd, PK_MS_0HEAD, buffer, PK_MS_LEN); + if (buffer[2] && buffer[2] + 3 <= PK_MS_LEN) + v4l2_info(sd, "MPEG Source InfoFrame: %*ph\n", + buffer[2] + 3, buffer); + + i2c_rd(sd, PK_ISRC1_0HEAD, buffer, PK_ISRC1_LEN); + if (buffer[0] == 0x05) + v4l2_info(sd, "ISRC1 Packet: %*ph\n", + PK_ISRC1_LEN, buffer); + + i2c_rd(sd, PK_ISRC2_0HEAD, buffer, PK_ISRC2_LEN); + if (buffer[0] == 0x06) + v4l2_info(sd, "ISRC2 Packet: %*ph\n", + PK_ISRC2_LEN, buffer); } /* --------------- CTRLS --------------- */ @@ -1354,7 +1451,7 @@ static int tc358743_log_status(struct v4l2_subdev *sd) v4l2_info(sd, "Deep color mode: %d-bits per channel\n", deep_color_mode[(i2c_rd8(sd, VI_STATUS1) & MASK_S_DEEPCOLOR) >> 2]); - print_avi_infoframe(sd); + print_infoframes(sd); return 0; } @@ -1501,7 +1598,7 @@ static irqreturn_t tc358743_irq_handler(int irq, void *dev_id) static void tc358743_irq_poll_timer(struct timer_list *t) { - struct tc358743_state *state = from_timer(state, t, timer); + struct tc358743_state *state = timer_container_of(state, t, timer); unsigned int msecs; schedule_work(&state->work_i2c_poll); @@ -1687,12 +1784,23 @@ static int tc358743_enum_mbus_code(struct v4l2_subdev *sd, return 0; } +static u32 tc358743_g_colorspace(u32 code) +{ + switch (code) { + case MEDIA_BUS_FMT_RGB888_1X24: + return V4L2_COLORSPACE_SRGB; + case MEDIA_BUS_FMT_UYVY8_1X16: + return V4L2_COLORSPACE_SMPTE170M; + default: + return 0; + } +} + static int tc358743_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct tc358743_state *state = to_state(sd); - u8 vi_rep = i2c_rd8(sd, VI_REP); if (format->pad != 0) return -EINVAL; @@ -1702,23 +1810,7 @@ static int tc358743_get_fmt(struct v4l2_subdev *sd, format->format.height = state->timings.bt.height; format->format.field = V4L2_FIELD_NONE; - switch (vi_rep & MASK_VOUT_COLOR_SEL) { - case MASK_VOUT_COLOR_RGB_FULL: - case MASK_VOUT_COLOR_RGB_LIMITED: - format->format.colorspace = V4L2_COLORSPACE_SRGB; - break; - case MASK_VOUT_COLOR_601_YCBCR_LIMITED: - case MASK_VOUT_COLOR_601_YCBCR_FULL: - format->format.colorspace = V4L2_COLORSPACE_SMPTE170M; - break; - case MASK_VOUT_COLOR_709_YCBCR_FULL: - case MASK_VOUT_COLOR_709_YCBCR_LIMITED: - format->format.colorspace = V4L2_COLORSPACE_REC709; - break; - default: - format->format.colorspace = 0; - break; - } + format->format.colorspace = tc358743_g_colorspace(format->format.code); return 0; } @@ -1732,19 +1824,14 @@ static int tc358743_set_fmt(struct v4l2_subdev *sd, u32 code = format->format.code; /* is overwritten by get_fmt */ int ret = tc358743_get_fmt(sd, sd_state, format); - format->format.code = code; + if (code == MEDIA_BUS_FMT_RGB888_1X24 || + code == MEDIA_BUS_FMT_UYVY8_1X16) + format->format.code = code; + format->format.colorspace = tc358743_g_colorspace(format->format.code); if (ret) return ret; - switch (code) { - case MEDIA_BUS_FMT_RGB888_1X24: - case MEDIA_BUS_FMT_UYVY8_1X16: - break; - default: - return -EINVAL; - } - if (format->which == V4L2_SUBDEV_FORMAT_TRY) return 0; @@ -1968,8 +2055,19 @@ static int tc358743_probe_of(struct tc358743_state *state) state->pdata.refclk_hz = clk_get_rate(refclk); state->pdata.ddc5v_delay = DDC5V_DELAY_100_MS; state->pdata.enable_hdcp = false; - /* A FIFO level of 16 should be enough for 2-lane 720p60 at 594 MHz. */ - state->pdata.fifo_level = 16; + /* + * Ideally the FIFO trigger level should be set based on the input and + * output data rates, but the calculations required are buried in + * Toshiba's register settings spreadsheet. + * A value of 16 works with a 594Mbps data rate for 720p60 (using 2 + * lanes) and 1080p60 (using 4 lanes), but fails when the data rate + * is increased, or a lower pixel clock is used that result in CSI + * reading out faster than the data is arriving. + * + * A value of 374 works with both those modes at 594Mbps, and with most + * modes on 972Mbps. + */ + state->pdata.fifo_level = 374; /* * The PLL input clock is obtained by dividing refclk by pll_prd. * It must be between 6 MHz and 40 MHz, lower frequency is better. @@ -1989,6 +2087,7 @@ static int tc358743_probe_of(struct tc358743_state *state) /* * The CSI bps per lane must be between 62.5 Mbps and 1 Gbps. * The default is 594 Mbps for 4-lane 1080p60 or 2-lane 720p60. + * 972 Mbps allows 1080P50 UYVY over 2-lane. */ bps_pr_lane = 2 * endpoint.link_frequencies[0]; if (bps_pr_lane < 62500000U || bps_pr_lane > 1000000000U) { @@ -2002,23 +2101,42 @@ static int tc358743_probe_of(struct tc358743_state *state) state->pdata.refclk_hz * state->pdata.pll_prd; /* - * FIXME: These timings are from REF_02 for 594 Mbps per lane (297 MHz - * link frequency). In principle it should be possible to calculate + * FIXME: These timings are from REF_02 for 594 or 972 Mbps per lane + * (297 MHz or 486 MHz link frequency). + * In principle it should be possible to calculate * them based on link frequency and resolution. */ - if (bps_pr_lane != 594000000U) + switch (bps_pr_lane) { + default: dev_warn(dev, "untested bps per lane: %u bps\n", bps_pr_lane); - state->pdata.lineinitcnt = 0xe80; - state->pdata.lptxtimecnt = 0x003; - /* tclk-preparecnt: 3, tclk-zerocnt: 20 */ - state->pdata.tclk_headercnt = 0x1403; - state->pdata.tclk_trailcnt = 0x00; - /* ths-preparecnt: 3, ths-zerocnt: 1 */ - state->pdata.ths_headercnt = 0x0103; - state->pdata.twakeup = 0x4882; - state->pdata.tclk_postcnt = 0x008; - state->pdata.ths_trailcnt = 0x2; - state->pdata.hstxvregcnt = 0; + fallthrough; + case 594000000U: + state->pdata.lineinitcnt = 0xe80; + state->pdata.lptxtimecnt = 0x003; + /* tclk-preparecnt: 3, tclk-zerocnt: 20 */ + state->pdata.tclk_headercnt = 0x1403; + state->pdata.tclk_trailcnt = 0x00; + /* ths-preparecnt: 3, ths-zerocnt: 1 */ + state->pdata.ths_headercnt = 0x0103; + state->pdata.twakeup = 0x4882; + state->pdata.tclk_postcnt = 0x008; + state->pdata.ths_trailcnt = 0x2; + state->pdata.hstxvregcnt = 0; + break; + case 972000000U: + state->pdata.lineinitcnt = 0x1b58; + state->pdata.lptxtimecnt = 0x007; + /* tclk-preparecnt: 6, tclk-zerocnt: 40 */ + state->pdata.tclk_headercnt = 0x2806; + state->pdata.tclk_trailcnt = 0x00; + /* ths-preparecnt: 6, ths-zerocnt: 8 */ + state->pdata.ths_headercnt = 0x0806; + state->pdata.twakeup = 0x4268; + state->pdata.tclk_postcnt = 0x008; + state->pdata.ths_trailcnt = 0x5; + state->pdata.hstxvregcnt = 0; + break; + } state->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); @@ -2057,6 +2175,7 @@ static int tc358743_probe(struct i2c_client *client) struct tc358743_platform_data *pdata = client->dev.platform_data; struct v4l2_subdev *sd; u16 irq_mask = MASK_HDMI_MSK | MASK_CSI_MSK; + u16 chipid; int err; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) @@ -2088,7 +2207,8 @@ static int tc358743_probe(struct i2c_client *client) sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; /* i2c access */ - if ((i2c_rd16(sd, CHIPID) & MASK_CHIPID) != 0) { + if (i2c_rd16_err(sd, CHIPID, &chipid) || + (chipid & MASK_CHIPID) != 0) { v4l2_info(sd, "not a TC358743 on address 0x%x\n", client->addr << 1); return -ENODEV; @@ -2188,10 +2308,15 @@ static int tc358743_probe(struct i2c_client *client) if (err < 0) goto err_work_queues; + i2c_wr8(sd, TYP_ACP_SET, packet_type); + i2c_wr8(sd, PK_AUTO_CLR, 0xff); + i2c_wr8(sd, NO_PKT_CLR, MASK_NO_ACP_CLR); + state->debugfs_dir = debugfs_create_dir(sd->name, v4l2_debugfs_root()); state->infoframes = v4l2_debugfs_if_alloc(state->debugfs_dir, - V4L2_DEBUGFS_IF_AVI, sd, - tc358743_debugfs_if_read); + V4L2_DEBUGFS_IF_AVI | V4L2_DEBUGFS_IF_AUDIO | + V4L2_DEBUGFS_IF_SPD | V4L2_DEBUGFS_IF_HDMI | + V4L2_DEBUGFS_IF_DRM, sd, tc358743_debugfs_if_read); v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name, client->addr << 1, client->adapter->name); @@ -2201,10 +2326,10 @@ static int tc358743_probe(struct i2c_client *client) err_work_queues: cec_unregister_adapter(state->cec_adap); if (!state->i2c_client->irq) { - del_timer(&state->timer); + timer_delete_sync(&state->timer); flush_work(&state->work_i2c_poll); } - cancel_delayed_work(&state->delayed_work_enable_hotplug); + cancel_delayed_work_sync(&state->delayed_work_enable_hotplug); mutex_destroy(&state->confctl_mutex); err_hdl: media_entity_cleanup(&sd->entity); @@ -2218,7 +2343,7 @@ static void tc358743_remove(struct i2c_client *client) struct tc358743_state *state = to_state(sd); if (!state->i2c_client->irq) { - del_timer_sync(&state->timer); + timer_delete_sync(&state->timer); flush_work(&state->work_i2c_poll); } cancel_delayed_work_sync(&state->delayed_work_enable_hotplug); diff --git a/drivers/media/i2c/tc358743_regs.h b/drivers/media/i2c/tc358743_regs.h index 2495878dc358..aae288f8add3 100644 --- a/drivers/media/i2c/tc358743_regs.h +++ b/drivers/media/i2c/tc358743_regs.h @@ -692,6 +692,8 @@ #define MASK_NCO_F0_MOD_42MHZ 0x00 #define MASK_NCO_F0_MOD_27MHZ 0x01 +#define TYP_ACP_SET 0x8706 + #define PK_INT_MODE 0x8709 #define MASK_ISRC2_INT_MODE 0x80 #define MASK_ISRC_INT_MODE 0x40 @@ -702,6 +704,8 @@ #define MASK_AUD_INT_MODE 0x02 #define MASK_AVI_INT_MODE 0x01 +#define PK_AUTO_CLR 0x870a + #define NO_PKT_LIMIT 0x870B #define MASK_NO_ACP_LIMIT 0xf0 #define SET_NO_ACP_LIMIT_MS(milliseconds) ((((milliseconds) / 80) << 4) & \ @@ -720,25 +724,44 @@ #define ERR_PK_LIMIT 0x870D #define NO_PKT_LIMIT2 0x870E #define PK_AVI_0HEAD 0x8710 -#define PK_AVI_1HEAD 0x8711 -#define PK_AVI_2HEAD 0x8712 #define PK_AVI_0BYTE 0x8713 -#define PK_AVI_1BYTE 0x8714 -#define PK_AVI_2BYTE 0x8715 -#define PK_AVI_3BYTE 0x8716 -#define PK_AVI_4BYTE 0x8717 -#define PK_AVI_5BYTE 0x8718 -#define PK_AVI_6BYTE 0x8719 -#define PK_AVI_7BYTE 0x871A -#define PK_AVI_8BYTE 0x871B -#define PK_AVI_9BYTE 0x871C -#define PK_AVI_10BYTE 0x871D -#define PK_AVI_11BYTE 0x871E -#define PK_AVI_12BYTE 0x871F -#define PK_AVI_13BYTE 0x8720 -#define PK_AVI_14BYTE 0x8721 -#define PK_AVI_15BYTE 0x8722 #define PK_AVI_16BYTE 0x8723 +#define PK_AVI_LEN (PK_AVI_16BYTE - PK_AVI_0HEAD + 1) + +#define PK_AUD_0HEAD 0x8730 +#define PK_AUD_0BYTE 0x8733 +#define PK_AUD_10BYTE 0x873d +#define PK_AUD_LEN (PK_AUD_10BYTE - PK_AUD_0HEAD + 1) + +#define PK_MS_0HEAD 0x8740 +#define PK_MS_0BYTE 0x8743 +#define PK_MS_10BYTE 0x874d +#define PK_MS_LEN (PK_MS_10BYTE - PK_MS_0HEAD + 1) + +#define PK_SPD_0HEAD 0x8750 +#define PK_SPD_0BYTE 0x8753 +#define PK_SPD_27BYTE 0x876e +#define PK_SPD_LEN (PK_SPD_27BYTE - PK_SPD_0HEAD + 1) + +#define PK_VS_0HEAD 0x8770 +#define PK_VS_0BYTE 0x8773 +#define PK_VS_27BYTE 0x878e +#define PK_VS_LEN (PK_VS_27BYTE - PK_VS_0HEAD + 1) + +#define PK_ACP_0HEAD 0x8790 +#define PK_ACP_0BYTE 0x8793 +#define PK_ACP_27BYTE 0x87ae +#define PK_ACP_LEN (PK_ACP_27BYTE - PK_ACP_0HEAD + 1) + +#define PK_ISRC1_0HEAD 0x87b0 +#define PK_ISRC1_0BYTE 0x87b3 +#define PK_ISRC1_27BYTE 0x87c2 +#define PK_ISRC1_LEN (PK_ISRC1_27BYTE - PK_ISRC1_0HEAD + 1) + +#define PK_ISRC2_0HEAD 0x87d0 +#define PK_ISRC2_0BYTE 0x87d3 +#define PK_ISRC2_27BYTE 0x87ee +#define PK_ISRC2_LEN (PK_ISRC2_27BYTE - PK_ISRC2_0HEAD + 1) #define BKSV 0x8800 diff --git a/drivers/media/i2c/tc358746.c b/drivers/media/i2c/tc358746.c index 389582420ba7..86d9ba3ea4e5 100644 --- a/drivers/media/i2c/tc358746.c +++ b/drivers/media/i2c/tc358746.c @@ -161,10 +161,6 @@ struct tc358746 { u16 pll_pre_div; u16 pll_mul; -#define TC358746_VB_MAX_SIZE (511 * 32) -#define TC358746_VB_DEFAULT_SIZE (1 * 32) - unsigned int vb_size; /* Video buffer size in bits */ - struct phy_configure_opts_mipi_dphy dphy_cfg; }; @@ -202,6 +198,15 @@ enum { PDFORMAT_YUV444, }; +#define TC358746_FORMAT_RAW(_bpp, _code) \ +{ \ + .code = _code, \ + .bus_width = _bpp, \ + .bpp = _bpp, \ + .pdformat = PDFORMAT_RAW##_bpp, \ + .pdataf = PDATAF_MODE0, /* don't care */ \ +} + /* Check tc358746_src_mbus_code() if you add new formats */ static const struct tc358746_format tc358746_formats[] = { { @@ -230,7 +235,23 @@ static const struct tc358746_format tc358746_formats[] = { .bpp = 20, .pdformat = PDFORMAT_YUV422_10BIT, .pdataf = PDATAF_MODE0, /* don't care */ - } + }, + TC358746_FORMAT_RAW(8, MEDIA_BUS_FMT_SBGGR8_1X8), + TC358746_FORMAT_RAW(8, MEDIA_BUS_FMT_SGBRG8_1X8), + TC358746_FORMAT_RAW(8, MEDIA_BUS_FMT_SGRBG8_1X8), + TC358746_FORMAT_RAW(8, MEDIA_BUS_FMT_SRGGB8_1X8), + TC358746_FORMAT_RAW(10, MEDIA_BUS_FMT_SBGGR10_1X10), + TC358746_FORMAT_RAW(10, MEDIA_BUS_FMT_SGBRG10_1X10), + TC358746_FORMAT_RAW(10, MEDIA_BUS_FMT_SGRBG10_1X10), + TC358746_FORMAT_RAW(10, MEDIA_BUS_FMT_SRGGB10_1X10), + TC358746_FORMAT_RAW(12, MEDIA_BUS_FMT_SBGGR12_1X12), + TC358746_FORMAT_RAW(12, MEDIA_BUS_FMT_SGBRG12_1X12), + TC358746_FORMAT_RAW(12, MEDIA_BUS_FMT_SGRBG12_1X12), + TC358746_FORMAT_RAW(12, MEDIA_BUS_FMT_SRGGB12_1X12), + TC358746_FORMAT_RAW(14, MEDIA_BUS_FMT_SBGGR14_1X14), + TC358746_FORMAT_RAW(14, MEDIA_BUS_FMT_SGBRG14_1X14), + TC358746_FORMAT_RAW(14, MEDIA_BUS_FMT_SGRBG14_1X14), + TC358746_FORMAT_RAW(14, MEDIA_BUS_FMT_SRGGB14_1X14), }; /* Get n-th format for pad */ @@ -415,6 +436,70 @@ tc358746_apply_pll_config(struct tc358746 *tc358746) return tc358746_set_bits(tc358746, PLLCTL1_REG, CKEN); } +#define TC358746_VB_PRECISION 10 +#define TC358746_VB_MAX_SIZE (511 * 32) +#define TC358746_VB_DEFAULT_SIZE (1 * 32) + +static int tc358746_calc_vb_size(struct tc358746 *tc358746, + s64 source_link_freq, + const struct v4l2_mbus_framefmt *mbusfmt, + const struct tc358746_format *fmt) +{ + unsigned long csi_bitrate, source_bitrate; + unsigned int fifo_sz, tmp, n; + int vb_size; /* Video buffer size in bits */ + + source_bitrate = source_link_freq * fmt->bus_width; + + csi_bitrate = tc358746->dphy_cfg.lanes * tc358746->pll_rate; + + dev_dbg(tc358746->sd.dev, + "Fifo settings params: source-bitrate:%lu csi-bitrate:%lu", + source_bitrate, csi_bitrate); + + /* Avoid possible FIFO overflows */ + if (csi_bitrate < source_bitrate) + return -EINVAL; + + /* Best case */ + if (csi_bitrate == source_bitrate) { + fifo_sz = TC358746_VB_DEFAULT_SIZE; + vb_size = TC358746_VB_DEFAULT_SIZE; + } else { + /* + * Avoid possible FIFO underflow in case of + * csi_bitrate > source_bitrate. For such case the chip has a internal + * fifo which can be used to delay the line output. + * + * Fifo size calculation (excluding precision): + * + * fifo-sz, image-width - in bits + * sbr - source_bitrate in bits/s + * csir - csi_bitrate in bits/s + * + * image-width / csir >= (image-width - fifo-sz) / sbr + * image-width * sbr / csir >= image-width - fifo-sz + * fifo-sz >= image-width - image-width * sbr / csir; with n = csir/sbr + * fifo-sz >= image-width - image-width / n + */ + source_bitrate /= TC358746_VB_PRECISION; + n = csi_bitrate / source_bitrate; + tmp = (mbusfmt->width * TC358746_VB_PRECISION) / n; + fifo_sz = mbusfmt->width - tmp; + fifo_sz *= fmt->bpp; + vb_size = round_up(fifo_sz, 32); + } + + dev_dbg(tc358746->sd.dev, + "Found FIFO size[bits]:%u -> aligned to size[bits]:%u\n", + fifo_sz, vb_size); + + if (vb_size > TC358746_VB_MAX_SIZE) + return -EINVAL; + + return vb_size; +} + static int tc358746_apply_misc_config(struct tc358746 *tc358746) { const struct v4l2_mbus_framefmt *mbusfmt; @@ -422,6 +507,9 @@ static int tc358746_apply_misc_config(struct tc358746 *tc358746) struct v4l2_subdev_state *sink_state; const struct tc358746_format *fmt; struct device *dev = sd->dev; + struct media_pad *source_pad; + s64 source_link_freq; + int vb_size; u32 val; int err; @@ -430,6 +518,21 @@ static int tc358746_apply_misc_config(struct tc358746 *tc358746) mbusfmt = v4l2_subdev_state_get_format(sink_state, TC358746_SINK); fmt = tc358746_get_format_by_code(TC358746_SINK, mbusfmt->code); + source_pad = media_entity_remote_source_pad_unique(&sd->entity); + if (IS_ERR(source_pad)) { + dev_err(dev, "Failed to get source pad of %s\n", sd->name); + err = PTR_ERR(source_pad); + goto out; + } + source_link_freq = v4l2_get_link_freq(source_pad, 0, 0); + if (source_link_freq <= 0) { + dev_err(dev, + "Failed to query or invalid source link frequency\n"); + /* Return -EINVAL in case of source_link_freq is 0 */ + err = source_link_freq ?: -EINVAL; + goto out; + } + /* Self defined CSI user data type id's are not supported yet */ val = PDFMT(fmt->pdformat); dev_dbg(dev, "DATAFMT: 0x%x\n", val); @@ -443,7 +546,13 @@ static int tc358746_apply_misc_config(struct tc358746 *tc358746) if (err) goto out; - val = tc358746->vb_size / 32; + vb_size = tc358746_calc_vb_size(tc358746, source_link_freq, mbusfmt, fmt); + if (vb_size < 0) { + err = vb_size; + goto out; + } + + val = vb_size / 32; dev_dbg(dev, "FIFOCTL: %u (0x%x)\n", val, val); err = tc358746_write(tc358746, FIFOCTL_REG, val); if (err) @@ -460,24 +569,20 @@ out: return err; } -/* Use MHz as base so the div needs no u64 */ -static u32 tc358746_cfg_to_cnt(unsigned int cfg_val, - unsigned int clk_mhz, - unsigned int time_base) +static u32 tc358746_cfg_to_cnt(unsigned long cfg_val, unsigned long clk_hz, + unsigned long long time_base) { - return DIV_ROUND_UP(cfg_val * clk_mhz, time_base); + return div64_u64((u64)cfg_val * clk_hz + time_base - 1, time_base); } -static u32 tc358746_ps_to_cnt(unsigned int cfg_val, - unsigned int clk_mhz) +static u32 tc358746_ps_to_cnt(unsigned long cfg_val, unsigned long clk_hz) { - return tc358746_cfg_to_cnt(cfg_val, clk_mhz, USEC_PER_SEC); + return tc358746_cfg_to_cnt(cfg_val, clk_hz, PSEC_PER_SEC); } -static u32 tc358746_us_to_cnt(unsigned int cfg_val, - unsigned int clk_mhz) +static u32 tc358746_us_to_cnt(unsigned long cfg_val, unsigned long clk_hz) { - return tc358746_cfg_to_cnt(cfg_val, clk_mhz, 1); + return tc358746_cfg_to_cnt(cfg_val, clk_hz, USEC_PER_SEC); } static int tc358746_apply_dphy_config(struct tc358746 *tc358746) @@ -492,7 +597,6 @@ static int tc358746_apply_dphy_config(struct tc358746 *tc358746) /* The hs_byte_clk is also called SYSCLK in the excel sheet */ hs_byte_clk = cfg->hs_clk_rate / 8; - hs_byte_clk /= HZ_PER_MHZ; hf_clk = hs_byte_clk / 2; val = tc358746_us_to_cnt(cfg->init, hf_clk) - 1; @@ -712,7 +816,6 @@ static int tc358746_s_stream(struct v4l2_subdev *sd, int enable) return 0; err_out: - pm_runtime_mark_last_busy(sd->dev); pm_runtime_put_sync_autosuspend(sd->dev); return err; @@ -734,7 +837,6 @@ err_out: if (err) return err; - pm_runtime_mark_last_busy(sd->dev); pm_runtime_put_sync_autosuspend(sd->dev); return v4l2_subdev_call(src, video, s_stream, 0); @@ -882,97 +984,6 @@ static unsigned long tc358746_find_pll_settings(struct tc358746 *tc358746, return best_freq; } -#define TC358746_PRECISION 10 - -static int -tc358746_link_validate(struct v4l2_subdev *sd, struct media_link *link, - struct v4l2_subdev_format *source_fmt, - struct v4l2_subdev_format *sink_fmt) -{ - struct tc358746 *tc358746 = to_tc358746(sd); - unsigned long csi_bitrate, source_bitrate; - struct v4l2_subdev_state *sink_state; - struct v4l2_mbus_framefmt *mbusfmt; - const struct tc358746_format *fmt; - unsigned int fifo_sz, tmp, n; - struct v4l2_subdev *source; - s64 source_link_freq; - int err; - - err = v4l2_subdev_link_validate_default(sd, link, source_fmt, sink_fmt); - if (err) - return err; - - sink_state = v4l2_subdev_lock_and_get_active_state(sd); - mbusfmt = v4l2_subdev_state_get_format(sink_state, TC358746_SINK); - - /* Check the FIFO settings */ - fmt = tc358746_get_format_by_code(TC358746_SINK, mbusfmt->code); - - source = media_entity_to_v4l2_subdev(link->source->entity); - source_link_freq = v4l2_get_link_freq(source->ctrl_handler, 0, 0); - if (source_link_freq <= 0) { - dev_err(tc358746->sd.dev, - "Failed to query or invalid source link frequency\n"); - v4l2_subdev_unlock_state(sink_state); - /* Return -EINVAL in case of source_link_freq is 0 */ - return source_link_freq ? : -EINVAL; - } - source_bitrate = source_link_freq * fmt->bus_width; - - csi_bitrate = tc358746->dphy_cfg.lanes * tc358746->pll_rate; - - dev_dbg(tc358746->sd.dev, - "Fifo settings params: source-bitrate:%lu csi-bitrate:%lu", - source_bitrate, csi_bitrate); - - /* Avoid possible FIFO overflows */ - if (csi_bitrate < source_bitrate) { - v4l2_subdev_unlock_state(sink_state); - return -EINVAL; - } - - /* Best case */ - if (csi_bitrate == source_bitrate) { - fifo_sz = TC358746_VB_DEFAULT_SIZE; - tc358746->vb_size = TC358746_VB_DEFAULT_SIZE; - goto out; - } - - /* - * Avoid possible FIFO underflow in case of - * csi_bitrate > source_bitrate. For such case the chip has a internal - * fifo which can be used to delay the line output. - * - * Fifo size calculation (excluding precision): - * - * fifo-sz, image-width - in bits - * sbr - source_bitrate in bits/s - * csir - csi_bitrate in bits/s - * - * image-width / csir >= (image-width - fifo-sz) / sbr - * image-width * sbr / csir >= image-width - fifo-sz - * fifo-sz >= image-width - image-width * sbr / csir; with n = csir/sbr - * fifo-sz >= image-width - image-width / n - */ - - source_bitrate /= TC358746_PRECISION; - n = csi_bitrate / source_bitrate; - tmp = (mbusfmt->width * TC358746_PRECISION) / n; - fifo_sz = mbusfmt->width - tmp; - fifo_sz *= fmt->bpp; - tc358746->vb_size = round_up(fifo_sz, 32); - -out: - dev_dbg(tc358746->sd.dev, - "Found FIFO size[bits]:%u -> aligned to size[bits]:%u\n", - fifo_sz, tc358746->vb_size); - - v4l2_subdev_unlock_state(sink_state); - - return tc358746->vb_size > TC358746_VB_MAX_SIZE ? -EINVAL : 0; -} - static int tc358746_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad, struct v4l2_mbus_config *config) { @@ -1003,7 +1014,6 @@ tc358746_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) err = tc358746_read(tc358746, reg->reg, &val); reg->val = val; - pm_runtime_mark_last_busy(sd->dev); pm_runtime_put_sync_autosuspend(sd->dev); return err; @@ -1019,7 +1029,6 @@ tc358746_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) tc358746_write(tc358746, (u32)reg->reg, (u32)reg->val); - pm_runtime_mark_last_busy(sd->dev); pm_runtime_put_sync_autosuspend(sd->dev); return 0; @@ -1040,7 +1049,7 @@ static const struct v4l2_subdev_pad_ops tc358746_pad_ops = { .enum_mbus_code = tc358746_enum_mbus_code, .set_fmt = tc358746_set_fmt, .get_fmt = v4l2_subdev_get_fmt, - .link_validate = tc358746_link_validate, + .link_validate = v4l2_subdev_link_validate_default, .get_mbus_config = tc358746_get_mbus_config, }; @@ -1213,14 +1222,16 @@ tc358746_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) return tc358746->pll_rate / (prediv * postdiv); } -static long tc358746_mclk_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int tc358746_mclk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct tc358746 *tc358746 = clk_hw_to_tc358746(hw); - *parent_rate = tc358746->pll_rate; + req->best_parent_rate = tc358746->pll_rate; - return tc358746_find_mclk_settings(tc358746, rate); + req->rate = tc358746_find_mclk_settings(tc358746, req->rate); + + return 0; } static int tc358746_mclk_set_rate(struct clk_hw *hw, unsigned long rate, @@ -1237,7 +1248,7 @@ static const struct clk_ops tc358746_mclk_ops = { .enable = tc358746_mclk_enable, .disable = tc358746_mclk_disable, .recalc_rate = tc358746_recalc_rate, - .round_rate = tc358746_mclk_round_rate, + .determine_rate = tc358746_mclk_determine_rate, .set_rate = tc358746_mclk_set_rate, }; @@ -1352,8 +1363,6 @@ tc358746_init_output_port(struct tc358746 *tc358746, unsigned long refclk) if (err) goto err; - tc358746->vb_size = TC358746_VB_DEFAULT_SIZE; - return 0; err: @@ -1384,7 +1393,6 @@ static int tc358746_init_hw(struct tc358746 *tc358746) } err = tc358746_read(tc358746, CHIPID_REG, &val); - pm_runtime_mark_last_busy(dev); pm_runtime_put_sync_autosuspend(dev); if (err) return -ENODEV; diff --git a/drivers/media/i2c/tda1997x.c b/drivers/media/i2c/tda1997x.c index 3b7e5ff5b010..3532766cd795 100644 --- a/drivers/media/i2c/tda1997x.c +++ b/drivers/media/i2c/tda1997x.c @@ -589,8 +589,8 @@ static void tda1997x_enable_edid(struct v4l2_subdev *sd) v4l2_dbg(1, debug, sd, "%s\n", __func__); - /* Enable hotplug after 100ms */ - schedule_delayed_work(&state->delayed_work_enable_hpd, HZ / 10); + /* Enable hotplug after 143ms */ + schedule_delayed_work(&state->delayed_work_enable_hpd, HZ / 7); } /* ----------------------------------------------------------------------------- @@ -2315,11 +2315,10 @@ static int tda1997x_parse_dt(struct tda1997x_state *state) return -EINVAL; ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &bus_cfg); - if (ret) { - of_node_put(ep); - return ret; - } of_node_put(ep); + if (ret) + return ret; + pdata->vidout_bus_type = bus_cfg.bus_type; /* polarity of HS/VS/DE */ @@ -2798,7 +2797,6 @@ err_free_media: err_free_handler: v4l2_ctrl_handler_free(&state->hdl); err_free_mutex: - cancel_delayed_work(&state->delayed_work_enable_hpd); mutex_destroy(&state->page_lock); mutex_destroy(&state->lock); tda1997x_set_power(state, 0); diff --git a/drivers/media/i2c/tda9840.c b/drivers/media/i2c/tda9840.c index d61da811c9da..e3b266db571f 100644 --- a/drivers/media/i2c/tda9840.c +++ b/drivers/media/i2c/tda9840.c @@ -3,7 +3,7 @@ tda9840 - i2c-driver for the tda9840 by SGS Thomson Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de> - Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl> + Copyright (C) 2008 Hans Verkuil <hverkuil@kernel.org> The tda9840 is a stereo/dual sound processor with digital identification. It can be found at address 0x84 on the i2c-bus. diff --git a/drivers/media/i2c/tea6415c.c b/drivers/media/i2c/tea6415c.c index 4aaf66353610..0cd2e6c52e20 100644 --- a/drivers/media/i2c/tea6415c.c +++ b/drivers/media/i2c/tea6415c.c @@ -3,7 +3,7 @@ tea6415c - i2c-driver for the tea6415c by SGS Thomson Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de> - Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl> + Copyright (C) 2008 Hans Verkuil <hverkuil@kernel.org> The tea6415c is a bus controlled video-matrix-switch with 8 inputs and 6 outputs. diff --git a/drivers/media/i2c/tea6420.c b/drivers/media/i2c/tea6420.c index 5c5ea3973251..400883fc0c0f 100644 --- a/drivers/media/i2c/tea6420.c +++ b/drivers/media/i2c/tea6420.c @@ -3,7 +3,7 @@ tea6420 - i2c-driver for the tea6420 by SGS Thomson Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de> - Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl> + Copyright (C) 2008 Hans Verkuil <hverkuil@kernel.org> The tea6420 is a bus controlled audio-matrix with 5 stereo inputs, 4 stereo outputs and gain control for each output. diff --git a/drivers/media/i2c/thp7312.c b/drivers/media/i2c/thp7312.c index 8852c56431fe..775cfba188d8 100644 --- a/drivers/media/i2c/thp7312.c +++ b/drivers/media/i2c/thp7312.c @@ -808,7 +808,6 @@ static int thp7312_s_stream(struct v4l2_subdev *sd, int enable) if (!enable) { thp7312_stream_enable(thp7312, false); - pm_runtime_mark_last_busy(thp7312->dev); pm_runtime_put_autosuspend(thp7312->dev); v4l2_subdev_unlock_state(sd_state); @@ -839,7 +838,6 @@ static int thp7312_s_stream(struct v4l2_subdev *sd, int enable) goto finish_unlock; finish_pm: - pm_runtime_mark_last_busy(thp7312->dev); pm_runtime_put_autosuspend(thp7312->dev); finish_unlock: v4l2_subdev_unlock_state(sd_state); @@ -1147,7 +1145,6 @@ static int thp7312_s_ctrl(struct v4l2_ctrl *ctrl) break; } - pm_runtime_mark_last_busy(thp7312->dev); pm_runtime_put_autosuspend(thp7312->dev); return ret; @@ -2183,7 +2180,6 @@ static int thp7312_probe(struct i2c_client *client) * Decrease the PM usage count. The device will get suspended after the * autosuspend delay, turning the power off. */ - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); dev_info(dev, "THP7312 firmware version %02u.%02u\n", diff --git a/drivers/media/i2c/ths7303.c b/drivers/media/i2c/ths7303.c index b7cedc5b3e8e..ff268ebeb4d9 100644 --- a/drivers/media/i2c/ths7303.c +++ b/drivers/media/i2c/ths7303.c @@ -7,7 +7,7 @@ * Author: Chaithrika U S <chaithrika@ti.com> * * Contributors: - * Hans Verkuil <hansverk@cisco.com> + * Hans Verkuil <hverkuil@kernel.org> * Lad, Prabhakar <prabhakar.lad@ti.com> * Martin Bugge <marbugge@cisco.com> * diff --git a/drivers/media/i2c/tlv320aic23b.c b/drivers/media/i2c/tlv320aic23b.c index b7b31b6192af..6f6bc5236565 100644 --- a/drivers/media/i2c/tlv320aic23b.c +++ b/drivers/media/i2c/tlv320aic23b.c @@ -7,7 +7,7 @@ * Based on wm8775 driver * * Copyright (C) 2004 Ulf Eklund <ivtv at eklund.to> - * Copyright (C) 2005 Hans Verkuil <hverkuil@xs4all.nl> + * Copyright (C) 2005 Hans Verkuil <hverkuil@kernel.org> */ #include <linux/module.h> diff --git a/drivers/media/i2c/tvaudio.c b/drivers/media/i2c/tvaudio.c index 654725dfafac..6267e9ad39c0 100644 --- a/drivers/media/i2c/tvaudio.c +++ b/drivers/media/i2c/tvaudio.c @@ -318,7 +318,7 @@ static int chip_cmd(struct CHIPSTATE *chip, char *name, audiocmd *cmd) static void chip_thread_wake(struct timer_list *t) { - struct CHIPSTATE *chip = from_timer(chip, t, wt); + struct CHIPSTATE *chip = timer_container_of(chip, t, wt); wake_up_process(chip->thread); } @@ -1787,7 +1787,7 @@ static int tvaudio_s_radio(struct v4l2_subdev *sd) struct CHIPSTATE *chip = to_state(sd); chip->radio = 1; - /* del_timer(&chip->wt); */ + /* timer_delete(&chip->wt); */ return 0; } @@ -2071,7 +2071,7 @@ static void tvaudio_remove(struct i2c_client *client) struct v4l2_subdev *sd = i2c_get_clientdata(client); struct CHIPSTATE *chip = to_state(sd); - del_timer_sync(&chip->wt); + timer_delete_sync(&chip->wt); if (chip->thread) { /* shutdown async thread */ kthread_stop(chip->thread); diff --git a/drivers/media/i2c/upd64031a.c b/drivers/media/i2c/upd64031a.c index 9d0b72a213be..a178af46e695 100644 --- a/drivers/media/i2c/upd64031a.c +++ b/drivers/media/i2c/upd64031a.c @@ -4,7 +4,7 @@ * * 2003 by T.Adachi <tadachi@tadachi-net.com> * 2003 by Takeru KOMORIYA <komoriya@paken.org> - * 2006 by Hans Verkuil <hverkuil@xs4all.nl> + * 2006 by Hans Verkuil <hverkuil@kernel.org> */ diff --git a/drivers/media/i2c/upd64083.c b/drivers/media/i2c/upd64083.c index 2e99ed5da42c..5421dc5e32c9 100644 --- a/drivers/media/i2c/upd64083.c +++ b/drivers/media/i2c/upd64083.c @@ -4,7 +4,7 @@ * * 2003 by T.Adachi (tadachi@tadachi-net.com) * 2003 by Takeru KOMORIYA <komoriya@paken.org> - * 2006 by Hans Verkuil <hverkuil@xs4all.nl> + * 2006 by Hans Verkuil <hverkuil@kernel.org> */ #include <linux/module.h> diff --git a/drivers/media/i2c/vd55g1.c b/drivers/media/i2c/vd55g1.c new file mode 100644 index 000000000000..78d18c028154 --- /dev/null +++ b/drivers/media/i2c/vd55g1.c @@ -0,0 +1,2061 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for VD55G1 global shutter sensor family driver + * + * Copyright (C) 2025 STMicroelectronics SA + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/unaligned.h> +#include <linux/units.h> + +#include <media/mipi-csi2.h> +#include <media/v4l2-async.h> +#include <media/v4l2-cci.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-event.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-subdev.h> + +/* Register Map */ +#define VD55G1_REG_MODEL_ID CCI_REG32_LE(0x0000) +#define VD55G1_MODEL_ID_VD55G1 0x53354731 /* Mono */ +#define VD55G1_MODEL_ID_VD65G4 0x53354733 /* RGB */ +#define VD55G1_REG_REVISION CCI_REG16_LE(0x0004) +#define VD55G1_REVISION_CCB 0x2020 +#define VD55G1_REVISION_BAYER 0x3030 +#define VD55G1_REG_FWPATCH_REVISION CCI_REG16_LE(0x0012) +#define VD55G1_REG_FWPATCH_START_ADDR CCI_REG8(0x2000) +#define VD55G1_REG_SYSTEM_FSM CCI_REG8(0x001c) +#define VD55G1_SYSTEM_FSM_READY_TO_BOOT 0x01 +#define VD55G1_SYSTEM_FSM_SW_STBY 0x02 +#define VD55G1_SYSTEM_FSM_STREAMING 0x03 +#define VD55G1_REG_BOOT CCI_REG8(0x0200) +#define VD55G1_BOOT_BOOT 1 +#define VD55G1_BOOT_PATCH_AND_BOOT 2 +#define VD55G1_REG_STBY CCI_REG8(0x0201) +#define VD55G1_STBY_START_STREAM 1 +#define VD55G1_REG_STREAMING CCI_REG8(0x0202) +#define VD55G1_STREAMING_STOP_STREAM 1 +#define VD55G1_REG_EXT_CLOCK CCI_REG32_LE(0x0220) +#define VD55G1_REG_LINE_LENGTH CCI_REG16_LE(0x0300) +#define VD55G1_REG_ORIENTATION CCI_REG8(0x0302) +#define VD55G1_REG_FORMAT_CTRL CCI_REG8(0x030a) +#define VD55G1_REG_OIF_CTRL CCI_REG16_LE(0x030c) +#define VD55G1_REG_ISL_ENABLE CCI_REG16_LE(0x326) +#define VD55G1_REG_OIF_IMG_CTRL CCI_REG8(0x030f) +#define VD55G1_REG_MIPI_DATA_RATE CCI_REG32_LE(0x0224) +#define VD55G1_REG_PATGEN_CTRL CCI_REG16_LE(0x0304) +#define VD55G1_PATGEN_TYPE_SHIFT 4 +#define VD55G1_PATGEN_ENABLE BIT(0) +#define VD55G1_REG_MANUAL_ANALOG_GAIN CCI_REG8(0x0501) +#define VD55G1_REG_MANUAL_COARSE_EXPOSURE CCI_REG16_LE(0x0502) +#define VD55G1_REG_MANUAL_DIGITAL_GAIN CCI_REG16_LE(0x0504) +#define VD55G1_REG_APPLIED_COARSE_EXPOSURE CCI_REG16_LE(0x00e8) +#define VD55G1_REG_APPLIED_ANALOG_GAIN CCI_REG16_LE(0x00ea) +#define VD55G1_REG_APPLIED_DIGITAL_GAIN CCI_REG16_LE(0x00ec) +#define VD55G1_REG_AE_FORCE_COLDSTART CCI_REG8(0x0308) +#define VD55G1_REG_AE_COLDSTART_EXP_TIME CCI_REG32_LE(0x0374) +#define VD55G1_REG_READOUT_CTRL CCI_REG8(0x052e) +#define VD55G1_READOUT_CTRL_BIN_MODE_NORMAL 0 +#define VD55G1_READOUT_CTRL_BIN_MODE_DIGITAL_X2 1 +#define VD55G1_REG_DUSTER_CTRL CCI_REG8(0x03ae) +#define VD55G1_DUSTER_ENABLE BIT(0) +#define VD55G1_DUSTER_DISABLE 0 +#define VD55G1_DUSTER_DYN_ENABLE BIT(1) +#define VD55G1_DUSTER_RING_ENABLE BIT(4) +#define VD55G1_REG_AE_TARGET_PERCENTAGE CCI_REG8(0x0486) +#define VD55G1_REG_NEXT_CTX CCI_REG16_LE(0x03e4) +#define VD55G1_REG_EXPOSURE_USE_CASES CCI_REG8(0x0312) +#define VD55G1_EXPOSURE_USE_CASES_MULTI_CONTEXT BIT(2) +#define VD55G1_REG_EXPOSURE_MAX_COARSE CCI_REG16_LE(0x0372) +#define VD55G1_EXPOSURE_MAX_COARSE_DEF 0x7fff +#define VD55G1_EXPOSURE_MAX_COARSE_SUB 446 +#define VD55G1_REG_CTX_REPEAT_COUNT_CTX0 CCI_REG16_LE(0x03dc) +#define VD55G1_REG_CTX_REPEAT_COUNT_CTX1 CCI_REG16_LE(0x03de) + +#define VD55G1_REG_EXP_MODE(ctx) \ + CCI_REG8(0x0500 + VD55G1_CTX_OFFSET * (ctx)) +#define VD55G1_REG_FRAME_LENGTH(ctx) \ + CCI_REG32_LE(0x050c + VD55G1_CTX_OFFSET * (ctx)) +#define VD55G1_REG_X_START(ctx) \ + CCI_REG16_LE(0x0514 + VD55G1_CTX_OFFSET * (ctx)) +#define VD55G1_REG_X_WIDTH(ctx) \ + CCI_REG16_LE(0x0516 + VD55G1_CTX_OFFSET * (ctx)) +#define VD55G1_REG_Y_START(ctx) \ + CCI_REG16_LE(0x0510 + VD55G1_CTX_OFFSET * (ctx)) +#define VD55G1_REG_Y_HEIGHT(ctx) \ + CCI_REG16_LE(0x0512 + VD55G1_CTX_OFFSET * (ctx)) +#define VD55G1_REG_GPIO_0_CTRL(ctx) \ + CCI_REG8(0x051d + VD55G1_CTX_OFFSET * (ctx)) +#define VD55G1_GPIO_MODE_FSYNC_OUT 0x00 +#define VD55G1_GPIO_MODE_IN 0x01 +#define VD55G1_GPIO_MODE_STROBE 0x02 +#define VD55G1_REG_VT_MODE(ctx) \ + CCI_REG8(0x0536 + VD55G1_CTX_OFFSET * (ctx)) +#define VD55G1_VT_MODE_NORMAL 0 +#define VD55G1_VT_MODE_SUBTRACTION 1 +#define VD55G1_REG_MASK_FRAME_CTRL(ctx) \ + CCI_REG8(0x0537 + VD55G1_CTX_OFFSET * (ctx)) +#define VD55G1_MASK_FRAME_CTRL_OUTPUT 0 +#define VD55G1_MASK_FRAME_CTRL_MASK 1 +#define VD55G1_REG_EXPOSURE_INSTANCE(ctx) \ + CCI_REG32_LE(0x52D + VD55G1_CTX_OFFSET * (ctx)) + +#define VD55G1_WIDTH 804 +#define VD55G1_HEIGHT 704 +#define VD55G1_MODE_DEF 0 +#define VD55G1_NB_GPIOS 4 +#define VD55G1_MBUS_CODE_DEF 0 +#define VD55G1_DGAIN_DEF 256 +#define VD55G1_AGAIN_DEF 19 +#define VD55G1_EXPO_MAX_TERM 64 +#define VD55G1_EXPO_DEF 500 +#define VD55G1_LINE_LENGTH_MIN 1128 +#define VD55G1_LINE_LENGTH_SUB_MIN 1344 +#define VD55G1_VBLANK_MIN 86 +#define VD55G1_VBLANK_MAX 0xffff +#define VD55G1_FRAME_LENGTH_DEF 1860 /* 60 fps */ +#define VD55G1_MIPI_MARGIN 900 +#define VD55G1_CTX_OFFSET 0x50 +#define VD55G1_FWPATCH_REVISION_MAJOR 2 +#define VD55G1_FWPATCH_REVISION_MINOR 9 +#define VD55G1_XCLK_FREQ_MIN (6 * HZ_PER_MHZ) +#define VD55G1_XCLK_FREQ_MAX (27 * HZ_PER_MHZ) +#define VD55G1_MIPI_RATE_MIN (250 * MEGA) +#define VD55G1_MIPI_RATE_MAX (1200 * MEGA) + +#define VD55G1_MODEL_ID_NAME(id) \ + ((id) == VD55G1_MODEL_ID_VD55G1 ? "vd55g1" : "vd65g4") + +static const u8 vd55g1_patch_array[] = { + 0x44, 0x03, 0x09, 0x02, 0xe6, 0x01, 0x42, 0x00, 0xea, 0x01, 0x42, 0x00, + 0xf0, 0x01, 0x42, 0x00, 0xe6, 0x01, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0xfa, 0x68, 0x40, 0x00, 0xe8, + 0x09, 0xbe, 0x4c, 0x08, 0x00, 0xf2, 0x93, 0xdd, 0x1c, 0x00, 0xc0, 0xe2, + 0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x00, 0xfa, 0x6b, 0x80, 0x98, 0x7f, + 0xfc, 0xef, 0x11, 0xc1, 0x0f, 0x82, 0x69, 0xbe, 0x0f, 0xac, 0x58, 0x40, + 0x00, 0xe8, 0x0c, 0x0c, 0x00, 0xf2, 0x93, 0xdd, 0x1c, 0x00, 0x40, 0xe3, + 0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x84, 0xfa, 0x46, 0x0e, 0xe8, 0xe0, + 0x08, 0xde, 0x4a, 0x40, 0x84, 0xe0, 0xa5, 0x86, 0xa8, 0x7d, 0xfc, 0xef, + 0x6b, 0x80, 0x01, 0xbf, 0x28, 0x77, 0x0c, 0xef, 0x0b, 0x0e, 0x21, 0x78, + 0x06, 0xc0, 0x0b, 0xa5, 0xb5, 0x84, 0x06, 0x42, 0x98, 0xe1, 0x01, 0x81, + 0x01, 0x42, 0x38, 0xe0, 0x0c, 0xc4, 0x0e, 0x84, 0x46, 0x02, 0x84, 0xe0, + 0x0c, 0x84, 0x11, 0x81, 0x21, 0x81, 0x31, 0x81, 0x41, 0x81, 0x51, 0x81, + 0xc1, 0x81, 0x05, 0x83, 0x0c, 0x0c, 0x84, 0xf2, 0x93, 0xdd, 0x06, 0x40, + 0x98, 0xe1, 0xc8, 0x80, 0x58, 0x82, 0x48, 0xc0, 0x38, 0xc2, 0x29, 0x00, + 0x10, 0xe0, 0x19, 0x00, 0x14, 0xe0, 0x09, 0x00, 0x38, 0xe0, 0x5f, 0xb8, + 0x5f, 0xa8, 0x5f, 0xa6, 0x5f, 0xa4, 0x5f, 0xa2, 0x5f, 0xa0, 0x56, 0x41, + 0x98, 0xe1, 0x18, 0x82, 0x28, 0x80, 0x38, 0xc0, 0x5f, 0xa2, 0x19, 0x00, + 0x20, 0xf8, 0x5f, 0xa4, 0x28, 0xc2, 0x5f, 0xa6, 0x39, 0x00, 0x10, 0xe0, + 0x5f, 0xa2, 0x19, 0x00, 0x14, 0xe0, 0x5f, 0xa4, 0x29, 0x00, 0x18, 0xe0, + 0x5f, 0xa6, 0x39, 0x00, 0x40, 0xe0, 0x5f, 0xa2, 0x19, 0x00, 0x44, 0xe0, + 0x5f, 0xa4, 0x29, 0x00, 0x1c, 0xe0, 0x5f, 0xa6, 0x39, 0x00, 0x38, 0xe0, + 0x5f, 0xa2, 0x19, 0x00, 0x20, 0xe0, 0x5f, 0xa4, 0x29, 0x00, 0x24, 0xe0, + 0x5f, 0xa6, 0x39, 0x00, 0x28, 0xe0, 0x5f, 0xa2, 0x19, 0x00, 0x2c, 0xe0, + 0x5f, 0xa4, 0x29, 0x00, 0x30, 0xe0, 0x5f, 0xa6, 0x09, 0x00, 0x34, 0xe0, + 0x5f, 0xa2, 0x5f, 0xa4, 0x5f, 0xa0, 0x4a, 0x0a, 0xfc, 0xfb, 0xe5, 0x82, + 0x08, 0xde, 0x4a, 0x40, 0x88, 0xe0, 0xf6, 0x40, 0x00, 0xe0, 0x01, 0x4e, + 0x99, 0x78, 0x0a, 0xc0, 0x85, 0x80, 0x98, 0x40, 0x00, 0xe8, 0x35, 0x81, + 0xa8, 0x40, 0x00, 0xe8, 0x0b, 0x8c, 0x0c, 0x0c, 0x84, 0xf2, 0xd5, 0xed, + 0x83, 0xc1, 0x13, 0xc5, 0x93, 0xdd, 0xc3, 0xc1, 0x83, 0xc1, 0x13, 0xc3, + 0x93, 0xdd, 0xc3, 0xc1, 0x4c, 0x04, 0x04, 0xfa, 0xc6, 0x0f, 0x94, 0xe0, + 0x19, 0x0e, 0xc9, 0x65, 0x01, 0xc0, 0x28, 0xde, 0x0a, 0x42, 0x80, 0xe0, + 0x24, 0x02, 0x00, 0xfc, 0x16, 0xde, 0xa5, 0x8a, 0x19, 0x00, 0xb8, 0xe0, + 0x10, 0x02, 0x0c, 0xec, 0x1d, 0xe6, 0x14, 0x02, 0x88, 0x80, 0x4e, 0x04, + 0x01, 0x00, 0x10, 0x80, 0x25, 0x02, 0x08, 0x9c, 0x86, 0x02, 0x00, 0x80, + 0x08, 0x44, 0x00, 0x98, 0x55, 0x81, 0x11, 0x85, 0x45, 0x81, 0x11, 0x89, + 0x25, 0x81, 0x11, 0x83, 0x2b, 0x00, 0x24, 0xe0, 0x64, 0xc2, 0x0b, 0x84, + 0x08, 0x51, 0x00, 0xef, 0x2b, 0x80, 0x01, 0x83, 0x1b, 0x8c, 0x38, 0x7d, + 0x5c, 0xef, 0x18, 0xde, 0x0b, 0xa1, 0x25, 0x82, 0x0b, 0x0e, 0x88, 0xf9, + 0x0a, 0x00, 0x00, 0xe8, 0x10, 0x42, 0x04, 0x9c, 0x11, 0x4e, 0x0c, 0x80, + 0x10, 0x40, 0x04, 0xf0, 0x4e, 0x05, 0x01, 0x60, 0x10, 0xc0, 0x06, 0x88, + 0x10, 0x40, 0xf8, 0xf3, 0x06, 0xde, 0x4c, 0x0c, 0x04, 0xf2, 0x93, 0xdd, + 0x0c, 0x04, 0x1c, 0xfe, 0xf6, 0x0f, 0x94, 0xe0, 0x38, 0x9c, 0x46, 0x51, + 0xfc, 0xe0, 0x46, 0x49, 0x38, 0xe2, 0x30, 0x46, 0xf8, 0xf3, 0x36, 0x9c, + 0xc6, 0x46, 0x0c, 0xe1, 0x34, 0x8c, 0x94, 0xa0, 0x4e, 0xa0, 0x39, 0x06, + 0x80, 0xe0, 0x4a, 0x46, 0x94, 0xe0, 0x05, 0x8c, 0x6a, 0x40, 0x80, 0xe0, + 0x2c, 0x0c, 0x00, 0xe2, 0x0b, 0x8c, 0xb8, 0x7c, 0x5c, 0xef, 0x0b, 0x8c, + 0x9e, 0xa0, 0xf8, 0x40, 0x60, 0xef, 0x0b, 0xa1, 0x5a, 0x40, 0x80, 0xe0, + 0x65, 0x88, 0x28, 0x02, 0x01, 0x40, 0x00, 0x80, 0x2a, 0x42, 0x9c, 0xe1, + 0x28, 0x49, 0x60, 0xef, 0x96, 0x4d, 0x9c, 0xe1, 0x01, 0x81, 0x06, 0x98, + 0xd5, 0x81, 0x09, 0x0e, 0xa1, 0x64, 0x01, 0xc0, 0x4a, 0x40, 0x88, 0xe0, + 0x85, 0x80, 0xb8, 0x77, 0xfc, 0xef, 0x35, 0x81, 0xc8, 0x77, 0xfc, 0xef, + 0x08, 0x98, 0x4a, 0x00, 0xfc, 0xfb, 0x55, 0xfc, 0xe8, 0x4a, 0x60, 0xef, + 0x1a, 0x44, 0x9c, 0xe1, 0x35, 0x81, 0x1a, 0x4e, 0x9c, 0xe9, 0x1c, 0x00, + 0x00, 0xe2, 0x0c, 0x0c, 0x1c, 0xf6, 0x93, 0xdd, 0x0d, 0xc3, 0x1a, 0x41, + 0x08, 0xe4, 0x0a, 0x40, 0x84, 0xe1, 0x0c, 0x00, 0x00, 0xe2, 0x93, 0xdd, + 0x4c, 0x04, 0x1c, 0xfa, 0x86, 0x52, 0xec, 0xe1, 0x08, 0xa6, 0x65, 0x12, + 0x24, 0xf8, 0x0e, 0x02, 0x99, 0x7a, 0x00, 0xc0, 0x00, 0x40, 0xa0, 0xf3, + 0x06, 0xa6, 0x0b, 0x8c, 0x08, 0x49, 0x00, 0xef, 0x85, 0x12, 0x28, 0xf8, + 0x02, 0x02, 0xfc, 0xed, 0xf6, 0x47, 0xfd, 0x6f, 0xe0, 0xff, 0x04, 0xe2, + 0x14, 0x04, 0xc0, 0xe0, 0x0f, 0x86, 0x2f, 0xa0, 0x0b, 0x8c, 0x2e, 0xe2, + 0x08, 0x48, 0x00, 0xef, 0x86, 0x02, 0x84, 0xfe, 0x0e, 0x05, 0x09, 0x7d, + 0x00, 0xc0, 0x05, 0x52, 0x08, 0xf8, 0x18, 0x7d, 0xfc, 0xef, 0x4a, 0x40, + 0x80, 0xe0, 0x09, 0x12, 0x04, 0xc0, 0x65, 0x12, 0x20, 0xf8, 0x00, 0x40, + 0x40, 0xdc, 0x01, 0x52, 0x04, 0xc0, 0x0e, 0x00, 0x41, 0x78, 0xf5, 0xc5, + 0x6d, 0xc0, 0xb5, 0x82, 0x05, 0x10, 0x10, 0xe0, 0x11, 0xf1, 0x0f, 0x82, + 0x05, 0x50, 0x10, 0xe0, 0x05, 0x10, 0x10, 0xe0, 0xfe, 0x02, 0xf0, 0xff, + 0x0f, 0x82, 0x85, 0x83, 0x15, 0x10, 0x10, 0xe0, 0x16, 0x00, 0x91, 0x6e, + 0x69, 0xcd, 0x21, 0xf1, 0x6d, 0xc1, 0x01, 0x83, 0x2f, 0x82, 0x26, 0x00, + 0x00, 0x80, 0x2f, 0xa0, 0x25, 0x50, 0x10, 0xe0, 0x05, 0x10, 0x10, 0xe0, + 0x11, 0xa1, 0xfe, 0x04, 0xf0, 0xff, 0x06, 0x42, 0x00, 0x80, 0x0f, 0x84, + 0x0f, 0xa2, 0x05, 0x50, 0x10, 0xe0, 0x16, 0x00, 0x91, 0x6e, 0x69, 0xcd, + 0x6d, 0xc1, 0x71, 0x8d, 0x16, 0x00, 0x79, 0x61, 0x2d, 0xcb, 0x86, 0x0e, + 0x00, 0x80, 0x6d, 0xc1, 0x56, 0x0e, 0x00, 0xc0, 0x0b, 0x8c, 0x1b, 0x8e, + 0x71, 0x52, 0x0c, 0xf8, 0x08, 0x43, 0x00, 0xef, 0x05, 0x52, 0x14, 0xf8, + 0x15, 0x10, 0x28, 0xe0, 0x70, 0x04, 0x04, 0xec, 0x31, 0xe1, 0x29, 0x9e, + 0x1f, 0x86, 0x1f, 0xa4, 0x15, 0x50, 0x28, 0xe0, 0x86, 0x42, 0x3c, 0xe0, + 0x0e, 0x04, 0x9d, 0x64, 0x9b, 0xc2, 0x05, 0x52, 0x1c, 0xf8, 0x78, 0xa6, + 0x48, 0x77, 0xfc, 0xef, 0x4a, 0x40, 0x80, 0xe0, 0x70, 0x4e, 0x10, 0xdc, + 0x1e, 0x00, 0x81, 0x70, 0xeb, 0xcb, 0x70, 0x4e, 0xec, 0x93, 0x6d, 0xc1, + 0x11, 0x85, 0x36, 0x02, 0x00, 0x80, 0x76, 0xa6, 0x11, 0x52, 0x10, 0xf8, + 0x05, 0x10, 0x40, 0xe0, 0xfe, 0x47, 0x0c, 0xff, 0x14, 0x04, 0xa0, 0xe0, + 0x0f, 0x86, 0x0f, 0xa4, 0x05, 0x50, 0x40, 0xe0, 0x05, 0x10, 0x28, 0xe0, + 0xfe, 0x47, 0xfd, 0x7f, 0xe3, 0xff, 0x14, 0x04, 0xd0, 0xe0, 0x0f, 0x86, + 0x2f, 0xa0, 0x20, 0x00, 0x01, 0x6c, 0x00, 0xd0, 0x05, 0x50, 0x28, 0xe0, + 0x0b, 0x8c, 0xf8, 0x7e, 0xfc, 0xee, 0x0e, 0x03, 0x59, 0x78, 0xf5, 0xc5, + 0x0d, 0xc2, 0x05, 0x52, 0x0c, 0xf8, 0x08, 0xa6, 0x46, 0x42, 0xb4, 0xe0, + 0x18, 0x84, 0x00, 0x40, 0xf4, 0x93, 0x00, 0x40, 0x08, 0xdc, 0x1b, 0xa1, + 0x06, 0xa6, 0x05, 0x10, 0x40, 0x80, 0x04, 0x00, 0x50, 0x9c, 0x65, 0x8a, + 0x05, 0x10, 0x44, 0xe0, 0xf6, 0x43, 0xfd, 0x6f, 0x00, 0xf8, 0x0f, 0x82, + 0x06, 0x02, 0x01, 0x60, 0x1e, 0xc0, 0x0f, 0xa2, 0x05, 0x50, 0x44, 0xe0, + 0x05, 0x10, 0x44, 0xe0, 0x0e, 0x02, 0x00, 0xf8, 0x0f, 0x82, 0x09, 0xf6, + 0x05, 0x50, 0x44, 0xe0, 0x05, 0x10, 0x40, 0xe0, 0x04, 0x00, 0x54, 0xfc, + 0x05, 0x50, 0x40, 0xe0, 0x05, 0x10, 0x40, 0xe0, 0x04, 0x00, 0xcc, 0xfc, + 0x05, 0x50, 0x40, 0xe0, 0x05, 0x10, 0x40, 0xe0, 0x04, 0x00, 0x4c, 0xfc, + 0x05, 0x50, 0x40, 0xe0, 0x05, 0x10, 0x40, 0xe0, 0x04, 0x00, 0xd0, 0xfc, + 0x05, 0x50, 0x40, 0xe0, 0x4c, 0x0c, 0x1c, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, + 0xc6, 0x40, 0xfc, 0xe0, 0x04, 0x80, 0xc6, 0x44, 0x0c, 0xe1, 0x15, 0x04, + 0x0c, 0xf8, 0x0a, 0x80, 0x06, 0x07, 0x04, 0xe0, 0x03, 0x42, 0x48, 0xe1, + 0x46, 0x02, 0x40, 0xe2, 0x08, 0xc6, 0x44, 0x88, 0x06, 0x46, 0x0e, 0xe0, + 0x86, 0x01, 0x84, 0xe0, 0x33, 0x80, 0x39, 0x06, 0xd8, 0xef, 0x0a, 0x46, + 0x80, 0xe0, 0x31, 0xbf, 0x06, 0x06, 0x00, 0xc0, 0x31, 0x48, 0x60, 0xe0, + 0x34, 0x88, 0x49, 0x06, 0x40, 0xe1, 0x40, 0x48, 0x7c, 0xf3, 0x41, 0x46, + 0x40, 0xe1, 0x24, 0x8a, 0x39, 0x04, 0x10, 0xe0, 0x39, 0xc2, 0x31, 0x44, + 0x10, 0xe0, 0x14, 0xc4, 0x1b, 0xa5, 0x11, 0x83, 0x11, 0x40, 0x25, 0x6a, + 0x01, 0xc0, 0x08, 0x5c, 0x00, 0xda, 0x15, 0x00, 0xcc, 0xe0, 0x25, 0x00, + 0xf8, 0xe0, 0x1b, 0x85, 0x08, 0x5c, 0x00, 0x9a, 0x4e, 0x03, 0x01, 0x60, + 0x10, 0xc0, 0x29, 0x00, 0x1c, 0xe4, 0x18, 0x84, 0x20, 0x44, 0xf8, 0xf3, + 0x2f, 0xa2, 0x21, 0x40, 0x1c, 0xe4, 0x93, 0xdd, 0x0c, 0x00, 0x80, 0xfa, + 0x15, 0x00, 0x3c, 0xe0, 0x21, 0x81, 0x31, 0x85, 0x21, 0x42, 0x60, 0xe0, + 0x15, 0x00, 0x44, 0xe0, 0x31, 0x42, 0x40, 0xe1, 0x15, 0x00, 0x34, 0xe0, + 0x21, 0x42, 0x20, 0xe0, 0x15, 0x00, 0x34, 0xe0, 0xd6, 0x04, 0x10, 0xe0, + 0x23, 0x42, 0x30, 0xe0, 0x15, 0x00, 0x34, 0xe0, 0x86, 0x44, 0x04, 0xe0, + 0x23, 0x42, 0x38, 0xe0, 0x05, 0x00, 0x30, 0xe0, 0xc6, 0x02, 0x08, 0xe0, + 0x13, 0x40, 0x10, 0xe3, 0xe8, 0x56, 0x40, 0xef, 0x06, 0x40, 0x0c, 0xe1, + 0x04, 0x80, 0x06, 0x02, 0x94, 0xe0, 0x2b, 0x02, 0xc4, 0xea, 0x3b, 0x00, + 0x78, 0xe2, 0x20, 0x44, 0xfd, 0x73, 0x07, 0xc0, 0x30, 0x46, 0x01, 0x70, + 0xf8, 0xc0, 0x3f, 0xa4, 0x33, 0x40, 0x78, 0xe2, 0x0a, 0x84, 0x0c, 0x08, + 0x80, 0xf2, 0xf8, 0x3b, 0x3c, 0xff, 0xc3, 0xc1, 0x06, 0x40, 0x0c, 0xe1, + 0x04, 0x80, 0x1b, 0x00, 0x40, 0xe4, 0x19, 0xc2, 0x13, 0x40, 0x40, 0xe4, + 0x1b, 0x00, 0x40, 0xe4, 0x19, 0xc4, 0x13, 0x40, 0x40, 0xe4, 0x93, 0xdd, + 0xc6, 0x43, 0xec, 0xe0, 0x46, 0x41, 0xfc, 0xe0, 0x24, 0x84, 0x04, 0x80, + 0x31, 0x81, 0x4a, 0x44, 0x80, 0xe0, 0x86, 0x44, 0x0c, 0xe1, 0x09, 0x00, + 0x6c, 0xe0, 0xc4, 0x8a, 0x8e, 0x47, 0xfc, 0x9f, 0x01, 0x42, 0x51, 0x78, + 0x0c, 0xc0, 0x31, 0x58, 0x90, 0xe0, 0x34, 0x8a, 0x41, 0xbf, 0x06, 0x08, + 0x00, 0xc0, 0x41, 0x46, 0xa0, 0xe0, 0x34, 0x8a, 0x51, 0x81, 0xf6, 0x0b, + 0x00, 0xc0, 0x51, 0x46, 0xd0, 0xe0, 0x34, 0x8a, 0x01, 0xbf, 0x51, 0x46, + 0xe0, 0xe0, 0x44, 0x84, 0x0a, 0x48, 0x84, 0xe0, 0x75, 0x86, 0x54, 0xca, + 0x49, 0x88, 0x44, 0x06, 0x88, 0xe1, 0x36, 0x94, 0x4a, 0x46, 0x80, 0xe0, + 0x34, 0xca, 0x47, 0xc6, 0x11, 0x8d, 0x41, 0x46, 0xd0, 0xe0, 0x34, 0x88, + 0x76, 0x02, 0x00, 0xc0, 0x06, 0x00, 0x00, 0xc0, 0x16, 0x8c, 0x14, 0x88, + 0x01, 0x42, 0xc0, 0xe1, 0x01, 0x42, 0xe0, 0xe1, 0x01, 0x42, 0xf0, 0xe1, + 0x93, 0xdd, 0x34, 0xca, 0x41, 0x85, 0x46, 0x8c, 0x34, 0xca, 0x06, 0x48, + 0x00, 0xe0, 0x41, 0x46, 0xd0, 0xe0, 0x34, 0x88, 0x41, 0x83, 0x46, 0x8c, + 0x34, 0x88, 0x01, 0x46, 0xc0, 0xe1, 0x01, 0x46, 0xe0, 0xe1, 0x01, 0x46, + 0xf0, 0xe1, 0x09, 0x02, 0x20, 0xe0, 0x14, 0xca, 0x03, 0x42, 0x58, 0xe0, + 0x93, 0xdd, 0xc3, 0xc1, 0x4c, 0x04, 0x04, 0xfa, 0x46, 0x4e, 0x08, 0xe1, + 0x06, 0x4c, 0x0c, 0xe1, 0x0a, 0x9e, 0x14, 0x98, 0x05, 0x42, 0x44, 0xe0, + 0x10, 0x00, 0xe1, 0x65, 0x03, 0xc0, 0x78, 0x41, 0x00, 0xe8, 0x08, 0x9c, + 0x0b, 0xa1, 0x04, 0x98, 0x06, 0x02, 0x10, 0x80, 0x13, 0x40, 0xf8, 0x86, + 0x65, 0x82, 0x00, 0x00, 0xe1, 0x65, 0x03, 0xc0, 0xa8, 0x40, 0x00, 0xe8, + 0x14, 0x98, 0x04, 0x00, 0xa0, 0xfc, 0x03, 0x42, 0x00, 0xe7, 0x4c, 0x0c, + 0x04, 0xf2, 0x93, 0xdd, 0x0a, 0x80, 0x93, 0xdd, 0x0c, 0x04, 0x00, 0xfa, + 0x06, 0x02, 0xec, 0xe1, 0x64, 0x84, 0x15, 0x0c, 0x2c, 0xe0, 0x14, 0x02, + 0xa0, 0xfc, 0x15, 0x4c, 0x2c, 0xe0, 0xd8, 0x40, 0x00, 0xe8, 0x14, 0xd8, + 0x09, 0x82, 0x14, 0x02, 0x00, 0xfc, 0x1f, 0xa0, 0x1e, 0xd8, 0x01, 0x85, + 0x0c, 0x0c, 0x00, 0xf2, 0xe8, 0x32, 0x2c, 0xff, 0x93, 0xdd, 0xc3, 0xc1, + 0x0c, 0x04, 0x00, 0xfa, 0x6b, 0x80, 0xf6, 0x01, 0x94, 0xe0, 0x08, 0x80, + 0x4a, 0x40, 0x80, 0xe0, 0x45, 0x86, 0x06, 0x40, 0x0c, 0xe1, 0x04, 0x80, + 0xc6, 0x02, 0x40, 0xe2, 0x09, 0x00, 0xd0, 0xe0, 0x14, 0x84, 0x1b, 0xa5, + 0x15, 0x84, 0x07, 0xc5, 0x09, 0x82, 0x18, 0x41, 0x00, 0xe8, 0x46, 0x43, + 0xfc, 0xe0, 0x14, 0x84, 0x19, 0x02, 0xd8, 0xe0, 0x19, 0x82, 0x0b, 0x83, + 0x16, 0x00, 0x00, 0xc0, 0x01, 0x4c, 0x00, 0xc0, 0x0c, 0x0c, 0x00, 0xf2, + 0x93, 0xdd, 0xc3, 0xc1, 0x4a, 0x00, 0x00, 0xe0, 0x0c, 0x00, 0x00, 0xe2, + 0x93, 0xdd, 0xc3, 0xc1, 0x46, 0x40, 0x84, 0xe0, 0x11, 0xaf, 0x13, 0x40, + 0x6c, 0xec, 0x11, 0xb3, 0x13, 0x40, 0x70, 0xec, 0xc6, 0x43, 0xf0, 0xe0, + 0x13, 0x40, 0xdc, 0xec, 0xc6, 0x02, 0x24, 0xe0, 0x1c, 0x80, 0x93, 0xdd, + 0x4c, 0x00, 0x00, 0xfa, 0xc8, 0x60, 0x7c, 0xef, 0xe8, 0x61, 0x7c, 0xef, + 0x28, 0x7e, 0x80, 0xef, 0xc6, 0x40, 0x98, 0xe1, 0x11, 0x83, 0x16, 0x80, + 0x46, 0x01, 0x10, 0xe1, 0x11, 0x81, 0x16, 0x80, 0x4c, 0x08, 0x00, 0xf2, + 0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x0c, 0xfa, 0x6b, 0x80, 0x04, 0x98, + 0x7b, 0x82, 0x56, 0x42, 0xb4, 0xe0, 0x88, 0x84, 0x05, 0x00, 0x10, 0xe0, + 0x09, 0x86, 0x0b, 0xa5, 0x46, 0x02, 0x00, 0x80, 0x06, 0x05, 0x00, 0x80, + 0x25, 0x82, 0x0b, 0xa3, 0xa5, 0x80, 0x0b, 0xa1, 0x06, 0x00, 0xf4, 0xef, + 0xd5, 0x84, 0x11, 0x85, 0x21, 0x91, 0x0b, 0x8e, 0x88, 0x74, 0x10, 0xef, + 0x0b, 0xa1, 0xf5, 0x82, 0x0a, 0x9e, 0x1a, 0x9c, 0x24, 0x98, 0x07, 0xe0, + 0x0f, 0xa2, 0x0e, 0xca, 0x0a, 0xde, 0x1a, 0xdc, 0x24, 0x98, 0x03, 0xb0, + 0x07, 0xe0, 0x0f, 0xa2, 0x0e, 0xc8, 0x01, 0x81, 0x0c, 0x0c, 0x0c, 0xf2, + 0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x7c, 0xfa, 0x46, 0x42, 0x9c, 0xe0, + 0x0b, 0x02, 0x04, 0xe3, 0xf0, 0x1e, 0x30, 0xec, 0x0b, 0xa3, 0x35, 0x96, + 0x8e, 0x01, 0x01, 0x60, 0x10, 0xc0, 0x0e, 0xfc, 0xc6, 0x05, 0xd0, 0xe1, + 0x0b, 0x82, 0x31, 0x81, 0x10, 0x16, 0x00, 0xe5, 0x20, 0x10, 0x20, 0xe7, + 0x0e, 0xbe, 0xb5, 0x85, 0x94, 0xfc, 0xa4, 0xbe, 0x82, 0x4c, 0x9c, 0xf0, + 0x05, 0x0c, 0x40, 0xe0, 0x11, 0x89, 0x93, 0x8e, 0xa3, 0x8e, 0x58, 0x44, + 0x00, 0xe8, 0x15, 0x0c, 0xc0, 0xf8, 0x04, 0x0c, 0x80, 0xfb, 0x0c, 0xed, + 0x0b, 0x82, 0x1b, 0x8c, 0x48, 0x44, 0x00, 0xe8, 0x15, 0x10, 0x1c, 0xfc, + 0x0e, 0xa8, 0x0b, 0x82, 0x1b, 0x8c, 0xd8, 0x43, 0x00, 0xe8, 0x71, 0x88, + 0x0e, 0xa4, 0x0a, 0x0e, 0x40, 0xe0, 0x35, 0xf8, 0x04, 0xbe, 0x14, 0xbc, + 0x81, 0xa0, 0x03, 0x8e, 0x0e, 0xbe, 0x04, 0xfc, 0x11, 0x82, 0x3b, 0x82, + 0x03, 0x8e, 0x0e, 0xfc, 0x3b, 0xa9, 0x06, 0x0e, 0x00, 0xc0, 0x35, 0x5e, + 0x00, 0xc0, 0xd5, 0xfa, 0xc6, 0x01, 0xd0, 0xe1, 0x7b, 0x80, 0x04, 0x9e, + 0x11, 0x91, 0x98, 0x41, 0x00, 0xe8, 0x24, 0x9c, 0x46, 0x42, 0x9c, 0xe0, + 0x6b, 0x82, 0x03, 0x4c, 0xc4, 0xe0, 0x11, 0x91, 0x0b, 0x84, 0xf8, 0x40, + 0x00, 0xe8, 0x19, 0x0e, 0x20, 0xe5, 0x03, 0x4c, 0xc0, 0xe0, 0x0b, 0x82, + 0x08, 0x72, 0xfc, 0xef, 0x01, 0x4c, 0x24, 0xf9, 0xf1, 0x98, 0x0c, 0x0c, + 0x7c, 0xf2, 0x93, 0xdd, 0x4c, 0x00, 0x00, 0xfa, 0x48, 0x65, 0x2c, 0xef, + 0x4c, 0x08, 0x00, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x00, 0xfa, + 0x6b, 0x82, 0x78, 0x6e, 0xfc, 0xee, 0x46, 0x42, 0xec, 0xe0, 0x24, 0x84, + 0x24, 0x02, 0x80, 0xfa, 0x1d, 0xcc, 0x11, 0x83, 0xf5, 0x82, 0x24, 0x02, + 0xa0, 0xe1, 0x14, 0x02, 0x80, 0xfa, 0x1d, 0xcc, 0x11, 0x85, 0x15, 0x82, + 0x27, 0xe1, 0x24, 0x02, 0x80, 0xfa, 0x1d, 0xcc, 0x11, 0x89, 0x86, 0x02, + 0x00, 0x80, 0x0c, 0x0c, 0x00, 0xf2, 0x18, 0x17, 0xfc, 0xfe, 0xc3, 0xc1, + 0x0c, 0x04, 0x00, 0xfa, 0x06, 0x41, 0x8c, 0xe0, 0x1b, 0x00, 0xec, 0xe4, + 0x1b, 0xa3, 0x75, 0x84, 0x11, 0x81, 0x8e, 0x05, 0x01, 0x60, 0x10, 0xc0, + 0x00, 0x06, 0xc0, 0xe5, 0x95, 0x81, 0x44, 0x88, 0x1d, 0xee, 0x75, 0x80, + 0x4e, 0xc1, 0x25, 0x81, 0x4e, 0xcd, 0x21, 0x88, 0x11, 0x82, 0x0a, 0x02, + 0x40, 0xe0, 0xd5, 0xfc, 0x56, 0x00, 0x00, 0xe1, 0x18, 0x80, 0x1b, 0xa1, + 0xc5, 0x84, 0x08, 0x82, 0x4a, 0x00, 0xfc, 0xfb, 0x45, 0x84, 0x86, 0x4d, + 0x84, 0xe1, 0x04, 0x98, 0x05, 0x00, 0x10, 0xe0, 0x4a, 0x40, 0x80, 0xe0, + 0x45, 0x82, 0x11, 0x81, 0x0b, 0x8c, 0x58, 0x76, 0x28, 0xef, 0x0b, 0x8c, + 0x0c, 0x0c, 0x00, 0xf2, 0x88, 0x35, 0x28, 0xff, 0x0c, 0x0c, 0x00, 0xf2, + 0x93, 0xdd, 0xc3, 0xc1, 0x46, 0x41, 0xfc, 0xe0, 0x04, 0x80, 0x09, 0x00, + 0x80, 0xe0, 0x09, 0x9e, 0x0b, 0xa3, 0x75, 0x82, 0x46, 0x41, 0x80, 0xe1, + 0x04, 0x80, 0xc6, 0x42, 0x8c, 0xe0, 0x04, 0xc2, 0x00, 0x40, 0x00, 0xf2, + 0x07, 0xcf, 0x06, 0x84, 0x06, 0x40, 0x84, 0xe0, 0x15, 0x00, 0x28, 0xe5, + 0x1c, 0xc2, 0x93, 0xdd, 0x0b, 0xa1, 0xc6, 0x00, 0xa0, 0xe1, 0x15, 0x00, + 0x04, 0xf8, 0x05, 0x84, 0x21, 0x8b, 0x2c, 0x84, 0x14, 0x80, 0x2c, 0x84, + 0x14, 0x82, 0x2c, 0x84, 0x15, 0x00, 0x10, 0xe0, 0x21, 0xa1, 0x21, 0x42, + 0x10, 0xe0, 0x05, 0x00, 0x14, 0xe0, 0x01, 0x88, 0x75, 0x83, 0x21, 0x85, + 0x2c, 0x84, 0x14, 0x80, 0x06, 0x46, 0x00, 0xe0, 0x2c, 0x84, 0x14, 0x82, + 0x2c, 0x84, 0x14, 0xc0, 0x21, 0xa1, 0x21, 0x42, 0x20, 0xe0, 0x14, 0xc2, + 0x31, 0x42, 0x20, 0xe0, 0x15, 0x00, 0x10, 0xe0, 0x21, 0x42, 0x20, 0xe0, + 0x05, 0x00, 0x14, 0xe0, 0x01, 0x90, 0x06, 0x42, 0x00, 0xe0, 0x16, 0x80, + 0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x7c, 0xfa, 0x4a, 0x40, 0x80, 0xe0, + 0xf0, 0x1e, 0x30, 0xec, 0xe5, 0x82, 0xa6, 0x40, 0x00, 0xe1, 0x1a, 0x80, + 0x2a, 0xc0, 0x3a, 0xc2, 0x13, 0x40, 0x10, 0xe0, 0x1a, 0x82, 0x23, 0x40, + 0x18, 0xe0, 0x33, 0x40, 0x1c, 0xe0, 0x13, 0x40, 0x14, 0xe0, 0xf8, 0x61, + 0x68, 0xef, 0xc6, 0x13, 0x00, 0xe1, 0x15, 0x12, 0x28, 0xf8, 0x0b, 0x02, + 0x2c, 0xe0, 0x1b, 0x02, 0x24, 0xe0, 0x8a, 0x00, 0xa5, 0x64, 0x03, 0xc0, + 0x35, 0x82, 0x0a, 0x4e, 0x9c, 0xe1, 0x1a, 0x03, 0x11, 0x6f, 0x02, 0xc0, + 0xe8, 0x13, 0x01, 0x20, 0x00, 0xc0, 0x1f, 0xa0, 0x5a, 0x42, 0x80, 0xe0, + 0x0a, 0x4e, 0x9c, 0xe1, 0x68, 0x13, 0x00, 0xa0, 0x09, 0x12, 0x78, 0xf8, + 0xa1, 0x81, 0xf0, 0x02, 0x10, 0xe4, 0x07, 0xc4, 0x0c, 0xfc, 0xf0, 0x00, + 0x20, 0xe4, 0xa6, 0x91, 0xa8, 0x53, 0x74, 0xef, 0x05, 0x12, 0x30, 0xf8, + 0x25, 0x12, 0x28, 0xf8, 0x61, 0x87, 0x09, 0x00, 0x48, 0xe0, 0x81, 0x85, + 0x09, 0x86, 0x0b, 0xa7, 0x26, 0x0c, 0x00, 0xc0, 0x0b, 0xa1, 0x0b, 0x04, + 0x28, 0xe0, 0x16, 0x0c, 0x00, 0x80, 0x03, 0x52, 0x04, 0xf8, 0x0b, 0x04, + 0x20, 0xe0, 0x0c, 0xa6, 0x1b, 0x04, 0x2c, 0xe0, 0x3b, 0x04, 0x28, 0xe0, + 0x4b, 0x04, 0x20, 0xe0, 0x13, 0x86, 0x3b, 0x04, 0x24, 0xe0, 0x10, 0x0a, + 0x04, 0xec, 0x1a, 0xfc, 0x33, 0x88, 0x30, 0x06, 0x04, 0xec, 0x12, 0x4e, + 0x94, 0xf0, 0x32, 0x48, 0x84, 0xf0, 0x4c, 0xe4, 0x7c, 0xa4, 0xcb, 0x04, + 0x28, 0xe0, 0x14, 0x08, 0x84, 0xe1, 0xcd, 0xc9, 0xc2, 0x58, 0x90, 0x91, + 0x42, 0x4e, 0x94, 0x90, 0xc3, 0x52, 0x04, 0x98, 0x73, 0x52, 0x00, 0x80, + 0x5b, 0x04, 0x20, 0xe0, 0x5d, 0xc9, 0x52, 0x40, 0x90, 0x91, 0x42, 0x48, + 0x8c, 0x90, 0x03, 0x52, 0x04, 0x80, 0x43, 0x52, 0x08, 0x80, 0x3b, 0x04, + 0x2c, 0xe0, 0x49, 0x04, 0xb8, 0xe0, 0x33, 0x52, 0x1c, 0xf8, 0x2b, 0x04, + 0x24, 0xe0, 0x4b, 0xab, 0x23, 0x52, 0x18, 0xf8, 0x65, 0x8a, 0x4b, 0xa9, + 0xe5, 0x90, 0x4b, 0xa7, 0x22, 0x44, 0x84, 0xd0, 0x32, 0x46, 0x84, 0xd0, + 0x33, 0x52, 0x1c, 0xd8, 0x23, 0x52, 0x18, 0xd8, 0x95, 0x96, 0x20, 0x44, + 0xf9, 0x73, 0xff, 0xc0, 0x27, 0xc3, 0x23, 0x82, 0x23, 0x52, 0x18, 0xf8, + 0x24, 0x02, 0x80, 0xfb, 0x04, 0x00, 0x80, 0xfb, 0x2b, 0x8c, 0x58, 0x52, + 0x74, 0xef, 0x1b, 0x12, 0x1c, 0xf8, 0x2a, 0xfc, 0x0c, 0xe4, 0x17, 0xc3, + 0x13, 0x84, 0x13, 0x52, 0x1c, 0xf8, 0x0b, 0x12, 0x04, 0xf8, 0x14, 0x02, + 0x80, 0xfb, 0x2b, 0x8c, 0x68, 0x51, 0x74, 0xef, 0xc5, 0x87, 0x20, 0x44, + 0xe1, 0x73, 0xff, 0xc0, 0x27, 0xc7, 0x23, 0x82, 0x23, 0x52, 0x18, 0xf8, + 0x24, 0x02, 0x80, 0xfb, 0x04, 0x00, 0x80, 0xfb, 0x2b, 0x8c, 0x78, 0x57, + 0x74, 0xef, 0x1b, 0x12, 0x1c, 0xf8, 0x2a, 0xfc, 0x0c, 0xe4, 0x17, 0xc7, + 0x13, 0x84, 0x13, 0x52, 0x1c, 0xf8, 0x0b, 0x12, 0x04, 0xf8, 0x14, 0x02, + 0x80, 0xfb, 0x2b, 0x8c, 0x88, 0x56, 0x74, 0xef, 0xe5, 0x83, 0x20, 0x44, + 0xf1, 0x73, 0xff, 0xc0, 0x27, 0xc5, 0x23, 0x82, 0x23, 0x52, 0x18, 0xf8, + 0x24, 0x02, 0x80, 0xfb, 0x04, 0x00, 0x80, 0xfb, 0x2b, 0x8c, 0x18, 0x52, + 0x74, 0xef, 0x1b, 0x12, 0x1c, 0xf8, 0x2a, 0xfc, 0x0c, 0xe4, 0x17, 0xc5, + 0x13, 0x84, 0x13, 0x52, 0x1c, 0xf8, 0x0b, 0x12, 0x04, 0xf8, 0x14, 0x02, + 0x80, 0xfb, 0x2b, 0x8c, 0x28, 0x51, 0x74, 0xef, 0x7b, 0x80, 0x7c, 0xa4, + 0x08, 0x91, 0xa3, 0x52, 0x1c, 0xe0, 0xa3, 0x52, 0x24, 0xe0, 0x0b, 0xa1, + 0x83, 0x52, 0x1c, 0x80, 0x83, 0x52, 0x24, 0x80, 0x89, 0x12, 0x78, 0xf8, + 0xf6, 0x57, 0xfc, 0xef, 0x6b, 0x12, 0x1c, 0xf8, 0xab, 0x12, 0x18, 0xf8, + 0xd6, 0x57, 0xfc, 0x8f, 0x8b, 0xa3, 0xa0, 0x40, 0x00, 0x9c, 0xa5, 0x86, + 0x64, 0x00, 0x80, 0xfb, 0x1b, 0x90, 0xf8, 0x7d, 0xf8, 0xee, 0x6b, 0x80, + 0xa4, 0x00, 0x80, 0xfb, 0x1b, 0x90, 0x98, 0x7d, 0xf8, 0xee, 0x15, 0x12, + 0x28, 0xf8, 0x19, 0x02, 0xb8, 0xe0, 0x1b, 0xad, 0x95, 0x82, 0x1a, 0xa6, + 0xa0, 0x44, 0xf9, 0x73, 0xff, 0xc0, 0x27, 0xc3, 0x13, 0x94, 0x10, 0x02, + 0x08, 0xec, 0x1c, 0xe4, 0x23, 0x52, 0x18, 0xf8, 0x1b, 0x12, 0x04, 0xf8, + 0x03, 0x96, 0x03, 0x52, 0x28, 0xe0, 0x1c, 0xe6, 0x0a, 0xa6, 0x1a, 0xe4, + 0x63, 0x96, 0x63, 0x52, 0x20, 0xe0, 0x73, 0x52, 0x10, 0xe0, 0x03, 0x52, + 0x14, 0xe0, 0x13, 0x52, 0x18, 0xe0, 0x98, 0x52, 0x74, 0xef, 0x09, 0x12, + 0x8c, 0xe0, 0x0b, 0xa1, 0x01, 0x81, 0x01, 0x52, 0x90, 0xe0, 0x65, 0x82, + 0x05, 0x12, 0x30, 0xf8, 0x09, 0x00, 0xa8, 0xe0, 0x0a, 0x00, 0x0c, 0xf8, + 0x16, 0x00, 0x00, 0xc0, 0x01, 0x52, 0x90, 0xc0, 0x46, 0x41, 0x84, 0xe0, + 0x0a, 0x80, 0x0a, 0x4e, 0x9c, 0xe9, 0x1a, 0x00, 0x08, 0xe0, 0x38, 0x01, + 0x01, 0x20, 0x00, 0xc0, 0x0b, 0x12, 0x1c, 0xe0, 0x1b, 0x12, 0x24, 0xe0, + 0x2b, 0x12, 0x28, 0xe0, 0x03, 0x52, 0x2c, 0xe0, 0x0b, 0x12, 0x20, 0xe0, + 0x13, 0x52, 0x34, 0xe0, 0x23, 0x52, 0x38, 0xe0, 0x03, 0x52, 0x30, 0xe0, + 0x0c, 0x00, 0x00, 0xe2, 0xf1, 0x98, 0x0c, 0x0c, 0x7c, 0xf2, 0x93, 0xdd, + 0x13, 0xa9, 0x00, 0x00, 0xa8, 0xc1, 0x40, 0x00, 0x68, 0x04, 0xa0, 0xe0, + 0x40, 0x6c, 0x40, 0x00, 0xe8, 0x34, 0xc8, 0xe0, 0xfc, 0x91, 0x40, 0x00, + 0x68, 0x1f, 0xb8, 0xe0, 0x30, 0x16, 0x41, 0x00, 0x28, 0x39, 0x74, 0xe0, + 0xb0, 0x7e, 0x40, 0x00, 0xe8, 0x38, 0xc0, 0xe0, 0x30, 0x04, 0x41, 0x00, + 0x48, 0x1b, 0x80, 0xe0, 0x30, 0x2e, 0x40, 0x00, 0x88, 0x0c, 0xec, 0xe0, + 0x10, 0x9f, 0x40, 0x00, 0x88, 0x08, 0xb4, 0xe0, 0x10, 0x01, 0x41, 0x00, + 0x68, 0x01, 0x84, 0xe0, 0x54, 0xd6, 0x40, 0x00, 0xc8, 0x1a, 0x98, 0xe0, + 0xd0, 0xc8, 0x40, 0x00, 0x68, 0x08, 0xa0, 0xe0, 0x80, 0xdb, 0x40, 0x00, + 0xe8, 0x35, 0x94, 0xe0, 0x74, 0xff, 0x40, 0x00, 0xa8, 0x11, 0x80, 0xe0, + 0xf8, 0x89, 0x40, 0x00, 0x88, 0x16, 0xbc, 0xe0, 0x00, 0x90, 0x40, 0x00, + 0x08, 0x35, 0xb8, 0xe0, 0x7c, 0x73, 0x40, 0x00, 0x88, 0x1b, 0xc8, 0xe0, + 0xf4, 0xff, 0x40, 0x00, 0x68, 0x39, 0x80, 0xe0, 0xa4, 0xa4, 0x40, 0x00, + 0xa8, 0x16, 0xb0, 0xe0, 0x50, 0xc9, 0x40, 0x00, 0x28, 0x3a, 0x98, 0xe0, + 0x00, 0xb9, 0x00, 0x00, 0xb6, 0x85, 0x00, 0x00, +}; + +static const char * const vd55g1_tp_menu[] = { + "Disabled", + "Diagonal Grey Scale", + "Pseudo-random Noise", +}; + +static const s64 vd55g1_ev_bias_menu[] = { + -3000, -2500, -2000, -1500, -1000, -500, + 0, + 500, 1000, 1500, 2000, 2500, 3000, +}; + +static const char * const vd55g1_hdr_menu[] = { + "No HDR", + /* + * This mode acquires 2 frames on the sensor, the first one is ditched + * out and only used for auto exposure data, the second one is output to + * the host + */ + "Internal subtraction", +}; + +static const char * const vd55g1_supply_name[] = { + "vcore", + "vddio", + "vana", +}; + +enum vd55g1_hdr_mode { + VD55G1_NO_HDR, + VD55G1_HDR_SUB, +}; + +struct vd55g1_mode { + u32 width; + u32 height; +}; + +static const u32 vd55g1_mbus_formats_mono[] = { + MEDIA_BUS_FMT_Y8_1X8, + MEDIA_BUS_FMT_Y10_1X10, +}; + +/* Format order is : no flip, hflip, vflip, both */ +static const u32 vd55g1_mbus_formats_bayer[][4] = { + { + MEDIA_BUS_FMT_SRGGB8_1X8, + MEDIA_BUS_FMT_SGRBG8_1X8, + MEDIA_BUS_FMT_SGBRG8_1X8, + MEDIA_BUS_FMT_SBGGR8_1X8, + }, + { + MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SBGGR10_1X10, + }, +}; + +static const struct vd55g1_mode vd55g1_supported_modes[] = { + { + .width = VD55G1_WIDTH, + .height = VD55G1_HEIGHT, + }, + { + .width = 800, + .height = VD55G1_HEIGHT, + }, + { + .width = 800, + .height = 600, + }, + { + .width = 640, + .height = 480, + }, + { + .width = 320, + .height = 240, + }, +}; + +enum vd55g1_expo_state { + VD55G1_EXP_AUTO, + VD55G1_EXP_FREEZE, + VD55G1_EXP_MANUAL, + VD55G1_EXP_SINGLE_STEP, + VD55G1_EXP_BYPASS, +}; + +struct vd55g1_vblank_limits { + u16 min; + u16 def; + u16 max; +}; + +struct vd55g1 { + struct device *dev; + unsigned int id; + struct v4l2_subdev sd; + struct media_pad pad; + struct regulator_bulk_data supplies[ARRAY_SIZE(vd55g1_supply_name)]; + struct gpio_desc *reset_gpio; + struct clk *xclk; + struct regmap *regmap; + u32 xclk_freq; + u16 oif_ctrl; + u8 gpios[VD55G1_NB_GPIOS]; + unsigned long ext_leds_mask; + u32 mipi_rate; + u32 pixel_clock; + u64 link_freq; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *pixel_rate_ctrl; + struct v4l2_ctrl *vblank_ctrl; + struct v4l2_ctrl *hblank_ctrl; + struct { + struct v4l2_ctrl *hflip_ctrl; + struct v4l2_ctrl *vflip_ctrl; + }; + struct v4l2_ctrl *patgen_ctrl; + struct { + struct v4l2_ctrl *ae_ctrl; + struct v4l2_ctrl *expo_ctrl; + struct v4l2_ctrl *again_ctrl; + struct v4l2_ctrl *dgain_ctrl; + }; + struct v4l2_ctrl *ae_lock_ctrl; + struct v4l2_ctrl *ae_bias_ctrl; + struct v4l2_ctrl *led_ctrl; + struct v4l2_ctrl *hdr_ctrl; +}; + +static inline struct vd55g1 *to_vd55g1(struct v4l2_subdev *sd) +{ + return container_of_const(sd, struct vd55g1, sd); +} + +static inline struct vd55g1 *ctrl_to_vd55g1(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = &container_of_const(ctrl->handler, + struct vd55g1, + ctrl_handler)->sd; + + return to_vd55g1(sd); +} + +static unsigned int vd55g1_get_fmt_bpp(u32 code) +{ + switch (code) { + case MEDIA_BUS_FMT_Y8_1X8: + case MEDIA_BUS_FMT_SRGGB8_1X8: + case MEDIA_BUS_FMT_SGRBG8_1X8: + case MEDIA_BUS_FMT_SGBRG8_1X8: + case MEDIA_BUS_FMT_SBGGR8_1X8: + default: + return 8; + + case MEDIA_BUS_FMT_Y10_1X10: + case MEDIA_BUS_FMT_SRGGB10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SBGGR10_1X10: + return 10; + } +} + +static unsigned int vd55g1_get_fmt_data_type(u32 code) +{ + switch (code) { + case MEDIA_BUS_FMT_Y8_1X8: + case MEDIA_BUS_FMT_SRGGB8_1X8: + case MEDIA_BUS_FMT_SGRBG8_1X8: + case MEDIA_BUS_FMT_SGBRG8_1X8: + case MEDIA_BUS_FMT_SBGGR8_1X8: + default: + return MIPI_CSI2_DT_RAW8; + + case MEDIA_BUS_FMT_Y10_1X10: + case MEDIA_BUS_FMT_SRGGB10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SBGGR10_1X10: + return MIPI_CSI2_DT_RAW10; + } +} + +static u32 vd55g1_get_fmt_code(struct vd55g1 *sensor, u32 code) +{ + unsigned int i, j; + + if (sensor->id == VD55G1_MODEL_ID_VD55G1) + return code; + + for (i = 0; i < ARRAY_SIZE(vd55g1_mbus_formats_bayer); i++) { + for (j = 0; j < ARRAY_SIZE(vd55g1_mbus_formats_bayer[i]); j++) { + if (vd55g1_mbus_formats_bayer[i][j] == code) + goto adapt_bayer_pattern; + } + } + dev_warn(sensor->dev, "Unsupported mbus format\n"); + + return code; + +adapt_bayer_pattern: + j = 0; + /* In first init_state() call, controls might not be initialized yet */ + if (sensor->hflip_ctrl && sensor->vflip_ctrl) { + j = (sensor->hflip_ctrl->val ? 1 : 0) + + (sensor->vflip_ctrl->val ? 2 : 0); + } + + return vd55g1_mbus_formats_bayer[i][j]; +} + +static s32 vd55g1_get_pixel_rate(struct vd55g1 *sensor, + struct v4l2_mbus_framefmt *format) +{ + return sensor->mipi_rate / vd55g1_get_fmt_bpp(format->code); +} + +static unsigned int vd55g1_get_hblank_min(struct vd55g1 *sensor, + struct v4l2_mbus_framefmt *format, + struct v4l2_rect *crop) +{ + u32 mipi_req_line_time; + u32 mipi_req_line_length; + u32 min_line_length; + + /* MIPI required time */ + mipi_req_line_time = (crop->width * + vd55g1_get_fmt_bpp(format->code) + + VD55G1_MIPI_MARGIN) / + (sensor->mipi_rate / MEGA); + mipi_req_line_length = mipi_req_line_time * sensor->pixel_clock / + HZ_PER_MHZ; + + /* Absolute time required for ADCs to convert pixels */ + min_line_length = VD55G1_LINE_LENGTH_MIN; + if (sensor->hdr_ctrl->val == VD55G1_HDR_SUB) + min_line_length = VD55G1_LINE_LENGTH_SUB_MIN; + + /* Respect both constraint */ + min_line_length = max(min_line_length, mipi_req_line_length); + + return min_line_length - crop->width; +} + +static void vd55g1_get_vblank_limits(struct vd55g1 *sensor, + struct v4l2_rect *crop, + struct vd55g1_vblank_limits *limits) +{ + limits->min = VD55G1_VBLANK_MIN; + limits->def = VD55G1_FRAME_LENGTH_DEF - crop->height; + limits->max = VD55G1_VBLANK_MAX - crop->height; +} + +#define vd55g1_read(sensor, reg, val, err) \ + cci_read((sensor)->regmap, reg, val, err) + +#define vd55g1_write(sensor, reg, val, err) \ + cci_write((sensor)->regmap, reg, val, err) + +static int vd55g1_write_array(struct vd55g1 *sensor, u32 reg, unsigned int len, + const u8 *array, int *err) +{ + unsigned int chunk_sz = 1024; + unsigned int sz; + int ret = 0; + + if (err && *err) + return *err; + + /* + * This loop isn't necessary but in certains conditions (platforms, cpu + * load, etc.) it has been observed that the bulk write could timeout. + */ + while (len) { + sz = min(len, chunk_sz); + ret = regmap_bulk_write(sensor->regmap, reg, array, sz); + if (ret < 0) + goto out; + len -= sz; + reg += sz; + array += sz; + } + +out: + if (ret && err) + *err = ret; + + return ret; +} + +static int vd55g1_poll_reg(struct vd55g1 *sensor, u32 reg, u8 poll_val, + int *err) +{ + unsigned int val = 0; + int ret; + + if (err && *err) + return *err; + + ret = regmap_read_poll_timeout(sensor->regmap, CCI_REG_ADDR(reg), val, + (val == poll_val), 2000, + 500 * USEC_PER_MSEC); + + if (ret && err) + *err = ret; + + return ret; +} + +static int vd55g1_wait_state(struct vd55g1 *sensor, int state, int *err) +{ + return vd55g1_poll_reg(sensor, VD55G1_REG_SYSTEM_FSM, state, err); +} + +static int vd55g1_prepare_clock_tree(struct vd55g1 *sensor) +{ + u32 sys_clk, mipi_div, pixel_div; + + if (sensor->xclk_freq < VD55G1_XCLK_FREQ_MIN || + sensor->xclk_freq > VD55G1_XCLK_FREQ_MAX) { + dev_err(sensor->dev, + "Only %luMhz-%luMhz clock range supported. Provided %lu MHz\n", + VD55G1_XCLK_FREQ_MIN / HZ_PER_MHZ, + VD55G1_XCLK_FREQ_MAX / HZ_PER_MHZ, + sensor->xclk_freq / HZ_PER_MHZ); + return -EINVAL; + } + + /* MIPI bus is double data rate */ + sensor->mipi_rate = sensor->link_freq * 2; + + if (sensor->mipi_rate < VD55G1_MIPI_RATE_MIN || + sensor->mipi_rate > VD55G1_MIPI_RATE_MAX) { + dev_err(sensor->dev, + "Only %luMbps-%luMbps data rate range supported. Provided %lu Mbps\n", + VD55G1_MIPI_RATE_MIN / MEGA, + VD55G1_MIPI_RATE_MAX / MEGA, + sensor->mipi_rate / MEGA); + return -EINVAL; + } + + if (sensor->mipi_rate <= 300 * MEGA) + mipi_div = 4; + else if (sensor->mipi_rate <= 600 * MEGA) + mipi_div = 2; + else + mipi_div = 1; + + sys_clk = sensor->mipi_rate * mipi_div; + + if (sys_clk <= 780 * HZ_PER_MHZ) + pixel_div = 5; + else if (sys_clk <= 900 * HZ_PER_MHZ) + pixel_div = 6; + else + pixel_div = 8; + + sensor->pixel_clock = sys_clk / pixel_div; + + return 0; +} + +static int vd55g1_update_patgen(struct vd55g1 *sensor, u32 patgen_index) +{ + static const u8 index2val[] = { + 0x0, 0x22, 0x28 + }; + u32 pattern = index2val[patgen_index]; + u32 reg = pattern << VD55G1_PATGEN_TYPE_SHIFT; + u8 duster = VD55G1_DUSTER_RING_ENABLE | VD55G1_DUSTER_DYN_ENABLE | + VD55G1_DUSTER_ENABLE; + int ret = 0; + + BUILD_BUG_ON(ARRAY_SIZE(index2val) != ARRAY_SIZE(vd55g1_tp_menu)); + + if (pattern != 0) { + reg |= VD55G1_PATGEN_ENABLE; + /* Take care of duster to not mess up the test pattern output */ + duster = VD55G1_DUSTER_DISABLE; + } + + vd55g1_write(sensor, VD55G1_REG_DUSTER_CTRL, duster, &ret); + vd55g1_write(sensor, VD55G1_REG_PATGEN_CTRL, reg, &ret); + + return ret; +} + +static int vd55g1_update_expo_cluster(struct vd55g1 *sensor, bool is_auto) +{ + enum vd55g1_expo_state expo_state = is_auto ? VD55G1_EXP_AUTO : + VD55G1_EXP_MANUAL; + int ret = 0; + + if (sensor->ae_ctrl->is_new) + vd55g1_write(sensor, VD55G1_REG_EXP_MODE(0), expo_state, &ret); + + if (sensor->hdr_ctrl->val == VD55G1_HDR_SUB && + sensor->hdr_ctrl->is_new) { + vd55g1_write(sensor, VD55G1_REG_EXP_MODE(1), VD55G1_EXP_BYPASS, + &ret); + if (ret) + return ret; + } + + if (!is_auto && sensor->expo_ctrl->is_new) + vd55g1_write(sensor, VD55G1_REG_MANUAL_COARSE_EXPOSURE, + sensor->expo_ctrl->val, &ret); + + if (!is_auto && sensor->again_ctrl->is_new) + vd55g1_write(sensor, VD55G1_REG_MANUAL_ANALOG_GAIN, + sensor->again_ctrl->val, &ret); + + if (!is_auto && sensor->dgain_ctrl->is_new) + vd55g1_write(sensor, VD55G1_REG_MANUAL_DIGITAL_GAIN, + sensor->dgain_ctrl->val, &ret); + + return ret; +} + +static int vd55g1_lock_exposure(struct vd55g1 *sensor, u32 lock_val) +{ + bool ae_lock = lock_val & V4L2_LOCK_EXPOSURE; + enum vd55g1_expo_state expo_state = ae_lock ? VD55G1_EXP_FREEZE : + VD55G1_EXP_AUTO; + int ret = 0; + + if (sensor->ae_ctrl->val == V4L2_EXPOSURE_AUTO) + vd55g1_write(sensor, VD55G1_REG_EXP_MODE(0), expo_state, &ret); + + return ret; +} + +static int vd55g1_read_expo_cluster(struct vd55g1 *sensor) +{ + u64 exposure = 0; + u64 again = 0; + u64 dgain = 0; + int ret = 0; + + vd55g1_read(sensor, VD55G1_REG_APPLIED_COARSE_EXPOSURE, &exposure, + &ret); + vd55g1_read(sensor, VD55G1_REG_APPLIED_ANALOG_GAIN, &again, &ret); + vd55g1_read(sensor, VD55G1_REG_APPLIED_DIGITAL_GAIN, &dgain, &ret); + if (ret) + return ret; + + sensor->expo_ctrl->cur.val = exposure; + sensor->again_ctrl->cur.val = again; + sensor->dgain_ctrl->cur.val = dgain; + + return 0; +} + +static int vd55g1_update_frame_length(struct vd55g1 *sensor, + unsigned int frame_length) +{ + int ret = 0; + + if (sensor->hdr_ctrl->val == VD55G1_HDR_SUB) + vd55g1_write(sensor, VD55G1_REG_FRAME_LENGTH(1), frame_length, + &ret); + vd55g1_write(sensor, VD55G1_REG_FRAME_LENGTH(0), frame_length, &ret); + + return ret; +} + +static int vd55g1_update_exposure_target(struct vd55g1 *sensor, int index) +{ + /* + * Find auto exposure target with: default target exposure * 2^EV + * Defaut target exposure being 27 for the sensor. + */ + static const unsigned int index2exposure_target[] = { + 3, 5, 7, 10, 14, 19, 27, 38, 54, 76, 108, 153, 216, + }; + int exposure_target = index2exposure_target[index]; + + return vd55g1_write(sensor, VD55G1_REG_AE_TARGET_PERCENTAGE, + exposure_target, NULL); +} + +static int vd55g1_apply_cold_start(struct vd55g1 *sensor, + struct v4l2_rect *crop) +{ + /* + * Cold start register is a single register expressed as exposure time + * in us. This differ from status registers being a combination of + * exposure, digital gain, and analog gain, requiring the following + * format conversion. + */ + unsigned int line_length = crop->width + sensor->hblank_ctrl->val; + unsigned int line_time_us = DIV_ROUND_UP(line_length * MEGA, + sensor->pixel_clock); + u8 d_gain = DIV_ROUND_CLOSEST(sensor->dgain_ctrl->val, 1 << 8); + u8 a_gain = DIV_ROUND_CLOSEST(32, (32 - sensor->again_ctrl->val)); + unsigned int expo_us = sensor->expo_ctrl->val * d_gain * a_gain * + line_time_us; + int ret = 0; + + vd55g1_write(sensor, VD55G1_REG_AE_FORCE_COLDSTART, 1, &ret); + vd55g1_write(sensor, VD55G1_REG_AE_COLDSTART_EXP_TIME, expo_us, &ret); + + return ret; +} + +static void vd55g1_update_pad_fmt(struct vd55g1 *sensor, + const struct vd55g1_mode *mode, u32 code, + struct v4l2_mbus_framefmt *fmt) +{ + fmt->code = vd55g1_get_fmt_code(sensor, code); + fmt->width = mode->width; + fmt->height = mode->height; + fmt->colorspace = V4L2_COLORSPACE_RAW; + fmt->field = V4L2_FIELD_NONE; + fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + fmt->quantization = V4L2_QUANTIZATION_DEFAULT; + fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT; +} + +static int vd55g1_update_hdr_mode(struct vd55g1 *sensor) +{ + int ret = 0; + + switch (sensor->hdr_ctrl->val) { + case VD55G1_NO_HDR: + vd55g1_write(sensor, VD55G1_REG_EXPOSURE_MAX_COARSE, + VD55G1_EXPOSURE_MAX_COARSE_DEF, &ret); + vd55g1_write(sensor, VD55G1_REG_EXPOSURE_USE_CASES, 0, &ret); + vd55g1_write(sensor, VD55G1_REG_NEXT_CTX, 0x0, &ret); + + vd55g1_write(sensor, VD55G1_REG_CTX_REPEAT_COUNT_CTX0, 0, &ret); + + vd55g1_write(sensor, VD55G1_REG_VT_MODE(0), + VD55G1_VT_MODE_NORMAL, &ret); + vd55g1_write(sensor, VD55G1_REG_MASK_FRAME_CTRL(0), + VD55G1_MASK_FRAME_CTRL_OUTPUT, &ret); + break; + case VD55G1_HDR_SUB: + vd55g1_write(sensor, VD55G1_REG_EXPOSURE_MAX_COARSE, + VD55G1_EXPOSURE_MAX_COARSE_SUB, &ret); + vd55g1_write(sensor, VD55G1_REG_EXPOSURE_USE_CASES, + VD55G1_EXPOSURE_USE_CASES_MULTI_CONTEXT, &ret); + vd55g1_write(sensor, VD55G1_REG_NEXT_CTX, 0x0001, &ret); + + vd55g1_write(sensor, VD55G1_REG_CTX_REPEAT_COUNT_CTX0, 1, &ret); + vd55g1_write(sensor, VD55G1_REG_CTX_REPEAT_COUNT_CTX1, 1, &ret); + + vd55g1_write(sensor, VD55G1_REG_VT_MODE(0), + VD55G1_VT_MODE_NORMAL, &ret); + vd55g1_write(sensor, VD55G1_REG_MASK_FRAME_CTRL(0), + VD55G1_MASK_FRAME_CTRL_MASK, &ret); + vd55g1_write(sensor, VD55G1_REG_EXPOSURE_INSTANCE(0), 0, &ret); + vd55g1_write(sensor, VD55G1_REG_VT_MODE(1), + VD55G1_VT_MODE_SUBTRACTION, &ret); + vd55g1_write(sensor, VD55G1_REG_MASK_FRAME_CTRL(1), + VD55G1_MASK_FRAME_CTRL_OUTPUT, &ret); + vd55g1_write(sensor, VD55G1_REG_EXPOSURE_INSTANCE(1), 1, &ret); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int vd55g1_set_framefmt(struct vd55g1 *sensor, + struct v4l2_mbus_framefmt *format, + struct v4l2_rect *crop) +{ + u8 binning; + int ret = 0; + + vd55g1_write(sensor, VD55G1_REG_FORMAT_CTRL, + vd55g1_get_fmt_bpp(format->code), &ret); + vd55g1_write(sensor, VD55G1_REG_OIF_IMG_CTRL, + vd55g1_get_fmt_data_type(format->code), &ret); + + switch (crop->width / format->width) { + case 1: + default: + binning = VD55G1_READOUT_CTRL_BIN_MODE_NORMAL; + break; + case 2: + binning = VD55G1_READOUT_CTRL_BIN_MODE_DIGITAL_X2; + break; + } + vd55g1_write(sensor, VD55G1_REG_READOUT_CTRL, binning, &ret); + + vd55g1_write(sensor, VD55G1_REG_X_START(0), crop->left, &ret); + vd55g1_write(sensor, VD55G1_REG_X_WIDTH(0), crop->width, &ret); + vd55g1_write(sensor, VD55G1_REG_Y_START(0), crop->top, &ret); + vd55g1_write(sensor, VD55G1_REG_Y_HEIGHT(0), crop->height, &ret); + + vd55g1_write(sensor, VD55G1_REG_X_START(1), crop->left, &ret); + vd55g1_write(sensor, VD55G1_REG_X_WIDTH(1), crop->width, &ret); + vd55g1_write(sensor, VD55G1_REG_Y_START(1), crop->top, &ret); + vd55g1_write(sensor, VD55G1_REG_Y_HEIGHT(1), crop->height, &ret); + + return ret; +} + +static int vd55g1_update_gpios(struct vd55g1 *sensor, unsigned long gpio_mask) +{ + unsigned long io; + u8 gpio_val; + int ret = 0; + + for_each_set_bit(io, &gpio_mask, VD55G1_NB_GPIOS) { + gpio_val = sensor->gpios[io]; + + if (gpio_val == VD55G1_GPIO_MODE_STROBE && + sensor->led_ctrl->val == V4L2_FLASH_LED_MODE_NONE) { + gpio_val = VD55G1_GPIO_MODE_IN; + if (sensor->hdr_ctrl->val == VD55G1_HDR_SUB) { + /* Make its context 1 counterpart strobe too */ + vd55g1_write(sensor, + VD55G1_REG_GPIO_0_CTRL(1) + io, + gpio_val, &ret); + } + } + + ret = vd55g1_write(sensor, VD55G1_REG_GPIO_0_CTRL(0) + io, + gpio_val, &ret); + } + + return ret; +} + +static int vd55g1_ro_ctrls_setup(struct vd55g1 *sensor, struct v4l2_rect *crop) +{ + return vd55g1_write(sensor, VD55G1_REG_LINE_LENGTH, + crop->width + sensor->hblank_ctrl->val, NULL); +} + +static void vd55g1_grab_ctrls(struct vd55g1 *sensor, bool enable) +{ + /* These settings cannot change during stream */ + v4l2_ctrl_grab(sensor->hflip_ctrl, enable); + v4l2_ctrl_grab(sensor->vflip_ctrl, enable); + v4l2_ctrl_grab(sensor->patgen_ctrl, enable); + v4l2_ctrl_grab(sensor->hdr_ctrl, enable); +} + +static int vd55g1_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct vd55g1 *sensor = to_vd55g1(sd); + struct v4l2_rect *crop = + v4l2_subdev_state_get_crop(state, 0); + struct v4l2_mbus_framefmt *format = + v4l2_subdev_state_get_format(state, 0); + int ret; + + ret = pm_runtime_resume_and_get(sensor->dev); + if (ret < 0) + return ret; + + /* Configure output */ + vd55g1_write(sensor, VD55G1_REG_MIPI_DATA_RATE, + sensor->mipi_rate, &ret); + vd55g1_write(sensor, VD55G1_REG_OIF_CTRL, sensor->oif_ctrl, &ret); + vd55g1_write(sensor, VD55G1_REG_ISL_ENABLE, 0, &ret); + if (ret) + goto err_rpm_put; + + ret = vd55g1_set_framefmt(sensor, format, crop); + if (ret) + goto err_rpm_put; + + /* Setup default GPIO values; could be overridden by V4L2 ctrl setup */ + ret = vd55g1_update_gpios(sensor, GENMASK(VD55G1_NB_GPIOS - 1, 0)); + if (ret) + goto err_rpm_put; + + ret = vd55g1_apply_cold_start(sensor, crop); + if (ret) + goto err_rpm_put; + + /* Apply settings from V4L2 ctrls */ + ret = __v4l2_ctrl_handler_setup(&sensor->ctrl_handler); + if (ret) + goto err_rpm_put; + + /* Also apply settings from read-only V4L2 ctrls */ + ret = vd55g1_ro_ctrls_setup(sensor, crop); + if (ret) + goto err_rpm_put; + + /* Start streaming */ + vd55g1_write(sensor, VD55G1_REG_STBY, VD55G1_STBY_START_STREAM, &ret); + vd55g1_poll_reg(sensor, VD55G1_REG_STBY, 0, &ret); + vd55g1_wait_state(sensor, VD55G1_SYSTEM_FSM_STREAMING, &ret); + if (ret) + goto err_rpm_put; + + vd55g1_grab_ctrls(sensor, true); + + return 0; + +err_rpm_put: + pm_runtime_put(sensor->dev); + return -EINVAL; +} + +static int vd55g1_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct vd55g1 *sensor = to_vd55g1(sd); + int ret = 0; + + /* Retrieve Expo cluster to enable coldstart of AE */ + ret = vd55g1_read_expo_cluster(sensor); + + vd55g1_write(sensor, VD55G1_REG_STREAMING, VD55G1_STREAMING_STOP_STREAM, + &ret); + vd55g1_poll_reg(sensor, VD55G1_REG_STREAMING, 0, &ret); + vd55g1_wait_state(sensor, VD55G1_SYSTEM_FSM_SW_STBY, &ret); + + if (ret) + dev_warn(sensor->dev, "Can't disable stream\n"); + + vd55g1_grab_ctrls(sensor, false); + + pm_runtime_put_autosuspend(sensor->dev); + + return ret; +} + +static int vd55g1_patch(struct vd55g1 *sensor) +{ + u64 patch; + int ret = 0; + + /* vd55g1 needs a patch while vd65g4 does not */ + if (sensor->id == VD55G1_MODEL_ID_VD55G1) { + vd55g1_write_array(sensor, VD55G1_REG_FWPATCH_START_ADDR, + sizeof(vd55g1_patch_array), + vd55g1_patch_array, &ret); + vd55g1_write(sensor, VD55G1_REG_BOOT, + VD55G1_BOOT_PATCH_AND_BOOT, &ret); + vd55g1_poll_reg(sensor, VD55G1_REG_BOOT, 0, &ret); + if (ret) { + dev_err(sensor->dev, "Failed to apply patch\n"); + return ret; + } + + vd55g1_read(sensor, VD55G1_REG_FWPATCH_REVISION, &patch, &ret); + if (patch != (VD55G1_FWPATCH_REVISION_MAJOR << 8) + + VD55G1_FWPATCH_REVISION_MINOR) { + dev_err(sensor->dev, "Bad patch version expected %d.%d got %d.%d\n", + VD55G1_FWPATCH_REVISION_MAJOR, + VD55G1_FWPATCH_REVISION_MINOR, + (u8)(patch >> 8), (u8)(patch & 0xff)); + return -ENODEV; + } + dev_dbg(sensor->dev, "patch %d.%d applied\n", + (u8)(patch >> 8), (u8)(patch & 0xff)); + + } else { + vd55g1_write(sensor, VD55G1_REG_BOOT, VD55G1_BOOT_BOOT, &ret); + vd55g1_poll_reg(sensor, VD55G1_REG_BOOT, 0, &ret); + if (ret) { + dev_err(sensor->dev, "Failed to boot\n"); + return ret; + } + } + + ret = vd55g1_wait_state(sensor, VD55G1_SYSTEM_FSM_SW_STBY, NULL); + if (ret) { + dev_err(sensor->dev, "Sensor waiting after boot failed\n"); + return ret; + } + + return 0; +} + +static int vd55g1_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + const struct v4l2_rect *crop = v4l2_subdev_state_get_crop(sd_state, 0); + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = *crop; + return 0; + case V4L2_SEL_TGT_NATIVE_SIZE: + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = VD55G1_WIDTH; + sel->r.height = VD55G1_HEIGHT; + return 0; + } + + return -EINVAL; +} + +static int vd55g1_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct vd55g1 *sensor = to_vd55g1(sd); + u32 base_code; + + if (sensor->id == VD55G1_MODEL_ID_VD55G1) { + if (code->index >= ARRAY_SIZE(vd55g1_mbus_formats_mono)) + return -EINVAL; + base_code = vd55g1_mbus_formats_mono[code->index]; + } else { + if (code->index >= ARRAY_SIZE(vd55g1_mbus_formats_bayer)) + return -EINVAL; + base_code = vd55g1_mbus_formats_bayer[code->index][0]; + } + code->code = vd55g1_get_fmt_code(sensor, base_code); + + return 0; +} + +static int vd55g1_new_format_change_controls(struct vd55g1 *sensor, + struct v4l2_mbus_framefmt *format, + struct v4l2_rect *crop) +{ + struct vd55g1_vblank_limits vblank; + unsigned int hblank; + unsigned int frame_length = 0; + unsigned int expo_max; + int ret; + + /* Reset vblank and frame length to default */ + vd55g1_get_vblank_limits(sensor, crop, &vblank); + ret = __v4l2_ctrl_modify_range(sensor->vblank_ctrl, vblank.min, + vblank.max, 1, vblank.def); + if (ret) + return ret; + + /* Max exposure changes with vblank */ + frame_length = crop->height + sensor->vblank_ctrl->val; + expo_max = frame_length - VD55G1_EXPO_MAX_TERM; + ret = __v4l2_ctrl_modify_range(sensor->expo_ctrl, 0, expo_max, 1, + VD55G1_EXPO_DEF); + if (ret) + return ret; + + /* Update pixel rate to reflect new bpp */ + ret = __v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate_ctrl, + vd55g1_get_pixel_rate(sensor, format)); + if (ret) + return ret; + + /* Update hblank according to new width */ + hblank = vd55g1_get_hblank_min(sensor, format, crop); + ret = __v4l2_ctrl_modify_range(sensor->hblank_ctrl, hblank, hblank, 1, + hblank); + + return ret; +} + +static int vd55g1_set_pad_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *sd_fmt) +{ + struct vd55g1 *sensor = to_vd55g1(sd); + const struct vd55g1_mode *new_mode; + struct v4l2_mbus_framefmt *format; + struct v4l2_rect pad_crop; + unsigned int binning; + + new_mode = v4l2_find_nearest_size(vd55g1_supported_modes, + ARRAY_SIZE(vd55g1_supported_modes), + width, height, sd_fmt->format.width, + sd_fmt->format.height); + + vd55g1_update_pad_fmt(sensor, new_mode, sd_fmt->format.code, + &sd_fmt->format); + + /* + * Use binning to maximize the crop rectangle size, and centre it in the + * sensor. + */ + binning = min(VD55G1_WIDTH / sd_fmt->format.width, + VD55G1_HEIGHT / sd_fmt->format.height); + binning = min(binning, 2U); + pad_crop.width = sd_fmt->format.width * binning; + pad_crop.height = sd_fmt->format.height * binning; + pad_crop.left = (VD55G1_WIDTH - pad_crop.width) / 2; + pad_crop.top = (VD55G1_HEIGHT - pad_crop.height) / 2; + + format = v4l2_subdev_state_get_format(sd_state, sd_fmt->pad); + + *format = sd_fmt->format; + + *v4l2_subdev_state_get_crop(sd_state, sd_fmt->pad) = pad_crop; + if (sd_fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + return vd55g1_new_format_change_controls(sensor, + &sd_fmt->format, + &pad_crop); + + return 0; +} + +static int vd55g1_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + struct vd55g1 *sensor = to_vd55g1(sd); + struct v4l2_subdev_format fmt = { 0 }; + struct v4l2_subdev_route routes[] = { + { .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE } + }; + struct v4l2_subdev_krouting routing = { + .num_routes = ARRAY_SIZE(routes), + .routes = routes, + }; + int ret; + + /* Needed by v4l2_subdev_s_stream_helper(), even with 1 stream only */ + ret = v4l2_subdev_set_routing(sd, sd_state, &routing); + if (ret) + return ret; + + vd55g1_update_pad_fmt(sensor, &vd55g1_supported_modes[VD55G1_MODE_DEF], + vd55g1_get_fmt_code(sensor, VD55G1_MBUS_CODE_DEF), + &fmt.format); + + return vd55g1_set_pad_fmt(sd, sd_state, &fmt); +} + +static int vd55g1_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct vd55g1 *sensor = to_vd55g1(sd); + u32 code; + + if (fse->index >= ARRAY_SIZE(vd55g1_supported_modes)) + return -EINVAL; + + code = vd55g1_get_fmt_code(sensor, fse->code); + if (fse->code != code) + return -EINVAL; + + fse->min_width = vd55g1_supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = vd55g1_supported_modes[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static const struct v4l2_subdev_internal_ops vd55g1_internal_ops = { + .init_state = vd55g1_init_state, +}; + +static const struct v4l2_subdev_pad_ops vd55g1_pad_ops = { + .enum_mbus_code = vd55g1_enum_mbus_code, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = vd55g1_set_pad_fmt, + .get_selection = vd55g1_get_selection, + .enum_frame_size = vd55g1_enum_frame_size, + .enable_streams = vd55g1_enable_streams, + .disable_streams = vd55g1_disable_streams, +}; + +static const struct v4l2_subdev_video_ops vd55g1_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_ops vd55g1_subdev_ops = { + .video = &vd55g1_video_ops, + .pad = &vd55g1_pad_ops, +}; + +static int vd55g1_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vd55g1 *sensor = ctrl_to_vd55g1(ctrl); + int ret = 0; + + /* Interact with HW only when it is powered ON */ + if (!pm_runtime_get_if_in_use(sensor->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE_AUTO: + ret = vd55g1_read_expo_cluster(sensor); + break; + default: + ret = -EINVAL; + break; + } + + pm_runtime_put_autosuspend(sensor->dev); + + return ret; +} + +static int vd55g1_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vd55g1 *sensor = ctrl_to_vd55g1(ctrl); + unsigned int frame_length = 0; + unsigned int expo_max; + struct v4l2_subdev_state *state = + v4l2_subdev_get_locked_active_state(&sensor->sd); + struct v4l2_rect *crop = + v4l2_subdev_state_get_crop(state, 0); + struct v4l2_mbus_framefmt *format = + v4l2_subdev_state_get_format(state, 0); + unsigned int hblank = vd55g1_get_hblank_min(sensor, format, crop); + bool is_auto = false; + int ret = 0; + + if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY) + return 0; + + /* Update controls state, range, etc. whatever the state of the HW */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + frame_length = crop->height + ctrl->val; + expo_max = frame_length - VD55G1_EXPO_MAX_TERM; + ret = __v4l2_ctrl_modify_range(sensor->expo_ctrl, 0, expo_max, + 1, VD55G1_EXPO_DEF); + break; + case V4L2_CID_EXPOSURE_AUTO: + is_auto = (ctrl->val == V4L2_EXPOSURE_AUTO); + __v4l2_ctrl_grab(sensor->ae_lock_ctrl, !is_auto); + __v4l2_ctrl_grab(sensor->ae_bias_ctrl, !is_auto); + break; + case V4L2_CID_HDR_SENSOR_MODE: + /* Discriminate if the userspace changed the control value */ + if (ctrl->val != ctrl->cur.val) { + /* Max horizontal blanking changes with hdr mode */ + ret = __v4l2_ctrl_modify_range(sensor->hblank_ctrl, + hblank, hblank, 1, + hblank); + } + break; + default: + break; + } + + /* Don't modify hardware if controls modification failed */ + if (ret) + return ret; + + /* Interact with HW only when it is powered ON */ + if (!pm_runtime_get_if_in_use(sensor->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_HFLIP: + ret = vd55g1_write(sensor, VD55G1_REG_ORIENTATION, + sensor->hflip_ctrl->val | + (sensor->vflip_ctrl->val << 1), + NULL); + break; + case V4L2_CID_TEST_PATTERN: + ret = vd55g1_update_patgen(sensor, ctrl->val); + break; + case V4L2_CID_EXPOSURE_AUTO: + ret = vd55g1_update_expo_cluster(sensor, is_auto); + break; + case V4L2_CID_3A_LOCK: + ret = vd55g1_lock_exposure(sensor, ctrl->val); + break; + case V4L2_CID_AUTO_EXPOSURE_BIAS: + /* + * We use auto exposure target percentage register to control + * exposure bias for more precision. + */ + ret = vd55g1_update_exposure_target(sensor, ctrl->val); + break; + case V4L2_CID_VBLANK: + ret = vd55g1_update_frame_length(sensor, frame_length); + break; + case V4L2_CID_FLASH_LED_MODE: + ret = vd55g1_update_gpios(sensor, sensor->ext_leds_mask); + break; + case V4L2_CID_HDR_SENSOR_MODE: + ret = vd55g1_update_hdr_mode(sensor); + break; + default: + ret = -EINVAL; + break; + } + + pm_runtime_put_autosuspend(sensor->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops vd55g1_ctrl_ops = { + .g_volatile_ctrl = vd55g1_g_volatile_ctrl, + .s_ctrl = vd55g1_s_ctrl, +}; + +static int vd55g1_init_ctrls(struct vd55g1 *sensor) +{ + const struct v4l2_ctrl_ops *ops = &vd55g1_ctrl_ops; + struct v4l2_ctrl_handler *hdl = &sensor->ctrl_handler; + struct v4l2_ctrl *ctrl; + struct v4l2_fwnode_device_properties fwnode_props; + struct vd55g1_vblank_limits vblank; + unsigned int hblank; + struct v4l2_subdev_state *state = + v4l2_subdev_lock_and_get_active_state(&sensor->sd); + struct v4l2_rect *crop = + v4l2_subdev_state_get_crop(state, 0); + struct v4l2_mbus_framefmt *format = + v4l2_subdev_state_get_format(state, 0); + s32 pixel_rate = vd55g1_get_pixel_rate(sensor, format); + int ret; + + v4l2_ctrl_handler_init(hdl, 16); + + /* Flip cluster */ + sensor->hflip_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, + 0, 1, 1, 0); + if (sensor->hflip_ctrl) + sensor->hflip_ctrl->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + sensor->vflip_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, + 0, 1, 1, 0); + if (sensor->vflip_ctrl) + sensor->vflip_ctrl->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + v4l2_ctrl_cluster(2, &sensor->hflip_ctrl); + + /* Exposition cluster */ + sensor->ae_ctrl = v4l2_ctrl_new_std_menu(hdl, ops, + V4L2_CID_EXPOSURE_AUTO, 1, + ~0x3, V4L2_EXPOSURE_AUTO); + sensor->again_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN, + 0, 0x1c, 1, VD55G1_AGAIN_DEF); + sensor->dgain_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_DIGITAL_GAIN, + 256, 0xffff, 1, + VD55G1_DGAIN_DEF); + sensor->expo_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, 0, + VD55G1_FRAME_LENGTH_DEF - + VD55G1_EXPO_MAX_TERM, + 1, VD55G1_EXPO_DEF); + v4l2_ctrl_auto_cluster(4, &sensor->ae_ctrl, V4L2_EXPOSURE_MANUAL, true); + + sensor->patgen_ctrl = + v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(vd55g1_tp_menu) - 1, 0, + 0, vd55g1_tp_menu); + ctrl = v4l2_ctrl_new_int_menu(hdl, ops, V4L2_CID_LINK_FREQ, + 0, 0, &sensor->link_freq); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + sensor->pixel_rate_ctrl = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_PIXEL_RATE, 1, + INT_MAX, 1, + pixel_rate); + if (sensor->pixel_rate_ctrl) + sensor->pixel_rate_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + sensor->ae_lock_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_3A_LOCK, + 0, 1, 0, 0); + sensor->ae_bias_ctrl = + v4l2_ctrl_new_int_menu(hdl, ops, + V4L2_CID_AUTO_EXPOSURE_BIAS, + ARRAY_SIZE(vd55g1_ev_bias_menu) - 1, + ARRAY_SIZE(vd55g1_ev_bias_menu) / 2, + vd55g1_ev_bias_menu); + sensor->hdr_ctrl = + v4l2_ctrl_new_std_menu_items(hdl, ops, + V4L2_CID_HDR_SENSOR_MODE, + ARRAY_SIZE(vd55g1_hdr_menu) - 1, 0, + VD55G1_NO_HDR, vd55g1_hdr_menu); + hblank = vd55g1_get_hblank_min(sensor, format, crop); + sensor->hblank_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HBLANK, + hblank, hblank, 1, hblank); + if (sensor->hblank_ctrl) + sensor->hblank_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + vd55g1_get_vblank_limits(sensor, crop, &vblank); + sensor->vblank_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK, + vblank.min, vblank.max, + 1, vblank.def); + + /* Additional controls based on device tree properties */ + if (sensor->ext_leds_mask) { + sensor->led_ctrl = + v4l2_ctrl_new_std_menu(hdl, ops, + V4L2_CID_FLASH_LED_MODE, + V4L2_FLASH_LED_MODE_FLASH, 0, + V4L2_FLASH_LED_MODE_NONE); + } + + ret = v4l2_fwnode_device_parse(sensor->dev, &fwnode_props); + if (ret) + goto free_ctrls; + + ret = v4l2_ctrl_new_fwnode_properties(hdl, ops, &fwnode_props); + if (ret) + goto free_ctrls; + + sensor->sd.ctrl_handler = hdl; + goto unlock_state; + +free_ctrls: + v4l2_ctrl_handler_free(hdl); +unlock_state: + v4l2_subdev_unlock_state(state); + return ret; +} + +static int vd55g1_detect(struct vd55g1 *sensor) +{ + unsigned int dt_id = (uintptr_t)device_get_match_data(sensor->dev); + u64 rev, id; + int ret; + + ret = vd55g1_read(sensor, VD55G1_REG_MODEL_ID, &id, NULL); + if (ret) + return ret; + + if (id != VD55G1_MODEL_ID_VD55G1 && id != VD55G1_MODEL_ID_VD65G4) { + dev_warn(sensor->dev, "Unsupported sensor id 0x%x\n", + (u32)id); + return -ENODEV; + } + if (id != dt_id) { + dev_err(sensor->dev, "Probed sensor %s and device tree definition (%s) mismatch", + VD55G1_MODEL_ID_NAME(id), VD55G1_MODEL_ID_NAME(dt_id)); + return -ENODEV; + } + sensor->id = id; + + ret = vd55g1_read(sensor, VD55G1_REG_REVISION, &rev, NULL); + if (ret) + return ret; + + if ((id == VD55G1_MODEL_ID_VD55G1 && rev != VD55G1_REVISION_CCB) && + (id == VD55G1_MODEL_ID_VD65G4 && rev != VD55G1_REVISION_BAYER)) { + dev_err(sensor->dev, "Unsupported sensor revision 0x%x for sensor %s\n", + (u16)rev, VD55G1_MODEL_ID_NAME(id)); + return -ENODEV; + } + + return 0; +} + +static int vd55g1_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct vd55g1 *sensor = to_vd55g1(sd); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(vd55g1_supply_name), + sensor->supplies); + if (ret) { + dev_err(dev, "Failed to enable regulators %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(sensor->xclk); + if (ret) { + dev_err(dev, "Failed to enable clock %d\n", ret); + goto disable_bulk; + } + + gpiod_set_value_cansleep(sensor->reset_gpio, 0); + usleep_range(5000, 10000); + ret = vd55g1_wait_state(sensor, VD55G1_SYSTEM_FSM_READY_TO_BOOT, NULL); + if (ret) { + dev_err(dev, "Sensor reset failed %d\n", ret); + goto disable_clock; + } + + ret = vd55g1_detect(sensor); + if (ret) { + dev_err(dev, "Sensor detect failed %d\n", ret); + goto disable_clock; + } + + /* Setup clock now to advance through system FSM states */ + vd55g1_write(sensor, VD55G1_REG_EXT_CLOCK, sensor->xclk_freq, &ret); + + ret = vd55g1_patch(sensor); + if (ret) { + dev_err(dev, "Sensor patch failed %d\n", ret); + goto disable_clock; + } + + return 0; + +disable_clock: + gpiod_set_value_cansleep(sensor->reset_gpio, 1); + clk_disable_unprepare(sensor->xclk); +disable_bulk: + regulator_bulk_disable(ARRAY_SIZE(vd55g1_supply_name), + sensor->supplies); + + return ret; +} + +static int vd55g1_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct vd55g1 *sensor = to_vd55g1(sd); + + gpiod_set_value_cansleep(sensor->reset_gpio, 1); + clk_disable_unprepare(sensor->xclk); + regulator_bulk_disable(ARRAY_SIZE(sensor->supplies), sensor->supplies); + + return 0; +} + +static int vd55g1_check_csi_conf(struct vd55g1 *sensor, + struct fwnode_handle *endpoint) +{ + struct v4l2_fwnode_endpoint ep = { .bus_type = V4L2_MBUS_CSI2_DPHY }; + u8 n_lanes; + int ret; + + ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep); + if (ret) + return -EINVAL; + + /* Check lanes number */ + n_lanes = ep.bus.mipi_csi2.num_data_lanes; + if (n_lanes != 1) { + dev_err(sensor->dev, "Sensor only supports 1 lane, found %d\n", + n_lanes); + ret = -EINVAL; + goto done; + } + + /* Clock lane must be first */ + if (ep.bus.mipi_csi2.clock_lane != 0) { + dev_err(sensor->dev, "Clock lane must be mapped to lane 0\n"); + ret = -EINVAL; + goto done; + } + + /* Handle polarities in sensor configuration */ + sensor->oif_ctrl = (ep.bus.mipi_csi2.lane_polarities[0] << 3) | + (ep.bus.mipi_csi2.lane_polarities[1] << 6); + + /* Check the link frequency set in device tree */ + if (!ep.nr_of_link_frequencies) { + dev_err(sensor->dev, "link-frequency property not found in DT\n"); + ret = -EINVAL; + goto done; + } + if (ep.nr_of_link_frequencies != 1) { + dev_err(sensor->dev, "Multiple link frequencies not supported\n"); + ret = -EINVAL; + goto done; + } + sensor->link_freq = ep.link_frequencies[0]; + +done: + v4l2_fwnode_endpoint_free(&ep); + + return ret; +} + +static int vd55g1_parse_dt_gpios_array(struct vd55g1 *sensor, + char *prop_name, u32 *array, int *nb) +{ + unsigned int i; + int ret; + + *nb = device_property_count_u32(sensor->dev, prop_name); + if (*nb == -EINVAL) { + /* Property not found */ + *nb = 0; + return 0; + } + + ret = device_property_read_u32_array(sensor->dev, + prop_name, array, *nb); + if (ret) { + dev_err(sensor->dev, "Failed to read %s prop\n", prop_name); + return ret; + } + for (i = 0; i < *nb; i++) { + if (array[i] >= VD55G1_NB_GPIOS) { + dev_err(sensor->dev, "Invalid GPIO number %d\n", + array[i]); + return -EINVAL; + } + } + + return 0; +} + +static int vd55g1_parse_dt_gpios(struct vd55g1 *sensor) +{ + u32 led_gpios[VD55G1_NB_GPIOS]; + int nb_gpios_leds; + unsigned int i; + int ret; + + /* Initialize GPIOs to default */ + for (i = 0; i < VD55G1_NB_GPIOS; i++) + sensor->gpios[i] = VD55G1_GPIO_MODE_IN; + sensor->ext_leds_mask = 0; + + /* Take into account optional 'st,leds' output for GPIOs */ + ret = vd55g1_parse_dt_gpios_array(sensor, "st,leds", led_gpios, + &nb_gpios_leds); + if (ret) + return ret; + + for (i = 0; i < nb_gpios_leds; i++) { + sensor->gpios[led_gpios[i]] = VD55G1_GPIO_MODE_STROBE; + set_bit(led_gpios[i], &sensor->ext_leds_mask); + } + + return 0; +} + +static int vd55g1_parse_dt(struct vd55g1 *sensor) +{ + struct fwnode_handle *endpoint; + int ret; + + endpoint = fwnode_graph_get_endpoint_by_id(dev_fwnode(sensor->dev), + 0, 0, 0); + if (!endpoint) { + dev_err(sensor->dev, "Endpoint node not found\n"); + return -EINVAL; + } + + ret = vd55g1_check_csi_conf(sensor, endpoint); + fwnode_handle_put(endpoint); + if (ret) + return ret; + + return vd55g1_parse_dt_gpios(sensor); +} + +static int vd55g1_subdev_init(struct vd55g1 *sensor) +{ + int ret; + + /* Init sub device */ + sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sensor->sd.internal_ops = &vd55g1_internal_ops; + + /* Init source pad */ + 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) { + dev_err(sensor->dev, "Failed to init media entity: %d\n", ret); + return ret; + } + + sensor->sd.state_lock = sensor->ctrl_handler.lock; + ret = v4l2_subdev_init_finalize(&sensor->sd); + if (ret) { + dev_err(sensor->dev, "Subdev init error: %d\n", ret); + goto err_ctrls; + } + + /* + * Initialize controls after v4l2_subdev_init_finalize() to make sure + * active state is set + */ + ret = vd55g1_init_ctrls(sensor); + if (ret) { + dev_err(sensor->dev, "Controls initialization failed %d\n", + ret); + goto err_media; + } + + return 0; + +err_ctrls: + v4l2_ctrl_handler_free(sensor->sd.ctrl_handler); + +err_media: + media_entity_cleanup(&sensor->sd.entity); + return ret; +} + +static void vd55g1_subdev_cleanup(struct vd55g1 *sensor) +{ + v4l2_async_unregister_subdev(&sensor->sd); + v4l2_subdev_cleanup(&sensor->sd); + media_entity_cleanup(&sensor->sd.entity); + v4l2_ctrl_handler_free(sensor->sd.ctrl_handler); +} + +static int vd55g1_get_regulators(struct vd55g1 *sensor) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(vd55g1_supply_name); i++) + sensor->supplies[i].supply = vd55g1_supply_name[i]; + + return devm_regulator_bulk_get(sensor->dev, + ARRAY_SIZE(vd55g1_supply_name), + sensor->supplies); +} + +static int vd55g1_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct vd55g1 *sensor; + int ret; + + sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); + if (!sensor) + return -ENOMEM; + sensor->dev = &client->dev; + + v4l2_i2c_subdev_init(&sensor->sd, client, &vd55g1_subdev_ops); + + ret = vd55g1_parse_dt(sensor); + if (ret) + return dev_err_probe(dev, ret, "Failed to parse Device Tree\n"); + + /* Get (and check) resources : power regs, ext clock, reset gpio */ + ret = vd55g1_get_regulators(sensor); + if (ret) + return dev_err_probe(dev, ret, "Failed to get regulators\n"); + + sensor->xclk = devm_v4l2_sensor_clk_get(dev, NULL); + if (IS_ERR(sensor->xclk)) + return dev_err_probe(dev, PTR_ERR(sensor->xclk), + "Failed to get xclk\n"); + + sensor->xclk_freq = clk_get_rate(sensor->xclk); + ret = vd55g1_prepare_clock_tree(sensor); + if (ret) + return ret; + + sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(sensor->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(sensor->reset_gpio), + "Failed to get reset gpio\n"); + + sensor->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(sensor->regmap)) + return dev_err_probe(dev, PTR_ERR(sensor->regmap), + "Failed to init regmap\n"); + + /* Detect if sensor is present and if its revision is supported */ + ret = vd55g1_power_on(dev); + if (ret) + return ret; + + /* Enable pm_runtime and power off the sensor */ + pm_runtime_set_active(dev); + pm_runtime_get_noresume(dev); + pm_runtime_enable(dev); + pm_runtime_set_autosuspend_delay(dev, 4000); + pm_runtime_use_autosuspend(dev); + pm_runtime_put_autosuspend(dev); + + ret = vd55g1_subdev_init(sensor); + if (ret) { + dev_err(dev, "V4l2 init failed: %d\n", ret); + goto err_power_off; + } + + ret = v4l2_async_register_subdev(&sensor->sd); + if (ret) { + dev_err(dev, "async subdev register failed %d\n", ret); + goto err_subdev; + } + + return 0; + +err_subdev: + vd55g1_subdev_cleanup(sensor); +err_power_off: + pm_runtime_disable(dev); + pm_runtime_put_noidle(dev); + pm_runtime_dont_use_autosuspend(dev); + vd55g1_power_off(dev); + + return ret; +} + +static void vd55g1_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct vd55g1 *sensor = to_vd55g1(sd); + + vd55g1_subdev_cleanup(sensor); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + vd55g1_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); + pm_runtime_dont_use_autosuspend(&client->dev); +} + +static const struct of_device_id vd55g1_dt_ids[] = { + { .compatible = "st,vd55g1", .data = (void *)VD55G1_MODEL_ID_VD55G1 }, + { .compatible = "st,vd65g4", .data = (void *)VD55G1_MODEL_ID_VD65G4 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, vd55g1_dt_ids); + +static const struct dev_pm_ops vd55g1_pm_ops = { + SET_RUNTIME_PM_OPS(vd55g1_power_off, vd55g1_power_on, NULL) +}; + +static struct i2c_driver vd55g1_i2c_driver = { + .driver = { + .name = "vd55g1", + .of_match_table = vd55g1_dt_ids, + .pm = &vd55g1_pm_ops, + }, + .probe = vd55g1_probe, + .remove = vd55g1_remove, +}; + +module_i2c_driver(vd55g1_i2c_driver); + +MODULE_AUTHOR("Benjamin Mugnier <benjamin.mugnier@foss.st.com>"); +MODULE_AUTHOR("Sylvain Petinot <sylvain.petinot@foss.st.com>"); +MODULE_DESCRIPTION("VD55G1 camera subdev driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/vd56g3.c b/drivers/media/i2c/vd56g3.c new file mode 100644 index 000000000000..157acea9e286 --- /dev/null +++ b/drivers/media/i2c/vd56g3.c @@ -0,0 +1,1582 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * A V4L2 driver for ST VD56G3 (Mono) and VD66GY (RGB) global shutter cameras. + * Copyright (C) 2024, STMicroelectronics SA + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/unaligned.h> +#include <linux/units.h> + +#include <media/mipi-csi2.h> +#include <media/v4l2-async.h> +#include <media/v4l2-cci.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-subdev.h> + +/* Register Map */ +#define VD56G3_REG_MODEL_ID CCI_REG16_LE(0x0000) +#define VD56G3_MODEL_ID 0x5603 +#define VD56G3_REG_REVISION CCI_REG16_LE(0x0002) +#define VD56G3_REVISION_CUT3 0x31 +#define VD56G3_REG_OPTICAL_REVISION CCI_REG8(0x001a) +#define VD56G3_OPTICAL_REVISION_MONO 0 +#define VD56G3_OPTICAL_REVISION_BAYER 1 +#define VD56G3_REG_SYSTEM_FSM CCI_REG8(0x0028) +#define VD56G3_SYSTEM_FSM_READY_TO_BOOT 0x01 +#define VD56G3_SYSTEM_FSM_SW_STBY 0x02 +#define VD56G3_SYSTEM_FSM_STREAMING 0x03 +#define VD56G3_REG_APPLIED_COARSE_EXPOSURE CCI_REG16_LE(0x0064) +#define VD56G3_REG_APPLIED_ANALOG_GAIN CCI_REG8(0x0068) +#define VD56G3_REG_APPLIED_DIGITAL_GAIN CCI_REG16_LE(0x006a) +#define VD56G3_REG_BOOT CCI_REG8(0x0200) +#define VD56G3_CMD_ACK 0 +#define VD56G3_CMD_BOOT 1 +#define VD56G3_REG_STBY CCI_REG8(0x0201) +#define VD56G3_CMD_START_STREAM 1 +#define VD56G3_REG_STREAMING CCI_REG8(0x0202) +#define VD56G3_CMD_STOP_STREAM 1 +#define VD56G3_REG_EXT_CLOCK CCI_REG32_LE(0x0220) +#define VD56G3_REG_CLK_PLL_PREDIV CCI_REG8(0x0224) +#define VD56G3_REG_CLK_SYS_PLL_MULT CCI_REG8(0x0226) +#define VD56G3_REG_ORIENTATION CCI_REG8(0x0302) +#define VD56G3_REG_FORMAT_CTRL CCI_REG8(0x030a) +#define VD56G3_REG_OIF_CTRL CCI_REG16_LE(0x030c) +#define VD56G3_REG_OIF_IMG_CTRL CCI_REG8(0x030f) +#define VD56G3_REG_OIF_CSI_BITRATE CCI_REG16_LE(0x0312) +#define VD56G3_REG_DUSTER_CTRL CCI_REG8(0x0318) +#define VD56G3_DUSTER_DISABLE 0 +#define VD56G3_DUSTER_ENABLE_DEF_MODULES 0x13 +#define VD56G3_REG_ISL_ENABLE CCI_REG8(0x0333) +#define VD56G3_REG_DARKCAL_CTRL CCI_REG8(0x0340) +#define VD56G3_DARKCAL_ENABLE 1 +#define VD56G3_DARKCAL_DISABLE_DARKAVG 2 +#define VD56G3_REG_PATGEN_CTRL CCI_REG16_LE(0x0400) +#define VD56G3_PATGEN_ENABLE 1 +#define VD56G3_PATGEN_TYPE_SHIFT 4 +#define VD56G3_REG_AE_COLDSTART_COARSE_EXPOSURE CCI_REG16_LE(0x042a) +#define VD56G3_REG_AE_COLDSTART_ANALOG_GAIN CCI_REG8(0x042c) +#define VD56G3_REG_AE_COLDSTART_DIGITAL_GAIN CCI_REG16_LE(0x042e) +#define VD56G3_REG_AE_ROI_START_H CCI_REG16_LE(0x0432) +#define VD56G3_REG_AE_ROI_START_V CCI_REG16_LE(0x0434) +#define VD56G3_REG_AE_ROI_END_H CCI_REG16_LE(0x0436) +#define VD56G3_REG_AE_ROI_END_V CCI_REG16_LE(0x0438) +#define VD56G3_REG_AE_COMPENSATION CCI_REG16_LE(0x043a) +#define VD56G3_REG_EXP_MODE CCI_REG8(0x044c) +#define VD56G3_EXP_MODE_AUTO 0 +#define VD56G3_EXP_MODE_FREEZE 1 +#define VD56G3_EXP_MODE_MANUAL 2 +#define VD56G3_REG_MANUAL_ANALOG_GAIN CCI_REG8(0x044d) +#define VD56G3_REG_MANUAL_COARSE_EXPOSURE CCI_REG16_LE(0x044e) +#define VD56G3_REG_MANUAL_DIGITAL_GAIN_CH0 CCI_REG16_LE(0x0450) +#define VD56G3_REG_MANUAL_DIGITAL_GAIN_CH1 CCI_REG16_LE(0x0452) +#define VD56G3_REG_MANUAL_DIGITAL_GAIN_CH2 CCI_REG16_LE(0x0454) +#define VD56G3_REG_MANUAL_DIGITAL_GAIN_CH3 CCI_REG16_LE(0x0456) +#define VD56G3_REG_FRAME_LENGTH CCI_REG16_LE(0x0458) +#define VD56G3_REG_Y_START CCI_REG16_LE(0x045a) +#define VD56G3_REG_Y_END CCI_REG16_LE(0x045c) +#define VD56G3_REG_OUT_ROI_X_START CCI_REG16_LE(0x045e) +#define VD56G3_REG_OUT_ROI_X_END CCI_REG16_LE(0x0460) +#define VD56G3_REG_OUT_ROI_Y_START CCI_REG16_LE(0x0462) +#define VD56G3_REG_OUT_ROI_Y_END CCI_REG16_LE(0x0464) +#define VD56G3_REG_GPIO_0_CTRL CCI_REG8(0x0467) +#define VD56G3_GPIOX_GPIO_IN 0x01 +#define VD56G3_GPIOX_STROBE_MODE 0x02 +#define VD56G3_REG_READOUT_CTRL CCI_REG8(0x047e) +#define READOUT_NORMAL 0x00 +#define READOUT_DIGITAL_BINNING_X2 0x01 + +/* The VD56G3 is a portrait image sensor with native resolution of 1124x1364. */ +#define VD56G3_NATIVE_WIDTH 1124 +#define VD56G3_NATIVE_HEIGHT 1364 +#define VD56G3_DEFAULT_MODE 0 + +/* PLL settings */ +#define VD56G3_TARGET_PLL 804000000UL +#define VD56G3_VT_CLOCK_DIV 5 + +/* External clock must be in [6Mhz-27Mhz] */ +#define VD56G3_XCLK_FREQ_MIN (6 * HZ_PER_MHZ) +#define VD56G3_XCLK_FREQ_MAX (27 * HZ_PER_MHZ) + +/* Line length and Frame length (settings are for standard 10bits ADC mode) */ +#define VD56G3_LINE_LENGTH_MIN 1236 +#define VD56G3_VBLANK_MIN 110 +#define VD56G3_FRAME_LENGTH_DEF_60FPS 2168 +#define VD56G3_FRAME_LENGTH_MAX 0xffff + +/* Exposure settings */ +#define VD56G3_EXPOSURE_MARGIN 75 +#define VD56G3_EXPOSURE_MIN 5 +#define VD56G3_EXPOSURE_DEFAULT 1420 + +/* Output Interface settings */ +#define VD56G3_MAX_CSI_DATA_LANES 2 +#define VD56G3_LINK_FREQ_DEF_1LANE 750000000UL +#define VD56G3_LINK_FREQ_DEF_2LANES 402000000UL + +/* GPIOs */ +#define VD56G3_NB_GPIOS 8 + +/* regulator supplies */ +static const char *const vd56g3_supply_names[] = { + "vcore", + "vddio", + "vana", +}; + +/* ----------------------------------------------------------------------------- + * Models (VD56G3: Mono, VD66GY: Bayer RGB), Modes and formats + */ + +enum vd56g3_models { + VD56G3_MODEL_VD56G3, + VD56G3_MODEL_VD66GY, +}; + +struct vd56g3_mode { + u32 width; + u32 height; +}; + +static const struct vd56g3_mode vd56g3_supported_modes[] = { + { + .width = VD56G3_NATIVE_WIDTH, + .height = VD56G3_NATIVE_HEIGHT, + }, + { + .width = 1120, + .height = 1360, + }, + { + .width = 1024, + .height = 1280, + }, + { + .width = 1024, + .height = 768, + }, + { + .width = 768, + .height = 1024, + }, + { + .width = 720, + .height = 1280, + }, + { + .width = 640, + .height = 480, + }, + { + .width = 480, + .height = 640, + }, + { + .width = 320, + .height = 240, + }, +}; + +/* + * Sensor support 8bits and 10bits output in both variants + * - Monochrome + * - RGB (with all H/V flip variations) + */ +static const unsigned int vd56g3_mbus_codes[2][5] = { + { + MEDIA_BUS_FMT_Y8_1X8, + MEDIA_BUS_FMT_SGRBG8_1X8, + MEDIA_BUS_FMT_SRGGB8_1X8, + MEDIA_BUS_FMT_SBGGR8_1X8, + MEDIA_BUS_FMT_SGBRG8_1X8, + }, + { + MEDIA_BUS_FMT_Y10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SBGGR10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, + }, +}; + +struct vd56g3 { + struct device *dev; + struct v4l2_subdev sd; + struct media_pad pad; + struct regulator_bulk_data supplies[ARRAY_SIZE(vd56g3_supply_names)]; + struct gpio_desc *reset_gpio; + struct clk *xclk; + struct regmap *regmap; + u32 xclk_freq; + u32 pll_prediv; + u32 pll_mult; + u32 pixel_clock; + u16 oif_ctrl; + u8 nb_of_lane; + u32 gpios[VD56G3_NB_GPIOS]; + unsigned long ext_leds_mask; + bool is_mono; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *hblank_ctrl; + struct v4l2_ctrl *vblank_ctrl; + struct { + struct v4l2_ctrl *hflip_ctrl; + struct v4l2_ctrl *vflip_ctrl; + }; + struct v4l2_ctrl *patgen_ctrl; + struct { + struct v4l2_ctrl *ae_ctrl; + struct v4l2_ctrl *expo_ctrl; + struct v4l2_ctrl *again_ctrl; + struct v4l2_ctrl *dgain_ctrl; + }; + struct v4l2_ctrl *ae_lock_ctrl; + struct v4l2_ctrl *ae_bias_ctrl; + struct v4l2_ctrl *led_ctrl; +}; + +static inline struct vd56g3 *to_vd56g3(struct v4l2_subdev *sd) +{ + return container_of_const(sd, struct vd56g3, sd); +} + +static inline struct vd56g3 *ctrl_to_vd56g3(struct v4l2_ctrl *ctrl) +{ + return container_of_const(ctrl->handler, struct vd56g3, ctrl_handler); +} + +/* ----------------------------------------------------------------------------- + * Additional i2c register helpers + */ + +static int vd56g3_poll_reg(struct vd56g3 *sensor, u32 reg, u8 poll_val, + int *err) +{ + unsigned int val = 0; + int ret; + + if (err && *err) + return *err; + + /* + * Timeout must be higher than longuest frame duration. With current + * blanking constraints, frame duration can take up to 504ms. + */ + ret = regmap_read_poll_timeout(sensor->regmap, CCI_REG_ADDR(reg), val, + (val == poll_val), 2000, + 600 * USEC_PER_MSEC); + + if (ret && err) + *err = ret; + + return ret; +} + +static int vd56g3_wait_state(struct vd56g3 *sensor, int state, int *err) +{ + return vd56g3_poll_reg(sensor, VD56G3_REG_SYSTEM_FSM, state, err); +} + +/* ----------------------------------------------------------------------------- + * Controls: definitions, helpers and handlers + */ + +static const char *const vd56g3_tp_menu[] = { "Disabled", + "Solid Color", + "Vertical Color Bars", + "Horizontal Gray Scale", + "Vertical Gray Scale", + "Diagonal Gray Scale", + "Pseudo Random" }; + +static const s64 vd56g3_ev_bias_qmenu[] = { -4000, -3500, -3000, -2500, -2000, + -1500, -1000, -500, 0, 500, + 1000, 1500, 2000, 2500, 3000, + 3500, 4000 }; + +static const s64 vd56g3_link_freq_1lane[] = { VD56G3_LINK_FREQ_DEF_1LANE }; + +static const s64 vd56g3_link_freq_2lanes[] = { VD56G3_LINK_FREQ_DEF_2LANES }; + +static u8 vd56g3_get_bpp(__u32 code) +{ + switch (code) { + case MEDIA_BUS_FMT_Y8_1X8: + case MEDIA_BUS_FMT_SGRBG8_1X8: + case MEDIA_BUS_FMT_SRGGB8_1X8: + case MEDIA_BUS_FMT_SBGGR8_1X8: + case MEDIA_BUS_FMT_SGBRG8_1X8: + default: + return 8; + case MEDIA_BUS_FMT_Y10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SRGGB10_1X10: + case MEDIA_BUS_FMT_SBGGR10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + return 10; + } +} + +static u8 vd56g3_get_datatype(__u32 code) +{ + switch (code) { + case MEDIA_BUS_FMT_Y8_1X8: + case MEDIA_BUS_FMT_SGRBG8_1X8: + case MEDIA_BUS_FMT_SRGGB8_1X8: + case MEDIA_BUS_FMT_SBGGR8_1X8: + case MEDIA_BUS_FMT_SGBRG8_1X8: + default: + return MIPI_CSI2_DT_RAW8; + case MEDIA_BUS_FMT_Y10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SRGGB10_1X10: + case MEDIA_BUS_FMT_SBGGR10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + return MIPI_CSI2_DT_RAW10; + } +} + +static int vd56g3_read_expo_cluster(struct vd56g3 *sensor, bool force_cur_val) +{ + u64 exposure; + u64 again; + u64 dgain; + int ret = 0; + + /* + * When 'force_cur_val' is enabled, save the ctrl value in 'cur.val' + * instead of the normal 'val', this is used during poweroff to cache + * volatile ctrls and enable coldstart. + */ + cci_read(sensor->regmap, VD56G3_REG_APPLIED_COARSE_EXPOSURE, &exposure, + &ret); + cci_read(sensor->regmap, VD56G3_REG_APPLIED_ANALOG_GAIN, &again, &ret); + cci_read(sensor->regmap, VD56G3_REG_APPLIED_DIGITAL_GAIN, &dgain, &ret); + if (ret) + return ret; + + if (force_cur_val) { + sensor->expo_ctrl->cur.val = exposure; + sensor->again_ctrl->cur.val = again; + sensor->dgain_ctrl->cur.val = dgain; + } else { + sensor->expo_ctrl->val = exposure; + sensor->again_ctrl->val = again; + sensor->dgain_ctrl->val = dgain; + } + + return ret; +} + +static int vd56g3_update_patgen(struct vd56g3 *sensor, u32 patgen_index) +{ + u32 pattern = patgen_index <= 2 ? patgen_index : patgen_index + 13; + u16 patgen = pattern << VD56G3_PATGEN_TYPE_SHIFT; + u8 duster = VD56G3_DUSTER_ENABLE_DEF_MODULES; + u8 darkcal = VD56G3_DARKCAL_ENABLE; + int ret = 0; + + if (patgen_index) { + patgen |= VD56G3_PATGEN_ENABLE; + duster = VD56G3_DUSTER_DISABLE; + darkcal = VD56G3_DARKCAL_DISABLE_DARKAVG; + } + + cci_write(sensor->regmap, VD56G3_REG_DUSTER_CTRL, duster, &ret); + cci_write(sensor->regmap, VD56G3_REG_DARKCAL_CTRL, darkcal, &ret); + cci_write(sensor->regmap, VD56G3_REG_PATGEN_CTRL, patgen, &ret); + + return ret; +} + +static int vd56g3_update_expo_cluster(struct vd56g3 *sensor, bool is_auto) +{ + u8 expo_state = is_auto ? VD56G3_EXP_MODE_AUTO : VD56G3_EXP_MODE_MANUAL; + int ret = 0; + + if (sensor->ae_ctrl->is_new) + cci_write(sensor->regmap, VD56G3_REG_EXP_MODE, expo_state, + &ret); + + /* In Auto expo, set coldstart parameters */ + if (is_auto && sensor->ae_ctrl->is_new) { + cci_write(sensor->regmap, + VD56G3_REG_AE_COLDSTART_COARSE_EXPOSURE, + sensor->expo_ctrl->val, &ret); + cci_write(sensor->regmap, VD56G3_REG_AE_COLDSTART_ANALOG_GAIN, + sensor->again_ctrl->val, &ret); + cci_write(sensor->regmap, VD56G3_REG_AE_COLDSTART_DIGITAL_GAIN, + sensor->dgain_ctrl->val, &ret); + } + + /* In Manual expo, set exposure, analog and digital gains */ + if (!is_auto && sensor->expo_ctrl->is_new) + cci_write(sensor->regmap, VD56G3_REG_MANUAL_COARSE_EXPOSURE, + sensor->expo_ctrl->val, &ret); + + if (!is_auto && sensor->again_ctrl->is_new) + cci_write(sensor->regmap, VD56G3_REG_MANUAL_ANALOG_GAIN, + sensor->again_ctrl->val, &ret); + + if (!is_auto && sensor->dgain_ctrl->is_new) { + cci_write(sensor->regmap, VD56G3_REG_MANUAL_DIGITAL_GAIN_CH0, + sensor->dgain_ctrl->val, &ret); + cci_write(sensor->regmap, VD56G3_REG_MANUAL_DIGITAL_GAIN_CH1, + sensor->dgain_ctrl->val, &ret); + cci_write(sensor->regmap, VD56G3_REG_MANUAL_DIGITAL_GAIN_CH2, + sensor->dgain_ctrl->val, &ret); + cci_write(sensor->regmap, VD56G3_REG_MANUAL_DIGITAL_GAIN_CH3, + sensor->dgain_ctrl->val, &ret); + } + + return ret; +} + +static int vd56g3_lock_exposure(struct vd56g3 *sensor, u32 lock_val) +{ + bool ae_lock = lock_val & V4L2_LOCK_EXPOSURE; + u8 expo_state = ae_lock ? VD56G3_EXP_MODE_FREEZE : VD56G3_EXP_MODE_AUTO; + + if (sensor->ae_ctrl->val == V4L2_EXPOSURE_AUTO) + return cci_write(sensor->regmap, VD56G3_REG_EXP_MODE, + expo_state, NULL); + + return 0; +} + +static int vd56g3_write_gpiox(struct vd56g3 *sensor, unsigned long gpio_mask) +{ + unsigned long io; + u32 gpio_val; + int ret = 0; + + for_each_set_bit(io, &gpio_mask, VD56G3_NB_GPIOS) { + gpio_val = sensor->gpios[io]; + + if (gpio_val == VD56G3_GPIOX_STROBE_MODE && + sensor->led_ctrl->val == V4L2_FLASH_LED_MODE_NONE) + gpio_val = VD56G3_GPIOX_GPIO_IN; + + cci_write(sensor->regmap, VD56G3_REG_GPIO_0_CTRL + io, gpio_val, + &ret); + } + + return ret; +} + +static int vd56g3_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vd56g3 *sensor = ctrl_to_vd56g3(ctrl); + int ret = 0; + + /* Interact with HW only when it is powered ON */ + if (!pm_runtime_get_if_in_use(sensor->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE_AUTO: + ret = vd56g3_read_expo_cluster(sensor, false); + break; + default: + ret = -EINVAL; + break; + } + + pm_runtime_put_autosuspend(sensor->dev); + + return ret; +} + +static int vd56g3_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vd56g3 *sensor = ctrl_to_vd56g3(ctrl); + struct v4l2_subdev_state *state; + const struct v4l2_rect *crop; + unsigned int frame_length = 0; + unsigned int expo_max; + unsigned int ae_compensation; + bool is_auto = false; + int ret = 0; + + state = v4l2_subdev_get_locked_active_state(&sensor->sd); + crop = v4l2_subdev_state_get_crop(state, 0); + + if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY) + return 0; + + /* Update controls state, range, etc. whatever the state of the HW */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + frame_length = crop->height + ctrl->val; + expo_max = frame_length - VD56G3_EXPOSURE_MARGIN; + ret = __v4l2_ctrl_modify_range(sensor->expo_ctrl, + VD56G3_EXPOSURE_MIN, expo_max, 1, + min(VD56G3_EXPOSURE_DEFAULT, + expo_max)); + break; + case V4L2_CID_EXPOSURE_AUTO: + is_auto = (ctrl->val == V4L2_EXPOSURE_AUTO); + __v4l2_ctrl_grab(sensor->ae_lock_ctrl, !is_auto); + __v4l2_ctrl_grab(sensor->ae_bias_ctrl, !is_auto); + break; + default: + break; + } + + if (ret) + return ret; + + /* Interact with HW only when it is powered ON */ + if (!pm_runtime_get_if_in_use(sensor->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_HFLIP: + ret = cci_write(sensor->regmap, VD56G3_REG_ORIENTATION, + sensor->hflip_ctrl->val | + (sensor->vflip_ctrl->val << 1), + NULL); + break; + case V4L2_CID_TEST_PATTERN: + ret = vd56g3_update_patgen(sensor, ctrl->val); + break; + case V4L2_CID_EXPOSURE_AUTO: + ret = vd56g3_update_expo_cluster(sensor, is_auto); + break; + case V4L2_CID_3A_LOCK: + ret = vd56g3_lock_exposure(sensor, ctrl->val); + break; + case V4L2_CID_AUTO_EXPOSURE_BIAS: + ae_compensation = + DIV_ROUND_CLOSEST((int)vd56g3_ev_bias_qmenu[ctrl->val] * + 256, 1000); + ret = cci_write(sensor->regmap, VD56G3_REG_AE_COMPENSATION, + ae_compensation, NULL); + break; + case V4L2_CID_VBLANK: + ret = cci_write(sensor->regmap, VD56G3_REG_FRAME_LENGTH, + frame_length, NULL); + break; + case V4L2_CID_FLASH_LED_MODE: + ret = vd56g3_write_gpiox(sensor, sensor->ext_leds_mask); + break; + default: + ret = -EINVAL; + break; + } + + pm_runtime_put_autosuspend(sensor->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops vd56g3_ctrl_ops = { + .g_volatile_ctrl = vd56g3_g_volatile_ctrl, + .s_ctrl = vd56g3_s_ctrl, +}; + +static int vd56g3_update_controls(struct vd56g3 *sensor) +{ + struct v4l2_subdev_state *state; + const struct v4l2_rect *crop; + unsigned int hblank; + unsigned int vblank_min, vblank, vblank_max; + unsigned int frame_length; + unsigned int expo_max; + int ret; + + state = v4l2_subdev_get_locked_active_state(&sensor->sd); + crop = v4l2_subdev_state_get_crop(state, 0); + hblank = VD56G3_LINE_LENGTH_MIN - crop->width; + vblank_min = VD56G3_VBLANK_MIN; + vblank = VD56G3_FRAME_LENGTH_DEF_60FPS - crop->height; + vblank_max = VD56G3_FRAME_LENGTH_MAX - crop->height; + frame_length = crop->height + vblank; + expo_max = frame_length - VD56G3_EXPOSURE_MARGIN; + + /* Update blanking and exposure (ranges + values) */ + ret = __v4l2_ctrl_modify_range(sensor->hblank_ctrl, hblank, hblank, 1, + hblank); + if (ret) + return ret; + + ret = __v4l2_ctrl_modify_range(sensor->vblank_ctrl, vblank_min, + vblank_max, 1, vblank); + if (ret) + return ret; + + ret = __v4l2_ctrl_s_ctrl(sensor->vblank_ctrl, vblank); + if (ret) + return ret; + + ret = __v4l2_ctrl_modify_range(sensor->expo_ctrl, VD56G3_EXPOSURE_MIN, + expo_max, 1, VD56G3_EXPOSURE_DEFAULT); + if (ret) + return ret; + + return __v4l2_ctrl_s_ctrl(sensor->expo_ctrl, VD56G3_EXPOSURE_DEFAULT); +} + +static int vd56g3_init_controls(struct vd56g3 *sensor) +{ + const struct v4l2_ctrl_ops *ops = &vd56g3_ctrl_ops; + struct v4l2_ctrl_handler *hdl = &sensor->ctrl_handler; + struct v4l2_fwnode_device_properties fwnode_props; + struct v4l2_ctrl *ctrl; + int ret; + + v4l2_ctrl_handler_init(hdl, 25); + + /* Horizontal & vertical flips modify bayer code on RGB variant */ + sensor->hflip_ctrl = + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0); + if (sensor->hflip_ctrl) + sensor->hflip_ctrl->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + sensor->vflip_ctrl = + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0); + if (sensor->vflip_ctrl) + sensor->vflip_ctrl->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + sensor->patgen_ctrl = + v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(vd56g3_tp_menu) - 1, 0, + 0, vd56g3_tp_menu); + + ctrl = v4l2_ctrl_new_int_menu(hdl, ops, V4L2_CID_LINK_FREQ, + ARRAY_SIZE(vd56g3_link_freq_1lane) - 1, 0, + (sensor->nb_of_lane == 2) ? + vd56g3_link_freq_2lanes : + vd56g3_link_freq_1lane); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_PIXEL_RATE, + sensor->pixel_clock, sensor->pixel_clock, 1, + sensor->pixel_clock); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + sensor->ae_ctrl = v4l2_ctrl_new_std_menu(hdl, ops, + V4L2_CID_EXPOSURE_AUTO, + V4L2_EXPOSURE_MANUAL, 0, + V4L2_EXPOSURE_AUTO); + + sensor->ae_lock_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_3A_LOCK, 0, + GENMASK(2, 0), 0, 0); + + sensor->ae_bias_ctrl = + v4l2_ctrl_new_int_menu(hdl, ops, V4L2_CID_AUTO_EXPOSURE_BIAS, + ARRAY_SIZE(vd56g3_ev_bias_qmenu) - 1, + ARRAY_SIZE(vd56g3_ev_bias_qmenu) / 2, + vd56g3_ev_bias_qmenu); + + /* + * Analog gain [1, 8] is computed with the following logic : + * 32/(32 - again_reg), with again_reg in the range [0:28] + * Digital gain [1.00, 8.00] is coded as a Fixed Point 5.8 + */ + sensor->again_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN, + 0, 28, 1, 0); + sensor->dgain_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_DIGITAL_GAIN, + 0x100, 0x800, 1, 0x100); + + /* + * Set the exposure, horizontal and vertical blanking ctrls + * to hardcoded values, they will be updated in vd56g3_update_controls. + * Exposure being in an auto-cluster, set a significant value here. + */ + sensor->expo_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, + VD56G3_EXPOSURE_DEFAULT, + VD56G3_EXPOSURE_DEFAULT, 1, + VD56G3_EXPOSURE_DEFAULT); + sensor->hblank_ctrl = + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HBLANK, 1, 1, 1, 1); + if (sensor->hblank_ctrl) + sensor->hblank_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + sensor->vblank_ctrl = + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK, 1, 1, 1, 1); + + /* Additional control based on device tree properties */ + if (sensor->ext_leds_mask) + sensor->led_ctrl = + v4l2_ctrl_new_std_menu(hdl, ops, + V4L2_CID_FLASH_LED_MODE, + V4L2_FLASH_LED_MODE_FLASH, 0, + V4L2_FLASH_LED_MODE_NONE); + + if (hdl->error) { + ret = hdl->error; + goto free_ctrls; + } + + v4l2_ctrl_cluster(2, &sensor->hflip_ctrl); + v4l2_ctrl_auto_cluster(4, &sensor->ae_ctrl, V4L2_EXPOSURE_MANUAL, true); + + /* Optional controls coming from fwnode (e.g. rotation, orientation). */ + ret = v4l2_fwnode_device_parse(sensor->dev, &fwnode_props); + if (ret) + goto free_ctrls; + + ret = v4l2_ctrl_new_fwnode_properties(hdl, ops, &fwnode_props); + if (ret) + goto free_ctrls; + + sensor->sd.ctrl_handler = hdl; + + return 0; + +free_ctrls: + v4l2_ctrl_handler_free(hdl); + + return ret; +} + +/* ----------------------------------------------------------------------------- + * Pad ops + */ + +/* Media bus code is dependent of : + * - 8bits or 10bits output + * - variant : Mono or RGB + * - H/V flips parameters in case of RGB + */ +static u32 vd56g3_get_mbus_code(struct vd56g3 *sensor, u32 code) +{ + unsigned int i_bpp; + unsigned int j; + + for (i_bpp = 0; i_bpp < ARRAY_SIZE(vd56g3_mbus_codes); i_bpp++) { + for (j = 0; j < ARRAY_SIZE(vd56g3_mbus_codes[i_bpp]); j++) { + if (vd56g3_mbus_codes[i_bpp][j] == code) + goto endloops; + } + } + +endloops: + if (i_bpp >= ARRAY_SIZE(vd56g3_mbus_codes)) + i_bpp = 0; + + if (sensor->is_mono) + j = 0; + else + j = 1 + (sensor->hflip_ctrl->val ? 1 : 0) + + (sensor->vflip_ctrl->val ? 2 : 0); + + return vd56g3_mbus_codes[i_bpp][j]; +} + +static int vd56g3_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct vd56g3 *sensor = to_vd56g3(sd); + + if (code->index >= ARRAY_SIZE(vd56g3_mbus_codes)) + return -EINVAL; + + code->code = + vd56g3_get_mbus_code(sensor, vd56g3_mbus_codes[code->index][0]); + + return 0; +} + +static int vd56g3_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(vd56g3_supported_modes)) + return -EINVAL; + + fse->min_width = vd56g3_supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = vd56g3_supported_modes[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static void vd56g3_update_img_pad_format(struct vd56g3 *sensor, + const struct vd56g3_mode *mode, + u32 mbus_code, + struct v4l2_mbus_framefmt *mbus_fmt) +{ + mbus_fmt->width = mode->width; + mbus_fmt->height = mode->height; + mbus_fmt->code = vd56g3_get_mbus_code(sensor, mbus_code); + mbus_fmt->colorspace = V4L2_COLORSPACE_RAW; + mbus_fmt->field = V4L2_FIELD_NONE; + mbus_fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + mbus_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; + mbus_fmt->xfer_func = V4L2_XFER_FUNC_NONE; +} + +static int vd56g3_set_pad_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *sd_fmt) +{ + struct vd56g3 *sensor = to_vd56g3(sd); + const struct vd56g3_mode *new_mode; + struct v4l2_rect pad_crop; + unsigned int binning; + + new_mode = v4l2_find_nearest_size(vd56g3_supported_modes, + ARRAY_SIZE(vd56g3_supported_modes), + width, height, sd_fmt->format.width, + sd_fmt->format.height); + + vd56g3_update_img_pad_format(sensor, new_mode, sd_fmt->format.code, + &sd_fmt->format); + *v4l2_subdev_state_get_format(sd_state, sd_fmt->pad) = sd_fmt->format; + + /* Compute and update crop rectangle (maximized via binning) */ + binning = min(VD56G3_NATIVE_WIDTH / sd_fmt->format.width, + VD56G3_NATIVE_HEIGHT / sd_fmt->format.height); + binning = min(binning, 2U); + pad_crop.width = sd_fmt->format.width * binning; + pad_crop.height = sd_fmt->format.height * binning; + pad_crop.left = (VD56G3_NATIVE_WIDTH - pad_crop.width) / 2; + pad_crop.top = (VD56G3_NATIVE_HEIGHT - pad_crop.height) / 2; + *v4l2_subdev_state_get_crop(sd_state, sd_fmt->pad) = pad_crop; + + /* Update controls in case of active state */ + if (sd_fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + return vd56g3_update_controls(sensor); + + return 0; +} + +static int vd56g3_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = *v4l2_subdev_state_get_crop(sd_state, 0); + break; + case V4L2_SEL_TGT_NATIVE_SIZE: + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = VD56G3_NATIVE_WIDTH; + sel->r.height = VD56G3_NATIVE_HEIGHT; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int vd56g3_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_frame_desc *fd) +{ + struct v4l2_subdev_state *state; + const struct v4l2_mbus_framefmt *format; + + state = v4l2_subdev_lock_and_get_active_state(sd); + format = v4l2_subdev_state_get_format(state, pad); + v4l2_subdev_unlock_state(state); + + fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; + fd->num_entries = 1; + fd->entry[0].pixelcode = format->code; + fd->entry[0].stream = 0; + fd->entry[0].bus.csi2.vc = 0; + fd->entry[0].bus.csi2.dt = vd56g3_get_datatype(format->code); + + return 0; +} + +static int vd56g3_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct vd56g3 *sensor = to_vd56g3(sd); + const struct v4l2_mbus_framefmt *format = + v4l2_subdev_state_get_format(state, 0); + const struct v4l2_rect *crop = v4l2_subdev_state_get_crop(state, 0); + unsigned int csi_mbps = ((sensor->nb_of_lane == 2) ? + VD56G3_LINK_FREQ_DEF_2LANES : + VD56G3_LINK_FREQ_DEF_1LANE) * + 2 / MEGA; + unsigned int binning; + int ret; + + ret = pm_runtime_resume_and_get(sensor->dev); + if (ret < 0) + return ret; + + /* configure clocks */ + cci_write(sensor->regmap, VD56G3_REG_EXT_CLOCK, sensor->xclk_freq, + &ret); + cci_write(sensor->regmap, VD56G3_REG_CLK_PLL_PREDIV, sensor->pll_prediv, + &ret); + cci_write(sensor->regmap, VD56G3_REG_CLK_SYS_PLL_MULT, sensor->pll_mult, + &ret); + + /* configure output */ + cci_write(sensor->regmap, VD56G3_REG_FORMAT_CTRL, + vd56g3_get_bpp(format->code), &ret); + cci_write(sensor->regmap, VD56G3_REG_OIF_CTRL, sensor->oif_ctrl, &ret); + cci_write(sensor->regmap, VD56G3_REG_OIF_CSI_BITRATE, csi_mbps, &ret); + cci_write(sensor->regmap, VD56G3_REG_OIF_IMG_CTRL, + vd56g3_get_datatype(format->code), &ret); + cci_write(sensor->regmap, VD56G3_REG_ISL_ENABLE, 0, &ret); + + /* configure binning mode */ + switch (crop->width / format->width) { + case 1: + default: + binning = READOUT_NORMAL; + break; + case 2: + binning = READOUT_DIGITAL_BINNING_X2; + break; + } + cci_write(sensor->regmap, VD56G3_REG_READOUT_CTRL, binning, &ret); + + /* configure ROIs */ + cci_write(sensor->regmap, VD56G3_REG_Y_START, crop->top, &ret); + cci_write(sensor->regmap, VD56G3_REG_Y_END, + crop->top + crop->height - 1, &ret); + cci_write(sensor->regmap, VD56G3_REG_OUT_ROI_X_START, crop->left, &ret); + cci_write(sensor->regmap, VD56G3_REG_OUT_ROI_X_END, + crop->left + crop->width - 1, &ret); + cci_write(sensor->regmap, VD56G3_REG_OUT_ROI_Y_START, 0, &ret); + cci_write(sensor->regmap, VD56G3_REG_OUT_ROI_Y_END, crop->height - 1, + &ret); + cci_write(sensor->regmap, VD56G3_REG_AE_ROI_START_H, crop->left, &ret); + cci_write(sensor->regmap, VD56G3_REG_AE_ROI_END_H, + crop->left + crop->width - 1, &ret); + cci_write(sensor->regmap, VD56G3_REG_AE_ROI_START_V, 0, &ret); + cci_write(sensor->regmap, VD56G3_REG_AE_ROI_END_V, crop->height - 1, + &ret); + if (ret) + goto rpm_put; + + /* Setup default GPIO values; could be overridden by V4L2 ctrl setup */ + ret = vd56g3_write_gpiox(sensor, GENMASK(VD56G3_NB_GPIOS - 1, 0)); + if (ret) + goto rpm_put; + + /* Apply settings from V4L2 ctrls */ + ret = __v4l2_ctrl_handler_setup(&sensor->ctrl_handler); + if (ret) + goto rpm_put; + + /* start streaming */ + cci_write(sensor->regmap, VD56G3_REG_STBY, VD56G3_CMD_START_STREAM, + &ret); + vd56g3_poll_reg(sensor, VD56G3_REG_STBY, VD56G3_CMD_ACK, &ret); + vd56g3_wait_state(sensor, VD56G3_SYSTEM_FSM_STREAMING, &ret); + if (ret) + goto rpm_put; + + /* some controls are locked during streaming */ + __v4l2_ctrl_grab(sensor->hflip_ctrl, true); + __v4l2_ctrl_grab(sensor->vflip_ctrl, true); + __v4l2_ctrl_grab(sensor->patgen_ctrl, true); + + return ret; + +rpm_put: + dev_err(sensor->dev, "Failed to start streaming\n"); + pm_runtime_put_sync(sensor->dev); + + return ret; +} + +static int vd56g3_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct vd56g3 *sensor = to_vd56g3(sd); + int ret; + + /* Retrieve Expo cluster to enable coldstart of AE */ + ret = vd56g3_read_expo_cluster(sensor, true); + + cci_write(sensor->regmap, VD56G3_REG_STREAMING, VD56G3_CMD_STOP_STREAM, + &ret); + vd56g3_poll_reg(sensor, VD56G3_REG_STREAMING, VD56G3_CMD_ACK, &ret); + vd56g3_wait_state(sensor, VD56G3_SYSTEM_FSM_SW_STBY, &ret); + + /* locked controls must be unlocked */ + __v4l2_ctrl_grab(sensor->hflip_ctrl, false); + __v4l2_ctrl_grab(sensor->vflip_ctrl, false); + __v4l2_ctrl_grab(sensor->patgen_ctrl, false); + + pm_runtime_put_autosuspend(sensor->dev); + + return ret; +} + +static int vd56g3_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + unsigned int def_mode = VD56G3_DEFAULT_MODE; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_TRY, + .pad = 0, + .format = { + .code = vd56g3_mbus_codes[0][0], + .width = vd56g3_supported_modes[def_mode].width, + .height = vd56g3_supported_modes[def_mode].height, + }, + }; + + return vd56g3_set_pad_fmt(sd, sd_state, &fmt); +} + +static const struct v4l2_subdev_video_ops vd56g3_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_pad_ops vd56g3_pad_ops = { + .enum_mbus_code = vd56g3_enum_mbus_code, + .enum_frame_size = vd56g3_enum_frame_size, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = vd56g3_set_pad_fmt, + .get_selection = vd56g3_get_selection, + .get_frame_desc = vd56g3_get_frame_desc, + .enable_streams = vd56g3_enable_streams, + .disable_streams = vd56g3_disable_streams, +}; + +static const struct v4l2_subdev_ops vd56g3_subdev_ops = { + .video = &vd56g3_video_ops, + .pad = &vd56g3_pad_ops, +}; + +static const struct media_entity_operations vd56g3_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_internal_ops vd56g3_internal_ops = { + .init_state = vd56g3_init_state, +}; + +/* ----------------------------------------------------------------------------- + * Power management + */ + +static int vd56g3_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct vd56g3 *sensor = to_vd56g3(sd); + int ret; + + /* power on */ + ret = regulator_bulk_enable(ARRAY_SIZE(sensor->supplies), + sensor->supplies); + if (ret) { + dev_err(dev, "Failed to enable regulators: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(sensor->xclk); + if (ret) { + dev_err(dev, "Failed to enable clock: %d\n", ret); + goto disable_reg; + } + + gpiod_set_value_cansleep(sensor->reset_gpio, 0); + usleep_range(3500, 4000); + ret = vd56g3_wait_state(sensor, VD56G3_SYSTEM_FSM_READY_TO_BOOT, NULL); + if (ret) { + dev_err(dev, "Sensor reset failed: %d\n", ret); + goto disable_clock; + } + + /* boot sensor */ + cci_write(sensor->regmap, VD56G3_REG_BOOT, VD56G3_CMD_BOOT, &ret); + vd56g3_poll_reg(sensor, VD56G3_REG_BOOT, VD56G3_CMD_ACK, &ret); + vd56g3_wait_state(sensor, VD56G3_SYSTEM_FSM_SW_STBY, &ret); + if (ret) { + dev_err(dev, "Sensor boot failed: %d\n", ret); + goto disable_clock; + } + + return 0; + +disable_clock: + gpiod_set_value_cansleep(sensor->reset_gpio, 1); + clk_disable_unprepare(sensor->xclk); +disable_reg: + regulator_bulk_disable(ARRAY_SIZE(sensor->supplies), sensor->supplies); + + return ret; +} + +static int vd56g3_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct vd56g3 *sensor = to_vd56g3(sd); + + gpiod_set_value_cansleep(sensor->reset_gpio, 1); + clk_disable_unprepare(sensor->xclk); + regulator_bulk_disable(ARRAY_SIZE(sensor->supplies), sensor->supplies); + + return 0; +} + +static const struct dev_pm_ops vd56g3_pm_ops = { + SET_RUNTIME_PM_OPS(vd56g3_power_off, vd56g3_power_on, NULL) +}; + +/* ----------------------------------------------------------------------------- + * Probe and initialization + */ + +static int vd56g3_check_csi_conf(struct vd56g3 *sensor, + struct fwnode_handle *endpoint) +{ + struct v4l2_fwnode_endpoint ep = { .bus_type = V4L2_MBUS_CSI2_DPHY }; + u32 phy_data_lanes[VD56G3_MAX_CSI_DATA_LANES] = { ~0, ~0 }; + u8 n_lanes; + u64 frequency; + int p, l; + int ret = 0; + + ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep); + if (ret) + return -EINVAL; + + /* Check lanes number */ + n_lanes = ep.bus.mipi_csi2.num_data_lanes; + if (n_lanes != 1 && n_lanes != 2) { + dev_err(sensor->dev, "Invalid data lane number: %d\n", n_lanes); + ret = -EINVAL; + goto done; + } + sensor->nb_of_lane = n_lanes; + + /* Clock lane must be first */ + if (ep.bus.mipi_csi2.clock_lane != 0) { + dev_err(sensor->dev, "Clock lane must be mapped to lane 0\n"); + ret = -EINVAL; + goto done; + } + + /* + * Prepare Output Interface conf based on lane settings + * logical to physical lane conversion (+ pad remaining slots) + */ + for (l = 0; l < n_lanes; l++) + phy_data_lanes[ep.bus.mipi_csi2.data_lanes[l] - 1] = l; + for (p = 0; p < VD56G3_MAX_CSI_DATA_LANES; p++) { + if (phy_data_lanes[p] != ~0) + continue; + phy_data_lanes[p] = l; + l++; + } + sensor->oif_ctrl = n_lanes | + (ep.bus.mipi_csi2.lane_polarities[0] << 3) | + ((phy_data_lanes[0]) << 4) | + (ep.bus.mipi_csi2.lane_polarities[1] << 6) | + ((phy_data_lanes[1]) << 7) | + (ep.bus.mipi_csi2.lane_polarities[2] << 9); + + /* Check link frequency */ + if (!ep.nr_of_link_frequencies) { + dev_err(sensor->dev, "link-frequency not found in DT\n"); + ret = -EINVAL; + goto done; + } + frequency = (n_lanes == 2) ? VD56G3_LINK_FREQ_DEF_2LANES : + VD56G3_LINK_FREQ_DEF_1LANE; + if (ep.nr_of_link_frequencies != 1 || + ep.link_frequencies[0] != frequency) { + dev_err(sensor->dev, "Link frequency not supported: %lld\n", + ep.link_frequencies[0]); + ret = -EINVAL; + goto done; + } + +done: + v4l2_fwnode_endpoint_free(&ep); + + return ret; +} + +static int vd56g3_parse_dt_gpios_array(struct vd56g3 *sensor, char *prop_name, + u32 *array, unsigned int *nb) +{ + struct device *dev = sensor->dev; + unsigned int i; + int ret; + + if (!device_property_present(dev, prop_name)) { + *nb = 0; + return 0; + } + + ret = device_property_count_u32(dev, prop_name); + if (ret < 0) { + dev_err(dev, "Failed to read %s count\n", prop_name); + return ret; + } + + *nb = ret; + ret = device_property_read_u32_array(dev, prop_name, array, *nb); + if (ret) { + dev_err(dev, "Failed to read %s prop\n", prop_name); + return ret; + } + + for (i = 0; i < *nb; i++) { + if (array[i] >= VD56G3_NB_GPIOS) { + dev_err(dev, "Invalid GPIO: %d\n", array[i]); + return -EINVAL; + } + } + + return 0; +} + +static int vd56g3_parse_dt_gpios(struct vd56g3 *sensor) +{ + u32 led_gpios[VD56G3_NB_GPIOS]; + unsigned int nb_gpios_leds; + unsigned int i; + int ret; + + /* Initialize GPIOs to default */ + for (i = 0; i < VD56G3_NB_GPIOS; i++) + sensor->gpios[i] = VD56G3_GPIOX_GPIO_IN; + sensor->ext_leds_mask = 0; + + /* Take into account optional 'st,leds' output for GPIOs */ + ret = vd56g3_parse_dt_gpios_array(sensor, "st,leds", led_gpios, + &nb_gpios_leds); + if (ret) + return ret; + for (i = 0; i < nb_gpios_leds; i++) { + sensor->gpios[led_gpios[i]] = VD56G3_GPIOX_STROBE_MODE; + set_bit(led_gpios[i], &sensor->ext_leds_mask); + } + + return 0; +} + +static int vd56g3_parse_dt(struct vd56g3 *sensor) +{ + struct fwnode_handle *endpoint; + int ret; + + endpoint = fwnode_graph_get_endpoint_by_id(dev_fwnode(sensor->dev), 0, + 0, 0); + if (!endpoint) { + dev_err(sensor->dev, "Endpoint node not found\n"); + return -EINVAL; + } + + ret = vd56g3_check_csi_conf(sensor, endpoint); + fwnode_handle_put(endpoint); + if (ret) + return ret; + + return vd56g3_parse_dt_gpios(sensor); +} + +static int vd56g3_get_regulators(struct vd56g3 *sensor) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(sensor->supplies); i++) + sensor->supplies[i].supply = vd56g3_supply_names[i]; + + return devm_regulator_bulk_get(sensor->dev, + ARRAY_SIZE(sensor->supplies), + sensor->supplies); +} + +static int vd56g3_prepare_clock_tree(struct vd56g3 *sensor) +{ + const unsigned int predivs[] = { 1, 2, 4 }; + u32 pll_out; + int i; + + /* External clock must be in [6Mhz-27Mhz] */ + if (sensor->xclk_freq < VD56G3_XCLK_FREQ_MIN || + sensor->xclk_freq > VD56G3_XCLK_FREQ_MAX) { + dev_err(sensor->dev, + "Only 6Mhz-27Mhz clock range supported. Provided %lu MHz\n", + sensor->xclk_freq / HZ_PER_MHZ); + return -EINVAL; + } + + /* PLL input should be in [6Mhz-12Mhz[ */ + for (i = 0; i < ARRAY_SIZE(predivs); i++) { + sensor->pll_prediv = predivs[i]; + if (sensor->xclk_freq / sensor->pll_prediv < 12 * HZ_PER_MHZ) + break; + } + + /* PLL output clock must be as close as possible to 804Mhz */ + sensor->pll_mult = (VD56G3_TARGET_PLL * sensor->pll_prediv + + sensor->xclk_freq / 2) / + sensor->xclk_freq; + pll_out = sensor->xclk_freq * sensor->pll_mult / sensor->pll_prediv; + + /* Target Pixel Clock for standard 10bit ADC mode : 160.8Mhz */ + sensor->pixel_clock = pll_out / VD56G3_VT_CLOCK_DIV; + + return 0; +} + +static int vd56g3_detect(struct vd56g3 *sensor) +{ + struct device *dev = sensor->dev; + unsigned int model; + u64 model_id; + u64 device_revision; + u64 optical_revision; + int ret = 0; + + model = (uintptr_t)device_get_match_data(dev); + + ret = cci_read(sensor->regmap, VD56G3_REG_MODEL_ID, &model_id, NULL); + if (ret) + return ret; + + if (model_id != VD56G3_MODEL_ID) { + dev_err(dev, "Unsupported sensor id: %x\n", (u16)model_id); + return -ENODEV; + } + + ret = cci_read(sensor->regmap, VD56G3_REG_REVISION, &device_revision, + NULL); + if (ret) + return ret; + + if ((device_revision >> 8) != VD56G3_REVISION_CUT3) { + dev_err(dev, "Unsupported version: %x\n", (u16)device_revision); + return -ENODEV; + } + + ret = cci_read(sensor->regmap, VD56G3_REG_OPTICAL_REVISION, + &optical_revision, NULL); + if (ret) + return ret; + + sensor->is_mono = + ((optical_revision & 1) == VD56G3_OPTICAL_REVISION_MONO); + if ((sensor->is_mono && model == VD56G3_MODEL_VD66GY) || + (!sensor->is_mono && model == VD56G3_MODEL_VD56G3)) { + dev_err(dev, "Found %s sensor, while %s model is defined in DT\n", + (sensor->is_mono) ? "Mono" : "Bayer", + (model == VD56G3_MODEL_VD56G3) ? "vd56g3" : "vd66gy"); + return -ENODEV; + } + + return 0; +} + +static int vd56g3_subdev_init(struct vd56g3 *sensor) +{ + struct v4l2_subdev_state *state; + int ret; + + /* Init remaining sub device ops */ + sensor->sd.internal_ops = &vd56g3_internal_ops; + sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sensor->sd.entity.ops = &vd56g3_subdev_entity_ops; + + /* Init source pad */ + 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) { + dev_err(sensor->dev, "Failed to init media entity: %d\n", ret); + return ret; + } + + /* Init controls */ + ret = vd56g3_init_controls(sensor); + if (ret) { + dev_err(sensor->dev, "Controls initialization failed: %d\n", + ret); + goto err_media; + } + + /* Init vd56g3 struct : default resolution + raw8 */ + sensor->sd.state_lock = sensor->ctrl_handler.lock; + ret = v4l2_subdev_init_finalize(&sensor->sd); + if (ret) { + dev_err(sensor->dev, "Subdev init failed: %d\n", ret); + goto err_ctrls; + } + + /* Update controls according to the resolution set */ + state = v4l2_subdev_lock_and_get_active_state(&sensor->sd); + ret = vd56g3_update_controls(sensor); + v4l2_subdev_unlock_state(state); + if (ret) { + dev_err(sensor->dev, "Controls update failed: %d\n", ret); + goto err_ctrls; + } + + return 0; + +err_ctrls: + v4l2_ctrl_handler_free(sensor->sd.ctrl_handler); + +err_media: + media_entity_cleanup(&sensor->sd.entity); + + return ret; +} + +static void vd56g3_subdev_cleanup(struct vd56g3 *sensor) +{ + v4l2_async_unregister_subdev(&sensor->sd); + v4l2_subdev_cleanup(&sensor->sd); + media_entity_cleanup(&sensor->sd.entity); + v4l2_ctrl_handler_free(sensor->sd.ctrl_handler); +} + +static int vd56g3_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct vd56g3 *sensor; + int ret; + + sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); + if (!sensor) + return -ENOMEM; + + v4l2_i2c_subdev_init(&sensor->sd, client, &vd56g3_subdev_ops); + sensor->dev = dev; + + ret = vd56g3_parse_dt(sensor); + if (ret) + return dev_err_probe(dev, ret, "Failed to parse Device Tree\n"); + + /* Get (and check) resources : power regs, ext clock, reset gpio */ + ret = vd56g3_get_regulators(sensor); + if (ret) + return dev_err_probe(dev, ret, "Failed to get regulators\n"); + + sensor->xclk = devm_v4l2_sensor_clk_get(dev, NULL); + if (IS_ERR(sensor->xclk)) + return dev_err_probe(dev, PTR_ERR(sensor->xclk), + "Failed to get xclk\n"); + sensor->xclk_freq = clk_get_rate(sensor->xclk); + ret = vd56g3_prepare_clock_tree(sensor); + if (ret) + return ret; + + sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(sensor->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(sensor->reset_gpio), + "Failed to get reset gpio\n"); + + sensor->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(sensor->regmap)) + return dev_err_probe(dev, PTR_ERR(sensor->regmap), + "Failed to init regmap\n"); + + /* Power ON */ + ret = vd56g3_power_on(dev); + if (ret) + return dev_err_probe(dev, ret, "Sensor power on failed\n"); + + /* Enable PM runtime with autosuspend (sensor being ON, set active) */ + pm_runtime_set_active(dev); + pm_runtime_get_noresume(dev); + pm_runtime_enable(dev); + pm_runtime_set_autosuspend_delay(dev, 1000); + pm_runtime_use_autosuspend(dev); + + /* Check HW model/version */ + ret = vd56g3_detect(sensor); + if (ret) { + dev_err(dev, "Sensor detect failed: %d\n", ret); + goto err_power_off; + } + + /* Initialize & register subdev (v4l2_i2c subdev already initialized) */ + ret = vd56g3_subdev_init(sensor); + if (ret) { + dev_err(dev, "V4l2 init failed: %d\n", ret); + goto err_power_off; + } + + ret = v4l2_async_register_subdev(&sensor->sd); + if (ret) { + dev_err(dev, "Async subdev register failed: %d\n", ret); + goto err_subdev; + } + + /* Sensor could now be powered off (after the autosuspend delay) */ + pm_runtime_put_autosuspend(dev); + + dev_dbg(dev, "Successfully probe %s sensor\n", + (sensor->is_mono) ? "vd56g3" : "vd66gy"); + + return 0; + +err_subdev: + vd56g3_subdev_cleanup(sensor); +err_power_off: + pm_runtime_disable(dev); + pm_runtime_put_noidle(dev); + pm_runtime_dont_use_autosuspend(dev); + vd56g3_power_off(dev); + + return ret; +} + +static void vd56g3_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct vd56g3 *sensor = to_vd56g3(sd); + + vd56g3_subdev_cleanup(sensor); + + pm_runtime_disable(sensor->dev); + if (!pm_runtime_status_suspended(sensor->dev)) + vd56g3_power_off(sensor->dev); + pm_runtime_set_suspended(sensor->dev); + pm_runtime_dont_use_autosuspend(sensor->dev); +} + +static const struct of_device_id vd56g3_dt_ids[] = { + { .compatible = "st,vd56g3", .data = (void *)VD56G3_MODEL_VD56G3 }, + { .compatible = "st,vd66gy", .data = (void *)VD56G3_MODEL_VD66GY }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, vd56g3_dt_ids); + +static struct i2c_driver vd56g3_i2c_driver = { + .driver = { + .name = "vd56g3", + .of_match_table = vd56g3_dt_ids, + .pm = &vd56g3_pm_ops, + }, + .probe = vd56g3_probe, + .remove = vd56g3_remove, +}; + +module_i2c_driver(vd56g3_i2c_driver); + +MODULE_AUTHOR("Benjamin Mugnier <benjamin.mugnier@foss.st.com>"); +MODULE_AUTHOR("Mickael Guene <mickael.guene@st.com>"); +MODULE_AUTHOR("Sylvain Petinot <sylvain.petinot@foss.st.com>"); +MODULE_DESCRIPTION("ST VD56G3 sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/vgxy61.c b/drivers/media/i2c/vgxy61.c index d77468c8587b..d64d0099e6fe 100644 --- a/drivers/media/i2c/vgxy61.c +++ b/drivers/media/i2c/vgxy61.c @@ -892,8 +892,8 @@ static u32 vgxy61_get_expo_long_max(struct vgxy61_dev *sensor, third_rot_max_expo = (sensor->frame_length / 71) * short_expo_ratio; /* Take the minimum from all rules */ - return min(min(first_rot_max_expo, second_rot_max_expo), - third_rot_max_expo); + return min3(first_rot_max_expo, second_rot_max_expo, + third_rot_max_expo); } static int vgxy61_update_exposure(struct vgxy61_dev *sensor, u16 new_expo_long, @@ -1181,6 +1181,21 @@ static int vgxy61_s_stream(struct v4l2_subdev *sd, int enable) return ret; } +static int vgxy61_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_frame_desc *fd) +{ + struct vgxy61_dev *sensor = to_vgxy61_dev(sd); + + fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; + fd->num_entries = 1; + fd->entry[0].pixelcode = sensor->fmt.code; + fd->entry[0].stream = 0; + fd->entry[0].bus.csi2.vc = 0; + fd->entry[0].bus.csi2.dt = get_data_type_by_code(sensor->fmt.code); + + return 0; +} + static int vgxy61_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) @@ -1402,6 +1417,7 @@ static const struct v4l2_subdev_pad_ops vgxy61_pad_ops = { .set_fmt = vgxy61_set_fmt, .get_selection = vgxy61_get_selection, .enum_frame_size = vgxy61_enum_frame_size, + .get_frame_desc = vgxy61_get_frame_desc, }; static const struct v4l2_subdev_ops vgxy61_subdev_ops = { @@ -1761,11 +1777,11 @@ static int vgxy61_probe(struct i2c_client *client) return ret; } - sensor->xclk = devm_clk_get(dev, NULL); - if (IS_ERR(sensor->xclk)) { - dev_err(dev, "failed to get xclk\n"); - return PTR_ERR(sensor->xclk); - } + sensor->xclk = devm_v4l2_sensor_clk_get(dev, NULL); + if (IS_ERR(sensor->xclk)) + return dev_err_probe(dev, PTR_ERR(sensor->xclk), + "failed to get xclk\n"); + sensor->clk_freq = clk_get_rate(sensor->xclk); if (sensor->clk_freq < 6 * HZ_PER_MHZ || sensor->clk_freq > 27 * HZ_PER_MHZ) { diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c index 036a6375627a..1eee2d4f5b40 100644 --- a/drivers/media/i2c/video-i2c.c +++ b/drivers/media/i2c/video-i2c.c @@ -264,18 +264,8 @@ static int amg88xx_set_power(struct video_i2c_data *data, bool on) #if IS_REACHABLE(CONFIG_HWMON) -static const u32 amg88xx_temp_config[] = { - HWMON_T_INPUT, - 0 -}; - -static const struct hwmon_channel_info amg88xx_temp = { - .type = hwmon_temp, - .config = amg88xx_temp_config, -}; - static const struct hwmon_channel_info * const amg88xx_info[] = { - &amg88xx_temp, + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), NULL }; @@ -298,7 +288,6 @@ static int amg88xx_read(struct device *dev, enum hwmon_sensor_types type, return tmp; tmp = regmap_bulk_read(data->regmap, AMG88XX_REG_TTHL, &buf, 2); - pm_runtime_mark_last_busy(regmap_get_device(data->regmap)); pm_runtime_put_autosuspend(regmap_get_device(data->regmap)); if (tmp) return tmp; @@ -537,7 +526,6 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) return 0; error_rpm_put: - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); error_del_list: video_i2c_del_list(vq, VB2_BUF_STATE_QUEUED); @@ -554,7 +542,6 @@ static void stop_streaming(struct vb2_queue *vq) kthread_stop(data->kthread_vid_cap); data->kthread_vid_cap = NULL; - pm_runtime_mark_last_busy(regmap_get_device(data->regmap)); pm_runtime_put_autosuspend(regmap_get_device(data->regmap)); video_i2c_del_list(vq, VB2_BUF_STATE_ERROR); @@ -863,7 +850,6 @@ static int video_i2c_probe(struct i2c_client *client) if (ret < 0) goto error_pm_disable; - pm_runtime_mark_last_busy(&client->dev); pm_runtime_put_autosuspend(&client->dev); return 0; diff --git a/drivers/media/i2c/vp27smpx.c b/drivers/media/i2c/vp27smpx.c index 06fd46a63c72..df21950be24f 100644 --- a/drivers/media/i2c/vp27smpx.c +++ b/drivers/media/i2c/vp27smpx.c @@ -2,7 +2,7 @@ /* * vp27smpx - driver version 0.0.1 * - * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * Copyright (C) 2007 Hans Verkuil <hverkuil@kernel.org> * * Based on a tvaudio patch from Takahiro Adachi <tadachi@tadachi-net.com> * and Kazuhiko Kawakami <kazz-0@mail.goo.ne.jp> diff --git a/drivers/media/i2c/wm8739.c b/drivers/media/i2c/wm8739.c index c091b78a5b41..72eb10339d06 100644 --- a/drivers/media/i2c/wm8739.c +++ b/drivers/media/i2c/wm8739.c @@ -4,7 +4,7 @@ * * Copyright (C) 2005 T. Adachi <tadachi@tadachi-net.com> * - * Copyright (C) 2005 Hans Verkuil <hverkuil@xs4all.nl> + * Copyright (C) 2005 Hans Verkuil <hverkuil@kernel.org> * - Cleanup */ diff --git a/drivers/media/i2c/wm8775.c b/drivers/media/i2c/wm8775.c index 619b2988577c..56778d3bc28a 100644 --- a/drivers/media/i2c/wm8775.c +++ b/drivers/media/i2c/wm8775.c @@ -6,7 +6,7 @@ * * Based on saa7115 driver * - * Copyright (C) 2005 Hans Verkuil <hverkuil@xs4all.nl> + * Copyright (C) 2005 Hans Verkuil <hverkuil@kernel.org> * - Cleanup * - V4L2 API update * - sound fixes |
