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