mirror of
https://gitee.com/Vancouver2017/luban-lite.git
synced 2025-12-25 13:38:54 +00:00
1121 lines
26 KiB
C
1121 lines
26 KiB
C
/*
|
|
* Copyright (c) 2024-2025, ArtInChip Technology Co., Ltd
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Authors: matteo <duanmt@artinchip.com>
|
|
*/
|
|
|
|
#define LOG_TAG "ov9281"
|
|
|
|
#include <drivers/i2c.h>
|
|
#include <drivers/pin.h>
|
|
|
|
#include "aic_core.h"
|
|
#include "mpp_types.h"
|
|
#include "mpp_img_size.h"
|
|
#include "mpp_vin.h"
|
|
|
|
#include "camera_inner.h"
|
|
#include "drv_camera.h"
|
|
|
|
/* Format configuration of OV9281 */
|
|
#define OV9281_DEFAULT_MODE OV9281_MODE_640_480
|
|
#define OV9281_DEFAULT_BUS_TYPE MEDIA_BUS_RAW8_MONO
|
|
#define OV9281_DEFAULT_CODE MEDIA_BUS_FMT_Y8_1X8
|
|
#define OV9281_DEFAULT_DVP_BUS_WIDTH 8
|
|
#define OV9281_DEFAULT_AUTO_EXPOSURE 1
|
|
#define OV9281_DEFAULT_AUTO_GAIN 1
|
|
|
|
// #define OV9281_TEST_MODE
|
|
|
|
#define OV9281_LINK_FREQ_400MHZ 400000000
|
|
#define OV9281_LANES 2
|
|
|
|
/* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */
|
|
#define OV9281_PIXEL_RATE_10BIT (OV9281_LINK_FREQ_400MHZ * 2 * \
|
|
OV9281_LANES / 10)
|
|
#define OV9281_PIXEL_RATE_8BIT (OV9281_LINK_FREQ_400MHZ * 2 * \
|
|
OV9281_LANES / 8)
|
|
#define OV9281_XVCLK_FREQ 24000000
|
|
|
|
#define OV9281_CHIP_ID 0x9281
|
|
#define OV9281_SLAVE_ID 0x60
|
|
|
|
#define OV9281_REG_CHIP_ID_HIGH 0x300a
|
|
#define OV9281_REG_CHIP_ID_LOW 0x300b
|
|
#define OV9281_REG_TIMING_FORMAT_1 0x3820
|
|
#define OV9281_REG_TIMING_FORMAT_2 0x3821
|
|
#define OV9281_FLIP_BIT BIT(2)
|
|
|
|
#define OV9281_REG_CTRL_MODE 0x0100
|
|
#define OV9281_MODE_SW_STANDBY 0x0
|
|
#define OV9281_MODE_STREAMING BIT(0)
|
|
|
|
#define OV9281_REG_EXPOSURE 0x3500
|
|
#define OV9281_EXPOSURE_MIN 4
|
|
#define OV9281_EXPOSURE_STEP 1
|
|
/*
|
|
* Number of lines less than frame length (VTS) that exposure must be.
|
|
* Datasheet states 25, although empirically 5 appears to work.
|
|
*/
|
|
#define OV9281_EXPOSURE_OFFSET 25
|
|
|
|
#define OV9281_REG_GAIN_H 0x3508
|
|
#define OV9281_REG_GAIN_L 0x3509
|
|
#define OV9281_GAIN_H_MASK 0x07
|
|
#define OV9281_GAIN_H_SHIFT 8
|
|
#define OV9281_GAIN_L_MASK 0xff
|
|
#define OV9281_GAIN_MIN 0x10
|
|
#define OV9281_GAIN_MAX 0xf8
|
|
#define OV9281_GAIN_STEP 1
|
|
#define OV9281_GAIN_DEFAULT 0x10
|
|
|
|
#define OV9281_REG_TEST_PATTERN 0x5e00
|
|
#define OV9281_TEST_PATTERN_ENABLE 0x80
|
|
#define OV9281_TEST_PATTERN_DISABLE 0x0
|
|
|
|
#define OV9281_REG_VTS 0x380e
|
|
#define OV9281_VTS_MAX 0x7fff
|
|
|
|
/*
|
|
* OV9281 native and active pixel array size.
|
|
* Datasheet not available to confirm these values, so assume there are no
|
|
* border pixels.
|
|
*/
|
|
#define OV9281_NATIVE_WIDTH 1280U
|
|
#define OV9281_NATIVE_HEIGHT 800U
|
|
#define OV9281_PIXEL_ARRAY_LEFT 0U
|
|
#define OV9281_PIXEL_ARRAY_TOP 0U
|
|
#define OV9281_PIXEL_ARRAY_WIDTH 1280U
|
|
#define OV9281_PIXEL_ARRAY_HEIGHT 800U
|
|
|
|
#define REG_NULL 0xFFFF
|
|
|
|
#define OV9281_REG_VALUE_08BIT 1
|
|
#define OV9281_REG_VALUE_16BIT 2
|
|
#define OV9281_REG_VALUE_24BIT 3
|
|
|
|
#define OV9281_NAME "ov9281"
|
|
|
|
enum ov9281_mode_id {
|
|
OV9281_MODE_1280_800 = 0,
|
|
OV9281_MODE_1280_720,
|
|
OV9281_MODE_640_480,
|
|
OV9281_MODE_640_400,
|
|
OV9281_MODE_NUM,
|
|
};
|
|
|
|
struct ov9281_mode {
|
|
u32 width;
|
|
u32 height;
|
|
u32 hts_def;
|
|
u32 vts_def;
|
|
u32 exp_def;
|
|
struct mpp_rect crop;
|
|
const struct reg16_info *reg_list;
|
|
};
|
|
|
|
struct ov9281_dev {
|
|
struct rt_device dev;
|
|
struct rt_i2c_bus_device *iic;
|
|
struct clk *xvclk;
|
|
u32 reset_gpio;
|
|
u32 pwdn_gpio;
|
|
|
|
bool streaming;
|
|
bool power_on;
|
|
const struct ov9281_mode *cur_mode;
|
|
u32 code;
|
|
struct mpp_video_fmt fmt;
|
|
};
|
|
|
|
static struct ov9281_dev g_ov9281_dev = {0};
|
|
|
|
#define to_ov9281(dev) ((struct ov9281_dev *)dev)
|
|
|
|
/*
|
|
* Xclk 24Mhz
|
|
* max_framerate 120fps for 10 bit, 144fps for 8 bit.
|
|
* mipi_datarate per lane 800Mbps
|
|
*/
|
|
#if 0
|
|
static const struct reg16_info ov9281_common_regs[] = {
|
|
{0x0103, 0x01},
|
|
{0x0302, 0x32},
|
|
{0x030e, 0x02},
|
|
{0x3001, 0x00},
|
|
{0x3004, 0x00},
|
|
{0x3005, 0x00},
|
|
{0x3006, 0x04},
|
|
{0x3011, 0x0a},
|
|
{0x3013, 0x18},
|
|
{0x3022, 0x01},
|
|
{0x3023, 0x00},
|
|
{0x302c, 0x00},
|
|
{0x302f, 0x00},
|
|
{0x3030, 0x04},
|
|
{0x3039, 0x32},
|
|
{0x303a, 0x00},
|
|
{0x303f, 0x01},
|
|
{0x3500, 0x00},
|
|
{0x3501, 0x2a},
|
|
{0x3502, 0x90},
|
|
{0x3503, 0x08},
|
|
{0x3505, 0x8c},
|
|
{0x3507, 0x03},
|
|
{0x3508, 0x00},
|
|
{0x3509, 0x10},
|
|
{0x3610, 0x80},
|
|
{0x3611, 0xa0},
|
|
{0x3620, 0x6f},
|
|
{0x3632, 0x56},
|
|
{0x3633, 0x78},
|
|
{0x3666, 0x00},
|
|
{0x366f, 0x5a},
|
|
{0x3680, 0x84},
|
|
{0x3712, 0x80},
|
|
{0x372d, 0x22},
|
|
{0x3731, 0x80},
|
|
{0x3732, 0x30},
|
|
{0x377d, 0x22},
|
|
{0x3788, 0x02},
|
|
{0x3789, 0xa4},
|
|
{0x378a, 0x00},
|
|
{0x378b, 0x4a},
|
|
{0x3799, 0x20},
|
|
{0x3881, 0x42},
|
|
{0x38b1, 0x00},
|
|
{0x3920, 0xff},
|
|
{0x4010, 0x40},
|
|
{0x4043, 0x40},
|
|
{0x4307, 0x30},
|
|
{0x4317, 0x00},
|
|
{0x4501, 0x00},
|
|
{0x450a, 0x08},
|
|
{0x4601, 0x04},
|
|
{0x470f, 0x00},
|
|
{0x4f07, 0x00},
|
|
{0x4800, 0x00},
|
|
{0x5000, 0x9f},
|
|
{0x5001, 0x00},
|
|
{0x5e00, 0x00},
|
|
{0x5d00, 0x07},
|
|
{0x5d01, 0x00},
|
|
{REG_NULL, 0x00},
|
|
};
|
|
#endif
|
|
|
|
static const struct reg16_info ov9281_1280x800_regs[] = {
|
|
{0x0103, 0x01},
|
|
{0x0302, 0x24},
|
|
{0x030d, 0x48},
|
|
{0x030e, 0x06},
|
|
{0x3001, 0x22},
|
|
{0x3004, 0x01},
|
|
{0x3005, 0xff},
|
|
{0x3006, 0xe2},
|
|
{0x3011, 0x0a},
|
|
{0x3013, 0x18},
|
|
{0x3022, 0x07},
|
|
{0x3039, 0x2e},
|
|
{0x303a, 0xf0},
|
|
{0x3500, 0x00},
|
|
{0x3501, 0x2a},
|
|
{0x3502, 0x90},
|
|
{0x3503, 0x00},
|
|
{0x3508, 0x03},
|
|
{0x3509, 0x00},
|
|
{0x3610, 0x80},
|
|
{0x3611, 0xa0},
|
|
{0x3620, 0x78},
|
|
{0x3632, 0x56},
|
|
{0x3633, 0x50},
|
|
{0x3662, 0x05},
|
|
{0x3666, 0x5a},
|
|
{0x366f, 0x7e},
|
|
{0x3712, 0x80},
|
|
{0x372d, 0x22},
|
|
{0x3731, 0x80},
|
|
{0x3732, 0x30},
|
|
{0x3778, 0x00},
|
|
{0x377d, 0x22},
|
|
{0x3800, 0x00},
|
|
{0x3801, 0x00},
|
|
{0x3802, 0x00},
|
|
{0x3803, 0x00},
|
|
{0x3804, 0x05},
|
|
{0x3805, 0x0f},
|
|
{0x3806, 0x03},
|
|
{0x3807, 0x2f},
|
|
{0x3808, 0x05},
|
|
{0x3809, 0x00},
|
|
{0x380a, 0x03},
|
|
{0x380b, 0x20},
|
|
{0x380c, 0x02},
|
|
{0x380d, 0xd8},
|
|
{0x380e, 0x03},
|
|
{0x380f, 0x8e},
|
|
{0x3810, 0x00},
|
|
{0x3811, 0x08},
|
|
{0x3812, 0x00},
|
|
{0x3813, 0x08},
|
|
{0x3814, 0x11},
|
|
{0x3815, 0x11},
|
|
{0x3820, 0x40},
|
|
{0x3821, 0x00},
|
|
{0x3881, 0x42},
|
|
{0x4043, 0x40},
|
|
{0x4307, 0x30},
|
|
{0x4317, 0x01},
|
|
{0x4501, 0x00},
|
|
{0x4507, 0x00},
|
|
{0x4509, 0x00},
|
|
{0x4800, 0x00},
|
|
{0x5000, 0x9f},
|
|
{0x5001, 0x00},
|
|
{0x5e00, 0x00},
|
|
{0x0100, 0x01},
|
|
{REG_NULL, 0x00},
|
|
};
|
|
|
|
static const struct reg16_info ov9281_1280x720_regs[] = {
|
|
{0x0103, 0x01},
|
|
{0x0106, 0x00},
|
|
{0x0302, 0x30},
|
|
{0x030d, 0x60},
|
|
{0x030e, 0x06},
|
|
{0x3001, 0x62},
|
|
{0x3004, 0x01},
|
|
{0x3005, 0xff},
|
|
{0x3006, 0xe2},
|
|
{0x3011, 0x0a},
|
|
{0x3013, 0x18},
|
|
{0x301c, 0xf0},
|
|
{0x3022, 0x07},
|
|
{0x3030, 0x10},
|
|
{0x3039, 0x2e},
|
|
{0x303a, 0xf0},
|
|
{0x3500, 0x00},
|
|
{0x3501, 0x2a},
|
|
{0x3502, 0x90},
|
|
{0x3503, 0x08},
|
|
{0x3505, 0x8c},
|
|
{0x3507, 0x03},
|
|
{0x3508, 0x00},
|
|
{0x3509, 0x10},
|
|
{0x3610, 0x80},
|
|
{0x3611, 0xa0},
|
|
{0x3620, 0x6e},
|
|
{0x3632, 0x56},
|
|
{0x3633, 0x78},
|
|
{0x3662, 0x05}, // RAW10
|
|
{0x3666, 0x5a},
|
|
{0x366f, 0x7e},
|
|
{0x3680, 0x84},
|
|
{0x3707, 0x60},
|
|
{0x370d, 0x01},
|
|
{0x370e, 0x00},
|
|
{0x3712, 0x80},
|
|
{0x372d, 0x22},
|
|
{0x3731, 0x80},
|
|
{0x3732, 0x30},
|
|
{0x3778, 0x00},
|
|
{0x377d, 0x22},
|
|
{0x3788, 0x02},
|
|
{0x3789, 0xa4},
|
|
{0x378a, 0x00},
|
|
{0x378b, 0x4a},
|
|
{0x3799, 0x20},
|
|
{0x379c, 0x01},
|
|
{0x3800, 0x00}, // ;
|
|
{0x3801, 0x00}, // ;
|
|
{0x3802, 0x00}, // ;
|
|
{0x3803, 0x28}, // ;
|
|
{0x3804, 0x05},
|
|
{0x3805, 0x0f},
|
|
{0x3806, 0x03},
|
|
{0x3807, 0x2f},
|
|
{0x3808, 0x05}, // X OUTPUT SIZE
|
|
{0x3809, 0x00}, // X OUTPUT SIZE
|
|
{0x380a, 0x02}, // Y OUTPUT SIZE
|
|
{0x380b, 0xd0}, // Y OUTPUT SIZE
|
|
{0x380c, 0x02},
|
|
{0x380d, 0xd8},
|
|
{0x380e, 0x03}, // 72 fps
|
|
{0x380f, 0x8e},
|
|
{0x3810, 0x00},
|
|
{0x3811, 0x08},
|
|
{0x3812, 0x00},
|
|
{0x3813, 0x08},
|
|
{0x3814, 0x11},
|
|
{0x3815, 0x11},
|
|
{0x3820, 0x40},
|
|
{0x3821, 0x00},
|
|
{0x382b, 0x3a},
|
|
{0x382c, 0x06},
|
|
{0x382d, 0xc2},
|
|
{0x389d, 0x00},
|
|
{0x3881, 0x42},
|
|
{0x3882, 0x02},
|
|
{0x3883, 0x12},
|
|
{0x3885, 0x07},
|
|
{0x38a8, 0x02},
|
|
{0x38a9, 0x80},
|
|
{0x38b1, 0x03},
|
|
{0x38b3, 0x07},
|
|
{0x38c4, 0x00},
|
|
{0x38c5, 0xc0},
|
|
{0x38c6, 0x04},
|
|
{0x38c7, 0x80},
|
|
{0x3920, 0xff},
|
|
{0x4003, 0x40},
|
|
{0x4008, 0x04},
|
|
{0x4009, 0x0b},
|
|
{0x400c, 0x01},
|
|
{0x400d, 0x07},
|
|
{0x4010, 0xf0},
|
|
{0x4011, 0x3b},
|
|
{0x4043, 0x40},
|
|
{0x4307, 0x30},
|
|
{0x4317, 0x01},
|
|
{0x4501, 0x00},
|
|
{0x4507, 0x00},
|
|
{0x4509, 0x00},
|
|
{0x450a, 0x08},
|
|
{0x4601, 0x04},
|
|
{0x470f, 0xe0},
|
|
{0x4f07, 0x00},
|
|
{0x4800, 0x00},
|
|
{0x5000, 0x9f},
|
|
{0x5001, 0x00},
|
|
{0x5e00, 0x00},
|
|
{0x5d00, 0x0b},
|
|
{0x5d01, 0x02},
|
|
{0x4f00, 0x0c},
|
|
{0x4f10, 0x00},
|
|
{0x4f11, 0x88},
|
|
{0x4f12, 0x0f},
|
|
{0x4f13, 0xc4},
|
|
|
|
{0x3501, 0x27},
|
|
{0x3502, 0x50},
|
|
|
|
{0x0100, 0x01},
|
|
{REG_NULL, 0x00},
|
|
};
|
|
|
|
static const struct reg16_info ov9281_640x480_regs[] = {
|
|
{0x0103, 0x01},
|
|
{0x0106, 0x00},
|
|
{0x0302, 0x30},
|
|
{0x030d, 0x60},
|
|
{0x030e, 0x06},
|
|
{0x3001, 0x62},
|
|
{0x3004, 0x01},
|
|
{0x3005, 0xff},
|
|
{0x3006, 0xe2},
|
|
{0x3011, 0x0a},
|
|
{0x3013, 0x18},
|
|
{0x301c, 0xf0},
|
|
{0x3022, 0x07},
|
|
{0x3030, 0x10},
|
|
{0x3039, 0x2e},
|
|
{0x303a, 0xf0},
|
|
{0x3500, 0x00},
|
|
{0x3501, 0x2a},
|
|
{0x3502, 0x90},
|
|
{0x3503, 0x08},
|
|
{0x3505, 0x8c},
|
|
{0x3507, 0x03},
|
|
{0x3508, 0x00},
|
|
{0x3509, 0x10},
|
|
{0x3610, 0x80},
|
|
{0x3611, 0xa0},
|
|
{0x3620, 0x6e},
|
|
{0x3632, 0x56},
|
|
{0x3633, 0x78},
|
|
{0x3662, 0x05}, // RAW10
|
|
{0x3666, 0x5a},
|
|
{0x366f, 0x7e},
|
|
{0x3680, 0x84},
|
|
{0x3707, 0x60},
|
|
{0x370d, 0x01},
|
|
{0x370e, 0x00},
|
|
{0x3712, 0x80},
|
|
{0x372d, 0x22},
|
|
{0x3731, 0x80},
|
|
{0x3732, 0x30},
|
|
{0x3778, 0x00},
|
|
{0x377d, 0x22},
|
|
{0x3788, 0x02},
|
|
{0x3789, 0xa4},
|
|
{0x378a, 0x00},
|
|
{0x378b, 0x4a},
|
|
{0x3799, 0x20},
|
|
{0x379c, 0x01},
|
|
{0x3800, 0x01},
|
|
{0x3801, 0x40},
|
|
{0x3802, 0x00},
|
|
{0x3803, 0xa0},
|
|
{0x3804, 0x05},
|
|
{0x3805, 0x0f},
|
|
{0x3806, 0x03},
|
|
{0x3807, 0x2f},
|
|
{0x3808, 0x02}, // X OUTPUT SIZE
|
|
{0x3809, 0x80}, // X OUTPUT SIZE
|
|
{0x380a, 0x01}, // Y OUTPUT SIZE
|
|
{0x380b, 0xe0}, // Y OUTPUT SIZE
|
|
{0x380c, 0x02},
|
|
{0x380d, 0xd8},
|
|
{0x380e, 0x03},
|
|
{0x380f, 0x8e},
|
|
{0x3810, 0x00},
|
|
{0x3811, 0x08},
|
|
{0x3812, 0x00},
|
|
{0x3813, 0x08},
|
|
{0x3814, 0x11},
|
|
{0x3815, 0x11},
|
|
{0x3820, 0x40},
|
|
{0x3821, 0x00},
|
|
{0x382b, 0x3a},
|
|
{0x382c, 0x06},
|
|
{0x382d, 0xc2},
|
|
{0x389d, 0x00},
|
|
{0x3881, 0x42},
|
|
{0x3882, 0x02},
|
|
{0x3883, 0x12},
|
|
{0x3885, 0x07},
|
|
{0x38a8, 0x02},
|
|
{0x38a9, 0x80},
|
|
{0x38b1, 0x03},
|
|
{0x38b3, 0x07},
|
|
{0x38c4, 0x00},
|
|
{0x38c5, 0xc0},
|
|
{0x38c6, 0x04},
|
|
{0x38c7, 0x80},
|
|
{0x3920, 0xff},
|
|
{0x4003, 0x40},
|
|
{0x4008, 0x04},
|
|
{0x4009, 0x0b},
|
|
{0x400c, 0x01},
|
|
{0x400d, 0x07},
|
|
{0x4010, 0xf0},
|
|
{0x4011, 0x3b},
|
|
{0x4043, 0x40},
|
|
{0x4307, 0x30},
|
|
{0x4317, 0x01},
|
|
{0x4501, 0x00},
|
|
{0x4507, 0x00},
|
|
{0x4509, 0x00},
|
|
{0x450a, 0x08},
|
|
{0x4601, 0x04},
|
|
{0x470f, 0xe0},
|
|
{0x4f07, 0x00},
|
|
{0x4800, 0x00},
|
|
{0x5000, 0x9f},
|
|
{0x5001, 0x00},
|
|
{0x5e00, 0x00},
|
|
{0x5d00, 0x0b},
|
|
{0x5d01, 0x02},
|
|
{0x4f00, 0x0c},
|
|
{0x4f10, 0x00},
|
|
{0x4f11, 0x88},
|
|
{0x4f12, 0x0f},
|
|
{0x4f13, 0xc4},
|
|
|
|
{0x3501, 0x27},
|
|
{0x3502, 0x50},
|
|
|
|
{0x0100, 0x01},
|
|
{REG_NULL, 0x00},
|
|
};
|
|
|
|
static const struct reg16_info ov9281_640x400_regs[] = {
|
|
{0x0103, 0x01},
|
|
{0x0302, 0x24},
|
|
{0x030d, 0x48},
|
|
{0x030e, 0x06},
|
|
{0x3001, 0x22},
|
|
{0x3004, 0x01},
|
|
{0x3005, 0xff},
|
|
{0x3006, 0xe2},
|
|
{0x3011, 0x0a},
|
|
{0x3013, 0x18},
|
|
{0x3022, 0x07},
|
|
{0x3039, 0x2e},
|
|
{0x303a, 0xf0},
|
|
{0x3500, 0x00},
|
|
{0x3501, 0x01},
|
|
{0x3502, 0xf4},
|
|
{0x3503, 0x00},
|
|
{0x3508, 0x07},
|
|
{0x3509, 0x80},
|
|
{0x3610, 0x80},
|
|
{0x3611, 0xa0},
|
|
{0x3620, 0x78},
|
|
{0x3632, 0x56},
|
|
{0x3633, 0x50},
|
|
{0x3662, 0x05},
|
|
{0x3666, 0x5a},
|
|
{0x366f, 0x7e},
|
|
{0x3712, 0x80},
|
|
{0x372d, 0x22},
|
|
{0x3731, 0x80},
|
|
{0x3732, 0x30},
|
|
{0x3778, 0x10},
|
|
{0x377d, 0x22},
|
|
{0x3800, 0x00},
|
|
{0x3801, 0x00},
|
|
{0x3802, 0x00},
|
|
{0x3803, 0x00},
|
|
{0x3804, 0x05},
|
|
{0x3805, 0x0f},
|
|
{0x3806, 0x03},
|
|
{0x3807, 0x2f},
|
|
{0x3808, 0x02}, // 640
|
|
{0x3809, 0x80},
|
|
{0x380a, 0x01}, // 400
|
|
{0x380b, 0x90},
|
|
{0x380c, 0x02},
|
|
{0x380d, 0xd8},
|
|
{0x380e, 0x02},
|
|
{0x380f, 0x08},
|
|
{0x3810, 0x00},
|
|
{0x3811, 0x04},
|
|
{0x3812, 0x00},
|
|
{0x3813, 0x04},
|
|
{0x3814, 0x31},
|
|
{0x3815, 0x22},
|
|
{0x3820, 0x60},
|
|
{0x3821, 0x01},
|
|
{0x3881, 0x42},
|
|
{0x4043, 0x40},
|
|
{0x4307, 0x30},
|
|
{0x4317, 0x01}, // DVP
|
|
{0x4501, 0x00},
|
|
{0x4507, 0x03},
|
|
{0x4509, 0x80},
|
|
{0x4800, 0x00},
|
|
{0x5000, 0x9f},
|
|
{0x5001, 0x00},
|
|
{0x5e00, 0x00},
|
|
{0x0100, 0x01},
|
|
{REG_NULL, 0x00},
|
|
};
|
|
|
|
static const struct reg16_info op_8bit[] = {
|
|
{0x030d, 0x60},
|
|
{0x3662, 0x07},
|
|
{REG_NULL, 0x00},
|
|
};
|
|
|
|
static const struct ov9281_mode supported_modes[] = {
|
|
{
|
|
.width = 1280,
|
|
.height = 800,
|
|
.exp_def = 0x0320,
|
|
.hts_def = 0x05b0, /* 0x2d8*2 */
|
|
.vts_def = 0x038e,
|
|
.crop = {
|
|
.x = 0,
|
|
.y = 0,
|
|
.width = 1280,
|
|
.height = 800
|
|
},
|
|
.reg_list = ov9281_1280x800_regs,
|
|
},
|
|
{
|
|
.width = 1280,
|
|
.height = 720,
|
|
.exp_def = 0x0320,
|
|
.hts_def = 0x05b0, // 1456
|
|
.vts_def = 910,
|
|
.crop = {
|
|
.x = 0,
|
|
.y = 40,
|
|
.width = 1280,
|
|
.height = 720
|
|
},
|
|
.reg_list = ov9281_1280x720_regs,
|
|
},
|
|
{
|
|
.width = 640,
|
|
.height = 480,
|
|
.exp_def = 0x0320,
|
|
.hts_def = 0x05b0,
|
|
.vts_def = 422,
|
|
.crop = {
|
|
.x = 0,
|
|
.y = 0,
|
|
.width = 1280,
|
|
.height = 800
|
|
},
|
|
.reg_list = ov9281_640x480_regs,
|
|
},
|
|
{
|
|
.width = 640,
|
|
.height = 400,
|
|
.exp_def = 0x0320,
|
|
.hts_def = 0x05b0,
|
|
.vts_def = 422,
|
|
.crop = {
|
|
.x = 0,
|
|
.y = 0,
|
|
.width = 1280,
|
|
.height = 800
|
|
},
|
|
.reg_list = ov9281_640x400_regs,
|
|
},
|
|
};
|
|
|
|
/* Write one byte at a time */
|
|
static int ov9281_write_reg(struct ov9281_dev *sensor, u16 reg, u8 val)
|
|
{
|
|
if (rt_i2c_write_reg16(sensor->iic, OV9281_SLAVE_ID, reg, &val, 1) != 1) {
|
|
LOG_E("%s: error: reg = 0x%x, val = 0x%x", __func__, reg, val);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ov9281_write_array(struct ov9281_dev *sensor,
|
|
const struct reg16_info *regs)
|
|
{
|
|
u32 i;
|
|
int ret = 0;
|
|
|
|
for (i = 0; ret == 0 && regs[i].reg != REG_NULL; i++)
|
|
ret = ov9281_write_reg(sensor, regs[i].reg, regs[i].val);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Read one byte at a time */
|
|
static int ov9281_read_reg(struct ov9281_dev *sensor, u16 reg, u8 *val)
|
|
{
|
|
if (rt_i2c_read_reg16(sensor->iic, OV9281_SLAVE_ID, reg, val, 1) != 1) {
|
|
LOG_E("%s: error: reg = 0x%x, val = 0x%x", __func__, reg, *val);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static u16 ov9281_read_u16(struct ov9281_dev *sensor, u16 reg_h, u16 reg_l)
|
|
{
|
|
u8 val_h = 0, val_l = 0;
|
|
|
|
if (ov9281_read_reg(sensor, reg_h, &val_h) ||
|
|
ov9281_read_reg(sensor, reg_l, &val_l))
|
|
return 0;
|
|
|
|
return val_h << 8 | val_l;
|
|
}
|
|
|
|
#ifdef OV9281_TEST_MODE
|
|
static void ov9281_test_mode(struct ov9281_dev *sensor)
|
|
{
|
|
ov9281_write_reg(sensor, OV9281_REG_TEST_PATTERN, 0x84);
|
|
}
|
|
#endif
|
|
|
|
static int ov9281_set_fps(struct ov9281_dev *sensor, u32 fps)
|
|
{
|
|
u16 cur = 0, val = 0;
|
|
u32 base[] = {0x222, 0x222, 0x222, 0x186};
|
|
|
|
if (fps > 210)
|
|
fps = 210;
|
|
if (fps < 10)
|
|
fps = 10;
|
|
|
|
cur = ov9281_read_u16(sensor, 0x380e, 0x380f);
|
|
if (!cur) {
|
|
LOG_E("Failed to read FPS\n");
|
|
return -1;
|
|
}
|
|
|
|
if (!base[OV9281_DEFAULT_MODE])
|
|
return -1;
|
|
|
|
val = base[OV9281_DEFAULT_MODE] * 120 / fps;
|
|
|
|
LOG_I("Set FPS %d[0x%03x] -> %d[0x%03x]\n",
|
|
120 * base[OV9281_DEFAULT_MODE] / cur, cur,
|
|
120 * base[OV9281_DEFAULT_MODE] / val, val);
|
|
|
|
if (cur == val)
|
|
return 0;
|
|
|
|
if (ov9281_write_reg(sensor, 0x380e, val >> 8) ||
|
|
ov9281_write_reg(sensor, 0x380f, val & 0xFF))
|
|
return -1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static int ov9281_set_aec(struct ov9281_dev *sensor, u32 percent)
|
|
{
|
|
u16 frame_len = 0, cur = 0, val = 0;
|
|
u8 val_h = 0, val_l = 0;
|
|
|
|
frame_len = ov9281_read_u16(sensor, 0x380e, 0x380f);
|
|
if (!frame_len) {
|
|
LOG_E("Failed to get frame length\n");
|
|
return -1;
|
|
}
|
|
if (frame_len - 25 > 0xFFF) {
|
|
LOG_W("Frame length %d is too large\n", frame_len);
|
|
frame_len = 0xFFF + 25;
|
|
}
|
|
|
|
if (ov9281_read_reg(sensor, 0x3501, &val_h)
|
|
|| ov9281_read_reg(sensor, 0x3502, &val_l)) {
|
|
LOG_E("Failed to get current AEC\n");
|
|
return -1;
|
|
}
|
|
cur = (val_h << 4) | (val_l >> 4); // {16'h3501,16'h3502[7:4]}
|
|
|
|
val = PERCENT_TO_INT(1, frame_len - 25, percent);
|
|
LOG_I("Set AEC %d[0x%03x] -> %d[0x%03x] (Frame length %d[0x%03x])\n",
|
|
cur, cur, val, val, frame_len, frame_len);
|
|
|
|
if (cur == val)
|
|
return 0;
|
|
|
|
if (ov9281_write_reg(sensor, 0x3501, (val >> 4) & 0xFF) ||
|
|
ov9281_write_reg(sensor, 0x3502, (val & 0xF) << 4))
|
|
return -1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static int ov9281_set_agc(struct ov9281_dev *sensor, u32 percent)
|
|
{
|
|
u8 mode = 0;
|
|
u8 cur = 0, gain = PERCENT_TO_INT(1, 0xFF, percent);
|
|
|
|
if (ov9281_read_reg(sensor, 0x3503, &mode))
|
|
return -1;
|
|
|
|
mode &= ~0x4;
|
|
if (ov9281_write_reg(sensor, 0x3503, mode))
|
|
return -1;
|
|
|
|
if (ov9281_read_reg(sensor, 0x3509, &cur))
|
|
return -1;
|
|
|
|
LOG_I("Set AGC %d[0x%02x] -> %d[0x%02x]\n", cur, cur, gain, gain);
|
|
|
|
if (ov9281_write_reg(sensor, 0x3509, gain))
|
|
return -1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static int ov9281_enable_flip(struct ov9281_dev *sensor, u16 reg,
|
|
bool enable, char *name)
|
|
{
|
|
u8 cur = 0, mask = 0x4, shift = 2;
|
|
|
|
if (ov9281_read_reg(sensor, reg, &cur)) {
|
|
LOG_E("Failed to get current flip\n");
|
|
return -1;
|
|
}
|
|
|
|
LOG_I("Set %s flip %d -> %d\n", name, (cur & mask) >> shift, enable);
|
|
if ((cur & mask) >> shift == (u8)enable)
|
|
return 0;
|
|
|
|
if (enable)
|
|
return ov9281_write_reg(sensor, reg, cur | mask);
|
|
else
|
|
return ov9281_write_reg(sensor, reg, cur & ~mask);
|
|
}
|
|
|
|
static int ov9281_enable_h_flip(struct ov9281_dev *sensor, bool enable)
|
|
{
|
|
return ov9281_enable_flip(sensor, 0x3821, enable, "H");
|
|
}
|
|
|
|
static int ov9281_enable_v_flip(struct ov9281_dev *sensor, bool enable)
|
|
{
|
|
return ov9281_enable_flip(sensor, 0x3820, enable, "V");
|
|
}
|
|
|
|
static int ov9281_start_stream(struct ov9281_dev *sensor)
|
|
{
|
|
int ret;
|
|
|
|
#if 0
|
|
ret = ov9281_write_array(sensor, ov9281_common_regs);
|
|
if (ret)
|
|
return ret;
|
|
#endif
|
|
ret = ov9281_write_array(sensor, sensor->cur_mode->reg_list);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* Only support 8bit */
|
|
ret = ov9281_write_array(sensor, op_8bit);
|
|
if (ret)
|
|
return ret;
|
|
|
|
#ifdef OV9281_TEST_MODE
|
|
ov9281_test_mode(sensor);
|
|
#endif
|
|
ov9281_set_aec(sensor, 80);
|
|
ov9281_set_agc(sensor, 100);
|
|
|
|
return ov9281_write_reg(sensor, OV9281_REG_CTRL_MODE,
|
|
OV9281_MODE_STREAMING);
|
|
}
|
|
|
|
static int ov9281_stop_stream(struct ov9281_dev *sensor)
|
|
{
|
|
return ov9281_write_reg(sensor, OV9281_REG_CTRL_MODE,
|
|
OV9281_MODE_SW_STANDBY);
|
|
}
|
|
|
|
static int ov9281_s_stream(struct ov9281_dev *sensor, int on)
|
|
{
|
|
int ret = 0;
|
|
|
|
on = !!on;
|
|
if (on == sensor->streaming)
|
|
goto unlock_and_return;
|
|
|
|
if (on) {
|
|
ret = ov9281_start_stream(sensor);
|
|
if (ret) {
|
|
LOG_E("start stream failed while write regs\n");
|
|
goto unlock_and_return;
|
|
}
|
|
} else {
|
|
ov9281_stop_stream(sensor);
|
|
}
|
|
|
|
sensor->streaming = on;
|
|
|
|
unlock_and_return:
|
|
return ret;
|
|
}
|
|
|
|
/* Calculate the delay in us by clock rate and clock cycles */
|
|
static inline u32 ov9281_cal_delay(u32 cycles)
|
|
{
|
|
return DIV_ROUND_UP(cycles, OV9281_XVCLK_FREQ / 1000 / 1000);
|
|
}
|
|
|
|
static int ov9281_power_on(struct ov9281_dev *sensor)
|
|
{
|
|
u32 delay_us;
|
|
|
|
camera_pin_set_low(sensor->reset_gpio);
|
|
aicos_msleep(10);
|
|
camera_pin_set_high(sensor->reset_gpio);
|
|
|
|
camera_pin_set_high(sensor->pwdn_gpio);
|
|
|
|
/* 8192 cycles prior to first SCCB transaction */
|
|
delay_us = ov9281_cal_delay(8192);
|
|
aicos_udelay(delay_us * 2);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void ov9281_power_off(struct ov9281_dev *sensor)
|
|
{
|
|
camera_pin_set_low(sensor->pwdn_gpio);
|
|
camera_pin_set_low(sensor->reset_gpio);
|
|
}
|
|
|
|
static bool ov9281_is_open(struct ov9281_dev *sensor)
|
|
{
|
|
return sensor->power_on;
|
|
}
|
|
|
|
static int ov9281_s_power(struct ov9281_dev *sensor, int on)
|
|
{
|
|
/* If the power state is not modified - no work to do. */
|
|
if (sensor->power_on == !!on)
|
|
return 0;
|
|
|
|
if (on) {
|
|
ov9281_power_on(sensor);
|
|
LOG_I("Power on");
|
|
sensor->power_on = true;
|
|
} else {
|
|
ov9281_power_off(sensor);
|
|
LOG_I("Power off");
|
|
sensor->power_on = false;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ov9281_check_sensor_id(struct ov9281_dev *sensor)
|
|
{
|
|
u8 id_h = 0, id_l = 0;
|
|
|
|
if (ov9281_read_reg(sensor, OV9281_REG_CHIP_ID_HIGH, &id_h)
|
|
|| ov9281_read_reg(sensor, OV9281_REG_CHIP_ID_LOW, &id_l))
|
|
return -1;
|
|
|
|
if ((id_h << 8 | id_l) != OV9281_CHIP_ID) {
|
|
LOG_E("Unexpected sensor id: %02x %02x\n", id_h, id_l);
|
|
return -ENODEV;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static rt_err_t ov9281_init(rt_device_t dev)
|
|
{
|
|
struct ov9281_dev *sensor = to_ov9281(dev);
|
|
|
|
sensor->iic = camera_i2c_get();
|
|
if (!sensor->iic)
|
|
return -RT_EINVAL;
|
|
|
|
sensor->reset_gpio = camera_rst_pin_get();
|
|
sensor->pwdn_gpio = camera_pwdn_pin_get();
|
|
if (!sensor->reset_gpio || !sensor->pwdn_gpio)
|
|
return -RT_EINVAL;
|
|
|
|
sensor->cur_mode = &supported_modes[OV9281_DEFAULT_MODE];
|
|
sensor->fmt.code = OV9281_DEFAULT_CODE;
|
|
sensor->fmt.width = sensor->cur_mode->width;
|
|
sensor->fmt.height = sensor->cur_mode->height;
|
|
sensor->fmt.bus_type = OV9281_DEFAULT_BUS_TYPE;
|
|
sensor->fmt.flags = MEDIA_SIGNAL_HSYNC_ACTIVE_HIGH |
|
|
MEDIA_SIGNAL_VSYNC_ACTIVE_HIGH |
|
|
MEDIA_SIGNAL_PCLK_SAMPLE_FALLING;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static rt_err_t ov9281_open(rt_device_t dev, rt_uint16_t oflag)
|
|
{
|
|
struct ov9281_dev *sensor = to_ov9281(dev);
|
|
|
|
if (ov9281_is_open(sensor))
|
|
return RT_EOK;
|
|
|
|
ov9281_s_power(sensor, 1);
|
|
|
|
if (ov9281_check_sensor_id(sensor)) {
|
|
ov9281_power_off(sensor);
|
|
return -RT_ERROR;
|
|
}
|
|
LOG_I("OV9281 Inited");
|
|
return RT_EOK;
|
|
}
|
|
|
|
static rt_err_t ov9281_close(rt_device_t dev)
|
|
{
|
|
struct ov9281_dev *sensor = (struct ov9281_dev *)dev;
|
|
|
|
if (!ov9281_is_open(sensor))
|
|
return -RT_ERROR;
|
|
|
|
ov9281_s_power(sensor, 0);
|
|
LOG_D("OV9281 Close");
|
|
return RT_EOK;
|
|
}
|
|
|
|
static int ov9281_get_fmt(struct ov9281_dev *sensor, struct mpp_video_fmt *cfg)
|
|
{
|
|
|
|
cfg->code = sensor->fmt.code;
|
|
cfg->width = sensor->fmt.width;
|
|
cfg->height = sensor->fmt.height;
|
|
cfg->flags = sensor->fmt.flags;
|
|
cfg->bus_type = sensor->fmt.bus_type;
|
|
return RT_EOK;
|
|
}
|
|
|
|
static int ov9281_start(struct ov9281_dev *sensor)
|
|
{
|
|
return ov9281_s_stream(sensor, 1);
|
|
}
|
|
|
|
static int ov9281_stop(struct ov9281_dev *sensor)
|
|
{
|
|
return ov9281_s_stream(sensor, 0);
|
|
}
|
|
|
|
static int ov9281_pause(rt_device_t dev)
|
|
{
|
|
struct ov9281_dev *sensor = (struct ov9281_dev *)dev;
|
|
|
|
return ov9281_write_reg(sensor, OV9281_REG_CTRL_MODE, OV9281_MODE_SW_STANDBY);
|
|
}
|
|
|
|
static int ov9281_resume(rt_device_t dev)
|
|
{
|
|
struct ov9281_dev *sensor = (struct ov9281_dev *)dev;
|
|
|
|
return ov9281_write_reg(sensor, OV9281_REG_CTRL_MODE, OV9281_MODE_STREAMING);
|
|
}
|
|
|
|
static rt_err_t ov9281_control(rt_device_t dev, int cmd, void *args)
|
|
{
|
|
struct ov9281_dev *sensor = (struct ov9281_dev *)dev;
|
|
|
|
switch (cmd) {
|
|
case CAMERA_CMD_START:
|
|
return ov9281_start(sensor);
|
|
case CAMERA_CMD_STOP:
|
|
return ov9281_stop(sensor);
|
|
case CAMERA_CMD_PAUSE:
|
|
return ov9281_pause(dev);
|
|
case CAMERA_CMD_RESUME:
|
|
return ov9281_resume(dev);
|
|
case CAMERA_CMD_GET_FMT:
|
|
return ov9281_get_fmt(sensor, (struct mpp_video_fmt *)args);
|
|
case CAMERA_CMD_SET_FPS:
|
|
return ov9281_set_fps(sensor, *(u32 *)args);
|
|
case CAMERA_CMD_SET_AEC_VAL:
|
|
return ov9281_set_aec(sensor, *(u32 *)args);
|
|
case CAMERA_CMD_SET_AUTOGAIN:
|
|
return ov9281_set_agc(sensor, *(u32 *)args);
|
|
case CAMERA_CMD_SET_H_FLIP:
|
|
return ov9281_enable_h_flip(sensor, *(bool *)args);
|
|
case CAMERA_CMD_SET_V_FLIP:
|
|
return ov9281_enable_v_flip(sensor, *(bool *)args);
|
|
default:
|
|
LOG_I("Unsupported cmd: 0x%x", cmd);
|
|
return -RT_EINVAL;
|
|
}
|
|
return RT_EOK;
|
|
}
|
|
|
|
#ifdef RT_USING_DEVICE_OPS
|
|
static const struct rt_device_ops ov9281_ops =
|
|
{
|
|
.init = ov9281_init,
|
|
.open = ov9281_open,
|
|
.close = ov9281_close,
|
|
.control = ov9281_control,
|
|
};
|
|
#endif
|
|
|
|
int rt_hw_ov9281_init(void)
|
|
{
|
|
#ifdef RT_USING_DEVICE_OPS
|
|
g_ov9281_dev.dev.ops = &ov9281_ops;
|
|
#else
|
|
g_ov9281_dev.dev.init = ov9281_init;
|
|
g_ov9281_dev.dev.open = ov9281_open;
|
|
g_ov9281_dev.dev.close = ov9281_close;
|
|
g_ov9281_dev.dev.control = ov9281_control;
|
|
#endif
|
|
g_ov9281_dev.dev.type = RT_Device_Class_CAMERA;
|
|
|
|
rt_device_register(&g_ov9281_dev.dev, CAMERA_DEV_NAME, 0);
|
|
return 0;
|
|
}
|
|
INIT_DEVICE_EXPORT(rt_hw_ov9281_init);
|