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