blob: 2227c5640c12c9552ade8b05d92cc9d8a7ad3234 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * cpia CPiA driver
3 *
4 * Supports CPiA based Video Camera's.
5 *
6 * (C) Copyright 1999-2000 Peter Pregler
7 * (C) Copyright 1999-2000 Scott J. Bertin
8 * (C) Copyright 1999-2000 Johannes Erdfelt <johannes@erdfelt.com>
9 * (C) Copyright 2000 STMicroelectronics
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 */
25
26/* define _CPIA_DEBUG_ for verbose debug output (see cpia.h) */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030027/* #define _CPIA_DEBUG_ 1 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070028
29#include <linux/config.h>
30
31#include <linux/module.h>
32#include <linux/moduleparam.h>
33#include <linux/init.h>
34#include <linux/fs.h>
35#include <linux/vmalloc.h>
36#include <linux/slab.h>
37#include <linux/proc_fs.h>
38#include <linux/ctype.h>
39#include <linux/pagemap.h>
40#include <linux/delay.h>
41#include <asm/io.h>
Ingo Molnar3593cab2006-02-07 06:49:14 -020042#include <linux/mutex.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070043
44#ifdef CONFIG_KMOD
45#include <linux/kmod.h>
46#endif
47
48#include "cpia.h"
49
50#ifdef CONFIG_VIDEO_CPIA_PP
51extern int cpia_pp_init(void);
52#endif
53#ifdef CONFIG_VIDEO_CPIA_USB
54extern int cpia_usb_init(void);
55#endif
56
57static int video_nr = -1;
58
59#ifdef MODULE
60module_param(video_nr, int, 0);
Randy Dunlap2f8de1a2006-03-21 14:53:22 -030061MODULE_AUTHOR("Scott J. Bertin <sbertin@securenym.net> & Peter Pregler <Peter_Pregler@email.com> & Johannes Erdfelt <johannes@erdfelt.com>");
Linus Torvalds1da177e2005-04-16 15:20:36 -070062MODULE_DESCRIPTION("V4L-driver for Vision CPiA based cameras");
63MODULE_LICENSE("GPL");
64MODULE_SUPPORTED_DEVICE("video");
65#endif
66
67static unsigned short colorspace_conv = 0;
68module_param(colorspace_conv, ushort, 0444);
69MODULE_PARM_DESC(colorspace_conv,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030070 "\n<n> Colorspace conversion:"
71 "\n0 = disable"
72 "\n1 = enable"
73 "\nDefault value is 0"
74 "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -070075
76#define ABOUT "V4L-Driver for Vision CPiA based cameras"
77
78#ifndef VID_HARDWARE_CPIA
79#define VID_HARDWARE_CPIA 24 /* FIXME -> from linux/videodev.h */
80#endif
81
82#define CPIA_MODULE_CPIA (0<<5)
83#define CPIA_MODULE_SYSTEM (1<<5)
84#define CPIA_MODULE_VP_CTRL (5<<5)
85#define CPIA_MODULE_CAPTURE (6<<5)
86#define CPIA_MODULE_DEBUG (7<<5)
87
88#define INPUT (DATA_IN << 8)
89#define OUTPUT (DATA_OUT << 8)
90
91#define CPIA_COMMAND_GetCPIAVersion (INPUT | CPIA_MODULE_CPIA | 1)
92#define CPIA_COMMAND_GetPnPID (INPUT | CPIA_MODULE_CPIA | 2)
93#define CPIA_COMMAND_GetCameraStatus (INPUT | CPIA_MODULE_CPIA | 3)
94#define CPIA_COMMAND_GotoHiPower (OUTPUT | CPIA_MODULE_CPIA | 4)
95#define CPIA_COMMAND_GotoLoPower (OUTPUT | CPIA_MODULE_CPIA | 5)
96#define CPIA_COMMAND_GotoSuspend (OUTPUT | CPIA_MODULE_CPIA | 7)
97#define CPIA_COMMAND_GotoPassThrough (OUTPUT | CPIA_MODULE_CPIA | 8)
98#define CPIA_COMMAND_ModifyCameraStatus (OUTPUT | CPIA_MODULE_CPIA | 10)
99
100#define CPIA_COMMAND_ReadVCRegs (INPUT | CPIA_MODULE_SYSTEM | 1)
101#define CPIA_COMMAND_WriteVCReg (OUTPUT | CPIA_MODULE_SYSTEM | 2)
102#define CPIA_COMMAND_ReadMCPorts (INPUT | CPIA_MODULE_SYSTEM | 3)
103#define CPIA_COMMAND_WriteMCPort (OUTPUT | CPIA_MODULE_SYSTEM | 4)
104#define CPIA_COMMAND_SetBaudRate (OUTPUT | CPIA_MODULE_SYSTEM | 5)
105#define CPIA_COMMAND_SetECPTiming (OUTPUT | CPIA_MODULE_SYSTEM | 6)
106#define CPIA_COMMAND_ReadIDATA (INPUT | CPIA_MODULE_SYSTEM | 7)
107#define CPIA_COMMAND_WriteIDATA (OUTPUT | CPIA_MODULE_SYSTEM | 8)
108#define CPIA_COMMAND_GenericCall (OUTPUT | CPIA_MODULE_SYSTEM | 9)
109#define CPIA_COMMAND_I2CStart (OUTPUT | CPIA_MODULE_SYSTEM | 10)
110#define CPIA_COMMAND_I2CStop (OUTPUT | CPIA_MODULE_SYSTEM | 11)
111#define CPIA_COMMAND_I2CWrite (OUTPUT | CPIA_MODULE_SYSTEM | 12)
112#define CPIA_COMMAND_I2CRead (INPUT | CPIA_MODULE_SYSTEM | 13)
113
114#define CPIA_COMMAND_GetVPVersion (INPUT | CPIA_MODULE_VP_CTRL | 1)
115#define CPIA_COMMAND_ResetFrameCounter (INPUT | CPIA_MODULE_VP_CTRL | 2)
116#define CPIA_COMMAND_SetColourParams (OUTPUT | CPIA_MODULE_VP_CTRL | 3)
117#define CPIA_COMMAND_SetExposure (OUTPUT | CPIA_MODULE_VP_CTRL | 4)
118#define CPIA_COMMAND_SetColourBalance (OUTPUT | CPIA_MODULE_VP_CTRL | 6)
119#define CPIA_COMMAND_SetSensorFPS (OUTPUT | CPIA_MODULE_VP_CTRL | 7)
120#define CPIA_COMMAND_SetVPDefaults (OUTPUT | CPIA_MODULE_VP_CTRL | 8)
121#define CPIA_COMMAND_SetApcor (OUTPUT | CPIA_MODULE_VP_CTRL | 9)
122#define CPIA_COMMAND_SetFlickerCtrl (OUTPUT | CPIA_MODULE_VP_CTRL | 10)
123#define CPIA_COMMAND_SetVLOffset (OUTPUT | CPIA_MODULE_VP_CTRL | 11)
124#define CPIA_COMMAND_GetColourParams (INPUT | CPIA_MODULE_VP_CTRL | 16)
125#define CPIA_COMMAND_GetColourBalance (INPUT | CPIA_MODULE_VP_CTRL | 17)
126#define CPIA_COMMAND_GetExposure (INPUT | CPIA_MODULE_VP_CTRL | 18)
127#define CPIA_COMMAND_SetSensorMatrix (OUTPUT | CPIA_MODULE_VP_CTRL | 19)
128#define CPIA_COMMAND_ColourBars (OUTPUT | CPIA_MODULE_VP_CTRL | 25)
129#define CPIA_COMMAND_ReadVPRegs (INPUT | CPIA_MODULE_VP_CTRL | 30)
130#define CPIA_COMMAND_WriteVPReg (OUTPUT | CPIA_MODULE_VP_CTRL | 31)
131
132#define CPIA_COMMAND_GrabFrame (OUTPUT | CPIA_MODULE_CAPTURE | 1)
133#define CPIA_COMMAND_UploadFrame (OUTPUT | CPIA_MODULE_CAPTURE | 2)
134#define CPIA_COMMAND_SetGrabMode (OUTPUT | CPIA_MODULE_CAPTURE | 3)
135#define CPIA_COMMAND_InitStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 4)
136#define CPIA_COMMAND_FiniStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 5)
137#define CPIA_COMMAND_StartStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 6)
138#define CPIA_COMMAND_EndStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 7)
139#define CPIA_COMMAND_SetFormat (OUTPUT | CPIA_MODULE_CAPTURE | 8)
140#define CPIA_COMMAND_SetROI (OUTPUT | CPIA_MODULE_CAPTURE | 9)
141#define CPIA_COMMAND_SetCompression (OUTPUT | CPIA_MODULE_CAPTURE | 10)
142#define CPIA_COMMAND_SetCompressionTarget (OUTPUT | CPIA_MODULE_CAPTURE | 11)
143#define CPIA_COMMAND_SetYUVThresh (OUTPUT | CPIA_MODULE_CAPTURE | 12)
144#define CPIA_COMMAND_SetCompressionParams (OUTPUT | CPIA_MODULE_CAPTURE | 13)
145#define CPIA_COMMAND_DiscardFrame (OUTPUT | CPIA_MODULE_CAPTURE | 14)
146#define CPIA_COMMAND_GrabReset (OUTPUT | CPIA_MODULE_CAPTURE | 15)
147
148#define CPIA_COMMAND_OutputRS232 (OUTPUT | CPIA_MODULE_DEBUG | 1)
149#define CPIA_COMMAND_AbortProcess (OUTPUT | CPIA_MODULE_DEBUG | 4)
150#define CPIA_COMMAND_SetDramPage (OUTPUT | CPIA_MODULE_DEBUG | 5)
151#define CPIA_COMMAND_StartDramUpload (OUTPUT | CPIA_MODULE_DEBUG | 6)
152#define CPIA_COMMAND_StartDummyDtream (OUTPUT | CPIA_MODULE_DEBUG | 8)
153#define CPIA_COMMAND_AbortStream (OUTPUT | CPIA_MODULE_DEBUG | 9)
154#define CPIA_COMMAND_DownloadDRAM (OUTPUT | CPIA_MODULE_DEBUG | 10)
155#define CPIA_COMMAND_Null (OUTPUT | CPIA_MODULE_DEBUG | 11)
156
157enum {
158 FRAME_READY, /* Ready to grab into */
159 FRAME_GRABBING, /* In the process of being grabbed into */
160 FRAME_DONE, /* Finished grabbing, but not been synced yet */
161 FRAME_UNUSED, /* Unused (no MCAPTURE) */
162};
163
164#define COMMAND_NONE 0x0000
165#define COMMAND_SETCOMPRESSION 0x0001
166#define COMMAND_SETCOMPRESSIONTARGET 0x0002
167#define COMMAND_SETCOLOURPARAMS 0x0004
168#define COMMAND_SETFORMAT 0x0008
169#define COMMAND_PAUSE 0x0010
170#define COMMAND_RESUME 0x0020
171#define COMMAND_SETYUVTHRESH 0x0040
172#define COMMAND_SETECPTIMING 0x0080
173#define COMMAND_SETCOMPRESSIONPARAMS 0x0100
174#define COMMAND_SETEXPOSURE 0x0200
175#define COMMAND_SETCOLOURBALANCE 0x0400
176#define COMMAND_SETSENSORFPS 0x0800
177#define COMMAND_SETAPCOR 0x1000
178#define COMMAND_SETFLICKERCTRL 0x2000
179#define COMMAND_SETVLOFFSET 0x4000
180#define COMMAND_SETLIGHTS 0x8000
181
182#define ROUND_UP_EXP_FOR_FLICKER 15
183
184/* Constants for automatic frame rate adjustment */
185#define MAX_EXP 302
186#define MAX_EXP_102 255
187#define LOW_EXP 140
188#define VERY_LOW_EXP 70
189#define TC 94
190#define EXP_ACC_DARK 50
191#define EXP_ACC_LIGHT 90
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300192#define HIGH_COMP_102 160
193#define MAX_COMP 239
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194#define DARK_TIME 3
195#define LIGHT_TIME 3
196
197/* Maximum number of 10ms loops to wait for the stream to become ready */
198#define READY_TIMEOUT 100
199
200/* Developer's Guide Table 5 p 3-34
201 * indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/
202static u8 flicker_jumps[2][2][4] =
203{ { { 76, 38, 19, 9 }, { 92, 46, 23, 11 } },
204 { { 64, 32, 16, 8 }, { 76, 38, 19, 9} }
205};
206
207/* forward declaration of local function */
208static void reset_camera_struct(struct cam_data *cam);
209static int find_over_exposure(int brightness);
210static void set_flicker(struct cam_params *params, volatile u32 *command_flags,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300211 int on);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212
213
214/**********************************************************************
215 *
216 * Memory management
217 *
218 **********************************************************************/
219static void *rvmalloc(unsigned long size)
220{
221 void *mem;
222 unsigned long adr;
223
224 size = PAGE_ALIGN(size);
225 mem = vmalloc_32(size);
226 if (!mem)
227 return NULL;
228
229 memset(mem, 0, size); /* Clear the ram out, no junk to the user */
230 adr = (unsigned long) mem;
231 while (size > 0) {
232 SetPageReserved(vmalloc_to_page((void *)adr));
233 adr += PAGE_SIZE;
234 size -= PAGE_SIZE;
235 }
236
237 return mem;
238}
239
240static void rvfree(void *mem, unsigned long size)
241{
242 unsigned long adr;
243
244 if (!mem)
245 return;
246
247 adr = (unsigned long) mem;
248 while ((long) size > 0) {
249 ClearPageReserved(vmalloc_to_page((void *)adr));
250 adr += PAGE_SIZE;
251 size -= PAGE_SIZE;
252 }
253 vfree(mem);
254}
255
256/**********************************************************************
257 *
258 * /proc interface
259 *
260 **********************************************************************/
261#ifdef CONFIG_PROC_FS
262static struct proc_dir_entry *cpia_proc_root=NULL;
263
264static int cpia_read_proc(char *page, char **start, off_t off,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300265 int count, int *eof, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266{
267 char *out = page;
268 int len, tmp;
269 struct cam_data *cam = data;
270 char tmpstr[29];
271
272 /* IMPORTANT: This output MUST be kept under PAGE_SIZE
273 * or we need to get more sophisticated. */
274
275 out += sprintf(out, "read-only\n-----------------------\n");
276 out += sprintf(out, "V4L Driver version: %d.%d.%d\n",
277 CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER);
278 out += sprintf(out, "CPIA Version: %d.%02d (%d.%d)\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300279 cam->params.version.firmwareVersion,
280 cam->params.version.firmwareRevision,
281 cam->params.version.vcVersion,
282 cam->params.version.vcRevision);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283 out += sprintf(out, "CPIA PnP-ID: %04x:%04x:%04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300284 cam->params.pnpID.vendor, cam->params.pnpID.product,
285 cam->params.pnpID.deviceRevision);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286 out += sprintf(out, "VP-Version: %d.%d %04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300287 cam->params.vpVersion.vpVersion,
288 cam->params.vpVersion.vpRevision,
289 cam->params.vpVersion.cameraHeadID);
290
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 out += sprintf(out, "system_state: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300292 cam->params.status.systemState);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293 out += sprintf(out, "grab_state: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300294 cam->params.status.grabState);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 out += sprintf(out, "stream_state: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300296 cam->params.status.streamState);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297 out += sprintf(out, "fatal_error: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300298 cam->params.status.fatalError);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299 out += sprintf(out, "cmd_error: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300300 cam->params.status.cmdError);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301 out += sprintf(out, "debug_flags: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300302 cam->params.status.debugFlags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 out += sprintf(out, "vp_status: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300304 cam->params.status.vpStatus);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305 out += sprintf(out, "error_code: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300306 cam->params.status.errorCode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307 /* QX3 specific entries */
308 if (cam->params.qx3.qx3_detected) {
309 out += sprintf(out, "button: %4d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300310 cam->params.qx3.button);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311 out += sprintf(out, "cradled: %4d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300312 cam->params.qx3.cradled);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313 }
314 out += sprintf(out, "video_size: %s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300315 cam->params.format.videoSize == VIDEOSIZE_CIF ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316 "CIF " : "QCIF");
317 out += sprintf(out, "roi: (%3d, %3d) to (%3d, %3d)\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300318 cam->params.roi.colStart*8,
319 cam->params.roi.rowStart*4,
320 cam->params.roi.colEnd*8,
321 cam->params.roi.rowEnd*4);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 out += sprintf(out, "actual_fps: %3d\n", cam->fps);
323 out += sprintf(out, "transfer_rate: %4dkB/s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300324 cam->transfer_rate);
325
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 out += sprintf(out, "\nread-write\n");
327 out += sprintf(out, "----------------------- current min"
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300328 " max default comment\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329 out += sprintf(out, "brightness: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300330 cam->params.colourParams.brightness, 0, 100, 50);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331 if (cam->params.version.firmwareVersion == 1 &&
332 cam->params.version.firmwareRevision == 2)
333 /* 1-02 firmware limits contrast to 80 */
334 tmp = 80;
335 else
336 tmp = 96;
337
338 out += sprintf(out, "contrast: %8d %8d %8d %8d"
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300339 " steps of 8\n",
340 cam->params.colourParams.contrast, 0, tmp, 48);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 out += sprintf(out, "saturation: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300342 cam->params.colourParams.saturation, 0, 100, 50);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343 tmp = (25000+5000*cam->params.sensorFps.baserate)/
344 (1<<cam->params.sensorFps.divisor);
345 out += sprintf(out, "sensor_fps: %4d.%03d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300346 tmp/1000, tmp%1000, 3, 30, 15);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347 out += sprintf(out, "stream_start_line: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300348 2*cam->params.streamStartLine, 0,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349 cam->params.format.videoSize == VIDEOSIZE_CIF ? 288:144,
350 cam->params.format.videoSize == VIDEOSIZE_CIF ? 240:120);
351 out += sprintf(out, "sub_sample: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300352 cam->params.format.subSample == SUBSAMPLE_420 ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353 "420" : "422", "420", "422", "422");
354 out += sprintf(out, "yuv_order: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300355 cam->params.format.yuvOrder == YUVORDER_YUYV ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356 "YUYV" : "UYVY", "YUYV" , "UYVY", "YUYV");
357 out += sprintf(out, "ecp_timing: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300358 cam->params.ecpTiming ? "slow" : "normal", "slow",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359 "normal", "normal");
360
361 if (cam->params.colourBalance.balanceMode == 2) {
362 sprintf(tmpstr, "auto");
363 } else {
364 sprintf(tmpstr, "manual");
365 }
366 out += sprintf(out, "color_balance_mode: %8s %8s %8s"
367 " %8s\n", tmpstr, "manual", "auto", "auto");
368 out += sprintf(out, "red_gain: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300369 cam->params.colourBalance.redGain, 0, 212, 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 out += sprintf(out, "green_gain: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300371 cam->params.colourBalance.greenGain, 0, 212, 6);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372 out += sprintf(out, "blue_gain: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300373 cam->params.colourBalance.blueGain, 0, 212, 92);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374
375 if (cam->params.version.firmwareVersion == 1 &&
376 cam->params.version.firmwareRevision == 2)
377 /* 1-02 firmware limits gain to 2 */
378 sprintf(tmpstr, "%8d %8d %8d", 1, 2, 2);
379 else
380 sprintf(tmpstr, "%8d %8d %8d", 1, 8, 2);
381
382 if (cam->params.exposure.gainMode == 0)
383 out += sprintf(out, "max_gain: unknown %28s"
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300384 " powers of 2\n", tmpstr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 else
386 out += sprintf(out, "max_gain: %8d %28s"
387 " 1,2,4 or 8 \n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300388 1<<(cam->params.exposure.gainMode-1), tmpstr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389
390 switch(cam->params.exposure.expMode) {
391 case 1:
392 case 3:
393 sprintf(tmpstr, "manual");
394 break;
395 case 2:
396 sprintf(tmpstr, "auto");
397 break;
398 default:
399 sprintf(tmpstr, "unknown");
400 break;
401 }
402 out += sprintf(out, "exposure_mode: %8s %8s %8s"
403 " %8s\n", tmpstr, "manual", "auto", "auto");
404 out += sprintf(out, "centre_weight: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300405 (2-cam->params.exposure.centreWeight) ? "on" : "off",
406 "off", "on", "on");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407 out += sprintf(out, "gain: %8d %8d max_gain %8d 1,2,4,8 possible\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300408 1<<cam->params.exposure.gain, 1, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409 if (cam->params.version.firmwareVersion == 1 &&
410 cam->params.version.firmwareRevision == 2)
411 /* 1-02 firmware limits fineExp/2 to 127 */
412 tmp = 254;
413 else
414 tmp = 510;
415
416 out += sprintf(out, "fine_exp: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300417 cam->params.exposure.fineExp*2, 0, tmp, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 if (cam->params.version.firmwareVersion == 1 &&
419 cam->params.version.firmwareRevision == 2)
420 /* 1-02 firmware limits coarseExpHi to 0 */
421 tmp = MAX_EXP_102;
422 else
423 tmp = MAX_EXP;
424
425 out += sprintf(out, "coarse_exp: %8d %8d %8d"
426 " %8d\n", cam->params.exposure.coarseExpLo+
427 256*cam->params.exposure.coarseExpHi, 0, tmp, 185);
428 out += sprintf(out, "red_comp: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300429 cam->params.exposure.redComp, COMP_RED, 255, COMP_RED);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430 out += sprintf(out, "green1_comp: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300431 cam->params.exposure.green1Comp, COMP_GREEN1, 255,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 COMP_GREEN1);
433 out += sprintf(out, "green2_comp: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300434 cam->params.exposure.green2Comp, COMP_GREEN2, 255,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435 COMP_GREEN2);
436 out += sprintf(out, "blue_comp: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300437 cam->params.exposure.blueComp, COMP_BLUE, 255, COMP_BLUE);
438
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439 out += sprintf(out, "apcor_gain1: %#8x %#8x %#8x %#8x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300440 cam->params.apcor.gain1, 0, 0xff, 0x1c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441 out += sprintf(out, "apcor_gain2: %#8x %#8x %#8x %#8x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300442 cam->params.apcor.gain2, 0, 0xff, 0x1a);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443 out += sprintf(out, "apcor_gain4: %#8x %#8x %#8x %#8x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300444 cam->params.apcor.gain4, 0, 0xff, 0x2d);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445 out += sprintf(out, "apcor_gain8: %#8x %#8x %#8x %#8x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300446 cam->params.apcor.gain8, 0, 0xff, 0x2a);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447 out += sprintf(out, "vl_offset_gain1: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300448 cam->params.vlOffset.gain1, 0, 255, 24);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449 out += sprintf(out, "vl_offset_gain2: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300450 cam->params.vlOffset.gain2, 0, 255, 28);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451 out += sprintf(out, "vl_offset_gain4: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300452 cam->params.vlOffset.gain4, 0, 255, 30);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453 out += sprintf(out, "vl_offset_gain8: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300454 cam->params.vlOffset.gain8, 0, 255, 30);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 out += sprintf(out, "flicker_control: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300456 cam->params.flickerControl.flickerMode ? "on" : "off",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457 "off", "on", "off");
458 out += sprintf(out, "mains_frequency: %8d %8d %8d %8d"
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300459 " only 50/60\n",
460 cam->mainsFreq ? 60 : 50, 50, 60, 50);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461 if(cam->params.flickerControl.allowableOverExposure < 0)
462 out += sprintf(out, "allowable_overexposure: %4dauto auto %8d auto\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300463 -cam->params.flickerControl.allowableOverExposure,
464 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465 else
466 out += sprintf(out, "allowable_overexposure: %8d auto %8d auto\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300467 cam->params.flickerControl.allowableOverExposure,
468 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469 out += sprintf(out, "compression_mode: ");
470 switch(cam->params.compression.mode) {
471 case CPIA_COMPRESSION_NONE:
472 out += sprintf(out, "%8s", "none");
473 break;
474 case CPIA_COMPRESSION_AUTO:
475 out += sprintf(out, "%8s", "auto");
476 break;
477 case CPIA_COMPRESSION_MANUAL:
478 out += sprintf(out, "%8s", "manual");
479 break;
480 default:
481 out += sprintf(out, "%8s", "unknown");
482 break;
483 }
484 out += sprintf(out, " none,auto,manual auto\n");
485 out += sprintf(out, "decimation_enable: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300486 cam->params.compression.decimation ==
487 DECIMATION_ENAB ? "on":"off", "off", "on",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488 "off");
489 out += sprintf(out, "compression_target: %9s %9s %9s %9s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300490 cam->params.compressionTarget.frTargeting ==
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491 CPIA_COMPRESSION_TARGET_FRAMERATE ?
492 "framerate":"quality",
493 "framerate", "quality", "quality");
494 out += sprintf(out, "target_framerate: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300495 cam->params.compressionTarget.targetFR, 1, 30, 15);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496 out += sprintf(out, "target_quality: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300497 cam->params.compressionTarget.targetQ, 1, 64, 5);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498 out += sprintf(out, "y_threshold: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300499 cam->params.yuvThreshold.yThreshold, 0, 31, 6);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 out += sprintf(out, "uv_threshold: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300501 cam->params.yuvThreshold.uvThreshold, 0, 31, 6);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502 out += sprintf(out, "hysteresis: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300503 cam->params.compressionParams.hysteresis, 0, 255, 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504 out += sprintf(out, "threshold_max: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300505 cam->params.compressionParams.threshMax, 0, 255, 11);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506 out += sprintf(out, "small_step: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300507 cam->params.compressionParams.smallStep, 0, 255, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508 out += sprintf(out, "large_step: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300509 cam->params.compressionParams.largeStep, 0, 255, 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510 out += sprintf(out, "decimation_hysteresis: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300511 cam->params.compressionParams.decimationHysteresis,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512 0, 255, 2);
513 out += sprintf(out, "fr_diff_step_thresh: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300514 cam->params.compressionParams.frDiffStepThresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515 0, 255, 5);
516 out += sprintf(out, "q_diff_step_thresh: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300517 cam->params.compressionParams.qDiffStepThresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 0, 255, 3);
519 out += sprintf(out, "decimation_thresh_mod: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300520 cam->params.compressionParams.decimationThreshMod,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 0, 255, 2);
522 /* QX3 specific entries */
523 if (cam->params.qx3.qx3_detected) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300524 out += sprintf(out, "toplight: %8s %8s %8s %8s\n",
525 cam->params.qx3.toplight ? "on" : "off",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526 "off", "on", "off");
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300527 out += sprintf(out, "bottomlight: %8s %8s %8s %8s\n",
528 cam->params.qx3.bottomlight ? "on" : "off",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529 "off", "on", "off");
530 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300531
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532 len = out - page;
533 len -= off;
534 if (len < count) {
535 *eof = 1;
536 if (len <= 0) return 0;
537 } else
538 len = count;
539
540 *start = page + off;
541 return len;
542}
543
544
545static int match(char *checkstr, char **buffer, unsigned long *count,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300546 int *find_colon, int *err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547{
548 int ret, colon_found = 1;
549 int len = strlen(checkstr);
550 ret = (len <= *count && strncmp(*buffer, checkstr, len) == 0);
551 if (ret) {
552 *buffer += len;
553 *count -= len;
554 if (*find_colon) {
555 colon_found = 0;
556 while (*count && (**buffer == ' ' || **buffer == '\t' ||
557 (!colon_found && **buffer == ':'))) {
558 if (**buffer == ':')
559 colon_found = 1;
560 --*count;
561 ++*buffer;
562 }
563 if (!*count || !colon_found)
564 *err = -EINVAL;
565 *find_colon = 0;
566 }
567 }
568 return ret;
569}
570
571static unsigned long int value(char **buffer, unsigned long *count, int *err)
572{
573 char *p;
574 unsigned long int ret;
575 ret = simple_strtoul(*buffer, &p, 0);
576 if (p == *buffer)
577 *err = -EINVAL;
578 else {
579 *count -= p - *buffer;
580 *buffer = p;
581 }
582 return ret;
583}
584
585static int cpia_write_proc(struct file *file, const char __user *buf,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300586 unsigned long count, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587{
588 struct cam_data *cam = data;
589 struct cam_params new_params;
590 char *page, *buffer;
591 int retval, find_colon;
592 int size = count;
593 unsigned long val = 0;
594 u32 command_flags = 0;
595 u8 new_mains;
596
597 /*
598 * This code to copy from buf to page is shamelessly copied
599 * from the comx driver
600 */
601 if (count > PAGE_SIZE) {
602 printk(KERN_ERR "count is %lu > %d!!!\n", count, (int)PAGE_SIZE);
603 return -ENOSPC;
604 }
605
606 if (!(page = (char *)__get_free_page(GFP_KERNEL))) return -ENOMEM;
607
608 if(copy_from_user(page, buf, count))
609 {
610 retval = -EFAULT;
611 goto out;
612 }
613
614 if (page[count-1] == '\n')
615 page[count-1] = '\0';
616 else if (count < PAGE_SIZE)
617 page[count] = '\0';
618 else if (page[count]) {
619 retval = -EINVAL;
620 goto out;
621 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300622
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623 buffer = page;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300624
Ingo Molnar3593cab2006-02-07 06:49:14 -0200625 if (mutex_lock_interruptible(&cam->param_lock))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626 return -ERESTARTSYS;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300627
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628 /*
629 * Skip over leading whitespace
630 */
631 while (count && isspace(*buffer)) {
632 --count;
633 ++buffer;
634 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300635
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636 memcpy(&new_params, &cam->params, sizeof(struct cam_params));
637 new_mains = cam->mainsFreq;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300638
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639#define MATCH(x) (match(x, &buffer, &count, &find_colon, &retval))
640#define VALUE (value(&buffer,&count, &retval))
641#define FIRMWARE_VERSION(x,y) (new_params.version.firmwareVersion == (x) && \
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300642 new_params.version.firmwareRevision == (y))
643
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644 retval = 0;
645 while (count && !retval) {
646 find_colon = 1;
647 if (MATCH("brightness")) {
648 if (!retval)
649 val = VALUE;
650
651 if (!retval) {
652 if (val <= 100)
653 new_params.colourParams.brightness = val;
654 else
655 retval = -EINVAL;
656 }
657 command_flags |= COMMAND_SETCOLOURPARAMS;
658 if(new_params.flickerControl.allowableOverExposure < 0)
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300659 new_params.flickerControl.allowableOverExposure =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660 -find_over_exposure(new_params.colourParams.brightness);
661 if(new_params.flickerControl.flickerMode != 0)
662 command_flags |= COMMAND_SETFLICKERCTRL;
663
664 } else if (MATCH("contrast")) {
665 if (!retval)
666 val = VALUE;
667
668 if (!retval) {
669 if (val <= 100) {
670 /* contrast is in steps of 8, so round*/
671 val = ((val + 3) / 8) * 8;
672 /* 1-02 firmware limits contrast to 80*/
673 if (FIRMWARE_VERSION(1,2) && val > 80)
674 val = 80;
675
676 new_params.colourParams.contrast = val;
677 } else
678 retval = -EINVAL;
679 }
680 command_flags |= COMMAND_SETCOLOURPARAMS;
681 } else if (MATCH("saturation")) {
682 if (!retval)
683 val = VALUE;
684
685 if (!retval) {
686 if (val <= 100)
687 new_params.colourParams.saturation = val;
688 else
689 retval = -EINVAL;
690 }
691 command_flags |= COMMAND_SETCOLOURPARAMS;
692 } else if (MATCH("sensor_fps")) {
693 if (!retval)
694 val = VALUE;
695
696 if (!retval) {
697 /* find values so that sensorFPS is minimized,
698 * but >= val */
699 if (val > 30)
700 retval = -EINVAL;
701 else if (val > 25) {
702 new_params.sensorFps.divisor = 0;
703 new_params.sensorFps.baserate = 1;
704 } else if (val > 15) {
705 new_params.sensorFps.divisor = 0;
706 new_params.sensorFps.baserate = 0;
707 } else if (val > 12) {
708 new_params.sensorFps.divisor = 1;
709 new_params.sensorFps.baserate = 1;
710 } else if (val > 7) {
711 new_params.sensorFps.divisor = 1;
712 new_params.sensorFps.baserate = 0;
713 } else if (val > 6) {
714 new_params.sensorFps.divisor = 2;
715 new_params.sensorFps.baserate = 1;
716 } else if (val > 3) {
717 new_params.sensorFps.divisor = 2;
718 new_params.sensorFps.baserate = 0;
719 } else {
720 new_params.sensorFps.divisor = 3;
721 /* Either base rate would work here */
722 new_params.sensorFps.baserate = 1;
723 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300724 new_params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725 flicker_jumps[new_mains]
726 [new_params.sensorFps.baserate]
727 [new_params.sensorFps.divisor];
728 if (new_params.flickerControl.flickerMode)
729 command_flags |= COMMAND_SETFLICKERCTRL;
730 }
731 command_flags |= COMMAND_SETSENSORFPS;
732 cam->exposure_status = EXPOSURE_NORMAL;
733 } else if (MATCH("stream_start_line")) {
734 if (!retval)
735 val = VALUE;
736
737 if (!retval) {
738 int max_line = 288;
739
740 if (new_params.format.videoSize == VIDEOSIZE_QCIF)
741 max_line = 144;
742 if (val <= max_line)
743 new_params.streamStartLine = val/2;
744 else
745 retval = -EINVAL;
746 }
747 } else if (MATCH("sub_sample")) {
748 if (!retval && MATCH("420"))
749 new_params.format.subSample = SUBSAMPLE_420;
750 else if (!retval && MATCH("422"))
751 new_params.format.subSample = SUBSAMPLE_422;
752 else
753 retval = -EINVAL;
754
755 command_flags |= COMMAND_SETFORMAT;
756 } else if (MATCH("yuv_order")) {
757 if (!retval && MATCH("YUYV"))
758 new_params.format.yuvOrder = YUVORDER_YUYV;
759 else if (!retval && MATCH("UYVY"))
760 new_params.format.yuvOrder = YUVORDER_UYVY;
761 else
762 retval = -EINVAL;
763
764 command_flags |= COMMAND_SETFORMAT;
765 } else if (MATCH("ecp_timing")) {
766 if (!retval && MATCH("normal"))
767 new_params.ecpTiming = 0;
768 else if (!retval && MATCH("slow"))
769 new_params.ecpTiming = 1;
770 else
771 retval = -EINVAL;
772
773 command_flags |= COMMAND_SETECPTIMING;
774 } else if (MATCH("color_balance_mode")) {
775 if (!retval && MATCH("manual"))
776 new_params.colourBalance.balanceMode = 3;
777 else if (!retval && MATCH("auto"))
778 new_params.colourBalance.balanceMode = 2;
779 else
780 retval = -EINVAL;
781
782 command_flags |= COMMAND_SETCOLOURBALANCE;
783 } else if (MATCH("red_gain")) {
784 if (!retval)
785 val = VALUE;
786
787 if (!retval) {
788 if (val <= 212) {
789 new_params.colourBalance.redGain = val;
790 new_params.colourBalance.balanceMode = 1;
791 } else
792 retval = -EINVAL;
793 }
794 command_flags |= COMMAND_SETCOLOURBALANCE;
795 } else if (MATCH("green_gain")) {
796 if (!retval)
797 val = VALUE;
798
799 if (!retval) {
800 if (val <= 212) {
801 new_params.colourBalance.greenGain = val;
802 new_params.colourBalance.balanceMode = 1;
803 } else
804 retval = -EINVAL;
805 }
806 command_flags |= COMMAND_SETCOLOURBALANCE;
807 } else if (MATCH("blue_gain")) {
808 if (!retval)
809 val = VALUE;
810
811 if (!retval) {
812 if (val <= 212) {
813 new_params.colourBalance.blueGain = val;
814 new_params.colourBalance.balanceMode = 1;
815 } else
816 retval = -EINVAL;
817 }
818 command_flags |= COMMAND_SETCOLOURBALANCE;
819 } else if (MATCH("max_gain")) {
820 if (!retval)
821 val = VALUE;
822
823 if (!retval) {
824 /* 1-02 firmware limits gain to 2 */
825 if (FIRMWARE_VERSION(1,2) && val > 2)
826 val = 2;
827 switch(val) {
828 case 1:
829 new_params.exposure.gainMode = 1;
830 break;
831 case 2:
832 new_params.exposure.gainMode = 2;
833 break;
834 case 4:
835 new_params.exposure.gainMode = 3;
836 break;
837 case 8:
838 new_params.exposure.gainMode = 4;
839 break;
840 default:
841 retval = -EINVAL;
842 break;
843 }
844 }
845 command_flags |= COMMAND_SETEXPOSURE;
846 } else if (MATCH("exposure_mode")) {
847 if (!retval && MATCH("auto"))
848 new_params.exposure.expMode = 2;
849 else if (!retval && MATCH("manual")) {
850 if (new_params.exposure.expMode == 2)
851 new_params.exposure.expMode = 3;
852 if(new_params.flickerControl.flickerMode != 0)
853 command_flags |= COMMAND_SETFLICKERCTRL;
854 new_params.flickerControl.flickerMode = 0;
855 } else
856 retval = -EINVAL;
857
858 command_flags |= COMMAND_SETEXPOSURE;
859 } else if (MATCH("centre_weight")) {
860 if (!retval && MATCH("on"))
861 new_params.exposure.centreWeight = 1;
862 else if (!retval && MATCH("off"))
863 new_params.exposure.centreWeight = 2;
864 else
865 retval = -EINVAL;
866
867 command_flags |= COMMAND_SETEXPOSURE;
868 } else if (MATCH("gain")) {
869 if (!retval)
870 val = VALUE;
871
872 if (!retval) {
873 switch(val) {
874 case 1:
875 new_params.exposure.gain = 0;
876 break;
877 case 2:
878 new_params.exposure.gain = 1;
879 break;
880 case 4:
881 new_params.exposure.gain = 2;
882 break;
883 case 8:
884 new_params.exposure.gain = 3;
885 break;
886 default:
887 retval = -EINVAL;
888 break;
889 }
890 new_params.exposure.expMode = 1;
891 if(new_params.flickerControl.flickerMode != 0)
892 command_flags |= COMMAND_SETFLICKERCTRL;
893 new_params.flickerControl.flickerMode = 0;
894 command_flags |= COMMAND_SETEXPOSURE;
895 if (new_params.exposure.gain >
896 new_params.exposure.gainMode-1)
897 retval = -EINVAL;
898 }
899 } else if (MATCH("fine_exp")) {
900 if (!retval)
901 val = VALUE/2;
902
903 if (!retval) {
904 if (val < 256) {
905 /* 1-02 firmware limits fineExp/2 to 127*/
906 if (FIRMWARE_VERSION(1,2) && val > 127)
907 val = 127;
908 new_params.exposure.fineExp = val;
909 new_params.exposure.expMode = 1;
910 command_flags |= COMMAND_SETEXPOSURE;
911 if(new_params.flickerControl.flickerMode != 0)
912 command_flags |= COMMAND_SETFLICKERCTRL;
913 new_params.flickerControl.flickerMode = 0;
914 command_flags |= COMMAND_SETFLICKERCTRL;
915 } else
916 retval = -EINVAL;
917 }
918 } else if (MATCH("coarse_exp")) {
919 if (!retval)
920 val = VALUE;
921
922 if (!retval) {
923 if (val <= MAX_EXP) {
924 if (FIRMWARE_VERSION(1,2) &&
925 val > MAX_EXP_102)
926 val = MAX_EXP_102;
927 new_params.exposure.coarseExpLo =
928 val & 0xff;
929 new_params.exposure.coarseExpHi =
930 val >> 8;
931 new_params.exposure.expMode = 1;
932 command_flags |= COMMAND_SETEXPOSURE;
933 if(new_params.flickerControl.flickerMode != 0)
934 command_flags |= COMMAND_SETFLICKERCTRL;
935 new_params.flickerControl.flickerMode = 0;
936 command_flags |= COMMAND_SETFLICKERCTRL;
937 } else
938 retval = -EINVAL;
939 }
940 } else if (MATCH("red_comp")) {
941 if (!retval)
942 val = VALUE;
943
944 if (!retval) {
945 if (val >= COMP_RED && val <= 255) {
946 new_params.exposure.redComp = val;
947 new_params.exposure.compMode = 1;
948 command_flags |= COMMAND_SETEXPOSURE;
949 } else
950 retval = -EINVAL;
951 }
952 } else if (MATCH("green1_comp")) {
953 if (!retval)
954 val = VALUE;
955
956 if (!retval) {
957 if (val >= COMP_GREEN1 && val <= 255) {
958 new_params.exposure.green1Comp = val;
959 new_params.exposure.compMode = 1;
960 command_flags |= COMMAND_SETEXPOSURE;
961 } else
962 retval = -EINVAL;
963 }
964 } else if (MATCH("green2_comp")) {
965 if (!retval)
966 val = VALUE;
967
968 if (!retval) {
969 if (val >= COMP_GREEN2 && val <= 255) {
970 new_params.exposure.green2Comp = val;
971 new_params.exposure.compMode = 1;
972 command_flags |= COMMAND_SETEXPOSURE;
973 } else
974 retval = -EINVAL;
975 }
976 } else if (MATCH("blue_comp")) {
977 if (!retval)
978 val = VALUE;
979
980 if (!retval) {
981 if (val >= COMP_BLUE && val <= 255) {
982 new_params.exposure.blueComp = val;
983 new_params.exposure.compMode = 1;
984 command_flags |= COMMAND_SETEXPOSURE;
985 } else
986 retval = -EINVAL;
987 }
988 } else if (MATCH("apcor_gain1")) {
989 if (!retval)
990 val = VALUE;
991
992 if (!retval) {
993 command_flags |= COMMAND_SETAPCOR;
994 if (val <= 0xff)
995 new_params.apcor.gain1 = val;
996 else
997 retval = -EINVAL;
998 }
999 } else if (MATCH("apcor_gain2")) {
1000 if (!retval)
1001 val = VALUE;
1002
1003 if (!retval) {
1004 command_flags |= COMMAND_SETAPCOR;
1005 if (val <= 0xff)
1006 new_params.apcor.gain2 = val;
1007 else
1008 retval = -EINVAL;
1009 }
1010 } else if (MATCH("apcor_gain4")) {
1011 if (!retval)
1012 val = VALUE;
1013
1014 if (!retval) {
1015 command_flags |= COMMAND_SETAPCOR;
1016 if (val <= 0xff)
1017 new_params.apcor.gain4 = val;
1018 else
1019 retval = -EINVAL;
1020 }
1021 } else if (MATCH("apcor_gain8")) {
1022 if (!retval)
1023 val = VALUE;
1024
1025 if (!retval) {
1026 command_flags |= COMMAND_SETAPCOR;
1027 if (val <= 0xff)
1028 new_params.apcor.gain8 = val;
1029 else
1030 retval = -EINVAL;
1031 }
1032 } else if (MATCH("vl_offset_gain1")) {
1033 if (!retval)
1034 val = VALUE;
1035
1036 if (!retval) {
1037 if (val <= 0xff)
1038 new_params.vlOffset.gain1 = val;
1039 else
1040 retval = -EINVAL;
1041 }
1042 command_flags |= COMMAND_SETVLOFFSET;
1043 } else if (MATCH("vl_offset_gain2")) {
1044 if (!retval)
1045 val = VALUE;
1046
1047 if (!retval) {
1048 if (val <= 0xff)
1049 new_params.vlOffset.gain2 = val;
1050 else
1051 retval = -EINVAL;
1052 }
1053 command_flags |= COMMAND_SETVLOFFSET;
1054 } else if (MATCH("vl_offset_gain4")) {
1055 if (!retval)
1056 val = VALUE;
1057
1058 if (!retval) {
1059 if (val <= 0xff)
1060 new_params.vlOffset.gain4 = val;
1061 else
1062 retval = -EINVAL;
1063 }
1064 command_flags |= COMMAND_SETVLOFFSET;
1065 } else if (MATCH("vl_offset_gain8")) {
1066 if (!retval)
1067 val = VALUE;
1068
1069 if (!retval) {
1070 if (val <= 0xff)
1071 new_params.vlOffset.gain8 = val;
1072 else
1073 retval = -EINVAL;
1074 }
1075 command_flags |= COMMAND_SETVLOFFSET;
1076 } else if (MATCH("flicker_control")) {
1077 if (!retval && MATCH("on")) {
1078 set_flicker(&new_params, &command_flags, 1);
1079 } else if (!retval && MATCH("off")) {
1080 set_flicker(&new_params, &command_flags, 0);
1081 } else
1082 retval = -EINVAL;
1083
1084 command_flags |= COMMAND_SETFLICKERCTRL;
1085 } else if (MATCH("mains_frequency")) {
1086 if (!retval && MATCH("50")) {
1087 new_mains = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001088 new_params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089 flicker_jumps[new_mains]
1090 [new_params.sensorFps.baserate]
1091 [new_params.sensorFps.divisor];
1092 if (new_params.flickerControl.flickerMode)
1093 command_flags |= COMMAND_SETFLICKERCTRL;
1094 } else if (!retval && MATCH("60")) {
1095 new_mains = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001096 new_params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001097 flicker_jumps[new_mains]
1098 [new_params.sensorFps.baserate]
1099 [new_params.sensorFps.divisor];
1100 if (new_params.flickerControl.flickerMode)
1101 command_flags |= COMMAND_SETFLICKERCTRL;
1102 } else
1103 retval = -EINVAL;
1104 } else if (MATCH("allowable_overexposure")) {
1105 if (!retval && MATCH("auto")) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001106 new_params.flickerControl.allowableOverExposure =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001107 -find_over_exposure(new_params.colourParams.brightness);
1108 if(new_params.flickerControl.flickerMode != 0)
1109 command_flags |= COMMAND_SETFLICKERCTRL;
1110 } else {
1111 if (!retval)
1112 val = VALUE;
1113
1114 if (!retval) {
1115 if (val <= 0xff) {
1116 new_params.flickerControl.
1117 allowableOverExposure = val;
1118 if(new_params.flickerControl.flickerMode != 0)
1119 command_flags |= COMMAND_SETFLICKERCTRL;
1120 } else
1121 retval = -EINVAL;
1122 }
1123 }
1124 } else if (MATCH("compression_mode")) {
1125 if (!retval && MATCH("none"))
1126 new_params.compression.mode =
1127 CPIA_COMPRESSION_NONE;
1128 else if (!retval && MATCH("auto"))
1129 new_params.compression.mode =
1130 CPIA_COMPRESSION_AUTO;
1131 else if (!retval && MATCH("manual"))
1132 new_params.compression.mode =
1133 CPIA_COMPRESSION_MANUAL;
1134 else
1135 retval = -EINVAL;
1136
1137 command_flags |= COMMAND_SETCOMPRESSION;
1138 } else if (MATCH("decimation_enable")) {
1139 if (!retval && MATCH("off"))
1140 new_params.compression.decimation = 0;
1141 else if (!retval && MATCH("on"))
1142 new_params.compression.decimation = 1;
1143 else
1144 retval = -EINVAL;
1145
1146 command_flags |= COMMAND_SETCOMPRESSION;
1147 } else if (MATCH("compression_target")) {
1148 if (!retval && MATCH("quality"))
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001149 new_params.compressionTarget.frTargeting =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150 CPIA_COMPRESSION_TARGET_QUALITY;
1151 else if (!retval && MATCH("framerate"))
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001152 new_params.compressionTarget.frTargeting =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001153 CPIA_COMPRESSION_TARGET_FRAMERATE;
1154 else
1155 retval = -EINVAL;
1156
1157 command_flags |= COMMAND_SETCOMPRESSIONTARGET;
1158 } else if (MATCH("target_framerate")) {
1159 if (!retval)
1160 val = VALUE;
1161
1162 if (!retval) {
1163 if(val > 0 && val <= 30)
1164 new_params.compressionTarget.targetFR = val;
1165 else
1166 retval = -EINVAL;
1167 }
1168 command_flags |= COMMAND_SETCOMPRESSIONTARGET;
1169 } else if (MATCH("target_quality")) {
1170 if (!retval)
1171 val = VALUE;
1172
1173 if (!retval) {
1174 if(val > 0 && val <= 64)
1175 new_params.compressionTarget.targetQ = val;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001176 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177 retval = -EINVAL;
1178 }
1179 command_flags |= COMMAND_SETCOMPRESSIONTARGET;
1180 } else if (MATCH("y_threshold")) {
1181 if (!retval)
1182 val = VALUE;
1183
1184 if (!retval) {
1185 if (val < 32)
1186 new_params.yuvThreshold.yThreshold = val;
1187 else
1188 retval = -EINVAL;
1189 }
1190 command_flags |= COMMAND_SETYUVTHRESH;
1191 } else if (MATCH("uv_threshold")) {
1192 if (!retval)
1193 val = VALUE;
1194
1195 if (!retval) {
1196 if (val < 32)
1197 new_params.yuvThreshold.uvThreshold = val;
1198 else
1199 retval = -EINVAL;
1200 }
1201 command_flags |= COMMAND_SETYUVTHRESH;
1202 } else if (MATCH("hysteresis")) {
1203 if (!retval)
1204 val = VALUE;
1205
1206 if (!retval) {
1207 if (val <= 0xff)
1208 new_params.compressionParams.hysteresis = val;
1209 else
1210 retval = -EINVAL;
1211 }
1212 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1213 } else if (MATCH("threshold_max")) {
1214 if (!retval)
1215 val = VALUE;
1216
1217 if (!retval) {
1218 if (val <= 0xff)
1219 new_params.compressionParams.threshMax = val;
1220 else
1221 retval = -EINVAL;
1222 }
1223 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1224 } else if (MATCH("small_step")) {
1225 if (!retval)
1226 val = VALUE;
1227
1228 if (!retval) {
1229 if (val <= 0xff)
1230 new_params.compressionParams.smallStep = val;
1231 else
1232 retval = -EINVAL;
1233 }
1234 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1235 } else if (MATCH("large_step")) {
1236 if (!retval)
1237 val = VALUE;
1238
1239 if (!retval) {
1240 if (val <= 0xff)
1241 new_params.compressionParams.largeStep = val;
1242 else
1243 retval = -EINVAL;
1244 }
1245 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1246 } else if (MATCH("decimation_hysteresis")) {
1247 if (!retval)
1248 val = VALUE;
1249
1250 if (!retval) {
1251 if (val <= 0xff)
1252 new_params.compressionParams.decimationHysteresis = val;
1253 else
1254 retval = -EINVAL;
1255 }
1256 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1257 } else if (MATCH("fr_diff_step_thresh")) {
1258 if (!retval)
1259 val = VALUE;
1260
1261 if (!retval) {
1262 if (val <= 0xff)
1263 new_params.compressionParams.frDiffStepThresh = val;
1264 else
1265 retval = -EINVAL;
1266 }
1267 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1268 } else if (MATCH("q_diff_step_thresh")) {
1269 if (!retval)
1270 val = VALUE;
1271
1272 if (!retval) {
1273 if (val <= 0xff)
1274 new_params.compressionParams.qDiffStepThresh = val;
1275 else
1276 retval = -EINVAL;
1277 }
1278 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1279 } else if (MATCH("decimation_thresh_mod")) {
1280 if (!retval)
1281 val = VALUE;
1282
1283 if (!retval) {
1284 if (val <= 0xff)
1285 new_params.compressionParams.decimationThreshMod = val;
1286 else
1287 retval = -EINVAL;
1288 }
1289 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1290 } else if (MATCH("toplight")) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001291 if (!retval && MATCH("on"))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001292 new_params.qx3.toplight = 1;
1293 else if (!retval && MATCH("off"))
1294 new_params.qx3.toplight = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001295 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296 retval = -EINVAL;
1297 command_flags |= COMMAND_SETLIGHTS;
1298 } else if (MATCH("bottomlight")) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001299 if (!retval && MATCH("on"))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001300 new_params.qx3.bottomlight = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001301 else if (!retval && MATCH("off"))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302 new_params.qx3.bottomlight = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001303 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001304 retval = -EINVAL;
1305 command_flags |= COMMAND_SETLIGHTS;
1306 } else {
1307 DBG("No match found\n");
1308 retval = -EINVAL;
1309 }
1310
1311 if (!retval) {
1312 while (count && isspace(*buffer) && *buffer != '\n') {
1313 --count;
1314 ++buffer;
1315 }
1316 if (count) {
1317 if (*buffer == '\0' && count != 1)
1318 retval = -EINVAL;
1319 else if (*buffer != '\n' && *buffer != ';' &&
1320 *buffer != '\0')
1321 retval = -EINVAL;
1322 else {
1323 --count;
1324 ++buffer;
1325 }
1326 }
1327 }
1328 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001329#undef MATCH
Linus Torvalds1da177e2005-04-16 15:20:36 -07001330#undef VALUE
1331#undef FIRMWARE_VERSION
1332 if (!retval) {
1333 if (command_flags & COMMAND_SETCOLOURPARAMS) {
1334 /* Adjust cam->vp to reflect these changes */
1335 cam->vp.brightness =
1336 new_params.colourParams.brightness*65535/100;
1337 cam->vp.contrast =
1338 new_params.colourParams.contrast*65535/100;
1339 cam->vp.colour =
1340 new_params.colourParams.saturation*65535/100;
1341 }
1342 if((command_flags & COMMAND_SETEXPOSURE) &&
1343 new_params.exposure.expMode == 2)
1344 cam->exposure_status = EXPOSURE_NORMAL;
1345
1346 memcpy(&cam->params, &new_params, sizeof(struct cam_params));
1347 cam->mainsFreq = new_mains;
1348 cam->cmd_queue |= command_flags;
1349 retval = size;
1350 } else
1351 DBG("error: %d\n", retval);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001352
Ingo Molnar3593cab2006-02-07 06:49:14 -02001353 mutex_unlock(&cam->param_lock);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001354
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355out:
1356 free_page((unsigned long)page);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001357 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358}
1359
1360static void create_proc_cpia_cam(struct cam_data *cam)
1361{
1362 char name[7];
1363 struct proc_dir_entry *ent;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001364
Linus Torvalds1da177e2005-04-16 15:20:36 -07001365 if (!cpia_proc_root || !cam)
1366 return;
1367
1368 sprintf(name, "video%d", cam->vdev.minor);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001369
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370 ent = create_proc_entry(name, S_IFREG|S_IRUGO|S_IWUSR, cpia_proc_root);
1371 if (!ent)
1372 return;
1373
1374 ent->data = cam;
1375 ent->read_proc = cpia_read_proc;
1376 ent->write_proc = cpia_write_proc;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001377 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001378 size of the proc entry is 3736 bytes for the standard webcam;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001379 the extra features of the QX3 microscope add 189 bytes.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001380 (we have not yet probed the camera to see which type it is).
1381 */
1382 ent->size = 3736 + 189;
1383 cam->proc_entry = ent;
1384}
1385
1386static void destroy_proc_cpia_cam(struct cam_data *cam)
1387{
1388 char name[7];
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001389
Linus Torvalds1da177e2005-04-16 15:20:36 -07001390 if (!cam || !cam->proc_entry)
1391 return;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001392
Linus Torvalds1da177e2005-04-16 15:20:36 -07001393 sprintf(name, "video%d", cam->vdev.minor);
1394 remove_proc_entry(name, cpia_proc_root);
1395 cam->proc_entry = NULL;
1396}
1397
1398static void proc_cpia_create(void)
1399{
Al Viro66600222005-09-28 22:32:57 +01001400 cpia_proc_root = proc_mkdir("cpia", NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001401
1402 if (cpia_proc_root)
1403 cpia_proc_root->owner = THIS_MODULE;
1404 else
1405 LOG("Unable to initialise /proc/cpia\n");
1406}
1407
1408static void __exit proc_cpia_destroy(void)
1409{
1410 remove_proc_entry("cpia", NULL);
1411}
1412#endif /* CONFIG_PROC_FS */
1413
1414/* ----------------------- debug functions ---------------------- */
1415
1416#define printstatus(cam) \
1417 DBG("%02x %02x %02x %02x %02x %02x %02x %02x\n",\
1418 cam->params.status.systemState, cam->params.status.grabState, \
1419 cam->params.status.streamState, cam->params.status.fatalError, \
1420 cam->params.status.cmdError, cam->params.status.debugFlags, \
1421 cam->params.status.vpStatus, cam->params.status.errorCode);
1422
1423/* ----------------------- v4l helpers -------------------------- */
1424
1425/* supported frame palettes and depths */
1426static inline int valid_mode(u16 palette, u16 depth)
1427{
1428 if ((palette == VIDEO_PALETTE_YUV422 && depth == 16) ||
1429 (palette == VIDEO_PALETTE_YUYV && depth == 16))
1430 return 1;
1431
1432 if (colorspace_conv)
1433 return (palette == VIDEO_PALETTE_GREY && depth == 8) ||
1434 (palette == VIDEO_PALETTE_RGB555 && depth == 16) ||
1435 (palette == VIDEO_PALETTE_RGB565 && depth == 16) ||
1436 (palette == VIDEO_PALETTE_RGB24 && depth == 24) ||
1437 (palette == VIDEO_PALETTE_RGB32 && depth == 32) ||
1438 (palette == VIDEO_PALETTE_UYVY && depth == 16);
1439
1440 return 0;
1441}
1442
1443static int match_videosize( int width, int height )
1444{
1445 /* return the best match, where 'best' is as always
1446 * the largest that is not bigger than what is requested. */
1447 if (width>=352 && height>=288)
1448 return VIDEOSIZE_352_288; /* CIF */
1449
1450 if (width>=320 && height>=240)
1451 return VIDEOSIZE_320_240; /* SIF */
1452
1453 if (width>=288 && height>=216)
1454 return VIDEOSIZE_288_216;
1455
1456 if (width>=256 && height>=192)
1457 return VIDEOSIZE_256_192;
1458
1459 if (width>=224 && height>=168)
1460 return VIDEOSIZE_224_168;
1461
1462 if (width>=192 && height>=144)
1463 return VIDEOSIZE_192_144;
1464
1465 if (width>=176 && height>=144)
1466 return VIDEOSIZE_176_144; /* QCIF */
1467
1468 if (width>=160 && height>=120)
1469 return VIDEOSIZE_160_120; /* QSIF */
1470
1471 if (width>=128 && height>=96)
1472 return VIDEOSIZE_128_96;
1473
1474 if (width>=88 && height>=72)
1475 return VIDEOSIZE_88_72;
1476
1477 if (width>=64 && height>=48)
1478 return VIDEOSIZE_64_48;
1479
1480 if (width>=48 && height>=48)
1481 return VIDEOSIZE_48_48;
1482
1483 return -1;
1484}
1485
1486/* these are the capture sizes we support */
1487static void set_vw_size(struct cam_data *cam)
1488{
1489 /* the col/row/start/end values are the result of simple math */
1490 /* study the SetROI-command in cpia developers guide p 2-22 */
1491 /* streamStartLine is set to the recommended value in the cpia */
1492 /* developers guide p 3-37 */
1493 switch(cam->video_size) {
1494 case VIDEOSIZE_CIF:
1495 cam->vw.width = 352;
1496 cam->vw.height = 288;
1497 cam->params.format.videoSize=VIDEOSIZE_CIF;
1498 cam->params.roi.colStart=0;
1499 cam->params.roi.rowStart=0;
1500 cam->params.streamStartLine = 120;
1501 break;
1502 case VIDEOSIZE_SIF:
1503 cam->vw.width = 320;
1504 cam->vw.height = 240;
1505 cam->params.format.videoSize=VIDEOSIZE_CIF;
1506 cam->params.roi.colStart=2;
1507 cam->params.roi.rowStart=6;
1508 cam->params.streamStartLine = 120;
1509 break;
1510 case VIDEOSIZE_288_216:
1511 cam->vw.width = 288;
1512 cam->vw.height = 216;
1513 cam->params.format.videoSize=VIDEOSIZE_CIF;
1514 cam->params.roi.colStart=4;
1515 cam->params.roi.rowStart=9;
1516 cam->params.streamStartLine = 120;
1517 break;
1518 case VIDEOSIZE_256_192:
1519 cam->vw.width = 256;
1520 cam->vw.height = 192;
1521 cam->params.format.videoSize=VIDEOSIZE_CIF;
1522 cam->params.roi.colStart=6;
1523 cam->params.roi.rowStart=12;
1524 cam->params.streamStartLine = 120;
1525 break;
1526 case VIDEOSIZE_224_168:
1527 cam->vw.width = 224;
1528 cam->vw.height = 168;
1529 cam->params.format.videoSize=VIDEOSIZE_CIF;
1530 cam->params.roi.colStart=8;
1531 cam->params.roi.rowStart=15;
1532 cam->params.streamStartLine = 120;
1533 break;
1534 case VIDEOSIZE_192_144:
1535 cam->vw.width = 192;
1536 cam->vw.height = 144;
1537 cam->params.format.videoSize=VIDEOSIZE_CIF;
1538 cam->params.roi.colStart=10;
1539 cam->params.roi.rowStart=18;
1540 cam->params.streamStartLine = 120;
1541 break;
1542 case VIDEOSIZE_QCIF:
1543 cam->vw.width = 176;
1544 cam->vw.height = 144;
1545 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1546 cam->params.roi.colStart=0;
1547 cam->params.roi.rowStart=0;
1548 cam->params.streamStartLine = 60;
1549 break;
1550 case VIDEOSIZE_QSIF:
1551 cam->vw.width = 160;
1552 cam->vw.height = 120;
1553 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1554 cam->params.roi.colStart=1;
1555 cam->params.roi.rowStart=3;
1556 cam->params.streamStartLine = 60;
1557 break;
1558 case VIDEOSIZE_128_96:
1559 cam->vw.width = 128;
1560 cam->vw.height = 96;
1561 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1562 cam->params.roi.colStart=3;
1563 cam->params.roi.rowStart=6;
1564 cam->params.streamStartLine = 60;
1565 break;
1566 case VIDEOSIZE_88_72:
1567 cam->vw.width = 88;
1568 cam->vw.height = 72;
1569 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1570 cam->params.roi.colStart=5;
1571 cam->params.roi.rowStart=9;
1572 cam->params.streamStartLine = 60;
1573 break;
1574 case VIDEOSIZE_64_48:
1575 cam->vw.width = 64;
1576 cam->vw.height = 48;
1577 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1578 cam->params.roi.colStart=7;
1579 cam->params.roi.rowStart=12;
1580 cam->params.streamStartLine = 60;
1581 break;
1582 case VIDEOSIZE_48_48:
1583 cam->vw.width = 48;
1584 cam->vw.height = 48;
1585 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1586 cam->params.roi.colStart=8;
1587 cam->params.roi.rowStart=6;
1588 cam->params.streamStartLine = 60;
1589 break;
1590 default:
1591 LOG("bad videosize value: %d\n", cam->video_size);
1592 return;
1593 }
1594
1595 if(cam->vc.width == 0)
1596 cam->vc.width = cam->vw.width;
1597 if(cam->vc.height == 0)
1598 cam->vc.height = cam->vw.height;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001599
Linus Torvalds1da177e2005-04-16 15:20:36 -07001600 cam->params.roi.colStart += cam->vc.x >> 3;
1601 cam->params.roi.colEnd = cam->params.roi.colStart +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001602 (cam->vc.width >> 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001603 cam->params.roi.rowStart += cam->vc.y >> 2;
1604 cam->params.roi.rowEnd = cam->params.roi.rowStart +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001605 (cam->vc.height >> 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001606
1607 return;
1608}
1609
1610static int allocate_frame_buf(struct cam_data *cam)
1611{
1612 int i;
1613
1614 cam->frame_buf = rvmalloc(FRAME_NUM * CPIA_MAX_FRAME_SIZE);
1615 if (!cam->frame_buf)
1616 return -ENOBUFS;
1617
1618 for (i = 0; i < FRAME_NUM; i++)
1619 cam->frame[i].data = cam->frame_buf + i * CPIA_MAX_FRAME_SIZE;
1620
1621 return 0;
1622}
1623
1624static int free_frame_buf(struct cam_data *cam)
1625{
1626 int i;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001627
Linus Torvalds1da177e2005-04-16 15:20:36 -07001628 rvfree(cam->frame_buf, FRAME_NUM*CPIA_MAX_FRAME_SIZE);
1629 cam->frame_buf = NULL;
1630 for (i=0; i < FRAME_NUM; i++)
1631 cam->frame[i].data = NULL;
1632
1633 return 0;
1634}
1635
1636
1637static inline void free_frames(struct cpia_frame frame[FRAME_NUM])
1638{
1639 int i;
1640
1641 for (i=0; i < FRAME_NUM; i++)
1642 frame[i].state = FRAME_UNUSED;
1643 return;
1644}
1645
1646/**********************************************************************
1647 *
1648 * General functions
1649 *
1650 **********************************************************************/
1651/* send an arbitrary command to the camera */
1652static int do_command(struct cam_data *cam, u16 command, u8 a, u8 b, u8 c, u8 d)
1653{
1654 int retval, datasize;
1655 u8 cmd[8], data[8];
1656
1657 switch(command) {
1658 case CPIA_COMMAND_GetCPIAVersion:
1659 case CPIA_COMMAND_GetPnPID:
1660 case CPIA_COMMAND_GetCameraStatus:
1661 case CPIA_COMMAND_GetVPVersion:
1662 datasize=8;
1663 break;
1664 case CPIA_COMMAND_GetColourParams:
1665 case CPIA_COMMAND_GetColourBalance:
1666 case CPIA_COMMAND_GetExposure:
Ingo Molnar3593cab2006-02-07 06:49:14 -02001667 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001668 datasize=8;
1669 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001670 case CPIA_COMMAND_ReadMCPorts:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001671 case CPIA_COMMAND_ReadVCRegs:
1672 datasize = 4;
1673 break;
1674 default:
1675 datasize=0;
1676 break;
1677 }
1678
1679 cmd[0] = command>>8;
1680 cmd[1] = command&0xff;
1681 cmd[2] = a;
1682 cmd[3] = b;
1683 cmd[4] = c;
1684 cmd[5] = d;
1685 cmd[6] = datasize;
1686 cmd[7] = 0;
1687
1688 retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
1689 if (retval) {
1690 DBG("%x - failed, retval=%d\n", command, retval);
1691 if (command == CPIA_COMMAND_GetColourParams ||
1692 command == CPIA_COMMAND_GetColourBalance ||
1693 command == CPIA_COMMAND_GetExposure)
Ingo Molnar3593cab2006-02-07 06:49:14 -02001694 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001695 } else {
1696 switch(command) {
1697 case CPIA_COMMAND_GetCPIAVersion:
1698 cam->params.version.firmwareVersion = data[0];
1699 cam->params.version.firmwareRevision = data[1];
1700 cam->params.version.vcVersion = data[2];
1701 cam->params.version.vcRevision = data[3];
1702 break;
1703 case CPIA_COMMAND_GetPnPID:
1704 cam->params.pnpID.vendor = data[0]+(((u16)data[1])<<8);
1705 cam->params.pnpID.product = data[2]+(((u16)data[3])<<8);
1706 cam->params.pnpID.deviceRevision =
1707 data[4]+(((u16)data[5])<<8);
1708 break;
1709 case CPIA_COMMAND_GetCameraStatus:
1710 cam->params.status.systemState = data[0];
1711 cam->params.status.grabState = data[1];
1712 cam->params.status.streamState = data[2];
1713 cam->params.status.fatalError = data[3];
1714 cam->params.status.cmdError = data[4];
1715 cam->params.status.debugFlags = data[5];
1716 cam->params.status.vpStatus = data[6];
1717 cam->params.status.errorCode = data[7];
1718 break;
1719 case CPIA_COMMAND_GetVPVersion:
1720 cam->params.vpVersion.vpVersion = data[0];
1721 cam->params.vpVersion.vpRevision = data[1];
1722 cam->params.vpVersion.cameraHeadID =
1723 data[2]+(((u16)data[3])<<8);
1724 break;
1725 case CPIA_COMMAND_GetColourParams:
1726 cam->params.colourParams.brightness = data[0];
1727 cam->params.colourParams.contrast = data[1];
1728 cam->params.colourParams.saturation = data[2];
Ingo Molnar3593cab2006-02-07 06:49:14 -02001729 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001730 break;
1731 case CPIA_COMMAND_GetColourBalance:
1732 cam->params.colourBalance.redGain = data[0];
1733 cam->params.colourBalance.greenGain = data[1];
1734 cam->params.colourBalance.blueGain = data[2];
Ingo Molnar3593cab2006-02-07 06:49:14 -02001735 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001736 break;
1737 case CPIA_COMMAND_GetExposure:
1738 cam->params.exposure.gain = data[0];
1739 cam->params.exposure.fineExp = data[1];
1740 cam->params.exposure.coarseExpLo = data[2];
1741 cam->params.exposure.coarseExpHi = data[3];
1742 cam->params.exposure.redComp = data[4];
1743 cam->params.exposure.green1Comp = data[5];
1744 cam->params.exposure.green2Comp = data[6];
1745 cam->params.exposure.blueComp = data[7];
Ingo Molnar3593cab2006-02-07 06:49:14 -02001746 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001747 break;
1748
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001749 case CPIA_COMMAND_ReadMCPorts:
1750 if (!cam->params.qx3.qx3_detected)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001751 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001752 /* test button press */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001753 cam->params.qx3.button = ((data[1] & 0x02) == 0);
1754 if (cam->params.qx3.button) {
1755 /* button pressed - unlock the latch */
1756 do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xDF,0xDF,0);
1757 do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xFF,0xFF,0);
1758 }
1759
1760 /* test whether microscope is cradled */
1761 cam->params.qx3.cradled = ((data[2] & 0x40) == 0);
1762 break;
1763
1764 default:
1765 break;
1766 }
1767 }
1768 return retval;
1769}
1770
1771/* send a command to the camera with an additional data transaction */
1772static int do_command_extended(struct cam_data *cam, u16 command,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001773 u8 a, u8 b, u8 c, u8 d,
1774 u8 e, u8 f, u8 g, u8 h,
1775 u8 i, u8 j, u8 k, u8 l)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001776{
1777 int retval;
1778 u8 cmd[8], data[8];
1779
1780 cmd[0] = command>>8;
1781 cmd[1] = command&0xff;
1782 cmd[2] = a;
1783 cmd[3] = b;
1784 cmd[4] = c;
1785 cmd[5] = d;
1786 cmd[6] = 8;
1787 cmd[7] = 0;
1788 data[0] = e;
1789 data[1] = f;
1790 data[2] = g;
1791 data[3] = h;
1792 data[4] = i;
1793 data[5] = j;
1794 data[6] = k;
1795 data[7] = l;
1796
1797 retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
1798 if (retval)
1799 DBG("%x - failed\n", command);
1800
1801 return retval;
1802}
1803
1804/**********************************************************************
1805 *
1806 * Colorspace conversion
1807 *
1808 **********************************************************************/
1809#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16)
1810
1811static int convert420(unsigned char *yuv, unsigned char *rgb, int out_fmt,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001812 int linesize, int mmap_kludge)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001813{
1814 int y, u, v, r, g, b, y1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001815
Linus Torvalds1da177e2005-04-16 15:20:36 -07001816 /* Odd lines use the same u and v as the previous line.
1817 * Because of compression, it is necessary to get this
1818 * information from the decoded image. */
1819 switch(out_fmt) {
1820 case VIDEO_PALETTE_RGB555:
1821 y = (*yuv++ - 16) * 76310;
1822 y1 = (*yuv - 16) * 76310;
1823 r = ((*(rgb+1-linesize)) & 0x7c) << 1;
1824 g = ((*(rgb-linesize)) & 0xe0) >> 4 |
1825 ((*(rgb+1-linesize)) & 0x03) << 6;
1826 b = ((*(rgb-linesize)) & 0x1f) << 3;
1827 u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
1828 v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
1829 r = 104635 * v;
1830 g = -25690 * u - 53294 * v;
1831 b = 132278 * u;
1832 *rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3);
1833 *rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6);
1834 *rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3);
1835 *rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6);
1836 return 4;
1837 case VIDEO_PALETTE_RGB565:
1838 y = (*yuv++ - 16) * 76310;
1839 y1 = (*yuv - 16) * 76310;
1840 r = (*(rgb+1-linesize)) & 0xf8;
1841 g = ((*(rgb-linesize)) & 0xe0) >> 3 |
1842 ((*(rgb+1-linesize)) & 0x07) << 5;
1843 b = ((*(rgb-linesize)) & 0x1f) << 3;
1844 u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
1845 v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
1846 r = 104635 * v;
1847 g = -25690 * u - 53294 * v;
1848 b = 132278 * u;
1849 *rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3);
1850 *rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5);
1851 *rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3);
1852 *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5);
1853 return 4;
1854 break;
1855 case VIDEO_PALETTE_RGB24:
1856 case VIDEO_PALETTE_RGB32:
1857 y = (*yuv++ - 16) * 76310;
1858 y1 = (*yuv - 16) * 76310;
1859 if (mmap_kludge) {
1860 r = *(rgb+2-linesize);
1861 g = *(rgb+1-linesize);
1862 b = *(rgb-linesize);
1863 } else {
1864 r = *(rgb-linesize);
1865 g = *(rgb+1-linesize);
1866 b = *(rgb+2-linesize);
1867 }
1868 u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
1869 v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
1870 r = 104635 * v;
1871 g = -25690 * u + -53294 * v;
1872 b = 132278 * u;
1873 if (mmap_kludge) {
1874 *rgb++ = LIMIT(b+y);
1875 *rgb++ = LIMIT(g+y);
1876 *rgb++ = LIMIT(r+y);
1877 if(out_fmt == VIDEO_PALETTE_RGB32)
1878 rgb++;
1879 *rgb++ = LIMIT(b+y1);
1880 *rgb++ = LIMIT(g+y1);
1881 *rgb = LIMIT(r+y1);
1882 } else {
1883 *rgb++ = LIMIT(r+y);
1884 *rgb++ = LIMIT(g+y);
1885 *rgb++ = LIMIT(b+y);
1886 if(out_fmt == VIDEO_PALETTE_RGB32)
1887 rgb++;
1888 *rgb++ = LIMIT(r+y1);
1889 *rgb++ = LIMIT(g+y1);
1890 *rgb = LIMIT(b+y1);
1891 }
1892 if(out_fmt == VIDEO_PALETTE_RGB32)
1893 return 8;
1894 return 6;
1895 case VIDEO_PALETTE_YUV422:
1896 case VIDEO_PALETTE_YUYV:
1897 y = *yuv++;
1898 u = *(rgb+1-linesize);
1899 y1 = *yuv;
1900 v = *(rgb+3-linesize);
1901 *rgb++ = y;
1902 *rgb++ = u;
1903 *rgb++ = y1;
1904 *rgb = v;
1905 return 4;
1906 case VIDEO_PALETTE_UYVY:
1907 u = *(rgb-linesize);
1908 y = *yuv++;
1909 v = *(rgb+2-linesize);
1910 y1 = *yuv;
1911 *rgb++ = u;
1912 *rgb++ = y;
1913 *rgb++ = v;
1914 *rgb = y1;
1915 return 4;
1916 case VIDEO_PALETTE_GREY:
1917 *rgb++ = *yuv++;
1918 *rgb = *yuv;
1919 return 2;
1920 default:
1921 DBG("Empty: %d\n", out_fmt);
1922 return 0;
1923 }
1924}
1925
1926
1927static int yuvconvert(unsigned char *yuv, unsigned char *rgb, int out_fmt,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001928 int in_uyvy, int mmap_kludge)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001929{
1930 int y, u, v, r, g, b, y1;
1931
1932 switch(out_fmt) {
1933 case VIDEO_PALETTE_RGB555:
1934 case VIDEO_PALETTE_RGB565:
1935 case VIDEO_PALETTE_RGB24:
1936 case VIDEO_PALETTE_RGB32:
1937 if (in_uyvy) {
1938 u = *yuv++ - 128;
1939 y = (*yuv++ - 16) * 76310;
1940 v = *yuv++ - 128;
1941 y1 = (*yuv - 16) * 76310;
1942 } else {
1943 y = (*yuv++ - 16) * 76310;
1944 u = *yuv++ - 128;
1945 y1 = (*yuv++ - 16) * 76310;
1946 v = *yuv - 128;
1947 }
1948 r = 104635 * v;
1949 g = -25690 * u + -53294 * v;
1950 b = 132278 * u;
1951 break;
1952 default:
1953 y = *yuv++;
1954 u = *yuv++;
1955 y1 = *yuv++;
1956 v = *yuv;
1957 /* Just to avoid compiler warnings */
1958 r = 0;
1959 g = 0;
1960 b = 0;
1961 break;
1962 }
1963 switch(out_fmt) {
1964 case VIDEO_PALETTE_RGB555:
1965 *rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3);
1966 *rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6);
1967 *rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3);
1968 *rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6);
1969 return 4;
1970 case VIDEO_PALETTE_RGB565:
1971 *rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3);
1972 *rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5);
1973 *rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3);
1974 *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5);
1975 return 4;
1976 case VIDEO_PALETTE_RGB24:
1977 if (mmap_kludge) {
1978 *rgb++ = LIMIT(b+y);
1979 *rgb++ = LIMIT(g+y);
1980 *rgb++ = LIMIT(r+y);
1981 *rgb++ = LIMIT(b+y1);
1982 *rgb++ = LIMIT(g+y1);
1983 *rgb = LIMIT(r+y1);
1984 } else {
1985 *rgb++ = LIMIT(r+y);
1986 *rgb++ = LIMIT(g+y);
1987 *rgb++ = LIMIT(b+y);
1988 *rgb++ = LIMIT(r+y1);
1989 *rgb++ = LIMIT(g+y1);
1990 *rgb = LIMIT(b+y1);
1991 }
1992 return 6;
1993 case VIDEO_PALETTE_RGB32:
1994 if (mmap_kludge) {
1995 *rgb++ = LIMIT(b+y);
1996 *rgb++ = LIMIT(g+y);
1997 *rgb++ = LIMIT(r+y);
1998 rgb++;
1999 *rgb++ = LIMIT(b+y1);
2000 *rgb++ = LIMIT(g+y1);
2001 *rgb = LIMIT(r+y1);
2002 } else {
2003 *rgb++ = LIMIT(r+y);
2004 *rgb++ = LIMIT(g+y);
2005 *rgb++ = LIMIT(b+y);
2006 rgb++;
2007 *rgb++ = LIMIT(r+y1);
2008 *rgb++ = LIMIT(g+y1);
2009 *rgb = LIMIT(b+y1);
2010 }
2011 return 8;
2012 case VIDEO_PALETTE_GREY:
2013 *rgb++ = y;
2014 *rgb = y1;
2015 return 2;
2016 case VIDEO_PALETTE_YUV422:
2017 case VIDEO_PALETTE_YUYV:
2018 *rgb++ = y;
2019 *rgb++ = u;
2020 *rgb++ = y1;
2021 *rgb = v;
2022 return 4;
2023 case VIDEO_PALETTE_UYVY:
2024 *rgb++ = u;
2025 *rgb++ = y;
2026 *rgb++ = v;
2027 *rgb = y1;
2028 return 4;
2029 default:
2030 DBG("Empty: %d\n", out_fmt);
2031 return 0;
2032 }
2033}
2034
2035static int skipcount(int count, int fmt)
2036{
2037 switch(fmt) {
2038 case VIDEO_PALETTE_GREY:
2039 return count;
2040 case VIDEO_PALETTE_RGB555:
2041 case VIDEO_PALETTE_RGB565:
2042 case VIDEO_PALETTE_YUV422:
2043 case VIDEO_PALETTE_YUYV:
2044 case VIDEO_PALETTE_UYVY:
2045 return 2*count;
2046 case VIDEO_PALETTE_RGB24:
2047 return 3*count;
2048 case VIDEO_PALETTE_RGB32:
2049 return 4*count;
2050 default:
2051 return 0;
2052 }
2053}
2054
2055static int parse_picture(struct cam_data *cam, int size)
2056{
2057 u8 *obuf, *ibuf, *end_obuf;
2058 int ll, in_uyvy, compressed, decimation, even_line, origsize, out_fmt;
2059 int rows, cols, linesize, subsample_422;
2060
2061 /* make sure params don't change while we are decoding */
Ingo Molnar3593cab2006-02-07 06:49:14 -02002062 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002063
2064 obuf = cam->decompressed_frame.data;
2065 end_obuf = obuf+CPIA_MAX_FRAME_SIZE;
2066 ibuf = cam->raw_image;
2067 origsize = size;
2068 out_fmt = cam->vp.palette;
2069
2070 if ((ibuf[0] != MAGIC_0) || (ibuf[1] != MAGIC_1)) {
2071 LOG("header not found\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02002072 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002073 return -1;
2074 }
2075
2076 if ((ibuf[16] != VIDEOSIZE_QCIF) && (ibuf[16] != VIDEOSIZE_CIF)) {
2077 LOG("wrong video size\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02002078 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002079 return -1;
2080 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002081
Linus Torvalds1da177e2005-04-16 15:20:36 -07002082 if (ibuf[17] != SUBSAMPLE_420 && ibuf[17] != SUBSAMPLE_422) {
2083 LOG("illegal subtype %d\n",ibuf[17]);
Ingo Molnar3593cab2006-02-07 06:49:14 -02002084 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002085 return -1;
2086 }
2087 subsample_422 = ibuf[17] == SUBSAMPLE_422;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002088
Linus Torvalds1da177e2005-04-16 15:20:36 -07002089 if (ibuf[18] != YUVORDER_YUYV && ibuf[18] != YUVORDER_UYVY) {
2090 LOG("illegal yuvorder %d\n",ibuf[18]);
Ingo Molnar3593cab2006-02-07 06:49:14 -02002091 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002092 return -1;
2093 }
2094 in_uyvy = ibuf[18] == YUVORDER_UYVY;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002095
Linus Torvalds1da177e2005-04-16 15:20:36 -07002096 if ((ibuf[24] != cam->params.roi.colStart) ||
2097 (ibuf[25] != cam->params.roi.colEnd) ||
2098 (ibuf[26] != cam->params.roi.rowStart) ||
2099 (ibuf[27] != cam->params.roi.rowEnd)) {
2100 LOG("ROI mismatch\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02002101 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002102 return -1;
2103 }
2104 cols = 8*(ibuf[25] - ibuf[24]);
2105 rows = 4*(ibuf[27] - ibuf[26]);
2106
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002107
Linus Torvalds1da177e2005-04-16 15:20:36 -07002108 if ((ibuf[28] != NOT_COMPRESSED) && (ibuf[28] != COMPRESSED)) {
2109 LOG("illegal compression %d\n",ibuf[28]);
Ingo Molnar3593cab2006-02-07 06:49:14 -02002110 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002111 return -1;
2112 }
2113 compressed = (ibuf[28] == COMPRESSED);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002114
Linus Torvalds1da177e2005-04-16 15:20:36 -07002115 if (ibuf[29] != NO_DECIMATION && ibuf[29] != DECIMATION_ENAB) {
2116 LOG("illegal decimation %d\n",ibuf[29]);
Ingo Molnar3593cab2006-02-07 06:49:14 -02002117 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002118 return -1;
2119 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002120 decimation = (ibuf[29] == DECIMATION_ENAB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002121
2122 cam->params.yuvThreshold.yThreshold = ibuf[30];
2123 cam->params.yuvThreshold.uvThreshold = ibuf[31];
2124 cam->params.status.systemState = ibuf[32];
2125 cam->params.status.grabState = ibuf[33];
2126 cam->params.status.streamState = ibuf[34];
2127 cam->params.status.fatalError = ibuf[35];
2128 cam->params.status.cmdError = ibuf[36];
2129 cam->params.status.debugFlags = ibuf[37];
2130 cam->params.status.vpStatus = ibuf[38];
2131 cam->params.status.errorCode = ibuf[39];
2132 cam->fps = ibuf[41];
Ingo Molnar3593cab2006-02-07 06:49:14 -02002133 mutex_unlock(&cam->param_lock);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002134
Linus Torvalds1da177e2005-04-16 15:20:36 -07002135 linesize = skipcount(cols, out_fmt);
2136 ibuf += FRAME_HEADER_SIZE;
2137 size -= FRAME_HEADER_SIZE;
2138 ll = ibuf[0] | (ibuf[1] << 8);
2139 ibuf += 2;
2140 even_line = 1;
2141
2142 while (size > 0) {
2143 size -= (ll+2);
2144 if (size < 0) {
2145 LOG("Insufficient data in buffer\n");
2146 return -1;
2147 }
2148
2149 while (ll > 1) {
2150 if (!compressed || (compressed && !(*ibuf & 1))) {
2151 if(subsample_422 || even_line) {
2152 obuf += yuvconvert(ibuf, obuf, out_fmt,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002153 in_uyvy, cam->mmap_kludge);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002154 ibuf += 4;
2155 ll -= 4;
2156 } else {
2157 /* SUBSAMPLE_420 on an odd line */
2158 obuf += convert420(ibuf, obuf,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002159 out_fmt, linesize,
2160 cam->mmap_kludge);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002161 ibuf += 2;
2162 ll -= 2;
2163 }
2164 } else {
2165 /*skip compressed interval from previous frame*/
2166 obuf += skipcount(*ibuf >> 1, out_fmt);
2167 if (obuf > end_obuf) {
2168 LOG("Insufficient buffer size\n");
2169 return -1;
2170 }
2171 ++ibuf;
2172 ll--;
2173 }
2174 }
2175 if (ll == 1) {
2176 if (*ibuf != EOL) {
2177 DBG("EOL not found giving up after %d/%d"
2178 " bytes\n", origsize-size, origsize);
2179 return -1;
2180 }
2181
2182 ++ibuf; /* skip over EOL */
2183
2184 if ((size > 3) && (ibuf[0] == EOI) && (ibuf[1] == EOI) &&
2185 (ibuf[2] == EOI) && (ibuf[3] == EOI)) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002186 size -= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002187 break;
2188 }
2189
2190 if(decimation) {
2191 /* skip the odd lines for now */
2192 obuf += linesize;
2193 }
2194
2195 if (size > 1) {
2196 ll = ibuf[0] | (ibuf[1] << 8);
2197 ibuf += 2; /* skip over line length */
2198 }
2199 if(!decimation)
2200 even_line = !even_line;
2201 } else {
2202 LOG("line length was not 1 but %d after %d/%d bytes\n",
2203 ll, origsize-size, origsize);
2204 return -1;
2205 }
2206 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002207
Linus Torvalds1da177e2005-04-16 15:20:36 -07002208 if(decimation) {
2209 /* interpolate odd rows */
2210 int i, j;
2211 u8 *prev, *next;
2212 prev = cam->decompressed_frame.data;
2213 obuf = prev+linesize;
2214 next = obuf+linesize;
2215 for(i=1; i<rows-1; i+=2) {
2216 for(j=0; j<linesize; ++j) {
2217 *obuf++ = ((int)*prev++ + *next++) / 2;
2218 }
2219 prev += linesize;
2220 obuf += linesize;
2221 next += linesize;
2222 }
2223 /* last row is odd, just copy previous row */
2224 memcpy(obuf, prev, linesize);
2225 }
2226
2227 cam->decompressed_frame.count = obuf-cam->decompressed_frame.data;
2228
2229 return cam->decompressed_frame.count;
2230}
2231
2232/* InitStreamCap wrapper to select correct start line */
2233static inline int init_stream_cap(struct cam_data *cam)
2234{
2235 return do_command(cam, CPIA_COMMAND_InitStreamCap,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002236 0, cam->params.streamStartLine, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002237}
2238
2239
2240/* find_over_exposure
2241 * Finds a suitable value of OverExposure for use with SetFlickerCtrl
2242 * Some calculation is required because this value changes with the brightness
2243 * set with SetColourParameters
2244 *
2245 * Parameters: Brightness - last brightness value set with SetColourParameters
2246 *
2247 * Returns: OverExposure value to use with SetFlickerCtrl
2248 */
2249#define FLICKER_MAX_EXPOSURE 250
2250#define FLICKER_ALLOWABLE_OVER_EXPOSURE 146
2251#define FLICKER_BRIGHTNESS_CONSTANT 59
2252static int find_over_exposure(int brightness)
2253{
2254 int MaxAllowableOverExposure, OverExposure;
2255
2256 MaxAllowableOverExposure = FLICKER_MAX_EXPOSURE - brightness -
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002257 FLICKER_BRIGHTNESS_CONSTANT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002258
2259 if (MaxAllowableOverExposure < FLICKER_ALLOWABLE_OVER_EXPOSURE) {
2260 OverExposure = MaxAllowableOverExposure;
2261 } else {
2262 OverExposure = FLICKER_ALLOWABLE_OVER_EXPOSURE;
2263 }
2264
2265 return OverExposure;
2266}
2267#undef FLICKER_MAX_EXPOSURE
2268#undef FLICKER_ALLOWABLE_OVER_EXPOSURE
2269#undef FLICKER_BRIGHTNESS_CONSTANT
2270
2271/* update various camera modes and settings */
2272static void dispatch_commands(struct cam_data *cam)
2273{
Ingo Molnar3593cab2006-02-07 06:49:14 -02002274 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002275 if (cam->cmd_queue==COMMAND_NONE) {
Ingo Molnar3593cab2006-02-07 06:49:14 -02002276 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002277 return;
2278 }
2279 DEB_BYTE(cam->cmd_queue);
2280 DEB_BYTE(cam->cmd_queue>>8);
2281 if (cam->cmd_queue & COMMAND_SETFORMAT) {
2282 do_command(cam, CPIA_COMMAND_SetFormat,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002283 cam->params.format.videoSize,
2284 cam->params.format.subSample,
2285 cam->params.format.yuvOrder, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002286 do_command(cam, CPIA_COMMAND_SetROI,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002287 cam->params.roi.colStart, cam->params.roi.colEnd,
2288 cam->params.roi.rowStart, cam->params.roi.rowEnd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002289 cam->first_frame = 1;
2290 }
2291
2292 if (cam->cmd_queue & COMMAND_SETCOLOURPARAMS)
2293 do_command(cam, CPIA_COMMAND_SetColourParams,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002294 cam->params.colourParams.brightness,
2295 cam->params.colourParams.contrast,
2296 cam->params.colourParams.saturation, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002297
2298 if (cam->cmd_queue & COMMAND_SETAPCOR)
2299 do_command(cam, CPIA_COMMAND_SetApcor,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002300 cam->params.apcor.gain1,
2301 cam->params.apcor.gain2,
2302 cam->params.apcor.gain4,
2303 cam->params.apcor.gain8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002304
2305 if (cam->cmd_queue & COMMAND_SETVLOFFSET)
2306 do_command(cam, CPIA_COMMAND_SetVLOffset,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002307 cam->params.vlOffset.gain1,
2308 cam->params.vlOffset.gain2,
2309 cam->params.vlOffset.gain4,
2310 cam->params.vlOffset.gain8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002311
2312 if (cam->cmd_queue & COMMAND_SETEXPOSURE) {
2313 do_command_extended(cam, CPIA_COMMAND_SetExposure,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002314 cam->params.exposure.gainMode,
2315 1,
2316 cam->params.exposure.compMode,
2317 cam->params.exposure.centreWeight,
2318 cam->params.exposure.gain,
2319 cam->params.exposure.fineExp,
2320 cam->params.exposure.coarseExpLo,
2321 cam->params.exposure.coarseExpHi,
2322 cam->params.exposure.redComp,
2323 cam->params.exposure.green1Comp,
2324 cam->params.exposure.green2Comp,
2325 cam->params.exposure.blueComp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002326 if(cam->params.exposure.expMode != 1) {
2327 do_command_extended(cam, CPIA_COMMAND_SetExposure,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002328 0,
2329 cam->params.exposure.expMode,
2330 0, 0,
2331 cam->params.exposure.gain,
2332 cam->params.exposure.fineExp,
2333 cam->params.exposure.coarseExpLo,
2334 cam->params.exposure.coarseExpHi,
2335 0, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002336 }
2337 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002338
Linus Torvalds1da177e2005-04-16 15:20:36 -07002339 if (cam->cmd_queue & COMMAND_SETCOLOURBALANCE) {
2340 if (cam->params.colourBalance.balanceMode == 1) {
2341 do_command(cam, CPIA_COMMAND_SetColourBalance,
2342 1,
2343 cam->params.colourBalance.redGain,
2344 cam->params.colourBalance.greenGain,
2345 cam->params.colourBalance.blueGain);
2346 do_command(cam, CPIA_COMMAND_SetColourBalance,
2347 3, 0, 0, 0);
2348 }
2349 if (cam->params.colourBalance.balanceMode == 2) {
2350 do_command(cam, CPIA_COMMAND_SetColourBalance,
2351 2, 0, 0, 0);
2352 }
2353 if (cam->params.colourBalance.balanceMode == 3) {
2354 do_command(cam, CPIA_COMMAND_SetColourBalance,
2355 3, 0, 0, 0);
2356 }
2357 }
2358
2359 if (cam->cmd_queue & COMMAND_SETCOMPRESSIONTARGET)
2360 do_command(cam, CPIA_COMMAND_SetCompressionTarget,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002361 cam->params.compressionTarget.frTargeting,
2362 cam->params.compressionTarget.targetFR,
2363 cam->params.compressionTarget.targetQ, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002364
2365 if (cam->cmd_queue & COMMAND_SETYUVTHRESH)
2366 do_command(cam, CPIA_COMMAND_SetYUVThresh,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002367 cam->params.yuvThreshold.yThreshold,
2368 cam->params.yuvThreshold.uvThreshold, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002369
2370 if (cam->cmd_queue & COMMAND_SETCOMPRESSIONPARAMS)
2371 do_command_extended(cam, CPIA_COMMAND_SetCompressionParams,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002372 0, 0, 0, 0,
2373 cam->params.compressionParams.hysteresis,
2374 cam->params.compressionParams.threshMax,
2375 cam->params.compressionParams.smallStep,
2376 cam->params.compressionParams.largeStep,
2377 cam->params.compressionParams.decimationHysteresis,
2378 cam->params.compressionParams.frDiffStepThresh,
2379 cam->params.compressionParams.qDiffStepThresh,
2380 cam->params.compressionParams.decimationThreshMod);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002381
2382 if (cam->cmd_queue & COMMAND_SETCOMPRESSION)
2383 do_command(cam, CPIA_COMMAND_SetCompression,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002384 cam->params.compression.mode,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002385 cam->params.compression.decimation, 0, 0);
2386
2387 if (cam->cmd_queue & COMMAND_SETSENSORFPS)
2388 do_command(cam, CPIA_COMMAND_SetSensorFPS,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002389 cam->params.sensorFps.divisor,
2390 cam->params.sensorFps.baserate, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002391
2392 if (cam->cmd_queue & COMMAND_SETFLICKERCTRL)
2393 do_command(cam, CPIA_COMMAND_SetFlickerCtrl,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002394 cam->params.flickerControl.flickerMode,
2395 cam->params.flickerControl.coarseJump,
2396 abs(cam->params.flickerControl.allowableOverExposure),
2397 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002398
2399 if (cam->cmd_queue & COMMAND_SETECPTIMING)
2400 do_command(cam, CPIA_COMMAND_SetECPTiming,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002401 cam->params.ecpTiming, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002402
2403 if (cam->cmd_queue & COMMAND_PAUSE)
2404 do_command(cam, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0);
2405
2406 if (cam->cmd_queue & COMMAND_RESUME)
2407 init_stream_cap(cam);
2408
2409 if (cam->cmd_queue & COMMAND_SETLIGHTS && cam->params.qx3.qx3_detected)
2410 {
2411 int p1 = (cam->params.qx3.bottomlight == 0) << 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002412 int p2 = (cam->params.qx3.toplight == 0) << 3;
2413 do_command(cam, CPIA_COMMAND_WriteVCReg, 0x90, 0x8F, 0x50, 0);
2414 do_command(cam, CPIA_COMMAND_WriteMCPort, 2, 0, (p1|p2|0xE0), 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002415 }
2416
2417 cam->cmd_queue = COMMAND_NONE;
Ingo Molnar3593cab2006-02-07 06:49:14 -02002418 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002419 return;
2420}
2421
2422
2423
2424static void set_flicker(struct cam_params *params, volatile u32 *command_flags,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002425 int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002426{
2427 /* Everything in here is from the Windows driver */
2428#define FIRMWARE_VERSION(x,y) (params->version.firmwareVersion == (x) && \
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002429 params->version.firmwareRevision == (y))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002430/* define for compgain calculation */
2431#if 0
2432#define COMPGAIN(base, curexp, newexp) \
2433 (u8) ((((float) base - 128.0) * ((float) curexp / (float) newexp)) + 128.5)
2434#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
2435 (u16)((float)curexp * (float)(u8)(curcomp + 128) / (float)(u8)(basecomp - 128))
2436#else
2437 /* equivalent functions without floating point math */
2438#define COMPGAIN(base, curexp, newexp) \
2439 (u8)(128 + (((u32)(2*(base-128)*curexp + newexp)) / (2* newexp)) )
2440#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
2441 (u16)(((u32)(curexp * (u8)(curcomp + 128)) / (u8)(basecomp - 128)))
2442#endif
2443
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002444
Linus Torvalds1da177e2005-04-16 15:20:36 -07002445 int currentexp = params->exposure.coarseExpLo +
2446 params->exposure.coarseExpHi*256;
2447 int startexp;
2448 if (on) {
2449 int cj = params->flickerControl.coarseJump;
2450 params->flickerControl.flickerMode = 1;
2451 params->flickerControl.disabled = 0;
2452 if(params->exposure.expMode != 2)
2453 *command_flags |= COMMAND_SETEXPOSURE;
2454 params->exposure.expMode = 2;
2455 currentexp = currentexp << params->exposure.gain;
2456 params->exposure.gain = 0;
2457 /* round down current exposure to nearest value */
2458 startexp = (currentexp + ROUND_UP_EXP_FOR_FLICKER) / cj;
2459 if(startexp < 1)
2460 startexp = 1;
2461 startexp = (startexp * cj) - 1;
2462 if(FIRMWARE_VERSION(1,2))
2463 while(startexp > MAX_EXP_102)
2464 startexp -= cj;
2465 else
2466 while(startexp > MAX_EXP)
2467 startexp -= cj;
2468 params->exposure.coarseExpLo = startexp & 0xff;
2469 params->exposure.coarseExpHi = startexp >> 8;
2470 if (currentexp > startexp) {
2471 if (currentexp > (2 * startexp))
2472 currentexp = 2 * startexp;
2473 params->exposure.redComp = COMPGAIN (COMP_RED, currentexp, startexp);
2474 params->exposure.green1Comp = COMPGAIN (COMP_GREEN1, currentexp, startexp);
2475 params->exposure.green2Comp = COMPGAIN (COMP_GREEN2, currentexp, startexp);
2476 params->exposure.blueComp = COMPGAIN (COMP_BLUE, currentexp, startexp);
2477 } else {
2478 params->exposure.redComp = COMP_RED;
2479 params->exposure.green1Comp = COMP_GREEN1;
2480 params->exposure.green2Comp = COMP_GREEN2;
2481 params->exposure.blueComp = COMP_BLUE;
2482 }
2483 if(FIRMWARE_VERSION(1,2))
2484 params->exposure.compMode = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002485 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07002486 params->exposure.compMode = 1;
2487
2488 params->apcor.gain1 = 0x18;
2489 params->apcor.gain2 = 0x18;
2490 params->apcor.gain4 = 0x16;
2491 params->apcor.gain8 = 0x14;
2492 *command_flags |= COMMAND_SETAPCOR;
2493 } else {
2494 params->flickerControl.flickerMode = 0;
2495 params->flickerControl.disabled = 1;
2496 /* Coarse = average of equivalent coarse for each comp channel */
2497 startexp = EXP_FROM_COMP(COMP_RED, params->exposure.redComp, currentexp);
2498 startexp += EXP_FROM_COMP(COMP_GREEN1, params->exposure.green1Comp, currentexp);
2499 startexp += EXP_FROM_COMP(COMP_GREEN2, params->exposure.green2Comp, currentexp);
2500 startexp += EXP_FROM_COMP(COMP_BLUE, params->exposure.blueComp, currentexp);
2501 startexp = startexp >> 2;
2502 while(startexp > MAX_EXP &&
2503 params->exposure.gain < params->exposure.gainMode-1) {
2504 startexp = startexp >> 1;
2505 ++params->exposure.gain;
2506 }
2507 if(FIRMWARE_VERSION(1,2) && startexp > MAX_EXP_102)
2508 startexp = MAX_EXP_102;
2509 if(startexp > MAX_EXP)
2510 startexp = MAX_EXP;
2511 params->exposure.coarseExpLo = startexp&0xff;
2512 params->exposure.coarseExpHi = startexp >> 8;
2513 params->exposure.redComp = COMP_RED;
2514 params->exposure.green1Comp = COMP_GREEN1;
2515 params->exposure.green2Comp = COMP_GREEN2;
2516 params->exposure.blueComp = COMP_BLUE;
2517 params->exposure.compMode = 1;
2518 *command_flags |= COMMAND_SETEXPOSURE;
2519 params->apcor.gain1 = 0x18;
2520 params->apcor.gain2 = 0x16;
2521 params->apcor.gain4 = 0x24;
2522 params->apcor.gain8 = 0x34;
2523 *command_flags |= COMMAND_SETAPCOR;
2524 }
2525 params->vlOffset.gain1 = 20;
2526 params->vlOffset.gain2 = 24;
2527 params->vlOffset.gain4 = 26;
2528 params->vlOffset.gain8 = 26;
2529 *command_flags |= COMMAND_SETVLOFFSET;
2530#undef FIRMWARE_VERSION
2531#undef EXP_FROM_COMP
2532#undef COMPGAIN
2533}
2534
2535#define FIRMWARE_VERSION(x,y) (cam->params.version.firmwareVersion == (x) && \
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002536 cam->params.version.firmwareRevision == (y))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002537/* monitor the exposure and adjust the sensor frame rate if needed */
2538static void monitor_exposure(struct cam_data *cam)
2539{
2540 u8 exp_acc, bcomp, gain, coarseL, cmd[8], data[8];
2541 int retval, light_exp, dark_exp, very_dark_exp;
2542 int old_exposure, new_exposure, framerate;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002543
Linus Torvalds1da177e2005-04-16 15:20:36 -07002544 /* get necessary stats and register settings from camera */
2545 /* do_command can't handle this, so do it ourselves */
2546 cmd[0] = CPIA_COMMAND_ReadVPRegs>>8;
2547 cmd[1] = CPIA_COMMAND_ReadVPRegs&0xff;
2548 cmd[2] = 30;
2549 cmd[3] = 4;
2550 cmd[4] = 9;
2551 cmd[5] = 8;
2552 cmd[6] = 8;
2553 cmd[7] = 0;
2554 retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
2555 if (retval) {
2556 LOG("ReadVPRegs(30,4,9,8) - failed, retval=%d\n",
2557 retval);
2558 return;
2559 }
2560 exp_acc = data[0];
2561 bcomp = data[1];
2562 gain = data[2];
2563 coarseL = data[3];
2564
Ingo Molnar3593cab2006-02-07 06:49:14 -02002565 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002566 light_exp = cam->params.colourParams.brightness +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002567 TC - 50 + EXP_ACC_LIGHT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002568 if(light_exp > 255)
2569 light_exp = 255;
2570 dark_exp = cam->params.colourParams.brightness +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002571 TC - 50 - EXP_ACC_DARK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002572 if(dark_exp < 0)
2573 dark_exp = 0;
2574 very_dark_exp = dark_exp/2;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002575
Linus Torvalds1da177e2005-04-16 15:20:36 -07002576 old_exposure = cam->params.exposure.coarseExpHi * 256 +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002577 cam->params.exposure.coarseExpLo;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002578
2579 if(!cam->params.flickerControl.disabled) {
2580 /* Flicker control on */
2581 int max_comp = FIRMWARE_VERSION(1,2) ? MAX_COMP : HIGH_COMP_102;
2582 bcomp += 128; /* decode */
2583 if(bcomp >= max_comp && exp_acc < dark_exp) {
2584 /* dark */
2585 if(exp_acc < very_dark_exp) {
2586 /* very dark */
2587 if(cam->exposure_status == EXPOSURE_VERY_DARK)
2588 ++cam->exposure_count;
2589 else {
2590 cam->exposure_status = EXPOSURE_VERY_DARK;
2591 cam->exposure_count = 1;
2592 }
2593 } else {
2594 /* just dark */
2595 if(cam->exposure_status == EXPOSURE_DARK)
2596 ++cam->exposure_count;
2597 else {
2598 cam->exposure_status = EXPOSURE_DARK;
2599 cam->exposure_count = 1;
2600 }
2601 }
2602 } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) {
2603 /* light */
2604 if(old_exposure <= VERY_LOW_EXP) {
2605 /* very light */
2606 if(cam->exposure_status == EXPOSURE_VERY_LIGHT)
2607 ++cam->exposure_count;
2608 else {
2609 cam->exposure_status = EXPOSURE_VERY_LIGHT;
2610 cam->exposure_count = 1;
2611 }
2612 } else {
2613 /* just light */
2614 if(cam->exposure_status == EXPOSURE_LIGHT)
2615 ++cam->exposure_count;
2616 else {
2617 cam->exposure_status = EXPOSURE_LIGHT;
2618 cam->exposure_count = 1;
2619 }
2620 }
2621 } else {
2622 /* not dark or light */
2623 cam->exposure_status = EXPOSURE_NORMAL;
2624 }
2625 } else {
2626 /* Flicker control off */
2627 if(old_exposure >= MAX_EXP && exp_acc < dark_exp) {
2628 /* dark */
2629 if(exp_acc < very_dark_exp) {
2630 /* very dark */
2631 if(cam->exposure_status == EXPOSURE_VERY_DARK)
2632 ++cam->exposure_count;
2633 else {
2634 cam->exposure_status = EXPOSURE_VERY_DARK;
2635 cam->exposure_count = 1;
2636 }
2637 } else {
2638 /* just dark */
2639 if(cam->exposure_status == EXPOSURE_DARK)
2640 ++cam->exposure_count;
2641 else {
2642 cam->exposure_status = EXPOSURE_DARK;
2643 cam->exposure_count = 1;
2644 }
2645 }
2646 } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) {
2647 /* light */
2648 if(old_exposure <= VERY_LOW_EXP) {
2649 /* very light */
2650 if(cam->exposure_status == EXPOSURE_VERY_LIGHT)
2651 ++cam->exposure_count;
2652 else {
2653 cam->exposure_status = EXPOSURE_VERY_LIGHT;
2654 cam->exposure_count = 1;
2655 }
2656 } else {
2657 /* just light */
2658 if(cam->exposure_status == EXPOSURE_LIGHT)
2659 ++cam->exposure_count;
2660 else {
2661 cam->exposure_status = EXPOSURE_LIGHT;
2662 cam->exposure_count = 1;
2663 }
2664 }
2665 } else {
2666 /* not dark or light */
2667 cam->exposure_status = EXPOSURE_NORMAL;
2668 }
2669 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002670
Linus Torvalds1da177e2005-04-16 15:20:36 -07002671 framerate = cam->fps;
2672 if(framerate > 30 || framerate < 1)
2673 framerate = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002674
Linus Torvalds1da177e2005-04-16 15:20:36 -07002675 if(!cam->params.flickerControl.disabled) {
2676 /* Flicker control on */
2677 if((cam->exposure_status == EXPOSURE_VERY_DARK ||
2678 cam->exposure_status == EXPOSURE_DARK) &&
2679 cam->exposure_count >= DARK_TIME*framerate &&
2680 cam->params.sensorFps.divisor < 3) {
2681
2682 /* dark for too long */
2683 ++cam->params.sensorFps.divisor;
2684 cam->cmd_queue |= COMMAND_SETSENSORFPS;
2685
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002686 cam->params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07002687 flicker_jumps[cam->mainsFreq]
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002688 [cam->params.sensorFps.baserate]
2689 [cam->params.sensorFps.divisor];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002690 cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
2691
2692 new_exposure = cam->params.flickerControl.coarseJump-1;
2693 while(new_exposure < old_exposure/2)
2694 new_exposure += cam->params.flickerControl.coarseJump;
2695 cam->params.exposure.coarseExpLo = new_exposure & 0xff;
2696 cam->params.exposure.coarseExpHi = new_exposure >> 8;
2697 cam->cmd_queue |= COMMAND_SETEXPOSURE;
2698 cam->exposure_status = EXPOSURE_NORMAL;
2699 LOG("Automatically decreasing sensor_fps\n");
2700
2701 } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT ||
2702 cam->exposure_status == EXPOSURE_LIGHT) &&
2703 cam->exposure_count >= LIGHT_TIME*framerate &&
2704 cam->params.sensorFps.divisor > 0) {
2705
2706 /* light for too long */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002707 int max_exp = FIRMWARE_VERSION(1,2) ? MAX_EXP_102 : MAX_EXP ;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002708
2709 --cam->params.sensorFps.divisor;
2710 cam->cmd_queue |= COMMAND_SETSENSORFPS;
2711
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002712 cam->params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07002713 flicker_jumps[cam->mainsFreq]
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002714 [cam->params.sensorFps.baserate]
2715 [cam->params.sensorFps.divisor];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002716 cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
2717
2718 new_exposure = cam->params.flickerControl.coarseJump-1;
2719 while(new_exposure < 2*old_exposure &&
2720 new_exposure+
2721 cam->params.flickerControl.coarseJump < max_exp)
2722 new_exposure += cam->params.flickerControl.coarseJump;
2723 cam->params.exposure.coarseExpLo = new_exposure & 0xff;
2724 cam->params.exposure.coarseExpHi = new_exposure >> 8;
2725 cam->cmd_queue |= COMMAND_SETEXPOSURE;
2726 cam->exposure_status = EXPOSURE_NORMAL;
2727 LOG("Automatically increasing sensor_fps\n");
2728 }
2729 } else {
2730 /* Flicker control off */
2731 if((cam->exposure_status == EXPOSURE_VERY_DARK ||
2732 cam->exposure_status == EXPOSURE_DARK) &&
2733 cam->exposure_count >= DARK_TIME*framerate &&
2734 cam->params.sensorFps.divisor < 3) {
2735
2736 /* dark for too long */
2737 ++cam->params.sensorFps.divisor;
2738 cam->cmd_queue |= COMMAND_SETSENSORFPS;
2739
2740 if(cam->params.exposure.gain > 0) {
2741 --cam->params.exposure.gain;
2742 cam->cmd_queue |= COMMAND_SETEXPOSURE;
2743 }
2744 cam->exposure_status = EXPOSURE_NORMAL;
2745 LOG("Automatically decreasing sensor_fps\n");
2746
2747 } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT ||
2748 cam->exposure_status == EXPOSURE_LIGHT) &&
2749 cam->exposure_count >= LIGHT_TIME*framerate &&
2750 cam->params.sensorFps.divisor > 0) {
2751
2752 /* light for too long */
2753 --cam->params.sensorFps.divisor;
2754 cam->cmd_queue |= COMMAND_SETSENSORFPS;
2755
2756 if(cam->params.exposure.gain <
2757 cam->params.exposure.gainMode-1) {
2758 ++cam->params.exposure.gain;
2759 cam->cmd_queue |= COMMAND_SETEXPOSURE;
2760 }
2761 cam->exposure_status = EXPOSURE_NORMAL;
2762 LOG("Automatically increasing sensor_fps\n");
2763 }
2764 }
Ingo Molnar3593cab2006-02-07 06:49:14 -02002765 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002766}
2767
2768/*-----------------------------------------------------------------*/
2769/* if flicker is switched off, this function switches it back on.It checks,
2770 however, that conditions are suitable before restarting it.
2771 This should only be called for firmware version 1.2.
2772
2773 It also adjust the colour balance when an exposure step is detected - as
2774 long as flicker is running
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002775*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07002776static void restart_flicker(struct cam_data *cam)
2777{
2778 int cam_exposure, old_exp;
2779 if(!FIRMWARE_VERSION(1,2))
2780 return;
Ingo Molnar3593cab2006-02-07 06:49:14 -02002781 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002782 if(cam->params.flickerControl.flickerMode == 0 ||
2783 cam->raw_image[39] == 0) {
Ingo Molnar3593cab2006-02-07 06:49:14 -02002784 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002785 return;
2786 }
2787 cam_exposure = cam->raw_image[39]*2;
2788 old_exp = cam->params.exposure.coarseExpLo +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002789 cam->params.exposure.coarseExpHi*256;
2790 /*
2791 see how far away camera exposure is from a valid
2792 flicker exposure value
2793 */
2794 cam_exposure %= cam->params.flickerControl.coarseJump;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002795 if(!cam->params.flickerControl.disabled &&
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002796 cam_exposure <= cam->params.flickerControl.coarseJump - 3) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002797 /* Flicker control auto-disabled */
2798 cam->params.flickerControl.disabled = 1;
2799 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002800
Linus Torvalds1da177e2005-04-16 15:20:36 -07002801 if(cam->params.flickerControl.disabled &&
2802 cam->params.flickerControl.flickerMode &&
2803 old_exp > cam->params.flickerControl.coarseJump +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002804 ROUND_UP_EXP_FOR_FLICKER) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002805 /* exposure is now high enough to switch
2806 flicker control back on */
2807 set_flicker(&cam->params, &cam->cmd_queue, 1);
2808 if((cam->cmd_queue & COMMAND_SETEXPOSURE) &&
2809 cam->params.exposure.expMode == 2)
2810 cam->exposure_status = EXPOSURE_NORMAL;
2811
2812 }
Ingo Molnar3593cab2006-02-07 06:49:14 -02002813 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002814}
2815#undef FIRMWARE_VERSION
2816
2817static int clear_stall(struct cam_data *cam)
2818{
2819 /* FIXME: Does this actually work? */
2820 LOG("Clearing stall\n");
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002821
Linus Torvalds1da177e2005-04-16 15:20:36 -07002822 cam->ops->streamRead(cam->lowlevel_data, cam->raw_image, 0);
2823 do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0);
2824 return cam->params.status.streamState != STREAM_PAUSED;
2825}
2826
2827/* kernel thread function to read image from camera */
2828static int fetch_frame(void *data)
2829{
2830 int image_size, retry;
2831 struct cam_data *cam = (struct cam_data *)data;
2832 unsigned long oldjif, rate, diff;
2833
2834 /* Allow up to two bad images in a row to be read and
2835 * ignored before an error is reported */
2836 for (retry = 0; retry < 3; ++retry) {
2837 if (retry)
2838 DBG("retry=%d\n", retry);
2839
2840 if (!cam->ops)
2841 continue;
2842
2843 /* load first frame always uncompressed */
2844 if (cam->first_frame &&
2845 cam->params.compression.mode != CPIA_COMPRESSION_NONE) {
2846 do_command(cam, CPIA_COMMAND_SetCompression,
2847 CPIA_COMPRESSION_NONE,
2848 NO_DECIMATION, 0, 0);
2849 /* Trial & error - Discarding a frame prevents the
2850 first frame from having an error in the data. */
2851 do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0);
2852 }
2853
2854 /* init camera upload */
2855 if (do_command(cam, CPIA_COMMAND_GrabFrame, 0,
2856 cam->params.streamStartLine, 0, 0))
2857 continue;
2858
2859 if (cam->ops->wait_for_stream_ready) {
2860 /* loop until image ready */
2861 int count = 0;
2862 do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0);
2863 while (cam->params.status.streamState != STREAM_READY) {
2864 if(++count > READY_TIMEOUT)
2865 break;
2866 if(cam->params.status.streamState ==
2867 STREAM_PAUSED) {
2868 /* Bad news */
2869 if(!clear_stall(cam))
2870 return -EIO;
2871 }
2872
2873 cond_resched();
2874
2875 /* sleep for 10 ms, hopefully ;) */
2876 msleep_interruptible(10);
2877 if (signal_pending(current))
2878 return -EINTR;
2879
2880 do_command(cam, CPIA_COMMAND_GetCameraStatus,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002881 0, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002882 }
2883 if(cam->params.status.streamState != STREAM_READY) {
2884 continue;
2885 }
2886 }
2887
2888 cond_resched();
2889
2890 /* grab image from camera */
2891 oldjif = jiffies;
2892 image_size = cam->ops->streamRead(cam->lowlevel_data,
2893 cam->raw_image, 0);
2894 if (image_size <= 0) {
2895 DBG("streamRead failed: %d\n", image_size);
2896 continue;
2897 }
2898
2899 rate = image_size * HZ / 1024;
2900 diff = jiffies-oldjif;
2901 cam->transfer_rate = diff==0 ? rate : rate/diff;
2902 /* diff==0 ? unlikely but possible */
2903
2904 /* Switch flicker control back on if it got turned off */
2905 restart_flicker(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002906
Linus Torvalds1da177e2005-04-16 15:20:36 -07002907 /* If AEC is enabled, monitor the exposure and
2908 adjust the sensor frame rate if needed */
2909 if(cam->params.exposure.expMode == 2)
2910 monitor_exposure(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002911
Linus Torvalds1da177e2005-04-16 15:20:36 -07002912 /* camera idle now so dispatch queued commands */
2913 dispatch_commands(cam);
2914
2915 /* Update our knowledge of the camera state */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002916 do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
2917 do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002918 do_command(cam, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0);
2919
2920 /* decompress and convert image to by copying it from
2921 * raw_image to decompressed_frame
2922 */
2923
2924 cond_resched();
2925
2926 cam->image_size = parse_picture(cam, image_size);
2927 if (cam->image_size <= 0) {
2928 DBG("parse_picture failed %d\n", cam->image_size);
2929 if(cam->params.compression.mode !=
2930 CPIA_COMPRESSION_NONE) {
2931 /* Compression may not work right if we
2932 had a bad frame, get the next one
2933 uncompressed. */
2934 cam->first_frame = 1;
2935 do_command(cam, CPIA_COMMAND_SetGrabMode,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002936 CPIA_GRAB_SINGLE, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002937 /* FIXME: Trial & error - need up to 70ms for
2938 the grab mode change to complete ? */
2939 msleep_interruptible(70);
2940 if (signal_pending(current))
2941 return -EINTR;
2942 }
2943 } else
2944 break;
2945 }
2946
2947 if (retry < 3) {
2948 /* FIXME: this only works for double buffering */
2949 if (cam->frame[cam->curframe].state == FRAME_READY) {
2950 memcpy(cam->frame[cam->curframe].data,
2951 cam->decompressed_frame.data,
2952 cam->decompressed_frame.count);
2953 cam->frame[cam->curframe].state = FRAME_DONE;
2954 } else
2955 cam->decompressed_frame.state = FRAME_DONE;
2956
2957 if (cam->first_frame) {
2958 cam->first_frame = 0;
2959 do_command(cam, CPIA_COMMAND_SetCompression,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002960 cam->params.compression.mode,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002961 cam->params.compression.decimation, 0, 0);
2962
2963 /* Switch from single-grab to continuous grab */
2964 do_command(cam, CPIA_COMMAND_SetGrabMode,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002965 CPIA_GRAB_CONTINUOUS, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002966 }
2967 return 0;
2968 }
2969 return -EIO;
2970}
2971
2972static int capture_frame(struct cam_data *cam, struct video_mmap *vm)
2973{
2974 if (!cam->frame_buf) {
2975 /* we do lazy allocation */
2976 int err;
2977 if ((err = allocate_frame_buf(cam)))
2978 return err;
2979 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002980
Linus Torvalds1da177e2005-04-16 15:20:36 -07002981 cam->curframe = vm->frame;
2982 cam->frame[cam->curframe].state = FRAME_READY;
2983 return fetch_frame(cam);
2984}
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002985
Linus Torvalds1da177e2005-04-16 15:20:36 -07002986static int goto_high_power(struct cam_data *cam)
2987{
2988 if (do_command(cam, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0))
2989 return -EIO;
2990 msleep_interruptible(40); /* windows driver does it too */
2991 if(signal_pending(current))
2992 return -EINTR;
2993 if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
2994 return -EIO;
2995 if (cam->params.status.systemState == HI_POWER_STATE) {
2996 DBG("camera now in HIGH power state\n");
2997 return 0;
2998 }
2999 printstatus(cam);
3000 return -EIO;
3001}
3002
3003static int goto_low_power(struct cam_data *cam)
3004{
3005 if (do_command(cam, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0))
3006 return -1;
3007 if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
3008 return -1;
3009 if (cam->params.status.systemState == LO_POWER_STATE) {
3010 DBG("camera now in LOW power state\n");
3011 return 0;
3012 }
3013 printstatus(cam);
3014 return -1;
3015}
3016
3017static void save_camera_state(struct cam_data *cam)
3018{
3019 if(!(cam->cmd_queue & COMMAND_SETCOLOURBALANCE))
3020 do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
3021 if(!(cam->cmd_queue & COMMAND_SETEXPOSURE))
3022 do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
3023
3024 DBG("%d/%d/%d/%d/%d/%d/%d/%d\n",
3025 cam->params.exposure.gain,
3026 cam->params.exposure.fineExp,
3027 cam->params.exposure.coarseExpLo,
3028 cam->params.exposure.coarseExpHi,
3029 cam->params.exposure.redComp,
3030 cam->params.exposure.green1Comp,
3031 cam->params.exposure.green2Comp,
3032 cam->params.exposure.blueComp);
3033 DBG("%d/%d/%d\n",
3034 cam->params.colourBalance.redGain,
3035 cam->params.colourBalance.greenGain,
3036 cam->params.colourBalance.blueGain);
3037}
3038
3039static int set_camera_state(struct cam_data *cam)
3040{
3041 cam->cmd_queue = COMMAND_SETCOMPRESSION |
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003042 COMMAND_SETCOMPRESSIONTARGET |
3043 COMMAND_SETCOLOURPARAMS |
3044 COMMAND_SETFORMAT |
3045 COMMAND_SETYUVTHRESH |
3046 COMMAND_SETECPTIMING |
3047 COMMAND_SETCOMPRESSIONPARAMS |
3048 COMMAND_SETEXPOSURE |
3049 COMMAND_SETCOLOURBALANCE |
3050 COMMAND_SETSENSORFPS |
3051 COMMAND_SETAPCOR |
3052 COMMAND_SETFLICKERCTRL |
3053 COMMAND_SETVLOFFSET;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003054
3055 do_command(cam, CPIA_COMMAND_SetGrabMode, CPIA_GRAB_SINGLE,0,0,0);
3056 dispatch_commands(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003057
Linus Torvalds1da177e2005-04-16 15:20:36 -07003058 /* Wait 6 frames for the sensor to get all settings and
3059 AEC/ACB to settle */
3060 msleep_interruptible(6*(cam->params.sensorFps.baserate ? 33 : 40) *
3061 (1 << cam->params.sensorFps.divisor) + 10);
3062
3063 if(signal_pending(current))
3064 return -EINTR;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003065
Linus Torvalds1da177e2005-04-16 15:20:36 -07003066 save_camera_state(cam);
3067
3068 return 0;
3069}
3070
3071static void get_version_information(struct cam_data *cam)
3072{
3073 /* GetCPIAVersion */
3074 do_command(cam, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0);
3075
3076 /* GetPnPID */
3077 do_command(cam, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0);
3078}
3079
3080/* initialize camera */
3081static int reset_camera(struct cam_data *cam)
3082{
3083 int err;
3084 /* Start the camera in low power mode */
3085 if (goto_low_power(cam)) {
3086 if (cam->params.status.systemState != WARM_BOOT_STATE)
3087 return -ENODEV;
3088
3089 /* FIXME: this is just dirty trial and error */
3090 err = goto_high_power(cam);
3091 if(err)
3092 return err;
3093 do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0);
3094 if (goto_low_power(cam))
3095 return -ENODEV;
3096 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003097
Linus Torvalds1da177e2005-04-16 15:20:36 -07003098 /* procedure described in developer's guide p3-28 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003099
Linus Torvalds1da177e2005-04-16 15:20:36 -07003100 /* Check the firmware version. */
3101 cam->params.version.firmwareVersion = 0;
3102 get_version_information(cam);
3103 if (cam->params.version.firmwareVersion != 1)
3104 return -ENODEV;
3105
3106 /* A bug in firmware 1-02 limits gainMode to 2 */
3107 if(cam->params.version.firmwareRevision <= 2 &&
3108 cam->params.exposure.gainMode > 2) {
3109 cam->params.exposure.gainMode = 2;
3110 }
3111
3112 /* set QX3 detected flag */
3113 cam->params.qx3.qx3_detected = (cam->params.pnpID.vendor == 0x0813 &&
3114 cam->params.pnpID.product == 0x0001);
3115
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003116 /* The fatal error checking should be done after
Linus Torvalds1da177e2005-04-16 15:20:36 -07003117 * the camera powers up (developer's guide p 3-38) */
3118
3119 /* Set streamState before transition to high power to avoid bug
3120 * in firmware 1-02 */
3121 do_command(cam, CPIA_COMMAND_ModifyCameraStatus, STREAMSTATE, 0,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003122 STREAM_NOT_READY, 0);
3123
Linus Torvalds1da177e2005-04-16 15:20:36 -07003124 /* GotoHiPower */
3125 err = goto_high_power(cam);
3126 if (err)
3127 return err;
3128
3129 /* Check the camera status */
3130 if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
3131 return -EIO;
3132
3133 if (cam->params.status.fatalError) {
3134 DBG("fatal_error: %#04x\n",
3135 cam->params.status.fatalError);
3136 DBG("vp_status: %#04x\n",
3137 cam->params.status.vpStatus);
3138 if (cam->params.status.fatalError & ~(COM_FLAG|CPIA_FLAG)) {
3139 /* Fatal error in camera */
3140 return -EIO;
3141 } else if (cam->params.status.fatalError & (COM_FLAG|CPIA_FLAG)) {
3142 /* Firmware 1-02 may do this for parallel port cameras,
3143 * just clear the flags (developer's guide p 3-38) */
3144 do_command(cam, CPIA_COMMAND_ModifyCameraStatus,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003145 FATALERROR, ~(COM_FLAG|CPIA_FLAG), 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003146 }
3147 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003148
Linus Torvalds1da177e2005-04-16 15:20:36 -07003149 /* Check the camera status again */
3150 if (cam->params.status.fatalError) {
3151 if (cam->params.status.fatalError)
3152 return -EIO;
3153 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003154
Linus Torvalds1da177e2005-04-16 15:20:36 -07003155 /* VPVersion can't be retrieved before the camera is in HiPower,
3156 * so get it here instead of in get_version_information. */
3157 do_command(cam, CPIA_COMMAND_GetVPVersion, 0, 0, 0, 0);
3158
3159 /* set camera to a known state */
3160 return set_camera_state(cam);
3161}
3162
3163static void put_cam(struct cpia_camera_ops* ops)
3164{
3165 if (ops->owner)
3166 module_put(ops->owner);
3167}
3168
3169/* ------------------------- V4L interface --------------------- */
3170static int cpia_open(struct inode *inode, struct file *file)
3171{
3172 struct video_device *dev = video_devdata(file);
3173 struct cam_data *cam = dev->priv;
3174 int err;
3175
3176 if (!cam) {
3177 DBG("Internal error, cam_data not found!\n");
3178 return -ENODEV;
3179 }
3180
3181 if (cam->open_count > 0) {
3182 DBG("Camera already open\n");
3183 return -EBUSY;
3184 }
3185
3186 if (!try_module_get(cam->ops->owner))
3187 return -ENODEV;
3188
Ingo Molnar3593cab2006-02-07 06:49:14 -02003189 mutex_lock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003190 err = -ENOMEM;
3191 if (!cam->raw_image) {
3192 cam->raw_image = rvmalloc(CPIA_MAX_IMAGE_SIZE);
3193 if (!cam->raw_image)
3194 goto oops;
3195 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003196
Linus Torvalds1da177e2005-04-16 15:20:36 -07003197 if (!cam->decompressed_frame.data) {
3198 cam->decompressed_frame.data = rvmalloc(CPIA_MAX_FRAME_SIZE);
3199 if (!cam->decompressed_frame.data)
3200 goto oops;
3201 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003202
Linus Torvalds1da177e2005-04-16 15:20:36 -07003203 /* open cpia */
3204 err = -ENODEV;
3205 if (cam->ops->open(cam->lowlevel_data))
3206 goto oops;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003207
Linus Torvalds1da177e2005-04-16 15:20:36 -07003208 /* reset the camera */
3209 if ((err = reset_camera(cam)) != 0) {
3210 cam->ops->close(cam->lowlevel_data);
3211 goto oops;
3212 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003213
Linus Torvalds1da177e2005-04-16 15:20:36 -07003214 err = -EINTR;
3215 if(signal_pending(current))
3216 goto oops;
3217
3218 /* Set ownership of /proc/cpia/videoX to current user */
3219 if(cam->proc_entry)
3220 cam->proc_entry->uid = current->uid;
3221
3222 /* set mark for loading first frame uncompressed */
3223 cam->first_frame = 1;
3224
3225 /* init it to something */
3226 cam->mmap_kludge = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003227
Linus Torvalds1da177e2005-04-16 15:20:36 -07003228 ++cam->open_count;
3229 file->private_data = dev;
Ingo Molnar3593cab2006-02-07 06:49:14 -02003230 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003231 return 0;
3232
3233 oops:
3234 if (cam->decompressed_frame.data) {
3235 rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
3236 cam->decompressed_frame.data = NULL;
3237 }
3238 if (cam->raw_image) {
3239 rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
3240 cam->raw_image = NULL;
3241 }
Ingo Molnar3593cab2006-02-07 06:49:14 -02003242 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003243 put_cam(cam->ops);
3244 return err;
3245}
3246
3247static int cpia_close(struct inode *inode, struct file *file)
3248{
3249 struct video_device *dev = file->private_data;
3250 struct cam_data *cam = dev->priv;
3251
3252 if (cam->ops) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003253 /* Return ownership of /proc/cpia/videoX to root */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003254 if(cam->proc_entry)
3255 cam->proc_entry->uid = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003256
Linus Torvalds1da177e2005-04-16 15:20:36 -07003257 /* save camera state for later open (developers guide ch 3.5.3) */
3258 save_camera_state(cam);
3259
3260 /* GotoLoPower */
3261 goto_low_power(cam);
3262
3263 /* Update the camera status */
3264 do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
3265
3266 /* cleanup internal state stuff */
3267 free_frames(cam->frame);
3268
3269 /* close cpia */
3270 cam->ops->close(cam->lowlevel_data);
3271
3272 put_cam(cam->ops);
3273 }
3274
3275 if (--cam->open_count == 0) {
3276 /* clean up capture-buffers */
3277 if (cam->raw_image) {
3278 rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
3279 cam->raw_image = NULL;
3280 }
3281
3282 if (cam->decompressed_frame.data) {
3283 rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
3284 cam->decompressed_frame.data = NULL;
3285 }
3286
3287 if (cam->frame_buf)
3288 free_frame_buf(cam);
3289
3290 if (!cam->ops)
3291 kfree(cam);
3292 }
3293 file->private_data = NULL;
3294
3295 return 0;
3296}
3297
3298static ssize_t cpia_read(struct file *file, char __user *buf,
3299 size_t count, loff_t *ppos)
3300{
3301 struct video_device *dev = file->private_data;
3302 struct cam_data *cam = dev->priv;
3303 int err;
3304
3305 /* make this _really_ smp and multithread-safe */
Ingo Molnar3593cab2006-02-07 06:49:14 -02003306 if (mutex_lock_interruptible(&cam->busy_lock))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003307 return -EINTR;
3308
3309 if (!buf) {
3310 DBG("buf NULL\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02003311 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003312 return -EINVAL;
3313 }
3314
3315 if (!count) {
3316 DBG("count 0\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02003317 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003318 return 0;
3319 }
3320
3321 if (!cam->ops) {
3322 DBG("ops NULL\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02003323 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003324 return -ENODEV;
3325 }
3326
3327 /* upload frame */
3328 cam->decompressed_frame.state = FRAME_READY;
3329 cam->mmap_kludge=0;
3330 if((err = fetch_frame(cam)) != 0) {
3331 DBG("ERROR from fetch_frame: %d\n", err);
Ingo Molnar3593cab2006-02-07 06:49:14 -02003332 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003333 return err;
3334 }
3335 cam->decompressed_frame.state = FRAME_UNUSED;
3336
3337 /* copy data to user space */
3338 if (cam->decompressed_frame.count > count) {
3339 DBG("count wrong: %d, %lu\n", cam->decompressed_frame.count,
3340 (unsigned long) count);
Ingo Molnar3593cab2006-02-07 06:49:14 -02003341 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003342 return -EFAULT;
3343 }
3344 if (copy_to_user(buf, cam->decompressed_frame.data,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003345 cam->decompressed_frame.count)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003346 DBG("copy_to_user failed\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02003347 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003348 return -EFAULT;
3349 }
3350
Ingo Molnar3593cab2006-02-07 06:49:14 -02003351 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003352 return cam->decompressed_frame.count;
3353}
3354
3355static int cpia_do_ioctl(struct inode *inode, struct file *file,
3356 unsigned int ioctlnr, void *arg)
3357{
3358 struct video_device *dev = file->private_data;
3359 struct cam_data *cam = dev->priv;
3360 int retval = 0;
3361
3362 if (!cam || !cam->ops)
3363 return -ENODEV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003364
Linus Torvalds1da177e2005-04-16 15:20:36 -07003365 /* make this _really_ smp-safe */
Ingo Molnar3593cab2006-02-07 06:49:14 -02003366 if (mutex_lock_interruptible(&cam->busy_lock))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003367 return -EINTR;
3368
3369 //DBG("cpia_ioctl: %u\n", ioctlnr);
3370
3371 switch (ioctlnr) {
Alexey Dobriyanbe787ac2006-03-07 22:20:23 -03003372 /* query capabilities */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003373 case VIDIOCGCAP:
3374 {
3375 struct video_capability *b = arg;
3376
3377 DBG("VIDIOCGCAP\n");
3378 strcpy(b->name, "CPiA Camera");
3379 b->type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE;
3380 b->channels = 1;
3381 b->audios = 0;
3382 b->maxwidth = 352; /* VIDEOSIZE_CIF */
3383 b->maxheight = 288;
3384 b->minwidth = 48; /* VIDEOSIZE_48_48 */
3385 b->minheight = 48;
3386 break;
3387 }
3388
3389 /* get/set video source - we are a camera and nothing else */
3390 case VIDIOCGCHAN:
3391 {
3392 struct video_channel *v = arg;
3393
3394 DBG("VIDIOCGCHAN\n");
3395 if (v->channel != 0) {
3396 retval = -EINVAL;
3397 break;
3398 }
3399
3400 v->channel = 0;
3401 strcpy(v->name, "Camera");
3402 v->tuners = 0;
3403 v->flags = 0;
3404 v->type = VIDEO_TYPE_CAMERA;
3405 v->norm = 0;
3406 break;
3407 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003408
Linus Torvalds1da177e2005-04-16 15:20:36 -07003409 case VIDIOCSCHAN:
3410 {
3411 struct video_channel *v = arg;
3412
3413 DBG("VIDIOCSCHAN\n");
3414 if (v->channel != 0)
3415 retval = -EINVAL;
3416 break;
3417 }
3418
3419 /* image properties */
3420 case VIDIOCGPICT:
3421 {
3422 struct video_picture *pic = arg;
3423 DBG("VIDIOCGPICT\n");
3424 *pic = cam->vp;
3425 break;
3426 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003427
Linus Torvalds1da177e2005-04-16 15:20:36 -07003428 case VIDIOCSPICT:
3429 {
3430 struct video_picture *vp = arg;
3431
3432 DBG("VIDIOCSPICT\n");
3433
3434 /* check validity */
3435 DBG("palette: %d\n", vp->palette);
3436 DBG("depth: %d\n", vp->depth);
3437 if (!valid_mode(vp->palette, vp->depth)) {
3438 retval = -EINVAL;
3439 break;
3440 }
3441
Ingo Molnar3593cab2006-02-07 06:49:14 -02003442 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003443 /* brightness, colour, contrast need no check 0-65535 */
3444 cam->vp = *vp;
3445 /* update cam->params.colourParams */
3446 cam->params.colourParams.brightness = vp->brightness*100/65535;
3447 cam->params.colourParams.contrast = vp->contrast*100/65535;
3448 cam->params.colourParams.saturation = vp->colour*100/65535;
3449 /* contrast is in steps of 8, so round */
3450 cam->params.colourParams.contrast =
3451 ((cam->params.colourParams.contrast + 3) / 8) * 8;
3452 if (cam->params.version.firmwareVersion == 1 &&
3453 cam->params.version.firmwareRevision == 2 &&
3454 cam->params.colourParams.contrast > 80) {
3455 /* 1-02 firmware limits contrast to 80 */
3456 cam->params.colourParams.contrast = 80;
3457 }
3458
3459 /* Adjust flicker control if necessary */
3460 if(cam->params.flickerControl.allowableOverExposure < 0)
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003461 cam->params.flickerControl.allowableOverExposure =
Linus Torvalds1da177e2005-04-16 15:20:36 -07003462 -find_over_exposure(cam->params.colourParams.brightness);
3463 if(cam->params.flickerControl.flickerMode != 0)
3464 cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003465
Linus Torvalds1da177e2005-04-16 15:20:36 -07003466
3467 /* queue command to update camera */
3468 cam->cmd_queue |= COMMAND_SETCOLOURPARAMS;
Ingo Molnar3593cab2006-02-07 06:49:14 -02003469 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003470 DBG("VIDIOCSPICT: %d / %d // %d / %d / %d / %d\n",
3471 vp->depth, vp->palette, vp->brightness, vp->hue, vp->colour,
3472 vp->contrast);
3473 break;
3474 }
3475
3476 /* get/set capture window */
3477 case VIDIOCGWIN:
3478 {
3479 struct video_window *vw = arg;
3480 DBG("VIDIOCGWIN\n");
3481
3482 *vw = cam->vw;
3483 break;
3484 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003485
Linus Torvalds1da177e2005-04-16 15:20:36 -07003486 case VIDIOCSWIN:
3487 {
3488 /* copy_from_user, check validity, copy to internal structure */
3489 struct video_window *vw = arg;
3490 DBG("VIDIOCSWIN\n");
3491
3492 if (vw->clipcount != 0) { /* clipping not supported */
3493 retval = -EINVAL;
3494 break;
3495 }
3496 if (vw->clips != NULL) { /* clipping not supported */
3497 retval = -EINVAL;
3498 break;
3499 }
3500
3501 /* we set the video window to something smaller or equal to what
3502 * is requested by the user???
3503 */
Ingo Molnar3593cab2006-02-07 06:49:14 -02003504 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003505 if (vw->width != cam->vw.width || vw->height != cam->vw.height) {
3506 int video_size = match_videosize(vw->width, vw->height);
3507
3508 if (video_size < 0) {
3509 retval = -EINVAL;
Ingo Molnar3593cab2006-02-07 06:49:14 -02003510 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003511 break;
3512 }
3513 cam->video_size = video_size;
3514
3515 /* video size is changing, reset the subcapture area */
3516 memset(&cam->vc, 0, sizeof(cam->vc));
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003517
Linus Torvalds1da177e2005-04-16 15:20:36 -07003518 set_vw_size(cam);
3519 DBG("%d / %d\n", cam->vw.width, cam->vw.height);
3520 cam->cmd_queue |= COMMAND_SETFORMAT;
3521 }
3522
Ingo Molnar3593cab2006-02-07 06:49:14 -02003523 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003524
3525 /* setformat ignored by camera during streaming,
3526 * so stop/dispatch/start */
3527 if (cam->cmd_queue & COMMAND_SETFORMAT) {
3528 DBG("\n");
3529 dispatch_commands(cam);
3530 }
3531 DBG("%d/%d:%d\n", cam->video_size,
3532 cam->vw.width, cam->vw.height);
3533 break;
3534 }
3535
3536 /* mmap interface */
3537 case VIDIOCGMBUF:
3538 {
3539 struct video_mbuf *vm = arg;
3540 int i;
3541
3542 DBG("VIDIOCGMBUF\n");
3543 memset(vm, 0, sizeof(*vm));
3544 vm->size = CPIA_MAX_FRAME_SIZE*FRAME_NUM;
3545 vm->frames = FRAME_NUM;
3546 for (i = 0; i < FRAME_NUM; i++)
3547 vm->offsets[i] = CPIA_MAX_FRAME_SIZE * i;
3548 break;
3549 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003550
Linus Torvalds1da177e2005-04-16 15:20:36 -07003551 case VIDIOCMCAPTURE:
3552 {
3553 struct video_mmap *vm = arg;
3554 int video_size;
3555
3556 DBG("VIDIOCMCAPTURE: %d / %d / %dx%d\n", vm->format, vm->frame,
3557 vm->width, vm->height);
3558 if (vm->frame<0||vm->frame>=FRAME_NUM) {
3559 retval = -EINVAL;
3560 break;
3561 }
3562
3563 /* set video format */
3564 cam->vp.palette = vm->format;
3565 switch(vm->format) {
3566 case VIDEO_PALETTE_GREY:
3567 cam->vp.depth=8;
3568 break;
3569 case VIDEO_PALETTE_RGB555:
3570 case VIDEO_PALETTE_RGB565:
3571 case VIDEO_PALETTE_YUV422:
3572 case VIDEO_PALETTE_YUYV:
3573 case VIDEO_PALETTE_UYVY:
3574 cam->vp.depth = 16;
3575 break;
3576 case VIDEO_PALETTE_RGB24:
3577 cam->vp.depth = 24;
3578 break;
3579 case VIDEO_PALETTE_RGB32:
3580 cam->vp.depth = 32;
3581 break;
3582 default:
3583 retval = -EINVAL;
3584 break;
3585 }
3586 if (retval)
3587 break;
3588
3589 /* set video size */
3590 video_size = match_videosize(vm->width, vm->height);
3591 if (video_size < 0) {
3592 retval = -EINVAL;
3593 break;
3594 }
3595 if (video_size != cam->video_size) {
3596 cam->video_size = video_size;
3597
3598 /* video size is changing, reset the subcapture area */
3599 memset(&cam->vc, 0, sizeof(cam->vc));
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003600
Linus Torvalds1da177e2005-04-16 15:20:36 -07003601 set_vw_size(cam);
3602 cam->cmd_queue |= COMMAND_SETFORMAT;
3603 dispatch_commands(cam);
3604 }
3605 /* according to v4l-spec we must start streaming here */
3606 cam->mmap_kludge = 1;
3607 retval = capture_frame(cam, vm);
3608
3609 break;
3610 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003611
Linus Torvalds1da177e2005-04-16 15:20:36 -07003612 case VIDIOCSYNC:
3613 {
3614 int *frame = arg;
3615
3616 //DBG("VIDIOCSYNC: %d\n", *frame);
3617
3618 if (*frame<0 || *frame >= FRAME_NUM) {
3619 retval = -EINVAL;
3620 break;
3621 }
3622
3623 switch (cam->frame[*frame].state) {
3624 case FRAME_UNUSED:
3625 case FRAME_READY:
3626 case FRAME_GRABBING:
3627 DBG("sync to unused frame %d\n", *frame);
3628 retval = -EINVAL;
3629 break;
3630
3631 case FRAME_DONE:
3632 cam->frame[*frame].state = FRAME_UNUSED;
3633 //DBG("VIDIOCSYNC: %d synced\n", *frame);
3634 break;
3635 }
3636 if (retval == -EINTR) {
3637 /* FIXME - xawtv does not handle this nice */
3638 retval = 0;
3639 }
3640 break;
3641 }
3642
3643 case VIDIOCGCAPTURE:
3644 {
3645 struct video_capture *vc = arg;
3646
3647 DBG("VIDIOCGCAPTURE\n");
3648
3649 *vc = cam->vc;
3650
3651 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003652 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003653
3654 case VIDIOCSCAPTURE:
3655 {
3656 struct video_capture *vc = arg;
3657
3658 DBG("VIDIOCSCAPTURE\n");
3659
3660 if (vc->decimation != 0) { /* How should this be used? */
3661 retval = -EINVAL;
3662 break;
3663 }
3664 if (vc->flags != 0) { /* Even/odd grab not supported */
3665 retval = -EINVAL;
3666 break;
3667 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003668
Linus Torvalds1da177e2005-04-16 15:20:36 -07003669 /* Clip to the resolution we can set for the ROI
3670 (every 8 columns and 4 rows) */
3671 vc->x = vc->x & ~(__u32)7;
3672 vc->y = vc->y & ~(__u32)3;
3673 vc->width = vc->width & ~(__u32)7;
3674 vc->height = vc->height & ~(__u32)3;
3675
3676 if(vc->width == 0 || vc->height == 0 ||
3677 vc->x + vc->width > cam->vw.width ||
3678 vc->y + vc->height > cam->vw.height) {
3679 retval = -EINVAL;
3680 break;
3681 }
3682
3683 DBG("%d,%d/%dx%d\n", vc->x,vc->y,vc->width, vc->height);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003684
Ingo Molnar3593cab2006-02-07 06:49:14 -02003685 mutex_lock(&cam->param_lock);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003686
Linus Torvalds1da177e2005-04-16 15:20:36 -07003687 cam->vc.x = vc->x;
3688 cam->vc.y = vc->y;
3689 cam->vc.width = vc->width;
3690 cam->vc.height = vc->height;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003691
Linus Torvalds1da177e2005-04-16 15:20:36 -07003692 set_vw_size(cam);
3693 cam->cmd_queue |= COMMAND_SETFORMAT;
3694
Ingo Molnar3593cab2006-02-07 06:49:14 -02003695 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003696
3697 /* setformat ignored by camera during streaming,
3698 * so stop/dispatch/start */
3699 dispatch_commands(cam);
3700 break;
3701 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003702
Linus Torvalds1da177e2005-04-16 15:20:36 -07003703 case VIDIOCGUNIT:
3704 {
3705 struct video_unit *vu = arg;
3706
3707 DBG("VIDIOCGUNIT\n");
3708
3709 vu->video = cam->vdev.minor;
3710 vu->vbi = VIDEO_NO_UNIT;
3711 vu->radio = VIDEO_NO_UNIT;
3712 vu->audio = VIDEO_NO_UNIT;
3713 vu->teletext = VIDEO_NO_UNIT;
3714
3715 break;
3716 }
3717
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003718
Linus Torvalds1da177e2005-04-16 15:20:36 -07003719 /* pointless to implement overlay with this camera */
3720 case VIDIOCCAPTURE:
3721 case VIDIOCGFBUF:
3722 case VIDIOCSFBUF:
3723 case VIDIOCKEY:
3724 /* tuner interface - we have none */
3725 case VIDIOCGTUNER:
3726 case VIDIOCSTUNER:
3727 case VIDIOCGFREQ:
3728 case VIDIOCSFREQ:
3729 /* audio interface - we have none */
3730 case VIDIOCGAUDIO:
3731 case VIDIOCSAUDIO:
3732 retval = -EINVAL;
3733 break;
3734 default:
3735 retval = -ENOIOCTLCMD;
3736 break;
3737 }
3738
Ingo Molnar3593cab2006-02-07 06:49:14 -02003739 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003740 return retval;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003741}
Linus Torvalds1da177e2005-04-16 15:20:36 -07003742
3743static int cpia_ioctl(struct inode *inode, struct file *file,
3744 unsigned int cmd, unsigned long arg)
3745{
3746 return video_usercopy(inode, file, cmd, arg, cpia_do_ioctl);
3747}
3748
3749
3750/* FIXME */
3751static int cpia_mmap(struct file *file, struct vm_area_struct *vma)
3752{
3753 struct video_device *dev = file->private_data;
3754 unsigned long start = vma->vm_start;
3755 unsigned long size = vma->vm_end - vma->vm_start;
3756 unsigned long page, pos;
3757 struct cam_data *cam = dev->priv;
3758 int retval;
3759
3760 if (!cam || !cam->ops)
3761 return -ENODEV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003762
Linus Torvalds1da177e2005-04-16 15:20:36 -07003763 DBG("cpia_mmap: %ld\n", size);
3764
3765 if (size > FRAME_NUM*CPIA_MAX_FRAME_SIZE)
3766 return -EINVAL;
3767
3768 if (!cam || !cam->ops)
3769 return -ENODEV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003770
Linus Torvalds1da177e2005-04-16 15:20:36 -07003771 /* make this _really_ smp-safe */
Ingo Molnar3593cab2006-02-07 06:49:14 -02003772 if (mutex_lock_interruptible(&cam->busy_lock))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003773 return -EINTR;
3774
3775 if (!cam->frame_buf) { /* we do lazy allocation */
3776 if ((retval = allocate_frame_buf(cam))) {
Ingo Molnar3593cab2006-02-07 06:49:14 -02003777 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003778 return retval;
3779 }
3780 }
3781
3782 pos = (unsigned long)(cam->frame_buf);
3783 while (size > 0) {
3784 page = vmalloc_to_pfn((void *)pos);
3785 if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) {
Ingo Molnar3593cab2006-02-07 06:49:14 -02003786 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003787 return -EAGAIN;
3788 }
3789 start += PAGE_SIZE;
3790 pos += PAGE_SIZE;
3791 if (size > PAGE_SIZE)
3792 size -= PAGE_SIZE;
3793 else
3794 size = 0;
3795 }
3796
3797 DBG("cpia_mmap: %ld\n", size);
Ingo Molnar3593cab2006-02-07 06:49:14 -02003798 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003799
3800 return 0;
3801}
3802
3803static struct file_operations cpia_fops = {
3804 .owner = THIS_MODULE,
3805 .open = cpia_open,
3806 .release = cpia_close,
3807 .read = cpia_read,
3808 .mmap = cpia_mmap,
3809 .ioctl = cpia_ioctl,
Arnd Bergmann0d0fbf82006-01-09 15:24:57 -02003810 .compat_ioctl = v4l_compat_ioctl32,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003811 .llseek = no_llseek,
3812};
3813
3814static struct video_device cpia_template = {
3815 .owner = THIS_MODULE,
3816 .name = "CPiA Camera",
3817 .type = VID_TYPE_CAPTURE,
3818 .hardware = VID_HARDWARE_CPIA,
3819 .fops = &cpia_fops,
3820};
3821
3822/* initialise cam_data structure */
3823static void reset_camera_struct(struct cam_data *cam)
3824{
3825 /* The following parameter values are the defaults from
3826 * "Software Developer's Guide for CPiA Cameras". Any changes
3827 * to the defaults are noted in comments. */
3828 cam->params.colourParams.brightness = 50;
3829 cam->params.colourParams.contrast = 48;
3830 cam->params.colourParams.saturation = 50;
3831 cam->params.exposure.gainMode = 4;
3832 cam->params.exposure.expMode = 2; /* AEC */
3833 cam->params.exposure.compMode = 1;
3834 cam->params.exposure.centreWeight = 1;
3835 cam->params.exposure.gain = 0;
3836 cam->params.exposure.fineExp = 0;
3837 cam->params.exposure.coarseExpLo = 185;
3838 cam->params.exposure.coarseExpHi = 0;
3839 cam->params.exposure.redComp = COMP_RED;
3840 cam->params.exposure.green1Comp = COMP_GREEN1;
3841 cam->params.exposure.green2Comp = COMP_GREEN2;
3842 cam->params.exposure.blueComp = COMP_BLUE;
3843 cam->params.colourBalance.balanceMode = 2; /* ACB */
3844 cam->params.colourBalance.redGain = 32;
3845 cam->params.colourBalance.greenGain = 6;
3846 cam->params.colourBalance.blueGain = 92;
3847 cam->params.apcor.gain1 = 0x18;
3848 cam->params.apcor.gain2 = 0x16;
3849 cam->params.apcor.gain4 = 0x24;
3850 cam->params.apcor.gain8 = 0x34;
3851 cam->params.flickerControl.flickerMode = 0;
3852 cam->params.flickerControl.disabled = 1;
3853
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003854 cam->params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07003855 flicker_jumps[cam->mainsFreq]
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003856 [cam->params.sensorFps.baserate]
3857 [cam->params.sensorFps.divisor];
3858 cam->params.flickerControl.allowableOverExposure =
Linus Torvalds1da177e2005-04-16 15:20:36 -07003859 -find_over_exposure(cam->params.colourParams.brightness);
3860 cam->params.vlOffset.gain1 = 20;
3861 cam->params.vlOffset.gain2 = 24;
3862 cam->params.vlOffset.gain4 = 26;
3863 cam->params.vlOffset.gain8 = 26;
3864 cam->params.compressionParams.hysteresis = 3;
3865 cam->params.compressionParams.threshMax = 11;
3866 cam->params.compressionParams.smallStep = 1;
3867 cam->params.compressionParams.largeStep = 3;
3868 cam->params.compressionParams.decimationHysteresis = 2;
3869 cam->params.compressionParams.frDiffStepThresh = 5;
3870 cam->params.compressionParams.qDiffStepThresh = 3;
3871 cam->params.compressionParams.decimationThreshMod = 2;
3872 /* End of default values from Software Developer's Guide */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003873
Linus Torvalds1da177e2005-04-16 15:20:36 -07003874 cam->transfer_rate = 0;
3875 cam->exposure_status = EXPOSURE_NORMAL;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003876
Linus Torvalds1da177e2005-04-16 15:20:36 -07003877 /* Set Sensor FPS to 15fps. This seems better than 30fps
3878 * for indoor lighting. */
3879 cam->params.sensorFps.divisor = 1;
3880 cam->params.sensorFps.baserate = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003881
Linus Torvalds1da177e2005-04-16 15:20:36 -07003882 cam->params.yuvThreshold.yThreshold = 6; /* From windows driver */
3883 cam->params.yuvThreshold.uvThreshold = 6; /* From windows driver */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003884
Linus Torvalds1da177e2005-04-16 15:20:36 -07003885 cam->params.format.subSample = SUBSAMPLE_422;
3886 cam->params.format.yuvOrder = YUVORDER_YUYV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003887
Linus Torvalds1da177e2005-04-16 15:20:36 -07003888 cam->params.compression.mode = CPIA_COMPRESSION_AUTO;
3889 cam->params.compressionTarget.frTargeting =
3890 CPIA_COMPRESSION_TARGET_QUALITY;
3891 cam->params.compressionTarget.targetFR = 15; /* From windows driver */
3892 cam->params.compressionTarget.targetQ = 5; /* From windows driver */
3893
3894 cam->params.qx3.qx3_detected = 0;
3895 cam->params.qx3.toplight = 0;
3896 cam->params.qx3.bottomlight = 0;
3897 cam->params.qx3.button = 0;
3898 cam->params.qx3.cradled = 0;
3899
3900 cam->video_size = VIDEOSIZE_CIF;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003901
Linus Torvalds1da177e2005-04-16 15:20:36 -07003902 cam->vp.colour = 32768; /* 50% */
3903 cam->vp.hue = 32768; /* 50% */
3904 cam->vp.brightness = 32768; /* 50% */
3905 cam->vp.contrast = 32768; /* 50% */
3906 cam->vp.whiteness = 0; /* not used -> grayscale only */
3907 cam->vp.depth = 24; /* to be set by user */
3908 cam->vp.palette = VIDEO_PALETTE_RGB24; /* to be set by user */
3909
3910 cam->vc.x = 0;
3911 cam->vc.y = 0;
3912 cam->vc.width = 0;
3913 cam->vc.height = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003914
Linus Torvalds1da177e2005-04-16 15:20:36 -07003915 cam->vw.x = 0;
3916 cam->vw.y = 0;
3917 set_vw_size(cam);
3918 cam->vw.chromakey = 0;
3919 cam->vw.flags = 0;
3920 cam->vw.clipcount = 0;
3921 cam->vw.clips = NULL;
3922
3923 cam->cmd_queue = COMMAND_NONE;
3924 cam->first_frame = 1;
3925
3926 return;
3927}
3928
3929/* initialize cam_data structure */
3930static void init_camera_struct(struct cam_data *cam,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003931 struct cpia_camera_ops *ops )
Linus Torvalds1da177e2005-04-16 15:20:36 -07003932{
3933 int i;
3934
3935 /* Default everything to 0 */
3936 memset(cam, 0, sizeof(struct cam_data));
3937
3938 cam->ops = ops;
Ingo Molnar3593cab2006-02-07 06:49:14 -02003939 mutex_init(&cam->param_lock);
3940 mutex_init(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003941
3942 reset_camera_struct(cam);
3943
3944 cam->proc_entry = NULL;
3945
3946 memcpy(&cam->vdev, &cpia_template, sizeof(cpia_template));
3947 cam->vdev.priv = cam;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003948
Linus Torvalds1da177e2005-04-16 15:20:36 -07003949 cam->curframe = 0;
3950 for (i = 0; i < FRAME_NUM; i++) {
3951 cam->frame[i].width = 0;
3952 cam->frame[i].height = 0;
3953 cam->frame[i].state = FRAME_UNUSED;
3954 cam->frame[i].data = NULL;
3955 }
3956 cam->decompressed_frame.width = 0;
3957 cam->decompressed_frame.height = 0;
3958 cam->decompressed_frame.state = FRAME_UNUSED;
3959 cam->decompressed_frame.data = NULL;
3960}
3961
3962struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel)
3963{
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003964 struct cam_data *camera;
3965
Linus Torvalds1da177e2005-04-16 15:20:36 -07003966 if ((camera = kmalloc(sizeof(struct cam_data), GFP_KERNEL)) == NULL)
3967 return NULL;
3968
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003969
Linus Torvalds1da177e2005-04-16 15:20:36 -07003970 init_camera_struct( camera, ops );
3971 camera->lowlevel_data = lowlevel;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003972
Linus Torvalds1da177e2005-04-16 15:20:36 -07003973 /* register v4l device */
3974 if (video_register_device(&camera->vdev, VFL_TYPE_GRABBER, video_nr) == -1) {
3975 kfree(camera);
3976 printk(KERN_DEBUG "video_register_device failed\n");
3977 return NULL;
3978 }
3979
3980 /* get version information from camera: open/reset/close */
3981
3982 /* open cpia */
3983 if (camera->ops->open(camera->lowlevel_data))
3984 return camera;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003985
Linus Torvalds1da177e2005-04-16 15:20:36 -07003986 /* reset the camera */
3987 if (reset_camera(camera) != 0) {
3988 camera->ops->close(camera->lowlevel_data);
3989 return camera;
3990 }
3991
3992 /* close cpia */
3993 camera->ops->close(camera->lowlevel_data);
3994
3995#ifdef CONFIG_PROC_FS
3996 create_proc_cpia_cam(camera);
3997#endif
3998
3999 printk(KERN_INFO " CPiA Version: %d.%02d (%d.%d)\n",
4000 camera->params.version.firmwareVersion,
4001 camera->params.version.firmwareRevision,
4002 camera->params.version.vcVersion,
4003 camera->params.version.vcRevision);
4004 printk(KERN_INFO " CPiA PnP-ID: %04x:%04x:%04x\n",
4005 camera->params.pnpID.vendor,
4006 camera->params.pnpID.product,
4007 camera->params.pnpID.deviceRevision);
4008 printk(KERN_INFO " VP-Version: %d.%d %04x\n",
4009 camera->params.vpVersion.vpVersion,
4010 camera->params.vpVersion.vpRevision,
4011 camera->params.vpVersion.cameraHeadID);
4012
4013 return camera;
4014}
4015
4016void cpia_unregister_camera(struct cam_data *cam)
4017{
4018 DBG("unregistering video\n");
4019 video_unregister_device(&cam->vdev);
4020 if (cam->open_count) {
4021 put_cam(cam->ops);
4022 DBG("camera open -- setting ops to NULL\n");
4023 cam->ops = NULL;
4024 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03004025
Linus Torvalds1da177e2005-04-16 15:20:36 -07004026#ifdef CONFIG_PROC_FS
4027 DBG("destroying /proc/cpia/video%d\n", cam->vdev.minor);
4028 destroy_proc_cpia_cam(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03004029#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07004030 if (!cam->open_count) {
4031 DBG("freeing camera\n");
4032 kfree(cam);
4033 }
4034}
4035
4036static int __init cpia_init(void)
4037{
4038 printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT,
4039 CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER);
4040
4041 printk(KERN_WARNING "Since in-kernel colorspace conversion is not "
4042 "allowed, it is disabled by default now. Users should fix the "
4043 "applications in case they don't work without conversion "
4044 "reenabled by setting the 'colorspace_conv' module "
4045 "parameter to 1");
4046
4047#ifdef CONFIG_PROC_FS
4048 proc_cpia_create();
4049#endif
4050
4051#ifdef CONFIG_VIDEO_CPIA_PP
4052 cpia_pp_init();
4053#endif
4054#ifdef CONFIG_VIDEO_CPIA_USB
4055 cpia_usb_init();
4056#endif
4057
4058 return 0;
4059}
4060
4061static void __exit cpia_exit(void)
4062{
4063#ifdef CONFIG_PROC_FS
4064 proc_cpia_destroy();
4065#endif
4066}
4067
4068module_init(cpia_init);
4069module_exit(cpia_exit);
4070
4071/* Exported symbols for modules. */
4072
4073EXPORT_SYMBOL(cpia_register_camera);
4074EXPORT_SYMBOL(cpia_unregister_camera);