JianyuWang0623 commented on code in PR #18542: URL: https://github.com/apache/nuttx/pull/18542#discussion_r2940625279
########## 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: done ########## 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 done -- 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]
