From 863b43be469571904c1885a336692e2aad5c5376 Mon Sep 17 00:00:00 2001 From: Farah Fliss Date: Tue, 16 Jul 2024 11:24:06 +0200 Subject: [PATCH] drivers: video: ov5640: Add some controls Add some controls: - hue - saturation - brightness - contrast - gain - hflip - vflip - power line frequency filter - pixel rate (read-only) which is needed for changing frame rate Signed-off-by: Farah Fliss Signed-off-by: Phi Bang Nguyen --- drivers/video/ov5640.c | 220 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 213 insertions(+), 7 deletions(-) diff --git a/drivers/video/ov5640.c b/drivers/video/ov5640.c index 6394d48d3795f1..4d5eec5eeeb2d7 100644 --- a/drivers/video/ov5640.c +++ b/drivers/video/ov5640.c @@ -7,6 +7,8 @@ #define DT_DRV_COMPAT ovti_ov5640 #include +#include +#include #include #include #include @@ -33,6 +35,7 @@ LOG_MODULE_REGISTER(video_ov5640, CONFIG_VIDEO_LOG_LEVEL); #define SCCB_SYS_CTRL1_REG 0x3103 #define TIMING_TC_REG20_REG 0x3820 #define TIMING_TC_REG21_REG 0x3821 +#define HZ5060_CTRL00_REG 0x3c00 #define HZ5060_CTRL01_REG 0x3c01 #define ISP_CTRL01_REG 0x5001 #define PRE_ISP_TEST_SET1 0x503d @@ -44,13 +47,15 @@ LOG_MODULE_REGISTER(video_ov5640, CONFIG_VIDEO_LOG_LEVEL); #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 AEC_PK_REAL_GAIN 0x350a +#define AEC_PK_MANUAL 0x3503 +#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 @@ -72,14 +77,22 @@ LOG_MODULE_REGISTER(video_ov5640, CONFIG_VIDEO_LOG_LEVEL); #define AWB_CTRL30_REG 0x519e #define SDE_CTRL0_REG 0x5580 +#define SDE_CTRL1_REG 0x5581 +#define SDE_CTRL2_REG 0x5582 #define SDE_CTRL3_REG 0x5583 #define SDE_CTRL4_REG 0x5584 +#define SDE_CTRL5_REG 0x5585 +#define SDE_CTRL6_REG 0x5586 +#define SDE_CTRL7_REG 0x5587 +#define SDE_CTRL8_REG 0x5588 #define SDE_CTRL9_REG 0x5589 #define SDE_CTRL10_REG 0x558a #define SDE_CTRL11_REG 0x558b #define DEFAULT_MIPI_CHANNEL 0 +#define PI 3.141592654 + struct ov5640_config { struct i2c_dt_spec i2c; struct gpio_dt_spec reset_gpio; @@ -88,6 +101,7 @@ struct ov5640_config { struct ov5640_data { struct video_format fmt; + uint64_t cur_pixrate; }; struct ov5640_reg { @@ -587,14 +601,205 @@ static int ov5640_set_ctrl_test_pattern(const struct device *dev, int value) { const struct ov5640_config *cfg = dev->config; + if (!IN_RANGE(value, 0, ARRAY_SIZE(test_pattern_val) - 1)) { + return -EINVAL; + } + return ov5640_write_reg(&cfg->i2c, PRE_ISP_TEST_SET1, test_pattern_val[value]); } +static int ov5640_set_ctrl_hue(const struct device *dev, int value) +{ + const struct ov5640_config *cfg = dev->config; + int cos_coef, sin_coef, sign = 0; + + if (!IN_RANGE(value, 0, 360)) { + return -EINVAL; + } + + double rad_val = value; + int ret = ov5640_modify_reg(&cfg->i2c, SDE_CTRL0_REG, BIT(0), BIT(0)); + + if (ret) { + return ret; + } + + rad_val = value * PI / 180.0; + cos_coef = round(cos(rad_val) * 128); + sin_coef = round(sin(rad_val) * 128); + + if (0 <= value && value < 90) { + sign = 0x01; + } else if (90 <= value && value < 180) { + sign = 0x31; + } else if (180 <= value && value < 270) { + sign = 0x32; + } else if (270 <= value && value < 360) { + sign = 0x02; + } + + struct ov5640_reg hue_params[] = {{SDE_CTRL8_REG, sign}, + {SDE_CTRL1_REG, abs(cos_coef)}, + {SDE_CTRL2_REG, abs(sin_coef)}}; + + return ov5640_write_multi_regs(&cfg->i2c, hue_params, ARRAY_SIZE(hue_params)); +} + +static int ov5640_set_ctrl_saturation(const struct device *dev, int value) +{ + const struct ov5640_config *cfg = dev->config; + + if (!IN_RANGE(value, 0, UINT8_MAX)) { + return -EINVAL; + } + + struct ov5640_reg saturation_params[] = {{SDE_CTRL3_REG, value}, {SDE_CTRL4_REG, value}}; + int ret = ov5640_modify_reg(&cfg->i2c, SDE_CTRL8_REG, BIT(6) | BIT(0), BIT(6) | BIT(0)); + + if (ret) { + return ret; + } + + return ov5640_write_multi_regs(&cfg->i2c, saturation_params, ARRAY_SIZE(saturation_params)); +} + +static int ov5640_set_ctrl_brightness(const struct device *dev, int value) +{ + const struct ov5640_config *cfg = dev->config; + + if (!IN_RANGE(value, -UINT8_MAX, UINT8_MAX)) { + return -EINVAL; + } + + struct ov5640_reg brightness_params[] = {{SDE_CTRL8_REG, value >= 0 ? 0x01 : 0x09}, + {SDE_CTRL7_REG, abs(value) & 0xff}}; + int ret = ov5640_modify_reg(&cfg->i2c, SDE_CTRL0_REG, BIT(2), BIT(2)); + + if (ret) { + return ret; + } + + return ov5640_write_multi_regs(&cfg->i2c, brightness_params, ARRAY_SIZE(brightness_params)); +} + +static int ov5640_set_ctrl_contrast(const struct device *dev, int value) +{ + const struct ov5640_config *cfg = dev->config; + + if (!IN_RANGE(value, 0, UINT8_MAX)) { + return -EINVAL; + } + + int ret = ov5640_modify_reg(&cfg->i2c, SDE_CTRL0_REG, BIT(2), BIT(2)); + + if (ret) { + return ret; + } + + return ov5640_write_reg(&cfg->i2c, SDE_CTRL6_REG, value & 0xff); +} + +static int ov5640_set_ctrl_gain(const struct device *dev, int value) +{ + const struct ov5640_config *cfg = dev->config; + + if (!IN_RANGE(value, 0, UINT16_MAX)) { + return -EINVAL; + } + + if (value) { + int ret = ov5640_modify_reg(&cfg->i2c, AEC_PK_MANUAL, BIT(1), BIT(0)); + + if (ret) { + return ret; + } + + struct ov5640_reg gain_params[] = {{AEC_PK_REAL_GAIN, value >> 8}, + {AEC_PK_REAL_GAIN + 1, value & 0xff}}; + + return ov5640_write_multi_regs(&cfg->i2c, gain_params, ARRAY_SIZE(gain_params)); + } else { + return ov5640_write_reg(&cfg->i2c, AEC_PK_MANUAL, 0); + } +} + +static int ov5640_set_ctrl_hflip(const struct device *dev, int value) +{ + const struct ov5640_config *cfg = dev->config; + + return ov5640_modify_reg(&cfg->i2c, TIMING_TC_REG21_REG, BIT(2) | BIT(1), + value ? 0 : BIT(2) | BIT(1)); +} + +static int ov5640_set_ctrl_vflip(const struct device *dev, int value) +{ + const struct ov5640_config *cfg = dev->config; + + return ov5640_modify_reg(&cfg->i2c, TIMING_TC_REG20_REG, BIT(2) | BIT(1), + value ? BIT(2) | BIT(1) : 0); +} + +static int ov5640_set_ctrl_power_line_freq(const struct device *dev, int value) +{ + const struct ov5640_config *cfg = dev->config; + int ret; + + switch (value) { + case VIDEO_CID_POWER_LINE_FREQUENCY_AUTO: + ret = ov5640_modify_reg(&cfg->i2c, HZ5060_CTRL01_REG, BIT(7), 0); + return ret; + case VIDEO_CID_POWER_LINE_FREQUENCY_50HZ: + ret = ov5640_modify_reg(&cfg->i2c, HZ5060_CTRL00_REG, BIT(2), BIT(2)); + break; + case VIDEO_CID_POWER_LINE_FREQUENCY_60HZ: + ret = ov5640_modify_reg(&cfg->i2c, HZ5060_CTRL00_REG, BIT(2), 0); + break; + default: + return -EINVAL; + } + + if (ret) { + return ret; + } + + return ov5640_modify_reg(&cfg->i2c, HZ5060_CTRL01_REG, BIT(7), BIT(7)); +} + static int ov5640_set_ctrl(const struct device *dev, unsigned int cid, void *value) { switch (cid) { case VIDEO_CID_CAMERA_TEST_PATTERN: return ov5640_set_ctrl_test_pattern(dev, (int)value); + case VIDEO_CID_CAMERA_HUE: + return ov5640_set_ctrl_hue(dev, (int)value); + case VIDEO_CID_CAMERA_SATURATION: + return ov5640_set_ctrl_saturation(dev, (int)(value)); + case VIDEO_CID_CAMERA_BRIGHTNESS: + return ov5640_set_ctrl_brightness(dev, (int)(value)); + case VIDEO_CID_CAMERA_CONTRAST: + return ov5640_set_ctrl_contrast(dev, (int)value); + case VIDEO_CID_CAMERA_GAIN: + return ov5640_set_ctrl_gain(dev, (int)(value)); + case VIDEO_CID_HFLIP: + return ov5640_set_ctrl_hflip(dev, (int)(value)); + case VIDEO_CID_VFLIP: + return ov5640_set_ctrl_vflip(dev, (int)(value)); + case VIDEO_CID_POWER_LINE_FREQUENCY: + return ov5640_set_ctrl_power_line_freq(dev, (int)(value)); + default: + return -ENOTSUP; + } +} + +static inline int ov5640_get_ctrl(const struct device *dev, unsigned int cid, void *value) +{ + struct ov5640_data *drv_data = dev->data; + + switch (cid) { + case VIDEO_CID_PIXEL_RATE: + *((uint64_t *)value) = drv_data->cur_pixrate; + + return 0; default: return -ENOTSUP; } @@ -607,6 +812,7 @@ static const struct video_driver_api ov5640_driver_api = { .stream_start = ov5640_stream_start, .stream_stop = ov5640_stream_stop, .set_ctrl = ov5640_set_ctrl, + .get_ctrl = ov5640_get_ctrl, }; static int ov5640_init(const struct device *dev)