diff options
author | Erik Andren <erik.andren@gmail.com> | 2008-12-29 05:35:23 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2008-12-30 06:40:06 -0500 |
commit | 4c98834addfee3fdd42c505c37569261bf669d94 (patch) | |
tree | 71ca38fcaef06e5a854bfdf1ff13168d500ca4aa | |
parent | 15f64864e392612b230bc71d84e4537b80c607b1 (diff) |
V4L/DVB (10048): gspca - stv06xx: New subdriver.
Signed-off-by: Erik Andren <erik.andren@gmail.com>
Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r-- | Documentation/video4linux/gspca.txt | 3 | ||||
-rw-r--r-- | drivers/media/video/gspca/Kconfig | 1 | ||||
-rw-r--r-- | drivers/media/video/gspca/Makefile | 2 | ||||
-rw-r--r-- | drivers/media/video/gspca/stv06xx/Kconfig | 9 | ||||
-rw-r--r-- | drivers/media/video/gspca/stv06xx/Makefile | 6 | ||||
-rw-r--r-- | drivers/media/video/gspca/stv06xx/stv06xx.c | 522 | ||||
-rw-r--r-- | drivers/media/video/gspca/stv06xx/stv06xx.h | 107 | ||||
-rw-r--r-- | drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c | 533 | ||||
-rw-r--r-- | drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h | 263 | ||||
-rw-r--r-- | drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c | 430 | ||||
-rw-r--r-- | drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h | 275 | ||||
-rw-r--r-- | drivers/media/video/gspca/stv06xx/stv06xx_sensor.h | 92 | ||||
-rw-r--r-- | drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c | 251 | ||||
-rw-r--r-- | drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h | 315 |
14 files changed, 2808 insertions, 1 deletions
diff --git a/Documentation/video4linux/gspca.txt b/Documentation/video4linux/gspca.txt index 5daf2c801671..f54281d78c12 100644 --- a/Documentation/video4linux/gspca.txt +++ b/Documentation/video4linux/gspca.txt | |||
@@ -50,6 +50,9 @@ ov519 045e:028c Micro$oft xbox cam | |||
50 | spca508 0461:0815 Micro Innovation IC200 | 50 | spca508 0461:0815 Micro Innovation IC200 |
51 | sunplus 0461:0821 Fujifilm MV-1 | 51 | sunplus 0461:0821 Fujifilm MV-1 |
52 | zc3xx 0461:0a00 MicroInnovation WebCam320 | 52 | zc3xx 0461:0a00 MicroInnovation WebCam320 |
53 | stv06xx 046d:0840 QuickCam Express | ||
54 | stv06xx 046d:0850 LEGO cam / QuickCam Web | ||
55 | stv06xx 046d:0870 Dexxa WebCam USB | ||
53 | spca500 046d:0890 Logitech QuickCam traveler | 56 | spca500 046d:0890 Logitech QuickCam traveler |
54 | vc032x 046d:0892 Logitech Orbicam | 57 | vc032x 046d:0892 Logitech Orbicam |
55 | vc032x 046d:0896 Logitech Orbicam | 58 | vc032x 046d:0896 Logitech Orbicam |
diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig index 770fb699d048..ee6a691dff22 100644 --- a/drivers/media/video/gspca/Kconfig +++ b/drivers/media/video/gspca/Kconfig | |||
@@ -18,6 +18,7 @@ menuconfig USB_GSPCA | |||
18 | if USB_GSPCA && VIDEO_V4L2 | 18 | if USB_GSPCA && VIDEO_V4L2 |
19 | 19 | ||
20 | source "drivers/media/video/gspca/m5602/Kconfig" | 20 | source "drivers/media/video/gspca/m5602/Kconfig" |
21 | source "drivers/media/video/gspca/stv06xx/Kconfig" | ||
21 | 22 | ||
22 | config USB_GSPCA_CONEX | 23 | config USB_GSPCA_CONEX |
23 | tristate "Conexant Camera Driver" | 24 | tristate "Conexant Camera Driver" |
diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile index 6c8046e232c7..bd8d9ee40504 100644 --- a/drivers/media/video/gspca/Makefile +++ b/drivers/media/video/gspca/Makefile | |||
@@ -47,4 +47,4 @@ gspca_vc032x-objs := vc032x.o | |||
47 | gspca_zc3xx-objs := zc3xx.o | 47 | gspca_zc3xx-objs := zc3xx.o |
48 | 48 | ||
49 | obj-$(CONFIG_USB_M5602) += m5602/ | 49 | obj-$(CONFIG_USB_M5602) += m5602/ |
50 | 50 | obj-$(CONFIG_USB_STV06XX) += stv06xx/ | |
diff --git a/drivers/media/video/gspca/stv06xx/Kconfig b/drivers/media/video/gspca/stv06xx/Kconfig new file mode 100644 index 000000000000..634ad38d9fb8 --- /dev/null +++ b/drivers/media/video/gspca/stv06xx/Kconfig | |||
@@ -0,0 +1,9 @@ | |||
1 | config USB_STV06XX | ||
2 | tristate "STV06XX USB Camera Driver" | ||
3 | depends on USB_GSPCA | ||
4 | help | ||
5 | Say Y here if you want support for cameras based on | ||
6 | the ST STV06XX chip. | ||
7 | |||
8 | To compile this driver as a module, choose M here: the | ||
9 | module will be called gspca_stv06xx. | ||
diff --git a/drivers/media/video/gspca/stv06xx/Makefile b/drivers/media/video/gspca/stv06xx/Makefile new file mode 100644 index 000000000000..8f002b6233b2 --- /dev/null +++ b/drivers/media/video/gspca/stv06xx/Makefile | |||
@@ -0,0 +1,6 @@ | |||
1 | obj-$(CONFIG_USB_STV06XX) += gspca_stv06xx.o | ||
2 | |||
3 | gspca_stv06xx-objs := stv06xx.o \ | ||
4 | stv06xx_vv6410.o \ | ||
5 | stv06xx_hdcs.o \ | ||
6 | stv06xx_pb0100.o | ||
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.c b/drivers/media/video/gspca/stv06xx/stv06xx.c new file mode 100644 index 000000000000..29e43718bfdf --- /dev/null +++ b/drivers/media/video/gspca/stv06xx/stv06xx.c | |||
@@ -0,0 +1,522 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher | ||
3 | * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland | ||
4 | * Copyright (c) 2002, 2003 Tuukka Toivonen | ||
5 | * Copyright (c) 2008 Erik Andrén | ||
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | * | ||
21 | * P/N 861037: Sensor HDCS1000 ASIC STV0600 | ||
22 | * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 | ||
23 | * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express | ||
24 | * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam | ||
25 | * P/N 861075-0040: Sensor HDCS1000 ASIC | ||
26 | * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB | ||
27 | * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web | ||
28 | */ | ||
29 | |||
30 | #include "stv06xx_sensor.h" | ||
31 | |||
32 | MODULE_AUTHOR("Erik Andrén"); | ||
33 | MODULE_DESCRIPTION("STV06XX USB Camera Driver"); | ||
34 | MODULE_LICENSE("GPL"); | ||
35 | |||
36 | int dump_bridge; | ||
37 | int dump_sensor; | ||
38 | |||
39 | int stv06xx_write_bridge(struct sd *sd, u16 address, u16 i2c_data) | ||
40 | { | ||
41 | int err; | ||
42 | struct usb_device *udev = sd->gspca_dev.dev; | ||
43 | __u8 *buf = sd->gspca_dev.usb_buf; | ||
44 | u8 len = (i2c_data > 0xff) ? 2 : 1; | ||
45 | |||
46 | buf[0] = i2c_data & 0xff; | ||
47 | buf[1] = (i2c_data >> 8) & 0xff; | ||
48 | |||
49 | err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), | ||
50 | 0x04, 0x40, address, 0, buf, len, | ||
51 | STV06XX_URB_MSG_TIMEOUT); | ||
52 | |||
53 | |||
54 | PDEBUG(D_CONF, "Written 0x%x to address 0x%x, status: %d", | ||
55 | i2c_data, address, err); | ||
56 | |||
57 | return (err < 0) ? err : 0; | ||
58 | } | ||
59 | |||
60 | int stv06xx_read_bridge(struct sd *sd, u16 address, u8 *i2c_data) | ||
61 | { | ||
62 | int err; | ||
63 | struct usb_device *udev = sd->gspca_dev.dev; | ||
64 | __u8 *buf = sd->gspca_dev.usb_buf; | ||
65 | |||
66 | err = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), | ||
67 | 0x04, 0xc0, address, 0, buf, 1, | ||
68 | STV06XX_URB_MSG_TIMEOUT); | ||
69 | |||
70 | *i2c_data = buf[0]; | ||
71 | |||
72 | PDEBUG(D_CONF, "Read 0x%x from address 0x%x, status %d", | ||
73 | *i2c_data, address, err); | ||
74 | |||
75 | return (err < 0) ? err : 0; | ||
76 | } | ||
77 | |||
78 | /* Wraps the normal write sensor bytes / words functions for writing a | ||
79 | single value */ | ||
80 | int stv06xx_write_sensor(struct sd *sd, u8 address, u16 value) | ||
81 | { | ||
82 | if (sd->sensor->i2c_len == 2) { | ||
83 | u16 data[2] = { address, value }; | ||
84 | return stv06xx_write_sensor_words(sd, data, 1); | ||
85 | } else { | ||
86 | u8 data[2] = { address, value }; | ||
87 | return stv06xx_write_sensor_bytes(sd, data, 1); | ||
88 | } | ||
89 | } | ||
90 | |||
91 | static int stv06xx_write_sensor_finish(struct sd *sd) | ||
92 | { | ||
93 | int err = 0; | ||
94 | |||
95 | if (IS_850(sd)) { | ||
96 | struct usb_device *udev = sd->gspca_dev.dev; | ||
97 | __u8 *buf = sd->gspca_dev.usb_buf; | ||
98 | |||
99 | /* Quickam Web needs an extra packet */ | ||
100 | buf[0] = 0; | ||
101 | err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), | ||
102 | 0x04, 0x40, 0x1704, 0, buf, 1, | ||
103 | STV06XX_URB_MSG_TIMEOUT); | ||
104 | } | ||
105 | |||
106 | return (err < 0) ? err : 0; | ||
107 | } | ||
108 | |||
109 | int stv06xx_write_sensor_bytes(struct sd *sd, const u8 *data, u8 len) | ||
110 | { | ||
111 | int err, i, j; | ||
112 | struct usb_device *udev = sd->gspca_dev.dev; | ||
113 | __u8 *buf = sd->gspca_dev.usb_buf; | ||
114 | |||
115 | PDEBUG(D_USBO, "I2C: Command buffer contains %d entries", len); | ||
116 | for (i = 0; i < len;) { | ||
117 | /* Build the command buffer */ | ||
118 | memset(buf, 0, I2C_BUFFER_LENGTH); | ||
119 | for (j = 0; j < I2C_MAX_BYTES && i < len; j++, i++) { | ||
120 | buf[j] = data[2*i]; | ||
121 | buf[0x10 + j] = data[2*i+1]; | ||
122 | PDEBUG(D_USBO, "I2C: Writing 0x%02x to reg 0x%02x", | ||
123 | data[2*i+1], data[2*i]); | ||
124 | } | ||
125 | buf[0x20] = sd->sensor->i2c_addr; | ||
126 | buf[0x21] = j - 1; /* Number of commands to send - 1 */ | ||
127 | buf[0x22] = I2C_WRITE_CMD; | ||
128 | err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), | ||
129 | 0x04, 0x40, 0x0400, 0, buf, | ||
130 | I2C_BUFFER_LENGTH, | ||
131 | STV06XX_URB_MSG_TIMEOUT); | ||
132 | if (err < 0) | ||
133 | return err; | ||
134 | } | ||
135 | return stv06xx_write_sensor_finish(sd); | ||
136 | } | ||
137 | |||
138 | int stv06xx_write_sensor_words(struct sd *sd, const u16 *data, u8 len) | ||
139 | { | ||
140 | int err, i, j; | ||
141 | struct usb_device *udev = sd->gspca_dev.dev; | ||
142 | __u8 *buf = sd->gspca_dev.usb_buf; | ||
143 | |||
144 | PDEBUG(D_USBO, "I2C: Command buffer contains %d entries", len); | ||
145 | |||
146 | for (i = 0; i < len;) { | ||
147 | /* Build the command buffer */ | ||
148 | memset(buf, 0, I2C_BUFFER_LENGTH); | ||
149 | for (j = 0; j < I2C_MAX_WORDS && i < len; j++, i++) { | ||
150 | buf[j] = data[2*i]; | ||
151 | buf[0x10 + j * 2] = data[2*i+1]; | ||
152 | buf[0x10 + j * 2 + 1] = data[2*i+1] >> 8; | ||
153 | PDEBUG(D_USBO, "I2C: Writing 0x%04x to reg 0x%02x", | ||
154 | data[2*i+1], data[2*i]); | ||
155 | } | ||
156 | buf[0x20] = sd->sensor->i2c_addr; | ||
157 | buf[0x21] = j - 1; /* Number of commands to send - 1 */ | ||
158 | buf[0x22] = I2C_WRITE_CMD; | ||
159 | err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), | ||
160 | 0x04, 0x40, 0x0400, 0, buf, | ||
161 | I2C_BUFFER_LENGTH, | ||
162 | STV06XX_URB_MSG_TIMEOUT); | ||
163 | if (err < 0) | ||
164 | return err; | ||
165 | } | ||
166 | return stv06xx_write_sensor_finish(sd); | ||
167 | } | ||
168 | |||
169 | int stv06xx_read_sensor(struct sd *sd, const u8 address, u16 *value) | ||
170 | { | ||
171 | int err; | ||
172 | struct usb_device *udev = sd->gspca_dev.dev; | ||
173 | __u8 *buf = sd->gspca_dev.usb_buf; | ||
174 | |||
175 | err = stv06xx_write_bridge(sd, STV_I2C_FLUSH, sd->sensor->i2c_flush); | ||
176 | if (err < 0) | ||
177 | return err; | ||
178 | |||
179 | /* Clear mem */ | ||
180 | memset(buf, 0, I2C_BUFFER_LENGTH); | ||
181 | |||
182 | buf[0] = address; | ||
183 | buf[0x20] = sd->sensor->i2c_addr; | ||
184 | buf[0x21] = 0; | ||
185 | |||
186 | /* Read I2C register */ | ||
187 | buf[0x22] = I2C_READ_CMD; | ||
188 | |||
189 | err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), | ||
190 | 0x04, 0x40, 0x1400, 0, buf, I2C_BUFFER_LENGTH, | ||
191 | STV06XX_URB_MSG_TIMEOUT); | ||
192 | if (err < 0) { | ||
193 | PDEBUG(D_ERR, "I2C Read: error writing address: %d", err); | ||
194 | return err; | ||
195 | } | ||
196 | |||
197 | err = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), | ||
198 | 0x04, 0xc0, 0x1410, 0, buf, sd->sensor->i2c_len, | ||
199 | STV06XX_URB_MSG_TIMEOUT); | ||
200 | if (sd->sensor->i2c_len == 2) | ||
201 | *value = buf[0] | (buf[1] << 8); | ||
202 | else | ||
203 | *value = buf[0]; | ||
204 | |||
205 | PDEBUG(D_USBO, "I2C: Read 0x%x from address 0x%x, status: %d", | ||
206 | *value, address, err); | ||
207 | |||
208 | return (err < 0) ? err : 0; | ||
209 | } | ||
210 | |||
211 | /* Dumps all bridge registers */ | ||
212 | static void stv06xx_dump_bridge(struct sd *sd) | ||
213 | { | ||
214 | int i; | ||
215 | u8 data, buf; | ||
216 | |||
217 | info("Dumping all stv06xx bridge registers"); | ||
218 | for (i = 0x1400; i < 0x160f; i++) { | ||
219 | stv06xx_read_bridge(sd, i, &data); | ||
220 | |||
221 | info("Read 0x%x from address 0x%x", data, i); | ||
222 | } | ||
223 | |||
224 | for (i = 0x1400; i < 0x160f; i++) { | ||
225 | stv06xx_read_bridge(sd, i, &data); | ||
226 | buf = data; | ||
227 | |||
228 | stv06xx_write_bridge(sd, i, 0xff); | ||
229 | stv06xx_read_bridge(sd, i, &data); | ||
230 | if (data == 0xff) | ||
231 | info("Register 0x%x is read/write", i); | ||
232 | else if (data != buf) | ||
233 | info("Register 0x%x is read/write," | ||
234 | "but only partially", i); | ||
235 | else | ||
236 | info("Register 0x%x is read-only", i); | ||
237 | |||
238 | stv06xx_write_bridge(sd, i, buf); | ||
239 | } | ||
240 | } | ||
241 | |||
242 | /* this function is called at probe and resume time */ | ||
243 | static int stv06xx_init(struct gspca_dev *gspca_dev) | ||
244 | { | ||
245 | struct sd *sd = (struct sd *) gspca_dev; | ||
246 | int err; | ||
247 | |||
248 | PDEBUG(D_PROBE, "Initializing camera"); | ||
249 | |||
250 | /* Let the usb init settle for a bit | ||
251 | before performing the initialization */ | ||
252 | msleep(250); | ||
253 | |||
254 | err = sd->sensor->init(sd); | ||
255 | |||
256 | if (dump_sensor) | ||
257 | sd->sensor->dump(sd); | ||
258 | |||
259 | return (err < 0) ? err : 0; | ||
260 | } | ||
261 | |||
262 | /* Start the camera */ | ||
263 | static int stv06xx_start(struct gspca_dev *gspca_dev) | ||
264 | { | ||
265 | struct sd *sd = (struct sd *) gspca_dev; | ||
266 | int err; | ||
267 | |||
268 | /* Prepare the sensor for start */ | ||
269 | err = sd->sensor->start(sd); | ||
270 | if (err < 0) | ||
271 | goto out; | ||
272 | |||
273 | /* Start isochronous streaming */ | ||
274 | err = stv06xx_write_bridge(sd, STV_ISO_ENABLE, 1); | ||
275 | |||
276 | out: | ||
277 | if (err < 0) | ||
278 | PDEBUG(D_STREAM, "Starting stream failed"); | ||
279 | else | ||
280 | PDEBUG(D_STREAM, "Started streaming"); | ||
281 | |||
282 | return (err < 0) ? err : 0; | ||
283 | } | ||
284 | |||
285 | static void stv06xx_stopN(struct gspca_dev *gspca_dev) | ||
286 | { | ||
287 | int err; | ||
288 | struct sd *sd = (struct sd *) gspca_dev; | ||
289 | |||
290 | /* stop ISO-streaming */ | ||
291 | err = stv06xx_write_bridge(sd, STV_ISO_ENABLE, 0); | ||
292 | if (err < 0) | ||
293 | goto out; | ||
294 | |||
295 | err = sd->sensor->stop(sd); | ||
296 | if (err < 0) | ||
297 | goto out; | ||
298 | |||
299 | out: | ||
300 | if (err < 0) | ||
301 | PDEBUG(D_STREAM, "Failed to stop stream"); | ||
302 | else | ||
303 | PDEBUG(D_STREAM, "Stopped streaming"); | ||
304 | } | ||
305 | |||
306 | /* | ||
307 | * Analyse an USB packet of the data stream and store it appropriately. | ||
308 | * Each packet contains an integral number of chunks. Each chunk has | ||
309 | * 2-bytes identification, followed by 2-bytes that describe the chunk | ||
310 | * length. Known/guessed chunk identifications are: | ||
311 | * 8001/8005/C001/C005 - Begin new frame | ||
312 | * 8002/8006/C002/C006 - End frame | ||
313 | * 0200/4200 - Contains actual image data, bayer or compressed | ||
314 | * 0005 - 11 bytes of unknown data | ||
315 | * 0100 - 2 bytes of unknown data | ||
316 | * The 0005 and 0100 chunks seem to appear only in compressed stream. | ||
317 | */ | ||
318 | static void stv06xx_pkt_scan(struct gspca_dev *gspca_dev, | ||
319 | struct gspca_frame *frame, /* target */ | ||
320 | __u8 *data, /* isoc packet */ | ||
321 | int len) /* iso packet length */ | ||
322 | { | ||
323 | PDEBUG(D_PACK, "Packet of length %d arrived", len); | ||
324 | |||
325 | /* A packet may contain several frames | ||
326 | loop until the whole packet is reached */ | ||
327 | while (len) { | ||
328 | int id, chunk_len; | ||
329 | |||
330 | if (len < 4) { | ||
331 | PDEBUG(D_PACK, "Packet is smaller than 4 bytes"); | ||
332 | return; | ||
333 | } | ||
334 | |||
335 | /* Capture the id */ | ||
336 | id = (data[0] << 8) | data[1]; | ||
337 | |||
338 | /* Capture the chunk length */ | ||
339 | chunk_len = (data[2] << 8) | data[3]; | ||
340 | PDEBUG(D_PACK, "Chunk id: %x, length: %d", id, chunk_len); | ||
341 | |||
342 | data += 4; | ||
343 | len -= 4; | ||
344 | |||
345 | if (len < chunk_len) { | ||
346 | PDEBUG(D_ERR, "URB packet length is smaller" | ||
347 | " than the specified chunk length"); | ||
348 | return; | ||
349 | } | ||
350 | |||
351 | switch (id) { | ||
352 | case 0x0200: | ||
353 | case 0x4200: | ||
354 | PDEBUG(D_PACK, "Frame data packet detected"); | ||
355 | |||
356 | gspca_frame_add(gspca_dev, INTER_PACKET, frame, | ||
357 | data, chunk_len); | ||
358 | break; | ||
359 | |||
360 | case 0x8001: | ||
361 | case 0x8005: | ||
362 | case 0xc001: | ||
363 | case 0xc005: | ||
364 | PDEBUG(D_PACK, "Starting new frame"); | ||
365 | |||
366 | /* Create a new frame, chunk length should be zero */ | ||
367 | gspca_frame_add(gspca_dev, FIRST_PACKET, | ||
368 | frame, data, 0); | ||
369 | |||
370 | if (chunk_len) | ||
371 | PDEBUG(D_ERR, "Chunk length is " | ||
372 | "non-zero on a SOF"); | ||
373 | break; | ||
374 | |||
375 | case 0x8002: | ||
376 | case 0x8006: | ||
377 | case 0xc002: | ||
378 | PDEBUG(D_PACK, "End of frame detected"); | ||
379 | |||
380 | /* Complete the last frame (if any) */ | ||
381 | gspca_frame_add(gspca_dev, LAST_PACKET, frame, data, 0); | ||
382 | |||
383 | if (chunk_len) | ||
384 | PDEBUG(D_ERR, "Chunk length is " | ||
385 | "non-zero on a EOF"); | ||
386 | break; | ||
387 | |||
388 | case 0x0005: | ||
389 | PDEBUG(D_PACK, "Chunk 0x005 detected"); | ||
390 | /* Unknown chunk with 11 bytes of data, | ||
391 | occurs just before end of each frame | ||
392 | in compressed mode */ | ||
393 | break; | ||
394 | |||
395 | case 0x0100: | ||
396 | PDEBUG(D_PACK, "Chunk 0x0100 detected"); | ||
397 | /* Unknown chunk with 2 bytes of data, | ||
398 | occurs 2-3 times per USB interrupt */ | ||
399 | break; | ||
400 | default: | ||
401 | PDEBUG(D_PACK, "Unknown chunk %d detected", id); | ||
402 | /* Unknown chunk */ | ||
403 | } | ||
404 | data += chunk_len; | ||
405 | len -= chunk_len; | ||
406 | } | ||
407 | } | ||
408 | |||
409 | static int stv06xx_config(struct gspca_dev *gspca_dev, | ||
410 | const struct usb_device_id *id); | ||
411 | |||
412 | /* sub-driver description */ | ||
413 | static const struct sd_desc sd_desc = { | ||
414 | .name = MODULE_NAME, | ||
415 | .config = stv06xx_config, | ||
416 | .init = stv06xx_init, | ||
417 | .start = stv06xx_start, | ||
418 | .stopN = stv06xx_stopN, | ||
419 | .pkt_scan = stv06xx_pkt_scan | ||
420 | }; | ||
421 | |||
422 | /* This function is called at probe time */ | ||
423 | static int stv06xx_config(struct gspca_dev *gspca_dev, | ||
424 | const struct usb_device_id *id) | ||
425 | { | ||
426 | struct sd *sd = (struct sd *) gspca_dev; | ||
427 | struct cam *cam; | ||
428 | |||
429 | PDEBUG(D_PROBE, "Configuring camera"); | ||
430 | |||
431 | cam = &gspca_dev->cam; | ||
432 | cam->epaddr = STV_ISOC_ENDPOINT_ADDR; | ||
433 | sd->desc = sd_desc; | ||
434 | gspca_dev->sd_desc = &sd->desc; | ||
435 | |||
436 | if (dump_bridge) | ||
437 | stv06xx_dump_bridge(sd); | ||
438 | |||
439 | sd->sensor = &stv06xx_sensor_vv6410; | ||
440 | if (!sd->sensor->probe(sd)) | ||
441 | return 0; | ||
442 | |||
443 | sd->sensor = &stv06xx_sensor_hdcs1x00; | ||
444 | if (!sd->sensor->probe(sd)) | ||
445 | return 0; | ||
446 | |||
447 | sd->sensor = &stv06xx_sensor_hdcs1020; | ||
448 | if (!sd->sensor->probe(sd)) | ||
449 | return 0; | ||
450 | |||
451 | sd->sensor = &stv06xx_sensor_pb0100; | ||
452 | if (!sd->sensor->probe(sd)) | ||
453 | return 0; | ||
454 | |||
455 | sd->sensor = NULL; | ||
456 | return -ENODEV; | ||
457 | } | ||
458 | |||
459 | |||
460 | |||
461 | /* -- module initialisation -- */ | ||
462 | static const __devinitdata struct usb_device_id device_table[] = { | ||
463 | {USB_DEVICE(0x046d, 0x0840)}, /* QuickCam Express */ | ||
464 | {USB_DEVICE(0x046d, 0x0850)}, /* LEGO cam / QuickCam Web */ | ||
465 | {USB_DEVICE(0x046d, 0x0870)}, /* Dexxa WebCam USB */ | ||
466 | {} | ||
467 | }; | ||
468 | MODULE_DEVICE_TABLE(usb, device_table); | ||
469 | |||
470 | /* -- device connect -- */ | ||
471 | static int sd_probe(struct usb_interface *intf, | ||
472 | const struct usb_device_id *id) | ||
473 | { | ||
474 | PDEBUG(D_PROBE, "Probing for a stv06xx device"); | ||
475 | return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), | ||
476 | THIS_MODULE); | ||
477 | } | ||
478 | |||
479 | void sd_disconnect(struct usb_interface *intf) | ||
480 | { | ||
481 | struct gspca_dev *gspca_dev = usb_get_intfdata(intf); | ||
482 | struct sd *sd = (struct sd *) gspca_dev; | ||
483 | PDEBUG(D_PROBE, "Disconnecting the stv06xx device"); | ||
484 | |||
485 | if (sd->sensor->disconnect) | ||
486 | sd->sensor->disconnect(sd); | ||
487 | gspca_disconnect(intf); | ||
488 | } | ||
489 | |||
490 | static struct usb_driver sd_driver = { | ||
491 | .name = MODULE_NAME, | ||
492 | .id_table = device_table, | ||
493 | .probe = sd_probe, | ||
494 | .disconnect = sd_disconnect, | ||
495 | #ifdef CONFIG_PM | ||
496 | .suspend = gspca_suspend, | ||
497 | .resume = gspca_resume, | ||
498 | #endif | ||
499 | }; | ||
500 | |||
501 | /* -- module insert / remove -- */ | ||
502 | static int __init sd_mod_init(void) | ||
503 | { | ||
504 | if (usb_register(&sd_driver) < 0) | ||
505 | return -1; | ||
506 | PDEBUG(D_PROBE, "registered"); | ||
507 | return 0; | ||
508 | } | ||
509 | static void __exit sd_mod_exit(void) | ||
510 | { | ||
511 | usb_deregister(&sd_driver); | ||
512 | PDEBUG(D_PROBE, "deregistered"); | ||
513 | } | ||
514 | |||
515 | module_init(sd_mod_init); | ||
516 | module_exit(sd_mod_exit); | ||
517 | |||
518 | module_param(dump_bridge, bool, S_IRUGO | S_IWUSR); | ||
519 | MODULE_PARM_DESC(dump_bridge, "Dumps all usb bridge registers at startup"); | ||
520 | |||
521 | module_param(dump_sensor, bool, S_IRUGO | S_IWUSR); | ||
522 | MODULE_PARM_DESC(dump_sensor, "Dumps all sensor registers at startup"); | ||
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.h b/drivers/media/video/gspca/stv06xx/stv06xx.h new file mode 100644 index 000000000000..1207e7d17f14 --- /dev/null +++ b/drivers/media/video/gspca/stv06xx/stv06xx.h | |||
@@ -0,0 +1,107 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher | ||
3 | * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland | ||
4 | * Copyright (c) 2002, 2003 Tuukka Toivonen | ||
5 | * Copyright (c) 2008 Erik Andrén | ||
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | * | ||
21 | * P/N 861037: Sensor HDCS1000 ASIC STV0600 | ||
22 | * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 | ||
23 | * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express | ||
24 | * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam | ||
25 | * P/N 861075-0040: Sensor HDCS1000 ASIC | ||
26 | * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB | ||
27 | * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web | ||
28 | */ | ||
29 | |||
30 | #ifndef STV06XX_H_ | ||
31 | #define STV06XX_H_ | ||
32 | |||
33 | #include "gspca.h" | ||
34 | |||
35 | #define MODULE_NAME "STV06xx" | ||
36 | |||
37 | #define STV_ISOC_ENDPOINT_ADDR 0x81 | ||
38 | |||
39 | #ifndef V4L2_PIX_FMT_SGRBG8 | ||
40 | #define V4L2_PIX_FMT_SGRBG8 v4l2_fourcc('G', 'R', 'B', 'G') | ||
41 | #endif | ||
42 | |||
43 | #define STV_REG23 0x0423 | ||
44 | |||
45 | /* Control registers of the STV0600 ASIC */ | ||
46 | #define STV_I2C_PARTNER 0x1420 | ||
47 | #define STV_I2C_VAL_REG_VAL_PAIRS_MIN1 0x1421 | ||
48 | #define STV_I2C_READ_WRITE_TOGGLE 0x1422 | ||
49 | #define STV_I2C_FLUSH 0x1423 | ||
50 | #define STV_I2C_SUCC_READ_REG_VALS 0x1424 | ||
51 | |||
52 | #define STV_ISO_ENABLE 0x1440 | ||
53 | #define STV_SCAN_RATE 0x1443 | ||
54 | #define STV_LED_CTRL 0x1445 | ||
55 | #define STV_STV0600_EMULATION 0x1446 | ||
56 | #define STV_REG00 0x1500 | ||
57 | #define STV_REG01 0x1501 | ||
58 | #define STV_REG02 0x1502 | ||
59 | #define STV_REG03 0x1503 | ||
60 | #define STV_REG04 0x1504 | ||
61 | |||
62 | #define STV_ISO_SIZE_L 0x15c1 | ||
63 | #define STV_ISO_SIZE_H 0x15c2 | ||
64 | |||
65 | /* Refers to the CIF 352x288 and QCIF 176x144 */ | ||
66 | /* 1: 288 lines, 2: 144 lines */ | ||
67 | #define STV_Y_CTRL 0x15c3 | ||
68 | |||
69 | /* 0xa: 352 columns, 0x6: 176 columns */ | ||
70 | #define STV_X_CTRL 0x1680 | ||
71 | |||
72 | #define STV06XX_URB_MSG_TIMEOUT 5000 | ||
73 | |||
74 | #define I2C_MAX_BYTES 16 | ||
75 | #define I2C_MAX_WORDS 8 | ||
76 | |||
77 | #define I2C_BUFFER_LENGTH 0x23 | ||
78 | #define I2C_READ_CMD 3 | ||
79 | #define I2C_WRITE_CMD 1 | ||
80 | |||
81 | #define LED_ON 1 | ||
82 | #define LED_OFF 0 | ||
83 | |||
84 | /* STV06xx device descriptor */ | ||
85 | struct sd { | ||
86 | struct gspca_dev gspca_dev; | ||
87 | |||
88 | /* A pointer to the currently connected sensor */ | ||
89 | const struct stv06xx_sensor *sensor; | ||
90 | |||
91 | /* A pointer to the sd_desc struct */ | ||
92 | struct sd_desc desc; | ||
93 | |||
94 | /* Sensor private data */ | ||
95 | void *sensor_priv; | ||
96 | }; | ||
97 | |||
98 | int stv06xx_write_bridge(struct sd *sd, u16 address, u16 i2c_data); | ||
99 | int stv06xx_read_bridge(struct sd *sd, u16 address, u8 *i2c_data); | ||
100 | |||
101 | int stv06xx_write_sensor_bytes(struct sd *sd, const u8 *data, u8 len); | ||
102 | int stv06xx_write_sensor_words(struct sd *sd, const u16 *data, u8 len); | ||
103 | |||
104 | int stv06xx_read_sensor(struct sd *sd, const u8 address, u16 *value); | ||
105 | int stv06xx_write_sensor(struct sd *sd, u8 address, u16 value); | ||
106 | |||
107 | #endif | ||
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c new file mode 100644 index 000000000000..1cfe58504553 --- /dev/null +++ b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c | |||
@@ -0,0 +1,533 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher | ||
3 | * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland | ||
4 | * Copyright (c) 2002, 2003 Tuukka Toivonen | ||
5 | * Copyright (c) 2008 Erik Andrén | ||
6 | * Copyright (c) 2008 Chia-I Wu | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | * | ||
22 | * P/N 861037: Sensor HDCS1000 ASIC STV0600 | ||
23 | * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 | ||
24 | * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express | ||
25 | * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam | ||
26 | * P/N 861075-0040: Sensor HDCS1000 ASIC | ||
27 | * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB | ||
28 | * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web | ||
29 | */ | ||
30 | |||
31 | #include "stv06xx_hdcs.h" | ||
32 | |||
33 | enum hdcs_power_state { | ||
34 | HDCS_STATE_SLEEP, | ||
35 | HDCS_STATE_IDLE, | ||
36 | HDCS_STATE_RUN | ||
37 | }; | ||
38 | |||
39 | /* no lock? */ | ||
40 | struct hdcs { | ||
41 | enum hdcs_power_state state; | ||
42 | int w, h; | ||
43 | |||
44 | /* visible area of the sensor array */ | ||
45 | struct { | ||
46 | int left, top; | ||
47 | int width, height; | ||
48 | int border; | ||
49 | } array; | ||
50 | |||
51 | struct { | ||
52 | /* Column timing overhead */ | ||
53 | u8 cto; | ||
54 | /* Column processing overhead */ | ||
55 | u8 cpo; | ||
56 | /* Row sample period constant */ | ||
57 | u16 rs; | ||
58 | /* Exposure reset duration */ | ||
59 | u16 er; | ||
60 | } exp; | ||
61 | |||
62 | int psmp; | ||
63 | }; | ||
64 | |||
65 | static int hdcs_reg_write_seq(struct sd *sd, u8 reg, u8 *vals, u8 len) | ||
66 | { | ||
67 | u8 regs[I2C_MAX_BYTES * 2]; | ||
68 | int i; | ||
69 | |||
70 | if (unlikely((len <= 0) || (len >= I2C_MAX_BYTES) || | ||
71 | (reg + len > 0xff))) | ||
72 | return -EINVAL; | ||
73 | |||
74 | for (i = 0; i < len; i++, reg++) { | ||
75 | regs[2*i] = reg; | ||
76 | regs[2*i+1] = vals[i]; | ||
77 | } | ||
78 | |||
79 | return stv06xx_write_sensor_bytes(sd, regs, len); | ||
80 | } | ||
81 | |||
82 | static int hdcs_set_state(struct sd *sd, enum hdcs_power_state state) | ||
83 | { | ||
84 | struct hdcs *hdcs = sd->sensor_priv; | ||
85 | u8 val; | ||
86 | int ret; | ||
87 | |||
88 | if (hdcs->state == state) | ||
89 | return 0; | ||
90 | |||
91 | /* we need to go idle before running or sleeping */ | ||
92 | if (hdcs->state != HDCS_STATE_IDLE) { | ||
93 | ret = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), 0); | ||
94 | if (ret) | ||
95 | return ret; | ||
96 | } | ||
97 | |||
98 | hdcs->state = HDCS_STATE_IDLE; | ||
99 | |||
100 | if (state == HDCS_STATE_IDLE) | ||
101 | return 0; | ||
102 | |||
103 | switch (state) { | ||
104 | case HDCS_STATE_SLEEP: | ||
105 | val = HDCS_SLEEP_MODE; | ||
106 | break; | ||
107 | |||
108 | case HDCS_STATE_RUN: | ||
109 | val = HDCS_RUN_ENABLE; | ||
110 | break; | ||
111 | |||
112 | default: | ||
113 | return -EINVAL; | ||
114 | } | ||
115 | |||
116 | ret = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), val); | ||
117 | if (ret < 0) | ||
118 | hdcs->state = state; | ||
119 | |||
120 | return ret; | ||
121 | } | ||
122 | |||
123 | static int hdcs_reset(struct sd *sd) | ||
124 | { | ||
125 | struct hdcs *hdcs = sd->sensor_priv; | ||
126 | int err; | ||
127 | |||
128 | err = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), 1); | ||
129 | if (err < 0) | ||
130 | return err; | ||
131 | |||
132 | err = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), 0); | ||
133 | if (err < 0) | ||
134 | hdcs->state = HDCS_STATE_IDLE; | ||
135 | |||
136 | return err; | ||
137 | } | ||
138 | |||
139 | static int hdcs_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) | ||
140 | { | ||
141 | struct sd *sd = (struct sd *) gspca_dev; | ||
142 | struct hdcs *hdcs = sd->sensor_priv; | ||
143 | |||
144 | /* Column time period */ | ||
145 | int ct; | ||
146 | /* Column processing period */ | ||
147 | int cp; | ||
148 | /* Row processing period */ | ||
149 | int rp; | ||
150 | int cycles; | ||
151 | int err; | ||
152 | int rowexp; | ||
153 | u16 data[2]; | ||
154 | |||
155 | err = stv06xx_read_sensor(sd, HDCS_ROWEXPL, &data[0]); | ||
156 | if (err < 0) | ||
157 | return err; | ||
158 | |||
159 | err = stv06xx_read_sensor(sd, HDCS_ROWEXPH, &data[1]); | ||
160 | if (err < 0) | ||
161 | return err; | ||
162 | |||
163 | rowexp = (data[1] << 8) | data[0]; | ||
164 | |||
165 | ct = hdcs->exp.cto + hdcs->psmp + (HDCS_ADC_START_SIG_DUR + 2); | ||
166 | cp = hdcs->exp.cto + (hdcs->w * ct / 2); | ||
167 | rp = hdcs->exp.rs + cp; | ||
168 | |||
169 | cycles = rp * rowexp; | ||
170 | *val = cycles / HDCS_CLK_FREQ_MHZ; | ||
171 | PDEBUG(D_V4L2, "Read exposure %d", *val); | ||
172 | return 0; | ||
173 | } | ||
174 | |||
175 | static int hdcs_set_exposure(struct gspca_dev *gspca_dev, __s32 val) | ||
176 | { | ||
177 | struct sd *sd = (struct sd *) gspca_dev; | ||
178 | struct hdcs *hdcs = sd->sensor_priv; | ||
179 | int rowexp, srowexp; | ||
180 | int max_srowexp; | ||
181 | /* Column time period */ | ||
182 | int ct; | ||
183 | /* Column processing period */ | ||
184 | int cp; | ||
185 | /* Row processing period */ | ||
186 | int rp; | ||
187 | /* Minimum number of column timing periods | ||
188 | within the column processing period */ | ||
189 | int mnct; | ||
190 | int cycles, err; | ||
191 | u8 exp[4]; | ||
192 | |||
193 | cycles = val * HDCS_CLK_FREQ_MHZ; | ||
194 | |||
195 | ct = hdcs->exp.cto + hdcs->psmp + (HDCS_ADC_START_SIG_DUR + 2); | ||
196 | cp = hdcs->exp.cto + (hdcs->w * ct / 2); | ||
197 | |||
198 | /* the cycles one row takes */ | ||
199 | rp = hdcs->exp.rs + cp; | ||
200 | |||
201 | rowexp = cycles / rp; | ||
202 | |||
203 | /* the remaining cycles */ | ||
204 | cycles -= rowexp * rp; | ||
205 | |||
206 | /* calculate sub-row exposure */ | ||
207 | if (IS_1020(sd)) { | ||
208 | /* see HDCS-1020 datasheet 3.5.6.4, p. 63 */ | ||
209 | srowexp = hdcs->w - (cycles + hdcs->exp.er + 13) / ct; | ||
210 | |||
211 | mnct = (hdcs->exp.er + 12 + ct - 1) / ct; | ||
212 | max_srowexp = hdcs->w - mnct; | ||
213 | } else { | ||
214 | /* see HDCS-1000 datasheet 3.4.5.5, p. 61 */ | ||
215 | srowexp = cp - hdcs->exp.er - 6 - cycles; | ||
216 | |||
217 | mnct = (hdcs->exp.er + 5 + ct - 1) / ct; | ||
218 | max_srowexp = cp - mnct * ct - 1; | ||
219 | } | ||
220 | |||
221 | if (srowexp < 0) | ||
222 | srowexp = 0; | ||
223 | else if (srowexp > max_srowexp) | ||
224 | srowexp = max_srowexp; | ||
225 | |||
226 | if (IS_1020(sd)) { | ||
227 | exp[0] = rowexp & 0xff; | ||
228 | exp[1] = rowexp >> 8; | ||
229 | exp[2] = (srowexp >> 2) & 0xff; | ||
230 | /* this clears exposure error flag */ | ||
231 | exp[3] = 0x1; | ||
232 | err = hdcs_reg_write_seq(sd, HDCS_ROWEXPL, exp, 4); | ||
233 | } else { | ||
234 | exp[0] = rowexp & 0xff; | ||
235 | exp[1] = rowexp >> 8; | ||
236 | exp[2] = srowexp & 0xff; | ||
237 | exp[3] = srowexp >> 8; | ||
238 | err = hdcs_reg_write_seq(sd, HDCS_ROWEXPL, exp, 4); | ||
239 | if (err < 0) | ||
240 | return err; | ||
241 | |||
242 | /* clear exposure error flag */ | ||
243 | err = stv06xx_write_sensor(sd, | ||
244 | HDCS_STATUS, BIT(4)); | ||
245 | } | ||
246 | PDEBUG(D_V4L2, "Writing exposure %d, rowexp %d, srowexp %d", | ||
247 | val, rowexp, srowexp); | ||
248 | return err; | ||
249 | } | ||
250 | |||
251 | static int hdcs_set_gains(struct sd *sd, u8 r, u8 g, u8 b) | ||
252 | { | ||
253 | u8 gains[4]; | ||
254 | |||
255 | /* the voltage gain Av = (1 + 19 * val / 127) * (1 + bit7) */ | ||
256 | if (r > 127) | ||
257 | r = 0x80 | (r / 2); | ||
258 | if (g > 127) | ||
259 | g = 0x80 | (g / 2); | ||
260 | if (b > 127) | ||
261 | b = 0x80 | (b / 2); | ||
262 | |||
263 | gains[0] = g; | ||
264 | gains[1] = r; | ||
265 | gains[2] = b; | ||
266 | gains[3] = g; | ||
267 | |||
268 | return hdcs_reg_write_seq(sd, HDCS_ERECPGA, gains, 4); | ||
269 | } | ||
270 | |||
271 | static int hdcs_get_gain(struct gspca_dev *gspca_dev, __s32 *val) | ||
272 | { | ||
273 | struct sd *sd = (struct sd *) gspca_dev; | ||
274 | int err; | ||
275 | u16 data; | ||
276 | |||
277 | err = stv06xx_read_sensor(sd, HDCS_ERECPGA, &data); | ||
278 | |||
279 | /* Bit 7 doubles the gain */ | ||
280 | if (data & 0x80) | ||
281 | *val = (data & 0x7f) * 2; | ||
282 | else | ||
283 | *val = data; | ||
284 | |||
285 | PDEBUG(D_V4L2, "Read gain %d", *val); | ||
286 | return err; | ||
287 | } | ||
288 | |||
289 | static int hdcs_set_gain(struct gspca_dev *gspca_dev, __s32 val) | ||
290 | { | ||
291 | PDEBUG(D_V4L2, "Writing gain %d", val); | ||
292 | return hdcs_set_gains((struct sd *) gspca_dev, | ||
293 | val & 0xff, val & 0xff, val & 0xff); | ||
294 | } | ||
295 | |||
296 | static int hdcs_set_size(struct sd *sd, | ||
297 | unsigned int width, unsigned int height) | ||
298 | { | ||
299 | struct hdcs *hdcs = sd->sensor_priv; | ||
300 | u8 win[4]; | ||
301 | unsigned int x, y; | ||
302 | int err; | ||
303 | |||
304 | /* must be multiple of 4 */ | ||
305 | width = (width + 3) & ~0x3; | ||
306 | height = (height + 3) & ~0x3; | ||
307 | |||
308 | if (width > hdcs->array.width) | ||
309 | width = hdcs->array.width; | ||
310 | |||
311 | if (IS_1020(sd)) { | ||
312 | /* the borders are also invalid */ | ||
313 | if (height + 2 * hdcs->array.border + HDCS_1020_BOTTOM_Y_SKIP | ||
314 | > hdcs->array.height) | ||
315 | height = hdcs->array.height - 2 * hdcs->array.border - | ||
316 | HDCS_1020_BOTTOM_Y_SKIP; | ||
317 | |||
318 | y = (hdcs->array.height - HDCS_1020_BOTTOM_Y_SKIP - height) / 2 | ||
319 | + hdcs->array.top; | ||
320 | } else if (height > hdcs->array.height) { | ||
321 | height = hdcs->array.height; | ||
322 | y = hdcs->array.top + (hdcs->array.height - height) / 2; | ||
323 | } | ||
324 | |||
325 | x = hdcs->array.left + (hdcs->array.width - width) / 2; | ||
326 | |||
327 | win[0] = y / 4; | ||
328 | win[1] = x / 4; | ||
329 | win[2] = (y + height) / 4 - 1; | ||
330 | win[3] = (x + width) / 4 - 1; | ||
331 | |||
332 | err = hdcs_reg_write_seq(sd, HDCS_FWROW, win, 4); | ||
333 | if (err < 0) | ||
334 | return err; | ||
335 | |||
336 | /* Update the current width and height */ | ||
337 | hdcs->w = width; | ||
338 | hdcs->h = height; | ||
339 | return err; | ||
340 | } | ||
341 | |||
342 | static int hdcs_probe_1x00(struct sd *sd) | ||
343 | { | ||
344 | struct hdcs *hdcs; | ||
345 | u16 sensor; | ||
346 | int ret; | ||
347 | |||
348 | ret = stv06xx_read_sensor(sd, HDCS_IDENT, &sensor); | ||
349 | if (ret < 0 || sensor != 0x08) | ||
350 | return -ENODEV; | ||
351 | |||
352 | info("HDCS-1000/1100 sensor detected"); | ||
353 | |||
354 | sd->gspca_dev.cam.cam_mode = stv06xx_sensor_hdcs1x00.modes; | ||
355 | sd->gspca_dev.cam.nmodes = stv06xx_sensor_hdcs1x00.nmodes; | ||
356 | sd->desc.ctrls = stv06xx_sensor_hdcs1x00.ctrls; | ||
357 | sd->desc.nctrls = stv06xx_sensor_hdcs1x00.nctrls; | ||
358 | |||
359 | hdcs = kmalloc(sizeof(struct hdcs), GFP_KERNEL); | ||
360 | if (!hdcs) | ||
361 | return -ENOMEM; | ||
362 | |||
363 | hdcs->array.left = 8; | ||
364 | hdcs->array.top = 8; | ||
365 | hdcs->array.width = HDCS_1X00_DEF_WIDTH; | ||
366 | hdcs->array.height = HDCS_1X00_DEF_HEIGHT; | ||
367 | hdcs->array.border = 4; | ||
368 | |||
369 | hdcs->exp.cto = 4; | ||
370 | hdcs->exp.cpo = 2; | ||
371 | hdcs->exp.rs = 186; | ||
372 | hdcs->exp.er = 100; | ||
373 | |||
374 | /* | ||
375 | * Frame rate on HDCS-1000 0x46D:0x840 depends on PSMP: | ||
376 | * 4 = doesn't work at all | ||
377 | * 5 = 7.8 fps, | ||
378 | * 6 = 6.9 fps, | ||
379 | * 8 = 6.3 fps, | ||
380 | * 10 = 5.5 fps, | ||
381 | * 15 = 4.4 fps, | ||
382 | * 31 = 2.8 fps | ||
383 | * | ||
384 | * Frame rate on HDCS-1000 0x46D:0x870 depends on PSMP: | ||
385 | * 15 = doesn't work at all | ||
386 | * 18 = doesn't work at all | ||
387 | * 19 = 7.3 fps | ||
388 | * 20 = 7.4 fps | ||
389 | * 21 = 7.4 fps | ||
390 | * 22 = 7.4 fps | ||
391 | * 24 = 6.3 fps | ||
392 | * 30 = 5.4 fps | ||
393 | */ | ||
394 | hdcs->psmp = IS_870(sd) ? 20 : 5; | ||
395 | |||
396 | sd->sensor_priv = hdcs; | ||
397 | |||
398 | return 0; | ||
399 | } | ||
400 | |||
401 | static int hdcs_probe_1020(struct sd *sd) | ||
402 | { | ||
403 | struct hdcs *hdcs; | ||
404 | u16 sensor; | ||
405 | int ret; | ||
406 | |||
407 | ret = stv06xx_read_sensor(sd, HDCS_IDENT, &sensor); | ||
408 | if (ret < 0 || sensor != 0x10) | ||
409 | return -ENODEV; | ||
410 | |||
411 | info("HDCS-1020 sensor detected"); | ||
412 | |||
413 | sd->gspca_dev.cam.cam_mode = stv06xx_sensor_hdcs1020.modes; | ||
414 | sd->gspca_dev.cam.nmodes = stv06xx_sensor_hdcs1020.nmodes; | ||
415 | sd->desc.ctrls = stv06xx_sensor_hdcs1020.ctrls; | ||
416 | sd->desc.nctrls = stv06xx_sensor_hdcs1020.nctrls; | ||
417 | |||
418 | hdcs = kmalloc(sizeof(struct hdcs), GFP_KERNEL); | ||
419 | if (!hdcs) | ||
420 | return -ENOMEM; | ||
421 | |||
422 | /* | ||
423 | * From Andrey's test image: looks like HDCS-1020 upper-left | ||
424 | * visible pixel is at 24,8 (y maybe even smaller?) and lower-right | ||
425 | * visible pixel at 375,299 (x maybe even larger?) | ||
426 | */ | ||
427 | hdcs->array.left = 24; | ||
428 | hdcs->array.top = 4; | ||
429 | hdcs->array.width = HDCS_1020_DEF_WIDTH; | ||
430 | hdcs->array.height = 304; | ||
431 | hdcs->array.border = 4; | ||
432 | |||
433 | hdcs->psmp = 6; | ||
434 | |||
435 | hdcs->exp.cto = 3; | ||
436 | hdcs->exp.cpo = 3; | ||
437 | hdcs->exp.rs = 155; | ||
438 | hdcs->exp.er = 96; | ||
439 | |||
440 | sd->sensor_priv = hdcs; | ||
441 | |||
442 | return 0; | ||
443 | } | ||
444 | |||
445 | static int hdcs_start(struct sd *sd) | ||
446 | { | ||
447 | PDEBUG(D_STREAM, "Starting stream"); | ||
448 | |||
449 | return hdcs_set_state(sd, HDCS_STATE_RUN); | ||
450 | } | ||
451 | |||
452 | static int hdcs_stop(struct sd *sd) | ||
453 | { | ||
454 | PDEBUG(D_STREAM, "Halting stream"); | ||
455 | |||
456 | return hdcs_set_state(sd, HDCS_STATE_SLEEP); | ||
457 | } | ||
458 | |||
459 | static void hdcs_disconnect(struct sd *sd) | ||
460 | { | ||
461 | PDEBUG(D_PROBE, "Disconnecting the sensor"); | ||
462 | kfree(sd->sensor_priv); | ||
463 | } | ||
464 | |||
465 | static int hdcs_init(struct sd *sd) | ||
466 | { | ||
467 | struct hdcs *hdcs = sd->sensor_priv; | ||
468 | int i, err = 0; | ||
469 | |||
470 | /* Set the STV0602AA in STV0600 emulation mode */ | ||
471 | if (IS_870(sd)) | ||
472 | stv06xx_write_bridge(sd, STV_STV0600_EMULATION, 1); | ||
473 | |||
474 | /* Execute the bridge init */ | ||
475 | for (i = 0; i < ARRAY_SIZE(stv_bridge_init) && !err; i++) { | ||
476 | err = stv06xx_write_bridge(sd, stv_bridge_init[i][0], | ||
477 | stv_bridge_init[i][1]); | ||
478 | } | ||
479 | if (err < 0) | ||
480 | return err; | ||
481 | |||
482 | /* sensor soft reset */ | ||
483 | hdcs_reset(sd); | ||
484 | |||
485 | /* Execute the sensor init */ | ||
486 | for (i = 0; i < ARRAY_SIZE(stv_sensor_init) && !err; i++) { | ||
487 | err = stv06xx_write_sensor(sd, stv_sensor_init[i][0], | ||
488 | stv_sensor_init[i][1]); | ||
489 | } | ||
490 | if (err < 0) | ||
491 | return err; | ||
492 | |||
493 | /* Enable continous frame capture, bit 2: stop when frame complete */ | ||
494 | err = stv06xx_write_sensor(sd, HDCS_REG_CONFIG(sd), BIT(3)); | ||
495 | if (err < 0) | ||
496 | return err; | ||
497 | |||
498 | /* Set PGA sample duration | ||
499 | (was 0x7E for IS_870, but caused slow framerate with HDCS-1020) */ | ||
500 | if (IS_1020(sd)) | ||
501 | err = stv06xx_write_sensor(sd, HDCS_TCTRL, | ||
502 | (HDCS_ADC_START_SIG_DUR << 6) | hdcs->psmp); | ||
503 | else | ||
504 | err = stv06xx_write_sensor(sd, HDCS_TCTRL, | ||
505 | (HDCS_ADC_START_SIG_DUR << 5) | hdcs->psmp); | ||
506 | if (err < 0) | ||
507 | return err; | ||
508 | |||
509 | err = hdcs_set_gains(sd, HDCS_DEFAULT_GAIN, HDCS_DEFAULT_GAIN, | ||
510 | HDCS_DEFAULT_GAIN); | ||
511 | if (err < 0) | ||
512 | return err; | ||
513 | |||
514 | err = hdcs_set_exposure(&sd->gspca_dev, HDCS_DEFAULT_EXPOSURE); | ||
515 | if (err < 0) | ||
516 | return err; | ||
517 | |||
518 | err = hdcs_set_size(sd, hdcs->array.width, hdcs->array.height); | ||
519 | return err; | ||
520 | } | ||
521 | |||
522 | static int hdcs_dump(struct sd *sd) | ||
523 | { | ||
524 | u16 reg, val; | ||
525 | |||
526 | info("Dumping sensor registers:"); | ||
527 | |||
528 | for (reg = HDCS_IDENT; reg <= HDCS_ROWEXPH; reg++) { | ||
529 | stv06xx_read_sensor(sd, reg, &val); | ||
530 | info("reg 0x%02x = 0x%02x", reg, val); | ||
531 | } | ||
532 | return 0; | ||
533 | } | ||
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h new file mode 100644 index 000000000000..9c7279a4cd88 --- /dev/null +++ b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h | |||
@@ -0,0 +1,263 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher | ||
3 | * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland | ||
4 | * Copyright (c) 2002, 2003 Tuukka Toivonen | ||
5 | * Copyright (c) 2008 Erik Andrén | ||
6 | * Copyright (c) 2008 Chia-I Wu | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | * | ||
22 | * P/N 861037: Sensor HDCS1000 ASIC STV0600 | ||
23 | * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 | ||
24 | * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express | ||
25 | * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam | ||
26 | * P/N 861075-0040: Sensor HDCS1000 ASIC | ||
27 | * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB | ||
28 | * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web | ||
29 | */ | ||
30 | |||
31 | #ifndef STV06XX_HDCS_H_ | ||
32 | #define STV06XX_HDCS_H_ | ||
33 | |||
34 | #include "stv06xx_sensor.h" | ||
35 | |||
36 | #define HDCS_REG_CONFIG(sd) (IS_1020(sd) ? HDCS20_CONFIG : HDCS00_CONFIG) | ||
37 | #define HDCS_REG_CONTROL(sd) (IS_1020(sd) ? HDCS20_CONTROL : HDCS00_CONTROL) | ||
38 | |||
39 | #define HDCS_1X00_DEF_WIDTH 360 | ||
40 | #define HDCS_1X00_DEF_HEIGHT 296 | ||
41 | |||
42 | #define HDCS_1020_DEF_WIDTH 352 | ||
43 | #define HDCS_1020_DEF_HEIGHT 292 | ||
44 | |||
45 | #define HDCS_1020_BOTTOM_Y_SKIP 4 | ||
46 | |||
47 | #define HDCS_CLK_FREQ_MHZ 25 | ||
48 | |||
49 | #define HDCS_ADC_START_SIG_DUR 3 | ||
50 | |||
51 | /* LSB bit of I2C or register address signifies write (0) or read (1) */ | ||
52 | /* I2C Registers common for both HDCS-1000/1100 and HDCS-1020 */ | ||
53 | /* Identifications Register */ | ||
54 | #define HDCS_IDENT (0x00 << 1) | ||
55 | /* Status Register */ | ||
56 | #define HDCS_STATUS (0x01 << 1) | ||
57 | /* Interrupt Mask Register */ | ||
58 | #define HDCS_IMASK (0x02 << 1) | ||
59 | /* Pad Control Register */ | ||
60 | #define HDCS_PCTRL (0x03 << 1) | ||
61 | /* Pad Drive Control Register */ | ||
62 | #define HDCS_PDRV (0x04 << 1) | ||
63 | /* Interface Control Register */ | ||
64 | #define HDCS_ICTRL (0x05 << 1) | ||
65 | /* Interface Timing Register */ | ||
66 | #define HDCS_ITMG (0x06 << 1) | ||
67 | /* Baud Fraction Register */ | ||
68 | #define HDCS_BFRAC (0x07 << 1) | ||
69 | /* Baud Rate Register */ | ||
70 | #define HDCS_BRATE (0x08 << 1) | ||
71 | /* ADC Control Register */ | ||
72 | #define HDCS_ADCCTRL (0x09 << 1) | ||
73 | /* First Window Row Register */ | ||
74 | #define HDCS_FWROW (0x0a << 1) | ||
75 | /* First Window Column Register */ | ||
76 | #define HDCS_FWCOL (0x0b << 1) | ||
77 | /* Last Window Row Register */ | ||
78 | #define HDCS_LWROW (0x0c << 1) | ||
79 | /* Last Window Column Register */ | ||
80 | #define HDCS_LWCOL (0x0d << 1) | ||
81 | /* Timing Control Register */ | ||
82 | #define HDCS_TCTRL (0x0e << 1) | ||
83 | /* PGA Gain Register: Even Row, Even Column */ | ||
84 | #define HDCS_ERECPGA (0x0f << 1) | ||
85 | /* PGA Gain Register: Even Row, Odd Column */ | ||
86 | #define HDCS_EROCPGA (0x10 << 1) | ||
87 | /* PGA Gain Register: Odd Row, Even Column */ | ||
88 | #define HDCS_ORECPGA (0x11 << 1) | ||
89 | /* PGA Gain Register: Odd Row, Odd Column */ | ||
90 | #define HDCS_OROCPGA (0x12 << 1) | ||
91 | /* Row Exposure Low Register */ | ||
92 | #define HDCS_ROWEXPL (0x13 << 1) | ||
93 | /* Row Exposure High Register */ | ||
94 | #define HDCS_ROWEXPH (0x14 << 1) | ||
95 | |||
96 | /* I2C Registers only for HDCS-1000/1100 */ | ||
97 | /* Sub-Row Exposure Low Register */ | ||
98 | #define HDCS00_SROWEXPL (0x15 << 1) | ||
99 | /* Sub-Row Exposure High Register */ | ||
100 | #define HDCS00_SROWEXPH (0x16 << 1) | ||
101 | /* Configuration Register */ | ||
102 | #define HDCS00_CONFIG (0x17 << 1) | ||
103 | /* Control Register */ | ||
104 | #define HDCS00_CONTROL (0x18 << 1) | ||
105 | |||
106 | /* I2C Registers only for HDCS-1020 */ | ||
107 | /* Sub-Row Exposure Register */ | ||
108 | #define HDCS20_SROWEXP (0x15 << 1) | ||
109 | /* Error Control Register */ | ||
110 | #define HDCS20_ERROR (0x16 << 1) | ||
111 | /* Interface Timing 2 Register */ | ||
112 | #define HDCS20_ITMG2 (0x17 << 1) | ||
113 | /* Interface Control 2 Register */ | ||
114 | #define HDCS20_ICTRL2 (0x18 << 1) | ||
115 | /* Horizontal Blank Register */ | ||
116 | #define HDCS20_HBLANK (0x19 << 1) | ||
117 | /* Vertical Blank Register */ | ||
118 | #define HDCS20_VBLANK (0x1a << 1) | ||
119 | /* Configuration Register */ | ||
120 | #define HDCS20_CONFIG (0x1b << 1) | ||
121 | /* Control Register */ | ||
122 | #define HDCS20_CONTROL (0x1c << 1) | ||
123 | |||
124 | #define HDCS_RUN_ENABLE (1 << 2) | ||
125 | #define HDCS_SLEEP_MODE (1 << 1) | ||
126 | |||
127 | #define HDCS_DEFAULT_EXPOSURE 5000 | ||
128 | #define HDCS_DEFAULT_GAIN 128 | ||
129 | |||
130 | static int hdcs_probe_1x00(struct sd *sd); | ||
131 | static int hdcs_probe_1020(struct sd *sd); | ||
132 | static int hdcs_start(struct sd *sd); | ||
133 | static int hdcs_init(struct sd *sd); | ||
134 | static int hdcs_stop(struct sd *sd); | ||
135 | static int hdcs_dump(struct sd *sd); | ||
136 | static void hdcs_disconnect(struct sd *sd); | ||
137 | |||
138 | static int hdcs_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); | ||
139 | static int hdcs_set_exposure(struct gspca_dev *gspca_dev, __s32 val); | ||
140 | static int hdcs_set_gain(struct gspca_dev *gspca_dev, __s32 val); | ||
141 | static int hdcs_get_gain(struct gspca_dev *gspca_dev, __s32 *val); | ||
142 | |||
143 | const struct stv06xx_sensor stv06xx_sensor_hdcs1x00 = { | ||
144 | .name = "HP HDCS-1000/1100", | ||
145 | .i2c_flush = 0, | ||
146 | .i2c_addr = (0x55 << 1), | ||
147 | .i2c_len = 1, | ||
148 | |||
149 | .init = hdcs_init, | ||
150 | .probe = hdcs_probe_1x00, | ||
151 | .start = hdcs_start, | ||
152 | .stop = hdcs_stop, | ||
153 | .disconnect = hdcs_disconnect, | ||
154 | .dump = hdcs_dump, | ||
155 | |||
156 | .nctrls = 2, | ||
157 | .ctrls = { | ||
158 | { | ||
159 | { | ||
160 | .id = V4L2_CID_EXPOSURE, | ||
161 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
162 | .name = "exposure", | ||
163 | .minimum = 0x00, | ||
164 | .maximum = 0xffff, | ||
165 | .step = 0x1, | ||
166 | .default_value = HDCS_DEFAULT_EXPOSURE, | ||
167 | .flags = V4L2_CTRL_FLAG_SLIDER | ||
168 | }, | ||
169 | .set = hdcs_set_exposure, | ||
170 | .get = hdcs_get_exposure | ||
171 | }, | ||
172 | { | ||
173 | { | ||
174 | .id = V4L2_CID_GAIN, | ||
175 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
176 | .name = "gain", | ||
177 | .minimum = 0x00, | ||
178 | .maximum = 0xff, | ||
179 | .step = 0x1, | ||
180 | .default_value = HDCS_DEFAULT_GAIN, | ||
181 | .flags = V4L2_CTRL_FLAG_SLIDER | ||
182 | }, | ||
183 | .set = hdcs_set_gain, | ||
184 | .get = hdcs_get_gain | ||
185 | } | ||
186 | }, | ||
187 | |||
188 | .nmodes = 1, | ||
189 | .modes = { | ||
190 | { | ||
191 | HDCS_1X00_DEF_WIDTH, | ||
192 | HDCS_1X00_DEF_HEIGHT, | ||
193 | V4L2_PIX_FMT_SBGGR8, | ||
194 | V4L2_FIELD_NONE, | ||
195 | .sizeimage = | ||
196 | HDCS_1X00_DEF_WIDTH * HDCS_1X00_DEF_HEIGHT, | ||
197 | .bytesperline = HDCS_1X00_DEF_WIDTH, | ||
198 | .colorspace = V4L2_COLORSPACE_SRGB, | ||
199 | .priv = 1 | ||
200 | } | ||
201 | } | ||
202 | }; | ||
203 | |||
204 | const struct stv06xx_sensor stv06xx_sensor_hdcs1020 = { | ||
205 | .name = "HDCS-1020", | ||
206 | .i2c_flush = 0, | ||
207 | .i2c_addr = (0x55 << 1), | ||
208 | .i2c_len = 1, | ||
209 | |||
210 | .nctrls = 0, | ||
211 | .ctrls = {}, | ||
212 | |||
213 | .init = hdcs_init, | ||
214 | .probe = hdcs_probe_1020, | ||
215 | .start = hdcs_start, | ||
216 | .stop = hdcs_stop, | ||
217 | .dump = hdcs_dump, | ||
218 | |||
219 | .nmodes = 1, | ||
220 | .modes = { | ||
221 | { | ||
222 | HDCS_1020_DEF_WIDTH, | ||
223 | HDCS_1020_DEF_HEIGHT, | ||
224 | V4L2_PIX_FMT_SBGGR8, | ||
225 | V4L2_FIELD_NONE, | ||
226 | .sizeimage = | ||
227 | HDCS_1020_DEF_WIDTH * HDCS_1020_DEF_HEIGHT, | ||
228 | .bytesperline = HDCS_1020_DEF_WIDTH, | ||
229 | .colorspace = V4L2_COLORSPACE_SRGB, | ||
230 | .priv = 1 | ||
231 | } | ||
232 | } | ||
233 | }; | ||
234 | |||
235 | static const u16 stv_bridge_init[][2] = { | ||
236 | {STV_ISO_ENABLE, 0}, | ||
237 | {STV_REG23, 0}, | ||
238 | {STV_REG00, 0x1d}, | ||
239 | {STV_REG01, 0xb5}, | ||
240 | {STV_REG02, 0xa8}, | ||
241 | {STV_REG03, 0x95}, | ||
242 | {STV_REG04, 0x07}, | ||
243 | |||
244 | {STV_SCAN_RATE, 0x20}, | ||
245 | {STV_ISO_SIZE_L, 847}, | ||
246 | {STV_Y_CTRL, 0x01}, | ||
247 | {STV_X_CTRL, 0x0a} | ||
248 | }; | ||
249 | |||
250 | static const u8 stv_sensor_init[][2] = { | ||
251 | /* Clear status (writing 1 will clear the corresponding status bit) */ | ||
252 | {HDCS_STATUS, BIT(6) | BIT(5) | BIT(4) | BIT(3) | BIT(2) | BIT(1)}, | ||
253 | /* Disable all interrupts */ | ||
254 | {HDCS_IMASK, 0x00}, | ||
255 | {HDCS_PCTRL, BIT(6) | BIT(5) | BIT(1) | BIT(0)}, | ||
256 | {HDCS_PDRV, 0x00}, | ||
257 | {HDCS_ICTRL, BIT(5)}, | ||
258 | {HDCS_ITMG, BIT(4) | BIT(1)}, | ||
259 | /* ADC output resolution to 10 bits */ | ||
260 | {HDCS_ADCCTRL, 10} | ||
261 | }; | ||
262 | |||
263 | #endif | ||
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c new file mode 100644 index 000000000000..d0a0f8596454 --- /dev/null +++ b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c | |||
@@ -0,0 +1,430 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher | ||
3 | * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland | ||
4 | * Copyright (c) 2002, 2003 Tuukka Toivonen | ||
5 | * Copyright (c) 2008 Erik Andrén | ||
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | * | ||
21 | * P/N 861037: Sensor HDCS1000 ASIC STV0600 | ||
22 | * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 | ||
23 | * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express | ||
24 | * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam | ||
25 | * P/N 861075-0040: Sensor HDCS1000 ASIC | ||
26 | * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB | ||
27 | * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web | ||
28 | */ | ||
29 | |||
30 | /* | ||
31 | * The spec file for the PB-0100 suggests the following for best quality | ||
32 | * images after the sensor has been reset : | ||
33 | * | ||
34 | * PB_ADCGAINL = R60 = 0x03 (3 dec) : sets low reference of ADC | ||
35 | to produce good black level | ||
36 | * PB_PREADCTRL = R32 = 0x1400 (5120 dec) : Enables global gain changes | ||
37 | through R53 | ||
38 | * PB_ADCMINGAIN = R52 = 0x10 (16 dec) : Sets the minimum gain for | ||
39 | auto-exposure | ||
40 | * PB_ADCGLOBALGAIN = R53 = 0x10 (16 dec) : Sets the global gain | ||
41 | * PB_EXPGAIN = R14 = 0x11 (17 dec) : Sets the auto-exposure value | ||
42 | * PB_UPDATEINT = R23 = 0x02 (2 dec) : Sets the speed on | ||
43 | auto-exposure routine | ||
44 | * PB_CFILLIN = R5 = 0x0E (14 dec) : Sets the frame rate | ||
45 | */ | ||
46 | |||
47 | #include "stv06xx_pb0100.h" | ||
48 | |||
49 | static int pb0100_probe(struct sd *sd) | ||
50 | { | ||
51 | u16 sensor; | ||
52 | int i, err; | ||
53 | s32 *sensor_settings; | ||
54 | |||
55 | err = stv06xx_read_sensor(sd, PB_IDENT, &sensor); | ||
56 | |||
57 | if (err < 0) | ||
58 | return -ENODEV; | ||
59 | |||
60 | if ((sensor >> 8) == 0x64) { | ||
61 | sensor_settings = kmalloc( | ||
62 | stv06xx_sensor_pb0100.nctrls * sizeof(s32), | ||
63 | GFP_KERNEL); | ||
64 | if (!sensor_settings) | ||
65 | return -ENOMEM; | ||
66 | |||
67 | info("Photobit pb0100 sensor detected"); | ||
68 | |||
69 | sd->gspca_dev.cam.cam_mode = stv06xx_sensor_pb0100.modes; | ||
70 | sd->gspca_dev.cam.nmodes = stv06xx_sensor_pb0100.nmodes; | ||
71 | sd->desc.ctrls = stv06xx_sensor_pb0100.ctrls; | ||
72 | sd->desc.nctrls = stv06xx_sensor_pb0100.nctrls; | ||
73 | for (i = 0; i < stv06xx_sensor_pb0100.nctrls; i++) | ||
74 | sensor_settings[i] = stv06xx_sensor_pb0100. | ||
75 | ctrls[i].qctrl.default_value; | ||
76 | sd->sensor_priv = sensor_settings; | ||
77 | |||
78 | return 0; | ||
79 | } | ||
80 | |||
81 | return -ENODEV; | ||
82 | } | ||
83 | |||
84 | static int pb0100_start(struct sd *sd) | ||
85 | { | ||
86 | int err; | ||
87 | struct cam *cam = &sd->gspca_dev.cam; | ||
88 | s32 *sensor_settings = sd->sensor_priv; | ||
89 | u32 mode = cam->cam_mode[sd->gspca_dev.curr_mode].priv; | ||
90 | |||
91 | /* Setup sensor window */ | ||
92 | if (mode & PB0100_CROP_TO_VGA) { | ||
93 | stv06xx_write_sensor(sd, PB_RSTART, 30); | ||
94 | stv06xx_write_sensor(sd, PB_CSTART, 20); | ||
95 | stv06xx_write_sensor(sd, PB_RWSIZE, 240 - 1); | ||
96 | stv06xx_write_sensor(sd, PB_CWSIZE, 320 - 1); | ||
97 | } else { | ||
98 | stv06xx_write_sensor(sd, PB_RSTART, 8); | ||
99 | stv06xx_write_sensor(sd, PB_CSTART, 4); | ||
100 | stv06xx_write_sensor(sd, PB_RWSIZE, 288 - 1); | ||
101 | stv06xx_write_sensor(sd, PB_CWSIZE, 352 - 1); | ||
102 | } | ||
103 | |||
104 | if (mode & PB0100_SUBSAMPLE) { | ||
105 | stv06xx_write_bridge(sd, STV_Y_CTRL, 0x02); /* Wrong, FIXME */ | ||
106 | stv06xx_write_bridge(sd, STV_X_CTRL, 0x06); | ||
107 | |||
108 | stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x10); | ||
109 | } else { | ||
110 | stv06xx_write_bridge(sd, STV_Y_CTRL, 0x01); | ||
111 | stv06xx_write_bridge(sd, STV_X_CTRL, 0x0a); | ||
112 | /* larger -> slower */ | ||
113 | stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x20); | ||
114 | } | ||
115 | |||
116 | /* set_gain also sets red and blue balance */ | ||
117 | pb0100_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]); | ||
118 | pb0100_set_exposure(&sd->gspca_dev, sensor_settings[EXPOSURE_IDX]); | ||
119 | pb0100_set_autogain_target(&sd->gspca_dev, | ||
120 | sensor_settings[AUTOGAIN_TARGET_IDX]); | ||
121 | pb0100_set_autogain(&sd->gspca_dev, sensor_settings[AUTOGAIN_IDX]); | ||
122 | |||
123 | err = stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3)|BIT(1)); | ||
124 | PDEBUG(D_STREAM, "Started stream, status: %d", err); | ||
125 | |||
126 | return (err < 0) ? err : 0; | ||
127 | } | ||
128 | |||
129 | static int pb0100_stop(struct sd *sd) | ||
130 | { | ||
131 | int err; | ||
132 | |||
133 | err = stv06xx_write_sensor(sd, PB_ABORTFRAME, 1); | ||
134 | |||
135 | if (err < 0) | ||
136 | goto out; | ||
137 | |||
138 | /* Set bit 1 to zero */ | ||
139 | err = stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3)); | ||
140 | |||
141 | PDEBUG(D_STREAM, "Halting stream"); | ||
142 | out: | ||
143 | return (err < 0) ? err : 0; | ||
144 | } | ||
145 | |||
146 | /* FIXME: Sort the init commands out and put them into tables, | ||
147 | this is only for getting the camera to work */ | ||
148 | /* FIXME: No error handling for now, | ||
149 | add this once the init has been converted to proper tables */ | ||
150 | static int pb0100_init(struct sd *sd) | ||
151 | { | ||
152 | stv06xx_write_bridge(sd, STV_REG00, 1); | ||
153 | stv06xx_write_bridge(sd, STV_SCAN_RATE, 0); | ||
154 | |||
155 | /* Reset sensor */ | ||
156 | stv06xx_write_sensor(sd, PB_RESET, 1); | ||
157 | stv06xx_write_sensor(sd, PB_RESET, 0); | ||
158 | |||
159 | /* Disable chip */ | ||
160 | stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3)); | ||
161 | |||
162 | /* Gain stuff...*/ | ||
163 | stv06xx_write_sensor(sd, PB_PREADCTRL, BIT(12)|BIT(10)|BIT(6)); | ||
164 | stv06xx_write_sensor(sd, PB_ADCGLOBALGAIN, 12); | ||
165 | |||
166 | /* Set up auto-exposure */ | ||
167 | /* ADC VREF_HI new setting for a transition | ||
168 | from the Expose1 to the Expose2 setting */ | ||
169 | stv06xx_write_sensor(sd, PB_R28, 12); | ||
170 | /* gain max for autoexposure */ | ||
171 | stv06xx_write_sensor(sd, PB_ADCMAXGAIN, 180); | ||
172 | /* gain min for autoexposure */ | ||
173 | stv06xx_write_sensor(sd, PB_ADCMINGAIN, 12); | ||
174 | /* Maximum frame integration time (programmed into R8) | ||
175 | allowed for auto-exposure routine */ | ||
176 | stv06xx_write_sensor(sd, PB_R54, 3); | ||
177 | /* Minimum frame integration time (programmed into R8) | ||
178 | allowed for auto-exposure routine */ | ||
179 | stv06xx_write_sensor(sd, PB_R55, 0); | ||
180 | stv06xx_write_sensor(sd, PB_UPDATEINT, 1); | ||
181 | /* R15 Expose0 (maximum that auto-exposure may use) */ | ||
182 | stv06xx_write_sensor(sd, PB_R15, 800); | ||
183 | /* R17 Expose2 (minimum that auto-exposure may use) */ | ||
184 | stv06xx_write_sensor(sd, PB_R17, 10); | ||
185 | |||
186 | stv06xx_write_sensor(sd, PB_EXPGAIN, 0); | ||
187 | |||
188 | /* 0x14 */ | ||
189 | stv06xx_write_sensor(sd, PB_VOFFSET, 0); | ||
190 | /* 0x0D */ | ||
191 | stv06xx_write_sensor(sd, PB_ADCGAINH, 11); | ||
192 | /* Set black level (important!) */ | ||
193 | stv06xx_write_sensor(sd, PB_ADCGAINL, 0); | ||
194 | |||
195 | /* ??? */ | ||
196 | stv06xx_write_bridge(sd, STV_REG00, 0x11); | ||
197 | stv06xx_write_bridge(sd, STV_REG03, 0x45); | ||
198 | stv06xx_write_bridge(sd, STV_REG04, 0x07); | ||
199 | |||
200 | /* ISO-Size (0x27b: 635... why? - HDCS uses 847) */ | ||
201 | stv06xx_write_bridge(sd, STV_ISO_SIZE_L, 847); | ||
202 | |||
203 | /* Scan/timing for the sensor */ | ||
204 | stv06xx_write_sensor(sd, PB_ROWSPEED, BIT(4)|BIT(3)|BIT(1)); | ||
205 | stv06xx_write_sensor(sd, PB_CFILLIN, 14); | ||
206 | stv06xx_write_sensor(sd, PB_VBL, 0); | ||
207 | stv06xx_write_sensor(sd, PB_FINTTIME, 0); | ||
208 | stv06xx_write_sensor(sd, PB_RINTTIME, 123); | ||
209 | |||
210 | stv06xx_write_bridge(sd, STV_REG01, 0xc2); | ||
211 | stv06xx_write_bridge(sd, STV_REG02, 0xb0); | ||
212 | return 0; | ||
213 | } | ||
214 | |||
215 | static int pb0100_dump(struct sd *sd) | ||
216 | { | ||
217 | return 0; | ||
218 | } | ||
219 | |||
220 | static int pb0100_get_gain(struct gspca_dev *gspca_dev, __s32 *val) | ||
221 | { | ||
222 | struct sd *sd = (struct sd *) gspca_dev; | ||
223 | s32 *sensor_settings = sd->sensor_priv; | ||
224 | |||
225 | *val = sensor_settings[GAIN_IDX]; | ||
226 | |||
227 | return 0; | ||
228 | } | ||
229 | |||
230 | static int pb0100_set_gain(struct gspca_dev *gspca_dev, __s32 val) | ||
231 | { | ||
232 | int err; | ||
233 | struct sd *sd = (struct sd *) gspca_dev; | ||
234 | s32 *sensor_settings = sd->sensor_priv; | ||
235 | |||
236 | if (sensor_settings[AUTOGAIN_IDX]) | ||
237 | return -EBUSY; | ||
238 | |||
239 | sensor_settings[GAIN_IDX] = val; | ||
240 | err = stv06xx_write_sensor(sd, PB_G1GAIN, val); | ||
241 | if (!err) | ||
242 | err = stv06xx_write_sensor(sd, PB_G2GAIN, val); | ||
243 | PDEBUG(D_V4L2, "Set green gain to %d, status: %d", val, err); | ||
244 | |||
245 | if (!err) | ||
246 | err = pb0100_set_red_balance(gspca_dev, | ||
247 | sensor_settings[RED_BALANCE_IDX]); | ||
248 | if (!err) | ||
249 | err = pb0100_set_blue_balance(gspca_dev, | ||
250 | sensor_settings[BLUE_BALANCE_IDX]); | ||
251 | |||
252 | return err; | ||
253 | } | ||
254 | |||
255 | static int pb0100_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val) | ||
256 | { | ||
257 | struct sd *sd = (struct sd *) gspca_dev; | ||
258 | s32 *sensor_settings = sd->sensor_priv; | ||
259 | |||
260 | *val = sensor_settings[RED_BALANCE_IDX]; | ||
261 | |||
262 | return 0; | ||
263 | } | ||
264 | |||
265 | static int pb0100_set_red_balance(struct gspca_dev *gspca_dev, __s32 val) | ||
266 | { | ||
267 | int err; | ||
268 | struct sd *sd = (struct sd *) gspca_dev; | ||
269 | s32 *sensor_settings = sd->sensor_priv; | ||
270 | |||
271 | if (sensor_settings[AUTOGAIN_IDX]) | ||
272 | return -EBUSY; | ||
273 | |||
274 | sensor_settings[RED_BALANCE_IDX] = val; | ||
275 | val += sensor_settings[GAIN_IDX]; | ||
276 | if (val < 0) | ||
277 | val = 0; | ||
278 | else if (val > 255) | ||
279 | val = 255; | ||
280 | |||
281 | err = stv06xx_write_sensor(sd, PB_RGAIN, val); | ||
282 | PDEBUG(D_V4L2, "Set red gain to %d, status: %d", val, err); | ||
283 | |||
284 | return err; | ||
285 | } | ||
286 | |||
287 | static int pb0100_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val) | ||
288 | { | ||
289 | struct sd *sd = (struct sd *) gspca_dev; | ||
290 | s32 *sensor_settings = sd->sensor_priv; | ||
291 | |||
292 | *val = sensor_settings[BLUE_BALANCE_IDX]; | ||
293 | |||
294 | return 0; | ||
295 | } | ||
296 | |||
297 | static int pb0100_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val) | ||
298 | { | ||
299 | int err; | ||
300 | struct sd *sd = (struct sd *) gspca_dev; | ||
301 | s32 *sensor_settings = sd->sensor_priv; | ||
302 | |||
303 | if (sensor_settings[AUTOGAIN_IDX]) | ||
304 | return -EBUSY; | ||
305 | |||
306 | sensor_settings[BLUE_BALANCE_IDX] = val; | ||
307 | val += sensor_settings[GAIN_IDX]; | ||
308 | if (val < 0) | ||
309 | val = 0; | ||
310 | else if (val > 255) | ||
311 | val = 255; | ||
312 | |||
313 | err = stv06xx_write_sensor(sd, PB_BGAIN, val); | ||
314 | PDEBUG(D_V4L2, "Set blue gain to %d, status: %d", val, err); | ||
315 | |||
316 | return err; | ||
317 | } | ||
318 | |||
319 | static int pb0100_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) | ||
320 | { | ||
321 | struct sd *sd = (struct sd *) gspca_dev; | ||
322 | s32 *sensor_settings = sd->sensor_priv; | ||
323 | |||
324 | *val = sensor_settings[EXPOSURE_IDX]; | ||
325 | |||
326 | return 0; | ||
327 | } | ||
328 | |||
329 | static int pb0100_set_exposure(struct gspca_dev *gspca_dev, __s32 val) | ||
330 | { | ||
331 | int err; | ||
332 | struct sd *sd = (struct sd *) gspca_dev; | ||
333 | s32 *sensor_settings = sd->sensor_priv; | ||
334 | |||
335 | if (sensor_settings[AUTOGAIN_IDX]) | ||
336 | return -EBUSY; | ||
337 | |||
338 | sensor_settings[EXPOSURE_IDX] = val; | ||
339 | err = stv06xx_write_sensor(sd, PB_RINTTIME, val); | ||
340 | PDEBUG(D_V4L2, "Set exposure to %d, status: %d", val, err); | ||
341 | |||
342 | return err; | ||
343 | } | ||
344 | |||
345 | static int pb0100_get_autogain(struct gspca_dev *gspca_dev, __s32 *val) | ||
346 | { | ||
347 | struct sd *sd = (struct sd *) gspca_dev; | ||
348 | s32 *sensor_settings = sd->sensor_priv; | ||
349 | |||
350 | *val = sensor_settings[AUTOGAIN_IDX]; | ||
351 | |||
352 | return 0; | ||
353 | } | ||
354 | |||
355 | static int pb0100_set_autogain(struct gspca_dev *gspca_dev, __s32 val) | ||
356 | { | ||
357 | int err; | ||
358 | struct sd *sd = (struct sd *) gspca_dev; | ||
359 | s32 *sensor_settings = sd->sensor_priv; | ||
360 | |||
361 | sensor_settings[AUTOGAIN_IDX] = val; | ||
362 | if (sensor_settings[AUTOGAIN_IDX]) { | ||
363 | if (sensor_settings[NATURAL_IDX]) | ||
364 | val = BIT(6)|BIT(4)|BIT(0); | ||
365 | else | ||
366 | val = BIT(4)|BIT(0); | ||
367 | } else | ||
368 | val = 0; | ||
369 | |||
370 | err = stv06xx_write_sensor(sd, PB_EXPGAIN, val); | ||
371 | PDEBUG(D_V4L2, "Set autogain to %d (natural: %d), status: %d", | ||
372 | sensor_settings[AUTOGAIN_IDX], sensor_settings[NATURAL_IDX], | ||
373 | err); | ||
374 | |||
375 | return err; | ||
376 | } | ||
377 | |||
378 | static int pb0100_get_autogain_target(struct gspca_dev *gspca_dev, __s32 *val) | ||
379 | { | ||
380 | struct sd *sd = (struct sd *) gspca_dev; | ||
381 | s32 *sensor_settings = sd->sensor_priv; | ||
382 | |||
383 | *val = sensor_settings[AUTOGAIN_TARGET_IDX]; | ||
384 | |||
385 | return 0; | ||
386 | } | ||
387 | |||
388 | static int pb0100_set_autogain_target(struct gspca_dev *gspca_dev, __s32 val) | ||
389 | { | ||
390 | int err, totalpixels, brightpixels, darkpixels; | ||
391 | struct sd *sd = (struct sd *) gspca_dev; | ||
392 | s32 *sensor_settings = sd->sensor_priv; | ||
393 | |||
394 | sensor_settings[AUTOGAIN_TARGET_IDX] = val; | ||
395 | |||
396 | /* Number of pixels counted by the sensor when subsampling the pixels. | ||
397 | * Slightly larger than the real value to avoid oscillation */ | ||
398 | totalpixels = gspca_dev->width * gspca_dev->height; | ||
399 | totalpixels = totalpixels/(8*8) + totalpixels/(64*64); | ||
400 | |||
401 | brightpixels = (totalpixels * val) >> 8; | ||
402 | darkpixels = totalpixels - brightpixels; | ||
403 | err = stv06xx_write_sensor(sd, PB_R21, brightpixels); | ||
404 | if (!err) | ||
405 | err = stv06xx_write_sensor(sd, PB_R22, darkpixels); | ||
406 | |||
407 | PDEBUG(D_V4L2, "Set autogain target to %d, status: %d", val, err); | ||
408 | |||
409 | return err; | ||
410 | } | ||
411 | |||
412 | static int pb0100_get_natural(struct gspca_dev *gspca_dev, __s32 *val) | ||
413 | { | ||
414 | struct sd *sd = (struct sd *) gspca_dev; | ||
415 | s32 *sensor_settings = sd->sensor_priv; | ||
416 | |||
417 | *val = sensor_settings[NATURAL_IDX]; | ||
418 | |||
419 | return 0; | ||
420 | } | ||
421 | |||
422 | static int pb0100_set_natural(struct gspca_dev *gspca_dev, __s32 val) | ||
423 | { | ||
424 | struct sd *sd = (struct sd *) gspca_dev; | ||
425 | s32 *sensor_settings = sd->sensor_priv; | ||
426 | |||
427 | sensor_settings[NATURAL_IDX] = val; | ||
428 | |||
429 | return pb0100_set_autogain(gspca_dev, sensor_settings[AUTOGAIN_IDX]); | ||
430 | } | ||
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h new file mode 100644 index 000000000000..5ea21a1154c4 --- /dev/null +++ b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h | |||
@@ -0,0 +1,275 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher | ||
3 | * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland | ||
4 | * Copyright (c) 2002, 2003 Tuukka Toivonen | ||
5 | * Copyright (c) 2008 Erik Andrén | ||
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | * | ||
21 | * P/N 861037: Sensor HDCS1000 ASIC STV0600 | ||
22 | * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 | ||
23 | * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express | ||
24 | * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam | ||
25 | * P/N 861075-0040: Sensor HDCS1000 ASIC | ||
26 | * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB | ||
27 | * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web | ||
28 | */ | ||
29 | |||
30 | #ifndef STV06XX_PB0100_H_ | ||
31 | #define STV06XX_PB0100_H_ | ||
32 | |||
33 | #include "stv06xx_sensor.h" | ||
34 | |||
35 | /* mode priv field flags */ | ||
36 | #define PB0100_CROP_TO_VGA 0x01 | ||
37 | #define PB0100_SUBSAMPLE 0x02 | ||
38 | |||
39 | /* I2C Registers */ | ||
40 | #define PB_IDENT 0x00 /* Chip Version */ | ||
41 | #define PB_RSTART 0x01 /* Row Window Start */ | ||
42 | #define PB_CSTART 0x02 /* Column Window Start */ | ||
43 | #define PB_RWSIZE 0x03 /* Row Window Size */ | ||
44 | #define PB_CWSIZE 0x04 /* Column Window Size */ | ||
45 | #define PB_CFILLIN 0x05 /* Column Fill-In */ | ||
46 | #define PB_VBL 0x06 /* Vertical Blank Count */ | ||
47 | #define PB_CONTROL 0x07 /* Control Mode */ | ||
48 | #define PB_FINTTIME 0x08 /* Integration Time/Frame Unit Count */ | ||
49 | #define PB_RINTTIME 0x09 /* Integration Time/Row Unit Count */ | ||
50 | #define PB_ROWSPEED 0x0a /* Row Speed Control */ | ||
51 | #define PB_ABORTFRAME 0x0b /* Abort Frame */ | ||
52 | #define PB_R12 0x0c /* Reserved */ | ||
53 | #define PB_RESET 0x0d /* Reset */ | ||
54 | #define PB_EXPGAIN 0x0e /* Exposure Gain Command */ | ||
55 | #define PB_R15 0x0f /* Expose0 */ | ||
56 | #define PB_R16 0x10 /* Expose1 */ | ||
57 | #define PB_R17 0x11 /* Expose2 */ | ||
58 | #define PB_R18 0x12 /* Low0_DAC */ | ||
59 | #define PB_R19 0x13 /* Low1_DAC */ | ||
60 | #define PB_R20 0x14 /* Low2_DAC */ | ||
61 | #define PB_R21 0x15 /* Threshold11 */ | ||
62 | #define PB_R22 0x16 /* Threshold0x */ | ||
63 | #define PB_UPDATEINT 0x17 /* Update Interval */ | ||
64 | #define PB_R24 0x18 /* High_DAC */ | ||
65 | #define PB_R25 0x19 /* Trans0H */ | ||
66 | #define PB_R26 0x1a /* Trans1L */ | ||
67 | #define PB_R27 0x1b /* Trans1H */ | ||
68 | #define PB_R28 0x1c /* Trans2L */ | ||
69 | #define PB_R29 0x1d /* Reserved */ | ||
70 | #define PB_R30 0x1e /* Reserved */ | ||
71 | #define PB_R31 0x1f /* Wait to Read */ | ||
72 | #define PB_PREADCTRL 0x20 /* Pixel Read Control Mode */ | ||
73 | #define PB_R33 0x21 /* IREF_VLN */ | ||
74 | #define PB_R34 0x22 /* IREF_VLP */ | ||
75 | #define PB_R35 0x23 /* IREF_VLN_INTEG */ | ||
76 | #define PB_R36 0x24 /* IREF_MASTER */ | ||
77 | #define PB_R37 0x25 /* IDACP */ | ||
78 | #define PB_R38 0x26 /* IDACN */ | ||
79 | #define PB_R39 0x27 /* DAC_Control_Reg */ | ||
80 | #define PB_R40 0x28 /* VCL */ | ||
81 | #define PB_R41 0x29 /* IREF_VLN_ADCIN */ | ||
82 | #define PB_R42 0x2a /* Reserved */ | ||
83 | #define PB_G1GAIN 0x2b /* Green 1 Gain */ | ||
84 | #define PB_BGAIN 0x2c /* Blue Gain */ | ||
85 | #define PB_RGAIN 0x2d /* Red Gain */ | ||
86 | #define PB_G2GAIN 0x2e /* Green 2 Gain */ | ||
87 | #define PB_R47 0x2f /* Dark Row Address */ | ||
88 | #define PB_R48 0x30 /* Dark Row Options */ | ||
89 | #define PB_R49 0x31 /* Reserved */ | ||
90 | #define PB_R50 0x32 /* Image Test Data */ | ||
91 | #define PB_ADCMAXGAIN 0x33 /* Maximum Gain */ | ||
92 | #define PB_ADCMINGAIN 0x34 /* Minimum Gain */ | ||
93 | #define PB_ADCGLOBALGAIN 0x35 /* Global Gain */ | ||
94 | #define PB_R54 0x36 /* Maximum Frame */ | ||
95 | #define PB_R55 0x37 /* Minimum Frame */ | ||
96 | #define PB_R56 0x38 /* Reserved */ | ||
97 | #define PB_VOFFSET 0x39 /* VOFFSET */ | ||
98 | #define PB_R58 0x3a /* Snap-Shot Sequence Trigger */ | ||
99 | #define PB_ADCGAINH 0x3b /* VREF_HI */ | ||
100 | #define PB_ADCGAINL 0x3c /* VREF_LO */ | ||
101 | #define PB_R61 0x3d /* Reserved */ | ||
102 | #define PB_R62 0x3e /* Reserved */ | ||
103 | #define PB_R63 0x3f /* Reserved */ | ||
104 | #define PB_R64 0x40 /* Red/Blue Gain */ | ||
105 | #define PB_R65 0x41 /* Green 2/Green 1 Gain */ | ||
106 | #define PB_R66 0x42 /* VREF_HI/LO */ | ||
107 | #define PB_R67 0x43 /* Integration Time/Row Unit Count */ | ||
108 | #define PB_R240 0xf0 /* ADC Test */ | ||
109 | #define PB_R241 0xf1 /* Chip Enable */ | ||
110 | #define PB_R242 0xf2 /* Reserved */ | ||
111 | |||
112 | static int pb0100_probe(struct sd *sd); | ||
113 | static int pb0100_start(struct sd *sd); | ||
114 | static int pb0100_init(struct sd *sd); | ||
115 | static int pb0100_stop(struct sd *sd); | ||
116 | static int pb0100_dump(struct sd *sd); | ||
117 | |||
118 | /* V4L2 controls supported by the driver */ | ||
119 | static int pb0100_get_gain(struct gspca_dev *gspca_dev, __s32 *val); | ||
120 | static int pb0100_set_gain(struct gspca_dev *gspca_dev, __s32 val); | ||
121 | static int pb0100_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val); | ||
122 | static int pb0100_set_red_balance(struct gspca_dev *gspca_dev, __s32 val); | ||
123 | static int pb0100_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val); | ||
124 | static int pb0100_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val); | ||
125 | static int pb0100_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); | ||
126 | static int pb0100_set_exposure(struct gspca_dev *gspca_dev, __s32 val); | ||
127 | static int pb0100_get_autogain(struct gspca_dev *gspca_dev, __s32 *val); | ||
128 | static int pb0100_set_autogain(struct gspca_dev *gspca_dev, __s32 val); | ||
129 | static int pb0100_get_autogain_target(struct gspca_dev *gspca_dev, __s32 *val); | ||
130 | static int pb0100_set_autogain_target(struct gspca_dev *gspca_dev, __s32 val); | ||
131 | static int pb0100_get_natural(struct gspca_dev *gspca_dev, __s32 *val); | ||
132 | static int pb0100_set_natural(struct gspca_dev *gspca_dev, __s32 val); | ||
133 | |||
134 | const struct stv06xx_sensor stv06xx_sensor_pb0100 = { | ||
135 | .name = "PB-0100", | ||
136 | .i2c_flush = 1, | ||
137 | .i2c_addr = 0xba, | ||
138 | .i2c_len = 2, | ||
139 | |||
140 | .nctrls = 7, | ||
141 | .ctrls = { | ||
142 | #define GAIN_IDX 0 | ||
143 | { | ||
144 | { | ||
145 | .id = V4L2_CID_GAIN, | ||
146 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
147 | .name = "Gain", | ||
148 | .minimum = 0, | ||
149 | .maximum = 255, | ||
150 | .step = 1, | ||
151 | .default_value = 128 | ||
152 | }, | ||
153 | .set = pb0100_set_gain, | ||
154 | .get = pb0100_get_gain | ||
155 | }, | ||
156 | #define RED_BALANCE_IDX 1 | ||
157 | { | ||
158 | { | ||
159 | .id = V4L2_CID_RED_BALANCE, | ||
160 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
161 | .name = "Red Balance", | ||
162 | .minimum = -255, | ||
163 | .maximum = 255, | ||
164 | .step = 1, | ||
165 | .default_value = 0 | ||
166 | }, | ||
167 | .set = pb0100_set_red_balance, | ||
168 | .get = pb0100_get_red_balance | ||
169 | }, | ||
170 | #define BLUE_BALANCE_IDX 2 | ||
171 | { | ||
172 | { | ||
173 | .id = V4L2_CID_BLUE_BALANCE, | ||
174 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
175 | .name = "Blue Balance", | ||
176 | .minimum = -255, | ||
177 | .maximum = 255, | ||
178 | .step = 1, | ||
179 | .default_value = 0 | ||
180 | }, | ||
181 | .set = pb0100_set_blue_balance, | ||
182 | .get = pb0100_get_blue_balance | ||
183 | }, | ||
184 | #define EXPOSURE_IDX 3 | ||
185 | { | ||
186 | { | ||
187 | .id = V4L2_CID_EXPOSURE, | ||
188 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
189 | .name = "Exposure", | ||
190 | .minimum = 0, | ||
191 | .maximum = 511, | ||
192 | .step = 1, | ||
193 | .default_value = 12 | ||
194 | }, | ||
195 | .set = pb0100_set_exposure, | ||
196 | .get = pb0100_get_exposure | ||
197 | }, | ||
198 | #define AUTOGAIN_IDX 4 | ||
199 | { | ||
200 | { | ||
201 | .id = V4L2_CID_AUTOGAIN, | ||
202 | .type = V4L2_CTRL_TYPE_BOOLEAN, | ||
203 | .name = "Automatic Gain and Exposure", | ||
204 | .minimum = 0, | ||
205 | .maximum = 1, | ||
206 | .step = 1, | ||
207 | .default_value = 1 | ||
208 | }, | ||
209 | .set = pb0100_set_autogain, | ||
210 | .get = pb0100_get_autogain | ||
211 | }, | ||
212 | #define AUTOGAIN_TARGET_IDX 5 | ||
213 | { | ||
214 | { | ||
215 | .id = V4L2_CTRL_CLASS_USER + 0x1000, | ||
216 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
217 | .name = "Automatic Gain Target", | ||
218 | .minimum = 0, | ||
219 | .maximum = 255, | ||
220 | .step = 1, | ||
221 | .default_value = 128 | ||
222 | }, | ||
223 | .set = pb0100_set_autogain_target, | ||
224 | .get = pb0100_get_autogain_target | ||
225 | }, | ||
226 | #define NATURAL_IDX 6 | ||
227 | { | ||
228 | { | ||
229 | .id = V4L2_CTRL_CLASS_USER + 0x1001, | ||
230 | .type = V4L2_CTRL_TYPE_BOOLEAN, | ||
231 | .name = "Natural Light Source", | ||
232 | .minimum = 0, | ||
233 | .maximum = 1, | ||
234 | .step = 1, | ||
235 | .default_value = 1 | ||
236 | }, | ||
237 | .set = pb0100_set_natural, | ||
238 | .get = pb0100_get_natural | ||
239 | }, | ||
240 | }, | ||
241 | |||
242 | .init = pb0100_init, | ||
243 | .probe = pb0100_probe, | ||
244 | .start = pb0100_start, | ||
245 | .stop = pb0100_stop, | ||
246 | .dump = pb0100_dump, | ||
247 | |||
248 | .nmodes = 2, | ||
249 | .modes = { | ||
250 | /* low res / subsample modes disabled as they are only half res horizontal, | ||
251 | halving the vertical resolution does not seem to work */ | ||
252 | { | ||
253 | 320, | ||
254 | 240, | ||
255 | V4L2_PIX_FMT_SGRBG8, | ||
256 | V4L2_FIELD_NONE, | ||
257 | .sizeimage = 320 * 240, | ||
258 | .bytesperline = 320, | ||
259 | .colorspace = V4L2_COLORSPACE_SRGB, | ||
260 | .priv = PB0100_CROP_TO_VGA | ||
261 | }, | ||
262 | { | ||
263 | 352, | ||
264 | 288, | ||
265 | V4L2_PIX_FMT_SGRBG8, | ||
266 | V4L2_FIELD_NONE, | ||
267 | .sizeimage = 352 * 288, | ||
268 | .bytesperline = 352, | ||
269 | .colorspace = V4L2_COLORSPACE_SRGB, | ||
270 | .priv = 0 | ||
271 | }, | ||
272 | } | ||
273 | }; | ||
274 | |||
275 | #endif | ||
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h b/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h new file mode 100644 index 000000000000..c726dacefa1f --- /dev/null +++ b/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h | |||
@@ -0,0 +1,92 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher | ||
3 | * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland | ||
4 | * Copyright (c) 2002, 2003 Tuukka Toivonen | ||
5 | * Copyright (c) 2008 Erik Andrén | ||
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | * | ||
21 | * P/N 861037: Sensor HDCS1000 ASIC STV0600 | ||
22 | * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 | ||
23 | * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express | ||
24 | * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam | ||
25 | * P/N 861075-0040: Sensor HDCS1000 ASIC | ||
26 | * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB | ||
27 | * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web | ||
28 | */ | ||
29 | |||
30 | #ifndef STV06XX_SENSOR_H_ | ||
31 | #define STV06XX_SENSOR_H_ | ||
32 | |||
33 | #include "stv06xx.h" | ||
34 | |||
35 | #define IS_850(sd) ((sd)->gspca_dev.dev->descriptor.idProduct == 0x850) | ||
36 | #define IS_870(sd) ((sd)->gspca_dev.dev->descriptor.idProduct == 0x870) | ||
37 | #define IS_1020(sd) ((sd)->sensor == &stv06xx_sensor_hdcs1020) | ||
38 | |||
39 | extern const struct stv06xx_sensor stv06xx_sensor_vv6410; | ||
40 | extern const struct stv06xx_sensor stv06xx_sensor_hdcs1x00; | ||
41 | extern const struct stv06xx_sensor stv06xx_sensor_hdcs1020; | ||
42 | extern const struct stv06xx_sensor stv06xx_sensor_pb0100; | ||
43 | |||
44 | #define STV06XX_MAX_CTRLS (V4L2_CID_LASTP1 - V4L2_CID_BASE + 10) | ||
45 | |||
46 | struct stv06xx_sensor { | ||
47 | /* Defines the name of a sensor */ | ||
48 | char name[32]; | ||
49 | |||
50 | /* Sensor i2c address */ | ||
51 | u8 i2c_addr; | ||
52 | |||
53 | /* Flush value*/ | ||
54 | u8 i2c_flush; | ||
55 | |||
56 | /* length of an i2c word */ | ||
57 | u8 i2c_len; | ||
58 | |||
59 | /* Probes if the sensor is connected */ | ||
60 | int (*probe)(struct sd *sd); | ||
61 | |||
62 | /* Performs a initialization sequence */ | ||
63 | int (*init)(struct sd *sd); | ||
64 | |||
65 | /* Executed at device disconnect */ | ||
66 | void (*disconnect)(struct sd *sd); | ||
67 | |||
68 | /* Reads a sensor register */ | ||
69 | int (*read_sensor)(struct sd *sd, const u8 address, | ||
70 | u8 *i2c_data, const u8 len); | ||
71 | |||
72 | /* Writes to a sensor register */ | ||
73 | int (*write_sensor)(struct sd *sd, const u8 address, | ||
74 | u8 *i2c_data, const u8 len); | ||
75 | |||
76 | /* Instructs the sensor to start streaming */ | ||
77 | int (*start)(struct sd *sd); | ||
78 | |||
79 | /* Instructs the sensor to stop streaming */ | ||
80 | int (*stop)(struct sd *sd); | ||
81 | |||
82 | /* Instructs the sensor to dump all its contents */ | ||
83 | int (*dump)(struct sd *sd); | ||
84 | |||
85 | int nctrls; | ||
86 | struct ctrl ctrls[STV06XX_MAX_CTRLS]; | ||
87 | |||
88 | char nmodes; | ||
89 | struct v4l2_pix_format modes[]; | ||
90 | }; | ||
91 | |||
92 | #endif | ||
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c new file mode 100644 index 000000000000..1ca91f2a6dee --- /dev/null +++ b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c | |||
@@ -0,0 +1,251 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher | ||
3 | * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland | ||
4 | * Copyright (c) 2002, 2003 Tuukka Toivonen | ||
5 | * Copyright (c) 2008 Erik Andrén | ||
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | * | ||
21 | * P/N 861037: Sensor HDCS1000 ASIC STV0600 | ||
22 | * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 | ||
23 | * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express | ||
24 | * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam | ||
25 | * P/N 861075-0040: Sensor HDCS1000 ASIC | ||
26 | * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB | ||
27 | * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web | ||
28 | */ | ||
29 | |||
30 | #include "stv06xx_vv6410.h" | ||
31 | |||
32 | static int vv6410_probe(struct sd *sd) | ||
33 | { | ||
34 | u16 data; | ||
35 | int err; | ||
36 | |||
37 | err = stv06xx_read_sensor(sd, VV6410_DEVICEH, &data); | ||
38 | |||
39 | if (err < 0) | ||
40 | return -ENODEV; | ||
41 | |||
42 | if (data == 0x19) { | ||
43 | info("vv6410 sensor detected"); | ||
44 | |||
45 | sd->gspca_dev.cam.cam_mode = stv06xx_sensor_vv6410.modes; | ||
46 | sd->gspca_dev.cam.nmodes = stv06xx_sensor_vv6410.nmodes; | ||
47 | sd->desc.ctrls = stv06xx_sensor_vv6410.ctrls; | ||
48 | sd->desc.nctrls = stv06xx_sensor_vv6410.nctrls; | ||
49 | return 0; | ||
50 | } | ||
51 | |||
52 | return -ENODEV; | ||
53 | } | ||
54 | |||
55 | static int vv6410_init(struct sd *sd) | ||
56 | { | ||
57 | int err = 0, i; | ||
58 | |||
59 | for (i = 0; i < ARRAY_SIZE(stv_bridge_init); i++) { | ||
60 | /* if NULL then len contains single value */ | ||
61 | if (stv_bridge_init[i].data == NULL) { | ||
62 | err = stv06xx_write_bridge(sd, | ||
63 | stv_bridge_init[i].start, | ||
64 | stv_bridge_init[i].len); | ||
65 | } else { | ||
66 | int j; | ||
67 | for (j = 0; j < stv_bridge_init[i].len; j++) | ||
68 | err = stv06xx_write_bridge(sd, | ||
69 | stv_bridge_init[i].start + j, | ||
70 | stv_bridge_init[i].data[j]); | ||
71 | } | ||
72 | } | ||
73 | |||
74 | if (err < 0) | ||
75 | return err; | ||
76 | |||
77 | err = stv06xx_write_sensor_bytes(sd, (u8 *) vv6410_sensor_init, | ||
78 | ARRAY_SIZE(vv6410_sensor_init)); | ||
79 | |||
80 | return (err < 0) ? err : 0; | ||
81 | } | ||
82 | |||
83 | static int vv6410_start(struct sd *sd) | ||
84 | { | ||
85 | int err; | ||
86 | struct cam *cam = &sd->gspca_dev.cam; | ||
87 | u32 priv = cam->cam_mode[sd->gspca_dev.curr_mode].priv; | ||
88 | |||
89 | if (priv & VV6410_CROP_TO_QVGA) { | ||
90 | PDEBUG(D_CONF, "Cropping to QVGA"); | ||
91 | stv06xx_write_sensor(sd, VV6410_XENDH, 320 - 1); | ||
92 | stv06xx_write_sensor(sd, VV6410_YENDH, 240 - 1); | ||
93 | } else { | ||
94 | stv06xx_write_sensor(sd, VV6410_XENDH, 360 - 1); | ||
95 | stv06xx_write_sensor(sd, VV6410_YENDH, 294 - 1); | ||
96 | } | ||
97 | |||
98 | if (priv & VV6410_SUBSAMPLE) { | ||
99 | PDEBUG(D_CONF, "Enabling subsampling"); | ||
100 | stv06xx_write_bridge(sd, STV_Y_CTRL, 0x02); | ||
101 | stv06xx_write_bridge(sd, STV_X_CTRL, 0x06); | ||
102 | |||
103 | stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x10); | ||
104 | } else { | ||
105 | stv06xx_write_bridge(sd, STV_Y_CTRL, 0x01); | ||
106 | stv06xx_write_bridge(sd, STV_X_CTRL, 0x0a); | ||
107 | |||
108 | stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x20); | ||
109 | } | ||
110 | |||
111 | /* Turn on LED */ | ||
112 | err = stv06xx_write_bridge(sd, STV_LED_CTRL, LED_ON); | ||
113 | if (err < 0) | ||
114 | return err; | ||
115 | |||
116 | err = stv06xx_write_sensor(sd, VV6410_SETUP0, 0); | ||
117 | if (err < 0) | ||
118 | return err; | ||
119 | |||
120 | PDEBUG(D_STREAM, "Starting stream"); | ||
121 | |||
122 | return 0; | ||
123 | } | ||
124 | |||
125 | static int vv6410_stop(struct sd *sd) | ||
126 | { | ||
127 | int err; | ||
128 | |||
129 | /* Turn off LED */ | ||
130 | err = stv06xx_write_bridge(sd, STV_LED_CTRL, LED_OFF); | ||
131 | if (err < 0) | ||
132 | return err; | ||
133 | |||
134 | err = stv06xx_write_sensor(sd, VV6410_SETUP0, VV6410_LOW_POWER_MODE); | ||
135 | if (err < 0) | ||
136 | return err; | ||
137 | |||
138 | PDEBUG(D_STREAM, "Halting stream"); | ||
139 | |||
140 | return (err < 0) ? err : 0; | ||
141 | } | ||
142 | |||
143 | static int vv6410_dump(struct sd *sd) | ||
144 | { | ||
145 | u8 i; | ||
146 | int err = 0; | ||
147 | |||
148 | info("Dumping all vv6410 sensor registers"); | ||
149 | for (i = 0; i < 0xff && !err; i++) { | ||
150 | u16 data; | ||
151 | err = stv06xx_read_sensor(sd, i, &data); | ||
152 | info("Register 0x%x contained 0x%x", i, data); | ||
153 | } | ||
154 | return (err < 0) ? err : 0; | ||
155 | } | ||
156 | |||
157 | static int vv6410_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) | ||
158 | { | ||
159 | int err; | ||
160 | u16 i2c_data; | ||
161 | struct sd *sd = (struct sd *) gspca_dev; | ||
162 | |||
163 | err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data); | ||
164 | |||
165 | *val = (i2c_data & VV6410_HFLIP) ? 1 : 0; | ||
166 | |||
167 | PDEBUG(D_V4L2, "Read horizontal flip %d", *val); | ||
168 | |||
169 | return (err < 0) ? err : 0; | ||
170 | } | ||
171 | |||
172 | static int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val) | ||
173 | { | ||
174 | int err; | ||
175 | u16 i2c_data; | ||
176 | struct sd *sd = (struct sd *) gspca_dev; | ||
177 | err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data); | ||
178 | if (err < 0) | ||
179 | return err; | ||
180 | |||
181 | if (val) | ||
182 | i2c_data |= VV6410_HFLIP; | ||
183 | else | ||
184 | i2c_data &= ~VV6410_HFLIP; | ||
185 | |||
186 | PDEBUG(D_V4L2, "Set horizontal flip to %d", val); | ||
187 | err = stv06xx_write_sensor(sd, VV6410_DATAFORMAT, i2c_data); | ||
188 | |||
189 | return (err < 0) ? err : 0; | ||
190 | } | ||
191 | |||
192 | static int vv6410_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) | ||
193 | { | ||
194 | int err; | ||
195 | u16 i2c_data; | ||
196 | struct sd *sd = (struct sd *) gspca_dev; | ||
197 | |||
198 | err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data); | ||
199 | |||
200 | *val = (i2c_data & VV6410_VFLIP) ? 1 : 0; | ||
201 | |||
202 | PDEBUG(D_V4L2, "Read vertical flip %d", *val); | ||
203 | |||
204 | return (err < 0) ? err : 0; | ||
205 | } | ||
206 | |||
207 | static int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val) | ||
208 | { | ||
209 | int err; | ||
210 | u16 i2c_data; | ||
211 | struct sd *sd = (struct sd *) gspca_dev; | ||
212 | err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data); | ||
213 | if (err < 0) | ||
214 | return err; | ||
215 | |||
216 | if (val) | ||
217 | i2c_data |= VV6410_VFLIP; | ||
218 | else | ||
219 | i2c_data &= ~VV6410_VFLIP; | ||
220 | |||
221 | PDEBUG(D_V4L2, "Set vertical flip to %d", val); | ||
222 | err = stv06xx_write_sensor(sd, VV6410_DATAFORMAT, i2c_data); | ||
223 | |||
224 | return (err < 0) ? err : 0; | ||
225 | } | ||
226 | |||
227 | static int vv6410_get_analog_gain(struct gspca_dev *gspca_dev, __s32 *val) | ||
228 | { | ||
229 | int err; | ||
230 | u16 i2c_data; | ||
231 | struct sd *sd = (struct sd *) gspca_dev; | ||
232 | |||
233 | err = stv06xx_read_sensor(sd, VV6410_ANALOGGAIN, &i2c_data); | ||
234 | |||
235 | *val = i2c_data & 0xf; | ||
236 | |||
237 | PDEBUG(D_V4L2, "Read analog gain %d", *val); | ||
238 | |||
239 | return (err < 0) ? err : 0; | ||
240 | } | ||
241 | |||
242 | static int vv6410_set_analog_gain(struct gspca_dev *gspca_dev, __s32 val) | ||
243 | { | ||
244 | int err; | ||
245 | struct sd *sd = (struct sd *) gspca_dev; | ||
246 | |||
247 | PDEBUG(D_V4L2, "Set analog gain to %d", val); | ||
248 | err = stv06xx_write_sensor(sd, VV6410_ANALOGGAIN, 0xf0 | (val & 0xf)); | ||
249 | |||
250 | return (err < 0) ? err : 0; | ||
251 | } | ||
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h new file mode 100644 index 000000000000..3ff8c4ea3362 --- /dev/null +++ b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h | |||
@@ -0,0 +1,315 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher | ||
3 | * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland | ||
4 | * Copyright (c) 2002, 2003 Tuukka Toivonen | ||
5 | * Copyright (c) 2008 Erik Andrén | ||
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | * | ||
21 | * P/N 861037: Sensor HDCS1000 ASIC STV0600 | ||
22 | * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 | ||
23 | * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express | ||
24 | * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam | ||
25 | * P/N 861075-0040: Sensor HDCS1000 ASIC | ||
26 | * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB | ||
27 | * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web | ||
28 | */ | ||
29 | |||
30 | #ifndef STV06XX_VV6410_H_ | ||
31 | #define STV06XX_VV6410_H_ | ||
32 | |||
33 | #include "stv06xx_sensor.h" | ||
34 | |||
35 | #define VV6410_COLS 416 | ||
36 | #define VV6410_ROWS 320 | ||
37 | |||
38 | /* Status registers */ | ||
39 | /* Chip identification number including revision indicator */ | ||
40 | #define VV6410_DEVICEH 0x00 | ||
41 | #define VV6410_DEVICEL 0x01 | ||
42 | |||
43 | /* User can determine whether timed I2C data | ||
44 | has been consumed by interrogating flag states */ | ||
45 | #define VV6410_STATUS0 0x02 | ||
46 | |||
47 | /* Current line counter value */ | ||
48 | #define VV6410_LINECOUNTH 0x03 | ||
49 | #define VV6410_LINECOUNTL 0x04 | ||
50 | |||
51 | /* End x coordinate of image size */ | ||
52 | #define VV6410_XENDH 0x05 | ||
53 | #define VV6410_XENDL 0x06 | ||
54 | |||
55 | /* End y coordinate of image size */ | ||
56 | #define VV6410_YENDH 0x07 | ||
57 | #define VV6410_YENDL 0x08 | ||
58 | |||
59 | /* This is the average pixel value returned from the | ||
60 | dark line offset cancellation algorithm */ | ||
61 | #define VV6410_DARKAVGH 0x09 | ||
62 | #define VV6410_DARKAVGL 0x0a | ||
63 | |||
64 | /* This is the average pixel value returned from the | ||
65 | black line offset cancellation algorithm */ | ||
66 | #define VV6410_BLACKAVGH 0x0b | ||
67 | #define VV6410_BLACKAVGL 0x0c | ||
68 | |||
69 | /* Flags to indicate whether the x or y image coordinates have been clipped */ | ||
70 | #define VV6410_STATUS1 0x0d | ||
71 | |||
72 | /* Setup registers */ | ||
73 | |||
74 | /* Low-power/sleep modes & video timing */ | ||
75 | #define VV6410_SETUP0 0x10 | ||
76 | |||
77 | /* Various parameters */ | ||
78 | #define VV6410_SETUP1 0x11 | ||
79 | |||
80 | /* Contains pixel counter reset value used by external sync */ | ||
81 | #define VV6410_SYNCVALUE 0x12 | ||
82 | |||
83 | /* Frame grabbing modes (FST, LST and QCK) */ | ||
84 | #define VV6410_FGMODES 0x14 | ||
85 | |||
86 | /* FST and QCK mapping modes. */ | ||
87 | #define VV6410_PINMAPPING 0x15 | ||
88 | |||
89 | /* Data resolution */ | ||
90 | #define VV6410_DATAFORMAT 0x16 | ||
91 | |||
92 | /* Output coding formats */ | ||
93 | #define VV6410_OPFORMAT 0x17 | ||
94 | |||
95 | /* Various mode select bits */ | ||
96 | #define VV6410_MODESELECT 0x18 | ||
97 | |||
98 | /* Exposure registers */ | ||
99 | /* Fine exposure. */ | ||
100 | #define VV6410_FINEH 0x20 | ||
101 | #define VV6410_FINEL 0x21 | ||
102 | |||
103 | /* Coarse exposure */ | ||
104 | #define VV6410_COARSEH 0x22 | ||
105 | #define VV6410_COARSEL 0x23 | ||
106 | |||
107 | /* Analog gain setting */ | ||
108 | #define VV6410_ANALOGGAIN 0x24 | ||
109 | |||
110 | /* Clock division */ | ||
111 | #define VV6410_CLKDIV 0x25 | ||
112 | |||
113 | /* Dark line offset cancellation value */ | ||
114 | #define VV6410_DARKOFFSETH 0x2c | ||
115 | #define VV6410_DARKOFFSETL 0x2d | ||
116 | |||
117 | /* Dark line offset cancellation enable */ | ||
118 | #define VV6410_DARKOFFSETSETUP 0x2e | ||
119 | |||
120 | /* Video timing registers */ | ||
121 | /* Line Length (Pixel Clocks) */ | ||
122 | #define VV6410_LINELENGTHH 0x52 | ||
123 | #define VV6410_LINELENGTHL 0x53 | ||
124 | |||
125 | /* X-co-ordinate of top left corner of region of interest (x-offset) */ | ||
126 | #define VV6410_XOFFSETH 0x57 | ||
127 | #define VV6410_XOFFSETL 0x58 | ||
128 | |||
129 | /* Y-coordinate of top left corner of region of interest (y-offset) */ | ||
130 | #define VV6410_YOFFSETH 0x59 | ||
131 | #define VV6410_YOFFSETL 0x5a | ||
132 | |||
133 | /* Field length (Lines) */ | ||
134 | #define VV6410_FIELDLENGTHH 0x61 | ||
135 | #define VV6410_FIELDLENGTHL 0x62 | ||
136 | |||
137 | /* System registers */ | ||
138 | /* Black offset cancellation default value */ | ||
139 | #define VV6410_BLACKOFFSETH 0x70 | ||
140 | #define VV6410_BLACKOFFSETL 0x71 | ||
141 | |||
142 | /* Black offset cancellation setup */ | ||
143 | #define VV6410_BLACKOFFSETSETUP 0x72 | ||
144 | |||
145 | /* Analog Control Register 0 */ | ||
146 | #define VV6410_CR0 0x75 | ||
147 | |||
148 | /* Analog Control Register 1 */ | ||
149 | #define VV6410_CR1 0x76 | ||
150 | |||
151 | /* ADC Setup Register */ | ||
152 | #define VV6410_AS0 0x77 | ||
153 | |||
154 | /* Analog Test Register */ | ||
155 | #define VV6410_AT0 0x78 | ||
156 | |||
157 | /* Audio Amplifier Setup Register */ | ||
158 | #define VV6410_AT1 0x79 | ||
159 | |||
160 | #define VV6410_HFLIP (1 << 3) | ||
161 | #define VV6410_VFLIP (1 << 4) | ||
162 | |||
163 | #define VV6410_LOW_POWER_MODE (1 << 0) | ||
164 | #define VV6410_SOFT_RESET (1 << 2) | ||
165 | #define VV6410_PAL_25_FPS (0 << 3) | ||
166 | |||
167 | #define VV6410_CLK_DIV_2 (1 << 1) | ||
168 | |||
169 | #define VV6410_FINE_EXPOSURE 320 | ||
170 | #define VV6410_COARSE_EXPOSURE 192 | ||
171 | #define VV6410_DEFAULT_GAIN 5 | ||
172 | |||
173 | #define VV6410_SUBSAMPLE 0x01 | ||
174 | #define VV6410_CROP_TO_QVGA 0x02 | ||
175 | |||
176 | static int vv6410_probe(struct sd *sd); | ||
177 | static int vv6410_start(struct sd *sd); | ||
178 | static int vv6410_init(struct sd *sd); | ||
179 | static int vv6410_stop(struct sd *sd); | ||
180 | static int vv6410_dump(struct sd *sd); | ||
181 | |||
182 | /* V4L2 controls supported by the driver */ | ||
183 | static int vv6410_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); | ||
184 | static int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val); | ||
185 | static int vv6410_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); | ||
186 | static int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val); | ||
187 | static int vv6410_get_analog_gain(struct gspca_dev *gspca_dev, __s32 *val); | ||
188 | static int vv6410_set_analog_gain(struct gspca_dev *gspca_dev, __s32 val); | ||
189 | |||
190 | const struct stv06xx_sensor stv06xx_sensor_vv6410 = { | ||
191 | .name = "ST VV6410", | ||
192 | .i2c_flush = 5, | ||
193 | .i2c_addr = 0x20, | ||
194 | .i2c_len = 1, | ||
195 | .init = vv6410_init, | ||
196 | .probe = vv6410_probe, | ||
197 | .start = vv6410_start, | ||
198 | .stop = vv6410_stop, | ||
199 | .dump = vv6410_dump, | ||
200 | |||
201 | .nctrls = 3, | ||
202 | .ctrls = { | ||
203 | { | ||
204 | { | ||
205 | .id = V4L2_CID_HFLIP, | ||
206 | .type = V4L2_CTRL_TYPE_BOOLEAN, | ||
207 | .name = "horizontal flip", | ||
208 | .minimum = 0, | ||
209 | .maximum = 1, | ||
210 | .step = 1, | ||
211 | .default_value = 0 | ||
212 | }, | ||
213 | .set = vv6410_set_hflip, | ||
214 | .get = vv6410_get_hflip | ||
215 | }, { | ||
216 | { | ||
217 | .id = V4L2_CID_VFLIP, | ||
218 | .type = V4L2_CTRL_TYPE_BOOLEAN, | ||
219 | .name = "vertical flip", | ||
220 | .minimum = 0, | ||
221 | .maximum = 1, | ||
222 | .step = 1, | ||
223 | .default_value = 0 | ||
224 | }, | ||
225 | .set = vv6410_set_vflip, | ||
226 | .get = vv6410_get_vflip | ||
227 | }, { | ||
228 | { | ||
229 | .id = V4L2_CID_GAIN, | ||
230 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
231 | .name = "analog gain", | ||
232 | .minimum = 0, | ||
233 | .maximum = 15, | ||
234 | .step = 1, | ||
235 | .default_value = 0 | ||
236 | }, | ||
237 | .set = vv6410_set_analog_gain, | ||
238 | .get = vv6410_get_analog_gain | ||
239 | } | ||
240 | }, | ||
241 | |||
242 | .nmodes = 1, | ||
243 | .modes = { | ||
244 | { | ||
245 | 356, | ||
246 | 292, | ||
247 | V4L2_PIX_FMT_SGRBG8, | ||
248 | V4L2_FIELD_NONE, | ||
249 | .sizeimage = | ||
250 | 356 * 292, | ||
251 | .bytesperline = 356, | ||
252 | .colorspace = V4L2_COLORSPACE_SRGB, | ||
253 | .priv = 0 | ||
254 | } | ||
255 | } | ||
256 | }; | ||
257 | |||
258 | /* If NULL, only single value to write, stored in len */ | ||
259 | struct stv_init { | ||
260 | const u8 *data; | ||
261 | u16 start; | ||
262 | u8 len; | ||
263 | }; | ||
264 | |||
265 | static const u8 x1500[] = { /* 0x1500 - 0x150f */ | ||
266 | 0x0b, 0xa7, 0xb7, 0x00, 0x00 | ||
267 | }; | ||
268 | |||
269 | static const u8 x1536[] = { /* 0x1536 - 0x153b */ | ||
270 | 0x02, 0x00, 0x60, 0x01, 0x20, 0x01 | ||
271 | }; | ||
272 | |||
273 | static const u8 x15c1[] = { /* 0x15c1 - 0x15c2 */ | ||
274 | 0xff, 0x03 /* Output word 0x03ff = 1023 (ISO size) */ | ||
275 | }; | ||
276 | |||
277 | static const struct stv_init stv_bridge_init[] = { | ||
278 | /* This reg is written twice. Some kind of reset? */ | ||
279 | {NULL, 0x1620, 0x80}, | ||
280 | {NULL, 0x1620, 0x00}, | ||
281 | {NULL, 0x1423, 0x04}, | ||
282 | {x1500, 0x1500, ARRAY_SIZE(x1500)}, | ||
283 | {x1536, 0x1536, ARRAY_SIZE(x1536)}, | ||
284 | {x15c1, 0x15c1, ARRAY_SIZE(x15c1)} | ||
285 | }; | ||
286 | |||
287 | static const u8 vv6410_sensor_init[][2] = { | ||
288 | /* Setup registers */ | ||
289 | {VV6410_SETUP0, VV6410_SOFT_RESET}, | ||
290 | {VV6410_SETUP0, VV6410_LOW_POWER_MODE}, | ||
291 | /* Use shuffled read-out mode */ | ||
292 | {VV6410_SETUP1, BIT(6)}, | ||
293 | /* All modes to 1 */ | ||
294 | {VV6410_FGMODES, BIT(6) | BIT(4) | BIT(2) | BIT(0)}, | ||
295 | {VV6410_PINMAPPING, 0x00}, | ||
296 | /* Pre-clock generator divide off */ | ||
297 | {VV6410_DATAFORMAT, BIT(7) | BIT(0)}, | ||
298 | |||
299 | /* Exposure registers */ | ||
300 | {VV6410_FINEH, VV6410_FINE_EXPOSURE >> 8}, | ||
301 | {VV6410_FINEL, VV6410_FINE_EXPOSURE & 0xff}, | ||
302 | {VV6410_COARSEH, VV6410_COARSE_EXPOSURE >> 8}, | ||
303 | {VV6410_COARSEL, VV6410_COARSE_EXPOSURE & 0xff}, | ||
304 | {VV6410_ANALOGGAIN, 0xf0 | VV6410_DEFAULT_GAIN}, | ||
305 | {VV6410_CLKDIV, VV6410_CLK_DIV_2}, | ||
306 | |||
307 | /* System registers */ | ||
308 | /* Enable voltage doubler */ | ||
309 | {VV6410_AS0, BIT(6) | BIT(4) | BIT(3) | BIT(2) | BIT(1)}, | ||
310 | {VV6410_AT0, 0x00}, | ||
311 | /* Power up audio, differential */ | ||
312 | {VV6410_AT1, BIT(4)|BIT(0)}, | ||
313 | }; | ||
314 | |||
315 | #endif | ||