From 8e40238bdd44bb0088d19c053ab2261523c81637 Mon Sep 17 00:00:00 2001 From: Lionel VITTE Date: Fri, 8 Nov 2019 16:52:41 +0100 Subject: [PATCH 13/31] ARM stm32mp1 r3 MEDIA --- Documentation/media/uapi/v4l/subdev-formats.rst | 107 +++ drivers/media/i2c/Kconfig | 13 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/ov5640.c | 1000 +++++++++++++-------- drivers/media/i2c/st-mipid02.c | 1076 +++++++++++++++++++++++ drivers/media/platform/Kconfig | 2 +- drivers/media/platform/stm32/stm32-cec.c | 96 +- drivers/media/platform/stm32/stm32-dcmi.c | 366 ++++++-- drivers/media/usb/uvc/uvc_queue.c | 15 +- drivers/media/usb/uvc/uvc_v4l2.c | 11 +- drivers/media/usb/uvc/uvcvideo.h | 2 + drivers/media/v4l2-core/v4l2-fwnode.c | 3 + include/uapi/linux/media-bus-format.h | 3 +- 13 files changed, 2266 insertions(+), 429 deletions(-) create mode 100644 drivers/media/i2c/st-mipid02.c diff --git a/Documentation/media/uapi/v4l/subdev-formats.rst b/Documentation/media/uapi/v4l/subdev-formats.rst index 8e73fcf..44f427c 100644 --- a/Documentation/media/uapi/v4l/subdev-formats.rst +++ b/Documentation/media/uapi/v4l/subdev-formats.rst @@ -973,6 +973,113 @@ The following tables list existing packed RGB formats. - r\ :sub:`2` - r\ :sub:`1` - r\ :sub:`0` + * .. _MEDIA-BUS-FMT-BGR888-3X8: + + - MEDIA_BUS_FMT_BGR888_3X8 + - 0x101b + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - b\ :sub:`7` + - b\ :sub:`6` + - b\ :sub:`5` + - b\ :sub:`4` + - b\ :sub:`3` + - b\ :sub:`2` + - b\ :sub:`1` + - b\ :sub:`0` + * - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - g\ :sub:`7` + - g\ :sub:`6` + - g\ :sub:`5` + - g\ :sub:`4` + - g\ :sub:`3` + - g\ :sub:`2` + - g\ :sub:`1` + - g\ :sub:`0` + * - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - r\ :sub:`7` + - r\ :sub:`6` + - r\ :sub:`5` + - r\ :sub:`4` + - r\ :sub:`3` + - r\ :sub:`2` + - r\ :sub:`1` + - r\ :sub:`0` * .. _MEDIA-BUS-FMT-GBR888-1X24: - MEDIA_BUS_FMT_GBR888_1X24 diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 8b1ae1d..6a8e58d 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -1068,6 +1068,19 @@ config VIDEO_I2C To compile this driver as a module, choose M here: the module will be called video-i2c +config VIDEO_ST_MIPID02 + tristate "STMicroelectronics MIPID02 CSI-2 to PARALLEL bridge" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + select V4L2_FWNODE + help + Support for STMicroelectronics MIPID02 CSI-2 to PARALLEL bridge. + It is used to allow usage of CSI-2 sensor with PARALLEL port + controller. + + To compile this driver as a module, choose M here: the + module will be called st-mipid02. + endmenu menu "Sensors used on soc_camera driver" diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 520b3c3..051c68e 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -108,5 +108,6 @@ obj-$(CONFIG_VIDEO_OV2659) += ov2659.o obj-$(CONFIG_VIDEO_TC358743) += tc358743.o obj-$(CONFIG_VIDEO_IMX258) += imx258.o obj-$(CONFIG_VIDEO_IMX274) += imx274.o +obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o obj-$(CONFIG_SDR_MAX2175) += max2175.o diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index 2023df1..62269ac 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -66,6 +66,7 @@ #define OV5640_REG_TIMING_VTS 0x380e #define OV5640_REG_TIMING_TC_REG20 0x3820 #define OV5640_REG_TIMING_TC_REG21 0x3821 +#define OV5640_REG_DVP_PCLK_DIVIDER 0x3824 #define OV5640_REG_AEC_CTRL00 0x3a00 #define OV5640_REG_AEC_B50_STEP 0x3a08 #define OV5640_REG_AEC_B60_STEP 0x3a0a @@ -82,6 +83,9 @@ #define OV5640_REG_SIGMADELTA_CTRL0C 0x3c0c #define OV5640_REG_FRAME_CTRL01 0x4202 #define OV5640_REG_FORMAT_CONTROL00 0x4300 +#define OV5640_REG_VFIFO_HSIZE 0x4602 +#define OV5640_REG_VFIFO_VSIZE 0x4604 +#define OV5640_REG_JPG_MODE_SELECT 0x4713 #define OV5640_REG_POLARITY_CTRL00 0x4740 #define OV5640_REG_MIPI_CTRL00 0x4800 #define OV5640_REG_DEBUG_MODE 0x4814 @@ -94,9 +98,6 @@ #define OV5640_REG_SDE_CTRL5 0x5585 #define OV5640_REG_AVG_READOUT 0x56a1 -#define OV5640_SCLK2X_ROOT_DIVIDER_DEFAULT 1 -#define OV5640_SCLK_ROOT_DIVIDER_DEFAULT 2 - enum ov5640_mode_id { OV5640_MODE_QCIF_176_144 = 0, OV5640_MODE_QVGA_320_240, @@ -113,9 +114,19 @@ enum ov5640_mode_id { enum ov5640_frame_rate { OV5640_15_FPS = 0, OV5640_30_FPS, + OV5640_60_FPS, OV5640_NUM_FRAMERATES, }; +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, +}; + struct ov5640_pixfmt { u32 code; u32 colorspace; @@ -127,6 +138,10 @@ static const struct ov5640_pixfmt ov5640_formats[] = { { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_SRGB, }, { MEDIA_BUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB, }, { MEDIA_BUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB, }, + { MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB, }, + { MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_COLORSPACE_SRGB, }, + { MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_COLORSPACE_SRGB, }, + { MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_COLORSPACE_SRGB, }, }; /* @@ -141,6 +156,7 @@ MODULE_PARM_DESC(virtual_channel, static const int ov5640_framerates[] = { [OV5640_15_FPS] = 15, [OV5640_30_FPS] = 30, + [OV5640_60_FPS] = 60, }; /* regulator supplies */ @@ -202,6 +218,7 @@ struct ov5640_ctrls { struct v4l2_ctrl *test_pattern; struct v4l2_ctrl *hflip; struct v4l2_ctrl *vflip; + struct v4l2_ctrl *link_freq; }; struct ov5640_dev { @@ -261,8 +278,7 @@ static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) static const struct reg_value ov5640_init_setting_30fps_VGA[] = { {0x3103, 0x11, 0, 0}, {0x3008, 0x82, 0, 5}, {0x3008, 0x42, 0, 0}, {0x3103, 0x03, 0, 0}, {0x3017, 0x00, 0, 0}, {0x3018, 0x00, 0, 0}, - {0x3034, 0x18, 0, 0}, {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, - {0x3037, 0x13, 0, 0}, {0x3630, 0x36, 0, 0}, + {0x3630, 0x36, 0, 0}, {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}, @@ -289,7 +305,7 @@ static const struct reg_value ov5640_init_setting_30fps_VGA[] = { {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x3000, 0x00, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3006, 0xc3, 0, 0}, {0x302e, 0x08, 0, 0}, {0x4300, 0x3f, 0, 0}, - {0x501f, 0x00, 0, 0}, {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, + {0x501f, 0x00, 0, 0}, {0x4407, 0x04, 0, 0}, {0x440e, 0x00, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, {0x4837, 0x0a, 0, 0}, {0x3824, 0x02, 0, 0}, {0x5000, 0xa7, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x5180, 0xff, 0, 0}, @@ -344,66 +360,8 @@ static const struct reg_value ov5640_init_setting_30fps_VGA[] = { {0x3a1f, 0x14, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3c00, 0x04, 0, 300}, }; -static const struct reg_value ov5640_setting_30fps_VGA_640_480[] = { - {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, - {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3814, 0x31, 0, 0}, - {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, - {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, - {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3810, 0x00, 0, 0}, - {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, - {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, 0x0e, 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}, {0x4713, 0x03, 0, 0}, - {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, - {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x3503, 0x00, 0, 0}, -}; - -static const struct reg_value ov5640_setting_15fps_VGA_640_480[] = { - {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, - {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3814, 0x31, 0, 0}, - {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, - {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, - {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3810, 0x00, 0, 0}, - {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, - {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}, {0x4713, 0x03, 0, 0}, - {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, - {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, -}; - -static const struct reg_value ov5640_setting_30fps_XGA_1024_768[] = { - {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, - {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3814, 0x31, 0, 0}, - {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, - {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, - {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3810, 0x00, 0, 0}, - {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, - {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, 0x0e, 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}, {0x4713, 0x03, 0, 0}, - {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, - {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x3503, 0x00, 0, 0}, - {0x3035, 0x12, 0, 0}, -}; - -static const struct reg_value ov5640_setting_15fps_XGA_1024_768[] = { - {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, +static const struct reg_value ov5640_setting_VGA_640_480[] = { + {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, @@ -416,13 +374,13 @@ static const struct reg_value ov5640_setting_15fps_XGA_1024_768[] = { {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}, {0x4713, 0x03, 0, 0}, - {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, - {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, + {0x4407, 0x04, 0, 0}, + {0x5001, 0xa3, 0, 0}, }; -static const struct reg_value ov5640_setting_30fps_QVGA_320_240[] = { - {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, +static const struct reg_value ov5640_setting_XGA_1024_768[] = { + {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, @@ -435,13 +393,12 @@ static const struct reg_value ov5640_setting_30fps_QVGA_320_240[] = { {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}, {0x4713, 0x03, 0, 0}, - {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, - {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x5001, 0xa3, 0, 0}, }; -static const struct reg_value ov5640_setting_15fps_QVGA_320_240[] = { - {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, +static const struct reg_value ov5640_setting_QVGA_320_240[] = { + {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, @@ -454,13 +411,12 @@ static const struct reg_value ov5640_setting_15fps_QVGA_320_240[] = { {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}, {0x4713, 0x03, 0, 0}, - {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, - {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x5001, 0xa3, 0, 0}, }; -static const struct reg_value ov5640_setting_30fps_QCIF_176_144[] = { - {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, +static const struct reg_value ov5640_setting_QCIF_176_144[] = { + {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, @@ -473,32 +429,12 @@ static const struct reg_value ov5640_setting_30fps_QCIF_176_144[] = { {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}, {0x4713, 0x03, 0, 0}, - {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, - {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x5001, 0xa3, 0, 0}, }; -static const struct reg_value ov5640_setting_15fps_QCIF_176_144[] = { - {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, - {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3814, 0x31, 0, 0}, - {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, - {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, - {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3810, 0x00, 0, 0}, - {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, - {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}, {0x4713, 0x03, 0, 0}, - {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, - {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, -}; - -static const struct reg_value ov5640_setting_30fps_NTSC_720_480[] = { - {0x3035, 0x12, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, +static const struct reg_value ov5640_setting_NTSC_720_480[] = { + {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, @@ -511,32 +447,12 @@ static const struct reg_value ov5640_setting_30fps_NTSC_720_480[] = { {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}, {0x4713, 0x03, 0, 0}, - {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, - {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x5001, 0xa3, 0, 0}, }; -static const struct reg_value ov5640_setting_15fps_NTSC_720_480[] = { - {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, - {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3814, 0x31, 0, 0}, - {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, - {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, - {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3810, 0x00, 0, 0}, - {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x3c, 0, 0}, - {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}, {0x4713, 0x03, 0, 0}, - {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, - {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, -}; - -static const struct reg_value ov5640_setting_30fps_PAL_720_576[] = { - {0x3035, 0x12, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, +static const struct reg_value ov5640_setting_PAL_720_576[] = { + {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, @@ -549,33 +465,12 @@ static const struct reg_value ov5640_setting_30fps_PAL_720_576[] = { {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}, {0x4713, 0x03, 0, 0}, - {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, - {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x5001, 0xa3, 0, 0}, }; -static const struct reg_value ov5640_setting_15fps_PAL_720_576[] = { - {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, - {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3814, 0x31, 0, 0}, - {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, - {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, - {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3810, 0x00, 0, 0}, - {0x3811, 0x38, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, - {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}, {0x4713, 0x03, 0, 0}, - {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, - {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, -}; - -static const struct reg_value ov5640_setting_30fps_720P_1280_720[] = { - {0x3008, 0x42, 0, 0}, - {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0}, +static const struct reg_value ov5640_setting_720P_1280_720[] = { + {0x3c07, 0x07, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, @@ -588,34 +483,13 @@ static const struct reg_value ov5640_setting_30fps_720P_1280_720[] = { {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}, - {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x02, 0, 0}, - {0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, - {0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0}, {0x4005, 0x1a, 0, 0}, - {0x3008, 0x02, 0, 0}, {0x3503, 0, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x5001, 0xa3, 0, 0}, }; -static const struct reg_value ov5640_setting_15fps_720P_1280_720[] = { - {0x3035, 0x41, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0}, - {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3814, 0x31, 0, 0}, - {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, - {0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0}, - {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0}, - {0x3810, 0x00, 0, 0}, - {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, - {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}, - {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x02, 0, 0}, - {0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, - {0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0}, -}; - -static const struct reg_value ov5640_setting_30fps_1080P_1920_1080[] = { +static const struct reg_value ov5640_setting_1080P_1920_1080[] = { {0x3008, 0x42, 0, 0}, - {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0}, + {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, {0x3814, 0x11, 0, 0}, {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, @@ -628,10 +502,10 @@ static const struct reg_value ov5640_setting_30fps_1080P_1920_1080[] = { {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, 0x06, 0, 0}, {0x4713, 0x03, 0, 0}, - {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, - {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0}, {0x3035, 0x11, 0, 0}, - {0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, + {0x4407, 0x04, 0, 0}, + {0x5001, 0x83, 0, 0}, + {0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, {0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0}, {0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0}, @@ -640,46 +514,12 @@ static const struct reg_value ov5640_setting_30fps_1080P_1920_1080[] = { {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}, - {0x3a15, 0x60, 0, 0}, {0x4713, 0x02, 0, 0}, {0x4407, 0x04, 0, 0}, - {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0}, + {0x3a15, 0x60, 0, 0}, {0x4407, 0x04, 0, 0}, {0x4005, 0x1a, 0, 0}, {0x3008, 0x02, 0, 0}, - {0x3503, 0, 0, 0}, -}; - -static const struct reg_value ov5640_setting_15fps_1080P_1920_1080[] = { - {0x3008, 0x42, 0, 0}, - {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0}, - {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3814, 0x11, 0, 0}, - {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, - {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0}, - {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0}, - {0x3810, 0x00, 0, 0}, - {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, - {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}, - {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0}, - {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, - {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0}, {0x3035, 0x21, 0, 0}, - {0x3036, 0x54, 0, 1}, {0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0}, - {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0}, - {0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0}, - {0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0}, - {0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0}, - {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}, - {0x3a15, 0x60, 0, 0}, {0x4713, 0x02, 0, 0}, {0x4407, 0x04, 0, 0}, - {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0}, - {0x4005, 0x1a, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3503, 0, 0, 0}, }; -static const struct reg_value ov5640_setting_15fps_QSXGA_2592_1944[] = { - {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0}, +static const struct reg_value ov5640_setting_QSXGA_2592_1944[] = { + {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, {0x3814, 0x11, 0, 0}, {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, @@ -692,9 +532,9 @@ static const struct reg_value ov5640_setting_15fps_QSXGA_2592_1944[] = { {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, 0x06, 0, 0}, {0x4713, 0x03, 0, 0}, - {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, - {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 70}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, + {0x4407, 0x04, 0, 0}, + {0x5001, 0x83, 0, 70}, }; /* power-on sensor init reg table */ @@ -705,79 +545,43 @@ static const struct ov5640_mode_info ov5640_mode_init_data = { }; static const struct ov5640_mode_info -ov5640_mode_data[OV5640_NUM_FRAMERATES][OV5640_NUM_MODES] = { - { - {OV5640_MODE_QCIF_176_144, SUBSAMPLING, - 176, 1896, 144, 984, - ov5640_setting_15fps_QCIF_176_144, - ARRAY_SIZE(ov5640_setting_15fps_QCIF_176_144)}, - {OV5640_MODE_QVGA_320_240, SUBSAMPLING, - 320, 1896, 240, 984, - ov5640_setting_15fps_QVGA_320_240, - ARRAY_SIZE(ov5640_setting_15fps_QVGA_320_240)}, - {OV5640_MODE_VGA_640_480, SUBSAMPLING, - 640, 1896, 480, 1080, - ov5640_setting_15fps_VGA_640_480, - ARRAY_SIZE(ov5640_setting_15fps_VGA_640_480)}, - {OV5640_MODE_NTSC_720_480, SUBSAMPLING, - 720, 1896, 480, 984, - ov5640_setting_15fps_NTSC_720_480, - ARRAY_SIZE(ov5640_setting_15fps_NTSC_720_480)}, - {OV5640_MODE_PAL_720_576, SUBSAMPLING, - 720, 1896, 576, 984, - ov5640_setting_15fps_PAL_720_576, - ARRAY_SIZE(ov5640_setting_15fps_PAL_720_576)}, - {OV5640_MODE_XGA_1024_768, SUBSAMPLING, - 1024, 1896, 768, 1080, - ov5640_setting_15fps_XGA_1024_768, - ARRAY_SIZE(ov5640_setting_15fps_XGA_1024_768)}, - {OV5640_MODE_720P_1280_720, SUBSAMPLING, - 1280, 1892, 720, 740, - ov5640_setting_15fps_720P_1280_720, - ARRAY_SIZE(ov5640_setting_15fps_720P_1280_720)}, - {OV5640_MODE_1080P_1920_1080, SCALING, - 1920, 2500, 1080, 1120, - ov5640_setting_15fps_1080P_1920_1080, - ARRAY_SIZE(ov5640_setting_15fps_1080P_1920_1080)}, - {OV5640_MODE_QSXGA_2592_1944, SCALING, - 2592, 2844, 1944, 1968, - ov5640_setting_15fps_QSXGA_2592_1944, - ARRAY_SIZE(ov5640_setting_15fps_QSXGA_2592_1944)}, - }, { - {OV5640_MODE_QCIF_176_144, SUBSAMPLING, - 176, 1896, 144, 984, - ov5640_setting_30fps_QCIF_176_144, - ARRAY_SIZE(ov5640_setting_30fps_QCIF_176_144)}, - {OV5640_MODE_QVGA_320_240, SUBSAMPLING, - 320, 1896, 240, 984, - ov5640_setting_30fps_QVGA_320_240, - ARRAY_SIZE(ov5640_setting_30fps_QVGA_320_240)}, - {OV5640_MODE_VGA_640_480, SUBSAMPLING, - 640, 1896, 480, 1080, - ov5640_setting_30fps_VGA_640_480, - ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480)}, - {OV5640_MODE_NTSC_720_480, SUBSAMPLING, - 720, 1896, 480, 984, - ov5640_setting_30fps_NTSC_720_480, - ARRAY_SIZE(ov5640_setting_30fps_NTSC_720_480)}, - {OV5640_MODE_PAL_720_576, SUBSAMPLING, - 720, 1896, 576, 984, - ov5640_setting_30fps_PAL_720_576, - ARRAY_SIZE(ov5640_setting_30fps_PAL_720_576)}, - {OV5640_MODE_XGA_1024_768, SUBSAMPLING, - 1024, 1896, 768, 1080, - ov5640_setting_30fps_XGA_1024_768, - ARRAY_SIZE(ov5640_setting_30fps_XGA_1024_768)}, - {OV5640_MODE_720P_1280_720, SUBSAMPLING, - 1280, 1892, 720, 740, - ov5640_setting_30fps_720P_1280_720, - ARRAY_SIZE(ov5640_setting_30fps_720P_1280_720)}, - {OV5640_MODE_1080P_1920_1080, SCALING, - 1920, 2500, 1080, 1120, - ov5640_setting_30fps_1080P_1920_1080, - ARRAY_SIZE(ov5640_setting_30fps_1080P_1920_1080)}, - {OV5640_MODE_QSXGA_2592_1944, -1, 0, 0, 0, 0, NULL, 0}, - }, +ov5640_mode_data[OV5640_NUM_MODES] = { + {OV5640_MODE_QCIF_176_144, SUBSAMPLING, + 176, 1896, 144, 984, + ov5640_setting_QCIF_176_144, + ARRAY_SIZE(ov5640_setting_QCIF_176_144)}, + {OV5640_MODE_QVGA_320_240, SUBSAMPLING, + 320, 1896, 240, 984, + ov5640_setting_QVGA_320_240, + ARRAY_SIZE(ov5640_setting_QVGA_320_240)}, + {OV5640_MODE_VGA_640_480, SUBSAMPLING, + 640, 1896, 480, 1080, + ov5640_setting_VGA_640_480, + ARRAY_SIZE(ov5640_setting_VGA_640_480)}, + {OV5640_MODE_NTSC_720_480, SUBSAMPLING, + 720, 1896, 480, 984, + ov5640_setting_NTSC_720_480, + ARRAY_SIZE(ov5640_setting_NTSC_720_480)}, + {OV5640_MODE_PAL_720_576, SUBSAMPLING, + 720, 1896, 576, 984, + ov5640_setting_PAL_720_576, + ARRAY_SIZE(ov5640_setting_PAL_720_576)}, + {OV5640_MODE_XGA_1024_768, SUBSAMPLING, + 1024, 1896, 768, 1080, + ov5640_setting_XGA_1024_768, + ARRAY_SIZE(ov5640_setting_XGA_1024_768)}, + {OV5640_MODE_720P_1280_720, SUBSAMPLING, + 1280, 1892, 720, 740, + ov5640_setting_720P_1280_720, + ARRAY_SIZE(ov5640_setting_720P_1280_720)}, + {OV5640_MODE_1080P_1920_1080, SCALING, + 1920, 2500, 1080, 1120, + ov5640_setting_1080P_1920_1080, + ARRAY_SIZE(ov5640_setting_1080P_1920_1080)}, + {OV5640_MODE_QSXGA_2592_1944, SCALING, + 2592, 2844, 1944, 1968, + ov5640_setting_QSXGA_2592_1944, + ARRAY_SIZE(ov5640_setting_QSXGA_2592_1944)}, }; static int ov5640_init_slave_id(struct ov5640_dev *sensor) @@ -909,27 +713,389 @@ static int ov5640_mod_reg(struct ov5640_dev *sensor, u16 reg, return ov5640_write_reg(sensor, reg, val); } -/* download ov5640 settings to sensor through i2c */ -static int ov5640_set_timings(struct ov5640_dev *sensor, - const struct ov5640_mode_info *mode) +/* + * After trying the various combinations, reading various + * documentations spreaded around the net, and from the various + * 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 + * +-+------------+ + * | +---------+ + * +->| Bit Div | - reg 0x3035, bits 0-3 + * +-+-------+ + * | +-------------+ + * +->| 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 + * + * This is deviating from the datasheet at least for the register + * 0x3108, since it's said here that the PCLK would be clocked from + * the PLL. + * + * There seems to be also (unverified) constraints: + * - 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 + * + * In the two latter cases, these constraints are met since our + * factors are hardcoded. If we were to change that, we would need to + * take this into account. The only varying parts are the PLL + * multiplier and the system clock divider, which are shared between + * all these clocks so won't cause any issue. + */ + +/* + * 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 + +/* + * Hardcode these values for scaler and non-scaler modes. + * FIXME: to be re-calcualted for 1 data lanes setups + */ +#define OV5640_MIPI_DIV_PCLK 2 +#define OV5640_MIPI_DIV_SCLK 1 + +/* + * 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. + */ + if (!rate) + 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. + * + * @rate: The requested bandwidth per lane in bytes per second. + * 'Bandwidth Per Lane' is calculated as: + * bpl = HTOT * VTOT * FPS * bpp / num_lanes; + * + * This function use the requested bandwidth to calculate: + * - sample_rate = bpl / (bpp / num_lanes); + * = bpl / (PLL_RDIV * BIT_DIV * PCLK_DIV * MIPI_DIV / num_lanes); + * + * - mipi_sclk = bpl / MIPI_DIV / 2; ( / 2 is for CSI-2 DDR) + * + * with these fixed parameters: + * PLL_RDIV = 2; + * BIT_DIVIDER = 2; (MIPI_BIT_MODE == 8 ? 2 : 2,5); + * PCLK_DIV = 1; + * + * The MIPI clock generation differs for modes that use the scaler and modes + * that do not. In case the scaler is in use, the MIPI_SCLK generates the MIPI + * BIT CLk, and thus: + * + * - mipi_sclk = bpl / MIPI_DIV / 2; + * MIPI_DIV = 1; + * + * For modes that do not go through the scaler, the MIPI BIT CLOCK is generated + * from the pixel clock, and thus: + * + * - sample_rate = bpl / (bpp / num_lanes); + * = bpl / (2 * 2 * 1 * MIPI_DIV / num_lanes); + * = bpl / (4 * MIPI_DIV / num_lanes); + * - MIPI_DIV = bpp / (4 * num_lanes); + * + * FIXME: this have been tested with 16bpp and 2 lanes setup only. + * MIPI_DIV is fixed to value 2, but it -might- be changed according to the + * above formula for setups with 1 lane or image formats with different bpp. + * + * FIXME: this deviates from the sensor manual documentation which is quite + * thin on the MIPI clock tree generation part. + */ +static int ov5640_set_mipi_pclk(struct ov5640_dev *sensor, + unsigned long rate) { + const struct ov5640_mode_info *mode = sensor->current_mode; + u8 prediv, mult, sysdiv; + u8 mipi_div; int ret; - ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPHO, mode->hact); - if (ret < 0) + /* + * 1280x720 is reported to use 'SUBSAMPLING' only, + * but according to the sensor manual it goes through the + * scaler before subsampling. + */ + if (mode->dn_mode == SCALING || + (mode->id == OV5640_MODE_720P_1280_720)) + mipi_div = OV5640_MIPI_DIV_SCLK; + else + mipi_div = OV5640_MIPI_DIV_PCLK; + + ov5640_calc_sys_clk(sensor, rate, &prediv, &mult, &sysdiv); + + ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL0, + 0x0f, OV5640_PLL_CTRL0_MIPI_MODE_8BIT); + + ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL1, + 0xff, sysdiv << 4 | mipi_div); + if (ret) return ret; - ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPVO, mode->vact); + 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, OV5640_PLL_CTRL3_PLL_ROOT_DIV_2 | prediv); + if (ret) + return ret; + + return ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, + 0x30, OV5640_PLL_SYS_ROOT_DIVIDER_BYPASS); +} + +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; +} + +static int ov5640_set_dvp_pclk(struct ov5640_dev *sensor, unsigned long rate) +{ + const struct ov5640_mode_info *mode = sensor->current_mode; + u8 prediv, mult, sysdiv, pll_rdiv, bit_div, pclk_div; + struct i2c_client *client = sensor->i2c_client; + unsigned int pclk_freq, max_pclk_freq; + u8 dvp_pclk_divider; + int ret; + + /* + * 1280x720 and 1024x768 are reported to use 'SUBSAMPLING' only, + * but they seems to go through the scaler before subsampling. + */ + if (mode->dn_mode == SCALING || + (mode->id == OV5640_MODE_720P_1280_720) || + (mode->id == OV5640_MODE_XGA_1024_768)) + dvp_pclk_divider = 1; + else + dvp_pclk_divider = 2; + + ret = ov5640_write_reg(sensor, OV5640_REG_DVP_PCLK_DIVIDER, + dvp_pclk_divider); + if (ret) + return ret; + pclk_freq = rate / dvp_pclk_divider; + max_pclk_freq = sensor->ep.bus.parallel.pclk_max_frequency; + + /* clip rate according to optional maximum pixel clock limit */ + if (max_pclk_freq && (pclk_freq > max_pclk_freq)) { + rate = max_pclk_freq * dvp_pclk_divider; + dev_dbg(&client->dev, "DVP pixel clock too high (%d > %d Hz), reducing rate...\n", + pclk_freq, max_pclk_freq); + } + + 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)); +} + +#if 0 +/* set JPEG framing sizes */ +static int ov5640_set_jpeg_timings(struct ov5640_dev *sensor, + const struct ov5640_mode_info *mode) +{ + int ret; + + /* + * 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; - ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HTS, mode->htot); + ret = ov5640_write_reg16(sensor, OV5640_REG_VFIFO_HSIZE, mode->hact); if (ret < 0) return ret; - return ov5640_write_reg16(sensor, OV5640_REG_TIMING_VTS, mode->vtot); + return ov5640_write_reg16(sensor, OV5640_REG_VFIFO_VSIZE, mode->vact); } +#endif +/* download ov5640 settings to sensor through i2c */ static int ov5640_load_regs(struct ov5640_dev *sensor, const struct ov5640_mode_info *mode) { @@ -957,7 +1123,7 @@ static int ov5640_load_regs(struct ov5640_dev *sensor, usleep_range(1000 * delay_ms, 1000 * delay_ms + 100); } - return ov5640_set_timings(sensor, mode); + return ret; } static int ov5640_set_autoexposure(struct ov5640_dev *sensor, bool on) @@ -1062,16 +1228,6 @@ static int ov5640_set_stream_dvp(struct ov5640_dev *sensor, bool on) if (on) { /* - * reset MIPI PCLK/SERCLK divider - * - * SC PLL CONTRL1 0 - * - [3..0]: MIPI PCLK/SERCLK divider - */ - ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL1, 0x0f, 0); - if (ret) - return ret; - - /* * configure parallel port control lines polarity * * POLARITY CTRL0 @@ -1438,14 +1594,44 @@ static int ov5640_set_virtual_channel(struct ov5640_dev *sensor) return ov5640_write_reg(sensor, OV5640_REG_DEBUG_MODE, temp); } +static int ov5640_set_timings(struct ov5640_dev *sensor, + const struct ov5640_mode_info *mode) +{ + int ret; +#if 0 + if (sensor->fmt.code == MEDIA_BUS_FMT_JPEG_1X8) { + ret = ov5640_set_jpeg_timings(sensor, mode); + if (ret < 0) + return ret; + } +#endif + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPHO, mode->hact); + if (ret < 0) + return ret; + + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPVO, mode->vact); + if (ret < 0) + return ret; + + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HTS, mode->htot); + if (ret < 0) + return ret; + + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_VTS, mode->vtot); + if (ret < 0) + return ret; + + return 0; +} + static const struct ov5640_mode_info * ov5640_find_mode(struct ov5640_dev *sensor, enum ov5640_frame_rate fr, int width, int height, bool nearest) { const struct ov5640_mode_info *mode; - mode = v4l2_find_nearest_size(ov5640_mode_data[fr], - ARRAY_SIZE(ov5640_mode_data[fr]), + mode = v4l2_find_nearest_size(ov5640_mode_data, + ARRAY_SIZE(ov5640_mode_data), hact, vact, width, height); @@ -1453,6 +1639,11 @@ ov5640_find_mode(struct ov5640_dev *sensor, enum ov5640_frame_rate fr, (!nearest && (mode->hact != width || mode->vact != height))) return NULL; + /* Only 640x480 can operate at 60fps (for now) */ + if (fr == OV5640_60_FPS && + !(mode->hact == 640 && mode->vact == 480)) + return NULL; + return mode; } @@ -1637,8 +1828,12 @@ static int ov5640_set_mode(struct ov5640_dev *sensor) enum ov5640_downsize_mode dn_mode, orig_dn_mode; bool auto_gain = sensor->ctrls.auto_gain->val == 1; bool auto_exp = sensor->ctrls.auto_exp->val == V4L2_EXPOSURE_AUTO; + unsigned long rate; int ret; + if (!orig_mode) + orig_mode = mode; + dn_mode = mode->dn_mode; orig_dn_mode = orig_mode->dn_mode; @@ -1655,6 +1850,23 @@ static int ov5640_set_mode(struct ov5640_dev *sensor) goto restore_auto_gain; } + /* + * All the formats we support have 16 bits per pixel, seems to require + * the same rate than YUV, so we can just use 16 bpp all the time. + */ + rate = mode->vtot * mode->htot * 16; + rate *= ov5640_framerates[sensor->current_fr]; + if (sensor->ep.bus_type == V4L2_MBUS_CSI2) { + rate = rate / sensor->ep.bus.mipi_csi2.num_data_lanes; + ret = ov5640_set_mipi_pclk(sensor, rate); + } else { + rate = rate / sensor->ep.bus.parallel.bus_width; + ret = ov5640_set_dvp_pclk(sensor, rate); + } + + if (ret < 0) + return 0; + if ((dn_mode == SUBSAMPLING && orig_dn_mode == SCALING) || (dn_mode == SCALING && orig_dn_mode == SUBSAMPLING)) { /* @@ -1678,6 +1890,10 @@ static int ov5640_set_mode(struct ov5640_dev *sensor) if (auto_exp) ov5640_set_autoexposure(sensor, true); + ret = ov5640_set_timings(sensor, mode); + if (ret < 0) + return ret; + ret = ov5640_set_binning(sensor, dn_mode != SCALING); if (ret < 0) return ret; @@ -1724,8 +1940,8 @@ static int ov5640_restore_mode(struct ov5640_dev *sensor) sensor->last_mode = &ov5640_mode_init_data; ret = ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, 0x3f, - (ilog2(OV5640_SCLK2X_ROOT_DIVIDER_DEFAULT) << 2) | - ilog2(OV5640_SCLK_ROOT_DIVIDER_DEFAULT)); + (ilog2(OV5640_SCLK2X_ROOT_DIV) << 2) | + ilog2(OV5640_SCLK_ROOT_DIV)); if (ret) return ret; @@ -1925,34 +2141,39 @@ static int ov5640_try_frame_interval(struct ov5640_dev *sensor, u32 width, u32 height) { const struct ov5640_mode_info *mode; - u32 minfps, maxfps, fps; - int ret; + enum ov5640_frame_rate rate = OV5640_15_FPS; + int minfps, maxfps, best_fps, fps; + int i; minfps = ov5640_framerates[OV5640_15_FPS]; - maxfps = ov5640_framerates[OV5640_30_FPS]; + maxfps = ov5640_framerates[OV5640_60_FPS]; if (fi->numerator == 0) { fi->denominator = maxfps; fi->numerator = 1; - return OV5640_30_FPS; + rate = OV5640_60_FPS; + goto find_mode; } - fps = DIV_ROUND_CLOSEST(fi->denominator, fi->numerator); + fps = clamp_val(DIV_ROUND_CLOSEST(fi->denominator, fi->numerator), + minfps, maxfps); - fi->numerator = 1; - if (fps > maxfps) - fi->denominator = maxfps; - else if (fps < minfps) - fi->denominator = minfps; - else if (2 * fps >= 2 * minfps + (maxfps - minfps)) - fi->denominator = maxfps; - else - fi->denominator = minfps; + best_fps = minfps; + for (i = 0; i < ARRAY_SIZE(ov5640_framerates); i++) { + int curr_fps = ov5640_framerates[i]; + + if (abs(curr_fps - fps) < abs(best_fps - fps)) { + best_fps = curr_fps; + rate = i; + } + } - ret = (fi->denominator == minfps) ? OV5640_15_FPS : OV5640_30_FPS; + fi->numerator = 1; + fi->denominator = best_fps; - mode = ov5640_find_mode(sensor, ret, width, height, false); - return mode ? ret : -EINVAL; +find_mode: + mode = ov5640_find_mode(sensor, rate, width, height, false); + return mode ? rate : -EINVAL; } static int ov5640_get_fmt(struct v4l2_subdev *sd, @@ -2013,6 +2234,10 @@ static int ov5640_try_fmt_internal(struct v4l2_subdev *sd, return 0; } +static const s64 link_freq_menu_items[] = { + 384000000, +}; + static int ov5640_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format) @@ -2061,46 +2286,67 @@ static int ov5640_set_framefmt(struct ov5640_dev *sensor, struct v4l2_mbus_framefmt *format) { int ret = 0; - bool is_rgb = false; bool is_jpeg = false; - u8 val; + u8 fmt, mux; switch (format->code) { case MEDIA_BUS_FMT_UYVY8_2X8: /* YUV422, UYVY */ - val = 0x3f; + fmt = 0x3f; + mux = OV5640_FMT_MUX_YUV422; break; case MEDIA_BUS_FMT_YUYV8_2X8: /* YUV422, YUYV */ - val = 0x30; + fmt = 0x30; + mux = OV5640_FMT_MUX_YUV422; break; case MEDIA_BUS_FMT_RGB565_2X8_LE: /* RGB565 {g[2:0],b[4:0]},{r[4:0],g[5:3]} */ - val = 0x6F; - is_rgb = true; + fmt = 0x6F; + mux = OV5640_FMT_MUX_RGB; break; case MEDIA_BUS_FMT_RGB565_2X8_BE: /* RGB565 {r[4:0],g[5:3]},{g[2:0],b[4:0]} */ - val = 0x61; - is_rgb = true; + fmt = 0x61; + mux = OV5640_FMT_MUX_RGB; break; case MEDIA_BUS_FMT_JPEG_1X8: /* YUV422, YUYV */ - val = 0x30; + fmt = 0x30; + mux = OV5640_FMT_MUX_YUV422; is_jpeg = true; break; + case MEDIA_BUS_FMT_SBGGR8_1X8: + /* Raw, BGBG... / GRGR... */ + fmt = 0x00; + mux = OV5640_FMT_MUX_RAW_DPC; + break; + case MEDIA_BUS_FMT_SGBRG8_1X8: + /* Raw bayer, GBGB... / RGRG... */ + fmt = 0x01; + mux = OV5640_FMT_MUX_RAW_DPC; + break; + case MEDIA_BUS_FMT_SGRBG8_1X8: + /* Raw bayer, GRGR... / BGBG... */ + fmt = 0x02; + mux = OV5640_FMT_MUX_RAW_DPC; + break; + case MEDIA_BUS_FMT_SRGGB8_1X8: + /* Raw bayer, RGRG... / GBGB... */ + fmt = 0x03; + mux = OV5640_FMT_MUX_RAW_DPC; + break; default: return -EINVAL; } /* FORMAT CONTROL00: YUV and RGB formatting */ - ret = ov5640_write_reg(sensor, OV5640_REG_FORMAT_CONTROL00, val); + ret = ov5640_write_reg(sensor, OV5640_REG_FORMAT_CONTROL00, fmt); if (ret) return ret; /* FORMAT MUX CONTROL: ISP YUV or RGB */ - ret = ov5640_write_reg(sensor, OV5640_REG_ISP_FORMAT_MUX_CTRL, - is_rgb ? 0x01 : 0x00); + ret = ov5640_write_reg(sensor, OV5640_REG_ISP_FORMAT_MUX_CTRL, mux); if (ret) return ret; @@ -2268,10 +2514,41 @@ static int ov5640_set_ctrl_gain(struct ov5640_dev *sensor, bool auto_gain) return ret; } +static const char * const test_pattern_menu[] = { + "Disabled", + "Color bars", + "Color bars w/ rolling bar", + "Color squares", + "Color squares w/ rolling bar", +}; + +#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, + OV5640_TEST_ENABLE | OV5640_TEST_BAR_VERT_CHANGE_1 | + OV5640_TEST_BAR, + 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, +}; + static int ov5640_set_ctrl_test_pattern(struct ov5640_dev *sensor, int value) { - return ov5640_mod_reg(sensor, OV5640_REG_PRE_ISP_TEST_SET1, - 0xa4, value ? 0xa4 : 0); + return ov5640_write_reg(sensor, OV5640_REG_PRE_ISP_TEST_SET1, + test_pattern_val[value]); } static int ov5640_set_ctrl_light_freq(struct ov5640_dev *sensor, int value) @@ -2399,6 +2676,8 @@ static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_VFLIP: ret = ov5640_set_ctrl_vflip(sensor, ctrl->val); break; + case V4L2_CID_LINK_FREQ: + return 0; default: ret = -EINVAL; break; @@ -2412,11 +2691,6 @@ static const struct v4l2_ctrl_ops ov5640_ctrl_ops = { .s_ctrl = ov5640_s_ctrl, }; -static const char * const test_pattern_menu[] = { - "Disabled", - "Color bars", -}; - static int ov5640_init_controls(struct ov5640_dev *sensor) { const struct v4l2_ctrl_ops *ops = &ov5640_ctrl_ops; @@ -2471,6 +2745,9 @@ static int ov5640_init_controls(struct ov5640_dev *sensor) V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0, V4L2_CID_POWER_LINE_FREQUENCY_50HZ); + ctrls->link_freq = v4l2_ctrl_new_int_menu(hdl, ops, V4L2_CID_LINK_FREQ, + 0, 0, link_freq_menu_items); + if (hdl->error) { ret = hdl->error; goto free_ctrls; @@ -2501,10 +2778,10 @@ static int ov5640_enum_frame_size(struct v4l2_subdev *sd, return -EINVAL; fse->min_width = - ov5640_mode_data[0][fse->index].hact; + ov5640_mode_data[fse->index].hact; fse->max_width = fse->min_width; fse->min_height = - ov5640_mode_data[0][fse->index].vact; + ov5640_mode_data[fse->index].vact; fse->max_height = fse->min_height; return 0; @@ -2569,8 +2846,11 @@ static int ov5640_s_frame_interval(struct v4l2_subdev *sd, frame_rate = ov5640_try_frame_interval(sensor, &fi->interval, mode->hact, mode->vact); - if (frame_rate < 0) - frame_rate = OV5640_15_FPS; + if (frame_rate < 0) { + /* Always return a valid frame interval value */ + fi->interval = sensor->frame_interval; + goto out; + } mode = ov5640_find_mode(sensor, frame_rate, mode->hact, mode->vact, true); @@ -2735,7 +3015,7 @@ static int ov5640_probe(struct i2c_client *client, sensor->frame_interval.denominator = ov5640_framerates[OV5640_30_FPS]; sensor->current_fr = OV5640_30_FPS; sensor->current_mode = - &ov5640_mode_data[OV5640_30_FPS][OV5640_MODE_VGA_640_480]; + &ov5640_mode_data[OV5640_MODE_VGA_640_480]; sensor->last_mode = sensor->current_mode; sensor->ae_target = 52; diff --git a/drivers/media/i2c/st-mipid02.c b/drivers/media/i2c/st-mipid02.c new file mode 100644 index 0000000..7751960 --- /dev/null +++ b/drivers/media/i2c/st-mipid02.c @@ -0,0 +1,1076 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for ST MIPID02 CSI-2 to PARALLEL bridge + * + * Copyright (C) STMicroelectronics SA 2019 + * Authors: Mickael Guene + * for STMicroelectronics. + * + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define V4L2_MBUS_CSI2_DPHY V4L2_MBUS_CSI2 + +#define MIPID02_CLK_LANE_WR_REG1 0x01 +#define MIPID02_CLK_LANE_REG1 0x02 +#define MIPID02_CLK_LANE_REG3 0x04 +#define MIPID02_DATA_LANE0_REG1 0x05 +#define MIPID02_DATA_LANE0_REG2 0x06 +#define MIPID02_DATA_LANE1_REG1 0x09 +#define MIPID02_DATA_LANE1_REG2 0x0a +#define MIPID02_MODE_REG1 0x14 +#define MIPID02_MODE_REG2 0x15 +#define MIPID02_DATA_ID_RREG 0x17 +#define MIPID02_DATA_SELECTION_CTRL 0x19 +#define MIPID02_PIX_WIDTH_CTRL 0x1e +#define MIPID02_PIX_WIDTH_CTRL_EMB 0x1f + +/* Bits definition for MIPID02_CLK_LANE_REG1 */ +#define CLK_ENABLE BIT(0) +/* Bits definition for MIPID02_CLK_LANE_REG3 */ +#define CLK_MIPI_CSI BIT(1) +/* Bits definition for MIPID02_DATA_LANE0_REG1 */ +#define DATA_ENABLE BIT(0) +/* Bits definition for MIPID02_DATA_LANEx_REG2 */ +#define DATA_MIPI_CSI BIT(0) +/* Bits definition for MIPID02_MODE_REG1 */ +#define MODE_DATA_SWAP BIT(2) +#define MODE_NO_BYPASS BIT(6) +/* Bits definition for MIPID02_MODE_REG2 */ +#define MODE_HSYNC_ACTIVE_HIGH BIT(1) +#define MODE_VSYNC_ACTIVE_HIGH BIT(2) +/* Bits definition for MIPID02_DATA_SELECTION_CTRL */ +#define SELECTION_MANUAL_DATA BIT(2) +#define SELECTION_MANUAL_WIDTH BIT(3) + +static const u32 mipid02_supported_fmt_codes[] = { + MEDIA_BUS_FMT_SBGGR8_1X8, MEDIA_BUS_FMT_SGBRG8_1X8, + MEDIA_BUS_FMT_SGRBG8_1X8, MEDIA_BUS_FMT_SRGGB8_1X8, + MEDIA_BUS_FMT_SBGGR10_1X10, MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10, MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SBGGR12_1X12, MEDIA_BUS_FMT_SGBRG12_1X12, + MEDIA_BUS_FMT_SGRBG12_1X12, MEDIA_BUS_FMT_SRGGB12_1X12, + MEDIA_BUS_FMT_UYVY8_1X16, MEDIA_BUS_FMT_BGR888_1X24, + MEDIA_BUS_FMT_RGB565_2X8_LE, MEDIA_BUS_FMT_RGB565_2X8_BE, + MEDIA_BUS_FMT_YUYV8_2X8, MEDIA_BUS_FMT_UYVY8_2X8, + MEDIA_BUS_FMT_JPEG_1X8 +}; + +/* regulator supplies */ +static const char * const mipid02_supply_name[] = { + "VDDE", /* 1.8V digital I/O supply */ + "VDDIN", /* 1V8 voltage regulator supply */ +}; + +#define MIPID02_NUM_SUPPLIES ARRAY_SIZE(mipid02_supply_name) + +#define MIPID02_SINK_0 0 +#define MIPID02_SINK_1 1 +#define MIPID02_SOURCE 2 +#define MIPID02_PAD_NB 3 + +struct mipid02_dev { + struct i2c_client *i2c_client; + struct regulator_bulk_data supplies[MIPID02_NUM_SUPPLIES]; + struct v4l2_subdev sd; + struct media_pad pad[MIPID02_PAD_NB]; + struct clk *xclk; + struct gpio_desc *reset_gpio; + /* endpoints info */ + struct v4l2_fwnode_endpoint rx; + u64 link_frequency; + struct v4l2_fwnode_endpoint tx; + /* remote source */ + struct v4l2_async_subdev asd; + struct v4l2_async_notifier notifier; + struct v4l2_subdev *s_subdev; + /* registers */ + struct { + u8 clk_lane_reg1; + u8 data_lane0_reg1; + u8 data_lane1_reg1; + u8 mode_reg1; + u8 mode_reg2; + u8 data_selection_ctrl; + u8 data_id_rreg; + u8 pix_width_ctrl; + u8 pix_width_ctrl_emb; + } r; + /* lock to protect all members below */ + struct mutex lock; + bool streaming; + struct v4l2_mbus_framefmt fmt; +}; + +static int bpp_from_code(__u32 code) +{ + switch (code) { + case MEDIA_BUS_FMT_SBGGR8_1X8: + case MEDIA_BUS_FMT_SGBRG8_1X8: + case MEDIA_BUS_FMT_SGRBG8_1X8: + case MEDIA_BUS_FMT_SRGGB8_1X8: + return 8; + case MEDIA_BUS_FMT_SBGGR10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SRGGB10_1X10: + return 10; + case MEDIA_BUS_FMT_SBGGR12_1X12: + case MEDIA_BUS_FMT_SGBRG12_1X12: + case MEDIA_BUS_FMT_SGRBG12_1X12: + case MEDIA_BUS_FMT_SRGGB12_1X12: + return 12; + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_YUYV8_2X8: + case MEDIA_BUS_FMT_UYVY8_2X8: + case MEDIA_BUS_FMT_RGB565_2X8_LE: + case MEDIA_BUS_FMT_RGB565_2X8_BE: + return 16; + case MEDIA_BUS_FMT_BGR888_1X24: + return 24; + default: + return 0; + } +} + +static u8 data_type_from_code(__u32 code) +{ + switch (code) { + case MEDIA_BUS_FMT_SBGGR8_1X8: + case MEDIA_BUS_FMT_SGBRG8_1X8: + case MEDIA_BUS_FMT_SGRBG8_1X8: + case MEDIA_BUS_FMT_SRGGB8_1X8: + return 0x2a; + case MEDIA_BUS_FMT_SBGGR10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SRGGB10_1X10: + return 0x2b; + case MEDIA_BUS_FMT_SBGGR12_1X12: + case MEDIA_BUS_FMT_SGBRG12_1X12: + case MEDIA_BUS_FMT_SGRBG12_1X12: + case MEDIA_BUS_FMT_SRGGB12_1X12: + return 0x2c; + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_YUYV8_2X8: + case MEDIA_BUS_FMT_UYVY8_2X8: + return 0x1e; + case MEDIA_BUS_FMT_BGR888_1X24: + return 0x24; + case MEDIA_BUS_FMT_RGB565_2X8_LE: + case MEDIA_BUS_FMT_RGB565_2X8_BE: + return 0x22; + default: + return 0; + } +} + +static void init_format(struct v4l2_mbus_framefmt *fmt) +{ + fmt->code = MEDIA_BUS_FMT_SBGGR8_1X8; + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(V4L2_COLORSPACE_SRGB); + fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; + fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(V4L2_COLORSPACE_SRGB); + fmt->width = 640; + fmt->height = 480; +} + +static __u32 get_fmt_code(__u32 code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(mipid02_supported_fmt_codes); i++) { + if (code == mipid02_supported_fmt_codes[i]) + return code; + } + + return mipid02_supported_fmt_codes[0]; +} + +static __u32 serial_to_parallel_code(__u32 serial) +{ + if (serial == MEDIA_BUS_FMT_UYVY8_1X16) + return MEDIA_BUS_FMT_UYVY8_2X8; + if (serial == MEDIA_BUS_FMT_BGR888_1X24) + return MEDIA_BUS_FMT_BGR888_3X8; + + return serial; +} + +static inline struct mipid02_dev *to_mipid02_dev(struct v4l2_subdev *sd) +{ + return container_of(sd, struct mipid02_dev, sd); +} + +static int mipid02_read_reg(struct mipid02_dev *bridge, u16 reg, u8 *val) +{ + struct i2c_client *client = bridge->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 = val; + msg[1].len = 1; + + ret = i2c_transfer(client->adapter, msg, 2); + if (ret < 0) { + dev_dbg(&client->dev, "%s: %x i2c_transfer, reg: %x => %d\n", + __func__, client->addr, reg, ret); + return ret; + } + + return 0; +} + +static int mipid02_write_reg(struct mipid02_dev *bridge, u16 reg, u8 val) +{ + struct i2c_client *client = bridge->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) { + dev_dbg(&client->dev, "%s: i2c_transfer, reg: %x => %d\n", + __func__, reg, ret); + return ret; + } + + return 0; +} + +static int mipid02_get_regulators(struct mipid02_dev *bridge) +{ + unsigned int i; + + for (i = 0; i < MIPID02_NUM_SUPPLIES; i++) + bridge->supplies[i].supply = mipid02_supply_name[i]; + + return devm_regulator_bulk_get(&bridge->i2c_client->dev, + MIPID02_NUM_SUPPLIES, + bridge->supplies); +} + +static void mipid02_apply_reset(struct mipid02_dev *bridge) +{ + gpiod_set_value_cansleep(bridge->reset_gpio, 0); + usleep_range(5000, 10000); + gpiod_set_value_cansleep(bridge->reset_gpio, 1); + usleep_range(5000, 10000); + gpiod_set_value_cansleep(bridge->reset_gpio, 0); + usleep_range(5000, 10000); +} + +static int mipid02_set_power_on(struct mipid02_dev *bridge) +{ + struct i2c_client *client = bridge->i2c_client; + int ret; + + ret = clk_prepare_enable(bridge->xclk); + if (ret) { + dev_err(&client->dev, "%s: failed to enable clock\n", __func__); + return ret; + } + + ret = regulator_bulk_enable(MIPID02_NUM_SUPPLIES, + bridge->supplies); + if (ret) { + dev_err(&client->dev, "%s: failed to enable regulators\n", + __func__); + goto xclk_off; + } + + if (bridge->reset_gpio) { + dev_dbg(&client->dev, "apply reset"); + mipid02_apply_reset(bridge); + } else { + dev_dbg(&client->dev, "don't apply reset"); + usleep_range(5000, 10000); + } + + return 0; + +xclk_off: + clk_disable_unprepare(bridge->xclk); + return ret; +} + +static void mipid02_set_power_off(struct mipid02_dev *bridge) +{ + regulator_bulk_disable(MIPID02_NUM_SUPPLIES, bridge->supplies); + clk_disable_unprepare(bridge->xclk); +} + +static int mipid02_detect(struct mipid02_dev *bridge) +{ + u8 reg; + + /* + * There is no version registers. Just try to read register + * MIPID02_CLK_LANE_WR_REG1. + */ + return mipid02_read_reg(bridge, MIPID02_CLK_LANE_WR_REG1, ®); +} + +static u32 mipid02_get_link_freq_from_cid_link_freq(struct mipid02_dev *bridge, + struct v4l2_subdev *subdev) +{ + struct v4l2_querymenu qm = {.id = V4L2_CID_LINK_FREQ, }; + struct v4l2_ctrl *ctrl; + int ret; + + ctrl = v4l2_ctrl_find(subdev->ctrl_handler, V4L2_CID_LINK_FREQ); + if (!ctrl) + return 0; + qm.index = v4l2_ctrl_g_ctrl(ctrl); + + ret = v4l2_querymenu(subdev->ctrl_handler, &qm); + if (ret) + return 0; + + return qm.value; +} + +static u32 mipid02_get_link_freq_from_cid_pixel_rate(struct mipid02_dev *bridge, + struct v4l2_subdev *subdev) +{ + struct v4l2_fwnode_endpoint *ep = &bridge->rx; + struct v4l2_ctrl *ctrl; + u32 pixel_clock; + u32 bpp = bpp_from_code(bridge->fmt.code); + + ctrl = v4l2_ctrl_find(subdev->ctrl_handler, V4L2_CID_PIXEL_RATE); + if (!ctrl) + return 0; + pixel_clock = v4l2_ctrl_g_ctrl_int64(ctrl); + + return pixel_clock * bpp / (2 * ep->bus.mipi_csi2.num_data_lanes); +} + +/* + * We need to know link frequency to setup clk_lane_reg1 timings. Link frequency + * will be computed using connected device V4L2_CID_PIXEL_RATE, bit per pixel + * and number of lanes. + */ +static int mipid02_configure_from_rx_speed(struct mipid02_dev *bridge) +{ + struct i2c_client *client = bridge->i2c_client; + struct v4l2_subdev *subdev = bridge->s_subdev; + u32 link_freq; + + link_freq = mipid02_get_link_freq_from_cid_link_freq(bridge, subdev); + if (!link_freq) { + link_freq = mipid02_get_link_freq_from_cid_pixel_rate(bridge, + subdev); + if (!link_freq) { + dev_err(&client->dev, "Failed to get link frequency"); + return -EINVAL; + } + } + + dev_dbg(&client->dev, "detect link_freq = %d Hz", link_freq); + bridge->r.clk_lane_reg1 |= (2000000000 / link_freq) << 2; + + return 0; +} + +static int mipid02_configure_clk_lane(struct mipid02_dev *bridge) +{ + struct i2c_client *client = bridge->i2c_client; + struct v4l2_fwnode_endpoint *ep = &bridge->rx; + bool *polarities = ep->bus.mipi_csi2.lane_polarities; + + /* midid02 doesn't support clock lane remapping */ + if (ep->bus.mipi_csi2.clock_lane != 0) { + dev_err(&client->dev, "clk lane must be map to lane 0\n"); + return -EINVAL; + } + bridge->r.clk_lane_reg1 |= (polarities[0] << 1) | CLK_ENABLE; + + return 0; +} + +static int mipid02_configure_data0_lane(struct mipid02_dev *bridge, int nb, + bool are_lanes_swap, bool *polarities) +{ + bool are_pin_swap = are_lanes_swap ? polarities[2] : polarities[1]; + + if (nb == 1 && are_lanes_swap) + return 0; + + /* + * data lane 0 as pin swap polarity reversed compared to clock and + * data lane 1 + */ + if (!are_pin_swap) + bridge->r.data_lane0_reg1 = 1 << 1; + bridge->r.data_lane0_reg1 |= DATA_ENABLE; + + return 0; +} + +static int mipid02_configure_data1_lane(struct mipid02_dev *bridge, int nb, + bool are_lanes_swap, bool *polarities) +{ + bool are_pin_swap = are_lanes_swap ? polarities[1] : polarities[2]; + + if (nb == 1 && !are_lanes_swap) + return 0; + + if (are_pin_swap) + bridge->r.data_lane1_reg1 = 1 << 1; + bridge->r.data_lane1_reg1 |= DATA_ENABLE; + + return 0; +} + +static int mipid02_configure_from_rx(struct mipid02_dev *bridge) +{ + struct v4l2_fwnode_endpoint *ep = &bridge->rx; + bool are_lanes_swap = ep->bus.mipi_csi2.data_lanes[0] == 2; + bool *polarities = ep->bus.mipi_csi2.lane_polarities; + int nb = ep->bus.mipi_csi2.num_data_lanes; + int ret; + + ret = mipid02_configure_clk_lane(bridge); + if (ret) + return ret; + + ret = mipid02_configure_data0_lane(bridge, nb, are_lanes_swap, + polarities); + if (ret) + return ret; + + ret = mipid02_configure_data1_lane(bridge, nb, are_lanes_swap, + polarities); + if (ret) + return ret; + + bridge->r.mode_reg1 |= are_lanes_swap ? MODE_DATA_SWAP : 0; + bridge->r.mode_reg1 |= (nb - 1) << 1; + + return mipid02_configure_from_rx_speed(bridge); +} + +static int mipid02_configure_from_tx(struct mipid02_dev *bridge) +{ + struct v4l2_fwnode_endpoint *ep = &bridge->tx; + + bridge->r.data_selection_ctrl = SELECTION_MANUAL_WIDTH; + bridge->r.pix_width_ctrl = ep->bus.parallel.bus_width; + bridge->r.pix_width_ctrl_emb = ep->bus.parallel.bus_width; + if (ep->bus.parallel.flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) + bridge->r.mode_reg2 |= MODE_HSYNC_ACTIVE_HIGH; + if (ep->bus.parallel.flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) + bridge->r.mode_reg2 |= MODE_VSYNC_ACTIVE_HIGH; + + return 0; +} + +static int mipid02_configure_from_code(struct mipid02_dev *bridge) +{ + u8 data_type; + + bridge->r.data_id_rreg = 0; + + if (bridge->fmt.code != MEDIA_BUS_FMT_JPEG_1X8) { + bridge->r.data_selection_ctrl |= SELECTION_MANUAL_DATA; + + data_type = data_type_from_code(bridge->fmt.code); + if (!data_type) + return -EINVAL; + bridge->r.data_id_rreg = data_type; + } + + return 0; +} + +static int mipid02_stream_disable(struct mipid02_dev *bridge) +{ + struct i2c_client *client = bridge->i2c_client; + int ret; + + /* Disable all lanes */ + ret = mipid02_write_reg(bridge, MIPID02_CLK_LANE_REG1, 0); + if (ret) + goto error; + ret = mipid02_write_reg(bridge, MIPID02_DATA_LANE0_REG1, 0); + if (ret) + goto error; + ret = mipid02_write_reg(bridge, MIPID02_DATA_LANE1_REG1, 0); + if (ret) + goto error; +error: + if (ret) + dev_err(&client->dev, "failed to stream off %d", ret); + + return ret; +} + +static int mipid02_stream_enable(struct mipid02_dev *bridge) +{ + struct i2c_client *client = bridge->i2c_client; + int ret = -EINVAL; + + if (!bridge->s_subdev) + goto error; + + memset(&bridge->r, 0, sizeof(bridge->r)); + /* build registers content */ + ret = mipid02_configure_from_rx(bridge); + if (ret) + goto error; + ret = mipid02_configure_from_tx(bridge); + if (ret) + goto error; + ret = mipid02_configure_from_code(bridge); + if (ret) + goto error; + + /* write mipi registers */ + ret = mipid02_write_reg(bridge, MIPID02_CLK_LANE_REG1, + bridge->r.clk_lane_reg1); + if (ret) + goto error; + ret = mipid02_write_reg(bridge, MIPID02_CLK_LANE_REG3, CLK_MIPI_CSI); + if (ret) + goto error; + ret = mipid02_write_reg(bridge, MIPID02_DATA_LANE0_REG1, + bridge->r.data_lane0_reg1); + if (ret) + goto error; + ret = mipid02_write_reg(bridge, MIPID02_DATA_LANE0_REG2, + DATA_MIPI_CSI); + if (ret) + goto error; + ret = mipid02_write_reg(bridge, MIPID02_DATA_LANE1_REG1, + bridge->r.data_lane1_reg1); + if (ret) + goto error; + ret = mipid02_write_reg(bridge, MIPID02_DATA_LANE1_REG2, + DATA_MIPI_CSI); + if (ret) + goto error; + ret = mipid02_write_reg(bridge, MIPID02_MODE_REG1, + MODE_NO_BYPASS | bridge->r.mode_reg1); + if (ret) + goto error; + ret = mipid02_write_reg(bridge, MIPID02_MODE_REG2, + bridge->r.mode_reg2); + if (ret) + goto error; + ret = mipid02_write_reg(bridge, MIPID02_DATA_ID_RREG, + bridge->r.data_id_rreg); + if (ret) + goto error; + ret = mipid02_write_reg(bridge, MIPID02_DATA_SELECTION_CTRL, + bridge->r.data_selection_ctrl); + if (ret) + goto error; + ret = mipid02_write_reg(bridge, MIPID02_PIX_WIDTH_CTRL, + bridge->r.pix_width_ctrl); + if (ret) + goto error; + ret = mipid02_write_reg(bridge, MIPID02_PIX_WIDTH_CTRL_EMB, + bridge->r.pix_width_ctrl_emb); + if (ret) + goto error; + + return 0; + +error: + dev_err(&client->dev, "failed to stream on %d", ret); + mipid02_stream_disable(bridge); + + return ret; +} + +static int mipid02_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct mipid02_dev *bridge = to_mipid02_dev(sd); + struct i2c_client *client = bridge->i2c_client; + int ret = 0; + + dev_dbg(&client->dev, "%s : requested %d / current = %d", __func__, + enable, bridge->streaming); + mutex_lock(&bridge->lock); + + if (bridge->streaming == enable) + goto out; + + ret = enable ? mipid02_stream_enable(bridge) : + mipid02_stream_disable(bridge); + if (!ret) + bridge->streaming = enable; + +out: + dev_dbg(&client->dev, "%s current now = %d / %d", __func__, + bridge->streaming, ret); + mutex_unlock(&bridge->lock); + + return ret; +} + +static int mipid02_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct mipid02_dev *bridge = to_mipid02_dev(sd); + int ret = 0; + + switch (code->pad) { + case MIPID02_SINK_0: + if (code->index >= ARRAY_SIZE(mipid02_supported_fmt_codes)) + ret = -EINVAL; + else + code->code = mipid02_supported_fmt_codes[code->index]; + break; + case MIPID02_SOURCE: + if (code->index == 0) + code->code = serial_to_parallel_code(bridge->fmt.code); + else + ret = -EINVAL; + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int mipid02_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *mbus_fmt = &format->format; + struct mipid02_dev *bridge = to_mipid02_dev(sd); + struct i2c_client *client = bridge->i2c_client; + struct v4l2_mbus_framefmt *fmt; + + dev_dbg(&client->dev, "%s probe %d", __func__, format->pad); + + if (format->pad >= MIPID02_PAD_NB) + return -EINVAL; + /* second CSI-2 pad not yet supported */ + if (format->pad == MIPID02_SINK_1) + return -EINVAL; + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) + fmt = v4l2_subdev_get_try_format(&bridge->sd, cfg, format->pad); + else + fmt = &bridge->fmt; + + mutex_lock(&bridge->lock); + + *mbus_fmt = *fmt; + /* code may need to be converted for source */ + if (format->pad == MIPID02_SOURCE) + mbus_fmt->code = serial_to_parallel_code(mbus_fmt->code); + + mutex_unlock(&bridge->lock); + + return 0; +} + +static void mipid02_set_fmt_source(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct mipid02_dev *bridge = to_mipid02_dev(sd); + + /* source pad mirror active sink pad */ + format->format = bridge->fmt; + /* but code may need to be converted */ + format->format.code = serial_to_parallel_code(format->format.code); + + /* only apply format for V4L2_SUBDEV_FORMAT_TRY case */ + if (format->which != V4L2_SUBDEV_FORMAT_TRY) + return; + + *v4l2_subdev_get_try_format(sd, cfg, format->pad) = format->format; +} + +static void mipid02_set_fmt_sink(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct mipid02_dev *bridge = to_mipid02_dev(sd); + struct v4l2_mbus_framefmt *fmt; + + format->format.code = get_fmt_code(format->format.code); + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) + fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); + else + fmt = &bridge->fmt; + + *fmt = format->format; +} + +static int mipid02_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct mipid02_dev *bridge = to_mipid02_dev(sd); + struct i2c_client *client = bridge->i2c_client; + int ret = 0; + + dev_dbg(&client->dev, "%s for %d", __func__, format->pad); + + if (format->pad >= MIPID02_PAD_NB) + return -EINVAL; + /* second CSI-2 pad not yet supported */ + if (format->pad == MIPID02_SINK_1) + return -EINVAL; + + mutex_lock(&bridge->lock); + + if (bridge->streaming) { + ret = -EBUSY; + goto error; + } + + if (format->pad == MIPID02_SOURCE) + mipid02_set_fmt_source(sd, cfg, format); + else + mipid02_set_fmt_sink(sd, cfg, format); + +error: + mutex_unlock(&bridge->lock); + + return ret; +} + +static const struct v4l2_subdev_video_ops mipid02_video_ops = { + .s_stream = mipid02_s_stream, +}; + +static const struct v4l2_subdev_pad_ops mipid02_pad_ops = { + .enum_mbus_code = mipid02_enum_mbus_code, + .get_fmt = mipid02_get_fmt, + .set_fmt = mipid02_set_fmt, +}; + +static const struct v4l2_subdev_ops mipid02_subdev_ops = { + .video = &mipid02_video_ops, + .pad = &mipid02_pad_ops, +}; + +static const struct media_entity_operations mipid02_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static int mipid02_async_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *s_subdev, + struct v4l2_async_subdev *asd) +{ + struct mipid02_dev *bridge = to_mipid02_dev(notifier->sd); + struct i2c_client *client = bridge->i2c_client; + int source_pad; + int ret; + + dev_dbg(&client->dev, "sensor_async_bound call %p", s_subdev); + + source_pad = media_entity_get_fwnode_pad(&s_subdev->entity, + s_subdev->fwnode, + MEDIA_PAD_FL_SOURCE); + if (source_pad < 0) { + dev_err(&client->dev, "Couldn't find output pad for subdev %s\n", + s_subdev->name); + return source_pad; + } + + ret = media_create_pad_link(&s_subdev->entity, source_pad, + &bridge->sd.entity, 0, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret) { + dev_err(&client->dev, "Couldn't create media link %d", ret); + return ret; + } + + bridge->s_subdev = s_subdev; + + return 0; +} + +static void mipid02_async_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *s_subdev, + struct v4l2_async_subdev *asd) +{ + struct mipid02_dev *bridge = to_mipid02_dev(notifier->sd); + + bridge->s_subdev = NULL; +} + +static const struct v4l2_async_notifier_operations mipid02_notifier_ops = { + .bound = mipid02_async_bound, + .unbind = mipid02_async_unbind, +}; + +static int mipid02_parse_rx_ep(struct mipid02_dev *bridge) +{ + struct v4l2_fwnode_endpoint ep = { .bus_type = V4L2_MBUS_CSI2_DPHY }; + struct i2c_client *client = bridge->i2c_client; + struct device_node *ep_node; + int ret; + + /* parse rx (endpoint 0) */ + ep_node = of_graph_get_endpoint_by_regs(bridge->i2c_client->dev.of_node, + 0, 0); + if (!ep_node) { + dev_err(&client->dev, "unable to find port0 ep"); + ret = -EINVAL; + goto error; + } + + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), &ep); + if (ret) { + dev_err(&client->dev, "Could not parse v4l2 endpoint %d\n", + ret); + goto error_of_node_put; + } + + /* do some sanity checks */ + if (ep.bus.mipi_csi2.num_data_lanes > 2) { + dev_err(&client->dev, "max supported data lanes is 2 / got %d", + ep.bus.mipi_csi2.num_data_lanes); + ret = -EINVAL; + goto error_of_node_put; + } + + /* register it for later use */ + bridge->rx = ep; + + /* register async notifier so we get noticed when sensor is connected */ + bridge->asd.match.fwnode = + fwnode_graph_get_remote_port_parent(of_fwnode_handle(ep_node)); + bridge->asd.match_type = V4L2_ASYNC_MATCH_FWNODE; + of_node_put(ep_node); + bridge->notifier.subdevs = + devm_kzalloc(&bridge->i2c_client->dev, + sizeof(*bridge->notifier.subdevs), + GFP_KERNEL); + if (!bridge->notifier.subdevs) + return -ENOMEM; + bridge->notifier.subdevs[0] = &bridge->asd; + bridge->notifier.num_subdevs = 1; + bridge->notifier.ops = &mipid02_notifier_ops; + + ret = v4l2_async_subdev_notifier_register(&bridge->sd, + &bridge->notifier); + if (ret) + v4l2_async_notifier_cleanup(&bridge->notifier); + + return ret; + +error_of_node_put: + of_node_put(ep_node); +error: + + return ret; +} + +static int mipid02_parse_tx_ep(struct mipid02_dev *bridge) +{ + struct v4l2_fwnode_endpoint ep = { .bus_type = V4L2_MBUS_PARALLEL }; + struct i2c_client *client = bridge->i2c_client; + struct device_node *ep_node; + int ret; + + /* parse tx (endpoint 2) */ + ep_node = of_graph_get_endpoint_by_regs(bridge->i2c_client->dev.of_node, + 2, 0); + if (!ep_node) { + dev_err(&client->dev, "unable to find port1 ep"); + ret = -EINVAL; + goto error; + } + + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), &ep); + if (ret) { + dev_err(&client->dev, "Could not parse v4l2 endpoint\n"); + goto error_of_node_put; + } + + of_node_put(ep_node); + bridge->tx = ep; + + return 0; + +error_of_node_put: + of_node_put(ep_node); +error: + + return -EINVAL; +} + +static int mipid02_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct mipid02_dev *bridge; + u32 clk_freq; + int ret; + + bridge = devm_kzalloc(dev, sizeof(*bridge), GFP_KERNEL); + if (!bridge) + return -ENOMEM; + + init_format(&bridge->fmt); + + bridge->i2c_client = client; + v4l2_i2c_subdev_init(&bridge->sd, client, &mipid02_subdev_ops); + + /* got and check clock */ + bridge->xclk = devm_clk_get(dev, "xclk"); + if (IS_ERR(bridge->xclk)) { + dev_err(dev, "failed to get xclk\n"); + return PTR_ERR(bridge->xclk); + } + + clk_freq = clk_get_rate(bridge->xclk); + if (clk_freq < 6000000 || clk_freq > 27000000) { + dev_err(dev, "xclk freq must be in 6-27 Mhz range. got %d Hz\n", + clk_freq); + return -EINVAL; + } + + bridge->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + + ret = mipid02_get_regulators(bridge); + if (ret) { + dev_err(dev, "failed to get regulators %d", ret); + return ret; + } + + mutex_init(&bridge->lock); + bridge->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + bridge->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + bridge->sd.entity.ops = &mipid02_subdev_entity_ops; + bridge->pad[0].flags = MEDIA_PAD_FL_SINK; + bridge->pad[1].flags = MEDIA_PAD_FL_SINK; + bridge->pad[2].flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&bridge->sd.entity, MIPID02_PAD_NB, + bridge->pad); + if (ret) { + dev_err(&client->dev, "pads init failed %d", ret); + goto mutex_cleanup; + } + + /* enable clock, power and reset device if available */ + ret = mipid02_set_power_on(bridge); + if (ret) + goto entity_cleanup; + + ret = mipid02_detect(bridge); + if (ret) { + dev_err(&client->dev, "failed to detect mipid02 %d", ret); + goto power_off; + } + + ret = mipid02_parse_tx_ep(bridge); + if (ret) { + dev_err(&client->dev, "failed to parse tx %d", ret); + goto power_off; + } + + ret = mipid02_parse_rx_ep(bridge); + if (ret) { + dev_err(&client->dev, "failed to parse rx %d", ret); + goto power_off; + } + + ret = v4l2_async_register_subdev(&bridge->sd); + if (ret < 0) { + dev_err(&client->dev, "v4l2_async_register_subdev failed %d", + ret); + goto unregister_notifier; + } + + dev_info(&client->dev, "mipid02 device probe successfully"); + + return 0; + +unregister_notifier: + v4l2_async_notifier_unregister(&bridge->notifier); + v4l2_async_notifier_cleanup(&bridge->notifier); +power_off: + mipid02_set_power_off(bridge); +entity_cleanup: + media_entity_cleanup(&bridge->sd.entity); +mutex_cleanup: + mutex_destroy(&bridge->lock); + + return ret; +} + +static int mipid02_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct mipid02_dev *bridge = to_mipid02_dev(sd); + + v4l2_async_notifier_unregister(&bridge->notifier); + v4l2_async_notifier_cleanup(&bridge->notifier); + v4l2_async_unregister_subdev(&bridge->sd); + mipid02_set_power_off(bridge); + media_entity_cleanup(&bridge->sd.entity); + mutex_destroy(&bridge->lock); + + return 0; +} + +static const struct of_device_id mipid02_dt_ids[] = { + { .compatible = "st,st-mipid02" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mipid02_dt_ids); + +static struct i2c_driver mipid02_i2c_driver = { + .driver = { + .name = "st-mipid02", + .of_match_table = mipid02_dt_ids, + }, + .probe_new = mipid02_probe, + .remove = mipid02_remove, +}; + +module_i2c_driver(mipid02_i2c_driver); + +MODULE_AUTHOR("Mickael Guene "); +MODULE_DESCRIPTION("STMicroelectronics MIPID02 CSI-2 bridge driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 54fe90a..6f0721e 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -111,7 +111,7 @@ config VIDEO_S3C_CAMIF config VIDEO_STM32_DCMI tristate "STM32 Digital Camera Memory Interface (DCMI) support" - depends on VIDEO_V4L2 && OF + depends on VIDEO_V4L2 && OF && MEDIA_CONTROLLER depends on ARCH_STM32 || COMPILE_TEST select VIDEOBUF2_DMA_CONTIG select V4L2_FWNODE diff --git a/drivers/media/platform/stm32/stm32-cec.c b/drivers/media/platform/stm32/stm32-cec.c index 7c496bc..1e657fe 100644 --- a/drivers/media/platform/stm32/stm32-cec.c +++ b/drivers/media/platform/stm32/stm32-cec.c @@ -11,7 +11,9 @@ #include #include #include +#include #include +#include #include #include @@ -56,6 +58,13 @@ #define ALL_TX_IT (TXEND | TXBR | TXACKE | TXERR | TXUDR | ARBLST) #define ALL_RX_IT (RXEND | RXBR | RXACKE | RXOVR) +/* + * 400 ms is the time it takes for one 16 byte message to be + * transferred and 5 is the maximum number of retries. Add + * another 100 ms as a margin. + */ +#define CEC_XFER_TIMEOUT_MS (5 * 400 + 100) + struct stm32_cec { struct cec_adapter *adap; struct device *dev; @@ -68,6 +77,9 @@ struct stm32_cec { struct cec_msg rx_msg; struct cec_msg tx_msg; int tx_cnt; + u32 c_reg; + u32 ie_reg; + u32 cfg_reg; }; static void cec_hw_init(struct stm32_cec *cec) @@ -174,6 +186,9 @@ static int stm32_cec_adap_enable(struct cec_adapter *adap, bool enable) dev_err(cec->dev, "fail to enable cec clock\n"); clk_enable(cec->clk_hdmi_cec); + + cec_hw_init(cec); + regmap_update_bits(cec->regmap, CEC_CR, CECEN, CECEN); } else { clk_disable(cec->clk_cec); @@ -188,7 +203,11 @@ static int stm32_cec_adap_log_addr(struct cec_adapter *adap, u8 logical_addr) { struct stm32_cec *cec = adap->priv; u32 oar = (1 << logical_addr) << 16; + u32 val; + /* Poll every 100µs the register CEC_CR to wait end of transmission */ + regmap_read_poll_timeout(cec->regmap, CEC_CR, val, !(val & TXSOM), + 100, CEC_XFER_TIMEOUT_MS * 1000); regmap_update_bits(cec->regmap, CEC_CR, CECEN, 0); if (logical_addr == CEC_LOG_ADDR_INVALID) @@ -260,8 +279,8 @@ static int stm32_cec_probe(struct platform_device *pdev) if (IS_ERR(mmio)) return PTR_ERR(mmio); - cec->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "cec", mmio, - &stm32_cec_regmap_cfg); + cec->regmap = devm_regmap_init_mmio(&pdev->dev, mmio, + &stm32_cec_regmap_cfg); if (IS_ERR(cec->regmap)) return PTR_ERR(cec->regmap); @@ -315,8 +334,6 @@ static int stm32_cec_probe(struct platform_device *pdev) return ret; } - cec_hw_init(cec); - platform_set_drvdata(pdev, cec); return 0; @@ -334,6 +351,76 @@ static int stm32_cec_remove(struct platform_device *pdev) return 0; } +static __maybe_unused int cec_runtime_suspend(struct device *dev) +{ + struct stm32_cec *cec = dev_get_drvdata(dev); + + clk_disable(cec->clk_cec); + clk_disable(cec->clk_hdmi_cec); + + return 0; +} + +static __maybe_unused int cec_runtime_resume(struct device *dev) +{ + struct stm32_cec *cec = dev_get_drvdata(dev); + int ret; + + ret = clk_enable(cec->clk_cec); + if (ret) { + dev_err(cec->dev, "fail to enable cec clock\n"); + return ret; + } + + ret = clk_enable(cec->clk_hdmi_cec); + if (ret) + dev_err(cec->dev, "fail to enable hdmi cec clock\n"); + + return ret; +} + +static __maybe_unused int cec_suspend(struct device *dev) +{ + struct stm32_cec *cec = dev_get_drvdata(dev); + + /* change pinctrl state */ + pinctrl_pm_select_sleep_state(dev); + + /* save resgisters settings to cec context */ + regmap_read(cec->regmap, CEC_CR, &cec->c_reg); + regmap_read(cec->regmap, CEC_IER, &cec->ie_reg); + regmap_read(cec->regmap, CEC_CFGR, &cec->cfg_reg); + + /* disable clock */ + pm_runtime_force_suspend(dev); + + return 0; +} + +static __maybe_unused int cec_resume(struct device *dev) +{ + struct stm32_cec *cec = dev_get_drvdata(dev); + + /* clock enable */ + pm_runtime_force_resume(dev); + + /* restore from cec context registers settings */ + regmap_write(cec->regmap, CEC_CFGR, cec->cfg_reg); + regmap_write(cec->regmap, CEC_IER, cec->ie_reg); + regmap_write(cec->regmap, CEC_CR, cec->c_reg); + + /* restore pinctl default state */ + pinctrl_pm_select_default_state(dev); + + return 0; +} + +static const struct dev_pm_ops cec_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(cec_suspend, cec_resume) + SET_RUNTIME_PM_OPS(cec_runtime_suspend, + cec_runtime_resume, NULL) +}; + static const struct of_device_id stm32_cec_of_match[] = { { .compatible = "st,stm32-cec" }, { /* end node */ } @@ -346,6 +433,7 @@ static struct platform_driver stm32_cec_driver = { .driver = { .name = CEC_NAME, .of_match_table = stm32_cec_of_match, + .pm = &cec_pm_ops, }, }; diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c index 18d0b56..0a59f44 100644 --- a/drivers/media/platform/stm32/stm32-dcmi.c +++ b/drivers/media/platform/stm32/stm32-dcmi.c @@ -95,13 +95,18 @@ enum state { #define MIN_HEIGHT 16U #define MAX_HEIGHT 2592U +/* DMA can sustain YUV 720p@15fps max */ +#define MAX_DMA_BANDWIDTH (1280 * 720 * 2 * 15) + #define TIMEOUT_MS 1000 -struct dcmi_graph_entity { - struct device_node *node; +#define OVERRUN_ERROR_THRESHOLD 3 +struct dcmi_graph_entity { struct v4l2_async_subdev asd; - struct v4l2_subdev *subdev; + + struct device_node *remote_node; + struct v4l2_subdev *source; }; struct dcmi_format { @@ -167,6 +172,10 @@ struct stm32_dcmi { /* Ensure DMA operations atomicity */ struct mutex dma_lock; + + struct media_device mdev; + struct media_pad vid_cap_pad; + struct media_pipeline pipeline; }; static inline struct stm32_dcmi *notifier_to_dcmi(struct v4l2_async_notifier *n) @@ -446,11 +455,13 @@ static irqreturn_t dcmi_irq_thread(int irq, void *arg) spin_lock_irq(&dcmi->irqlock); - if ((dcmi->misr & IT_OVR) || (dcmi->misr & IT_ERR)) { - dcmi->errors_count++; - if (dcmi->misr & IT_OVR) - dcmi->overrun_count++; + if (dcmi->misr & IT_OVR) { + dcmi->overrun_count++; + if (dcmi->overrun_count > OVERRUN_ERROR_THRESHOLD) + dcmi->errors_count++; } + if (dcmi->misr & IT_ERR) + dcmi->errors_count++; if (dcmi->sd_format->fourcc == V4L2_PIX_FMT_JPEG && dcmi->misr & IT_FRAME) { @@ -576,6 +587,144 @@ static void dcmi_buf_queue(struct vb2_buffer *vb) spin_unlock_irq(&dcmi->irqlock); } +static struct media_entity *dcmi_find_source(struct stm32_dcmi *dcmi) +{ + struct media_entity *entity = &dcmi->vdev->entity; + struct media_pad *pad; + + /* Walk searching for entity having no sink */ + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_entity_remote_pad(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + break; + + entity = pad->entity; + } + + return entity; +} + +static int dcmi_pipeline_s_fmt(struct stm32_dcmi *dcmi, + struct v4l2_subdev_pad_config *pad_cfg, + struct v4l2_subdev_format *format) +{ + struct media_entity *entity = &dcmi->entity.source->entity; + struct v4l2_subdev *subdev; + struct media_pad *sink_pad = NULL; + struct media_pad *src_pad = NULL; + struct media_pad *pad = NULL; + struct v4l2_subdev_format fmt = *format; + bool found = false; + int ret; + + /* + * Starting from sensor subdevice, walk within + * pipeline and set format on each subdevice + */ + while (1) { + unsigned int i; + + /* Search if current entity has a source pad */ + for (i = 0; i < entity->num_pads; i++) { + pad = &entity->pads[i]; + if (pad->flags & MEDIA_PAD_FL_SOURCE) { + src_pad = pad; + found = true; + break; + } + } + if (!found) + break; + + subdev = media_entity_to_v4l2_subdev(entity); + + /* Propagate format on sink pad if any, otherwise source pad */ + if (sink_pad) + pad = sink_pad; + + dev_dbg(dcmi->dev, "\"%s\":%d pad format set to 0x%x %ux%u\n", + subdev->name, pad->index, format->format.code, + format->format.width, format->format.height); + + fmt.pad = pad->index; + ret = v4l2_subdev_call(subdev, pad, set_fmt, pad_cfg, &fmt); + if (ret < 0) { + dev_err(dcmi->dev, "%s: Failed to set format 0x%x %ux%u on \"%s\":%d pad (%d)\n", + __func__, format->format.code, + format->format.width, format->format.height, + subdev->name, pad->index, ret); + return ret; + } + + if (fmt.format.code != format->format.code || + fmt.format.width != format->format.width || + fmt.format.height != format->format.height) { + dev_dbg(dcmi->dev, "\"%s\":%d pad format has been changed to 0x%x %ux%u\n", + subdev->name, pad->index, fmt.format.code, + fmt.format.width, fmt.format.height); + } + + /* Walk to next entity */ + sink_pad = media_entity_remote_pad(src_pad); + if (!sink_pad || !is_media_entity_v4l2_subdev(sink_pad->entity)) + break; + + entity = sink_pad->entity; + } + *format = fmt; + + return 0; +} + +static int dcmi_pipeline_s_stream(struct stm32_dcmi *dcmi, int state) +{ + struct media_entity *entity = &dcmi->vdev->entity; + struct v4l2_subdev *subdev; + struct media_pad *pad; + int ret; + + /* Start/stop all entities within pipeline */ + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_entity_remote_pad(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + ret = v4l2_subdev_call(subdev, video, s_stream, state); + if (ret < 0 && ret != -ENOIOCTLCMD) { + dev_err(dcmi->dev, "%s: \"%s\" failed to %s streaming (%d)\n", + __func__, subdev->name, + state ? "start" : "stop", ret); + return ret; + } + + dev_dbg(dcmi->dev, "\"%s\" is %s\n", + subdev->name, state ? "started" : "stopped"); + } + + return 0; +} + +static int dcmi_pipeline_start(struct stm32_dcmi *dcmi) +{ + return dcmi_pipeline_s_stream(dcmi, 1); +} + +static void dcmi_pipeline_stop(struct stm32_dcmi *dcmi) +{ + dcmi_pipeline_s_stream(dcmi, 0); +} + static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) { struct stm32_dcmi *dcmi = vb2_get_drv_priv(vq); @@ -590,14 +739,17 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) goto err_release_buffers; } - /* Enable stream on the sub device */ - ret = v4l2_subdev_call(dcmi->entity.subdev, video, s_stream, 1); - if (ret && ret != -ENOIOCTLCMD) { - dev_err(dcmi->dev, "%s: Failed to start streaming, subdev streamon error", - __func__); + ret = media_pipeline_start(&dcmi->vdev->entity, &dcmi->pipeline); + if (ret < 0) { + dev_err(dcmi->dev, "%s: Failed to start streaming, media pipeline start error (%d)\n", + __func__, ret); goto err_pm_put; } + ret = dcmi_pipeline_start(dcmi); + if (ret) + goto err_media_pipeline_stop; + spin_lock_irq(&dcmi->irqlock); /* Set bus width */ @@ -635,8 +787,31 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) dcmi_set_crop(dcmi); /* Enable jpeg capture */ - if (dcmi->sd_format->fourcc == V4L2_PIX_FMT_JPEG) - reg_set(dcmi->regs, DCMI_CR, CR_CM);/* Snapshot mode */ + if (dcmi->sd_format->fourcc == V4L2_PIX_FMT_JPEG) { + unsigned int rate; + struct v4l2_streamparm p = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE + }; + struct v4l2_fract frame_interval = {1, 30}; + + ret = v4l2_g_parm_cap(dcmi->vdev, dcmi->entity.source, &p); + if (!ret) + frame_interval = p.parm.capture.timeperframe; + + rate = dcmi->fmt.fmt.pix.sizeimage * + frame_interval.denominator / frame_interval.numerator; + + /* + * If rate exceed DMA capabilities, switch to snapshot mode + * to ensure that current DMA transfer is elapsed before + * capturing a new JPEG. + */ + if (rate > MAX_DMA_BANDWIDTH) { + reg_set(dcmi->regs, DCMI_CR, CR_CM);/* Snapshot mode */ + dev_dbg(dcmi->dev, "Capture rate is too high for continuous mode (%d > %d bytes/s), switch to snapshot mode\n", + rate, MAX_DMA_BANDWIDTH); + } + } /* Enable dcmi */ reg_set(dcmi->regs, DCMI_CR, CR_ENABLE); @@ -669,16 +844,22 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) if (ret) { dev_err(dcmi->dev, "%s: Start streaming failed, cannot start capture\n", __func__); - goto err_subdev_streamoff; + goto err_pipeline_stop; } /* Enable interruptions */ - reg_set(dcmi->regs, DCMI_IER, IT_FRAME | IT_OVR | IT_ERR); + if (dcmi->sd_format->fourcc == V4L2_PIX_FMT_JPEG) + reg_set(dcmi->regs, DCMI_IER, IT_FRAME | IT_OVR | IT_ERR); + else + reg_set(dcmi->regs, DCMI_IER, IT_OVR | IT_ERR); return 0; -err_subdev_streamoff: - v4l2_subdev_call(dcmi->entity.subdev, video, s_stream, 0); +err_pipeline_stop: + dcmi_pipeline_stop(dcmi); + +err_media_pipeline_stop: + media_pipeline_stop(&dcmi->vdev->entity); err_pm_put: pm_runtime_put(dcmi->dev); @@ -703,13 +884,10 @@ static void dcmi_stop_streaming(struct vb2_queue *vq) { struct stm32_dcmi *dcmi = vb2_get_drv_priv(vq); struct dcmi_buf *buf, *node; - int ret; - /* Disable stream on the sub device */ - ret = v4l2_subdev_call(dcmi->entity.subdev, video, s_stream, 0); - if (ret && ret != -ENOIOCTLCMD) - dev_err(dcmi->dev, "%s: Failed to stop streaming, subdev streamoff error (%d)\n", - __func__, ret); + dcmi_pipeline_stop(dcmi); + + media_pipeline_stop(&dcmi->vdev->entity); spin_lock_irq(&dcmi->irqlock); @@ -850,7 +1028,7 @@ static int dcmi_try_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f, } v4l2_fill_mbus_format(&format.format, pix, sd_fmt->mbus_code); - ret = v4l2_subdev_call(dcmi->entity.subdev, pad, set_fmt, + ret = v4l2_subdev_call(dcmi->entity.source, pad, set_fmt, &pad_cfg, &format); if (ret < 0) return ret; @@ -927,8 +1105,7 @@ static int dcmi_set_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f) mf->width = sd_framesize.width; mf->height = sd_framesize.height; - ret = v4l2_subdev_call(dcmi->entity.subdev, pad, - set_fmt, NULL, &format); + ret = dcmi_pipeline_s_fmt(dcmi, NULL, &format); if (ret < 0) return ret; @@ -984,7 +1161,7 @@ static int dcmi_get_sensor_format(struct stm32_dcmi *dcmi, }; int ret; - ret = v4l2_subdev_call(dcmi->entity.subdev, pad, get_fmt, NULL, &fmt); + ret = v4l2_subdev_call(dcmi->entity.source, pad, get_fmt, NULL, &fmt); if (ret) return ret; @@ -1013,7 +1190,7 @@ static int dcmi_set_sensor_format(struct stm32_dcmi *dcmi, } v4l2_fill_mbus_format(&format.format, pix, sd_fmt->mbus_code); - ret = v4l2_subdev_call(dcmi->entity.subdev, pad, set_fmt, + ret = v4l2_subdev_call(dcmi->entity.source, pad, set_fmt, &pad_cfg, &format); if (ret < 0) return ret; @@ -1036,7 +1213,7 @@ static int dcmi_get_sensor_bounds(struct stm32_dcmi *dcmi, /* * Get sensor bounds first */ - ret = v4l2_subdev_call(dcmi->entity.subdev, pad, get_selection, + ret = v4l2_subdev_call(dcmi->entity.source, pad, get_selection, NULL, &bounds); if (!ret) *r = bounds.r; @@ -1217,7 +1394,7 @@ static int dcmi_enum_framesizes(struct file *file, void *fh, fse.code = sd_fmt->mbus_code; - ret = v4l2_subdev_call(dcmi->entity.subdev, pad, enum_frame_size, + ret = v4l2_subdev_call(dcmi->entity.source, pad, enum_frame_size, NULL, &fse); if (ret) return ret; @@ -1234,7 +1411,7 @@ static int dcmi_g_parm(struct file *file, void *priv, { struct stm32_dcmi *dcmi = video_drvdata(file); - return v4l2_g_parm_cap(video_devdata(file), dcmi->entity.subdev, p); + return v4l2_g_parm_cap(video_devdata(file), dcmi->entity.source, p); } static int dcmi_s_parm(struct file *file, void *priv, @@ -1242,7 +1419,7 @@ static int dcmi_s_parm(struct file *file, void *priv, { struct stm32_dcmi *dcmi = video_drvdata(file); - return v4l2_s_parm_cap(video_devdata(file), dcmi->entity.subdev, p); + return v4l2_s_parm_cap(video_devdata(file), dcmi->entity.source, p); } static int dcmi_enum_frameintervals(struct file *file, void *fh, @@ -1264,7 +1441,7 @@ static int dcmi_enum_frameintervals(struct file *file, void *fh, fie.code = sd_fmt->mbus_code; - ret = v4l2_subdev_call(dcmi->entity.subdev, pad, + ret = v4l2_subdev_call(dcmi->entity.source, pad, enum_frame_interval, NULL, &fie); if (ret) return ret; @@ -1284,7 +1461,7 @@ MODULE_DEVICE_TABLE(of, stm32_dcmi_of_match); static int dcmi_open(struct file *file) { struct stm32_dcmi *dcmi = video_drvdata(file); - struct v4l2_subdev *sd = dcmi->entity.subdev; + struct v4l2_subdev *sd = dcmi->entity.source; int ret; if (mutex_lock_interruptible(&dcmi->lock)) @@ -1315,7 +1492,7 @@ static int dcmi_open(struct file *file) static int dcmi_release(struct file *file) { struct stm32_dcmi *dcmi = video_drvdata(file); - struct v4l2_subdev *sd = dcmi->entity.subdev; + struct v4l2_subdev *sd = dcmi->entity.source; bool fh_singular; int ret; @@ -1402,6 +1579,12 @@ static int dcmi_set_default_fmt(struct stm32_dcmi *dcmi) return 0; } +/* + * FIXME: For the time being we only support subdevices + * which expose RGB & YUV "parallel form" mbus code (_2X8). + * Nevertheless, this allows to support serial source subdevices + * and serial to parallel bridges which conform to this. + */ static const struct dcmi_format dcmi_formats[] = { { .fourcc = V4L2_PIX_FMT_RGB565, @@ -1426,7 +1609,7 @@ static int dcmi_formats_init(struct stm32_dcmi *dcmi) { const struct dcmi_format *sd_fmts[ARRAY_SIZE(dcmi_formats)]; unsigned int num_fmts = 0, i, j; - struct v4l2_subdev *subdev = dcmi->entity.subdev; + struct v4l2_subdev *subdev = dcmi->entity.source; struct v4l2_subdev_mbus_code_enum mbus_code = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, }; @@ -1440,12 +1623,20 @@ static int dcmi_formats_init(struct stm32_dcmi *dcmi) /* Code supported, have we got this fourcc yet? */ for (j = 0; j < num_fmts; j++) if (sd_fmts[j]->fourcc == - dcmi_formats[i].fourcc) + dcmi_formats[i].fourcc) { /* Already available */ + dev_dbg(dcmi->dev, "Skipping fourcc/code: %4.4s/0x%x\n", + (char *)&sd_fmts[j]->fourcc, + mbus_code.code); break; - if (j == num_fmts) + } + if (j == num_fmts) { /* New */ sd_fmts[num_fmts++] = dcmi_formats + i; + dev_dbg(dcmi->dev, "Supported fourcc/code: %4.4s/0x%x\n", + (char *)&sd_fmts[num_fmts - 1]->fourcc, + sd_fmts[num_fmts - 1]->mbus_code); + } } mbus_code.index++; } @@ -1472,7 +1663,7 @@ static int dcmi_formats_init(struct stm32_dcmi *dcmi) static int dcmi_framesizes_init(struct stm32_dcmi *dcmi) { unsigned int num_fsize = 0; - struct v4l2_subdev *subdev = dcmi->entity.subdev; + struct v4l2_subdev *subdev = dcmi->entity.source; struct v4l2_subdev_frame_size_enum fse = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, .code = dcmi->sd_format->mbus_code, @@ -1519,7 +1710,20 @@ static int dcmi_graph_notify_complete(struct v4l2_async_notifier *notifier) struct stm32_dcmi *dcmi = notifier_to_dcmi(notifier); int ret; - dcmi->vdev->ctrl_handler = dcmi->entity.subdev->ctrl_handler; + /* + * Now that the graph is complete, + * we search for the source subdevice + * in order to expose it through V4L2 interface + */ + dcmi->entity.source = + media_entity_to_v4l2_subdev(dcmi_find_source(dcmi)); + if (!dcmi->entity.source) { + dev_err(dcmi->dev, "Source subdevice not found\n"); + return -ENODEV; + } + + dcmi->vdev->ctrl_handler = dcmi->entity.source->ctrl_handler; + ret = dcmi_formats_init(dcmi); if (ret) { dev_err(dcmi->dev, "No supported mediabus format found\n"); @@ -1544,14 +1748,6 @@ static int dcmi_graph_notify_complete(struct v4l2_async_notifier *notifier) return ret; } - ret = video_register_device(dcmi->vdev, VFL_TYPE_GRABBER, -1); - if (ret) { - dev_err(dcmi->dev, "Failed to register video device\n"); - return ret; - } - - dev_dbg(dcmi->dev, "Device registered as %s\n", - video_device_node_name(dcmi->vdev)); return 0; } @@ -1572,12 +1768,31 @@ static int dcmi_graph_notify_bound(struct v4l2_async_notifier *notifier, struct v4l2_async_subdev *asd) { struct stm32_dcmi *dcmi = notifier_to_dcmi(notifier); + unsigned int ret; + int src_pad; - dev_dbg(dcmi->dev, "Subdev %s bound\n", subdev->name); + dev_dbg(dcmi->dev, "Subdev \"%s\" bound\n", subdev->name); - dcmi->entity.subdev = subdev; + /* + * Link this sub-device to DCMI, it could be + * a parallel camera sensor or a bridge + */ + src_pad = media_entity_get_fwnode_pad(&subdev->entity, + subdev->fwnode, + MEDIA_PAD_FL_SOURCE); + + ret = media_create_pad_link(&subdev->entity, src_pad, + &dcmi->vdev->entity, 0, + MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + if (ret) + dev_err(dcmi->dev, "Failed to create media pad link with subdev \"%s\"\n", + subdev->name); + else + dev_dbg(dcmi->dev, "DCMI is now linked to \"%s\"\n", + subdev->name); - return 0; + return ret; } static const struct v4l2_async_notifier_operations dcmi_graph_notify_ops = { @@ -1601,7 +1816,7 @@ static int dcmi_graph_parse(struct stm32_dcmi *dcmi, struct device_node *node) return -EINVAL; /* Remote node to connect */ - dcmi->entity.node = remote; + dcmi->entity.remote_node = remote; dcmi->entity.asd.match_type = V4L2_ASYNC_MATCH_FWNODE; dcmi->entity.asd.match.fwnode = of_fwnode_handle(remote); return 0; @@ -1622,7 +1837,7 @@ static int dcmi_graph_init(struct stm32_dcmi *dcmi) /* Register the subdevices notifier. */ subdevs = devm_kzalloc(dcmi->dev, sizeof(*subdevs), GFP_KERNEL); if (!subdevs) { - of_node_put(dcmi->entity.node); + of_node_put(dcmi->entity.remote_node); return -ENOMEM; } @@ -1635,7 +1850,7 @@ static int dcmi_graph_init(struct stm32_dcmi *dcmi) ret = v4l2_async_notifier_register(&dcmi->v4l2_dev, &dcmi->notifier); if (ret < 0) { dev_err(dcmi->dev, "Notifier registration failed\n"); - of_node_put(dcmi->entity.node); + of_node_put(dcmi->entity.remote_node); return ret; } @@ -1746,10 +1961,19 @@ static int dcmi_probe(struct platform_device *pdev) q = &dcmi->queue; + dcmi->v4l2_dev.mdev = &dcmi->mdev; + + /* Initialize media device */ + strscpy(dcmi->mdev.model, DRV_NAME, sizeof(dcmi->mdev.model)); + snprintf(dcmi->mdev.bus_info, sizeof(dcmi->mdev.bus_info), + "platform:%s", DRV_NAME); + dcmi->mdev.dev = &pdev->dev; + media_device_init(&dcmi->mdev); + /* Initialize the top-level structure */ ret = v4l2_device_register(&pdev->dev, &dcmi->v4l2_dev); if (ret) - goto err_dma_release; + goto err_media_device_cleanup; dcmi->vdev = video_device_alloc(); if (!dcmi->vdev) { @@ -1769,6 +1993,25 @@ static int dcmi_probe(struct platform_device *pdev) V4L2_CAP_READWRITE; video_set_drvdata(dcmi->vdev, dcmi); + /* Media entity pads */ + dcmi->vid_cap_pad.flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&dcmi->vdev->entity, + 1, &dcmi->vid_cap_pad); + if (ret) { + dev_err(dcmi->dev, "Failed to init media entity pad\n"); + goto err_device_release; + } + dcmi->vdev->entity.flags |= MEDIA_ENT_FL_DEFAULT; + + ret = video_register_device(dcmi->vdev, VFL_TYPE_GRABBER, -1); + if (ret) { + dev_err(dcmi->dev, "Failed to register video device\n"); + goto err_media_entity_cleanup; + } + + dev_dbg(dcmi->dev, "Device registered as %s\n", + video_device_node_name(dcmi->vdev)); + /* Buffer queue */ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; q->io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF; @@ -1784,12 +2027,12 @@ static int dcmi_probe(struct platform_device *pdev) ret = vb2_queue_init(q); if (ret < 0) { dev_err(&pdev->dev, "Failed to initialize vb2 queue\n"); - goto err_device_release; + goto err_media_entity_cleanup; } ret = dcmi_graph_init(dcmi); if (ret < 0) - goto err_device_release; + goto err_media_entity_cleanup; /* Reset device */ ret = reset_control_assert(dcmi->rstc); @@ -1814,11 +2057,14 @@ static int dcmi_probe(struct platform_device *pdev) return 0; +err_media_entity_cleanup: + media_entity_cleanup(&dcmi->vdev->entity); err_device_release: video_device_release(dcmi->vdev); err_device_unregister: v4l2_device_unregister(&dcmi->v4l2_dev); -err_dma_release: +err_media_device_cleanup: + media_device_cleanup(&dcmi->mdev); dma_release_channel(dcmi->dma_chan); return ret; @@ -1831,7 +2077,9 @@ static int dcmi_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); v4l2_async_notifier_unregister(&dcmi->notifier); + media_entity_cleanup(&dcmi->vdev->entity); v4l2_device_unregister(&dcmi->v4l2_dev); + media_device_cleanup(&dcmi->mdev); dma_release_channel(dcmi->dma_chan); diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c index fecccb5..89a7839 100644 --- a/drivers/media/usb/uvc/uvc_queue.c +++ b/drivers/media/usb/uvc/uvc_queue.c @@ -224,7 +224,7 @@ int uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type, int ret; queue->queue.type = type; - queue->queue.io_modes = VB2_MMAP | VB2_USERPTR; + queue->queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; queue->queue.drv_priv = queue; queue->queue.buf_struct_size = sizeof(struct uvc_buffer); queue->queue.mem_ops = &vb2_vmalloc_memops; @@ -357,6 +357,19 @@ int uvc_queue_streamoff(struct uvc_video_queue *queue, enum v4l2_buf_type type) return ret; } +ssize_t uvc_queue_read(struct uvc_video_queue *queue, struct file *file, + char __user *buf, size_t count, loff_t *ppos) +{ + ssize_t ret; + + mutex_lock(&queue->mutex); + ret = vb2_read(&queue->queue, buf, count, ppos, + file->f_flags & O_NONBLOCK); + mutex_unlock(&queue->mutex); + + return ret; +} + int uvc_queue_mmap(struct uvc_video_queue *queue, struct vm_area_struct *vma) { return vb2_mmap(&queue->queue, vma); diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c index 18a7384..242d886 100644 --- a/drivers/media/usb/uvc/uvc_v4l2.c +++ b/drivers/media/usb/uvc/uvc_v4l2.c @@ -594,7 +594,8 @@ static int uvc_ioctl_querycap(struct file *file, void *fh, strlcpy(cap->driver, "uvcvideo", sizeof(cap->driver)); strlcpy(cap->card, vdev->name, sizeof(cap->card)); usb_make_path(stream->dev->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING + cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE | chain->caps; return 0; @@ -1433,8 +1434,12 @@ static long uvc_v4l2_compat_ioctl32(struct file *file, static ssize_t uvc_v4l2_read(struct file *file, char __user *data, size_t count, loff_t *ppos) { - uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_read: not implemented.\n"); - return -EINVAL; + struct uvc_fh *handle = file->private_data; + struct uvc_streaming *stream = handle->stream; + + uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_read\n"); + + return uvc_queue_read(&stream->queue, file, data, count, ppos); } static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma) diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index a738486..a7e843a 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -704,6 +704,8 @@ int uvc_queue_streamoff(struct uvc_video_queue *queue, enum v4l2_buf_type type); void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect); struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue, struct uvc_buffer *buf); +ssize_t uvc_queue_read(struct uvc_video_queue *queue, struct file *file, + char __user *buf, size_t count, loff_t *ppos); int uvc_queue_mmap(struct uvc_video_queue *queue, struct vm_area_struct *vma); __poll_t uvc_queue_poll(struct uvc_video_queue *queue, struct file *file, diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index 169bdbb..505338e 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -158,6 +158,9 @@ static void v4l2_fwnode_endpoint_parse_parallel_bus( flags |= v ? V4L2_MBUS_DATA_ENABLE_HIGH : V4L2_MBUS_DATA_ENABLE_LOW; + if (!fwnode_property_read_u32(fwnode, "pclk-max-frequency", &v)) + bus->pclk_max_frequency = v; + bus->flags = flags; } diff --git a/include/uapi/linux/media-bus-format.h b/include/uapi/linux/media-bus-format.h index d6a5a3b..2a6b253 100644 --- a/include/uapi/linux/media-bus-format.h +++ b/include/uapi/linux/media-bus-format.h @@ -34,7 +34,7 @@ #define MEDIA_BUS_FMT_FIXED 0x0001 -/* RGB - next is 0x101b */ +/* RGB - next is 0x101c */ #define MEDIA_BUS_FMT_RGB444_1X12 0x1016 #define MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE 0x1001 #define MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE 0x1002 @@ -50,6 +50,7 @@ #define MEDIA_BUS_FMT_RGB666_1X24_CPADHI 0x1015 #define MEDIA_BUS_FMT_RGB666_1X7X3_SPWG 0x1010 #define MEDIA_BUS_FMT_BGR888_1X24 0x1013 +#define MEDIA_BUS_FMT_BGR888_3X8 0x101b #define MEDIA_BUS_FMT_GBR888_1X24 0x1014 #define MEDIA_BUS_FMT_RGB888_1X24 0x100a #define MEDIA_BUS_FMT_RGB888_2X12_BE 0x100b -- 2.7.4