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