2019-05-27 14:55:01 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2017-06-08 02:33:56 +08:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved.
|
|
|
|
* Copyright (C) 2014-2017 Mentor Graphics Inc.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/clk.h>
|
|
|
|
#include <linux/clk-provider.h>
|
|
|
|
#include <linux/clkdev.h>
|
|
|
|
#include <linux/ctype.h>
|
|
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/device.h>
|
2018-02-01 16:44:06 +08:00
|
|
|
#include <linux/gpio/consumer.h>
|
2017-06-08 02:33:56 +08:00
|
|
|
#include <linux/i2c.h>
|
|
|
|
#include <linux/init.h>
|
2023-07-15 01:47:08 +08:00
|
|
|
#include <linux/mod_devicetable.h>
|
2017-06-08 02:33:56 +08:00
|
|
|
#include <linux/module.h>
|
2022-07-21 15:41:38 +08:00
|
|
|
#include <linux/pm_runtime.h>
|
2018-02-01 16:44:06 +08:00
|
|
|
#include <linux/regulator/consumer.h>
|
2017-06-08 02:33:56 +08:00
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/types.h>
|
|
|
|
#include <media/v4l2-async.h>
|
|
|
|
#include <media/v4l2-ctrls.h>
|
|
|
|
#include <media/v4l2-device.h>
|
2018-11-13 00:00:52 +08:00
|
|
|
#include <media/v4l2-event.h>
|
2017-06-08 02:33:56 +08:00
|
|
|
#include <media/v4l2-fwnode.h>
|
|
|
|
#include <media/v4l2-subdev.h>
|
|
|
|
|
|
|
|
/* min/typical/max system clock (xclk) frequencies */
|
|
|
|
#define OV5640_XCLK_MIN 6000000
|
2018-06-06 17:11:38 +08:00
|
|
|
#define OV5640_XCLK_MAX 54000000
|
2017-06-08 02:33:56 +08:00
|
|
|
|
2022-05-13 22:13:58 +08:00
|
|
|
#define OV5640_NATIVE_WIDTH 2624
|
|
|
|
#define OV5640_NATIVE_HEIGHT 1964
|
|
|
|
#define OV5640_PIXEL_ARRAY_TOP 14
|
|
|
|
#define OV5640_PIXEL_ARRAY_LEFT 16
|
|
|
|
#define OV5640_PIXEL_ARRAY_WIDTH 2592
|
|
|
|
#define OV5640_PIXEL_ARRAY_HEIGHT 1944
|
|
|
|
|
2022-05-13 22:14:04 +08:00
|
|
|
/* FIXME: not documented. */
|
|
|
|
#define OV5640_MIN_VBLANK 24
|
|
|
|
#define OV5640_MAX_VTS 3375
|
|
|
|
|
2017-06-08 02:33:56 +08:00
|
|
|
#define OV5640_DEFAULT_SLAVE_ID 0x3c
|
|
|
|
|
2022-05-13 22:13:54 +08:00
|
|
|
#define OV5640_LINK_RATE_MAX 490000000U
|
|
|
|
|
2018-01-31 17:08:10 +08:00
|
|
|
#define OV5640_REG_SYS_RESET02 0x3002
|
|
|
|
#define OV5640_REG_SYS_CLOCK_ENABLE02 0x3006
|
2018-01-03 17:57:31 +08:00
|
|
|
#define OV5640_REG_SYS_CTRL0 0x3008
|
2020-09-05 04:18:30 +08:00
|
|
|
#define OV5640_REG_SYS_CTRL0_SW_PWDN 0x42
|
|
|
|
#define OV5640_REG_SYS_CTRL0_SW_PWUP 0x02
|
2023-01-03 20:27:35 +08:00
|
|
|
#define OV5640_REG_SYS_CTRL0_SW_RST 0x82
|
2017-06-08 02:33:56 +08:00
|
|
|
#define OV5640_REG_CHIP_ID 0x300a
|
2018-01-03 17:57:31 +08:00
|
|
|
#define OV5640_REG_IO_MIPI_CTRL00 0x300e
|
|
|
|
#define OV5640_REG_PAD_OUTPUT_ENABLE01 0x3017
|
|
|
|
#define OV5640_REG_PAD_OUTPUT_ENABLE02 0x3018
|
2017-06-08 02:33:56 +08:00
|
|
|
#define OV5640_REG_PAD_OUTPUT00 0x3019
|
2018-01-03 17:57:31 +08:00
|
|
|
#define OV5640_REG_SYSTEM_CONTROL1 0x302e
|
2017-06-08 02:33:56 +08:00
|
|
|
#define OV5640_REG_SC_PLL_CTRL0 0x3034
|
|
|
|
#define OV5640_REG_SC_PLL_CTRL1 0x3035
|
|
|
|
#define OV5640_REG_SC_PLL_CTRL2 0x3036
|
|
|
|
#define OV5640_REG_SC_PLL_CTRL3 0x3037
|
|
|
|
#define OV5640_REG_SLAVE_ID 0x3100
|
2018-01-03 17:57:31 +08:00
|
|
|
#define OV5640_REG_SCCB_SYS_CTRL1 0x3103
|
2017-06-08 02:33:56 +08:00
|
|
|
#define OV5640_REG_SYS_ROOT_DIVIDER 0x3108
|
|
|
|
#define OV5640_REG_AWB_R_GAIN 0x3400
|
|
|
|
#define OV5640_REG_AWB_G_GAIN 0x3402
|
|
|
|
#define OV5640_REG_AWB_B_GAIN 0x3404
|
|
|
|
#define OV5640_REG_AWB_MANUAL_CTRL 0x3406
|
|
|
|
#define OV5640_REG_AEC_PK_EXPOSURE_HI 0x3500
|
|
|
|
#define OV5640_REG_AEC_PK_EXPOSURE_MED 0x3501
|
|
|
|
#define OV5640_REG_AEC_PK_EXPOSURE_LO 0x3502
|
|
|
|
#define OV5640_REG_AEC_PK_MANUAL 0x3503
|
|
|
|
#define OV5640_REG_AEC_PK_REAL_GAIN 0x350a
|
|
|
|
#define OV5640_REG_AEC_PK_VTS 0x350c
|
2022-05-13 22:13:56 +08:00
|
|
|
#define OV5640_REG_TIMING_HS 0x3800
|
|
|
|
#define OV5640_REG_TIMING_VS 0x3802
|
|
|
|
#define OV5640_REG_TIMING_HW 0x3804
|
|
|
|
#define OV5640_REG_TIMING_VH 0x3806
|
2018-04-16 20:36:56 +08:00
|
|
|
#define OV5640_REG_TIMING_DVPHO 0x3808
|
|
|
|
#define OV5640_REG_TIMING_DVPVO 0x380a
|
2017-06-08 02:33:56 +08:00
|
|
|
#define OV5640_REG_TIMING_HTS 0x380c
|
|
|
|
#define OV5640_REG_TIMING_VTS 0x380e
|
2022-05-13 22:13:56 +08:00
|
|
|
#define OV5640_REG_TIMING_HOFFS 0x3810
|
|
|
|
#define OV5640_REG_TIMING_VOFFS 0x3812
|
2018-06-18 18:29:17 +08:00
|
|
|
#define OV5640_REG_TIMING_TC_REG20 0x3820
|
2017-06-08 02:33:56 +08:00
|
|
|
#define OV5640_REG_TIMING_TC_REG21 0x3821
|
|
|
|
#define OV5640_REG_AEC_CTRL00 0x3a00
|
|
|
|
#define OV5640_REG_AEC_B50_STEP 0x3a08
|
|
|
|
#define OV5640_REG_AEC_B60_STEP 0x3a0a
|
|
|
|
#define OV5640_REG_AEC_CTRL0D 0x3a0d
|
|
|
|
#define OV5640_REG_AEC_CTRL0E 0x3a0e
|
|
|
|
#define OV5640_REG_AEC_CTRL0F 0x3a0f
|
|
|
|
#define OV5640_REG_AEC_CTRL10 0x3a10
|
|
|
|
#define OV5640_REG_AEC_CTRL11 0x3a11
|
|
|
|
#define OV5640_REG_AEC_CTRL1B 0x3a1b
|
|
|
|
#define OV5640_REG_AEC_CTRL1E 0x3a1e
|
|
|
|
#define OV5640_REG_AEC_CTRL1F 0x3a1f
|
|
|
|
#define OV5640_REG_HZ5060_CTRL00 0x3c00
|
|
|
|
#define OV5640_REG_HZ5060_CTRL01 0x3c01
|
|
|
|
#define OV5640_REG_SIGMADELTA_CTRL0C 0x3c0c
|
|
|
|
#define OV5640_REG_FRAME_CTRL01 0x4202
|
2018-01-03 17:57:32 +08:00
|
|
|
#define OV5640_REG_FORMAT_CONTROL00 0x4300
|
2019-01-18 16:52:05 +08:00
|
|
|
#define OV5640_REG_VFIFO_HSIZE 0x4602
|
|
|
|
#define OV5640_REG_VFIFO_VSIZE 0x4604
|
2019-01-18 16:52:06 +08:00
|
|
|
#define OV5640_REG_JPG_MODE_SELECT 0x4713
|
2020-09-05 04:18:34 +08:00
|
|
|
#define OV5640_REG_CCIR656_CTRL00 0x4730
|
2018-01-03 17:57:31 +08:00
|
|
|
#define OV5640_REG_POLARITY_CTRL00 0x4740
|
2017-06-08 02:33:56 +08:00
|
|
|
#define OV5640_REG_MIPI_CTRL00 0x4800
|
|
|
|
#define OV5640_REG_DEBUG_MODE 0x4814
|
2022-05-13 22:13:55 +08:00
|
|
|
#define OV5640_REG_PCLK_PERIOD 0x4837
|
2018-01-03 17:57:32 +08:00
|
|
|
#define OV5640_REG_ISP_FORMAT_MUX_CTRL 0x501f
|
2017-06-08 02:33:56 +08:00
|
|
|
#define OV5640_REG_PRE_ISP_TEST_SET1 0x503d
|
|
|
|
#define OV5640_REG_SDE_CTRL0 0x5580
|
|
|
|
#define OV5640_REG_SDE_CTRL1 0x5581
|
|
|
|
#define OV5640_REG_SDE_CTRL3 0x5583
|
|
|
|
#define OV5640_REG_SDE_CTRL4 0x5584
|
|
|
|
#define OV5640_REG_SDE_CTRL5 0x5585
|
|
|
|
#define OV5640_REG_AVG_READOUT 0x56a1
|
|
|
|
|
|
|
|
enum ov5640_mode_id {
|
2020-10-08 16:29:16 +08:00
|
|
|
OV5640_MODE_QQVGA_160_120 = 0,
|
|
|
|
OV5640_MODE_QCIF_176_144,
|
2017-06-08 02:33:56 +08:00
|
|
|
OV5640_MODE_QVGA_320_240,
|
|
|
|
OV5640_MODE_VGA_640_480,
|
|
|
|
OV5640_MODE_NTSC_720_480,
|
|
|
|
OV5640_MODE_PAL_720_576,
|
|
|
|
OV5640_MODE_XGA_1024_768,
|
|
|
|
OV5640_MODE_720P_1280_720,
|
|
|
|
OV5640_MODE_1080P_1920_1080,
|
|
|
|
OV5640_MODE_QSXGA_2592_1944,
|
|
|
|
OV5640_NUM_MODES,
|
|
|
|
};
|
|
|
|
|
|
|
|
enum ov5640_frame_rate {
|
|
|
|
OV5640_15_FPS = 0,
|
|
|
|
OV5640_30_FPS,
|
2018-12-03 16:44:26 +08:00
|
|
|
OV5640_60_FPS,
|
2017-06-08 02:33:56 +08:00
|
|
|
OV5640_NUM_FRAMERATES,
|
|
|
|
};
|
|
|
|
|
2022-05-13 22:13:49 +08:00
|
|
|
enum ov5640_pixel_rate_id {
|
|
|
|
OV5640_PIXEL_RATE_168M,
|
|
|
|
OV5640_PIXEL_RATE_148M,
|
|
|
|
OV5640_PIXEL_RATE_124M,
|
|
|
|
OV5640_PIXEL_RATE_96M,
|
|
|
|
OV5640_PIXEL_RATE_48M,
|
|
|
|
OV5640_NUM_PIXEL_RATES,
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The chip manual suggests 24/48/96/192 MHz pixel clocks.
|
|
|
|
*
|
|
|
|
* 192MHz exceeds the sysclk limits; use 168MHz as maximum pixel rate for
|
|
|
|
* full resolution mode @15 FPS.
|
|
|
|
*/
|
|
|
|
static const u32 ov5640_pixel_rates[] = {
|
|
|
|
[OV5640_PIXEL_RATE_168M] = 168000000,
|
|
|
|
[OV5640_PIXEL_RATE_148M] = 148000000,
|
|
|
|
[OV5640_PIXEL_RATE_124M] = 124000000,
|
|
|
|
[OV5640_PIXEL_RATE_96M] = 96000000,
|
|
|
|
[OV5640_PIXEL_RATE_48M] = 48000000,
|
|
|
|
};
|
|
|
|
|
2022-05-13 22:13:53 +08:00
|
|
|
/*
|
|
|
|
* MIPI CSI-2 link frequencies.
|
|
|
|
*
|
|
|
|
* Derived from the above defined pixel rate for bpp = (8, 16, 24) and
|
|
|
|
* data_lanes = (1, 2)
|
|
|
|
*
|
|
|
|
* link_freq = (pixel_rate * bpp) / (2 * data_lanes)
|
|
|
|
*/
|
|
|
|
static const s64 ov5640_csi2_link_freqs[] = {
|
|
|
|
992000000, 888000000, 768000000, 744000000, 672000000, 672000000,
|
|
|
|
592000000, 592000000, 576000000, 576000000, 496000000, 496000000,
|
|
|
|
384000000, 384000000, 384000000, 336000000, 296000000, 288000000,
|
|
|
|
248000000, 192000000, 192000000, 192000000, 96000000,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Link freq for default mode: UYVY 16 bpp, 2 data lanes. */
|
|
|
|
#define OV5640_DEFAULT_LINK_FREQ 13
|
|
|
|
|
2018-11-03 00:38:43 +08:00
|
|
|
enum ov5640_format_mux {
|
|
|
|
OV5640_FMT_MUX_YUV422 = 0,
|
|
|
|
OV5640_FMT_MUX_RGB,
|
|
|
|
OV5640_FMT_MUX_DITHER,
|
|
|
|
OV5640_FMT_MUX_RAW_DPC,
|
|
|
|
OV5640_FMT_MUX_SNR_RAW,
|
|
|
|
OV5640_FMT_MUX_RAW_CIP,
|
|
|
|
};
|
|
|
|
|
2022-05-13 22:14:15 +08:00
|
|
|
struct ov5640_pixfmt {
|
2018-01-03 17:57:32 +08:00
|
|
|
u32 code;
|
|
|
|
u32 colorspace;
|
2022-05-13 22:13:52 +08:00
|
|
|
u8 bpp;
|
2022-05-13 22:14:16 +08:00
|
|
|
u8 ctrl00;
|
|
|
|
enum ov5640_format_mux mux;
|
2022-05-13 22:14:15 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static const struct ov5640_pixfmt ov5640_dvp_formats[] = {
|
2022-05-13 22:13:52 +08:00
|
|
|
{
|
2022-05-13 22:14:16 +08:00
|
|
|
/* YUV422, YUYV */
|
|
|
|
.code = MEDIA_BUS_FMT_JPEG_1X8,
|
|
|
|
.colorspace = V4L2_COLORSPACE_JPEG,
|
|
|
|
.bpp = 16,
|
|
|
|
.ctrl00 = 0x30,
|
|
|
|
.mux = OV5640_FMT_MUX_YUV422,
|
2022-05-13 22:13:52 +08:00
|
|
|
}, {
|
2022-05-13 22:14:16 +08:00
|
|
|
/* YUV422, UYVY */
|
|
|
|
.code = MEDIA_BUS_FMT_UYVY8_2X8,
|
|
|
|
.colorspace = V4L2_COLORSPACE_SRGB,
|
|
|
|
.bpp = 16,
|
|
|
|
.ctrl00 = 0x3f,
|
|
|
|
.mux = OV5640_FMT_MUX_YUV422,
|
2022-05-13 22:13:52 +08:00
|
|
|
}, {
|
2022-05-13 22:14:16 +08:00
|
|
|
/* YUV422, YUYV */
|
|
|
|
.code = MEDIA_BUS_FMT_YUYV8_2X8,
|
|
|
|
.colorspace = V4L2_COLORSPACE_SRGB,
|
|
|
|
.bpp = 16,
|
|
|
|
.ctrl00 = 0x30,
|
|
|
|
.mux = OV5640_FMT_MUX_YUV422,
|
2022-05-13 22:13:52 +08:00
|
|
|
}, {
|
2022-05-13 22:14:16 +08:00
|
|
|
/* RGB565 {g[2:0],b[4:0]},{r[4:0],g[5:3]} */
|
|
|
|
.code = MEDIA_BUS_FMT_RGB565_2X8_LE,
|
|
|
|
.colorspace = V4L2_COLORSPACE_SRGB,
|
|
|
|
.bpp = 16,
|
|
|
|
.ctrl00 = 0x6f,
|
|
|
|
.mux = OV5640_FMT_MUX_RGB,
|
2022-05-13 22:13:52 +08:00
|
|
|
}, {
|
2022-05-13 22:14:16 +08:00
|
|
|
/* RGB565 {r[4:0],g[5:3]},{g[2:0],b[4:0]} */
|
|
|
|
.code = MEDIA_BUS_FMT_RGB565_2X8_BE,
|
|
|
|
.colorspace = V4L2_COLORSPACE_SRGB,
|
|
|
|
.bpp = 16,
|
|
|
|
.ctrl00 = 0x61,
|
|
|
|
.mux = OV5640_FMT_MUX_RGB,
|
2022-05-13 22:13:52 +08:00
|
|
|
}, {
|
2022-05-13 22:14:16 +08:00
|
|
|
/* Raw, BGBG... / GRGR... */
|
|
|
|
.code = MEDIA_BUS_FMT_SBGGR8_1X8,
|
|
|
|
.colorspace = V4L2_COLORSPACE_SRGB,
|
|
|
|
.bpp = 8,
|
|
|
|
.ctrl00 = 0x00,
|
|
|
|
.mux = OV5640_FMT_MUX_RAW_DPC,
|
2022-05-13 22:14:15 +08:00
|
|
|
}, {
|
2022-05-13 22:14:16 +08:00
|
|
|
/* Raw bayer, GBGB... / RGRG... */
|
|
|
|
.code = MEDIA_BUS_FMT_SGBRG8_1X8,
|
|
|
|
.colorspace = V4L2_COLORSPACE_SRGB,
|
|
|
|
.bpp = 8,
|
|
|
|
.ctrl00 = 0x01,
|
|
|
|
.mux = OV5640_FMT_MUX_RAW_DPC,
|
2022-05-13 22:14:15 +08:00
|
|
|
}, {
|
2022-05-13 22:14:16 +08:00
|
|
|
/* Raw bayer, GRGR... / BGBG... */
|
|
|
|
.code = MEDIA_BUS_FMT_SGRBG8_1X8,
|
|
|
|
.colorspace = V4L2_COLORSPACE_SRGB,
|
|
|
|
.bpp = 8,
|
|
|
|
.ctrl00 = 0x02,
|
|
|
|
.mux = OV5640_FMT_MUX_RAW_DPC,
|
2022-05-13 22:14:15 +08:00
|
|
|
}, {
|
2022-05-13 22:14:16 +08:00
|
|
|
/* Raw bayer, RGRG... / GBGB... */
|
|
|
|
.code = MEDIA_BUS_FMT_SRGGB8_1X8,
|
|
|
|
.colorspace = V4L2_COLORSPACE_SRGB,
|
|
|
|
.bpp = 8,
|
|
|
|
.ctrl00 = 0x03,
|
|
|
|
.mux = OV5640_FMT_MUX_RAW_DPC,
|
2022-05-13 22:14:15 +08:00
|
|
|
},
|
|
|
|
{ /* sentinel */ }
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct ov5640_pixfmt ov5640_csi2_formats[] = {
|
|
|
|
{
|
2022-05-13 22:14:16 +08:00
|
|
|
/* YUV422, YUYV */
|
|
|
|
.code = MEDIA_BUS_FMT_JPEG_1X8,
|
|
|
|
.colorspace = V4L2_COLORSPACE_JPEG,
|
|
|
|
.bpp = 16,
|
|
|
|
.ctrl00 = 0x30,
|
|
|
|
.mux = OV5640_FMT_MUX_YUV422,
|
2022-05-13 22:13:52 +08:00
|
|
|
}, {
|
2022-05-13 22:14:16 +08:00
|
|
|
/* YUV422, UYVY */
|
|
|
|
.code = MEDIA_BUS_FMT_UYVY8_1X16,
|
|
|
|
.colorspace = V4L2_COLORSPACE_SRGB,
|
|
|
|
.bpp = 16,
|
|
|
|
.ctrl00 = 0x3f,
|
|
|
|
.mux = OV5640_FMT_MUX_YUV422,
|
2022-05-13 22:14:15 +08:00
|
|
|
}, {
|
2022-05-13 22:14:16 +08:00
|
|
|
/* YUV422, YUYV */
|
|
|
|
.code = MEDIA_BUS_FMT_YUYV8_1X16,
|
|
|
|
.colorspace = V4L2_COLORSPACE_SRGB,
|
|
|
|
.bpp = 16,
|
|
|
|
.ctrl00 = 0x30,
|
|
|
|
.mux = OV5640_FMT_MUX_YUV422,
|
2022-05-13 22:14:11 +08:00
|
|
|
}, {
|
2022-05-13 22:14:16 +08:00
|
|
|
/* RGB565 {g[2:0],b[4:0]},{r[4:0],g[5:3]} */
|
|
|
|
.code = MEDIA_BUS_FMT_RGB565_1X16,
|
|
|
|
.colorspace = V4L2_COLORSPACE_SRGB,
|
|
|
|
.bpp = 16,
|
|
|
|
.ctrl00 = 0x6f,
|
|
|
|
.mux = OV5640_FMT_MUX_RGB,
|
2022-05-13 22:14:12 +08:00
|
|
|
}, {
|
2022-05-13 22:14:16 +08:00
|
|
|
/* BGR888: RGB */
|
|
|
|
.code = MEDIA_BUS_FMT_BGR888_1X24,
|
|
|
|
.colorspace = V4L2_COLORSPACE_SRGB,
|
|
|
|
.bpp = 24,
|
|
|
|
.ctrl00 = 0x23,
|
|
|
|
.mux = OV5640_FMT_MUX_RGB,
|
2022-05-13 22:13:52 +08:00
|
|
|
}, {
|
2022-05-13 22:14:16 +08:00
|
|
|
/* Raw, BGBG... / GRGR... */
|
|
|
|
.code = MEDIA_BUS_FMT_SBGGR8_1X8,
|
|
|
|
.colorspace = V4L2_COLORSPACE_SRGB,
|
|
|
|
.bpp = 8,
|
|
|
|
.ctrl00 = 0x00,
|
|
|
|
.mux = OV5640_FMT_MUX_RAW_DPC,
|
2022-05-13 22:13:52 +08:00
|
|
|
}, {
|
2022-05-13 22:14:16 +08:00
|
|
|
/* Raw bayer, GBGB... / RGRG... */
|
|
|
|
.code = MEDIA_BUS_FMT_SGBRG8_1X8,
|
|
|
|
.colorspace = V4L2_COLORSPACE_SRGB,
|
|
|
|
.bpp = 8,
|
|
|
|
.ctrl00 = 0x01,
|
|
|
|
.mux = OV5640_FMT_MUX_RAW_DPC,
|
2022-05-13 22:13:52 +08:00
|
|
|
}, {
|
2022-05-13 22:14:16 +08:00
|
|
|
/* Raw bayer, GRGR... / BGBG... */
|
|
|
|
.code = MEDIA_BUS_FMT_SGRBG8_1X8,
|
|
|
|
.colorspace = V4L2_COLORSPACE_SRGB,
|
|
|
|
.bpp = 8,
|
|
|
|
.ctrl00 = 0x02,
|
|
|
|
.mux = OV5640_FMT_MUX_RAW_DPC,
|
2022-05-13 22:13:52 +08:00
|
|
|
}, {
|
2022-05-13 22:14:16 +08:00
|
|
|
/* Raw bayer, RGRG... / GBGB... */
|
|
|
|
.code = MEDIA_BUS_FMT_SRGGB8_1X8,
|
|
|
|
.colorspace = V4L2_COLORSPACE_SRGB,
|
|
|
|
.bpp = 8,
|
|
|
|
.ctrl00 = 0x03,
|
|
|
|
.mux = OV5640_FMT_MUX_RAW_DPC,
|
2022-05-13 22:13:52 +08:00
|
|
|
},
|
2022-05-13 22:14:15 +08:00
|
|
|
{ /* sentinel */ }
|
2018-01-03 17:57:32 +08:00
|
|
|
};
|
|
|
|
|
2017-06-08 02:33:56 +08:00
|
|
|
/*
|
|
|
|
* FIXME: remove this when a subdev API becomes available
|
|
|
|
* to set the MIPI CSI-2 virtual channel.
|
|
|
|
*/
|
|
|
|
static unsigned int virtual_channel;
|
2018-02-06 21:24:09 +08:00
|
|
|
module_param(virtual_channel, uint, 0444);
|
2017-06-08 02:33:56 +08:00
|
|
|
MODULE_PARM_DESC(virtual_channel,
|
|
|
|
"MIPI CSI-2 virtual channel (0..3), default 0");
|
|
|
|
|
|
|
|
static const int ov5640_framerates[] = {
|
|
|
|
[OV5640_15_FPS] = 15,
|
|
|
|
[OV5640_30_FPS] = 30,
|
2018-12-03 16:44:26 +08:00
|
|
|
[OV5640_60_FPS] = 60,
|
2017-06-08 02:33:56 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
/* regulator supplies */
|
|
|
|
static const char * const ov5640_supply_name[] = {
|
2018-02-01 16:44:06 +08:00
|
|
|
"DOVDD", /* Digital I/O (1.8V) supply */
|
2017-06-08 02:33:56 +08:00
|
|
|
"AVDD", /* Analog (2.8V) supply */
|
2019-06-28 19:00:35 +08:00
|
|
|
"DVDD", /* Digital Core (1.5V) supply */
|
2017-06-08 02:33:56 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
#define OV5640_NUM_SUPPLIES ARRAY_SIZE(ov5640_supply_name)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Image size under 1280 * 960 are SUBSAMPLING
|
|
|
|
* Image size upper 1280 * 960 are SCALING
|
|
|
|
*/
|
|
|
|
enum ov5640_downsize_mode {
|
|
|
|
SUBSAMPLING,
|
|
|
|
SCALING,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct reg_value {
|
|
|
|
u16 reg_addr;
|
|
|
|
u8 val;
|
|
|
|
u8 mask;
|
|
|
|
u32 delay_ms;
|
|
|
|
};
|
|
|
|
|
2022-05-13 22:13:58 +08:00
|
|
|
struct ov5640_timings {
|
2022-05-13 22:13:56 +08:00
|
|
|
/* Analog crop rectangle. */
|
|
|
|
struct v4l2_rect analog_crop;
|
|
|
|
/* Visibile crop: from analog crop top-left corner. */
|
|
|
|
struct v4l2_rect crop;
|
2022-05-13 22:13:58 +08:00
|
|
|
/* Total pixels per line: width + fixed hblank. */
|
2018-04-16 20:36:55 +08:00
|
|
|
u32 htot;
|
2022-05-13 22:13:58 +08:00
|
|
|
/* Default vertical blanking: frame height = height + vblank. */
|
2022-05-13 22:13:56 +08:00
|
|
|
u32 vblank_def;
|
2022-05-13 22:13:58 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
struct ov5640_mode_info {
|
|
|
|
enum ov5640_mode_id id;
|
|
|
|
enum ov5640_downsize_mode dn_mode;
|
|
|
|
enum ov5640_pixel_rate_id pixel_rate;
|
|
|
|
|
|
|
|
unsigned int width;
|
|
|
|
unsigned int height;
|
|
|
|
|
|
|
|
struct ov5640_timings dvp_timings;
|
|
|
|
struct ov5640_timings csi2_timings;
|
|
|
|
|
2017-06-08 02:33:56 +08:00
|
|
|
const struct reg_value *reg_data;
|
|
|
|
u32 reg_data_size;
|
2022-05-13 22:13:58 +08:00
|
|
|
|
|
|
|
/* Used by s_frame_interval only. */
|
2019-10-26 03:07:23 +08:00
|
|
|
u32 max_fps;
|
2022-05-13 22:14:05 +08:00
|
|
|
u32 def_fps;
|
2017-06-08 02:33:56 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
struct ov5640_ctrls {
|
|
|
|
struct v4l2_ctrl_handler handler;
|
2019-10-09 20:35:08 +08:00
|
|
|
struct v4l2_ctrl *pixel_rate;
|
2022-05-13 22:13:53 +08:00
|
|
|
struct v4l2_ctrl *link_freq;
|
2022-05-13 22:14:03 +08:00
|
|
|
struct v4l2_ctrl *hblank;
|
2022-05-13 22:14:04 +08:00
|
|
|
struct v4l2_ctrl *vblank;
|
2017-06-08 02:33:56 +08:00
|
|
|
struct {
|
|
|
|
struct v4l2_ctrl *auto_exp;
|
|
|
|
struct v4l2_ctrl *exposure;
|
|
|
|
};
|
|
|
|
struct {
|
|
|
|
struct v4l2_ctrl *auto_wb;
|
|
|
|
struct v4l2_ctrl *blue_balance;
|
|
|
|
struct v4l2_ctrl *red_balance;
|
|
|
|
};
|
|
|
|
struct {
|
|
|
|
struct v4l2_ctrl *auto_gain;
|
|
|
|
struct v4l2_ctrl *gain;
|
|
|
|
};
|
|
|
|
struct v4l2_ctrl *brightness;
|
2018-04-16 20:36:51 +08:00
|
|
|
struct v4l2_ctrl *light_freq;
|
2017-06-08 02:33:56 +08:00
|
|
|
struct v4l2_ctrl *saturation;
|
|
|
|
struct v4l2_ctrl *contrast;
|
|
|
|
struct v4l2_ctrl *hue;
|
|
|
|
struct v4l2_ctrl *test_pattern;
|
2018-06-18 18:29:17 +08:00
|
|
|
struct v4l2_ctrl *hflip;
|
|
|
|
struct v4l2_ctrl *vflip;
|
2017-06-08 02:33:56 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
struct ov5640_dev {
|
|
|
|
struct i2c_client *i2c_client;
|
|
|
|
struct v4l2_subdev sd;
|
|
|
|
struct media_pad pad;
|
|
|
|
struct v4l2_fwnode_endpoint ep; /* the parsed DT endpoint info */
|
|
|
|
struct clk *xclk; /* system clock to OV5640 */
|
|
|
|
u32 xclk_freq;
|
|
|
|
|
|
|
|
struct regulator_bulk_data supplies[OV5640_NUM_SUPPLIES];
|
|
|
|
struct gpio_desc *reset_gpio;
|
|
|
|
struct gpio_desc *pwdn_gpio;
|
2018-06-18 18:29:19 +08:00
|
|
|
bool upside_down;
|
2017-06-08 02:33:56 +08:00
|
|
|
|
|
|
|
/* lock to protect all members below */
|
|
|
|
struct mutex lock;
|
|
|
|
|
|
|
|
struct v4l2_mbus_framefmt fmt;
|
2018-08-16 17:46:53 +08:00
|
|
|
bool pending_fmt_change;
|
2017-06-08 02:33:56 +08:00
|
|
|
|
|
|
|
const struct ov5640_mode_info *current_mode;
|
2018-09-11 21:48:21 +08:00
|
|
|
const struct ov5640_mode_info *last_mode;
|
2017-06-08 02:33:56 +08:00
|
|
|
enum ov5640_frame_rate current_fr;
|
|
|
|
struct v4l2_fract frame_interval;
|
2022-05-13 22:13:54 +08:00
|
|
|
s64 current_link_freq;
|
2017-06-08 02:33:56 +08:00
|
|
|
|
|
|
|
struct ov5640_ctrls ctrls;
|
|
|
|
|
|
|
|
u32 prev_sysclk, prev_hts;
|
|
|
|
u32 ae_low, ae_high, ae_target;
|
|
|
|
|
|
|
|
bool pending_mode_change;
|
|
|
|
bool streaming;
|
|
|
|
};
|
|
|
|
|
|
|
|
static inline struct ov5640_dev *to_ov5640_dev(struct v4l2_subdev *sd)
|
|
|
|
{
|
|
|
|
return container_of(sd, struct ov5640_dev, sd);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl)
|
|
|
|
{
|
|
|
|
return &container_of(ctrl->handler, struct ov5640_dev,
|
|
|
|
ctrls.handler)->sd;
|
|
|
|
}
|
|
|
|
|
2022-05-13 22:13:51 +08:00
|
|
|
static inline bool ov5640_is_csi2(const struct ov5640_dev *sensor)
|
|
|
|
{
|
|
|
|
return sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY;
|
|
|
|
}
|
|
|
|
|
2022-05-13 22:14:15 +08:00
|
|
|
static inline const struct ov5640_pixfmt *
|
|
|
|
ov5640_formats(struct ov5640_dev *sensor)
|
|
|
|
{
|
|
|
|
return ov5640_is_csi2(sensor) ? ov5640_csi2_formats
|
|
|
|
: ov5640_dvp_formats;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct ov5640_pixfmt *
|
|
|
|
ov5640_code_to_pixfmt(struct ov5640_dev *sensor, u32 code)
|
|
|
|
{
|
|
|
|
const struct ov5640_pixfmt *formats = ov5640_formats(sensor);
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
for (i = 0; formats[i].code; ++i) {
|
|
|
|
if (formats[i].code == code)
|
|
|
|
return &formats[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
return &formats[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
static u32 ov5640_code_to_bpp(struct ov5640_dev *sensor, u32 code)
|
|
|
|
{
|
|
|
|
const struct ov5640_pixfmt *format = ov5640_code_to_pixfmt(sensor,
|
|
|
|
code);
|
|
|
|
|
|
|
|
return format->bpp;
|
|
|
|
}
|
|
|
|
|
2017-06-08 02:33:56 +08:00
|
|
|
/*
|
|
|
|
* FIXME: all of these register tables are likely filled with
|
|
|
|
* entries that set the register to their power-on default values,
|
|
|
|
* and which are otherwise not touched by this driver. Those entries
|
|
|
|
* should be identified and removed to speed register load time
|
|
|
|
* over i2c.
|
|
|
|
*/
|
2018-08-16 17:46:53 +08:00
|
|
|
/* YUV422 UYVY VGA@30fps */
|
2022-05-13 22:14:08 +08:00
|
|
|
|
2022-12-12 12:05:26 +08:00
|
|
|
static const struct v4l2_mbus_framefmt ov5640_csi2_default_fmt = {
|
|
|
|
.code = MEDIA_BUS_FMT_UYVY8_1X16,
|
|
|
|
.width = 640,
|
|
|
|
.height = 480,
|
|
|
|
.colorspace = V4L2_COLORSPACE_SRGB,
|
|
|
|
.ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(V4L2_COLORSPACE_SRGB),
|
|
|
|
.quantization = V4L2_QUANTIZATION_FULL_RANGE,
|
|
|
|
.xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(V4L2_COLORSPACE_SRGB),
|
|
|
|
.field = V4L2_FIELD_NONE,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct v4l2_mbus_framefmt ov5640_dvp_default_fmt = {
|
2022-05-13 22:14:08 +08:00
|
|
|
.code = MEDIA_BUS_FMT_UYVY8_2X8,
|
|
|
|
.width = 640,
|
|
|
|
.height = 480,
|
|
|
|
.colorspace = V4L2_COLORSPACE_SRGB,
|
|
|
|
.ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(V4L2_COLORSPACE_SRGB),
|
|
|
|
.quantization = V4L2_QUANTIZATION_FULL_RANGE,
|
|
|
|
.xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(V4L2_COLORSPACE_SRGB),
|
|
|
|
.field = V4L2_FIELD_NONE,
|
|
|
|
};
|
|
|
|
|
2022-05-13 22:14:02 +08:00
|
|
|
static const struct reg_value ov5640_init_setting[] = {
|
2023-01-03 20:27:35 +08:00
|
|
|
{0x3103, 0x11, 0, 0},
|
2020-09-05 04:18:32 +08:00
|
|
|
{0x3103, 0x03, 0, 0}, {0x3630, 0x36, 0, 0},
|
2017-06-08 02:33:56 +08:00
|
|
|
{0x3631, 0x0e, 0, 0}, {0x3632, 0xe2, 0, 0}, {0x3633, 0x12, 0, 0},
|
|
|
|
{0x3621, 0xe0, 0, 0}, {0x3704, 0xa0, 0, 0}, {0x3703, 0x5a, 0, 0},
|
|
|
|
{0x3715, 0x78, 0, 0}, {0x3717, 0x01, 0, 0}, {0x370b, 0x60, 0, 0},
|
|
|
|
{0x3705, 0x1a, 0, 0}, {0x3905, 0x02, 0, 0}, {0x3906, 0x10, 0, 0},
|
|
|
|
{0x3901, 0x0a, 0, 0}, {0x3731, 0x12, 0, 0}, {0x3600, 0x08, 0, 0},
|
|
|
|
{0x3601, 0x33, 0, 0}, {0x302d, 0x60, 0, 0}, {0x3620, 0x52, 0, 0},
|
|
|
|
{0x371b, 0x20, 0, 0}, {0x471c, 0x50, 0, 0}, {0x3a13, 0x43, 0, 0},
|
|
|
|
{0x3a18, 0x00, 0, 0}, {0x3a19, 0xf8, 0, 0}, {0x3635, 0x13, 0, 0},
|
|
|
|
{0x3636, 0x03, 0, 0}, {0x3634, 0x40, 0, 0}, {0x3622, 0x01, 0, 0},
|
|
|
|
{0x3c01, 0xa4, 0, 0}, {0x3c04, 0x28, 0, 0}, {0x3c05, 0x98, 0, 0},
|
|
|
|
{0x3c06, 0x00, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c08, 0x00, 0, 0},
|
|
|
|
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
|
|
|
|
{0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
|
2022-05-13 22:13:56 +08:00
|
|
|
{0x3815, 0x31, 0, 0},
|
2017-06-08 02:33:56 +08:00
|
|
|
{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
|
|
|
|
{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
|
|
|
|
{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
|
|
|
|
{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
|
|
|
|
{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
|
|
|
|
{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x3000, 0x00, 0, 0},
|
|
|
|
{0x3002, 0x1c, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3006, 0xc3, 0, 0},
|
media: ov5640: Re-work MIPI startup sequence
Rework the MIPI interface startup sequence with the following changes:
- Remove MIPI bus initialization from the initial settings blob
- At set_power(1) time power up MIPI Tx/Rx and set data and clock lanes in
LP11 during 'sleep' and 'idle' with MIPI clock in non-continuous mode.
- At s_stream time enable/disable the MIPI interface output.
- Restore default settings at set_power(0) time.
Before this commit the sensor MIPI interface was initialized with settings
that require a start/stop sequence at power-up time in order to force lanes
into LP11 state, as they were initialized in LP00 when in 'sleep mode',
which is assumed to be the sensor manual definition for the D-PHY defined
stop mode.
The stream start/stop was performed by enabling disabling clock gating,
and had the side effect to change the lanes sleep mode configuration when
stream was stopped.
Clock gating/ungating:
- ret = ov5640_mod_reg(sensor, OV5640_REG_MIPI_CTRL00, BIT(5),
- on ? 0 : BIT(5));
- if (ret)
Set lanes in LP11 when in 'sleep mode':
- ret = ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT00,
- on ? 0x00 : 0x70);
This commit fixes an issue reported by Jagan Teki on i.MX6 platforms that
prevents the host interface from powering up correctly:
https://lkml.org/lkml/2018/6/1/38
It also improves MIPI capture operations stability on my testing platform
where MIPI capture often failed and returned all-purple frames.
Fixes: f22996db44e2 ("media: ov5640: add support of DVP parallel interface")
Tested-by: Steve Longerbeam <slongerbeam@gmail.com> (i.MX6q SabreSD, CSI-2)
Tested-by: Loic Poulain <loic.poulain@linaro.org> (Dragonboard-410c, CSI-2)
Reported-by: Jagan Teki <jagan@amarulasolutions.com>
Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
2018-07-06 17:51:52 +08:00
|
|
|
{0x302e, 0x08, 0, 0}, {0x4300, 0x3f, 0, 0},
|
2023-06-12 10:43:40 +08:00
|
|
|
{0x501f, 0x00, 0, 0}, {0x440e, 0x00, 0, 0}, {0x4837, 0x0a, 0, 0},
|
2017-06-08 02:33:56 +08:00
|
|
|
{0x5000, 0xa7, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x5180, 0xff, 0, 0},
|
|
|
|
{0x5181, 0xf2, 0, 0}, {0x5182, 0x00, 0, 0}, {0x5183, 0x14, 0, 0},
|
|
|
|
{0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0}, {0x5186, 0x09, 0, 0},
|
|
|
|
{0x5187, 0x09, 0, 0}, {0x5188, 0x09, 0, 0}, {0x5189, 0x88, 0, 0},
|
|
|
|
{0x518a, 0x54, 0, 0}, {0x518b, 0xee, 0, 0}, {0x518c, 0xb2, 0, 0},
|
|
|
|
{0x518d, 0x50, 0, 0}, {0x518e, 0x34, 0, 0}, {0x518f, 0x6b, 0, 0},
|
|
|
|
{0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0},
|
|
|
|
{0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0},
|
|
|
|
{0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0},
|
|
|
|
{0x5199, 0x6c, 0, 0}, {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0},
|
|
|
|
{0x519c, 0x09, 0, 0}, {0x519d, 0x2b, 0, 0}, {0x519e, 0x38, 0, 0},
|
|
|
|
{0x5381, 0x1e, 0, 0}, {0x5382, 0x5b, 0, 0}, {0x5383, 0x08, 0, 0},
|
|
|
|
{0x5384, 0x0a, 0, 0}, {0x5385, 0x7e, 0, 0}, {0x5386, 0x88, 0, 0},
|
|
|
|
{0x5387, 0x7c, 0, 0}, {0x5388, 0x6c, 0, 0}, {0x5389, 0x10, 0, 0},
|
|
|
|
{0x538a, 0x01, 0, 0}, {0x538b, 0x98, 0, 0}, {0x5300, 0x08, 0, 0},
|
|
|
|
{0x5301, 0x30, 0, 0}, {0x5302, 0x10, 0, 0}, {0x5303, 0x00, 0, 0},
|
|
|
|
{0x5304, 0x08, 0, 0}, {0x5305, 0x30, 0, 0}, {0x5306, 0x08, 0, 0},
|
|
|
|
{0x5307, 0x16, 0, 0}, {0x5309, 0x08, 0, 0}, {0x530a, 0x30, 0, 0},
|
|
|
|
{0x530b, 0x04, 0, 0}, {0x530c, 0x06, 0, 0}, {0x5480, 0x01, 0, 0},
|
|
|
|
{0x5481, 0x08, 0, 0}, {0x5482, 0x14, 0, 0}, {0x5483, 0x28, 0, 0},
|
|
|
|
{0x5484, 0x51, 0, 0}, {0x5485, 0x65, 0, 0}, {0x5486, 0x71, 0, 0},
|
|
|
|
{0x5487, 0x7d, 0, 0}, {0x5488, 0x87, 0, 0}, {0x5489, 0x91, 0, 0},
|
|
|
|
{0x548a, 0x9a, 0, 0}, {0x548b, 0xaa, 0, 0}, {0x548c, 0xb8, 0, 0},
|
|
|
|
{0x548d, 0xcd, 0, 0}, {0x548e, 0xdd, 0, 0}, {0x548f, 0xea, 0, 0},
|
|
|
|
{0x5490, 0x1d, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5583, 0x40, 0, 0},
|
|
|
|
{0x5584, 0x10, 0, 0}, {0x5589, 0x10, 0, 0}, {0x558a, 0x00, 0, 0},
|
|
|
|
{0x558b, 0xf8, 0, 0}, {0x5800, 0x23, 0, 0}, {0x5801, 0x14, 0, 0},
|
|
|
|
{0x5802, 0x0f, 0, 0}, {0x5803, 0x0f, 0, 0}, {0x5804, 0x12, 0, 0},
|
|
|
|
{0x5805, 0x26, 0, 0}, {0x5806, 0x0c, 0, 0}, {0x5807, 0x08, 0, 0},
|
|
|
|
{0x5808, 0x05, 0, 0}, {0x5809, 0x05, 0, 0}, {0x580a, 0x08, 0, 0},
|
|
|
|
{0x580b, 0x0d, 0, 0}, {0x580c, 0x08, 0, 0}, {0x580d, 0x03, 0, 0},
|
|
|
|
{0x580e, 0x00, 0, 0}, {0x580f, 0x00, 0, 0}, {0x5810, 0x03, 0, 0},
|
|
|
|
{0x5811, 0x09, 0, 0}, {0x5812, 0x07, 0, 0}, {0x5813, 0x03, 0, 0},
|
|
|
|
{0x5814, 0x00, 0, 0}, {0x5815, 0x01, 0, 0}, {0x5816, 0x03, 0, 0},
|
|
|
|
{0x5817, 0x08, 0, 0}, {0x5818, 0x0d, 0, 0}, {0x5819, 0x08, 0, 0},
|
|
|
|
{0x581a, 0x05, 0, 0}, {0x581b, 0x06, 0, 0}, {0x581c, 0x08, 0, 0},
|
|
|
|
{0x581d, 0x0e, 0, 0}, {0x581e, 0x29, 0, 0}, {0x581f, 0x17, 0, 0},
|
|
|
|
{0x5820, 0x11, 0, 0}, {0x5821, 0x11, 0, 0}, {0x5822, 0x15, 0, 0},
|
|
|
|
{0x5823, 0x28, 0, 0}, {0x5824, 0x46, 0, 0}, {0x5825, 0x26, 0, 0},
|
|
|
|
{0x5826, 0x08, 0, 0}, {0x5827, 0x26, 0, 0}, {0x5828, 0x64, 0, 0},
|
|
|
|
{0x5829, 0x26, 0, 0}, {0x582a, 0x24, 0, 0}, {0x582b, 0x22, 0, 0},
|
|
|
|
{0x582c, 0x24, 0, 0}, {0x582d, 0x24, 0, 0}, {0x582e, 0x06, 0, 0},
|
|
|
|
{0x582f, 0x22, 0, 0}, {0x5830, 0x40, 0, 0}, {0x5831, 0x42, 0, 0},
|
|
|
|
{0x5832, 0x24, 0, 0}, {0x5833, 0x26, 0, 0}, {0x5834, 0x24, 0, 0},
|
|
|
|
{0x5835, 0x22, 0, 0}, {0x5836, 0x22, 0, 0}, {0x5837, 0x26, 0, 0},
|
|
|
|
{0x5838, 0x44, 0, 0}, {0x5839, 0x24, 0, 0}, {0x583a, 0x26, 0, 0},
|
|
|
|
{0x583b, 0x28, 0, 0}, {0x583c, 0x42, 0, 0}, {0x583d, 0xce, 0, 0},
|
|
|
|
{0x5025, 0x00, 0, 0}, {0x3a0f, 0x30, 0, 0}, {0x3a10, 0x28, 0, 0},
|
|
|
|
{0x3a1b, 0x30, 0, 0}, {0x3a1e, 0x26, 0, 0}, {0x3a11, 0x60, 0, 0},
|
|
|
|
{0x3a1f, 0x14, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3c00, 0x04, 0, 300},
|
|
|
|
};
|
|
|
|
|
2022-05-13 22:14:01 +08:00
|
|
|
static const struct reg_value ov5640_setting_low_res[] = {
|
2018-12-03 16:44:18 +08:00
|
|
|
{0x3c07, 0x08, 0, 0},
|
2017-06-08 02:33:56 +08:00
|
|
|
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
|
2018-06-18 18:29:17 +08:00
|
|
|
{0x3814, 0x31, 0, 0},
|
2022-05-13 22:13:56 +08:00
|
|
|
{0x3815, 0x31, 0, 0},
|
2017-06-08 02:33:56 +08:00
|
|
|
{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
|
|
|
|
{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
|
|
|
|
{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
|
|
|
|
{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
|
|
|
|
{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
|
2019-01-18 16:52:06 +08:00
|
|
|
{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
|
2023-06-12 10:43:40 +08:00
|
|
|
{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
|
|
|
|
{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
|
2017-06-08 02:33:56 +08:00
|
|
|
};
|
|
|
|
|
2018-12-03 16:44:23 +08:00
|
|
|
static const struct reg_value ov5640_setting_720P_1280_720[] = {
|
2018-12-03 16:44:18 +08:00
|
|
|
{0x3c07, 0x07, 0, 0},
|
2017-06-08 02:33:56 +08:00
|
|
|
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
|
2018-06-18 18:29:17 +08:00
|
|
|
{0x3814, 0x31, 0, 0},
|
2022-05-13 22:13:56 +08:00
|
|
|
{0x3815, 0x31, 0, 0},
|
2017-06-08 02:33:56 +08:00
|
|
|
{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
|
|
|
|
{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0},
|
|
|
|
{0x3a03, 0xe4, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0xbc, 0, 0},
|
|
|
|
{0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x72, 0, 0}, {0x3a0e, 0x01, 0, 0},
|
|
|
|
{0x3a0d, 0x02, 0, 0}, {0x3a14, 0x02, 0, 0}, {0x3a15, 0xe4, 0, 0},
|
2019-01-18 16:52:06 +08:00
|
|
|
{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
|
2017-06-08 02:33:56 +08:00
|
|
|
{0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0},
|
|
|
|
{0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0},
|
|
|
|
};
|
|
|
|
|
2018-12-03 16:44:23 +08:00
|
|
|
static const struct reg_value ov5640_setting_1080P_1920_1080[] = {
|
2018-12-03 16:44:18 +08:00
|
|
|
{0x3c07, 0x08, 0, 0},
|
2017-06-08 02:33:56 +08:00
|
|
|
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
|
2018-06-18 18:29:17 +08:00
|
|
|
{0x3814, 0x11, 0, 0},
|
2022-05-13 22:13:56 +08:00
|
|
|
{0x3815, 0x11, 0, 0},
|
2017-06-08 02:33:56 +08:00
|
|
|
{0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0},
|
|
|
|
{0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0},
|
|
|
|
{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
|
|
|
|
{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
|
|
|
|
{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
|
2019-01-18 16:52:06 +08:00
|
|
|
{0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0},
|
2017-06-08 02:33:56 +08:00
|
|
|
{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
|
2018-12-03 16:44:18 +08:00
|
|
|
{0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0},
|
|
|
|
{0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0},
|
2017-06-08 02:33:56 +08:00
|
|
|
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
|
2018-04-16 20:36:55 +08:00
|
|
|
{0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0},
|
2017-06-08 02:33:56 +08:00
|
|
|
{0x3a02, 0x04, 0, 0}, {0x3a03, 0x60, 0, 0}, {0x3a08, 0x01, 0, 0},
|
|
|
|
{0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0},
|
|
|
|
{0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0},
|
2019-01-18 16:52:06 +08:00
|
|
|
{0x3a15, 0x60, 0, 0}, {0x4407, 0x04, 0, 0},
|
2017-06-08 02:33:56 +08:00
|
|
|
{0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0},
|
2019-10-09 20:35:09 +08:00
|
|
|
{0x4005, 0x1a, 0, 0},
|
2017-06-08 02:33:56 +08:00
|
|
|
};
|
|
|
|
|
2018-12-03 16:44:23 +08:00
|
|
|
static const struct reg_value ov5640_setting_QSXGA_2592_1944[] = {
|
2018-12-03 16:44:18 +08:00
|
|
|
{0x3c07, 0x08, 0, 0},
|
2017-06-08 02:33:56 +08:00
|
|
|
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
|
2018-06-18 18:29:17 +08:00
|
|
|
{0x3814, 0x11, 0, 0},
|
2022-05-13 22:13:56 +08:00
|
|
|
{0x3815, 0x11, 0, 0},
|
2017-06-08 02:33:56 +08:00
|
|
|
{0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0},
|
|
|
|
{0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0},
|
|
|
|
{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
|
|
|
|
{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
|
|
|
|
{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
|
2019-01-18 16:52:06 +08:00
|
|
|
{0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0},
|
2017-06-08 02:33:56 +08:00
|
|
|
{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
|
|
|
|
{0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 70},
|
|
|
|
};
|
|
|
|
|
2022-05-13 22:13:58 +08:00
|
|
|
static const struct ov5640_mode_info ov5640_mode_data[OV5640_NUM_MODES] = {
|
2022-05-13 22:13:50 +08:00
|
|
|
{
|
|
|
|
/* 160x120 */
|
2022-05-13 22:13:56 +08:00
|
|
|
.id = OV5640_MODE_QQVGA_160_120,
|
|
|
|
.dn_mode = SUBSAMPLING,
|
|
|
|
.pixel_rate = OV5640_PIXEL_RATE_48M,
|
2022-05-13 22:13:58 +08:00
|
|
|
.width = 160,
|
|
|
|
.height = 120,
|
|
|
|
.dvp_timings = {
|
|
|
|
.analog_crop = {
|
|
|
|
.left = 0,
|
|
|
|
.top = 4,
|
|
|
|
.width = 2624,
|
|
|
|
.height = 1944,
|
|
|
|
},
|
|
|
|
.crop = {
|
|
|
|
.left = 16,
|
|
|
|
.top = 6,
|
|
|
|
.width = 160,
|
|
|
|
.height = 120,
|
|
|
|
},
|
|
|
|
.htot = 1896,
|
|
|
|
.vblank_def = 864,
|
2022-05-13 22:13:56 +08:00
|
|
|
},
|
2022-05-13 22:13:58 +08:00
|
|
|
.csi2_timings = {
|
|
|
|
/* Feed the full valid pixel array to the ISP. */
|
|
|
|
.analog_crop = {
|
|
|
|
.left = OV5640_PIXEL_ARRAY_LEFT,
|
|
|
|
.top = OV5640_PIXEL_ARRAY_TOP,
|
|
|
|
.width = OV5640_PIXEL_ARRAY_WIDTH,
|
|
|
|
.height = OV5640_PIXEL_ARRAY_HEIGHT,
|
|
|
|
},
|
|
|
|
/* Maintain a minimum processing margin. */
|
|
|
|
.crop = {
|
|
|
|
.left = 2,
|
|
|
|
.top = 4,
|
|
|
|
.width = 160,
|
|
|
|
.height = 120,
|
|
|
|
},
|
2022-05-13 22:14:07 +08:00
|
|
|
.htot = 1600,
|
|
|
|
.vblank_def = 878,
|
2022-05-13 22:13:56 +08:00
|
|
|
},
|
2022-05-13 22:14:01 +08:00
|
|
|
.reg_data = ov5640_setting_low_res,
|
|
|
|
.reg_data_size = ARRAY_SIZE(ov5640_setting_low_res),
|
2022-05-13 22:14:05 +08:00
|
|
|
.max_fps = OV5640_30_FPS,
|
|
|
|
.def_fps = OV5640_30_FPS
|
2022-05-13 22:13:50 +08:00
|
|
|
}, {
|
|
|
|
/* 176x144 */
|
2022-05-13 22:13:56 +08:00
|
|
|
.id = OV5640_MODE_QCIF_176_144,
|
|
|
|
.dn_mode = SUBSAMPLING,
|
|
|
|
.pixel_rate = OV5640_PIXEL_RATE_48M,
|
2022-05-13 22:13:58 +08:00
|
|
|
.width = 176,
|
|
|
|
.height = 144,
|
|
|
|
.dvp_timings = {
|
|
|
|
.analog_crop = {
|
|
|
|
.left = 0,
|
|
|
|
.top = 4,
|
|
|
|
.width = 2624,
|
|
|
|
.height = 1944,
|
|
|
|
},
|
|
|
|
.crop = {
|
|
|
|
.left = 16,
|
|
|
|
.top = 6,
|
|
|
|
.width = 176,
|
|
|
|
.height = 144,
|
|
|
|
},
|
|
|
|
.htot = 1896,
|
|
|
|
.vblank_def = 840,
|
2022-05-13 22:13:56 +08:00
|
|
|
},
|
2022-05-13 22:13:58 +08:00
|
|
|
.csi2_timings = {
|
|
|
|
/* Feed the full valid pixel array to the ISP. */
|
|
|
|
.analog_crop = {
|
|
|
|
.left = OV5640_PIXEL_ARRAY_LEFT,
|
|
|
|
.top = OV5640_PIXEL_ARRAY_TOP,
|
|
|
|
.width = OV5640_PIXEL_ARRAY_WIDTH,
|
|
|
|
.height = OV5640_PIXEL_ARRAY_HEIGHT,
|
|
|
|
},
|
|
|
|
/* Maintain a minimum processing margin. */
|
|
|
|
.crop = {
|
|
|
|
.left = 2,
|
|
|
|
.top = 4,
|
|
|
|
.width = 176,
|
|
|
|
.height = 144,
|
|
|
|
},
|
2022-05-13 22:14:07 +08:00
|
|
|
.htot = 1600,
|
|
|
|
.vblank_def = 854,
|
2022-05-13 22:13:56 +08:00
|
|
|
},
|
2022-05-13 22:14:01 +08:00
|
|
|
.reg_data = ov5640_setting_low_res,
|
|
|
|
.reg_data_size = ARRAY_SIZE(ov5640_setting_low_res),
|
2022-05-13 22:14:05 +08:00
|
|
|
.max_fps = OV5640_30_FPS,
|
|
|
|
.def_fps = OV5640_30_FPS
|
2022-05-13 22:13:50 +08:00
|
|
|
}, {
|
|
|
|
/* 320x240 */
|
2022-05-13 22:13:56 +08:00
|
|
|
.id = OV5640_MODE_QVGA_320_240,
|
|
|
|
.dn_mode = SUBSAMPLING,
|
2022-05-13 22:13:58 +08:00
|
|
|
.width = 320,
|
|
|
|
.height = 240,
|
2022-05-13 22:13:56 +08:00
|
|
|
.pixel_rate = OV5640_PIXEL_RATE_48M,
|
2022-05-13 22:13:58 +08:00
|
|
|
.dvp_timings = {
|
|
|
|
.analog_crop = {
|
|
|
|
.left = 0,
|
|
|
|
.top = 4,
|
|
|
|
.width = 2624,
|
|
|
|
.height = 1944,
|
|
|
|
},
|
|
|
|
.crop = {
|
|
|
|
.left = 16,
|
|
|
|
.top = 6,
|
|
|
|
.width = 320,
|
|
|
|
.height = 240,
|
|
|
|
},
|
|
|
|
.htot = 1896,
|
|
|
|
.vblank_def = 744,
|
2022-05-13 22:13:56 +08:00
|
|
|
},
|
2022-05-13 22:13:58 +08:00
|
|
|
.csi2_timings = {
|
|
|
|
/* Feed the full valid pixel array to the ISP. */
|
|
|
|
.analog_crop = {
|
|
|
|
.left = OV5640_PIXEL_ARRAY_LEFT,
|
|
|
|
.top = OV5640_PIXEL_ARRAY_TOP,
|
|
|
|
.width = OV5640_PIXEL_ARRAY_WIDTH,
|
|
|
|
.height = OV5640_PIXEL_ARRAY_HEIGHT,
|
|
|
|
},
|
|
|
|
/* Maintain a minimum processing margin. */
|
|
|
|
.crop = {
|
|
|
|
.left = 2,
|
|
|
|
.top = 4,
|
|
|
|
.width = 320,
|
|
|
|
.height = 240,
|
|
|
|
},
|
2022-05-13 22:14:07 +08:00
|
|
|
.htot = 1600,
|
|
|
|
.vblank_def = 760,
|
2022-05-13 22:13:56 +08:00
|
|
|
},
|
2022-05-13 22:14:01 +08:00
|
|
|
.reg_data = ov5640_setting_low_res,
|
|
|
|
.reg_data_size = ARRAY_SIZE(ov5640_setting_low_res),
|
2022-05-13 22:14:05 +08:00
|
|
|
.max_fps = OV5640_30_FPS,
|
|
|
|
.def_fps = OV5640_30_FPS
|
2022-05-13 22:13:50 +08:00
|
|
|
}, {
|
|
|
|
/* 640x480 */
|
2022-05-13 22:13:56 +08:00
|
|
|
.id = OV5640_MODE_VGA_640_480,
|
|
|
|
.dn_mode = SUBSAMPLING,
|
|
|
|
.pixel_rate = OV5640_PIXEL_RATE_48M,
|
2022-05-13 22:13:58 +08:00
|
|
|
.width = 640,
|
|
|
|
.height = 480,
|
|
|
|
.dvp_timings = {
|
|
|
|
.analog_crop = {
|
|
|
|
.left = 0,
|
|
|
|
.top = 4,
|
|
|
|
.width = 2624,
|
|
|
|
.height = 1944,
|
|
|
|
},
|
|
|
|
.crop = {
|
|
|
|
.left = 16,
|
|
|
|
.top = 6,
|
|
|
|
.width = 640,
|
|
|
|
.height = 480,
|
|
|
|
},
|
|
|
|
.htot = 1896,
|
|
|
|
.vblank_def = 600,
|
2022-05-13 22:13:56 +08:00
|
|
|
},
|
2022-05-13 22:13:58 +08:00
|
|
|
.csi2_timings = {
|
|
|
|
/* Feed the full valid pixel array to the ISP. */
|
|
|
|
.analog_crop = {
|
|
|
|
.left = OV5640_PIXEL_ARRAY_LEFT,
|
|
|
|
.top = OV5640_PIXEL_ARRAY_TOP,
|
|
|
|
.width = OV5640_PIXEL_ARRAY_WIDTH,
|
|
|
|
.height = OV5640_PIXEL_ARRAY_HEIGHT,
|
|
|
|
},
|
|
|
|
/* Maintain a minimum processing margin. */
|
|
|
|
.crop = {
|
|
|
|
.left = 2,
|
|
|
|
.top = 4,
|
|
|
|
.width = 640,
|
|
|
|
.height = 480,
|
|
|
|
},
|
2022-05-13 22:14:07 +08:00
|
|
|
.htot = 1600,
|
|
|
|
.vblank_def = 520,
|
2022-05-13 22:13:56 +08:00
|
|
|
},
|
2022-05-13 22:14:01 +08:00
|
|
|
.reg_data = ov5640_setting_low_res,
|
|
|
|
.reg_data_size = ARRAY_SIZE(ov5640_setting_low_res),
|
2022-05-13 22:14:05 +08:00
|
|
|
.max_fps = OV5640_60_FPS,
|
|
|
|
.def_fps = OV5640_30_FPS
|
2022-05-13 22:13:50 +08:00
|
|
|
}, {
|
|
|
|
/* 720x480 */
|
2022-05-13 22:13:56 +08:00
|
|
|
.id = OV5640_MODE_NTSC_720_480,
|
|
|
|
.dn_mode = SUBSAMPLING,
|
2022-05-13 22:13:58 +08:00
|
|
|
.width = 720,
|
|
|
|
.height = 480,
|
2022-05-13 22:13:56 +08:00
|
|
|
.pixel_rate = OV5640_PIXEL_RATE_96M,
|
2022-05-13 22:13:58 +08:00
|
|
|
.dvp_timings = {
|
|
|
|
.analog_crop = {
|
|
|
|
.left = 0,
|
|
|
|
.top = 4,
|
|
|
|
.width = 2624,
|
|
|
|
.height = 1944,
|
|
|
|
},
|
|
|
|
.crop = {
|
|
|
|
.left = 56,
|
|
|
|
.top = 60,
|
|
|
|
.width = 720,
|
|
|
|
.height = 480,
|
|
|
|
},
|
|
|
|
.htot = 1896,
|
|
|
|
.vblank_def = 504,
|
2022-05-13 22:13:56 +08:00
|
|
|
},
|
2022-05-13 22:13:58 +08:00
|
|
|
.csi2_timings = {
|
|
|
|
/* Feed the full valid pixel array to the ISP. */
|
|
|
|
.analog_crop = {
|
|
|
|
.left = OV5640_PIXEL_ARRAY_LEFT,
|
|
|
|
.top = OV5640_PIXEL_ARRAY_TOP,
|
|
|
|
.width = OV5640_PIXEL_ARRAY_WIDTH,
|
|
|
|
.height = OV5640_PIXEL_ARRAY_HEIGHT,
|
|
|
|
},
|
|
|
|
.crop = {
|
|
|
|
.left = 56,
|
|
|
|
.top = 60,
|
|
|
|
.width = 720,
|
|
|
|
.height = 480,
|
|
|
|
},
|
|
|
|
.htot = 1896,
|
2022-05-13 22:14:07 +08:00
|
|
|
.vblank_def = 1206,
|
2022-05-13 22:13:56 +08:00
|
|
|
},
|
2022-05-13 22:14:01 +08:00
|
|
|
.reg_data = ov5640_setting_low_res,
|
|
|
|
.reg_data_size = ARRAY_SIZE(ov5640_setting_low_res),
|
2022-05-13 22:14:05 +08:00
|
|
|
.max_fps = OV5640_30_FPS,
|
|
|
|
.def_fps = OV5640_30_FPS
|
2022-05-13 22:13:50 +08:00
|
|
|
}, {
|
|
|
|
/* 720x576 */
|
2022-05-13 22:13:56 +08:00
|
|
|
.id = OV5640_MODE_PAL_720_576,
|
|
|
|
.dn_mode = SUBSAMPLING,
|
2022-05-13 22:13:58 +08:00
|
|
|
.width = 720,
|
|
|
|
.height = 576,
|
2022-05-13 22:13:56 +08:00
|
|
|
.pixel_rate = OV5640_PIXEL_RATE_96M,
|
2022-05-13 22:13:58 +08:00
|
|
|
.dvp_timings = {
|
|
|
|
.analog_crop = {
|
|
|
|
.left = 0,
|
|
|
|
.top = 4,
|
|
|
|
.width = 2624,
|
|
|
|
.height = 1944,
|
|
|
|
},
|
|
|
|
.crop = {
|
|
|
|
.left = 56,
|
|
|
|
.top = 6,
|
|
|
|
.width = 720,
|
|
|
|
.height = 576,
|
|
|
|
},
|
|
|
|
.htot = 1896,
|
|
|
|
.vblank_def = 408,
|
2022-05-13 22:13:56 +08:00
|
|
|
},
|
2022-05-13 22:13:58 +08:00
|
|
|
.csi2_timings = {
|
|
|
|
/* Feed the full valid pixel array to the ISP. */
|
|
|
|
.analog_crop = {
|
|
|
|
.left = OV5640_PIXEL_ARRAY_LEFT,
|
|
|
|
.top = OV5640_PIXEL_ARRAY_TOP,
|
|
|
|
.width = OV5640_PIXEL_ARRAY_WIDTH,
|
|
|
|
.height = OV5640_PIXEL_ARRAY_HEIGHT,
|
|
|
|
},
|
|
|
|
.crop = {
|
|
|
|
.left = 56,
|
|
|
|
.top = 6,
|
|
|
|
.width = 720,
|
|
|
|
.height = 576,
|
|
|
|
},
|
|
|
|
.htot = 1896,
|
2022-05-13 22:14:07 +08:00
|
|
|
.vblank_def = 1110,
|
2022-05-13 22:13:56 +08:00
|
|
|
},
|
2022-05-13 22:14:01 +08:00
|
|
|
.reg_data = ov5640_setting_low_res,
|
|
|
|
.reg_data_size = ARRAY_SIZE(ov5640_setting_low_res),
|
2022-05-13 22:14:05 +08:00
|
|
|
.max_fps = OV5640_30_FPS,
|
|
|
|
.def_fps = OV5640_30_FPS
|
2022-05-13 22:13:50 +08:00
|
|
|
}, {
|
|
|
|
/* 1024x768 */
|
2022-05-13 22:13:56 +08:00
|
|
|
.id = OV5640_MODE_XGA_1024_768,
|
|
|
|
.dn_mode = SUBSAMPLING,
|
|
|
|
.pixel_rate = OV5640_PIXEL_RATE_96M,
|
2022-05-13 22:13:58 +08:00
|
|
|
.width = 1024,
|
|
|
|
.height = 768,
|
|
|
|
.dvp_timings = {
|
|
|
|
.analog_crop = {
|
|
|
|
.left = 0,
|
|
|
|
.top = 4,
|
|
|
|
.width = 2624,
|
|
|
|
.height = 1944,
|
|
|
|
},
|
|
|
|
.crop = {
|
|
|
|
.left = 16,
|
|
|
|
.top = 6,
|
|
|
|
.width = 1024,
|
|
|
|
.height = 768,
|
|
|
|
},
|
|
|
|
.htot = 1896,
|
|
|
|
.vblank_def = 312,
|
2022-05-13 22:13:56 +08:00
|
|
|
},
|
2022-05-13 22:13:58 +08:00
|
|
|
.csi2_timings = {
|
|
|
|
.analog_crop = {
|
|
|
|
.left = 0,
|
|
|
|
.top = 4,
|
|
|
|
.width = OV5640_NATIVE_WIDTH,
|
|
|
|
.height = OV5640_PIXEL_ARRAY_HEIGHT,
|
|
|
|
},
|
|
|
|
.crop = {
|
|
|
|
.left = 16,
|
|
|
|
.top = 6,
|
|
|
|
.width = 1024,
|
|
|
|
.height = 768,
|
|
|
|
},
|
|
|
|
.htot = 1896,
|
2022-05-13 22:14:07 +08:00
|
|
|
.vblank_def = 918,
|
2022-05-13 22:13:56 +08:00
|
|
|
},
|
2022-05-13 22:14:01 +08:00
|
|
|
.reg_data = ov5640_setting_low_res,
|
|
|
|
.reg_data_size = ARRAY_SIZE(ov5640_setting_low_res),
|
2022-05-13 22:14:05 +08:00
|
|
|
.max_fps = OV5640_30_FPS,
|
|
|
|
.def_fps = OV5640_30_FPS
|
2022-05-13 22:13:50 +08:00
|
|
|
}, {
|
|
|
|
/* 1280x720 */
|
2022-05-13 22:13:56 +08:00
|
|
|
.id = OV5640_MODE_720P_1280_720,
|
|
|
|
.dn_mode = SUBSAMPLING,
|
|
|
|
.pixel_rate = OV5640_PIXEL_RATE_124M,
|
2022-05-13 22:13:58 +08:00
|
|
|
.width = 1280,
|
|
|
|
.height = 720,
|
|
|
|
.dvp_timings = {
|
|
|
|
.analog_crop = {
|
|
|
|
.left = 0,
|
|
|
|
.top = 250,
|
|
|
|
.width = 2624,
|
|
|
|
.height = 1456,
|
|
|
|
},
|
|
|
|
.crop = {
|
|
|
|
.left = 16,
|
|
|
|
.top = 4,
|
|
|
|
.width = 1280,
|
|
|
|
.height = 720,
|
|
|
|
},
|
|
|
|
.htot = 1892,
|
|
|
|
.vblank_def = 20,
|
2022-05-13 22:13:56 +08:00
|
|
|
},
|
2022-05-13 22:13:58 +08:00
|
|
|
.csi2_timings = {
|
|
|
|
.analog_crop = {
|
|
|
|
.left = 0,
|
|
|
|
.top = 250,
|
|
|
|
.width = 2624,
|
|
|
|
.height = 1456,
|
|
|
|
},
|
|
|
|
.crop = {
|
|
|
|
.left = 16,
|
|
|
|
.top = 4,
|
|
|
|
.width = 1280,
|
|
|
|
.height = 720,
|
|
|
|
},
|
2022-05-13 22:14:07 +08:00
|
|
|
.htot = 1600,
|
|
|
|
.vblank_def = 560,
|
2022-05-13 22:13:56 +08:00
|
|
|
},
|
|
|
|
.reg_data = ov5640_setting_720P_1280_720,
|
|
|
|
.reg_data_size = ARRAY_SIZE(ov5640_setting_720P_1280_720),
|
2022-05-13 22:14:05 +08:00
|
|
|
.max_fps = OV5640_30_FPS,
|
|
|
|
.def_fps = OV5640_30_FPS
|
2022-05-13 22:13:50 +08:00
|
|
|
}, {
|
|
|
|
/* 1920x1080 */
|
2022-05-13 22:13:56 +08:00
|
|
|
.id = OV5640_MODE_1080P_1920_1080,
|
|
|
|
.dn_mode = SCALING,
|
|
|
|
.pixel_rate = OV5640_PIXEL_RATE_148M,
|
2022-05-13 22:13:58 +08:00
|
|
|
.width = 1920,
|
|
|
|
.height = 1080,
|
|
|
|
.dvp_timings = {
|
|
|
|
.analog_crop = {
|
|
|
|
.left = 336,
|
|
|
|
.top = 434,
|
|
|
|
.width = 1952,
|
|
|
|
.height = 1088,
|
|
|
|
},
|
|
|
|
.crop = {
|
|
|
|
.left = 16,
|
|
|
|
.top = 4,
|
|
|
|
.width = 1920,
|
|
|
|
.height = 1080,
|
|
|
|
},
|
|
|
|
.htot = 2500,
|
|
|
|
.vblank_def = 40,
|
2022-05-13 22:13:56 +08:00
|
|
|
},
|
2022-05-13 22:13:58 +08:00
|
|
|
.csi2_timings = {
|
|
|
|
/* Crop the full valid pixel array in the center. */
|
|
|
|
.analog_crop = {
|
|
|
|
.left = 336,
|
|
|
|
.top = 434,
|
|
|
|
.width = 1952,
|
|
|
|
.height = 1088,
|
|
|
|
},
|
|
|
|
/* Maintain a larger processing margins. */
|
|
|
|
.crop = {
|
|
|
|
.left = 16,
|
|
|
|
.top = 4,
|
|
|
|
.width = 1920,
|
|
|
|
.height = 1080,
|
|
|
|
},
|
2022-05-13 22:14:07 +08:00
|
|
|
.htot = 2234,
|
|
|
|
.vblank_def = 24,
|
2022-05-13 22:13:56 +08:00
|
|
|
},
|
|
|
|
.reg_data = ov5640_setting_1080P_1920_1080,
|
|
|
|
.reg_data_size = ARRAY_SIZE(ov5640_setting_1080P_1920_1080),
|
2022-05-13 22:14:05 +08:00
|
|
|
.max_fps = OV5640_30_FPS,
|
|
|
|
.def_fps = OV5640_30_FPS
|
2022-05-13 22:13:50 +08:00
|
|
|
}, {
|
|
|
|
/* 2592x1944 */
|
2022-05-13 22:13:56 +08:00
|
|
|
.id = OV5640_MODE_QSXGA_2592_1944,
|
|
|
|
.dn_mode = SCALING,
|
|
|
|
.pixel_rate = OV5640_PIXEL_RATE_168M,
|
2022-05-13 22:13:58 +08:00
|
|
|
.width = OV5640_PIXEL_ARRAY_WIDTH,
|
|
|
|
.height = OV5640_PIXEL_ARRAY_HEIGHT,
|
|
|
|
.dvp_timings = {
|
|
|
|
.analog_crop = {
|
|
|
|
.left = 0,
|
|
|
|
.top = 0,
|
|
|
|
.width = 2624,
|
|
|
|
.height = 1952,
|
|
|
|
},
|
|
|
|
.crop = {
|
|
|
|
.left = 16,
|
|
|
|
.top = 4,
|
|
|
|
.width = 2592,
|
|
|
|
.height = 1944,
|
|
|
|
},
|
|
|
|
.htot = 2844,
|
|
|
|
.vblank_def = 24,
|
2022-05-13 22:13:56 +08:00
|
|
|
},
|
2022-05-13 22:13:58 +08:00
|
|
|
.csi2_timings = {
|
|
|
|
/* Give more processing margin to full resolution. */
|
|
|
|
.analog_crop = {
|
|
|
|
.left = 0,
|
|
|
|
.top = 0,
|
|
|
|
.width = OV5640_NATIVE_WIDTH,
|
|
|
|
.height = 1952,
|
|
|
|
},
|
|
|
|
.crop = {
|
|
|
|
.left = 16,
|
|
|
|
.top = 4,
|
|
|
|
.width = 2592,
|
|
|
|
.height = 1944,
|
|
|
|
},
|
|
|
|
.htot = 2844,
|
|
|
|
.vblank_def = 24,
|
2022-05-13 22:13:56 +08:00
|
|
|
},
|
|
|
|
.reg_data = ov5640_setting_QSXGA_2592_1944,
|
|
|
|
.reg_data_size = ARRAY_SIZE(ov5640_setting_QSXGA_2592_1944),
|
2022-05-13 22:14:05 +08:00
|
|
|
.max_fps = OV5640_15_FPS,
|
|
|
|
.def_fps = OV5640_15_FPS
|
2022-05-13 22:13:50 +08:00
|
|
|
},
|
2017-06-08 02:33:56 +08:00
|
|
|
};
|
|
|
|
|
2022-05-13 22:13:59 +08:00
|
|
|
static const struct ov5640_timings *
|
|
|
|
ov5640_timings(const struct ov5640_dev *sensor,
|
|
|
|
const struct ov5640_mode_info *mode)
|
|
|
|
{
|
|
|
|
if (ov5640_is_csi2(sensor))
|
|
|
|
return &mode->csi2_timings;
|
|
|
|
|
|
|
|
return &mode->dvp_timings;
|
|
|
|
}
|
|
|
|
|
2017-06-08 02:33:56 +08:00
|
|
|
static int ov5640_init_slave_id(struct ov5640_dev *sensor)
|
|
|
|
{
|
|
|
|
struct i2c_client *client = sensor->i2c_client;
|
|
|
|
struct i2c_msg msg;
|
|
|
|
u8 buf[3];
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (client->addr == OV5640_DEFAULT_SLAVE_ID)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
buf[0] = OV5640_REG_SLAVE_ID >> 8;
|
|
|
|
buf[1] = OV5640_REG_SLAVE_ID & 0xff;
|
|
|
|
buf[2] = client->addr << 1;
|
|
|
|
|
|
|
|
msg.addr = OV5640_DEFAULT_SLAVE_ID;
|
|
|
|
msg.flags = 0;
|
|
|
|
msg.buf = buf;
|
|
|
|
msg.len = sizeof(buf);
|
|
|
|
|
|
|
|
ret = i2c_transfer(client->adapter, &msg, 1);
|
|
|
|
if (ret < 0) {
|
|
|
|
dev_err(&client->dev, "%s: failed with %d\n", __func__, ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ov5640_write_reg(struct ov5640_dev *sensor, u16 reg, u8 val)
|
|
|
|
{
|
|
|
|
struct i2c_client *client = sensor->i2c_client;
|
|
|
|
struct i2c_msg msg;
|
|
|
|
u8 buf[3];
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
buf[0] = reg >> 8;
|
|
|
|
buf[1] = reg & 0xff;
|
|
|
|
buf[2] = val;
|
|
|
|
|
|
|
|
msg.addr = client->addr;
|
|
|
|
msg.flags = client->flags;
|
|
|
|
msg.buf = buf;
|
|
|
|
msg.len = sizeof(buf);
|
|
|
|
|
|
|
|
ret = i2c_transfer(client->adapter, &msg, 1);
|
|
|
|
if (ret < 0) {
|
2018-01-31 20:46:17 +08:00
|
|
|
dev_err(&client->dev, "%s: error: reg=%x, val=%x\n",
|
2017-06-08 02:33:56 +08:00
|
|
|
__func__, reg, val);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ov5640_read_reg(struct ov5640_dev *sensor, u16 reg, u8 *val)
|
|
|
|
{
|
|
|
|
struct i2c_client *client = sensor->i2c_client;
|
|
|
|
struct i2c_msg msg[2];
|
|
|
|
u8 buf[2];
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
buf[0] = reg >> 8;
|
|
|
|
buf[1] = reg & 0xff;
|
|
|
|
|
|
|
|
msg[0].addr = client->addr;
|
|
|
|
msg[0].flags = client->flags;
|
|
|
|
msg[0].buf = buf;
|
|
|
|
msg[0].len = sizeof(buf);
|
|
|
|
|
|
|
|
msg[1].addr = client->addr;
|
|
|
|
msg[1].flags = client->flags | I2C_M_RD;
|
|
|
|
msg[1].buf = buf;
|
|
|
|
msg[1].len = 1;
|
|
|
|
|
|
|
|
ret = i2c_transfer(client->adapter, msg, 2);
|
2018-01-31 20:46:17 +08:00
|
|
|
if (ret < 0) {
|
|
|
|
dev_err(&client->dev, "%s: error: reg=%x\n",
|
|
|
|
__func__, reg);
|
2017-06-08 02:33:56 +08:00
|
|
|
return ret;
|
2018-01-31 20:46:17 +08:00
|
|
|
}
|
2017-06-08 02:33:56 +08:00
|
|
|
|
|
|
|
*val = buf[0];
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ov5640_read_reg16(struct ov5640_dev *sensor, u16 reg, u16 *val)
|
|
|
|
{
|
|
|
|
u8 hi, lo;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = ov5640_read_reg(sensor, reg, &hi);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2018-02-01 16:44:06 +08:00
|
|
|
ret = ov5640_read_reg(sensor, reg + 1, &lo);
|
2017-06-08 02:33:56 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
*val = ((u16)hi << 8) | (u16)lo;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ov5640_write_reg16(struct ov5640_dev *sensor, u16 reg, u16 val)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = ov5640_write_reg(sensor, reg, val >> 8);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return ov5640_write_reg(sensor, reg + 1, val & 0xff);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ov5640_mod_reg(struct ov5640_dev *sensor, u16 reg,
|
|
|
|
u8 mask, u8 val)
|
|
|
|
{
|
|
|
|
u8 readval;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = ov5640_read_reg(sensor, reg, &readval);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
readval &= ~mask;
|
|
|
|
val &= mask;
|
|
|
|
val |= readval;
|
|
|
|
|
|
|
|
return ov5640_write_reg(sensor, reg, val);
|
|
|
|
}
|
|
|
|
|
2018-12-03 16:44:17 +08:00
|
|
|
/*
|
|
|
|
* After trying the various combinations, reading various
|
2019-02-19 03:28:58 +08:00
|
|
|
* documentations spread around the net, and from the various
|
2018-12-03 16:44:17 +08:00
|
|
|
* feedback, the clock tree is probably as follows:
|
|
|
|
*
|
|
|
|
* +--------------+
|
|
|
|
* | Ext. Clock |
|
|
|
|
* +-+------------+
|
|
|
|
* | +----------+
|
|
|
|
* +->| PLL1 | - reg 0x3036, for the multiplier
|
|
|
|
* +-+--------+ - reg 0x3037, bits 0-3 for the pre-divider
|
|
|
|
* | +--------------+
|
|
|
|
* +->| System Clock | - reg 0x3035, bits 4-7
|
|
|
|
* +-+------------+
|
|
|
|
* | +--------------+
|
|
|
|
* +->| MIPI Divider | - reg 0x3035, bits 0-3
|
|
|
|
* | +-+------------+
|
|
|
|
* | +----------------> MIPI SCLK
|
|
|
|
* | + +-----+
|
|
|
|
* | +->| / 2 |-------> MIPI BIT CLK
|
|
|
|
* | +-----+
|
|
|
|
* | +--------------+
|
|
|
|
* +->| PLL Root Div | - reg 0x3037, bit 4
|
|
|
|
* +-+------------+
|
|
|
|
* | +---------+
|
2020-08-03 17:06:58 +08:00
|
|
|
* +->| Bit Div | - reg 0x3034, bits 0-3
|
2018-12-03 16:44:17 +08:00
|
|
|
* +-+-------+
|
|
|
|
* | +-------------+
|
|
|
|
* +->| SCLK Div | - reg 0x3108, bits 0-1
|
|
|
|
* | +-+-----------+
|
|
|
|
* | +---------------> SCLK
|
|
|
|
* | +-------------+
|
|
|
|
* +->| SCLK 2X Div | - reg 0x3108, bits 2-3
|
|
|
|
* | +-+-----------+
|
|
|
|
* | +---------------> SCLK 2X
|
|
|
|
* | +-------------+
|
|
|
|
* +->| PCLK Div | - reg 0x3108, bits 4-5
|
|
|
|
* ++------------+
|
|
|
|
* + +-----------+
|
|
|
|
* +->| P_DIV | - reg 0x3035, bits 0-3
|
|
|
|
* +-----+-----+
|
|
|
|
* +------------> PCLK
|
|
|
|
*
|
2022-05-13 22:13:55 +08:00
|
|
|
* There seems to be also constraints:
|
2018-12-03 16:44:17 +08:00
|
|
|
* - the PLL pre-divider output rate should be in the 4-27MHz range
|
|
|
|
* - the PLL multiplier output rate should be in the 500-1000MHz range
|
|
|
|
* - PCLK >= SCLK * 2 in YUV, >= SCLK in Raw or JPEG
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is supposed to be ranging from 1 to 8, but the value is always
|
|
|
|
* set to 3 in the vendor kernels.
|
|
|
|
*/
|
|
|
|
#define OV5640_PLL_PREDIV 3
|
|
|
|
|
|
|
|
#define OV5640_PLL_MULT_MIN 4
|
|
|
|
#define OV5640_PLL_MULT_MAX 252
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is supposed to be ranging from 1 to 16, but the value is
|
|
|
|
* always set to either 1 or 2 in the vendor kernels.
|
|
|
|
*/
|
|
|
|
#define OV5640_SYSDIV_MIN 1
|
|
|
|
#define OV5640_SYSDIV_MAX 16
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is supposed to be ranging from 1 to 2, but the value is always
|
|
|
|
* set to 2 in the vendor kernels.
|
|
|
|
*/
|
|
|
|
#define OV5640_PLL_ROOT_DIV 2
|
|
|
|
#define OV5640_PLL_CTRL3_PLL_ROOT_DIV_2 BIT(4)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We only supports 8-bit formats at the moment
|
|
|
|
*/
|
|
|
|
#define OV5640_BIT_DIV 2
|
|
|
|
#define OV5640_PLL_CTRL0_MIPI_MODE_8BIT 0x08
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is supposed to be ranging from 1 to 8, but the value is always
|
|
|
|
* set to 2 in the vendor kernels.
|
|
|
|
*/
|
|
|
|
#define OV5640_SCLK_ROOT_DIV 2
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is hardcoded so that the consistency is maintained between SCLK and
|
|
|
|
* SCLK 2x.
|
|
|
|
*/
|
|
|
|
#define OV5640_SCLK2X_ROOT_DIV (OV5640_SCLK_ROOT_DIV / 2)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is supposed to be ranging from 1 to 8, but the value is always
|
|
|
|
* set to 1 in the vendor kernels.
|
|
|
|
*/
|
|
|
|
#define OV5640_PCLK_ROOT_DIV 1
|
|
|
|
#define OV5640_PLL_SYS_ROOT_DIVIDER_BYPASS 0x00
|
|
|
|
|
|
|
|
static unsigned long ov5640_compute_sys_clk(struct ov5640_dev *sensor,
|
|
|
|
u8 pll_prediv, u8 pll_mult,
|
|
|
|
u8 sysdiv)
|
|
|
|
{
|
|
|
|
unsigned long sysclk = sensor->xclk_freq / pll_prediv * pll_mult;
|
|
|
|
|
|
|
|
/* PLL1 output cannot exceed 1GHz. */
|
|
|
|
if (sysclk / 1000000 > 1000)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return sysclk / sysdiv;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned long ov5640_calc_sys_clk(struct ov5640_dev *sensor,
|
|
|
|
unsigned long rate,
|
|
|
|
u8 *pll_prediv, u8 *pll_mult,
|
|
|
|
u8 *sysdiv)
|
|
|
|
{
|
|
|
|
unsigned long best = ~0;
|
|
|
|
u8 best_sysdiv = 1, best_mult = 1;
|
|
|
|
u8 _sysdiv, _pll_mult;
|
|
|
|
|
|
|
|
for (_sysdiv = OV5640_SYSDIV_MIN;
|
|
|
|
_sysdiv <= OV5640_SYSDIV_MAX;
|
|
|
|
_sysdiv++) {
|
|
|
|
for (_pll_mult = OV5640_PLL_MULT_MIN;
|
|
|
|
_pll_mult <= OV5640_PLL_MULT_MAX;
|
|
|
|
_pll_mult++) {
|
|
|
|
unsigned long _rate;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The PLL multiplier cannot be odd if above
|
|
|
|
* 127.
|
|
|
|
*/
|
|
|
|
if (_pll_mult > 127 && (_pll_mult % 2))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
_rate = ov5640_compute_sys_clk(sensor,
|
|
|
|
OV5640_PLL_PREDIV,
|
|
|
|
_pll_mult, _sysdiv);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We have reached the maximum allowed PLL1 output,
|
|
|
|
* increase sysdiv.
|
|
|
|
*/
|
2019-10-29 20:42:11 +08:00
|
|
|
if (!_rate)
|
2018-12-03 16:44:17 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Prefer rates above the expected clock rate than
|
|
|
|
* below, even if that means being less precise.
|
|
|
|
*/
|
|
|
|
if (_rate < rate)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (abs(rate - _rate) < abs(rate - best)) {
|
|
|
|
best = _rate;
|
|
|
|
best_sysdiv = _sysdiv;
|
|
|
|
best_mult = _pll_mult;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_rate == rate)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
*sysdiv = best_sysdiv;
|
|
|
|
*pll_prediv = OV5640_PLL_PREDIV;
|
|
|
|
*pll_mult = best_mult;
|
|
|
|
|
|
|
|
return best;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ov5640_set_mipi_pclk() - Calculate the clock tree configuration values
|
|
|
|
* for the MIPI CSI-2 output.
|
|
|
|
*/
|
2022-05-13 22:13:55 +08:00
|
|
|
static int ov5640_set_mipi_pclk(struct ov5640_dev *sensor)
|
2018-12-03 16:44:17 +08:00
|
|
|
{
|
2022-05-13 22:13:55 +08:00
|
|
|
u8 bit_div, mipi_div, pclk_div, sclk_div, sclk2x_div, root_div;
|
2018-12-03 16:44:17 +08:00
|
|
|
u8 prediv, mult, sysdiv;
|
2022-05-13 22:13:55 +08:00
|
|
|
unsigned long link_freq;
|
|
|
|
unsigned long sysclk;
|
|
|
|
u8 pclk_period;
|
|
|
|
u32 sample_rate;
|
|
|
|
u32 num_lanes;
|
2018-12-03 16:44:17 +08:00
|
|
|
int ret;
|
|
|
|
|
2022-05-13 22:13:55 +08:00
|
|
|
/* Use the link freq computed at ov5640_update_pixel_rate() time. */
|
|
|
|
link_freq = sensor->current_link_freq;
|
|
|
|
|
2018-12-03 16:44:17 +08:00
|
|
|
/*
|
2022-05-13 22:13:55 +08:00
|
|
|
* - mipi_div - Additional divider for the MIPI lane clock.
|
|
|
|
*
|
|
|
|
* Higher link frequencies would make sysclk > 1GHz.
|
|
|
|
* Keep the sysclk low and do not divide in the MIPI domain.
|
2018-12-03 16:44:17 +08:00
|
|
|
*/
|
2022-05-13 22:13:55 +08:00
|
|
|
if (link_freq > OV5640_LINK_RATE_MAX)
|
|
|
|
mipi_div = 1;
|
2018-12-03 16:44:17 +08:00
|
|
|
else
|
2022-05-13 22:13:55 +08:00
|
|
|
mipi_div = 2;
|
2018-12-03 16:44:17 +08:00
|
|
|
|
2022-05-13 22:13:55 +08:00
|
|
|
sysclk = link_freq * mipi_div;
|
|
|
|
ov5640_calc_sys_clk(sensor, sysclk, &prediv, &mult, &sysdiv);
|
2018-12-03 16:44:17 +08:00
|
|
|
|
2022-05-13 22:13:55 +08:00
|
|
|
/*
|
|
|
|
* Adjust PLL parameters to maintain the MIPI_SCLK-to-PCLK ratio.
|
|
|
|
*
|
|
|
|
* - root_div = 2 (fixed)
|
|
|
|
* - bit_div : MIPI 8-bit = 2; MIPI 10-bit = 2.5
|
|
|
|
* - pclk_div = 1 (fixed)
|
|
|
|
* - p_div = (2 lanes ? mipi_div : 2 * mipi_div)
|
|
|
|
*
|
|
|
|
* This results in the following MIPI_SCLK depending on the number
|
|
|
|
* of lanes:
|
|
|
|
*
|
|
|
|
* - 2 lanes: MIPI_SCLK = (4 or 5) * PCLK
|
|
|
|
* - 1 lanes: MIPI_SCLK = (8 or 10) * PCLK
|
|
|
|
*/
|
|
|
|
root_div = OV5640_PLL_CTRL3_PLL_ROOT_DIV_2;
|
|
|
|
bit_div = OV5640_PLL_CTRL0_MIPI_MODE_8BIT;
|
|
|
|
pclk_div = ilog2(OV5640_PCLK_ROOT_DIV);
|
2018-12-03 16:44:17 +08:00
|
|
|
|
2022-05-13 22:13:55 +08:00
|
|
|
/*
|
|
|
|
* Scaler clock:
|
|
|
|
* - YUV: PCLK >= 2 * SCLK
|
|
|
|
* - RAW or JPEG: PCLK >= SCLK
|
|
|
|
* - sclk2x_div = sclk_div / 2
|
|
|
|
*/
|
|
|
|
sclk_div = ilog2(OV5640_SCLK_ROOT_DIV);
|
|
|
|
sclk2x_div = ilog2(OV5640_SCLK2X_ROOT_DIV);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the pixel clock period expressed in ns with 1-bit decimal
|
|
|
|
* (0x01=0.5ns).
|
|
|
|
*
|
|
|
|
* The register is very briefly documented. In the OV5645 datasheet it
|
|
|
|
* is described as (2 * pclk period), and from testing it seems the
|
|
|
|
* actual definition is 2 * 8-bit sample period.
|
|
|
|
*
|
|
|
|
* 2 * sample_period = (mipi_clk * 2 * num_lanes / bpp) * (bpp / 8) / 2
|
|
|
|
*/
|
|
|
|
num_lanes = sensor->ep.bus.mipi_csi2.num_data_lanes;
|
|
|
|
sample_rate = (link_freq * mipi_div * num_lanes * 2) / 16;
|
|
|
|
pclk_period = 2000000000UL / sample_rate;
|
|
|
|
|
|
|
|
/* Program the clock tree registers. */
|
|
|
|
ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL0, 0x0f, bit_div);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL1, 0xff,
|
|
|
|
(sysdiv << 4) | mipi_div);
|
2018-12-03 16:44:17 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL2, 0xff, mult);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2022-05-13 22:13:55 +08:00
|
|
|
ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL3, 0x1f,
|
|
|
|
root_div | prediv);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, 0x3f,
|
|
|
|
(pclk_div << 4) | (sclk2x_div << 2) | sclk_div);
|
2018-12-03 16:44:17 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2022-05-13 22:13:55 +08:00
|
|
|
return ov5640_write_reg(sensor, OV5640_REG_PCLK_PERIOD, pclk_period);
|
|
|
|
}
|
|
|
|
|
|
|
|
static u32 ov5640_calc_pixel_rate(struct ov5640_dev *sensor)
|
|
|
|
{
|
2022-05-13 22:13:56 +08:00
|
|
|
const struct ov5640_mode_info *mode = sensor->current_mode;
|
2022-05-13 22:13:58 +08:00
|
|
|
const struct ov5640_timings *timings = &mode->dvp_timings;
|
2022-05-13 22:13:55 +08:00
|
|
|
u32 rate;
|
|
|
|
|
2022-05-13 22:13:58 +08:00
|
|
|
rate = timings->htot * (timings->crop.height + timings->vblank_def);
|
2022-05-13 22:13:55 +08:00
|
|
|
rate *= ov5640_framerates[sensor->current_fr];
|
|
|
|
|
|
|
|
return rate;
|
2018-12-03 16:44:17 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned long ov5640_calc_pclk(struct ov5640_dev *sensor,
|
|
|
|
unsigned long rate,
|
|
|
|
u8 *pll_prediv, u8 *pll_mult, u8 *sysdiv,
|
|
|
|
u8 *pll_rdiv, u8 *bit_div, u8 *pclk_div)
|
|
|
|
{
|
|
|
|
unsigned long _rate = rate * OV5640_PLL_ROOT_DIV * OV5640_BIT_DIV *
|
|
|
|
OV5640_PCLK_ROOT_DIV;
|
|
|
|
|
|
|
|
_rate = ov5640_calc_sys_clk(sensor, _rate, pll_prediv, pll_mult,
|
|
|
|
sysdiv);
|
|
|
|
*pll_rdiv = OV5640_PLL_ROOT_DIV;
|
|
|
|
*bit_div = OV5640_BIT_DIV;
|
|
|
|
*pclk_div = OV5640_PCLK_ROOT_DIV;
|
|
|
|
|
|
|
|
return _rate / *pll_rdiv / *bit_div / *pclk_div;
|
|
|
|
}
|
|
|
|
|
2022-05-13 22:13:55 +08:00
|
|
|
static int ov5640_set_dvp_pclk(struct ov5640_dev *sensor)
|
2018-12-03 16:44:17 +08:00
|
|
|
{
|
|
|
|
u8 prediv, mult, sysdiv, pll_rdiv, bit_div, pclk_div;
|
2022-05-13 22:13:55 +08:00
|
|
|
u32 rate;
|
2018-12-03 16:44:17 +08:00
|
|
|
int ret;
|
|
|
|
|
2022-05-13 22:13:55 +08:00
|
|
|
rate = ov5640_calc_pixel_rate(sensor);
|
2022-05-13 22:14:15 +08:00
|
|
|
rate *= ov5640_code_to_bpp(sensor, sensor->fmt.code);
|
2022-05-13 22:13:55 +08:00
|
|
|
rate /= sensor->ep.bus.parallel.bus_width;
|
|
|
|
|
2018-12-03 16:44:17 +08:00
|
|
|
ov5640_calc_pclk(sensor, rate, &prediv, &mult, &sysdiv, &pll_rdiv,
|
|
|
|
&bit_div, &pclk_div);
|
|
|
|
|
|
|
|
if (bit_div == 2)
|
|
|
|
bit_div = 8;
|
|
|
|
|
|
|
|
ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL0,
|
|
|
|
0x0f, bit_div);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We need to set sysdiv according to the clock, and to clear
|
|
|
|
* the MIPI divider.
|
|
|
|
*/
|
|
|
|
ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL1,
|
|
|
|
0xff, sysdiv << 4);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL2,
|
|
|
|
0xff, mult);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL3,
|
|
|
|
0x1f, prediv | ((pll_rdiv - 1) << 4));
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, 0x30,
|
|
|
|
(ilog2(pclk_div) << 4));
|
|
|
|
}
|
|
|
|
|
2019-01-18 16:52:05 +08:00
|
|
|
/* set JPEG framing sizes */
|
|
|
|
static int ov5640_set_jpeg_timings(struct ov5640_dev *sensor,
|
|
|
|
const struct ov5640_mode_info *mode)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
2019-01-18 16:52:06 +08:00
|
|
|
/*
|
|
|
|
* compression mode 3 timing
|
|
|
|
*
|
|
|
|
* Data is transmitted with programmable width (VFIFO_HSIZE).
|
|
|
|
* No padding done. Last line may have less data. Varying
|
|
|
|
* number of lines per frame, depending on amount of data.
|
|
|
|
*/
|
|
|
|
ret = ov5640_mod_reg(sensor, OV5640_REG_JPG_MODE_SELECT, 0x7, 0x3);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2022-05-13 22:13:58 +08:00
|
|
|
ret = ov5640_write_reg16(sensor, OV5640_REG_VFIFO_HSIZE, mode->width);
|
2019-01-18 16:52:05 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2022-05-13 22:13:58 +08:00
|
|
|
return ov5640_write_reg16(sensor, OV5640_REG_VFIFO_VSIZE, mode->height);
|
2019-01-18 16:52:05 +08:00
|
|
|
}
|
|
|
|
|
2017-06-08 02:33:56 +08:00
|
|
|
/* download ov5640 settings to sensor through i2c */
|
2018-07-18 18:06:23 +08:00
|
|
|
static int ov5640_set_timings(struct ov5640_dev *sensor,
|
|
|
|
const struct ov5640_mode_info *mode)
|
|
|
|
{
|
2022-05-13 22:13:58 +08:00
|
|
|
const struct ov5640_timings *timings;
|
|
|
|
const struct v4l2_rect *analog_crop;
|
|
|
|
const struct v4l2_rect *crop;
|
2018-07-18 18:06:23 +08:00
|
|
|
int ret;
|
|
|
|
|
2019-01-18 16:52:05 +08:00
|
|
|
if (sensor->fmt.code == MEDIA_BUS_FMT_JPEG_1X8) {
|
|
|
|
ret = ov5640_set_jpeg_timings(sensor, mode);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2022-05-13 22:13:59 +08:00
|
|
|
timings = ov5640_timings(sensor, mode);
|
2022-05-13 22:13:58 +08:00
|
|
|
analog_crop = &timings->analog_crop;
|
|
|
|
crop = &timings->crop;
|
|
|
|
|
2022-05-13 22:13:56 +08:00
|
|
|
ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HS,
|
|
|
|
analog_crop->left);
|
2018-07-18 18:06:23 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2022-05-13 22:13:56 +08:00
|
|
|
ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_VS,
|
|
|
|
analog_crop->top);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HW,
|
|
|
|
analog_crop->left + analog_crop->width - 1);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_VH,
|
|
|
|
analog_crop->top + analog_crop->height - 1);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HOFFS, crop->left);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_VOFFS, crop->top);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2022-05-13 22:13:58 +08:00
|
|
|
ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPHO, mode->width);
|
2022-05-13 22:13:56 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2022-05-13 22:13:58 +08:00
|
|
|
ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPVO, mode->height);
|
2018-07-18 18:06:23 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2022-05-13 22:13:58 +08:00
|
|
|
ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HTS, timings->htot);
|
2018-07-18 18:06:23 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2022-05-13 22:13:56 +08:00
|
|
|
ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_VTS,
|
2022-05-13 22:13:58 +08:00
|
|
|
mode->height + timings->vblank_def);
|
2022-05-13 22:13:56 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return 0;
|
2018-07-18 18:06:23 +08:00
|
|
|
}
|
|
|
|
|
2022-05-13 22:14:02 +08:00
|
|
|
static void ov5640_load_regs(struct ov5640_dev *sensor,
|
|
|
|
const struct reg_value *regs, unsigned int regnum)
|
2017-06-08 02:33:56 +08:00
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
u32 delay_ms;
|
|
|
|
u16 reg_addr;
|
|
|
|
u8 mask, val;
|
|
|
|
int ret = 0;
|
|
|
|
|
2022-05-13 22:14:02 +08:00
|
|
|
for (i = 0; i < regnum; ++i, ++regs) {
|
2017-06-08 02:33:56 +08:00
|
|
|
delay_ms = regs->delay_ms;
|
|
|
|
reg_addr = regs->reg_addr;
|
|
|
|
val = regs->val;
|
|
|
|
mask = regs->mask;
|
|
|
|
|
2020-09-05 04:18:30 +08:00
|
|
|
/* remain in power down mode for DVP */
|
|
|
|
if (regs->reg_addr == OV5640_REG_SYS_CTRL0 &&
|
|
|
|
val == OV5640_REG_SYS_CTRL0_SW_PWUP &&
|
2022-05-13 22:13:51 +08:00
|
|
|
!ov5640_is_csi2(sensor))
|
2020-09-05 04:18:30 +08:00
|
|
|
continue;
|
|
|
|
|
2017-06-08 02:33:56 +08:00
|
|
|
if (mask)
|
|
|
|
ret = ov5640_mod_reg(sensor, reg_addr, mask, val);
|
|
|
|
else
|
|
|
|
ret = ov5640_write_reg(sensor, reg_addr, val);
|
|
|
|
if (ret)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (delay_ms)
|
2018-02-01 16:44:06 +08:00
|
|
|
usleep_range(1000 * delay_ms, 1000 * delay_ms + 100);
|
2017-06-08 02:33:56 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-11 21:48:17 +08:00
|
|
|
static int ov5640_set_autoexposure(struct ov5640_dev *sensor, bool on)
|
|
|
|
{
|
|
|
|
return ov5640_mod_reg(sensor, OV5640_REG_AEC_PK_MANUAL,
|
|
|
|
BIT(0), on ? 0 : BIT(0));
|
|
|
|
}
|
|
|
|
|
2017-06-08 02:33:56 +08:00
|
|
|
/* read exposure, in number of line periods */
|
|
|
|
static int ov5640_get_exposure(struct ov5640_dev *sensor)
|
|
|
|
{
|
|
|
|
int exp, ret;
|
|
|
|
u8 temp;
|
|
|
|
|
|
|
|
ret = ov5640_read_reg(sensor, OV5640_REG_AEC_PK_EXPOSURE_HI, &temp);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
exp = ((int)temp & 0x0f) << 16;
|
|
|
|
ret = ov5640_read_reg(sensor, OV5640_REG_AEC_PK_EXPOSURE_MED, &temp);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
exp |= ((int)temp << 8);
|
|
|
|
ret = ov5640_read_reg(sensor, OV5640_REG_AEC_PK_EXPOSURE_LO, &temp);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
exp |= (int)temp;
|
|
|
|
|
|
|
|
return exp >> 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* write exposure, given number of line periods */
|
|
|
|
static int ov5640_set_exposure(struct ov5640_dev *sensor, u32 exposure)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
exposure <<= 4;
|
|
|
|
|
|
|
|
ret = ov5640_write_reg(sensor,
|
|
|
|
OV5640_REG_AEC_PK_EXPOSURE_LO,
|
|
|
|
exposure & 0xff);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
ret = ov5640_write_reg(sensor,
|
|
|
|
OV5640_REG_AEC_PK_EXPOSURE_MED,
|
|
|
|
(exposure >> 8) & 0xff);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
return ov5640_write_reg(sensor,
|
|
|
|
OV5640_REG_AEC_PK_EXPOSURE_HI,
|
|
|
|
(exposure >> 16) & 0x0f);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ov5640_get_gain(struct ov5640_dev *sensor)
|
|
|
|
{
|
|
|
|
u16 gain;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = ov5640_read_reg16(sensor, OV5640_REG_AEC_PK_REAL_GAIN, &gain);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return gain & 0x3ff;
|
|
|
|
}
|
|
|
|
|
2018-09-11 21:48:18 +08:00
|
|
|
static int ov5640_set_gain(struct ov5640_dev *sensor, int gain)
|
|
|
|
{
|
|
|
|
return ov5640_write_reg16(sensor, OV5640_REG_AEC_PK_REAL_GAIN,
|
|
|
|
(u16)gain & 0x3ff);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ov5640_set_autogain(struct ov5640_dev *sensor, bool on)
|
|
|
|
{
|
|
|
|
return ov5640_mod_reg(sensor, OV5640_REG_AEC_PK_MANUAL,
|
|
|
|
BIT(1), on ? 0 : BIT(1));
|
|
|
|
}
|
|
|
|
|
2018-01-03 17:57:31 +08:00
|
|
|
static int ov5640_set_stream_dvp(struct ov5640_dev *sensor, bool on)
|
|
|
|
{
|
2020-09-05 04:18:30 +08:00
|
|
|
return ov5640_write_reg(sensor, OV5640_REG_SYS_CTRL0, on ?
|
|
|
|
OV5640_REG_SYS_CTRL0_SW_PWUP :
|
|
|
|
OV5640_REG_SYS_CTRL0_SW_PWDN);
|
2018-01-03 17:57:31 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ov5640_set_stream_mipi(struct ov5640_dev *sensor, bool on)
|
2017-06-08 02:33:56 +08:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
media: ov5640: Re-work MIPI startup sequence
Rework the MIPI interface startup sequence with the following changes:
- Remove MIPI bus initialization from the initial settings blob
- At set_power(1) time power up MIPI Tx/Rx and set data and clock lanes in
LP11 during 'sleep' and 'idle' with MIPI clock in non-continuous mode.
- At s_stream time enable/disable the MIPI interface output.
- Restore default settings at set_power(0) time.
Before this commit the sensor MIPI interface was initialized with settings
that require a start/stop sequence at power-up time in order to force lanes
into LP11 state, as they were initialized in LP00 when in 'sleep mode',
which is assumed to be the sensor manual definition for the D-PHY defined
stop mode.
The stream start/stop was performed by enabling disabling clock gating,
and had the side effect to change the lanes sleep mode configuration when
stream was stopped.
Clock gating/ungating:
- ret = ov5640_mod_reg(sensor, OV5640_REG_MIPI_CTRL00, BIT(5),
- on ? 0 : BIT(5));
- if (ret)
Set lanes in LP11 when in 'sleep mode':
- ret = ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT00,
- on ? 0x00 : 0x70);
This commit fixes an issue reported by Jagan Teki on i.MX6 platforms that
prevents the host interface from powering up correctly:
https://lkml.org/lkml/2018/6/1/38
It also improves MIPI capture operations stability on my testing platform
where MIPI capture often failed and returned all-purple frames.
Fixes: f22996db44e2 ("media: ov5640: add support of DVP parallel interface")
Tested-by: Steve Longerbeam <slongerbeam@gmail.com> (i.MX6q SabreSD, CSI-2)
Tested-by: Loic Poulain <loic.poulain@linaro.org> (Dragonboard-410c, CSI-2)
Reported-by: Jagan Teki <jagan@amarulasolutions.com>
Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
2018-07-06 17:51:52 +08:00
|
|
|
/*
|
|
|
|
* Enable/disable the MIPI interface
|
|
|
|
*
|
|
|
|
* 0x300e = on ? 0x45 : 0x40
|
|
|
|
*
|
|
|
|
* FIXME: the sensor manual (version 2.03) reports
|
|
|
|
* [7:5] = 000 : 1 data lane mode
|
|
|
|
* [7:5] = 001 : 2 data lanes mode
|
|
|
|
* But this settings do not work, while the following ones
|
|
|
|
* have been validated for 2 data lanes mode.
|
|
|
|
*
|
|
|
|
* [7:5] = 010 : 2 data lanes mode
|
|
|
|
* [4] = 0 : Power up MIPI HS Tx
|
|
|
|
* [3] = 0 : Power up MIPI LS Rx
|
|
|
|
* [2] = 1/0 : MIPI interface enable/disable
|
|
|
|
* [1:0] = 01/00: FIXME: 'debug'
|
|
|
|
*/
|
|
|
|
ret = ov5640_write_reg(sensor, OV5640_REG_IO_MIPI_CTRL00,
|
|
|
|
on ? 0x45 : 0x40);
|
2017-06-08 02:33:56 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return ov5640_write_reg(sensor, OV5640_REG_FRAME_CTRL01,
|
|
|
|
on ? 0x00 : 0x0f);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ov5640_get_sysclk(struct ov5640_dev *sensor)
|
|
|
|
{
|
|
|
|
/* calculate sysclk */
|
|
|
|
u32 xvclk = sensor->xclk_freq / 10000;
|
|
|
|
u32 multiplier, prediv, VCO, sysdiv, pll_rdiv;
|
|
|
|
u32 sclk_rdiv_map[] = {1, 2, 4, 8};
|
|
|
|
u32 bit_div2x = 1, sclk_rdiv, sysclk;
|
|
|
|
u8 temp1, temp2;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = ov5640_read_reg(sensor, OV5640_REG_SC_PLL_CTRL0, &temp1);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
temp2 = temp1 & 0x0f;
|
|
|
|
if (temp2 == 8 || temp2 == 10)
|
|
|
|
bit_div2x = temp2 / 2;
|
|
|
|
|
|
|
|
ret = ov5640_read_reg(sensor, OV5640_REG_SC_PLL_CTRL1, &temp1);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
sysdiv = temp1 >> 4;
|
|
|
|
if (sysdiv == 0)
|
|
|
|
sysdiv = 16;
|
|
|
|
|
|
|
|
ret = ov5640_read_reg(sensor, OV5640_REG_SC_PLL_CTRL2, &temp1);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
multiplier = temp1;
|
|
|
|
|
|
|
|
ret = ov5640_read_reg(sensor, OV5640_REG_SC_PLL_CTRL3, &temp1);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
prediv = temp1 & 0x0f;
|
|
|
|
pll_rdiv = ((temp1 >> 4) & 0x01) + 1;
|
|
|
|
|
|
|
|
ret = ov5640_read_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, &temp1);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
temp2 = temp1 & 0x03;
|
|
|
|
sclk_rdiv = sclk_rdiv_map[temp2];
|
|
|
|
|
|
|
|
if (!prediv || !sysdiv || !pll_rdiv || !bit_div2x)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
VCO = xvclk * multiplier / prediv;
|
|
|
|
|
|
|
|
sysclk = VCO / sysdiv / pll_rdiv * 2 / bit_div2x / sclk_rdiv;
|
|
|
|
|
|
|
|
return sysclk;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ov5640_set_night_mode(struct ov5640_dev *sensor)
|
|
|
|
{
|
|
|
|
/* read HTS from register settings */
|
|
|
|
u8 mode;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = ov5640_read_reg(sensor, OV5640_REG_AEC_CTRL00, &mode);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
mode &= 0xfb;
|
|
|
|
return ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL00, mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ov5640_get_hts(struct ov5640_dev *sensor)
|
|
|
|
{
|
|
|
|
/* read HTS from register settings */
|
|
|
|
u16 hts;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = ov5640_read_reg16(sensor, OV5640_REG_TIMING_HTS, &hts);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
return hts;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ov5640_get_vts(struct ov5640_dev *sensor)
|
|
|
|
{
|
|
|
|
u16 vts;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = ov5640_read_reg16(sensor, OV5640_REG_TIMING_VTS, &vts);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
return vts;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ov5640_set_vts(struct ov5640_dev *sensor, int vts)
|
|
|
|
{
|
|
|
|
return ov5640_write_reg16(sensor, OV5640_REG_TIMING_VTS, vts);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ov5640_get_light_freq(struct ov5640_dev *sensor)
|
|
|
|
{
|
|
|
|
/* get banding filter value */
|
|
|
|
int ret, light_freq = 0;
|
|
|
|
u8 temp, temp1;
|
|
|
|
|
|
|
|
ret = ov5640_read_reg(sensor, OV5640_REG_HZ5060_CTRL01, &temp);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
if (temp & 0x80) {
|
|
|
|
/* manual */
|
|
|
|
ret = ov5640_read_reg(sensor, OV5640_REG_HZ5060_CTRL00,
|
|
|
|
&temp1);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
if (temp1 & 0x04) {
|
|
|
|
/* 50Hz */
|
|
|
|
light_freq = 50;
|
|
|
|
} else {
|
|
|
|
/* 60Hz */
|
|
|
|
light_freq = 60;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* auto */
|
|
|
|
ret = ov5640_read_reg(sensor, OV5640_REG_SIGMADELTA_CTRL0C,
|
|
|
|
&temp1);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
if (temp1 & 0x01) {
|
|
|
|
/* 50Hz */
|
|
|
|
light_freq = 50;
|
|
|
|
} else {
|
|
|
|
/* 60Hz */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return light_freq;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ov5640_set_bandingfilter(struct ov5640_dev *sensor)
|
|
|
|
{
|
|
|
|
u32 band_step60, max_band60, band_step50, max_band50, prev_vts;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* read preview PCLK */
|
|
|
|
ret = ov5640_get_sysclk(sensor);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
if (ret == 0)
|
|
|
|
return -EINVAL;
|
|
|
|
sensor->prev_sysclk = ret;
|
|
|
|
/* read preview HTS */
|
|
|
|
ret = ov5640_get_hts(sensor);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
if (ret == 0)
|
|
|
|
return -EINVAL;
|
|
|
|
sensor->prev_hts = ret;
|
|
|
|
|
|
|
|
/* read preview VTS */
|
|
|
|
ret = ov5640_get_vts(sensor);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
prev_vts = ret;
|
|
|
|
|
|
|
|
/* calculate banding filter */
|
|
|
|
/* 60Hz */
|
|
|
|
band_step60 = sensor->prev_sysclk * 100 / sensor->prev_hts * 100 / 120;
|
|
|
|
ret = ov5640_write_reg16(sensor, OV5640_REG_AEC_B60_STEP, band_step60);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
if (!band_step60)
|
|
|
|
return -EINVAL;
|
|
|
|
max_band60 = (int)((prev_vts - 4) / band_step60);
|
|
|
|
ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL0D, max_band60);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* 50Hz */
|
|
|
|
band_step50 = sensor->prev_sysclk * 100 / sensor->prev_hts;
|
|
|
|
ret = ov5640_write_reg16(sensor, OV5640_REG_AEC_B50_STEP, band_step50);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
if (!band_step50)
|
|
|
|
return -EINVAL;
|
|
|
|
max_band50 = (int)((prev_vts - 4) / band_step50);
|
|
|
|
return ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL0E, max_band50);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ov5640_set_ae_target(struct ov5640_dev *sensor, int target)
|
|
|
|
{
|
|
|
|
/* stable in high */
|
|
|
|
u32 fast_high, fast_low;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
sensor->ae_low = target * 23 / 25; /* 0.92 */
|
|
|
|
sensor->ae_high = target * 27 / 25; /* 1.08 */
|
|
|
|
|
|
|
|
fast_high = sensor->ae_high << 1;
|
|
|
|
if (fast_high > 255)
|
|
|
|
fast_high = 255;
|
|
|
|
|
|
|
|
fast_low = sensor->ae_low >> 1;
|
|
|
|
|
|
|
|
ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL0F, sensor->ae_high);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL10, sensor->ae_low);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL1B, sensor->ae_high);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL1E, sensor->ae_low);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL11, fast_high);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
return ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL1F, fast_low);
|
|
|
|
}
|
|
|
|
|
2018-09-11 21:48:19 +08:00
|
|
|
static int ov5640_get_binning(struct ov5640_dev *sensor)
|
2017-06-08 02:33:56 +08:00
|
|
|
{
|
|
|
|
u8 temp;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = ov5640_read_reg(sensor, OV5640_REG_TIMING_TC_REG21, &temp);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2018-09-11 21:48:19 +08:00
|
|
|
|
|
|
|
return temp & BIT(0);
|
2017-06-08 02:33:56 +08:00
|
|
|
}
|
|
|
|
|
2018-06-18 18:29:17 +08:00
|
|
|
static int ov5640_set_binning(struct ov5640_dev *sensor, bool enable)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* TIMING TC REG21:
|
|
|
|
* - [0]: Horizontal binning enable
|
|
|
|
*/
|
|
|
|
ret = ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG21,
|
|
|
|
BIT(0), enable ? BIT(0) : 0);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
/*
|
|
|
|
* TIMING TC REG20:
|
|
|
|
* - [0]: Undocumented, but hardcoded init sequences
|
|
|
|
* are always setting REG21/REG20 bit 0 to same value...
|
|
|
|
*/
|
|
|
|
return ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG20,
|
|
|
|
BIT(0), enable ? BIT(0) : 0);
|
|
|
|
}
|
|
|
|
|
2017-06-08 02:33:56 +08:00
|
|
|
static int ov5640_set_virtual_channel(struct ov5640_dev *sensor)
|
|
|
|
{
|
2018-02-06 21:24:09 +08:00
|
|
|
struct i2c_client *client = sensor->i2c_client;
|
2017-06-08 02:33:56 +08:00
|
|
|
u8 temp, channel = virtual_channel;
|
|
|
|
int ret;
|
|
|
|
|
2018-02-06 21:24:09 +08:00
|
|
|
if (channel > 3) {
|
|
|
|
dev_err(&client->dev,
|
|
|
|
"%s: wrong virtual_channel parameter, expected (0..3), got %d\n",
|
|
|
|
__func__, channel);
|
2017-06-08 02:33:56 +08:00
|
|
|
return -EINVAL;
|
2018-02-06 21:24:09 +08:00
|
|
|
}
|
2017-06-08 02:33:56 +08:00
|
|
|
|
|
|
|
ret = ov5640_read_reg(sensor, OV5640_REG_DEBUG_MODE, &temp);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
temp &= ~(3 << 6);
|
|
|
|
temp |= (channel << 6);
|
|
|
|
return ov5640_write_reg(sensor, OV5640_REG_DEBUG_MODE, temp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct ov5640_mode_info *
|
2022-05-13 22:14:06 +08:00
|
|
|
ov5640_find_mode(struct ov5640_dev *sensor, int width, int height, bool nearest)
|
2017-06-08 02:33:56 +08:00
|
|
|
{
|
2018-06-20 16:40:57 +08:00
|
|
|
const struct ov5640_mode_info *mode;
|
2017-06-08 02:33:56 +08:00
|
|
|
|
2018-12-03 16:44:23 +08:00
|
|
|
mode = v4l2_find_nearest_size(ov5640_mode_data,
|
|
|
|
ARRAY_SIZE(ov5640_mode_data),
|
2022-05-13 22:13:58 +08:00
|
|
|
width, height, width, height);
|
2017-06-08 02:33:56 +08:00
|
|
|
|
2018-06-20 16:40:57 +08:00
|
|
|
if (!mode ||
|
2022-05-13 22:13:56 +08:00
|
|
|
(!nearest &&
|
2022-05-13 22:13:58 +08:00
|
|
|
(mode->width != width || mode->height != height)))
|
2018-06-20 16:40:57 +08:00
|
|
|
return NULL;
|
2017-06-08 02:33:56 +08:00
|
|
|
|
|
|
|
return mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* sensor changes between scaling and subsampling, go through
|
|
|
|
* exposure calculation
|
|
|
|
*/
|
2018-02-01 16:44:06 +08:00
|
|
|
static int ov5640_set_mode_exposure_calc(struct ov5640_dev *sensor,
|
|
|
|
const struct ov5640_mode_info *mode)
|
2017-06-08 02:33:56 +08:00
|
|
|
{
|
|
|
|
u32 prev_shutter, prev_gain16;
|
|
|
|
u32 cap_shutter, cap_gain16;
|
|
|
|
u32 cap_sysclk, cap_hts, cap_vts;
|
|
|
|
u32 light_freq, cap_bandfilt, cap_maxband;
|
|
|
|
u32 cap_gain16_shutter;
|
|
|
|
u8 average;
|
|
|
|
int ret;
|
|
|
|
|
2018-02-01 16:44:06 +08:00
|
|
|
if (!mode->reg_data)
|
2017-06-08 02:33:56 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* read preview shutter */
|
|
|
|
ret = ov5640_get_exposure(sensor);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
prev_shutter = ret;
|
2018-09-11 21:48:19 +08:00
|
|
|
ret = ov5640_get_binning(sensor);
|
2017-06-08 02:33:56 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
if (ret && mode->id != OV5640_MODE_720P_1280_720 &&
|
|
|
|
mode->id != OV5640_MODE_1080P_1920_1080)
|
|
|
|
prev_shutter *= 2;
|
|
|
|
|
|
|
|
/* read preview gain */
|
|
|
|
ret = ov5640_get_gain(sensor);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
prev_gain16 = ret;
|
|
|
|
|
|
|
|
/* get average */
|
|
|
|
ret = ov5640_read_reg(sensor, OV5640_REG_AVG_READOUT, &average);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* turn off night mode for capture */
|
|
|
|
ret = ov5640_set_night_mode(sensor);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* Write capture setting */
|
2022-05-13 22:14:02 +08:00
|
|
|
ov5640_load_regs(sensor, mode->reg_data, mode->reg_data_size);
|
|
|
|
ret = ov5640_set_timings(sensor, mode);
|
2017-06-08 02:33:56 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* read capture VTS */
|
|
|
|
ret = ov5640_get_vts(sensor);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
cap_vts = ret;
|
|
|
|
ret = ov5640_get_hts(sensor);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
if (ret == 0)
|
|
|
|
return -EINVAL;
|
|
|
|
cap_hts = ret;
|
|
|
|
|
|
|
|
ret = ov5640_get_sysclk(sensor);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
if (ret == 0)
|
|
|
|
return -EINVAL;
|
|
|
|
cap_sysclk = ret;
|
|
|
|
|
|
|
|
/* calculate capture banding filter */
|
|
|
|
ret = ov5640_get_light_freq(sensor);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
light_freq = ret;
|
|
|
|
|
|
|
|
if (light_freq == 60) {
|
|
|
|
/* 60Hz */
|
|
|
|
cap_bandfilt = cap_sysclk * 100 / cap_hts * 100 / 120;
|
|
|
|
} else {
|
|
|
|
/* 50Hz */
|
|
|
|
cap_bandfilt = cap_sysclk * 100 / cap_hts;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!sensor->prev_sysclk) {
|
|
|
|
ret = ov5640_get_sysclk(sensor);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
if (ret == 0)
|
|
|
|
return -EINVAL;
|
|
|
|
sensor->prev_sysclk = ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!cap_bandfilt)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
cap_maxband = (int)((cap_vts - 4) / cap_bandfilt);
|
|
|
|
|
|
|
|
/* calculate capture shutter/gain16 */
|
|
|
|
if (average > sensor->ae_low && average < sensor->ae_high) {
|
|
|
|
/* in stable range */
|
|
|
|
cap_gain16_shutter =
|
|
|
|
prev_gain16 * prev_shutter *
|
|
|
|
cap_sysclk / sensor->prev_sysclk *
|
|
|
|
sensor->prev_hts / cap_hts *
|
|
|
|
sensor->ae_target / average;
|
|
|
|
} else {
|
|
|
|
cap_gain16_shutter =
|
|
|
|
prev_gain16 * prev_shutter *
|
|
|
|
cap_sysclk / sensor->prev_sysclk *
|
|
|
|
sensor->prev_hts / cap_hts;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* gain to shutter */
|
|
|
|
if (cap_gain16_shutter < (cap_bandfilt * 16)) {
|
|
|
|
/* shutter < 1/100 */
|
|
|
|
cap_shutter = cap_gain16_shutter / 16;
|
|
|
|
if (cap_shutter < 1)
|
|
|
|
cap_shutter = 1;
|
|
|
|
|
|
|
|
cap_gain16 = cap_gain16_shutter / cap_shutter;
|
|
|
|
if (cap_gain16 < 16)
|
|
|
|
cap_gain16 = 16;
|
|
|
|
} else {
|
|
|
|
if (cap_gain16_shutter > (cap_bandfilt * cap_maxband * 16)) {
|
|
|
|
/* exposure reach max */
|
|
|
|
cap_shutter = cap_bandfilt * cap_maxband;
|
|
|
|
if (!cap_shutter)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
cap_gain16 = cap_gain16_shutter / cap_shutter;
|
|
|
|
} else {
|
|
|
|
/* 1/100 < (cap_shutter = n/100) =< max */
|
|
|
|
cap_shutter =
|
|
|
|
((int)(cap_gain16_shutter / 16 / cap_bandfilt))
|
|
|
|
* cap_bandfilt;
|
|
|
|
if (!cap_shutter)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
cap_gain16 = cap_gain16_shutter / cap_shutter;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set capture gain */
|
2018-09-11 21:48:18 +08:00
|
|
|
ret = ov5640_set_gain(sensor, cap_gain16);
|
2017-06-08 02:33:56 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* write capture shutter */
|
|
|
|
if (cap_shutter > (cap_vts - 4)) {
|
|
|
|
cap_vts = cap_shutter + 4;
|
|
|
|
ret = ov5640_set_vts(sensor, cap_vts);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set exposure */
|
2018-09-11 21:48:18 +08:00
|
|
|
return ov5640_set_exposure(sensor, cap_shutter);
|
2017-06-08 02:33:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* if sensor changes inside scaling or subsampling
|
|
|
|
* change mode directly
|
|
|
|
*/
|
|
|
|
static int ov5640_set_mode_direct(struct ov5640_dev *sensor,
|
2018-09-11 21:48:18 +08:00
|
|
|
const struct ov5640_mode_info *mode)
|
2017-06-08 02:33:56 +08:00
|
|
|
{
|
2018-02-01 16:44:06 +08:00
|
|
|
if (!mode->reg_data)
|
2017-06-08 02:33:56 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* Write capture setting */
|
2022-05-13 22:14:02 +08:00
|
|
|
ov5640_load_regs(sensor, mode->reg_data, mode->reg_data_size);
|
|
|
|
return ov5640_set_timings(sensor, mode);
|
2017-06-08 02:33:56 +08:00
|
|
|
}
|
|
|
|
|
2018-09-11 21:48:21 +08:00
|
|
|
static int ov5640_set_mode(struct ov5640_dev *sensor)
|
2017-06-08 02:33:56 +08:00
|
|
|
{
|
|
|
|
const struct ov5640_mode_info *mode = sensor->current_mode;
|
2018-09-11 21:48:21 +08:00
|
|
|
const struct ov5640_mode_info *orig_mode = sensor->last_mode;
|
2017-06-08 02:33:56 +08:00
|
|
|
enum ov5640_downsize_mode dn_mode, orig_dn_mode;
|
2018-09-11 21:48:18 +08:00
|
|
|
bool auto_gain = sensor->ctrls.auto_gain->val == 1;
|
2018-09-11 21:48:17 +08:00
|
|
|
bool auto_exp = sensor->ctrls.auto_exp->val == V4L2_EXPOSURE_AUTO;
|
2017-06-08 02:33:56 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
dn_mode = mode->dn_mode;
|
|
|
|
orig_dn_mode = orig_mode->dn_mode;
|
|
|
|
|
|
|
|
/* auto gain and exposure must be turned off when changing modes */
|
2018-09-11 21:48:18 +08:00
|
|
|
if (auto_gain) {
|
|
|
|
ret = ov5640_set_autogain(sensor, false);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
2018-04-16 20:36:52 +08:00
|
|
|
|
2018-09-11 21:48:18 +08:00
|
|
|
if (auto_exp) {
|
|
|
|
ret = ov5640_set_autoexposure(sensor, false);
|
|
|
|
if (ret)
|
|
|
|
goto restore_auto_gain;
|
|
|
|
}
|
2017-06-08 02:33:56 +08:00
|
|
|
|
2022-05-13 22:13:55 +08:00
|
|
|
if (ov5640_is_csi2(sensor))
|
|
|
|
ret = ov5640_set_mipi_pclk(sensor);
|
|
|
|
else
|
|
|
|
ret = ov5640_set_dvp_pclk(sensor);
|
2018-12-03 16:44:17 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return 0;
|
|
|
|
|
2017-06-08 02:33:56 +08:00
|
|
|
if ((dn_mode == SUBSAMPLING && orig_dn_mode == SCALING) ||
|
|
|
|
(dn_mode == SCALING && orig_dn_mode == SUBSAMPLING)) {
|
|
|
|
/*
|
|
|
|
* change between subsampling and scaling
|
2018-09-11 21:48:18 +08:00
|
|
|
* go through exposure calculation
|
2017-06-08 02:33:56 +08:00
|
|
|
*/
|
|
|
|
ret = ov5640_set_mode_exposure_calc(sensor, mode);
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* change inside subsampling or scaling
|
|
|
|
* download firmware directly
|
|
|
|
*/
|
2018-09-11 21:48:18 +08:00
|
|
|
ret = ov5640_set_mode_direct(sensor, mode);
|
2017-06-08 02:33:56 +08:00
|
|
|
}
|
|
|
|
if (ret < 0)
|
2018-09-11 21:48:18 +08:00
|
|
|
goto restore_auto_exp_gain;
|
|
|
|
|
|
|
|
/* restore auto gain and exposure */
|
|
|
|
if (auto_gain)
|
|
|
|
ov5640_set_autogain(sensor, true);
|
|
|
|
if (auto_exp)
|
|
|
|
ov5640_set_autoexposure(sensor, true);
|
2017-06-08 02:33:56 +08:00
|
|
|
|
2018-06-18 18:29:17 +08:00
|
|
|
ret = ov5640_set_binning(sensor, dn_mode != SCALING);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
2017-06-08 02:33:56 +08:00
|
|
|
ret = ov5640_set_ae_target(sensor, sensor->ae_target);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
ret = ov5640_get_light_freq(sensor);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
ret = ov5640_set_bandingfilter(sensor);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
ret = ov5640_set_virtual_channel(sensor);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
sensor->pending_mode_change = false;
|
2018-09-11 21:48:21 +08:00
|
|
|
sensor->last_mode = mode;
|
2017-06-08 02:33:56 +08:00
|
|
|
|
|
|
|
return 0;
|
2018-09-11 21:48:18 +08:00
|
|
|
|
|
|
|
restore_auto_exp_gain:
|
|
|
|
if (auto_exp)
|
|
|
|
ov5640_set_autoexposure(sensor, true);
|
|
|
|
restore_auto_gain:
|
|
|
|
if (auto_gain)
|
|
|
|
ov5640_set_autogain(sensor, true);
|
|
|
|
|
|
|
|
return ret;
|
2017-06-08 02:33:56 +08:00
|
|
|
}
|
|
|
|
|
2018-03-11 23:34:41 +08:00
|
|
|
static int ov5640_set_framefmt(struct ov5640_dev *sensor,
|
|
|
|
struct v4l2_mbus_framefmt *format);
|
|
|
|
|
2017-06-08 02:33:56 +08:00
|
|
|
/* restore the last set video mode after chip power-on */
|
|
|
|
static int ov5640_restore_mode(struct ov5640_dev *sensor)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* first load the initial register values */
|
2022-05-13 22:14:02 +08:00
|
|
|
ov5640_load_regs(sensor, ov5640_init_setting,
|
|
|
|
ARRAY_SIZE(ov5640_init_setting));
|
2017-06-08 02:33:56 +08:00
|
|
|
|
2018-04-16 20:36:53 +08:00
|
|
|
ret = ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, 0x3f,
|
2018-12-03 16:44:19 +08:00
|
|
|
(ilog2(OV5640_SCLK2X_ROOT_DIV) << 2) |
|
|
|
|
ilog2(OV5640_SCLK_ROOT_DIV));
|
2018-04-16 20:36:53 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2017-06-08 02:33:56 +08:00
|
|
|
/* now restore the last capture mode */
|
2018-09-11 21:48:21 +08:00
|
|
|
ret = ov5640_set_mode(sensor);
|
2018-03-11 23:34:41 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return ov5640_set_framefmt(sensor, &sensor->fmt);
|
2017-06-08 02:33:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ov5640_power(struct ov5640_dev *sensor, bool enable)
|
|
|
|
{
|
2018-01-03 17:57:28 +08:00
|
|
|
gpiod_set_value_cansleep(sensor->pwdn_gpio, enable ? 0 : 1);
|
2017-06-08 02:33:56 +08:00
|
|
|
}
|
|
|
|
|
2023-01-03 20:27:36 +08:00
|
|
|
/*
|
|
|
|
* From section 2.7 power up sequence:
|
|
|
|
* t0 + t1 + t2 >= 5ms Delay from DOVDD stable to PWDN pull down
|
|
|
|
* t3 >= 1ms Delay from PWDN pull down to RESETB pull up
|
|
|
|
* t4 >= 20ms Delay from RESETB pull up to SCCB (i2c) stable
|
|
|
|
*
|
|
|
|
* Some modules don't expose RESETB/PWDN pins directly, instead providing a
|
|
|
|
* "PWUP" GPIO which is wired through appropriate delays and inverters to the
|
|
|
|
* pins.
|
|
|
|
*
|
|
|
|
* In such cases, this gpio should be mapped to pwdn_gpio in the driver, and we
|
|
|
|
* should still toggle the pwdn_gpio below with the appropriate delays, while
|
|
|
|
* the calls to reset_gpio will be ignored.
|
|
|
|
*/
|
|
|
|
static void ov5640_powerup_sequence(struct ov5640_dev *sensor)
|
2017-06-08 02:33:56 +08:00
|
|
|
{
|
2023-01-03 20:27:35 +08:00
|
|
|
if (sensor->pwdn_gpio) {
|
2023-07-25 06:21:16 +08:00
|
|
|
gpiod_set_value_cansleep(sensor->reset_gpio, 1);
|
2017-06-08 02:33:56 +08:00
|
|
|
|
2023-01-03 20:27:35 +08:00
|
|
|
/* camera power cycle */
|
|
|
|
ov5640_power(sensor, false);
|
2023-07-25 06:21:16 +08:00
|
|
|
usleep_range(5000, 10000); /* t2 */
|
2023-01-03 20:27:35 +08:00
|
|
|
ov5640_power(sensor, true);
|
2023-07-25 06:21:16 +08:00
|
|
|
usleep_range(1000, 2000); /* t3 */
|
2017-06-08 02:33:56 +08:00
|
|
|
|
2023-01-03 20:27:35 +08:00
|
|
|
gpiod_set_value_cansleep(sensor->reset_gpio, 0);
|
|
|
|
} else {
|
|
|
|
/* software reset */
|
|
|
|
ov5640_write_reg(sensor, OV5640_REG_SYS_CTRL0,
|
|
|
|
OV5640_REG_SYS_CTRL0_SW_RST);
|
|
|
|
}
|
2023-07-25 06:21:16 +08:00
|
|
|
usleep_range(20000, 25000); /* t4 */
|
2023-01-03 20:27:35 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* software standby: allows registers programming;
|
|
|
|
* exit at restore_mode() for CSI, s_stream(1) for DVP
|
|
|
|
*/
|
|
|
|
ov5640_write_reg(sensor, OV5640_REG_SYS_CTRL0,
|
|
|
|
OV5640_REG_SYS_CTRL0_SW_PWDN);
|
2017-06-08 02:33:56 +08:00
|
|
|
}
|
|
|
|
|
2018-01-03 17:57:29 +08:00
|
|
|
static int ov5640_set_power_on(struct ov5640_dev *sensor)
|
2017-06-08 02:33:56 +08:00
|
|
|
{
|
2018-01-03 17:57:29 +08:00
|
|
|
struct i2c_client *client = sensor->i2c_client;
|
|
|
|
int ret;
|
2017-06-08 02:33:56 +08:00
|
|
|
|
2018-01-03 17:57:29 +08:00
|
|
|
ret = clk_prepare_enable(sensor->xclk);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(&client->dev, "%s: failed to enable clock\n",
|
|
|
|
__func__);
|
|
|
|
return ret;
|
|
|
|
}
|
2017-06-08 02:33:56 +08:00
|
|
|
|
2018-01-03 17:57:29 +08:00
|
|
|
ret = regulator_bulk_enable(OV5640_NUM_SUPPLIES,
|
|
|
|
sensor->supplies);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(&client->dev, "%s: failed to enable regulators\n",
|
|
|
|
__func__);
|
|
|
|
goto xclk_off;
|
|
|
|
}
|
|
|
|
|
2023-01-03 20:27:36 +08:00
|
|
|
ov5640_powerup_sequence(sensor);
|
2018-01-03 17:57:29 +08:00
|
|
|
|
|
|
|
ret = ov5640_init_slave_id(sensor);
|
|
|
|
if (ret)
|
|
|
|
goto power_off;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
power_off:
|
|
|
|
ov5640_power(sensor, false);
|
|
|
|
regulator_bulk_disable(OV5640_NUM_SUPPLIES, sensor->supplies);
|
|
|
|
xclk_off:
|
|
|
|
clk_disable_unprepare(sensor->xclk);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ov5640_set_power_off(struct ov5640_dev *sensor)
|
|
|
|
{
|
|
|
|
ov5640_power(sensor, false);
|
|
|
|
regulator_bulk_disable(OV5640_NUM_SUPPLIES, sensor->supplies);
|
|
|
|
clk_disable_unprepare(sensor->xclk);
|
|
|
|
}
|
2017-06-08 02:33:56 +08:00
|
|
|
|
2020-09-05 04:18:31 +08:00
|
|
|
static int ov5640_set_power_mipi(struct ov5640_dev *sensor, bool on)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!on) {
|
|
|
|
/* Reset MIPI bus settings to their default values. */
|
|
|
|
ov5640_write_reg(sensor, OV5640_REG_IO_MIPI_CTRL00, 0x58);
|
|
|
|
ov5640_write_reg(sensor, OV5640_REG_MIPI_CTRL00, 0x04);
|
|
|
|
ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT00, 0x00);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Power up MIPI HS Tx and LS Rx; 2 data lanes mode
|
|
|
|
*
|
|
|
|
* 0x300e = 0x40
|
|
|
|
* [7:5] = 010 : 2 data lanes mode (see FIXME note in
|
|
|
|
* "ov5640_set_stream_mipi()")
|
|
|
|
* [4] = 0 : Power up MIPI HS Tx
|
|
|
|
* [3] = 0 : Power up MIPI LS Rx
|
2023-08-02 22:47:25 +08:00
|
|
|
* [2] = 1 : MIPI interface enabled
|
2020-09-05 04:18:31 +08:00
|
|
|
*/
|
2023-08-02 22:47:25 +08:00
|
|
|
ret = ov5640_write_reg(sensor, OV5640_REG_IO_MIPI_CTRL00, 0x44);
|
2020-09-05 04:18:31 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Gate clock and set LP11 in 'no packets mode' (idle)
|
|
|
|
*
|
|
|
|
* 0x4800 = 0x24
|
|
|
|
* [5] = 1 : Gate clock when 'no packets'
|
|
|
|
* [2] = 1 : MIPI bus in LP11 when 'no packets'
|
|
|
|
*/
|
|
|
|
ret = ov5640_write_reg(sensor, OV5640_REG_MIPI_CTRL00, 0x24);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set data lanes and clock in LP11 when 'sleeping'
|
|
|
|
*
|
|
|
|
* 0x3019 = 0x70
|
|
|
|
* [6] = 1 : MIPI data lane 2 in LP11 when 'sleeping'
|
|
|
|
* [5] = 1 : MIPI data lane 1 in LP11 when 'sleeping'
|
|
|
|
* [4] = 1 : MIPI clock lane in LP11 when 'sleeping'
|
|
|
|
*/
|
|
|
|
ret = ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT00, 0x70);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* Give lanes some time to coax into LP11 state. */
|
|
|
|
usleep_range(500, 1000);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-09-05 04:18:32 +08:00
|
|
|
static int ov5640_set_power_dvp(struct ov5640_dev *sensor, bool on)
|
|
|
|
{
|
2020-09-05 04:18:33 +08:00
|
|
|
unsigned int flags = sensor->ep.bus.parallel.flags;
|
2020-10-13 17:02:23 +08:00
|
|
|
bool bt656 = sensor->ep.bus_type == V4L2_MBUS_BT656;
|
|
|
|
u8 polarities = 0;
|
2020-09-05 04:18:32 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!on) {
|
|
|
|
/* Reset settings to their default values. */
|
2020-10-13 17:02:23 +08:00
|
|
|
ov5640_write_reg(sensor, OV5640_REG_CCIR656_CTRL00, 0x00);
|
2020-09-05 04:18:33 +08:00
|
|
|
ov5640_write_reg(sensor, OV5640_REG_IO_MIPI_CTRL00, 0x58);
|
|
|
|
ov5640_write_reg(sensor, OV5640_REG_POLARITY_CTRL00, 0x20);
|
2020-09-05 04:18:32 +08:00
|
|
|
ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT_ENABLE01, 0x00);
|
|
|
|
ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT_ENABLE02, 0x00);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-09-05 04:18:33 +08:00
|
|
|
/*
|
|
|
|
* Note about parallel port configuration.
|
|
|
|
*
|
|
|
|
* When configured in parallel mode, the OV5640 will
|
|
|
|
* output 10 bits data on DVP data lines [9:0].
|
|
|
|
* If only 8 bits data are wanted, the 8 bits data lines
|
|
|
|
* of the camera interface must be physically connected
|
|
|
|
* on the DVP data lines [9:2].
|
|
|
|
*
|
|
|
|
* Control lines polarity can be configured through
|
|
|
|
* devicetree endpoint control lines properties.
|
|
|
|
* If no endpoint control lines properties are set,
|
|
|
|
* polarity will be as below:
|
|
|
|
* - VSYNC: active high
|
|
|
|
* - HREF: active low
|
|
|
|
* - PCLK: active low
|
2020-10-13 17:02:23 +08:00
|
|
|
*
|
|
|
|
* VSYNC & HREF are not configured if BT656 bus mode is selected
|
2020-09-05 04:18:33 +08:00
|
|
|
*/
|
2020-10-13 17:02:23 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* BT656 embedded synchronization configuration
|
|
|
|
*
|
|
|
|
* CCIR656 CTRL00
|
|
|
|
* - [7]: SYNC code selection (0: auto generate sync code,
|
|
|
|
* 1: sync code from regs 0x4732-0x4735)
|
|
|
|
* - [6]: f value in CCIR656 SYNC code when fixed f value
|
|
|
|
* - [5]: Fixed f value
|
|
|
|
* - [4:3]: Blank toggle data options (00: data=1'h040/1'h200,
|
|
|
|
* 01: data from regs 0x4736-0x4738, 10: always keep 0)
|
|
|
|
* - [1]: Clip data disable
|
|
|
|
* - [0]: CCIR656 mode enable
|
|
|
|
*
|
|
|
|
* Default CCIR656 SAV/EAV mode with default codes
|
|
|
|
* SAV=0xff000080 & EAV=0xff00009d is enabled here with settings:
|
|
|
|
* - CCIR656 mode enable
|
|
|
|
* - auto generation of sync codes
|
|
|
|
* - blank toggle data 1'h040/1'h200
|
|
|
|
* - clip reserved data (0x00 & 0xff changed to 0x01 & 0xfe)
|
|
|
|
*/
|
|
|
|
ret = ov5640_write_reg(sensor, OV5640_REG_CCIR656_CTRL00,
|
|
|
|
bt656 ? 0x01 : 0x00);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2020-09-05 04:18:33 +08:00
|
|
|
/*
|
|
|
|
* configure parallel port control lines polarity
|
|
|
|
*
|
|
|
|
* POLARITY CTRL0
|
|
|
|
* - [5]: PCLK polarity (0: active low, 1: active high)
|
|
|
|
* - [1]: HREF polarity (0: active low, 1: active high)
|
|
|
|
* - [0]: VSYNC polarity (mismatch here between
|
|
|
|
* datasheet and hardware, 0 is active high
|
|
|
|
* and 1 is active low...)
|
|
|
|
*/
|
2020-10-13 17:02:23 +08:00
|
|
|
if (!bt656) {
|
2020-09-05 04:18:34 +08:00
|
|
|
if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
|
2020-10-13 17:02:23 +08:00
|
|
|
polarities |= BIT(1);
|
2020-09-05 04:18:34 +08:00
|
|
|
if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
|
2020-10-13 17:02:23 +08:00
|
|
|
polarities |= BIT(0);
|
2020-09-05 04:18:34 +08:00
|
|
|
}
|
2020-10-13 17:02:23 +08:00
|
|
|
if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
|
|
|
|
polarities |= BIT(5);
|
|
|
|
|
|
|
|
ret = ov5640_write_reg(sensor, OV5640_REG_POLARITY_CTRL00, polarities);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2020-09-05 04:18:33 +08:00
|
|
|
|
|
|
|
/*
|
2020-10-13 17:02:23 +08:00
|
|
|
* powerdown MIPI TX/RX PHY & enable DVP
|
2020-09-05 04:18:33 +08:00
|
|
|
*
|
|
|
|
* MIPI CONTROL 00
|
2020-10-13 17:02:23 +08:00
|
|
|
* [4] = 1 : Power down MIPI HS Tx
|
|
|
|
* [3] = 1 : Power down MIPI LS Rx
|
|
|
|
* [2] = 0 : DVP enable (MIPI disable)
|
2020-09-05 04:18:33 +08:00
|
|
|
*/
|
|
|
|
ret = ov5640_write_reg(sensor, OV5640_REG_IO_MIPI_CTRL00, 0x18);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2020-09-05 04:18:32 +08:00
|
|
|
/*
|
|
|
|
* enable VSYNC/HREF/PCLK DVP control lines
|
|
|
|
* & D[9:6] DVP data lines
|
|
|
|
*
|
|
|
|
* PAD OUTPUT ENABLE 01
|
|
|
|
* - 6: VSYNC output enable
|
|
|
|
* - 5: HREF output enable
|
|
|
|
* - 4: PCLK output enable
|
|
|
|
* - [3:0]: D[9:6] output enable
|
|
|
|
*/
|
2020-09-05 04:18:34 +08:00
|
|
|
ret = ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT_ENABLE01,
|
2020-10-13 17:02:23 +08:00
|
|
|
bt656 ? 0x1f : 0x7f);
|
2020-09-05 04:18:32 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* enable D[5:0] DVP data lines
|
|
|
|
*
|
|
|
|
* PAD OUTPUT ENABLE 02
|
|
|
|
* - [7:2]: D[5:0] output enable
|
|
|
|
*/
|
|
|
|
return ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT_ENABLE02, 0xfc);
|
|
|
|
}
|
|
|
|
|
2018-01-03 17:57:29 +08:00
|
|
|
static int ov5640_set_power(struct ov5640_dev *sensor, bool on)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
2017-06-08 02:33:56 +08:00
|
|
|
|
2018-01-03 17:57:29 +08:00
|
|
|
if (on) {
|
|
|
|
ret = ov5640_set_power_on(sensor);
|
2017-06-08 02:33:56 +08:00
|
|
|
if (ret)
|
2018-01-03 17:57:29 +08:00
|
|
|
return ret;
|
2017-06-08 02:33:56 +08:00
|
|
|
|
|
|
|
ret = ov5640_restore_mode(sensor);
|
|
|
|
if (ret)
|
|
|
|
goto power_off;
|
2020-09-05 04:18:31 +08:00
|
|
|
}
|
2017-06-08 02:33:56 +08:00
|
|
|
|
2020-09-05 04:18:32 +08:00
|
|
|
if (sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY)
|
2020-09-05 04:18:31 +08:00
|
|
|
ret = ov5640_set_power_mipi(sensor, on);
|
2020-09-05 04:18:32 +08:00
|
|
|
else
|
|
|
|
ret = ov5640_set_power_dvp(sensor, on);
|
|
|
|
if (ret)
|
|
|
|
goto power_off;
|
media: ov5640: Re-work MIPI startup sequence
Rework the MIPI interface startup sequence with the following changes:
- Remove MIPI bus initialization from the initial settings blob
- At set_power(1) time power up MIPI Tx/Rx and set data and clock lanes in
LP11 during 'sleep' and 'idle' with MIPI clock in non-continuous mode.
- At s_stream time enable/disable the MIPI interface output.
- Restore default settings at set_power(0) time.
Before this commit the sensor MIPI interface was initialized with settings
that require a start/stop sequence at power-up time in order to force lanes
into LP11 state, as they were initialized in LP00 when in 'sleep mode',
which is assumed to be the sensor manual definition for the D-PHY defined
stop mode.
The stream start/stop was performed by enabling disabling clock gating,
and had the side effect to change the lanes sleep mode configuration when
stream was stopped.
Clock gating/ungating:
- ret = ov5640_mod_reg(sensor, OV5640_REG_MIPI_CTRL00, BIT(5),
- on ? 0 : BIT(5));
- if (ret)
Set lanes in LP11 when in 'sleep mode':
- ret = ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT00,
- on ? 0x00 : 0x70);
This commit fixes an issue reported by Jagan Teki on i.MX6 platforms that
prevents the host interface from powering up correctly:
https://lkml.org/lkml/2018/6/1/38
It also improves MIPI capture operations stability on my testing platform
where MIPI capture often failed and returned all-purple frames.
Fixes: f22996db44e2 ("media: ov5640: add support of DVP parallel interface")
Tested-by: Steve Longerbeam <slongerbeam@gmail.com> (i.MX6q SabreSD, CSI-2)
Tested-by: Loic Poulain <loic.poulain@linaro.org> (Dragonboard-410c, CSI-2)
Reported-by: Jagan Teki <jagan@amarulasolutions.com>
Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
2018-07-06 17:51:52 +08:00
|
|
|
|
2020-09-05 04:18:31 +08:00
|
|
|
if (!on)
|
media: ov5640: Re-work MIPI startup sequence
Rework the MIPI interface startup sequence with the following changes:
- Remove MIPI bus initialization from the initial settings blob
- At set_power(1) time power up MIPI Tx/Rx and set data and clock lanes in
LP11 during 'sleep' and 'idle' with MIPI clock in non-continuous mode.
- At s_stream time enable/disable the MIPI interface output.
- Restore default settings at set_power(0) time.
Before this commit the sensor MIPI interface was initialized with settings
that require a start/stop sequence at power-up time in order to force lanes
into LP11 state, as they were initialized in LP00 when in 'sleep mode',
which is assumed to be the sensor manual definition for the D-PHY defined
stop mode.
The stream start/stop was performed by enabling disabling clock gating,
and had the side effect to change the lanes sleep mode configuration when
stream was stopped.
Clock gating/ungating:
- ret = ov5640_mod_reg(sensor, OV5640_REG_MIPI_CTRL00, BIT(5),
- on ? 0 : BIT(5));
- if (ret)
Set lanes in LP11 when in 'sleep mode':
- ret = ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT00,
- on ? 0x00 : 0x70);
This commit fixes an issue reported by Jagan Teki on i.MX6 platforms that
prevents the host interface from powering up correctly:
https://lkml.org/lkml/2018/6/1/38
It also improves MIPI capture operations stability on my testing platform
where MIPI capture often failed and returned all-purple frames.
Fixes: f22996db44e2 ("media: ov5640: add support of DVP parallel interface")
Tested-by: Steve Longerbeam <slongerbeam@gmail.com> (i.MX6q SabreSD, CSI-2)
Tested-by: Loic Poulain <loic.poulain@linaro.org> (Dragonboard-410c, CSI-2)
Reported-by: Jagan Teki <jagan@amarulasolutions.com>
Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
2018-07-06 17:51:52 +08:00
|
|
|
ov5640_set_power_off(sensor);
|
2017-06-08 02:33:56 +08:00
|
|
|
|
media: ov5640: Re-work MIPI startup sequence
Rework the MIPI interface startup sequence with the following changes:
- Remove MIPI bus initialization from the initial settings blob
- At set_power(1) time power up MIPI Tx/Rx and set data and clock lanes in
LP11 during 'sleep' and 'idle' with MIPI clock in non-continuous mode.
- At s_stream time enable/disable the MIPI interface output.
- Restore default settings at set_power(0) time.
Before this commit the sensor MIPI interface was initialized with settings
that require a start/stop sequence at power-up time in order to force lanes
into LP11 state, as they were initialized in LP00 when in 'sleep mode',
which is assumed to be the sensor manual definition for the D-PHY defined
stop mode.
The stream start/stop was performed by enabling disabling clock gating,
and had the side effect to change the lanes sleep mode configuration when
stream was stopped.
Clock gating/ungating:
- ret = ov5640_mod_reg(sensor, OV5640_REG_MIPI_CTRL00, BIT(5),
- on ? 0 : BIT(5));
- if (ret)
Set lanes in LP11 when in 'sleep mode':
- ret = ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT00,
- on ? 0x00 : 0x70);
This commit fixes an issue reported by Jagan Teki on i.MX6 platforms that
prevents the host interface from powering up correctly:
https://lkml.org/lkml/2018/6/1/38
It also improves MIPI capture operations stability on my testing platform
where MIPI capture often failed and returned all-purple frames.
Fixes: f22996db44e2 ("media: ov5640: add support of DVP parallel interface")
Tested-by: Steve Longerbeam <slongerbeam@gmail.com> (i.MX6q SabreSD, CSI-2)
Tested-by: Loic Poulain <loic.poulain@linaro.org> (Dragonboard-410c, CSI-2)
Reported-by: Jagan Teki <jagan@amarulasolutions.com>
Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
2018-07-06 17:51:52 +08:00
|
|
|
return 0;
|
|
|
|
|
2017-06-08 02:33:56 +08:00
|
|
|
power_off:
|
2018-01-03 17:57:29 +08:00
|
|
|
ov5640_set_power_off(sensor);
|
2017-06-08 02:33:56 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2022-07-21 15:41:38 +08:00
|
|
|
static int ov5640_sensor_suspend(struct device *dev)
|
2017-06-08 02:33:56 +08:00
|
|
|
{
|
2022-07-21 15:41:38 +08:00
|
|
|
struct v4l2_subdev *sd = dev_get_drvdata(dev);
|
|
|
|
struct ov5640_dev *ov5640 = to_ov5640_dev(sd);
|
2017-06-08 02:33:56 +08:00
|
|
|
|
2022-07-21 15:41:38 +08:00
|
|
|
return ov5640_set_power(ov5640, false);
|
|
|
|
}
|
2017-06-08 02:33:56 +08:00
|
|
|
|
2022-07-21 15:41:38 +08:00
|
|
|
static int ov5640_sensor_resume(struct device *dev)
|
|
|
|
{
|
|
|
|
struct v4l2_subdev *sd = dev_get_drvdata(dev);
|
|
|
|
struct ov5640_dev *ov5640 = to_ov5640_dev(sd);
|
2017-06-08 02:33:56 +08:00
|
|
|
|
2022-07-21 15:41:38 +08:00
|
|
|
return ov5640_set_power(ov5640, true);
|
2017-06-08 02:33:56 +08:00
|
|
|
}
|
|
|
|
|
2022-07-21 15:41:38 +08:00
|
|
|
/* --------------- Subdev Operations --------------- */
|
|
|
|
|
2017-06-08 02:33:56 +08:00
|
|
|
static int ov5640_try_frame_interval(struct ov5640_dev *sensor,
|
|
|
|
struct v4l2_fract *fi,
|
2022-12-02 21:00:00 +08:00
|
|
|
const struct ov5640_mode_info *mode_info)
|
2017-06-08 02:33:56 +08:00
|
|
|
{
|
2022-12-02 21:00:00 +08:00
|
|
|
const struct ov5640_mode_info *mode = mode_info;
|
2019-01-25 01:58:01 +08:00
|
|
|
enum ov5640_frame_rate rate = OV5640_15_FPS;
|
2018-12-03 16:44:25 +08:00
|
|
|
int minfps, maxfps, best_fps, fps;
|
|
|
|
int i;
|
2017-06-08 02:33:56 +08:00
|
|
|
|
|
|
|
minfps = ov5640_framerates[OV5640_15_FPS];
|
2022-12-02 21:00:00 +08:00
|
|
|
maxfps = ov5640_framerates[mode->max_fps];
|
2017-06-08 02:33:56 +08:00
|
|
|
|
|
|
|
if (fi->numerator == 0) {
|
|
|
|
fi->denominator = maxfps;
|
|
|
|
fi->numerator = 1;
|
2022-12-02 21:00:00 +08:00
|
|
|
rate = mode->max_fps;
|
2018-12-03 16:44:26 +08:00
|
|
|
goto find_mode;
|
2017-06-08 02:33:56 +08:00
|
|
|
}
|
|
|
|
|
2018-12-03 16:44:25 +08:00
|
|
|
fps = clamp_val(DIV_ROUND_CLOSEST(fi->denominator, fi->numerator),
|
|
|
|
minfps, maxfps);
|
2017-06-08 02:33:56 +08:00
|
|
|
|
2018-12-03 16:44:25 +08:00
|
|
|
best_fps = minfps;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(ov5640_framerates); i++) {
|
|
|
|
int curr_fps = ov5640_framerates[i];
|
2017-06-08 02:33:56 +08:00
|
|
|
|
2018-12-03 16:44:25 +08:00
|
|
|
if (abs(curr_fps - fps) < abs(best_fps - fps)) {
|
|
|
|
best_fps = curr_fps;
|
|
|
|
rate = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fi->numerator = 1;
|
|
|
|
fi->denominator = best_fps;
|
2017-06-08 02:33:56 +08:00
|
|
|
|
2018-12-03 16:44:26 +08:00
|
|
|
find_mode:
|
2022-12-02 21:00:00 +08:00
|
|
|
mode = ov5640_find_mode(sensor, mode->width, mode->height, false);
|
2018-12-03 16:44:24 +08:00
|
|
|
return mode ? rate : -EINVAL;
|
2017-06-08 02:33:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ov5640_get_fmt(struct v4l2_subdev *sd,
|
media: v4l2-subdev: add subdev-wide state struct
We have 'struct v4l2_subdev_pad_config' which contains configuration for
a single pad used for the TRY functionality, and an array of those
structs is passed to various v4l2_subdev_pad_ops.
I was working on subdev internal routing between pads, and realized that
there's no way to add TRY functionality for routes, which is not pad
specific configuration. Adding a separate struct for try-route config
wouldn't work either, as e.g. set-fmt needs to know the try-route
configuration to propagate the settings.
This patch adds a new struct, 'struct v4l2_subdev_state' (which at the
moment only contains the v4l2_subdev_pad_config array) and the new
struct is used in most of the places where v4l2_subdev_pad_config was
used. All v4l2_subdev_pad_ops functions taking v4l2_subdev_pad_config
are changed to instead take v4l2_subdev_state.
The changes to drivers/media/v4l2-core/v4l2-subdev.c and
include/media/v4l2-subdev.h were written by hand, and all the driver
changes were done with the semantic patch below. The spatch needs to be
applied to a select list of directories. I used the following shell
commands to apply the spatch:
dirs="drivers/media/i2c drivers/media/platform drivers/media/usb drivers/media/test-drivers/vimc drivers/media/pci drivers/staging/media"
for dir in $dirs; do spatch -j8 --dir --include-headers --no-show-diff --in-place --sp-file v4l2-subdev-state.cocci $dir; done
Note that Coccinelle chokes on a few drivers (gcc extensions?). With
minor changes we can make Coccinelle run fine, and these changes can be
reverted after spatch. The diff for these changes is:
For drivers/media/i2c/s5k5baf.c:
@@ -1481,7 +1481,7 @@ static int s5k5baf_set_selection(struct v4l2_subdev *sd,
&s5k5baf_cis_rect,
v4l2_subdev_get_try_crop(sd, cfg, PAD_CIS),
v4l2_subdev_get_try_compose(sd, cfg, PAD_CIS),
- v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT)
+ v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT),
};
s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r);
return 0;
For drivers/media/platform/s3c-camif/camif-capture.c:
@@ -1230,7 +1230,7 @@ static int s3c_camif_subdev_get_fmt(struct v4l2_subdev *sd,
*mf = camif->mbus_fmt;
break;
- case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ case CAMIF_SD_PAD_SOURCE_C:
/* crop rectangle at camera interface input */
mf->width = camif->camif_crop.width;
mf->height = camif->camif_crop.height;
@@ -1332,7 +1332,7 @@ static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd,
}
break;
- case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ case CAMIF_SD_PAD_SOURCE_C:
/* Pixel format can be only changed on the sink pad. */
mf->code = camif->mbus_fmt.code;
mf->width = crop->width;
The semantic patch is:
// <smpl>
// Change function parameter
@@
identifier func;
identifier cfg;
@@
func(...,
- struct v4l2_subdev_pad_config *cfg
+ struct v4l2_subdev_state *sd_state
, ...)
{
<...
- cfg
+ sd_state
...>
}
// Change function declaration parameter
@@
identifier func;
identifier cfg;
type T;
@@
T func(...,
- struct v4l2_subdev_pad_config *cfg
+ struct v4l2_subdev_state *sd_state
, ...);
// Change function return value
@@
identifier func;
@@
- struct v4l2_subdev_pad_config
+ struct v4l2_subdev_state
*func(...)
{
...
}
// Change function declaration return value
@@
identifier func;
@@
- struct v4l2_subdev_pad_config
+ struct v4l2_subdev_state
*func(...);
// Some drivers pass a local pad_cfg for a single pad to a called function. Wrap it
// inside a pad_state.
@@
identifier func;
identifier pad_cfg;
@@
func(...)
{
...
struct v4l2_subdev_pad_config pad_cfg;
+ struct v4l2_subdev_state pad_state = { .pads = &pad_cfg };
<+...
(
v4l2_subdev_call
|
sensor_call
|
isi_try_fse
|
isc_try_fse
|
saa_call_all
)
(...,
- &pad_cfg
+ &pad_state
,...)
...+>
}
// If the function uses fields from pad_config, access via state->pads
@@
identifier func;
identifier state;
@@
func(...,
struct v4l2_subdev_state *state
, ...)
{
<...
(
- state->try_fmt
+ state->pads->try_fmt
|
- state->try_crop
+ state->pads->try_crop
|
- state->try_compose
+ state->pads->try_compose
)
...>
}
// If the function accesses the filehandle, use fh->state instead
@@
struct v4l2_subdev_fh *fh;
@@
- fh->pad
+ fh->state
@@
struct v4l2_subdev_fh fh;
@@
- fh.pad
+ fh.state
// Start of vsp1 specific
@@
@@
struct vsp1_entity {
...
- struct v4l2_subdev_pad_config *config;
+ struct v4l2_subdev_state *config;
...
};
@@
symbol entity;
@@
vsp1_entity_init(...)
{
...
entity->config =
- v4l2_subdev_alloc_pad_config
+ v4l2_subdev_alloc_state
(&entity->subdev);
...
}
@@
symbol entity;
@@
vsp1_entity_destroy(...)
{
...
- v4l2_subdev_free_pad_config
+ v4l2_subdev_free_state
(entity->config);
...
}
@exists@
identifier func =~ "(^vsp1.*)|(hsit_set_format)|(sru_enum_frame_size)|(sru_set_format)|(uif_get_selection)|(uif_set_selection)|(uds_enum_frame_size)|(uds_set_format)|(brx_set_format)|(brx_get_selection)|(histo_get_selection)|(histo_set_selection)|(brx_set_selection)";
symbol config;
@@
func(...) {
...
- struct v4l2_subdev_pad_config *config;
+ struct v4l2_subdev_state *config;
...
}
// End of vsp1 specific
// Start of rcar specific
@@
identifier sd;
identifier pad_cfg;
@@
rvin_try_format(...)
{
...
- struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_state *sd_state;
...
- pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+ sd_state = v4l2_subdev_alloc_state(sd);
<...
- pad_cfg
+ sd_state
...>
- v4l2_subdev_free_pad_config(pad_cfg);
+ v4l2_subdev_free_state(sd_state);
...
}
// End of rcar specific
// Start of rockchip specific
@@
identifier func =~ "(rkisp1_rsz_get_pad_fmt)|(rkisp1_rsz_get_pad_crop)|(rkisp1_rsz_register)";
symbol rsz;
symbol pad_cfg;
@@
func(...)
{
+ struct v4l2_subdev_state state = { .pads = rsz->pad_cfg };
...
- rsz->pad_cfg
+ &state
...
}
@@
identifier func =~ "(rkisp1_isp_get_pad_fmt)|(rkisp1_isp_get_pad_crop)";
symbol isp;
symbol pad_cfg;
@@
func(...)
{
+ struct v4l2_subdev_state state = { .pads = isp->pad_cfg };
...
- isp->pad_cfg
+ &state
...
}
@@
symbol rkisp1;
symbol isp;
symbol pad_cfg;
@@
rkisp1_isp_register(...)
{
+ struct v4l2_subdev_state state = { .pads = rkisp1->isp.pad_cfg };
...
- rkisp1->isp.pad_cfg
+ &state
...
}
// End of rockchip specific
// Start of tegra-video specific
@@
identifier sd;
identifier pad_cfg;
@@
__tegra_channel_try_format(...)
{
...
- struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_state *sd_state;
...
- pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+ sd_state = v4l2_subdev_alloc_state(sd);
<...
- pad_cfg
+ sd_state
...>
- v4l2_subdev_free_pad_config(pad_cfg);
+ v4l2_subdev_free_state(sd_state);
...
}
@@
identifier sd_state;
@@
__tegra_channel_try_format(...)
{
...
struct v4l2_subdev_state *sd_state;
<...
- sd_state->try_crop
+ sd_state->pads->try_crop
...>
}
// End of tegra-video specific
// </smpl>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2021-06-10 22:55:58 +08:00
|
|
|
struct v4l2_subdev_state *sd_state,
|
2017-06-08 02:33:56 +08:00
|
|
|
struct v4l2_subdev_format *format)
|
|
|
|
{
|
|
|
|
struct ov5640_dev *sensor = to_ov5640_dev(sd);
|
|
|
|
struct v4l2_mbus_framefmt *fmt;
|
|
|
|
|
|
|
|
if (format->pad != 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
mutex_lock(&sensor->lock);
|
|
|
|
|
|
|
|
if (format->which == V4L2_SUBDEV_FORMAT_TRY)
|
media: v4l: subdev: Switch to stream-aware state functions
Switch all drivers accessing sub-device state to use the stream-aware
functions. We will soon remove the old ones.
This patch has been generated using the following Coccinelle script:
---------8<------------
@@
expression E1, E2, E3;
@@
- v4l2_subdev_get_pad_format(E1, E2, E3)
+ v4l2_subdev_state_get_format(E2, E3)
@@
expression E1, E2, E3;
@@
- v4l2_subdev_get_pad_crop(E1, E2, E3)
+ v4l2_subdev_state_get_crop(E2, E3)
@@
expression E1, E2, E3;
@@
- v4l2_subdev_get_pad_compose(E1, E2, E3)
+ v4l2_subdev_state_get_compose(E2, E3)
@@
expression E1, E2, E3;
@@
- v4l2_subdev_get_try_format(E1, E2, E3)
+ v4l2_subdev_state_get_format(E2, E3)
@@
expression E1, E2, E3;
@@
- v4l2_subdev_get_try_crop(E1, E2, E3)
+ v4l2_subdev_state_get_crop(E2, E3)
@@
expression E1, E2, E3;
@@
- v4l2_subdev_get_try_compose(E1, E2, E3)
+ v4l2_subdev_state_get_compose(E2, E3)
---------8<------------
Additionally drivers/media/i2c/s5k5baf.c and
drivers/media/platform/samsung/s3c-camif/camif-capture.c have been
manually changed as Coccinelle didn't. Further local variables have been
removed as they became unused as a result of the other changes.
Also Coccinelle introduced indentation by space in files
drivers/media/i2c/st-mipid02.c and
drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c. This has been also
corrected.
The diff from Coccinelle-generated changes are:
> diff --git b/drivers/media/i2c/imx319.c a/drivers/media/i2c/imx319.c
> index e549692ff478..420984382173 100644
> --- b/drivers/media/i2c/imx319.c
> +++ a/drivers/media/i2c/imx319.c
> @@ -2001,7 +2001,6 @@ static int imx319_do_get_pad_format(struct imx319 *imx319,
> struct v4l2_subdev_format *fmt)
> {
> struct v4l2_mbus_framefmt *framefmt;
> - struct v4l2_subdev *sd = &imx319->sd;
>
> if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
> framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad);
> diff --git b/drivers/media/i2c/imx355.c a/drivers/media/i2c/imx355.c
> index 96bdde685d65..e1b1d2fc79dd 100644
> --- b/drivers/media/i2c/imx355.c
> +++ a/drivers/media/i2c/imx355.c
> @@ -1299,7 +1299,6 @@ static int imx355_do_get_pad_format(struct imx355 *imx355,
> struct v4l2_subdev_format *fmt)
> {
> struct v4l2_mbus_framefmt *framefmt;
> - struct v4l2_subdev *sd = &imx355->sd;
>
> if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
> framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad);
> diff --git b/drivers/media/i2c/ov08x40.c a/drivers/media/i2c/ov08x40.c
> index ca799bbcfdb7..abbb0b774d43 100644
> --- b/drivers/media/i2c/ov08x40.c
> +++ a/drivers/media/i2c/ov08x40.c
> @@ -2774,7 +2774,6 @@ static int ov08x40_do_get_pad_format(struct ov08x40 *ov08x,
> struct v4l2_subdev_format *fmt)
> {
> struct v4l2_mbus_framefmt *framefmt;
> - struct v4l2_subdev *sd = &ov08x->sd;
>
> if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
> framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad);
> diff --git b/drivers/media/i2c/ov13858.c a/drivers/media/i2c/ov13858.c
> index 7816d9787c61..09387e335d80 100644
> --- b/drivers/media/i2c/ov13858.c
> +++ a/drivers/media/i2c/ov13858.c
> @@ -1316,7 +1316,6 @@ static int ov13858_do_get_pad_format(struct ov13858 *ov13858,
> struct v4l2_subdev_format *fmt)
> {
> struct v4l2_mbus_framefmt *framefmt;
> - struct v4l2_subdev *sd = &ov13858->sd;
>
> if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
> framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad);
> diff --git b/drivers/media/i2c/ov13b10.c a/drivers/media/i2c/ov13b10.c
> index 268cd4b03f9c..c06411d5ee2b 100644
> --- b/drivers/media/i2c/ov13b10.c
> +++ a/drivers/media/i2c/ov13b10.c
> @@ -1001,7 +1001,6 @@ static int ov13b10_do_get_pad_format(struct ov13b10 *ov13b,
> struct v4l2_subdev_format *fmt)
> {
> struct v4l2_mbus_framefmt *framefmt;
> - struct v4l2_subdev *sd = &ov13b->sd;
>
> if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
> framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad);
> diff --git b/drivers/media/i2c/s5c73m3/s5c73m3-core.c a/drivers/media/i2c/s5c73m3/s5c73m3-core.c
> index 47605e36bc60..8f9b5713daf7 100644
> --- b/drivers/media/i2c/s5c73m3/s5c73m3-core.c
> +++ a/drivers/media/i2c/s5c73m3/s5c73m3-core.c
> @@ -819,7 +819,6 @@ static void s5c73m3_oif_try_format(struct s5c73m3 *state,
> struct v4l2_subdev_format *fmt,
> const struct s5c73m3_frame_size **fs)
> {
> - struct v4l2_subdev *sd = &state->sensor_sd;
> u32 code;
>
> switch (fmt->pad) {
> diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c
> index 67da2045f543..03ccfb0e1e11 100644
> --- a/drivers/media/i2c/s5k5baf.c
> +++ b/drivers/media/i2c/s5k5baf.c
> @@ -1472,14 +1472,11 @@ static int s5k5baf_set_selection(struct v4l2_subdev *sd,
>
> if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
> rects = (struct v4l2_rect * []) {
> - &s5k5baf_cis_rect,
> - v4l2_subdev_get_try_crop(sd, sd_state,
> - PAD_CIS),
> - v4l2_subdev_get_try_compose(sd, sd_state,
> - PAD_CIS),
> - v4l2_subdev_get_try_crop(sd, sd_state,
> - PAD_OUT)
> - };
> + &s5k5baf_cis_rect,
> + v4l2_subdev_state_get_crop(sd_state, PAD_CIS),
> + v4l2_subdev_state_get_compose(sd_state, PAD_CIS),
> + v4l2_subdev_state_get_crop(sd_state, PAD_OUT)
> + };
> s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r);
> return 0;
> }
> diff --git b/drivers/media/platform/samsung/s3c-camif/camif-capture.c a/drivers/media/platform/samsung/s3c-camif/camif-capture.c
> index 295e083f38e8..be58260ea67e 100644
> --- b/drivers/media/platform/samsung/s3c-camif/camif-capture.c
> +++ a/drivers/media/platform/samsung/s3c-camif/camif-capture.c
> @@ -1216,7 +1216,7 @@ static int s3c_camif_subdev_get_fmt(struct v4l2_subdev *sd,
> struct v4l2_mbus_framefmt *mf = &fmt->format;
>
> if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
> - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad);
> + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad);
> fmt->format = *mf;
> return 0;
> }
> @@ -1305,7 +1305,7 @@ static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd,
> __camif_subdev_try_format(camif, mf, fmt->pad);
>
> if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
> - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad);
> + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad);
> *mf = fmt->format;
> mutex_unlock(&camif->lock);
> return 0;
> diff --git b/drivers/media/platform/ti/cal/cal-camerarx.c a/drivers/media/platform/ti/cal/cal-camerarx.c
> index cea454ed9c20..61433744c6c4 100644
> --- b/drivers/media/platform/ti/cal/cal-camerarx.c
> +++ a/drivers/media/platform/ti/cal/cal-camerarx.c
> @@ -621,8 +621,6 @@ static int cal_camerarx_sd_enum_mbus_code(struct v4l2_subdev *sd,
> struct v4l2_subdev_state *state,
> struct v4l2_subdev_mbus_code_enum *code)
> {
> - struct cal_camerarx *phy = to_cal_camerarx(sd);
> -
> /* No transcoding, source and sink codes must match. */
> if (cal_rx_pad_is_source(code->pad)) {
> struct v4l2_mbus_framefmt *fmt;
> diff --git b/drivers/staging/media/imx/imx-ic-prp.c a/drivers/staging/media/imx/imx-ic-prp.c
> index dd558fac6477..61d69f19657e 100644
> --- b/drivers/staging/media/imx/imx-ic-prp.c
> +++ a/drivers/staging/media/imx/imx-ic-prp.c
> @@ -82,8 +82,6 @@ static struct v4l2_mbus_framefmt *
> __prp_get_fmt(struct prp_priv *priv, struct v4l2_subdev_state *sd_state,
> unsigned int pad, enum v4l2_subdev_format_whence which)
> {
> - struct imx_ic_priv *ic_priv = priv->ic_priv;
> -
> if (which == V4L2_SUBDEV_FORMAT_TRY)
> return v4l2_subdev_state_get_format(sd_state, pad);
> else
> diff --git b/drivers/staging/media/imx/imx-ic-prpencvf.c a/drivers/staging/media/imx/imx-ic-prpencvf.c
> index 02db7dbb884b..ec73c901079e 100644
> --- b/drivers/staging/media/imx/imx-ic-prpencvf.c
> +++ a/drivers/staging/media/imx/imx-ic-prpencvf.c
> @@ -790,8 +790,6 @@ static struct v4l2_mbus_framefmt *
> __prp_get_fmt(struct prp_priv *priv, struct v4l2_subdev_state *sd_state,
> unsigned int pad, enum v4l2_subdev_format_whence which)
> {
> - struct imx_ic_priv *ic_priv = priv->ic_priv;
> -
> if (which == V4L2_SUBDEV_FORMAT_TRY)
> return v4l2_subdev_state_get_format(sd_state, pad);
> else
> diff --git a/drivers/media/i2c/st-mipid02.c b/drivers/media/i2c/st-mipid02.c
> index 9c9361354c00..b08a249b5fdd 100644
> --- a/drivers/media/i2c/st-mipid02.c
> +++ b/drivers/media/i2c/st-mipid02.c
> @@ -751,7 +751,7 @@ static void mipid02_set_fmt_source(struct v4l2_subdev *sd,
> format->format = bridge->fmt;
> else
> format->format = *v4l2_subdev_state_get_format(sd_state,
> - MIPID02_SINK_0);
> + MIPID02_SINK_0);
>
> /* but code may need to be converted */
> format->format.code = serial_to_parallel_code(format->format.code);
> diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c
> index 117912d3bfbd..96353648c032 100644
> --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c
> +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c
> @@ -319,7 +319,7 @@ static void rkisp1_isp_start(struct rkisp1_isp *isp,
> rkisp1_write(rkisp1, RKISP1_CIF_ISP_CTRL, val);
>
> src_fmt = v4l2_subdev_state_get_format(sd_state,
> - RKISP1_ISP_PAD_SOURCE_VIDEO);
> + RKISP1_ISP_PAD_SOURCE_VIDEO);
> src_info = rkisp1_mbus_info_get_by_code(src_fmt->code);
>
> if (src_info->pixel_enc != V4L2_PIXEL_ENC_BAYER)
> @@ -475,9 +475,9 @@ static void rkisp1_isp_set_src_fmt(struct rkisp1_isp *isp,
> sink_fmt = v4l2_subdev_state_get_format(sd_state,
> RKISP1_ISP_PAD_SINK_VIDEO);
> src_fmt = v4l2_subdev_state_get_format(sd_state,
> - RKISP1_ISP_PAD_SOURCE_VIDEO);
> + RKISP1_ISP_PAD_SOURCE_VIDEO);
> src_crop = v4l2_subdev_state_get_crop(sd_state,
> - RKISP1_ISP_PAD_SOURCE_VIDEO);
> + RKISP1_ISP_PAD_SOURCE_VIDEO);
>
> /*
> * Media bus code. The ISP can operate in pass-through mode (Bayer in,
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
2023-10-13 15:37:10 +08:00
|
|
|
fmt = v4l2_subdev_state_get_format(sd_state, format->pad);
|
2017-06-08 02:33:56 +08:00
|
|
|
else
|
|
|
|
fmt = &sensor->fmt;
|
|
|
|
|
|
|
|
format->format = *fmt;
|
|
|
|
|
|
|
|
mutex_unlock(&sensor->lock);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ov5640_try_fmt_internal(struct v4l2_subdev *sd,
|
|
|
|
struct v4l2_mbus_framefmt *fmt,
|
|
|
|
const struct ov5640_mode_info **new_mode)
|
|
|
|
{
|
|
|
|
struct ov5640_dev *sensor = to_ov5640_dev(sd);
|
|
|
|
const struct ov5640_mode_info *mode;
|
2022-05-13 22:14:15 +08:00
|
|
|
const struct ov5640_pixfmt *pixfmt;
|
|
|
|
unsigned int bpp;
|
2017-06-08 02:33:56 +08:00
|
|
|
|
2022-05-13 22:14:06 +08:00
|
|
|
mode = ov5640_find_mode(sensor, fmt->width, fmt->height, true);
|
2017-06-08 02:33:56 +08:00
|
|
|
if (!mode)
|
|
|
|
return -EINVAL;
|
2022-05-13 22:14:14 +08:00
|
|
|
|
2022-05-13 22:14:15 +08:00
|
|
|
pixfmt = ov5640_code_to_pixfmt(sensor, fmt->code);
|
|
|
|
bpp = pixfmt->bpp;
|
|
|
|
|
2022-05-13 22:14:14 +08:00
|
|
|
/*
|
|
|
|
* Adjust mode according to bpp:
|
|
|
|
* - 8bpp modes work for resolution >= 1280x720
|
|
|
|
* - 24bpp modes work resolution < 1280x720
|
|
|
|
*/
|
|
|
|
if (bpp == 8 && mode->width < 1280)
|
|
|
|
mode = &ov5640_mode_data[OV5640_MODE_720P_1280_720];
|
|
|
|
else if (bpp == 24 && mode->width > 1024)
|
|
|
|
mode = &ov5640_mode_data[OV5640_MODE_XGA_1024_768];
|
|
|
|
|
2022-05-13 22:13:58 +08:00
|
|
|
fmt->width = mode->width;
|
|
|
|
fmt->height = mode->height;
|
2017-06-08 02:33:56 +08:00
|
|
|
|
|
|
|
if (new_mode)
|
|
|
|
*new_mode = mode;
|
2018-01-03 17:57:32 +08:00
|
|
|
|
2022-05-13 22:14:15 +08:00
|
|
|
fmt->code = pixfmt->code;
|
|
|
|
fmt->colorspace = pixfmt->colorspace;
|
2018-03-07 01:04:39 +08:00
|
|
|
fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
|
|
|
|
fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
|
|
|
|
fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
|
2018-01-03 17:57:32 +08:00
|
|
|
|
2017-06-08 02:33:56 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-07-19 15:30:12 +08:00
|
|
|
static void __v4l2_ctrl_vblank_update(struct ov5640_dev *sensor, u32 vblank)
|
|
|
|
{
|
|
|
|
const struct ov5640_mode_info *mode = sensor->current_mode;
|
|
|
|
|
|
|
|
__v4l2_ctrl_modify_range(sensor->ctrls.vblank, OV5640_MIN_VBLANK,
|
|
|
|
OV5640_MAX_VTS - mode->height, 1, vblank);
|
|
|
|
|
|
|
|
__v4l2_ctrl_s_ctrl(sensor->ctrls.vblank, vblank);
|
|
|
|
}
|
|
|
|
|
2022-05-13 22:13:54 +08:00
|
|
|
static int ov5640_update_pixel_rate(struct ov5640_dev *sensor)
|
|
|
|
{
|
|
|
|
const struct ov5640_mode_info *mode = sensor->current_mode;
|
|
|
|
enum ov5640_pixel_rate_id pixel_rate_id = mode->pixel_rate;
|
|
|
|
struct v4l2_mbus_framefmt *fmt = &sensor->fmt;
|
2023-07-19 15:30:12 +08:00
|
|
|
const struct ov5640_timings *timings = ov5640_timings(sensor, mode);
|
2022-05-13 22:14:04 +08:00
|
|
|
s32 exposure_val, exposure_max;
|
2022-05-13 22:14:03 +08:00
|
|
|
unsigned int hblank;
|
2022-05-13 22:13:54 +08:00
|
|
|
unsigned int i = 0;
|
|
|
|
u32 pixel_rate;
|
|
|
|
s64 link_freq;
|
|
|
|
u32 num_lanes;
|
2022-05-13 22:14:05 +08:00
|
|
|
u32 vblank;
|
2022-05-13 22:13:54 +08:00
|
|
|
u32 bpp;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Update the pixel rate control value.
|
|
|
|
*
|
|
|
|
* For DVP mode, maintain the pixel rate calculation using fixed FPS.
|
|
|
|
*/
|
|
|
|
if (!ov5640_is_csi2(sensor)) {
|
|
|
|
__v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate,
|
|
|
|
ov5640_calc_pixel_rate(sensor));
|
|
|
|
|
2023-07-19 15:30:12 +08:00
|
|
|
__v4l2_ctrl_vblank_update(sensor, timings->vblank_def);
|
|
|
|
|
2022-05-13 22:13:54 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The MIPI CSI-2 link frequency should comply with the CSI-2
|
|
|
|
* specification and be lower than 1GHz.
|
|
|
|
*
|
|
|
|
* Start from the suggested pixel_rate for the current mode and
|
|
|
|
* progressively slow it down if it exceeds 1GHz.
|
|
|
|
*/
|
|
|
|
num_lanes = sensor->ep.bus.mipi_csi2.num_data_lanes;
|
2022-05-13 22:14:15 +08:00
|
|
|
bpp = ov5640_code_to_bpp(sensor, fmt->code);
|
2022-05-13 22:13:54 +08:00
|
|
|
do {
|
|
|
|
pixel_rate = ov5640_pixel_rates[pixel_rate_id];
|
|
|
|
link_freq = pixel_rate * bpp / (2 * num_lanes);
|
|
|
|
} while (link_freq >= 1000000000U &&
|
|
|
|
++pixel_rate_id < OV5640_NUM_PIXEL_RATES);
|
|
|
|
|
|
|
|
sensor->current_link_freq = link_freq;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Higher link rates require the clock tree to be programmed with
|
|
|
|
* 'mipi_div' = 1; this has the effect of halving the actual output
|
|
|
|
* pixel rate in the MIPI domain.
|
|
|
|
*
|
|
|
|
* Adjust the pixel rate and link frequency control value to report it
|
|
|
|
* correctly to userspace.
|
|
|
|
*/
|
|
|
|
if (link_freq > OV5640_LINK_RATE_MAX) {
|
|
|
|
pixel_rate /= 2;
|
|
|
|
link_freq /= 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(ov5640_csi2_link_freqs); ++i) {
|
|
|
|
if (ov5640_csi2_link_freqs[i] == link_freq)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
WARN_ON(i == ARRAY_SIZE(ov5640_csi2_link_freqs));
|
|
|
|
|
|
|
|
__v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate, pixel_rate);
|
|
|
|
__v4l2_ctrl_s_ctrl(sensor->ctrls.link_freq, i);
|
|
|
|
|
2022-05-13 22:14:03 +08:00
|
|
|
hblank = timings->htot - mode->width;
|
|
|
|
__v4l2_ctrl_modify_range(sensor->ctrls.hblank,
|
|
|
|
hblank, hblank, 1, hblank);
|
|
|
|
|
2022-05-13 22:14:05 +08:00
|
|
|
vblank = timings->vblank_def;
|
2023-07-19 15:30:12 +08:00
|
|
|
__v4l2_ctrl_vblank_update(sensor, vblank);
|
2022-05-13 22:14:04 +08:00
|
|
|
|
2022-05-13 22:14:05 +08:00
|
|
|
exposure_max = timings->crop.height + vblank - 4;
|
2022-05-13 22:14:04 +08:00
|
|
|
exposure_val = clamp_t(s32, sensor->ctrls.exposure->val,
|
|
|
|
sensor->ctrls.exposure->minimum,
|
|
|
|
exposure_max);
|
2022-05-13 22:14:05 +08:00
|
|
|
|
2022-05-13 22:14:04 +08:00
|
|
|
__v4l2_ctrl_modify_range(sensor->ctrls.exposure,
|
|
|
|
sensor->ctrls.exposure->minimum,
|
|
|
|
exposure_max, 1, exposure_val);
|
|
|
|
|
2022-05-13 22:13:54 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-06-08 02:33:56 +08:00
|
|
|
static int ov5640_set_fmt(struct v4l2_subdev *sd,
|
media: v4l2-subdev: add subdev-wide state struct
We have 'struct v4l2_subdev_pad_config' which contains configuration for
a single pad used for the TRY functionality, and an array of those
structs is passed to various v4l2_subdev_pad_ops.
I was working on subdev internal routing between pads, and realized that
there's no way to add TRY functionality for routes, which is not pad
specific configuration. Adding a separate struct for try-route config
wouldn't work either, as e.g. set-fmt needs to know the try-route
configuration to propagate the settings.
This patch adds a new struct, 'struct v4l2_subdev_state' (which at the
moment only contains the v4l2_subdev_pad_config array) and the new
struct is used in most of the places where v4l2_subdev_pad_config was
used. All v4l2_subdev_pad_ops functions taking v4l2_subdev_pad_config
are changed to instead take v4l2_subdev_state.
The changes to drivers/media/v4l2-core/v4l2-subdev.c and
include/media/v4l2-subdev.h were written by hand, and all the driver
changes were done with the semantic patch below. The spatch needs to be
applied to a select list of directories. I used the following shell
commands to apply the spatch:
dirs="drivers/media/i2c drivers/media/platform drivers/media/usb drivers/media/test-drivers/vimc drivers/media/pci drivers/staging/media"
for dir in $dirs; do spatch -j8 --dir --include-headers --no-show-diff --in-place --sp-file v4l2-subdev-state.cocci $dir; done
Note that Coccinelle chokes on a few drivers (gcc extensions?). With
minor changes we can make Coccinelle run fine, and these changes can be
reverted after spatch. The diff for these changes is:
For drivers/media/i2c/s5k5baf.c:
@@ -1481,7 +1481,7 @@ static int s5k5baf_set_selection(struct v4l2_subdev *sd,
&s5k5baf_cis_rect,
v4l2_subdev_get_try_crop(sd, cfg, PAD_CIS),
v4l2_subdev_get_try_compose(sd, cfg, PAD_CIS),
- v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT)
+ v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT),
};
s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r);
return 0;
For drivers/media/platform/s3c-camif/camif-capture.c:
@@ -1230,7 +1230,7 @@ static int s3c_camif_subdev_get_fmt(struct v4l2_subdev *sd,
*mf = camif->mbus_fmt;
break;
- case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ case CAMIF_SD_PAD_SOURCE_C:
/* crop rectangle at camera interface input */
mf->width = camif->camif_crop.width;
mf->height = camif->camif_crop.height;
@@ -1332,7 +1332,7 @@ static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd,
}
break;
- case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ case CAMIF_SD_PAD_SOURCE_C:
/* Pixel format can be only changed on the sink pad. */
mf->code = camif->mbus_fmt.code;
mf->width = crop->width;
The semantic patch is:
// <smpl>
// Change function parameter
@@
identifier func;
identifier cfg;
@@
func(...,
- struct v4l2_subdev_pad_config *cfg
+ struct v4l2_subdev_state *sd_state
, ...)
{
<...
- cfg
+ sd_state
...>
}
// Change function declaration parameter
@@
identifier func;
identifier cfg;
type T;
@@
T func(...,
- struct v4l2_subdev_pad_config *cfg
+ struct v4l2_subdev_state *sd_state
, ...);
// Change function return value
@@
identifier func;
@@
- struct v4l2_subdev_pad_config
+ struct v4l2_subdev_state
*func(...)
{
...
}
// Change function declaration return value
@@
identifier func;
@@
- struct v4l2_subdev_pad_config
+ struct v4l2_subdev_state
*func(...);
// Some drivers pass a local pad_cfg for a single pad to a called function. Wrap it
// inside a pad_state.
@@
identifier func;
identifier pad_cfg;
@@
func(...)
{
...
struct v4l2_subdev_pad_config pad_cfg;
+ struct v4l2_subdev_state pad_state = { .pads = &pad_cfg };
<+...
(
v4l2_subdev_call
|
sensor_call
|
isi_try_fse
|
isc_try_fse
|
saa_call_all
)
(...,
- &pad_cfg
+ &pad_state
,...)
...+>
}
// If the function uses fields from pad_config, access via state->pads
@@
identifier func;
identifier state;
@@
func(...,
struct v4l2_subdev_state *state
, ...)
{
<...
(
- state->try_fmt
+ state->pads->try_fmt
|
- state->try_crop
+ state->pads->try_crop
|
- state->try_compose
+ state->pads->try_compose
)
...>
}
// If the function accesses the filehandle, use fh->state instead
@@
struct v4l2_subdev_fh *fh;
@@
- fh->pad
+ fh->state
@@
struct v4l2_subdev_fh fh;
@@
- fh.pad
+ fh.state
// Start of vsp1 specific
@@
@@
struct vsp1_entity {
...
- struct v4l2_subdev_pad_config *config;
+ struct v4l2_subdev_state *config;
...
};
@@
symbol entity;
@@
vsp1_entity_init(...)
{
...
entity->config =
- v4l2_subdev_alloc_pad_config
+ v4l2_subdev_alloc_state
(&entity->subdev);
...
}
@@
symbol entity;
@@
vsp1_entity_destroy(...)
{
...
- v4l2_subdev_free_pad_config
+ v4l2_subdev_free_state
(entity->config);
...
}
@exists@
identifier func =~ "(^vsp1.*)|(hsit_set_format)|(sru_enum_frame_size)|(sru_set_format)|(uif_get_selection)|(uif_set_selection)|(uds_enum_frame_size)|(uds_set_format)|(brx_set_format)|(brx_get_selection)|(histo_get_selection)|(histo_set_selection)|(brx_set_selection)";
symbol config;
@@
func(...) {
...
- struct v4l2_subdev_pad_config *config;
+ struct v4l2_subdev_state *config;
...
}
// End of vsp1 specific
// Start of rcar specific
@@
identifier sd;
identifier pad_cfg;
@@
rvin_try_format(...)
{
...
- struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_state *sd_state;
...
- pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+ sd_state = v4l2_subdev_alloc_state(sd);
<...
- pad_cfg
+ sd_state
...>
- v4l2_subdev_free_pad_config(pad_cfg);
+ v4l2_subdev_free_state(sd_state);
...
}
// End of rcar specific
// Start of rockchip specific
@@
identifier func =~ "(rkisp1_rsz_get_pad_fmt)|(rkisp1_rsz_get_pad_crop)|(rkisp1_rsz_register)";
symbol rsz;
symbol pad_cfg;
@@
func(...)
{
+ struct v4l2_subdev_state state = { .pads = rsz->pad_cfg };
...
- rsz->pad_cfg
+ &state
...
}
@@
identifier func =~ "(rkisp1_isp_get_pad_fmt)|(rkisp1_isp_get_pad_crop)";
symbol isp;
symbol pad_cfg;
@@
func(...)
{
+ struct v4l2_subdev_state state = { .pads = isp->pad_cfg };
...
- isp->pad_cfg
+ &state
...
}
@@
symbol rkisp1;
symbol isp;
symbol pad_cfg;
@@
rkisp1_isp_register(...)
{
+ struct v4l2_subdev_state state = { .pads = rkisp1->isp.pad_cfg };
...
- rkisp1->isp.pad_cfg
+ &state
...
}
// End of rockchip specific
// Start of tegra-video specific
@@
identifier sd;
identifier pad_cfg;
@@
__tegra_channel_try_format(...)
{
...
- struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_state *sd_state;
...
- pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+ sd_state = v4l2_subdev_alloc_state(sd);
<...
- pad_cfg
+ sd_state
...>
- v4l2_subdev_free_pad_config(pad_cfg);
+ v4l2_subdev_free_state(sd_state);
...
}
@@
identifier sd_state;
@@
__tegra_channel_try_format(...)
{
...
struct v4l2_subdev_state *sd_state;
<...
- sd_state->try_crop
+ sd_state->pads->try_crop
...>
}
// End of tegra-video specific
// </smpl>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2021-06-10 22:55:58 +08:00
|
|
|
struct v4l2_subdev_state *sd_state,
|
2017-06-08 02:33:56 +08:00
|
|
|
struct v4l2_subdev_format *format)
|
|
|
|
{
|
|
|
|
struct ov5640_dev *sensor = to_ov5640_dev(sd);
|
|
|
|
const struct ov5640_mode_info *new_mode;
|
2018-03-07 01:04:39 +08:00
|
|
|
struct v4l2_mbus_framefmt *mbus_fmt = &format->format;
|
2017-06-08 02:33:56 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (format->pad != 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
mutex_lock(&sensor->lock);
|
|
|
|
|
|
|
|
if (sensor->streaming) {
|
|
|
|
ret = -EBUSY;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2023-05-05 15:16:18 +08:00
|
|
|
ret = ov5640_try_fmt_internal(sd, mbus_fmt, &new_mode);
|
2017-06-08 02:33:56 +08:00
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
|
2021-11-02 03:52:51 +08:00
|
|
|
if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
|
media: v4l: subdev: Switch to stream-aware state functions
Switch all drivers accessing sub-device state to use the stream-aware
functions. We will soon remove the old ones.
This patch has been generated using the following Coccinelle script:
---------8<------------
@@
expression E1, E2, E3;
@@
- v4l2_subdev_get_pad_format(E1, E2, E3)
+ v4l2_subdev_state_get_format(E2, E3)
@@
expression E1, E2, E3;
@@
- v4l2_subdev_get_pad_crop(E1, E2, E3)
+ v4l2_subdev_state_get_crop(E2, E3)
@@
expression E1, E2, E3;
@@
- v4l2_subdev_get_pad_compose(E1, E2, E3)
+ v4l2_subdev_state_get_compose(E2, E3)
@@
expression E1, E2, E3;
@@
- v4l2_subdev_get_try_format(E1, E2, E3)
+ v4l2_subdev_state_get_format(E2, E3)
@@
expression E1, E2, E3;
@@
- v4l2_subdev_get_try_crop(E1, E2, E3)
+ v4l2_subdev_state_get_crop(E2, E3)
@@
expression E1, E2, E3;
@@
- v4l2_subdev_get_try_compose(E1, E2, E3)
+ v4l2_subdev_state_get_compose(E2, E3)
---------8<------------
Additionally drivers/media/i2c/s5k5baf.c and
drivers/media/platform/samsung/s3c-camif/camif-capture.c have been
manually changed as Coccinelle didn't. Further local variables have been
removed as they became unused as a result of the other changes.
Also Coccinelle introduced indentation by space in files
drivers/media/i2c/st-mipid02.c and
drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c. This has been also
corrected.
The diff from Coccinelle-generated changes are:
> diff --git b/drivers/media/i2c/imx319.c a/drivers/media/i2c/imx319.c
> index e549692ff478..420984382173 100644
> --- b/drivers/media/i2c/imx319.c
> +++ a/drivers/media/i2c/imx319.c
> @@ -2001,7 +2001,6 @@ static int imx319_do_get_pad_format(struct imx319 *imx319,
> struct v4l2_subdev_format *fmt)
> {
> struct v4l2_mbus_framefmt *framefmt;
> - struct v4l2_subdev *sd = &imx319->sd;
>
> if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
> framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad);
> diff --git b/drivers/media/i2c/imx355.c a/drivers/media/i2c/imx355.c
> index 96bdde685d65..e1b1d2fc79dd 100644
> --- b/drivers/media/i2c/imx355.c
> +++ a/drivers/media/i2c/imx355.c
> @@ -1299,7 +1299,6 @@ static int imx355_do_get_pad_format(struct imx355 *imx355,
> struct v4l2_subdev_format *fmt)
> {
> struct v4l2_mbus_framefmt *framefmt;
> - struct v4l2_subdev *sd = &imx355->sd;
>
> if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
> framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad);
> diff --git b/drivers/media/i2c/ov08x40.c a/drivers/media/i2c/ov08x40.c
> index ca799bbcfdb7..abbb0b774d43 100644
> --- b/drivers/media/i2c/ov08x40.c
> +++ a/drivers/media/i2c/ov08x40.c
> @@ -2774,7 +2774,6 @@ static int ov08x40_do_get_pad_format(struct ov08x40 *ov08x,
> struct v4l2_subdev_format *fmt)
> {
> struct v4l2_mbus_framefmt *framefmt;
> - struct v4l2_subdev *sd = &ov08x->sd;
>
> if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
> framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad);
> diff --git b/drivers/media/i2c/ov13858.c a/drivers/media/i2c/ov13858.c
> index 7816d9787c61..09387e335d80 100644
> --- b/drivers/media/i2c/ov13858.c
> +++ a/drivers/media/i2c/ov13858.c
> @@ -1316,7 +1316,6 @@ static int ov13858_do_get_pad_format(struct ov13858 *ov13858,
> struct v4l2_subdev_format *fmt)
> {
> struct v4l2_mbus_framefmt *framefmt;
> - struct v4l2_subdev *sd = &ov13858->sd;
>
> if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
> framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad);
> diff --git b/drivers/media/i2c/ov13b10.c a/drivers/media/i2c/ov13b10.c
> index 268cd4b03f9c..c06411d5ee2b 100644
> --- b/drivers/media/i2c/ov13b10.c
> +++ a/drivers/media/i2c/ov13b10.c
> @@ -1001,7 +1001,6 @@ static int ov13b10_do_get_pad_format(struct ov13b10 *ov13b,
> struct v4l2_subdev_format *fmt)
> {
> struct v4l2_mbus_framefmt *framefmt;
> - struct v4l2_subdev *sd = &ov13b->sd;
>
> if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
> framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad);
> diff --git b/drivers/media/i2c/s5c73m3/s5c73m3-core.c a/drivers/media/i2c/s5c73m3/s5c73m3-core.c
> index 47605e36bc60..8f9b5713daf7 100644
> --- b/drivers/media/i2c/s5c73m3/s5c73m3-core.c
> +++ a/drivers/media/i2c/s5c73m3/s5c73m3-core.c
> @@ -819,7 +819,6 @@ static void s5c73m3_oif_try_format(struct s5c73m3 *state,
> struct v4l2_subdev_format *fmt,
> const struct s5c73m3_frame_size **fs)
> {
> - struct v4l2_subdev *sd = &state->sensor_sd;
> u32 code;
>
> switch (fmt->pad) {
> diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c
> index 67da2045f543..03ccfb0e1e11 100644
> --- a/drivers/media/i2c/s5k5baf.c
> +++ b/drivers/media/i2c/s5k5baf.c
> @@ -1472,14 +1472,11 @@ static int s5k5baf_set_selection(struct v4l2_subdev *sd,
>
> if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
> rects = (struct v4l2_rect * []) {
> - &s5k5baf_cis_rect,
> - v4l2_subdev_get_try_crop(sd, sd_state,
> - PAD_CIS),
> - v4l2_subdev_get_try_compose(sd, sd_state,
> - PAD_CIS),
> - v4l2_subdev_get_try_crop(sd, sd_state,
> - PAD_OUT)
> - };
> + &s5k5baf_cis_rect,
> + v4l2_subdev_state_get_crop(sd_state, PAD_CIS),
> + v4l2_subdev_state_get_compose(sd_state, PAD_CIS),
> + v4l2_subdev_state_get_crop(sd_state, PAD_OUT)
> + };
> s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r);
> return 0;
> }
> diff --git b/drivers/media/platform/samsung/s3c-camif/camif-capture.c a/drivers/media/platform/samsung/s3c-camif/camif-capture.c
> index 295e083f38e8..be58260ea67e 100644
> --- b/drivers/media/platform/samsung/s3c-camif/camif-capture.c
> +++ a/drivers/media/platform/samsung/s3c-camif/camif-capture.c
> @@ -1216,7 +1216,7 @@ static int s3c_camif_subdev_get_fmt(struct v4l2_subdev *sd,
> struct v4l2_mbus_framefmt *mf = &fmt->format;
>
> if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
> - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad);
> + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad);
> fmt->format = *mf;
> return 0;
> }
> @@ -1305,7 +1305,7 @@ static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd,
> __camif_subdev_try_format(camif, mf, fmt->pad);
>
> if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
> - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad);
> + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad);
> *mf = fmt->format;
> mutex_unlock(&camif->lock);
> return 0;
> diff --git b/drivers/media/platform/ti/cal/cal-camerarx.c a/drivers/media/platform/ti/cal/cal-camerarx.c
> index cea454ed9c20..61433744c6c4 100644
> --- b/drivers/media/platform/ti/cal/cal-camerarx.c
> +++ a/drivers/media/platform/ti/cal/cal-camerarx.c
> @@ -621,8 +621,6 @@ static int cal_camerarx_sd_enum_mbus_code(struct v4l2_subdev *sd,
> struct v4l2_subdev_state *state,
> struct v4l2_subdev_mbus_code_enum *code)
> {
> - struct cal_camerarx *phy = to_cal_camerarx(sd);
> -
> /* No transcoding, source and sink codes must match. */
> if (cal_rx_pad_is_source(code->pad)) {
> struct v4l2_mbus_framefmt *fmt;
> diff --git b/drivers/staging/media/imx/imx-ic-prp.c a/drivers/staging/media/imx/imx-ic-prp.c
> index dd558fac6477..61d69f19657e 100644
> --- b/drivers/staging/media/imx/imx-ic-prp.c
> +++ a/drivers/staging/media/imx/imx-ic-prp.c
> @@ -82,8 +82,6 @@ static struct v4l2_mbus_framefmt *
> __prp_get_fmt(struct prp_priv *priv, struct v4l2_subdev_state *sd_state,
> unsigned int pad, enum v4l2_subdev_format_whence which)
> {
> - struct imx_ic_priv *ic_priv = priv->ic_priv;
> -
> if (which == V4L2_SUBDEV_FORMAT_TRY)
> return v4l2_subdev_state_get_format(sd_state, pad);
> else
> diff --git b/drivers/staging/media/imx/imx-ic-prpencvf.c a/drivers/staging/media/imx/imx-ic-prpencvf.c
> index 02db7dbb884b..ec73c901079e 100644
> --- b/drivers/staging/media/imx/imx-ic-prpencvf.c
> +++ a/drivers/staging/media/imx/imx-ic-prpencvf.c
> @@ -790,8 +790,6 @@ static struct v4l2_mbus_framefmt *
> __prp_get_fmt(struct prp_priv *priv, struct v4l2_subdev_state *sd_state,
> unsigned int pad, enum v4l2_subdev_format_whence which)
> {
> - struct imx_ic_priv *ic_priv = priv->ic_priv;
> -
> if (which == V4L2_SUBDEV_FORMAT_TRY)
> return v4l2_subdev_state_get_format(sd_state, pad);
> else
> diff --git a/drivers/media/i2c/st-mipid02.c b/drivers/media/i2c/st-mipid02.c
> index 9c9361354c00..b08a249b5fdd 100644
> --- a/drivers/media/i2c/st-mipid02.c
> +++ b/drivers/media/i2c/st-mipid02.c
> @@ -751,7 +751,7 @@ static void mipid02_set_fmt_source(struct v4l2_subdev *sd,
> format->format = bridge->fmt;
> else
> format->format = *v4l2_subdev_state_get_format(sd_state,
> - MIPID02_SINK_0);
> + MIPID02_SINK_0);
>
> /* but code may need to be converted */
> format->format.code = serial_to_parallel_code(format->format.code);
> diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c
> index 117912d3bfbd..96353648c032 100644
> --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c
> +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c
> @@ -319,7 +319,7 @@ static void rkisp1_isp_start(struct rkisp1_isp *isp,
> rkisp1_write(rkisp1, RKISP1_CIF_ISP_CTRL, val);
>
> src_fmt = v4l2_subdev_state_get_format(sd_state,
> - RKISP1_ISP_PAD_SOURCE_VIDEO);
> + RKISP1_ISP_PAD_SOURCE_VIDEO);
> src_info = rkisp1_mbus_info_get_by_code(src_fmt->code);
>
> if (src_info->pixel_enc != V4L2_PIXEL_ENC_BAYER)
> @@ -475,9 +475,9 @@ static void rkisp1_isp_set_src_fmt(struct rkisp1_isp *isp,
> sink_fmt = v4l2_subdev_state_get_format(sd_state,
> RKISP1_ISP_PAD_SINK_VIDEO);
> src_fmt = v4l2_subdev_state_get_format(sd_state,
> - RKISP1_ISP_PAD_SOURCE_VIDEO);
> + RKISP1_ISP_PAD_SOURCE_VIDEO);
> src_crop = v4l2_subdev_state_get_crop(sd_state,
> - RKISP1_ISP_PAD_SOURCE_VIDEO);
> + RKISP1_ISP_PAD_SOURCE_VIDEO);
>
> /*
> * Media bus code. The ISP can operate in pass-through mode (Bayer in,
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
2023-10-13 15:37:10 +08:00
|
|
|
*v4l2_subdev_state_get_format(sd_state, 0) = *mbus_fmt;
|
2021-11-02 03:52:51 +08:00
|
|
|
goto out;
|
|
|
|
}
|
2017-06-08 02:33:56 +08:00
|
|
|
|
2018-07-04 21:04:38 +08:00
|
|
|
if (new_mode != sensor->current_mode) {
|
2022-05-13 22:14:05 +08:00
|
|
|
sensor->current_fr = new_mode->def_fps;
|
2018-07-04 21:04:38 +08:00
|
|
|
sensor->current_mode = new_mode;
|
|
|
|
sensor->pending_mode_change = true;
|
|
|
|
}
|
2018-12-03 16:44:16 +08:00
|
|
|
if (mbus_fmt->code != sensor->fmt.code)
|
2018-08-16 17:46:53 +08:00
|
|
|
sensor->pending_fmt_change = true;
|
2018-12-03 16:44:16 +08:00
|
|
|
|
2021-11-02 03:52:51 +08:00
|
|
|
/* update format even if code is unchanged, resolution might change */
|
|
|
|
sensor->fmt = *mbus_fmt;
|
|
|
|
|
2022-05-13 22:13:54 +08:00
|
|
|
ov5640_update_pixel_rate(sensor);
|
|
|
|
|
2017-06-08 02:33:56 +08:00
|
|
|
out:
|
|
|
|
mutex_unlock(&sensor->lock);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2022-05-13 22:14:09 +08:00
|
|
|
static int ov5640_get_selection(struct v4l2_subdev *sd,
|
|
|
|
struct v4l2_subdev_state *sd_state,
|
|
|
|
struct v4l2_subdev_selection *sel)
|
|
|
|
{
|
|
|
|
struct ov5640_dev *sensor = to_ov5640_dev(sd);
|
|
|
|
const struct ov5640_mode_info *mode = sensor->current_mode;
|
|
|
|
const struct ov5640_timings *timings;
|
|
|
|
|
|
|
|
switch (sel->target) {
|
|
|
|
case V4L2_SEL_TGT_CROP: {
|
|
|
|
mutex_lock(&sensor->lock);
|
|
|
|
timings = ov5640_timings(sensor, mode);
|
|
|
|
sel->r = timings->analog_crop;
|
|
|
|
mutex_unlock(&sensor->lock);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
case V4L2_SEL_TGT_NATIVE_SIZE:
|
|
|
|
case V4L2_SEL_TGT_CROP_BOUNDS:
|
|
|
|
sel->r.top = 0;
|
|
|
|
sel->r.left = 0;
|
|
|
|
sel->r.width = OV5640_NATIVE_WIDTH;
|
|
|
|
sel->r.height = OV5640_NATIVE_HEIGHT;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case V4L2_SEL_TGT_CROP_DEFAULT:
|
|
|
|
sel->r.top = OV5640_PIXEL_ARRAY_TOP;
|
|
|
|
sel->r.left = OV5640_PIXEL_ARRAY_LEFT;
|
|
|
|
sel->r.width = OV5640_PIXEL_ARRAY_WIDTH;
|
|
|
|
sel->r.height = OV5640_PIXEL_ARRAY_HEIGHT;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2018-01-03 17:57:32 +08:00
|
|
|
static int ov5640_set_framefmt(struct ov5640_dev *sensor,
|
|
|
|
struct v4l2_mbus_framefmt *format)
|
|
|
|
{
|
2022-05-13 22:14:16 +08:00
|
|
|
bool is_jpeg = format->code == MEDIA_BUS_FMT_JPEG_1X8;
|
|
|
|
const struct ov5640_pixfmt *pixfmt;
|
2018-01-03 17:57:32 +08:00
|
|
|
int ret = 0;
|
|
|
|
|
2022-05-13 22:14:16 +08:00
|
|
|
pixfmt = ov5640_code_to_pixfmt(sensor, format->code);
|
2018-01-03 17:57:32 +08:00
|
|
|
|
|
|
|
/* FORMAT CONTROL00: YUV and RGB formatting */
|
2022-05-13 22:14:16 +08:00
|
|
|
ret = ov5640_write_reg(sensor, OV5640_REG_FORMAT_CONTROL00,
|
|
|
|
pixfmt->ctrl00);
|
2018-01-03 17:57:32 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* FORMAT MUX CONTROL: ISP YUV or RGB */
|
2022-05-13 22:14:16 +08:00
|
|
|
ret = ov5640_write_reg(sensor, OV5640_REG_ISP_FORMAT_MUX_CTRL,
|
|
|
|
pixfmt->mux);
|
2018-01-31 17:08:10 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* TIMING TC REG21:
|
|
|
|
* - [5]: JPEG enable
|
|
|
|
*/
|
|
|
|
ret = ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG21,
|
|
|
|
BIT(5), is_jpeg ? BIT(5) : 0);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* SYSTEM RESET02:
|
|
|
|
* - [4]: Reset JFIFO
|
|
|
|
* - [3]: Reset SFIFO
|
|
|
|
* - [2]: Reset JPEG
|
|
|
|
*/
|
|
|
|
ret = ov5640_mod_reg(sensor, OV5640_REG_SYS_RESET02,
|
|
|
|
BIT(4) | BIT(3) | BIT(2),
|
|
|
|
is_jpeg ? 0 : (BIT(4) | BIT(3) | BIT(2)));
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* CLOCK ENABLE02:
|
|
|
|
* - [5]: Enable JPEG 2x clock
|
|
|
|
* - [3]: Enable JPEG clock
|
|
|
|
*/
|
|
|
|
return ov5640_mod_reg(sensor, OV5640_REG_SYS_CLOCK_ENABLE02,
|
|
|
|
BIT(5) | BIT(3),
|
|
|
|
is_jpeg ? (BIT(5) | BIT(3)) : 0);
|
2018-01-03 17:57:32 +08:00
|
|
|
}
|
2017-06-08 02:33:56 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Sensor Controls.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int ov5640_set_ctrl_hue(struct ov5640_dev *sensor, int value)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (value) {
|
|
|
|
ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0,
|
|
|
|
BIT(0), BIT(0));
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
ret = ov5640_write_reg16(sensor, OV5640_REG_SDE_CTRL1, value);
|
|
|
|
} else {
|
|
|
|
ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0, BIT(0), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ov5640_set_ctrl_contrast(struct ov5640_dev *sensor, int value)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (value) {
|
|
|
|
ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0,
|
|
|
|
BIT(2), BIT(2));
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
ret = ov5640_write_reg(sensor, OV5640_REG_SDE_CTRL5,
|
|
|
|
value & 0xff);
|
|
|
|
} else {
|
|
|
|
ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0, BIT(2), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ov5640_set_ctrl_saturation(struct ov5640_dev *sensor, int value)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (value) {
|
|
|
|
ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0,
|
|
|
|
BIT(1), BIT(1));
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
ret = ov5640_write_reg(sensor, OV5640_REG_SDE_CTRL3,
|
|
|
|
value & 0xff);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
ret = ov5640_write_reg(sensor, OV5640_REG_SDE_CTRL4,
|
|
|
|
value & 0xff);
|
|
|
|
} else {
|
|
|
|
ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0, BIT(1), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ov5640_set_ctrl_white_balance(struct ov5640_dev *sensor, int awb)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = ov5640_mod_reg(sensor, OV5640_REG_AWB_MANUAL_CTRL,
|
|
|
|
BIT(0), awb ? 0 : 1);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
if (!awb) {
|
|
|
|
u16 red = (u16)sensor->ctrls.red_balance->val;
|
|
|
|
u16 blue = (u16)sensor->ctrls.blue_balance->val;
|
|
|
|
|
|
|
|
ret = ov5640_write_reg16(sensor, OV5640_REG_AWB_R_GAIN, red);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
ret = ov5640_write_reg16(sensor, OV5640_REG_AWB_B_GAIN, blue);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-09-11 21:48:18 +08:00
|
|
|
static int ov5640_set_ctrl_exposure(struct ov5640_dev *sensor,
|
|
|
|
enum v4l2_exposure_auto_type auto_exposure)
|
2017-06-08 02:33:56 +08:00
|
|
|
{
|
|
|
|
struct ov5640_ctrls *ctrls = &sensor->ctrls;
|
2018-09-11 21:48:18 +08:00
|
|
|
bool auto_exp = (auto_exposure == V4L2_EXPOSURE_AUTO);
|
2017-06-08 02:33:56 +08:00
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (ctrls->auto_exp->is_new) {
|
2018-09-11 21:48:18 +08:00
|
|
|
ret = ov5640_set_autoexposure(sensor, auto_exp);
|
2017-06-08 02:33:56 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-09-11 21:48:18 +08:00
|
|
|
if (!auto_exp && ctrls->exposure->is_new) {
|
2017-06-08 02:33:56 +08:00
|
|
|
u16 max_exp;
|
|
|
|
|
|
|
|
ret = ov5640_read_reg16(sensor, OV5640_REG_AEC_PK_VTS,
|
|
|
|
&max_exp);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
ret = ov5640_get_vts(sensor);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
max_exp += ret;
|
2018-01-22 18:06:40 +08:00
|
|
|
ret = 0;
|
2017-06-08 02:33:56 +08:00
|
|
|
|
|
|
|
if (ctrls->exposure->val < max_exp)
|
|
|
|
ret = ov5640_set_exposure(sensor, ctrls->exposure->val);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-09-11 21:48:18 +08:00
|
|
|
static int ov5640_set_ctrl_gain(struct ov5640_dev *sensor, bool auto_gain)
|
2017-06-08 02:33:56 +08:00
|
|
|
{
|
|
|
|
struct ov5640_ctrls *ctrls = &sensor->ctrls;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (ctrls->auto_gain->is_new) {
|
2018-09-11 21:48:18 +08:00
|
|
|
ret = ov5640_set_autogain(sensor, auto_gain);
|
2017-06-08 02:33:56 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-09-11 21:48:18 +08:00
|
|
|
if (!auto_gain && ctrls->gain->is_new)
|
|
|
|
ret = ov5640_set_gain(sensor, ctrls->gain->val);
|
2017-06-08 02:33:56 +08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-01-18 16:52:01 +08:00
|
|
|
static const char * const test_pattern_menu[] = {
|
|
|
|
"Disabled",
|
|
|
|
"Color bars",
|
2019-01-18 16:52:04 +08:00
|
|
|
"Color bars w/ rolling bar",
|
|
|
|
"Color squares",
|
|
|
|
"Color squares w/ rolling bar",
|
2019-01-18 16:52:01 +08:00
|
|
|
};
|
|
|
|
|
2019-01-18 16:52:02 +08:00
|
|
|
#define OV5640_TEST_ENABLE BIT(7)
|
|
|
|
#define OV5640_TEST_ROLLING BIT(6) /* rolling horizontal bar */
|
|
|
|
#define OV5640_TEST_TRANSPARENT BIT(5)
|
|
|
|
#define OV5640_TEST_SQUARE_BW BIT(4) /* black & white squares */
|
|
|
|
#define OV5640_TEST_BAR_STANDARD (0 << 2)
|
|
|
|
#define OV5640_TEST_BAR_VERT_CHANGE_1 (1 << 2)
|
|
|
|
#define OV5640_TEST_BAR_HOR_CHANGE (2 << 2)
|
|
|
|
#define OV5640_TEST_BAR_VERT_CHANGE_2 (3 << 2)
|
|
|
|
#define OV5640_TEST_BAR (0 << 0)
|
|
|
|
#define OV5640_TEST_RANDOM (1 << 0)
|
|
|
|
#define OV5640_TEST_SQUARE (2 << 0)
|
|
|
|
#define OV5640_TEST_BLACK (3 << 0)
|
|
|
|
|
|
|
|
static const u8 test_pattern_val[] = {
|
|
|
|
0,
|
2019-01-18 16:52:03 +08:00
|
|
|
OV5640_TEST_ENABLE | OV5640_TEST_BAR_VERT_CHANGE_1 |
|
2019-01-18 16:52:02 +08:00
|
|
|
OV5640_TEST_BAR,
|
2019-01-18 16:52:04 +08:00
|
|
|
OV5640_TEST_ENABLE | OV5640_TEST_ROLLING |
|
|
|
|
OV5640_TEST_BAR_VERT_CHANGE_1 | OV5640_TEST_BAR,
|
|
|
|
OV5640_TEST_ENABLE | OV5640_TEST_SQUARE,
|
|
|
|
OV5640_TEST_ENABLE | OV5640_TEST_ROLLING | OV5640_TEST_SQUARE,
|
2019-01-18 16:52:02 +08:00
|
|
|
};
|
|
|
|
|
2017-06-08 02:33:56 +08:00
|
|
|
static int ov5640_set_ctrl_test_pattern(struct ov5640_dev *sensor, int value)
|
|
|
|
{
|
2019-01-18 16:52:02 +08:00
|
|
|
return ov5640_write_reg(sensor, OV5640_REG_PRE_ISP_TEST_SET1,
|
|
|
|
test_pattern_val[value]);
|
2017-06-08 02:33:56 +08:00
|
|
|
}
|
|
|
|
|
2018-04-16 20:36:51 +08:00
|
|
|
static int ov5640_set_ctrl_light_freq(struct ov5640_dev *sensor, int value)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = ov5640_mod_reg(sensor, OV5640_REG_HZ5060_CTRL01, BIT(7),
|
|
|
|
(value == V4L2_CID_POWER_LINE_FREQUENCY_AUTO) ?
|
|
|
|
0 : BIT(7));
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return ov5640_mod_reg(sensor, OV5640_REG_HZ5060_CTRL00, BIT(2),
|
|
|
|
(value == V4L2_CID_POWER_LINE_FREQUENCY_50HZ) ?
|
|
|
|
BIT(2) : 0);
|
|
|
|
}
|
|
|
|
|
2018-06-18 18:29:17 +08:00
|
|
|
static int ov5640_set_ctrl_hflip(struct ov5640_dev *sensor, int value)
|
|
|
|
{
|
|
|
|
/*
|
2018-06-18 18:29:19 +08:00
|
|
|
* If sensor is mounted upside down, mirror logic is inversed.
|
|
|
|
*
|
2018-06-18 18:29:17 +08:00
|
|
|
* Sensor is a BSI (Back Side Illuminated) one,
|
|
|
|
* so image captured is physically mirrored.
|
|
|
|
* This is why mirror logic is inversed in
|
|
|
|
* order to cancel this mirror effect.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* TIMING TC REG21:
|
|
|
|
* - [2]: ISP mirror
|
|
|
|
* - [1]: Sensor mirror
|
|
|
|
*/
|
|
|
|
return ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG21,
|
|
|
|
BIT(2) | BIT(1),
|
2018-06-18 18:29:19 +08:00
|
|
|
(!(value ^ sensor->upside_down)) ?
|
|
|
|
(BIT(2) | BIT(1)) : 0);
|
2018-06-18 18:29:17 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ov5640_set_ctrl_vflip(struct ov5640_dev *sensor, int value)
|
|
|
|
{
|
2018-06-18 18:29:19 +08:00
|
|
|
/* If sensor is mounted upside down, flip logic is inversed */
|
|
|
|
|
2018-06-18 18:29:17 +08:00
|
|
|
/*
|
|
|
|
* TIMING TC REG20:
|
|
|
|
* - [2]: ISP vflip
|
|
|
|
* - [1]: Sensor vflip
|
|
|
|
*/
|
|
|
|
return ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG20,
|
|
|
|
BIT(2) | BIT(1),
|
2018-06-18 18:29:19 +08:00
|
|
|
(value ^ sensor->upside_down) ?
|
|
|
|
(BIT(2) | BIT(1)) : 0);
|
2018-06-18 18:29:17 +08:00
|
|
|
}
|
|
|
|
|
2022-05-13 22:14:04 +08:00
|
|
|
static int ov5640_set_ctrl_vblank(struct ov5640_dev *sensor, int value)
|
|
|
|
{
|
|
|
|
const struct ov5640_mode_info *mode = sensor->current_mode;
|
|
|
|
|
|
|
|
/* Update the VTOT timing register value. */
|
|
|
|
return ov5640_write_reg16(sensor, OV5640_REG_TIMING_VTS,
|
|
|
|
mode->height + value);
|
|
|
|
}
|
|
|
|
|
2017-06-08 02:33:56 +08:00
|
|
|
static int ov5640_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
|
|
|
|
{
|
|
|
|
struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
|
|
|
|
struct ov5640_dev *sensor = to_ov5640_dev(sd);
|
|
|
|
int val;
|
|
|
|
|
|
|
|
/* v4l2_ctrl_lock() locks our own mutex */
|
|
|
|
|
2022-07-21 15:41:38 +08:00
|
|
|
if (!pm_runtime_get_if_in_use(&sensor->i2c_client->dev))
|
|
|
|
return 0;
|
|
|
|
|
2017-06-08 02:33:56 +08:00
|
|
|
switch (ctrl->id) {
|
|
|
|
case V4L2_CID_AUTOGAIN:
|
|
|
|
val = ov5640_get_gain(sensor);
|
|
|
|
if (val < 0)
|
|
|
|
return val;
|
|
|
|
sensor->ctrls.gain->val = val;
|
|
|
|
break;
|
|
|
|
case V4L2_CID_EXPOSURE_AUTO:
|
|
|
|
val = ov5640_get_exposure(sensor);
|
|
|
|
if (val < 0)
|
|
|
|
return val;
|
|
|
|
sensor->ctrls.exposure->val = val;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2023-01-16 01:30:10 +08:00
|
|
|
pm_runtime_mark_last_busy(&sensor->i2c_client->dev);
|
2022-07-21 15:41:38 +08:00
|
|
|
pm_runtime_put_autosuspend(&sensor->i2c_client->dev);
|
|
|
|
|
2017-06-08 02:33:56 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl)
|
|
|
|
{
|
|
|
|
struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
|
|
|
|
struct ov5640_dev *sensor = to_ov5640_dev(sd);
|
2022-05-13 22:14:04 +08:00
|
|
|
const struct ov5640_mode_info *mode = sensor->current_mode;
|
|
|
|
const struct ov5640_timings *timings;
|
|
|
|
unsigned int exp_max;
|
2017-06-08 02:33:56 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* v4l2_ctrl_lock() locks our own mutex */
|
|
|
|
|
2022-05-13 22:14:04 +08:00
|
|
|
switch (ctrl->id) {
|
|
|
|
case V4L2_CID_VBLANK:
|
|
|
|
/* Update the exposure range to the newly programmed vblank. */
|
|
|
|
timings = ov5640_timings(sensor, mode);
|
|
|
|
exp_max = mode->height + ctrl->val - 4;
|
|
|
|
__v4l2_ctrl_modify_range(sensor->ctrls.exposure,
|
|
|
|
sensor->ctrls.exposure->minimum,
|
|
|
|
exp_max, sensor->ctrls.exposure->step,
|
|
|
|
timings->vblank_def);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-06-08 02:33:56 +08:00
|
|
|
/*
|
|
|
|
* If the device is not powered up by the host driver do
|
|
|
|
* not apply any controls to H/W at this time. Instead
|
2022-07-21 15:41:38 +08:00
|
|
|
* the controls will be restored at start streaming time.
|
2017-06-08 02:33:56 +08:00
|
|
|
*/
|
2022-07-21 15:41:38 +08:00
|
|
|
if (!pm_runtime_get_if_in_use(&sensor->i2c_client->dev))
|
2017-06-08 02:33:56 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
switch (ctrl->id) {
|
|
|
|
case V4L2_CID_AUTOGAIN:
|
|
|
|
ret = ov5640_set_ctrl_gain(sensor, ctrl->val);
|
|
|
|
break;
|
|
|
|
case V4L2_CID_EXPOSURE_AUTO:
|
|
|
|
ret = ov5640_set_ctrl_exposure(sensor, ctrl->val);
|
|
|
|
break;
|
|
|
|
case V4L2_CID_AUTO_WHITE_BALANCE:
|
|
|
|
ret = ov5640_set_ctrl_white_balance(sensor, ctrl->val);
|
|
|
|
break;
|
|
|
|
case V4L2_CID_HUE:
|
|
|
|
ret = ov5640_set_ctrl_hue(sensor, ctrl->val);
|
|
|
|
break;
|
|
|
|
case V4L2_CID_CONTRAST:
|
|
|
|
ret = ov5640_set_ctrl_contrast(sensor, ctrl->val);
|
|
|
|
break;
|
|
|
|
case V4L2_CID_SATURATION:
|
|
|
|
ret = ov5640_set_ctrl_saturation(sensor, ctrl->val);
|
|
|
|
break;
|
|
|
|
case V4L2_CID_TEST_PATTERN:
|
|
|
|
ret = ov5640_set_ctrl_test_pattern(sensor, ctrl->val);
|
|
|
|
break;
|
2018-04-16 20:36:51 +08:00
|
|
|
case V4L2_CID_POWER_LINE_FREQUENCY:
|
|
|
|
ret = ov5640_set_ctrl_light_freq(sensor, ctrl->val);
|
|
|
|
break;
|
2018-06-18 18:29:17 +08:00
|
|
|
case V4L2_CID_HFLIP:
|
|
|
|
ret = ov5640_set_ctrl_hflip(sensor, ctrl->val);
|
|
|
|
break;
|
|
|
|
case V4L2_CID_VFLIP:
|
|
|
|
ret = ov5640_set_ctrl_vflip(sensor, ctrl->val);
|
|
|
|
break;
|
2022-05-13 22:14:04 +08:00
|
|
|
case V4L2_CID_VBLANK:
|
|
|
|
ret = ov5640_set_ctrl_vblank(sensor, ctrl->val);
|
|
|
|
break;
|
2017-06-08 02:33:56 +08:00
|
|
|
default:
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2023-01-16 01:30:10 +08:00
|
|
|
pm_runtime_mark_last_busy(&sensor->i2c_client->dev);
|
2022-07-21 15:41:38 +08:00
|
|
|
pm_runtime_put_autosuspend(&sensor->i2c_client->dev);
|
|
|
|
|
2017-06-08 02:33:56 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct v4l2_ctrl_ops ov5640_ctrl_ops = {
|
|
|
|
.g_volatile_ctrl = ov5640_g_volatile_ctrl,
|
|
|
|
.s_ctrl = ov5640_s_ctrl,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int ov5640_init_controls(struct ov5640_dev *sensor)
|
|
|
|
{
|
2022-05-13 22:13:49 +08:00
|
|
|
const struct ov5640_mode_info *mode = sensor->current_mode;
|
2017-06-08 02:33:56 +08:00
|
|
|
const struct v4l2_ctrl_ops *ops = &ov5640_ctrl_ops;
|
|
|
|
struct ov5640_ctrls *ctrls = &sensor->ctrls;
|
|
|
|
struct v4l2_ctrl_handler *hdl = &ctrls->handler;
|
2022-05-13 22:14:10 +08:00
|
|
|
struct v4l2_fwnode_device_properties props;
|
2022-05-13 22:14:03 +08:00
|
|
|
const struct ov5640_timings *timings;
|
2022-05-13 22:14:04 +08:00
|
|
|
unsigned int max_vblank;
|
2022-05-13 22:14:03 +08:00
|
|
|
unsigned int hblank;
|
2017-06-08 02:33:56 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
v4l2_ctrl_handler_init(hdl, 32);
|
|
|
|
|
|
|
|
/* we can use our own mutex for the ctrl lock */
|
|
|
|
hdl->lock = &sensor->lock;
|
|
|
|
|
2019-10-09 20:35:08 +08:00
|
|
|
/* Clock related controls */
|
|
|
|
ctrls->pixel_rate = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_PIXEL_RATE,
|
2022-05-13 22:13:49 +08:00
|
|
|
ov5640_pixel_rates[OV5640_NUM_PIXEL_RATES - 1],
|
|
|
|
ov5640_pixel_rates[0], 1,
|
|
|
|
ov5640_pixel_rates[mode->pixel_rate]);
|
2019-10-09 20:35:08 +08:00
|
|
|
|
2022-05-13 22:13:53 +08:00
|
|
|
ctrls->link_freq = v4l2_ctrl_new_int_menu(hdl, ops,
|
|
|
|
V4L2_CID_LINK_FREQ,
|
|
|
|
ARRAY_SIZE(ov5640_csi2_link_freqs) - 1,
|
|
|
|
OV5640_DEFAULT_LINK_FREQ,
|
|
|
|
ov5640_csi2_link_freqs);
|
|
|
|
|
2022-05-13 22:14:03 +08:00
|
|
|
timings = ov5640_timings(sensor, mode);
|
|
|
|
hblank = timings->htot - mode->width;
|
|
|
|
ctrls->hblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HBLANK, hblank,
|
|
|
|
hblank, 1, hblank);
|
|
|
|
|
2022-05-13 22:14:04 +08:00
|
|
|
max_vblank = OV5640_MAX_VTS - mode->height;
|
|
|
|
ctrls->vblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK,
|
|
|
|
OV5640_MIN_VBLANK, max_vblank,
|
|
|
|
1, timings->vblank_def);
|
|
|
|
|
2017-06-08 02:33:56 +08:00
|
|
|
/* Auto/manual white balance */
|
|
|
|
ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops,
|
|
|
|
V4L2_CID_AUTO_WHITE_BALANCE,
|
|
|
|
0, 1, 1, 1);
|
|
|
|
ctrls->blue_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BLUE_BALANCE,
|
|
|
|
0, 4095, 1, 0);
|
|
|
|
ctrls->red_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_RED_BALANCE,
|
|
|
|
0, 4095, 1, 0);
|
|
|
|
/* Auto/manual exposure */
|
|
|
|
ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops,
|
|
|
|
V4L2_CID_EXPOSURE_AUTO,
|
|
|
|
V4L2_EXPOSURE_MANUAL, 0,
|
|
|
|
V4L2_EXPOSURE_AUTO);
|
|
|
|
ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE,
|
|
|
|
0, 65535, 1, 0);
|
|
|
|
/* Auto/manual gain */
|
|
|
|
ctrls->auto_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTOGAIN,
|
|
|
|
0, 1, 1, 1);
|
media: ov5640: Fix analogue gain control
Gain control is badly documented in publicly available (including
leaked) documentation.
There is an AGC pre-gain in register 0x3a13, expressed as a 6-bit value
(plus an enable bit in bit 6). The driver hardcodes it to 0x43, which
one application note states is equal to x1.047. The documentation also
states that 0x40 is equel to x1.000. The pre-gain thus seems to be
expressed as in 1/64 increments, and thus ranges from x1.00 to x1.984.
What the pre-gain does is however unspecified.
There is then an AGC gain limit, in registers 0x3a18 and 0x3a19,
expressed as a 10-bit "real gain format" value. One application note
sets it to 0x00f8 and states it is equal to x15.5, so it appears to be
expressed in 1/16 increments, up to x63.9375.
The manual gain is stored in registers 0x350a and 0x350b, also as a
10-bit "real gain format" value. It is documented in the application
note as a Q6.4 values, up to x63.9375.
One version of the datasheet indicates that the sensor supports a
digital gain:
The OV5640 supports 1/2/4 digital gain. Normally, the gain is
controlled automatically by the automatic gain control (AGC) block.
It isn't clear how that would be controlled manually.
There appears to be no indication regarding whether the gain controlled
through registers 0x350a and 0x350b is an analogue gain only or also
includes digital gain. The words "real gain" don't necessarily mean
"combined analogue and digital gains". Some OmniVision sensors (such as
the OV8858) are documented as supoprting different formats for the gain
values, selectable through a register bit, and they are called "real
gain format" and "sensor gain format". For that sensor, we have (one of)
the gain registers documented as
0x3503[2]=0, gain[7:0] is real gain format, where low 4 bits are
fraction bits, for example, 0x10 is 1x gain, 0x28 is 2.5x gain
If 0x3503[2]=1, gain[7:0] is sensor gain format, gain[7:4] is coarse
gain, 00000: 1x, 00001: 2x, 00011: 4x, 00111: 8x, gain[7] is 1,
gain[3:0] is fine gain. For example, 0x10 is 1x gain, 0x30 is 2x gain,
0x70 is 4x gain
(The second part of the text makes little sense)
"Real gain" may thus refer to the combination of the coarse and fine
analogue gains as a single value.
The OV5640 0x350a and 0x350b registers thus appear to control analogue
gain. The driver incorrectly uses V4L2_CID_GAIN as V4L2 has a specific
control for analogue gain, V4L2_CID_ANALOGUE_GAIN. Use it.
If registers 0x350a and 0x350b are later found to control digital gain
as well, the driver could then restrict the range of the analogue gain
control value to lower than x64 and add a separate digital gain control.
Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Jai Luthra <j-luthra@ti.com>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
2022-11-28 16:02:01 +08:00
|
|
|
ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN,
|
2017-06-08 02:33:56 +08:00
|
|
|
0, 1023, 1, 0);
|
|
|
|
|
|
|
|
ctrls->saturation = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION,
|
|
|
|
0, 255, 1, 64);
|
|
|
|
ctrls->hue = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HUE,
|
|
|
|
0, 359, 1, 0);
|
|
|
|
ctrls->contrast = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST,
|
|
|
|
0, 255, 1, 0);
|
|
|
|
ctrls->test_pattern =
|
|
|
|
v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN,
|
|
|
|
ARRAY_SIZE(test_pattern_menu) - 1,
|
|
|
|
0, 0, test_pattern_menu);
|
2018-06-18 18:29:17 +08:00
|
|
|
ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP,
|
|
|
|
0, 1, 1, 0);
|
|
|
|
ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP,
|
|
|
|
0, 1, 1, 0);
|
2017-06-08 02:33:56 +08:00
|
|
|
|
2018-04-16 20:36:51 +08:00
|
|
|
ctrls->light_freq =
|
|
|
|
v4l2_ctrl_new_std_menu(hdl, ops,
|
|
|
|
V4L2_CID_POWER_LINE_FREQUENCY,
|
|
|
|
V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0,
|
|
|
|
V4L2_CID_POWER_LINE_FREQUENCY_50HZ);
|
|
|
|
|
2017-06-08 02:33:56 +08:00
|
|
|
if (hdl->error) {
|
|
|
|
ret = hdl->error;
|
|
|
|
goto free_ctrls;
|
|
|
|
}
|
|
|
|
|
2022-05-13 22:14:10 +08:00
|
|
|
ret = v4l2_fwnode_device_parse(&sensor->i2c_client->dev, &props);
|
|
|
|
if (ret)
|
|
|
|
goto free_ctrls;
|
|
|
|
|
|
|
|
if (props.rotation == 180)
|
|
|
|
sensor->upside_down = true;
|
|
|
|
|
|
|
|
ret = v4l2_ctrl_new_fwnode_properties(hdl, ops, &props);
|
|
|
|
if (ret)
|
|
|
|
goto free_ctrls;
|
|
|
|
|
2019-10-09 20:35:08 +08:00
|
|
|
ctrls->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY;
|
2022-05-13 22:13:53 +08:00
|
|
|
ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
|
2022-05-13 22:14:03 +08:00
|
|
|
ctrls->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
|
2017-06-08 02:33:56 +08:00
|
|
|
ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE;
|
|
|
|
ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE;
|
|
|
|
|
|
|
|
v4l2_ctrl_auto_cluster(3, &ctrls->auto_wb, 0, false);
|
|
|
|
v4l2_ctrl_auto_cluster(2, &ctrls->auto_gain, 0, true);
|
|
|
|
v4l2_ctrl_auto_cluster(2, &ctrls->auto_exp, 1, true);
|
|
|
|
|
|
|
|
sensor->sd.ctrl_handler = hdl;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
free_ctrls:
|
|
|
|
v4l2_ctrl_handler_free(hdl);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ov5640_enum_frame_size(struct v4l2_subdev *sd,
|
media: v4l2-subdev: add subdev-wide state struct
We have 'struct v4l2_subdev_pad_config' which contains configuration for
a single pad used for the TRY functionality, and an array of those
structs is passed to various v4l2_subdev_pad_ops.
I was working on subdev internal routing between pads, and realized that
there's no way to add TRY functionality for routes, which is not pad
specific configuration. Adding a separate struct for try-route config
wouldn't work either, as e.g. set-fmt needs to know the try-route
configuration to propagate the settings.
This patch adds a new struct, 'struct v4l2_subdev_state' (which at the
moment only contains the v4l2_subdev_pad_config array) and the new
struct is used in most of the places where v4l2_subdev_pad_config was
used. All v4l2_subdev_pad_ops functions taking v4l2_subdev_pad_config
are changed to instead take v4l2_subdev_state.
The changes to drivers/media/v4l2-core/v4l2-subdev.c and
include/media/v4l2-subdev.h were written by hand, and all the driver
changes were done with the semantic patch below. The spatch needs to be
applied to a select list of directories. I used the following shell
commands to apply the spatch:
dirs="drivers/media/i2c drivers/media/platform drivers/media/usb drivers/media/test-drivers/vimc drivers/media/pci drivers/staging/media"
for dir in $dirs; do spatch -j8 --dir --include-headers --no-show-diff --in-place --sp-file v4l2-subdev-state.cocci $dir; done
Note that Coccinelle chokes on a few drivers (gcc extensions?). With
minor changes we can make Coccinelle run fine, and these changes can be
reverted after spatch. The diff for these changes is:
For drivers/media/i2c/s5k5baf.c:
@@ -1481,7 +1481,7 @@ static int s5k5baf_set_selection(struct v4l2_subdev *sd,
&s5k5baf_cis_rect,
v4l2_subdev_get_try_crop(sd, cfg, PAD_CIS),
v4l2_subdev_get_try_compose(sd, cfg, PAD_CIS),
- v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT)
+ v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT),
};
s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r);
return 0;
For drivers/media/platform/s3c-camif/camif-capture.c:
@@ -1230,7 +1230,7 @@ static int s3c_camif_subdev_get_fmt(struct v4l2_subdev *sd,
*mf = camif->mbus_fmt;
break;
- case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ case CAMIF_SD_PAD_SOURCE_C:
/* crop rectangle at camera interface input */
mf->width = camif->camif_crop.width;
mf->height = camif->camif_crop.height;
@@ -1332,7 +1332,7 @@ static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd,
}
break;
- case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ case CAMIF_SD_PAD_SOURCE_C:
/* Pixel format can be only changed on the sink pad. */
mf->code = camif->mbus_fmt.code;
mf->width = crop->width;
The semantic patch is:
// <smpl>
// Change function parameter
@@
identifier func;
identifier cfg;
@@
func(...,
- struct v4l2_subdev_pad_config *cfg
+ struct v4l2_subdev_state *sd_state
, ...)
{
<...
- cfg
+ sd_state
...>
}
// Change function declaration parameter
@@
identifier func;
identifier cfg;
type T;
@@
T func(...,
- struct v4l2_subdev_pad_config *cfg
+ struct v4l2_subdev_state *sd_state
, ...);
// Change function return value
@@
identifier func;
@@
- struct v4l2_subdev_pad_config
+ struct v4l2_subdev_state
*func(...)
{
...
}
// Change function declaration return value
@@
identifier func;
@@
- struct v4l2_subdev_pad_config
+ struct v4l2_subdev_state
*func(...);
// Some drivers pass a local pad_cfg for a single pad to a called function. Wrap it
// inside a pad_state.
@@
identifier func;
identifier pad_cfg;
@@
func(...)
{
...
struct v4l2_subdev_pad_config pad_cfg;
+ struct v4l2_subdev_state pad_state = { .pads = &pad_cfg };
<+...
(
v4l2_subdev_call
|
sensor_call
|
isi_try_fse
|
isc_try_fse
|
saa_call_all
)
(...,
- &pad_cfg
+ &pad_state
,...)
...+>
}
// If the function uses fields from pad_config, access via state->pads
@@
identifier func;
identifier state;
@@
func(...,
struct v4l2_subdev_state *state
, ...)
{
<...
(
- state->try_fmt
+ state->pads->try_fmt
|
- state->try_crop
+ state->pads->try_crop
|
- state->try_compose
+ state->pads->try_compose
)
...>
}
// If the function accesses the filehandle, use fh->state instead
@@
struct v4l2_subdev_fh *fh;
@@
- fh->pad
+ fh->state
@@
struct v4l2_subdev_fh fh;
@@
- fh.pad
+ fh.state
// Start of vsp1 specific
@@
@@
struct vsp1_entity {
...
- struct v4l2_subdev_pad_config *config;
+ struct v4l2_subdev_state *config;
...
};
@@
symbol entity;
@@
vsp1_entity_init(...)
{
...
entity->config =
- v4l2_subdev_alloc_pad_config
+ v4l2_subdev_alloc_state
(&entity->subdev);
...
}
@@
symbol entity;
@@
vsp1_entity_destroy(...)
{
...
- v4l2_subdev_free_pad_config
+ v4l2_subdev_free_state
(entity->config);
...
}
@exists@
identifier func =~ "(^vsp1.*)|(hsit_set_format)|(sru_enum_frame_size)|(sru_set_format)|(uif_get_selection)|(uif_set_selection)|(uds_enum_frame_size)|(uds_set_format)|(brx_set_format)|(brx_get_selection)|(histo_get_selection)|(histo_set_selection)|(brx_set_selection)";
symbol config;
@@
func(...) {
...
- struct v4l2_subdev_pad_config *config;
+ struct v4l2_subdev_state *config;
...
}
// End of vsp1 specific
// Start of rcar specific
@@
identifier sd;
identifier pad_cfg;
@@
rvin_try_format(...)
{
...
- struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_state *sd_state;
...
- pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+ sd_state = v4l2_subdev_alloc_state(sd);
<...
- pad_cfg
+ sd_state
...>
- v4l2_subdev_free_pad_config(pad_cfg);
+ v4l2_subdev_free_state(sd_state);
...
}
// End of rcar specific
// Start of rockchip specific
@@
identifier func =~ "(rkisp1_rsz_get_pad_fmt)|(rkisp1_rsz_get_pad_crop)|(rkisp1_rsz_register)";
symbol rsz;
symbol pad_cfg;
@@
func(...)
{
+ struct v4l2_subdev_state state = { .pads = rsz->pad_cfg };
...
- rsz->pad_cfg
+ &state
...
}
@@
identifier func =~ "(rkisp1_isp_get_pad_fmt)|(rkisp1_isp_get_pad_crop)";
symbol isp;
symbol pad_cfg;
@@
func(...)
{
+ struct v4l2_subdev_state state = { .pads = isp->pad_cfg };
...
- isp->pad_cfg
+ &state
...
}
@@
symbol rkisp1;
symbol isp;
symbol pad_cfg;
@@
rkisp1_isp_register(...)
{
+ struct v4l2_subdev_state state = { .pads = rkisp1->isp.pad_cfg };
...
- rkisp1->isp.pad_cfg
+ &state
...
}
// End of rockchip specific
// Start of tegra-video specific
@@
identifier sd;
identifier pad_cfg;
@@
__tegra_channel_try_format(...)
{
...
- struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_state *sd_state;
...
- pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+ sd_state = v4l2_subdev_alloc_state(sd);
<...
- pad_cfg
+ sd_state
...>
- v4l2_subdev_free_pad_config(pad_cfg);
+ v4l2_subdev_free_state(sd_state);
...
}
@@
identifier sd_state;
@@
__tegra_channel_try_format(...)
{
...
struct v4l2_subdev_state *sd_state;
<...
- sd_state->try_crop
+ sd_state->pads->try_crop
...>
}
// End of tegra-video specific
// </smpl>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2021-06-10 22:55:58 +08:00
|
|
|
struct v4l2_subdev_state *sd_state,
|
2017-06-08 02:33:56 +08:00
|
|
|
struct v4l2_subdev_frame_size_enum *fse)
|
|
|
|
{
|
2022-05-13 22:14:15 +08:00
|
|
|
struct ov5640_dev *sensor = to_ov5640_dev(sd);
|
|
|
|
u32 bpp = ov5640_code_to_bpp(sensor, fse->code);
|
2022-05-13 22:14:13 +08:00
|
|
|
unsigned int index = fse->index;
|
|
|
|
|
2017-06-08 02:33:56 +08:00
|
|
|
if (fse->pad != 0)
|
|
|
|
return -EINVAL;
|
2022-05-13 22:14:13 +08:00
|
|
|
if (!bpp)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* Only low-resolution modes are supported for 24bpp formats. */
|
|
|
|
if (bpp == 24 && index >= OV5640_MODE_720P_1280_720)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* FIXME: Low resolution modes don't work in 8bpp formats. */
|
|
|
|
if (bpp == 8)
|
|
|
|
index += OV5640_MODE_720P_1280_720;
|
|
|
|
|
|
|
|
if (index >= OV5640_NUM_MODES)
|
2017-06-08 02:33:56 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
2022-05-13 22:14:13 +08:00
|
|
|
fse->min_width = ov5640_mode_data[index].width;
|
2018-02-01 16:44:06 +08:00
|
|
|
fse->max_width = fse->min_width;
|
2022-05-13 22:14:13 +08:00
|
|
|
fse->min_height = ov5640_mode_data[index].height;
|
2018-02-01 16:44:06 +08:00
|
|
|
fse->max_height = fse->min_height;
|
2017-06-08 02:33:56 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ov5640_enum_frame_interval(
|
|
|
|
struct v4l2_subdev *sd,
|
media: v4l2-subdev: add subdev-wide state struct
We have 'struct v4l2_subdev_pad_config' which contains configuration for
a single pad used for the TRY functionality, and an array of those
structs is passed to various v4l2_subdev_pad_ops.
I was working on subdev internal routing between pads, and realized that
there's no way to add TRY functionality for routes, which is not pad
specific configuration. Adding a separate struct for try-route config
wouldn't work either, as e.g. set-fmt needs to know the try-route
configuration to propagate the settings.
This patch adds a new struct, 'struct v4l2_subdev_state' (which at the
moment only contains the v4l2_subdev_pad_config array) and the new
struct is used in most of the places where v4l2_subdev_pad_config was
used. All v4l2_subdev_pad_ops functions taking v4l2_subdev_pad_config
are changed to instead take v4l2_subdev_state.
The changes to drivers/media/v4l2-core/v4l2-subdev.c and
include/media/v4l2-subdev.h were written by hand, and all the driver
changes were done with the semantic patch below. The spatch needs to be
applied to a select list of directories. I used the following shell
commands to apply the spatch:
dirs="drivers/media/i2c drivers/media/platform drivers/media/usb drivers/media/test-drivers/vimc drivers/media/pci drivers/staging/media"
for dir in $dirs; do spatch -j8 --dir --include-headers --no-show-diff --in-place --sp-file v4l2-subdev-state.cocci $dir; done
Note that Coccinelle chokes on a few drivers (gcc extensions?). With
minor changes we can make Coccinelle run fine, and these changes can be
reverted after spatch. The diff for these changes is:
For drivers/media/i2c/s5k5baf.c:
@@ -1481,7 +1481,7 @@ static int s5k5baf_set_selection(struct v4l2_subdev *sd,
&s5k5baf_cis_rect,
v4l2_subdev_get_try_crop(sd, cfg, PAD_CIS),
v4l2_subdev_get_try_compose(sd, cfg, PAD_CIS),
- v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT)
+ v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT),
};
s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r);
return 0;
For drivers/media/platform/s3c-camif/camif-capture.c:
@@ -1230,7 +1230,7 @@ static int s3c_camif_subdev_get_fmt(struct v4l2_subdev *sd,
*mf = camif->mbus_fmt;
break;
- case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ case CAMIF_SD_PAD_SOURCE_C:
/* crop rectangle at camera interface input */
mf->width = camif->camif_crop.width;
mf->height = camif->camif_crop.height;
@@ -1332,7 +1332,7 @@ static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd,
}
break;
- case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ case CAMIF_SD_PAD_SOURCE_C:
/* Pixel format can be only changed on the sink pad. */
mf->code = camif->mbus_fmt.code;
mf->width = crop->width;
The semantic patch is:
// <smpl>
// Change function parameter
@@
identifier func;
identifier cfg;
@@
func(...,
- struct v4l2_subdev_pad_config *cfg
+ struct v4l2_subdev_state *sd_state
, ...)
{
<...
- cfg
+ sd_state
...>
}
// Change function declaration parameter
@@
identifier func;
identifier cfg;
type T;
@@
T func(...,
- struct v4l2_subdev_pad_config *cfg
+ struct v4l2_subdev_state *sd_state
, ...);
// Change function return value
@@
identifier func;
@@
- struct v4l2_subdev_pad_config
+ struct v4l2_subdev_state
*func(...)
{
...
}
// Change function declaration return value
@@
identifier func;
@@
- struct v4l2_subdev_pad_config
+ struct v4l2_subdev_state
*func(...);
// Some drivers pass a local pad_cfg for a single pad to a called function. Wrap it
// inside a pad_state.
@@
identifier func;
identifier pad_cfg;
@@
func(...)
{
...
struct v4l2_subdev_pad_config pad_cfg;
+ struct v4l2_subdev_state pad_state = { .pads = &pad_cfg };
<+...
(
v4l2_subdev_call
|
sensor_call
|
isi_try_fse
|
isc_try_fse
|
saa_call_all
)
(...,
- &pad_cfg
+ &pad_state
,...)
...+>
}
// If the function uses fields from pad_config, access via state->pads
@@
identifier func;
identifier state;
@@
func(...,
struct v4l2_subdev_state *state
, ...)
{
<...
(
- state->try_fmt
+ state->pads->try_fmt
|
- state->try_crop
+ state->pads->try_crop
|
- state->try_compose
+ state->pads->try_compose
)
...>
}
// If the function accesses the filehandle, use fh->state instead
@@
struct v4l2_subdev_fh *fh;
@@
- fh->pad
+ fh->state
@@
struct v4l2_subdev_fh fh;
@@
- fh.pad
+ fh.state
// Start of vsp1 specific
@@
@@
struct vsp1_entity {
...
- struct v4l2_subdev_pad_config *config;
+ struct v4l2_subdev_state *config;
...
};
@@
symbol entity;
@@
vsp1_entity_init(...)
{
...
entity->config =
- v4l2_subdev_alloc_pad_config
+ v4l2_subdev_alloc_state
(&entity->subdev);
...
}
@@
symbol entity;
@@
vsp1_entity_destroy(...)
{
...
- v4l2_subdev_free_pad_config
+ v4l2_subdev_free_state
(entity->config);
...
}
@exists@
identifier func =~ "(^vsp1.*)|(hsit_set_format)|(sru_enum_frame_size)|(sru_set_format)|(uif_get_selection)|(uif_set_selection)|(uds_enum_frame_size)|(uds_set_format)|(brx_set_format)|(brx_get_selection)|(histo_get_selection)|(histo_set_selection)|(brx_set_selection)";
symbol config;
@@
func(...) {
...
- struct v4l2_subdev_pad_config *config;
+ struct v4l2_subdev_state *config;
...
}
// End of vsp1 specific
// Start of rcar specific
@@
identifier sd;
identifier pad_cfg;
@@
rvin_try_format(...)
{
...
- struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_state *sd_state;
...
- pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+ sd_state = v4l2_subdev_alloc_state(sd);
<...
- pad_cfg
+ sd_state
...>
- v4l2_subdev_free_pad_config(pad_cfg);
+ v4l2_subdev_free_state(sd_state);
...
}
// End of rcar specific
// Start of rockchip specific
@@
identifier func =~ "(rkisp1_rsz_get_pad_fmt)|(rkisp1_rsz_get_pad_crop)|(rkisp1_rsz_register)";
symbol rsz;
symbol pad_cfg;
@@
func(...)
{
+ struct v4l2_subdev_state state = { .pads = rsz->pad_cfg };
...
- rsz->pad_cfg
+ &state
...
}
@@
identifier func =~ "(rkisp1_isp_get_pad_fmt)|(rkisp1_isp_get_pad_crop)";
symbol isp;
symbol pad_cfg;
@@
func(...)
{
+ struct v4l2_subdev_state state = { .pads = isp->pad_cfg };
...
- isp->pad_cfg
+ &state
...
}
@@
symbol rkisp1;
symbol isp;
symbol pad_cfg;
@@
rkisp1_isp_register(...)
{
+ struct v4l2_subdev_state state = { .pads = rkisp1->isp.pad_cfg };
...
- rkisp1->isp.pad_cfg
+ &state
...
}
// End of rockchip specific
// Start of tegra-video specific
@@
identifier sd;
identifier pad_cfg;
@@
__tegra_channel_try_format(...)
{
...
- struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_state *sd_state;
...
- pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+ sd_state = v4l2_subdev_alloc_state(sd);
<...
- pad_cfg
+ sd_state
...>
- v4l2_subdev_free_pad_config(pad_cfg);
+ v4l2_subdev_free_state(sd_state);
...
}
@@
identifier sd_state;
@@
__tegra_channel_try_format(...)
{
...
struct v4l2_subdev_state *sd_state;
<...
- sd_state->try_crop
+ sd_state->pads->try_crop
...>
}
// End of tegra-video specific
// </smpl>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2021-06-10 22:55:58 +08:00
|
|
|
struct v4l2_subdev_state *sd_state,
|
2017-06-08 02:33:56 +08:00
|
|
|
struct v4l2_subdev_frame_interval_enum *fie)
|
|
|
|
{
|
|
|
|
struct ov5640_dev *sensor = to_ov5640_dev(sd);
|
2022-12-02 21:00:00 +08:00
|
|
|
const struct ov5640_mode_info *mode;
|
2017-06-08 02:33:56 +08:00
|
|
|
struct v4l2_fract tpf;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (fie->pad != 0)
|
|
|
|
return -EINVAL;
|
|
|
|
if (fie->index >= OV5640_NUM_FRAMERATES)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2022-12-02 21:00:00 +08:00
|
|
|
mode = ov5640_find_mode(sensor, fie->width, fie->height, false);
|
|
|
|
if (!mode)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2017-06-08 02:33:56 +08:00
|
|
|
tpf.numerator = 1;
|
|
|
|
tpf.denominator = ov5640_framerates[fie->index];
|
|
|
|
|
2022-12-02 21:00:00 +08:00
|
|
|
ret = ov5640_try_frame_interval(sensor, &tpf, mode);
|
2017-06-08 02:33:56 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
fie->interval = tpf;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ov5640_g_frame_interval(struct v4l2_subdev *sd,
|
|
|
|
struct v4l2_subdev_frame_interval *fi)
|
|
|
|
{
|
|
|
|
struct ov5640_dev *sensor = to_ov5640_dev(sd);
|
|
|
|
|
|
|
|
mutex_lock(&sensor->lock);
|
|
|
|
fi->interval = sensor->frame_interval;
|
|
|
|
mutex_unlock(&sensor->lock);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ov5640_s_frame_interval(struct v4l2_subdev *sd,
|
|
|
|
struct v4l2_subdev_frame_interval *fi)
|
|
|
|
{
|
|
|
|
struct ov5640_dev *sensor = to_ov5640_dev(sd);
|
|
|
|
const struct ov5640_mode_info *mode;
|
|
|
|
int frame_rate, ret = 0;
|
|
|
|
|
|
|
|
if (fi->pad != 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
mutex_lock(&sensor->lock);
|
|
|
|
|
|
|
|
if (sensor->streaming) {
|
|
|
|
ret = -EBUSY;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
mode = sensor->current_mode;
|
|
|
|
|
2022-12-02 21:00:00 +08:00
|
|
|
frame_rate = ov5640_try_frame_interval(sensor, &fi->interval, mode);
|
2018-12-03 16:44:26 +08:00
|
|
|
if (frame_rate < 0) {
|
|
|
|
/* Always return a valid frame interval value */
|
|
|
|
fi->interval = sensor->frame_interval;
|
|
|
|
goto out;
|
|
|
|
}
|
2017-06-08 02:33:56 +08:00
|
|
|
|
2022-05-13 22:14:06 +08:00
|
|
|
mode = ov5640_find_mode(sensor, mode->width, mode->height, true);
|
2018-06-20 16:40:57 +08:00
|
|
|
if (!mode) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2022-05-13 22:14:06 +08:00
|
|
|
if (ov5640_framerates[frame_rate] > ov5640_framerates[mode->max_fps]) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2018-10-04 19:21:57 +08:00
|
|
|
if (mode != sensor->current_mode ||
|
|
|
|
frame_rate != sensor->current_fr) {
|
|
|
|
sensor->current_fr = frame_rate;
|
|
|
|
sensor->frame_interval = fi->interval;
|
2018-07-04 21:04:38 +08:00
|
|
|
sensor->current_mode = mode;
|
|
|
|
sensor->pending_mode_change = true;
|
2019-10-09 20:35:08 +08:00
|
|
|
|
2022-05-13 22:14:05 +08:00
|
|
|
ov5640_update_pixel_rate(sensor);
|
2018-07-04 21:04:38 +08:00
|
|
|
}
|
2017-06-08 02:33:56 +08:00
|
|
|
out:
|
|
|
|
mutex_unlock(&sensor->lock);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ov5640_enum_mbus_code(struct v4l2_subdev *sd,
|
media: v4l2-subdev: add subdev-wide state struct
We have 'struct v4l2_subdev_pad_config' which contains configuration for
a single pad used for the TRY functionality, and an array of those
structs is passed to various v4l2_subdev_pad_ops.
I was working on subdev internal routing between pads, and realized that
there's no way to add TRY functionality for routes, which is not pad
specific configuration. Adding a separate struct for try-route config
wouldn't work either, as e.g. set-fmt needs to know the try-route
configuration to propagate the settings.
This patch adds a new struct, 'struct v4l2_subdev_state' (which at the
moment only contains the v4l2_subdev_pad_config array) and the new
struct is used in most of the places where v4l2_subdev_pad_config was
used. All v4l2_subdev_pad_ops functions taking v4l2_subdev_pad_config
are changed to instead take v4l2_subdev_state.
The changes to drivers/media/v4l2-core/v4l2-subdev.c and
include/media/v4l2-subdev.h were written by hand, and all the driver
changes were done with the semantic patch below. The spatch needs to be
applied to a select list of directories. I used the following shell
commands to apply the spatch:
dirs="drivers/media/i2c drivers/media/platform drivers/media/usb drivers/media/test-drivers/vimc drivers/media/pci drivers/staging/media"
for dir in $dirs; do spatch -j8 --dir --include-headers --no-show-diff --in-place --sp-file v4l2-subdev-state.cocci $dir; done
Note that Coccinelle chokes on a few drivers (gcc extensions?). With
minor changes we can make Coccinelle run fine, and these changes can be
reverted after spatch. The diff for these changes is:
For drivers/media/i2c/s5k5baf.c:
@@ -1481,7 +1481,7 @@ static int s5k5baf_set_selection(struct v4l2_subdev *sd,
&s5k5baf_cis_rect,
v4l2_subdev_get_try_crop(sd, cfg, PAD_CIS),
v4l2_subdev_get_try_compose(sd, cfg, PAD_CIS),
- v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT)
+ v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT),
};
s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r);
return 0;
For drivers/media/platform/s3c-camif/camif-capture.c:
@@ -1230,7 +1230,7 @@ static int s3c_camif_subdev_get_fmt(struct v4l2_subdev *sd,
*mf = camif->mbus_fmt;
break;
- case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ case CAMIF_SD_PAD_SOURCE_C:
/* crop rectangle at camera interface input */
mf->width = camif->camif_crop.width;
mf->height = camif->camif_crop.height;
@@ -1332,7 +1332,7 @@ static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd,
}
break;
- case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ case CAMIF_SD_PAD_SOURCE_C:
/* Pixel format can be only changed on the sink pad. */
mf->code = camif->mbus_fmt.code;
mf->width = crop->width;
The semantic patch is:
// <smpl>
// Change function parameter
@@
identifier func;
identifier cfg;
@@
func(...,
- struct v4l2_subdev_pad_config *cfg
+ struct v4l2_subdev_state *sd_state
, ...)
{
<...
- cfg
+ sd_state
...>
}
// Change function declaration parameter
@@
identifier func;
identifier cfg;
type T;
@@
T func(...,
- struct v4l2_subdev_pad_config *cfg
+ struct v4l2_subdev_state *sd_state
, ...);
// Change function return value
@@
identifier func;
@@
- struct v4l2_subdev_pad_config
+ struct v4l2_subdev_state
*func(...)
{
...
}
// Change function declaration return value
@@
identifier func;
@@
- struct v4l2_subdev_pad_config
+ struct v4l2_subdev_state
*func(...);
// Some drivers pass a local pad_cfg for a single pad to a called function. Wrap it
// inside a pad_state.
@@
identifier func;
identifier pad_cfg;
@@
func(...)
{
...
struct v4l2_subdev_pad_config pad_cfg;
+ struct v4l2_subdev_state pad_state = { .pads = &pad_cfg };
<+...
(
v4l2_subdev_call
|
sensor_call
|
isi_try_fse
|
isc_try_fse
|
saa_call_all
)
(...,
- &pad_cfg
+ &pad_state
,...)
...+>
}
// If the function uses fields from pad_config, access via state->pads
@@
identifier func;
identifier state;
@@
func(...,
struct v4l2_subdev_state *state
, ...)
{
<...
(
- state->try_fmt
+ state->pads->try_fmt
|
- state->try_crop
+ state->pads->try_crop
|
- state->try_compose
+ state->pads->try_compose
)
...>
}
// If the function accesses the filehandle, use fh->state instead
@@
struct v4l2_subdev_fh *fh;
@@
- fh->pad
+ fh->state
@@
struct v4l2_subdev_fh fh;
@@
- fh.pad
+ fh.state
// Start of vsp1 specific
@@
@@
struct vsp1_entity {
...
- struct v4l2_subdev_pad_config *config;
+ struct v4l2_subdev_state *config;
...
};
@@
symbol entity;
@@
vsp1_entity_init(...)
{
...
entity->config =
- v4l2_subdev_alloc_pad_config
+ v4l2_subdev_alloc_state
(&entity->subdev);
...
}
@@
symbol entity;
@@
vsp1_entity_destroy(...)
{
...
- v4l2_subdev_free_pad_config
+ v4l2_subdev_free_state
(entity->config);
...
}
@exists@
identifier func =~ "(^vsp1.*)|(hsit_set_format)|(sru_enum_frame_size)|(sru_set_format)|(uif_get_selection)|(uif_set_selection)|(uds_enum_frame_size)|(uds_set_format)|(brx_set_format)|(brx_get_selection)|(histo_get_selection)|(histo_set_selection)|(brx_set_selection)";
symbol config;
@@
func(...) {
...
- struct v4l2_subdev_pad_config *config;
+ struct v4l2_subdev_state *config;
...
}
// End of vsp1 specific
// Start of rcar specific
@@
identifier sd;
identifier pad_cfg;
@@
rvin_try_format(...)
{
...
- struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_state *sd_state;
...
- pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+ sd_state = v4l2_subdev_alloc_state(sd);
<...
- pad_cfg
+ sd_state
...>
- v4l2_subdev_free_pad_config(pad_cfg);
+ v4l2_subdev_free_state(sd_state);
...
}
// End of rcar specific
// Start of rockchip specific
@@
identifier func =~ "(rkisp1_rsz_get_pad_fmt)|(rkisp1_rsz_get_pad_crop)|(rkisp1_rsz_register)";
symbol rsz;
symbol pad_cfg;
@@
func(...)
{
+ struct v4l2_subdev_state state = { .pads = rsz->pad_cfg };
...
- rsz->pad_cfg
+ &state
...
}
@@
identifier func =~ "(rkisp1_isp_get_pad_fmt)|(rkisp1_isp_get_pad_crop)";
symbol isp;
symbol pad_cfg;
@@
func(...)
{
+ struct v4l2_subdev_state state = { .pads = isp->pad_cfg };
...
- isp->pad_cfg
+ &state
...
}
@@
symbol rkisp1;
symbol isp;
symbol pad_cfg;
@@
rkisp1_isp_register(...)
{
+ struct v4l2_subdev_state state = { .pads = rkisp1->isp.pad_cfg };
...
- rkisp1->isp.pad_cfg
+ &state
...
}
// End of rockchip specific
// Start of tegra-video specific
@@
identifier sd;
identifier pad_cfg;
@@
__tegra_channel_try_format(...)
{
...
- struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_state *sd_state;
...
- pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+ sd_state = v4l2_subdev_alloc_state(sd);
<...
- pad_cfg
+ sd_state
...>
- v4l2_subdev_free_pad_config(pad_cfg);
+ v4l2_subdev_free_state(sd_state);
...
}
@@
identifier sd_state;
@@
__tegra_channel_try_format(...)
{
...
struct v4l2_subdev_state *sd_state;
<...
- sd_state->try_crop
+ sd_state->pads->try_crop
...>
}
// End of tegra-video specific
// </smpl>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2021-06-10 22:55:58 +08:00
|
|
|
struct v4l2_subdev_state *sd_state,
|
2018-02-01 16:44:06 +08:00
|
|
|
struct v4l2_subdev_mbus_code_enum *code)
|
2017-06-08 02:33:56 +08:00
|
|
|
{
|
2022-05-13 22:14:15 +08:00
|
|
|
struct ov5640_dev *sensor = to_ov5640_dev(sd);
|
|
|
|
const struct ov5640_pixfmt *formats;
|
|
|
|
unsigned int num_formats;
|
|
|
|
|
|
|
|
if (ov5640_is_csi2(sensor)) {
|
|
|
|
formats = ov5640_csi2_formats;
|
|
|
|
num_formats = ARRAY_SIZE(ov5640_csi2_formats) - 1;
|
|
|
|
} else {
|
|
|
|
formats = ov5640_dvp_formats;
|
|
|
|
num_formats = ARRAY_SIZE(ov5640_dvp_formats) - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (code->index >= num_formats)
|
2017-06-08 02:33:56 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
2022-05-13 22:14:15 +08:00
|
|
|
code->code = formats[code->index].code;
|
|
|
|
|
2017-06-08 02:33:56 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ov5640_s_stream(struct v4l2_subdev *sd, int enable)
|
|
|
|
{
|
|
|
|
struct ov5640_dev *sensor = to_ov5640_dev(sd);
|
|
|
|
int ret = 0;
|
|
|
|
|
2022-07-21 15:41:38 +08:00
|
|
|
if (enable) {
|
|
|
|
ret = pm_runtime_resume_and_get(&sensor->i2c_client->dev);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = v4l2_ctrl_handler_setup(&sensor->ctrls.handler);
|
|
|
|
if (ret) {
|
|
|
|
pm_runtime_put(&sensor->i2c_client->dev);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-08 02:33:56 +08:00
|
|
|
mutex_lock(&sensor->lock);
|
|
|
|
|
|
|
|
if (sensor->streaming == !enable) {
|
|
|
|
if (enable && sensor->pending_mode_change) {
|
2018-09-11 21:48:21 +08:00
|
|
|
ret = ov5640_set_mode(sensor);
|
2017-06-08 02:33:56 +08:00
|
|
|
if (ret)
|
|
|
|
goto out;
|
2018-08-16 17:46:53 +08:00
|
|
|
}
|
2018-01-03 17:57:32 +08:00
|
|
|
|
2018-08-16 17:46:53 +08:00
|
|
|
if (enable && sensor->pending_fmt_change) {
|
2018-01-03 17:57:32 +08:00
|
|
|
ret = ov5640_set_framefmt(sensor, &sensor->fmt);
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
2018-08-16 17:46:53 +08:00
|
|
|
sensor->pending_fmt_change = false;
|
2017-06-08 02:33:56 +08:00
|
|
|
}
|
|
|
|
|
2022-05-13 22:13:51 +08:00
|
|
|
if (ov5640_is_csi2(sensor))
|
2018-01-03 17:57:31 +08:00
|
|
|
ret = ov5640_set_stream_mipi(sensor, enable);
|
|
|
|
else
|
|
|
|
ret = ov5640_set_stream_dvp(sensor, enable);
|
|
|
|
|
2017-06-08 02:33:56 +08:00
|
|
|
if (!ret)
|
|
|
|
sensor->streaming = enable;
|
|
|
|
}
|
2022-07-21 15:41:38 +08:00
|
|
|
|
2017-06-08 02:33:56 +08:00
|
|
|
out:
|
|
|
|
mutex_unlock(&sensor->lock);
|
2022-07-21 15:41:38 +08:00
|
|
|
|
2023-01-16 01:30:10 +08:00
|
|
|
if (!enable || ret) {
|
|
|
|
pm_runtime_mark_last_busy(&sensor->i2c_client->dev);
|
2022-07-21 15:41:38 +08:00
|
|
|
pm_runtime_put_autosuspend(&sensor->i2c_client->dev);
|
2023-01-16 01:30:10 +08:00
|
|
|
}
|
2022-07-21 15:41:38 +08:00
|
|
|
|
2017-06-08 02:33:56 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2022-05-13 22:14:08 +08:00
|
|
|
static int ov5640_init_cfg(struct v4l2_subdev *sd,
|
|
|
|
struct v4l2_subdev_state *state)
|
|
|
|
{
|
2022-12-12 12:05:26 +08:00
|
|
|
struct ov5640_dev *sensor = to_ov5640_dev(sd);
|
2022-05-13 22:14:08 +08:00
|
|
|
struct v4l2_mbus_framefmt *fmt =
|
media: v4l: subdev: Switch to stream-aware state functions
Switch all drivers accessing sub-device state to use the stream-aware
functions. We will soon remove the old ones.
This patch has been generated using the following Coccinelle script:
---------8<------------
@@
expression E1, E2, E3;
@@
- v4l2_subdev_get_pad_format(E1, E2, E3)
+ v4l2_subdev_state_get_format(E2, E3)
@@
expression E1, E2, E3;
@@
- v4l2_subdev_get_pad_crop(E1, E2, E3)
+ v4l2_subdev_state_get_crop(E2, E3)
@@
expression E1, E2, E3;
@@
- v4l2_subdev_get_pad_compose(E1, E2, E3)
+ v4l2_subdev_state_get_compose(E2, E3)
@@
expression E1, E2, E3;
@@
- v4l2_subdev_get_try_format(E1, E2, E3)
+ v4l2_subdev_state_get_format(E2, E3)
@@
expression E1, E2, E3;
@@
- v4l2_subdev_get_try_crop(E1, E2, E3)
+ v4l2_subdev_state_get_crop(E2, E3)
@@
expression E1, E2, E3;
@@
- v4l2_subdev_get_try_compose(E1, E2, E3)
+ v4l2_subdev_state_get_compose(E2, E3)
---------8<------------
Additionally drivers/media/i2c/s5k5baf.c and
drivers/media/platform/samsung/s3c-camif/camif-capture.c have been
manually changed as Coccinelle didn't. Further local variables have been
removed as they became unused as a result of the other changes.
Also Coccinelle introduced indentation by space in files
drivers/media/i2c/st-mipid02.c and
drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c. This has been also
corrected.
The diff from Coccinelle-generated changes are:
> diff --git b/drivers/media/i2c/imx319.c a/drivers/media/i2c/imx319.c
> index e549692ff478..420984382173 100644
> --- b/drivers/media/i2c/imx319.c
> +++ a/drivers/media/i2c/imx319.c
> @@ -2001,7 +2001,6 @@ static int imx319_do_get_pad_format(struct imx319 *imx319,
> struct v4l2_subdev_format *fmt)
> {
> struct v4l2_mbus_framefmt *framefmt;
> - struct v4l2_subdev *sd = &imx319->sd;
>
> if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
> framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad);
> diff --git b/drivers/media/i2c/imx355.c a/drivers/media/i2c/imx355.c
> index 96bdde685d65..e1b1d2fc79dd 100644
> --- b/drivers/media/i2c/imx355.c
> +++ a/drivers/media/i2c/imx355.c
> @@ -1299,7 +1299,6 @@ static int imx355_do_get_pad_format(struct imx355 *imx355,
> struct v4l2_subdev_format *fmt)
> {
> struct v4l2_mbus_framefmt *framefmt;
> - struct v4l2_subdev *sd = &imx355->sd;
>
> if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
> framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad);
> diff --git b/drivers/media/i2c/ov08x40.c a/drivers/media/i2c/ov08x40.c
> index ca799bbcfdb7..abbb0b774d43 100644
> --- b/drivers/media/i2c/ov08x40.c
> +++ a/drivers/media/i2c/ov08x40.c
> @@ -2774,7 +2774,6 @@ static int ov08x40_do_get_pad_format(struct ov08x40 *ov08x,
> struct v4l2_subdev_format *fmt)
> {
> struct v4l2_mbus_framefmt *framefmt;
> - struct v4l2_subdev *sd = &ov08x->sd;
>
> if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
> framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad);
> diff --git b/drivers/media/i2c/ov13858.c a/drivers/media/i2c/ov13858.c
> index 7816d9787c61..09387e335d80 100644
> --- b/drivers/media/i2c/ov13858.c
> +++ a/drivers/media/i2c/ov13858.c
> @@ -1316,7 +1316,6 @@ static int ov13858_do_get_pad_format(struct ov13858 *ov13858,
> struct v4l2_subdev_format *fmt)
> {
> struct v4l2_mbus_framefmt *framefmt;
> - struct v4l2_subdev *sd = &ov13858->sd;
>
> if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
> framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad);
> diff --git b/drivers/media/i2c/ov13b10.c a/drivers/media/i2c/ov13b10.c
> index 268cd4b03f9c..c06411d5ee2b 100644
> --- b/drivers/media/i2c/ov13b10.c
> +++ a/drivers/media/i2c/ov13b10.c
> @@ -1001,7 +1001,6 @@ static int ov13b10_do_get_pad_format(struct ov13b10 *ov13b,
> struct v4l2_subdev_format *fmt)
> {
> struct v4l2_mbus_framefmt *framefmt;
> - struct v4l2_subdev *sd = &ov13b->sd;
>
> if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
> framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad);
> diff --git b/drivers/media/i2c/s5c73m3/s5c73m3-core.c a/drivers/media/i2c/s5c73m3/s5c73m3-core.c
> index 47605e36bc60..8f9b5713daf7 100644
> --- b/drivers/media/i2c/s5c73m3/s5c73m3-core.c
> +++ a/drivers/media/i2c/s5c73m3/s5c73m3-core.c
> @@ -819,7 +819,6 @@ static void s5c73m3_oif_try_format(struct s5c73m3 *state,
> struct v4l2_subdev_format *fmt,
> const struct s5c73m3_frame_size **fs)
> {
> - struct v4l2_subdev *sd = &state->sensor_sd;
> u32 code;
>
> switch (fmt->pad) {
> diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c
> index 67da2045f543..03ccfb0e1e11 100644
> --- a/drivers/media/i2c/s5k5baf.c
> +++ b/drivers/media/i2c/s5k5baf.c
> @@ -1472,14 +1472,11 @@ static int s5k5baf_set_selection(struct v4l2_subdev *sd,
>
> if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
> rects = (struct v4l2_rect * []) {
> - &s5k5baf_cis_rect,
> - v4l2_subdev_get_try_crop(sd, sd_state,
> - PAD_CIS),
> - v4l2_subdev_get_try_compose(sd, sd_state,
> - PAD_CIS),
> - v4l2_subdev_get_try_crop(sd, sd_state,
> - PAD_OUT)
> - };
> + &s5k5baf_cis_rect,
> + v4l2_subdev_state_get_crop(sd_state, PAD_CIS),
> + v4l2_subdev_state_get_compose(sd_state, PAD_CIS),
> + v4l2_subdev_state_get_crop(sd_state, PAD_OUT)
> + };
> s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r);
> return 0;
> }
> diff --git b/drivers/media/platform/samsung/s3c-camif/camif-capture.c a/drivers/media/platform/samsung/s3c-camif/camif-capture.c
> index 295e083f38e8..be58260ea67e 100644
> --- b/drivers/media/platform/samsung/s3c-camif/camif-capture.c
> +++ a/drivers/media/platform/samsung/s3c-camif/camif-capture.c
> @@ -1216,7 +1216,7 @@ static int s3c_camif_subdev_get_fmt(struct v4l2_subdev *sd,
> struct v4l2_mbus_framefmt *mf = &fmt->format;
>
> if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
> - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad);
> + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad);
> fmt->format = *mf;
> return 0;
> }
> @@ -1305,7 +1305,7 @@ static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd,
> __camif_subdev_try_format(camif, mf, fmt->pad);
>
> if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
> - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad);
> + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad);
> *mf = fmt->format;
> mutex_unlock(&camif->lock);
> return 0;
> diff --git b/drivers/media/platform/ti/cal/cal-camerarx.c a/drivers/media/platform/ti/cal/cal-camerarx.c
> index cea454ed9c20..61433744c6c4 100644
> --- b/drivers/media/platform/ti/cal/cal-camerarx.c
> +++ a/drivers/media/platform/ti/cal/cal-camerarx.c
> @@ -621,8 +621,6 @@ static int cal_camerarx_sd_enum_mbus_code(struct v4l2_subdev *sd,
> struct v4l2_subdev_state *state,
> struct v4l2_subdev_mbus_code_enum *code)
> {
> - struct cal_camerarx *phy = to_cal_camerarx(sd);
> -
> /* No transcoding, source and sink codes must match. */
> if (cal_rx_pad_is_source(code->pad)) {
> struct v4l2_mbus_framefmt *fmt;
> diff --git b/drivers/staging/media/imx/imx-ic-prp.c a/drivers/staging/media/imx/imx-ic-prp.c
> index dd558fac6477..61d69f19657e 100644
> --- b/drivers/staging/media/imx/imx-ic-prp.c
> +++ a/drivers/staging/media/imx/imx-ic-prp.c
> @@ -82,8 +82,6 @@ static struct v4l2_mbus_framefmt *
> __prp_get_fmt(struct prp_priv *priv, struct v4l2_subdev_state *sd_state,
> unsigned int pad, enum v4l2_subdev_format_whence which)
> {
> - struct imx_ic_priv *ic_priv = priv->ic_priv;
> -
> if (which == V4L2_SUBDEV_FORMAT_TRY)
> return v4l2_subdev_state_get_format(sd_state, pad);
> else
> diff --git b/drivers/staging/media/imx/imx-ic-prpencvf.c a/drivers/staging/media/imx/imx-ic-prpencvf.c
> index 02db7dbb884b..ec73c901079e 100644
> --- b/drivers/staging/media/imx/imx-ic-prpencvf.c
> +++ a/drivers/staging/media/imx/imx-ic-prpencvf.c
> @@ -790,8 +790,6 @@ static struct v4l2_mbus_framefmt *
> __prp_get_fmt(struct prp_priv *priv, struct v4l2_subdev_state *sd_state,
> unsigned int pad, enum v4l2_subdev_format_whence which)
> {
> - struct imx_ic_priv *ic_priv = priv->ic_priv;
> -
> if (which == V4L2_SUBDEV_FORMAT_TRY)
> return v4l2_subdev_state_get_format(sd_state, pad);
> else
> diff --git a/drivers/media/i2c/st-mipid02.c b/drivers/media/i2c/st-mipid02.c
> index 9c9361354c00..b08a249b5fdd 100644
> --- a/drivers/media/i2c/st-mipid02.c
> +++ b/drivers/media/i2c/st-mipid02.c
> @@ -751,7 +751,7 @@ static void mipid02_set_fmt_source(struct v4l2_subdev *sd,
> format->format = bridge->fmt;
> else
> format->format = *v4l2_subdev_state_get_format(sd_state,
> - MIPID02_SINK_0);
> + MIPID02_SINK_0);
>
> /* but code may need to be converted */
> format->format.code = serial_to_parallel_code(format->format.code);
> diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c
> index 117912d3bfbd..96353648c032 100644
> --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c
> +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c
> @@ -319,7 +319,7 @@ static void rkisp1_isp_start(struct rkisp1_isp *isp,
> rkisp1_write(rkisp1, RKISP1_CIF_ISP_CTRL, val);
>
> src_fmt = v4l2_subdev_state_get_format(sd_state,
> - RKISP1_ISP_PAD_SOURCE_VIDEO);
> + RKISP1_ISP_PAD_SOURCE_VIDEO);
> src_info = rkisp1_mbus_info_get_by_code(src_fmt->code);
>
> if (src_info->pixel_enc != V4L2_PIXEL_ENC_BAYER)
> @@ -475,9 +475,9 @@ static void rkisp1_isp_set_src_fmt(struct rkisp1_isp *isp,
> sink_fmt = v4l2_subdev_state_get_format(sd_state,
> RKISP1_ISP_PAD_SINK_VIDEO);
> src_fmt = v4l2_subdev_state_get_format(sd_state,
> - RKISP1_ISP_PAD_SOURCE_VIDEO);
> + RKISP1_ISP_PAD_SOURCE_VIDEO);
> src_crop = v4l2_subdev_state_get_crop(sd_state,
> - RKISP1_ISP_PAD_SOURCE_VIDEO);
> + RKISP1_ISP_PAD_SOURCE_VIDEO);
>
> /*
> * Media bus code. The ISP can operate in pass-through mode (Bayer in,
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
2023-10-13 15:37:10 +08:00
|
|
|
v4l2_subdev_state_get_format(state, 0);
|
|
|
|
struct v4l2_rect *crop = v4l2_subdev_state_get_crop(state, 0);
|
2022-05-13 22:14:08 +08:00
|
|
|
|
2022-12-12 12:05:26 +08:00
|
|
|
*fmt = ov5640_is_csi2(sensor) ? ov5640_csi2_default_fmt :
|
|
|
|
ov5640_dvp_default_fmt;
|
2022-05-13 22:14:08 +08:00
|
|
|
|
2022-05-13 22:14:09 +08:00
|
|
|
crop->left = OV5640_PIXEL_ARRAY_LEFT;
|
|
|
|
crop->top = OV5640_PIXEL_ARRAY_TOP;
|
|
|
|
crop->width = OV5640_PIXEL_ARRAY_WIDTH;
|
|
|
|
crop->height = OV5640_PIXEL_ARRAY_HEIGHT;
|
|
|
|
|
2022-05-13 22:14:08 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-06-08 02:33:56 +08:00
|
|
|
static const struct v4l2_subdev_core_ops ov5640_core_ops = {
|
2018-11-13 00:00:52 +08:00
|
|
|
.log_status = v4l2_ctrl_subdev_log_status,
|
|
|
|
.subscribe_event = v4l2_ctrl_subdev_subscribe_event,
|
|
|
|
.unsubscribe_event = v4l2_event_subdev_unsubscribe,
|
2017-06-08 02:33:56 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static const struct v4l2_subdev_video_ops ov5640_video_ops = {
|
|
|
|
.g_frame_interval = ov5640_g_frame_interval,
|
|
|
|
.s_frame_interval = ov5640_s_frame_interval,
|
|
|
|
.s_stream = ov5640_s_stream,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct v4l2_subdev_pad_ops ov5640_pad_ops = {
|
2022-05-13 22:14:08 +08:00
|
|
|
.init_cfg = ov5640_init_cfg,
|
2017-06-08 02:33:56 +08:00
|
|
|
.enum_mbus_code = ov5640_enum_mbus_code,
|
|
|
|
.get_fmt = ov5640_get_fmt,
|
|
|
|
.set_fmt = ov5640_set_fmt,
|
2022-05-13 22:14:09 +08:00
|
|
|
.get_selection = ov5640_get_selection,
|
2017-06-08 02:33:56 +08:00
|
|
|
.enum_frame_size = ov5640_enum_frame_size,
|
|
|
|
.enum_frame_interval = ov5640_enum_frame_interval,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct v4l2_subdev_ops ov5640_subdev_ops = {
|
|
|
|
.core = &ov5640_core_ops,
|
|
|
|
.video = &ov5640_video_ops,
|
|
|
|
.pad = &ov5640_pad_ops,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int ov5640_get_regulators(struct ov5640_dev *sensor)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < OV5640_NUM_SUPPLIES; i++)
|
|
|
|
sensor->supplies[i].supply = ov5640_supply_name[i];
|
|
|
|
|
|
|
|
return devm_regulator_bulk_get(&sensor->i2c_client->dev,
|
|
|
|
OV5640_NUM_SUPPLIES,
|
|
|
|
sensor->supplies);
|
|
|
|
}
|
|
|
|
|
2018-01-03 17:57:29 +08:00
|
|
|
static int ov5640_check_chip_id(struct ov5640_dev *sensor)
|
|
|
|
{
|
|
|
|
struct i2c_client *client = sensor->i2c_client;
|
|
|
|
int ret = 0;
|
|
|
|
u16 chip_id;
|
|
|
|
|
|
|
|
ret = ov5640_read_reg16(sensor, OV5640_REG_CHIP_ID, &chip_id);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(&client->dev, "%s: failed to read chip identifier\n",
|
|
|
|
__func__);
|
2022-07-21 15:41:38 +08:00
|
|
|
return ret;
|
2018-01-03 17:57:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (chip_id != 0x5640) {
|
|
|
|
dev_err(&client->dev, "%s: wrong chip identifier, expected 0x5640, got 0x%x\n",
|
|
|
|
__func__, chip_id);
|
2022-07-21 15:41:38 +08:00
|
|
|
return -ENXIO;
|
2018-01-03 17:57:29 +08:00
|
|
|
}
|
|
|
|
|
2022-07-21 15:41:38 +08:00
|
|
|
return 0;
|
2018-01-03 17:57:29 +08:00
|
|
|
}
|
|
|
|
|
2019-07-11 05:51:49 +08:00
|
|
|
static int ov5640_probe(struct i2c_client *client)
|
2017-06-08 02:33:56 +08:00
|
|
|
{
|
|
|
|
struct device *dev = &client->dev;
|
|
|
|
struct fwnode_handle *endpoint;
|
|
|
|
struct ov5640_dev *sensor;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
|
|
|
|
if (!sensor)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
sensor->i2c_client = client;
|
2018-08-16 17:46:53 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* default init sequence initialize sensor to
|
2023-05-09 14:56:45 +08:00
|
|
|
* YUV422 UYVY VGA(30FPS in parallel mode, 60 in MIPI CSI-2 mode)
|
2018-08-16 17:46:53 +08:00
|
|
|
*/
|
2017-06-08 02:33:56 +08:00
|
|
|
sensor->frame_interval.numerator = 1;
|
|
|
|
sensor->frame_interval.denominator = ov5640_framerates[OV5640_30_FPS];
|
|
|
|
sensor->current_fr = OV5640_30_FPS;
|
|
|
|
sensor->current_mode =
|
2018-12-03 16:44:23 +08:00
|
|
|
&ov5640_mode_data[OV5640_MODE_VGA_640_480];
|
2018-09-11 21:48:21 +08:00
|
|
|
sensor->last_mode = sensor->current_mode;
|
2022-11-25 17:20:24 +08:00
|
|
|
sensor->current_link_freq =
|
|
|
|
ov5640_csi2_link_freqs[OV5640_DEFAULT_LINK_FREQ];
|
2017-06-08 02:33:56 +08:00
|
|
|
|
|
|
|
sensor->ae_target = 52;
|
|
|
|
|
2018-04-24 18:25:47 +08:00
|
|
|
endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev),
|
|
|
|
NULL);
|
2017-06-08 02:33:56 +08:00
|
|
|
if (!endpoint) {
|
|
|
|
dev_err(dev, "endpoint node not found\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = v4l2_fwnode_endpoint_parse(endpoint, &sensor->ep);
|
|
|
|
fwnode_handle_put(endpoint);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(dev, "Could not parse endpoint\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-09-05 04:18:35 +08:00
|
|
|
if (sensor->ep.bus_type != V4L2_MBUS_PARALLEL &&
|
|
|
|
sensor->ep.bus_type != V4L2_MBUS_CSI2_DPHY &&
|
|
|
|
sensor->ep.bus_type != V4L2_MBUS_BT656) {
|
|
|
|
dev_err(dev, "Unsupported bus type %d\n", sensor->ep.bus_type);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2022-12-12 12:05:26 +08:00
|
|
|
sensor->fmt = ov5640_is_csi2(sensor) ? ov5640_csi2_default_fmt :
|
|
|
|
ov5640_dvp_default_fmt;
|
|
|
|
|
2017-06-08 02:33:56 +08:00
|
|
|
/* get system clock (xclk) */
|
|
|
|
sensor->xclk = devm_clk_get(dev, "xclk");
|
|
|
|
if (IS_ERR(sensor->xclk)) {
|
|
|
|
dev_err(dev, "failed to get xclk\n");
|
|
|
|
return PTR_ERR(sensor->xclk);
|
|
|
|
}
|
|
|
|
|
|
|
|
sensor->xclk_freq = clk_get_rate(sensor->xclk);
|
|
|
|
if (sensor->xclk_freq < OV5640_XCLK_MIN ||
|
|
|
|
sensor->xclk_freq > OV5640_XCLK_MAX) {
|
|
|
|
dev_err(dev, "xclk frequency out of range: %d Hz\n",
|
|
|
|
sensor->xclk_freq);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* request optional power down pin */
|
|
|
|
sensor->pwdn_gpio = devm_gpiod_get_optional(dev, "powerdown",
|
|
|
|
GPIOD_OUT_HIGH);
|
2019-06-28 19:00:34 +08:00
|
|
|
if (IS_ERR(sensor->pwdn_gpio))
|
|
|
|
return PTR_ERR(sensor->pwdn_gpio);
|
|
|
|
|
2017-06-08 02:33:56 +08:00
|
|
|
/* request optional reset pin */
|
|
|
|
sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset",
|
|
|
|
GPIOD_OUT_HIGH);
|
2019-06-28 19:00:34 +08:00
|
|
|
if (IS_ERR(sensor->reset_gpio))
|
|
|
|
return PTR_ERR(sensor->reset_gpio);
|
2017-06-08 02:33:56 +08:00
|
|
|
|
|
|
|
v4l2_i2c_subdev_init(&sensor->sd, client, &ov5640_subdev_ops);
|
|
|
|
|
2018-11-13 00:00:52 +08:00
|
|
|
sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
|
|
|
|
V4L2_SUBDEV_FL_HAS_EVENTS;
|
2017-06-08 02:33:56 +08:00
|
|
|
sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
|
|
|
|
sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
|
|
|
|
ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = ov5640_get_regulators(sensor);
|
|
|
|
if (ret)
|
2022-07-21 15:41:38 +08:00
|
|
|
goto entity_cleanup;
|
2017-06-08 02:33:56 +08:00
|
|
|
|
|
|
|
mutex_init(&sensor->lock);
|
|
|
|
|
2022-07-21 15:41:38 +08:00
|
|
|
ret = ov5640_init_controls(sensor);
|
2018-01-03 17:57:29 +08:00
|
|
|
if (ret)
|
|
|
|
goto entity_cleanup;
|
|
|
|
|
2022-07-21 15:41:38 +08:00
|
|
|
ret = ov5640_sensor_resume(dev);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(dev, "failed to power on\n");
|
2023-08-07 17:05:35 +08:00
|
|
|
goto free_ctrls;
|
2022-07-21 15:41:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
pm_runtime_set_active(dev);
|
|
|
|
pm_runtime_get_noresume(dev);
|
|
|
|
pm_runtime_enable(dev);
|
|
|
|
|
|
|
|
ret = ov5640_check_chip_id(sensor);
|
|
|
|
if (ret)
|
|
|
|
goto err_pm_runtime;
|
2017-06-08 02:33:56 +08:00
|
|
|
|
2021-03-06 00:42:34 +08:00
|
|
|
ret = v4l2_async_register_subdev_sensor(&sensor->sd);
|
2017-06-08 02:33:56 +08:00
|
|
|
if (ret)
|
2022-07-21 15:41:38 +08:00
|
|
|
goto err_pm_runtime;
|
|
|
|
|
|
|
|
pm_runtime_set_autosuspend_delay(dev, 1000);
|
|
|
|
pm_runtime_use_autosuspend(dev);
|
2023-01-16 01:30:10 +08:00
|
|
|
pm_runtime_mark_last_busy(dev);
|
2022-07-21 15:41:38 +08:00
|
|
|
pm_runtime_put_autosuspend(dev);
|
2017-06-08 02:33:56 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
2022-07-21 15:41:38 +08:00
|
|
|
err_pm_runtime:
|
|
|
|
pm_runtime_put_noidle(dev);
|
|
|
|
pm_runtime_disable(dev);
|
|
|
|
ov5640_sensor_suspend(dev);
|
2023-08-07 17:05:35 +08:00
|
|
|
free_ctrls:
|
|
|
|
v4l2_ctrl_handler_free(&sensor->ctrls.handler);
|
2017-06-08 02:33:56 +08:00
|
|
|
entity_cleanup:
|
|
|
|
media_entity_cleanup(&sensor->sd.entity);
|
2020-03-25 20:20:00 +08:00
|
|
|
mutex_destroy(&sensor->lock);
|
2017-06-08 02:33:56 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2022-08-15 16:02:30 +08:00
|
|
|
static void ov5640_remove(struct i2c_client *client)
|
2017-06-08 02:33:56 +08:00
|
|
|
{
|
|
|
|
struct v4l2_subdev *sd = i2c_get_clientdata(client);
|
|
|
|
struct ov5640_dev *sensor = to_ov5640_dev(sd);
|
2022-07-21 15:41:38 +08:00
|
|
|
struct device *dev = &client->dev;
|
|
|
|
|
|
|
|
pm_runtime_disable(dev);
|
|
|
|
if (!pm_runtime_status_suspended(dev))
|
|
|
|
ov5640_sensor_suspend(dev);
|
|
|
|
pm_runtime_set_suspended(dev);
|
2017-06-08 02:33:56 +08:00
|
|
|
|
|
|
|
v4l2_async_unregister_subdev(&sensor->sd);
|
|
|
|
media_entity_cleanup(&sensor->sd.entity);
|
|
|
|
v4l2_ctrl_handler_free(&sensor->ctrls.handler);
|
2020-03-25 20:20:00 +08:00
|
|
|
mutex_destroy(&sensor->lock);
|
2017-06-08 02:33:56 +08:00
|
|
|
}
|
|
|
|
|
2022-07-21 15:41:38 +08:00
|
|
|
static const struct dev_pm_ops ov5640_pm_ops = {
|
|
|
|
SET_RUNTIME_PM_OPS(ov5640_sensor_suspend, ov5640_sensor_resume, NULL)
|
|
|
|
};
|
|
|
|
|
2017-06-08 02:33:56 +08:00
|
|
|
static const struct i2c_device_id ov5640_id[] = {
|
|
|
|
{"ov5640", 0},
|
|
|
|
{},
|
|
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(i2c, ov5640_id);
|
|
|
|
|
|
|
|
static const struct of_device_id ov5640_dt_ids[] = {
|
|
|
|
{ .compatible = "ovti,ov5640" },
|
|
|
|
{ /* sentinel */ }
|
|
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(of, ov5640_dt_ids);
|
|
|
|
|
|
|
|
static struct i2c_driver ov5640_i2c_driver = {
|
|
|
|
.driver = {
|
|
|
|
.name = "ov5640",
|
|
|
|
.of_match_table = ov5640_dt_ids,
|
2022-07-21 15:41:38 +08:00
|
|
|
.pm = &ov5640_pm_ops,
|
2017-06-08 02:33:56 +08:00
|
|
|
},
|
|
|
|
.id_table = ov5640_id,
|
2023-05-14 20:04:07 +08:00
|
|
|
.probe = ov5640_probe,
|
2017-06-08 02:33:56 +08:00
|
|
|
.remove = ov5640_remove,
|
|
|
|
};
|
|
|
|
|
|
|
|
module_i2c_driver(ov5640_i2c_driver);
|
|
|
|
|
|
|
|
MODULE_DESCRIPTION("OV5640 MIPI Camera Subdev Driver");
|
|
|
|
MODULE_LICENSE("GPL");
|