diff --git a/arch/arm/boot/dts/overlays/ov7251-overlay.dts b/arch/arm/boot/dts/overlays/ov7251-overlay.dts index 0e44be8a446865..3cf109ec998fde 100644 --- a/arch/arm/boot/dts/overlays/ov7251-overlay.dts +++ b/arch/arm/boot/dts/overlays/ov7251-overlay.dts @@ -38,7 +38,7 @@ data-lanes = <1>; clock-noncontinuous; link-frequencies = - /bits/ 64 <456000000>; + /bits/ 64 <240000000>; }; }; }; @@ -49,6 +49,7 @@ target = <&csi1>; csi: __overlay__ { status = "okay"; + brcm,media-controller; port { csi1_ep: endpoint { diff --git a/drivers/media/i2c/ov7251.c b/drivers/media/i2c/ov7251.c index 2bcc3b70918f96..adc8e1980cfd9a 100644 --- a/drivers/media/i2c/ov7251.c +++ b/drivers/media/i2c/ov7251.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include #include @@ -35,28 +37,82 @@ #define OV7251_AEC_EXPO_2 0x3502 #define OV7251_AEC_AGC_ADJ_0 0x350a #define OV7251_AEC_AGC_ADJ_1 0x350b +/* Exposure must be at least 20 lines shorter than VTS */ +#define OV7251_EXPOSURE_OFFSET 20 + /* HTS is registers 0x380c and 0x380d */ +#define OV7251_HTS 0x3a0 +#define OV7251_VTS_HIGH 0x380e +#define OV7251_VTS_LOW 0x380f +#define OV7251_VTS_MIN_OFFSET 92 +#define OV7251_VTS_MAX 0x7fff #define OV7251_TIMING_FORMAT1 0x3820 #define OV7251_TIMING_FORMAT1_VFLIP BIT(2) #define OV7251_TIMING_FORMAT2 0x3821 #define OV7251_TIMING_FORMAT2_MIRROR BIT(2) #define OV7251_PRE_ISP_00 0x5e00 #define OV7251_PRE_ISP_00_TEST_PATTERN BIT(7) +#define OV7251_PLL1_PRE_DIV_REG 0x30b4 +#define OV7251_PLL1_MULT_REG 0x30b3 +#define OV7251_PLL1_DIVIDER_REG 0x30b1 +#define OV7251_PLL1_PIX_DIV_REG 0x30b0 +#define OV7251_PLL1_MIPI_DIV_REG 0x30b5 +#define OV7251_PLL2_PRE_DIV_REG 0x3098 +#define OV7251_PLL2_MULT_REG 0x3099 +#define OV7251_PLL2_DIVIDER_REG 0x309d +#define OV7251_PLL2_SYS_DIV_REG 0x309a +#define OV7251_PLL2_ADC_DIV_REG 0x309b + +/* + * OV7251 native and active pixel array size. + * Datasheet not available to confirm these values, so assume there are no + * border pixels. + */ +#define OV7251_NATIVE_WIDTH 640U +#define OV7251_NATIVE_HEIGHT 480U +#define OV7251_PIXEL_ARRAY_LEFT 0U +#define OV7251_PIXEL_ARRAY_TOP 0U +#define OV7251_PIXEL_ARRAY_WIDTH 640U +#define OV7251_PIXEL_ARRAY_HEIGHT 480U + +#define OV7251_PIXEL_CLOCK 48000000 struct reg_value { u16 reg; u8 val; }; +struct ov7251_frame_ival_info { + u16 vts; + struct v4l2_fract timeperframe; +}; + struct ov7251_mode_info { u32 width; u32 height; const struct reg_value *data; u32 data_size; - u32 pixel_clock; - u32 link_freq; - u16 exposure_max; u16 exposure_def; - struct v4l2_fract timeperframe; +}; + +struct ov7251_pll1_config { + unsigned int pre_div; + unsigned int mult; + unsigned int div; + unsigned int pix_div; + unsigned int mipi_div; +}; + +struct ov7251_pll2_config { + unsigned int pre_div; + unsigned int mult; + unsigned int div; + unsigned int sys_div; + unsigned int adc_div; +}; + +struct ov7251_pll_configs { + const struct ov7251_pll1_config *pll1; + const struct ov7251_pll2_config *pll2; }; struct ov7251 { @@ -70,17 +126,19 @@ struct ov7251 { struct clk *xclk; u32 xclk_freq; + const struct ov7251_pll_configs *pll_configs; + struct regulator *io_regulator; struct regulator *core_regulator; struct regulator *analog_regulator; const struct ov7251_mode_info *current_mode; + const struct ov7251_frame_ival_info *current_ival; struct v4l2_ctrl_handler ctrls; - struct v4l2_ctrl *pixel_clock; - struct v4l2_ctrl *link_freq; struct v4l2_ctrl *exposure; - struct v4l2_ctrl *gain; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; /* Cached register values */ u8 aec_pk_manual; @@ -99,288 +157,65 @@ static inline struct ov7251 *to_ov7251(struct v4l2_subdev *sd) return container_of(sd, struct ov7251, sd); } -static const struct reg_value ov7251_global_init_setting[] = { - { 0x0103, 0x01 }, - { 0x303b, 0x02 }, +enum xclk_rate { + OV7251_19_2_MHZ, + OV7251_24_MHZ, + OV7251_NUM_SUPPORTED_RATES }; -static const struct reg_value ov7251_setting_vga_30fps[] = { - { 0x3005, 0x00 }, - { 0x3012, 0xc0 }, - { 0x3013, 0xd2 }, - { 0x3014, 0x04 }, - { 0x3016, 0xf0 }, - { 0x3017, 0xf0 }, - { 0x3018, 0xf0 }, - { 0x301a, 0xf0 }, - { 0x301b, 0xf0 }, - { 0x301c, 0xf0 }, - { 0x3023, 0x05 }, - { 0x3037, 0xf0 }, - { 0x3098, 0x04 }, /* pll2 pre divider */ - { 0x3099, 0x28 }, /* pll2 multiplier */ - { 0x309a, 0x05 }, /* pll2 sys divider */ - { 0x309b, 0x04 }, /* pll2 adc divider */ - { 0x309d, 0x00 }, /* pll2 divider */ - { 0x30b0, 0x0a }, /* pll1 pix divider */ - { 0x30b1, 0x01 }, /* pll1 divider */ - { 0x30b3, 0x64 }, /* pll1 multiplier */ - { 0x30b4, 0x03 }, /* pll1 pre divider */ - { 0x30b5, 0x05 }, /* pll1 mipi divider */ - { 0x3106, 0xda }, - { 0x3503, 0x07 }, - { 0x3509, 0x10 }, - { 0x3600, 0x1c }, - { 0x3602, 0x62 }, - { 0x3620, 0xb7 }, - { 0x3622, 0x04 }, - { 0x3626, 0x21 }, - { 0x3627, 0x30 }, - { 0x3630, 0x44 }, - { 0x3631, 0x35 }, - { 0x3634, 0x60 }, - { 0x3636, 0x00 }, - { 0x3662, 0x01 }, - { 0x3663, 0x70 }, - { 0x3664, 0x50 }, - { 0x3666, 0x0a }, - { 0x3669, 0x1a }, - { 0x366a, 0x00 }, - { 0x366b, 0x50 }, - { 0x3673, 0x01 }, - { 0x3674, 0xff }, - { 0x3675, 0x03 }, - { 0x3705, 0xc1 }, - { 0x3709, 0x40 }, - { 0x373c, 0x08 }, - { 0x3742, 0x00 }, - { 0x3757, 0xb3 }, - { 0x3788, 0x00 }, - { 0x37a8, 0x01 }, - { 0x37a9, 0xc0 }, - { 0x3800, 0x00 }, - { 0x3801, 0x04 }, - { 0x3802, 0x00 }, - { 0x3803, 0x04 }, - { 0x3804, 0x02 }, - { 0x3805, 0x8b }, - { 0x3806, 0x01 }, - { 0x3807, 0xeb }, - { 0x3808, 0x02 }, /* width high */ - { 0x3809, 0x80 }, /* width low */ - { 0x380a, 0x01 }, /* height high */ - { 0x380b, 0xe0 }, /* height low */ - { 0x380c, 0x03 }, /* total horiz timing high */ - { 0x380d, 0xa0 }, /* total horiz timing low */ - { 0x380e, 0x06 }, /* total vertical timing high */ - { 0x380f, 0xbc }, /* total vertical timing low */ - { 0x3810, 0x00 }, - { 0x3811, 0x04 }, - { 0x3812, 0x00 }, - { 0x3813, 0x05 }, - { 0x3814, 0x11 }, - { 0x3815, 0x11 }, - { 0x3820, 0x40 }, - { 0x3821, 0x00 }, - { 0x382f, 0x0e }, - { 0x3832, 0x00 }, - { 0x3833, 0x05 }, - { 0x3834, 0x00 }, - { 0x3835, 0x0c }, - { 0x3837, 0x00 }, - { 0x3b80, 0x00 }, - { 0x3b81, 0xa5 }, - { 0x3b82, 0x10 }, - { 0x3b83, 0x00 }, - { 0x3b84, 0x08 }, - { 0x3b85, 0x00 }, - { 0x3b86, 0x01 }, - { 0x3b87, 0x00 }, - { 0x3b88, 0x00 }, - { 0x3b89, 0x00 }, - { 0x3b8a, 0x00 }, - { 0x3b8b, 0x05 }, - { 0x3b8c, 0x00 }, - { 0x3b8d, 0x00 }, - { 0x3b8e, 0x00 }, - { 0x3b8f, 0x1a }, - { 0x3b94, 0x05 }, - { 0x3b95, 0xf2 }, - { 0x3b96, 0x40 }, - { 0x3c00, 0x89 }, - { 0x3c01, 0x63 }, - { 0x3c02, 0x01 }, - { 0x3c03, 0x00 }, - { 0x3c04, 0x00 }, - { 0x3c05, 0x03 }, - { 0x3c06, 0x00 }, - { 0x3c07, 0x06 }, - { 0x3c0c, 0x01 }, - { 0x3c0d, 0xd0 }, - { 0x3c0e, 0x02 }, - { 0x3c0f, 0x0a }, - { 0x4001, 0x42 }, - { 0x4004, 0x04 }, - { 0x4005, 0x00 }, - { 0x404e, 0x01 }, - { 0x4300, 0xff }, - { 0x4301, 0x00 }, - { 0x4315, 0x00 }, - { 0x4501, 0x48 }, - { 0x4600, 0x00 }, - { 0x4601, 0x4e }, - { 0x4801, 0x0f }, - { 0x4806, 0x0f }, - { 0x4819, 0xaa }, - { 0x4823, 0x3e }, - { 0x4837, 0x19 }, - { 0x4a0d, 0x00 }, - { 0x4a47, 0x7f }, - { 0x4a49, 0xf0 }, - { 0x4a4b, 0x30 }, - { 0x5000, 0x85 }, - { 0x5001, 0x80 }, +static const struct ov7251_pll1_config ov7251_pll1_config_19_2_mhz = { + .pre_div = 0x03, + .mult = 0x4b, + .div = 0x01, + .pix_div = 0x0a, + .mipi_div = 0x05 }; -static const struct reg_value ov7251_setting_vga_60fps[] = { - { 0x3005, 0x00 }, - { 0x3012, 0xc0 }, - { 0x3013, 0xd2 }, - { 0x3014, 0x04 }, - { 0x3016, 0x10 }, - { 0x3017, 0x00 }, - { 0x3018, 0x00 }, - { 0x301a, 0x00 }, - { 0x301b, 0x00 }, - { 0x301c, 0x00 }, - { 0x3023, 0x05 }, - { 0x3037, 0xf0 }, - { 0x3098, 0x04 }, /* pll2 pre divider */ - { 0x3099, 0x28 }, /* pll2 multiplier */ - { 0x309a, 0x05 }, /* pll2 sys divider */ - { 0x309b, 0x04 }, /* pll2 adc divider */ - { 0x309d, 0x00 }, /* pll2 divider */ - { 0x30b0, 0x0a }, /* pll1 pix divider */ - { 0x30b1, 0x01 }, /* pll1 divider */ - { 0x30b3, 0x64 }, /* pll1 multiplier */ - { 0x30b4, 0x03 }, /* pll1 pre divider */ - { 0x30b5, 0x05 }, /* pll1 mipi divider */ - { 0x3106, 0xda }, - { 0x3503, 0x07 }, - { 0x3509, 0x10 }, - { 0x3600, 0x1c }, - { 0x3602, 0x62 }, - { 0x3620, 0xb7 }, - { 0x3622, 0x04 }, - { 0x3626, 0x21 }, - { 0x3627, 0x30 }, - { 0x3630, 0x44 }, - { 0x3631, 0x35 }, - { 0x3634, 0x60 }, - { 0x3636, 0x00 }, - { 0x3662, 0x01 }, - { 0x3663, 0x70 }, - { 0x3664, 0x50 }, - { 0x3666, 0x0a }, - { 0x3669, 0x1a }, - { 0x366a, 0x00 }, - { 0x366b, 0x50 }, - { 0x3673, 0x01 }, - { 0x3674, 0xff }, - { 0x3675, 0x03 }, - { 0x3705, 0xc1 }, - { 0x3709, 0x40 }, - { 0x373c, 0x08 }, - { 0x3742, 0x00 }, - { 0x3757, 0xb3 }, - { 0x3788, 0x00 }, - { 0x37a8, 0x01 }, - { 0x37a9, 0xc0 }, - { 0x3800, 0x00 }, - { 0x3801, 0x04 }, - { 0x3802, 0x00 }, - { 0x3803, 0x04 }, - { 0x3804, 0x02 }, - { 0x3805, 0x8b }, - { 0x3806, 0x01 }, - { 0x3807, 0xeb }, - { 0x3808, 0x02 }, /* width high */ - { 0x3809, 0x80 }, /* width low */ - { 0x380a, 0x01 }, /* height high */ - { 0x380b, 0xe0 }, /* height low */ - { 0x380c, 0x03 }, /* total horiz timing high */ - { 0x380d, 0xa0 }, /* total horiz timing low */ - { 0x380e, 0x03 }, /* total vertical timing high */ - { 0x380f, 0x5c }, /* total vertical timing low */ - { 0x3810, 0x00 }, - { 0x3811, 0x04 }, - { 0x3812, 0x00 }, - { 0x3813, 0x05 }, - { 0x3814, 0x11 }, - { 0x3815, 0x11 }, - { 0x3820, 0x40 }, - { 0x3821, 0x00 }, - { 0x382f, 0x0e }, - { 0x3832, 0x00 }, - { 0x3833, 0x05 }, - { 0x3834, 0x00 }, - { 0x3835, 0x0c }, - { 0x3837, 0x00 }, - { 0x3b80, 0x00 }, - { 0x3b81, 0xa5 }, - { 0x3b82, 0x10 }, - { 0x3b83, 0x00 }, - { 0x3b84, 0x08 }, - { 0x3b85, 0x00 }, - { 0x3b86, 0x01 }, - { 0x3b87, 0x00 }, - { 0x3b88, 0x00 }, - { 0x3b89, 0x00 }, - { 0x3b8a, 0x00 }, - { 0x3b8b, 0x05 }, - { 0x3b8c, 0x00 }, - { 0x3b8d, 0x00 }, - { 0x3b8e, 0x00 }, - { 0x3b8f, 0x1a }, - { 0x3b94, 0x05 }, - { 0x3b95, 0xf2 }, - { 0x3b96, 0x40 }, - { 0x3c00, 0x89 }, - { 0x3c01, 0x63 }, - { 0x3c02, 0x01 }, - { 0x3c03, 0x00 }, - { 0x3c04, 0x00 }, - { 0x3c05, 0x03 }, - { 0x3c06, 0x00 }, - { 0x3c07, 0x06 }, - { 0x3c0c, 0x01 }, - { 0x3c0d, 0xd0 }, - { 0x3c0e, 0x02 }, - { 0x3c0f, 0x0a }, - { 0x4001, 0x42 }, - { 0x4004, 0x04 }, - { 0x4005, 0x00 }, - { 0x404e, 0x01 }, - { 0x4300, 0xff }, - { 0x4301, 0x00 }, - { 0x4315, 0x00 }, - { 0x4501, 0x48 }, - { 0x4600, 0x00 }, - { 0x4601, 0x4e }, - { 0x4801, 0x0f }, - { 0x4806, 0x0f }, - { 0x4819, 0xaa }, - { 0x4823, 0x3e }, - { 0x4837, 0x19 }, - { 0x4a0d, 0x00 }, - { 0x4a47, 0x7f }, - { 0x4a49, 0xf0 }, - { 0x4a4b, 0x30 }, - { 0x5000, 0x85 }, - { 0x5001, 0x80 }, +static const struct ov7251_pll1_config ov7251_pll1_config_24_mhz = { + .pre_div = 0x03, + .mult = 0x64, + .div = 0x01, + .pix_div = 0x0a, + .mipi_div = 0x05 +}; + +static const struct ov7251_pll2_config ov7251_pll2_config_19_2_mhz = { + .pre_div = 0x04, + .mult = 0x32, + .div = 0x00, + .sys_div = 0x05, + .adc_div = 0x04 +}; + +static const struct ov7251_pll2_config ov7251_pll2_config_24_mhz = { + .pre_div = 0x04, + .mult = 0x28, + .div = 0x00, + .sys_div = 0x05, + .adc_div = 0x04 +}; + +static const struct ov7251_pll_configs ov7251_pll_configs_19_2_mhz = { + .pll1 = &ov7251_pll1_config_19_2_mhz, + .pll2 = &ov7251_pll2_config_19_2_mhz +}; + +static const struct ov7251_pll_configs ov7251_pll_configs_24_mhz = { + .pll1 = &ov7251_pll1_config_24_mhz, + .pll2 = &ov7251_pll2_config_24_mhz +}; + +static const struct ov7251_pll_configs *ov7251_pll_configs[] = { + [OV7251_19_2_MHZ] = &ov7251_pll_configs_19_2_mhz, + [OV7251_24_MHZ] = &ov7251_pll_configs_24_mhz +}; + +static const struct reg_value ov7251_global_init_setting[] = { + { 0x0103, 0x01 }, + { 0x303b, 0x02 }, }; -static const struct reg_value ov7251_setting_vga_90fps[] = { +static const struct reg_value ov7251_setting_vga[] = { { 0x3005, 0x00 }, { 0x3012, 0xc0 }, { 0x3013, 0xd2 }, @@ -393,16 +228,6 @@ static const struct reg_value ov7251_setting_vga_90fps[] = { { 0x301c, 0x00 }, { 0x3023, 0x05 }, { 0x3037, 0xf0 }, - { 0x3098, 0x04 }, /* pll2 pre divider */ - { 0x3099, 0x28 }, /* pll2 multiplier */ - { 0x309a, 0x05 }, /* pll2 sys divider */ - { 0x309b, 0x04 }, /* pll2 adc divider */ - { 0x309d, 0x00 }, /* pll2 divider */ - { 0x30b0, 0x0a }, /* pll1 pix divider */ - { 0x30b1, 0x01 }, /* pll1 divider */ - { 0x30b3, 0x64 }, /* pll1 multiplier */ - { 0x30b4, 0x03 }, /* pll1 pre divider */ - { 0x30b5, 0x05 }, /* pll1 mipi divider */ { 0x3106, 0xda }, { 0x3503, 0x07 }, { 0x3509, 0x10 }, @@ -448,8 +273,6 @@ static const struct reg_value ov7251_setting_vga_90fps[] = { { 0x380b, 0xe0 }, /* height low */ { 0x380c, 0x03 }, /* total horiz timing high */ { 0x380d, 0xa0 }, /* total horiz timing low */ - { 0x380e, 0x02 }, /* total vertical timing high */ - { 0x380f, 0x3c }, /* total vertical timing low */ { 0x3810, 0x00 }, { 0x3811, 0x04 }, { 0x3812, 0x00 }, @@ -518,48 +341,32 @@ static const struct reg_value ov7251_setting_vga_90fps[] = { { 0x5001, 0x80 }, }; +static const unsigned long supported_xclk_rates[] = { + [OV7251_19_2_MHZ] = 19200000, + [OV7251_24_MHZ] = 24000000, +}; + static const s64 link_freq[] = { 240000000, }; -static const struct ov7251_mode_info ov7251_mode_info_data[] = { +static const struct ov7251_frame_ival_info ov7251_frame_ival_info_data[] = { { - .width = 640, - .height = 480, - .data = ov7251_setting_vga_30fps, - .data_size = ARRAY_SIZE(ov7251_setting_vga_30fps), - .pixel_clock = 48000000, - .link_freq = 0, /* an index in link_freq[] */ - .exposure_max = 1704, - .exposure_def = 504, + .vts = 0x6bc, .timeperframe = { .numerator = 100, .denominator = 3000 } }, { - .width = 640, - .height = 480, - .data = ov7251_setting_vga_60fps, - .data_size = ARRAY_SIZE(ov7251_setting_vga_60fps), - .pixel_clock = 48000000, - .link_freq = 0, /* an index in link_freq[] */ - .exposure_max = 840, - .exposure_def = 504, + .vts = 0x35c, .timeperframe = { .numerator = 100, .denominator = 6014 } }, { - .width = 640, - .height = 480, - .data = ov7251_setting_vga_90fps, - .data_size = ARRAY_SIZE(ov7251_setting_vga_90fps), - .pixel_clock = 48000000, - .link_freq = 0, /* an index in link_freq[] */ - .exposure_max = 552, - .exposure_def = 504, + .vts = 0x23c, .timeperframe = { .numerator = 100, .denominator = 9043 @@ -567,6 +374,16 @@ static const struct ov7251_mode_info ov7251_mode_info_data[] = { }, }; +static const struct ov7251_mode_info ov7251_mode_info_data[] = { + { + .width = 640, + .height = 480, + .data = ov7251_setting_vga, + .data_size = ARRAY_SIZE(ov7251_setting_vga), + .exposure_def = 504, + }, +}; + static int ov7251_regulators_enable(struct ov7251 *ov7251) { int ret; @@ -691,6 +508,63 @@ static int ov7251_read_reg(struct ov7251 *ov7251, u16 reg, u8 *val) return 0; } +static int ov7251_pll_configure(struct ov7251 *ov7251) +{ + const struct ov7251_pll_configs *configs; + int ret; + + configs = ov7251->pll_configs; + + ret = ov7251_write_reg(ov7251, OV7251_PLL1_PRE_DIV_REG, + configs->pll1->pre_div); + if (ret < 0) + return ret; + + ret = ov7251_write_reg(ov7251, OV7251_PLL1_MULT_REG, + configs->pll1->mult); + if (ret < 0) + return ret; + ret = ov7251_write_reg(ov7251, OV7251_PLL1_DIVIDER_REG, + configs->pll1->div); + if (ret < 0) + return ret; + + ret = ov7251_write_reg(ov7251, OV7251_PLL1_PIX_DIV_REG, + configs->pll1->pix_div); + if (ret < 0) + return ret; + + ret = ov7251_write_reg(ov7251, OV7251_PLL1_MIPI_DIV_REG, + configs->pll1->mipi_div); + if (ret < 0) + return ret; + + ret = ov7251_write_reg(ov7251, OV7251_PLL2_PRE_DIV_REG, + configs->pll2->pre_div); + if (ret < 0) + return ret; + + ret = ov7251_write_reg(ov7251, OV7251_PLL2_MULT_REG, + configs->pll2->mult); + if (ret < 0) + return ret; + + ret = ov7251_write_reg(ov7251, OV7251_PLL2_DIVIDER_REG, + configs->pll2->div); + if (ret < 0) + return ret; + + ret = ov7251_write_reg(ov7251, OV7251_PLL2_SYS_DIV_REG, + configs->pll2->sys_div); + if (ret < 0) + return ret; + + ret = ov7251_write_reg(ov7251, OV7251_PLL2_ADC_DIV_REG, + configs->pll2->adc_div); + + return ret; +} + static int ov7251_set_exposure(struct ov7251 *ov7251, s32 exposure) { u16 reg; @@ -765,41 +639,22 @@ static void ov7251_set_power_off(struct ov7251 *ov7251) ov7251_regulators_disable(ov7251); } -static int ov7251_s_power(struct v4l2_subdev *sd, int on) +static int __maybe_unused ov7251_sensor_suspend(struct device *dev) { + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct ov7251 *ov7251 = to_ov7251(sd); - int ret = 0; - - mutex_lock(&ov7251->lock); - - /* If the power state is not modified - no work to do. */ - if (ov7251->power_on == !!on) - goto exit; - - if (on) { - ret = ov7251_set_power_on(ov7251); - if (ret < 0) - goto exit; - ret = ov7251_set_register_array(ov7251, - ov7251_global_init_setting, - ARRAY_SIZE(ov7251_global_init_setting)); - if (ret < 0) { - dev_err(ov7251->dev, "could not set init registers\n"); - ov7251_set_power_off(ov7251); - goto exit; - } + ov7251_set_power_off(ov7251); - ov7251->power_on = true; - } else { - ov7251_set_power_off(ov7251); - ov7251->power_on = false; - } + return 0; +} -exit: - mutex_unlock(&ov7251->lock); +static int __maybe_unused ov7251_sensor_resume(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov7251 *ov7251 = to_ov7251(sd); - return ret; + return ov7251_set_power_on(ov7251); } static int ov7251_set_hflip(struct ov7251 *ov7251, s32 value) @@ -836,6 +691,19 @@ static int ov7251_set_vflip(struct ov7251 *ov7251, s32 value) return ret; } +static int ov7251_set_vblank(struct ov7251 *ov7251, s32 value) +{ + u16 reg; + u8 val[2]; + + reg = OV7251_VTS_HIGH; + value += ov7251->current_mode->height; + val[0] = (value & 0xff00) >> 8; /* goes to OV7251_VTS_HIGH */ + val[1] = value & 0xff; /* goes to OV7251_VTS_LOW */ + + return ov7251_write_seq_regs(ov7251, reg, val, 2); +} + static int ov7251_set_test_pattern(struct ov7251 *ov7251, s32 value) { u8 val = ov7251->pre_isp_00; @@ -862,18 +730,29 @@ static int ov7251_s_ctrl(struct v4l2_ctrl *ctrl) { struct ov7251 *ov7251 = container_of(ctrl->handler, struct ov7251, ctrls); + s64 max; int ret; /* v4l2_ctrl_lock() locks our mutex */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ + max = ov7251->current_mode->height + ctrl->val - OV7251_EXPOSURE_OFFSET; + __v4l2_ctrl_modify_range(ov7251->exposure, + ov7251->exposure->minimum, max, + ov7251->exposure->step, + ov7251->exposure->default_value); + break; + } - if (!ov7251->power_on) + if (!pm_runtime_get_if_in_use(ov7251->dev)) return 0; switch (ctrl->id) { case V4L2_CID_EXPOSURE: ret = ov7251_set_exposure(ov7251, ctrl->val); break; - case V4L2_CID_GAIN: + case V4L2_CID_ANALOGUE_GAIN: ret = ov7251_set_gain(ov7251, ctrl->val); break; case V4L2_CID_TEST_PATTERN: @@ -885,11 +764,16 @@ static int ov7251_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_VFLIP: ret = ov7251_set_vflip(ov7251, ctrl->val); break; + case V4L2_CID_VBLANK: + ret = ov7251_set_vblank(ov7251, ctrl->val); + break; default: ret = -EINVAL; break; } + pm_runtime_put(ov7251->dev); + return ret; } @@ -934,13 +818,13 @@ static int ov7251_enum_frame_ival(struct v4l2_subdev *subdev, unsigned int index = fie->index; unsigned int i; - for (i = 0; i < ARRAY_SIZE(ov7251_mode_info_data); i++) { - if (fie->width != ov7251_mode_info_data[i].width || - fie->height != ov7251_mode_info_data[i].height) + for (i = 0; i < ARRAY_SIZE(ov7251_frame_ival_info_data); i++) { + if (fie->width != ov7251_mode_info_data[0].width || + fie->height != ov7251_mode_info_data[0].height) continue; if (index-- == 0) { - fie->interval = ov7251_mode_info_data[i].timeperframe; + fie->interval = ov7251_frame_ival_info_data[i].timeperframe; return 0; } } @@ -999,23 +883,18 @@ static inline u32 avg_fps(const struct v4l2_fract *t) return (t->denominator + (t->numerator >> 1)) / t->numerator; } -static const struct ov7251_mode_info * -ov7251_find_mode_by_ival(struct ov7251 *ov7251, struct v4l2_fract *timeperframe) +static const struct ov7251_frame_ival_info * +ov7251_find_frame_ival_by_ival(struct ov7251 *ov7251, struct v4l2_fract *timeperframe) { - const struct ov7251_mode_info *mode = ov7251->current_mode; unsigned int fps_req = avg_fps(timeperframe); unsigned int max_dist_match = (unsigned int) -1; unsigned int i, n = 0; - for (i = 0; i < ARRAY_SIZE(ov7251_mode_info_data); i++) { + for (i = 0; i < ARRAY_SIZE(ov7251_frame_ival_info_data); i++) { unsigned int dist; unsigned int fps_tmp; - if (mode->width != ov7251_mode_info_data[i].width || - mode->height != ov7251_mode_info_data[i].height) - continue; - - fps_tmp = avg_fps(&ov7251_mode_info_data[i].timeperframe); + fps_tmp = avg_fps(&ov7251_frame_ival_info_data[i].timeperframe); dist = abs(fps_req - fps_tmp); @@ -1025,7 +904,7 @@ ov7251_find_mode_by_ival(struct ov7251 *ov7251, struct v4l2_fract *timeperframe) } } - return &ov7251_mode_info_data[n]; + return &ov7251_frame_ival_info_data[n]; } static int ov7251_set_format(struct v4l2_subdev *sd, @@ -1036,6 +915,7 @@ static int ov7251_set_format(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *__format; struct v4l2_rect *__crop; const struct ov7251_mode_info *new_mode; + s64 h_blank; int ret = 0; mutex_lock(&ov7251->lock); @@ -1052,18 +932,14 @@ static int ov7251_set_format(struct v4l2_subdev *sd, __crop->height = new_mode->height; if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - ret = __v4l2_ctrl_s_ctrl_int64(ov7251->pixel_clock, - new_mode->pixel_clock); - if (ret < 0) - goto exit; - - ret = __v4l2_ctrl_s_ctrl(ov7251->link_freq, - new_mode->link_freq); - if (ret < 0) - goto exit; - - ret = __v4l2_ctrl_modify_range(ov7251->exposure, - 1, new_mode->exposure_max, + h_blank = OV7251_HTS - new_mode->width; + __v4l2_ctrl_modify_range(ov7251->hblank, h_blank, + h_blank, 1, h_blank); + __v4l2_ctrl_s_ctrl(ov7251->hblank, h_blank); + + ret = __v4l2_ctrl_modify_range(ov7251->exposure, 1, + ov7251->current_ival->vts - + OV7251_EXPOSURE_OFFSET, 1, new_mode->exposure_def); if (ret < 0) goto exit; @@ -1073,10 +949,6 @@ static int ov7251_set_format(struct v4l2_subdev *sd, if (ret < 0) goto exit; - ret = __v4l2_ctrl_s_ctrl(ov7251->gain, 16); - if (ret < 0) - goto exit; - ov7251->current_mode = new_mode; } @@ -1123,15 +995,34 @@ static int ov7251_get_selection(struct v4l2_subdev *sd, { struct ov7251 *ov7251 = to_ov7251(sd); - if (sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + mutex_lock(&ov7251->lock); + sel->r = *__ov7251_get_pad_crop(ov7251, sd_state, sel->pad, + sel->which); + mutex_unlock(&ov7251->lock); - mutex_lock(&ov7251->lock); - sel->r = *__ov7251_get_pad_crop(ov7251, sd_state, sel->pad, - sel->which); - mutex_unlock(&ov7251->lock); + return 0; - return 0; + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = OV7251_NATIVE_WIDTH; + sel->r.height = OV7251_NATIVE_HEIGHT; + + return 0; + + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = OV7251_PIXEL_ARRAY_TOP; + sel->r.left = OV7251_PIXEL_ARRAY_LEFT; + sel->r.width = OV7251_PIXEL_ARRAY_WIDTH; + sel->r.height = OV7251_PIXEL_ARRAY_HEIGHT; + + return 0; + } + + return -EINVAL; } static int ov7251_s_stream(struct v4l2_subdev *subdev, int enable) @@ -1142,6 +1033,24 @@ static int ov7251_s_stream(struct v4l2_subdev *subdev, int enable) mutex_lock(&ov7251->lock); if (enable) { + ret = pm_runtime_get_sync(ov7251->dev); + if (ret < 0) + return ret; + + ret = ov7251_set_register_array(ov7251, + ov7251_global_init_setting, + ARRAY_SIZE(ov7251_global_init_setting)); + if (ret < 0) { + dev_err(ov7251->dev, "could not set global_init_setting\n"); + goto err_power_down; + } + + ret = ov7251_pll_configure(ov7251); + if (ret) { + dev_err(ov7251->dev, "error configuring PLLs\n"); + goto err_power_down; + } + ret = ov7251_set_register_array(ov7251, ov7251->current_mode->data, ov7251->current_mode->data_size); @@ -1149,23 +1058,29 @@ static int ov7251_s_stream(struct v4l2_subdev *subdev, int enable) dev_err(ov7251->dev, "could not set mode %dx%d\n", ov7251->current_mode->width, ov7251->current_mode->height); - goto exit; + goto err_power_down; } ret = __v4l2_ctrl_handler_setup(&ov7251->ctrls); if (ret < 0) { dev_err(ov7251->dev, "could not sync v4l2 controls\n"); - goto exit; + goto err_power_down; } ret = ov7251_write_reg(ov7251, OV7251_SC_MODE_SELECT, OV7251_SC_MODE_SELECT_STREAMING); + if (ret) + goto err_power_down; } else { ret = ov7251_write_reg(ov7251, OV7251_SC_MODE_SELECT, OV7251_SC_MODE_SELECT_SW_STANDBY); + pm_runtime_put(ov7251->dev); } -exit: mutex_unlock(&ov7251->lock); + return ret; +err_power_down: + pm_runtime_put_noidle(ov7251->dev); + mutex_unlock(&ov7251->lock); return ret; } @@ -1175,7 +1090,7 @@ static int ov7251_get_frame_interval(struct v4l2_subdev *subdev, struct ov7251 *ov7251 = to_ov7251(subdev); mutex_lock(&ov7251->lock); - fi->interval = ov7251->current_mode->timeperframe; + fi->interval = ov7251->current_ival->timeperframe; mutex_unlock(&ov7251->lock); return 0; @@ -1185,42 +1100,30 @@ static int ov7251_set_frame_interval(struct v4l2_subdev *subdev, struct v4l2_subdev_frame_interval *fi) { struct ov7251 *ov7251 = to_ov7251(subdev); - const struct ov7251_mode_info *new_mode; + const struct ov7251_frame_ival_info *new_ival; int ret = 0; mutex_lock(&ov7251->lock); - new_mode = ov7251_find_mode_by_ival(ov7251, &fi->interval); - - if (new_mode != ov7251->current_mode) { - ret = __v4l2_ctrl_s_ctrl_int64(ov7251->pixel_clock, - new_mode->pixel_clock); - if (ret < 0) - goto exit; - - ret = __v4l2_ctrl_s_ctrl(ov7251->link_freq, - new_mode->link_freq); - if (ret < 0) - goto exit; - - ret = __v4l2_ctrl_modify_range(ov7251->exposure, - 1, new_mode->exposure_max, - 1, new_mode->exposure_def); - if (ret < 0) - goto exit; + new_ival = ov7251_find_frame_ival_by_ival(ov7251, &fi->interval); - ret = __v4l2_ctrl_s_ctrl(ov7251->exposure, - new_mode->exposure_def); + if (new_ival != ov7251->current_ival) { + ret = __v4l2_ctrl_modify_range(ov7251->exposure, 1, + new_ival->vts - + OV7251_EXPOSURE_OFFSET, + 1, ov7251->exposure->val); if (ret < 0) goto exit; - ret = __v4l2_ctrl_s_ctrl(ov7251->gain, 16); + ret = __v4l2_ctrl_s_ctrl(ov7251->vblank, + new_ival->vts - + ov7251->current_mode->height); if (ret < 0) goto exit; - ov7251->current_mode = new_mode; + ov7251->current_ival = new_ival; } - fi->interval = ov7251->current_mode->timeperframe; + fi->interval = ov7251->current_ival->timeperframe; exit: mutex_unlock(&ov7251->lock); @@ -1228,10 +1131,6 @@ static int ov7251_set_frame_interval(struct v4l2_subdev *subdev, return ret; } -static const struct v4l2_subdev_core_ops ov7251_core_ops = { - .s_power = ov7251_s_power, -}; - static const struct v4l2_subdev_video_ops ov7251_video_ops = { .s_stream = ov7251_s_stream, .g_frame_interval = ov7251_get_frame_interval, @@ -1249,19 +1148,117 @@ static const struct v4l2_subdev_pad_ops ov7251_subdev_pad_ops = { }; static const struct v4l2_subdev_ops ov7251_subdev_ops = { - .core = &ov7251_core_ops, .video = &ov7251_video_ops, .pad = &ov7251_subdev_pad_ops, }; +static int ov7251_check_hwcfg(struct ov7251 *ov7251) +{ + struct fwnode_handle *fwnode = dev_fwnode(ov7251->dev); + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; + struct fwnode_handle *endpoint; + bool freq_found; + int i, j; + int ret; + + endpoint = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!endpoint) + return -EPROBE_DEFER; /* could be provided by cio2-bridge */ + + ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg); + fwnode_handle_put(endpoint); + if (ret) + return dev_err_probe(ov7251->dev, ret, + "parsing endpoint node failed\n"); + + if (bus_cfg.bus_type != V4L2_MBUS_CSI2_DPHY) { + ret = -EINVAL; + dev_err(ov7251->dev, "invalid bus type (%u), must be (%u)\n", + bus_cfg.bus_type, V4L2_MBUS_CSI2_DPHY); + goto out_free_bus_cfg; + } + + if (bus_cfg.bus.mipi_csi2.num_data_lanes != 1) { + dev_err(ov7251->dev, "only a 1-lane CSI2 config is supported"); + ret = -EINVAL; + goto out_free_bus_cfg; + } + + if (!bus_cfg.nr_of_link_frequencies) { + dev_err(ov7251->dev, "no link frequencies defined\n"); + ret = -EINVAL; + goto out_free_bus_cfg; + } + + freq_found = false; + for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++) { + for (j = 0; j < ARRAY_SIZE(link_freq); j++) + if (bus_cfg.link_frequencies[i] == link_freq[j]) { + freq_found = true; + break; + } + + if (freq_found) + break; + } + + if (i == bus_cfg.nr_of_link_frequencies) { + dev_err(ov7251->dev, "no supported link freq found\n"); + ret = -EINVAL; + goto out_free_bus_cfg; + } + +out_free_bus_cfg: + v4l2_fwnode_endpoint_free(&bus_cfg); + + return ret; +} + +static int ov7251_detect_chip(struct ov7251 *ov7251) +{ + u8 chip_id_high, chip_id_low, chip_rev; + int ret; + + ret = ov7251_read_reg(ov7251, OV7251_CHIP_ID_HIGH, &chip_id_high); + if (ret < 0 || chip_id_high != OV7251_CHIP_ID_HIGH_BYTE) + return dev_err_probe(ov7251->dev, -ENODEV, + "could not read ID high\n"); + + ret = ov7251_read_reg(ov7251, OV7251_CHIP_ID_LOW, &chip_id_low); + if (ret < 0 || chip_id_low != OV7251_CHIP_ID_LOW_BYTE) + return dev_err_probe(ov7251->dev, -ENODEV, + "could not read ID low\n"); + + ret = ov7251_read_reg(ov7251, OV7251_SC_GP_IO_IN1, &chip_rev); + if (ret < 0) + return dev_err_probe(ov7251->dev, -ENODEV, + "could not read revision\n"); + chip_rev >>= 4; + + dev_info(ov7251->dev, + "OV7251 revision %x (%s) detected at address 0x%02x\n", + chip_rev, + chip_rev == 0x4 ? "1A / 1B" : + chip_rev == 0x5 ? "1C / 1D" : + chip_rev == 0x6 ? "1E" : + chip_rev == 0x7 ? "1F" : "unknown", + ov7251->i2c_client->addr); + + return 0; +} + static int ov7251_probe(struct i2c_client *client) { struct v4l2_fwnode_device_properties props; struct device *dev = &client->dev; - struct fwnode_handle *endpoint; + struct v4l2_ctrl *ctrl; struct ov7251 *ov7251; - u8 chip_id_high, chip_id_low, chip_rev; + unsigned int rate = 0; + u32 h_blank, v_blank, v_blank_max; int ret; + int i; ov7251 = devm_kzalloc(dev, sizeof(struct ov7251), GFP_KERNEL); if (!ov7251) @@ -1270,51 +1267,45 @@ static int ov7251_probe(struct i2c_client *client) ov7251->i2c_client = client; ov7251->dev = dev; - endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); - if (!endpoint) { - dev_err(dev, "endpoint node not found\n"); - return -EINVAL; - } - - ret = v4l2_fwnode_endpoint_parse(endpoint, &ov7251->ep); - fwnode_handle_put(endpoint); - if (ret < 0) { - dev_err(dev, "parsing endpoint node failed\n"); + ret = ov7251_check_hwcfg(ov7251); + if (ret) return ret; - } - - if (ov7251->ep.bus_type != V4L2_MBUS_CSI2_DPHY) { - dev_err(dev, "invalid bus type (%u), must be CSI2 (%u)\n", - ov7251->ep.bus_type, V4L2_MBUS_CSI2_DPHY); - return -EINVAL; - } /* get system clock (xclk) */ - ov7251->xclk = devm_clk_get(dev, "xclk"); + ov7251->xclk = devm_clk_get(dev, NULL); if (IS_ERR(ov7251->xclk)) { dev_err(dev, "could not get xclk"); return PTR_ERR(ov7251->xclk); } + /* + * We could have either a 24MHz or 19.2MHz clock rate from either dt or + * ACPI. We also need to support the IPU3 case which will have both an + * external clock AND a clock-frequency property. + */ ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", - &ov7251->xclk_freq); - if (ret) { - dev_err(dev, "could not get xclk frequency\n"); - return ret; + &rate); + if (!ret && ov7251->xclk) { + ret = clk_set_rate(ov7251->xclk, rate); + if (ret) + return dev_err_probe(dev, ret, + "failed to set clock rate\n"); + } else if (ret && !ov7251->xclk) { + return dev_err_probe(dev, ret, "invalid clock config\n"); } - /* external clock must be 24MHz, allow 1% tolerance */ - if (ov7251->xclk_freq < 23760000 || ov7251->xclk_freq > 24240000) { - dev_err(dev, "external clock frequency %u is not supported\n", - ov7251->xclk_freq); - return -EINVAL; - } + ov7251->xclk_freq = rate ? rate : clk_get_rate(ov7251->xclk); - ret = clk_set_rate(ov7251->xclk, ov7251->xclk_freq); - if (ret) { - dev_err(dev, "could not set xclk frequency\n"); - return ret; - } + for (i = 0; i < ARRAY_SIZE(supported_xclk_rates); i++) + if (ov7251->xclk_freq == supported_xclk_rates[i]) + break; + + if (i == ARRAY_SIZE(supported_xclk_rates)) + return dev_err_probe(dev, -EINVAL, + "clock rate %u Hz is unsupported\n", + ov7251->xclk_freq); + + ov7251->pll_configs = ov7251_pll_configs[i]; ov7251->io_regulator = devm_regulator_get(dev, "vdddo"); if (IS_ERR(ov7251->io_regulator)) { @@ -1343,7 +1334,10 @@ static int ov7251_probe(struct i2c_client *client) mutex_init(&ov7251->lock); - v4l2_ctrl_handler_init(&ov7251->ctrls, 9); + ov7251->current_mode = &ov7251_mode_info_data[0]; + ov7251->current_ival = &ov7251_frame_ival_info_data[0]; + + v4l2_ctrl_handler_init(&ov7251->ctrls, 11); ov7251->ctrls.lock = &ov7251->lock; v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops, @@ -1352,23 +1346,34 @@ static int ov7251_probe(struct i2c_client *client) V4L2_CID_VFLIP, 0, 1, 1, 0); ov7251->exposure = v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops, V4L2_CID_EXPOSURE, 1, 32, 1, 32); - ov7251->gain = v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops, - V4L2_CID_GAIN, 16, 1023, 1, 16); + v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, 16, 1023, 1, 16); v4l2_ctrl_new_std_menu_items(&ov7251->ctrls, &ov7251_ctrl_ops, V4L2_CID_TEST_PATTERN, ARRAY_SIZE(ov7251_test_pattern_menu) - 1, 0, 0, ov7251_test_pattern_menu); - ov7251->pixel_clock = v4l2_ctrl_new_std(&ov7251->ctrls, - &ov7251_ctrl_ops, - V4L2_CID_PIXEL_RATE, - 1, INT_MAX, 1, 1); - ov7251->link_freq = v4l2_ctrl_new_int_menu(&ov7251->ctrls, - &ov7251_ctrl_ops, - V4L2_CID_LINK_FREQ, - ARRAY_SIZE(link_freq) - 1, - 0, link_freq); - if (ov7251->link_freq) - ov7251->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops, + V4L2_CID_PIXEL_RATE, OV7251_PIXEL_CLOCK, + OV7251_PIXEL_CLOCK, 1, OV7251_PIXEL_CLOCK); + ctrl = v4l2_ctrl_new_int_menu(&ov7251->ctrls, &ov7251_ctrl_ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(link_freq) - 1, + 0, link_freq); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + h_blank = OV7251_HTS - ov7251->current_mode->width; + ov7251->hblank = v4l2_ctrl_new_std(&ov7251->ctrls, NULL, + V4L2_CID_HBLANK, h_blank, + h_blank, 1, h_blank); + if (ov7251->hblank) + ov7251->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v_blank = ov7251->current_ival->vts - ov7251->current_mode->height; + v_blank_max = OV7251_VTS_MAX - ov7251->current_mode->width; + ov7251->vblank = v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops, + V4L2_CID_VBLANK, + OV7251_VTS_MIN_OFFSET, + v_blank_max, 1, v_blank); ov7251->sd.ctrl_handler = &ov7251->ctrls; @@ -1400,47 +1405,24 @@ static int ov7251_probe(struct i2c_client *client) goto free_ctrl; } - ret = ov7251_s_power(&ov7251->sd, true); - if (ret < 0) { - dev_err(dev, "could not power up OV7251\n"); + ret = ov7251_set_power_on(ov7251); + if (ret) goto free_entity; - } - - ret = ov7251_read_reg(ov7251, OV7251_CHIP_ID_HIGH, &chip_id_high); - if (ret < 0 || chip_id_high != OV7251_CHIP_ID_HIGH_BYTE) { - dev_err(dev, "could not read ID high\n"); - ret = -ENODEV; - goto power_down; - } - ret = ov7251_read_reg(ov7251, OV7251_CHIP_ID_LOW, &chip_id_low); - if (ret < 0 || chip_id_low != OV7251_CHIP_ID_LOW_BYTE) { - dev_err(dev, "could not read ID low\n"); - ret = -ENODEV; - goto power_down; - } - ret = ov7251_read_reg(ov7251, OV7251_SC_GP_IO_IN1, &chip_rev); - if (ret < 0) { - dev_err(dev, "could not read revision\n"); - ret = -ENODEV; + ret = ov7251_detect_chip(ov7251); + if (ret) goto power_down; - } - chip_rev >>= 4; - dev_info(dev, "OV7251 revision %x (%s) detected at address 0x%02x\n", - chip_rev, - chip_rev == 0x4 ? "1A / 1B" : - chip_rev == 0x5 ? "1C / 1D" : - chip_rev == 0x6 ? "1E" : - chip_rev == 0x7 ? "1F" : "unknown", - client->addr); + pm_runtime_set_active(&client->dev); + pm_runtime_get_noresume(&client->dev); + pm_runtime_enable(&client->dev); ret = ov7251_read_reg(ov7251, OV7251_PRE_ISP_00, &ov7251->pre_isp_00); if (ret < 0) { dev_err(dev, "could not read test pattern value\n"); ret = -ENODEV; - goto power_down; + goto err_pm_runtime; } ret = ov7251_read_reg(ov7251, OV7251_TIMING_FORMAT1, @@ -1448,7 +1430,7 @@ static int ov7251_probe(struct i2c_client *client) if (ret < 0) { dev_err(dev, "could not read vflip value\n"); ret = -ENODEV; - goto power_down; + goto err_pm_runtime; } ret = ov7251_read_reg(ov7251, OV7251_TIMING_FORMAT2, @@ -1456,10 +1438,12 @@ static int ov7251_probe(struct i2c_client *client) if (ret < 0) { dev_err(dev, "could not read hflip value\n"); ret = -ENODEV; - goto power_down; + goto err_pm_runtime; } - ov7251_s_power(&ov7251->sd, false); + pm_runtime_set_autosuspend_delay(&client->dev, 1000); + pm_runtime_use_autosuspend(&client->dev); + pm_runtime_put_autosuspend(&client->dev); ret = v4l2_async_register_subdev(&ov7251->sd); if (ret < 0) { @@ -1471,8 +1455,11 @@ static int ov7251_probe(struct i2c_client *client) return 0; +err_pm_runtime: + pm_runtime_disable(ov7251->dev); + pm_runtime_put_noidle(ov7251->dev); power_down: - ov7251_s_power(&ov7251->sd, false); + ov7251_set_power_off(ov7251); free_entity: media_entity_cleanup(&ov7251->sd.entity); free_ctrl: @@ -1492,19 +1479,36 @@ static int ov7251_remove(struct i2c_client *client) v4l2_ctrl_handler_free(&ov7251->ctrls); mutex_destroy(&ov7251->lock); + pm_runtime_disable(ov7251->dev); + if (!pm_runtime_status_suspended(ov7251->dev)) + ov7251_set_power_off(ov7251); + pm_runtime_set_suspended(ov7251->dev); + return 0; } +static const struct dev_pm_ops ov7251_pm_ops = { + SET_RUNTIME_PM_OPS(ov7251_sensor_suspend, ov7251_sensor_resume, NULL) +}; + static const struct of_device_id ov7251_of_match[] = { { .compatible = "ovti,ov7251" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, ov7251_of_match); +static const struct acpi_device_id ov7251_acpi_match[] = { + { "INT347E" }, + { } +}; +MODULE_DEVICE_TABLE(acpi, ov7251_acpi_match); + static struct i2c_driver ov7251_i2c_driver = { .driver = { .of_match_table = ov7251_of_match, + .acpi_match_table = ov7251_acpi_match, .name = "ov7251", + .pm = &ov7251_pm_ops, }, .probe_new = ov7251_probe, .remove = ov7251_remove,