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; | |
182 | else if (frames > 25) | |
183 | frames = 25; | |
1da177e4 LT |
184 | frames = frames2frames[frames]; |
185 | fps = frames2table[frames]; | |
186 | pEntry = &Nala_table[size][fps]; | |
187 | if (pEntry->alternate == 0) | |
188 | return -EINVAL; | |
189 | ||
938d5b9e | 190 | if (send_to_cam) |
24be689b HG |
191 | ret = send_video_command(pdev, pdev->vendpoint, |
192 | pEntry->mode, 3); | |
193 | if (ret < 0) | |
1da177e4 | 194 | return ret; |
d56410e0 | 195 | |
24be689b HG |
196 | if (pEntry->compressed && pixfmt == V4L2_PIX_FMT_YUV420) |
197 | pwc_dec1_init(pdev, pEntry->mode); | |
1da177e4 LT |
198 | |
199 | /* Set various parameters */ | |
d167a85c | 200 | pdev->pixfmt = pixfmt; |
1da177e4 | 201 | pdev->vframes = frames; |
1da177e4 | 202 | pdev->valternate = pEntry->alternate; |
795e6eb3 HG |
203 | pdev->width = pwc_image_sizes[size][0]; |
204 | pdev->height = pwc_image_sizes[size][1]; | |
205 | pdev->frame_size = (pdev->width * pdev->height * 3) / 2; | |
1da177e4 LT |
206 | if (pEntry->compressed) { |
207 | if (pdev->release < 5) { /* 4 fold compression */ | |
208 | pdev->vbandlength = 528; | |
209 | pdev->frame_size /= 4; | |
210 | } | |
211 | else { | |
212 | pdev->vbandlength = 704; | |
213 | pdev->frame_size /= 3; | |
214 | } | |
215 | } | |
216 | else | |
217 | pdev->vbandlength = 0; | |
5bbe18d7 HG |
218 | |
219 | /* Let pwc-if.c:isoc_init know we don't support higher compression */ | |
220 | *compression = 3; | |
221 | ||
1da177e4 LT |
222 | return 0; |
223 | } | |
224 | ||
225 | ||
d167a85c | 226 | static int set_video_mode_Timon(struct pwc_device *pdev, int size, int pixfmt, |
938d5b9e | 227 | int frames, int *compression, int send_to_cam) |
1da177e4 | 228 | { |
1da177e4 | 229 | const struct Timon_table_entry *pChoose; |
938d5b9e | 230 | int fps, ret = 0; |
1da177e4 | 231 | |
54d3fb3b | 232 | if (size >= PSZ_MAX || *compression < 0 || *compression > 3) |
1da177e4 | 233 | return -EINVAL; |
54d3fb3b HG |
234 | if (frames < 5) |
235 | frames = 5; | |
236 | else if (size == PSZ_VGA && frames > 15) | |
237 | frames = 15; | |
238 | else if (frames > 30) | |
239 | frames = 30; | |
1da177e4 LT |
240 | fps = (frames / 5) - 1; |
241 | ||
5bbe18d7 | 242 | /* Find a supported framerate with progressively higher compression */ |
1da177e4 | 243 | pChoose = NULL; |
5bbe18d7 HG |
244 | while (*compression <= 3) { |
245 | pChoose = &Timon_table[size][fps][*compression]; | |
246 | if (pChoose->alternate != 0) | |
247 | break; | |
248 | (*compression)++; | |
1da177e4 LT |
249 | } |
250 | if (pChoose == NULL || pChoose->alternate == 0) | |
251 | return -ENOENT; /* Not supported. */ | |
252 | ||
938d5b9e | 253 | if (send_to_cam) |
24be689b HG |
254 | ret = send_video_command(pdev, pdev->vendpoint, |
255 | pChoose->mode, 13); | |
1da177e4 LT |
256 | if (ret < 0) |
257 | return ret; | |
258 | ||
d167a85c | 259 | if (pChoose->bandlength > 0 && pixfmt == V4L2_PIX_FMT_YUV420) |
24be689b | 260 | pwc_dec23_init(pdev, pChoose->mode); |
1da177e4 LT |
261 | |
262 | /* Set various parameters */ | |
d167a85c | 263 | pdev->pixfmt = pixfmt; |
54d3fb3b | 264 | pdev->vframes = (fps + 1) * 5; |
1da177e4 | 265 | pdev->valternate = pChoose->alternate; |
795e6eb3 HG |
266 | pdev->width = pwc_image_sizes[size][0]; |
267 | pdev->height = pwc_image_sizes[size][1]; | |
1da177e4 LT |
268 | pdev->vbandlength = pChoose->bandlength; |
269 | if (pChoose->bandlength > 0) | |
795e6eb3 | 270 | pdev->frame_size = (pChoose->bandlength * pdev->height) / 4; |
1da177e4 | 271 | else |
795e6eb3 | 272 | pdev->frame_size = (pdev->width * pdev->height * 12) / 8; |
1da177e4 LT |
273 | return 0; |
274 | } | |
275 | ||
276 | ||
d167a85c | 277 | static int set_video_mode_Kiara(struct pwc_device *pdev, int size, int pixfmt, |
938d5b9e | 278 | int frames, int *compression, int send_to_cam) |
1da177e4 LT |
279 | { |
280 | const struct Kiara_table_entry *pChoose = NULL; | |
938d5b9e | 281 | int fps, ret = 0; |
1da177e4 | 282 | |
54d3fb3b | 283 | if (size >= PSZ_MAX || *compression < 0 || *compression > 3) |
1da177e4 | 284 | return -EINVAL; |
54d3fb3b HG |
285 | if (frames < 5) |
286 | frames = 5; | |
287 | else if (size == PSZ_VGA && frames > 15) | |
288 | frames = 15; | |
289 | else if (frames > 30) | |
290 | frames = 30; | |
1da177e4 LT |
291 | fps = (frames / 5) - 1; |
292 | ||
5bbe18d7 HG |
293 | /* Find a supported framerate with progressively higher compression */ |
294 | while (*compression <= 3) { | |
295 | pChoose = &Kiara_table[size][fps][*compression]; | |
dc8a7e83 HG |
296 | if (pChoose->alternate != 0) |
297 | break; | |
5bbe18d7 | 298 | (*compression)++; |
1da177e4 LT |
299 | } |
300 | if (pChoose == NULL || pChoose->alternate == 0) | |
301 | return -ENOENT; /* Not supported. */ | |
302 | ||
1da177e4 | 303 | /* Firmware bug: video endpoint is 5, but commands are sent to endpoint 4 */ |
938d5b9e | 304 | if (send_to_cam) |
24be689b | 305 | ret = send_video_command(pdev, 4, pChoose->mode, 12); |
1da177e4 LT |
306 | if (ret < 0) |
307 | return ret; | |
308 | ||
d167a85c | 309 | if (pChoose->bandlength > 0 && pixfmt == V4L2_PIX_FMT_YUV420) |
24be689b | 310 | pwc_dec23_init(pdev, pChoose->mode); |
1da177e4 | 311 | |
1da177e4 | 312 | /* All set and go */ |
d167a85c | 313 | pdev->pixfmt = pixfmt; |
54d3fb3b | 314 | pdev->vframes = (fps + 1) * 5; |
1da177e4 | 315 | pdev->valternate = pChoose->alternate; |
795e6eb3 HG |
316 | pdev->width = pwc_image_sizes[size][0]; |
317 | pdev->height = pwc_image_sizes[size][1]; | |
1da177e4 LT |
318 | pdev->vbandlength = pChoose->bandlength; |
319 | if (pdev->vbandlength > 0) | |
795e6eb3 | 320 | pdev->frame_size = (pdev->vbandlength * pdev->height) / 4; |
1da177e4 | 321 | else |
795e6eb3 | 322 | pdev->frame_size = (pdev->width * pdev->height * 12) / 8; |
dc8a7e83 HG |
323 | PWC_TRACE("frame_size=%d, vframes=%d, vsize=%d, vbandlength=%d\n", |
324 | pdev->frame_size, pdev->vframes, size, pdev->vbandlength); | |
1da177e4 LT |
325 | return 0; |
326 | } | |
327 | ||
dc8a7e83 | 328 | int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, |
938d5b9e | 329 | int pixfmt, int frames, int *compression, int send_to_cam) |
1da177e4 | 330 | { |
d56410e0 | 331 | int ret, size; |
1da177e4 | 332 | |
56ae24aa | 333 | PWC_DEBUG_FLOW("set_video_mode(%dx%d @ %d, pixfmt %08x).\n", |
d167a85c | 334 | width, height, frames, pixfmt); |
795e6eb3 | 335 | size = pwc_get_size(pdev, width, height); |
2b455db6 | 336 | PWC_TRACE("decode_size = %d.\n", size); |
1da177e4 | 337 | |
2b455db6 | 338 | if (DEVICE_USE_CODEC1(pdev->type)) { |
d167a85c | 339 | ret = set_video_mode_Nala(pdev, size, pixfmt, frames, |
938d5b9e | 340 | compression, send_to_cam); |
2b455db6 | 341 | } else if (DEVICE_USE_CODEC3(pdev->type)) { |
d167a85c | 342 | ret = set_video_mode_Kiara(pdev, size, pixfmt, frames, |
938d5b9e | 343 | compression, send_to_cam); |
2b455db6 | 344 | } else { |
d167a85c | 345 | ret = set_video_mode_Timon(pdev, size, pixfmt, frames, |
938d5b9e | 346 | compression, send_to_cam); |
1da177e4 LT |
347 | } |
348 | if (ret < 0) { | |
2b455db6 | 349 | PWC_ERROR("Failed to set video mode %s@%d fps; return code = %d\n", size2name[size], frames, ret); |
1da177e4 LT |
350 | return ret; |
351 | } | |
1da177e4 | 352 | pdev->frame_total_size = pdev->frame_size + pdev->frame_header_size + pdev->frame_trailer_size; |
795e6eb3 | 353 | PWC_DEBUG_SIZE("Set resolution to %dx%d\n", pdev->width, pdev->height); |
1da177e4 LT |
354 | return 0; |
355 | } | |
356 | ||
9ee6d78c LS |
357 | static unsigned int pwc_get_fps_Nala(struct pwc_device *pdev, unsigned int index, unsigned int size) |
358 | { | |
359 | unsigned int i; | |
360 | ||
361 | for (i = 0; i < PWC_FPS_MAX_NALA; i++) { | |
362 | if (Nala_table[size][i].alternate) { | |
363 | if (index--==0) return Nala_fps_vector[i]; | |
364 | } | |
365 | } | |
366 | return 0; | |
367 | } | |
368 | ||
369 | static unsigned int pwc_get_fps_Kiara(struct pwc_device *pdev, unsigned int index, unsigned int size) | |
370 | { | |
371 | unsigned int i; | |
372 | ||
373 | for (i = 0; i < PWC_FPS_MAX_KIARA; i++) { | |
374 | if (Kiara_table[size][i][3].alternate) { | |
375 | if (index--==0) return Kiara_fps_vector[i]; | |
376 | } | |
377 | } | |
378 | return 0; | |
379 | } | |
380 | ||
381 | static unsigned int pwc_get_fps_Timon(struct pwc_device *pdev, unsigned int index, unsigned int size) | |
382 | { | |
383 | unsigned int i; | |
384 | ||
385 | for (i=0; i < PWC_FPS_MAX_TIMON; i++) { | |
386 | if (Timon_table[size][i][3].alternate) { | |
387 | if (index--==0) return Timon_fps_vector[i]; | |
388 | } | |
389 | } | |
390 | return 0; | |
391 | } | |
392 | ||
393 | unsigned int pwc_get_fps(struct pwc_device *pdev, unsigned int index, unsigned int size) | |
394 | { | |
395 | unsigned int ret; | |
396 | ||
397 | if (DEVICE_USE_CODEC1(pdev->type)) { | |
398 | ret = pwc_get_fps_Nala(pdev, index, size); | |
399 | ||
400 | } else if (DEVICE_USE_CODEC3(pdev->type)) { | |
401 | ret = pwc_get_fps_Kiara(pdev, index, size); | |
402 | ||
403 | } else { | |
404 | ret = pwc_get_fps_Timon(pdev, index, size); | |
405 | } | |
406 | ||
407 | return ret; | |
408 | } | |
409 | ||
6c9cac89 | 410 | int pwc_get_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data) |
1da177e4 | 411 | { |
1da177e4 LT |
412 | int ret; |
413 | ||
24be689b | 414 | ret = recv_control_msg(pdev, request, value, 1); |
1da177e4 LT |
415 | if (ret < 0) |
416 | return ret; | |
1da177e4 | 417 | |
24be689b | 418 | *data = pdev->ctrl_buf[0]; |
6c9cac89 | 419 | return 0; |
1da177e4 LT |
420 | } |
421 | ||
6c9cac89 | 422 | int pwc_set_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, u8 data) |
1da177e4 | 423 | { |
1da177e4 LT |
424 | int ret; |
425 | ||
24be689b HG |
426 | pdev->ctrl_buf[0] = data; |
427 | ret = send_control_msg(pdev, request, value, pdev->ctrl_buf, 1); | |
1da177e4 LT |
428 | if (ret < 0) |
429 | return ret; | |
1da177e4 | 430 | |
6c9cac89 | 431 | return 0; |
1da177e4 LT |
432 | } |
433 | ||
6c9cac89 | 434 | int pwc_get_s8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data) |
1da177e4 | 435 | { |
1da177e4 | 436 | int ret; |
d56410e0 | 437 | |
24be689b | 438 | ret = recv_control_msg(pdev, request, value, 1); |
1da177e4 LT |
439 | if (ret < 0) |
440 | return ret; | |
1da177e4 | 441 | |
24be689b | 442 | *data = ((s8 *)pdev->ctrl_buf)[0]; |
2b455db6 | 443 | return 0; |
1da177e4 LT |
444 | } |
445 | ||
6c9cac89 | 446 | int pwc_get_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data) |
1da177e4 | 447 | { |
1da177e4 | 448 | int ret; |
d56410e0 | 449 | |
24be689b | 450 | ret = recv_control_msg(pdev, request, value, 2); |
1da177e4 LT |
451 | if (ret < 0) |
452 | return ret; | |
6c9cac89 | 453 | |
24be689b | 454 | *data = (pdev->ctrl_buf[1] << 8) | pdev->ctrl_buf[0]; |
1da177e4 LT |
455 | return 0; |
456 | } | |
457 | ||
6c9cac89 | 458 | int pwc_set_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, u16 data) |
1da177e4 | 459 | { |
1da177e4 | 460 | int ret; |
d56410e0 | 461 | |
24be689b HG |
462 | pdev->ctrl_buf[0] = data & 0xff; |
463 | pdev->ctrl_buf[1] = data >> 8; | |
464 | ret = send_control_msg(pdev, request, value, pdev->ctrl_buf, 2); | |
1da177e4 LT |
465 | if (ret < 0) |
466 | return ret; | |
467 | ||
1da177e4 LT |
468 | return 0; |
469 | } | |
470 | ||
6c9cac89 | 471 | int pwc_button_ctrl(struct pwc_device *pdev, u16 value) |
1da177e4 | 472 | { |
2b455db6 LS |
473 | int ret; |
474 | ||
6c9cac89 | 475 | ret = send_control_msg(pdev, SET_STATUS_CTL, value, NULL, 0); |
2b455db6 LS |
476 | if (ret < 0) |
477 | return ret; | |
6c9cac89 | 478 | |
2b455db6 LS |
479 | return 0; |
480 | } | |
481 | ||
1da177e4 | 482 | /* POWER */ |
3b4d0ec7 | 483 | void pwc_camera_power(struct pwc_device *pdev, int power) |
1da177e4 | 484 | { |
3b4d0ec7 HG |
485 | int r; |
486 | ||
487 | if (!pdev->power_save) | |
488 | return; | |
1da177e4 LT |
489 | |
490 | if (pdev->type < 675 || (pdev->type < 730 && pdev->release < 6)) | |
3b4d0ec7 | 491 | return; /* Not supported by Nala or Timon < release 6 */ |
1da177e4 LT |
492 | |
493 | if (power) | |
24be689b | 494 | pdev->ctrl_buf[0] = 0x00; /* active */ |
1da177e4 | 495 | else |
24be689b HG |
496 | pdev->ctrl_buf[0] = 0xFF; /* power save */ |
497 | r = send_control_msg(pdev, SET_STATUS_CTL, | |
498 | SET_POWER_SAVE_MODE_FORMATTER, pdev->ctrl_buf, 1); | |
3b4d0ec7 HG |
499 | if (r < 0) |
500 | PWC_ERROR("Failed to power %s camera (%d)\n", | |
501 | power ? "on" : "off", r); | |
502 | } | |
1da177e4 | 503 | |
1da177e4 LT |
504 | int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value) |
505 | { | |
3b4d0ec7 | 506 | int r; |
1da177e4 LT |
507 | |
508 | if (pdev->type < 730) | |
509 | return 0; | |
510 | on_value /= 100; | |
511 | off_value /= 100; | |
512 | if (on_value < 0) | |
513 | on_value = 0; | |
514 | if (on_value > 0xff) | |
515 | on_value = 0xff; | |
516 | if (off_value < 0) | |
517 | off_value = 0; | |
518 | if (off_value > 0xff) | |
519 | off_value = 0xff; | |
520 | ||
24be689b HG |
521 | pdev->ctrl_buf[0] = on_value; |
522 | pdev->ctrl_buf[1] = off_value; | |
1da177e4 | 523 | |
3b4d0ec7 | 524 | r = send_control_msg(pdev, |
24be689b | 525 | SET_STATUS_CTL, LED_FORMATTER, pdev->ctrl_buf, 2); |
3b4d0ec7 HG |
526 | if (r < 0) |
527 | PWC_ERROR("Failed to set LED on/off time (%d)\n", r); | |
528 | ||
529 | return r; | |
1da177e4 LT |
530 | } |
531 | ||
6eba9357 | 532 | #ifdef CONFIG_USB_PWC_DEBUG |
1da177e4 LT |
533 | int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor) |
534 | { | |
1da177e4 | 535 | int ret = -1, request; |
d56410e0 | 536 | |
1da177e4 LT |
537 | if (pdev->type < 675) |
538 | request = SENSOR_TYPE_FORMATTER1; | |
539 | else if (pdev->type < 730) | |
540 | return -1; /* The Vesta series doesn't have this call */ | |
541 | else | |
542 | request = SENSOR_TYPE_FORMATTER2; | |
d56410e0 | 543 | |
24be689b | 544 | ret = recv_control_msg(pdev, GET_STATUS_CTL, request, 1); |
1da177e4 LT |
545 | if (ret < 0) |
546 | return ret; | |
547 | if (pdev->type < 675) | |
24be689b | 548 | *sensor = pdev->ctrl_buf[0] | 0x100; |
1da177e4 | 549 | else |
24be689b | 550 | *sensor = pdev->ctrl_buf[0]; |
1da177e4 LT |
551 | return 0; |
552 | } | |
6eba9357 | 553 | #endif |