This is an automated email from the ASF dual-hosted git repository. acassis pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit fadfa85296d9ad7e4cac01d26e4bee1dd9dc1c09 Author: wangjianyu3 <[email protected]> AuthorDate: Mon Mar 16 11:49:41 2026 +0800 drivers/video: add GC0308 VGA CMOS image sensor driver Add support for the GalaxyCore GC0308 VGA CMOS image sensor. The driver implements the NuttX imgsensor interface and supports VGA (640x480), QVGA (320x240), and QQVGA (160x120) resolutions via Kconfig selection. Output format is RGB565X (big-endian). Signed-off-by: wangjianyu3 <[email protected]> --- drivers/video/CMakeLists.txt | 4 + drivers/video/Kconfig | 7 + drivers/video/Make.defs | 4 + drivers/video/gc0308.c | 891 +++++++++++++++++++++++++++++++++++++++++++ include/nuttx/video/gc0308.h | 57 +++ 5 files changed, 963 insertions(+) diff --git a/drivers/video/CMakeLists.txt b/drivers/video/CMakeLists.txt index 7e07c20cd36..5fb88b1b8cd 100644 --- a/drivers/video/CMakeLists.txt +++ b/drivers/video/CMakeLists.txt @@ -50,6 +50,10 @@ if(CONFIG_DRIVERS_VIDEO) list(APPEND SRCS ov2640.c) endif() + if(CONFIG_VIDEO_GC0308) + list(APPEND SRCS gc0308.c) + endif() + endif() if(CONFIG_GOLDFISH_FB) diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index ce2437f0bda..fd7ab00701a 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -465,6 +465,13 @@ config OV2640_REGDEBUG endif # VIDEO_OV2640 +config VIDEO_GC0308 + bool "GC0308 camera chip" + default n + select I2C + ---help--- + GC0308 VGA CMOS image sensor with DVP interface. + config GOLDFISH_CAMERA bool "Goldfish camera support" depends on VIDEO diff --git a/drivers/video/Make.defs b/drivers/video/Make.defs index 7d62a646e7a..07b3f1c3f15 100644 --- a/drivers/video/Make.defs +++ b/drivers/video/Make.defs @@ -60,6 +60,10 @@ ifeq ($(CONFIG_VIDEO_OV2640),y) CSRCS += ov2640.c endif +ifeq ($(CONFIG_VIDEO_GC0308),y) + CSRCS += gc0308.c +endif + endif ifeq ($(CONFIG_GOLDFISH_FB),y) diff --git a/drivers/video/gc0308.c b/drivers/video/gc0308.c new file mode 100644 index 00000000000..339940e5a69 --- /dev/null +++ b/drivers/video/gc0308.c @@ -0,0 +1,891 @@ +/**************************************************************************** + * 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 <sys/param.h> +#include <stdint.h> +#include <string.h> +#include <errno.h> +#include <debug.h> + +#include <nuttx/kmalloc.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 */ + +/* Native sensor resolution (VGA) — used as default when caller passes 0 */ + +#define GC0308_NATIVE_WIDTH 640 +#define GC0308_NATIVE_HEIGHT 480 + +/* 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; + uint16_t width; + uint16_t height; + struct v4l2_frmsizeenum frmsizes; + 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_frmivalenum g_gc0308_frmintervals[] = +{ + { + .type = V4L2_FRMIVAL_TYPE_DISCRETE, + .discrete = + { + .numerator = 1, + .denominator = 30, + }, + }, +}; + +/**************************************************************************** + * 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_modreg + * + * Description: + * Read-modify-write an 8-bit register. + * + ****************************************************************************/ + +static int gc0308_modreg(struct i2c_master_s *i2c, + uint8_t regaddr, + uint8_t clearbits, uint8_t setbits) +{ + uint8_t regval; + int ret; + + ret = gc0308_getreg(i2c, regaddr, ®val); + if (ret < 0) + { + return ret; + } + + regval = (regval & ~clearbits) | setbits; + + return gc0308_putreg(i2c, regaddr, regval); +} + +/**************************************************************************** + * Name: gc0308_putreglist + ****************************************************************************/ + +static int gc0308_putreglist(struct i2c_master_s *i2c, + const struct gc0308_reg_s *reglist, + size_t nentries) +{ + size_t i; + int ret; + + for (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; + uint8_t id = 0; + uint8_t fmt = 0; + 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, + nitems(g_gc0308_init_regs)); + 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; + } + + ret = gc0308_modreg(priv->i2c, GC0308_REG_OUTPUT_FMT, 0x0f, 0x06); + if (ret < 0) + { + return ret; + } + + /* Configure subsample for non-VGA resolutions */ + + if (priv->width < 640 || priv->height < 480) + { + uint8_t ratio; + + /* Switch to page 1 */ + + ret = gc0308_putreg(priv->i2c, GC0308_REG_RESET, 0x01); + if (ret < 0) + { + return ret; + } + + ret = gc0308_modreg(priv->i2c, GC0308_P1_REG_SUB_CTRL, 0, 0x80); + ret |= gc0308_modreg(priv->i2c, GC0308_P1_REG_SUB_EN, 0, 0x01); + + if (priv->width <= 160) + { + ratio = 0x44; /* 1/4 */ + } + else + { + ratio = 0x22; /* 1/2 */ + } + + ret |= gc0308_putreg(priv->i2c, GC0308_P1_REG_SUB_RATIO, ratio); + 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); + } + + /* Debug: verify key registers */ + + 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) +{ + struct gc0308_dev_s *priv = (struct gc0308_dev_s *)sensor; + + 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 != priv->width || + datafmts[IMGSENSOR_FMT_MAIN].height != priv->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 + * width - Desired frame width (0 = VGA 640) + * height - Desired frame height (0 = VGA 480) + * + * Returned Value: + * Pointer to imgsensor_s on success; NULL on failure. + * + ****************************************************************************/ + +struct imgsensor_s *gc0308_initialize(struct i2c_master_s *i2c, + uint16_t width, + uint16_t height) +{ + struct gc0308_dev_s *priv; + + if (!i2c) + { + return NULL; + } + + priv = kmm_zalloc(sizeof(struct gc0308_dev_s)); + if (!priv) + { + return NULL; + } + + priv->i2c = i2c; + priv->streaming = false; + priv->width = width ? width : GC0308_NATIVE_WIDTH; + priv->height = height ? height : GC0308_NATIVE_HEIGHT; + + priv->frmsizes.type = V4L2_FRMSIZE_TYPE_DISCRETE; + priv->frmsizes.discrete.width = priv->width; + priv->frmsizes.discrete.height = priv->height; + + priv->sensor.ops = &g_gc0308_ops; + priv->sensor.fmtdescs = g_gc0308_fmtdescs; + priv->sensor.fmtdescs_num = nitems(g_gc0308_fmtdescs); + priv->sensor.frmsizes = &priv->frmsizes; + priv->sensor.frmsizes_num = 1; + priv->sensor.frmintervals = g_gc0308_frmintervals; + priv->sensor.frmintervals_num = nitems(g_gc0308_frmintervals); + + return &priv->sensor; +} diff --git a/include/nuttx/video/gc0308.h b/include/nuttx/video/gc0308.h new file mode 100644 index 00000000000..608492e6eb0 --- /dev/null +++ b/include/nuttx/video/gc0308.h @@ -0,0 +1,57 @@ +/**************************************************************************** + * include/nuttx/video/gc0308.h + * + * 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. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_VIDEO_GC0308_H +#define __INCLUDE_NUTTX_VIDEO_GC0308_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/i2c/i2c_master.h> +#include <nuttx/video/imgsensor.h> + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: gc0308_initialize + * + * Description: + * Initialize the GC0308 camera sensor driver. + * + * Input Parameters: + * i2c - I2C bus device + * width - Desired frame width (0 = VGA 640) + * height - Desired frame height (0 = VGA 480) + * + * Returned Value: + * Pointer to imgsensor_s on success; NULL on failure. + * + ****************************************************************************/ + +struct imgsensor_s *gc0308_initialize(struct i2c_master_s *i2c, + uint16_t width, + uint16_t height); + +#endif /* __INCLUDE_NUTTX_VIDEO_GC0308_H */
