/**************************************************************************** * 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 (130 * USEC_PER_MSEC) /* unit : usec */ #define TRANSITION_TIME_TO_STREAMING (40 * USEC_PER_MSEC) /* unit : usec */ #define DELAY_TIME_JPEGDQT_SWAP (35 * 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) /* Definition for calculation of extended frame number */ #define VTIME_PER_FRAME (30518) #define INTERVAL_PER_FRAME (33333) /* ISX019 image sensor output frame size. */ #define ISX019_WIDTH (1280) #define ISX019_HEIGHT (960) /* The number of whole image splits for spot position decision. */ #define ISX019_SPOT_POSITION_SPLIT_NUM_X (9) #define ISX019_SPOT_POSITION_SPLIT_NUM_Y (7) /**************************************************************************** * 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 spot_pos; 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 { struct imgsensor_s sensor; mutex_t fpga_lock; mutex_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)(FAR isx019_dev_t *priv, imgsensor_value_t value); typedef CODE int (*getvalue_t)(FAR isx019_dev_t *priv, 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(FAR struct imgsensor_s *sensor); static int isx019_init(FAR struct imgsensor_s *sensor); static int isx019_uninit(FAR struct imgsensor_s *sensor); static FAR const char * isx019_get_driver_name(FAR struct imgsensor_s *sensor); static int isx019_validate_frame_setting(FAR struct imgsensor_s *sensor, imgsensor_stream_type_t type, uint8_t nr_datafmt, FAR imgsensor_format_t *datafmts, FAR imgsensor_interval_t *interval); static int isx019_start_capture(FAR struct imgsensor_s *sensor, imgsensor_stream_type_t type, uint8_t nr_datafmt, FAR imgsensor_format_t *datafmts, FAR imgsensor_interval_t *interval); static int isx019_stop_capture(FAR struct imgsensor_s *sensor, imgsensor_stream_type_t type); static int isx019_get_frame_interval(FAR struct imgsensor_s *sensor, imgsensor_stream_type_t type, FAR imgsensor_interval_t *interval); static int isx019_get_supported_value(FAR struct imgsensor_s *sensor, uint32_t id, FAR imgsensor_supported_value_t *value); static int isx019_get_value(FAR struct imgsensor_s *sensor, uint32_t id, uint32_t size, FAR imgsensor_value_t *value); static int isx019_set_value(FAR struct imgsensor_s *sensor, uint32_t id, uint32_t size, imgsensor_value_t value); static int initialize_jpg_quality(FAR isx019_dev_t *priv); static void initialize_wbmode(FAR isx019_dev_t *priv); static int send_read_cmd(FAR isx019_dev_t *priv, FAR const struct i2c_config_s *config, uint8_t cat, uint16_t addr, uint8_t size); /**************************************************************************** * Private Data ****************************************************************************/ static const 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_frame_interval, isx019_get_supported_value, isx019_get_value, isx019_set_value, }; static isx019_dev_t g_isx019_private = { { &g_isx019_ops }, NXMUTEX_INITIALIZER, NXMUTEX_INITIALIZER, }; static const isx019_fpga_jpg_quality_t g_isx019_jpg_quality[] = { { 10, { 21, 16, 16, 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, 16, 14, 21, 16, 16, 21, 32, 21, 16, 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, 4, 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, { 16, 11, 11, 11, 12, 11, 16, 12, 12, 16, 21, 14, 13, 14, 21, 26, 21, 16, 16, 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, 6, 6, 4, 3, 133, 2, 2, 6, 139, 139, 137, 3, 3, 3, 2, 6, 139, 5, 4, 3, 133, 2, 131, 4, 137, 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, }, { 16, 14, 14, 16, 18, 16, 21, 18, 18, 21, 21, 16, 21, 16, 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, }, { 4, 137, 4, 3, 3, 133, 131, 131, 137, 135, 135, 4, 3, 133, 2, 131, 4, 135, 3, 3, 133, 2, 131, 131, 3, 4, 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, 16, 11, 10, 11, 16, 21, 14, 12, 12, 14, 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, 4, 3, 133, 3, 8, 7, 7, 6, 137, 135, 135, 3, 8, 7, 141, 139, 135, 3, 133, 2, 139, 6, 139, 3, 3, 133, 2, 131, 4, 137, 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, 16, 14, 14, 16, 21, 14, 14, 14, 21, 21, 16, 16, 16, 16, 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, 6, 5, 4, 3, 3, 133, 2, 6, 137, 137, 137, 4, 3, 133, 2, 5, 137, 137, 4, 3, 3, 2, 131, 4, 137, 4, 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, 16, 13, 13, 14, 13, 13, 16, 16, 12, 14, 13, 13, 14, 12, 16, 14, 18, 18, 21, 18, 18, 14, 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, 4, 11, 11, 11, 8, 141, 5, 139, 137, 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, 4, 137, 133, 2, 131, 131, 131, 131, }, { 9, 8, 8, 9, 10, 9, 11, 9, 9, 11, 14, 11, 13, 11, 14, 16, 14, 14, 14, 14, 16, 18, 13, 13, 14, 13, 13, 18, 26, 16, 14, 14, 14, 14, 16, 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, 6, 137, 4, 135, 133, 8, 141, 7, 6, 137, 5, 4, 3, 7, 7, 5, 137, 5, 137, 3, 133, 6, 6, 137, 137, 137, 3, 133, 2, 137, 137, 5, 137, 3, 133, 2, 131, 4, 5, 137, 3, 133, 2, 131, 131, 135, 4, 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, 16, 12, 12, 12, 12, 12, 12, 16, 12, 14, 14, 16, 14, 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, 6, 139, 4, 16, 13, 13, 11, 8, 141, 139, 139, 16, 13, 13, 11, 141, 139, 137, 135, 11, 11, 11, 6, 139, 137, 135, 133, 7, 8, 141, 139, 4, 3, 133, 2, 6, 141, 139, 137, 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, 16, 16, 26, 26, 21, 16, 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, 4, 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, 16, 16, 16, 16, 16, 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, 4, 11, 13, 11, 7, 139, 5, 4, 3, 9, 11, 7, 139, 5, 4, 3, 3, 8, 8, 139, 139, 4, 3, 3, 3, 141, 141, 139, 4, 3, 3, 3, 3, }, { 4, 5, 5, 8, 7, 8, 14, 10, 10, 14, 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, 137, 3, 3, 3, 3, 13, 9, 141, 137, 3, 3, 3, 3, 8, 141, 137, 3, 3, 3, 3, 3, 137, 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, 14, 14, 14, 14, 14, 14, 14, 12, 12, 12, 12, 12, 14, 14, 12, 12, 12, 12, 12, 12, 14, 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, 137, 137, 137, 21, 16, 11, 6, 137, 139, 139, 139, 13, 11, 7, 137, 139, 139, 139, 139, 7, 6, 137, 139, 139, 139, 139, 139, 5, 137, 139, 139, 139, 139, 139, 139, 137, 139, 139, 139, 139, 139, 139, 139, 137, 139, 139, 139, 139, 139, 139, 139, 137, 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)) static const int32_t g_isx019_colorfx[] = { IMGSENSOR_COLORFX_NONE, IMGSENSOR_COLORFX_BW, IMGSENSOR_COLORFX_VIVID, }; #define NR_COLORFX (sizeof(g_isx019_colorfx) / sizeof(int32_t)) static const 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 int fpga_i2c_write(FAR isx019_dev_t *priv, uint8_t addr, FAR const void *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; nxmutex_lock(&priv->i2c_lock); /* ISX019 requires that send read command to ISX019 before FPGA access. */ send_read_cmd(priv, &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(priv->i2c, &config, buf, size + FPGA_I2C_REGADDR_LEN); nxmutex_unlock(&priv->i2c_lock); return ret; } static int fpga_i2c_read(FAR isx019_dev_t *priv, uint8_t addr, FAR void *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; nxmutex_lock(&priv->i2c_lock); /* ISX019 requires that send read command to ISX019 before FPGA access. */ send_read_cmd(priv, &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(priv->i2c, &config, &addr, FPGA_I2C_REGADDR_LEN); if (ret >= 0) { ret = i2c_read(priv->i2c, &config, data, size); } nxmutex_unlock(&priv->i2c_lock); return ret; } static void fpga_activate_setting(FAR isx019_dev_t *priv) { uint8_t regval = FPGA_ACTIVATE_REQUEST; fpga_i2c_write(priv, FPGA_ACTIVATE, ®val, 1); while (1) { fpga_i2c_read(priv, FPGA_ACTIVATE, ®val, 1); if (regval == 0) { break; } } } static uint8_t calc_isx019_chksum(FAR const 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 const 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 isx019_dev_t *priv, FAR const struct i2c_config_s *config) { int ret; uint8_t buf[ISX019_I2C_WRRES_TOTALLEN]; ret = i2c_read(priv->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 isx019_dev_t *priv, FAR const struct i2c_config_s *config, FAR void *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(priv->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 isx019_dev_t *priv, FAR const struct i2c_config_s *config, uint8_t cat, uint16_t addr, FAR const void *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(priv->i2c, config, buf, len); } static int isx019_i2c_write(FAR isx019_dev_t *priv, uint8_t cat, uint16_t addr, FAR const void *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; nxmutex_lock(&priv->i2c_lock); ret = send_write_cmd(priv, &config, cat, addr, data, size); if (ret == OK) { ret = recv_write_response(priv, &config); } nxmutex_unlock(&priv->i2c_lock); return ret; } static int send_read_cmd(FAR isx019_dev_t *priv, FAR const 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(priv->i2c, config, buf, len); return ret; } static int isx019_i2c_read(FAR isx019_dev_t *priv, uint8_t cat, uint16_t addr, FAR void *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; nxmutex_lock(&priv->i2c_lock); ret = send_read_cmd(priv, &config, cat, addr, size); if (ret == OK) { ret = recv_read_response(priv, &config, data, size); } nxmutex_unlock(&priv->i2c_lock); return ret; } static void fpga_init(FAR isx019_dev_t *priv) { uint8_t regval; regval = FPGA_RESET_ENABLE; fpga_i2c_write(priv, FPGA_RESET, ®val, 1); regval = FPGA_DATA_OUTPUT_STOP; fpga_i2c_write(priv, FPGA_DATA_OUTPUT, ®val, 1); fpga_activate_setting(priv); regval = FPGA_RESET_RELEASE; fpga_i2c_write(priv, FPGA_RESET, ®val, 1); fpga_activate_setting(priv); } static int set_drive_mode(FAR isx019_dev_t *priv) { 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(priv, CAT_CONFIG, MODE_SENSSEL, &drv[INDEX_SENS], 1); isx019_i2c_write(priv, CAT_CONFIG, MODE_POSTSEL, &drv[INDEX_POST], 1); isx019_i2c_write(priv, 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, FAR isx019_dev_t *priv, CODE int (*trial_func)(FAR isx019_dev_t *)) { int ret; struct timespec start; struct timespec now; struct timespec delta; ret = clock_systime_timespec(&start); if (ret < 0) { return false; } while (1) { ret = trial_func(priv); if (ret >= 0) { return true; } else { ret = clock_systime_timespec(&now); if (ret < 0) { return false; } clock_timespec_subtract(&now, &start, &delta); if ((delta.tv_sec > sec) || ((delta.tv_sec == sec) && (delta.tv_nsec > (usec * NSEC_PER_USEC)))) { break; } } }; return false; } static int try_isx019_i2c(FAR isx019_dev_t *priv) { uint8_t buf; return isx019_i2c_read(priv, CAT_SYSCOM, DEVSTS, &buf, 1); } static int try_fpga_i2c(FAR isx019_dev_t *priv) { uint8_t buf; return fpga_i2c_read(priv, FPGA_VERSION, &buf, 1); } static void power_on(FAR isx019_dev_t *priv) { priv->i2c = board_isx019_initialize(); board_isx019_power_on(); board_isx019_release_reset(); } static void power_off(FAR isx019_dev_t *priv) { board_isx019_set_reset(); board_isx019_power_off(); board_isx019_uninitialize(priv->i2c); } static bool isx019_is_available(FAR struct imgsensor_s *sensor) { FAR isx019_dev_t *priv = (FAR isx019_dev_t *)sensor; bool ret; power_on(priv); /* 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, priv, try_isx019_i2c)) { if (try_repeat(FPGA_ACCESSIBLE_WAIT_SEC, FPGA_ACCESSIBLE_WAIT_USEC, priv, try_fpga_i2c)) { ret = true; } } power_off(priv); return ret; } static int32_t get_value32(FAR isx019_dev_t *priv, uint32_t id) { imgsensor_value_t val; isx019_get_value(&priv->sensor, id, 0, &val); return val.value32; } static void store_default_value(FAR isx019_dev_t *priv) { FAR isx019_default_value_t *def = &priv->default_value; def->brightness = get_value32(priv, IMGSENSOR_ID_BRIGHTNESS); def->contrast = get_value32(priv, IMGSENSOR_ID_CONTRAST); def->saturation = get_value32(priv, IMGSENSOR_ID_SATURATION); def->hue = get_value32(priv, IMGSENSOR_ID_HUE); def->awb = get_value32(priv, IMGSENSOR_ID_AUTO_WHITE_BALANCE); def->gamma = get_value32(priv, IMGSENSOR_ID_GAMMA); def->ev = get_value32(priv, IMGSENSOR_ID_EXPOSURE); def->hflip_video = get_value32(priv, IMGSENSOR_ID_HFLIP_VIDEO); def->vflip_video = get_value32(priv, IMGSENSOR_ID_VFLIP_VIDEO); def->hflip_still = get_value32(priv, IMGSENSOR_ID_HFLIP_STILL); def->vflip_still = get_value32(priv, IMGSENSOR_ID_VFLIP_STILL); def->sharpness = get_value32(priv, IMGSENSOR_ID_SHARPNESS); def->ae = get_value32(priv, IMGSENSOR_ID_EXPOSURE_AUTO); def->exptime = get_value32(priv, IMGSENSOR_ID_EXPOSURE_ABSOLUTE); def->wbmode = get_value32(priv, IMGSENSOR_ID_AUTO_N_PRESET_WB); def->hdr = get_value32(priv, IMGSENSOR_ID_WIDE_DYNAMIC_RANGE); def->iso = get_value32(priv, IMGSENSOR_ID_ISO_SENSITIVITY); def->iso_auto = get_value32(priv, IMGSENSOR_ID_ISO_SENSITIVITY_AUTO); def->meter = get_value32(priv, IMGSENSOR_ID_EXPOSURE_METERING); def->spot_pos = get_value32(priv, IMGSENSOR_ID_SPOT_POSITION); def->threealock = get_value32(priv, IMGSENSOR_ID_3A_LOCK); def->threeastatus = get_value32(priv, IMGSENSOR_ID_3A_STATUS); def->jpgquality = get_value32(priv, IMGSENSOR_ID_JPEG_QUALITY); } static int isx019_init(FAR struct imgsensor_s *sensor) { FAR isx019_dev_t *priv = (FAR isx019_dev_t *)sensor; uint32_t clk; power_on(priv); set_drive_mode(priv); fpga_init(priv); initialize_wbmode(priv); initialize_jpg_quality(priv); store_default_value(priv); clk = board_isx019_get_master_clock(); priv->clock_ratio = (float)clk / ISX019_STANDARD_MASTER_CLOCK; return OK; } static int isx019_uninit(FAR struct imgsensor_s *sensor) { FAR isx019_dev_t *priv = (FAR isx019_dev_t *)sensor; power_off(priv); return OK; } static FAR const char *isx019_get_driver_name(FAR struct imgsensor_s *sensor) { #ifdef CONFIG_VIDEO_ISX019_NAME_WITH_VERSION FAR isx019_dev_t *priv = (FAR isx019_dev_t *)sensor; static char name[20]; uint8_t f_ver = 0; uint16_t is_ver = 0; isx019_i2c_read(priv, CAT_VERSION, ROM_VERSION, &is_ver, 2); fpga_i2c_read(priv, 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(FAR struct imgsensor_s *sensor, 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(FAR isx019_dev_t *priv, imgsensor_stream_type_t type) { uint8_t flip; flip = (type == IMGSENSOR_STREAM_TYPE_VIDEO) ? priv->flip_video : priv->flip_still; return isx019_i2c_write(priv, CAT_CONFIG, REVERSE, &flip, 1); } static int activate_clip(FAR isx019_dev_t *priv, 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) ? &priv->clip_video : &priv->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; case 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; default: /* Otherwise, clear clip setting. */ size = FPGA_CLIP_NON; top = 0; left = 0; break; } fpga_i2c_write(priv, FPGA_CLIP_SIZE, &size, 1); fpga_i2c_write(priv, FPGA_CLIP_TOP, &top, 1); fpga_i2c_write(priv, FPGA_CLIP_LEFT, &left, 1); return OK; } static int isx019_start_capture(FAR struct imgsensor_s *sensor, imgsensor_stream_type_t type, uint8_t nr_fmt, FAR imgsensor_format_t *fmt, FAR imgsensor_interval_t *interval) { FAR isx019_dev_t *priv = (FAR isx019_dev_t *)sensor; int ret; uint8_t regval = 0; ret = isx019_validate_frame_setting(sensor, type, nr_fmt, fmt, interval); if (ret != OK) { return ret; } ret = activate_flip(priv, type); if (ret != OK) { return ret; } nxmutex_lock(&priv->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; break; case 640: regval |= FPGA_SCALE_640_480; break; case 320: regval |= FPGA_SCALE_320_240; break; default: /* 160 */ regval |= FPGA_SCALE_160_120; break; } activate_clip(priv, type, fmt[IMGSENSOR_FMT_MAIN].width, fmt[IMGSENSOR_FMT_MAIN].height); fpga_i2c_write(priv, 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(priv, FPGA_FPS_AND_THUMBNAIL, ®val, 1); regval = FPGA_DATA_OUTPUT_START; fpga_i2c_write(priv, FPGA_DATA_OUTPUT, ®val, 1); fpga_activate_setting(priv); nxmutex_unlock(&priv->fpga_lock); priv->stream = type; return OK; } static int isx019_stop_capture(FAR struct imgsensor_s *sensor, imgsensor_stream_type_t type) { return OK; } static int calc_gcm(int a, int b) { int r; DEBUGASSERT((a != 0) && (b != 0)); while ((r = a % b) != 0) { a = b; b = r; } return b; } static int isx019_get_frame_interval(FAR struct imgsensor_s *sensor, imgsensor_stream_type_t type, FAR imgsensor_interval_t *interval) { FAR isx019_dev_t *priv = (FAR isx019_dev_t *)sensor; uint32_t vtime = VTIME_PER_FRAME; uint32_t frame = 1; uint8_t fps = FPGA_FPS_1_1; int decimation = 1; int gcm; if (interval == NULL) { return -EINVAL; } /* ISX019's base frame interval = 1/30. */ interval->denominator = 30; interval->numerator = 1; /* ISX019 has the frame extension feature, which automatically * exposes longer than one frame in dark environment. * The number of extended frame is calculated from V_TIME register, * which has the value * VTIME_PER_FRAME + INTERVAL_PER_FRAME * (frame number - 1) */ isx019_i2c_read(priv, CAT_AESOUT, V_TIME, &vtime, 4); frame = 1 + (vtime - VTIME_PER_FRAME) / INTERVAL_PER_FRAME; interval->numerator *= frame; /* Also, consider frame decimation by FPGA. * decimation amount is gotten from FPGA register. */ fpga_i2c_read(priv, FPGA_FPS_AND_THUMBNAIL, &fps, 1); switch (fps & FPGA_FPS_BITS) { case FPGA_FPS_1_1: decimation = 1; break; case FPGA_FPS_1_2: decimation = 2; break; case FPGA_FPS_1_3: decimation = 3; break; default: /* FPGA_FPS_1_4 */ decimation = 4; break; } interval->numerator *= decimation; /* Reduce the fraction. */ gcm = calc_gcm(30, frame * decimation); interval->denominator /= gcm; interval->numerator /= gcm; return OK; } static int isx019_get_supported_value(FAR struct imgsensor_s *sensor, uint32_t id, FAR imgsensor_supported_value_t *val) { FAR isx019_dev_t *priv = (FAR isx019_dev_t *)sensor; FAR struct isx019_default_value_s *def = &priv->default_value; int ret = OK; 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_SPOT_POSITION: val->type = IMGSENSOR_CTRL_TYPE_INTEGER; SET_RANGE(val->u.range, MIN_SPOTPOS, MAX_SPOTPOS, STEP_SPOTPOS, def->spot_pos); 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_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_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(FAR isx019_dev_t *priv, imgsensor_value_t val) { set_flip(&priv->flip_video, H_REVERSE, val.value32); if (priv->stream == IMGSENSOR_STREAM_TYPE_VIDEO) { activate_flip(priv, IMGSENSOR_STREAM_TYPE_VIDEO); } return OK; } static int set_vflip_video(FAR isx019_dev_t *priv, imgsensor_value_t val) { set_flip(&priv->flip_video, V_REVERSE, val.value32); if (priv->stream == IMGSENSOR_STREAM_TYPE_VIDEO) { activate_flip(priv, IMGSENSOR_STREAM_TYPE_VIDEO); } return OK; } static int set_hflip_still(FAR isx019_dev_t *priv, imgsensor_value_t val) { set_flip(&priv->flip_still, H_REVERSE, val.value32); if (priv->stream == IMGSENSOR_STREAM_TYPE_STILL) { activate_flip(priv, IMGSENSOR_STREAM_TYPE_STILL); } return OK; } static int set_vflip_still(FAR isx019_dev_t *priv, imgsensor_value_t val) { set_flip(&priv->flip_still, V_REVERSE, val.value32); if (priv->stream == IMGSENSOR_STREAM_TYPE_STILL) { activate_flip(priv, IMGSENSOR_STREAM_TYPE_STILL); } return OK; } static int set_colorfx(FAR isx019_dev_t *priv, imgsensor_value_t val) { int ret = -EINVAL; FAR isx019_default_value_t *def = &priv->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(priv, CAT_PICTTUNE, UISATURATION, &sat, 1); if (ret == OK) { ret = isx019_i2c_write(priv, CAT_PICTTUNE, UISHARPNESS, &sharp, 1); if (ret == OK) { priv->colorfx = val.value32; } } return ret; } static int set_ae(FAR isx019_dev_t *priv, imgsensor_value_t val) { uint32_t regval = 0; if (val.value32 == IMGSENSOR_EXPOSURE_AUTO) { regval = 0; } else { isx019_i2c_read(priv, CAT_AESOUT, SHT_TIME, ®val, 4); } return isx019_i2c_write(priv, CAT_CATAE, SHT_PRIMODE, ®val, 4); } static int set_exptime(FAR isx019_dev_t *priv, 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 * priv->clock_ratio; return isx019_i2c_write(priv, CAT_CATAE, SHT_PRIMODE, ®val, 4); } static int set_awb_hold(FAR isx019_dev_t *priv) { uint8_t mode = AWBMODE_HOLD; return isx019_i2c_write(priv, CAT_CATAWB, AWBMODE, &mode, 1); } static void initialize_wbmode(FAR isx019_dev_t *priv) { priv->wb_mode = IMGSENSOR_WHITE_BALANCE_AUTO; } static int update_wbmode_reg(FAR isx019_dev_t *priv, int32_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) { 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(priv, CAT_AWB_USERTYPE, r_addr, &r, 2); isx019_i2c_write(priv, CAT_AWB_USERTYPE, b_addr, &b, 2); isx019_i2c_write(priv, CAT_CATAWB, AWBUSER_NO, &toggle, 1); isx019_i2c_write(priv, CAT_CATAWB, AWBMODE, &mode, 1); return OK; } static bool is_awb_enable(FAR isx019_dev_t *priv) { uint8_t mode = AWBMODE_AUTO; isx019_i2c_read(priv, CAT_CATAWB, AWBMODE, &mode, 1); return mode != AWBMODE_HOLD; } static int set_wbmode(FAR isx019_dev_t *priv, imgsensor_value_t val) { /* Update register only if IMGSENSOR_ID_AUTO_WHITE_BALANCE = 1. */ if (is_awb_enable(priv)) { update_wbmode_reg(priv, val.value32); } priv->wb_mode = val.value32; return OK; } static int set_awb(FAR isx019_dev_t *priv, imgsensor_value_t val) { /* true -> false : Update regster to HOLD * false -> true : Update register * with IMGSENSOR_ID_AUTO_N_PRESET_WB setting * otherwise : Nothing to do */ if (is_awb_enable(priv)) { if (val.value32 == 0) { set_awb_hold(priv); } } else { if (val.value32 == 1) { update_wbmode_reg(priv, priv->wb_mode); } } return OK; } static int set_meter(FAR isx019_dev_t *priv, 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(priv, CAT_AUTOCTRL, AEWEIGHTMODE, &normal, 1); isx019_i2c_write(priv, CAT_AEWD, AEWEIGHTMODE_WD, &hdr, 1); return OK; } static void get_current_framesize(FAR isx019_dev_t *priv, FAR uint16_t *w, FAR uint16_t *h) { uint8_t frmsz; DEBUGASSERT(w && h); fpga_i2c_read(priv, FPGA_FORMAT_AND_SCALE, &frmsz, 1); switch (frmsz & 0xf0) { case FPGA_SCALE_1280_960: *w = 1280; *h = 960; break; case FPGA_SCALE_640_480: *w = 640; *h = 480; break; case FPGA_SCALE_320_240: *w = 320; *h = 240; break; case FPGA_SCALE_160_120: *w = 160; *h = 120; break; default: /* It may not come here due to register specification */ break; } } static void get_current_clip_setting(FAR isx019_dev_t *priv, FAR uint16_t *w, FAR uint16_t *h, FAR uint16_t *offset_x, FAR uint16_t *offset_y) { uint8_t sz; uint8_t top; uint8_t left; fpga_i2c_read(priv, FPGA_CLIP_SIZE, &sz, 1); fpga_i2c_read(priv, FPGA_CLIP_TOP, &top, 1); fpga_i2c_read(priv, FPGA_CLIP_LEFT, &left, 1); *offset_x = left * FPGA_CLIP_UNIT; *offset_y = top * FPGA_CLIP_UNIT; switch (sz) { case FPGA_CLIP_NON: *w = 0; *h = 0; *offset_x = 0; *offset_y = 0; break; case FPGA_CLIP_1280_720: *w = 1280; *h = 720; break; case FPGA_CLIP_640_360: *w = 640; *h = 360; break; default: /* It may not come here due to register specification */ break; } } static int calc_spot_position_regval(uint16_t val, uint16_t basis, uint16_t sz, uint16_t offset, int split) { int ret; int ratio; /* Change basis from `sz` to `basis` about `val` and `offset`. */ ratio = basis / sz; ret = val * ratio; ret += (offset * FPGA_CLIP_UNIT * ratio); return (ret * split) / basis; } static int set_spot_position(FAR isx019_dev_t *priv, imgsensor_value_t val) { uint8_t regval; uint8_t reg_x; uint8_t reg_y; uint16_t w; uint16_t h; uint16_t clip_w; uint16_t clip_h; uint16_t offset_x; uint16_t offset_y; uint16_t x = (uint16_t)(val.value32 >> 16); uint16_t y = (uint16_t)(val.value32 & 0xffff); int split; /* Spot position of ISX019 is divided into 9x7 sections. * - Horizontal direction is devided into 9 sections. * - Vertical direction is divided into 7 sections. * The register value 0 means left top. * The register value 62 means right bottom. * Then, the following ISX019 board flow. * - image sensor output the 1280x960 image * - FPGA scale * - FPGA clipping */ get_current_framesize(priv, &w, &h); if ((x >= w) || (y >= h)) { return -EINVAL; } get_current_clip_setting(priv, &clip_w, &clip_h, &offset_x, &offset_y); if ((clip_w != 0) && (clip_h != 0)) { if ((x >= clip_w) || (y >= clip_h)) { return -EINVAL; } } split = ISX019_SPOT_POSITION_SPLIT_NUM_X; reg_x = calc_spot_position_regval(x, 1280, w, offset_x, split); split = ISX019_SPOT_POSITION_SPLIT_NUM_Y; reg_y = calc_spot_position_regval(y, 960, h, offset_y, split); regval = reg_y * ISX019_SPOT_POSITION_SPLIT_NUM_X + reg_x; return isx019_i2c_write(priv, CAT_CATAE, SPOT_FRM_NUM, ®val, 1); } static int set_3alock(FAR isx019_dev_t *priv, imgsensor_value_t val) { uint8_t regval; regval = (val.value32 & IMGSENSOR_LOCK_WHITE_BALANCE) ? AWBMODE_HOLD : AWBMODE_AUTO; isx019_i2c_write(priv, CAT_CATAWB, AWBMODE, ®val, 1); regval = (val.value32 & IMGSENSOR_LOCK_EXPOSURE) ? AEMODE_HOLD : AEMODE_AUTO; isx019_i2c_write(priv, CAT_CATAE, AEMODE, ®val, 1); return OK; } static int set_3aparameter(FAR isx019_dev_t *priv, 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(priv, CAT_AWB_USERTYPE, USER4_R, &val.p_u8[OFFSET_3APARAMETER_AWB_R], 2); isx019_i2c_write(priv, CAT_AWB_USERTYPE, USER4_B, &val.p_u8[OFFSET_3APARAMETER_AWB_B], 2); regval = 4; isx019_i2c_write(priv, CAT_CATAWB, AWBUSER_NO, ®val, 1); regval = AWBMODE_MANUAL; isx019_i2c_write(priv, CAT_CATAWB, AWBMODE, ®val, 1); isx019_i2c_write(priv, CAT_CATAE, SHT_PRIMODE, &val.p_u8[OFFSET_3APARAMETER_AE_SHTTIME], 4); isx019_i2c_write(priv, CAT_CATAE, GAIN_PRIMODE, &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(FAR isx019_dev_t *priv, 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(priv, CAT_CATAE, GAIN_PRIMODE, &gain, 2); priv->iso = val.value32; return OK; } static int set_iso_auto(FAR isx019_dev_t *priv, imgsensor_value_t val) { uint8_t buf; uint16_t gain; if (val.value32 == IMGSENSOR_ISO_SENSITIVITY_AUTO) { gain = 0; priv->iso = 0; } else /* IMGSENSOR_ISO_SENSITIVITY_MANUAL */ { isx019_i2c_read(priv, CAT_CATAE, GAIN_PRIMODE, &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(priv, CAT_AECOM, GAIN_LEVEL, &buf, 1); gain = buf * 3; } priv->iso = val.value32; } return isx019_i2c_write(priv, CAT_CATAE, GAIN_PRIMODE, &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(FAR isx019_dev_t *priv, 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(priv, CAT_PICTGAMMA, offset, ®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(priv, CAT_PICTGAMMA, offset, ®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(priv, CAT_PICTGAMMA, GAM_KNOT_C8, ®val, 2); isx019_i2c_write(priv, CAT_PICTGAMMA, GAM_KNOT_C10, ®val, 2); isx019_i2c_read(priv, CAT_PICTGAMMA, GAM_KNOT_C11, ®val, 2); isx019_i2c_write(priv, CAT_PICTGAMMA, GAM_KNOT_C9, ®val, 2); priv->gamma = val.value32; return OK; } static void search_dqt_data(int32_t quality, FAR const uint8_t **y_head, FAR const uint8_t **y_calc, FAR const uint8_t **c_head, FAR const uint8_t **c_calc) { int i; FAR const 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(FAR isx019_dev_t *priv, uint8_t component, uint8_t target, FAR const 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(priv, select, &component, 1); for (i = 0; i < JPEG_DQT_ARRAY_SIZE; i++) { regval = i | FPGA_DQT_WRITE | FPGA_DQT_BUFFER; fpga_i2c_write(priv, addr, ®val, 1); fpga_i2c_write(priv, data, &buf[i], 1); } return OK; } static int set_jpg_quality(FAR isx019_dev_t *priv, imgsensor_value_t val) { FAR const uint8_t *y_head; FAR const uint8_t *y_calc; FAR const uint8_t *c_head; FAR const 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; } nxmutex_lock(&priv->fpga_lock); /* Update DQT data and activate them. */ set_dqt(priv, FPGA_DQT_LUMA, FPGA_DQT_DATA, y_head); set_dqt(priv, FPGA_DQT_CHROMA, FPGA_DQT_DATA, c_head); set_dqt(priv, FPGA_DQT_LUMA, FPGA_DQT_CALC_DATA, y_calc); set_dqt(priv, FPGA_DQT_CHROMA, FPGA_DQT_CALC_DATA, c_calc); fpga_activate_setting(priv); /* Wait for swap of non-active side and active side. */ nxsig_usleep(DELAY_TIME_JPEGDQT_SWAP); /* Update non-active side in preparation for other activation trigger. */ set_dqt(priv, FPGA_DQT_LUMA, FPGA_DQT_DATA, y_head); set_dqt(priv, FPGA_DQT_CHROMA, FPGA_DQT_DATA, c_head); set_dqt(priv, FPGA_DQT_LUMA, FPGA_DQT_CALC_DATA, y_calc); set_dqt(priv, FPGA_DQT_CHROMA, FPGA_DQT_CALC_DATA, c_calc); nxmutex_unlock(&priv->fpga_lock); priv->jpg_quality = val.value32; return OK; } static int initialize_jpg_quality(FAR isx019_dev_t *priv) { imgsensor_value_t val; val.value32 = CONFIG_VIDEO_ISX019_INITIAL_JPEG_QUALITY; return set_jpg_quality(priv, 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(FAR isx019_dev_t *priv, imgsensor_value_t val) { return set_clip(val.p_u32, &priv->clip_video); } static int set_clip_still(FAR isx019_dev_t *priv, imgsensor_value_t val) { return set_clip(val.p_u32, &priv->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_WHITE_BALANCE: func = set_awb; 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_SPOT_POSITION: func = set_spot_position; 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(FAR uint8_t *flip, uint8_t direction) { DEBUGASSERT(flip); return (*flip & direction) ? 1 : 0; } static int get_hflip_video(FAR isx019_dev_t *priv, FAR imgsensor_value_t *val) { if (val == NULL) { return -EINVAL; } val->value32 = get_flip(&priv->flip_video, H_REVERSE); return OK; } static int get_vflip_video(FAR isx019_dev_t *priv, FAR imgsensor_value_t *val) { if (val == NULL) { return -EINVAL; } val->value32 = get_flip(&priv->flip_video, V_REVERSE); return OK; } static int get_hflip_still(FAR isx019_dev_t *priv, FAR imgsensor_value_t *val) { if (val == NULL) { return -EINVAL; } val->value32 = get_flip(&priv->flip_still, H_REVERSE); return OK; } static int get_vflip_still(FAR isx019_dev_t *priv, FAR imgsensor_value_t *val) { if (val == NULL) { return -EINVAL; } val->value32 = get_flip(&priv->flip_still, V_REVERSE); return OK; } static int get_colorfx(FAR isx019_dev_t *priv, FAR imgsensor_value_t *val) { if (val == NULL) { return -EINVAL; } val->value32 = priv->colorfx; return OK; } static int get_ae(FAR isx019_dev_t *priv, FAR imgsensor_value_t *val) { uint32_t regval; if (val == NULL) { return -EINVAL; } isx019_i2c_read(priv, CAT_CATAE, SHT_PRIMODE, ®val, 4); val->value32 = (regval == 0) ? IMGSENSOR_EXPOSURE_AUTO : IMGSENSOR_EXPOSURE_MANUAL; return OK; } static int get_exptime(FAR isx019_dev_t *priv, FAR imgsensor_value_t *val) { uint32_t regval; isx019_i2c_read(priv, CAT_AESOUT, SHT_TIME, ®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 / priv->clock_ratio) + 99) / 100; return OK; } static int get_awb(FAR isx019_dev_t *priv, FAR imgsensor_value_t *val) { if (val == NULL) { return -EINVAL; } val->value32 = is_awb_enable(priv); return OK; } static int get_wbmode(FAR isx019_dev_t *priv, FAR imgsensor_value_t *val) { if (val == NULL) { return -EINVAL; } val->value32 = priv->wb_mode; return OK; } static int get_meter(FAR isx019_dev_t *priv, FAR imgsensor_value_t *val) { uint8_t regval; if (val == NULL) { return -EINVAL; } isx019_i2c_read(priv, 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 uint32_t restore_spot_position(uint16_t regval, uint16_t basis, uint16_t sz, uint16_t clip_sz, uint16_t offset, uint16_t split) { uint16_t ret; uint16_t unit; uint16_t border; /* First, convert register value to coordinate value. */ unit = basis / split; ret = (regval * unit) + (unit / 2); /* Second, consider the ratio between basis size and frame size. */ ret = ret * sz / basis; /* Third, consider offset value of clip setting. */ if (ret > offset) { ret = ret - offset; } else { ret = 0; } /* If the coordinate protrudes from the frame, * regard it as the boader of the frame. */ border = (clip_sz != 0) ? (clip_sz - 1) : (sz - 1); if (ret > border) { ret = border; } return ret; } static int get_spot_position(FAR isx019_dev_t *priv, FAR imgsensor_value_t *val) { uint8_t regval; uint8_t regx; uint8_t regy; uint16_t w; uint16_t h; uint32_t x; uint32_t y; uint16_t clip_w; uint16_t clip_h; uint16_t offset_x; uint16_t offset_y; int split; isx019_i2c_read(priv, CAT_CATAE, SPOT_FRM_NUM, ®val, 1); regx = regval % ISX019_SPOT_POSITION_SPLIT_NUM_X; regy = regval / ISX019_SPOT_POSITION_SPLIT_NUM_X; get_current_framesize(priv, &w, &h); get_current_clip_setting(priv, &clip_w, &clip_h, &offset_x, &offset_y); split = ISX019_SPOT_POSITION_SPLIT_NUM_X; x = restore_spot_position(regx, ISX019_WIDTH, w, clip_w, offset_x, split); split = ISX019_SPOT_POSITION_SPLIT_NUM_Y; y = restore_spot_position(regy, ISX019_HEIGHT, h, clip_h, offset_y, split); val->value32 = (int32_t)((x << 16) | y); return OK; } static int get_3alock(FAR isx019_dev_t *priv, FAR imgsensor_value_t *val) { uint8_t regval; uint8_t awb; uint8_t ae; if (val == NULL) { return -EINVAL; } isx019_i2c_read(priv, CAT_CATAWB, AWBMODE, ®val, 1); awb = (regval == AWBMODE_AUTO) ? 0 : IMGSENSOR_LOCK_WHITE_BALANCE; isx019_i2c_read(priv, CAT_CATAE, AEMODE, ®val, 1); ae = (regval == AEMODE_AUTO) ? 0 : IMGSENSOR_LOCK_EXPOSURE; val->value32 = awb | ae; return OK; } static int get_3aparameter(FAR isx019_dev_t *priv, FAR imgsensor_value_t *val) { if (val == NULL) { return -EINVAL; } if (val->p_u8 == NULL) { return -EINVAL; } isx019_i2c_read(priv, CAT_AWBSOUT, CONT_R, &val->p_u8[OFFSET_3APARAMETER_AWB_R], 2); isx019_i2c_read(priv, CAT_AWBSOUT, CONT_B, &val->p_u8[OFFSET_3APARAMETER_AWB_B], 2); isx019_i2c_read(priv, CAT_AESOUT, SHT_TIME, &val->p_u8[OFFSET_3APARAMETER_AE_SHTTIME], 4); isx019_i2c_read(priv, CAT_AECOM, GAIN_LEVEL, &val->p_u8[OFFSET_3APARAMETER_AE_GAIN], 1); return OK; } static int get_3astatus(FAR isx019_dev_t *priv, FAR imgsensor_value_t *val) { uint8_t regval; if (val == NULL) { return -EINVAL; } isx019_i2c_read(priv, 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. */ /* Divide 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 isx019_dev_t *priv, FAR imgsensor_value_t *val) { uint8_t buf = 0; if (val == NULL) { return -EINVAL; } if (priv->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(priv, CAT_AECOM, GAIN_LEVEL, &buf, 1); val->value32 = calc_iso((double)buf * 0.3) * USEC_PER_MSEC; } else { val->value32 = priv->iso; } return OK; } static int get_iso_auto(FAR isx019_dev_t *priv, FAR imgsensor_value_t *val) { uint16_t gain; if (val == NULL) { return -EINVAL; } isx019_i2c_read(priv, CAT_CATAE, GAIN_PRIMODE, &gain, 2); val->value32 = (gain == 0) ? IMGSENSOR_ISO_SENSITIVITY_AUTO : IMGSENSOR_ISO_SENSITIVITY_MANUAL; return OK; } static int get_gamma(FAR isx019_dev_t *priv, FAR imgsensor_value_t *val) { if (val == NULL) { return -EINVAL; } val->value32 = priv->gamma; return OK; } static int get_jpg_quality(FAR isx019_dev_t *priv, FAR imgsensor_value_t *val) { if (val == NULL) { return -EINVAL; } val->value32 = priv->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_WHITE_BALANCE: func = get_awb; 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_SPOT_POSITION: func = get_spot_position; 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(FAR struct imgsensor_s *sensor, uint32_t id, uint32_t size, FAR imgsensor_value_t *val) { FAR isx019_dev_t *priv = (FAR isx019_dev_t *)sensor; 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(priv, reg.category, reg.offset, &val32, reg.size); val->value32 = cvrt(val32); } else { get = get_value_func(id); if (get) { ret = get(priv, 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 = -ERANGE; } 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(FAR isx019_dev_t *priv, uint32_t id, uint32_t size, imgsensor_value_t val) { int ret; imgsensor_supported_value_t sup; ret = isx019_get_supported_value(&priv->sensor, 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(FAR struct imgsensor_s *sensor, uint32_t id, uint32_t size, imgsensor_value_t val) { FAR isx019_dev_t *priv = (FAR isx019_dev_t *)sensor; int ret = -EINVAL; isx019_reginfo_t reg; convert_t cvrt; setvalue_t set; int32_t val32; ret = validate_value(priv, id, size, val); if (ret != OK) { return ret; } cvrt = get_reginfo(id, true, ®); if (cvrt) { val32 = cvrt(val.value32); ret = isx019_i2c_write(priv, reg.category, reg.offset, &val32, reg.size); } else { set = set_value_func(id); if (set) { ret = set(priv, val); } } return ret; } /**************************************************************************** * Public Functions ****************************************************************************/ int isx019_initialize(void) { FAR isx019_dev_t *priv = &g_isx019_private; imgsensor_register(&priv->sensor); return OK; } int isx019_uninitialize(void) { return OK; } #ifdef CONFIG_VIDEO_ISX019_REGDEBUG int isx019_read_register(uint8_t cat, uint16_t addr, FAR uint8_t *buf, uint8_t size) { FAR isx019_dev_t *priv = &g_isx019_private; int ret; if (cat == 0xff) { ret = fpga_i2c_read(priv, (uint8_t)addr, buf, size); } else { ret = isx019_i2c_read(priv, cat, addr, buf, size); } return ret; } #endif