18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Driver for Samsung S5K6AAFX SXGA 1/6" 1.3M CMOS Image Sensor
48c2ecf20Sopenharmony_ci * with embedded SoC ISP.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (C) 2011, Samsung Electronics Co., Ltd.
78c2ecf20Sopenharmony_ci * Sylwester Nawrocki <s.nawrocki@samsung.com>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Based on a driver authored by Dongsoo Nathaniel Kim.
108c2ecf20Sopenharmony_ci * Copyright (C) 2009, Dongsoo Nathaniel Kim <dongsoo45.kim@samsung.com>
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/clk.h>
148c2ecf20Sopenharmony_ci#include <linux/delay.h>
158c2ecf20Sopenharmony_ci#include <linux/gpio.h>
168c2ecf20Sopenharmony_ci#include <linux/i2c.h>
178c2ecf20Sopenharmony_ci#include <linux/media.h>
188c2ecf20Sopenharmony_ci#include <linux/module.h>
198c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h>
208c2ecf20Sopenharmony_ci#include <linux/slab.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include <media/media-entity.h>
238c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h>
248c2ecf20Sopenharmony_ci#include <media/v4l2-device.h>
258c2ecf20Sopenharmony_ci#include <media/v4l2-subdev.h>
268c2ecf20Sopenharmony_ci#include <media/v4l2-mediabus.h>
278c2ecf20Sopenharmony_ci#include <media/i2c/s5k6aa.h>
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic int debug;
308c2ecf20Sopenharmony_cimodule_param(debug, int, 0644);
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define DRIVER_NAME			"S5K6AA"
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci/* The token to indicate array termination */
358c2ecf20Sopenharmony_ci#define S5K6AA_TERM			0xffff
368c2ecf20Sopenharmony_ci#define S5K6AA_OUT_WIDTH_DEF		640
378c2ecf20Sopenharmony_ci#define S5K6AA_OUT_HEIGHT_DEF		480
388c2ecf20Sopenharmony_ci#define S5K6AA_WIN_WIDTH_MAX		1280
398c2ecf20Sopenharmony_ci#define S5K6AA_WIN_HEIGHT_MAX		1024
408c2ecf20Sopenharmony_ci#define S5K6AA_WIN_WIDTH_MIN		8
418c2ecf20Sopenharmony_ci#define S5K6AA_WIN_HEIGHT_MIN		8
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci/*
448c2ecf20Sopenharmony_ci * H/W register Interface (0xD0000000 - 0xD0000FFF)
458c2ecf20Sopenharmony_ci */
468c2ecf20Sopenharmony_ci#define AHB_MSB_ADDR_PTR		0xfcfc
478c2ecf20Sopenharmony_ci#define GEN_REG_OFFSH			0xd000
488c2ecf20Sopenharmony_ci#define REG_CMDWR_ADDRH			0x0028
498c2ecf20Sopenharmony_ci#define REG_CMDWR_ADDRL			0x002a
508c2ecf20Sopenharmony_ci#define REG_CMDRD_ADDRH			0x002c
518c2ecf20Sopenharmony_ci#define REG_CMDRD_ADDRL			0x002e
528c2ecf20Sopenharmony_ci#define REG_CMDBUF0_ADDR		0x0f12
538c2ecf20Sopenharmony_ci#define REG_CMDBUF1_ADDR		0x0f10
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci/*
568c2ecf20Sopenharmony_ci * Host S/W Register interface (0x70000000 - 0x70002000)
578c2ecf20Sopenharmony_ci * The value of the two most significant address bytes is 0x7000,
588c2ecf20Sopenharmony_ci * (HOST_SWIF_OFFS_H). The register addresses below specify 2 LSBs.
598c2ecf20Sopenharmony_ci */
608c2ecf20Sopenharmony_ci#define HOST_SWIF_OFFSH			0x7000
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci/* Initialization parameters */
638c2ecf20Sopenharmony_ci/* Master clock frequency in KHz */
648c2ecf20Sopenharmony_ci#define REG_I_INCLK_FREQ_L		0x01b8
658c2ecf20Sopenharmony_ci#define REG_I_INCLK_FREQ_H		0x01ba
668c2ecf20Sopenharmony_ci#define  MIN_MCLK_FREQ_KHZ		6000U
678c2ecf20Sopenharmony_ci#define  MAX_MCLK_FREQ_KHZ		27000U
688c2ecf20Sopenharmony_ci#define REG_I_USE_NPVI_CLOCKS		0x01c6
698c2ecf20Sopenharmony_ci#define REG_I_USE_NMIPI_CLOCKS		0x01c8
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci/* Clock configurations, n = 0..2. REG_I_* frequency unit is 4 kHz. */
728c2ecf20Sopenharmony_ci#define REG_I_OPCLK_4KHZ(n)		((n) * 6 + 0x01cc)
738c2ecf20Sopenharmony_ci#define REG_I_MIN_OUTRATE_4KHZ(n)	((n) * 6 + 0x01ce)
748c2ecf20Sopenharmony_ci#define REG_I_MAX_OUTRATE_4KHZ(n)	((n) * 6 + 0x01d0)
758c2ecf20Sopenharmony_ci#define  SYS_PLL_OUT_FREQ		(48000000 / 4000)
768c2ecf20Sopenharmony_ci#define  PCLK_FREQ_MIN			(24000000 / 4000)
778c2ecf20Sopenharmony_ci#define  PCLK_FREQ_MAX			(48000000 / 4000)
788c2ecf20Sopenharmony_ci#define REG_I_INIT_PARAMS_UPDATED	0x01e0
798c2ecf20Sopenharmony_ci#define REG_I_ERROR_INFO		0x01e2
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci/* General purpose parameters */
828c2ecf20Sopenharmony_ci#define REG_USER_BRIGHTNESS		0x01e4
838c2ecf20Sopenharmony_ci#define REG_USER_CONTRAST		0x01e6
848c2ecf20Sopenharmony_ci#define REG_USER_SATURATION		0x01e8
858c2ecf20Sopenharmony_ci#define REG_USER_SHARPBLUR		0x01ea
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci#define REG_G_SPEC_EFFECTS		0x01ee
888c2ecf20Sopenharmony_ci#define REG_G_ENABLE_PREV		0x01f0
898c2ecf20Sopenharmony_ci#define REG_G_ENABLE_PREV_CHG		0x01f2
908c2ecf20Sopenharmony_ci#define REG_G_NEW_CFG_SYNC		0x01f8
918c2ecf20Sopenharmony_ci#define REG_G_PREVZOOM_IN_WIDTH		0x020a
928c2ecf20Sopenharmony_ci#define REG_G_PREVZOOM_IN_HEIGHT	0x020c
938c2ecf20Sopenharmony_ci#define REG_G_PREVZOOM_IN_XOFFS		0x020e
948c2ecf20Sopenharmony_ci#define REG_G_PREVZOOM_IN_YOFFS		0x0210
958c2ecf20Sopenharmony_ci#define REG_G_INPUTS_CHANGE_REQ		0x021a
968c2ecf20Sopenharmony_ci#define REG_G_ACTIVE_PREV_CFG		0x021c
978c2ecf20Sopenharmony_ci#define REG_G_PREV_CFG_CHG		0x021e
988c2ecf20Sopenharmony_ci#define REG_G_PREV_OPEN_AFTER_CH	0x0220
998c2ecf20Sopenharmony_ci#define REG_G_PREV_CFG_ERROR		0x0222
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci/* Preview control section. n = 0...4. */
1028c2ecf20Sopenharmony_ci#define PREG(n, x)			((n) * 0x26 + x)
1038c2ecf20Sopenharmony_ci#define REG_P_OUT_WIDTH(n)		PREG(n, 0x0242)
1048c2ecf20Sopenharmony_ci#define REG_P_OUT_HEIGHT(n)		PREG(n, 0x0244)
1058c2ecf20Sopenharmony_ci#define REG_P_FMT(n)			PREG(n, 0x0246)
1068c2ecf20Sopenharmony_ci#define REG_P_MAX_OUT_RATE(n)		PREG(n, 0x0248)
1078c2ecf20Sopenharmony_ci#define REG_P_MIN_OUT_RATE(n)		PREG(n, 0x024a)
1088c2ecf20Sopenharmony_ci#define REG_P_PVI_MASK(n)		PREG(n, 0x024c)
1098c2ecf20Sopenharmony_ci#define REG_P_CLK_INDEX(n)		PREG(n, 0x024e)
1108c2ecf20Sopenharmony_ci#define REG_P_FR_RATE_TYPE(n)		PREG(n, 0x0250)
1118c2ecf20Sopenharmony_ci#define  FR_RATE_DYNAMIC		0
1128c2ecf20Sopenharmony_ci#define  FR_RATE_FIXED			1
1138c2ecf20Sopenharmony_ci#define  FR_RATE_FIXED_ACCURATE		2
1148c2ecf20Sopenharmony_ci#define REG_P_FR_RATE_Q_TYPE(n)		PREG(n, 0x0252)
1158c2ecf20Sopenharmony_ci#define  FR_RATE_Q_BEST_FRRATE		1 /* Binning enabled */
1168c2ecf20Sopenharmony_ci#define  FR_RATE_Q_BEST_QUALITY		2 /* Binning disabled */
1178c2ecf20Sopenharmony_ci/* Frame period in 0.1 ms units */
1188c2ecf20Sopenharmony_ci#define REG_P_MAX_FR_TIME(n)		PREG(n, 0x0254)
1198c2ecf20Sopenharmony_ci#define REG_P_MIN_FR_TIME(n)		PREG(n, 0x0256)
1208c2ecf20Sopenharmony_ci/* Conversion to REG_P_[MAX/MIN]_FR_TIME value; __t: time in us */
1218c2ecf20Sopenharmony_ci#define  US_TO_FR_TIME(__t)		((__t) / 100)
1228c2ecf20Sopenharmony_ci#define  S5K6AA_MIN_FR_TIME		33300  /* us */
1238c2ecf20Sopenharmony_ci#define  S5K6AA_MAX_FR_TIME		650000 /* us */
1248c2ecf20Sopenharmony_ci#define  S5K6AA_MAX_HIGHRES_FR_TIME	666    /* x100 us */
1258c2ecf20Sopenharmony_ci/* The below 5 registers are for "device correction" values */
1268c2ecf20Sopenharmony_ci#define REG_P_COLORTEMP(n)		PREG(n, 0x025e)
1278c2ecf20Sopenharmony_ci#define REG_P_PREV_MIRROR(n)		PREG(n, 0x0262)
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci/* Extended image property controls */
1308c2ecf20Sopenharmony_ci/* Exposure time in 10 us units */
1318c2ecf20Sopenharmony_ci#define REG_SF_USR_EXPOSURE_L		0x03c6
1328c2ecf20Sopenharmony_ci#define REG_SF_USR_EXPOSURE_H		0x03c8
1338c2ecf20Sopenharmony_ci#define REG_SF_USR_EXPOSURE_CHG		0x03ca
1348c2ecf20Sopenharmony_ci#define REG_SF_USR_TOT_GAIN		0x03cc
1358c2ecf20Sopenharmony_ci#define REG_SF_USR_TOT_GAIN_CHG		0x03ce
1368c2ecf20Sopenharmony_ci#define REG_SF_RGAIN			0x03d0
1378c2ecf20Sopenharmony_ci#define REG_SF_RGAIN_CHG		0x03d2
1388c2ecf20Sopenharmony_ci#define REG_SF_GGAIN			0x03d4
1398c2ecf20Sopenharmony_ci#define REG_SF_GGAIN_CHG		0x03d6
1408c2ecf20Sopenharmony_ci#define REG_SF_BGAIN			0x03d8
1418c2ecf20Sopenharmony_ci#define REG_SF_BGAIN_CHG		0x03da
1428c2ecf20Sopenharmony_ci#define REG_SF_FLICKER_QUANT		0x03dc
1438c2ecf20Sopenharmony_ci#define REG_SF_FLICKER_QUANT_CHG	0x03de
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci/* Output interface (parallel/MIPI) setup */
1468c2ecf20Sopenharmony_ci#define REG_OIF_EN_MIPI_LANES		0x03fa
1478c2ecf20Sopenharmony_ci#define REG_OIF_EN_PACKETS		0x03fc
1488c2ecf20Sopenharmony_ci#define REG_OIF_CFG_CHG			0x03fe
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci/* Auto-algorithms enable mask */
1518c2ecf20Sopenharmony_ci#define REG_DBG_AUTOALG_EN		0x0400
1528c2ecf20Sopenharmony_ci#define  AALG_ALL_EN_MASK		(1 << 0)
1538c2ecf20Sopenharmony_ci#define  AALG_AE_EN_MASK		(1 << 1)
1548c2ecf20Sopenharmony_ci#define  AALG_DIVLEI_EN_MASK		(1 << 2)
1558c2ecf20Sopenharmony_ci#define  AALG_WB_EN_MASK		(1 << 3)
1568c2ecf20Sopenharmony_ci#define  AALG_FLICKER_EN_MASK		(1 << 5)
1578c2ecf20Sopenharmony_ci#define  AALG_FIT_EN_MASK		(1 << 6)
1588c2ecf20Sopenharmony_ci#define  AALG_WRHW_EN_MASK		(1 << 7)
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci/* Firmware revision information */
1618c2ecf20Sopenharmony_ci#define REG_FW_APIVER			0x012e
1628c2ecf20Sopenharmony_ci#define  S5K6AAFX_FW_APIVER		0x0001
1638c2ecf20Sopenharmony_ci#define REG_FW_REVISION			0x0130
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci/* For now we use only one user configuration register set */
1668c2ecf20Sopenharmony_ci#define S5K6AA_MAX_PRESETS		1
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_cistatic const char * const s5k6aa_supply_names[] = {
1698c2ecf20Sopenharmony_ci	"vdd_core",	/* Digital core supply 1.5V (1.4V to 1.6V) */
1708c2ecf20Sopenharmony_ci	"vdda",		/* Analog power supply 2.8V (2.6V to 3.0V) */
1718c2ecf20Sopenharmony_ci	"vdd_reg",	/* Regulator input power 1.8V (1.7V to 1.9V)
1728c2ecf20Sopenharmony_ci			   or 2.8V (2.6V to 3.0) */
1738c2ecf20Sopenharmony_ci	"vddio",	/* I/O supply 1.8V (1.65V to 1.95V)
1748c2ecf20Sopenharmony_ci			   or 2.8V (2.5V to 3.1V) */
1758c2ecf20Sopenharmony_ci};
1768c2ecf20Sopenharmony_ci#define S5K6AA_NUM_SUPPLIES ARRAY_SIZE(s5k6aa_supply_names)
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_cienum s5k6aa_gpio_id {
1798c2ecf20Sopenharmony_ci	STBY,
1808c2ecf20Sopenharmony_ci	RSET,
1818c2ecf20Sopenharmony_ci	GPIO_NUM,
1828c2ecf20Sopenharmony_ci};
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_cistruct s5k6aa_regval {
1858c2ecf20Sopenharmony_ci	u16 addr;
1868c2ecf20Sopenharmony_ci	u16 val;
1878c2ecf20Sopenharmony_ci};
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistruct s5k6aa_pixfmt {
1908c2ecf20Sopenharmony_ci	u32 code;
1918c2ecf20Sopenharmony_ci	u32 colorspace;
1928c2ecf20Sopenharmony_ci	/* REG_P_FMT(x) register value */
1938c2ecf20Sopenharmony_ci	u16 reg_p_fmt;
1948c2ecf20Sopenharmony_ci};
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_cistruct s5k6aa_preset {
1978c2ecf20Sopenharmony_ci	/* output pixel format and resolution */
1988c2ecf20Sopenharmony_ci	struct v4l2_mbus_framefmt mbus_fmt;
1998c2ecf20Sopenharmony_ci	u8 clk_id;
2008c2ecf20Sopenharmony_ci	u8 index;
2018c2ecf20Sopenharmony_ci};
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistruct s5k6aa_ctrls {
2048c2ecf20Sopenharmony_ci	struct v4l2_ctrl_handler handler;
2058c2ecf20Sopenharmony_ci	/* Auto / manual white balance cluster */
2068c2ecf20Sopenharmony_ci	struct v4l2_ctrl *awb;
2078c2ecf20Sopenharmony_ci	struct v4l2_ctrl *gain_red;
2088c2ecf20Sopenharmony_ci	struct v4l2_ctrl *gain_blue;
2098c2ecf20Sopenharmony_ci	struct v4l2_ctrl *gain_green;
2108c2ecf20Sopenharmony_ci	/* Mirror cluster */
2118c2ecf20Sopenharmony_ci	struct v4l2_ctrl *hflip;
2128c2ecf20Sopenharmony_ci	struct v4l2_ctrl *vflip;
2138c2ecf20Sopenharmony_ci	/* Auto exposure / manual exposure and gain cluster */
2148c2ecf20Sopenharmony_ci	struct v4l2_ctrl *auto_exp;
2158c2ecf20Sopenharmony_ci	struct v4l2_ctrl *exposure;
2168c2ecf20Sopenharmony_ci	struct v4l2_ctrl *gain;
2178c2ecf20Sopenharmony_ci};
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cistruct s5k6aa_interval {
2208c2ecf20Sopenharmony_ci	u16 reg_fr_time;
2218c2ecf20Sopenharmony_ci	struct v4l2_fract interval;
2228c2ecf20Sopenharmony_ci	/* Maximum rectangle for the interval */
2238c2ecf20Sopenharmony_ci	struct v4l2_frmsize_discrete size;
2248c2ecf20Sopenharmony_ci};
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cistruct s5k6aa {
2278c2ecf20Sopenharmony_ci	struct v4l2_subdev sd;
2288c2ecf20Sopenharmony_ci	struct media_pad pad;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	enum v4l2_mbus_type bus_type;
2318c2ecf20Sopenharmony_ci	u8 mipi_lanes;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	int (*s_power)(int enable);
2348c2ecf20Sopenharmony_ci	struct regulator_bulk_data supplies[S5K6AA_NUM_SUPPLIES];
2358c2ecf20Sopenharmony_ci	struct s5k6aa_gpio gpio[GPIO_NUM];
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	/* external master clock frequency */
2388c2ecf20Sopenharmony_ci	unsigned long mclk_frequency;
2398c2ecf20Sopenharmony_ci	/* ISP internal master clock frequency */
2408c2ecf20Sopenharmony_ci	u16 clk_fop;
2418c2ecf20Sopenharmony_ci	/* output pixel clock frequency range */
2428c2ecf20Sopenharmony_ci	u16 pclk_fmin;
2438c2ecf20Sopenharmony_ci	u16 pclk_fmax;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	unsigned int inv_hflip:1;
2468c2ecf20Sopenharmony_ci	unsigned int inv_vflip:1;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	/* protects the struct members below */
2498c2ecf20Sopenharmony_ci	struct mutex lock;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	/* sensor matrix scan window */
2528c2ecf20Sopenharmony_ci	struct v4l2_rect ccd_rect;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	struct s5k6aa_ctrls ctrls;
2558c2ecf20Sopenharmony_ci	struct s5k6aa_preset presets[S5K6AA_MAX_PRESETS];
2568c2ecf20Sopenharmony_ci	struct s5k6aa_preset *preset;
2578c2ecf20Sopenharmony_ci	const struct s5k6aa_interval *fiv;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	unsigned int streaming:1;
2608c2ecf20Sopenharmony_ci	unsigned int apply_cfg:1;
2618c2ecf20Sopenharmony_ci	unsigned int apply_crop:1;
2628c2ecf20Sopenharmony_ci	unsigned int power;
2638c2ecf20Sopenharmony_ci};
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_cistatic struct s5k6aa_regval s5k6aa_analog_config[] = {
2668c2ecf20Sopenharmony_ci	/* Analog settings */
2678c2ecf20Sopenharmony_ci	{ 0x112a, 0x0000 }, { 0x1132, 0x0000 },
2688c2ecf20Sopenharmony_ci	{ 0x113e, 0x0000 }, { 0x115c, 0x0000 },
2698c2ecf20Sopenharmony_ci	{ 0x1164, 0x0000 }, { 0x1174, 0x0000 },
2708c2ecf20Sopenharmony_ci	{ 0x1178, 0x0000 }, { 0x077a, 0x0000 },
2718c2ecf20Sopenharmony_ci	{ 0x077c, 0x0000 }, { 0x077e, 0x0000 },
2728c2ecf20Sopenharmony_ci	{ 0x0780, 0x0000 }, { 0x0782, 0x0000 },
2738c2ecf20Sopenharmony_ci	{ 0x0784, 0x0000 }, { 0x0786, 0x0000 },
2748c2ecf20Sopenharmony_ci	{ 0x0788, 0x0000 }, { 0x07a2, 0x0000 },
2758c2ecf20Sopenharmony_ci	{ 0x07a4, 0x0000 }, { 0x07a6, 0x0000 },
2768c2ecf20Sopenharmony_ci	{ 0x07a8, 0x0000 }, { 0x07b6, 0x0000 },
2778c2ecf20Sopenharmony_ci	{ 0x07b8, 0x0002 }, { 0x07ba, 0x0004 },
2788c2ecf20Sopenharmony_ci	{ 0x07bc, 0x0004 }, { 0x07be, 0x0005 },
2798c2ecf20Sopenharmony_ci	{ 0x07c0, 0x0005 }, { S5K6AA_TERM, 0 },
2808c2ecf20Sopenharmony_ci};
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci/* TODO: Add RGB888 and Bayer format */
2838c2ecf20Sopenharmony_cistatic const struct s5k6aa_pixfmt s5k6aa_formats[] = {
2848c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_YUYV8_2X8,	V4L2_COLORSPACE_JPEG,	5 },
2858c2ecf20Sopenharmony_ci	/* range 16-240 */
2868c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_YUYV8_2X8,	V4L2_COLORSPACE_REC709,	6 },
2878c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_RGB565_2X8_BE,	V4L2_COLORSPACE_JPEG,	0 },
2888c2ecf20Sopenharmony_ci};
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cistatic const struct s5k6aa_interval s5k6aa_intervals[] = {
2918c2ecf20Sopenharmony_ci	{ 1000, {10000, 1000000}, {1280, 1024} }, /* 10 fps */
2928c2ecf20Sopenharmony_ci	{ 666,  {15000, 1000000}, {1280, 1024} }, /* 15 fps */
2938c2ecf20Sopenharmony_ci	{ 500,  {20000, 1000000}, {1280, 720} },  /* 20 fps */
2948c2ecf20Sopenharmony_ci	{ 400,  {25000, 1000000}, {640, 480} },   /* 25 fps */
2958c2ecf20Sopenharmony_ci	{ 333,  {33300, 1000000}, {640, 480} },   /* 30 fps */
2968c2ecf20Sopenharmony_ci};
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci#define S5K6AA_INTERVAL_DEF_INDEX 1 /* 15 fps */
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_cistatic inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	return &container_of(ctrl->handler, struct s5k6aa, ctrls.handler)->sd;
3038c2ecf20Sopenharmony_ci}
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_cistatic inline struct s5k6aa *to_s5k6aa(struct v4l2_subdev *sd)
3068c2ecf20Sopenharmony_ci{
3078c2ecf20Sopenharmony_ci	return container_of(sd, struct s5k6aa, sd);
3088c2ecf20Sopenharmony_ci}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci/* Set initial values for all preview presets */
3118c2ecf20Sopenharmony_cistatic void s5k6aa_presets_data_init(struct s5k6aa *s5k6aa)
3128c2ecf20Sopenharmony_ci{
3138c2ecf20Sopenharmony_ci	struct s5k6aa_preset *preset = &s5k6aa->presets[0];
3148c2ecf20Sopenharmony_ci	int i;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	for (i = 0; i < S5K6AA_MAX_PRESETS; i++) {
3178c2ecf20Sopenharmony_ci		preset->mbus_fmt.width	= S5K6AA_OUT_WIDTH_DEF;
3188c2ecf20Sopenharmony_ci		preset->mbus_fmt.height	= S5K6AA_OUT_HEIGHT_DEF;
3198c2ecf20Sopenharmony_ci		preset->mbus_fmt.code	= s5k6aa_formats[0].code;
3208c2ecf20Sopenharmony_ci		preset->index		= i;
3218c2ecf20Sopenharmony_ci		preset->clk_id		= 0;
3228c2ecf20Sopenharmony_ci		preset++;
3238c2ecf20Sopenharmony_ci	}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	s5k6aa->fiv = &s5k6aa_intervals[S5K6AA_INTERVAL_DEF_INDEX];
3268c2ecf20Sopenharmony_ci	s5k6aa->preset = &s5k6aa->presets[0];
3278c2ecf20Sopenharmony_ci}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_cistatic int s5k6aa_i2c_read(struct i2c_client *client, u16 addr, u16 *val)
3308c2ecf20Sopenharmony_ci{
3318c2ecf20Sopenharmony_ci	u8 wbuf[2] = {addr >> 8, addr & 0xFF};
3328c2ecf20Sopenharmony_ci	struct i2c_msg msg[2];
3338c2ecf20Sopenharmony_ci	u8 rbuf[2];
3348c2ecf20Sopenharmony_ci	int ret;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	msg[0].addr = client->addr;
3378c2ecf20Sopenharmony_ci	msg[0].flags = 0;
3388c2ecf20Sopenharmony_ci	msg[0].len = 2;
3398c2ecf20Sopenharmony_ci	msg[0].buf = wbuf;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	msg[1].addr = client->addr;
3428c2ecf20Sopenharmony_ci	msg[1].flags = I2C_M_RD;
3438c2ecf20Sopenharmony_ci	msg[1].len = 2;
3448c2ecf20Sopenharmony_ci	msg[1].buf = rbuf;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	ret = i2c_transfer(client->adapter, msg, 2);
3478c2ecf20Sopenharmony_ci	*val = be16_to_cpu(*((__be16 *)rbuf));
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	v4l2_dbg(3, debug, client, "i2c_read: 0x%04X : 0x%04x\n", addr, *val);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	return ret == 2 ? 0 : ret;
3528c2ecf20Sopenharmony_ci}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_cistatic int s5k6aa_i2c_write(struct i2c_client *client, u16 addr, u16 val)
3558c2ecf20Sopenharmony_ci{
3568c2ecf20Sopenharmony_ci	u8 buf[4] = {addr >> 8, addr & 0xFF, val >> 8, val & 0xFF};
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	int ret = i2c_master_send(client, buf, 4);
3598c2ecf20Sopenharmony_ci	v4l2_dbg(3, debug, client, "i2c_write: 0x%04X : 0x%04x\n", addr, val);
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	return ret == 4 ? 0 : ret;
3628c2ecf20Sopenharmony_ci}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci/* The command register write, assumes Command_Wr_addH = 0x7000. */
3658c2ecf20Sopenharmony_cistatic int s5k6aa_write(struct i2c_client *c, u16 addr, u16 val)
3668c2ecf20Sopenharmony_ci{
3678c2ecf20Sopenharmony_ci	int ret = s5k6aa_i2c_write(c, REG_CMDWR_ADDRL, addr);
3688c2ecf20Sopenharmony_ci	if (ret)
3698c2ecf20Sopenharmony_ci		return ret;
3708c2ecf20Sopenharmony_ci	return s5k6aa_i2c_write(c, REG_CMDBUF0_ADDR, val);
3718c2ecf20Sopenharmony_ci}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci/* The command register read, assumes Command_Rd_addH = 0x7000. */
3748c2ecf20Sopenharmony_cistatic int s5k6aa_read(struct i2c_client *client, u16 addr, u16 *val)
3758c2ecf20Sopenharmony_ci{
3768c2ecf20Sopenharmony_ci	int ret = s5k6aa_i2c_write(client, REG_CMDRD_ADDRL, addr);
3778c2ecf20Sopenharmony_ci	if (ret)
3788c2ecf20Sopenharmony_ci		return ret;
3798c2ecf20Sopenharmony_ci	return s5k6aa_i2c_read(client, REG_CMDBUF0_ADDR, val);
3808c2ecf20Sopenharmony_ci}
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_cistatic int s5k6aa_write_array(struct v4l2_subdev *sd,
3838c2ecf20Sopenharmony_ci			      const struct s5k6aa_regval *msg)
3848c2ecf20Sopenharmony_ci{
3858c2ecf20Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(sd);
3868c2ecf20Sopenharmony_ci	u16 addr_incr = 0;
3878c2ecf20Sopenharmony_ci	int ret = 0;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	while (msg->addr != S5K6AA_TERM) {
3908c2ecf20Sopenharmony_ci		if (addr_incr != 2)
3918c2ecf20Sopenharmony_ci			ret = s5k6aa_i2c_write(client, REG_CMDWR_ADDRL,
3928c2ecf20Sopenharmony_ci					       msg->addr);
3938c2ecf20Sopenharmony_ci		if (ret)
3948c2ecf20Sopenharmony_ci			break;
3958c2ecf20Sopenharmony_ci		ret = s5k6aa_i2c_write(client, REG_CMDBUF0_ADDR, msg->val);
3968c2ecf20Sopenharmony_ci		if (ret)
3978c2ecf20Sopenharmony_ci			break;
3988c2ecf20Sopenharmony_ci		/* Assume that msg->addr is always less than 0xfffc */
3998c2ecf20Sopenharmony_ci		addr_incr = (msg + 1)->addr - msg->addr;
4008c2ecf20Sopenharmony_ci		msg++;
4018c2ecf20Sopenharmony_ci	}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	return ret;
4048c2ecf20Sopenharmony_ci}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci/* Configure the AHB high address bytes for GTG registers access */
4078c2ecf20Sopenharmony_cistatic int s5k6aa_set_ahb_address(struct i2c_client *client)
4088c2ecf20Sopenharmony_ci{
4098c2ecf20Sopenharmony_ci	int ret = s5k6aa_i2c_write(client, AHB_MSB_ADDR_PTR, GEN_REG_OFFSH);
4108c2ecf20Sopenharmony_ci	if (ret)
4118c2ecf20Sopenharmony_ci		return ret;
4128c2ecf20Sopenharmony_ci	ret = s5k6aa_i2c_write(client, REG_CMDRD_ADDRH, HOST_SWIF_OFFSH);
4138c2ecf20Sopenharmony_ci	if (ret)
4148c2ecf20Sopenharmony_ci		return ret;
4158c2ecf20Sopenharmony_ci	return s5k6aa_i2c_write(client, REG_CMDWR_ADDRH, HOST_SWIF_OFFSH);
4168c2ecf20Sopenharmony_ci}
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci/**
4198c2ecf20Sopenharmony_ci * s5k6aa_configure_pixel_clock - apply ISP main clock/PLL configuration
4208c2ecf20Sopenharmony_ci * @s5k6aa: pointer to &struct s5k6aa describing the device
4218c2ecf20Sopenharmony_ci *
4228c2ecf20Sopenharmony_ci * Configure the internal ISP PLL for the required output frequency.
4238c2ecf20Sopenharmony_ci * Locking: called with s5k6aa.lock mutex held.
4248c2ecf20Sopenharmony_ci */
4258c2ecf20Sopenharmony_cistatic int s5k6aa_configure_pixel_clocks(struct s5k6aa *s5k6aa)
4268c2ecf20Sopenharmony_ci{
4278c2ecf20Sopenharmony_ci	struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd);
4288c2ecf20Sopenharmony_ci	unsigned long fmclk = s5k6aa->mclk_frequency / 1000;
4298c2ecf20Sopenharmony_ci	u16 status;
4308c2ecf20Sopenharmony_ci	int ret;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	if (WARN(fmclk < MIN_MCLK_FREQ_KHZ || fmclk > MAX_MCLK_FREQ_KHZ,
4338c2ecf20Sopenharmony_ci		 "Invalid clock frequency: %ld\n", fmclk))
4348c2ecf20Sopenharmony_ci		return -EINVAL;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	s5k6aa->pclk_fmin = PCLK_FREQ_MIN;
4378c2ecf20Sopenharmony_ci	s5k6aa->pclk_fmax = PCLK_FREQ_MAX;
4388c2ecf20Sopenharmony_ci	s5k6aa->clk_fop = SYS_PLL_OUT_FREQ;
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	/* External input clock frequency in kHz */
4418c2ecf20Sopenharmony_ci	ret = s5k6aa_write(c, REG_I_INCLK_FREQ_H, fmclk >> 16);
4428c2ecf20Sopenharmony_ci	if (!ret)
4438c2ecf20Sopenharmony_ci		ret = s5k6aa_write(c, REG_I_INCLK_FREQ_L, fmclk & 0xFFFF);
4448c2ecf20Sopenharmony_ci	if (!ret)
4458c2ecf20Sopenharmony_ci		ret = s5k6aa_write(c, REG_I_USE_NPVI_CLOCKS, 1);
4468c2ecf20Sopenharmony_ci	/* Internal PLL frequency */
4478c2ecf20Sopenharmony_ci	if (!ret)
4488c2ecf20Sopenharmony_ci		ret = s5k6aa_write(c, REG_I_OPCLK_4KHZ(0), s5k6aa->clk_fop);
4498c2ecf20Sopenharmony_ci	if (!ret)
4508c2ecf20Sopenharmony_ci		ret = s5k6aa_write(c, REG_I_MIN_OUTRATE_4KHZ(0),
4518c2ecf20Sopenharmony_ci				   s5k6aa->pclk_fmin);
4528c2ecf20Sopenharmony_ci	if (!ret)
4538c2ecf20Sopenharmony_ci		ret = s5k6aa_write(c, REG_I_MAX_OUTRATE_4KHZ(0),
4548c2ecf20Sopenharmony_ci				   s5k6aa->pclk_fmax);
4558c2ecf20Sopenharmony_ci	if (!ret)
4568c2ecf20Sopenharmony_ci		ret = s5k6aa_write(c, REG_I_INIT_PARAMS_UPDATED, 1);
4578c2ecf20Sopenharmony_ci	if (!ret)
4588c2ecf20Sopenharmony_ci		ret = s5k6aa_read(c, REG_I_ERROR_INFO, &status);
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	return ret ? ret : (status ? -EINVAL : 0);
4618c2ecf20Sopenharmony_ci}
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci/* Set horizontal and vertical image flipping */
4648c2ecf20Sopenharmony_cistatic int s5k6aa_set_mirror(struct s5k6aa *s5k6aa, int horiz_flip)
4658c2ecf20Sopenharmony_ci{
4668c2ecf20Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
4678c2ecf20Sopenharmony_ci	int index = s5k6aa->preset->index;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	unsigned int vflip = s5k6aa->ctrls.vflip->val ^ s5k6aa->inv_vflip;
4708c2ecf20Sopenharmony_ci	unsigned int flip = (horiz_flip ^ s5k6aa->inv_hflip) | (vflip << 1);
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	return s5k6aa_write(client, REG_P_PREV_MIRROR(index), flip);
4738c2ecf20Sopenharmony_ci}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci/* Configure auto/manual white balance and R/G/B gains */
4768c2ecf20Sopenharmony_cistatic int s5k6aa_set_awb(struct s5k6aa *s5k6aa, int awb)
4778c2ecf20Sopenharmony_ci{
4788c2ecf20Sopenharmony_ci	struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd);
4798c2ecf20Sopenharmony_ci	struct s5k6aa_ctrls *ctrls = &s5k6aa->ctrls;
4808c2ecf20Sopenharmony_ci	u16 reg;
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	int ret = s5k6aa_read(c, REG_DBG_AUTOALG_EN, &reg);
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	if (!ret && !awb) {
4858c2ecf20Sopenharmony_ci		ret = s5k6aa_write(c, REG_SF_RGAIN, ctrls->gain_red->val);
4868c2ecf20Sopenharmony_ci		if (!ret)
4878c2ecf20Sopenharmony_ci			ret = s5k6aa_write(c, REG_SF_RGAIN_CHG, 1);
4888c2ecf20Sopenharmony_ci		if (ret)
4898c2ecf20Sopenharmony_ci			return ret;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci		ret = s5k6aa_write(c, REG_SF_GGAIN, ctrls->gain_green->val);
4928c2ecf20Sopenharmony_ci		if (!ret)
4938c2ecf20Sopenharmony_ci			ret = s5k6aa_write(c, REG_SF_GGAIN_CHG, 1);
4948c2ecf20Sopenharmony_ci		if (ret)
4958c2ecf20Sopenharmony_ci			return ret;
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci		ret = s5k6aa_write(c, REG_SF_BGAIN, ctrls->gain_blue->val);
4988c2ecf20Sopenharmony_ci		if (!ret)
4998c2ecf20Sopenharmony_ci			ret = s5k6aa_write(c, REG_SF_BGAIN_CHG, 1);
5008c2ecf20Sopenharmony_ci	}
5018c2ecf20Sopenharmony_ci	if (!ret) {
5028c2ecf20Sopenharmony_ci		reg = awb ? reg | AALG_WB_EN_MASK : reg & ~AALG_WB_EN_MASK;
5038c2ecf20Sopenharmony_ci		ret = s5k6aa_write(c, REG_DBG_AUTOALG_EN, reg);
5048c2ecf20Sopenharmony_ci	}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	return ret;
5078c2ecf20Sopenharmony_ci}
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci/* Program FW with exposure time, 'exposure' in us units */
5108c2ecf20Sopenharmony_cistatic int s5k6aa_set_user_exposure(struct i2c_client *client, int exposure)
5118c2ecf20Sopenharmony_ci{
5128c2ecf20Sopenharmony_ci	unsigned int time = exposure / 10;
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	int ret = s5k6aa_write(client, REG_SF_USR_EXPOSURE_L, time & 0xffff);
5158c2ecf20Sopenharmony_ci	if (!ret)
5168c2ecf20Sopenharmony_ci		ret = s5k6aa_write(client, REG_SF_USR_EXPOSURE_H, time >> 16);
5178c2ecf20Sopenharmony_ci	if (ret)
5188c2ecf20Sopenharmony_ci		return ret;
5198c2ecf20Sopenharmony_ci	return s5k6aa_write(client, REG_SF_USR_EXPOSURE_CHG, 1);
5208c2ecf20Sopenharmony_ci}
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_cistatic int s5k6aa_set_user_gain(struct i2c_client *client, int gain)
5238c2ecf20Sopenharmony_ci{
5248c2ecf20Sopenharmony_ci	int ret = s5k6aa_write(client, REG_SF_USR_TOT_GAIN, gain);
5258c2ecf20Sopenharmony_ci	if (ret)
5268c2ecf20Sopenharmony_ci		return ret;
5278c2ecf20Sopenharmony_ci	return s5k6aa_write(client, REG_SF_USR_TOT_GAIN_CHG, 1);
5288c2ecf20Sopenharmony_ci}
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci/* Set auto/manual exposure and total gain */
5318c2ecf20Sopenharmony_cistatic int s5k6aa_set_auto_exposure(struct s5k6aa *s5k6aa, int value)
5328c2ecf20Sopenharmony_ci{
5338c2ecf20Sopenharmony_ci	struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd);
5348c2ecf20Sopenharmony_ci	unsigned int exp_time = s5k6aa->ctrls.exposure->val;
5358c2ecf20Sopenharmony_ci	u16 auto_alg;
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	int ret = s5k6aa_read(c, REG_DBG_AUTOALG_EN, &auto_alg);
5388c2ecf20Sopenharmony_ci	if (ret)
5398c2ecf20Sopenharmony_ci		return ret;
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	v4l2_dbg(1, debug, c, "man_exp: %d, auto_exp: %d, a_alg: 0x%x\n",
5428c2ecf20Sopenharmony_ci		 exp_time, value, auto_alg);
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	if (value == V4L2_EXPOSURE_AUTO) {
5458c2ecf20Sopenharmony_ci		auto_alg |= AALG_AE_EN_MASK | AALG_DIVLEI_EN_MASK;
5468c2ecf20Sopenharmony_ci	} else {
5478c2ecf20Sopenharmony_ci		ret = s5k6aa_set_user_exposure(c, exp_time);
5488c2ecf20Sopenharmony_ci		if (ret)
5498c2ecf20Sopenharmony_ci			return ret;
5508c2ecf20Sopenharmony_ci		ret = s5k6aa_set_user_gain(c, s5k6aa->ctrls.gain->val);
5518c2ecf20Sopenharmony_ci		if (ret)
5528c2ecf20Sopenharmony_ci			return ret;
5538c2ecf20Sopenharmony_ci		auto_alg &= ~(AALG_AE_EN_MASK | AALG_DIVLEI_EN_MASK);
5548c2ecf20Sopenharmony_ci	}
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	return s5k6aa_write(c, REG_DBG_AUTOALG_EN, auto_alg);
5578c2ecf20Sopenharmony_ci}
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_cistatic int s5k6aa_set_anti_flicker(struct s5k6aa *s5k6aa, int value)
5608c2ecf20Sopenharmony_ci{
5618c2ecf20Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
5628c2ecf20Sopenharmony_ci	u16 auto_alg;
5638c2ecf20Sopenharmony_ci	int ret;
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	ret = s5k6aa_read(client, REG_DBG_AUTOALG_EN, &auto_alg);
5668c2ecf20Sopenharmony_ci	if (ret)
5678c2ecf20Sopenharmony_ci		return ret;
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	if (value == V4L2_CID_POWER_LINE_FREQUENCY_AUTO) {
5708c2ecf20Sopenharmony_ci		auto_alg |= AALG_FLICKER_EN_MASK;
5718c2ecf20Sopenharmony_ci	} else {
5728c2ecf20Sopenharmony_ci		auto_alg &= ~AALG_FLICKER_EN_MASK;
5738c2ecf20Sopenharmony_ci		/* The V4L2_CID_LINE_FREQUENCY control values match
5748c2ecf20Sopenharmony_ci		 * the register values */
5758c2ecf20Sopenharmony_ci		ret = s5k6aa_write(client, REG_SF_FLICKER_QUANT, value);
5768c2ecf20Sopenharmony_ci		if (ret)
5778c2ecf20Sopenharmony_ci			return ret;
5788c2ecf20Sopenharmony_ci		ret = s5k6aa_write(client, REG_SF_FLICKER_QUANT_CHG, 1);
5798c2ecf20Sopenharmony_ci		if (ret)
5808c2ecf20Sopenharmony_ci			return ret;
5818c2ecf20Sopenharmony_ci	}
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	return s5k6aa_write(client, REG_DBG_AUTOALG_EN, auto_alg);
5848c2ecf20Sopenharmony_ci}
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_cistatic int s5k6aa_set_colorfx(struct s5k6aa *s5k6aa, int val)
5878c2ecf20Sopenharmony_ci{
5888c2ecf20Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
5898c2ecf20Sopenharmony_ci	static const struct v4l2_control colorfx[] = {
5908c2ecf20Sopenharmony_ci		{ V4L2_COLORFX_NONE,	 0 },
5918c2ecf20Sopenharmony_ci		{ V4L2_COLORFX_BW,	 1 },
5928c2ecf20Sopenharmony_ci		{ V4L2_COLORFX_NEGATIVE, 2 },
5938c2ecf20Sopenharmony_ci		{ V4L2_COLORFX_SEPIA,	 3 },
5948c2ecf20Sopenharmony_ci		{ V4L2_COLORFX_SKY_BLUE, 4 },
5958c2ecf20Sopenharmony_ci		{ V4L2_COLORFX_SKETCH,	 5 },
5968c2ecf20Sopenharmony_ci	};
5978c2ecf20Sopenharmony_ci	int i;
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(colorfx); i++) {
6008c2ecf20Sopenharmony_ci		if (colorfx[i].id == val)
6018c2ecf20Sopenharmony_ci			return s5k6aa_write(client, REG_G_SPEC_EFFECTS,
6028c2ecf20Sopenharmony_ci					    colorfx[i].value);
6038c2ecf20Sopenharmony_ci	}
6048c2ecf20Sopenharmony_ci	return -EINVAL;
6058c2ecf20Sopenharmony_ci}
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_cistatic int s5k6aa_preview_config_status(struct i2c_client *client)
6088c2ecf20Sopenharmony_ci{
6098c2ecf20Sopenharmony_ci	u16 error = 0;
6108c2ecf20Sopenharmony_ci	int ret = s5k6aa_read(client, REG_G_PREV_CFG_ERROR, &error);
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	v4l2_dbg(1, debug, client, "error: 0x%x (%d)\n", error, ret);
6138c2ecf20Sopenharmony_ci	return ret ? ret : (error ? -EINVAL : 0);
6148c2ecf20Sopenharmony_ci}
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_cistatic int s5k6aa_get_pixfmt_index(struct s5k6aa *s5k6aa,
6178c2ecf20Sopenharmony_ci				   struct v4l2_mbus_framefmt *mf)
6188c2ecf20Sopenharmony_ci{
6198c2ecf20Sopenharmony_ci	unsigned int i;
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(s5k6aa_formats); i++)
6228c2ecf20Sopenharmony_ci		if (mf->colorspace == s5k6aa_formats[i].colorspace &&
6238c2ecf20Sopenharmony_ci		    mf->code == s5k6aa_formats[i].code)
6248c2ecf20Sopenharmony_ci			return i;
6258c2ecf20Sopenharmony_ci	return 0;
6268c2ecf20Sopenharmony_ci}
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_cistatic int s5k6aa_set_output_framefmt(struct s5k6aa *s5k6aa,
6298c2ecf20Sopenharmony_ci				      struct s5k6aa_preset *preset)
6308c2ecf20Sopenharmony_ci{
6318c2ecf20Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
6328c2ecf20Sopenharmony_ci	int fmt_index = s5k6aa_get_pixfmt_index(s5k6aa, &preset->mbus_fmt);
6338c2ecf20Sopenharmony_ci	int ret;
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	ret = s5k6aa_write(client, REG_P_OUT_WIDTH(preset->index),
6368c2ecf20Sopenharmony_ci			   preset->mbus_fmt.width);
6378c2ecf20Sopenharmony_ci	if (!ret)
6388c2ecf20Sopenharmony_ci		ret = s5k6aa_write(client, REG_P_OUT_HEIGHT(preset->index),
6398c2ecf20Sopenharmony_ci				   preset->mbus_fmt.height);
6408c2ecf20Sopenharmony_ci	if (!ret)
6418c2ecf20Sopenharmony_ci		ret = s5k6aa_write(client, REG_P_FMT(preset->index),
6428c2ecf20Sopenharmony_ci				   s5k6aa_formats[fmt_index].reg_p_fmt);
6438c2ecf20Sopenharmony_ci	return ret;
6448c2ecf20Sopenharmony_ci}
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_cistatic int s5k6aa_set_input_params(struct s5k6aa *s5k6aa)
6478c2ecf20Sopenharmony_ci{
6488c2ecf20Sopenharmony_ci	struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd);
6498c2ecf20Sopenharmony_ci	struct v4l2_rect *r = &s5k6aa->ccd_rect;
6508c2ecf20Sopenharmony_ci	int ret;
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_WIDTH, r->width);
6538c2ecf20Sopenharmony_ci	if (!ret)
6548c2ecf20Sopenharmony_ci		ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_HEIGHT, r->height);
6558c2ecf20Sopenharmony_ci	if (!ret)
6568c2ecf20Sopenharmony_ci		ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_XOFFS, r->left);
6578c2ecf20Sopenharmony_ci	if (!ret)
6588c2ecf20Sopenharmony_ci		ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_YOFFS, r->top);
6598c2ecf20Sopenharmony_ci	if (!ret)
6608c2ecf20Sopenharmony_ci		ret = s5k6aa_write(c, REG_G_INPUTS_CHANGE_REQ, 1);
6618c2ecf20Sopenharmony_ci	if (!ret)
6628c2ecf20Sopenharmony_ci		s5k6aa->apply_crop = 0;
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	return ret;
6658c2ecf20Sopenharmony_ci}
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci/**
6688c2ecf20Sopenharmony_ci * s5k6aa_configure_video_bus - configure the video output interface
6698c2ecf20Sopenharmony_ci * @s5k6aa: pointer to &struct s5k6aa describing the device
6708c2ecf20Sopenharmony_ci * @bus_type: video bus type: parallel or MIPI-CSI
6718c2ecf20Sopenharmony_ci * @nlanes: number of MIPI lanes to be used (MIPI-CSI only)
6728c2ecf20Sopenharmony_ci *
6738c2ecf20Sopenharmony_ci * Note: Only parallel bus operation has been tested.
6748c2ecf20Sopenharmony_ci */
6758c2ecf20Sopenharmony_cistatic int s5k6aa_configure_video_bus(struct s5k6aa *s5k6aa,
6768c2ecf20Sopenharmony_ci				      enum v4l2_mbus_type bus_type, int nlanes)
6778c2ecf20Sopenharmony_ci{
6788c2ecf20Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
6798c2ecf20Sopenharmony_ci	u16 cfg = 0;
6808c2ecf20Sopenharmony_ci	int ret;
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	/*
6838c2ecf20Sopenharmony_ci	 * TODO: The sensor is supposed to support BT.601 and BT.656
6848c2ecf20Sopenharmony_ci	 * but there is nothing indicating how to switch between both
6858c2ecf20Sopenharmony_ci	 * in the datasheet. For now default BT.601 interface is assumed.
6868c2ecf20Sopenharmony_ci	 */
6878c2ecf20Sopenharmony_ci	if (bus_type == V4L2_MBUS_CSI2_DPHY)
6888c2ecf20Sopenharmony_ci		cfg = nlanes;
6898c2ecf20Sopenharmony_ci	else if (bus_type != V4L2_MBUS_PARALLEL)
6908c2ecf20Sopenharmony_ci		return -EINVAL;
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	ret = s5k6aa_write(client, REG_OIF_EN_MIPI_LANES, cfg);
6938c2ecf20Sopenharmony_ci	if (ret)
6948c2ecf20Sopenharmony_ci		return ret;
6958c2ecf20Sopenharmony_ci	return s5k6aa_write(client, REG_OIF_CFG_CHG, 1);
6968c2ecf20Sopenharmony_ci}
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci/* This function should be called when switching to new user configuration set*/
6998c2ecf20Sopenharmony_cistatic int s5k6aa_new_config_sync(struct i2c_client *client, int timeout,
7008c2ecf20Sopenharmony_ci				  int cid)
7018c2ecf20Sopenharmony_ci{
7028c2ecf20Sopenharmony_ci	unsigned long end = jiffies + msecs_to_jiffies(timeout);
7038c2ecf20Sopenharmony_ci	u16 reg = 1;
7048c2ecf20Sopenharmony_ci	int ret;
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	ret = s5k6aa_write(client, REG_G_ACTIVE_PREV_CFG, cid);
7078c2ecf20Sopenharmony_ci	if (!ret)
7088c2ecf20Sopenharmony_ci		ret = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1);
7098c2ecf20Sopenharmony_ci	if (!ret)
7108c2ecf20Sopenharmony_ci		ret = s5k6aa_write(client, REG_G_NEW_CFG_SYNC, 1);
7118c2ecf20Sopenharmony_ci	if (timeout == 0)
7128c2ecf20Sopenharmony_ci		return ret;
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	while (ret >= 0 && time_is_after_jiffies(end)) {
7158c2ecf20Sopenharmony_ci		ret = s5k6aa_read(client, REG_G_NEW_CFG_SYNC, &reg);
7168c2ecf20Sopenharmony_ci		if (!reg)
7178c2ecf20Sopenharmony_ci			return 0;
7188c2ecf20Sopenharmony_ci		usleep_range(1000, 5000);
7198c2ecf20Sopenharmony_ci	}
7208c2ecf20Sopenharmony_ci	return ret ? ret : -ETIMEDOUT;
7218c2ecf20Sopenharmony_ci}
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci/**
7248c2ecf20Sopenharmony_ci * s5k6aa_set_prev_config - write user preview register set
7258c2ecf20Sopenharmony_ci * @s5k6aa: pointer to &struct s5k6aa describing the device
7268c2ecf20Sopenharmony_ci * @preset: s5kaa preset to be applied
7278c2ecf20Sopenharmony_ci *
7288c2ecf20Sopenharmony_ci * Configure output resolution and color format, pixel clock
7298c2ecf20Sopenharmony_ci * frequency range, device frame rate type and frame period range.
7308c2ecf20Sopenharmony_ci */
7318c2ecf20Sopenharmony_cistatic int s5k6aa_set_prev_config(struct s5k6aa *s5k6aa,
7328c2ecf20Sopenharmony_ci				  struct s5k6aa_preset *preset)
7338c2ecf20Sopenharmony_ci{
7348c2ecf20Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
7358c2ecf20Sopenharmony_ci	int idx = preset->index;
7368c2ecf20Sopenharmony_ci	u16 frame_rate_q;
7378c2ecf20Sopenharmony_ci	int ret;
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci	if (s5k6aa->fiv->reg_fr_time >= S5K6AA_MAX_HIGHRES_FR_TIME)
7408c2ecf20Sopenharmony_ci		frame_rate_q = FR_RATE_Q_BEST_FRRATE;
7418c2ecf20Sopenharmony_ci	else
7428c2ecf20Sopenharmony_ci		frame_rate_q = FR_RATE_Q_BEST_QUALITY;
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	ret = s5k6aa_set_output_framefmt(s5k6aa, preset);
7458c2ecf20Sopenharmony_ci	if (!ret)
7468c2ecf20Sopenharmony_ci		ret = s5k6aa_write(client, REG_P_MAX_OUT_RATE(idx),
7478c2ecf20Sopenharmony_ci				   s5k6aa->pclk_fmax);
7488c2ecf20Sopenharmony_ci	if (!ret)
7498c2ecf20Sopenharmony_ci		ret = s5k6aa_write(client, REG_P_MIN_OUT_RATE(idx),
7508c2ecf20Sopenharmony_ci				   s5k6aa->pclk_fmin);
7518c2ecf20Sopenharmony_ci	if (!ret)
7528c2ecf20Sopenharmony_ci		ret = s5k6aa_write(client, REG_P_CLK_INDEX(idx),
7538c2ecf20Sopenharmony_ci				   preset->clk_id);
7548c2ecf20Sopenharmony_ci	if (!ret)
7558c2ecf20Sopenharmony_ci		ret = s5k6aa_write(client, REG_P_FR_RATE_TYPE(idx),
7568c2ecf20Sopenharmony_ci				   FR_RATE_DYNAMIC);
7578c2ecf20Sopenharmony_ci	if (!ret)
7588c2ecf20Sopenharmony_ci		ret = s5k6aa_write(client, REG_P_FR_RATE_Q_TYPE(idx),
7598c2ecf20Sopenharmony_ci				   frame_rate_q);
7608c2ecf20Sopenharmony_ci	if (!ret)
7618c2ecf20Sopenharmony_ci		ret = s5k6aa_write(client, REG_P_MAX_FR_TIME(idx),
7628c2ecf20Sopenharmony_ci				   s5k6aa->fiv->reg_fr_time + 33);
7638c2ecf20Sopenharmony_ci	if (!ret)
7648c2ecf20Sopenharmony_ci		ret = s5k6aa_write(client, REG_P_MIN_FR_TIME(idx),
7658c2ecf20Sopenharmony_ci				   s5k6aa->fiv->reg_fr_time - 33);
7668c2ecf20Sopenharmony_ci	if (!ret)
7678c2ecf20Sopenharmony_ci		ret = s5k6aa_new_config_sync(client, 250, idx);
7688c2ecf20Sopenharmony_ci	if (!ret)
7698c2ecf20Sopenharmony_ci		ret = s5k6aa_preview_config_status(client);
7708c2ecf20Sopenharmony_ci	if (!ret)
7718c2ecf20Sopenharmony_ci		s5k6aa->apply_cfg = 0;
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	v4l2_dbg(1, debug, client, "Frame interval: %d +/- 3.3ms. (%d)\n",
7748c2ecf20Sopenharmony_ci		 s5k6aa->fiv->reg_fr_time, ret);
7758c2ecf20Sopenharmony_ci	return ret;
7768c2ecf20Sopenharmony_ci}
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci/**
7798c2ecf20Sopenharmony_ci * s5k6aa_initialize_isp - basic ISP MCU initialization
7808c2ecf20Sopenharmony_ci * @sd: pointer to V4L2 sub-device descriptor
7818c2ecf20Sopenharmony_ci *
7828c2ecf20Sopenharmony_ci * Configure AHB addresses for registers read/write; configure PLLs for
7838c2ecf20Sopenharmony_ci * required output pixel clock. The ISP power supply needs to be already
7848c2ecf20Sopenharmony_ci * enabled, with an optional H/W reset.
7858c2ecf20Sopenharmony_ci * Locking: called with s5k6aa.lock mutex held.
7868c2ecf20Sopenharmony_ci */
7878c2ecf20Sopenharmony_cistatic int s5k6aa_initialize_isp(struct v4l2_subdev *sd)
7888c2ecf20Sopenharmony_ci{
7898c2ecf20Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(sd);
7908c2ecf20Sopenharmony_ci	struct s5k6aa *s5k6aa = to_s5k6aa(sd);
7918c2ecf20Sopenharmony_ci	int ret;
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci	s5k6aa->apply_crop = 1;
7948c2ecf20Sopenharmony_ci	s5k6aa->apply_cfg = 1;
7958c2ecf20Sopenharmony_ci	msleep(100);
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	ret = s5k6aa_set_ahb_address(client);
7988c2ecf20Sopenharmony_ci	if (ret)
7998c2ecf20Sopenharmony_ci		return ret;
8008c2ecf20Sopenharmony_ci	ret = s5k6aa_configure_video_bus(s5k6aa, s5k6aa->bus_type,
8018c2ecf20Sopenharmony_ci					 s5k6aa->mipi_lanes);
8028c2ecf20Sopenharmony_ci	if (ret)
8038c2ecf20Sopenharmony_ci		return ret;
8048c2ecf20Sopenharmony_ci	ret = s5k6aa_write_array(sd, s5k6aa_analog_config);
8058c2ecf20Sopenharmony_ci	if (ret)
8068c2ecf20Sopenharmony_ci		return ret;
8078c2ecf20Sopenharmony_ci	msleep(20);
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	return s5k6aa_configure_pixel_clocks(s5k6aa);
8108c2ecf20Sopenharmony_ci}
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_cistatic int s5k6aa_gpio_set_value(struct s5k6aa *priv, int id, u32 val)
8138c2ecf20Sopenharmony_ci{
8148c2ecf20Sopenharmony_ci	if (!gpio_is_valid(priv->gpio[id].gpio))
8158c2ecf20Sopenharmony_ci		return 0;
8168c2ecf20Sopenharmony_ci	gpio_set_value(priv->gpio[id].gpio, !!val);
8178c2ecf20Sopenharmony_ci	return 1;
8188c2ecf20Sopenharmony_ci}
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_cistatic int s5k6aa_gpio_assert(struct s5k6aa *priv, int id)
8218c2ecf20Sopenharmony_ci{
8228c2ecf20Sopenharmony_ci	return s5k6aa_gpio_set_value(priv, id, priv->gpio[id].level);
8238c2ecf20Sopenharmony_ci}
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_cistatic int s5k6aa_gpio_deassert(struct s5k6aa *priv, int id)
8268c2ecf20Sopenharmony_ci{
8278c2ecf20Sopenharmony_ci	return s5k6aa_gpio_set_value(priv, id, !priv->gpio[id].level);
8288c2ecf20Sopenharmony_ci}
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_cistatic int __s5k6aa_power_on(struct s5k6aa *s5k6aa)
8318c2ecf20Sopenharmony_ci{
8328c2ecf20Sopenharmony_ci	int ret;
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci	ret = regulator_bulk_enable(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies);
8358c2ecf20Sopenharmony_ci	if (ret)
8368c2ecf20Sopenharmony_ci		return ret;
8378c2ecf20Sopenharmony_ci	if (s5k6aa_gpio_deassert(s5k6aa, STBY))
8388c2ecf20Sopenharmony_ci		usleep_range(150, 200);
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	if (s5k6aa->s_power)
8418c2ecf20Sopenharmony_ci		ret = s5k6aa->s_power(1);
8428c2ecf20Sopenharmony_ci	usleep_range(4000, 5000);
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	if (s5k6aa_gpio_deassert(s5k6aa, RSET))
8458c2ecf20Sopenharmony_ci		msleep(20);
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	return ret;
8488c2ecf20Sopenharmony_ci}
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_cistatic int __s5k6aa_power_off(struct s5k6aa *s5k6aa)
8518c2ecf20Sopenharmony_ci{
8528c2ecf20Sopenharmony_ci	int ret;
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	if (s5k6aa_gpio_assert(s5k6aa, RSET))
8558c2ecf20Sopenharmony_ci		usleep_range(100, 150);
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci	if (s5k6aa->s_power) {
8588c2ecf20Sopenharmony_ci		ret = s5k6aa->s_power(0);
8598c2ecf20Sopenharmony_ci		if (ret)
8608c2ecf20Sopenharmony_ci			return ret;
8618c2ecf20Sopenharmony_ci	}
8628c2ecf20Sopenharmony_ci	if (s5k6aa_gpio_assert(s5k6aa, STBY))
8638c2ecf20Sopenharmony_ci		usleep_range(50, 100);
8648c2ecf20Sopenharmony_ci	s5k6aa->streaming = 0;
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	return regulator_bulk_disable(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies);
8678c2ecf20Sopenharmony_ci}
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci/*
8708c2ecf20Sopenharmony_ci * V4L2 subdev core and video operations
8718c2ecf20Sopenharmony_ci */
8728c2ecf20Sopenharmony_cistatic int s5k6aa_set_power(struct v4l2_subdev *sd, int on)
8738c2ecf20Sopenharmony_ci{
8748c2ecf20Sopenharmony_ci	struct s5k6aa *s5k6aa = to_s5k6aa(sd);
8758c2ecf20Sopenharmony_ci	int ret = 0;
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	mutex_lock(&s5k6aa->lock);
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci	if (s5k6aa->power == !on) {
8808c2ecf20Sopenharmony_ci		if (on) {
8818c2ecf20Sopenharmony_ci			ret = __s5k6aa_power_on(s5k6aa);
8828c2ecf20Sopenharmony_ci			if (!ret)
8838c2ecf20Sopenharmony_ci				ret = s5k6aa_initialize_isp(sd);
8848c2ecf20Sopenharmony_ci		} else {
8858c2ecf20Sopenharmony_ci			ret = __s5k6aa_power_off(s5k6aa);
8868c2ecf20Sopenharmony_ci		}
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci		if (!ret)
8898c2ecf20Sopenharmony_ci			s5k6aa->power += on ? 1 : -1;
8908c2ecf20Sopenharmony_ci	}
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	mutex_unlock(&s5k6aa->lock);
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci	if (!on || ret || s5k6aa->power != 1)
8958c2ecf20Sopenharmony_ci		return ret;
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci	return v4l2_ctrl_handler_setup(sd->ctrl_handler);
8988c2ecf20Sopenharmony_ci}
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_cistatic int __s5k6aa_stream(struct s5k6aa *s5k6aa, int enable)
9018c2ecf20Sopenharmony_ci{
9028c2ecf20Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
9038c2ecf20Sopenharmony_ci	int ret = 0;
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	ret = s5k6aa_write(client, REG_G_ENABLE_PREV, enable);
9068c2ecf20Sopenharmony_ci	if (!ret)
9078c2ecf20Sopenharmony_ci		ret = s5k6aa_write(client, REG_G_ENABLE_PREV_CHG, 1);
9088c2ecf20Sopenharmony_ci	if (!ret)
9098c2ecf20Sopenharmony_ci		s5k6aa->streaming = enable;
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	return ret;
9128c2ecf20Sopenharmony_ci}
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_cistatic int s5k6aa_s_stream(struct v4l2_subdev *sd, int on)
9158c2ecf20Sopenharmony_ci{
9168c2ecf20Sopenharmony_ci	struct s5k6aa *s5k6aa = to_s5k6aa(sd);
9178c2ecf20Sopenharmony_ci	int ret = 0;
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	mutex_lock(&s5k6aa->lock);
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci	if (s5k6aa->streaming == !on) {
9228c2ecf20Sopenharmony_ci		if (!ret && s5k6aa->apply_cfg)
9238c2ecf20Sopenharmony_ci			ret = s5k6aa_set_prev_config(s5k6aa, s5k6aa->preset);
9248c2ecf20Sopenharmony_ci		if (s5k6aa->apply_crop)
9258c2ecf20Sopenharmony_ci			ret = s5k6aa_set_input_params(s5k6aa);
9268c2ecf20Sopenharmony_ci		if (!ret)
9278c2ecf20Sopenharmony_ci			ret = __s5k6aa_stream(s5k6aa, !!on);
9288c2ecf20Sopenharmony_ci	}
9298c2ecf20Sopenharmony_ci	mutex_unlock(&s5k6aa->lock);
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci	return ret;
9328c2ecf20Sopenharmony_ci}
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_cistatic int s5k6aa_g_frame_interval(struct v4l2_subdev *sd,
9358c2ecf20Sopenharmony_ci				   struct v4l2_subdev_frame_interval *fi)
9368c2ecf20Sopenharmony_ci{
9378c2ecf20Sopenharmony_ci	struct s5k6aa *s5k6aa = to_s5k6aa(sd);
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci	mutex_lock(&s5k6aa->lock);
9408c2ecf20Sopenharmony_ci	fi->interval = s5k6aa->fiv->interval;
9418c2ecf20Sopenharmony_ci	mutex_unlock(&s5k6aa->lock);
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci	return 0;
9448c2ecf20Sopenharmony_ci}
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_cistatic int __s5k6aa_set_frame_interval(struct s5k6aa *s5k6aa,
9478c2ecf20Sopenharmony_ci				       struct v4l2_subdev_frame_interval *fi)
9488c2ecf20Sopenharmony_ci{
9498c2ecf20Sopenharmony_ci	struct v4l2_mbus_framefmt *mbus_fmt = &s5k6aa->preset->mbus_fmt;
9508c2ecf20Sopenharmony_ci	const struct s5k6aa_interval *fiv = &s5k6aa_intervals[0];
9518c2ecf20Sopenharmony_ci	unsigned int err, min_err = UINT_MAX;
9528c2ecf20Sopenharmony_ci	unsigned int i, fr_time;
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci	if (fi->interval.denominator == 0)
9558c2ecf20Sopenharmony_ci		return -EINVAL;
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci	fr_time = fi->interval.numerator * 10000 / fi->interval.denominator;
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(s5k6aa_intervals); i++) {
9608c2ecf20Sopenharmony_ci		const struct s5k6aa_interval *iv = &s5k6aa_intervals[i];
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci		if (mbus_fmt->width > iv->size.width ||
9638c2ecf20Sopenharmony_ci		    mbus_fmt->height > iv->size.height)
9648c2ecf20Sopenharmony_ci			continue;
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_ci		err = abs(iv->reg_fr_time - fr_time);
9678c2ecf20Sopenharmony_ci		if (err < min_err) {
9688c2ecf20Sopenharmony_ci			fiv = iv;
9698c2ecf20Sopenharmony_ci			min_err = err;
9708c2ecf20Sopenharmony_ci		}
9718c2ecf20Sopenharmony_ci	}
9728c2ecf20Sopenharmony_ci	s5k6aa->fiv = fiv;
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci	v4l2_dbg(1, debug, &s5k6aa->sd, "Changed frame interval to %d us\n",
9758c2ecf20Sopenharmony_ci		 fiv->reg_fr_time * 100);
9768c2ecf20Sopenharmony_ci	return 0;
9778c2ecf20Sopenharmony_ci}
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_cistatic int s5k6aa_s_frame_interval(struct v4l2_subdev *sd,
9808c2ecf20Sopenharmony_ci				   struct v4l2_subdev_frame_interval *fi)
9818c2ecf20Sopenharmony_ci{
9828c2ecf20Sopenharmony_ci	struct s5k6aa *s5k6aa = to_s5k6aa(sd);
9838c2ecf20Sopenharmony_ci	int ret;
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci	v4l2_dbg(1, debug, sd, "Setting %d/%d frame interval\n",
9868c2ecf20Sopenharmony_ci		 fi->interval.numerator, fi->interval.denominator);
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci	mutex_lock(&s5k6aa->lock);
9898c2ecf20Sopenharmony_ci	ret = __s5k6aa_set_frame_interval(s5k6aa, fi);
9908c2ecf20Sopenharmony_ci	s5k6aa->apply_cfg = 1;
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_ci	mutex_unlock(&s5k6aa->lock);
9938c2ecf20Sopenharmony_ci	return ret;
9948c2ecf20Sopenharmony_ci}
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci/*
9978c2ecf20Sopenharmony_ci * V4L2 subdev pad level and video operations
9988c2ecf20Sopenharmony_ci */
9998c2ecf20Sopenharmony_cistatic int s5k6aa_enum_frame_interval(struct v4l2_subdev *sd,
10008c2ecf20Sopenharmony_ci			      struct v4l2_subdev_pad_config *cfg,
10018c2ecf20Sopenharmony_ci			      struct v4l2_subdev_frame_interval_enum *fie)
10028c2ecf20Sopenharmony_ci{
10038c2ecf20Sopenharmony_ci	struct s5k6aa *s5k6aa = to_s5k6aa(sd);
10048c2ecf20Sopenharmony_ci	const struct s5k6aa_interval *fi;
10058c2ecf20Sopenharmony_ci	int ret = 0;
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci	if (fie->index >= ARRAY_SIZE(s5k6aa_intervals))
10088c2ecf20Sopenharmony_ci		return -EINVAL;
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci	v4l_bound_align_image(&fie->width, S5K6AA_WIN_WIDTH_MIN,
10118c2ecf20Sopenharmony_ci			      S5K6AA_WIN_WIDTH_MAX, 1,
10128c2ecf20Sopenharmony_ci			      &fie->height, S5K6AA_WIN_HEIGHT_MIN,
10138c2ecf20Sopenharmony_ci			      S5K6AA_WIN_HEIGHT_MAX, 1, 0);
10148c2ecf20Sopenharmony_ci
10158c2ecf20Sopenharmony_ci	mutex_lock(&s5k6aa->lock);
10168c2ecf20Sopenharmony_ci	fi = &s5k6aa_intervals[fie->index];
10178c2ecf20Sopenharmony_ci	if (fie->width > fi->size.width || fie->height > fi->size.height)
10188c2ecf20Sopenharmony_ci		ret = -EINVAL;
10198c2ecf20Sopenharmony_ci	else
10208c2ecf20Sopenharmony_ci		fie->interval = fi->interval;
10218c2ecf20Sopenharmony_ci	mutex_unlock(&s5k6aa->lock);
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_ci	return ret;
10248c2ecf20Sopenharmony_ci}
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_cistatic int s5k6aa_enum_mbus_code(struct v4l2_subdev *sd,
10278c2ecf20Sopenharmony_ci				 struct v4l2_subdev_pad_config *cfg,
10288c2ecf20Sopenharmony_ci				 struct v4l2_subdev_mbus_code_enum *code)
10298c2ecf20Sopenharmony_ci{
10308c2ecf20Sopenharmony_ci	if (code->index >= ARRAY_SIZE(s5k6aa_formats))
10318c2ecf20Sopenharmony_ci		return -EINVAL;
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci	code->code = s5k6aa_formats[code->index].code;
10348c2ecf20Sopenharmony_ci	return 0;
10358c2ecf20Sopenharmony_ci}
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_cistatic int s5k6aa_enum_frame_size(struct v4l2_subdev *sd,
10388c2ecf20Sopenharmony_ci				  struct v4l2_subdev_pad_config *cfg,
10398c2ecf20Sopenharmony_ci				  struct v4l2_subdev_frame_size_enum *fse)
10408c2ecf20Sopenharmony_ci{
10418c2ecf20Sopenharmony_ci	int i = ARRAY_SIZE(s5k6aa_formats);
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_ci	if (fse->index > 0)
10448c2ecf20Sopenharmony_ci		return -EINVAL;
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci	while (--i)
10478c2ecf20Sopenharmony_ci		if (fse->code == s5k6aa_formats[i].code)
10488c2ecf20Sopenharmony_ci			break;
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci	fse->code = s5k6aa_formats[i].code;
10518c2ecf20Sopenharmony_ci	fse->min_width  = S5K6AA_WIN_WIDTH_MIN;
10528c2ecf20Sopenharmony_ci	fse->max_width  = S5K6AA_WIN_WIDTH_MAX;
10538c2ecf20Sopenharmony_ci	fse->max_height = S5K6AA_WIN_HEIGHT_MIN;
10548c2ecf20Sopenharmony_ci	fse->min_height = S5K6AA_WIN_HEIGHT_MAX;
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci	return 0;
10578c2ecf20Sopenharmony_ci}
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_cistatic struct v4l2_rect *
10608c2ecf20Sopenharmony_ci__s5k6aa_get_crop_rect(struct s5k6aa *s5k6aa, struct v4l2_subdev_pad_config *cfg,
10618c2ecf20Sopenharmony_ci		       enum v4l2_subdev_format_whence which)
10628c2ecf20Sopenharmony_ci{
10638c2ecf20Sopenharmony_ci	if (which == V4L2_SUBDEV_FORMAT_ACTIVE)
10648c2ecf20Sopenharmony_ci		return &s5k6aa->ccd_rect;
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_ci	WARN_ON(which != V4L2_SUBDEV_FORMAT_TRY);
10678c2ecf20Sopenharmony_ci	return v4l2_subdev_get_try_crop(&s5k6aa->sd, cfg, 0);
10688c2ecf20Sopenharmony_ci}
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_cistatic void s5k6aa_try_format(struct s5k6aa *s5k6aa,
10718c2ecf20Sopenharmony_ci			      struct v4l2_mbus_framefmt *mf)
10728c2ecf20Sopenharmony_ci{
10738c2ecf20Sopenharmony_ci	unsigned int index;
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_ci	v4l_bound_align_image(&mf->width, S5K6AA_WIN_WIDTH_MIN,
10768c2ecf20Sopenharmony_ci			      S5K6AA_WIN_WIDTH_MAX, 1,
10778c2ecf20Sopenharmony_ci			      &mf->height, S5K6AA_WIN_HEIGHT_MIN,
10788c2ecf20Sopenharmony_ci			      S5K6AA_WIN_HEIGHT_MAX, 1, 0);
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_ci	if (mf->colorspace != V4L2_COLORSPACE_JPEG &&
10818c2ecf20Sopenharmony_ci	    mf->colorspace != V4L2_COLORSPACE_REC709)
10828c2ecf20Sopenharmony_ci		mf->colorspace = V4L2_COLORSPACE_JPEG;
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_ci	index = s5k6aa_get_pixfmt_index(s5k6aa, mf);
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_ci	mf->colorspace	= s5k6aa_formats[index].colorspace;
10878c2ecf20Sopenharmony_ci	mf->code	= s5k6aa_formats[index].code;
10888c2ecf20Sopenharmony_ci	mf->field	= V4L2_FIELD_NONE;
10898c2ecf20Sopenharmony_ci}
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_cistatic int s5k6aa_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
10928c2ecf20Sopenharmony_ci			  struct v4l2_subdev_format *fmt)
10938c2ecf20Sopenharmony_ci{
10948c2ecf20Sopenharmony_ci	struct s5k6aa *s5k6aa = to_s5k6aa(sd);
10958c2ecf20Sopenharmony_ci	struct v4l2_mbus_framefmt *mf;
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_ci	memset(fmt->reserved, 0, sizeof(fmt->reserved));
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_ci	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
11008c2ecf20Sopenharmony_ci		mf = v4l2_subdev_get_try_format(sd, cfg, 0);
11018c2ecf20Sopenharmony_ci		fmt->format = *mf;
11028c2ecf20Sopenharmony_ci		return 0;
11038c2ecf20Sopenharmony_ci	}
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci	mutex_lock(&s5k6aa->lock);
11068c2ecf20Sopenharmony_ci	fmt->format = s5k6aa->preset->mbus_fmt;
11078c2ecf20Sopenharmony_ci	mutex_unlock(&s5k6aa->lock);
11088c2ecf20Sopenharmony_ci
11098c2ecf20Sopenharmony_ci	return 0;
11108c2ecf20Sopenharmony_ci}
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_cistatic int s5k6aa_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
11138c2ecf20Sopenharmony_ci			  struct v4l2_subdev_format *fmt)
11148c2ecf20Sopenharmony_ci{
11158c2ecf20Sopenharmony_ci	struct s5k6aa *s5k6aa = to_s5k6aa(sd);
11168c2ecf20Sopenharmony_ci	struct s5k6aa_preset *preset = s5k6aa->preset;
11178c2ecf20Sopenharmony_ci	struct v4l2_mbus_framefmt *mf;
11188c2ecf20Sopenharmony_ci	struct v4l2_rect *crop;
11198c2ecf20Sopenharmony_ci	int ret = 0;
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ci	mutex_lock(&s5k6aa->lock);
11228c2ecf20Sopenharmony_ci	s5k6aa_try_format(s5k6aa, &fmt->format);
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
11258c2ecf20Sopenharmony_ci		mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
11268c2ecf20Sopenharmony_ci		crop = v4l2_subdev_get_try_crop(sd, cfg, 0);
11278c2ecf20Sopenharmony_ci	} else {
11288c2ecf20Sopenharmony_ci		if (s5k6aa->streaming) {
11298c2ecf20Sopenharmony_ci			ret = -EBUSY;
11308c2ecf20Sopenharmony_ci		} else {
11318c2ecf20Sopenharmony_ci			mf = &preset->mbus_fmt;
11328c2ecf20Sopenharmony_ci			crop = &s5k6aa->ccd_rect;
11338c2ecf20Sopenharmony_ci			s5k6aa->apply_cfg = 1;
11348c2ecf20Sopenharmony_ci		}
11358c2ecf20Sopenharmony_ci	}
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci	if (ret == 0) {
11388c2ecf20Sopenharmony_ci		struct v4l2_subdev_frame_interval fiv = {
11398c2ecf20Sopenharmony_ci			.interval = {0, 1}
11408c2ecf20Sopenharmony_ci		};
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci		*mf = fmt->format;
11438c2ecf20Sopenharmony_ci		/*
11448c2ecf20Sopenharmony_ci		 * Make sure the crop window is valid, i.e. its size is
11458c2ecf20Sopenharmony_ci		 * greater than the output window, as the ISP supports
11468c2ecf20Sopenharmony_ci		 * only down-scaling.
11478c2ecf20Sopenharmony_ci		 */
11488c2ecf20Sopenharmony_ci		crop->width = clamp_t(unsigned int, crop->width, mf->width,
11498c2ecf20Sopenharmony_ci				      S5K6AA_WIN_WIDTH_MAX);
11508c2ecf20Sopenharmony_ci		crop->height = clamp_t(unsigned int, crop->height, mf->height,
11518c2ecf20Sopenharmony_ci				       S5K6AA_WIN_HEIGHT_MAX);
11528c2ecf20Sopenharmony_ci		crop->left = clamp_t(unsigned int, crop->left, 0,
11538c2ecf20Sopenharmony_ci				     S5K6AA_WIN_WIDTH_MAX - crop->width);
11548c2ecf20Sopenharmony_ci		crop->top  = clamp_t(unsigned int, crop->top, 0,
11558c2ecf20Sopenharmony_ci				     S5K6AA_WIN_HEIGHT_MAX - crop->height);
11568c2ecf20Sopenharmony_ci
11578c2ecf20Sopenharmony_ci		/* Reset to minimum possible frame interval */
11588c2ecf20Sopenharmony_ci		ret = __s5k6aa_set_frame_interval(s5k6aa, &fiv);
11598c2ecf20Sopenharmony_ci	}
11608c2ecf20Sopenharmony_ci	mutex_unlock(&s5k6aa->lock);
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ci	return ret;
11638c2ecf20Sopenharmony_ci}
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_cistatic int s5k6aa_get_selection(struct v4l2_subdev *sd,
11668c2ecf20Sopenharmony_ci				struct v4l2_subdev_pad_config *cfg,
11678c2ecf20Sopenharmony_ci				struct v4l2_subdev_selection *sel)
11688c2ecf20Sopenharmony_ci{
11698c2ecf20Sopenharmony_ci	struct s5k6aa *s5k6aa = to_s5k6aa(sd);
11708c2ecf20Sopenharmony_ci	struct v4l2_rect *rect;
11718c2ecf20Sopenharmony_ci
11728c2ecf20Sopenharmony_ci	if (sel->target != V4L2_SEL_TGT_CROP)
11738c2ecf20Sopenharmony_ci		return -EINVAL;
11748c2ecf20Sopenharmony_ci
11758c2ecf20Sopenharmony_ci	memset(sel->reserved, 0, sizeof(sel->reserved));
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_ci	mutex_lock(&s5k6aa->lock);
11788c2ecf20Sopenharmony_ci	rect = __s5k6aa_get_crop_rect(s5k6aa, cfg, sel->which);
11798c2ecf20Sopenharmony_ci	sel->r = *rect;
11808c2ecf20Sopenharmony_ci	mutex_unlock(&s5k6aa->lock);
11818c2ecf20Sopenharmony_ci
11828c2ecf20Sopenharmony_ci	v4l2_dbg(1, debug, sd, "Current crop rectangle: (%d,%d)/%dx%d\n",
11838c2ecf20Sopenharmony_ci		 rect->left, rect->top, rect->width, rect->height);
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ci	return 0;
11868c2ecf20Sopenharmony_ci}
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_cistatic int s5k6aa_set_selection(struct v4l2_subdev *sd,
11898c2ecf20Sopenharmony_ci				struct v4l2_subdev_pad_config *cfg,
11908c2ecf20Sopenharmony_ci				struct v4l2_subdev_selection *sel)
11918c2ecf20Sopenharmony_ci{
11928c2ecf20Sopenharmony_ci	struct s5k6aa *s5k6aa = to_s5k6aa(sd);
11938c2ecf20Sopenharmony_ci	struct v4l2_mbus_framefmt *mf;
11948c2ecf20Sopenharmony_ci	unsigned int max_x, max_y;
11958c2ecf20Sopenharmony_ci	struct v4l2_rect *crop_r;
11968c2ecf20Sopenharmony_ci
11978c2ecf20Sopenharmony_ci	if (sel->target != V4L2_SEL_TGT_CROP)
11988c2ecf20Sopenharmony_ci		return -EINVAL;
11998c2ecf20Sopenharmony_ci
12008c2ecf20Sopenharmony_ci	mutex_lock(&s5k6aa->lock);
12018c2ecf20Sopenharmony_ci	crop_r = __s5k6aa_get_crop_rect(s5k6aa, cfg, sel->which);
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci	if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
12048c2ecf20Sopenharmony_ci		mf = &s5k6aa->preset->mbus_fmt;
12058c2ecf20Sopenharmony_ci		s5k6aa->apply_crop = 1;
12068c2ecf20Sopenharmony_ci	} else {
12078c2ecf20Sopenharmony_ci		mf = v4l2_subdev_get_try_format(sd, cfg, 0);
12088c2ecf20Sopenharmony_ci	}
12098c2ecf20Sopenharmony_ci	v4l_bound_align_image(&sel->r.width, mf->width,
12108c2ecf20Sopenharmony_ci			      S5K6AA_WIN_WIDTH_MAX, 1,
12118c2ecf20Sopenharmony_ci			      &sel->r.height, mf->height,
12128c2ecf20Sopenharmony_ci			      S5K6AA_WIN_HEIGHT_MAX, 1, 0);
12138c2ecf20Sopenharmony_ci
12148c2ecf20Sopenharmony_ci	max_x = (S5K6AA_WIN_WIDTH_MAX - sel->r.width) & ~1;
12158c2ecf20Sopenharmony_ci	max_y = (S5K6AA_WIN_HEIGHT_MAX - sel->r.height) & ~1;
12168c2ecf20Sopenharmony_ci
12178c2ecf20Sopenharmony_ci	sel->r.left = clamp_t(unsigned int, sel->r.left, 0, max_x);
12188c2ecf20Sopenharmony_ci	sel->r.top  = clamp_t(unsigned int, sel->r.top, 0, max_y);
12198c2ecf20Sopenharmony_ci
12208c2ecf20Sopenharmony_ci	*crop_r = sel->r;
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_ci	mutex_unlock(&s5k6aa->lock);
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci	v4l2_dbg(1, debug, sd, "Set crop rectangle: (%d,%d)/%dx%d\n",
12258c2ecf20Sopenharmony_ci		 crop_r->left, crop_r->top, crop_r->width, crop_r->height);
12268c2ecf20Sopenharmony_ci
12278c2ecf20Sopenharmony_ci	return 0;
12288c2ecf20Sopenharmony_ci}
12298c2ecf20Sopenharmony_ci
12308c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_pad_ops s5k6aa_pad_ops = {
12318c2ecf20Sopenharmony_ci	.enum_mbus_code		= s5k6aa_enum_mbus_code,
12328c2ecf20Sopenharmony_ci	.enum_frame_size	= s5k6aa_enum_frame_size,
12338c2ecf20Sopenharmony_ci	.enum_frame_interval	= s5k6aa_enum_frame_interval,
12348c2ecf20Sopenharmony_ci	.get_fmt		= s5k6aa_get_fmt,
12358c2ecf20Sopenharmony_ci	.set_fmt		= s5k6aa_set_fmt,
12368c2ecf20Sopenharmony_ci	.get_selection		= s5k6aa_get_selection,
12378c2ecf20Sopenharmony_ci	.set_selection		= s5k6aa_set_selection,
12388c2ecf20Sopenharmony_ci};
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_video_ops s5k6aa_video_ops = {
12418c2ecf20Sopenharmony_ci	.g_frame_interval	= s5k6aa_g_frame_interval,
12428c2ecf20Sopenharmony_ci	.s_frame_interval	= s5k6aa_s_frame_interval,
12438c2ecf20Sopenharmony_ci	.s_stream		= s5k6aa_s_stream,
12448c2ecf20Sopenharmony_ci};
12458c2ecf20Sopenharmony_ci
12468c2ecf20Sopenharmony_ci/*
12478c2ecf20Sopenharmony_ci * V4L2 subdev controls
12488c2ecf20Sopenharmony_ci */
12498c2ecf20Sopenharmony_ci
12508c2ecf20Sopenharmony_cistatic int s5k6aa_s_ctrl(struct v4l2_ctrl *ctrl)
12518c2ecf20Sopenharmony_ci{
12528c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
12538c2ecf20Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(sd);
12548c2ecf20Sopenharmony_ci	struct s5k6aa *s5k6aa = to_s5k6aa(sd);
12558c2ecf20Sopenharmony_ci	int idx, err = 0;
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_ci	v4l2_dbg(1, debug, sd, "ctrl: 0x%x, value: %d\n", ctrl->id, ctrl->val);
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_ci	mutex_lock(&s5k6aa->lock);
12608c2ecf20Sopenharmony_ci	/*
12618c2ecf20Sopenharmony_ci	 * If the device is not powered up by the host driver do
12628c2ecf20Sopenharmony_ci	 * not apply any controls to H/W at this time. Instead
12638c2ecf20Sopenharmony_ci	 * the controls will be restored right after power-up.
12648c2ecf20Sopenharmony_ci	 */
12658c2ecf20Sopenharmony_ci	if (s5k6aa->power == 0)
12668c2ecf20Sopenharmony_ci		goto unlock;
12678c2ecf20Sopenharmony_ci	idx = s5k6aa->preset->index;
12688c2ecf20Sopenharmony_ci
12698c2ecf20Sopenharmony_ci	switch (ctrl->id) {
12708c2ecf20Sopenharmony_ci	case V4L2_CID_AUTO_WHITE_BALANCE:
12718c2ecf20Sopenharmony_ci		err = s5k6aa_set_awb(s5k6aa, ctrl->val);
12728c2ecf20Sopenharmony_ci		break;
12738c2ecf20Sopenharmony_ci
12748c2ecf20Sopenharmony_ci	case V4L2_CID_BRIGHTNESS:
12758c2ecf20Sopenharmony_ci		err = s5k6aa_write(client, REG_USER_BRIGHTNESS, ctrl->val);
12768c2ecf20Sopenharmony_ci		break;
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_ci	case V4L2_CID_COLORFX:
12798c2ecf20Sopenharmony_ci		err = s5k6aa_set_colorfx(s5k6aa, ctrl->val);
12808c2ecf20Sopenharmony_ci		break;
12818c2ecf20Sopenharmony_ci
12828c2ecf20Sopenharmony_ci	case V4L2_CID_CONTRAST:
12838c2ecf20Sopenharmony_ci		err = s5k6aa_write(client, REG_USER_CONTRAST, ctrl->val);
12848c2ecf20Sopenharmony_ci		break;
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_ci	case V4L2_CID_EXPOSURE_AUTO:
12878c2ecf20Sopenharmony_ci		err = s5k6aa_set_auto_exposure(s5k6aa, ctrl->val);
12888c2ecf20Sopenharmony_ci		break;
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_ci	case V4L2_CID_HFLIP:
12918c2ecf20Sopenharmony_ci		err = s5k6aa_set_mirror(s5k6aa, ctrl->val);
12928c2ecf20Sopenharmony_ci		if (err)
12938c2ecf20Sopenharmony_ci			break;
12948c2ecf20Sopenharmony_ci		err = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1);
12958c2ecf20Sopenharmony_ci		break;
12968c2ecf20Sopenharmony_ci
12978c2ecf20Sopenharmony_ci	case V4L2_CID_POWER_LINE_FREQUENCY:
12988c2ecf20Sopenharmony_ci		err = s5k6aa_set_anti_flicker(s5k6aa, ctrl->val);
12998c2ecf20Sopenharmony_ci		break;
13008c2ecf20Sopenharmony_ci
13018c2ecf20Sopenharmony_ci	case V4L2_CID_SATURATION:
13028c2ecf20Sopenharmony_ci		err = s5k6aa_write(client, REG_USER_SATURATION, ctrl->val);
13038c2ecf20Sopenharmony_ci		break;
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_ci	case V4L2_CID_SHARPNESS:
13068c2ecf20Sopenharmony_ci		err = s5k6aa_write(client, REG_USER_SHARPBLUR, ctrl->val);
13078c2ecf20Sopenharmony_ci		break;
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci	case V4L2_CID_WHITE_BALANCE_TEMPERATURE:
13108c2ecf20Sopenharmony_ci		err = s5k6aa_write(client, REG_P_COLORTEMP(idx), ctrl->val);
13118c2ecf20Sopenharmony_ci		if (err)
13128c2ecf20Sopenharmony_ci			break;
13138c2ecf20Sopenharmony_ci		err = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1);
13148c2ecf20Sopenharmony_ci		break;
13158c2ecf20Sopenharmony_ci	}
13168c2ecf20Sopenharmony_ciunlock:
13178c2ecf20Sopenharmony_ci	mutex_unlock(&s5k6aa->lock);
13188c2ecf20Sopenharmony_ci	return err;
13198c2ecf20Sopenharmony_ci}
13208c2ecf20Sopenharmony_ci
13218c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops s5k6aa_ctrl_ops = {
13228c2ecf20Sopenharmony_ci	.s_ctrl	= s5k6aa_s_ctrl,
13238c2ecf20Sopenharmony_ci};
13248c2ecf20Sopenharmony_ci
13258c2ecf20Sopenharmony_cistatic int s5k6aa_log_status(struct v4l2_subdev *sd)
13268c2ecf20Sopenharmony_ci{
13278c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_log_status(sd->ctrl_handler, sd->name);
13288c2ecf20Sopenharmony_ci	return 0;
13298c2ecf20Sopenharmony_ci}
13308c2ecf20Sopenharmony_ci
13318c2ecf20Sopenharmony_ci#define V4L2_CID_RED_GAIN	(V4L2_CTRL_CLASS_CAMERA | 0x1001)
13328c2ecf20Sopenharmony_ci#define V4L2_CID_GREEN_GAIN	(V4L2_CTRL_CLASS_CAMERA | 0x1002)
13338c2ecf20Sopenharmony_ci#define V4L2_CID_BLUE_GAIN	(V4L2_CTRL_CLASS_CAMERA | 0x1003)
13348c2ecf20Sopenharmony_ci
13358c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_config s5k6aa_ctrls[] = {
13368c2ecf20Sopenharmony_ci	{
13378c2ecf20Sopenharmony_ci		.ops	= &s5k6aa_ctrl_ops,
13388c2ecf20Sopenharmony_ci		.id	= V4L2_CID_RED_GAIN,
13398c2ecf20Sopenharmony_ci		.type	= V4L2_CTRL_TYPE_INTEGER,
13408c2ecf20Sopenharmony_ci		.name	= "Gain, Red",
13418c2ecf20Sopenharmony_ci		.min	= 0,
13428c2ecf20Sopenharmony_ci		.max	= 256,
13438c2ecf20Sopenharmony_ci		.def	= 127,
13448c2ecf20Sopenharmony_ci		.step	= 1,
13458c2ecf20Sopenharmony_ci	}, {
13468c2ecf20Sopenharmony_ci		.ops	= &s5k6aa_ctrl_ops,
13478c2ecf20Sopenharmony_ci		.id	= V4L2_CID_GREEN_GAIN,
13488c2ecf20Sopenharmony_ci		.type	= V4L2_CTRL_TYPE_INTEGER,
13498c2ecf20Sopenharmony_ci		.name	= "Gain, Green",
13508c2ecf20Sopenharmony_ci		.min	= 0,
13518c2ecf20Sopenharmony_ci		.max	= 256,
13528c2ecf20Sopenharmony_ci		.def	= 127,
13538c2ecf20Sopenharmony_ci		.step	= 1,
13548c2ecf20Sopenharmony_ci	}, {
13558c2ecf20Sopenharmony_ci		.ops	= &s5k6aa_ctrl_ops,
13568c2ecf20Sopenharmony_ci		.id	= V4L2_CID_BLUE_GAIN,
13578c2ecf20Sopenharmony_ci		.type	= V4L2_CTRL_TYPE_INTEGER,
13588c2ecf20Sopenharmony_ci		.name	= "Gain, Blue",
13598c2ecf20Sopenharmony_ci		.min	= 0,
13608c2ecf20Sopenharmony_ci		.max	= 256,
13618c2ecf20Sopenharmony_ci		.def	= 127,
13628c2ecf20Sopenharmony_ci		.step	= 1,
13638c2ecf20Sopenharmony_ci	},
13648c2ecf20Sopenharmony_ci};
13658c2ecf20Sopenharmony_ci
13668c2ecf20Sopenharmony_cistatic int s5k6aa_initialize_ctrls(struct s5k6aa *s5k6aa)
13678c2ecf20Sopenharmony_ci{
13688c2ecf20Sopenharmony_ci	const struct v4l2_ctrl_ops *ops = &s5k6aa_ctrl_ops;
13698c2ecf20Sopenharmony_ci	struct s5k6aa_ctrls *ctrls = &s5k6aa->ctrls;
13708c2ecf20Sopenharmony_ci	struct v4l2_ctrl_handler *hdl = &ctrls->handler;
13718c2ecf20Sopenharmony_ci
13728c2ecf20Sopenharmony_ci	int ret = v4l2_ctrl_handler_init(hdl, 16);
13738c2ecf20Sopenharmony_ci	if (ret)
13748c2ecf20Sopenharmony_ci		return ret;
13758c2ecf20Sopenharmony_ci	/* Auto white balance cluster */
13768c2ecf20Sopenharmony_ci	ctrls->awb = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE,
13778c2ecf20Sopenharmony_ci				       0, 1, 1, 1);
13788c2ecf20Sopenharmony_ci	ctrls->gain_red = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[0], NULL);
13798c2ecf20Sopenharmony_ci	ctrls->gain_green = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[1], NULL);
13808c2ecf20Sopenharmony_ci	ctrls->gain_blue = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[2], NULL);
13818c2ecf20Sopenharmony_ci	v4l2_ctrl_auto_cluster(4, &ctrls->awb, 0, false);
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_ci	ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
13848c2ecf20Sopenharmony_ci	ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
13858c2ecf20Sopenharmony_ci	v4l2_ctrl_cluster(2, &ctrls->hflip);
13868c2ecf20Sopenharmony_ci
13878c2ecf20Sopenharmony_ci	ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops,
13888c2ecf20Sopenharmony_ci				V4L2_CID_EXPOSURE_AUTO,
13898c2ecf20Sopenharmony_ci				V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO);
13908c2ecf20Sopenharmony_ci	/* Exposure time: x 1 us */
13918c2ecf20Sopenharmony_ci	ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE,
13928c2ecf20Sopenharmony_ci					    0, 6000000U, 1, 100000U);
13938c2ecf20Sopenharmony_ci	/* Total gain: 256 <=> 1x */
13948c2ecf20Sopenharmony_ci	ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN,
13958c2ecf20Sopenharmony_ci					0, 256, 1, 256);
13968c2ecf20Sopenharmony_ci	v4l2_ctrl_auto_cluster(3, &ctrls->auto_exp, 0, false);
13978c2ecf20Sopenharmony_ci
13988c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_POWER_LINE_FREQUENCY,
13998c2ecf20Sopenharmony_ci			       V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0,
14008c2ecf20Sopenharmony_ci			       V4L2_CID_POWER_LINE_FREQUENCY_AUTO);
14018c2ecf20Sopenharmony_ci
14028c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_COLORFX,
14038c2ecf20Sopenharmony_ci			       V4L2_COLORFX_SKY_BLUE, ~0x6f, V4L2_COLORFX_NONE);
14048c2ecf20Sopenharmony_ci
14058c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_WHITE_BALANCE_TEMPERATURE,
14068c2ecf20Sopenharmony_ci			  0, 256, 1, 0);
14078c2ecf20Sopenharmony_ci
14088c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION, -127, 127, 1, 0);
14098c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -127, 127, 1, 0);
14108c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -127, 127, 1, 0);
14118c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SHARPNESS, -127, 127, 1, 0);
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_ci	if (hdl->error) {
14148c2ecf20Sopenharmony_ci		ret = hdl->error;
14158c2ecf20Sopenharmony_ci		v4l2_ctrl_handler_free(hdl);
14168c2ecf20Sopenharmony_ci		return ret;
14178c2ecf20Sopenharmony_ci	}
14188c2ecf20Sopenharmony_ci
14198c2ecf20Sopenharmony_ci	s5k6aa->sd.ctrl_handler = hdl;
14208c2ecf20Sopenharmony_ci	return 0;
14218c2ecf20Sopenharmony_ci}
14228c2ecf20Sopenharmony_ci
14238c2ecf20Sopenharmony_ci/*
14248c2ecf20Sopenharmony_ci * V4L2 subdev internal operations
14258c2ecf20Sopenharmony_ci */
14268c2ecf20Sopenharmony_cistatic int s5k6aa_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
14278c2ecf20Sopenharmony_ci{
14288c2ecf20Sopenharmony_ci	struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(sd, fh->pad, 0);
14298c2ecf20Sopenharmony_ci	struct v4l2_rect *crop = v4l2_subdev_get_try_crop(sd, fh->pad, 0);
14308c2ecf20Sopenharmony_ci
14318c2ecf20Sopenharmony_ci	format->colorspace = s5k6aa_formats[0].colorspace;
14328c2ecf20Sopenharmony_ci	format->code = s5k6aa_formats[0].code;
14338c2ecf20Sopenharmony_ci	format->width = S5K6AA_OUT_WIDTH_DEF;
14348c2ecf20Sopenharmony_ci	format->height = S5K6AA_OUT_HEIGHT_DEF;
14358c2ecf20Sopenharmony_ci	format->field = V4L2_FIELD_NONE;
14368c2ecf20Sopenharmony_ci
14378c2ecf20Sopenharmony_ci	crop->width = S5K6AA_WIN_WIDTH_MAX;
14388c2ecf20Sopenharmony_ci	crop->height = S5K6AA_WIN_HEIGHT_MAX;
14398c2ecf20Sopenharmony_ci	crop->left = 0;
14408c2ecf20Sopenharmony_ci	crop->top = 0;
14418c2ecf20Sopenharmony_ci
14428c2ecf20Sopenharmony_ci	return 0;
14438c2ecf20Sopenharmony_ci}
14448c2ecf20Sopenharmony_ci
14458c2ecf20Sopenharmony_cistatic int s5k6aa_check_fw_revision(struct s5k6aa *s5k6aa)
14468c2ecf20Sopenharmony_ci{
14478c2ecf20Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
14488c2ecf20Sopenharmony_ci	u16 api_ver = 0, fw_rev = 0;
14498c2ecf20Sopenharmony_ci
14508c2ecf20Sopenharmony_ci	int ret = s5k6aa_set_ahb_address(client);
14518c2ecf20Sopenharmony_ci
14528c2ecf20Sopenharmony_ci	if (!ret)
14538c2ecf20Sopenharmony_ci		ret = s5k6aa_read(client, REG_FW_APIVER, &api_ver);
14548c2ecf20Sopenharmony_ci	if (!ret)
14558c2ecf20Sopenharmony_ci		ret = s5k6aa_read(client, REG_FW_REVISION, &fw_rev);
14568c2ecf20Sopenharmony_ci	if (ret) {
14578c2ecf20Sopenharmony_ci		v4l2_err(&s5k6aa->sd, "FW revision check failed!\n");
14588c2ecf20Sopenharmony_ci		return ret;
14598c2ecf20Sopenharmony_ci	}
14608c2ecf20Sopenharmony_ci
14618c2ecf20Sopenharmony_ci	v4l2_info(&s5k6aa->sd, "FW API ver.: 0x%X, FW rev.: 0x%X\n",
14628c2ecf20Sopenharmony_ci		  api_ver, fw_rev);
14638c2ecf20Sopenharmony_ci
14648c2ecf20Sopenharmony_ci	return api_ver == S5K6AAFX_FW_APIVER ? 0 : -ENODEV;
14658c2ecf20Sopenharmony_ci}
14668c2ecf20Sopenharmony_ci
14678c2ecf20Sopenharmony_cistatic int s5k6aa_registered(struct v4l2_subdev *sd)
14688c2ecf20Sopenharmony_ci{
14698c2ecf20Sopenharmony_ci	struct s5k6aa *s5k6aa = to_s5k6aa(sd);
14708c2ecf20Sopenharmony_ci	int ret;
14718c2ecf20Sopenharmony_ci
14728c2ecf20Sopenharmony_ci	mutex_lock(&s5k6aa->lock);
14738c2ecf20Sopenharmony_ci	ret = __s5k6aa_power_on(s5k6aa);
14748c2ecf20Sopenharmony_ci	if (!ret) {
14758c2ecf20Sopenharmony_ci		msleep(100);
14768c2ecf20Sopenharmony_ci		ret = s5k6aa_check_fw_revision(s5k6aa);
14778c2ecf20Sopenharmony_ci		__s5k6aa_power_off(s5k6aa);
14788c2ecf20Sopenharmony_ci	}
14798c2ecf20Sopenharmony_ci	mutex_unlock(&s5k6aa->lock);
14808c2ecf20Sopenharmony_ci
14818c2ecf20Sopenharmony_ci	return ret;
14828c2ecf20Sopenharmony_ci}
14838c2ecf20Sopenharmony_ci
14848c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_internal_ops s5k6aa_subdev_internal_ops = {
14858c2ecf20Sopenharmony_ci	.registered = s5k6aa_registered,
14868c2ecf20Sopenharmony_ci	.open = s5k6aa_open,
14878c2ecf20Sopenharmony_ci};
14888c2ecf20Sopenharmony_ci
14898c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_core_ops s5k6aa_core_ops = {
14908c2ecf20Sopenharmony_ci	.s_power = s5k6aa_set_power,
14918c2ecf20Sopenharmony_ci	.log_status = s5k6aa_log_status,
14928c2ecf20Sopenharmony_ci};
14938c2ecf20Sopenharmony_ci
14948c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops s5k6aa_subdev_ops = {
14958c2ecf20Sopenharmony_ci	.core = &s5k6aa_core_ops,
14968c2ecf20Sopenharmony_ci	.pad = &s5k6aa_pad_ops,
14978c2ecf20Sopenharmony_ci	.video = &s5k6aa_video_ops,
14988c2ecf20Sopenharmony_ci};
14998c2ecf20Sopenharmony_ci
15008c2ecf20Sopenharmony_ci/*
15018c2ecf20Sopenharmony_ci * GPIO setup
15028c2ecf20Sopenharmony_ci */
15038c2ecf20Sopenharmony_ci
15048c2ecf20Sopenharmony_cistatic int s5k6aa_configure_gpios(struct s5k6aa *s5k6aa,
15058c2ecf20Sopenharmony_ci				  const struct s5k6aa_platform_data *pdata)
15068c2ecf20Sopenharmony_ci{
15078c2ecf20Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
15088c2ecf20Sopenharmony_ci	const struct s5k6aa_gpio *gpio;
15098c2ecf20Sopenharmony_ci	unsigned long flags;
15108c2ecf20Sopenharmony_ci	int ret;
15118c2ecf20Sopenharmony_ci
15128c2ecf20Sopenharmony_ci	s5k6aa->gpio[STBY].gpio = -EINVAL;
15138c2ecf20Sopenharmony_ci	s5k6aa->gpio[RSET].gpio  = -EINVAL;
15148c2ecf20Sopenharmony_ci
15158c2ecf20Sopenharmony_ci	gpio = &pdata->gpio_stby;
15168c2ecf20Sopenharmony_ci	if (gpio_is_valid(gpio->gpio)) {
15178c2ecf20Sopenharmony_ci		flags = (gpio->level ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW)
15188c2ecf20Sopenharmony_ci		      | GPIOF_EXPORT;
15198c2ecf20Sopenharmony_ci		ret = devm_gpio_request_one(&client->dev, gpio->gpio, flags,
15208c2ecf20Sopenharmony_ci					    "S5K6AA_STBY");
15218c2ecf20Sopenharmony_ci		if (ret < 0)
15228c2ecf20Sopenharmony_ci			return ret;
15238c2ecf20Sopenharmony_ci
15248c2ecf20Sopenharmony_ci		s5k6aa->gpio[STBY] = *gpio;
15258c2ecf20Sopenharmony_ci	}
15268c2ecf20Sopenharmony_ci
15278c2ecf20Sopenharmony_ci	gpio = &pdata->gpio_reset;
15288c2ecf20Sopenharmony_ci	if (gpio_is_valid(gpio->gpio)) {
15298c2ecf20Sopenharmony_ci		flags = (gpio->level ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW)
15308c2ecf20Sopenharmony_ci		      | GPIOF_EXPORT;
15318c2ecf20Sopenharmony_ci		ret = devm_gpio_request_one(&client->dev, gpio->gpio, flags,
15328c2ecf20Sopenharmony_ci					    "S5K6AA_RST");
15338c2ecf20Sopenharmony_ci		if (ret < 0)
15348c2ecf20Sopenharmony_ci			return ret;
15358c2ecf20Sopenharmony_ci
15368c2ecf20Sopenharmony_ci		s5k6aa->gpio[RSET] = *gpio;
15378c2ecf20Sopenharmony_ci	}
15388c2ecf20Sopenharmony_ci
15398c2ecf20Sopenharmony_ci	return 0;
15408c2ecf20Sopenharmony_ci}
15418c2ecf20Sopenharmony_ci
15428c2ecf20Sopenharmony_cistatic int s5k6aa_probe(struct i2c_client *client,
15438c2ecf20Sopenharmony_ci			const struct i2c_device_id *id)
15448c2ecf20Sopenharmony_ci{
15458c2ecf20Sopenharmony_ci	const struct s5k6aa_platform_data *pdata = client->dev.platform_data;
15468c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd;
15478c2ecf20Sopenharmony_ci	struct s5k6aa *s5k6aa;
15488c2ecf20Sopenharmony_ci	int i, ret;
15498c2ecf20Sopenharmony_ci
15508c2ecf20Sopenharmony_ci	if (pdata == NULL) {
15518c2ecf20Sopenharmony_ci		dev_err(&client->dev, "Platform data not specified\n");
15528c2ecf20Sopenharmony_ci		return -EINVAL;
15538c2ecf20Sopenharmony_ci	}
15548c2ecf20Sopenharmony_ci
15558c2ecf20Sopenharmony_ci	if (pdata->mclk_frequency == 0) {
15568c2ecf20Sopenharmony_ci		dev_err(&client->dev, "MCLK frequency not specified\n");
15578c2ecf20Sopenharmony_ci		return -EINVAL;
15588c2ecf20Sopenharmony_ci	}
15598c2ecf20Sopenharmony_ci
15608c2ecf20Sopenharmony_ci	s5k6aa = devm_kzalloc(&client->dev, sizeof(*s5k6aa), GFP_KERNEL);
15618c2ecf20Sopenharmony_ci	if (!s5k6aa)
15628c2ecf20Sopenharmony_ci		return -ENOMEM;
15638c2ecf20Sopenharmony_ci
15648c2ecf20Sopenharmony_ci	mutex_init(&s5k6aa->lock);
15658c2ecf20Sopenharmony_ci
15668c2ecf20Sopenharmony_ci	s5k6aa->mclk_frequency = pdata->mclk_frequency;
15678c2ecf20Sopenharmony_ci	s5k6aa->bus_type = pdata->bus_type;
15688c2ecf20Sopenharmony_ci	s5k6aa->mipi_lanes = pdata->nlanes;
15698c2ecf20Sopenharmony_ci	s5k6aa->s_power	= pdata->set_power;
15708c2ecf20Sopenharmony_ci	s5k6aa->inv_hflip = pdata->horiz_flip;
15718c2ecf20Sopenharmony_ci	s5k6aa->inv_vflip = pdata->vert_flip;
15728c2ecf20Sopenharmony_ci
15738c2ecf20Sopenharmony_ci	sd = &s5k6aa->sd;
15748c2ecf20Sopenharmony_ci	v4l2_i2c_subdev_init(sd, client, &s5k6aa_subdev_ops);
15758c2ecf20Sopenharmony_ci	/* Static name; NEVER use in new drivers! */
15768c2ecf20Sopenharmony_ci	strscpy(sd->name, DRIVER_NAME, sizeof(sd->name));
15778c2ecf20Sopenharmony_ci
15788c2ecf20Sopenharmony_ci	sd->internal_ops = &s5k6aa_subdev_internal_ops;
15798c2ecf20Sopenharmony_ci	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
15808c2ecf20Sopenharmony_ci
15818c2ecf20Sopenharmony_ci	s5k6aa->pad.flags = MEDIA_PAD_FL_SOURCE;
15828c2ecf20Sopenharmony_ci	sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
15838c2ecf20Sopenharmony_ci	ret = media_entity_pads_init(&sd->entity, 1, &s5k6aa->pad);
15848c2ecf20Sopenharmony_ci	if (ret)
15858c2ecf20Sopenharmony_ci		return ret;
15868c2ecf20Sopenharmony_ci
15878c2ecf20Sopenharmony_ci	ret = s5k6aa_configure_gpios(s5k6aa, pdata);
15888c2ecf20Sopenharmony_ci	if (ret)
15898c2ecf20Sopenharmony_ci		goto out_err;
15908c2ecf20Sopenharmony_ci
15918c2ecf20Sopenharmony_ci	for (i = 0; i < S5K6AA_NUM_SUPPLIES; i++)
15928c2ecf20Sopenharmony_ci		s5k6aa->supplies[i].supply = s5k6aa_supply_names[i];
15938c2ecf20Sopenharmony_ci
15948c2ecf20Sopenharmony_ci	ret = devm_regulator_bulk_get(&client->dev, S5K6AA_NUM_SUPPLIES,
15958c2ecf20Sopenharmony_ci				 s5k6aa->supplies);
15968c2ecf20Sopenharmony_ci	if (ret) {
15978c2ecf20Sopenharmony_ci		dev_err(&client->dev, "Failed to get regulators\n");
15988c2ecf20Sopenharmony_ci		goto out_err;
15998c2ecf20Sopenharmony_ci	}
16008c2ecf20Sopenharmony_ci
16018c2ecf20Sopenharmony_ci	ret = s5k6aa_initialize_ctrls(s5k6aa);
16028c2ecf20Sopenharmony_ci	if (ret)
16038c2ecf20Sopenharmony_ci		goto out_err;
16048c2ecf20Sopenharmony_ci
16058c2ecf20Sopenharmony_ci	s5k6aa_presets_data_init(s5k6aa);
16068c2ecf20Sopenharmony_ci
16078c2ecf20Sopenharmony_ci	s5k6aa->ccd_rect.width = S5K6AA_WIN_WIDTH_MAX;
16088c2ecf20Sopenharmony_ci	s5k6aa->ccd_rect.height	= S5K6AA_WIN_HEIGHT_MAX;
16098c2ecf20Sopenharmony_ci	s5k6aa->ccd_rect.left = 0;
16108c2ecf20Sopenharmony_ci	s5k6aa->ccd_rect.top = 0;
16118c2ecf20Sopenharmony_ci
16128c2ecf20Sopenharmony_ci	return 0;
16138c2ecf20Sopenharmony_ci
16148c2ecf20Sopenharmony_ciout_err:
16158c2ecf20Sopenharmony_ci	media_entity_cleanup(&s5k6aa->sd.entity);
16168c2ecf20Sopenharmony_ci	return ret;
16178c2ecf20Sopenharmony_ci}
16188c2ecf20Sopenharmony_ci
16198c2ecf20Sopenharmony_cistatic int s5k6aa_remove(struct i2c_client *client)
16208c2ecf20Sopenharmony_ci{
16218c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd = i2c_get_clientdata(client);
16228c2ecf20Sopenharmony_ci
16238c2ecf20Sopenharmony_ci	v4l2_device_unregister_subdev(sd);
16248c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_free(sd->ctrl_handler);
16258c2ecf20Sopenharmony_ci	media_entity_cleanup(&sd->entity);
16268c2ecf20Sopenharmony_ci
16278c2ecf20Sopenharmony_ci	return 0;
16288c2ecf20Sopenharmony_ci}
16298c2ecf20Sopenharmony_ci
16308c2ecf20Sopenharmony_cistatic const struct i2c_device_id s5k6aa_id[] = {
16318c2ecf20Sopenharmony_ci	{ DRIVER_NAME, 0 },
16328c2ecf20Sopenharmony_ci	{ },
16338c2ecf20Sopenharmony_ci};
16348c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, s5k6aa_id);
16358c2ecf20Sopenharmony_ci
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_cistatic struct i2c_driver s5k6aa_i2c_driver = {
16388c2ecf20Sopenharmony_ci	.driver = {
16398c2ecf20Sopenharmony_ci		.name = DRIVER_NAME
16408c2ecf20Sopenharmony_ci	},
16418c2ecf20Sopenharmony_ci	.probe		= s5k6aa_probe,
16428c2ecf20Sopenharmony_ci	.remove		= s5k6aa_remove,
16438c2ecf20Sopenharmony_ci	.id_table	= s5k6aa_id,
16448c2ecf20Sopenharmony_ci};
16458c2ecf20Sopenharmony_ci
16468c2ecf20Sopenharmony_cimodule_i2c_driver(s5k6aa_i2c_driver);
16478c2ecf20Sopenharmony_ci
16488c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Samsung S5K6AA(FX) SXGA camera driver");
16498c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
16508c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1651