|  | /* | 
|  | * Copyright (c) 2020, FrankLi Limited | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | #define DT_DRV_COMPAT ovti_ov7725 | 
|  |  | 
|  | #include <zephyr/kernel.h> | 
|  | #include <zephyr/device.h> | 
|  | #include <zephyr/sys/byteorder.h> | 
|  | #include <zephyr/logging/log.h> | 
|  | #include <zephyr/drivers/video.h> | 
|  | #include <zephyr/drivers/i2c.h> | 
|  | #include <zephyr/drivers/gpio.h> | 
|  |  | 
|  | LOG_MODULE_REGISTER(video_ov7725, CONFIG_VIDEO_LOG_LEVEL); | 
|  |  | 
|  | #define OV7725_REVISION  0x7721U | 
|  |  | 
|  | #define OV7725_GAIN       0x00U | 
|  | #define OV7725_BLUE       0x01U | 
|  | #define OV7725_RED        0x02U | 
|  | #define OV7725_GREEN      0x03U | 
|  | #define OV7725_BAVG       0x05U | 
|  | #define OV7725_GAVG       0x06U | 
|  | #define OV7725_RAVG       0x07U | 
|  | #define OV7725_AECH       0x08U | 
|  | #define OV7725_COM2       0x09U | 
|  | #define OV7725_PID        0x0AU | 
|  | #define OV7725_VER        0x0BU | 
|  | #define OV7725_COM3       0x0CU | 
|  | #define OV7725_COM4       0x0DU | 
|  | #define OV7725_COM5       0x0EU | 
|  | #define OV7725_COM6       0x0FU | 
|  | #define OV7725_AEC        0x10U | 
|  | #define OV7725_CLKRC      0x11U | 
|  | #define OV7725_COM7       0x12U | 
|  | #define OV7725_COM8       0x13U | 
|  | #define OV7725_COM9       0x14U | 
|  | #define OV7725_COM10      0x15U | 
|  | #define OV7725_REG16      0x16U | 
|  | #define OV7725_HSTART     0x17U | 
|  | #define OV7725_HSIZE      0x18U | 
|  | #define OV7725_VSTART     0x19U | 
|  | #define OV7725_VSIZE      0x1AU | 
|  | #define OV7725_PSHFT      0x1BU | 
|  | #define OV7725_MIDH       0x1CU | 
|  | #define OV7725_MIDL       0x1DU | 
|  | #define OV7725_LAEC       0x1FU | 
|  | #define OV7725_COM11      0x20U | 
|  | #define OV7725_BDBASE     0x22U | 
|  | #define OV7725_BDMSTEP    0x23U | 
|  | #define OV7725_AEW        0x24U | 
|  | #define OV7725_AEB        0x25U | 
|  | #define OV7725_VPT        0x26U | 
|  | #define OV7725_REG28      0x28U | 
|  | #define OV7725_HOUTSIZE   0x29U | 
|  | #define OV7725_EXHCH      0x2AU | 
|  | #define OV7725_EXHCL      0x2BU | 
|  | #define OV7725_VOUTSIZE   0x2CU | 
|  | #define OV7725_ADVFL      0x2DU | 
|  | #define OV7725_ADVFH      0x2EU | 
|  | #define OV7725_YAVE       0x2FU | 
|  | #define OV7725_LUMHTH     0x30U | 
|  | #define OV7725_LUMLTH     0x31U | 
|  | #define OV7725_HREF       0x32U | 
|  | #define OV7725_DM_LNL     0x33U | 
|  | #define OV7725_DM_LNH     0x34U | 
|  | #define OV7725_ADOFF_B    0x35U | 
|  | #define OV7725_ADOFF_R    0x36U | 
|  | #define OV7725_ADOFF_GB   0x37U | 
|  | #define OV7725_ADOFF_GR   0x38U | 
|  | #define OV7725_OFF_B      0x39U | 
|  | #define OV7725_OFF_R      0x3AU | 
|  | #define OV7725_OFF_GB     0x3BU | 
|  | #define OV7725_OFF_GR     0x3CU | 
|  | #define OV7725_COM12      0x3DU | 
|  | #define OV7725_COM13      0x3EU | 
|  | #define OV7725_COM14      0x3FU | 
|  | #define OV7725_COM16      0x41U | 
|  | #define OV7725_TGT_B      0x42U | 
|  | #define OV7725_TGT_R      0x43U | 
|  | #define OV7725_TGT_GB     0x44U | 
|  | #define OV7725_TGT_GR     0x45U | 
|  | #define OV7725_LC_CTR     0x46U | 
|  | #define OV7725_LC_XC      0x47U | 
|  | #define OV7725_LC_YC      0x48U | 
|  | #define OV7725_LC_COEF    0x49U | 
|  | #define OV7725_LC_RADI    0x4AU | 
|  | #define OV7725_LC_COEFB   0x4BU | 
|  | #define OV7725_LC_COEFR   0x4CU | 
|  | #define OV7725_FIXGAIN    0x4DU | 
|  | #define OV7725_AREF1      0x4FU | 
|  | #define OV7725_AREF6      0x54U | 
|  | #define OV7725_UFIX       0x60U | 
|  | #define OV7725_VFIX       0x61U | 
|  | #define OV7725_AWBB_BLK   0x62U | 
|  | #define OV7725_AWB_CTRL0  0x63U | 
|  | #define OV7725_DSP_CTRL1  0x64U | 
|  | #define OV7725_DSP_CTRL2  0x65U | 
|  | #define OV7725_DSP_CTRL3  0x66U | 
|  | #define OV7725_DSP_CTRL4  0x67U | 
|  | #define OV7725_AWB_BIAS   0x68U | 
|  | #define OV7725_AWB_CTRL1  0x69U | 
|  | #define OV7725_AWB_CTRL2  0x6AU | 
|  | #define OV7725_AWB_CTRL3  0x6BU | 
|  | #define OV7725_AWB_CTRL4  0x6CU | 
|  | #define OV7725_AWB_CTRL5  0x6DU | 
|  | #define OV7725_AWB_CTRL6  0x6EU | 
|  | #define OV7725_AWB_CTRL7  0x6FU | 
|  | #define OV7725_AWB_CTRL8  0x70U | 
|  | #define OV7725_AWB_CTRL9  0x71U | 
|  | #define OV7725_AWB_CTRL10 0x72U | 
|  | #define OV7725_AWB_CTRL11 0x73U | 
|  | #define OV7725_AWB_CTRL12 0x74U | 
|  | #define OV7725_AWB_CTRL13 0x75U | 
|  | #define OV7725_AWB_CTRL14 0x76U | 
|  | #define OV7725_AWB_CTRL15 0x77U | 
|  | #define OV7725_AWB_CTRL16 0x78U | 
|  | #define OV7725_AWB_CTRL17 0x79U | 
|  | #define OV7725_AWB_CTRL18 0x7AU | 
|  | #define OV7725_AWB_CTRL19 0x7BU | 
|  | #define OV7725_AWB_CTRL20 0x7CU | 
|  | #define OV7725_AWB_CTRL21 0x7DU | 
|  | #define OV7725_GAM1       0x7EU | 
|  | #define OV7725_GAM2       0x7FU | 
|  | #define OV7725_GAM3       0x80U | 
|  | #define OV7725_GAM4       0x81U | 
|  | #define OV7725_GAM5       0x82U | 
|  | #define OV7725_GAM6       0x83U | 
|  | #define OV7725_GAM7       0x84U | 
|  | #define OV7725_GAM8       0x85U | 
|  | #define OV7725_GAM9       0x86U | 
|  | #define OV7725_GAM10      0x87U | 
|  | #define OV7725_GAM11      0x88U | 
|  | #define OV7725_GAM12      0x89U | 
|  | #define OV7725_GAM13      0x8AU | 
|  | #define OV7725_GAM14      0x8BU | 
|  | #define OV7725_GAM15      0x8CU | 
|  | #define OV7725_SLOP       0x8DU | 
|  | #define OV7725_DNSTH      0x8EU | 
|  | #define OV7725_EDGE0      0x8FU | 
|  | #define OV7725_EDGE1      0x90U | 
|  | #define OV7725_DNSOFF     0x91U | 
|  | #define OV7725_EDGE2      0x92U | 
|  | #define OV7725_EDGE3      0x93U | 
|  | #define OV7725_MTX1       0x94U | 
|  | #define OV7725_MTX2       0x95U | 
|  | #define OV7725_MTX3       0x96U | 
|  | #define OV7725_MTX4       0x97U | 
|  | #define OV7725_MTX5       0x98U | 
|  | #define OV7725_MTX6       0x99U | 
|  | #define OV7725_MTX_CTRL   0x9AU | 
|  | #define OV7725_BRIGHT     0x9BU | 
|  | #define OV7725_CNST       0x9CU | 
|  | #define OV7725_UVADJ0     0x9EU | 
|  | #define OV7725_UVADJ1     0x9FU | 
|  | #define OV7725_SCAL0      0xA0U | 
|  | #define OV7725_SCAL1      0xA1U | 
|  | #define OV7725_SCAL2      0xA2U | 
|  | #define OV7725_SDE        0xA6U | 
|  | #define OV7725_USAT       0xA7U | 
|  | #define OV7725_VSAT       0xA8U | 
|  | #define OV7725_HUECOS     0xA9U | 
|  | #define OV7725_HUESIN     0xAAU | 
|  | #define OV7725_SIGN       0xABU | 
|  | #define OV7725_DSPAUTO    0xACU | 
|  |  | 
|  | #define OV7725_COM10_VSYNC_NEG_MASK    BIT(1) | 
|  | #define OV7725_COM10_HREF_REVERSE_MASK BIT(3) | 
|  | #define OV7725_COM10_PCLK_REVERSE_MASK BIT(4) | 
|  | #define OV7725_COM10_PCLK_OUT_MASK     BIT(5) | 
|  | #define OV7725_COM10_DATA_NEG_MASK     BIT(7) | 
|  |  | 
|  | struct ov7725_config { | 
|  | struct i2c_dt_spec i2c; | 
|  | #if DT_INST_NODE_HAS_PROP(0, reset_gpios) | 
|  | struct gpio_dt_spec reset_gpio; | 
|  | #endif | 
|  | }; | 
|  |  | 
|  | struct ov7725_data { | 
|  | struct video_format fmt; | 
|  | }; | 
|  |  | 
|  | struct ov7725_clock { | 
|  | uint32_t input_clk; | 
|  | uint32_t framerate; | 
|  | uint8_t clkrc;  /*!< Register CLKRC. */ | 
|  | uint8_t com4;   /*!< Register COM4. */ | 
|  | uint8_t dm_lnl; /*!< Register DM_LNL. */ | 
|  | }; | 
|  |  | 
|  | struct ov7725_pixel_format { | 
|  | uint32_t pixel_format; | 
|  | uint8_t com7; | 
|  | }; | 
|  |  | 
|  | struct ov7725_reg { | 
|  | uint8_t addr; | 
|  | uint8_t value; | 
|  | }; | 
|  |  | 
|  | static const struct ov7725_clock ov7725_clock_configs[] = { | 
|  | { .input_clk = 24000000, .framerate = 30, | 
|  | .clkrc = 0x01, .com4 = 0x41, .dm_lnl = 0x00 }, | 
|  | { .input_clk = 24000000, .framerate = 15, | 
|  | .clkrc = 0x03, .com4 = 0x41, .dm_lnl = 0x00 }, | 
|  | { .input_clk = 24000000, .framerate = 25, | 
|  | .clkrc = 0x01, .com4 = 0x41, .dm_lnl = 0x66 }, | 
|  | { .input_clk = 24000000, .framerate = 14, | 
|  | .clkrc = 0x03, .com4 = 0x41, .dm_lnl = 0x1a }, | 
|  | { .input_clk = 26000000, .framerate = 30, | 
|  | .clkrc = 0x01, .com4 = 0x41, .dm_lnl = 0x2b }, | 
|  | { .input_clk = 26000000, .framerate = 15, | 
|  | .clkrc = 0x03, .com4 = 0x41, .dm_lnl = 0x2b }, | 
|  | { .input_clk = 26000000, .framerate = 25, | 
|  | .clkrc = 0x01, .com4 = 0x41, .dm_lnl = 0x99 }, | 
|  | { .input_clk = 26000000, .framerate = 14, | 
|  | .clkrc = 0x03, .com4 = 0x41, .dm_lnl = 0x46 }, | 
|  | { .input_clk = 13000000, .framerate = 30, | 
|  | .clkrc = 0x00, .com4 = 0x41, .dm_lnl = 0x2b }, | 
|  | { .input_clk = 13000000, .framerate = 15, | 
|  | .clkrc = 0x01, .com4 = 0x41, .dm_lnl = 0x2b }, | 
|  | { .input_clk = 13000000, .framerate = 25, | 
|  | .clkrc = 0x00, .com4 = 0x41, .dm_lnl = 0x99 }, | 
|  | { .input_clk = 13000000, .framerate = 14, | 
|  | .clkrc = 0x01, .com4 = 0x41, .dm_lnl = 0x46 }, | 
|  | }; | 
|  |  | 
|  |  | 
|  | static const struct ov7725_pixel_format ov7725_pf_configs[] = { | 
|  | { .pixel_format = VIDEO_PIX_FMT_RGB565, .com7 = (1 << 2) | (2) } | 
|  | }; | 
|  |  | 
|  | static const struct ov7725_reg ov7725_init_reg_tb[] = { | 
|  | /*Output config*/ | 
|  | { OV7725_CLKRC,          0x00 }, | 
|  | { OV7725_COM7,           0x06 }, | 
|  | { OV7725_HSTART,         0x3f }, | 
|  | { OV7725_HSIZE,          0x50 }, | 
|  | { OV7725_VSTART,         0x03 }, | 
|  | { OV7725_VSIZE,          0x78 }, | 
|  | { OV7725_HREF,           0x00 }, | 
|  | { OV7725_HOUTSIZE,       0x50 }, | 
|  | { OV7725_VOUTSIZE,       0x78 }, | 
|  |  | 
|  | /*DSP control*/ | 
|  | { OV7725_TGT_B,          0x7f }, | 
|  | { OV7725_FIXGAIN,        0x09 }, | 
|  | { OV7725_AWB_CTRL0,      0xe0 }, | 
|  | { OV7725_DSP_CTRL1,      0xff }, | 
|  | { OV7725_DSP_CTRL2,      0x00 }, | 
|  | { OV7725_DSP_CTRL3,      0x00 }, | 
|  | { OV7725_DSP_CTRL4,      0x00 }, | 
|  |  | 
|  | /*AGC AEC AWB*/ | 
|  | { OV7725_COM8,           0xf0 }, | 
|  | { OV7725_COM4,           0x81 }, | 
|  | { OV7725_COM6,           0xc5 }, | 
|  | { OV7725_COM9,           0x11 }, | 
|  | { OV7725_BDBASE,         0x7F }, | 
|  | { OV7725_BDMSTEP,        0x03 }, | 
|  | { OV7725_AEW,            0x40 }, | 
|  | { OV7725_AEB,            0x30 }, | 
|  | { OV7725_VPT,            0xa1 }, | 
|  | { OV7725_EXHCL,          0x9e }, | 
|  | { OV7725_AWB_CTRL3,      0xaa }, | 
|  | { OV7725_COM8,           0xff }, | 
|  |  | 
|  | /*matrix sharpness brightness contrast*/ | 
|  | { OV7725_EDGE1,          0x08 }, | 
|  | { OV7725_DNSOFF,         0x01 }, | 
|  | { OV7725_EDGE2,          0x03 }, | 
|  | { OV7725_EDGE3,          0x00 }, | 
|  | { OV7725_MTX1,           0xb0 }, | 
|  | { OV7725_MTX2,           0x9d }, | 
|  | { OV7725_MTX3,           0x13 }, | 
|  | { OV7725_MTX4,           0x16 }, | 
|  | { OV7725_MTX5,           0x7b }, | 
|  | { OV7725_MTX6,           0x91 }, | 
|  | { OV7725_MTX_CTRL,       0x1e }, | 
|  | { OV7725_BRIGHT,         0x08 }, | 
|  | { OV7725_CNST,           0x20 }, | 
|  | { OV7725_UVADJ0,         0x81 }, | 
|  | { OV7725_SDE,            0X06 }, | 
|  | { OV7725_USAT,           0x65 }, | 
|  | { OV7725_VSAT,           0x65 }, | 
|  | { OV7725_HUECOS,         0X80 }, | 
|  | { OV7725_HUESIN,         0X80 }, | 
|  |  | 
|  | /*GAMMA config*/ | 
|  | { OV7725_GAM1,           0x0c }, | 
|  | { OV7725_GAM2,           0x16 }, | 
|  | { OV7725_GAM3,           0x2a }, | 
|  | { OV7725_GAM4,           0x4e }, | 
|  | { OV7725_GAM5,           0x61 }, | 
|  | { OV7725_GAM6,           0x6f }, | 
|  | { OV7725_GAM7,           0x7b }, | 
|  | { OV7725_GAM8,           0x86 }, | 
|  | { OV7725_GAM9,           0x8e }, | 
|  | { OV7725_GAM10,          0x97 }, | 
|  | { OV7725_GAM11,          0xa4 }, | 
|  | { OV7725_GAM12,          0xaf }, | 
|  | { OV7725_GAM13,          0xc5 }, | 
|  | { OV7725_GAM14,          0xd7 }, | 
|  | { OV7725_GAM15,          0xe8 }, | 
|  | { OV7725_SLOP,           0x20 }, | 
|  |  | 
|  | { OV7725_COM3,           0x40 }, | 
|  | { OV7725_COM5,           0xf5 }, | 
|  | { OV7725_COM10,          0x02 }, | 
|  | { OV7725_COM2,           0x01 } | 
|  | }; | 
|  |  | 
|  | static int ov7725_write_reg(const struct i2c_dt_spec *spec, uint8_t reg_addr, | 
|  | uint8_t value) | 
|  | { | 
|  | struct i2c_msg msgs[2]; | 
|  |  | 
|  | msgs[0].buf = (uint8_t *)®_addr; | 
|  | msgs[0].len = 1; | 
|  | msgs[0].flags = I2C_MSG_WRITE; | 
|  |  | 
|  | msgs[1].buf = (uint8_t *)&value; | 
|  | msgs[1].len = 1; | 
|  | msgs[1].flags = I2C_MSG_WRITE | I2C_MSG_STOP; | 
|  |  | 
|  | return i2c_transfer_dt(spec, msgs, 2); | 
|  | } | 
|  |  | 
|  | static int ov7725_read_reg(const struct i2c_dt_spec *spec, uint8_t reg_addr, | 
|  | uint8_t *value) | 
|  | { | 
|  | struct i2c_msg msgs[2]; | 
|  |  | 
|  | msgs[0].buf = (uint8_t *)®_addr; | 
|  | msgs[0].len = 1; | 
|  | /* | 
|  | * When using I2C to read the registers of the SCCB device, | 
|  | * a stop bit is required after writing the register address | 
|  | */ | 
|  | msgs[0].flags = I2C_MSG_WRITE | I2C_MSG_STOP; | 
|  |  | 
|  | msgs[1].buf = (uint8_t *)value; | 
|  | msgs[1].len = 1; | 
|  | msgs[1].flags = I2C_MSG_READ | I2C_MSG_STOP | I2C_MSG_RESTART; | 
|  |  | 
|  | return i2c_transfer_dt(spec, msgs, 2); | 
|  | } | 
|  |  | 
|  | int ov7725_modify_reg(const struct i2c_dt_spec *spec, | 
|  | uint8_t reg_addr, | 
|  | uint8_t clear_mask, | 
|  | uint8_t value) | 
|  | { | 
|  | int ret; | 
|  | uint8_t set_value; | 
|  |  | 
|  | ret = ov7725_read_reg(spec, reg_addr, &set_value); | 
|  |  | 
|  | if (ret == 0) { | 
|  | set_value = (set_value & (~clear_mask)) | | 
|  | (set_value & clear_mask); | 
|  | ret = ov7725_write_reg(spec, reg_addr, set_value); | 
|  | } | 
|  |  | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int ov7725_write_all(const struct device *dev, | 
|  | const struct ov7725_reg *regs, | 
|  | uint16_t reg_num) | 
|  | { | 
|  | uint16_t i = 0; | 
|  | const struct ov7725_config *cfg = dev->config; | 
|  |  | 
|  | for (i = 0; i < reg_num; i++) { | 
|  | int err; | 
|  |  | 
|  | err = ov7725_write_reg(&cfg->i2c, regs[i].addr, regs[i].value); | 
|  | if (err) { | 
|  | return err; | 
|  | } | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int ov7725_set_clock(const struct device *dev, | 
|  | unsigned int framerate, | 
|  | unsigned int input_clk) | 
|  | { | 
|  | const struct ov7725_config *cfg = dev->config; | 
|  |  | 
|  | for (unsigned int i = 0; i < ARRAY_SIZE(ov7725_clock_configs); i++) { | 
|  | if ((ov7725_clock_configs[i].framerate == framerate) && | 
|  | (ov7725_clock_configs[i].input_clk == input_clk)) { | 
|  | ov7725_write_reg(&cfg->i2c, OV7725_CLKRC, | 
|  | ov7725_clock_configs[i].clkrc); | 
|  | ov7725_modify_reg(&cfg->i2c, OV7725_COM4, 0xc0, | 
|  | ov7725_clock_configs[i].com4); | 
|  | ov7725_write_reg(&cfg->i2c, OV7725_EXHCL, 0x00); | 
|  | ov7725_write_reg(&cfg->i2c, OV7725_DM_LNL, | 
|  | ov7725_clock_configs[i].dm_lnl); | 
|  | ov7725_write_reg(&cfg->i2c, OV7725_DM_LNH, 0x00); | 
|  | ov7725_write_reg(&cfg->i2c, OV7725_ADVFL, 0x00); | 
|  | ov7725_write_reg(&cfg->i2c, OV7725_ADVFH, 0x00); | 
|  | return ov7725_write_reg(&cfg->i2c, OV7725_COM5, 0x65); | 
|  | } | 
|  | } | 
|  |  | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | static int ov7725_set_fmt(const struct device *dev, | 
|  | enum video_endpoint_id ep, | 
|  | struct video_format *fmt) | 
|  | { | 
|  | struct ov7725_data *drv_data = dev->data; | 
|  | const struct ov7725_config *cfg = dev->config; | 
|  | uint8_t com10 = 0; | 
|  | uint16_t width, height; | 
|  | uint16_t hstart, vstart, hsize; | 
|  | int ret; | 
|  |  | 
|  | /* we only support one format for now (VGA RGB565) */ | 
|  | if (fmt->pixelformat != VIDEO_PIX_FMT_RGB565 || fmt->height != 480 || | 
|  | fmt->width != 640) { | 
|  | return -ENOTSUP; | 
|  | } | 
|  |  | 
|  | width = fmt->width; | 
|  | height = fmt->height; | 
|  |  | 
|  | if (!memcmp(&drv_data->fmt, fmt, sizeof(drv_data->fmt))) { | 
|  | /* nothing to do */ | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | drv_data->fmt = *fmt; | 
|  |  | 
|  | /* Configure Sensor */ | 
|  | ret = ov7725_write_all(dev, ov7725_init_reg_tb, | 
|  | ARRAY_SIZE(ov7725_init_reg_tb)); | 
|  | if (ret) { | 
|  | LOG_ERR("Unable to write ov7725 config"); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* Set clock : framerate 30fps, input clock 24M*/ | 
|  | ov7725_set_clock(dev, 30, 24000000); | 
|  |  | 
|  | /* Set output format */ | 
|  | for (uint8_t i = 0; i < ARRAY_SIZE(ov7725_pf_configs); i++) { | 
|  | if (ov7725_pf_configs[i].pixel_format == fmt->pixelformat) { | 
|  | ret =  ov7725_modify_reg(&cfg->i2c, | 
|  | OV7725_COM7, | 
|  | 0x1FU, | 
|  | ov7725_pf_configs[i].com7); | 
|  | if (ret) { | 
|  | LOG_ERR("Unable to write ov7725 pixel format"); | 
|  | return ret; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | ov7725_modify_reg(&cfg->i2c, OV7725_COM7, (1 << 5), (0 << 5)); | 
|  |  | 
|  | com10 |= OV7725_COM10_VSYNC_NEG_MASK; | 
|  | ov7725_write_reg(&cfg->i2c, OV7725_COM10, com10); | 
|  |  | 
|  | /* Don't swap output MSB/LSB. */ | 
|  | ov7725_write_reg(&cfg->i2c, OV7725_COM3, 0x00); | 
|  |  | 
|  | /* | 
|  | * Output drive capability | 
|  | * 0: 1X | 
|  | * 1: 2X | 
|  | * 2: 3X | 
|  | * 3: 4X | 
|  | */ | 
|  | ov7725_modify_reg(&cfg->i2c, OV7725_COM2, 0x03, 0x03); | 
|  |  | 
|  | /* Resolution and timing. */ | 
|  | hstart = 0x22U << 2U; | 
|  | vstart = 0x07U << 1U; | 
|  | hsize = width + 16U; | 
|  |  | 
|  | /* Set the window size. */ | 
|  | ov7725_write_reg(&cfg->i2c, OV7725_HSTART, hstart >> 2U); | 
|  | ov7725_write_reg(&cfg->i2c, OV7725_HSIZE, hsize >> 2U); | 
|  | ov7725_write_reg(&cfg->i2c, OV7725_VSTART, vstart >> 1U); | 
|  | ov7725_write_reg(&cfg->i2c, OV7725_VSIZE, height >> 1U); | 
|  | ov7725_write_reg(&cfg->i2c, OV7725_HOUTSIZE, width >> 2U); | 
|  | ov7725_write_reg(&cfg->i2c, OV7725_VOUTSIZE, height >> 1U); | 
|  | ov7725_write_reg(&cfg->i2c, OV7725_HREF, | 
|  | ((vstart & 1U) << 6U) | | 
|  | ((hstart & 3U) << 4U) | | 
|  | ((height & 1U) << 2U) | | 
|  | ((hsize & 3U) << 0U)); | 
|  | return ov7725_write_reg(&cfg->i2c, OV7725_EXHCH, | 
|  | ((height & 1U) << 2U) | | 
|  | ((width & 3U) << 0U)); | 
|  | } | 
|  |  | 
|  | static int ov7725_get_fmt(const struct device *dev, | 
|  | enum video_endpoint_id ep, | 
|  | struct video_format *fmt) | 
|  | { | 
|  | struct ov7725_data *drv_data = dev->data; | 
|  |  | 
|  | *fmt = drv_data->fmt; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int ov7725_set_stream(const struct device *dev, bool enable) | 
|  | { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static const struct video_format_cap fmts[] = { | 
|  | { | 
|  | .pixelformat = VIDEO_PIX_FMT_RGB565, | 
|  | .width_min = 640, | 
|  | .width_max = 640, | 
|  | .height_min = 480, | 
|  | .height_max = 480, | 
|  | .width_step = 0, | 
|  | .height_step = 0, | 
|  | }, | 
|  | { 0 } | 
|  | }; | 
|  |  | 
|  | static int ov7725_get_caps(const struct device *dev, | 
|  | enum video_endpoint_id ep, | 
|  | struct video_caps *caps) | 
|  | { | 
|  | caps->format_caps = fmts; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static DEVICE_API(video, ov7725_driver_api) = { | 
|  | .set_format = ov7725_set_fmt, | 
|  | .get_format = ov7725_get_fmt, | 
|  | .get_caps = ov7725_get_caps, | 
|  | .set_stream = ov7725_set_stream, | 
|  | }; | 
|  |  | 
|  | static int ov7725_init(const struct device *dev) | 
|  | { | 
|  | const struct ov7725_config *cfg = dev->config; | 
|  | struct video_format fmt; | 
|  | uint8_t pid, ver; | 
|  | int ret; | 
|  |  | 
|  | #if DT_INST_NODE_HAS_PROP(0, reset_gpios) | 
|  | ret = gpio_pin_configure_dt(&cfg->reset_gpio, GPIO_OUTPUT_ACTIVE); | 
|  | if (ret) { | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | gpio_pin_set_dt(&cfg->reset_gpio, 0); | 
|  | k_sleep(K_MSEC(1)); | 
|  | gpio_pin_set_dt(&cfg->reset_gpio, 1); | 
|  | k_sleep(K_MSEC(1)); | 
|  | #endif | 
|  |  | 
|  | /* Identify the device. */ | 
|  | ret = ov7725_read_reg(&cfg->i2c, OV7725_PID, &pid); | 
|  | if (ret) { | 
|  | LOG_ERR("Unable to read PID"); | 
|  | return -ENODEV; | 
|  | } | 
|  |  | 
|  | ret = ov7725_read_reg(&cfg->i2c, OV7725_VER, &ver); | 
|  | if (ret) { | 
|  | LOG_ERR("Unable to read VER"); | 
|  | return -ENODEV; | 
|  | } | 
|  |  | 
|  | if (OV7725_REVISION != (((uint32_t)pid << 8U) | (uint32_t)ver)) { | 
|  | LOG_ERR("OV7725 Get Vision fail\n"); | 
|  | return -ENODEV; | 
|  | } | 
|  |  | 
|  | /* Device identify OK, perform software reset. */ | 
|  | ov7725_write_reg(&cfg->i2c, OV7725_COM7, 0x80); | 
|  |  | 
|  | k_sleep(K_MSEC(2)); | 
|  |  | 
|  | /* set default/init format VGA RGB565 */ | 
|  | fmt.pixelformat = VIDEO_PIX_FMT_RGB565; | 
|  | fmt.width = 640; | 
|  | fmt.height = 480; | 
|  | fmt.pitch = 640 * 2; | 
|  | ret = ov7725_set_fmt(dev, VIDEO_EP_OUT, &fmt); | 
|  | if (ret) { | 
|  | LOG_ERR("Unable to configure default format"); | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Unique Instance */ | 
|  | static const struct ov7725_config ov7725_cfg_0 = { | 
|  | .i2c = I2C_DT_SPEC_INST_GET(0), | 
|  | #if DT_INST_NODE_HAS_PROP(0, reset_gpios) | 
|  | .reset_gpio = GPIO_DT_SPEC_INST_GET(0, reset_gpios), | 
|  | #endif | 
|  | }; | 
|  | static struct ov7725_data ov7725_data_0; | 
|  |  | 
|  | static int ov7725_init_0(const struct device *dev) | 
|  | { | 
|  | const struct ov7725_config *cfg = dev->config; | 
|  |  | 
|  | if (!device_is_ready(cfg->i2c.bus)) { | 
|  | LOG_ERR("Bus device is not ready"); | 
|  | return -ENODEV; | 
|  | } | 
|  |  | 
|  | #if DT_INST_NODE_HAS_PROP(0, reset_gpios) | 
|  | 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; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | return ov7725_init(dev); | 
|  | } | 
|  |  | 
|  | DEVICE_DT_INST_DEFINE(0, &ov7725_init_0, NULL, | 
|  | &ov7725_data_0, &ov7725_cfg_0, | 
|  | POST_KERNEL, CONFIG_VIDEO_INIT_PRIORITY, | 
|  | &ov7725_driver_api); |