xref: /openbmc/linux/drivers/media/i2c/mt9p031.c (revision 69681cd04164812e4089f3f57e450e9b7c27c1c2)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2cb7a01acSMauro Carvalho Chehab /*
3cb7a01acSMauro Carvalho Chehab  * Driver for MT9P031 CMOS Image Sensor from Aptina
4cb7a01acSMauro Carvalho Chehab  *
5cb7a01acSMauro Carvalho Chehab  * Copyright (C) 2011, Laurent Pinchart <laurent.pinchart@ideasonboard.com>
6cb7a01acSMauro Carvalho Chehab  * Copyright (C) 2011, Javier Martin <javier.martin@vista-silicon.com>
7cb7a01acSMauro Carvalho Chehab  * Copyright (C) 2011, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
8cb7a01acSMauro Carvalho Chehab  *
9cb7a01acSMauro Carvalho Chehab  * Based on the MT9V032 driver and Bastian Hecht's code.
10cb7a01acSMauro Carvalho Chehab  */
11cb7a01acSMauro Carvalho Chehab 
12d6749258SLaurent Pinchart #include <linux/clk.h>
13cb7a01acSMauro Carvalho Chehab #include <linux/delay.h>
14cb7a01acSMauro Carvalho Chehab #include <linux/device.h>
157c3be9f8SLaurent Pinchart #include <linux/gpio/consumer.h>
16cb7a01acSMauro Carvalho Chehab #include <linux/i2c.h>
17cb7a01acSMauro Carvalho Chehab #include <linux/log2.h>
188d4da37cSLad, Prabhakar #include <linux/module.h>
1964695905SSachin Kamat #include <linux/of.h>
20fd9fdb78SPhilipp Zabel #include <linux/of_graph.h>
21cb7a01acSMauro Carvalho Chehab #include <linux/pm.h>
2297f21276SLaurent Pinchart #include <linux/regulator/consumer.h>
23cb7a01acSMauro Carvalho Chehab #include <linux/slab.h>
24cb7a01acSMauro Carvalho Chehab #include <linux/videodev2.h>
25cb7a01acSMauro Carvalho Chehab 
26b5dcee22SMauro Carvalho Chehab #include <media/i2c/mt9p031.h>
279012d088SLad, Prabhakar #include <media/v4l2-async.h>
28cb7a01acSMauro Carvalho Chehab #include <media/v4l2-ctrls.h>
29cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h>
30ae47ee5fSChristian Hemp #include <media/v4l2-fwnode.h>
31cb7a01acSMauro Carvalho Chehab #include <media/v4l2-subdev.h>
32cb7a01acSMauro Carvalho Chehab 
33cb7a01acSMauro Carvalho Chehab #include "aptina-pll.h"
34cb7a01acSMauro Carvalho Chehab 
35cb7a01acSMauro Carvalho Chehab #define MT9P031_PIXEL_ARRAY_WIDTH			2752
36cb7a01acSMauro Carvalho Chehab #define MT9P031_PIXEL_ARRAY_HEIGHT			2004
37cb7a01acSMauro Carvalho Chehab 
38cb7a01acSMauro Carvalho Chehab #define MT9P031_CHIP_VERSION				0x00
39cb7a01acSMauro Carvalho Chehab #define		MT9P031_CHIP_VERSION_VALUE		0x1801
40cb7a01acSMauro Carvalho Chehab #define MT9P031_ROW_START				0x01
41cb7a01acSMauro Carvalho Chehab #define		MT9P031_ROW_START_MIN			0
42cb7a01acSMauro Carvalho Chehab #define		MT9P031_ROW_START_MAX			2004
43cb7a01acSMauro Carvalho Chehab #define		MT9P031_ROW_START_DEF			54
44cb7a01acSMauro Carvalho Chehab #define MT9P031_COLUMN_START				0x02
45cb7a01acSMauro Carvalho Chehab #define		MT9P031_COLUMN_START_MIN		0
46cb7a01acSMauro Carvalho Chehab #define		MT9P031_COLUMN_START_MAX		2750
47cb7a01acSMauro Carvalho Chehab #define		MT9P031_COLUMN_START_DEF		16
48cb7a01acSMauro Carvalho Chehab #define MT9P031_WINDOW_HEIGHT				0x03
49cb7a01acSMauro Carvalho Chehab #define		MT9P031_WINDOW_HEIGHT_MIN		2
50cb7a01acSMauro Carvalho Chehab #define		MT9P031_WINDOW_HEIGHT_MAX		2006
51cb7a01acSMauro Carvalho Chehab #define		MT9P031_WINDOW_HEIGHT_DEF		1944
52cb7a01acSMauro Carvalho Chehab #define MT9P031_WINDOW_WIDTH				0x04
53cb7a01acSMauro Carvalho Chehab #define		MT9P031_WINDOW_WIDTH_MIN		2
54cb7a01acSMauro Carvalho Chehab #define		MT9P031_WINDOW_WIDTH_MAX		2752
55cb7a01acSMauro Carvalho Chehab #define		MT9P031_WINDOW_WIDTH_DEF		2592
56cb7a01acSMauro Carvalho Chehab #define MT9P031_HORIZONTAL_BLANK			0x05
57cb7a01acSMauro Carvalho Chehab #define		MT9P031_HORIZONTAL_BLANK_MIN		0
58cb7a01acSMauro Carvalho Chehab #define		MT9P031_HORIZONTAL_BLANK_MAX		4095
59cb7a01acSMauro Carvalho Chehab #define MT9P031_VERTICAL_BLANK				0x06
605266c98bSLaurent Pinchart #define		MT9P031_VERTICAL_BLANK_MIN		1
615266c98bSLaurent Pinchart #define		MT9P031_VERTICAL_BLANK_MAX		4096
625266c98bSLaurent Pinchart #define		MT9P031_VERTICAL_BLANK_DEF		26
63cb7a01acSMauro Carvalho Chehab #define MT9P031_OUTPUT_CONTROL				0x07
64cb7a01acSMauro Carvalho Chehab #define		MT9P031_OUTPUT_CONTROL_CEN		2
65cb7a01acSMauro Carvalho Chehab #define		MT9P031_OUTPUT_CONTROL_SYN		1
66cb7a01acSMauro Carvalho Chehab #define		MT9P031_OUTPUT_CONTROL_DEF		0x1f82
67cb7a01acSMauro Carvalho Chehab #define MT9P031_SHUTTER_WIDTH_UPPER			0x08
68cb7a01acSMauro Carvalho Chehab #define MT9P031_SHUTTER_WIDTH_LOWER			0x09
69cb7a01acSMauro Carvalho Chehab #define		MT9P031_SHUTTER_WIDTH_MIN		1
70cb7a01acSMauro Carvalho Chehab #define		MT9P031_SHUTTER_WIDTH_MAX		1048575
71cb7a01acSMauro Carvalho Chehab #define		MT9P031_SHUTTER_WIDTH_DEF		1943
72cb7a01acSMauro Carvalho Chehab #define	MT9P031_PLL_CONTROL				0x10
73cb7a01acSMauro Carvalho Chehab #define		MT9P031_PLL_CONTROL_PWROFF		0x0050
74cb7a01acSMauro Carvalho Chehab #define		MT9P031_PLL_CONTROL_PWRON		0x0051
75cb7a01acSMauro Carvalho Chehab #define		MT9P031_PLL_CONTROL_USEPLL		0x0052
76cb7a01acSMauro Carvalho Chehab #define	MT9P031_PLL_CONFIG_1				0x11
77cb7a01acSMauro Carvalho Chehab #define	MT9P031_PLL_CONFIG_2				0x12
78cb7a01acSMauro Carvalho Chehab #define MT9P031_PIXEL_CLOCK_CONTROL			0x0a
790a0e78d1SStefan Riedmueller #define		MT9P031_PIXEL_CLOCK_INVERT		BIT(15)
80a970449eSLaurent Pinchart #define		MT9P031_PIXEL_CLOCK_SHIFT(n)		((n) << 8)
81a970449eSLaurent Pinchart #define		MT9P031_PIXEL_CLOCK_DIVIDE(n)		((n) << 0)
820961ba6dSDirk Bender #define MT9P031_RESTART					0x0b
830a0e78d1SStefan Riedmueller #define		MT9P031_FRAME_PAUSE_RESTART		BIT(1)
840a0e78d1SStefan Riedmueller #define		MT9P031_FRAME_RESTART			BIT(0)
85cb7a01acSMauro Carvalho Chehab #define MT9P031_SHUTTER_DELAY				0x0c
86cb7a01acSMauro Carvalho Chehab #define MT9P031_RST					0x0d
870a0e78d1SStefan Riedmueller #define		MT9P031_RST_ENABLE			BIT(0)
88cb7a01acSMauro Carvalho Chehab #define MT9P031_READ_MODE_1				0x1e
89cb7a01acSMauro Carvalho Chehab #define MT9P031_READ_MODE_2				0x20
900a0e78d1SStefan Riedmueller #define		MT9P031_READ_MODE_2_ROW_MIR		BIT(15)
910a0e78d1SStefan Riedmueller #define		MT9P031_READ_MODE_2_COL_MIR		BIT(14)
920a0e78d1SStefan Riedmueller #define		MT9P031_READ_MODE_2_ROW_BLC		BIT(6)
93cb7a01acSMauro Carvalho Chehab #define MT9P031_ROW_ADDRESS_MODE			0x22
94cb7a01acSMauro Carvalho Chehab #define MT9P031_COLUMN_ADDRESS_MODE			0x23
95cb7a01acSMauro Carvalho Chehab #define MT9P031_GLOBAL_GAIN				0x35
96cb7a01acSMauro Carvalho Chehab #define		MT9P031_GLOBAL_GAIN_MIN			8
97cb7a01acSMauro Carvalho Chehab #define		MT9P031_GLOBAL_GAIN_MAX			1024
98cb7a01acSMauro Carvalho Chehab #define		MT9P031_GLOBAL_GAIN_DEF			8
990a0e78d1SStefan Riedmueller #define		MT9P031_GLOBAL_GAIN_MULT		BIT(6)
100cb7a01acSMauro Carvalho Chehab #define MT9P031_ROW_BLACK_TARGET			0x49
101cb7a01acSMauro Carvalho Chehab #define MT9P031_ROW_BLACK_DEF_OFFSET			0x4b
102cb7a01acSMauro Carvalho Chehab #define MT9P031_GREEN1_OFFSET				0x60
103cb7a01acSMauro Carvalho Chehab #define MT9P031_GREEN2_OFFSET				0x61
104cb7a01acSMauro Carvalho Chehab #define MT9P031_BLACK_LEVEL_CALIBRATION			0x62
1050a0e78d1SStefan Riedmueller #define		MT9P031_BLC_MANUAL_BLC			BIT(0)
106cb7a01acSMauro Carvalho Chehab #define MT9P031_RED_OFFSET				0x63
107cb7a01acSMauro Carvalho Chehab #define MT9P031_BLUE_OFFSET				0x64
108cb7a01acSMauro Carvalho Chehab #define MT9P031_TEST_PATTERN				0xa0
109cb7a01acSMauro Carvalho Chehab #define		MT9P031_TEST_PATTERN_SHIFT		3
1100a0e78d1SStefan Riedmueller #define		MT9P031_TEST_PATTERN_ENABLE		BIT(0)
111cb7a01acSMauro Carvalho Chehab #define MT9P031_TEST_PATTERN_GREEN			0xa1
112cb7a01acSMauro Carvalho Chehab #define MT9P031_TEST_PATTERN_RED			0xa2
113cb7a01acSMauro Carvalho Chehab #define MT9P031_TEST_PATTERN_BLUE			0xa3
114cb7a01acSMauro Carvalho Chehab 
115cb7a01acSMauro Carvalho Chehab enum mt9p031_model {
116cb7a01acSMauro Carvalho Chehab 	MT9P031_MODEL_COLOR,
117cb7a01acSMauro Carvalho Chehab 	MT9P031_MODEL_MONOCHROME,
118cb7a01acSMauro Carvalho Chehab };
119cb7a01acSMauro Carvalho Chehab 
120cb7a01acSMauro Carvalho Chehab struct mt9p031 {
121cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev subdev;
122cb7a01acSMauro Carvalho Chehab 	struct media_pad pad;
123cb7a01acSMauro Carvalho Chehab 	struct v4l2_rect crop;  /* Sensor window */
124cb7a01acSMauro Carvalho Chehab 	struct v4l2_mbus_framefmt format;
125cb7a01acSMauro Carvalho Chehab 	struct mt9p031_platform_data *pdata;
126cb7a01acSMauro Carvalho Chehab 	struct mutex power_lock; /* lock to protect power_count */
127cb7a01acSMauro Carvalho Chehab 	int power_count;
128cb7a01acSMauro Carvalho Chehab 
129d6749258SLaurent Pinchart 	struct clk *clk;
1307997196cSLaurent Pinchart 	struct regulator_bulk_data regulators[3];
13197f21276SLaurent Pinchart 
132cb7a01acSMauro Carvalho Chehab 	enum mt9p031_model model;
133cb7a01acSMauro Carvalho Chehab 	struct aptina_pll pll;
134a970449eSLaurent Pinchart 	unsigned int clk_div;
135a970449eSLaurent Pinchart 	bool use_pll;
1367c3be9f8SLaurent Pinchart 	struct gpio_desc *reset;
137cb7a01acSMauro Carvalho Chehab 
138cb7a01acSMauro Carvalho Chehab 	struct v4l2_ctrl_handler ctrls;
139cb7a01acSMauro Carvalho Chehab 	struct v4l2_ctrl *blc_auto;
140cb7a01acSMauro Carvalho Chehab 	struct v4l2_ctrl *blc_offset;
141cb7a01acSMauro Carvalho Chehab 
142cb7a01acSMauro Carvalho Chehab 	/* Registers cache */
143cb7a01acSMauro Carvalho Chehab 	u16 output_control;
144cb7a01acSMauro Carvalho Chehab 	u16 mode2;
145cb7a01acSMauro Carvalho Chehab };
146cb7a01acSMauro Carvalho Chehab 
147cb7a01acSMauro Carvalho Chehab static struct mt9p031 *to_mt9p031(struct v4l2_subdev *sd)
148cb7a01acSMauro Carvalho Chehab {
149cb7a01acSMauro Carvalho Chehab 	return container_of(sd, struct mt9p031, subdev);
150cb7a01acSMauro Carvalho Chehab }
151cb7a01acSMauro Carvalho Chehab 
152cb7a01acSMauro Carvalho Chehab static int mt9p031_read(struct i2c_client *client, u8 reg)
153cb7a01acSMauro Carvalho Chehab {
154cb7a01acSMauro Carvalho Chehab 	return i2c_smbus_read_word_swapped(client, reg);
155cb7a01acSMauro Carvalho Chehab }
156cb7a01acSMauro Carvalho Chehab 
157cb7a01acSMauro Carvalho Chehab static int mt9p031_write(struct i2c_client *client, u8 reg, u16 data)
158cb7a01acSMauro Carvalho Chehab {
159cb7a01acSMauro Carvalho Chehab 	return i2c_smbus_write_word_swapped(client, reg, data);
160cb7a01acSMauro Carvalho Chehab }
161cb7a01acSMauro Carvalho Chehab 
162cb7a01acSMauro Carvalho Chehab static int mt9p031_set_output_control(struct mt9p031 *mt9p031, u16 clear,
163cb7a01acSMauro Carvalho Chehab 				      u16 set)
164cb7a01acSMauro Carvalho Chehab {
165cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
166cb7a01acSMauro Carvalho Chehab 	u16 value = (mt9p031->output_control & ~clear) | set;
167cb7a01acSMauro Carvalho Chehab 	int ret;
168cb7a01acSMauro Carvalho Chehab 
169cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_OUTPUT_CONTROL, value);
170cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
171cb7a01acSMauro Carvalho Chehab 		return ret;
172cb7a01acSMauro Carvalho Chehab 
173cb7a01acSMauro Carvalho Chehab 	mt9p031->output_control = value;
174cb7a01acSMauro Carvalho Chehab 	return 0;
175cb7a01acSMauro Carvalho Chehab }
176cb7a01acSMauro Carvalho Chehab 
177cb7a01acSMauro Carvalho Chehab static int mt9p031_set_mode2(struct mt9p031 *mt9p031, u16 clear, u16 set)
178cb7a01acSMauro Carvalho Chehab {
179cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
180cb7a01acSMauro Carvalho Chehab 	u16 value = (mt9p031->mode2 & ~clear) | set;
181cb7a01acSMauro Carvalho Chehab 	int ret;
182cb7a01acSMauro Carvalho Chehab 
183cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_READ_MODE_2, value);
184cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
185cb7a01acSMauro Carvalho Chehab 		return ret;
186cb7a01acSMauro Carvalho Chehab 
187cb7a01acSMauro Carvalho Chehab 	mt9p031->mode2 = value;
188cb7a01acSMauro Carvalho Chehab 	return 0;
189cb7a01acSMauro Carvalho Chehab }
190cb7a01acSMauro Carvalho Chehab 
191cb7a01acSMauro Carvalho Chehab static int mt9p031_reset(struct mt9p031 *mt9p031)
192cb7a01acSMauro Carvalho Chehab {
193cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
194cb7a01acSMauro Carvalho Chehab 	int ret;
195cb7a01acSMauro Carvalho Chehab 
196cb7a01acSMauro Carvalho Chehab 	/* Disable chip output, synchronous option update */
197cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_RST, MT9P031_RST_ENABLE);
198cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
199cb7a01acSMauro Carvalho Chehab 		return ret;
2000a0e78d1SStefan Riedmueller 	ret = mt9p031_write(client, MT9P031_RST, 0);
201cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
202cb7a01acSMauro Carvalho Chehab 		return ret;
203cb7a01acSMauro Carvalho Chehab 
204a970449eSLaurent Pinchart 	ret = mt9p031_write(client, MT9P031_PIXEL_CLOCK_CONTROL,
205a970449eSLaurent Pinchart 			    MT9P031_PIXEL_CLOCK_DIVIDE(mt9p031->clk_div));
206a970449eSLaurent Pinchart 	if (ret < 0)
207a970449eSLaurent Pinchart 		return ret;
208a970449eSLaurent Pinchart 
209cb7a01acSMauro Carvalho Chehab 	return mt9p031_set_output_control(mt9p031, MT9P031_OUTPUT_CONTROL_CEN,
210cb7a01acSMauro Carvalho Chehab 					  0);
211cb7a01acSMauro Carvalho Chehab }
212cb7a01acSMauro Carvalho Chehab 
213d6749258SLaurent Pinchart static int mt9p031_clk_setup(struct mt9p031 *mt9p031)
214cb7a01acSMauro Carvalho Chehab {
215cb7a01acSMauro Carvalho Chehab 	static const struct aptina_pll_limits limits = {
216cb7a01acSMauro Carvalho Chehab 		.ext_clock_min = 6000000,
217cb7a01acSMauro Carvalho Chehab 		.ext_clock_max = 27000000,
218cb7a01acSMauro Carvalho Chehab 		.int_clock_min = 2000000,
219cb7a01acSMauro Carvalho Chehab 		.int_clock_max = 13500000,
220cb7a01acSMauro Carvalho Chehab 		.out_clock_min = 180000000,
221cb7a01acSMauro Carvalho Chehab 		.out_clock_max = 360000000,
222cb7a01acSMauro Carvalho Chehab 		.pix_clock_max = 96000000,
223cb7a01acSMauro Carvalho Chehab 		.n_min = 1,
224cb7a01acSMauro Carvalho Chehab 		.n_max = 64,
225cb7a01acSMauro Carvalho Chehab 		.m_min = 16,
226cb7a01acSMauro Carvalho Chehab 		.m_max = 255,
227cb7a01acSMauro Carvalho Chehab 		.p1_min = 1,
228cb7a01acSMauro Carvalho Chehab 		.p1_max = 128,
229cb7a01acSMauro Carvalho Chehab 	};
230cb7a01acSMauro Carvalho Chehab 
231cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
232cb7a01acSMauro Carvalho Chehab 	struct mt9p031_platform_data *pdata = mt9p031->pdata;
233b9c18096SEnrico Scholz 	unsigned long ext_freq;
234ee2d16d7SLad, Prabhakar 	int ret;
235cb7a01acSMauro Carvalho Chehab 
236d6749258SLaurent Pinchart 	mt9p031->clk = devm_clk_get(&client->dev, NULL);
237d6749258SLaurent Pinchart 	if (IS_ERR(mt9p031->clk))
238d6749258SLaurent Pinchart 		return PTR_ERR(mt9p031->clk);
239d6749258SLaurent Pinchart 
240ee2d16d7SLad, Prabhakar 	ret = clk_set_rate(mt9p031->clk, pdata->ext_freq);
241ee2d16d7SLad, Prabhakar 	if (ret < 0)
242ee2d16d7SLad, Prabhakar 		return ret;
243d6749258SLaurent Pinchart 
244b9c18096SEnrico Scholz 	ext_freq = clk_get_rate(mt9p031->clk);
245b9c18096SEnrico Scholz 
246a970449eSLaurent Pinchart 	/* If the external clock frequency is out of bounds for the PLL use the
247a970449eSLaurent Pinchart 	 * pixel clock divider only and disable the PLL.
248a970449eSLaurent Pinchart 	 */
249b9c18096SEnrico Scholz 	if (ext_freq > limits.ext_clock_max) {
250a970449eSLaurent Pinchart 		unsigned int div;
251a970449eSLaurent Pinchart 
252b9c18096SEnrico Scholz 		div = DIV_ROUND_UP(ext_freq, pdata->target_freq);
253a970449eSLaurent Pinchart 		div = roundup_pow_of_two(div) / 2;
254a970449eSLaurent Pinchart 
255198b47ddSEnrico Scholz 		mt9p031->clk_div = min_t(unsigned int, div, 64);
256a970449eSLaurent Pinchart 		mt9p031->use_pll = false;
257a970449eSLaurent Pinchart 
258a970449eSLaurent Pinchart 		return 0;
259a970449eSLaurent Pinchart 	}
260cb7a01acSMauro Carvalho Chehab 
261b9c18096SEnrico Scholz 	mt9p031->pll.ext_clock = ext_freq;
262cb7a01acSMauro Carvalho Chehab 	mt9p031->pll.pix_clock = pdata->target_freq;
263a970449eSLaurent Pinchart 	mt9p031->use_pll = true;
264cb7a01acSMauro Carvalho Chehab 
265cb7a01acSMauro Carvalho Chehab 	return aptina_pll_calculate(&client->dev, &limits, &mt9p031->pll);
266cb7a01acSMauro Carvalho Chehab }
267cb7a01acSMauro Carvalho Chehab 
268cb7a01acSMauro Carvalho Chehab static int mt9p031_pll_enable(struct mt9p031 *mt9p031)
269cb7a01acSMauro Carvalho Chehab {
270cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
271cb7a01acSMauro Carvalho Chehab 	int ret;
272cb7a01acSMauro Carvalho Chehab 
273a970449eSLaurent Pinchart 	if (!mt9p031->use_pll)
274a970449eSLaurent Pinchart 		return 0;
275a970449eSLaurent Pinchart 
276cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_PLL_CONTROL,
277cb7a01acSMauro Carvalho Chehab 			    MT9P031_PLL_CONTROL_PWRON);
278cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
279cb7a01acSMauro Carvalho Chehab 		return ret;
280cb7a01acSMauro Carvalho Chehab 
281cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_PLL_CONFIG_1,
282cb7a01acSMauro Carvalho Chehab 			    (mt9p031->pll.m << 8) | (mt9p031->pll.n - 1));
283cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
284cb7a01acSMauro Carvalho Chehab 		return ret;
285cb7a01acSMauro Carvalho Chehab 
286cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_PLL_CONFIG_2, mt9p031->pll.p1 - 1);
287cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
288cb7a01acSMauro Carvalho Chehab 		return ret;
289cb7a01acSMauro Carvalho Chehab 
290cb7a01acSMauro Carvalho Chehab 	usleep_range(1000, 2000);
291cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_PLL_CONTROL,
292cb7a01acSMauro Carvalho Chehab 			    MT9P031_PLL_CONTROL_PWRON |
293cb7a01acSMauro Carvalho Chehab 			    MT9P031_PLL_CONTROL_USEPLL);
294cb7a01acSMauro Carvalho Chehab 	return ret;
295cb7a01acSMauro Carvalho Chehab }
296cb7a01acSMauro Carvalho Chehab 
297cb7a01acSMauro Carvalho Chehab static inline int mt9p031_pll_disable(struct mt9p031 *mt9p031)
298cb7a01acSMauro Carvalho Chehab {
299cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
300cb7a01acSMauro Carvalho Chehab 
301a970449eSLaurent Pinchart 	if (!mt9p031->use_pll)
302a970449eSLaurent Pinchart 		return 0;
303a970449eSLaurent Pinchart 
304cb7a01acSMauro Carvalho Chehab 	return mt9p031_write(client, MT9P031_PLL_CONTROL,
305cb7a01acSMauro Carvalho Chehab 			     MT9P031_PLL_CONTROL_PWROFF);
306cb7a01acSMauro Carvalho Chehab }
307cb7a01acSMauro Carvalho Chehab 
308cb7a01acSMauro Carvalho Chehab static int mt9p031_power_on(struct mt9p031 *mt9p031)
309cb7a01acSMauro Carvalho Chehab {
3107997196cSLaurent Pinchart 	int ret;
3117997196cSLaurent Pinchart 
3127c3be9f8SLaurent Pinchart 	/* Ensure RESET_BAR is active */
3137c3be9f8SLaurent Pinchart 	if (mt9p031->reset) {
3147c3be9f8SLaurent Pinchart 		gpiod_set_value(mt9p031->reset, 1);
315cb7a01acSMauro Carvalho Chehab 		usleep_range(1000, 2000);
316cb7a01acSMauro Carvalho Chehab 	}
317cb7a01acSMauro Carvalho Chehab 
31897f21276SLaurent Pinchart 	/* Bring up the supplies */
3197997196cSLaurent Pinchart 	ret = regulator_bulk_enable(ARRAY_SIZE(mt9p031->regulators),
3207997196cSLaurent Pinchart 				   mt9p031->regulators);
3217997196cSLaurent Pinchart 	if (ret < 0)
3227997196cSLaurent Pinchart 		return ret;
32397f21276SLaurent Pinchart 
324e8e45593SLaurent Pinchart 	/* Enable clock */
325ee2d16d7SLad, Prabhakar 	if (mt9p031->clk) {
326ee2d16d7SLad, Prabhakar 		ret = clk_prepare_enable(mt9p031->clk);
327ee2d16d7SLad, Prabhakar 		if (ret) {
328ee2d16d7SLad, Prabhakar 			regulator_bulk_disable(ARRAY_SIZE(mt9p031->regulators),
329ee2d16d7SLad, Prabhakar 					       mt9p031->regulators);
330ee2d16d7SLad, Prabhakar 			return ret;
331ee2d16d7SLad, Prabhakar 		}
332ee2d16d7SLad, Prabhakar 	}
333cb7a01acSMauro Carvalho Chehab 
334cb7a01acSMauro Carvalho Chehab 	/* Now RESET_BAR must be high */
3357c3be9f8SLaurent Pinchart 	if (mt9p031->reset) {
3367c3be9f8SLaurent Pinchart 		gpiod_set_value(mt9p031->reset, 0);
337cb7a01acSMauro Carvalho Chehab 		usleep_range(1000, 2000);
338cb7a01acSMauro Carvalho Chehab 	}
339cb7a01acSMauro Carvalho Chehab 
340cb7a01acSMauro Carvalho Chehab 	return 0;
341cb7a01acSMauro Carvalho Chehab }
342cb7a01acSMauro Carvalho Chehab 
343cb7a01acSMauro Carvalho Chehab static void mt9p031_power_off(struct mt9p031 *mt9p031)
344cb7a01acSMauro Carvalho Chehab {
3457c3be9f8SLaurent Pinchart 	if (mt9p031->reset) {
3467c3be9f8SLaurent Pinchart 		gpiod_set_value(mt9p031->reset, 1);
347cb7a01acSMauro Carvalho Chehab 		usleep_range(1000, 2000);
348cb7a01acSMauro Carvalho Chehab 	}
349cb7a01acSMauro Carvalho Chehab 
3507997196cSLaurent Pinchart 	regulator_bulk_disable(ARRAY_SIZE(mt9p031->regulators),
3517997196cSLaurent Pinchart 			       mt9p031->regulators);
35297f21276SLaurent Pinchart 
353d6749258SLaurent Pinchart 	clk_disable_unprepare(mt9p031->clk);
354cb7a01acSMauro Carvalho Chehab }
355cb7a01acSMauro Carvalho Chehab 
356cb7a01acSMauro Carvalho Chehab static int __mt9p031_set_power(struct mt9p031 *mt9p031, bool on)
357cb7a01acSMauro Carvalho Chehab {
358cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
359cb7a01acSMauro Carvalho Chehab 	int ret;
360cb7a01acSMauro Carvalho Chehab 
361cb7a01acSMauro Carvalho Chehab 	if (!on) {
362cb7a01acSMauro Carvalho Chehab 		mt9p031_power_off(mt9p031);
363cb7a01acSMauro Carvalho Chehab 		return 0;
364cb7a01acSMauro Carvalho Chehab 	}
365cb7a01acSMauro Carvalho Chehab 
366cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_power_on(mt9p031);
367cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
368cb7a01acSMauro Carvalho Chehab 		return ret;
369cb7a01acSMauro Carvalho Chehab 
370cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_reset(mt9p031);
371cb7a01acSMauro Carvalho Chehab 	if (ret < 0) {
372cb7a01acSMauro Carvalho Chehab 		dev_err(&client->dev, "Failed to reset the camera\n");
373cb7a01acSMauro Carvalho Chehab 		return ret;
374cb7a01acSMauro Carvalho Chehab 	}
375cb7a01acSMauro Carvalho Chehab 
376ae47ee5fSChristian Hemp 	/* Configure the pixel clock polarity */
377ae47ee5fSChristian Hemp 	if (mt9p031->pdata && mt9p031->pdata->pixclk_pol) {
378ae47ee5fSChristian Hemp 		ret = mt9p031_write(client, MT9P031_PIXEL_CLOCK_CONTROL,
379ae47ee5fSChristian Hemp 				MT9P031_PIXEL_CLOCK_INVERT);
380ae47ee5fSChristian Hemp 		if (ret < 0)
381ae47ee5fSChristian Hemp 			return ret;
382ae47ee5fSChristian Hemp 	}
383ae47ee5fSChristian Hemp 
384cb7a01acSMauro Carvalho Chehab 	return v4l2_ctrl_handler_setup(&mt9p031->ctrls);
385cb7a01acSMauro Carvalho Chehab }
386cb7a01acSMauro Carvalho Chehab 
387cb7a01acSMauro Carvalho Chehab /* -----------------------------------------------------------------------------
388cb7a01acSMauro Carvalho Chehab  * V4L2 subdev video operations
389cb7a01acSMauro Carvalho Chehab  */
390cb7a01acSMauro Carvalho Chehab 
391cb7a01acSMauro Carvalho Chehab static int mt9p031_set_params(struct mt9p031 *mt9p031)
392cb7a01acSMauro Carvalho Chehab {
393cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
394cb7a01acSMauro Carvalho Chehab 	struct v4l2_mbus_framefmt *format = &mt9p031->format;
395cb7a01acSMauro Carvalho Chehab 	const struct v4l2_rect *crop = &mt9p031->crop;
396cb7a01acSMauro Carvalho Chehab 	unsigned int hblank;
397cb7a01acSMauro Carvalho Chehab 	unsigned int vblank;
398cb7a01acSMauro Carvalho Chehab 	unsigned int xskip;
399cb7a01acSMauro Carvalho Chehab 	unsigned int yskip;
400cb7a01acSMauro Carvalho Chehab 	unsigned int xbin;
401cb7a01acSMauro Carvalho Chehab 	unsigned int ybin;
402cb7a01acSMauro Carvalho Chehab 	int ret;
403cb7a01acSMauro Carvalho Chehab 
404cb7a01acSMauro Carvalho Chehab 	/* Windows position and size.
405cb7a01acSMauro Carvalho Chehab 	 *
406cb7a01acSMauro Carvalho Chehab 	 * TODO: Make sure the start coordinates and window size match the
407cb7a01acSMauro Carvalho Chehab 	 * skipping, binning and mirroring (see description of registers 2 and 4
408cb7a01acSMauro Carvalho Chehab 	 * in table 13, and Binning section on page 41).
409cb7a01acSMauro Carvalho Chehab 	 */
410cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_COLUMN_START, crop->left);
411cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
412cb7a01acSMauro Carvalho Chehab 		return ret;
413cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_ROW_START, crop->top);
414cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
415cb7a01acSMauro Carvalho Chehab 		return ret;
416cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_WINDOW_WIDTH, crop->width - 1);
417cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
418cb7a01acSMauro Carvalho Chehab 		return ret;
419cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_WINDOW_HEIGHT, crop->height - 1);
420cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
421cb7a01acSMauro Carvalho Chehab 		return ret;
422cb7a01acSMauro Carvalho Chehab 
423cb7a01acSMauro Carvalho Chehab 	/* Row and column binning and skipping. Use the maximum binning value
424cb7a01acSMauro Carvalho Chehab 	 * compatible with the skipping settings.
425cb7a01acSMauro Carvalho Chehab 	 */
426cb7a01acSMauro Carvalho Chehab 	xskip = DIV_ROUND_CLOSEST(crop->width, format->width);
427cb7a01acSMauro Carvalho Chehab 	yskip = DIV_ROUND_CLOSEST(crop->height, format->height);
428cb7a01acSMauro Carvalho Chehab 	xbin = 1 << (ffs(xskip) - 1);
429cb7a01acSMauro Carvalho Chehab 	ybin = 1 << (ffs(yskip) - 1);
430cb7a01acSMauro Carvalho Chehab 
431cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_COLUMN_ADDRESS_MODE,
432cb7a01acSMauro Carvalho Chehab 			    ((xbin - 1) << 4) | (xskip - 1));
433cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
434cb7a01acSMauro Carvalho Chehab 		return ret;
435cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_ROW_ADDRESS_MODE,
436cb7a01acSMauro Carvalho Chehab 			    ((ybin - 1) << 4) | (yskip - 1));
437cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
438cb7a01acSMauro Carvalho Chehab 		return ret;
439cb7a01acSMauro Carvalho Chehab 
440cb7a01acSMauro Carvalho Chehab 	/* Blanking - use minimum value for horizontal blanking and default
441cb7a01acSMauro Carvalho Chehab 	 * value for vertical blanking.
442cb7a01acSMauro Carvalho Chehab 	 */
4435266c98bSLaurent Pinchart 	hblank = 346 * ybin + 64 + (80 >> min_t(unsigned int, xbin, 3));
444cb7a01acSMauro Carvalho Chehab 	vblank = MT9P031_VERTICAL_BLANK_DEF;
445cb7a01acSMauro Carvalho Chehab 
4465266c98bSLaurent Pinchart 	ret = mt9p031_write(client, MT9P031_HORIZONTAL_BLANK, hblank - 1);
447cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
448cb7a01acSMauro Carvalho Chehab 		return ret;
4495266c98bSLaurent Pinchart 	ret = mt9p031_write(client, MT9P031_VERTICAL_BLANK, vblank - 1);
450cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
451cb7a01acSMauro Carvalho Chehab 		return ret;
452cb7a01acSMauro Carvalho Chehab 
453cb7a01acSMauro Carvalho Chehab 	return ret;
454cb7a01acSMauro Carvalho Chehab }
455cb7a01acSMauro Carvalho Chehab 
456cb7a01acSMauro Carvalho Chehab static int mt9p031_s_stream(struct v4l2_subdev *subdev, int enable)
457cb7a01acSMauro Carvalho Chehab {
458cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
4590961ba6dSDirk Bender 	struct i2c_client *client = v4l2_get_subdevdata(subdev);
4600961ba6dSDirk Bender 	int val;
461cb7a01acSMauro Carvalho Chehab 	int ret;
462cb7a01acSMauro Carvalho Chehab 
463cb7a01acSMauro Carvalho Chehab 	if (!enable) {
4640961ba6dSDirk Bender 		/* enable pause restart */
4650961ba6dSDirk Bender 		val = MT9P031_FRAME_PAUSE_RESTART;
4660961ba6dSDirk Bender 		ret = mt9p031_write(client, MT9P031_RESTART, val);
4670961ba6dSDirk Bender 		if (ret < 0)
4680961ba6dSDirk Bender 			return ret;
4690961ba6dSDirk Bender 
4700961ba6dSDirk Bender 		/* enable restart + keep pause restart set */
4710961ba6dSDirk Bender 		val |= MT9P031_FRAME_RESTART;
4720961ba6dSDirk Bender 		ret = mt9p031_write(client, MT9P031_RESTART, val);
4730961ba6dSDirk Bender 		if (ret < 0)
4740961ba6dSDirk Bender 			return ret;
4750961ba6dSDirk Bender 
476cb7a01acSMauro Carvalho Chehab 		/* Stop sensor readout */
477cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_set_output_control(mt9p031,
478cb7a01acSMauro Carvalho Chehab 						 MT9P031_OUTPUT_CONTROL_CEN, 0);
479cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
480cb7a01acSMauro Carvalho Chehab 			return ret;
481cb7a01acSMauro Carvalho Chehab 
482cb7a01acSMauro Carvalho Chehab 		return mt9p031_pll_disable(mt9p031);
483cb7a01acSMauro Carvalho Chehab 	}
484cb7a01acSMauro Carvalho Chehab 
485cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_set_params(mt9p031);
486cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
487cb7a01acSMauro Carvalho Chehab 		return ret;
488cb7a01acSMauro Carvalho Chehab 
489cb7a01acSMauro Carvalho Chehab 	/* Switch to master "normal" mode */
490cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_set_output_control(mt9p031, 0,
491cb7a01acSMauro Carvalho Chehab 					 MT9P031_OUTPUT_CONTROL_CEN);
492cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
493cb7a01acSMauro Carvalho Chehab 		return ret;
494cb7a01acSMauro Carvalho Chehab 
4950961ba6dSDirk Bender 	/*
4960961ba6dSDirk Bender 	 * - clear pause restart
4970961ba6dSDirk Bender 	 * - don't clear restart as clearing restart manually can cause
4980961ba6dSDirk Bender 	 *   undefined behavior
4990961ba6dSDirk Bender 	 */
5000961ba6dSDirk Bender 	val = MT9P031_FRAME_RESTART;
5010961ba6dSDirk Bender 	ret = mt9p031_write(client, MT9P031_RESTART, val);
5020961ba6dSDirk Bender 	if (ret < 0)
5030961ba6dSDirk Bender 		return ret;
5040961ba6dSDirk Bender 
505cb7a01acSMauro Carvalho Chehab 	return mt9p031_pll_enable(mt9p031);
506cb7a01acSMauro Carvalho Chehab }
507cb7a01acSMauro Carvalho Chehab 
508cb7a01acSMauro Carvalho Chehab static int mt9p031_enum_mbus_code(struct v4l2_subdev *subdev,
5090d346d2aSTomi Valkeinen 				  struct v4l2_subdev_state *sd_state,
510cb7a01acSMauro Carvalho Chehab 				  struct v4l2_subdev_mbus_code_enum *code)
511cb7a01acSMauro Carvalho Chehab {
512cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
513cb7a01acSMauro Carvalho Chehab 
514cb7a01acSMauro Carvalho Chehab 	if (code->pad || code->index)
515cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
516cb7a01acSMauro Carvalho Chehab 
517cb7a01acSMauro Carvalho Chehab 	code->code = mt9p031->format.code;
518cb7a01acSMauro Carvalho Chehab 	return 0;
519cb7a01acSMauro Carvalho Chehab }
520cb7a01acSMauro Carvalho Chehab 
521cb7a01acSMauro Carvalho Chehab static int mt9p031_enum_frame_size(struct v4l2_subdev *subdev,
5220d346d2aSTomi Valkeinen 				   struct v4l2_subdev_state *sd_state,
523cb7a01acSMauro Carvalho Chehab 				   struct v4l2_subdev_frame_size_enum *fse)
524cb7a01acSMauro Carvalho Chehab {
525cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
526cb7a01acSMauro Carvalho Chehab 
527cb7a01acSMauro Carvalho Chehab 	if (fse->index >= 8 || fse->code != mt9p031->format.code)
528cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
529cb7a01acSMauro Carvalho Chehab 
530cb7a01acSMauro Carvalho Chehab 	fse->min_width = MT9P031_WINDOW_WIDTH_DEF
531cb7a01acSMauro Carvalho Chehab 		       / min_t(unsigned int, 7, fse->index + 1);
532cb7a01acSMauro Carvalho Chehab 	fse->max_width = fse->min_width;
533cb7a01acSMauro Carvalho Chehab 	fse->min_height = MT9P031_WINDOW_HEIGHT_DEF / (fse->index + 1);
534cb7a01acSMauro Carvalho Chehab 	fse->max_height = fse->min_height;
535cb7a01acSMauro Carvalho Chehab 
536cb7a01acSMauro Carvalho Chehab 	return 0;
537cb7a01acSMauro Carvalho Chehab }
538cb7a01acSMauro Carvalho Chehab 
539cb7a01acSMauro Carvalho Chehab static struct v4l2_mbus_framefmt *
5400d346d2aSTomi Valkeinen __mt9p031_get_pad_format(struct mt9p031 *mt9p031,
5410d346d2aSTomi Valkeinen 			 struct v4l2_subdev_state *sd_state,
542cb7a01acSMauro Carvalho Chehab 			 unsigned int pad, u32 which)
543cb7a01acSMauro Carvalho Chehab {
544cb7a01acSMauro Carvalho Chehab 	switch (which) {
545cb7a01acSMauro Carvalho Chehab 	case V4L2_SUBDEV_FORMAT_TRY:
5460d346d2aSTomi Valkeinen 		return v4l2_subdev_get_try_format(&mt9p031->subdev, sd_state,
5470d346d2aSTomi Valkeinen 						  pad);
548cb7a01acSMauro Carvalho Chehab 	case V4L2_SUBDEV_FORMAT_ACTIVE:
549cb7a01acSMauro Carvalho Chehab 		return &mt9p031->format;
550cb7a01acSMauro Carvalho Chehab 	default:
551cb7a01acSMauro Carvalho Chehab 		return NULL;
552cb7a01acSMauro Carvalho Chehab 	}
553cb7a01acSMauro Carvalho Chehab }
554cb7a01acSMauro Carvalho Chehab 
555cb7a01acSMauro Carvalho Chehab static struct v4l2_rect *
5560d346d2aSTomi Valkeinen __mt9p031_get_pad_crop(struct mt9p031 *mt9p031,
5570d346d2aSTomi Valkeinen 		       struct v4l2_subdev_state *sd_state,
558cb7a01acSMauro Carvalho Chehab 		       unsigned int pad, u32 which)
559cb7a01acSMauro Carvalho Chehab {
560cb7a01acSMauro Carvalho Chehab 	switch (which) {
561cb7a01acSMauro Carvalho Chehab 	case V4L2_SUBDEV_FORMAT_TRY:
5620d346d2aSTomi Valkeinen 		return v4l2_subdev_get_try_crop(&mt9p031->subdev, sd_state,
5630d346d2aSTomi Valkeinen 						pad);
564cb7a01acSMauro Carvalho Chehab 	case V4L2_SUBDEV_FORMAT_ACTIVE:
565cb7a01acSMauro Carvalho Chehab 		return &mt9p031->crop;
566cb7a01acSMauro Carvalho Chehab 	default:
567cb7a01acSMauro Carvalho Chehab 		return NULL;
568cb7a01acSMauro Carvalho Chehab 	}
569cb7a01acSMauro Carvalho Chehab }
570cb7a01acSMauro Carvalho Chehab 
571cb7a01acSMauro Carvalho Chehab static int mt9p031_get_format(struct v4l2_subdev *subdev,
5720d346d2aSTomi Valkeinen 			      struct v4l2_subdev_state *sd_state,
573cb7a01acSMauro Carvalho Chehab 			      struct v4l2_subdev_format *fmt)
574cb7a01acSMauro Carvalho Chehab {
575cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
576cb7a01acSMauro Carvalho Chehab 
5770d346d2aSTomi Valkeinen 	fmt->format = *__mt9p031_get_pad_format(mt9p031, sd_state, fmt->pad,
578cb7a01acSMauro Carvalho Chehab 						fmt->which);
579cb7a01acSMauro Carvalho Chehab 	return 0;
580cb7a01acSMauro Carvalho Chehab }
581cb7a01acSMauro Carvalho Chehab 
582cb7a01acSMauro Carvalho Chehab static int mt9p031_set_format(struct v4l2_subdev *subdev,
5830d346d2aSTomi Valkeinen 			      struct v4l2_subdev_state *sd_state,
584cb7a01acSMauro Carvalho Chehab 			      struct v4l2_subdev_format *format)
585cb7a01acSMauro Carvalho Chehab {
586cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
587cb7a01acSMauro Carvalho Chehab 	struct v4l2_mbus_framefmt *__format;
588cb7a01acSMauro Carvalho Chehab 	struct v4l2_rect *__crop;
589cb7a01acSMauro Carvalho Chehab 	unsigned int width;
590cb7a01acSMauro Carvalho Chehab 	unsigned int height;
591cb7a01acSMauro Carvalho Chehab 	unsigned int hratio;
592cb7a01acSMauro Carvalho Chehab 	unsigned int vratio;
593cb7a01acSMauro Carvalho Chehab 
5940d346d2aSTomi Valkeinen 	__crop = __mt9p031_get_pad_crop(mt9p031, sd_state, format->pad,
595cb7a01acSMauro Carvalho Chehab 					format->which);
596cb7a01acSMauro Carvalho Chehab 
597cb7a01acSMauro Carvalho Chehab 	/* Clamp the width and height to avoid dividing by zero. */
598cb7a01acSMauro Carvalho Chehab 	width = clamp_t(unsigned int, ALIGN(format->format.width, 2),
599f90580caSRicardo Ribalda 			max_t(unsigned int, __crop->width / 7,
600f90580caSRicardo Ribalda 			      MT9P031_WINDOW_WIDTH_MIN),
601cb7a01acSMauro Carvalho Chehab 			__crop->width);
602cb7a01acSMauro Carvalho Chehab 	height = clamp_t(unsigned int, ALIGN(format->format.height, 2),
603f90580caSRicardo Ribalda 			 max_t(unsigned int, __crop->height / 8,
604f90580caSRicardo Ribalda 			       MT9P031_WINDOW_HEIGHT_MIN),
605cb7a01acSMauro Carvalho Chehab 			 __crop->height);
606cb7a01acSMauro Carvalho Chehab 
607cb7a01acSMauro Carvalho Chehab 	hratio = DIV_ROUND_CLOSEST(__crop->width, width);
608cb7a01acSMauro Carvalho Chehab 	vratio = DIV_ROUND_CLOSEST(__crop->height, height);
609cb7a01acSMauro Carvalho Chehab 
6100d346d2aSTomi Valkeinen 	__format = __mt9p031_get_pad_format(mt9p031, sd_state, format->pad,
611cb7a01acSMauro Carvalho Chehab 					    format->which);
612cb7a01acSMauro Carvalho Chehab 	__format->width = __crop->width / hratio;
613cb7a01acSMauro Carvalho Chehab 	__format->height = __crop->height / vratio;
614cb7a01acSMauro Carvalho Chehab 
615cb7a01acSMauro Carvalho Chehab 	format->format = *__format;
616cb7a01acSMauro Carvalho Chehab 
617cb7a01acSMauro Carvalho Chehab 	return 0;
618cb7a01acSMauro Carvalho Chehab }
619cb7a01acSMauro Carvalho Chehab 
6201a023febSHans Verkuil static int mt9p031_get_selection(struct v4l2_subdev *subdev,
6210d346d2aSTomi Valkeinen 				 struct v4l2_subdev_state *sd_state,
6221a023febSHans Verkuil 				 struct v4l2_subdev_selection *sel)
623cb7a01acSMauro Carvalho Chehab {
624cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
625cb7a01acSMauro Carvalho Chehab 
6261a023febSHans Verkuil 	if (sel->target != V4L2_SEL_TGT_CROP)
6271a023febSHans Verkuil 		return -EINVAL;
6281a023febSHans Verkuil 
6290d346d2aSTomi Valkeinen 	sel->r = *__mt9p031_get_pad_crop(mt9p031, sd_state, sel->pad,
6300d346d2aSTomi Valkeinen 					 sel->which);
631cb7a01acSMauro Carvalho Chehab 	return 0;
632cb7a01acSMauro Carvalho Chehab }
633cb7a01acSMauro Carvalho Chehab 
6341a023febSHans Verkuil static int mt9p031_set_selection(struct v4l2_subdev *subdev,
6350d346d2aSTomi Valkeinen 				 struct v4l2_subdev_state *sd_state,
6361a023febSHans Verkuil 				 struct v4l2_subdev_selection *sel)
637cb7a01acSMauro Carvalho Chehab {
638cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
639cb7a01acSMauro Carvalho Chehab 	struct v4l2_mbus_framefmt *__format;
640cb7a01acSMauro Carvalho Chehab 	struct v4l2_rect *__crop;
641cb7a01acSMauro Carvalho Chehab 	struct v4l2_rect rect;
642cb7a01acSMauro Carvalho Chehab 
6431a023febSHans Verkuil 	if (sel->target != V4L2_SEL_TGT_CROP)
6441a023febSHans Verkuil 		return -EINVAL;
6451a023febSHans Verkuil 
646cb7a01acSMauro Carvalho Chehab 	/* Clamp the crop rectangle boundaries and align them to a multiple of 2
647cb7a01acSMauro Carvalho Chehab 	 * pixels to ensure a GRBG Bayer pattern.
648cb7a01acSMauro Carvalho Chehab 	 */
6491a023febSHans Verkuil 	rect.left = clamp(ALIGN(sel->r.left, 2), MT9P031_COLUMN_START_MIN,
650cb7a01acSMauro Carvalho Chehab 			  MT9P031_COLUMN_START_MAX);
6511a023febSHans Verkuil 	rect.top = clamp(ALIGN(sel->r.top, 2), MT9P031_ROW_START_MIN,
652cb7a01acSMauro Carvalho Chehab 			 MT9P031_ROW_START_MAX);
6531a023febSHans Verkuil 	rect.width = clamp_t(unsigned int, ALIGN(sel->r.width, 2),
654cb7a01acSMauro Carvalho Chehab 			     MT9P031_WINDOW_WIDTH_MIN,
655cb7a01acSMauro Carvalho Chehab 			     MT9P031_WINDOW_WIDTH_MAX);
6561a023febSHans Verkuil 	rect.height = clamp_t(unsigned int, ALIGN(sel->r.height, 2),
657cb7a01acSMauro Carvalho Chehab 			      MT9P031_WINDOW_HEIGHT_MIN,
658cb7a01acSMauro Carvalho Chehab 			      MT9P031_WINDOW_HEIGHT_MAX);
659cb7a01acSMauro Carvalho Chehab 
660f90580caSRicardo Ribalda 	rect.width = min_t(unsigned int, rect.width,
661f90580caSRicardo Ribalda 			   MT9P031_PIXEL_ARRAY_WIDTH - rect.left);
662f90580caSRicardo Ribalda 	rect.height = min_t(unsigned int, rect.height,
663f90580caSRicardo Ribalda 			    MT9P031_PIXEL_ARRAY_HEIGHT - rect.top);
664cb7a01acSMauro Carvalho Chehab 
6650d346d2aSTomi Valkeinen 	__crop = __mt9p031_get_pad_crop(mt9p031, sd_state, sel->pad,
6660d346d2aSTomi Valkeinen 					sel->which);
667cb7a01acSMauro Carvalho Chehab 
668cb7a01acSMauro Carvalho Chehab 	if (rect.width != __crop->width || rect.height != __crop->height) {
669cb7a01acSMauro Carvalho Chehab 		/* Reset the output image size if the crop rectangle size has
670cb7a01acSMauro Carvalho Chehab 		 * been modified.
671cb7a01acSMauro Carvalho Chehab 		 */
6720d346d2aSTomi Valkeinen 		__format = __mt9p031_get_pad_format(mt9p031, sd_state,
6730d346d2aSTomi Valkeinen 						    sel->pad,
6741a023febSHans Verkuil 						    sel->which);
675cb7a01acSMauro Carvalho Chehab 		__format->width = rect.width;
676cb7a01acSMauro Carvalho Chehab 		__format->height = rect.height;
677cb7a01acSMauro Carvalho Chehab 	}
678cb7a01acSMauro Carvalho Chehab 
679cb7a01acSMauro Carvalho Chehab 	*__crop = rect;
6801a023febSHans Verkuil 	sel->r = rect;
681cb7a01acSMauro Carvalho Chehab 
682cb7a01acSMauro Carvalho Chehab 	return 0;
683cb7a01acSMauro Carvalho Chehab }
684cb7a01acSMauro Carvalho Chehab 
685*69681cd0SMarek Vasut static int mt9p031_init_cfg(struct v4l2_subdev *subdev,
686*69681cd0SMarek Vasut 			    struct v4l2_subdev_state *sd_state)
687*69681cd0SMarek Vasut {
688*69681cd0SMarek Vasut 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
689*69681cd0SMarek Vasut 	struct v4l2_mbus_framefmt *format;
690*69681cd0SMarek Vasut 	struct v4l2_rect *crop;
691*69681cd0SMarek Vasut 	const int which = sd_state == NULL ? V4L2_SUBDEV_FORMAT_ACTIVE :
692*69681cd0SMarek Vasut 					     V4L2_SUBDEV_FORMAT_TRY;
693*69681cd0SMarek Vasut 
694*69681cd0SMarek Vasut 	crop = __mt9p031_get_pad_crop(mt9p031, sd_state, 0, which);
695*69681cd0SMarek Vasut 	v4l2_subdev_get_try_crop(subdev, sd_state, 0);
696*69681cd0SMarek Vasut 	crop->left = MT9P031_COLUMN_START_DEF;
697*69681cd0SMarek Vasut 	crop->top = MT9P031_ROW_START_DEF;
698*69681cd0SMarek Vasut 	crop->width = MT9P031_WINDOW_WIDTH_DEF;
699*69681cd0SMarek Vasut 	crop->height = MT9P031_WINDOW_HEIGHT_DEF;
700*69681cd0SMarek Vasut 
701*69681cd0SMarek Vasut 	format = __mt9p031_get_pad_format(mt9p031, sd_state, 0, which);
702*69681cd0SMarek Vasut 
703*69681cd0SMarek Vasut 	if (mt9p031->model == MT9P031_MODEL_MONOCHROME)
704*69681cd0SMarek Vasut 		format->code = MEDIA_BUS_FMT_Y12_1X12;
705*69681cd0SMarek Vasut 	else
706*69681cd0SMarek Vasut 		format->code = MEDIA_BUS_FMT_SGRBG12_1X12;
707*69681cd0SMarek Vasut 
708*69681cd0SMarek Vasut 	format->width = MT9P031_WINDOW_WIDTH_DEF;
709*69681cd0SMarek Vasut 	format->height = MT9P031_WINDOW_HEIGHT_DEF;
710*69681cd0SMarek Vasut 	format->field = V4L2_FIELD_NONE;
711*69681cd0SMarek Vasut 	format->colorspace = V4L2_COLORSPACE_SRGB;
712*69681cd0SMarek Vasut 
713*69681cd0SMarek Vasut 	return 0;
714*69681cd0SMarek Vasut }
715*69681cd0SMarek Vasut 
716cb7a01acSMauro Carvalho Chehab /* -----------------------------------------------------------------------------
717cb7a01acSMauro Carvalho Chehab  * V4L2 subdev control operations
718cb7a01acSMauro Carvalho Chehab  */
719cb7a01acSMauro Carvalho Chehab 
720cb7a01acSMauro Carvalho Chehab #define V4L2_CID_BLC_AUTO		(V4L2_CID_USER_BASE | 0x1002)
721cb7a01acSMauro Carvalho Chehab #define V4L2_CID_BLC_TARGET_LEVEL	(V4L2_CID_USER_BASE | 0x1003)
722cb7a01acSMauro Carvalho Chehab #define V4L2_CID_BLC_ANALOG_OFFSET	(V4L2_CID_USER_BASE | 0x1004)
723cb7a01acSMauro Carvalho Chehab #define V4L2_CID_BLC_DIGITAL_OFFSET	(V4L2_CID_USER_BASE | 0x1005)
724cb7a01acSMauro Carvalho Chehab 
725535ec214SLaurent Pinchart static int mt9p031_restore_blc(struct mt9p031 *mt9p031)
726535ec214SLaurent Pinchart {
727535ec214SLaurent Pinchart 	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
728535ec214SLaurent Pinchart 	int ret;
729535ec214SLaurent Pinchart 
730535ec214SLaurent Pinchart 	if (mt9p031->blc_auto->cur.val != 0) {
731535ec214SLaurent Pinchart 		ret = mt9p031_set_mode2(mt9p031, 0,
732535ec214SLaurent Pinchart 					MT9P031_READ_MODE_2_ROW_BLC);
733535ec214SLaurent Pinchart 		if (ret < 0)
734535ec214SLaurent Pinchart 			return ret;
735535ec214SLaurent Pinchart 	}
736535ec214SLaurent Pinchart 
737535ec214SLaurent Pinchart 	if (mt9p031->blc_offset->cur.val != 0) {
738535ec214SLaurent Pinchart 		ret = mt9p031_write(client, MT9P031_ROW_BLACK_TARGET,
739535ec214SLaurent Pinchart 				    mt9p031->blc_offset->cur.val);
740535ec214SLaurent Pinchart 		if (ret < 0)
741535ec214SLaurent Pinchart 			return ret;
742535ec214SLaurent Pinchart 	}
743535ec214SLaurent Pinchart 
744535ec214SLaurent Pinchart 	return 0;
745535ec214SLaurent Pinchart }
746535ec214SLaurent Pinchart 
747cb7a01acSMauro Carvalho Chehab static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl)
748cb7a01acSMauro Carvalho Chehab {
749cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 =
750cb7a01acSMauro Carvalho Chehab 			container_of(ctrl->handler, struct mt9p031, ctrls);
751cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
752cb7a01acSMauro Carvalho Chehab 	u16 data;
753cb7a01acSMauro Carvalho Chehab 	int ret;
754cb7a01acSMauro Carvalho Chehab 
7558bf54c43SLaurent Pinchart 	if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE)
7568bf54c43SLaurent Pinchart 		return 0;
7578bf54c43SLaurent Pinchart 
758cb7a01acSMauro Carvalho Chehab 	switch (ctrl->id) {
759cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_EXPOSURE:
760cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_write(client, MT9P031_SHUTTER_WIDTH_UPPER,
761cb7a01acSMauro Carvalho Chehab 				    (ctrl->val >> 16) & 0xffff);
762cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
763cb7a01acSMauro Carvalho Chehab 			return ret;
764cb7a01acSMauro Carvalho Chehab 
765cb7a01acSMauro Carvalho Chehab 		return mt9p031_write(client, MT9P031_SHUTTER_WIDTH_LOWER,
766cb7a01acSMauro Carvalho Chehab 				     ctrl->val & 0xffff);
767cb7a01acSMauro Carvalho Chehab 
768cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_GAIN:
769cb7a01acSMauro Carvalho Chehab 		/* Gain is controlled by 2 analog stages and a digital stage.
770cb7a01acSMauro Carvalho Chehab 		 * Valid values for the 3 stages are
771cb7a01acSMauro Carvalho Chehab 		 *
772cb7a01acSMauro Carvalho Chehab 		 * Stage                Min     Max     Step
773cb7a01acSMauro Carvalho Chehab 		 * ------------------------------------------
774cb7a01acSMauro Carvalho Chehab 		 * First analog stage   x1      x2      1
775cb7a01acSMauro Carvalho Chehab 		 * Second analog stage  x1      x4      0.125
776cb7a01acSMauro Carvalho Chehab 		 * Digital stage        x1      x16     0.125
777cb7a01acSMauro Carvalho Chehab 		 *
778cb7a01acSMauro Carvalho Chehab 		 * To minimize noise, the gain stages should be used in the
779cb7a01acSMauro Carvalho Chehab 		 * second analog stage, first analog stage, digital stage order.
780cb7a01acSMauro Carvalho Chehab 		 * Gain from a previous stage should be pushed to its maximum
781cb7a01acSMauro Carvalho Chehab 		 * value before the next stage is used.
782cb7a01acSMauro Carvalho Chehab 		 */
783cb7a01acSMauro Carvalho Chehab 		if (ctrl->val <= 32) {
784cb7a01acSMauro Carvalho Chehab 			data = ctrl->val;
785cb7a01acSMauro Carvalho Chehab 		} else if (ctrl->val <= 64) {
786cb7a01acSMauro Carvalho Chehab 			ctrl->val &= ~1;
787cb7a01acSMauro Carvalho Chehab 			data = (1 << 6) | (ctrl->val >> 1);
788cb7a01acSMauro Carvalho Chehab 		} else {
789cb7a01acSMauro Carvalho Chehab 			ctrl->val &= ~7;
790cb7a01acSMauro Carvalho Chehab 			data = ((ctrl->val - 64) << 5) | (1 << 6) | 32;
791cb7a01acSMauro Carvalho Chehab 		}
792cb7a01acSMauro Carvalho Chehab 
793cb7a01acSMauro Carvalho Chehab 		return mt9p031_write(client, MT9P031_GLOBAL_GAIN, data);
794cb7a01acSMauro Carvalho Chehab 
795cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_HFLIP:
796cb7a01acSMauro Carvalho Chehab 		if (ctrl->val)
797cb7a01acSMauro Carvalho Chehab 			return mt9p031_set_mode2(mt9p031,
798cb7a01acSMauro Carvalho Chehab 					0, MT9P031_READ_MODE_2_COL_MIR);
799cb7a01acSMauro Carvalho Chehab 		else
800cb7a01acSMauro Carvalho Chehab 			return mt9p031_set_mode2(mt9p031,
801cb7a01acSMauro Carvalho Chehab 					MT9P031_READ_MODE_2_COL_MIR, 0);
802cb7a01acSMauro Carvalho Chehab 
803cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_VFLIP:
804cb7a01acSMauro Carvalho Chehab 		if (ctrl->val)
805cb7a01acSMauro Carvalho Chehab 			return mt9p031_set_mode2(mt9p031,
806cb7a01acSMauro Carvalho Chehab 					0, MT9P031_READ_MODE_2_ROW_MIR);
807cb7a01acSMauro Carvalho Chehab 		else
808cb7a01acSMauro Carvalho Chehab 			return mt9p031_set_mode2(mt9p031,
809cb7a01acSMauro Carvalho Chehab 					MT9P031_READ_MODE_2_ROW_MIR, 0);
810cb7a01acSMauro Carvalho Chehab 
811cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_TEST_PATTERN:
8128bf54c43SLaurent Pinchart 		/* The digital side of the Black Level Calibration function must
8138bf54c43SLaurent Pinchart 		 * be disabled when generating a test pattern to avoid artifacts
8148bf54c43SLaurent Pinchart 		 * in the image. Activate (deactivate) the BLC-related controls
8158bf54c43SLaurent Pinchart 		 * when the test pattern is enabled (disabled).
8168bf54c43SLaurent Pinchart 		 */
8178bf54c43SLaurent Pinchart 		v4l2_ctrl_activate(mt9p031->blc_auto, ctrl->val == 0);
8188bf54c43SLaurent Pinchart 		v4l2_ctrl_activate(mt9p031->blc_offset, ctrl->val == 0);
8198bf54c43SLaurent Pinchart 
820cb7a01acSMauro Carvalho Chehab 		if (!ctrl->val) {
8218bf54c43SLaurent Pinchart 			/* Restore the BLC settings. */
822535ec214SLaurent Pinchart 			ret = mt9p031_restore_blc(mt9p031);
823cb7a01acSMauro Carvalho Chehab 			if (ret < 0)
824cb7a01acSMauro Carvalho Chehab 				return ret;
825535ec214SLaurent Pinchart 
8260a0e78d1SStefan Riedmueller 			return mt9p031_write(client, MT9P031_TEST_PATTERN, 0);
827cb7a01acSMauro Carvalho Chehab 		}
828cb7a01acSMauro Carvalho Chehab 
829cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_write(client, MT9P031_TEST_PATTERN_GREEN, 0x05a0);
830cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
831cb7a01acSMauro Carvalho Chehab 			return ret;
832cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_write(client, MT9P031_TEST_PATTERN_RED, 0x0a50);
833cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
834cb7a01acSMauro Carvalho Chehab 			return ret;
835cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_write(client, MT9P031_TEST_PATTERN_BLUE, 0x0aa0);
836cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
837cb7a01acSMauro Carvalho Chehab 			return ret;
838cb7a01acSMauro Carvalho Chehab 
8398bf54c43SLaurent Pinchart 		/* Disable digital BLC when generating a test pattern. */
840cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_set_mode2(mt9p031, MT9P031_READ_MODE_2_ROW_BLC,
841cb7a01acSMauro Carvalho Chehab 					0);
842cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
843cb7a01acSMauro Carvalho Chehab 			return ret;
844cb7a01acSMauro Carvalho Chehab 
845cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET, 0);
846cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
847cb7a01acSMauro Carvalho Chehab 			return ret;
848cb7a01acSMauro Carvalho Chehab 
849cb7a01acSMauro Carvalho Chehab 		return mt9p031_write(client, MT9P031_TEST_PATTERN,
850cb7a01acSMauro Carvalho Chehab 				((ctrl->val - 1) << MT9P031_TEST_PATTERN_SHIFT)
851cb7a01acSMauro Carvalho Chehab 				| MT9P031_TEST_PATTERN_ENABLE);
852cb7a01acSMauro Carvalho Chehab 
853cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_BLC_AUTO:
854cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_set_mode2(mt9p031,
855cb7a01acSMauro Carvalho Chehab 				ctrl->val ? 0 : MT9P031_READ_MODE_2_ROW_BLC,
856cb7a01acSMauro Carvalho Chehab 				ctrl->val ? MT9P031_READ_MODE_2_ROW_BLC : 0);
857cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
858cb7a01acSMauro Carvalho Chehab 			return ret;
859cb7a01acSMauro Carvalho Chehab 
860cb7a01acSMauro Carvalho Chehab 		return mt9p031_write(client, MT9P031_BLACK_LEVEL_CALIBRATION,
861cb7a01acSMauro Carvalho Chehab 				     ctrl->val ? 0 : MT9P031_BLC_MANUAL_BLC);
862cb7a01acSMauro Carvalho Chehab 
863cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_BLC_TARGET_LEVEL:
864cb7a01acSMauro Carvalho Chehab 		return mt9p031_write(client, MT9P031_ROW_BLACK_TARGET,
865cb7a01acSMauro Carvalho Chehab 				     ctrl->val);
866cb7a01acSMauro Carvalho Chehab 
867cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_BLC_ANALOG_OFFSET:
868cb7a01acSMauro Carvalho Chehab 		data = ctrl->val & ((1 << 9) - 1);
869cb7a01acSMauro Carvalho Chehab 
870cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_write(client, MT9P031_GREEN1_OFFSET, data);
871cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
872cb7a01acSMauro Carvalho Chehab 			return ret;
873cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_write(client, MT9P031_GREEN2_OFFSET, data);
874cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
875cb7a01acSMauro Carvalho Chehab 			return ret;
876cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_write(client, MT9P031_RED_OFFSET, data);
877cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
878cb7a01acSMauro Carvalho Chehab 			return ret;
879cb7a01acSMauro Carvalho Chehab 		return mt9p031_write(client, MT9P031_BLUE_OFFSET, data);
880cb7a01acSMauro Carvalho Chehab 
881cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_BLC_DIGITAL_OFFSET:
882cb7a01acSMauro Carvalho Chehab 		return mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET,
883cb7a01acSMauro Carvalho Chehab 				     ctrl->val & ((1 << 12) - 1));
884cb7a01acSMauro Carvalho Chehab 	}
885cb7a01acSMauro Carvalho Chehab 
886cb7a01acSMauro Carvalho Chehab 	return 0;
887cb7a01acSMauro Carvalho Chehab }
888cb7a01acSMauro Carvalho Chehab 
889217bdb07SJulia Lawall static const struct v4l2_ctrl_ops mt9p031_ctrl_ops = {
890cb7a01acSMauro Carvalho Chehab 	.s_ctrl = mt9p031_s_ctrl,
891cb7a01acSMauro Carvalho Chehab };
892cb7a01acSMauro Carvalho Chehab 
893cb7a01acSMauro Carvalho Chehab static const char * const mt9p031_test_pattern_menu[] = {
894cb7a01acSMauro Carvalho Chehab 	"Disabled",
895cb7a01acSMauro Carvalho Chehab 	"Color Field",
896cb7a01acSMauro Carvalho Chehab 	"Horizontal Gradient",
897cb7a01acSMauro Carvalho Chehab 	"Vertical Gradient",
898cb7a01acSMauro Carvalho Chehab 	"Diagonal Gradient",
899cb7a01acSMauro Carvalho Chehab 	"Classic Test Pattern",
900cb7a01acSMauro Carvalho Chehab 	"Walking 1s",
901cb7a01acSMauro Carvalho Chehab 	"Monochrome Horizontal Bars",
902cb7a01acSMauro Carvalho Chehab 	"Monochrome Vertical Bars",
903cb7a01acSMauro Carvalho Chehab 	"Vertical Color Bars",
904cb7a01acSMauro Carvalho Chehab };
905cb7a01acSMauro Carvalho Chehab 
906cb7a01acSMauro Carvalho Chehab static const struct v4l2_ctrl_config mt9p031_ctrls[] = {
907cb7a01acSMauro Carvalho Chehab 	{
908cb7a01acSMauro Carvalho Chehab 		.ops		= &mt9p031_ctrl_ops,
909cb7a01acSMauro Carvalho Chehab 		.id		= V4L2_CID_BLC_AUTO,
910cb7a01acSMauro Carvalho Chehab 		.type		= V4L2_CTRL_TYPE_BOOLEAN,
911cb7a01acSMauro Carvalho Chehab 		.name		= "BLC, Auto",
912cb7a01acSMauro Carvalho Chehab 		.min		= 0,
913cb7a01acSMauro Carvalho Chehab 		.max		= 1,
914cb7a01acSMauro Carvalho Chehab 		.step		= 1,
915cb7a01acSMauro Carvalho Chehab 		.def		= 1,
916cb7a01acSMauro Carvalho Chehab 		.flags		= 0,
917cb7a01acSMauro Carvalho Chehab 	}, {
918cb7a01acSMauro Carvalho Chehab 		.ops		= &mt9p031_ctrl_ops,
919cb7a01acSMauro Carvalho Chehab 		.id		= V4L2_CID_BLC_TARGET_LEVEL,
920cb7a01acSMauro Carvalho Chehab 		.type		= V4L2_CTRL_TYPE_INTEGER,
921cb7a01acSMauro Carvalho Chehab 		.name		= "BLC Target Level",
922cb7a01acSMauro Carvalho Chehab 		.min		= 0,
923cb7a01acSMauro Carvalho Chehab 		.max		= 4095,
924cb7a01acSMauro Carvalho Chehab 		.step		= 1,
925cb7a01acSMauro Carvalho Chehab 		.def		= 168,
926cb7a01acSMauro Carvalho Chehab 		.flags		= 0,
927cb7a01acSMauro Carvalho Chehab 	}, {
928cb7a01acSMauro Carvalho Chehab 		.ops		= &mt9p031_ctrl_ops,
929cb7a01acSMauro Carvalho Chehab 		.id		= V4L2_CID_BLC_ANALOG_OFFSET,
930cb7a01acSMauro Carvalho Chehab 		.type		= V4L2_CTRL_TYPE_INTEGER,
931cb7a01acSMauro Carvalho Chehab 		.name		= "BLC Analog Offset",
932cb7a01acSMauro Carvalho Chehab 		.min		= -255,
933cb7a01acSMauro Carvalho Chehab 		.max		= 255,
934cb7a01acSMauro Carvalho Chehab 		.step		= 1,
935cb7a01acSMauro Carvalho Chehab 		.def		= 32,
936cb7a01acSMauro Carvalho Chehab 		.flags		= 0,
937cb7a01acSMauro Carvalho Chehab 	}, {
938cb7a01acSMauro Carvalho Chehab 		.ops		= &mt9p031_ctrl_ops,
939cb7a01acSMauro Carvalho Chehab 		.id		= V4L2_CID_BLC_DIGITAL_OFFSET,
940cb7a01acSMauro Carvalho Chehab 		.type		= V4L2_CTRL_TYPE_INTEGER,
941cb7a01acSMauro Carvalho Chehab 		.name		= "BLC Digital Offset",
942cb7a01acSMauro Carvalho Chehab 		.min		= -2048,
943cb7a01acSMauro Carvalho Chehab 		.max		= 2047,
944cb7a01acSMauro Carvalho Chehab 		.step		= 1,
945cb7a01acSMauro Carvalho Chehab 		.def		= 40,
946cb7a01acSMauro Carvalho Chehab 		.flags		= 0,
947cb7a01acSMauro Carvalho Chehab 	}
948cb7a01acSMauro Carvalho Chehab };
949cb7a01acSMauro Carvalho Chehab 
950cb7a01acSMauro Carvalho Chehab /* -----------------------------------------------------------------------------
951cb7a01acSMauro Carvalho Chehab  * V4L2 subdev core operations
952cb7a01acSMauro Carvalho Chehab  */
953cb7a01acSMauro Carvalho Chehab 
954cb7a01acSMauro Carvalho Chehab static int mt9p031_set_power(struct v4l2_subdev *subdev, int on)
955cb7a01acSMauro Carvalho Chehab {
956cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
957cb7a01acSMauro Carvalho Chehab 	int ret = 0;
958cb7a01acSMauro Carvalho Chehab 
959cb7a01acSMauro Carvalho Chehab 	mutex_lock(&mt9p031->power_lock);
960cb7a01acSMauro Carvalho Chehab 
961cb7a01acSMauro Carvalho Chehab 	/* If the power count is modified from 0 to != 0 or from != 0 to 0,
962cb7a01acSMauro Carvalho Chehab 	 * update the power state.
963cb7a01acSMauro Carvalho Chehab 	 */
964cb7a01acSMauro Carvalho Chehab 	if (mt9p031->power_count == !on) {
965cb7a01acSMauro Carvalho Chehab 		ret = __mt9p031_set_power(mt9p031, !!on);
966cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
967cb7a01acSMauro Carvalho Chehab 			goto out;
968cb7a01acSMauro Carvalho Chehab 	}
969cb7a01acSMauro Carvalho Chehab 
970cb7a01acSMauro Carvalho Chehab 	/* Update the power count. */
971cb7a01acSMauro Carvalho Chehab 	mt9p031->power_count += on ? 1 : -1;
972cb7a01acSMauro Carvalho Chehab 	WARN_ON(mt9p031->power_count < 0);
973cb7a01acSMauro Carvalho Chehab 
974cb7a01acSMauro Carvalho Chehab out:
975cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&mt9p031->power_lock);
976cb7a01acSMauro Carvalho Chehab 	return ret;
977cb7a01acSMauro Carvalho Chehab }
978cb7a01acSMauro Carvalho Chehab 
979cb7a01acSMauro Carvalho Chehab /* -----------------------------------------------------------------------------
980cb7a01acSMauro Carvalho Chehab  * V4L2 subdev internal operations
981cb7a01acSMauro Carvalho Chehab  */
982cb7a01acSMauro Carvalho Chehab 
983cb7a01acSMauro Carvalho Chehab static int mt9p031_registered(struct v4l2_subdev *subdev)
984cb7a01acSMauro Carvalho Chehab {
985cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(subdev);
986cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
987cb7a01acSMauro Carvalho Chehab 	s32 data;
988cb7a01acSMauro Carvalho Chehab 	int ret;
989cb7a01acSMauro Carvalho Chehab 
990cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_power_on(mt9p031);
991cb7a01acSMauro Carvalho Chehab 	if (ret < 0) {
992cb7a01acSMauro Carvalho Chehab 		dev_err(&client->dev, "MT9P031 power up failed\n");
993cb7a01acSMauro Carvalho Chehab 		return ret;
994cb7a01acSMauro Carvalho Chehab 	}
995cb7a01acSMauro Carvalho Chehab 
996cb7a01acSMauro Carvalho Chehab 	/* Read out the chip version register */
997cb7a01acSMauro Carvalho Chehab 	data = mt9p031_read(client, MT9P031_CHIP_VERSION);
998bbcc9fa0SGuennadi Liakhovetski 	mt9p031_power_off(mt9p031);
999bbcc9fa0SGuennadi Liakhovetski 
1000cb7a01acSMauro Carvalho Chehab 	if (data != MT9P031_CHIP_VERSION_VALUE) {
1001cb7a01acSMauro Carvalho Chehab 		dev_err(&client->dev, "MT9P031 not detected, wrong version "
1002cb7a01acSMauro Carvalho Chehab 			"0x%04x\n", data);
1003cb7a01acSMauro Carvalho Chehab 		return -ENODEV;
1004cb7a01acSMauro Carvalho Chehab 	}
1005cb7a01acSMauro Carvalho Chehab 
1006cb7a01acSMauro Carvalho Chehab 	dev_info(&client->dev, "MT9P031 detected at address 0x%02x\n",
1007cb7a01acSMauro Carvalho Chehab 		 client->addr);
1008cb7a01acSMauro Carvalho Chehab 
1009bbcc9fa0SGuennadi Liakhovetski 	return 0;
1010cb7a01acSMauro Carvalho Chehab }
1011cb7a01acSMauro Carvalho Chehab 
1012cb7a01acSMauro Carvalho Chehab static int mt9p031_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
1013cb7a01acSMauro Carvalho Chehab {
1014cb7a01acSMauro Carvalho Chehab 	return mt9p031_set_power(subdev, 1);
1015cb7a01acSMauro Carvalho Chehab }
1016cb7a01acSMauro Carvalho Chehab 
1017cb7a01acSMauro Carvalho Chehab static int mt9p031_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
1018cb7a01acSMauro Carvalho Chehab {
1019cb7a01acSMauro Carvalho Chehab 	return mt9p031_set_power(subdev, 0);
1020cb7a01acSMauro Carvalho Chehab }
1021cb7a01acSMauro Carvalho Chehab 
10227c137c60SBhumika Goyal static const struct v4l2_subdev_core_ops mt9p031_subdev_core_ops = {
1023cb7a01acSMauro Carvalho Chehab 	.s_power        = mt9p031_set_power,
1024cb7a01acSMauro Carvalho Chehab };
1025cb7a01acSMauro Carvalho Chehab 
10267c137c60SBhumika Goyal static const struct v4l2_subdev_video_ops mt9p031_subdev_video_ops = {
1027cb7a01acSMauro Carvalho Chehab 	.s_stream       = mt9p031_s_stream,
1028cb7a01acSMauro Carvalho Chehab };
1029cb7a01acSMauro Carvalho Chehab 
10307c137c60SBhumika Goyal static const struct v4l2_subdev_pad_ops mt9p031_subdev_pad_ops = {
1031*69681cd0SMarek Vasut 	.init_cfg = mt9p031_init_cfg,
1032cb7a01acSMauro Carvalho Chehab 	.enum_mbus_code = mt9p031_enum_mbus_code,
1033cb7a01acSMauro Carvalho Chehab 	.enum_frame_size = mt9p031_enum_frame_size,
1034cb7a01acSMauro Carvalho Chehab 	.get_fmt = mt9p031_get_format,
1035cb7a01acSMauro Carvalho Chehab 	.set_fmt = mt9p031_set_format,
10361a023febSHans Verkuil 	.get_selection = mt9p031_get_selection,
10371a023febSHans Verkuil 	.set_selection = mt9p031_set_selection,
1038cb7a01acSMauro Carvalho Chehab };
1039cb7a01acSMauro Carvalho Chehab 
10407c137c60SBhumika Goyal static const struct v4l2_subdev_ops mt9p031_subdev_ops = {
1041cb7a01acSMauro Carvalho Chehab 	.core   = &mt9p031_subdev_core_ops,
1042cb7a01acSMauro Carvalho Chehab 	.video  = &mt9p031_subdev_video_ops,
1043cb7a01acSMauro Carvalho Chehab 	.pad    = &mt9p031_subdev_pad_ops,
1044cb7a01acSMauro Carvalho Chehab };
1045cb7a01acSMauro Carvalho Chehab 
1046cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_internal_ops mt9p031_subdev_internal_ops = {
1047cb7a01acSMauro Carvalho Chehab 	.registered = mt9p031_registered,
1048cb7a01acSMauro Carvalho Chehab 	.open = mt9p031_open,
1049cb7a01acSMauro Carvalho Chehab 	.close = mt9p031_close,
1050cb7a01acSMauro Carvalho Chehab };
1051cb7a01acSMauro Carvalho Chehab 
1052cb7a01acSMauro Carvalho Chehab /* -----------------------------------------------------------------------------
1053cb7a01acSMauro Carvalho Chehab  * Driver initialization and probing
1054cb7a01acSMauro Carvalho Chehab  */
1055cb7a01acSMauro Carvalho Chehab 
10568d4da37cSLad, Prabhakar static struct mt9p031_platform_data *
10578d4da37cSLad, Prabhakar mt9p031_get_pdata(struct i2c_client *client)
10588d4da37cSLad, Prabhakar {
1059ae47ee5fSChristian Hemp 	struct mt9p031_platform_data *pdata = NULL;
10608d4da37cSLad, Prabhakar 	struct device_node *np;
1061ae47ee5fSChristian Hemp 	struct v4l2_fwnode_endpoint endpoint = {
1062ae47ee5fSChristian Hemp 		.bus_type = V4L2_MBUS_PARALLEL
1063ae47ee5fSChristian Hemp 	};
10648d4da37cSLad, Prabhakar 
10658d4da37cSLad, Prabhakar 	if (!IS_ENABLED(CONFIG_OF) || !client->dev.of_node)
10668d4da37cSLad, Prabhakar 		return client->dev.platform_data;
10678d4da37cSLad, Prabhakar 
1068fd9fdb78SPhilipp Zabel 	np = of_graph_get_next_endpoint(client->dev.of_node, NULL);
10698d4da37cSLad, Prabhakar 	if (!np)
10708d4da37cSLad, Prabhakar 		return NULL;
10718d4da37cSLad, Prabhakar 
1072ae47ee5fSChristian Hemp 	if (v4l2_fwnode_endpoint_parse(of_fwnode_handle(np), &endpoint) < 0)
1073ae47ee5fSChristian Hemp 		goto done;
1074ae47ee5fSChristian Hemp 
10758d4da37cSLad, Prabhakar 	pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
10768d4da37cSLad, Prabhakar 	if (!pdata)
10778d4da37cSLad, Prabhakar 		goto done;
10788d4da37cSLad, Prabhakar 
10798d4da37cSLad, Prabhakar 	of_property_read_u32(np, "input-clock-frequency", &pdata->ext_freq);
10808d4da37cSLad, Prabhakar 	of_property_read_u32(np, "pixel-clock-frequency", &pdata->target_freq);
10818d4da37cSLad, Prabhakar 
1082ae47ee5fSChristian Hemp 	pdata->pixclk_pol = !!(endpoint.bus.parallel.flags &
1083ae47ee5fSChristian Hemp 			       V4L2_MBUS_PCLK_SAMPLE_RISING);
1084ae47ee5fSChristian Hemp 
10858d4da37cSLad, Prabhakar done:
10868d4da37cSLad, Prabhakar 	of_node_put(np);
10878d4da37cSLad, Prabhakar 	return pdata;
10888d4da37cSLad, Prabhakar }
10898d4da37cSLad, Prabhakar 
1090cb7a01acSMauro Carvalho Chehab static int mt9p031_probe(struct i2c_client *client,
1091cb7a01acSMauro Carvalho Chehab 			 const struct i2c_device_id *did)
1092cb7a01acSMauro Carvalho Chehab {
10938d4da37cSLad, Prabhakar 	struct mt9p031_platform_data *pdata = mt9p031_get_pdata(client);
1094a8a3e813SWolfram Sang 	struct i2c_adapter *adapter = client->adapter;
1095cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031;
1096cb7a01acSMauro Carvalho Chehab 	unsigned int i;
1097cb7a01acSMauro Carvalho Chehab 	int ret;
1098cb7a01acSMauro Carvalho Chehab 
1099cb7a01acSMauro Carvalho Chehab 	if (pdata == NULL) {
1100cb7a01acSMauro Carvalho Chehab 		dev_err(&client->dev, "No platform data\n");
1101cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
1102cb7a01acSMauro Carvalho Chehab 	}
1103cb7a01acSMauro Carvalho Chehab 
1104cb7a01acSMauro Carvalho Chehab 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
1105cb7a01acSMauro Carvalho Chehab 		dev_warn(&client->dev,
1106cb7a01acSMauro Carvalho Chehab 			"I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
1107cb7a01acSMauro Carvalho Chehab 		return -EIO;
1108cb7a01acSMauro Carvalho Chehab 	}
1109cb7a01acSMauro Carvalho Chehab 
111037b9f211SLaurent Pinchart 	mt9p031 = devm_kzalloc(&client->dev, sizeof(*mt9p031), GFP_KERNEL);
1111cb7a01acSMauro Carvalho Chehab 	if (mt9p031 == NULL)
1112cb7a01acSMauro Carvalho Chehab 		return -ENOMEM;
1113cb7a01acSMauro Carvalho Chehab 
1114cb7a01acSMauro Carvalho Chehab 	mt9p031->pdata = pdata;
1115cb7a01acSMauro Carvalho Chehab 	mt9p031->output_control	= MT9P031_OUTPUT_CONTROL_DEF;
1116cb7a01acSMauro Carvalho Chehab 	mt9p031->mode2 = MT9P031_READ_MODE_2_ROW_BLC;
1117cb7a01acSMauro Carvalho Chehab 	mt9p031->model = did->driver_data;
1118cb7a01acSMauro Carvalho Chehab 
11197997196cSLaurent Pinchart 	mt9p031->regulators[0].supply = "vdd";
11207997196cSLaurent Pinchart 	mt9p031->regulators[1].supply = "vdd_io";
11217997196cSLaurent Pinchart 	mt9p031->regulators[2].supply = "vaa";
112297f21276SLaurent Pinchart 
11237997196cSLaurent Pinchart 	ret = devm_regulator_bulk_get(&client->dev, 3, mt9p031->regulators);
11247997196cSLaurent Pinchart 	if (ret < 0) {
112597f21276SLaurent Pinchart 		dev_err(&client->dev, "Unable to get regulators\n");
11267997196cSLaurent Pinchart 		return ret;
112797f21276SLaurent Pinchart 	}
112897f21276SLaurent Pinchart 
112915af4a53SLad, Prabhakar 	mutex_init(&mt9p031->power_lock);
113015af4a53SLad, Prabhakar 
1131b28d7017SLad, Prabhakar 	v4l2_ctrl_handler_init(&mt9p031->ctrls, ARRAY_SIZE(mt9p031_ctrls) + 6);
1132cb7a01acSMauro Carvalho Chehab 
1133cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
1134cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_EXPOSURE, MT9P031_SHUTTER_WIDTH_MIN,
1135cb7a01acSMauro Carvalho Chehab 			  MT9P031_SHUTTER_WIDTH_MAX, 1,
1136cb7a01acSMauro Carvalho Chehab 			  MT9P031_SHUTTER_WIDTH_DEF);
1137cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
1138cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_GAIN, MT9P031_GLOBAL_GAIN_MIN,
1139cb7a01acSMauro Carvalho Chehab 			  MT9P031_GLOBAL_GAIN_MAX, 1, MT9P031_GLOBAL_GAIN_DEF);
1140cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
1141cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_HFLIP, 0, 1, 1, 0);
1142cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
1143cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_VFLIP, 0, 1, 1, 0);
1144cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
1145cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_PIXEL_RATE, pdata->target_freq,
1146cb7a01acSMauro Carvalho Chehab 			  pdata->target_freq, 1, pdata->target_freq);
1147b28d7017SLad, Prabhakar 	v4l2_ctrl_new_std_menu_items(&mt9p031->ctrls, &mt9p031_ctrl_ops,
1148b28d7017SLad, Prabhakar 			  V4L2_CID_TEST_PATTERN,
1149b28d7017SLad, Prabhakar 			  ARRAY_SIZE(mt9p031_test_pattern_menu) - 1, 0,
1150b28d7017SLad, Prabhakar 			  0, mt9p031_test_pattern_menu);
1151cb7a01acSMauro Carvalho Chehab 
1152cb7a01acSMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(mt9p031_ctrls); ++i)
1153cb7a01acSMauro Carvalho Chehab 		v4l2_ctrl_new_custom(&mt9p031->ctrls, &mt9p031_ctrls[i], NULL);
1154cb7a01acSMauro Carvalho Chehab 
1155cb7a01acSMauro Carvalho Chehab 	mt9p031->subdev.ctrl_handler = &mt9p031->ctrls;
1156cb7a01acSMauro Carvalho Chehab 
1157cb7a01acSMauro Carvalho Chehab 	if (mt9p031->ctrls.error) {
1158cb7a01acSMauro Carvalho Chehab 		printk(KERN_INFO "%s: control initialization error %d\n",
1159cb7a01acSMauro Carvalho Chehab 		       __func__, mt9p031->ctrls.error);
1160cb7a01acSMauro Carvalho Chehab 		ret = mt9p031->ctrls.error;
1161cb7a01acSMauro Carvalho Chehab 		goto done;
1162cb7a01acSMauro Carvalho Chehab 	}
1163cb7a01acSMauro Carvalho Chehab 
1164cb7a01acSMauro Carvalho Chehab 	mt9p031->blc_auto = v4l2_ctrl_find(&mt9p031->ctrls, V4L2_CID_BLC_AUTO);
1165cb7a01acSMauro Carvalho Chehab 	mt9p031->blc_offset = v4l2_ctrl_find(&mt9p031->ctrls,
1166cb7a01acSMauro Carvalho Chehab 					     V4L2_CID_BLC_DIGITAL_OFFSET);
1167cb7a01acSMauro Carvalho Chehab 
1168cb7a01acSMauro Carvalho Chehab 	v4l2_i2c_subdev_init(&mt9p031->subdev, client, &mt9p031_subdev_ops);
1169cb7a01acSMauro Carvalho Chehab 	mt9p031->subdev.internal_ops = &mt9p031_subdev_internal_ops;
1170cb7a01acSMauro Carvalho Chehab 
1171173bf6e5SHans Verkuil 	mt9p031->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR;
1172cb7a01acSMauro Carvalho Chehab 	mt9p031->pad.flags = MEDIA_PAD_FL_SOURCE;
1173ab22e77cSMauro Carvalho Chehab 	ret = media_entity_pads_init(&mt9p031->subdev.entity, 1, &mt9p031->pad);
1174cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
1175cb7a01acSMauro Carvalho Chehab 		goto done;
1176cb7a01acSMauro Carvalho Chehab 
1177cb7a01acSMauro Carvalho Chehab 	mt9p031->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
1178cb7a01acSMauro Carvalho Chehab 
1179*69681cd0SMarek Vasut 	ret = mt9p031_init_cfg(&mt9p031->subdev, NULL);
1180*69681cd0SMarek Vasut 	if (ret)
1181*69681cd0SMarek Vasut 		goto done;
1182cb7a01acSMauro Carvalho Chehab 
11837c3be9f8SLaurent Pinchart 	mt9p031->reset = devm_gpiod_get_optional(&client->dev, "reset",
11847c3be9f8SLaurent Pinchart 						 GPIOD_OUT_HIGH);
1185cb7a01acSMauro Carvalho Chehab 
1186d6749258SLaurent Pinchart 	ret = mt9p031_clk_setup(mt9p031);
11879012d088SLad, Prabhakar 	if (ret)
11889012d088SLad, Prabhakar 		goto done;
11899012d088SLad, Prabhakar 
11909012d088SLad, Prabhakar 	ret = v4l2_async_register_subdev(&mt9p031->subdev);
1191cb7a01acSMauro Carvalho Chehab 
1192cb7a01acSMauro Carvalho Chehab done:
1193cb7a01acSMauro Carvalho Chehab 	if (ret < 0) {
1194cb7a01acSMauro Carvalho Chehab 		v4l2_ctrl_handler_free(&mt9p031->ctrls);
1195cb7a01acSMauro Carvalho Chehab 		media_entity_cleanup(&mt9p031->subdev.entity);
119615af4a53SLad, Prabhakar 		mutex_destroy(&mt9p031->power_lock);
1197cb7a01acSMauro Carvalho Chehab 	}
1198cb7a01acSMauro Carvalho Chehab 
1199cb7a01acSMauro Carvalho Chehab 	return ret;
1200cb7a01acSMauro Carvalho Chehab }
1201cb7a01acSMauro Carvalho Chehab 
1202cb7a01acSMauro Carvalho Chehab static int mt9p031_remove(struct i2c_client *client)
1203cb7a01acSMauro Carvalho Chehab {
1204cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
1205cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
1206cb7a01acSMauro Carvalho Chehab 
1207cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_free(&mt9p031->ctrls);
12089012d088SLad, Prabhakar 	v4l2_async_unregister_subdev(subdev);
1209cb7a01acSMauro Carvalho Chehab 	media_entity_cleanup(&subdev->entity);
121015af4a53SLad, Prabhakar 	mutex_destroy(&mt9p031->power_lock);
1211cb7a01acSMauro Carvalho Chehab 
1212cb7a01acSMauro Carvalho Chehab 	return 0;
1213cb7a01acSMauro Carvalho Chehab }
1214cb7a01acSMauro Carvalho Chehab 
1215cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id mt9p031_id[] = {
121692fa766bSMarek Vasut 	{ "mt9p006", MT9P031_MODEL_COLOR },
1217cb7a01acSMauro Carvalho Chehab 	{ "mt9p031", MT9P031_MODEL_COLOR },
1218cb7a01acSMauro Carvalho Chehab 	{ "mt9p031m", MT9P031_MODEL_MONOCHROME },
1219cb7a01acSMauro Carvalho Chehab 	{ }
1220cb7a01acSMauro Carvalho Chehab };
1221cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, mt9p031_id);
1222cb7a01acSMauro Carvalho Chehab 
12238d4da37cSLad, Prabhakar #if IS_ENABLED(CONFIG_OF)
12248d4da37cSLad, Prabhakar static const struct of_device_id mt9p031_of_match[] = {
122592fa766bSMarek Vasut 	{ .compatible = "aptina,mt9p006", },
12268d4da37cSLad, Prabhakar 	{ .compatible = "aptina,mt9p031", },
12278d4da37cSLad, Prabhakar 	{ .compatible = "aptina,mt9p031m", },
12288d4da37cSLad, Prabhakar 	{ /* sentinel */ },
12298d4da37cSLad, Prabhakar };
12308d4da37cSLad, Prabhakar MODULE_DEVICE_TABLE(of, mt9p031_of_match);
12318d4da37cSLad, Prabhakar #endif
12328d4da37cSLad, Prabhakar 
1233cb7a01acSMauro Carvalho Chehab static struct i2c_driver mt9p031_i2c_driver = {
1234cb7a01acSMauro Carvalho Chehab 	.driver = {
12358d4da37cSLad, Prabhakar 		.of_match_table = of_match_ptr(mt9p031_of_match),
1236cb7a01acSMauro Carvalho Chehab 		.name = "mt9p031",
1237cb7a01acSMauro Carvalho Chehab 	},
1238cb7a01acSMauro Carvalho Chehab 	.probe          = mt9p031_probe,
1239cb7a01acSMauro Carvalho Chehab 	.remove         = mt9p031_remove,
1240cb7a01acSMauro Carvalho Chehab 	.id_table       = mt9p031_id,
1241cb7a01acSMauro Carvalho Chehab };
1242cb7a01acSMauro Carvalho Chehab 
1243cb7a01acSMauro Carvalho Chehab module_i2c_driver(mt9p031_i2c_driver);
1244cb7a01acSMauro Carvalho Chehab 
1245cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("Aptina MT9P031 Camera driver");
1246cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Bastian Hecht <hechtb@gmail.com>");
1247cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL v2");
1248