diff options
Diffstat (limited to 'drivers/media/video/ovcamchip/ov7x10.c')
-rw-r--r-- | drivers/media/video/ovcamchip/ov7x10.c | 335 |
1 files changed, 335 insertions, 0 deletions
diff --git a/drivers/media/video/ovcamchip/ov7x10.c b/drivers/media/video/ovcamchip/ov7x10.c new file mode 100644 index 000000000000..6c383d4b14fa --- /dev/null +++ b/drivers/media/video/ovcamchip/ov7x10.c | |||
@@ -0,0 +1,335 @@ | |||
1 | /* OmniVision OV7610/OV7110 Camera Chip Support Code | ||
2 | * | ||
3 | * Copyright (c) 1999-2004 Mark McClelland <mark@alpha.dyndns.org> | ||
4 | * http://alpha.dyndns.org/ov511/ | ||
5 | * | ||
6 | * Color fixes by by Orion Sky Lawlor <olawlor@acm.org> (2/26/2000) | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms of the GNU General Public License as published by the | ||
10 | * Free Software Foundation; either version 2 of the License, or (at your | ||
11 | * option) any later version. NO WARRANTY OF ANY KIND is expressed or implied. | ||
12 | */ | ||
13 | |||
14 | #define DEBUG | ||
15 | |||
16 | #include <linux/slab.h> | ||
17 | #include "ovcamchip_priv.h" | ||
18 | |||
19 | /* Registers */ | ||
20 | #define REG_GAIN 0x00 /* gain [5:0] */ | ||
21 | #define REG_BLUE 0x01 /* blue channel balance */ | ||
22 | #define REG_RED 0x02 /* red channel balance */ | ||
23 | #define REG_SAT 0x03 /* saturation */ | ||
24 | #define REG_CNT 0x05 /* Y contrast */ | ||
25 | #define REG_BRT 0x06 /* Y brightness */ | ||
26 | #define REG_BLUE_BIAS 0x0C /* blue channel bias [5:0] */ | ||
27 | #define REG_RED_BIAS 0x0D /* red channel bias [5:0] */ | ||
28 | #define REG_GAMMA_COEFF 0x0E /* gamma settings */ | ||
29 | #define REG_WB_RANGE 0x0F /* AEC/ALC/S-AWB settings */ | ||
30 | #define REG_EXP 0x10 /* manual exposure setting */ | ||
31 | #define REG_CLOCK 0x11 /* polarity/clock prescaler */ | ||
32 | #define REG_FIELD_DIVIDE 0x16 /* field interval/mode settings */ | ||
33 | #define REG_HWIN_START 0x17 /* horizontal window start */ | ||
34 | #define REG_HWIN_END 0x18 /* horizontal window end */ | ||
35 | #define REG_VWIN_START 0x19 /* vertical window start */ | ||
36 | #define REG_VWIN_END 0x1A /* vertical window end */ | ||
37 | #define REG_PIXEL_SHIFT 0x1B /* pixel shift */ | ||
38 | #define REG_YOFFSET 0x21 /* Y channel offset */ | ||
39 | #define REG_UOFFSET 0x22 /* U channel offset */ | ||
40 | #define REG_ECW 0x24 /* exposure white level for AEC */ | ||
41 | #define REG_ECB 0x25 /* exposure black level for AEC */ | ||
42 | #define REG_FRAMERATE_H 0x2A /* frame rate MSB + misc */ | ||
43 | #define REG_FRAMERATE_L 0x2B /* frame rate LSB */ | ||
44 | #define REG_ALC 0x2C /* Auto Level Control settings */ | ||
45 | #define REG_VOFFSET 0x2E /* V channel offset adjustment */ | ||
46 | #define REG_ARRAY_BIAS 0x2F /* array bias -- don't change */ | ||
47 | #define REG_YGAMMA 0x33 /* misc gamma settings [7:6] */ | ||
48 | #define REG_BIAS_ADJUST 0x34 /* misc bias settings */ | ||
49 | |||
50 | /* Window parameters */ | ||
51 | #define HWSBASE 0x38 | ||
52 | #define HWEBASE 0x3a | ||
53 | #define VWSBASE 0x05 | ||
54 | #define VWEBASE 0x05 | ||
55 | |||
56 | struct ov7x10 { | ||
57 | int auto_brt; | ||
58 | int auto_exp; | ||
59 | int bandfilt; | ||
60 | int mirror; | ||
61 | }; | ||
62 | |||
63 | /* Lawrence Glaister <lg@jfm.bc.ca> reports: | ||
64 | * | ||
65 | * Register 0x0f in the 7610 has the following effects: | ||
66 | * | ||
67 | * 0x85 (AEC method 1): Best overall, good contrast range | ||
68 | * 0x45 (AEC method 2): Very overexposed | ||
69 | * 0xa5 (spec sheet default): Ok, but the black level is | ||
70 | * shifted resulting in loss of contrast | ||
71 | * 0x05 (old driver setting): very overexposed, too much | ||
72 | * contrast | ||
73 | */ | ||
74 | static struct ovcamchip_regvals regvals_init_7x10[] = { | ||
75 | { 0x10, 0xff }, | ||
76 | { 0x16, 0x03 }, | ||
77 | { 0x28, 0x24 }, | ||
78 | { 0x2b, 0xac }, | ||
79 | { 0x12, 0x00 }, | ||
80 | { 0x38, 0x81 }, | ||
81 | { 0x28, 0x24 }, /* 0c */ | ||
82 | { 0x0f, 0x85 }, /* lg's setting */ | ||
83 | { 0x15, 0x01 }, | ||
84 | { 0x20, 0x1c }, | ||
85 | { 0x23, 0x2a }, | ||
86 | { 0x24, 0x10 }, | ||
87 | { 0x25, 0x8a }, | ||
88 | { 0x26, 0xa2 }, | ||
89 | { 0x27, 0xc2 }, | ||
90 | { 0x2a, 0x04 }, | ||
91 | { 0x2c, 0xfe }, | ||
92 | { 0x2d, 0x93 }, | ||
93 | { 0x30, 0x71 }, | ||
94 | { 0x31, 0x60 }, | ||
95 | { 0x32, 0x26 }, | ||
96 | { 0x33, 0x20 }, | ||
97 | { 0x34, 0x48 }, | ||
98 | { 0x12, 0x24 }, | ||
99 | { 0x11, 0x01 }, | ||
100 | { 0x0c, 0x24 }, | ||
101 | { 0x0d, 0x24 }, | ||
102 | { 0xff, 0xff }, /* END MARKER */ | ||
103 | }; | ||
104 | |||
105 | /* This initializes the OV7x10 camera chip and relevant variables. */ | ||
106 | static int ov7x10_init(struct i2c_client *c) | ||
107 | { | ||
108 | struct ovcamchip *ov = i2c_get_clientdata(c); | ||
109 | struct ov7x10 *s; | ||
110 | int rc; | ||
111 | |||
112 | DDEBUG(4, &c->dev, "entered"); | ||
113 | |||
114 | rc = ov_write_regvals(c, regvals_init_7x10); | ||
115 | if (rc < 0) | ||
116 | return rc; | ||
117 | |||
118 | ov->spriv = s = kmalloc(sizeof *s, GFP_KERNEL); | ||
119 | if (!s) | ||
120 | return -ENOMEM; | ||
121 | memset(s, 0, sizeof *s); | ||
122 | |||
123 | s->auto_brt = 1; | ||
124 | s->auto_exp = 1; | ||
125 | |||
126 | return rc; | ||
127 | } | ||
128 | |||
129 | static int ov7x10_free(struct i2c_client *c) | ||
130 | { | ||
131 | struct ovcamchip *ov = i2c_get_clientdata(c); | ||
132 | |||
133 | kfree(ov->spriv); | ||
134 | return 0; | ||
135 | } | ||
136 | |||
137 | static int ov7x10_set_control(struct i2c_client *c, | ||
138 | struct ovcamchip_control *ctl) | ||
139 | { | ||
140 | struct ovcamchip *ov = i2c_get_clientdata(c); | ||
141 | struct ov7x10 *s = ov->spriv; | ||
142 | int rc; | ||
143 | int v = ctl->value; | ||
144 | |||
145 | switch (ctl->id) { | ||
146 | case OVCAMCHIP_CID_CONT: | ||
147 | rc = ov_write(c, REG_CNT, v >> 8); | ||
148 | break; | ||
149 | case OVCAMCHIP_CID_BRIGHT: | ||
150 | rc = ov_write(c, REG_BRT, v >> 8); | ||
151 | break; | ||
152 | case OVCAMCHIP_CID_SAT: | ||
153 | rc = ov_write(c, REG_SAT, v >> 8); | ||
154 | break; | ||
155 | case OVCAMCHIP_CID_HUE: | ||
156 | rc = ov_write(c, REG_RED, 0xFF - (v >> 8)); | ||
157 | if (rc < 0) | ||
158 | goto out; | ||
159 | |||
160 | rc = ov_write(c, REG_BLUE, v >> 8); | ||
161 | break; | ||
162 | case OVCAMCHIP_CID_EXP: | ||
163 | rc = ov_write(c, REG_EXP, v); | ||
164 | break; | ||
165 | case OVCAMCHIP_CID_FREQ: | ||
166 | { | ||
167 | int sixty = (v == 60); | ||
168 | |||
169 | rc = ov_write_mask(c, 0x2a, sixty?0x00:0x80, 0x80); | ||
170 | if (rc < 0) | ||
171 | goto out; | ||
172 | |||
173 | rc = ov_write(c, 0x2b, sixty?0x00:0xac); | ||
174 | if (rc < 0) | ||
175 | goto out; | ||
176 | |||
177 | rc = ov_write_mask(c, 0x13, 0x10, 0x10); | ||
178 | if (rc < 0) | ||
179 | goto out; | ||
180 | |||
181 | rc = ov_write_mask(c, 0x13, 0x00, 0x10); | ||
182 | break; | ||
183 | } | ||
184 | case OVCAMCHIP_CID_BANDFILT: | ||
185 | rc = ov_write_mask(c, 0x2d, v?0x04:0x00, 0x04); | ||
186 | s->bandfilt = v; | ||
187 | break; | ||
188 | case OVCAMCHIP_CID_AUTOBRIGHT: | ||
189 | rc = ov_write_mask(c, 0x2d, v?0x10:0x00, 0x10); | ||
190 | s->auto_brt = v; | ||
191 | break; | ||
192 | case OVCAMCHIP_CID_AUTOEXP: | ||
193 | rc = ov_write_mask(c, 0x29, v?0x00:0x80, 0x80); | ||
194 | s->auto_exp = v; | ||
195 | break; | ||
196 | case OVCAMCHIP_CID_MIRROR: | ||
197 | rc = ov_write_mask(c, 0x12, v?0x40:0x00, 0x40); | ||
198 | s->mirror = v; | ||
199 | break; | ||
200 | default: | ||
201 | DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); | ||
202 | return -EPERM; | ||
203 | } | ||
204 | |||
205 | out: | ||
206 | DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, v, rc); | ||
207 | return rc; | ||
208 | } | ||
209 | |||
210 | static int ov7x10_get_control(struct i2c_client *c, | ||
211 | struct ovcamchip_control *ctl) | ||
212 | { | ||
213 | struct ovcamchip *ov = i2c_get_clientdata(c); | ||
214 | struct ov7x10 *s = ov->spriv; | ||
215 | int rc = 0; | ||
216 | unsigned char val = 0; | ||
217 | |||
218 | switch (ctl->id) { | ||
219 | case OVCAMCHIP_CID_CONT: | ||
220 | rc = ov_read(c, REG_CNT, &val); | ||
221 | ctl->value = val << 8; | ||
222 | break; | ||
223 | case OVCAMCHIP_CID_BRIGHT: | ||
224 | rc = ov_read(c, REG_BRT, &val); | ||
225 | ctl->value = val << 8; | ||
226 | break; | ||
227 | case OVCAMCHIP_CID_SAT: | ||
228 | rc = ov_read(c, REG_SAT, &val); | ||
229 | ctl->value = val << 8; | ||
230 | break; | ||
231 | case OVCAMCHIP_CID_HUE: | ||
232 | rc = ov_read(c, REG_BLUE, &val); | ||
233 | ctl->value = val << 8; | ||
234 | break; | ||
235 | case OVCAMCHIP_CID_EXP: | ||
236 | rc = ov_read(c, REG_EXP, &val); | ||
237 | ctl->value = val; | ||
238 | break; | ||
239 | case OVCAMCHIP_CID_BANDFILT: | ||
240 | ctl->value = s->bandfilt; | ||
241 | break; | ||
242 | case OVCAMCHIP_CID_AUTOBRIGHT: | ||
243 | ctl->value = s->auto_brt; | ||
244 | break; | ||
245 | case OVCAMCHIP_CID_AUTOEXP: | ||
246 | ctl->value = s->auto_exp; | ||
247 | break; | ||
248 | case OVCAMCHIP_CID_MIRROR: | ||
249 | ctl->value = s->mirror; | ||
250 | break; | ||
251 | default: | ||
252 | DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); | ||
253 | return -EPERM; | ||
254 | } | ||
255 | |||
256 | DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, ctl->value, rc); | ||
257 | return rc; | ||
258 | } | ||
259 | |||
260 | static int ov7x10_mode_init(struct i2c_client *c, struct ovcamchip_window *win) | ||
261 | { | ||
262 | int qvga = win->quarter; | ||
263 | |||
264 | /******** QVGA-specific regs ********/ | ||
265 | |||
266 | ov_write(c, 0x14, qvga?0x24:0x04); | ||
267 | |||
268 | /******** Palette-specific regs ********/ | ||
269 | |||
270 | if (win->format == VIDEO_PALETTE_GREY) { | ||
271 | ov_write_mask(c, 0x0e, 0x40, 0x40); | ||
272 | ov_write_mask(c, 0x13, 0x20, 0x20); | ||
273 | } else { | ||
274 | ov_write_mask(c, 0x0e, 0x00, 0x40); | ||
275 | ov_write_mask(c, 0x13, 0x00, 0x20); | ||
276 | } | ||
277 | |||
278 | /******** Clock programming ********/ | ||
279 | |||
280 | ov_write(c, 0x11, win->clockdiv); | ||
281 | |||
282 | /******** Resolution-specific ********/ | ||
283 | |||
284 | if (win->width == 640 && win->height == 480) | ||
285 | ov_write(c, 0x35, 0x9e); | ||
286 | else | ||
287 | ov_write(c, 0x35, 0x1e); | ||
288 | |||
289 | return 0; | ||
290 | } | ||
291 | |||
292 | static int ov7x10_set_window(struct i2c_client *c, struct ovcamchip_window *win) | ||
293 | { | ||
294 | int ret, hwscale, vwscale; | ||
295 | |||
296 | ret = ov7x10_mode_init(c, win); | ||
297 | if (ret < 0) | ||
298 | return ret; | ||
299 | |||
300 | if (win->quarter) { | ||
301 | hwscale = 1; | ||
302 | vwscale = 0; | ||
303 | } else { | ||
304 | hwscale = 2; | ||
305 | vwscale = 1; | ||
306 | } | ||
307 | |||
308 | ov_write(c, 0x17, HWSBASE + (win->x >> hwscale)); | ||
309 | ov_write(c, 0x18, HWEBASE + ((win->x + win->width) >> hwscale)); | ||
310 | ov_write(c, 0x19, VWSBASE + (win->y >> vwscale)); | ||
311 | ov_write(c, 0x1a, VWEBASE + ((win->y + win->height) >> vwscale)); | ||
312 | |||
313 | return 0; | ||
314 | } | ||
315 | |||
316 | static int ov7x10_command(struct i2c_client *c, unsigned int cmd, void *arg) | ||
317 | { | ||
318 | switch (cmd) { | ||
319 | case OVCAMCHIP_CMD_S_CTRL: | ||
320 | return ov7x10_set_control(c, arg); | ||
321 | case OVCAMCHIP_CMD_G_CTRL: | ||
322 | return ov7x10_get_control(c, arg); | ||
323 | case OVCAMCHIP_CMD_S_MODE: | ||
324 | return ov7x10_set_window(c, arg); | ||
325 | default: | ||
326 | DDEBUG(2, &c->dev, "command not supported: %d", cmd); | ||
327 | return -ENOIOCTLCMD; | ||
328 | } | ||
329 | } | ||
330 | |||
331 | struct ovcamchip_ops ov7x10_ops = { | ||
332 | .init = ov7x10_init, | ||
333 | .free = ov7x10_free, | ||
334 | .command = ov7x10_command, | ||
335 | }; | ||