1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2cb7a01acSMauro Carvalho Chehab /*
3cb7a01acSMauro Carvalho Chehab * Driver for MT9P031 CMOS Image Sensor from Aptina
4cb7a01acSMauro Carvalho Chehab *
5cb7a01acSMauro Carvalho Chehab * Copyright (C) 2011, Laurent Pinchart <laurent.pinchart@ideasonboard.com>
6cb7a01acSMauro Carvalho Chehab * Copyright (C) 2011, Javier Martin <javier.martin@vista-silicon.com>
7cb7a01acSMauro Carvalho Chehab * Copyright (C) 2011, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
8cb7a01acSMauro Carvalho Chehab *
9cb7a01acSMauro Carvalho Chehab * Based on the MT9V032 driver and Bastian Hecht's code.
10cb7a01acSMauro Carvalho Chehab */
11cb7a01acSMauro Carvalho Chehab
12d6749258SLaurent Pinchart #include <linux/clk.h>
13cb7a01acSMauro Carvalho Chehab #include <linux/delay.h>
14cb7a01acSMauro Carvalho Chehab #include <linux/device.h>
157c3be9f8SLaurent Pinchart #include <linux/gpio/consumer.h>
16cb7a01acSMauro Carvalho Chehab #include <linux/i2c.h>
17cb7a01acSMauro Carvalho Chehab #include <linux/log2.h>
188d4da37cSLad, Prabhakar #include <linux/module.h>
1964695905SSachin Kamat #include <linux/of.h>
20fd9fdb78SPhilipp Zabel #include <linux/of_graph.h>
21cb7a01acSMauro Carvalho Chehab #include <linux/pm.h>
2297f21276SLaurent Pinchart #include <linux/regulator/consumer.h>
23cb7a01acSMauro Carvalho Chehab #include <linux/slab.h>
24cb7a01acSMauro Carvalho Chehab #include <linux/videodev2.h>
25cb7a01acSMauro Carvalho Chehab
26b5dcee22SMauro Carvalho Chehab #include <media/i2c/mt9p031.h>
279012d088SLad, Prabhakar #include <media/v4l2-async.h>
28cb7a01acSMauro Carvalho Chehab #include <media/v4l2-ctrls.h>
29cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h>
30ae47ee5fSChristian Hemp #include <media/v4l2-fwnode.h>
31cb7a01acSMauro Carvalho Chehab #include <media/v4l2-subdev.h>
32cb7a01acSMauro Carvalho Chehab
33cb7a01acSMauro Carvalho Chehab #include "aptina-pll.h"
34cb7a01acSMauro Carvalho Chehab
35cb7a01acSMauro Carvalho Chehab #define MT9P031_PIXEL_ARRAY_WIDTH 2752
36cb7a01acSMauro Carvalho Chehab #define MT9P031_PIXEL_ARRAY_HEIGHT 2004
37cb7a01acSMauro Carvalho Chehab
38cb7a01acSMauro Carvalho Chehab #define MT9P031_CHIP_VERSION 0x00
39cb7a01acSMauro Carvalho Chehab #define MT9P031_CHIP_VERSION_VALUE 0x1801
40cb7a01acSMauro Carvalho Chehab #define MT9P031_ROW_START 0x01
41cb7a01acSMauro Carvalho Chehab #define MT9P031_ROW_START_MIN 0
42cb7a01acSMauro Carvalho Chehab #define MT9P031_ROW_START_MAX 2004
43cb7a01acSMauro Carvalho Chehab #define MT9P031_ROW_START_DEF 54
44cb7a01acSMauro Carvalho Chehab #define MT9P031_COLUMN_START 0x02
45cb7a01acSMauro Carvalho Chehab #define MT9P031_COLUMN_START_MIN 0
46cb7a01acSMauro Carvalho Chehab #define MT9P031_COLUMN_START_MAX 2750
47cb7a01acSMauro Carvalho Chehab #define MT9P031_COLUMN_START_DEF 16
48cb7a01acSMauro Carvalho Chehab #define MT9P031_WINDOW_HEIGHT 0x03
49cb7a01acSMauro Carvalho Chehab #define MT9P031_WINDOW_HEIGHT_MIN 2
50cb7a01acSMauro Carvalho Chehab #define MT9P031_WINDOW_HEIGHT_MAX 2006
51cb7a01acSMauro Carvalho Chehab #define MT9P031_WINDOW_HEIGHT_DEF 1944
52cb7a01acSMauro Carvalho Chehab #define MT9P031_WINDOW_WIDTH 0x04
53cb7a01acSMauro Carvalho Chehab #define MT9P031_WINDOW_WIDTH_MIN 2
54cb7a01acSMauro Carvalho Chehab #define MT9P031_WINDOW_WIDTH_MAX 2752
55cb7a01acSMauro Carvalho Chehab #define MT9P031_WINDOW_WIDTH_DEF 2592
56cb7a01acSMauro Carvalho Chehab #define MT9P031_HORIZONTAL_BLANK 0x05
57cb7a01acSMauro Carvalho Chehab #define MT9P031_HORIZONTAL_BLANK_MIN 0
58cb7a01acSMauro Carvalho Chehab #define MT9P031_HORIZONTAL_BLANK_MAX 4095
59cb7a01acSMauro Carvalho Chehab #define MT9P031_VERTICAL_BLANK 0x06
605266c98bSLaurent Pinchart #define MT9P031_VERTICAL_BLANK_MIN 1
615266c98bSLaurent Pinchart #define MT9P031_VERTICAL_BLANK_MAX 4096
625266c98bSLaurent Pinchart #define MT9P031_VERTICAL_BLANK_DEF 26
63cb7a01acSMauro Carvalho Chehab #define MT9P031_OUTPUT_CONTROL 0x07
64cb7a01acSMauro Carvalho Chehab #define MT9P031_OUTPUT_CONTROL_CEN 2
65cb7a01acSMauro Carvalho Chehab #define MT9P031_OUTPUT_CONTROL_SYN 1
66cb7a01acSMauro Carvalho Chehab #define MT9P031_OUTPUT_CONTROL_DEF 0x1f82
67cb7a01acSMauro Carvalho Chehab #define MT9P031_SHUTTER_WIDTH_UPPER 0x08
68cb7a01acSMauro Carvalho Chehab #define MT9P031_SHUTTER_WIDTH_LOWER 0x09
69cb7a01acSMauro Carvalho Chehab #define MT9P031_SHUTTER_WIDTH_MIN 1
70cb7a01acSMauro Carvalho Chehab #define MT9P031_SHUTTER_WIDTH_MAX 1048575
71cb7a01acSMauro Carvalho Chehab #define MT9P031_SHUTTER_WIDTH_DEF 1943
72cb7a01acSMauro Carvalho Chehab #define MT9P031_PLL_CONTROL 0x10
73cb7a01acSMauro Carvalho Chehab #define MT9P031_PLL_CONTROL_PWROFF 0x0050
74cb7a01acSMauro Carvalho Chehab #define MT9P031_PLL_CONTROL_PWRON 0x0051
75cb7a01acSMauro Carvalho Chehab #define MT9P031_PLL_CONTROL_USEPLL 0x0052
76cb7a01acSMauro Carvalho Chehab #define MT9P031_PLL_CONFIG_1 0x11
77cb7a01acSMauro Carvalho Chehab #define MT9P031_PLL_CONFIG_2 0x12
78cb7a01acSMauro Carvalho Chehab #define MT9P031_PIXEL_CLOCK_CONTROL 0x0a
790a0e78d1SStefan Riedmueller #define MT9P031_PIXEL_CLOCK_INVERT BIT(15)
80a970449eSLaurent Pinchart #define MT9P031_PIXEL_CLOCK_SHIFT(n) ((n) << 8)
81a970449eSLaurent Pinchart #define MT9P031_PIXEL_CLOCK_DIVIDE(n) ((n) << 0)
820961ba6dSDirk Bender #define MT9P031_RESTART 0x0b
830a0e78d1SStefan Riedmueller #define MT9P031_FRAME_PAUSE_RESTART BIT(1)
840a0e78d1SStefan Riedmueller #define MT9P031_FRAME_RESTART BIT(0)
85cb7a01acSMauro Carvalho Chehab #define MT9P031_SHUTTER_DELAY 0x0c
86cb7a01acSMauro Carvalho Chehab #define MT9P031_RST 0x0d
870a0e78d1SStefan Riedmueller #define MT9P031_RST_ENABLE BIT(0)
88cb7a01acSMauro Carvalho Chehab #define MT9P031_READ_MODE_1 0x1e
89cb7a01acSMauro Carvalho Chehab #define MT9P031_READ_MODE_2 0x20
900a0e78d1SStefan Riedmueller #define MT9P031_READ_MODE_2_ROW_MIR BIT(15)
910a0e78d1SStefan Riedmueller #define MT9P031_READ_MODE_2_COL_MIR BIT(14)
920a0e78d1SStefan Riedmueller #define MT9P031_READ_MODE_2_ROW_BLC BIT(6)
93cb7a01acSMauro Carvalho Chehab #define MT9P031_ROW_ADDRESS_MODE 0x22
94cb7a01acSMauro Carvalho Chehab #define MT9P031_COLUMN_ADDRESS_MODE 0x23
95cb7a01acSMauro Carvalho Chehab #define MT9P031_GLOBAL_GAIN 0x35
96cb7a01acSMauro Carvalho Chehab #define MT9P031_GLOBAL_GAIN_MIN 8
97cb7a01acSMauro Carvalho Chehab #define MT9P031_GLOBAL_GAIN_MAX 1024
98cb7a01acSMauro Carvalho Chehab #define MT9P031_GLOBAL_GAIN_DEF 8
990a0e78d1SStefan Riedmueller #define MT9P031_GLOBAL_GAIN_MULT BIT(6)
100cb7a01acSMauro Carvalho Chehab #define MT9P031_ROW_BLACK_TARGET 0x49
101cb7a01acSMauro Carvalho Chehab #define MT9P031_ROW_BLACK_DEF_OFFSET 0x4b
102cb7a01acSMauro Carvalho Chehab #define MT9P031_GREEN1_OFFSET 0x60
103cb7a01acSMauro Carvalho Chehab #define MT9P031_GREEN2_OFFSET 0x61
104cb7a01acSMauro Carvalho Chehab #define MT9P031_BLACK_LEVEL_CALIBRATION 0x62
1050a0e78d1SStefan Riedmueller #define MT9P031_BLC_MANUAL_BLC BIT(0)
106cb7a01acSMauro Carvalho Chehab #define MT9P031_RED_OFFSET 0x63
107cb7a01acSMauro Carvalho Chehab #define MT9P031_BLUE_OFFSET 0x64
108cb7a01acSMauro Carvalho Chehab #define MT9P031_TEST_PATTERN 0xa0
109cb7a01acSMauro Carvalho Chehab #define MT9P031_TEST_PATTERN_SHIFT 3
1100a0e78d1SStefan Riedmueller #define MT9P031_TEST_PATTERN_ENABLE BIT(0)
111cb7a01acSMauro Carvalho Chehab #define MT9P031_TEST_PATTERN_GREEN 0xa1
112cb7a01acSMauro Carvalho Chehab #define MT9P031_TEST_PATTERN_RED 0xa2
113cb7a01acSMauro Carvalho Chehab #define MT9P031_TEST_PATTERN_BLUE 0xa3
114cb7a01acSMauro Carvalho Chehab
115cb7a01acSMauro Carvalho Chehab enum mt9p031_model {
116cb7a01acSMauro Carvalho Chehab MT9P031_MODEL_COLOR,
117cb7a01acSMauro Carvalho Chehab MT9P031_MODEL_MONOCHROME,
118cb7a01acSMauro Carvalho Chehab };
119cb7a01acSMauro Carvalho Chehab
120cb7a01acSMauro Carvalho Chehab struct mt9p031 {
121cb7a01acSMauro Carvalho Chehab struct v4l2_subdev subdev;
122cb7a01acSMauro Carvalho Chehab struct media_pad pad;
123cb7a01acSMauro Carvalho Chehab struct v4l2_rect crop; /* Sensor window */
124cb7a01acSMauro Carvalho Chehab struct v4l2_mbus_framefmt format;
125cb7a01acSMauro Carvalho Chehab struct mt9p031_platform_data *pdata;
126cb7a01acSMauro Carvalho Chehab struct mutex power_lock; /* lock to protect power_count */
127cb7a01acSMauro Carvalho Chehab int power_count;
128cb7a01acSMauro Carvalho Chehab
129d6749258SLaurent Pinchart struct clk *clk;
1307997196cSLaurent Pinchart struct regulator_bulk_data regulators[3];
13197f21276SLaurent Pinchart
132cb7a01acSMauro Carvalho Chehab enum mt9p031_model model;
133cb7a01acSMauro Carvalho Chehab struct aptina_pll pll;
134a970449eSLaurent Pinchart unsigned int clk_div;
135a970449eSLaurent Pinchart bool use_pll;
1367c3be9f8SLaurent Pinchart struct gpio_desc *reset;
137cb7a01acSMauro Carvalho Chehab
138cb7a01acSMauro Carvalho Chehab struct v4l2_ctrl_handler ctrls;
139cb7a01acSMauro Carvalho Chehab struct v4l2_ctrl *blc_auto;
140cb7a01acSMauro Carvalho Chehab struct v4l2_ctrl *blc_offset;
141cb7a01acSMauro Carvalho Chehab
142cb7a01acSMauro Carvalho Chehab /* Registers cache */
143cb7a01acSMauro Carvalho Chehab u16 output_control;
144cb7a01acSMauro Carvalho Chehab u16 mode2;
145cb7a01acSMauro Carvalho Chehab };
146cb7a01acSMauro Carvalho Chehab
to_mt9p031(struct v4l2_subdev * sd)147cb7a01acSMauro Carvalho Chehab static struct mt9p031 *to_mt9p031(struct v4l2_subdev *sd)
148cb7a01acSMauro Carvalho Chehab {
149cb7a01acSMauro Carvalho Chehab return container_of(sd, struct mt9p031, subdev);
150cb7a01acSMauro Carvalho Chehab }
151cb7a01acSMauro Carvalho Chehab
mt9p031_read(struct i2c_client * client,u8 reg)152cb7a01acSMauro Carvalho Chehab static int mt9p031_read(struct i2c_client *client, u8 reg)
153cb7a01acSMauro Carvalho Chehab {
154cb7a01acSMauro Carvalho Chehab return i2c_smbus_read_word_swapped(client, reg);
155cb7a01acSMauro Carvalho Chehab }
156cb7a01acSMauro Carvalho Chehab
mt9p031_write(struct i2c_client * client,u8 reg,u16 data)157cb7a01acSMauro Carvalho Chehab static int mt9p031_write(struct i2c_client *client, u8 reg, u16 data)
158cb7a01acSMauro Carvalho Chehab {
159cb7a01acSMauro Carvalho Chehab return i2c_smbus_write_word_swapped(client, reg, data);
160cb7a01acSMauro Carvalho Chehab }
161cb7a01acSMauro Carvalho Chehab
mt9p031_set_output_control(struct mt9p031 * mt9p031,u16 clear,u16 set)162cb7a01acSMauro Carvalho Chehab static int mt9p031_set_output_control(struct mt9p031 *mt9p031, u16 clear,
163cb7a01acSMauro Carvalho Chehab u16 set)
164cb7a01acSMauro Carvalho Chehab {
165cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
166cb7a01acSMauro Carvalho Chehab u16 value = (mt9p031->output_control & ~clear) | set;
167cb7a01acSMauro Carvalho Chehab int ret;
168cb7a01acSMauro Carvalho Chehab
169cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_OUTPUT_CONTROL, value);
170cb7a01acSMauro Carvalho Chehab if (ret < 0)
171cb7a01acSMauro Carvalho Chehab return ret;
172cb7a01acSMauro Carvalho Chehab
173cb7a01acSMauro Carvalho Chehab mt9p031->output_control = value;
174cb7a01acSMauro Carvalho Chehab return 0;
175cb7a01acSMauro Carvalho Chehab }
176cb7a01acSMauro Carvalho Chehab
mt9p031_set_mode2(struct mt9p031 * mt9p031,u16 clear,u16 set)177cb7a01acSMauro Carvalho Chehab static int mt9p031_set_mode2(struct mt9p031 *mt9p031, u16 clear, u16 set)
178cb7a01acSMauro Carvalho Chehab {
179cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
180cb7a01acSMauro Carvalho Chehab u16 value = (mt9p031->mode2 & ~clear) | set;
181cb7a01acSMauro Carvalho Chehab int ret;
182cb7a01acSMauro Carvalho Chehab
183cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_READ_MODE_2, value);
184cb7a01acSMauro Carvalho Chehab if (ret < 0)
185cb7a01acSMauro Carvalho Chehab return ret;
186cb7a01acSMauro Carvalho Chehab
187cb7a01acSMauro Carvalho Chehab mt9p031->mode2 = value;
188cb7a01acSMauro Carvalho Chehab return 0;
189cb7a01acSMauro Carvalho Chehab }
190cb7a01acSMauro Carvalho Chehab
mt9p031_reset(struct mt9p031 * mt9p031)191cb7a01acSMauro Carvalho Chehab static int mt9p031_reset(struct mt9p031 *mt9p031)
192cb7a01acSMauro Carvalho Chehab {
193cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
194cb7a01acSMauro Carvalho Chehab int ret;
195cb7a01acSMauro Carvalho Chehab
196cb7a01acSMauro Carvalho Chehab /* Disable chip output, synchronous option update */
197cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_RST, MT9P031_RST_ENABLE);
198cb7a01acSMauro Carvalho Chehab if (ret < 0)
199cb7a01acSMauro Carvalho Chehab return ret;
2000a0e78d1SStefan Riedmueller ret = mt9p031_write(client, MT9P031_RST, 0);
201cb7a01acSMauro Carvalho Chehab if (ret < 0)
202cb7a01acSMauro Carvalho Chehab return ret;
203cb7a01acSMauro Carvalho Chehab
204a970449eSLaurent Pinchart ret = mt9p031_write(client, MT9P031_PIXEL_CLOCK_CONTROL,
205a970449eSLaurent Pinchart MT9P031_PIXEL_CLOCK_DIVIDE(mt9p031->clk_div));
206a970449eSLaurent Pinchart if (ret < 0)
207a970449eSLaurent Pinchart return ret;
208a970449eSLaurent Pinchart
209cb7a01acSMauro Carvalho Chehab return mt9p031_set_output_control(mt9p031, MT9P031_OUTPUT_CONTROL_CEN,
210cb7a01acSMauro Carvalho Chehab 0);
211cb7a01acSMauro Carvalho Chehab }
212cb7a01acSMauro Carvalho Chehab
mt9p031_clk_setup(struct mt9p031 * mt9p031)213d6749258SLaurent Pinchart static int mt9p031_clk_setup(struct mt9p031 *mt9p031)
214cb7a01acSMauro Carvalho Chehab {
215cb7a01acSMauro Carvalho Chehab static const struct aptina_pll_limits limits = {
216cb7a01acSMauro Carvalho Chehab .ext_clock_min = 6000000,
217cb7a01acSMauro Carvalho Chehab .ext_clock_max = 27000000,
218cb7a01acSMauro Carvalho Chehab .int_clock_min = 2000000,
219cb7a01acSMauro Carvalho Chehab .int_clock_max = 13500000,
220cb7a01acSMauro Carvalho Chehab .out_clock_min = 180000000,
221cb7a01acSMauro Carvalho Chehab .out_clock_max = 360000000,
222cb7a01acSMauro Carvalho Chehab .pix_clock_max = 96000000,
223cb7a01acSMauro Carvalho Chehab .n_min = 1,
224cb7a01acSMauro Carvalho Chehab .n_max = 64,
225cb7a01acSMauro Carvalho Chehab .m_min = 16,
226cb7a01acSMauro Carvalho Chehab .m_max = 255,
227cb7a01acSMauro Carvalho Chehab .p1_min = 1,
228cb7a01acSMauro Carvalho Chehab .p1_max = 128,
229cb7a01acSMauro Carvalho Chehab };
230cb7a01acSMauro Carvalho Chehab
231cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
232cb7a01acSMauro Carvalho Chehab struct mt9p031_platform_data *pdata = mt9p031->pdata;
233b9c18096SEnrico Scholz unsigned long ext_freq;
234ee2d16d7SLad, Prabhakar int ret;
235cb7a01acSMauro Carvalho Chehab
236d6749258SLaurent Pinchart mt9p031->clk = devm_clk_get(&client->dev, NULL);
237d6749258SLaurent Pinchart if (IS_ERR(mt9p031->clk))
238d6749258SLaurent Pinchart return PTR_ERR(mt9p031->clk);
239d6749258SLaurent Pinchart
240ee2d16d7SLad, Prabhakar ret = clk_set_rate(mt9p031->clk, pdata->ext_freq);
241ee2d16d7SLad, Prabhakar if (ret < 0)
242ee2d16d7SLad, Prabhakar return ret;
243d6749258SLaurent Pinchart
244b9c18096SEnrico Scholz ext_freq = clk_get_rate(mt9p031->clk);
245b9c18096SEnrico Scholz
246a970449eSLaurent Pinchart /* If the external clock frequency is out of bounds for the PLL use the
247a970449eSLaurent Pinchart * pixel clock divider only and disable the PLL.
248a970449eSLaurent Pinchart */
249b9c18096SEnrico Scholz if (ext_freq > limits.ext_clock_max) {
250a970449eSLaurent Pinchart unsigned int div;
251a970449eSLaurent Pinchart
252b9c18096SEnrico Scholz div = DIV_ROUND_UP(ext_freq, pdata->target_freq);
253a970449eSLaurent Pinchart div = roundup_pow_of_two(div) / 2;
254a970449eSLaurent Pinchart
255198b47ddSEnrico Scholz mt9p031->clk_div = min_t(unsigned int, div, 64);
256a970449eSLaurent Pinchart mt9p031->use_pll = false;
257a970449eSLaurent Pinchart
258a970449eSLaurent Pinchart return 0;
259a970449eSLaurent Pinchart }
260cb7a01acSMauro Carvalho Chehab
261b9c18096SEnrico Scholz mt9p031->pll.ext_clock = ext_freq;
262cb7a01acSMauro Carvalho Chehab mt9p031->pll.pix_clock = pdata->target_freq;
263a970449eSLaurent Pinchart mt9p031->use_pll = true;
264cb7a01acSMauro Carvalho Chehab
265cb7a01acSMauro Carvalho Chehab return aptina_pll_calculate(&client->dev, &limits, &mt9p031->pll);
266cb7a01acSMauro Carvalho Chehab }
267cb7a01acSMauro Carvalho Chehab
mt9p031_pll_enable(struct mt9p031 * mt9p031)268cb7a01acSMauro Carvalho Chehab static int mt9p031_pll_enable(struct mt9p031 *mt9p031)
269cb7a01acSMauro Carvalho Chehab {
270cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
271cb7a01acSMauro Carvalho Chehab int ret;
272cb7a01acSMauro Carvalho Chehab
273a970449eSLaurent Pinchart if (!mt9p031->use_pll)
274a970449eSLaurent Pinchart return 0;
275a970449eSLaurent Pinchart
276cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_PLL_CONTROL,
277cb7a01acSMauro Carvalho Chehab MT9P031_PLL_CONTROL_PWRON);
278cb7a01acSMauro Carvalho Chehab if (ret < 0)
279cb7a01acSMauro Carvalho Chehab return ret;
280cb7a01acSMauro Carvalho Chehab
281cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_PLL_CONFIG_1,
282cb7a01acSMauro Carvalho Chehab (mt9p031->pll.m << 8) | (mt9p031->pll.n - 1));
283cb7a01acSMauro Carvalho Chehab if (ret < 0)
284cb7a01acSMauro Carvalho Chehab return ret;
285cb7a01acSMauro Carvalho Chehab
286cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_PLL_CONFIG_2, mt9p031->pll.p1 - 1);
287cb7a01acSMauro Carvalho Chehab if (ret < 0)
288cb7a01acSMauro Carvalho Chehab return ret;
289cb7a01acSMauro Carvalho Chehab
290cb7a01acSMauro Carvalho Chehab usleep_range(1000, 2000);
291cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_PLL_CONTROL,
292cb7a01acSMauro Carvalho Chehab MT9P031_PLL_CONTROL_PWRON |
293cb7a01acSMauro Carvalho Chehab MT9P031_PLL_CONTROL_USEPLL);
294cb7a01acSMauro Carvalho Chehab return ret;
295cb7a01acSMauro Carvalho Chehab }
296cb7a01acSMauro Carvalho Chehab
mt9p031_pll_disable(struct mt9p031 * mt9p031)297cb7a01acSMauro Carvalho Chehab static inline int mt9p031_pll_disable(struct mt9p031 *mt9p031)
298cb7a01acSMauro Carvalho Chehab {
299cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
300cb7a01acSMauro Carvalho Chehab
301a970449eSLaurent Pinchart if (!mt9p031->use_pll)
302a970449eSLaurent Pinchart return 0;
303a970449eSLaurent Pinchart
304cb7a01acSMauro Carvalho Chehab return mt9p031_write(client, MT9P031_PLL_CONTROL,
305cb7a01acSMauro Carvalho Chehab MT9P031_PLL_CONTROL_PWROFF);
306cb7a01acSMauro Carvalho Chehab }
307cb7a01acSMauro Carvalho Chehab
mt9p031_power_on(struct mt9p031 * mt9p031)308cb7a01acSMauro Carvalho Chehab static int mt9p031_power_on(struct mt9p031 *mt9p031)
309cb7a01acSMauro Carvalho Chehab {
3100958944aSMarek Vasut unsigned long rate, delay;
3117997196cSLaurent Pinchart int ret;
3127997196cSLaurent Pinchart
3137c3be9f8SLaurent Pinchart /* Ensure RESET_BAR is active */
3147c3be9f8SLaurent Pinchart if (mt9p031->reset) {
3157c3be9f8SLaurent Pinchart gpiod_set_value(mt9p031->reset, 1);
316cb7a01acSMauro Carvalho Chehab usleep_range(1000, 2000);
317cb7a01acSMauro Carvalho Chehab }
318cb7a01acSMauro Carvalho Chehab
31997f21276SLaurent Pinchart /* Bring up the supplies */
3207997196cSLaurent Pinchart ret = regulator_bulk_enable(ARRAY_SIZE(mt9p031->regulators),
3217997196cSLaurent Pinchart mt9p031->regulators);
3227997196cSLaurent Pinchart if (ret < 0)
3237997196cSLaurent Pinchart return ret;
32497f21276SLaurent Pinchart
325e8e45593SLaurent Pinchart /* Enable clock */
326ee2d16d7SLad, Prabhakar if (mt9p031->clk) {
327ee2d16d7SLad, Prabhakar ret = clk_prepare_enable(mt9p031->clk);
328ee2d16d7SLad, Prabhakar if (ret) {
329ee2d16d7SLad, Prabhakar regulator_bulk_disable(ARRAY_SIZE(mt9p031->regulators),
330ee2d16d7SLad, Prabhakar mt9p031->regulators);
331ee2d16d7SLad, Prabhakar return ret;
332ee2d16d7SLad, Prabhakar }
333ee2d16d7SLad, Prabhakar }
334cb7a01acSMauro Carvalho Chehab
335cb7a01acSMauro Carvalho Chehab /* Now RESET_BAR must be high */
3367c3be9f8SLaurent Pinchart if (mt9p031->reset) {
3377c3be9f8SLaurent Pinchart gpiod_set_value(mt9p031->reset, 0);
3380958944aSMarek Vasut /* Wait 850000 EXTCLK cycles before de-asserting reset. */
3390958944aSMarek Vasut rate = clk_get_rate(mt9p031->clk);
3400958944aSMarek Vasut if (!rate)
3410958944aSMarek Vasut rate = 6000000; /* Slowest supported clock, 6 MHz */
3420958944aSMarek Vasut delay = DIV_ROUND_UP(850000 * 1000, rate);
3430958944aSMarek Vasut msleep(delay);
344cb7a01acSMauro Carvalho Chehab }
345cb7a01acSMauro Carvalho Chehab
346cb7a01acSMauro Carvalho Chehab return 0;
347cb7a01acSMauro Carvalho Chehab }
348cb7a01acSMauro Carvalho Chehab
mt9p031_power_off(struct mt9p031 * mt9p031)349cb7a01acSMauro Carvalho Chehab static void mt9p031_power_off(struct mt9p031 *mt9p031)
350cb7a01acSMauro Carvalho Chehab {
3517c3be9f8SLaurent Pinchart if (mt9p031->reset) {
3527c3be9f8SLaurent Pinchart gpiod_set_value(mt9p031->reset, 1);
353cb7a01acSMauro Carvalho Chehab usleep_range(1000, 2000);
354cb7a01acSMauro Carvalho Chehab }
355cb7a01acSMauro Carvalho Chehab
3567997196cSLaurent Pinchart regulator_bulk_disable(ARRAY_SIZE(mt9p031->regulators),
3577997196cSLaurent Pinchart mt9p031->regulators);
35897f21276SLaurent Pinchart
359d6749258SLaurent Pinchart clk_disable_unprepare(mt9p031->clk);
360cb7a01acSMauro Carvalho Chehab }
361cb7a01acSMauro Carvalho Chehab
__mt9p031_set_power(struct mt9p031 * mt9p031,bool on)362cb7a01acSMauro Carvalho Chehab static int __mt9p031_set_power(struct mt9p031 *mt9p031, bool on)
363cb7a01acSMauro Carvalho Chehab {
364cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
365cb7a01acSMauro Carvalho Chehab int ret;
366cb7a01acSMauro Carvalho Chehab
367cb7a01acSMauro Carvalho Chehab if (!on) {
368cb7a01acSMauro Carvalho Chehab mt9p031_power_off(mt9p031);
369cb7a01acSMauro Carvalho Chehab return 0;
370cb7a01acSMauro Carvalho Chehab }
371cb7a01acSMauro Carvalho Chehab
372cb7a01acSMauro Carvalho Chehab ret = mt9p031_power_on(mt9p031);
373cb7a01acSMauro Carvalho Chehab if (ret < 0)
374cb7a01acSMauro Carvalho Chehab return ret;
375cb7a01acSMauro Carvalho Chehab
376cb7a01acSMauro Carvalho Chehab ret = mt9p031_reset(mt9p031);
377cb7a01acSMauro Carvalho Chehab if (ret < 0) {
378cb7a01acSMauro Carvalho Chehab dev_err(&client->dev, "Failed to reset the camera\n");
379cb7a01acSMauro Carvalho Chehab return ret;
380cb7a01acSMauro Carvalho Chehab }
381cb7a01acSMauro Carvalho Chehab
382ae47ee5fSChristian Hemp /* Configure the pixel clock polarity */
383ae47ee5fSChristian Hemp if (mt9p031->pdata && mt9p031->pdata->pixclk_pol) {
384ae47ee5fSChristian Hemp ret = mt9p031_write(client, MT9P031_PIXEL_CLOCK_CONTROL,
385ae47ee5fSChristian Hemp MT9P031_PIXEL_CLOCK_INVERT);
386ae47ee5fSChristian Hemp if (ret < 0)
387ae47ee5fSChristian Hemp return ret;
388ae47ee5fSChristian Hemp }
389ae47ee5fSChristian Hemp
390cb7a01acSMauro Carvalho Chehab return v4l2_ctrl_handler_setup(&mt9p031->ctrls);
391cb7a01acSMauro Carvalho Chehab }
392cb7a01acSMauro Carvalho Chehab
393cb7a01acSMauro Carvalho Chehab /* -----------------------------------------------------------------------------
394cb7a01acSMauro Carvalho Chehab * V4L2 subdev video operations
395cb7a01acSMauro Carvalho Chehab */
396cb7a01acSMauro Carvalho Chehab
mt9p031_set_params(struct mt9p031 * mt9p031)397cb7a01acSMauro Carvalho Chehab static int mt9p031_set_params(struct mt9p031 *mt9p031)
398cb7a01acSMauro Carvalho Chehab {
399cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
400cb7a01acSMauro Carvalho Chehab struct v4l2_mbus_framefmt *format = &mt9p031->format;
401cb7a01acSMauro Carvalho Chehab const struct v4l2_rect *crop = &mt9p031->crop;
402cb7a01acSMauro Carvalho Chehab unsigned int hblank;
403cb7a01acSMauro Carvalho Chehab unsigned int vblank;
404cb7a01acSMauro Carvalho Chehab unsigned int xskip;
405cb7a01acSMauro Carvalho Chehab unsigned int yskip;
406cb7a01acSMauro Carvalho Chehab unsigned int xbin;
407cb7a01acSMauro Carvalho Chehab unsigned int ybin;
408cb7a01acSMauro Carvalho Chehab int ret;
409cb7a01acSMauro Carvalho Chehab
410cb7a01acSMauro Carvalho Chehab /* Windows position and size.
411cb7a01acSMauro Carvalho Chehab *
412cb7a01acSMauro Carvalho Chehab * TODO: Make sure the start coordinates and window size match the
413cb7a01acSMauro Carvalho Chehab * skipping, binning and mirroring (see description of registers 2 and 4
414cb7a01acSMauro Carvalho Chehab * in table 13, and Binning section on page 41).
415cb7a01acSMauro Carvalho Chehab */
416cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_COLUMN_START, crop->left);
417cb7a01acSMauro Carvalho Chehab if (ret < 0)
418cb7a01acSMauro Carvalho Chehab return ret;
419cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_ROW_START, crop->top);
420cb7a01acSMauro Carvalho Chehab if (ret < 0)
421cb7a01acSMauro Carvalho Chehab return ret;
422cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_WINDOW_WIDTH, crop->width - 1);
423cb7a01acSMauro Carvalho Chehab if (ret < 0)
424cb7a01acSMauro Carvalho Chehab return ret;
425cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_WINDOW_HEIGHT, crop->height - 1);
426cb7a01acSMauro Carvalho Chehab if (ret < 0)
427cb7a01acSMauro Carvalho Chehab return ret;
428cb7a01acSMauro Carvalho Chehab
429cb7a01acSMauro Carvalho Chehab /* Row and column binning and skipping. Use the maximum binning value
430cb7a01acSMauro Carvalho Chehab * compatible with the skipping settings.
431cb7a01acSMauro Carvalho Chehab */
432cb7a01acSMauro Carvalho Chehab xskip = DIV_ROUND_CLOSEST(crop->width, format->width);
433cb7a01acSMauro Carvalho Chehab yskip = DIV_ROUND_CLOSEST(crop->height, format->height);
434cb7a01acSMauro Carvalho Chehab xbin = 1 << (ffs(xskip) - 1);
435cb7a01acSMauro Carvalho Chehab ybin = 1 << (ffs(yskip) - 1);
436cb7a01acSMauro Carvalho Chehab
437cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_COLUMN_ADDRESS_MODE,
438cb7a01acSMauro Carvalho Chehab ((xbin - 1) << 4) | (xskip - 1));
439cb7a01acSMauro Carvalho Chehab if (ret < 0)
440cb7a01acSMauro Carvalho Chehab return ret;
441cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_ROW_ADDRESS_MODE,
442cb7a01acSMauro Carvalho Chehab ((ybin - 1) << 4) | (yskip - 1));
443cb7a01acSMauro Carvalho Chehab if (ret < 0)
444cb7a01acSMauro Carvalho Chehab return ret;
445cb7a01acSMauro Carvalho Chehab
446cb7a01acSMauro Carvalho Chehab /* Blanking - use minimum value for horizontal blanking and default
447cb7a01acSMauro Carvalho Chehab * value for vertical blanking.
448cb7a01acSMauro Carvalho Chehab */
4495266c98bSLaurent Pinchart hblank = 346 * ybin + 64 + (80 >> min_t(unsigned int, xbin, 3));
450cb7a01acSMauro Carvalho Chehab vblank = MT9P031_VERTICAL_BLANK_DEF;
451cb7a01acSMauro Carvalho Chehab
4525266c98bSLaurent Pinchart ret = mt9p031_write(client, MT9P031_HORIZONTAL_BLANK, hblank - 1);
453cb7a01acSMauro Carvalho Chehab if (ret < 0)
454cb7a01acSMauro Carvalho Chehab return ret;
4555266c98bSLaurent Pinchart ret = mt9p031_write(client, MT9P031_VERTICAL_BLANK, vblank - 1);
456cb7a01acSMauro Carvalho Chehab if (ret < 0)
457cb7a01acSMauro Carvalho Chehab return ret;
458cb7a01acSMauro Carvalho Chehab
459cb7a01acSMauro Carvalho Chehab return ret;
460cb7a01acSMauro Carvalho Chehab }
461cb7a01acSMauro Carvalho Chehab
mt9p031_s_stream(struct v4l2_subdev * subdev,int enable)462cb7a01acSMauro Carvalho Chehab static int mt9p031_s_stream(struct v4l2_subdev *subdev, int enable)
463cb7a01acSMauro Carvalho Chehab {
464cb7a01acSMauro Carvalho Chehab struct mt9p031 *mt9p031 = to_mt9p031(subdev);
4650961ba6dSDirk Bender struct i2c_client *client = v4l2_get_subdevdata(subdev);
4660961ba6dSDirk Bender int val;
467cb7a01acSMauro Carvalho Chehab int ret;
468cb7a01acSMauro Carvalho Chehab
469cb7a01acSMauro Carvalho Chehab if (!enable) {
4700961ba6dSDirk Bender /* enable pause restart */
4710961ba6dSDirk Bender val = MT9P031_FRAME_PAUSE_RESTART;
4720961ba6dSDirk Bender ret = mt9p031_write(client, MT9P031_RESTART, val);
4730961ba6dSDirk Bender if (ret < 0)
4740961ba6dSDirk Bender return ret;
4750961ba6dSDirk Bender
4760961ba6dSDirk Bender /* enable restart + keep pause restart set */
4770961ba6dSDirk Bender val |= MT9P031_FRAME_RESTART;
4780961ba6dSDirk Bender ret = mt9p031_write(client, MT9P031_RESTART, val);
4790961ba6dSDirk Bender if (ret < 0)
4800961ba6dSDirk Bender return ret;
4810961ba6dSDirk Bender
482cb7a01acSMauro Carvalho Chehab /* Stop sensor readout */
483cb7a01acSMauro Carvalho Chehab ret = mt9p031_set_output_control(mt9p031,
484cb7a01acSMauro Carvalho Chehab MT9P031_OUTPUT_CONTROL_CEN, 0);
485cb7a01acSMauro Carvalho Chehab if (ret < 0)
486cb7a01acSMauro Carvalho Chehab return ret;
487cb7a01acSMauro Carvalho Chehab
488cb7a01acSMauro Carvalho Chehab return mt9p031_pll_disable(mt9p031);
489cb7a01acSMauro Carvalho Chehab }
490cb7a01acSMauro Carvalho Chehab
491cb7a01acSMauro Carvalho Chehab ret = mt9p031_set_params(mt9p031);
492cb7a01acSMauro Carvalho Chehab if (ret < 0)
493cb7a01acSMauro Carvalho Chehab return ret;
494cb7a01acSMauro Carvalho Chehab
495cb7a01acSMauro Carvalho Chehab /* Switch to master "normal" mode */
496cb7a01acSMauro Carvalho Chehab ret = mt9p031_set_output_control(mt9p031, 0,
497cb7a01acSMauro Carvalho Chehab MT9P031_OUTPUT_CONTROL_CEN);
498cb7a01acSMauro Carvalho Chehab if (ret < 0)
499cb7a01acSMauro Carvalho Chehab return ret;
500cb7a01acSMauro Carvalho Chehab
5010961ba6dSDirk Bender /*
5020961ba6dSDirk Bender * - clear pause restart
5030961ba6dSDirk Bender * - don't clear restart as clearing restart manually can cause
5040961ba6dSDirk Bender * undefined behavior
5050961ba6dSDirk Bender */
5060961ba6dSDirk Bender val = MT9P031_FRAME_RESTART;
5070961ba6dSDirk Bender ret = mt9p031_write(client, MT9P031_RESTART, val);
5080961ba6dSDirk Bender if (ret < 0)
5090961ba6dSDirk Bender return ret;
5100961ba6dSDirk Bender
511cb7a01acSMauro Carvalho Chehab return mt9p031_pll_enable(mt9p031);
512cb7a01acSMauro Carvalho Chehab }
513cb7a01acSMauro Carvalho Chehab
mt9p031_enum_mbus_code(struct v4l2_subdev * subdev,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_mbus_code_enum * code)514cb7a01acSMauro Carvalho Chehab static int mt9p031_enum_mbus_code(struct v4l2_subdev *subdev,
5150d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state,
516cb7a01acSMauro Carvalho Chehab struct v4l2_subdev_mbus_code_enum *code)
517cb7a01acSMauro Carvalho Chehab {
518cb7a01acSMauro Carvalho Chehab struct mt9p031 *mt9p031 = to_mt9p031(subdev);
519cb7a01acSMauro Carvalho Chehab
520cb7a01acSMauro Carvalho Chehab if (code->pad || code->index)
521cb7a01acSMauro Carvalho Chehab return -EINVAL;
522cb7a01acSMauro Carvalho Chehab
523cb7a01acSMauro Carvalho Chehab code->code = mt9p031->format.code;
524cb7a01acSMauro Carvalho Chehab return 0;
525cb7a01acSMauro Carvalho Chehab }
526cb7a01acSMauro Carvalho Chehab
mt9p031_enum_frame_size(struct v4l2_subdev * subdev,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_frame_size_enum * fse)527cb7a01acSMauro Carvalho Chehab static int mt9p031_enum_frame_size(struct v4l2_subdev *subdev,
5280d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state,
529cb7a01acSMauro Carvalho Chehab struct v4l2_subdev_frame_size_enum *fse)
530cb7a01acSMauro Carvalho Chehab {
531cb7a01acSMauro Carvalho Chehab struct mt9p031 *mt9p031 = to_mt9p031(subdev);
532cb7a01acSMauro Carvalho Chehab
533cb7a01acSMauro Carvalho Chehab if (fse->index >= 8 || fse->code != mt9p031->format.code)
534cb7a01acSMauro Carvalho Chehab return -EINVAL;
535cb7a01acSMauro Carvalho Chehab
536cb7a01acSMauro Carvalho Chehab fse->min_width = MT9P031_WINDOW_WIDTH_DEF
537cb7a01acSMauro Carvalho Chehab / min_t(unsigned int, 7, fse->index + 1);
538cb7a01acSMauro Carvalho Chehab fse->max_width = fse->min_width;
539cb7a01acSMauro Carvalho Chehab fse->min_height = MT9P031_WINDOW_HEIGHT_DEF / (fse->index + 1);
540cb7a01acSMauro Carvalho Chehab fse->max_height = fse->min_height;
541cb7a01acSMauro Carvalho Chehab
542cb7a01acSMauro Carvalho Chehab return 0;
543cb7a01acSMauro Carvalho Chehab }
544cb7a01acSMauro Carvalho Chehab
545cb7a01acSMauro Carvalho Chehab static struct v4l2_mbus_framefmt *
__mt9p031_get_pad_format(struct mt9p031 * mt9p031,struct v4l2_subdev_state * sd_state,unsigned int pad,u32 which)5460d346d2aSTomi Valkeinen __mt9p031_get_pad_format(struct mt9p031 *mt9p031,
5470d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state,
548cb7a01acSMauro Carvalho Chehab unsigned int pad, u32 which)
549cb7a01acSMauro Carvalho Chehab {
550cb7a01acSMauro Carvalho Chehab switch (which) {
551cb7a01acSMauro Carvalho Chehab case V4L2_SUBDEV_FORMAT_TRY:
5520d346d2aSTomi Valkeinen return v4l2_subdev_get_try_format(&mt9p031->subdev, sd_state,
5530d346d2aSTomi Valkeinen pad);
554cb7a01acSMauro Carvalho Chehab case V4L2_SUBDEV_FORMAT_ACTIVE:
555cb7a01acSMauro Carvalho Chehab return &mt9p031->format;
556cb7a01acSMauro Carvalho Chehab default:
557cb7a01acSMauro Carvalho Chehab return NULL;
558cb7a01acSMauro Carvalho Chehab }
559cb7a01acSMauro Carvalho Chehab }
560cb7a01acSMauro Carvalho Chehab
561cb7a01acSMauro Carvalho Chehab static struct v4l2_rect *
__mt9p031_get_pad_crop(struct mt9p031 * mt9p031,struct v4l2_subdev_state * sd_state,unsigned int pad,u32 which)5620d346d2aSTomi Valkeinen __mt9p031_get_pad_crop(struct mt9p031 *mt9p031,
5630d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state,
564cb7a01acSMauro Carvalho Chehab unsigned int pad, u32 which)
565cb7a01acSMauro Carvalho Chehab {
566cb7a01acSMauro Carvalho Chehab switch (which) {
567cb7a01acSMauro Carvalho Chehab case V4L2_SUBDEV_FORMAT_TRY:
5680d346d2aSTomi Valkeinen return v4l2_subdev_get_try_crop(&mt9p031->subdev, sd_state,
5690d346d2aSTomi Valkeinen pad);
570cb7a01acSMauro Carvalho Chehab case V4L2_SUBDEV_FORMAT_ACTIVE:
571cb7a01acSMauro Carvalho Chehab return &mt9p031->crop;
572cb7a01acSMauro Carvalho Chehab default:
573cb7a01acSMauro Carvalho Chehab return NULL;
574cb7a01acSMauro Carvalho Chehab }
575cb7a01acSMauro Carvalho Chehab }
576cb7a01acSMauro Carvalho Chehab
mt9p031_get_format(struct v4l2_subdev * subdev,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * fmt)577cb7a01acSMauro Carvalho Chehab static int mt9p031_get_format(struct v4l2_subdev *subdev,
5780d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state,
579cb7a01acSMauro Carvalho Chehab struct v4l2_subdev_format *fmt)
580cb7a01acSMauro Carvalho Chehab {
581cb7a01acSMauro Carvalho Chehab struct mt9p031 *mt9p031 = to_mt9p031(subdev);
582cb7a01acSMauro Carvalho Chehab
5830d346d2aSTomi Valkeinen fmt->format = *__mt9p031_get_pad_format(mt9p031, sd_state, fmt->pad,
584cb7a01acSMauro Carvalho Chehab fmt->which);
585cb7a01acSMauro Carvalho Chehab return 0;
586cb7a01acSMauro Carvalho Chehab }
587cb7a01acSMauro Carvalho Chehab
mt9p031_set_format(struct v4l2_subdev * subdev,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * format)588cb7a01acSMauro Carvalho Chehab static int mt9p031_set_format(struct v4l2_subdev *subdev,
5890d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state,
590cb7a01acSMauro Carvalho Chehab struct v4l2_subdev_format *format)
591cb7a01acSMauro Carvalho Chehab {
592cb7a01acSMauro Carvalho Chehab struct mt9p031 *mt9p031 = to_mt9p031(subdev);
593cb7a01acSMauro Carvalho Chehab struct v4l2_mbus_framefmt *__format;
594cb7a01acSMauro Carvalho Chehab struct v4l2_rect *__crop;
595cb7a01acSMauro Carvalho Chehab unsigned int width;
596cb7a01acSMauro Carvalho Chehab unsigned int height;
597cb7a01acSMauro Carvalho Chehab unsigned int hratio;
598cb7a01acSMauro Carvalho Chehab unsigned int vratio;
599cb7a01acSMauro Carvalho Chehab
6000d346d2aSTomi Valkeinen __crop = __mt9p031_get_pad_crop(mt9p031, sd_state, format->pad,
601cb7a01acSMauro Carvalho Chehab format->which);
602cb7a01acSMauro Carvalho Chehab
603cb7a01acSMauro Carvalho Chehab /* Clamp the width and height to avoid dividing by zero. */
604cb7a01acSMauro Carvalho Chehab width = clamp_t(unsigned int, ALIGN(format->format.width, 2),
605f90580caSRicardo Ribalda max_t(unsigned int, __crop->width / 7,
606f90580caSRicardo Ribalda MT9P031_WINDOW_WIDTH_MIN),
607cb7a01acSMauro Carvalho Chehab __crop->width);
608cb7a01acSMauro Carvalho Chehab height = clamp_t(unsigned int, ALIGN(format->format.height, 2),
609f90580caSRicardo Ribalda max_t(unsigned int, __crop->height / 8,
610f90580caSRicardo Ribalda MT9P031_WINDOW_HEIGHT_MIN),
611cb7a01acSMauro Carvalho Chehab __crop->height);
612cb7a01acSMauro Carvalho Chehab
613cb7a01acSMauro Carvalho Chehab hratio = DIV_ROUND_CLOSEST(__crop->width, width);
614cb7a01acSMauro Carvalho Chehab vratio = DIV_ROUND_CLOSEST(__crop->height, height);
615cb7a01acSMauro Carvalho Chehab
6160d346d2aSTomi Valkeinen __format = __mt9p031_get_pad_format(mt9p031, sd_state, format->pad,
617cb7a01acSMauro Carvalho Chehab format->which);
618cb7a01acSMauro Carvalho Chehab __format->width = __crop->width / hratio;
619cb7a01acSMauro Carvalho Chehab __format->height = __crop->height / vratio;
620cb7a01acSMauro Carvalho Chehab
621cb7a01acSMauro Carvalho Chehab format->format = *__format;
622cb7a01acSMauro Carvalho Chehab
623cb7a01acSMauro Carvalho Chehab return 0;
624cb7a01acSMauro Carvalho Chehab }
625cb7a01acSMauro Carvalho Chehab
mt9p031_get_selection(struct v4l2_subdev * subdev,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_selection * sel)6261a023febSHans Verkuil static int mt9p031_get_selection(struct v4l2_subdev *subdev,
6270d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state,
6281a023febSHans Verkuil struct v4l2_subdev_selection *sel)
629cb7a01acSMauro Carvalho Chehab {
630cb7a01acSMauro Carvalho Chehab struct mt9p031 *mt9p031 = to_mt9p031(subdev);
631cb7a01acSMauro Carvalho Chehab
6323193ceeaSMarek Vasut switch (sel->target) {
6333193ceeaSMarek Vasut case V4L2_SEL_TGT_CROP_BOUNDS:
6343193ceeaSMarek Vasut sel->r.left = MT9P031_COLUMN_START_MIN;
6353193ceeaSMarek Vasut sel->r.top = MT9P031_ROW_START_MIN;
6363193ceeaSMarek Vasut sel->r.width = MT9P031_WINDOW_WIDTH_MAX;
6373193ceeaSMarek Vasut sel->r.height = MT9P031_WINDOW_HEIGHT_MAX;
638cb7a01acSMauro Carvalho Chehab return 0;
6393193ceeaSMarek Vasut
6403193ceeaSMarek Vasut case V4L2_SEL_TGT_CROP:
6413193ceeaSMarek Vasut sel->r = *__mt9p031_get_pad_crop(mt9p031, sd_state,
6423193ceeaSMarek Vasut sel->pad, sel->which);
6433193ceeaSMarek Vasut return 0;
6443193ceeaSMarek Vasut
6453193ceeaSMarek Vasut default:
6463193ceeaSMarek Vasut return -EINVAL;
6473193ceeaSMarek Vasut }
648cb7a01acSMauro Carvalho Chehab }
649cb7a01acSMauro Carvalho Chehab
mt9p031_set_selection(struct v4l2_subdev * subdev,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_selection * sel)6501a023febSHans Verkuil static int mt9p031_set_selection(struct v4l2_subdev *subdev,
6510d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state,
6521a023febSHans Verkuil struct v4l2_subdev_selection *sel)
653cb7a01acSMauro Carvalho Chehab {
654cb7a01acSMauro Carvalho Chehab struct mt9p031 *mt9p031 = to_mt9p031(subdev);
655cb7a01acSMauro Carvalho Chehab struct v4l2_mbus_framefmt *__format;
656cb7a01acSMauro Carvalho Chehab struct v4l2_rect *__crop;
657cb7a01acSMauro Carvalho Chehab struct v4l2_rect rect;
658cb7a01acSMauro Carvalho Chehab
6591a023febSHans Verkuil if (sel->target != V4L2_SEL_TGT_CROP)
6601a023febSHans Verkuil return -EINVAL;
6611a023febSHans Verkuil
662cb7a01acSMauro Carvalho Chehab /* Clamp the crop rectangle boundaries and align them to a multiple of 2
663cb7a01acSMauro Carvalho Chehab * pixels to ensure a GRBG Bayer pattern.
664cb7a01acSMauro Carvalho Chehab */
6651a023febSHans Verkuil rect.left = clamp(ALIGN(sel->r.left, 2), MT9P031_COLUMN_START_MIN,
666cb7a01acSMauro Carvalho Chehab MT9P031_COLUMN_START_MAX);
6671a023febSHans Verkuil rect.top = clamp(ALIGN(sel->r.top, 2), MT9P031_ROW_START_MIN,
668cb7a01acSMauro Carvalho Chehab MT9P031_ROW_START_MAX);
6691a023febSHans Verkuil rect.width = clamp_t(unsigned int, ALIGN(sel->r.width, 2),
670cb7a01acSMauro Carvalho Chehab MT9P031_WINDOW_WIDTH_MIN,
671cb7a01acSMauro Carvalho Chehab MT9P031_WINDOW_WIDTH_MAX);
6721a023febSHans Verkuil rect.height = clamp_t(unsigned int, ALIGN(sel->r.height, 2),
673cb7a01acSMauro Carvalho Chehab MT9P031_WINDOW_HEIGHT_MIN,
674cb7a01acSMauro Carvalho Chehab MT9P031_WINDOW_HEIGHT_MAX);
675cb7a01acSMauro Carvalho Chehab
676f90580caSRicardo Ribalda rect.width = min_t(unsigned int, rect.width,
677f90580caSRicardo Ribalda MT9P031_PIXEL_ARRAY_WIDTH - rect.left);
678f90580caSRicardo Ribalda rect.height = min_t(unsigned int, rect.height,
679f90580caSRicardo Ribalda MT9P031_PIXEL_ARRAY_HEIGHT - rect.top);
680cb7a01acSMauro Carvalho Chehab
6810d346d2aSTomi Valkeinen __crop = __mt9p031_get_pad_crop(mt9p031, sd_state, sel->pad,
6820d346d2aSTomi Valkeinen sel->which);
683cb7a01acSMauro Carvalho Chehab
684cb7a01acSMauro Carvalho Chehab if (rect.width != __crop->width || rect.height != __crop->height) {
685cb7a01acSMauro Carvalho Chehab /* Reset the output image size if the crop rectangle size has
686cb7a01acSMauro Carvalho Chehab * been modified.
687cb7a01acSMauro Carvalho Chehab */
6880d346d2aSTomi Valkeinen __format = __mt9p031_get_pad_format(mt9p031, sd_state,
6890d346d2aSTomi Valkeinen sel->pad,
6901a023febSHans Verkuil sel->which);
691cb7a01acSMauro Carvalho Chehab __format->width = rect.width;
692cb7a01acSMauro Carvalho Chehab __format->height = rect.height;
693cb7a01acSMauro Carvalho Chehab }
694cb7a01acSMauro Carvalho Chehab
695cb7a01acSMauro Carvalho Chehab *__crop = rect;
6961a023febSHans Verkuil sel->r = rect;
697cb7a01acSMauro Carvalho Chehab
698cb7a01acSMauro Carvalho Chehab return 0;
699cb7a01acSMauro Carvalho Chehab }
700cb7a01acSMauro Carvalho Chehab
mt9p031_init_cfg(struct v4l2_subdev * subdev,struct v4l2_subdev_state * sd_state)70169681cd0SMarek Vasut static int mt9p031_init_cfg(struct v4l2_subdev *subdev,
70269681cd0SMarek Vasut struct v4l2_subdev_state *sd_state)
70369681cd0SMarek Vasut {
70469681cd0SMarek Vasut struct mt9p031 *mt9p031 = to_mt9p031(subdev);
70569681cd0SMarek Vasut struct v4l2_mbus_framefmt *format;
70669681cd0SMarek Vasut struct v4l2_rect *crop;
70769681cd0SMarek Vasut const int which = sd_state == NULL ? V4L2_SUBDEV_FORMAT_ACTIVE :
70869681cd0SMarek Vasut V4L2_SUBDEV_FORMAT_TRY;
70969681cd0SMarek Vasut
71069681cd0SMarek Vasut crop = __mt9p031_get_pad_crop(mt9p031, sd_state, 0, which);
71169681cd0SMarek Vasut crop->left = MT9P031_COLUMN_START_DEF;
71269681cd0SMarek Vasut crop->top = MT9P031_ROW_START_DEF;
71369681cd0SMarek Vasut crop->width = MT9P031_WINDOW_WIDTH_DEF;
71469681cd0SMarek Vasut crop->height = MT9P031_WINDOW_HEIGHT_DEF;
71569681cd0SMarek Vasut
71669681cd0SMarek Vasut format = __mt9p031_get_pad_format(mt9p031, sd_state, 0, which);
71769681cd0SMarek Vasut
71869681cd0SMarek Vasut if (mt9p031->model == MT9P031_MODEL_MONOCHROME)
71969681cd0SMarek Vasut format->code = MEDIA_BUS_FMT_Y12_1X12;
72069681cd0SMarek Vasut else
72169681cd0SMarek Vasut format->code = MEDIA_BUS_FMT_SGRBG12_1X12;
72269681cd0SMarek Vasut
72369681cd0SMarek Vasut format->width = MT9P031_WINDOW_WIDTH_DEF;
72469681cd0SMarek Vasut format->height = MT9P031_WINDOW_HEIGHT_DEF;
72569681cd0SMarek Vasut format->field = V4L2_FIELD_NONE;
72669681cd0SMarek Vasut format->colorspace = V4L2_COLORSPACE_SRGB;
72769681cd0SMarek Vasut
72869681cd0SMarek Vasut return 0;
72969681cd0SMarek Vasut }
73069681cd0SMarek Vasut
731cb7a01acSMauro Carvalho Chehab /* -----------------------------------------------------------------------------
732cb7a01acSMauro Carvalho Chehab * V4L2 subdev control operations
733cb7a01acSMauro Carvalho Chehab */
734cb7a01acSMauro Carvalho Chehab
735cb7a01acSMauro Carvalho Chehab #define V4L2_CID_BLC_AUTO (V4L2_CID_USER_BASE | 0x1002)
736cb7a01acSMauro Carvalho Chehab #define V4L2_CID_BLC_TARGET_LEVEL (V4L2_CID_USER_BASE | 0x1003)
737cb7a01acSMauro Carvalho Chehab #define V4L2_CID_BLC_ANALOG_OFFSET (V4L2_CID_USER_BASE | 0x1004)
738cb7a01acSMauro Carvalho Chehab #define V4L2_CID_BLC_DIGITAL_OFFSET (V4L2_CID_USER_BASE | 0x1005)
739cb7a01acSMauro Carvalho Chehab
mt9p031_restore_blc(struct mt9p031 * mt9p031)740535ec214SLaurent Pinchart static int mt9p031_restore_blc(struct mt9p031 *mt9p031)
741535ec214SLaurent Pinchart {
742535ec214SLaurent Pinchart struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
743535ec214SLaurent Pinchart int ret;
744535ec214SLaurent Pinchart
745535ec214SLaurent Pinchart if (mt9p031->blc_auto->cur.val != 0) {
746535ec214SLaurent Pinchart ret = mt9p031_set_mode2(mt9p031, 0,
747535ec214SLaurent Pinchart MT9P031_READ_MODE_2_ROW_BLC);
748535ec214SLaurent Pinchart if (ret < 0)
749535ec214SLaurent Pinchart return ret;
750535ec214SLaurent Pinchart }
751535ec214SLaurent Pinchart
752535ec214SLaurent Pinchart if (mt9p031->blc_offset->cur.val != 0) {
753535ec214SLaurent Pinchart ret = mt9p031_write(client, MT9P031_ROW_BLACK_TARGET,
754535ec214SLaurent Pinchart mt9p031->blc_offset->cur.val);
755535ec214SLaurent Pinchart if (ret < 0)
756535ec214SLaurent Pinchart return ret;
757535ec214SLaurent Pinchart }
758535ec214SLaurent Pinchart
759535ec214SLaurent Pinchart return 0;
760535ec214SLaurent Pinchart }
761535ec214SLaurent Pinchart
mt9p031_s_ctrl(struct v4l2_ctrl * ctrl)762cb7a01acSMauro Carvalho Chehab static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl)
763cb7a01acSMauro Carvalho Chehab {
764cb7a01acSMauro Carvalho Chehab struct mt9p031 *mt9p031 =
765cb7a01acSMauro Carvalho Chehab container_of(ctrl->handler, struct mt9p031, ctrls);
766cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
767cb7a01acSMauro Carvalho Chehab u16 data;
768cb7a01acSMauro Carvalho Chehab int ret;
769cb7a01acSMauro Carvalho Chehab
7708bf54c43SLaurent Pinchart if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE)
7718bf54c43SLaurent Pinchart return 0;
7728bf54c43SLaurent Pinchart
773cb7a01acSMauro Carvalho Chehab switch (ctrl->id) {
774cb7a01acSMauro Carvalho Chehab case V4L2_CID_EXPOSURE:
775cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_SHUTTER_WIDTH_UPPER,
776cb7a01acSMauro Carvalho Chehab (ctrl->val >> 16) & 0xffff);
777cb7a01acSMauro Carvalho Chehab if (ret < 0)
778cb7a01acSMauro Carvalho Chehab return ret;
779cb7a01acSMauro Carvalho Chehab
780cb7a01acSMauro Carvalho Chehab return mt9p031_write(client, MT9P031_SHUTTER_WIDTH_LOWER,
781cb7a01acSMauro Carvalho Chehab ctrl->val & 0xffff);
782cb7a01acSMauro Carvalho Chehab
783cb7a01acSMauro Carvalho Chehab case V4L2_CID_GAIN:
784cb7a01acSMauro Carvalho Chehab /* Gain is controlled by 2 analog stages and a digital stage.
785cb7a01acSMauro Carvalho Chehab * Valid values for the 3 stages are
786cb7a01acSMauro Carvalho Chehab *
787cb7a01acSMauro Carvalho Chehab * Stage Min Max Step
788cb7a01acSMauro Carvalho Chehab * ------------------------------------------
789cb7a01acSMauro Carvalho Chehab * First analog stage x1 x2 1
790cb7a01acSMauro Carvalho Chehab * Second analog stage x1 x4 0.125
791cb7a01acSMauro Carvalho Chehab * Digital stage x1 x16 0.125
792cb7a01acSMauro Carvalho Chehab *
793cb7a01acSMauro Carvalho Chehab * To minimize noise, the gain stages should be used in the
794cb7a01acSMauro Carvalho Chehab * second analog stage, first analog stage, digital stage order.
795cb7a01acSMauro Carvalho Chehab * Gain from a previous stage should be pushed to its maximum
796cb7a01acSMauro Carvalho Chehab * value before the next stage is used.
797cb7a01acSMauro Carvalho Chehab */
798cb7a01acSMauro Carvalho Chehab if (ctrl->val <= 32) {
799cb7a01acSMauro Carvalho Chehab data = ctrl->val;
800cb7a01acSMauro Carvalho Chehab } else if (ctrl->val <= 64) {
801cb7a01acSMauro Carvalho Chehab ctrl->val &= ~1;
802cb7a01acSMauro Carvalho Chehab data = (1 << 6) | (ctrl->val >> 1);
803cb7a01acSMauro Carvalho Chehab } else {
804cb7a01acSMauro Carvalho Chehab ctrl->val &= ~7;
805cb7a01acSMauro Carvalho Chehab data = ((ctrl->val - 64) << 5) | (1 << 6) | 32;
806cb7a01acSMauro Carvalho Chehab }
807cb7a01acSMauro Carvalho Chehab
808cb7a01acSMauro Carvalho Chehab return mt9p031_write(client, MT9P031_GLOBAL_GAIN, data);
809cb7a01acSMauro Carvalho Chehab
810cb7a01acSMauro Carvalho Chehab case V4L2_CID_HFLIP:
811cb7a01acSMauro Carvalho Chehab if (ctrl->val)
812cb7a01acSMauro Carvalho Chehab return mt9p031_set_mode2(mt9p031,
813cb7a01acSMauro Carvalho Chehab 0, MT9P031_READ_MODE_2_COL_MIR);
814cb7a01acSMauro Carvalho Chehab else
815cb7a01acSMauro Carvalho Chehab return mt9p031_set_mode2(mt9p031,
816cb7a01acSMauro Carvalho Chehab MT9P031_READ_MODE_2_COL_MIR, 0);
817cb7a01acSMauro Carvalho Chehab
818cb7a01acSMauro Carvalho Chehab case V4L2_CID_VFLIP:
819cb7a01acSMauro Carvalho Chehab if (ctrl->val)
820cb7a01acSMauro Carvalho Chehab return mt9p031_set_mode2(mt9p031,
821cb7a01acSMauro Carvalho Chehab 0, MT9P031_READ_MODE_2_ROW_MIR);
822cb7a01acSMauro Carvalho Chehab else
823cb7a01acSMauro Carvalho Chehab return mt9p031_set_mode2(mt9p031,
824cb7a01acSMauro Carvalho Chehab MT9P031_READ_MODE_2_ROW_MIR, 0);
825cb7a01acSMauro Carvalho Chehab
826cb7a01acSMauro Carvalho Chehab case V4L2_CID_TEST_PATTERN:
8278bf54c43SLaurent Pinchart /* The digital side of the Black Level Calibration function must
8288bf54c43SLaurent Pinchart * be disabled when generating a test pattern to avoid artifacts
8298bf54c43SLaurent Pinchart * in the image. Activate (deactivate) the BLC-related controls
8308bf54c43SLaurent Pinchart * when the test pattern is enabled (disabled).
8318bf54c43SLaurent Pinchart */
8328bf54c43SLaurent Pinchart v4l2_ctrl_activate(mt9p031->blc_auto, ctrl->val == 0);
8338bf54c43SLaurent Pinchart v4l2_ctrl_activate(mt9p031->blc_offset, ctrl->val == 0);
8348bf54c43SLaurent Pinchart
835cb7a01acSMauro Carvalho Chehab if (!ctrl->val) {
8368bf54c43SLaurent Pinchart /* Restore the BLC settings. */
837535ec214SLaurent Pinchart ret = mt9p031_restore_blc(mt9p031);
838cb7a01acSMauro Carvalho Chehab if (ret < 0)
839cb7a01acSMauro Carvalho Chehab return ret;
840535ec214SLaurent Pinchart
8410a0e78d1SStefan Riedmueller return mt9p031_write(client, MT9P031_TEST_PATTERN, 0);
842cb7a01acSMauro Carvalho Chehab }
843cb7a01acSMauro Carvalho Chehab
844cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_TEST_PATTERN_GREEN, 0x05a0);
845cb7a01acSMauro Carvalho Chehab if (ret < 0)
846cb7a01acSMauro Carvalho Chehab return ret;
847cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_TEST_PATTERN_RED, 0x0a50);
848cb7a01acSMauro Carvalho Chehab if (ret < 0)
849cb7a01acSMauro Carvalho Chehab return ret;
850cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_TEST_PATTERN_BLUE, 0x0aa0);
851cb7a01acSMauro Carvalho Chehab if (ret < 0)
852cb7a01acSMauro Carvalho Chehab return ret;
853cb7a01acSMauro Carvalho Chehab
8548bf54c43SLaurent Pinchart /* Disable digital BLC when generating a test pattern. */
855cb7a01acSMauro Carvalho Chehab ret = mt9p031_set_mode2(mt9p031, MT9P031_READ_MODE_2_ROW_BLC,
856cb7a01acSMauro Carvalho Chehab 0);
857cb7a01acSMauro Carvalho Chehab if (ret < 0)
858cb7a01acSMauro Carvalho Chehab return ret;
859cb7a01acSMauro Carvalho Chehab
860cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET, 0);
861cb7a01acSMauro Carvalho Chehab if (ret < 0)
862cb7a01acSMauro Carvalho Chehab return ret;
863cb7a01acSMauro Carvalho Chehab
864cb7a01acSMauro Carvalho Chehab return mt9p031_write(client, MT9P031_TEST_PATTERN,
865cb7a01acSMauro Carvalho Chehab ((ctrl->val - 1) << MT9P031_TEST_PATTERN_SHIFT)
866cb7a01acSMauro Carvalho Chehab | MT9P031_TEST_PATTERN_ENABLE);
867cb7a01acSMauro Carvalho Chehab
868cb7a01acSMauro Carvalho Chehab case V4L2_CID_BLC_AUTO:
869cb7a01acSMauro Carvalho Chehab ret = mt9p031_set_mode2(mt9p031,
870cb7a01acSMauro Carvalho Chehab ctrl->val ? 0 : MT9P031_READ_MODE_2_ROW_BLC,
871cb7a01acSMauro Carvalho Chehab ctrl->val ? MT9P031_READ_MODE_2_ROW_BLC : 0);
872cb7a01acSMauro Carvalho Chehab if (ret < 0)
873cb7a01acSMauro Carvalho Chehab return ret;
874cb7a01acSMauro Carvalho Chehab
875cb7a01acSMauro Carvalho Chehab return mt9p031_write(client, MT9P031_BLACK_LEVEL_CALIBRATION,
876cb7a01acSMauro Carvalho Chehab ctrl->val ? 0 : MT9P031_BLC_MANUAL_BLC);
877cb7a01acSMauro Carvalho Chehab
878cb7a01acSMauro Carvalho Chehab case V4L2_CID_BLC_TARGET_LEVEL:
879cb7a01acSMauro Carvalho Chehab return mt9p031_write(client, MT9P031_ROW_BLACK_TARGET,
880cb7a01acSMauro Carvalho Chehab ctrl->val);
881cb7a01acSMauro Carvalho Chehab
882cb7a01acSMauro Carvalho Chehab case V4L2_CID_BLC_ANALOG_OFFSET:
883cb7a01acSMauro Carvalho Chehab data = ctrl->val & ((1 << 9) - 1);
884cb7a01acSMauro Carvalho Chehab
885cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_GREEN1_OFFSET, data);
886cb7a01acSMauro Carvalho Chehab if (ret < 0)
887cb7a01acSMauro Carvalho Chehab return ret;
888cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_GREEN2_OFFSET, data);
889cb7a01acSMauro Carvalho Chehab if (ret < 0)
890cb7a01acSMauro Carvalho Chehab return ret;
891cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_RED_OFFSET, data);
892cb7a01acSMauro Carvalho Chehab if (ret < 0)
893cb7a01acSMauro Carvalho Chehab return ret;
894cb7a01acSMauro Carvalho Chehab return mt9p031_write(client, MT9P031_BLUE_OFFSET, data);
895cb7a01acSMauro Carvalho Chehab
896cb7a01acSMauro Carvalho Chehab case V4L2_CID_BLC_DIGITAL_OFFSET:
897cb7a01acSMauro Carvalho Chehab return mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET,
898cb7a01acSMauro Carvalho Chehab ctrl->val & ((1 << 12) - 1));
899cb7a01acSMauro Carvalho Chehab }
900cb7a01acSMauro Carvalho Chehab
901cb7a01acSMauro Carvalho Chehab return 0;
902cb7a01acSMauro Carvalho Chehab }
903cb7a01acSMauro Carvalho Chehab
904217bdb07SJulia Lawall static const struct v4l2_ctrl_ops mt9p031_ctrl_ops = {
905cb7a01acSMauro Carvalho Chehab .s_ctrl = mt9p031_s_ctrl,
906cb7a01acSMauro Carvalho Chehab };
907cb7a01acSMauro Carvalho Chehab
908cb7a01acSMauro Carvalho Chehab static const char * const mt9p031_test_pattern_menu[] = {
909cb7a01acSMauro Carvalho Chehab "Disabled",
910cb7a01acSMauro Carvalho Chehab "Color Field",
911cb7a01acSMauro Carvalho Chehab "Horizontal Gradient",
912cb7a01acSMauro Carvalho Chehab "Vertical Gradient",
913cb7a01acSMauro Carvalho Chehab "Diagonal Gradient",
914cb7a01acSMauro Carvalho Chehab "Classic Test Pattern",
915cb7a01acSMauro Carvalho Chehab "Walking 1s",
916cb7a01acSMauro Carvalho Chehab "Monochrome Horizontal Bars",
917cb7a01acSMauro Carvalho Chehab "Monochrome Vertical Bars",
918cb7a01acSMauro Carvalho Chehab "Vertical Color Bars",
919cb7a01acSMauro Carvalho Chehab };
920cb7a01acSMauro Carvalho Chehab
921cb7a01acSMauro Carvalho Chehab static const struct v4l2_ctrl_config mt9p031_ctrls[] = {
922cb7a01acSMauro Carvalho Chehab {
923cb7a01acSMauro Carvalho Chehab .ops = &mt9p031_ctrl_ops,
924cb7a01acSMauro Carvalho Chehab .id = V4L2_CID_BLC_AUTO,
925cb7a01acSMauro Carvalho Chehab .type = V4L2_CTRL_TYPE_BOOLEAN,
926cb7a01acSMauro Carvalho Chehab .name = "BLC, Auto",
927cb7a01acSMauro Carvalho Chehab .min = 0,
928cb7a01acSMauro Carvalho Chehab .max = 1,
929cb7a01acSMauro Carvalho Chehab .step = 1,
930cb7a01acSMauro Carvalho Chehab .def = 1,
931cb7a01acSMauro Carvalho Chehab .flags = 0,
932cb7a01acSMauro Carvalho Chehab }, {
933cb7a01acSMauro Carvalho Chehab .ops = &mt9p031_ctrl_ops,
934cb7a01acSMauro Carvalho Chehab .id = V4L2_CID_BLC_TARGET_LEVEL,
935cb7a01acSMauro Carvalho Chehab .type = V4L2_CTRL_TYPE_INTEGER,
936cb7a01acSMauro Carvalho Chehab .name = "BLC Target Level",
937cb7a01acSMauro Carvalho Chehab .min = 0,
938cb7a01acSMauro Carvalho Chehab .max = 4095,
939cb7a01acSMauro Carvalho Chehab .step = 1,
940cb7a01acSMauro Carvalho Chehab .def = 168,
941cb7a01acSMauro Carvalho Chehab .flags = 0,
942cb7a01acSMauro Carvalho Chehab }, {
943cb7a01acSMauro Carvalho Chehab .ops = &mt9p031_ctrl_ops,
944cb7a01acSMauro Carvalho Chehab .id = V4L2_CID_BLC_ANALOG_OFFSET,
945cb7a01acSMauro Carvalho Chehab .type = V4L2_CTRL_TYPE_INTEGER,
946cb7a01acSMauro Carvalho Chehab .name = "BLC Analog Offset",
947cb7a01acSMauro Carvalho Chehab .min = -255,
948cb7a01acSMauro Carvalho Chehab .max = 255,
949cb7a01acSMauro Carvalho Chehab .step = 1,
950cb7a01acSMauro Carvalho Chehab .def = 32,
951cb7a01acSMauro Carvalho Chehab .flags = 0,
952cb7a01acSMauro Carvalho Chehab }, {
953cb7a01acSMauro Carvalho Chehab .ops = &mt9p031_ctrl_ops,
954cb7a01acSMauro Carvalho Chehab .id = V4L2_CID_BLC_DIGITAL_OFFSET,
955cb7a01acSMauro Carvalho Chehab .type = V4L2_CTRL_TYPE_INTEGER,
956cb7a01acSMauro Carvalho Chehab .name = "BLC Digital Offset",
957cb7a01acSMauro Carvalho Chehab .min = -2048,
958cb7a01acSMauro Carvalho Chehab .max = 2047,
959cb7a01acSMauro Carvalho Chehab .step = 1,
960cb7a01acSMauro Carvalho Chehab .def = 40,
961cb7a01acSMauro Carvalho Chehab .flags = 0,
962cb7a01acSMauro Carvalho Chehab }
963cb7a01acSMauro Carvalho Chehab };
964cb7a01acSMauro Carvalho Chehab
965cb7a01acSMauro Carvalho Chehab /* -----------------------------------------------------------------------------
966cb7a01acSMauro Carvalho Chehab * V4L2 subdev core operations
967cb7a01acSMauro Carvalho Chehab */
968cb7a01acSMauro Carvalho Chehab
mt9p031_set_power(struct v4l2_subdev * subdev,int on)969cb7a01acSMauro Carvalho Chehab static int mt9p031_set_power(struct v4l2_subdev *subdev, int on)
970cb7a01acSMauro Carvalho Chehab {
971cb7a01acSMauro Carvalho Chehab struct mt9p031 *mt9p031 = to_mt9p031(subdev);
972cb7a01acSMauro Carvalho Chehab int ret = 0;
973cb7a01acSMauro Carvalho Chehab
974cb7a01acSMauro Carvalho Chehab mutex_lock(&mt9p031->power_lock);
975cb7a01acSMauro Carvalho Chehab
976cb7a01acSMauro Carvalho Chehab /* If the power count is modified from 0 to != 0 or from != 0 to 0,
977cb7a01acSMauro Carvalho Chehab * update the power state.
978cb7a01acSMauro Carvalho Chehab */
979cb7a01acSMauro Carvalho Chehab if (mt9p031->power_count == !on) {
980cb7a01acSMauro Carvalho Chehab ret = __mt9p031_set_power(mt9p031, !!on);
981cb7a01acSMauro Carvalho Chehab if (ret < 0)
982cb7a01acSMauro Carvalho Chehab goto out;
983cb7a01acSMauro Carvalho Chehab }
984cb7a01acSMauro Carvalho Chehab
985cb7a01acSMauro Carvalho Chehab /* Update the power count. */
986cb7a01acSMauro Carvalho Chehab mt9p031->power_count += on ? 1 : -1;
987cb7a01acSMauro Carvalho Chehab WARN_ON(mt9p031->power_count < 0);
988cb7a01acSMauro Carvalho Chehab
989cb7a01acSMauro Carvalho Chehab out:
990cb7a01acSMauro Carvalho Chehab mutex_unlock(&mt9p031->power_lock);
991cb7a01acSMauro Carvalho Chehab return ret;
992cb7a01acSMauro Carvalho Chehab }
993cb7a01acSMauro Carvalho Chehab
994cb7a01acSMauro Carvalho Chehab /* -----------------------------------------------------------------------------
995cb7a01acSMauro Carvalho Chehab * V4L2 subdev internal operations
996cb7a01acSMauro Carvalho Chehab */
997cb7a01acSMauro Carvalho Chehab
mt9p031_registered(struct v4l2_subdev * subdev)998cb7a01acSMauro Carvalho Chehab static int mt9p031_registered(struct v4l2_subdev *subdev)
999cb7a01acSMauro Carvalho Chehab {
1000cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(subdev);
1001cb7a01acSMauro Carvalho Chehab struct mt9p031 *mt9p031 = to_mt9p031(subdev);
1002cb7a01acSMauro Carvalho Chehab s32 data;
1003cb7a01acSMauro Carvalho Chehab int ret;
1004cb7a01acSMauro Carvalho Chehab
1005cb7a01acSMauro Carvalho Chehab ret = mt9p031_power_on(mt9p031);
1006cb7a01acSMauro Carvalho Chehab if (ret < 0) {
1007cb7a01acSMauro Carvalho Chehab dev_err(&client->dev, "MT9P031 power up failed\n");
1008cb7a01acSMauro Carvalho Chehab return ret;
1009cb7a01acSMauro Carvalho Chehab }
1010cb7a01acSMauro Carvalho Chehab
1011cb7a01acSMauro Carvalho Chehab /* Read out the chip version register */
1012cb7a01acSMauro Carvalho Chehab data = mt9p031_read(client, MT9P031_CHIP_VERSION);
1013bbcc9fa0SGuennadi Liakhovetski mt9p031_power_off(mt9p031);
1014bbcc9fa0SGuennadi Liakhovetski
1015cb7a01acSMauro Carvalho Chehab if (data != MT9P031_CHIP_VERSION_VALUE) {
1016cb7a01acSMauro Carvalho Chehab dev_err(&client->dev, "MT9P031 not detected, wrong version "
1017cb7a01acSMauro Carvalho Chehab "0x%04x\n", data);
1018cb7a01acSMauro Carvalho Chehab return -ENODEV;
1019cb7a01acSMauro Carvalho Chehab }
1020cb7a01acSMauro Carvalho Chehab
1021cb7a01acSMauro Carvalho Chehab dev_info(&client->dev, "MT9P031 detected at address 0x%02x\n",
1022cb7a01acSMauro Carvalho Chehab client->addr);
1023cb7a01acSMauro Carvalho Chehab
1024bbcc9fa0SGuennadi Liakhovetski return 0;
1025cb7a01acSMauro Carvalho Chehab }
1026cb7a01acSMauro Carvalho Chehab
mt9p031_open(struct v4l2_subdev * subdev,struct v4l2_subdev_fh * fh)1027cb7a01acSMauro Carvalho Chehab static int mt9p031_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
1028cb7a01acSMauro Carvalho Chehab {
1029cb7a01acSMauro Carvalho Chehab return mt9p031_set_power(subdev, 1);
1030cb7a01acSMauro Carvalho Chehab }
1031cb7a01acSMauro Carvalho Chehab
mt9p031_close(struct v4l2_subdev * subdev,struct v4l2_subdev_fh * fh)1032cb7a01acSMauro Carvalho Chehab static int mt9p031_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
1033cb7a01acSMauro Carvalho Chehab {
1034cb7a01acSMauro Carvalho Chehab return mt9p031_set_power(subdev, 0);
1035cb7a01acSMauro Carvalho Chehab }
1036cb7a01acSMauro Carvalho Chehab
10377c137c60SBhumika Goyal static const struct v4l2_subdev_core_ops mt9p031_subdev_core_ops = {
1038cb7a01acSMauro Carvalho Chehab .s_power = mt9p031_set_power,
1039cb7a01acSMauro Carvalho Chehab };
1040cb7a01acSMauro Carvalho Chehab
10417c137c60SBhumika Goyal static const struct v4l2_subdev_video_ops mt9p031_subdev_video_ops = {
1042cb7a01acSMauro Carvalho Chehab .s_stream = mt9p031_s_stream,
1043cb7a01acSMauro Carvalho Chehab };
1044cb7a01acSMauro Carvalho Chehab
10457c137c60SBhumika Goyal static const struct v4l2_subdev_pad_ops mt9p031_subdev_pad_ops = {
104669681cd0SMarek Vasut .init_cfg = mt9p031_init_cfg,
1047cb7a01acSMauro Carvalho Chehab .enum_mbus_code = mt9p031_enum_mbus_code,
1048cb7a01acSMauro Carvalho Chehab .enum_frame_size = mt9p031_enum_frame_size,
1049cb7a01acSMauro Carvalho Chehab .get_fmt = mt9p031_get_format,
1050cb7a01acSMauro Carvalho Chehab .set_fmt = mt9p031_set_format,
10511a023febSHans Verkuil .get_selection = mt9p031_get_selection,
10521a023febSHans Verkuil .set_selection = mt9p031_set_selection,
1053cb7a01acSMauro Carvalho Chehab };
1054cb7a01acSMauro Carvalho Chehab
10557c137c60SBhumika Goyal static const struct v4l2_subdev_ops mt9p031_subdev_ops = {
1056cb7a01acSMauro Carvalho Chehab .core = &mt9p031_subdev_core_ops,
1057cb7a01acSMauro Carvalho Chehab .video = &mt9p031_subdev_video_ops,
1058cb7a01acSMauro Carvalho Chehab .pad = &mt9p031_subdev_pad_ops,
1059cb7a01acSMauro Carvalho Chehab };
1060cb7a01acSMauro Carvalho Chehab
1061cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_internal_ops mt9p031_subdev_internal_ops = {
1062cb7a01acSMauro Carvalho Chehab .registered = mt9p031_registered,
1063cb7a01acSMauro Carvalho Chehab .open = mt9p031_open,
1064cb7a01acSMauro Carvalho Chehab .close = mt9p031_close,
1065cb7a01acSMauro Carvalho Chehab };
1066cb7a01acSMauro Carvalho Chehab
1067cb7a01acSMauro Carvalho Chehab /* -----------------------------------------------------------------------------
1068cb7a01acSMauro Carvalho Chehab * Driver initialization and probing
1069cb7a01acSMauro Carvalho Chehab */
1070cb7a01acSMauro Carvalho Chehab
10718d4da37cSLad, Prabhakar static struct mt9p031_platform_data *
mt9p031_get_pdata(struct i2c_client * client)10728d4da37cSLad, Prabhakar mt9p031_get_pdata(struct i2c_client *client)
10738d4da37cSLad, Prabhakar {
1074ae47ee5fSChristian Hemp struct mt9p031_platform_data *pdata = NULL;
10758d4da37cSLad, Prabhakar struct device_node *np;
1076ae47ee5fSChristian Hemp struct v4l2_fwnode_endpoint endpoint = {
1077ae47ee5fSChristian Hemp .bus_type = V4L2_MBUS_PARALLEL
1078ae47ee5fSChristian Hemp };
10798d4da37cSLad, Prabhakar
10808d4da37cSLad, Prabhakar if (!IS_ENABLED(CONFIG_OF) || !client->dev.of_node)
10818d4da37cSLad, Prabhakar return client->dev.platform_data;
10828d4da37cSLad, Prabhakar
1083fd9fdb78SPhilipp Zabel np = of_graph_get_next_endpoint(client->dev.of_node, NULL);
10848d4da37cSLad, Prabhakar if (!np)
10858d4da37cSLad, Prabhakar return NULL;
10868d4da37cSLad, Prabhakar
1087ae47ee5fSChristian Hemp if (v4l2_fwnode_endpoint_parse(of_fwnode_handle(np), &endpoint) < 0)
1088ae47ee5fSChristian Hemp goto done;
1089ae47ee5fSChristian Hemp
10908d4da37cSLad, Prabhakar pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
10918d4da37cSLad, Prabhakar if (!pdata)
10928d4da37cSLad, Prabhakar goto done;
10938d4da37cSLad, Prabhakar
10948d4da37cSLad, Prabhakar of_property_read_u32(np, "input-clock-frequency", &pdata->ext_freq);
10958d4da37cSLad, Prabhakar of_property_read_u32(np, "pixel-clock-frequency", &pdata->target_freq);
10968d4da37cSLad, Prabhakar
1097ae47ee5fSChristian Hemp pdata->pixclk_pol = !!(endpoint.bus.parallel.flags &
1098ae47ee5fSChristian Hemp V4L2_MBUS_PCLK_SAMPLE_RISING);
1099ae47ee5fSChristian Hemp
11008d4da37cSLad, Prabhakar done:
11018d4da37cSLad, Prabhakar of_node_put(np);
11028d4da37cSLad, Prabhakar return pdata;
11038d4da37cSLad, Prabhakar }
11048d4da37cSLad, Prabhakar
mt9p031_probe(struct i2c_client * client)11056ed661f1SUwe Kleine-König static int mt9p031_probe(struct i2c_client *client)
1106cb7a01acSMauro Carvalho Chehab {
11076ed661f1SUwe Kleine-König const struct i2c_device_id *did = i2c_client_get_device_id(client);
11088d4da37cSLad, Prabhakar struct mt9p031_platform_data *pdata = mt9p031_get_pdata(client);
1109a8a3e813SWolfram Sang struct i2c_adapter *adapter = client->adapter;
1110cb7a01acSMauro Carvalho Chehab struct mt9p031 *mt9p031;
1111cb7a01acSMauro Carvalho Chehab unsigned int i;
1112cb7a01acSMauro Carvalho Chehab int ret;
1113cb7a01acSMauro Carvalho Chehab
1114cb7a01acSMauro Carvalho Chehab if (pdata == NULL) {
1115cb7a01acSMauro Carvalho Chehab dev_err(&client->dev, "No platform data\n");
1116cb7a01acSMauro Carvalho Chehab return -EINVAL;
1117cb7a01acSMauro Carvalho Chehab }
1118cb7a01acSMauro Carvalho Chehab
1119cb7a01acSMauro Carvalho Chehab if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
1120cb7a01acSMauro Carvalho Chehab dev_warn(&client->dev,
1121cb7a01acSMauro Carvalho Chehab "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
1122cb7a01acSMauro Carvalho Chehab return -EIO;
1123cb7a01acSMauro Carvalho Chehab }
1124cb7a01acSMauro Carvalho Chehab
112537b9f211SLaurent Pinchart mt9p031 = devm_kzalloc(&client->dev, sizeof(*mt9p031), GFP_KERNEL);
1126cb7a01acSMauro Carvalho Chehab if (mt9p031 == NULL)
1127cb7a01acSMauro Carvalho Chehab return -ENOMEM;
1128cb7a01acSMauro Carvalho Chehab
1129cb7a01acSMauro Carvalho Chehab mt9p031->pdata = pdata;
1130cb7a01acSMauro Carvalho Chehab mt9p031->output_control = MT9P031_OUTPUT_CONTROL_DEF;
1131cb7a01acSMauro Carvalho Chehab mt9p031->mode2 = MT9P031_READ_MODE_2_ROW_BLC;
1132cb7a01acSMauro Carvalho Chehab mt9p031->model = did->driver_data;
1133cb7a01acSMauro Carvalho Chehab
11347997196cSLaurent Pinchart mt9p031->regulators[0].supply = "vdd";
11357997196cSLaurent Pinchart mt9p031->regulators[1].supply = "vdd_io";
11367997196cSLaurent Pinchart mt9p031->regulators[2].supply = "vaa";
113797f21276SLaurent Pinchart
11387997196cSLaurent Pinchart ret = devm_regulator_bulk_get(&client->dev, 3, mt9p031->regulators);
11397997196cSLaurent Pinchart if (ret < 0) {
114097f21276SLaurent Pinchart dev_err(&client->dev, "Unable to get regulators\n");
11417997196cSLaurent Pinchart return ret;
114297f21276SLaurent Pinchart }
114397f21276SLaurent Pinchart
114415af4a53SLad, Prabhakar mutex_init(&mt9p031->power_lock);
114515af4a53SLad, Prabhakar
1146b28d7017SLad, Prabhakar v4l2_ctrl_handler_init(&mt9p031->ctrls, ARRAY_SIZE(mt9p031_ctrls) + 6);
1147cb7a01acSMauro Carvalho Chehab
1148cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
1149cb7a01acSMauro Carvalho Chehab V4L2_CID_EXPOSURE, MT9P031_SHUTTER_WIDTH_MIN,
1150cb7a01acSMauro Carvalho Chehab MT9P031_SHUTTER_WIDTH_MAX, 1,
1151cb7a01acSMauro Carvalho Chehab MT9P031_SHUTTER_WIDTH_DEF);
1152cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
1153cb7a01acSMauro Carvalho Chehab V4L2_CID_GAIN, MT9P031_GLOBAL_GAIN_MIN,
1154cb7a01acSMauro Carvalho Chehab MT9P031_GLOBAL_GAIN_MAX, 1, MT9P031_GLOBAL_GAIN_DEF);
1155cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
1156cb7a01acSMauro Carvalho Chehab V4L2_CID_HFLIP, 0, 1, 1, 0);
1157cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
1158cb7a01acSMauro Carvalho Chehab V4L2_CID_VFLIP, 0, 1, 1, 0);
1159cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
1160cb7a01acSMauro Carvalho Chehab V4L2_CID_PIXEL_RATE, pdata->target_freq,
1161cb7a01acSMauro Carvalho Chehab pdata->target_freq, 1, pdata->target_freq);
1162b28d7017SLad, Prabhakar v4l2_ctrl_new_std_menu_items(&mt9p031->ctrls, &mt9p031_ctrl_ops,
1163b28d7017SLad, Prabhakar V4L2_CID_TEST_PATTERN,
1164b28d7017SLad, Prabhakar ARRAY_SIZE(mt9p031_test_pattern_menu) - 1, 0,
1165b28d7017SLad, Prabhakar 0, mt9p031_test_pattern_menu);
1166cb7a01acSMauro Carvalho Chehab
1167cb7a01acSMauro Carvalho Chehab for (i = 0; i < ARRAY_SIZE(mt9p031_ctrls); ++i)
1168cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_custom(&mt9p031->ctrls, &mt9p031_ctrls[i], NULL);
1169cb7a01acSMauro Carvalho Chehab
1170cb7a01acSMauro Carvalho Chehab mt9p031->subdev.ctrl_handler = &mt9p031->ctrls;
1171cb7a01acSMauro Carvalho Chehab
1172cb7a01acSMauro Carvalho Chehab if (mt9p031->ctrls.error) {
1173cb7a01acSMauro Carvalho Chehab printk(KERN_INFO "%s: control initialization error %d\n",
1174cb7a01acSMauro Carvalho Chehab __func__, mt9p031->ctrls.error);
1175cb7a01acSMauro Carvalho Chehab ret = mt9p031->ctrls.error;
1176cb7a01acSMauro Carvalho Chehab goto done;
1177cb7a01acSMauro Carvalho Chehab }
1178cb7a01acSMauro Carvalho Chehab
1179cb7a01acSMauro Carvalho Chehab mt9p031->blc_auto = v4l2_ctrl_find(&mt9p031->ctrls, V4L2_CID_BLC_AUTO);
1180cb7a01acSMauro Carvalho Chehab mt9p031->blc_offset = v4l2_ctrl_find(&mt9p031->ctrls,
1181cb7a01acSMauro Carvalho Chehab V4L2_CID_BLC_DIGITAL_OFFSET);
1182cb7a01acSMauro Carvalho Chehab
1183cb7a01acSMauro Carvalho Chehab v4l2_i2c_subdev_init(&mt9p031->subdev, client, &mt9p031_subdev_ops);
1184cb7a01acSMauro Carvalho Chehab mt9p031->subdev.internal_ops = &mt9p031_subdev_internal_ops;
1185cb7a01acSMauro Carvalho Chehab
1186173bf6e5SHans Verkuil mt9p031->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR;
1187cb7a01acSMauro Carvalho Chehab mt9p031->pad.flags = MEDIA_PAD_FL_SOURCE;
1188ab22e77cSMauro Carvalho Chehab ret = media_entity_pads_init(&mt9p031->subdev.entity, 1, &mt9p031->pad);
1189cb7a01acSMauro Carvalho Chehab if (ret < 0)
1190cb7a01acSMauro Carvalho Chehab goto done;
1191cb7a01acSMauro Carvalho Chehab
1192cb7a01acSMauro Carvalho Chehab mt9p031->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
1193cb7a01acSMauro Carvalho Chehab
119469681cd0SMarek Vasut ret = mt9p031_init_cfg(&mt9p031->subdev, NULL);
119569681cd0SMarek Vasut if (ret)
119669681cd0SMarek Vasut goto done;
1197cb7a01acSMauro Carvalho Chehab
11987c3be9f8SLaurent Pinchart mt9p031->reset = devm_gpiod_get_optional(&client->dev, "reset",
11997c3be9f8SLaurent Pinchart GPIOD_OUT_HIGH);
1200cb7a01acSMauro Carvalho Chehab
1201d6749258SLaurent Pinchart ret = mt9p031_clk_setup(mt9p031);
12029012d088SLad, Prabhakar if (ret)
12039012d088SLad, Prabhakar goto done;
12049012d088SLad, Prabhakar
12059012d088SLad, Prabhakar ret = v4l2_async_register_subdev(&mt9p031->subdev);
1206cb7a01acSMauro Carvalho Chehab
1207cb7a01acSMauro Carvalho Chehab done:
1208cb7a01acSMauro Carvalho Chehab if (ret < 0) {
1209cb7a01acSMauro Carvalho Chehab v4l2_ctrl_handler_free(&mt9p031->ctrls);
1210cb7a01acSMauro Carvalho Chehab media_entity_cleanup(&mt9p031->subdev.entity);
121115af4a53SLad, Prabhakar mutex_destroy(&mt9p031->power_lock);
1212cb7a01acSMauro Carvalho Chehab }
1213cb7a01acSMauro Carvalho Chehab
1214cb7a01acSMauro Carvalho Chehab return ret;
1215cb7a01acSMauro Carvalho Chehab }
1216cb7a01acSMauro Carvalho Chehab
mt9p031_remove(struct i2c_client * client)1217ed5c2f5fSUwe Kleine-König static void mt9p031_remove(struct i2c_client *client)
1218cb7a01acSMauro Carvalho Chehab {
1219cb7a01acSMauro Carvalho Chehab struct v4l2_subdev *subdev = i2c_get_clientdata(client);
1220cb7a01acSMauro Carvalho Chehab struct mt9p031 *mt9p031 = to_mt9p031(subdev);
1221cb7a01acSMauro Carvalho Chehab
1222cb7a01acSMauro Carvalho Chehab v4l2_ctrl_handler_free(&mt9p031->ctrls);
12239012d088SLad, Prabhakar v4l2_async_unregister_subdev(subdev);
1224cb7a01acSMauro Carvalho Chehab media_entity_cleanup(&subdev->entity);
122515af4a53SLad, Prabhakar mutex_destroy(&mt9p031->power_lock);
1226cb7a01acSMauro Carvalho Chehab }
1227cb7a01acSMauro Carvalho Chehab
1228cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id mt9p031_id[] = {
122992fa766bSMarek Vasut { "mt9p006", MT9P031_MODEL_COLOR },
1230cb7a01acSMauro Carvalho Chehab { "mt9p031", MT9P031_MODEL_COLOR },
1231cb7a01acSMauro Carvalho Chehab { "mt9p031m", MT9P031_MODEL_MONOCHROME },
1232cb7a01acSMauro Carvalho Chehab { }
1233cb7a01acSMauro Carvalho Chehab };
1234cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, mt9p031_id);
1235cb7a01acSMauro Carvalho Chehab
12368d4da37cSLad, Prabhakar #if IS_ENABLED(CONFIG_OF)
12378d4da37cSLad, Prabhakar static const struct of_device_id mt9p031_of_match[] = {
123892fa766bSMarek Vasut { .compatible = "aptina,mt9p006", },
12398d4da37cSLad, Prabhakar { .compatible = "aptina,mt9p031", },
12408d4da37cSLad, Prabhakar { .compatible = "aptina,mt9p031m", },
12418d4da37cSLad, Prabhakar { /* sentinel */ },
12428d4da37cSLad, Prabhakar };
12438d4da37cSLad, Prabhakar MODULE_DEVICE_TABLE(of, mt9p031_of_match);
12448d4da37cSLad, Prabhakar #endif
12458d4da37cSLad, Prabhakar
1246cb7a01acSMauro Carvalho Chehab static struct i2c_driver mt9p031_i2c_driver = {
1247cb7a01acSMauro Carvalho Chehab .driver = {
12488d4da37cSLad, Prabhakar .of_match_table = of_match_ptr(mt9p031_of_match),
1249cb7a01acSMauro Carvalho Chehab .name = "mt9p031",
1250cb7a01acSMauro Carvalho Chehab },
1251*aaeb31c0SUwe Kleine-König .probe = mt9p031_probe,
1252cb7a01acSMauro Carvalho Chehab .remove = mt9p031_remove,
1253cb7a01acSMauro Carvalho Chehab .id_table = mt9p031_id,
1254cb7a01acSMauro Carvalho Chehab };
1255cb7a01acSMauro Carvalho Chehab
1256cb7a01acSMauro Carvalho Chehab module_i2c_driver(mt9p031_i2c_driver);
1257cb7a01acSMauro Carvalho Chehab
1258cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("Aptina MT9P031 Camera driver");
1259cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Bastian Hecht <hechtb@gmail.com>");
1260cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL v2");
1261