Merge branch 'for-2.6.27' of git://linux-nfs.org/~bfields/linux
[linux-2.6] / drivers / media / video / ovcamchip / ov6x20.c
1 /* OmniVision OV6620/OV6120 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 */
22 #define REG_CNT                 0x05    /* Y contrast */
23 #define REG_BRT                 0x06    /* Y brightness */
24 #define REG_WB_BLUE             0x0C    /* WB blue ratio [5:0] */
25 #define REG_WB_RED              0x0D    /* WB red ratio [5:0] */
26 #define REG_EXP                 0x10    /* exposure */
27
28 /* Window parameters */
29 #define HWSBASE 0x38
30 #define HWEBASE 0x3A
31 #define VWSBASE 0x05
32 #define VWEBASE 0x06
33
34 struct ov6x20 {
35         int auto_brt;
36         int auto_exp;
37         int backlight;
38         int bandfilt;
39         int mirror;
40 };
41
42 /* Initial values for use with OV511/OV511+ cameras */
43 static struct ovcamchip_regvals regvals_init_6x20_511[] = {
44         { 0x12, 0x80 }, /* reset */
45         { 0x11, 0x01 },
46         { 0x03, 0x60 },
47         { 0x05, 0x7f }, /* For when autoadjust is off */
48         { 0x07, 0xa8 },
49         { 0x0c, 0x24 },
50         { 0x0d, 0x24 },
51         { 0x0f, 0x15 }, /* COMS */
52         { 0x10, 0x75 }, /* AEC Exposure time */
53         { 0x12, 0x24 }, /* Enable AGC and AWB */
54         { 0x14, 0x04 },
55         { 0x16, 0x03 },
56         { 0x26, 0xb2 }, /* BLC enable */
57         /* 0x28: 0x05 Selects RGB format if RGB on */
58         { 0x28, 0x05 },
59         { 0x2a, 0x04 }, /* Disable framerate adjust */
60         { 0x2d, 0x99 },
61         { 0x33, 0xa0 }, /* Color Processing Parameter */
62         { 0x34, 0xd2 }, /* Max A/D range */
63         { 0x38, 0x8b },
64         { 0x39, 0x40 },
65
66         { 0x3c, 0x39 }, /* Enable AEC mode changing */
67         { 0x3c, 0x3c }, /* Change AEC mode */
68         { 0x3c, 0x24 }, /* Disable AEC mode changing */
69
70         { 0x3d, 0x80 },
71         /* These next two registers (0x4a, 0x4b) are undocumented. They
72          * control the color balance */
73         { 0x4a, 0x80 },
74         { 0x4b, 0x80 },
75         { 0x4d, 0xd2 }, /* This reduces noise a bit */
76         { 0x4e, 0xc1 },
77         { 0x4f, 0x04 },
78         { 0xff, 0xff }, /* END MARKER */
79 };
80
81 /* Initial values for use with OV518 cameras */
82 static struct ovcamchip_regvals regvals_init_6x20_518[] = {
83         { 0x12, 0x80 }, /* Do a reset */
84         { 0x03, 0xc0 }, /* Saturation */
85         { 0x05, 0x8a }, /* Contrast */
86         { 0x0c, 0x24 }, /* AWB blue */
87         { 0x0d, 0x24 }, /* AWB red */
88         { 0x0e, 0x8d }, /* Additional 2x gain */
89         { 0x0f, 0x25 }, /* Black expanding level = 1.3V */
90         { 0x11, 0x01 }, /* Clock div. */
91         { 0x12, 0x24 }, /* Enable AGC and AWB */
92         { 0x13, 0x01 }, /* (default) */
93         { 0x14, 0x80 }, /* Set reserved bit 7 */
94         { 0x15, 0x01 }, /* (default) */
95         { 0x16, 0x03 }, /* (default) */
96         { 0x17, 0x38 }, /* (default) */
97         { 0x18, 0xea }, /* (default) */
98         { 0x19, 0x04 },
99         { 0x1a, 0x93 },
100         { 0x1b, 0x00 }, /* (default) */
101         { 0x1e, 0xc4 }, /* (default) */
102         { 0x1f, 0x04 }, /* (default) */
103         { 0x20, 0x20 }, /* Enable 1st stage aperture correction */
104         { 0x21, 0x10 }, /* Y offset */
105         { 0x22, 0x88 }, /* U offset */
106         { 0x23, 0xc0 }, /* Set XTAL power level */
107         { 0x24, 0x53 }, /* AEC bright ratio */
108         { 0x25, 0x7a }, /* AEC black ratio */
109         { 0x26, 0xb2 }, /* BLC enable */
110         { 0x27, 0xa2 }, /* Full output range */
111         { 0x28, 0x01 }, /* (default) */
112         { 0x29, 0x00 }, /* (default) */
113         { 0x2a, 0x84 }, /* (default) */
114         { 0x2b, 0xa8 }, /* Set custom frame rate */
115         { 0x2c, 0xa0 }, /* (reserved) */
116         { 0x2d, 0x95 }, /* Enable banding filter */
117         { 0x2e, 0x88 }, /* V offset */
118         { 0x33, 0x22 }, /* Luminance gamma on */
119         { 0x34, 0xc7 }, /* A/D bias */
120         { 0x36, 0x12 }, /* (reserved) */
121         { 0x37, 0x63 }, /* (reserved) */
122         { 0x38, 0x8b }, /* Quick AEC/AEB */
123         { 0x39, 0x00 }, /* (default) */
124         { 0x3a, 0x0f }, /* (default) */
125         { 0x3b, 0x3c }, /* (default) */
126         { 0x3c, 0x5c }, /* AEC controls */
127         { 0x3d, 0x80 }, /* Drop 1 (bad) frame when AEC change */
128         { 0x3e, 0x80 }, /* (default) */
129         { 0x3f, 0x02 }, /* (default) */
130         { 0x40, 0x10 }, /* (reserved) */
131         { 0x41, 0x10 }, /* (reserved) */
132         { 0x42, 0x00 }, /* (reserved) */
133         { 0x43, 0x7f }, /* (reserved) */
134         { 0x44, 0x80 }, /* (reserved) */
135         { 0x45, 0x1c }, /* (reserved) */
136         { 0x46, 0x1c }, /* (reserved) */
137         { 0x47, 0x80 }, /* (reserved) */
138         { 0x48, 0x5f }, /* (reserved) */
139         { 0x49, 0x00 }, /* (reserved) */
140         { 0x4a, 0x00 }, /* Color balance (undocumented) */
141         { 0x4b, 0x80 }, /* Color balance (undocumented) */
142         { 0x4c, 0x58 }, /* (reserved) */
143         { 0x4d, 0xd2 }, /* U *= .938, V *= .838 */
144         { 0x4e, 0xa0 }, /* (default) */
145         { 0x4f, 0x04 }, /* UV 3-point average */
146         { 0x50, 0xff }, /* (reserved) */
147         { 0x51, 0x58 }, /* (reserved) */
148         { 0x52, 0xc0 }, /* (reserved) */
149         { 0x53, 0x42 }, /* (reserved) */
150         { 0x27, 0xa6 }, /* Enable manual offset adj. (reg 21 & 22) */
151         { 0x12, 0x20 },
152         { 0x12, 0x24 },
153
154         { 0xff, 0xff }, /* END MARKER */
155 };
156
157 /* This initializes the OV6x20 camera chip and relevant variables. */
158 static int ov6x20_init(struct i2c_client *c)
159 {
160         struct ovcamchip *ov = i2c_get_clientdata(c);
161         struct ov6x20 *s;
162         int rc;
163
164         DDEBUG(4, &c->dev, "entered");
165
166         switch (c->adapter->id) {
167         case I2C_HW_SMBUS_OV511:
168                 rc = ov_write_regvals(c, regvals_init_6x20_511);
169                 break;
170         case I2C_HW_SMBUS_OV518:
171                 rc = ov_write_regvals(c, regvals_init_6x20_518);
172                 break;
173         default:
174                 dev_err(&c->dev, "ov6x20: Unsupported adapter\n");
175                 rc = -ENODEV;
176         }
177
178         if (rc < 0)
179                 return rc;
180
181         ov->spriv = s = kzalloc(sizeof *s, GFP_KERNEL);
182         if (!s)
183                 return -ENOMEM;
184
185         s->auto_brt = 1;
186         s->auto_exp = 1;
187
188         return rc;
189 }
190
191 static int ov6x20_free(struct i2c_client *c)
192 {
193         struct ovcamchip *ov = i2c_get_clientdata(c);
194
195         kfree(ov->spriv);
196         return 0;
197 }
198
199 static int ov6x20_set_control(struct i2c_client *c,
200                               struct ovcamchip_control *ctl)
201 {
202         struct ovcamchip *ov = i2c_get_clientdata(c);
203         struct ov6x20 *s = ov->spriv;
204         int rc;
205         int v = ctl->value;
206
207         switch (ctl->id) {
208         case OVCAMCHIP_CID_CONT:
209                 rc = ov_write(c, REG_CNT, v >> 8);
210                 break;
211         case OVCAMCHIP_CID_BRIGHT:
212                 rc = ov_write(c, REG_BRT, v >> 8);
213                 break;
214         case OVCAMCHIP_CID_SAT:
215                 rc = ov_write(c, REG_SAT, v >> 8);
216                 break;
217         case OVCAMCHIP_CID_HUE:
218                 rc = ov_write(c, REG_RED, 0xFF - (v >> 8));
219                 if (rc < 0)
220                         goto out;
221
222                 rc = ov_write(c, REG_BLUE, v >> 8);
223                 break;
224         case OVCAMCHIP_CID_EXP:
225                 rc = ov_write(c, REG_EXP, v);
226                 break;
227         case OVCAMCHIP_CID_FREQ:
228         {
229                 int sixty = (v == 60);
230
231                 rc = ov_write(c, 0x2b, sixty?0xa8:0x28);
232                 if (rc < 0)
233                         goto out;
234
235                 rc = ov_write(c, 0x2a, sixty?0x84:0xa4);
236                 break;
237         }
238         case OVCAMCHIP_CID_BANDFILT:
239                 rc = ov_write_mask(c, 0x2d, v?0x04:0x00, 0x04);
240                 s->bandfilt = v;
241                 break;
242         case OVCAMCHIP_CID_AUTOBRIGHT:
243                 rc = ov_write_mask(c, 0x2d, v?0x10:0x00, 0x10);
244                 s->auto_brt = v;
245                 break;
246         case OVCAMCHIP_CID_AUTOEXP:
247                 rc = ov_write_mask(c, 0x13, v?0x01:0x00, 0x01);
248                 s->auto_exp = v;
249                 break;
250         case OVCAMCHIP_CID_BACKLIGHT:
251         {
252                 rc = ov_write_mask(c, 0x4e, v?0xe0:0xc0, 0xe0);
253                 if (rc < 0)
254                         goto out;
255
256                 rc = ov_write_mask(c, 0x29, v?0x08:0x00, 0x08);
257                 if (rc < 0)
258                         goto out;
259
260                 rc = ov_write_mask(c, 0x0e, v?0x80:0x00, 0x80);
261                 s->backlight = v;
262                 break;
263         }
264         case OVCAMCHIP_CID_MIRROR:
265                 rc = ov_write_mask(c, 0x12, v?0x40:0x00, 0x40);
266                 s->mirror = v;
267                 break;
268         default:
269                 DDEBUG(2, &c->dev, "control not supported: %d", ctl->id);
270                 return -EPERM;
271         }
272
273 out:
274         DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, v, rc);
275         return rc;
276 }
277
278 static int ov6x20_get_control(struct i2c_client *c,
279                               struct ovcamchip_control *ctl)
280 {
281         struct ovcamchip *ov = i2c_get_clientdata(c);
282         struct ov6x20 *s = ov->spriv;
283         int rc = 0;
284         unsigned char val = 0;
285
286         switch (ctl->id) {
287         case OVCAMCHIP_CID_CONT:
288                 rc = ov_read(c, REG_CNT, &val);
289                 ctl->value = val << 8;
290                 break;
291         case OVCAMCHIP_CID_BRIGHT:
292                 rc = ov_read(c, REG_BRT, &val);
293                 ctl->value = val << 8;
294                 break;
295         case OVCAMCHIP_CID_SAT:
296                 rc = ov_read(c, REG_SAT, &val);
297                 ctl->value = val << 8;
298                 break;
299         case OVCAMCHIP_CID_HUE:
300                 rc = ov_read(c, REG_BLUE, &val);
301                 ctl->value = val << 8;
302                 break;
303         case OVCAMCHIP_CID_EXP:
304                 rc = ov_read(c, REG_EXP, &val);
305                 ctl->value = val;
306                 break;
307         case OVCAMCHIP_CID_BANDFILT:
308                 ctl->value = s->bandfilt;
309                 break;
310         case OVCAMCHIP_CID_AUTOBRIGHT:
311                 ctl->value = s->auto_brt;
312                 break;
313         case OVCAMCHIP_CID_AUTOEXP:
314                 ctl->value = s->auto_exp;
315                 break;
316         case OVCAMCHIP_CID_BACKLIGHT:
317                 ctl->value = s->backlight;
318                 break;
319         case OVCAMCHIP_CID_MIRROR:
320                 ctl->value = s->mirror;
321                 break;
322         default:
323                 DDEBUG(2, &c->dev, "control not supported: %d", ctl->id);
324                 return -EPERM;
325         }
326
327         DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, ctl->value, rc);
328         return rc;
329 }
330
331 static int ov6x20_mode_init(struct i2c_client *c, struct ovcamchip_window *win)
332 {
333         /******** QCIF-specific regs ********/
334
335         ov_write(c, 0x14, win->quarter?0x24:0x04);
336
337         /******** Palette-specific regs ********/
338
339         /* OV518 needs 8 bit multiplexed in color mode, and 16 bit in B&W */
340         if (c->adapter->id == I2C_HW_SMBUS_OV518) {
341                 if (win->format == VIDEO_PALETTE_GREY)
342                         ov_write_mask(c, 0x13, 0x00, 0x20);
343                 else
344                         ov_write_mask(c, 0x13, 0x20, 0x20);
345         } else {
346                 if (win->format == VIDEO_PALETTE_GREY)
347                         ov_write_mask(c, 0x13, 0x20, 0x20);
348                 else
349                         ov_write_mask(c, 0x13, 0x00, 0x20);
350         }
351
352         /******** Clock programming ********/
353
354         /* The OV6620 needs special handling. This prevents the
355          * severe banding that normally occurs */
356
357         /* Clock down */
358         ov_write(c, 0x2a, 0x04);
359
360         ov_write(c, 0x11, win->clockdiv);
361
362         ov_write(c, 0x2a, 0x84);
363         /* This next setting is critical. It seems to improve
364          * the gain or the contrast. The "reserved" bits seem
365          * to have some effect in this case. */
366         ov_write(c, 0x2d, 0x85); /* FIXME: This messes up banding filter */
367
368         return 0;
369 }
370
371 static int ov6x20_set_window(struct i2c_client *c, struct ovcamchip_window *win)
372 {
373         int ret, hwscale, vwscale;
374
375         ret = ov6x20_mode_init(c, win);
376         if (ret < 0)
377                 return ret;
378
379         if (win->quarter) {
380                 hwscale = 0;
381                 vwscale = 0;
382         } else {
383                 hwscale = 1;
384                 vwscale = 1;    /* The datasheet says 0; it's wrong */
385         }
386
387         ov_write(c, 0x17, HWSBASE + (win->x >> hwscale));
388         ov_write(c, 0x18, HWEBASE + ((win->x + win->width) >> hwscale));
389         ov_write(c, 0x19, VWSBASE + (win->y >> vwscale));
390         ov_write(c, 0x1a, VWEBASE + ((win->y + win->height) >> vwscale));
391
392         return 0;
393 }
394
395 static int ov6x20_command(struct i2c_client *c, unsigned int cmd, void *arg)
396 {
397         switch (cmd) {
398         case OVCAMCHIP_CMD_S_CTRL:
399                 return ov6x20_set_control(c, arg);
400         case OVCAMCHIP_CMD_G_CTRL:
401                 return ov6x20_get_control(c, arg);
402         case OVCAMCHIP_CMD_S_MODE:
403                 return ov6x20_set_window(c, arg);
404         default:
405                 DDEBUG(2, &c->dev, "command not supported: %d", cmd);
406                 return -ENOIOCTLCMD;
407         }
408 }
409
410 struct ovcamchip_ops ov6x20_ops = {
411         .init    =      ov6x20_init,
412         .free    =      ov6x20_free,
413         .command =      ov6x20_command,
414 };