diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/media/video/ovcamchip/ov6x30.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/media/video/ovcamchip/ov6x30.c')
-rw-r--r-- | drivers/media/video/ovcamchip/ov6x30.c | 374 |
1 files changed, 374 insertions, 0 deletions
diff --git a/drivers/media/video/ovcamchip/ov6x30.c b/drivers/media/video/ovcamchip/ov6x30.c new file mode 100644 index 000000000000..44a842379b45 --- /dev/null +++ b/drivers/media/video/ovcamchip/ov6x30.c | |||
@@ -0,0 +1,374 @@ | |||
1 | /* OmniVision OV6630/OV6130 Camera Chip Support Code | ||
2 | * | ||
3 | * Copyright (c) 1999-2004 Mark McClelland <mark@alpha.dyndns.org> | ||
4 | * http://alpha.dyndns.org/ov511/ | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms of the GNU General Public License as published by the | ||
8 | * Free Software Foundation; either version 2 of the License, or (at your | ||
9 | * option) any later version. NO WARRANTY OF ANY KIND is expressed or implied. | ||
10 | */ | ||
11 | |||
12 | #define DEBUG | ||
13 | |||
14 | #include <linux/slab.h> | ||
15 | #include "ovcamchip_priv.h" | ||
16 | |||
17 | /* Registers */ | ||
18 | #define REG_GAIN 0x00 /* gain [5:0] */ | ||
19 | #define REG_BLUE 0x01 /* blue gain */ | ||
20 | #define REG_RED 0x02 /* red gain */ | ||
21 | #define REG_SAT 0x03 /* saturation [7:3] */ | ||
22 | #define REG_CNT 0x05 /* Y contrast [3:0] */ | ||
23 | #define REG_BRT 0x06 /* Y brightness */ | ||
24 | #define REG_SHARP 0x07 /* sharpness */ | ||
25 | #define REG_WB_BLUE 0x0C /* WB blue ratio [5:0] */ | ||
26 | #define REG_WB_RED 0x0D /* WB red ratio [5:0] */ | ||
27 | #define REG_EXP 0x10 /* exposure */ | ||
28 | |||
29 | /* Window parameters */ | ||
30 | #define HWSBASE 0x38 | ||
31 | #define HWEBASE 0x3A | ||
32 | #define VWSBASE 0x05 | ||
33 | #define VWEBASE 0x06 | ||
34 | |||
35 | struct ov6x30 { | ||
36 | int auto_brt; | ||
37 | int auto_exp; | ||
38 | int backlight; | ||
39 | int bandfilt; | ||
40 | int mirror; | ||
41 | }; | ||
42 | |||
43 | static struct ovcamchip_regvals regvals_init_6x30[] = { | ||
44 | { 0x12, 0x80 }, /* reset */ | ||
45 | { 0x00, 0x1f }, /* Gain */ | ||
46 | { 0x01, 0x99 }, /* Blue gain */ | ||
47 | { 0x02, 0x7c }, /* Red gain */ | ||
48 | { 0x03, 0xc0 }, /* Saturation */ | ||
49 | { 0x05, 0x0a }, /* Contrast */ | ||
50 | { 0x06, 0x95 }, /* Brightness */ | ||
51 | { 0x07, 0x2d }, /* Sharpness */ | ||
52 | { 0x0c, 0x20 }, | ||
53 | { 0x0d, 0x20 }, | ||
54 | { 0x0e, 0x20 }, | ||
55 | { 0x0f, 0x05 }, | ||
56 | { 0x10, 0x9a }, /* "exposure check" */ | ||
57 | { 0x11, 0x00 }, /* Pixel clock = fastest */ | ||
58 | { 0x12, 0x24 }, /* Enable AGC and AWB */ | ||
59 | { 0x13, 0x21 }, | ||
60 | { 0x14, 0x80 }, | ||
61 | { 0x15, 0x01 }, | ||
62 | { 0x16, 0x03 }, | ||
63 | { 0x17, 0x38 }, | ||
64 | { 0x18, 0xea }, | ||
65 | { 0x19, 0x04 }, | ||
66 | { 0x1a, 0x93 }, | ||
67 | { 0x1b, 0x00 }, | ||
68 | { 0x1e, 0xc4 }, | ||
69 | { 0x1f, 0x04 }, | ||
70 | { 0x20, 0x20 }, | ||
71 | { 0x21, 0x10 }, | ||
72 | { 0x22, 0x88 }, | ||
73 | { 0x23, 0xc0 }, /* Crystal circuit power level */ | ||
74 | { 0x25, 0x9a }, /* Increase AEC black pixel ratio */ | ||
75 | { 0x26, 0xb2 }, /* BLC enable */ | ||
76 | { 0x27, 0xa2 }, | ||
77 | { 0x28, 0x00 }, | ||
78 | { 0x29, 0x00 }, | ||
79 | { 0x2a, 0x84 }, /* (keep) */ | ||
80 | { 0x2b, 0xa8 }, /* (keep) */ | ||
81 | { 0x2c, 0xa0 }, | ||
82 | { 0x2d, 0x95 }, /* Enable auto-brightness */ | ||
83 | { 0x2e, 0x88 }, | ||
84 | { 0x33, 0x26 }, | ||
85 | { 0x34, 0x03 }, | ||
86 | { 0x36, 0x8f }, | ||
87 | { 0x37, 0x80 }, | ||
88 | { 0x38, 0x83 }, | ||
89 | { 0x39, 0x80 }, | ||
90 | { 0x3a, 0x0f }, | ||
91 | { 0x3b, 0x3c }, | ||
92 | { 0x3c, 0x1a }, | ||
93 | { 0x3d, 0x80 }, | ||
94 | { 0x3e, 0x80 }, | ||
95 | { 0x3f, 0x0e }, | ||
96 | { 0x40, 0x00 }, /* White bal */ | ||
97 | { 0x41, 0x00 }, /* White bal */ | ||
98 | { 0x42, 0x80 }, | ||
99 | { 0x43, 0x3f }, /* White bal */ | ||
100 | { 0x44, 0x80 }, | ||
101 | { 0x45, 0x20 }, | ||
102 | { 0x46, 0x20 }, | ||
103 | { 0x47, 0x80 }, | ||
104 | { 0x48, 0x7f }, | ||
105 | { 0x49, 0x00 }, | ||
106 | { 0x4a, 0x00 }, | ||
107 | { 0x4b, 0x80 }, | ||
108 | { 0x4c, 0xd0 }, | ||
109 | { 0x4d, 0x10 }, /* U = 0.563u, V = 0.714v */ | ||
110 | { 0x4e, 0x40 }, | ||
111 | { 0x4f, 0x07 }, /* UV average mode, color killer: strongest */ | ||
112 | { 0x50, 0xff }, | ||
113 | { 0x54, 0x23 }, /* Max AGC gain: 18dB */ | ||
114 | { 0x55, 0xff }, | ||
115 | { 0x56, 0x12 }, | ||
116 | { 0x57, 0x81 }, /* (default) */ | ||
117 | { 0x58, 0x75 }, | ||
118 | { 0x59, 0x01 }, /* AGC dark current compensation: +1 */ | ||
119 | { 0x5a, 0x2c }, | ||
120 | { 0x5b, 0x0f }, /* AWB chrominance levels */ | ||
121 | { 0x5c, 0x10 }, | ||
122 | { 0x3d, 0x80 }, | ||
123 | { 0x27, 0xa6 }, | ||
124 | /* Toggle AWB off and on */ | ||
125 | { 0x12, 0x20 }, | ||
126 | { 0x12, 0x24 }, | ||
127 | |||
128 | { 0xff, 0xff }, /* END MARKER */ | ||
129 | }; | ||
130 | |||
131 | /* This initializes the OV6x30 camera chip and relevant variables. */ | ||
132 | static int ov6x30_init(struct i2c_client *c) | ||
133 | { | ||
134 | struct ovcamchip *ov = i2c_get_clientdata(c); | ||
135 | struct ov6x30 *s; | ||
136 | int rc; | ||
137 | |||
138 | DDEBUG(4, &c->dev, "entered"); | ||
139 | |||
140 | rc = ov_write_regvals(c, regvals_init_6x30); | ||
141 | if (rc < 0) | ||
142 | return rc; | ||
143 | |||
144 | ov->spriv = s = kmalloc(sizeof *s, GFP_KERNEL); | ||
145 | if (!s) | ||
146 | return -ENOMEM; | ||
147 | memset(s, 0, sizeof *s); | ||
148 | |||
149 | s->auto_brt = 1; | ||
150 | s->auto_exp = 1; | ||
151 | |||
152 | return rc; | ||
153 | } | ||
154 | |||
155 | static int ov6x30_free(struct i2c_client *c) | ||
156 | { | ||
157 | struct ovcamchip *ov = i2c_get_clientdata(c); | ||
158 | |||
159 | kfree(ov->spriv); | ||
160 | return 0; | ||
161 | } | ||
162 | |||
163 | static int ov6x30_set_control(struct i2c_client *c, | ||
164 | struct ovcamchip_control *ctl) | ||
165 | { | ||
166 | struct ovcamchip *ov = i2c_get_clientdata(c); | ||
167 | struct ov6x30 *s = ov->spriv; | ||
168 | int rc; | ||
169 | int v = ctl->value; | ||
170 | |||
171 | switch (ctl->id) { | ||
172 | case OVCAMCHIP_CID_CONT: | ||
173 | rc = ov_write_mask(c, REG_CNT, v >> 12, 0x0f); | ||
174 | break; | ||
175 | case OVCAMCHIP_CID_BRIGHT: | ||
176 | rc = ov_write(c, REG_BRT, v >> 8); | ||
177 | break; | ||
178 | case OVCAMCHIP_CID_SAT: | ||
179 | rc = ov_write(c, REG_SAT, v >> 8); | ||
180 | break; | ||
181 | case OVCAMCHIP_CID_HUE: | ||
182 | rc = ov_write(c, REG_RED, 0xFF - (v >> 8)); | ||
183 | if (rc < 0) | ||
184 | goto out; | ||
185 | |||
186 | rc = ov_write(c, REG_BLUE, v >> 8); | ||
187 | break; | ||
188 | case OVCAMCHIP_CID_EXP: | ||
189 | rc = ov_write(c, REG_EXP, v); | ||
190 | break; | ||
191 | case OVCAMCHIP_CID_FREQ: | ||
192 | { | ||
193 | int sixty = (v == 60); | ||
194 | |||
195 | rc = ov_write(c, 0x2b, sixty?0xa8:0x28); | ||
196 | if (rc < 0) | ||
197 | goto out; | ||
198 | |||
199 | rc = ov_write(c, 0x2a, sixty?0x84:0xa4); | ||
200 | break; | ||
201 | } | ||
202 | case OVCAMCHIP_CID_BANDFILT: | ||
203 | rc = ov_write_mask(c, 0x2d, v?0x04:0x00, 0x04); | ||
204 | s->bandfilt = v; | ||
205 | break; | ||
206 | case OVCAMCHIP_CID_AUTOBRIGHT: | ||
207 | rc = ov_write_mask(c, 0x2d, v?0x10:0x00, 0x10); | ||
208 | s->auto_brt = v; | ||
209 | break; | ||
210 | case OVCAMCHIP_CID_AUTOEXP: | ||
211 | rc = ov_write_mask(c, 0x28, v?0x00:0x10, 0x10); | ||
212 | s->auto_exp = v; | ||
213 | break; | ||
214 | case OVCAMCHIP_CID_BACKLIGHT: | ||
215 | { | ||
216 | rc = ov_write_mask(c, 0x4e, v?0x80:0x60, 0xe0); | ||
217 | if (rc < 0) | ||
218 | goto out; | ||
219 | |||
220 | rc = ov_write_mask(c, 0x29, v?0x08:0x00, 0x08); | ||
221 | if (rc < 0) | ||
222 | goto out; | ||
223 | |||
224 | rc = ov_write_mask(c, 0x28, v?0x02:0x00, 0x02); | ||
225 | s->backlight = v; | ||
226 | break; | ||
227 | } | ||
228 | case OVCAMCHIP_CID_MIRROR: | ||
229 | rc = ov_write_mask(c, 0x12, v?0x40:0x00, 0x40); | ||
230 | s->mirror = v; | ||
231 | break; | ||
232 | default: | ||
233 | DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); | ||
234 | return -EPERM; | ||
235 | } | ||
236 | |||
237 | out: | ||
238 | DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, v, rc); | ||
239 | return rc; | ||
240 | } | ||
241 | |||
242 | static int ov6x30_get_control(struct i2c_client *c, | ||
243 | struct ovcamchip_control *ctl) | ||
244 | { | ||
245 | struct ovcamchip *ov = i2c_get_clientdata(c); | ||
246 | struct ov6x30 *s = ov->spriv; | ||
247 | int rc = 0; | ||
248 | unsigned char val = 0; | ||
249 | |||
250 | switch (ctl->id) { | ||
251 | case OVCAMCHIP_CID_CONT: | ||
252 | rc = ov_read(c, REG_CNT, &val); | ||
253 | ctl->value = (val & 0x0f) << 12; | ||
254 | break; | ||
255 | case OVCAMCHIP_CID_BRIGHT: | ||
256 | rc = ov_read(c, REG_BRT, &val); | ||
257 | ctl->value = val << 8; | ||
258 | break; | ||
259 | case OVCAMCHIP_CID_SAT: | ||
260 | rc = ov_read(c, REG_SAT, &val); | ||
261 | ctl->value = val << 8; | ||
262 | break; | ||
263 | case OVCAMCHIP_CID_HUE: | ||
264 | rc = ov_read(c, REG_BLUE, &val); | ||
265 | ctl->value = val << 8; | ||
266 | break; | ||
267 | case OVCAMCHIP_CID_EXP: | ||
268 | rc = ov_read(c, REG_EXP, &val); | ||
269 | ctl->value = val; | ||
270 | break; | ||
271 | case OVCAMCHIP_CID_BANDFILT: | ||
272 | ctl->value = s->bandfilt; | ||
273 | break; | ||
274 | case OVCAMCHIP_CID_AUTOBRIGHT: | ||
275 | ctl->value = s->auto_brt; | ||
276 | break; | ||
277 | case OVCAMCHIP_CID_AUTOEXP: | ||
278 | ctl->value = s->auto_exp; | ||
279 | break; | ||
280 | case OVCAMCHIP_CID_BACKLIGHT: | ||
281 | ctl->value = s->backlight; | ||
282 | break; | ||
283 | case OVCAMCHIP_CID_MIRROR: | ||
284 | ctl->value = s->mirror; | ||
285 | break; | ||
286 | default: | ||
287 | DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); | ||
288 | return -EPERM; | ||
289 | } | ||
290 | |||
291 | DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, ctl->value, rc); | ||
292 | return rc; | ||
293 | } | ||
294 | |||
295 | static int ov6x30_mode_init(struct i2c_client *c, struct ovcamchip_window *win) | ||
296 | { | ||
297 | /******** QCIF-specific regs ********/ | ||
298 | |||
299 | ov_write_mask(c, 0x14, win->quarter?0x20:0x00, 0x20); | ||
300 | |||
301 | /******** Palette-specific regs ********/ | ||
302 | |||
303 | if (win->format == VIDEO_PALETTE_GREY) { | ||
304 | if (c->adapter->id == (I2C_ALGO_SMBUS | I2C_HW_SMBUS_OV518)) { | ||
305 | /* Do nothing - we're already in 8-bit mode */ | ||
306 | } else { | ||
307 | ov_write_mask(c, 0x13, 0x20, 0x20); | ||
308 | } | ||
309 | } else { | ||
310 | /* The OV518 needs special treatment. Although both the OV518 | ||
311 | * and the OV6630 support a 16-bit video bus, only the 8 bit Y | ||
312 | * bus is actually used. The UV bus is tied to ground. | ||
313 | * Therefore, the OV6630 needs to be in 8-bit multiplexed | ||
314 | * output mode */ | ||
315 | |||
316 | if (c->adapter->id == (I2C_ALGO_SMBUS | I2C_HW_SMBUS_OV518)) { | ||
317 | /* Do nothing - we want to stay in 8-bit mode */ | ||
318 | /* Warning: Messing with reg 0x13 breaks OV518 color */ | ||
319 | } else { | ||
320 | ov_write_mask(c, 0x13, 0x00, 0x20); | ||
321 | } | ||
322 | } | ||
323 | |||
324 | /******** Clock programming ********/ | ||
325 | |||
326 | ov_write(c, 0x11, win->clockdiv); | ||
327 | |||
328 | return 0; | ||
329 | } | ||
330 | |||
331 | static int ov6x30_set_window(struct i2c_client *c, struct ovcamchip_window *win) | ||
332 | { | ||
333 | int ret, hwscale, vwscale; | ||
334 | |||
335 | ret = ov6x30_mode_init(c, win); | ||
336 | if (ret < 0) | ||
337 | return ret; | ||
338 | |||
339 | if (win->quarter) { | ||
340 | hwscale = 0; | ||
341 | vwscale = 0; | ||
342 | } else { | ||
343 | hwscale = 1; | ||
344 | vwscale = 1; /* The datasheet says 0; it's wrong */ | ||
345 | } | ||
346 | |||
347 | ov_write(c, 0x17, HWSBASE + (win->x >> hwscale)); | ||
348 | ov_write(c, 0x18, HWEBASE + ((win->x + win->width) >> hwscale)); | ||
349 | ov_write(c, 0x19, VWSBASE + (win->y >> vwscale)); | ||
350 | ov_write(c, 0x1a, VWEBASE + ((win->y + win->height) >> vwscale)); | ||
351 | |||
352 | return 0; | ||
353 | } | ||
354 | |||
355 | static int ov6x30_command(struct i2c_client *c, unsigned int cmd, void *arg) | ||
356 | { | ||
357 | switch (cmd) { | ||
358 | case OVCAMCHIP_CMD_S_CTRL: | ||
359 | return ov6x30_set_control(c, arg); | ||
360 | case OVCAMCHIP_CMD_G_CTRL: | ||
361 | return ov6x30_get_control(c, arg); | ||
362 | case OVCAMCHIP_CMD_S_MODE: | ||
363 | return ov6x30_set_window(c, arg); | ||
364 | default: | ||
365 | DDEBUG(2, &c->dev, "command not supported: %d", cmd); | ||
366 | return -ENOIOCTLCMD; | ||
367 | } | ||
368 | } | ||
369 | |||
370 | struct ovcamchip_ops ov6x30_ops = { | ||
371 | .init = ov6x30_init, | ||
372 | .free = ov6x30_free, | ||
373 | .command = ov6x30_command, | ||
374 | }; | ||