// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2020 MediaTek Inc.
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/units.h>
#include <media/media-entity.h>
#include <media/v4l2-async.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-fwnode.h>
#include <media/v4l2-subdev.h>
#define OV02A10_ID 0x2509
#define OV02A10_ID_MASK GENMASK(15, 0)
#define OV02A10_REG_CHIP_ID 0x02
/* Bit[1] vertical upside down */
/* Bit[0] horizontal mirror */
#define REG_MIRROR_FLIP_CONTROL 0x3f
/* Orientation */
#define REG_MIRROR_FLIP_ENABLE 0x03
/* Bit[2:0] MIPI transmission speed select */
#define TX_SPEED_AREA_SEL 0xa1
#define OV02A10_MIPI_TX_SPEED_DEFAULT 0x04
#define REG_PAGE_SWITCH 0xfd
#define REG_GLOBAL_EFFECTIVE 0x01
#define REG_ENABLE BIT(0)
#define REG_SC_CTRL_MODE 0xac
#define SC_CTRL_MODE_STANDBY 0x00
#define SC_CTRL_MODE_STREAMING 0x01
/* Exposure control */
#define OV02A10_EXP_SHIFT 8
#define OV02A10_REG_EXPOSURE_H 0x03
#define OV02A10_REG_EXPOSURE_L 0x04
#define OV02A10_EXPOSURE_MIN 4
#define OV02A10_EXPOSURE_MAX_MARGIN 4
#define OV02A10_EXPOSURE_STEP 1
/* Vblanking control */
#define OV02A10_VTS_SHIFT 8
#define OV02A10_REG_VTS_H 0x05
#define OV02A10_REG_VTS_L 0x06
#define OV02A10_VTS_MAX 0x209f
#define OV02A10_BASE_LINES 1224
/* Analog gain control */
#define OV02A10_REG_GAIN 0x24
#define OV02A10_GAIN_MIN 0x10
#define OV02A10_GAIN_MAX 0xf8
#define OV02A10_GAIN_STEP 0x01
#define OV02A10_GAIN_DEFAULT 0x40
/* Test pattern control */
#define OV02A10_REG_TEST_PATTERN 0xb6
#define OV02A10_LINK_FREQ_390MHZ (390 * HZ_PER_MHZ)
#define OV02A10_ECLK_FREQ (24 * HZ_PER_MHZ)
/* Number of lanes supported by this driver */
#define OV02A10_DATA_LANES 1
/* Bits per sample of sensor output */
#define OV02A10_BITS_PER_SAMPLE 10
static const char * const ov02a10_supply_names[] = {
"dovdd", /* Digital I/O power */
"avdd", /* Analog power */
"dvdd", /* Digital core power */
};
struct ov02a10_reg {
u8 addr;
u8 val;
};
struct ov02a10_reg_list {
u32 num_of_regs;
const struct ov02a10_reg *regs;
};
struct ov02a10_mode {
u32 width;
u32 height;
u32 exp_def;
u32 hts_def;
u32 vts_def;
const struct ov02a10_reg_list reg_list;
};
struct ov02a10 {
u32 eclk_freq;
/* Indication of MIPI transmission speed select */
u32 mipi_clock_voltage;
struct clk *eclk;
struct gpio_desc *pd_gpio;
struct gpio_desc *rst_gpio;
struct regulator_bulk_data supplies[ARRAY_SIZE(ov02a10_supply_names)];
bool streaming;
bool upside_down;
/*
* Serialize control access, get/set format, get selection
* and start streaming.
*/
struct mutex mutex;
struct v4l2_subdev subdev;
struct media_pad pad;
struct v4l2_mbus_framefmt fmt;
struct v4l2_ctrl_handler ctrl_handler;
struct v4l2_ctrl *exposure;
const struct ov02a10_mode *cur_mode;
};
static inline struct ov02a10 *to_ov02a10(struct v4l2_subdev *sd)
{
return container_of(sd, struct ov02a10, subdev);
}
/*
* eclk 24Mhz
* pclk 39Mhz
* linelength 934(0x3a6)
* framelength 1390(0x56E)
* grabwindow_width 1600
* grabwindow_height 1200
* max_framerate 30fps
* mipi_datarate per lane 780Mbps
*/
static const struct ov02a10_reg ov02a10_1600x1200_regs[] = {
{0xfd, 0x01},
{0xac, 0x00},
{0xfd, 0x00},
{0x2f, 0x29},
{0x34, 0x00},
{0x35, 0x21},
{0x30, 0x15},
{0x33, 0x01},
{0xfd, 0x01},
{0x44, 0x00},
{0x2a, 0x4c},
{0x2b, 0x1e},
{0x2c, 0x60},
{0x25, 0x11},
{0x03, 0x01},
{0x04, 0xae},
{0x09, 0x00},
{0x0a, 0x02},
{0x06, 0xa6},
{0x31, 0x00},
{0x24, 0x40},
{0x01, 0x01},
{0xfb, 0x73},
{0xfd, 0x01},
{0x16, 0x04},
{0x1c, 0x09},
{0x21, 0x42},
{0x12, 0x04},
{0x13, 0x10},
{0x11, 0x40},
{0x33, 0x81},
{0xd0, 0x00},
{0xd1, 0x01},
{0xd2, 0x00},
{0x50, 0x10},
{0x51, 0x23},
{0x52, 0x20},
{0x53, 0x10},
{0x54, 0x02},
{0x55, 0x20},
{0x56, 0x02},
{0x58, 0x48},
{0x5d, 0x15},
{0x5e, 0x05},
{0x66, 0x66},
{0x68, 0x68},
{0x6b, 0x00},
{0x6c, 0x00},
{0x6f, 0x40},
{0x70, 0x40},
{0x71, 0x0a},
{0x72, 0xf0},
{0x73, 0x10},
{0x75, 0x80},
{0x76, 0x10},
{0x84, 0x00},
{0x85, 0x10},
{0x86, 0x10},
{0x87, 0x00},
{0x8a, 0x22},
{0x8b, 0x22},
{0x19, 0xf1},
{0x29, 0x01},
{0xfd, 0x01},
{0x9d, 0x16},
{0xa0, 0x29},
{0xa1, 0x04},
{0xad, 0x62},
{0xae, 0x00},
{0xaf, 0x85},
{0xb1, 0x01},
{0x8e, 0x06},
{0x8f, 0x40},
{0x90, 0x04},
{0x91, 0xb0},
{0x45, 0x01},
{0x46, 0x00},
{0x47, 0x6c},
{0x48, 0x03},
{0x49, 0x8b},
{0x4a, 0x00},
{0x4b, 0x07},
{0x4c, 0x04},
{0x4d, 0xb7},
{0xf0, 0x40},
{0xf1, 0x40},
{0xf2, 0x40},
{0xf3, 0x40},
{0x3f, 0x00},
{0xfd, 0x01},
{0x05, 0x00},
{0x06, 0xa6},
{0xfd, 0x01},
};
static const char * const ov02a10_test_pattern_menu[] = {
"Disabled",
"Eight Ve