Commit | Line | Data |
---|---|---|
8d648271 MV |
1 | /* |
2 | * OmniVision OV96xx Camera Driver | |
3 | * | |
4 | * Copyright (C) 2009 Marek Vasut <marek.vasut@gmail.com> | |
5 | * | |
6 | * Based on ov772x camera driver: | |
7 | * | |
8 | * Copyright (C) 2008 Renesas Solutions Corp. | |
9 | * Kuninori Morimoto <morimoto.kuninori@renesas.com> | |
10 | * | |
11 | * Based on ov7670 and soc_camera_platform driver, | |
12 | * | |
13 | * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net> | |
14 | * Copyright (C) 2008 Magnus Damm | |
15 | * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> | |
16 | * | |
17 | * This program is free software; you can redistribute it and/or modify | |
18 | * it under the terms of the GNU General Public License version 2 as | |
19 | * published by the Free Software Foundation. | |
20 | */ | |
21 | ||
22 | #include <linux/init.h> | |
23 | #include <linux/module.h> | |
24 | #include <linux/i2c.h> | |
25 | #include <linux/slab.h> | |
26 | #include <linux/delay.h> | |
95d20109 | 27 | #include <linux/v4l2-mediabus.h> |
8d648271 | 28 | #include <linux/videodev2.h> |
de2e388d GL |
29 | |
30 | #include <media/soc_camera.h> | |
8d648271 MV |
31 | #include <media/v4l2-chip-ident.h> |
32 | #include <media/v4l2-common.h> | |
839b48df | 33 | #include <media/v4l2-ctrls.h> |
8d648271 MV |
34 | |
35 | #include "ov9640.h" | |
36 | ||
f7b74f76 DC |
37 | #define to_ov9640_sensor(sd) container_of(sd, struct ov9640_priv, subdev) |
38 | ||
8d648271 MV |
39 | /* default register setup */ |
40 | static const struct ov9640_reg ov9640_regs_dflt[] = { | |
41 | { OV9640_COM5, OV9640_COM5_SYSCLK | OV9640_COM5_LONGEXP }, | |
42 | { OV9640_COM6, OV9640_COM6_OPT_BLC | OV9640_COM6_ADBLC_BIAS | | |
43 | OV9640_COM6_FMT_RST | OV9640_COM6_ADBLC_OPTEN }, | |
44 | { OV9640_PSHFT, OV9640_PSHFT_VAL(0x01) }, | |
45 | { OV9640_ACOM, OV9640_ACOM_2X_ANALOG | OV9640_ACOM_RSVD }, | |
46 | { OV9640_TSLB, OV9640_TSLB_YUYV_UYVY }, | |
47 | { OV9640_COM16, OV9640_COM16_RB_AVG }, | |
48 | ||
49 | /* Gamma curve P */ | |
50 | { 0x6c, 0x40 }, { 0x6d, 0x30 }, { 0x6e, 0x4b }, { 0x6f, 0x60 }, | |
51 | { 0x70, 0x70 }, { 0x71, 0x70 }, { 0x72, 0x70 }, { 0x73, 0x70 }, | |
52 | { 0x74, 0x60 }, { 0x75, 0x60 }, { 0x76, 0x50 }, { 0x77, 0x48 }, | |
53 | { 0x78, 0x3a }, { 0x79, 0x2e }, { 0x7a, 0x28 }, { 0x7b, 0x22 }, | |
54 | ||
55 | /* Gamma curve T */ | |
56 | { 0x7c, 0x04 }, { 0x7d, 0x07 }, { 0x7e, 0x10 }, { 0x7f, 0x28 }, | |
57 | { 0x80, 0x36 }, { 0x81, 0x44 }, { 0x82, 0x52 }, { 0x83, 0x60 }, | |
58 | { 0x84, 0x6c }, { 0x85, 0x78 }, { 0x86, 0x8c }, { 0x87, 0x9e }, | |
59 | { 0x88, 0xbb }, { 0x89, 0xd2 }, { 0x8a, 0xe6 }, | |
60 | }; | |
61 | ||
62 | /* Configurations | |
63 | * NOTE: for YUV, alter the following registers: | |
64 | * COM12 |= OV9640_COM12_YUV_AVG | |
65 | * | |
66 | * for RGB, alter the following registers: | |
000f64ef MCC |
67 | * COM7 |= OV9640_COM7_RGB |
68 | * COM13 |= OV9640_COM13_RGB_AVG | |
69 | * COM15 |= proper RGB color encoding mode | |
8d648271 MV |
70 | */ |
71 | static const struct ov9640_reg ov9640_regs_qqcif[] = { | |
72 | { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x0f) }, | |
73 | { OV9640_COM1, OV9640_COM1_QQFMT | OV9640_COM1_HREF_2SKIP }, | |
74 | { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, | |
75 | { OV9640_COM7, OV9640_COM7_QCIF }, | |
76 | { OV9640_COM12, OV9640_COM12_RSVD }, | |
77 | { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, | |
78 | { OV9640_COM15, OV9640_COM15_OR_10F0 }, | |
79 | }; | |
80 | ||
81 | static const struct ov9640_reg ov9640_regs_qqvga[] = { | |
82 | { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x07) }, | |
83 | { OV9640_COM1, OV9640_COM1_QQFMT | OV9640_COM1_HREF_2SKIP }, | |
84 | { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, | |
85 | { OV9640_COM7, OV9640_COM7_QVGA }, | |
86 | { OV9640_COM12, OV9640_COM12_RSVD }, | |
87 | { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, | |
88 | { OV9640_COM15, OV9640_COM15_OR_10F0 }, | |
89 | }; | |
90 | ||
91 | static const struct ov9640_reg ov9640_regs_qcif[] = { | |
92 | { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x07) }, | |
93 | { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, | |
94 | { OV9640_COM7, OV9640_COM7_QCIF }, | |
95 | { OV9640_COM12, OV9640_COM12_RSVD }, | |
96 | { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, | |
97 | { OV9640_COM15, OV9640_COM15_OR_10F0 }, | |
98 | }; | |
99 | ||
100 | static const struct ov9640_reg ov9640_regs_qvga[] = { | |
101 | { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x03) }, | |
102 | { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, | |
103 | { OV9640_COM7, OV9640_COM7_QVGA }, | |
104 | { OV9640_COM12, OV9640_COM12_RSVD }, | |
105 | { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, | |
106 | { OV9640_COM15, OV9640_COM15_OR_10F0 }, | |
107 | }; | |
108 | ||
109 | static const struct ov9640_reg ov9640_regs_cif[] = { | |
110 | { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x03) }, | |
111 | { OV9640_COM3, OV9640_COM3_VP }, | |
112 | { OV9640_COM7, OV9640_COM7_CIF }, | |
113 | { OV9640_COM12, OV9640_COM12_RSVD }, | |
114 | { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, | |
115 | { OV9640_COM15, OV9640_COM15_OR_10F0 }, | |
116 | }; | |
117 | ||
118 | static const struct ov9640_reg ov9640_regs_vga[] = { | |
119 | { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x01) }, | |
120 | { OV9640_COM3, OV9640_COM3_VP }, | |
121 | { OV9640_COM7, OV9640_COM7_VGA }, | |
122 | { OV9640_COM12, OV9640_COM12_RSVD }, | |
123 | { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, | |
124 | { OV9640_COM15, OV9640_COM15_OR_10F0 }, | |
125 | }; | |
126 | ||
127 | static const struct ov9640_reg ov9640_regs_sxga[] = { | |
128 | { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x01) }, | |
129 | { OV9640_COM3, OV9640_COM3_VP }, | |
130 | { OV9640_COM7, 0 }, | |
131 | { OV9640_COM12, OV9640_COM12_RSVD }, | |
132 | { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, | |
133 | { OV9640_COM15, OV9640_COM15_OR_10F0 }, | |
134 | }; | |
135 | ||
136 | static const struct ov9640_reg ov9640_regs_yuv[] = { | |
137 | { OV9640_MTX1, 0x58 }, | |
138 | { OV9640_MTX2, 0x48 }, | |
139 | { OV9640_MTX3, 0x10 }, | |
140 | { OV9640_MTX4, 0x28 }, | |
141 | { OV9640_MTX5, 0x48 }, | |
142 | { OV9640_MTX6, 0x70 }, | |
143 | { OV9640_MTX7, 0x40 }, | |
144 | { OV9640_MTX8, 0x40 }, | |
145 | { OV9640_MTX9, 0x40 }, | |
146 | { OV9640_MTXS, 0x0f }, | |
147 | }; | |
148 | ||
149 | static const struct ov9640_reg ov9640_regs_rgb[] = { | |
150 | { OV9640_MTX1, 0x71 }, | |
151 | { OV9640_MTX2, 0x3e }, | |
152 | { OV9640_MTX3, 0x0c }, | |
153 | { OV9640_MTX4, 0x33 }, | |
154 | { OV9640_MTX5, 0x72 }, | |
155 | { OV9640_MTX6, 0x00 }, | |
156 | { OV9640_MTX7, 0x2b }, | |
157 | { OV9640_MTX8, 0x66 }, | |
158 | { OV9640_MTX9, 0xd2 }, | |
159 | { OV9640_MTXS, 0x65 }, | |
160 | }; | |
161 | ||
760697be | 162 | static enum v4l2_mbus_pixelcode ov9640_codes[] = { |
ace6e979 | 163 | V4L2_MBUS_FMT_UYVY8_2X8, |
760697be GL |
164 | V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE, |
165 | V4L2_MBUS_FMT_RGB565_2X8_LE, | |
8d648271 MV |
166 | }; |
167 | ||
8d648271 MV |
168 | /* read a register */ |
169 | static int ov9640_reg_read(struct i2c_client *client, u8 reg, u8 *val) | |
170 | { | |
171 | int ret; | |
172 | u8 data = reg; | |
173 | struct i2c_msg msg = { | |
174 | .addr = client->addr, | |
175 | .flags = 0, | |
176 | .len = 1, | |
177 | .buf = &data, | |
178 | }; | |
179 | ||
180 | ret = i2c_transfer(client->adapter, &msg, 1); | |
181 | if (ret < 0) | |
182 | goto err; | |
183 | ||
184 | msg.flags = I2C_M_RD; | |
185 | ret = i2c_transfer(client->adapter, &msg, 1); | |
186 | if (ret < 0) | |
187 | goto err; | |
188 | ||
189 | *val = data; | |
190 | return 0; | |
191 | ||
192 | err: | |
193 | dev_err(&client->dev, "Failed reading register 0x%02x!\n", reg); | |
194 | return ret; | |
195 | } | |
196 | ||
197 | /* write a register */ | |
198 | static int ov9640_reg_write(struct i2c_client *client, u8 reg, u8 val) | |
199 | { | |
200 | int ret; | |
201 | u8 _val; | |
202 | unsigned char data[2] = { reg, val }; | |
203 | struct i2c_msg msg = { | |
204 | .addr = client->addr, | |
205 | .flags = 0, | |
206 | .len = 2, | |
207 | .buf = data, | |
208 | }; | |
209 | ||
210 | ret = i2c_transfer(client->adapter, &msg, 1); | |
211 | if (ret < 0) { | |
212 | dev_err(&client->dev, "Failed writing register 0x%02x!\n", reg); | |
213 | return ret; | |
214 | } | |
215 | ||
216 | /* we have to read the register back ... no idea why, maybe HW bug */ | |
217 | ret = ov9640_reg_read(client, reg, &_val); | |
218 | if (ret) | |
219 | dev_err(&client->dev, | |
220 | "Failed reading back register 0x%02x!\n", reg); | |
221 | ||
222 | return 0; | |
223 | } | |
224 | ||
225 | ||
226 | /* Read a register, alter its bits, write it back */ | |
227 | static int ov9640_reg_rmw(struct i2c_client *client, u8 reg, u8 set, u8 unset) | |
228 | { | |
229 | u8 val; | |
230 | int ret; | |
231 | ||
232 | ret = ov9640_reg_read(client, reg, &val); | |
233 | if (ret) { | |
234 | dev_err(&client->dev, | |
235 | "[Read]-Modify-Write of register %02x failed!\n", reg); | |
236 | return val; | |
237 | } | |
238 | ||
239 | val |= set; | |
240 | val &= ~unset; | |
241 | ||
242 | ret = ov9640_reg_write(client, reg, val); | |
243 | if (ret) | |
244 | dev_err(&client->dev, | |
245 | "Read-Modify-[Write] of register %02x failed!\n", reg); | |
246 | ||
247 | return ret; | |
248 | } | |
249 | ||
250 | /* Soft reset the camera. This has nothing to do with the RESET pin! */ | |
251 | static int ov9640_reset(struct i2c_client *client) | |
252 | { | |
253 | int ret; | |
254 | ||
255 | ret = ov9640_reg_write(client, OV9640_COM7, OV9640_COM7_SCCB_RESET); | |
256 | if (ret) | |
257 | dev_err(&client->dev, | |
25985edc | 258 | "An error occurred while entering soft reset!\n"); |
8d648271 MV |
259 | |
260 | return ret; | |
261 | } | |
262 | ||
263 | /* Start/Stop streaming from the device */ | |
264 | static int ov9640_s_stream(struct v4l2_subdev *sd, int enable) | |
265 | { | |
266 | return 0; | |
267 | } | |
268 | ||
8d648271 | 269 | /* Set status of additional camera capabilities */ |
839b48df | 270 | static int ov9640_s_ctrl(struct v4l2_ctrl *ctrl) |
8d648271 | 271 | { |
839b48df HV |
272 | struct ov9640_priv *priv = container_of(ctrl->handler, struct ov9640_priv, hdl); |
273 | struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); | |
8d648271 MV |
274 | |
275 | switch (ctrl->id) { | |
276 | case V4L2_CID_VFLIP: | |
839b48df HV |
277 | if (ctrl->val) |
278 | return ov9640_reg_rmw(client, OV9640_MVFP, | |
8d648271 | 279 | OV9640_MVFP_V, 0); |
839b48df | 280 | return ov9640_reg_rmw(client, OV9640_MVFP, 0, OV9640_MVFP_V); |
8d648271 | 281 | case V4L2_CID_HFLIP: |
839b48df HV |
282 | if (ctrl->val) |
283 | return ov9640_reg_rmw(client, OV9640_MVFP, | |
8d648271 | 284 | OV9640_MVFP_H, 0); |
839b48df | 285 | return ov9640_reg_rmw(client, OV9640_MVFP, 0, OV9640_MVFP_H); |
8d648271 | 286 | } |
839b48df | 287 | return -EINVAL; |
8d648271 MV |
288 | } |
289 | ||
290 | /* Get chip identification */ | |
291 | static int ov9640_g_chip_ident(struct v4l2_subdev *sd, | |
292 | struct v4l2_dbg_chip_ident *id) | |
293 | { | |
f7b74f76 | 294 | struct ov9640_priv *priv = to_ov9640_sensor(sd); |
8d648271 MV |
295 | |
296 | id->ident = priv->model; | |
297 | id->revision = priv->revision; | |
298 | ||
299 | return 0; | |
300 | } | |
301 | ||
302 | #ifdef CONFIG_VIDEO_ADV_DEBUG | |
303 | static int ov9640_get_register(struct v4l2_subdev *sd, | |
304 | struct v4l2_dbg_register *reg) | |
305 | { | |
c4ce6d14 | 306 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
8d648271 MV |
307 | int ret; |
308 | u8 val; | |
309 | ||
310 | if (reg->reg & ~0xff) | |
311 | return -EINVAL; | |
312 | ||
313 | reg->size = 1; | |
314 | ||
315 | ret = ov9640_reg_read(client, reg->reg, &val); | |
316 | if (ret) | |
317 | return ret; | |
318 | ||
319 | reg->val = (__u64)val; | |
320 | ||
321 | return 0; | |
322 | } | |
323 | ||
324 | static int ov9640_set_register(struct v4l2_subdev *sd, | |
325 | struct v4l2_dbg_register *reg) | |
326 | { | |
c4ce6d14 | 327 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
8d648271 MV |
328 | |
329 | if (reg->reg & ~0xff || reg->val & ~0xff) | |
330 | return -EINVAL; | |
331 | ||
332 | return ov9640_reg_write(client, reg->reg, reg->val); | |
333 | } | |
334 | #endif | |
335 | ||
4ec10bac LP |
336 | static int ov9640_s_power(struct v4l2_subdev *sd, int on) |
337 | { | |
338 | struct i2c_client *client = v4l2_get_subdevdata(sd); | |
339 | struct soc_camera_link *icl = soc_camera_i2c_to_link(client); | |
340 | ||
341 | return soc_camera_set_power(&client->dev, icl, on); | |
342 | } | |
343 | ||
8d648271 MV |
344 | /* select nearest higher resolution for capture */ |
345 | static void ov9640_res_roundup(u32 *width, u32 *height) | |
346 | { | |
347 | int i; | |
348 | enum { QQCIF, QQVGA, QCIF, QVGA, CIF, VGA, SXGA }; | |
349 | int res_x[] = { 88, 160, 176, 320, 352, 640, 1280 }; | |
350 | int res_y[] = { 72, 120, 144, 240, 288, 480, 960 }; | |
351 | ||
352 | for (i = 0; i < ARRAY_SIZE(res_x); i++) { | |
353 | if (res_x[i] >= *width && res_y[i] >= *height) { | |
354 | *width = res_x[i]; | |
355 | *height = res_y[i]; | |
356 | return; | |
357 | } | |
358 | } | |
359 | ||
360 | *width = res_x[SXGA]; | |
361 | *height = res_y[SXGA]; | |
362 | } | |
363 | ||
364 | /* Prepare necessary register changes depending on color encoding */ | |
760697be GL |
365 | static void ov9640_alter_regs(enum v4l2_mbus_pixelcode code, |
366 | struct ov9640_reg_alt *alt) | |
8d648271 | 367 | { |
760697be GL |
368 | switch (code) { |
369 | default: | |
ace6e979 | 370 | case V4L2_MBUS_FMT_UYVY8_2X8: |
8d648271 MV |
371 | alt->com12 = OV9640_COM12_YUV_AVG; |
372 | alt->com13 = OV9640_COM13_Y_DELAY_EN | | |
373 | OV9640_COM13_YUV_DLY(0x01); | |
374 | break; | |
760697be | 375 | case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE: |
8d648271 MV |
376 | alt->com7 = OV9640_COM7_RGB; |
377 | alt->com13 = OV9640_COM13_RGB_AVG; | |
378 | alt->com15 = OV9640_COM15_RGB_555; | |
379 | break; | |
760697be | 380 | case V4L2_MBUS_FMT_RGB565_2X8_LE: |
8d648271 MV |
381 | alt->com7 = OV9640_COM7_RGB; |
382 | alt->com13 = OV9640_COM13_RGB_AVG; | |
383 | alt->com15 = OV9640_COM15_RGB_565; | |
384 | break; | |
385 | }; | |
386 | } | |
387 | ||
388 | /* Setup registers according to resolution and color encoding */ | |
760697be GL |
389 | static int ov9640_write_regs(struct i2c_client *client, u32 width, |
390 | enum v4l2_mbus_pixelcode code, struct ov9640_reg_alt *alts) | |
8d648271 MV |
391 | { |
392 | const struct ov9640_reg *ov9640_regs, *matrix_regs; | |
393 | int ov9640_regs_len, matrix_regs_len; | |
394 | int i, ret; | |
395 | u8 val; | |
396 | ||
397 | /* select register configuration for given resolution */ | |
398 | switch (width) { | |
399 | case W_QQCIF: | |
400 | ov9640_regs = ov9640_regs_qqcif; | |
401 | ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qqcif); | |
402 | break; | |
403 | case W_QQVGA: | |
404 | ov9640_regs = ov9640_regs_qqvga; | |
405 | ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qqvga); | |
406 | break; | |
407 | case W_QCIF: | |
408 | ov9640_regs = ov9640_regs_qcif; | |
409 | ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qcif); | |
410 | break; | |
411 | case W_QVGA: | |
412 | ov9640_regs = ov9640_regs_qvga; | |
413 | ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qvga); | |
414 | break; | |
415 | case W_CIF: | |
416 | ov9640_regs = ov9640_regs_cif; | |
417 | ov9640_regs_len = ARRAY_SIZE(ov9640_regs_cif); | |
418 | break; | |
419 | case W_VGA: | |
420 | ov9640_regs = ov9640_regs_vga; | |
421 | ov9640_regs_len = ARRAY_SIZE(ov9640_regs_vga); | |
422 | break; | |
423 | case W_SXGA: | |
424 | ov9640_regs = ov9640_regs_sxga; | |
425 | ov9640_regs_len = ARRAY_SIZE(ov9640_regs_sxga); | |
426 | break; | |
427 | default: | |
428 | dev_err(&client->dev, "Failed to select resolution!\n"); | |
429 | return -EINVAL; | |
430 | } | |
431 | ||
432 | /* select color matrix configuration for given color encoding */ | |
ace6e979 | 433 | if (code == V4L2_MBUS_FMT_UYVY8_2X8) { |
8d648271 MV |
434 | matrix_regs = ov9640_regs_yuv; |
435 | matrix_regs_len = ARRAY_SIZE(ov9640_regs_yuv); | |
436 | } else { | |
437 | matrix_regs = ov9640_regs_rgb; | |
438 | matrix_regs_len = ARRAY_SIZE(ov9640_regs_rgb); | |
439 | } | |
440 | ||
441 | /* write register settings into the module */ | |
442 | for (i = 0; i < ov9640_regs_len; i++) { | |
443 | val = ov9640_regs[i].val; | |
444 | ||
445 | switch (ov9640_regs[i].reg) { | |
446 | case OV9640_COM7: | |
447 | val |= alts->com7; | |
448 | break; | |
449 | case OV9640_COM12: | |
450 | val |= alts->com12; | |
451 | break; | |
452 | case OV9640_COM13: | |
453 | val |= alts->com13; | |
454 | break; | |
455 | case OV9640_COM15: | |
456 | val |= alts->com15; | |
457 | break; | |
458 | } | |
459 | ||
460 | ret = ov9640_reg_write(client, ov9640_regs[i].reg, val); | |
461 | if (ret) | |
462 | return ret; | |
463 | } | |
464 | ||
465 | /* write color matrix configuration into the module */ | |
466 | for (i = 0; i < matrix_regs_len; i++) { | |
467 | ret = ov9640_reg_write(client, matrix_regs[i].reg, | |
468 | matrix_regs[i].val); | |
469 | if (ret) | |
470 | return ret; | |
471 | } | |
472 | ||
473 | return 0; | |
474 | } | |
475 | ||
476 | /* program default register values */ | |
477 | static int ov9640_prog_dflt(struct i2c_client *client) | |
478 | { | |
479 | int i, ret; | |
480 | ||
481 | for (i = 0; i < ARRAY_SIZE(ov9640_regs_dflt); i++) { | |
482 | ret = ov9640_reg_write(client, ov9640_regs_dflt[i].reg, | |
483 | ov9640_regs_dflt[i].val); | |
484 | if (ret) | |
485 | return ret; | |
486 | } | |
487 | ||
488 | /* wait for the changes to actually happen, 140ms are not enough yet */ | |
489 | mdelay(150); | |
490 | ||
491 | return 0; | |
492 | } | |
493 | ||
494 | /* set the format we will capture in */ | |
760697be GL |
495 | static int ov9640_s_fmt(struct v4l2_subdev *sd, |
496 | struct v4l2_mbus_framefmt *mf) | |
8d648271 | 497 | { |
c4ce6d14 | 498 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
8d648271 | 499 | struct ov9640_reg_alt alts = {0}; |
760697be GL |
500 | enum v4l2_colorspace cspace; |
501 | enum v4l2_mbus_pixelcode code = mf->code; | |
8d648271 MV |
502 | int ret; |
503 | ||
760697be GL |
504 | ov9640_res_roundup(&mf->width, &mf->height); |
505 | ov9640_alter_regs(mf->code, &alts); | |
8d648271 MV |
506 | |
507 | ov9640_reset(client); | |
508 | ||
509 | ret = ov9640_prog_dflt(client); | |
510 | if (ret) | |
511 | return ret; | |
512 | ||
760697be GL |
513 | switch (code) { |
514 | case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE: | |
515 | case V4L2_MBUS_FMT_RGB565_2X8_LE: | |
516 | cspace = V4L2_COLORSPACE_SRGB; | |
517 | break; | |
518 | default: | |
ace6e979 GL |
519 | code = V4L2_MBUS_FMT_UYVY8_2X8; |
520 | case V4L2_MBUS_FMT_UYVY8_2X8: | |
760697be GL |
521 | cspace = V4L2_COLORSPACE_JPEG; |
522 | } | |
523 | ||
524 | ret = ov9640_write_regs(client, mf->width, code, &alts); | |
525 | if (!ret) { | |
526 | mf->code = code; | |
527 | mf->colorspace = cspace; | |
528 | } | |
529 | ||
530 | return ret; | |
8d648271 MV |
531 | } |
532 | ||
760697be GL |
533 | static int ov9640_try_fmt(struct v4l2_subdev *sd, |
534 | struct v4l2_mbus_framefmt *mf) | |
8d648271 | 535 | { |
760697be | 536 | ov9640_res_roundup(&mf->width, &mf->height); |
8d648271 | 537 | |
760697be GL |
538 | mf->field = V4L2_FIELD_NONE; |
539 | ||
540 | switch (mf->code) { | |
541 | case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE: | |
542 | case V4L2_MBUS_FMT_RGB565_2X8_LE: | |
543 | mf->colorspace = V4L2_COLORSPACE_SRGB; | |
544 | break; | |
545 | default: | |
ace6e979 GL |
546 | mf->code = V4L2_MBUS_FMT_UYVY8_2X8; |
547 | case V4L2_MBUS_FMT_UYVY8_2X8: | |
760697be GL |
548 | mf->colorspace = V4L2_COLORSPACE_JPEG; |
549 | } | |
8d648271 MV |
550 | |
551 | return 0; | |
552 | } | |
553 | ||
3805f201 | 554 | static int ov9640_enum_fmt(struct v4l2_subdev *sd, unsigned int index, |
760697be GL |
555 | enum v4l2_mbus_pixelcode *code) |
556 | { | |
3805f201 | 557 | if (index >= ARRAY_SIZE(ov9640_codes)) |
760697be GL |
558 | return -EINVAL; |
559 | ||
560 | *code = ov9640_codes[index]; | |
561 | return 0; | |
562 | } | |
563 | ||
8d648271 MV |
564 | static int ov9640_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) |
565 | { | |
566 | a->c.left = 0; | |
567 | a->c.top = 0; | |
568 | a->c.width = W_SXGA; | |
569 | a->c.height = H_SXGA; | |
570 | a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | |
571 | ||
572 | return 0; | |
573 | } | |
574 | ||
575 | static int ov9640_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) | |
576 | { | |
577 | a->bounds.left = 0; | |
578 | a->bounds.top = 0; | |
579 | a->bounds.width = W_SXGA; | |
580 | a->bounds.height = H_SXGA; | |
581 | a->defrect = a->bounds; | |
582 | a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | |
583 | a->pixelaspect.numerator = 1; | |
584 | a->pixelaspect.denominator = 1; | |
585 | ||
586 | return 0; | |
587 | } | |
588 | ||
14178aa5 | 589 | static int ov9640_video_probe(struct i2c_client *client) |
8d648271 | 590 | { |
63c9718c DC |
591 | struct v4l2_subdev *sd = i2c_get_clientdata(client); |
592 | struct ov9640_priv *priv = to_ov9640_sensor(sd); | |
8d648271 MV |
593 | u8 pid, ver, midh, midl; |
594 | const char *devname; | |
4bbc6d52 LP |
595 | int ret; |
596 | ||
597 | ret = ov9640_s_power(&priv->subdev, 1); | |
598 | if (ret < 0) | |
599 | return ret; | |
8d648271 | 600 | |
8d648271 MV |
601 | /* |
602 | * check and show product ID and manufacturer ID | |
603 | */ | |
604 | ||
605 | ret = ov9640_reg_read(client, OV9640_PID, &pid); | |
839b48df HV |
606 | if (!ret) |
607 | ret = ov9640_reg_read(client, OV9640_VER, &ver); | |
608 | if (!ret) | |
609 | ret = ov9640_reg_read(client, OV9640_MIDH, &midh); | |
610 | if (!ret) | |
611 | ret = ov9640_reg_read(client, OV9640_MIDL, &midl); | |
8d648271 | 612 | if (ret) |
4bbc6d52 | 613 | goto done; |
8d648271 MV |
614 | |
615 | switch (VERSION(pid, ver)) { | |
616 | case OV9640_V2: | |
617 | devname = "ov9640"; | |
618 | priv->model = V4L2_IDENT_OV9640; | |
619 | priv->revision = 2; | |
908d4d14 | 620 | break; |
8d648271 MV |
621 | case OV9640_V3: |
622 | devname = "ov9640"; | |
623 | priv->model = V4L2_IDENT_OV9640; | |
624 | priv->revision = 3; | |
625 | break; | |
626 | default: | |
627 | dev_err(&client->dev, "Product ID error %x:%x\n", pid, ver); | |
4bbc6d52 LP |
628 | ret = -ENODEV; |
629 | goto done; | |
8d648271 MV |
630 | } |
631 | ||
632 | dev_info(&client->dev, "%s Product ID %0x:%0x Manufacturer ID %x:%x\n", | |
633 | devname, pid, ver, midh, midl); | |
634 | ||
4bbc6d52 LP |
635 | ret = v4l2_ctrl_handler_setup(&priv->hdl); |
636 | ||
637 | done: | |
638 | ov9640_s_power(&priv->subdev, 0); | |
639 | return ret; | |
8d648271 MV |
640 | } |
641 | ||
839b48df HV |
642 | static const struct v4l2_ctrl_ops ov9640_ctrl_ops = { |
643 | .s_ctrl = ov9640_s_ctrl, | |
8d648271 MV |
644 | }; |
645 | ||
646 | static struct v4l2_subdev_core_ops ov9640_core_ops = { | |
8d648271 MV |
647 | .g_chip_ident = ov9640_g_chip_ident, |
648 | #ifdef CONFIG_VIDEO_ADV_DEBUG | |
649 | .g_register = ov9640_get_register, | |
650 | .s_register = ov9640_set_register, | |
651 | #endif | |
4ec10bac | 652 | .s_power = ov9640_s_power, |
8d648271 MV |
653 | }; |
654 | ||
88e816a2 | 655 | /* Request bus settings on camera side */ |
de2e388d GL |
656 | static int ov9640_g_mbus_config(struct v4l2_subdev *sd, |
657 | struct v4l2_mbus_config *cfg) | |
658 | { | |
659 | struct i2c_client *client = v4l2_get_subdevdata(sd); | |
14178aa5 | 660 | struct soc_camera_link *icl = soc_camera_i2c_to_link(client); |
de2e388d GL |
661 | |
662 | cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | | |
663 | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | | |
664 | V4L2_MBUS_DATA_ACTIVE_HIGH; | |
665 | cfg->type = V4L2_MBUS_PARALLEL; | |
666 | cfg->flags = soc_camera_apply_board_flags(icl, cfg); | |
667 | ||
668 | return 0; | |
669 | } | |
670 | ||
8d648271 | 671 | static struct v4l2_subdev_video_ops ov9640_video_ops = { |
760697be GL |
672 | .s_stream = ov9640_s_stream, |
673 | .s_mbus_fmt = ov9640_s_fmt, | |
674 | .try_mbus_fmt = ov9640_try_fmt, | |
675 | .enum_mbus_fmt = ov9640_enum_fmt, | |
676 | .cropcap = ov9640_cropcap, | |
677 | .g_crop = ov9640_g_crop, | |
de2e388d | 678 | .g_mbus_config = ov9640_g_mbus_config, |
8d648271 MV |
679 | }; |
680 | ||
681 | static struct v4l2_subdev_ops ov9640_subdev_ops = { | |
682 | .core = &ov9640_core_ops, | |
683 | .video = &ov9640_video_ops, | |
684 | }; | |
685 | ||
686 | /* | |
687 | * i2c_driver function | |
688 | */ | |
689 | static int ov9640_probe(struct i2c_client *client, | |
690 | const struct i2c_device_id *did) | |
691 | { | |
692 | struct ov9640_priv *priv; | |
14178aa5 | 693 | struct soc_camera_link *icl = soc_camera_i2c_to_link(client); |
8d648271 MV |
694 | int ret; |
695 | ||
8d648271 MV |
696 | if (!icl) { |
697 | dev_err(&client->dev, "Missing platform_data for driver\n"); | |
698 | return -EINVAL; | |
699 | } | |
700 | ||
701 | priv = kzalloc(sizeof(struct ov9640_priv), GFP_KERNEL); | |
702 | if (!priv) { | |
703 | dev_err(&client->dev, | |
704 | "Failed to allocate memory for private data!\n"); | |
705 | return -ENOMEM; | |
706 | } | |
707 | ||
708 | v4l2_i2c_subdev_init(&priv->subdev, client, &ov9640_subdev_ops); | |
709 | ||
839b48df HV |
710 | v4l2_ctrl_handler_init(&priv->hdl, 2); |
711 | v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops, | |
712 | V4L2_CID_VFLIP, 0, 1, 1, 0); | |
713 | v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops, | |
714 | V4L2_CID_HFLIP, 0, 1, 1, 0); | |
715 | priv->subdev.ctrl_handler = &priv->hdl; | |
716 | if (priv->hdl.error) { | |
717 | int err = priv->hdl.error; | |
718 | ||
719 | kfree(priv); | |
720 | return err; | |
721 | } | |
8d648271 | 722 | |
14178aa5 | 723 | ret = ov9640_video_probe(client); |
8d648271 MV |
724 | |
725 | if (ret) { | |
839b48df | 726 | v4l2_ctrl_handler_free(&priv->hdl); |
8d648271 MV |
727 | kfree(priv); |
728 | } | |
729 | ||
730 | return ret; | |
731 | } | |
732 | ||
733 | static int ov9640_remove(struct i2c_client *client) | |
734 | { | |
63c9718c DC |
735 | struct v4l2_subdev *sd = i2c_get_clientdata(client); |
736 | struct ov9640_priv *priv = to_ov9640_sensor(sd); | |
8d648271 | 737 | |
839b48df HV |
738 | v4l2_device_unregister_subdev(&priv->subdev); |
739 | v4l2_ctrl_handler_free(&priv->hdl); | |
8d648271 MV |
740 | kfree(priv); |
741 | return 0; | |
742 | } | |
743 | ||
744 | static const struct i2c_device_id ov9640_id[] = { | |
745 | { "ov9640", 0 }, | |
746 | { } | |
747 | }; | |
748 | MODULE_DEVICE_TABLE(i2c, ov9640_id); | |
749 | ||
750 | static struct i2c_driver ov9640_i2c_driver = { | |
751 | .driver = { | |
752 | .name = "ov9640", | |
753 | }, | |
754 | .probe = ov9640_probe, | |
755 | .remove = ov9640_remove, | |
756 | .id_table = ov9640_id, | |
757 | }; | |
758 | ||
c6e8d86f | 759 | module_i2c_driver(ov9640_i2c_driver); |
8d648271 MV |
760 | |
761 | MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV96xx"); | |
762 | MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); | |
763 | MODULE_LICENSE("GPL v2"); |