| /* |
| * Copyright 2024 NXP |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define DT_DRV_COMPAT ovti_ov5640 |
| |
| #include <zephyr/device.h> |
| #include <zephyr/drivers/i2c.h> |
| #include <zephyr/drivers/gpio.h> |
| #include <zephyr/drivers/video.h> |
| #include <zephyr/kernel.h> |
| |
| #define LOG_LEVEL CONFIG_LOG_DEFAULT_LEVEL |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(ov5640); |
| |
| #include <zephyr/sys/byteorder.h> |
| |
| #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) |