xref: /linux/drivers/media/i2c/mt9p031.c (revision 9012d0888c8fc473d5760f2367b002dfddee80f9)
1cb7a01acSMauro Carvalho Chehab /*
2cb7a01acSMauro Carvalho Chehab  * Driver for MT9P031 CMOS Image Sensor from Aptina
3cb7a01acSMauro Carvalho Chehab  *
4cb7a01acSMauro Carvalho Chehab  * Copyright (C) 2011, Laurent Pinchart <laurent.pinchart@ideasonboard.com>
5cb7a01acSMauro Carvalho Chehab  * Copyright (C) 2011, Javier Martin <javier.martin@vista-silicon.com>
6cb7a01acSMauro Carvalho Chehab  * Copyright (C) 2011, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
7cb7a01acSMauro Carvalho Chehab  *
8cb7a01acSMauro Carvalho Chehab  * Based on the MT9V032 driver and Bastian Hecht's code.
9cb7a01acSMauro Carvalho Chehab  *
10cb7a01acSMauro Carvalho Chehab  * This program is free software; you can redistribute it and/or modify
11cb7a01acSMauro Carvalho Chehab  * it under the terms of the GNU General Public License version 2 as
12cb7a01acSMauro Carvalho Chehab  * published by the Free Software Foundation.
13cb7a01acSMauro Carvalho Chehab  */
14cb7a01acSMauro Carvalho Chehab 
15d6749258SLaurent Pinchart #include <linux/clk.h>
16cb7a01acSMauro Carvalho Chehab #include <linux/delay.h>
17cb7a01acSMauro Carvalho Chehab #include <linux/device.h>
18cb7a01acSMauro Carvalho Chehab #include <linux/gpio.h>
19cb7a01acSMauro Carvalho Chehab #include <linux/i2c.h>
20cb7a01acSMauro Carvalho Chehab #include <linux/log2.h>
218d4da37cSLad, Prabhakar #include <linux/module.h>
2264695905SSachin Kamat #include <linux/of.h>
238d4da37cSLad, Prabhakar #include <linux/of_gpio.h>
24fd9fdb78SPhilipp Zabel #include <linux/of_graph.h>
25cb7a01acSMauro Carvalho Chehab #include <linux/pm.h>
2697f21276SLaurent Pinchart #include <linux/regulator/consumer.h>
27cb7a01acSMauro Carvalho Chehab #include <linux/slab.h>
28cb7a01acSMauro Carvalho Chehab #include <linux/videodev2.h>
29cb7a01acSMauro Carvalho Chehab 
30cb7a01acSMauro Carvalho Chehab #include <media/mt9p031.h>
31*9012d088SLad, Prabhakar #include <media/v4l2-async.h>
32cb7a01acSMauro Carvalho Chehab #include <media/v4l2-ctrls.h>
33cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h>
34cb7a01acSMauro Carvalho Chehab #include <media/v4l2-subdev.h>
35cb7a01acSMauro Carvalho Chehab 
36cb7a01acSMauro Carvalho Chehab #include "aptina-pll.h"
37cb7a01acSMauro Carvalho Chehab 
38cb7a01acSMauro Carvalho Chehab #define MT9P031_PIXEL_ARRAY_WIDTH			2752
39cb7a01acSMauro Carvalho Chehab #define MT9P031_PIXEL_ARRAY_HEIGHT			2004
40cb7a01acSMauro Carvalho Chehab 
41cb7a01acSMauro Carvalho Chehab #define MT9P031_CHIP_VERSION				0x00
42cb7a01acSMauro Carvalho Chehab #define		MT9P031_CHIP_VERSION_VALUE		0x1801
43cb7a01acSMauro Carvalho Chehab #define MT9P031_ROW_START				0x01
44cb7a01acSMauro Carvalho Chehab #define		MT9P031_ROW_START_MIN			0
45cb7a01acSMauro Carvalho Chehab #define		MT9P031_ROW_START_MAX			2004
46cb7a01acSMauro Carvalho Chehab #define		MT9P031_ROW_START_DEF			54
47cb7a01acSMauro Carvalho Chehab #define MT9P031_COLUMN_START				0x02
48cb7a01acSMauro Carvalho Chehab #define		MT9P031_COLUMN_START_MIN		0
49cb7a01acSMauro Carvalho Chehab #define		MT9P031_COLUMN_START_MAX		2750
50cb7a01acSMauro Carvalho Chehab #define		MT9P031_COLUMN_START_DEF		16
51cb7a01acSMauro Carvalho Chehab #define MT9P031_WINDOW_HEIGHT				0x03
52cb7a01acSMauro Carvalho Chehab #define		MT9P031_WINDOW_HEIGHT_MIN		2
53cb7a01acSMauro Carvalho Chehab #define		MT9P031_WINDOW_HEIGHT_MAX		2006
54cb7a01acSMauro Carvalho Chehab #define		MT9P031_WINDOW_HEIGHT_DEF		1944
55cb7a01acSMauro Carvalho Chehab #define MT9P031_WINDOW_WIDTH				0x04
56cb7a01acSMauro Carvalho Chehab #define		MT9P031_WINDOW_WIDTH_MIN		2
57cb7a01acSMauro Carvalho Chehab #define		MT9P031_WINDOW_WIDTH_MAX		2752
58cb7a01acSMauro Carvalho Chehab #define		MT9P031_WINDOW_WIDTH_DEF		2592
59cb7a01acSMauro Carvalho Chehab #define MT9P031_HORIZONTAL_BLANK			0x05
60cb7a01acSMauro Carvalho Chehab #define		MT9P031_HORIZONTAL_BLANK_MIN		0
61cb7a01acSMauro Carvalho Chehab #define		MT9P031_HORIZONTAL_BLANK_MAX		4095
62cb7a01acSMauro Carvalho Chehab #define MT9P031_VERTICAL_BLANK				0x06
635266c98bSLaurent Pinchart #define		MT9P031_VERTICAL_BLANK_MIN		1
645266c98bSLaurent Pinchart #define		MT9P031_VERTICAL_BLANK_MAX		4096
655266c98bSLaurent Pinchart #define		MT9P031_VERTICAL_BLANK_DEF		26
66cb7a01acSMauro Carvalho Chehab #define MT9P031_OUTPUT_CONTROL				0x07
67cb7a01acSMauro Carvalho Chehab #define		MT9P031_OUTPUT_CONTROL_CEN		2
68cb7a01acSMauro Carvalho Chehab #define		MT9P031_OUTPUT_CONTROL_SYN		1
69cb7a01acSMauro Carvalho Chehab #define		MT9P031_OUTPUT_CONTROL_DEF		0x1f82
70cb7a01acSMauro Carvalho Chehab #define MT9P031_SHUTTER_WIDTH_UPPER			0x08
71cb7a01acSMauro Carvalho Chehab #define MT9P031_SHUTTER_WIDTH_LOWER			0x09
72cb7a01acSMauro Carvalho Chehab #define		MT9P031_SHUTTER_WIDTH_MIN		1
73cb7a01acSMauro Carvalho Chehab #define		MT9P031_SHUTTER_WIDTH_MAX		1048575
74cb7a01acSMauro Carvalho Chehab #define		MT9P031_SHUTTER_WIDTH_DEF		1943
75cb7a01acSMauro Carvalho Chehab #define	MT9P031_PLL_CONTROL				0x10
76cb7a01acSMauro Carvalho Chehab #define		MT9P031_PLL_CONTROL_PWROFF		0x0050
77cb7a01acSMauro Carvalho Chehab #define		MT9P031_PLL_CONTROL_PWRON		0x0051
78cb7a01acSMauro Carvalho Chehab #define		MT9P031_PLL_CONTROL_USEPLL		0x0052
79cb7a01acSMauro Carvalho Chehab #define	MT9P031_PLL_CONFIG_1				0x11
80cb7a01acSMauro Carvalho Chehab #define	MT9P031_PLL_CONFIG_2				0x12
81cb7a01acSMauro Carvalho Chehab #define MT9P031_PIXEL_CLOCK_CONTROL			0x0a
82a970449eSLaurent Pinchart #define		MT9P031_PIXEL_CLOCK_INVERT		(1 << 15)
83a970449eSLaurent Pinchart #define		MT9P031_PIXEL_CLOCK_SHIFT(n)		((n) << 8)
84a970449eSLaurent Pinchart #define		MT9P031_PIXEL_CLOCK_DIVIDE(n)		((n) << 0)
85cb7a01acSMauro Carvalho Chehab #define MT9P031_FRAME_RESTART				0x0b
86cb7a01acSMauro Carvalho Chehab #define MT9P031_SHUTTER_DELAY				0x0c
87cb7a01acSMauro Carvalho Chehab #define MT9P031_RST					0x0d
88cb7a01acSMauro Carvalho Chehab #define		MT9P031_RST_ENABLE			1
89cb7a01acSMauro Carvalho Chehab #define		MT9P031_RST_DISABLE			0
90cb7a01acSMauro Carvalho Chehab #define MT9P031_READ_MODE_1				0x1e
91cb7a01acSMauro Carvalho Chehab #define MT9P031_READ_MODE_2				0x20
92cb7a01acSMauro Carvalho Chehab #define		MT9P031_READ_MODE_2_ROW_MIR		(1 << 15)
93cb7a01acSMauro Carvalho Chehab #define		MT9P031_READ_MODE_2_COL_MIR		(1 << 14)
94cb7a01acSMauro Carvalho Chehab #define		MT9P031_READ_MODE_2_ROW_BLC		(1 << 6)
95cb7a01acSMauro Carvalho Chehab #define MT9P031_ROW_ADDRESS_MODE			0x22
96cb7a01acSMauro Carvalho Chehab #define MT9P031_COLUMN_ADDRESS_MODE			0x23
97cb7a01acSMauro Carvalho Chehab #define MT9P031_GLOBAL_GAIN				0x35
98cb7a01acSMauro Carvalho Chehab #define		MT9P031_GLOBAL_GAIN_MIN			8
99cb7a01acSMauro Carvalho Chehab #define		MT9P031_GLOBAL_GAIN_MAX			1024
100cb7a01acSMauro Carvalho Chehab #define		MT9P031_GLOBAL_GAIN_DEF			8
101cb7a01acSMauro Carvalho Chehab #define		MT9P031_GLOBAL_GAIN_MULT		(1 << 6)
102cb7a01acSMauro Carvalho Chehab #define MT9P031_ROW_BLACK_TARGET			0x49
103cb7a01acSMauro Carvalho Chehab #define MT9P031_ROW_BLACK_DEF_OFFSET			0x4b
104cb7a01acSMauro Carvalho Chehab #define MT9P031_GREEN1_OFFSET				0x60
105cb7a01acSMauro Carvalho Chehab #define MT9P031_GREEN2_OFFSET				0x61
106cb7a01acSMauro Carvalho Chehab #define MT9P031_BLACK_LEVEL_CALIBRATION			0x62
107cb7a01acSMauro Carvalho Chehab #define		MT9P031_BLC_MANUAL_BLC			(1 << 0)
108cb7a01acSMauro Carvalho Chehab #define MT9P031_RED_OFFSET				0x63
109cb7a01acSMauro Carvalho Chehab #define MT9P031_BLUE_OFFSET				0x64
110cb7a01acSMauro Carvalho Chehab #define MT9P031_TEST_PATTERN				0xa0
111cb7a01acSMauro Carvalho Chehab #define		MT9P031_TEST_PATTERN_SHIFT		3
112cb7a01acSMauro Carvalho Chehab #define		MT9P031_TEST_PATTERN_ENABLE		(1 << 0)
113cb7a01acSMauro Carvalho Chehab #define		MT9P031_TEST_PATTERN_DISABLE		(0 << 0)
114cb7a01acSMauro Carvalho Chehab #define MT9P031_TEST_PATTERN_GREEN			0xa1
115cb7a01acSMauro Carvalho Chehab #define MT9P031_TEST_PATTERN_RED			0xa2
116cb7a01acSMauro Carvalho Chehab #define MT9P031_TEST_PATTERN_BLUE			0xa3
117cb7a01acSMauro Carvalho Chehab 
118cb7a01acSMauro Carvalho Chehab enum mt9p031_model {
119cb7a01acSMauro Carvalho Chehab 	MT9P031_MODEL_COLOR,
120cb7a01acSMauro Carvalho Chehab 	MT9P031_MODEL_MONOCHROME,
121cb7a01acSMauro Carvalho Chehab };
122cb7a01acSMauro Carvalho Chehab 
123cb7a01acSMauro Carvalho Chehab struct mt9p031 {
124cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev subdev;
125cb7a01acSMauro Carvalho Chehab 	struct media_pad pad;
126cb7a01acSMauro Carvalho Chehab 	struct v4l2_rect crop;  /* Sensor window */
127cb7a01acSMauro Carvalho Chehab 	struct v4l2_mbus_framefmt format;
128cb7a01acSMauro Carvalho Chehab 	struct mt9p031_platform_data *pdata;
129cb7a01acSMauro Carvalho Chehab 	struct mutex power_lock; /* lock to protect power_count */
130cb7a01acSMauro Carvalho Chehab 	int power_count;
131cb7a01acSMauro Carvalho Chehab 
132d6749258SLaurent Pinchart 	struct clk *clk;
1337997196cSLaurent Pinchart 	struct regulator_bulk_data regulators[3];
13497f21276SLaurent Pinchart 
135cb7a01acSMauro Carvalho Chehab 	enum mt9p031_model model;
136cb7a01acSMauro Carvalho Chehab 	struct aptina_pll pll;
137a970449eSLaurent Pinchart 	unsigned int clk_div;
138a970449eSLaurent Pinchart 	bool use_pll;
139cb7a01acSMauro Carvalho Chehab 	int reset;
140cb7a01acSMauro Carvalho Chehab 
141cb7a01acSMauro Carvalho Chehab 	struct v4l2_ctrl_handler ctrls;
142cb7a01acSMauro Carvalho Chehab 	struct v4l2_ctrl *blc_auto;
143cb7a01acSMauro Carvalho Chehab 	struct v4l2_ctrl *blc_offset;
144cb7a01acSMauro Carvalho Chehab 
145cb7a01acSMauro Carvalho Chehab 	/* Registers cache */
146cb7a01acSMauro Carvalho Chehab 	u16 output_control;
147cb7a01acSMauro Carvalho Chehab 	u16 mode2;
148cb7a01acSMauro Carvalho Chehab };
149cb7a01acSMauro Carvalho Chehab 
150cb7a01acSMauro Carvalho Chehab static struct mt9p031 *to_mt9p031(struct v4l2_subdev *sd)
151cb7a01acSMauro Carvalho Chehab {
152cb7a01acSMauro Carvalho Chehab 	return container_of(sd, struct mt9p031, subdev);
153cb7a01acSMauro Carvalho Chehab }
154cb7a01acSMauro Carvalho Chehab 
155cb7a01acSMauro Carvalho Chehab static int mt9p031_read(struct i2c_client *client, u8 reg)
156cb7a01acSMauro Carvalho Chehab {
157cb7a01acSMauro Carvalho Chehab 	return i2c_smbus_read_word_swapped(client, reg);
158cb7a01acSMauro Carvalho Chehab }
159cb7a01acSMauro Carvalho Chehab 
160cb7a01acSMauro Carvalho Chehab static int mt9p031_write(struct i2c_client *client, u8 reg, u16 data)
161cb7a01acSMauro Carvalho Chehab {
162cb7a01acSMauro Carvalho Chehab 	return i2c_smbus_write_word_swapped(client, reg, data);
163cb7a01acSMauro Carvalho Chehab }
164cb7a01acSMauro Carvalho Chehab 
165cb7a01acSMauro Carvalho Chehab static int mt9p031_set_output_control(struct mt9p031 *mt9p031, u16 clear,
166cb7a01acSMauro Carvalho Chehab 				      u16 set)
167cb7a01acSMauro Carvalho Chehab {
168cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
169cb7a01acSMauro Carvalho Chehab 	u16 value = (mt9p031->output_control & ~clear) | set;
170cb7a01acSMauro Carvalho Chehab 	int ret;
171cb7a01acSMauro Carvalho Chehab 
172cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_OUTPUT_CONTROL, value);
173cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
174cb7a01acSMauro Carvalho Chehab 		return ret;
175cb7a01acSMauro Carvalho Chehab 
176cb7a01acSMauro Carvalho Chehab 	mt9p031->output_control = value;
177cb7a01acSMauro Carvalho Chehab 	return 0;
178cb7a01acSMauro Carvalho Chehab }
179cb7a01acSMauro Carvalho Chehab 
180cb7a01acSMauro Carvalho Chehab static int mt9p031_set_mode2(struct mt9p031 *mt9p031, u16 clear, u16 set)
181cb7a01acSMauro Carvalho Chehab {
182cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
183cb7a01acSMauro Carvalho Chehab 	u16 value = (mt9p031->mode2 & ~clear) | set;
184cb7a01acSMauro Carvalho Chehab 	int ret;
185cb7a01acSMauro Carvalho Chehab 
186cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_READ_MODE_2, value);
187cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
188cb7a01acSMauro Carvalho Chehab 		return ret;
189cb7a01acSMauro Carvalho Chehab 
190cb7a01acSMauro Carvalho Chehab 	mt9p031->mode2 = value;
191cb7a01acSMauro Carvalho Chehab 	return 0;
192cb7a01acSMauro Carvalho Chehab }
193cb7a01acSMauro Carvalho Chehab 
194cb7a01acSMauro Carvalho Chehab static int mt9p031_reset(struct mt9p031 *mt9p031)
195cb7a01acSMauro Carvalho Chehab {
196cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
197cb7a01acSMauro Carvalho Chehab 	int ret;
198cb7a01acSMauro Carvalho Chehab 
199cb7a01acSMauro Carvalho Chehab 	/* Disable chip output, synchronous option update */
200cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_RST, MT9P031_RST_ENABLE);
201cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
202cb7a01acSMauro Carvalho Chehab 		return ret;
203cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_RST, MT9P031_RST_DISABLE);
204cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
205cb7a01acSMauro Carvalho Chehab 		return ret;
206cb7a01acSMauro Carvalho Chehab 
207a970449eSLaurent Pinchart 	ret = mt9p031_write(client, MT9P031_PIXEL_CLOCK_CONTROL,
208a970449eSLaurent Pinchart 			    MT9P031_PIXEL_CLOCK_DIVIDE(mt9p031->clk_div));
209a970449eSLaurent Pinchart 	if (ret < 0)
210a970449eSLaurent Pinchart 		return ret;
211a970449eSLaurent Pinchart 
212cb7a01acSMauro Carvalho Chehab 	return mt9p031_set_output_control(mt9p031, MT9P031_OUTPUT_CONTROL_CEN,
213cb7a01acSMauro Carvalho Chehab 					  0);
214cb7a01acSMauro Carvalho Chehab }
215cb7a01acSMauro Carvalho Chehab 
216d6749258SLaurent Pinchart static int mt9p031_clk_setup(struct mt9p031 *mt9p031)
217cb7a01acSMauro Carvalho Chehab {
218cb7a01acSMauro Carvalho Chehab 	static const struct aptina_pll_limits limits = {
219cb7a01acSMauro Carvalho Chehab 		.ext_clock_min = 6000000,
220cb7a01acSMauro Carvalho Chehab 		.ext_clock_max = 27000000,
221cb7a01acSMauro Carvalho Chehab 		.int_clock_min = 2000000,
222cb7a01acSMauro Carvalho Chehab 		.int_clock_max = 13500000,
223cb7a01acSMauro Carvalho Chehab 		.out_clock_min = 180000000,
224cb7a01acSMauro Carvalho Chehab 		.out_clock_max = 360000000,
225cb7a01acSMauro Carvalho Chehab 		.pix_clock_max = 96000000,
226cb7a01acSMauro Carvalho Chehab 		.n_min = 1,
227cb7a01acSMauro Carvalho Chehab 		.n_max = 64,
228cb7a01acSMauro Carvalho Chehab 		.m_min = 16,
229cb7a01acSMauro Carvalho Chehab 		.m_max = 255,
230cb7a01acSMauro Carvalho Chehab 		.p1_min = 1,
231cb7a01acSMauro Carvalho Chehab 		.p1_max = 128,
232cb7a01acSMauro Carvalho Chehab 	};
233cb7a01acSMauro Carvalho Chehab 
234cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
235cb7a01acSMauro Carvalho Chehab 	struct mt9p031_platform_data *pdata = mt9p031->pdata;
236ee2d16d7SLad, Prabhakar 	int ret;
237cb7a01acSMauro Carvalho Chehab 
238d6749258SLaurent Pinchart 	mt9p031->clk = devm_clk_get(&client->dev, NULL);
239d6749258SLaurent Pinchart 	if (IS_ERR(mt9p031->clk))
240d6749258SLaurent Pinchart 		return PTR_ERR(mt9p031->clk);
241d6749258SLaurent Pinchart 
242ee2d16d7SLad, Prabhakar 	ret = clk_set_rate(mt9p031->clk, pdata->ext_freq);
243ee2d16d7SLad, Prabhakar 	if (ret < 0)
244ee2d16d7SLad, Prabhakar 		return ret;
245d6749258SLaurent Pinchart 
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 	 */
249a970449eSLaurent Pinchart 	if (pdata->ext_freq > limits.ext_clock_max) {
250a970449eSLaurent Pinchart 		unsigned int div;
251a970449eSLaurent Pinchart 
252a970449eSLaurent Pinchart 		div = DIV_ROUND_UP(pdata->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 
261cb7a01acSMauro Carvalho Chehab 	mt9p031->pll.ext_clock = pdata->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 
312cb7a01acSMauro Carvalho Chehab 	/* Ensure RESET_BAR is low */
3132660a22bSLaurent Pinchart 	if (gpio_is_valid(mt9p031->reset)) {
314cb7a01acSMauro Carvalho Chehab 		gpio_set_value(mt9p031->reset, 0);
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 */
3352660a22bSLaurent Pinchart 	if (gpio_is_valid(mt9p031->reset)) {
336cb7a01acSMauro Carvalho Chehab 		gpio_set_value(mt9p031->reset, 1);
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 {
3452660a22bSLaurent Pinchart 	if (gpio_is_valid(mt9p031->reset)) {
346cb7a01acSMauro Carvalho Chehab 		gpio_set_value(mt9p031->reset, 0);
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 	if (mt9p031->clk)
354d6749258SLaurent Pinchart 		clk_disable_unprepare(mt9p031->clk);
355cb7a01acSMauro Carvalho Chehab }
356cb7a01acSMauro Carvalho Chehab 
357cb7a01acSMauro Carvalho Chehab static int __mt9p031_set_power(struct mt9p031 *mt9p031, bool on)
358cb7a01acSMauro Carvalho Chehab {
359cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
360cb7a01acSMauro Carvalho Chehab 	int ret;
361cb7a01acSMauro Carvalho Chehab 
362cb7a01acSMauro Carvalho Chehab 	if (!on) {
363cb7a01acSMauro Carvalho Chehab 		mt9p031_power_off(mt9p031);
364cb7a01acSMauro Carvalho Chehab 		return 0;
365cb7a01acSMauro Carvalho Chehab 	}
366cb7a01acSMauro Carvalho Chehab 
367cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_power_on(mt9p031);
368cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
369cb7a01acSMauro Carvalho Chehab 		return ret;
370cb7a01acSMauro Carvalho Chehab 
371cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_reset(mt9p031);
372cb7a01acSMauro Carvalho Chehab 	if (ret < 0) {
373cb7a01acSMauro Carvalho Chehab 		dev_err(&client->dev, "Failed to reset the camera\n");
374cb7a01acSMauro Carvalho Chehab 		return ret;
375cb7a01acSMauro Carvalho Chehab 	}
376cb7a01acSMauro Carvalho Chehab 
377cb7a01acSMauro Carvalho Chehab 	return v4l2_ctrl_handler_setup(&mt9p031->ctrls);
378cb7a01acSMauro Carvalho Chehab }
379cb7a01acSMauro Carvalho Chehab 
380cb7a01acSMauro Carvalho Chehab /* -----------------------------------------------------------------------------
381cb7a01acSMauro Carvalho Chehab  * V4L2 subdev video operations
382cb7a01acSMauro Carvalho Chehab  */
383cb7a01acSMauro Carvalho Chehab 
384cb7a01acSMauro Carvalho Chehab static int mt9p031_set_params(struct mt9p031 *mt9p031)
385cb7a01acSMauro Carvalho Chehab {
386cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
387cb7a01acSMauro Carvalho Chehab 	struct v4l2_mbus_framefmt *format = &mt9p031->format;
388cb7a01acSMauro Carvalho Chehab 	const struct v4l2_rect *crop = &mt9p031->crop;
389cb7a01acSMauro Carvalho Chehab 	unsigned int hblank;
390cb7a01acSMauro Carvalho Chehab 	unsigned int vblank;
391cb7a01acSMauro Carvalho Chehab 	unsigned int xskip;
392cb7a01acSMauro Carvalho Chehab 	unsigned int yskip;
393cb7a01acSMauro Carvalho Chehab 	unsigned int xbin;
394cb7a01acSMauro Carvalho Chehab 	unsigned int ybin;
395cb7a01acSMauro Carvalho Chehab 	int ret;
396cb7a01acSMauro Carvalho Chehab 
397cb7a01acSMauro Carvalho Chehab 	/* Windows position and size.
398cb7a01acSMauro Carvalho Chehab 	 *
399cb7a01acSMauro Carvalho Chehab 	 * TODO: Make sure the start coordinates and window size match the
400cb7a01acSMauro Carvalho Chehab 	 * skipping, binning and mirroring (see description of registers 2 and 4
401cb7a01acSMauro Carvalho Chehab 	 * in table 13, and Binning section on page 41).
402cb7a01acSMauro Carvalho Chehab 	 */
403cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_COLUMN_START, crop->left);
404cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
405cb7a01acSMauro Carvalho Chehab 		return ret;
406cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_ROW_START, crop->top);
407cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
408cb7a01acSMauro Carvalho Chehab 		return ret;
409cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_WINDOW_WIDTH, crop->width - 1);
410cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
411cb7a01acSMauro Carvalho Chehab 		return ret;
412cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_WINDOW_HEIGHT, crop->height - 1);
413cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
414cb7a01acSMauro Carvalho Chehab 		return ret;
415cb7a01acSMauro Carvalho Chehab 
416cb7a01acSMauro Carvalho Chehab 	/* Row and column binning and skipping. Use the maximum binning value
417cb7a01acSMauro Carvalho Chehab 	 * compatible with the skipping settings.
418cb7a01acSMauro Carvalho Chehab 	 */
419cb7a01acSMauro Carvalho Chehab 	xskip = DIV_ROUND_CLOSEST(crop->width, format->width);
420cb7a01acSMauro Carvalho Chehab 	yskip = DIV_ROUND_CLOSEST(crop->height, format->height);
421cb7a01acSMauro Carvalho Chehab 	xbin = 1 << (ffs(xskip) - 1);
422cb7a01acSMauro Carvalho Chehab 	ybin = 1 << (ffs(yskip) - 1);
423cb7a01acSMauro Carvalho Chehab 
424cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_COLUMN_ADDRESS_MODE,
425cb7a01acSMauro Carvalho Chehab 			    ((xbin - 1) << 4) | (xskip - 1));
426cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
427cb7a01acSMauro Carvalho Chehab 		return ret;
428cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_ROW_ADDRESS_MODE,
429cb7a01acSMauro Carvalho Chehab 			    ((ybin - 1) << 4) | (yskip - 1));
430cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
431cb7a01acSMauro Carvalho Chehab 		return ret;
432cb7a01acSMauro Carvalho Chehab 
433cb7a01acSMauro Carvalho Chehab 	/* Blanking - use minimum value for horizontal blanking and default
434cb7a01acSMauro Carvalho Chehab 	 * value for vertical blanking.
435cb7a01acSMauro Carvalho Chehab 	 */
4365266c98bSLaurent Pinchart 	hblank = 346 * ybin + 64 + (80 >> min_t(unsigned int, xbin, 3));
437cb7a01acSMauro Carvalho Chehab 	vblank = MT9P031_VERTICAL_BLANK_DEF;
438cb7a01acSMauro Carvalho Chehab 
4395266c98bSLaurent Pinchart 	ret = mt9p031_write(client, MT9P031_HORIZONTAL_BLANK, hblank - 1);
440cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
441cb7a01acSMauro Carvalho Chehab 		return ret;
4425266c98bSLaurent Pinchart 	ret = mt9p031_write(client, MT9P031_VERTICAL_BLANK, vblank - 1);
443cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
444cb7a01acSMauro Carvalho Chehab 		return ret;
445cb7a01acSMauro Carvalho Chehab 
446cb7a01acSMauro Carvalho Chehab 	return ret;
447cb7a01acSMauro Carvalho Chehab }
448cb7a01acSMauro Carvalho Chehab 
449cb7a01acSMauro Carvalho Chehab static int mt9p031_s_stream(struct v4l2_subdev *subdev, int enable)
450cb7a01acSMauro Carvalho Chehab {
451cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
452cb7a01acSMauro Carvalho Chehab 	int ret;
453cb7a01acSMauro Carvalho Chehab 
454cb7a01acSMauro Carvalho Chehab 	if (!enable) {
455cb7a01acSMauro Carvalho Chehab 		/* Stop sensor readout */
456cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_set_output_control(mt9p031,
457cb7a01acSMauro Carvalho Chehab 						 MT9P031_OUTPUT_CONTROL_CEN, 0);
458cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
459cb7a01acSMauro Carvalho Chehab 			return ret;
460cb7a01acSMauro Carvalho Chehab 
461cb7a01acSMauro Carvalho Chehab 		return mt9p031_pll_disable(mt9p031);
462cb7a01acSMauro Carvalho Chehab 	}
463cb7a01acSMauro Carvalho Chehab 
464cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_set_params(mt9p031);
465cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
466cb7a01acSMauro Carvalho Chehab 		return ret;
467cb7a01acSMauro Carvalho Chehab 
468cb7a01acSMauro Carvalho Chehab 	/* Switch to master "normal" mode */
469cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_set_output_control(mt9p031, 0,
470cb7a01acSMauro Carvalho Chehab 					 MT9P031_OUTPUT_CONTROL_CEN);
471cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
472cb7a01acSMauro Carvalho Chehab 		return ret;
473cb7a01acSMauro Carvalho Chehab 
474cb7a01acSMauro Carvalho Chehab 	return mt9p031_pll_enable(mt9p031);
475cb7a01acSMauro Carvalho Chehab }
476cb7a01acSMauro Carvalho Chehab 
477cb7a01acSMauro Carvalho Chehab static int mt9p031_enum_mbus_code(struct v4l2_subdev *subdev,
478f7234138SHans Verkuil 				  struct v4l2_subdev_pad_config *cfg,
479cb7a01acSMauro Carvalho Chehab 				  struct v4l2_subdev_mbus_code_enum *code)
480cb7a01acSMauro Carvalho Chehab {
481cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
482cb7a01acSMauro Carvalho Chehab 
483cb7a01acSMauro Carvalho Chehab 	if (code->pad || code->index)
484cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
485cb7a01acSMauro Carvalho Chehab 
486cb7a01acSMauro Carvalho Chehab 	code->code = mt9p031->format.code;
487cb7a01acSMauro Carvalho Chehab 	return 0;
488cb7a01acSMauro Carvalho Chehab }
489cb7a01acSMauro Carvalho Chehab 
490cb7a01acSMauro Carvalho Chehab static int mt9p031_enum_frame_size(struct v4l2_subdev *subdev,
491f7234138SHans Verkuil 				   struct v4l2_subdev_pad_config *cfg,
492cb7a01acSMauro Carvalho Chehab 				   struct v4l2_subdev_frame_size_enum *fse)
493cb7a01acSMauro Carvalho Chehab {
494cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
495cb7a01acSMauro Carvalho Chehab 
496cb7a01acSMauro Carvalho Chehab 	if (fse->index >= 8 || fse->code != mt9p031->format.code)
497cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
498cb7a01acSMauro Carvalho Chehab 
499cb7a01acSMauro Carvalho Chehab 	fse->min_width = MT9P031_WINDOW_WIDTH_DEF
500cb7a01acSMauro Carvalho Chehab 		       / min_t(unsigned int, 7, fse->index + 1);
501cb7a01acSMauro Carvalho Chehab 	fse->max_width = fse->min_width;
502cb7a01acSMauro Carvalho Chehab 	fse->min_height = MT9P031_WINDOW_HEIGHT_DEF / (fse->index + 1);
503cb7a01acSMauro Carvalho Chehab 	fse->max_height = fse->min_height;
504cb7a01acSMauro Carvalho Chehab 
505cb7a01acSMauro Carvalho Chehab 	return 0;
506cb7a01acSMauro Carvalho Chehab }
507cb7a01acSMauro Carvalho Chehab 
508cb7a01acSMauro Carvalho Chehab static struct v4l2_mbus_framefmt *
509f7234138SHans Verkuil __mt9p031_get_pad_format(struct mt9p031 *mt9p031, struct v4l2_subdev_pad_config *cfg,
510cb7a01acSMauro Carvalho Chehab 			 unsigned int pad, u32 which)
511cb7a01acSMauro Carvalho Chehab {
512cb7a01acSMauro Carvalho Chehab 	switch (which) {
513cb7a01acSMauro Carvalho Chehab 	case V4L2_SUBDEV_FORMAT_TRY:
514f7234138SHans Verkuil 		return v4l2_subdev_get_try_format(&mt9p031->subdev, cfg, 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 *
523f7234138SHans Verkuil __mt9p031_get_pad_crop(struct mt9p031 *mt9p031, struct v4l2_subdev_pad_config *cfg,
524cb7a01acSMauro Carvalho Chehab 		     unsigned int pad, u32 which)
525cb7a01acSMauro Carvalho Chehab {
526cb7a01acSMauro Carvalho Chehab 	switch (which) {
527cb7a01acSMauro Carvalho Chehab 	case V4L2_SUBDEV_FORMAT_TRY:
528f7234138SHans Verkuil 		return v4l2_subdev_get_try_crop(&mt9p031->subdev, cfg, pad);
529cb7a01acSMauro Carvalho Chehab 	case V4L2_SUBDEV_FORMAT_ACTIVE:
530cb7a01acSMauro Carvalho Chehab 		return &mt9p031->crop;
531cb7a01acSMauro Carvalho Chehab 	default:
532cb7a01acSMauro Carvalho Chehab 		return NULL;
533cb7a01acSMauro Carvalho Chehab 	}
534cb7a01acSMauro Carvalho Chehab }
535cb7a01acSMauro Carvalho Chehab 
536cb7a01acSMauro Carvalho Chehab static int mt9p031_get_format(struct v4l2_subdev *subdev,
537f7234138SHans Verkuil 			      struct v4l2_subdev_pad_config *cfg,
538cb7a01acSMauro Carvalho Chehab 			      struct v4l2_subdev_format *fmt)
539cb7a01acSMauro Carvalho Chehab {
540cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
541cb7a01acSMauro Carvalho Chehab 
542f7234138SHans Verkuil 	fmt->format = *__mt9p031_get_pad_format(mt9p031, cfg, fmt->pad,
543cb7a01acSMauro Carvalho Chehab 						fmt->which);
544cb7a01acSMauro Carvalho Chehab 	return 0;
545cb7a01acSMauro Carvalho Chehab }
546cb7a01acSMauro Carvalho Chehab 
547cb7a01acSMauro Carvalho Chehab static int mt9p031_set_format(struct v4l2_subdev *subdev,
548f7234138SHans Verkuil 			      struct v4l2_subdev_pad_config *cfg,
549cb7a01acSMauro Carvalho Chehab 			      struct v4l2_subdev_format *format)
550cb7a01acSMauro Carvalho Chehab {
551cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
552cb7a01acSMauro Carvalho Chehab 	struct v4l2_mbus_framefmt *__format;
553cb7a01acSMauro Carvalho Chehab 	struct v4l2_rect *__crop;
554cb7a01acSMauro Carvalho Chehab 	unsigned int width;
555cb7a01acSMauro Carvalho Chehab 	unsigned int height;
556cb7a01acSMauro Carvalho Chehab 	unsigned int hratio;
557cb7a01acSMauro Carvalho Chehab 	unsigned int vratio;
558cb7a01acSMauro Carvalho Chehab 
559f7234138SHans Verkuil 	__crop = __mt9p031_get_pad_crop(mt9p031, cfg, format->pad,
560cb7a01acSMauro Carvalho Chehab 					format->which);
561cb7a01acSMauro Carvalho Chehab 
562cb7a01acSMauro Carvalho Chehab 	/* Clamp the width and height to avoid dividing by zero. */
563cb7a01acSMauro Carvalho Chehab 	width = clamp_t(unsigned int, ALIGN(format->format.width, 2),
564f90580caSRicardo Ribalda 			max_t(unsigned int, __crop->width / 7,
565f90580caSRicardo Ribalda 			      MT9P031_WINDOW_WIDTH_MIN),
566cb7a01acSMauro Carvalho Chehab 			__crop->width);
567cb7a01acSMauro Carvalho Chehab 	height = clamp_t(unsigned int, ALIGN(format->format.height, 2),
568f90580caSRicardo Ribalda 			 max_t(unsigned int, __crop->height / 8,
569f90580caSRicardo Ribalda 			       MT9P031_WINDOW_HEIGHT_MIN),
570cb7a01acSMauro Carvalho Chehab 			 __crop->height);
571cb7a01acSMauro Carvalho Chehab 
572cb7a01acSMauro Carvalho Chehab 	hratio = DIV_ROUND_CLOSEST(__crop->width, width);
573cb7a01acSMauro Carvalho Chehab 	vratio = DIV_ROUND_CLOSEST(__crop->height, height);
574cb7a01acSMauro Carvalho Chehab 
575f7234138SHans Verkuil 	__format = __mt9p031_get_pad_format(mt9p031, cfg, format->pad,
576cb7a01acSMauro Carvalho Chehab 					    format->which);
577cb7a01acSMauro Carvalho Chehab 	__format->width = __crop->width / hratio;
578cb7a01acSMauro Carvalho Chehab 	__format->height = __crop->height / vratio;
579cb7a01acSMauro Carvalho Chehab 
580cb7a01acSMauro Carvalho Chehab 	format->format = *__format;
581cb7a01acSMauro Carvalho Chehab 
582cb7a01acSMauro Carvalho Chehab 	return 0;
583cb7a01acSMauro Carvalho Chehab }
584cb7a01acSMauro Carvalho Chehab 
5851a023febSHans Verkuil static int mt9p031_get_selection(struct v4l2_subdev *subdev,
586f7234138SHans Verkuil 				 struct v4l2_subdev_pad_config *cfg,
5871a023febSHans Verkuil 				 struct v4l2_subdev_selection *sel)
588cb7a01acSMauro Carvalho Chehab {
589cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
590cb7a01acSMauro Carvalho Chehab 
5911a023febSHans Verkuil 	if (sel->target != V4L2_SEL_TGT_CROP)
5921a023febSHans Verkuil 		return -EINVAL;
5931a023febSHans Verkuil 
594f7234138SHans Verkuil 	sel->r = *__mt9p031_get_pad_crop(mt9p031, cfg, sel->pad, sel->which);
595cb7a01acSMauro Carvalho Chehab 	return 0;
596cb7a01acSMauro Carvalho Chehab }
597cb7a01acSMauro Carvalho Chehab 
5981a023febSHans Verkuil static int mt9p031_set_selection(struct v4l2_subdev *subdev,
599f7234138SHans Verkuil 				 struct v4l2_subdev_pad_config *cfg,
6001a023febSHans Verkuil 				 struct v4l2_subdev_selection *sel)
601cb7a01acSMauro Carvalho Chehab {
602cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
603cb7a01acSMauro Carvalho Chehab 	struct v4l2_mbus_framefmt *__format;
604cb7a01acSMauro Carvalho Chehab 	struct v4l2_rect *__crop;
605cb7a01acSMauro Carvalho Chehab 	struct v4l2_rect rect;
606cb7a01acSMauro Carvalho Chehab 
6071a023febSHans Verkuil 	if (sel->target != V4L2_SEL_TGT_CROP)
6081a023febSHans Verkuil 		return -EINVAL;
6091a023febSHans Verkuil 
610cb7a01acSMauro Carvalho Chehab 	/* Clamp the crop rectangle boundaries and align them to a multiple of 2
611cb7a01acSMauro Carvalho Chehab 	 * pixels to ensure a GRBG Bayer pattern.
612cb7a01acSMauro Carvalho Chehab 	 */
6131a023febSHans Verkuil 	rect.left = clamp(ALIGN(sel->r.left, 2), MT9P031_COLUMN_START_MIN,
614cb7a01acSMauro Carvalho Chehab 			  MT9P031_COLUMN_START_MAX);
6151a023febSHans Verkuil 	rect.top = clamp(ALIGN(sel->r.top, 2), MT9P031_ROW_START_MIN,
616cb7a01acSMauro Carvalho Chehab 			 MT9P031_ROW_START_MAX);
6171a023febSHans Verkuil 	rect.width = clamp_t(unsigned int, ALIGN(sel->r.width, 2),
618cb7a01acSMauro Carvalho Chehab 			     MT9P031_WINDOW_WIDTH_MIN,
619cb7a01acSMauro Carvalho Chehab 			     MT9P031_WINDOW_WIDTH_MAX);
6201a023febSHans Verkuil 	rect.height = clamp_t(unsigned int, ALIGN(sel->r.height, 2),
621cb7a01acSMauro Carvalho Chehab 			      MT9P031_WINDOW_HEIGHT_MIN,
622cb7a01acSMauro Carvalho Chehab 			      MT9P031_WINDOW_HEIGHT_MAX);
623cb7a01acSMauro Carvalho Chehab 
624f90580caSRicardo Ribalda 	rect.width = min_t(unsigned int, rect.width,
625f90580caSRicardo Ribalda 			   MT9P031_PIXEL_ARRAY_WIDTH - rect.left);
626f90580caSRicardo Ribalda 	rect.height = min_t(unsigned int, rect.height,
627f90580caSRicardo Ribalda 			    MT9P031_PIXEL_ARRAY_HEIGHT - rect.top);
628cb7a01acSMauro Carvalho Chehab 
629f7234138SHans Verkuil 	__crop = __mt9p031_get_pad_crop(mt9p031, cfg, sel->pad, sel->which);
630cb7a01acSMauro Carvalho Chehab 
631cb7a01acSMauro Carvalho Chehab 	if (rect.width != __crop->width || rect.height != __crop->height) {
632cb7a01acSMauro Carvalho Chehab 		/* Reset the output image size if the crop rectangle size has
633cb7a01acSMauro Carvalho Chehab 		 * been modified.
634cb7a01acSMauro Carvalho Chehab 		 */
635f7234138SHans Verkuil 		__format = __mt9p031_get_pad_format(mt9p031, cfg, sel->pad,
6361a023febSHans Verkuil 						    sel->which);
637cb7a01acSMauro Carvalho Chehab 		__format->width = rect.width;
638cb7a01acSMauro Carvalho Chehab 		__format->height = rect.height;
639cb7a01acSMauro Carvalho Chehab 	}
640cb7a01acSMauro Carvalho Chehab 
641cb7a01acSMauro Carvalho Chehab 	*__crop = rect;
6421a023febSHans Verkuil 	sel->r = rect;
643cb7a01acSMauro Carvalho Chehab 
644cb7a01acSMauro Carvalho Chehab 	return 0;
645cb7a01acSMauro Carvalho Chehab }
646cb7a01acSMauro Carvalho Chehab 
647cb7a01acSMauro Carvalho Chehab /* -----------------------------------------------------------------------------
648cb7a01acSMauro Carvalho Chehab  * V4L2 subdev control operations
649cb7a01acSMauro Carvalho Chehab  */
650cb7a01acSMauro Carvalho Chehab 
651cb7a01acSMauro Carvalho Chehab #define V4L2_CID_BLC_AUTO		(V4L2_CID_USER_BASE | 0x1002)
652cb7a01acSMauro Carvalho Chehab #define V4L2_CID_BLC_TARGET_LEVEL	(V4L2_CID_USER_BASE | 0x1003)
653cb7a01acSMauro Carvalho Chehab #define V4L2_CID_BLC_ANALOG_OFFSET	(V4L2_CID_USER_BASE | 0x1004)
654cb7a01acSMauro Carvalho Chehab #define V4L2_CID_BLC_DIGITAL_OFFSET	(V4L2_CID_USER_BASE | 0x1005)
655cb7a01acSMauro Carvalho Chehab 
656535ec214SLaurent Pinchart static int mt9p031_restore_blc(struct mt9p031 *mt9p031)
657535ec214SLaurent Pinchart {
658535ec214SLaurent Pinchart 	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
659535ec214SLaurent Pinchart 	int ret;
660535ec214SLaurent Pinchart 
661535ec214SLaurent Pinchart 	if (mt9p031->blc_auto->cur.val != 0) {
662535ec214SLaurent Pinchart 		ret = mt9p031_set_mode2(mt9p031, 0,
663535ec214SLaurent Pinchart 					MT9P031_READ_MODE_2_ROW_BLC);
664535ec214SLaurent Pinchart 		if (ret < 0)
665535ec214SLaurent Pinchart 			return ret;
666535ec214SLaurent Pinchart 	}
667535ec214SLaurent Pinchart 
668535ec214SLaurent Pinchart 	if (mt9p031->blc_offset->cur.val != 0) {
669535ec214SLaurent Pinchart 		ret = mt9p031_write(client, MT9P031_ROW_BLACK_TARGET,
670535ec214SLaurent Pinchart 				    mt9p031->blc_offset->cur.val);
671535ec214SLaurent Pinchart 		if (ret < 0)
672535ec214SLaurent Pinchart 			return ret;
673535ec214SLaurent Pinchart 	}
674535ec214SLaurent Pinchart 
675535ec214SLaurent Pinchart 	return 0;
676535ec214SLaurent Pinchart }
677535ec214SLaurent Pinchart 
678cb7a01acSMauro Carvalho Chehab static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl)
679cb7a01acSMauro Carvalho Chehab {
680cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 =
681cb7a01acSMauro Carvalho Chehab 			container_of(ctrl->handler, struct mt9p031, ctrls);
682cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
683cb7a01acSMauro Carvalho Chehab 	u16 data;
684cb7a01acSMauro Carvalho Chehab 	int ret;
685cb7a01acSMauro Carvalho Chehab 
6868bf54c43SLaurent Pinchart 	if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE)
6878bf54c43SLaurent Pinchart 		return 0;
6888bf54c43SLaurent Pinchart 
689cb7a01acSMauro Carvalho Chehab 	switch (ctrl->id) {
690cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_EXPOSURE:
691cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_write(client, MT9P031_SHUTTER_WIDTH_UPPER,
692cb7a01acSMauro Carvalho Chehab 				    (ctrl->val >> 16) & 0xffff);
693cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
694cb7a01acSMauro Carvalho Chehab 			return ret;
695cb7a01acSMauro Carvalho Chehab 
696cb7a01acSMauro Carvalho Chehab 		return mt9p031_write(client, MT9P031_SHUTTER_WIDTH_LOWER,
697cb7a01acSMauro Carvalho Chehab 				     ctrl->val & 0xffff);
698cb7a01acSMauro Carvalho Chehab 
699cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_GAIN:
700cb7a01acSMauro Carvalho Chehab 		/* Gain is controlled by 2 analog stages and a digital stage.
701cb7a01acSMauro Carvalho Chehab 		 * Valid values for the 3 stages are
702cb7a01acSMauro Carvalho Chehab 		 *
703cb7a01acSMauro Carvalho Chehab 		 * Stage                Min     Max     Step
704cb7a01acSMauro Carvalho Chehab 		 * ------------------------------------------
705cb7a01acSMauro Carvalho Chehab 		 * First analog stage   x1      x2      1
706cb7a01acSMauro Carvalho Chehab 		 * Second analog stage  x1      x4      0.125
707cb7a01acSMauro Carvalho Chehab 		 * Digital stage        x1      x16     0.125
708cb7a01acSMauro Carvalho Chehab 		 *
709cb7a01acSMauro Carvalho Chehab 		 * To minimize noise, the gain stages should be used in the
710cb7a01acSMauro Carvalho Chehab 		 * second analog stage, first analog stage, digital stage order.
711cb7a01acSMauro Carvalho Chehab 		 * Gain from a previous stage should be pushed to its maximum
712cb7a01acSMauro Carvalho Chehab 		 * value before the next stage is used.
713cb7a01acSMauro Carvalho Chehab 		 */
714cb7a01acSMauro Carvalho Chehab 		if (ctrl->val <= 32) {
715cb7a01acSMauro Carvalho Chehab 			data = ctrl->val;
716cb7a01acSMauro Carvalho Chehab 		} else if (ctrl->val <= 64) {
717cb7a01acSMauro Carvalho Chehab 			ctrl->val &= ~1;
718cb7a01acSMauro Carvalho Chehab 			data = (1 << 6) | (ctrl->val >> 1);
719cb7a01acSMauro Carvalho Chehab 		} else {
720cb7a01acSMauro Carvalho Chehab 			ctrl->val &= ~7;
721cb7a01acSMauro Carvalho Chehab 			data = ((ctrl->val - 64) << 5) | (1 << 6) | 32;
722cb7a01acSMauro Carvalho Chehab 		}
723cb7a01acSMauro Carvalho Chehab 
724cb7a01acSMauro Carvalho Chehab 		return mt9p031_write(client, MT9P031_GLOBAL_GAIN, data);
725cb7a01acSMauro Carvalho Chehab 
726cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_HFLIP:
727cb7a01acSMauro Carvalho Chehab 		if (ctrl->val)
728cb7a01acSMauro Carvalho Chehab 			return mt9p031_set_mode2(mt9p031,
729cb7a01acSMauro Carvalho Chehab 					0, MT9P031_READ_MODE_2_COL_MIR);
730cb7a01acSMauro Carvalho Chehab 		else
731cb7a01acSMauro Carvalho Chehab 			return mt9p031_set_mode2(mt9p031,
732cb7a01acSMauro Carvalho Chehab 					MT9P031_READ_MODE_2_COL_MIR, 0);
733cb7a01acSMauro Carvalho Chehab 
734cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_VFLIP:
735cb7a01acSMauro Carvalho Chehab 		if (ctrl->val)
736cb7a01acSMauro Carvalho Chehab 			return mt9p031_set_mode2(mt9p031,
737cb7a01acSMauro Carvalho Chehab 					0, MT9P031_READ_MODE_2_ROW_MIR);
738cb7a01acSMauro Carvalho Chehab 		else
739cb7a01acSMauro Carvalho Chehab 			return mt9p031_set_mode2(mt9p031,
740cb7a01acSMauro Carvalho Chehab 					MT9P031_READ_MODE_2_ROW_MIR, 0);
741cb7a01acSMauro Carvalho Chehab 
742cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_TEST_PATTERN:
7438bf54c43SLaurent Pinchart 		/* The digital side of the Black Level Calibration function must
7448bf54c43SLaurent Pinchart 		 * be disabled when generating a test pattern to avoid artifacts
7458bf54c43SLaurent Pinchart 		 * in the image. Activate (deactivate) the BLC-related controls
7468bf54c43SLaurent Pinchart 		 * when the test pattern is enabled (disabled).
7478bf54c43SLaurent Pinchart 		 */
7488bf54c43SLaurent Pinchart 		v4l2_ctrl_activate(mt9p031->blc_auto, ctrl->val == 0);
7498bf54c43SLaurent Pinchart 		v4l2_ctrl_activate(mt9p031->blc_offset, ctrl->val == 0);
7508bf54c43SLaurent Pinchart 
751cb7a01acSMauro Carvalho Chehab 		if (!ctrl->val) {
7528bf54c43SLaurent Pinchart 			/* Restore the BLC settings. */
753535ec214SLaurent Pinchart 			ret = mt9p031_restore_blc(mt9p031);
754cb7a01acSMauro Carvalho Chehab 			if (ret < 0)
755cb7a01acSMauro Carvalho Chehab 				return ret;
756535ec214SLaurent Pinchart 
757cb7a01acSMauro Carvalho Chehab 			return mt9p031_write(client, MT9P031_TEST_PATTERN,
758cb7a01acSMauro Carvalho Chehab 					     MT9P031_TEST_PATTERN_DISABLE);
759cb7a01acSMauro Carvalho Chehab 		}
760cb7a01acSMauro Carvalho Chehab 
761cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_write(client, MT9P031_TEST_PATTERN_GREEN, 0x05a0);
762cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
763cb7a01acSMauro Carvalho Chehab 			return ret;
764cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_write(client, MT9P031_TEST_PATTERN_RED, 0x0a50);
765cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
766cb7a01acSMauro Carvalho Chehab 			return ret;
767cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_write(client, MT9P031_TEST_PATTERN_BLUE, 0x0aa0);
768cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
769cb7a01acSMauro Carvalho Chehab 			return ret;
770cb7a01acSMauro Carvalho Chehab 
7718bf54c43SLaurent Pinchart 		/* Disable digital BLC when generating a test pattern. */
772cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_set_mode2(mt9p031, MT9P031_READ_MODE_2_ROW_BLC,
773cb7a01acSMauro Carvalho Chehab 					0);
774cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
775cb7a01acSMauro Carvalho Chehab 			return ret;
776cb7a01acSMauro Carvalho Chehab 
777cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET, 0);
778cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
779cb7a01acSMauro Carvalho Chehab 			return ret;
780cb7a01acSMauro Carvalho Chehab 
781cb7a01acSMauro Carvalho Chehab 		return mt9p031_write(client, MT9P031_TEST_PATTERN,
782cb7a01acSMauro Carvalho Chehab 				((ctrl->val - 1) << MT9P031_TEST_PATTERN_SHIFT)
783cb7a01acSMauro Carvalho Chehab 				| MT9P031_TEST_PATTERN_ENABLE);
784cb7a01acSMauro Carvalho Chehab 
785cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_BLC_AUTO:
786cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_set_mode2(mt9p031,
787cb7a01acSMauro Carvalho Chehab 				ctrl->val ? 0 : MT9P031_READ_MODE_2_ROW_BLC,
788cb7a01acSMauro Carvalho Chehab 				ctrl->val ? MT9P031_READ_MODE_2_ROW_BLC : 0);
789cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
790cb7a01acSMauro Carvalho Chehab 			return ret;
791cb7a01acSMauro Carvalho Chehab 
792cb7a01acSMauro Carvalho Chehab 		return mt9p031_write(client, MT9P031_BLACK_LEVEL_CALIBRATION,
793cb7a01acSMauro Carvalho Chehab 				     ctrl->val ? 0 : MT9P031_BLC_MANUAL_BLC);
794cb7a01acSMauro Carvalho Chehab 
795cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_BLC_TARGET_LEVEL:
796cb7a01acSMauro Carvalho Chehab 		return mt9p031_write(client, MT9P031_ROW_BLACK_TARGET,
797cb7a01acSMauro Carvalho Chehab 				     ctrl->val);
798cb7a01acSMauro Carvalho Chehab 
799cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_BLC_ANALOG_OFFSET:
800cb7a01acSMauro Carvalho Chehab 		data = ctrl->val & ((1 << 9) - 1);
801cb7a01acSMauro Carvalho Chehab 
802cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_write(client, MT9P031_GREEN1_OFFSET, data);
803cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
804cb7a01acSMauro Carvalho Chehab 			return ret;
805cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_write(client, MT9P031_GREEN2_OFFSET, data);
806cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
807cb7a01acSMauro Carvalho Chehab 			return ret;
808cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_write(client, MT9P031_RED_OFFSET, data);
809cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
810cb7a01acSMauro Carvalho Chehab 			return ret;
811cb7a01acSMauro Carvalho Chehab 		return mt9p031_write(client, MT9P031_BLUE_OFFSET, data);
812cb7a01acSMauro Carvalho Chehab 
813cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_BLC_DIGITAL_OFFSET:
814cb7a01acSMauro Carvalho Chehab 		return mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET,
815cb7a01acSMauro Carvalho Chehab 				     ctrl->val & ((1 << 12) - 1));
816cb7a01acSMauro Carvalho Chehab 	}
817cb7a01acSMauro Carvalho Chehab 
818cb7a01acSMauro Carvalho Chehab 	return 0;
819cb7a01acSMauro Carvalho Chehab }
820cb7a01acSMauro Carvalho Chehab 
821cb7a01acSMauro Carvalho Chehab static struct v4l2_ctrl_ops mt9p031_ctrl_ops = {
822cb7a01acSMauro Carvalho Chehab 	.s_ctrl = mt9p031_s_ctrl,
823cb7a01acSMauro Carvalho Chehab };
824cb7a01acSMauro Carvalho Chehab 
825cb7a01acSMauro Carvalho Chehab static const char * const mt9p031_test_pattern_menu[] = {
826cb7a01acSMauro Carvalho Chehab 	"Disabled",
827cb7a01acSMauro Carvalho Chehab 	"Color Field",
828cb7a01acSMauro Carvalho Chehab 	"Horizontal Gradient",
829cb7a01acSMauro Carvalho Chehab 	"Vertical Gradient",
830cb7a01acSMauro Carvalho Chehab 	"Diagonal Gradient",
831cb7a01acSMauro Carvalho Chehab 	"Classic Test Pattern",
832cb7a01acSMauro Carvalho Chehab 	"Walking 1s",
833cb7a01acSMauro Carvalho Chehab 	"Monochrome Horizontal Bars",
834cb7a01acSMauro Carvalho Chehab 	"Monochrome Vertical Bars",
835cb7a01acSMauro Carvalho Chehab 	"Vertical Color Bars",
836cb7a01acSMauro Carvalho Chehab };
837cb7a01acSMauro Carvalho Chehab 
838cb7a01acSMauro Carvalho Chehab static const struct v4l2_ctrl_config mt9p031_ctrls[] = {
839cb7a01acSMauro Carvalho Chehab 	{
840cb7a01acSMauro Carvalho Chehab 		.ops		= &mt9p031_ctrl_ops,
841cb7a01acSMauro Carvalho Chehab 		.id		= V4L2_CID_BLC_AUTO,
842cb7a01acSMauro Carvalho Chehab 		.type		= V4L2_CTRL_TYPE_BOOLEAN,
843cb7a01acSMauro Carvalho Chehab 		.name		= "BLC, Auto",
844cb7a01acSMauro Carvalho Chehab 		.min		= 0,
845cb7a01acSMauro Carvalho Chehab 		.max		= 1,
846cb7a01acSMauro Carvalho Chehab 		.step		= 1,
847cb7a01acSMauro Carvalho Chehab 		.def		= 1,
848cb7a01acSMauro Carvalho Chehab 		.flags		= 0,
849cb7a01acSMauro Carvalho Chehab 	}, {
850cb7a01acSMauro Carvalho Chehab 		.ops		= &mt9p031_ctrl_ops,
851cb7a01acSMauro Carvalho Chehab 		.id		= V4L2_CID_BLC_TARGET_LEVEL,
852cb7a01acSMauro Carvalho Chehab 		.type		= V4L2_CTRL_TYPE_INTEGER,
853cb7a01acSMauro Carvalho Chehab 		.name		= "BLC Target Level",
854cb7a01acSMauro Carvalho Chehab 		.min		= 0,
855cb7a01acSMauro Carvalho Chehab 		.max		= 4095,
856cb7a01acSMauro Carvalho Chehab 		.step		= 1,
857cb7a01acSMauro Carvalho Chehab 		.def		= 168,
858cb7a01acSMauro Carvalho Chehab 		.flags		= 0,
859cb7a01acSMauro Carvalho Chehab 	}, {
860cb7a01acSMauro Carvalho Chehab 		.ops		= &mt9p031_ctrl_ops,
861cb7a01acSMauro Carvalho Chehab 		.id		= V4L2_CID_BLC_ANALOG_OFFSET,
862cb7a01acSMauro Carvalho Chehab 		.type		= V4L2_CTRL_TYPE_INTEGER,
863cb7a01acSMauro Carvalho Chehab 		.name		= "BLC Analog Offset",
864cb7a01acSMauro Carvalho Chehab 		.min		= -255,
865cb7a01acSMauro Carvalho Chehab 		.max		= 255,
866cb7a01acSMauro Carvalho Chehab 		.step		= 1,
867cb7a01acSMauro Carvalho Chehab 		.def		= 32,
868cb7a01acSMauro Carvalho Chehab 		.flags		= 0,
869cb7a01acSMauro Carvalho Chehab 	}, {
870cb7a01acSMauro Carvalho Chehab 		.ops		= &mt9p031_ctrl_ops,
871cb7a01acSMauro Carvalho Chehab 		.id		= V4L2_CID_BLC_DIGITAL_OFFSET,
872cb7a01acSMauro Carvalho Chehab 		.type		= V4L2_CTRL_TYPE_INTEGER,
873cb7a01acSMauro Carvalho Chehab 		.name		= "BLC Digital Offset",
874cb7a01acSMauro Carvalho Chehab 		.min		= -2048,
875cb7a01acSMauro Carvalho Chehab 		.max		= 2047,
876cb7a01acSMauro Carvalho Chehab 		.step		= 1,
877cb7a01acSMauro Carvalho Chehab 		.def		= 40,
878cb7a01acSMauro Carvalho Chehab 		.flags		= 0,
879cb7a01acSMauro Carvalho Chehab 	}
880cb7a01acSMauro Carvalho Chehab };
881cb7a01acSMauro Carvalho Chehab 
882cb7a01acSMauro Carvalho Chehab /* -----------------------------------------------------------------------------
883cb7a01acSMauro Carvalho Chehab  * V4L2 subdev core operations
884cb7a01acSMauro Carvalho Chehab  */
885cb7a01acSMauro Carvalho Chehab 
886cb7a01acSMauro Carvalho Chehab static int mt9p031_set_power(struct v4l2_subdev *subdev, int on)
887cb7a01acSMauro Carvalho Chehab {
888cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
889cb7a01acSMauro Carvalho Chehab 	int ret = 0;
890cb7a01acSMauro Carvalho Chehab 
891cb7a01acSMauro Carvalho Chehab 	mutex_lock(&mt9p031->power_lock);
892cb7a01acSMauro Carvalho Chehab 
893cb7a01acSMauro Carvalho Chehab 	/* If the power count is modified from 0 to != 0 or from != 0 to 0,
894cb7a01acSMauro Carvalho Chehab 	 * update the power state.
895cb7a01acSMauro Carvalho Chehab 	 */
896cb7a01acSMauro Carvalho Chehab 	if (mt9p031->power_count == !on) {
897cb7a01acSMauro Carvalho Chehab 		ret = __mt9p031_set_power(mt9p031, !!on);
898cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
899cb7a01acSMauro Carvalho Chehab 			goto out;
900cb7a01acSMauro Carvalho Chehab 	}
901cb7a01acSMauro Carvalho Chehab 
902cb7a01acSMauro Carvalho Chehab 	/* Update the power count. */
903cb7a01acSMauro Carvalho Chehab 	mt9p031->power_count += on ? 1 : -1;
904cb7a01acSMauro Carvalho Chehab 	WARN_ON(mt9p031->power_count < 0);
905cb7a01acSMauro Carvalho Chehab 
906cb7a01acSMauro Carvalho Chehab out:
907cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&mt9p031->power_lock);
908cb7a01acSMauro Carvalho Chehab 	return ret;
909cb7a01acSMauro Carvalho Chehab }
910cb7a01acSMauro Carvalho Chehab 
911cb7a01acSMauro Carvalho Chehab /* -----------------------------------------------------------------------------
912cb7a01acSMauro Carvalho Chehab  * V4L2 subdev internal operations
913cb7a01acSMauro Carvalho Chehab  */
914cb7a01acSMauro Carvalho Chehab 
915cb7a01acSMauro Carvalho Chehab static int mt9p031_registered(struct v4l2_subdev *subdev)
916cb7a01acSMauro Carvalho Chehab {
917cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(subdev);
918cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
919cb7a01acSMauro Carvalho Chehab 	s32 data;
920cb7a01acSMauro Carvalho Chehab 	int ret;
921cb7a01acSMauro Carvalho Chehab 
922cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_power_on(mt9p031);
923cb7a01acSMauro Carvalho Chehab 	if (ret < 0) {
924cb7a01acSMauro Carvalho Chehab 		dev_err(&client->dev, "MT9P031 power up failed\n");
925cb7a01acSMauro Carvalho Chehab 		return ret;
926cb7a01acSMauro Carvalho Chehab 	}
927cb7a01acSMauro Carvalho Chehab 
928cb7a01acSMauro Carvalho Chehab 	/* Read out the chip version register */
929cb7a01acSMauro Carvalho Chehab 	data = mt9p031_read(client, MT9P031_CHIP_VERSION);
930bbcc9fa0SGuennadi Liakhovetski 	mt9p031_power_off(mt9p031);
931bbcc9fa0SGuennadi Liakhovetski 
932cb7a01acSMauro Carvalho Chehab 	if (data != MT9P031_CHIP_VERSION_VALUE) {
933cb7a01acSMauro Carvalho Chehab 		dev_err(&client->dev, "MT9P031 not detected, wrong version "
934cb7a01acSMauro Carvalho Chehab 			"0x%04x\n", data);
935cb7a01acSMauro Carvalho Chehab 		return -ENODEV;
936cb7a01acSMauro Carvalho Chehab 	}
937cb7a01acSMauro Carvalho Chehab 
938cb7a01acSMauro Carvalho Chehab 	dev_info(&client->dev, "MT9P031 detected at address 0x%02x\n",
939cb7a01acSMauro Carvalho Chehab 		 client->addr);
940cb7a01acSMauro Carvalho Chehab 
941bbcc9fa0SGuennadi Liakhovetski 	return 0;
942cb7a01acSMauro Carvalho Chehab }
943cb7a01acSMauro Carvalho Chehab 
944cb7a01acSMauro Carvalho Chehab static int mt9p031_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
945cb7a01acSMauro Carvalho Chehab {
946cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
947cb7a01acSMauro Carvalho Chehab 	struct v4l2_mbus_framefmt *format;
948cb7a01acSMauro Carvalho Chehab 	struct v4l2_rect *crop;
949cb7a01acSMauro Carvalho Chehab 
950f7234138SHans Verkuil 	crop = v4l2_subdev_get_try_crop(subdev, fh->pad, 0);
951cb7a01acSMauro Carvalho Chehab 	crop->left = MT9P031_COLUMN_START_DEF;
952cb7a01acSMauro Carvalho Chehab 	crop->top = MT9P031_ROW_START_DEF;
953cb7a01acSMauro Carvalho Chehab 	crop->width = MT9P031_WINDOW_WIDTH_DEF;
954cb7a01acSMauro Carvalho Chehab 	crop->height = MT9P031_WINDOW_HEIGHT_DEF;
955cb7a01acSMauro Carvalho Chehab 
956f7234138SHans Verkuil 	format = v4l2_subdev_get_try_format(subdev, fh->pad, 0);
957cb7a01acSMauro Carvalho Chehab 
958cb7a01acSMauro Carvalho Chehab 	if (mt9p031->model == MT9P031_MODEL_MONOCHROME)
959f5fe58fdSBoris BREZILLON 		format->code = MEDIA_BUS_FMT_Y12_1X12;
960cb7a01acSMauro Carvalho Chehab 	else
961f5fe58fdSBoris BREZILLON 		format->code = MEDIA_BUS_FMT_SGRBG12_1X12;
962cb7a01acSMauro Carvalho Chehab 
963cb7a01acSMauro Carvalho Chehab 	format->width = MT9P031_WINDOW_WIDTH_DEF;
964cb7a01acSMauro Carvalho Chehab 	format->height = MT9P031_WINDOW_HEIGHT_DEF;
965cb7a01acSMauro Carvalho Chehab 	format->field = V4L2_FIELD_NONE;
966cb7a01acSMauro Carvalho Chehab 	format->colorspace = V4L2_COLORSPACE_SRGB;
967cb7a01acSMauro Carvalho Chehab 
968cb7a01acSMauro Carvalho Chehab 	return mt9p031_set_power(subdev, 1);
969cb7a01acSMauro Carvalho Chehab }
970cb7a01acSMauro Carvalho Chehab 
971cb7a01acSMauro Carvalho Chehab static int mt9p031_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
972cb7a01acSMauro Carvalho Chehab {
973cb7a01acSMauro Carvalho Chehab 	return mt9p031_set_power(subdev, 0);
974cb7a01acSMauro Carvalho Chehab }
975cb7a01acSMauro Carvalho Chehab 
976cb7a01acSMauro Carvalho Chehab static struct v4l2_subdev_core_ops mt9p031_subdev_core_ops = {
977cb7a01acSMauro Carvalho Chehab 	.s_power        = mt9p031_set_power,
978cb7a01acSMauro Carvalho Chehab };
979cb7a01acSMauro Carvalho Chehab 
980cb7a01acSMauro Carvalho Chehab static struct v4l2_subdev_video_ops mt9p031_subdev_video_ops = {
981cb7a01acSMauro Carvalho Chehab 	.s_stream       = mt9p031_s_stream,
982cb7a01acSMauro Carvalho Chehab };
983cb7a01acSMauro Carvalho Chehab 
984cb7a01acSMauro Carvalho Chehab static struct v4l2_subdev_pad_ops mt9p031_subdev_pad_ops = {
985cb7a01acSMauro Carvalho Chehab 	.enum_mbus_code = mt9p031_enum_mbus_code,
986cb7a01acSMauro Carvalho Chehab 	.enum_frame_size = mt9p031_enum_frame_size,
987cb7a01acSMauro Carvalho Chehab 	.get_fmt = mt9p031_get_format,
988cb7a01acSMauro Carvalho Chehab 	.set_fmt = mt9p031_set_format,
9891a023febSHans Verkuil 	.get_selection = mt9p031_get_selection,
9901a023febSHans Verkuil 	.set_selection = mt9p031_set_selection,
991cb7a01acSMauro Carvalho Chehab };
992cb7a01acSMauro Carvalho Chehab 
993cb7a01acSMauro Carvalho Chehab static struct v4l2_subdev_ops mt9p031_subdev_ops = {
994cb7a01acSMauro Carvalho Chehab 	.core   = &mt9p031_subdev_core_ops,
995cb7a01acSMauro Carvalho Chehab 	.video  = &mt9p031_subdev_video_ops,
996cb7a01acSMauro Carvalho Chehab 	.pad    = &mt9p031_subdev_pad_ops,
997cb7a01acSMauro Carvalho Chehab };
998cb7a01acSMauro Carvalho Chehab 
999cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_internal_ops mt9p031_subdev_internal_ops = {
1000cb7a01acSMauro Carvalho Chehab 	.registered = mt9p031_registered,
1001cb7a01acSMauro Carvalho Chehab 	.open = mt9p031_open,
1002cb7a01acSMauro Carvalho Chehab 	.close = mt9p031_close,
1003cb7a01acSMauro Carvalho Chehab };
1004cb7a01acSMauro Carvalho Chehab 
1005cb7a01acSMauro Carvalho Chehab /* -----------------------------------------------------------------------------
1006cb7a01acSMauro Carvalho Chehab  * Driver initialization and probing
1007cb7a01acSMauro Carvalho Chehab  */
1008cb7a01acSMauro Carvalho Chehab 
10098d4da37cSLad, Prabhakar static struct mt9p031_platform_data *
10108d4da37cSLad, Prabhakar mt9p031_get_pdata(struct i2c_client *client)
10118d4da37cSLad, Prabhakar {
10128d4da37cSLad, Prabhakar 	struct mt9p031_platform_data *pdata;
10138d4da37cSLad, Prabhakar 	struct device_node *np;
10148d4da37cSLad, Prabhakar 
10158d4da37cSLad, Prabhakar 	if (!IS_ENABLED(CONFIG_OF) || !client->dev.of_node)
10168d4da37cSLad, Prabhakar 		return client->dev.platform_data;
10178d4da37cSLad, Prabhakar 
1018fd9fdb78SPhilipp Zabel 	np = of_graph_get_next_endpoint(client->dev.of_node, NULL);
10198d4da37cSLad, Prabhakar 	if (!np)
10208d4da37cSLad, Prabhakar 		return NULL;
10218d4da37cSLad, Prabhakar 
10228d4da37cSLad, Prabhakar 	pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
10238d4da37cSLad, Prabhakar 	if (!pdata)
10248d4da37cSLad, Prabhakar 		goto done;
10258d4da37cSLad, Prabhakar 
10268d4da37cSLad, Prabhakar 	pdata->reset = of_get_named_gpio(client->dev.of_node, "reset-gpios", 0);
10278d4da37cSLad, Prabhakar 	of_property_read_u32(np, "input-clock-frequency", &pdata->ext_freq);
10288d4da37cSLad, Prabhakar 	of_property_read_u32(np, "pixel-clock-frequency", &pdata->target_freq);
10298d4da37cSLad, Prabhakar 
10308d4da37cSLad, Prabhakar done:
10318d4da37cSLad, Prabhakar 	of_node_put(np);
10328d4da37cSLad, Prabhakar 	return pdata;
10338d4da37cSLad, Prabhakar }
10348d4da37cSLad, Prabhakar 
1035cb7a01acSMauro Carvalho Chehab static int mt9p031_probe(struct i2c_client *client,
1036cb7a01acSMauro Carvalho Chehab 			 const struct i2c_device_id *did)
1037cb7a01acSMauro Carvalho Chehab {
10388d4da37cSLad, Prabhakar 	struct mt9p031_platform_data *pdata = mt9p031_get_pdata(client);
1039cb7a01acSMauro Carvalho Chehab 	struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
1040cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031;
1041cb7a01acSMauro Carvalho Chehab 	unsigned int i;
1042cb7a01acSMauro Carvalho Chehab 	int ret;
1043cb7a01acSMauro Carvalho Chehab 
1044cb7a01acSMauro Carvalho Chehab 	if (pdata == NULL) {
1045cb7a01acSMauro Carvalho Chehab 		dev_err(&client->dev, "No platform data\n");
1046cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
1047cb7a01acSMauro Carvalho Chehab 	}
1048cb7a01acSMauro Carvalho Chehab 
1049cb7a01acSMauro Carvalho Chehab 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
1050cb7a01acSMauro Carvalho Chehab 		dev_warn(&client->dev,
1051cb7a01acSMauro Carvalho Chehab 			"I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
1052cb7a01acSMauro Carvalho Chehab 		return -EIO;
1053cb7a01acSMauro Carvalho Chehab 	}
1054cb7a01acSMauro Carvalho Chehab 
105537b9f211SLaurent Pinchart 	mt9p031 = devm_kzalloc(&client->dev, sizeof(*mt9p031), GFP_KERNEL);
1056cb7a01acSMauro Carvalho Chehab 	if (mt9p031 == NULL)
1057cb7a01acSMauro Carvalho Chehab 		return -ENOMEM;
1058cb7a01acSMauro Carvalho Chehab 
1059cb7a01acSMauro Carvalho Chehab 	mt9p031->pdata = pdata;
1060cb7a01acSMauro Carvalho Chehab 	mt9p031->output_control	= MT9P031_OUTPUT_CONTROL_DEF;
1061cb7a01acSMauro Carvalho Chehab 	mt9p031->mode2 = MT9P031_READ_MODE_2_ROW_BLC;
1062cb7a01acSMauro Carvalho Chehab 	mt9p031->model = did->driver_data;
1063cb7a01acSMauro Carvalho Chehab 	mt9p031->reset = -1;
1064cb7a01acSMauro Carvalho Chehab 
10657997196cSLaurent Pinchart 	mt9p031->regulators[0].supply = "vdd";
10667997196cSLaurent Pinchart 	mt9p031->regulators[1].supply = "vdd_io";
10677997196cSLaurent Pinchart 	mt9p031->regulators[2].supply = "vaa";
106897f21276SLaurent Pinchart 
10697997196cSLaurent Pinchart 	ret = devm_regulator_bulk_get(&client->dev, 3, mt9p031->regulators);
10707997196cSLaurent Pinchart 	if (ret < 0) {
107197f21276SLaurent Pinchart 		dev_err(&client->dev, "Unable to get regulators\n");
10727997196cSLaurent Pinchart 		return ret;
107397f21276SLaurent Pinchart 	}
107497f21276SLaurent Pinchart 
107515af4a53SLad, Prabhakar 	mutex_init(&mt9p031->power_lock);
107615af4a53SLad, Prabhakar 
1077b28d7017SLad, Prabhakar 	v4l2_ctrl_handler_init(&mt9p031->ctrls, ARRAY_SIZE(mt9p031_ctrls) + 6);
1078cb7a01acSMauro Carvalho Chehab 
1079cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
1080cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_EXPOSURE, MT9P031_SHUTTER_WIDTH_MIN,
1081cb7a01acSMauro Carvalho Chehab 			  MT9P031_SHUTTER_WIDTH_MAX, 1,
1082cb7a01acSMauro Carvalho Chehab 			  MT9P031_SHUTTER_WIDTH_DEF);
1083cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
1084cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_GAIN, MT9P031_GLOBAL_GAIN_MIN,
1085cb7a01acSMauro Carvalho Chehab 			  MT9P031_GLOBAL_GAIN_MAX, 1, MT9P031_GLOBAL_GAIN_DEF);
1086cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
1087cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_HFLIP, 0, 1, 1, 0);
1088cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
1089cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_VFLIP, 0, 1, 1, 0);
1090cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
1091cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_PIXEL_RATE, pdata->target_freq,
1092cb7a01acSMauro Carvalho Chehab 			  pdata->target_freq, 1, pdata->target_freq);
1093b28d7017SLad, Prabhakar 	v4l2_ctrl_new_std_menu_items(&mt9p031->ctrls, &mt9p031_ctrl_ops,
1094b28d7017SLad, Prabhakar 			  V4L2_CID_TEST_PATTERN,
1095b28d7017SLad, Prabhakar 			  ARRAY_SIZE(mt9p031_test_pattern_menu) - 1, 0,
1096b28d7017SLad, Prabhakar 			  0, mt9p031_test_pattern_menu);
1097cb7a01acSMauro Carvalho Chehab 
1098cb7a01acSMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(mt9p031_ctrls); ++i)
1099cb7a01acSMauro Carvalho Chehab 		v4l2_ctrl_new_custom(&mt9p031->ctrls, &mt9p031_ctrls[i], NULL);
1100cb7a01acSMauro Carvalho Chehab 
1101cb7a01acSMauro Carvalho Chehab 	mt9p031->subdev.ctrl_handler = &mt9p031->ctrls;
1102cb7a01acSMauro Carvalho Chehab 
1103cb7a01acSMauro Carvalho Chehab 	if (mt9p031->ctrls.error) {
1104cb7a01acSMauro Carvalho Chehab 		printk(KERN_INFO "%s: control initialization error %d\n",
1105cb7a01acSMauro Carvalho Chehab 		       __func__, mt9p031->ctrls.error);
1106cb7a01acSMauro Carvalho Chehab 		ret = mt9p031->ctrls.error;
1107cb7a01acSMauro Carvalho Chehab 		goto done;
1108cb7a01acSMauro Carvalho Chehab 	}
1109cb7a01acSMauro Carvalho Chehab 
1110cb7a01acSMauro Carvalho Chehab 	mt9p031->blc_auto = v4l2_ctrl_find(&mt9p031->ctrls, V4L2_CID_BLC_AUTO);
1111cb7a01acSMauro Carvalho Chehab 	mt9p031->blc_offset = v4l2_ctrl_find(&mt9p031->ctrls,
1112cb7a01acSMauro Carvalho Chehab 					     V4L2_CID_BLC_DIGITAL_OFFSET);
1113cb7a01acSMauro Carvalho Chehab 
1114cb7a01acSMauro Carvalho Chehab 	v4l2_i2c_subdev_init(&mt9p031->subdev, client, &mt9p031_subdev_ops);
1115cb7a01acSMauro Carvalho Chehab 	mt9p031->subdev.internal_ops = &mt9p031_subdev_internal_ops;
1116cb7a01acSMauro Carvalho Chehab 
1117cb7a01acSMauro Carvalho Chehab 	mt9p031->pad.flags = MEDIA_PAD_FL_SOURCE;
1118cb7a01acSMauro Carvalho Chehab 	ret = media_entity_init(&mt9p031->subdev.entity, 1, &mt9p031->pad, 0);
1119cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
1120cb7a01acSMauro Carvalho Chehab 		goto done;
1121cb7a01acSMauro Carvalho Chehab 
1122cb7a01acSMauro Carvalho Chehab 	mt9p031->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
1123cb7a01acSMauro Carvalho Chehab 
1124cb7a01acSMauro Carvalho Chehab 	mt9p031->crop.width = MT9P031_WINDOW_WIDTH_DEF;
1125cb7a01acSMauro Carvalho Chehab 	mt9p031->crop.height = MT9P031_WINDOW_HEIGHT_DEF;
1126cb7a01acSMauro Carvalho Chehab 	mt9p031->crop.left = MT9P031_COLUMN_START_DEF;
1127cb7a01acSMauro Carvalho Chehab 	mt9p031->crop.top = MT9P031_ROW_START_DEF;
1128cb7a01acSMauro Carvalho Chehab 
1129cb7a01acSMauro Carvalho Chehab 	if (mt9p031->model == MT9P031_MODEL_MONOCHROME)
1130f5fe58fdSBoris BREZILLON 		mt9p031->format.code = MEDIA_BUS_FMT_Y12_1X12;
1131cb7a01acSMauro Carvalho Chehab 	else
1132f5fe58fdSBoris BREZILLON 		mt9p031->format.code = MEDIA_BUS_FMT_SGRBG12_1X12;
1133cb7a01acSMauro Carvalho Chehab 
1134cb7a01acSMauro Carvalho Chehab 	mt9p031->format.width = MT9P031_WINDOW_WIDTH_DEF;
1135cb7a01acSMauro Carvalho Chehab 	mt9p031->format.height = MT9P031_WINDOW_HEIGHT_DEF;
1136cb7a01acSMauro Carvalho Chehab 	mt9p031->format.field = V4L2_FIELD_NONE;
1137cb7a01acSMauro Carvalho Chehab 	mt9p031->format.colorspace = V4L2_COLORSPACE_SRGB;
1138cb7a01acSMauro Carvalho Chehab 
11392660a22bSLaurent Pinchart 	if (gpio_is_valid(pdata->reset)) {
114037b9f211SLaurent Pinchart 		ret = devm_gpio_request_one(&client->dev, pdata->reset,
114137b9f211SLaurent Pinchart 					    GPIOF_OUT_INIT_LOW, "mt9p031_rst");
1142cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
1143cb7a01acSMauro Carvalho Chehab 			goto done;
1144cb7a01acSMauro Carvalho Chehab 
1145cb7a01acSMauro Carvalho Chehab 		mt9p031->reset = pdata->reset;
1146cb7a01acSMauro Carvalho Chehab 	}
1147cb7a01acSMauro Carvalho Chehab 
1148d6749258SLaurent Pinchart 	ret = mt9p031_clk_setup(mt9p031);
1149*9012d088SLad, Prabhakar 	if (ret)
1150*9012d088SLad, Prabhakar 		goto done;
1151*9012d088SLad, Prabhakar 
1152*9012d088SLad, Prabhakar 	ret = v4l2_async_register_subdev(&mt9p031->subdev);
1153cb7a01acSMauro Carvalho Chehab 
1154cb7a01acSMauro Carvalho Chehab done:
1155cb7a01acSMauro Carvalho Chehab 	if (ret < 0) {
1156cb7a01acSMauro Carvalho Chehab 		v4l2_ctrl_handler_free(&mt9p031->ctrls);
1157cb7a01acSMauro Carvalho Chehab 		media_entity_cleanup(&mt9p031->subdev.entity);
115815af4a53SLad, Prabhakar 		mutex_destroy(&mt9p031->power_lock);
1159cb7a01acSMauro Carvalho Chehab 	}
1160cb7a01acSMauro Carvalho Chehab 
1161cb7a01acSMauro Carvalho Chehab 	return ret;
1162cb7a01acSMauro Carvalho Chehab }
1163cb7a01acSMauro Carvalho Chehab 
1164cb7a01acSMauro Carvalho Chehab static int mt9p031_remove(struct i2c_client *client)
1165cb7a01acSMauro Carvalho Chehab {
1166cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
1167cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
1168cb7a01acSMauro Carvalho Chehab 
1169cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_free(&mt9p031->ctrls);
1170*9012d088SLad, Prabhakar 	v4l2_async_unregister_subdev(subdev);
1171cb7a01acSMauro Carvalho Chehab 	media_entity_cleanup(&subdev->entity);
117215af4a53SLad, Prabhakar 	mutex_destroy(&mt9p031->power_lock);
1173cb7a01acSMauro Carvalho Chehab 
1174cb7a01acSMauro Carvalho Chehab 	return 0;
1175cb7a01acSMauro Carvalho Chehab }
1176cb7a01acSMauro Carvalho Chehab 
1177cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id mt9p031_id[] = {
1178cb7a01acSMauro Carvalho Chehab 	{ "mt9p031", MT9P031_MODEL_COLOR },
1179cb7a01acSMauro Carvalho Chehab 	{ "mt9p031m", MT9P031_MODEL_MONOCHROME },
1180cb7a01acSMauro Carvalho Chehab 	{ }
1181cb7a01acSMauro Carvalho Chehab };
1182cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, mt9p031_id);
1183cb7a01acSMauro Carvalho Chehab 
11848d4da37cSLad, Prabhakar #if IS_ENABLED(CONFIG_OF)
11858d4da37cSLad, Prabhakar static const struct of_device_id mt9p031_of_match[] = {
11868d4da37cSLad, Prabhakar 	{ .compatible = "aptina,mt9p031", },
11878d4da37cSLad, Prabhakar 	{ .compatible = "aptina,mt9p031m", },
11888d4da37cSLad, Prabhakar 	{ /* sentinel */ },
11898d4da37cSLad, Prabhakar };
11908d4da37cSLad, Prabhakar MODULE_DEVICE_TABLE(of, mt9p031_of_match);
11918d4da37cSLad, Prabhakar #endif
11928d4da37cSLad, Prabhakar 
1193cb7a01acSMauro Carvalho Chehab static struct i2c_driver mt9p031_i2c_driver = {
1194cb7a01acSMauro Carvalho Chehab 	.driver = {
11958d4da37cSLad, Prabhakar 		.of_match_table = of_match_ptr(mt9p031_of_match),
1196cb7a01acSMauro Carvalho Chehab 		.name = "mt9p031",
1197cb7a01acSMauro Carvalho Chehab 	},
1198cb7a01acSMauro Carvalho Chehab 	.probe          = mt9p031_probe,
1199cb7a01acSMauro Carvalho Chehab 	.remove         = mt9p031_remove,
1200cb7a01acSMauro Carvalho Chehab 	.id_table       = mt9p031_id,
1201cb7a01acSMauro Carvalho Chehab };
1202cb7a01acSMauro Carvalho Chehab 
1203cb7a01acSMauro Carvalho Chehab module_i2c_driver(mt9p031_i2c_driver);
1204cb7a01acSMauro Carvalho Chehab 
1205cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("Aptina MT9P031 Camera driver");
1206cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Bastian Hecht <hechtb@gmail.com>");
1207cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL v2");
1208