nuttx/drivers/video/isx019.c
SPRESENSE 3419f018a4 drivers/video/isx019: Delete unnecessary code
Because of misunderstanding spec, unnecessary codes exist.
Delete them.
2024-03-11 15:44:39 +01:00

3892 lines
99 KiB
C

/****************************************************************************
* 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 <nuttx/config.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/i2c/i2c_master.h>
#include <nuttx/signal.h>
#include <nuttx/clock.h>
#include <arch/board/board.h>
#include <nuttx/video/isx019.h>
#include <nuttx/video/imgsensor.h>
#include <math.h>
#include <nuttx/mutex.h>
#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, t, s) do \
{ \
(a)->category = (c); \
(a)->offset = (o); \
(a)->type = (t); \
(a)->size = (s); \
} \
while (0);
/* Register type, which represents the number of bits and
* whether it is signed or unsigned.
*/
#define ISX019_REGTYPE_INT8 (0)
#define ISX019_REGTYPE_UINT8 (1)
#define ISX019_REGTYPE_INT16 (2)
#define SET_REGINFO_INT8(a, c, o) SET_REGINFO(a, c, o, ISX019_REGTYPE_INT8, 1)
#define SET_REGINFO_UINT8(a, c, o) SET_REGINFO(a, c, o, ISX019_REGTYPE_UINT8, 1)
#define SET_REGINFO_INT16(a, c, o) SET_REGINFO(a, c, o, ISX019_REGTYPE_INT16, 2)
#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;
double gamma;
int32_t jpg_quality;
int32_t hue;
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 type;
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))
static const int32_t g_isx019_iso[] =
{
1000, /* ISO1 */
1200, /* ISO1.2 */
1600, /* ISO1.6 */
2000, /* ISO2 */
2500, /* ISO2.5 */
3000, /* ISO3 */
4000, /* ISO4 */
5000, /* ISO5 */
6000, /* ISO6 */
8000, /* ISO8 */
10000, /* ISO10 */
12000, /* ISO12 */
16000, /* ISO16 */
20000, /* ISO20 */
25000, /* ISO25 */
32000, /* ISO32 */
40000, /* ISO40 */
50000, /* ISO50 */
64000, /* ISO64 */
80000, /* ISO80 */
100000, /* ISO100 */
125000, /* ISO125 */
160000, /* ISO160 */
200000, /* ISO200 */
250000, /* ISO250 */
320000, /* ISO320 */
400000, /* ISO400 */
500000, /* ISO500 */
640000, /* ISO640 */
800000, /* ISO800 */
1000000, /* ISO1000 */
1250000, /* ISO1250 */
1600000, /* ISO1600 */
2000000, /* ISO2000 */
2500000, /* ISO2500 */
3200000, /* ISO3200 */
4000000, /* ISO4000 */
5000000, /* ISO5000 */
};
#define NR_ISO (sizeof(g_isx019_iso) / sizeof(int32_t))
/* Gain values corresponding to each element of g_isx019_iso table.
* This needs to have the same size as g_isx019_iso.
*/
static const uint8_t g_isx019_gain[] =
{
1, /* gain for ISO1 */
2, /* gain for ISO1.2 */
3, /* gain for ISO1.6 */
4, /* gain for ISO2 */
5, /* gain for ISO2.5 */
6, /* gain for ISO3 */
7, /* gain for ISO4 */
8, /* gain for ISO5 */
9, /* gain for ISO6 */
10, /* gain for ISO8 */
11, /* gain for ISO10 */
12, /* gain for ISO12 */
13, /* gain for ISO16 */
14, /* gain for ISO20 */
15, /* gain for ISO25 */
16, /* gain for ISO32 */
17, /* gain for ISO40 */
18, /* gain for ISO50 */
19, /* gain for ISO64 */
20, /* gain for ISO80 */
21, /* gain for ISO100 */
22, /* gain for ISO125 */
23, /* gain for ISO160 */
24, /* gain for ISO200 */
25, /* gain for ISO250 */
26, /* gain for ISO320 */
27, /* gain for ISO400 */
28, /* gain for ISO500 */
29, /* gain for ISO640 */
30, /* gain for ISO800 */
31, /* gain for ISO1000 */
32, /* gain for ISO1250 */
33, /* gain for ISO1600 */
34, /* gain for ISO2000 */
35, /* gain for ISO2500 */
36, /* gain for ISO3200 */
37, /* gain for ISO4000 */
38, /* gain for ISO5000 */
};
static const int32_t g_isx019_iso_auto[] =
{
IMGSENSOR_ISO_SENSITIVITY_MANUAL,
IMGSENSOR_ISO_SENSITIVITY_AUTO,
};
#define NR_ISO_AUTO (sizeof(g_isx019_iso_auto) / sizeof(int32_t))
static const int32_t g_isx019_metering[] =
{
IMGSENSOR_EXPOSURE_METERING_AVERAGE,
IMGSENSOR_EXPOSURE_METERING_CENTER_WEIGHTED,
IMGSENSOR_EXPOSURE_METERING_SPOT,
IMGSENSOR_EXPOSURE_METERING_MATRIX,
};
#define NR_METERING (sizeof(g_isx019_metering) / sizeof(int32_t))
static const int32_t g_isx019_ae[] =
{
IMGSENSOR_EXPOSURE_AUTO,
IMGSENSOR_EXPOSURE_MANUAL,
};
#define NR_AE (sizeof(g_isx019_ae) / 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 = FPGA_I2C_FREQUENCY;
config.address = FPGA_I2C_SLVADDR;
config.addrlen = FPGA_I2C_SLVADDR_LEN;
nxmutex_lock(&priv->i2c_lock);
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 = FPGA_I2C_FREQUENCY;
config.address = FPGA_I2C_SLVADDR;
config.addrlen = FPGA_I2C_SLVADDR_LEN;
nxmutex_lock(&priv->i2c_lock);
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, &regval, 1);
while (1)
{
fpga_i2c_read(priv, FPGA_ACTIVATE, &regval, 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, &regval, 1);
regval = FPGA_DATA_OUTPUT_STOP;
fpga_i2c_write(priv, FPGA_DATA_OUTPUT, &regval, 1);
fpga_activate_setting(priv);
regval = FPGA_RESET_RELEASE;
fpga_i2c_write(priv, FPGA_RESET, &regval, 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);
/* Set initial gamma value for getting current value API. */
priv->gamma = 1000;
/* Because store_default_value() needs the clock ratio,
* clock_ratio has to be calculated first.
*/
clk = board_isx019_get_master_clock();
priv->clock_ratio = (float)clk / ISX019_STANDARD_MASTER_CLOCK;
store_default_value(priv);
/* Store initial HUE value for getting current value API. */
priv->hue = priv->default_value.hue;
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, &regval, 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, &regval, 1);
regval = FPGA_DATA_OUTPUT_START;
fpga_i2c_write(priv, FPGA_DATA_OUTPUT, &regval, 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_BOOLEAN;
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_BOOLEAN;
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_BOOLEAN;
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_BOOLEAN;
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_BOOLEAN;
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_MENU;
SET_DISCRETE(val->u.discrete,
NR_AE,
g_isx019_ae,
IMGSENSOR_EXPOSURE_AUTO);
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_MENU;
SET_DISCRETE(val->u.discrete,
NR_ISO,
g_isx019_iso,
def->iso);
break;
case IMGSENSOR_ID_ISO_SENSITIVITY_AUTO:
val->type = IMGSENSOR_CTRL_TYPE_INTEGER_MENU;
SET_DISCRETE(val->u.discrete,
NR_ISO_AUTO,
g_isx019_iso_auto,
IMGSENSOR_ISO_SENSITIVITY_AUTO);
break;
case IMGSENSOR_ID_EXPOSURE_METERING:
val->type = IMGSENSOR_CTRL_TYPE_INTEGER_MENU;
SET_DISCRETE(val->u.discrete,
NR_METERING,
g_isx019_metering,
IMGSENSOR_EXPOSURE_METERING_AVERAGE);
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 * 4);
}
static int32_t convert_brightness_reg2is(int32_t val)
{
return (val / 4);
}
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_INT16(reg, CAT_PICTTUNE, UIBRIGHTNESS);
cvrt = is_set ? convert_brightness_is2reg
: convert_brightness_reg2is;
break;
case IMGSENSOR_ID_CONTRAST:
SET_REGINFO_UINT8(reg, CAT_PICTTUNE, UICONTRAST);
cvrt = not_convert;
break;
case IMGSENSOR_ID_SATURATION:
SET_REGINFO_UINT8(reg, CAT_PICTTUNE, UISATURATION);
cvrt = not_convert;
break;
case IMGSENSOR_ID_EXPOSURE:
SET_REGINFO_INT8(reg, CAT_AEDGRM, EVSEL);
cvrt = not_convert;
break;
case IMGSENSOR_ID_SHARPNESS:
SET_REGINFO_UINT8(reg, CAT_PICTTUNE, UISHARPNESS);
cvrt = not_convert;
break;
case IMGSENSOR_ID_WIDE_DYNAMIC_RANGE:
SET_REGINFO_UINT8(reg, CAT_AEWD, AEWDMODE);
cvrt = is_set ? convert_hdr_is2reg : convert_hdr_reg2is;
break;
default:
break;
}
return cvrt;
}
static int set_hue(FAR isx019_dev_t *priv,
imgsensor_value_t val)
{
int ret;
int val32 = val.value32 * 90 / 128;
ret = isx019_i2c_write(priv, CAT_PICTTUNE, UIHUE, &val32, 1);
if (ret == OK)
{
/* Store value before conversion for get_hue(). */
priv->hue = val.value32;
}
return ret;
}
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, &regval, 4);
}
return isx019_i2c_write(priv, CAT_CATAE, SHT_PRIMODE, &regval, 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, &regval, 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, &regval, 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, &regval, 1);
regval = (val.value32 & IMGSENSOR_LOCK_EXPOSURE) ? AEMODE_HOLD
: AEMODE_AUTO;
isx019_i2c_write(priv, CAT_CATAE, AEMODE, &regval, 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, &regval, 1);
regval = AWBMODE_MANUAL;
isx019_i2c_write(priv, CAT_CATAWB, AWBMODE, &regval, 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 get_gain_from_iso(int32_t iso)
{
int i;
for (i = 0; i < NR_ISO; i++)
{
if (g_isx019_iso[i] == iso)
{
break;
}
}
/* Return gain corresponding to specified ISO sensitivity. */
return (uint16_t)g_isx019_gain[i];
}
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 = get_gain_from_iso(val.value32) * 10;
isx019_i2c_write(priv, CAT_CATAE, GAIN_PRIMODE, &gain, 2);
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;
}
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;
}
}
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, &regval, 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, &regval, 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, &regval, 2);
isx019_i2c_write(priv, CAT_PICTGAMMA, GAM_KNOT_C10, &regval, 2);
isx019_i2c_read(priv, CAT_PICTGAMMA, GAM_KNOT_C11, &regval, 2);
isx019_i2c_write(priv, CAT_PICTGAMMA, GAM_KNOT_C9, &regval, 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, &regval, 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_HUE:
func = set_hue;
break;
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 int get_hue(FAR isx019_dev_t *priv,
FAR imgsensor_value_t *val)
{
if (val == NULL)
{
return -EINVAL;
}
/* Return stored value without reading register. */
val->value32 = priv->hue;
return OK;
}
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, &regval, 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, &regval, 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, &regval, 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, &regval, 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, &regval, 1);
awb = (regval == AWBMODE_AUTO) ? 0 : IMGSENSOR_LOCK_WHITE_BALANCE;
isx019_i2c_read(priv, CAT_CATAE, AEMODE, &regval, 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, &regval, 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 int32_t get_iso_from_gain(uint8_t gain)
{
int i;
/* g_isx019_gain and g_isx019_iso has the common index. */
for (i = 0; i < NR_ISO; i++)
{
if (g_isx019_gain[i] == gain)
{
break;
}
}
if (i >= NR_ISO)
{
i = NR_ISO - 1;
}
return g_isx019_iso[i];
}
static int get_iso(FAR isx019_dev_t *priv,
FAR imgsensor_value_t *val)
{
uint8_t gain = 0;
if (val == NULL)
{
return -EINVAL;
}
/* The current gain value register has the 0.3dB unit.
* So, round the gain to integer, and convert to ISO.
*/
isx019_i2c_read(priv, CAT_AECOM, GAIN_LEVEL, &gain, 1);
gain = ((gain * 3) + 5) / 10;
val->value32 = get_iso_from_gain(gain);
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_HUE:
func = get_hue;
break;
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;
union
{
int32_t i32;
int16_t i16;
int8_t i8;
} regval;
DEBUGASSERT(val);
cvrt = get_reginfo(id, false, &reg);
if (cvrt)
{
memset(&regval, 0, sizeof(regval));
ret = isx019_i2c_read(priv,
reg.category, reg.offset, &regval.i32, reg.size);
switch (reg.type)
{
case ISX019_REGTYPE_INT8:
regval.i32 = (int32_t)regval.i8;
break;
case ISX019_REGTYPE_INT16:
regval.i32 = (int32_t)regval.i16;
break;
default:
break;
}
val->value32 = cvrt(regval.i32);
}
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, &reg);
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