diff options
author | Marek Vasut <marek.vasut@gmail.com> | 2009-09-23 03:59:24 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2009-12-05 15:40:01 -0500 |
commit | 8d64827172ae680d34d0611a1e865b546e6a5f08 (patch) | |
tree | 8192517a82ae195c9534986b678af76c7b91f2ba /drivers | |
parent | 96fa2b508d2d3fe040cf4ef2fffb955f0a537ea1 (diff) |
V4L/DVB (13040): V4L2: Add a v4l2-subdev (soc-camera) driver for OmniVision OV9640 sensor
Signed-off-by: Marek Vasut <marek.vasut@gmail.com>
Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/media/video/Kconfig | 6 | ||||
-rw-r--r-- | drivers/media/video/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/video/ov9640.c | 801 | ||||
-rw-r--r-- | drivers/media/video/ov9640.h | 209 |
4 files changed, 1017 insertions, 0 deletions
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index e6186b338a12..c318676f2d29 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig | |||
@@ -865,6 +865,12 @@ config SOC_CAMERA_OV772X | |||
865 | help | 865 | help |
866 | This is a ov772x camera driver | 866 | This is a ov772x camera driver |
867 | 867 | ||
868 | config SOC_CAMERA_OV9640 | ||
869 | tristate "ov9640 camera support" | ||
870 | depends on SOC_CAMERA && I2C | ||
871 | help | ||
872 | This is a ov9640 camera driver | ||
873 | |||
868 | config MX1_VIDEO | 874 | config MX1_VIDEO |
869 | bool | 875 | bool |
870 | 876 | ||
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index e541932a789b..e706ceecef84 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile | |||
@@ -77,6 +77,7 @@ obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o | |||
77 | obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o | 77 | obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o |
78 | obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o | 78 | obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o |
79 | obj-$(CONFIG_SOC_CAMERA_OV772X) += ov772x.o | 79 | obj-$(CONFIG_SOC_CAMERA_OV772X) += ov772x.o |
80 | obj-$(CONFIG_SOC_CAMERA_OV9640) += ov9640.o | ||
80 | obj-$(CONFIG_SOC_CAMERA_TW9910) += tw9910.o | 81 | obj-$(CONFIG_SOC_CAMERA_TW9910) += tw9910.o |
81 | 82 | ||
82 | # And now the v4l2 drivers: | 83 | # And now the v4l2 drivers: |
diff --git a/drivers/media/video/ov9640.c b/drivers/media/video/ov9640.c new file mode 100644 index 000000000000..c81ae2192887 --- /dev/null +++ b/drivers/media/video/ov9640.c | |||
@@ -0,0 +1,801 @@ | |||
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> | ||
27 | #include <linux/videodev2.h> | ||
28 | #include <media/v4l2-chip-ident.h> | ||
29 | #include <media/v4l2-common.h> | ||
30 | #include <media/soc_camera.h> | ||
31 | |||
32 | #include "ov9640.h" | ||
33 | |||
34 | /* default register setup */ | ||
35 | static const struct ov9640_reg ov9640_regs_dflt[] = { | ||
36 | { OV9640_COM5, OV9640_COM5_SYSCLK | OV9640_COM5_LONGEXP }, | ||
37 | { OV9640_COM6, OV9640_COM6_OPT_BLC | OV9640_COM6_ADBLC_BIAS | | ||
38 | OV9640_COM6_FMT_RST | OV9640_COM6_ADBLC_OPTEN }, | ||
39 | { OV9640_PSHFT, OV9640_PSHFT_VAL(0x01) }, | ||
40 | { OV9640_ACOM, OV9640_ACOM_2X_ANALOG | OV9640_ACOM_RSVD }, | ||
41 | { OV9640_TSLB, OV9640_TSLB_YUYV_UYVY }, | ||
42 | { OV9640_COM16, OV9640_COM16_RB_AVG }, | ||
43 | |||
44 | /* Gamma curve P */ | ||
45 | { 0x6c, 0x40 }, { 0x6d, 0x30 }, { 0x6e, 0x4b }, { 0x6f, 0x60 }, | ||
46 | { 0x70, 0x70 }, { 0x71, 0x70 }, { 0x72, 0x70 }, { 0x73, 0x70 }, | ||
47 | { 0x74, 0x60 }, { 0x75, 0x60 }, { 0x76, 0x50 }, { 0x77, 0x48 }, | ||
48 | { 0x78, 0x3a }, { 0x79, 0x2e }, { 0x7a, 0x28 }, { 0x7b, 0x22 }, | ||
49 | |||
50 | /* Gamma curve T */ | ||
51 | { 0x7c, 0x04 }, { 0x7d, 0x07 }, { 0x7e, 0x10 }, { 0x7f, 0x28 }, | ||
52 | { 0x80, 0x36 }, { 0x81, 0x44 }, { 0x82, 0x52 }, { 0x83, 0x60 }, | ||
53 | { 0x84, 0x6c }, { 0x85, 0x78 }, { 0x86, 0x8c }, { 0x87, 0x9e }, | ||
54 | { 0x88, 0xbb }, { 0x89, 0xd2 }, { 0x8a, 0xe6 }, | ||
55 | }; | ||
56 | |||
57 | /* Configurations | ||
58 | * NOTE: for YUV, alter the following registers: | ||
59 | * COM12 |= OV9640_COM12_YUV_AVG | ||
60 | * | ||
61 | * for RGB, alter the following registers: | ||
62 | * COM7 |= OV9640_COM7_RGB | ||
63 | * COM13 |= OV9640_COM13_RGB_AVG | ||
64 | * COM15 |= proper RGB color encoding mode | ||
65 | */ | ||
66 | static const struct ov9640_reg ov9640_regs_qqcif[] = { | ||
67 | { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x0f) }, | ||
68 | { OV9640_COM1, OV9640_COM1_QQFMT | OV9640_COM1_HREF_2SKIP }, | ||
69 | { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, | ||
70 | { OV9640_COM7, OV9640_COM7_QCIF }, | ||
71 | { OV9640_COM12, OV9640_COM12_RSVD }, | ||
72 | { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, | ||
73 | { OV9640_COM15, OV9640_COM15_OR_10F0 }, | ||
74 | }; | ||
75 | |||
76 | static const struct ov9640_reg ov9640_regs_qqvga[] = { | ||
77 | { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x07) }, | ||
78 | { OV9640_COM1, OV9640_COM1_QQFMT | OV9640_COM1_HREF_2SKIP }, | ||
79 | { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, | ||
80 | { OV9640_COM7, OV9640_COM7_QVGA }, | ||
81 | { OV9640_COM12, OV9640_COM12_RSVD }, | ||
82 | { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, | ||
83 | { OV9640_COM15, OV9640_COM15_OR_10F0 }, | ||
84 | }; | ||
85 | |||
86 | static const struct ov9640_reg ov9640_regs_qcif[] = { | ||
87 | { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x07) }, | ||
88 | { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, | ||
89 | { OV9640_COM7, OV9640_COM7_QCIF }, | ||
90 | { OV9640_COM12, OV9640_COM12_RSVD }, | ||
91 | { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, | ||
92 | { OV9640_COM15, OV9640_COM15_OR_10F0 }, | ||
93 | }; | ||
94 | |||
95 | static const struct ov9640_reg ov9640_regs_qvga[] = { | ||
96 | { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x03) }, | ||
97 | { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, | ||
98 | { OV9640_COM7, OV9640_COM7_QVGA }, | ||
99 | { OV9640_COM12, OV9640_COM12_RSVD }, | ||
100 | { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, | ||
101 | { OV9640_COM15, OV9640_COM15_OR_10F0 }, | ||
102 | }; | ||
103 | |||
104 | static const struct ov9640_reg ov9640_regs_cif[] = { | ||
105 | { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x03) }, | ||
106 | { OV9640_COM3, OV9640_COM3_VP }, | ||
107 | { OV9640_COM7, OV9640_COM7_CIF }, | ||
108 | { OV9640_COM12, OV9640_COM12_RSVD }, | ||
109 | { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, | ||
110 | { OV9640_COM15, OV9640_COM15_OR_10F0 }, | ||
111 | }; | ||
112 | |||
113 | static const struct ov9640_reg ov9640_regs_vga[] = { | ||
114 | { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x01) }, | ||
115 | { OV9640_COM3, OV9640_COM3_VP }, | ||
116 | { OV9640_COM7, OV9640_COM7_VGA }, | ||
117 | { OV9640_COM12, OV9640_COM12_RSVD }, | ||
118 | { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, | ||
119 | { OV9640_COM15, OV9640_COM15_OR_10F0 }, | ||
120 | }; | ||
121 | |||
122 | static const struct ov9640_reg ov9640_regs_sxga[] = { | ||
123 | { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x01) }, | ||
124 | { OV9640_COM3, OV9640_COM3_VP }, | ||
125 | { OV9640_COM7, 0 }, | ||
126 | { OV9640_COM12, OV9640_COM12_RSVD }, | ||
127 | { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, | ||
128 | { OV9640_COM15, OV9640_COM15_OR_10F0 }, | ||
129 | }; | ||
130 | |||
131 | static const struct ov9640_reg ov9640_regs_yuv[] = { | ||
132 | { OV9640_MTX1, 0x58 }, | ||
133 | { OV9640_MTX2, 0x48 }, | ||
134 | { OV9640_MTX3, 0x10 }, | ||
135 | { OV9640_MTX4, 0x28 }, | ||
136 | { OV9640_MTX5, 0x48 }, | ||
137 | { OV9640_MTX6, 0x70 }, | ||
138 | { OV9640_MTX7, 0x40 }, | ||
139 | { OV9640_MTX8, 0x40 }, | ||
140 | { OV9640_MTX9, 0x40 }, | ||
141 | { OV9640_MTXS, 0x0f }, | ||
142 | }; | ||
143 | |||
144 | static const struct ov9640_reg ov9640_regs_rgb[] = { | ||
145 | { OV9640_MTX1, 0x71 }, | ||
146 | { OV9640_MTX2, 0x3e }, | ||
147 | { OV9640_MTX3, 0x0c }, | ||
148 | { OV9640_MTX4, 0x33 }, | ||
149 | { OV9640_MTX5, 0x72 }, | ||
150 | { OV9640_MTX6, 0x00 }, | ||
151 | { OV9640_MTX7, 0x2b }, | ||
152 | { OV9640_MTX8, 0x66 }, | ||
153 | { OV9640_MTX9, 0xd2 }, | ||
154 | { OV9640_MTXS, 0x65 }, | ||
155 | }; | ||
156 | |||
157 | /* | ||
158 | * TODO: this sensor also supports RGB555 and RGB565 formats, but support for | ||
159 | * them has not yet been sufficiently tested and so it is not included with | ||
160 | * this version of the driver. To test and debug these formats add two entries | ||
161 | * to the below array, see ov722x.c for an example. | ||
162 | */ | ||
163 | static const struct soc_camera_data_format ov9640_fmt_lists[] = { | ||
164 | { | ||
165 | .name = "UYVY", | ||
166 | .fourcc = V4L2_PIX_FMT_UYVY, | ||
167 | .depth = 16, | ||
168 | .colorspace = V4L2_COLORSPACE_JPEG, | ||
169 | }, | ||
170 | }; | ||
171 | |||
172 | static const struct v4l2_queryctrl ov9640_controls[] = { | ||
173 | { | ||
174 | .id = V4L2_CID_VFLIP, | ||
175 | .type = V4L2_CTRL_TYPE_BOOLEAN, | ||
176 | .name = "Flip Vertically", | ||
177 | .minimum = 0, | ||
178 | .maximum = 1, | ||
179 | .step = 1, | ||
180 | .default_value = 0, | ||
181 | }, | ||
182 | { | ||
183 | .id = V4L2_CID_HFLIP, | ||
184 | .type = V4L2_CTRL_TYPE_BOOLEAN, | ||
185 | .name = "Flip Horizontally", | ||
186 | .minimum = 0, | ||
187 | .maximum = 1, | ||
188 | .step = 1, | ||
189 | .default_value = 0, | ||
190 | }, | ||
191 | }; | ||
192 | |||
193 | /* read a register */ | ||
194 | static int ov9640_reg_read(struct i2c_client *client, u8 reg, u8 *val) | ||
195 | { | ||
196 | int ret; | ||
197 | u8 data = reg; | ||
198 | struct i2c_msg msg = { | ||
199 | .addr = client->addr, | ||
200 | .flags = 0, | ||
201 | .len = 1, | ||
202 | .buf = &data, | ||
203 | }; | ||
204 | |||
205 | ret = i2c_transfer(client->adapter, &msg, 1); | ||
206 | if (ret < 0) | ||
207 | goto err; | ||
208 | |||
209 | msg.flags = I2C_M_RD; | ||
210 | ret = i2c_transfer(client->adapter, &msg, 1); | ||
211 | if (ret < 0) | ||
212 | goto err; | ||
213 | |||
214 | *val = data; | ||
215 | return 0; | ||
216 | |||
217 | err: | ||
218 | dev_err(&client->dev, "Failed reading register 0x%02x!\n", reg); | ||
219 | return ret; | ||
220 | } | ||
221 | |||
222 | /* write a register */ | ||
223 | static int ov9640_reg_write(struct i2c_client *client, u8 reg, u8 val) | ||
224 | { | ||
225 | int ret; | ||
226 | u8 _val; | ||
227 | unsigned char data[2] = { reg, val }; | ||
228 | struct i2c_msg msg = { | ||
229 | .addr = client->addr, | ||
230 | .flags = 0, | ||
231 | .len = 2, | ||
232 | .buf = data, | ||
233 | }; | ||
234 | |||
235 | ret = i2c_transfer(client->adapter, &msg, 1); | ||
236 | if (ret < 0) { | ||
237 | dev_err(&client->dev, "Failed writing register 0x%02x!\n", reg); | ||
238 | return ret; | ||
239 | } | ||
240 | |||
241 | /* we have to read the register back ... no idea why, maybe HW bug */ | ||
242 | ret = ov9640_reg_read(client, reg, &_val); | ||
243 | if (ret) | ||
244 | dev_err(&client->dev, | ||
245 | "Failed reading back register 0x%02x!\n", reg); | ||
246 | |||
247 | return 0; | ||
248 | } | ||
249 | |||
250 | |||
251 | /* Read a register, alter its bits, write it back */ | ||
252 | static int ov9640_reg_rmw(struct i2c_client *client, u8 reg, u8 set, u8 unset) | ||
253 | { | ||
254 | u8 val; | ||
255 | int ret; | ||
256 | |||
257 | ret = ov9640_reg_read(client, reg, &val); | ||
258 | if (ret) { | ||
259 | dev_err(&client->dev, | ||
260 | "[Read]-Modify-Write of register %02x failed!\n", reg); | ||
261 | return val; | ||
262 | } | ||
263 | |||
264 | val |= set; | ||
265 | val &= ~unset; | ||
266 | |||
267 | ret = ov9640_reg_write(client, reg, val); | ||
268 | if (ret) | ||
269 | dev_err(&client->dev, | ||
270 | "Read-Modify-[Write] of register %02x failed!\n", reg); | ||
271 | |||
272 | return ret; | ||
273 | } | ||
274 | |||
275 | /* Soft reset the camera. This has nothing to do with the RESET pin! */ | ||
276 | static int ov9640_reset(struct i2c_client *client) | ||
277 | { | ||
278 | int ret; | ||
279 | |||
280 | ret = ov9640_reg_write(client, OV9640_COM7, OV9640_COM7_SCCB_RESET); | ||
281 | if (ret) | ||
282 | dev_err(&client->dev, | ||
283 | "An error occured while entering soft reset!\n"); | ||
284 | |||
285 | return ret; | ||
286 | } | ||
287 | |||
288 | /* Start/Stop streaming from the device */ | ||
289 | static int ov9640_s_stream(struct v4l2_subdev *sd, int enable) | ||
290 | { | ||
291 | return 0; | ||
292 | } | ||
293 | |||
294 | /* Alter bus settings on camera side */ | ||
295 | static int ov9640_set_bus_param(struct soc_camera_device *icd, | ||
296 | unsigned long flags) | ||
297 | { | ||
298 | return 0; | ||
299 | } | ||
300 | |||
301 | /* Request bus settings on camera side */ | ||
302 | static unsigned long ov9640_query_bus_param(struct soc_camera_device *icd) | ||
303 | { | ||
304 | struct soc_camera_link *icl = to_soc_camera_link(icd); | ||
305 | |||
306 | /* | ||
307 | * REVISIT: the camera probably can do 10 bit transfers, but I don't | ||
308 | * have those pins connected on my hardware. | ||
309 | */ | ||
310 | unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER | | ||
311 | SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH | | ||
312 | SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8; | ||
313 | |||
314 | return soc_camera_apply_sensor_flags(icl, flags); | ||
315 | } | ||
316 | |||
317 | /* Get status of additional camera capabilities */ | ||
318 | static int ov9640_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) | ||
319 | { | ||
320 | struct i2c_client *client = sd->priv; | ||
321 | struct ov9640_priv *priv = container_of(i2c_get_clientdata(client), | ||
322 | struct ov9640_priv, subdev); | ||
323 | |||
324 | switch (ctrl->id) { | ||
325 | case V4L2_CID_VFLIP: | ||
326 | ctrl->value = priv->flag_vflip; | ||
327 | break; | ||
328 | case V4L2_CID_HFLIP: | ||
329 | ctrl->value = priv->flag_hflip; | ||
330 | break; | ||
331 | } | ||
332 | return 0; | ||
333 | } | ||
334 | |||
335 | /* Set status of additional camera capabilities */ | ||
336 | static int ov9640_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) | ||
337 | { | ||
338 | struct i2c_client *client = sd->priv; | ||
339 | struct ov9640_priv *priv = container_of(i2c_get_clientdata(client), | ||
340 | struct ov9640_priv, subdev); | ||
341 | |||
342 | int ret = 0; | ||
343 | |||
344 | switch (ctrl->id) { | ||
345 | case V4L2_CID_VFLIP: | ||
346 | priv->flag_vflip = ctrl->value; | ||
347 | if (ctrl->value) | ||
348 | ret = ov9640_reg_rmw(client, OV9640_MVFP, | ||
349 | OV9640_MVFP_V, 0); | ||
350 | else | ||
351 | ret = ov9640_reg_rmw(client, OV9640_MVFP, | ||
352 | 0, OV9640_MVFP_V); | ||
353 | break; | ||
354 | case V4L2_CID_HFLIP: | ||
355 | priv->flag_hflip = ctrl->value; | ||
356 | if (ctrl->value) | ||
357 | ret = ov9640_reg_rmw(client, OV9640_MVFP, | ||
358 | OV9640_MVFP_H, 0); | ||
359 | else | ||
360 | ret = ov9640_reg_rmw(client, OV9640_MVFP, | ||
361 | 0, OV9640_MVFP_H); | ||
362 | break; | ||
363 | } | ||
364 | |||
365 | return ret; | ||
366 | } | ||
367 | |||
368 | /* Get chip identification */ | ||
369 | static int ov9640_g_chip_ident(struct v4l2_subdev *sd, | ||
370 | struct v4l2_dbg_chip_ident *id) | ||
371 | { | ||
372 | struct i2c_client *client = sd->priv; | ||
373 | struct ov9640_priv *priv = container_of(i2c_get_clientdata(client), | ||
374 | struct ov9640_priv, subdev); | ||
375 | |||
376 | id->ident = priv->model; | ||
377 | id->revision = priv->revision; | ||
378 | |||
379 | return 0; | ||
380 | } | ||
381 | |||
382 | #ifdef CONFIG_VIDEO_ADV_DEBUG | ||
383 | static int ov9640_get_register(struct v4l2_subdev *sd, | ||
384 | struct v4l2_dbg_register *reg) | ||
385 | { | ||
386 | struct i2c_client *client = sd->priv; | ||
387 | int ret; | ||
388 | u8 val; | ||
389 | |||
390 | if (reg->reg & ~0xff) | ||
391 | return -EINVAL; | ||
392 | |||
393 | reg->size = 1; | ||
394 | |||
395 | ret = ov9640_reg_read(client, reg->reg, &val); | ||
396 | if (ret) | ||
397 | return ret; | ||
398 | |||
399 | reg->val = (__u64)val; | ||
400 | |||
401 | return 0; | ||
402 | } | ||
403 | |||
404 | static int ov9640_set_register(struct v4l2_subdev *sd, | ||
405 | struct v4l2_dbg_register *reg) | ||
406 | { | ||
407 | struct i2c_client *client = sd->priv; | ||
408 | |||
409 | if (reg->reg & ~0xff || reg->val & ~0xff) | ||
410 | return -EINVAL; | ||
411 | |||
412 | return ov9640_reg_write(client, reg->reg, reg->val); | ||
413 | } | ||
414 | #endif | ||
415 | |||
416 | /* select nearest higher resolution for capture */ | ||
417 | static void ov9640_res_roundup(u32 *width, u32 *height) | ||
418 | { | ||
419 | int i; | ||
420 | enum { QQCIF, QQVGA, QCIF, QVGA, CIF, VGA, SXGA }; | ||
421 | int res_x[] = { 88, 160, 176, 320, 352, 640, 1280 }; | ||
422 | int res_y[] = { 72, 120, 144, 240, 288, 480, 960 }; | ||
423 | |||
424 | for (i = 0; i < ARRAY_SIZE(res_x); i++) { | ||
425 | if (res_x[i] >= *width && res_y[i] >= *height) { | ||
426 | *width = res_x[i]; | ||
427 | *height = res_y[i]; | ||
428 | return; | ||
429 | } | ||
430 | } | ||
431 | |||
432 | *width = res_x[SXGA]; | ||
433 | *height = res_y[SXGA]; | ||
434 | } | ||
435 | |||
436 | /* Prepare necessary register changes depending on color encoding */ | ||
437 | static void ov9640_alter_regs(u32 pixfmt, struct ov9640_reg_alt *alt) | ||
438 | { | ||
439 | switch (pixfmt) { | ||
440 | case V4L2_PIX_FMT_UYVY: | ||
441 | alt->com12 = OV9640_COM12_YUV_AVG; | ||
442 | alt->com13 = OV9640_COM13_Y_DELAY_EN | | ||
443 | OV9640_COM13_YUV_DLY(0x01); | ||
444 | break; | ||
445 | case V4L2_PIX_FMT_RGB555: | ||
446 | alt->com7 = OV9640_COM7_RGB; | ||
447 | alt->com13 = OV9640_COM13_RGB_AVG; | ||
448 | alt->com15 = OV9640_COM15_RGB_555; | ||
449 | break; | ||
450 | case V4L2_PIX_FMT_RGB565: | ||
451 | alt->com7 = OV9640_COM7_RGB; | ||
452 | alt->com13 = OV9640_COM13_RGB_AVG; | ||
453 | alt->com15 = OV9640_COM15_RGB_565; | ||
454 | break; | ||
455 | }; | ||
456 | } | ||
457 | |||
458 | /* Setup registers according to resolution and color encoding */ | ||
459 | static int ov9640_write_regs(struct i2c_client *client, | ||
460 | u32 width, u32 pixfmt, struct ov9640_reg_alt *alts) | ||
461 | { | ||
462 | const struct ov9640_reg *ov9640_regs, *matrix_regs; | ||
463 | int ov9640_regs_len, matrix_regs_len; | ||
464 | int i, ret; | ||
465 | u8 val; | ||
466 | |||
467 | /* select register configuration for given resolution */ | ||
468 | switch (width) { | ||
469 | case W_QQCIF: | ||
470 | ov9640_regs = ov9640_regs_qqcif; | ||
471 | ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qqcif); | ||
472 | break; | ||
473 | case W_QQVGA: | ||
474 | ov9640_regs = ov9640_regs_qqvga; | ||
475 | ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qqvga); | ||
476 | break; | ||
477 | case W_QCIF: | ||
478 | ov9640_regs = ov9640_regs_qcif; | ||
479 | ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qcif); | ||
480 | break; | ||
481 | case W_QVGA: | ||
482 | ov9640_regs = ov9640_regs_qvga; | ||
483 | ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qvga); | ||
484 | break; | ||
485 | case W_CIF: | ||
486 | ov9640_regs = ov9640_regs_cif; | ||
487 | ov9640_regs_len = ARRAY_SIZE(ov9640_regs_cif); | ||
488 | break; | ||
489 | case W_VGA: | ||
490 | ov9640_regs = ov9640_regs_vga; | ||
491 | ov9640_regs_len = ARRAY_SIZE(ov9640_regs_vga); | ||
492 | break; | ||
493 | case W_SXGA: | ||
494 | ov9640_regs = ov9640_regs_sxga; | ||
495 | ov9640_regs_len = ARRAY_SIZE(ov9640_regs_sxga); | ||
496 | break; | ||
497 | default: | ||
498 | dev_err(&client->dev, "Failed to select resolution!\n"); | ||
499 | return -EINVAL; | ||
500 | } | ||
501 | |||
502 | /* select color matrix configuration for given color encoding */ | ||
503 | if (pixfmt == V4L2_PIX_FMT_UYVY) { | ||
504 | matrix_regs = ov9640_regs_yuv; | ||
505 | matrix_regs_len = ARRAY_SIZE(ov9640_regs_yuv); | ||
506 | } else { | ||
507 | matrix_regs = ov9640_regs_rgb; | ||
508 | matrix_regs_len = ARRAY_SIZE(ov9640_regs_rgb); | ||
509 | } | ||
510 | |||
511 | /* write register settings into the module */ | ||
512 | for (i = 0; i < ov9640_regs_len; i++) { | ||
513 | val = ov9640_regs[i].val; | ||
514 | |||
515 | switch (ov9640_regs[i].reg) { | ||
516 | case OV9640_COM7: | ||
517 | val |= alts->com7; | ||
518 | break; | ||
519 | case OV9640_COM12: | ||
520 | val |= alts->com12; | ||
521 | break; | ||
522 | case OV9640_COM13: | ||
523 | val |= alts->com13; | ||
524 | break; | ||
525 | case OV9640_COM15: | ||
526 | val |= alts->com15; | ||
527 | break; | ||
528 | } | ||
529 | |||
530 | ret = ov9640_reg_write(client, ov9640_regs[i].reg, val); | ||
531 | if (ret) | ||
532 | return ret; | ||
533 | } | ||
534 | |||
535 | /* write color matrix configuration into the module */ | ||
536 | for (i = 0; i < matrix_regs_len; i++) { | ||
537 | ret = ov9640_reg_write(client, matrix_regs[i].reg, | ||
538 | matrix_regs[i].val); | ||
539 | if (ret) | ||
540 | return ret; | ||
541 | } | ||
542 | |||
543 | return 0; | ||
544 | } | ||
545 | |||
546 | /* program default register values */ | ||
547 | static int ov9640_prog_dflt(struct i2c_client *client) | ||
548 | { | ||
549 | int i, ret; | ||
550 | |||
551 | for (i = 0; i < ARRAY_SIZE(ov9640_regs_dflt); i++) { | ||
552 | ret = ov9640_reg_write(client, ov9640_regs_dflt[i].reg, | ||
553 | ov9640_regs_dflt[i].val); | ||
554 | if (ret) | ||
555 | return ret; | ||
556 | } | ||
557 | |||
558 | /* wait for the changes to actually happen, 140ms are not enough yet */ | ||
559 | mdelay(150); | ||
560 | |||
561 | return 0; | ||
562 | } | ||
563 | |||
564 | /* set the format we will capture in */ | ||
565 | static int ov9640_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) | ||
566 | { | ||
567 | struct i2c_client *client = sd->priv; | ||
568 | struct v4l2_pix_format *pix = &f->fmt.pix; | ||
569 | struct ov9640_reg_alt alts = {0}; | ||
570 | int ret; | ||
571 | |||
572 | ov9640_res_roundup(&pix->width, &pix->height); | ||
573 | ov9640_alter_regs(pix->pixelformat, &alts); | ||
574 | |||
575 | ov9640_reset(client); | ||
576 | |||
577 | ret = ov9640_prog_dflt(client); | ||
578 | if (ret) | ||
579 | return ret; | ||
580 | |||
581 | return ov9640_write_regs(client, pix->width, pix->pixelformat, &alts); | ||
582 | } | ||
583 | |||
584 | static int ov9640_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) | ||
585 | { | ||
586 | struct v4l2_pix_format *pix = &f->fmt.pix; | ||
587 | |||
588 | ov9640_res_roundup(&pix->width, &pix->height); | ||
589 | pix->field = V4L2_FIELD_NONE; | ||
590 | |||
591 | return 0; | ||
592 | } | ||
593 | |||
594 | static int ov9640_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) | ||
595 | { | ||
596 | a->c.left = 0; | ||
597 | a->c.top = 0; | ||
598 | a->c.width = W_SXGA; | ||
599 | a->c.height = H_SXGA; | ||
600 | a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
601 | |||
602 | return 0; | ||
603 | } | ||
604 | |||
605 | static int ov9640_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) | ||
606 | { | ||
607 | a->bounds.left = 0; | ||
608 | a->bounds.top = 0; | ||
609 | a->bounds.width = W_SXGA; | ||
610 | a->bounds.height = H_SXGA; | ||
611 | a->defrect = a->bounds; | ||
612 | a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
613 | a->pixelaspect.numerator = 1; | ||
614 | a->pixelaspect.denominator = 1; | ||
615 | |||
616 | return 0; | ||
617 | } | ||
618 | |||
619 | |||
620 | |||
621 | static int ov9640_video_probe(struct soc_camera_device *icd, | ||
622 | struct i2c_client *client) | ||
623 | { | ||
624 | struct ov9640_priv *priv = i2c_get_clientdata(client); | ||
625 | u8 pid, ver, midh, midl; | ||
626 | const char *devname; | ||
627 | int ret = 0; | ||
628 | |||
629 | /* | ||
630 | * We must have a parent by now. And it cannot be a wrong one. | ||
631 | * So this entire test is completely redundant. | ||
632 | */ | ||
633 | if (!icd->dev.parent || | ||
634 | to_soc_camera_host(icd->dev.parent)->nr != icd->iface) { | ||
635 | dev_err(&client->dev, "Parent missing or invalid!\n"); | ||
636 | ret = -ENODEV; | ||
637 | goto err; | ||
638 | } | ||
639 | |||
640 | icd->formats = ov9640_fmt_lists; | ||
641 | icd->num_formats = ARRAY_SIZE(ov9640_fmt_lists); | ||
642 | |||
643 | /* | ||
644 | * check and show product ID and manufacturer ID | ||
645 | */ | ||
646 | |||
647 | ret = ov9640_reg_read(client, OV9640_PID, &pid); | ||
648 | if (ret) | ||
649 | goto err; | ||
650 | |||
651 | ret = ov9640_reg_read(client, OV9640_VER, &ver); | ||
652 | if (ret) | ||
653 | goto err; | ||
654 | |||
655 | ret = ov9640_reg_read(client, OV9640_MIDH, &midh); | ||
656 | if (ret) | ||
657 | goto err; | ||
658 | |||
659 | ret = ov9640_reg_read(client, OV9640_MIDL, &midl); | ||
660 | if (ret) | ||
661 | goto err; | ||
662 | |||
663 | switch (VERSION(pid, ver)) { | ||
664 | case OV9640_V2: | ||
665 | devname = "ov9640"; | ||
666 | priv->model = V4L2_IDENT_OV9640; | ||
667 | priv->revision = 2; | ||
668 | case OV9640_V3: | ||
669 | devname = "ov9640"; | ||
670 | priv->model = V4L2_IDENT_OV9640; | ||
671 | priv->revision = 3; | ||
672 | break; | ||
673 | default: | ||
674 | dev_err(&client->dev, "Product ID error %x:%x\n", pid, ver); | ||
675 | ret = -ENODEV; | ||
676 | goto err; | ||
677 | } | ||
678 | |||
679 | dev_info(&client->dev, "%s Product ID %0x:%0x Manufacturer ID %x:%x\n", | ||
680 | devname, pid, ver, midh, midl); | ||
681 | |||
682 | err: | ||
683 | return ret; | ||
684 | } | ||
685 | |||
686 | static struct soc_camera_ops ov9640_ops = { | ||
687 | .set_bus_param = ov9640_set_bus_param, | ||
688 | .query_bus_param = ov9640_query_bus_param, | ||
689 | .controls = ov9640_controls, | ||
690 | .num_controls = ARRAY_SIZE(ov9640_controls), | ||
691 | }; | ||
692 | |||
693 | static struct v4l2_subdev_core_ops ov9640_core_ops = { | ||
694 | .g_ctrl = ov9640_g_ctrl, | ||
695 | .s_ctrl = ov9640_s_ctrl, | ||
696 | .g_chip_ident = ov9640_g_chip_ident, | ||
697 | #ifdef CONFIG_VIDEO_ADV_DEBUG | ||
698 | .g_register = ov9640_get_register, | ||
699 | .s_register = ov9640_set_register, | ||
700 | #endif | ||
701 | |||
702 | }; | ||
703 | |||
704 | static struct v4l2_subdev_video_ops ov9640_video_ops = { | ||
705 | .s_stream = ov9640_s_stream, | ||
706 | .s_fmt = ov9640_s_fmt, | ||
707 | .try_fmt = ov9640_try_fmt, | ||
708 | .cropcap = ov9640_cropcap, | ||
709 | .g_crop = ov9640_g_crop, | ||
710 | |||
711 | }; | ||
712 | |||
713 | static struct v4l2_subdev_ops ov9640_subdev_ops = { | ||
714 | .core = &ov9640_core_ops, | ||
715 | .video = &ov9640_video_ops, | ||
716 | }; | ||
717 | |||
718 | /* | ||
719 | * i2c_driver function | ||
720 | */ | ||
721 | static int ov9640_probe(struct i2c_client *client, | ||
722 | const struct i2c_device_id *did) | ||
723 | { | ||
724 | struct ov9640_priv *priv; | ||
725 | struct soc_camera_device *icd = client->dev.platform_data; | ||
726 | struct soc_camera_link *icl; | ||
727 | int ret; | ||
728 | |||
729 | if (!icd) { | ||
730 | dev_err(&client->dev, "Missing soc-camera data!\n"); | ||
731 | return -EINVAL; | ||
732 | } | ||
733 | |||
734 | icl = to_soc_camera_link(icd); | ||
735 | if (!icl) { | ||
736 | dev_err(&client->dev, "Missing platform_data for driver\n"); | ||
737 | return -EINVAL; | ||
738 | } | ||
739 | |||
740 | priv = kzalloc(sizeof(struct ov9640_priv), GFP_KERNEL); | ||
741 | if (!priv) { | ||
742 | dev_err(&client->dev, | ||
743 | "Failed to allocate memory for private data!\n"); | ||
744 | return -ENOMEM; | ||
745 | } | ||
746 | |||
747 | v4l2_i2c_subdev_init(&priv->subdev, client, &ov9640_subdev_ops); | ||
748 | |||
749 | icd->ops = &ov9640_ops; | ||
750 | |||
751 | ret = ov9640_video_probe(icd, client); | ||
752 | |||
753 | if (ret) { | ||
754 | icd->ops = NULL; | ||
755 | i2c_set_clientdata(client, NULL); | ||
756 | kfree(priv); | ||
757 | } | ||
758 | |||
759 | return ret; | ||
760 | } | ||
761 | |||
762 | static int ov9640_remove(struct i2c_client *client) | ||
763 | { | ||
764 | struct ov9640_priv *priv = i2c_get_clientdata(client); | ||
765 | |||
766 | i2c_set_clientdata(client, NULL); | ||
767 | kfree(priv); | ||
768 | return 0; | ||
769 | } | ||
770 | |||
771 | static const struct i2c_device_id ov9640_id[] = { | ||
772 | { "ov9640", 0 }, | ||
773 | { } | ||
774 | }; | ||
775 | MODULE_DEVICE_TABLE(i2c, ov9640_id); | ||
776 | |||
777 | static struct i2c_driver ov9640_i2c_driver = { | ||
778 | .driver = { | ||
779 | .name = "ov9640", | ||
780 | }, | ||
781 | .probe = ov9640_probe, | ||
782 | .remove = ov9640_remove, | ||
783 | .id_table = ov9640_id, | ||
784 | }; | ||
785 | |||
786 | static int __init ov9640_module_init(void) | ||
787 | { | ||
788 | return i2c_add_driver(&ov9640_i2c_driver); | ||
789 | } | ||
790 | |||
791 | static void __exit ov9640_module_exit(void) | ||
792 | { | ||
793 | i2c_del_driver(&ov9640_i2c_driver); | ||
794 | } | ||
795 | |||
796 | module_init(ov9640_module_init); | ||
797 | module_exit(ov9640_module_exit); | ||
798 | |||
799 | MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV96xx"); | ||
800 | MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); | ||
801 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/media/video/ov9640.h b/drivers/media/video/ov9640.h new file mode 100644 index 000000000000..f8a51b70792e --- /dev/null +++ b/drivers/media/video/ov9640.h | |||
@@ -0,0 +1,209 @@ | |||
1 | /* | ||
2 | * OmniVision OV96xx Camera Header File | ||
3 | * | ||
4 | * Copyright (C) 2009 Marek Vasut <marek.vasut@gmail.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | #ifndef __DRIVERS_MEDIA_VIDEO_OV9640_H__ | ||
12 | #define __DRIVERS_MEDIA_VIDEO_OV9640_H__ | ||
13 | |||
14 | /* Register definitions */ | ||
15 | #define OV9640_GAIN 0x00 | ||
16 | #define OV9640_BLUE 0x01 | ||
17 | #define OV9640_RED 0x02 | ||
18 | #define OV9640_VFER 0x03 | ||
19 | #define OV9640_COM1 0x04 | ||
20 | #define OV9640_BAVE 0x05 | ||
21 | #define OV9640_GEAVE 0x06 | ||
22 | #define OV9640_RSID 0x07 | ||
23 | #define OV9640_RAVE 0x08 | ||
24 | #define OV9640_COM2 0x09 | ||
25 | #define OV9640_PID 0x0a | ||
26 | #define OV9640_VER 0x0b | ||
27 | #define OV9640_COM3 0x0c | ||
28 | #define OV9640_COM4 0x0d | ||
29 | #define OV9640_COM5 0x0e | ||
30 | #define OV9640_COM6 0x0f | ||
31 | #define OV9640_AECH 0x10 | ||
32 | #define OV9640_CLKRC 0x11 | ||
33 | #define OV9640_COM7 0x12 | ||
34 | #define OV9640_COM8 0x13 | ||
35 | #define OV9640_COM9 0x14 | ||
36 | #define OV9640_COM10 0x15 | ||
37 | /* 0x16 - RESERVED */ | ||
38 | #define OV9640_HSTART 0x17 | ||
39 | #define OV9640_HSTOP 0x18 | ||
40 | #define OV9640_VSTART 0x19 | ||
41 | #define OV9640_VSTOP 0x1a | ||
42 | #define OV9640_PSHFT 0x1b | ||
43 | #define OV9640_MIDH 0x1c | ||
44 | #define OV9640_MIDL 0x1d | ||
45 | #define OV9640_MVFP 0x1e | ||
46 | #define OV9640_LAEC 0x1f | ||
47 | #define OV9640_BOS 0x20 | ||
48 | #define OV9640_GBOS 0x21 | ||
49 | #define OV9640_GROS 0x22 | ||
50 | #define OV9640_ROS 0x23 | ||
51 | #define OV9640_AEW 0x24 | ||
52 | #define OV9640_AEB 0x25 | ||
53 | #define OV9640_VPT 0x26 | ||
54 | #define OV9640_BBIAS 0x27 | ||
55 | #define OV9640_GBBIAS 0x28 | ||
56 | /* 0x29 - RESERVED */ | ||
57 | #define OV9640_EXHCH 0x2a | ||
58 | #define OV9640_EXHCL 0x2b | ||
59 | #define OV9640_RBIAS 0x2c | ||
60 | #define OV9640_ADVFL 0x2d | ||
61 | #define OV9640_ADVFH 0x2e | ||
62 | #define OV9640_YAVE 0x2f | ||
63 | #define OV9640_HSYST 0x30 | ||
64 | #define OV9640_HSYEN 0x31 | ||
65 | #define OV9640_HREF 0x32 | ||
66 | #define OV9640_CHLF 0x33 | ||
67 | #define OV9640_ARBLM 0x34 | ||
68 | /* 0x35..0x36 - RESERVED */ | ||
69 | #define OV9640_ADC 0x37 | ||
70 | #define OV9640_ACOM 0x38 | ||
71 | #define OV9640_OFON 0x39 | ||
72 | #define OV9640_TSLB 0x3a | ||
73 | #define OV9640_COM11 0x3b | ||
74 | #define OV9640_COM12 0x3c | ||
75 | #define OV9640_COM13 0x3d | ||
76 | #define OV9640_COM14 0x3e | ||
77 | #define OV9640_EDGE 0x3f | ||
78 | #define OV9640_COM15 0x40 | ||
79 | #define OV9640_COM16 0x41 | ||
80 | #define OV9640_COM17 0x42 | ||
81 | /* 0x43..0x4e - RESERVED */ | ||
82 | #define OV9640_MTX1 0x4f | ||
83 | #define OV9640_MTX2 0x50 | ||
84 | #define OV9640_MTX3 0x51 | ||
85 | #define OV9640_MTX4 0x52 | ||
86 | #define OV9640_MTX5 0x53 | ||
87 | #define OV9640_MTX6 0x54 | ||
88 | #define OV9640_MTX7 0x55 | ||
89 | #define OV9640_MTX8 0x56 | ||
90 | #define OV9640_MTX9 0x57 | ||
91 | #define OV9640_MTXS 0x58 | ||
92 | /* 0x59..0x61 - RESERVED */ | ||
93 | #define OV9640_LCC1 0x62 | ||
94 | #define OV9640_LCC2 0x63 | ||
95 | #define OV9640_LCC3 0x64 | ||
96 | #define OV9640_LCC4 0x65 | ||
97 | #define OV9640_LCC5 0x66 | ||
98 | #define OV9640_MANU 0x67 | ||
99 | #define OV9640_MANV 0x68 | ||
100 | #define OV9640_HV 0x69 | ||
101 | #define OV9640_MBD 0x6a | ||
102 | #define OV9640_DBLV 0x6b | ||
103 | #define OV9640_GSP 0x6c /* ... till 0x7b */ | ||
104 | #define OV9640_GST 0x7c /* ... till 0x8a */ | ||
105 | |||
106 | #define OV9640_CLKRC_DPLL_EN 0x80 | ||
107 | #define OV9640_CLKRC_DIRECT 0x40 | ||
108 | #define OV9640_CLKRC_DIV(x) ((x) & 0x3f) | ||
109 | |||
110 | #define OV9640_PSHFT_VAL(x) ((x) & 0xff) | ||
111 | |||
112 | #define OV9640_ACOM_2X_ANALOG 0x80 | ||
113 | #define OV9640_ACOM_RSVD 0x12 | ||
114 | |||
115 | #define OV9640_MVFP_V 0x10 | ||
116 | #define OV9640_MVFP_H 0x20 | ||
117 | |||
118 | #define OV9640_COM1_HREF_NOSKIP 0x00 | ||
119 | #define OV9640_COM1_HREF_2SKIP 0x04 | ||
120 | #define OV9640_COM1_HREF_3SKIP 0x08 | ||
121 | #define OV9640_COM1_QQFMT 0x20 | ||
122 | |||
123 | #define OV9640_COM2_SSM 0x10 | ||
124 | |||
125 | #define OV9640_COM3_VP 0x04 | ||
126 | |||
127 | #define OV9640_COM4_QQ_VP 0x80 | ||
128 | #define OV9640_COM4_RSVD 0x40 | ||
129 | |||
130 | #define OV9640_COM5_SYSCLK 0x80 | ||
131 | #define OV9640_COM5_LONGEXP 0x01 | ||
132 | |||
133 | #define OV9640_COM6_OPT_BLC 0x40 | ||
134 | #define OV9640_COM6_ADBLC_BIAS 0x08 | ||
135 | #define OV9640_COM6_FMT_RST 0x82 | ||
136 | #define OV9640_COM6_ADBLC_OPTEN 0x01 | ||
137 | |||
138 | #define OV9640_COM7_RAW_RGB 0x01 | ||
139 | #define OV9640_COM7_RGB 0x04 | ||
140 | #define OV9640_COM7_QCIF 0x08 | ||
141 | #define OV9640_COM7_QVGA 0x10 | ||
142 | #define OV9640_COM7_CIF 0x20 | ||
143 | #define OV9640_COM7_VGA 0x40 | ||
144 | #define OV9640_COM7_SCCB_RESET 0x80 | ||
145 | |||
146 | #define OV9640_TSLB_YVYU_YUYV 0x04 | ||
147 | #define OV9640_TSLB_YUYV_UYVY 0x08 | ||
148 | |||
149 | #define OV9640_COM12_YUV_AVG 0x04 | ||
150 | #define OV9640_COM12_RSVD 0x40 | ||
151 | |||
152 | #define OV9640_COM13_GAMMA_NONE 0x00 | ||
153 | #define OV9640_COM13_GAMMA_Y 0x40 | ||
154 | #define OV9640_COM13_GAMMA_RAW 0x80 | ||
155 | #define OV9640_COM13_RGB_AVG 0x20 | ||
156 | #define OV9640_COM13_MATRIX_EN 0x10 | ||
157 | #define OV9640_COM13_Y_DELAY_EN 0x08 | ||
158 | #define OV9640_COM13_YUV_DLY(x) ((x) & 0x07) | ||
159 | |||
160 | #define OV9640_COM15_OR_00FF 0x00 | ||
161 | #define OV9640_COM15_OR_01FE 0x40 | ||
162 | #define OV9640_COM15_OR_10F0 0xc0 | ||
163 | #define OV9640_COM15_RGB_NORM 0x00 | ||
164 | #define OV9640_COM15_RGB_565 0x10 | ||
165 | #define OV9640_COM15_RGB_555 0x30 | ||
166 | |||
167 | #define OV9640_COM16_RB_AVG 0x01 | ||
168 | |||
169 | /* IDs */ | ||
170 | #define OV9640_V2 0x9648 | ||
171 | #define OV9640_V3 0x9649 | ||
172 | #define VERSION(pid, ver) (((pid) << 8) | ((ver) & 0xFF)) | ||
173 | |||
174 | /* supported resolutions */ | ||
175 | enum { | ||
176 | W_QQCIF = 88, | ||
177 | W_QQVGA = 160, | ||
178 | W_QCIF = 176, | ||
179 | W_QVGA = 320, | ||
180 | W_CIF = 352, | ||
181 | W_VGA = 640, | ||
182 | W_SXGA = 1280 | ||
183 | }; | ||
184 | #define H_SXGA 960 | ||
185 | |||
186 | /* Misc. structures */ | ||
187 | struct ov9640_reg_alt { | ||
188 | u8 com7; | ||
189 | u8 com12; | ||
190 | u8 com13; | ||
191 | u8 com15; | ||
192 | }; | ||
193 | |||
194 | struct ov9640_reg { | ||
195 | u8 reg; | ||
196 | u8 val; | ||
197 | }; | ||
198 | |||
199 | struct ov9640_priv { | ||
200 | struct v4l2_subdev subdev; | ||
201 | |||
202 | int model; | ||
203 | int revision; | ||
204 | |||
205 | bool flag_vflip; | ||
206 | bool flag_hflip; | ||
207 | }; | ||
208 | |||
209 | #endif /* __DRIVERS_MEDIA_VIDEO_OV9640_H__ */ | ||