xref: /openbmc/linux/drivers/media/i2c/mt9p031.c (revision b9c18096f5948aae9c792aee9f355bfd64452569)
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>
30cb7a01acSMauro Carvalho Chehab #include <media/v4l2-subdev.h>
31cb7a01acSMauro Carvalho Chehab 
32cb7a01acSMauro Carvalho Chehab #include "aptina-pll.h"
33cb7a01acSMauro Carvalho Chehab 
34cb7a01acSMauro Carvalho Chehab #define MT9P031_PIXEL_ARRAY_WIDTH			2752
35cb7a01acSMauro Carvalho Chehab #define MT9P031_PIXEL_ARRAY_HEIGHT			2004
36cb7a01acSMauro Carvalho Chehab 
37cb7a01acSMauro Carvalho Chehab #define MT9P031_CHIP_VERSION				0x00
38cb7a01acSMauro Carvalho Chehab #define		MT9P031_CHIP_VERSION_VALUE		0x1801
39cb7a01acSMauro Carvalho Chehab #define MT9P031_ROW_START				0x01
40cb7a01acSMauro Carvalho Chehab #define		MT9P031_ROW_START_MIN			0
41cb7a01acSMauro Carvalho Chehab #define		MT9P031_ROW_START_MAX			2004
42cb7a01acSMauro Carvalho Chehab #define		MT9P031_ROW_START_DEF			54
43cb7a01acSMauro Carvalho Chehab #define MT9P031_COLUMN_START				0x02
44cb7a01acSMauro Carvalho Chehab #define		MT9P031_COLUMN_START_MIN		0
45cb7a01acSMauro Carvalho Chehab #define		MT9P031_COLUMN_START_MAX		2750
46cb7a01acSMauro Carvalho Chehab #define		MT9P031_COLUMN_START_DEF		16
47cb7a01acSMauro Carvalho Chehab #define MT9P031_WINDOW_HEIGHT				0x03
48cb7a01acSMauro Carvalho Chehab #define		MT9P031_WINDOW_HEIGHT_MIN		2
49cb7a01acSMauro Carvalho Chehab #define		MT9P031_WINDOW_HEIGHT_MAX		2006
50cb7a01acSMauro Carvalho Chehab #define		MT9P031_WINDOW_HEIGHT_DEF		1944
51cb7a01acSMauro Carvalho Chehab #define MT9P031_WINDOW_WIDTH				0x04
52cb7a01acSMauro Carvalho Chehab #define		MT9P031_WINDOW_WIDTH_MIN		2
53cb7a01acSMauro Carvalho Chehab #define		MT9P031_WINDOW_WIDTH_MAX		2752
54cb7a01acSMauro Carvalho Chehab #define		MT9P031_WINDOW_WIDTH_DEF		2592
55cb7a01acSMauro Carvalho Chehab #define MT9P031_HORIZONTAL_BLANK			0x05
56cb7a01acSMauro Carvalho Chehab #define		MT9P031_HORIZONTAL_BLANK_MIN		0
57cb7a01acSMauro Carvalho Chehab #define		MT9P031_HORIZONTAL_BLANK_MAX		4095
58cb7a01acSMauro Carvalho Chehab #define MT9P031_VERTICAL_BLANK				0x06
595266c98bSLaurent Pinchart #define		MT9P031_VERTICAL_BLANK_MIN		1
605266c98bSLaurent Pinchart #define		MT9P031_VERTICAL_BLANK_MAX		4096
615266c98bSLaurent Pinchart #define		MT9P031_VERTICAL_BLANK_DEF		26
62cb7a01acSMauro Carvalho Chehab #define MT9P031_OUTPUT_CONTROL				0x07
63cb7a01acSMauro Carvalho Chehab #define		MT9P031_OUTPUT_CONTROL_CEN		2
64cb7a01acSMauro Carvalho Chehab #define		MT9P031_OUTPUT_CONTROL_SYN		1
65cb7a01acSMauro Carvalho Chehab #define		MT9P031_OUTPUT_CONTROL_DEF		0x1f82
66cb7a01acSMauro Carvalho Chehab #define MT9P031_SHUTTER_WIDTH_UPPER			0x08
67cb7a01acSMauro Carvalho Chehab #define MT9P031_SHUTTER_WIDTH_LOWER			0x09
68cb7a01acSMauro Carvalho Chehab #define		MT9P031_SHUTTER_WIDTH_MIN		1
69cb7a01acSMauro Carvalho Chehab #define		MT9P031_SHUTTER_WIDTH_MAX		1048575
70cb7a01acSMauro Carvalho Chehab #define		MT9P031_SHUTTER_WIDTH_DEF		1943
71cb7a01acSMauro Carvalho Chehab #define	MT9P031_PLL_CONTROL				0x10
72cb7a01acSMauro Carvalho Chehab #define		MT9P031_PLL_CONTROL_PWROFF		0x0050
73cb7a01acSMauro Carvalho Chehab #define		MT9P031_PLL_CONTROL_PWRON		0x0051
74cb7a01acSMauro Carvalho Chehab #define		MT9P031_PLL_CONTROL_USEPLL		0x0052
75cb7a01acSMauro Carvalho Chehab #define	MT9P031_PLL_CONFIG_1				0x11
76cb7a01acSMauro Carvalho Chehab #define	MT9P031_PLL_CONFIG_2				0x12
77cb7a01acSMauro Carvalho Chehab #define MT9P031_PIXEL_CLOCK_CONTROL			0x0a
78a970449eSLaurent Pinchart #define		MT9P031_PIXEL_CLOCK_INVERT		(1 << 15)
79a970449eSLaurent Pinchart #define		MT9P031_PIXEL_CLOCK_SHIFT(n)		((n) << 8)
80a970449eSLaurent Pinchart #define		MT9P031_PIXEL_CLOCK_DIVIDE(n)		((n) << 0)
81cb7a01acSMauro Carvalho Chehab #define MT9P031_FRAME_RESTART				0x0b
82cb7a01acSMauro Carvalho Chehab #define MT9P031_SHUTTER_DELAY				0x0c
83cb7a01acSMauro Carvalho Chehab #define MT9P031_RST					0x0d
84cb7a01acSMauro Carvalho Chehab #define		MT9P031_RST_ENABLE			1
85cb7a01acSMauro Carvalho Chehab #define		MT9P031_RST_DISABLE			0
86cb7a01acSMauro Carvalho Chehab #define MT9P031_READ_MODE_1				0x1e
87cb7a01acSMauro Carvalho Chehab #define MT9P031_READ_MODE_2				0x20
88cb7a01acSMauro Carvalho Chehab #define		MT9P031_READ_MODE_2_ROW_MIR		(1 << 15)
89cb7a01acSMauro Carvalho Chehab #define		MT9P031_READ_MODE_2_COL_MIR		(1 << 14)
90cb7a01acSMauro Carvalho Chehab #define		MT9P031_READ_MODE_2_ROW_BLC		(1 << 6)
91cb7a01acSMauro Carvalho Chehab #define MT9P031_ROW_ADDRESS_MODE			0x22
92cb7a01acSMauro Carvalho Chehab #define MT9P031_COLUMN_ADDRESS_MODE			0x23
93cb7a01acSMauro Carvalho Chehab #define MT9P031_GLOBAL_GAIN				0x35
94cb7a01acSMauro Carvalho Chehab #define		MT9P031_GLOBAL_GAIN_MIN			8
95cb7a01acSMauro Carvalho Chehab #define		MT9P031_GLOBAL_GAIN_MAX			1024
96cb7a01acSMauro Carvalho Chehab #define		MT9P031_GLOBAL_GAIN_DEF			8
97cb7a01acSMauro Carvalho Chehab #define		MT9P031_GLOBAL_GAIN_MULT		(1 << 6)
98cb7a01acSMauro Carvalho Chehab #define MT9P031_ROW_BLACK_TARGET			0x49
99cb7a01acSMauro Carvalho Chehab #define MT9P031_ROW_BLACK_DEF_OFFSET			0x4b
100cb7a01acSMauro Carvalho Chehab #define MT9P031_GREEN1_OFFSET				0x60
101cb7a01acSMauro Carvalho Chehab #define MT9P031_GREEN2_OFFSET				0x61
102cb7a01acSMauro Carvalho Chehab #define MT9P031_BLACK_LEVEL_CALIBRATION			0x62
103cb7a01acSMauro Carvalho Chehab #define		MT9P031_BLC_MANUAL_BLC			(1 << 0)
104cb7a01acSMauro Carvalho Chehab #define MT9P031_RED_OFFSET				0x63
105cb7a01acSMauro Carvalho Chehab #define MT9P031_BLUE_OFFSET				0x64
106cb7a01acSMauro Carvalho Chehab #define MT9P031_TEST_PATTERN				0xa0
107cb7a01acSMauro Carvalho Chehab #define		MT9P031_TEST_PATTERN_SHIFT		3
108cb7a01acSMauro Carvalho Chehab #define		MT9P031_TEST_PATTERN_ENABLE		(1 << 0)
109cb7a01acSMauro Carvalho Chehab #define		MT9P031_TEST_PATTERN_DISABLE		(0 << 0)
110cb7a01acSMauro Carvalho Chehab #define MT9P031_TEST_PATTERN_GREEN			0xa1
111cb7a01acSMauro Carvalho Chehab #define MT9P031_TEST_PATTERN_RED			0xa2
112cb7a01acSMauro Carvalho Chehab #define MT9P031_TEST_PATTERN_BLUE			0xa3
113cb7a01acSMauro Carvalho Chehab 
114cb7a01acSMauro Carvalho Chehab enum mt9p031_model {
115cb7a01acSMauro Carvalho Chehab 	MT9P031_MODEL_COLOR,
116cb7a01acSMauro Carvalho Chehab 	MT9P031_MODEL_MONOCHROME,
117cb7a01acSMauro Carvalho Chehab };
118cb7a01acSMauro Carvalho Chehab 
119cb7a01acSMauro Carvalho Chehab struct mt9p031 {
120cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev subdev;
121cb7a01acSMauro Carvalho Chehab 	struct media_pad pad;
122cb7a01acSMauro Carvalho Chehab 	struct v4l2_rect crop;  /* Sensor window */
123cb7a01acSMauro Carvalho Chehab 	struct v4l2_mbus_framefmt format;
124cb7a01acSMauro Carvalho Chehab 	struct mt9p031_platform_data *pdata;
125cb7a01acSMauro Carvalho Chehab 	struct mutex power_lock; /* lock to protect power_count */
126cb7a01acSMauro Carvalho Chehab 	int power_count;
127cb7a01acSMauro Carvalho Chehab 
128d6749258SLaurent Pinchart 	struct clk *clk;
1297997196cSLaurent Pinchart 	struct regulator_bulk_data regulators[3];
13097f21276SLaurent Pinchart 
131cb7a01acSMauro Carvalho Chehab 	enum mt9p031_model model;
132cb7a01acSMauro Carvalho Chehab 	struct aptina_pll pll;
133a970449eSLaurent Pinchart 	unsigned int clk_div;
134a970449eSLaurent Pinchart 	bool use_pll;
1357c3be9f8SLaurent Pinchart 	struct gpio_desc *reset;
136cb7a01acSMauro Carvalho Chehab 
137cb7a01acSMauro Carvalho Chehab 	struct v4l2_ctrl_handler ctrls;
138cb7a01acSMauro Carvalho Chehab 	struct v4l2_ctrl *blc_auto;
139cb7a01acSMauro Carvalho Chehab 	struct v4l2_ctrl *blc_offset;
140cb7a01acSMauro Carvalho Chehab 
141cb7a01acSMauro Carvalho Chehab 	/* Registers cache */
142cb7a01acSMauro Carvalho Chehab 	u16 output_control;
143cb7a01acSMauro Carvalho Chehab 	u16 mode2;
144cb7a01acSMauro Carvalho Chehab };
145cb7a01acSMauro Carvalho Chehab 
146cb7a01acSMauro Carvalho Chehab static struct mt9p031 *to_mt9p031(struct v4l2_subdev *sd)
147cb7a01acSMauro Carvalho Chehab {
148cb7a01acSMauro Carvalho Chehab 	return container_of(sd, struct mt9p031, subdev);
149cb7a01acSMauro Carvalho Chehab }
150cb7a01acSMauro Carvalho Chehab 
151cb7a01acSMauro Carvalho Chehab static int mt9p031_read(struct i2c_client *client, u8 reg)
152cb7a01acSMauro Carvalho Chehab {
153cb7a01acSMauro Carvalho Chehab 	return i2c_smbus_read_word_swapped(client, reg);
154cb7a01acSMauro Carvalho Chehab }
155cb7a01acSMauro Carvalho Chehab 
156cb7a01acSMauro Carvalho Chehab static int mt9p031_write(struct i2c_client *client, u8 reg, u16 data)
157cb7a01acSMauro Carvalho Chehab {
158cb7a01acSMauro Carvalho Chehab 	return i2c_smbus_write_word_swapped(client, reg, data);
159cb7a01acSMauro Carvalho Chehab }
160cb7a01acSMauro Carvalho Chehab 
161cb7a01acSMauro Carvalho Chehab static int mt9p031_set_output_control(struct mt9p031 *mt9p031, u16 clear,
162cb7a01acSMauro Carvalho Chehab 				      u16 set)
163cb7a01acSMauro Carvalho Chehab {
164cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
165cb7a01acSMauro Carvalho Chehab 	u16 value = (mt9p031->output_control & ~clear) | set;
166cb7a01acSMauro Carvalho Chehab 	int ret;
167cb7a01acSMauro Carvalho Chehab 
168cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_OUTPUT_CONTROL, value);
169cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
170cb7a01acSMauro Carvalho Chehab 		return ret;
171cb7a01acSMauro Carvalho Chehab 
172cb7a01acSMauro Carvalho Chehab 	mt9p031->output_control = value;
173cb7a01acSMauro Carvalho Chehab 	return 0;
174cb7a01acSMauro Carvalho Chehab }
175cb7a01acSMauro Carvalho Chehab 
176cb7a01acSMauro Carvalho Chehab static int mt9p031_set_mode2(struct mt9p031 *mt9p031, u16 clear, u16 set)
177cb7a01acSMauro Carvalho Chehab {
178cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
179cb7a01acSMauro Carvalho Chehab 	u16 value = (mt9p031->mode2 & ~clear) | set;
180cb7a01acSMauro Carvalho Chehab 	int ret;
181cb7a01acSMauro Carvalho Chehab 
182cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_READ_MODE_2, value);
183cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
184cb7a01acSMauro Carvalho Chehab 		return ret;
185cb7a01acSMauro Carvalho Chehab 
186cb7a01acSMauro Carvalho Chehab 	mt9p031->mode2 = value;
187cb7a01acSMauro Carvalho Chehab 	return 0;
188cb7a01acSMauro Carvalho Chehab }
189cb7a01acSMauro Carvalho Chehab 
190cb7a01acSMauro Carvalho Chehab static int mt9p031_reset(struct mt9p031 *mt9p031)
191cb7a01acSMauro Carvalho Chehab {
192cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
193cb7a01acSMauro Carvalho Chehab 	int ret;
194cb7a01acSMauro Carvalho Chehab 
195cb7a01acSMauro Carvalho Chehab 	/* Disable chip output, synchronous option update */
196cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_RST, MT9P031_RST_ENABLE);
197cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
198cb7a01acSMauro Carvalho Chehab 		return ret;
199cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_RST, MT9P031_RST_DISABLE);
200cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
201cb7a01acSMauro Carvalho Chehab 		return ret;
202cb7a01acSMauro Carvalho Chehab 
203a970449eSLaurent Pinchart 	ret = mt9p031_write(client, MT9P031_PIXEL_CLOCK_CONTROL,
204a970449eSLaurent Pinchart 			    MT9P031_PIXEL_CLOCK_DIVIDE(mt9p031->clk_div));
205a970449eSLaurent Pinchart 	if (ret < 0)
206a970449eSLaurent Pinchart 		return ret;
207a970449eSLaurent Pinchart 
208cb7a01acSMauro Carvalho Chehab 	return mt9p031_set_output_control(mt9p031, MT9P031_OUTPUT_CONTROL_CEN,
209cb7a01acSMauro Carvalho Chehab 					  0);
210cb7a01acSMauro Carvalho Chehab }
211cb7a01acSMauro Carvalho Chehab 
212d6749258SLaurent Pinchart static int mt9p031_clk_setup(struct mt9p031 *mt9p031)
213cb7a01acSMauro Carvalho Chehab {
214cb7a01acSMauro Carvalho Chehab 	static const struct aptina_pll_limits limits = {
215cb7a01acSMauro Carvalho Chehab 		.ext_clock_min = 6000000,
216cb7a01acSMauro Carvalho Chehab 		.ext_clock_max = 27000000,
217cb7a01acSMauro Carvalho Chehab 		.int_clock_min = 2000000,
218cb7a01acSMauro Carvalho Chehab 		.int_clock_max = 13500000,
219cb7a01acSMauro Carvalho Chehab 		.out_clock_min = 180000000,
220cb7a01acSMauro Carvalho Chehab 		.out_clock_max = 360000000,
221cb7a01acSMauro Carvalho Chehab 		.pix_clock_max = 96000000,
222cb7a01acSMauro Carvalho Chehab 		.n_min = 1,
223cb7a01acSMauro Carvalho Chehab 		.n_max = 64,
224cb7a01acSMauro Carvalho Chehab 		.m_min = 16,
225cb7a01acSMauro Carvalho Chehab 		.m_max = 255,
226cb7a01acSMauro Carvalho Chehab 		.p1_min = 1,
227cb7a01acSMauro Carvalho Chehab 		.p1_max = 128,
228cb7a01acSMauro Carvalho Chehab 	};
229cb7a01acSMauro Carvalho Chehab 
230cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
231cb7a01acSMauro Carvalho Chehab 	struct mt9p031_platform_data *pdata = mt9p031->pdata;
232*b9c18096SEnrico Scholz 	unsigned long ext_freq;
233ee2d16d7SLad, Prabhakar 	int ret;
234cb7a01acSMauro Carvalho Chehab 
235d6749258SLaurent Pinchart 	mt9p031->clk = devm_clk_get(&client->dev, NULL);
236d6749258SLaurent Pinchart 	if (IS_ERR(mt9p031->clk))
237d6749258SLaurent Pinchart 		return PTR_ERR(mt9p031->clk);
238d6749258SLaurent Pinchart 
239ee2d16d7SLad, Prabhakar 	ret = clk_set_rate(mt9p031->clk, pdata->ext_freq);
240ee2d16d7SLad, Prabhakar 	if (ret < 0)
241ee2d16d7SLad, Prabhakar 		return ret;
242d6749258SLaurent Pinchart 
243*b9c18096SEnrico Scholz 	ext_freq = clk_get_rate(mt9p031->clk);
244*b9c18096SEnrico Scholz 
245a970449eSLaurent Pinchart 	/* If the external clock frequency is out of bounds for the PLL use the
246a970449eSLaurent Pinchart 	 * pixel clock divider only and disable the PLL.
247a970449eSLaurent Pinchart 	 */
248*b9c18096SEnrico Scholz 	if (ext_freq > limits.ext_clock_max) {
249a970449eSLaurent Pinchart 		unsigned int div;
250a970449eSLaurent Pinchart 
251*b9c18096SEnrico Scholz 		div = DIV_ROUND_UP(ext_freq, pdata->target_freq);
252a970449eSLaurent Pinchart 		div = roundup_pow_of_two(div) / 2;
253a970449eSLaurent Pinchart 
254198b47ddSEnrico Scholz 		mt9p031->clk_div = min_t(unsigned int, div, 64);
255a970449eSLaurent Pinchart 		mt9p031->use_pll = false;
256a970449eSLaurent Pinchart 
257a970449eSLaurent Pinchart 		return 0;
258a970449eSLaurent Pinchart 	}
259cb7a01acSMauro Carvalho Chehab 
260*b9c18096SEnrico Scholz 	mt9p031->pll.ext_clock = ext_freq;
261cb7a01acSMauro Carvalho Chehab 	mt9p031->pll.pix_clock = pdata->target_freq;
262a970449eSLaurent Pinchart 	mt9p031->use_pll = true;
263cb7a01acSMauro Carvalho Chehab 
264cb7a01acSMauro Carvalho Chehab 	return aptina_pll_calculate(&client->dev, &limits, &mt9p031->pll);
265cb7a01acSMauro Carvalho Chehab }
266cb7a01acSMauro Carvalho Chehab 
267cb7a01acSMauro Carvalho Chehab static int mt9p031_pll_enable(struct mt9p031 *mt9p031)
268cb7a01acSMauro Carvalho Chehab {
269cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
270cb7a01acSMauro Carvalho Chehab 	int ret;
271cb7a01acSMauro Carvalho Chehab 
272a970449eSLaurent Pinchart 	if (!mt9p031->use_pll)
273a970449eSLaurent Pinchart 		return 0;
274a970449eSLaurent Pinchart 
275cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_PLL_CONTROL,
276cb7a01acSMauro Carvalho Chehab 			    MT9P031_PLL_CONTROL_PWRON);
277cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
278cb7a01acSMauro Carvalho Chehab 		return ret;
279cb7a01acSMauro Carvalho Chehab 
280cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_PLL_CONFIG_1,
281cb7a01acSMauro Carvalho Chehab 			    (mt9p031->pll.m << 8) | (mt9p031->pll.n - 1));
282cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
283cb7a01acSMauro Carvalho Chehab 		return ret;
284cb7a01acSMauro Carvalho Chehab 
285cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_PLL_CONFIG_2, mt9p031->pll.p1 - 1);
286cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
287cb7a01acSMauro Carvalho Chehab 		return ret;
288cb7a01acSMauro Carvalho Chehab 
289cb7a01acSMauro Carvalho Chehab 	usleep_range(1000, 2000);
290cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_PLL_CONTROL,
291cb7a01acSMauro Carvalho Chehab 			    MT9P031_PLL_CONTROL_PWRON |
292cb7a01acSMauro Carvalho Chehab 			    MT9P031_PLL_CONTROL_USEPLL);
293cb7a01acSMauro Carvalho Chehab 	return ret;
294cb7a01acSMauro Carvalho Chehab }
295cb7a01acSMauro Carvalho Chehab 
296cb7a01acSMauro Carvalho Chehab static inline int mt9p031_pll_disable(struct mt9p031 *mt9p031)
297cb7a01acSMauro Carvalho Chehab {
298cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
299cb7a01acSMauro Carvalho Chehab 
300a970449eSLaurent Pinchart 	if (!mt9p031->use_pll)
301a970449eSLaurent Pinchart 		return 0;
302a970449eSLaurent Pinchart 
303cb7a01acSMauro Carvalho Chehab 	return mt9p031_write(client, MT9P031_PLL_CONTROL,
304cb7a01acSMauro Carvalho Chehab 			     MT9P031_PLL_CONTROL_PWROFF);
305cb7a01acSMauro Carvalho Chehab }
306cb7a01acSMauro Carvalho Chehab 
307cb7a01acSMauro Carvalho Chehab static int mt9p031_power_on(struct mt9p031 *mt9p031)
308cb7a01acSMauro Carvalho Chehab {
3097997196cSLaurent Pinchart 	int ret;
3107997196cSLaurent Pinchart 
3117c3be9f8SLaurent Pinchart 	/* Ensure RESET_BAR is active */
3127c3be9f8SLaurent Pinchart 	if (mt9p031->reset) {
3137c3be9f8SLaurent Pinchart 		gpiod_set_value(mt9p031->reset, 1);
314cb7a01acSMauro Carvalho Chehab 		usleep_range(1000, 2000);
315cb7a01acSMauro Carvalho Chehab 	}
316cb7a01acSMauro Carvalho Chehab 
31797f21276SLaurent Pinchart 	/* Bring up the supplies */
3187997196cSLaurent Pinchart 	ret = regulator_bulk_enable(ARRAY_SIZE(mt9p031->regulators),
3197997196cSLaurent Pinchart 				   mt9p031->regulators);
3207997196cSLaurent Pinchart 	if (ret < 0)
3217997196cSLaurent Pinchart 		return ret;
32297f21276SLaurent Pinchart 
323e8e45593SLaurent Pinchart 	/* Enable clock */
324ee2d16d7SLad, Prabhakar 	if (mt9p031->clk) {
325ee2d16d7SLad, Prabhakar 		ret = clk_prepare_enable(mt9p031->clk);
326ee2d16d7SLad, Prabhakar 		if (ret) {
327ee2d16d7SLad, Prabhakar 			regulator_bulk_disable(ARRAY_SIZE(mt9p031->regulators),
328ee2d16d7SLad, Prabhakar 					       mt9p031->regulators);
329ee2d16d7SLad, Prabhakar 			return ret;
330ee2d16d7SLad, Prabhakar 		}
331ee2d16d7SLad, Prabhakar 	}
332cb7a01acSMauro Carvalho Chehab 
333cb7a01acSMauro Carvalho Chehab 	/* Now RESET_BAR must be high */
3347c3be9f8SLaurent Pinchart 	if (mt9p031->reset) {
3357c3be9f8SLaurent Pinchart 		gpiod_set_value(mt9p031->reset, 0);
336cb7a01acSMauro Carvalho Chehab 		usleep_range(1000, 2000);
337cb7a01acSMauro Carvalho Chehab 	}
338cb7a01acSMauro Carvalho Chehab 
339cb7a01acSMauro Carvalho Chehab 	return 0;
340cb7a01acSMauro Carvalho Chehab }
341cb7a01acSMauro Carvalho Chehab 
342cb7a01acSMauro Carvalho Chehab static void mt9p031_power_off(struct mt9p031 *mt9p031)
343cb7a01acSMauro Carvalho Chehab {
3447c3be9f8SLaurent Pinchart 	if (mt9p031->reset) {
3457c3be9f8SLaurent Pinchart 		gpiod_set_value(mt9p031->reset, 1);
346cb7a01acSMauro Carvalho Chehab 		usleep_range(1000, 2000);
347cb7a01acSMauro Carvalho Chehab 	}
348cb7a01acSMauro Carvalho Chehab 
3497997196cSLaurent Pinchart 	regulator_bulk_disable(ARRAY_SIZE(mt9p031->regulators),
3507997196cSLaurent Pinchart 			       mt9p031->regulators);
35197f21276SLaurent Pinchart 
352d6749258SLaurent Pinchart 	clk_disable_unprepare(mt9p031->clk);
353cb7a01acSMauro Carvalho Chehab }
354cb7a01acSMauro Carvalho Chehab 
355cb7a01acSMauro Carvalho Chehab static int __mt9p031_set_power(struct mt9p031 *mt9p031, bool on)
356cb7a01acSMauro Carvalho Chehab {
357cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
358cb7a01acSMauro Carvalho Chehab 	int ret;
359cb7a01acSMauro Carvalho Chehab 
360cb7a01acSMauro Carvalho Chehab 	if (!on) {
361cb7a01acSMauro Carvalho Chehab 		mt9p031_power_off(mt9p031);
362cb7a01acSMauro Carvalho Chehab 		return 0;
363cb7a01acSMauro Carvalho Chehab 	}
364cb7a01acSMauro Carvalho Chehab 
365cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_power_on(mt9p031);
366cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
367cb7a01acSMauro Carvalho Chehab 		return ret;
368cb7a01acSMauro Carvalho Chehab 
369cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_reset(mt9p031);
370cb7a01acSMauro Carvalho Chehab 	if (ret < 0) {
371cb7a01acSMauro Carvalho Chehab 		dev_err(&client->dev, "Failed to reset the camera\n");
372cb7a01acSMauro Carvalho Chehab 		return ret;
373cb7a01acSMauro Carvalho Chehab 	}
374cb7a01acSMauro Carvalho Chehab 
375cb7a01acSMauro Carvalho Chehab 	return v4l2_ctrl_handler_setup(&mt9p031->ctrls);
376cb7a01acSMauro Carvalho Chehab }
377cb7a01acSMauro Carvalho Chehab 
378cb7a01acSMauro Carvalho Chehab /* -----------------------------------------------------------------------------
379cb7a01acSMauro Carvalho Chehab  * V4L2 subdev video operations
380cb7a01acSMauro Carvalho Chehab  */
381cb7a01acSMauro Carvalho Chehab 
382cb7a01acSMauro Carvalho Chehab static int mt9p031_set_params(struct mt9p031 *mt9p031)
383cb7a01acSMauro Carvalho Chehab {
384cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
385cb7a01acSMauro Carvalho Chehab 	struct v4l2_mbus_framefmt *format = &mt9p031->format;
386cb7a01acSMauro Carvalho Chehab 	const struct v4l2_rect *crop = &mt9p031->crop;
387cb7a01acSMauro Carvalho Chehab 	unsigned int hblank;
388cb7a01acSMauro Carvalho Chehab 	unsigned int vblank;
389cb7a01acSMauro Carvalho Chehab 	unsigned int xskip;
390cb7a01acSMauro Carvalho Chehab 	unsigned int yskip;
391cb7a01acSMauro Carvalho Chehab 	unsigned int xbin;
392cb7a01acSMauro Carvalho Chehab 	unsigned int ybin;
393cb7a01acSMauro Carvalho Chehab 	int ret;
394cb7a01acSMauro Carvalho Chehab 
395cb7a01acSMauro Carvalho Chehab 	/* Windows position and size.
396cb7a01acSMauro Carvalho Chehab 	 *
397cb7a01acSMauro Carvalho Chehab 	 * TODO: Make sure the start coordinates and window size match the
398cb7a01acSMauro Carvalho Chehab 	 * skipping, binning and mirroring (see description of registers 2 and 4
399cb7a01acSMauro Carvalho Chehab 	 * in table 13, and Binning section on page 41).
400cb7a01acSMauro Carvalho Chehab 	 */
401cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_COLUMN_START, crop->left);
402cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
403cb7a01acSMauro Carvalho Chehab 		return ret;
404cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_ROW_START, crop->top);
405cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
406cb7a01acSMauro Carvalho Chehab 		return ret;
407cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_WINDOW_WIDTH, crop->width - 1);
408cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
409cb7a01acSMauro Carvalho Chehab 		return ret;
410cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_WINDOW_HEIGHT, crop->height - 1);
411cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
412cb7a01acSMauro Carvalho Chehab 		return ret;
413cb7a01acSMauro Carvalho Chehab 
414cb7a01acSMauro Carvalho Chehab 	/* Row and column binning and skipping. Use the maximum binning value
415cb7a01acSMauro Carvalho Chehab 	 * compatible with the skipping settings.
416cb7a01acSMauro Carvalho Chehab 	 */
417cb7a01acSMauro Carvalho Chehab 	xskip = DIV_ROUND_CLOSEST(crop->width, format->width);
418cb7a01acSMauro Carvalho Chehab 	yskip = DIV_ROUND_CLOSEST(crop->height, format->height);
419cb7a01acSMauro Carvalho Chehab 	xbin = 1 << (ffs(xskip) - 1);
420cb7a01acSMauro Carvalho Chehab 	ybin = 1 << (ffs(yskip) - 1);
421cb7a01acSMauro Carvalho Chehab 
422cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_COLUMN_ADDRESS_MODE,
423cb7a01acSMauro Carvalho Chehab 			    ((xbin - 1) << 4) | (xskip - 1));
424cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
425cb7a01acSMauro Carvalho Chehab 		return ret;
426cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_ROW_ADDRESS_MODE,
427cb7a01acSMauro Carvalho Chehab 			    ((ybin - 1) << 4) | (yskip - 1));
428cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
429cb7a01acSMauro Carvalho Chehab 		return ret;
430cb7a01acSMauro Carvalho Chehab 
431cb7a01acSMauro Carvalho Chehab 	/* Blanking - use minimum value for horizontal blanking and default
432cb7a01acSMauro Carvalho Chehab 	 * value for vertical blanking.
433cb7a01acSMauro Carvalho Chehab 	 */
4345266c98bSLaurent Pinchart 	hblank = 346 * ybin + 64 + (80 >> min_t(unsigned int, xbin, 3));
435cb7a01acSMauro Carvalho Chehab 	vblank = MT9P031_VERTICAL_BLANK_DEF;
436cb7a01acSMauro Carvalho Chehab 
4375266c98bSLaurent Pinchart 	ret = mt9p031_write(client, MT9P031_HORIZONTAL_BLANK, hblank - 1);
438cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
439cb7a01acSMauro Carvalho Chehab 		return ret;
4405266c98bSLaurent Pinchart 	ret = mt9p031_write(client, MT9P031_VERTICAL_BLANK, vblank - 1);
441cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
442cb7a01acSMauro Carvalho Chehab 		return ret;
443cb7a01acSMauro Carvalho Chehab 
444cb7a01acSMauro Carvalho Chehab 	return ret;
445cb7a01acSMauro Carvalho Chehab }
446cb7a01acSMauro Carvalho Chehab 
447cb7a01acSMauro Carvalho Chehab static int mt9p031_s_stream(struct v4l2_subdev *subdev, int enable)
448cb7a01acSMauro Carvalho Chehab {
449cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
450cb7a01acSMauro Carvalho Chehab 	int ret;
451cb7a01acSMauro Carvalho Chehab 
452cb7a01acSMauro Carvalho Chehab 	if (!enable) {
453cb7a01acSMauro Carvalho Chehab 		/* Stop sensor readout */
454cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_set_output_control(mt9p031,
455cb7a01acSMauro Carvalho Chehab 						 MT9P031_OUTPUT_CONTROL_CEN, 0);
456cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
457cb7a01acSMauro Carvalho Chehab 			return ret;
458cb7a01acSMauro Carvalho Chehab 
459cb7a01acSMauro Carvalho Chehab 		return mt9p031_pll_disable(mt9p031);
460cb7a01acSMauro Carvalho Chehab 	}
461cb7a01acSMauro Carvalho Chehab 
462cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_set_params(mt9p031);
463cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
464cb7a01acSMauro Carvalho Chehab 		return ret;
465cb7a01acSMauro Carvalho Chehab 
466cb7a01acSMauro Carvalho Chehab 	/* Switch to master "normal" mode */
467cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_set_output_control(mt9p031, 0,
468cb7a01acSMauro Carvalho Chehab 					 MT9P031_OUTPUT_CONTROL_CEN);
469cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
470cb7a01acSMauro Carvalho Chehab 		return ret;
471cb7a01acSMauro Carvalho Chehab 
472cb7a01acSMauro Carvalho Chehab 	return mt9p031_pll_enable(mt9p031);
473cb7a01acSMauro Carvalho Chehab }
474cb7a01acSMauro Carvalho Chehab 
475cb7a01acSMauro Carvalho Chehab static int mt9p031_enum_mbus_code(struct v4l2_subdev *subdev,
4760d346d2aSTomi Valkeinen 				  struct v4l2_subdev_state *sd_state,
477cb7a01acSMauro Carvalho Chehab 				  struct v4l2_subdev_mbus_code_enum *code)
478cb7a01acSMauro Carvalho Chehab {
479cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
480cb7a01acSMauro Carvalho Chehab 
481cb7a01acSMauro Carvalho Chehab 	if (code->pad || code->index)
482cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
483cb7a01acSMauro Carvalho Chehab 
484cb7a01acSMauro Carvalho Chehab 	code->code = mt9p031->format.code;
485cb7a01acSMauro Carvalho Chehab 	return 0;
486cb7a01acSMauro Carvalho Chehab }
487cb7a01acSMauro Carvalho Chehab 
488cb7a01acSMauro Carvalho Chehab static int mt9p031_enum_frame_size(struct v4l2_subdev *subdev,
4890d346d2aSTomi Valkeinen 				   struct v4l2_subdev_state *sd_state,
490cb7a01acSMauro Carvalho Chehab 				   struct v4l2_subdev_frame_size_enum *fse)
491cb7a01acSMauro Carvalho Chehab {
492cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
493cb7a01acSMauro Carvalho Chehab 
494cb7a01acSMauro Carvalho Chehab 	if (fse->index >= 8 || fse->code != mt9p031->format.code)
495cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
496cb7a01acSMauro Carvalho Chehab 
497cb7a01acSMauro Carvalho Chehab 	fse->min_width = MT9P031_WINDOW_WIDTH_DEF
498cb7a01acSMauro Carvalho Chehab 		       / min_t(unsigned int, 7, fse->index + 1);
499cb7a01acSMauro Carvalho Chehab 	fse->max_width = fse->min_width;
500cb7a01acSMauro Carvalho Chehab 	fse->min_height = MT9P031_WINDOW_HEIGHT_DEF / (fse->index + 1);
501cb7a01acSMauro Carvalho Chehab 	fse->max_height = fse->min_height;
502cb7a01acSMauro Carvalho Chehab 
503cb7a01acSMauro Carvalho Chehab 	return 0;
504cb7a01acSMauro Carvalho Chehab }
505cb7a01acSMauro Carvalho Chehab 
506cb7a01acSMauro Carvalho Chehab static struct v4l2_mbus_framefmt *
5070d346d2aSTomi Valkeinen __mt9p031_get_pad_format(struct mt9p031 *mt9p031,
5080d346d2aSTomi Valkeinen 			 struct v4l2_subdev_state *sd_state,
509cb7a01acSMauro Carvalho Chehab 			 unsigned int pad, u32 which)
510cb7a01acSMauro Carvalho Chehab {
511cb7a01acSMauro Carvalho Chehab 	switch (which) {
512cb7a01acSMauro Carvalho Chehab 	case V4L2_SUBDEV_FORMAT_TRY:
5130d346d2aSTomi Valkeinen 		return v4l2_subdev_get_try_format(&mt9p031->subdev, sd_state,
5140d346d2aSTomi Valkeinen 						  pad);
515cb7a01acSMauro Carvalho Chehab 	case V4L2_SUBDEV_FORMAT_ACTIVE:
516cb7a01acSMauro Carvalho Chehab 		return &mt9p031->format;
517cb7a01acSMauro Carvalho Chehab 	default:
518cb7a01acSMauro Carvalho Chehab 		return NULL;
519cb7a01acSMauro Carvalho Chehab 	}
520cb7a01acSMauro Carvalho Chehab }
521cb7a01acSMauro Carvalho Chehab 
522cb7a01acSMauro Carvalho Chehab static struct v4l2_rect *
5230d346d2aSTomi Valkeinen __mt9p031_get_pad_crop(struct mt9p031 *mt9p031,
5240d346d2aSTomi Valkeinen 		       struct v4l2_subdev_state *sd_state,
525cb7a01acSMauro Carvalho Chehab 		       unsigned int pad, u32 which)
526cb7a01acSMauro Carvalho Chehab {
527cb7a01acSMauro Carvalho Chehab 	switch (which) {
528cb7a01acSMauro Carvalho Chehab 	case V4L2_SUBDEV_FORMAT_TRY:
5290d346d2aSTomi Valkeinen 		return v4l2_subdev_get_try_crop(&mt9p031->subdev, sd_state,
5300d346d2aSTomi Valkeinen 						pad);
531cb7a01acSMauro Carvalho Chehab 	case V4L2_SUBDEV_FORMAT_ACTIVE:
532cb7a01acSMauro Carvalho Chehab 		return &mt9p031->crop;
533cb7a01acSMauro Carvalho Chehab 	default:
534cb7a01acSMauro Carvalho Chehab 		return NULL;
535cb7a01acSMauro Carvalho Chehab 	}
536cb7a01acSMauro Carvalho Chehab }
537cb7a01acSMauro Carvalho Chehab 
538cb7a01acSMauro Carvalho Chehab static int mt9p031_get_format(struct v4l2_subdev *subdev,
5390d346d2aSTomi Valkeinen 			      struct v4l2_subdev_state *sd_state,
540cb7a01acSMauro Carvalho Chehab 			      struct v4l2_subdev_format *fmt)
541cb7a01acSMauro Carvalho Chehab {
542cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
543cb7a01acSMauro Carvalho Chehab 
5440d346d2aSTomi Valkeinen 	fmt->format = *__mt9p031_get_pad_format(mt9p031, sd_state, fmt->pad,
545cb7a01acSMauro Carvalho Chehab 						fmt->which);
546cb7a01acSMauro Carvalho Chehab 	return 0;
547cb7a01acSMauro Carvalho Chehab }
548cb7a01acSMauro Carvalho Chehab 
549cb7a01acSMauro Carvalho Chehab static int mt9p031_set_format(struct v4l2_subdev *subdev,
5500d346d2aSTomi Valkeinen 			      struct v4l2_subdev_state *sd_state,
551cb7a01acSMauro Carvalho Chehab 			      struct v4l2_subdev_format *format)
552cb7a01acSMauro Carvalho Chehab {
553cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
554cb7a01acSMauro Carvalho Chehab 	struct v4l2_mbus_framefmt *__format;
555cb7a01acSMauro Carvalho Chehab 	struct v4l2_rect *__crop;
556cb7a01acSMauro Carvalho Chehab 	unsigned int width;
557cb7a01acSMauro Carvalho Chehab 	unsigned int height;
558cb7a01acSMauro Carvalho Chehab 	unsigned int hratio;
559cb7a01acSMauro Carvalho Chehab 	unsigned int vratio;
560cb7a01acSMauro Carvalho Chehab 
5610d346d2aSTomi Valkeinen 	__crop = __mt9p031_get_pad_crop(mt9p031, sd_state, format->pad,
562cb7a01acSMauro Carvalho Chehab 					format->which);
563cb7a01acSMauro Carvalho Chehab 
564cb7a01acSMauro Carvalho Chehab 	/* Clamp the width and height to avoid dividing by zero. */
565cb7a01acSMauro Carvalho Chehab 	width = clamp_t(unsigned int, ALIGN(format->format.width, 2),
566f90580caSRicardo Ribalda 			max_t(unsigned int, __crop->width / 7,
567f90580caSRicardo Ribalda 			      MT9P031_WINDOW_WIDTH_MIN),
568cb7a01acSMauro Carvalho Chehab 			__crop->width);
569cb7a01acSMauro Carvalho Chehab 	height = clamp_t(unsigned int, ALIGN(format->format.height, 2),
570f90580caSRicardo Ribalda 			 max_t(unsigned int, __crop->height / 8,
571f90580caSRicardo Ribalda 			       MT9P031_WINDOW_HEIGHT_MIN),
572cb7a01acSMauro Carvalho Chehab 			 __crop->height);
573cb7a01acSMauro Carvalho Chehab 
574cb7a01acSMauro Carvalho Chehab 	hratio = DIV_ROUND_CLOSEST(__crop->width, width);
575cb7a01acSMauro Carvalho Chehab 	vratio = DIV_ROUND_CLOSEST(__crop->height, height);
576cb7a01acSMauro Carvalho Chehab 
5770d346d2aSTomi Valkeinen 	__format = __mt9p031_get_pad_format(mt9p031, sd_state, format->pad,
578cb7a01acSMauro Carvalho Chehab 					    format->which);
579cb7a01acSMauro Carvalho Chehab 	__format->width = __crop->width / hratio;
580cb7a01acSMauro Carvalho Chehab 	__format->height = __crop->height / vratio;
581cb7a01acSMauro Carvalho Chehab 
582cb7a01acSMauro Carvalho Chehab 	format->format = *__format;
583cb7a01acSMauro Carvalho Chehab 
584cb7a01acSMauro Carvalho Chehab 	return 0;
585cb7a01acSMauro Carvalho Chehab }
586cb7a01acSMauro Carvalho Chehab 
5871a023febSHans Verkuil static int mt9p031_get_selection(struct v4l2_subdev *subdev,
5880d346d2aSTomi Valkeinen 				 struct v4l2_subdev_state *sd_state,
5891a023febSHans Verkuil 				 struct v4l2_subdev_selection *sel)
590cb7a01acSMauro Carvalho Chehab {
591cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
592cb7a01acSMauro Carvalho Chehab 
5931a023febSHans Verkuil 	if (sel->target != V4L2_SEL_TGT_CROP)
5941a023febSHans Verkuil 		return -EINVAL;
5951a023febSHans Verkuil 
5960d346d2aSTomi Valkeinen 	sel->r = *__mt9p031_get_pad_crop(mt9p031, sd_state, sel->pad,
5970d346d2aSTomi Valkeinen 					 sel->which);
598cb7a01acSMauro Carvalho Chehab 	return 0;
599cb7a01acSMauro Carvalho Chehab }
600cb7a01acSMauro Carvalho Chehab 
6011a023febSHans Verkuil static int mt9p031_set_selection(struct v4l2_subdev *subdev,
6020d346d2aSTomi Valkeinen 				 struct v4l2_subdev_state *sd_state,
6031a023febSHans Verkuil 				 struct v4l2_subdev_selection *sel)
604cb7a01acSMauro Carvalho Chehab {
605cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
606cb7a01acSMauro Carvalho Chehab 	struct v4l2_mbus_framefmt *__format;
607cb7a01acSMauro Carvalho Chehab 	struct v4l2_rect *__crop;
608cb7a01acSMauro Carvalho Chehab 	struct v4l2_rect rect;
609cb7a01acSMauro Carvalho Chehab 
6101a023febSHans Verkuil 	if (sel->target != V4L2_SEL_TGT_CROP)
6111a023febSHans Verkuil 		return -EINVAL;
6121a023febSHans Verkuil 
613cb7a01acSMauro Carvalho Chehab 	/* Clamp the crop rectangle boundaries and align them to a multiple of 2
614cb7a01acSMauro Carvalho Chehab 	 * pixels to ensure a GRBG Bayer pattern.
615cb7a01acSMauro Carvalho Chehab 	 */
6161a023febSHans Verkuil 	rect.left = clamp(ALIGN(sel->r.left, 2), MT9P031_COLUMN_START_MIN,
617cb7a01acSMauro Carvalho Chehab 			  MT9P031_COLUMN_START_MAX);
6181a023febSHans Verkuil 	rect.top = clamp(ALIGN(sel->r.top, 2), MT9P031_ROW_START_MIN,
619cb7a01acSMauro Carvalho Chehab 			 MT9P031_ROW_START_MAX);
6201a023febSHans Verkuil 	rect.width = clamp_t(unsigned int, ALIGN(sel->r.width, 2),
621cb7a01acSMauro Carvalho Chehab 			     MT9P031_WINDOW_WIDTH_MIN,
622cb7a01acSMauro Carvalho Chehab 			     MT9P031_WINDOW_WIDTH_MAX);
6231a023febSHans Verkuil 	rect.height = clamp_t(unsigned int, ALIGN(sel->r.height, 2),
624cb7a01acSMauro Carvalho Chehab 			      MT9P031_WINDOW_HEIGHT_MIN,
625cb7a01acSMauro Carvalho Chehab 			      MT9P031_WINDOW_HEIGHT_MAX);
626cb7a01acSMauro Carvalho Chehab 
627f90580caSRicardo Ribalda 	rect.width = min_t(unsigned int, rect.width,
628f90580caSRicardo Ribalda 			   MT9P031_PIXEL_ARRAY_WIDTH - rect.left);
629f90580caSRicardo Ribalda 	rect.height = min_t(unsigned int, rect.height,
630f90580caSRicardo Ribalda 			    MT9P031_PIXEL_ARRAY_HEIGHT - rect.top);
631cb7a01acSMauro Carvalho Chehab 
6320d346d2aSTomi Valkeinen 	__crop = __mt9p031_get_pad_crop(mt9p031, sd_state, sel->pad,
6330d346d2aSTomi Valkeinen 					sel->which);
634cb7a01acSMauro Carvalho Chehab 
635cb7a01acSMauro Carvalho Chehab 	if (rect.width != __crop->width || rect.height != __crop->height) {
636cb7a01acSMauro Carvalho Chehab 		/* Reset the output image size if the crop rectangle size has
637cb7a01acSMauro Carvalho Chehab 		 * been modified.
638cb7a01acSMauro Carvalho Chehab 		 */
6390d346d2aSTomi Valkeinen 		__format = __mt9p031_get_pad_format(mt9p031, sd_state,
6400d346d2aSTomi Valkeinen 						    sel->pad,
6411a023febSHans Verkuil 						    sel->which);
642cb7a01acSMauro Carvalho Chehab 		__format->width = rect.width;
643cb7a01acSMauro Carvalho Chehab 		__format->height = rect.height;
644cb7a01acSMauro Carvalho Chehab 	}
645cb7a01acSMauro Carvalho Chehab 
646cb7a01acSMauro Carvalho Chehab 	*__crop = rect;
6471a023febSHans Verkuil 	sel->r = rect;
648cb7a01acSMauro Carvalho Chehab 
649cb7a01acSMauro Carvalho Chehab 	return 0;
650cb7a01acSMauro Carvalho Chehab }
651cb7a01acSMauro Carvalho Chehab 
652cb7a01acSMauro Carvalho Chehab /* -----------------------------------------------------------------------------
653cb7a01acSMauro Carvalho Chehab  * V4L2 subdev control operations
654cb7a01acSMauro Carvalho Chehab  */
655cb7a01acSMauro Carvalho Chehab 
656cb7a01acSMauro Carvalho Chehab #define V4L2_CID_BLC_AUTO		(V4L2_CID_USER_BASE | 0x1002)
657cb7a01acSMauro Carvalho Chehab #define V4L2_CID_BLC_TARGET_LEVEL	(V4L2_CID_USER_BASE | 0x1003)
658cb7a01acSMauro Carvalho Chehab #define V4L2_CID_BLC_ANALOG_OFFSET	(V4L2_CID_USER_BASE | 0x1004)
659cb7a01acSMauro Carvalho Chehab #define V4L2_CID_BLC_DIGITAL_OFFSET	(V4L2_CID_USER_BASE | 0x1005)
660cb7a01acSMauro Carvalho Chehab 
661535ec214SLaurent Pinchart static int mt9p031_restore_blc(struct mt9p031 *mt9p031)
662535ec214SLaurent Pinchart {
663535ec214SLaurent Pinchart 	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
664535ec214SLaurent Pinchart 	int ret;
665535ec214SLaurent Pinchart 
666535ec214SLaurent Pinchart 	if (mt9p031->blc_auto->cur.val != 0) {
667535ec214SLaurent Pinchart 		ret = mt9p031_set_mode2(mt9p031, 0,
668535ec214SLaurent Pinchart 					MT9P031_READ_MODE_2_ROW_BLC);
669535ec214SLaurent Pinchart 		if (ret < 0)
670535ec214SLaurent Pinchart 			return ret;
671535ec214SLaurent Pinchart 	}
672535ec214SLaurent Pinchart 
673535ec214SLaurent Pinchart 	if (mt9p031->blc_offset->cur.val != 0) {
674535ec214SLaurent Pinchart 		ret = mt9p031_write(client, MT9P031_ROW_BLACK_TARGET,
675535ec214SLaurent Pinchart 				    mt9p031->blc_offset->cur.val);
676535ec214SLaurent Pinchart 		if (ret < 0)
677535ec214SLaurent Pinchart 			return ret;
678535ec214SLaurent Pinchart 	}
679535ec214SLaurent Pinchart 
680535ec214SLaurent Pinchart 	return 0;
681535ec214SLaurent Pinchart }
682535ec214SLaurent Pinchart 
683cb7a01acSMauro Carvalho Chehab static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl)
684cb7a01acSMauro Carvalho Chehab {
685cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 =
686cb7a01acSMauro Carvalho Chehab 			container_of(ctrl->handler, struct mt9p031, ctrls);
687cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
688cb7a01acSMauro Carvalho Chehab 	u16 data;
689cb7a01acSMauro Carvalho Chehab 	int ret;
690cb7a01acSMauro Carvalho Chehab 
6918bf54c43SLaurent Pinchart 	if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE)
6928bf54c43SLaurent Pinchart 		return 0;
6938bf54c43SLaurent Pinchart 
694cb7a01acSMauro Carvalho Chehab 	switch (ctrl->id) {
695cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_EXPOSURE:
696cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_write(client, MT9P031_SHUTTER_WIDTH_UPPER,
697cb7a01acSMauro Carvalho Chehab 				    (ctrl->val >> 16) & 0xffff);
698cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
699cb7a01acSMauro Carvalho Chehab 			return ret;
700cb7a01acSMauro Carvalho Chehab 
701cb7a01acSMauro Carvalho Chehab 		return mt9p031_write(client, MT9P031_SHUTTER_WIDTH_LOWER,
702cb7a01acSMauro Carvalho Chehab 				     ctrl->val & 0xffff);
703cb7a01acSMauro Carvalho Chehab 
704cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_GAIN:
705cb7a01acSMauro Carvalho Chehab 		/* Gain is controlled by 2 analog stages and a digital stage.
706cb7a01acSMauro Carvalho Chehab 		 * Valid values for the 3 stages are
707cb7a01acSMauro Carvalho Chehab 		 *
708cb7a01acSMauro Carvalho Chehab 		 * Stage                Min     Max     Step
709cb7a01acSMauro Carvalho Chehab 		 * ------------------------------------------
710cb7a01acSMauro Carvalho Chehab 		 * First analog stage   x1      x2      1
711cb7a01acSMauro Carvalho Chehab 		 * Second analog stage  x1      x4      0.125
712cb7a01acSMauro Carvalho Chehab 		 * Digital stage        x1      x16     0.125
713cb7a01acSMauro Carvalho Chehab 		 *
714cb7a01acSMauro Carvalho Chehab 		 * To minimize noise, the gain stages should be used in the
715cb7a01acSMauro Carvalho Chehab 		 * second analog stage, first analog stage, digital stage order.
716cb7a01acSMauro Carvalho Chehab 		 * Gain from a previous stage should be pushed to its maximum
717cb7a01acSMauro Carvalho Chehab 		 * value before the next stage is used.
718cb7a01acSMauro Carvalho Chehab 		 */
719cb7a01acSMauro Carvalho Chehab 		if (ctrl->val <= 32) {
720cb7a01acSMauro Carvalho Chehab 			data = ctrl->val;
721cb7a01acSMauro Carvalho Chehab 		} else if (ctrl->val <= 64) {
722cb7a01acSMauro Carvalho Chehab 			ctrl->val &= ~1;
723cb7a01acSMauro Carvalho Chehab 			data = (1 << 6) | (ctrl->val >> 1);
724cb7a01acSMauro Carvalho Chehab 		} else {
725cb7a01acSMauro Carvalho Chehab 			ctrl->val &= ~7;
726cb7a01acSMauro Carvalho Chehab 			data = ((ctrl->val - 64) << 5) | (1 << 6) | 32;
727cb7a01acSMauro Carvalho Chehab 		}
728cb7a01acSMauro Carvalho Chehab 
729cb7a01acSMauro Carvalho Chehab 		return mt9p031_write(client, MT9P031_GLOBAL_GAIN, data);
730cb7a01acSMauro Carvalho Chehab 
731cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_HFLIP:
732cb7a01acSMauro Carvalho Chehab 		if (ctrl->val)
733cb7a01acSMauro Carvalho Chehab 			return mt9p031_set_mode2(mt9p031,
734cb7a01acSMauro Carvalho Chehab 					0, MT9P031_READ_MODE_2_COL_MIR);
735cb7a01acSMauro Carvalho Chehab 		else
736cb7a01acSMauro Carvalho Chehab 			return mt9p031_set_mode2(mt9p031,
737cb7a01acSMauro Carvalho Chehab 					MT9P031_READ_MODE_2_COL_MIR, 0);
738cb7a01acSMauro Carvalho Chehab 
739cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_VFLIP:
740cb7a01acSMauro Carvalho Chehab 		if (ctrl->val)
741cb7a01acSMauro Carvalho Chehab 			return mt9p031_set_mode2(mt9p031,
742cb7a01acSMauro Carvalho Chehab 					0, MT9P031_READ_MODE_2_ROW_MIR);
743cb7a01acSMauro Carvalho Chehab 		else
744cb7a01acSMauro Carvalho Chehab 			return mt9p031_set_mode2(mt9p031,
745cb7a01acSMauro Carvalho Chehab 					MT9P031_READ_MODE_2_ROW_MIR, 0);
746cb7a01acSMauro Carvalho Chehab 
747cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_TEST_PATTERN:
7488bf54c43SLaurent Pinchart 		/* The digital side of the Black Level Calibration function must
7498bf54c43SLaurent Pinchart 		 * be disabled when generating a test pattern to avoid artifacts
7508bf54c43SLaurent Pinchart 		 * in the image. Activate (deactivate) the BLC-related controls
7518bf54c43SLaurent Pinchart 		 * when the test pattern is enabled (disabled).
7528bf54c43SLaurent Pinchart 		 */
7538bf54c43SLaurent Pinchart 		v4l2_ctrl_activate(mt9p031->blc_auto, ctrl->val == 0);
7548bf54c43SLaurent Pinchart 		v4l2_ctrl_activate(mt9p031->blc_offset, ctrl->val == 0);
7558bf54c43SLaurent Pinchart 
756cb7a01acSMauro Carvalho Chehab 		if (!ctrl->val) {
7578bf54c43SLaurent Pinchart 			/* Restore the BLC settings. */
758535ec214SLaurent Pinchart 			ret = mt9p031_restore_blc(mt9p031);
759cb7a01acSMauro Carvalho Chehab 			if (ret < 0)
760cb7a01acSMauro Carvalho Chehab 				return ret;
761535ec214SLaurent Pinchart 
762cb7a01acSMauro Carvalho Chehab 			return mt9p031_write(client, MT9P031_TEST_PATTERN,
763cb7a01acSMauro Carvalho Chehab 					     MT9P031_TEST_PATTERN_DISABLE);
764cb7a01acSMauro Carvalho Chehab 		}
765cb7a01acSMauro Carvalho Chehab 
766cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_write(client, MT9P031_TEST_PATTERN_GREEN, 0x05a0);
767cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
768cb7a01acSMauro Carvalho Chehab 			return ret;
769cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_write(client, MT9P031_TEST_PATTERN_RED, 0x0a50);
770cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
771cb7a01acSMauro Carvalho Chehab 			return ret;
772cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_write(client, MT9P031_TEST_PATTERN_BLUE, 0x0aa0);
773cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
774cb7a01acSMauro Carvalho Chehab 			return ret;
775cb7a01acSMauro Carvalho Chehab 
7768bf54c43SLaurent Pinchart 		/* Disable digital BLC when generating a test pattern. */
777cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_set_mode2(mt9p031, MT9P031_READ_MODE_2_ROW_BLC,
778cb7a01acSMauro Carvalho Chehab 					0);
779cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
780cb7a01acSMauro Carvalho Chehab 			return ret;
781cb7a01acSMauro Carvalho Chehab 
782cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET, 0);
783cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
784cb7a01acSMauro Carvalho Chehab 			return ret;
785cb7a01acSMauro Carvalho Chehab 
786cb7a01acSMauro Carvalho Chehab 		return mt9p031_write(client, MT9P031_TEST_PATTERN,
787cb7a01acSMauro Carvalho Chehab 				((ctrl->val - 1) << MT9P031_TEST_PATTERN_SHIFT)
788cb7a01acSMauro Carvalho Chehab 				| MT9P031_TEST_PATTERN_ENABLE);
789cb7a01acSMauro Carvalho Chehab 
790cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_BLC_AUTO:
791cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_set_mode2(mt9p031,
792cb7a01acSMauro Carvalho Chehab 				ctrl->val ? 0 : MT9P031_READ_MODE_2_ROW_BLC,
793cb7a01acSMauro Carvalho Chehab 				ctrl->val ? MT9P031_READ_MODE_2_ROW_BLC : 0);
794cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
795cb7a01acSMauro Carvalho Chehab 			return ret;
796cb7a01acSMauro Carvalho Chehab 
797cb7a01acSMauro Carvalho Chehab 		return mt9p031_write(client, MT9P031_BLACK_LEVEL_CALIBRATION,
798cb7a01acSMauro Carvalho Chehab 				     ctrl->val ? 0 : MT9P031_BLC_MANUAL_BLC);
799cb7a01acSMauro Carvalho Chehab 
800cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_BLC_TARGET_LEVEL:
801cb7a01acSMauro Carvalho Chehab 		return mt9p031_write(client, MT9P031_ROW_BLACK_TARGET,
802cb7a01acSMauro Carvalho Chehab 				     ctrl->val);
803cb7a01acSMauro Carvalho Chehab 
804cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_BLC_ANALOG_OFFSET:
805cb7a01acSMauro Carvalho Chehab 		data = ctrl->val & ((1 << 9) - 1);
806cb7a01acSMauro Carvalho Chehab 
807cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_write(client, MT9P031_GREEN1_OFFSET, data);
808cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
809cb7a01acSMauro Carvalho Chehab 			return ret;
810cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_write(client, MT9P031_GREEN2_OFFSET, data);
811cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
812cb7a01acSMauro Carvalho Chehab 			return ret;
813cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_write(client, MT9P031_RED_OFFSET, data);
814cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
815cb7a01acSMauro Carvalho Chehab 			return ret;
816cb7a01acSMauro Carvalho Chehab 		return mt9p031_write(client, MT9P031_BLUE_OFFSET, data);
817cb7a01acSMauro Carvalho Chehab 
818cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_BLC_DIGITAL_OFFSET:
819cb7a01acSMauro Carvalho Chehab 		return mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET,
820cb7a01acSMauro Carvalho Chehab 				     ctrl->val & ((1 << 12) - 1));
821cb7a01acSMauro Carvalho Chehab 	}
822cb7a01acSMauro Carvalho Chehab 
823cb7a01acSMauro Carvalho Chehab 	return 0;
824cb7a01acSMauro Carvalho Chehab }
825cb7a01acSMauro Carvalho Chehab 
826217bdb07SJulia Lawall static const struct v4l2_ctrl_ops mt9p031_ctrl_ops = {
827cb7a01acSMauro Carvalho Chehab 	.s_ctrl = mt9p031_s_ctrl,
828cb7a01acSMauro Carvalho Chehab };
829cb7a01acSMauro Carvalho Chehab 
830cb7a01acSMauro Carvalho Chehab static const char * const mt9p031_test_pattern_menu[] = {
831cb7a01acSMauro Carvalho Chehab 	"Disabled",
832cb7a01acSMauro Carvalho Chehab 	"Color Field",
833cb7a01acSMauro Carvalho Chehab 	"Horizontal Gradient",
834cb7a01acSMauro Carvalho Chehab 	"Vertical Gradient",
835cb7a01acSMauro Carvalho Chehab 	"Diagonal Gradient",
836cb7a01acSMauro Carvalho Chehab 	"Classic Test Pattern",
837cb7a01acSMauro Carvalho Chehab 	"Walking 1s",
838cb7a01acSMauro Carvalho Chehab 	"Monochrome Horizontal Bars",
839cb7a01acSMauro Carvalho Chehab 	"Monochrome Vertical Bars",
840cb7a01acSMauro Carvalho Chehab 	"Vertical Color Bars",
841cb7a01acSMauro Carvalho Chehab };
842cb7a01acSMauro Carvalho Chehab 
843cb7a01acSMauro Carvalho Chehab static const struct v4l2_ctrl_config mt9p031_ctrls[] = {
844cb7a01acSMauro Carvalho Chehab 	{
845cb7a01acSMauro Carvalho Chehab 		.ops		= &mt9p031_ctrl_ops,
846cb7a01acSMauro Carvalho Chehab 		.id		= V4L2_CID_BLC_AUTO,
847cb7a01acSMauro Carvalho Chehab 		.type		= V4L2_CTRL_TYPE_BOOLEAN,
848cb7a01acSMauro Carvalho Chehab 		.name		= "BLC, Auto",
849cb7a01acSMauro Carvalho Chehab 		.min		= 0,
850cb7a01acSMauro Carvalho Chehab 		.max		= 1,
851cb7a01acSMauro Carvalho Chehab 		.step		= 1,
852cb7a01acSMauro Carvalho Chehab 		.def		= 1,
853cb7a01acSMauro Carvalho Chehab 		.flags		= 0,
854cb7a01acSMauro Carvalho Chehab 	}, {
855cb7a01acSMauro Carvalho Chehab 		.ops		= &mt9p031_ctrl_ops,
856cb7a01acSMauro Carvalho Chehab 		.id		= V4L2_CID_BLC_TARGET_LEVEL,
857cb7a01acSMauro Carvalho Chehab 		.type		= V4L2_CTRL_TYPE_INTEGER,
858cb7a01acSMauro Carvalho Chehab 		.name		= "BLC Target Level",
859cb7a01acSMauro Carvalho Chehab 		.min		= 0,
860cb7a01acSMauro Carvalho Chehab 		.max		= 4095,
861cb7a01acSMauro Carvalho Chehab 		.step		= 1,
862cb7a01acSMauro Carvalho Chehab 		.def		= 168,
863cb7a01acSMauro Carvalho Chehab 		.flags		= 0,
864cb7a01acSMauro Carvalho Chehab 	}, {
865cb7a01acSMauro Carvalho Chehab 		.ops		= &mt9p031_ctrl_ops,
866cb7a01acSMauro Carvalho Chehab 		.id		= V4L2_CID_BLC_ANALOG_OFFSET,
867cb7a01acSMauro Carvalho Chehab 		.type		= V4L2_CTRL_TYPE_INTEGER,
868cb7a01acSMauro Carvalho Chehab 		.name		= "BLC Analog Offset",
869cb7a01acSMauro Carvalho Chehab 		.min		= -255,
870cb7a01acSMauro Carvalho Chehab 		.max		= 255,
871cb7a01acSMauro Carvalho Chehab 		.step		= 1,
872cb7a01acSMauro Carvalho Chehab 		.def		= 32,
873cb7a01acSMauro Carvalho Chehab 		.flags		= 0,
874cb7a01acSMauro Carvalho Chehab 	}, {
875cb7a01acSMauro Carvalho Chehab 		.ops		= &mt9p031_ctrl_ops,
876cb7a01acSMauro Carvalho Chehab 		.id		= V4L2_CID_BLC_DIGITAL_OFFSET,
877cb7a01acSMauro Carvalho Chehab 		.type		= V4L2_CTRL_TYPE_INTEGER,
878cb7a01acSMauro Carvalho Chehab 		.name		= "BLC Digital Offset",
879cb7a01acSMauro Carvalho Chehab 		.min		= -2048,
880cb7a01acSMauro Carvalho Chehab 		.max		= 2047,
881cb7a01acSMauro Carvalho Chehab 		.step		= 1,
882cb7a01acSMauro Carvalho Chehab 		.def		= 40,
883cb7a01acSMauro Carvalho Chehab 		.flags		= 0,
884cb7a01acSMauro Carvalho Chehab 	}
885cb7a01acSMauro Carvalho Chehab };
886cb7a01acSMauro Carvalho Chehab 
887cb7a01acSMauro Carvalho Chehab /* -----------------------------------------------------------------------------
888cb7a01acSMauro Carvalho Chehab  * V4L2 subdev core operations
889cb7a01acSMauro Carvalho Chehab  */
890cb7a01acSMauro Carvalho Chehab 
891cb7a01acSMauro Carvalho Chehab static int mt9p031_set_power(struct v4l2_subdev *subdev, int on)
892cb7a01acSMauro Carvalho Chehab {
893cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
894cb7a01acSMauro Carvalho Chehab 	int ret = 0;
895cb7a01acSMauro Carvalho Chehab 
896cb7a01acSMauro Carvalho Chehab 	mutex_lock(&mt9p031->power_lock);
897cb7a01acSMauro Carvalho Chehab 
898cb7a01acSMauro Carvalho Chehab 	/* If the power count is modified from 0 to != 0 or from != 0 to 0,
899cb7a01acSMauro Carvalho Chehab 	 * update the power state.
900cb7a01acSMauro Carvalho Chehab 	 */
901cb7a01acSMauro Carvalho Chehab 	if (mt9p031->power_count == !on) {
902cb7a01acSMauro Carvalho Chehab 		ret = __mt9p031_set_power(mt9p031, !!on);
903cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
904cb7a01acSMauro Carvalho Chehab 			goto out;
905cb7a01acSMauro Carvalho Chehab 	}
906cb7a01acSMauro Carvalho Chehab 
907cb7a01acSMauro Carvalho Chehab 	/* Update the power count. */
908cb7a01acSMauro Carvalho Chehab 	mt9p031->power_count += on ? 1 : -1;
909cb7a01acSMauro Carvalho Chehab 	WARN_ON(mt9p031->power_count < 0);
910cb7a01acSMauro Carvalho Chehab 
911cb7a01acSMauro Carvalho Chehab out:
912cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&mt9p031->power_lock);
913cb7a01acSMauro Carvalho Chehab 	return ret;
914cb7a01acSMauro Carvalho Chehab }
915cb7a01acSMauro Carvalho Chehab 
916cb7a01acSMauro Carvalho Chehab /* -----------------------------------------------------------------------------
917cb7a01acSMauro Carvalho Chehab  * V4L2 subdev internal operations
918cb7a01acSMauro Carvalho Chehab  */
919cb7a01acSMauro Carvalho Chehab 
920cb7a01acSMauro Carvalho Chehab static int mt9p031_registered(struct v4l2_subdev *subdev)
921cb7a01acSMauro Carvalho Chehab {
922cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(subdev);
923cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
924cb7a01acSMauro Carvalho Chehab 	s32 data;
925cb7a01acSMauro Carvalho Chehab 	int ret;
926cb7a01acSMauro Carvalho Chehab 
927cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_power_on(mt9p031);
928cb7a01acSMauro Carvalho Chehab 	if (ret < 0) {
929cb7a01acSMauro Carvalho Chehab 		dev_err(&client->dev, "MT9P031 power up failed\n");
930cb7a01acSMauro Carvalho Chehab 		return ret;
931cb7a01acSMauro Carvalho Chehab 	}
932cb7a01acSMauro Carvalho Chehab 
933cb7a01acSMauro Carvalho Chehab 	/* Read out the chip version register */
934cb7a01acSMauro Carvalho Chehab 	data = mt9p031_read(client, MT9P031_CHIP_VERSION);
935bbcc9fa0SGuennadi Liakhovetski 	mt9p031_power_off(mt9p031);
936bbcc9fa0SGuennadi Liakhovetski 
937cb7a01acSMauro Carvalho Chehab 	if (data != MT9P031_CHIP_VERSION_VALUE) {
938cb7a01acSMauro Carvalho Chehab 		dev_err(&client->dev, "MT9P031 not detected, wrong version "
939cb7a01acSMauro Carvalho Chehab 			"0x%04x\n", data);
940cb7a01acSMauro Carvalho Chehab 		return -ENODEV;
941cb7a01acSMauro Carvalho Chehab 	}
942cb7a01acSMauro Carvalho Chehab 
943cb7a01acSMauro Carvalho Chehab 	dev_info(&client->dev, "MT9P031 detected at address 0x%02x\n",
944cb7a01acSMauro Carvalho Chehab 		 client->addr);
945cb7a01acSMauro Carvalho Chehab 
946bbcc9fa0SGuennadi Liakhovetski 	return 0;
947cb7a01acSMauro Carvalho Chehab }
948cb7a01acSMauro Carvalho Chehab 
949cb7a01acSMauro Carvalho Chehab static int mt9p031_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
950cb7a01acSMauro Carvalho Chehab {
951cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
952cb7a01acSMauro Carvalho Chehab 	struct v4l2_mbus_framefmt *format;
953cb7a01acSMauro Carvalho Chehab 	struct v4l2_rect *crop;
954cb7a01acSMauro Carvalho Chehab 
9550d346d2aSTomi Valkeinen 	crop = v4l2_subdev_get_try_crop(subdev, fh->state, 0);
956cb7a01acSMauro Carvalho Chehab 	crop->left = MT9P031_COLUMN_START_DEF;
957cb7a01acSMauro Carvalho Chehab 	crop->top = MT9P031_ROW_START_DEF;
958cb7a01acSMauro Carvalho Chehab 	crop->width = MT9P031_WINDOW_WIDTH_DEF;
959cb7a01acSMauro Carvalho Chehab 	crop->height = MT9P031_WINDOW_HEIGHT_DEF;
960cb7a01acSMauro Carvalho Chehab 
9610d346d2aSTomi Valkeinen 	format = v4l2_subdev_get_try_format(subdev, fh->state, 0);
962cb7a01acSMauro Carvalho Chehab 
963cb7a01acSMauro Carvalho Chehab 	if (mt9p031->model == MT9P031_MODEL_MONOCHROME)
964f5fe58fdSBoris BREZILLON 		format->code = MEDIA_BUS_FMT_Y12_1X12;
965cb7a01acSMauro Carvalho Chehab 	else
966f5fe58fdSBoris BREZILLON 		format->code = MEDIA_BUS_FMT_SGRBG12_1X12;
967cb7a01acSMauro Carvalho Chehab 
968cb7a01acSMauro Carvalho Chehab 	format->width = MT9P031_WINDOW_WIDTH_DEF;
969cb7a01acSMauro Carvalho Chehab 	format->height = MT9P031_WINDOW_HEIGHT_DEF;
970cb7a01acSMauro Carvalho Chehab 	format->field = V4L2_FIELD_NONE;
971cb7a01acSMauro Carvalho Chehab 	format->colorspace = V4L2_COLORSPACE_SRGB;
972cb7a01acSMauro Carvalho Chehab 
973cb7a01acSMauro Carvalho Chehab 	return mt9p031_set_power(subdev, 1);
974cb7a01acSMauro Carvalho Chehab }
975cb7a01acSMauro Carvalho Chehab 
976cb7a01acSMauro Carvalho Chehab static int mt9p031_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
977cb7a01acSMauro Carvalho Chehab {
978cb7a01acSMauro Carvalho Chehab 	return mt9p031_set_power(subdev, 0);
979cb7a01acSMauro Carvalho Chehab }
980cb7a01acSMauro Carvalho Chehab 
9817c137c60SBhumika Goyal static const struct v4l2_subdev_core_ops mt9p031_subdev_core_ops = {
982cb7a01acSMauro Carvalho Chehab 	.s_power        = mt9p031_set_power,
983cb7a01acSMauro Carvalho Chehab };
984cb7a01acSMauro Carvalho Chehab 
9857c137c60SBhumika Goyal static const struct v4l2_subdev_video_ops mt9p031_subdev_video_ops = {
986cb7a01acSMauro Carvalho Chehab 	.s_stream       = mt9p031_s_stream,
987cb7a01acSMauro Carvalho Chehab };
988cb7a01acSMauro Carvalho Chehab 
9897c137c60SBhumika Goyal static const struct v4l2_subdev_pad_ops mt9p031_subdev_pad_ops = {
990cb7a01acSMauro Carvalho Chehab 	.enum_mbus_code = mt9p031_enum_mbus_code,
991cb7a01acSMauro Carvalho Chehab 	.enum_frame_size = mt9p031_enum_frame_size,
992cb7a01acSMauro Carvalho Chehab 	.get_fmt = mt9p031_get_format,
993cb7a01acSMauro Carvalho Chehab 	.set_fmt = mt9p031_set_format,
9941a023febSHans Verkuil 	.get_selection = mt9p031_get_selection,
9951a023febSHans Verkuil 	.set_selection = mt9p031_set_selection,
996cb7a01acSMauro Carvalho Chehab };
997cb7a01acSMauro Carvalho Chehab 
9987c137c60SBhumika Goyal static const struct v4l2_subdev_ops mt9p031_subdev_ops = {
999cb7a01acSMauro Carvalho Chehab 	.core   = &mt9p031_subdev_core_ops,
1000cb7a01acSMauro Carvalho Chehab 	.video  = &mt9p031_subdev_video_ops,
1001cb7a01acSMauro Carvalho Chehab 	.pad    = &mt9p031_subdev_pad_ops,
1002cb7a01acSMauro Carvalho Chehab };
1003cb7a01acSMauro Carvalho Chehab 
1004cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_internal_ops mt9p031_subdev_internal_ops = {
1005cb7a01acSMauro Carvalho Chehab 	.registered = mt9p031_registered,
1006cb7a01acSMauro Carvalho Chehab 	.open = mt9p031_open,
1007cb7a01acSMauro Carvalho Chehab 	.close = mt9p031_close,
1008cb7a01acSMauro Carvalho Chehab };
1009cb7a01acSMauro Carvalho Chehab 
1010cb7a01acSMauro Carvalho Chehab /* -----------------------------------------------------------------------------
1011cb7a01acSMauro Carvalho Chehab  * Driver initialization and probing
1012cb7a01acSMauro Carvalho Chehab  */
1013cb7a01acSMauro Carvalho Chehab 
10148d4da37cSLad, Prabhakar static struct mt9p031_platform_data *
10158d4da37cSLad, Prabhakar mt9p031_get_pdata(struct i2c_client *client)
10168d4da37cSLad, Prabhakar {
10178d4da37cSLad, Prabhakar 	struct mt9p031_platform_data *pdata;
10188d4da37cSLad, Prabhakar 	struct device_node *np;
10198d4da37cSLad, Prabhakar 
10208d4da37cSLad, Prabhakar 	if (!IS_ENABLED(CONFIG_OF) || !client->dev.of_node)
10218d4da37cSLad, Prabhakar 		return client->dev.platform_data;
10228d4da37cSLad, Prabhakar 
1023fd9fdb78SPhilipp Zabel 	np = of_graph_get_next_endpoint(client->dev.of_node, NULL);
10248d4da37cSLad, Prabhakar 	if (!np)
10258d4da37cSLad, Prabhakar 		return NULL;
10268d4da37cSLad, Prabhakar 
10278d4da37cSLad, Prabhakar 	pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
10288d4da37cSLad, Prabhakar 	if (!pdata)
10298d4da37cSLad, Prabhakar 		goto done;
10308d4da37cSLad, Prabhakar 
10318d4da37cSLad, Prabhakar 	of_property_read_u32(np, "input-clock-frequency", &pdata->ext_freq);
10328d4da37cSLad, Prabhakar 	of_property_read_u32(np, "pixel-clock-frequency", &pdata->target_freq);
10338d4da37cSLad, Prabhakar 
10348d4da37cSLad, Prabhakar done:
10358d4da37cSLad, Prabhakar 	of_node_put(np);
10368d4da37cSLad, Prabhakar 	return pdata;
10378d4da37cSLad, Prabhakar }
10388d4da37cSLad, Prabhakar 
1039cb7a01acSMauro Carvalho Chehab static int mt9p031_probe(struct i2c_client *client,
1040cb7a01acSMauro Carvalho Chehab 			 const struct i2c_device_id *did)
1041cb7a01acSMauro Carvalho Chehab {
10428d4da37cSLad, Prabhakar 	struct mt9p031_platform_data *pdata = mt9p031_get_pdata(client);
1043a8a3e813SWolfram Sang 	struct i2c_adapter *adapter = client->adapter;
1044cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031;
1045cb7a01acSMauro Carvalho Chehab 	unsigned int i;
1046cb7a01acSMauro Carvalho Chehab 	int ret;
1047cb7a01acSMauro Carvalho Chehab 
1048cb7a01acSMauro Carvalho Chehab 	if (pdata == NULL) {
1049cb7a01acSMauro Carvalho Chehab 		dev_err(&client->dev, "No platform data\n");
1050cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
1051cb7a01acSMauro Carvalho Chehab 	}
1052cb7a01acSMauro Carvalho Chehab 
1053cb7a01acSMauro Carvalho Chehab 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
1054cb7a01acSMauro Carvalho Chehab 		dev_warn(&client->dev,
1055cb7a01acSMauro Carvalho Chehab 			"I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
1056cb7a01acSMauro Carvalho Chehab 		return -EIO;
1057cb7a01acSMauro Carvalho Chehab 	}
1058cb7a01acSMauro Carvalho Chehab 
105937b9f211SLaurent Pinchart 	mt9p031 = devm_kzalloc(&client->dev, sizeof(*mt9p031), GFP_KERNEL);
1060cb7a01acSMauro Carvalho Chehab 	if (mt9p031 == NULL)
1061cb7a01acSMauro Carvalho Chehab 		return -ENOMEM;
1062cb7a01acSMauro Carvalho Chehab 
1063cb7a01acSMauro Carvalho Chehab 	mt9p031->pdata = pdata;
1064cb7a01acSMauro Carvalho Chehab 	mt9p031->output_control	= MT9P031_OUTPUT_CONTROL_DEF;
1065cb7a01acSMauro Carvalho Chehab 	mt9p031->mode2 = MT9P031_READ_MODE_2_ROW_BLC;
1066cb7a01acSMauro Carvalho Chehab 	mt9p031->model = did->driver_data;
1067cb7a01acSMauro Carvalho Chehab 
10687997196cSLaurent Pinchart 	mt9p031->regulators[0].supply = "vdd";
10697997196cSLaurent Pinchart 	mt9p031->regulators[1].supply = "vdd_io";
10707997196cSLaurent Pinchart 	mt9p031->regulators[2].supply = "vaa";
107197f21276SLaurent Pinchart 
10727997196cSLaurent Pinchart 	ret = devm_regulator_bulk_get(&client->dev, 3, mt9p031->regulators);
10737997196cSLaurent Pinchart 	if (ret < 0) {
107497f21276SLaurent Pinchart 		dev_err(&client->dev, "Unable to get regulators\n");
10757997196cSLaurent Pinchart 		return ret;
107697f21276SLaurent Pinchart 	}
107797f21276SLaurent Pinchart 
107815af4a53SLad, Prabhakar 	mutex_init(&mt9p031->power_lock);
107915af4a53SLad, Prabhakar 
1080b28d7017SLad, Prabhakar 	v4l2_ctrl_handler_init(&mt9p031->ctrls, ARRAY_SIZE(mt9p031_ctrls) + 6);
1081cb7a01acSMauro Carvalho Chehab 
1082cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
1083cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_EXPOSURE, MT9P031_SHUTTER_WIDTH_MIN,
1084cb7a01acSMauro Carvalho Chehab 			  MT9P031_SHUTTER_WIDTH_MAX, 1,
1085cb7a01acSMauro Carvalho Chehab 			  MT9P031_SHUTTER_WIDTH_DEF);
1086cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
1087cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_GAIN, MT9P031_GLOBAL_GAIN_MIN,
1088cb7a01acSMauro Carvalho Chehab 			  MT9P031_GLOBAL_GAIN_MAX, 1, MT9P031_GLOBAL_GAIN_DEF);
1089cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
1090cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_HFLIP, 0, 1, 1, 0);
1091cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
1092cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_VFLIP, 0, 1, 1, 0);
1093cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
1094cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_PIXEL_RATE, pdata->target_freq,
1095cb7a01acSMauro Carvalho Chehab 			  pdata->target_freq, 1, pdata->target_freq);
1096b28d7017SLad, Prabhakar 	v4l2_ctrl_new_std_menu_items(&mt9p031->ctrls, &mt9p031_ctrl_ops,
1097b28d7017SLad, Prabhakar 			  V4L2_CID_TEST_PATTERN,
1098b28d7017SLad, Prabhakar 			  ARRAY_SIZE(mt9p031_test_pattern_menu) - 1, 0,
1099b28d7017SLad, Prabhakar 			  0, mt9p031_test_pattern_menu);
1100cb7a01acSMauro Carvalho Chehab 
1101cb7a01acSMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(mt9p031_ctrls); ++i)
1102cb7a01acSMauro Carvalho Chehab 		v4l2_ctrl_new_custom(&mt9p031->ctrls, &mt9p031_ctrls[i], NULL);
1103cb7a01acSMauro Carvalho Chehab 
1104cb7a01acSMauro Carvalho Chehab 	mt9p031->subdev.ctrl_handler = &mt9p031->ctrls;
1105cb7a01acSMauro Carvalho Chehab 
1106cb7a01acSMauro Carvalho Chehab 	if (mt9p031->ctrls.error) {
1107cb7a01acSMauro Carvalho Chehab 		printk(KERN_INFO "%s: control initialization error %d\n",
1108cb7a01acSMauro Carvalho Chehab 		       __func__, mt9p031->ctrls.error);
1109cb7a01acSMauro Carvalho Chehab 		ret = mt9p031->ctrls.error;
1110cb7a01acSMauro Carvalho Chehab 		goto done;
1111cb7a01acSMauro Carvalho Chehab 	}
1112cb7a01acSMauro Carvalho Chehab 
1113cb7a01acSMauro Carvalho Chehab 	mt9p031->blc_auto = v4l2_ctrl_find(&mt9p031->ctrls, V4L2_CID_BLC_AUTO);
1114cb7a01acSMauro Carvalho Chehab 	mt9p031->blc_offset = v4l2_ctrl_find(&mt9p031->ctrls,
1115cb7a01acSMauro Carvalho Chehab 					     V4L2_CID_BLC_DIGITAL_OFFSET);
1116cb7a01acSMauro Carvalho Chehab 
1117cb7a01acSMauro Carvalho Chehab 	v4l2_i2c_subdev_init(&mt9p031->subdev, client, &mt9p031_subdev_ops);
1118cb7a01acSMauro Carvalho Chehab 	mt9p031->subdev.internal_ops = &mt9p031_subdev_internal_ops;
1119cb7a01acSMauro Carvalho Chehab 
1120173bf6e5SHans Verkuil 	mt9p031->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR;
1121cb7a01acSMauro Carvalho Chehab 	mt9p031->pad.flags = MEDIA_PAD_FL_SOURCE;
1122ab22e77cSMauro Carvalho Chehab 	ret = media_entity_pads_init(&mt9p031->subdev.entity, 1, &mt9p031->pad);
1123cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
1124cb7a01acSMauro Carvalho Chehab 		goto done;
1125cb7a01acSMauro Carvalho Chehab 
1126cb7a01acSMauro Carvalho Chehab 	mt9p031->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
1127cb7a01acSMauro Carvalho Chehab 
1128cb7a01acSMauro Carvalho Chehab 	mt9p031->crop.width = MT9P031_WINDOW_WIDTH_DEF;
1129cb7a01acSMauro Carvalho Chehab 	mt9p031->crop.height = MT9P031_WINDOW_HEIGHT_DEF;
1130cb7a01acSMauro Carvalho Chehab 	mt9p031->crop.left = MT9P031_COLUMN_START_DEF;
1131cb7a01acSMauro Carvalho Chehab 	mt9p031->crop.top = MT9P031_ROW_START_DEF;
1132cb7a01acSMauro Carvalho Chehab 
1133cb7a01acSMauro Carvalho Chehab 	if (mt9p031->model == MT9P031_MODEL_MONOCHROME)
1134f5fe58fdSBoris BREZILLON 		mt9p031->format.code = MEDIA_BUS_FMT_Y12_1X12;
1135cb7a01acSMauro Carvalho Chehab 	else
1136f5fe58fdSBoris BREZILLON 		mt9p031->format.code = MEDIA_BUS_FMT_SGRBG12_1X12;
1137cb7a01acSMauro Carvalho Chehab 
1138cb7a01acSMauro Carvalho Chehab 	mt9p031->format.width = MT9P031_WINDOW_WIDTH_DEF;
1139cb7a01acSMauro Carvalho Chehab 	mt9p031->format.height = MT9P031_WINDOW_HEIGHT_DEF;
1140cb7a01acSMauro Carvalho Chehab 	mt9p031->format.field = V4L2_FIELD_NONE;
1141cb7a01acSMauro Carvalho Chehab 	mt9p031->format.colorspace = V4L2_COLORSPACE_SRGB;
1142cb7a01acSMauro Carvalho Chehab 
11437c3be9f8SLaurent Pinchart 	mt9p031->reset = devm_gpiod_get_optional(&client->dev, "reset",
11447c3be9f8SLaurent Pinchart 						 GPIOD_OUT_HIGH);
1145cb7a01acSMauro Carvalho Chehab 
1146d6749258SLaurent Pinchart 	ret = mt9p031_clk_setup(mt9p031);
11479012d088SLad, Prabhakar 	if (ret)
11489012d088SLad, Prabhakar 		goto done;
11499012d088SLad, Prabhakar 
11509012d088SLad, Prabhakar 	ret = v4l2_async_register_subdev(&mt9p031->subdev);
1151cb7a01acSMauro Carvalho Chehab 
1152cb7a01acSMauro Carvalho Chehab done:
1153cb7a01acSMauro Carvalho Chehab 	if (ret < 0) {
1154cb7a01acSMauro Carvalho Chehab 		v4l2_ctrl_handler_free(&mt9p031->ctrls);
1155cb7a01acSMauro Carvalho Chehab 		media_entity_cleanup(&mt9p031->subdev.entity);
115615af4a53SLad, Prabhakar 		mutex_destroy(&mt9p031->power_lock);
1157cb7a01acSMauro Carvalho Chehab 	}
1158cb7a01acSMauro Carvalho Chehab 
1159cb7a01acSMauro Carvalho Chehab 	return ret;
1160cb7a01acSMauro Carvalho Chehab }
1161cb7a01acSMauro Carvalho Chehab 
1162cb7a01acSMauro Carvalho Chehab static int mt9p031_remove(struct i2c_client *client)
1163cb7a01acSMauro Carvalho Chehab {
1164cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
1165cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
1166cb7a01acSMauro Carvalho Chehab 
1167cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_free(&mt9p031->ctrls);
11689012d088SLad, Prabhakar 	v4l2_async_unregister_subdev(subdev);
1169cb7a01acSMauro Carvalho Chehab 	media_entity_cleanup(&subdev->entity);
117015af4a53SLad, Prabhakar 	mutex_destroy(&mt9p031->power_lock);
1171cb7a01acSMauro Carvalho Chehab 
1172cb7a01acSMauro Carvalho Chehab 	return 0;
1173cb7a01acSMauro Carvalho Chehab }
1174cb7a01acSMauro Carvalho Chehab 
1175cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id mt9p031_id[] = {
1176cb7a01acSMauro Carvalho Chehab 	{ "mt9p031", MT9P031_MODEL_COLOR },
1177cb7a01acSMauro Carvalho Chehab 	{ "mt9p031m", MT9P031_MODEL_MONOCHROME },
1178cb7a01acSMauro Carvalho Chehab 	{ }
1179cb7a01acSMauro Carvalho Chehab };
1180cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, mt9p031_id);
1181cb7a01acSMauro Carvalho Chehab 
11828d4da37cSLad, Prabhakar #if IS_ENABLED(CONFIG_OF)
11838d4da37cSLad, Prabhakar static const struct of_device_id mt9p031_of_match[] = {
11848d4da37cSLad, Prabhakar 	{ .compatible = "aptina,mt9p031", },
11858d4da37cSLad, Prabhakar 	{ .compatible = "aptina,mt9p031m", },
11868d4da37cSLad, Prabhakar 	{ /* sentinel */ },
11878d4da37cSLad, Prabhakar };
11888d4da37cSLad, Prabhakar MODULE_DEVICE_TABLE(of, mt9p031_of_match);
11898d4da37cSLad, Prabhakar #endif
11908d4da37cSLad, Prabhakar 
1191cb7a01acSMauro Carvalho Chehab static struct i2c_driver mt9p031_i2c_driver = {
1192cb7a01acSMauro Carvalho Chehab 	.driver = {
11938d4da37cSLad, Prabhakar 		.of_match_table = of_match_ptr(mt9p031_of_match),
1194cb7a01acSMauro Carvalho Chehab 		.name = "mt9p031",
1195cb7a01acSMauro Carvalho Chehab 	},
1196cb7a01acSMauro Carvalho Chehab 	.probe          = mt9p031_probe,
1197cb7a01acSMauro Carvalho Chehab 	.remove         = mt9p031_remove,
1198cb7a01acSMauro Carvalho Chehab 	.id_table       = mt9p031_id,
1199cb7a01acSMauro Carvalho Chehab };
1200cb7a01acSMauro Carvalho Chehab 
1201cb7a01acSMauro Carvalho Chehab module_i2c_driver(mt9p031_i2c_driver);
1202cb7a01acSMauro Carvalho Chehab 
1203cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("Aptina MT9P031 Camera driver");
1204cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Bastian Hecht <hechtb@gmail.com>");
1205cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL v2");
1206