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    = &regaddr;
+  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, &regval);
+  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 */

Reply via email to