vserver 1.9.3
[linux-2.6.git] / drivers / usb / media / sn9c102_pas106b.c
1 /***************************************************************************
2  * Driver for PAS106B image sensor connected to the SN9C10[12] PC Camera   *
3  * Controllers                                                             *
4  *                                                                         *
5  * Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it>       *
6  *                                                                         *
7  * This program is free software; you can redistribute it and/or modify    *
8  * it under the terms of the GNU General Public License as published by    *
9  * the Free Software Foundation; either version 2 of the License, or       *
10  * (at your option) any later version.                                     *
11  *                                                                         *
12  * This program is distributed in the hope that it will be useful,         *
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of          *
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           *
15  * GNU General Public License for more details.                            *
16  *                                                                         *
17  * You should have received a copy of the GNU General Public License       *
18  * along with this program; if not, write to the Free Software             *
19  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.               *
20  ***************************************************************************/
21
22 #include <linux/delay.h>
23 #include "sn9c102_sensor.h"
24
25
26 static struct sn9c102_sensor pas106b;
27
28
29 static int pas106b_init(struct sn9c102_device* cam)
30 {
31         int err = 0;
32
33         err += sn9c102_write_reg(cam, 0x00, 0x10);
34         err += sn9c102_write_reg(cam, 0x00, 0x11);
35         err += sn9c102_write_reg(cam, 0x00, 0x14);
36         err += sn9c102_write_reg(cam, 0x20, 0x17);
37         err += sn9c102_write_reg(cam, 0x20, 0x19);
38         err += sn9c102_write_reg(cam, 0x09, 0x18);
39
40         err += sn9c102_i2c_write(cam, 0x02, 0x0c);
41         err += sn9c102_i2c_write(cam, 0x03, 0x12);
42         err += sn9c102_i2c_write(cam, 0x04, 0x05);
43         err += sn9c102_i2c_write(cam, 0x05, 0x5a);
44         err += sn9c102_i2c_write(cam, 0x06, 0x88);
45         err += sn9c102_i2c_write(cam, 0x07, 0x80);
46         err += sn9c102_i2c_write(cam, 0x08, 0x01);
47         err += sn9c102_i2c_write(cam, 0x0a, 0x01);
48         err += sn9c102_i2c_write(cam, 0x0b, 0x00);
49         err += sn9c102_i2c_write(cam, 0x10, 0x06);
50         err += sn9c102_i2c_write(cam, 0x11, 0x06);
51         err += sn9c102_i2c_write(cam, 0x12, 0x00);
52         err += sn9c102_i2c_write(cam, 0x14, 0x02);
53         err += sn9c102_i2c_write(cam, 0x13, 0x01);
54
55         msleep(400);
56
57         return err;
58 }
59
60
61 static int pas106b_get_ctrl(struct sn9c102_device* cam, 
62                             struct v4l2_control* ctrl)
63 {
64         switch (ctrl->id) {
65         case V4L2_CID_RED_BALANCE:
66                 if ((ctrl->value = sn9c102_i2c_read(cam, 0x0c)) < 0)
67                         return -EIO;
68                 ctrl->value &= 0x1f;
69                 return 0;
70         case V4L2_CID_BLUE_BALANCE:
71                 if ((ctrl->value = sn9c102_i2c_read(cam, 0x09)) < 0)
72                         return -EIO;
73                 ctrl->value &= 0x1f;
74                 return 0;
75         case V4L2_CID_GAIN:
76                 if ((ctrl->value = sn9c102_i2c_read(cam, 0x0e)) < 0)
77                         return -EIO;
78                 ctrl->value &= 0x1f;
79                 return 0;
80         case V4L2_CID_BRIGHTNESS:
81                 if ((ctrl->value = sn9c102_i2c_read(cam, 0x0d)) < 0)
82                         return -EIO;
83                 ctrl->value &= 0x1f;
84                 return 0;
85         case V4L2_CID_CONTRAST:
86                 if ((ctrl->value = sn9c102_i2c_read(cam, 0x0f)) < 0)
87                         return -EIO;
88                 ctrl->value &= 0x07;
89                 return 0;
90         default:
91                 return -EINVAL;
92         }
93 }
94
95
96 static int pas106b_set_ctrl(struct sn9c102_device* cam, 
97                             const struct v4l2_control* ctrl)
98 {
99         int err = 0;
100
101         switch (ctrl->id) {
102         case V4L2_CID_RED_BALANCE:
103                 err += sn9c102_i2c_write(cam, 0x0c, ctrl->value & 0x1f);
104                 break;
105         case V4L2_CID_BLUE_BALANCE:
106                 err += sn9c102_i2c_write(cam, 0x09, ctrl->value & 0x1f);
107                 break;
108         case V4L2_CID_GAIN:
109                 err += sn9c102_i2c_write(cam, 0x0e, ctrl->value & 0x1f);
110                 break;
111         case V4L2_CID_BRIGHTNESS:
112                 err += sn9c102_i2c_write(cam, 0x0d, 0x1f-(ctrl->value & 0x1f));
113                 break;
114         case V4L2_CID_CONTRAST:
115                 err += sn9c102_i2c_write(cam, 0x0f, ctrl->value & 0x03);
116                 break;
117         default:
118                 return -EINVAL;
119         }
120         err += sn9c102_i2c_write(cam, 0x13, 0x01);
121
122         return err;
123 }
124
125
126 static int pas106b_set_crop(struct sn9c102_device* cam, 
127                             const struct v4l2_rect* rect)
128 {
129         struct sn9c102_sensor* s = &pas106b;
130         int err = 0;
131         u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4,
132            v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3;
133
134         err += sn9c102_write_reg(cam, h_start, 0x12);
135         err += sn9c102_write_reg(cam, v_start, 0x13);
136
137         return err;
138 }
139
140
141 static struct sn9c102_sensor pas106b = {
142         .name = "PAS106B",
143         .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
144         .frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ,
145         .interface = SN9C102_I2C_2WIRES,
146         .slave_read_id = 0x40,
147         .slave_write_id = 0x40,
148         .init = &pas106b_init,
149         .qctrl = {
150                 {
151                         .id = V4L2_CID_RED_BALANCE,
152                         .type = V4L2_CTRL_TYPE_INTEGER,
153                         .name = "red balance",
154                         .minimum = 0x00,
155                         .maximum = 0x1f,
156                         .step = 0x01,
157                         .default_value = 0x04,
158                         .flags = 0,
159                 },
160                 {
161                         .id = V4L2_CID_BLUE_BALANCE,
162                         .type = V4L2_CTRL_TYPE_INTEGER,
163                         .name = "blue balance",
164                         .minimum = 0x00,
165                         .maximum = 0x1f,
166                         .step = 0x01,
167                         .default_value = 0x06,
168                         .flags = 0,
169                 },
170                 {
171                         .id = V4L2_CID_GAIN,
172                         .type = V4L2_CTRL_TYPE_INTEGER,
173                         .name = "global gain",
174                         .minimum = 0x00,
175                         .maximum = 0x1f,
176                         .step = 0x01,
177                         .default_value = 0x0d,
178                         .flags = 0,
179                 },
180                 {
181                         .id = V4L2_CID_BRIGHTNESS,
182                         .type = V4L2_CTRL_TYPE_INTEGER,
183                         .name = "brightness",
184                         .minimum = 0x00,
185                         .maximum = 0x1f,
186                         .step = 0x01,
187                         .default_value = 0x1f,
188                         .flags = 0,
189                 },
190                 {
191                         .id = V4L2_CID_CONTRAST,
192                         .type = V4L2_CTRL_TYPE_INTEGER,
193                         .name = "contrast",
194                         .minimum = 0x00,
195                         .maximum = 0x07,
196                         .step = 0x01,
197                         .default_value = 0x00, /* 0x00~0x03 have same effect */
198                         .flags = 0,
199                 },
200         },
201         .get_ctrl = &pas106b_get_ctrl,
202         .set_ctrl = &pas106b_set_ctrl,
203         .cropcap = {
204                 .bounds = {
205                         .left = 0,
206                         .top = 0,
207                         .width = 352,
208                         .height = 288,
209                 },
210                 .defrect = {
211                         .left = 0,
212                         .top = 0,
213                         .width = 352,
214                         .height = 288,
215                 },
216         },
217         .set_crop = &pas106b_set_crop,
218         .pix_format = {
219                 .width = 352,
220                 .height = 288,
221                 .pixelformat = V4L2_PIX_FMT_SBGGR8,
222                 .priv = 8, /* we use this field as 'bits per pixel' */
223         }
224 };
225
226
227 int sn9c102_probe_pas106b(struct sn9c102_device* cam)
228 {
229         int r0 = 0, r1 = 0, err = 0;
230         unsigned int pid = 0;
231
232         /*
233            Minimal initialization to enable the I2C communication
234            NOTE: do NOT change the values!
235         */
236         err += sn9c102_write_reg(cam, 0x01, 0x01); /* sensor power down */
237         err += sn9c102_write_reg(cam, 0x00, 0x01); /* sensor power on */
238         err += sn9c102_write_reg(cam, 0x28, 0x17); /* sensor clock at 24 MHz */
239         if (err)
240                 return -EIO;
241
242         r0 = sn9c102_i2c_try_read(cam, &pas106b, 0x00);
243         r1 = sn9c102_i2c_try_read(cam, &pas106b, 0x01);
244
245         if (r0 < 0 || r1 < 0)
246                 return -EIO;
247
248         pid = (r0 << 11) | ((r1 & 0xf0) >> 4);
249         if (pid != 0x007)
250                 return -ENODEV;
251
252         sn9c102_attach_sensor(cam, &pas106b);
253
254         return 0;
255 }