diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c index cae1866134a0..25aff0a8d307 100644 --- a/drivers/media/i2c/ov8865.c +++ b/drivers/media/i2c/ov8865.c @@ -1,3169 +1,1631 @@ -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-License-Identifier: GPL-2.0 /* - * Copyright 2020 Kévin L'hôpital - * Copyright 2020 Bootlin - * Author: Paul Kocialkowski + * ov8865 driver + * Copyright (C) 2024 Rockchip Electronics Co., Ltd. */ #include -#include #include +#include +#include #include -#include #include -#include #include +#include +#include #include -#include +#include +#include +#include +#include +#include + +#include +#include +#include #include #include +#include #include #include #include +#include -/* Register definitions */ +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x00) -/* System */ +#ifndef V4L2_CID_DIGITAL_GAIN +#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN +#endif -#define OV8865_SW_STANDBY_REG 0x100 -#define OV8865_SW_STANDBY_STREAM_ON BIT(0) +#define MIPI_FREQ 360000000U +#define OV8865_PIXEL_RATE (MIPI_FREQ * 2LL * 4LL / 10LL) -#define OV8865_SW_RESET_REG 0x103 -#define OV8865_SW_RESET_RESET BIT(0) +#define OV8865_XVCLK_FREQ 24000000 -#define OV8865_PLL_CTRL0_REG 0x300 -#define OV8865_PLL_CTRL0_PRE_DIV(v) ((v) & GENMASK(2, 0)) -#define OV8865_PLL_CTRL1_REG 0x301 -#define OV8865_PLL_CTRL1_MUL_H(v) (((v) & GENMASK(9, 8)) >> 8) -#define OV8865_PLL_CTRL2_REG 0x302 -#define OV8865_PLL_CTRL2_MUL_L(v) ((v) & GENMASK(7, 0)) -#define OV8865_PLL_CTRL3_REG 0x303 -#define OV8865_PLL_CTRL3_M_DIV(v) (((v) - 1) & GENMASK(3, 0)) -#define OV8865_PLL_CTRL4_REG 0x304 -#define OV8865_PLL_CTRL4_MIPI_DIV(v) ((v) & GENMASK(1, 0)) -#define OV8865_PLL_CTRL5_REG 0x305 -#define OV8865_PLL_CTRL5_SYS_PRE_DIV(v) ((v) & GENMASK(1, 0)) -#define OV8865_PLL_CTRL6_REG 0x306 -#define OV8865_PLL_CTRL6_SYS_DIV(v) (((v) - 1) & BIT(0)) +#define CHIP_ID 0x008865 +#define OV8865_REG_CHIP_ID 0x300a -#define OV8865_PLL_CTRL8_REG 0x308 -#define OV8865_PLL_CTRL9_REG 0x309 -#define OV8865_PLL_CTRLA_REG 0x30a -#define OV8865_PLL_CTRLA_PRE_DIV_HALF(v) (((v) - 1) & BIT(0)) -#define OV8865_PLL_CTRLB_REG 0x30b -#define OV8865_PLL_CTRLB_PRE_DIV(v) ((v) & GENMASK(2, 0)) -#define OV8865_PLL_CTRLC_REG 0x30c -#define OV8865_PLL_CTRLC_MUL_H(v) (((v) & GENMASK(9, 8)) >> 8) -#define OV8865_PLL_CTRLD_REG 0x30d -#define OV8865_PLL_CTRLD_MUL_L(v) ((v) & GENMASK(7, 0)) -#define OV8865_PLL_CTRLE_REG 0x30e -#define OV8865_PLL_CTRLE_SYS_DIV(v) ((v) & GENMASK(2, 0)) -#define OV8865_PLL_CTRLF_REG 0x30f -#define OV8865_PLL_CTRLF_SYS_PRE_DIV(v) (((v) - 1) & GENMASK(3, 0)) -#define OV8865_PLL_CTRL10_REG 0x310 -#define OV8865_PLL_CTRL11_REG 0x311 -#define OV8865_PLL_CTRL12_REG 0x312 -#define OV8865_PLL_CTRL12_PRE_DIV_HALF(v) ((((v) - 1) << 4) & BIT(4)) -#define OV8865_PLL_CTRL12_DAC_DIV(v) (((v) - 1) & GENMASK(3, 0)) +#define OV8865_REG_CTRL_MODE 0x0100 +#define OV8865_MODE_SW_STANDBY 0x0 +#define OV8865_MODE_STREAMING 0x1 -#define OV8865_PLL_CTRL1B_REG 0x31b -#define OV8865_PLL_CTRL1C_REG 0x31c +#define OV8865_REG_EXPOSURE 0x3500 +#define OV8865_EXPOSURE_MIN 2 +#define OV8865_EXPOSURE_STEP 1 +#define OV8865_VTS_MAX 0x7fff -#define OV8865_PLL_CTRL1E_REG 0x31e -#define OV8865_PLL_CTRL1E_PLL1_NO_LAT BIT(3) +#define OV8865_REG_GAIN_H 0x3508 +#define OV8865_REG_GAIN_L 0x3509 +#define OV8865_GAIN_H_MASK 0x1f +#define OV8865_GAIN_H_SHIFT 8 +#define OV8865_GAIN_L_MASK 0xff +#define OV8865_GAIN_MIN 0x80 +#define OV8865_GAIN_MAX 0x1fff +#define OV8865_GAIN_STEP 1 +#define OV8865_GAIN_DEFAULT 0x80 -#define OV8865_PAD_OEN0_REG 0x3000 +#define OV8865_REG_TEST_PATTERN 0x5e00 +#define OV8865_TEST_PATTERN_ENABLE 0x80 +#define OV8865_TEST_PATTERN_DISABLE 0x0 -#define OV8865_PAD_OEN2_REG 0x3002 +#define OV8865_REG_VTS 0x380e -#define OV8865_CLK_RST5_REG 0x3005 +#define REG_NULL 0xFFFF -#define OV8865_CHIP_ID_HH_REG 0x300a -#define OV8865_CHIP_ID_HH_VALUE 0x00 -#define OV8865_CHIP_ID_H_REG 0x300b -#define OV8865_CHIP_ID_H_VALUE 0x88 -#define OV8865_CHIP_ID_L_REG 0x300c -#define OV8865_CHIP_ID_L_VALUE 0x65 -#define OV8865_PAD_OUT2_REG 0x300d +#define OV8865_REG_VALUE_08BIT 1 +#define OV8865_REG_VALUE_16BIT 2 +#define OV8865_REG_VALUE_24BIT 3 -#define OV8865_PAD_SEL2_REG 0x3010 -#define OV8865_PAD_PK_REG 0x3011 -#define OV8865_PAD_PK_DRIVE_STRENGTH_1X (0 << 5) -#define OV8865_PAD_PK_DRIVE_STRENGTH_2X (1 << 5) -#define OV8865_PAD_PK_DRIVE_STRENGTH_3X (2 << 5) -#define OV8865_PAD_PK_DRIVE_STRENGTH_4X (3 << 5) +#define OV8865_LANES 4 +#define OV8865_BITS_PER_SAMPLE 10 -#define OV8865_PUMP_CLK_DIV_REG 0x3015 -#define OV8865_PUMP_CLK_DIV_PUMP_N(v) (((v) << 4) & GENMASK(6, 4)) -#define OV8865_PUMP_CLK_DIV_PUMP_P(v) ((v) & GENMASK(2, 0)) +#define OV8865_CHIP_REVISION_REG 0x302A -#define OV8865_MIPI_SC_CTRL0_REG 0x3018 -#define OV8865_MIPI_SC_CTRL0_LANES(v) ((((v) - 1) << 5) & \ - GENMASK(7, 5)) -#define OV8865_MIPI_SC_CTRL0_MIPI_EN BIT(4) -#define OV8865_MIPI_SC_CTRL0_UNKNOWN BIT(1) -#define OV8865_MIPI_SC_CTRL0_LANES_PD_MIPI BIT(0) -#define OV8865_MIPI_SC_CTRL1_REG 0x3019 -#define OV8865_CLK_RST0_REG 0x301a -#define OV8865_CLK_RST1_REG 0x301b -#define OV8865_CLK_RST2_REG 0x301c -#define OV8865_CLK_RST3_REG 0x301d -#define OV8865_CLK_RST4_REG 0x301e +#define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default" +#define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep" -#define OV8865_PCLK_SEL_REG 0x3020 -#define OV8865_PCLK_SEL_PCLK_DIV_MASK BIT(3) -#define OV8865_PCLK_SEL_PCLK_DIV(v) ((((v) - 1) << 3) & BIT(3)) +#define OV8865_NAME "ov8865" +#define OV8865_MEDIA_BUS_FMT MEDIA_BUS_FMT_SBGGR10_1X10 -#define OV8865_MISC_CTRL_REG 0x3021 -#define OV8865_MIPI_SC_CTRL2_REG 0x3022 -#define OV8865_MIPI_SC_CTRL2_CLK_LANES_PD_MIPI BIT(1) -#define OV8865_MIPI_SC_CTRL2_PD_MIPI_RST_SYNC BIT(0) - -#define OV8865_MIPI_BIT_SEL_REG 0x3031 -#define OV8865_MIPI_BIT_SEL(v) (((v) << 0) & GENMASK(4, 0)) -#define OV8865_CLK_SEL0_REG 0x3032 -#define OV8865_CLK_SEL0_PLL1_SYS_SEL(v) (((v) << 7) & BIT(7)) -#define OV8865_CLK_SEL1_REG 0x3033 -#define OV8865_CLK_SEL1_MIPI_EOF BIT(5) -#define OV8865_CLK_SEL1_UNKNOWN BIT(2) -#define OV8865_CLK_SEL1_PLL_SCLK_SEL_MASK BIT(1) -#define OV8865_CLK_SEL1_PLL_SCLK_SEL(v) (((v) << 1) & BIT(1)) - -#define OV8865_SCLK_CTRL_REG 0x3106 -#define OV8865_SCLK_CTRL_SCLK_DIV(v) (((v) << 4) & GENMASK(7, 4)) -#define OV8865_SCLK_CTRL_SCLK_PRE_DIV(v) (((v) << 2) & GENMASK(3, 2)) -#define OV8865_SCLK_CTRL_UNKNOWN BIT(0) - -/* Exposure/gain */ - -#define OV8865_EXPOSURE_CTRL_HH_REG 0x3500 -#define OV8865_EXPOSURE_CTRL_HH(v) (((v) & GENMASK(19, 16)) >> 16) -#define OV8865_EXPOSURE_CTRL_H_REG 0x3501 -#define OV8865_EXPOSURE_CTRL_H(v) (((v) & GENMASK(15, 8)) >> 8) -#define OV8865_EXPOSURE_CTRL_L_REG 0x3502 -#define OV8865_EXPOSURE_CTRL_L(v) ((v) & GENMASK(7, 0)) -#define OV8865_EXPOSURE_GAIN_MANUAL_REG 0x3503 -#define OV8865_INTEGRATION_TIME_MARGIN 8 - -#define OV8865_GAIN_CTRL_H_REG 0x3508 -#define OV8865_GAIN_CTRL_H(v) (((v) & GENMASK(12, 8)) >> 8) -#define OV8865_GAIN_CTRL_L_REG 0x3509 -#define OV8865_GAIN_CTRL_L(v) ((v) & GENMASK(7, 0)) - -/* Timing */ - -#define OV8865_CROP_START_X_H_REG 0x3800 -#define OV8865_CROP_START_X_H(v) (((v) & GENMASK(11, 8)) >> 8) -#define OV8865_CROP_START_X_L_REG 0x3801 -#define OV8865_CROP_START_X_L(v) ((v) & GENMASK(7, 0)) -#define OV8865_CROP_START_Y_H_REG 0x3802 -#define OV8865_CROP_START_Y_H(v) (((v) & GENMASK(11, 8)) >> 8) -#define OV8865_CROP_START_Y_L_REG 0x3803 -#define OV8865_CROP_START_Y_L(v) ((v) & GENMASK(7, 0)) -#define OV8865_CROP_END_X_H_REG 0x3804 -#define OV8865_CROP_END_X_H(v) (((v) & GENMASK(11, 8)) >> 8) -#define OV8865_CROP_END_X_L_REG 0x3805 -#define OV8865_CROP_END_X_L(v) ((v) & GENMASK(7, 0)) -#define OV8865_CROP_END_Y_H_REG 0x3806 -#define OV8865_CROP_END_Y_H(v) (((v) & GENMASK(11, 8)) >> 8) -#define OV8865_CROP_END_Y_L_REG 0x3807 -#define OV8865_CROP_END_Y_L(v) ((v) & GENMASK(7, 0)) -#define OV8865_OUTPUT_SIZE_X_H_REG 0x3808 -#define OV8865_OUTPUT_SIZE_X_H(v) (((v) & GENMASK(11, 8)) >> 8) -#define OV8865_OUTPUT_SIZE_X_L_REG 0x3809 -#define OV8865_OUTPUT_SIZE_X_L(v) ((v) & GENMASK(7, 0)) -#define OV8865_OUTPUT_SIZE_Y_H_REG 0x380a -#define OV8865_OUTPUT_SIZE_Y_H(v) (((v) & GENMASK(11, 8)) >> 8) -#define OV8865_OUTPUT_SIZE_Y_L_REG 0x380b -#define OV8865_OUTPUT_SIZE_Y_L(v) ((v) & GENMASK(7, 0)) -#define OV8865_HTS_H_REG 0x380c -#define OV8865_HTS_H(v) (((v) & GENMASK(11, 8)) >> 8) -#define OV8865_HTS_L_REG 0x380d -#define OV8865_HTS_L(v) ((v) & GENMASK(7, 0)) -#define OV8865_VTS_H_REG 0x380e -#define OV8865_VTS_H(v) (((v) & GENMASK(11, 8)) >> 8) -#define OV8865_VTS_L_REG 0x380f -#define OV8865_VTS_L(v) ((v) & GENMASK(7, 0)) -#define OV8865_TIMING_MAX_VTS 0xffff -#define OV8865_TIMING_MIN_VTS 0x04 -#define OV8865_OFFSET_X_H_REG 0x3810 -#define OV8865_OFFSET_X_H(v) (((v) & GENMASK(15, 8)) >> 8) -#define OV8865_OFFSET_X_L_REG 0x3811 -#define OV8865_OFFSET_X_L(v) ((v) & GENMASK(7, 0)) -#define OV8865_OFFSET_Y_H_REG 0x3812 -#define OV8865_OFFSET_Y_H(v) (((v) & GENMASK(14, 8)) >> 8) -#define OV8865_OFFSET_Y_L_REG 0x3813 -#define OV8865_OFFSET_Y_L(v) ((v) & GENMASK(7, 0)) -#define OV8865_INC_X_ODD_REG 0x3814 -#define OV8865_INC_X_ODD(v) ((v) & GENMASK(4, 0)) -#define OV8865_INC_X_EVEN_REG 0x3815 -#define OV8865_INC_X_EVEN(v) ((v) & GENMASK(4, 0)) -#define OV8865_VSYNC_START_H_REG 0x3816 -#define OV8865_VSYNC_START_H(v) (((v) & GENMASK(15, 8)) >> 8) -#define OV8865_VSYNC_START_L_REG 0x3817 -#define OV8865_VSYNC_START_L(v) ((v) & GENMASK(7, 0)) -#define OV8865_VSYNC_END_H_REG 0x3818 -#define OV8865_VSYNC_END_H(v) (((v) & GENMASK(15, 8)) >> 8) -#define OV8865_VSYNC_END_L_REG 0x3819 -#define OV8865_VSYNC_END_L(v) ((v) & GENMASK(7, 0)) -#define OV8865_HSYNC_FIRST_H_REG 0x381a -#define OV8865_HSYNC_FIRST_H(v) (((v) & GENMASK(15, 8)) >> 8) -#define OV8865_HSYNC_FIRST_L_REG 0x381b -#define OV8865_HSYNC_FIRST_L(v) ((v) & GENMASK(7, 0)) - -#define OV8865_FORMAT1_REG 0x3820 -#define OV8865_FORMAT1_FLIP_VERT_ISP_EN BIT(2) -#define OV8865_FORMAT1_FLIP_VERT_SENSOR_EN BIT(1) -#define OV8865_FORMAT2_REG 0x3821 -#define OV8865_FORMAT2_HSYNC_EN BIT(6) -#define OV8865_FORMAT2_FST_VBIN_EN BIT(5) -#define OV8865_FORMAT2_FST_HBIN_EN BIT(4) -#define OV8865_FORMAT2_ISP_HORZ_VAR2_EN BIT(3) -#define OV8865_FORMAT2_FLIP_HORZ_ISP_EN BIT(2) -#define OV8865_FORMAT2_FLIP_HORZ_SENSOR_EN BIT(1) -#define OV8865_FORMAT2_SYNC_HBIN_EN BIT(0) - -#define OV8865_INC_Y_ODD_REG 0x382a -#define OV8865_INC_Y_ODD(v) ((v) & GENMASK(4, 0)) -#define OV8865_INC_Y_EVEN_REG 0x382b -#define OV8865_INC_Y_EVEN(v) ((v) & GENMASK(4, 0)) - -#define OV8865_ABLC_NUM_REG 0x3830 -#define OV8865_ABLC_NUM(v) ((v) & GENMASK(4, 0)) - -#define OV8865_ZLINE_NUM_REG 0x3836 -#define OV8865_ZLINE_NUM(v) ((v) & GENMASK(4, 0)) - -#define OV8865_AUTO_SIZE_CTRL_REG 0x3841 -#define OV8865_AUTO_SIZE_CTRL_OFFSET_Y_REG BIT(5) -#define OV8865_AUTO_SIZE_CTRL_OFFSET_X_REG BIT(4) -#define OV8865_AUTO_SIZE_CTRL_CROP_END_Y_REG BIT(3) -#define OV8865_AUTO_SIZE_CTRL_CROP_END_X_REG BIT(2) -#define OV8865_AUTO_SIZE_CTRL_CROP_START_Y_REG BIT(1) -#define OV8865_AUTO_SIZE_CTRL_CROP_START_X_REG BIT(0) -#define OV8865_AUTO_SIZE_X_OFFSET_H_REG 0x3842 -#define OV8865_AUTO_SIZE_X_OFFSET_L_REG 0x3843 -#define OV8865_AUTO_SIZE_Y_OFFSET_H_REG 0x3844 -#define OV8865_AUTO_SIZE_Y_OFFSET_L_REG 0x3845 -#define OV8865_AUTO_SIZE_BOUNDARIES_REG 0x3846 -#define OV8865_AUTO_SIZE_BOUNDARIES_Y(v) (((v) << 4) & GENMASK(7, 4)) -#define OV8865_AUTO_SIZE_BOUNDARIES_X(v) ((v) & GENMASK(3, 0)) - -/* PSRAM */ - -#define OV8865_PSRAM_CTRL8_REG 0x3f08 - -/* Black Level */ - -#define OV8865_BLC_CTRL0_REG 0x4000 -#define OV8865_BLC_CTRL0_TRIG_RANGE_EN BIT(7) -#define OV8865_BLC_CTRL0_TRIG_FORMAT_EN BIT(6) -#define OV8865_BLC_CTRL0_TRIG_GAIN_EN BIT(5) -#define OV8865_BLC_CTRL0_TRIG_EXPOSURE_EN BIT(4) -#define OV8865_BLC_CTRL0_TRIG_MANUAL_EN BIT(3) -#define OV8865_BLC_CTRL0_FREEZE_EN BIT(2) -#define OV8865_BLC_CTRL0_ALWAYS_EN BIT(1) -#define OV8865_BLC_CTRL0_FILTER_EN BIT(0) -#define OV8865_BLC_CTRL1_REG 0x4001 -#define OV8865_BLC_CTRL1_DITHER_EN BIT(7) -#define OV8865_BLC_CTRL1_ZERO_LINE_DIFF_EN BIT(6) -#define OV8865_BLC_CTRL1_COL_SHIFT_256 (0 << 4) -#define OV8865_BLC_CTRL1_COL_SHIFT_128 (1 << 4) -#define OV8865_BLC_CTRL1_COL_SHIFT_64 (2 << 4) -#define OV8865_BLC_CTRL1_COL_SHIFT_32 (3 << 4) -#define OV8865_BLC_CTRL1_OFFSET_LIMIT_EN BIT(2) -#define OV8865_BLC_CTRL1_COLUMN_CANCEL_EN BIT(1) -#define OV8865_BLC_CTRL2_REG 0x4002 -#define OV8865_BLC_CTRL3_REG 0x4003 -#define OV8865_BLC_CTRL4_REG 0x4004 -#define OV8865_BLC_CTRL5_REG 0x4005 -#define OV8865_BLC_CTRL6_REG 0x4006 -#define OV8865_BLC_CTRL7_REG 0x4007 -#define OV8865_BLC_CTRL8_REG 0x4008 -#define OV8865_BLC_CTRL9_REG 0x4009 -#define OV8865_BLC_CTRLA_REG 0x400a -#define OV8865_BLC_CTRLB_REG 0x400b -#define OV8865_BLC_CTRLC_REG 0x400c -#define OV8865_BLC_CTRLD_REG 0x400d -#define OV8865_BLC_CTRLD_OFFSET_TRIGGER(v) ((v) & GENMASK(7, 0)) - -#define OV8865_BLC_CTRL1F_REG 0x401f -#define OV8865_BLC_CTRL1F_RB_REVERSE BIT(3) -#define OV8865_BLC_CTRL1F_INTERPOL_X_EN BIT(2) -#define OV8865_BLC_CTRL1F_INTERPOL_Y_EN BIT(1) - -#define OV8865_BLC_ANCHOR_LEFT_START_H_REG 0x4020 -#define OV8865_BLC_ANCHOR_LEFT_START_H(v) (((v) & GENMASK(11, 8)) >> 8) -#define OV8865_BLC_ANCHOR_LEFT_START_L_REG 0x4021 -#define OV8865_BLC_ANCHOR_LEFT_START_L(v) ((v) & GENMASK(7, 0)) -#define OV8865_BLC_ANCHOR_LEFT_END_H_REG 0x4022 -#define OV8865_BLC_ANCHOR_LEFT_END_H(v) (((v) & GENMASK(11, 8)) >> 8) -#define OV8865_BLC_ANCHOR_LEFT_END_L_REG 0x4023 -#define OV8865_BLC_ANCHOR_LEFT_END_L(v) ((v) & GENMASK(7, 0)) -#define OV8865_BLC_ANCHOR_RIGHT_START_H_REG 0x4024 -#define OV8865_BLC_ANCHOR_RIGHT_START_H(v) (((v) & GENMASK(11, 8)) >> 8) -#define OV8865_BLC_ANCHOR_RIGHT_START_L_REG 0x4025 -#define OV8865_BLC_ANCHOR_RIGHT_START_L(v) ((v) & GENMASK(7, 0)) -#define OV8865_BLC_ANCHOR_RIGHT_END_H_REG 0x4026 -#define OV8865_BLC_ANCHOR_RIGHT_END_H(v) (((v) & GENMASK(11, 8)) >> 8) -#define OV8865_BLC_ANCHOR_RIGHT_END_L_REG 0x4027 -#define OV8865_BLC_ANCHOR_RIGHT_END_L(v) ((v) & GENMASK(7, 0)) - -#define OV8865_BLC_TOP_ZLINE_START_REG 0x4028 -#define OV8865_BLC_TOP_ZLINE_START(v) ((v) & GENMASK(5, 0)) -#define OV8865_BLC_TOP_ZLINE_NUM_REG 0x4029 -#define OV8865_BLC_TOP_ZLINE_NUM(v) ((v) & GENMASK(4, 0)) -#define OV8865_BLC_TOP_BLKLINE_START_REG 0x402a -#define OV8865_BLC_TOP_BLKLINE_START(v) ((v) & GENMASK(5, 0)) -#define OV8865_BLC_TOP_BLKLINE_NUM_REG 0x402b -#define OV8865_BLC_TOP_BLKLINE_NUM(v) ((v) & GENMASK(4, 0)) -#define OV8865_BLC_BOT_ZLINE_START_REG 0x402c -#define OV8865_BLC_BOT_ZLINE_START(v) ((v) & GENMASK(5, 0)) -#define OV8865_BLC_BOT_ZLINE_NUM_REG 0x402d -#define OV8865_BLC_BOT_ZLINE_NUM(v) ((v) & GENMASK(4, 0)) -#define OV8865_BLC_BOT_BLKLINE_START_REG 0x402e -#define OV8865_BLC_BOT_BLKLINE_START(v) ((v) & GENMASK(5, 0)) -#define OV8865_BLC_BOT_BLKLINE_NUM_REG 0x402f -#define OV8865_BLC_BOT_BLKLINE_NUM(v) ((v) & GENMASK(4, 0)) - -#define OV8865_BLC_OFFSET_LIMIT_REG 0x4034 -#define OV8865_BLC_OFFSET_LIMIT(v) ((v) & GENMASK(7, 0)) - -/* VFIFO */ - -#define OV8865_VFIFO_READ_START_H_REG 0x4600 -#define OV8865_VFIFO_READ_START_H(v) (((v) & GENMASK(15, 8)) >> 8) -#define OV8865_VFIFO_READ_START_L_REG 0x4601 -#define OV8865_VFIFO_READ_START_L(v) ((v) & GENMASK(7, 0)) - -/* MIPI */ - -#define OV8865_MIPI_CTRL0_REG 0x4800 -#define OV8865_MIPI_CTRL1_REG 0x4801 -#define OV8865_MIPI_CTRL2_REG 0x4802 -#define OV8865_MIPI_CTRL3_REG 0x4803 -#define OV8865_MIPI_CTRL4_REG 0x4804 -#define OV8865_MIPI_CTRL5_REG 0x4805 -#define OV8865_MIPI_CTRL6_REG 0x4806 -#define OV8865_MIPI_CTRL7_REG 0x4807 -#define OV8865_MIPI_CTRL8_REG 0x4808 - -#define OV8865_MIPI_FCNT_MAX_H_REG 0x4810 -#define OV8865_MIPI_FCNT_MAX_L_REG 0x4811 - -#define OV8865_MIPI_CTRL13_REG 0x4813 -#define OV8865_MIPI_CTRL14_REG 0x4814 -#define OV8865_MIPI_CTRL15_REG 0x4815 -#define OV8865_MIPI_EMBEDDED_DT_REG 0x4816 - -#define OV8865_MIPI_HS_ZERO_MIN_H_REG 0x4818 -#define OV8865_MIPI_HS_ZERO_MIN_L_REG 0x4819 -#define OV8865_MIPI_HS_TRAIL_MIN_H_REG 0x481a -#define OV8865_MIPI_HS_TRAIL_MIN_L_REG 0x481b -#define OV8865_MIPI_CLK_ZERO_MIN_H_REG 0x481c -#define OV8865_MIPI_CLK_ZERO_MIN_L_REG 0x481d -#define OV8865_MIPI_CLK_PREPARE_MAX_REG 0x481e -#define OV8865_MIPI_CLK_PREPARE_MIN_REG 0x481f -#define OV8865_MIPI_CLK_POST_MIN_H_REG 0x4820 -#define OV8865_MIPI_CLK_POST_MIN_L_REG 0x4821 -#define OV8865_MIPI_CLK_TRAIL_MIN_H_REG 0x4822 -#define OV8865_MIPI_CLK_TRAIL_MIN_L_REG 0x4823 -#define OV8865_MIPI_LPX_P_MIN_H_REG 0x4824 -#define OV8865_MIPI_LPX_P_MIN_L_REG 0x4825 -#define OV8865_MIPI_HS_PREPARE_MIN_REG 0x4826 -#define OV8865_MIPI_HS_PREPARE_MAX_REG 0x4827 -#define OV8865_MIPI_HS_EXIT_MIN_H_REG 0x4828 -#define OV8865_MIPI_HS_EXIT_MIN_L_REG 0x4829 -#define OV8865_MIPI_UI_HS_ZERO_MIN_REG 0x482a -#define OV8865_MIPI_UI_HS_TRAIL_MIN_REG 0x482b -#define OV8865_MIPI_UI_CLK_ZERO_MIN_REG 0x482c -#define OV8865_MIPI_UI_CLK_PREPARE_REG 0x482d -#define OV8865_MIPI_UI_CLK_POST_MIN_REG 0x482e -#define OV8865_MIPI_UI_CLK_TRAIL_MIN_REG 0x482f -#define OV8865_MIPI_UI_LPX_P_MIN_REG 0x4830 -#define OV8865_MIPI_UI_HS_PREPARE_REG 0x4831 -#define OV8865_MIPI_UI_HS_EXIT_MIN_REG 0x4832 -#define OV8865_MIPI_PKT_START_SIZE_REG 0x4833 - -#define OV8865_MIPI_PCLK_PERIOD_REG 0x4837 -#define OV8865_MIPI_LP_GPIO0_REG 0x4838 -#define OV8865_MIPI_LP_GPIO1_REG 0x4839 - -#define OV8865_MIPI_CTRL3C_REG 0x483c -#define OV8865_MIPI_LP_GPIO4_REG 0x483d - -#define OV8865_MIPI_CTRL4A_REG 0x484a -#define OV8865_MIPI_CTRL4B_REG 0x484b -#define OV8865_MIPI_CTRL4C_REG 0x484c -#define OV8865_MIPI_LANE_TEST_PATTERN_REG 0x484d -#define OV8865_MIPI_FRAME_END_DELAY_REG 0x484e -#define OV8865_MIPI_CLOCK_TEST_PATTERN_REG 0x484f -#define OV8865_MIPI_LANE_SEL01_REG 0x4850 -#define OV8865_MIPI_LANE_SEL01_LANE0(v) (((v) << 0) & GENMASK(2, 0)) -#define OV8865_MIPI_LANE_SEL01_LANE1(v) (((v) << 4) & GENMASK(6, 4)) -#define OV8865_MIPI_LANE_SEL23_REG 0x4851 -#define OV8865_MIPI_LANE_SEL23_LANE2(v) (((v) << 0) & GENMASK(2, 0)) -#define OV8865_MIPI_LANE_SEL23_LANE3(v) (((v) << 4) & GENMASK(6, 4)) - -/* ISP */ - -#define OV8865_ISP_CTRL0_REG 0x5000 -#define OV8865_ISP_CTRL0_LENC_EN BIT(7) -#define OV8865_ISP_CTRL0_WHITE_BALANCE_EN BIT(4) -#define OV8865_ISP_CTRL0_DPC_BLACK_EN BIT(2) -#define OV8865_ISP_CTRL0_DPC_WHITE_EN BIT(1) -#define OV8865_ISP_CTRL1_REG 0x5001 -#define OV8865_ISP_CTRL1_BLC_EN BIT(0) -#define OV8865_ISP_CTRL2_REG 0x5002 -#define OV8865_ISP_CTRL2_DEBUG BIT(3) -#define OV8865_ISP_CTRL2_VARIOPIXEL_EN BIT(2) -#define OV8865_ISP_CTRL2_VSYNC_LATCH_EN BIT(0) -#define OV8865_ISP_CTRL3_REG 0x5003 - -#define OV8865_ISP_GAIN_RED_H_REG 0x5018 -#define OV8865_ISP_GAIN_RED_H(v) (((v) & GENMASK(13, 6)) >> 6) -#define OV8865_ISP_GAIN_RED_L_REG 0x5019 -#define OV8865_ISP_GAIN_RED_L(v) ((v) & GENMASK(5, 0)) -#define OV8865_ISP_GAIN_GREEN_H_REG 0x501a -#define OV8865_ISP_GAIN_GREEN_H(v) (((v) & GENMASK(13, 6)) >> 6) -#define OV8865_ISP_GAIN_GREEN_L_REG 0x501b -#define OV8865_ISP_GAIN_GREEN_L(v) ((v) & GENMASK(5, 0)) -#define OV8865_ISP_GAIN_BLUE_H_REG 0x501c -#define OV8865_ISP_GAIN_BLUE_H(v) (((v) & GENMASK(13, 6)) >> 6) -#define OV8865_ISP_GAIN_BLUE_L_REG 0x501d -#define OV8865_ISP_GAIN_BLUE_L(v) ((v) & GENMASK(5, 0)) - -/* VarioPixel */ - -#define OV8865_VAP_CTRL0_REG 0x5900 -#define OV8865_VAP_CTRL1_REG 0x5901 -#define OV8865_VAP_CTRL1_HSUB_COEF(v) ((((v) - 1) << 2) & \ - GENMASK(3, 2)) -#define OV8865_VAP_CTRL1_VSUB_COEF(v) (((v) - 1) & GENMASK(1, 0)) - -/* Pre-DSP */ - -#define OV8865_PRE_CTRL0_REG 0x5e00 -#define OV8865_PRE_CTRL0_PATTERN_EN BIT(7) -#define OV8865_PRE_CTRL0_ROLLING_BAR_EN BIT(6) -#define OV8865_PRE_CTRL0_TRANSPARENT_MODE BIT(5) -#define OV8865_PRE_CTRL0_SQUARES_BW_MODE BIT(4) -#define OV8865_PRE_CTRL0_PATTERN_COLOR_BARS 0 -#define OV8865_PRE_CTRL0_PATTERN_RANDOM_DATA 1 -#define OV8865_PRE_CTRL0_PATTERN_COLOR_SQUARES 2 -#define OV8865_PRE_CTRL0_PATTERN_BLACK 3 - -/* Pixel Array */ - -#define OV8865_NATIVE_WIDTH 3296 -#define OV8865_NATIVE_HEIGHT 2528 -#define OV8865_ACTIVE_START_LEFT 16 -#define OV8865_ACTIVE_START_TOP 40 -#define OV8865_ACTIVE_WIDTH 3264 -#define OV8865_ACTIVE_HEIGHT 2448 - -/* Macros */ - -#define ov8865_subdev_sensor(s) \ - container_of(s, struct ov8865_sensor, subdev) - -#define ov8865_ctrl_subdev(c) \ - (&container_of((c)->handler, struct ov8865_sensor, \ - ctrls.handler)->subdev) - -/* Data structures */ - -struct ov8865_register_value { - u16 address; - u8 value; - unsigned int delay_ms; +static const char * const ov8865_supply_names[] = { + "avdd", /* Analog power */ + "dovdd", /* Digital I/O power */ + "dvdd", /* Digital core power */ }; -/* - * PLL1 Clock Tree: - * - * +-< EXTCLK - * | - * +-+ pll_pre_div_half (0x30a [0]) - * | - * +-+ pll_pre_div (0x300 [2:0], special values: - * | 0: 1, 1: 1.5, 3: 2.5, 4: 3, 5: 4, 7: 8) - * +-+ pll_mul (0x301 [1:0], 0x302 [7:0]) - * | - * +-+ m_div (0x303 [3:0]) - * | | - * | +-> PHY_SCLK - * | | - * | +-+ mipi_div (0x304 [1:0], special values: 0: 4, 1: 5, 2: 6, 3: 8) - * | | - * | +-+ pclk_div (0x3020 [3]) - * | | - * | +-> PCLK - * | - * +-+ sys_pre_div (0x305 [1:0], special values: 0: 3, 1: 4, 2: 5, 3: 6) - * | - * +-+ sys_div (0x306 [0]) - * | - * +-+ sys_sel (0x3032 [7], 0: PLL1, 1: PLL2) - * | - * +-+ sclk_sel (0x3033 [1], 0: sys_sel, 1: PLL2 DAC_CLK) - * | - * +-+ sclk_pre_div (0x3106 [3:2], special values: - * | 0: 1, 1: 2, 2: 4, 3: 1) - * | - * +-+ sclk_div (0x3106 [7:4], special values: 0: 1) - * | - * +-> SCLK - */ +#define OV8865_NUM_SUPPLIES ARRAY_SIZE(ov8865_supply_names) -struct ov8865_pll1_config { - unsigned int pll_pre_div_half; - unsigned int pll_pre_div; - unsigned int pll_mul; - unsigned int m_div; - unsigned int mipi_div; - unsigned int pclk_div; - unsigned int sys_pre_div; - unsigned int sys_div; +struct regval { + u16 addr; + u8 val; }; -/* - * PLL2 Clock Tree: - * - * +-< EXTCLK - * | - * +-+ pll_pre_div_half (0x312 [4]) - * | - * +-+ pll_pre_div (0x30b [2:0], special values: - * | 0: 1, 1: 1.5, 3: 2.5, 4: 3, 5: 4, 7: 8) - * +-+ pll_mul (0x30c [1:0], 0x30d [7:0]) - * | - * +-+ dac_div (0x312 [3:0]) - * | | - * | +-> DAC_CLK - * | - * +-+ sys_pre_div (0x30f [3:0]) - * | - * +-+ sys_div (0x30e [2:0], special values: - * | 0: 1, 1: 1.5, 3: 2.5, 4: 3, 5: 3.5, 6: 4, 7:5) - * | - * +-+ sys_sel (0x3032 [7], 0: PLL1, 1: PLL2) - * | - * +-+ sclk_sel (0x3033 [1], 0: sys_sel, 1: PLL2 DAC_CLK) - * | - * +-+ sclk_pre_div (0x3106 [3:2], special values: - * | 0: 1, 1: 2, 2: 4, 3: 1) - * | - * +-+ sclk_div (0x3106 [7:4], special values: 0: 1) - * | - * +-> SCLK - */ - -struct ov8865_pll2_config { - unsigned int pll_pre_div_half; - unsigned int pll_pre_div; - unsigned int pll_mul; - unsigned int dac_div; - unsigned int sys_pre_div; - unsigned int sys_div; -}; - -struct ov8865_sclk_config { - unsigned int sys_sel; - unsigned int sclk_sel; - unsigned int sclk_pre_div; - unsigned int sclk_div; -}; - -struct ov8865_pll_configs { - const struct ov8865_pll1_config *pll1_config; - const struct ov8865_pll2_config *pll2_config_native; - const struct ov8865_pll2_config *pll2_config_binning; -}; - -/* Clock rate */ - -enum extclk_rate { - OV8865_19_2_MHZ, - OV8865_24_MHZ, - OV8865_NUM_SUPPORTED_RATES -}; - -static const unsigned long supported_extclk_rates[] = { - [OV8865_19_2_MHZ] = 19200000, - [OV8865_24_MHZ] = 24000000, -}; - -/* - * General formulas for (array-centered) mode calculation: - * - photo_array_width = 3296 - * - crop_start_x = (photo_array_width - output_size_x) / 2 - * - crop_end_x = crop_start_x + offset_x + output_size_x - 1 - * - * - photo_array_height = 2480 - * - crop_start_y = (photo_array_height - output_size_y) / 2 - * - crop_end_y = crop_start_y + offset_y + output_size_y - 1 - */ - struct ov8865_mode { - unsigned int crop_start_x; - unsigned int offset_x; - unsigned int output_size_x; - unsigned int crop_end_x; - unsigned int hts; - - unsigned int crop_start_y; - unsigned int offset_y; - unsigned int output_size_y; - unsigned int crop_end_y; - unsigned int vts; - - /* With auto size, only output and total sizes need to be set. */ - bool size_auto; - unsigned int size_auto_boundary_x; - unsigned int size_auto_boundary_y; - - bool binning_x; - bool binning_y; - bool variopixel; - unsigned int variopixel_hsub_coef; - unsigned int variopixel_vsub_coef; - - /* Bits for the format register, used for binning. */ - bool sync_hbin; - bool horz_var2; - - unsigned int inc_x_odd; - unsigned int inc_x_even; - unsigned int inc_y_odd; - unsigned int inc_y_even; - - unsigned int vfifo_read_start; - - unsigned int ablc_num; - unsigned int zline_num; - - unsigned int blc_top_zero_line_start; - unsigned int blc_top_zero_line_num; - unsigned int blc_top_black_line_start; - unsigned int blc_top_black_line_num; - - unsigned int blc_bottom_zero_line_start; - unsigned int blc_bottom_zero_line_num; - unsigned int blc_bottom_black_line_start; - unsigned int blc_bottom_black_line_num; - - u8 blc_col_shift_mask; - - unsigned int blc_anchor_left_start; - unsigned int blc_anchor_left_end; - unsigned int blc_anchor_right_start; - unsigned int blc_anchor_right_end; - - bool pll2_binning; - - const struct ov8865_register_value *register_values; - unsigned int register_values_count; + u32 bus_fmt; + u32 width; + u32 height; + struct v4l2_fract max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + u32 mipi_freq_idx; + u32 bpp; + const struct regval *reg_list; + u32 vc[PAD_MAX]; }; -struct ov8865_state { - const struct ov8865_mode *mode; - u32 mbus_code; +struct ov8865 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *power_gpio; + struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; + struct regulator_bulk_data supplies[OV8865_NUM_SUPPLIES]; - bool streaming; + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_sleep; + + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *anal_gain; + struct v4l2_ctrl *digi_gain; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *test_pattern; + struct v4l2_ctrl *h_flip; + struct v4l2_ctrl *v_flip; + struct mutex mutex; + bool streaming; + const struct ov8865_mode *cur_mode; + + unsigned int lane_num; + unsigned int cfg_num; + + bool power_on; + + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; + struct rkmodule_inf module_inf; }; -struct ov8865_ctrls { - struct v4l2_ctrl *link_freq; - struct v4l2_ctrl *pixel_rate; - struct v4l2_ctrl *hblank; - struct v4l2_ctrl *vblank; - struct v4l2_ctrl *exposure; - - struct v4l2_ctrl_handler handler; -}; - -struct ov8865_sensor { - struct device *dev; - struct i2c_client *i2c_client; - struct gpio_desc *reset; - struct gpio_desc *powerdown; - struct regulator *avdd; - struct regulator *dvdd; - struct regulator *dovdd; - - unsigned long extclk_rate; - const struct ov8865_pll_configs *pll_configs; - struct clk *extclk; - - struct v4l2_fwnode_endpoint endpoint; - struct v4l2_subdev subdev; - struct media_pad pad; - - struct mutex mutex; - - struct ov8865_state state; - struct ov8865_ctrls ctrls; -}; - -/* Static definitions */ +#define to_ov8865(sd) container_of(sd, struct ov8865, subdev) /* - * PHY_SCLK = 720 MHz - * MIPI_PCLK = 90 MHz + * Xclk 24Mhz */ - -static const struct ov8865_pll1_config ov8865_pll1_config_native_19_2mhz = { - .pll_pre_div_half = 1, - .pll_pre_div = 2, - .pll_mul = 75, - .m_div = 1, - .mipi_div = 3, - .pclk_div = 1, - .sys_pre_div = 1, - .sys_div = 2, -}; - -static const struct ov8865_pll1_config ov8865_pll1_config_native_24mhz = { - .pll_pre_div_half = 1, - .pll_pre_div = 0, - .pll_mul = 30, - .m_div = 1, - .mipi_div = 3, - .pclk_div = 1, - .sys_pre_div = 1, - .sys_div = 2, +static const struct regval ov8865_global_regs[] = { + {0x0103, 0x01}, + {0x0100, 0x00}, + {0x3638, 0xff}, + {0x0302, 0x1e}, + {0x0303, 0x00}, + {0x0304, 0x03}, + {0x030e, 0x00}, + {0x030f, 0x09}, + {0x0312, 0x01}, + {0x031e, 0x0c}, + {0x3015, 0x01}, + {0x3018, 0x72}, + {0x3020, 0x93}, + {0x3022, 0x01}, + {0x3031, 0x0a}, + {0x3106, 0x01}, + {0x3305, 0xf1}, + {0x3308, 0x00}, + {0x3309, 0x28}, + {0x330a, 0x00}, + {0x330b, 0x20}, + {0x330c, 0x00}, + {0x330d, 0x00}, + {0x330e, 0x00}, + {0x330f, 0x40}, + {0x3307, 0x04}, + {0x3604, 0x04}, + {0x3602, 0x30}, + {0x3605, 0x00}, + {0x3607, 0x20}, + {0x3608, 0x11}, + {0x3609, 0x68}, + {0x360a, 0x40}, + {0x360c, 0xdd}, + {0x360e, 0x0c}, + {0x3610, 0x07}, + {0x3612, 0x86}, + {0x3613, 0x58}, + {0x3614, 0x28}, + {0x3617, 0x40}, + {0x3618, 0x5a}, + {0x3619, 0x9b}, + {0x361c, 0x00}, + {0x361d, 0x60}, + {0x3631, 0x60}, + {0x3633, 0x10}, + {0x3634, 0x10}, + {0x3635, 0x10}, + {0x3636, 0x10}, + {0x3641, 0x55}, + {0x3646, 0x86}, + {0x3647, 0x27}, + {0x364a, 0x1b}, + {0x3500, 0x00}, + {0x3501, 0x4c}, + {0x3502, 0x00}, + {0x3503, 0x00}, + {0x3508, 0x02}, + {0x3509, 0x00}, + {0x3700, 0x24}, + {0x3701, 0x0c}, + {0x3702, 0x28}, + {0x3703, 0x19}, + {0x3704, 0x14}, + {0x3705, 0x00}, + {0x3706, 0x38}, + {0x3707, 0x04}, + {0x3708, 0x24}, + {0x3709, 0x40}, + {0x370a, 0x00}, + {0x370b, 0xb8}, + {0x370c, 0x04}, + {0x3718, 0x12}, + {0x3719, 0x31}, + {0x3712, 0x42}, + {0x3714, 0x12}, + {0x371e, 0x19}, + {0x371f, 0x40}, + {0x3720, 0x05}, + {0x3721, 0x05}, + {0x3724, 0x02}, + {0x3725, 0x02}, + {0x3726, 0x06}, + {0x3728, 0x05}, + {0x3729, 0x02}, + {0x372a, 0x03}, + {0x372b, 0x53}, + {0x372c, 0xa3}, + {0x372d, 0x53}, + {0x372e, 0x06}, + {0x372f, 0x10}, + {0x3730, 0x01}, + {0x3731, 0x06}, + {0x3732, 0x14}, + {0x3733, 0x10}, + {0x3734, 0x40}, + {0x3736, 0x20}, + {0x373a, 0x02}, + {0x373b, 0x0c}, + {0x373c, 0x0a}, + {0x373e, 0x03}, + {0x3755, 0x40}, + {0x3758, 0x00}, + {0x3759, 0x4c}, + {0x375a, 0x06}, + {0x375b, 0x13}, + {0x375c, 0x40}, + {0x375d, 0x02}, + {0x375e, 0x00}, + {0x375f, 0x14}, + {0x3767, 0x1c}, + {0x3768, 0x04}, + {0x3769, 0x20}, + {0x376c, 0xc0}, + {0x376d, 0xc0}, + {0x376a, 0x08}, + {0x3761, 0x00}, + {0x3762, 0x00}, + {0x3763, 0x00}, + {0x3766, 0xff}, + {0x376b, 0x42}, + {0x3772, 0x23}, + {0x3773, 0x02}, + {0x3774, 0x16}, + {0x3775, 0x12}, + {0x3776, 0x08}, + {0x37a0, 0x44}, + {0x37a1, 0x3d}, + {0x37a2, 0x3d}, + {0x37a3, 0x01}, + {0x37a4, 0x00}, + {0x37a5, 0x08}, + {0x37a6, 0x00}, + {0x37a7, 0x44}, + {0x37a8, 0x58}, + {0x37a9, 0x58}, + {0x3760, 0x00}, + {0x376f, 0x01}, + {0x37aa, 0x44}, + {0x37ab, 0x2e}, + {0x37ac, 0x2e}, + {0x37ad, 0x33}, + {0x37ae, 0x0d}, + {0x37af, 0x0d}, + {0x37b0, 0x00}, + {0x37b1, 0x00}, + {0x37b2, 0x00}, + {0x37b3, 0x42}, + {0x37b4, 0x42}, + {0x37b5, 0x33}, + {0x37b6, 0x00}, + {0x37b7, 0x00}, + {0x37b8, 0x00}, + {0x37b9, 0xff}, + {0x3800, 0x00}, + {0x3801, 0x0c}, + {0x3802, 0x00}, + {0x3803, 0x0c}, + {0x3804, 0x0c}, + {0x3805, 0xd3}, + {0x3806, 0x09}, + {0x3807, 0xa3}, + {0x3808, 0x06}, + {0x3809, 0x60}, + {0x380a, 0x04}, + {0x380b, 0xc8}, + {0x380c, 0x07}, + {0x380d, 0x83}, + {0x380e, 0x04}, + {0x380f, 0xe0}, + {0x3810, 0x00}, + {0x3811, 0x04}, + {0x3813, 0x04}, + {0x3814, 0x03}, + {0x3815, 0x01}, + {0x3820, 0x00}, + {0x3821, 0x67}, + {0x382a, 0x03}, + {0x382b, 0x01}, + {0x3830, 0x08}, + {0x3836, 0x02}, + {0x3837, 0x18}, + {0x3841, 0xff}, + {0x3846, 0x88}, + {0x3d85, 0x06}, + {0x3d8c, 0x75}, + {0x3d8d, 0xef}, + {0x3f08, 0x0b}, + {0x4000, 0xf1}, + {0x4001, 0x14}, + {0x4005, 0x10}, + {0x400b, 0x0c}, + {0x400d, 0x10}, + {0x401b, 0x00}, + {0x401d, 0x00}, + {0x4020, 0x01}, + {0x4021, 0x20}, + {0x4022, 0x01}, + {0x4023, 0x9f}, + {0x4024, 0x03}, + {0x4025, 0xe0}, + {0x4026, 0x04}, + {0x4027, 0x5f}, + {0x4028, 0x00}, + {0x4029, 0x02}, + {0x402a, 0x04}, + {0x402b, 0x04}, + {0x402c, 0x02}, + {0x402d, 0x02}, + {0x402e, 0x08}, + {0x402f, 0x02}, + {0x401f, 0x00}, + {0x4034, 0x3f}, + {0x4300, 0xff}, + {0x4301, 0x00}, + {0x4302, 0x0f}, + {0x4500, 0x40}, + {0x4503, 0x10}, + {0x4601, 0x74}, + {0x481f, 0x32}, + {0x4837, 0x16}, + {0x4850, 0x10}, + {0x4851, 0x32}, + {0x4b00, 0x2a}, + {0x4b0d, 0x00}, + {0x4d00, 0x04}, + {0x4d01, 0x18}, + {0x4d02, 0xc3}, + {0x4d03, 0xff}, + {0x4d04, 0xff}, + {0x4d05, 0xff}, + {0x5000, 0x96}, + {0x5001, 0x01}, + {0x5002, 0x08}, + {0x5901, 0x00}, + {0x5e00, 0x00}, + {0x5e01, 0x41}, + {0x0100, 0x01}, + {0x5b00, 0x02}, + {0x5b01, 0xd0}, + {0x5b02, 0x03}, + {0x5b03, 0xff}, + {0x5b05, 0x6c}, + {0x5780, 0xfc}, + {0x5781, 0xdf}, + {0x5782, 0x3f}, + {0x5783, 0x08}, + {0x5784, 0x0c}, + {0x5786, 0x20}, + {0x5787, 0x40}, + {0x5788, 0x08}, + {0x5789, 0x08}, + {0x578a, 0x02}, + {0x578b, 0x01}, + {0x578c, 0x01}, + {0x578d, 0x0c}, + {0x578e, 0x02}, + {0x578f, 0x01}, + {0x5790, 0x01}, + {0x5800, 0x1d}, + {0x5801, 0x0e}, + {0x5802, 0x0c}, + {0x5803, 0x0c}, + {0x5804, 0x0f}, + {0x5805, 0x22}, + {0x5806, 0x0a}, + {0x5807, 0x06}, + {0x5808, 0x05}, + {0x5809, 0x05}, + {0x580a, 0x07}, + {0x580b, 0x0a}, + {0x580c, 0x06}, + {0x580d, 0x02}, + {0x580e, 0x00}, + {0x580f, 0x00}, + {0x5810, 0x03}, + {0x5811, 0x07}, + {0x5812, 0x06}, + {0x5813, 0x02}, + {0x5814, 0x00}, + {0x5815, 0x00}, + {0x5816, 0x03}, + {0x5817, 0x07}, + {0x5818, 0x09}, + {0x5819, 0x06}, + {0x581a, 0x04}, + {0x581b, 0x04}, + {0x581c, 0x06}, + {0x581d, 0x0a}, + {0x581e, 0x19}, + {0x581f, 0x0d}, + {0x5820, 0x0b}, + {0x5821, 0x0b}, + {0x5822, 0x0e}, + {0x5823, 0x22}, + {0x5824, 0x23}, + {0x5825, 0x28}, + {0x5826, 0x29}, + {0x5827, 0x27}, + {0x5828, 0x13}, + {0x5829, 0x26}, + {0x582a, 0x33}, + {0x582b, 0x32}, + {0x582c, 0x33}, + {0x582d, 0x16}, + {0x582e, 0x14}, + {0x582f, 0x30}, + {0x5830, 0x31}, + {0x5831, 0x30}, + {0x5832, 0x15}, + {0x5833, 0x26}, + {0x5834, 0x23}, + {0x5835, 0x21}, + {0x5836, 0x23}, + {0x5837, 0x05}, + {0x5838, 0x36}, + {0x5839, 0x27}, + {0x583a, 0x28}, + {0x583b, 0x26}, + {0x583c, 0x24}, + {0x583d, 0xdf}, + {REG_NULL, 0x00}, }; /* - * DAC_CLK = 360 MHz - * SCLK = 144 MHz + * XVCLK=24Mhz, sysclk=144Mhz + * MIPI 4 lane, 720Mbps/lane */ - -static const struct ov8865_pll2_config ov8865_pll2_config_native_19_2mhz = { - .pll_pre_div_half = 1, - .pll_pre_div = 5, - .pll_mul = 75, - .dac_div = 1, - .sys_pre_div = 1, - .sys_div = 3, +static const struct regval ov8865_3264x2448_30fps_regs[] = { + {0x0100, 0x00}, + {0x030f, 0x04}, + {0x3018, 0x72}, + {0x3106, 0x01}, + {0x3501, 0x98}, + {0x3502, 0x60}, + {0x3700, 0x48}, + {0x3701, 0x18}, + {0x3702, 0x50}, + {0x3703, 0x32}, + {0x3704, 0x28}, + {0x3706, 0x70}, + {0x3707, 0x08}, + {0x3708, 0x48}, + {0x3709, 0x80}, + {0x370a, 0x01}, + {0x370b, 0x70}, + {0x370c, 0x07}, + {0x3718, 0x14}, + {0x3712, 0x44}, + {0x371e, 0x31}, + {0x371f, 0x7f}, + {0x3720, 0x0a}, + {0x3721, 0x0a}, + {0x3724, 0x04}, + {0x3725, 0x04}, + {0x3726, 0x0c}, + {0x3728, 0x0a}, + {0x3729, 0x03}, + {0x372a, 0x06}, + {0x372b, 0xa6}, + {0x372c, 0xa6}, + {0x372d, 0xa6}, + {0x372e, 0x0c}, + {0x372f, 0x20}, + {0x3730, 0x02}, + {0x3731, 0x0c}, + {0x3732, 0x28}, + {0x3736, 0x30}, + {0x373a, 0x04}, + {0x373b, 0x18}, + {0x373c, 0x14}, + {0x373e, 0x06}, + {0x375a, 0x0c}, + {0x375b, 0x26}, + {0x375d, 0x04}, + {0x375f, 0x28}, + {0x3767, 0x1e}, + {0x3772, 0x46}, + {0x3773, 0x04}, + {0x3774, 0x2c}, + {0x3775, 0x13}, + {0x3776, 0x10}, + {0x37a0, 0x88}, + {0x37a1, 0x7a}, + {0x37a2, 0x7a}, + {0x37a3, 0x02}, + {0x37a5, 0x09}, + {0x37a7, 0x88}, + {0x37a8, 0xb0}, + {0x37a9, 0xb0}, + {0x37aa, 0x88}, + {0x37ab, 0x5c}, + {0x37ac, 0x5c}, + {0x37ad, 0x55}, + {0x37ae, 0x19}, + {0x37af, 0x19}, + {0x37b3, 0x84}, + {0x37b4, 0x84}, + {0x37b5, 0x66}, + {0x3808, 0x0c}, + {0x3809, 0xc0}, + {0x380a, 0x09}, + {0x380b, 0x90}, + {0x380c, 0x07}, + {0x380d, 0x98}, + {0x380e, 0x09}, + {0x380f, 0xa6}, + {0x3813, 0x02}, + {0x3814, 0x01}, + {0x3821, 0x46}, + {0x382a, 0x01}, + {0x382b, 0x01}, + {0x3830, 0x04}, + {0x3836, 0x01}, + {0x3846, 0x48}, + {0x3f08, 0x16}, + {0x4000, 0xf1}, + {0x4001, 0x04}, + {0x4020, 0x02}, + {0x4021, 0x40}, + {0x4022, 0x03}, + {0x4023, 0x3f}, + {0x4024, 0x07}, + {0x4025, 0xc0}, + {0x4026, 0x08}, + {0x4027, 0xbf}, + {0x402a, 0x04}, + {0x402b, 0x04}, + {0x402c, 0x02}, + {0x402d, 0x02}, + {0x402e, 0x08}, + {0x4500, 0x68}, + {0x4601, 0x10}, + {0x5002, 0x08}, + {0x5901, 0x00}, + {REG_NULL, 0x00}, }; -static const struct ov8865_pll2_config ov8865_pll2_config_native_24mhz = { - .pll_pre_div_half = 1, - .pll_pre_div = 0, - .pll_mul = 30, - .dac_div = 2, - .sys_pre_div = 5, - .sys_div = 0, -}; - -/* - * DAC_CLK = 360 MHz - * SCLK = 72 MHz - */ - -static const struct ov8865_pll2_config ov8865_pll2_config_binning_19_2mhz = { - .pll_pre_div_half = 1, - .pll_pre_div = 2, - .pll_mul = 75, - .dac_div = 2, - .sys_pre_div = 10, - .sys_div = 0, -}; - -static const struct ov8865_pll2_config ov8865_pll2_config_binning_24mhz = { - .pll_pre_div_half = 1, - .pll_pre_div = 0, - .pll_mul = 30, - .dac_div = 2, - .sys_pre_div = 10, - .sys_div = 0, -}; - -static const struct ov8865_pll_configs ov8865_pll_configs_19_2mhz = { - .pll1_config = &ov8865_pll1_config_native_19_2mhz, - .pll2_config_native = &ov8865_pll2_config_native_19_2mhz, - .pll2_config_binning = &ov8865_pll2_config_binning_19_2mhz, -}; - -static const struct ov8865_pll_configs ov8865_pll_configs_24mhz = { - .pll1_config = &ov8865_pll1_config_native_24mhz, - .pll2_config_native = &ov8865_pll2_config_native_24mhz, - .pll2_config_binning = &ov8865_pll2_config_binning_24mhz, -}; - -static const struct ov8865_pll_configs *ov8865_pll_configs[] = { - &ov8865_pll_configs_19_2mhz, - &ov8865_pll_configs_24mhz, -}; - -static const struct ov8865_sclk_config ov8865_sclk_config_native = { - .sys_sel = 1, - .sclk_sel = 0, - .sclk_pre_div = 0, - .sclk_div = 0, -}; - -static const struct ov8865_register_value ov8865_register_values_native[] = { - /* Sensor */ - - { 0x3700, 0x48 }, - { 0x3701, 0x18 }, - { 0x3702, 0x50 }, - { 0x3703, 0x32 }, - { 0x3704, 0x28 }, - { 0x3706, 0x70 }, - { 0x3707, 0x08 }, - { 0x3708, 0x48 }, - { 0x3709, 0x80 }, - { 0x370a, 0x01 }, - { 0x370b, 0x70 }, - { 0x370c, 0x07 }, - { 0x3718, 0x14 }, - { 0x3712, 0x44 }, - { 0x371e, 0x31 }, - { 0x371f, 0x7f }, - { 0x3720, 0x0a }, - { 0x3721, 0x0a }, - { 0x3724, 0x04 }, - { 0x3725, 0x04 }, - { 0x3726, 0x0c }, - { 0x3728, 0x0a }, - { 0x3729, 0x03 }, - { 0x372a, 0x06 }, - { 0x372b, 0xa6 }, - { 0x372c, 0xa6 }, - { 0x372d, 0xa6 }, - { 0x372e, 0x0c }, - { 0x372f, 0x20 }, - { 0x3730, 0x02 }, - { 0x3731, 0x0c }, - { 0x3732, 0x28 }, - { 0x3736, 0x30 }, - { 0x373a, 0x04 }, - { 0x373b, 0x18 }, - { 0x373c, 0x14 }, - { 0x373e, 0x06 }, - { 0x375a, 0x0c }, - { 0x375b, 0x26 }, - { 0x375d, 0x04 }, - { 0x375f, 0x28 }, - { 0x3767, 0x1e }, - { 0x3772, 0x46 }, - { 0x3773, 0x04 }, - { 0x3774, 0x2c }, - { 0x3775, 0x13 }, - { 0x3776, 0x10 }, - { 0x37a0, 0x88 }, - { 0x37a1, 0x7a }, - { 0x37a2, 0x7a }, - { 0x37a3, 0x02 }, - { 0x37a5, 0x09 }, - { 0x37a7, 0x88 }, - { 0x37a8, 0xb0 }, - { 0x37a9, 0xb0 }, - { 0x37aa, 0x88 }, - { 0x37ab, 0x5c }, - { 0x37ac, 0x5c }, - { 0x37ad, 0x55 }, - { 0x37ae, 0x19 }, - { 0x37af, 0x19 }, - { 0x37b3, 0x84 }, - { 0x37b4, 0x84 }, - { 0x37b5, 0x66 }, - - /* PSRAM */ - - { OV8865_PSRAM_CTRL8_REG, 0x16 }, - - /* ADC Sync */ - - { 0x4500, 0x68 }, -}; - -static const struct ov8865_register_value ov8865_register_values_binning[] = { - /* Sensor */ - - { 0x3700, 0x24 }, - { 0x3701, 0x0c }, - { 0x3702, 0x28 }, - { 0x3703, 0x19 }, - { 0x3704, 0x14 }, - { 0x3706, 0x38 }, - { 0x3707, 0x04 }, - { 0x3708, 0x24 }, - { 0x3709, 0x40 }, - { 0x370a, 0x00 }, - { 0x370b, 0xb8 }, - { 0x370c, 0x04 }, - { 0x3718, 0x12 }, - { 0x3712, 0x42 }, - { 0x371e, 0x19 }, - { 0x371f, 0x40 }, - { 0x3720, 0x05 }, - { 0x3721, 0x05 }, - { 0x3724, 0x02 }, - { 0x3725, 0x02 }, - { 0x3726, 0x06 }, - { 0x3728, 0x05 }, - { 0x3729, 0x02 }, - { 0x372a, 0x03 }, - { 0x372b, 0x53 }, - { 0x372c, 0xa3 }, - { 0x372d, 0x53 }, - { 0x372e, 0x06 }, - { 0x372f, 0x10 }, - { 0x3730, 0x01 }, - { 0x3731, 0x06 }, - { 0x3732, 0x14 }, - { 0x3736, 0x20 }, - { 0x373a, 0x02 }, - { 0x373b, 0x0c }, - { 0x373c, 0x0a }, - { 0x373e, 0x03 }, - { 0x375a, 0x06 }, - { 0x375b, 0x13 }, - { 0x375d, 0x02 }, - { 0x375f, 0x14 }, - { 0x3767, 0x1c }, - { 0x3772, 0x23 }, - { 0x3773, 0x02 }, - { 0x3774, 0x16 }, - { 0x3775, 0x12 }, - { 0x3776, 0x08 }, - { 0x37a0, 0x44 }, - { 0x37a1, 0x3d }, - { 0x37a2, 0x3d }, - { 0x37a3, 0x01 }, - { 0x37a5, 0x08 }, - { 0x37a7, 0x44 }, - { 0x37a8, 0x58 }, - { 0x37a9, 0x58 }, - { 0x37aa, 0x44 }, - { 0x37ab, 0x2e }, - { 0x37ac, 0x2e }, - { 0x37ad, 0x33 }, - { 0x37ae, 0x0d }, - { 0x37af, 0x0d }, - { 0x37b3, 0x42 }, - { 0x37b4, 0x42 }, - { 0x37b5, 0x33 }, - - /* PSRAM */ - - { OV8865_PSRAM_CTRL8_REG, 0x0b }, - - /* ADC Sync */ - - { 0x4500, 0x40 }, -}; - -static const struct ov8865_mode ov8865_modes[] = { - /* 3264x2448 */ +static const struct ov8865_mode supported_modes[] = { { - /* Horizontal */ - .output_size_x = 3264, - .hts = 3888, - - /* Vertical */ - .output_size_y = 2448, - .vts = 2470, - - .size_auto = true, - .size_auto_boundary_x = 8, - .size_auto_boundary_y = 4, - - /* Subsample increase */ - .inc_x_odd = 1, - .inc_x_even = 1, - .inc_y_odd = 1, - .inc_y_even = 1, - - /* VFIFO */ - .vfifo_read_start = 16, - - .ablc_num = 4, - .zline_num = 1, - - /* Black Level */ - - .blc_top_zero_line_start = 0, - .blc_top_zero_line_num = 2, - .blc_top_black_line_start = 4, - .blc_top_black_line_num = 4, - - .blc_bottom_zero_line_start = 2, - .blc_bottom_zero_line_num = 2, - .blc_bottom_black_line_start = 8, - .blc_bottom_black_line_num = 2, - - .blc_anchor_left_start = 576, - .blc_anchor_left_end = 831, - .blc_anchor_right_start = 1984, - .blc_anchor_right_end = 2239, - - /* PLL */ - .pll2_binning = false, - - /* Registers */ - .register_values = ov8865_register_values_native, - .register_values_count = - ARRAY_SIZE(ov8865_register_values_native), - }, - /* 3264x1836 */ - { - /* Horizontal */ - .output_size_x = 3264, - .hts = 3888, - - /* Vertical */ - .output_size_y = 1836, - .vts = 2470, - - .size_auto = true, - .size_auto_boundary_x = 8, - .size_auto_boundary_y = 4, - - /* Subsample increase */ - .inc_x_odd = 1, - .inc_x_even = 1, - .inc_y_odd = 1, - .inc_y_even = 1, - - /* VFIFO */ - .vfifo_read_start = 16, - - .ablc_num = 4, - .zline_num = 1, - - /* Black Level */ - - .blc_top_zero_line_start = 0, - .blc_top_zero_line_num = 2, - .blc_top_black_line_start = 4, - .blc_top_black_line_num = 4, - - .blc_bottom_zero_line_start = 2, - .blc_bottom_zero_line_num = 2, - .blc_bottom_black_line_start = 8, - .blc_bottom_black_line_num = 2, - - .blc_anchor_left_start = 576, - .blc_anchor_left_end = 831, - .blc_anchor_right_start = 1984, - .blc_anchor_right_end = 2239, - - /* PLL */ - .pll2_binning = false, - - /* Registers */ - .register_values = ov8865_register_values_native, - .register_values_count = - ARRAY_SIZE(ov8865_register_values_native), - }, - /* 1632x1224 */ - { - /* Horizontal */ - .output_size_x = 1632, - .hts = 1923, - - /* Vertical */ - .output_size_y = 1224, - .vts = 1248, - - .size_auto = true, - .size_auto_boundary_x = 8, - .size_auto_boundary_y = 8, - - /* Subsample increase */ - .inc_x_odd = 3, - .inc_x_even = 1, - .inc_y_odd = 3, - .inc_y_even = 1, - - /* Binning */ - .binning_y = true, - .sync_hbin = true, - - /* VFIFO */ - .vfifo_read_start = 116, - - .ablc_num = 8, - .zline_num = 2, - - /* Black Level */ - - .blc_top_zero_line_start = 0, - .blc_top_zero_line_num = 2, - .blc_top_black_line_start = 4, - .blc_top_black_line_num = 4, - - .blc_bottom_zero_line_start = 2, - .blc_bottom_zero_line_num = 2, - .blc_bottom_black_line_start = 8, - .blc_bottom_black_line_num = 2, - - .blc_anchor_left_start = 288, - .blc_anchor_left_end = 415, - .blc_anchor_right_start = 992, - .blc_anchor_right_end = 1119, - - /* PLL */ - .pll2_binning = true, - - /* Registers */ - .register_values = ov8865_register_values_binning, - .register_values_count = - ARRAY_SIZE(ov8865_register_values_binning), - }, - /* 800x600 (SVGA) */ - { - /* Horizontal */ - .output_size_x = 800, - .hts = 1250, - - /* Vertical */ - .output_size_y = 600, - .vts = 640, - - .size_auto = true, - .size_auto_boundary_x = 8, - .size_auto_boundary_y = 8, - - /* Subsample increase */ - .inc_x_odd = 3, - .inc_x_even = 1, - .inc_y_odd = 5, - .inc_y_even = 3, - - /* Binning */ - .binning_y = true, - .variopixel = true, - .variopixel_hsub_coef = 2, - .variopixel_vsub_coef = 1, - .sync_hbin = true, - .horz_var2 = true, - - /* VFIFO */ - .vfifo_read_start = 80, - - .ablc_num = 8, - .zline_num = 2, - - /* Black Level */ - - .blc_top_zero_line_start = 0, - .blc_top_zero_line_num = 2, - .blc_top_black_line_start = 2, - .blc_top_black_line_num = 2, - - .blc_bottom_zero_line_start = 0, - .blc_bottom_zero_line_num = 0, - .blc_bottom_black_line_start = 4, - .blc_bottom_black_line_num = 2, - - .blc_col_shift_mask = OV8865_BLC_CTRL1_COL_SHIFT_128, - - .blc_anchor_left_start = 288, - .blc_anchor_left_end = 415, - .blc_anchor_right_start = 992, - .blc_anchor_right_end = 1119, - - /* PLL */ - .pll2_binning = true, - - /* Registers */ - .register_values = ov8865_register_values_binning, - .register_values_count = - ARRAY_SIZE(ov8865_register_values_binning), + .width = 3264, + .height = 2448, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x09a2, + .hts_def = 0x0798 * 2, + .vts_def = 0x09a6, + .reg_list = ov8865_3264x2448_30fps_regs, + .mipi_freq_idx = 0, + .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, + .bpp = 10, + .vc[PAD0] = 0, }, }; -static const u32 ov8865_mbus_codes[] = { - MEDIA_BUS_FMT_SBGGR10_1X10, +static const s64 link_freq_menu_items[] = { + MIPI_FREQ }; -static const struct ov8865_register_value ov8865_init_sequence[] = { - /* Analog */ - - { 0x3604, 0x04 }, - { 0x3602, 0x30 }, - { 0x3605, 0x00 }, - { 0x3607, 0x20 }, - { 0x3608, 0x11 }, - { 0x3609, 0x68 }, - { 0x360a, 0x40 }, - { 0x360c, 0xdd }, - { 0x360e, 0x0c }, - { 0x3610, 0x07 }, - { 0x3612, 0x86 }, - { 0x3613, 0x58 }, - { 0x3614, 0x28 }, - { 0x3617, 0x40 }, - { 0x3618, 0x5a }, - { 0x3619, 0x9b }, - { 0x361c, 0x00 }, - { 0x361d, 0x60 }, - { 0x3631, 0x60 }, - { 0x3633, 0x10 }, - { 0x3634, 0x10 }, - { 0x3635, 0x10 }, - { 0x3636, 0x10 }, - { 0x3638, 0xff }, - { 0x3641, 0x55 }, - { 0x3646, 0x86 }, - { 0x3647, 0x27 }, - { 0x364a, 0x1b }, - - /* Sensor */ - - { 0x3700, 0x24 }, - { 0x3701, 0x0c }, - { 0x3702, 0x28 }, - { 0x3703, 0x19 }, - { 0x3704, 0x14 }, - { 0x3705, 0x00 }, - { 0x3706, 0x38 }, - { 0x3707, 0x04 }, - { 0x3708, 0x24 }, - { 0x3709, 0x40 }, - { 0x370a, 0x00 }, - { 0x370b, 0xb8 }, - { 0x370c, 0x04 }, - { 0x3718, 0x12 }, - { 0x3719, 0x31 }, - { 0x3712, 0x42 }, - { 0x3714, 0x12 }, - { 0x371e, 0x19 }, - { 0x371f, 0x40 }, - { 0x3720, 0x05 }, - { 0x3721, 0x05 }, - { 0x3724, 0x02 }, - { 0x3725, 0x02 }, - { 0x3726, 0x06 }, - { 0x3728, 0x05 }, - { 0x3729, 0x02 }, - { 0x372a, 0x03 }, - { 0x372b, 0x53 }, - { 0x372c, 0xa3 }, - { 0x372d, 0x53 }, - { 0x372e, 0x06 }, - { 0x372f, 0x10 }, - { 0x3730, 0x01 }, - { 0x3731, 0x06 }, - { 0x3732, 0x14 }, - { 0x3733, 0x10 }, - { 0x3734, 0x40 }, - { 0x3736, 0x20 }, - { 0x373a, 0x02 }, - { 0x373b, 0x0c }, - { 0x373c, 0x0a }, - { 0x373e, 0x03 }, - { 0x3755, 0x40 }, - { 0x3758, 0x00 }, - { 0x3759, 0x4c }, - { 0x375a, 0x06 }, - { 0x375b, 0x13 }, - { 0x375c, 0x40 }, - { 0x375d, 0x02 }, - { 0x375e, 0x00 }, - { 0x375f, 0x14 }, - { 0x3767, 0x1c }, - { 0x3768, 0x04 }, - { 0x3769, 0x20 }, - { 0x376c, 0xc0 }, - { 0x376d, 0xc0 }, - { 0x376a, 0x08 }, - { 0x3761, 0x00 }, - { 0x3762, 0x00 }, - { 0x3763, 0x00 }, - { 0x3766, 0xff }, - { 0x376b, 0x42 }, - { 0x3772, 0x23 }, - { 0x3773, 0x02 }, - { 0x3774, 0x16 }, - { 0x3775, 0x12 }, - { 0x3776, 0x08 }, - { 0x37a0, 0x44 }, - { 0x37a1, 0x3d }, - { 0x37a2, 0x3d }, - { 0x37a3, 0x01 }, - { 0x37a4, 0x00 }, - { 0x37a5, 0x08 }, - { 0x37a6, 0x00 }, - { 0x37a7, 0x44 }, - { 0x37a8, 0x58 }, - { 0x37a9, 0x58 }, - { 0x3760, 0x00 }, - { 0x376f, 0x01 }, - { 0x37aa, 0x44 }, - { 0x37ab, 0x2e }, - { 0x37ac, 0x2e }, - { 0x37ad, 0x33 }, - { 0x37ae, 0x0d }, - { 0x37af, 0x0d }, - { 0x37b0, 0x00 }, - { 0x37b1, 0x00 }, - { 0x37b2, 0x00 }, - { 0x37b3, 0x42 }, - { 0x37b4, 0x42 }, - { 0x37b5, 0x33 }, - { 0x37b6, 0x00 }, - { 0x37b7, 0x00 }, - { 0x37b8, 0x00 }, - { 0x37b9, 0xff }, - - /* ADC Sync */ - - { 0x4503, 0x10 }, -}; - -static const s64 ov8865_link_freq_menu[] = { - 360000000, -}; - -static const char *const ov8865_test_pattern_menu[] = { +static const char * const ov8865_test_pattern_menu[] = { "Disabled", - "Random data", - "Color bars", - "Color bars with rolling bar", - "Color squares", - "Color squares with rolling bar" + "Vertical Color Bar Type 1", + "Vertical Color Bar Type 2", + "Vertical Color Bar Type 3", + "Vertical Color Bar Type 4" }; -static const u8 ov8865_test_pattern_bits[] = { - 0, - OV8865_PRE_CTRL0_PATTERN_EN | OV8865_PRE_CTRL0_PATTERN_RANDOM_DATA, - OV8865_PRE_CTRL0_PATTERN_EN | OV8865_PRE_CTRL0_PATTERN_COLOR_BARS, - OV8865_PRE_CTRL0_PATTERN_EN | OV8865_PRE_CTRL0_ROLLING_BAR_EN | - OV8865_PRE_CTRL0_PATTERN_COLOR_BARS, - OV8865_PRE_CTRL0_PATTERN_EN | OV8865_PRE_CTRL0_PATTERN_COLOR_SQUARES, - OV8865_PRE_CTRL0_PATTERN_EN | OV8865_PRE_CTRL0_ROLLING_BAR_EN | - OV8865_PRE_CTRL0_PATTERN_COLOR_SQUARES, -}; - -/* Input/Output */ - -static int ov8865_read(struct ov8865_sensor *sensor, u16 address, u8 *value) +/* Write registers up to 4 at a time */ +static int ov8865_write_reg(struct i2c_client *client, u16 reg, + u32 len, u32 val) { - unsigned char data[2] = { address >> 8, address & 0xff }; - struct i2c_client *client = sensor->i2c_client; - int ret; + u32 buf_i, val_i; + u8 buf[6]; + u8 *val_p; + __be32 val_be; - ret = i2c_master_send(client, data, sizeof(data)); - if (ret < 0) { - dev_dbg(&client->dev, "i2c send error at address %#04x\n", - address); - return ret; - } + if (len > 4) + return -EINVAL; - ret = i2c_master_recv(client, value, 1); - if (ret < 0) { - dev_dbg(&client->dev, "i2c recv error at address %#04x\n", - address); - return ret; - } + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + + val_be = cpu_to_be32(val); + val_p = (u8 *)&val_be; + buf_i = 2; + val_i = 4 - len; + + while (val_i < 4) + buf[buf_i++] = val_p[val_i++]; + + if (i2c_master_send(client, buf, len + 2) != len + 2) + return -EIO; return 0; } -static int ov8865_write(struct ov8865_sensor *sensor, u16 address, u8 value) +static int ov8865_write_array(struct i2c_client *client, + const struct regval *regs) { - unsigned char data[3] = { address >> 8, address & 0xff, value }; - struct i2c_client *client = sensor->i2c_client; - int ret; - - ret = i2c_master_send(client, data, sizeof(data)); - if (ret < 0) { - dev_dbg(&client->dev, "i2c send error at address %#04x\n", - address); - return ret; - } - - return 0; -} - -static int ov8865_write_sequence(struct ov8865_sensor *sensor, - const struct ov8865_register_value *sequence, - unsigned int sequence_count) -{ - unsigned int i; + u32 i; int ret = 0; - for (i = 0; i < sequence_count; i++) { - ret = ov8865_write(sensor, sequence[i].address, - sequence[i].value); - if (ret) - break; + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) + ret = ov8865_write_reg(client, regs[i].addr, + OV8865_REG_VALUE_08BIT, + regs[i].val); - if (sequence[i].delay_ms) - msleep(sequence[i].delay_ms); + return ret; +} + +/* Read registers up to 4 at a time */ +static int ov8865_read_reg(struct i2c_client *client, u16 reg, + unsigned int len, u32 *val) +{ + struct i2c_msg msgs[2]; + u8 *data_be_p; + __be32 data_be = 0; + __be16 reg_addr_be = cpu_to_be16(reg); + int ret; + + if (len > 4 || !len) + return -EINVAL; + + data_be_p = (u8 *)&data_be; + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 2; + msgs[0].buf = (u8 *)®_addr_be; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_be_p[4 - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = be32_to_cpu(data_be); + + return 0; +} + +static int ov8865_get_reso_dist(const struct ov8865_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); +} + +static const struct ov8865_mode * +ov8865_find_best_fit(struct ov8865 *ov8865, + struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *framefmt = &fmt->format; + int dist; + int cur_best_fit = 0; + int cur_best_fit_dist = -1; + unsigned int i; + + for (i = 0; i < ov8865->cfg_num; i++) { + dist = ov8865_get_reso_dist(&supported_modes[i], framefmt); + if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { + cur_best_fit_dist = dist; + cur_best_fit = i; + } + } + + return &supported_modes[cur_best_fit]; +} + +static int ov8865_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct ov8865 *ov8865 = to_ov8865(sd); + const struct ov8865_mode *mode; + s64 h_blank, vblank_def; + u64 pixel_rate = 0; + u32 lane_num = OV8865_LANES; + + mutex_lock(&ov8865->mutex); + + mode = ov8865_find_best_fit(ov8865, fmt); + fmt->format.code = OV8865_MEDIA_BUS_FMT; + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; +#else + mutex_unlock(&ov8865->mutex); + return -ENOTTY; +#endif + } else { + ov8865->cur_mode = mode; + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(ov8865->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(ov8865->vblank, vblank_def, + OV8865_VTS_MAX - mode->height, + 1, vblank_def); + __v4l2_ctrl_s_ctrl(ov8865->vblank, vblank_def); + pixel_rate = (u32)link_freq_menu_items[mode->mipi_freq_idx] / + mode->bpp * 2 * lane_num; + + __v4l2_ctrl_s_ctrl_int64(ov8865->pixel_rate, + pixel_rate); + __v4l2_ctrl_s_ctrl(ov8865->link_freq, + mode->mipi_freq_idx); + } + dev_info(&ov8865->client->dev, "%s: mode->mipi_freq_idx(%d)", + __func__, mode->mipi_freq_idx); + + mutex_unlock(&ov8865->mutex); + + return 0; +} + +static int ov8865_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct ov8865 *ov8865 = to_ov8865(sd); + const struct ov8865_mode *mode = ov8865->cur_mode; + + mutex_lock(&ov8865->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); +#else + mutex_unlock(&ov8865->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = OV8865_MEDIA_BUS_FMT; + fmt->format.field = V4L2_FIELD_NONE; + } + mutex_unlock(&ov8865->mutex); + + return 0; +} + +static int ov8865_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 = OV8865_MEDIA_BUS_FMT; + + return 0; +} + +static int ov8865_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct ov8865 *ov8865 = to_ov8865(sd); + + if (fse->index >= ov8865->cfg_num) + return -EINVAL; + + if (fse->code != OV8865_MEDIA_BUS_FMT) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = supported_modes[fse->index].width; + fse->max_height = supported_modes[fse->index].height; + fse->min_height = supported_modes[fse->index].height; + + return 0; +} + +static int ov8865_enable_test_pattern(struct ov8865 *ov8865, u32 pattern) +{ + u32 val; + + if (pattern) + val = (pattern - 1) | OV8865_TEST_PATTERN_ENABLE; + else + val = OV8865_TEST_PATTERN_DISABLE; + + return ov8865_write_reg(ov8865->client, + OV8865_REG_TEST_PATTERN, + OV8865_REG_VALUE_08BIT, + val); +} + +static int ov8865_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct ov8865 *ov8865 = to_ov8865(sd); + const struct ov8865_mode *mode = ov8865->cur_mode; + + fi->interval = mode->max_fps; + + return 0; +} + +static void ov8865_get_module_inf(struct ov8865 *ov8865, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strscpy(inf->base.sensor, OV8865_NAME, sizeof(inf->base.sensor)); + strscpy(inf->base.module, ov8865->module_name, sizeof(inf->base.module)); + strscpy(inf->base.lens, ov8865->len_name, sizeof(inf->base.lens)); + +} + +static long ov8865_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct ov8865 *ov8865 = to_ov8865(sd); + long ret = 0; + u32 stream = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + ov8865_get_module_inf(ov8865, (struct rkmodule_inf *)arg); + break; + case RKMODULE_SET_QUICK_STREAM: + + stream = *((u32 *)arg); + + if (stream) + ret = ov8865_write_reg(ov8865->client, + OV8865_REG_CTRL_MODE, + OV8865_REG_VALUE_08BIT, + OV8865_MODE_STREAMING); + else + ret = ov8865_write_reg(ov8865->client, + OV8865_REG_CTRL_MODE, + OV8865_REG_VALUE_08BIT, + OV8865_MODE_SW_STANDBY); + break; + default: + ret = -ENOTTY; + break; } return ret; } -static int ov8865_update_bits(struct ov8865_sensor *sensor, u16 address, - u8 mask, u8 bits) +#ifdef CONFIG_COMPAT +static long ov8865_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) { - u8 value = 0; - int ret; + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + long ret = 0; + u32 stream = 0; - ret = ov8865_read(sensor, address, &value); - if (ret) - return ret; - - value &= ~mask; - value |= bits; - - return ov8865_write(sensor, address, value); -} - -/* Sensor */ - -static int ov8865_sw_reset(struct ov8865_sensor *sensor) -{ - return ov8865_write(sensor, OV8865_SW_RESET_REG, OV8865_SW_RESET_RESET); -} - -static int ov8865_sw_standby(struct ov8865_sensor *sensor, int standby) -{ - u8 value = 0; - - if (!standby) - value = OV8865_SW_STANDBY_STREAM_ON; - - return ov8865_write(sensor, OV8865_SW_STANDBY_REG, value); -} - -static int ov8865_chip_id_check(struct ov8865_sensor *sensor) -{ - u16 regs[] = { OV8865_CHIP_ID_HH_REG, OV8865_CHIP_ID_H_REG, - OV8865_CHIP_ID_L_REG }; - u8 values[] = { OV8865_CHIP_ID_HH_VALUE, OV8865_CHIP_ID_H_VALUE, - OV8865_CHIP_ID_L_VALUE }; - unsigned int i; - u8 value; - int ret; - - for (i = 0; i < ARRAY_SIZE(regs); i++) { - ret = ov8865_read(sensor, regs[i], &value); - if (ret < 0) + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; return ret; - - if (value != values[i]) { - dev_err(sensor->dev, - "chip id value mismatch: %#x instead of %#x\n", - value, values[i]); - return -EINVAL; } - } - return 0; -} - -static int ov8865_charge_pump_configure(struct ov8865_sensor *sensor) -{ - return ov8865_write(sensor, OV8865_PUMP_CLK_DIV_REG, - OV8865_PUMP_CLK_DIV_PUMP_P(1)); -} - -static int ov8865_mipi_configure(struct ov8865_sensor *sensor) -{ - struct v4l2_mbus_config_mipi_csi2 *bus_mipi_csi2 = - &sensor->endpoint.bus.mipi_csi2; - unsigned int lanes_count = bus_mipi_csi2->num_data_lanes; - int ret; - - ret = ov8865_write(sensor, OV8865_MIPI_SC_CTRL0_REG, - OV8865_MIPI_SC_CTRL0_LANES(lanes_count) | - OV8865_MIPI_SC_CTRL0_MIPI_EN | - OV8865_MIPI_SC_CTRL0_UNKNOWN); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_MIPI_SC_CTRL2_REG, - OV8865_MIPI_SC_CTRL2_PD_MIPI_RST_SYNC); - if (ret) - return ret; - - if (lanes_count >= 2) { - ret = ov8865_write(sensor, OV8865_MIPI_LANE_SEL01_REG, - OV8865_MIPI_LANE_SEL01_LANE0(0) | - OV8865_MIPI_LANE_SEL01_LANE1(1)); - if (ret) - return ret; - } - - if (lanes_count >= 4) { - ret = ov8865_write(sensor, OV8865_MIPI_LANE_SEL23_REG, - OV8865_MIPI_LANE_SEL23_LANE2(2) | - OV8865_MIPI_LANE_SEL23_LANE3(3)); - if (ret) - return ret; - } - - ret = ov8865_update_bits(sensor, OV8865_CLK_SEL1_REG, - OV8865_CLK_SEL1_MIPI_EOF, - OV8865_CLK_SEL1_MIPI_EOF); - if (ret) - return ret; - - /* - * This value might need to change depending on PCLK rate, - * but it's unclear how. This value seems to generally work - * while the default value was found to cause transmission errors. - */ - return ov8865_write(sensor, OV8865_MIPI_PCLK_PERIOD_REG, 0x16); -} - -static int ov8865_black_level_configure(struct ov8865_sensor *sensor) -{ - int ret; - - /* Trigger BLC on relevant events and enable filter. */ - ret = ov8865_write(sensor, OV8865_BLC_CTRL0_REG, - OV8865_BLC_CTRL0_TRIG_RANGE_EN | - OV8865_BLC_CTRL0_TRIG_FORMAT_EN | - OV8865_BLC_CTRL0_TRIG_GAIN_EN | - OV8865_BLC_CTRL0_TRIG_EXPOSURE_EN | - OV8865_BLC_CTRL0_FILTER_EN); - if (ret) - return ret; - - /* Lower BLC offset trigger threshold. */ - ret = ov8865_write(sensor, OV8865_BLC_CTRLD_REG, - OV8865_BLC_CTRLD_OFFSET_TRIGGER(16)); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_BLC_CTRL1F_REG, 0); - if (ret) - return ret; - - /* Increase BLC offset maximum limit. */ - return ov8865_write(sensor, OV8865_BLC_OFFSET_LIMIT_REG, - OV8865_BLC_OFFSET_LIMIT(63)); -} - -static int ov8865_isp_configure(struct ov8865_sensor *sensor) -{ - int ret; - - /* Disable lens correction. */ - ret = ov8865_write(sensor, OV8865_ISP_CTRL0_REG, - OV8865_ISP_CTRL0_WHITE_BALANCE_EN | - OV8865_ISP_CTRL0_DPC_BLACK_EN | - OV8865_ISP_CTRL0_DPC_WHITE_EN); - if (ret) - return ret; - - return ov8865_write(sensor, OV8865_ISP_CTRL1_REG, - OV8865_ISP_CTRL1_BLC_EN); -} - -static unsigned long ov8865_mode_pll1_rate(struct ov8865_sensor *sensor, - const struct ov8865_mode *mode) -{ - const struct ov8865_pll1_config *config; - unsigned long pll1_rate; - - config = sensor->pll_configs->pll1_config; - pll1_rate = sensor->extclk_rate * config->pll_mul / config->pll_pre_div_half; - - switch (config->pll_pre_div) { - case 0: + ret = ov8865_ioctl(sd, cmd, inf); + if (!ret) { + ret = copy_to_user(up, inf, sizeof(*inf)); + if (ret) + ret = -EFAULT; + } + kfree(inf); break; - case 1: - pll1_rate *= 3; - pll1_rate /= 2; - break; - case 3: - pll1_rate *= 5; - pll1_rate /= 2; - break; - case 4: - pll1_rate /= 3; - break; - case 5: - pll1_rate /= 4; - break; - case 7: - pll1_rate /= 8; + case RKMODULE_SET_QUICK_STREAM: + ret = copy_from_user(&stream, up, sizeof(u32)); + if (!ret) + ret = ov8865_ioctl(sd, cmd, &stream); + else + ret = -EFAULT; break; default: - pll1_rate /= config->pll_pre_div; + ret = -ENOTTY; break; } - return pll1_rate; + return ret; } +#endif -static int ov8865_mode_pll1_configure(struct ov8865_sensor *sensor, - const struct ov8865_mode *mode, - u32 mbus_code) -{ - const struct ov8865_pll1_config *config; - u8 value; - int ret; - - config = sensor->pll_configs->pll1_config; - - switch (mbus_code) { - case MEDIA_BUS_FMT_SBGGR10_1X10: - value = OV8865_MIPI_BIT_SEL(10); - break; - default: - return -EINVAL; - } - - ret = ov8865_write(sensor, OV8865_MIPI_BIT_SEL_REG, value); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_PLL_CTRLA_REG, - OV8865_PLL_CTRLA_PRE_DIV_HALF(config->pll_pre_div_half)); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_PLL_CTRL0_REG, - OV8865_PLL_CTRL0_PRE_DIV(config->pll_pre_div)); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_PLL_CTRL1_REG, - OV8865_PLL_CTRL1_MUL_H(config->pll_mul)); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_PLL_CTRL2_REG, - OV8865_PLL_CTRL2_MUL_L(config->pll_mul)); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_PLL_CTRL3_REG, - OV8865_PLL_CTRL3_M_DIV(config->m_div)); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_PLL_CTRL4_REG, - OV8865_PLL_CTRL4_MIPI_DIV(config->mipi_div)); - if (ret) - return ret; - - ret = ov8865_update_bits(sensor, OV8865_PCLK_SEL_REG, - OV8865_PCLK_SEL_PCLK_DIV_MASK, - OV8865_PCLK_SEL_PCLK_DIV(config->pclk_div)); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_PLL_CTRL5_REG, - OV8865_PLL_CTRL5_SYS_PRE_DIV(config->sys_pre_div)); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_PLL_CTRL6_REG, - OV8865_PLL_CTRL6_SYS_DIV(config->sys_div)); - if (ret) - return ret; - - return ov8865_update_bits(sensor, OV8865_PLL_CTRL1E_REG, - OV8865_PLL_CTRL1E_PLL1_NO_LAT, - OV8865_PLL_CTRL1E_PLL1_NO_LAT); -} - -static int ov8865_mode_pll2_configure(struct ov8865_sensor *sensor, - const struct ov8865_mode *mode) -{ - const struct ov8865_pll2_config *config; - int ret; - - config = mode->pll2_binning ? sensor->pll_configs->pll2_config_binning : - sensor->pll_configs->pll2_config_native; - - ret = ov8865_write(sensor, OV8865_PLL_CTRL12_REG, - OV8865_PLL_CTRL12_PRE_DIV_HALF(config->pll_pre_div_half) | - OV8865_PLL_CTRL12_DAC_DIV(config->dac_div)); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_PLL_CTRLB_REG, - OV8865_PLL_CTRLB_PRE_DIV(config->pll_pre_div)); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_PLL_CTRLC_REG, - OV8865_PLL_CTRLC_MUL_H(config->pll_mul)); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_PLL_CTRLD_REG, - OV8865_PLL_CTRLD_MUL_L(config->pll_mul)); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_PLL_CTRLF_REG, - OV8865_PLL_CTRLF_SYS_PRE_DIV(config->sys_pre_div)); - if (ret) - return ret; - - return ov8865_write(sensor, OV8865_PLL_CTRLE_REG, - OV8865_PLL_CTRLE_SYS_DIV(config->sys_div)); -} - -static int ov8865_mode_sclk_configure(struct ov8865_sensor *sensor, - const struct ov8865_mode *mode) -{ - const struct ov8865_sclk_config *config = &ov8865_sclk_config_native; - int ret; - - ret = ov8865_write(sensor, OV8865_CLK_SEL0_REG, - OV8865_CLK_SEL0_PLL1_SYS_SEL(config->sys_sel)); - if (ret) - return ret; - - ret = ov8865_update_bits(sensor, OV8865_CLK_SEL1_REG, - OV8865_CLK_SEL1_PLL_SCLK_SEL_MASK, - OV8865_CLK_SEL1_PLL_SCLK_SEL(config->sclk_sel)); - if (ret) - return ret; - - return ov8865_write(sensor, OV8865_SCLK_CTRL_REG, - OV8865_SCLK_CTRL_UNKNOWN | - OV8865_SCLK_CTRL_SCLK_DIV(config->sclk_div) | - OV8865_SCLK_CTRL_SCLK_PRE_DIV(config->sclk_pre_div)); -} - -static int ov8865_mode_binning_configure(struct ov8865_sensor *sensor, - const struct ov8865_mode *mode) -{ - unsigned int variopixel_hsub_coef, variopixel_vsub_coef; - u8 value; - int ret; - - ret = ov8865_write(sensor, OV8865_FORMAT1_REG, 0); - if (ret) - return ret; - - value = OV8865_FORMAT2_HSYNC_EN; - - if (mode->binning_x) - value |= OV8865_FORMAT2_FST_HBIN_EN; - - if (mode->binning_y) - value |= OV8865_FORMAT2_FST_VBIN_EN; - - if (mode->sync_hbin) - value |= OV8865_FORMAT2_SYNC_HBIN_EN; - - if (mode->horz_var2) - value |= OV8865_FORMAT2_ISP_HORZ_VAR2_EN; - - ret = ov8865_write(sensor, OV8865_FORMAT2_REG, value); - if (ret) - return ret; - - ret = ov8865_update_bits(sensor, OV8865_ISP_CTRL2_REG, - OV8865_ISP_CTRL2_VARIOPIXEL_EN, - mode->variopixel ? - OV8865_ISP_CTRL2_VARIOPIXEL_EN : 0); - if (ret) - return ret; - - if (mode->variopixel) { - /* VarioPixel coefs needs to be > 1. */ - variopixel_hsub_coef = mode->variopixel_hsub_coef; - variopixel_vsub_coef = mode->variopixel_vsub_coef; - } else { - variopixel_hsub_coef = 1; - variopixel_vsub_coef = 1; - } - - ret = ov8865_write(sensor, OV8865_VAP_CTRL1_REG, - OV8865_VAP_CTRL1_HSUB_COEF(variopixel_hsub_coef) | - OV8865_VAP_CTRL1_VSUB_COEF(variopixel_vsub_coef)); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_INC_X_ODD_REG, - OV8865_INC_X_ODD(mode->inc_x_odd)); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_INC_X_EVEN_REG, - OV8865_INC_X_EVEN(mode->inc_x_even)); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_INC_Y_ODD_REG, - OV8865_INC_Y_ODD(mode->inc_y_odd)); - if (ret) - return ret; - - return ov8865_write(sensor, OV8865_INC_Y_EVEN_REG, - OV8865_INC_Y_EVEN(mode->inc_y_even)); -} - -static int ov8865_mode_black_level_configure(struct ov8865_sensor *sensor, - const struct ov8865_mode *mode) +static int __ov8865_start_stream(struct ov8865 *ov8865) { int ret; - /* Note that a zero value for blc_col_shift_mask is the default 256. */ - ret = ov8865_write(sensor, OV8865_BLC_CTRL1_REG, - mode->blc_col_shift_mask | - OV8865_BLC_CTRL1_OFFSET_LIMIT_EN); + ret = ov8865_write_array(ov8865->client, ov8865_global_regs); if (ret) return ret; - /* BLC top zero line */ - - ret = ov8865_write(sensor, OV8865_BLC_TOP_ZLINE_START_REG, - OV8865_BLC_TOP_ZLINE_START(mode->blc_top_zero_line_start)); + ret = ov8865_write_array(ov8865->client, ov8865->cur_mode->reg_list); if (ret) return ret; - ret = ov8865_write(sensor, OV8865_BLC_TOP_ZLINE_NUM_REG, - OV8865_BLC_TOP_ZLINE_NUM(mode->blc_top_zero_line_num)); + /* In case these controls are set before streaming */ + mutex_unlock(&ov8865->mutex); + ret = v4l2_ctrl_handler_setup(&ov8865->ctrl_handler); + mutex_lock(&ov8865->mutex); if (ret) return ret; - /* BLC top black line */ - - ret = ov8865_write(sensor, OV8865_BLC_TOP_BLKLINE_START_REG, - OV8865_BLC_TOP_BLKLINE_START(mode->blc_top_black_line_start)); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_BLC_TOP_BLKLINE_NUM_REG, - OV8865_BLC_TOP_BLKLINE_NUM(mode->blc_top_black_line_num)); - if (ret) - return ret; - - /* BLC bottom zero line */ - - ret = ov8865_write(sensor, OV8865_BLC_BOT_ZLINE_START_REG, - OV8865_BLC_BOT_ZLINE_START(mode->blc_bottom_zero_line_start)); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_BLC_BOT_ZLINE_NUM_REG, - OV8865_BLC_BOT_ZLINE_NUM(mode->blc_bottom_zero_line_num)); - if (ret) - return ret; - - /* BLC bottom black line */ - - ret = ov8865_write(sensor, OV8865_BLC_BOT_BLKLINE_START_REG, - OV8865_BLC_BOT_BLKLINE_START(mode->blc_bottom_black_line_start)); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_BLC_BOT_BLKLINE_NUM_REG, - OV8865_BLC_BOT_BLKLINE_NUM(mode->blc_bottom_black_line_num)); - if (ret) - return ret; - - /* BLC anchor */ - - ret = ov8865_write(sensor, OV8865_BLC_ANCHOR_LEFT_START_H_REG, - OV8865_BLC_ANCHOR_LEFT_START_H(mode->blc_anchor_left_start)); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_BLC_ANCHOR_LEFT_START_L_REG, - OV8865_BLC_ANCHOR_LEFT_START_L(mode->blc_anchor_left_start)); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_BLC_ANCHOR_LEFT_END_H_REG, - OV8865_BLC_ANCHOR_LEFT_END_H(mode->blc_anchor_left_end)); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_BLC_ANCHOR_LEFT_END_L_REG, - OV8865_BLC_ANCHOR_LEFT_END_L(mode->blc_anchor_left_end)); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_BLC_ANCHOR_RIGHT_START_H_REG, - OV8865_BLC_ANCHOR_RIGHT_START_H(mode->blc_anchor_right_start)); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_BLC_ANCHOR_RIGHT_START_L_REG, - OV8865_BLC_ANCHOR_RIGHT_START_L(mode->blc_anchor_right_start)); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_BLC_ANCHOR_RIGHT_END_H_REG, - OV8865_BLC_ANCHOR_RIGHT_END_H(mode->blc_anchor_right_end)); - if (ret) - return ret; - - return ov8865_write(sensor, OV8865_BLC_ANCHOR_RIGHT_END_L_REG, - OV8865_BLC_ANCHOR_RIGHT_END_L(mode->blc_anchor_right_end)); + return ov8865_write_reg(ov8865->client, + OV8865_REG_CTRL_MODE, + OV8865_REG_VALUE_08BIT, + OV8865_MODE_STREAMING); } -static int ov8865_mode_configure(struct ov8865_sensor *sensor, - const struct ov8865_mode *mode, u32 mbus_code) +static int __ov8865_stop_stream(struct ov8865 *ov8865) { - int ret; - - /* Output Size X */ - - ret = ov8865_write(sensor, OV8865_OUTPUT_SIZE_X_H_REG, - OV8865_OUTPUT_SIZE_X_H(mode->output_size_x)); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_OUTPUT_SIZE_X_L_REG, - OV8865_OUTPUT_SIZE_X_L(mode->output_size_x)); - if (ret) - return ret; - - /* Horizontal Total Size */ - - ret = ov8865_write(sensor, OV8865_HTS_H_REG, OV8865_HTS_H(mode->hts)); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_HTS_L_REG, OV8865_HTS_L(mode->hts)); - if (ret) - return ret; - - /* Output Size Y */ - - ret = ov8865_write(sensor, OV8865_OUTPUT_SIZE_Y_H_REG, - OV8865_OUTPUT_SIZE_Y_H(mode->output_size_y)); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_OUTPUT_SIZE_Y_L_REG, - OV8865_OUTPUT_SIZE_Y_L(mode->output_size_y)); - if (ret) - return ret; - - /* Vertical Total Size */ - - ret = ov8865_write(sensor, OV8865_VTS_H_REG, OV8865_VTS_H(mode->vts)); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_VTS_L_REG, OV8865_VTS_L(mode->vts)); - if (ret) - return ret; - - if (mode->size_auto) { - /* Auto Size */ - - ret = ov8865_write(sensor, OV8865_AUTO_SIZE_CTRL_REG, - OV8865_AUTO_SIZE_CTRL_OFFSET_Y_REG | - OV8865_AUTO_SIZE_CTRL_OFFSET_X_REG | - OV8865_AUTO_SIZE_CTRL_CROP_END_Y_REG | - OV8865_AUTO_SIZE_CTRL_CROP_END_X_REG | - OV8865_AUTO_SIZE_CTRL_CROP_START_Y_REG | - OV8865_AUTO_SIZE_CTRL_CROP_START_X_REG); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_AUTO_SIZE_BOUNDARIES_REG, - OV8865_AUTO_SIZE_BOUNDARIES_Y(mode->size_auto_boundary_y) | - OV8865_AUTO_SIZE_BOUNDARIES_X(mode->size_auto_boundary_x)); - if (ret) - return ret; - } else { - /* Crop Start X */ - - ret = ov8865_write(sensor, OV8865_CROP_START_X_H_REG, - OV8865_CROP_START_X_H(mode->crop_start_x)); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_CROP_START_X_L_REG, - OV8865_CROP_START_X_L(mode->crop_start_x)); - if (ret) - return ret; - - /* Offset X */ - - ret = ov8865_write(sensor, OV8865_OFFSET_X_H_REG, - OV8865_OFFSET_X_H(mode->offset_x)); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_OFFSET_X_L_REG, - OV8865_OFFSET_X_L(mode->offset_x)); - if (ret) - return ret; - - /* Crop End X */ - - ret = ov8865_write(sensor, OV8865_CROP_END_X_H_REG, - OV8865_CROP_END_X_H(mode->crop_end_x)); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_CROP_END_X_L_REG, - OV8865_CROP_END_X_L(mode->crop_end_x)); - if (ret) - return ret; - - /* Crop Start Y */ - - ret = ov8865_write(sensor, OV8865_CROP_START_Y_H_REG, - OV8865_CROP_START_Y_H(mode->crop_start_y)); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_CROP_START_Y_L_REG, - OV8865_CROP_START_Y_L(mode->crop_start_y)); - if (ret) - return ret; - - /* Offset Y */ - - ret = ov8865_write(sensor, OV8865_OFFSET_Y_H_REG, - OV8865_OFFSET_Y_H(mode->offset_y)); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_OFFSET_Y_L_REG, - OV8865_OFFSET_Y_L(mode->offset_y)); - if (ret) - return ret; - - /* Crop End Y */ - - ret = ov8865_write(sensor, OV8865_CROP_END_Y_H_REG, - OV8865_CROP_END_Y_H(mode->crop_end_y)); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_CROP_END_Y_L_REG, - OV8865_CROP_END_Y_L(mode->crop_end_y)); - if (ret) - return ret; - } - - /* VFIFO */ - - ret = ov8865_write(sensor, OV8865_VFIFO_READ_START_H_REG, - OV8865_VFIFO_READ_START_H(mode->vfifo_read_start)); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_VFIFO_READ_START_L_REG, - OV8865_VFIFO_READ_START_L(mode->vfifo_read_start)); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_ABLC_NUM_REG, - OV8865_ABLC_NUM(mode->ablc_num)); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_ZLINE_NUM_REG, - OV8865_ZLINE_NUM(mode->zline_num)); - if (ret) - return ret; - - /* Binning */ - - ret = ov8865_mode_binning_configure(sensor, mode); - if (ret) - return ret; - - /* Black Level */ - - ret = ov8865_mode_black_level_configure(sensor, mode); - if (ret) - return ret; - - /* PLLs */ - - ret = ov8865_mode_pll1_configure(sensor, mode, mbus_code); - if (ret) - return ret; - - ret = ov8865_mode_pll2_configure(sensor, mode); - if (ret) - return ret; - - ret = ov8865_mode_sclk_configure(sensor, mode); - if (ret) - return ret; - - /* Extra registers */ - - if (mode->register_values) { - ret = ov8865_write_sequence(sensor, mode->register_values, - mode->register_values_count); - if (ret) - return ret; - } - - return 0; + return ov8865_write_reg(ov8865->client, + OV8865_REG_CTRL_MODE, + OV8865_REG_VALUE_08BIT, + OV8865_MODE_SW_STANDBY); } -static unsigned long ov8865_mode_mipi_clk_rate(struct ov8865_sensor *sensor, - const struct ov8865_mode *mode) +static int ov8865_s_stream(struct v4l2_subdev *sd, int on) { - const struct ov8865_pll1_config *config; - unsigned long pll1_rate; - - config = sensor->pll_configs->pll1_config; - - pll1_rate = ov8865_mode_pll1_rate(sensor, mode); - - return pll1_rate / config->m_div / 2; -} - -/* Exposure */ - -static int ov8865_exposure_configure(struct ov8865_sensor *sensor, u32 exposure) -{ - int ret; - - /* The sensor stores exposure in units of 1/16th of a line */ - exposure *= 16; - - ret = ov8865_write(sensor, OV8865_EXPOSURE_CTRL_HH_REG, - OV8865_EXPOSURE_CTRL_HH(exposure)); - if (ret) - return ret; - - ret = ov8865_write(sensor, OV8865_EXPOSURE_CTRL_H_REG, - OV8865_EXPOSURE_CTRL_H(exposure)); - if (ret) - return ret; - - return ov8865_write(sensor, OV8865_EXPOSURE_CTRL_L_REG, - OV8865_EXPOSURE_CTRL_L(exposure)); -} - -/* Gain */ - -static int ov8865_analog_gain_configure(struct ov8865_sensor *sensor, u32 gain) -{ - int ret; - - ret = ov8865_write(sensor, OV8865_GAIN_CTRL_H_REG, - OV8865_GAIN_CTRL_H(gain)); - if (ret) - return ret; - - return ov8865_write(sensor, OV8865_GAIN_CTRL_L_REG, - OV8865_GAIN_CTRL_L(gain)); -} - -/* White Balance */ - -static int ov8865_red_balance_configure(struct ov8865_sensor *sensor, - u32 red_balance) -{ - int ret; - - ret = ov8865_write(sensor, OV8865_ISP_GAIN_RED_H_REG, - OV8865_ISP_GAIN_RED_H(red_balance)); - if (ret) - return ret; - - return ov8865_write(sensor, OV8865_ISP_GAIN_RED_L_REG, - OV8865_ISP_GAIN_RED_L(red_balance)); -} - -static int ov8865_blue_balance_configure(struct ov8865_sensor *sensor, - u32 blue_balance) -{ - int ret; - - ret = ov8865_write(sensor, OV8865_ISP_GAIN_BLUE_H_REG, - OV8865_ISP_GAIN_BLUE_H(blue_balance)); - if (ret) - return ret; - - return ov8865_write(sensor, OV8865_ISP_GAIN_BLUE_L_REG, - OV8865_ISP_GAIN_BLUE_L(blue_balance)); -} - -/* Flip */ - -static int ov8865_flip_vert_configure(struct ov8865_sensor *sensor, bool enable) -{ - u8 bits = OV8865_FORMAT1_FLIP_VERT_ISP_EN | - OV8865_FORMAT1_FLIP_VERT_SENSOR_EN; - - return ov8865_update_bits(sensor, OV8865_FORMAT1_REG, bits, - enable ? bits : 0); -} - -static int ov8865_flip_horz_configure(struct ov8865_sensor *sensor, bool enable) -{ - u8 bits = OV8865_FORMAT2_FLIP_HORZ_ISP_EN | - OV8865_FORMAT2_FLIP_HORZ_SENSOR_EN; - - return ov8865_update_bits(sensor, OV8865_FORMAT2_REG, bits, - enable ? bits : 0); -} - -/* Test Pattern */ - -static int ov8865_test_pattern_configure(struct ov8865_sensor *sensor, - unsigned int index) -{ - if (index >= ARRAY_SIZE(ov8865_test_pattern_bits)) - return -EINVAL; - - return ov8865_write(sensor, OV8865_PRE_CTRL0_REG, - ov8865_test_pattern_bits[index]); -} - -/* Blanking */ - -static int ov8865_vts_configure(struct ov8865_sensor *sensor, u32 vblank) -{ - u16 vts = sensor->state.mode->output_size_y + vblank; - int ret; - - ret = ov8865_write(sensor, OV8865_VTS_H_REG, OV8865_VTS_H(vts)); - if (ret) - return ret; - - return ov8865_write(sensor, OV8865_VTS_L_REG, OV8865_VTS_L(vts)); -} - -/* State */ - -static int ov8865_state_mipi_configure(struct ov8865_sensor *sensor, - const struct ov8865_mode *mode, - u32 mbus_code) -{ - struct ov8865_ctrls *ctrls = &sensor->ctrls; - struct v4l2_mbus_config_mipi_csi2 *bus_mipi_csi2 = - &sensor->endpoint.bus.mipi_csi2; - unsigned long mipi_clk_rate; - unsigned int bits_per_sample; - unsigned int lanes_count; - unsigned int i, j; - s64 mipi_pixel_rate; - - mipi_clk_rate = ov8865_mode_mipi_clk_rate(sensor, mode); - if (!mipi_clk_rate) - return -EINVAL; - - for (i = 0; i < ARRAY_SIZE(ov8865_link_freq_menu); i++) { - s64 freq = ov8865_link_freq_menu[i]; - - if (freq == mipi_clk_rate) - break; - } - - for (j = 0; j < sensor->endpoint.nr_of_link_frequencies; j++) { - u64 freq = sensor->endpoint.link_frequencies[j]; - - if (freq == mipi_clk_rate) - break; - } - - if (i == ARRAY_SIZE(ov8865_link_freq_menu)) { - dev_err(sensor->dev, - "failed to find %lu clk rate in link freq\n", - mipi_clk_rate); - } else if (j == sensor->endpoint.nr_of_link_frequencies) { - dev_err(sensor->dev, - "failed to find %lu clk rate in endpoint link-frequencies\n", - mipi_clk_rate); - } else { - __v4l2_ctrl_s_ctrl(ctrls->link_freq, i); - } - - switch (mbus_code) { - case MEDIA_BUS_FMT_SBGGR10_1X10: - bits_per_sample = 10; - break; - default: - return -EINVAL; - } - - lanes_count = bus_mipi_csi2->num_data_lanes; - mipi_pixel_rate = mipi_clk_rate * 2 * lanes_count / bits_per_sample; - - __v4l2_ctrl_s_ctrl_int64(ctrls->pixel_rate, mipi_pixel_rate); - - return 0; -} - -static int ov8865_state_configure(struct ov8865_sensor *sensor, - const struct ov8865_mode *mode, - u32 mbus_code) -{ - int ret; - - 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; - - sensor->state.mode = mode; - sensor->state.mbus_code = mbus_code; - - return 0; -} - -static int ov8865_state_init(struct ov8865_sensor *sensor) -{ - return ov8865_state_configure(sensor, &ov8865_modes[0], - ov8865_mbus_codes[0]); -} - -/* Sensor Base */ - -static int ov8865_sensor_init(struct ov8865_sensor *sensor) -{ - int ret; - - ret = ov8865_sw_reset(sensor); - if (ret) { - dev_err(sensor->dev, "failed to perform sw reset\n"); - return ret; - } - - ret = ov8865_sw_standby(sensor, 1); - if (ret) { - dev_err(sensor->dev, "failed to set sensor standby\n"); - return ret; - } - - ret = ov8865_chip_id_check(sensor); - if (ret) { - dev_err(sensor->dev, "failed to check sensor chip id\n"); - return ret; - } - - ret = ov8865_write_sequence(sensor, ov8865_init_sequence, - ARRAY_SIZE(ov8865_init_sequence)); - if (ret) { - dev_err(sensor->dev, "failed to write init sequence\n"); - return ret; - } - - ret = ov8865_charge_pump_configure(sensor); - if (ret) { - dev_err(sensor->dev, "failed to configure pad\n"); - return ret; - } - - ret = ov8865_mipi_configure(sensor); - if (ret) { - dev_err(sensor->dev, "failed to configure MIPI\n"); - return ret; - } - - ret = ov8865_isp_configure(sensor); - if (ret) { - dev_err(sensor->dev, "failed to configure ISP\n"); - return ret; - } - - ret = ov8865_black_level_configure(sensor); - if (ret) { - dev_err(sensor->dev, "failed to configure black level\n"); - return ret; - } - - /* Configure current mode. */ - ret = ov8865_state_configure(sensor, sensor->state.mode, - sensor->state.mbus_code); - if (ret) { - dev_err(sensor->dev, "failed to configure state\n"); - return ret; - } - - return 0; -} - -static int ov8865_sensor_power(struct ov8865_sensor *sensor, bool on) -{ - /* Keep initialized to zero for disable label. */ + struct ov8865 *ov8865 = to_ov8865(sd); + struct i2c_client *client = ov8865->client; int ret = 0; + dev_info(&client->dev, "%s: on: %d, %dx%d@%d\n", __func__, on, + ov8865->cur_mode->width, + ov8865->cur_mode->height, + DIV_ROUND_CLOSEST(ov8865->cur_mode->max_fps.denominator, + ov8865->cur_mode->max_fps.numerator)); + + mutex_lock(&ov8865->mutex); + on = !!on; + if (on == ov8865->streaming) + goto unlock_and_return; + if (on) { - gpiod_set_value_cansleep(sensor->reset, 1); - gpiod_set_value_cansleep(sensor->powerdown, 1); - - ret = regulator_enable(sensor->dovdd); - if (ret) { - dev_err(sensor->dev, - "failed to enable DOVDD regulator\n"); - return ret; + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; } - ret = regulator_enable(sensor->avdd); + ret = __ov8865_start_stream(ov8865); if (ret) { - dev_err(sensor->dev, - "failed to enable AVDD regulator\n"); - goto disable_dovdd; + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; } - - ret = regulator_enable(sensor->dvdd); - if (ret) { - dev_err(sensor->dev, - "failed to enable DVDD regulator\n"); - goto disable_avdd; - } - - ret = clk_prepare_enable(sensor->extclk); - if (ret) { - dev_err(sensor->dev, "failed to enable EXTCLK clock\n"); - goto disable_dvdd; - } - - gpiod_set_value_cansleep(sensor->reset, 0); - gpiod_set_value_cansleep(sensor->powerdown, 0); - - /* Time to enter streaming mode according to power timings. */ - usleep_range(10000, 12000); } else { - gpiod_set_value_cansleep(sensor->powerdown, 1); - gpiod_set_value_cansleep(sensor->reset, 1); - - clk_disable_unprepare(sensor->extclk); - -disable_dvdd: - regulator_disable(sensor->dvdd); -disable_avdd: - regulator_disable(sensor->avdd); -disable_dovdd: - regulator_disable(sensor->dovdd); + __ov8865_stop_stream(ov8865); + pm_runtime_put(&client->dev); } + ov8865->streaming = on; + +unlock_and_return: + mutex_unlock(&ov8865->mutex); + return ret; } -/* Controls */ - -static int ov8865_s_ctrl(struct v4l2_ctrl *ctrl) +static int ov8865_s_power(struct v4l2_subdev *sd, int on) { - struct v4l2_subdev *subdev = ov8865_ctrl_subdev(ctrl); - struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev); - unsigned int index; - int ret; + struct ov8865 *ov8865 = to_ov8865(sd); + struct i2c_client *client = ov8865->client; + int ret = 0; - /* If VBLANK is altered we need to update exposure to compensate */ - if (ctrl->id == V4L2_CID_VBLANK) { - int exposure_max; + dev_info(&client->dev, "%s: on: %d\n", __func__, on); - exposure_max = sensor->state.mode->output_size_y + ctrl->val - - OV8865_INTEGRATION_TIME_MARGIN; - __v4l2_ctrl_modify_range(sensor->ctrls.exposure, - sensor->ctrls.exposure->minimum, - exposure_max, - sensor->ctrls.exposure->step, - min(sensor->ctrls.exposure->val, - exposure_max)); + mutex_lock(&ov8865->mutex); + + /* If the power state is not modified - no work to do. */ + if (ov8865->power_on == !!on) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ov8865->power_on = true; + } else { + pm_runtime_put(&client->dev); + ov8865->power_on = false; } - /* Wait for the sensor to be on before setting controls. */ - if (pm_runtime_suspended(sensor->dev)) +unlock_and_return: + mutex_unlock(&ov8865->mutex); + + return ret; +} + +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 ov8865_cal_delay(u32 cycles) +{ + return DIV_ROUND_UP(cycles, OV8865_XVCLK_FREQ / 1000 / 1000); +} + +static int __ov8865_power_on(struct ov8865 *ov8865) +{ + int ret; + u32 delay_us; + struct device *dev = &ov8865->client->dev; + + if (!IS_ERR(ov8865->power_gpio)) + gpiod_set_value_cansleep(ov8865->power_gpio, 1); + + usleep_range(1000, 2000); + + if (!IS_ERR_OR_NULL(ov8865->pins_default)) { + ret = pinctrl_select_state(ov8865->pinctrl, + ov8865->pins_default); + if (ret < 0) + dev_err(dev, "could not set pins\n"); + } + + ret = clk_set_rate(ov8865->xvclk, OV8865_XVCLK_FREQ); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate (24MHz)\n"); + if (clk_get_rate(ov8865->xvclk) != OV8865_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); + ret = clk_prepare_enable(ov8865->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + + if (!IS_ERR(ov8865->reset_gpio)) + gpiod_set_value_cansleep(ov8865->reset_gpio, 0); + + ret = regulator_bulk_enable(OV8865_NUM_SUPPLIES, ov8865->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + + if (!IS_ERR(ov8865->reset_gpio)) + gpiod_set_value_cansleep(ov8865->reset_gpio, 1); + + usleep_range(1000, 2000); + if (!IS_ERR(ov8865->pwdn_gpio)) + gpiod_set_value_cansleep(ov8865->pwdn_gpio, 1); + + /* 8192 cycles prior to first SCCB transaction */ + delay_us = ov8865_cal_delay(8192); + usleep_range(delay_us, delay_us * 2); + + return 0; + +disable_clk: + clk_disable_unprepare(ov8865->xvclk); + + return ret; +} + +static void __ov8865_power_off(struct ov8865 *ov8865) +{ + int ret; + struct device *dev = &ov8865->client->dev; + + if (!IS_ERR(ov8865->pwdn_gpio)) + gpiod_set_value_cansleep(ov8865->pwdn_gpio, 0); + clk_disable_unprepare(ov8865->xvclk); + if (!IS_ERR(ov8865->reset_gpio)) + gpiod_set_value_cansleep(ov8865->reset_gpio, 0); + if (!IS_ERR_OR_NULL(ov8865->pins_sleep)) { + ret = pinctrl_select_state(ov8865->pinctrl, + ov8865->pins_sleep); + if (ret < 0) + dev_dbg(dev, "could not set pins\n"); + } + + if (!IS_ERR(ov8865->power_gpio)) + gpiod_set_value_cansleep(ov8865->power_gpio, 0); + + regulator_bulk_disable(OV8865_NUM_SUPPLIES, ov8865->supplies); +} + +static int __maybe_unused ov8865_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov8865 *ov8865 = to_ov8865(sd); + + return __ov8865_power_on(ov8865); +} + +static int __maybe_unused ov8865_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov8865 *ov8865 = to_ov8865(sd); + + __ov8865_power_off(ov8865); + + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int ov8865_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct ov8865 *ov8865 = to_ov8865(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->state, 0); + const struct ov8865_mode *def_mode = &supported_modes[0]; + + mutex_lock(&ov8865->mutex); + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = OV8865_MEDIA_BUS_FMT; + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&ov8865->mutex); + /* No crop or compose */ + + return 0; +} +#endif + +static int ov8865_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval_enum *fie) +{ + if (fie->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + fie->code = OV8865_MEDIA_BUS_FMT; + fie->width = supported_modes[fie->index].width; + fie->height = supported_modes[fie->index].height; + fie->interval = supported_modes[fie->index].max_fps; + return 0; +} + +static int ov8865_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad_id, + struct v4l2_mbus_config *config) +{ + config->type = V4L2_MBUS_CSI2_DPHY; + config->bus.mipi_csi2.num_data_lanes = OV8865_LANES; + + return 0; +} + +static const struct dev_pm_ops ov8865_pm_ops = { + SET_RUNTIME_PM_OPS(ov8865_runtime_suspend, + ov8865_runtime_resume, NULL) +}; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops ov8865_internal_ops = { + .open = ov8865_open, +}; +#endif + +static const struct v4l2_subdev_core_ops ov8865_core_ops = { + .s_power = ov8865_s_power, + .ioctl = ov8865_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = ov8865_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops ov8865_video_ops = { + .s_stream = ov8865_s_stream, + .g_frame_interval = ov8865_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops ov8865_pad_ops = { + .enum_mbus_code = ov8865_enum_mbus_code, + .enum_frame_size = ov8865_enum_frame_sizes, + .enum_frame_interval = ov8865_enum_frame_interval, + .get_fmt = ov8865_get_fmt, + .set_fmt = ov8865_set_fmt, + .get_mbus_config = ov8865_g_mbus_config, +}; + +static const struct v4l2_subdev_ops ov8865_subdev_ops = { + .core = &ov8865_core_ops, + .video = &ov8865_video_ops, + .pad = &ov8865_pad_ops, +}; + +static int ov8865_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov8865 *ov8865 = container_of(ctrl->handler, + struct ov8865, ctrl_handler); + struct i2c_client *client = ov8865->client; + s64 max; + int ret = 0; + + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ + max = ov8865->cur_mode->height + ctrl->val - 4; + __v4l2_ctrl_modify_range(ov8865->exposure, + ov8865->exposure->minimum, max, + ov8865->exposure->step, + ov8865->exposure->default_value); + break; + } + + if (!pm_runtime_get_if_in_use(&client->dev)) return 0; switch (ctrl->id) { case V4L2_CID_EXPOSURE: - ret = ov8865_exposure_configure(sensor, ctrl->val); - if (ret) - return ret; + /* 4 least significant bits of expsoure are fractional part */ + dev_dbg(&client->dev, "set exposure value 0x%x\n", ctrl->val); + ret = ov8865_write_reg(ov8865->client, + OV8865_REG_EXPOSURE, + OV8865_REG_VALUE_24BIT, + ctrl->val << 4); break; case V4L2_CID_ANALOGUE_GAIN: - ret = ov8865_analog_gain_configure(sensor, ctrl->val); - if (ret) - return ret; + dev_dbg(&client->dev, "set analog gain value 0x%x\n", ctrl->val); + ret = ov8865_write_reg(ov8865->client, + OV8865_REG_GAIN_H, + OV8865_REG_VALUE_08BIT, + (ctrl->val >> OV8865_GAIN_H_SHIFT) & + OV8865_GAIN_H_MASK); + ret |= ov8865_write_reg(ov8865->client, + OV8865_REG_GAIN_L, + OV8865_REG_VALUE_08BIT, + ctrl->val & OV8865_GAIN_L_MASK); break; - case V4L2_CID_RED_BALANCE: - return ov8865_red_balance_configure(sensor, ctrl->val); - case V4L2_CID_BLUE_BALANCE: - return ov8865_blue_balance_configure(sensor, ctrl->val); - case V4L2_CID_HFLIP: - return ov8865_flip_horz_configure(sensor, !!ctrl->val); - case V4L2_CID_VFLIP: - return ov8865_flip_vert_configure(sensor, !!ctrl->val); - case V4L2_CID_TEST_PATTERN: - index = (unsigned int)ctrl->val; - return ov8865_test_pattern_configure(sensor, index); case V4L2_CID_VBLANK: - return ov8865_vts_configure(sensor, ctrl->val); + dev_dbg(&client->dev, "set vb value 0x%x\n", ctrl->val); + ret = ov8865_write_reg(ov8865->client, + OV8865_REG_VTS, + OV8865_REG_VALUE_16BIT, + ctrl->val + ov8865->cur_mode->height); + break; + case V4L2_CID_TEST_PATTERN: + ret = ov8865_enable_test_pattern(ov8865, ctrl->val); + break; default: - return -EINVAL; + dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", + __func__, ctrl->id, ctrl->val); + break; } - return 0; + pm_runtime_put(&client->dev); + + return ret; } static const struct v4l2_ctrl_ops ov8865_ctrl_ops = { - .s_ctrl = ov8865_s_ctrl, + .s_ctrl = ov8865_set_ctrl, }; -static int ov8865_ctrls_init(struct ov8865_sensor *sensor) +static int ov8865_initialize_controls(struct ov8865 *ov8865) { - struct ov8865_ctrls *ctrls = &sensor->ctrls; - struct v4l2_ctrl_handler *handler = &ctrls->handler; - const struct v4l2_ctrl_ops *ops = &ov8865_ctrl_ops; - const struct ov8865_mode *mode = &ov8865_modes[0]; - struct v4l2_fwnode_device_properties props; - unsigned int vblank_max, vblank_def; - unsigned int hblank; + const struct ov8865_mode *mode; + struct v4l2_ctrl_handler *handler; + s64 exposure_max, vblank_def; + u32 h_blank; int ret; + u64 dst_pixel_rate = 0; + u32 lane_num = OV8865_LANES; - v4l2_ctrl_handler_init(handler, 32); - - /* Use our mutex for ctrl locking. */ - handler->lock = &sensor->mutex; - - /* Exposure */ - - ctrls->exposure = v4l2_ctrl_new_std(handler, ops, V4L2_CID_EXPOSURE, 2, - 65535, 1, 32); - - /* Gain */ - - v4l2_ctrl_new_std(handler, ops, V4L2_CID_ANALOGUE_GAIN, 128, 2048, 128, - 128); - - /* White Balance */ - - v4l2_ctrl_new_std(handler, ops, V4L2_CID_RED_BALANCE, 1, 32767, 1, - 1024); - - v4l2_ctrl_new_std(handler, ops, V4L2_CID_BLUE_BALANCE, 1, 32767, 1, - 1024); - - /* Flip */ - - v4l2_ctrl_new_std(handler, ops, V4L2_CID_HFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(handler, ops, V4L2_CID_VFLIP, 0, 1, 1, 0); - - /* Test Pattern */ - - v4l2_ctrl_new_std_menu_items(handler, ops, V4L2_CID_TEST_PATTERN, - ARRAY_SIZE(ov8865_test_pattern_menu) - 1, - 0, 0, ov8865_test_pattern_menu); - - /* Blanking */ - hblank = mode->hts - mode->output_size_x; - ctrls->hblank = v4l2_ctrl_new_std(handler, ops, V4L2_CID_HBLANK, hblank, - hblank, 1, hblank); - - if (ctrls->hblank) - ctrls->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; - - vblank_max = OV8865_TIMING_MAX_VTS - mode->output_size_y; - vblank_def = mode->vts - mode->output_size_y; - ctrls->vblank = v4l2_ctrl_new_std(handler, ops, V4L2_CID_VBLANK, - OV8865_TIMING_MIN_VTS, vblank_max, 1, - vblank_def); - - /* MIPI CSI-2 */ - - ctrls->link_freq = - v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, - ARRAY_SIZE(ov8865_link_freq_menu) - 1, - 0, ov8865_link_freq_menu); - - ctrls->pixel_rate = - v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, 1, - INT_MAX, 1, 1); - - /* set properties from fwnode (e.g. rotation, orientation) */ - ret = v4l2_fwnode_device_parse(sensor->dev, &props); + handler = &ov8865->ctrl_handler; + mode = ov8865->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 8); if (ret) - goto error_ctrls; + return ret; + handler->lock = &ov8865->mutex; - ret = v4l2_ctrl_new_fwnode_properties(handler, ops, &props); - if (ret) - goto error_ctrls; + ov8865->link_freq = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, + 0, 0, link_freq_menu_items); + + dst_pixel_rate = (u32)link_freq_menu_items[mode->mipi_freq_idx] / mode->bpp * 2 * lane_num; + + ov8865->pixel_rate = v4l2_ctrl_new_std(handler, NULL, + V4L2_CID_PIXEL_RATE, + 0, OV8865_PIXEL_RATE, + 1, dst_pixel_rate); + + __v4l2_ctrl_s_ctrl(ov8865->link_freq, + mode->mipi_freq_idx); + + h_blank = mode->hts_def - mode->width; + ov8865->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (ov8865->hblank) + ov8865->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = mode->vts_def - mode->height; + ov8865->vblank = v4l2_ctrl_new_std(handler, &ov8865_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + OV8865_VTS_MAX - mode->height, + 1, vblank_def); + + exposure_max = mode->vts_def - 4; + ov8865->exposure = v4l2_ctrl_new_std(handler, &ov8865_ctrl_ops, + V4L2_CID_EXPOSURE, OV8865_EXPOSURE_MIN, + exposure_max, OV8865_EXPOSURE_STEP, + mode->exp_def); + + ov8865->anal_gain = v4l2_ctrl_new_std(handler, &ov8865_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, OV8865_GAIN_MIN, + OV8865_GAIN_MAX, OV8865_GAIN_STEP, + OV8865_GAIN_DEFAULT); + + ov8865->test_pattern = v4l2_ctrl_new_std_menu_items(handler, + &ov8865_ctrl_ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov8865_test_pattern_menu) - 1, + 0, 0, ov8865_test_pattern_menu); if (handler->error) { ret = handler->error; - goto error_ctrls; + dev_err(&ov8865->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; } - ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; - ctrls->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY; - - sensor->subdev.ctrl_handler = handler; + ov8865->subdev.ctrl_handler = handler; return 0; -error_ctrls: +err_free_handler: v4l2_ctrl_handler_free(handler); return ret; } -/* Subdev Video Operations */ - -static int ov8865_s_stream(struct v4l2_subdev *subdev, int enable) +static int ov8865_check_sensor_id(struct ov8865 *ov8865, + struct i2c_client *client) { - struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev); - struct ov8865_state *state = &sensor->state; + struct device *dev = &ov8865->client->dev; + u32 id = 0; int ret; - if (enable) { - ret = pm_runtime_resume_and_get(sensor->dev); - if (ret < 0) - return ret; + ret = ov8865_read_reg(client, OV8865_REG_CHIP_ID, + OV8865_REG_VALUE_24BIT, &id); + if (id != CHIP_ID) { + dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret); + return -ENODEV; } - mutex_lock(&sensor->mutex); - ret = ov8865_sw_standby(sensor, !enable); - mutex_unlock(&sensor->mutex); - - if (ret) - return ret; - - state->streaming = !!enable; - - if (!enable) - pm_runtime_put(sensor->dev); + ret = ov8865_read_reg(client, OV8865_CHIP_REVISION_REG, + OV8865_REG_VALUE_08BIT, &id); + if (ret) { + dev_err(dev, "Read chip revision register error\n"); + return -ENODEV; + } + dev_info(dev, "Detected OV%06x sensor, REVISION 0x%x\n", CHIP_ID, id); return 0; } -static int ov8865_g_frame_interval(struct v4l2_subdev *subdev, - struct v4l2_subdev_frame_interval *interval) +static int ov8865_configure_regulators(struct ov8865 *ov8865) { - struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev); - const struct ov8865_mode *mode; - unsigned int framesize; - unsigned int fps; + unsigned int i; - mutex_lock(&sensor->mutex); + for (i = 0; i < OV8865_NUM_SUPPLIES; i++) + ov8865->supplies[i].supply = ov8865_supply_names[i]; - mode = sensor->state.mode; - framesize = mode->hts * (mode->output_size_y + - sensor->ctrls.vblank->val); - fps = DIV_ROUND_CLOSEST(sensor->ctrls.pixel_rate->val, framesize); - - interval->interval.numerator = 1; - interval->interval.denominator = fps; - - mutex_unlock(&sensor->mutex); - - return 0; + return devm_regulator_bulk_get(&ov8865->client->dev, + OV8865_NUM_SUPPLIES, + ov8865->supplies); } -static const struct v4l2_subdev_video_ops ov8865_subdev_video_ops = { - .s_stream = ov8865_s_stream, - .g_frame_interval = ov8865_g_frame_interval, - .s_frame_interval = ov8865_g_frame_interval, -}; - -/* Subdev Pad Operations */ - -static int ov8865_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_mbus_code_enum *code_enum) -{ - if (code_enum->index >= ARRAY_SIZE(ov8865_mbus_codes)) - return -EINVAL; - - code_enum->code = ov8865_mbus_codes[code_enum->index]; - - return 0; -} - -static void ov8865_mbus_format_fill(struct v4l2_mbus_framefmt *mbus_format, - u32 mbus_code, - const struct ov8865_mode *mode) -{ - mbus_format->width = mode->output_size_x; - mbus_format->height = mode->output_size_y; - mbus_format->code = mbus_code; - - mbus_format->field = V4L2_FIELD_NONE; - mbus_format->colorspace = V4L2_COLORSPACE_RAW; - mbus_format->ycbcr_enc = - V4L2_MAP_YCBCR_ENC_DEFAULT(mbus_format->colorspace); - mbus_format->quantization = V4L2_QUANTIZATION_FULL_RANGE; - mbus_format->xfer_func = - V4L2_MAP_XFER_FUNC_DEFAULT(mbus_format->colorspace); -} - -static int ov8865_get_fmt(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *format) -{ - struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev); - struct v4l2_mbus_framefmt *mbus_format = &format->format; - - mutex_lock(&sensor->mutex); - - if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *mbus_format = *v4l2_subdev_get_try_format(subdev, sd_state, - format->pad); - else - ov8865_mbus_format_fill(mbus_format, sensor->state.mbus_code, - sensor->state.mode); - - mutex_unlock(&sensor->mutex); - - return 0; -} - -static int ov8865_set_fmt(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *format) -{ - struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev); - struct v4l2_mbus_framefmt *mbus_format = &format->format; - const struct ov8865_mode *mode; - u32 mbus_code = 0; - unsigned int hblank; - unsigned int index; - int exposure_max; - int ret = 0; - - mutex_lock(&sensor->mutex); - - if (sensor->state.streaming) { - ret = -EBUSY; - goto complete; - } - - /* Try to find requested mbus code. */ - for (index = 0; index < ARRAY_SIZE(ov8865_mbus_codes); index++) { - if (ov8865_mbus_codes[index] == mbus_format->code) { - mbus_code = mbus_format->code; - break; - } - } - - /* Fallback to default. */ - if (!mbus_code) - mbus_code = ov8865_mbus_codes[0]; - - /* Find the mode with nearest dimensions. */ - mode = v4l2_find_nearest_size(ov8865_modes, ARRAY_SIZE(ov8865_modes), - output_size_x, output_size_y, - mbus_format->width, mbus_format->height); - if (!mode) { - ret = -EINVAL; - goto complete; - } - - ov8865_mbus_format_fill(mbus_format, mbus_code, mode); - - if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *v4l2_subdev_get_try_format(subdev, sd_state, format->pad) = - *mbus_format; - else if (sensor->state.mode != mode || - sensor->state.mbus_code != mbus_code) - ret = ov8865_state_configure(sensor, mode, mbus_code); - - __v4l2_ctrl_modify_range(sensor->ctrls.vblank, OV8865_TIMING_MIN_VTS, - OV8865_TIMING_MAX_VTS - mode->output_size_y, - 1, mode->vts - mode->output_size_y); - - hblank = mode->hts - mode->output_size_x; - __v4l2_ctrl_modify_range(sensor->ctrls.hblank, hblank, hblank, 1, - hblank); - - exposure_max = mode->vts - OV8865_INTEGRATION_TIME_MARGIN; - __v4l2_ctrl_modify_range(sensor->ctrls.exposure, - sensor->ctrls.exposure->minimum, exposure_max, - sensor->ctrls.exposure->step, - min(sensor->ctrls.exposure->val, - exposure_max)); - -complete: - mutex_unlock(&sensor->mutex); - - return ret; -} - -static int ov8865_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_frame_size_enum *size_enum) -{ - const struct ov8865_mode *mode; - - if (size_enum->index >= ARRAY_SIZE(ov8865_modes)) - return -EINVAL; - - mode = &ov8865_modes[size_enum->index]; - - size_enum->min_width = size_enum->max_width = mode->output_size_x; - size_enum->min_height = size_enum->max_height = mode->output_size_y; - - return 0; -} - -static void -__ov8865_get_pad_crop(struct ov8865_sensor *sensor, - struct v4l2_subdev_state *state, unsigned int pad, - enum v4l2_subdev_format_whence which, struct v4l2_rect *r) -{ - const struct ov8865_mode *mode = sensor->state.mode; - - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - *r = *v4l2_subdev_get_try_crop(&sensor->subdev, state, pad); - break; - case V4L2_SUBDEV_FORMAT_ACTIVE: - r->height = mode->output_size_y; - r->width = mode->output_size_x; - r->top = (OV8865_NATIVE_HEIGHT - mode->output_size_y) / 2; - r->left = (OV8865_NATIVE_WIDTH - mode->output_size_x) / 2; - break; - } -} - -static int ov8865_get_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *state, - struct v4l2_subdev_selection *sel) -{ - struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev); - - switch (sel->target) { - case V4L2_SEL_TGT_CROP: - mutex_lock(&sensor->mutex); - __ov8865_get_pad_crop(sensor, state, sel->pad, - sel->which, &sel->r); - mutex_unlock(&sensor->mutex); - break; - case V4L2_SEL_TGT_NATIVE_SIZE: - sel->r.top = 0; - sel->r.left = 0; - sel->r.width = OV8865_NATIVE_WIDTH; - sel->r.height = OV8865_NATIVE_HEIGHT; - break; - case V4L2_SEL_TGT_CROP_BOUNDS: - case V4L2_SEL_TGT_CROP_DEFAULT: - sel->r.top = OV8865_ACTIVE_START_TOP; - sel->r.left = OV8865_ACTIVE_START_LEFT; - sel->r.width = OV8865_ACTIVE_WIDTH; - sel->r.height = OV8865_ACTIVE_HEIGHT; - break; - default: - return -EINVAL; - } - - return 0; -} - -static const struct v4l2_subdev_pad_ops ov8865_subdev_pad_ops = { - .enum_mbus_code = ov8865_enum_mbus_code, - .get_fmt = ov8865_get_fmt, - .set_fmt = ov8865_set_fmt, - .enum_frame_size = ov8865_enum_frame_size, - .get_selection = ov8865_get_selection, - .set_selection = ov8865_get_selection, -}; - -static const struct v4l2_subdev_ops ov8865_subdev_ops = { - .video = &ov8865_subdev_video_ops, - .pad = &ov8865_subdev_pad_ops, -}; - -static int ov8865_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev); - struct ov8865_state *state = &sensor->state; - int ret = 0; - - mutex_lock(&sensor->mutex); - - if (state->streaming) { - ret = ov8865_sw_standby(sensor, true); - if (ret) - goto complete; - } - - ret = ov8865_sensor_power(sensor, false); - if (ret) - ov8865_sw_standby(sensor, false); - -complete: - mutex_unlock(&sensor->mutex); - - return ret; -} - -static int ov8865_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev); - struct ov8865_state *state = &sensor->state; - int ret = 0; - - mutex_lock(&sensor->mutex); - - ret = ov8865_sensor_power(sensor, true); - if (ret) - goto complete; - - ret = ov8865_sensor_init(sensor); - if (ret) - goto error_power; - - ret = __v4l2_ctrl_handler_setup(&sensor->ctrls.handler); - if (ret) - goto error_power; - - if (state->streaming) { - ret = ov8865_sw_standby(sensor, false); - if (ret) - goto error_power; - } - - goto complete; - -error_power: - ov8865_sensor_power(sensor, false); - -complete: - mutex_unlock(&sensor->mutex); - - return ret; -} - -static int ov8865_probe(struct i2c_client *client) +static int ov8865_probe(struct i2c_client *client, + const struct i2c_device_id *id) { struct device *dev = &client->dev; - struct fwnode_handle *handle; - struct ov8865_sensor *sensor; - struct v4l2_subdev *subdev; - struct media_pad *pad; - unsigned int rate = 0; - unsigned int i; + struct device_node *node = dev->of_node; + struct ov8865 *ov8865; + struct v4l2_subdev *sd; + char facing[2]; int ret; - sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); - if (!sensor) + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + ov8865 = devm_kzalloc(dev, sizeof(*ov8865), GFP_KERNEL); + if (!ov8865) return -ENOMEM; - sensor->dev = dev; - sensor->i2c_client = client; - - /* Regulators */ - - /* DVDD: digital core */ - sensor->dvdd = devm_regulator_get(dev, "dvdd"); - if (IS_ERR(sensor->dvdd)) - return dev_err_probe(dev, PTR_ERR(sensor->dvdd), - "cannot get DVDD regulator\n"); - - /* DOVDD: digital I/O */ - sensor->dovdd = devm_regulator_get(dev, "dovdd"); - if (IS_ERR(sensor->dovdd)) - return dev_err_probe(dev, PTR_ERR(sensor->dovdd), - "cannot get DOVDD regulator\n"); - - /* AVDD: analog */ - sensor->avdd = devm_regulator_get(dev, "avdd"); - if (IS_ERR(sensor->avdd)) - return dev_err_probe(dev, PTR_ERR(sensor->avdd), - "cannot get AVDD (analog) regulator\n"); - - /* Graph Endpoint */ - - handle = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); - if (!handle) - return -EPROBE_DEFER; - - sensor->endpoint.bus_type = V4L2_MBUS_CSI2_DPHY; - - ret = v4l2_fwnode_endpoint_alloc_parse(handle, &sensor->endpoint); - fwnode_handle_put(handle); + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &ov8865->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &ov8865->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &ov8865->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &ov8865->len_name); if (ret) { - dev_err(dev, "failed to parse endpoint node\n"); + dev_err(dev, + "could not get module information!\n"); + return -EINVAL; + } + + ov8865->client = client; + ov8865->cur_mode = &supported_modes[0]; + ov8865->cfg_num = ARRAY_SIZE(supported_modes); + ov8865->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(ov8865->xvclk)) { + dev_err(dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + ov8865->power_gpio = devm_gpiod_get(dev, "power", GPIOD_OUT_LOW); + if (IS_ERR(ov8865->power_gpio)) + dev_warn(dev, "Failed to get power-gpios, maybe no use\n"); + + ov8865->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ov8865->reset_gpio)) + dev_warn(dev, "Failed to get reset-gpios, maybe no use\n"); + + ov8865->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(ov8865->pwdn_gpio)) + dev_warn(dev, "Failed to get pwdn-gpios, maybe no use\n"); + + ret = ov8865_configure_regulators(ov8865); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); return ret; } - /* GPIOs */ + ov8865->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR(ov8865->pinctrl)) { + ov8865->pins_default = + pinctrl_lookup_state(ov8865->pinctrl, + OF_CAMERA_PINCTRL_STATE_DEFAULT); + if (IS_ERR(ov8865->pins_default)) + dev_err(dev, "could not get default pinstate\n"); - sensor->powerdown = devm_gpiod_get_optional(dev, "powerdown", - GPIOD_OUT_HIGH); - if (IS_ERR(sensor->powerdown)) { - ret = PTR_ERR(sensor->powerdown); - goto error_endpoint; + ov8865->pins_sleep = + pinctrl_lookup_state(ov8865->pinctrl, + OF_CAMERA_PINCTRL_STATE_SLEEP); + if (IS_ERR(ov8865->pins_sleep)) + dev_err(dev, "could not get sleep pinstate\n"); } - sensor->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); - if (IS_ERR(sensor->reset)) { - ret = PTR_ERR(sensor->reset); - goto error_endpoint; - } + mutex_init(&ov8865->mutex); - /* 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"); - goto error_endpoint; - } - - sensor->extclk_rate = rate ? rate : clk_get_rate(sensor->extclk); - - for (i = 0; i < ARRAY_SIZE(supported_extclk_rates); i++) { - if (sensor->extclk_rate == supported_extclk_rates[i]) - break; - } - - if (i == ARRAY_SIZE(supported_extclk_rates)) { - dev_err(dev, "clock rate %lu Hz is unsupported\n", - sensor->extclk_rate); - ret = -EINVAL; - goto error_endpoint; - } - - sensor->pll_configs = ov8865_pll_configs[i]; - - /* Subdev, entity and pad */ - - subdev = &sensor->subdev; - v4l2_i2c_subdev_init(subdev, client, &ov8865_subdev_ops); - - subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - subdev->entity.function = MEDIA_ENT_F_CAM_SENSOR; - - pad = &sensor->pad; - pad->flags = MEDIA_PAD_FL_SOURCE; - - ret = media_entity_pads_init(&subdev->entity, 1, pad); + sd = &ov8865->subdev; + v4l2_i2c_subdev_init(sd, client, &ov8865_subdev_ops); + ret = ov8865_initialize_controls(ov8865); if (ret) - goto error_entity; + goto err_destroy_mutex; - /* Mutex */ - - mutex_init(&sensor->mutex); - - /* Sensor */ - - ret = ov8865_ctrls_init(sensor); + ret = __ov8865_power_on(ov8865); if (ret) - goto error_mutex; + goto err_free_handler; - mutex_lock(&sensor->mutex); - ret = ov8865_state_init(sensor); - mutex_unlock(&sensor->mutex); + ret = ov8865_check_sensor_id(ov8865, client); if (ret) - goto error_ctrls; + goto err_power_off; - /* Runtime PM */ +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &ov8865_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + ov8865->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &ov8865->pad); + if (ret < 0) + goto err_power_off; +#endif - pm_runtime_set_suspended(sensor->dev); - pm_runtime_enable(sensor->dev); + memset(facing, 0, sizeof(facing)); + if (strcmp(ov8865->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; - /* V4L2 subdev register */ + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + ov8865->module_index, facing, + OV8865_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor(sd); + if (ret) { + dev_err(dev, "v4l2 async register subdev failed\n"); + goto err_clean_entity; + } - ret = v4l2_async_register_subdev_sensor(subdev); - if (ret) - goto error_pm; + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); return 0; -error_pm: - pm_runtime_disable(sensor->dev); - -error_ctrls: - v4l2_ctrl_handler_free(&sensor->ctrls.handler); - -error_mutex: - mutex_destroy(&sensor->mutex); - -error_entity: - media_entity_cleanup(&sensor->subdev.entity); - -error_endpoint: - v4l2_fwnode_endpoint_free(&sensor->endpoint); +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif +err_power_off: + __ov8865_power_off(ov8865); +err_free_handler: + v4l2_ctrl_handler_free(&ov8865->ctrl_handler); +err_destroy_mutex: + mutex_destroy(&ov8865->mutex); return ret; } static void ov8865_remove(struct i2c_client *client) { - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov8865 *ov8865 = to_ov8865(sd); - v4l2_async_unregister_subdev(subdev); - pm_runtime_disable(sensor->dev); - v4l2_ctrl_handler_free(&sensor->ctrls.handler); - mutex_destroy(&sensor->mutex); - media_entity_cleanup(&subdev->entity); + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + v4l2_ctrl_handler_free(&ov8865->ctrl_handler); + mutex_destroy(&ov8865->mutex); - v4l2_fwnode_endpoint_free(&sensor->endpoint); + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __ov8865_power_off(ov8865); + pm_runtime_set_suspended(&client->dev); } -static const struct dev_pm_ops ov8865_pm_ops = { - SET_RUNTIME_PM_OPS(ov8865_suspend, ov8865_resume, NULL) -}; - -static const struct acpi_device_id ov8865_acpi_match[] = { - {"INT347A"}, - { } -}; -MODULE_DEVICE_TABLE(acpi, ov8865_acpi_match); - +#if IS_ENABLED(CONFIG_OF) static const struct of_device_id ov8865_of_match[] = { { .compatible = "ovti,ov8865" }, - { } + {}, }; MODULE_DEVICE_TABLE(of, ov8865_of_match); +#endif -static struct i2c_driver ov8865_driver = { - .driver = { - .name = "ov8865", - .of_match_table = ov8865_of_match, - .acpi_match_table = ov8865_acpi_match, - .pm = &ov8865_pm_ops, - }, - .probe_new = ov8865_probe, - .remove = ov8865_remove, +static const struct i2c_device_id ov8865_match_id[] = { + { "ovti,ov8865", 0 }, + { }, }; -module_i2c_driver(ov8865_driver); +static struct i2c_driver ov8865_i2c_driver = { + .driver = { + .name = OV8865_NAME, + .pm = &ov8865_pm_ops, + .of_match_table = of_match_ptr(ov8865_of_match), + }, + .probe = &ov8865_probe, + .remove = &ov8865_remove, + .id_table = ov8865_match_id, +}; -MODULE_AUTHOR("Paul Kocialkowski "); -MODULE_DESCRIPTION("V4L2 driver for the OmniVision OV8865 image sensor"); +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&ov8865_i2c_driver); +} + +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&ov8865_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_DESCRIPTION("OmniVision ov8865 sensor driver"); MODULE_LICENSE("GPL v2");