blob: 2cf77001804d6938b265f8b1636ac8a59b8ed3a5 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/* Driver for Philips webcam
2 Functions that send various control messages to the webcam, including
3 video modes.
4 (C) 1999-2003 Nemosoft Unv.
Luc Saillard2b455db2006-04-24 10:29:46 -03005 (C) 2004-2006 Luc Saillard (luc@saillard.org)
Hans de Goede6c9cac82011-06-26 12:52:01 -03006 (C) 2011 Hans de Goede <hdegoede@redhat.com>
Linus Torvalds1da177e2005-04-16 15:20:36 -07007
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
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030035 2001/08/03 Alvarado Added methods for changing white balance and
36 red/green gains
Linus Torvalds1da177e2005-04-16 15:20:36 -070037 */
38
39/* Control functions for the cam; brightness, contrast, video mode, etc. */
40
41#ifdef __KERNEL__
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030042#include <asm/uaccess.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070043#endif
44#include <asm/errno.h>
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030045
Linus Torvalds1da177e2005-04-16 15:20:36 -070046#include "pwc.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070047#include "pwc-kiara.h"
48#include "pwc-timon.h"
Luc Saillard2b455db2006-04-24 10:29:46 -030049#include "pwc-dec1.h"
50#include "pwc-dec23.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070051
Hans de Goede6c9cac82011-06-26 12:52:01 -030052/* Selectors for status controls used only in this file */
Luc Saillard2b455db2006-04-24 10:29:46 -030053#define GET_STATUS_B00 0x0B00
Linus Torvalds1da177e2005-04-16 15:20:36 -070054#define SENSOR_TYPE_FORMATTER1 0x0C00
Luc Saillard2b455db2006-04-24 10:29:46 -030055#define GET_STATUS_3000 0x3000
Linus Torvalds1da177e2005-04-16 15:20:36 -070056#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
Luc Saillard2b455db2006-04-24 10:29:46 -030060#define LOWLIGHT 0x3500
61#define GET_STATUS_3600 0x3600
Linus Torvalds1da177e2005-04-16 15:20:36 -070062#define SENSOR_TYPE_FORMATTER2 0x3700
Luc Saillard2b455db2006-04-24 10:29:46 -030063#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 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070067
68/* Formatters for the Video Endpoint controls [GS]ET_EP_STREAM_CTL */
69#define VIDEO_OUTPUT_CONTROL_FORMATTER 0x0100
70
Arjan van de Ven4c4c9432005-11-29 09:43:42 +010071static const char *size2name[PSZ_MAX] =
Linus Torvalds1da177e2005-04-16 15:20:36 -070072{
73 "subQCIF",
74 "QSIF",
75 "QCIF",
76 "SIF",
77 "CIF",
78 "VGA",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030079};
Linus Torvalds1da177e2005-04-16 15:20:36 -070080
81/********/
82
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030083/* Entries for the Nala (645/646) camera; the Nala doesn't have compression
Linus Torvalds1da177e2005-04-16 15:20:36 -070084 preferences, so you either get compressed or non-compressed streams.
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030085
Linus Torvalds1da177e2005-04-16 15:20:36 -070086 An alternate value of 0 means this mode is not available at all.
87 */
88
Luc Saillard9ee6d782007-04-22 23:54:36 -030089#define PWC_FPS_MAX_NALA 8
90
Linus Torvalds1da177e2005-04-16 15:20:36 -070091struct 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
Luc Saillard9ee6d782007-04-22 23:54:36 -030098static unsigned int Nala_fps_vector[PWC_FPS_MAX_NALA] = { 4, 5, 7, 10, 12, 15, 20, 24 };
99
100static struct Nala_table_entry Nala_table[PSZ_MAX][PWC_FPS_MAX_NALA] =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101{
102#include "pwc-nala.h"
103};
104
Luc Saillard2b455db2006-04-24 10:29:46 -0300105static void pwc_set_image_buffer_size(struct pwc_device *pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106
107/****************************************************************************/
108
Martin Fuzzey6b35ca02009-04-21 21:48:09 +0200109static int _send_control_msg(struct pwc_device *pdev,
Hans de Goede6c9cac82011-06-26 12:52:01 -0300110 u8 request, u16 value, int index, void *buf, int buflen)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111{
Martin Fuzzey6b35ca02009-04-21 21:48:09 +0200112 int rc;
113 void *kbuf = NULL;
114
115 if (buflen) {
Thomas Meyer6d00c9a2011-11-17 18:43:40 -0300116 kbuf = kmemdup(buf, buflen, GFP_KERNEL); /* not allowed on stack */
Martin Fuzzey6b35ca02009-04-21 21:48:09 +0200117 if (kbuf == NULL)
118 return -ENOMEM;
Martin Fuzzey6b35ca02009-04-21 21:48:09 +0200119 }
120
121 rc = usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0),
122 request,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123 USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
Martin Fuzzey6b35ca02009-04-21 21:48:09 +0200124 value,
125 index,
Hans de Goede6c9cac82011-06-26 12:52:01 -0300126 kbuf, buflen, USB_CTRL_SET_TIMEOUT);
Martin Fuzzey6b35ca02009-04-21 21:48:09 +0200127
128 kfree(kbuf);
129 return rc;
130}
131
132static int recv_control_msg(struct pwc_device *pdev,
133 u8 request, u16 value, void *buf, int buflen)
134{
135 int rc;
136 void *kbuf = kmalloc(buflen, GFP_KERNEL); /* not allowed on stack */
137
138 if (kbuf == NULL)
139 return -ENOMEM;
140
141 rc = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0),
142 request,
143 USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
144 value,
145 pdev->vcinterface,
Hans de Goede6c9cac82011-06-26 12:52:01 -0300146 kbuf, buflen, USB_CTRL_GET_TIMEOUT);
Martin Fuzzey6b35ca02009-04-21 21:48:09 +0200147 memcpy(buf, kbuf, buflen);
148 kfree(kbuf);
Hans de Goede6c9cac82011-06-26 12:52:01 -0300149
150 if (rc < 0)
151 PWC_ERROR("recv_control_msg error %d req %02x val %04x\n",
152 rc, request, value);
Martin Fuzzey6b35ca02009-04-21 21:48:09 +0200153 return rc;
154}
155
156static inline int send_video_command(struct pwc_device *pdev,
157 int index, void *buf, int buflen)
158{
159 return _send_control_msg(pdev,
160 SET_EP_STREAM_CTL,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 VIDEO_OUTPUT_CONTROL_FORMATTER,
162 index,
Hans de Goede6c9cac82011-06-26 12:52:01 -0300163 buf, buflen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164}
165
Hans de Goede294e2892011-07-03 12:23:24 -0300166int send_control_msg(struct pwc_device *pdev,
Martin Fuzzey6b35ca02009-04-21 21:48:09 +0200167 u8 request, u16 value, void *buf, int buflen)
168{
169 return _send_control_msg(pdev,
Hans de Goede6c9cac82011-06-26 12:52:01 -0300170 request, value, pdev->vcinterface, buf, buflen);
Martin Fuzzey6b35ca02009-04-21 21:48:09 +0200171}
172
Luc Saillard2b455db2006-04-24 10:29:46 -0300173static int set_video_mode_Nala(struct pwc_device *pdev, int size, int frames)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174{
175 unsigned char buf[3];
176 int ret, fps;
177 struct Nala_table_entry *pEntry;
178 int frames2frames[31] =
179 { /* closest match of framerate */
180 0, 0, 0, 0, 4, /* 0-4 */
181 5, 5, 7, 7, 10, /* 5-9 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300182 10, 10, 12, 12, 15, /* 10-14 */
183 15, 15, 15, 20, 20, /* 15-19 */
184 20, 20, 20, 24, 24, /* 20-24 */
185 24, 24, 24, 24, 24, /* 25-29 */
186 24 /* 30 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 };
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300188 int frames2table[31] =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189 { 0, 0, 0, 0, 0, /* 0-4 */
190 1, 1, 1, 2, 2, /* 5-9 */
191 3, 3, 4, 4, 4, /* 10-14 */
192 5, 5, 5, 5, 5, /* 15-19 */
193 6, 6, 6, 6, 7, /* 20-24 */
194 7, 7, 7, 7, 7, /* 25-29 */
195 7 /* 30 */
196 };
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300197
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198 if (size < 0 || size > PSZ_CIF || frames < 4 || frames > 25)
199 return -EINVAL;
200 frames = frames2frames[frames];
201 fps = frames2table[frames];
202 pEntry = &Nala_table[size][fps];
203 if (pEntry->alternate == 0)
204 return -EINVAL;
205
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300206 memcpy(buf, pEntry->mode, 3);
Martin Fuzzey6b35ca02009-04-21 21:48:09 +0200207 ret = send_video_command(pdev, pdev->vendpoint, buf, 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208 if (ret < 0) {
Luc Saillard2b455db2006-04-24 10:29:46 -0300209 PWC_DEBUG_MODULE("Failed to send video command... %d\n", ret);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210 return ret;
211 }
Hans de Goede6eba9352011-06-26 06:49:59 -0300212 if (pEntry->compressed && pdev->pixfmt == V4L2_PIX_FMT_YUV420) {
213 ret = pwc_dec1_init(pdev, pdev->type, pdev->release, buf);
214 if (ret < 0)
215 return ret;
216 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300217
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218 pdev->cmd_len = 3;
219 memcpy(pdev->cmd_buf, buf, 3);
220
221 /* Set various parameters */
222 pdev->vframes = frames;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 pdev->valternate = pEntry->alternate;
224 pdev->image = pwc_image_sizes[size];
225 pdev->frame_size = (pdev->image.x * pdev->image.y * 3) / 2;
226 if (pEntry->compressed) {
227 if (pdev->release < 5) { /* 4 fold compression */
228 pdev->vbandlength = 528;
229 pdev->frame_size /= 4;
230 }
231 else {
232 pdev->vbandlength = 704;
233 pdev->frame_size /= 3;
234 }
235 }
236 else
237 pdev->vbandlength = 0;
238 return 0;
239}
240
241
Hans de Goededc8a7e82011-12-31 10:52:02 -0300242static int set_video_mode_Timon(struct pwc_device *pdev, int size, int frames,
243 int compression)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244{
245 unsigned char buf[13];
246 const struct Timon_table_entry *pChoose;
247 int ret, fps;
248
249 if (size >= PSZ_MAX || frames < 5 || frames > 30 || compression < 0 || compression > 3)
250 return -EINVAL;
251 if (size == PSZ_VGA && frames > 15)
252 return -EINVAL;
253 fps = (frames / 5) - 1;
254
255 /* Find a supported framerate with progressively higher compression ratios
256 if the preferred ratio is not available.
257 */
258 pChoose = NULL;
259 while (compression <= 3) {
260 pChoose = &Timon_table[size][fps][compression];
261 if (pChoose->alternate != 0)
262 break;
263 compression++;
264 }
265 if (pChoose == NULL || pChoose->alternate == 0)
266 return -ENOENT; /* Not supported. */
267
268 memcpy(buf, pChoose->mode, 13);
Martin Fuzzey6b35ca02009-04-21 21:48:09 +0200269 ret = send_video_command(pdev, pdev->vendpoint, buf, 13);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270 if (ret < 0)
271 return ret;
272
Hans de Goede6eba9352011-06-26 06:49:59 -0300273 if (pChoose->bandlength > 0 && pdev->pixfmt == V4L2_PIX_FMT_YUV420) {
274 ret = pwc_dec23_init(pdev, pdev->type, buf);
275 if (ret < 0)
276 return ret;
277 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278
279 pdev->cmd_len = 13;
280 memcpy(pdev->cmd_buf, buf, 13);
281
282 /* Set various parameters */
283 pdev->vframes = frames;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284 pdev->valternate = pChoose->alternate;
285 pdev->image = pwc_image_sizes[size];
286 pdev->vbandlength = pChoose->bandlength;
287 if (pChoose->bandlength > 0)
288 pdev->frame_size = (pChoose->bandlength * pdev->image.y) / 4;
289 else
290 pdev->frame_size = (pdev->image.x * pdev->image.y * 12) / 8;
291 return 0;
292}
293
294
Hans de Goededc8a7e82011-12-31 10:52:02 -0300295static int set_video_mode_Kiara(struct pwc_device *pdev, int size, int frames,
296 int compression)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297{
298 const struct Kiara_table_entry *pChoose = NULL;
299 int fps, ret;
300 unsigned char buf[12];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301
302 if (size >= PSZ_MAX || frames < 5 || frames > 30 || compression < 0 || compression > 3)
303 return -EINVAL;
304 if (size == PSZ_VGA && frames > 15)
305 return -EINVAL;
306 fps = (frames / 5) - 1;
307
Hans de Goededc8a7e82011-12-31 10:52:02 -0300308 /* Find a supported framerate with progressively higher compression
309 ratios if the preferred ratio is not available.
310 Skip this step when using RAW modes.
311 */
312 while (compression <= 3) {
313 pChoose = &Kiara_table[size][fps][compression];
314 if (pChoose->alternate != 0)
315 break;
316 compression++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317 }
318 if (pChoose == NULL || pChoose->alternate == 0)
319 return -ENOENT; /* Not supported. */
320
Luc Saillard2b455db2006-04-24 10:29:46 -0300321 PWC_TRACE("Using alternate setting %d.\n", pChoose->alternate);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300322
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 /* usb_control_msg won't take staticly allocated arrays as argument?? */
324 memcpy(buf, pChoose->mode, 12);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325
326 /* Firmware bug: video endpoint is 5, but commands are sent to endpoint 4 */
Martin Fuzzey6b35ca02009-04-21 21:48:09 +0200327 ret = send_video_command(pdev, 4 /* pdev->vendpoint */, buf, 12);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328 if (ret < 0)
329 return ret;
330
Hans de Goede6eba9352011-06-26 06:49:59 -0300331 if (pChoose->bandlength > 0 && pdev->pixfmt == V4L2_PIX_FMT_YUV420) {
332 ret = pwc_dec23_init(pdev, pdev->type, buf);
333 if (ret < 0)
334 return ret;
335 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336
337 pdev->cmd_len = 12;
338 memcpy(pdev->cmd_buf, buf, 12);
339 /* All set and go */
340 pdev->vframes = frames;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 pdev->valternate = pChoose->alternate;
342 pdev->image = pwc_image_sizes[size];
343 pdev->vbandlength = pChoose->bandlength;
344 if (pdev->vbandlength > 0)
345 pdev->frame_size = (pdev->vbandlength * pdev->image.y) / 4;
346 else
347 pdev->frame_size = (pdev->image.x * pdev->image.y * 12) / 8;
Hans de Goededc8a7e82011-12-31 10:52:02 -0300348 PWC_TRACE("frame_size=%d, vframes=%d, vsize=%d, vbandlength=%d\n",
349 pdev->frame_size, pdev->vframes, size, pdev->vbandlength);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350 return 0;
351}
352
353
354
355/**
356 @pdev: device structure
357 @width: viewport width
358 @height: viewport height
359 @frame: framerate, in fps
360 @compression: preferred compression ratio
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361 */
Hans de Goededc8a7e82011-12-31 10:52:02 -0300362int pwc_set_video_mode(struct pwc_device *pdev, int width, int height,
363 int frames, int compression)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364{
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300365 int ret, size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366
Hans Verkuil479567c2010-09-12 17:05:11 -0300367 PWC_DEBUG_FLOW("set_video_mode(%dx%d @ %d, pixfmt %08x).\n", width, height, frames, pdev->pixfmt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 size = pwc_decode_size(pdev, width, height);
369 if (size < 0) {
Luc Saillard2b455db2006-04-24 10:29:46 -0300370 PWC_DEBUG_MODULE("Could not find suitable size.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371 return -ERANGE;
372 }
Luc Saillard2b455db2006-04-24 10:29:46 -0300373 PWC_TRACE("decode_size = %d.\n", size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374
Luc Saillard2b455db2006-04-24 10:29:46 -0300375 if (DEVICE_USE_CODEC1(pdev->type)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 ret = set_video_mode_Nala(pdev, size, frames);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377
Luc Saillard2b455db2006-04-24 10:29:46 -0300378 } else if (DEVICE_USE_CODEC3(pdev->type)) {
Hans de Goededc8a7e82011-12-31 10:52:02 -0300379 ret = set_video_mode_Kiara(pdev, size, frames, compression);
Luc Saillard2b455db2006-04-24 10:29:46 -0300380
381 } else {
Hans de Goededc8a7e82011-12-31 10:52:02 -0300382 ret = set_video_mode_Timon(pdev, size, frames, compression);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383 }
384 if (ret < 0) {
Luc Saillard2b455db2006-04-24 10:29:46 -0300385 PWC_ERROR("Failed to set video mode %s@%d fps; return code = %d\n", size2name[size], frames, ret);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386 return ret;
387 }
388 pdev->view.x = width;
389 pdev->view.y = height;
Hans de Goede6eba9352011-06-26 06:49:59 -0300390 pdev->vcompression = compression;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391 pdev->frame_total_size = pdev->frame_size + pdev->frame_header_size + pdev->frame_trailer_size;
392 pwc_set_image_buffer_size(pdev);
Luc Saillard2b455db2006-04-24 10:29:46 -0300393 PWC_DEBUG_SIZE("Set viewport to %dx%d, image size is %dx%d.\n", width, height, pwc_image_sizes[size].x, pwc_image_sizes[size].y);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394 return 0;
395}
396
Luc Saillard9ee6d782007-04-22 23:54:36 -0300397static unsigned int pwc_get_fps_Nala(struct pwc_device *pdev, unsigned int index, unsigned int size)
398{
399 unsigned int i;
400
401 for (i = 0; i < PWC_FPS_MAX_NALA; i++) {
402 if (Nala_table[size][i].alternate) {
403 if (index--==0) return Nala_fps_vector[i];
404 }
405 }
406 return 0;
407}
408
409static unsigned int pwc_get_fps_Kiara(struct pwc_device *pdev, unsigned int index, unsigned int size)
410{
411 unsigned int i;
412
413 for (i = 0; i < PWC_FPS_MAX_KIARA; i++) {
414 if (Kiara_table[size][i][3].alternate) {
415 if (index--==0) return Kiara_fps_vector[i];
416 }
417 }
418 return 0;
419}
420
421static unsigned int pwc_get_fps_Timon(struct pwc_device *pdev, unsigned int index, unsigned int size)
422{
423 unsigned int i;
424
425 for (i=0; i < PWC_FPS_MAX_TIMON; i++) {
426 if (Timon_table[size][i][3].alternate) {
427 if (index--==0) return Timon_fps_vector[i];
428 }
429 }
430 return 0;
431}
432
433unsigned int pwc_get_fps(struct pwc_device *pdev, unsigned int index, unsigned int size)
434{
435 unsigned int ret;
436
437 if (DEVICE_USE_CODEC1(pdev->type)) {
438 ret = pwc_get_fps_Nala(pdev, index, size);
439
440 } else if (DEVICE_USE_CODEC3(pdev->type)) {
441 ret = pwc_get_fps_Kiara(pdev, index, size);
442
443 } else {
444 ret = pwc_get_fps_Timon(pdev, index, size);
445 }
446
447 return ret;
448}
449
Luc Saillard2b455db2006-04-24 10:29:46 -0300450static void pwc_set_image_buffer_size(struct pwc_device *pdev)
451{
Hans de Goede885fe182011-06-06 15:33:44 -0300452 int factor = 0;
Luc Saillard2b455db2006-04-24 10:29:46 -0300453
Hans Verkuil479567c2010-09-12 17:05:11 -0300454 /* for V4L2_PIX_FMT_YUV420 */
455 switch (pdev->pixfmt) {
456 case V4L2_PIX_FMT_YUV420:
Luc Saillard2b455db2006-04-24 10:29:46 -0300457 factor = 6;
458 break;
Hans Verkuil479567c2010-09-12 17:05:11 -0300459 case V4L2_PIX_FMT_PWC1:
460 case V4L2_PIX_FMT_PWC2:
Luc Saillard2b455db2006-04-24 10:29:46 -0300461 factor = 6; /* can be uncompressed YUV420P */
462 break;
463 }
464
465 /* Set sizes in bytes */
466 pdev->image.size = pdev->image.x * pdev->image.y * factor / 4;
467 pdev->view.size = pdev->view.x * pdev->view.y * factor / 4;
468
469 /* Align offset, or you'll get some very weird results in
470 YUV420 mode... x must be multiple of 4 (to get the Y's in
471 place), and y even (or you'll mixup U & V). This is less of a
472 problem for YUV420P.
473 */
474 pdev->offset.x = ((pdev->view.x - pdev->image.x) / 2) & 0xFFFC;
475 pdev->offset.y = ((pdev->view.y - pdev->image.y) / 2) & 0xFFFE;
Luc Saillard2b455db2006-04-24 10:29:46 -0300476}
477
Hans de Goede6c9cac82011-06-26 12:52:01 -0300478int pwc_get_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480 int ret;
Hans de Goede6c9cac82011-06-26 12:52:01 -0300481 u8 buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482
Hans de Goede6c9cac82011-06-26 12:52:01 -0300483 ret = recv_control_msg(pdev, request, value, &buf, sizeof(buf));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 if (ret < 0)
485 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486
Hans de Goede6c9cac82011-06-26 12:52:01 -0300487 *data = buf;
Luc Saillard2b455db2006-04-24 10:29:46 -0300488 return 0;
489}
490
Hans de Goede6c9cac82011-06-26 12:52:01 -0300491int pwc_set_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, u8 data)
Luc Saillard2b455db2006-04-24 10:29:46 -0300492{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493 int ret;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300494
Hans de Goede6c9cac82011-06-26 12:52:01 -0300495 ret = send_control_msg(pdev, request, value, &data, sizeof(data));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496 if (ret < 0)
497 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498
499 return 0;
500}
501
Hans de Goede6c9cac82011-06-26 12:52:01 -0300502int pwc_get_s8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503{
Luc Saillard2b455db2006-04-24 10:29:46 -0300504 int ret;
Hans de Goede6c9cac82011-06-26 12:52:01 -0300505 s8 buf;
Luc Saillard2b455db2006-04-24 10:29:46 -0300506
Hans de Goede6c9cac82011-06-26 12:52:01 -0300507 ret = recv_control_msg(pdev, request, value, &buf, sizeof(buf));
Luc Saillard2b455db2006-04-24 10:29:46 -0300508 if (ret < 0)
509 return ret;
Hans de Goede6c9cac82011-06-26 12:52:01 -0300510
511 *data = buf;
512 return 0;
513}
514
515int pwc_get_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data)
516{
517 int ret;
518 u8 buf[2];
519
520 ret = recv_control_msg(pdev, request, value, buf, sizeof(buf));
521 if (ret < 0)
522 return ret;
523
524 *data = (buf[1] << 8) | buf[0];
525 return 0;
526}
527
528int pwc_set_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, u16 data)
529{
530 int ret;
531 u8 buf[2];
532
533 buf[0] = data & 0xff;
534 buf[1] = data >> 8;
535 ret = send_control_msg(pdev, request, value, buf, sizeof(buf));
536 if (ret < 0)
537 return ret;
538
539 return 0;
540}
541
542int pwc_button_ctrl(struct pwc_device *pdev, u16 value)
543{
544 int ret;
545
546 ret = send_control_msg(pdev, SET_STATUS_CTL, value, NULL, 0);
547 if (ret < 0)
548 return ret;
549
Luc Saillard2b455db2006-04-24 10:29:46 -0300550 return 0;
551}
552
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553/* POWER */
Hans de Goede3b4d0ec2011-06-26 03:51:19 -0300554void pwc_camera_power(struct pwc_device *pdev, int power)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555{
556 char buf;
Hans de Goede3b4d0ec2011-06-26 03:51:19 -0300557 int r;
558
559 if (!pdev->power_save)
560 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561
562 if (pdev->type < 675 || (pdev->type < 730 && pdev->release < 6))
Hans de Goede3b4d0ec2011-06-26 03:51:19 -0300563 return; /* Not supported by Nala or Timon < release 6 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564
565 if (power)
566 buf = 0x00; /* active */
567 else
568 buf = 0xFF; /* power save */
Hans de Goede3b4d0ec2011-06-26 03:51:19 -0300569 r = send_control_msg(pdev,
Martin Fuzzey6b35ca02009-04-21 21:48:09 +0200570 SET_STATUS_CTL, SET_POWER_SAVE_MODE_FORMATTER,
571 &buf, sizeof(buf));
Hans de Goede3b4d0ec2011-06-26 03:51:19 -0300572
573 if (r < 0)
574 PWC_ERROR("Failed to power %s camera (%d)\n",
575 power ? "on" : "off", r);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576}
577
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value)
579{
580 unsigned char buf[2];
Hans de Goede3b4d0ec2011-06-26 03:51:19 -0300581 int r;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582
583 if (pdev->type < 730)
584 return 0;
585 on_value /= 100;
586 off_value /= 100;
587 if (on_value < 0)
588 on_value = 0;
589 if (on_value > 0xff)
590 on_value = 0xff;
591 if (off_value < 0)
592 off_value = 0;
593 if (off_value > 0xff)
594 off_value = 0xff;
595
596 buf[0] = on_value;
597 buf[1] = off_value;
598
Hans de Goede3b4d0ec2011-06-26 03:51:19 -0300599 r = send_control_msg(pdev,
Martin Fuzzey6b35ca02009-04-21 21:48:09 +0200600 SET_STATUS_CTL, LED_FORMATTER, &buf, sizeof(buf));
Hans de Goede3b4d0ec2011-06-26 03:51:19 -0300601 if (r < 0)
602 PWC_ERROR("Failed to set LED on/off time (%d)\n", r);
603
604 return r;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605}
606
Adrian Bunkb20c3cf2006-06-23 06:49:34 -0300607static int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608{
609 unsigned char buf[2];
610 int ret;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300611
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612 if (pdev->type < 730) {
613 *on_value = -1;
614 *off_value = -1;
615 return 0;
616 }
617
Martin Fuzzey6b35ca02009-04-21 21:48:09 +0200618 ret = recv_control_msg(pdev,
619 GET_STATUS_CTL, LED_FORMATTER, &buf, sizeof(buf));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620 if (ret < 0)
621 return ret;
622 *on_value = buf[0] * 100;
623 *off_value = buf[1] * 100;
624 return 0;
625}
626
Luc Saillard2b455db2006-04-24 10:29:46 -0300627static int _pwc_mpt_reset(struct pwc_device *pdev, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628{
629 unsigned char buf;
Hans de Goedec20d78c2011-10-09 09:16:46 -0300630 int r;
631
632 mutex_lock(&pdev->udevlock);
633 if (!pdev->udev) {
634 r = -ENODEV;
635 goto leave;
636 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300637
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638 buf = flags & 0x03; // only lower two bits are currently used
Hans de Goedec20d78c2011-10-09 09:16:46 -0300639 r = send_control_msg(pdev,
Martin Fuzzey6b35ca02009-04-21 21:48:09 +0200640 SET_MPT_CTL, PT_RESET_CONTROL_FORMATTER, &buf, sizeof(buf));
Hans de Goedec20d78c2011-10-09 09:16:46 -0300641leave:
642 mutex_unlock(&pdev->udevlock);
643 return r;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644}
645
Luc Saillard2b455db2006-04-24 10:29:46 -0300646int pwc_mpt_reset(struct pwc_device *pdev, int flags)
647{
648 int ret;
649 ret = _pwc_mpt_reset(pdev, flags);
650 if (ret >= 0) {
651 pdev->pan_angle = 0;
652 pdev->tilt_angle = 0;
653 }
654 return ret;
655}
656
657static int _pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658{
659 unsigned char buf[4];
Hans de Goedec20d78c2011-10-09 09:16:46 -0300660 int r;
661
662 mutex_lock(&pdev->udevlock);
663 if (!pdev->udev) {
664 r = -ENODEV;
665 goto leave;
666 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300667
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668 /* set new relative angle; angles are expressed in degrees * 100,
Steven Cole093cf722005-05-03 19:07:24 -0600669 but cam as .5 degree resolution, hence divide by 200. Also
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670 the angle must be multiplied by 64 before it's send to
671 the cam (??)
672 */
673 pan = 64 * pan / 100;
674 tilt = -64 * tilt / 100; /* positive tilt is down, which is not what the user would expect */
675 buf[0] = pan & 0xFF;
676 buf[1] = (pan >> 8) & 0xFF;
677 buf[2] = tilt & 0xFF;
678 buf[3] = (tilt >> 8) & 0xFF;
Hans de Goedec20d78c2011-10-09 09:16:46 -0300679 r = send_control_msg(pdev,
Martin Fuzzey6b35ca02009-04-21 21:48:09 +0200680 SET_MPT_CTL, PT_RELATIVE_CONTROL_FORMATTER, &buf, sizeof(buf));
Hans de Goedec20d78c2011-10-09 09:16:46 -0300681leave:
682 mutex_unlock(&pdev->udevlock);
683 return r;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684}
685
Luc Saillard2b455db2006-04-24 10:29:46 -0300686int pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt)
687{
688 int ret;
689
690 /* check absolute ranges */
691 if (pan < pdev->angle_range.pan_min ||
692 pan > pdev->angle_range.pan_max ||
693 tilt < pdev->angle_range.tilt_min ||
694 tilt > pdev->angle_range.tilt_max)
695 return -ERANGE;
696
697 /* go to relative range, check again */
698 pan -= pdev->pan_angle;
699 tilt -= pdev->tilt_angle;
700 /* angles are specified in degrees * 100, thus the limit = 36000 */
701 if (pan < -36000 || pan > 36000 || tilt < -36000 || tilt > 36000)
702 return -ERANGE;
703
704 ret = _pwc_mpt_set_angle(pdev, pan, tilt);
705 if (ret >= 0) {
706 pdev->pan_angle += pan;
707 pdev->tilt_angle += tilt;
708 }
709 if (ret == -EPIPE) /* stall -> out of range */
710 ret = -ERANGE;
711 return ret;
712}
713
714static int pwc_mpt_get_status(struct pwc_device *pdev, struct pwc_mpt_status *status)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715{
716 int ret;
717 unsigned char buf[5];
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300718
Hans de Goedec20d78c2011-10-09 09:16:46 -0300719 mutex_lock(&pdev->udevlock);
720 if (!pdev->udev) {
721 ret = -ENODEV;
722 goto leave;
723 }
724
Martin Fuzzey6b35ca02009-04-21 21:48:09 +0200725 ret = recv_control_msg(pdev,
726 GET_MPT_CTL, PT_STATUS_FORMATTER, &buf, sizeof(buf));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727 if (ret < 0)
Hans de Goedec20d78c2011-10-09 09:16:46 -0300728 goto leave;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729 status->status = buf[0] & 0x7; // 3 bits are used for reporting
730 status->time_pan = (buf[1] << 8) + buf[2];
731 status->time_tilt = (buf[3] << 8) + buf[4];
Hans de Goedec20d78c2011-10-09 09:16:46 -0300732leave:
733 mutex_unlock(&pdev->udevlock);
734 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735}
736
Hans de Goede6eba9352011-06-26 06:49:59 -0300737#ifdef CONFIG_USB_PWC_DEBUG
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor)
739{
740 unsigned char buf;
741 int ret = -1, request;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300742
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743 if (pdev->type < 675)
744 request = SENSOR_TYPE_FORMATTER1;
745 else if (pdev->type < 730)
746 return -1; /* The Vesta series doesn't have this call */
747 else
748 request = SENSOR_TYPE_FORMATTER2;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300749
Martin Fuzzey6b35ca02009-04-21 21:48:09 +0200750 ret = recv_control_msg(pdev,
751 GET_STATUS_CTL, request, &buf, sizeof(buf));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752 if (ret < 0)
753 return ret;
754 if (pdev->type < 675)
755 *sensor = buf | 0x100;
756 else
757 *sensor = buf;
758 return 0;
759}
Hans de Goede6eba9352011-06-26 06:49:59 -0300760#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761
762 /* End of Add-Ons */
763 /* ************************************************* */
764
Luc Saillard2b455db2006-04-24 10:29:46 -0300765/* Linux 2.5.something and 2.6 pass direct pointers to arguments of
766 ioctl() calls. With 2.4, you have to do tedious copy_from_user()
767 and copy_to_user() calls. With these macros we circumvent this,
768 and let me maintain only one source file. The functionality is
769 exactly the same otherwise.
770 */
771
Luc Saillard2b455db2006-04-24 10:29:46 -0300772/* define local variable for arg */
773#define ARG_DEF(ARG_type, ARG_name)\
774 ARG_type *ARG_name = arg;
775/* copy arg to local variable */
776#define ARG_IN(ARG_name) /* nothing */
777/* argument itself (referenced) */
778#define ARGR(ARG_name) (*ARG_name)
779/* argument address */
780#define ARGA(ARG_name) ARG_name
781/* copy local variable to arg */
782#define ARG_OUT(ARG_name) /* nothing */
783
Hans de Goede6c9cac82011-06-26 12:52:01 -0300784/*
785 * Our ctrls use native values, but the old custom pwc ioctl interface expects
786 * values from 0 - 65535, define 2 helper functions to scale things. */
787static int pwc_ioctl_g_ctrl(struct v4l2_ctrl *ctrl)
788{
789 return v4l2_ctrl_g_ctrl(ctrl) * 65535 / ctrl->maximum;
790}
791
792static int pwc_ioctl_s_ctrl(struct v4l2_ctrl *ctrl, int val)
793{
794 return v4l2_ctrl_s_ctrl(ctrl, val * ctrl->maximum / 65535);
795}
796
Hans Verkuil069b7472008-12-30 07:04:34 -0300797long pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798{
Hans Verkuil069b7472008-12-30 07:04:34 -0300799 long ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800
801 switch(cmd) {
802 case VIDIOCPWCRUSER:
Hans de Goedec20d78c2011-10-09 09:16:46 -0300803 ret = v4l2_ctrl_s_ctrl(pdev->restore_user, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300805
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806 case VIDIOCPWCSUSER:
Hans de Goedec20d78c2011-10-09 09:16:46 -0300807 ret = v4l2_ctrl_s_ctrl(pdev->save_user, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300809
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810 case VIDIOCPWCFACTORY:
Hans de Goedec20d78c2011-10-09 09:16:46 -0300811 ret = v4l2_ctrl_s_ctrl(pdev->restore_factory, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300813
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814 case VIDIOCPWCSCQUAL:
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300815 {
Luc Saillard2b455db2006-04-24 10:29:46 -0300816 ARG_DEF(int, qual)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817
Hans de Goedec20d78c2011-10-09 09:16:46 -0300818 mutex_lock(&pdev->udevlock);
819 if (!pdev->udev) {
820 ret = -ENODEV;
821 goto leave;
822 }
823
824 if (pdev->iso_init) {
Hans de Goede3751e282010-11-16 11:39:25 -0300825 ret = -EBUSY;
Hans de Goedec20d78c2011-10-09 09:16:46 -0300826 goto leave;
Hans de Goede3751e282010-11-16 11:39:25 -0300827 }
828
Luc Saillard2b455db2006-04-24 10:29:46 -0300829 ARG_IN(qual)
830 if (ARGR(qual) < 0 || ARGR(qual) > 3)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831 ret = -EINVAL;
832 else
Hans de Goededc8a7e82011-12-31 10:52:02 -0300833 ret = pwc_set_video_mode(pdev,
834 pdev->view.x, pdev->view.y,
835 pdev->vframes, ARGR(qual));
Hans de Goedec20d78c2011-10-09 09:16:46 -0300836leave:
837 mutex_unlock(&pdev->udevlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838 break;
839 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300840
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841 case VIDIOCPWCGCQUAL:
842 {
Luc Saillard2b455db2006-04-24 10:29:46 -0300843 ARG_DEF(int, qual)
844
845 ARGR(qual) = pdev->vcompression;
846 ARG_OUT(qual)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847 break;
848 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300849
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850 case VIDIOCPWCPROBE:
851 {
Luc Saillard2b455db2006-04-24 10:29:46 -0300852 ARG_DEF(struct pwc_probe, probe)
853
Hans de Goede9a7b2d12011-06-06 14:43:39 -0300854 strcpy(ARGR(probe).name, pdev->vdev.name);
Luc Saillard2b455db2006-04-24 10:29:46 -0300855 ARGR(probe).type = pdev->type;
856 ARG_OUT(probe)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857 break;
858 }
859
860 case VIDIOCPWCGSERIAL:
861 {
Luc Saillard2b455db2006-04-24 10:29:46 -0300862 ARG_DEF(struct pwc_serial, serial)
863
864 strcpy(ARGR(serial).serial, pdev->serial);
865 ARG_OUT(serial)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866 break;
867 }
868
869 case VIDIOCPWCSAGC:
870 {
Luc Saillard2b455db2006-04-24 10:29:46 -0300871 ARG_DEF(int, agc)
Luc Saillard2b455db2006-04-24 10:29:46 -0300872 ARG_IN(agc)
Hans de Goede6c9cac82011-06-26 12:52:01 -0300873 ret = v4l2_ctrl_s_ctrl(pdev->autogain, ARGR(agc) < 0);
874 if (ret == 0 && ARGR(agc) >= 0)
875 ret = pwc_ioctl_s_ctrl(pdev->gain, ARGR(agc));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876 break;
877 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300878
Linus Torvalds1da177e2005-04-16 15:20:36 -0700879 case VIDIOCPWCGAGC:
880 {
Luc Saillard2b455db2006-04-24 10:29:46 -0300881 ARG_DEF(int, agc)
Hans de Goede6c9cac82011-06-26 12:52:01 -0300882 if (v4l2_ctrl_g_ctrl(pdev->autogain))
883 ARGR(agc) = -1;
884 else
885 ARGR(agc) = pwc_ioctl_g_ctrl(pdev->gain);
Luc Saillard2b455db2006-04-24 10:29:46 -0300886 ARG_OUT(agc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700887 break;
888 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300889
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890 case VIDIOCPWCSSHUTTER:
891 {
Hans de Goede6c9cac82011-06-26 12:52:01 -0300892 ARG_DEF(int, shutter)
893 ARG_IN(shutter)
894 ret = v4l2_ctrl_s_ctrl(pdev->exposure_auto,
895 /* Menu idx 0 = auto, idx 1 = manual */
896 ARGR(shutter) >= 0);
897 if (ret == 0 && ARGR(shutter) >= 0)
898 ret = pwc_ioctl_s_ctrl(pdev->exposure, ARGR(shutter));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899 break;
900 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300901
902 case VIDIOCPWCSAWB:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903 {
Luc Saillard2b455db2006-04-24 10:29:46 -0300904 ARG_DEF(struct pwc_whitebalance, wb)
Luc Saillard2b455db2006-04-24 10:29:46 -0300905 ARG_IN(wb)
Hans de Goede6c9cac82011-06-26 12:52:01 -0300906 ret = v4l2_ctrl_s_ctrl(pdev->auto_white_balance,
907 ARGR(wb).mode);
908 if (ret == 0 && ARGR(wb).mode == PWC_WB_MANUAL)
909 ret = pwc_ioctl_s_ctrl(pdev->red_balance,
910 ARGR(wb).manual_red);
911 if (ret == 0 && ARGR(wb).mode == PWC_WB_MANUAL)
912 ret = pwc_ioctl_s_ctrl(pdev->blue_balance,
913 ARGR(wb).manual_blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914 break;
915 }
916
917 case VIDIOCPWCGAWB:
918 {
Luc Saillard2b455db2006-04-24 10:29:46 -0300919 ARG_DEF(struct pwc_whitebalance, wb)
Hans de Goede6c9cac82011-06-26 12:52:01 -0300920 ARGR(wb).mode = v4l2_ctrl_g_ctrl(pdev->auto_white_balance);
921 ARGR(wb).manual_red = ARGR(wb).read_red =
922 pwc_ioctl_g_ctrl(pdev->red_balance);
923 ARGR(wb).manual_blue = ARGR(wb).read_blue =
924 pwc_ioctl_g_ctrl(pdev->blue_balance);
Luc Saillard2b455db2006-04-24 10:29:46 -0300925 ARG_OUT(wb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926 break;
927 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300928
Linus Torvalds1da177e2005-04-16 15:20:36 -0700929 case VIDIOCPWCSAWBSPEED:
930 {
Luc Saillard2b455db2006-04-24 10:29:46 -0300931 ARG_DEF(struct pwc_wb_speed, wbs)
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300932
Luc Saillard2b455db2006-04-24 10:29:46 -0300933 if (ARGR(wbs).control_speed > 0) {
Hans de Goedef4af65952011-10-09 09:56:23 -0300934 ret = pwc_ioctl_s_ctrl(pdev->awb_speed,
935 ARGR(wbs).control_speed);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936 }
Hans de Goedef4af65952011-10-09 09:56:23 -0300937 if (ret == 0 && ARGR(wbs).control_delay > 0) {
938 ret = pwc_ioctl_s_ctrl(pdev->awb_delay,
939 ARGR(wbs).control_delay);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940 }
941 break;
942 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300943
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944 case VIDIOCPWCGAWBSPEED:
945 {
Luc Saillard2b455db2006-04-24 10:29:46 -0300946 ARG_DEF(struct pwc_wb_speed, wbs)
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300947
Hans de Goedef4af65952011-10-09 09:56:23 -0300948 ARGR(wbs).control_speed = v4l2_ctrl_g_ctrl(pdev->awb_speed);
949 ARGR(wbs).control_delay = v4l2_ctrl_g_ctrl(pdev->awb_delay);
Luc Saillard2b455db2006-04-24 10:29:46 -0300950 ARG_OUT(wbs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951 break;
952 }
953
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300954 case VIDIOCPWCSLED:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955 {
Luc Saillard2b455db2006-04-24 10:29:46 -0300956 ARG_DEF(struct pwc_leds, leds)
957
Hans de Goedec20d78c2011-10-09 09:16:46 -0300958 mutex_lock(&pdev->udevlock);
959 if (!pdev->udev) {
960 ret = -ENODEV;
961 break;
962 }
963
Luc Saillard2b455db2006-04-24 10:29:46 -0300964 ARG_IN(leds)
965 ret = pwc_set_leds(pdev, ARGR(leds).led_on, ARGR(leds).led_off);
Hans de Goedec20d78c2011-10-09 09:16:46 -0300966
967 mutex_unlock(&pdev->udevlock);
Trent Piepho657de3c2006-06-20 00:30:57 -0300968 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700969 }
970
971
972 case VIDIOCPWCGLED:
973 {
Luc Saillard2b455db2006-04-24 10:29:46 -0300974 ARG_DEF(struct pwc_leds, leds)
975
Hans de Goedec20d78c2011-10-09 09:16:46 -0300976 mutex_lock(&pdev->udevlock);
977 if (!pdev->udev) {
978 ret = -ENODEV;
979 break;
980 }
981
Luc Saillard2b455db2006-04-24 10:29:46 -0300982 ret = pwc_get_leds(pdev, &ARGR(leds).led_on, &ARGR(leds).led_off);
983 ARG_OUT(leds)
Hans de Goedec20d78c2011-10-09 09:16:46 -0300984
985 mutex_unlock(&pdev->udevlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986 break;
987 }
988
989 case VIDIOCPWCSCONTOUR:
990 {
Luc Saillard2b455db2006-04-24 10:29:46 -0300991 ARG_DEF(int, contour)
Luc Saillard2b455db2006-04-24 10:29:46 -0300992 ARG_IN(contour)
Hans de Goede6c9cac82011-06-26 12:52:01 -0300993 ret = v4l2_ctrl_s_ctrl(pdev->autocontour, ARGR(contour) < 0);
994 if (ret == 0 && ARGR(contour) >= 0)
995 ret = pwc_ioctl_s_ctrl(pdev->contour, ARGR(contour));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700996 break;
997 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300998
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999 case VIDIOCPWCGCONTOUR:
1000 {
Luc Saillard2b455db2006-04-24 10:29:46 -03001001 ARG_DEF(int, contour)
Hans de Goede6c9cac82011-06-26 12:52:01 -03001002 if (v4l2_ctrl_g_ctrl(pdev->autocontour))
1003 ARGR(contour) = -1;
1004 else
1005 ARGR(contour) = pwc_ioctl_g_ctrl(pdev->contour);
Luc Saillard2b455db2006-04-24 10:29:46 -03001006 ARG_OUT(contour)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007 break;
1008 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001009
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010 case VIDIOCPWCSBACKLIGHT:
1011 {
Luc Saillard2b455db2006-04-24 10:29:46 -03001012 ARG_DEF(int, backlight)
Luc Saillard2b455db2006-04-24 10:29:46 -03001013 ARG_IN(backlight)
Hans de Goede6c9cac82011-06-26 12:52:01 -03001014 ret = v4l2_ctrl_s_ctrl(pdev->backlight, ARGR(backlight));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015 break;
1016 }
1017
1018 case VIDIOCPWCGBACKLIGHT:
1019 {
Luc Saillard2b455db2006-04-24 10:29:46 -03001020 ARG_DEF(int, backlight)
Hans de Goede6c9cac82011-06-26 12:52:01 -03001021 ARGR(backlight) = v4l2_ctrl_g_ctrl(pdev->backlight);
Luc Saillard2b455db2006-04-24 10:29:46 -03001022 ARG_OUT(backlight)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001023 break;
1024 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001025
Linus Torvalds1da177e2005-04-16 15:20:36 -07001026 case VIDIOCPWCSFLICKER:
1027 {
Luc Saillard2b455db2006-04-24 10:29:46 -03001028 ARG_DEF(int, flicker)
Luc Saillard2b455db2006-04-24 10:29:46 -03001029 ARG_IN(flicker)
Hans de Goede6c9cac82011-06-26 12:52:01 -03001030 ret = v4l2_ctrl_s_ctrl(pdev->flicker, ARGR(flicker));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031 break;
1032 }
1033
1034 case VIDIOCPWCGFLICKER:
1035 {
Luc Saillard2b455db2006-04-24 10:29:46 -03001036 ARG_DEF(int, flicker)
Hans de Goede6c9cac82011-06-26 12:52:01 -03001037 ARGR(flicker) = v4l2_ctrl_g_ctrl(pdev->flicker);
Luc Saillard2b455db2006-04-24 10:29:46 -03001038 ARG_OUT(flicker)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001039 break;
1040 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001041
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042 case VIDIOCPWCSDYNNOISE:
1043 {
Luc Saillard2b455db2006-04-24 10:29:46 -03001044 ARG_DEF(int, dynnoise)
Luc Saillard2b455db2006-04-24 10:29:46 -03001045 ARG_IN(dynnoise)
Hans de Goede6c9cac82011-06-26 12:52:01 -03001046 ret = v4l2_ctrl_s_ctrl(pdev->noise_reduction, ARGR(dynnoise));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047 break;
1048 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001049
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050 case VIDIOCPWCGDYNNOISE:
1051 {
Luc Saillard2b455db2006-04-24 10:29:46 -03001052 ARG_DEF(int, dynnoise)
Hans de Goede6c9cac82011-06-26 12:52:01 -03001053 ARGR(dynnoise) = v4l2_ctrl_g_ctrl(pdev->noise_reduction);
Luc Saillard2b455db2006-04-24 10:29:46 -03001054 ARG_OUT(dynnoise);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055 break;
1056 }
1057
1058 case VIDIOCPWCGREALSIZE:
1059 {
Luc Saillard2b455db2006-04-24 10:29:46 -03001060 ARG_DEF(struct pwc_imagesize, size)
1061
1062 ARGR(size).width = pdev->image.x;
1063 ARGR(size).height = pdev->image.y;
1064 ARG_OUT(size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001066 }
1067
1068 case VIDIOCPWCMPTRESET:
1069 {
1070 if (pdev->features & FEATURE_MOTOR_PANTILT)
1071 {
Luc Saillard2b455db2006-04-24 10:29:46 -03001072 ARG_DEF(int, flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073
Luc Saillard2b455db2006-04-24 10:29:46 -03001074 ARG_IN(flags)
1075 ret = pwc_mpt_reset(pdev, ARGR(flags));
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001076 }
1077 else
1078 {
1079 ret = -ENXIO;
1080 }
1081 break;
1082 }
1083
1084 case VIDIOCPWCMPTGRANGE:
1085 {
1086 if (pdev->features & FEATURE_MOTOR_PANTILT)
1087 {
Luc Saillard2b455db2006-04-24 10:29:46 -03001088 ARG_DEF(struct pwc_mpt_range, range)
1089
1090 ARGR(range) = pdev->angle_range;
1091 ARG_OUT(range)
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001092 }
1093 else
1094 {
1095 ret = -ENXIO;
1096 }
1097 break;
1098 }
1099
1100 case VIDIOCPWCMPTSANGLE:
1101 {
1102 int new_pan, new_tilt;
1103
1104 if (pdev->features & FEATURE_MOTOR_PANTILT)
1105 {
Luc Saillard2b455db2006-04-24 10:29:46 -03001106 ARG_DEF(struct pwc_mpt_angles, angles)
1107
1108 ARG_IN(angles)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001109 /* The camera can only set relative angles, so
1110 do some calculations when getting an absolute angle .
1111 */
Luc Saillard2b455db2006-04-24 10:29:46 -03001112 if (ARGR(angles).absolute)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001113 {
Luc Saillard2b455db2006-04-24 10:29:46 -03001114 new_pan = ARGR(angles).pan;
1115 new_tilt = ARGR(angles).tilt;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001116 }
1117 else
1118 {
Luc Saillard2b455db2006-04-24 10:29:46 -03001119 new_pan = pdev->pan_angle + ARGR(angles).pan;
1120 new_tilt = pdev->tilt_angle + ARGR(angles).tilt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001121 }
Luc Saillard2b455db2006-04-24 10:29:46 -03001122 ret = pwc_mpt_set_angle(pdev, new_pan, new_tilt);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001123 }
1124 else
1125 {
1126 ret = -ENXIO;
1127 }
1128 break;
1129 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001130
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001131 case VIDIOCPWCMPTGANGLE:
1132 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001134 if (pdev->features & FEATURE_MOTOR_PANTILT)
1135 {
Luc Saillard2b455db2006-04-24 10:29:46 -03001136 ARG_DEF(struct pwc_mpt_angles, angles)
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001137
Luc Saillard2b455db2006-04-24 10:29:46 -03001138 ARGR(angles).absolute = 1;
1139 ARGR(angles).pan = pdev->pan_angle;
1140 ARGR(angles).tilt = pdev->tilt_angle;
1141 ARG_OUT(angles)
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001142 }
1143 else
1144 {
1145 ret = -ENXIO;
1146 }
1147 break;
1148 }
1149
1150 case VIDIOCPWCMPTSTATUS:
1151 {
1152 if (pdev->features & FEATURE_MOTOR_PANTILT)
1153 {
Luc Saillard2b455db2006-04-24 10:29:46 -03001154 ARG_DEF(struct pwc_mpt_status, status)
1155
1156 ret = pwc_mpt_get_status(pdev, ARGA(status));
1157 ARG_OUT(status)
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001158 }
1159 else
1160 {
1161 ret = -ENXIO;
1162 }
1163 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164 }
1165
1166 case VIDIOCPWCGVIDCMD:
1167 {
Hans Verkuilc6eb8ea2008-09-03 17:11:54 -03001168 ARG_DEF(struct pwc_video_command, vcmd);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001169
Hans Verkuilc6eb8ea2008-09-03 17:11:54 -03001170 ARGR(vcmd).type = pdev->type;
1171 ARGR(vcmd).release = pdev->release;
1172 ARGR(vcmd).command_len = pdev->cmd_len;
1173 memcpy(&ARGR(vcmd).command_buf, pdev->cmd_buf, pdev->cmd_len);
1174 ARGR(vcmd).bandlength = pdev->vbandlength;
1175 ARGR(vcmd).frame_size = pdev->frame_size;
1176 ARG_OUT(vcmd)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177 break;
1178 }
Michael Krufkyb930e1d2007-08-27 18:16:54 -03001179 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001180 case VIDIOCPWCGVIDTABLE:
1181 {
Luc Saillard2b455db2006-04-24 10:29:46 -03001182 ARG_DEF(struct pwc_table_init_buffer, table);
1183 ARGR(table).len = pdev->cmd_len;
1184 memcpy(&ARGR(table).buffer, pdev->decompress_data, pdev->decompressor->table_size);
1185 ARG_OUT(table)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001186 break;
1187 }
1188 */
1189
1190 default:
1191 ret = -ENOIOCTLCMD;
1192 break;
1193 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001194
Linus Torvalds1da177e2005-04-16 15:20:36 -07001195 if (ret > 0)
1196 return 0;
1197 return ret;
1198}
1199
1200
Luc Saillard2b455db2006-04-24 10:29:46 -03001201/* vim: set cinoptions= formatoptions=croql cindent shiftwidth=8 tabstop=8: */