From f483aadf60b247e94f94f5b2d494aa22ff177a55 Mon Sep 17 00:00:00 2001 From: Phi Bang Nguyen Date: Thu, 29 Feb 2024 16:34:39 +0100 Subject: [PATCH 1/3] dts: bindings: Add bindings for ov5640 camera Add bindings for ov5640 camera sensor Signed-off-by: Phi Bang Nguyen --- dts/bindings/video/ovti,ov5640.yaml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 dts/bindings/video/ovti,ov5640.yaml diff --git a/dts/bindings/video/ovti,ov5640.yaml b/dts/bindings/video/ovti,ov5640.yaml new file mode 100644 index 0000000000000..eecb0e3d3192b --- /dev/null +++ b/dts/bindings/video/ovti,ov5640.yaml @@ -0,0 +1,20 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: OV5640 CMOS video sensor + +compatible: "ovti,ov5640" + +include: i2c-device.yaml + +properties: + reset-gpios: + type: phandle-array + description: | + The RESETB pin is asserted to cause a hard reset. The sensor + receives this as an active-low signal. + powerdown-gpios: + type: phandle-array + description: | + The PWDN pin is asserted to disable the sensor. The sensor + receives this as an active-high signal. From ccb7b019cba41290b6fa0473d0e4262fb7ff7b89 Mon Sep 17 00:00:00 2001 From: Phi Bang Nguyen Date: Thu, 29 Feb 2024 16:33:55 +0100 Subject: [PATCH 2/3] drivers: video: Add ov5640 camera driver Add driver to support ov5640 camera sensor Signed-off-by: Phi Bang Nguyen --- drivers/video/CMakeLists.txt | 1 + drivers/video/Kconfig | 2 + drivers/video/Kconfig.ov5640 | 12 + drivers/video/ov5640.c | 698 +++++++++++++++++++++++++++++++++++ 4 files changed, 713 insertions(+) create mode 100644 drivers/video/Kconfig.ov5640 create mode 100644 drivers/video/ov5640.c diff --git a/drivers/video/CMakeLists.txt b/drivers/video/CMakeLists.txt index 0b279de38134f..b93c5bb2f66c6 100644 --- a/drivers/video/CMakeLists.txt +++ b/drivers/video/CMakeLists.txt @@ -10,3 +10,4 @@ zephyr_library_sources_ifdef(CONFIG_VIDEO_MT9M114 mt9m114.c) zephyr_library_sources_ifdef(CONFIG_VIDEO_OV7725 ov7725.c) zephyr_library_sources_ifdef(CONFIG_VIDEO_OV2640 ov2640.c) zephyr_library_sources_ifdef(CONFIG_VIDEO_STM32_DCMI video_stm32_dcmi.c) +zephyr_library_sources_ifdef(CONFIG_VIDEO_OV5640 ov5640.c) diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 8a489cc4be6bd..9f44944150fc9 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -43,4 +43,6 @@ source "drivers/video/Kconfig.ov2640" source "drivers/video/Kconfig.stm32_dcmi" +source "drivers/video/Kconfig.ov5640" + endif # VIDEO diff --git a/drivers/video/Kconfig.ov5640 b/drivers/video/Kconfig.ov5640 new file mode 100644 index 0000000000000..950079dd35507 --- /dev/null +++ b/drivers/video/Kconfig.ov5640 @@ -0,0 +1,12 @@ +# OV5640 + +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +config VIDEO_OV5640 + bool "OV5640 CMOS digital image sensor" + select I2C + depends on DT_HAS_OVTI_OV5640_ENABLED + default y + help + Enable driver for OV5640 CMOS digital image sensor device diff --git a/drivers/video/ov5640.c b/drivers/video/ov5640.c new file mode 100644 index 0000000000000..7c76c7b88c93c --- /dev/null +++ b/drivers/video/ov5640.c @@ -0,0 +1,698 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT ovti_ov5640 + +#include +#include +#include +#include +#include + +#define LOG_LEVEL CONFIG_LOG_DEFAULT_LEVEL +#include +LOG_MODULE_REGISTER(ov5640); + +#include + +#define CHIP_ID_REG 0x300a +#define CHIP_ID_VAL 0x5640 + +#define SYS_CTRL0_REG 0x3008 +#define SYS_CTRL0_SW_PWDN 0x42 +#define SYS_CTRL0_SW_PWUP 0x02 +#define SYS_CTRL0_SW_RST 0x82 + +#define SYS_RESET00_REG 0x3000 +#define SYS_RESET02_REG 0x3002 +#define SYS_CLK_ENABLE00_REG 0x3004 +#define SYS_CLK_ENABLE02_REG 0x3006 +#define IO_MIPI_CTRL00_REG 0x300e +#define SYSTEM_CONTROL1_REG 0x302e +#define SCCB_SYS_CTRL1_REG 0x3103 +#define TIMING_TC_REG20_REG 0x3820 +#define TIMING_TC_REG21_REG 0x3821 +#define HZ5060_CTRL01_REG 0x3c01 +#define ISP_CTRL01_REG 0x5001 + +#define SC_PLL_CTRL0_REG 0x3034 +#define SC_PLL_CTRL1_REG 0x3035 +#define SC_PLL_CTRL2_REG 0x3036 +#define SC_PLL_CTRL3_REG 0x3037 +#define SYS_ROOT_DIV_REG 0x3108 +#define PCLK_PERIOD_REG 0x4837 + +#define AEC_CTRL00_REG 0x3a00 +#define AEC_CTRL0F_REG 0x3a0f +#define AEC_CTRL10_REG 0x3a10 +#define AEC_CTRL11_REG 0x3a11 +#define AEC_CTRL1B_REG 0x3a1b +#define AEC_CTRL1E_REG 0x3a1e +#define AEC_CTRL1F_REG 0x3a1f + +#define BLC_CTRL01_REG 0x4001 +#define BLC_CTRL04_REG 0x4004 +#define BLC_CTRL05_REG 0x4005 + +#define AWB_CTRL00_REG 0x5180 +#define AWB_CTRL01_REG 0x5181 +#define AWB_CTRL02_REG 0x5182 +#define AWB_CTRL03_REG 0x5183 +#define AWB_CTRL04_REG 0x5184 +#define AWB_CTRL05_REG 0x5185 +#define AWB_CTRL17_REG 0x5191 +#define AWB_CTRL18_REG 0x5192 +#define AWB_CTRL19_REG 0x5193 +#define AWB_CTRL20_REG 0x5194 +#define AWB_CTRL21_REG 0x5195 +#define AWB_CTRL22_REG 0x5196 +#define AWB_CTRL23_REG 0x5197 +#define AWB_CTRL30_REG 0x519e + +#define SDE_CTRL0_REG 0x5580 +#define SDE_CTRL3_REG 0x5583 +#define SDE_CTRL4_REG 0x5584 +#define SDE_CTRL9_REG 0x5589 +#define SDE_CTRL10_REG 0x558a +#define SDE_CTRL11_REG 0x558b + +#define DEFAULT_MIPI_CHANNEL 0 + +#define OV5640_RESOLUTION_PARAM_NUM 24 + +struct ov5640_config { + struct i2c_dt_spec i2c; + struct gpio_dt_spec reset_gpio; + struct gpio_dt_spec powerdown_gpio; +}; + +struct ov5640_data { + struct video_format fmt; +}; + +struct ov5640_reg { + uint16_t addr; + uint8_t val; +}; + +struct ov5640_mipi_clock_config { + uint8_t pllCtrl1; + uint8_t pllCtrl2; +}; + +struct ov5640_resolution_config { + uint16_t width; + uint16_t height; + const struct ov5640_reg *res_params; + const struct ov5640_mipi_clock_config mipi_pclk; +}; + +static const struct ov5640_reg ov5640InitParams[] = { + /* Power down */ + {SYS_CTRL0_REG, SYS_CTRL0_SW_PWDN}, + + /* System setting. */ + {SCCB_SYS_CTRL1_REG, 0x13}, + {SCCB_SYS_CTRL1_REG, 0x03}, + {SYS_RESET00_REG, 0x00}, + {SYS_CLK_ENABLE00_REG, 0xff}, + {SYS_RESET02_REG, 0x1c}, + {SYS_CLK_ENABLE02_REG, 0xc3}, + {SYSTEM_CONTROL1_REG, 0x08}, + {0x3618, 0x00}, + {0x3612, 0x29}, + {0x3708, 0x64}, + {0x3709, 0x52}, + {0x370c, 0x03}, + {TIMING_TC_REG20_REG, 0x41}, + {TIMING_TC_REG21_REG, 0x07}, + {0x3630, 0x36}, + {0x3631, 0x0e}, + {0x3632, 0xe2}, + {0x3633, 0x12}, + {0x3621, 0xe0}, + {0x3704, 0xa0}, + {0x3703, 0x5a}, + {0x3715, 0x78}, + {0x3717, 0x01}, + {0x370b, 0x60}, + {0x3705, 0x1a}, + {0x3905, 0x02}, + {0x3906, 0x10}, + {0x3901, 0x0a}, + {0x3731, 0x12}, + {0x3600, 0x08}, + {0x3601, 0x33}, + {0x302d, 0x60}, + {0x3620, 0x52}, + {0x371b, 0x20}, + {0x471c, 0x50}, + {0x3a13, 0x43}, + {0x3a18, 0x00}, + {0x3a19, 0x7c}, + {0x3635, 0x13}, + {0x3636, 0x03}, + {0x3634, 0x40}, + {0x3622, 0x01}, + {HZ5060_CTRL01_REG, 0x00}, + {AEC_CTRL00_REG, 0x58}, + {BLC_CTRL01_REG, 0x02}, + {BLC_CTRL04_REG, 0x02}, + {BLC_CTRL05_REG, 0x1a}, + {ISP_CTRL01_REG, 0xa3}, + + /* AEC */ + {AEC_CTRL0F_REG, 0x30}, + {AEC_CTRL10_REG, 0x28}, + {AEC_CTRL1B_REG, 0x30}, + {AEC_CTRL1E_REG, 0x26}, + {AEC_CTRL11_REG, 0x60}, + {AEC_CTRL1F_REG, 0x14}, + + /* AWB */ + {AWB_CTRL00_REG, 0xff}, + {AWB_CTRL01_REG, 0xf2}, + {AWB_CTRL02_REG, 0x00}, + {AWB_CTRL03_REG, 0x14}, + {AWB_CTRL04_REG, 0x25}, + {AWB_CTRL05_REG, 0x24}, + {0x5186, 0x09}, + {0x5187, 0x09}, + {0x5188, 0x09}, + {0x5189, 0x88}, + {0x518a, 0x54}, + {0x518b, 0xee}, + {0x518c, 0xb2}, + {0x518d, 0x50}, + {0x518e, 0x34}, + {0x518f, 0x6b}, + {0x5190, 0x46}, + {AWB_CTRL17_REG, 0xf8}, + {AWB_CTRL18_REG, 0x04}, + {AWB_CTRL19_REG, 0x70}, + {AWB_CTRL20_REG, 0xf0}, + {AWB_CTRL21_REG, 0xf0}, + {AWB_CTRL22_REG, 0x03}, + {AWB_CTRL23_REG, 0x01}, + {0x5198, 0x04}, + {0x5199, 0x6c}, + {0x519a, 0x04}, + {0x519b, 0x00}, + {0x519c, 0x09}, + {0x519d, 0x2b}, + {AWB_CTRL30_REG, 0x38}, + + /* Color Matrix */ + {0x5381, 0x1e}, + {0x5382, 0x5b}, + {0x5383, 0x08}, + {0x5384, 0x0a}, + {0x5385, 0x7e}, + {0x5386, 0x88}, + {0x5387, 0x7c}, + {0x5388, 0x6c}, + {0x5389, 0x10}, + {0x538a, 0x01}, + {0x538b, 0x98}, + + /* Sharp */ + {0x5300, 0x08}, + {0x5301, 0x30}, + {0x5302, 0x10}, + {0x5303, 0x00}, + {0x5304, 0x08}, + {0x5305, 0x30}, + {0x5306, 0x08}, + {0x5307, 0x16}, + {0x5309, 0x08}, + {0x530a, 0x30}, + {0x530b, 0x04}, + {0x530c, 0x06}, + + /* Gamma */ + {0x5480, 0x01}, + {0x5481, 0x08}, + {0x5482, 0x14}, + {0x5483, 0x28}, + {0x5484, 0x51}, + {0x5485, 0x65}, + {0x5486, 0x71}, + {0x5487, 0x7d}, + {0x5488, 0x87}, + {0x5489, 0x91}, + {0x548a, 0x9a}, + {0x548b, 0xaa}, + {0x548c, 0xb8}, + {0x548d, 0xcd}, + {0x548e, 0xdd}, + {0x548f, 0xea}, + {0x5490, 0x1d}, + + /* UV adjust. */ + {SDE_CTRL0_REG, 0x02}, + {SDE_CTRL3_REG, 0x40}, + {SDE_CTRL4_REG, 0x10}, + {SDE_CTRL9_REG, 0x10}, + {SDE_CTRL10_REG, 0x00}, + {SDE_CTRL11_REG, 0xf8}, + + /* Lens correction. */ + {0x5800, 0x23}, + {0x5801, 0x14}, + {0x5802, 0x0f}, + {0x5803, 0x0f}, + {0x5804, 0x12}, + {0x5805, 0x26}, + {0x5806, 0x0c}, + {0x5807, 0x08}, + {0x5808, 0x05}, + {0x5809, 0x05}, + {0x580a, 0x08}, + {0x580b, 0x0d}, + {0x580c, 0x08}, + {0x580d, 0x03}, + {0x580e, 0x00}, + {0x580f, 0x00}, + {0x5810, 0x03}, + {0x5811, 0x09}, + {0x5812, 0x07}, + {0x5813, 0x03}, + {0x5814, 0x00}, + {0x5815, 0x01}, + {0x5816, 0x03}, + {0x5817, 0x08}, + {0x5818, 0x0d}, + {0x5819, 0x08}, + {0x581a, 0x05}, + {0x581b, 0x06}, + {0x581c, 0x08}, + {0x581d, 0x0e}, + {0x581e, 0x29}, + {0x581f, 0x17}, + {0x5820, 0x11}, + {0x5821, 0x11}, + {0x5822, 0x15}, + {0x5823, 0x28}, + {0x5824, 0x46}, + {0x5825, 0x26}, + {0x5826, 0x08}, + {0x5827, 0x26}, + {0x5828, 0x64}, + {0x5829, 0x26}, + {0x582a, 0x24}, + {0x582b, 0x22}, + {0x582c, 0x24}, + {0x582d, 0x24}, + {0x582e, 0x06}, + {0x582f, 0x22}, + {0x5830, 0x40}, + {0x5831, 0x42}, + {0x5832, 0x24}, + {0x5833, 0x26}, + {0x5834, 0x24}, + {0x5835, 0x22}, + {0x5836, 0x22}, + {0x5837, 0x26}, + {0x5838, 0x44}, + {0x5839, 0x24}, + {0x583a, 0x26}, + {0x583b, 0x28}, + {0x583c, 0x42}, + {0x583d, 0xce}, + {0x5000, 0xa7}, +}; + +static const struct ov5640_reg ov5640_low_res_params[] = { + {0x3800, 0x00}, {0x3801, 0x00}, {0x3802, 0x00}, {0x3803, 0x04}, {0x3804, 0x0a}, + {0x3805, 0x3f}, {0x3806, 0x07}, {0x3807, 0x9b}, {0x3808, 0x02}, {0x3809, 0x80}, + {0x380a, 0x01}, {0x380b, 0xe0}, {0x380c, 0x07}, {0x380d, 0x68}, {0x380e, 0x03}, + {0x380f, 0xd8}, {0x3810, 0x00}, {0x3811, 0x10}, {0x3812, 0x00}, {0x3813, 0x06}, + {0x3814, 0x31}, {0x3815, 0x31}, {0x3824, 0x02}, {0x460c, 0x22}}; + +static const struct ov5640_reg ov5640_720p_res_params[] = { + {0x3800, 0x00}, {0x3801, 0x00}, {0x3802, 0x00}, {0x3803, 0xfa}, {0x3804, 0x0a}, + {0x3805, 0x3f}, {0x3806, 0x06}, {0x3807, 0xa9}, {0x3808, 0x05}, {0x3809, 0x00}, + {0x380a, 0x02}, {0x380b, 0xd0}, {0x380c, 0x07}, {0x380d, 0x64}, {0x380e, 0x02}, + {0x380f, 0xe4}, {0x3810, 0x00}, {0x3811, 0x10}, {0x3812, 0x00}, {0x3813, 0x04}, + {0x3814, 0x31}, {0x3815, 0x31}, {0x3824, 0x04}, {0x460c, 0x20}}; + +static const struct ov5640_resolution_config resolutionParams[] = { + {.width = 640, + .height = 480, + .res_params = ov5640_low_res_params, + .mipi_pclk = { + .pllCtrl1 = 0x14, + .pllCtrl2 = 0x38, + }}, + {.width = 1280, + .height = 720, + .res_params = ov5640_720p_res_params, + .mipi_pclk = { + .pllCtrl1 = 0x21, + .pllCtrl2 = 0x54, + }}, +}; + +#define OV5640_VIDEO_FORMAT_CAP(width, height, format) \ + { \ + .pixelformat = (format), .width_min = (width), .width_max = (width), \ + .height_min = (height), .height_max = (height), .width_step = 0, .height_step = 0 \ + } + +static const struct video_format_cap fmts[] = { + OV5640_VIDEO_FORMAT_CAP(1280, 720, VIDEO_PIX_FMT_RGB565), + OV5640_VIDEO_FORMAT_CAP(1280, 720, VIDEO_PIX_FMT_YUYV), + OV5640_VIDEO_FORMAT_CAP(640, 480, VIDEO_PIX_FMT_RGB565), + OV5640_VIDEO_FORMAT_CAP(640, 480, VIDEO_PIX_FMT_YUYV), + {0}}; + +static int ov5640_read_reg(const struct i2c_dt_spec *spec, const uint16_t addr, void *val, + const uint8_t val_size) +{ + int ret; + struct i2c_msg msg[2]; + uint8_t addr_buf[2]; + + if (val_size > 4) { + return -ENOTSUP; + } + + addr_buf[1] = addr & 0xFF; + addr_buf[0] = addr >> 8; + msg[0].buf = addr_buf; + msg[0].len = 2U; + msg[0].flags = I2C_MSG_WRITE; + + msg[1].buf = (uint8_t *)val; + msg[1].len = val_size; + msg[1].flags = I2C_MSG_READ | I2C_MSG_STOP | I2C_MSG_RESTART; + + ret = i2c_transfer_dt(spec, msg, 2); + if (ret) { + return ret; + } + + switch (val_size) { + case 4: + *(uint32_t *)val = sys_be32_to_cpu(*(uint32_t *)val); + break; + case 2: + *(uint16_t *)val = sys_be16_to_cpu(*(uint16_t *)val); + break; + case 1: + break; + default: + return -ENOTSUP; + } + + return 0; +} + +static int ov5640_write_reg(const struct i2c_dt_spec *spec, const uint16_t addr, const uint8_t val) +{ + uint8_t addr_buf[2]; + struct i2c_msg msg[2]; + + addr_buf[1] = addr & 0xFF; + addr_buf[0] = addr >> 8; + msg[0].buf = addr_buf; + msg[0].len = 2U; + msg[0].flags = I2C_MSG_WRITE; + + msg[1].buf = (uint8_t *)&val; + msg[1].len = 1; + msg[1].flags = I2C_MSG_WRITE | I2C_MSG_STOP; + + return i2c_transfer_dt(spec, msg, 2); +} + +static int ov5640_modify_reg(const struct i2c_dt_spec *spec, const uint16_t addr, + const uint8_t mask, const uint8_t val) +{ + uint8_t regVal = 0; + int ret = ov5640_read_reg(spec, addr, ®Val, sizeof(regVal)); + + if (ret) { + return ret; + } + + return ov5640_write_reg(spec, addr, (regVal & ~mask) | (val & mask)); +} + +static int ov5640_write_multi_regs(const struct i2c_dt_spec *spec, const struct ov5640_reg *regs, + const uint32_t num_regs) +{ + int ret; + + for (int i = 0; i < num_regs; i++) { + ret = ov5640_write_reg(spec, regs[i].addr, regs[i].val); + if (ret) { + return ret; + } + } + + return 0; +} + +static int ov5640_set_fmt(const struct device *dev, enum video_endpoint_id ep, + struct video_format *fmt) +{ + struct ov5640_data *drv_data = dev->data; + const struct ov5640_config *cfg = dev->config; + int ret; + int i; + + for (i = 0; i < ARRAY_SIZE(fmts); ++i) { + if (fmt->pixelformat == fmts[i].pixelformat && fmt->width >= fmts[i].width_min && + fmt->width <= fmts[i].width_max && fmt->height >= fmts[i].height_min && + fmt->height <= fmts[i].height_max) { + break; + } + } + + if (i == ARRAY_SIZE(fmts)) { + LOG_ERR("Unsupported pixel format or resolution"); + return -ENOTSUP; + } + + if (!memcmp(&drv_data->fmt, fmt, sizeof(drv_data->fmt))) { + return 0; + } + + drv_data->fmt = *fmt; + + /* Set resolution parameters */ + for (i = 0; i < ARRAY_SIZE(resolutionParams); i++) { + if (fmt->width == resolutionParams[i].width && + fmt->height == resolutionParams[i].height) { + ret = ov5640_write_multi_regs(&cfg->i2c, resolutionParams[i].res_params, + OV5640_RESOLUTION_PARAM_NUM); + if (ret) { + LOG_ERR("Unable to set resolution parameters"); + return ret; + } + break; + } + } + + /* Set pixel format, default to VIDEO_PIX_FMT_RGB565 */ + struct ov5640_reg fmt_params[2] = { + {0x4300, 0x6f}, + {0x501f, 0x01}, + }; + + if (fmt->pixelformat == VIDEO_PIX_FMT_YUYV) { + fmt_params[0].val = 0x3f; + fmt_params[1].val = 0x00; + } + + ret = ov5640_write_multi_regs(&cfg->i2c, fmt_params, ARRAY_SIZE(fmt_params)); + if (ret) { + LOG_ERR("Unable to set pixel format"); + return ret; + } + + /* Configure MIPI pixel clock */ + ret |= ov5640_modify_reg(&cfg->i2c, SC_PLL_CTRL0_REG, 0x0f, 0x08); + ret |= ov5640_modify_reg(&cfg->i2c, SC_PLL_CTRL1_REG, 0xff, + resolutionParams[i].mipi_pclk.pllCtrl1); + ret |= ov5640_modify_reg(&cfg->i2c, SC_PLL_CTRL2_REG, 0xff, + resolutionParams[i].mipi_pclk.pllCtrl2); + ret |= ov5640_modify_reg(&cfg->i2c, SC_PLL_CTRL3_REG, 0x1f, 0x13); + ret |= ov5640_modify_reg(&cfg->i2c, SYS_ROOT_DIV_REG, 0x3f, 0x01); + ret |= ov5640_write_reg(&cfg->i2c, PCLK_PERIOD_REG, 0x0a); + if (ret) { + LOG_ERR("Unable to configure MIPI pixel clock"); + return ret; + } + + return 0; +} + +static int ov5640_get_fmt(const struct device *dev, enum video_endpoint_id ep, + struct video_format *fmt) +{ + struct ov5640_data *drv_data = dev->data; + + *fmt = drv_data->fmt; + + return 0; +} + +static int ov5640_get_caps(const struct device *dev, enum video_endpoint_id ep, + struct video_caps *caps) +{ + caps->format_caps = fmts; + return 0; +} + +static int ov5640_stream_start(const struct device *dev) +{ + const struct ov5640_config *cfg = dev->config; + /* Power up MIPI PHY HS Tx & LP Rx in 2 data lanes mode */ + int ret = ov5640_write_reg(&cfg->i2c, IO_MIPI_CTRL00_REG, 0x45); + + if (ret) { + LOG_ERR("Unable to power up MIPI PHY"); + return ret; + } + return ov5640_write_reg(&cfg->i2c, SYS_CTRL0_REG, SYS_CTRL0_SW_PWUP); +} + +static int ov5640_stream_stop(const struct device *dev) +{ + const struct ov5640_config *cfg = dev->config; + /* Power down MIPI PHY HS Tx & LP Rx */ + int ret = ov5640_write_reg(&cfg->i2c, IO_MIPI_CTRL00_REG, 0x40); + + if (ret) { + LOG_ERR("Unable to power down MIPI PHY"); + return ret; + } + return ov5640_write_reg(&cfg->i2c, SYS_CTRL0_REG, SYS_CTRL0_SW_PWDN); +} + +static const struct video_driver_api ov5640_driver_api = { + .set_format = ov5640_set_fmt, + .get_format = ov5640_get_fmt, + .get_caps = ov5640_get_caps, + .stream_start = ov5640_stream_start, + .stream_stop = ov5640_stream_stop, +}; + +static int ov5640_init(const struct device *dev) +{ + const struct ov5640_config *cfg = dev->config; + struct video_format fmt; + uint16_t chip_id; + int ret; + + if (!device_is_ready(cfg->i2c.bus)) { + LOG_ERR("Bus device is not ready"); + return -ENODEV; + } + + if (!gpio_is_ready_dt(&cfg->reset_gpio)) { + LOG_ERR("%s: device %s is not ready", dev->name, cfg->reset_gpio.port->name); + return -ENODEV; + } + + if (!gpio_is_ready_dt(&cfg->powerdown_gpio)) { + LOG_ERR("%s: device %s is not ready", dev->name, cfg->powerdown_gpio.port->name); + return -ENODEV; + } + + /* Power up sequence */ + if (cfg->powerdown_gpio.port != NULL) { + ret = gpio_pin_configure_dt(&cfg->powerdown_gpio, GPIO_OUTPUT_ACTIVE); + if (ret) { + return ret; + } + } + + if (cfg->reset_gpio.port != NULL) { + ret = gpio_pin_configure_dt(&cfg->reset_gpio, GPIO_OUTPUT_ACTIVE); + if (ret) { + return ret; + } + } + + k_sleep(K_MSEC(5)); + + if (cfg->powerdown_gpio.port != NULL) { + gpio_pin_set_dt(&cfg->powerdown_gpio, 0); + } + + k_sleep(K_MSEC(1)); + + if (cfg->reset_gpio.port != NULL) { + gpio_pin_set_dt(&cfg->reset_gpio, 0); + } + + k_sleep(K_MSEC(20)); + + /* Software reset */ + ret = ov5640_write_reg(&cfg->i2c, SYS_CTRL0_REG, SYS_CTRL0_SW_RST); + if (ret) { + LOG_ERR("Unable to perform software reset"); + return -EIO; + } + + k_sleep(K_MSEC(5)); + + /* Initialize register values */ + ret = ov5640_write_multi_regs(&cfg->i2c, ov5640InitParams, ARRAY_SIZE(ov5640InitParams)); + if (ret) { + LOG_ERR("Unable to initialize the sensor"); + return -EIO; + } + + /* Set virtual channel */ + ret = ov5640_modify_reg(&cfg->i2c, 0x4814, 3U << 6, (uint8_t)(DEFAULT_MIPI_CHANNEL) << 6); + if (ret) { + LOG_ERR("Unable to set virtual channel"); + return -EIO; + } + + /* Check sensor chip id */ + ret = ov5640_read_reg(&cfg->i2c, CHIP_ID_REG, &chip_id, sizeof(chip_id)); + if (ret) { + LOG_ERR("Unable to read sensor chip ID, ret = %d", ret); + return -ENODEV; + } + + if (chip_id != CHIP_ID_VAL) { + LOG_ERR("Wrong chip ID: %04x (expected %04x)", chip_id, CHIP_ID_VAL); + return -ENODEV; + } + + /* Set default format to 720p RGB565 */ + fmt.pixelformat = VIDEO_PIX_FMT_RGB565; + fmt.width = 1280; + fmt.height = 720; + fmt.pitch = fmt.width * 2; + ret = ov5640_set_fmt(dev, VIDEO_EP_OUT, &fmt); + if (ret) { + LOG_ERR("Unable to configure default format"); + return -EIO; + } + + return 0; +} + +#define OV5640_INIT(n) \ + static struct ov5640_data ov5640_data_##n; \ + \ + static const struct ov5640_config ov5640_cfg_##n = { \ + .i2c = I2C_DT_SPEC_INST_GET(n), \ + .reset_gpio = GPIO_DT_SPEC_INST_GET_OR(n, reset_gpios, {0}), \ + .powerdown_gpio = GPIO_DT_SPEC_INST_GET_OR(n, powerdown_gpios, {0}), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(n, &ov5640_init, NULL, &ov5640_data_##n, &ov5640_cfg_##n, \ + POST_KERNEL, CONFIG_VIDEO_INIT_PRIORITY, &ov5640_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(OV5640_INIT) From 4c0b845577aaed9d8aa6b9ef049de417371199ca Mon Sep 17 00:00:00 2001 From: Phi Bang Nguyen Date: Mon, 4 Mar 2024 15:55:57 +0100 Subject: [PATCH 3/3] tests: drivers: build_all: video: Add entry for ov5640 Add entry for ov5640 and add mimxrt1170_evk to the test platforms Signed-off-by: Phi Bang Nguyen --- tests/drivers/build_all/video/app.overlay | 7 +++++++ tests/drivers/build_all/video/testcase.yaml | 4 +++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/drivers/build_all/video/app.overlay b/tests/drivers/build_all/video/app.overlay index a38cfdd0e133c..e06d2940817a9 100644 --- a/tests/drivers/build_all/video/app.overlay +++ b/tests/drivers/build_all/video/app.overlay @@ -47,6 +47,13 @@ reg = <0x2>; reset-gpios = <&test_gpio 0 0>; }; + + test_i2c_ov5640: ov5640@3 { + compatible = "ovti,ov5640"; + reg = <0x3>; + reset-gpios = <&test_gpio 0 0>; + powerdown-gpios = <&test_gpio 1 0>; + }; }; }; }; diff --git a/tests/drivers/build_all/video/testcase.yaml b/tests/drivers/build_all/video/testcase.yaml index 08dd430da7314..2acb801b9c643 100644 --- a/tests/drivers/build_all/video/testcase.yaml +++ b/tests/drivers/build_all/video/testcase.yaml @@ -13,4 +13,6 @@ tests: - gpio - i2c drivers.video.mcux_csi.build: - platform_allow: mimxrt1064_evk + platform_allow: + - mimxrt1064_evk + - mimxrt1170_evk/mimxrt1176/cm7