xref: /openbmc/linux/drivers/media/i2c/mt9p031.c (revision f90580ca0133c533763a6cb3e632a21098a382df)
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>
24cb7a01acSMauro Carvalho Chehab #include <linux/pm.h>
2597f21276SLaurent Pinchart #include <linux/regulator/consumer.h>
26cb7a01acSMauro Carvalho Chehab #include <linux/slab.h>
27cb7a01acSMauro Carvalho Chehab #include <linux/videodev2.h>
28cb7a01acSMauro Carvalho Chehab 
29cb7a01acSMauro Carvalho Chehab #include <media/mt9p031.h>
30cb7a01acSMauro Carvalho Chehab #include <media/v4l2-ctrls.h>
31cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h>
328d4da37cSLad, Prabhakar #include <media/v4l2-of.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
81cb7a01acSMauro Carvalho Chehab #define MT9P031_FRAME_RESTART				0x0b
82cb7a01acSMauro Carvalho Chehab #define MT9P031_SHUTTER_DELAY				0x0c
83cb7a01acSMauro Carvalho Chehab #define MT9P031_RST					0x0d
84cb7a01acSMauro Carvalho Chehab #define		MT9P031_RST_ENABLE			1
85cb7a01acSMauro Carvalho Chehab #define		MT9P031_RST_DISABLE			0
86cb7a01acSMauro Carvalho Chehab #define MT9P031_READ_MODE_1				0x1e
87cb7a01acSMauro Carvalho Chehab #define MT9P031_READ_MODE_2				0x20
88cb7a01acSMauro Carvalho Chehab #define		MT9P031_READ_MODE_2_ROW_MIR		(1 << 15)
89cb7a01acSMauro Carvalho Chehab #define		MT9P031_READ_MODE_2_COL_MIR		(1 << 14)
90cb7a01acSMauro Carvalho Chehab #define		MT9P031_READ_MODE_2_ROW_BLC		(1 << 6)
91cb7a01acSMauro Carvalho Chehab #define MT9P031_ROW_ADDRESS_MODE			0x22
92cb7a01acSMauro Carvalho Chehab #define MT9P031_COLUMN_ADDRESS_MODE			0x23
93cb7a01acSMauro Carvalho Chehab #define MT9P031_GLOBAL_GAIN				0x35
94cb7a01acSMauro Carvalho Chehab #define		MT9P031_GLOBAL_GAIN_MIN			8
95cb7a01acSMauro Carvalho Chehab #define		MT9P031_GLOBAL_GAIN_MAX			1024
96cb7a01acSMauro Carvalho Chehab #define		MT9P031_GLOBAL_GAIN_DEF			8
97cb7a01acSMauro Carvalho Chehab #define		MT9P031_GLOBAL_GAIN_MULT		(1 << 6)
98cb7a01acSMauro Carvalho Chehab #define MT9P031_ROW_BLACK_TARGET			0x49
99cb7a01acSMauro Carvalho Chehab #define MT9P031_ROW_BLACK_DEF_OFFSET			0x4b
100cb7a01acSMauro Carvalho Chehab #define MT9P031_GREEN1_OFFSET				0x60
101cb7a01acSMauro Carvalho Chehab #define MT9P031_GREEN2_OFFSET				0x61
102cb7a01acSMauro Carvalho Chehab #define MT9P031_BLACK_LEVEL_CALIBRATION			0x62
103cb7a01acSMauro Carvalho Chehab #define		MT9P031_BLC_MANUAL_BLC			(1 << 0)
104cb7a01acSMauro Carvalho Chehab #define MT9P031_RED_OFFSET				0x63
105cb7a01acSMauro Carvalho Chehab #define MT9P031_BLUE_OFFSET				0x64
106cb7a01acSMauro Carvalho Chehab #define MT9P031_TEST_PATTERN				0xa0
107cb7a01acSMauro Carvalho Chehab #define		MT9P031_TEST_PATTERN_SHIFT		3
108cb7a01acSMauro Carvalho Chehab #define		MT9P031_TEST_PATTERN_ENABLE		(1 << 0)
109cb7a01acSMauro Carvalho Chehab #define		MT9P031_TEST_PATTERN_DISABLE		(0 << 0)
110cb7a01acSMauro Carvalho Chehab #define MT9P031_TEST_PATTERN_GREEN			0xa1
111cb7a01acSMauro Carvalho Chehab #define MT9P031_TEST_PATTERN_RED			0xa2
112cb7a01acSMauro Carvalho Chehab #define MT9P031_TEST_PATTERN_BLUE			0xa3
113cb7a01acSMauro Carvalho Chehab 
114cb7a01acSMauro Carvalho Chehab enum mt9p031_model {
115cb7a01acSMauro Carvalho Chehab 	MT9P031_MODEL_COLOR,
116cb7a01acSMauro Carvalho Chehab 	MT9P031_MODEL_MONOCHROME,
117cb7a01acSMauro Carvalho Chehab };
118cb7a01acSMauro Carvalho Chehab 
119cb7a01acSMauro Carvalho Chehab struct mt9p031 {
120cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev subdev;
121cb7a01acSMauro Carvalho Chehab 	struct media_pad pad;
122cb7a01acSMauro Carvalho Chehab 	struct v4l2_rect crop;  /* Sensor window */
123cb7a01acSMauro Carvalho Chehab 	struct v4l2_mbus_framefmt format;
124cb7a01acSMauro Carvalho Chehab 	struct mt9p031_platform_data *pdata;
125cb7a01acSMauro Carvalho Chehab 	struct mutex power_lock; /* lock to protect power_count */
126cb7a01acSMauro Carvalho Chehab 	int power_count;
127cb7a01acSMauro Carvalho Chehab 
128d6749258SLaurent Pinchart 	struct clk *clk;
1297997196cSLaurent Pinchart 	struct regulator_bulk_data regulators[3];
13097f21276SLaurent Pinchart 
131cb7a01acSMauro Carvalho Chehab 	enum mt9p031_model model;
132cb7a01acSMauro Carvalho Chehab 	struct aptina_pll pll;
133cb7a01acSMauro Carvalho Chehab 	int reset;
134cb7a01acSMauro Carvalho Chehab 
135cb7a01acSMauro Carvalho Chehab 	struct v4l2_ctrl_handler ctrls;
136cb7a01acSMauro Carvalho Chehab 	struct v4l2_ctrl *blc_auto;
137cb7a01acSMauro Carvalho Chehab 	struct v4l2_ctrl *blc_offset;
138cb7a01acSMauro Carvalho Chehab 
139cb7a01acSMauro Carvalho Chehab 	/* Registers cache */
140cb7a01acSMauro Carvalho Chehab 	u16 output_control;
141cb7a01acSMauro Carvalho Chehab 	u16 mode2;
142cb7a01acSMauro Carvalho Chehab };
143cb7a01acSMauro Carvalho Chehab 
144cb7a01acSMauro Carvalho Chehab static struct mt9p031 *to_mt9p031(struct v4l2_subdev *sd)
145cb7a01acSMauro Carvalho Chehab {
146cb7a01acSMauro Carvalho Chehab 	return container_of(sd, struct mt9p031, subdev);
147cb7a01acSMauro Carvalho Chehab }
148cb7a01acSMauro Carvalho Chehab 
149cb7a01acSMauro Carvalho Chehab static int mt9p031_read(struct i2c_client *client, u8 reg)
150cb7a01acSMauro Carvalho Chehab {
151cb7a01acSMauro Carvalho Chehab 	return i2c_smbus_read_word_swapped(client, reg);
152cb7a01acSMauro Carvalho Chehab }
153cb7a01acSMauro Carvalho Chehab 
154cb7a01acSMauro Carvalho Chehab static int mt9p031_write(struct i2c_client *client, u8 reg, u16 data)
155cb7a01acSMauro Carvalho Chehab {
156cb7a01acSMauro Carvalho Chehab 	return i2c_smbus_write_word_swapped(client, reg, data);
157cb7a01acSMauro Carvalho Chehab }
158cb7a01acSMauro Carvalho Chehab 
159cb7a01acSMauro Carvalho Chehab static int mt9p031_set_output_control(struct mt9p031 *mt9p031, u16 clear,
160cb7a01acSMauro Carvalho Chehab 				      u16 set)
161cb7a01acSMauro Carvalho Chehab {
162cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
163cb7a01acSMauro Carvalho Chehab 	u16 value = (mt9p031->output_control & ~clear) | set;
164cb7a01acSMauro Carvalho Chehab 	int ret;
165cb7a01acSMauro Carvalho Chehab 
166cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_OUTPUT_CONTROL, value);
167cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
168cb7a01acSMauro Carvalho Chehab 		return ret;
169cb7a01acSMauro Carvalho Chehab 
170cb7a01acSMauro Carvalho Chehab 	mt9p031->output_control = value;
171cb7a01acSMauro Carvalho Chehab 	return 0;
172cb7a01acSMauro Carvalho Chehab }
173cb7a01acSMauro Carvalho Chehab 
174cb7a01acSMauro Carvalho Chehab static int mt9p031_set_mode2(struct mt9p031 *mt9p031, u16 clear, u16 set)
175cb7a01acSMauro Carvalho Chehab {
176cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
177cb7a01acSMauro Carvalho Chehab 	u16 value = (mt9p031->mode2 & ~clear) | set;
178cb7a01acSMauro Carvalho Chehab 	int ret;
179cb7a01acSMauro Carvalho Chehab 
180cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_READ_MODE_2, value);
181cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
182cb7a01acSMauro Carvalho Chehab 		return ret;
183cb7a01acSMauro Carvalho Chehab 
184cb7a01acSMauro Carvalho Chehab 	mt9p031->mode2 = value;
185cb7a01acSMauro Carvalho Chehab 	return 0;
186cb7a01acSMauro Carvalho Chehab }
187cb7a01acSMauro Carvalho Chehab 
188cb7a01acSMauro Carvalho Chehab static int mt9p031_reset(struct mt9p031 *mt9p031)
189cb7a01acSMauro Carvalho Chehab {
190cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
191cb7a01acSMauro Carvalho Chehab 	int ret;
192cb7a01acSMauro Carvalho Chehab 
193cb7a01acSMauro Carvalho Chehab 	/* Disable chip output, synchronous option update */
194cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_RST, MT9P031_RST_ENABLE);
195cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
196cb7a01acSMauro Carvalho Chehab 		return ret;
197cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_RST, MT9P031_RST_DISABLE);
198cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
199cb7a01acSMauro Carvalho Chehab 		return ret;
200cb7a01acSMauro Carvalho Chehab 
201cb7a01acSMauro Carvalho Chehab 	return mt9p031_set_output_control(mt9p031, MT9P031_OUTPUT_CONTROL_CEN,
202cb7a01acSMauro Carvalho Chehab 					  0);
203cb7a01acSMauro Carvalho Chehab }
204cb7a01acSMauro Carvalho Chehab 
205d6749258SLaurent Pinchart static int mt9p031_clk_setup(struct mt9p031 *mt9p031)
206cb7a01acSMauro Carvalho Chehab {
207cb7a01acSMauro Carvalho Chehab 	static const struct aptina_pll_limits limits = {
208cb7a01acSMauro Carvalho Chehab 		.ext_clock_min = 6000000,
209cb7a01acSMauro Carvalho Chehab 		.ext_clock_max = 27000000,
210cb7a01acSMauro Carvalho Chehab 		.int_clock_min = 2000000,
211cb7a01acSMauro Carvalho Chehab 		.int_clock_max = 13500000,
212cb7a01acSMauro Carvalho Chehab 		.out_clock_min = 180000000,
213cb7a01acSMauro Carvalho Chehab 		.out_clock_max = 360000000,
214cb7a01acSMauro Carvalho Chehab 		.pix_clock_max = 96000000,
215cb7a01acSMauro Carvalho Chehab 		.n_min = 1,
216cb7a01acSMauro Carvalho Chehab 		.n_max = 64,
217cb7a01acSMauro Carvalho Chehab 		.m_min = 16,
218cb7a01acSMauro Carvalho Chehab 		.m_max = 255,
219cb7a01acSMauro Carvalho Chehab 		.p1_min = 1,
220cb7a01acSMauro Carvalho Chehab 		.p1_max = 128,
221cb7a01acSMauro Carvalho Chehab 	};
222cb7a01acSMauro Carvalho Chehab 
223cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
224cb7a01acSMauro Carvalho Chehab 	struct mt9p031_platform_data *pdata = mt9p031->pdata;
225cb7a01acSMauro Carvalho Chehab 
226d6749258SLaurent Pinchart 	mt9p031->clk = devm_clk_get(&client->dev, NULL);
227d6749258SLaurent Pinchart 	if (IS_ERR(mt9p031->clk))
228d6749258SLaurent Pinchart 		return PTR_ERR(mt9p031->clk);
229d6749258SLaurent Pinchart 
230d6749258SLaurent Pinchart 	clk_set_rate(mt9p031->clk, pdata->ext_freq);
231d6749258SLaurent Pinchart 
232cb7a01acSMauro Carvalho Chehab 	mt9p031->pll.ext_clock = pdata->ext_freq;
233cb7a01acSMauro Carvalho Chehab 	mt9p031->pll.pix_clock = pdata->target_freq;
234cb7a01acSMauro Carvalho Chehab 
235cb7a01acSMauro Carvalho Chehab 	return aptina_pll_calculate(&client->dev, &limits, &mt9p031->pll);
236cb7a01acSMauro Carvalho Chehab }
237cb7a01acSMauro Carvalho Chehab 
238cb7a01acSMauro Carvalho Chehab static int mt9p031_pll_enable(struct mt9p031 *mt9p031)
239cb7a01acSMauro Carvalho Chehab {
240cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
241cb7a01acSMauro Carvalho Chehab 	int ret;
242cb7a01acSMauro Carvalho Chehab 
243cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_PLL_CONTROL,
244cb7a01acSMauro Carvalho Chehab 			    MT9P031_PLL_CONTROL_PWRON);
245cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
246cb7a01acSMauro Carvalho Chehab 		return ret;
247cb7a01acSMauro Carvalho Chehab 
248cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_PLL_CONFIG_1,
249cb7a01acSMauro Carvalho Chehab 			    (mt9p031->pll.m << 8) | (mt9p031->pll.n - 1));
250cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
251cb7a01acSMauro Carvalho Chehab 		return ret;
252cb7a01acSMauro Carvalho Chehab 
253cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_PLL_CONFIG_2, mt9p031->pll.p1 - 1);
254cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
255cb7a01acSMauro Carvalho Chehab 		return ret;
256cb7a01acSMauro Carvalho Chehab 
257cb7a01acSMauro Carvalho Chehab 	usleep_range(1000, 2000);
258cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_PLL_CONTROL,
259cb7a01acSMauro Carvalho Chehab 			    MT9P031_PLL_CONTROL_PWRON |
260cb7a01acSMauro Carvalho Chehab 			    MT9P031_PLL_CONTROL_USEPLL);
261cb7a01acSMauro Carvalho Chehab 	return ret;
262cb7a01acSMauro Carvalho Chehab }
263cb7a01acSMauro Carvalho Chehab 
264cb7a01acSMauro Carvalho Chehab static inline int mt9p031_pll_disable(struct mt9p031 *mt9p031)
265cb7a01acSMauro Carvalho Chehab {
266cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
267cb7a01acSMauro Carvalho Chehab 
268cb7a01acSMauro Carvalho Chehab 	return mt9p031_write(client, MT9P031_PLL_CONTROL,
269cb7a01acSMauro Carvalho Chehab 			     MT9P031_PLL_CONTROL_PWROFF);
270cb7a01acSMauro Carvalho Chehab }
271cb7a01acSMauro Carvalho Chehab 
272cb7a01acSMauro Carvalho Chehab static int mt9p031_power_on(struct mt9p031 *mt9p031)
273cb7a01acSMauro Carvalho Chehab {
2747997196cSLaurent Pinchart 	int ret;
2757997196cSLaurent Pinchart 
276cb7a01acSMauro Carvalho Chehab 	/* Ensure RESET_BAR is low */
2772660a22bSLaurent Pinchart 	if (gpio_is_valid(mt9p031->reset)) {
278cb7a01acSMauro Carvalho Chehab 		gpio_set_value(mt9p031->reset, 0);
279cb7a01acSMauro Carvalho Chehab 		usleep_range(1000, 2000);
280cb7a01acSMauro Carvalho Chehab 	}
281cb7a01acSMauro Carvalho Chehab 
28297f21276SLaurent Pinchart 	/* Bring up the supplies */
2837997196cSLaurent Pinchart 	ret = regulator_bulk_enable(ARRAY_SIZE(mt9p031->regulators),
2847997196cSLaurent Pinchart 				   mt9p031->regulators);
2857997196cSLaurent Pinchart 	if (ret < 0)
2867997196cSLaurent Pinchart 		return ret;
28797f21276SLaurent Pinchart 
288cb7a01acSMauro Carvalho Chehab 	/* Emable clock */
289d6749258SLaurent Pinchart 	if (mt9p031->clk)
290d6749258SLaurent Pinchart 		clk_prepare_enable(mt9p031->clk);
291cb7a01acSMauro Carvalho Chehab 
292cb7a01acSMauro Carvalho Chehab 	/* Now RESET_BAR must be high */
2932660a22bSLaurent Pinchart 	if (gpio_is_valid(mt9p031->reset)) {
294cb7a01acSMauro Carvalho Chehab 		gpio_set_value(mt9p031->reset, 1);
295cb7a01acSMauro Carvalho Chehab 		usleep_range(1000, 2000);
296cb7a01acSMauro Carvalho Chehab 	}
297cb7a01acSMauro Carvalho Chehab 
298cb7a01acSMauro Carvalho Chehab 	return 0;
299cb7a01acSMauro Carvalho Chehab }
300cb7a01acSMauro Carvalho Chehab 
301cb7a01acSMauro Carvalho Chehab static void mt9p031_power_off(struct mt9p031 *mt9p031)
302cb7a01acSMauro Carvalho Chehab {
3032660a22bSLaurent Pinchart 	if (gpio_is_valid(mt9p031->reset)) {
304cb7a01acSMauro Carvalho Chehab 		gpio_set_value(mt9p031->reset, 0);
305cb7a01acSMauro Carvalho Chehab 		usleep_range(1000, 2000);
306cb7a01acSMauro Carvalho Chehab 	}
307cb7a01acSMauro Carvalho Chehab 
3087997196cSLaurent Pinchart 	regulator_bulk_disable(ARRAY_SIZE(mt9p031->regulators),
3097997196cSLaurent Pinchart 			       mt9p031->regulators);
31097f21276SLaurent Pinchart 
311d6749258SLaurent Pinchart 	if (mt9p031->clk)
312d6749258SLaurent Pinchart 		clk_disable_unprepare(mt9p031->clk);
313cb7a01acSMauro Carvalho Chehab }
314cb7a01acSMauro Carvalho Chehab 
315cb7a01acSMauro Carvalho Chehab static int __mt9p031_set_power(struct mt9p031 *mt9p031, bool on)
316cb7a01acSMauro Carvalho Chehab {
317cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
318cb7a01acSMauro Carvalho Chehab 	int ret;
319cb7a01acSMauro Carvalho Chehab 
320cb7a01acSMauro Carvalho Chehab 	if (!on) {
321cb7a01acSMauro Carvalho Chehab 		mt9p031_power_off(mt9p031);
322cb7a01acSMauro Carvalho Chehab 		return 0;
323cb7a01acSMauro Carvalho Chehab 	}
324cb7a01acSMauro Carvalho Chehab 
325cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_power_on(mt9p031);
326cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
327cb7a01acSMauro Carvalho Chehab 		return ret;
328cb7a01acSMauro Carvalho Chehab 
329cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_reset(mt9p031);
330cb7a01acSMauro Carvalho Chehab 	if (ret < 0) {
331cb7a01acSMauro Carvalho Chehab 		dev_err(&client->dev, "Failed to reset the camera\n");
332cb7a01acSMauro Carvalho Chehab 		return ret;
333cb7a01acSMauro Carvalho Chehab 	}
334cb7a01acSMauro Carvalho Chehab 
335cb7a01acSMauro Carvalho Chehab 	return v4l2_ctrl_handler_setup(&mt9p031->ctrls);
336cb7a01acSMauro Carvalho Chehab }
337cb7a01acSMauro Carvalho Chehab 
338cb7a01acSMauro Carvalho Chehab /* -----------------------------------------------------------------------------
339cb7a01acSMauro Carvalho Chehab  * V4L2 subdev video operations
340cb7a01acSMauro Carvalho Chehab  */
341cb7a01acSMauro Carvalho Chehab 
342cb7a01acSMauro Carvalho Chehab static int mt9p031_set_params(struct mt9p031 *mt9p031)
343cb7a01acSMauro Carvalho Chehab {
344cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
345cb7a01acSMauro Carvalho Chehab 	struct v4l2_mbus_framefmt *format = &mt9p031->format;
346cb7a01acSMauro Carvalho Chehab 	const struct v4l2_rect *crop = &mt9p031->crop;
347cb7a01acSMauro Carvalho Chehab 	unsigned int hblank;
348cb7a01acSMauro Carvalho Chehab 	unsigned int vblank;
349cb7a01acSMauro Carvalho Chehab 	unsigned int xskip;
350cb7a01acSMauro Carvalho Chehab 	unsigned int yskip;
351cb7a01acSMauro Carvalho Chehab 	unsigned int xbin;
352cb7a01acSMauro Carvalho Chehab 	unsigned int ybin;
353cb7a01acSMauro Carvalho Chehab 	int ret;
354cb7a01acSMauro Carvalho Chehab 
355cb7a01acSMauro Carvalho Chehab 	/* Windows position and size.
356cb7a01acSMauro Carvalho Chehab 	 *
357cb7a01acSMauro Carvalho Chehab 	 * TODO: Make sure the start coordinates and window size match the
358cb7a01acSMauro Carvalho Chehab 	 * skipping, binning and mirroring (see description of registers 2 and 4
359cb7a01acSMauro Carvalho Chehab 	 * in table 13, and Binning section on page 41).
360cb7a01acSMauro Carvalho Chehab 	 */
361cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_COLUMN_START, crop->left);
362cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
363cb7a01acSMauro Carvalho Chehab 		return ret;
364cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_ROW_START, crop->top);
365cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
366cb7a01acSMauro Carvalho Chehab 		return ret;
367cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_WINDOW_WIDTH, crop->width - 1);
368cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
369cb7a01acSMauro Carvalho Chehab 		return ret;
370cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_WINDOW_HEIGHT, crop->height - 1);
371cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
372cb7a01acSMauro Carvalho Chehab 		return ret;
373cb7a01acSMauro Carvalho Chehab 
374cb7a01acSMauro Carvalho Chehab 	/* Row and column binning and skipping. Use the maximum binning value
375cb7a01acSMauro Carvalho Chehab 	 * compatible with the skipping settings.
376cb7a01acSMauro Carvalho Chehab 	 */
377cb7a01acSMauro Carvalho Chehab 	xskip = DIV_ROUND_CLOSEST(crop->width, format->width);
378cb7a01acSMauro Carvalho Chehab 	yskip = DIV_ROUND_CLOSEST(crop->height, format->height);
379cb7a01acSMauro Carvalho Chehab 	xbin = 1 << (ffs(xskip) - 1);
380cb7a01acSMauro Carvalho Chehab 	ybin = 1 << (ffs(yskip) - 1);
381cb7a01acSMauro Carvalho Chehab 
382cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_COLUMN_ADDRESS_MODE,
383cb7a01acSMauro Carvalho Chehab 			    ((xbin - 1) << 4) | (xskip - 1));
384cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
385cb7a01acSMauro Carvalho Chehab 		return ret;
386cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_write(client, MT9P031_ROW_ADDRESS_MODE,
387cb7a01acSMauro Carvalho Chehab 			    ((ybin - 1) << 4) | (yskip - 1));
388cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
389cb7a01acSMauro Carvalho Chehab 		return ret;
390cb7a01acSMauro Carvalho Chehab 
391cb7a01acSMauro Carvalho Chehab 	/* Blanking - use minimum value for horizontal blanking and default
392cb7a01acSMauro Carvalho Chehab 	 * value for vertical blanking.
393cb7a01acSMauro Carvalho Chehab 	 */
3945266c98bSLaurent Pinchart 	hblank = 346 * ybin + 64 + (80 >> min_t(unsigned int, xbin, 3));
395cb7a01acSMauro Carvalho Chehab 	vblank = MT9P031_VERTICAL_BLANK_DEF;
396cb7a01acSMauro Carvalho Chehab 
3975266c98bSLaurent Pinchart 	ret = mt9p031_write(client, MT9P031_HORIZONTAL_BLANK, hblank - 1);
398cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
399cb7a01acSMauro Carvalho Chehab 		return ret;
4005266c98bSLaurent Pinchart 	ret = mt9p031_write(client, MT9P031_VERTICAL_BLANK, vblank - 1);
401cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
402cb7a01acSMauro Carvalho Chehab 		return ret;
403cb7a01acSMauro Carvalho Chehab 
404cb7a01acSMauro Carvalho Chehab 	return ret;
405cb7a01acSMauro Carvalho Chehab }
406cb7a01acSMauro Carvalho Chehab 
407cb7a01acSMauro Carvalho Chehab static int mt9p031_s_stream(struct v4l2_subdev *subdev, int enable)
408cb7a01acSMauro Carvalho Chehab {
409cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
410cb7a01acSMauro Carvalho Chehab 	int ret;
411cb7a01acSMauro Carvalho Chehab 
412cb7a01acSMauro Carvalho Chehab 	if (!enable) {
413cb7a01acSMauro Carvalho Chehab 		/* Stop sensor readout */
414cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_set_output_control(mt9p031,
415cb7a01acSMauro Carvalho Chehab 						 MT9P031_OUTPUT_CONTROL_CEN, 0);
416cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
417cb7a01acSMauro Carvalho Chehab 			return ret;
418cb7a01acSMauro Carvalho Chehab 
419cb7a01acSMauro Carvalho Chehab 		return mt9p031_pll_disable(mt9p031);
420cb7a01acSMauro Carvalho Chehab 	}
421cb7a01acSMauro Carvalho Chehab 
422cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_set_params(mt9p031);
423cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
424cb7a01acSMauro Carvalho Chehab 		return ret;
425cb7a01acSMauro Carvalho Chehab 
426cb7a01acSMauro Carvalho Chehab 	/* Switch to master "normal" mode */
427cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_set_output_control(mt9p031, 0,
428cb7a01acSMauro Carvalho Chehab 					 MT9P031_OUTPUT_CONTROL_CEN);
429cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
430cb7a01acSMauro Carvalho Chehab 		return ret;
431cb7a01acSMauro Carvalho Chehab 
432cb7a01acSMauro Carvalho Chehab 	return mt9p031_pll_enable(mt9p031);
433cb7a01acSMauro Carvalho Chehab }
434cb7a01acSMauro Carvalho Chehab 
435cb7a01acSMauro Carvalho Chehab static int mt9p031_enum_mbus_code(struct v4l2_subdev *subdev,
436cb7a01acSMauro Carvalho Chehab 				  struct v4l2_subdev_fh *fh,
437cb7a01acSMauro Carvalho Chehab 				  struct v4l2_subdev_mbus_code_enum *code)
438cb7a01acSMauro Carvalho Chehab {
439cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
440cb7a01acSMauro Carvalho Chehab 
441cb7a01acSMauro Carvalho Chehab 	if (code->pad || code->index)
442cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
443cb7a01acSMauro Carvalho Chehab 
444cb7a01acSMauro Carvalho Chehab 	code->code = mt9p031->format.code;
445cb7a01acSMauro Carvalho Chehab 	return 0;
446cb7a01acSMauro Carvalho Chehab }
447cb7a01acSMauro Carvalho Chehab 
448cb7a01acSMauro Carvalho Chehab static int mt9p031_enum_frame_size(struct v4l2_subdev *subdev,
449cb7a01acSMauro Carvalho Chehab 				   struct v4l2_subdev_fh *fh,
450cb7a01acSMauro Carvalho Chehab 				   struct v4l2_subdev_frame_size_enum *fse)
451cb7a01acSMauro Carvalho Chehab {
452cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
453cb7a01acSMauro Carvalho Chehab 
454cb7a01acSMauro Carvalho Chehab 	if (fse->index >= 8 || fse->code != mt9p031->format.code)
455cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
456cb7a01acSMauro Carvalho Chehab 
457cb7a01acSMauro Carvalho Chehab 	fse->min_width = MT9P031_WINDOW_WIDTH_DEF
458cb7a01acSMauro Carvalho Chehab 		       / min_t(unsigned int, 7, fse->index + 1);
459cb7a01acSMauro Carvalho Chehab 	fse->max_width = fse->min_width;
460cb7a01acSMauro Carvalho Chehab 	fse->min_height = MT9P031_WINDOW_HEIGHT_DEF / (fse->index + 1);
461cb7a01acSMauro Carvalho Chehab 	fse->max_height = fse->min_height;
462cb7a01acSMauro Carvalho Chehab 
463cb7a01acSMauro Carvalho Chehab 	return 0;
464cb7a01acSMauro Carvalho Chehab }
465cb7a01acSMauro Carvalho Chehab 
466cb7a01acSMauro Carvalho Chehab static struct v4l2_mbus_framefmt *
467cb7a01acSMauro Carvalho Chehab __mt9p031_get_pad_format(struct mt9p031 *mt9p031, struct v4l2_subdev_fh *fh,
468cb7a01acSMauro Carvalho Chehab 			 unsigned int pad, u32 which)
469cb7a01acSMauro Carvalho Chehab {
470cb7a01acSMauro Carvalho Chehab 	switch (which) {
471cb7a01acSMauro Carvalho Chehab 	case V4L2_SUBDEV_FORMAT_TRY:
472cb7a01acSMauro Carvalho Chehab 		return v4l2_subdev_get_try_format(fh, pad);
473cb7a01acSMauro Carvalho Chehab 	case V4L2_SUBDEV_FORMAT_ACTIVE:
474cb7a01acSMauro Carvalho Chehab 		return &mt9p031->format;
475cb7a01acSMauro Carvalho Chehab 	default:
476cb7a01acSMauro Carvalho Chehab 		return NULL;
477cb7a01acSMauro Carvalho Chehab 	}
478cb7a01acSMauro Carvalho Chehab }
479cb7a01acSMauro Carvalho Chehab 
480cb7a01acSMauro Carvalho Chehab static struct v4l2_rect *
481cb7a01acSMauro Carvalho Chehab __mt9p031_get_pad_crop(struct mt9p031 *mt9p031, struct v4l2_subdev_fh *fh,
482cb7a01acSMauro Carvalho Chehab 		     unsigned int pad, u32 which)
483cb7a01acSMauro Carvalho Chehab {
484cb7a01acSMauro Carvalho Chehab 	switch (which) {
485cb7a01acSMauro Carvalho Chehab 	case V4L2_SUBDEV_FORMAT_TRY:
486cb7a01acSMauro Carvalho Chehab 		return v4l2_subdev_get_try_crop(fh, pad);
487cb7a01acSMauro Carvalho Chehab 	case V4L2_SUBDEV_FORMAT_ACTIVE:
488cb7a01acSMauro Carvalho Chehab 		return &mt9p031->crop;
489cb7a01acSMauro Carvalho Chehab 	default:
490cb7a01acSMauro Carvalho Chehab 		return NULL;
491cb7a01acSMauro Carvalho Chehab 	}
492cb7a01acSMauro Carvalho Chehab }
493cb7a01acSMauro Carvalho Chehab 
494cb7a01acSMauro Carvalho Chehab static int mt9p031_get_format(struct v4l2_subdev *subdev,
495cb7a01acSMauro Carvalho Chehab 			      struct v4l2_subdev_fh *fh,
496cb7a01acSMauro Carvalho Chehab 			      struct v4l2_subdev_format *fmt)
497cb7a01acSMauro Carvalho Chehab {
498cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
499cb7a01acSMauro Carvalho Chehab 
500cb7a01acSMauro Carvalho Chehab 	fmt->format = *__mt9p031_get_pad_format(mt9p031, fh, fmt->pad,
501cb7a01acSMauro Carvalho Chehab 						fmt->which);
502cb7a01acSMauro Carvalho Chehab 	return 0;
503cb7a01acSMauro Carvalho Chehab }
504cb7a01acSMauro Carvalho Chehab 
505cb7a01acSMauro Carvalho Chehab static int mt9p031_set_format(struct v4l2_subdev *subdev,
506cb7a01acSMauro Carvalho Chehab 			      struct v4l2_subdev_fh *fh,
507cb7a01acSMauro Carvalho Chehab 			      struct v4l2_subdev_format *format)
508cb7a01acSMauro Carvalho Chehab {
509cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
510cb7a01acSMauro Carvalho Chehab 	struct v4l2_mbus_framefmt *__format;
511cb7a01acSMauro Carvalho Chehab 	struct v4l2_rect *__crop;
512cb7a01acSMauro Carvalho Chehab 	unsigned int width;
513cb7a01acSMauro Carvalho Chehab 	unsigned int height;
514cb7a01acSMauro Carvalho Chehab 	unsigned int hratio;
515cb7a01acSMauro Carvalho Chehab 	unsigned int vratio;
516cb7a01acSMauro Carvalho Chehab 
517cb7a01acSMauro Carvalho Chehab 	__crop = __mt9p031_get_pad_crop(mt9p031, fh, format->pad,
518cb7a01acSMauro Carvalho Chehab 					format->which);
519cb7a01acSMauro Carvalho Chehab 
520cb7a01acSMauro Carvalho Chehab 	/* Clamp the width and height to avoid dividing by zero. */
521cb7a01acSMauro Carvalho Chehab 	width = clamp_t(unsigned int, ALIGN(format->format.width, 2),
522*f90580caSRicardo Ribalda 			max_t(unsigned int, __crop->width / 7,
523*f90580caSRicardo Ribalda 			      MT9P031_WINDOW_WIDTH_MIN),
524cb7a01acSMauro Carvalho Chehab 			__crop->width);
525cb7a01acSMauro Carvalho Chehab 	height = clamp_t(unsigned int, ALIGN(format->format.height, 2),
526*f90580caSRicardo Ribalda 			 max_t(unsigned int, __crop->height / 8,
527*f90580caSRicardo Ribalda 			       MT9P031_WINDOW_HEIGHT_MIN),
528cb7a01acSMauro Carvalho Chehab 			 __crop->height);
529cb7a01acSMauro Carvalho Chehab 
530cb7a01acSMauro Carvalho Chehab 	hratio = DIV_ROUND_CLOSEST(__crop->width, width);
531cb7a01acSMauro Carvalho Chehab 	vratio = DIV_ROUND_CLOSEST(__crop->height, height);
532cb7a01acSMauro Carvalho Chehab 
533cb7a01acSMauro Carvalho Chehab 	__format = __mt9p031_get_pad_format(mt9p031, fh, format->pad,
534cb7a01acSMauro Carvalho Chehab 					    format->which);
535cb7a01acSMauro Carvalho Chehab 	__format->width = __crop->width / hratio;
536cb7a01acSMauro Carvalho Chehab 	__format->height = __crop->height / vratio;
537cb7a01acSMauro Carvalho Chehab 
538cb7a01acSMauro Carvalho Chehab 	format->format = *__format;
539cb7a01acSMauro Carvalho Chehab 
540cb7a01acSMauro Carvalho Chehab 	return 0;
541cb7a01acSMauro Carvalho Chehab }
542cb7a01acSMauro Carvalho Chehab 
543cb7a01acSMauro Carvalho Chehab static int mt9p031_get_crop(struct v4l2_subdev *subdev,
544cb7a01acSMauro Carvalho Chehab 			    struct v4l2_subdev_fh *fh,
545cb7a01acSMauro Carvalho Chehab 			    struct v4l2_subdev_crop *crop)
546cb7a01acSMauro Carvalho Chehab {
547cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
548cb7a01acSMauro Carvalho Chehab 
549cb7a01acSMauro Carvalho Chehab 	crop->rect = *__mt9p031_get_pad_crop(mt9p031, fh, crop->pad,
550cb7a01acSMauro Carvalho Chehab 					     crop->which);
551cb7a01acSMauro Carvalho Chehab 	return 0;
552cb7a01acSMauro Carvalho Chehab }
553cb7a01acSMauro Carvalho Chehab 
554cb7a01acSMauro Carvalho Chehab static int mt9p031_set_crop(struct v4l2_subdev *subdev,
555cb7a01acSMauro Carvalho Chehab 			    struct v4l2_subdev_fh *fh,
556cb7a01acSMauro Carvalho Chehab 			    struct v4l2_subdev_crop *crop)
557cb7a01acSMauro Carvalho Chehab {
558cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
559cb7a01acSMauro Carvalho Chehab 	struct v4l2_mbus_framefmt *__format;
560cb7a01acSMauro Carvalho Chehab 	struct v4l2_rect *__crop;
561cb7a01acSMauro Carvalho Chehab 	struct v4l2_rect rect;
562cb7a01acSMauro Carvalho Chehab 
563cb7a01acSMauro Carvalho Chehab 	/* Clamp the crop rectangle boundaries and align them to a multiple of 2
564cb7a01acSMauro Carvalho Chehab 	 * pixels to ensure a GRBG Bayer pattern.
565cb7a01acSMauro Carvalho Chehab 	 */
566cb7a01acSMauro Carvalho Chehab 	rect.left = clamp(ALIGN(crop->rect.left, 2), MT9P031_COLUMN_START_MIN,
567cb7a01acSMauro Carvalho Chehab 			  MT9P031_COLUMN_START_MAX);
568cb7a01acSMauro Carvalho Chehab 	rect.top = clamp(ALIGN(crop->rect.top, 2), MT9P031_ROW_START_MIN,
569cb7a01acSMauro Carvalho Chehab 			 MT9P031_ROW_START_MAX);
570*f90580caSRicardo Ribalda 	rect.width = clamp_t(unsigned int, ALIGN(crop->rect.width, 2),
571cb7a01acSMauro Carvalho Chehab 			     MT9P031_WINDOW_WIDTH_MIN,
572cb7a01acSMauro Carvalho Chehab 			     MT9P031_WINDOW_WIDTH_MAX);
573*f90580caSRicardo Ribalda 	rect.height = clamp_t(unsigned int, ALIGN(crop->rect.height, 2),
574cb7a01acSMauro Carvalho Chehab 			      MT9P031_WINDOW_HEIGHT_MIN,
575cb7a01acSMauro Carvalho Chehab 			      MT9P031_WINDOW_HEIGHT_MAX);
576cb7a01acSMauro Carvalho Chehab 
577*f90580caSRicardo Ribalda 	rect.width = min_t(unsigned int, rect.width,
578*f90580caSRicardo Ribalda 			   MT9P031_PIXEL_ARRAY_WIDTH - rect.left);
579*f90580caSRicardo Ribalda 	rect.height = min_t(unsigned int, rect.height,
580*f90580caSRicardo Ribalda 			    MT9P031_PIXEL_ARRAY_HEIGHT - rect.top);
581cb7a01acSMauro Carvalho Chehab 
582cb7a01acSMauro Carvalho Chehab 	__crop = __mt9p031_get_pad_crop(mt9p031, fh, crop->pad, crop->which);
583cb7a01acSMauro Carvalho Chehab 
584cb7a01acSMauro Carvalho Chehab 	if (rect.width != __crop->width || rect.height != __crop->height) {
585cb7a01acSMauro Carvalho Chehab 		/* Reset the output image size if the crop rectangle size has
586cb7a01acSMauro Carvalho Chehab 		 * been modified.
587cb7a01acSMauro Carvalho Chehab 		 */
588cb7a01acSMauro Carvalho Chehab 		__format = __mt9p031_get_pad_format(mt9p031, fh, crop->pad,
589cb7a01acSMauro Carvalho Chehab 						    crop->which);
590cb7a01acSMauro Carvalho Chehab 		__format->width = rect.width;
591cb7a01acSMauro Carvalho Chehab 		__format->height = rect.height;
592cb7a01acSMauro Carvalho Chehab 	}
593cb7a01acSMauro Carvalho Chehab 
594cb7a01acSMauro Carvalho Chehab 	*__crop = rect;
595cb7a01acSMauro Carvalho Chehab 	crop->rect = rect;
596cb7a01acSMauro Carvalho Chehab 
597cb7a01acSMauro Carvalho Chehab 	return 0;
598cb7a01acSMauro Carvalho Chehab }
599cb7a01acSMauro Carvalho Chehab 
600cb7a01acSMauro Carvalho Chehab /* -----------------------------------------------------------------------------
601cb7a01acSMauro Carvalho Chehab  * V4L2 subdev control operations
602cb7a01acSMauro Carvalho Chehab  */
603cb7a01acSMauro Carvalho Chehab 
604cb7a01acSMauro Carvalho Chehab #define V4L2_CID_BLC_AUTO		(V4L2_CID_USER_BASE | 0x1002)
605cb7a01acSMauro Carvalho Chehab #define V4L2_CID_BLC_TARGET_LEVEL	(V4L2_CID_USER_BASE | 0x1003)
606cb7a01acSMauro Carvalho Chehab #define V4L2_CID_BLC_ANALOG_OFFSET	(V4L2_CID_USER_BASE | 0x1004)
607cb7a01acSMauro Carvalho Chehab #define V4L2_CID_BLC_DIGITAL_OFFSET	(V4L2_CID_USER_BASE | 0x1005)
608cb7a01acSMauro Carvalho Chehab 
609cb7a01acSMauro Carvalho Chehab static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl)
610cb7a01acSMauro Carvalho Chehab {
611cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 =
612cb7a01acSMauro Carvalho Chehab 			container_of(ctrl->handler, struct mt9p031, ctrls);
613cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
614cb7a01acSMauro Carvalho Chehab 	u16 data;
615cb7a01acSMauro Carvalho Chehab 	int ret;
616cb7a01acSMauro Carvalho Chehab 
617cb7a01acSMauro Carvalho Chehab 	switch (ctrl->id) {
618cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_EXPOSURE:
619cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_write(client, MT9P031_SHUTTER_WIDTH_UPPER,
620cb7a01acSMauro Carvalho Chehab 				    (ctrl->val >> 16) & 0xffff);
621cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
622cb7a01acSMauro Carvalho Chehab 			return ret;
623cb7a01acSMauro Carvalho Chehab 
624cb7a01acSMauro Carvalho Chehab 		return mt9p031_write(client, MT9P031_SHUTTER_WIDTH_LOWER,
625cb7a01acSMauro Carvalho Chehab 				     ctrl->val & 0xffff);
626cb7a01acSMauro Carvalho Chehab 
627cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_GAIN:
628cb7a01acSMauro Carvalho Chehab 		/* Gain is controlled by 2 analog stages and a digital stage.
629cb7a01acSMauro Carvalho Chehab 		 * Valid values for the 3 stages are
630cb7a01acSMauro Carvalho Chehab 		 *
631cb7a01acSMauro Carvalho Chehab 		 * Stage                Min     Max     Step
632cb7a01acSMauro Carvalho Chehab 		 * ------------------------------------------
633cb7a01acSMauro Carvalho Chehab 		 * First analog stage   x1      x2      1
634cb7a01acSMauro Carvalho Chehab 		 * Second analog stage  x1      x4      0.125
635cb7a01acSMauro Carvalho Chehab 		 * Digital stage        x1      x16     0.125
636cb7a01acSMauro Carvalho Chehab 		 *
637cb7a01acSMauro Carvalho Chehab 		 * To minimize noise, the gain stages should be used in the
638cb7a01acSMauro Carvalho Chehab 		 * second analog stage, first analog stage, digital stage order.
639cb7a01acSMauro Carvalho Chehab 		 * Gain from a previous stage should be pushed to its maximum
640cb7a01acSMauro Carvalho Chehab 		 * value before the next stage is used.
641cb7a01acSMauro Carvalho Chehab 		 */
642cb7a01acSMauro Carvalho Chehab 		if (ctrl->val <= 32) {
643cb7a01acSMauro Carvalho Chehab 			data = ctrl->val;
644cb7a01acSMauro Carvalho Chehab 		} else if (ctrl->val <= 64) {
645cb7a01acSMauro Carvalho Chehab 			ctrl->val &= ~1;
646cb7a01acSMauro Carvalho Chehab 			data = (1 << 6) | (ctrl->val >> 1);
647cb7a01acSMauro Carvalho Chehab 		} else {
648cb7a01acSMauro Carvalho Chehab 			ctrl->val &= ~7;
649cb7a01acSMauro Carvalho Chehab 			data = ((ctrl->val - 64) << 5) | (1 << 6) | 32;
650cb7a01acSMauro Carvalho Chehab 		}
651cb7a01acSMauro Carvalho Chehab 
652cb7a01acSMauro Carvalho Chehab 		return mt9p031_write(client, MT9P031_GLOBAL_GAIN, data);
653cb7a01acSMauro Carvalho Chehab 
654cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_HFLIP:
655cb7a01acSMauro Carvalho Chehab 		if (ctrl->val)
656cb7a01acSMauro Carvalho Chehab 			return mt9p031_set_mode2(mt9p031,
657cb7a01acSMauro Carvalho Chehab 					0, MT9P031_READ_MODE_2_COL_MIR);
658cb7a01acSMauro Carvalho Chehab 		else
659cb7a01acSMauro Carvalho Chehab 			return mt9p031_set_mode2(mt9p031,
660cb7a01acSMauro Carvalho Chehab 					MT9P031_READ_MODE_2_COL_MIR, 0);
661cb7a01acSMauro Carvalho Chehab 
662cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_VFLIP:
663cb7a01acSMauro Carvalho Chehab 		if (ctrl->val)
664cb7a01acSMauro Carvalho Chehab 			return mt9p031_set_mode2(mt9p031,
665cb7a01acSMauro Carvalho Chehab 					0, MT9P031_READ_MODE_2_ROW_MIR);
666cb7a01acSMauro Carvalho Chehab 		else
667cb7a01acSMauro Carvalho Chehab 			return mt9p031_set_mode2(mt9p031,
668cb7a01acSMauro Carvalho Chehab 					MT9P031_READ_MODE_2_ROW_MIR, 0);
669cb7a01acSMauro Carvalho Chehab 
670cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_TEST_PATTERN:
671cb7a01acSMauro Carvalho Chehab 		if (!ctrl->val) {
672cb7a01acSMauro Carvalho Chehab 			/* Restore the black level compensation settings. */
673cb7a01acSMauro Carvalho Chehab 			if (mt9p031->blc_auto->cur.val != 0) {
674cb7a01acSMauro Carvalho Chehab 				ret = mt9p031_s_ctrl(mt9p031->blc_auto);
675cb7a01acSMauro Carvalho Chehab 				if (ret < 0)
676cb7a01acSMauro Carvalho Chehab 					return ret;
677cb7a01acSMauro Carvalho Chehab 			}
678cb7a01acSMauro Carvalho Chehab 			if (mt9p031->blc_offset->cur.val != 0) {
679cb7a01acSMauro Carvalho Chehab 				ret = mt9p031_s_ctrl(mt9p031->blc_offset);
680cb7a01acSMauro Carvalho Chehab 				if (ret < 0)
681cb7a01acSMauro Carvalho Chehab 					return ret;
682cb7a01acSMauro Carvalho Chehab 			}
683cb7a01acSMauro Carvalho Chehab 			return mt9p031_write(client, MT9P031_TEST_PATTERN,
684cb7a01acSMauro Carvalho Chehab 					     MT9P031_TEST_PATTERN_DISABLE);
685cb7a01acSMauro Carvalho Chehab 		}
686cb7a01acSMauro Carvalho Chehab 
687cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_write(client, MT9P031_TEST_PATTERN_GREEN, 0x05a0);
688cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
689cb7a01acSMauro Carvalho Chehab 			return ret;
690cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_write(client, MT9P031_TEST_PATTERN_RED, 0x0a50);
691cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
692cb7a01acSMauro Carvalho Chehab 			return ret;
693cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_write(client, MT9P031_TEST_PATTERN_BLUE, 0x0aa0);
694cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
695cb7a01acSMauro Carvalho Chehab 			return ret;
696cb7a01acSMauro Carvalho Chehab 
697cb7a01acSMauro Carvalho Chehab 		/* Disable digital black level compensation when using a test
698cb7a01acSMauro Carvalho Chehab 		 * pattern.
699cb7a01acSMauro Carvalho Chehab 		 */
700cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_set_mode2(mt9p031, MT9P031_READ_MODE_2_ROW_BLC,
701cb7a01acSMauro Carvalho Chehab 					0);
702cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
703cb7a01acSMauro Carvalho Chehab 			return ret;
704cb7a01acSMauro Carvalho Chehab 
705cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET, 0);
706cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
707cb7a01acSMauro Carvalho Chehab 			return ret;
708cb7a01acSMauro Carvalho Chehab 
709cb7a01acSMauro Carvalho Chehab 		return mt9p031_write(client, MT9P031_TEST_PATTERN,
710cb7a01acSMauro Carvalho Chehab 				((ctrl->val - 1) << MT9P031_TEST_PATTERN_SHIFT)
711cb7a01acSMauro Carvalho Chehab 				| MT9P031_TEST_PATTERN_ENABLE);
712cb7a01acSMauro Carvalho Chehab 
713cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_BLC_AUTO:
714cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_set_mode2(mt9p031,
715cb7a01acSMauro Carvalho Chehab 				ctrl->val ? 0 : MT9P031_READ_MODE_2_ROW_BLC,
716cb7a01acSMauro Carvalho Chehab 				ctrl->val ? MT9P031_READ_MODE_2_ROW_BLC : 0);
717cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
718cb7a01acSMauro Carvalho Chehab 			return ret;
719cb7a01acSMauro Carvalho Chehab 
720cb7a01acSMauro Carvalho Chehab 		return mt9p031_write(client, MT9P031_BLACK_LEVEL_CALIBRATION,
721cb7a01acSMauro Carvalho Chehab 				     ctrl->val ? 0 : MT9P031_BLC_MANUAL_BLC);
722cb7a01acSMauro Carvalho Chehab 
723cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_BLC_TARGET_LEVEL:
724cb7a01acSMauro Carvalho Chehab 		return mt9p031_write(client, MT9P031_ROW_BLACK_TARGET,
725cb7a01acSMauro Carvalho Chehab 				     ctrl->val);
726cb7a01acSMauro Carvalho Chehab 
727cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_BLC_ANALOG_OFFSET:
728cb7a01acSMauro Carvalho Chehab 		data = ctrl->val & ((1 << 9) - 1);
729cb7a01acSMauro Carvalho Chehab 
730cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_write(client, MT9P031_GREEN1_OFFSET, data);
731cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
732cb7a01acSMauro Carvalho Chehab 			return ret;
733cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_write(client, MT9P031_GREEN2_OFFSET, data);
734cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
735cb7a01acSMauro Carvalho Chehab 			return ret;
736cb7a01acSMauro Carvalho Chehab 		ret = mt9p031_write(client, MT9P031_RED_OFFSET, data);
737cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
738cb7a01acSMauro Carvalho Chehab 			return ret;
739cb7a01acSMauro Carvalho Chehab 		return mt9p031_write(client, MT9P031_BLUE_OFFSET, data);
740cb7a01acSMauro Carvalho Chehab 
741cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_BLC_DIGITAL_OFFSET:
742cb7a01acSMauro Carvalho Chehab 		return mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET,
743cb7a01acSMauro Carvalho Chehab 				     ctrl->val & ((1 << 12) - 1));
744cb7a01acSMauro Carvalho Chehab 	}
745cb7a01acSMauro Carvalho Chehab 
746cb7a01acSMauro Carvalho Chehab 	return 0;
747cb7a01acSMauro Carvalho Chehab }
748cb7a01acSMauro Carvalho Chehab 
749cb7a01acSMauro Carvalho Chehab static struct v4l2_ctrl_ops mt9p031_ctrl_ops = {
750cb7a01acSMauro Carvalho Chehab 	.s_ctrl = mt9p031_s_ctrl,
751cb7a01acSMauro Carvalho Chehab };
752cb7a01acSMauro Carvalho Chehab 
753cb7a01acSMauro Carvalho Chehab static const char * const mt9p031_test_pattern_menu[] = {
754cb7a01acSMauro Carvalho Chehab 	"Disabled",
755cb7a01acSMauro Carvalho Chehab 	"Color Field",
756cb7a01acSMauro Carvalho Chehab 	"Horizontal Gradient",
757cb7a01acSMauro Carvalho Chehab 	"Vertical Gradient",
758cb7a01acSMauro Carvalho Chehab 	"Diagonal Gradient",
759cb7a01acSMauro Carvalho Chehab 	"Classic Test Pattern",
760cb7a01acSMauro Carvalho Chehab 	"Walking 1s",
761cb7a01acSMauro Carvalho Chehab 	"Monochrome Horizontal Bars",
762cb7a01acSMauro Carvalho Chehab 	"Monochrome Vertical Bars",
763cb7a01acSMauro Carvalho Chehab 	"Vertical Color Bars",
764cb7a01acSMauro Carvalho Chehab };
765cb7a01acSMauro Carvalho Chehab 
766cb7a01acSMauro Carvalho Chehab static const struct v4l2_ctrl_config mt9p031_ctrls[] = {
767cb7a01acSMauro Carvalho Chehab 	{
768cb7a01acSMauro Carvalho Chehab 		.ops		= &mt9p031_ctrl_ops,
769cb7a01acSMauro Carvalho Chehab 		.id		= V4L2_CID_BLC_AUTO,
770cb7a01acSMauro Carvalho Chehab 		.type		= V4L2_CTRL_TYPE_BOOLEAN,
771cb7a01acSMauro Carvalho Chehab 		.name		= "BLC, Auto",
772cb7a01acSMauro Carvalho Chehab 		.min		= 0,
773cb7a01acSMauro Carvalho Chehab 		.max		= 1,
774cb7a01acSMauro Carvalho Chehab 		.step		= 1,
775cb7a01acSMauro Carvalho Chehab 		.def		= 1,
776cb7a01acSMauro Carvalho Chehab 		.flags		= 0,
777cb7a01acSMauro Carvalho Chehab 	}, {
778cb7a01acSMauro Carvalho Chehab 		.ops		= &mt9p031_ctrl_ops,
779cb7a01acSMauro Carvalho Chehab 		.id		= V4L2_CID_BLC_TARGET_LEVEL,
780cb7a01acSMauro Carvalho Chehab 		.type		= V4L2_CTRL_TYPE_INTEGER,
781cb7a01acSMauro Carvalho Chehab 		.name		= "BLC Target Level",
782cb7a01acSMauro Carvalho Chehab 		.min		= 0,
783cb7a01acSMauro Carvalho Chehab 		.max		= 4095,
784cb7a01acSMauro Carvalho Chehab 		.step		= 1,
785cb7a01acSMauro Carvalho Chehab 		.def		= 168,
786cb7a01acSMauro Carvalho Chehab 		.flags		= 0,
787cb7a01acSMauro Carvalho Chehab 	}, {
788cb7a01acSMauro Carvalho Chehab 		.ops		= &mt9p031_ctrl_ops,
789cb7a01acSMauro Carvalho Chehab 		.id		= V4L2_CID_BLC_ANALOG_OFFSET,
790cb7a01acSMauro Carvalho Chehab 		.type		= V4L2_CTRL_TYPE_INTEGER,
791cb7a01acSMauro Carvalho Chehab 		.name		= "BLC Analog Offset",
792cb7a01acSMauro Carvalho Chehab 		.min		= -255,
793cb7a01acSMauro Carvalho Chehab 		.max		= 255,
794cb7a01acSMauro Carvalho Chehab 		.step		= 1,
795cb7a01acSMauro Carvalho Chehab 		.def		= 32,
796cb7a01acSMauro Carvalho Chehab 		.flags		= 0,
797cb7a01acSMauro Carvalho Chehab 	}, {
798cb7a01acSMauro Carvalho Chehab 		.ops		= &mt9p031_ctrl_ops,
799cb7a01acSMauro Carvalho Chehab 		.id		= V4L2_CID_BLC_DIGITAL_OFFSET,
800cb7a01acSMauro Carvalho Chehab 		.type		= V4L2_CTRL_TYPE_INTEGER,
801cb7a01acSMauro Carvalho Chehab 		.name		= "BLC Digital Offset",
802cb7a01acSMauro Carvalho Chehab 		.min		= -2048,
803cb7a01acSMauro Carvalho Chehab 		.max		= 2047,
804cb7a01acSMauro Carvalho Chehab 		.step		= 1,
805cb7a01acSMauro Carvalho Chehab 		.def		= 40,
806cb7a01acSMauro Carvalho Chehab 		.flags		= 0,
807cb7a01acSMauro Carvalho Chehab 	}
808cb7a01acSMauro Carvalho Chehab };
809cb7a01acSMauro Carvalho Chehab 
810cb7a01acSMauro Carvalho Chehab /* -----------------------------------------------------------------------------
811cb7a01acSMauro Carvalho Chehab  * V4L2 subdev core operations
812cb7a01acSMauro Carvalho Chehab  */
813cb7a01acSMauro Carvalho Chehab 
814cb7a01acSMauro Carvalho Chehab static int mt9p031_set_power(struct v4l2_subdev *subdev, int on)
815cb7a01acSMauro Carvalho Chehab {
816cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
817cb7a01acSMauro Carvalho Chehab 	int ret = 0;
818cb7a01acSMauro Carvalho Chehab 
819cb7a01acSMauro Carvalho Chehab 	mutex_lock(&mt9p031->power_lock);
820cb7a01acSMauro Carvalho Chehab 
821cb7a01acSMauro Carvalho Chehab 	/* If the power count is modified from 0 to != 0 or from != 0 to 0,
822cb7a01acSMauro Carvalho Chehab 	 * update the power state.
823cb7a01acSMauro Carvalho Chehab 	 */
824cb7a01acSMauro Carvalho Chehab 	if (mt9p031->power_count == !on) {
825cb7a01acSMauro Carvalho Chehab 		ret = __mt9p031_set_power(mt9p031, !!on);
826cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
827cb7a01acSMauro Carvalho Chehab 			goto out;
828cb7a01acSMauro Carvalho Chehab 	}
829cb7a01acSMauro Carvalho Chehab 
830cb7a01acSMauro Carvalho Chehab 	/* Update the power count. */
831cb7a01acSMauro Carvalho Chehab 	mt9p031->power_count += on ? 1 : -1;
832cb7a01acSMauro Carvalho Chehab 	WARN_ON(mt9p031->power_count < 0);
833cb7a01acSMauro Carvalho Chehab 
834cb7a01acSMauro Carvalho Chehab out:
835cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&mt9p031->power_lock);
836cb7a01acSMauro Carvalho Chehab 	return ret;
837cb7a01acSMauro Carvalho Chehab }
838cb7a01acSMauro Carvalho Chehab 
839cb7a01acSMauro Carvalho Chehab /* -----------------------------------------------------------------------------
840cb7a01acSMauro Carvalho Chehab  * V4L2 subdev internal operations
841cb7a01acSMauro Carvalho Chehab  */
842cb7a01acSMauro Carvalho Chehab 
843cb7a01acSMauro Carvalho Chehab static int mt9p031_registered(struct v4l2_subdev *subdev)
844cb7a01acSMauro Carvalho Chehab {
845cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(subdev);
846cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
847cb7a01acSMauro Carvalho Chehab 	s32 data;
848cb7a01acSMauro Carvalho Chehab 	int ret;
849cb7a01acSMauro Carvalho Chehab 
850cb7a01acSMauro Carvalho Chehab 	ret = mt9p031_power_on(mt9p031);
851cb7a01acSMauro Carvalho Chehab 	if (ret < 0) {
852cb7a01acSMauro Carvalho Chehab 		dev_err(&client->dev, "MT9P031 power up failed\n");
853cb7a01acSMauro Carvalho Chehab 		return ret;
854cb7a01acSMauro Carvalho Chehab 	}
855cb7a01acSMauro Carvalho Chehab 
856cb7a01acSMauro Carvalho Chehab 	/* Read out the chip version register */
857cb7a01acSMauro Carvalho Chehab 	data = mt9p031_read(client, MT9P031_CHIP_VERSION);
858bbcc9fa0SGuennadi Liakhovetski 	mt9p031_power_off(mt9p031);
859bbcc9fa0SGuennadi Liakhovetski 
860cb7a01acSMauro Carvalho Chehab 	if (data != MT9P031_CHIP_VERSION_VALUE) {
861cb7a01acSMauro Carvalho Chehab 		dev_err(&client->dev, "MT9P031 not detected, wrong version "
862cb7a01acSMauro Carvalho Chehab 			"0x%04x\n", data);
863cb7a01acSMauro Carvalho Chehab 		return -ENODEV;
864cb7a01acSMauro Carvalho Chehab 	}
865cb7a01acSMauro Carvalho Chehab 
866cb7a01acSMauro Carvalho Chehab 	dev_info(&client->dev, "MT9P031 detected at address 0x%02x\n",
867cb7a01acSMauro Carvalho Chehab 		 client->addr);
868cb7a01acSMauro Carvalho Chehab 
869bbcc9fa0SGuennadi Liakhovetski 	return 0;
870cb7a01acSMauro Carvalho Chehab }
871cb7a01acSMauro Carvalho Chehab 
872cb7a01acSMauro Carvalho Chehab static int mt9p031_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
873cb7a01acSMauro Carvalho Chehab {
874cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
875cb7a01acSMauro Carvalho Chehab 	struct v4l2_mbus_framefmt *format;
876cb7a01acSMauro Carvalho Chehab 	struct v4l2_rect *crop;
877cb7a01acSMauro Carvalho Chehab 
878cb7a01acSMauro Carvalho Chehab 	crop = v4l2_subdev_get_try_crop(fh, 0);
879cb7a01acSMauro Carvalho Chehab 	crop->left = MT9P031_COLUMN_START_DEF;
880cb7a01acSMauro Carvalho Chehab 	crop->top = MT9P031_ROW_START_DEF;
881cb7a01acSMauro Carvalho Chehab 	crop->width = MT9P031_WINDOW_WIDTH_DEF;
882cb7a01acSMauro Carvalho Chehab 	crop->height = MT9P031_WINDOW_HEIGHT_DEF;
883cb7a01acSMauro Carvalho Chehab 
884cb7a01acSMauro Carvalho Chehab 	format = v4l2_subdev_get_try_format(fh, 0);
885cb7a01acSMauro Carvalho Chehab 
886cb7a01acSMauro Carvalho Chehab 	if (mt9p031->model == MT9P031_MODEL_MONOCHROME)
887cb7a01acSMauro Carvalho Chehab 		format->code = V4L2_MBUS_FMT_Y12_1X12;
888cb7a01acSMauro Carvalho Chehab 	else
889cb7a01acSMauro Carvalho Chehab 		format->code = V4L2_MBUS_FMT_SGRBG12_1X12;
890cb7a01acSMauro Carvalho Chehab 
891cb7a01acSMauro Carvalho Chehab 	format->width = MT9P031_WINDOW_WIDTH_DEF;
892cb7a01acSMauro Carvalho Chehab 	format->height = MT9P031_WINDOW_HEIGHT_DEF;
893cb7a01acSMauro Carvalho Chehab 	format->field = V4L2_FIELD_NONE;
894cb7a01acSMauro Carvalho Chehab 	format->colorspace = V4L2_COLORSPACE_SRGB;
895cb7a01acSMauro Carvalho Chehab 
896cb7a01acSMauro Carvalho Chehab 	return mt9p031_set_power(subdev, 1);
897cb7a01acSMauro Carvalho Chehab }
898cb7a01acSMauro Carvalho Chehab 
899cb7a01acSMauro Carvalho Chehab static int mt9p031_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
900cb7a01acSMauro Carvalho Chehab {
901cb7a01acSMauro Carvalho Chehab 	return mt9p031_set_power(subdev, 0);
902cb7a01acSMauro Carvalho Chehab }
903cb7a01acSMauro Carvalho Chehab 
904cb7a01acSMauro Carvalho Chehab static struct v4l2_subdev_core_ops mt9p031_subdev_core_ops = {
905cb7a01acSMauro Carvalho Chehab 	.s_power        = mt9p031_set_power,
906cb7a01acSMauro Carvalho Chehab };
907cb7a01acSMauro Carvalho Chehab 
908cb7a01acSMauro Carvalho Chehab static struct v4l2_subdev_video_ops mt9p031_subdev_video_ops = {
909cb7a01acSMauro Carvalho Chehab 	.s_stream       = mt9p031_s_stream,
910cb7a01acSMauro Carvalho Chehab };
911cb7a01acSMauro Carvalho Chehab 
912cb7a01acSMauro Carvalho Chehab static struct v4l2_subdev_pad_ops mt9p031_subdev_pad_ops = {
913cb7a01acSMauro Carvalho Chehab 	.enum_mbus_code = mt9p031_enum_mbus_code,
914cb7a01acSMauro Carvalho Chehab 	.enum_frame_size = mt9p031_enum_frame_size,
915cb7a01acSMauro Carvalho Chehab 	.get_fmt = mt9p031_get_format,
916cb7a01acSMauro Carvalho Chehab 	.set_fmt = mt9p031_set_format,
917cb7a01acSMauro Carvalho Chehab 	.get_crop = mt9p031_get_crop,
918cb7a01acSMauro Carvalho Chehab 	.set_crop = mt9p031_set_crop,
919cb7a01acSMauro Carvalho Chehab };
920cb7a01acSMauro Carvalho Chehab 
921cb7a01acSMauro Carvalho Chehab static struct v4l2_subdev_ops mt9p031_subdev_ops = {
922cb7a01acSMauro Carvalho Chehab 	.core   = &mt9p031_subdev_core_ops,
923cb7a01acSMauro Carvalho Chehab 	.video  = &mt9p031_subdev_video_ops,
924cb7a01acSMauro Carvalho Chehab 	.pad    = &mt9p031_subdev_pad_ops,
925cb7a01acSMauro Carvalho Chehab };
926cb7a01acSMauro Carvalho Chehab 
927cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_internal_ops mt9p031_subdev_internal_ops = {
928cb7a01acSMauro Carvalho Chehab 	.registered = mt9p031_registered,
929cb7a01acSMauro Carvalho Chehab 	.open = mt9p031_open,
930cb7a01acSMauro Carvalho Chehab 	.close = mt9p031_close,
931cb7a01acSMauro Carvalho Chehab };
932cb7a01acSMauro Carvalho Chehab 
933cb7a01acSMauro Carvalho Chehab /* -----------------------------------------------------------------------------
934cb7a01acSMauro Carvalho Chehab  * Driver initialization and probing
935cb7a01acSMauro Carvalho Chehab  */
936cb7a01acSMauro Carvalho Chehab 
9378d4da37cSLad, Prabhakar static struct mt9p031_platform_data *
9388d4da37cSLad, Prabhakar mt9p031_get_pdata(struct i2c_client *client)
9398d4da37cSLad, Prabhakar {
9408d4da37cSLad, Prabhakar 	struct mt9p031_platform_data *pdata;
9418d4da37cSLad, Prabhakar 	struct device_node *np;
9428d4da37cSLad, Prabhakar 
9438d4da37cSLad, Prabhakar 	if (!IS_ENABLED(CONFIG_OF) || !client->dev.of_node)
9448d4da37cSLad, Prabhakar 		return client->dev.platform_data;
9458d4da37cSLad, Prabhakar 
9468d4da37cSLad, Prabhakar 	np = v4l2_of_get_next_endpoint(client->dev.of_node, NULL);
9478d4da37cSLad, Prabhakar 	if (!np)
9488d4da37cSLad, Prabhakar 		return NULL;
9498d4da37cSLad, Prabhakar 
9508d4da37cSLad, Prabhakar 	pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
9518d4da37cSLad, Prabhakar 	if (!pdata)
9528d4da37cSLad, Prabhakar 		goto done;
9538d4da37cSLad, Prabhakar 
9548d4da37cSLad, Prabhakar 	pdata->reset = of_get_named_gpio(client->dev.of_node, "reset-gpios", 0);
9558d4da37cSLad, Prabhakar 	of_property_read_u32(np, "input-clock-frequency", &pdata->ext_freq);
9568d4da37cSLad, Prabhakar 	of_property_read_u32(np, "pixel-clock-frequency", &pdata->target_freq);
9578d4da37cSLad, Prabhakar 
9588d4da37cSLad, Prabhakar done:
9598d4da37cSLad, Prabhakar 	of_node_put(np);
9608d4da37cSLad, Prabhakar 	return pdata;
9618d4da37cSLad, Prabhakar }
9628d4da37cSLad, Prabhakar 
963cb7a01acSMauro Carvalho Chehab static int mt9p031_probe(struct i2c_client *client,
964cb7a01acSMauro Carvalho Chehab 			 const struct i2c_device_id *did)
965cb7a01acSMauro Carvalho Chehab {
9668d4da37cSLad, Prabhakar 	struct mt9p031_platform_data *pdata = mt9p031_get_pdata(client);
967cb7a01acSMauro Carvalho Chehab 	struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
968cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031;
969cb7a01acSMauro Carvalho Chehab 	unsigned int i;
970cb7a01acSMauro Carvalho Chehab 	int ret;
971cb7a01acSMauro Carvalho Chehab 
972cb7a01acSMauro Carvalho Chehab 	if (pdata == NULL) {
973cb7a01acSMauro Carvalho Chehab 		dev_err(&client->dev, "No platform data\n");
974cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
975cb7a01acSMauro Carvalho Chehab 	}
976cb7a01acSMauro Carvalho Chehab 
977cb7a01acSMauro Carvalho Chehab 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
978cb7a01acSMauro Carvalho Chehab 		dev_warn(&client->dev,
979cb7a01acSMauro Carvalho Chehab 			"I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
980cb7a01acSMauro Carvalho Chehab 		return -EIO;
981cb7a01acSMauro Carvalho Chehab 	}
982cb7a01acSMauro Carvalho Chehab 
98337b9f211SLaurent Pinchart 	mt9p031 = devm_kzalloc(&client->dev, sizeof(*mt9p031), GFP_KERNEL);
984cb7a01acSMauro Carvalho Chehab 	if (mt9p031 == NULL)
985cb7a01acSMauro Carvalho Chehab 		return -ENOMEM;
986cb7a01acSMauro Carvalho Chehab 
987cb7a01acSMauro Carvalho Chehab 	mt9p031->pdata = pdata;
988cb7a01acSMauro Carvalho Chehab 	mt9p031->output_control	= MT9P031_OUTPUT_CONTROL_DEF;
989cb7a01acSMauro Carvalho Chehab 	mt9p031->mode2 = MT9P031_READ_MODE_2_ROW_BLC;
990cb7a01acSMauro Carvalho Chehab 	mt9p031->model = did->driver_data;
991cb7a01acSMauro Carvalho Chehab 	mt9p031->reset = -1;
992cb7a01acSMauro Carvalho Chehab 
9937997196cSLaurent Pinchart 	mt9p031->regulators[0].supply = "vdd";
9947997196cSLaurent Pinchart 	mt9p031->regulators[1].supply = "vdd_io";
9957997196cSLaurent Pinchart 	mt9p031->regulators[2].supply = "vaa";
99697f21276SLaurent Pinchart 
9977997196cSLaurent Pinchart 	ret = devm_regulator_bulk_get(&client->dev, 3, mt9p031->regulators);
9987997196cSLaurent Pinchart 	if (ret < 0) {
99997f21276SLaurent Pinchart 		dev_err(&client->dev, "Unable to get regulators\n");
10007997196cSLaurent Pinchart 		return ret;
100197f21276SLaurent Pinchart 	}
100297f21276SLaurent Pinchart 
1003b28d7017SLad, Prabhakar 	v4l2_ctrl_handler_init(&mt9p031->ctrls, ARRAY_SIZE(mt9p031_ctrls) + 6);
1004cb7a01acSMauro Carvalho Chehab 
1005cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
1006cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_EXPOSURE, MT9P031_SHUTTER_WIDTH_MIN,
1007cb7a01acSMauro Carvalho Chehab 			  MT9P031_SHUTTER_WIDTH_MAX, 1,
1008cb7a01acSMauro Carvalho Chehab 			  MT9P031_SHUTTER_WIDTH_DEF);
1009cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
1010cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_GAIN, MT9P031_GLOBAL_GAIN_MIN,
1011cb7a01acSMauro Carvalho Chehab 			  MT9P031_GLOBAL_GAIN_MAX, 1, MT9P031_GLOBAL_GAIN_DEF);
1012cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
1013cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_HFLIP, 0, 1, 1, 0);
1014cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
1015cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_VFLIP, 0, 1, 1, 0);
1016cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
1017cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_PIXEL_RATE, pdata->target_freq,
1018cb7a01acSMauro Carvalho Chehab 			  pdata->target_freq, 1, pdata->target_freq);
1019b28d7017SLad, Prabhakar 	v4l2_ctrl_new_std_menu_items(&mt9p031->ctrls, &mt9p031_ctrl_ops,
1020b28d7017SLad, Prabhakar 			  V4L2_CID_TEST_PATTERN,
1021b28d7017SLad, Prabhakar 			  ARRAY_SIZE(mt9p031_test_pattern_menu) - 1, 0,
1022b28d7017SLad, Prabhakar 			  0, mt9p031_test_pattern_menu);
1023cb7a01acSMauro Carvalho Chehab 
1024cb7a01acSMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(mt9p031_ctrls); ++i)
1025cb7a01acSMauro Carvalho Chehab 		v4l2_ctrl_new_custom(&mt9p031->ctrls, &mt9p031_ctrls[i], NULL);
1026cb7a01acSMauro Carvalho Chehab 
1027cb7a01acSMauro Carvalho Chehab 	mt9p031->subdev.ctrl_handler = &mt9p031->ctrls;
1028cb7a01acSMauro Carvalho Chehab 
1029cb7a01acSMauro Carvalho Chehab 	if (mt9p031->ctrls.error) {
1030cb7a01acSMauro Carvalho Chehab 		printk(KERN_INFO "%s: control initialization error %d\n",
1031cb7a01acSMauro Carvalho Chehab 		       __func__, mt9p031->ctrls.error);
1032cb7a01acSMauro Carvalho Chehab 		ret = mt9p031->ctrls.error;
1033cb7a01acSMauro Carvalho Chehab 		goto done;
1034cb7a01acSMauro Carvalho Chehab 	}
1035cb7a01acSMauro Carvalho Chehab 
1036cb7a01acSMauro Carvalho Chehab 	mt9p031->blc_auto = v4l2_ctrl_find(&mt9p031->ctrls, V4L2_CID_BLC_AUTO);
1037cb7a01acSMauro Carvalho Chehab 	mt9p031->blc_offset = v4l2_ctrl_find(&mt9p031->ctrls,
1038cb7a01acSMauro Carvalho Chehab 					     V4L2_CID_BLC_DIGITAL_OFFSET);
1039cb7a01acSMauro Carvalho Chehab 
1040cb7a01acSMauro Carvalho Chehab 	mutex_init(&mt9p031->power_lock);
1041cb7a01acSMauro Carvalho Chehab 	v4l2_i2c_subdev_init(&mt9p031->subdev, client, &mt9p031_subdev_ops);
1042cb7a01acSMauro Carvalho Chehab 	mt9p031->subdev.internal_ops = &mt9p031_subdev_internal_ops;
1043cb7a01acSMauro Carvalho Chehab 
1044cb7a01acSMauro Carvalho Chehab 	mt9p031->pad.flags = MEDIA_PAD_FL_SOURCE;
1045cb7a01acSMauro Carvalho Chehab 	ret = media_entity_init(&mt9p031->subdev.entity, 1, &mt9p031->pad, 0);
1046cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
1047cb7a01acSMauro Carvalho Chehab 		goto done;
1048cb7a01acSMauro Carvalho Chehab 
1049cb7a01acSMauro Carvalho Chehab 	mt9p031->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
1050cb7a01acSMauro Carvalho Chehab 
1051cb7a01acSMauro Carvalho Chehab 	mt9p031->crop.width = MT9P031_WINDOW_WIDTH_DEF;
1052cb7a01acSMauro Carvalho Chehab 	mt9p031->crop.height = MT9P031_WINDOW_HEIGHT_DEF;
1053cb7a01acSMauro Carvalho Chehab 	mt9p031->crop.left = MT9P031_COLUMN_START_DEF;
1054cb7a01acSMauro Carvalho Chehab 	mt9p031->crop.top = MT9P031_ROW_START_DEF;
1055cb7a01acSMauro Carvalho Chehab 
1056cb7a01acSMauro Carvalho Chehab 	if (mt9p031->model == MT9P031_MODEL_MONOCHROME)
1057cb7a01acSMauro Carvalho Chehab 		mt9p031->format.code = V4L2_MBUS_FMT_Y12_1X12;
1058cb7a01acSMauro Carvalho Chehab 	else
1059cb7a01acSMauro Carvalho Chehab 		mt9p031->format.code = V4L2_MBUS_FMT_SGRBG12_1X12;
1060cb7a01acSMauro Carvalho Chehab 
1061cb7a01acSMauro Carvalho Chehab 	mt9p031->format.width = MT9P031_WINDOW_WIDTH_DEF;
1062cb7a01acSMauro Carvalho Chehab 	mt9p031->format.height = MT9P031_WINDOW_HEIGHT_DEF;
1063cb7a01acSMauro Carvalho Chehab 	mt9p031->format.field = V4L2_FIELD_NONE;
1064cb7a01acSMauro Carvalho Chehab 	mt9p031->format.colorspace = V4L2_COLORSPACE_SRGB;
1065cb7a01acSMauro Carvalho Chehab 
10662660a22bSLaurent Pinchart 	if (gpio_is_valid(pdata->reset)) {
106737b9f211SLaurent Pinchart 		ret = devm_gpio_request_one(&client->dev, pdata->reset,
106837b9f211SLaurent Pinchart 					    GPIOF_OUT_INIT_LOW, "mt9p031_rst");
1069cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
1070cb7a01acSMauro Carvalho Chehab 			goto done;
1071cb7a01acSMauro Carvalho Chehab 
1072cb7a01acSMauro Carvalho Chehab 		mt9p031->reset = pdata->reset;
1073cb7a01acSMauro Carvalho Chehab 	}
1074cb7a01acSMauro Carvalho Chehab 
1075d6749258SLaurent Pinchart 	ret = mt9p031_clk_setup(mt9p031);
1076cb7a01acSMauro Carvalho Chehab 
1077cb7a01acSMauro Carvalho Chehab done:
1078cb7a01acSMauro Carvalho Chehab 	if (ret < 0) {
1079cb7a01acSMauro Carvalho Chehab 		v4l2_ctrl_handler_free(&mt9p031->ctrls);
1080cb7a01acSMauro Carvalho Chehab 		media_entity_cleanup(&mt9p031->subdev.entity);
1081cb7a01acSMauro Carvalho Chehab 	}
1082cb7a01acSMauro Carvalho Chehab 
1083cb7a01acSMauro Carvalho Chehab 	return ret;
1084cb7a01acSMauro Carvalho Chehab }
1085cb7a01acSMauro Carvalho Chehab 
1086cb7a01acSMauro Carvalho Chehab static int mt9p031_remove(struct i2c_client *client)
1087cb7a01acSMauro Carvalho Chehab {
1088cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
1089cb7a01acSMauro Carvalho Chehab 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
1090cb7a01acSMauro Carvalho Chehab 
1091cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_free(&mt9p031->ctrls);
1092cb7a01acSMauro Carvalho Chehab 	v4l2_device_unregister_subdev(subdev);
1093cb7a01acSMauro Carvalho Chehab 	media_entity_cleanup(&subdev->entity);
1094cb7a01acSMauro Carvalho Chehab 
1095cb7a01acSMauro Carvalho Chehab 	return 0;
1096cb7a01acSMauro Carvalho Chehab }
1097cb7a01acSMauro Carvalho Chehab 
1098cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id mt9p031_id[] = {
1099cb7a01acSMauro Carvalho Chehab 	{ "mt9p031", MT9P031_MODEL_COLOR },
1100cb7a01acSMauro Carvalho Chehab 	{ "mt9p031m", MT9P031_MODEL_MONOCHROME },
1101cb7a01acSMauro Carvalho Chehab 	{ }
1102cb7a01acSMauro Carvalho Chehab };
1103cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, mt9p031_id);
1104cb7a01acSMauro Carvalho Chehab 
11058d4da37cSLad, Prabhakar #if IS_ENABLED(CONFIG_OF)
11068d4da37cSLad, Prabhakar static const struct of_device_id mt9p031_of_match[] = {
11078d4da37cSLad, Prabhakar 	{ .compatible = "aptina,mt9p031", },
11088d4da37cSLad, Prabhakar 	{ .compatible = "aptina,mt9p031m", },
11098d4da37cSLad, Prabhakar 	{ /* sentinel */ },
11108d4da37cSLad, Prabhakar };
11118d4da37cSLad, Prabhakar MODULE_DEVICE_TABLE(of, mt9p031_of_match);
11128d4da37cSLad, Prabhakar #endif
11138d4da37cSLad, Prabhakar 
1114cb7a01acSMauro Carvalho Chehab static struct i2c_driver mt9p031_i2c_driver = {
1115cb7a01acSMauro Carvalho Chehab 	.driver = {
11168d4da37cSLad, Prabhakar 		.of_match_table = of_match_ptr(mt9p031_of_match),
1117cb7a01acSMauro Carvalho Chehab 		.name = "mt9p031",
1118cb7a01acSMauro Carvalho Chehab 	},
1119cb7a01acSMauro Carvalho Chehab 	.probe          = mt9p031_probe,
1120cb7a01acSMauro Carvalho Chehab 	.remove         = mt9p031_remove,
1121cb7a01acSMauro Carvalho Chehab 	.id_table       = mt9p031_id,
1122cb7a01acSMauro Carvalho Chehab };
1123cb7a01acSMauro Carvalho Chehab 
1124cb7a01acSMauro Carvalho Chehab module_i2c_driver(mt9p031_i2c_driver);
1125cb7a01acSMauro Carvalho Chehab 
1126cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("Aptina MT9P031 Camera driver");
1127cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Bastian Hecht <hechtb@gmail.com>");
1128cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL v2");
1129