648 lines
17 KiB
C
648 lines
17 KiB
C
/*
|
|
* 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_stream_start(const struct device *dev)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int ov7725_stream_stop(const struct device *dev)
|
|
{
|
|
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 const struct video_driver_api ov7725_driver_api = {
|
|
.set_format = ov7725_set_fmt,
|
|
.get_format = ov7725_get_fmt,
|
|
.get_caps = ov7725_get_caps,
|
|
.stream_start = ov7725_stream_start,
|
|
.stream_stop = ov7725_stream_stop,
|
|
};
|
|
|
|
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);
|