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