Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/drivers/media/i2c/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 80 kB image not shown  

Quelle  ov8865.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Copyright 2020 Kévin L'hôpital <kevin.lhopital@bootlin.com>
 * Copyright 2020 Bootlin
 * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
 */


#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/of_graph.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/videodev2.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-fwnode.h>
#include <media/v4l2-image-sizes.h>
#include <media/v4l2-mediabus.h>

/* Register definitions */

/* System */

#define OV8865_SW_STANDBY_REG   0x100
#define OV8865_SW_STANDBY_STREAM_ON  BIT(0)

#define OV8865_SW_RESET_REG   0x103
#define OV8865_SW_RESET_RESET   BIT(0)

#define OV8865_PLL_CTRL0_REG   0x300
#define OV8865_PLL_CTRL0_PRE_DIV(v)  ((v) & GENMASK(2, 0))
#define OV8865_PLL_CTRL1_REG   0x301
#define OV8865_PLL_CTRL1_MUL_H(v)  (((v) & GENMASK(9, 8)) >> 8)
#define OV8865_PLL_CTRL2_REG   0x302
#define OV8865_PLL_CTRL2_MUL_L(v)  ((v) & GENMASK(7, 0))
#define OV8865_PLL_CTRL3_REG   0x303
#define OV8865_PLL_CTRL3_M_DIV(v)  (((v) - 1) & GENMASK(3, 0))
#define OV8865_PLL_CTRL4_REG   0x304
#define OV8865_PLL_CTRL4_MIPI_DIV(v)  ((v) & GENMASK(1, 0))
#define OV8865_PLL_CTRL5_REG   0x305
#define OV8865_PLL_CTRL5_SYS_PRE_DIV(v)  ((v) & GENMASK(1, 0))
#define OV8865_PLL_CTRL6_REG   0x306
#define OV8865_PLL_CTRL6_SYS_DIV(v)  (((v) - 1) & BIT(0))

#define OV8865_PLL_CTRL8_REG   0x308
#define OV8865_PLL_CTRL9_REG   0x309
#define OV8865_PLL_CTRLA_REG   0x30a
#define OV8865_PLL_CTRLA_PRE_DIV_HALF(v) (((v) - 1) & BIT(0))
#define OV8865_PLL_CTRLB_REG   0x30b
#define OV8865_PLL_CTRLB_PRE_DIV(v)  ((v) & GENMASK(2, 0))
#define OV8865_PLL_CTRLC_REG   0x30c
#define OV8865_PLL_CTRLC_MUL_H(v)  (((v) & GENMASK(9, 8)) >> 8)
#define OV8865_PLL_CTRLD_REG   0x30d
#define OV8865_PLL_CTRLD_MUL_L(v)  ((v) & GENMASK(7, 0))
#define OV8865_PLL_CTRLE_REG   0x30e
#define OV8865_PLL_CTRLE_SYS_DIV(v)  ((v) & GENMASK(2, 0))
#define OV8865_PLL_CTRLF_REG   0x30f
#define OV8865_PLL_CTRLF_SYS_PRE_DIV(v)  (((v) - 1) & GENMASK(3, 0))
#define OV8865_PLL_CTRL10_REG   0x310
#define OV8865_PLL_CTRL11_REG   0x311
#define OV8865_PLL_CTRL12_REG   0x312
#define OV8865_PLL_CTRL12_PRE_DIV_HALF(v) ((((v) - 1) << 4) & BIT(4))
#define OV8865_PLL_CTRL12_DAC_DIV(v)  (((v) - 1) & GENMASK(3, 0))

#define OV8865_PLL_CTRL1B_REG   0x31b
#define OV8865_PLL_CTRL1C_REG   0x31c

#define OV8865_PLL_CTRL1E_REG   0x31e
#define OV8865_PLL_CTRL1E_PLL1_NO_LAT  BIT(3)

#define OV8865_PAD_OEN0_REG   0x3000

#define OV8865_PAD_OEN2_REG   0x3002

#define OV8865_CLK_RST5_REG   0x3005

#define OV8865_CHIP_ID_HH_REG   0x300a
#define OV8865_CHIP_ID_HH_VALUE   0x00
#define OV8865_CHIP_ID_H_REG   0x300b
#define OV8865_CHIP_ID_H_VALUE   0x88
#define OV8865_CHIP_ID_L_REG   0x300c
#define OV8865_CHIP_ID_L_VALUE   0x65
#define OV8865_PAD_OUT2_REG   0x300d

#define OV8865_PAD_SEL2_REG   0x3010
#define OV8865_PAD_PK_REG   0x3011
#define OV8865_PAD_PK_DRIVE_STRENGTH_1X  (0 << 5)
#define OV8865_PAD_PK_DRIVE_STRENGTH_2X  (1 << 5)
#define OV8865_PAD_PK_DRIVE_STRENGTH_3X  (2 << 5)
#define OV8865_PAD_PK_DRIVE_STRENGTH_4X  (3 << 5)

#define OV8865_PUMP_CLK_DIV_REG   0x3015
#define OV8865_PUMP_CLK_DIV_PUMP_N(v)  (((v) << 4) & GENMASK(6, 4))
#define OV8865_PUMP_CLK_DIV_PUMP_P(v)  ((v) & GENMASK(2, 0))

#define OV8865_MIPI_SC_CTRL0_REG  0x3018
#define OV8865_MIPI_SC_CTRL0_LANES(v)  ((((v) - 1) << 5) & \
       GENMASK(7, 5))
#define OV8865_MIPI_SC_CTRL0_MIPI_EN  BIT(4)
#define OV8865_MIPI_SC_CTRL0_UNKNOWN  BIT(1)
#define OV8865_MIPI_SC_CTRL0_LANES_PD_MIPI BIT(0)
#define OV8865_MIPI_SC_CTRL1_REG  0x3019
#define OV8865_CLK_RST0_REG   0x301a
#define OV8865_CLK_RST1_REG   0x301b
#define OV8865_CLK_RST2_REG   0x301c
#define OV8865_CLK_RST3_REG   0x301d
#define OV8865_CLK_RST4_REG   0x301e

#define OV8865_PCLK_SEL_REG   0x3020
#define OV8865_PCLK_SEL_PCLK_DIV_MASK  BIT(3)
#define OV8865_PCLK_SEL_PCLK_DIV(v)  ((((v) - 1) << 3) & BIT(3))

#define OV8865_MISC_CTRL_REG   0x3021
#define OV8865_MIPI_SC_CTRL2_REG  0x3022
#define OV8865_MIPI_SC_CTRL2_CLK_LANES_PD_MIPI BIT(1)
#define OV8865_MIPI_SC_CTRL2_PD_MIPI_RST_SYNC BIT(0)

#define OV8865_MIPI_BIT_SEL_REG   0x3031
#define OV8865_MIPI_BIT_SEL(v)   (((v) << 0) & GENMASK(4, 0))
#define OV8865_CLK_SEL0_REG   0x3032
#define OV8865_CLK_SEL0_PLL1_SYS_SEL(v)  (((v) << 7) & BIT(7))
#define OV8865_CLK_SEL1_REG   0x3033
#define OV8865_CLK_SEL1_MIPI_EOF  BIT(5)
#define OV8865_CLK_SEL1_UNKNOWN   BIT(2)
#define OV8865_CLK_SEL1_PLL_SCLK_SEL_MASK BIT(1)
#define OV8865_CLK_SEL1_PLL_SCLK_SEL(v)  (((v) << 1) & BIT(1))

#define OV8865_SCLK_CTRL_REG   0x3106
#define OV8865_SCLK_CTRL_SCLK_DIV(v)  (((v) << 4) & GENMASK(7, 4))
#define OV8865_SCLK_CTRL_SCLK_PRE_DIV(v) (((v) << 2) & GENMASK(3, 2))
#define OV8865_SCLK_CTRL_UNKNOWN  BIT(0)

/* Exposure/gain */

#define OV8865_EXPOSURE_CTRL_HH_REG  0x3500
#define OV8865_EXPOSURE_CTRL_HH(v)  (((v) & GENMASK(19, 16)) >> 16)
#define OV8865_EXPOSURE_CTRL_H_REG  0x3501
#define OV8865_EXPOSURE_CTRL_H(v)  (((v) & GENMASK(15, 8)) >> 8)
#define OV8865_EXPOSURE_CTRL_L_REG  0x3502
#define OV8865_EXPOSURE_CTRL_L(v)  ((v) & GENMASK(7, 0))
#define OV8865_EXPOSURE_GAIN_MANUAL_REG  0x3503
#define OV8865_INTEGRATION_TIME_MARGIN  8

#define OV8865_GAIN_CTRL_H_REG   0x3508
#define OV8865_GAIN_CTRL_H(v)   (((v) & GENMASK(12, 8)) >> 8)
#define OV8865_GAIN_CTRL_L_REG   0x3509
#define OV8865_GAIN_CTRL_L(v)   ((v) & GENMASK(7, 0))

/* Timing */

#define OV8865_CROP_START_X_H_REG  0x3800
#define OV8865_CROP_START_X_H(v)  (((v) & GENMASK(11, 8)) >> 8)
#define OV8865_CROP_START_X_L_REG  0x3801
#define OV8865_CROP_START_X_L(v)  ((v) & GENMASK(7, 0))
#define OV8865_CROP_START_Y_H_REG  0x3802
#define OV8865_CROP_START_Y_H(v)  (((v) & GENMASK(11, 8)) >> 8)
#define OV8865_CROP_START_Y_L_REG  0x3803
#define OV8865_CROP_START_Y_L(v)  ((v) & GENMASK(7, 0))
#define OV8865_CROP_END_X_H_REG   0x3804
#define OV8865_CROP_END_X_H(v)   (((v) & GENMASK(11, 8)) >> 8)
#define OV8865_CROP_END_X_L_REG   0x3805
#define OV8865_CROP_END_X_L(v)   ((v) & GENMASK(7, 0))
#define OV8865_CROP_END_Y_H_REG   0x3806
#define OV8865_CROP_END_Y_H(v)   (((v) & GENMASK(11, 8)) >> 8)
#define OV8865_CROP_END_Y_L_REG   0x3807
#define OV8865_CROP_END_Y_L(v)   ((v) & GENMASK(7, 0))
#define OV8865_OUTPUT_SIZE_X_H_REG  0x3808
#define OV8865_OUTPUT_SIZE_X_H(v)  (((v) & GENMASK(11, 8)) >> 8)
#define OV8865_OUTPUT_SIZE_X_L_REG  0x3809
#define OV8865_OUTPUT_SIZE_X_L(v)  ((v) & GENMASK(7, 0))
#define OV8865_OUTPUT_SIZE_Y_H_REG  0x380a
#define OV8865_OUTPUT_SIZE_Y_H(v)  (((v) & GENMASK(11, 8)) >> 8)
#define OV8865_OUTPUT_SIZE_Y_L_REG  0x380b
#define OV8865_OUTPUT_SIZE_Y_L(v)  ((v) & GENMASK(7, 0))
#define OV8865_HTS_H_REG   0x380c
#define OV8865_HTS_H(v)    (((v) & GENMASK(11, 8)) >> 8)
#define OV8865_HTS_L_REG   0x380d
#define OV8865_HTS_L(v)    ((v) & GENMASK(7, 0))
#define OV8865_VTS_H_REG   0x380e
#define OV8865_VTS_H(v)    (((v) & GENMASK(11, 8)) >> 8)
#define OV8865_VTS_L_REG   0x380f
#define OV8865_VTS_L(v)    ((v) & GENMASK(7, 0))
#define OV8865_TIMING_MAX_VTS   0xffff
#define OV8865_TIMING_MIN_VTS   0x04
#define OV8865_OFFSET_X_H_REG   0x3810
#define OV8865_OFFSET_X_H(v)   (((v) & GENMASK(15, 8)) >> 8)
#define OV8865_OFFSET_X_L_REG   0x3811
#define OV8865_OFFSET_X_L(v)   ((v) & GENMASK(7, 0))
#define OV8865_OFFSET_Y_H_REG   0x3812
#define OV8865_OFFSET_Y_H(v)   (((v) & GENMASK(14, 8)) >> 8)
#define OV8865_OFFSET_Y_L_REG   0x3813
#define OV8865_OFFSET_Y_L(v)   ((v) & GENMASK(7, 0))
#define OV8865_INC_X_ODD_REG   0x3814
#define OV8865_INC_X_ODD(v)   ((v) & GENMASK(4, 0))
#define OV8865_INC_X_EVEN_REG   0x3815
#define OV8865_INC_X_EVEN(v)   ((v) & GENMASK(4, 0))
#define OV8865_VSYNC_START_H_REG  0x3816
#define OV8865_VSYNC_START_H(v)   (((v) & GENMASK(15, 8)) >> 8)
#define OV8865_VSYNC_START_L_REG  0x3817
#define OV8865_VSYNC_START_L(v)   ((v) & GENMASK(7, 0))
#define OV8865_VSYNC_END_H_REG   0x3818
#define OV8865_VSYNC_END_H(v)   (((v) & GENMASK(15, 8)) >> 8)
#define OV8865_VSYNC_END_L_REG   0x3819
#define OV8865_VSYNC_END_L(v)   ((v) & GENMASK(7, 0))
#define OV8865_HSYNC_FIRST_H_REG  0x381a
#define OV8865_HSYNC_FIRST_H(v)   (((v) & GENMASK(15, 8)) >> 8)
#define OV8865_HSYNC_FIRST_L_REG  0x381b
#define OV8865_HSYNC_FIRST_L(v)   ((v) & GENMASK(7, 0))

#define OV8865_FORMAT1_REG   0x3820
#define OV8865_FORMAT1_FLIP_VERT_ISP_EN  BIT(2)
#define OV8865_FORMAT1_FLIP_VERT_SENSOR_EN BIT(1)
#define OV8865_FORMAT2_REG   0x3821
#define OV8865_FORMAT2_HSYNC_EN   BIT(6)
#define OV8865_FORMAT2_FST_VBIN_EN  BIT(5)
#define OV8865_FORMAT2_FST_HBIN_EN  BIT(4)
#define OV8865_FORMAT2_ISP_HORZ_VAR2_EN  BIT(3)
#define OV8865_FORMAT2_FLIP_HORZ_ISP_EN  BIT(2)
#define OV8865_FORMAT2_FLIP_HORZ_SENSOR_EN BIT(1)
#define OV8865_FORMAT2_SYNC_HBIN_EN  BIT(0)

#define OV8865_INC_Y_ODD_REG   0x382a
#define OV8865_INC_Y_ODD(v)   ((v) & GENMASK(4, 0))
#define OV8865_INC_Y_EVEN_REG   0x382b
#define OV8865_INC_Y_EVEN(v)   ((v) & GENMASK(4, 0))

#define OV8865_ABLC_NUM_REG   0x3830
#define OV8865_ABLC_NUM(v)   ((v) & GENMASK(4, 0))

#define OV8865_ZLINE_NUM_REG   0x3836
#define OV8865_ZLINE_NUM(v)   ((v) & GENMASK(4, 0))

#define OV8865_AUTO_SIZE_CTRL_REG  0x3841
#define OV8865_AUTO_SIZE_CTRL_OFFSET_Y_REG BIT(5)
#define OV8865_AUTO_SIZE_CTRL_OFFSET_X_REG BIT(4)
#define OV8865_AUTO_SIZE_CTRL_CROP_END_Y_REG BIT(3)
#define OV8865_AUTO_SIZE_CTRL_CROP_END_X_REG BIT(2)
#define OV8865_AUTO_SIZE_CTRL_CROP_START_Y_REG BIT(1)
#define OV8865_AUTO_SIZE_CTRL_CROP_START_X_REG BIT(0)
#define OV8865_AUTO_SIZE_X_OFFSET_H_REG  0x3842
#define OV8865_AUTO_SIZE_X_OFFSET_L_REG  0x3843
#define OV8865_AUTO_SIZE_Y_OFFSET_H_REG  0x3844
#define OV8865_AUTO_SIZE_Y_OFFSET_L_REG  0x3845
#define OV8865_AUTO_SIZE_BOUNDARIES_REG  0x3846
#define OV8865_AUTO_SIZE_BOUNDARIES_Y(v) (((v) << 4) & GENMASK(7, 4))
#define OV8865_AUTO_SIZE_BOUNDARIES_X(v) ((v) & GENMASK(3, 0))

/* PSRAM */

#define OV8865_PSRAM_CTRL8_REG   0x3f08

/* Black Level */

#define OV8865_BLC_CTRL0_REG   0x4000
#define OV8865_BLC_CTRL0_TRIG_RANGE_EN  BIT(7)
#define OV8865_BLC_CTRL0_TRIG_FORMAT_EN  BIT(6)
#define OV8865_BLC_CTRL0_TRIG_GAIN_EN  BIT(5)
#define OV8865_BLC_CTRL0_TRIG_EXPOSURE_EN BIT(4)
#define OV8865_BLC_CTRL0_TRIG_MANUAL_EN  BIT(3)
#define OV8865_BLC_CTRL0_FREEZE_EN  BIT(2)
#define OV8865_BLC_CTRL0_ALWAYS_EN  BIT(1)
#define OV8865_BLC_CTRL0_FILTER_EN  BIT(0)
#define OV8865_BLC_CTRL1_REG   0x4001
#define OV8865_BLC_CTRL1_DITHER_EN  BIT(7)
#define OV8865_BLC_CTRL1_ZERO_LINE_DIFF_EN BIT(6)
#define OV8865_BLC_CTRL1_COL_SHIFT_256  (0 << 4)
#define OV8865_BLC_CTRL1_COL_SHIFT_128  (1 << 4)
#define OV8865_BLC_CTRL1_COL_SHIFT_64  (2 << 4)
#define OV8865_BLC_CTRL1_COL_SHIFT_32  (3 << 4)
#define OV8865_BLC_CTRL1_OFFSET_LIMIT_EN BIT(2)
#define OV8865_BLC_CTRL1_COLUMN_CANCEL_EN BIT(1)
#define OV8865_BLC_CTRL2_REG   0x4002
#define OV8865_BLC_CTRL3_REG   0x4003
#define OV8865_BLC_CTRL4_REG   0x4004
#define OV8865_BLC_CTRL5_REG   0x4005
#define OV8865_BLC_CTRL6_REG   0x4006
#define OV8865_BLC_CTRL7_REG   0x4007
#define OV8865_BLC_CTRL8_REG   0x4008
#define OV8865_BLC_CTRL9_REG   0x4009
#define OV8865_BLC_CTRLA_REG   0x400a
#define OV8865_BLC_CTRLB_REG   0x400b
#define OV8865_BLC_CTRLC_REG   0x400c
#define OV8865_BLC_CTRLD_REG   0x400d
#define OV8865_BLC_CTRLD_OFFSET_TRIGGER(v) ((v) & GENMASK(7, 0))

#define OV8865_BLC_CTRL1F_REG   0x401f
#define OV8865_BLC_CTRL1F_RB_REVERSE  BIT(3)
#define OV8865_BLC_CTRL1F_INTERPOL_X_EN  BIT(2)
#define OV8865_BLC_CTRL1F_INTERPOL_Y_EN  BIT(1)

#define OV8865_BLC_ANCHOR_LEFT_START_H_REG 0x4020
#define OV8865_BLC_ANCHOR_LEFT_START_H(v) (((v) & GENMASK(11, 8)) >> 8)
#define OV8865_BLC_ANCHOR_LEFT_START_L_REG 0x4021
#define OV8865_BLC_ANCHOR_LEFT_START_L(v) ((v) & GENMASK(7, 0))
#define OV8865_BLC_ANCHOR_LEFT_END_H_REG 0x4022
#define OV8865_BLC_ANCHOR_LEFT_END_H(v)  (((v) & GENMASK(11, 8)) >> 8)
#define OV8865_BLC_ANCHOR_LEFT_END_L_REG 0x4023
#define OV8865_BLC_ANCHOR_LEFT_END_L(v)  ((v) & GENMASK(7, 0))
#define OV8865_BLC_ANCHOR_RIGHT_START_H_REG 0x4024
#define OV8865_BLC_ANCHOR_RIGHT_START_H(v) (((v) & GENMASK(11, 8)) >> 8)
#define OV8865_BLC_ANCHOR_RIGHT_START_L_REG 0x4025
#define OV8865_BLC_ANCHOR_RIGHT_START_L(v) ((v) & GENMASK(7, 0))
#define OV8865_BLC_ANCHOR_RIGHT_END_H_REG 0x4026
#define OV8865_BLC_ANCHOR_RIGHT_END_H(v) (((v) & GENMASK(11, 8)) >> 8)
#define OV8865_BLC_ANCHOR_RIGHT_END_L_REG 0x4027
#define OV8865_BLC_ANCHOR_RIGHT_END_L(v) ((v) & GENMASK(7, 0))

#define OV8865_BLC_TOP_ZLINE_START_REG  0x4028
#define OV8865_BLC_TOP_ZLINE_START(v)  ((v) & GENMASK(5, 0))
#define OV8865_BLC_TOP_ZLINE_NUM_REG  0x4029
#define OV8865_BLC_TOP_ZLINE_NUM(v)  ((v) & GENMASK(4, 0))
#define OV8865_BLC_TOP_BLKLINE_START_REG 0x402a
#define OV8865_BLC_TOP_BLKLINE_START(v)  ((v) & GENMASK(5, 0))
#define OV8865_BLC_TOP_BLKLINE_NUM_REG  0x402b
#define OV8865_BLC_TOP_BLKLINE_NUM(v)  ((v) & GENMASK(4, 0))
#define OV8865_BLC_BOT_ZLINE_START_REG  0x402c
#define OV8865_BLC_BOT_ZLINE_START(v)  ((v) & GENMASK(5, 0))
#define OV8865_BLC_BOT_ZLINE_NUM_REG  0x402d
#define OV8865_BLC_BOT_ZLINE_NUM(v)  ((v) & GENMASK(4, 0))
#define OV8865_BLC_BOT_BLKLINE_START_REG 0x402e
#define OV8865_BLC_BOT_BLKLINE_START(v)  ((v) & GENMASK(5, 0))
#define OV8865_BLC_BOT_BLKLINE_NUM_REG  0x402f
#define OV8865_BLC_BOT_BLKLINE_NUM(v)  ((v) & GENMASK(4, 0))

#define OV8865_BLC_OFFSET_LIMIT_REG  0x4034
#define OV8865_BLC_OFFSET_LIMIT(v)  ((v) & GENMASK(7, 0))

/* VFIFO */

#define OV8865_VFIFO_READ_START_H_REG  0x4600
#define OV8865_VFIFO_READ_START_H(v)  (((v) & GENMASK(15, 8)) >> 8)
#define OV8865_VFIFO_READ_START_L_REG  0x4601
#define OV8865_VFIFO_READ_START_L(v)  ((v) & GENMASK(7, 0))

/* MIPI */

#define OV8865_MIPI_CTRL0_REG   0x4800
#define OV8865_MIPI_CTRL1_REG   0x4801
#define OV8865_MIPI_CTRL2_REG   0x4802
#define OV8865_MIPI_CTRL3_REG   0x4803
#define OV8865_MIPI_CTRL4_REG   0x4804
#define OV8865_MIPI_CTRL5_REG   0x4805
#define OV8865_MIPI_CTRL6_REG   0x4806
#define OV8865_MIPI_CTRL7_REG   0x4807
#define OV8865_MIPI_CTRL8_REG   0x4808

#define OV8865_MIPI_FCNT_MAX_H_REG  0x4810
#define OV8865_MIPI_FCNT_MAX_L_REG  0x4811

#define OV8865_MIPI_CTRL13_REG   0x4813
#define OV8865_MIPI_CTRL14_REG   0x4814
#define OV8865_MIPI_CTRL15_REG   0x4815
#define OV8865_MIPI_EMBEDDED_DT_REG  0x4816

#define OV8865_MIPI_HS_ZERO_MIN_H_REG  0x4818
#define OV8865_MIPI_HS_ZERO_MIN_L_REG  0x4819
#define OV8865_MIPI_HS_TRAIL_MIN_H_REG  0x481a
#define OV8865_MIPI_HS_TRAIL_MIN_L_REG  0x481b
#define OV8865_MIPI_CLK_ZERO_MIN_H_REG  0x481c
#define OV8865_MIPI_CLK_ZERO_MIN_L_REG  0x481d
#define OV8865_MIPI_CLK_PREPARE_MAX_REG  0x481e
#define OV8865_MIPI_CLK_PREPARE_MIN_REG  0x481f
#define OV8865_MIPI_CLK_POST_MIN_H_REG  0x4820
#define OV8865_MIPI_CLK_POST_MIN_L_REG  0x4821
#define OV8865_MIPI_CLK_TRAIL_MIN_H_REG  0x4822
#define OV8865_MIPI_CLK_TRAIL_MIN_L_REG  0x4823
#define OV8865_MIPI_LPX_P_MIN_H_REG  0x4824
#define OV8865_MIPI_LPX_P_MIN_L_REG  0x4825
#define OV8865_MIPI_HS_PREPARE_MIN_REG  0x4826
#define OV8865_MIPI_HS_PREPARE_MAX_REG  0x4827
#define OV8865_MIPI_HS_EXIT_MIN_H_REG  0x4828
#define OV8865_MIPI_HS_EXIT_MIN_L_REG  0x4829
#define OV8865_MIPI_UI_HS_ZERO_MIN_REG  0x482a
#define OV8865_MIPI_UI_HS_TRAIL_MIN_REG  0x482b
#define OV8865_MIPI_UI_CLK_ZERO_MIN_REG  0x482c
#define OV8865_MIPI_UI_CLK_PREPARE_REG  0x482d
#define OV8865_MIPI_UI_CLK_POST_MIN_REG  0x482e
#define OV8865_MIPI_UI_CLK_TRAIL_MIN_REG 0x482f
#define OV8865_MIPI_UI_LPX_P_MIN_REG  0x4830
#define OV8865_MIPI_UI_HS_PREPARE_REG  0x4831
#define OV8865_MIPI_UI_HS_EXIT_MIN_REG  0x4832
#define OV8865_MIPI_PKT_START_SIZE_REG  0x4833

#define OV8865_MIPI_PCLK_PERIOD_REG  0x4837
#define OV8865_MIPI_LP_GPIO0_REG  0x4838
#define OV8865_MIPI_LP_GPIO1_REG  0x4839

#define OV8865_MIPI_CTRL3C_REG   0x483c
#define OV8865_MIPI_LP_GPIO4_REG  0x483d

#define OV8865_MIPI_CTRL4A_REG   0x484a
#define OV8865_MIPI_CTRL4B_REG   0x484b
#define OV8865_MIPI_CTRL4C_REG   0x484c
#define OV8865_MIPI_LANE_TEST_PATTERN_REG 0x484d
#define OV8865_MIPI_FRAME_END_DELAY_REG  0x484e
#define OV8865_MIPI_CLOCK_TEST_PATTERN_REG 0x484f
#define OV8865_MIPI_LANE_SEL01_REG  0x4850
#define OV8865_MIPI_LANE_SEL01_LANE0(v)  (((v) << 0) & GENMASK(2, 0))
#define OV8865_MIPI_LANE_SEL01_LANE1(v)  (((v) << 4) & GENMASK(6, 4))
#define OV8865_MIPI_LANE_SEL23_REG  0x4851
#define OV8865_MIPI_LANE_SEL23_LANE2(v)  (((v) << 0) & GENMASK(2, 0))
#define OV8865_MIPI_LANE_SEL23_LANE3(v)  (((v) << 4) & GENMASK(6, 4))

/* ISP */

#define OV8865_ISP_CTRL0_REG   0x5000
#define OV8865_ISP_CTRL0_LENC_EN  BIT(7)
#define OV8865_ISP_CTRL0_WHITE_BALANCE_EN BIT(4)
#define OV8865_ISP_CTRL0_DPC_BLACK_EN  BIT(2)
#define OV8865_ISP_CTRL0_DPC_WHITE_EN  BIT(1)
#define OV8865_ISP_CTRL1_REG   0x5001
#define OV8865_ISP_CTRL1_BLC_EN   BIT(0)
#define OV8865_ISP_CTRL2_REG   0x5002
#define OV8865_ISP_CTRL2_DEBUG   BIT(3)
#define OV8865_ISP_CTRL2_VARIOPIXEL_EN  BIT(2)
#define OV8865_ISP_CTRL2_VSYNC_LATCH_EN  BIT(0)
#define OV8865_ISP_CTRL3_REG   0x5003

#define OV8865_ISP_GAIN_RED_H_REG  0x5018
#define OV8865_ISP_GAIN_RED_H(v)  (((v) & GENMASK(13, 6)) >> 6)
#define OV8865_ISP_GAIN_RED_L_REG  0x5019
#define OV8865_ISP_GAIN_RED_L(v)  ((v) & GENMASK(5, 0))
#define OV8865_ISP_GAIN_GREEN_H_REG  0x501a
#define OV8865_ISP_GAIN_GREEN_H(v)  (((v) & GENMASK(13, 6)) >> 6)
#define OV8865_ISP_GAIN_GREEN_L_REG  0x501b
#define OV8865_ISP_GAIN_GREEN_L(v)  ((v) & GENMASK(5, 0))
#define OV8865_ISP_GAIN_BLUE_H_REG  0x501c
#define OV8865_ISP_GAIN_BLUE_H(v)  (((v) & GENMASK(13, 6)) >> 6)
#define OV8865_ISP_GAIN_BLUE_L_REG  0x501d
#define OV8865_ISP_GAIN_BLUE_L(v)  ((v) & GENMASK(5, 0))

/* VarioPixel */

#define OV8865_VAP_CTRL0_REG   0x5900
#define OV8865_VAP_CTRL1_REG   0x5901
#define OV8865_VAP_CTRL1_HSUB_COEF(v)  ((((v) - 1) << 2) & \
       GENMASK(3, 2))
#define OV8865_VAP_CTRL1_VSUB_COEF(v)  (((v) - 1) & GENMASK(1, 0))

/* Pre-DSP */

#define OV8865_PRE_CTRL0_REG   0x5e00
#define OV8865_PRE_CTRL0_PATTERN_EN  BIT(7)
#define OV8865_PRE_CTRL0_ROLLING_BAR_EN  BIT(6)
#define OV8865_PRE_CTRL0_TRANSPARENT_MODE BIT(5)
#define OV8865_PRE_CTRL0_SQUARES_BW_MODE BIT(4)
#define OV8865_PRE_CTRL0_PATTERN_COLOR_BARS 0
#define OV8865_PRE_CTRL0_PATTERN_RANDOM_DATA 1
#define OV8865_PRE_CTRL0_PATTERN_COLOR_SQUARES 2
#define OV8865_PRE_CTRL0_PATTERN_BLACK  3

/* Pixel Array */

#define OV8865_NATIVE_WIDTH   3296
#define OV8865_NATIVE_HEIGHT   2528
#define OV8865_ACTIVE_START_LEFT  16
#define OV8865_ACTIVE_START_TOP   40
#define OV8865_ACTIVE_WIDTH   3264
#define OV8865_ACTIVE_HEIGHT   2448

/* Macros */

#define ov8865_subdev_sensor(s) \
 container_of(s, struct ov8865_sensor, subdev)

#define ov8865_ctrl_subdev(c) \
 (&container_of((c)->handler, struct ov8865_sensor, \
         ctrls.handler)->subdev)

/* Data structures */

struct ov8865_register_value {
 u16 address;
 u8 value;
 unsigned int delay_ms;
};

/*
 * PLL1 Clock Tree:
 *
 * +-< EXTCLK
 * |
 * +-+ pll_pre_div_half (0x30a [0])
 *   |
 *   +-+ pll_pre_div (0x300 [2:0], special values:
 *     |              0: 1, 1: 1.5, 3: 2.5, 4: 3, 5: 4, 7: 8)
 *     +-+ pll_mul (0x301 [1:0], 0x302 [7:0])
 *       |
 *       +-+ m_div (0x303 [3:0])
 *       | |
 *       | +-> PHY_SCLK
 *       | |
 *       | +-+ mipi_div (0x304 [1:0], special values: 0: 4, 1: 5, 2: 6, 3: 8)
 *       |   |
 *       |   +-+ pclk_div (0x3020 [3])
 *       |     |
 *       |     +-> PCLK
 *       |
 *       +-+ sys_pre_div (0x305 [1:0], special values: 0: 3, 1: 4, 2: 5, 3: 6)
 *         |
 *         +-+ sys_div (0x306 [0])
 *           |
 *           +-+ sys_sel (0x3032 [7], 0: PLL1, 1: PLL2)
 *             |
 *             +-+ sclk_sel (0x3033 [1], 0: sys_sel, 1: PLL2 DAC_CLK)
 *               |
 *               +-+ sclk_pre_div (0x3106 [3:2], special values:
 *                 |               0: 1, 1: 2, 2: 4, 3: 1)
 *                 |
 *                 +-+ sclk_div (0x3106 [7:4], special values: 0: 1)
 *                   |
 *                   +-> SCLK
 */


struct ov8865_pll1_config {
 unsigned int pll_pre_div_half;
 unsigned int pll_pre_div;
 unsigned int pll_mul;
 unsigned int m_div;
 unsigned int mipi_div;
 unsigned int pclk_div;
 unsigned int sys_pre_div;
 unsigned int sys_div;
};

/*
 * PLL2 Clock Tree:
 *
 * +-< EXTCLK
 * |
 * +-+ pll_pre_div_half (0x312 [4])
 *   |
 *   +-+ pll_pre_div (0x30b [2:0], special values:
 *     |              0: 1, 1: 1.5, 3: 2.5, 4: 3, 5: 4, 7: 8)
 *     +-+ pll_mul (0x30c [1:0], 0x30d [7:0])
 *       |
 *       +-+ dac_div (0x312 [3:0])
 *       | |
 *       | +-> DAC_CLK
 *       |
 *       +-+ sys_pre_div (0x30f [3:0])
 *         |
 *         +-+ sys_div (0x30e [2:0], special values:
 *           |          0: 1, 1: 1.5, 3: 2.5, 4: 3, 5: 3.5, 6: 4, 7:5)
 *           |
 *           +-+ sys_sel (0x3032 [7], 0: PLL1, 1: PLL2)
 *             |
 *             +-+ sclk_sel (0x3033 [1], 0: sys_sel, 1: PLL2 DAC_CLK)
 *               |
 *               +-+ sclk_pre_div (0x3106 [3:2], special values:
 *                 |               0: 1, 1: 2, 2: 4, 3: 1)
 *                 |
 *                 +-+ sclk_div (0x3106 [7:4], special values: 0: 1)
 *                   |
 *                   +-> SCLK
 */


struct ov8865_pll2_config {
 unsigned int pll_pre_div_half;
 unsigned int pll_pre_div;
 unsigned int pll_mul;
 unsigned int dac_div;
 unsigned int sys_pre_div;
 unsigned int sys_div;
};

struct ov8865_sclk_config {
 unsigned int sys_sel;
 unsigned int sclk_sel;
 unsigned int sclk_pre_div;
 unsigned int sclk_div;
};

struct ov8865_pll_configs {
 const struct ov8865_pll1_config *pll1_config;
 const struct ov8865_pll2_config *pll2_config_native;
 const struct ov8865_pll2_config *pll2_config_binning;
};

/* Clock rate */

enum extclk_rate {
 OV8865_19_2_MHZ,
 OV8865_24_MHZ,
 OV8865_NUM_SUPPORTED_RATES
};

static const unsigned long supported_extclk_rates[] = {
 [OV8865_19_2_MHZ] = 19200000,
 [OV8865_24_MHZ] = 24000000,
};

/*
 * General formulas for (array-centered) mode calculation:
 * - photo_array_width = 3296
 * - crop_start_x = (photo_array_width - output_size_x) / 2
 * - crop_end_x = crop_start_x + offset_x + output_size_x - 1
 *
 * - photo_array_height = 2480
 * - crop_start_y = (photo_array_height - output_size_y) / 2
 * - crop_end_y = crop_start_y + offset_y + output_size_y - 1
 */


struct ov8865_mode {
 unsigned int crop_start_x;
 unsigned int offset_x;
 unsigned int output_size_x;
 unsigned int crop_end_x;
 unsigned int hts;

 unsigned int crop_start_y;
 unsigned int offset_y;
 unsigned int output_size_y;
 unsigned int crop_end_y;
 unsigned int vts;

 /* With auto size, only output and total sizes need to be set. */
 bool size_auto;
 unsigned int size_auto_boundary_x;
 unsigned int size_auto_boundary_y;

 bool binning_x;
 bool binning_y;
 bool variopixel;
 unsigned int variopixel_hsub_coef;
 unsigned int variopixel_vsub_coef;

 /* Bits for the format register, used for binning. */
 bool sync_hbin;
 bool horz_var2;

 unsigned int inc_x_odd;
 unsigned int inc_x_even;
 unsigned int inc_y_odd;
 unsigned int inc_y_even;

 unsigned int vfifo_read_start;

 unsigned int ablc_num;
 unsigned int zline_num;

 unsigned int blc_top_zero_line_start;
 unsigned int blc_top_zero_line_num;
 unsigned int blc_top_black_line_start;
 unsigned int blc_top_black_line_num;

 unsigned int blc_bottom_zero_line_start;
 unsigned int blc_bottom_zero_line_num;
 unsigned int blc_bottom_black_line_start;
 unsigned int blc_bottom_black_line_num;

 u8 blc_col_shift_mask;

 unsigned int blc_anchor_left_start;
 unsigned int blc_anchor_left_end;
 unsigned int blc_anchor_right_start;
 unsigned int blc_anchor_right_end;

 bool pll2_binning;

 const struct ov8865_register_value *register_values;
 unsigned int register_values_count;
};

struct ov8865_state {
 const struct ov8865_mode *mode;
 u32 mbus_code;

 bool streaming;
};

struct ov8865_ctrls {
 struct v4l2_ctrl *link_freq;
 struct v4l2_ctrl *pixel_rate;
 struct v4l2_ctrl *hblank;
 struct v4l2_ctrl *vblank;
 struct v4l2_ctrl *exposure;

 struct v4l2_ctrl_handler handler;
};

struct ov8865_sensor {
 struct device *dev;
 struct i2c_client *i2c_client;
 struct gpio_desc *reset;
 struct gpio_desc *powerdown;
 struct regulator *avdd;
 struct regulator *dvdd;
 struct regulator *dovdd;

 unsigned long extclk_rate;
 const struct ov8865_pll_configs *pll_configs;
 struct clk *extclk;

 struct v4l2_fwnode_endpoint endpoint;
 struct v4l2_subdev subdev;
 struct media_pad pad;

 struct mutex mutex;

 struct ov8865_state state;
 struct ov8865_ctrls ctrls;
};

/* Static definitions */

/*
 * PHY_SCLK = 720 MHz
 * MIPI_PCLK = 90 MHz
 */


static const struct ov8865_pll1_config ov8865_pll1_config_native_19_2mhz = {
  .pll_pre_div_half = 1,
  .pll_pre_div  = 2,
  .pll_mul  = 75,
  .m_div   = 1,
  .mipi_div  = 3,
  .pclk_div  = 1,
  .sys_pre_div  = 1,
  .sys_div  = 2,
};

static const struct ov8865_pll1_config ov8865_pll1_config_native_24mhz = {
  .pll_pre_div_half = 1,
  .pll_pre_div  = 0,
  .pll_mul  = 30,
  .m_div   = 1,
  .mipi_div  = 3,
  .pclk_div  = 1,
  .sys_pre_div  = 1,
  .sys_div  = 2,
};

/*
 * DAC_CLK = 360 MHz
 * SCLK = 144 MHz
 */


static const struct ov8865_pll2_config ov8865_pll2_config_native_19_2mhz = {
  .pll_pre_div_half = 1,
  .pll_pre_div  = 5,
  .pll_mul  = 75,
  .dac_div  = 1,
  .sys_pre_div  = 1,
  .sys_div  = 3,
};

static const struct ov8865_pll2_config ov8865_pll2_config_native_24mhz = {
  .pll_pre_div_half = 1,
  .pll_pre_div  = 0,
  .pll_mul  = 30,
  .dac_div  = 2,
  .sys_pre_div  = 5,
  .sys_div  = 0,
};

/*
 * DAC_CLK = 360 MHz
 * SCLK = 72 MHz
 */


static const struct ov8865_pll2_config ov8865_pll2_config_binning_19_2mhz = {
 .pll_pre_div_half = 1,
 .pll_pre_div  = 2,
 .pll_mul  = 75,
 .dac_div  = 2,
 .sys_pre_div  = 10,
 .sys_div  = 0,
};

static const struct ov8865_pll2_config ov8865_pll2_config_binning_24mhz = {
 .pll_pre_div_half = 1,
 .pll_pre_div  = 0,
 .pll_mul  = 30,
 .dac_div  = 2,
 .sys_pre_div  = 10,
 .sys_div  = 0,
};

static const struct ov8865_pll_configs ov8865_pll_configs_19_2mhz = {
 .pll1_config = &ov8865_pll1_config_native_19_2mhz,
 .pll2_config_native = &ov8865_pll2_config_native_19_2mhz,
 .pll2_config_binning = &ov8865_pll2_config_binning_19_2mhz,
};

static const struct ov8865_pll_configs ov8865_pll_configs_24mhz = {
 .pll1_config = &ov8865_pll1_config_native_24mhz,
 .pll2_config_native = &ov8865_pll2_config_native_24mhz,
 .pll2_config_binning = &ov8865_pll2_config_binning_24mhz,
};

static const struct ov8865_pll_configs *ov8865_pll_configs[] = {
 &ov8865_pll_configs_19_2mhz,
 &ov8865_pll_configs_24mhz,
};

static const struct ov8865_sclk_config ov8865_sclk_config_native = {
 .sys_sel  = 1,
 .sclk_sel  = 0,
 .sclk_pre_div  = 0,
 .sclk_div  = 0,
};

static const struct ov8865_register_value ov8865_register_values_native[] = {
 /* Sensor */

 { 0x3700, 0x48 },
 { 0x3701, 0x18 },
 { 0x3702, 0x50 },
 { 0x3703, 0x32 },
 { 0x3704, 0x28 },
 { 0x3706, 0x70 },
 { 0x3707, 0x08 },
 { 0x3708, 0x48 },
 { 0x3709, 0x80 },
 { 0x370a, 0x01 },
 { 0x370b, 0x70 },
 { 0x370c, 0x07 },
 { 0x3718, 0x14 },
 { 0x3712, 0x44 },
 { 0x371e, 0x31 },
 { 0x371f, 0x7f },
 { 0x3720, 0x0a },
 { 0x3721, 0x0a },
 { 0x3724, 0x04 },
 { 0x3725, 0x04 },
 { 0x3726, 0x0c },
 { 0x3728, 0x0a },
 { 0x3729, 0x03 },
 { 0x372a, 0x06 },
 { 0x372b, 0xa6 },
 { 0x372c, 0xa6 },
 { 0x372d, 0xa6 },
 { 0x372e, 0x0c },
 { 0x372f, 0x20 },
 { 0x3730, 0x02 },
 { 0x3731, 0x0c },
 { 0x3732, 0x28 },
 { 0x3736, 0x30 },
 { 0x373a, 0x04 },
 { 0x373b, 0x18 },
 { 0x373c, 0x14 },
 { 0x373e, 0x06 },
 { 0x375a, 0x0c },
 { 0x375b, 0x26 },
 { 0x375d, 0x04 },
 { 0x375f, 0x28 },
 { 0x3767, 0x1e },
 { 0x3772, 0x46 },
 { 0x3773, 0x04 },
 { 0x3774, 0x2c },
 { 0x3775, 0x13 },
 { 0x3776, 0x10 },
 { 0x37a0, 0x88 },
 { 0x37a1, 0x7a },
 { 0x37a2, 0x7a },
 { 0x37a3, 0x02 },
 { 0x37a5, 0x09 },
 { 0x37a7, 0x88 },
 { 0x37a8, 0xb0 },
 { 0x37a9, 0xb0 },
 { 0x37aa, 0x88 },
 { 0x37ab, 0x5c },
 { 0x37ac, 0x5c },
 { 0x37ad, 0x55 },
 { 0x37ae, 0x19 },
 { 0x37af, 0x19 },
 { 0x37b3, 0x84 },
 { 0x37b4, 0x84 },
 { 0x37b5, 0x66 },

 /* PSRAM */

 { OV8865_PSRAM_CTRL8_REG, 0x16 },

 /* ADC Sync */

 { 0x4500, 0x68 },
};

static const struct ov8865_register_value ov8865_register_values_binning[] = {
 /* Sensor */

 { 0x3700, 0x24 },
 { 0x3701, 0x0c },
 { 0x3702, 0x28 },
 { 0x3703, 0x19 },
 { 0x3704, 0x14 },
 { 0x3706, 0x38 },
 { 0x3707, 0x04 },
 { 0x3708, 0x24 },
 { 0x3709, 0x40 },
 { 0x370a, 0x00 },
 { 0x370b, 0xb8 },
 { 0x370c, 0x04 },
 { 0x3718, 0x12 },
 { 0x3712, 0x42 },
 { 0x371e, 0x19 },
 { 0x371f, 0x40 },
 { 0x3720, 0x05 },
 { 0x3721, 0x05 },
 { 0x3724, 0x02 },
 { 0x3725, 0x02 },
 { 0x3726, 0x06 },
 { 0x3728, 0x05 },
 { 0x3729, 0x02 },
 { 0x372a, 0x03 },
 { 0x372b, 0x53 },
 { 0x372c, 0xa3 },
 { 0x372d, 0x53 },
 { 0x372e, 0x06 },
 { 0x372f, 0x10 },
 { 0x3730, 0x01 },
 { 0x3731, 0x06 },
 { 0x3732, 0x14 },
 { 0x3736, 0x20 },
 { 0x373a, 0x02 },
 { 0x373b, 0x0c },
 { 0x373c, 0x0a },
 { 0x373e, 0x03 },
 { 0x375a, 0x06 },
 { 0x375b, 0x13 },
 { 0x375d, 0x02 },
 { 0x375f, 0x14 },
 { 0x3767, 0x1c },
 { 0x3772, 0x23 },
 { 0x3773, 0x02 },
 { 0x3774, 0x16 },
 { 0x3775, 0x12 },
 { 0x3776, 0x08 },
 { 0x37a0, 0x44 },
 { 0x37a1, 0x3d },
 { 0x37a2, 0x3d },
 { 0x37a3, 0x01 },
 { 0x37a5, 0x08 },
 { 0x37a7, 0x44 },
 { 0x37a8, 0x58 },
 { 0x37a9, 0x58 },
 { 0x37aa, 0x44 },
 { 0x37ab, 0x2e },
 { 0x37ac, 0x2e },
 { 0x37ad, 0x33 },
 { 0x37ae, 0x0d },
 { 0x37af, 0x0d },
 { 0x37b3, 0x42 },
 { 0x37b4, 0x42 },
 { 0x37b5, 0x33 },

 /* PSRAM */

 { OV8865_PSRAM_CTRL8_REG, 0x0b },

 /* ADC Sync */

 { 0x4500, 0x40 },
};

static const struct ov8865_mode ov8865_modes[] = {
 /* 3264x2448 */
 {
  /* Horizontal */
  .output_size_x   = 3264,
  .hts    = 3888,

  /* Vertical */
  .output_size_y   = 2448,
  .vts    = 2470,

  .size_auto   = true,
  .size_auto_boundary_x  = 8,
  .size_auto_boundary_y  = 4,

  /* Subsample increase */
  .inc_x_odd   = 1,
  .inc_x_even   = 1,
  .inc_y_odd   = 1,
  .inc_y_even   = 1,

  /* VFIFO */
  .vfifo_read_start  = 16,

  .ablc_num   = 4,
  .zline_num   = 1,

  /* Black Level */

  .blc_top_zero_line_start = 0,
  .blc_top_zero_line_num  = 2,
  .blc_top_black_line_start = 4,
  .blc_top_black_line_num  = 4,

  .blc_bottom_zero_line_start = 2,
  .blc_bottom_zero_line_num = 2,
  .blc_bottom_black_line_start = 8,
  .blc_bottom_black_line_num = 2,

  .blc_anchor_left_start  = 576,
  .blc_anchor_left_end  = 831,
  .blc_anchor_right_start  = 1984,
  .blc_anchor_right_end  = 2239,

  /* PLL */
  .pll2_binning   = false,

  /* Registers */
  .register_values = ov8865_register_values_native,
  .register_values_count =
   ARRAY_SIZE(ov8865_register_values_native),
 },
 /* 3264x1836 */
 {
  /* Horizontal */
  .output_size_x   = 3264,
  .hts    = 3888,

  /* Vertical */
  .output_size_y   = 1836,
  .vts    = 2470,

  .size_auto   = true,
  .size_auto_boundary_x  = 8,
  .size_auto_boundary_y  = 4,

  /* Subsample increase */
  .inc_x_odd   = 1,
  .inc_x_even   = 1,
  .inc_y_odd   = 1,
  .inc_y_even   = 1,

  /* VFIFO */
  .vfifo_read_start  = 16,

  .ablc_num   = 4,
  .zline_num   = 1,

  /* Black Level */

  .blc_top_zero_line_start = 0,
  .blc_top_zero_line_num  = 2,
  .blc_top_black_line_start = 4,
  .blc_top_black_line_num  = 4,

  .blc_bottom_zero_line_start = 2,
  .blc_bottom_zero_line_num = 2,
  .blc_bottom_black_line_start = 8,
  .blc_bottom_black_line_num = 2,

  .blc_anchor_left_start  = 576,
  .blc_anchor_left_end  = 831,
  .blc_anchor_right_start  = 1984,
  .blc_anchor_right_end  = 2239,

  /* PLL */
  .pll2_binning   = false,

  /* Registers */
  .register_values = ov8865_register_values_native,
  .register_values_count =
   ARRAY_SIZE(ov8865_register_values_native),
 },
 /* 1632x1224 */
 {
  /* Horizontal */
  .output_size_x   = 1632,
  .hts    = 1923,

  /* Vertical */
  .output_size_y   = 1224,
  .vts    = 1248,

  .size_auto   = true,
  .size_auto_boundary_x  = 8,
  .size_auto_boundary_y  = 8,

  /* Subsample increase */
  .inc_x_odd   = 3,
  .inc_x_even   = 1,
  .inc_y_odd   = 3,
  .inc_y_even   = 1,

  /* Binning */
  .binning_y   = true,
  .sync_hbin   = true,

  /* VFIFO */
  .vfifo_read_start  = 116,

  .ablc_num   = 8,
  .zline_num   = 2,

  /* Black Level */

  .blc_top_zero_line_start = 0,
  .blc_top_zero_line_num  = 2,
  .blc_top_black_line_start = 4,
  .blc_top_black_line_num  = 4,

  .blc_bottom_zero_line_start = 2,
  .blc_bottom_zero_line_num = 2,
  .blc_bottom_black_line_start = 8,
  .blc_bottom_black_line_num = 2,

  .blc_anchor_left_start  = 288,
  .blc_anchor_left_end  = 415,
  .blc_anchor_right_start  = 992,
  .blc_anchor_right_end  = 1119,

  /* PLL */
  .pll2_binning   = true,

  /* Registers */
  .register_values = ov8865_register_values_binning,
  .register_values_count =
   ARRAY_SIZE(ov8865_register_values_binning),
 },
 /* 800x600 (SVGA) */
 {
  /* Horizontal */
  .output_size_x   = 800,
  .hts    = 1250,

  /* Vertical */
  .output_size_y   = 600,
  .vts    = 640,

  .size_auto   = true,
  .size_auto_boundary_x  = 8,
  .size_auto_boundary_y  = 8,

  /* Subsample increase */
  .inc_x_odd   = 3,
  .inc_x_even   = 1,
  .inc_y_odd   = 5,
  .inc_y_even   = 3,

  /* Binning */
  .binning_y   = true,
  .variopixel   = true,
  .variopixel_hsub_coef  = 2,
  .variopixel_vsub_coef  = 1,
  .sync_hbin   = true,
  .horz_var2   = true,

  /* VFIFO */
  .vfifo_read_start  = 80,

  .ablc_num   = 8,
  .zline_num   = 2,

  /* Black Level */

  .blc_top_zero_line_start = 0,
  .blc_top_zero_line_num  = 2,
  .blc_top_black_line_start = 2,
  .blc_top_black_line_num  = 2,

  .blc_bottom_zero_line_start = 0,
  .blc_bottom_zero_line_num = 0,
  .blc_bottom_black_line_start = 4,
  .blc_bottom_black_line_num = 2,

  .blc_col_shift_mask = OV8865_BLC_CTRL1_COL_SHIFT_128,

  .blc_anchor_left_start  = 288,
  .blc_anchor_left_end  = 415,
  .blc_anchor_right_start  = 992,
  .blc_anchor_right_end  = 1119,

  /* PLL */
  .pll2_binning   = true,

  /* Registers */
  .register_values = ov8865_register_values_binning,
  .register_values_count =
   ARRAY_SIZE(ov8865_register_values_binning),
 },
};

static const u32 ov8865_mbus_codes[] = {
 MEDIA_BUS_FMT_SBGGR10_1X10,
};

static const struct ov8865_register_value ov8865_init_sequence[] = {
 /* Analog */

 { 0x3604, 0x04 },
 { 0x3602, 0x30 },
 { 0x3605, 0x00 },
 { 0x3607, 0x20 },
 { 0x3608, 0x11 },
 { 0x3609, 0x68 },
 { 0x360a, 0x40 },
 { 0x360c, 0xdd },
 { 0x360e, 0x0c },
 { 0x3610, 0x07 },
 { 0x3612, 0x86 },
 { 0x3613, 0x58 },
 { 0x3614, 0x28 },
 { 0x3617, 0x40 },
 { 0x3618, 0x5a },
 { 0x3619, 0x9b },
 { 0x361c, 0x00 },
 { 0x361d, 0x60 },
 { 0x3631, 0x60 },
 { 0x3633, 0x10 },
 { 0x3634, 0x10 },
 { 0x3635, 0x10 },
 { 0x3636, 0x10 },
 { 0x3638, 0xff },
 { 0x3641, 0x55 },
 { 0x3646, 0x86 },
 { 0x3647, 0x27 },
 { 0x364a, 0x1b },

 /* Sensor */

 { 0x3700, 0x24 },
 { 0x3701, 0x0c },
 { 0x3702, 0x28 },
 { 0x3703, 0x19 },
 { 0x3704, 0x14 },
 { 0x3705, 0x00 },
 { 0x3706, 0x38 },
 { 0x3707, 0x04 },
 { 0x3708, 0x24 },
 { 0x3709, 0x40 },
 { 0x370a, 0x00 },
 { 0x370b, 0xb8 },
 { 0x370c, 0x04 },
 { 0x3718, 0x12 },
 { 0x3719, 0x31 },
 { 0x3712, 0x42 },
 { 0x3714, 0x12 },
 { 0x371e, 0x19 },
 { 0x371f, 0x40 },
 { 0x3720, 0x05 },
 { 0x3721, 0x05 },
 { 0x3724, 0x02 },
 { 0x3725, 0x02 },
 { 0x3726, 0x06 },
 { 0x3728, 0x05 },
 { 0x3729, 0x02 },
 { 0x372a, 0x03 },
 { 0x372b, 0x53 },
 { 0x372c, 0xa3 },
 { 0x372d, 0x53 },
 { 0x372e, 0x06 },
 { 0x372f, 0x10 },
 { 0x3730, 0x01 },
 { 0x3731, 0x06 },
 { 0x3732, 0x14 },
 { 0x3733, 0x10 },
 { 0x3734, 0x40 },
 { 0x3736, 0x20 },
 { 0x373a, 0x02 },
 { 0x373b, 0x0c },
 { 0x373c, 0x0a },
 { 0x373e, 0x03 },
 { 0x3755, 0x40 },
 { 0x3758, 0x00 },
 { 0x3759, 0x4c },
 { 0x375a, 0x06 },
 { 0x375b, 0x13 },
 { 0x375c, 0x40 },
 { 0x375d, 0x02 },
 { 0x375e, 0x00 },
 { 0x375f, 0x14 },
 { 0x3767, 0x1c },
 { 0x3768, 0x04 },
 { 0x3769, 0x20 },
 { 0x376c, 0xc0 },
 { 0x376d, 0xc0 },
 { 0x376a, 0x08 },
 { 0x3761, 0x00 },
 { 0x3762, 0x00 },
 { 0x3763, 0x00 },
 { 0x3766, 0xff },
 { 0x376b, 0x42 },
 { 0x3772, 0x23 },
 { 0x3773, 0x02 },
 { 0x3774, 0x16 },
 { 0x3775, 0x12 },
 { 0x3776, 0x08 },
 { 0x37a0, 0x44 },
 { 0x37a1, 0x3d },
 { 0x37a2, 0x3d },
 { 0x37a3, 0x01 },
 { 0x37a4, 0x00 },
 { 0x37a5, 0x08 },
 { 0x37a6, 0x00 },
 { 0x37a7, 0x44 },
 { 0x37a8, 0x58 },
 { 0x37a9, 0x58 },
 { 0x3760, 0x00 },
 { 0x376f, 0x01 },
 { 0x37aa, 0x44 },
 { 0x37ab, 0x2e },
 { 0x37ac, 0x2e },
 { 0x37ad, 0x33 },
 { 0x37ae, 0x0d },
 { 0x37af, 0x0d },
 { 0x37b0, 0x00 },
 { 0x37b1, 0x00 },
 { 0x37b2, 0x00 },
 { 0x37b3, 0x42 },
 { 0x37b4, 0x42 },
 { 0x37b5, 0x33 },
 { 0x37b6, 0x00 },
 { 0x37b7, 0x00 },
 { 0x37b8, 0x00 },
 { 0x37b9, 0xff },

 /* ADC Sync */

 { 0x4503, 0x10 },
};

static const s64 ov8865_link_freq_menu[] = {
 360000000,
};

static const char *const ov8865_test_pattern_menu[] = {
 "Disabled",
 "Random data",
 "Color bars",
 "Color bars with rolling bar",
 "Color squares",
 "Color squares with rolling bar"
};

static const u8 ov8865_test_pattern_bits[] = {
 0,
 OV8865_PRE_CTRL0_PATTERN_EN | OV8865_PRE_CTRL0_PATTERN_RANDOM_DATA,
 OV8865_PRE_CTRL0_PATTERN_EN | OV8865_PRE_CTRL0_PATTERN_COLOR_BARS,
 OV8865_PRE_CTRL0_PATTERN_EN | OV8865_PRE_CTRL0_ROLLING_BAR_EN |
 OV8865_PRE_CTRL0_PATTERN_COLOR_BARS,
 OV8865_PRE_CTRL0_PATTERN_EN | OV8865_PRE_CTRL0_PATTERN_COLOR_SQUARES,
 OV8865_PRE_CTRL0_PATTERN_EN | OV8865_PRE_CTRL0_ROLLING_BAR_EN |
 OV8865_PRE_CTRL0_PATTERN_COLOR_SQUARES,
};

/* Input/Output */

static int ov8865_read(struct ov8865_sensor *sensor, u16 address, u8 *value)
{
 unsigned char data[2] = { address >> 8, address & 0xff };
 struct i2c_client *client = sensor->i2c_client;
 int ret;

 ret = i2c_master_send(client, data, sizeof(data));
 if (ret < 0) {
  dev_dbg(&client->dev, "i2c send error at address %#04x\n",
   address);
  return ret;
 }

 ret = i2c_master_recv(client, value, 1);
 if (ret < 0) {
  dev_dbg(&client->dev, "i2c recv error at address %#04x\n",
   address);
  return ret;
 }

 return 0;
}

static int ov8865_write(struct ov8865_sensor *sensor, u16 address, u8 value)
{
 unsigned char data[3] = { address >> 8, address & 0xff, value };
 struct i2c_client *client = sensor->i2c_client;
 int ret;

 ret = i2c_master_send(client, data, sizeof(data));
 if (ret < 0) {
  dev_dbg(&client->dev, "i2c send error at address %#04x\n",
   address);
  return ret;
 }

 return 0;
}

static int ov8865_write_sequence(struct ov8865_sensor *sensor,
     const struct ov8865_register_value *sequence,
     unsigned int sequence_count)
{
 unsigned int i;
 int ret = 0;

 for (i = 0; i < sequence_count; i++) {
  ret = ov8865_write(sensor, sequence[i].address,
       sequence[i].value);
  if (ret)
   break;

  if (sequence[i].delay_ms)
   msleep(sequence[i].delay_ms);
 }

 return ret;
}

static int ov8865_update_bits(struct ov8865_sensor *sensor, u16 address,
         u8 mask, u8 bits)
{
 u8 value = 0;
 int ret;

 ret = ov8865_read(sensor, address, &value);
 if (ret)
  return ret;

 value &= ~mask;
 value |= bits;

 return ov8865_write(sensor, address, value);
}

/* Sensor */

static int ov8865_sw_reset(struct ov8865_sensor *sensor)
{
 return ov8865_write(sensor, OV8865_SW_RESET_REG, OV8865_SW_RESET_RESET);
}

static int ov8865_sw_standby(struct ov8865_sensor *sensor, int standby)
{
 u8 value = 0;

 if (!standby)
  value = OV8865_SW_STANDBY_STREAM_ON;

 return ov8865_write(sensor, OV8865_SW_STANDBY_REG, value);
}

static int ov8865_chip_id_check(struct ov8865_sensor *sensor)
{
 u16 regs[] = { OV8865_CHIP_ID_HH_REG, OV8865_CHIP_ID_H_REG,
         OV8865_CHIP_ID_L_REG };
 u8 values[] = { OV8865_CHIP_ID_HH_VALUE, OV8865_CHIP_ID_H_VALUE,
   OV8865_CHIP_ID_L_VALUE };
 unsigned int i;
 u8 value;
 int ret;

 for (i = 0; i < ARRAY_SIZE(regs); i++) {
  ret = ov8865_read(sensor, regs[i], &value);
  if (ret < 0)
   return ret;

  if (value != values[i]) {
   dev_err(sensor->dev,
    "chip id value mismatch: %#x instead of %#x\n",
    value, values[i]);
   return -EINVAL;
  }
 }

 return 0;
}

static int ov8865_charge_pump_configure(struct ov8865_sensor *sensor)
{
 return ov8865_write(sensor, OV8865_PUMP_CLK_DIV_REG,
       OV8865_PUMP_CLK_DIV_PUMP_P(1));
}

static int ov8865_mipi_configure(struct ov8865_sensor *sensor)
{
 struct v4l2_mbus_config_mipi_csi2 *bus_mipi_csi2 =
  &sensor->endpoint.bus.mipi_csi2;
 unsigned int lanes_count = bus_mipi_csi2->num_data_lanes;
 int ret;

 ret = ov8865_write(sensor, OV8865_MIPI_SC_CTRL0_REG,
      OV8865_MIPI_SC_CTRL0_LANES(lanes_count) |
      OV8865_MIPI_SC_CTRL0_MIPI_EN |
      OV8865_MIPI_SC_CTRL0_UNKNOWN);
 if (ret)
  return ret;

 ret = ov8865_write(sensor, OV8865_MIPI_SC_CTRL2_REG,
      OV8865_MIPI_SC_CTRL2_PD_MIPI_RST_SYNC);
 if (ret)
  return ret;

 if (lanes_count >= 2) {
  ret = ov8865_write(sensor, OV8865_MIPI_LANE_SEL01_REG,
       OV8865_MIPI_LANE_SEL01_LANE0(0) |
       OV8865_MIPI_LANE_SEL01_LANE1(1));
  if (ret)
   return ret;
 }

 if (lanes_count >= 4) {
  ret = ov8865_write(sensor, OV8865_MIPI_LANE_SEL23_REG,
       OV8865_MIPI_LANE_SEL23_LANE2(2) |
       OV8865_MIPI_LANE_SEL23_LANE3(3));
  if (ret)
   return ret;
 }

 ret = ov8865_update_bits(sensor, OV8865_CLK_SEL1_REG,
     OV8865_CLK_SEL1_MIPI_EOF,
     OV8865_CLK_SEL1_MIPI_EOF);
 if (ret)
  return ret;

 /*
 * This value might need to change depending on PCLK rate,
 * but it's unclear how. This value seems to generally work
 * while the default value was found to cause transmission errors.
 */

 return ov8865_write(sensor, OV8865_MIPI_PCLK_PERIOD_REG, 0x16);
}

static int ov8865_black_level_configure(struct ov8865_sensor *sensor)
{
 int ret;

 /* Trigger BLC on relevant events and enable filter. */
 ret = ov8865_write(sensor, OV8865_BLC_CTRL0_REG,
      OV8865_BLC_CTRL0_TRIG_RANGE_EN |
      OV8865_BLC_CTRL0_TRIG_FORMAT_EN |
      OV8865_BLC_CTRL0_TRIG_GAIN_EN |
      OV8865_BLC_CTRL0_TRIG_EXPOSURE_EN |
      OV8865_BLC_CTRL0_FILTER_EN);
 if (ret)
  return ret;

 /* Lower BLC offset trigger threshold. */
 ret = ov8865_write(sensor, OV8865_BLC_CTRLD_REG,
      OV8865_BLC_CTRLD_OFFSET_TRIGGER(16));
 if (ret)
  return ret;

 ret = ov8865_write(sensor, OV8865_BLC_CTRL1F_REG, 0);
 if (ret)
  return ret;

 /* Increase BLC offset maximum limit. */
 return ov8865_write(sensor, OV8865_BLC_OFFSET_LIMIT_REG,
       OV8865_BLC_OFFSET_LIMIT(63));
}

static int ov8865_isp_configure(struct ov8865_sensor *sensor)
{
 int ret;

 /* Disable lens correction. */
 ret = ov8865_write(sensor, OV8865_ISP_CTRL0_REG,
      OV8865_ISP_CTRL0_WHITE_BALANCE_EN |
      OV8865_ISP_CTRL0_DPC_BLACK_EN |
      OV8865_ISP_CTRL0_DPC_WHITE_EN);
 if (ret)
  return ret;

 return ov8865_write(sensor, OV8865_ISP_CTRL1_REG,
       OV8865_ISP_CTRL1_BLC_EN);
}

static unsigned long ov8865_mode_pll1_rate(struct ov8865_sensor *sensor,
        const struct ov8865_mode *mode)
{
 const struct ov8865_pll1_config *config;
 unsigned long pll1_rate;

 config = sensor->pll_configs->pll1_config;
 pll1_rate = sensor->extclk_rate * config->pll_mul / config->pll_pre_div_half;

 switch (config->pll_pre_div) {
 case 0:
  break;
 case 1:
  pll1_rate *= 3;
  pll1_rate /= 2;
  break;
 case 3:
  pll1_rate *= 5;
  pll1_rate /= 2;
  break;
 case 4:
  pll1_rate /= 3;
  break;
 case 5:
  pll1_rate /= 4;
  break;
 case 7:
  pll1_rate /= 8;
  break;
 default:
  pll1_rate /= config->pll_pre_div;
  break;
 }

 return pll1_rate;
}

static int ov8865_mode_pll1_configure(struct ov8865_sensor *sensor,
          const struct ov8865_mode *mode,
          u32 mbus_code)
{
 const struct ov8865_pll1_config *config;
 u8 value;
 int ret;

 config = sensor->pll_configs->pll1_config;

 switch (mbus_code) {
 case MEDIA_BUS_FMT_SBGGR10_1X10:
  value = OV8865_MIPI_BIT_SEL(10);
  break;
 default:
  return -EINVAL;
 }

 ret = ov8865_write(sensor, OV8865_MIPI_BIT_SEL_REG, value);
 if (ret)
  return ret;

 ret = ov8865_write(sensor, OV8865_PLL_CTRLA_REG,
      OV8865_PLL_CTRLA_PRE_DIV_HALF(config->pll_pre_div_half));
 if (ret)
  return ret;

 ret = ov8865_write(sensor, OV8865_PLL_CTRL0_REG,
      OV8865_PLL_CTRL0_PRE_DIV(config->pll_pre_div));
 if (ret)
  return ret;

 ret = ov8865_write(sensor, OV8865_PLL_CTRL1_REG,
      OV8865_PLL_CTRL1_MUL_H(config->pll_mul));
 if (ret)
  return ret;

 ret = ov8865_write(sensor, OV8865_PLL_CTRL2_REG,
      OV8865_PLL_CTRL2_MUL_L(config->pll_mul));
 if (ret)
  return ret;

 ret = ov8865_write(sensor, OV8865_PLL_CTRL3_REG,
      OV8865_PLL_CTRL3_M_DIV(config->m_div));
 if (ret)
  return ret;

 ret = ov8865_write(sensor, OV8865_PLL_CTRL4_REG,
      OV8865_PLL_CTRL4_MIPI_DIV(config->mipi_div));
 if (ret)
  return ret;

 ret = ov8865_update_bits(sensor, OV8865_PCLK_SEL_REG,
     OV8865_PCLK_SEL_PCLK_DIV_MASK,
     OV8865_PCLK_SEL_PCLK_DIV(config->pclk_div));
 if (ret)
  return ret;

 ret = ov8865_write(sensor, OV8865_PLL_CTRL5_REG,
      OV8865_PLL_CTRL5_SYS_PRE_DIV(config->sys_pre_div));
 if (ret)
  return ret;

 ret = ov8865_write(sensor, OV8865_PLL_CTRL6_REG,
      OV8865_PLL_CTRL6_SYS_DIV(config->sys_div));
 if (ret)
  return ret;

 return ov8865_update_bits(sensor, OV8865_PLL_CTRL1E_REG,
      OV8865_PLL_CTRL1E_PLL1_NO_LAT,
      OV8865_PLL_CTRL1E_PLL1_NO_LAT);
}

static int ov8865_mode_pll2_configure(struct ov8865_sensor *sensor,
          const struct ov8865_mode *mode)
{
 const struct ov8865_pll2_config *config;
 int ret;

 config = mode->pll2_binning ? sensor->pll_configs->pll2_config_binning :
          sensor->pll_configs->pll2_config_native;

 ret = ov8865_write(sensor, OV8865_PLL_CTRL12_REG,
      OV8865_PLL_CTRL12_PRE_DIV_HALF(config->pll_pre_div_half) |
      OV8865_PLL_CTRL12_DAC_DIV(config->dac_div));
 if (ret)
  return ret;

 ret = ov8865_write(sensor, OV8865_PLL_CTRLB_REG,
      OV8865_PLL_CTRLB_PRE_DIV(config->pll_pre_div));
 if (ret)
  return ret;

 ret = ov8865_write(sensor, OV8865_PLL_CTRLC_REG,
      OV8865_PLL_CTRLC_MUL_H(config->pll_mul));
 if (ret)
  return ret;

 ret = ov8865_write(sensor, OV8865_PLL_CTRLD_REG,
      OV8865_PLL_CTRLD_MUL_L(config->pll_mul));
 if (ret)
  return ret;

 ret = ov8865_write(sensor, OV8865_PLL_CTRLF_REG,
      OV8865_PLL_CTRLF_SYS_PRE_DIV(config->sys_pre_div));
 if (ret)
  return ret;

 return ov8865_write(sensor, OV8865_PLL_CTRLE_REG,
       OV8865_PLL_CTRLE_SYS_DIV(config->sys_div));
}

static int ov8865_mode_sclk_configure(struct ov8865_sensor *sensor,
          const struct ov8865_mode *mode)
{
 const struct ov8865_sclk_config *config = &ov8865_sclk_config_native;
 int ret;

 ret = ov8865_write(sensor, OV8865_CLK_SEL0_REG,
      OV8865_CLK_SEL0_PLL1_SYS_SEL(config->sys_sel));
 if (ret)
  return ret;

 ret = ov8865_update_bits(sensor, OV8865_CLK_SEL1_REG,
     OV8865_CLK_SEL1_PLL_SCLK_SEL_MASK,
     OV8865_CLK_SEL1_PLL_SCLK_SEL(config->sclk_sel));
 if (ret)
  return ret;

 return ov8865_write(sensor, OV8865_SCLK_CTRL_REG,
       OV8865_SCLK_CTRL_UNKNOWN |
       OV8865_SCLK_CTRL_SCLK_DIV(config->sclk_div) |
       OV8865_SCLK_CTRL_SCLK_PRE_DIV(config->sclk_pre_div));
}

static int ov8865_mode_binning_configure(struct ov8865_sensor *sensor,
      const struct ov8865_mode *mode)
{
 unsigned int variopixel_hsub_coef, variopixel_vsub_coef;
 u8 value;
 int ret;

 ret = ov8865_write(sensor, OV8865_FORMAT1_REG, 0);
 if (ret)
  return ret;

 value = OV8865_FORMAT2_HSYNC_EN;

 if (mode->binning_x)
  value |= OV8865_FORMAT2_FST_HBIN_EN;

 if (mode->binning_y)
  value |= OV8865_FORMAT2_FST_VBIN_EN;

 if (mode->sync_hbin)
  value |= OV8865_FORMAT2_SYNC_HBIN_EN;

 if (mode->horz_var2)
  value |= OV8865_FORMAT2_ISP_HORZ_VAR2_EN;

 ret = ov8865_write(sensor, OV8865_FORMAT2_REG, value);
 if (ret)
  return ret;

 ret = ov8865_update_bits(sensor, OV8865_ISP_CTRL2_REG,
     OV8865_ISP_CTRL2_VARIOPIXEL_EN,
     mode->variopixel ?
     OV8865_ISP_CTRL2_VARIOPIXEL_EN : 0);
 if (ret)
  return ret;

 if (mode->variopixel) {
  /* VarioPixel coefs needs to be > 1. */
  variopixel_hsub_coef = mode->variopixel_hsub_coef;
  variopixel_vsub_coef = mode->variopixel_vsub_coef;
 } else {
  variopixel_hsub_coef = 1;
  variopixel_vsub_coef = 1;
 }

 ret = ov8865_write(sensor, OV8865_VAP_CTRL1_REG,
      OV8865_VAP_CTRL1_HSUB_COEF(variopixel_hsub_coef) |
      OV8865_VAP_CTRL1_VSUB_COEF(variopixel_vsub_coef));
 if (ret)
  return ret;

 ret = ov8865_write(sensor, OV8865_INC_X_ODD_REG,
      OV8865_INC_X_ODD(mode->inc_x_odd));
 if (ret)
  return ret;

 ret = ov8865_write(sensor, OV8865_INC_X_EVEN_REG,
      OV8865_INC_X_EVEN(mode->inc_x_even));
 if (ret)
  return ret;

 ret = ov8865_write(sensor, OV8865_INC_Y_ODD_REG,
      OV8865_INC_Y_ODD(mode->inc_y_odd));
 if (ret)
  return ret;

 return ov8865_write(sensor, OV8865_INC_Y_EVEN_REG,
       OV8865_INC_Y_EVEN(mode->inc_y_even));
}

static int ov8865_mode_black_level_configure(struct ov8865_sensor *sensor,
          const struct ov8865_mode *mode)
{
 int ret;

 /* Note that a zero value for blc_col_shift_mask is the default 256. */
 ret = ov8865_write(sensor, OV8865_BLC_CTRL1_REG,
      mode->blc_col_shift_mask |
      OV8865_BLC_CTRL1_OFFSET_LIMIT_EN);
 if (ret)
  return ret;

 /* BLC top zero line */

 ret = ov8865_write(sensor, OV8865_BLC_TOP_ZLINE_START_REG,
      OV8865_BLC_TOP_ZLINE_START(mode->blc_top_zero_line_start));
 if (ret)
  return ret;

 ret = ov8865_write(sensor, OV8865_BLC_TOP_ZLINE_NUM_REG,
      OV8865_BLC_TOP_ZLINE_NUM(mode->blc_top_zero_line_num));
 if (ret)
  return ret;

 /* BLC top black line */

 ret = ov8865_write(sensor, OV8865_BLC_TOP_BLKLINE_START_REG,
      OV8865_BLC_TOP_BLKLINE_START(mode->blc_top_black_line_start));
 if (ret)
  return ret;

 ret = ov8865_write(sensor, OV8865_BLC_TOP_BLKLINE_NUM_REG,
      OV8865_BLC_TOP_BLKLINE_NUM(mode->blc_top_black_line_num));
 if (ret)
  return ret;

 /* BLC bottom zero line */

 ret = ov8865_write(sensor, OV8865_BLC_BOT_ZLINE_START_REG,
      OV8865_BLC_BOT_ZLINE_START(mode->blc_bottom_zero_line_start));
 if (ret)
  return ret;

 ret = ov8865_write(sensor, OV8865_BLC_BOT_ZLINE_NUM_REG,
      OV8865_BLC_BOT_ZLINE_NUM(mode->blc_bottom_zero_line_num));
 if (ret)
  return ret;

 /* BLC bottom black line */

 ret = ov8865_write(sensor, OV8865_BLC_BOT_BLKLINE_START_REG,
      OV8865_BLC_BOT_BLKLINE_START(mode->blc_bottom_black_line_start));
 if (ret)
  return ret;

 ret = ov8865_write(sensor, OV8865_BLC_BOT_BLKLINE_NUM_REG,
      OV8865_BLC_BOT_BLKLINE_NUM(mode->blc_bottom_black_line_num));
 if (ret)
  return ret;

 /* BLC anchor */

 ret = ov8865_write(sensor, OV8865_BLC_ANCHOR_LEFT_START_H_REG,
      OV8865_BLC_ANCHOR_LEFT_START_H(mode->blc_anchor_left_start));
 if (ret)
  return ret;

 ret = ov8865_write(sensor, OV8865_BLC_ANCHOR_LEFT_START_L_REG,
      OV8865_BLC_ANCHOR_LEFT_START_L(mode->blc_anchor_left_start));
 if (ret)
  return ret;

 ret = ov8865_write(sensor, OV8865_BLC_ANCHOR_LEFT_END_H_REG,
      OV8865_BLC_ANCHOR_LEFT_END_H(mode->blc_anchor_left_end));
 if (ret)
  return ret;

 ret = ov8865_write(sensor, OV8865_BLC_ANCHOR_LEFT_END_L_REG,
      OV8865_BLC_ANCHOR_LEFT_END_L(mode->blc_anchor_left_end));
 if (ret)
  return ret;

 ret = ov8865_write(sensor, OV8865_BLC_ANCHOR_RIGHT_START_H_REG,
      OV8865_BLC_ANCHOR_RIGHT_START_H(mode->blc_anchor_right_start));
 if (ret)
  return ret;

 ret = ov8865_write(sensor, OV8865_BLC_ANCHOR_RIGHT_START_L_REG,
      OV8865_BLC_ANCHOR_RIGHT_START_L(mode->blc_anchor_right_start));
 if (ret)
  return ret;

 ret = ov8865_write(sensor, OV8865_BLC_ANCHOR_RIGHT_END_H_REG,
      OV8865_BLC_ANCHOR_RIGHT_END_H(mode->blc_anchor_right_end));
 if (ret)
  return ret;

 return ov8865_write(sensor, OV8865_BLC_ANCHOR_RIGHT_END_L_REG,
       OV8865_BLC_ANCHOR_RIGHT_END_L(mode->blc_anchor_right_end));
}

static int ov8865_mode_configure(struct ov8865_sensor *sensor,
     const struct ov8865_mode *mode, u32 mbus_code)
{
 int ret;

 /* Output Size X */

 ret = ov8865_write(sensor, OV8865_OUTPUT_SIZE_X_H_REG,
      OV8865_OUTPUT_SIZE_X_H(mode->output_size_x));
 if (ret)
  return ret;

 ret = ov8865_write(sensor, OV8865_OUTPUT_SIZE_X_L_REG,
      OV8865_OUTPUT_SIZE_X_L(mode->output_size_x));
 if (ret)
  return ret;

 /* Horizontal Total Size */

 ret = ov8865_write(sensor, OV8865_HTS_H_REG, OV8865_HTS_H(mode->hts));
 if (ret)
  return ret;

 ret = ov8865_write(sensor, OV8865_HTS_L_REG, OV8865_HTS_L(mode->hts));
 if (ret)
  return ret;

 /* Output Size Y */

 ret = ov8865_write(sensor, OV8865_OUTPUT_SIZE_Y_H_REG,
      OV8865_OUTPUT_SIZE_Y_H(mode->output_size_y));
 if (ret)
  return ret;

 ret = ov8865_write(sensor, OV8865_OUTPUT_SIZE_Y_L_REG,
      OV8865_OUTPUT_SIZE_Y_L(mode->output_size_y));
 if (ret)
  return ret;

 /* Vertical Total Size */

 ret = ov8865_write(sensor, OV8865_VTS_H_REG, OV8865_VTS_H(mode->vts));
 if (ret)
  return ret;

 ret = ov8865_write(sensor, OV8865_VTS_L_REG, OV8865_VTS_L(mode->vts));
 if (ret)
  return ret;

 if (mode->size_auto) {
  /* Auto Size */

  ret = ov8865_write(sensor, OV8865_AUTO_SIZE_CTRL_REG,
       OV8865_AUTO_SIZE_CTRL_OFFSET_Y_REG |
       OV8865_AUTO_SIZE_CTRL_OFFSET_X_REG |
       OV8865_AUTO_SIZE_CTRL_CROP_END_Y_REG |
       OV8865_AUTO_SIZE_CTRL_CROP_END_X_REG |
       OV8865_AUTO_SIZE_CTRL_CROP_START_Y_REG |
       OV8865_AUTO_SIZE_CTRL_CROP_START_X_REG);
  if (ret)
   return ret;

  ret = ov8865_write(sensor, OV8865_AUTO_SIZE_BOUNDARIES_REG,
       OV8865_AUTO_SIZE_BOUNDARIES_Y(mode->size_auto_boundary_y) |
       OV8865_AUTO_SIZE_BOUNDARIES_X(mode->size_auto_boundary_x));
  if (ret)
   return ret;
 } else {
  /* Crop Start X */

  ret = ov8865_write(sensor, OV8865_CROP_START_X_H_REG,
       OV8865_CROP_START_X_H(mode->crop_start_x));
  if (ret)
   return ret;

  ret = ov8865_write(sensor, OV8865_CROP_START_X_L_REG,
       OV8865_CROP_START_X_L(mode->crop_start_x));
  if (ret)
   return ret;

  /* Offset X */

  ret = ov8865_write(sensor, OV8865_OFFSET_X_H_REG,
       OV8865_OFFSET_X_H(mode->offset_x));
  if (ret)
   return ret;

  ret = ov8865_write(sensor, OV8865_OFFSET_X_L_REG,
       OV8865_OFFSET_X_L(mode->offset_x));
  if (ret)
   return ret;

  /* Crop End X */

  ret = ov8865_write(sensor, OV8865_CROP_END_X_H_REG,
       OV8865_CROP_END_X_H(mode->crop_end_x));
  if (ret)
   return ret;

  ret = ov8865_write(sensor, OV8865_CROP_END_X_L_REG,
       OV8865_CROP_END_X_L(mode->crop_end_x));
  if (ret)
   return ret;

  /* Crop Start Y */

  ret = ov8865_write(sensor, OV8865_CROP_START_Y_H_REG,
       OV8865_CROP_START_Y_H(mode->crop_start_y));
  if (ret)
   return ret;

  ret = ov8865_write(sensor, OV8865_CROP_START_Y_L_REG,
       OV8865_CROP_START_Y_L(mode->crop_start_y));
  if (ret)
   return ret;

  /* Offset Y */

  ret = ov8865_write(sensor, OV8865_OFFSET_Y_H_REG,
       OV8865_OFFSET_Y_H(mode->offset_y));
  if (ret)
   return ret;

  ret = ov8865_write(sensor, OV8865_OFFSET_Y_L_REG,
       OV8865_OFFSET_Y_L(mode->offset_y));
  if (ret)
   return ret;

  /* Crop End Y */

  ret = ov8865_write(sensor, OV8865_CROP_END_Y_H_REG,
       OV8865_CROP_END_Y_H(mode->crop_end_y));
  if (ret)
   return ret;

  ret = ov8865_write(sensor, OV8865_CROP_END_Y_L_REG,
       OV8865_CROP_END_Y_L(mode->crop_end_y));
  if (ret)
   return ret;
 }

 /* VFIFO */

 ret = ov8865_write(sensor, OV8865_VFIFO_READ_START_H_REG,
      OV8865_VFIFO_READ_START_H(mode->vfifo_read_start));
 if (ret)
  return ret;

 ret = ov8865_write(sensor, OV8865_VFIFO_READ_START_L_REG,
      OV8865_VFIFO_READ_START_L(mode->vfifo_read_start));
 if (ret)
  return ret;

 ret = ov8865_write(sensor, OV8865_ABLC_NUM_REG,
      OV8865_ABLC_NUM(mode->ablc_num));
 if (ret)
  return ret;

 ret = ov8865_write(sensor, OV8865_ZLINE_NUM_REG,
      OV8865_ZLINE_NUM(mode->zline_num));
 if (ret)
  return ret;

 /* Binning */

 ret = ov8865_mode_binning_configure(sensor, mode);
 if (ret)
  return ret;

 /* Black Level */

 ret = ov8865_mode_black_level_configure(sensor, mode);
 if (ret)
  return ret;

 /* PLLs */

 ret = ov8865_mode_pll1_configure(sensor, mode, mbus_code);
 if (ret)
  return ret;

 ret = ov8865_mode_pll2_configure(sensor, mode);
 if (ret)
  return ret;

 ret = ov8865_mode_sclk_configure(sensor, mode);
 if (ret)
  return ret;

 /* Extra registers */

 if (mode->register_values) {
  ret = ov8865_write_sequence(sensor, mode->register_values,
         mode->register_values_count);
  if (ret)
   return ret;
 }

 return 0;
}

static unsigned long ov8865_mode_mipi_clk_rate(struct ov8865_sensor *sensor,
            const struct ov8865_mode *mode)
{
 const struct ov8865_pll1_config *config;
 unsigned long pll1_rate;

 config = sensor->pll_configs->pll1_config;

 pll1_rate = ov8865_mode_pll1_rate(sensor, mode);

 return pll1_rate / config->m_div / 2;
}

/* Exposure */

static int ov8865_exposure_configure(struct ov8865_sensor *sensor, u32 exposure)
{
 int ret;

 /* The sensor stores exposure in units of 1/16th of a line */
 exposure *= 16;

 ret = ov8865_write(sensor, OV8865_EXPOSURE_CTRL_HH_REG,
      OV8865_EXPOSURE_CTRL_HH(exposure));
 if (ret)
  return ret;

 ret = ov8865_write(sensor, OV8865_EXPOSURE_CTRL_H_REG,
      OV8865_EXPOSURE_CTRL_H(exposure));
 if (ret)
  return ret;

 return ov8865_write(sensor, OV8865_EXPOSURE_CTRL_L_REG,
       OV8865_EXPOSURE_CTRL_L(exposure));
}

/* Gain */

static int ov8865_analog_gain_configure(struct ov8865_sensor *sensor, u32 gain)
{
 int ret;

 ret = ov8865_write(sensor, OV8865_GAIN_CTRL_H_REG,
      OV8865_GAIN_CTRL_H(gain));
 if (ret)
  return ret;

 return ov8865_write(sensor, OV8865_GAIN_CTRL_L_REG,
       OV8865_GAIN_CTRL_L(gain));
}

/* White Balance */

static int ov8865_red_balance_configure(struct ov8865_sensor *sensor,
     u32 red_balance)
{
 int ret;

 ret = ov8865_write(sensor, OV8865_ISP_GAIN_RED_H_REG,
      OV8865_ISP_GAIN_RED_H(red_balance));
 if (ret)
  return ret;

 return ov8865_write(sensor, OV8865_ISP_GAIN_RED_L_REG,
       OV8865_ISP_GAIN_RED_L(red_balance));
}

static int ov8865_blue_balance_configure(struct ov8865_sensor *sensor,
      u32 blue_balance)
{
 int ret;

 ret = ov8865_write(sensor, OV8865_ISP_GAIN_BLUE_H_REG,
      OV8865_ISP_GAIN_BLUE_H(blue_balance));
 if (ret)
  return ret;

 return ov8865_write(sensor, OV8865_ISP_GAIN_BLUE_L_REG,
       OV8865_ISP_GAIN_BLUE_L(blue_balance));
}

/* Flip */

static int ov8865_flip_vert_configure(struct ov8865_sensor *sensor, bool enable)
{
 u8 bits = OV8865_FORMAT1_FLIP_VERT_ISP_EN |
    OV8865_FORMAT1_FLIP_VERT_SENSOR_EN;

 return ov8865_update_bits(sensor, OV8865_FORMAT1_REG, bits,
      enable ? bits : 0);
}

static int ov8865_flip_horz_configure(struct ov8865_sensor *sensor, bool enable)
{
 u8 bits = OV8865_FORMAT2_FLIP_HORZ_ISP_EN |
    OV8865_FORMAT2_FLIP_HORZ_SENSOR_EN;

 return ov8865_update_bits(sensor, OV8865_FORMAT2_REG, bits,
      enable ? bits : 0);
}

/* Test Pattern */

static int ov8865_test_pattern_configure(struct ov8865_sensor *sensor,
      unsigned int index)
{
 if (index >= ARRAY_SIZE(ov8865_test_pattern_bits))
  return -EINVAL;

 return ov8865_write(sensor, OV8865_PRE_CTRL0_REG,
       ov8865_test_pattern_bits[index]);
}

/* Blanking */

static int ov8865_vts_configure(struct ov8865_sensor *sensor, u32 vblank)
{
 u16 vts = sensor->state.mode->output_size_y + vblank;
 int ret;

 ret = ov8865_write(sensor, OV8865_VTS_H_REG, OV8865_VTS_H(vts));
 if (ret)
  return ret;

 return ov8865_write(sensor, OV8865_VTS_L_REG, OV8865_VTS_L(vts));
}

/* State */

static int ov8865_state_mipi_configure(struct ov8865_sensor *sensor,
           const struct ov8865_mode *mode,
           u32 mbus_code)
{
 struct ov8865_ctrls *ctrls = &sensor->ctrls;
 struct v4l2_mbus_config_mipi_csi2 *bus_mipi_csi2 =
  &sensor->endpoint.bus.mipi_csi2;
 unsigned long mipi_clk_rate;
 unsigned int bits_per_sample;
 unsigned int lanes_count;
 unsigned int i, j;
 s64 mipi_pixel_rate;

 mipi_clk_rate = ov8865_mode_mipi_clk_rate(sensor, mode);
 if (!mipi_clk_rate)
  return -EINVAL;

 for (i = 0; i < ARRAY_SIZE(ov8865_link_freq_menu); i++) {
  s64 freq = ov8865_link_freq_menu[i];

  if (freq == mipi_clk_rate)
   break;
 }

 for (j = 0; j < sensor->endpoint.nr_of_link_frequencies; j++) {
  u64 freq = sensor->endpoint.link_frequencies[j];

  if (freq == mipi_clk_rate)
   break;
 }

 if (i == ARRAY_SIZE(ov8865_link_freq_menu)) {
  dev_err(sensor->dev,
   "failed to find %lu clk rate in link freq\n",
   mipi_clk_rate);
 } else if (j == sensor->endpoint.nr_of_link_frequencies) {
  dev_err(sensor->dev,
   "failed to find %lu clk rate in endpoint link-frequencies\n",
   mipi_clk_rate);
 } else {
  __v4l2_ctrl_s_ctrl(ctrls->link_freq, i);
 }

 switch (mbus_code) {
 case MEDIA_BUS_FMT_SBGGR10_1X10:
  bits_per_sample = 10;
  break;
 default:
  return -EINVAL;
 }

 lanes_count = bus_mipi_csi2->num_data_lanes;
 mipi_pixel_rate = mipi_clk_rate * 2 * lanes_count / bits_per_sample;

 __v4l2_ctrl_s_ctrl_int64(ctrls->pixel_rate, mipi_pixel_rate);

 return 0;
}

static int ov8865_state_configure(struct ov8865_sensor *sensor,
      const struct ov8865_mode *mode,
      u32 mbus_code)
{
 int ret;

 if (sensor->state.streaming)
  return -EBUSY;

 /* State will be configured at first power on otherwise. */
 if (pm_runtime_enabled(sensor->dev) &&
     !pm_runtime_suspended(sensor->dev)) {
  ret = ov8865_mode_configure(sensor, mode, mbus_code);
  if (ret)
   return ret;
 }

 ret = ov8865_state_mipi_configure(sensor, mode, mbus_code);
 if (ret)
  return ret;

 sensor->state.mode = mode;
 sensor->state.mbus_code = mbus_code;

 return 0;
}

static int ov8865_state_init(struct ov8865_sensor *sensor)
{
 return ov8865_state_configure(sensor, &ov8865_modes[0],
          ov8865_mbus_codes[0]);
}

/* Sensor Base */

static int ov8865_sensor_init(struct ov8865_sensor *sensor)
{
 int ret;

 ret = ov8865_sw_reset(sensor);
 if (ret) {
  dev_err(sensor->dev, "failed to perform sw reset\n");
  return ret;
 }

 ret = ov8865_sw_standby(sensor, 1);
 if (ret) {
  dev_err(sensor->dev, "failed to set sensor standby\n");
  return ret;
 }

 ret = ov8865_chip_id_check(sensor);
 if (ret) {
  dev_err(sensor->dev, "failed to check sensor chip id\n");
  return ret;
 }

 ret = ov8865_write_sequence(sensor, ov8865_init_sequence,
        ARRAY_SIZE(ov8865_init_sequence));
 if (ret) {
  dev_err(sensor->dev, "failed to write init sequence\n");
  return ret;
 }

 ret = ov8865_charge_pump_configure(sensor);
 if (ret) {
  dev_err(sensor->dev, "failed to configure pad\n");
  return ret;
 }

 ret = ov8865_mipi_configure(sensor);
 if (ret) {
  dev_err(sensor->dev, "failed to configure MIPI\n");
  return ret;
 }

 ret = ov8865_isp_configure(sensor);
 if (ret) {
  dev_err(sensor->dev, "failed to configure ISP\n");
  return ret;
 }

 ret = ov8865_black_level_configure(sensor);
 if (ret) {
  dev_err(sensor->dev, "failed to configure black level\n");
  return ret;
 }

 /* Configure current mode. */
 ret = ov8865_state_configure(sensor, sensor->state.mode,
         sensor->state.mbus_code);
 if (ret) {
  dev_err(sensor->dev, "failed to configure state\n");
  return ret;
 }

 return 0;
}

static int ov8865_sensor_power(struct ov8865_sensor *sensor, bool on)
{
 /* Keep initialized to zero for disable label. */
 int ret = 0;

 if (on) {
  gpiod_set_value_cansleep(sensor->reset, 1);
  gpiod_set_value_cansleep(sensor->powerdown, 1);

  ret = regulator_enable(sensor->dovdd);
  if (ret) {
   dev_err(sensor->dev,
    "failed to enable DOVDD regulator\n");
   return ret;
  }

  ret = regulator_enable(sensor->avdd);
  if (ret) {
   dev_err(sensor->dev,
    "failed to enable AVDD regulator\n");
   goto disable_dovdd;
  }

  ret = regulator_enable(sensor->dvdd);
  if (ret) {
   dev_err(sensor->dev,
    "failed to enable DVDD regulator\n");
   goto disable_avdd;
  }

  ret = clk_prepare_enable(sensor->extclk);
  if (ret) {
   dev_err(sensor->dev, "failed to enable EXTCLK clock\n");
   goto disable_dvdd;
  }

  gpiod_set_value_cansleep(sensor->reset, 0);
  gpiod_set_value_cansleep(sensor->powerdown, 0);

  /* Time to enter streaming mode according to power timings. */
  usleep_range(10000, 12000);
 } else {
  gpiod_set_value_cansleep(sensor->powerdown, 1);
  gpiod_set_value_cansleep(sensor->reset, 1);

  clk_disable_unprepare(sensor->extclk);

disable_dvdd:
  regulator_disable(sensor->dvdd);
disable_avdd:
  regulator_disable(sensor->avdd);
disable_dovdd:
  regulator_disable(sensor->dovdd);
 }

 return ret;
}

/* Controls */

static int ov8865_s_ctrl(struct v4l2_ctrl *ctrl)
{
 struct v4l2_subdev *subdev = ov8865_ctrl_subdev(ctrl);
 struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev);
 unsigned int index;
 int ret;

 /* If VBLANK is altered we need to update exposure to compensate */
 if (ctrl->id == V4L2_CID_VBLANK) {
  int exposure_max;

  exposure_max = sensor->state.mode->output_size_y + ctrl->val -
          OV8865_INTEGRATION_TIME_MARGIN;
  __v4l2_ctrl_modify_range(sensor->ctrls.exposure,
      sensor->ctrls.exposure->minimum,
      exposure_max,
      sensor->ctrls.exposure->step,
      min(sensor->ctrls.exposure->val,
          exposure_max));
 }

 /* Wait for the sensor to be on before setting controls. */
 if (pm_runtime_suspended(sensor->dev))
  return 0;

 switch (ctrl->id) {
 case V4L2_CID_EXPOSURE:
  ret = ov8865_exposure_configure(sensor, ctrl->val);
  if (ret)
   return ret;
  break;
 case V4L2_CID_ANALOGUE_GAIN:
  ret = ov8865_analog_gain_configure(sensor, ctrl->val);
  if (ret)
   return ret;
  break;
 case V4L2_CID_RED_BALANCE:
  return ov8865_red_balance_configure(sensor, ctrl->val);
 case V4L2_CID_BLUE_BALANCE:
  return ov8865_blue_balance_configure(sensor, ctrl->val);
 case V4L2_CID_HFLIP:
  return ov8865_flip_horz_configure(sensor, !!ctrl->val);
 case V4L2_CID_VFLIP:
  return ov8865_flip_vert_configure(sensor, !!ctrl->val);
 case V4L2_CID_TEST_PATTERN:
  index = (unsigned int)ctrl->val;
  return ov8865_test_pattern_configure(sensor, index);
 case V4L2_CID_VBLANK:
  return ov8865_vts_configure(sensor, ctrl->val);
 default:
  return -EINVAL;
 }

 return 0;
}

static const struct v4l2_ctrl_ops ov8865_ctrl_ops = {
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=96 H=95 G=95

¤ Dauer der Verarbeitung: 0.15 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.