Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* Driver for Philips webcam |
2 | Functions that send various control messages to the webcam, including | |
3 | video modes. | |
4 | (C) 1999-2003 Nemosoft Unv. | |
2b455db6 | 5 | (C) 2004-2006 Luc Saillard (luc@saillard.org) |
6c9cac89 | 6 | (C) 2011 Hans de Goede <hdegoede@redhat.com> |
1da177e4 LT |
7 | |
8 | NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx | |
9 | driver and thus may have bugs that are not present in the original version. | |
10 | Please send bug reports and support requests to <luc@saillard.org>. | |
11 | ||
12 | NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx | |
13 | driver and thus may have bugs that are not present in the original version. | |
14 | Please send bug reports and support requests to <luc@saillard.org>. | |
15 | The decompression routines have been implemented by reverse-engineering the | |
16 | Nemosoft binary pwcx module. Caveat emptor. | |
17 | ||
18 | This program is free software; you can redistribute it and/or modify | |
19 | it under the terms of the GNU General Public License as published by | |
20 | the Free Software Foundation; either version 2 of the License, or | |
21 | (at your option) any later version. | |
22 | ||
23 | This program is distributed in the hope that it will be useful, | |
24 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
25 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
26 | GNU General Public License for more details. | |
27 | ||
28 | You should have received a copy of the GNU General Public License | |
29 | along with this program; if not, write to the Free Software | |
30 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
31 | */ | |
32 | ||
33 | /* | |
34 | Changes | |
d56410e0 MCC |
35 | 2001/08/03 Alvarado Added methods for changing white balance and |
36 | red/green gains | |
1da177e4 LT |
37 | */ |
38 | ||
39 | /* Control functions for the cam; brightness, contrast, video mode, etc. */ | |
40 | ||
41 | #ifdef __KERNEL__ | |
d56410e0 | 42 | #include <asm/uaccess.h> |
1da177e4 LT |
43 | #endif |
44 | #include <asm/errno.h> | |
d56410e0 | 45 | |
1da177e4 | 46 | #include "pwc.h" |
1da177e4 LT |
47 | #include "pwc-kiara.h" |
48 | #include "pwc-timon.h" | |
2b455db6 LS |
49 | #include "pwc-dec1.h" |
50 | #include "pwc-dec23.h" | |
1da177e4 | 51 | |
6c9cac89 | 52 | /* Selectors for status controls used only in this file */ |
2b455db6 | 53 | #define GET_STATUS_B00 0x0B00 |
1da177e4 | 54 | #define SENSOR_TYPE_FORMATTER1 0x0C00 |
2b455db6 | 55 | #define GET_STATUS_3000 0x3000 |
1da177e4 LT |
56 | #define READ_RAW_Y_MEAN_FORMATTER 0x3100 |
57 | #define SET_POWER_SAVE_MODE_FORMATTER 0x3200 | |
58 | #define MIRROR_IMAGE_FORMATTER 0x3300 | |
59 | #define LED_FORMATTER 0x3400 | |
2b455db6 LS |
60 | #define LOWLIGHT 0x3500 |
61 | #define GET_STATUS_3600 0x3600 | |
1da177e4 | 62 | #define SENSOR_TYPE_FORMATTER2 0x3700 |
2b455db6 LS |
63 | #define GET_STATUS_3800 0x3800 |
64 | #define GET_STATUS_4000 0x4000 | |
65 | #define GET_STATUS_4100 0x4100 /* Get */ | |
66 | #define CTL_STATUS_4200 0x4200 /* [GS] 1 */ | |
1da177e4 LT |
67 | |
68 | /* Formatters for the Video Endpoint controls [GS]ET_EP_STREAM_CTL */ | |
69 | #define VIDEO_OUTPUT_CONTROL_FORMATTER 0x0100 | |
70 | ||
4c4c9432 | 71 | static const char *size2name[PSZ_MAX] = |
1da177e4 LT |
72 | { |
73 | "subQCIF", | |
74 | "QSIF", | |
75 | "QCIF", | |
76 | "SIF", | |
77 | "CIF", | |
78 | "VGA", | |
d56410e0 | 79 | }; |
1da177e4 LT |
80 | |
81 | /********/ | |
82 | ||
d56410e0 | 83 | /* Entries for the Nala (645/646) camera; the Nala doesn't have compression |
1da177e4 | 84 | preferences, so you either get compressed or non-compressed streams. |
d56410e0 | 85 | |
1da177e4 LT |
86 | An alternate value of 0 means this mode is not available at all. |
87 | */ | |
88 | ||
9ee6d78c LS |
89 | #define PWC_FPS_MAX_NALA 8 |
90 | ||
1da177e4 LT |
91 | struct Nala_table_entry { |
92 | char alternate; /* USB alternate setting */ | |
93 | int compressed; /* Compressed yes/no */ | |
94 | ||
95 | unsigned char mode[3]; /* precomputed mode table */ | |
96 | }; | |
97 | ||
9ee6d78c LS |
98 | static unsigned int Nala_fps_vector[PWC_FPS_MAX_NALA] = { 4, 5, 7, 10, 12, 15, 20, 24 }; |
99 | ||
100 | static struct Nala_table_entry Nala_table[PSZ_MAX][PWC_FPS_MAX_NALA] = | |
1da177e4 LT |
101 | { |
102 | #include "pwc-nala.h" | |
103 | }; | |
104 | ||
1da177e4 LT |
105 | /****************************************************************************/ |
106 | ||
6b35ca0d | 107 | static int recv_control_msg(struct pwc_device *pdev, |
24be689b | 108 | u8 request, u16 value, int recv_count) |
6b35ca0d MF |
109 | { |
110 | int rc; | |
6b35ca0d MF |
111 | |
112 | rc = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0), | |
113 | request, | |
114 | USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | |
24be689b HG |
115 | value, pdev->vcinterface, |
116 | pdev->ctrl_buf, recv_count, USB_CTRL_GET_TIMEOUT); | |
6c9cac89 HG |
117 | if (rc < 0) |
118 | PWC_ERROR("recv_control_msg error %d req %02x val %04x\n", | |
119 | rc, request, value); | |
6b35ca0d MF |
120 | return rc; |
121 | } | |
1da177e4 | 122 | |
6b35ca0d | 123 | static inline int send_video_command(struct pwc_device *pdev, |
24be689b | 124 | int index, const unsigned char *buf, int buflen) |
1da177e4 | 125 | { |
24be689b HG |
126 | int rc; |
127 | ||
128 | memcpy(pdev->ctrl_buf, buf, buflen); | |
129 | ||
130 | rc = usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), | |
131 | SET_EP_STREAM_CTL, | |
132 | USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | |
133 | VIDEO_OUTPUT_CONTROL_FORMATTER, index, | |
134 | pdev->ctrl_buf, buflen, USB_CTRL_SET_TIMEOUT); | |
135 | if (rc >= 0) | |
136 | memcpy(pdev->cmd_buf, buf, buflen); | |
137 | else | |
138 | PWC_ERROR("send_video_command error %d\n", rc); | |
139 | ||
140 | return rc; | |
1da177e4 LT |
141 | } |
142 | ||
294e2896 | 143 | int send_control_msg(struct pwc_device *pdev, |
6b35ca0d MF |
144 | u8 request, u16 value, void *buf, int buflen) |
145 | { | |
24be689b HG |
146 | return usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), |
147 | request, | |
148 | USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | |
149 | value, pdev->vcinterface, | |
150 | buf, buflen, USB_CTRL_SET_TIMEOUT); | |
6b35ca0d MF |
151 | } |
152 | ||
d167a85c | 153 | static int set_video_mode_Nala(struct pwc_device *pdev, int size, int pixfmt, |
938d5b9e | 154 | int frames, int *compression, int send_to_cam) |
1da177e4 | 155 | { |
938d5b9e | 156 | int fps, ret = 0; |
1da177e4 LT |
157 | struct Nala_table_entry *pEntry; |
158 | int frames2frames[31] = | |
159 | { /* closest match of framerate */ | |
160 | 0, 0, 0, 0, 4, /* 0-4 */ | |
161 | 5, 5, 7, 7, 10, /* 5-9 */ | |
d56410e0 MCC |
162 | 10, 10, 12, 12, 15, /* 10-14 */ |
163 | 15, 15, 15, 20, 20, /* 15-19 */ | |
164 | 20, 20, 20, 24, 24, /* 20-24 */ | |
165 | 24, 24, 24, 24, 24, /* 25-29 */ | |
166 | 24 /* 30 */ | |
1da177e4 | 167 | }; |
d56410e0 | 168 | int frames2table[31] = |
1da177e4 LT |
169 | { 0, 0, 0, 0, 0, /* 0-4 */ |
170 | 1, 1, 1, 2, 2, /* 5-9 */ | |
171 | 3, 3, 4, 4, 4, /* 10-14 */ | |
172 | 5, 5, 5, 5, 5, /* 15-19 */ | |
173 | 6, 6, 6, 6, 7, /* 20-24 */ | |
174 | 7, 7, 7, 7, 7, /* 25-29 */ | |
175 | 7 /* 30 */ | |
176 | }; | |
d56410e0 | 177 | |
54d3fb3b | 178 | if (size < 0 || size > PSZ_CIF) |
1da177e4 | 179 | return -EINVAL; |
54d3fb3b HG |
180 | if (frames < 4) |
181 | frames = 4; | |
bb969707 HG |
182 | else if (size > PSZ_QCIF && frames > 15) |
183 | frames = 15; | |
54d3fb3b HG |
184 | else if (frames > 25) |
185 | frames = 25; | |
1da177e4 LT |
186 | frames = frames2frames[frames]; |
187 | fps = frames2table[frames]; | |
188 | pEntry = &Nala_table[size][fps]; | |
189 | if (pEntry->alternate == 0) | |
190 | return -EINVAL; | |
191 | ||
938d5b9e | 192 | if (send_to_cam) |
24be689b HG |
193 | ret = send_video_command(pdev, pdev->vendpoint, |
194 | pEntry->mode, 3); | |
195 | if (ret < 0) | |
1da177e4 | 196 | return ret; |
d56410e0 | 197 | |
24be689b HG |
198 | if (pEntry->compressed && pixfmt == V4L2_PIX_FMT_YUV420) |
199 | pwc_dec1_init(pdev, pEntry->mode); | |
1da177e4 LT |
200 | |
201 | /* Set various parameters */ | |
d167a85c | 202 | pdev->pixfmt = pixfmt; |
1da177e4 | 203 | pdev->vframes = frames; |
1da177e4 | 204 | pdev->valternate = pEntry->alternate; |
795e6eb3 HG |
205 | pdev->width = pwc_image_sizes[size][0]; |
206 | pdev->height = pwc_image_sizes[size][1]; | |
207 | pdev->frame_size = (pdev->width * pdev->height * 3) / 2; | |
1da177e4 LT |
208 | if (pEntry->compressed) { |
209 | if (pdev->release < 5) { /* 4 fold compression */ | |
210 | pdev->vbandlength = 528; | |
211 | pdev->frame_size /= 4; | |
212 | } | |
213 | else { | |
214 | pdev->vbandlength = 704; | |
215 | pdev->frame_size /= 3; | |
216 | } | |
217 | } | |
218 | else | |
219 | pdev->vbandlength = 0; | |
5bbe18d7 HG |
220 | |
221 | /* Let pwc-if.c:isoc_init know we don't support higher compression */ | |
222 | *compression = 3; | |
223 | ||
1da177e4 LT |
224 | return 0; |
225 | } | |
226 | ||
227 | ||
d167a85c | 228 | static int set_video_mode_Timon(struct pwc_device *pdev, int size, int pixfmt, |
938d5b9e | 229 | int frames, int *compression, int send_to_cam) |
1da177e4 | 230 | { |
1da177e4 | 231 | const struct Timon_table_entry *pChoose; |
938d5b9e | 232 | int fps, ret = 0; |
1da177e4 | 233 | |
54d3fb3b | 234 | if (size >= PSZ_MAX || *compression < 0 || *compression > 3) |
1da177e4 | 235 | return -EINVAL; |
54d3fb3b HG |
236 | if (frames < 5) |
237 | frames = 5; | |
238 | else if (size == PSZ_VGA && frames > 15) | |
239 | frames = 15; | |
240 | else if (frames > 30) | |
241 | frames = 30; | |
1da177e4 LT |
242 | fps = (frames / 5) - 1; |
243 | ||
5bbe18d7 | 244 | /* Find a supported framerate with progressively higher compression */ |
1da177e4 | 245 | pChoose = NULL; |
5bbe18d7 HG |
246 | while (*compression <= 3) { |
247 | pChoose = &Timon_table[size][fps][*compression]; | |
248 | if (pChoose->alternate != 0) | |
249 | break; | |
250 | (*compression)++; | |
1da177e4 LT |
251 | } |
252 | if (pChoose == NULL || pChoose->alternate == 0) | |
253 | return -ENOENT; /* Not supported. */ | |
254 | ||
938d5b9e | 255 | if (send_to_cam) |
24be689b HG |
256 | ret = send_video_command(pdev, pdev->vendpoint, |
257 | pChoose->mode, 13); | |
1da177e4 LT |
258 | if (ret < 0) |
259 | return ret; | |
260 | ||
d167a85c | 261 | if (pChoose->bandlength > 0 && pixfmt == V4L2_PIX_FMT_YUV420) |
24be689b | 262 | pwc_dec23_init(pdev, pChoose->mode); |
1da177e4 LT |
263 | |
264 | /* Set various parameters */ | |
d167a85c | 265 | pdev->pixfmt = pixfmt; |
54d3fb3b | 266 | pdev->vframes = (fps + 1) * 5; |
1da177e4 | 267 | pdev->valternate = pChoose->alternate; |
795e6eb3 HG |
268 | pdev->width = pwc_image_sizes[size][0]; |
269 | pdev->height = pwc_image_sizes[size][1]; | |
1da177e4 LT |
270 | pdev->vbandlength = pChoose->bandlength; |
271 | if (pChoose->bandlength > 0) | |
795e6eb3 | 272 | pdev->frame_size = (pChoose->bandlength * pdev->height) / 4; |
1da177e4 | 273 | else |
795e6eb3 | 274 | pdev->frame_size = (pdev->width * pdev->height * 12) / 8; |
1da177e4 LT |
275 | return 0; |
276 | } | |
277 | ||
278 | ||
d167a85c | 279 | static int set_video_mode_Kiara(struct pwc_device *pdev, int size, int pixfmt, |
938d5b9e | 280 | int frames, int *compression, int send_to_cam) |
1da177e4 LT |
281 | { |
282 | const struct Kiara_table_entry *pChoose = NULL; | |
938d5b9e | 283 | int fps, ret = 0; |
1da177e4 | 284 | |
54d3fb3b | 285 | if (size >= PSZ_MAX || *compression < 0 || *compression > 3) |
1da177e4 | 286 | return -EINVAL; |
54d3fb3b HG |
287 | if (frames < 5) |
288 | frames = 5; | |
289 | else if (size == PSZ_VGA && frames > 15) | |
290 | frames = 15; | |
291 | else if (frames > 30) | |
292 | frames = 30; | |
1da177e4 LT |
293 | fps = (frames / 5) - 1; |
294 | ||
5bbe18d7 HG |
295 | /* Find a supported framerate with progressively higher compression */ |
296 | while (*compression <= 3) { | |
297 | pChoose = &Kiara_table[size][fps][*compression]; | |
dc8a7e83 HG |
298 | if (pChoose->alternate != 0) |
299 | break; | |
5bbe18d7 | 300 | (*compression)++; |
1da177e4 LT |
301 | } |
302 | if (pChoose == NULL || pChoose->alternate == 0) | |
303 | return -ENOENT; /* Not supported. */ | |
304 | ||
1da177e4 | 305 | /* Firmware bug: video endpoint is 5, but commands are sent to endpoint 4 */ |
938d5b9e | 306 | if (send_to_cam) |
24be689b | 307 | ret = send_video_command(pdev, 4, pChoose->mode, 12); |
1da177e4 LT |
308 | if (ret < 0) |
309 | return ret; | |
310 | ||
d167a85c | 311 | if (pChoose->bandlength > 0 && pixfmt == V4L2_PIX_FMT_YUV420) |
24be689b | 312 | pwc_dec23_init(pdev, pChoose->mode); |
1da177e4 | 313 | |
1da177e4 | 314 | /* All set and go */ |
d167a85c | 315 | pdev->pixfmt = pixfmt; |
54d3fb3b | 316 | pdev->vframes = (fps + 1) * 5; |
1da177e4 | 317 | pdev->valternate = pChoose->alternate; |
795e6eb3 HG |
318 | pdev->width = pwc_image_sizes[size][0]; |
319 | pdev->height = pwc_image_sizes[size][1]; | |
1da177e4 LT |
320 | pdev->vbandlength = pChoose->bandlength; |
321 | if (pdev->vbandlength > 0) | |
795e6eb3 | 322 | pdev->frame_size = (pdev->vbandlength * pdev->height) / 4; |
1da177e4 | 323 | else |
795e6eb3 | 324 | pdev->frame_size = (pdev->width * pdev->height * 12) / 8; |
dc8a7e83 HG |
325 | PWC_TRACE("frame_size=%d, vframes=%d, vsize=%d, vbandlength=%d\n", |
326 | pdev->frame_size, pdev->vframes, size, pdev->vbandlength); | |
1da177e4 LT |
327 | return 0; |
328 | } | |
329 | ||
dc8a7e83 | 330 | int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, |
938d5b9e | 331 | int pixfmt, int frames, int *compression, int send_to_cam) |
1da177e4 | 332 | { |
d56410e0 | 333 | int ret, size; |
1da177e4 | 334 | |
56ae24aa | 335 | PWC_DEBUG_FLOW("set_video_mode(%dx%d @ %d, pixfmt %08x).\n", |
d167a85c | 336 | width, height, frames, pixfmt); |
795e6eb3 | 337 | size = pwc_get_size(pdev, width, height); |
2b455db6 | 338 | PWC_TRACE("decode_size = %d.\n", size); |
1da177e4 | 339 | |
2b455db6 | 340 | if (DEVICE_USE_CODEC1(pdev->type)) { |
d167a85c | 341 | ret = set_video_mode_Nala(pdev, size, pixfmt, frames, |
938d5b9e | 342 | compression, send_to_cam); |
2b455db6 | 343 | } else if (DEVICE_USE_CODEC3(pdev->type)) { |
d167a85c | 344 | ret = set_video_mode_Kiara(pdev, size, pixfmt, frames, |
938d5b9e | 345 | compression, send_to_cam); |
2b455db6 | 346 | } else { |
d167a85c | 347 | ret = set_video_mode_Timon(pdev, size, pixfmt, frames, |
938d5b9e | 348 | compression, send_to_cam); |
1da177e4 LT |
349 | } |
350 | if (ret < 0) { | |
2b455db6 | 351 | PWC_ERROR("Failed to set video mode %s@%d fps; return code = %d\n", size2name[size], frames, ret); |
1da177e4 LT |
352 | return ret; |
353 | } | |
1da177e4 | 354 | pdev->frame_total_size = pdev->frame_size + pdev->frame_header_size + pdev->frame_trailer_size; |
795e6eb3 | 355 | PWC_DEBUG_SIZE("Set resolution to %dx%d\n", pdev->width, pdev->height); |
1da177e4 LT |
356 | return 0; |
357 | } | |
358 | ||
9ee6d78c LS |
359 | static unsigned int pwc_get_fps_Nala(struct pwc_device *pdev, unsigned int index, unsigned int size) |
360 | { | |
361 | unsigned int i; | |
362 | ||
363 | for (i = 0; i < PWC_FPS_MAX_NALA; i++) { | |
364 | if (Nala_table[size][i].alternate) { | |
365 | if (index--==0) return Nala_fps_vector[i]; | |
366 | } | |
367 | } | |
368 | return 0; | |
369 | } | |
370 | ||
371 | static unsigned int pwc_get_fps_Kiara(struct pwc_device *pdev, unsigned int index, unsigned int size) | |
372 | { | |
373 | unsigned int i; | |
374 | ||
375 | for (i = 0; i < PWC_FPS_MAX_KIARA; i++) { | |
376 | if (Kiara_table[size][i][3].alternate) { | |
377 | if (index--==0) return Kiara_fps_vector[i]; | |
378 | } | |
379 | } | |
380 | return 0; | |
381 | } | |
382 | ||
383 | static unsigned int pwc_get_fps_Timon(struct pwc_device *pdev, unsigned int index, unsigned int size) | |
384 | { | |
385 | unsigned int i; | |
386 | ||
387 | for (i=0; i < PWC_FPS_MAX_TIMON; i++) { | |
388 | if (Timon_table[size][i][3].alternate) { | |
389 | if (index--==0) return Timon_fps_vector[i]; | |
390 | } | |
391 | } | |
392 | return 0; | |
393 | } | |
394 | ||
395 | unsigned int pwc_get_fps(struct pwc_device *pdev, unsigned int index, unsigned int size) | |
396 | { | |
397 | unsigned int ret; | |
398 | ||
399 | if (DEVICE_USE_CODEC1(pdev->type)) { | |
400 | ret = pwc_get_fps_Nala(pdev, index, size); | |
401 | ||
402 | } else if (DEVICE_USE_CODEC3(pdev->type)) { | |
403 | ret = pwc_get_fps_Kiara(pdev, index, size); | |
404 | ||
405 | } else { | |
406 | ret = pwc_get_fps_Timon(pdev, index, size); | |
407 | } | |
408 | ||
409 | return ret; | |
410 | } | |
411 | ||
6c9cac89 | 412 | int pwc_get_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data) |
1da177e4 | 413 | { |
1da177e4 LT |
414 | int ret; |
415 | ||
24be689b | 416 | ret = recv_control_msg(pdev, request, value, 1); |
1da177e4 LT |
417 | if (ret < 0) |
418 | return ret; | |
1da177e4 | 419 | |
24be689b | 420 | *data = pdev->ctrl_buf[0]; |
6c9cac89 | 421 | return 0; |
1da177e4 LT |
422 | } |
423 | ||
6c9cac89 | 424 | int pwc_set_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, u8 data) |
1da177e4 | 425 | { |
1da177e4 LT |
426 | int ret; |
427 | ||
24be689b HG |
428 | pdev->ctrl_buf[0] = data; |
429 | ret = send_control_msg(pdev, request, value, pdev->ctrl_buf, 1); | |
1da177e4 LT |
430 | if (ret < 0) |
431 | return ret; | |
1da177e4 | 432 | |
6c9cac89 | 433 | return 0; |
1da177e4 LT |
434 | } |
435 | ||
6c9cac89 | 436 | int pwc_get_s8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data) |
1da177e4 | 437 | { |
1da177e4 | 438 | int ret; |
d56410e0 | 439 | |
24be689b | 440 | ret = recv_control_msg(pdev, request, value, 1); |
1da177e4 LT |
441 | if (ret < 0) |
442 | return ret; | |
1da177e4 | 443 | |
24be689b | 444 | *data = ((s8 *)pdev->ctrl_buf)[0]; |
2b455db6 | 445 | return 0; |
1da177e4 LT |
446 | } |
447 | ||
6c9cac89 | 448 | int pwc_get_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data) |
1da177e4 | 449 | { |
1da177e4 | 450 | int ret; |
d56410e0 | 451 | |
24be689b | 452 | ret = recv_control_msg(pdev, request, value, 2); |
1da177e4 LT |
453 | if (ret < 0) |
454 | return ret; | |
6c9cac89 | 455 | |
24be689b | 456 | *data = (pdev->ctrl_buf[1] << 8) | pdev->ctrl_buf[0]; |
1da177e4 LT |
457 | return 0; |
458 | } | |
459 | ||
6c9cac89 | 460 | int pwc_set_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, u16 data) |
1da177e4 | 461 | { |
1da177e4 | 462 | int ret; |
d56410e0 | 463 | |
24be689b HG |
464 | pdev->ctrl_buf[0] = data & 0xff; |
465 | pdev->ctrl_buf[1] = data >> 8; | |
466 | ret = send_control_msg(pdev, request, value, pdev->ctrl_buf, 2); | |
1da177e4 LT |
467 | if (ret < 0) |
468 | return ret; | |
469 | ||
1da177e4 LT |
470 | return 0; |
471 | } | |
472 | ||
6c9cac89 | 473 | int pwc_button_ctrl(struct pwc_device *pdev, u16 value) |
1da177e4 | 474 | { |
2b455db6 LS |
475 | int ret; |
476 | ||
6c9cac89 | 477 | ret = send_control_msg(pdev, SET_STATUS_CTL, value, NULL, 0); |
2b455db6 LS |
478 | if (ret < 0) |
479 | return ret; | |
6c9cac89 | 480 | |
2b455db6 LS |
481 | return 0; |
482 | } | |
483 | ||
1da177e4 | 484 | /* POWER */ |
3b4d0ec7 | 485 | void pwc_camera_power(struct pwc_device *pdev, int power) |
1da177e4 | 486 | { |
3b4d0ec7 HG |
487 | int r; |
488 | ||
489 | if (!pdev->power_save) | |
490 | return; | |
1da177e4 LT |
491 | |
492 | if (pdev->type < 675 || (pdev->type < 730 && pdev->release < 6)) | |
3b4d0ec7 | 493 | return; /* Not supported by Nala or Timon < release 6 */ |
1da177e4 LT |
494 | |
495 | if (power) | |
24be689b | 496 | pdev->ctrl_buf[0] = 0x00; /* active */ |
1da177e4 | 497 | else |
24be689b HG |
498 | pdev->ctrl_buf[0] = 0xFF; /* power save */ |
499 | r = send_control_msg(pdev, SET_STATUS_CTL, | |
500 | SET_POWER_SAVE_MODE_FORMATTER, pdev->ctrl_buf, 1); | |
3b4d0ec7 HG |
501 | if (r < 0) |
502 | PWC_ERROR("Failed to power %s camera (%d)\n", | |
503 | power ? "on" : "off", r); | |
504 | } | |
1da177e4 | 505 | |
1da177e4 LT |
506 | int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value) |
507 | { | |
3b4d0ec7 | 508 | int r; |
1da177e4 LT |
509 | |
510 | if (pdev->type < 730) | |
511 | return 0; | |
512 | on_value /= 100; | |
513 | off_value /= 100; | |
514 | if (on_value < 0) | |
515 | on_value = 0; | |
516 | if (on_value > 0xff) | |
517 | on_value = 0xff; | |
518 | if (off_value < 0) | |
519 | off_value = 0; | |
520 | if (off_value > 0xff) | |
521 | off_value = 0xff; | |
522 | ||
24be689b HG |
523 | pdev->ctrl_buf[0] = on_value; |
524 | pdev->ctrl_buf[1] = off_value; | |
1da177e4 | 525 | |
3b4d0ec7 | 526 | r = send_control_msg(pdev, |
24be689b | 527 | SET_STATUS_CTL, LED_FORMATTER, pdev->ctrl_buf, 2); |
3b4d0ec7 HG |
528 | if (r < 0) |
529 | PWC_ERROR("Failed to set LED on/off time (%d)\n", r); | |
530 | ||
531 | return r; | |
1da177e4 LT |
532 | } |
533 | ||
6eba9357 | 534 | #ifdef CONFIG_USB_PWC_DEBUG |
1da177e4 LT |
535 | int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor) |
536 | { | |
1da177e4 | 537 | int ret = -1, request; |
d56410e0 | 538 | |
1da177e4 LT |
539 | if (pdev->type < 675) |
540 | request = SENSOR_TYPE_FORMATTER1; | |
541 | else if (pdev->type < 730) | |
542 | return -1; /* The Vesta series doesn't have this call */ | |
543 | else | |
544 | request = SENSOR_TYPE_FORMATTER2; | |
d56410e0 | 545 | |
24be689b | 546 | ret = recv_control_msg(pdev, GET_STATUS_CTL, request, 1); |
1da177e4 LT |
547 | if (ret < 0) |
548 | return ret; | |
549 | if (pdev->type < 675) | |
24be689b | 550 | *sensor = pdev->ctrl_buf[0] | 0x100; |
1da177e4 | 551 | else |
24be689b | 552 | *sensor = pdev->ctrl_buf[0]; |
1da177e4 LT |
553 | return 0; |
554 | } | |
6eba9357 | 555 | #endif |