xref: /openbmc/linux/drivers/media/i2c/ov5647.c (revision 2f038c97)
124169a5aSJacopo Mondi // SPDX-License-Identifier: GPL-2.0
23c2472a3SRamiro Oliveira /*
33c2472a3SRamiro Oliveira  * A V4L2 driver for OmniVision OV5647 cameras.
43c2472a3SRamiro Oliveira  *
53c2472a3SRamiro Oliveira  * Based on Samsung S5K6AAFX SXGA 1/6" 1.3M CMOS Image Sensor driver
63c2472a3SRamiro Oliveira  * Copyright (C) 2011 Sylwester Nawrocki <s.nawrocki@samsung.com>
73c2472a3SRamiro Oliveira  *
83c2472a3SRamiro Oliveira  * Based on Omnivision OV7670 Camera Driver
93c2472a3SRamiro Oliveira  * Copyright (C) 2006-7 Jonathan Corbet <corbet@lwn.net>
103c2472a3SRamiro Oliveira  *
113c2472a3SRamiro Oliveira  * Copyright (C) 2016, Synopsys, Inc.
123c2472a3SRamiro Oliveira  */
133c2472a3SRamiro Oliveira 
143c2472a3SRamiro Oliveira #include <linux/clk.h>
153c2472a3SRamiro Oliveira #include <linux/delay.h>
16b050791dSDave Stevenson #include <linux/gpio/consumer.h>
173c2472a3SRamiro Oliveira #include <linux/i2c.h>
183c2472a3SRamiro Oliveira #include <linux/init.h>
193c2472a3SRamiro Oliveira #include <linux/io.h>
203c2472a3SRamiro Oliveira #include <linux/module.h>
21859969b3SSakari Ailus #include <linux/of_graph.h>
22089b7c70SJacopo Mondi #include <linux/pm_runtime.h>
233c2472a3SRamiro Oliveira #include <linux/slab.h>
243c2472a3SRamiro Oliveira #include <linux/videodev2.h>
254974c2f1SDavid Plowman #include <media/v4l2-ctrls.h>
263c2472a3SRamiro Oliveira #include <media/v4l2-device.h>
27859969b3SSakari Ailus #include <media/v4l2-fwnode.h>
283c2472a3SRamiro Oliveira #include <media/v4l2-image-sizes.h>
293c2472a3SRamiro Oliveira #include <media/v4l2-mediabus.h>
303c2472a3SRamiro Oliveira 
31b050791dSDave Stevenson /*
32b050791dSDave Stevenson  * From the datasheet, "20ms after PWDN goes low or 20ms after RESETB goes
33b050791dSDave Stevenson  * high if reset is inserted after PWDN goes high, host can access sensor's
34b050791dSDave Stevenson  * SCCB to initialize sensor."
35b050791dSDave Stevenson  */
36b050791dSDave Stevenson #define PWDN_ACTIVE_DELAY_MS	20
37b050791dSDave Stevenson 
38cef66734SJacob Chen #define MIPI_CTRL00_CLOCK_LANE_GATE		BIT(5)
39dea4fcfeSDave Stevenson #define MIPI_CTRL00_LINE_SYNC_ENABLE		BIT(4)
40cef66734SJacob Chen #define MIPI_CTRL00_BUS_IDLE			BIT(2)
41cef66734SJacob Chen #define MIPI_CTRL00_CLOCK_LANE_DISABLE		BIT(0)
42cef66734SJacob Chen 
43cef66734SJacob Chen #define OV5647_SW_STANDBY		0x0100
443c2472a3SRamiro Oliveira #define OV5647_SW_RESET			0x0103
45c9a05cecSJacopo Mondi #define OV5647_REG_CHIPID_H		0x300a
46c9a05cecSJacopo Mondi #define OV5647_REG_CHIPID_L		0x300b
47c9a05cecSJacopo Mondi #define OV5640_REG_PAD_OUT		0x300d
484974c2f1SDavid Plowman #define OV5647_REG_EXP_HI		0x3500
494974c2f1SDavid Plowman #define OV5647_REG_EXP_MID		0x3501
504974c2f1SDavid Plowman #define OV5647_REG_EXP_LO		0x3502
514974c2f1SDavid Plowman #define OV5647_REG_AEC_AGC		0x3503
524974c2f1SDavid Plowman #define OV5647_REG_GAIN_HI		0x350a
534974c2f1SDavid Plowman #define OV5647_REG_GAIN_LO		0x350b
542512c064SDave Stevenson #define OV5647_REG_VTS_HI		0x380e
552512c064SDave Stevenson #define OV5647_REG_VTS_LO		0x380f
56cef66734SJacob Chen #define OV5647_REG_FRAME_OFF_NUMBER	0x4202
57cef66734SJacob Chen #define OV5647_REG_MIPI_CTRL00		0x4800
58cef66734SJacob Chen #define OV5647_REG_MIPI_CTRL14		0x4814
594974c2f1SDavid Plowman #define OV5647_REG_AWB			0x5001
603c2472a3SRamiro Oliveira 
613c2472a3SRamiro Oliveira #define REG_TERM 0xfffe
623c2472a3SRamiro Oliveira #define VAL_TERM 0xfe
633c2472a3SRamiro Oliveira #define REG_DLY  0xffff
643c2472a3SRamiro Oliveira 
6514f70a32SDave Stevenson /* OV5647 native and active pixel array size */
6614f70a32SDave Stevenson #define OV5647_NATIVE_WIDTH		2624U
6714f70a32SDave Stevenson #define OV5647_NATIVE_HEIGHT		1956U
683c2472a3SRamiro Oliveira 
6914f70a32SDave Stevenson #define OV5647_PIXEL_ARRAY_LEFT		16U
7014f70a32SDave Stevenson #define OV5647_PIXEL_ARRAY_TOP		16U
7114f70a32SDave Stevenson #define OV5647_PIXEL_ARRAY_WIDTH	2592U
7214f70a32SDave Stevenson #define OV5647_PIXEL_ARRAY_HEIGHT	1944U
733c2472a3SRamiro Oliveira 
742512c064SDave Stevenson #define OV5647_VBLANK_MIN		4
752512c064SDave Stevenson #define OV5647_VTS_MAX			32767
762512c064SDave Stevenson 
77646a0249SDave Stevenson #define OV5647_EXPOSURE_MIN		4
78646a0249SDave Stevenson #define OV5647_EXPOSURE_STEP		1
79646a0249SDave Stevenson #define OV5647_EXPOSURE_DEFAULT		1000
80646a0249SDave Stevenson #define OV5647_EXPOSURE_MAX		65535
81646a0249SDave Stevenson 
823c2472a3SRamiro Oliveira struct regval_list {
833c2472a3SRamiro Oliveira 	u16 addr;
843c2472a3SRamiro Oliveira 	u8 data;
853c2472a3SRamiro Oliveira };
863c2472a3SRamiro Oliveira 
87d7d6074eSJacopo Mondi struct ov5647_mode {
88d7d6074eSJacopo Mondi 	struct v4l2_mbus_framefmt	format;
8914f70a32SDave Stevenson 	struct v4l2_rect		crop;
90911f4516SDave Stevenson 	u64				pixel_rate;
91c6da1ae4SJacopo Mondi 	int				hts;
922512c064SDave Stevenson 	int				vts;
93d7d6074eSJacopo Mondi 	const struct regval_list	*reg_list;
94d7d6074eSJacopo Mondi 	unsigned int			num_regs;
95d7d6074eSJacopo Mondi };
96d7d6074eSJacopo Mondi 
97d7d6074eSJacopo Mondi struct ov5647_format_list {
98d7d6074eSJacopo Mondi 	unsigned int			mbus_code;
99d7d6074eSJacopo Mondi 	const struct ov5647_mode	*modes;
100d7d6074eSJacopo Mondi 	unsigned int			num_modes;
101d7d6074eSJacopo Mondi };
102d7d6074eSJacopo Mondi 
1033c2472a3SRamiro Oliveira struct ov5647 {
1043c2472a3SRamiro Oliveira 	struct v4l2_subdev		sd;
1053c2472a3SRamiro Oliveira 	struct media_pad		pad;
1063c2472a3SRamiro Oliveira 	struct mutex			lock;
1073c2472a3SRamiro Oliveira 	int				power_count;
1083c2472a3SRamiro Oliveira 	struct clk			*xclk;
109b050791dSDave Stevenson 	struct gpio_desc		*pwdn;
110dea4fcfeSDave Stevenson 	bool				clock_ncont;
1114974c2f1SDavid Plowman 	struct v4l2_ctrl_handler	ctrls;
112d7d6074eSJacopo Mondi 	const struct ov5647_mode	*mode;
113911f4516SDave Stevenson 	struct v4l2_ctrl		*pixel_rate;
114c6da1ae4SJacopo Mondi 	struct v4l2_ctrl		*hblank;
1152512c064SDave Stevenson 	struct v4l2_ctrl		*vblank;
116646a0249SDave Stevenson 	struct v4l2_ctrl		*exposure;
117*2f038c97SJacopo Mondi 	bool				streaming;
1183c2472a3SRamiro Oliveira };
1193c2472a3SRamiro Oliveira 
1205bc5ca71SJacopo Mondi static inline struct ov5647 *to_sensor(struct v4l2_subdev *sd)
1213c2472a3SRamiro Oliveira {
1223c2472a3SRamiro Oliveira 	return container_of(sd, struct ov5647, sd);
1233c2472a3SRamiro Oliveira }
1243c2472a3SRamiro Oliveira 
1253c2472a3SRamiro Oliveira static struct regval_list sensor_oe_disable_regs[] = {
1263c2472a3SRamiro Oliveira 	{0x3000, 0x00},
1273c2472a3SRamiro Oliveira 	{0x3001, 0x00},
1283c2472a3SRamiro Oliveira 	{0x3002, 0x00},
1293c2472a3SRamiro Oliveira };
1303c2472a3SRamiro Oliveira 
1313c2472a3SRamiro Oliveira static struct regval_list sensor_oe_enable_regs[] = {
1323c2472a3SRamiro Oliveira 	{0x3000, 0x0f},
1333c2472a3SRamiro Oliveira 	{0x3001, 0xff},
1343c2472a3SRamiro Oliveira 	{0x3002, 0xe4},
1353c2472a3SRamiro Oliveira };
1363c2472a3SRamiro Oliveira 
137e907bd66SJacopo Mondi static const struct regval_list ov5647_640x480_8bpp[] = {
1383c2472a3SRamiro Oliveira 	{0x0100, 0x00},
1393c2472a3SRamiro Oliveira 	{0x0103, 0x01},
1403c2472a3SRamiro Oliveira 	{0x3034, 0x08},
1413c2472a3SRamiro Oliveira 	{0x3035, 0x21},
1423c2472a3SRamiro Oliveira 	{0x3036, 0x46},
1433c2472a3SRamiro Oliveira 	{0x303c, 0x11},
1443c2472a3SRamiro Oliveira 	{0x3106, 0xf5},
1453c2472a3SRamiro Oliveira 	{0x3821, 0x07},
1463c2472a3SRamiro Oliveira 	{0x3820, 0x41},
1473c2472a3SRamiro Oliveira 	{0x3827, 0xec},
1483c2472a3SRamiro Oliveira 	{0x370c, 0x0f},
1493c2472a3SRamiro Oliveira 	{0x3612, 0x59},
1503c2472a3SRamiro Oliveira 	{0x3618, 0x00},
1513c2472a3SRamiro Oliveira 	{0x5000, 0x06},
1523c2472a3SRamiro Oliveira 	{0x5002, 0x41},
1533c2472a3SRamiro Oliveira 	{0x5003, 0x08},
1543c2472a3SRamiro Oliveira 	{0x5a00, 0x08},
1553c2472a3SRamiro Oliveira 	{0x3000, 0x00},
1563c2472a3SRamiro Oliveira 	{0x3001, 0x00},
1573c2472a3SRamiro Oliveira 	{0x3002, 0x00},
1583c2472a3SRamiro Oliveira 	{0x3016, 0x08},
1593c2472a3SRamiro Oliveira 	{0x3017, 0xe0},
1603c2472a3SRamiro Oliveira 	{0x3018, 0x44},
1613c2472a3SRamiro Oliveira 	{0x301c, 0xf8},
1623c2472a3SRamiro Oliveira 	{0x301d, 0xf0},
1633c2472a3SRamiro Oliveira 	{0x3a18, 0x00},
1643c2472a3SRamiro Oliveira 	{0x3a19, 0xf8},
1653c2472a3SRamiro Oliveira 	{0x3c01, 0x80},
1663c2472a3SRamiro Oliveira 	{0x3b07, 0x0c},
1673c2472a3SRamiro Oliveira 	{0x380c, 0x07},
1683c2472a3SRamiro Oliveira 	{0x380d, 0x68},
1693c2472a3SRamiro Oliveira 	{0x3814, 0x31},
1703c2472a3SRamiro Oliveira 	{0x3815, 0x31},
1713c2472a3SRamiro Oliveira 	{0x3708, 0x64},
1723c2472a3SRamiro Oliveira 	{0x3709, 0x52},
1733c2472a3SRamiro Oliveira 	{0x3808, 0x02},
1743c2472a3SRamiro Oliveira 	{0x3809, 0x80},
1753c2472a3SRamiro Oliveira 	{0x380a, 0x01},
176c9a05cecSJacopo Mondi 	{0x380b, 0xe0},
1773c2472a3SRamiro Oliveira 	{0x3801, 0x00},
1783c2472a3SRamiro Oliveira 	{0x3802, 0x00},
1793c2472a3SRamiro Oliveira 	{0x3803, 0x00},
1803c2472a3SRamiro Oliveira 	{0x3804, 0x0a},
1813c2472a3SRamiro Oliveira 	{0x3805, 0x3f},
1823c2472a3SRamiro Oliveira 	{0x3806, 0x07},
1833c2472a3SRamiro Oliveira 	{0x3807, 0xa1},
1843c2472a3SRamiro Oliveira 	{0x3811, 0x08},
1853c2472a3SRamiro Oliveira 	{0x3813, 0x02},
1863c2472a3SRamiro Oliveira 	{0x3630, 0x2e},
1873c2472a3SRamiro Oliveira 	{0x3632, 0xe2},
1883c2472a3SRamiro Oliveira 	{0x3633, 0x23},
1893c2472a3SRamiro Oliveira 	{0x3634, 0x44},
1903c2472a3SRamiro Oliveira 	{0x3636, 0x06},
1913c2472a3SRamiro Oliveira 	{0x3620, 0x64},
1923c2472a3SRamiro Oliveira 	{0x3621, 0xe0},
1933c2472a3SRamiro Oliveira 	{0x3600, 0x37},
1943c2472a3SRamiro Oliveira 	{0x3704, 0xa0},
1953c2472a3SRamiro Oliveira 	{0x3703, 0x5a},
1963c2472a3SRamiro Oliveira 	{0x3715, 0x78},
1973c2472a3SRamiro Oliveira 	{0x3717, 0x01},
1983c2472a3SRamiro Oliveira 	{0x3731, 0x02},
1993c2472a3SRamiro Oliveira 	{0x370b, 0x60},
2003c2472a3SRamiro Oliveira 	{0x3705, 0x1a},
2013c2472a3SRamiro Oliveira 	{0x3f05, 0x02},
2023c2472a3SRamiro Oliveira 	{0x3f06, 0x10},
2033c2472a3SRamiro Oliveira 	{0x3f01, 0x0a},
2043c2472a3SRamiro Oliveira 	{0x3a08, 0x01},
2053c2472a3SRamiro Oliveira 	{0x3a09, 0x27},
2063c2472a3SRamiro Oliveira 	{0x3a0a, 0x00},
2073c2472a3SRamiro Oliveira 	{0x3a0b, 0xf6},
2083c2472a3SRamiro Oliveira 	{0x3a0d, 0x04},
2093c2472a3SRamiro Oliveira 	{0x3a0e, 0x03},
2103c2472a3SRamiro Oliveira 	{0x3a0f, 0x58},
2113c2472a3SRamiro Oliveira 	{0x3a10, 0x50},
2123c2472a3SRamiro Oliveira 	{0x3a1b, 0x58},
2133c2472a3SRamiro Oliveira 	{0x3a1e, 0x50},
2143c2472a3SRamiro Oliveira 	{0x3a11, 0x60},
2153c2472a3SRamiro Oliveira 	{0x3a1f, 0x28},
2163c2472a3SRamiro Oliveira 	{0x4001, 0x02},
2173c2472a3SRamiro Oliveira 	{0x4004, 0x02},
2183c2472a3SRamiro Oliveira 	{0x4000, 0x09},
2193c2472a3SRamiro Oliveira 	{0x4837, 0x24},
2203c2472a3SRamiro Oliveira 	{0x4050, 0x6e},
2213c2472a3SRamiro Oliveira 	{0x4051, 0x8f},
2223c2472a3SRamiro Oliveira 	{0x0100, 0x01},
2233c2472a3SRamiro Oliveira };
2243c2472a3SRamiro Oliveira 
225a8df5af6SJacopo Mondi static struct regval_list ov5647_2592x1944_10bpp[] = {
226a8df5af6SJacopo Mondi 	{0x0100, 0x00},
227a8df5af6SJacopo Mondi 	{0x0103, 0x01},
228a8df5af6SJacopo Mondi 	{0x3034, 0x1a},
229a8df5af6SJacopo Mondi 	{0x3035, 0x21},
230a8df5af6SJacopo Mondi 	{0x3036, 0x69},
231a8df5af6SJacopo Mondi 	{0x303c, 0x11},
232a8df5af6SJacopo Mondi 	{0x3106, 0xf5},
233a8df5af6SJacopo Mondi 	{0x3821, 0x06},
234a8df5af6SJacopo Mondi 	{0x3820, 0x00},
235a8df5af6SJacopo Mondi 	{0x3827, 0xec},
236a8df5af6SJacopo Mondi 	{0x370c, 0x03},
237a8df5af6SJacopo Mondi 	{0x3612, 0x5b},
238a8df5af6SJacopo Mondi 	{0x3618, 0x04},
239a8df5af6SJacopo Mondi 	{0x5000, 0x06},
240a8df5af6SJacopo Mondi 	{0x5002, 0x41},
241a8df5af6SJacopo Mondi 	{0x5003, 0x08},
242a8df5af6SJacopo Mondi 	{0x5a00, 0x08},
243a8df5af6SJacopo Mondi 	{0x3000, 0x00},
244a8df5af6SJacopo Mondi 	{0x3001, 0x00},
245a8df5af6SJacopo Mondi 	{0x3002, 0x00},
246a8df5af6SJacopo Mondi 	{0x3016, 0x08},
247a8df5af6SJacopo Mondi 	{0x3017, 0xe0},
248a8df5af6SJacopo Mondi 	{0x3018, 0x44},
249a8df5af6SJacopo Mondi 	{0x301c, 0xf8},
250a8df5af6SJacopo Mondi 	{0x301d, 0xf0},
251a8df5af6SJacopo Mondi 	{0x3a18, 0x00},
252a8df5af6SJacopo Mondi 	{0x3a19, 0xf8},
253a8df5af6SJacopo Mondi 	{0x3c01, 0x80},
254a8df5af6SJacopo Mondi 	{0x3b07, 0x0c},
255a8df5af6SJacopo Mondi 	{0x380c, 0x0b},
256a8df5af6SJacopo Mondi 	{0x380d, 0x1c},
257a8df5af6SJacopo Mondi 	{0x3814, 0x11},
258a8df5af6SJacopo Mondi 	{0x3815, 0x11},
259a8df5af6SJacopo Mondi 	{0x3708, 0x64},
260a8df5af6SJacopo Mondi 	{0x3709, 0x12},
261a8df5af6SJacopo Mondi 	{0x3808, 0x0a},
262a8df5af6SJacopo Mondi 	{0x3809, 0x20},
263a8df5af6SJacopo Mondi 	{0x380a, 0x07},
264a8df5af6SJacopo Mondi 	{0x380b, 0x98},
265a8df5af6SJacopo Mondi 	{0x3800, 0x00},
266a8df5af6SJacopo Mondi 	{0x3801, 0x00},
267a8df5af6SJacopo Mondi 	{0x3802, 0x00},
268a8df5af6SJacopo Mondi 	{0x3803, 0x00},
269a8df5af6SJacopo Mondi 	{0x3804, 0x0a},
270a8df5af6SJacopo Mondi 	{0x3805, 0x3f},
271a8df5af6SJacopo Mondi 	{0x3806, 0x07},
272a8df5af6SJacopo Mondi 	{0x3807, 0xa3},
273a8df5af6SJacopo Mondi 	{0x3811, 0x10},
274a8df5af6SJacopo Mondi 	{0x3813, 0x06},
275a8df5af6SJacopo Mondi 	{0x3630, 0x2e},
276a8df5af6SJacopo Mondi 	{0x3632, 0xe2},
277a8df5af6SJacopo Mondi 	{0x3633, 0x23},
278a8df5af6SJacopo Mondi 	{0x3634, 0x44},
279a8df5af6SJacopo Mondi 	{0x3636, 0x06},
280a8df5af6SJacopo Mondi 	{0x3620, 0x64},
281a8df5af6SJacopo Mondi 	{0x3621, 0xe0},
282a8df5af6SJacopo Mondi 	{0x3600, 0x37},
283a8df5af6SJacopo Mondi 	{0x3704, 0xa0},
284a8df5af6SJacopo Mondi 	{0x3703, 0x5a},
285a8df5af6SJacopo Mondi 	{0x3715, 0x78},
286a8df5af6SJacopo Mondi 	{0x3717, 0x01},
287a8df5af6SJacopo Mondi 	{0x3731, 0x02},
288a8df5af6SJacopo Mondi 	{0x370b, 0x60},
289a8df5af6SJacopo Mondi 	{0x3705, 0x1a},
290a8df5af6SJacopo Mondi 	{0x3f05, 0x02},
291a8df5af6SJacopo Mondi 	{0x3f06, 0x10},
292a8df5af6SJacopo Mondi 	{0x3f01, 0x0a},
293a8df5af6SJacopo Mondi 	{0x3a08, 0x01},
294a8df5af6SJacopo Mondi 	{0x3a09, 0x28},
295a8df5af6SJacopo Mondi 	{0x3a0a, 0x00},
296a8df5af6SJacopo Mondi 	{0x3a0b, 0xf6},
297a8df5af6SJacopo Mondi 	{0x3a0d, 0x08},
298a8df5af6SJacopo Mondi 	{0x3a0e, 0x06},
299a8df5af6SJacopo Mondi 	{0x3a0f, 0x58},
300a8df5af6SJacopo Mondi 	{0x3a10, 0x50},
301a8df5af6SJacopo Mondi 	{0x3a1b, 0x58},
302a8df5af6SJacopo Mondi 	{0x3a1e, 0x50},
303a8df5af6SJacopo Mondi 	{0x3a11, 0x60},
304a8df5af6SJacopo Mondi 	{0x3a1f, 0x28},
305a8df5af6SJacopo Mondi 	{0x4001, 0x02},
306a8df5af6SJacopo Mondi 	{0x4004, 0x04},
307a8df5af6SJacopo Mondi 	{0x4000, 0x09},
308a8df5af6SJacopo Mondi 	{0x4837, 0x19},
309a8df5af6SJacopo Mondi 	{0x4800, 0x24},
310a8df5af6SJacopo Mondi 	{0x3503, 0x03},
311a8df5af6SJacopo Mondi 	{0x0100, 0x01},
312a8df5af6SJacopo Mondi };
313a8df5af6SJacopo Mondi 
314a8df5af6SJacopo Mondi static struct regval_list ov5647_1080p30_10bpp[] = {
315a8df5af6SJacopo Mondi 	{0x0100, 0x00},
316a8df5af6SJacopo Mondi 	{0x0103, 0x01},
317a8df5af6SJacopo Mondi 	{0x3034, 0x1a},
318a8df5af6SJacopo Mondi 	{0x3035, 0x21},
319a8df5af6SJacopo Mondi 	{0x3036, 0x62},
320a8df5af6SJacopo Mondi 	{0x303c, 0x11},
321a8df5af6SJacopo Mondi 	{0x3106, 0xf5},
322a8df5af6SJacopo Mondi 	{0x3821, 0x06},
323a8df5af6SJacopo Mondi 	{0x3820, 0x00},
324a8df5af6SJacopo Mondi 	{0x3827, 0xec},
325a8df5af6SJacopo Mondi 	{0x370c, 0x03},
326a8df5af6SJacopo Mondi 	{0x3612, 0x5b},
327a8df5af6SJacopo Mondi 	{0x3618, 0x04},
328a8df5af6SJacopo Mondi 	{0x5000, 0x06},
329a8df5af6SJacopo Mondi 	{0x5002, 0x41},
330a8df5af6SJacopo Mondi 	{0x5003, 0x08},
331a8df5af6SJacopo Mondi 	{0x5a00, 0x08},
332a8df5af6SJacopo Mondi 	{0x3000, 0x00},
333a8df5af6SJacopo Mondi 	{0x3001, 0x00},
334a8df5af6SJacopo Mondi 	{0x3002, 0x00},
335a8df5af6SJacopo Mondi 	{0x3016, 0x08},
336a8df5af6SJacopo Mondi 	{0x3017, 0xe0},
337a8df5af6SJacopo Mondi 	{0x3018, 0x44},
338a8df5af6SJacopo Mondi 	{0x301c, 0xf8},
339a8df5af6SJacopo Mondi 	{0x301d, 0xf0},
340a8df5af6SJacopo Mondi 	{0x3a18, 0x00},
341a8df5af6SJacopo Mondi 	{0x3a19, 0xf8},
342a8df5af6SJacopo Mondi 	{0x3c01, 0x80},
343a8df5af6SJacopo Mondi 	{0x3b07, 0x0c},
344a8df5af6SJacopo Mondi 	{0x380c, 0x09},
345a8df5af6SJacopo Mondi 	{0x380d, 0x70},
346a8df5af6SJacopo Mondi 	{0x3814, 0x11},
347a8df5af6SJacopo Mondi 	{0x3815, 0x11},
348a8df5af6SJacopo Mondi 	{0x3708, 0x64},
349a8df5af6SJacopo Mondi 	{0x3709, 0x12},
350a8df5af6SJacopo Mondi 	{0x3808, 0x07},
351a8df5af6SJacopo Mondi 	{0x3809, 0x80},
352a8df5af6SJacopo Mondi 	{0x380a, 0x04},
353a8df5af6SJacopo Mondi 	{0x380b, 0x38},
354a8df5af6SJacopo Mondi 	{0x3800, 0x01},
355a8df5af6SJacopo Mondi 	{0x3801, 0x5c},
356a8df5af6SJacopo Mondi 	{0x3802, 0x01},
357a8df5af6SJacopo Mondi 	{0x3803, 0xb2},
358a8df5af6SJacopo Mondi 	{0x3804, 0x08},
359a8df5af6SJacopo Mondi 	{0x3805, 0xe3},
360a8df5af6SJacopo Mondi 	{0x3806, 0x05},
361a8df5af6SJacopo Mondi 	{0x3807, 0xf1},
362a8df5af6SJacopo Mondi 	{0x3811, 0x04},
363a8df5af6SJacopo Mondi 	{0x3813, 0x02},
364a8df5af6SJacopo Mondi 	{0x3630, 0x2e},
365a8df5af6SJacopo Mondi 	{0x3632, 0xe2},
366a8df5af6SJacopo Mondi 	{0x3633, 0x23},
367a8df5af6SJacopo Mondi 	{0x3634, 0x44},
368a8df5af6SJacopo Mondi 	{0x3636, 0x06},
369a8df5af6SJacopo Mondi 	{0x3620, 0x64},
370a8df5af6SJacopo Mondi 	{0x3621, 0xe0},
371a8df5af6SJacopo Mondi 	{0x3600, 0x37},
372a8df5af6SJacopo Mondi 	{0x3704, 0xa0},
373a8df5af6SJacopo Mondi 	{0x3703, 0x5a},
374a8df5af6SJacopo Mondi 	{0x3715, 0x78},
375a8df5af6SJacopo Mondi 	{0x3717, 0x01},
376a8df5af6SJacopo Mondi 	{0x3731, 0x02},
377a8df5af6SJacopo Mondi 	{0x370b, 0x60},
378a8df5af6SJacopo Mondi 	{0x3705, 0x1a},
379a8df5af6SJacopo Mondi 	{0x3f05, 0x02},
380a8df5af6SJacopo Mondi 	{0x3f06, 0x10},
381a8df5af6SJacopo Mondi 	{0x3f01, 0x0a},
382a8df5af6SJacopo Mondi 	{0x3a08, 0x01},
383a8df5af6SJacopo Mondi 	{0x3a09, 0x4b},
384a8df5af6SJacopo Mondi 	{0x3a0a, 0x01},
385a8df5af6SJacopo Mondi 	{0x3a0b, 0x13},
386a8df5af6SJacopo Mondi 	{0x3a0d, 0x04},
387a8df5af6SJacopo Mondi 	{0x3a0e, 0x03},
388a8df5af6SJacopo Mondi 	{0x3a0f, 0x58},
389a8df5af6SJacopo Mondi 	{0x3a10, 0x50},
390a8df5af6SJacopo Mondi 	{0x3a1b, 0x58},
391a8df5af6SJacopo Mondi 	{0x3a1e, 0x50},
392a8df5af6SJacopo Mondi 	{0x3a11, 0x60},
393a8df5af6SJacopo Mondi 	{0x3a1f, 0x28},
394a8df5af6SJacopo Mondi 	{0x4001, 0x02},
395a8df5af6SJacopo Mondi 	{0x4004, 0x04},
396a8df5af6SJacopo Mondi 	{0x4000, 0x09},
397a8df5af6SJacopo Mondi 	{0x4837, 0x19},
398a8df5af6SJacopo Mondi 	{0x4800, 0x34},
399a8df5af6SJacopo Mondi 	{0x3503, 0x03},
400a8df5af6SJacopo Mondi 	{0x0100, 0x01},
401a8df5af6SJacopo Mondi };
402a8df5af6SJacopo Mondi 
403a8df5af6SJacopo Mondi static struct regval_list ov5647_2x2binned_10bpp[] = {
404a8df5af6SJacopo Mondi 	{0x0100, 0x00},
405a8df5af6SJacopo Mondi 	{0x0103, 0x01},
406a8df5af6SJacopo Mondi 	{0x3034, 0x1a},
407a8df5af6SJacopo Mondi 	{0x3035, 0x21},
408a8df5af6SJacopo Mondi 	{0x3036, 0x62},
409a8df5af6SJacopo Mondi 	{0x303c, 0x11},
410a8df5af6SJacopo Mondi 	{0x3106, 0xf5},
411a8df5af6SJacopo Mondi 	{0x3827, 0xec},
412a8df5af6SJacopo Mondi 	{0x370c, 0x03},
413a8df5af6SJacopo Mondi 	{0x3612, 0x59},
414a8df5af6SJacopo Mondi 	{0x3618, 0x00},
415a8df5af6SJacopo Mondi 	{0x5000, 0x06},
416a8df5af6SJacopo Mondi 	{0x5002, 0x41},
417a8df5af6SJacopo Mondi 	{0x5003, 0x08},
418a8df5af6SJacopo Mondi 	{0x5a00, 0x08},
419a8df5af6SJacopo Mondi 	{0x3000, 0x00},
420a8df5af6SJacopo Mondi 	{0x3001, 0x00},
421a8df5af6SJacopo Mondi 	{0x3002, 0x00},
422a8df5af6SJacopo Mondi 	{0x3016, 0x08},
423a8df5af6SJacopo Mondi 	{0x3017, 0xe0},
424a8df5af6SJacopo Mondi 	{0x3018, 0x44},
425a8df5af6SJacopo Mondi 	{0x301c, 0xf8},
426a8df5af6SJacopo Mondi 	{0x301d, 0xf0},
427a8df5af6SJacopo Mondi 	{0x3a18, 0x00},
428a8df5af6SJacopo Mondi 	{0x3a19, 0xf8},
429a8df5af6SJacopo Mondi 	{0x3c01, 0x80},
430a8df5af6SJacopo Mondi 	{0x3b07, 0x0c},
431a8df5af6SJacopo Mondi 	{0x3800, 0x00},
432a8df5af6SJacopo Mondi 	{0x3801, 0x00},
433a8df5af6SJacopo Mondi 	{0x3802, 0x00},
434a8df5af6SJacopo Mondi 	{0x3803, 0x00},
435a8df5af6SJacopo Mondi 	{0x3804, 0x0a},
436a8df5af6SJacopo Mondi 	{0x3805, 0x3f},
437a8df5af6SJacopo Mondi 	{0x3806, 0x07},
438a8df5af6SJacopo Mondi 	{0x3807, 0xa3},
439a8df5af6SJacopo Mondi 	{0x3808, 0x05},
440a8df5af6SJacopo Mondi 	{0x3809, 0x10},
441a8df5af6SJacopo Mondi 	{0x380a, 0x03},
442a8df5af6SJacopo Mondi 	{0x380b, 0xcc},
443a8df5af6SJacopo Mondi 	{0x380c, 0x07},
444a8df5af6SJacopo Mondi 	{0x380d, 0x68},
445a8df5af6SJacopo Mondi 	{0x3811, 0x0c},
446a8df5af6SJacopo Mondi 	{0x3813, 0x06},
447a8df5af6SJacopo Mondi 	{0x3814, 0x31},
448a8df5af6SJacopo Mondi 	{0x3815, 0x31},
449a8df5af6SJacopo Mondi 	{0x3630, 0x2e},
450a8df5af6SJacopo Mondi 	{0x3632, 0xe2},
451a8df5af6SJacopo Mondi 	{0x3633, 0x23},
452a8df5af6SJacopo Mondi 	{0x3634, 0x44},
453a8df5af6SJacopo Mondi 	{0x3636, 0x06},
454a8df5af6SJacopo Mondi 	{0x3620, 0x64},
455a8df5af6SJacopo Mondi 	{0x3621, 0xe0},
456a8df5af6SJacopo Mondi 	{0x3600, 0x37},
457a8df5af6SJacopo Mondi 	{0x3704, 0xa0},
458a8df5af6SJacopo Mondi 	{0x3703, 0x5a},
459a8df5af6SJacopo Mondi 	{0x3715, 0x78},
460a8df5af6SJacopo Mondi 	{0x3717, 0x01},
461a8df5af6SJacopo Mondi 	{0x3731, 0x02},
462a8df5af6SJacopo Mondi 	{0x370b, 0x60},
463a8df5af6SJacopo Mondi 	{0x3705, 0x1a},
464a8df5af6SJacopo Mondi 	{0x3f05, 0x02},
465a8df5af6SJacopo Mondi 	{0x3f06, 0x10},
466a8df5af6SJacopo Mondi 	{0x3f01, 0x0a},
467a8df5af6SJacopo Mondi 	{0x3a08, 0x01},
468a8df5af6SJacopo Mondi 	{0x3a09, 0x28},
469a8df5af6SJacopo Mondi 	{0x3a0a, 0x00},
470a8df5af6SJacopo Mondi 	{0x3a0b, 0xf6},
471a8df5af6SJacopo Mondi 	{0x3a0d, 0x08},
472a8df5af6SJacopo Mondi 	{0x3a0e, 0x06},
473a8df5af6SJacopo Mondi 	{0x3a0f, 0x58},
474a8df5af6SJacopo Mondi 	{0x3a10, 0x50},
475a8df5af6SJacopo Mondi 	{0x3a1b, 0x58},
476a8df5af6SJacopo Mondi 	{0x3a1e, 0x50},
477a8df5af6SJacopo Mondi 	{0x3a11, 0x60},
478a8df5af6SJacopo Mondi 	{0x3a1f, 0x28},
479a8df5af6SJacopo Mondi 	{0x4001, 0x02},
480a8df5af6SJacopo Mondi 	{0x4004, 0x04},
481a8df5af6SJacopo Mondi 	{0x4000, 0x09},
482a8df5af6SJacopo Mondi 	{0x4837, 0x16},
483a8df5af6SJacopo Mondi 	{0x4800, 0x24},
484a8df5af6SJacopo Mondi 	{0x3503, 0x03},
485a8df5af6SJacopo Mondi 	{0x3820, 0x41},
486a8df5af6SJacopo Mondi 	{0x3821, 0x07},
487a8df5af6SJacopo Mondi 	{0x350a, 0x00},
488a8df5af6SJacopo Mondi 	{0x350b, 0x10},
489a8df5af6SJacopo Mondi 	{0x3500, 0x00},
490a8df5af6SJacopo Mondi 	{0x3501, 0x1a},
491a8df5af6SJacopo Mondi 	{0x3502, 0xf0},
492a8df5af6SJacopo Mondi 	{0x3212, 0xa0},
493a8df5af6SJacopo Mondi 	{0x0100, 0x01},
494a8df5af6SJacopo Mondi };
495a8df5af6SJacopo Mondi 
496a8df5af6SJacopo Mondi static struct regval_list ov5647_640x480_10bpp[] = {
497a8df5af6SJacopo Mondi 	{0x0100, 0x00},
498a8df5af6SJacopo Mondi 	{0x0103, 0x01},
499a8df5af6SJacopo Mondi 	{0x3035, 0x11},
500a8df5af6SJacopo Mondi 	{0x3036, 0x46},
501a8df5af6SJacopo Mondi 	{0x303c, 0x11},
502a8df5af6SJacopo Mondi 	{0x3821, 0x07},
503a8df5af6SJacopo Mondi 	{0x3820, 0x41},
504a8df5af6SJacopo Mondi 	{0x370c, 0x03},
505a8df5af6SJacopo Mondi 	{0x3612, 0x59},
506a8df5af6SJacopo Mondi 	{0x3618, 0x00},
507a8df5af6SJacopo Mondi 	{0x5000, 0x06},
508a8df5af6SJacopo Mondi 	{0x5003, 0x08},
509a8df5af6SJacopo Mondi 	{0x5a00, 0x08},
510a8df5af6SJacopo Mondi 	{0x3000, 0xff},
511a8df5af6SJacopo Mondi 	{0x3001, 0xff},
512a8df5af6SJacopo Mondi 	{0x3002, 0xff},
513a8df5af6SJacopo Mondi 	{0x301d, 0xf0},
514a8df5af6SJacopo Mondi 	{0x3a18, 0x00},
515a8df5af6SJacopo Mondi 	{0x3a19, 0xf8},
516a8df5af6SJacopo Mondi 	{0x3c01, 0x80},
517a8df5af6SJacopo Mondi 	{0x3b07, 0x0c},
518a8df5af6SJacopo Mondi 	{0x380c, 0x07},
519a8df5af6SJacopo Mondi 	{0x380d, 0x3c},
520a8df5af6SJacopo Mondi 	{0x3814, 0x35},
521a8df5af6SJacopo Mondi 	{0x3815, 0x35},
522a8df5af6SJacopo Mondi 	{0x3708, 0x64},
523a8df5af6SJacopo Mondi 	{0x3709, 0x52},
524a8df5af6SJacopo Mondi 	{0x3808, 0x02},
525a8df5af6SJacopo Mondi 	{0x3809, 0x80},
526a8df5af6SJacopo Mondi 	{0x380a, 0x01},
527a8df5af6SJacopo Mondi 	{0x380b, 0xe0},
528a8df5af6SJacopo Mondi 	{0x3800, 0x00},
529a8df5af6SJacopo Mondi 	{0x3801, 0x10},
530a8df5af6SJacopo Mondi 	{0x3802, 0x00},
531a8df5af6SJacopo Mondi 	{0x3803, 0x00},
532a8df5af6SJacopo Mondi 	{0x3804, 0x0a},
533a8df5af6SJacopo Mondi 	{0x3805, 0x2f},
534a8df5af6SJacopo Mondi 	{0x3806, 0x07},
535a8df5af6SJacopo Mondi 	{0x3807, 0x9f},
536a8df5af6SJacopo Mondi 	{0x3630, 0x2e},
537a8df5af6SJacopo Mondi 	{0x3632, 0xe2},
538a8df5af6SJacopo Mondi 	{0x3633, 0x23},
539a8df5af6SJacopo Mondi 	{0x3634, 0x44},
540a8df5af6SJacopo Mondi 	{0x3620, 0x64},
541a8df5af6SJacopo Mondi 	{0x3621, 0xe0},
542a8df5af6SJacopo Mondi 	{0x3600, 0x37},
543a8df5af6SJacopo Mondi 	{0x3704, 0xa0},
544a8df5af6SJacopo Mondi 	{0x3703, 0x5a},
545a8df5af6SJacopo Mondi 	{0x3715, 0x78},
546a8df5af6SJacopo Mondi 	{0x3717, 0x01},
547a8df5af6SJacopo Mondi 	{0x3731, 0x02},
548a8df5af6SJacopo Mondi 	{0x370b, 0x60},
549a8df5af6SJacopo Mondi 	{0x3705, 0x1a},
550a8df5af6SJacopo Mondi 	{0x3f05, 0x02},
551a8df5af6SJacopo Mondi 	{0x3f06, 0x10},
552a8df5af6SJacopo Mondi 	{0x3f01, 0x0a},
553a8df5af6SJacopo Mondi 	{0x3a08, 0x01},
554a8df5af6SJacopo Mondi 	{0x3a09, 0x2e},
555a8df5af6SJacopo Mondi 	{0x3a0a, 0x00},
556a8df5af6SJacopo Mondi 	{0x3a0b, 0xfb},
557a8df5af6SJacopo Mondi 	{0x3a0d, 0x02},
558a8df5af6SJacopo Mondi 	{0x3a0e, 0x01},
559a8df5af6SJacopo Mondi 	{0x3a0f, 0x58},
560a8df5af6SJacopo Mondi 	{0x3a10, 0x50},
561a8df5af6SJacopo Mondi 	{0x3a1b, 0x58},
562a8df5af6SJacopo Mondi 	{0x3a1e, 0x50},
563a8df5af6SJacopo Mondi 	{0x3a11, 0x60},
564a8df5af6SJacopo Mondi 	{0x3a1f, 0x28},
565a8df5af6SJacopo Mondi 	{0x4001, 0x02},
566a8df5af6SJacopo Mondi 	{0x4004, 0x02},
567a8df5af6SJacopo Mondi 	{0x4000, 0x09},
568a8df5af6SJacopo Mondi 	{0x3000, 0x00},
569a8df5af6SJacopo Mondi 	{0x3001, 0x00},
570a8df5af6SJacopo Mondi 	{0x3002, 0x00},
571a8df5af6SJacopo Mondi 	{0x3017, 0xe0},
572a8df5af6SJacopo Mondi 	{0x301c, 0xfc},
573a8df5af6SJacopo Mondi 	{0x3636, 0x06},
574a8df5af6SJacopo Mondi 	{0x3016, 0x08},
575a8df5af6SJacopo Mondi 	{0x3827, 0xec},
576a8df5af6SJacopo Mondi 	{0x3018, 0x44},
577a8df5af6SJacopo Mondi 	{0x3035, 0x21},
578a8df5af6SJacopo Mondi 	{0x3106, 0xf5},
579a8df5af6SJacopo Mondi 	{0x3034, 0x1a},
580a8df5af6SJacopo Mondi 	{0x301c, 0xf8},
581a8df5af6SJacopo Mondi 	{0x4800, 0x34},
582a8df5af6SJacopo Mondi 	{0x3503, 0x03},
583a8df5af6SJacopo Mondi 	{0x0100, 0x01},
584a8df5af6SJacopo Mondi };
585a8df5af6SJacopo Mondi 
586e907bd66SJacopo Mondi static const struct ov5647_mode ov5647_8bpp_modes[] = {
587e907bd66SJacopo Mondi 	/* 8-bit VGA mode: Uncentred crop 2x2 binned 1296x972 image. */
588d7d6074eSJacopo Mondi 	{
589d7d6074eSJacopo Mondi 		.format	= {
590d7d6074eSJacopo Mondi 			.code		= MEDIA_BUS_FMT_SBGGR8_1X8,
591d7d6074eSJacopo Mondi 			.colorspace	= V4L2_COLORSPACE_SRGB,
592d7d6074eSJacopo Mondi 			.field		= V4L2_FIELD_NONE,
593d7d6074eSJacopo Mondi 			.width		= 640,
594d7d6074eSJacopo Mondi 			.height		= 480
595d7d6074eSJacopo Mondi 		},
59614f70a32SDave Stevenson 		.crop = {
59714f70a32SDave Stevenson 			.left		= OV5647_PIXEL_ARRAY_LEFT,
59814f70a32SDave Stevenson 			.top		= OV5647_PIXEL_ARRAY_TOP,
59914f70a32SDave Stevenson 			.width		= 1280,
60014f70a32SDave Stevenson 			.height		= 960,
60114f70a32SDave Stevenson 		},
602911f4516SDave Stevenson 		.pixel_rate	= 77291670,
603c6da1ae4SJacopo Mondi 		.hts		= 1896,
6042512c064SDave Stevenson 		.vts		= 0x3d8,
605e907bd66SJacopo Mondi 		.reg_list	= ov5647_640x480_8bpp,
606e907bd66SJacopo Mondi 		.num_regs	= ARRAY_SIZE(ov5647_640x480_8bpp)
607d7d6074eSJacopo Mondi 	},
608d7d6074eSJacopo Mondi };
609d7d6074eSJacopo Mondi 
610a8df5af6SJacopo Mondi static const struct ov5647_mode ov5647_10bpp_modes[] = {
611a8df5af6SJacopo Mondi 	/* 2592x1944 full resolution full FOV 10-bit mode. */
612a8df5af6SJacopo Mondi 	{
613a8df5af6SJacopo Mondi 		.format = {
614a8df5af6SJacopo Mondi 			.code		= MEDIA_BUS_FMT_SBGGR10_1X10,
615a8df5af6SJacopo Mondi 			.colorspace	= V4L2_COLORSPACE_SRGB,
616a8df5af6SJacopo Mondi 			.field		= V4L2_FIELD_NONE,
617a8df5af6SJacopo Mondi 			.width		= 2592,
618a8df5af6SJacopo Mondi 			.height		= 1944
619a8df5af6SJacopo Mondi 		},
620a8df5af6SJacopo Mondi 		.crop = {
621a8df5af6SJacopo Mondi 			.left		= OV5647_PIXEL_ARRAY_LEFT,
622a8df5af6SJacopo Mondi 			.top		= OV5647_PIXEL_ARRAY_TOP,
623a8df5af6SJacopo Mondi 			.width		= 2592,
624a8df5af6SJacopo Mondi 			.height		= 1944
625a8df5af6SJacopo Mondi 		},
626911f4516SDave Stevenson 		.pixel_rate	= 87500000,
627c6da1ae4SJacopo Mondi 		.hts		= 2844,
6282512c064SDave Stevenson 		.vts		= 0x7b0,
629a8df5af6SJacopo Mondi 		.reg_list	= ov5647_2592x1944_10bpp,
630a8df5af6SJacopo Mondi 		.num_regs	= ARRAY_SIZE(ov5647_2592x1944_10bpp)
631a8df5af6SJacopo Mondi 	},
632a8df5af6SJacopo Mondi 	/* 1080p30 10-bit mode. Full resolution centre-cropped down to 1080p. */
633a8df5af6SJacopo Mondi 	{
634a8df5af6SJacopo Mondi 		.format = {
635a8df5af6SJacopo Mondi 			.code		= MEDIA_BUS_FMT_SBGGR10_1X10,
636a8df5af6SJacopo Mondi 			.colorspace	= V4L2_COLORSPACE_SRGB,
637a8df5af6SJacopo Mondi 			.field		= V4L2_FIELD_NONE,
638a8df5af6SJacopo Mondi 			.width		= 1920,
639a8df5af6SJacopo Mondi 			.height		= 1080
640a8df5af6SJacopo Mondi 		},
641a8df5af6SJacopo Mondi 		.crop = {
642a8df5af6SJacopo Mondi 			.left		= 348 + OV5647_PIXEL_ARRAY_LEFT,
643a8df5af6SJacopo Mondi 			.top		= 434 + OV5647_PIXEL_ARRAY_TOP,
644a8df5af6SJacopo Mondi 			.width		= 1928,
645a8df5af6SJacopo Mondi 			.height		= 1080,
646a8df5af6SJacopo Mondi 		},
647911f4516SDave Stevenson 		.pixel_rate	= 81666700,
648c6da1ae4SJacopo Mondi 		.hts		= 2416,
6492512c064SDave Stevenson 		.vts		= 0x450,
650a8df5af6SJacopo Mondi 		.reg_list	= ov5647_1080p30_10bpp,
651a8df5af6SJacopo Mondi 		.num_regs	= ARRAY_SIZE(ov5647_1080p30_10bpp)
652a8df5af6SJacopo Mondi 	},
653a8df5af6SJacopo Mondi 	/* 2x2 binned full FOV 10-bit mode. */
654a8df5af6SJacopo Mondi 	{
655a8df5af6SJacopo Mondi 		.format = {
656a8df5af6SJacopo Mondi 			.code		= MEDIA_BUS_FMT_SBGGR10_1X10,
657a8df5af6SJacopo Mondi 			.colorspace	= V4L2_COLORSPACE_SRGB,
658a8df5af6SJacopo Mondi 			.field		= V4L2_FIELD_NONE,
659a8df5af6SJacopo Mondi 			.width		= 1296,
660a8df5af6SJacopo Mondi 			.height		= 972
661a8df5af6SJacopo Mondi 		},
662a8df5af6SJacopo Mondi 		.crop = {
663a8df5af6SJacopo Mondi 			.left		= OV5647_PIXEL_ARRAY_LEFT,
664a8df5af6SJacopo Mondi 			.top		= OV5647_PIXEL_ARRAY_TOP,
665a8df5af6SJacopo Mondi 			.width		= 2592,
666a8df5af6SJacopo Mondi 			.height		= 1944,
667a8df5af6SJacopo Mondi 		},
668911f4516SDave Stevenson 		.pixel_rate	= 81666700,
669c6da1ae4SJacopo Mondi 		.hts		= 1896,
6702512c064SDave Stevenson 		.vts		= 0x59b,
671a8df5af6SJacopo Mondi 		.reg_list	= ov5647_2x2binned_10bpp,
672a8df5af6SJacopo Mondi 		.num_regs	= ARRAY_SIZE(ov5647_2x2binned_10bpp)
673a8df5af6SJacopo Mondi 	},
674a8df5af6SJacopo Mondi 	/* 10-bit VGA full FOV 60fps. 2x2 binned and subsampled down to VGA. */
675a8df5af6SJacopo Mondi 	{
676a8df5af6SJacopo Mondi 		.format = {
677a8df5af6SJacopo Mondi 			.code		= MEDIA_BUS_FMT_SBGGR10_1X10,
678a8df5af6SJacopo Mondi 			.colorspace	= V4L2_COLORSPACE_SRGB,
679a8df5af6SJacopo Mondi 			.field		= V4L2_FIELD_NONE,
680a8df5af6SJacopo Mondi 			.width		= 640,
681a8df5af6SJacopo Mondi 			.height		= 480
682a8df5af6SJacopo Mondi 		},
683a8df5af6SJacopo Mondi 		.crop = {
684a8df5af6SJacopo Mondi 			.left		= 16 + OV5647_PIXEL_ARRAY_LEFT,
685a8df5af6SJacopo Mondi 			.top		= OV5647_PIXEL_ARRAY_TOP,
686a8df5af6SJacopo Mondi 			.width		= 2560,
687a8df5af6SJacopo Mondi 			.height		= 1920,
688a8df5af6SJacopo Mondi 		},
689911f4516SDave Stevenson 		.pixel_rate	= 55000000,
690c6da1ae4SJacopo Mondi 		.hts		= 1852,
6912512c064SDave Stevenson 		.vts		= 0x1f8,
692a8df5af6SJacopo Mondi 		.reg_list	= ov5647_640x480_10bpp,
693a8df5af6SJacopo Mondi 		.num_regs	= ARRAY_SIZE(ov5647_640x480_10bpp)
694a8df5af6SJacopo Mondi 	},
695a8df5af6SJacopo Mondi };
696a8df5af6SJacopo Mondi 
697d7d6074eSJacopo Mondi static const struct ov5647_format_list ov5647_formats[] = {
698d7d6074eSJacopo Mondi 	{
699d7d6074eSJacopo Mondi 		.mbus_code	= MEDIA_BUS_FMT_SBGGR8_1X8,
700e907bd66SJacopo Mondi 		.modes		= ov5647_8bpp_modes,
701e907bd66SJacopo Mondi 		.num_modes	= ARRAY_SIZE(ov5647_8bpp_modes),
702d7d6074eSJacopo Mondi 	},
703a8df5af6SJacopo Mondi 	{
704a8df5af6SJacopo Mondi 		.mbus_code	= MEDIA_BUS_FMT_SBGGR10_1X10,
705a8df5af6SJacopo Mondi 		.modes		= ov5647_10bpp_modes,
706a8df5af6SJacopo Mondi 		.num_modes	= ARRAY_SIZE(ov5647_10bpp_modes),
707a8df5af6SJacopo Mondi 	},
708d7d6074eSJacopo Mondi };
709d7d6074eSJacopo Mondi 
710d7d6074eSJacopo Mondi #define OV5647_NUM_FORMATS	(ARRAY_SIZE(ov5647_formats))
711d7d6074eSJacopo Mondi 
71287576ac6SJacopo Mondi /* Default sensor mode is 2x2 binned 640x480 SBGGR10_1X10. */
71387576ac6SJacopo Mondi #define OV5647_DEFAULT_MODE	(&ov5647_formats[1].modes[3])
71487576ac6SJacopo Mondi #define OV5647_DEFAULT_FORMAT	(ov5647_formats[1].modes[3].format)
715d7d6074eSJacopo Mondi 
7162512c064SDave Stevenson static int ov5647_write16(struct v4l2_subdev *sd, u16 reg, u16 val)
7172512c064SDave Stevenson {
7182512c064SDave Stevenson 	unsigned char data[4] = { reg >> 8, reg & 0xff, val >> 8, val & 0xff};
7192512c064SDave Stevenson 	struct i2c_client *client = v4l2_get_subdevdata(sd);
7202512c064SDave Stevenson 	int ret;
7212512c064SDave Stevenson 
7222512c064SDave Stevenson 	ret = i2c_master_send(client, data, 4);
7232512c064SDave Stevenson 	if (ret < 0) {
7242512c064SDave Stevenson 		dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n",
7252512c064SDave Stevenson 			__func__, reg);
7262512c064SDave Stevenson 		return ret;
7272512c064SDave Stevenson 	}
7282512c064SDave Stevenson 
7292512c064SDave Stevenson 	return 0;
7302512c064SDave Stevenson }
7312512c064SDave Stevenson 
7323c2472a3SRamiro Oliveira static int ov5647_write(struct v4l2_subdev *sd, u16 reg, u8 val)
7333c2472a3SRamiro Oliveira {
7343c2472a3SRamiro Oliveira 	unsigned char data[3] = { reg >> 8, reg & 0xff, val};
7353c2472a3SRamiro Oliveira 	struct i2c_client *client = v4l2_get_subdevdata(sd);
736c9a05cecSJacopo Mondi 	int ret;
7373c2472a3SRamiro Oliveira 
7383c2472a3SRamiro Oliveira 	ret = i2c_master_send(client, data, 3);
7392b18cbcfSJacopo Mondi 	if (ret < 0) {
7403c2472a3SRamiro Oliveira 		dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n",
7413c2472a3SRamiro Oliveira 				__func__, reg);
7423c2472a3SRamiro Oliveira 		return ret;
7433c2472a3SRamiro Oliveira 	}
7443c2472a3SRamiro Oliveira 
7452b18cbcfSJacopo Mondi 	return 0;
7462b18cbcfSJacopo Mondi }
7472b18cbcfSJacopo Mondi 
7483c2472a3SRamiro Oliveira static int ov5647_read(struct v4l2_subdev *sd, u16 reg, u8 *val)
7493c2472a3SRamiro Oliveira {
7503c2472a3SRamiro Oliveira 	unsigned char data_w[2] = { reg >> 8, reg & 0xff };
7513c2472a3SRamiro Oliveira 	struct i2c_client *client = v4l2_get_subdevdata(sd);
752c9a05cecSJacopo Mondi 	int ret;
7533c2472a3SRamiro Oliveira 
7543c2472a3SRamiro Oliveira 	ret = i2c_master_send(client, data_w, 2);
7553c2472a3SRamiro Oliveira 	if (ret < 0) {
7563c2472a3SRamiro Oliveira 		dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n",
7573c2472a3SRamiro Oliveira 			__func__, reg);
7583c2472a3SRamiro Oliveira 		return ret;
7593c2472a3SRamiro Oliveira 	}
7603c2472a3SRamiro Oliveira 
7613c2472a3SRamiro Oliveira 	ret = i2c_master_recv(client, val, 1);
7622b18cbcfSJacopo Mondi 	if (ret < 0) {
7633c2472a3SRamiro Oliveira 		dev_dbg(&client->dev, "%s: i2c read error, reg: %x\n",
7643c2472a3SRamiro Oliveira 				__func__, reg);
7653c2472a3SRamiro Oliveira 		return ret;
7663c2472a3SRamiro Oliveira 	}
7673c2472a3SRamiro Oliveira 
7682b18cbcfSJacopo Mondi 	return 0;
7692b18cbcfSJacopo Mondi }
7702b18cbcfSJacopo Mondi 
7713c2472a3SRamiro Oliveira static int ov5647_write_array(struct v4l2_subdev *sd,
772d7d6074eSJacopo Mondi 			      const struct regval_list *regs, int array_size)
7733c2472a3SRamiro Oliveira {
7743c2472a3SRamiro Oliveira 	int i, ret;
7753c2472a3SRamiro Oliveira 
7763c2472a3SRamiro Oliveira 	for (i = 0; i < array_size; i++) {
7773c2472a3SRamiro Oliveira 		ret = ov5647_write(sd, regs[i].addr, regs[i].data);
7783c2472a3SRamiro Oliveira 		if (ret < 0)
7793c2472a3SRamiro Oliveira 			return ret;
7803c2472a3SRamiro Oliveira 	}
7813c2472a3SRamiro Oliveira 
7823c2472a3SRamiro Oliveira 	return 0;
7833c2472a3SRamiro Oliveira }
7843c2472a3SRamiro Oliveira 
7853c2472a3SRamiro Oliveira static int ov5647_set_virtual_channel(struct v4l2_subdev *sd, int channel)
7863c2472a3SRamiro Oliveira {
7873c2472a3SRamiro Oliveira 	u8 channel_id;
7883c2472a3SRamiro Oliveira 	int ret;
7893c2472a3SRamiro Oliveira 
790cef66734SJacob Chen 	ret = ov5647_read(sd, OV5647_REG_MIPI_CTRL14, &channel_id);
7913c2472a3SRamiro Oliveira 	if (ret < 0)
7923c2472a3SRamiro Oliveira 		return ret;
7933c2472a3SRamiro Oliveira 
7943c2472a3SRamiro Oliveira 	channel_id &= ~(3 << 6);
795c9a05cecSJacopo Mondi 
796c9a05cecSJacopo Mondi 	return ov5647_write(sd, OV5647_REG_MIPI_CTRL14,
797c9a05cecSJacopo Mondi 			    channel_id | (channel << 6));
7983c2472a3SRamiro Oliveira }
7993c2472a3SRamiro Oliveira 
800f7a70f9aSJacopo Mondi static int ov5647_set_mode(struct v4l2_subdev *sd)
801f7a70f9aSJacopo Mondi {
802f7a70f9aSJacopo Mondi 	struct i2c_client *client = v4l2_get_subdevdata(sd);
803d7d6074eSJacopo Mondi 	struct ov5647 *sensor = to_sensor(sd);
804f7a70f9aSJacopo Mondi 	u8 resetval, rdval;
805f7a70f9aSJacopo Mondi 	int ret;
806f7a70f9aSJacopo Mondi 
807f7a70f9aSJacopo Mondi 	ret = ov5647_read(sd, OV5647_SW_STANDBY, &rdval);
808f7a70f9aSJacopo Mondi 	if (ret < 0)
809f7a70f9aSJacopo Mondi 		return ret;
810f7a70f9aSJacopo Mondi 
811d7d6074eSJacopo Mondi 	ret = ov5647_write_array(sd, sensor->mode->reg_list,
812d7d6074eSJacopo Mondi 				 sensor->mode->num_regs);
813f7a70f9aSJacopo Mondi 	if (ret < 0) {
814f7a70f9aSJacopo Mondi 		dev_err(&client->dev, "write sensor default regs error\n");
815f7a70f9aSJacopo Mondi 		return ret;
816f7a70f9aSJacopo Mondi 	}
817f7a70f9aSJacopo Mondi 
818f7a70f9aSJacopo Mondi 	ret = ov5647_set_virtual_channel(sd, 0);
819f7a70f9aSJacopo Mondi 	if (ret < 0)
820f7a70f9aSJacopo Mondi 		return ret;
821f7a70f9aSJacopo Mondi 
822f7a70f9aSJacopo Mondi 	ret = ov5647_read(sd, OV5647_SW_STANDBY, &resetval);
823f7a70f9aSJacopo Mondi 	if (ret < 0)
824f7a70f9aSJacopo Mondi 		return ret;
825f7a70f9aSJacopo Mondi 
826f7a70f9aSJacopo Mondi 	if (!(resetval & 0x01)) {
827f7a70f9aSJacopo Mondi 		dev_err(&client->dev, "Device was in SW standby");
828f7a70f9aSJacopo Mondi 		ret = ov5647_write(sd, OV5647_SW_STANDBY, 0x01);
829f7a70f9aSJacopo Mondi 		if (ret < 0)
830f7a70f9aSJacopo Mondi 			return ret;
831f7a70f9aSJacopo Mondi 	}
832f7a70f9aSJacopo Mondi 
833f7a70f9aSJacopo Mondi 	return 0;
834f7a70f9aSJacopo Mondi }
835f7a70f9aSJacopo Mondi 
8363c2472a3SRamiro Oliveira static int ov5647_stream_on(struct v4l2_subdev *sd)
8373c2472a3SRamiro Oliveira {
838f7a70f9aSJacopo Mondi 	struct i2c_client *client = v4l2_get_subdevdata(sd);
8395bc5ca71SJacopo Mondi 	struct ov5647 *sensor = to_sensor(sd);
840dea4fcfeSDave Stevenson 	u8 val = MIPI_CTRL00_BUS_IDLE;
8413c2472a3SRamiro Oliveira 	int ret;
8423c2472a3SRamiro Oliveira 
843f7a70f9aSJacopo Mondi 	ret = ov5647_set_mode(sd);
844f7a70f9aSJacopo Mondi 	if (ret) {
845f7a70f9aSJacopo Mondi 		dev_err(&client->dev, "Failed to program sensor mode: %d\n", ret);
846f7a70f9aSJacopo Mondi 		return ret;
847f7a70f9aSJacopo Mondi 	}
848f7a70f9aSJacopo Mondi 
8494974c2f1SDavid Plowman 	/* Apply customized values from user when stream starts. */
8504974c2f1SDavid Plowman 	ret =  __v4l2_ctrl_handler_setup(sd->ctrl_handler);
8514974c2f1SDavid Plowman 	if (ret)
8524974c2f1SDavid Plowman 		return ret;
8534974c2f1SDavid Plowman 
8545bc5ca71SJacopo Mondi 	if (sensor->clock_ncont)
855dea4fcfeSDave Stevenson 		val |= MIPI_CTRL00_CLOCK_LANE_GATE |
856dea4fcfeSDave Stevenson 		       MIPI_CTRL00_LINE_SYNC_ENABLE;
857dea4fcfeSDave Stevenson 
858dea4fcfeSDave Stevenson 	ret = ov5647_write(sd, OV5647_REG_MIPI_CTRL00, val);
8597bc9f038SJacob Chen 	if (ret < 0)
8607bc9f038SJacob Chen 		return ret;
8617bc9f038SJacob Chen 
862cef66734SJacob Chen 	ret = ov5647_write(sd, OV5647_REG_FRAME_OFF_NUMBER, 0x00);
8633c2472a3SRamiro Oliveira 	if (ret < 0)
8643c2472a3SRamiro Oliveira 		return ret;
8653c2472a3SRamiro Oliveira 
866cef66734SJacob Chen 	return ov5647_write(sd, OV5640_REG_PAD_OUT, 0x00);
8673c2472a3SRamiro Oliveira }
8683c2472a3SRamiro Oliveira 
8693c2472a3SRamiro Oliveira static int ov5647_stream_off(struct v4l2_subdev *sd)
8703c2472a3SRamiro Oliveira {
8713c2472a3SRamiro Oliveira 	int ret;
8723c2472a3SRamiro Oliveira 
873c9a05cecSJacopo Mondi 	ret = ov5647_write(sd, OV5647_REG_MIPI_CTRL00,
874c9a05cecSJacopo Mondi 			   MIPI_CTRL00_CLOCK_LANE_GATE | MIPI_CTRL00_BUS_IDLE |
875c9a05cecSJacopo Mondi 			   MIPI_CTRL00_CLOCK_LANE_DISABLE);
8767bc9f038SJacob Chen 	if (ret < 0)
8777bc9f038SJacob Chen 		return ret;
8787bc9f038SJacob Chen 
879cef66734SJacob Chen 	ret = ov5647_write(sd, OV5647_REG_FRAME_OFF_NUMBER, 0x0f);
8803c2472a3SRamiro Oliveira 	if (ret < 0)
8813c2472a3SRamiro Oliveira 		return ret;
8823c2472a3SRamiro Oliveira 
883cef66734SJacob Chen 	return ov5647_write(sd, OV5640_REG_PAD_OUT, 0x01);
8843c2472a3SRamiro Oliveira }
8853c2472a3SRamiro Oliveira 
886089b7c70SJacopo Mondi static int ov5647_power_on(struct device *dev)
8873c2472a3SRamiro Oliveira {
888089b7c70SJacopo Mondi 	struct ov5647 *sensor = dev_get_drvdata(dev);
8893c2472a3SRamiro Oliveira 	int ret;
8903c2472a3SRamiro Oliveira 
891089b7c70SJacopo Mondi 	dev_dbg(dev, "OV5647 power on\n");
8923c2472a3SRamiro Oliveira 
8935bc5ca71SJacopo Mondi 	if (sensor->pwdn) {
8945bc5ca71SJacopo Mondi 		gpiod_set_value_cansleep(sensor->pwdn, 0);
895b050791dSDave Stevenson 		msleep(PWDN_ACTIVE_DELAY_MS);
896b050791dSDave Stevenson 	}
897b050791dSDave Stevenson 
8985bc5ca71SJacopo Mondi 	ret = clk_prepare_enable(sensor->xclk);
8993c2472a3SRamiro Oliveira 	if (ret < 0) {
900089b7c70SJacopo Mondi 		dev_err(dev, "clk prepare enable failed\n");
901089b7c70SJacopo Mondi 		goto error_pwdn;
9023c2472a3SRamiro Oliveira 	}
9033c2472a3SRamiro Oliveira 
904089b7c70SJacopo Mondi 	ret = ov5647_write_array(&sensor->sd, sensor_oe_enable_regs,
9053c2472a3SRamiro Oliveira 				 ARRAY_SIZE(sensor_oe_enable_regs));
9063c2472a3SRamiro Oliveira 	if (ret < 0) {
907089b7c70SJacopo Mondi 		dev_err(dev, "write sensor_oe_enable_regs error\n");
908089b7c70SJacopo Mondi 		goto error_clk_disable;
9093c2472a3SRamiro Oliveira 	}
9103c2472a3SRamiro Oliveira 
911f7a70f9aSJacopo Mondi 	/* Stream off to coax lanes into LP-11 state. */
912089b7c70SJacopo Mondi 	ret = ov5647_stream_off(&sensor->sd);
9133c2472a3SRamiro Oliveira 	if (ret < 0) {
914089b7c70SJacopo Mondi 		dev_err(dev, "camera not available, check power\n");
915089b7c70SJacopo Mondi 		goto error_clk_disable;
9163c2472a3SRamiro Oliveira 	}
9173c2472a3SRamiro Oliveira 
918089b7c70SJacopo Mondi 	return 0;
919089b7c70SJacopo Mondi 
920089b7c70SJacopo Mondi error_clk_disable:
921089b7c70SJacopo Mondi 	clk_disable_unprepare(sensor->xclk);
922089b7c70SJacopo Mondi error_pwdn:
923089b7c70SJacopo Mondi 	gpiod_set_value_cansleep(sensor->pwdn, 1);
924089b7c70SJacopo Mondi 
925089b7c70SJacopo Mondi 	return ret;
926089b7c70SJacopo Mondi }
927089b7c70SJacopo Mondi 
928089b7c70SJacopo Mondi static int ov5647_power_off(struct device *dev)
929089b7c70SJacopo Mondi {
930089b7c70SJacopo Mondi 	struct ov5647 *sensor = dev_get_drvdata(dev);
931089b7c70SJacopo Mondi 	u8 rdval;
932089b7c70SJacopo Mondi 	int ret;
933089b7c70SJacopo Mondi 
934089b7c70SJacopo Mondi 	dev_dbg(dev, "OV5647 power off\n");
935089b7c70SJacopo Mondi 
936089b7c70SJacopo Mondi 	ret = ov5647_write_array(&sensor->sd, sensor_oe_disable_regs,
9373c2472a3SRamiro Oliveira 				 ARRAY_SIZE(sensor_oe_disable_regs));
9383c2472a3SRamiro Oliveira 	if (ret < 0)
939089b7c70SJacopo Mondi 		dev_dbg(dev, "disable oe failed\n");
9403c2472a3SRamiro Oliveira 
941089b7c70SJacopo Mondi 	/* Enter software standby */
942089b7c70SJacopo Mondi 	ret = ov5647_read(&sensor->sd, OV5647_SW_STANDBY, &rdval);
9433c2472a3SRamiro Oliveira 	if (ret < 0)
944089b7c70SJacopo Mondi 		dev_dbg(dev, "software standby failed\n");
945089b7c70SJacopo Mondi 
946089b7c70SJacopo Mondi 	rdval &= ~0x01;
947089b7c70SJacopo Mondi 	ret = ov5647_write(&sensor->sd, OV5647_SW_STANDBY, rdval);
948089b7c70SJacopo Mondi 	if (ret < 0)
949089b7c70SJacopo Mondi 		dev_dbg(dev, "software standby failed\n");
9503c2472a3SRamiro Oliveira 
9515bc5ca71SJacopo Mondi 	clk_disable_unprepare(sensor->xclk);
9525bc5ca71SJacopo Mondi 	gpiod_set_value_cansleep(sensor->pwdn, 1);
9533c2472a3SRamiro Oliveira 
954089b7c70SJacopo Mondi 	return 0;
9553c2472a3SRamiro Oliveira }
9563c2472a3SRamiro Oliveira 
9573c2472a3SRamiro Oliveira #ifdef CONFIG_VIDEO_ADV_DEBUG
9583c2472a3SRamiro Oliveira static int ov5647_sensor_get_register(struct v4l2_subdev *sd,
9593c2472a3SRamiro Oliveira 				      struct v4l2_dbg_register *reg)
9603c2472a3SRamiro Oliveira {
9613c2472a3SRamiro Oliveira 	int ret;
962c9a05cecSJacopo Mondi 	u8 val;
9633c2472a3SRamiro Oliveira 
9643c2472a3SRamiro Oliveira 	ret = ov5647_read(sd, reg->reg & 0xff, &val);
9653c2472a3SRamiro Oliveira 	if (ret < 0)
9663c2472a3SRamiro Oliveira 		return ret;
9673c2472a3SRamiro Oliveira 
9683c2472a3SRamiro Oliveira 	reg->val = val;
9693c2472a3SRamiro Oliveira 	reg->size = 1;
9703c2472a3SRamiro Oliveira 
9713c2472a3SRamiro Oliveira 	return 0;
9723c2472a3SRamiro Oliveira }
9733c2472a3SRamiro Oliveira 
9743c2472a3SRamiro Oliveira static int ov5647_sensor_set_register(struct v4l2_subdev *sd,
9753c2472a3SRamiro Oliveira 				      const struct v4l2_dbg_register *reg)
9763c2472a3SRamiro Oliveira {
9773c2472a3SRamiro Oliveira 	return ov5647_write(sd, reg->reg & 0xff, reg->val & 0xff);
9783c2472a3SRamiro Oliveira }
9793c2472a3SRamiro Oliveira #endif
9803c2472a3SRamiro Oliveira 
981c9a05cecSJacopo Mondi /* Subdev core operations registration */
9823c2472a3SRamiro Oliveira static const struct v4l2_subdev_core_ops ov5647_subdev_core_ops = {
9833c2472a3SRamiro Oliveira #ifdef CONFIG_VIDEO_ADV_DEBUG
9843c2472a3SRamiro Oliveira 	.g_register		= ov5647_sensor_get_register,
9853c2472a3SRamiro Oliveira 	.s_register		= ov5647_sensor_set_register,
9863c2472a3SRamiro Oliveira #endif
9873c2472a3SRamiro Oliveira };
9883c2472a3SRamiro Oliveira 
98914f70a32SDave Stevenson static const struct v4l2_rect *
99014f70a32SDave Stevenson __ov5647_get_pad_crop(struct ov5647 *ov5647, struct v4l2_subdev_pad_config *cfg,
99114f70a32SDave Stevenson 		      unsigned int pad, enum v4l2_subdev_format_whence which)
99214f70a32SDave Stevenson {
99314f70a32SDave Stevenson 	switch (which) {
99414f70a32SDave Stevenson 	case V4L2_SUBDEV_FORMAT_TRY:
99514f70a32SDave Stevenson 		return v4l2_subdev_get_try_crop(&ov5647->sd, cfg, pad);
99614f70a32SDave Stevenson 	case V4L2_SUBDEV_FORMAT_ACTIVE:
99714f70a32SDave Stevenson 		return &ov5647->mode->crop;
99814f70a32SDave Stevenson 	}
99914f70a32SDave Stevenson 
100014f70a32SDave Stevenson 	return NULL;
100114f70a32SDave Stevenson }
100214f70a32SDave Stevenson 
10033c2472a3SRamiro Oliveira static int ov5647_s_stream(struct v4l2_subdev *sd, int enable)
10043c2472a3SRamiro Oliveira {
1005*2f038c97SJacopo Mondi 	struct i2c_client *client = v4l2_get_subdevdata(sd);
10065bc5ca71SJacopo Mondi 	struct ov5647 *sensor = to_sensor(sd);
1007ab614f27SJacopo Mondi 	int ret;
1008ab614f27SJacopo Mondi 
1009ab614f27SJacopo Mondi 	mutex_lock(&sensor->lock);
1010*2f038c97SJacopo Mondi 	if (sensor->streaming == enable) {
1011*2f038c97SJacopo Mondi 		mutex_unlock(&sensor->lock);
1012*2f038c97SJacopo Mondi 		return 0;
1013*2f038c97SJacopo Mondi 	}
1014*2f038c97SJacopo Mondi 
1015*2f038c97SJacopo Mondi 	if (enable) {
1016*2f038c97SJacopo Mondi 		ret = pm_runtime_get_sync(&client->dev);
1017*2f038c97SJacopo Mondi 		if (ret < 0)
1018*2f038c97SJacopo Mondi 			goto error_unlock;
1019*2f038c97SJacopo Mondi 
1020ab614f27SJacopo Mondi 		ret = ov5647_stream_on(sd);
1021*2f038c97SJacopo Mondi 		if (ret < 0) {
1022*2f038c97SJacopo Mondi 			dev_err(&client->dev, "stream start failed: %d\n", ret);
1023*2f038c97SJacopo Mondi 			goto error_unlock;
1024*2f038c97SJacopo Mondi 		}
1025*2f038c97SJacopo Mondi 	} else {
1026ab614f27SJacopo Mondi 		ret = ov5647_stream_off(sd);
1027*2f038c97SJacopo Mondi 		if (ret < 0) {
1028*2f038c97SJacopo Mondi 			dev_err(&client->dev, "stream stop failed: %d\n", ret);
1029*2f038c97SJacopo Mondi 			goto error_unlock;
1030*2f038c97SJacopo Mondi 		}
1031*2f038c97SJacopo Mondi 		pm_runtime_put(&client->dev);
1032*2f038c97SJacopo Mondi 	}
1033*2f038c97SJacopo Mondi 
1034*2f038c97SJacopo Mondi 	sensor->streaming = enable;
1035*2f038c97SJacopo Mondi 	mutex_unlock(&sensor->lock);
1036*2f038c97SJacopo Mondi 
1037*2f038c97SJacopo Mondi 	return 0;
1038*2f038c97SJacopo Mondi 
1039*2f038c97SJacopo Mondi error_unlock:
1040*2f038c97SJacopo Mondi 	pm_runtime_put(&client->dev);
1041ab614f27SJacopo Mondi 	mutex_unlock(&sensor->lock);
1042ab614f27SJacopo Mondi 
1043ab614f27SJacopo Mondi 	return ret;
10443c2472a3SRamiro Oliveira }
10453c2472a3SRamiro Oliveira 
10463c2472a3SRamiro Oliveira static const struct v4l2_subdev_video_ops ov5647_subdev_video_ops = {
10473c2472a3SRamiro Oliveira 	.s_stream =		ov5647_s_stream,
10483c2472a3SRamiro Oliveira };
10493c2472a3SRamiro Oliveira 
10503c2472a3SRamiro Oliveira static int ov5647_enum_mbus_code(struct v4l2_subdev *sd,
10513c2472a3SRamiro Oliveira 				 struct v4l2_subdev_pad_config *cfg,
10523c2472a3SRamiro Oliveira 				 struct v4l2_subdev_mbus_code_enum *code)
10533c2472a3SRamiro Oliveira {
1054d7d6074eSJacopo Mondi 	if (code->index >= OV5647_NUM_FORMATS)
10553c2472a3SRamiro Oliveira 		return -EINVAL;
10563c2472a3SRamiro Oliveira 
1057d7d6074eSJacopo Mondi 	code->code = ov5647_formats[code->index].mbus_code;
10583c2472a3SRamiro Oliveira 
10593c2472a3SRamiro Oliveira 	return 0;
10603c2472a3SRamiro Oliveira }
10613c2472a3SRamiro Oliveira 
1062464090c0SJacopo Mondi static int ov5647_enum_frame_size(struct v4l2_subdev *sd,
1063464090c0SJacopo Mondi 				  struct v4l2_subdev_pad_config *cfg,
1064464090c0SJacopo Mondi 				  struct v4l2_subdev_frame_size_enum *fse)
1065464090c0SJacopo Mondi {
1066d7d6074eSJacopo Mondi 	const struct v4l2_mbus_framefmt *fmt;
1067d7d6074eSJacopo Mondi 	unsigned int i = 0;
1068d7d6074eSJacopo Mondi 
1069d7d6074eSJacopo Mondi 	for (; i < OV5647_NUM_FORMATS; ++i) {
1070d7d6074eSJacopo Mondi 		if (ov5647_formats[i].mbus_code == fse->code)
1071d7d6074eSJacopo Mondi 			break;
1072d7d6074eSJacopo Mondi 	}
1073d7d6074eSJacopo Mondi 	if (i == OV5647_NUM_FORMATS)
1074464090c0SJacopo Mondi 		return -EINVAL;
1075464090c0SJacopo Mondi 
1076d7d6074eSJacopo Mondi 	if (fse->index >= ov5647_formats[i].num_modes)
1077464090c0SJacopo Mondi 		return -EINVAL;
1078464090c0SJacopo Mondi 
1079d7d6074eSJacopo Mondi 	fmt = &ov5647_formats[i].modes[fse->index].format;
1080d7d6074eSJacopo Mondi 	fse->min_width = fmt->width;
1081d7d6074eSJacopo Mondi 	fse->max_width = fmt->width;
1082d7d6074eSJacopo Mondi 	fse->min_height = fmt->height;
1083d7d6074eSJacopo Mondi 	fse->max_height = fmt->height;
1084464090c0SJacopo Mondi 
1085464090c0SJacopo Mondi 	return 0;
1086464090c0SJacopo Mondi }
1087464090c0SJacopo Mondi 
10886869e971SJacopo Mondi static int ov5647_get_pad_fmt(struct v4l2_subdev *sd,
10890f87233aSDave Stevenson 			      struct v4l2_subdev_pad_config *cfg,
10900f87233aSDave Stevenson 			      struct v4l2_subdev_format *format)
10910f87233aSDave Stevenson {
10920f87233aSDave Stevenson 	struct v4l2_mbus_framefmt *fmt = &format->format;
10936869e971SJacopo Mondi 	const struct v4l2_mbus_framefmt *sensor_format;
10946869e971SJacopo Mondi 	struct ov5647 *sensor = to_sensor(sd);
10950f87233aSDave Stevenson 
10966869e971SJacopo Mondi 	mutex_lock(&sensor->lock);
10976869e971SJacopo Mondi 	switch (format->which) {
10986869e971SJacopo Mondi 	case V4L2_SUBDEV_FORMAT_TRY:
10996869e971SJacopo Mondi 		sensor_format = v4l2_subdev_get_try_format(sd, cfg, format->pad);
11006869e971SJacopo Mondi 		break;
11016869e971SJacopo Mondi 	default:
11026869e971SJacopo Mondi 		sensor_format = &sensor->mode->format;
11036869e971SJacopo Mondi 		break;
11046869e971SJacopo Mondi 	}
11056869e971SJacopo Mondi 
11066869e971SJacopo Mondi 	*fmt = *sensor_format;
11076869e971SJacopo Mondi 	mutex_unlock(&sensor->lock);
11086869e971SJacopo Mondi 
11096869e971SJacopo Mondi 	return 0;
11106869e971SJacopo Mondi }
11116869e971SJacopo Mondi 
11126869e971SJacopo Mondi static int ov5647_set_pad_fmt(struct v4l2_subdev *sd,
11136869e971SJacopo Mondi 			      struct v4l2_subdev_pad_config *cfg,
11146869e971SJacopo Mondi 			      struct v4l2_subdev_format *format)
11156869e971SJacopo Mondi {
11166869e971SJacopo Mondi 	struct v4l2_mbus_framefmt *fmt = &format->format;
11176869e971SJacopo Mondi 	const struct ov5647_mode *ov5647_mode_list;
11186869e971SJacopo Mondi 	struct ov5647 *sensor = to_sensor(sd);
1119646a0249SDave Stevenson 
11206869e971SJacopo Mondi 	const struct ov5647_mode *mode;
11216869e971SJacopo Mondi 	unsigned int num_modes;
11226869e971SJacopo Mondi 	unsigned int i;
11236869e971SJacopo Mondi 
11246869e971SJacopo Mondi 	for (i = 0; i < OV5647_NUM_FORMATS; ++i) {
11256869e971SJacopo Mondi 		if (ov5647_formats[i].mbus_code != fmt->code)
11266869e971SJacopo Mondi 			continue;
11276869e971SJacopo Mondi 
11286869e971SJacopo Mondi 		ov5647_mode_list = ov5647_formats[i].modes;
11296869e971SJacopo Mondi 		num_modes = ov5647_formats[i].num_modes;
11306869e971SJacopo Mondi 		break;
11316869e971SJacopo Mondi 	}
11326869e971SJacopo Mondi 
11336869e971SJacopo Mondi 	/*
11346869e971SJacopo Mondi 	 * Default mbus code MEDIA_BUS_FMT_SBGGR10_1X10 if the requested one is
11356869e971SJacopo Mondi 	 * not supported.
11366869e971SJacopo Mondi 	 */
11376869e971SJacopo Mondi 	if (i == OV5647_NUM_FORMATS) {
11386869e971SJacopo Mondi 		ov5647_mode_list = ov5647_10bpp_modes;
11396869e971SJacopo Mondi 		num_modes = ARRAY_SIZE(ov5647_10bpp_modes);
11406869e971SJacopo Mondi 	}
11416869e971SJacopo Mondi 
11426869e971SJacopo Mondi 	mode = v4l2_find_nearest_size(ov5647_mode_list, num_modes,
11436869e971SJacopo Mondi 				      format.width, format.height,
11446869e971SJacopo Mondi 				      fmt->width, fmt->height);
11456869e971SJacopo Mondi 
11466869e971SJacopo Mondi 	/* Update the sensor mode and apply at it at streamon time. */
11476869e971SJacopo Mondi 	mutex_lock(&sensor->lock);
1148911f4516SDave Stevenson 	if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
11496869e971SJacopo Mondi 		*v4l2_subdev_get_try_format(sd, cfg, format->pad) = mode->format;
1150911f4516SDave Stevenson 	} else {
1151646a0249SDave Stevenson 		int exposure_max, exposure_def;
11522512c064SDave Stevenson 		int hblank, vblank;
1153c6da1ae4SJacopo Mondi 
11546869e971SJacopo Mondi 		sensor->mode = mode;
1155911f4516SDave Stevenson 		__v4l2_ctrl_modify_range(sensor->pixel_rate, mode->pixel_rate,
1156911f4516SDave Stevenson 					 mode->pixel_rate, 1, mode->pixel_rate);
1157c6da1ae4SJacopo Mondi 
1158c6da1ae4SJacopo Mondi 		hblank = mode->hts - mode->format.width;
1159c6da1ae4SJacopo Mondi 		__v4l2_ctrl_modify_range(sensor->hblank, hblank, hblank, 1,
1160c6da1ae4SJacopo Mondi 					 hblank);
11612512c064SDave Stevenson 
11622512c064SDave Stevenson 		vblank = mode->vts - mode->format.height;
11632512c064SDave Stevenson 		__v4l2_ctrl_modify_range(sensor->vblank, OV5647_VBLANK_MIN,
11642512c064SDave Stevenson 					 OV5647_VTS_MAX - mode->format.height,
11652512c064SDave Stevenson 					 1, vblank);
11662512c064SDave Stevenson 		__v4l2_ctrl_s_ctrl(sensor->vblank, vblank);
1167646a0249SDave Stevenson 
1168646a0249SDave Stevenson 		exposure_max = mode->vts - 4;
1169646a0249SDave Stevenson 		exposure_def = min(exposure_max, OV5647_EXPOSURE_DEFAULT);
1170646a0249SDave Stevenson 		__v4l2_ctrl_modify_range(sensor->exposure,
1171646a0249SDave Stevenson 					 sensor->exposure->minimum,
1172646a0249SDave Stevenson 					 exposure_max, sensor->exposure->step,
1173646a0249SDave Stevenson 					 exposure_def);
1174911f4516SDave Stevenson 	}
11756869e971SJacopo Mondi 	*fmt = mode->format;
11766869e971SJacopo Mondi 	mutex_unlock(&sensor->lock);
11770f87233aSDave Stevenson 
11780f87233aSDave Stevenson 	return 0;
11790f87233aSDave Stevenson }
11800f87233aSDave Stevenson 
118114f70a32SDave Stevenson static int ov5647_get_selection(struct v4l2_subdev *sd,
118214f70a32SDave Stevenson 				struct v4l2_subdev_pad_config *cfg,
118314f70a32SDave Stevenson 				struct v4l2_subdev_selection *sel)
118414f70a32SDave Stevenson {
118514f70a32SDave Stevenson 	switch (sel->target) {
118614f70a32SDave Stevenson 	case V4L2_SEL_TGT_CROP: {
118714f70a32SDave Stevenson 		struct ov5647 *sensor = to_sensor(sd);
118814f70a32SDave Stevenson 
118914f70a32SDave Stevenson 		mutex_lock(&sensor->lock);
119014f70a32SDave Stevenson 		sel->r = *__ov5647_get_pad_crop(sensor, cfg, sel->pad,
119114f70a32SDave Stevenson 						sel->which);
119214f70a32SDave Stevenson 		mutex_unlock(&sensor->lock);
119314f70a32SDave Stevenson 
119414f70a32SDave Stevenson 		return 0;
119514f70a32SDave Stevenson 	}
119614f70a32SDave Stevenson 
119714f70a32SDave Stevenson 	case V4L2_SEL_TGT_NATIVE_SIZE:
119814f70a32SDave Stevenson 		sel->r.top = 0;
119914f70a32SDave Stevenson 		sel->r.left = 0;
120014f70a32SDave Stevenson 		sel->r.width = OV5647_NATIVE_WIDTH;
120114f70a32SDave Stevenson 		sel->r.height = OV5647_NATIVE_HEIGHT;
120214f70a32SDave Stevenson 
120314f70a32SDave Stevenson 		return 0;
120414f70a32SDave Stevenson 
120514f70a32SDave Stevenson 	case V4L2_SEL_TGT_CROP_DEFAULT:
120614f70a32SDave Stevenson 	case V4L2_SEL_TGT_CROP_BOUNDS:
120714f70a32SDave Stevenson 		sel->r.top = OV5647_PIXEL_ARRAY_TOP;
120814f70a32SDave Stevenson 		sel->r.left = OV5647_PIXEL_ARRAY_LEFT;
120914f70a32SDave Stevenson 		sel->r.width = OV5647_PIXEL_ARRAY_WIDTH;
121014f70a32SDave Stevenson 		sel->r.height = OV5647_PIXEL_ARRAY_HEIGHT;
121114f70a32SDave Stevenson 
121214f70a32SDave Stevenson 		return 0;
121314f70a32SDave Stevenson 	}
121414f70a32SDave Stevenson 
121514f70a32SDave Stevenson 	return -EINVAL;
121614f70a32SDave Stevenson }
121714f70a32SDave Stevenson 
12183c2472a3SRamiro Oliveira static const struct v4l2_subdev_pad_ops ov5647_subdev_pad_ops = {
12193c2472a3SRamiro Oliveira 	.enum_mbus_code		= ov5647_enum_mbus_code,
1220464090c0SJacopo Mondi 	.enum_frame_size	= ov5647_enum_frame_size,
12216869e971SJacopo Mondi 	.set_fmt		= ov5647_set_pad_fmt,
12226869e971SJacopo Mondi 	.get_fmt		= ov5647_get_pad_fmt,
122314f70a32SDave Stevenson 	.get_selection		= ov5647_get_selection,
12243c2472a3SRamiro Oliveira };
12253c2472a3SRamiro Oliveira 
12263c2472a3SRamiro Oliveira static const struct v4l2_subdev_ops ov5647_subdev_ops = {
12273c2472a3SRamiro Oliveira 	.core		= &ov5647_subdev_core_ops,
12283c2472a3SRamiro Oliveira 	.video		= &ov5647_subdev_video_ops,
12293c2472a3SRamiro Oliveira 	.pad		= &ov5647_subdev_pad_ops,
12303c2472a3SRamiro Oliveira };
12313c2472a3SRamiro Oliveira 
12323c2472a3SRamiro Oliveira static int ov5647_detect(struct v4l2_subdev *sd)
12333c2472a3SRamiro Oliveira {
1234c9a05cecSJacopo Mondi 	struct i2c_client *client = v4l2_get_subdevdata(sd);
12353c2472a3SRamiro Oliveira 	u8 read;
12363c2472a3SRamiro Oliveira 	int ret;
12373c2472a3SRamiro Oliveira 
12383c2472a3SRamiro Oliveira 	ret = ov5647_write(sd, OV5647_SW_RESET, 0x01);
12393c2472a3SRamiro Oliveira 	if (ret < 0)
12403c2472a3SRamiro Oliveira 		return ret;
12413c2472a3SRamiro Oliveira 
12423c2472a3SRamiro Oliveira 	ret = ov5647_read(sd, OV5647_REG_CHIPID_H, &read);
12433c2472a3SRamiro Oliveira 	if (ret < 0)
12443c2472a3SRamiro Oliveira 		return ret;
12453c2472a3SRamiro Oliveira 
12463c2472a3SRamiro Oliveira 	if (read != 0x56) {
12473c2472a3SRamiro Oliveira 		dev_err(&client->dev, "ID High expected 0x56 got %x", read);
12483c2472a3SRamiro Oliveira 		return -ENODEV;
12493c2472a3SRamiro Oliveira 	}
12503c2472a3SRamiro Oliveira 
12513c2472a3SRamiro Oliveira 	ret = ov5647_read(sd, OV5647_REG_CHIPID_L, &read);
12523c2472a3SRamiro Oliveira 	if (ret < 0)
12533c2472a3SRamiro Oliveira 		return ret;
12543c2472a3SRamiro Oliveira 
12553c2472a3SRamiro Oliveira 	if (read != 0x47) {
12563c2472a3SRamiro Oliveira 		dev_err(&client->dev, "ID Low expected 0x47 got %x", read);
12573c2472a3SRamiro Oliveira 		return -ENODEV;
12583c2472a3SRamiro Oliveira 	}
12593c2472a3SRamiro Oliveira 
12603c2472a3SRamiro Oliveira 	return ov5647_write(sd, OV5647_SW_RESET, 0x00);
12613c2472a3SRamiro Oliveira }
12623c2472a3SRamiro Oliveira 
12633c2472a3SRamiro Oliveira static int ov5647_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
12643c2472a3SRamiro Oliveira {
12653c2472a3SRamiro Oliveira 	struct v4l2_mbus_framefmt *format =
12663c2472a3SRamiro Oliveira 				v4l2_subdev_get_try_format(sd, fh->pad, 0);
1267c9a05cecSJacopo Mondi 	struct v4l2_rect *crop = v4l2_subdev_get_try_crop(sd, fh->pad, 0);
12683c2472a3SRamiro Oliveira 
126914f70a32SDave Stevenson 	crop->left = OV5647_PIXEL_ARRAY_LEFT;
127014f70a32SDave Stevenson 	crop->top = OV5647_PIXEL_ARRAY_TOP;
127114f70a32SDave Stevenson 	crop->width = OV5647_PIXEL_ARRAY_WIDTH;
127214f70a32SDave Stevenson 	crop->height = OV5647_PIXEL_ARRAY_HEIGHT;
12733c2472a3SRamiro Oliveira 
1274d7d6074eSJacopo Mondi 	*format = OV5647_DEFAULT_FORMAT;
12753c2472a3SRamiro Oliveira 
12763c2472a3SRamiro Oliveira 	return 0;
12773c2472a3SRamiro Oliveira }
12783c2472a3SRamiro Oliveira 
12793c2472a3SRamiro Oliveira static const struct v4l2_subdev_internal_ops ov5647_subdev_internal_ops = {
12803c2472a3SRamiro Oliveira 	.open = ov5647_open,
12813c2472a3SRamiro Oliveira };
12823c2472a3SRamiro Oliveira 
12834974c2f1SDavid Plowman static int ov5647_s_auto_white_balance(struct v4l2_subdev *sd, u32 val)
12844974c2f1SDavid Plowman {
12854974c2f1SDavid Plowman 	return ov5647_write(sd, OV5647_REG_AWB, val ? 1 : 0);
12864974c2f1SDavid Plowman }
12874974c2f1SDavid Plowman 
12884974c2f1SDavid Plowman static int ov5647_s_autogain(struct v4l2_subdev *sd, u32 val)
12894974c2f1SDavid Plowman {
12904974c2f1SDavid Plowman 	int ret;
12914974c2f1SDavid Plowman 	u8 reg;
12924974c2f1SDavid Plowman 
12934974c2f1SDavid Plowman 	/* Non-zero turns on AGC by clearing bit 1.*/
12944974c2f1SDavid Plowman 	ret = ov5647_read(sd, OV5647_REG_AEC_AGC, &reg);
12954974c2f1SDavid Plowman 	if (ret)
12964974c2f1SDavid Plowman 		return ret;
12974974c2f1SDavid Plowman 
12984974c2f1SDavid Plowman 	return ov5647_write(sd, OV5647_REG_AEC_AGC, val ? reg & ~BIT(1)
12994974c2f1SDavid Plowman 							: reg | BIT(1));
13004974c2f1SDavid Plowman }
13014974c2f1SDavid Plowman 
13024974c2f1SDavid Plowman static int ov5647_s_exposure_auto(struct v4l2_subdev *sd, u32 val)
13034974c2f1SDavid Plowman {
13044974c2f1SDavid Plowman 	int ret;
13054974c2f1SDavid Plowman 	u8 reg;
13064974c2f1SDavid Plowman 
13074974c2f1SDavid Plowman 	/*
13084974c2f1SDavid Plowman 	 * Everything except V4L2_EXPOSURE_MANUAL turns on AEC by
13094974c2f1SDavid Plowman 	 * clearing bit 0.
13104974c2f1SDavid Plowman 	 */
13114974c2f1SDavid Plowman 	ret = ov5647_read(sd, OV5647_REG_AEC_AGC, &reg);
13124974c2f1SDavid Plowman 	if (ret)
13134974c2f1SDavid Plowman 		return ret;
13144974c2f1SDavid Plowman 
13154974c2f1SDavid Plowman 	return ov5647_write(sd, OV5647_REG_AEC_AGC,
13164974c2f1SDavid Plowman 			    val == V4L2_EXPOSURE_MANUAL ? reg | BIT(0)
13174974c2f1SDavid Plowman 							: reg & ~BIT(0));
13184974c2f1SDavid Plowman }
13194974c2f1SDavid Plowman 
13204974c2f1SDavid Plowman static int ov5647_s_analogue_gain(struct v4l2_subdev *sd, u32 val)
13214974c2f1SDavid Plowman {
13224974c2f1SDavid Plowman 	int ret;
13234974c2f1SDavid Plowman 
13244974c2f1SDavid Plowman 	/* 10 bits of gain, 2 in the high register. */
13254974c2f1SDavid Plowman 	ret = ov5647_write(sd, OV5647_REG_GAIN_HI, (val >> 8) & 3);
13264974c2f1SDavid Plowman 	if (ret)
13274974c2f1SDavid Plowman 		return ret;
13284974c2f1SDavid Plowman 
13294974c2f1SDavid Plowman 	return ov5647_write(sd, OV5647_REG_GAIN_LO, val & 0xff);
13304974c2f1SDavid Plowman }
13314974c2f1SDavid Plowman 
13324974c2f1SDavid Plowman static int ov5647_s_exposure(struct v4l2_subdev *sd, u32 val)
13334974c2f1SDavid Plowman {
13344974c2f1SDavid Plowman 	int ret;
13354974c2f1SDavid Plowman 
13364974c2f1SDavid Plowman 	/*
13374974c2f1SDavid Plowman 	 * Sensor has 20 bits, but the bottom 4 bits are fractions of a line
13384974c2f1SDavid Plowman 	 * which we leave as zero (and don't receive in "val").
13394974c2f1SDavid Plowman 	 */
13404974c2f1SDavid Plowman 	ret = ov5647_write(sd, OV5647_REG_EXP_HI, (val >> 12) & 0xf);
13414974c2f1SDavid Plowman 	if (ret)
13424974c2f1SDavid Plowman 		return ret;
13434974c2f1SDavid Plowman 
13444974c2f1SDavid Plowman 	ret = ov5647_write(sd, OV5647_REG_EXP_MID, (val >> 4) & 0xff);
13454974c2f1SDavid Plowman 	if (ret)
13464974c2f1SDavid Plowman 		return ret;
13474974c2f1SDavid Plowman 
13484974c2f1SDavid Plowman 	return ov5647_write(sd, OV5647_REG_EXP_LO, (val & 0xf) << 4);
13494974c2f1SDavid Plowman }
13504974c2f1SDavid Plowman 
13514974c2f1SDavid Plowman static int ov5647_s_ctrl(struct v4l2_ctrl *ctrl)
13524974c2f1SDavid Plowman {
13534974c2f1SDavid Plowman 	struct ov5647 *sensor = container_of(ctrl->handler,
13544974c2f1SDavid Plowman 					    struct ov5647, ctrls);
13554974c2f1SDavid Plowman 	struct v4l2_subdev *sd = &sensor->sd;
13564974c2f1SDavid Plowman 	struct i2c_client *client = v4l2_get_subdevdata(sd);
13574974c2f1SDavid Plowman 
13584974c2f1SDavid Plowman 	/* v4l2_ctrl_lock() locks our own mutex */
13594974c2f1SDavid Plowman 
1360646a0249SDave Stevenson 	if (ctrl->id == V4L2_CID_VBLANK) {
1361646a0249SDave Stevenson 		int exposure_max, exposure_def;
1362646a0249SDave Stevenson 
1363646a0249SDave Stevenson 		/* Update max exposure while meeting expected vblanking */
1364646a0249SDave Stevenson 		exposure_max = sensor->mode->format.height + ctrl->val - 4;
1365646a0249SDave Stevenson 		exposure_def = min(exposure_max, OV5647_EXPOSURE_DEFAULT);
1366646a0249SDave Stevenson 		__v4l2_ctrl_modify_range(sensor->exposure,
1367646a0249SDave Stevenson 					 sensor->exposure->minimum,
1368646a0249SDave Stevenson 					 exposure_max, sensor->exposure->step,
1369646a0249SDave Stevenson 					 exposure_def);
1370646a0249SDave Stevenson 	}
1371646a0249SDave Stevenson 
13724974c2f1SDavid Plowman 	/*
13734974c2f1SDavid Plowman 	 * If the device is not powered up by the host driver do
13744974c2f1SDavid Plowman 	 * not apply any controls to H/W at this time. Instead
13754974c2f1SDavid Plowman 	 * the controls will be restored at s_stream(1) time.
13764974c2f1SDavid Plowman 	 */
13774974c2f1SDavid Plowman 	if (!sensor->power_count)
13784974c2f1SDavid Plowman 		return 0;
13794974c2f1SDavid Plowman 
13804974c2f1SDavid Plowman 	switch (ctrl->id) {
13814974c2f1SDavid Plowman 	case V4L2_CID_AUTO_WHITE_BALANCE:
13824974c2f1SDavid Plowman 		return ov5647_s_auto_white_balance(sd, ctrl->val);
13834974c2f1SDavid Plowman 	case V4L2_CID_AUTOGAIN:
13844974c2f1SDavid Plowman 		return ov5647_s_autogain(sd, ctrl->val);
13854974c2f1SDavid Plowman 	case V4L2_CID_EXPOSURE_AUTO:
13864974c2f1SDavid Plowman 		return ov5647_s_exposure_auto(sd, ctrl->val);
13874974c2f1SDavid Plowman 	case V4L2_CID_ANALOGUE_GAIN:
13884974c2f1SDavid Plowman 		return  ov5647_s_analogue_gain(sd, ctrl->val);
13894974c2f1SDavid Plowman 	case V4L2_CID_EXPOSURE:
13904974c2f1SDavid Plowman 		return ov5647_s_exposure(sd, ctrl->val);
1391911f4516SDave Stevenson 	case V4L2_CID_PIXEL_RATE:
1392911f4516SDave Stevenson 		/* Read-only, but we adjust it based on mode. */
1393911f4516SDave Stevenson 		return 0;
1394c6da1ae4SJacopo Mondi 	case V4L2_CID_HBLANK:
1395c6da1ae4SJacopo Mondi 		/* Read-only, but we adjust it based on mode. */
1396c6da1ae4SJacopo Mondi 		return 0;
13972512c064SDave Stevenson 	case V4L2_CID_VBLANK:
13982512c064SDave Stevenson 		return ov5647_write16(sd, OV5647_REG_VTS_HI,
13992512c064SDave Stevenson 				      sensor->mode->format.height + ctrl->val);
14004974c2f1SDavid Plowman 	default:
14014974c2f1SDavid Plowman 		dev_info(&client->dev,
14024974c2f1SDavid Plowman 			 "Control (id:0x%x, val:0x%x) not supported\n",
14034974c2f1SDavid Plowman 			 ctrl->id, ctrl->val);
14044974c2f1SDavid Plowman 		return -EINVAL;
14054974c2f1SDavid Plowman 	}
14064974c2f1SDavid Plowman 
14074974c2f1SDavid Plowman 	return 0;
14084974c2f1SDavid Plowman }
14094974c2f1SDavid Plowman 
14104974c2f1SDavid Plowman static const struct v4l2_ctrl_ops ov5647_ctrl_ops = {
14114974c2f1SDavid Plowman 	.s_ctrl = ov5647_s_ctrl,
14124974c2f1SDavid Plowman };
14134974c2f1SDavid Plowman 
14144974c2f1SDavid Plowman static int ov5647_init_controls(struct ov5647 *sensor)
14154974c2f1SDavid Plowman {
14164974c2f1SDavid Plowman 	struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd);
1417646a0249SDave Stevenson 	int hblank, exposure_max, exposure_def;
14184974c2f1SDavid Plowman 
14192512c064SDave Stevenson 	v4l2_ctrl_handler_init(&sensor->ctrls, 8);
14204974c2f1SDavid Plowman 
14214974c2f1SDavid Plowman 	v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
14224974c2f1SDavid Plowman 			  V4L2_CID_AUTOGAIN, 0, 1, 1, 0);
14234974c2f1SDavid Plowman 
14244974c2f1SDavid Plowman 	v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
14254974c2f1SDavid Plowman 			  V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 0);
14264974c2f1SDavid Plowman 
14274974c2f1SDavid Plowman 	v4l2_ctrl_new_std_menu(&sensor->ctrls, &ov5647_ctrl_ops,
14284974c2f1SDavid Plowman 			       V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL,
14294974c2f1SDavid Plowman 			       0, V4L2_EXPOSURE_MANUAL);
14304974c2f1SDavid Plowman 
1431646a0249SDave Stevenson 	exposure_max = sensor->mode->vts - 4;
1432646a0249SDave Stevenson 	exposure_def = min(exposure_max, OV5647_EXPOSURE_DEFAULT);
1433646a0249SDave Stevenson 	sensor->exposure = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
1434646a0249SDave Stevenson 					     V4L2_CID_EXPOSURE,
1435646a0249SDave Stevenson 					     OV5647_EXPOSURE_MIN,
1436646a0249SDave Stevenson 					     exposure_max, OV5647_EXPOSURE_STEP,
1437646a0249SDave Stevenson 					     exposure_def);
14384974c2f1SDavid Plowman 
14394974c2f1SDavid Plowman 	/* min: 16 = 1.0x; max (10 bits); default: 32 = 2.0x. */
14404974c2f1SDavid Plowman 	v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
14414974c2f1SDavid Plowman 			  V4L2_CID_ANALOGUE_GAIN, 16, 1023, 1, 32);
14424974c2f1SDavid Plowman 
1443911f4516SDave Stevenson 	/* By default, PIXEL_RATE is read only, but it does change per mode */
1444911f4516SDave Stevenson 	sensor->pixel_rate = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
1445911f4516SDave Stevenson 					       V4L2_CID_PIXEL_RATE,
1446911f4516SDave Stevenson 					       sensor->mode->pixel_rate,
1447911f4516SDave Stevenson 					       sensor->mode->pixel_rate, 1,
1448911f4516SDave Stevenson 					       sensor->mode->pixel_rate);
1449c6da1ae4SJacopo Mondi 
1450c6da1ae4SJacopo Mondi 	/* By default, HBLANK is read only, but it does change per mode. */
1451c6da1ae4SJacopo Mondi 	hblank = sensor->mode->hts - sensor->mode->format.width;
1452c6da1ae4SJacopo Mondi 	sensor->hblank = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
1453c6da1ae4SJacopo Mondi 					   V4L2_CID_HBLANK, hblank, hblank, 1,
1454c6da1ae4SJacopo Mondi 					   hblank);
1455c6da1ae4SJacopo Mondi 
14562512c064SDave Stevenson 	sensor->vblank = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
14572512c064SDave Stevenson 					   V4L2_CID_VBLANK, OV5647_VBLANK_MIN,
14582512c064SDave Stevenson 					   OV5647_VTS_MAX -
14592512c064SDave Stevenson 					   sensor->mode->format.height, 1,
14602512c064SDave Stevenson 					   sensor->mode->vts -
14612512c064SDave Stevenson 					   sensor->mode->format.height);
14622512c064SDave Stevenson 
1463911f4516SDave Stevenson 	if (sensor->ctrls.error)
1464911f4516SDave Stevenson 		goto handler_free;
1465911f4516SDave Stevenson 
1466911f4516SDave Stevenson 	sensor->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY;
1467c6da1ae4SJacopo Mondi 	sensor->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
1468911f4516SDave Stevenson 	sensor->sd.ctrl_handler = &sensor->ctrls;
1469911f4516SDave Stevenson 
1470911f4516SDave Stevenson 	return 0;
1471911f4516SDave Stevenson 
1472911f4516SDave Stevenson handler_free:
1473911f4516SDave Stevenson 	dev_err(&client->dev, "%s Controls initialization failed (%d)\n",
14744974c2f1SDavid Plowman 		__func__, sensor->ctrls.error);
14754974c2f1SDavid Plowman 	v4l2_ctrl_handler_free(&sensor->ctrls);
14764974c2f1SDavid Plowman 
14774974c2f1SDavid Plowman 	return sensor->ctrls.error;
14784974c2f1SDavid Plowman }
14794974c2f1SDavid Plowman 
1480dea4fcfeSDave Stevenson static int ov5647_parse_dt(struct ov5647 *sensor, struct device_node *np)
14813c2472a3SRamiro Oliveira {
1482dea4fcfeSDave Stevenson 	struct v4l2_fwnode_endpoint bus_cfg = {
1483dea4fcfeSDave Stevenson 		.bus_type = V4L2_MBUS_CSI2_DPHY,
1484dea4fcfeSDave Stevenson 	};
14853c2472a3SRamiro Oliveira 	struct device_node *ep;
14863c2472a3SRamiro Oliveira 	int ret;
14873c2472a3SRamiro Oliveira 
14883c2472a3SRamiro Oliveira 	ep = of_graph_get_next_endpoint(np, NULL);
14893c2472a3SRamiro Oliveira 	if (!ep)
14903c2472a3SRamiro Oliveira 		return -EINVAL;
14913c2472a3SRamiro Oliveira 
1492859969b3SSakari Ailus 	ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &bus_cfg);
1493dea4fcfeSDave Stevenson 	if (ret)
1494dea4fcfeSDave Stevenson 		goto out;
14953c2472a3SRamiro Oliveira 
1496dea4fcfeSDave Stevenson 	sensor->clock_ncont = bus_cfg.bus.mipi_csi2.flags &
1497dea4fcfeSDave Stevenson 			      V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK;
1498dea4fcfeSDave Stevenson 
1499dea4fcfeSDave Stevenson out:
15003c2472a3SRamiro Oliveira 	of_node_put(ep);
1501c9a05cecSJacopo Mondi 
15023c2472a3SRamiro Oliveira 	return ret;
15033c2472a3SRamiro Oliveira }
15043c2472a3SRamiro Oliveira 
1505e6714993SKieran Bingham static int ov5647_probe(struct i2c_client *client)
15063c2472a3SRamiro Oliveira {
1507c9a05cecSJacopo Mondi 	struct device_node *np = client->dev.of_node;
15083c2472a3SRamiro Oliveira 	struct device *dev = &client->dev;
15093c2472a3SRamiro Oliveira 	struct ov5647 *sensor;
15103c2472a3SRamiro Oliveira 	struct v4l2_subdev *sd;
15113c2472a3SRamiro Oliveira 	u32 xclk_freq;
1512c9a05cecSJacopo Mondi 	int ret;
15133c2472a3SRamiro Oliveira 
15143c2472a3SRamiro Oliveira 	sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
15153c2472a3SRamiro Oliveira 	if (!sensor)
15163c2472a3SRamiro Oliveira 		return -ENOMEM;
15173c2472a3SRamiro Oliveira 
15183c2472a3SRamiro Oliveira 	if (IS_ENABLED(CONFIG_OF) && np) {
1519dea4fcfeSDave Stevenson 		ret = ov5647_parse_dt(sensor, np);
15203c2472a3SRamiro Oliveira 		if (ret) {
15213c2472a3SRamiro Oliveira 			dev_err(dev, "DT parsing error: %d\n", ret);
15223c2472a3SRamiro Oliveira 			return ret;
15233c2472a3SRamiro Oliveira 		}
15243c2472a3SRamiro Oliveira 	}
15253c2472a3SRamiro Oliveira 
15263c2472a3SRamiro Oliveira 	sensor->xclk = devm_clk_get(dev, NULL);
15273c2472a3SRamiro Oliveira 	if (IS_ERR(sensor->xclk)) {
15283c2472a3SRamiro Oliveira 		dev_err(dev, "could not get xclk");
15293c2472a3SRamiro Oliveira 		return PTR_ERR(sensor->xclk);
15303c2472a3SRamiro Oliveira 	}
15313c2472a3SRamiro Oliveira 
15323c2472a3SRamiro Oliveira 	xclk_freq = clk_get_rate(sensor->xclk);
15333c2472a3SRamiro Oliveira 	if (xclk_freq != 25000000) {
15343c2472a3SRamiro Oliveira 		dev_err(dev, "Unsupported clock frequency: %u\n", xclk_freq);
15353c2472a3SRamiro Oliveira 		return -EINVAL;
15363c2472a3SRamiro Oliveira 	}
15373c2472a3SRamiro Oliveira 
1538c9a05cecSJacopo Mondi 	/* Request the power down GPIO asserted. */
1539c9a05cecSJacopo Mondi 	sensor->pwdn = devm_gpiod_get_optional(dev, "pwdn", GPIOD_OUT_HIGH);
1540b050791dSDave Stevenson 	if (IS_ERR(sensor->pwdn)) {
1541b050791dSDave Stevenson 		dev_err(dev, "Failed to get 'pwdn' gpio\n");
1542b050791dSDave Stevenson 		return -EINVAL;
1543b050791dSDave Stevenson 	}
1544b050791dSDave Stevenson 
15453c2472a3SRamiro Oliveira 	mutex_init(&sensor->lock);
15463c2472a3SRamiro Oliveira 
1547d7d6074eSJacopo Mondi 	sensor->mode = OV5647_DEFAULT_MODE;
1548d7d6074eSJacopo Mondi 
15494974c2f1SDavid Plowman 	ret = ov5647_init_controls(sensor);
15504974c2f1SDavid Plowman 	if (ret)
15514974c2f1SDavid Plowman 		goto mutex_destroy;
15524974c2f1SDavid Plowman 
15533c2472a3SRamiro Oliveira 	sd = &sensor->sd;
15543c2472a3SRamiro Oliveira 	v4l2_i2c_subdev_init(sd, client, &ov5647_subdev_ops);
1555c9a05cecSJacopo Mondi 	sd->internal_ops = &ov5647_subdev_internal_ops;
15567ef761a0SDave Stevenson 	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
15573c2472a3SRamiro Oliveira 
15583c2472a3SRamiro Oliveira 	sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
15593c2472a3SRamiro Oliveira 	sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
15603c2472a3SRamiro Oliveira 	ret = media_entity_pads_init(&sd->entity, 1, &sensor->pad);
15613c2472a3SRamiro Oliveira 	if (ret < 0)
15624974c2f1SDavid Plowman 		goto ctrl_handler_free;
15633c2472a3SRamiro Oliveira 
1564089b7c70SJacopo Mondi 	ret = ov5647_power_on(dev);
1565089b7c70SJacopo Mondi 	if (ret)
1566089b7c70SJacopo Mondi 		goto entity_cleanup;
1567b050791dSDave Stevenson 
15683c2472a3SRamiro Oliveira 	ret = ov5647_detect(sd);
15693c2472a3SRamiro Oliveira 	if (ret < 0)
1570089b7c70SJacopo Mondi 		goto power_off;
15713c2472a3SRamiro Oliveira 
15723c2472a3SRamiro Oliveira 	ret = v4l2_async_register_subdev(sd);
15733c2472a3SRamiro Oliveira 	if (ret < 0)
1574089b7c70SJacopo Mondi 		goto power_off;
1575089b7c70SJacopo Mondi 
1576089b7c70SJacopo Mondi 	/* Enable runtime PM and turn off the device */
1577089b7c70SJacopo Mondi 	pm_runtime_set_active(dev);
1578089b7c70SJacopo Mondi 	pm_runtime_enable(dev);
1579089b7c70SJacopo Mondi 	pm_runtime_idle(dev);
15803c2472a3SRamiro Oliveira 
15813c2472a3SRamiro Oliveira 	dev_dbg(dev, "OmniVision OV5647 camera driver probed\n");
1582c9a05cecSJacopo Mondi 
15833c2472a3SRamiro Oliveira 	return 0;
1584c9a05cecSJacopo Mondi 
1585089b7c70SJacopo Mondi power_off:
1586089b7c70SJacopo Mondi 	ov5647_power_off(dev);
1587c9a05cecSJacopo Mondi entity_cleanup:
15883c2472a3SRamiro Oliveira 	media_entity_cleanup(&sd->entity);
15894974c2f1SDavid Plowman ctrl_handler_free:
15904974c2f1SDavid Plowman 	v4l2_ctrl_handler_free(&sensor->ctrls);
1591c9a05cecSJacopo Mondi mutex_destroy:
15923c2472a3SRamiro Oliveira 	mutex_destroy(&sensor->lock);
1593c9a05cecSJacopo Mondi 
15943c2472a3SRamiro Oliveira 	return ret;
15953c2472a3SRamiro Oliveira }
15963c2472a3SRamiro Oliveira 
15973c2472a3SRamiro Oliveira static int ov5647_remove(struct i2c_client *client)
15983c2472a3SRamiro Oliveira {
15993c2472a3SRamiro Oliveira 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
16005bc5ca71SJacopo Mondi 	struct ov5647 *sensor = to_sensor(sd);
16013c2472a3SRamiro Oliveira 
16025bc5ca71SJacopo Mondi 	v4l2_async_unregister_subdev(&sensor->sd);
16035bc5ca71SJacopo Mondi 	media_entity_cleanup(&sensor->sd.entity);
16045bc5ca71SJacopo Mondi 	v4l2_ctrl_handler_free(&sensor->ctrls);
16053c2472a3SRamiro Oliveira 	v4l2_device_unregister_subdev(sd);
1606089b7c70SJacopo Mondi 	pm_runtime_disable(&client->dev);
16075bc5ca71SJacopo Mondi 	mutex_destroy(&sensor->lock);
16083c2472a3SRamiro Oliveira 
16093c2472a3SRamiro Oliveira 	return 0;
16103c2472a3SRamiro Oliveira }
16113c2472a3SRamiro Oliveira 
1612089b7c70SJacopo Mondi static const struct dev_pm_ops ov5647_pm_ops = {
1613089b7c70SJacopo Mondi 	SET_RUNTIME_PM_OPS(ov5647_power_off, ov5647_power_on, NULL)
1614089b7c70SJacopo Mondi };
1615089b7c70SJacopo Mondi 
16163c2472a3SRamiro Oliveira static const struct i2c_device_id ov5647_id[] = {
16173c2472a3SRamiro Oliveira 	{ "ov5647", 0 },
1618c9a05cecSJacopo Mondi 	{ /* sentinel */ }
16193c2472a3SRamiro Oliveira };
16203c2472a3SRamiro Oliveira MODULE_DEVICE_TABLE(i2c, ov5647_id);
16213c2472a3SRamiro Oliveira 
16223c2472a3SRamiro Oliveira #if IS_ENABLED(CONFIG_OF)
16233c2472a3SRamiro Oliveira static const struct of_device_id ov5647_of_match[] = {
16243c2472a3SRamiro Oliveira 	{ .compatible = "ovti,ov5647" },
16253c2472a3SRamiro Oliveira 	{ /* sentinel */ },
16263c2472a3SRamiro Oliveira };
16273c2472a3SRamiro Oliveira MODULE_DEVICE_TABLE(of, ov5647_of_match);
16283c2472a3SRamiro Oliveira #endif
16293c2472a3SRamiro Oliveira 
16303c2472a3SRamiro Oliveira static struct i2c_driver ov5647_driver = {
16313c2472a3SRamiro Oliveira 	.driver = {
16323c2472a3SRamiro Oliveira 		.of_match_table = of_match_ptr(ov5647_of_match),
1633c9a05cecSJacopo Mondi 		.name	= "ov5647",
1634089b7c70SJacopo Mondi 		.pm	= &ov5647_pm_ops,
16353c2472a3SRamiro Oliveira 	},
1636e6714993SKieran Bingham 	.probe_new	= ov5647_probe,
16373c2472a3SRamiro Oliveira 	.remove		= ov5647_remove,
16383c2472a3SRamiro Oliveira 	.id_table	= ov5647_id,
16393c2472a3SRamiro Oliveira };
16403c2472a3SRamiro Oliveira 
16413c2472a3SRamiro Oliveira module_i2c_driver(ov5647_driver);
16423c2472a3SRamiro Oliveira 
16433c2472a3SRamiro Oliveira MODULE_AUTHOR("Ramiro Oliveira <roliveir@synopsys.com>");
16443c2472a3SRamiro Oliveira MODULE_DESCRIPTION("A low-level driver for OmniVision ov5647 sensors");
16453c2472a3SRamiro Oliveira MODULE_LICENSE("GPL v2");
1646