Linux kernel & device driver programming

Cross-Referenced Linux and Device Driver Code

[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ]
Version: [ 2.6.11.8 ] [ 2.6.25 ] [ 2.6.25.8 ] [ 2.6.31.13 ] Architecture: [ i386 ]
  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_ALGO_SMBUS | I2C_HW_SMBUS_OV511:
168                 rc = ov_write_regvals(c, regvals_init_6x20_511);
169                 break;
170         case I2C_ALGO_SMBUS | 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 = kmalloc(sizeof *s, GFP_KERNEL);
182         if (!s)
183                 return -ENOMEM;
184         memset(s, 0, sizeof *s);
185 
186         s->auto_brt = 1;
187         s->auto_exp = 1;
188 
189         return rc;
190 }
191 
192 static int ov6x20_free(struct i2c_client *c)
193 {
194         struct ovcamchip *ov = i2c_get_clientdata(c);
195 
196         kfree(ov->spriv);
197         return 0;
198 }
199 
200 static int ov6x20_set_control(struct i2c_client *c,
201                               struct ovcamchip_control *ctl)
202 {
203         struct ovcamchip *ov = i2c_get_clientdata(c);
204         struct ov6x20 *s = ov->spriv;
205         int rc;
206         int v = ctl->value;
207 
208         switch (ctl->id) {
209         case OVCAMCHIP_CID_CONT:
210                 rc = ov_write(c, REG_CNT, v >> 8);
211                 break;
212         case OVCAMCHIP_CID_BRIGHT:
213                 rc = ov_write(c, REG_BRT, v >> 8);
214                 break;
215         case OVCAMCHIP_CID_SAT:
216                 rc = ov_write(c, REG_SAT, v >> 8);
217                 break;
218         case OVCAMCHIP_CID_HUE:
219                 rc = ov_write(c, REG_RED, 0xFF - (v >> 8));
220                 if (rc < 0)
221                         goto out;
222 
223                 rc = ov_write(c, REG_BLUE, v >> 8);
224                 break;
225         case OVCAMCHIP_CID_EXP:
226                 rc = ov_write(c, REG_EXP, v);
227                 break;
228         case OVCAMCHIP_CID_FREQ:
229         {
230                 int sixty = (v == 60);
231 
232                 rc = ov_write(c, 0x2b, sixty?0xa8:0x28);
233                 if (rc < 0)
234                         goto out;
235 
236                 rc = ov_write(c, 0x2a, sixty?0x84:0xa4);
237                 break;
238         }
239         case OVCAMCHIP_CID_BANDFILT:
240                 rc = ov_write_mask(c, 0x2d, v?0x04:0x00, 0x04);
241                 s->bandfilt = v;
242                 break;
243         case OVCAMCHIP_CID_AUTOBRIGHT:
244                 rc = ov_write_mask(c, 0x2d, v?0x10:0x00, 0x10);
245                 s->auto_brt = v;
246                 break;
247         case OVCAMCHIP_CID_AUTOEXP:
248                 rc = ov_write_mask(c, 0x13, v?0x01:0x00, 0x01);
249                 s->auto_exp = v;
250                 break;
251         case OVCAMCHIP_CID_BACKLIGHT:
252         {
253                 rc = ov_write_mask(c, 0x4e, v?0xe0:0xc0, 0xe0);
254                 if (rc < 0)
255                         goto out;
256 
257                 rc = ov_write_mask(c, 0x29, v?0x08:0x00, 0x08);
258                 if (rc < 0)
259                         goto out;
260 
261                 rc = ov_write_mask(c, 0x0e, v?0x80:0x00, 0x80);
262                 s->backlight = v;
263                 break;
264         }
265         case OVCAMCHIP_CID_MIRROR:
266                 rc = ov_write_mask(c, 0x12, v?0x40:0x00, 0x40);
267                 s->mirror = v;
268                 break;
269         default:
270                 DDEBUG(2, &c->dev, "control not supported: %d", ctl->id);
271                 return -EPERM;
272         }
273 
274 out:
275         DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, v, rc);
276         return rc;
277 }
278 
279 static int ov6x20_get_control(struct i2c_client *c,
280                               struct ovcamchip_control *ctl)
281 {
282         struct ovcamchip *ov = i2c_get_clientdata(c);
283         struct ov6x20 *s = ov->spriv;
284         int rc = 0;
285         unsigned char val = 0;
286 
287         switch (ctl->id) {
288         case OVCAMCHIP_CID_CONT:
289                 rc = ov_read(c, REG_CNT, &val);
290                 ctl->value = val << 8;
291                 break;
292         case OVCAMCHIP_CID_BRIGHT:
293                 rc = ov_read(c, REG_BRT, &val);
294                 ctl->value = val << 8;
295                 break;
296         case OVCAMCHIP_CID_SAT:
297                 rc = ov_read(c, REG_SAT, &val);
298                 ctl->value = val << 8;
299                 break;
300         case OVCAMCHIP_CID_HUE:
301                 rc = ov_read(c, REG_BLUE, &val);
302                 ctl->value = val << 8;
303                 break;
304         case OVCAMCHIP_CID_EXP:
305                 rc = ov_read(c, REG_EXP, &val);
306                 ctl->value = val;
307                 break;
308         case OVCAMCHIP_CID_BANDFILT:
309                 ctl->value = s->bandfilt;
310                 break;
311         case OVCAMCHIP_CID_AUTOBRIGHT:
312                 ctl->value = s->auto_brt;
313                 break;
314         case OVCAMCHIP_CID_AUTOEXP:
315                 ctl->value = s->auto_exp;
316                 break;
317         case OVCAMCHIP_CID_BACKLIGHT:
318                 ctl->value = s->backlight;
319                 break;
320         case OVCAMCHIP_CID_MIRROR:
321                 ctl->value = s->mirror;
322                 break;
323         default:
324                 DDEBUG(2, &c->dev, "control not supported: %d", ctl->id);
325                 return -EPERM;
326         }
327 
328         DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, ctl->value, rc);
329         return rc;
330 }
331 
332 static int ov6x20_mode_init(struct i2c_client *c, struct ovcamchip_window *win)
333 {
334         /******** QCIF-specific regs ********/
335 
336         ov_write(c, 0x14, win->quarter?0x24:0x04);
337 
338         /******** Palette-specific regs ********/
339 
340         /* OV518 needs 8 bit multiplexed in color mode, and 16 bit in B&W */
341         if (c->adapter->id == (I2C_ALGO_SMBUS | I2C_HW_SMBUS_OV518)) {
342                 if (win->format == VIDEO_PALETTE_GREY)
343                         ov_write_mask(c, 0x13, 0x00, 0x20);
344                 else
345                         ov_write_mask(c, 0x13, 0x20, 0x20);
346         } else {
347                 if (win->format == VIDEO_PALETTE_GREY)
348                         ov_write_mask(c, 0x13, 0x20, 0x20);
349                 else
350                         ov_write_mask(c, 0x13, 0x00, 0x20);
351         }
352 
353         /******** Clock programming ********/
354 
355         /* The OV6620 needs special handling. This prevents the
356          * severe banding that normally occurs */
357 
358         /* Clock down */
359         ov_write(c, 0x2a, 0x04);
360 
361         ov_write(c, 0x11, win->clockdiv);
362 
363         ov_write(c, 0x2a, 0x84);
364         /* This next setting is critical. It seems to improve
365          * the gain or the contrast. The "reserved" bits seem
366          * to have some effect in this case. */
367         ov_write(c, 0x2d, 0x85); /* FIXME: This messes up banding filter */
368 
369         return 0;
370 }
371 
372 static int ov6x20_set_window(struct i2c_client *c, struct ovcamchip_window *win)
373 {
374         int ret, hwscale, vwscale;
375 
376         ret = ov6x20_mode_init(c, win);
377         if (ret < 0)
378                 return ret;
379 
380         if (win->quarter) {
381                 hwscale = 0;
382                 vwscale = 0;
383         } else {
384                 hwscale = 1;
385                 vwscale = 1;    /* The datasheet says 0; it's wrong */
386         }
387 
388         ov_write(c, 0x17, HWSBASE + (win->x >> hwscale));
389         ov_write(c, 0x18, HWEBASE + ((win->x + win->width) >> hwscale));
390         ov_write(c, 0x19, VWSBASE + (win->y >> vwscale));
391         ov_write(c, 0x1a, VWEBASE + ((win->y + win->height) >> vwscale));
392 
393         return 0;
394 }
395 
396 static int ov6x20_command(struct i2c_client *c, unsigned int cmd, void *arg)
397 {
398         switch (cmd) {
399         case OVCAMCHIP_CMD_S_CTRL:
400                 return ov6x20_set_control(c, arg);
401         case OVCAMCHIP_CMD_G_CTRL:
402                 return ov6x20_get_control(c, arg);
403         case OVCAMCHIP_CMD_S_MODE:
404                 return ov6x20_set_window(c, arg);
405         default:
406                 DDEBUG(2, &c->dev, "command not supported: %d", cmd);
407                 return -ENOIOCTLCMD;
408         }
409 }
410 
411 struct ovcamchip_ops ov6x20_ops = {
412         .init    =      ov6x20_init,
413         .free    =      ov6x20_free,
414         .command =      ov6x20_command,
415 };
416 
  This page was automatically generated by the LXR engine.