xiaoxiang781216 commented on code in PR #18542: URL: https://github.com/apache/nuttx/pull/18542#discussion_r2939997149
########## drivers/video/gc0308.c: ########## @@ -0,0 +1,861 @@ +/**************************************************************************** + * drivers/video/gc0308.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <stdint.h> +#include <string.h> +#include <errno.h> +#include <debug.h> + +#include <nuttx/i2c/i2c_master.h> +#include <nuttx/video/imgsensor.h> +#include <nuttx/arch.h> +#include <nuttx/video/video.h> + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define GC0308_I2C_ADDR 0x21 +#define GC0308_I2C_FREQ 100000 + +/* GC0308 Register Addresses */ + +#define GC0308_REG_CHIP_ID 0x00 +#define GC0308_CHIP_ID_VAL 0x9b + +/* Page 0 registers */ + +#define GC0308_REG_RESET 0xfe /* Page/reset register */ +#define GC0308_REG_HBLANK_H 0x01 +#define GC0308_REG_HBLANK_L 0x02 +#define GC0308_REG_VBLANK_H 0x03 +#define GC0308_REG_VBLANK_L 0x04 +#define GC0308_REG_SH_DELAY 0x05 +#define GC0308_REG_ROW_START_H 0x06 +#define GC0308_REG_ROW_START_L 0x07 +#define GC0308_REG_COL_START_H 0x08 +#define GC0308_REG_COL_START_L 0x09 +#define GC0308_REG_WIN_HEIGHT_H 0x0a +#define GC0308_REG_WIN_HEIGHT_L 0x0b +#define GC0308_REG_WIN_WIDTH_H 0x0c +#define GC0308_REG_WIN_WIDTH_L 0x0d +#define GC0308_REG_VS_ST 0x0e +#define GC0308_REG_VS_ET 0x0f +#define GC0308_REG_VB_HB 0x10 +#define GC0308_REG_RSH_WIDTH 0x11 +#define GC0308_REG_TSP_WIDTH 0x12 +#define GC0308_REG_SAMPLE_HOLD 0x13 +#define GC0308_REG_CISCTL_MODE1 0x14 +#define GC0308_REG_CISCTL_MODE2 0x15 +#define GC0308_REG_CISCTL_MODE3 0x16 +#define GC0308_REG_CISCTL_MODE4 0x17 + +/* Output format (Page 0) */ + +#define GC0308_REG_OUTPUT_FMT 0x24 /* Bits[3:0]: output format select */ +#define GC0308_REG_OUT_FORMAT 0x44 +#define GC0308_REG_OUT_EN 0x45 +#define GC0308_REG_SYNC_MODE 0x46 + +/* Analog & bias */ + +#define GC0308_REG_ANALOG_MODE1 0x1a +#define GC0308_REG_ANALOG_MODE2 0x1b + +/* Exposure */ + +#define GC0308_REG_EXP_H 0x03 +#define GC0308_REG_EXP_L 0x04 + +/* Gain */ + +#define GC0308_REG_GLOBAL_GAIN 0x50 + +/* Crop */ + +#define GC0308_REG_CROP_WIN_MODE 0x46 +#define GC0308_REG_CROP_Y1_H 0x47 +#define GC0308_REG_CROP_Y1_L 0x48 +#define GC0308_REG_CROP_X1_H 0x49 +#define GC0308_REG_CROP_X1_L 0x4a +#define GC0308_REG_CROP_WIN_H_H 0x4b +#define GC0308_REG_CROP_WIN_H_L 0x4c +#define GC0308_REG_CROP_WIN_W_H 0x4d +#define GC0308_REG_CROP_WIN_W_L 0x4e + +/* Subsample (Page 0) */ + +#define GC0308_REG_SUBSAMPLE 0x59 +#define GC0308_REG_SUB_MODE 0x5a +#define GC0308_REG_SUB_ROW_N1 0x5b +#define GC0308_REG_SUB_ROW_N2 0x5c +#define GC0308_REG_SUB_COL_N1 0x5d +#define GC0308_REG_SUB_COL_N2 0x5e + +/* Subsample control (Page 1) */ + +#define GC0308_P1_REG_SUB_CTRL 0x53 /* Bit7: subsample enable */ +#define GC0308_P1_REG_SUB_RATIO 0x54 /* H[7:4] V[3:0] ratio */ +#define GC0308_P1_REG_SUB_EN 0x55 /* Bit0: subsample output enable */ +#define GC0308_P1_REG_SUB_HOFF 0x56 /* H offset */ +#define GC0308_P1_REG_SUB_VOFF 0x57 /* V offset */ +#define GC0308_P1_REG_SUB_HSIZE 0x58 /* H size adjust */ +#define GC0308_P1_REG_SUB_VSIZE 0x59 /* V size adjust */ + +/* Configured resolution */ + +#ifdef CONFIG_VIDEO_GC0308_VGA +# define GC0308_WIDTH 640 +# define GC0308_HEIGHT 480 +#elif defined(CONFIG_VIDEO_GC0308_QVGA) +# define GC0308_WIDTH 320 +# define GC0308_HEIGHT 240 +#elif defined(CONFIG_VIDEO_GC0308_QQVGA) +# define GC0308_WIDTH 160 +# define GC0308_HEIGHT 120 +#else +# error "No GC0308 resolution selected" +#endif + +/* Mirror/flip */ + +#define GC0308_REG_CISCTL_MODE1_MIRROR 0x14 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct gc0308_reg_s +{ + uint8_t addr; + uint8_t val; +}; + +struct gc0308_dev_s +{ + struct imgsensor_s sensor; + struct i2c_master_s *i2c; + bool streaming; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static bool gc0308_is_available(struct imgsensor_s *sensor); +static int gc0308_init(struct imgsensor_s *sensor); +static int gc0308_uninit(struct imgsensor_s *sensor); +static const char *gc0308_get_driver_name(struct imgsensor_s *sensor); +static int gc0308_validate_frame_setting(struct imgsensor_s *sensor, + imgsensor_stream_type_t type, + uint8_t nr_datafmts, + imgsensor_format_t *datafmts, + imgsensor_interval_t *interval); +static int gc0308_start_capture(struct imgsensor_s *sensor, + imgsensor_stream_type_t type, + uint8_t nr_datafmts, + imgsensor_format_t *datafmts, + imgsensor_interval_t *interval); +static int gc0308_stop_capture(struct imgsensor_s *sensor, + imgsensor_stream_type_t type); +static int gc0308_get_supported_value(struct imgsensor_s *sensor, + uint32_t id, + imgsensor_supported_value_t *value); +static int gc0308_get_value(struct imgsensor_s *sensor, + uint32_t id, uint32_t size, imgsensor_value_t *value); +static int gc0308_set_value(struct imgsensor_s *sensor, + uint32_t id, uint32_t size, imgsensor_value_t value); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* GC0308 initialization register table from espressif/esp32-camera vendor + * driver. This is the proven register set for GC0308 VGA output. + */ + +static const struct gc0308_reg_s g_gc0308_init_regs[] = +{ + { 0xfe, 0x00 }, + { 0xec, 0x20 }, + { 0x05, 0x00 }, + { 0x06, 0x00 }, + { 0x07, 0x00 }, + { 0x08, 0x00 }, + { 0x09, 0x01 }, + { 0x0a, 0xe8 }, + { 0x0b, 0x02 }, + { 0x0c, 0x88 }, + { 0x0d, 0x02 }, + { 0x0e, 0x02 }, + { 0x10, 0x26 }, + { 0x11, 0x0d }, + { 0x12, 0x2a }, + { 0x13, 0x00 }, + { 0x14, 0x10 }, + { 0x15, 0x0a }, + { 0x16, 0x05 }, + { 0x17, 0x01 }, + { 0x18, 0x44 }, + { 0x19, 0x44 }, + { 0x1a, 0x2a }, + { 0x1b, 0x00 }, + { 0x1c, 0x49 }, + { 0x1d, 0x9a }, + { 0x1e, 0x61 }, + { 0x1f, 0x00 }, + { 0x20, 0x7f }, + { 0x21, 0xfa }, + { 0x22, 0x57 }, + { 0x24, 0xa2 }, /* YCbYCr output format */ + { 0x25, 0x0f }, + { 0x26, 0x03 }, + { 0x28, 0x00 }, + { 0x2d, 0x0a }, + { 0x2f, 0x01 }, + { 0x30, 0xf7 }, + { 0x31, 0x50 }, + { 0x32, 0x00 }, + { 0x33, 0x28 }, + { 0x34, 0x2a }, + { 0x35, 0x28 }, + { 0x39, 0x04 }, + { 0x3a, 0x20 }, + { 0x3b, 0x20 }, + { 0x3c, 0x00 }, + { 0x3d, 0x00 }, + { 0x3e, 0x00 }, + { 0x3f, 0x00 }, + { 0x50, 0x14 }, /* Global gain */ + { 0x52, 0x41 }, + { 0x53, 0x80 }, + { 0x54, 0x80 }, + { 0x55, 0x80 }, + { 0x56, 0x80 }, + { 0x5a, 0x56 }, /* AWB R gain */ + { 0x5b, 0x40 }, /* AWB G gain */ + { 0x5c, 0x4a }, /* AWB B gain */ + { 0x8b, 0x20 }, + { 0x8c, 0x20 }, + { 0x8d, 0x20 }, + { 0x8e, 0x14 }, + { 0x8f, 0x10 }, + { 0x90, 0x14 }, + { 0x91, 0x3c }, + { 0x92, 0x50 }, + { 0x5d, 0x12 }, + { 0x5e, 0x1a }, + { 0x5f, 0x24 }, + { 0x60, 0x07 }, + { 0x61, 0x15 }, + { 0x62, 0x08 }, + { 0x64, 0x03 }, + { 0x66, 0xe8 }, + { 0x67, 0x86 }, + { 0x68, 0x82 }, + { 0x69, 0x18 }, + { 0x6a, 0x0f }, + { 0x6b, 0x00 }, + { 0x6c, 0x5f }, + { 0x6d, 0x8f }, + { 0x6e, 0x55 }, + { 0x6f, 0x38 }, + { 0x70, 0x15 }, + { 0x71, 0x33 }, + { 0x72, 0xdc }, + { 0x73, 0x00 }, + { 0x74, 0x02 }, + { 0x75, 0x3f }, + { 0x76, 0x02 }, + { 0x77, 0x38 }, + { 0x78, 0x88 }, + { 0x79, 0x81 }, + { 0x7a, 0x81 }, + { 0x7b, 0x22 }, + { 0x7c, 0xff }, + { 0x93, 0x48 }, /* Color matrix */ + { 0x94, 0x02 }, + { 0x95, 0x07 }, + { 0x96, 0xe0 }, + { 0x97, 0x40 }, + { 0x98, 0xf0 }, + { 0xb1, 0x40 }, /* Saturation */ + { 0xb2, 0x40 }, + { 0xb3, 0x40 }, + { 0xb6, 0xe0 }, + { 0xbd, 0x38 }, + { 0xbe, 0x36 }, + { 0xd0, 0xcb }, /* AEC */ + { 0xd1, 0x10 }, + { 0xd2, 0x90 }, + { 0xd3, 0x48 }, + { 0xd5, 0xf2 }, + { 0xd6, 0x16 }, + { 0xdb, 0x92 }, + { 0xdc, 0xa5 }, + { 0xdf, 0x23 }, + { 0xd9, 0x00 }, + { 0xda, 0x00 }, + { 0xe0, 0x09 }, + { 0xed, 0x04 }, + { 0xee, 0xa0 }, + { 0xef, 0x40 }, + { 0x80, 0x03 }, + { 0x9f, 0x10 }, + { 0xa0, 0x20 }, + { 0xa1, 0x38 }, + { 0xa2, 0x4e }, + { 0xa3, 0x63 }, + { 0xa4, 0x76 }, + { 0xa5, 0x87 }, + { 0xa6, 0xa2 }, + { 0xa7, 0xb8 }, + { 0xa8, 0xca }, + { 0xa9, 0xd8 }, + { 0xaa, 0xe3 }, + { 0xab, 0xeb }, + { 0xac, 0xf0 }, + { 0xad, 0xf8 }, + { 0xae, 0xfd }, + { 0xaf, 0xff }, + { 0xc0, 0x00 }, + { 0xc1, 0x10 }, + { 0xc2, 0x1c }, + { 0xc3, 0x30 }, + { 0xc4, 0x43 }, + { 0xc5, 0x54 }, + { 0xc6, 0x65 }, + { 0xc7, 0x75 }, + { 0xc8, 0x93 }, + { 0xc9, 0xb0 }, + { 0xca, 0xcb }, + { 0xcb, 0xe6 }, + { 0xcc, 0xff }, + { 0xf0, 0x02 }, + { 0xf1, 0x01 }, + { 0xf2, 0x02 }, + { 0xf3, 0x30 }, + { 0xf7, 0x04 }, + { 0xf8, 0x02 }, + { 0xf9, 0x9f }, + { 0xfa, 0x78 }, + { 0xfe, 0x01 }, + { 0x00, 0xf5 }, + { 0x02, 0x20 }, + { 0x04, 0x10 }, + { 0x05, 0x08 }, + { 0x06, 0x20 }, + { 0x08, 0x0a }, + { 0x0a, 0xa0 }, + { 0x0b, 0x60 }, + { 0x0c, 0x08 }, + { 0x0e, 0x44 }, + { 0x0f, 0x32 }, + { 0x10, 0x41 }, + { 0x11, 0x37 }, + { 0x12, 0x22 }, + { 0x13, 0x19 }, + { 0x14, 0x44 }, + { 0x15, 0x44 }, + { 0x16, 0xc2 }, + { 0x17, 0xa8 }, + { 0x18, 0x18 }, + { 0x19, 0x50 }, + { 0x1a, 0xd8 }, + { 0x1b, 0xf5 }, + { 0x70, 0x40 }, + { 0x71, 0x58 }, + { 0x72, 0x30 }, + { 0x73, 0x48 }, + { 0x74, 0x20 }, + { 0x75, 0x60 }, + { 0x77, 0x20 }, + { 0x78, 0x32 }, + { 0x30, 0x03 }, + { 0x31, 0x40 }, + { 0x32, 0x10 }, + { 0x33, 0xe0 }, + { 0x34, 0xe0 }, + { 0x35, 0x00 }, + { 0x36, 0x80 }, + { 0x37, 0x00 }, + { 0x38, 0x04 }, + { 0x39, 0x09 }, + { 0x3a, 0x12 }, + { 0x3b, 0x1c }, + { 0x3c, 0x28 }, + { 0x3d, 0x31 }, + { 0x3e, 0x44 }, + { 0x3f, 0x57 }, + { 0x40, 0x6c }, + { 0x41, 0x81 }, + { 0x42, 0x94 }, + { 0x43, 0xa7 }, + { 0x44, 0xb8 }, + { 0x45, 0xd6 }, + { 0x46, 0xee }, + { 0x47, 0x0d }, + { 0x62, 0xf7 }, + { 0x63, 0x68 }, + { 0x64, 0xd3 }, + { 0x65, 0xd3 }, + { 0x66, 0x60 }, + { 0xfe, 0x00 }, + { 0x01, 0x32 }, + { 0x02, 0x0c }, + { 0x0f, 0x01 }, + { 0xe2, 0x00 }, + { 0xe3, 0x78 }, + { 0xe4, 0x00 }, + { 0xe5, 0xfe }, + { 0xe6, 0x01 }, + { 0xe7, 0xe0 }, + { 0xe8, 0x01 }, + { 0xe9, 0xe0 }, + { 0xea, 0x01 }, + { 0xeb, 0xe0 }, + { 0xfe, 0x00 }, +}; + +static const struct imgsensor_ops_s g_gc0308_ops = +{ + .is_available = gc0308_is_available, + .init = gc0308_init, + .uninit = gc0308_uninit, + .get_driver_name = gc0308_get_driver_name, + .validate_frame_setting = gc0308_validate_frame_setting, + .start_capture = gc0308_start_capture, + .stop_capture = gc0308_stop_capture, + .get_supported_value = gc0308_get_supported_value, + .get_value = gc0308_get_value, + .set_value = gc0308_set_value, +}; + +static const struct v4l2_fmtdesc g_gc0308_fmtdescs[] = +{ + { + .pixelformat = V4L2_PIX_FMT_RGB565, + .description = "RGB565", + }, +}; + +static const struct v4l2_frmsizeenum g_gc0308_frmsizes[] = +{ + { + .type = V4L2_FRMSIZE_TYPE_DISCRETE, + .discrete = + { + .width = GC0308_WIDTH, + .height = GC0308_HEIGHT, + }, + }, +}; + +static const struct v4l2_frmivalenum g_gc0308_frmintervals[] = +{ + { + .type = V4L2_FRMIVAL_TYPE_DISCRETE, + .discrete = + { + .numerator = 1, + .denominator = 30, + }, + }, +}; + +static struct gc0308_dev_s g_gc0308_priv; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: gc0308_putreg + ****************************************************************************/ + +static int gc0308_putreg(struct i2c_master_s *i2c, + uint8_t regaddr, uint8_t regval) +{ + struct i2c_msg_s msg; + uint8_t buf[2]; + int ret; + + buf[0] = regaddr; + buf[1] = regval; + + msg.frequency = GC0308_I2C_FREQ; + msg.addr = GC0308_I2C_ADDR; + msg.flags = 0; + msg.buffer = buf; + msg.length = 2; + + ret = I2C_TRANSFER(i2c, &msg, 1); + if (ret < 0) + { + snerr("ERROR: I2C write to 0x%02x failed: %d\n", regaddr, ret); + } + + return ret; +} + +/**************************************************************************** + * Name: gc0308_getreg + ****************************************************************************/ + +static int gc0308_getreg(struct i2c_master_s *i2c, + uint8_t regaddr, uint8_t *regval) +{ + struct i2c_msg_s msg[2]; + int ret; + + msg[0].frequency = GC0308_I2C_FREQ; + msg[0].addr = GC0308_I2C_ADDR; + msg[0].flags = 0; + msg[0].buffer = ®addr; + msg[0].length = 1; + + msg[1].frequency = GC0308_I2C_FREQ; + msg[1].addr = GC0308_I2C_ADDR; + msg[1].flags = I2C_M_READ; + msg[1].buffer = regval; + msg[1].length = 1; + + ret = I2C_TRANSFER(i2c, msg, 2); + if (ret < 0) + { + snerr("ERROR: I2C read from 0x%02x failed: %d\n", regaddr, ret); + } + + return ret; +} + +/**************************************************************************** + * Name: gc0308_putreglist + ****************************************************************************/ + +static int gc0308_putreglist(struct i2c_master_s *i2c, + const struct gc0308_reg_s *reglist, + size_t nentries) +{ + int ret; + + for (size_t i = 0; i < nentries; i++) + { + ret = gc0308_putreg(i2c, reglist[i].addr, reglist[i].val); + if (ret < 0) + { + snerr("GC0308 write[%d] 0x%02x=0x%02x FAILED: %d\n", + (int)i, reglist[i].addr, reglist[i].val, ret); + return ret; + } + } + + return OK; +} + +/**************************************************************************** + * Name: gc0308_is_available + ****************************************************************************/ + +static bool gc0308_is_available(struct imgsensor_s *sensor) +{ + struct gc0308_dev_s *priv = (struct gc0308_dev_s *)sensor; + uint8_t id = 0; + int ret; + + /* Select page 0 */ + + gc0308_putreg(priv->i2c, GC0308_REG_RESET, 0x00); + + ret = gc0308_getreg(priv->i2c, GC0308_REG_CHIP_ID, &id); + if (ret < 0) + { + return false; + } + + sninfo("GC0308 chip ID: 0x%02x (expected 0x%02x)\n", + id, GC0308_CHIP_ID_VAL); + + return (id == GC0308_CHIP_ID_VAL); +} + +/**************************************************************************** + * Name: gc0308_init + ****************************************************************************/ + +static int gc0308_init(struct imgsensor_s *sensor) +{ + struct gc0308_dev_s *priv = (struct gc0308_dev_s *)sensor; + int ret; + + /* Software reset per vendor: write 0xf0 to reg 0xfe, wait 80ms */ + + ret = gc0308_putreg(priv->i2c, 0xfe, 0xf0); + if (ret < 0) + { + snerr("GC0308 soft reset failed: %d\n", ret); + return ret; + } + + up_mdelay(80); + + /* Write vendor initialization register table */ + + ret = gc0308_putreglist(priv->i2c, g_gc0308_init_regs, + sizeof(g_gc0308_init_regs) / + sizeof(g_gc0308_init_regs[0])); + if (ret < 0) + { + snerr("GC0308 init regs failed: %d\n", ret); + return ret; + } + + up_mdelay(80); + + /* Set RGB565 output format: reg 0x24 bits[3:0] = 6 */ + + ret = gc0308_putreg(priv->i2c, GC0308_REG_RESET, 0x00); + if (ret < 0) + { + return ret; + } + + uint8_t fmt = 0; + gc0308_getreg(priv->i2c, GC0308_REG_OUTPUT_FMT, &fmt); + fmt = (fmt & 0xf0) | 0x06; /* RGB565 */ + ret = gc0308_putreg(priv->i2c, GC0308_REG_OUTPUT_FMT, fmt); + if (ret < 0) + { + return ret; + } + +#ifndef CONFIG_VIDEO_GC0308_VGA + /* Non-VGA resolutions need subsample — switch to page 1 */ + + ret = gc0308_putreg(priv->i2c, GC0308_REG_RESET, 0x01); + if (ret < 0) + { + return ret; + } + + uint8_t val = 0; + gc0308_getreg(priv->i2c, GC0308_P1_REG_SUB_CTRL, &val); + ret = gc0308_putreg(priv->i2c, GC0308_P1_REG_SUB_CTRL, val | 0x80); + gc0308_getreg(priv->i2c, GC0308_P1_REG_SUB_EN, &val); + ret |= gc0308_putreg(priv->i2c, GC0308_P1_REG_SUB_EN, val | 0x01); + +# if defined(CONFIG_VIDEO_GC0308_QVGA) + ret |= gc0308_putreg(priv->i2c, GC0308_P1_REG_SUB_RATIO, 0x22); +# elif defined(CONFIG_VIDEO_GC0308_QQVGA) + ret |= gc0308_putreg(priv->i2c, GC0308_P1_REG_SUB_RATIO, 0x44); +# endif + + ret |= gc0308_putreg(priv->i2c, GC0308_P1_REG_SUB_HOFF, 0x00); + ret |= gc0308_putreg(priv->i2c, GC0308_P1_REG_SUB_VOFF, 0x00); + ret |= gc0308_putreg(priv->i2c, GC0308_P1_REG_SUB_HSIZE, 0x00); + ret |= gc0308_putreg(priv->i2c, GC0308_P1_REG_SUB_VSIZE, 0x00); + + /* Back to page 0 */ + + ret |= gc0308_putreg(priv->i2c, GC0308_REG_RESET, 0x00); +#endif + + /* Debug: verify key registers */ + + uint8_t id = 0; + gc0308_getreg(priv->i2c, 0x00, &id); + gc0308_getreg(priv->i2c, 0x24, &fmt); + syslog(LOG_INFO, "GC0308 init done: id=0x%02x fmt=0x%02x ret=%d\n", + id, fmt, ret); + + return ret; +} + +/**************************************************************************** + * Name: gc0308_uninit + ****************************************************************************/ + +static int gc0308_uninit(struct imgsensor_s *sensor) +{ + struct gc0308_dev_s *priv = (struct gc0308_dev_s *)sensor; + + /* Soft reset per vendor */ + + gc0308_putreg(priv->i2c, GC0308_REG_RESET, 0xf0); + priv->streaming = false; + + return OK; +} + +/**************************************************************************** + * Name: gc0308_get_driver_name + ****************************************************************************/ + +static const char *gc0308_get_driver_name(struct imgsensor_s *sensor) +{ + return "GC0308"; +} + +/**************************************************************************** + * Name: gc0308_validate_frame_setting + ****************************************************************************/ + +static int gc0308_validate_frame_setting(struct imgsensor_s *sensor, + imgsensor_stream_type_t type, + uint8_t nr_datafmts, + imgsensor_format_t *datafmts, + imgsensor_interval_t *interval) +{ + if (nr_datafmts < 1 || !datafmts) + { + return -EINVAL; + } + + if (datafmts[IMGSENSOR_FMT_MAIN].pixelformat != + IMGSENSOR_PIX_FMT_RGB565) + { + return -EINVAL; + } + + if (datafmts[IMGSENSOR_FMT_MAIN].width != GC0308_WIDTH || + datafmts[IMGSENSOR_FMT_MAIN].height != GC0308_HEIGHT) + { + return -EINVAL; + } + + return OK; +} + +/**************************************************************************** + * Name: gc0308_start_capture + ****************************************************************************/ + +static int gc0308_start_capture(struct imgsensor_s *sensor, + imgsensor_stream_type_t type, + uint8_t nr_datafmts, + imgsensor_format_t *datafmts, + imgsensor_interval_t *interval) +{ + struct gc0308_dev_s *priv = (struct gc0308_dev_s *)sensor; + + if (priv->streaming) + { + return -EBUSY; + } + + priv->streaming = true; + return OK; +} + +/**************************************************************************** + * Name: gc0308_stop_capture + ****************************************************************************/ + +static int gc0308_stop_capture(struct imgsensor_s *sensor, + imgsensor_stream_type_t type) +{ + struct gc0308_dev_s *priv = (struct gc0308_dev_s *)sensor; + + priv->streaming = false; + return OK; +} + +/**************************************************************************** + * Name: gc0308_get_supported_value + ****************************************************************************/ + +static int gc0308_get_supported_value(struct imgsensor_s *sensor, + uint32_t id, + imgsensor_supported_value_t *value) +{ + return -ENOTTY; +} + +/**************************************************************************** + * Name: gc0308_get_value + ****************************************************************************/ + +static int gc0308_get_value(struct imgsensor_s *sensor, + uint32_t id, uint32_t size, imgsensor_value_t *value) +{ + return -ENOTTY; +} + +/**************************************************************************** + * Name: gc0308_set_value + ****************************************************************************/ + +static int gc0308_set_value(struct imgsensor_s *sensor, + uint32_t id, uint32_t size, imgsensor_value_t value) +{ + return -ENOTTY; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: gc0308_initialize + * + * Description: + * Initialize the GC0308 camera sensor driver. + * + * Input Parameters: + * i2c - I2C bus device + * + * Returned Value: + * Pointer to imgsensor_s on success; NULL on failure. + * + ****************************************************************************/ + +struct imgsensor_s *gc0308_initialize(struct i2c_master_s *i2c) +{ + struct gc0308_dev_s *priv = &g_gc0308_priv; + + if (!i2c) + { + return NULL; + } + + priv->i2c = i2c; + priv->streaming = false; + + priv->sensor.ops = &g_gc0308_ops; + priv->sensor.fmtdescs = g_gc0308_fmtdescs; + priv->sensor.fmtdescs_num = sizeof(g_gc0308_fmtdescs) / Review Comment: all to nitems ########## drivers/video/gc0308.c: ########## @@ -0,0 +1,861 @@ +/**************************************************************************** + * drivers/video/gc0308.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <stdint.h> +#include <string.h> +#include <errno.h> +#include <debug.h> + +#include <nuttx/i2c/i2c_master.h> +#include <nuttx/video/imgsensor.h> +#include <nuttx/arch.h> +#include <nuttx/video/video.h> + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define GC0308_I2C_ADDR 0x21 +#define GC0308_I2C_FREQ 100000 + +/* GC0308 Register Addresses */ + +#define GC0308_REG_CHIP_ID 0x00 +#define GC0308_CHIP_ID_VAL 0x9b + +/* Page 0 registers */ + +#define GC0308_REG_RESET 0xfe /* Page/reset register */ +#define GC0308_REG_HBLANK_H 0x01 +#define GC0308_REG_HBLANK_L 0x02 +#define GC0308_REG_VBLANK_H 0x03 +#define GC0308_REG_VBLANK_L 0x04 +#define GC0308_REG_SH_DELAY 0x05 +#define GC0308_REG_ROW_START_H 0x06 +#define GC0308_REG_ROW_START_L 0x07 +#define GC0308_REG_COL_START_H 0x08 +#define GC0308_REG_COL_START_L 0x09 +#define GC0308_REG_WIN_HEIGHT_H 0x0a +#define GC0308_REG_WIN_HEIGHT_L 0x0b +#define GC0308_REG_WIN_WIDTH_H 0x0c +#define GC0308_REG_WIN_WIDTH_L 0x0d +#define GC0308_REG_VS_ST 0x0e +#define GC0308_REG_VS_ET 0x0f +#define GC0308_REG_VB_HB 0x10 +#define GC0308_REG_RSH_WIDTH 0x11 +#define GC0308_REG_TSP_WIDTH 0x12 +#define GC0308_REG_SAMPLE_HOLD 0x13 +#define GC0308_REG_CISCTL_MODE1 0x14 +#define GC0308_REG_CISCTL_MODE2 0x15 +#define GC0308_REG_CISCTL_MODE3 0x16 +#define GC0308_REG_CISCTL_MODE4 0x17 + +/* Output format (Page 0) */ + +#define GC0308_REG_OUTPUT_FMT 0x24 /* Bits[3:0]: output format select */ +#define GC0308_REG_OUT_FORMAT 0x44 +#define GC0308_REG_OUT_EN 0x45 +#define GC0308_REG_SYNC_MODE 0x46 + +/* Analog & bias */ + +#define GC0308_REG_ANALOG_MODE1 0x1a +#define GC0308_REG_ANALOG_MODE2 0x1b + +/* Exposure */ + +#define GC0308_REG_EXP_H 0x03 +#define GC0308_REG_EXP_L 0x04 + +/* Gain */ + +#define GC0308_REG_GLOBAL_GAIN 0x50 + +/* Crop */ + +#define GC0308_REG_CROP_WIN_MODE 0x46 +#define GC0308_REG_CROP_Y1_H 0x47 +#define GC0308_REG_CROP_Y1_L 0x48 +#define GC0308_REG_CROP_X1_H 0x49 +#define GC0308_REG_CROP_X1_L 0x4a +#define GC0308_REG_CROP_WIN_H_H 0x4b +#define GC0308_REG_CROP_WIN_H_L 0x4c +#define GC0308_REG_CROP_WIN_W_H 0x4d +#define GC0308_REG_CROP_WIN_W_L 0x4e + +/* Subsample (Page 0) */ + +#define GC0308_REG_SUBSAMPLE 0x59 +#define GC0308_REG_SUB_MODE 0x5a +#define GC0308_REG_SUB_ROW_N1 0x5b +#define GC0308_REG_SUB_ROW_N2 0x5c +#define GC0308_REG_SUB_COL_N1 0x5d +#define GC0308_REG_SUB_COL_N2 0x5e + +/* Subsample control (Page 1) */ + +#define GC0308_P1_REG_SUB_CTRL 0x53 /* Bit7: subsample enable */ +#define GC0308_P1_REG_SUB_RATIO 0x54 /* H[7:4] V[3:0] ratio */ +#define GC0308_P1_REG_SUB_EN 0x55 /* Bit0: subsample output enable */ +#define GC0308_P1_REG_SUB_HOFF 0x56 /* H offset */ +#define GC0308_P1_REG_SUB_VOFF 0x57 /* V offset */ +#define GC0308_P1_REG_SUB_HSIZE 0x58 /* H size adjust */ +#define GC0308_P1_REG_SUB_VSIZE 0x59 /* V size adjust */ + +/* Configured resolution */ + +#ifdef CONFIG_VIDEO_GC0308_VGA +# define GC0308_WIDTH 640 +# define GC0308_HEIGHT 480 +#elif defined(CONFIG_VIDEO_GC0308_QVGA) +# define GC0308_WIDTH 320 +# define GC0308_HEIGHT 240 +#elif defined(CONFIG_VIDEO_GC0308_QQVGA) +# define GC0308_WIDTH 160 +# define GC0308_HEIGHT 120 +#else +# error "No GC0308 resolution selected" +#endif + +/* Mirror/flip */ + +#define GC0308_REG_CISCTL_MODE1_MIRROR 0x14 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct gc0308_reg_s +{ + uint8_t addr; + uint8_t val; +}; + +struct gc0308_dev_s +{ + struct imgsensor_s sensor; + struct i2c_master_s *i2c; + bool streaming; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static bool gc0308_is_available(struct imgsensor_s *sensor); +static int gc0308_init(struct imgsensor_s *sensor); +static int gc0308_uninit(struct imgsensor_s *sensor); +static const char *gc0308_get_driver_name(struct imgsensor_s *sensor); +static int gc0308_validate_frame_setting(struct imgsensor_s *sensor, + imgsensor_stream_type_t type, + uint8_t nr_datafmts, + imgsensor_format_t *datafmts, + imgsensor_interval_t *interval); +static int gc0308_start_capture(struct imgsensor_s *sensor, + imgsensor_stream_type_t type, + uint8_t nr_datafmts, + imgsensor_format_t *datafmts, + imgsensor_interval_t *interval); +static int gc0308_stop_capture(struct imgsensor_s *sensor, + imgsensor_stream_type_t type); +static int gc0308_get_supported_value(struct imgsensor_s *sensor, + uint32_t id, + imgsensor_supported_value_t *value); +static int gc0308_get_value(struct imgsensor_s *sensor, + uint32_t id, uint32_t size, imgsensor_value_t *value); +static int gc0308_set_value(struct imgsensor_s *sensor, + uint32_t id, uint32_t size, imgsensor_value_t value); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* GC0308 initialization register table from espressif/esp32-camera vendor + * driver. This is the proven register set for GC0308 VGA output. + */ + +static const struct gc0308_reg_s g_gc0308_init_regs[] = +{ + { 0xfe, 0x00 }, + { 0xec, 0x20 }, + { 0x05, 0x00 }, + { 0x06, 0x00 }, + { 0x07, 0x00 }, + { 0x08, 0x00 }, + { 0x09, 0x01 }, + { 0x0a, 0xe8 }, + { 0x0b, 0x02 }, + { 0x0c, 0x88 }, + { 0x0d, 0x02 }, + { 0x0e, 0x02 }, + { 0x10, 0x26 }, + { 0x11, 0x0d }, + { 0x12, 0x2a }, + { 0x13, 0x00 }, + { 0x14, 0x10 }, + { 0x15, 0x0a }, + { 0x16, 0x05 }, + { 0x17, 0x01 }, + { 0x18, 0x44 }, + { 0x19, 0x44 }, + { 0x1a, 0x2a }, + { 0x1b, 0x00 }, + { 0x1c, 0x49 }, + { 0x1d, 0x9a }, + { 0x1e, 0x61 }, + { 0x1f, 0x00 }, + { 0x20, 0x7f }, + { 0x21, 0xfa }, + { 0x22, 0x57 }, + { 0x24, 0xa2 }, /* YCbYCr output format */ + { 0x25, 0x0f }, + { 0x26, 0x03 }, + { 0x28, 0x00 }, + { 0x2d, 0x0a }, + { 0x2f, 0x01 }, + { 0x30, 0xf7 }, + { 0x31, 0x50 }, + { 0x32, 0x00 }, + { 0x33, 0x28 }, + { 0x34, 0x2a }, + { 0x35, 0x28 }, + { 0x39, 0x04 }, + { 0x3a, 0x20 }, + { 0x3b, 0x20 }, + { 0x3c, 0x00 }, + { 0x3d, 0x00 }, + { 0x3e, 0x00 }, + { 0x3f, 0x00 }, + { 0x50, 0x14 }, /* Global gain */ + { 0x52, 0x41 }, + { 0x53, 0x80 }, + { 0x54, 0x80 }, + { 0x55, 0x80 }, + { 0x56, 0x80 }, + { 0x5a, 0x56 }, /* AWB R gain */ + { 0x5b, 0x40 }, /* AWB G gain */ + { 0x5c, 0x4a }, /* AWB B gain */ + { 0x8b, 0x20 }, + { 0x8c, 0x20 }, + { 0x8d, 0x20 }, + { 0x8e, 0x14 }, + { 0x8f, 0x10 }, + { 0x90, 0x14 }, + { 0x91, 0x3c }, + { 0x92, 0x50 }, + { 0x5d, 0x12 }, + { 0x5e, 0x1a }, + { 0x5f, 0x24 }, + { 0x60, 0x07 }, + { 0x61, 0x15 }, + { 0x62, 0x08 }, + { 0x64, 0x03 }, + { 0x66, 0xe8 }, + { 0x67, 0x86 }, + { 0x68, 0x82 }, + { 0x69, 0x18 }, + { 0x6a, 0x0f }, + { 0x6b, 0x00 }, + { 0x6c, 0x5f }, + { 0x6d, 0x8f }, + { 0x6e, 0x55 }, + { 0x6f, 0x38 }, + { 0x70, 0x15 }, + { 0x71, 0x33 }, + { 0x72, 0xdc }, + { 0x73, 0x00 }, + { 0x74, 0x02 }, + { 0x75, 0x3f }, + { 0x76, 0x02 }, + { 0x77, 0x38 }, + { 0x78, 0x88 }, + { 0x79, 0x81 }, + { 0x7a, 0x81 }, + { 0x7b, 0x22 }, + { 0x7c, 0xff }, + { 0x93, 0x48 }, /* Color matrix */ + { 0x94, 0x02 }, + { 0x95, 0x07 }, + { 0x96, 0xe0 }, + { 0x97, 0x40 }, + { 0x98, 0xf0 }, + { 0xb1, 0x40 }, /* Saturation */ + { 0xb2, 0x40 }, + { 0xb3, 0x40 }, + { 0xb6, 0xe0 }, + { 0xbd, 0x38 }, + { 0xbe, 0x36 }, + { 0xd0, 0xcb }, /* AEC */ + { 0xd1, 0x10 }, + { 0xd2, 0x90 }, + { 0xd3, 0x48 }, + { 0xd5, 0xf2 }, + { 0xd6, 0x16 }, + { 0xdb, 0x92 }, + { 0xdc, 0xa5 }, + { 0xdf, 0x23 }, + { 0xd9, 0x00 }, + { 0xda, 0x00 }, + { 0xe0, 0x09 }, + { 0xed, 0x04 }, + { 0xee, 0xa0 }, + { 0xef, 0x40 }, + { 0x80, 0x03 }, + { 0x9f, 0x10 }, + { 0xa0, 0x20 }, + { 0xa1, 0x38 }, + { 0xa2, 0x4e }, + { 0xa3, 0x63 }, + { 0xa4, 0x76 }, + { 0xa5, 0x87 }, + { 0xa6, 0xa2 }, + { 0xa7, 0xb8 }, + { 0xa8, 0xca }, + { 0xa9, 0xd8 }, + { 0xaa, 0xe3 }, + { 0xab, 0xeb }, + { 0xac, 0xf0 }, + { 0xad, 0xf8 }, + { 0xae, 0xfd }, + { 0xaf, 0xff }, + { 0xc0, 0x00 }, + { 0xc1, 0x10 }, + { 0xc2, 0x1c }, + { 0xc3, 0x30 }, + { 0xc4, 0x43 }, + { 0xc5, 0x54 }, + { 0xc6, 0x65 }, + { 0xc7, 0x75 }, + { 0xc8, 0x93 }, + { 0xc9, 0xb0 }, + { 0xca, 0xcb }, + { 0xcb, 0xe6 }, + { 0xcc, 0xff }, + { 0xf0, 0x02 }, + { 0xf1, 0x01 }, + { 0xf2, 0x02 }, + { 0xf3, 0x30 }, + { 0xf7, 0x04 }, + { 0xf8, 0x02 }, + { 0xf9, 0x9f }, + { 0xfa, 0x78 }, + { 0xfe, 0x01 }, + { 0x00, 0xf5 }, + { 0x02, 0x20 }, + { 0x04, 0x10 }, + { 0x05, 0x08 }, + { 0x06, 0x20 }, + { 0x08, 0x0a }, + { 0x0a, 0xa0 }, + { 0x0b, 0x60 }, + { 0x0c, 0x08 }, + { 0x0e, 0x44 }, + { 0x0f, 0x32 }, + { 0x10, 0x41 }, + { 0x11, 0x37 }, + { 0x12, 0x22 }, + { 0x13, 0x19 }, + { 0x14, 0x44 }, + { 0x15, 0x44 }, + { 0x16, 0xc2 }, + { 0x17, 0xa8 }, + { 0x18, 0x18 }, + { 0x19, 0x50 }, + { 0x1a, 0xd8 }, + { 0x1b, 0xf5 }, + { 0x70, 0x40 }, + { 0x71, 0x58 }, + { 0x72, 0x30 }, + { 0x73, 0x48 }, + { 0x74, 0x20 }, + { 0x75, 0x60 }, + { 0x77, 0x20 }, + { 0x78, 0x32 }, + { 0x30, 0x03 }, + { 0x31, 0x40 }, + { 0x32, 0x10 }, + { 0x33, 0xe0 }, + { 0x34, 0xe0 }, + { 0x35, 0x00 }, + { 0x36, 0x80 }, + { 0x37, 0x00 }, + { 0x38, 0x04 }, + { 0x39, 0x09 }, + { 0x3a, 0x12 }, + { 0x3b, 0x1c }, + { 0x3c, 0x28 }, + { 0x3d, 0x31 }, + { 0x3e, 0x44 }, + { 0x3f, 0x57 }, + { 0x40, 0x6c }, + { 0x41, 0x81 }, + { 0x42, 0x94 }, + { 0x43, 0xa7 }, + { 0x44, 0xb8 }, + { 0x45, 0xd6 }, + { 0x46, 0xee }, + { 0x47, 0x0d }, + { 0x62, 0xf7 }, + { 0x63, 0x68 }, + { 0x64, 0xd3 }, + { 0x65, 0xd3 }, + { 0x66, 0x60 }, + { 0xfe, 0x00 }, + { 0x01, 0x32 }, + { 0x02, 0x0c }, + { 0x0f, 0x01 }, + { 0xe2, 0x00 }, + { 0xe3, 0x78 }, + { 0xe4, 0x00 }, + { 0xe5, 0xfe }, + { 0xe6, 0x01 }, + { 0xe7, 0xe0 }, + { 0xe8, 0x01 }, + { 0xe9, 0xe0 }, + { 0xea, 0x01 }, + { 0xeb, 0xe0 }, + { 0xfe, 0x00 }, +}; + +static const struct imgsensor_ops_s g_gc0308_ops = +{ + .is_available = gc0308_is_available, + .init = gc0308_init, + .uninit = gc0308_uninit, + .get_driver_name = gc0308_get_driver_name, + .validate_frame_setting = gc0308_validate_frame_setting, + .start_capture = gc0308_start_capture, + .stop_capture = gc0308_stop_capture, + .get_supported_value = gc0308_get_supported_value, + .get_value = gc0308_get_value, + .set_value = gc0308_set_value, +}; + +static const struct v4l2_fmtdesc g_gc0308_fmtdescs[] = +{ + { + .pixelformat = V4L2_PIX_FMT_RGB565, + .description = "RGB565", + }, +}; + +static const struct v4l2_frmsizeenum g_gc0308_frmsizes[] = +{ + { + .type = V4L2_FRMSIZE_TYPE_DISCRETE, + .discrete = + { + .width = GC0308_WIDTH, + .height = GC0308_HEIGHT, + }, + }, +}; + +static const struct v4l2_frmivalenum g_gc0308_frmintervals[] = +{ + { + .type = V4L2_FRMIVAL_TYPE_DISCRETE, + .discrete = + { + .numerator = 1, + .denominator = 30, + }, + }, +}; + +static struct gc0308_dev_s g_gc0308_priv; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: gc0308_putreg + ****************************************************************************/ + +static int gc0308_putreg(struct i2c_master_s *i2c, + uint8_t regaddr, uint8_t regval) +{ + struct i2c_msg_s msg; + uint8_t buf[2]; + int ret; + + buf[0] = regaddr; + buf[1] = regval; + + msg.frequency = GC0308_I2C_FREQ; + msg.addr = GC0308_I2C_ADDR; + msg.flags = 0; + msg.buffer = buf; + msg.length = 2; + + ret = I2C_TRANSFER(i2c, &msg, 1); + if (ret < 0) + { + snerr("ERROR: I2C write to 0x%02x failed: %d\n", regaddr, ret); + } + + return ret; +} + +/**************************************************************************** + * Name: gc0308_getreg + ****************************************************************************/ + +static int gc0308_getreg(struct i2c_master_s *i2c, + uint8_t regaddr, uint8_t *regval) +{ + struct i2c_msg_s msg[2]; + int ret; + + msg[0].frequency = GC0308_I2C_FREQ; + msg[0].addr = GC0308_I2C_ADDR; + msg[0].flags = 0; + msg[0].buffer = ®addr; + msg[0].length = 1; + + msg[1].frequency = GC0308_I2C_FREQ; + msg[1].addr = GC0308_I2C_ADDR; + msg[1].flags = I2C_M_READ; + msg[1].buffer = regval; + msg[1].length = 1; + + ret = I2C_TRANSFER(i2c, msg, 2); + if (ret < 0) + { + snerr("ERROR: I2C read from 0x%02x failed: %d\n", regaddr, ret); + } + + return ret; +} + +/**************************************************************************** + * Name: gc0308_putreglist + ****************************************************************************/ + +static int gc0308_putreglist(struct i2c_master_s *i2c, + const struct gc0308_reg_s *reglist, + size_t nentries) +{ + int ret; + + for (size_t i = 0; i < nentries; i++) + { + ret = gc0308_putreg(i2c, reglist[i].addr, reglist[i].val); + if (ret < 0) + { + snerr("GC0308 write[%d] 0x%02x=0x%02x FAILED: %d\n", + (int)i, reglist[i].addr, reglist[i].val, ret); + return ret; + } + } + + return OK; +} + +/**************************************************************************** + * Name: gc0308_is_available + ****************************************************************************/ + +static bool gc0308_is_available(struct imgsensor_s *sensor) +{ + struct gc0308_dev_s *priv = (struct gc0308_dev_s *)sensor; + uint8_t id = 0; + int ret; + + /* Select page 0 */ + + gc0308_putreg(priv->i2c, GC0308_REG_RESET, 0x00); + + ret = gc0308_getreg(priv->i2c, GC0308_REG_CHIP_ID, &id); + if (ret < 0) + { + return false; + } + + sninfo("GC0308 chip ID: 0x%02x (expected 0x%02x)\n", + id, GC0308_CHIP_ID_VAL); + + return (id == GC0308_CHIP_ID_VAL); +} + +/**************************************************************************** + * Name: gc0308_init + ****************************************************************************/ + +static int gc0308_init(struct imgsensor_s *sensor) +{ + struct gc0308_dev_s *priv = (struct gc0308_dev_s *)sensor; + int ret; + + /* Software reset per vendor: write 0xf0 to reg 0xfe, wait 80ms */ + + ret = gc0308_putreg(priv->i2c, 0xfe, 0xf0); + if (ret < 0) + { + snerr("GC0308 soft reset failed: %d\n", ret); + return ret; + } + + up_mdelay(80); + + /* Write vendor initialization register table */ + + ret = gc0308_putreglist(priv->i2c, g_gc0308_init_regs, + sizeof(g_gc0308_init_regs) / Review Comment: nitems ########## drivers/video/gc0308.c: ########## @@ -0,0 +1,861 @@ +/**************************************************************************** + * drivers/video/gc0308.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <stdint.h> +#include <string.h> +#include <errno.h> +#include <debug.h> + +#include <nuttx/i2c/i2c_master.h> +#include <nuttx/video/imgsensor.h> +#include <nuttx/arch.h> +#include <nuttx/video/video.h> + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define GC0308_I2C_ADDR 0x21 +#define GC0308_I2C_FREQ 100000 + +/* GC0308 Register Addresses */ + +#define GC0308_REG_CHIP_ID 0x00 +#define GC0308_CHIP_ID_VAL 0x9b + +/* Page 0 registers */ + +#define GC0308_REG_RESET 0xfe /* Page/reset register */ +#define GC0308_REG_HBLANK_H 0x01 +#define GC0308_REG_HBLANK_L 0x02 +#define GC0308_REG_VBLANK_H 0x03 +#define GC0308_REG_VBLANK_L 0x04 +#define GC0308_REG_SH_DELAY 0x05 +#define GC0308_REG_ROW_START_H 0x06 +#define GC0308_REG_ROW_START_L 0x07 +#define GC0308_REG_COL_START_H 0x08 +#define GC0308_REG_COL_START_L 0x09 +#define GC0308_REG_WIN_HEIGHT_H 0x0a +#define GC0308_REG_WIN_HEIGHT_L 0x0b +#define GC0308_REG_WIN_WIDTH_H 0x0c +#define GC0308_REG_WIN_WIDTH_L 0x0d +#define GC0308_REG_VS_ST 0x0e +#define GC0308_REG_VS_ET 0x0f +#define GC0308_REG_VB_HB 0x10 +#define GC0308_REG_RSH_WIDTH 0x11 +#define GC0308_REG_TSP_WIDTH 0x12 +#define GC0308_REG_SAMPLE_HOLD 0x13 +#define GC0308_REG_CISCTL_MODE1 0x14 +#define GC0308_REG_CISCTL_MODE2 0x15 +#define GC0308_REG_CISCTL_MODE3 0x16 +#define GC0308_REG_CISCTL_MODE4 0x17 + +/* Output format (Page 0) */ + +#define GC0308_REG_OUTPUT_FMT 0x24 /* Bits[3:0]: output format select */ +#define GC0308_REG_OUT_FORMAT 0x44 +#define GC0308_REG_OUT_EN 0x45 +#define GC0308_REG_SYNC_MODE 0x46 + +/* Analog & bias */ + +#define GC0308_REG_ANALOG_MODE1 0x1a +#define GC0308_REG_ANALOG_MODE2 0x1b + +/* Exposure */ + +#define GC0308_REG_EXP_H 0x03 +#define GC0308_REG_EXP_L 0x04 + +/* Gain */ + +#define GC0308_REG_GLOBAL_GAIN 0x50 + +/* Crop */ + +#define GC0308_REG_CROP_WIN_MODE 0x46 +#define GC0308_REG_CROP_Y1_H 0x47 +#define GC0308_REG_CROP_Y1_L 0x48 +#define GC0308_REG_CROP_X1_H 0x49 +#define GC0308_REG_CROP_X1_L 0x4a +#define GC0308_REG_CROP_WIN_H_H 0x4b +#define GC0308_REG_CROP_WIN_H_L 0x4c +#define GC0308_REG_CROP_WIN_W_H 0x4d +#define GC0308_REG_CROP_WIN_W_L 0x4e + +/* Subsample (Page 0) */ + +#define GC0308_REG_SUBSAMPLE 0x59 +#define GC0308_REG_SUB_MODE 0x5a +#define GC0308_REG_SUB_ROW_N1 0x5b +#define GC0308_REG_SUB_ROW_N2 0x5c +#define GC0308_REG_SUB_COL_N1 0x5d +#define GC0308_REG_SUB_COL_N2 0x5e + +/* Subsample control (Page 1) */ + +#define GC0308_P1_REG_SUB_CTRL 0x53 /* Bit7: subsample enable */ +#define GC0308_P1_REG_SUB_RATIO 0x54 /* H[7:4] V[3:0] ratio */ +#define GC0308_P1_REG_SUB_EN 0x55 /* Bit0: subsample output enable */ +#define GC0308_P1_REG_SUB_HOFF 0x56 /* H offset */ +#define GC0308_P1_REG_SUB_VOFF 0x57 /* V offset */ +#define GC0308_P1_REG_SUB_HSIZE 0x58 /* H size adjust */ +#define GC0308_P1_REG_SUB_VSIZE 0x59 /* V size adjust */ + +/* Configured resolution */ + +#ifdef CONFIG_VIDEO_GC0308_VGA +# define GC0308_WIDTH 640 +# define GC0308_HEIGHT 480 +#elif defined(CONFIG_VIDEO_GC0308_QVGA) +# define GC0308_WIDTH 320 +# define GC0308_HEIGHT 240 +#elif defined(CONFIG_VIDEO_GC0308_QQVGA) +# define GC0308_WIDTH 160 +# define GC0308_HEIGHT 120 +#else +# error "No GC0308 resolution selected" +#endif + +/* Mirror/flip */ + +#define GC0308_REG_CISCTL_MODE1_MIRROR 0x14 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct gc0308_reg_s +{ + uint8_t addr; + uint8_t val; +}; + +struct gc0308_dev_s +{ + struct imgsensor_s sensor; + struct i2c_master_s *i2c; + bool streaming; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static bool gc0308_is_available(struct imgsensor_s *sensor); +static int gc0308_init(struct imgsensor_s *sensor); +static int gc0308_uninit(struct imgsensor_s *sensor); +static const char *gc0308_get_driver_name(struct imgsensor_s *sensor); +static int gc0308_validate_frame_setting(struct imgsensor_s *sensor, + imgsensor_stream_type_t type, + uint8_t nr_datafmts, + imgsensor_format_t *datafmts, + imgsensor_interval_t *interval); +static int gc0308_start_capture(struct imgsensor_s *sensor, + imgsensor_stream_type_t type, + uint8_t nr_datafmts, + imgsensor_format_t *datafmts, + imgsensor_interval_t *interval); +static int gc0308_stop_capture(struct imgsensor_s *sensor, + imgsensor_stream_type_t type); +static int gc0308_get_supported_value(struct imgsensor_s *sensor, + uint32_t id, + imgsensor_supported_value_t *value); +static int gc0308_get_value(struct imgsensor_s *sensor, + uint32_t id, uint32_t size, imgsensor_value_t *value); +static int gc0308_set_value(struct imgsensor_s *sensor, + uint32_t id, uint32_t size, imgsensor_value_t value); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* GC0308 initialization register table from espressif/esp32-camera vendor + * driver. This is the proven register set for GC0308 VGA output. + */ + +static const struct gc0308_reg_s g_gc0308_init_regs[] = +{ + { 0xfe, 0x00 }, + { 0xec, 0x20 }, + { 0x05, 0x00 }, + { 0x06, 0x00 }, + { 0x07, 0x00 }, + { 0x08, 0x00 }, + { 0x09, 0x01 }, + { 0x0a, 0xe8 }, + { 0x0b, 0x02 }, + { 0x0c, 0x88 }, + { 0x0d, 0x02 }, + { 0x0e, 0x02 }, + { 0x10, 0x26 }, + { 0x11, 0x0d }, + { 0x12, 0x2a }, + { 0x13, 0x00 }, + { 0x14, 0x10 }, + { 0x15, 0x0a }, + { 0x16, 0x05 }, + { 0x17, 0x01 }, + { 0x18, 0x44 }, + { 0x19, 0x44 }, + { 0x1a, 0x2a }, + { 0x1b, 0x00 }, + { 0x1c, 0x49 }, + { 0x1d, 0x9a }, + { 0x1e, 0x61 }, + { 0x1f, 0x00 }, + { 0x20, 0x7f }, + { 0x21, 0xfa }, + { 0x22, 0x57 }, + { 0x24, 0xa2 }, /* YCbYCr output format */ + { 0x25, 0x0f }, + { 0x26, 0x03 }, + { 0x28, 0x00 }, + { 0x2d, 0x0a }, + { 0x2f, 0x01 }, + { 0x30, 0xf7 }, + { 0x31, 0x50 }, + { 0x32, 0x00 }, + { 0x33, 0x28 }, + { 0x34, 0x2a }, + { 0x35, 0x28 }, + { 0x39, 0x04 }, + { 0x3a, 0x20 }, + { 0x3b, 0x20 }, + { 0x3c, 0x00 }, + { 0x3d, 0x00 }, + { 0x3e, 0x00 }, + { 0x3f, 0x00 }, + { 0x50, 0x14 }, /* Global gain */ + { 0x52, 0x41 }, + { 0x53, 0x80 }, + { 0x54, 0x80 }, + { 0x55, 0x80 }, + { 0x56, 0x80 }, + { 0x5a, 0x56 }, /* AWB R gain */ + { 0x5b, 0x40 }, /* AWB G gain */ + { 0x5c, 0x4a }, /* AWB B gain */ + { 0x8b, 0x20 }, + { 0x8c, 0x20 }, + { 0x8d, 0x20 }, + { 0x8e, 0x14 }, + { 0x8f, 0x10 }, + { 0x90, 0x14 }, + { 0x91, 0x3c }, + { 0x92, 0x50 }, + { 0x5d, 0x12 }, + { 0x5e, 0x1a }, + { 0x5f, 0x24 }, + { 0x60, 0x07 }, + { 0x61, 0x15 }, + { 0x62, 0x08 }, + { 0x64, 0x03 }, + { 0x66, 0xe8 }, + { 0x67, 0x86 }, + { 0x68, 0x82 }, + { 0x69, 0x18 }, + { 0x6a, 0x0f }, + { 0x6b, 0x00 }, + { 0x6c, 0x5f }, + { 0x6d, 0x8f }, + { 0x6e, 0x55 }, + { 0x6f, 0x38 }, + { 0x70, 0x15 }, + { 0x71, 0x33 }, + { 0x72, 0xdc }, + { 0x73, 0x00 }, + { 0x74, 0x02 }, + { 0x75, 0x3f }, + { 0x76, 0x02 }, + { 0x77, 0x38 }, + { 0x78, 0x88 }, + { 0x79, 0x81 }, + { 0x7a, 0x81 }, + { 0x7b, 0x22 }, + { 0x7c, 0xff }, + { 0x93, 0x48 }, /* Color matrix */ + { 0x94, 0x02 }, + { 0x95, 0x07 }, + { 0x96, 0xe0 }, + { 0x97, 0x40 }, + { 0x98, 0xf0 }, + { 0xb1, 0x40 }, /* Saturation */ + { 0xb2, 0x40 }, + { 0xb3, 0x40 }, + { 0xb6, 0xe0 }, + { 0xbd, 0x38 }, + { 0xbe, 0x36 }, + { 0xd0, 0xcb }, /* AEC */ + { 0xd1, 0x10 }, + { 0xd2, 0x90 }, + { 0xd3, 0x48 }, + { 0xd5, 0xf2 }, + { 0xd6, 0x16 }, + { 0xdb, 0x92 }, + { 0xdc, 0xa5 }, + { 0xdf, 0x23 }, + { 0xd9, 0x00 }, + { 0xda, 0x00 }, + { 0xe0, 0x09 }, + { 0xed, 0x04 }, + { 0xee, 0xa0 }, + { 0xef, 0x40 }, + { 0x80, 0x03 }, + { 0x9f, 0x10 }, + { 0xa0, 0x20 }, + { 0xa1, 0x38 }, + { 0xa2, 0x4e }, + { 0xa3, 0x63 }, + { 0xa4, 0x76 }, + { 0xa5, 0x87 }, + { 0xa6, 0xa2 }, + { 0xa7, 0xb8 }, + { 0xa8, 0xca }, + { 0xa9, 0xd8 }, + { 0xaa, 0xe3 }, + { 0xab, 0xeb }, + { 0xac, 0xf0 }, + { 0xad, 0xf8 }, + { 0xae, 0xfd }, + { 0xaf, 0xff }, + { 0xc0, 0x00 }, + { 0xc1, 0x10 }, + { 0xc2, 0x1c }, + { 0xc3, 0x30 }, + { 0xc4, 0x43 }, + { 0xc5, 0x54 }, + { 0xc6, 0x65 }, + { 0xc7, 0x75 }, + { 0xc8, 0x93 }, + { 0xc9, 0xb0 }, + { 0xca, 0xcb }, + { 0xcb, 0xe6 }, + { 0xcc, 0xff }, + { 0xf0, 0x02 }, + { 0xf1, 0x01 }, + { 0xf2, 0x02 }, + { 0xf3, 0x30 }, + { 0xf7, 0x04 }, + { 0xf8, 0x02 }, + { 0xf9, 0x9f }, + { 0xfa, 0x78 }, + { 0xfe, 0x01 }, + { 0x00, 0xf5 }, + { 0x02, 0x20 }, + { 0x04, 0x10 }, + { 0x05, 0x08 }, + { 0x06, 0x20 }, + { 0x08, 0x0a }, + { 0x0a, 0xa0 }, + { 0x0b, 0x60 }, + { 0x0c, 0x08 }, + { 0x0e, 0x44 }, + { 0x0f, 0x32 }, + { 0x10, 0x41 }, + { 0x11, 0x37 }, + { 0x12, 0x22 }, + { 0x13, 0x19 }, + { 0x14, 0x44 }, + { 0x15, 0x44 }, + { 0x16, 0xc2 }, + { 0x17, 0xa8 }, + { 0x18, 0x18 }, + { 0x19, 0x50 }, + { 0x1a, 0xd8 }, + { 0x1b, 0xf5 }, + { 0x70, 0x40 }, + { 0x71, 0x58 }, + { 0x72, 0x30 }, + { 0x73, 0x48 }, + { 0x74, 0x20 }, + { 0x75, 0x60 }, + { 0x77, 0x20 }, + { 0x78, 0x32 }, + { 0x30, 0x03 }, + { 0x31, 0x40 }, + { 0x32, 0x10 }, + { 0x33, 0xe0 }, + { 0x34, 0xe0 }, + { 0x35, 0x00 }, + { 0x36, 0x80 }, + { 0x37, 0x00 }, + { 0x38, 0x04 }, + { 0x39, 0x09 }, + { 0x3a, 0x12 }, + { 0x3b, 0x1c }, + { 0x3c, 0x28 }, + { 0x3d, 0x31 }, + { 0x3e, 0x44 }, + { 0x3f, 0x57 }, + { 0x40, 0x6c }, + { 0x41, 0x81 }, + { 0x42, 0x94 }, + { 0x43, 0xa7 }, + { 0x44, 0xb8 }, + { 0x45, 0xd6 }, + { 0x46, 0xee }, + { 0x47, 0x0d }, + { 0x62, 0xf7 }, + { 0x63, 0x68 }, + { 0x64, 0xd3 }, + { 0x65, 0xd3 }, + { 0x66, 0x60 }, + { 0xfe, 0x00 }, + { 0x01, 0x32 }, + { 0x02, 0x0c }, + { 0x0f, 0x01 }, + { 0xe2, 0x00 }, + { 0xe3, 0x78 }, + { 0xe4, 0x00 }, + { 0xe5, 0xfe }, + { 0xe6, 0x01 }, + { 0xe7, 0xe0 }, + { 0xe8, 0x01 }, + { 0xe9, 0xe0 }, + { 0xea, 0x01 }, + { 0xeb, 0xe0 }, + { 0xfe, 0x00 }, +}; + +static const struct imgsensor_ops_s g_gc0308_ops = +{ + .is_available = gc0308_is_available, + .init = gc0308_init, + .uninit = gc0308_uninit, + .get_driver_name = gc0308_get_driver_name, + .validate_frame_setting = gc0308_validate_frame_setting, + .start_capture = gc0308_start_capture, + .stop_capture = gc0308_stop_capture, + .get_supported_value = gc0308_get_supported_value, + .get_value = gc0308_get_value, + .set_value = gc0308_set_value, +}; + +static const struct v4l2_fmtdesc g_gc0308_fmtdescs[] = +{ + { + .pixelformat = V4L2_PIX_FMT_RGB565, + .description = "RGB565", + }, +}; + +static const struct v4l2_frmsizeenum g_gc0308_frmsizes[] = +{ + { + .type = V4L2_FRMSIZE_TYPE_DISCRETE, + .discrete = + { + .width = GC0308_WIDTH, + .height = GC0308_HEIGHT, + }, + }, +}; + +static const struct v4l2_frmivalenum g_gc0308_frmintervals[] = +{ + { + .type = V4L2_FRMIVAL_TYPE_DISCRETE, + .discrete = + { + .numerator = 1, + .denominator = 30, + }, + }, +}; + +static struct gc0308_dev_s g_gc0308_priv; Review Comment: let's change to local variable to support multiple instance in the future ########## drivers/video/gc0308.c: ########## @@ -0,0 +1,861 @@ +/**************************************************************************** + * drivers/video/gc0308.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <stdint.h> +#include <string.h> +#include <errno.h> +#include <debug.h> + +#include <nuttx/i2c/i2c_master.h> +#include <nuttx/video/imgsensor.h> +#include <nuttx/arch.h> +#include <nuttx/video/video.h> + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define GC0308_I2C_ADDR 0x21 +#define GC0308_I2C_FREQ 100000 + +/* GC0308 Register Addresses */ + +#define GC0308_REG_CHIP_ID 0x00 +#define GC0308_CHIP_ID_VAL 0x9b + +/* Page 0 registers */ + +#define GC0308_REG_RESET 0xfe /* Page/reset register */ +#define GC0308_REG_HBLANK_H 0x01 +#define GC0308_REG_HBLANK_L 0x02 +#define GC0308_REG_VBLANK_H 0x03 +#define GC0308_REG_VBLANK_L 0x04 +#define GC0308_REG_SH_DELAY 0x05 +#define GC0308_REG_ROW_START_H 0x06 +#define GC0308_REG_ROW_START_L 0x07 +#define GC0308_REG_COL_START_H 0x08 +#define GC0308_REG_COL_START_L 0x09 +#define GC0308_REG_WIN_HEIGHT_H 0x0a +#define GC0308_REG_WIN_HEIGHT_L 0x0b +#define GC0308_REG_WIN_WIDTH_H 0x0c +#define GC0308_REG_WIN_WIDTH_L 0x0d +#define GC0308_REG_VS_ST 0x0e +#define GC0308_REG_VS_ET 0x0f +#define GC0308_REG_VB_HB 0x10 +#define GC0308_REG_RSH_WIDTH 0x11 +#define GC0308_REG_TSP_WIDTH 0x12 +#define GC0308_REG_SAMPLE_HOLD 0x13 +#define GC0308_REG_CISCTL_MODE1 0x14 +#define GC0308_REG_CISCTL_MODE2 0x15 +#define GC0308_REG_CISCTL_MODE3 0x16 +#define GC0308_REG_CISCTL_MODE4 0x17 + +/* Output format (Page 0) */ + +#define GC0308_REG_OUTPUT_FMT 0x24 /* Bits[3:0]: output format select */ +#define GC0308_REG_OUT_FORMAT 0x44 +#define GC0308_REG_OUT_EN 0x45 +#define GC0308_REG_SYNC_MODE 0x46 + +/* Analog & bias */ + +#define GC0308_REG_ANALOG_MODE1 0x1a +#define GC0308_REG_ANALOG_MODE2 0x1b + +/* Exposure */ + +#define GC0308_REG_EXP_H 0x03 +#define GC0308_REG_EXP_L 0x04 + +/* Gain */ + +#define GC0308_REG_GLOBAL_GAIN 0x50 + +/* Crop */ + +#define GC0308_REG_CROP_WIN_MODE 0x46 +#define GC0308_REG_CROP_Y1_H 0x47 +#define GC0308_REG_CROP_Y1_L 0x48 +#define GC0308_REG_CROP_X1_H 0x49 +#define GC0308_REG_CROP_X1_L 0x4a +#define GC0308_REG_CROP_WIN_H_H 0x4b +#define GC0308_REG_CROP_WIN_H_L 0x4c +#define GC0308_REG_CROP_WIN_W_H 0x4d +#define GC0308_REG_CROP_WIN_W_L 0x4e + +/* Subsample (Page 0) */ + +#define GC0308_REG_SUBSAMPLE 0x59 +#define GC0308_REG_SUB_MODE 0x5a +#define GC0308_REG_SUB_ROW_N1 0x5b +#define GC0308_REG_SUB_ROW_N2 0x5c +#define GC0308_REG_SUB_COL_N1 0x5d +#define GC0308_REG_SUB_COL_N2 0x5e + +/* Subsample control (Page 1) */ + +#define GC0308_P1_REG_SUB_CTRL 0x53 /* Bit7: subsample enable */ +#define GC0308_P1_REG_SUB_RATIO 0x54 /* H[7:4] V[3:0] ratio */ +#define GC0308_P1_REG_SUB_EN 0x55 /* Bit0: subsample output enable */ +#define GC0308_P1_REG_SUB_HOFF 0x56 /* H offset */ +#define GC0308_P1_REG_SUB_VOFF 0x57 /* V offset */ +#define GC0308_P1_REG_SUB_HSIZE 0x58 /* H size adjust */ +#define GC0308_P1_REG_SUB_VSIZE 0x59 /* V size adjust */ + +/* Configured resolution */ + +#ifdef CONFIG_VIDEO_GC0308_VGA +# define GC0308_WIDTH 640 +# define GC0308_HEIGHT 480 +#elif defined(CONFIG_VIDEO_GC0308_QVGA) +# define GC0308_WIDTH 320 +# define GC0308_HEIGHT 240 +#elif defined(CONFIG_VIDEO_GC0308_QQVGA) +# define GC0308_WIDTH 160 +# define GC0308_HEIGHT 120 +#else +# error "No GC0308 resolution selected" +#endif + +/* Mirror/flip */ + +#define GC0308_REG_CISCTL_MODE1_MIRROR 0x14 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct gc0308_reg_s +{ + uint8_t addr; + uint8_t val; +}; + +struct gc0308_dev_s +{ + struct imgsensor_s sensor; + struct i2c_master_s *i2c; + bool streaming; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static bool gc0308_is_available(struct imgsensor_s *sensor); +static int gc0308_init(struct imgsensor_s *sensor); +static int gc0308_uninit(struct imgsensor_s *sensor); +static const char *gc0308_get_driver_name(struct imgsensor_s *sensor); +static int gc0308_validate_frame_setting(struct imgsensor_s *sensor, + imgsensor_stream_type_t type, + uint8_t nr_datafmts, + imgsensor_format_t *datafmts, + imgsensor_interval_t *interval); +static int gc0308_start_capture(struct imgsensor_s *sensor, + imgsensor_stream_type_t type, + uint8_t nr_datafmts, + imgsensor_format_t *datafmts, + imgsensor_interval_t *interval); +static int gc0308_stop_capture(struct imgsensor_s *sensor, + imgsensor_stream_type_t type); +static int gc0308_get_supported_value(struct imgsensor_s *sensor, + uint32_t id, + imgsensor_supported_value_t *value); +static int gc0308_get_value(struct imgsensor_s *sensor, + uint32_t id, uint32_t size, imgsensor_value_t *value); +static int gc0308_set_value(struct imgsensor_s *sensor, + uint32_t id, uint32_t size, imgsensor_value_t value); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* GC0308 initialization register table from espressif/esp32-camera vendor + * driver. This is the proven register set for GC0308 VGA output. + */ + +static const struct gc0308_reg_s g_gc0308_init_regs[] = +{ + { 0xfe, 0x00 }, + { 0xec, 0x20 }, + { 0x05, 0x00 }, + { 0x06, 0x00 }, + { 0x07, 0x00 }, + { 0x08, 0x00 }, + { 0x09, 0x01 }, + { 0x0a, 0xe8 }, + { 0x0b, 0x02 }, + { 0x0c, 0x88 }, + { 0x0d, 0x02 }, + { 0x0e, 0x02 }, + { 0x10, 0x26 }, + { 0x11, 0x0d }, + { 0x12, 0x2a }, + { 0x13, 0x00 }, + { 0x14, 0x10 }, + { 0x15, 0x0a }, + { 0x16, 0x05 }, + { 0x17, 0x01 }, + { 0x18, 0x44 }, + { 0x19, 0x44 }, + { 0x1a, 0x2a }, + { 0x1b, 0x00 }, + { 0x1c, 0x49 }, + { 0x1d, 0x9a }, + { 0x1e, 0x61 }, + { 0x1f, 0x00 }, + { 0x20, 0x7f }, + { 0x21, 0xfa }, + { 0x22, 0x57 }, + { 0x24, 0xa2 }, /* YCbYCr output format */ + { 0x25, 0x0f }, + { 0x26, 0x03 }, + { 0x28, 0x00 }, + { 0x2d, 0x0a }, + { 0x2f, 0x01 }, + { 0x30, 0xf7 }, + { 0x31, 0x50 }, + { 0x32, 0x00 }, + { 0x33, 0x28 }, + { 0x34, 0x2a }, + { 0x35, 0x28 }, + { 0x39, 0x04 }, + { 0x3a, 0x20 }, + { 0x3b, 0x20 }, + { 0x3c, 0x00 }, + { 0x3d, 0x00 }, + { 0x3e, 0x00 }, + { 0x3f, 0x00 }, + { 0x50, 0x14 }, /* Global gain */ + { 0x52, 0x41 }, + { 0x53, 0x80 }, + { 0x54, 0x80 }, + { 0x55, 0x80 }, + { 0x56, 0x80 }, + { 0x5a, 0x56 }, /* AWB R gain */ + { 0x5b, 0x40 }, /* AWB G gain */ + { 0x5c, 0x4a }, /* AWB B gain */ + { 0x8b, 0x20 }, + { 0x8c, 0x20 }, + { 0x8d, 0x20 }, + { 0x8e, 0x14 }, + { 0x8f, 0x10 }, + { 0x90, 0x14 }, + { 0x91, 0x3c }, + { 0x92, 0x50 }, + { 0x5d, 0x12 }, + { 0x5e, 0x1a }, + { 0x5f, 0x24 }, + { 0x60, 0x07 }, + { 0x61, 0x15 }, + { 0x62, 0x08 }, + { 0x64, 0x03 }, + { 0x66, 0xe8 }, + { 0x67, 0x86 }, + { 0x68, 0x82 }, + { 0x69, 0x18 }, + { 0x6a, 0x0f }, + { 0x6b, 0x00 }, + { 0x6c, 0x5f }, + { 0x6d, 0x8f }, + { 0x6e, 0x55 }, + { 0x6f, 0x38 }, + { 0x70, 0x15 }, + { 0x71, 0x33 }, + { 0x72, 0xdc }, + { 0x73, 0x00 }, + { 0x74, 0x02 }, + { 0x75, 0x3f }, + { 0x76, 0x02 }, + { 0x77, 0x38 }, + { 0x78, 0x88 }, + { 0x79, 0x81 }, + { 0x7a, 0x81 }, + { 0x7b, 0x22 }, + { 0x7c, 0xff }, + { 0x93, 0x48 }, /* Color matrix */ + { 0x94, 0x02 }, + { 0x95, 0x07 }, + { 0x96, 0xe0 }, + { 0x97, 0x40 }, + { 0x98, 0xf0 }, + { 0xb1, 0x40 }, /* Saturation */ + { 0xb2, 0x40 }, + { 0xb3, 0x40 }, + { 0xb6, 0xe0 }, + { 0xbd, 0x38 }, + { 0xbe, 0x36 }, + { 0xd0, 0xcb }, /* AEC */ + { 0xd1, 0x10 }, + { 0xd2, 0x90 }, + { 0xd3, 0x48 }, + { 0xd5, 0xf2 }, + { 0xd6, 0x16 }, + { 0xdb, 0x92 }, + { 0xdc, 0xa5 }, + { 0xdf, 0x23 }, + { 0xd9, 0x00 }, + { 0xda, 0x00 }, + { 0xe0, 0x09 }, + { 0xed, 0x04 }, + { 0xee, 0xa0 }, + { 0xef, 0x40 }, + { 0x80, 0x03 }, + { 0x9f, 0x10 }, + { 0xa0, 0x20 }, + { 0xa1, 0x38 }, + { 0xa2, 0x4e }, + { 0xa3, 0x63 }, + { 0xa4, 0x76 }, + { 0xa5, 0x87 }, + { 0xa6, 0xa2 }, + { 0xa7, 0xb8 }, + { 0xa8, 0xca }, + { 0xa9, 0xd8 }, + { 0xaa, 0xe3 }, + { 0xab, 0xeb }, + { 0xac, 0xf0 }, + { 0xad, 0xf8 }, + { 0xae, 0xfd }, + { 0xaf, 0xff }, + { 0xc0, 0x00 }, + { 0xc1, 0x10 }, + { 0xc2, 0x1c }, + { 0xc3, 0x30 }, + { 0xc4, 0x43 }, + { 0xc5, 0x54 }, + { 0xc6, 0x65 }, + { 0xc7, 0x75 }, + { 0xc8, 0x93 }, + { 0xc9, 0xb0 }, + { 0xca, 0xcb }, + { 0xcb, 0xe6 }, + { 0xcc, 0xff }, + { 0xf0, 0x02 }, + { 0xf1, 0x01 }, + { 0xf2, 0x02 }, + { 0xf3, 0x30 }, + { 0xf7, 0x04 }, + { 0xf8, 0x02 }, + { 0xf9, 0x9f }, + { 0xfa, 0x78 }, + { 0xfe, 0x01 }, + { 0x00, 0xf5 }, + { 0x02, 0x20 }, + { 0x04, 0x10 }, + { 0x05, 0x08 }, + { 0x06, 0x20 }, + { 0x08, 0x0a }, + { 0x0a, 0xa0 }, + { 0x0b, 0x60 }, + { 0x0c, 0x08 }, + { 0x0e, 0x44 }, + { 0x0f, 0x32 }, + { 0x10, 0x41 }, + { 0x11, 0x37 }, + { 0x12, 0x22 }, + { 0x13, 0x19 }, + { 0x14, 0x44 }, + { 0x15, 0x44 }, + { 0x16, 0xc2 }, + { 0x17, 0xa8 }, + { 0x18, 0x18 }, + { 0x19, 0x50 }, + { 0x1a, 0xd8 }, + { 0x1b, 0xf5 }, + { 0x70, 0x40 }, + { 0x71, 0x58 }, + { 0x72, 0x30 }, + { 0x73, 0x48 }, + { 0x74, 0x20 }, + { 0x75, 0x60 }, + { 0x77, 0x20 }, + { 0x78, 0x32 }, + { 0x30, 0x03 }, + { 0x31, 0x40 }, + { 0x32, 0x10 }, + { 0x33, 0xe0 }, + { 0x34, 0xe0 }, + { 0x35, 0x00 }, + { 0x36, 0x80 }, + { 0x37, 0x00 }, + { 0x38, 0x04 }, + { 0x39, 0x09 }, + { 0x3a, 0x12 }, + { 0x3b, 0x1c }, + { 0x3c, 0x28 }, + { 0x3d, 0x31 }, + { 0x3e, 0x44 }, + { 0x3f, 0x57 }, + { 0x40, 0x6c }, + { 0x41, 0x81 }, + { 0x42, 0x94 }, + { 0x43, 0xa7 }, + { 0x44, 0xb8 }, + { 0x45, 0xd6 }, + { 0x46, 0xee }, + { 0x47, 0x0d }, + { 0x62, 0xf7 }, + { 0x63, 0x68 }, + { 0x64, 0xd3 }, + { 0x65, 0xd3 }, + { 0x66, 0x60 }, + { 0xfe, 0x00 }, + { 0x01, 0x32 }, + { 0x02, 0x0c }, + { 0x0f, 0x01 }, + { 0xe2, 0x00 }, + { 0xe3, 0x78 }, + { 0xe4, 0x00 }, + { 0xe5, 0xfe }, + { 0xe6, 0x01 }, + { 0xe7, 0xe0 }, + { 0xe8, 0x01 }, + { 0xe9, 0xe0 }, + { 0xea, 0x01 }, + { 0xeb, 0xe0 }, + { 0xfe, 0x00 }, +}; + +static const struct imgsensor_ops_s g_gc0308_ops = +{ + .is_available = gc0308_is_available, + .init = gc0308_init, + .uninit = gc0308_uninit, + .get_driver_name = gc0308_get_driver_name, + .validate_frame_setting = gc0308_validate_frame_setting, + .start_capture = gc0308_start_capture, + .stop_capture = gc0308_stop_capture, + .get_supported_value = gc0308_get_supported_value, + .get_value = gc0308_get_value, + .set_value = gc0308_set_value, +}; + +static const struct v4l2_fmtdesc g_gc0308_fmtdescs[] = +{ + { + .pixelformat = V4L2_PIX_FMT_RGB565, + .description = "RGB565", + }, +}; + +static const struct v4l2_frmsizeenum g_gc0308_frmsizes[] = +{ + { + .type = V4L2_FRMSIZE_TYPE_DISCRETE, + .discrete = + { + .width = GC0308_WIDTH, Review Comment: why not remove CONFIG_VIDEO_GC0308_VGAxx and support the multiple resolution ########## arch/xtensa/src/esp32s3/esp32s3_cam.c: ########## @@ -0,0 +1,808 @@ +/**************************************************************************** + * arch/xtensa/src/esp32s3/esp32s3_cam.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <inttypes.h> +#include <stdint.h> +#include <string.h> +#include <assert.h> +#include <errno.h> +#include <debug.h> + +#include <nuttx/spinlock.h> +#include <nuttx/kmalloc.h> +#include <nuttx/semaphore.h> +#include <nuttx/video/imgdata.h> + +#include <arch/board/board.h> + +#include "esp32s3_clockconfig.h" +#include "esp32s3_gpio.h" +#include "esp32s3_dma.h" +#include "esp32s3_irq.h" + +#include "xtensa.h" +#include "hardware/esp32s3_system.h" +#include "hardware/esp32s3_gpio_sigmap.h" +#include "hardware/esp32s3_lcd_cam.h" + +#include "periph_ctrl.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Use PLL=160MHz as clock resource for CAM */ + +#define ESP32S3_CAM_CLK_SEL 2 +#define ESP32S3_CAM_CLK_MHZ 160 + +/* XCLK = 160 / (6 + 2/3) = 24 MHz */ + +#define ESP32S3_CAM_CLKM_DIV_NUM 6 +#define ESP32S3_CAM_CLKM_DIV_A 3 +#define ESP32S3_CAM_CLKM_DIV_B 2 + +/* DMA buffer configuration */ + +#define ESP32S3_CAM_DMA_BUFLEN 4096 +#define ESP32S3_CAM_DMADESC_NUM 40 + +/* VSYNC filter threshold */ + +#define ESP32S3_CAM_VSYNC_FILTER 4 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct esp32s3_cam_s +{ + struct imgdata_s data; + + bool capturing; + + int cpuint; + uint8_t cpu; + int32_t dma_channel; + + struct esp32s3_dmadesc_s dmadesc[ESP32S3_CAM_DMADESC_NUM]; + + uint8_t *fb; /* Frame buffer */ + uint32_t fb_size; /* Frame buffer size */ + uint32_t fb_pos; /* Current write position */ + uint8_t vsync_cnt; /* VSYNC counter for frame sync */ + + imgdata_capture_t cb; /* Capture done callback */ + void *cb_arg; /* Callback argument */ + + uint16_t width; + uint16_t height; + uint32_t pixfmt; + + spinlock_t lock; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int esp32s3_cam_init(struct imgdata_s *data); +static int esp32s3_cam_uninit(struct imgdata_s *data); +static int esp32s3_cam_set_buf(struct imgdata_s *data, + uint8_t nr_datafmts, + imgdata_format_t *datafmts, + uint8_t *addr, uint32_t size); +static int esp32s3_cam_validate_frame_setting(struct imgdata_s *data, + uint8_t nr_datafmts, + imgdata_format_t *datafmts, + imgdata_interval_t *interval); +static int esp32s3_cam_start_capture(struct imgdata_s *data, + uint8_t nr_datafmts, + imgdata_format_t *datafmts, + imgdata_interval_t *interval, + imgdata_capture_t callback, + void *arg); +static int esp32s3_cam_stop_capture(struct imgdata_s *data); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct imgdata_ops_s g_cam_ops = +{ + .init = esp32s3_cam_init, + .uninit = esp32s3_cam_uninit, + .set_buf = esp32s3_cam_set_buf, + .validate_frame_setting = esp32s3_cam_validate_frame_setting, + .start_capture = esp32s3_cam_start_capture, + .stop_capture = esp32s3_cam_stop_capture, +}; + +static struct esp32s3_cam_s g_cam_priv = +{ + .data = + { + .ops = &g_cam_ops, + }, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: cam_interrupt + * + * Description: + * CAM VSYNC interrupt handler. Called when a frame is complete. + * + ****************************************************************************/ + +static int IRAM_ATTR cam_interrupt(int irq, void *context, void *arg) +{ + struct esp32s3_cam_s *priv = (struct esp32s3_cam_s *)arg; + uint32_t status = getreg32(LCD_CAM_LC_DMA_INT_ST_REG); + + /* Only handle CAM VSYNC interrupt */ + + if (status & LCD_CAM_CAM_VSYNC_INT_ST_M) + { + /* Clear interrupt */ + + putreg32(LCD_CAM_CAM_VSYNC_INT_CLR_M, + LCD_CAM_LC_DMA_INT_CLR_REG); + + if (priv->capturing) + { + priv->vsync_cnt++; + + /* First VSYNC = start of frame (DMA begins filling buffer). + * Second VSYNC = end of frame (buffer is complete). + */ + + if (priv->vsync_cnt == 1) + { + /* Frame capture just started, DMA is now receiving data. + * Nothing to do here — wait for next VSYNC. + */ + } + else if (priv->vsync_cnt >= 2 && priv->cb) + { + struct timeval ts; + uint32_t regval; + + /* Stop capture */ + + regval = getreg32(LCD_CAM_CAM_CTRL1_REG); + regval &= ~LCD_CAM_CAM_START_M; + putreg32(regval, LCD_CAM_CAM_CTRL1_REG); Review Comment: call modreg32 in all similar places ########## drivers/video/gc0308.c: ########## @@ -0,0 +1,861 @@ +/**************************************************************************** + * drivers/video/gc0308.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <stdint.h> +#include <string.h> +#include <errno.h> +#include <debug.h> + +#include <nuttx/i2c/i2c_master.h> +#include <nuttx/video/imgsensor.h> +#include <nuttx/arch.h> +#include <nuttx/video/video.h> + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define GC0308_I2C_ADDR 0x21 +#define GC0308_I2C_FREQ 100000 + +/* GC0308 Register Addresses */ + +#define GC0308_REG_CHIP_ID 0x00 +#define GC0308_CHIP_ID_VAL 0x9b + +/* Page 0 registers */ + +#define GC0308_REG_RESET 0xfe /* Page/reset register */ +#define GC0308_REG_HBLANK_H 0x01 +#define GC0308_REG_HBLANK_L 0x02 +#define GC0308_REG_VBLANK_H 0x03 +#define GC0308_REG_VBLANK_L 0x04 +#define GC0308_REG_SH_DELAY 0x05 +#define GC0308_REG_ROW_START_H 0x06 +#define GC0308_REG_ROW_START_L 0x07 +#define GC0308_REG_COL_START_H 0x08 +#define GC0308_REG_COL_START_L 0x09 +#define GC0308_REG_WIN_HEIGHT_H 0x0a +#define GC0308_REG_WIN_HEIGHT_L 0x0b +#define GC0308_REG_WIN_WIDTH_H 0x0c +#define GC0308_REG_WIN_WIDTH_L 0x0d +#define GC0308_REG_VS_ST 0x0e +#define GC0308_REG_VS_ET 0x0f +#define GC0308_REG_VB_HB 0x10 +#define GC0308_REG_RSH_WIDTH 0x11 +#define GC0308_REG_TSP_WIDTH 0x12 +#define GC0308_REG_SAMPLE_HOLD 0x13 +#define GC0308_REG_CISCTL_MODE1 0x14 +#define GC0308_REG_CISCTL_MODE2 0x15 +#define GC0308_REG_CISCTL_MODE3 0x16 +#define GC0308_REG_CISCTL_MODE4 0x17 + +/* Output format (Page 0) */ + +#define GC0308_REG_OUTPUT_FMT 0x24 /* Bits[3:0]: output format select */ +#define GC0308_REG_OUT_FORMAT 0x44 +#define GC0308_REG_OUT_EN 0x45 +#define GC0308_REG_SYNC_MODE 0x46 + +/* Analog & bias */ + +#define GC0308_REG_ANALOG_MODE1 0x1a +#define GC0308_REG_ANALOG_MODE2 0x1b + +/* Exposure */ + +#define GC0308_REG_EXP_H 0x03 +#define GC0308_REG_EXP_L 0x04 + +/* Gain */ + +#define GC0308_REG_GLOBAL_GAIN 0x50 + +/* Crop */ + +#define GC0308_REG_CROP_WIN_MODE 0x46 +#define GC0308_REG_CROP_Y1_H 0x47 +#define GC0308_REG_CROP_Y1_L 0x48 +#define GC0308_REG_CROP_X1_H 0x49 +#define GC0308_REG_CROP_X1_L 0x4a +#define GC0308_REG_CROP_WIN_H_H 0x4b +#define GC0308_REG_CROP_WIN_H_L 0x4c +#define GC0308_REG_CROP_WIN_W_H 0x4d +#define GC0308_REG_CROP_WIN_W_L 0x4e + +/* Subsample (Page 0) */ + +#define GC0308_REG_SUBSAMPLE 0x59 +#define GC0308_REG_SUB_MODE 0x5a +#define GC0308_REG_SUB_ROW_N1 0x5b +#define GC0308_REG_SUB_ROW_N2 0x5c +#define GC0308_REG_SUB_COL_N1 0x5d +#define GC0308_REG_SUB_COL_N2 0x5e + +/* Subsample control (Page 1) */ + +#define GC0308_P1_REG_SUB_CTRL 0x53 /* Bit7: subsample enable */ +#define GC0308_P1_REG_SUB_RATIO 0x54 /* H[7:4] V[3:0] ratio */ +#define GC0308_P1_REG_SUB_EN 0x55 /* Bit0: subsample output enable */ +#define GC0308_P1_REG_SUB_HOFF 0x56 /* H offset */ +#define GC0308_P1_REG_SUB_VOFF 0x57 /* V offset */ +#define GC0308_P1_REG_SUB_HSIZE 0x58 /* H size adjust */ +#define GC0308_P1_REG_SUB_VSIZE 0x59 /* V size adjust */ + +/* Configured resolution */ + +#ifdef CONFIG_VIDEO_GC0308_VGA +# define GC0308_WIDTH 640 +# define GC0308_HEIGHT 480 +#elif defined(CONFIG_VIDEO_GC0308_QVGA) +# define GC0308_WIDTH 320 +# define GC0308_HEIGHT 240 +#elif defined(CONFIG_VIDEO_GC0308_QQVGA) +# define GC0308_WIDTH 160 +# define GC0308_HEIGHT 120 +#else +# error "No GC0308 resolution selected" +#endif + +/* Mirror/flip */ + +#define GC0308_REG_CISCTL_MODE1_MIRROR 0x14 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct gc0308_reg_s +{ + uint8_t addr; + uint8_t val; +}; + +struct gc0308_dev_s +{ + struct imgsensor_s sensor; + struct i2c_master_s *i2c; + bool streaming; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static bool gc0308_is_available(struct imgsensor_s *sensor); +static int gc0308_init(struct imgsensor_s *sensor); +static int gc0308_uninit(struct imgsensor_s *sensor); +static const char *gc0308_get_driver_name(struct imgsensor_s *sensor); +static int gc0308_validate_frame_setting(struct imgsensor_s *sensor, + imgsensor_stream_type_t type, + uint8_t nr_datafmts, + imgsensor_format_t *datafmts, + imgsensor_interval_t *interval); +static int gc0308_start_capture(struct imgsensor_s *sensor, + imgsensor_stream_type_t type, + uint8_t nr_datafmts, + imgsensor_format_t *datafmts, + imgsensor_interval_t *interval); +static int gc0308_stop_capture(struct imgsensor_s *sensor, + imgsensor_stream_type_t type); +static int gc0308_get_supported_value(struct imgsensor_s *sensor, + uint32_t id, + imgsensor_supported_value_t *value); +static int gc0308_get_value(struct imgsensor_s *sensor, + uint32_t id, uint32_t size, imgsensor_value_t *value); +static int gc0308_set_value(struct imgsensor_s *sensor, + uint32_t id, uint32_t size, imgsensor_value_t value); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* GC0308 initialization register table from espressif/esp32-camera vendor + * driver. This is the proven register set for GC0308 VGA output. + */ + +static const struct gc0308_reg_s g_gc0308_init_regs[] = +{ + { 0xfe, 0x00 }, + { 0xec, 0x20 }, + { 0x05, 0x00 }, + { 0x06, 0x00 }, + { 0x07, 0x00 }, + { 0x08, 0x00 }, + { 0x09, 0x01 }, + { 0x0a, 0xe8 }, + { 0x0b, 0x02 }, + { 0x0c, 0x88 }, + { 0x0d, 0x02 }, + { 0x0e, 0x02 }, + { 0x10, 0x26 }, + { 0x11, 0x0d }, + { 0x12, 0x2a }, + { 0x13, 0x00 }, + { 0x14, 0x10 }, + { 0x15, 0x0a }, + { 0x16, 0x05 }, + { 0x17, 0x01 }, + { 0x18, 0x44 }, + { 0x19, 0x44 }, + { 0x1a, 0x2a }, + { 0x1b, 0x00 }, + { 0x1c, 0x49 }, + { 0x1d, 0x9a }, + { 0x1e, 0x61 }, + { 0x1f, 0x00 }, + { 0x20, 0x7f }, + { 0x21, 0xfa }, + { 0x22, 0x57 }, + { 0x24, 0xa2 }, /* YCbYCr output format */ + { 0x25, 0x0f }, + { 0x26, 0x03 }, + { 0x28, 0x00 }, + { 0x2d, 0x0a }, + { 0x2f, 0x01 }, + { 0x30, 0xf7 }, + { 0x31, 0x50 }, + { 0x32, 0x00 }, + { 0x33, 0x28 }, + { 0x34, 0x2a }, + { 0x35, 0x28 }, + { 0x39, 0x04 }, + { 0x3a, 0x20 }, + { 0x3b, 0x20 }, + { 0x3c, 0x00 }, + { 0x3d, 0x00 }, + { 0x3e, 0x00 }, + { 0x3f, 0x00 }, + { 0x50, 0x14 }, /* Global gain */ + { 0x52, 0x41 }, + { 0x53, 0x80 }, + { 0x54, 0x80 }, + { 0x55, 0x80 }, + { 0x56, 0x80 }, + { 0x5a, 0x56 }, /* AWB R gain */ + { 0x5b, 0x40 }, /* AWB G gain */ + { 0x5c, 0x4a }, /* AWB B gain */ + { 0x8b, 0x20 }, + { 0x8c, 0x20 }, + { 0x8d, 0x20 }, + { 0x8e, 0x14 }, + { 0x8f, 0x10 }, + { 0x90, 0x14 }, + { 0x91, 0x3c }, + { 0x92, 0x50 }, + { 0x5d, 0x12 }, + { 0x5e, 0x1a }, + { 0x5f, 0x24 }, + { 0x60, 0x07 }, + { 0x61, 0x15 }, + { 0x62, 0x08 }, + { 0x64, 0x03 }, + { 0x66, 0xe8 }, + { 0x67, 0x86 }, + { 0x68, 0x82 }, + { 0x69, 0x18 }, + { 0x6a, 0x0f }, + { 0x6b, 0x00 }, + { 0x6c, 0x5f }, + { 0x6d, 0x8f }, + { 0x6e, 0x55 }, + { 0x6f, 0x38 }, + { 0x70, 0x15 }, + { 0x71, 0x33 }, + { 0x72, 0xdc }, + { 0x73, 0x00 }, + { 0x74, 0x02 }, + { 0x75, 0x3f }, + { 0x76, 0x02 }, + { 0x77, 0x38 }, + { 0x78, 0x88 }, + { 0x79, 0x81 }, + { 0x7a, 0x81 }, + { 0x7b, 0x22 }, + { 0x7c, 0xff }, + { 0x93, 0x48 }, /* Color matrix */ + { 0x94, 0x02 }, + { 0x95, 0x07 }, + { 0x96, 0xe0 }, + { 0x97, 0x40 }, + { 0x98, 0xf0 }, + { 0xb1, 0x40 }, /* Saturation */ + { 0xb2, 0x40 }, + { 0xb3, 0x40 }, + { 0xb6, 0xe0 }, + { 0xbd, 0x38 }, + { 0xbe, 0x36 }, + { 0xd0, 0xcb }, /* AEC */ + { 0xd1, 0x10 }, + { 0xd2, 0x90 }, + { 0xd3, 0x48 }, + { 0xd5, 0xf2 }, + { 0xd6, 0x16 }, + { 0xdb, 0x92 }, + { 0xdc, 0xa5 }, + { 0xdf, 0x23 }, + { 0xd9, 0x00 }, + { 0xda, 0x00 }, + { 0xe0, 0x09 }, + { 0xed, 0x04 }, + { 0xee, 0xa0 }, + { 0xef, 0x40 }, + { 0x80, 0x03 }, + { 0x9f, 0x10 }, + { 0xa0, 0x20 }, + { 0xa1, 0x38 }, + { 0xa2, 0x4e }, + { 0xa3, 0x63 }, + { 0xa4, 0x76 }, + { 0xa5, 0x87 }, + { 0xa6, 0xa2 }, + { 0xa7, 0xb8 }, + { 0xa8, 0xca }, + { 0xa9, 0xd8 }, + { 0xaa, 0xe3 }, + { 0xab, 0xeb }, + { 0xac, 0xf0 }, + { 0xad, 0xf8 }, + { 0xae, 0xfd }, + { 0xaf, 0xff }, + { 0xc0, 0x00 }, + { 0xc1, 0x10 }, + { 0xc2, 0x1c }, + { 0xc3, 0x30 }, + { 0xc4, 0x43 }, + { 0xc5, 0x54 }, + { 0xc6, 0x65 }, + { 0xc7, 0x75 }, + { 0xc8, 0x93 }, + { 0xc9, 0xb0 }, + { 0xca, 0xcb }, + { 0xcb, 0xe6 }, + { 0xcc, 0xff }, + { 0xf0, 0x02 }, + { 0xf1, 0x01 }, + { 0xf2, 0x02 }, + { 0xf3, 0x30 }, + { 0xf7, 0x04 }, + { 0xf8, 0x02 }, + { 0xf9, 0x9f }, + { 0xfa, 0x78 }, + { 0xfe, 0x01 }, + { 0x00, 0xf5 }, + { 0x02, 0x20 }, + { 0x04, 0x10 }, + { 0x05, 0x08 }, + { 0x06, 0x20 }, + { 0x08, 0x0a }, + { 0x0a, 0xa0 }, + { 0x0b, 0x60 }, + { 0x0c, 0x08 }, + { 0x0e, 0x44 }, + { 0x0f, 0x32 }, + { 0x10, 0x41 }, + { 0x11, 0x37 }, + { 0x12, 0x22 }, + { 0x13, 0x19 }, + { 0x14, 0x44 }, + { 0x15, 0x44 }, + { 0x16, 0xc2 }, + { 0x17, 0xa8 }, + { 0x18, 0x18 }, + { 0x19, 0x50 }, + { 0x1a, 0xd8 }, + { 0x1b, 0xf5 }, + { 0x70, 0x40 }, + { 0x71, 0x58 }, + { 0x72, 0x30 }, + { 0x73, 0x48 }, + { 0x74, 0x20 }, + { 0x75, 0x60 }, + { 0x77, 0x20 }, + { 0x78, 0x32 }, + { 0x30, 0x03 }, + { 0x31, 0x40 }, + { 0x32, 0x10 }, + { 0x33, 0xe0 }, + { 0x34, 0xe0 }, + { 0x35, 0x00 }, + { 0x36, 0x80 }, + { 0x37, 0x00 }, + { 0x38, 0x04 }, + { 0x39, 0x09 }, + { 0x3a, 0x12 }, + { 0x3b, 0x1c }, + { 0x3c, 0x28 }, + { 0x3d, 0x31 }, + { 0x3e, 0x44 }, + { 0x3f, 0x57 }, + { 0x40, 0x6c }, + { 0x41, 0x81 }, + { 0x42, 0x94 }, + { 0x43, 0xa7 }, + { 0x44, 0xb8 }, + { 0x45, 0xd6 }, + { 0x46, 0xee }, + { 0x47, 0x0d }, + { 0x62, 0xf7 }, + { 0x63, 0x68 }, + { 0x64, 0xd3 }, + { 0x65, 0xd3 }, + { 0x66, 0x60 }, + { 0xfe, 0x00 }, + { 0x01, 0x32 }, + { 0x02, 0x0c }, + { 0x0f, 0x01 }, + { 0xe2, 0x00 }, + { 0xe3, 0x78 }, + { 0xe4, 0x00 }, + { 0xe5, 0xfe }, + { 0xe6, 0x01 }, + { 0xe7, 0xe0 }, + { 0xe8, 0x01 }, + { 0xe9, 0xe0 }, + { 0xea, 0x01 }, + { 0xeb, 0xe0 }, + { 0xfe, 0x00 }, +}; + +static const struct imgsensor_ops_s g_gc0308_ops = +{ + .is_available = gc0308_is_available, + .init = gc0308_init, + .uninit = gc0308_uninit, + .get_driver_name = gc0308_get_driver_name, + .validate_frame_setting = gc0308_validate_frame_setting, + .start_capture = gc0308_start_capture, + .stop_capture = gc0308_stop_capture, + .get_supported_value = gc0308_get_supported_value, + .get_value = gc0308_get_value, + .set_value = gc0308_set_value, +}; + +static const struct v4l2_fmtdesc g_gc0308_fmtdescs[] = +{ + { + .pixelformat = V4L2_PIX_FMT_RGB565, + .description = "RGB565", + }, +}; + +static const struct v4l2_frmsizeenum g_gc0308_frmsizes[] = +{ + { + .type = V4L2_FRMSIZE_TYPE_DISCRETE, + .discrete = + { + .width = GC0308_WIDTH, + .height = GC0308_HEIGHT, + }, + }, +}; + +static const struct v4l2_frmivalenum g_gc0308_frmintervals[] = +{ + { + .type = V4L2_FRMIVAL_TYPE_DISCRETE, + .discrete = + { + .numerator = 1, + .denominator = 30, + }, + }, +}; + +static struct gc0308_dev_s g_gc0308_priv; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: gc0308_putreg + ****************************************************************************/ + +static int gc0308_putreg(struct i2c_master_s *i2c, + uint8_t regaddr, uint8_t regval) +{ + struct i2c_msg_s msg; + uint8_t buf[2]; + int ret; + + buf[0] = regaddr; + buf[1] = regval; + + msg.frequency = GC0308_I2C_FREQ; + msg.addr = GC0308_I2C_ADDR; + msg.flags = 0; + msg.buffer = buf; + msg.length = 2; + + ret = I2C_TRANSFER(i2c, &msg, 1); + if (ret < 0) + { + snerr("ERROR: I2C write to 0x%02x failed: %d\n", regaddr, ret); + } + + return ret; +} + +/**************************************************************************** + * Name: gc0308_getreg + ****************************************************************************/ + +static int gc0308_getreg(struct i2c_master_s *i2c, + uint8_t regaddr, uint8_t *regval) +{ + struct i2c_msg_s msg[2]; + int ret; + + msg[0].frequency = GC0308_I2C_FREQ; + msg[0].addr = GC0308_I2C_ADDR; + msg[0].flags = 0; + msg[0].buffer = ®addr; + msg[0].length = 1; + + msg[1].frequency = GC0308_I2C_FREQ; + msg[1].addr = GC0308_I2C_ADDR; + msg[1].flags = I2C_M_READ; + msg[1].buffer = regval; + msg[1].length = 1; + + ret = I2C_TRANSFER(i2c, msg, 2); + if (ret < 0) + { + snerr("ERROR: I2C read from 0x%02x failed: %d\n", regaddr, ret); + } + + return ret; +} + +/**************************************************************************** + * Name: gc0308_putreglist + ****************************************************************************/ + +static int gc0308_putreglist(struct i2c_master_s *i2c, + const struct gc0308_reg_s *reglist, + size_t nentries) +{ + int ret; + + for (size_t i = 0; i < nentries; i++) + { + ret = gc0308_putreg(i2c, reglist[i].addr, reglist[i].val); + if (ret < 0) + { + snerr("GC0308 write[%d] 0x%02x=0x%02x FAILED: %d\n", + (int)i, reglist[i].addr, reglist[i].val, ret); + return ret; + } + } + + return OK; +} + +/**************************************************************************** + * Name: gc0308_is_available + ****************************************************************************/ + +static bool gc0308_is_available(struct imgsensor_s *sensor) +{ + struct gc0308_dev_s *priv = (struct gc0308_dev_s *)sensor; + uint8_t id = 0; + int ret; + + /* Select page 0 */ + + gc0308_putreg(priv->i2c, GC0308_REG_RESET, 0x00); + + ret = gc0308_getreg(priv->i2c, GC0308_REG_CHIP_ID, &id); + if (ret < 0) + { + return false; + } + + sninfo("GC0308 chip ID: 0x%02x (expected 0x%02x)\n", + id, GC0308_CHIP_ID_VAL); + + return (id == GC0308_CHIP_ID_VAL); +} + +/**************************************************************************** + * Name: gc0308_init + ****************************************************************************/ + +static int gc0308_init(struct imgsensor_s *sensor) +{ + struct gc0308_dev_s *priv = (struct gc0308_dev_s *)sensor; + int ret; + + /* Software reset per vendor: write 0xf0 to reg 0xfe, wait 80ms */ + + ret = gc0308_putreg(priv->i2c, 0xfe, 0xf0); + if (ret < 0) + { + snerr("GC0308 soft reset failed: %d\n", ret); + return ret; + } + + up_mdelay(80); + + /* Write vendor initialization register table */ + + ret = gc0308_putreglist(priv->i2c, g_gc0308_init_regs, + sizeof(g_gc0308_init_regs) / + sizeof(g_gc0308_init_regs[0])); + if (ret < 0) + { + snerr("GC0308 init regs failed: %d\n", ret); + return ret; + } + + up_mdelay(80); + + /* Set RGB565 output format: reg 0x24 bits[3:0] = 6 */ + + ret = gc0308_putreg(priv->i2c, GC0308_REG_RESET, 0x00); + if (ret < 0) + { + return ret; + } + + uint8_t fmt = 0; + gc0308_getreg(priv->i2c, GC0308_REG_OUTPUT_FMT, &fmt); + fmt = (fmt & 0xf0) | 0x06; /* RGB565 */ + ret = gc0308_putreg(priv->i2c, GC0308_REG_OUTPUT_FMT, fmt); + if (ret < 0) + { + return ret; + } + +#ifndef CONFIG_VIDEO_GC0308_VGA + /* Non-VGA resolutions need subsample — switch to page 1 */ + + ret = gc0308_putreg(priv->i2c, GC0308_REG_RESET, 0x01); + if (ret < 0) + { + return ret; + } + + uint8_t val = 0; + gc0308_getreg(priv->i2c, GC0308_P1_REG_SUB_CTRL, &val); + ret = gc0308_putreg(priv->i2c, GC0308_P1_REG_SUB_CTRL, val | 0x80); Review Comment: add gc0308_modreg and replace gc0308_getreg+gc0308_putreg ########## arch/xtensa/src/esp32s3/esp32s3_cam.c: ########## @@ -0,0 +1,808 @@ +/**************************************************************************** + * arch/xtensa/src/esp32s3/esp32s3_cam.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <inttypes.h> +#include <stdint.h> +#include <string.h> +#include <assert.h> +#include <errno.h> +#include <debug.h> + +#include <nuttx/spinlock.h> +#include <nuttx/kmalloc.h> +#include <nuttx/semaphore.h> +#include <nuttx/video/imgdata.h> + +#include <arch/board/board.h> + +#include "esp32s3_clockconfig.h" +#include "esp32s3_gpio.h" +#include "esp32s3_dma.h" +#include "esp32s3_irq.h" + +#include "xtensa.h" +#include "hardware/esp32s3_system.h" +#include "hardware/esp32s3_gpio_sigmap.h" +#include "hardware/esp32s3_lcd_cam.h" + +#include "periph_ctrl.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Use PLL=160MHz as clock resource for CAM */ + +#define ESP32S3_CAM_CLK_SEL 2 +#define ESP32S3_CAM_CLK_MHZ 160 + +/* XCLK = 160 / (6 + 2/3) = 24 MHz */ + +#define ESP32S3_CAM_CLKM_DIV_NUM 6 +#define ESP32S3_CAM_CLKM_DIV_A 3 +#define ESP32S3_CAM_CLKM_DIV_B 2 + +/* DMA buffer configuration */ + +#define ESP32S3_CAM_DMA_BUFLEN 4096 +#define ESP32S3_CAM_DMADESC_NUM 40 + +/* VSYNC filter threshold */ + +#define ESP32S3_CAM_VSYNC_FILTER 4 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct esp32s3_cam_s +{ + struct imgdata_s data; + + bool capturing; + + int cpuint; + uint8_t cpu; + int32_t dma_channel; + + struct esp32s3_dmadesc_s dmadesc[ESP32S3_CAM_DMADESC_NUM]; + + uint8_t *fb; /* Frame buffer */ + uint32_t fb_size; /* Frame buffer size */ + uint32_t fb_pos; /* Current write position */ + uint8_t vsync_cnt; /* VSYNC counter for frame sync */ + + imgdata_capture_t cb; /* Capture done callback */ + void *cb_arg; /* Callback argument */ + + uint16_t width; + uint16_t height; + uint32_t pixfmt; + + spinlock_t lock; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int esp32s3_cam_init(struct imgdata_s *data); +static int esp32s3_cam_uninit(struct imgdata_s *data); +static int esp32s3_cam_set_buf(struct imgdata_s *data, + uint8_t nr_datafmts, + imgdata_format_t *datafmts, + uint8_t *addr, uint32_t size); +static int esp32s3_cam_validate_frame_setting(struct imgdata_s *data, + uint8_t nr_datafmts, + imgdata_format_t *datafmts, + imgdata_interval_t *interval); +static int esp32s3_cam_start_capture(struct imgdata_s *data, + uint8_t nr_datafmts, + imgdata_format_t *datafmts, + imgdata_interval_t *interval, + imgdata_capture_t callback, + void *arg); +static int esp32s3_cam_stop_capture(struct imgdata_s *data); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct imgdata_ops_s g_cam_ops = +{ + .init = esp32s3_cam_init, + .uninit = esp32s3_cam_uninit, + .set_buf = esp32s3_cam_set_buf, + .validate_frame_setting = esp32s3_cam_validate_frame_setting, + .start_capture = esp32s3_cam_start_capture, + .stop_capture = esp32s3_cam_stop_capture, +}; + +static struct esp32s3_cam_s g_cam_priv = +{ + .data = + { + .ops = &g_cam_ops, + }, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: cam_interrupt + * + * Description: + * CAM VSYNC interrupt handler. Called when a frame is complete. + * + ****************************************************************************/ + +static int IRAM_ATTR cam_interrupt(int irq, void *context, void *arg) +{ + struct esp32s3_cam_s *priv = (struct esp32s3_cam_s *)arg; + uint32_t status = getreg32(LCD_CAM_LC_DMA_INT_ST_REG); + + /* Only handle CAM VSYNC interrupt */ + + if (status & LCD_CAM_CAM_VSYNC_INT_ST_M) + { + /* Clear interrupt */ + + putreg32(LCD_CAM_CAM_VSYNC_INT_CLR_M, + LCD_CAM_LC_DMA_INT_CLR_REG); + + if (priv->capturing) + { + priv->vsync_cnt++; + + /* First VSYNC = start of frame (DMA begins filling buffer). + * Second VSYNC = end of frame (buffer is complete). + */ + + if (priv->vsync_cnt == 1) + { + /* Frame capture just started, DMA is now receiving data. + * Nothing to do here — wait for next VSYNC. + */ + } + else if (priv->vsync_cnt >= 2 && priv->cb) + { + struct timeval ts; + uint32_t regval; + + /* Stop capture */ + + regval = getreg32(LCD_CAM_CAM_CTRL1_REG); + regval &= ~LCD_CAM_CAM_START_M; + putreg32(regval, LCD_CAM_CAM_CTRL1_REG); + + regval = getreg32(LCD_CAM_CAM_CTRL_REG); + regval |= LCD_CAM_CAM_UPDATE_REG_M; + putreg32(regval, LCD_CAM_CAM_CTRL_REG); + + gettimeofday(&ts, NULL); + + /* Notify frame complete */ + + priv->cb(0, priv->fb_size, &ts, priv->cb_arg); + + /* Restart capture for next frame */ + + priv->vsync_cnt = 0; + + /* Reset DMA */ + + esp32s3_dma_reset_channel(priv->dma_channel, false); + + /* Reset CAM + AFIFO */ + + regval = getreg32(LCD_CAM_CAM_CTRL1_REG); + regval |= LCD_CAM_CAM_RESET_M; + putreg32(regval, LCD_CAM_CAM_CTRL1_REG); + regval &= ~LCD_CAM_CAM_RESET_M; + putreg32(regval, LCD_CAM_CAM_CTRL1_REG); + + regval |= LCD_CAM_CAM_AFIFO_RESET_M; + putreg32(regval, LCD_CAM_CAM_CTRL1_REG); + regval &= ~LCD_CAM_CAM_AFIFO_RESET_M; + putreg32(regval, LCD_CAM_CAM_CTRL1_REG); + + /* Re-set REC_DATA_BYTELEN after reset */ + + regval &= ~LCD_CAM_CAM_REC_DATA_BYTELEN_M; + regval |= ((ESP32S3_CAM_DMA_BUFLEN - 1) + << LCD_CAM_CAM_REC_DATA_BYTELEN_S); + putreg32(regval, LCD_CAM_CAM_CTRL1_REG); + + /* Reload DMA descriptors */ + + esp32s3_dma_load(priv->dmadesc, priv->dma_channel, false); + esp32s3_dma_enable(priv->dma_channel, false); + + /* Restart */ + + regval = getreg32(LCD_CAM_CAM_CTRL1_REG); + regval |= LCD_CAM_CAM_START_M; + putreg32(regval, LCD_CAM_CAM_CTRL1_REG); + + regval = getreg32(LCD_CAM_CAM_CTRL_REG); + regval |= LCD_CAM_CAM_UPDATE_REG_M; + putreg32(regval, LCD_CAM_CAM_CTRL_REG); + } + } + } + + /* Clear any CAM HS interrupt too */ + + if (status & LCD_CAM_CAM_HS_INT_ST_M) + { + putreg32(LCD_CAM_CAM_HS_INT_CLR_M, + LCD_CAM_LC_DMA_INT_CLR_REG); + } + + return 0; +} + +/**************************************************************************** + * Name: esp32s3_cam_gpio_config + * + * Description: + * Configure GPIO pins for DVP camera interface. + * + ****************************************************************************/ + +static void esp32s3_cam_gpio_config(void) +{ + /* Data pins D0-D7 as input via GPIO matrix */ + + esp32s3_configgpio(CONFIG_ESP32S3_CAM_D0_PIN, INPUT); + esp32s3_gpio_matrix_in(CONFIG_ESP32S3_CAM_D0_PIN, + CAM_DATA_IN0_IDX, false); + + esp32s3_configgpio(CONFIG_ESP32S3_CAM_D1_PIN, INPUT); + esp32s3_gpio_matrix_in(CONFIG_ESP32S3_CAM_D1_PIN, + CAM_DATA_IN1_IDX, false); + + esp32s3_configgpio(CONFIG_ESP32S3_CAM_D2_PIN, INPUT); + esp32s3_gpio_matrix_in(CONFIG_ESP32S3_CAM_D2_PIN, + CAM_DATA_IN2_IDX, false); + + esp32s3_configgpio(CONFIG_ESP32S3_CAM_D3_PIN, INPUT); + esp32s3_gpio_matrix_in(CONFIG_ESP32S3_CAM_D3_PIN, + CAM_DATA_IN3_IDX, false); + + esp32s3_configgpio(CONFIG_ESP32S3_CAM_D4_PIN, INPUT); + esp32s3_gpio_matrix_in(CONFIG_ESP32S3_CAM_D4_PIN, + CAM_DATA_IN4_IDX, false); + + esp32s3_configgpio(CONFIG_ESP32S3_CAM_D5_PIN, INPUT); + esp32s3_gpio_matrix_in(CONFIG_ESP32S3_CAM_D5_PIN, + CAM_DATA_IN5_IDX, false); + + esp32s3_configgpio(CONFIG_ESP32S3_CAM_D6_PIN, INPUT); + esp32s3_gpio_matrix_in(CONFIG_ESP32S3_CAM_D6_PIN, + CAM_DATA_IN6_IDX, false); + + esp32s3_configgpio(CONFIG_ESP32S3_CAM_D7_PIN, INPUT); + esp32s3_gpio_matrix_in(CONFIG_ESP32S3_CAM_D7_PIN, + CAM_DATA_IN7_IDX, false); + + /* PCLK input */ + + esp32s3_configgpio(CONFIG_ESP32S3_CAM_PCLK_PIN, INPUT); + esp32s3_gpio_matrix_in(CONFIG_ESP32S3_CAM_PCLK_PIN, + CAM_PCLK_IDX, false); + + /* VSYNC input */ + + esp32s3_configgpio(CONFIG_ESP32S3_CAM_VSYNC_PIN, INPUT); + esp32s3_gpio_matrix_in(CONFIG_ESP32S3_CAM_VSYNC_PIN, + CAM_V_SYNC_IDX, false); + + /* HREF (H_ENABLE) input */ + + esp32s3_configgpio(CONFIG_ESP32S3_CAM_HREF_PIN, INPUT); + esp32s3_gpio_matrix_in(CONFIG_ESP32S3_CAM_HREF_PIN, + CAM_H_ENABLE_IDX, false); + + /* XCLK output to sensor */ + + esp32s3_configgpio(CONFIG_ESP32S3_CAM_XCLK_PIN, OUTPUT); + esp32s3_gpio_matrix_out(CONFIG_ESP32S3_CAM_XCLK_PIN, + CAM_CLK_IDX, false, false); +} + +/**************************************************************************** + * Name: esp32s3_cam_enableclk + * + * Description: + * Enable CAM clock and configure XCLK output. + * + ****************************************************************************/ + +static void esp32s3_cam_enableclk(void) +{ + uint32_t regval; + + /* Enable LCD_CAM peripheral clock (shared with LCD) */ + + periph_module_enable(PERIPH_LCD_CAM_MODULE); + + /* Configure CAM clock: + * CLK_SEL = 3 (PLL 160MHz) + * DIV_NUM = 6, DIV_A = 3, DIV_B = 2 + * XCLK = 160 / (6 + 2/3) = 24 MHz + */ + + regval = (ESP32S3_CAM_CLK_SEL << LCD_CAM_CAM_CLK_SEL_S) | + (ESP32S3_CAM_CLKM_DIV_A << LCD_CAM_CAM_CLKM_DIV_A_S) | + (ESP32S3_CAM_CLKM_DIV_B << LCD_CAM_CAM_CLKM_DIV_B_S) | + (ESP32S3_CAM_CLKM_DIV_NUM << LCD_CAM_CAM_CLKM_DIV_NUM_S) | + LCD_CAM_CAM_VS_EOF_EN_M | /* VSYNC triggers DMA EOF */ + (ESP32S3_CAM_VSYNC_FILTER << LCD_CAM_CAM_VSYNC_FILTER_THRES_S); + putreg32(regval, LCD_CAM_CAM_CTRL_REG); +} + +/**************************************************************************** + * Name: esp32s3_cam_dmasetup + * + * Description: + * Configure DMA for camera RX. + * + ****************************************************************************/ + +static int esp32s3_cam_dmasetup(struct esp32s3_cam_s *priv) +{ + priv->dma_channel = esp32s3_dma_request(ESP32S3_DMA_PERIPH_LCDCAM, + 1, 10, true); + if (priv->dma_channel < 0) + { + snerr("ERROR: Failed to allocate GDMA channel\n"); + return -ENOMEM; + } + + esp32s3_dma_set_ext_memblk(priv->dma_channel, + false, + ESP32S3_DMA_EXT_MEMBLK_64B); + + return OK; +} + +/**************************************************************************** + * Name: esp32s3_cam_config + * + * Description: + * Configure CAM controller registers and interrupts. + * + ****************************************************************************/ + +static int esp32s3_cam_config(struct esp32s3_cam_s *priv) +{ + int ret; + uint32_t regval; + irqstate_t flags; + + /* Reset CAM module and AFIFO first */ + + regval = LCD_CAM_CAM_RESET_M | LCD_CAM_CAM_AFIFO_RESET_M; + putreg32(regval, LCD_CAM_CAM_CTRL1_REG); + + /* Configure CAM_CTRL1 after reset: + * - VSYNC filter enable + * - REC_DATA_BYTELEN = DMA node size - 1 (controls in_suc_eof) + * Note: 8-bit DVP sensor outputs BE RGB565 (high byte first). + * CAM_BYTE_ORDER only reverses bits within a byte in + * 1-byte mode, so HW byte swap is not available here. + * Application must swap bytes if LE RGB565 is needed. + */ + + regval = LCD_CAM_CAM_VSYNC_FILTER_EN_M | + ((ESP32S3_CAM_DMA_BUFLEN - 1) << LCD_CAM_CAM_REC_DATA_BYTELEN_S); + putreg32(regval, LCD_CAM_CAM_CTRL1_REG); + + /* Update registers */ + + regval = getreg32(LCD_CAM_CAM_CTRL_REG); + regval |= LCD_CAM_CAM_UPDATE_REG_M; + putreg32(regval, LCD_CAM_CAM_CTRL_REG); + + /* Bypass RGB/YUV conversion (bit=0 means bypass) */ + + putreg32(0, LCD_CAM_CAM_RGB_YUV_REG); + + /* Setup DMA */ + + if (esp32s3_cam_dmasetup(priv) != OK) + { + return -ENOMEM; + } + + /* Disable all LCD_CAM interrupts and clear pending */ + + putreg32(0, LCD_CAM_LC_DMA_INT_ENA_REG); + putreg32(0xffffffff, LCD_CAM_LC_DMA_INT_CLR_REG); + + /* Setup interrupt with CPU interrupts disabled. + * Order matters: + * 1) setup_irq creates cpuint → peripheral IRQ mapping + * 2) irq_attach registers the handler for that IRQ + * Since spin_lock_irqsave masks CPU interrupts, even if the + * peripheral line is asserted after setup_irq, the interrupt + * won't be serviced until after irq_attach completes. + */ + + flags = spin_lock_irqsave(&priv->lock); + + priv->cpu = this_cpu(); + + /* 1) Map peripheral → CPU interrupt (creates IRQ mapping) */ + + priv->cpuint = esp32s3_setup_irq(priv->cpu, + ESP32S3_PERIPH_LCD_CAM, + ESP32S3_INT_PRIO_DEF, + ESP32S3_CPUINT_LEVEL); + DEBUGASSERT(priv->cpuint >= 0); + + /* 2) Attach handler (mapping now exists, so IRQ is valid) */ + + ret = irq_attach(ESP32S3_IRQ_LCD_CAM, cam_interrupt, priv); + DEBUGASSERT(ret == 0); + UNUSED(ret); + + /* 3) Clear any spurious interrupt that fired during setup */ + + putreg32(0xffffffff, LCD_CAM_LC_DMA_INT_CLR_REG); + + spin_unlock_irqrestore(&priv->lock, flags); + + up_enable_irq(ESP32S3_IRQ_LCD_CAM); + + /* Enable CAM VSYNC interrupt in hardware */ + + regval = getreg32(LCD_CAM_LC_DMA_INT_ENA_REG); + regval |= LCD_CAM_CAM_VSYNC_INT_ENA_M; + putreg32(regval, LCD_CAM_LC_DMA_INT_ENA_REG); + + return OK; +} + +/**************************************************************************** + * Name: esp32s3_cam_init + ****************************************************************************/ + +static int esp32s3_cam_init(struct imgdata_s *data) +{ + struct esp32s3_cam_s *priv = (struct esp32s3_cam_s *)data; + + priv->capturing = false; + priv->cb = NULL; + priv->cb_arg = NULL; + priv->fb = NULL; + priv->fb_size = 0; + priv->fb_pos = 0; + priv->width = 0; + priv->height = 0; + priv->pixfmt = 0; + + spin_lock_init(&priv->lock); + + /* Configure GPIO pins */ + + esp32s3_cam_gpio_config(); + + /* Enable clock and configure XCLK */ + + esp32s3_cam_enableclk(); + + /* Configure CAM controller */ + + return esp32s3_cam_config(priv); +} + +/**************************************************************************** + * Name: esp32s3_cam_uninit + ****************************************************************************/ + +static int esp32s3_cam_uninit(struct imgdata_s *data) +{ + struct esp32s3_cam_s *priv = (struct esp32s3_cam_s *)data; + uint32_t regval; + + /* Stop capture */ + + regval = getreg32(LCD_CAM_CAM_CTRL1_REG); + regval &= ~LCD_CAM_CAM_START_M; + putreg32(regval, LCD_CAM_CAM_CTRL1_REG); + + /* Disable interrupt */ + + up_disable_irq(ESP32S3_IRQ_LCD_CAM); + irq_detach(ESP32S3_IRQ_LCD_CAM); + esp32s3_teardown_irq(priv->cpu, ESP32S3_PERIPH_LCD_CAM, priv->cpuint); + + /* Release DMA */ + + if (priv->dma_channel >= 0) + { + esp32s3_dma_release(priv->dma_channel); + priv->dma_channel = -1; + } + + /* Free frame buffer */ + + if (priv->fb) + { + kmm_free(priv->fb); + priv->fb = NULL; + } + + priv->capturing = false; + + return OK; +} + +/**************************************************************************** + * Name: esp32s3_cam_set_buf + ****************************************************************************/ + +static int esp32s3_cam_set_buf(struct imgdata_s *data, + uint8_t nr_datafmts, + imgdata_format_t *datafmts, + uint8_t *addr, uint32_t size) +{ + struct esp32s3_cam_s *priv = (struct esp32s3_cam_s *)data; + + /* Store negotiated format from upper layer */ + + priv->width = datafmts[IMGDATA_FMT_MAIN].width; + priv->height = datafmts[IMGDATA_FMT_MAIN].height; + priv->pixfmt = datafmts[IMGDATA_FMT_MAIN].pixelformat; + + if (addr != NULL && size > 0) + { + priv->fb = addr; + priv->fb_size = size; + } + else + { + /* Allocate frame buffer in PSRAM if available. + * 8-bit DVP formats (RGB565, YUV422) are all 2 bytes per pixel. + */ + + priv->fb_size = priv->width * priv->height * 2; + priv->fb = kmm_memalign(64, priv->fb_size); + if (!priv->fb) + { + snerr("ERROR: Failed to allocate frame buffer\n"); + return -ENOMEM; + } + } + + memset(priv->fb, 0, priv->fb_size); + + /* Setup DMA descriptors for RX into frame buffer */ + + esp32s3_dma_setup(priv->dmadesc, + ESP32S3_CAM_DMADESC_NUM, + priv->fb, + priv->fb_size, + false, /* RX */ + priv->dma_channel); + + return OK; +} + +/**************************************************************************** + * Name: esp32s3_cam_validate_frame_setting + ****************************************************************************/ + +static int esp32s3_cam_validate_frame_setting(struct imgdata_s *data, + uint8_t nr_datafmts, + imgdata_format_t *datafmts, + imgdata_interval_t *interval) +{ + if (nr_datafmts < 1 || !datafmts) + { + return -EINVAL; + } + + /* Pixel format is validated by the sensor driver (imgsensor); + * imgdata only checks that width and height are non-zero. + */ + + if (datafmts[IMGDATA_FMT_MAIN].width == 0 || + datafmts[IMGDATA_FMT_MAIN].height == 0) + { + return -EINVAL; + } + + return OK; +} + +/**************************************************************************** + * Name: esp32s3_cam_start_capture + ****************************************************************************/ + +static int esp32s3_cam_start_capture(struct imgdata_s *data, + uint8_t nr_datafmts, Review Comment: align ########## drivers/video/gc0308.c: ########## @@ -0,0 +1,861 @@ +/**************************************************************************** + * drivers/video/gc0308.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <stdint.h> +#include <string.h> +#include <errno.h> +#include <debug.h> + +#include <nuttx/i2c/i2c_master.h> +#include <nuttx/video/imgsensor.h> +#include <nuttx/arch.h> +#include <nuttx/video/video.h> + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define GC0308_I2C_ADDR 0x21 +#define GC0308_I2C_FREQ 100000 + +/* GC0308 Register Addresses */ + +#define GC0308_REG_CHIP_ID 0x00 +#define GC0308_CHIP_ID_VAL 0x9b + +/* Page 0 registers */ + +#define GC0308_REG_RESET 0xfe /* Page/reset register */ +#define GC0308_REG_HBLANK_H 0x01 +#define GC0308_REG_HBLANK_L 0x02 +#define GC0308_REG_VBLANK_H 0x03 +#define GC0308_REG_VBLANK_L 0x04 +#define GC0308_REG_SH_DELAY 0x05 +#define GC0308_REG_ROW_START_H 0x06 +#define GC0308_REG_ROW_START_L 0x07 +#define GC0308_REG_COL_START_H 0x08 +#define GC0308_REG_COL_START_L 0x09 +#define GC0308_REG_WIN_HEIGHT_H 0x0a +#define GC0308_REG_WIN_HEIGHT_L 0x0b +#define GC0308_REG_WIN_WIDTH_H 0x0c +#define GC0308_REG_WIN_WIDTH_L 0x0d +#define GC0308_REG_VS_ST 0x0e +#define GC0308_REG_VS_ET 0x0f +#define GC0308_REG_VB_HB 0x10 +#define GC0308_REG_RSH_WIDTH 0x11 +#define GC0308_REG_TSP_WIDTH 0x12 +#define GC0308_REG_SAMPLE_HOLD 0x13 +#define GC0308_REG_CISCTL_MODE1 0x14 +#define GC0308_REG_CISCTL_MODE2 0x15 +#define GC0308_REG_CISCTL_MODE3 0x16 +#define GC0308_REG_CISCTL_MODE4 0x17 + +/* Output format (Page 0) */ + +#define GC0308_REG_OUTPUT_FMT 0x24 /* Bits[3:0]: output format select */ +#define GC0308_REG_OUT_FORMAT 0x44 +#define GC0308_REG_OUT_EN 0x45 +#define GC0308_REG_SYNC_MODE 0x46 + +/* Analog & bias */ + +#define GC0308_REG_ANALOG_MODE1 0x1a +#define GC0308_REG_ANALOG_MODE2 0x1b + +/* Exposure */ + +#define GC0308_REG_EXP_H 0x03 +#define GC0308_REG_EXP_L 0x04 + +/* Gain */ + +#define GC0308_REG_GLOBAL_GAIN 0x50 + +/* Crop */ + +#define GC0308_REG_CROP_WIN_MODE 0x46 +#define GC0308_REG_CROP_Y1_H 0x47 +#define GC0308_REG_CROP_Y1_L 0x48 +#define GC0308_REG_CROP_X1_H 0x49 +#define GC0308_REG_CROP_X1_L 0x4a +#define GC0308_REG_CROP_WIN_H_H 0x4b +#define GC0308_REG_CROP_WIN_H_L 0x4c +#define GC0308_REG_CROP_WIN_W_H 0x4d +#define GC0308_REG_CROP_WIN_W_L 0x4e + +/* Subsample (Page 0) */ + +#define GC0308_REG_SUBSAMPLE 0x59 +#define GC0308_REG_SUB_MODE 0x5a +#define GC0308_REG_SUB_ROW_N1 0x5b +#define GC0308_REG_SUB_ROW_N2 0x5c +#define GC0308_REG_SUB_COL_N1 0x5d +#define GC0308_REG_SUB_COL_N2 0x5e + +/* Subsample control (Page 1) */ + +#define GC0308_P1_REG_SUB_CTRL 0x53 /* Bit7: subsample enable */ +#define GC0308_P1_REG_SUB_RATIO 0x54 /* H[7:4] V[3:0] ratio */ +#define GC0308_P1_REG_SUB_EN 0x55 /* Bit0: subsample output enable */ +#define GC0308_P1_REG_SUB_HOFF 0x56 /* H offset */ +#define GC0308_P1_REG_SUB_VOFF 0x57 /* V offset */ +#define GC0308_P1_REG_SUB_HSIZE 0x58 /* H size adjust */ +#define GC0308_P1_REG_SUB_VSIZE 0x59 /* V size adjust */ + +/* Configured resolution */ + +#ifdef CONFIG_VIDEO_GC0308_VGA +# define GC0308_WIDTH 640 +# define GC0308_HEIGHT 480 +#elif defined(CONFIG_VIDEO_GC0308_QVGA) +# define GC0308_WIDTH 320 +# define GC0308_HEIGHT 240 +#elif defined(CONFIG_VIDEO_GC0308_QQVGA) +# define GC0308_WIDTH 160 +# define GC0308_HEIGHT 120 +#else +# error "No GC0308 resolution selected" +#endif + +/* Mirror/flip */ + +#define GC0308_REG_CISCTL_MODE1_MIRROR 0x14 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct gc0308_reg_s +{ + uint8_t addr; + uint8_t val; +}; + +struct gc0308_dev_s +{ + struct imgsensor_s sensor; + struct i2c_master_s *i2c; + bool streaming; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static bool gc0308_is_available(struct imgsensor_s *sensor); +static int gc0308_init(struct imgsensor_s *sensor); +static int gc0308_uninit(struct imgsensor_s *sensor); +static const char *gc0308_get_driver_name(struct imgsensor_s *sensor); +static int gc0308_validate_frame_setting(struct imgsensor_s *sensor, + imgsensor_stream_type_t type, + uint8_t nr_datafmts, + imgsensor_format_t *datafmts, + imgsensor_interval_t *interval); +static int gc0308_start_capture(struct imgsensor_s *sensor, + imgsensor_stream_type_t type, + uint8_t nr_datafmts, + imgsensor_format_t *datafmts, + imgsensor_interval_t *interval); +static int gc0308_stop_capture(struct imgsensor_s *sensor, + imgsensor_stream_type_t type); +static int gc0308_get_supported_value(struct imgsensor_s *sensor, + uint32_t id, + imgsensor_supported_value_t *value); +static int gc0308_get_value(struct imgsensor_s *sensor, + uint32_t id, uint32_t size, imgsensor_value_t *value); +static int gc0308_set_value(struct imgsensor_s *sensor, + uint32_t id, uint32_t size, imgsensor_value_t value); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* GC0308 initialization register table from espressif/esp32-camera vendor + * driver. This is the proven register set for GC0308 VGA output. + */ + +static const struct gc0308_reg_s g_gc0308_init_regs[] = +{ + { 0xfe, 0x00 }, + { 0xec, 0x20 }, + { 0x05, 0x00 }, + { 0x06, 0x00 }, + { 0x07, 0x00 }, + { 0x08, 0x00 }, + { 0x09, 0x01 }, + { 0x0a, 0xe8 }, + { 0x0b, 0x02 }, + { 0x0c, 0x88 }, + { 0x0d, 0x02 }, + { 0x0e, 0x02 }, + { 0x10, 0x26 }, + { 0x11, 0x0d }, + { 0x12, 0x2a }, + { 0x13, 0x00 }, + { 0x14, 0x10 }, + { 0x15, 0x0a }, + { 0x16, 0x05 }, + { 0x17, 0x01 }, + { 0x18, 0x44 }, + { 0x19, 0x44 }, + { 0x1a, 0x2a }, + { 0x1b, 0x00 }, + { 0x1c, 0x49 }, + { 0x1d, 0x9a }, + { 0x1e, 0x61 }, + { 0x1f, 0x00 }, + { 0x20, 0x7f }, + { 0x21, 0xfa }, + { 0x22, 0x57 }, + { 0x24, 0xa2 }, /* YCbYCr output format */ + { 0x25, 0x0f }, + { 0x26, 0x03 }, + { 0x28, 0x00 }, + { 0x2d, 0x0a }, + { 0x2f, 0x01 }, + { 0x30, 0xf7 }, + { 0x31, 0x50 }, + { 0x32, 0x00 }, + { 0x33, 0x28 }, + { 0x34, 0x2a }, + { 0x35, 0x28 }, + { 0x39, 0x04 }, + { 0x3a, 0x20 }, + { 0x3b, 0x20 }, + { 0x3c, 0x00 }, + { 0x3d, 0x00 }, + { 0x3e, 0x00 }, + { 0x3f, 0x00 }, + { 0x50, 0x14 }, /* Global gain */ + { 0x52, 0x41 }, + { 0x53, 0x80 }, + { 0x54, 0x80 }, + { 0x55, 0x80 }, + { 0x56, 0x80 }, + { 0x5a, 0x56 }, /* AWB R gain */ + { 0x5b, 0x40 }, /* AWB G gain */ + { 0x5c, 0x4a }, /* AWB B gain */ + { 0x8b, 0x20 }, + { 0x8c, 0x20 }, + { 0x8d, 0x20 }, + { 0x8e, 0x14 }, + { 0x8f, 0x10 }, + { 0x90, 0x14 }, + { 0x91, 0x3c }, + { 0x92, 0x50 }, + { 0x5d, 0x12 }, + { 0x5e, 0x1a }, + { 0x5f, 0x24 }, + { 0x60, 0x07 }, + { 0x61, 0x15 }, + { 0x62, 0x08 }, + { 0x64, 0x03 }, + { 0x66, 0xe8 }, + { 0x67, 0x86 }, + { 0x68, 0x82 }, + { 0x69, 0x18 }, + { 0x6a, 0x0f }, + { 0x6b, 0x00 }, + { 0x6c, 0x5f }, + { 0x6d, 0x8f }, + { 0x6e, 0x55 }, + { 0x6f, 0x38 }, + { 0x70, 0x15 }, + { 0x71, 0x33 }, + { 0x72, 0xdc }, + { 0x73, 0x00 }, + { 0x74, 0x02 }, + { 0x75, 0x3f }, + { 0x76, 0x02 }, + { 0x77, 0x38 }, + { 0x78, 0x88 }, + { 0x79, 0x81 }, + { 0x7a, 0x81 }, + { 0x7b, 0x22 }, + { 0x7c, 0xff }, + { 0x93, 0x48 }, /* Color matrix */ + { 0x94, 0x02 }, + { 0x95, 0x07 }, + { 0x96, 0xe0 }, + { 0x97, 0x40 }, + { 0x98, 0xf0 }, + { 0xb1, 0x40 }, /* Saturation */ + { 0xb2, 0x40 }, + { 0xb3, 0x40 }, + { 0xb6, 0xe0 }, + { 0xbd, 0x38 }, + { 0xbe, 0x36 }, + { 0xd0, 0xcb }, /* AEC */ + { 0xd1, 0x10 }, + { 0xd2, 0x90 }, + { 0xd3, 0x48 }, + { 0xd5, 0xf2 }, + { 0xd6, 0x16 }, + { 0xdb, 0x92 }, + { 0xdc, 0xa5 }, + { 0xdf, 0x23 }, + { 0xd9, 0x00 }, + { 0xda, 0x00 }, + { 0xe0, 0x09 }, + { 0xed, 0x04 }, + { 0xee, 0xa0 }, + { 0xef, 0x40 }, + { 0x80, 0x03 }, + { 0x9f, 0x10 }, + { 0xa0, 0x20 }, + { 0xa1, 0x38 }, + { 0xa2, 0x4e }, + { 0xa3, 0x63 }, + { 0xa4, 0x76 }, + { 0xa5, 0x87 }, + { 0xa6, 0xa2 }, + { 0xa7, 0xb8 }, + { 0xa8, 0xca }, + { 0xa9, 0xd8 }, + { 0xaa, 0xe3 }, + { 0xab, 0xeb }, + { 0xac, 0xf0 }, + { 0xad, 0xf8 }, + { 0xae, 0xfd }, + { 0xaf, 0xff }, + { 0xc0, 0x00 }, + { 0xc1, 0x10 }, + { 0xc2, 0x1c }, + { 0xc3, 0x30 }, + { 0xc4, 0x43 }, + { 0xc5, 0x54 }, + { 0xc6, 0x65 }, + { 0xc7, 0x75 }, + { 0xc8, 0x93 }, + { 0xc9, 0xb0 }, + { 0xca, 0xcb }, + { 0xcb, 0xe6 }, + { 0xcc, 0xff }, + { 0xf0, 0x02 }, + { 0xf1, 0x01 }, + { 0xf2, 0x02 }, + { 0xf3, 0x30 }, + { 0xf7, 0x04 }, + { 0xf8, 0x02 }, + { 0xf9, 0x9f }, + { 0xfa, 0x78 }, + { 0xfe, 0x01 }, + { 0x00, 0xf5 }, + { 0x02, 0x20 }, + { 0x04, 0x10 }, + { 0x05, 0x08 }, + { 0x06, 0x20 }, + { 0x08, 0x0a }, + { 0x0a, 0xa0 }, + { 0x0b, 0x60 }, + { 0x0c, 0x08 }, + { 0x0e, 0x44 }, + { 0x0f, 0x32 }, + { 0x10, 0x41 }, + { 0x11, 0x37 }, + { 0x12, 0x22 }, + { 0x13, 0x19 }, + { 0x14, 0x44 }, + { 0x15, 0x44 }, + { 0x16, 0xc2 }, + { 0x17, 0xa8 }, + { 0x18, 0x18 }, + { 0x19, 0x50 }, + { 0x1a, 0xd8 }, + { 0x1b, 0xf5 }, + { 0x70, 0x40 }, + { 0x71, 0x58 }, + { 0x72, 0x30 }, + { 0x73, 0x48 }, + { 0x74, 0x20 }, + { 0x75, 0x60 }, + { 0x77, 0x20 }, + { 0x78, 0x32 }, + { 0x30, 0x03 }, + { 0x31, 0x40 }, + { 0x32, 0x10 }, + { 0x33, 0xe0 }, + { 0x34, 0xe0 }, + { 0x35, 0x00 }, + { 0x36, 0x80 }, + { 0x37, 0x00 }, + { 0x38, 0x04 }, + { 0x39, 0x09 }, + { 0x3a, 0x12 }, + { 0x3b, 0x1c }, + { 0x3c, 0x28 }, + { 0x3d, 0x31 }, + { 0x3e, 0x44 }, + { 0x3f, 0x57 }, + { 0x40, 0x6c }, + { 0x41, 0x81 }, + { 0x42, 0x94 }, + { 0x43, 0xa7 }, + { 0x44, 0xb8 }, + { 0x45, 0xd6 }, + { 0x46, 0xee }, + { 0x47, 0x0d }, + { 0x62, 0xf7 }, + { 0x63, 0x68 }, + { 0x64, 0xd3 }, + { 0x65, 0xd3 }, + { 0x66, 0x60 }, + { 0xfe, 0x00 }, + { 0x01, 0x32 }, + { 0x02, 0x0c }, + { 0x0f, 0x01 }, + { 0xe2, 0x00 }, + { 0xe3, 0x78 }, + { 0xe4, 0x00 }, + { 0xe5, 0xfe }, + { 0xe6, 0x01 }, + { 0xe7, 0xe0 }, + { 0xe8, 0x01 }, + { 0xe9, 0xe0 }, + { 0xea, 0x01 }, + { 0xeb, 0xe0 }, + { 0xfe, 0x00 }, +}; + +static const struct imgsensor_ops_s g_gc0308_ops = +{ + .is_available = gc0308_is_available, + .init = gc0308_init, + .uninit = gc0308_uninit, + .get_driver_name = gc0308_get_driver_name, + .validate_frame_setting = gc0308_validate_frame_setting, + .start_capture = gc0308_start_capture, + .stop_capture = gc0308_stop_capture, + .get_supported_value = gc0308_get_supported_value, + .get_value = gc0308_get_value, + .set_value = gc0308_set_value, +}; + +static const struct v4l2_fmtdesc g_gc0308_fmtdescs[] = +{ + { + .pixelformat = V4L2_PIX_FMT_RGB565, + .description = "RGB565", + }, +}; + +static const struct v4l2_frmsizeenum g_gc0308_frmsizes[] = +{ + { + .type = V4L2_FRMSIZE_TYPE_DISCRETE, + .discrete = + { + .width = GC0308_WIDTH, + .height = GC0308_HEIGHT, + }, + }, +}; + +static const struct v4l2_frmivalenum g_gc0308_frmintervals[] = +{ + { + .type = V4L2_FRMIVAL_TYPE_DISCRETE, + .discrete = + { + .numerator = 1, + .denominator = 30, + }, + }, +}; + +static struct gc0308_dev_s g_gc0308_priv; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: gc0308_putreg + ****************************************************************************/ + +static int gc0308_putreg(struct i2c_master_s *i2c, + uint8_t regaddr, uint8_t regval) +{ + struct i2c_msg_s msg; + uint8_t buf[2]; + int ret; + + buf[0] = regaddr; + buf[1] = regval; + + msg.frequency = GC0308_I2C_FREQ; + msg.addr = GC0308_I2C_ADDR; + msg.flags = 0; + msg.buffer = buf; + msg.length = 2; + + ret = I2C_TRANSFER(i2c, &msg, 1); + if (ret < 0) + { + snerr("ERROR: I2C write to 0x%02x failed: %d\n", regaddr, ret); + } + + return ret; +} + +/**************************************************************************** + * Name: gc0308_getreg + ****************************************************************************/ + +static int gc0308_getreg(struct i2c_master_s *i2c, + uint8_t regaddr, uint8_t *regval) +{ + struct i2c_msg_s msg[2]; + int ret; + + msg[0].frequency = GC0308_I2C_FREQ; + msg[0].addr = GC0308_I2C_ADDR; + msg[0].flags = 0; + msg[0].buffer = ®addr; + msg[0].length = 1; + + msg[1].frequency = GC0308_I2C_FREQ; + msg[1].addr = GC0308_I2C_ADDR; + msg[1].flags = I2C_M_READ; + msg[1].buffer = regval; + msg[1].length = 1; + + ret = I2C_TRANSFER(i2c, msg, 2); + if (ret < 0) + { + snerr("ERROR: I2C read from 0x%02x failed: %d\n", regaddr, ret); + } + + return ret; +} + +/**************************************************************************** + * Name: gc0308_putreglist + ****************************************************************************/ + +static int gc0308_putreglist(struct i2c_master_s *i2c, + const struct gc0308_reg_s *reglist, + size_t nentries) +{ + int ret; + + for (size_t i = 0; i < nentries; i++) + { + ret = gc0308_putreg(i2c, reglist[i].addr, reglist[i].val); + if (ret < 0) + { + snerr("GC0308 write[%d] 0x%02x=0x%02x FAILED: %d\n", + (int)i, reglist[i].addr, reglist[i].val, ret); + return ret; + } + } + + return OK; +} + +/**************************************************************************** + * Name: gc0308_is_available + ****************************************************************************/ + +static bool gc0308_is_available(struct imgsensor_s *sensor) +{ + struct gc0308_dev_s *priv = (struct gc0308_dev_s *)sensor; + uint8_t id = 0; + int ret; + + /* Select page 0 */ + + gc0308_putreg(priv->i2c, GC0308_REG_RESET, 0x00); + + ret = gc0308_getreg(priv->i2c, GC0308_REG_CHIP_ID, &id); + if (ret < 0) + { + return false; + } + + sninfo("GC0308 chip ID: 0x%02x (expected 0x%02x)\n", + id, GC0308_CHIP_ID_VAL); + + return (id == GC0308_CHIP_ID_VAL); +} + +/**************************************************************************** + * Name: gc0308_init + ****************************************************************************/ + +static int gc0308_init(struct imgsensor_s *sensor) +{ + struct gc0308_dev_s *priv = (struct gc0308_dev_s *)sensor; + int ret; + + /* Software reset per vendor: write 0xf0 to reg 0xfe, wait 80ms */ + + ret = gc0308_putreg(priv->i2c, 0xfe, 0xf0); + if (ret < 0) + { + snerr("GC0308 soft reset failed: %d\n", ret); + return ret; + } + + up_mdelay(80); + + /* Write vendor initialization register table */ + + ret = gc0308_putreglist(priv->i2c, g_gc0308_init_regs, + sizeof(g_gc0308_init_regs) / + sizeof(g_gc0308_init_regs[0])); + if (ret < 0) + { + snerr("GC0308 init regs failed: %d\n", ret); + return ret; + } + + up_mdelay(80); + + /* Set RGB565 output format: reg 0x24 bits[3:0] = 6 */ + + ret = gc0308_putreg(priv->i2c, GC0308_REG_RESET, 0x00); + if (ret < 0) + { + return ret; + } + + uint8_t fmt = 0; Review Comment: move to beginning -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
