linux/drivers/media/i2c/ov7670.c

// SPDX-License-Identifier: GPL-2.0-only
/*
 * A V4L2 driver for OmniVision OV7670 cameras.
 *
 * Copyright 2006 One Laptop Per Child Association, Inc.  Written
 * by Jonathan Corbet with substantial inspiration from Mark
 * McClelland's ovcamchip code.
 *
 * Copyright 2006-7 Jonathan Corbet <[email protected]>
 */
#include <linux/clk.h>
#include <linux/init.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/videodev2.h>
#include <linux/gpio/consumer.h>
#include <media/v4l2-device.h>
#include <media/v4l2-event.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-fwnode.h>
#include <media/v4l2-mediabus.h>
#include <media/v4l2-image-sizes.h>
#include <media/i2c/ov7670.h>

MODULE_AUTHOR();
MODULE_DESCRIPTION();
MODULE_LICENSE();

static bool debug;
module_param(debug, bool, 0644);
MODULE_PARM_DESC();

/*
 * The 7670 sits on i2c with ID 0x42
 */
#define OV7670_I2C_ADDR

#define PLL_FACTOR

/* Registers */
#define REG_GAIN
#define REG_BLUE
#define REG_RED
#define REG_VREF
#define REG_COM1
#define COM1_CCIR656
#define REG_BAVE
#define REG_GbAVE
#define REG_AECHH
#define REG_RAVE
#define REG_COM2
#define COM2_SSLEEP
#define REG_PID
#define REG_VER
#define REG_COM3
#define COM3_SWAP
#define COM3_SCALEEN
#define COM3_DCWEN
#define REG_COM4
#define REG_COM5
#define REG_COM6
#define REG_AECH
#define REG_CLKRC
#define CLK_EXT
#define CLK_SCALE
#define REG_COM7
#define COM7_RESET
#define COM7_FMT_MASK
#define COM7_FMT_VGA
#define COM7_FMT_CIF
#define COM7_FMT_QVGA
#define COM7_FMT_QCIF
#define COM7_RGB
#define COM7_YUV
#define COM7_BAYER
#define COM7_PBAYER
#define REG_COM8
#define COM8_FASTAEC
#define COM8_AECSTEP
#define COM8_BFILT
#define COM8_AGC
#define COM8_AWB
#define COM8_AEC
#define REG_COM9
#define REG_COM10
#define COM10_HSYNC
#define COM10_PCLK_HB
#define COM10_HREF_REV
#define COM10_VS_LEAD
#define COM10_VS_NEG
#define COM10_HS_NEG
#define REG_HSTART
#define REG_HSTOP
#define REG_VSTART
#define REG_VSTOP
#define REG_PSHFT
#define REG_MIDH
#define REG_MIDL
#define REG_MVFP
#define MVFP_MIRROR
#define MVFP_FLIP

#define REG_AEW
#define REG_AEB
#define REG_VPT
#define REG_HSYST
#define REG_HSYEN
#define REG_HREF
#define REG_TSLB
#define TSLB_YLAST
#define REG_COM11
#define COM11_NIGHT
#define COM11_NMFR
#define COM11_HZAUTO
#define COM11_50HZ
#define COM11_EXP
#define REG_COM12
#define COM12_HREF
#define REG_COM13
#define COM13_GAMMA
#define COM13_UVSAT
#define COM13_UVSWAP
#define REG_COM14
#define COM14_DCWEN
#define REG_EDGE
#define REG_COM15
#define COM15_R10F0
#define COM15_R01FE
#define COM15_R00FF
#define COM15_RGB565
#define COM15_RGB555
#define REG_COM16
#define COM16_AWBGAIN
#define REG_COM17
#define COM17_AECWIN
#define COM17_CBAR

/*
 * This matrix defines how the colors are generated, must be
 * tweaked to adjust hue and saturation.
 *
 * Order: v-red, v-green, v-blue, u-red, u-green, u-blue
 *
 * They are nine-bit signed quantities, with the sign bit
 * stored in 0x58.  Sign for v-red is bit 0, and up from there.
 */
#define REG_CMATRIX_BASE
#define CMATRIX_LEN
#define REG_CMATRIX_SIGN


#define REG_BRIGHT
#define REG_CONTRAS

#define REG_GFIX

#define REG_DBLV
#define DBLV_BYPASS
#define DBLV_X4
#define DBLV_X6
#define DBLV_X8

#define REG_SCALING_XSC
#define TEST_PATTTERN_0
#define REG_SCALING_YSC
#define TEST_PATTTERN_1

#define REG_REG76
#define R76_BLKPCOR
#define R76_WHTPCOR

#define REG_RGB444
#define R444_ENABLE
#define R444_RGBX

#define REG_HAECC1
#define REG_HAECC2

#define REG_BD50MAX
#define REG_HAECC3
#define REG_HAECC4
#define REG_HAECC5
#define REG_HAECC6
#define REG_HAECC7
#define REG_BD60MAX

struct ov7670_win_size {};

struct ov7670_devtype {};

/*
 * Information we maintain about a known sensor.
 */
struct ov7670_format_struct;  /* coming later */
struct ov7670_info {};

static inline struct ov7670_info *to_state(struct v4l2_subdev *sd)
{}

static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
{}



/*
 * The default register settings, as obtained from OmniVision.  There
 * is really no making sense of most of these - lots of "reserved" values
 * and such.
 *
 * These settings give VGA YUYV.
 */

struct regval_list {};

static struct regval_list ov7670_default_regs[] =;


/*
 * Here we'll try to encapsulate the changes for just the output
 * video format.
 *
 * RGB656 and YUV422 come from OV; RGB444 is homebrewed.
 *
 * IMPORTANT RULE: the first entry must be for COM7, see ov7670_s_fmt for why.
 */


static struct regval_list ov7670_fmt_yuv422[] =;

static struct regval_list ov7670_fmt_rgb565[] =;

static struct regval_list ov7670_fmt_rgb444[] =;

static struct regval_list ov7670_fmt_raw[] =;



/*
 * Low-level register I/O.
 *
 * Note that there are two versions of these.  On the XO 1, the
 * i2c controller only does SMBUS, so that's what we use.  The
 * ov7670 is not really an SMBUS device, though, so the communication
 * is not always entirely reliable.
 */
static int ov7670_read_smbus(struct v4l2_subdev *sd, unsigned char reg,
		unsigned char *value)
{}


static int ov7670_write_smbus(struct v4l2_subdev *sd, unsigned char reg,
		unsigned char value)
{}

/*
 * On most platforms, we'd rather do straight i2c I/O.
 */
static int ov7670_read_i2c(struct v4l2_subdev *sd, unsigned char reg,
		unsigned char *value)
{}


static int ov7670_write_i2c(struct v4l2_subdev *sd, unsigned char reg,
		unsigned char value)
{}

static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg,
		unsigned char *value)
{}

static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg,
		unsigned char value)
{}

static int ov7670_update_bits(struct v4l2_subdev *sd, unsigned char reg,
		unsigned char mask, unsigned char value)
{}

/*
 * Write a list of register settings; ff/ff stops the process.
 */
static int ov7670_write_array(struct v4l2_subdev *sd, struct regval_list *vals)
{}


/*
 * Stuff that knows about the sensor.
 */
static int ov7670_reset(struct v4l2_subdev *sd, u32 val)
{}


static int ov7670_init(struct v4l2_subdev *sd, u32 val)
{}

static int ov7670_detect(struct v4l2_subdev *sd)
{}


/*
 * Store information about the video data format.  The color matrix
 * is deeply tied into the format, so keep the relevant values here.
 * The magic matrix numbers come from OmniVision.
 */
static struct ov7670_format_struct {} ov7670_formats[] =;
#define N_OV7670_FMTS


/*
 * Then there is the issue of window sizes.  Try to capture the info here.
 */

/*
 * QCIF mode is done (by OV) in a very strange way - it actually looks like
 * VGA with weird scaling options - they do *not* use the canned QCIF mode
 * which is allegedly provided by the sensor.  So here's the weird register
 * settings.
 */
static struct regval_list ov7670_qcif_regs[] =;

static struct ov7670_win_size ov7670_win_sizes[] =;

static struct ov7670_win_size ov7675_win_sizes[] =;

static void ov7675_get_framerate(struct v4l2_subdev *sd,
				 struct v4l2_fract *tpf)
{}

static int ov7675_apply_framerate(struct v4l2_subdev *sd)
{}

static int ov7675_set_framerate(struct v4l2_subdev *sd,
				 struct v4l2_fract *tpf)
{}

static void ov7670_get_framerate_legacy(struct v4l2_subdev *sd,
				 struct v4l2_fract *tpf)
{}

static int ov7670_set_framerate_legacy(struct v4l2_subdev *sd,
					struct v4l2_fract *tpf)
{}

/*
 * Store a set of start/stop values into the camera.
 */
static int ov7670_set_hw(struct v4l2_subdev *sd, int hstart, int hstop,
		int vstart, int vstop)
{}


static int ov7670_enum_mbus_code(struct v4l2_subdev *sd,
		struct v4l2_subdev_state *sd_state,
		struct v4l2_subdev_mbus_code_enum *code)
{}

static int ov7670_try_fmt_internal(struct v4l2_subdev *sd,
		struct v4l2_mbus_framefmt *fmt,
		struct ov7670_format_struct **ret_fmt,
		struct ov7670_win_size **ret_wsize)
{}

static int ov7670_apply_fmt(struct v4l2_subdev *sd)
{}

/*
 * Set a format.
 */
static int ov7670_set_fmt(struct v4l2_subdev *sd,
		struct v4l2_subdev_state *sd_state,
		struct v4l2_subdev_format *format)
{}

static int ov7670_get_fmt(struct v4l2_subdev *sd,
			  struct v4l2_subdev_state *sd_state,
			  struct v4l2_subdev_format *format)
{}

/*
 * Implement G/S_PARM.  There is a "high quality" mode we could try
 * to do someday; for now, we just do the frame rate tweak.
 */
static int ov7670_get_frame_interval(struct v4l2_subdev *sd,
				     struct v4l2_subdev_state *sd_state,
				     struct v4l2_subdev_frame_interval *ival)
{}

static int ov7670_set_frame_interval(struct v4l2_subdev *sd,
				     struct v4l2_subdev_state *sd_state,
				     struct v4l2_subdev_frame_interval *ival)
{}


/*
 * Frame intervals.  Since frame rates are controlled with the clock
 * divider, we can only do 30/n for integer n values.  So no continuous
 * or stepwise options.  Here we just pick a handful of logical values.
 */

static int ov7670_frame_rates[] =;

static int ov7670_enum_frame_interval(struct v4l2_subdev *sd,
				      struct v4l2_subdev_state *sd_state,
				      struct v4l2_subdev_frame_interval_enum *fie)
{}

/*
 * Frame size enumeration
 */
static int ov7670_enum_frame_size(struct v4l2_subdev *sd,
				  struct v4l2_subdev_state *sd_state,
				  struct v4l2_subdev_frame_size_enum *fse)
{}

/*
 * Code for dealing with controls.
 */

static int ov7670_store_cmatrix(struct v4l2_subdev *sd,
		int matrix[CMATRIX_LEN])
{}


/*
 * Hue also requires messing with the color matrix.  It also requires
 * trig functions, which tend not to be well supported in the kernel.
 * So here is a simple table of sine values, 0-90 degrees, in steps
 * of five degrees.  Values are multiplied by 1000.
 *
 * The following naive approximate trig functions require an argument
 * carefully limited to -180 <= theta <= 180.
 */
#define SIN_STEP
static const int ov7670_sin_table[] =;

static int ov7670_sine(int theta)
{}

static int ov7670_cosine(int theta)
{}




static void ov7670_calc_cmatrix(struct ov7670_info *info,
		int matrix[CMATRIX_LEN], int sat, int hue)
{}



static int ov7670_s_sat_hue(struct v4l2_subdev *sd, int sat, int hue)
{}


/*
 * Some weird registers seem to store values in a sign/magnitude format!
 */

static unsigned char ov7670_abs_to_sm(unsigned char v)
{}

static int ov7670_s_brightness(struct v4l2_subdev *sd, int value)
{}

static int ov7670_s_contrast(struct v4l2_subdev *sd, int value)
{}

static int ov7670_s_hflip(struct v4l2_subdev *sd, int value)
{}

static int ov7670_s_vflip(struct v4l2_subdev *sd, int value)
{}

/*
 * GAIN is split between REG_GAIN and REG_VREF[7:6].  If one believes
 * the data sheet, the VREF parts should be the most significant, but
 * experience shows otherwise.  There seems to be little value in
 * messing with the VREF bits, so we leave them alone.
 */
static int ov7670_g_gain(struct v4l2_subdev *sd, __s32 *value)
{}

static int ov7670_s_gain(struct v4l2_subdev *sd, int value)
{}

/*
 * Tweak autogain.
 */
static int ov7670_s_autogain(struct v4l2_subdev *sd, int value)
{}

static int ov7670_s_exp(struct v4l2_subdev *sd, int value)
{}

/*
 * Tweak autoexposure.
 */
static int ov7670_s_autoexp(struct v4l2_subdev *sd,
		enum v4l2_exposure_auto_type value)
{}

static const char * const ov7670_test_pattern_menu[] =;

static int ov7670_s_test_pattern(struct v4l2_subdev *sd, int value)
{}

static int ov7670_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
{}

static int ov7670_s_ctrl(struct v4l2_ctrl *ctrl)
{}

static const struct v4l2_ctrl_ops ov7670_ctrl_ops =;

#ifdef CONFIG_VIDEO_ADV_DEBUG
static int ov7670_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
{}

static int ov7670_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)
{}
#endif

static void ov7670_power_on(struct v4l2_subdev *sd)
{}

static void ov7670_power_off(struct v4l2_subdev *sd)
{}

static int ov7670_s_power(struct v4l2_subdev *sd, int on)
{}

static void ov7670_get_default_format(struct v4l2_subdev *sd,
				      struct v4l2_mbus_framefmt *format)
{}

static int ov7670_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
{}

/* ----------------------------------------------------------------------- */

static const struct v4l2_subdev_core_ops ov7670_core_ops =;

static const struct v4l2_subdev_pad_ops ov7670_pad_ops =;

static const struct v4l2_subdev_ops ov7670_ops =;

static const struct v4l2_subdev_internal_ops ov7670_subdev_internal_ops =;

/* ----------------------------------------------------------------------- */

static int ov7670_init_gpio(struct i2c_client *client, struct ov7670_info *info)
{}

/*
 * ov7670_parse_dt() - Parse device tree to collect mbus configuration
 *			properties
 */
static int ov7670_parse_dt(struct device *dev,
			   struct ov7670_info *info)
{}

static int ov7670_probe(struct i2c_client *client)
{}

static void ov7670_remove(struct i2c_client *client)
{}

static const struct ov7670_devtype ov7670_devdata =;

static const struct ov7670_devtype ov7675_devdata =;

static const struct i2c_device_id ov7670_id[] =;
MODULE_DEVICE_TABLE(i2c, ov7670_id);

static const struct of_device_id ov7670_of_match[] =;
MODULE_DEVICE_TABLE(of, ov7670_of_match);

static struct i2c_driver ov7670_driver =;

module_i2c_driver();