Skip to content

Instantly share code, notes, and snippets.

@RobGries
Last active February 8, 2018 21:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save RobGries/b7beb724574f6da2ff660604980f6311 to your computer and use it in GitHub Desktop.
Save RobGries/b7beb724574f6da2ff660604980f6311 to your computer and use it in GitHub Desktop.
OV5640-Driver-For-17.09
/*
* Driver for the OV5640 camera sensor.
*
* Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
* Copyright (C) 2015 By Tech Design S.L. All Rights Reserved.
* Copyright (C) 2012-2013 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright (C) 2016-2017 D3 Engineering, All Rights Reserved.
*
* Based on:
* - the OV5645 driver from QC msm-3.10 kernel on codeaurora.org:
* https://us.codeaurora.org/cgit/quic/la/kernel/msm-3.10/tree/drivers/
* media/platform/msm/camera_v2/sensor/ov5645.c?h=LA.BR.1.2.4_rb1.41
* - the OV5640 driver posted on linux-media:
* https://www.mail-archive.com/linux-media%40vger.kernel.org/msg92671.html
* - the ov5640yuv_Sensor.c file by Jackie Su provided in sample drivers from
* Leopard Imaging
* - https://raw.githubusercontent.com/allwinner-zh/linux-3.4-sunxi/
* 6964d467510849e3e262518cb87bff7ef92e01f5/drivers/media/video/sunxi-vfe/device/ov5640.c
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_graph.h>
#include <linux/regulator/consumer.h>
#include <linux/signal.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/version.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-of.h>
#include <media/v4l2-subdev.h>
static DEFINE_MUTEX(ov5640_lock);
/* In the 17.09 kernel we do not need this any more
*
* HACKs here!
#include <../drivers/media/platform/qcom/cci/msm_cci.h>
#ifdef dev_dbg
#undef dev_dbg
#define dev_dbg dev_err
#endif
*/
#define OV5640_VOLTAGE_ANALOG 2800000
#define OV5640_VOLTAGE_DIGITAL_CORE 1500000
#define OV5640_VOLTAGE_DIGITAL_IO 1800000
#define OV5640_SYSTEM_CTRL0 0x3008
#define OV5640_SYSTEM_CTRL0_START 0x02
#define OV5640_SYSTEM_CTRL0_STOP 0x42
#define OV5640_CHIP_ID_HIGH 0x300a
#define OV5640_CHIP_ID_HIGH_BYTE 0x56
#define OV5640_CHIP_ID_LOW 0x300b
#define OV5640_CHIP_ID_LOW_BYTE 0x40
#define OV5640_AWB_MANUAL_CONTROL 0x3406
#define OV5640_AWB_MANUAL_ENABLE BIT(0)
#define OV5640_AEC_PK_MANUAL 0x3503
#define OV5640_AEC_MANUAL_ENABLE BIT(0)
#define OV5640_AGC_MANUAL_ENABLE BIT(1)
#define OV5640_TIMING_TC_REG20 0x3820
#define OV5640_SENSOR_VFLIP BIT(1)
#define OV5640_ISP_VFLIP BIT(2)
#define OV5640_TIMING_TC_REG21 0x3821
#define OV5640_ISP_MIRROR BIT(2)
#define OV5640_SENSOR_MIRROR BIT(1)
#define OV5640_PRE_ISP_TEST_SETTING_1 0x503d
#define OV5640_TEST_PATTERN_MASK 0x3
#define OV5640_SET_TEST_PATTERN(x) ((x) & OV5640_TEST_PATTERN_MASK)
#define OV5640_TEST_PATTERN_ENABLE BIT(7)
#define OV5640_SDE_SAT_U 0x5583
#define OV5640_SDE_SAT_V 0x5584
#define FOCUS_NOT_INITIALIZED (999)
struct reg_value {
u16 reg;
u8 val;
};
struct ov5640_mode_info {
u32 width;
u32 height;
const struct reg_value *data;
u32 data_size;
u32 pixel_clock;
u32 link_freq;
};
struct ov5640 {
struct i2c_client *i2c_client;
struct device *dev;
struct v4l2_subdev sd;
struct media_pad pad;
struct v4l2_of_endpoint ep;
struct v4l2_mbus_framefmt fmt;
struct v4l2_rect crop;
struct clk *xclk;
struct regulator *io_regulator;
struct regulator *core_regulator;
struct regulator *analog_regulator;
const struct ov5640_mode_info *current_mode;
struct v4l2_ctrl_handler ctrls;
/* Cached register values */
u8 aec_pk_manual;
u8 timing_tc_reg20;
u8 timing_tc_reg21;
struct mutex power_lock; /* lock to protect power state */
int power_count;
struct gpio_desc *enable_gpio;
struct gpio_desc *rst_gpio;
bool hflip; /* Initially set horizontal flip */
bool vflip; /* Initially set vertical flip */
bool use_cci; /* indicate that CCI driver hack should be used */
//changes for 17.09
//struct v4l2_subdev *cci;
s32 focus_state;
struct v4l2_ctrl *af_status;
bool autofocus; /* indicate that camera should autofocus */
struct v4l2_ctrl *pixel_clock;
struct v4l2_ctrl *link_freq;
};
//=======================================================================
// micro code for AF function
static u8 AD5820_Config[] =
{
0x02, 0x0f, 0xd6, 0x02, 0x0a, 0x39, 0xc2, 0x01, 0x22, 0x22, 0x00, 0x02, 0x0f, 0xb2, 0xe5, 0x1f, //0x8000,
0x70, 0x72, 0xf5, 0x1e, 0xd2, 0x35, 0xff, 0xef, 0x25, 0xe0, 0x24, 0x4e, 0xf8, 0xe4, 0xf6, 0x08, //0x8010,
0xf6, 0x0f, 0xbf, 0x34, 0xf2, 0x90, 0x0e, 0x93, 0xe4, 0x93, 0xff, 0xe5, 0x4b, 0xc3, 0x9f, 0x50, //0x8020,
0x04, 0x7f, 0x05, 0x80, 0x02, 0x7f, 0xfb, 0x78, 0xbd, 0xa6, 0x07, 0x12, 0x0f, 0x04, 0x40, 0x04, //0x8030,
0x7f, 0x03, 0x80, 0x02, 0x7f, 0x30, 0x78, 0xbc, 0xa6, 0x07, 0xe6, 0x18, 0xf6, 0x08, 0xe6, 0x78, //0x8040,
0xb9, 0xf6, 0x78, 0xbc, 0xe6, 0x78, 0xba, 0xf6, 0x78, 0xbf, 0x76, 0x33, 0xe4, 0x08, 0xf6, 0x78, //0x8050,
0xb8, 0x76, 0x01, 0x75, 0x4a, 0x02, 0x78, 0xb6, 0xf6, 0x08, 0xf6, 0x74, 0xff, 0x78, 0xc1, 0xf6, //0x8060,
0x08, 0xf6, 0x75, 0x1f, 0x01, 0x78, 0xbc, 0xe6, 0x75, 0xf0, 0x05, 0xa4, 0xf5, 0x4b, 0x12, 0x0a, //0x8070,
0xff, 0xc2, 0x37, 0x22, 0x78, 0xb8, 0xe6, 0xd3, 0x94, 0x00, 0x40, 0x02, 0x16, 0x22, 0xe5, 0x1f, //0x8080,
0xb4, 0x05, 0x23, 0xe4, 0xf5, 0x1f, 0xc2, 0x01, 0x78, 0xb6, 0xe6, 0xfe, 0x08, 0xe6, 0xff, 0x78, //0x8090,
0x4e, 0xa6, 0x06, 0x08, 0xa6, 0x07, 0xa2, 0x37, 0xe4, 0x33, 0xf5, 0x3c, 0x90, 0x30, 0x28, 0xf0, //0x80a0,
0x75, 0x1e, 0x10, 0xd2, 0x35, 0x22, 0xe5, 0x4b, 0x75, 0xf0, 0x05, 0x84, 0x78, 0xbc, 0xf6, 0x90, //0x80b0,
0x0e, 0x8c, 0xe4, 0x93, 0xff, 0x25, 0xe0, 0x24, 0x0a, 0xf8, 0xe6, 0xfc, 0x08, 0xe6, 0xfd, 0x78, //0x80c0,
0xbc, 0xe6, 0x25, 0xe0, 0x24, 0x4e, 0xf8, 0xa6, 0x04, 0x08, 0xa6, 0x05, 0xef, 0x12, 0x0f, 0x0b, //0x80d0,
0xd3, 0x78, 0xb7, 0x96, 0xee, 0x18, 0x96, 0x40, 0x0d, 0x78, 0xbc, 0xe6, 0x78, 0xb9, 0xf6, 0x78, //0x80e0,
0xb6, 0xa6, 0x06, 0x08, 0xa6, 0x07, 0x90, 0x0e, 0x8c, 0xe4, 0x93, 0x12, 0x0f, 0x0b, 0xc3, 0x78, //0x80f0,
0xc2, 0x96, 0xee, 0x18, 0x96, 0x50, 0x0d, 0x78, 0xbc, 0xe6, 0x78, 0xba, 0xf6, 0x78, 0xc1, 0xa6, //0x8100,
0x06, 0x08, 0xa6, 0x07, 0x78, 0xb6, 0xe6, 0xfe, 0x08, 0xe6, 0xc3, 0x78, 0xc2, 0x96, 0xff, 0xee, //0x8110,
0x18, 0x96, 0x78, 0xc3, 0xf6, 0x08, 0xa6, 0x07, 0x90, 0x0e, 0x95, 0xe4, 0x18, 0x12, 0x0e, 0xe9, //0x8120,
0x40, 0x02, 0xd2, 0x37, 0x78, 0xbc, 0xe6, 0x08, 0x26, 0x08, 0xf6, 0xe5, 0x1f, 0x64, 0x01, 0x70, //0x8130,
0x4a, 0xe6, 0xc3, 0x78, 0xc0, 0x12, 0x0e, 0xdf, 0x40, 0x05, 0x12, 0x0e, 0xda, 0x40, 0x39, 0x12, //0x8140,
0x0f, 0x02, 0x40, 0x04, 0x7f, 0xfe, 0x80, 0x02, 0x7f, 0x02, 0x78, 0xbd, 0xa6, 0x07, 0x78, 0xb9, //0x8150,
0xe6, 0x24, 0x03, 0x78, 0xbf, 0xf6, 0x78, 0xb9, 0xe6, 0x24, 0xfd, 0x78, 0xc0, 0xf6, 0x12, 0x0f, //0x8160,
0x02, 0x40, 0x06, 0x78, 0xc0, 0xe6, 0xff, 0x80, 0x04, 0x78, 0xbf, 0xe6, 0xff, 0x78, 0xbe, 0xa6, //0x8170,
0x07, 0x75, 0x1f, 0x02, 0x78, 0xb8, 0x76, 0x01, 0x02, 0x02, 0x4a, 0xe5, 0x1f, 0x64, 0x02, 0x60, //0x8180,
0x03, 0x02, 0x02, 0x2a, 0x78, 0xbe, 0xe6, 0xff, 0xc3, 0x78, 0xc0, 0x12, 0x0e, 0xe0, 0x40, 0x08, //0x8190,
0x12, 0x0e, 0xda, 0x50, 0x03, 0x02, 0x02, 0x28, 0x12, 0x0f, 0x02, 0x40, 0x04, 0x7f, 0xff, 0x80, //0x81a0,
0x02, 0x7f, 0x01, 0x78, 0xbd, 0xa6, 0x07, 0x78, 0xb9, 0xe6, 0x04, 0x78, 0xbf, 0xf6, 0x78, 0xb9, //0x81b0,
0xe6, 0x14, 0x78, 0xc0, 0xf6, 0x18, 0x12, 0x0f, 0x04, 0x40, 0x04, 0xe6, 0xff, 0x80, 0x02, 0x7f, //0x81c0,
0x00, 0x78, 0xbf, 0xa6, 0x07, 0xd3, 0x08, 0xe6, 0x64, 0x80, 0x94, 0x80, 0x40, 0x04, 0xe6, 0xff, //0x81d0,
0x80, 0x02, 0x7f, 0x00, 0x78, 0xc0, 0xa6, 0x07, 0xc3, 0x18, 0xe6, 0x64, 0x80, 0x94, 0xb3, 0x50, //0x81e0,
0x04, 0xe6, 0xff, 0x80, 0x02, 0x7f, 0x33, 0x78, 0xbf, 0xa6, 0x07, 0xc3, 0x08, 0xe6, 0x64, 0x80, //0x81f0,
0x94, 0xb3, 0x50, 0x04, 0xe6, 0xff, 0x80, 0x02, 0x7f, 0x33, 0x78, 0xc0, 0xa6, 0x07, 0x12, 0x0f, //0x8200,
0x02, 0x40, 0x06, 0x78, 0xc0, 0xe6, 0xff, 0x80, 0x04, 0x78, 0xbf, 0xe6, 0xff, 0x78, 0xbe, 0xa6, //0x8210,
0x07, 0x75, 0x1f, 0x03, 0x78, 0xb8, 0x76, 0x01, 0x80, 0x20, 0xe5, 0x1f, 0x64, 0x03, 0x70, 0x26, //0x8220,
0x78, 0xbe, 0xe6, 0xff, 0xc3, 0x78, 0xc0, 0x12, 0x0e, 0xe0, 0x40, 0x05, 0x12, 0x0e, 0xda, 0x40, //0x8230,
0x09, 0x78, 0xb9, 0xe6, 0x78, 0xbe, 0xf6, 0x75, 0x1f, 0x04, 0x78, 0xbe, 0xe6, 0x75, 0xf0, 0x05, //0x8240,
0xa4, 0xf5, 0x4b, 0x02, 0x0a, 0xff, 0xe5, 0x1f, 0xb4, 0x04, 0x10, 0x90, 0x0e, 0x94, 0xe4, 0x78, //0x8250,
0xc3, 0x12, 0x0e, 0xe9, 0x40, 0x02, 0xd2, 0x37, 0x75, 0x1f, 0x05, 0x22, 0x30, 0x01, 0x03, 0x02, //0x8260,
0x04, 0xc0, 0x30, 0x02, 0x03, 0x02, 0x04, 0xc0, 0x90, 0x51, 0xa5, 0xe0, 0x78, 0x93, 0xf6, 0xa3, //0x8270,
0xe0, 0x08, 0xf6, 0xa3, 0xe0, 0x08, 0xf6, 0xe5, 0x1f, 0x70, 0x3c, 0x75, 0x1e, 0x20, 0xd2, 0x35, //0x8280,
0x12, 0x0c, 0x7a, 0x78, 0x7e, 0xa6, 0x06, 0x08, 0xa6, 0x07, 0x78, 0x8b, 0xa6, 0x09, 0x18, 0x76, //0x8290,
0x01, 0x12, 0x0c, 0x5b, 0x78, 0x4e, 0xa6, 0x06, 0x08, 0xa6, 0x07, 0x78, 0x8b, 0xe6, 0x78, 0x6e, //0x82a0,
0xf6, 0x75, 0x1f, 0x01, 0x78, 0x93, 0xe6, 0x78, 0x90, 0xf6, 0x78, 0x94, 0xe6, 0x78, 0x91, 0xf6, //0x82b0,
0x78, 0x95, 0xe6, 0x78, 0x92, 0xf6, 0x22, 0x79, 0x90, 0xe7, 0xd3, 0x78, 0x93, 0x96, 0x40, 0x05, //0x82c0,
0xe7, 0x96, 0xff, 0x80, 0x08, 0xc3, 0x79, 0x93, 0xe7, 0x78, 0x90, 0x96, 0xff, 0x78, 0x88, 0x76, //0x82d0,
0x00, 0x08, 0xa6, 0x07, 0x79, 0x91, 0xe7, 0xd3, 0x78, 0x94, 0x96, 0x40, 0x05, 0xe7, 0x96, 0xff, //0x82e0,
0x80, 0x08, 0xc3, 0x79, 0x94, 0xe7, 0x78, 0x91, 0x96, 0xff, 0x12, 0x0c, 0x8e, 0x79, 0x92, 0xe7, //0x82f0,
0xd3, 0x78, 0x95, 0x96, 0x40, 0x05, 0xe7, 0x96, 0xff, 0x80, 0x08, 0xc3, 0x79, 0x95, 0xe7, 0x78, //0x8300,
0x92, 0x96, 0xff, 0x12, 0x0c, 0x8e, 0x12, 0x0c, 0x5b, 0x78, 0x8a, 0xe6, 0x25, 0xe0, 0x24, 0x4e, //0x8310,
0xf8, 0xa6, 0x06, 0x08, 0xa6, 0x07, 0x78, 0x8a, 0xe6, 0x24, 0x6e, 0xf8, 0xa6, 0x09, 0x78, 0x8a, //0x8320,
0xe6, 0x24, 0x01, 0xff, 0xe4, 0x33, 0xfe, 0xd3, 0xef, 0x94, 0x0f, 0xee, 0x64, 0x80, 0x94, 0x80, //0x8330,
0x40, 0x04, 0x7f, 0x00, 0x80, 0x05, 0x78, 0x8a, 0xe6, 0x04, 0xff, 0x78, 0x8a, 0xa6, 0x07, 0xe5, //0x8340,
0x1f, 0xb4, 0x01, 0x0a, 0xe6, 0x60, 0x03, 0x02, 0x04, 0xc0, 0x75, 0x1f, 0x02, 0x22, 0x12, 0x0c, //0x8350,
0x7a, 0x78, 0x80, 0xa6, 0x06, 0x08, 0xa6, 0x07, 0x12, 0x0c, 0x7a, 0x78, 0x82, 0xa6, 0x06, 0x08, //0x8360,
0xa6, 0x07, 0x78, 0x6e, 0xe6, 0x78, 0x8c, 0xf6, 0x78, 0x6e, 0xe6, 0x78, 0x8d, 0xf6, 0x7f, 0x01, //0x8370,
0xef, 0x25, 0xe0, 0x24, 0x4f, 0xf9, 0xc3, 0x78, 0x81, 0xe6, 0x97, 0x18, 0xe6, 0x19, 0x97, 0x50, //0x8380,
0x0a, 0x12, 0x0c, 0x82, 0x78, 0x80, 0xa6, 0x04, 0x08, 0xa6, 0x05, 0x74, 0x6e, 0x2f, 0xf9, 0x78, //0x8390,
0x8c, 0xe6, 0xc3, 0x97, 0x50, 0x08, 0x74, 0x6e, 0x2f, 0xf8, 0xe6, 0x78, 0x8c, 0xf6, 0xef, 0x25, //0x83a0,
0xe0, 0x24, 0x4f, 0xf9, 0xd3, 0x78, 0x83, 0xe6, 0x97, 0x18, 0xe6, 0x19, 0x97, 0x40, 0x0a, 0x12, //0x83b0,
0x0c, 0x82, 0x78, 0x82, 0xa6, 0x04, 0x08, 0xa6, 0x05, 0x74, 0x6e, 0x2f, 0xf9, 0x78, 0x8d, 0xe6, //0x83c0,
0xd3, 0x97, 0x40, 0x08, 0x74, 0x6e, 0x2f, 0xf8, 0xe6, 0x78, 0x8d, 0xf6, 0x0f, 0xef, 0x64, 0x10, //0x83d0,
0x70, 0x9e, 0xc3, 0x79, 0x81, 0xe7, 0x78, 0x83, 0x96, 0xff, 0x19, 0xe7, 0x18, 0x96, 0x78, 0x84, //0x83e0,
0xf6, 0x08, 0xa6, 0x07, 0xc3, 0x79, 0x8c, 0xe7, 0x78, 0x8d, 0x96, 0x08, 0xf6, 0xd3, 0x79, 0x81, //0x83f0,
0xe7, 0x78, 0x7f, 0x96, 0x19, 0xe7, 0x18, 0x96, 0x40, 0x05, 0x09, 0xe7, 0x08, 0x80, 0x06, 0xc3, //0x8400,
0x79, 0x7f, 0xe7, 0x78, 0x81, 0x96, 0xff, 0x19, 0xe7, 0x18, 0x96, 0xfe, 0x78, 0x86, 0xa6, 0x06, //0x8410,
0x08, 0xa6, 0x07, 0x79, 0x8c, 0xe7, 0xd3, 0x78, 0x8b, 0x96, 0x40, 0x05, 0xe7, 0x96, 0xff, 0x80, //0x8420,
0x08, 0xc3, 0x79, 0x8b, 0xe7, 0x78, 0x8c, 0x96, 0xff, 0x78, 0x8f, 0xa6, 0x07, 0xe5, 0x1f, 0x64, //0x8430,
0x02, 0x70, 0x69, 0x90, 0x0e, 0x91, 0x93, 0xff, 0x18, 0xe6, 0xc3, 0x9f, 0x50, 0x72, 0x12, 0x0c, //0x8440,
0x4a, 0x12, 0x0c, 0x2f, 0x90, 0x0e, 0x8e, 0x12, 0x0c, 0x38, 0x78, 0x80, 0x12, 0x0c, 0x6b, 0x7b, //0x8450,
0x04, 0x12, 0x0c, 0x1d, 0xc3, 0x12, 0x06, 0x45, 0x50, 0x56, 0x90, 0x0e, 0x92, 0xe4, 0x93, 0xff, //0x8460,
0x78, 0x8f, 0xe6, 0x9f, 0x40, 0x02, 0x80, 0x11, 0x90, 0x0e, 0x90, 0xe4, 0x93, 0xff, 0xd3, 0x78, //0x8470,
0x89, 0xe6, 0x9f, 0x18, 0xe6, 0x94, 0x00, 0x40, 0x03, 0x75, 0x1f, 0x05, 0x12, 0x0c, 0x4a, 0x12, //0x8480,
0x0c, 0x2f, 0x90, 0x0e, 0x8f, 0x12, 0x0c, 0x38, 0x78, 0x7e, 0x12, 0x0c, 0x6b, 0x7b, 0x40, 0x12, //0x8490,
0x0c, 0x1d, 0xd3, 0x12, 0x06, 0x45, 0x40, 0x18, 0x75, 0x1f, 0x05, 0x22, 0xe5, 0x1f, 0xb4, 0x05, //0x84a0,
0x0f, 0xd2, 0x01, 0xc2, 0x02, 0xe4, 0xf5, 0x1f, 0xf5, 0x1e, 0xd2, 0x35, 0xd2, 0x33, 0xd2, 0x36, //0x84b0,
0x22, 0xef, 0x8d, 0xf0, 0xa4, 0xa8, 0xf0, 0xcf, 0x8c, 0xf0, 0xa4, 0x28, 0xce, 0x8d, 0xf0, 0xa4, //0x84c0,
0x2e, 0xfe, 0x22, 0xbc, 0x00, 0x0b, 0xbe, 0x00, 0x29, 0xef, 0x8d, 0xf0, 0x84, 0xff, 0xad, 0xf0, //0x84d0,
0x22, 0xe4, 0xcc, 0xf8, 0x75, 0xf0, 0x08, 0xef, 0x2f, 0xff, 0xee, 0x33, 0xfe, 0xec, 0x33, 0xfc, //0x84e0,
0xee, 0x9d, 0xec, 0x98, 0x40, 0x05, 0xfc, 0xee, 0x9d, 0xfe, 0x0f, 0xd5, 0xf0, 0xe9, 0xe4, 0xce, //0x84f0,
0xfd, 0x22, 0xed, 0xf8, 0xf5, 0xf0, 0xee, 0x84, 0x20, 0xd2, 0x1c, 0xfe, 0xad, 0xf0, 0x75, 0xf0, //0x8500,
0x08, 0xef, 0x2f, 0xff, 0xed, 0x33, 0xfd, 0x40, 0x07, 0x98, 0x50, 0x06, 0xd5, 0xf0, 0xf2, 0x22, //0x8510,
0xc3, 0x98, 0xfd, 0x0f, 0xd5, 0xf0, 0xea, 0x22, 0xe8, 0x8f, 0xf0, 0xa4, 0xcc, 0x8b, 0xf0, 0xa4, //0x8520,
0x2c, 0xfc, 0xe9, 0x8e, 0xf0, 0xa4, 0x2c, 0xfc, 0x8a, 0xf0, 0xed, 0xa4, 0x2c, 0xfc, 0xea, 0x8e, //0x8530,
0xf0, 0xa4, 0xcd, 0xa8, 0xf0, 0x8b, 0xf0, 0xa4, 0x2d, 0xcc, 0x38, 0x25, 0xf0, 0xfd, 0xe9, 0x8f, //0x8540,
0xf0, 0xa4, 0x2c, 0xcd, 0x35, 0xf0, 0xfc, 0xeb, 0x8e, 0xf0, 0xa4, 0xfe, 0xa9, 0xf0, 0xeb, 0x8f, //0x8550,
0xf0, 0xa4, 0xcf, 0xc5, 0xf0, 0x2e, 0xcd, 0x39, 0xfe, 0xe4, 0x3c, 0xfc, 0xea, 0xa4, 0x2d, 0xce, //0x8560,
0x35, 0xf0, 0xfd, 0xe4, 0x3c, 0xfc, 0x22, 0x75, 0xf0, 0x08, 0x75, 0x82, 0x00, 0xef, 0x2f, 0xff, //0x8570,
0xee, 0x33, 0xfe, 0xcd, 0x33, 0xcd, 0xcc, 0x33, 0xcc, 0xc5, 0x82, 0x33, 0xc5, 0x82, 0x9b, 0xed, //0x8580,
0x9a, 0xec, 0x99, 0xe5, 0x82, 0x98, 0x40, 0x0c, 0xf5, 0x82, 0xee, 0x9b, 0xfe, 0xed, 0x9a, 0xfd, //0x8590,
0xec, 0x99, 0xfc, 0x0f, 0xd5, 0xf0, 0xd6, 0xe4, 0xce, 0xfb, 0xe4, 0xcd, 0xfa, 0xe4, 0xcc, 0xf9, //0x85a0,
0xa8, 0x82, 0x22, 0xb8, 0x00, 0xc1, 0xb9, 0x00, 0x59, 0xba, 0x00, 0x2d, 0xec, 0x8b, 0xf0, 0x84, //0x85b0,
0xcf, 0xce, 0xcd, 0xfc, 0xe5, 0xf0, 0xcb, 0xf9, 0x78, 0x18, 0xef, 0x2f, 0xff, 0xee, 0x33, 0xfe, //0x85c0,
0xed, 0x33, 0xfd, 0xec, 0x33, 0xfc, 0xeb, 0x33, 0xfb, 0x10, 0xd7, 0x03, 0x99, 0x40, 0x04, 0xeb, //0x85d0,
0x99, 0xfb, 0x0f, 0xd8, 0xe5, 0xe4, 0xf9, 0xfa, 0x22, 0x78, 0x18, 0xef, 0x2f, 0xff, 0xee, 0x33, //0x85e0,
0xfe, 0xed, 0x33, 0xfd, 0xec, 0x33, 0xfc, 0xc9, 0x33, 0xc9, 0x10, 0xd7, 0x05, 0x9b, 0xe9, 0x9a, //0x85f0,
0x40, 0x07, 0xec, 0x9b, 0xfc, 0xe9, 0x9a, 0xf9, 0x0f, 0xd8, 0xe0, 0xe4, 0xc9, 0xfa, 0xe4, 0xcc, //0x8600,
0xfb, 0x22, 0x75, 0xf0, 0x10, 0xef, 0x2f, 0xff, 0xee, 0x33, 0xfe, 0xed, 0x33, 0xfd, 0xcc, 0x33, //0x8610,
0xcc, 0xc8, 0x33, 0xc8, 0x10, 0xd7, 0x07, 0x9b, 0xec, 0x9a, 0xe8, 0x99, 0x40, 0x0a, 0xed, 0x9b, //0x8620,
0xfd, 0xec, 0x9a, 0xfc, 0xe8, 0x99, 0xf8, 0x0f, 0xd5, 0xf0, 0xda, 0xe4, 0xcd, 0xfb, 0xe4, 0xcc, //0x8630,
0xfa, 0xe4, 0xc8, 0xf9, 0x22, 0xeb, 0x9f, 0xf5, 0xf0, 0xea, 0x9e, 0x42, 0xf0, 0xe9, 0x9d, 0x42, //0x8640,
0xf0, 0xe8, 0x9c, 0x45, 0xf0, 0x22, 0xe8, 0x60, 0x0f, 0xec, 0xc3, 0x13, 0xfc, 0xed, 0x13, 0xfd, //0x8650,
0xee, 0x13, 0xfe, 0xef, 0x13, 0xff, 0xd8, 0xf1, 0x22, 0xe8, 0x60, 0x0f, 0xef, 0xc3, 0x33, 0xff, //0x8660,
0xee, 0x33, 0xfe, 0xed, 0x33, 0xfd, 0xec, 0x33, 0xfc, 0xd8, 0xf1, 0x22, 0xe4, 0x93, 0xfc, 0x74, //0x8670,
0x01, 0x93, 0xfd, 0x74, 0x02, 0x93, 0xfe, 0x74, 0x03, 0x93, 0xff, 0x22, 0xe6, 0xfb, 0x08, 0xe6, //0x8680,
0xf9, 0x08, 0xe6, 0xfa, 0x08, 0xe6, 0xcb, 0xf8, 0x22, 0xec, 0xf6, 0x08, 0xed, 0xf6, 0x08, 0xee, //0x8690,
0xf6, 0x08, 0xef, 0xf6, 0x22, 0xa4, 0x25, 0x82, 0xf5, 0x82, 0xe5, 0xf0, 0x35, 0x83, 0xf5, 0x83, //0x86a0,
0x22, 0xd0, 0x83, 0xd0, 0x82, 0xf8, 0xe4, 0x93, 0x70, 0x12, 0x74, 0x01, 0x93, 0x70, 0x0d, 0xa3, //0x86b0,
0xa3, 0x93, 0xf8, 0x74, 0x01, 0x93, 0xf5, 0x82, 0x88, 0x83, 0xe4, 0x73, 0x74, 0x02, 0x93, 0x68, //0x86c0,
0x60, 0xef, 0xa3, 0xa3, 0xa3, 0x80, 0xdf, 0x90, 0x38, 0x04, 0x78, 0x52, 0x12, 0x0b, 0xfd, 0x90, //0x86d0,
0x38, 0x00, 0xe0, 0xfe, 0xa3, 0xe0, 0xfd, 0xed, 0xff, 0xc3, 0x12, 0x0b, 0x9e, 0x90, 0x38, 0x10, //0x86e0,
0x12, 0x0b, 0x92, 0x90, 0x38, 0x06, 0x78, 0x54, 0x12, 0x0b, 0xfd, 0x90, 0x38, 0x02, 0xe0, 0xfe, //0x86f0,
0xa3, 0xe0, 0xfd, 0xed, 0xff, 0xc3, 0x12, 0x0b, 0x9e, 0x90, 0x38, 0x12, 0x12, 0x0b, 0x92, 0xa3, //0x8700,
0xe0, 0xb4, 0x31, 0x07, 0x78, 0x52, 0x79, 0x52, 0x12, 0x0c, 0x13, 0x90, 0x38, 0x14, 0xe0, 0xb4, //0x8710,
0x71, 0x15, 0x78, 0x52, 0xe6, 0xfe, 0x08, 0xe6, 0x78, 0x02, 0xce, 0xc3, 0x13, 0xce, 0x13, 0xd8, //0x8720,
0xf9, 0x79, 0x53, 0xf7, 0xee, 0x19, 0xf7, 0x90, 0x38, 0x15, 0xe0, 0xb4, 0x31, 0x07, 0x78, 0x54, //0x8730,
0x79, 0x54, 0x12, 0x0c, 0x13, 0x90, 0x38, 0x15, 0xe0, 0xb4, 0x71, 0x15, 0x78, 0x54, 0xe6, 0xfe, //0x8740,
0x08, 0xe6, 0x78, 0x02, 0xce, 0xc3, 0x13, 0xce, 0x13, 0xd8, 0xf9, 0x79, 0x55, 0xf7, 0xee, 0x19, //0x8750,
0xf7, 0x79, 0x52, 0x12, 0x0b, 0xd9, 0x09, 0x12, 0x0b, 0xd9, 0xaf, 0x47, 0x12, 0x0b, 0xb2, 0xe5, //0x8760,
0x44, 0xfb, 0x7a, 0x00, 0xfd, 0x7c, 0x00, 0x12, 0x04, 0xd3, 0x78, 0x5a, 0xa6, 0x06, 0x08, 0xa6, //0x8770,
0x07, 0xaf, 0x45, 0x12, 0x0b, 0xb2, 0xad, 0x03, 0x7c, 0x00, 0x12, 0x04, 0xd3, 0x78, 0x56, 0xa6, //0x8780,
0x06, 0x08, 0xa6, 0x07, 0xaf, 0x48, 0x78, 0x54, 0x12, 0x0b, 0xb4, 0xe5, 0x43, 0xfb, 0xfd, 0x7c, //0x8790,
0x00, 0x12, 0x04, 0xd3, 0x78, 0x5c, 0xa6, 0x06, 0x08, 0xa6, 0x07, 0xaf, 0x46, 0x7e, 0x00, 0x78, //0x87a0,
0x54, 0x12, 0x0b, 0xb6, 0xad, 0x03, 0x7c, 0x00, 0x12, 0x04, 0xd3, 0x78, 0x58, 0xa6, 0x06, 0x08, //0x87b0,
0xa6, 0x07, 0xc3, 0x78, 0x5b, 0xe6, 0x94, 0x08, 0x18, 0xe6, 0x94, 0x00, 0x50, 0x05, 0x76, 0x00, //0x87c0,
0x08, 0x76, 0x08, 0xc3, 0x78, 0x5d, 0xe6, 0x94, 0x08, 0x18, 0xe6, 0x94, 0x00, 0x50, 0x05, 0x76, //0x87d0,
0x00, 0x08, 0x76, 0x08, 0x78, 0x5a, 0x12, 0x0b, 0xc6, 0xff, 0xd3, 0x78, 0x57, 0xe6, 0x9f, 0x18, //0x87e0,
0xe6, 0x9e, 0x40, 0x0e, 0x78, 0x5a, 0xe6, 0x13, 0xfe, 0x08, 0xe6, 0x78, 0x57, 0x12, 0x0c, 0x08, //0x87f0,
0x80, 0x04, 0x7e, 0x00, 0x7f, 0x00, 0x78, 0x5e, 0x12, 0x0b, 0xbe, 0xff, 0xd3, 0x78, 0x59, 0xe6, //0x8800,
0x9f, 0x18, 0xe6, 0x9e, 0x40, 0x0e, 0x78, 0x5c, 0xe6, 0x13, 0xfe, 0x08, 0xe6, 0x78, 0x59, 0x12, //0x8810,
0x0c, 0x08, 0x80, 0x04, 0x7e, 0x00, 0x7f, 0x00, 0xe4, 0xfc, 0xfd, 0x78, 0x62, 0x12, 0x06, 0x99, //0x8820,
0x78, 0x5a, 0x12, 0x0b, 0xc6, 0x78, 0x57, 0x26, 0xff, 0xee, 0x18, 0x36, 0xfe, 0x78, 0x66, 0x12, //0x8830,
0x0b, 0xbe, 0x78, 0x59, 0x26, 0xff, 0xee, 0x18, 0x36, 0xfe, 0xe4, 0xfc, 0xfd, 0x78, 0x6a, 0x12, //0x8840,
0x06, 0x99, 0x12, 0x0b, 0xce, 0x78, 0x66, 0x12, 0x06, 0x8c, 0xd3, 0x12, 0x06, 0x45, 0x40, 0x08, //0x8850,
0x12, 0x0b, 0xce, 0x78, 0x66, 0x12, 0x06, 0x99, 0x78, 0x54, 0x12, 0x0b, 0xd0, 0x78, 0x6a, 0x12, //0x8860,
0x06, 0x8c, 0xd3, 0x12, 0x06, 0x45, 0x40, 0x0a, 0x78, 0x54, 0x12, 0x0b, 0xd0, 0x78, 0x6a, 0x12, //0x8870,
0x06, 0x99, 0x78, 0x61, 0xe6, 0x90, 0x60, 0x01, 0xf0, 0x78, 0x65, 0xe6, 0xa3, 0xf0, 0x78, 0x69, //0x8880,
0xe6, 0xa3, 0xf0, 0x78, 0x55, 0xe6, 0xa3, 0xf0, 0x7d, 0x01, 0x78, 0x61, 0x12, 0x0b, 0xe9, 0x24, //0x8890,
0x01, 0x12, 0x0b, 0xa6, 0x78, 0x65, 0x12, 0x0b, 0xe9, 0x24, 0x02, 0x12, 0x0b, 0xa6, 0x78, 0x69, //0x88a0,
0x12, 0x0b, 0xe9, 0x24, 0x03, 0x12, 0x0b, 0xa6, 0x78, 0x6d, 0x12, 0x0b, 0xe9, 0x24, 0x04, 0x12, //0x88b0,
0x0b, 0xa6, 0x0d, 0xbd, 0x05, 0xd4, 0xc2, 0x0e, 0xc2, 0x06, 0x22, 0x85, 0x08, 0x41, 0x90, 0x30, //0x88c0,
0x24, 0xe0, 0xf5, 0x3d, 0xa3, 0xe0, 0xf5, 0x3e, 0xa3, 0xe0, 0xf5, 0x3f, 0xa3, 0xe0, 0xf5, 0x40, //0x88d0,
0xa3, 0xe0, 0xf5, 0x3c, 0xd2, 0x34, 0xe5, 0x41, 0x12, 0x06, 0xb1, 0x09, 0x31, 0x03, 0x09, 0x35, //0x88e0,
0x04, 0x09, 0x3b, 0x05, 0x09, 0x3e, 0x06, 0x09, 0x41, 0x07, 0x09, 0x4a, 0x08, 0x09, 0x5b, 0x12, //0x88f0,
0x09, 0x73, 0x18, 0x09, 0x89, 0x19, 0x09, 0x5e, 0x1a, 0x09, 0x6a, 0x1b, 0x09, 0xad, 0x80, 0x09, //0x8900,
0xb2, 0x81, 0x0a, 0x1d, 0x8f, 0x0a, 0x09, 0x90, 0x0a, 0x1d, 0x91, 0x0a, 0x1d, 0x92, 0x0a, 0x1d, //0x8910,
0x93, 0x0a, 0x1d, 0x94, 0x0a, 0x1d, 0x98, 0x0a, 0x17, 0x9f, 0x0a, 0x1a, 0xec, 0x00, 0x00, 0x0a, //0x8920,
0x38, 0x12, 0x0f, 0x74, 0x22, 0x12, 0x0f, 0x74, 0xd2, 0x03, 0x22, 0xd2, 0x03, 0x22, 0xc2, 0x03, //0x8930,
0x22, 0xa2, 0x37, 0xe4, 0x33, 0xf5, 0x3c, 0x02, 0x0a, 0x1d, 0xc2, 0x01, 0xc2, 0x02, 0xc2, 0x03, //0x8940,
0x12, 0x0d, 0x0d, 0x75, 0x1e, 0x70, 0xd2, 0x35, 0x02, 0x0a, 0x1d, 0x02, 0x0a, 0x04, 0x85, 0x40, //0x8950,
0x4a, 0x85, 0x3c, 0x4b, 0x12, 0x0a, 0xff, 0x02, 0x0a, 0x1d, 0x85, 0x4a, 0x40, 0x85, 0x4b, 0x3c, //0x8960,
0x02, 0x0a, 0x1d, 0xe4, 0xf5, 0x22, 0xf5, 0x23, 0x85, 0x40, 0x31, 0x85, 0x3f, 0x30, 0x85, 0x3e, //0x8970,
0x2f, 0x85, 0x3d, 0x2e, 0x12, 0x0f, 0x46, 0x80, 0x1f, 0x75, 0x22, 0x00, 0x75, 0x23, 0x01, 0x74, //0x8980,
0xff, 0xf5, 0x2d, 0xf5, 0x2c, 0xf5, 0x2b, 0xf5, 0x2a, 0x12, 0x0f, 0x46, 0x85, 0x2d, 0x40, 0x85, //0x8990,
0x2c, 0x3f, 0x85, 0x2b, 0x3e, 0x85, 0x2a, 0x3d, 0xe4, 0xf5, 0x3c, 0x80, 0x70, 0x12, 0x0f, 0x16, //0x89a0,
0x80, 0x6b, 0x85, 0x3d, 0x45, 0x85, 0x3e, 0x46, 0xe5, 0x47, 0xc3, 0x13, 0xff, 0xe5, 0x45, 0xc3, //0x89b0,
0x9f, 0x50, 0x02, 0x8f, 0x45, 0xe5, 0x48, 0xc3, 0x13, 0xff, 0xe5, 0x46, 0xc3, 0x9f, 0x50, 0x02, //0x89c0,
0x8f, 0x46, 0xe5, 0x47, 0xc3, 0x13, 0xff, 0xfd, 0xe5, 0x45, 0x2d, 0xfd, 0xe4, 0x33, 0xfc, 0xe5, //0x89d0,
0x44, 0x12, 0x0f, 0x90, 0x40, 0x05, 0xe5, 0x44, 0x9f, 0xf5, 0x45, 0xe5, 0x48, 0xc3, 0x13, 0xff, //0x89e0,
0xfd, 0xe5, 0x46, 0x2d, 0xfd, 0xe4, 0x33, 0xfc, 0xe5, 0x43, 0x12, 0x0f, 0x90, 0x40, 0x05, 0xe5, //0x89f0,
0x43, 0x9f, 0xf5, 0x46, 0x12, 0x06, 0xd7, 0x80, 0x14, 0x85, 0x40, 0x48, 0x85, 0x3f, 0x47, 0x85, //0x8a00,
0x3e, 0x46, 0x85, 0x3d, 0x45, 0x80, 0x06, 0x02, 0x06, 0xd7, 0x12, 0x0d, 0x7e, 0x90, 0x30, 0x24, //0x8a10,
0xe5, 0x3d, 0xf0, 0xa3, 0xe5, 0x3e, 0xf0, 0xa3, 0xe5, 0x3f, 0xf0, 0xa3, 0xe5, 0x40, 0xf0, 0xa3, //0x8a20,
0xe5, 0x3c, 0xf0, 0x90, 0x30, 0x23, 0xe4, 0xf0, 0x22, 0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, //0x8a30,
0xd0, 0x90, 0x3f, 0x0c, 0xe0, 0xf5, 0x32, 0xe5, 0x32, 0x30, 0xe3, 0x74, 0x30, 0x36, 0x66, 0x90, //0x8a40,
0x60, 0x19, 0xe0, 0xf5, 0x0a, 0xa3, 0xe0, 0xf5, 0x0b, 0x90, 0x60, 0x1d, 0xe0, 0xf5, 0x14, 0xa3, //0x8a50,
0xe0, 0xf5, 0x15, 0x90, 0x60, 0x21, 0xe0, 0xf5, 0x0c, 0xa3, 0xe0, 0xf5, 0x0d, 0x90, 0x60, 0x29, //0x8a60,
0xe0, 0xf5, 0x0e, 0xa3, 0xe0, 0xf5, 0x0f, 0x90, 0x60, 0x31, 0xe0, 0xf5, 0x10, 0xa3, 0xe0, 0xf5, //0x8a70,
0x11, 0x90, 0x60, 0x39, 0xe0, 0xf5, 0x12, 0xa3, 0xe0, 0xf5, 0x13, 0x30, 0x01, 0x06, 0x30, 0x33, //0x8a80,
0x03, 0xd3, 0x80, 0x01, 0xc3, 0x92, 0x09, 0x30, 0x02, 0x06, 0x30, 0x33, 0x03, 0xd3, 0x80, 0x01, //0x8a90,
0xc3, 0x92, 0x0a, 0x30, 0x33, 0x0c, 0x30, 0x03, 0x09, 0x20, 0x02, 0x06, 0x20, 0x01, 0x03, 0xd3, //0x8aa0,
0x80, 0x01, 0xc3, 0x92, 0x0b, 0x90, 0x30, 0x01, 0xe0, 0x44, 0x40, 0xf0, 0xe0, 0x54, 0xbf, 0xf0, //0x8ab0,
0xe5, 0x32, 0x30, 0xe1, 0x14, 0x30, 0x34, 0x11, 0x90, 0x30, 0x22, 0xe0, 0xf5, 0x08, 0xe4, 0xf0, //0x8ac0,
0x30, 0x00, 0x03, 0xd3, 0x80, 0x01, 0xc3, 0x92, 0x08, 0xe5, 0x32, 0x30, 0xe5, 0x12, 0x90, 0x56, //0x8ad0,
0xa1, 0xe0, 0xf5, 0x09, 0x30, 0x31, 0x09, 0x30, 0x05, 0x03, 0xd3, 0x80, 0x01, 0xc3, 0x92, 0x0d, //0x8ae0,
0x90, 0x3f, 0x0c, 0xe5, 0x32, 0xf0, 0xd0, 0xd0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32, 0x90, //0x8af0,
0x0e, 0x7e, 0xe4, 0x93, 0xfe, 0x74, 0x01, 0x93, 0xff, 0xc3, 0x90, 0x0e, 0x7c, 0x74, 0x01, 0x93, //0x8b00,
0x9f, 0xff, 0xe4, 0x93, 0x9e, 0xfe, 0xe4, 0x8f, 0x3b, 0x8e, 0x3a, 0xf5, 0x39, 0xf5, 0x38, 0xab, //0x8b10,
0x3b, 0xaa, 0x3a, 0xa9, 0x39, 0xa8, 0x38, 0xaf, 0x4b, 0xfc, 0xfd, 0xfe, 0x12, 0x05, 0x28, 0x12, //0x8b20,
0x0d, 0xe1, 0xe4, 0x7b, 0xff, 0xfa, 0xf9, 0xf8, 0x12, 0x05, 0xb3, 0x12, 0x0d, 0xe1, 0x90, 0x0e, //0x8b30,
0x69, 0xe4, 0x12, 0x0d, 0xf6, 0x12, 0x0d, 0xe1, 0xe4, 0x85, 0x4a, 0x37, 0xf5, 0x36, 0xf5, 0x35, //0x8b40,
0xf5, 0x34, 0xaf, 0x37, 0xae, 0x36, 0xad, 0x35, 0xac, 0x34, 0xa3, 0x12, 0x0d, 0xf6, 0x8f, 0x37, //0x8b50,
0x8e, 0x36, 0x8d, 0x35, 0x8c, 0x34, 0xe5, 0x3b, 0x45, 0x37, 0xf5, 0x3b, 0xe5, 0x3a, 0x45, 0x36, //0x8b60,
0xf5, 0x3a, 0xe5, 0x39, 0x45, 0x35, 0xf5, 0x39, 0xe5, 0x38, 0x45, 0x34, 0xf5, 0x38, 0xe4, 0xf5, //0x8b70,
0x22, 0xf5, 0x23, 0x85, 0x3b, 0x31, 0x85, 0x3a, 0x30, 0x85, 0x39, 0x2f, 0x85, 0x38, 0x2e, 0x02, //0x8b80,
0x0f, 0x46, 0xe0, 0xa3, 0xe0, 0x75, 0xf0, 0x02, 0xa4, 0xff, 0xae, 0xf0, 0xc3, 0x08, 0xe6, 0x9f, //0x8b90,
0xf6, 0x18, 0xe6, 0x9e, 0xf6, 0x22, 0xff, 0xe5, 0xf0, 0x34, 0x60, 0x8f, 0x82, 0xf5, 0x83, 0xec, //0x8ba0,
0xf0, 0x22, 0x78, 0x52, 0x7e, 0x00, 0xe6, 0xfc, 0x08, 0xe6, 0xfd, 0x02, 0x04, 0xc1, 0xe4, 0xfc, //0x8bb0,
0xfd, 0x12, 0x06, 0x99, 0x78, 0x5c, 0xe6, 0xc3, 0x13, 0xfe, 0x08, 0xe6, 0x13, 0x22, 0x78, 0x52, //0x8bc0,
0xe6, 0xfe, 0x08, 0xe6, 0xff, 0xe4, 0xfc, 0xfd, 0x22, 0xe7, 0xc4, 0xf8, 0x54, 0xf0, 0xc8, 0x68, //0x8bd0,
0xf7, 0x09, 0xe7, 0xc4, 0x54, 0x0f, 0x48, 0xf7, 0x22, 0xe6, 0xfc, 0xed, 0x75, 0xf0, 0x04, 0xa4, //0x8be0,
0x22, 0x12, 0x06, 0x7c, 0x8f, 0x48, 0x8e, 0x47, 0x8d, 0x46, 0x8c, 0x45, 0x22, 0xe0, 0xfe, 0xa3, //0x8bf0,
0xe0, 0xfd, 0xee, 0xf6, 0xed, 0x08, 0xf6, 0x22, 0x13, 0xff, 0xc3, 0xe6, 0x9f, 0xff, 0x18, 0xe6, //0x8c00,
0x9e, 0xfe, 0x22, 0xe6, 0xc3, 0x13, 0xf7, 0x08, 0xe6, 0x13, 0x09, 0xf7, 0x22, 0xad, 0x39, 0xac, //0x8c10,
0x38, 0xfa, 0xf9, 0xf8, 0x12, 0x05, 0x28, 0x8f, 0x3b, 0x8e, 0x3a, 0x8d, 0x39, 0x8c, 0x38, 0xab, //0x8c20,
0x37, 0xaa, 0x36, 0xa9, 0x35, 0xa8, 0x34, 0x22, 0x93, 0xff, 0xe4, 0xfc, 0xfd, 0xfe, 0x12, 0x05, //0x8c30,
0x28, 0x8f, 0x37, 0x8e, 0x36, 0x8d, 0x35, 0x8c, 0x34, 0x22, 0x78, 0x84, 0xe6, 0xfe, 0x08, 0xe6, //0x8c40,
0xff, 0xe4, 0x8f, 0x37, 0x8e, 0x36, 0xf5, 0x35, 0xf5, 0x34, 0x22, 0x90, 0x0e, 0x8c, 0xe4, 0x93, //0x8c50,
0x25, 0xe0, 0x24, 0x0a, 0xf8, 0xe6, 0xfe, 0x08, 0xe6, 0xff, 0x22, 0xe6, 0xfe, 0x08, 0xe6, 0xff, //0x8c60,
0xe4, 0x8f, 0x3b, 0x8e, 0x3a, 0xf5, 0x39, 0xf5, 0x38, 0x22, 0x78, 0x4e, 0xe6, 0xfe, 0x08, 0xe6, //0x8c70,
0xff, 0x22, 0xef, 0x25, 0xe0, 0x24, 0x4e, 0xf8, 0xe6, 0xfc, 0x08, 0xe6, 0xfd, 0x22, 0x78, 0x89, //0x8c80,
0xef, 0x26, 0xf6, 0x18, 0xe4, 0x36, 0xf6, 0x22, 0x75, 0x89, 0x03, 0x75, 0xa8, 0x01, 0x75, 0xb8, //0x8c90,
0x04, 0x75, 0x34, 0xff, 0x75, 0x35, 0x0e, 0x75, 0x36, 0x15, 0x75, 0x37, 0x0d, 0x12, 0x0e, 0x9a, //0x8ca0,
0x12, 0x00, 0x09, 0x12, 0x0f, 0x16, 0x12, 0x00, 0x06, 0xd2, 0x00, 0xd2, 0x34, 0xd2, 0xaf, 0x75, //0x8cb0,
0x34, 0xff, 0x75, 0x35, 0x0e, 0x75, 0x36, 0x49, 0x75, 0x37, 0x03, 0x12, 0x0e, 0x9a, 0x30, 0x08, //0x8cc0,
0x09, 0xc2, 0x34, 0x12, 0x08, 0xcb, 0xc2, 0x08, 0xd2, 0x34, 0x30, 0x0b, 0x09, 0xc2, 0x36, 0x12, //0x8cd0,
0x02, 0x6c, 0xc2, 0x0b, 0xd2, 0x36, 0x30, 0x09, 0x09, 0xc2, 0x36, 0x12, 0x00, 0x0e, 0xc2, 0x09, //0x8ce0,
0xd2, 0x36, 0x30, 0x0e, 0x03, 0x12, 0x06, 0xd7, 0x30, 0x35, 0xd3, 0x90, 0x30, 0x29, 0xe5, 0x1e, //0x8cf0,
0xf0, 0xb4, 0x10, 0x05, 0x90, 0x30, 0x23, 0xe4, 0xf0, 0xc2, 0x35, 0x80, 0xc1, 0xe4, 0xf5, 0x4b, //0x8d00,
0x90, 0x0e, 0x7a, 0x93, 0xff, 0xe4, 0x8f, 0x37, 0xf5, 0x36, 0xf5, 0x35, 0xf5, 0x34, 0xaf, 0x37, //0x8d10,
0xae, 0x36, 0xad, 0x35, 0xac, 0x34, 0x90, 0x0e, 0x6a, 0x12, 0x0d, 0xf6, 0x8f, 0x37, 0x8e, 0x36, //0x8d20,
0x8d, 0x35, 0x8c, 0x34, 0x90, 0x0e, 0x72, 0x12, 0x06, 0x7c, 0xef, 0x45, 0x37, 0xf5, 0x37, 0xee, //0x8d30,
0x45, 0x36, 0xf5, 0x36, 0xed, 0x45, 0x35, 0xf5, 0x35, 0xec, 0x45, 0x34, 0xf5, 0x34, 0xe4, 0xf5, //0x8d40,
0x22, 0xf5, 0x23, 0x85, 0x37, 0x31, 0x85, 0x36, 0x30, 0x85, 0x35, 0x2f, 0x85, 0x34, 0x2e, 0x12, //0x8d50,
0x0f, 0x46, 0xe4, 0xf5, 0x22, 0xf5, 0x23, 0x90, 0x0e, 0x72, 0x12, 0x0d, 0xea, 0x12, 0x0f, 0x46, //0x8d60,
0xe4, 0xf5, 0x22, 0xf5, 0x23, 0x90, 0x0e, 0x6e, 0x12, 0x0d, 0xea, 0x02, 0x0f, 0x46, 0xe5, 0x40, //0x8d70,
0x24, 0xf2, 0xf5, 0x37, 0xe5, 0x3f, 0x34, 0x43, 0xf5, 0x36, 0xe5, 0x3e, 0x34, 0xa2, 0xf5, 0x35, //0x8d80,
0xe5, 0x3d, 0x34, 0x28, 0xf5, 0x34, 0xe5, 0x37, 0xff, 0xe4, 0xfe, 0xfd, 0xfc, 0x78, 0x18, 0x12, //0x8d90,
0x06, 0x69, 0x8f, 0x40, 0x8e, 0x3f, 0x8d, 0x3e, 0x8c, 0x3d, 0xe5, 0x37, 0x54, 0xa0, 0xff, 0xe5, //0x8da0,
0x36, 0xfe, 0xe4, 0xfd, 0xfc, 0x78, 0x07, 0x12, 0x06, 0x56, 0x78, 0x10, 0x12, 0x0f, 0x9a, 0xe4, //0x8db0,
0xff, 0xfe, 0xe5, 0x35, 0xfd, 0xe4, 0xfc, 0x78, 0x0e, 0x12, 0x06, 0x56, 0x12, 0x0f, 0x9d, 0xe4, //0x8dc0,
0xff, 0xfe, 0xfd, 0xe5, 0x34, 0xfc, 0x78, 0x18, 0x12, 0x06, 0x56, 0x78, 0x08, 0x12, 0x0f, 0x9a, //0x8dd0,
0x22, 0x8f, 0x3b, 0x8e, 0x3a, 0x8d, 0x39, 0x8c, 0x38, 0x22, 0x12, 0x06, 0x7c, 0x8f, 0x31, 0x8e, //0x8de0,
0x30, 0x8d, 0x2f, 0x8c, 0x2e, 0x22, 0x93, 0xf9, 0xf8, 0x02, 0x06, 0x69, 0x00, 0x00, 0x00, 0x00, //0x8df0,
0x12, 0x01, 0x17, 0x08, 0x31, 0x15, 0x53, 0x54, 0x44, 0x20, 0x20, 0x20, 0x20, 0x20, 0x13, 0x01, //0x8e00,
0x10, 0x01, 0x56, 0x40, 0x1a, 0x30, 0x29, 0x7e, 0x00, 0x30, 0x04, 0x20, 0xdf, 0x30, 0x05, 0x40, //0x8e10,
0xbf, 0x50, 0x03, 0x00, 0xfd, 0x50, 0x27, 0x01, 0xfe, 0x60, 0x00, 0x11, 0x00, 0x3f, 0x05, 0x30, //0x8e20,
0x00, 0x3f, 0x06, 0x22, 0x00, 0x3f, 0x01, 0x2a, 0x00, 0x3f, 0x02, 0x00, 0x00, 0x36, 0x06, 0x07, //0x8e30,
0x00, 0x3f, 0x0b, 0x0f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x30, 0x01, 0x40, 0xbf, 0x30, 0x01, 0x00, //0x8e40,
0xbf, 0x30, 0x29, 0x70, 0x00, 0x3a, 0x00, 0x00, 0xff, 0x3a, 0x00, 0x00, 0xff, 0x36, 0x03, 0x36, //0x8e50,
0x02, 0x41, 0x44, 0x58, 0x20, 0x18, 0x10, 0x0a, 0x04, 0x04, 0x00, 0x03, 0xff, 0x64, 0x00, 0x00, //0x8e60,
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x04, 0x06, 0x06, 0x00, 0x03, 0x51, 0x00, 0x7a, //0x8e70,
0x50, 0x3c, 0x28, 0x1e, 0x10, 0x10, 0x50, 0x2d, 0x28, 0x16, 0x10, 0x10, 0x02, 0x00, 0x10, 0x0c, //0x8e80,
0x10, 0x04, 0x0c, 0x6e, 0x06, 0x05, 0x00, 0xa5, 0x5a, 0x00, 0xae, 0x35, 0xaf, 0x36, 0xe4, 0xfd, //0x8e90,
0xed, 0xc3, 0x95, 0x37, 0x50, 0x33, 0x12, 0x0f, 0xe2, 0xe4, 0x93, 0xf5, 0x38, 0x74, 0x01, 0x93, //0x8ea0,
0xf5, 0x39, 0x45, 0x38, 0x60, 0x23, 0x85, 0x39, 0x82, 0x85, 0x38, 0x83, 0xe0, 0xfc, 0x12, 0x0f, //0x8eb0,
0xe2, 0x74, 0x03, 0x93, 0x52, 0x04, 0x12, 0x0f, 0xe2, 0x74, 0x02, 0x93, 0x42, 0x04, 0x85, 0x39, //0x8ec0,
0x82, 0x85, 0x38, 0x83, 0xec, 0xf0, 0x0d, 0x80, 0xc7, 0x22, 0x78, 0xbe, 0xe6, 0xd3, 0x08, 0xff, //0x8ed0,
0xe6, 0x64, 0x80, 0xf8, 0xef, 0x64, 0x80, 0x98, 0x22, 0x93, 0xff, 0x7e, 0x00, 0xe6, 0xfc, 0x08, //0x8ee0,
0xe6, 0xfd, 0x12, 0x04, 0xc1, 0x78, 0xc1, 0xe6, 0xfc, 0x08, 0xe6, 0xfd, 0xd3, 0xef, 0x9d, 0xee, //0x8ef0,
0x9c, 0x22, 0x78, 0xbd, 0xd3, 0xe6, 0x64, 0x80, 0x94, 0x80, 0x22, 0x25, 0xe0, 0x24, 0x0a, 0xf8, //0x8f00,
0xe6, 0xfe, 0x08, 0xe6, 0xff, 0x22, 0xe5, 0x3c, 0xd3, 0x94, 0x00, 0x40, 0x0b, 0x90, 0x0e, 0x88, //0x8f10,
0x12, 0x0b, 0xf1, 0x90, 0x0e, 0x86, 0x80, 0x09, 0x90, 0x0e, 0x82, 0x12, 0x0b, 0xf1, 0x90, 0x0e, //0x8f20,
0x80, 0xe4, 0x93, 0xf5, 0x44, 0xa3, 0xe4, 0x93, 0xf5, 0x43, 0xd2, 0x06, 0x30, 0x06, 0x03, 0xd3, //0x8f30,
0x80, 0x01, 0xc3, 0x92, 0x0e, 0x22, 0xa2, 0xaf, 0x92, 0x32, 0xc2, 0xaf, 0xe5, 0x23, 0x45, 0x22, //0x8f40,
0x90, 0x0e, 0x5d, 0x60, 0x0e, 0x12, 0x0f, 0xcb, 0xe0, 0xf5, 0x2c, 0x12, 0x0f, 0xc8, 0xe0, 0xf5, //0x8f50,
0x2d, 0x80, 0x0c, 0x12, 0x0f, 0xcb, 0xe5, 0x30, 0xf0, 0x12, 0x0f, 0xc8, 0xe5, 0x31, 0xf0, 0xa2, //0x8f60,
0x32, 0x92, 0xaf, 0x22, 0xd2, 0x01, 0xc2, 0x02, 0xe4, 0xf5, 0x1f, 0xf5, 0x1e, 0xd2, 0x35, 0xd2, //0x8f70,
0x33, 0xd2, 0x36, 0xd2, 0x01, 0xc2, 0x02, 0xf5, 0x1f, 0xf5, 0x1e, 0xd2, 0x35, 0xd2, 0x33, 0x22, //0x8f80,
0xfb, 0xd3, 0xed, 0x9b, 0x74, 0x80, 0xf8, 0x6c, 0x98, 0x22, 0x12, 0x06, 0x69, 0xe5, 0x40, 0x2f, //0x8f90,
0xf5, 0x40, 0xe5, 0x3f, 0x3e, 0xf5, 0x3f, 0xe5, 0x3e, 0x3d, 0xf5, 0x3e, 0xe5, 0x3d, 0x3c, 0xf5, //0x8fa0,
0x3d, 0x22, 0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0x90, 0x3f, 0x0d, 0xe0, 0xf5, 0x33, 0xe5, 0x33, //0x8fb0,
0xf0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32, 0x90, 0x0e, 0x5f, 0xe4, 0x93, 0xfe, 0x74, 0x01, //0x8fc0,
0x93, 0xf5, 0x82, 0x8e, 0x83, 0x22, 0x78, 0x7f, 0xe4, 0xf6, 0xd8, 0xfd, 0x75, 0x81, 0xcd, 0x02, //0x8fd0,
0x0c, 0x98, 0x8f, 0x82, 0x8e, 0x83, 0x75, 0xf0, 0x04, 0xed, 0x02, 0x06, 0xa5, //0x8fe0
};
//=======================================================================
static inline struct ov5640 *to_ov5640(struct v4l2_subdev *sd)
{
return container_of(sd, struct ov5640, sd);
}
#define REG_DLY (999) // fake register, indicates need to delay before going on
static const struct reg_value ov5640_global_init_setting[] = {
{0x3103, 0x11},
{0x3008, 0x82}, // reset
{REG_DLY, 0x1e},// delay here of 30ms
{0x3008, 0x42}, // power down
{0x3103, 0x03},
{0x3017, 0x00}, // disable oe
{0x3018, 0x00},
// PLL settings
{0x3034, 0x18},
{0x3035, 0x11},
{0x3036, 0x54}, // 0x46 - 30fps,
{0x3037, 0x13}, // div
{0x3108, 0x01},
{0x3824, 0x01}, // PCLK divider
{REG_DLY,0x05}, // delay 5ms
{0x3630, 0x36},
{0x3631, 0x0e},
{0x3632, 0xe2},
{0x3633, 0x12},
{0x3621, 0xe0},
{0x3704, 0xa0},
{0x3703, 0x5a},
{0x3715, 0x78},
{0x3717, 0x01},
{0x370b, 0x60},
{0x3705, 0x1a},
{0x3905, 0x02},
{0x3906, 0x10},
{0x3901, 0x0a},
{0x3731, 0x12},
{0x3600, 0x08},
{0x3601, 0x33},
{0x302d, 0x60},
{0x3620, 0x52},
{0x371b, 0x20},
{0x471c, 0x50},
{0x3a13, 0x43},
{0x3a18, 0x00},
{0x3a19, 0x88}, // AGC ceiling
{0x3635, 0x13},
{0x3636, 0x03},
{0x3634, 0x40},
{0x3622, 0x01},
{0x3c01, 0x34},
{0x3c04, 0x28},
{0x3c05, 0x98},
{0x3c06, 0x00},
{0x3c07, 0x08}, // spec light meter threshold
{0x3c08, 0x00},
{0x3c09, 0x1c},
{0x3c0a, 0x9c},
{0x3c0b, 0x40},
{0x3820, 0x46}, // timing control
{0x3821, 0x00}, // timing control
{0x3814, 0x11},
{0x3815, 0x11},
{0x3800, 0x00}, // size setup
{0x3801, 0x00},
{0x3802, 0x00},
{0x3803, 0x04},
{0x3804, 0x0a},
{0x3805, 0x3f},
{0x3806, 0x07},
{0x3807, 0x9b},
{0x3808, 0x02},
{0x3809, 0x80},
{0x380a, 0x01},
{0x380b, 0xe0},
{0x380c, 0x07},
{0x380d, 0x68},
{0x380e, 0x03},
{0x380f, 0xd8},
{0x3810, 0x00},
{0x3811, 0x10},
{0x3812, 0x00},
{0x3813, 0x04},
{0x3618, 0x04},
{0x3612, 0x2b},
{0x3708, 0x63},
{0x3709, 0x12},
{0x370c, 0x00},
{0x4001, 0x02},
{0x4004, 0x02}, // black line control process
{0x3000, 0x00}, // no resets
{0x3002, 0x1c},
{0x3004, 0xff},
{0x3006, 0xc3},
{0x300e, 0x45}, // 2 lane MIPI, MIPI enable, DEBUG
{0x302e, 0x08},
{0x4300, 0x32},
{0x4837, 0x0a}, // PCLK period
{0x501f, 0x00},
{0x4713, 0x02},
{0x4407, 0x04},
{0x440e, 0x00},
{0x460c, 0x20},
{0x5000, 0xa7},
{0x5001, 0xa3},
//AWB
{0x3406, 0x00}, // set AWB to AUTO
{0x5180, 0xff},
{0x5181, 0xf2},
{0x5182, 0x00},
{0x5183, 0x14},
{0x5184, 0x25},
{0x5185, 0x24},
{0x5186, 0x16},
{0x5187, 0x16},
{0x5188, 0x16},
{0x5189, 0x6e},
{0x518a, 0x68},
{0x518b, 0xe0},
{0x518c, 0xb2},
{0x518d, 0x42},
{0x518e, 0x3e},
{0x518f, 0x4c},
{0x5190, 0x56},
{0x5191, 0xf8},
{0x5192, 0x04},
{0x5193, 0x70},
{0x5194, 0xf0},
{0x5195, 0xf0},
{0x5196, 0x03},
{0x5197, 0x01},
{0x5198, 0x04},
{0x5199, 0x12},
{0x519a, 0x04},
{0x519b, 0x00},
{0x519c, 0x06},
{0x519d, 0x82},
{0x519e, 0x38},
// Color
{0x5381, 0x1e},
{0x5382, 0x5b},
{0x5383, 0x14},
{0x5384, 0x05},
{0x5385, 0x77},
{0x5386, 0x7c},
{0x5387, 0x72},
{0x5388, 0x58},
{0x5389, 0x1a},
{0x538a, 0x01},
{0x538b, 0x98},
//Sharpness/Denoise
{0x5300, 0x08},
{0x5301, 0x30},
{0x5302, 0x30},
{0x5303, 0x10},
{0x5304, 0x08},
{0x5305, 0x30},
{0x5306, 0x1c},
{0x5307, 0x2c},
{0x5309, 0x08},
{0x530a, 0x30},
{0x530b, 0x04},
{0x530c, 0x06},
// Gamma
{0x5480, 0x01},
{0x5481, 0x06},
{0x5482, 0x12},
{0x5483, 0x1e},
{0x5484, 0x4a},
{0x5485, 0x58},
{0x5486, 0x65},
{0x5487, 0x73},
{0x5488, 0x7d},
{0x5489, 0x88},
{0x548a, 0x92},
{0x548b, 0xa3},
{0x548c, 0xb2},
{0x548d, 0xc8},
{0x548e, 0xdd},
{0x548f, 0xf0},
{0x5490, 0x15},
// UV
{0x5580, 0x06},
{0x5583, 0x40},
{0x5584, 0x10},
{0x5589, 0x10},
{0x558a, 0x00},
{0x558b, 0xf8},
{0x501d, 0x40},
// Lens Shading
{0x5800, 0x15},
{0x5801, 0x10},
{0x5802, 0x0D},
{0x5803, 0x0D},
{0x5804, 0x0F},
{0x5805, 0x15},
{0x5806, 0x0A},
{0x5807, 0x07},
{0x5808, 0x05},
{0x5809, 0x05},
{0x580A, 0x07},
{0x580B, 0x0B},
{0x580C, 0x07},
{0x580D, 0x03},
{0x580E, 0x01},
{0x580F, 0x01},
{0x5810, 0x03},
{0x5811, 0x07},
{0x5812, 0x07},
{0x5813, 0x03},
{0x5814, 0x01},
{0x5815, 0x01},
{0x5816, 0x03},
{0x5817, 0x06},
{0x5818, 0x0D},
{0x5819, 0x08},
{0x581A, 0x06},
{0x581B, 0x06},
{0x581D, 0x0B},
{0x581E, 0x14},
{0x581F, 0x13},
{0x5820, 0x0E},
{0x5821, 0x0E},
{0x5822, 0x12},
{0x5823, 0x12},
{0x5824, 0x46},
{0x5825, 0x26},
{0x5826, 0x06},
{0x5827, 0x46},
{0x5828, 0x44},
{0x5829, 0x26},
{0x582A, 0x24},
{0x582B, 0x42},
{0x582C, 0x24},
{0x582D, 0x46},
{0x582E, 0x24},
{0x582F, 0x42},
{0x5830, 0x60},
{0x5831, 0x42},
{0x5832, 0x24},
{0x5833, 0x26},
{0x5834, 0x24},
{0x5835, 0x24},
{0x5836, 0x24},
{0x5837, 0x46},
{0x5838, 0x44},
{0x5839, 0x46},
{0x583A, 0x26},
{0x583B, 0x48},
{0x583C, 0x44},
{0x583D, 0xBF},
{0x5025, 0x00},
// EV
{0x3a0f, 0x30},
{0x3a10, 0x28},
{0x3a1b, 0x30},
{0x3a1e, 0x26},
{0x3a11, 0x60},
{0x3a1f, 0x14},
};
//=====================================================================
static const struct reg_value ov5640_setting_720p[] = {
{0x3820, 0x46}, // timing control
{0x3821, 0x00}, // timing control
{0x3034, 0x18},
{0x3035, 0x21}, //0x11:60fps 0x21:30fps 0x41:15fps 0xa1:7.5fps
{0x3036, 0x54},
{0x3037, 0x13},
{0x3108, 0x01},
{0x3824, 0x01},
{REG_DLY,0x05}, // delay 5ms
//timing
//1280x720
{0x3808, 0x05}, //H size MSB
{0x3809, 0x00}, //H size LSB
{0x380a, 0x02}, //V size MSB
{0x380b, 0xd0}, //V size LSB
{0x380c, 0x07}, //HTS MSB
{0x380d, 0x64}, //HTS LSB
{0x380e, 0x02}, //VTS MSB
{0x380f, 0xe4}, //LSB
//banding step
{0x3a08, 0x00}, //50HZ step MSB
{0x3a09, 0xdd}, //50HZ step LSB
{0x3a0a, 0x00}, //60HZ step MSB
{0x3a0b, 0xb8}, //60HZ step LSB
{0x3a0e, 0x03}, //50HZ step max
{0x3a0d, 0x04}, //60HZ step max
{0x3503, 0x00}, //AGC manual 0x01, AEC manual 0x10
{0x350c, 0x00},
{0x350d, 0x00},
{0x3c07, 0x07}, //light meter 1 threshold
{0x3814, 0x31}, //horizontal subsample
{0x3815, 0x31}, //vertical subsample
{0x3800, 0x00}, //x address start high byte
{0x3801, 0x00}, //x address start low byte
{0x3802, 0x00}, //y address start high byte
{0x3803, 0xfa}, //y address start low byte
{0x3804, 0x0a}, //x address end high byte
{0x3805, 0x3f}, //x address end low byte
{0x3806, 0x06}, //y address end high byte
{0x3807, 0xa9}, //y address end low byte
{0x3810, 0x00}, //isp hortizontal offset high byte
{0x3811, 0x10}, //isp hortizontal offset low byte
{0x3812, 0x00}, //isp vertical offset high byte
{0x3813, 0x04}, //isp vertical offset low byte
{0x4002, 0x45}, //BLC related
{0x4005, 0x18}, //BLC related
{0x3618, 0x00},
{0x3612, 0x29},
{0x3709, 0x52},
{0x370c, 0x03},
{0x3a02, 0x02}, //60HZ max exposure limit MSB
{0x3a03, 0xe0}, //60HZ max exposure limit LSB
{0x3a14, 0x02}, //50HZ max exposure limit MSB
{0x3a15, 0xe0}, //50HZ max exposure limit LSB
{0x4004, 0x02}, //BLC line number
{0x3002, 0x1c}, //reset JFIFO SFIFO JPG
{0x3006, 0xc3}, //enable xx clock
{0x460c, 0x20}, //PCLK Manuale
{0x4837, 0x16}, //PCLK period
{0x5001, 0x83}, //ISP effect
{0x302c, 0x42},//bit[7:6]: output drive capability
//00: 1x 01: 2x 10: 3x 11: 4x
{0x3a18, 0x00},//
{0x3a19, 0xd8},//
};
static const struct reg_value ov5640_setting_1080p[] = {
{0x3800, 0x01},
{0x3801, 0x50},
{0x3802, 0x01},
{0x3803, 0xb2},
{0x3804, 0x08},
{0x3805, 0xef},
{0x3806, 0x05},
{0x3807, 0xf1},
//timing
//1920x1080
{0x3808, 0x07}, //H size MSB
{0x3809, 0x80}, //H size LSB
{0x380a, 0x04}, //V size MSB
{0x380b, 0x38}, //V size LSB
{0x380c, 0x09}, //HTS MSB
{0x380d, 0xc4}, //HTS LSB
{0x380e, 0x04}, //VTS MSB
{0x380f, 0x60}, //VTS LSB
{0x3a02, 0x04},
{0x3a03, 0x60},
// banding step
{0x3a08, 0x01}, //50HZ step MSB
{0x3a09, 0x50}, //50HZ step LSB
{0x3a0a, 0x01}, //60HZ step MSB
{0x3a0b, 0x18}, //60HZ step LSB
{0x3a0e, 0x03}, //50HZ step max
{0x3a0d, 0x04}, //60HZ step max
{0x3503, 0x00}, //AEC enable
{0x350c, 0x00},
{0x350d, 0x00},
{0x3c07, 0x07}, //light meter 1 thereshold
{0x3814, 0x11}, //horizton subsample
{0x3815, 0x11}, //vertical subsample
{0x3800, 0x01}, //x address start high byte
{0x3801, 0x50}, //x address start low byte
{0x3802, 0x01}, //y address start high byte
{0x3803, 0xb2}, //y address start low byte
{0x3804, 0x08}, //x address end high byte
{0x3805, 0xef}, //x address end low byte
{0x3806, 0x05}, //y address end high byte
{0x3807, 0xf1}, //y address end low byte
{0x3810, 0x00}, //isp hortizontal offset high byte
{0x3811, 0x10}, //isp hortizontal offset low byte
{0x3812, 0x00}, //isp vertical offset high byte
{0x3813, 0x04}, //isp vertical offset low byte
{0x4002, 0x45}, //BLC related
{0x4005, 0x18}, //BLC related
{0x3618, 0x04},
{0x3612, 0x2b},
{0x3709, 0x12},
{0x370c, 0x00},
{0x3a02, 0x04}, //60HZ max exposure limit MSB
{0x3a03, 0x60}, //60HZ max exposure limit LSB
{0x3a14, 0x04}, //50HZ max exposure limit MSB
{0x3a15, 0x60}, //50HZ max exposure limit LSB
{0x5684, 0x07}, //HWIDTH avg window hi byte
{0x5685, 0xa0}, //HWIDTH avg window lo byte
{0x5686, 0x04}, //VWIDTH avg window hi byte
{0x5687, 0x40}, //VWIDTH avg window lo byte
};
static const struct reg_value ov5640_setting_full[] = {
{0x3820, 0x46}, //timing control
{0x3821, 0x00}, //timing control
{0x3034, 0x18},
{0x3035, 0xa1}, //0x11:60fps 0x21:30fps 0x41:15fps 0xa1:7.5fps
{0x3036, 0x54},
{0x3037, 0x13},
{0x3108, 0x01},
{0x3824, 0x01},
{REG_DLY, 0x05}, //delay 5ms
// timing
{0x3808, 0x0a}, //H size MSB
{0x3809, 0x20}, //H size LSB
{0x380a, 0x07}, //V size MSB
{0x380b, 0x98}, //V size LSB
{0x380c, 0x0b}, //HTS MSB
{0x380d, 0x1c}, //HTS LSB
{0x380e, 0x07}, //VTS MSB
{0x380f, 0xb0}, //LSB
// banding step
{0x3a08, 0x00}, //50HZ step MSB
{0x3a09, 0x93}, //50HZ step LSB
{0x3a0a, 0x00}, //60HZ step MSB
{0x3a0b, 0x7b}, //60HZ step LSB
{0x3a0e, 0x0d}, //50HZ step max
{0x3a0d, 0x10}, //60HZ step max
{0x3503, 0x00}, //AEC
{0x350a, 0x00},
{0x350b, 0x10},
{0x350c, 0x00},
{0x350d, 0x00},
{0x3c07, 0x07}, //light meter 1 threshold
{0x3814, 0x11}, //horizton subsample
{0x3815, 0x11}, //vertical subsample
{0x3800, 0x00}, //x address start high byte
{0x3801, 0x00}, //x address start low byte
{0x3802, 0x00}, //y address start high byte
{0x3803, 0x00}, //y address start low byte
{0x3804, 0x0a}, //x address end high byte
{0x3805, 0x3f}, //x address end low byte
{0x3806, 0x07}, //y address end high byte
{0x3807, 0x9f}, //y address end low byte
{0x3810, 0x00}, //isp hortizontal offset high byte
{0x3811, 0x10}, //isp hortizontal offset low byte
{0x3812, 0x00}, //isp vertical offset high byte
{0x3813, 0x04}, //isp vertical offset low byte
{0x4002, 0xc5}, //BLC related
{0x4005, 0x1a}, //BLC related
{0x3618, 0x04},
{0x3612, 0x2b},
{0x3709, 0x12},
{0x370c, 0x00},
{0x3a02, 0x07}, //60HZ max exposure limit MSB
{0x3a03, 0xb0}, //60HZ max exposure limit LSB
{0x3a14, 0x07}, //50HZ max exposure limit MSB
{0x3a15, 0xb0}, //50HZ max exposure limit LSB
{0x4004, 0x06}, //BLC line number
{0x4837, 0x2c}, //PCLK period
{0x5001, 0xa3}, //ISP effect
{0x302c, 0x42},//bit[7:6]: output drive capability
//00: 1x 01: 2x 10: 3x 11: 4x
};
static const s64 link_freq[] = {
222880000,
334320000
};
static const struct ov5640_mode_info ov5640_mode_info_data[] = {
{
.width = 1280,
.height = 720,
.data = ov5640_setting_720p,
.data_size = ARRAY_SIZE(ov5640_setting_720p),
.pixel_clock = 111440000,
.link_freq = 0
},
{
.width = 1920,
.height = 1080,
.data = ov5640_setting_1080p,
.data_size = ARRAY_SIZE(ov5640_setting_1080p),
.pixel_clock = 167160000,
.link_freq = 1
},
{
.width = 2592,
.height = 1936,
.data = ov5640_setting_full,
.data_size = ARRAY_SIZE(ov5640_setting_full),
.pixel_clock = 167160000,
.link_freq = 1
},
};
static int ov5640_regulators_enable(struct ov5640 *ov5640)
{
int ret;
ret = regulator_enable(ov5640->io_regulator);
if (ret < 0) {
dev_err(ov5640->dev, "set io voltage failed\n");
return ret;
}
ret = regulator_enable(ov5640->analog_regulator);
if (ret) {
dev_err(ov5640->dev, "set analog voltage failed\n");
goto err_disable_io;
}
ret = regulator_enable(ov5640->core_regulator);
if (ret) {
dev_err(ov5640->dev, "set core voltage failed\n");
goto err_disable_analog;
}
return 0;
err_disable_analog:
regulator_disable(ov5640->analog_regulator);
err_disable_io:
regulator_disable(ov5640->io_regulator);
return ret;
}
static void ov5640_regulators_disable(struct ov5640 *ov5640)
{
int ret;
ret = regulator_disable(ov5640->core_regulator);
if (ret < 0)
dev_err(ov5640->dev, "core regulator disable failed\n");
ret = regulator_disable(ov5640->analog_regulator);
if (ret < 0)
dev_err(ov5640->dev, "analog regulator disable failed\n");
ret = regulator_disable(ov5640->io_regulator);
if (ret < 0)
dev_err(ov5640->dev, "io regulator disable failed\n");
}
static int ov5640_write_reg_to(struct ov5640 *ov5640, u16 reg, u8 val, u16 i2c_addr)
{
// int ret;
// NOT NEEDED for 17.09 kernel
// if (ov5640->use_cci)
// {
// /* perform i2c via qcom CCI interface */
// ret = msm_cci_ctrl_write(i2c_addr, reg, &val, 1);
// }
// else
// {
// struct i2c_msg msg;
// u8 data[3] = {(reg & 0xff00) >> 8, reg & 0xff, val};
// msg.addr = i2c_addr >> 1;
// msg.len = 3;
// msg.buf = data;
// msg.flags = 0;
// ret = i2c_transfer(ov5640->i2c_client->adapter, &msg, 1);
// }
u8 regbuf[3] = {
reg >> 8,
reg & 0xff,
val
};
struct i2c_msg msgs = {
.addr = i2c_addr,
.flags = 0,
.len = 3,
.buf = regbuf
};
int ret;
ret = i2c_transfer(ov5640->i2c_client->adapter, &msgs, 1);
if (ret < 0)
dev_err(ov5640->dev,
"%s: write reg error %d on addr 0x%x: reg=0x%x, val=0x%x\n",
__func__, ret, i2c_addr, reg, val);
return ret;
}
static int ov5640_write_reg(struct ov5640 *ov5640, u16 reg, u8 val)
{
// int ret;
// u16 i2c_addr = ov5640->i2c_client->addr;
//NOT NEEDED for 17.09 kernel
// if (ov5640->use_cci)
// {
// /* perform i2c via qcom CCI interface */
// ret = msm_cci_ctrl_write(i2c_addr, reg, &val, 1);
// }
// else
// {
// struct i2c_msg msg;
// u8 data[3] = {(reg & 0xff00) >> 8, reg & 0xff, val};
// msg.addr = i2c_addr >> 1;
// msg.len = 3;
// msg.buf = data;
// msg.flags = 0;
// ret = i2c_transfer(ov5640->i2c_client->adapter, &msg, 1);
// }
u8 regbuf[3];
int ret;
regbuf[0] = reg >> 8;
regbuf[1] = reg & 0xff;
regbuf[2] = val;
ret = i2c_master_send(ov5640->i2c_client, regbuf, 3);
if (ret < 0)
dev_err(ov5640->dev, "%s: write reg error %d: reg=%x, val=%x\n",
__func__, ret, reg, val);
return ret;
}
static int ov5640_read_reg(struct ov5640 *ov5640, u16 reg, u8 *val)
{
// int ret;
// u16 i2c_addr = ov5640->i2c_client->addr;
//NOT NEEDED for 17.09 kernel
// if (ov5640->use_cci)
// {
// /* perform i2c via qcom CCI interface */
// ret = msm_cci_ctrl_read(i2c_addr, reg, val, 1);
// }
// else
// {
// struct i2c_msg msg[2];
// u8 data[2] = {(reg & 0xff00) >> 8, reg & 0xff};
// msg[0].addr = i2c_addr >> 1;
// msg[0].len = 2;
// msg[0].buf = data;
// msg[0].flags = 0;
//
// msg[1].addr = i2c_addr >> 1;
// msg[1].len = 1;
// msg[1].buf = val;
// msg[1].flags = I2C_M_RD;
//
// ret = i2c_transfer(ov5640->i2c_client->adapter, msg, 2);
// }
//
u8 regbuf[2];
int ret;
regbuf[0] = reg >> 8;
regbuf[1] = reg & 0xff;
ret = i2c_master_send(ov5640->i2c_client, regbuf, 2);
if (ret < 0) {
dev_err(ov5640->dev, "%s: write reg error %d: reg=%x\n",
__func__, ret, reg);
return ret;
}
ret = i2c_master_recv(ov5640->i2c_client, val, 1);
if (ret < 0) {
dev_err(ov5640->dev, "%s: read reg error %d: reg=%x\n",
__func__, ret, reg);
return ret;
}
return 0;
}
static int ov5640_af_init(struct ov5640 *ov5640)
{
u8 state=0x8F;
int remaining = 0;
int totalCnt = 0;
int sentCnt = 0;
u16 addr = 0x8000;
int ret = 0;
ret = ov5640_write_reg(ov5640, 0x3000, 0x20); // reset mcu
if (ret < 0)
{
dev_err(ov5640->dev, "Focus initialization: cant reset the MCU\n");
return ret;
}
msleep(10);
totalCnt = ARRAY_SIZE(AD5820_Config);
for (sentCnt = 0; sentCnt < totalCnt; sentCnt++, addr++)
{
ret = ov5640_write_reg(ov5640, addr, AD5820_Config[sentCnt]);
if (ret < 0)
{
dev_err(ov5640->dev, "Focus initialization: failed writing reg: 0x%x\n", addr);
return ret;
}
}
msleep(10);
ov5640_write_reg(ov5640, 0x3022, 0x00);
ov5640_write_reg(ov5640, 0x3023, 0x00);
ov5640_write_reg(ov5640, 0x3024, 0x00);
ov5640_write_reg(ov5640, 0x3025, 0x00);
ov5640_write_reg(ov5640, 0x3026, 0x00);
ov5640_write_reg(ov5640, 0x3027, 0x00);
ov5640_write_reg(ov5640, 0x3028, 0x00);
ov5640_write_reg(ov5640, 0x3029, 0x7f);
ov5640_write_reg(ov5640, 0x3000, 0x00);
// Wait for up tp 1000ms to converge
for (remaining = 1000;;)
{
ret = (u8)ov5640_read_reg(ov5640, 0x3029, &state);
if (ret < 0)
{
dev_err(ov5640->dev, "Focus initialization: cant read status reg 0x3029\n");
break;
}
if (state == 0x70)
{
ov5640->focus_state = V4L2_AUTO_FOCUS_STATUS_IDLE;
break;
}
remaining -= (10 - msleep_interruptible(10));
if (signal_pending(current))
break;
if (remaining <= 0)
{
dev_err(ov5640->dev, "Focus initialization: timed out waiting to complete\n");
ret = -1;
break;
}
}
return(ret);
}
static int ov5640_set_af_mode(struct ov5640 *ov5640, s32 focusmode, s32 param)
{
int ret = 0;
u8 state = 0x8F;
int remaining ;
if (ov5640->focus_state == FOCUS_NOT_INITIALIZED)
{
return(0);
}
//send focus mode command to firmware
ov5640_write_reg(ov5640, 0x3023, 0x01);
switch(focusmode)
{
case V4L2_CID_FOCUS_AUTO:
dev_dbg(ov5640->dev, "V4L2_CID_FOCUS_AUTO\n");
if (param == 0)
{
if (ov5640->autofocus)
{
// stop it and wait for status
ov5640_write_reg(ov5640, 0x3022, 0x08);
ov5640->autofocus = 0;
break;
}
return(0);
}
else if (param == 1)
{
// Start autofocus don't wait for focus to converge, return immediately
ov5640_write_reg(ov5640, 0x3022, 0x04);
ov5640->autofocus = 1;
return(0);
}
break;
case V4L2_CID_AUTO_FOCUS_START:
// Start a single pass AF ending in focus or failure
// presume we are set up and can start
dev_dbg(ov5640->dev, "V4L2_CID_AUTO_FOCUS_START\n");
ov5640_write_reg(ov5640, 0x3022, 0x03);
return(0);
case V4L2_CID_3A_LOCK:
dev_dbg(ov5640->dev, "V4L2_CID_3A_LOCK\n");
if (param & V4L2_LOCK_FOCUS) {
// goto idle, wait for status to be acknowledged
ov5640_write_reg(ov5640, 0x3022, 0x06);
break;
} else {
// resume signle pass focus, don't wait for convergence
ov5640_write_reg(ov5640, 0x3022, 0x03);
return (0);
}
case V4L2_CID_AUTO_FOCUS_STOP:
dev_dbg(ov5640->dev, "V4L2_CID_AUTO_FOCUS_STOP\n");
// goto stop, wait for status to be acknowledged
ov5640_write_reg(ov5640, 0x3022, 0x08);
break;
}
// Wait for up tp 3000ms to converge
for (remaining = 3000;;)
{
ret = (u8)ov5640_read_reg(ov5640, 0x3023, &state);
if (ret < 0)
{
break;
}
if (state == 0x00)
{
dev_dbg(ov5640->dev, "Focus succeeded in %dms\n", remaining);
break;
}
remaining -= (30 - msleep_interruptible(30));
if (signal_pending(current))
break;
if (remaining <= 0)
{
dev_dbg(ov5640->dev,"Focus failed: timed out\n");
ret = -1;
break;
}
}
return(ret);
}
static int ov5640_set_af_window(struct ov5640 *ov5640, int param)
{
int x_center, y_center;
int ret = 0;
if (ov5640->focus_state == FOCUS_NOT_INITIALIZED)
{
return(0);
}
// All current resos are 16:9 aspect ratio
switch (param)
{
case V4L2_AUTO_FOCUS_RANGE_AUTO:
case V4L2_AUTO_FOCUS_RANGE_MACRO:
// No optical zoom on the 5640 so these settings are the same
x_center = 40;
y_center = 23;
break;
case V4L2_AUTO_FOCUS_RANGE_NORMAL:
case V4L2_AUTO_FOCUS_RANGE_INFINITY:
// This is an implied "stop" of autofocus which returns the ov5640 to
// infinite/normal focus
ov5640_set_af_mode(ov5640, V4L2_CID_FOCUS_AUTO, V4L2_AUTO_FOCUS_RANGE_NORMAL);
return(0);
default:
dev_err(ov5640->dev, "Focus mode unknown: %x\n", param);
return(-1);
}
ov5640_write_reg(ov5640, 0x3022, 0x81); // cmd_tag
ov5640_write_reg(ov5640, 0x3023, 0x01);
ov5640_write_reg(ov5640, 0x3024, x_center);
ov5640_write_reg(ov5640, 0x3025, y_center);
return(ret);
}
static int ov5640_setup_af(struct ov5640 *ov5640)
{
int ret = 0;
dev_dbg(ov5640->dev,"Autofocus initialization started\n");
ret = ov5640_af_init(ov5640);
if (ret < 0) {
dev_err(ov5640->dev, "Autofocus initialization failed\n");
return(ret);
}
// Set to normal window
ov5640_set_af_window(ov5640, V4L2_AUTO_FOCUS_RANGE_NORMAL);
ov5640_set_af_mode(ov5640, V4L2_CID_FOCUS_AUTO, V4L2_AUTO_FOCUS_RANGE_NORMAL);
dev_dbg(ov5640->dev, "Autofocus initialization succeeded\n");
ov5640->focus_state = V4L2_AUTO_FOCUS_STATUS_IDLE;
return(0);
}
static int ov5640_get_af_status(struct ov5640 *ov5640, int *focus)
{
int ret = 0;
u8 state;
if (ov5640->focus_state == FOCUS_NOT_INITIALIZED)
{
*focus = ov5640->focus_state = V4L2_AUTO_FOCUS_STATUS_IDLE;
return(0);
}
ret = (u8)ov5640_read_reg(ov5640, 0x3029, &state);
if (ret < 0) {
dev_err(ov5640->dev, "Autofocus status failed\n");
return(ret);
}
switch(state)
{
case 0x70:
*focus = ov5640->focus_state = V4L2_AUTO_FOCUS_STATUS_IDLE;
break;
case 0x00:
*focus = ov5640->focus_state = V4L2_AUTO_FOCUS_STATUS_BUSY;
break;
case 0x10:
*focus = ov5640->focus_state = V4L2_AUTO_FOCUS_STATUS_REACHED;
break;
default:
*focus = ov5640->focus_state = V4L2_AUTO_FOCUS_STATUS_FAILED;
break;
}
return(0);
}
static int ov5640_set_aec_mode(struct ov5640 *ov5640, u32 mode)
{
u8 val = ov5640->aec_pk_manual;
int ret;
if (mode == V4L2_EXPOSURE_AUTO)
val &= ~OV5640_AEC_MANUAL_ENABLE;
else /* V4L2_EXPOSURE_MANUAL */
val |= OV5640_AEC_MANUAL_ENABLE;
ret = ov5640_write_reg(ov5640, OV5640_AEC_PK_MANUAL, val);
if (!ret)
ov5640->aec_pk_manual = val;
return ret;
}
static int ov5640_set_agc_mode(struct ov5640 *ov5640, u32 enable)
{
u8 val = ov5640->aec_pk_manual;
int ret;
if (enable)
val &= ~OV5640_AGC_MANUAL_ENABLE;
else
val |= OV5640_AGC_MANUAL_ENABLE;
ret = ov5640_write_reg(ov5640, OV5640_AEC_PK_MANUAL, val);
if (!ret)
ov5640->aec_pk_manual = val;
return ret;
}
static int ov5640_set_register_array(struct ov5640 *ov5640,
const struct reg_value *settings,
unsigned int num_settings)
{
unsigned int i;
int ret;
for (i = 0; i < num_settings; ++i, ++settings) {
if (settings->reg == REG_DLY) {
msleep(settings->val);
}
else {
ret = ov5640_write_reg(ov5640, settings->reg, settings->val);
if (ret < 0)
return ret;
}
}
return 0;
}
static int ov5640_set_power_on(struct ov5640 *ov5640)
{
int ret;
ret = ov5640_regulators_enable(ov5640);
if (ret < 0) {
return ret;
}
ret = clk_prepare_enable(ov5640->xclk);
if (ret < 0) {
dev_err(ov5640->dev, "clk prepare enable failed\n");
ov5640_regulators_disable(ov5640);
return ret;
}
usleep_range(5000, 15000);
gpiod_set_value_cansleep(ov5640->enable_gpio, 1);
dev_info(ov5640->dev, "Enable LOW\n");
usleep_range(1000, 2000);
gpiod_set_value_cansleep(ov5640->rst_gpio, 0);
dev_info(ov5640->dev, "Reset High\n");
msleep(20);
return 0;
}
static void ov5640_set_power_off(struct ov5640 *ov5640)
{
gpiod_set_value_cansleep(ov5640->rst_gpio, 1);
gpiod_set_value_cansleep(ov5640->enable_gpio, 0);
clk_disable_unprepare(ov5640->xclk);
ov5640_regulators_disable(ov5640);
}
static int ov5640_s_power(struct v4l2_subdev *sd, int on)
{
struct ov5640 *ov5640 = to_ov5640(sd);
int ret = 0;
mutex_lock(&ov5640->power_lock);
//NOT NEEDED for 17.09 kernel
// if (on && ov5640->use_cci) {
// /* initialize cci driver if it will be used for i2c */
// ret = msm_cci_ctrl_init();
// if (ret < 0)
// goto exit;
// }
dev_info(ov5640->dev, "s_power %d\n", on);
/* If the power count is modified from 0 to != 0 or from != 0 to 0,
* update the power state.
*/
if (ov5640->power_count == !on) {
if (on) {
mutex_lock(&ov5640_lock);
ret = ov5640_set_power_on(ov5640);
if (ret < 0)
goto exit;
if (ov5640->i2c_client->addr != 0x78)
{
ret = ov5640_write_reg_to(ov5640, 0x3100,
ov5640->i2c_client->addr, 0x78);
if (ret < 0) {
dev_err(ov5640->dev,
"could not change i2c address\n");
ov5640_set_power_off(ov5640);
mutex_unlock(&ov5640_lock);
goto exit;
}
}
mutex_unlock(&ov5640_lock);
ret = ov5640_set_register_array(ov5640,
ov5640_global_init_setting,
ARRAY_SIZE(ov5640_global_init_setting));
if (ret < 0) {
dev_err(ov5640->dev,
"could not set init registers\n");
ov5640_set_power_off(ov5640);
goto exit;
}
ret = ov5640_write_reg(ov5640, OV5640_SYSTEM_CTRL0,
OV5640_SYSTEM_CTRL0_STOP);
if (ret < 0) {
ov5640_set_power_off(ov5640);
goto exit;
}
} else {
ov5640_set_power_off(ov5640);
}
}
/* Update the power count. */
ov5640->power_count += on ? 1 : -1;
WARN_ON(ov5640->power_count < 0);
exit:
//NOT NEEDED for 17.09 kernel
// if (!on && ov5640->use_cci)
// msm_cci_ctrl_release();
mutex_unlock(&ov5640->power_lock);
return ret;
}
static int ov5640_set_saturation(struct ov5640 *ov5640, s32 value)
{
u32 reg_value = (value * 0x10) + 0x40;
int ret;
ret = ov5640_write_reg(ov5640, OV5640_SDE_SAT_U, reg_value);
if (ret < 0)
return ret;
return ov5640_write_reg(ov5640, OV5640_SDE_SAT_V, reg_value);
}
static int ov5640_set_hflip(struct ov5640 *ov5640, s32 value)
{
u8 val = ov5640->timing_tc_reg21;
int ret;
if (value == 0)
val &= ~(OV5640_SENSOR_MIRROR | OV5640_ISP_MIRROR);
else
val |= (OV5640_SENSOR_MIRROR | OV5640_ISP_MIRROR);
ret = ov5640_write_reg(ov5640, OV5640_TIMING_TC_REG21, val);
if (!ret)
ov5640->timing_tc_reg21 = val;
return ret;
}
static int ov5640_set_vflip(struct ov5640 *ov5640, s32 value)
{
u8 val = ov5640->timing_tc_reg20;
int ret;
if (value == 0)
val &= ~(OV5640_SENSOR_VFLIP | OV5640_ISP_VFLIP);
else
val |= (OV5640_SENSOR_VFLIP | OV5640_ISP_VFLIP);
ret = ov5640_write_reg(ov5640, OV5640_TIMING_TC_REG20, val);
if (!ret)
ov5640->timing_tc_reg20 = val;
return ret;
}
static int ov5640_set_test_pattern(struct ov5640 *ov5640, s32 value)
{
u8 val = 0;
if (value) {
val = OV5640_SET_TEST_PATTERN(value - 1);
val |= OV5640_TEST_PATTERN_ENABLE;
}
return ov5640_write_reg(ov5640, OV5640_PRE_ISP_TEST_SETTING_1, val);
}
static const char * const ov5640_test_pattern_menu[] = {
"Disabled",
"Vertical Color Bars",
"Pseudo-Random Data",
"Color Square",
"Black Image",
};
static int ov5640_set_awb(struct ov5640 *ov5640, s32 enable_auto)
{
u8 val = 0;
if (!enable_auto)
val = OV5640_AWB_MANUAL_ENABLE;
return ov5640_write_reg(ov5640, OV5640_AWB_MANUAL_CONTROL, val);
}
static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct ov5640 *ov5640 = container_of(ctrl->handler,
struct ov5640, ctrls);
int ret;
mutex_lock(&ov5640->power_lock);
if (!ov5640->power_count) {
mutex_unlock(&ov5640->power_lock);
return 0;
}
switch (ctrl->id) {
case V4L2_CID_SATURATION:
ret = ov5640_set_saturation(ov5640, ctrl->val);
break;
case V4L2_CID_AUTO_WHITE_BALANCE:
ret = ov5640_set_awb(ov5640, ctrl->val);
break;
case V4L2_CID_AUTOGAIN:
ret = ov5640_set_agc_mode(ov5640, ctrl->val);
break;
case V4L2_CID_EXPOSURE_AUTO:
ret = ov5640_set_aec_mode(ov5640, ctrl->val);
break;
case V4L2_CID_TEST_PATTERN:
ret = ov5640_set_test_pattern(ov5640, ctrl->val);
break;
case V4L2_CID_HFLIP:
ret = ov5640_set_hflip(ov5640, ctrl->val);
break;
case V4L2_CID_VFLIP:
ret = ov5640_set_vflip(ov5640, ctrl->val);
break;
case V4L2_CID_FOCUS_AUTO:
dev_dbg(ov5640->dev, "Set ctrl: V4L2_CID_FOCUS_AUTO, param=%x\n", ctrl->val);
ret = ov5640_set_af_mode(ov5640, V4L2_CID_FOCUS_AUTO, ctrl->val);
break;
case V4L2_CID_AUTO_FOCUS_START: // single shot auto focus start
dev_dbg(ov5640->dev, "Set ctrl: V4L2_CID_AUTO_FOCUS_START, param=%x\n", ctrl->val);
ret = ov5640_set_af_mode(ov5640, V4L2_CID_AUTO_FOCUS_START, ctrl->val);
break;
case V4L2_CID_3A_LOCK:
dev_dbg(ov5640->dev, "Set ctrl: V4L2_CID_3A_LOCK, param=%x\n", ctrl->val);
ret = ov5640_set_af_mode(ov5640, V4L2_CID_3A_LOCK, ctrl->val);
break;
case V4L2_CID_AUTO_FOCUS_STOP: // single shot auto focus stop
dev_dbg(ov5640->dev, "Set ctrl: V4L2_CID_AUTO_FOCUS_STOP, param=%x\n", ctrl->val);
ret = ov5640_set_af_mode(ov5640, V4L2_CID_AUTO_FOCUS_STOP, ctrl->val);
break;
case V4L2_CID_AUTO_FOCUS_RANGE:
dev_dbg(ov5640->dev, "Set ctrl: V4L2_CID_AUTO_FOCUS_RANGE, param=%x\n", ctrl->val);
ret = ov5640_set_af_window(ov5640, ctrl->val);
break;
default:
dev_dbg(ov5640->dev, "Ctrl call: Unhandled: %x param=%x\n", ctrl->id,ctrl->val);
ret = -EINVAL;
break;
}
mutex_unlock(&ov5640->power_lock);
return ret;
}
static int ov5640_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
{
struct ov5640 *ov5640 = container_of(ctrl->handler,
struct ov5640, ctrls);
int ret = 0;
mutex_lock(&ov5640->power_lock);
if (!ov5640->power_count) {
mutex_unlock(&ov5640->power_lock);
return 0;
}
switch (ctrl->id) {
case V4L2_CID_AUTO_FOCUS_STATUS:
ret = ov5640_get_af_status(ov5640, &ctrl->val);
if (ret < 0) {
dev_err(ov5640->dev, "Get ctrl: V4L2_CID_AUTO_FOCUS_STATUS failed\n");
}
break;
default:
dev_dbg(ov5640->dev, "Ctrl call: Unhandled: %x param=%x\n", ctrl->id, ctrl->val);
ret = -EINVAL;
break;
}
mutex_unlock(&ov5640->power_lock);
return ret;
}
static struct v4l2_ctrl_ops ov5640_ctrl_ops = {
.s_ctrl = ov5640_s_ctrl,
.g_volatile_ctrl = ov5640_g_volatile_ctrl,
};
static int ov5640_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_mbus_code_enum *code)
{
struct ov5640 *ov5640 = to_ov5640(sd);
dev_info(ov5640->dev, "Mbus code index %d\n", code->index);
if (code->index > 0)
return -EINVAL;
code->code = MEDIA_BUS_FMT_UYVY8_2X8;
return 0;
}
static int ov5640_enum_frame_size(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_frame_size_enum *fse)
{
struct ov5640 *ov5640 = to_ov5640(subdev);
dev_info(ov5640->dev, "Mbus fs code %d index %d\n", fse->code, fse->index);
if (fse->code != MEDIA_BUS_FMT_UYVY8_2X8)
return -EINVAL;
if (fse->index >= ARRAY_SIZE(ov5640_mode_info_data))
return -EINVAL;
fse->min_width = ov5640_mode_info_data[fse->index].width;
fse->max_width = ov5640_mode_info_data[fse->index].width;
fse->min_height = ov5640_mode_info_data[fse->index].height;
fse->max_height = ov5640_mode_info_data[fse->index].height;
return 0;
}
static struct v4l2_mbus_framefmt *
__ov5640_get_pad_format(struct ov5640 *ov5640,
struct v4l2_subdev_pad_config *cfg,
unsigned int pad,
enum v4l2_subdev_format_whence which)
{
switch (which) {
case V4L2_SUBDEV_FORMAT_TRY:
return v4l2_subdev_get_try_format(&ov5640->sd, cfg, pad);
case V4L2_SUBDEV_FORMAT_ACTIVE:
return &ov5640->fmt;
default:
return &ov5640->fmt; /* Returning null causes 0 derefence in ov5640_get_format */
}
}
static int ov5640_get_format(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *format)
{
struct ov5640 *ov5640 = to_ov5640(sd);
dev_info(ov5640->dev, "get_fmt which %d\n", format->which);
format->format = *__ov5640_get_pad_format(ov5640, cfg, format->pad,
format->which);
dev_info(ov5640->dev, "get_fmt: %x %dx%d\n",
format->format.code, format->format.width,
format->format.height);
return 0;
}
static struct v4l2_rect *
__ov5640_get_pad_crop(struct ov5640 *ov5640, struct v4l2_subdev_pad_config *cfg,
unsigned int pad, enum v4l2_subdev_format_whence which)
{
switch (which) {
case V4L2_SUBDEV_FORMAT_TRY:
return v4l2_subdev_get_try_crop(&ov5640->sd, cfg, pad);
case V4L2_SUBDEV_FORMAT_ACTIVE:
return &ov5640->crop;
default:
return NULL;
}
}
static const struct ov5640_mode_info *
ov5640_find_nearest_mode(unsigned int width, unsigned int height)
{
unsigned int i;
for (i = ARRAY_SIZE(ov5640_mode_info_data) - 1; i >= 0; i--) {
if (ov5640_mode_info_data[i].width <= width &&
ov5640_mode_info_data[i].height <= height)
break;
}
if (i < 0)
i = 0;
return &ov5640_mode_info_data[i];
}
// static int ov5640_set_format(struct v4l2_subdev *sd,
// struct v4l2_subdev_pad_config *cfg,
// struct v4l2_subdev_format *format)
// {
// struct ov5640 *ov5640 = to_ov5640(sd);
// struct v4l2_mbus_framefmt *__format;
// struct v4l2_rect *__crop;
// const struct ov5640_mode_info *new_mode;
//
// __crop = __ov5640_get_pad_crop(ov5640, cfg, format->pad,
// format->which);
//
// new_mode = ov5640_find_nearest_mode(format->format.width,
// format->format.height);
// __crop->width = new_mode->width;
// __crop->height = new_mode->height;
//
// if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE)
// ov5640->current_mode = new_mode;
//
// __format = __ov5640_get_pad_format(ov5640, cfg, format->pad,
// format->which);
// __format->width = __crop->width;
// __format->height = __crop->height;
// __format->code = MEDIA_BUS_FMT_UYVY8_2X8;
// __format->field = V4L2_FIELD_NONE;
// __format->colorspace = V4L2_COLORSPACE_SRGB;
//
// format->format = *__format;
//
// return 0;
// }
static int ov5640_set_format(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *format)
{
struct ov5640 *ov5640 = to_ov5640(sd);
struct v4l2_mbus_framefmt *__format;
struct v4l2_rect *__crop;
const struct ov5640_mode_info *new_mode;
int ret;
__crop = __ov5640_get_pad_crop(ov5640, cfg, format->pad,
format->which);
new_mode = ov5640_find_nearest_mode(format->format.width,
format->format.height);
__crop->width = new_mode->width;
__crop->height = new_mode->height;
if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
ret = v4l2_ctrl_s_ctrl_int64(ov5640->pixel_clock,
new_mode->pixel_clock);
if (ret < 0)
return ret;
ret = v4l2_ctrl_s_ctrl(ov5640->link_freq,
new_mode->link_freq);
if (ret < 0)
return ret;
ov5640->current_mode = new_mode;
}
__format = __ov5640_get_pad_format(ov5640, cfg, format->pad,
format->which);
__format->width = __crop->width;
__format->height = __crop->height;
__format->code = MEDIA_BUS_FMT_UYVY8_2X8;
__format->field = V4L2_FIELD_NONE;
__format->colorspace = V4L2_COLORSPACE_SRGB;
format->format = *__format;
return 0;
}
static int ov5640_entity_init_cfg(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg)
{
struct v4l2_subdev_format fmt = { 0 };
struct ov5640 *ov5640 = to_ov5640(subdev);
dev_err(ov5640->dev, "%s: Enter\n", __func__);
fmt.which = cfg ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
fmt.format.width = 1920;
fmt.format.height = 1080;
ov5640_set_format(subdev, cfg, &fmt);
return 0;
}
static int ov5640_get_selection(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_selection *sel)
{
struct ov5640 *ov5640 = to_ov5640(sd);
dev_info(ov5640->dev, "get_selection %d\n", sel->target);
if (sel->target != V4L2_SEL_TGT_CROP)
return -EINVAL;
sel->r = *__ov5640_get_pad_crop(ov5640, cfg, sel->pad,
sel->which);
return 0;
}
static int ov5640_s_stream(struct v4l2_subdev *subdev, int enable)
{
struct ov5640 *ov5640 = to_ov5640(subdev);
int ret;
dev_info(ov5640->dev, "stream %d\n", enable);
if (enable) {
ret = ov5640_set_register_array(ov5640,
ov5640->current_mode->data,
ov5640->current_mode->data_size);
if (ret < 0) {
dev_err(ov5640->dev, "could not set mode %dx%d\n",
ov5640->current_mode->width,
ov5640->current_mode->height);
return ret;
}
ret = ov5640_write_reg(ov5640, OV5640_SYSTEM_CTRL0,
OV5640_SYSTEM_CTRL0_START);
if (ret < 0)
return ret;
//works
ret = ov5640_setup_af(ov5640);
if (ret < 0) {
dev_err(ov5640->dev, "%s: could not initialize mcu for focus control\n",
__func__);
return(ret);
}
ret = v4l2_ctrl_handler_setup(&ov5640->ctrls);
if (ret < 0) {
dev_err(ov5640->dev, "could not sync v4l2 controls\n");
return ret;
}
} else {
ret = ov5640_write_reg(ov5640, OV5640_SYSTEM_CTRL0,
OV5640_SYSTEM_CTRL0_STOP);
if (ret < 0)
return ret;
}
return 0;
}
static int ov5640_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
{
struct ov5640 *ov5640 = to_ov5640(sd);
struct v4l2_subdev_pad_config cfg;
int ret;
dev_err(ov5640->dev, "%s: Enter\n", __func__);
memset(&cfg, 0, sizeof(cfg));
ret = ov5640_entity_init_cfg(sd, &cfg);
if (ret < 0)
return(ret);
return(0);
}
static const struct v4l2_subdev_core_ops ov5640_core_ops = {
.s_power = ov5640_s_power,
};
static const struct v4l2_subdev_video_ops ov5640_video_ops = {
.s_stream = ov5640_s_stream,
};
static const struct v4l2_subdev_pad_ops ov5640_subdev_pad_ops = {
.enum_mbus_code = ov5640_enum_mbus_code,
.enum_frame_size = ov5640_enum_frame_size,
.get_fmt = ov5640_get_format,
.set_fmt = ov5640_set_format,
.get_selection = ov5640_get_selection,
};
static const struct v4l2_subdev_ops ov5640_subdev_ops = {
.core = &ov5640_core_ops,
.video = &ov5640_video_ops,
.pad = &ov5640_subdev_pad_ops,
};
static const struct v4l2_subdev_internal_ops ov5640_subdev_internal_ops = {
.open = ov5640_open
};
static int ov5640_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct device_node *endpoint;
struct ov5640 *ov5640;
u8 chip_id_high, chip_id_low;
u32 xclk_freq;
int ret;
dev_dbg(dev, "%s: Enter, i2c addr = 0x%x\n", __func__, client->addr);
ov5640 = devm_kzalloc(dev, sizeof(struct ov5640), GFP_KERNEL);
if (!ov5640)
return -ENOMEM;
ov5640->i2c_client = client;
ov5640->dev = dev;
//ov5640->use_cci = of_property_read_bool(dev->of_node, "qcom,use_cci");
ov5640->hflip = of_property_read_bool(dev->of_node, "ovti,hflip");
ov5640->vflip = of_property_read_bool(dev->of_node, "ovti,vflip");
endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
if (!endpoint) {
dev_err(dev, "endpoint node not found\n");
return -EINVAL;
}
ret = v4l2_of_parse_endpoint(endpoint, &ov5640->ep);
if (ret < 0) {
dev_err(dev, "parsing endpoint node failed\n");
return ret;
}
of_node_put(endpoint);
if (ov5640->ep.bus_type != V4L2_MBUS_CSI2) {
dev_err(dev, "invalid bus type, must be CSI2\n");
return -EINVAL;
}
/* get system clock (xclk) */
ov5640->xclk = devm_clk_get(dev, "xclk");
if (IS_ERR(ov5640->xclk)) {
dev_err(dev, "could not get xclk");
return PTR_ERR(ov5640->xclk);
}
ret = of_property_read_u32(dev->of_node, "clock-frequency", &xclk_freq);
if (ret) {
dev_err(dev, "could not get xclk frequency\n");
return ret;
}
if (xclk_freq != 23880000) {
dev_err(dev, "external clock frequency %u is not supported\n",
xclk_freq);
return -EINVAL;
}
ret = clk_set_rate(ov5640->xclk, xclk_freq);
if (ret) {
dev_err(dev, "could not set xclk frequency\n");
return ret;
}
ov5640->io_regulator = devm_regulator_get(dev, "vdddo");
if (IS_ERR(ov5640->io_regulator)) {
dev_err(dev, "cannot get io regulator\n");
return PTR_ERR(ov5640->io_regulator);
}
ret = regulator_set_voltage(ov5640->io_regulator,
OV5640_VOLTAGE_DIGITAL_IO,
OV5640_VOLTAGE_DIGITAL_IO);
if (ret < 0) {
dev_err(dev, "cannot set io voltage\n");
return ret;
}
ov5640->core_regulator = devm_regulator_get(dev, "vddd");
if (IS_ERR(ov5640->core_regulator)) {
dev_err(dev, "cannot get core regulator\n");
return PTR_ERR(ov5640->core_regulator);
}
ret = regulator_set_voltage(ov5640->core_regulator,
OV5640_VOLTAGE_DIGITAL_CORE,
OV5640_VOLTAGE_DIGITAL_CORE);
if (ret < 0) {
dev_err(dev, "cannot set core voltage\n");
return ret;
}
ov5640->analog_regulator = devm_regulator_get(dev, "vdda");
if (IS_ERR(ov5640->analog_regulator)) {
dev_err(dev, "cannot get analog regulator\n");
return PTR_ERR(ov5640->analog_regulator);
}
ret = regulator_set_voltage(ov5640->analog_regulator,
OV5640_VOLTAGE_ANALOG,
OV5640_VOLTAGE_ANALOG);
if (ret < 0) {
dev_err(dev, "cannot set analog voltage\n");
return ret;
}
ov5640->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
dev_info(dev, "Enable HIGH\n");
if (IS_ERR(ov5640->enable_gpio)) {
dev_err(dev, "cannot get enable gpio\n");
return PTR_ERR(ov5640->enable_gpio);
}
ov5640->rst_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
dev_info(dev, "Reset LOW\n");
if (IS_ERR(ov5640->rst_gpio)) {
dev_err(dev, "cannot get reset gpio\n");
return PTR_ERR(ov5640->rst_gpio);
}
mutex_init(&ov5640->power_lock);
ov5640->focus_state = FOCUS_NOT_INITIALIZED;
v4l2_ctrl_handler_init(&ov5640->ctrls, 15);
v4l2_ctrl_new_std(&ov5640->ctrls, &ov5640_ctrl_ops,
V4L2_CID_SATURATION, -4, 4, 1, 0);
v4l2_ctrl_new_std(&ov5640->ctrls, &ov5640_ctrl_ops,
V4L2_CID_HFLIP, 0, 1, 1, ov5640->hflip);
v4l2_ctrl_new_std(&ov5640->ctrls, &ov5640_ctrl_ops,
V4L2_CID_VFLIP, 0, 1, 1, ov5640->vflip);
v4l2_ctrl_new_std(&ov5640->ctrls, &ov5640_ctrl_ops,
V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
v4l2_ctrl_new_std(&ov5640->ctrls, &ov5640_ctrl_ops,
V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
v4l2_ctrl_new_std_menu(&ov5640->ctrls, &ov5640_ctrl_ops,
V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL,
0, V4L2_EXPOSURE_AUTO);
v4l2_ctrl_new_std_menu_items(&ov5640->ctrls, &ov5640_ctrl_ops,
V4L2_CID_TEST_PATTERN,
ARRAY_SIZE(ov5640_test_pattern_menu) - 1,
0, 0, ov5640_test_pattern_menu);
v4l2_ctrl_new_std(&ov5640->ctrls, &ov5640_ctrl_ops,
V4L2_CID_FOCUS_AUTO, 0, 1, 1, 0);
v4l2_ctrl_new_std(&ov5640->ctrls, &ov5640_ctrl_ops,
V4L2_CID_3A_LOCK, 0,
V4L2_LOCK_FOCUS |
V4L2_LOCK_WHITE_BALANCE |
V4L2_LOCK_EXPOSURE, 0, 0);
v4l2_ctrl_new_std(&ov5640->ctrls, &ov5640_ctrl_ops,
V4L2_CID_AUTO_FOCUS_START, 0, 1, 1, 0);
v4l2_ctrl_new_std(&ov5640->ctrls, &ov5640_ctrl_ops,
V4L2_CID_AUTO_FOCUS_STOP, 0, 1, 1, 0);
v4l2_ctrl_new_std_menu(&ov5640->ctrls, &ov5640_ctrl_ops,
V4L2_CID_AUTO_FOCUS_RANGE,
V4L2_AUTO_FOCUS_RANGE_MACRO,
~(1 << V4L2_AUTO_FOCUS_RANGE_AUTO |
1 << V4L2_AUTO_FOCUS_RANGE_MACRO |
1 << V4L2_AUTO_FOCUS_RANGE_NORMAL |
1 << V4L2_AUTO_FOCUS_RANGE_INFINITY),
V4L2_AUTO_FOCUS_RANGE_NORMAL);
ov5640->af_status = v4l2_ctrl_new_std(&ov5640->ctrls, &ov5640_ctrl_ops,
V4L2_CID_AUTO_FOCUS_STATUS, 0,
(V4L2_AUTO_FOCUS_STATUS_BUSY |
V4L2_AUTO_FOCUS_STATUS_REACHED |
V4L2_AUTO_FOCUS_STATUS_FAILED),
0, V4L2_AUTO_FOCUS_STATUS_IDLE);
ov5640->af_status->flags |= V4L2_CTRL_FLAG_VOLATILE;
ov5640->pixel_clock = v4l2_ctrl_new_std(&ov5640->ctrls,
&ov5640_ctrl_ops,
V4L2_CID_PIXEL_RATE,
1, INT_MAX, 1, 1);
ov5640->link_freq = v4l2_ctrl_new_int_menu(&ov5640->ctrls,
&ov5640_ctrl_ops,
V4L2_CID_LINK_FREQ,
ARRAY_SIZE(link_freq) - 1,
0, link_freq);
if (ov5640->link_freq)
ov5640->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
ov5640->sd.ctrl_handler = &ov5640->ctrls;
if (ov5640->ctrls.error) {
dev_err(dev, "%s: control initialization error %d\n",
__func__, ov5640->ctrls.error);
ret = ov5640->ctrls.error;
goto free_ctrl;
}
client->flags |= I2C_CLIENT_SCCB;
v4l2_i2c_subdev_init(&ov5640->sd, client, &ov5640_subdev_ops);
ov5640->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
ov5640->sd.internal_ops = &ov5640_subdev_internal_ops;
ov5640->sd.dev = &client->dev;
ov5640->pad.flags = MEDIA_PAD_FL_SOURCE;
ov5640->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
ret = media_entity_pads_init(&ov5640->sd.entity, 1, &ov5640->pad);
if (ret < 0) {
dev_err(dev, "could not register media entity\n");
goto free_ctrl;
}
ret = ov5640_s_power(&ov5640->sd, true);
if (ret < 0) {
dev_err(dev, "could not power up OV5640\n");
goto free_entity;
}
ret = ov5640_read_reg(ov5640, OV5640_CHIP_ID_HIGH, &chip_id_high);
if (ret < 0 || chip_id_high != OV5640_CHIP_ID_HIGH_BYTE) {
dev_err(dev, "could not read ID high\n");
ret = -ENODEV;
goto power_down;
}
ret = ov5640_read_reg(ov5640, OV5640_CHIP_ID_LOW, &chip_id_low);
if (ret < 0 || chip_id_low != OV5640_CHIP_ID_LOW_BYTE) {
dev_err(dev, "could not read ID low\n");
ret = -ENODEV;
goto power_down;
}
dev_info(dev, "OV5640 detected at address 0x%02x\n", client->addr);
ret = ov5640_read_reg(ov5640, OV5640_AEC_PK_MANUAL,
&ov5640->aec_pk_manual);
if (ret < 0) {
dev_err(dev, "could not read AEC/AGC mode\n");
ret = -ENODEV;
goto power_down;
}
ret = ov5640_read_reg(ov5640, OV5640_TIMING_TC_REG20,
&ov5640->timing_tc_reg20);
if (ret < 0) {
dev_err(dev, "could not read vflip value\n");
ret = -ENODEV;
goto power_down;
}
ret = ov5640_read_reg(ov5640, OV5640_TIMING_TC_REG21,
&ov5640->timing_tc_reg21);
if (ret < 0) {
dev_err(dev, "could not read hflip value\n");
ret = -ENODEV;
goto power_down;
}
ov5640_s_power(&ov5640->sd, false);
ov5640_entity_init_cfg(&ov5640->sd, NULL);
ret = v4l2_async_register_subdev(&ov5640->sd);
if (ret < 0) {
dev_err(dev, "could not register v4l2 device\n");
goto free_entity;
}
return 0;
power_down:
ov5640_s_power(&ov5640->sd, false);
free_entity:
free_ctrl:
v4l2_ctrl_handler_free(&ov5640->ctrls);
mutex_destroy(&ov5640->power_lock);
return ret;
}
static int ov5640_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct ov5640 *ov5640 = to_ov5640(sd);
v4l2_async_unregister_subdev(&ov5640->sd);
media_entity_cleanup(&ov5640->sd.entity);
v4l2_ctrl_handler_free(&ov5640->ctrls);
mutex_destroy(&ov5640->power_lock);
return 0;
}
static const struct i2c_device_id ov5640_id[] = {
{ "ov5640", 0 },
{}
};
MODULE_DEVICE_TABLE(i2c, ov5640_id);
static const struct of_device_id ov5640_of_match[] = {
{ .compatible = "ovti,ov5640" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, ov5640_of_match);
static struct i2c_driver ov5640_i2c_driver = {
.driver = {
.of_match_table = of_match_ptr(ov5640_of_match),
.name = "ov5640",
},
.probe = ov5640_probe,
.remove = ov5640_remove,
.id_table = ov5640_id,
};
module_i2c_driver(ov5640_i2c_driver);
MODULE_DESCRIPTION("Omnivision OV5640 Camera Driver");
MODULE_AUTHOR("Todor Tomov <todor.tomov@linaro.org>");
MODULE_AUTHOR("Scott Dunnington <sdunnington@d3engineering.com>");
MODULE_LICENSE("GPL v2");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment