From 72f399e0528b5aebc10ba3da1fb7a7cddea9c928 Mon Sep 17 00:00:00 2001 From: Alin Jerpelea Date: Wed, 11 May 2022 07:59:27 +0000 Subject: [PATCH] drivers: video: add isx019 camera sensor The driver is provided by Spresense SDK ISX019 has the DOL-HDR feature, which combines images with different exposure so that both bright and dark data of high-contrast subject can be viewed at the same time. Signed-off-by: Alin Jerpelea --- boards/arm/cxd56xx/common/src/Make.defs | 4 + boards/arm/cxd56xx/common/src/cxd56_isx019.c | 203 + boards/arm/cxd56xx/spresense/include/board.h | 7 +- .../cxd56xx/spresense/include/cxd56_isx019.h | 131 + .../arm/cxd56xx/spresense/src/cxd56_bringup.c | 12 +- drivers/video/Kconfig | 35 + drivers/video/Make.defs | 4 + drivers/video/isx019.c | 3249 +++++++++++++++++ drivers/video/isx019_range.h | 133 + drivers/video/isx019_reg.h | 408 +++ include/nuttx/video/isx019.h | 55 + 11 files changed, 4238 insertions(+), 3 deletions(-) create mode 100644 boards/arm/cxd56xx/common/src/cxd56_isx019.c create mode 100644 boards/arm/cxd56xx/spresense/include/cxd56_isx019.h create mode 100644 drivers/video/isx019.c create mode 100644 drivers/video/isx019_range.h create mode 100644 drivers/video/isx019_reg.h create mode 100644 include/nuttx/video/isx019.h diff --git a/boards/arm/cxd56xx/common/src/Make.defs b/boards/arm/cxd56xx/common/src/Make.defs index 572a772536..5c0e6a66d7 100644 --- a/boards/arm/cxd56xx/common/src/Make.defs +++ b/boards/arm/cxd56xx/common/src/Make.defs @@ -154,6 +154,10 @@ ifeq ($(CONFIG_VIDEO_ISX012),y) CSRCS += cxd56_isx012.c endif +ifeq ($(CONFIG_VIDEO_ISX019),y) +CSRCS += cxd56_isx019.c +endif + ifeq ($(CONFIG_CXD56_IMAGEPROC),y) CSRCS += cxd56_imageproc.c endif diff --git a/boards/arm/cxd56xx/common/src/cxd56_isx019.c b/boards/arm/cxd56xx/common/src/cxd56_isx019.c new file mode 100644 index 0000000000..b8aed7043a --- /dev/null +++ b/boards/arm/cxd56xx/common/src/cxd56_isx019.c @@ -0,0 +1,203 @@ +/**************************************************************************** + * boards/arm/cxd56xx/common/src/cxd56_isx019.c + * + * 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 + +#include +#include +#include + +#include +#include +#include + +#include "cxd56_gpio.h" +#include "cxd56_pinconfig.h" +#include "cxd56_i2c.h" +#include "cxd56_clock.h" + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Check if the following are defined in the board.h */ + +#ifndef IMAGER_RST +# error "IMAGER_RST must be defined in board.h !!" +#endif + +#define POWER_CHECK_TIME (1 * USEC_PER_MSEC) /* ms */ +#define POWER_OFF_TIME (50 * USEC_PER_MSEC) /* ms */ + +#define POWER_CHECK_RETRY (10) + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int board_isx019_power_on(void) +{ + int ret; + int i; + + ret = board_power_control(POWER_IMAGE_SENSOR, true); + if (ret) + { + _err("ERROR: Failed to power on ImageSensor. %d\n", ret); + return -ENODEV; + } + + ret = -ETIMEDOUT; + for (i = 0; i < POWER_CHECK_RETRY; i++) + { + /* Need to wait for a while after power-on */ + + nxsig_usleep(POWER_CHECK_TIME); + + if (board_power_monitor(POWER_IMAGE_SENSOR)) + { + ret = OK; + break; + } + } + + return ret; +} + +int board_isx019_power_off(void) +{ + int ret; + int i; + + ret = board_power_control(POWER_IMAGE_SENSOR, false); + if (ret) + { + _err("ERROR: Failed to power off ImageSensor. %d\n", ret); + return -ENODEV; + } + + /* Need to wait for power-off to be reflected */ + + nxsig_usleep(POWER_OFF_TIME); + + ret = -ETIMEDOUT; + for (i = 0; i < POWER_CHECK_RETRY; i++) + { + if (!board_power_monitor(POWER_IMAGE_SENSOR)) + { + ret = OK; + break; + } + + nxsig_usleep(POWER_CHECK_TIME); + } + + return ret; +} + +void board_isx019_set_reset(void) +{ + cxd56_gpio_write(IMAGER_RST, false); +} + +void board_isx019_release_reset(void) +{ + cxd56_gpio_write(IMAGER_RST, true); +} + +struct i2c_master_s *board_isx019_initialize(void) +{ + int retry = 50; + + _info("Initializing ISX019...\n"); + + while (!g_rtc_enabled && 0 < retry--) + { + /* ISX019 requires stable RTC */ + + nxsig_usleep(100 * USEC_PER_MSEC); + } + + cxd56_gpio_config(IMAGER_RST, false); + board_isx019_set_reset(); + + /* To avoid IS_DATA0 and IS_DATA7 being Hi-Z state during FPGA config, + * output these pins to LOW. + */ + + cxd56_gpio_config(PIN_IS_DATA0, false); + cxd56_gpio_config(PIN_IS_DATA7, false); + cxd56_gpio_write(PIN_IS_DATA0, false); + cxd56_gpio_write(PIN_IS_DATA7, false); + + /* Initialize i2c device */ + + return cxd56_i2cbus_initialize(IMAGER_I2C); +} + +int board_isx019_uninitialize(struct i2c_master_s *i2c) +{ + int ret; + + _info("Uninitializing ISX019...\n"); + + /* Restore IS_DATA0 and IS_DATA7 to Hi-Z state */ + + cxd56_gpio_config(PIN_IS_DATA0, false); + cxd56_gpio_config(PIN_IS_DATA7, false); + cxd56_gpio_write_hiz(PIN_IS_DATA0); + cxd56_gpio_write_hiz(PIN_IS_DATA7); + + /* Uninitialize i2c device */ + + ret = isx019_uninitialize(); + if (ret < 0) + { + _err("Failed to uninitialize ISX019.\n"); + } + + if (i2c == NULL) + { + _err("Error uninitialize ISX019.\n"); + return -ENODEV; + } + else + { + ret = cxd56_i2cbus_uninitialize(i2c); + if (ret < 0) + { + _err("Error uninitialize I2C BUS.\n"); + return -EPERM; + } + } + + return ret; +} + +uint32_t board_isx019_get_master_clock(void) +{ + return cxd56_get_xosc_clock(); +} diff --git a/boards/arm/cxd56xx/spresense/include/board.h b/boards/arm/cxd56xx/spresense/include/board.h index fa720ca65c..ad695ea8a0 100644 --- a/boards/arm/cxd56xx/spresense/include/board.h +++ b/boards/arm/cxd56xx/spresense/include/board.h @@ -64,7 +64,12 @@ #include "cxd56_scd41.h" #include "cxd56_sensors.h" -#include "cxd56_isx012.h" +#ifdef CONFIG_VIDEO_ISX012 +# include "cxd56_isx012.h" +#endif /* CONFIG_VIDEO_ISX012 */ +#ifdef CONFIG_VIDEO_ISX019 +# include "cxd56_isx019.h" +#endif /* CONFIG_VIDEO_ISX019 */ /**************************************************************************** * Pre-processor Definitions diff --git a/boards/arm/cxd56xx/spresense/include/cxd56_isx019.h b/boards/arm/cxd56xx/spresense/include/cxd56_isx019.h new file mode 100644 index 0000000000..f99bde8384 --- /dev/null +++ b/boards/arm/cxd56xx/spresense/include/cxd56_isx019.h @@ -0,0 +1,131 @@ +/**************************************************************************** + * boards/arm/cxd56xx/spresense/include/cxd56_isx019.h + * + * 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 __BOARDS_ARM_CXD56XX_SPRESENSE_INCLUDE_CXD56_ISX019_H +#define __BOARDS_ARM_CXD56XX_SPRESENSE_INCLUDE_CXD56_ISX019_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +#ifndef __ASSEMBLY__ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: board_isx019_power_on + * + * Description: + * Power on ISX019 + * + ****************************************************************************/ + +int board_isx019_power_on(void); + +/**************************************************************************** + * Name: board_isx019_power_off + * + * Description: + * Power off ISX019 + * + ****************************************************************************/ + +int board_isx019_power_off(void); + +/**************************************************************************** + * Name: board_isx019_set_reset + * + * Description: + * Set reset ISX019 + * + ****************************************************************************/ + +void board_isx019_set_reset(void); + +/**************************************************************************** + * Name: board_isx019_release_reset + * + * Description: + * Release reset ISX019 + * + ****************************************************************************/ + +void board_isx019_release_reset(void); + +/**************************************************************************** + * Name: board_isx019_initialize + * + * Description: + * Initialize ISX019 i2c driver and register the ISX019 device. + * + ****************************************************************************/ + +struct i2c_master_s *board_isx019_initialize(void); + +/**************************************************************************** + * Name: board_isx019_uninitialize + * + * Description: + * Uninitialize ISX019 i2c driver and register the ISX019 device. + * + ****************************************************************************/ + +int board_isx019_uninitialize(struct i2c_master_s *i2c); + +/**************************************************************************** + * Name: board_isx019_get_master_clock + * + * Description: + * Get ISX019 master clock. + * + ****************************************************************************/ + +uint32_t board_isx019_get_master_clock(void); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* __BOARDS_ARM_CXD56XX_SPRESENSE_INCLUDE_CXD56_ISX019_H */ diff --git a/boards/arm/cxd56xx/spresense/src/cxd56_bringup.c b/boards/arm/cxd56xx/spresense/src/cxd56_bringup.c index e38a400adb..a736fa3c74 100644 --- a/boards/arm/cxd56xx/spresense/src/cxd56_bringup.c +++ b/boards/arm/cxd56xx/spresense/src/cxd56_bringup.c @@ -372,13 +372,21 @@ int cxd56_bringup(void) } #endif +#ifdef CONFIG_VIDEO_ISX019 + ret = isx019_initialize(); + if (ret < 0) + { + _err("ERROR: Failed to initialize ISX019 board. %d\n", errno); + } +#endif + #ifdef CONFIG_VIDEO_ISX012 ret = isx012_initialize(); if (ret < 0) { _err("ERROR: Failed to initialize ISX012 board. %d\n", errno); } -#endif /* CONFIG_VIDEO_ISX012 */ +#endif #ifdef CONFIG_CXD56_CISIF ret = cxd56_cisif_initialize(); @@ -387,7 +395,7 @@ int cxd56_bringup(void) _err("ERROR: Failed to initialize CISIF. %d\n", errno); ret = ERROR; } -#endif /* CONFIG_CXD56_CISIF */ +#endif #if defined(CONFIG_CXD56_SDIO) /* In order to prevent Hi-Z from being input to the SD Card controller, diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 8b57c429c2..1508938ed5 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -150,6 +150,41 @@ config VIDEO_ISX012 default n select I2C +config VIDEO_ISX019 + bool "ISX019 Image sensor" + default n + select I2C + +if VIDEO_ISX019 +choice + prompt "HDR mode selection" + default VIDEO_ISX019_DOL3 + ---help--- + ISX019 has the DOL-HDR feature, which combines images with different exposure so that + both bright and dark data of high-contrast subject can be viewed at the same time. + Select DOLX(X = 2 or 3), in which X represents the number of combined images. + +config VIDEO_ISX019_DOL3 + bool "DOL3" + ---help--- + DOL3 is the mode that combines 3 images with different exposure. + +config VIDEO_ISX019_DOL2 + bool "DOL2" + ---help--- + DOL2 is the mode that combines 2 images with different exposure. + +endchoice + +config VIDEO_ISX019_INITIAL_JPEG_QUALITY + int "Initial JPEG quality" + default 70 + range 1 100 + ---help--- + The initial JPEG quality. + +endif + config VIDEO_OV2640 bool "OV2640 camera chip" default n diff --git a/drivers/video/Make.defs b/drivers/video/Make.defs index 0828a4ae98..de29436788 100644 --- a/drivers/video/Make.defs +++ b/drivers/video/Make.defs @@ -38,6 +38,10 @@ ifeq ($(CONFIG_VIDEO_ISX012),y) CSRCS += isx012.c endif +ifeq ($(CONFIG_VIDEO_ISX019),y) + CSRCS += isx019.c +endif + ifeq ($(CONFIG_VIDEO_OV2640),y) CSRCS += ov2640.c endif diff --git a/drivers/video/isx019.c b/drivers/video/isx019.c new file mode 100644 index 0000000000..9bc8c68aed --- /dev/null +++ b/drivers/video/isx019.c @@ -0,0 +1,3249 @@ +/**************************************************************************** + * drivers/video/isx019.c + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "isx019_reg.h" +#include "isx019_range.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Wait time on power on sequence. */ + +#define TRANSITION_TIME_TO_STARTUP (120 * USEC_PER_MSEC) /* unit : usec */ +#define TRANSITION_TIME_TO_STREAMING (30 * USEC_PER_MSEC) /* unit : usec */ + +/* For get_supported_value() I/F */ + +#define SET_RANGE(range, min, max, s, def) \ + do \ + { \ + (range).minimum = (min); \ + (range).maximum = (max); \ + (range).step = (s); \ + (range).default_value = (def); \ + } \ + while (0); + +#define SET_DISCRETE(disc, nr, val, def) \ + do \ + { \ + (disc).nr_values = (nr); \ + (disc).values = (val); \ + (disc).default_value = (def); \ + } \ + while (0); + +#define SET_ELEMS(elem, nr, min, max, s) \ + do \ + { \ + (elem).nr_elems = (nr); \ + (elem).minimum = (min); \ + (elem).maximum = (max); \ + (elem).step = (s); \ + } \ + while (0); + +#define COMPARE_FRAMESIZE(w, h, sup_w, sup_h) (((w) == (sup_w)) && \ + ((h) == (sup_h))) + +#define VALIDATE_FRAMESIZE(w, h) (COMPARE_FRAMESIZE((w), (h), 1280, 960) || \ + COMPARE_FRAMESIZE((w), (h), 1280, 720) || \ + COMPARE_FRAMESIZE((w), (h), 640, 480) || \ + COMPARE_FRAMESIZE((w), (h), 640, 360) || \ + COMPARE_FRAMESIZE((w), (h), 480, 360) || \ + COMPARE_FRAMESIZE((w), (h), 320, 240) || \ + COMPARE_FRAMESIZE((w), (h), 160, 120)) + +#define VALIDATE_THUMNAIL_SIZE(m, s) (((s) != 0) && \ + ((m) % (s) == 0) && \ + ((m) / (s) < 5) && \ + ((m) / (s) > 0)) + +/* For set_value() and get_value() I/F */ + +#define SET_REGINFO(a, c, o, s) do \ + { \ + (a)->category = (c); \ + (a)->offset = (o); \ + (a)->size = (s); \ + } \ + while (0); + +#define VALIDATE_RANGE(v, min, max, step) (((v) >= (min)) && \ + ((v) <= (max)) && \ + (((v) - (min)) % (step) == 0)) + +/* Offset for IMGSENSOR_ID_3A_PARAMETER control */ + +#define OFFSET_3APARAMETER_AWB_R (0) +#define OFFSET_3APARAMETER_AWB_B (2) +#define OFFSET_3APARAMETER_AE_SHTTIME (4) +#define OFFSET_3APARAMETER_AE_GAIN (8) + +/* Index of array for drive mode setting */ + +#define INDEX_SENS (0) +#define INDEX_POST (1) +#define INDEX_SENSPOST (2) +#define INDEX_IO (3) + +/* Timer value for power on control */ + +#define ISX019_ACCESSIBLE_WAIT_SEC (0) +#define ISX019_ACCESSIBLE_WAIT_USEC (200000) +#define FPGA_ACCESSIBLE_WAIT_SEC (1) +#define FPGA_ACCESSIBLE_WAIT_USEC (0) + +/* Array size of DQT setting for JPEG quality */ + +#define JPEG_DQT_ARRAY_SIZE (64) + +/* ISX019 standard master clock */ + +#define ISX019_STANDARD_MASTER_CLOCK (27000000) + +/* Vivid colors setting */ + +#define VIVID_COLORS_SATURATION (0xf0) +#define VIVID_COLORS_SHARPNESS (0x20) + +/* Black white colors setting */ + +#define BW_COLORS_SATURATION (0x00) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct isx019_default_value_s +{ + int32_t brightness; + int32_t contrast; + int32_t saturation; + int32_t hue; + int32_t awb; + int32_t gamma; + int32_t ev; + int32_t hflip_video; + int32_t vflip_video; + int32_t hflip_still; + int32_t vflip_still; + int32_t sharpness; + int32_t ae; + int32_t exptime; + int32_t wbmode; + int32_t hdr; + int32_t iso; + int32_t iso_auto; + int32_t meter; + int32_t threealock; + int32_t threeastatus; + int32_t jpgquality; +}; + +typedef struct isx019_default_value_s isx019_default_value_t; + +struct isx019_rect_s +{ + int32_t left; + int32_t top; + uint32_t width; + uint32_t height; +}; + +typedef struct isx019_rect_s isx019_rect_t; + +struct isx019_dev_s +{ + sem_t fpga_lock; + sem_t i2c_lock; + FAR struct i2c_master_s *i2c; + float clock_ratio; + isx019_default_value_t default_value; + imgsensor_stream_type_t stream; + imgsensor_white_balance_t wb_mode; + uint8_t flip_video; + uint8_t flip_still; + isx019_rect_t clip_video; + isx019_rect_t clip_still; + int32_t iso; + double gamma; + int32_t jpg_quality; + imgsensor_colorfx_t colorfx; +}; + +typedef struct isx019_dev_s isx019_dev_t; + +typedef CODE int32_t (*convert_t)(int32_t value32); + +typedef CODE int (*setvalue_t)(imgsensor_value_t value); +typedef CODE int (*getvalue_t)(FAR imgsensor_value_t *value); + +struct isx019_reginfo_s +{ + uint16_t category; + uint16_t offset; + uint8_t size; +}; + +typedef struct isx019_reginfo_s isx019_reginfo_t; + +struct isx019_fpga_jpg_quality_s +{ + /* JPEG quality */ + + int quality; + + /* DQT header setting for y component */ + + uint8_t y_head[JPEG_DQT_ARRAY_SIZE]; + + /* DQT calculation data for y component */ + + uint8_t y_calc[JPEG_DQT_ARRAY_SIZE]; + + /* DQT header setting for c component */ + + uint8_t c_head[JPEG_DQT_ARRAY_SIZE]; + + /* DQT calculation data for c component */ + + uint8_t c_calc[JPEG_DQT_ARRAY_SIZE]; +}; + +typedef struct isx019_fpga_jpg_quality_s isx019_fpga_jpg_quality_t; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static bool isx019_is_available(void); +static int isx019_init(void); +static int isx019_uninit(void); +static FAR const char *isx019_get_driver_name(void); +static int isx019_validate_frame_setting(imgsensor_stream_type_t type, + uint8_t nr_datafmt, + FAR imgsensor_format_t *datafmts, + FAR imgsensor_interval_t *interval); +static int isx019_start_capture(imgsensor_stream_type_t type, + uint8_t nr_datafmt, + FAR imgsensor_format_t *datafmts, + FAR imgsensor_interval_t *interval); +static int isx019_stop_capture(imgsensor_stream_type_t type); +static int isx019_get_supported_value(uint32_t id, + FAR imgsensor_supported_value_t *value); +static int isx019_get_value(uint32_t id, uint32_t size, + FAR imgsensor_value_t *value); +static int isx019_set_value(uint32_t id, uint32_t size, + imgsensor_value_t value); +static int initialize_jpg_quality(void); +static int send_read_cmd(FAR struct i2c_config_s *config, + uint8_t cat, + uint16_t addr, + uint8_t size); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static isx019_dev_t g_isx019_private; + +static struct imgsensor_ops_s g_isx019_ops = +{ + isx019_is_available, + isx019_init, + isx019_uninit, + isx019_get_driver_name, + isx019_validate_frame_setting, + isx019_start_capture, + isx019_stop_capture, + isx019_get_supported_value, + isx019_get_value, + isx019_set_value, +}; + +static isx019_fpga_jpg_quality_t g_isx019_jpg_quality[] = +{ + { + 10, + { + 21, 15, 15, 26, 18, 26, 43, 21, + 21, 43, 43, 43, 32, 43, 43, 43, + 43, 43, 43, 43, 43, 64, 43, 43, + 43, 43, 43, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + }, + { + 3, 4, 133, 131, 131, 131, 1, 1, + 4, 135, 3, 131, 131, 131, 1, 1, + 133, 3, 2, 131, 131, 1, 1, 1, + 131, 131, 131, 131, 1, 1, 1, 1, + 131, 131, 131, 1, 1, 1, 1, 1, + 131, 131, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + }, + { + 21, 26, 26, 32, 26, 32, 43, 26, + 26, 43, 64, 43, 32, 43, 64, 64, + 64, 43, 43, 64, 64, 64, 64, 64, + 43, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + }, + { + 3, 133, 2, 131, 1, 1, 1, 1, + 133, 133, 133, 131, 1, 1, 1, 1, + 2, 133, 2, 131, 1, 1, 1, 1, + 131, 131, 131, 131, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + } + }, + { + 20, + { + 18, 14, 14, 14, 15, 14, 21, 15, + 15, 21, 32, 21, 18, 21, 32, 32, + 26, 21, 21, 26, 32, 32, 26, 26, + 26, 26, 26, 32, 43, 32, 32, 32, + 32, 32, 32, 43, 43, 43, 43, 43, + 43, 43, 43, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + }, + { + 135, 137, 137, 3, 2, 2, 2, 131, + 137, 4, 4, 3, 133, 133, 2, 131, + 137, 4, 135, 3, 133, 2, 131, 1, + 3, 3, 3, 133, 2, 131, 1, 1, + 2, 133, 133, 2, 131, 1, 1, 1, + 2, 133, 2, 131, 1, 1, 1, 1, + 2, 2, 131, 1, 1, 1, 1, 1, + 131, 131, 1, 1, 1, 1, 1, 1, + }, + { + 21, 21, 21, 21, 26, 21, 26, 21, + 21, 26, 26, 21, 26, 21, 26, 32, + 26, 26, 26, 26, 32, 43, 32, 32, + 32, 32, 32, 43, 64, 43, 43, 43, + 43, 43, 43, 64, 64, 64, 43, 43, + 43, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + }, + { + 3, 3, 3, 133, 133, 2, 131, 1, + 3, 133, 3, 3, 133, 2, 131, 1, + 3, 3, 133, 133, 2, 131, 1, 1, + 133, 3, 133, 2, 131, 131, 1, 1, + 133, 133, 2, 131, 131, 1, 1, 1, + 2, 2, 131, 131, 1, 1, 1, 1, + 131, 131, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + } + }, + { + 30, + { + 15, 11, 11, 11, 12, 11, 15, 12, + 12, 15, 21, 15, 13, 15, 21, 26, + 21, 15, 15, 21, 26, 32, 21, 21, + 21, 21, 21, 32, 32, 21, 26, 26, + 26, 26, 21, 32, 32, 32, 32, 43, + 32, 32, 32, 43, 43, 43, 43, 43, + 43, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + }, + { + 4, 12, 12, 4, 3, 133, 2, 2, + 12, 139, 139, 4, 3, 3, 3, 2, + 12, 139, 5, 4, 3, 133, 2, 131, + 4, 4, 4, 3, 133, 2, 131, 1, + 3, 3, 3, 133, 131, 131, 1, 1, + 133, 3, 133, 2, 131, 1, 1, 1, + 2, 3, 2, 131, 1, 1, 1, 1, + 2, 2, 131, 1, 1, 1, 1, 1, + }, + { + 18, 15, 15, 18, 18, 18, 21, 18, + 18, 21, 21, 18, 21, 18, 21, 26, + 21, 21, 21, 21, 26, 43, 26, 26, + 26, 26, 26, 43, 43, 32, 32, 32, + 32, 32, 32, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, + 43, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + }, + { + 135, 4, 135, 3, 3, 133, 131, 131, + 4, 135, 135, 135, 3, 133, 2, 131, + 135, 135, 3, 3, 133, 2, 131, 131, + 3, 135, 3, 133, 2, 131, 131, 1, + 3, 3, 133, 2, 131, 131, 1, 1, + 133, 133, 2, 131, 131, 1, 1, 1, + 131, 2, 131, 131, 1, 1, 1, 1, + 131, 131, 131, 1, 1, 1, 1, 1, + } + }, + { + 40, + { + 12, 8, 8, 8, 9, 8, 12, 9, + 9, 12, 18, 11, 10, 11, 18, 21, + 15, 12, 12, 15, 21, 26, 18, 18, + 21, 18, 18, 26, 21, 18, 21, 21, + 21, 21, 18, 21, 21, 26, 26, 32, + 26, 26, 21, 32, 32, 43, 43, 32, + 32, 43, 43, 43, 43, 43, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + }, + { + 139, 8, 8, 139, 135, 3, 133, 3, + 8, 7, 7, 12, 4, 135, 135, 3, + 8, 7, 141, 139, 135, 3, 133, 2, + 139, 12, 139, 3, 3, 133, 2, 131, + 135, 4, 135, 3, 2, 131, 131, 1, + 3, 135, 3, 133, 131, 131, 1, 1, + 133, 135, 133, 2, 131, 1, 1, 1, + 3, 3, 2, 131, 1, 1, 1, 1, + }, + { + 13, 11, 11, 13, 14, 13, 15, 14, + 14, 15, 21, 14, 15, 14, 21, 21, + 15, 18, 18, 15, 21, 26, 21, 21, + 21, 21, 21, 26, 32, 26, 21, 21, + 21, 21, 26, 32, 32, 32, 32, 32, + 32, 32, 32, 43, 43, 32, 32, 43, + 43, 43, 43, 43, 43, 43, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + }, + { + 5, 12, 5, 4, 3, 3, 133, 2, + 12, 137, 137, 137, 4, 3, 133, 2, + 5, 137, 4, 135, 3, 3, 2, 131, + 4, 137, 135, 3, 3, 2, 131, 131, + 3, 4, 3, 3, 2, 2, 131, 1, + 3, 3, 3, 2, 2, 131, 1, 1, + 133, 133, 2, 131, 131, 1, 1, 1, + 2, 2, 131, 131, 1, 1, 1, 1, + } + }, + { + 50, + { + 8, 6, 6, 6, 6, 6, 8, 6, + 6, 8, 12, 8, 7, 8, 12, 14, + 10, 8, 8, 10, 14, 15, 13, 13, + 14, 13, 13, 15, 18, 12, 14, 13, + 13, 14, 12, 18, 15, 18, 18, 21, + 18, 18, 15, 26, 26, 26, 26, 26, + 26, 32, 32, 32, 32, 32, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, + }, + { + 8, 11, 11, 8, 139, 137, 4, 135, + 11, 11, 11, 8, 141, 5, 139, 4, + 11, 11, 9, 8, 5, 137, 135, 133, + 8, 8, 8, 137, 5, 135, 133, 2, + 139, 141, 5, 5, 3, 133, 2, 131, + 137, 5, 137, 135, 133, 2, 131, 131, + 4, 139, 135, 133, 2, 131, 131, 131, + 135, 4, 133, 2, 131, 131, 131, 131, + }, + { + 9, 8, 8, 9, 10, 9, 11, 9, + 9, 11, 14, 11, 13, 11, 14, 18, + 14, 14, 14, 14, 18, 18, 13, 13, + 14, 13, 13, 18, 26, 18, 15, 15, + 15, 15, 18, 26, 21, 21, 21, 21, + 21, 21, 21, 26, 26, 26, 26, 26, + 26, 32, 32, 32, 32, 32, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, + }, + { + 7, 8, 7, 12, 137, 135, 135, 133, + 8, 141, 7, 12, 137, 5, 135, 3, + 7, 7, 5, 137, 5, 4, 3, 133, + 12, 12, 137, 137, 4, 3, 133, 2, + 137, 137, 5, 4, 3, 133, 2, 131, + 135, 5, 4, 3, 133, 2, 131, 131, + 135, 135, 3, 133, 2, 131, 131, 131, + 133, 3, 133, 2, 131, 131, 131, 131, + } + }, + { + 60, + { + 6, 4, 4, 4, 5, 4, 6, 5, + 5, 6, 9, 6, 5, 6, 9, 11, + 8, 6, 6, 8, 11, 12, 10, 10, + 11, 10, 10, 12, 15, 12, 12, 12, + 12, 12, 12, 15, 12, 14, 15, 15, + 15, 14, 12, 18, 18, 21, 21, 18, + 18, 26, 26, 26, 26, 26, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + }, + { + 11, 16, 16, 11, 7, 12, 139, 4, + 16, 13, 13, 11, 8, 141, 139, 139, + 16, 13, 13, 11, 141, 139, 137, 135, + 11, 11, 11, 12, 139, 4, 135, 133, + 7, 8, 141, 139, 4, 3, 133, 2, + 12, 141, 139, 4, 3, 133, 2, 2, + 139, 139, 137, 135, 133, 2, 2, 2, + 4, 139, 135, 133, 2, 2, 2, 2, + }, + { + 7, 7, 7, 13, 12, 13, 26, 15, + 15, 26, 26, 21, 18, 21, 26, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + }, + { + 9, 9, 5, 133, 133, 2, 2, 2, + 9, 139, 4, 3, 2, 2, 2, 2, + 5, 4, 135, 2, 2, 2, 2, 2, + 133, 3, 2, 2, 2, 2, 2, 2, + 133, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + } + }, + { + 70, + { + 4, 3, 3, 3, 3, 3, 4, 3, + 3, 4, 6, 4, 3, 4, 6, 7, + 5, 4, 4, 5, 7, 8, 6, 6, + 7, 6, 6, 8, 10, 8, 9, 9, + 9, 9, 8, 10, 10, 12, 12, 12, + 12, 12, 10, 12, 12, 13, 13, 12, + 12, 18, 18, 18, 18, 18, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + }, + { + 16, 21, 21, 16, 11, 9, 8, 141, + 21, 21, 21, 16, 13, 11, 8, 141, + 21, 21, 21, 16, 11, 7, 139, 139, + 16, 16, 16, 9, 7, 139, 139, 135, + 11, 13, 11, 7, 139, 5, 135, 3, + 9, 11, 7, 139, 5, 135, 3, 3, + 8, 8, 139, 139, 135, 3, 3, 3, + 141, 141, 139, 135, 3, 3, 3, 3, + }, + { + 4, 5, 5, 8, 7, 8, 15, 10, + 10, 15, 21, 14, 14, 14, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + }, + { + 16, 13, 8, 4, 3, 3, 3, 3, + 13, 9, 141, 137, 3, 3, 3, 3, + 8, 141, 137, 3, 3, 3, 3, 3, + 4, 137, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + } + }, + { + 80, + { + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 3, 2, 2, 2, 3, 4, + 3, 2, 2, 3, 4, 5, 4, 4, + 4, 4, 4, 5, 6, 5, 5, 5, + 5, 5, 5, 6, 6, 7, 7, 8, + 7, 7, 6, 9, 9, 10, 10, 9, + 9, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, + }, + { + 32, 32, 32, 32, 21, 16, 13, 11, + 32, 32, 32, 32, 21, 16, 13, 11, + 32, 32, 32, 32, 16, 13, 9, 7, + 32, 32, 32, 16, 13, 9, 7, 139, + 21, 21, 16, 13, 8, 141, 139, 139, + 16, 16, 13, 9, 141, 139, 139, 139, + 13, 13, 9, 7, 139, 139, 139, 139, + 11, 11, 7, 139, 139, 139, 139, 139, + }, + { + 3, 3, 3, 5, 4, 5, 9, 6, + 6, 9, 13, 11, 9, 11, 13, 15, + 14, 14, 14, 14, 15, 15, 12, 12, + 12, 12, 12, 15, 15, 12, 12, 12, + 12, 12, 12, 15, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, + }, + { + 21, 21, 13, 7, 5, 4, 4, 4, + 21, 16, 11, 12, 137, 139, 139, 139, + 13, 11, 7, 137, 139, 139, 139, 139, + 7, 12, 137, 139, 139, 139, 139, 139, + 5, 137, 139, 139, 139, 139, 139, 139, + 4, 139, 139, 139, 139, 139, 139, 139, + 4, 139, 139, 139, 139, 139, 139, 139, + 4, 139, 139, 139, 139, 139, 139, 139, + } + }, + { + 90, + { + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 2, 1, 1, 1, 2, 2, + 2, 1, 1, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 3, 2, 3, 3, + 3, 3, 2, 3, 3, 4, 4, 4, + 4, 4, 3, 5, 5, 5, 5, 5, + 5, 7, 7, 7, 7, 7, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + }, + { + 64, 64, 64, 64, 32, 32, 32, 21, + 64, 64, 64, 64, 32, 32, 32, 21, + 64, 64, 64, 64, 32, 21, 16, 13, + 64, 64, 64, 32, 21, 16, 13, 9, + 32, 32, 32, 21, 16, 13, 9, 8, + 32, 32, 21, 16, 13, 9, 8, 8, + 32, 32, 16, 13, 9, 8, 8, 8, + 21, 21, 13, 9, 8, 8, 8, 8, + }, + { + 1, 1, 1, 2, 2, 2, 5, 3, + 3, 5, 7, 5, 4, 5, 7, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + }, + { + 64, 64, 32, 13, 9, 8, 8, 8, + 64, 32, 21, 13, 8, 8, 8, 8, + 32, 21, 16, 8, 8, 8, 8, 8, + 13, 13, 8, 8, 8, 8, 8, 8, + 9, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + } + }, + { + 100, + { + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + }, + { + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 32, + 64, 64, 64, 64, 64, 64, 32, 32, + 64, 64, 64, 64, 64, 32, 32, 21, + 64, 64, 64, 64, 32, 32, 21, 21, + 64, 64, 64, 32, 32, 21, 21, 21, + 64, 64, 32, 32, 21, 21, 21, 21, + }, + { + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 2, 2, 1, 2, 2, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + }, + { + 64, 64, 64, 64, 32, 21, 21, 21, + 64, 64, 64, 32, 21, 21, 21, 21, + 64, 64, 64, 21, 21, 21, 21, 21, + 64, 32, 21, 21, 21, 21, 21, 21, + 32, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + } + } +}; + +#define NR_JPGSETTING_TBL \ + (sizeof(g_isx019_jpg_quality) / sizeof(isx019_fpga_jpg_quality_t)) + +int32_t g_isx019_colorfx[] = +{ + IMGSENSOR_COLORFX_NONE, + IMGSENSOR_COLORFX_BW, + IMGSENSOR_COLORFX_VIVID, +}; + +#define NR_COLORFX (sizeof(g_isx019_colorfx) / sizeof(int32_t)) + +int32_t g_isx019_wbmode[] = +{ + IMGSENSOR_WHITE_BALANCE_AUTO, + IMGSENSOR_WHITE_BALANCE_INCANDESCENT, + IMGSENSOR_WHITE_BALANCE_FLUORESCENT, + IMGSENSOR_WHITE_BALANCE_DAYLIGHT, + IMGSENSOR_WHITE_BALANCE_CLOUDY, + IMGSENSOR_WHITE_BALANCE_SHADE, +}; + +#define NR_WBMODE (sizeof(g_isx019_wbmode) / sizeof(int32_t)) + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void i2c_lock(void) +{ + nxsem_wait_uninterruptible(&g_isx019_private.i2c_lock); +} + +static void i2c_unlock(void) +{ + nxsem_post(&g_isx019_private.i2c_lock); +} + +static void fpga_lock(void) +{ + nxsem_wait_uninterruptible(&g_isx019_private.fpga_lock); +} + +static void fpga_unlock(void) +{ + nxsem_post(&g_isx019_private.fpga_lock); +} + +int fpga_i2c_write(uint8_t addr, FAR uint8_t *data, uint8_t size) +{ + struct i2c_config_s config; + static uint8_t buf[FPGA_I2C_REGSIZE_MAX + FPGA_I2C_REGADDR_LEN]; + int ret; + + DEBUGASSERT(size <= FPGA_I2C_REGSIZE_MAX); + + config.frequency = ISX019_I2C_FREQUENCY; + config.address = ISX019_I2C_SLVADDR; + config.addrlen = ISX019_I2C_SLVADDR_LEN; + + i2c_lock(); + + /* ISX019 requires that send read command to ISX019 before FPGA access. */ + + send_read_cmd(&config, CAT_VERSION, ROM_VERSION, 1); + + config.frequency = FPGA_I2C_FREQUENCY; + config.address = FPGA_I2C_SLVADDR; + config.addrlen = FPGA_I2C_SLVADDR_LEN; + + buf[FPGA_I2C_OFFSET_ADDR] = addr; + memcpy(&buf[FPGA_I2C_OFFSET_WRITEDATA], data, size); + ret = i2c_write(g_isx019_private.i2c, + &config, + buf, + size + FPGA_I2C_REGADDR_LEN); + i2c_unlock(); + + return ret; +} + +static int fpga_i2c_read(uint8_t addr, FAR uint8_t *data, uint8_t size) +{ + int ret; + struct i2c_config_s config; + + DEBUGASSERT(size <= FPGA_I2C_REGSIZE_MAX); + + config.frequency = ISX019_I2C_FREQUENCY; + config.address = ISX019_I2C_SLVADDR; + config.addrlen = ISX019_I2C_SLVADDR_LEN; + + i2c_lock(); + + /* ISX019 requires that send read command to ISX019 before FPGA access. */ + + send_read_cmd(&config, CAT_VERSION, ROM_VERSION, 1); + + config.frequency = FPGA_I2C_FREQUENCY; + config.address = FPGA_I2C_SLVADDR; + config.addrlen = FPGA_I2C_SLVADDR_LEN; + + ret = i2c_write(g_isx019_private.i2c, + &config, + &addr, + FPGA_I2C_REGADDR_LEN); + if (ret >= 0) + { + ret = i2c_read(g_isx019_private.i2c, &config, data, size); + } + + i2c_unlock(); + + return ret; +} + +static void fpga_activate_setting(void) +{ + uint8_t regval = FPGA_ACTIVATE_REQUEST; + fpga_i2c_write(FPGA_ACTIVATE, ®val, 1); + + while (1) + { + fpga_i2c_read(FPGA_ACTIVATE, ®val, 1); + if (regval == 0) + { + break; + } + } +} + +static uint8_t calc_isx019_chksum(FAR uint8_t *data, uint8_t len) +{ + int i; + uint8_t chksum = 0; + + for (i = 0; i < len; i++) + { + /* ISX019 checksum is lower 8bit of addition result. + * So, no problem even if overflow occur. + */ + + chksum += data[i]; + } + + return chksum; +} + +static bool validate_isx019_chksum(FAR uint8_t *data, uint8_t len) +{ + uint8_t chksum; + + chksum = calc_isx019_chksum(data, len - 1); + + return (data[len - 1] == chksum); +} + +static int recv_write_response(FAR struct i2c_config_s *config) +{ + int ret; + uint8_t buf[ISX019_I2C_WRRES_TOTALLEN]; + + ret = i2c_read(g_isx019_private.i2c, config, buf, sizeof(buf)); + if (ret < 0) + { + return ret; + } + + if ((buf[ISX019_I2C_OFFSET_TOTALLEN] != ISX019_I2C_WRRES_TOTALLEN) || + (buf[ISX019_I2C_OFFSET_CMDNUM] != 1) || + (buf[ISX019_I2C_OFFSET_CMDLEN] != ISX019_I2C_WRRES_LEN) || + (buf[ISX019_I2C_OFFSET_STS] != ISX019_I2C_STS_OK) || + !validate_isx019_chksum(buf, ISX019_I2C_WRRES_TOTALLEN)) + { + return -EPROTO; + } + + return OK; +} + +static int recv_read_response(FAR struct i2c_config_s *config, + FAR uint8_t *data, + uint8_t size) +{ + int ret; + uint8_t buf[ISX019_I2C_WRREQ_TOTALLEN(ISX019_I2C_REGSIZE_MAX)] = + { + 0 + }; + + DEBUGASSERT(size <= ISX019_I2C_REGSIZE_MAX); + + ret = i2c_read(g_isx019_private.i2c, + config, buf, ISX019_I2C_RDRES_TOTALLEN(size)); + if (ret < 0) + { + return ret; + } + + if ((buf[ISX019_I2C_OFFSET_TOTALLEN] != ISX019_I2C_RDRES_TOTALLEN(size)) || + (buf[ISX019_I2C_OFFSET_CMDNUM] != 1) || + (buf[ISX019_I2C_OFFSET_CMDLEN] != ISX019_I2C_RDRES_LEN(size)) || + (buf[ISX019_I2C_OFFSET_STS] != ISX019_I2C_STS_OK) || + !validate_isx019_chksum(buf, ISX019_I2C_RDRES_TOTALLEN(size))) + { + return -EPROTO; + } + + memcpy(data, &buf[ISX019_I2C_OFFSET_READDATA], size); + + return OK; +} + +static int send_write_cmd(FAR struct i2c_config_s *config, + uint8_t cat, + uint16_t addr, + FAR uint8_t *data, + uint8_t size) +{ + int len; + uint8_t buf[ISX019_I2C_WRREQ_TOTALLEN(ISX019_I2C_REGSIZE_MAX)] = + { + 0 + }; + + DEBUGASSERT(size <= ISX019_I2C_REGSIZE_MAX); + + buf[ISX019_I2C_OFFSET_TOTALLEN] = ISX019_I2C_WRREQ_TOTALLEN(size); + buf[ISX019_I2C_OFFSET_CMDNUM] = 1; + buf[ISX019_I2C_OFFSET_CMDLEN] = ISX019_I2C_WRREQ_LEN(size); + buf[ISX019_I2C_OFFSET_CMD] = ISX019_I2C_CMD_WRITE; + buf[ISX019_I2C_OFFSET_CATEGORY] = cat; + buf[ISX019_I2C_OFFSET_ADDRESS_H] = addr >> 8; + buf[ISX019_I2C_OFFSET_ADDRESS_L] = addr & 0xff; + + memcpy(&buf[ISX019_I2C_OFFSET_WRITEDATA], data, size); + + len = ISX019_I2C_OFFSET_WRITEDATA + size; + buf[len] = calc_isx019_chksum(buf, len); + len++; + + return i2c_write(g_isx019_private.i2c, config, buf, len); +} + +static int isx019_i2c_write(uint8_t cat, + uint16_t addr, + FAR uint8_t *data, + uint8_t size) +{ + int ret; + struct i2c_config_s config; + + DEBUGASSERT(size <= ISX019_I2C_REGSIZE_MAX); + + config.frequency = ISX019_I2C_FREQUENCY; + config.address = ISX019_I2C_SLVADDR; + config.addrlen = ISX019_I2C_SLVADDR_LEN; + + i2c_lock(); + + ret = send_write_cmd(&config, cat, addr, data, size); + if (ret == OK) + { + ret = recv_write_response(&config); + } + + i2c_unlock(); + return ret; +} + +static int send_read_cmd(FAR struct i2c_config_s *config, + uint8_t cat, + uint16_t addr, + uint8_t size) +{ + int ret; + int len; + uint8_t buf[ISX019_I2C_RDREQ_TOTALLEN] = + { + 0 + }; + + buf[ISX019_I2C_OFFSET_TOTALLEN] = ISX019_I2C_RDREQ_TOTALLEN; + buf[ISX019_I2C_OFFSET_CMDNUM] = 1; + buf[ISX019_I2C_OFFSET_CMDLEN] = ISX019_I2C_RDREQ_LEN; + buf[ISX019_I2C_OFFSET_CMD] = ISX019_I2C_CMD_READ; + buf[ISX019_I2C_OFFSET_CATEGORY] = cat; + buf[ISX019_I2C_OFFSET_ADDRESS_H] = addr >> 8; + buf[ISX019_I2C_OFFSET_ADDRESS_L] = addr & 0xff; + buf[ISX019_I2C_OFFSET_READSIZE] = size; + + len = ISX019_I2C_OFFSET_READSIZE + 1; + buf[len] = calc_isx019_chksum(buf, len); + len++; + + ret = i2c_write(g_isx019_private.i2c, config, buf, len); + return ret; +} + +static int isx019_i2c_read(uint8_t cat, + uint16_t addr, + FAR uint8_t *data, + uint8_t size) +{ + int ret; + struct i2c_config_s config; + + DEBUGASSERT(size <= ISX019_I2C_REGSIZE_MAX); + + config.frequency = ISX019_I2C_FREQUENCY; + config.address = ISX019_I2C_SLVADDR; + config.addrlen = ISX019_I2C_SLVADDR_LEN; + + i2c_lock(); + + ret = send_read_cmd(&config, cat, addr, size); + if (ret == OK) + { + ret = recv_read_response(&config, data, size); + } + + i2c_unlock(); + + return ret; +} + +static void fpga_init(void) +{ + uint8_t regval; + + regval = FPGA_RESET_ENABLE; + fpga_i2c_write(FPGA_RESET, ®val, 1); + regval = FPGA_DATA_OUTPUT_STOP; + fpga_i2c_write(FPGA_DATA_OUTPUT, ®val, 1); + fpga_activate_setting(); + regval = FPGA_RESET_RELEASE; + fpga_i2c_write(FPGA_RESET, ®val, 1); + fpga_activate_setting(); +} + +static int set_drive_mode(void) +{ + uint8_t drv[] = + { +#ifdef CONFIG_VIDEO_ISX019_DOL2 + DOL2_30FPS_SENS, DOL2_30FPS_POST, DOL2_30FPS_SENSPOST, DOL2_30FPS_IO +#else + DOL3_30FPS_SENS, DOL3_30FPS_POST, DOL3_30FPS_SENSPOST, DOL3_30FPS_IO +#endif + }; + + nxsig_usleep(TRANSITION_TIME_TO_STARTUP); + + isx019_i2c_write(CAT_CONFIG, MODE_SENSSEL, &drv[INDEX_SENS], 1); + isx019_i2c_write(CAT_CONFIG, MODE_POSTSEL, &drv[INDEX_POST], 1); + isx019_i2c_write(CAT_CONFIG, MODE_SENSPOST_SEL, &drv[INDEX_SENSPOST], 1); + + nxsig_usleep(TRANSITION_TIME_TO_STREAMING); + + return OK; +} + +static bool try_repeat(int sec, int usec, CODE int (*trial_func)(void)) +{ + int ret; + struct timeval start; + struct timeval now; + struct timeval delta; + struct timeval wait; + + wait.tv_sec = sec; + wait.tv_usec = usec; + + gettimeofday(&start, NULL); + while (1) + { + ret = trial_func(); + if (ret != -ENODEV) + { + break; + } + else + { + gettimeofday(&now, NULL); + timersub(&now, &start, &delta); + if (timercmp(&delta, &wait, >)) + { + break; + } + } + }; + + return (ret == OK); +} + +static int try_isx019_i2c(void) +{ + uint8_t buf; + return isx019_i2c_read(CAT_SYSCOM, DEVSTS, &buf, 1); +} + +static int try_fpga_i2c(void) +{ + uint8_t buf; + return fpga_i2c_read(FPGA_VERSION, &buf, 1); +} + +static void power_on(void) +{ + g_isx019_private.i2c = board_isx019_initialize(); + board_isx019_power_on(); + board_isx019_release_reset(); +} + +static void power_off(void) +{ + board_isx019_set_reset(); + board_isx019_power_off(); + board_isx019_uninitialize(g_isx019_private.i2c); +} + +static bool isx019_is_available(void) +{ + bool ret; + + power_on(); + + /* Try to access via I2C + * about both ISX019 image sensor and FPGA. + */ + + ret = false; + if (try_repeat(ISX019_ACCESSIBLE_WAIT_SEC, + ISX019_ACCESSIBLE_WAIT_USEC, + try_isx019_i2c)) + { + if (try_repeat(FPGA_ACCESSIBLE_WAIT_SEC, + FPGA_ACCESSIBLE_WAIT_USEC, + try_fpga_i2c)) + { + ret = true; + } + } + + power_off(); + + return ret; +} + +static int32_t get_value32(uint32_t id) +{ + imgsensor_value_t val; + isx019_get_value(id, 0, &val); + return val.value32; +} + +static void store_default_value(void) +{ + FAR isx019_default_value_t *def = &g_isx019_private.default_value; + + def->brightness = get_value32(IMGSENSOR_ID_BRIGHTNESS); + def->contrast = get_value32(IMGSENSOR_ID_CONTRAST); + def->saturation = get_value32(IMGSENSOR_ID_SATURATION); + def->hue = get_value32(IMGSENSOR_ID_HUE); + def->awb = get_value32(IMGSENSOR_ID_AUTO_WHITE_BALANCE); + def->gamma = get_value32(IMGSENSOR_ID_GAMMA); + def->ev = get_value32(IMGSENSOR_ID_EXPOSURE); + def->hflip_video = get_value32(IMGSENSOR_ID_HFLIP_VIDEO); + def->vflip_video = get_value32(IMGSENSOR_ID_VFLIP_VIDEO); + def->hflip_still = get_value32(IMGSENSOR_ID_HFLIP_STILL); + def->vflip_still = get_value32(IMGSENSOR_ID_VFLIP_STILL); + def->sharpness = get_value32(IMGSENSOR_ID_SHARPNESS); + def->ae = get_value32(IMGSENSOR_ID_EXPOSURE_AUTO); + def->exptime = get_value32(IMGSENSOR_ID_EXPOSURE_ABSOLUTE); + def->wbmode = get_value32(IMGSENSOR_ID_AUTO_N_PRESET_WB); + def->hdr = get_value32(IMGSENSOR_ID_WIDE_DYNAMIC_RANGE); + def->iso = get_value32(IMGSENSOR_ID_ISO_SENSITIVITY); + def->iso_auto = get_value32(IMGSENSOR_ID_ISO_SENSITIVITY_AUTO); + def->meter = get_value32(IMGSENSOR_ID_EXPOSURE_METERING); + def->threealock = get_value32(IMGSENSOR_ID_3A_LOCK); + def->threeastatus = get_value32(IMGSENSOR_ID_3A_STATUS); + def->jpgquality = get_value32(IMGSENSOR_ID_JPEG_QUALITY); +} + +static int isx019_init(void) +{ + uint32_t clk; + + power_on(); + set_drive_mode(); + fpga_init(); + initialize_jpg_quality(); + store_default_value(); + clk = board_isx019_get_master_clock(); + g_isx019_private.clock_ratio + = (float)clk / ISX019_STANDARD_MASTER_CLOCK; + + return OK; +} + +static int isx019_uninit(void) +{ + power_off(); + return OK; +} + +static FAR const char *isx019_get_driver_name(void) +{ +#ifdef CONFIG_VIDEO_ISX019_NAME_WITH_VERSION + static char name[16]; + uint8_t f_ver = 0; + uint16_t is_ver = 0; + + isx019_i2c_read(CAT_VERSION, ROM_VERSION, (FAR uint8_t *)&is_ver, 2); + fpga_i2c_read(FPGA_VERSION, &f_ver, 1); + snprintf(name, sizeof(name), "ISX019 v%04x_%02d", is_ver, f_ver); + + return name; +#else + return "ISX019"; +#endif +} + +static int validate_format(int nr_fmt, FAR imgsensor_format_t *fmt) +{ + int ret; + + uint16_t main_w; + uint16_t main_h; + uint16_t sub_w; + uint16_t sub_h; + + if ((nr_fmt < 1) || (nr_fmt > 2)) + { + return -EINVAL; + } + + if (fmt == NULL) + { + return -EINVAL; + } + + main_w = fmt[IMGSENSOR_FMT_MAIN].width; + main_h = fmt[IMGSENSOR_FMT_MAIN].height; + + switch (fmt[IMGSENSOR_FMT_MAIN].pixelformat) + { + case IMGSENSOR_PIX_FMT_UYVY: /* YUV 4:2:2 */ + case IMGSENSOR_PIX_FMT_RGB565: /* RGB565 */ + case IMGSENSOR_PIX_FMT_JPEG: /* JPEG */ + case IMGSENSOR_PIX_FMT_JPEG_WITH_SUBIMG: /* JPEG + sub image */ + + if (!VALIDATE_FRAMESIZE(main_w, main_h)) + { + ret = -EINVAL; + break; + } + + if (nr_fmt > 1) + { + sub_w = fmt[IMGSENSOR_FMT_SUB].width; + sub_h = fmt[IMGSENSOR_FMT_SUB].height; + if (!VALIDATE_THUMNAIL_SIZE(main_w, sub_w) || + !VALIDATE_THUMNAIL_SIZE(main_h, sub_h)) + { + ret = -EINVAL; + break; + } + } + + ret = OK; + break; + + default: /* otherwise */ + ret = -EINVAL; + break; + } + + return ret; +} + +static int validate_frameinterval(FAR imgsensor_interval_t *interval) +{ + int ret = -EINVAL; + + if (interval == NULL) + { + return -EINVAL; + } + + /* Avoid multiplication overflow */ + + if ((interval->denominator * 10) / 10 != interval->denominator) + { + return -EINVAL; + } + + /* Avoid division by zero */ + + if (interval->numerator == 0) + { + return -EINVAL; + } + + switch ((interval->denominator * 10) / interval->numerator) + { + case 300: /* 30FPS */ + case 150: /* 15FPS */ + case 100: /* 10FPS */ + case 75: /* 7.5FPS */ + ret = OK; + break; + + default: /* otherwise */ + ret = -EINVAL; + break; + } + + return ret; +} + +static int isx019_validate_frame_setting(imgsensor_stream_type_t type, + uint8_t nr_fmt, + FAR imgsensor_format_t *fmt, + FAR imgsensor_interval_t *interval) +{ + int ret = OK; + + ret = validate_format(nr_fmt, fmt); + if (ret != OK) + { + return ret; + } + + return validate_frameinterval(interval); +} + +static int activate_flip(imgsensor_stream_type_t type) +{ + uint8_t flip; + + flip = (type == IMGSENSOR_STREAM_TYPE_VIDEO) ? + g_isx019_private.flip_video : g_isx019_private.flip_still; + + return isx019_i2c_write(CAT_CONFIG, REVERSE, &flip, 1); +} + +static int activate_clip(imgsensor_stream_type_t type, + uint16_t w, + uint16_t h) +{ + FAR isx019_rect_t *clip; + uint8_t size; + uint8_t top; + uint8_t left = 0; + + clip = (type == IMGSENSOR_STREAM_TYPE_VIDEO) ? + &g_isx019_private.clip_video : &g_isx019_private.clip_still; + + switch (w) + { + case 1280: + if (clip->width == 640) /* In this case, c_h == 360 */ + { + size = FPGA_CLIP_640_360; + top = clip->top / FPGA_CLIP_UNIT; + left = clip->left / FPGA_CLIP_UNIT; + + if (h == 720) + { + /* Shift (960 - 720) / 2 lines */ + + top += 120 / FPGA_CLIP_UNIT; + } + } + else if (clip->width == 1280) + { + /* In this case, clip->height == 720 */ + + size = FPGA_CLIP_1280_720; + top = clip->top / FPGA_CLIP_UNIT; + left = clip->left / FPGA_CLIP_UNIT; + } + else /* no clip */ + { + if (h == 720) + { + size = FPGA_CLIP_1280_720; + + /* Shift (960 - 720) / 2 lines */ + + top = 120 / FPGA_CLIP_UNIT; + } + else + { + size = FPGA_CLIP_NON; + top = 0; + left = 0; + } + } + + break; + + default: /* 640 */ + if (clip->width == 640) + { + /* In this case, clip->height == 360 */ + + size = FPGA_CLIP_640_360; + top = clip->top / FPGA_CLIP_UNIT; + left = clip->left / FPGA_CLIP_UNIT; + } + else /* no clip */ + { + if (h == 360) + { + size = FPGA_CLIP_640_360; + + /* Shift (480 - 360) / 2 lines */ + + top = 60 / FPGA_CLIP_UNIT; + } + else + { + size = FPGA_CLIP_NON; + top = 0; + left = 0; + } + } + + break; + } + + fpga_i2c_write(FPGA_CLIP_SIZE, &size, 1); + fpga_i2c_write(FPGA_CLIP_TOP, &top, 1); + fpga_i2c_write(FPGA_CLIP_LEFT, &left, 1); + + return OK; +} + +static int isx019_start_capture(imgsensor_stream_type_t type, + uint8_t nr_fmt, + FAR imgsensor_format_t *fmt, + FAR imgsensor_interval_t *interval) +{ + int ret; + uint8_t regval = 0; + + ret = isx019_validate_frame_setting(type, nr_fmt, fmt, interval); + if (ret != OK) + { + return ret; + } + + ret = activate_flip(type); + if (ret != OK) + { + return ret; + } + + fpga_lock(); + + /* Update FORMAT_AND_SCALE register of FPGA */ + + switch (fmt[IMGSENSOR_FMT_MAIN].pixelformat) + { + case IMGSENSOR_PIX_FMT_RGB565: + regval |= FPGA_FORMAT_RGB; + break; + + case IMGSENSOR_PIX_FMT_UYVY: + regval |= FPGA_FORMAT_YUV; + break; + + case IMGSENSOR_PIX_FMT_JPEG: + regval |= FPGA_FORMAT_JPEG; + break; + + default: /* IMGSENSOR_PIX_FMT_JPEG_WITH_SUBIMG */ + if (nr_fmt == 1) + { + regval |= FPGA_FORMAT_JPEG; + } + else + { + regval |= FPGA_FORMAT_THUMBNAIL; + } + + break; + } + + switch (fmt[IMGSENSOR_FMT_MAIN].width) + { + case 1280: + regval |= FPGA_SCALE_1280_960; + activate_clip(type, + fmt[IMGSENSOR_FMT_MAIN].width, + fmt[IMGSENSOR_FMT_MAIN].height); + break; + + case 640: + regval |= FPGA_SCALE_640_480; + activate_clip(type, + fmt[IMGSENSOR_FMT_MAIN].width, + fmt[IMGSENSOR_FMT_MAIN].height); + break; + + case 320: + regval |= FPGA_SCALE_320_240; + break; + + default: /* 160 */ + + regval |= FPGA_SCALE_160_120; + break; + } + + fpga_i2c_write(FPGA_FORMAT_AND_SCALE, ®val, 1); + + /* Update FPS_AND_THUMBNAIL register of FPGA */ + + regval = 0; + + if (nr_fmt == 2) + { + switch (fmt[IMGSENSOR_FMT_MAIN].width / fmt[IMGSENSOR_FMT_SUB].width) + { + case 1 : /* 1/1 */ + regval |= FPGA_THUMBNAIL_SCALE_1_1; + break; + + case 2 : /* 1/2 */ + regval |= FPGA_THUMBNAIL_SCALE_1_2; + break; + + case 4 : /* 1/4 */ + regval |= FPGA_THUMBNAIL_SCALE_1_4; + break; + + default : /* 1/8 */ + regval |= FPGA_THUMBNAIL_SCALE_1_8; + break; + } + } + + switch ((interval->denominator * 10) / interval->numerator) + { + case 300: /* 30FPS */ + regval |= FPGA_FPS_1_1; + break; + + case 150: /* 15FPS */ + regval |= FPGA_FPS_1_2; + break; + + case 100: /* 10FPS */ + regval |= FPGA_FPS_1_3; + break; + + case 75: /* 7.5FPS */ + regval |= FPGA_FPS_1_4; + break; + + default: /* otherwise */ + + /* It may not come here because the value has already been validated + * in validate_frameinterval(). + */ + + break; + } + + fpga_i2c_write(FPGA_FPS_AND_THUMBNAIL, ®val, 1); + + regval = FPGA_DATA_OUTPUT_START; + fpga_i2c_write(FPGA_DATA_OUTPUT, ®val, 1); + + fpga_activate_setting(); + fpga_unlock(); + g_isx019_private.stream = type; + + return OK; +} + +static int isx019_stop_capture(imgsensor_stream_type_t type) +{ + uint8_t regval; + + regval = FPGA_DATA_OUTPUT_STOP; + fpga_lock(); + fpga_i2c_write(FPGA_DATA_OUTPUT, ®val, 1); + fpga_activate_setting(); + fpga_unlock(); + return OK; +} + +static int isx019_get_supported_value(uint32_t id, + FAR imgsensor_supported_value_t *val) +{ + int ret = OK; + FAR struct isx019_default_value_s *def = &g_isx019_private.default_value; + + DEBUGASSERT(val); + + switch (id) + { + case IMGSENSOR_ID_BRIGHTNESS: + val->type = IMGSENSOR_CTRL_TYPE_INTEGER; + SET_RANGE(val->u.range, MIN_BRIGHTNESS, MAX_BRIGHTNESS, + STEP_BRIGHTNESS, def->brightness); + break; + + case IMGSENSOR_ID_CONTRAST: + val->type = IMGSENSOR_CTRL_TYPE_INTEGER; + SET_RANGE(val->u.range, MIN_CONTRAST, MAX_CONTRAST, + STEP_CONTRAST, def->contrast); + break; + + case IMGSENSOR_ID_SATURATION: + val->type = IMGSENSOR_CTRL_TYPE_INTEGER; + SET_RANGE(val->u.range, MIN_SATURATION, MAX_SATURATION, + STEP_SATURATION, def->saturation); + break; + + case IMGSENSOR_ID_HUE: + val->type = IMGSENSOR_CTRL_TYPE_INTEGER; + SET_RANGE(val->u.range, MIN_HUE, MAX_HUE, + STEP_HUE, def->hue); + break; + + case IMGSENSOR_ID_AUTO_WHITE_BALANCE: + val->type = IMGSENSOR_CTRL_TYPE_INTEGER; + SET_RANGE(val->u.range, MIN_AWB, MAX_AWB, + STEP_AWB, def->awb); + break; + + case IMGSENSOR_ID_GAMMA: + val->type = IMGSENSOR_CTRL_TYPE_INTEGER; + SET_RANGE(val->u.range, MIN_GAMMA, MAX_GAMMA, + STEP_GAMMA, def->gamma); + break; + + case IMGSENSOR_ID_EXPOSURE: + val->type = IMGSENSOR_CTRL_TYPE_INTEGER; + SET_RANGE(val->u.range, MIN_EXPOSURE, MAX_EXPOSURE, + STEP_EXPOSURE, def->ev); + break; + + case IMGSENSOR_ID_HFLIP_VIDEO: + val->type = IMGSENSOR_CTRL_TYPE_INTEGER; + SET_RANGE(val->u.range, MIN_HFLIP, MAX_HFLIP, + STEP_HFLIP, def->hflip_video); + break; + + case IMGSENSOR_ID_VFLIP_VIDEO: + val->type = IMGSENSOR_CTRL_TYPE_INTEGER; + SET_RANGE(val->u.range, MIN_VFLIP, MAX_VFLIP, + STEP_VFLIP, def->vflip_video); + break; + + case IMGSENSOR_ID_HFLIP_STILL: + val->type = IMGSENSOR_CTRL_TYPE_INTEGER; + SET_RANGE(val->u.range, MIN_HFLIP, MAX_HFLIP, + STEP_HFLIP, def->hflip_still); + break; + + case IMGSENSOR_ID_VFLIP_STILL: + val->type = IMGSENSOR_CTRL_TYPE_INTEGER; + SET_RANGE(val->u.range, MIN_VFLIP, MAX_VFLIP, + STEP_VFLIP, def->hflip_still); + break; + + case IMGSENSOR_ID_SHARPNESS: + val->type = IMGSENSOR_CTRL_TYPE_INTEGER; + SET_RANGE(val->u.range, MIN_SHARPNESS, MAX_SHARPNESS, + STEP_SHARPNESS, def->sharpness); + break; + + case IMGSENSOR_ID_COLORFX: + val->type = IMGSENSOR_CTRL_TYPE_INTEGER_MENU; + SET_DISCRETE(val->u.discrete, + NR_COLORFX, + g_isx019_colorfx, + IMGSENSOR_COLORFX_NONE); + break; + + case IMGSENSOR_ID_EXPOSURE_AUTO: + val->type = IMGSENSOR_CTRL_TYPE_INTEGER; + SET_RANGE(val->u.range, MIN_AE, MAX_AE, + STEP_AE, def->ae); + break; + + case IMGSENSOR_ID_EXPOSURE_ABSOLUTE: + val->type = IMGSENSOR_CTRL_TYPE_INTEGER; + SET_RANGE(val->u.range, MIN_EXPOSURETIME, MAX_EXPOSURETIME, + STEP_EXPOSURETIME, def->exptime); + break; + + case IMGSENSOR_ID_AUTO_N_PRESET_WB: + val->type = IMGSENSOR_CTRL_TYPE_INTEGER_MENU; + SET_DISCRETE(val->u.discrete, + NR_WBMODE, + g_isx019_wbmode, + IMGSENSOR_WHITE_BALANCE_AUTO); + break; + + case IMGSENSOR_ID_WIDE_DYNAMIC_RANGE: + val->type = IMGSENSOR_CTRL_TYPE_INTEGER; + SET_RANGE(val->u.range, MIN_HDR, MAX_HDR, + STEP_HDR, def->hdr); + break; + + case IMGSENSOR_ID_ISO_SENSITIVITY: + val->type = IMGSENSOR_CTRL_TYPE_INTEGER; + SET_RANGE(val->u.range, MIN_ISO, MAX_ISO, + STEP_ISO, def->iso); + break; + + case IMGSENSOR_ID_ISO_SENSITIVITY_AUTO: + val->type = IMGSENSOR_CTRL_TYPE_INTEGER; + SET_RANGE(val->u.range, MIN_AUTOISO, MAX_AUTOISO, + STEP_AUTOISO, def->iso_auto); + break; + + case IMGSENSOR_ID_EXPOSURE_METERING: + val->type = IMGSENSOR_CTRL_TYPE_INTEGER; + SET_RANGE(val->u.range, MIN_METER, MAX_METER, + STEP_METER, def->meter); + break; + + case IMGSENSOR_ID_3A_LOCK: + val->type = IMGSENSOR_CTRL_TYPE_BITMASK; + SET_RANGE(val->u.range, MIN_3ALOCK, MAX_3ALOCK, + STEP_3ALOCK, def->threealock); + break; + + case IMGSENSOR_ID_3A_PARAMETER: + val->type = IMGSENSOR_CTRL_TYPE_U8; + SET_ELEMS(val->u.elems, NRELEM_3APARAM, MIN_3APARAM, MAX_3APARAM, + STEP_3APARAM); + break; + + case IMGSENSOR_ID_3A_STATUS: + val->type = IMGSENSOR_CTRL_TYPE_INTEGER; + SET_RANGE(val->u.range, MIN_3ASTATUS, MAX_3ASTATUS, + STEP_3ASTATUS, def->threeastatus); + break; + + case IMGSENSOR_ID_JPEG_QUALITY: + val->type = IMGSENSOR_CTRL_TYPE_INTEGER; + SET_RANGE(val->u.range, MIN_JPGQUALITY, MAX_JPGQUALITY, + STEP_JPGQUALITY, def->jpgquality); + break; + + case IMGSENSOR_ID_CLIP_VIDEO: + case IMGSENSOR_ID_CLIP_STILL: + val->type = IMGSENSOR_CTRL_TYPE_U32; + SET_ELEMS(val->u.elems, NRELEM_CLIP, MIN_CLIP, MAX_CLIP, + STEP_CLIP); + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int32_t not_convert(int32_t val) +{ + return val; +} + +static int32_t convert_brightness_is2reg(int32_t val) +{ + return (val << 2); +} + +static int32_t convert_brightness_reg2is(int32_t val) +{ + return (val >> 2); +} + +static int32_t convert_hue_is2reg(int32_t val) +{ + return (val * 90) / 128; +} + +static int32_t convert_hue_reg2is(int32_t val) +{ + return (val * 128) / 90; +} + +static int32_t convert_awb_is2reg(int32_t val) +{ + return (val == 1) ? 0 : 2; +} + +static int32_t convert_awb_reg2is(int32_t val) +{ + return (val == 0) ? 1 : 0; +} + +static int32_t convert_hdr_is2reg(int32_t val) +{ + int32_t ret = AEWDMODE_AUTO; + + switch (val) + { + case 0: + ret = AEWDMODE_NORMAL; + break; + + case 1: + ret = AEWDMODE_AUTO; + break; + + case 2: + ret = AEWDMODE_HDR; + break; + + default: + /* It may not come here because the value has already been validated + * in validate_value(). + */ + + break; + } + + return ret; +} + +static int32_t convert_hdr_reg2is(int32_t val) +{ + int32_t ret; + + switch (val) + { + case AEWDMODE_NORMAL: + ret = 0; + break; + + case AEWDMODE_AUTO: + ret = 1; + break; + + default: /* AEWDMODE_HDR */ + ret = 2; + break; + } + + return ret; +} + +static convert_t get_reginfo(uint32_t id, bool is_set, + FAR isx019_reginfo_t *reg) +{ + convert_t cvrt = NULL; + + DEBUGASSERT(reg); + + switch (id) + { + case IMGSENSOR_ID_BRIGHTNESS: + SET_REGINFO(reg, CAT_PICTTUNE, UIBRIGHTNESS, 2); + cvrt = is_set ? convert_brightness_is2reg + : convert_brightness_reg2is; + break; + + case IMGSENSOR_ID_CONTRAST: + SET_REGINFO(reg, CAT_PICTTUNE, UICONTRAST, 1); + cvrt = not_convert; + break; + + case IMGSENSOR_ID_SATURATION: + SET_REGINFO(reg, CAT_PICTTUNE, UISATURATION, 1); + cvrt = not_convert; + break; + + case IMGSENSOR_ID_HUE: + SET_REGINFO(reg, CAT_PICTTUNE, UIHUE, 1); + cvrt = is_set ? convert_hue_is2reg : convert_hue_reg2is; + break; + + case IMGSENSOR_ID_AUTO_WHITE_BALANCE: + SET_REGINFO(reg, CAT_CATAWB, AWBMODE, 1); + cvrt = is_set ? convert_awb_is2reg : convert_awb_reg2is; + break; + + case IMGSENSOR_ID_EXPOSURE: + SET_REGINFO(reg, CAT_AEDGRM, EVSEL, 1); + cvrt = not_convert; + break; + + case IMGSENSOR_ID_SHARPNESS: + SET_REGINFO(reg, CAT_PICTTUNE, UISHARPNESS, 1); + cvrt = not_convert; + break; + + case IMGSENSOR_ID_WIDE_DYNAMIC_RANGE: + SET_REGINFO(reg, CAT_AEWD, AEWDMODE, 1); + cvrt = is_set ? convert_hdr_is2reg : convert_hdr_reg2is; + break; + + default: + break; + } + + return cvrt; +} + +static void set_flip(FAR uint8_t *flip, uint8_t direction, int32_t val) +{ + DEBUGASSERT(flip); + + *flip = (val == 0) ? (*flip & ~direction) : (*flip | direction); +} + +static int set_hflip_video(imgsensor_value_t val) +{ + set_flip(&g_isx019_private.flip_video, H_REVERSE, val.value32); + if (g_isx019_private.stream == IMGSENSOR_STREAM_TYPE_VIDEO) + { + activate_flip(IMGSENSOR_STREAM_TYPE_VIDEO); + } + + return OK; +} + +static int set_vflip_video(imgsensor_value_t val) +{ + set_flip(&g_isx019_private.flip_video, V_REVERSE, val.value32); + if (g_isx019_private.stream == IMGSENSOR_STREAM_TYPE_VIDEO) + { + activate_flip(IMGSENSOR_STREAM_TYPE_VIDEO); + } + + return OK; +} + +static int set_hflip_still(imgsensor_value_t val) +{ + set_flip(&g_isx019_private.flip_still, H_REVERSE, val.value32); + if (g_isx019_private.stream == IMGSENSOR_STREAM_TYPE_STILL) + { + activate_flip(IMGSENSOR_STREAM_TYPE_STILL); + } + + return OK; +} + +static int set_vflip_still(imgsensor_value_t val) +{ + set_flip(&g_isx019_private.flip_still, V_REVERSE, val.value32); + if (g_isx019_private.stream == IMGSENSOR_STREAM_TYPE_STILL) + { + activate_flip(IMGSENSOR_STREAM_TYPE_STILL); + } + + return OK; +} + +static int set_colorfx(imgsensor_value_t val) +{ + int ret = -EINVAL; + FAR isx019_default_value_t *def = &g_isx019_private.default_value; + int32_t sat; + int32_t sharp; + + /* ISX019 realize color effects by adjusting saturation and sharpness. */ + + switch (val.value32) + { + case IMGSENSOR_COLORFX_NONE: + + sat = def->saturation; + sharp = def->sharpness; + break; + + case IMGSENSOR_COLORFX_BW: + + sat = BW_COLORS_SATURATION; + sharp = def->sharpness; + break; + + case IMGSENSOR_COLORFX_VIVID: + + sat = VIVID_COLORS_SATURATION; + sharp = VIVID_COLORS_SHARPNESS; + break; + + default: + + /* It may not come here because the value has already been validated + * in validate_value(). + */ + + break; + } + + ret = isx019_i2c_write(CAT_PICTTUNE, UISATURATION, (FAR uint8_t *)&sat, 1); + if (ret == OK) + { + ret = isx019_i2c_write(CAT_PICTTUNE, + UISHARPNESS, + (FAR uint8_t *)&sharp, + 1); + if (ret == OK) + { + g_isx019_private.colorfx = val.value32; + } + } + + return ret; +} + +static int set_ae(imgsensor_value_t val) +{ + uint32_t regval = 0; + + if (val.value32 == IMGSENSOR_EXPOSURE_AUTO) + { + regval = 0; + } + else + { + isx019_i2c_read(CAT_AESOUT, SHT_TIME, (FAR uint8_t *)®val, 4); + } + + return isx019_i2c_write(CAT_CATAE, SHT_PRIMODE, (FAR uint8_t *)®val, 4); +} + +static int set_exptime(imgsensor_value_t val) +{ + uint32_t regval; + + /* Take into account the master clock and convert unit. + * image sensor I/F : 100usec + * register : 1usec + */ + + regval = val.value32 * 100 * g_isx019_private.clock_ratio; + + return isx019_i2c_write(CAT_CATAE, SHT_PRIMODE, (FAR uint8_t *)®val, 4); +} + +static int set_wbmode(imgsensor_value_t val) +{ + /* AWBMODE mode0 : auto, mode4 : user defined white balance + * AWBUSER_NO definition number for AWBMODE = mode4 + * USER0_R, USER1_B : R,B value for AWBUSER_NO = 0 + * USER1_R, USER1_B : R,B value for AWBUSER_NO = 1 + */ + + uint8_t mode; + uint16_t r_addr; + uint16_t b_addr; + uint16_t r; + uint16_t b; + static bool toggle = false; + + if (toggle) + { + r_addr = USER0_R; + b_addr = USER0_B; + toggle = false; + } + else + { + r_addr = USER1_R; + b_addr = USER1_B; + toggle = true; + } + + switch (val.value32) + { + case IMGSENSOR_WHITE_BALANCE_AUTO: + mode = AWBMODE_AUTO; + break; + + case IMGSENSOR_WHITE_BALANCE_INCANDESCENT: + r = RED_INCANDESCENT; + b = BLUE_INCANDESCENT; + mode = AWBMODE_MANUAL; + break; + + case IMGSENSOR_WHITE_BALANCE_FLUORESCENT: + r = RED_FLUORESCENT; + b = BLUE_FLUORESCENT; + mode = AWBMODE_MANUAL; + break; + + case IMGSENSOR_WHITE_BALANCE_DAYLIGHT: + r = RED_DAYLIGHT; + b = BLUE_DAYLIGHT; + mode = AWBMODE_MANUAL; + break; + + case IMGSENSOR_WHITE_BALANCE_CLOUDY: + r = RED_CLOUDY; + b = BLUE_CLOUDY; + mode = AWBMODE_MANUAL; + break; + + default: /* IMGSENSOR_WHITE_BALANCE_SHADE */ + r = RED_SHADE; + b = BLUE_SHADE; + mode = AWBMODE_MANUAL; + break; + } + + isx019_i2c_write(CAT_AWB_USERTYPE, r_addr, (FAR uint8_t *)&r, 2); + isx019_i2c_write(CAT_AWB_USERTYPE, b_addr, (FAR uint8_t *)&b, 2); + isx019_i2c_write(CAT_CATAWB, AWBUSER_NO, (FAR uint8_t *)&toggle, 1); + isx019_i2c_write(CAT_CATAWB, AWBMODE, &mode, 1); + + g_isx019_private.wb_mode = val.value32; + + return OK; +} + +static int set_meter(imgsensor_value_t val) +{ + uint8_t normal; + uint8_t hdr; + + switch (val.value32) + { + case IMGSENSOR_EXPOSURE_METERING_AVERAGE: + normal = AEWEIGHT_AVERAGE; + hdr = AEWEIGHTHDR_AVERAGE; + break; + + case IMGSENSOR_EXPOSURE_METERING_CENTER_WEIGHTED: + normal = AEWEIGHT_CENTER; + hdr = AEWEIGHTHDR_CENTER; + break; + + case IMGSENSOR_EXPOSURE_METERING_SPOT: + normal = AEWEIGHT_SPOT; + hdr = AEWEIGHTHDR_SPOT; + break; + + default: /* IMGSENSOR_EXPOSURE_METERING_MATRIX */ + normal = AEWEIGHT_MATRIX; + hdr = AEWEIGHTHDR_MATRIX; + break; + } + + isx019_i2c_write(CAT_AUTOCTRL, AEWEIGHTMODE, &normal, 1); + isx019_i2c_write(CAT_AEWD, AEWEIGHTMODE_WD, &hdr, 1); + + return OK; +} + +static int set_3alock(imgsensor_value_t val) +{ + uint8_t regval; + + regval = (val.value32 & IMGSENSOR_LOCK_WHITE_BALANCE) ? AWBMODE_HOLD + : AWBMODE_AUTO; + isx019_i2c_write(CAT_CATAWB, AWBMODE, ®val, 1); + + regval = (val.value32 & IMGSENSOR_LOCK_EXPOSURE) ? AEMODE_HOLD + : AEMODE_AUTO; + isx019_i2c_write(CAT_CATAE, AEMODE, ®val, 1); + + return OK; +} + +static int set_3aparameter(imgsensor_value_t val) +{ + uint16_t gain; + uint8_t regval; + + if (val.p_u8 == NULL) + { + return -EINVAL; + } + + /* Convert unit + * GAIN_LEVEL register(accessed in get_3aparameter()) : 0.3dB + * GAIN_PRIMODE register(accessed in this function) : 0.1dB + */ + + gain = val.p_u8[OFFSET_3APARAMETER_AE_GAIN] * 3; + + isx019_i2c_write + (CAT_AWB_USERTYPE, USER4_R, &val.p_u8[OFFSET_3APARAMETER_AWB_R], 2); + isx019_i2c_write + (CAT_AWB_USERTYPE, USER4_B, &val.p_u8[OFFSET_3APARAMETER_AWB_B], 2); + + regval = 4; + isx019_i2c_write(CAT_CATAWB, AWBUSER_NO, (FAR uint8_t *)®val, 1); + + regval = AWBMODE_MANUAL; + isx019_i2c_write(CAT_CATAWB, AWBMODE, ®val, 1); + + isx019_i2c_write + (CAT_CATAE, SHT_PRIMODE, &val.p_u8[OFFSET_3APARAMETER_AE_SHTTIME], 4); + isx019_i2c_write(CAT_CATAE, GAIN_PRIMODE, (FAR uint8_t *)&gain, 2); + + return OK; +} + +static uint16_t calc_gain(double iso) +{ + double gain; + + gain = 1 + 10 * log(iso) / M_LN10; + + /* In the above formula, the unit of gain is dB. + * Because the register has the 0.1dB unit, + * return 10 times dB value. + */ + + return (uint16_t)(gain * 10); +} + +static int set_iso(imgsensor_value_t val) +{ + uint16_t gain; + + /* ISX019 has not ISO sensitivity register but gain register. + * So, calculate gain from ISO sensitivity. + */ + + gain = calc_gain(val.value32 / 1000); + isx019_i2c_write(CAT_CATAE, GAIN_PRIMODE, (FAR uint8_t *)&gain, 2); + + g_isx019_private.iso = val.value32; + return OK; +} + +static int set_iso_auto(imgsensor_value_t val) +{ + uint8_t buf; + uint16_t gain; + + if (val.value32 == IMGSENSOR_ISO_SENSITIVITY_AUTO) + { + gain = 0; + g_isx019_private.iso = 0; + } + else /* IMGSENSOR_ISO_SENSITIVITY_MANUAL */ + { + isx019_i2c_read(CAT_CATAE, GAIN_PRIMODE, (FAR uint8_t *)&gain, 2); + + if (gain == 0) + { + /* gain = 0 means auto adjustment mode. + * In such a case, apply current auto adjustment value + * as manual setting. + * Note : auto adjustment value register has the unit 0.3dB. + * So, convert the unit to 0.1dB. + */ + + isx019_i2c_read(CAT_AECOM, GAIN_LEVEL, &buf, 1); + gain = buf * 3; + } + + g_isx019_private.iso = val.value32; + } + + return isx019_i2c_write(CAT_CATAE, GAIN_PRIMODE, (FAR uint8_t *)&gain, 2); +} + +static uint16_t calc_gamma_regval(double in, double gamma) +{ + double out; + + /* 1) Calculate the normalized result. + * formula : output = input^gamma + * 2) Perform scaling for ISX019 register. + * 3) Change the format from the floating-point number type + * to the fixed-point number type according to the register. + */ + + out = pow(in, gamma); + out *= GAM_OUTPUT_SCALE; + + return ((uint8_t)out) << 2; +} + +static int set_gamma(imgsensor_value_t val) +{ + int i; + uint16_t regval; + uint16_t offset; + double gamma; + + gamma = (double)val.value32 / 1000; + + /* ISX019 gamma adjustment feature is constructed by + * registers for low-input and registers for high-input. + */ + + offset = GAM_KNOT_C0; + + for (i = 0; i < NR_GAM_KNOT_LOWINPUT; i++) + { + regval = calc_gamma_regval((double)i * GAM_LOWINPUT_INTERVAL, gamma); + isx019_i2c_write(CAT_PICTGAMMA, offset, (FAR uint8_t *)®val, 2); + offset += 2; + } + + offset = GAM_KNOT_C11; + + for (i = 0; i < NR_GAM_KNOT_HIGHINPUT; i++) + { + regval = calc_gamma_regval + ((double)(i + 1) * GAM_HIGHINPUT_INTERVAL, gamma); + isx019_i2c_write(CAT_PICTGAMMA, offset, (FAR uint8_t *)®val, 2); + offset += 2; + } + + /* Special register setting. + * GAM_KNOT_C9 and GAM_KNOT_C10 need to be set + * to be continuous. + * So, this driver set GAM_KNOT_C10 = GAM_KNOT_C8, + * GAM_KNOT_C9 = GAM_KNOT_C11. + */ + + isx019_i2c_read(CAT_PICTGAMMA, GAM_KNOT_C8, (FAR uint8_t *)®val, 2); + isx019_i2c_write(CAT_PICTGAMMA, GAM_KNOT_C10, (FAR uint8_t *)®val, 2); + isx019_i2c_read(CAT_PICTGAMMA, GAM_KNOT_C11, (FAR uint8_t *)®val, 2); + isx019_i2c_write(CAT_PICTGAMMA, GAM_KNOT_C9, (FAR uint8_t *)®val, 2); + + g_isx019_private.gamma = val.value32; + return OK; +} + +static void search_dqt_data(int32_t quality, + FAR uint8_t **y_head, FAR uint8_t **y_calc, + FAR uint8_t **c_head, FAR uint8_t **c_calc) +{ + int i; + FAR isx019_fpga_jpg_quality_t *jpg = &g_isx019_jpg_quality[0]; + + *y_head = NULL; + *y_calc = NULL; + *c_head = NULL; + *c_calc = NULL; + + /* Search approximate DQT data from a table by rounding quality. */ + + quality = ((quality + 5) / 10) * 10; + if (quality == 0) + { + /* Set the minimum value of quality to 10. */ + + quality = 10; + } + + for (i = 0; i < NR_JPGSETTING_TBL; i++) + { + if (quality == jpg->quality) + { + *y_head = jpg->y_head; + *y_calc = jpg->y_calc; + *c_head = jpg->c_head; + *c_calc = jpg->c_calc; + break; + } + + jpg++; + } +} + +int set_dqt(uint8_t component, uint8_t target, FAR uint8_t *buf) +{ + int i; + uint8_t addr; + uint8_t select; + uint8_t data; + uint8_t regval; + + if (target == FPGA_DQT_DATA) + { + addr = FPGA_DQT_ADDRESS; + select = FPGA_DQT_SELECT; + data = FPGA_DQT_DATA; + } + else + { + addr = FPGA_DQT_CALC_ADDRESS; + select = FPGA_DQT_CALC_SELECT; + data = FPGA_DQT_CALC_DATA; + } + + fpga_i2c_write(select, &component, 1); + for (i = 0; i < JPEG_DQT_ARRAY_SIZE; i++) + { + regval = i | FPGA_DQT_WRITE | FPGA_DQT_BUFFER; + fpga_i2c_write(addr, ®val, 1); + fpga_i2c_write(data, &buf[i], 1); + } + + return OK; +} + +static int set_jpg_quality(imgsensor_value_t val) +{ + FAR uint8_t *y_head; + FAR uint8_t *y_calc; + FAR uint8_t *c_head; + FAR uint8_t *c_calc; + + /* Set JPEG quality by setting DQT information to FPGA. */ + + search_dqt_data(val.value32, &y_head, &y_calc, &c_head, &c_calc); + if ((y_head == NULL) || + (y_calc == NULL) || + (c_head == NULL) || + (c_calc == NULL)) + { + return -EINVAL; + } + + fpga_lock(); + + /* Update DQT data and activate them. */ + + set_dqt(FPGA_DQT_LUMA, FPGA_DQT_DATA, y_head); + set_dqt(FPGA_DQT_CHROMA, FPGA_DQT_DATA, c_head); + set_dqt(FPGA_DQT_LUMA, FPGA_DQT_CALC_DATA, y_calc); + set_dqt(FPGA_DQT_CHROMA, FPGA_DQT_CALC_DATA, c_calc); + fpga_activate_setting(); + + /* Update non-active side in preparation for other activation trigger. */ + + set_dqt(FPGA_DQT_LUMA, FPGA_DQT_DATA, y_head); + set_dqt(FPGA_DQT_CHROMA, FPGA_DQT_DATA, c_head); + set_dqt(FPGA_DQT_LUMA, FPGA_DQT_CALC_DATA, y_calc); + set_dqt(FPGA_DQT_CHROMA, FPGA_DQT_CALC_DATA, c_calc); + + fpga_unlock(); + + g_isx019_private.jpg_quality = val.value32; + return OK; +} + +static int initialize_jpg_quality(void) +{ + imgsensor_value_t val; + + val.value32 = CONFIG_VIDEO_ISX019_INITIAL_JPEG_QUALITY; + return set_jpg_quality(val); +} + +static bool validate_clip_setting(FAR uint32_t *clip) +{ + bool ret = false; + uint32_t w; + uint32_t h; + + DEBUGASSERT(clip); + + w = clip[IMGSENSOR_CLIP_INDEX_WIDTH]; + h = clip[IMGSENSOR_CLIP_INDEX_HEIGHT]; + + if (((w == 1280) && (h == 720)) || + ((w == 640) && (h == 360)) || + ((w == 0) && (h == 0))) + { + ret = true; + } + + return ret; +} + +static int set_clip(FAR uint32_t *val, FAR isx019_rect_t *target) +{ + if (val == NULL) + { + return -EINVAL; + } + + if (!validate_clip_setting(val)) + { + return -EINVAL; + } + + target->left = (int32_t)val[IMGSENSOR_CLIP_INDEX_LEFT]; + target->top = (int32_t)val[IMGSENSOR_CLIP_INDEX_TOP]; + target->width = val[IMGSENSOR_CLIP_INDEX_WIDTH]; + target->height = val[IMGSENSOR_CLIP_INDEX_HEIGHT]; + + return OK; +} + +static int set_clip_video(imgsensor_value_t val) +{ + return set_clip(val.p_u32, &g_isx019_private.clip_video); +} + +static int set_clip_still(imgsensor_value_t val) +{ + return set_clip(val.p_u32, &g_isx019_private.clip_still); +} + +static setvalue_t set_value_func(uint32_t id) +{ + setvalue_t func = NULL; + + switch (id) + { + case IMGSENSOR_ID_GAMMA: + func = set_gamma; + break; + + case IMGSENSOR_ID_HFLIP_VIDEO: + func = set_hflip_video; + break; + + case IMGSENSOR_ID_VFLIP_VIDEO: + func = set_vflip_video; + break; + + case IMGSENSOR_ID_HFLIP_STILL: + func = set_hflip_still; + break; + + case IMGSENSOR_ID_VFLIP_STILL: + func = set_vflip_still; + break; + + case IMGSENSOR_ID_COLORFX: + func = set_colorfx; + break; + + case IMGSENSOR_ID_EXPOSURE_AUTO: + func = set_ae; + break; + + case IMGSENSOR_ID_EXPOSURE_ABSOLUTE: + func = set_exptime; + break; + + case IMGSENSOR_ID_AUTO_N_PRESET_WB: + func = set_wbmode; + break; + + case IMGSENSOR_ID_ISO_SENSITIVITY: + func = set_iso; + break; + + case IMGSENSOR_ID_ISO_SENSITIVITY_AUTO: + func = set_iso_auto; + break; + + case IMGSENSOR_ID_EXPOSURE_METERING: + func = set_meter; + break; + + case IMGSENSOR_ID_3A_LOCK: + func = set_3alock; + break; + + case IMGSENSOR_ID_3A_PARAMETER: + func = set_3aparameter; + break; + + case IMGSENSOR_ID_JPEG_QUALITY: + func = set_jpg_quality; + break; + + case IMGSENSOR_ID_CLIP_VIDEO: + func = set_clip_video; + break; + + case IMGSENSOR_ID_CLIP_STILL: + func = set_clip_still; + break; + + default: + break; + } + + return func; +} + +static int32_t get_flip(uint8_t *flip, uint8_t direction) +{ + DEBUGASSERT(flip); + + return (*flip & direction) ? 1 : 0; +} + +static int get_hflip_video(FAR imgsensor_value_t *val) +{ + if (val == NULL) + { + return -EINVAL; + } + + val->value32 = get_flip(&g_isx019_private.flip_video, H_REVERSE); + return OK; +} + +static int get_vflip_video(FAR imgsensor_value_t *val) +{ + if (val == NULL) + { + return -EINVAL; + } + + val->value32 = get_flip(&g_isx019_private.flip_video, V_REVERSE); + return OK; +} + +static int get_hflip_still(FAR imgsensor_value_t *val) +{ + if (val == NULL) + { + return -EINVAL; + } + + val->value32 = get_flip(&g_isx019_private.flip_still, H_REVERSE); + return OK; +} + +static int get_vflip_still(FAR imgsensor_value_t *val) +{ + if (val == NULL) + { + return -EINVAL; + } + + val->value32 = get_flip(&g_isx019_private.flip_still, V_REVERSE); + return OK; +} + +static int get_colorfx(FAR imgsensor_value_t *val) +{ + if (val == NULL) + { + return -EINVAL; + } + + val->value32 = g_isx019_private.colorfx; + return OK; +} + +static int get_ae(FAR imgsensor_value_t *val) +{ + uint32_t regval; + + if (val == NULL) + { + return -EINVAL; + } + + isx019_i2c_read(CAT_CATAE, SHT_PRIMODE, (FAR uint8_t *)®val, 4); + + val->value32 = (regval == 0) ? IMGSENSOR_EXPOSURE_AUTO + : IMGSENSOR_EXPOSURE_MANUAL; + + return OK; +} + +static int get_exptime(FAR imgsensor_value_t *val) +{ + uint32_t regval; + + isx019_i2c_read(CAT_AESOUT, SHT_TIME, (FAR uint8_t *)®val, 4); + + /* Round up to the nearest 100usec for eliminating errors in reverting to + * application value because this driver converts application value to + * value that takes into account the clock ratio and unit difference. + */ + + val->value32 = ((regval / g_isx019_private.clock_ratio) + 99) / 100; + + return OK; +} + +static int get_wbmode(FAR imgsensor_value_t *val) +{ + if (val == NULL) + { + return -EINVAL; + } + + val->value32 = g_isx019_private.wb_mode; + + return OK; +} + +static int get_meter(FAR imgsensor_value_t *val) +{ + uint8_t regval; + + if (val == NULL) + { + return -EINVAL; + } + + isx019_i2c_read(CAT_AUTOCTRL, AEWEIGHTMODE, ®val, 1); + + switch (regval) + { + case AEWEIGHT_AVERAGE: + val->value32 = IMGSENSOR_EXPOSURE_METERING_AVERAGE; + break; + + case AEWEIGHT_CENTER: + val->value32 = IMGSENSOR_EXPOSURE_METERING_CENTER_WEIGHTED; + break; + + case AEWEIGHT_SPOT: + val->value32 = IMGSENSOR_EXPOSURE_METERING_SPOT; + break; + + default: /* AEWEIGHT_MATRIX */ + val->value32 = IMGSENSOR_EXPOSURE_METERING_MATRIX; + break; + } + + return OK; +} + +static int get_3alock(FAR imgsensor_value_t *val) +{ + uint8_t regval; + uint8_t awb; + uint8_t ae; + + if (val == NULL) + { + return -EINVAL; + } + + isx019_i2c_read(CAT_CATAWB, AWBMODE, ®val, 1); + awb = (regval == AWBMODE_AUTO) ? 0 : IMGSENSOR_LOCK_WHITE_BALANCE; + + isx019_i2c_read(CAT_CATAE, AEMODE, ®val, 1); + ae = (regval == AEMODE_AUTO) ? 0 : IMGSENSOR_LOCK_EXPOSURE; + + val->value32 = awb | ae; + + return OK; +} + +static int get_3aparameter(FAR imgsensor_value_t *val) +{ + if (val == NULL) + { + return -EINVAL; + } + + if (val->p_u8 == NULL) + { + return -EINVAL; + } + + isx019_i2c_read + (CAT_AWBSOUT, CONT_R, &val->p_u8[OFFSET_3APARAMETER_AWB_R], 2); + isx019_i2c_read + (CAT_AWBSOUT, CONT_B, &val->p_u8[OFFSET_3APARAMETER_AWB_B], 2); + isx019_i2c_read + (CAT_AESOUT, SHT_TIME, &val->p_u8[OFFSET_3APARAMETER_AE_SHTTIME], 4); + isx019_i2c_read + (CAT_AECOM, GAIN_LEVEL, &val->p_u8[OFFSET_3APARAMETER_AE_GAIN], 1); + + return OK; +} + +static int get_3astatus(FAR imgsensor_value_t *val) +{ + uint8_t regval; + + if (val == NULL) + { + return -EINVAL; + } + + isx019_i2c_read(CAT_AWBSOUT, AWBSTS, ®val, 1); + + switch (regval) + { + case AWBSTS_STABLE: + val->value32 = IMGSENSOR_3A_STATUS_STABLE; + break; + + case AWBSTS_AEWAIT: + val->value32 = IMGSENSOR_3A_STATUS_AE_OPERATING; + break; + + default: + val->value32 = IMGSENSOR_3A_STATUS_AE_OPERATING | + IMGSENSOR_3A_STATUS_AWB_OPERATING; + } + + return OK; +} + +static double calc_iso(double gain) +{ + int k; + double z; + double r; + + /* ISO sensitivity = 10^((gain - 1) / 10) + * So, replace z = (gain - 1) / 10 and + * calculate 10^z. + */ + + /* Devide z into integer and other parts. + * z = log10(E) (k * ln2 + r) + * (k : integer, r < 0.5 * ln2) + * + * Then, 10^z = (2^k) * e^r (r < 0.5 * ln2) + */ + + z = (gain - 1) / 10; + + k = z * M_LN10 / M_LN2; + r = z * M_LN10 - k * M_LN2; + + return (1 << k) * exp(r); +} + +static int get_iso(FAR imgsensor_value_t *val) +{ + uint8_t buf = 0; + + if (val == NULL) + { + return -EINVAL; + } + + if (g_isx019_private.iso == 0) + { + /* iso = 0 means auto adjustment mode. + * In such a case, get gain from auto adjustment value register, + * which has the unit 0.3dB, and convert the gain to ISO. + */ + + isx019_i2c_read(CAT_AECOM, GAIN_LEVEL, &buf, 1); + val->value32 = calc_iso((double)buf * 0.3) * USEC_PER_MSEC; + } + else + { + val->value32 = g_isx019_private.iso; + } + + return OK; +} + +static int get_iso_auto(FAR imgsensor_value_t *val) +{ + uint16_t gain; + + if (val == NULL) + { + return -EINVAL; + } + + isx019_i2c_read(CAT_CATAE, GAIN_PRIMODE, (FAR uint8_t *)&gain, 2); + + val->value32 = (gain == 0) ? IMGSENSOR_ISO_SENSITIVITY_AUTO + : IMGSENSOR_ISO_SENSITIVITY_MANUAL; + return OK; +} + +static int get_gamma(FAR imgsensor_value_t *val) +{ + if (val == NULL) + { + return -EINVAL; + } + + val->value32 = g_isx019_private.gamma; + + return OK; +} + +static int get_jpg_quality(FAR imgsensor_value_t *val) +{ + if (val == NULL) + { + return -EINVAL; + } + + val->value32 = g_isx019_private.jpg_quality; + return OK; +} + +static getvalue_t get_value_func(uint32_t id) +{ + getvalue_t func = NULL; + + switch (id) + { + case IMGSENSOR_ID_GAMMA: + func = get_gamma; + break; + + case IMGSENSOR_ID_HFLIP_VIDEO: + func = get_hflip_video; + break; + + case IMGSENSOR_ID_VFLIP_VIDEO: + func = get_vflip_video; + break; + + case IMGSENSOR_ID_HFLIP_STILL: + func = get_hflip_still; + break; + + case IMGSENSOR_ID_VFLIP_STILL: + func = get_vflip_still; + break; + + case IMGSENSOR_ID_COLORFX: + func = get_colorfx; + break; + + case IMGSENSOR_ID_EXPOSURE_AUTO: + func = get_ae; + break; + + case IMGSENSOR_ID_EXPOSURE_ABSOLUTE: + func = get_exptime; + break; + + case IMGSENSOR_ID_AUTO_N_PRESET_WB: + func = get_wbmode; + break; + + case IMGSENSOR_ID_ISO_SENSITIVITY: + func = get_iso; + break; + + case IMGSENSOR_ID_ISO_SENSITIVITY_AUTO: + func = get_iso_auto; + break; + + case IMGSENSOR_ID_EXPOSURE_METERING: + func = get_meter; + break; + + case IMGSENSOR_ID_3A_LOCK: + func = get_3alock; + break; + + case IMGSENSOR_ID_3A_PARAMETER: + func = get_3aparameter; + break; + + case IMGSENSOR_ID_3A_STATUS: + func = get_3astatus; + break; + + case IMGSENSOR_ID_JPEG_QUALITY: + func = get_jpg_quality; + break; + + default: + break; + } + + return func; +} + +static int isx019_get_value(uint32_t id, + uint32_t size, + FAR imgsensor_value_t *val) +{ + int ret = -EINVAL; + isx019_reginfo_t reg; + convert_t cvrt; + getvalue_t get; + int32_t val32; + + DEBUGASSERT(val); + + cvrt = get_reginfo(id, false, ®); + if (cvrt) + { + ret = isx019_i2c_read + (reg.category, reg.offset, (FAR uint8_t *)&val32, reg.size); + val->value32 = cvrt(val32); + } + else + { + get = get_value_func(id); + if (get) + { + ret = get(val); + } + } + + return ret; +} + +static int validate_range(int32_t val, + FAR imgsensor_capability_range_t *range) +{ + int ret = OK; + + if (!VALIDATE_RANGE(val, range->minimum, range->maximum, range->step)) + { + ret = -EINVAL; + } + + return ret; +} + +static int validate_discrete(int32_t val, + FAR imgsensor_capability_discrete_t *disc) +{ + int ret = -EINVAL; + int i; + + for (i = 0; i < disc->nr_values; i++) + { + if (val == disc->values[i]) + { + ret = OK; + break; + } + } + + return ret; +} + +static int validate_elems_u8(FAR uint8_t *val, uint32_t sz, + FAR imgsensor_capability_elems_t *elems) +{ + int ret = OK; + int i; + + if (sz != elems->nr_elems) + { + return -EINVAL; + } + + for (i = 0; i < elems->nr_elems; i++) + { + if (!VALIDATE_RANGE + (val[i], elems->minimum, elems->maximum, elems->step)) + { + ret = -EINVAL; + break; + } + } + + return ret; +} + +static int validate_elems_u16 (FAR uint16_t *val, uint32_t sz, + FAR imgsensor_capability_elems_t *elems) +{ + int ret = OK; + int i; + + if (sz != elems->nr_elems * sizeof(uint16_t)) + { + return -EINVAL; + } + + for (i = 0; i < elems->nr_elems; i++) + { + if (!VALIDATE_RANGE + (val[i], elems->minimum, elems->maximum, elems->step)) + { + ret = -EINVAL; + break; + } + } + + return ret; +} + +static int validate_elems_u32 (FAR uint32_t *val, uint32_t sz, + FAR imgsensor_capability_elems_t *elems) +{ + int ret = OK; + int i; + + if (sz != elems->nr_elems * sizeof(uint32_t)) + { + return -EINVAL; + } + + for (i = 0; i < elems->nr_elems; i++) + { + if (!VALIDATE_RANGE + (val[i], elems->minimum, elems->maximum, elems->step)) + { + ret = -EINVAL; + break; + } + } + + return ret; +} + +static int validate_value(uint32_t id, + uint32_t size, + imgsensor_value_t val) +{ + int ret; + imgsensor_supported_value_t sup; + + ret = isx019_get_supported_value(id, &sup); + if (ret != OK) + { + return ret; + } + + switch (sup.type) + { + case IMGSENSOR_CTRL_TYPE_INTEGER_MENU: + ret = validate_discrete(val.value32, &sup.u.discrete); + break; + + case IMGSENSOR_CTRL_TYPE_U8: + ret = validate_elems_u8(val.p_u8, size, &sup.u.elems); + break; + + case IMGSENSOR_CTRL_TYPE_U16: + ret = validate_elems_u16(val.p_u16, size, &sup.u.elems); + break; + + case IMGSENSOR_CTRL_TYPE_U32: + ret = validate_elems_u32(val.p_u32, size, &sup.u.elems); + break; + + default: + ret = validate_range(val.value32, &sup.u.range); + break; + } + + return ret; +} + +static int isx019_set_value(uint32_t id, + uint32_t size, + imgsensor_value_t val) +{ + int ret = -EINVAL; + isx019_reginfo_t reg; + convert_t cvrt; + setvalue_t set; + int32_t val32; + + ret = validate_value(id, size, val); + if (ret != OK) + { + return ret; + } + + cvrt = get_reginfo(id, true, ®); + if (cvrt) + { + val32 = cvrt(val.value32); + ret = isx019_i2c_write + (reg.category, reg.offset, (FAR uint8_t *)&val32, reg.size); + } + else + { + set = set_value_func(id); + if (set) + { + ret = set(val); + } + } + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int isx019_initialize(void) +{ + imgsensor_register(&g_isx019_ops); + nxsem_init(&g_isx019_private.i2c_lock, 0, 1); + nxsem_init(&g_isx019_private.fpga_lock, 0, 1); + return OK; +} + +int isx019_uninitialize(void) +{ + nxsem_destroy(&g_isx019_private.i2c_lock); + nxsem_destroy(&g_isx019_private.fpga_lock); + return OK; +} + +#ifdef CONFIG_VIDEO_ISX019_REGDEBUG +int isx019_read_register(uint8_t cat, + uint16_t addr, + FAR uint8_t *buf, + uint8_t size) +{ + int ret; + + if (cat == 0xff) + { + ret = fpga_i2c_read((uint8_t)addr, buf, size); + } + else + { + ret = isx019_i2c_read(cat, addr, buf, size); + } + + return ret; +} +#endif diff --git a/drivers/video/isx019_range.h b/drivers/video/isx019_range.h new file mode 100644 index 0000000000..4a3d6d143f --- /dev/null +++ b/drivers/video/isx019_range.h @@ -0,0 +1,133 @@ +/**************************************************************************** + * drivers/video/isx019_range.h + * + * 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 __DRIVERS_VIDEO_ISX019_RANGE_H +#define __DRIVERS_VIDEO_ISX019_RANGE_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MIN_BRIGHTNESS (-128) +#define MAX_BRIGHTNESS (127) +#define STEP_BRIGHTNESS (1) + +#define MIN_CONTRAST (0) +#define MAX_CONTRAST (255) +#define STEP_CONTRAST (1) + +#define MIN_SATURATION (0) +#define MAX_SATURATION (255) +#define STEP_SATURATION (1) + +#define MIN_HUE (-128) +#define MAX_HUE (127) +#define STEP_HUE (1) + +#define MIN_GAMMA (0) +#define MAX_GAMMA (INT_MAX) +#define STEP_GAMMA (1) + +#define MIN_AWB (0) +#define MAX_AWB (1) +#define STEP_AWB (1) + +#define MIN_EXPOSURE (-6) +#define MAX_EXPOSURE (6) +#define STEP_EXPOSURE (1) + +#define MIN_HFLIP (0) +#define MAX_HFLIP (1) +#define STEP_HFLIP (1) + +#define MIN_VFLIP (0) +#define MAX_VFLIP (1) +#define STEP_VFLIP (1) + +#define MIN_SHARPNESS (0) +#define MAX_SHARPNESS (255) +#define STEP_SHARPNESS (1) + +#define MIN_AE (0) +#define MAX_AE (1) +#define STEP_AE (1) + +#define MIN_EXPOSURETIME (1) +#define MAX_EXPOSURETIME (102000) +#define STEP_EXPOSURETIME (1) + +#define MIN_WBMODE (0) +#define MAX_WBMODE (7) +#define STEP_WBMODE (1) + +#define MIN_HDR (0) +#define MAX_HDR (2) +#define STEP_HDR (1) + +#define MIN_METER (0) +#define MAX_METER (3) +#define STEP_METER (1) + +#define MIN_PAN (-32) +#define MAX_PAN (32) +#define STEP_PAN (1) + +#define MIN_TILT (-32) +#define MAX_TILT (32) +#define STEP_TILT (1) + +#define MIN_ISO (1) +#define MAX_ISO (INT_MAX) +#define STEP_ISO (1) + +#define MIN_AUTOISO (0) +#define MAX_AUTOISO (1) +#define STEP_AUTOISO (1) + +#define MIN_3ALOCK (0) +#define MAX_3ALOCK (3) +#define STEP_3ALOCK (1) + +#define NRELEM_3APARAM (9) +#define MIN_3APARAM (0) +#define MAX_3APARAM (255) +#define STEP_3APARAM (1) + +#define MIN_3ASTATUS (0) +#define MAX_3ASTATUS (3) +#define STEP_3ASTATUS (1) + +#define MIN_JPGQUALITY (1) +#define MAX_JPGQUALITY (100) +#define STEP_JPGQUALITY (1) + +#define NRELEM_CLIP IMGSENSOR_CLIP_NELEM +#define MIN_CLIP (0) +#define MAX_CLIP (1280) +#define STEP_CLIP (8) + +#endif /* __DRIVERS_VIDEO_ISX019_RANGE_H */ diff --git a/drivers/video/isx019_reg.h b/drivers/video/isx019_reg.h new file mode 100644 index 0000000000..71aead7cbd --- /dev/null +++ b/drivers/video/isx019_reg.h @@ -0,0 +1,408 @@ +/**************************************************************************** + * drivers/video/isx019_reg.h + * + * 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 __DRIVERS_VIDEO_ISX019_REG_H +#define __DRIVERS_VIDEO_ISX019_REG_H + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* FPGA I2C setting */ + +#define FPGA_I2C_SLVADDR (0x1b) +#define FPGA_I2C_SLVADDR_LEN (7) +#define FPGA_I2C_REGSIZE_MAX (1) +#define FPGA_I2C_REGADDR_LEN (1) +#define FPGA_I2C_FREQUENCY (100000) /* Standard mode : 100kHz */ + +/* FPGA I2C offset */ + +#define FPGA_I2C_OFFSET_ADDR (0) +#define FPGA_I2C_OFFSET_WRITEDATA (1) +#define FPGA_I2C_OFFSET_READDATA (0) + +/* FPGA register address */ + +#define FPGA_RESET (0x00) +#define FPGA_DATA_OUTPUT (0x01) +#define FPGA_FORMAT_AND_SCALE (0x02) +#define FPGA_FPS_AND_THUMBNAIL (0x03) +#define FPGA_CLIP_SIZE (0x04) +#define FPGA_CLIP_TOP (0x05) +#define FPGA_CLIP_LEFT (0x06) +#define FPGA_DQT_ADDRESS (0x07) +#define FPGA_DQT_SELECT (0x08) +#define FPGA_DQT_DATA (0x09) +#define FPGA_DQT_CALC_ADDRESS (0x0A) +#define FPGA_DQT_CALC_SELECT (0x0B) +#define FPGA_DQT_CALC_DATA (0x0C) +#define FPGA_ACTIVATE (0x0D) +#define FPGA_PIN_OUTPUT_SELECT (0x20) +#define FPGA_PIN_STATUS (0x21) +#define FPGA_VERSION (0x30) + +/* value for setting to FPGA register */ + +#define FPGA_RESET_ENABLE (0x00) +#define FPGA_RESET_RELEASE (0x01) + +#define FPGA_DATA_OUTPUT_START (0x01) +#define FPGA_DATA_OUTPUT_STOP (0x00) + +#define FPGA_FORMAT_RGB (0x00) +#define FPGA_FORMAT_YUV (0x01) +#define FPGA_FORMAT_JPEG (0x02) +#define FPGA_FORMAT_THUMBNAIL (0x03) + +#define FPGA_SCALE_1280_960 (0<<4) +#define FPGA_SCALE_640_480 (1<<4) +#define FPGA_SCALE_320_240 (2<<4) +#define FPGA_SCALE_160_120 (3<<4) + +#define FPGA_FPS_BITS (0x03) +#define FPGA_FPS_1_1 (0x00) +#define FPGA_FPS_1_2 (0x01) +#define FPGA_FPS_1_3 (0x02) +#define FPGA_FPS_1_4 (0x03) + +#define FPGA_THUMBNAIL_SCALE_1_1 (0<<4) +#define FPGA_THUMBNAIL_SCALE_1_2 (1<<4) +#define FPGA_THUMBNAIL_SCALE_1_4 (2<<4) +#define FPGA_THUMBNAIL_SCALE_1_8 (3<<4) + +#define FPGA_CLIP_NON (0x00) +#define FPGA_CLIP_1280_720 (0x01) +#define FPGA_CLIP_640_360 (0x02) +#define FPGA_CLIP_UNIT (8) + +#define FPGA_DQT_READ (0<<6) +#define FPGA_DQT_WRITE (1<<6) + +#define FPGA_DQT_CURRENT (0<<7) +#define FPGA_DQT_BUFFER (1<<7) + +#define FPGA_DQT_LUMA (0x00) +#define FPGA_DQT_CHROMA (0x01) + +#define FPGA_ACTIVATE_REQUEST (0x01) + +#define FPGA_PIN_OUTPUT_EXBUSY (0x00) +#define FPGA_PIN_OUTPUT_ERROR (0x01) + +#define FPGA_PIN_STATUS_EXBUSY (1<<0) +#define FPGA_PIN_STATUS_ERROR (1<<4) + +/* ISX019 I2C setting */ + +#define ISX019_I2C_SLVADDR (0x1a) +#define ISX019_I2C_SLVADDR_LEN (7) +#define ISX019_I2C_REGSIZE_MAX (4) +#define ISX019_I2C_REGADDR_LEN (4) +#define ISX019_I2C_FREQUENCY (100000) /* Standard mode : 100kHz */ + +/* Offset of ISX019 I2C command format */ + +#define ISX019_I2C_OFFSET_TOTALLEN (0) +#define ISX019_I2C_OFFSET_CMDNUM (1) +#define ISX019_I2C_OFFSET_CMDLEN (2) +#define ISX019_I2C_OFFSET_CMD (3) +#define ISX019_I2C_OFFSET_CATEGORY (4) +#define ISX019_I2C_OFFSET_ADDRESS_H (5) +#define ISX019_I2C_OFFSET_ADDRESS_L (6) +#define ISX019_I2C_OFFSET_WRITEDATA (7) +#define ISX019_I2C_OFFSET_READSIZE (7) +#define ISX019_I2C_OFFSET_FLASHCODE1 (4) +#define ISX019_I2C_OFFSET_FLASHCODE2 (5) +#define ISX019_I2C_OFFSET_FLASHCODE3 (6) +#define ISX019_I2C_OFFSET_FLASHCODE4 (7) +#define ISX019_I2C_OFFSET_STS (3) +#define ISX019_I2C_OFFSET_READDATA (4) + +/* Command code of ISX019 I2C command format */ + +#define ISX019_I2C_CMD_FLASHLOCK (0x00) +#define ISX019_I2C_CMD_READ (0x01) +#define ISX019_I2C_CMD_WRITE (0x02) +#define ISX019_I2C_CMD_FLASHWRITE (0x05) + +/* Command length of ISX019 I2C command format */ + +#define ISX019_I2C_TOTALLEN(s) (3 + (s)) + +#define ISX019_I2C_WRREQ_LEN(s) (5 + (s)) +#define ISX019_I2C_WRRES_LEN (2) +#define ISX019_I2C_WRREQ_TOTALLEN(s) ISX019_I2C_TOTALLEN(ISX019_I2C_WRREQ_LEN(s)) +#define ISX019_I2C_WRRES_TOTALLEN ISX019_I2C_TOTALLEN(ISX019_I2C_WRRES_LEN) + +#define ISX019_I2C_RDREQ_LEN (6) +#define ISX019_I2C_RDRES_LEN(s) (2 + (s)) +#define ISX019_I2C_RDREQ_TOTALLEN ISX019_I2C_TOTALLEN(ISX019_I2C_RDREQ_LEN) +#define ISX019_I2C_RDRES_TOTALLEN(s) ISX019_I2C_TOTALLEN(ISX019_I2C_RDRES_LEN(s)) + +#define ISX019_I2C_FLOCKREQ_LEN (6) +#define ISX019_I2C_FLOCKRES_LEN (2) +#define ISX019_I2C_FLOCKREQ_TOTALLEN ISX019_I2C_TOTALLEN(ISX019_I2C_FLOCKREQ_LEN) +#define ISX019_I2C_FLOCKRES_TOTALLEN ISX019_I2C_TOTALLEN(ISX019_I2C_FLOCKRES_LEN) + +#define ISX019_I2C_FWRREQ_LEN (2) +#define ISX019_I2C_FWRRES_LEN (2) +#define ISX019_I2C_FWRREQ_TOTALLEN ISX019_I2C_TOTALLEN(ISX019_I2C_FWRREQ_LEN) +#define ISX019_I2C_FWRRES_TOTALLEN ISX019_I2C_TOTALLEN(ISX019_I2C_FWRRES_LEN) + +/* Constant value of codes for flash lock */ + +#define ISX019_I2C_FLASHCODE1 (0x57) +#define ISX019_I2C_FLASHCODE2 (0x52) +#define ISX019_I2C_FLASHLOCK1 (0x44) +#define ISX019_I2C_FLASHLOCK2 (0x53) +#define ISX019_I2C_FLASHUNLOCK1 (0x45) +#define ISX019_I2C_FLASHUNLOCK2 (0x4e) + +/* Status code of I2C command format */ + +#define ISX019_I2C_STS_OK (0x01) +#define ISX019_I2C_STS_ERR_CMD (0xf1) +#define ISX019_I2C_STS_ERR_CAT (0xf2) +#define ISX019_I2C_STS_ERR_ADDR (0xf3) +#define ISX019_I2C_STS_ERR_ACCESS (0xf4) +#define ISX019_I2C_STS_ERR_BYTE (0xf7) +#define ISX019_I2C_STS_ERR_FLASH (0xfa) + +/* Category of ISX019 register */ + +#define CAT_VERSION (0x00) +#define CAT_CONFIG (0x01) +#define CAT_SYSCOM (0x06) +#define CAT_OTP (0x08) +#define CAT_AUTOCTRL (0x0c) +#define CAT_SMCFG (0x0f) +#define CAT_CATAE (0x13) +#define CAT_AEDGRM (0x14) +#define CAT_AEWD (0x15) +#define CAT_AECOM (0x18) +#define CAT_AESOUT (0x19) +#define CAT_CATAWB (0x21) +#define CAT_AWB_USERTYPE (0x26) +#define CAT_AUTOCOM (0x2c) +#define CAT_AWBSOUT (0x31) +#define CAT_PICTTUNE (0x34) +#define CAT_PICTGAMMA (0x41) + +/* Offset of ISX019 register */ + +/* For CAT_VERSION */ + +#define ROM_VERSION (0x0000) + +/* For CAT_CONFIG */ + +#define MODE_SENSSEL (0x0000) +#define MODE_POSTSEL (0x0001) +#define MODE_SENSPOST_SEL (0x0002) +#define MODE_IOSEL (0x0003) +#define REVERSE (0x0008) + +/* For CAT_SYSCOM */ + +#define DEVSTS (0x0001) + +/* For CAT_OTP */ + +#define CHIP_ID (0x0000) +#define CHIP_ID_LEN (11) + +/* For CAT_AUTOCTRL */ + +#define AEWEIGHTMODE (0x0000) + +/* For CAT_CATAE */ + +#define AEMODE (0x0000) +#define SHT_PRIMODE (0x0008) +#define GAIN_PRIMODE (0x000c) + +/* For CAT_AEDGRM */ + +#define SHTCTRLVAL1 (0x0008) +#define SHTCTRLVAL2 (0x0010) +#define SHTCTRLVAL3 (0x0018) +#define SHTCTRLVAL4 (0x0020) +#define SHTCTRLVAL5 (0x0028) +#define MAXFRMEXP (0x007b) +#define EVSEL (0x009c) + +/* For CAT_AEWD */ + +#define AEWDMODE (0x0000) +#define AEWEIGHTMODE_WD (0x0001) + +/* For CAT_AECOM */ + +#define GAIN_LEVEL (0x0036) + +/* For CAT_AESOUT */ + +#define SHT_TIME (0x01cc) +#define V_TIME (0x01d0) + +/* For CAT_CATAWB */ + +#define AWBMODE (0x0000) +#define AWBUSER_NO (0x000a) + +/* For CAT_AWB_USERTYPE */ + +#define USER0_R (0x0000) +#define USER0_B (0x0002) +#define USER1_R (0x000c) +#define USER1_B (0x000e) +#define USER2_R (0x0018) +#define USER2_B (0x001a) +#define USER3_R (0x0024) +#define USER3_B (0x0026) +#define USER4_R (0x0030) +#define USER4_B (0x0032) + +/* For CAT_AUTOCOM */ + +#define CONT_R (0x0006) +#define CONT_B (0x0008) + +/* For CAT_AWBSOUT */ + +#define AWBSTS (0x0000) + +/* For CAT_PICTTUNE */ + +#define UISHARPNESS (0x0000) +#define UICONTRAST (0x0001) +#define UIBRIGHTNESS (0x0002) +#define UIHUE (0x0004) +#define UISATURATION (0x0005) + +/* For CAT_PICTGAMMA */ + +#define GAM_KNOT_C0 (0x0000) +#define GAM_KNOT_C8 (0x0010) +#define GAM_KNOT_C9 (0x0012) +#define GAM_KNOT_C10 (0x0014) +#define GAM_KNOT_C11 (0x0016) +#define NR_GAM_KNOT_LOWINPUT (9) +#define NR_GAM_KNOT_HIGHINPUT (16) + +#define GAM_LOWINPUT_INTERVAL (double)(32.0 / 4096.0) +#define GAM_HIGHINPUT_INTERVAL (double)(256.0 / 4096.0) +#define GAM_OUTPUT_SCALE (255) + +/* Definition for ISX019 register setting value */ + +/* For drive mode */ + +#define NOT_STREAM_SENS (0) +#define NOT_STREAM_POST (0) +#define NOT_STREAM_SENSPOST (0) +#define NOT_STREAM_IO (0) + +#define NORM_30FPS_SENS (2) +#define NORM_30FPS_POST (2) +#define NORM_30FPS_SENSPOST (2) +#define NORM_30FPS_IO (2) + +#define DOL2_30FPS_SENS (6) +#define DOL2_30FPS_POST (2) +#define DOL2_30FPS_SENSPOST (24) +#define DOL2_30FPS_IO (2) + +#define DOL3_30FPS_SENS (44) +#define DOL3_30FPS_POST (125) +#define DOL3_30FPS_SENSPOST (160) +#define DOL3_30FPS_IO (2) + +#define NORM_25FPS_SENS (3) +#define NORM_25FPS_POST (8) +#define NORM_25FPS_SENSPOST (8) +#define NORM_25FPS_IO (2) + +#define DOL2_25FPS_SENS (7) +#define DOL2_25FPS_POST (8) +#define DOL2_25FPS_SENSPOST (30) +#define DOL2_25FPS_IO (2) + +#define DOL3_25FPS_SENS (45) +#define DOL3_25FPS_POST (131) +#define DOL3_25FPS_SENSPOST (166) +#define DOL3_25FPS_IO (2) + +/* For REVERSE register(H_REVERSE and V_REVERSE) */ + +#define H_REVERSE (0x01) +#define V_REVERSE (0x02) + +/* For AEWEIGHTMODE */ + +#define AEWEIGHT_AVERAGE (0x00) +#define AEWEIGHT_CENTER (0x01) +#define AEWEIGHT_SPOT (0x02) +#define AEWEIGHT_MATRIX (0x03) + +/* For AEWEIGHTMODE(HDR) */ + +#define AEWEIGHTHDR_AVERAGE (0x00) +#define AEWEIGHTHDR_CENTER (0x05) +#define AEWEIGHTHDR_SPOT (0x0a) +#define AEWEIGHTHDR_MATRIX (0x0f) + +/* For AWBMODE */ + +#define AWBMODE_AUTO (0x00) +#define AWBMODE_HOLD (0x02) +#define AWBMODE_MANUAL (0x04) + +/* For AEMODE */ + +#define AEMODE_AUTO (0x00) +#define AEMODE_HOLD (0x01) + +/* For AEWDMODE */ + +#define AEWDMODE_AUTO (0x00) +#define AEWDMODE_HDR (0x01) +#define AEWDMODE_NORMAL (0x02) + +/* For AWBSTS */ + +#define AWBSTS_STABLE (0x02) +#define AWBSTS_AEWAIT (0x06) + +/* For USERX_R, USERX_B */ + +#define RED_INCANDESCENT (0x115f) +#define BLUE_INCANDESCENT (0x0e40) +#define RED_FLUORESCENT (0x0daf) +#define BLUE_FLUORESCENT (0x1250) +#define RED_DAYLIGHT (0x0b81) +#define BLUE_DAYLIGHT (0x1832) +#define RED_CLOUDY (0x0a4f) +#define BLUE_CLOUDY (0x1bc5) +#define RED_SHADE (0x0a4f) +#define BLUE_SHADE (0x1bc5) + +#endif /* __DRIVERS_VIDEO_ISX019_REG_H */ diff --git a/include/nuttx/video/isx019.h b/include/nuttx/video/isx019.h new file mode 100644 index 0000000000..4116475a12 --- /dev/null +++ b/include/nuttx/video/isx019.h @@ -0,0 +1,55 @@ +/**************************************************************************** + * include/nuttx/video/isx019.h + * + * 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_ISX019_H +#define __INCLUDE_NUTTX_VIDEO_ISX019_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +int isx019_initialize(void); +int isx019_uninitialize(void); +#ifdef CONFIG_VIDEO_ISX019_REGDEBUG +int isx019_read_register(uint8_t cat, + uint16_t addr, + FAR uint8_t *buf, + uint8_t size); +#endif +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __INCLUDE_NUTTX_VIDEO_ISX019_H */