blob: a1d02e5ce0fda4dbcf83c8371f3f31e29abb3d6a [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
Linus Torvalds1da177e2005-04-16 15:20:36 -070029
30#include <linux/module.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070031#include <linux/init.h>
32#include <linux/fs.h>
33#include <linux/vmalloc.h>
34#include <linux/slab.h>
35#include <linux/proc_fs.h>
36#include <linux/ctype.h>
37#include <linux/pagemap.h>
38#include <linux/delay.h>
39#include <asm/io.h>
Ingo Molnar3593cab2006-02-07 06:49:14 -020040#include <linux/mutex.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070041
42#ifdef CONFIG_KMOD
43#include <linux/kmod.h>
44#endif
45
46#include "cpia.h"
47
Linus Torvalds1da177e2005-04-16 15:20:36 -070048static int video_nr = -1;
49
50#ifdef MODULE
51module_param(video_nr, int, 0);
Randy Dunlap2f8de1a2006-03-21 14:53:22 -030052MODULE_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 -070053MODULE_DESCRIPTION("V4L-driver for Vision CPiA based cameras");
54MODULE_LICENSE("GPL");
55MODULE_SUPPORTED_DEVICE("video");
56#endif
57
Randy Dunlap94190452006-03-27 16:18:25 -030058static unsigned short colorspace_conv;
Linus Torvalds1da177e2005-04-16 15:20:36 -070059module_param(colorspace_conv, ushort, 0444);
60MODULE_PARM_DESC(colorspace_conv,
Mauro Carvalho Chehab4286c6f2006-04-08 16:06:16 -030061 " Colorspace conversion:"
62 "\n 0 = disable, 1 = enable"
63 "\n Default value is 0"
64 );
Linus Torvalds1da177e2005-04-16 15:20:36 -070065
66#define ABOUT "V4L-Driver for Vision CPiA based cameras"
67
68#ifndef VID_HARDWARE_CPIA
69#define VID_HARDWARE_CPIA 24 /* FIXME -> from linux/videodev.h */
70#endif
71
72#define CPIA_MODULE_CPIA (0<<5)
73#define CPIA_MODULE_SYSTEM (1<<5)
74#define CPIA_MODULE_VP_CTRL (5<<5)
75#define CPIA_MODULE_CAPTURE (6<<5)
76#define CPIA_MODULE_DEBUG (7<<5)
77
78#define INPUT (DATA_IN << 8)
79#define OUTPUT (DATA_OUT << 8)
80
81#define CPIA_COMMAND_GetCPIAVersion (INPUT | CPIA_MODULE_CPIA | 1)
82#define CPIA_COMMAND_GetPnPID (INPUT | CPIA_MODULE_CPIA | 2)
83#define CPIA_COMMAND_GetCameraStatus (INPUT | CPIA_MODULE_CPIA | 3)
84#define CPIA_COMMAND_GotoHiPower (OUTPUT | CPIA_MODULE_CPIA | 4)
85#define CPIA_COMMAND_GotoLoPower (OUTPUT | CPIA_MODULE_CPIA | 5)
86#define CPIA_COMMAND_GotoSuspend (OUTPUT | CPIA_MODULE_CPIA | 7)
87#define CPIA_COMMAND_GotoPassThrough (OUTPUT | CPIA_MODULE_CPIA | 8)
88#define CPIA_COMMAND_ModifyCameraStatus (OUTPUT | CPIA_MODULE_CPIA | 10)
89
90#define CPIA_COMMAND_ReadVCRegs (INPUT | CPIA_MODULE_SYSTEM | 1)
91#define CPIA_COMMAND_WriteVCReg (OUTPUT | CPIA_MODULE_SYSTEM | 2)
92#define CPIA_COMMAND_ReadMCPorts (INPUT | CPIA_MODULE_SYSTEM | 3)
93#define CPIA_COMMAND_WriteMCPort (OUTPUT | CPIA_MODULE_SYSTEM | 4)
94#define CPIA_COMMAND_SetBaudRate (OUTPUT | CPIA_MODULE_SYSTEM | 5)
95#define CPIA_COMMAND_SetECPTiming (OUTPUT | CPIA_MODULE_SYSTEM | 6)
96#define CPIA_COMMAND_ReadIDATA (INPUT | CPIA_MODULE_SYSTEM | 7)
97#define CPIA_COMMAND_WriteIDATA (OUTPUT | CPIA_MODULE_SYSTEM | 8)
98#define CPIA_COMMAND_GenericCall (OUTPUT | CPIA_MODULE_SYSTEM | 9)
99#define CPIA_COMMAND_I2CStart (OUTPUT | CPIA_MODULE_SYSTEM | 10)
100#define CPIA_COMMAND_I2CStop (OUTPUT | CPIA_MODULE_SYSTEM | 11)
101#define CPIA_COMMAND_I2CWrite (OUTPUT | CPIA_MODULE_SYSTEM | 12)
102#define CPIA_COMMAND_I2CRead (INPUT | CPIA_MODULE_SYSTEM | 13)
103
104#define CPIA_COMMAND_GetVPVersion (INPUT | CPIA_MODULE_VP_CTRL | 1)
105#define CPIA_COMMAND_ResetFrameCounter (INPUT | CPIA_MODULE_VP_CTRL | 2)
106#define CPIA_COMMAND_SetColourParams (OUTPUT | CPIA_MODULE_VP_CTRL | 3)
107#define CPIA_COMMAND_SetExposure (OUTPUT | CPIA_MODULE_VP_CTRL | 4)
108#define CPIA_COMMAND_SetColourBalance (OUTPUT | CPIA_MODULE_VP_CTRL | 6)
109#define CPIA_COMMAND_SetSensorFPS (OUTPUT | CPIA_MODULE_VP_CTRL | 7)
110#define CPIA_COMMAND_SetVPDefaults (OUTPUT | CPIA_MODULE_VP_CTRL | 8)
111#define CPIA_COMMAND_SetApcor (OUTPUT | CPIA_MODULE_VP_CTRL | 9)
112#define CPIA_COMMAND_SetFlickerCtrl (OUTPUT | CPIA_MODULE_VP_CTRL | 10)
113#define CPIA_COMMAND_SetVLOffset (OUTPUT | CPIA_MODULE_VP_CTRL | 11)
114#define CPIA_COMMAND_GetColourParams (INPUT | CPIA_MODULE_VP_CTRL | 16)
115#define CPIA_COMMAND_GetColourBalance (INPUT | CPIA_MODULE_VP_CTRL | 17)
116#define CPIA_COMMAND_GetExposure (INPUT | CPIA_MODULE_VP_CTRL | 18)
117#define CPIA_COMMAND_SetSensorMatrix (OUTPUT | CPIA_MODULE_VP_CTRL | 19)
118#define CPIA_COMMAND_ColourBars (OUTPUT | CPIA_MODULE_VP_CTRL | 25)
119#define CPIA_COMMAND_ReadVPRegs (INPUT | CPIA_MODULE_VP_CTRL | 30)
120#define CPIA_COMMAND_WriteVPReg (OUTPUT | CPIA_MODULE_VP_CTRL | 31)
121
122#define CPIA_COMMAND_GrabFrame (OUTPUT | CPIA_MODULE_CAPTURE | 1)
123#define CPIA_COMMAND_UploadFrame (OUTPUT | CPIA_MODULE_CAPTURE | 2)
124#define CPIA_COMMAND_SetGrabMode (OUTPUT | CPIA_MODULE_CAPTURE | 3)
125#define CPIA_COMMAND_InitStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 4)
126#define CPIA_COMMAND_FiniStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 5)
127#define CPIA_COMMAND_StartStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 6)
128#define CPIA_COMMAND_EndStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 7)
129#define CPIA_COMMAND_SetFormat (OUTPUT | CPIA_MODULE_CAPTURE | 8)
130#define CPIA_COMMAND_SetROI (OUTPUT | CPIA_MODULE_CAPTURE | 9)
131#define CPIA_COMMAND_SetCompression (OUTPUT | CPIA_MODULE_CAPTURE | 10)
132#define CPIA_COMMAND_SetCompressionTarget (OUTPUT | CPIA_MODULE_CAPTURE | 11)
133#define CPIA_COMMAND_SetYUVThresh (OUTPUT | CPIA_MODULE_CAPTURE | 12)
134#define CPIA_COMMAND_SetCompressionParams (OUTPUT | CPIA_MODULE_CAPTURE | 13)
135#define CPIA_COMMAND_DiscardFrame (OUTPUT | CPIA_MODULE_CAPTURE | 14)
136#define CPIA_COMMAND_GrabReset (OUTPUT | CPIA_MODULE_CAPTURE | 15)
137
138#define CPIA_COMMAND_OutputRS232 (OUTPUT | CPIA_MODULE_DEBUG | 1)
139#define CPIA_COMMAND_AbortProcess (OUTPUT | CPIA_MODULE_DEBUG | 4)
140#define CPIA_COMMAND_SetDramPage (OUTPUT | CPIA_MODULE_DEBUG | 5)
141#define CPIA_COMMAND_StartDramUpload (OUTPUT | CPIA_MODULE_DEBUG | 6)
142#define CPIA_COMMAND_StartDummyDtream (OUTPUT | CPIA_MODULE_DEBUG | 8)
143#define CPIA_COMMAND_AbortStream (OUTPUT | CPIA_MODULE_DEBUG | 9)
144#define CPIA_COMMAND_DownloadDRAM (OUTPUT | CPIA_MODULE_DEBUG | 10)
145#define CPIA_COMMAND_Null (OUTPUT | CPIA_MODULE_DEBUG | 11)
146
147enum {
148 FRAME_READY, /* Ready to grab into */
149 FRAME_GRABBING, /* In the process of being grabbed into */
150 FRAME_DONE, /* Finished grabbing, but not been synced yet */
151 FRAME_UNUSED, /* Unused (no MCAPTURE) */
152};
153
154#define COMMAND_NONE 0x0000
155#define COMMAND_SETCOMPRESSION 0x0001
156#define COMMAND_SETCOMPRESSIONTARGET 0x0002
157#define COMMAND_SETCOLOURPARAMS 0x0004
158#define COMMAND_SETFORMAT 0x0008
159#define COMMAND_PAUSE 0x0010
160#define COMMAND_RESUME 0x0020
161#define COMMAND_SETYUVTHRESH 0x0040
162#define COMMAND_SETECPTIMING 0x0080
163#define COMMAND_SETCOMPRESSIONPARAMS 0x0100
164#define COMMAND_SETEXPOSURE 0x0200
165#define COMMAND_SETCOLOURBALANCE 0x0400
166#define COMMAND_SETSENSORFPS 0x0800
167#define COMMAND_SETAPCOR 0x1000
168#define COMMAND_SETFLICKERCTRL 0x2000
169#define COMMAND_SETVLOFFSET 0x4000
170#define COMMAND_SETLIGHTS 0x8000
171
172#define ROUND_UP_EXP_FOR_FLICKER 15
173
174/* Constants for automatic frame rate adjustment */
175#define MAX_EXP 302
176#define MAX_EXP_102 255
177#define LOW_EXP 140
178#define VERY_LOW_EXP 70
179#define TC 94
180#define EXP_ACC_DARK 50
181#define EXP_ACC_LIGHT 90
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300182#define HIGH_COMP_102 160
183#define MAX_COMP 239
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184#define DARK_TIME 3
185#define LIGHT_TIME 3
186
187/* Maximum number of 10ms loops to wait for the stream to become ready */
188#define READY_TIMEOUT 100
189
190/* Developer's Guide Table 5 p 3-34
191 * indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/
192static u8 flicker_jumps[2][2][4] =
193{ { { 76, 38, 19, 9 }, { 92, 46, 23, 11 } },
194 { { 64, 32, 16, 8 }, { 76, 38, 19, 9} }
195};
196
197/* forward declaration of local function */
198static void reset_camera_struct(struct cam_data *cam);
199static int find_over_exposure(int brightness);
200static void set_flicker(struct cam_params *params, volatile u32 *command_flags,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300201 int on);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202
203
204/**********************************************************************
205 *
206 * Memory management
207 *
208 **********************************************************************/
209static void *rvmalloc(unsigned long size)
210{
211 void *mem;
212 unsigned long adr;
213
214 size = PAGE_ALIGN(size);
215 mem = vmalloc_32(size);
216 if (!mem)
217 return NULL;
218
219 memset(mem, 0, size); /* Clear the ram out, no junk to the user */
220 adr = (unsigned long) mem;
221 while (size > 0) {
222 SetPageReserved(vmalloc_to_page((void *)adr));
223 adr += PAGE_SIZE;
224 size -= PAGE_SIZE;
225 }
226
227 return mem;
228}
229
230static void rvfree(void *mem, unsigned long size)
231{
232 unsigned long adr;
233
234 if (!mem)
235 return;
236
237 adr = (unsigned long) mem;
238 while ((long) size > 0) {
239 ClearPageReserved(vmalloc_to_page((void *)adr));
240 adr += PAGE_SIZE;
241 size -= PAGE_SIZE;
242 }
243 vfree(mem);
244}
245
246/**********************************************************************
247 *
248 * /proc interface
249 *
250 **********************************************************************/
251#ifdef CONFIG_PROC_FS
252static struct proc_dir_entry *cpia_proc_root=NULL;
253
254static int cpia_read_proc(char *page, char **start, off_t off,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300255 int count, int *eof, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256{
257 char *out = page;
258 int len, tmp;
259 struct cam_data *cam = data;
260 char tmpstr[29];
261
262 /* IMPORTANT: This output MUST be kept under PAGE_SIZE
263 * or we need to get more sophisticated. */
264
265 out += sprintf(out, "read-only\n-----------------------\n");
266 out += sprintf(out, "V4L Driver version: %d.%d.%d\n",
267 CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER);
268 out += sprintf(out, "CPIA Version: %d.%02d (%d.%d)\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300269 cam->params.version.firmwareVersion,
270 cam->params.version.firmwareRevision,
271 cam->params.version.vcVersion,
272 cam->params.version.vcRevision);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273 out += sprintf(out, "CPIA PnP-ID: %04x:%04x:%04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300274 cam->params.pnpID.vendor, cam->params.pnpID.product,
275 cam->params.pnpID.deviceRevision);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276 out += sprintf(out, "VP-Version: %d.%d %04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300277 cam->params.vpVersion.vpVersion,
278 cam->params.vpVersion.vpRevision,
279 cam->params.vpVersion.cameraHeadID);
280
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 out += sprintf(out, "system_state: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300282 cam->params.status.systemState);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283 out += sprintf(out, "grab_state: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300284 cam->params.status.grabState);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 out += sprintf(out, "stream_state: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300286 cam->params.status.streamState);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287 out += sprintf(out, "fatal_error: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300288 cam->params.status.fatalError);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289 out += sprintf(out, "cmd_error: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300290 cam->params.status.cmdError);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 out += sprintf(out, "debug_flags: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300292 cam->params.status.debugFlags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293 out += sprintf(out, "vp_status: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300294 cam->params.status.vpStatus);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 out += sprintf(out, "error_code: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300296 cam->params.status.errorCode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297 /* QX3 specific entries */
298 if (cam->params.qx3.qx3_detected) {
299 out += sprintf(out, "button: %4d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300300 cam->params.qx3.button);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301 out += sprintf(out, "cradled: %4d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300302 cam->params.qx3.cradled);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 }
304 out += sprintf(out, "video_size: %s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300305 cam->params.format.videoSize == VIDEOSIZE_CIF ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 "CIF " : "QCIF");
307 out += sprintf(out, "roi: (%3d, %3d) to (%3d, %3d)\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300308 cam->params.roi.colStart*8,
309 cam->params.roi.rowStart*4,
310 cam->params.roi.colEnd*8,
311 cam->params.roi.rowEnd*4);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312 out += sprintf(out, "actual_fps: %3d\n", cam->fps);
313 out += sprintf(out, "transfer_rate: %4dkB/s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300314 cam->transfer_rate);
315
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316 out += sprintf(out, "\nread-write\n");
317 out += sprintf(out, "----------------------- current min"
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300318 " max default comment\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319 out += sprintf(out, "brightness: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300320 cam->params.colourParams.brightness, 0, 100, 50);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321 if (cam->params.version.firmwareVersion == 1 &&
322 cam->params.version.firmwareRevision == 2)
323 /* 1-02 firmware limits contrast to 80 */
324 tmp = 80;
325 else
326 tmp = 96;
327
328 out += sprintf(out, "contrast: %8d %8d %8d %8d"
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300329 " steps of 8\n",
330 cam->params.colourParams.contrast, 0, tmp, 48);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331 out += sprintf(out, "saturation: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300332 cam->params.colourParams.saturation, 0, 100, 50);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 tmp = (25000+5000*cam->params.sensorFps.baserate)/
334 (1<<cam->params.sensorFps.divisor);
335 out += sprintf(out, "sensor_fps: %4d.%03d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300336 tmp/1000, tmp%1000, 3, 30, 15);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337 out += sprintf(out, "stream_start_line: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300338 2*cam->params.streamStartLine, 0,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339 cam->params.format.videoSize == VIDEOSIZE_CIF ? 288:144,
340 cam->params.format.videoSize == VIDEOSIZE_CIF ? 240:120);
341 out += sprintf(out, "sub_sample: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300342 cam->params.format.subSample == SUBSAMPLE_420 ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343 "420" : "422", "420", "422", "422");
344 out += sprintf(out, "yuv_order: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300345 cam->params.format.yuvOrder == YUVORDER_YUYV ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346 "YUYV" : "UYVY", "YUYV" , "UYVY", "YUYV");
347 out += sprintf(out, "ecp_timing: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300348 cam->params.ecpTiming ? "slow" : "normal", "slow",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349 "normal", "normal");
350
351 if (cam->params.colourBalance.balanceMode == 2) {
352 sprintf(tmpstr, "auto");
353 } else {
354 sprintf(tmpstr, "manual");
355 }
356 out += sprintf(out, "color_balance_mode: %8s %8s %8s"
357 " %8s\n", tmpstr, "manual", "auto", "auto");
358 out += sprintf(out, "red_gain: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300359 cam->params.colourBalance.redGain, 0, 212, 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360 out += sprintf(out, "green_gain: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300361 cam->params.colourBalance.greenGain, 0, 212, 6);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362 out += sprintf(out, "blue_gain: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300363 cam->params.colourBalance.blueGain, 0, 212, 92);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364
365 if (cam->params.version.firmwareVersion == 1 &&
366 cam->params.version.firmwareRevision == 2)
367 /* 1-02 firmware limits gain to 2 */
368 sprintf(tmpstr, "%8d %8d %8d", 1, 2, 2);
369 else
370 sprintf(tmpstr, "%8d %8d %8d", 1, 8, 2);
371
372 if (cam->params.exposure.gainMode == 0)
373 out += sprintf(out, "max_gain: unknown %28s"
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300374 " powers of 2\n", tmpstr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375 else
376 out += sprintf(out, "max_gain: %8d %28s"
377 " 1,2,4 or 8 \n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300378 1<<(cam->params.exposure.gainMode-1), tmpstr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379
380 switch(cam->params.exposure.expMode) {
381 case 1:
382 case 3:
383 sprintf(tmpstr, "manual");
384 break;
385 case 2:
386 sprintf(tmpstr, "auto");
387 break;
388 default:
389 sprintf(tmpstr, "unknown");
390 break;
391 }
392 out += sprintf(out, "exposure_mode: %8s %8s %8s"
393 " %8s\n", tmpstr, "manual", "auto", "auto");
394 out += sprintf(out, "centre_weight: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300395 (2-cam->params.exposure.centreWeight) ? "on" : "off",
396 "off", "on", "on");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397 out += sprintf(out, "gain: %8d %8d max_gain %8d 1,2,4,8 possible\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300398 1<<cam->params.exposure.gain, 1, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 if (cam->params.version.firmwareVersion == 1 &&
400 cam->params.version.firmwareRevision == 2)
401 /* 1-02 firmware limits fineExp/2 to 127 */
402 tmp = 254;
403 else
404 tmp = 510;
405
406 out += sprintf(out, "fine_exp: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300407 cam->params.exposure.fineExp*2, 0, tmp, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408 if (cam->params.version.firmwareVersion == 1 &&
409 cam->params.version.firmwareRevision == 2)
410 /* 1-02 firmware limits coarseExpHi to 0 */
411 tmp = MAX_EXP_102;
412 else
413 tmp = MAX_EXP;
414
415 out += sprintf(out, "coarse_exp: %8d %8d %8d"
416 " %8d\n", cam->params.exposure.coarseExpLo+
417 256*cam->params.exposure.coarseExpHi, 0, tmp, 185);
418 out += sprintf(out, "red_comp: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300419 cam->params.exposure.redComp, COMP_RED, 255, COMP_RED);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420 out += sprintf(out, "green1_comp: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300421 cam->params.exposure.green1Comp, COMP_GREEN1, 255,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422 COMP_GREEN1);
423 out += sprintf(out, "green2_comp: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300424 cam->params.exposure.green2Comp, COMP_GREEN2, 255,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425 COMP_GREEN2);
426 out += sprintf(out, "blue_comp: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300427 cam->params.exposure.blueComp, COMP_BLUE, 255, COMP_BLUE);
428
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429 out += sprintf(out, "apcor_gain1: %#8x %#8x %#8x %#8x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300430 cam->params.apcor.gain1, 0, 0xff, 0x1c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431 out += sprintf(out, "apcor_gain2: %#8x %#8x %#8x %#8x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300432 cam->params.apcor.gain2, 0, 0xff, 0x1a);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433 out += sprintf(out, "apcor_gain4: %#8x %#8x %#8x %#8x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300434 cam->params.apcor.gain4, 0, 0xff, 0x2d);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435 out += sprintf(out, "apcor_gain8: %#8x %#8x %#8x %#8x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300436 cam->params.apcor.gain8, 0, 0xff, 0x2a);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437 out += sprintf(out, "vl_offset_gain1: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300438 cam->params.vlOffset.gain1, 0, 255, 24);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439 out += sprintf(out, "vl_offset_gain2: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300440 cam->params.vlOffset.gain2, 0, 255, 28);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441 out += sprintf(out, "vl_offset_gain4: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300442 cam->params.vlOffset.gain4, 0, 255, 30);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443 out += sprintf(out, "vl_offset_gain8: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300444 cam->params.vlOffset.gain8, 0, 255, 30);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445 out += sprintf(out, "flicker_control: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300446 cam->params.flickerControl.flickerMode ? "on" : "off",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447 "off", "on", "off");
448 out += sprintf(out, "mains_frequency: %8d %8d %8d %8d"
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300449 " only 50/60\n",
450 cam->mainsFreq ? 60 : 50, 50, 60, 50);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451 if(cam->params.flickerControl.allowableOverExposure < 0)
452 out += sprintf(out, "allowable_overexposure: %4dauto auto %8d auto\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300453 -cam->params.flickerControl.allowableOverExposure,
454 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 else
456 out += sprintf(out, "allowable_overexposure: %8d auto %8d auto\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300457 cam->params.flickerControl.allowableOverExposure,
458 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459 out += sprintf(out, "compression_mode: ");
460 switch(cam->params.compression.mode) {
461 case CPIA_COMPRESSION_NONE:
462 out += sprintf(out, "%8s", "none");
463 break;
464 case CPIA_COMPRESSION_AUTO:
465 out += sprintf(out, "%8s", "auto");
466 break;
467 case CPIA_COMPRESSION_MANUAL:
468 out += sprintf(out, "%8s", "manual");
469 break;
470 default:
471 out += sprintf(out, "%8s", "unknown");
472 break;
473 }
474 out += sprintf(out, " none,auto,manual auto\n");
475 out += sprintf(out, "decimation_enable: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300476 cam->params.compression.decimation ==
477 DECIMATION_ENAB ? "on":"off", "off", "on",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478 "off");
479 out += sprintf(out, "compression_target: %9s %9s %9s %9s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300480 cam->params.compressionTarget.frTargeting ==
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481 CPIA_COMPRESSION_TARGET_FRAMERATE ?
482 "framerate":"quality",
483 "framerate", "quality", "quality");
484 out += sprintf(out, "target_framerate: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300485 cam->params.compressionTarget.targetFR, 1, 30, 15);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486 out += sprintf(out, "target_quality: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300487 cam->params.compressionTarget.targetQ, 1, 64, 5);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488 out += sprintf(out, "y_threshold: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300489 cam->params.yuvThreshold.yThreshold, 0, 31, 6);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490 out += sprintf(out, "uv_threshold: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300491 cam->params.yuvThreshold.uvThreshold, 0, 31, 6);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492 out += sprintf(out, "hysteresis: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300493 cam->params.compressionParams.hysteresis, 0, 255, 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494 out += sprintf(out, "threshold_max: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300495 cam->params.compressionParams.threshMax, 0, 255, 11);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496 out += sprintf(out, "small_step: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300497 cam->params.compressionParams.smallStep, 0, 255, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498 out += sprintf(out, "large_step: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300499 cam->params.compressionParams.largeStep, 0, 255, 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 out += sprintf(out, "decimation_hysteresis: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300501 cam->params.compressionParams.decimationHysteresis,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502 0, 255, 2);
503 out += sprintf(out, "fr_diff_step_thresh: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300504 cam->params.compressionParams.frDiffStepThresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505 0, 255, 5);
506 out += sprintf(out, "q_diff_step_thresh: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300507 cam->params.compressionParams.qDiffStepThresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508 0, 255, 3);
509 out += sprintf(out, "decimation_thresh_mod: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300510 cam->params.compressionParams.decimationThreshMod,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 0, 255, 2);
512 /* QX3 specific entries */
513 if (cam->params.qx3.qx3_detected) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300514 out += sprintf(out, "toplight: %8s %8s %8s %8s\n",
515 cam->params.qx3.toplight ? "on" : "off",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516 "off", "on", "off");
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300517 out += sprintf(out, "bottomlight: %8s %8s %8s %8s\n",
518 cam->params.qx3.bottomlight ? "on" : "off",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519 "off", "on", "off");
520 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300521
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522 len = out - page;
523 len -= off;
524 if (len < count) {
525 *eof = 1;
526 if (len <= 0) return 0;
527 } else
528 len = count;
529
530 *start = page + off;
531 return len;
532}
533
534
535static int match(char *checkstr, char **buffer, unsigned long *count,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300536 int *find_colon, int *err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537{
538 int ret, colon_found = 1;
539 int len = strlen(checkstr);
540 ret = (len <= *count && strncmp(*buffer, checkstr, len) == 0);
541 if (ret) {
542 *buffer += len;
543 *count -= len;
544 if (*find_colon) {
545 colon_found = 0;
546 while (*count && (**buffer == ' ' || **buffer == '\t' ||
547 (!colon_found && **buffer == ':'))) {
548 if (**buffer == ':')
549 colon_found = 1;
550 --*count;
551 ++*buffer;
552 }
553 if (!*count || !colon_found)
554 *err = -EINVAL;
555 *find_colon = 0;
556 }
557 }
558 return ret;
559}
560
561static unsigned long int value(char **buffer, unsigned long *count, int *err)
562{
563 char *p;
564 unsigned long int ret;
565 ret = simple_strtoul(*buffer, &p, 0);
566 if (p == *buffer)
567 *err = -EINVAL;
568 else {
569 *count -= p - *buffer;
570 *buffer = p;
571 }
572 return ret;
573}
574
575static int cpia_write_proc(struct file *file, const char __user *buf,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300576 unsigned long count, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577{
578 struct cam_data *cam = data;
579 struct cam_params new_params;
580 char *page, *buffer;
581 int retval, find_colon;
582 int size = count;
583 unsigned long val = 0;
584 u32 command_flags = 0;
585 u8 new_mains;
586
587 /*
588 * This code to copy from buf to page is shamelessly copied
589 * from the comx driver
590 */
591 if (count > PAGE_SIZE) {
592 printk(KERN_ERR "count is %lu > %d!!!\n", count, (int)PAGE_SIZE);
593 return -ENOSPC;
594 }
595
596 if (!(page = (char *)__get_free_page(GFP_KERNEL))) return -ENOMEM;
597
598 if(copy_from_user(page, buf, count))
599 {
600 retval = -EFAULT;
601 goto out;
602 }
603
604 if (page[count-1] == '\n')
605 page[count-1] = '\0';
606 else if (count < PAGE_SIZE)
607 page[count] = '\0';
608 else if (page[count]) {
609 retval = -EINVAL;
610 goto out;
611 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300612
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613 buffer = page;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300614
Ingo Molnar3593cab2006-02-07 06:49:14 -0200615 if (mutex_lock_interruptible(&cam->param_lock))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616 return -ERESTARTSYS;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300617
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618 /*
619 * Skip over leading whitespace
620 */
621 while (count && isspace(*buffer)) {
622 --count;
623 ++buffer;
624 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300625
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626 memcpy(&new_params, &cam->params, sizeof(struct cam_params));
627 new_mains = cam->mainsFreq;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300628
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629#define MATCH(x) (match(x, &buffer, &count, &find_colon, &retval))
630#define VALUE (value(&buffer,&count, &retval))
631#define FIRMWARE_VERSION(x,y) (new_params.version.firmwareVersion == (x) && \
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300632 new_params.version.firmwareRevision == (y))
633
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634 retval = 0;
635 while (count && !retval) {
636 find_colon = 1;
637 if (MATCH("brightness")) {
638 if (!retval)
639 val = VALUE;
640
641 if (!retval) {
642 if (val <= 100)
643 new_params.colourParams.brightness = val;
644 else
645 retval = -EINVAL;
646 }
647 command_flags |= COMMAND_SETCOLOURPARAMS;
648 if(new_params.flickerControl.allowableOverExposure < 0)
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300649 new_params.flickerControl.allowableOverExposure =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650 -find_over_exposure(new_params.colourParams.brightness);
651 if(new_params.flickerControl.flickerMode != 0)
652 command_flags |= COMMAND_SETFLICKERCTRL;
653
654 } else if (MATCH("contrast")) {
655 if (!retval)
656 val = VALUE;
657
658 if (!retval) {
659 if (val <= 100) {
660 /* contrast is in steps of 8, so round*/
661 val = ((val + 3) / 8) * 8;
662 /* 1-02 firmware limits contrast to 80*/
663 if (FIRMWARE_VERSION(1,2) && val > 80)
664 val = 80;
665
666 new_params.colourParams.contrast = val;
667 } else
668 retval = -EINVAL;
669 }
670 command_flags |= COMMAND_SETCOLOURPARAMS;
671 } else if (MATCH("saturation")) {
672 if (!retval)
673 val = VALUE;
674
675 if (!retval) {
676 if (val <= 100)
677 new_params.colourParams.saturation = val;
678 else
679 retval = -EINVAL;
680 }
681 command_flags |= COMMAND_SETCOLOURPARAMS;
682 } else if (MATCH("sensor_fps")) {
683 if (!retval)
684 val = VALUE;
685
686 if (!retval) {
687 /* find values so that sensorFPS is minimized,
688 * but >= val */
689 if (val > 30)
690 retval = -EINVAL;
691 else if (val > 25) {
692 new_params.sensorFps.divisor = 0;
693 new_params.sensorFps.baserate = 1;
694 } else if (val > 15) {
695 new_params.sensorFps.divisor = 0;
696 new_params.sensorFps.baserate = 0;
697 } else if (val > 12) {
698 new_params.sensorFps.divisor = 1;
699 new_params.sensorFps.baserate = 1;
700 } else if (val > 7) {
701 new_params.sensorFps.divisor = 1;
702 new_params.sensorFps.baserate = 0;
703 } else if (val > 6) {
704 new_params.sensorFps.divisor = 2;
705 new_params.sensorFps.baserate = 1;
706 } else if (val > 3) {
707 new_params.sensorFps.divisor = 2;
708 new_params.sensorFps.baserate = 0;
709 } else {
710 new_params.sensorFps.divisor = 3;
711 /* Either base rate would work here */
712 new_params.sensorFps.baserate = 1;
713 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300714 new_params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715 flicker_jumps[new_mains]
716 [new_params.sensorFps.baserate]
717 [new_params.sensorFps.divisor];
718 if (new_params.flickerControl.flickerMode)
719 command_flags |= COMMAND_SETFLICKERCTRL;
720 }
721 command_flags |= COMMAND_SETSENSORFPS;
722 cam->exposure_status = EXPOSURE_NORMAL;
723 } else if (MATCH("stream_start_line")) {
724 if (!retval)
725 val = VALUE;
726
727 if (!retval) {
728 int max_line = 288;
729
730 if (new_params.format.videoSize == VIDEOSIZE_QCIF)
731 max_line = 144;
732 if (val <= max_line)
733 new_params.streamStartLine = val/2;
734 else
735 retval = -EINVAL;
736 }
737 } else if (MATCH("sub_sample")) {
738 if (!retval && MATCH("420"))
739 new_params.format.subSample = SUBSAMPLE_420;
740 else if (!retval && MATCH("422"))
741 new_params.format.subSample = SUBSAMPLE_422;
742 else
743 retval = -EINVAL;
744
745 command_flags |= COMMAND_SETFORMAT;
746 } else if (MATCH("yuv_order")) {
747 if (!retval && MATCH("YUYV"))
748 new_params.format.yuvOrder = YUVORDER_YUYV;
749 else if (!retval && MATCH("UYVY"))
750 new_params.format.yuvOrder = YUVORDER_UYVY;
751 else
752 retval = -EINVAL;
753
754 command_flags |= COMMAND_SETFORMAT;
755 } else if (MATCH("ecp_timing")) {
756 if (!retval && MATCH("normal"))
757 new_params.ecpTiming = 0;
758 else if (!retval && MATCH("slow"))
759 new_params.ecpTiming = 1;
760 else
761 retval = -EINVAL;
762
763 command_flags |= COMMAND_SETECPTIMING;
764 } else if (MATCH("color_balance_mode")) {
765 if (!retval && MATCH("manual"))
766 new_params.colourBalance.balanceMode = 3;
767 else if (!retval && MATCH("auto"))
768 new_params.colourBalance.balanceMode = 2;
769 else
770 retval = -EINVAL;
771
772 command_flags |= COMMAND_SETCOLOURBALANCE;
773 } else if (MATCH("red_gain")) {
774 if (!retval)
775 val = VALUE;
776
777 if (!retval) {
778 if (val <= 212) {
779 new_params.colourBalance.redGain = val;
780 new_params.colourBalance.balanceMode = 1;
781 } else
782 retval = -EINVAL;
783 }
784 command_flags |= COMMAND_SETCOLOURBALANCE;
785 } else if (MATCH("green_gain")) {
786 if (!retval)
787 val = VALUE;
788
789 if (!retval) {
790 if (val <= 212) {
791 new_params.colourBalance.greenGain = val;
792 new_params.colourBalance.balanceMode = 1;
793 } else
794 retval = -EINVAL;
795 }
796 command_flags |= COMMAND_SETCOLOURBALANCE;
797 } else if (MATCH("blue_gain")) {
798 if (!retval)
799 val = VALUE;
800
801 if (!retval) {
802 if (val <= 212) {
803 new_params.colourBalance.blueGain = val;
804 new_params.colourBalance.balanceMode = 1;
805 } else
806 retval = -EINVAL;
807 }
808 command_flags |= COMMAND_SETCOLOURBALANCE;
809 } else if (MATCH("max_gain")) {
810 if (!retval)
811 val = VALUE;
812
813 if (!retval) {
814 /* 1-02 firmware limits gain to 2 */
815 if (FIRMWARE_VERSION(1,2) && val > 2)
816 val = 2;
817 switch(val) {
818 case 1:
819 new_params.exposure.gainMode = 1;
820 break;
821 case 2:
822 new_params.exposure.gainMode = 2;
823 break;
824 case 4:
825 new_params.exposure.gainMode = 3;
826 break;
827 case 8:
828 new_params.exposure.gainMode = 4;
829 break;
830 default:
831 retval = -EINVAL;
832 break;
833 }
834 }
835 command_flags |= COMMAND_SETEXPOSURE;
836 } else if (MATCH("exposure_mode")) {
837 if (!retval && MATCH("auto"))
838 new_params.exposure.expMode = 2;
839 else if (!retval && MATCH("manual")) {
840 if (new_params.exposure.expMode == 2)
841 new_params.exposure.expMode = 3;
842 if(new_params.flickerControl.flickerMode != 0)
843 command_flags |= COMMAND_SETFLICKERCTRL;
844 new_params.flickerControl.flickerMode = 0;
845 } else
846 retval = -EINVAL;
847
848 command_flags |= COMMAND_SETEXPOSURE;
849 } else if (MATCH("centre_weight")) {
850 if (!retval && MATCH("on"))
851 new_params.exposure.centreWeight = 1;
852 else if (!retval && MATCH("off"))
853 new_params.exposure.centreWeight = 2;
854 else
855 retval = -EINVAL;
856
857 command_flags |= COMMAND_SETEXPOSURE;
858 } else if (MATCH("gain")) {
859 if (!retval)
860 val = VALUE;
861
862 if (!retval) {
863 switch(val) {
864 case 1:
865 new_params.exposure.gain = 0;
866 break;
867 case 2:
868 new_params.exposure.gain = 1;
869 break;
870 case 4:
871 new_params.exposure.gain = 2;
872 break;
873 case 8:
874 new_params.exposure.gain = 3;
875 break;
876 default:
877 retval = -EINVAL;
878 break;
879 }
880 new_params.exposure.expMode = 1;
881 if(new_params.flickerControl.flickerMode != 0)
882 command_flags |= COMMAND_SETFLICKERCTRL;
883 new_params.flickerControl.flickerMode = 0;
884 command_flags |= COMMAND_SETEXPOSURE;
885 if (new_params.exposure.gain >
886 new_params.exposure.gainMode-1)
887 retval = -EINVAL;
888 }
889 } else if (MATCH("fine_exp")) {
890 if (!retval)
891 val = VALUE/2;
892
893 if (!retval) {
894 if (val < 256) {
895 /* 1-02 firmware limits fineExp/2 to 127*/
896 if (FIRMWARE_VERSION(1,2) && val > 127)
897 val = 127;
898 new_params.exposure.fineExp = val;
899 new_params.exposure.expMode = 1;
900 command_flags |= COMMAND_SETEXPOSURE;
901 if(new_params.flickerControl.flickerMode != 0)
902 command_flags |= COMMAND_SETFLICKERCTRL;
903 new_params.flickerControl.flickerMode = 0;
904 command_flags |= COMMAND_SETFLICKERCTRL;
905 } else
906 retval = -EINVAL;
907 }
908 } else if (MATCH("coarse_exp")) {
909 if (!retval)
910 val = VALUE;
911
912 if (!retval) {
913 if (val <= MAX_EXP) {
914 if (FIRMWARE_VERSION(1,2) &&
915 val > MAX_EXP_102)
916 val = MAX_EXP_102;
917 new_params.exposure.coarseExpLo =
918 val & 0xff;
919 new_params.exposure.coarseExpHi =
920 val >> 8;
921 new_params.exposure.expMode = 1;
922 command_flags |= COMMAND_SETEXPOSURE;
923 if(new_params.flickerControl.flickerMode != 0)
924 command_flags |= COMMAND_SETFLICKERCTRL;
925 new_params.flickerControl.flickerMode = 0;
926 command_flags |= COMMAND_SETFLICKERCTRL;
927 } else
928 retval = -EINVAL;
929 }
930 } else if (MATCH("red_comp")) {
931 if (!retval)
932 val = VALUE;
933
934 if (!retval) {
935 if (val >= COMP_RED && val <= 255) {
936 new_params.exposure.redComp = val;
937 new_params.exposure.compMode = 1;
938 command_flags |= COMMAND_SETEXPOSURE;
939 } else
940 retval = -EINVAL;
941 }
942 } else if (MATCH("green1_comp")) {
943 if (!retval)
944 val = VALUE;
945
946 if (!retval) {
947 if (val >= COMP_GREEN1 && val <= 255) {
948 new_params.exposure.green1Comp = val;
949 new_params.exposure.compMode = 1;
950 command_flags |= COMMAND_SETEXPOSURE;
951 } else
952 retval = -EINVAL;
953 }
954 } else if (MATCH("green2_comp")) {
955 if (!retval)
956 val = VALUE;
957
958 if (!retval) {
959 if (val >= COMP_GREEN2 && val <= 255) {
960 new_params.exposure.green2Comp = val;
961 new_params.exposure.compMode = 1;
962 command_flags |= COMMAND_SETEXPOSURE;
963 } else
964 retval = -EINVAL;
965 }
966 } else if (MATCH("blue_comp")) {
967 if (!retval)
968 val = VALUE;
969
970 if (!retval) {
971 if (val >= COMP_BLUE && val <= 255) {
972 new_params.exposure.blueComp = val;
973 new_params.exposure.compMode = 1;
974 command_flags |= COMMAND_SETEXPOSURE;
975 } else
976 retval = -EINVAL;
977 }
978 } else if (MATCH("apcor_gain1")) {
979 if (!retval)
980 val = VALUE;
981
982 if (!retval) {
983 command_flags |= COMMAND_SETAPCOR;
984 if (val <= 0xff)
985 new_params.apcor.gain1 = val;
986 else
987 retval = -EINVAL;
988 }
989 } else if (MATCH("apcor_gain2")) {
990 if (!retval)
991 val = VALUE;
992
993 if (!retval) {
994 command_flags |= COMMAND_SETAPCOR;
995 if (val <= 0xff)
996 new_params.apcor.gain2 = val;
997 else
998 retval = -EINVAL;
999 }
1000 } else if (MATCH("apcor_gain4")) {
1001 if (!retval)
1002 val = VALUE;
1003
1004 if (!retval) {
1005 command_flags |= COMMAND_SETAPCOR;
1006 if (val <= 0xff)
1007 new_params.apcor.gain4 = val;
1008 else
1009 retval = -EINVAL;
1010 }
1011 } else if (MATCH("apcor_gain8")) {
1012 if (!retval)
1013 val = VALUE;
1014
1015 if (!retval) {
1016 command_flags |= COMMAND_SETAPCOR;
1017 if (val <= 0xff)
1018 new_params.apcor.gain8 = val;
1019 else
1020 retval = -EINVAL;
1021 }
1022 } else if (MATCH("vl_offset_gain1")) {
1023 if (!retval)
1024 val = VALUE;
1025
1026 if (!retval) {
1027 if (val <= 0xff)
1028 new_params.vlOffset.gain1 = val;
1029 else
1030 retval = -EINVAL;
1031 }
1032 command_flags |= COMMAND_SETVLOFFSET;
1033 } else if (MATCH("vl_offset_gain2")) {
1034 if (!retval)
1035 val = VALUE;
1036
1037 if (!retval) {
1038 if (val <= 0xff)
1039 new_params.vlOffset.gain2 = val;
1040 else
1041 retval = -EINVAL;
1042 }
1043 command_flags |= COMMAND_SETVLOFFSET;
1044 } else if (MATCH("vl_offset_gain4")) {
1045 if (!retval)
1046 val = VALUE;
1047
1048 if (!retval) {
1049 if (val <= 0xff)
1050 new_params.vlOffset.gain4 = val;
1051 else
1052 retval = -EINVAL;
1053 }
1054 command_flags |= COMMAND_SETVLOFFSET;
1055 } else if (MATCH("vl_offset_gain8")) {
1056 if (!retval)
1057 val = VALUE;
1058
1059 if (!retval) {
1060 if (val <= 0xff)
1061 new_params.vlOffset.gain8 = val;
1062 else
1063 retval = -EINVAL;
1064 }
1065 command_flags |= COMMAND_SETVLOFFSET;
1066 } else if (MATCH("flicker_control")) {
1067 if (!retval && MATCH("on")) {
1068 set_flicker(&new_params, &command_flags, 1);
1069 } else if (!retval && MATCH("off")) {
1070 set_flicker(&new_params, &command_flags, 0);
1071 } else
1072 retval = -EINVAL;
1073
1074 command_flags |= COMMAND_SETFLICKERCTRL;
1075 } else if (MATCH("mains_frequency")) {
1076 if (!retval && MATCH("50")) {
1077 new_mains = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001078 new_params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079 flicker_jumps[new_mains]
1080 [new_params.sensorFps.baserate]
1081 [new_params.sensorFps.divisor];
1082 if (new_params.flickerControl.flickerMode)
1083 command_flags |= COMMAND_SETFLICKERCTRL;
1084 } else if (!retval && MATCH("60")) {
1085 new_mains = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001086 new_params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087 flicker_jumps[new_mains]
1088 [new_params.sensorFps.baserate]
1089 [new_params.sensorFps.divisor];
1090 if (new_params.flickerControl.flickerMode)
1091 command_flags |= COMMAND_SETFLICKERCTRL;
1092 } else
1093 retval = -EINVAL;
1094 } else if (MATCH("allowable_overexposure")) {
1095 if (!retval && MATCH("auto")) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001096 new_params.flickerControl.allowableOverExposure =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001097 -find_over_exposure(new_params.colourParams.brightness);
1098 if(new_params.flickerControl.flickerMode != 0)
1099 command_flags |= COMMAND_SETFLICKERCTRL;
1100 } else {
1101 if (!retval)
1102 val = VALUE;
1103
1104 if (!retval) {
1105 if (val <= 0xff) {
1106 new_params.flickerControl.
1107 allowableOverExposure = val;
1108 if(new_params.flickerControl.flickerMode != 0)
1109 command_flags |= COMMAND_SETFLICKERCTRL;
1110 } else
1111 retval = -EINVAL;
1112 }
1113 }
1114 } else if (MATCH("compression_mode")) {
1115 if (!retval && MATCH("none"))
1116 new_params.compression.mode =
1117 CPIA_COMPRESSION_NONE;
1118 else if (!retval && MATCH("auto"))
1119 new_params.compression.mode =
1120 CPIA_COMPRESSION_AUTO;
1121 else if (!retval && MATCH("manual"))
1122 new_params.compression.mode =
1123 CPIA_COMPRESSION_MANUAL;
1124 else
1125 retval = -EINVAL;
1126
1127 command_flags |= COMMAND_SETCOMPRESSION;
1128 } else if (MATCH("decimation_enable")) {
1129 if (!retval && MATCH("off"))
1130 new_params.compression.decimation = 0;
1131 else if (!retval && MATCH("on"))
1132 new_params.compression.decimation = 1;
1133 else
1134 retval = -EINVAL;
1135
1136 command_flags |= COMMAND_SETCOMPRESSION;
1137 } else if (MATCH("compression_target")) {
1138 if (!retval && MATCH("quality"))
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001139 new_params.compressionTarget.frTargeting =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001140 CPIA_COMPRESSION_TARGET_QUALITY;
1141 else if (!retval && MATCH("framerate"))
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001142 new_params.compressionTarget.frTargeting =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001143 CPIA_COMPRESSION_TARGET_FRAMERATE;
1144 else
1145 retval = -EINVAL;
1146
1147 command_flags |= COMMAND_SETCOMPRESSIONTARGET;
1148 } else if (MATCH("target_framerate")) {
1149 if (!retval)
1150 val = VALUE;
1151
1152 if (!retval) {
1153 if(val > 0 && val <= 30)
1154 new_params.compressionTarget.targetFR = val;
1155 else
1156 retval = -EINVAL;
1157 }
1158 command_flags |= COMMAND_SETCOMPRESSIONTARGET;
1159 } else if (MATCH("target_quality")) {
1160 if (!retval)
1161 val = VALUE;
1162
1163 if (!retval) {
1164 if(val > 0 && val <= 64)
1165 new_params.compressionTarget.targetQ = val;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001166 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001167 retval = -EINVAL;
1168 }
1169 command_flags |= COMMAND_SETCOMPRESSIONTARGET;
1170 } else if (MATCH("y_threshold")) {
1171 if (!retval)
1172 val = VALUE;
1173
1174 if (!retval) {
1175 if (val < 32)
1176 new_params.yuvThreshold.yThreshold = val;
1177 else
1178 retval = -EINVAL;
1179 }
1180 command_flags |= COMMAND_SETYUVTHRESH;
1181 } else if (MATCH("uv_threshold")) {
1182 if (!retval)
1183 val = VALUE;
1184
1185 if (!retval) {
1186 if (val < 32)
1187 new_params.yuvThreshold.uvThreshold = val;
1188 else
1189 retval = -EINVAL;
1190 }
1191 command_flags |= COMMAND_SETYUVTHRESH;
1192 } else if (MATCH("hysteresis")) {
1193 if (!retval)
1194 val = VALUE;
1195
1196 if (!retval) {
1197 if (val <= 0xff)
1198 new_params.compressionParams.hysteresis = val;
1199 else
1200 retval = -EINVAL;
1201 }
1202 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1203 } else if (MATCH("threshold_max")) {
1204 if (!retval)
1205 val = VALUE;
1206
1207 if (!retval) {
1208 if (val <= 0xff)
1209 new_params.compressionParams.threshMax = val;
1210 else
1211 retval = -EINVAL;
1212 }
1213 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1214 } else if (MATCH("small_step")) {
1215 if (!retval)
1216 val = VALUE;
1217
1218 if (!retval) {
1219 if (val <= 0xff)
1220 new_params.compressionParams.smallStep = val;
1221 else
1222 retval = -EINVAL;
1223 }
1224 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1225 } else if (MATCH("large_step")) {
1226 if (!retval)
1227 val = VALUE;
1228
1229 if (!retval) {
1230 if (val <= 0xff)
1231 new_params.compressionParams.largeStep = val;
1232 else
1233 retval = -EINVAL;
1234 }
1235 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1236 } else if (MATCH("decimation_hysteresis")) {
1237 if (!retval)
1238 val = VALUE;
1239
1240 if (!retval) {
1241 if (val <= 0xff)
1242 new_params.compressionParams.decimationHysteresis = val;
1243 else
1244 retval = -EINVAL;
1245 }
1246 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1247 } else if (MATCH("fr_diff_step_thresh")) {
1248 if (!retval)
1249 val = VALUE;
1250
1251 if (!retval) {
1252 if (val <= 0xff)
1253 new_params.compressionParams.frDiffStepThresh = val;
1254 else
1255 retval = -EINVAL;
1256 }
1257 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1258 } else if (MATCH("q_diff_step_thresh")) {
1259 if (!retval)
1260 val = VALUE;
1261
1262 if (!retval) {
1263 if (val <= 0xff)
1264 new_params.compressionParams.qDiffStepThresh = val;
1265 else
1266 retval = -EINVAL;
1267 }
1268 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1269 } else if (MATCH("decimation_thresh_mod")) {
1270 if (!retval)
1271 val = VALUE;
1272
1273 if (!retval) {
1274 if (val <= 0xff)
1275 new_params.compressionParams.decimationThreshMod = val;
1276 else
1277 retval = -EINVAL;
1278 }
1279 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1280 } else if (MATCH("toplight")) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001281 if (!retval && MATCH("on"))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282 new_params.qx3.toplight = 1;
1283 else if (!retval && MATCH("off"))
1284 new_params.qx3.toplight = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001285 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286 retval = -EINVAL;
1287 command_flags |= COMMAND_SETLIGHTS;
1288 } else if (MATCH("bottomlight")) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001289 if (!retval && MATCH("on"))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290 new_params.qx3.bottomlight = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001291 else if (!retval && MATCH("off"))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001292 new_params.qx3.bottomlight = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001293 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001294 retval = -EINVAL;
1295 command_flags |= COMMAND_SETLIGHTS;
1296 } else {
1297 DBG("No match found\n");
1298 retval = -EINVAL;
1299 }
1300
1301 if (!retval) {
1302 while (count && isspace(*buffer) && *buffer != '\n') {
1303 --count;
1304 ++buffer;
1305 }
1306 if (count) {
1307 if (*buffer == '\0' && count != 1)
1308 retval = -EINVAL;
1309 else if (*buffer != '\n' && *buffer != ';' &&
1310 *buffer != '\0')
1311 retval = -EINVAL;
1312 else {
1313 --count;
1314 ++buffer;
1315 }
1316 }
1317 }
1318 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001319#undef MATCH
Linus Torvalds1da177e2005-04-16 15:20:36 -07001320#undef VALUE
1321#undef FIRMWARE_VERSION
1322 if (!retval) {
1323 if (command_flags & COMMAND_SETCOLOURPARAMS) {
1324 /* Adjust cam->vp to reflect these changes */
1325 cam->vp.brightness =
1326 new_params.colourParams.brightness*65535/100;
1327 cam->vp.contrast =
1328 new_params.colourParams.contrast*65535/100;
1329 cam->vp.colour =
1330 new_params.colourParams.saturation*65535/100;
1331 }
1332 if((command_flags & COMMAND_SETEXPOSURE) &&
1333 new_params.exposure.expMode == 2)
1334 cam->exposure_status = EXPOSURE_NORMAL;
1335
1336 memcpy(&cam->params, &new_params, sizeof(struct cam_params));
1337 cam->mainsFreq = new_mains;
1338 cam->cmd_queue |= command_flags;
1339 retval = size;
1340 } else
1341 DBG("error: %d\n", retval);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001342
Ingo Molnar3593cab2006-02-07 06:49:14 -02001343 mutex_unlock(&cam->param_lock);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001344
Linus Torvalds1da177e2005-04-16 15:20:36 -07001345out:
1346 free_page((unsigned long)page);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001347 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001348}
1349
1350static void create_proc_cpia_cam(struct cam_data *cam)
1351{
Alexey Dobriyan13071f02007-01-14 15:29:42 -03001352 char name[5 + 1 + 10 + 1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001353 struct proc_dir_entry *ent;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001354
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355 if (!cpia_proc_root || !cam)
1356 return;
1357
Alexey Dobriyan13071f02007-01-14 15:29:42 -03001358 snprintf(name, sizeof(name), "video%d", cam->vdev.minor);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001359
Linus Torvalds1da177e2005-04-16 15:20:36 -07001360 ent = create_proc_entry(name, S_IFREG|S_IRUGO|S_IWUSR, cpia_proc_root);
1361 if (!ent)
1362 return;
1363
1364 ent->data = cam;
1365 ent->read_proc = cpia_read_proc;
1366 ent->write_proc = cpia_write_proc;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001367 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368 size of the proc entry is 3736 bytes for the standard webcam;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001369 the extra features of the QX3 microscope add 189 bytes.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370 (we have not yet probed the camera to see which type it is).
1371 */
1372 ent->size = 3736 + 189;
1373 cam->proc_entry = ent;
1374}
1375
1376static void destroy_proc_cpia_cam(struct cam_data *cam)
1377{
Alexey Dobriyan13071f02007-01-14 15:29:42 -03001378 char name[5 + 1 + 10 + 1];
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001379
Linus Torvalds1da177e2005-04-16 15:20:36 -07001380 if (!cam || !cam->proc_entry)
1381 return;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001382
Alexey Dobriyan13071f02007-01-14 15:29:42 -03001383 snprintf(name, sizeof(name), "video%d", cam->vdev.minor);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001384 remove_proc_entry(name, cpia_proc_root);
1385 cam->proc_entry = NULL;
1386}
1387
1388static void proc_cpia_create(void)
1389{
Al Viro66600222005-09-28 22:32:57 +01001390 cpia_proc_root = proc_mkdir("cpia", NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001391
1392 if (cpia_proc_root)
1393 cpia_proc_root->owner = THIS_MODULE;
1394 else
1395 LOG("Unable to initialise /proc/cpia\n");
1396}
1397
1398static void __exit proc_cpia_destroy(void)
1399{
1400 remove_proc_entry("cpia", NULL);
1401}
1402#endif /* CONFIG_PROC_FS */
1403
1404/* ----------------------- debug functions ---------------------- */
1405
1406#define printstatus(cam) \
1407 DBG("%02x %02x %02x %02x %02x %02x %02x %02x\n",\
1408 cam->params.status.systemState, cam->params.status.grabState, \
1409 cam->params.status.streamState, cam->params.status.fatalError, \
1410 cam->params.status.cmdError, cam->params.status.debugFlags, \
1411 cam->params.status.vpStatus, cam->params.status.errorCode);
1412
1413/* ----------------------- v4l helpers -------------------------- */
1414
1415/* supported frame palettes and depths */
1416static inline int valid_mode(u16 palette, u16 depth)
1417{
1418 if ((palette == VIDEO_PALETTE_YUV422 && depth == 16) ||
1419 (palette == VIDEO_PALETTE_YUYV && depth == 16))
1420 return 1;
1421
1422 if (colorspace_conv)
1423 return (palette == VIDEO_PALETTE_GREY && depth == 8) ||
1424 (palette == VIDEO_PALETTE_RGB555 && depth == 16) ||
1425 (palette == VIDEO_PALETTE_RGB565 && depth == 16) ||
1426 (palette == VIDEO_PALETTE_RGB24 && depth == 24) ||
1427 (palette == VIDEO_PALETTE_RGB32 && depth == 32) ||
1428 (palette == VIDEO_PALETTE_UYVY && depth == 16);
1429
1430 return 0;
1431}
1432
1433static int match_videosize( int width, int height )
1434{
1435 /* return the best match, where 'best' is as always
1436 * the largest that is not bigger than what is requested. */
1437 if (width>=352 && height>=288)
1438 return VIDEOSIZE_352_288; /* CIF */
1439
1440 if (width>=320 && height>=240)
1441 return VIDEOSIZE_320_240; /* SIF */
1442
1443 if (width>=288 && height>=216)
1444 return VIDEOSIZE_288_216;
1445
1446 if (width>=256 && height>=192)
1447 return VIDEOSIZE_256_192;
1448
1449 if (width>=224 && height>=168)
1450 return VIDEOSIZE_224_168;
1451
1452 if (width>=192 && height>=144)
1453 return VIDEOSIZE_192_144;
1454
1455 if (width>=176 && height>=144)
1456 return VIDEOSIZE_176_144; /* QCIF */
1457
1458 if (width>=160 && height>=120)
1459 return VIDEOSIZE_160_120; /* QSIF */
1460
1461 if (width>=128 && height>=96)
1462 return VIDEOSIZE_128_96;
1463
1464 if (width>=88 && height>=72)
1465 return VIDEOSIZE_88_72;
1466
1467 if (width>=64 && height>=48)
1468 return VIDEOSIZE_64_48;
1469
1470 if (width>=48 && height>=48)
1471 return VIDEOSIZE_48_48;
1472
1473 return -1;
1474}
1475
1476/* these are the capture sizes we support */
1477static void set_vw_size(struct cam_data *cam)
1478{
1479 /* the col/row/start/end values are the result of simple math */
1480 /* study the SetROI-command in cpia developers guide p 2-22 */
1481 /* streamStartLine is set to the recommended value in the cpia */
1482 /* developers guide p 3-37 */
1483 switch(cam->video_size) {
1484 case VIDEOSIZE_CIF:
1485 cam->vw.width = 352;
1486 cam->vw.height = 288;
1487 cam->params.format.videoSize=VIDEOSIZE_CIF;
1488 cam->params.roi.colStart=0;
1489 cam->params.roi.rowStart=0;
1490 cam->params.streamStartLine = 120;
1491 break;
1492 case VIDEOSIZE_SIF:
1493 cam->vw.width = 320;
1494 cam->vw.height = 240;
1495 cam->params.format.videoSize=VIDEOSIZE_CIF;
1496 cam->params.roi.colStart=2;
1497 cam->params.roi.rowStart=6;
1498 cam->params.streamStartLine = 120;
1499 break;
1500 case VIDEOSIZE_288_216:
1501 cam->vw.width = 288;
1502 cam->vw.height = 216;
1503 cam->params.format.videoSize=VIDEOSIZE_CIF;
1504 cam->params.roi.colStart=4;
1505 cam->params.roi.rowStart=9;
1506 cam->params.streamStartLine = 120;
1507 break;
1508 case VIDEOSIZE_256_192:
1509 cam->vw.width = 256;
1510 cam->vw.height = 192;
1511 cam->params.format.videoSize=VIDEOSIZE_CIF;
1512 cam->params.roi.colStart=6;
1513 cam->params.roi.rowStart=12;
1514 cam->params.streamStartLine = 120;
1515 break;
1516 case VIDEOSIZE_224_168:
1517 cam->vw.width = 224;
1518 cam->vw.height = 168;
1519 cam->params.format.videoSize=VIDEOSIZE_CIF;
1520 cam->params.roi.colStart=8;
1521 cam->params.roi.rowStart=15;
1522 cam->params.streamStartLine = 120;
1523 break;
1524 case VIDEOSIZE_192_144:
1525 cam->vw.width = 192;
1526 cam->vw.height = 144;
1527 cam->params.format.videoSize=VIDEOSIZE_CIF;
1528 cam->params.roi.colStart=10;
1529 cam->params.roi.rowStart=18;
1530 cam->params.streamStartLine = 120;
1531 break;
1532 case VIDEOSIZE_QCIF:
1533 cam->vw.width = 176;
1534 cam->vw.height = 144;
1535 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1536 cam->params.roi.colStart=0;
1537 cam->params.roi.rowStart=0;
1538 cam->params.streamStartLine = 60;
1539 break;
1540 case VIDEOSIZE_QSIF:
1541 cam->vw.width = 160;
1542 cam->vw.height = 120;
1543 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1544 cam->params.roi.colStart=1;
1545 cam->params.roi.rowStart=3;
1546 cam->params.streamStartLine = 60;
1547 break;
1548 case VIDEOSIZE_128_96:
1549 cam->vw.width = 128;
1550 cam->vw.height = 96;
1551 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1552 cam->params.roi.colStart=3;
1553 cam->params.roi.rowStart=6;
1554 cam->params.streamStartLine = 60;
1555 break;
1556 case VIDEOSIZE_88_72:
1557 cam->vw.width = 88;
1558 cam->vw.height = 72;
1559 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1560 cam->params.roi.colStart=5;
1561 cam->params.roi.rowStart=9;
1562 cam->params.streamStartLine = 60;
1563 break;
1564 case VIDEOSIZE_64_48:
1565 cam->vw.width = 64;
1566 cam->vw.height = 48;
1567 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1568 cam->params.roi.colStart=7;
1569 cam->params.roi.rowStart=12;
1570 cam->params.streamStartLine = 60;
1571 break;
1572 case VIDEOSIZE_48_48:
1573 cam->vw.width = 48;
1574 cam->vw.height = 48;
1575 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1576 cam->params.roi.colStart=8;
1577 cam->params.roi.rowStart=6;
1578 cam->params.streamStartLine = 60;
1579 break;
1580 default:
1581 LOG("bad videosize value: %d\n", cam->video_size);
1582 return;
1583 }
1584
1585 if(cam->vc.width == 0)
1586 cam->vc.width = cam->vw.width;
1587 if(cam->vc.height == 0)
1588 cam->vc.height = cam->vw.height;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001589
Linus Torvalds1da177e2005-04-16 15:20:36 -07001590 cam->params.roi.colStart += cam->vc.x >> 3;
1591 cam->params.roi.colEnd = cam->params.roi.colStart +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001592 (cam->vc.width >> 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001593 cam->params.roi.rowStart += cam->vc.y >> 2;
1594 cam->params.roi.rowEnd = cam->params.roi.rowStart +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001595 (cam->vc.height >> 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001596
1597 return;
1598}
1599
1600static int allocate_frame_buf(struct cam_data *cam)
1601{
1602 int i;
1603
1604 cam->frame_buf = rvmalloc(FRAME_NUM * CPIA_MAX_FRAME_SIZE);
1605 if (!cam->frame_buf)
1606 return -ENOBUFS;
1607
1608 for (i = 0; i < FRAME_NUM; i++)
1609 cam->frame[i].data = cam->frame_buf + i * CPIA_MAX_FRAME_SIZE;
1610
1611 return 0;
1612}
1613
1614static int free_frame_buf(struct cam_data *cam)
1615{
1616 int i;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001617
Linus Torvalds1da177e2005-04-16 15:20:36 -07001618 rvfree(cam->frame_buf, FRAME_NUM*CPIA_MAX_FRAME_SIZE);
1619 cam->frame_buf = NULL;
1620 for (i=0; i < FRAME_NUM; i++)
1621 cam->frame[i].data = NULL;
1622
1623 return 0;
1624}
1625
1626
1627static inline void free_frames(struct cpia_frame frame[FRAME_NUM])
1628{
1629 int i;
1630
1631 for (i=0; i < FRAME_NUM; i++)
1632 frame[i].state = FRAME_UNUSED;
1633 return;
1634}
1635
1636/**********************************************************************
1637 *
1638 * General functions
1639 *
1640 **********************************************************************/
1641/* send an arbitrary command to the camera */
1642static int do_command(struct cam_data *cam, u16 command, u8 a, u8 b, u8 c, u8 d)
1643{
1644 int retval, datasize;
1645 u8 cmd[8], data[8];
1646
1647 switch(command) {
1648 case CPIA_COMMAND_GetCPIAVersion:
1649 case CPIA_COMMAND_GetPnPID:
1650 case CPIA_COMMAND_GetCameraStatus:
1651 case CPIA_COMMAND_GetVPVersion:
1652 datasize=8;
1653 break;
1654 case CPIA_COMMAND_GetColourParams:
1655 case CPIA_COMMAND_GetColourBalance:
1656 case CPIA_COMMAND_GetExposure:
Ingo Molnar3593cab2006-02-07 06:49:14 -02001657 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001658 datasize=8;
1659 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001660 case CPIA_COMMAND_ReadMCPorts:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001661 case CPIA_COMMAND_ReadVCRegs:
1662 datasize = 4;
1663 break;
1664 default:
1665 datasize=0;
1666 break;
1667 }
1668
1669 cmd[0] = command>>8;
1670 cmd[1] = command&0xff;
1671 cmd[2] = a;
1672 cmd[3] = b;
1673 cmd[4] = c;
1674 cmd[5] = d;
1675 cmd[6] = datasize;
1676 cmd[7] = 0;
1677
1678 retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
1679 if (retval) {
1680 DBG("%x - failed, retval=%d\n", command, retval);
1681 if (command == CPIA_COMMAND_GetColourParams ||
1682 command == CPIA_COMMAND_GetColourBalance ||
1683 command == CPIA_COMMAND_GetExposure)
Ingo Molnar3593cab2006-02-07 06:49:14 -02001684 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001685 } else {
1686 switch(command) {
1687 case CPIA_COMMAND_GetCPIAVersion:
1688 cam->params.version.firmwareVersion = data[0];
1689 cam->params.version.firmwareRevision = data[1];
1690 cam->params.version.vcVersion = data[2];
1691 cam->params.version.vcRevision = data[3];
1692 break;
1693 case CPIA_COMMAND_GetPnPID:
1694 cam->params.pnpID.vendor = data[0]+(((u16)data[1])<<8);
1695 cam->params.pnpID.product = data[2]+(((u16)data[3])<<8);
1696 cam->params.pnpID.deviceRevision =
1697 data[4]+(((u16)data[5])<<8);
1698 break;
1699 case CPIA_COMMAND_GetCameraStatus:
1700 cam->params.status.systemState = data[0];
1701 cam->params.status.grabState = data[1];
1702 cam->params.status.streamState = data[2];
1703 cam->params.status.fatalError = data[3];
1704 cam->params.status.cmdError = data[4];
1705 cam->params.status.debugFlags = data[5];
1706 cam->params.status.vpStatus = data[6];
1707 cam->params.status.errorCode = data[7];
1708 break;
1709 case CPIA_COMMAND_GetVPVersion:
1710 cam->params.vpVersion.vpVersion = data[0];
1711 cam->params.vpVersion.vpRevision = data[1];
1712 cam->params.vpVersion.cameraHeadID =
1713 data[2]+(((u16)data[3])<<8);
1714 break;
1715 case CPIA_COMMAND_GetColourParams:
1716 cam->params.colourParams.brightness = data[0];
1717 cam->params.colourParams.contrast = data[1];
1718 cam->params.colourParams.saturation = data[2];
Ingo Molnar3593cab2006-02-07 06:49:14 -02001719 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001720 break;
1721 case CPIA_COMMAND_GetColourBalance:
1722 cam->params.colourBalance.redGain = data[0];
1723 cam->params.colourBalance.greenGain = data[1];
1724 cam->params.colourBalance.blueGain = data[2];
Ingo Molnar3593cab2006-02-07 06:49:14 -02001725 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001726 break;
1727 case CPIA_COMMAND_GetExposure:
1728 cam->params.exposure.gain = data[0];
1729 cam->params.exposure.fineExp = data[1];
1730 cam->params.exposure.coarseExpLo = data[2];
1731 cam->params.exposure.coarseExpHi = data[3];
1732 cam->params.exposure.redComp = data[4];
1733 cam->params.exposure.green1Comp = data[5];
1734 cam->params.exposure.green2Comp = data[6];
1735 cam->params.exposure.blueComp = data[7];
Ingo Molnar3593cab2006-02-07 06:49:14 -02001736 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001737 break;
1738
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001739 case CPIA_COMMAND_ReadMCPorts:
1740 if (!cam->params.qx3.qx3_detected)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001741 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001742 /* test button press */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001743 cam->params.qx3.button = ((data[1] & 0x02) == 0);
1744 if (cam->params.qx3.button) {
1745 /* button pressed - unlock the latch */
1746 do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xDF,0xDF,0);
1747 do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xFF,0xFF,0);
1748 }
1749
1750 /* test whether microscope is cradled */
1751 cam->params.qx3.cradled = ((data[2] & 0x40) == 0);
1752 break;
1753
1754 default:
1755 break;
1756 }
1757 }
1758 return retval;
1759}
1760
1761/* send a command to the camera with an additional data transaction */
1762static int do_command_extended(struct cam_data *cam, u16 command,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001763 u8 a, u8 b, u8 c, u8 d,
1764 u8 e, u8 f, u8 g, u8 h,
1765 u8 i, u8 j, u8 k, u8 l)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001766{
1767 int retval;
1768 u8 cmd[8], data[8];
1769
1770 cmd[0] = command>>8;
1771 cmd[1] = command&0xff;
1772 cmd[2] = a;
1773 cmd[3] = b;
1774 cmd[4] = c;
1775 cmd[5] = d;
1776 cmd[6] = 8;
1777 cmd[7] = 0;
1778 data[0] = e;
1779 data[1] = f;
1780 data[2] = g;
1781 data[3] = h;
1782 data[4] = i;
1783 data[5] = j;
1784 data[6] = k;
1785 data[7] = l;
1786
1787 retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
1788 if (retval)
1789 DBG("%x - failed\n", command);
1790
1791 return retval;
1792}
1793
1794/**********************************************************************
1795 *
1796 * Colorspace conversion
1797 *
1798 **********************************************************************/
1799#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16)
1800
1801static int convert420(unsigned char *yuv, unsigned char *rgb, int out_fmt,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001802 int linesize, int mmap_kludge)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001803{
1804 int y, u, v, r, g, b, y1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001805
Linus Torvalds1da177e2005-04-16 15:20:36 -07001806 /* Odd lines use the same u and v as the previous line.
1807 * Because of compression, it is necessary to get this
1808 * information from the decoded image. */
1809 switch(out_fmt) {
1810 case VIDEO_PALETTE_RGB555:
1811 y = (*yuv++ - 16) * 76310;
1812 y1 = (*yuv - 16) * 76310;
1813 r = ((*(rgb+1-linesize)) & 0x7c) << 1;
1814 g = ((*(rgb-linesize)) & 0xe0) >> 4 |
1815 ((*(rgb+1-linesize)) & 0x03) << 6;
1816 b = ((*(rgb-linesize)) & 0x1f) << 3;
1817 u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
1818 v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
1819 r = 104635 * v;
1820 g = -25690 * u - 53294 * v;
1821 b = 132278 * u;
1822 *rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3);
1823 *rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6);
1824 *rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3);
1825 *rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6);
1826 return 4;
1827 case VIDEO_PALETTE_RGB565:
1828 y = (*yuv++ - 16) * 76310;
1829 y1 = (*yuv - 16) * 76310;
1830 r = (*(rgb+1-linesize)) & 0xf8;
1831 g = ((*(rgb-linesize)) & 0xe0) >> 3 |
1832 ((*(rgb+1-linesize)) & 0x07) << 5;
1833 b = ((*(rgb-linesize)) & 0x1f) << 3;
1834 u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
1835 v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
1836 r = 104635 * v;
1837 g = -25690 * u - 53294 * v;
1838 b = 132278 * u;
1839 *rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3);
1840 *rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5);
1841 *rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3);
1842 *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5);
1843 return 4;
1844 break;
1845 case VIDEO_PALETTE_RGB24:
1846 case VIDEO_PALETTE_RGB32:
1847 y = (*yuv++ - 16) * 76310;
1848 y1 = (*yuv - 16) * 76310;
1849 if (mmap_kludge) {
1850 r = *(rgb+2-linesize);
1851 g = *(rgb+1-linesize);
1852 b = *(rgb-linesize);
1853 } else {
1854 r = *(rgb-linesize);
1855 g = *(rgb+1-linesize);
1856 b = *(rgb+2-linesize);
1857 }
1858 u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
1859 v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
1860 r = 104635 * v;
1861 g = -25690 * u + -53294 * v;
1862 b = 132278 * u;
1863 if (mmap_kludge) {
1864 *rgb++ = LIMIT(b+y);
1865 *rgb++ = LIMIT(g+y);
1866 *rgb++ = LIMIT(r+y);
1867 if(out_fmt == VIDEO_PALETTE_RGB32)
1868 rgb++;
1869 *rgb++ = LIMIT(b+y1);
1870 *rgb++ = LIMIT(g+y1);
1871 *rgb = LIMIT(r+y1);
1872 } else {
1873 *rgb++ = LIMIT(r+y);
1874 *rgb++ = LIMIT(g+y);
1875 *rgb++ = LIMIT(b+y);
1876 if(out_fmt == VIDEO_PALETTE_RGB32)
1877 rgb++;
1878 *rgb++ = LIMIT(r+y1);
1879 *rgb++ = LIMIT(g+y1);
1880 *rgb = LIMIT(b+y1);
1881 }
1882 if(out_fmt == VIDEO_PALETTE_RGB32)
1883 return 8;
1884 return 6;
1885 case VIDEO_PALETTE_YUV422:
1886 case VIDEO_PALETTE_YUYV:
1887 y = *yuv++;
1888 u = *(rgb+1-linesize);
1889 y1 = *yuv;
1890 v = *(rgb+3-linesize);
1891 *rgb++ = y;
1892 *rgb++ = u;
1893 *rgb++ = y1;
1894 *rgb = v;
1895 return 4;
1896 case VIDEO_PALETTE_UYVY:
1897 u = *(rgb-linesize);
1898 y = *yuv++;
1899 v = *(rgb+2-linesize);
1900 y1 = *yuv;
1901 *rgb++ = u;
1902 *rgb++ = y;
1903 *rgb++ = v;
1904 *rgb = y1;
1905 return 4;
1906 case VIDEO_PALETTE_GREY:
1907 *rgb++ = *yuv++;
1908 *rgb = *yuv;
1909 return 2;
1910 default:
1911 DBG("Empty: %d\n", out_fmt);
1912 return 0;
1913 }
1914}
1915
1916
1917static int yuvconvert(unsigned char *yuv, unsigned char *rgb, int out_fmt,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001918 int in_uyvy, int mmap_kludge)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001919{
1920 int y, u, v, r, g, b, y1;
1921
1922 switch(out_fmt) {
1923 case VIDEO_PALETTE_RGB555:
1924 case VIDEO_PALETTE_RGB565:
1925 case VIDEO_PALETTE_RGB24:
1926 case VIDEO_PALETTE_RGB32:
1927 if (in_uyvy) {
1928 u = *yuv++ - 128;
1929 y = (*yuv++ - 16) * 76310;
1930 v = *yuv++ - 128;
1931 y1 = (*yuv - 16) * 76310;
1932 } else {
1933 y = (*yuv++ - 16) * 76310;
1934 u = *yuv++ - 128;
1935 y1 = (*yuv++ - 16) * 76310;
1936 v = *yuv - 128;
1937 }
1938 r = 104635 * v;
1939 g = -25690 * u + -53294 * v;
1940 b = 132278 * u;
1941 break;
1942 default:
1943 y = *yuv++;
1944 u = *yuv++;
1945 y1 = *yuv++;
1946 v = *yuv;
1947 /* Just to avoid compiler warnings */
1948 r = 0;
1949 g = 0;
1950 b = 0;
1951 break;
1952 }
1953 switch(out_fmt) {
1954 case VIDEO_PALETTE_RGB555:
1955 *rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3);
1956 *rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6);
1957 *rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3);
1958 *rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6);
1959 return 4;
1960 case VIDEO_PALETTE_RGB565:
1961 *rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3);
1962 *rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5);
1963 *rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3);
1964 *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5);
1965 return 4;
1966 case VIDEO_PALETTE_RGB24:
1967 if (mmap_kludge) {
1968 *rgb++ = LIMIT(b+y);
1969 *rgb++ = LIMIT(g+y);
1970 *rgb++ = LIMIT(r+y);
1971 *rgb++ = LIMIT(b+y1);
1972 *rgb++ = LIMIT(g+y1);
1973 *rgb = LIMIT(r+y1);
1974 } else {
1975 *rgb++ = LIMIT(r+y);
1976 *rgb++ = LIMIT(g+y);
1977 *rgb++ = LIMIT(b+y);
1978 *rgb++ = LIMIT(r+y1);
1979 *rgb++ = LIMIT(g+y1);
1980 *rgb = LIMIT(b+y1);
1981 }
1982 return 6;
1983 case VIDEO_PALETTE_RGB32:
1984 if (mmap_kludge) {
1985 *rgb++ = LIMIT(b+y);
1986 *rgb++ = LIMIT(g+y);
1987 *rgb++ = LIMIT(r+y);
1988 rgb++;
1989 *rgb++ = LIMIT(b+y1);
1990 *rgb++ = LIMIT(g+y1);
1991 *rgb = LIMIT(r+y1);
1992 } else {
1993 *rgb++ = LIMIT(r+y);
1994 *rgb++ = LIMIT(g+y);
1995 *rgb++ = LIMIT(b+y);
1996 rgb++;
1997 *rgb++ = LIMIT(r+y1);
1998 *rgb++ = LIMIT(g+y1);
1999 *rgb = LIMIT(b+y1);
2000 }
2001 return 8;
2002 case VIDEO_PALETTE_GREY:
2003 *rgb++ = y;
2004 *rgb = y1;
2005 return 2;
2006 case VIDEO_PALETTE_YUV422:
2007 case VIDEO_PALETTE_YUYV:
2008 *rgb++ = y;
2009 *rgb++ = u;
2010 *rgb++ = y1;
2011 *rgb = v;
2012 return 4;
2013 case VIDEO_PALETTE_UYVY:
2014 *rgb++ = u;
2015 *rgb++ = y;
2016 *rgb++ = v;
2017 *rgb = y1;
2018 return 4;
2019 default:
2020 DBG("Empty: %d\n", out_fmt);
2021 return 0;
2022 }
2023}
2024
2025static int skipcount(int count, int fmt)
2026{
2027 switch(fmt) {
2028 case VIDEO_PALETTE_GREY:
2029 return count;
2030 case VIDEO_PALETTE_RGB555:
2031 case VIDEO_PALETTE_RGB565:
2032 case VIDEO_PALETTE_YUV422:
2033 case VIDEO_PALETTE_YUYV:
2034 case VIDEO_PALETTE_UYVY:
2035 return 2*count;
2036 case VIDEO_PALETTE_RGB24:
2037 return 3*count;
2038 case VIDEO_PALETTE_RGB32:
2039 return 4*count;
2040 default:
2041 return 0;
2042 }
2043}
2044
2045static int parse_picture(struct cam_data *cam, int size)
2046{
2047 u8 *obuf, *ibuf, *end_obuf;
2048 int ll, in_uyvy, compressed, decimation, even_line, origsize, out_fmt;
2049 int rows, cols, linesize, subsample_422;
2050
2051 /* make sure params don't change while we are decoding */
Ingo Molnar3593cab2006-02-07 06:49:14 -02002052 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002053
2054 obuf = cam->decompressed_frame.data;
2055 end_obuf = obuf+CPIA_MAX_FRAME_SIZE;
2056 ibuf = cam->raw_image;
2057 origsize = size;
2058 out_fmt = cam->vp.palette;
2059
2060 if ((ibuf[0] != MAGIC_0) || (ibuf[1] != MAGIC_1)) {
2061 LOG("header not found\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02002062 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002063 return -1;
2064 }
2065
2066 if ((ibuf[16] != VIDEOSIZE_QCIF) && (ibuf[16] != VIDEOSIZE_CIF)) {
2067 LOG("wrong video size\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02002068 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002069 return -1;
2070 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002071
Linus Torvalds1da177e2005-04-16 15:20:36 -07002072 if (ibuf[17] != SUBSAMPLE_420 && ibuf[17] != SUBSAMPLE_422) {
2073 LOG("illegal subtype %d\n",ibuf[17]);
Ingo Molnar3593cab2006-02-07 06:49:14 -02002074 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002075 return -1;
2076 }
2077 subsample_422 = ibuf[17] == SUBSAMPLE_422;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002078
Linus Torvalds1da177e2005-04-16 15:20:36 -07002079 if (ibuf[18] != YUVORDER_YUYV && ibuf[18] != YUVORDER_UYVY) {
2080 LOG("illegal yuvorder %d\n",ibuf[18]);
Ingo Molnar3593cab2006-02-07 06:49:14 -02002081 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002082 return -1;
2083 }
2084 in_uyvy = ibuf[18] == YUVORDER_UYVY;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002085
Linus Torvalds1da177e2005-04-16 15:20:36 -07002086 if ((ibuf[24] != cam->params.roi.colStart) ||
2087 (ibuf[25] != cam->params.roi.colEnd) ||
2088 (ibuf[26] != cam->params.roi.rowStart) ||
2089 (ibuf[27] != cam->params.roi.rowEnd)) {
2090 LOG("ROI mismatch\n");
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 cols = 8*(ibuf[25] - ibuf[24]);
2095 rows = 4*(ibuf[27] - ibuf[26]);
2096
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002097
Linus Torvalds1da177e2005-04-16 15:20:36 -07002098 if ((ibuf[28] != NOT_COMPRESSED) && (ibuf[28] != COMPRESSED)) {
2099 LOG("illegal compression %d\n",ibuf[28]);
Ingo Molnar3593cab2006-02-07 06:49:14 -02002100 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002101 return -1;
2102 }
2103 compressed = (ibuf[28] == COMPRESSED);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002104
Linus Torvalds1da177e2005-04-16 15:20:36 -07002105 if (ibuf[29] != NO_DECIMATION && ibuf[29] != DECIMATION_ENAB) {
2106 LOG("illegal decimation %d\n",ibuf[29]);
Ingo Molnar3593cab2006-02-07 06:49:14 -02002107 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002108 return -1;
2109 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002110 decimation = (ibuf[29] == DECIMATION_ENAB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002111
2112 cam->params.yuvThreshold.yThreshold = ibuf[30];
2113 cam->params.yuvThreshold.uvThreshold = ibuf[31];
2114 cam->params.status.systemState = ibuf[32];
2115 cam->params.status.grabState = ibuf[33];
2116 cam->params.status.streamState = ibuf[34];
2117 cam->params.status.fatalError = ibuf[35];
2118 cam->params.status.cmdError = ibuf[36];
2119 cam->params.status.debugFlags = ibuf[37];
2120 cam->params.status.vpStatus = ibuf[38];
2121 cam->params.status.errorCode = ibuf[39];
2122 cam->fps = ibuf[41];
Ingo Molnar3593cab2006-02-07 06:49:14 -02002123 mutex_unlock(&cam->param_lock);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002124
Linus Torvalds1da177e2005-04-16 15:20:36 -07002125 linesize = skipcount(cols, out_fmt);
2126 ibuf += FRAME_HEADER_SIZE;
2127 size -= FRAME_HEADER_SIZE;
2128 ll = ibuf[0] | (ibuf[1] << 8);
2129 ibuf += 2;
2130 even_line = 1;
2131
2132 while (size > 0) {
2133 size -= (ll+2);
2134 if (size < 0) {
2135 LOG("Insufficient data in buffer\n");
2136 return -1;
2137 }
2138
2139 while (ll > 1) {
2140 if (!compressed || (compressed && !(*ibuf & 1))) {
2141 if(subsample_422 || even_line) {
2142 obuf += yuvconvert(ibuf, obuf, out_fmt,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002143 in_uyvy, cam->mmap_kludge);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002144 ibuf += 4;
2145 ll -= 4;
2146 } else {
2147 /* SUBSAMPLE_420 on an odd line */
2148 obuf += convert420(ibuf, obuf,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002149 out_fmt, linesize,
2150 cam->mmap_kludge);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002151 ibuf += 2;
2152 ll -= 2;
2153 }
2154 } else {
2155 /*skip compressed interval from previous frame*/
2156 obuf += skipcount(*ibuf >> 1, out_fmt);
2157 if (obuf > end_obuf) {
2158 LOG("Insufficient buffer size\n");
2159 return -1;
2160 }
2161 ++ibuf;
2162 ll--;
2163 }
2164 }
2165 if (ll == 1) {
2166 if (*ibuf != EOL) {
2167 DBG("EOL not found giving up after %d/%d"
2168 " bytes\n", origsize-size, origsize);
2169 return -1;
2170 }
2171
2172 ++ibuf; /* skip over EOL */
2173
2174 if ((size > 3) && (ibuf[0] == EOI) && (ibuf[1] == EOI) &&
2175 (ibuf[2] == EOI) && (ibuf[3] == EOI)) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002176 size -= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002177 break;
2178 }
2179
2180 if(decimation) {
2181 /* skip the odd lines for now */
2182 obuf += linesize;
2183 }
2184
2185 if (size > 1) {
2186 ll = ibuf[0] | (ibuf[1] << 8);
2187 ibuf += 2; /* skip over line length */
2188 }
2189 if(!decimation)
2190 even_line = !even_line;
2191 } else {
2192 LOG("line length was not 1 but %d after %d/%d bytes\n",
2193 ll, origsize-size, origsize);
2194 return -1;
2195 }
2196 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002197
Linus Torvalds1da177e2005-04-16 15:20:36 -07002198 if(decimation) {
2199 /* interpolate odd rows */
2200 int i, j;
2201 u8 *prev, *next;
2202 prev = cam->decompressed_frame.data;
2203 obuf = prev+linesize;
2204 next = obuf+linesize;
2205 for(i=1; i<rows-1; i+=2) {
2206 for(j=0; j<linesize; ++j) {
2207 *obuf++ = ((int)*prev++ + *next++) / 2;
2208 }
2209 prev += linesize;
2210 obuf += linesize;
2211 next += linesize;
2212 }
2213 /* last row is odd, just copy previous row */
2214 memcpy(obuf, prev, linesize);
2215 }
2216
2217 cam->decompressed_frame.count = obuf-cam->decompressed_frame.data;
2218
2219 return cam->decompressed_frame.count;
2220}
2221
2222/* InitStreamCap wrapper to select correct start line */
2223static inline int init_stream_cap(struct cam_data *cam)
2224{
2225 return do_command(cam, CPIA_COMMAND_InitStreamCap,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002226 0, cam->params.streamStartLine, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002227}
2228
2229
2230/* find_over_exposure
2231 * Finds a suitable value of OverExposure for use with SetFlickerCtrl
2232 * Some calculation is required because this value changes with the brightness
2233 * set with SetColourParameters
2234 *
2235 * Parameters: Brightness - last brightness value set with SetColourParameters
2236 *
2237 * Returns: OverExposure value to use with SetFlickerCtrl
2238 */
2239#define FLICKER_MAX_EXPOSURE 250
2240#define FLICKER_ALLOWABLE_OVER_EXPOSURE 146
2241#define FLICKER_BRIGHTNESS_CONSTANT 59
2242static int find_over_exposure(int brightness)
2243{
2244 int MaxAllowableOverExposure, OverExposure;
2245
2246 MaxAllowableOverExposure = FLICKER_MAX_EXPOSURE - brightness -
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002247 FLICKER_BRIGHTNESS_CONSTANT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002248
2249 if (MaxAllowableOverExposure < FLICKER_ALLOWABLE_OVER_EXPOSURE) {
2250 OverExposure = MaxAllowableOverExposure;
2251 } else {
2252 OverExposure = FLICKER_ALLOWABLE_OVER_EXPOSURE;
2253 }
2254
2255 return OverExposure;
2256}
2257#undef FLICKER_MAX_EXPOSURE
2258#undef FLICKER_ALLOWABLE_OVER_EXPOSURE
2259#undef FLICKER_BRIGHTNESS_CONSTANT
2260
2261/* update various camera modes and settings */
2262static void dispatch_commands(struct cam_data *cam)
2263{
Ingo Molnar3593cab2006-02-07 06:49:14 -02002264 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002265 if (cam->cmd_queue==COMMAND_NONE) {
Ingo Molnar3593cab2006-02-07 06:49:14 -02002266 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002267 return;
2268 }
2269 DEB_BYTE(cam->cmd_queue);
2270 DEB_BYTE(cam->cmd_queue>>8);
2271 if (cam->cmd_queue & COMMAND_SETFORMAT) {
2272 do_command(cam, CPIA_COMMAND_SetFormat,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002273 cam->params.format.videoSize,
2274 cam->params.format.subSample,
2275 cam->params.format.yuvOrder, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002276 do_command(cam, CPIA_COMMAND_SetROI,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002277 cam->params.roi.colStart, cam->params.roi.colEnd,
2278 cam->params.roi.rowStart, cam->params.roi.rowEnd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002279 cam->first_frame = 1;
2280 }
2281
2282 if (cam->cmd_queue & COMMAND_SETCOLOURPARAMS)
2283 do_command(cam, CPIA_COMMAND_SetColourParams,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002284 cam->params.colourParams.brightness,
2285 cam->params.colourParams.contrast,
2286 cam->params.colourParams.saturation, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002287
2288 if (cam->cmd_queue & COMMAND_SETAPCOR)
2289 do_command(cam, CPIA_COMMAND_SetApcor,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002290 cam->params.apcor.gain1,
2291 cam->params.apcor.gain2,
2292 cam->params.apcor.gain4,
2293 cam->params.apcor.gain8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002294
2295 if (cam->cmd_queue & COMMAND_SETVLOFFSET)
2296 do_command(cam, CPIA_COMMAND_SetVLOffset,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002297 cam->params.vlOffset.gain1,
2298 cam->params.vlOffset.gain2,
2299 cam->params.vlOffset.gain4,
2300 cam->params.vlOffset.gain8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002301
2302 if (cam->cmd_queue & COMMAND_SETEXPOSURE) {
2303 do_command_extended(cam, CPIA_COMMAND_SetExposure,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002304 cam->params.exposure.gainMode,
2305 1,
2306 cam->params.exposure.compMode,
2307 cam->params.exposure.centreWeight,
2308 cam->params.exposure.gain,
2309 cam->params.exposure.fineExp,
2310 cam->params.exposure.coarseExpLo,
2311 cam->params.exposure.coarseExpHi,
2312 cam->params.exposure.redComp,
2313 cam->params.exposure.green1Comp,
2314 cam->params.exposure.green2Comp,
2315 cam->params.exposure.blueComp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002316 if(cam->params.exposure.expMode != 1) {
2317 do_command_extended(cam, CPIA_COMMAND_SetExposure,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002318 0,
2319 cam->params.exposure.expMode,
2320 0, 0,
2321 cam->params.exposure.gain,
2322 cam->params.exposure.fineExp,
2323 cam->params.exposure.coarseExpLo,
2324 cam->params.exposure.coarseExpHi,
2325 0, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002326 }
2327 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002328
Linus Torvalds1da177e2005-04-16 15:20:36 -07002329 if (cam->cmd_queue & COMMAND_SETCOLOURBALANCE) {
2330 if (cam->params.colourBalance.balanceMode == 1) {
2331 do_command(cam, CPIA_COMMAND_SetColourBalance,
2332 1,
2333 cam->params.colourBalance.redGain,
2334 cam->params.colourBalance.greenGain,
2335 cam->params.colourBalance.blueGain);
2336 do_command(cam, CPIA_COMMAND_SetColourBalance,
2337 3, 0, 0, 0);
2338 }
2339 if (cam->params.colourBalance.balanceMode == 2) {
2340 do_command(cam, CPIA_COMMAND_SetColourBalance,
2341 2, 0, 0, 0);
2342 }
2343 if (cam->params.colourBalance.balanceMode == 3) {
2344 do_command(cam, CPIA_COMMAND_SetColourBalance,
2345 3, 0, 0, 0);
2346 }
2347 }
2348
2349 if (cam->cmd_queue & COMMAND_SETCOMPRESSIONTARGET)
2350 do_command(cam, CPIA_COMMAND_SetCompressionTarget,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002351 cam->params.compressionTarget.frTargeting,
2352 cam->params.compressionTarget.targetFR,
2353 cam->params.compressionTarget.targetQ, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002354
2355 if (cam->cmd_queue & COMMAND_SETYUVTHRESH)
2356 do_command(cam, CPIA_COMMAND_SetYUVThresh,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002357 cam->params.yuvThreshold.yThreshold,
2358 cam->params.yuvThreshold.uvThreshold, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002359
2360 if (cam->cmd_queue & COMMAND_SETCOMPRESSIONPARAMS)
2361 do_command_extended(cam, CPIA_COMMAND_SetCompressionParams,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002362 0, 0, 0, 0,
2363 cam->params.compressionParams.hysteresis,
2364 cam->params.compressionParams.threshMax,
2365 cam->params.compressionParams.smallStep,
2366 cam->params.compressionParams.largeStep,
2367 cam->params.compressionParams.decimationHysteresis,
2368 cam->params.compressionParams.frDiffStepThresh,
2369 cam->params.compressionParams.qDiffStepThresh,
2370 cam->params.compressionParams.decimationThreshMod);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002371
2372 if (cam->cmd_queue & COMMAND_SETCOMPRESSION)
2373 do_command(cam, CPIA_COMMAND_SetCompression,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002374 cam->params.compression.mode,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002375 cam->params.compression.decimation, 0, 0);
2376
2377 if (cam->cmd_queue & COMMAND_SETSENSORFPS)
2378 do_command(cam, CPIA_COMMAND_SetSensorFPS,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002379 cam->params.sensorFps.divisor,
2380 cam->params.sensorFps.baserate, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002381
2382 if (cam->cmd_queue & COMMAND_SETFLICKERCTRL)
2383 do_command(cam, CPIA_COMMAND_SetFlickerCtrl,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002384 cam->params.flickerControl.flickerMode,
2385 cam->params.flickerControl.coarseJump,
2386 abs(cam->params.flickerControl.allowableOverExposure),
2387 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002388
2389 if (cam->cmd_queue & COMMAND_SETECPTIMING)
2390 do_command(cam, CPIA_COMMAND_SetECPTiming,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002391 cam->params.ecpTiming, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002392
2393 if (cam->cmd_queue & COMMAND_PAUSE)
2394 do_command(cam, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0);
2395
2396 if (cam->cmd_queue & COMMAND_RESUME)
2397 init_stream_cap(cam);
2398
2399 if (cam->cmd_queue & COMMAND_SETLIGHTS && cam->params.qx3.qx3_detected)
2400 {
2401 int p1 = (cam->params.qx3.bottomlight == 0) << 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002402 int p2 = (cam->params.qx3.toplight == 0) << 3;
2403 do_command(cam, CPIA_COMMAND_WriteVCReg, 0x90, 0x8F, 0x50, 0);
2404 do_command(cam, CPIA_COMMAND_WriteMCPort, 2, 0, (p1|p2|0xE0), 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002405 }
2406
2407 cam->cmd_queue = COMMAND_NONE;
Ingo Molnar3593cab2006-02-07 06:49:14 -02002408 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002409 return;
2410}
2411
2412
2413
2414static void set_flicker(struct cam_params *params, volatile u32 *command_flags,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002415 int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002416{
2417 /* Everything in here is from the Windows driver */
2418#define FIRMWARE_VERSION(x,y) (params->version.firmwareVersion == (x) && \
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002419 params->version.firmwareRevision == (y))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002420/* define for compgain calculation */
2421#if 0
2422#define COMPGAIN(base, curexp, newexp) \
2423 (u8) ((((float) base - 128.0) * ((float) curexp / (float) newexp)) + 128.5)
2424#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
2425 (u16)((float)curexp * (float)(u8)(curcomp + 128) / (float)(u8)(basecomp - 128))
2426#else
2427 /* equivalent functions without floating point math */
2428#define COMPGAIN(base, curexp, newexp) \
2429 (u8)(128 + (((u32)(2*(base-128)*curexp + newexp)) / (2* newexp)) )
2430#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
2431 (u16)(((u32)(curexp * (u8)(curcomp + 128)) / (u8)(basecomp - 128)))
2432#endif
2433
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002434
Linus Torvalds1da177e2005-04-16 15:20:36 -07002435 int currentexp = params->exposure.coarseExpLo +
2436 params->exposure.coarseExpHi*256;
2437 int startexp;
2438 if (on) {
2439 int cj = params->flickerControl.coarseJump;
2440 params->flickerControl.flickerMode = 1;
2441 params->flickerControl.disabled = 0;
2442 if(params->exposure.expMode != 2)
2443 *command_flags |= COMMAND_SETEXPOSURE;
2444 params->exposure.expMode = 2;
2445 currentexp = currentexp << params->exposure.gain;
2446 params->exposure.gain = 0;
2447 /* round down current exposure to nearest value */
2448 startexp = (currentexp + ROUND_UP_EXP_FOR_FLICKER) / cj;
2449 if(startexp < 1)
2450 startexp = 1;
2451 startexp = (startexp * cj) - 1;
2452 if(FIRMWARE_VERSION(1,2))
2453 while(startexp > MAX_EXP_102)
2454 startexp -= cj;
2455 else
2456 while(startexp > MAX_EXP)
2457 startexp -= cj;
2458 params->exposure.coarseExpLo = startexp & 0xff;
2459 params->exposure.coarseExpHi = startexp >> 8;
2460 if (currentexp > startexp) {
2461 if (currentexp > (2 * startexp))
2462 currentexp = 2 * startexp;
2463 params->exposure.redComp = COMPGAIN (COMP_RED, currentexp, startexp);
2464 params->exposure.green1Comp = COMPGAIN (COMP_GREEN1, currentexp, startexp);
2465 params->exposure.green2Comp = COMPGAIN (COMP_GREEN2, currentexp, startexp);
2466 params->exposure.blueComp = COMPGAIN (COMP_BLUE, currentexp, startexp);
2467 } else {
2468 params->exposure.redComp = COMP_RED;
2469 params->exposure.green1Comp = COMP_GREEN1;
2470 params->exposure.green2Comp = COMP_GREEN2;
2471 params->exposure.blueComp = COMP_BLUE;
2472 }
2473 if(FIRMWARE_VERSION(1,2))
2474 params->exposure.compMode = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002475 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07002476 params->exposure.compMode = 1;
2477
2478 params->apcor.gain1 = 0x18;
2479 params->apcor.gain2 = 0x18;
2480 params->apcor.gain4 = 0x16;
2481 params->apcor.gain8 = 0x14;
2482 *command_flags |= COMMAND_SETAPCOR;
2483 } else {
2484 params->flickerControl.flickerMode = 0;
2485 params->flickerControl.disabled = 1;
2486 /* Coarse = average of equivalent coarse for each comp channel */
2487 startexp = EXP_FROM_COMP(COMP_RED, params->exposure.redComp, currentexp);
2488 startexp += EXP_FROM_COMP(COMP_GREEN1, params->exposure.green1Comp, currentexp);
2489 startexp += EXP_FROM_COMP(COMP_GREEN2, params->exposure.green2Comp, currentexp);
2490 startexp += EXP_FROM_COMP(COMP_BLUE, params->exposure.blueComp, currentexp);
2491 startexp = startexp >> 2;
2492 while(startexp > MAX_EXP &&
2493 params->exposure.gain < params->exposure.gainMode-1) {
2494 startexp = startexp >> 1;
2495 ++params->exposure.gain;
2496 }
2497 if(FIRMWARE_VERSION(1,2) && startexp > MAX_EXP_102)
2498 startexp = MAX_EXP_102;
2499 if(startexp > MAX_EXP)
2500 startexp = MAX_EXP;
2501 params->exposure.coarseExpLo = startexp&0xff;
2502 params->exposure.coarseExpHi = startexp >> 8;
2503 params->exposure.redComp = COMP_RED;
2504 params->exposure.green1Comp = COMP_GREEN1;
2505 params->exposure.green2Comp = COMP_GREEN2;
2506 params->exposure.blueComp = COMP_BLUE;
2507 params->exposure.compMode = 1;
2508 *command_flags |= COMMAND_SETEXPOSURE;
2509 params->apcor.gain1 = 0x18;
2510 params->apcor.gain2 = 0x16;
2511 params->apcor.gain4 = 0x24;
2512 params->apcor.gain8 = 0x34;
2513 *command_flags |= COMMAND_SETAPCOR;
2514 }
2515 params->vlOffset.gain1 = 20;
2516 params->vlOffset.gain2 = 24;
2517 params->vlOffset.gain4 = 26;
2518 params->vlOffset.gain8 = 26;
2519 *command_flags |= COMMAND_SETVLOFFSET;
2520#undef FIRMWARE_VERSION
2521#undef EXP_FROM_COMP
2522#undef COMPGAIN
2523}
2524
2525#define FIRMWARE_VERSION(x,y) (cam->params.version.firmwareVersion == (x) && \
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002526 cam->params.version.firmwareRevision == (y))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002527/* monitor the exposure and adjust the sensor frame rate if needed */
2528static void monitor_exposure(struct cam_data *cam)
2529{
2530 u8 exp_acc, bcomp, gain, coarseL, cmd[8], data[8];
2531 int retval, light_exp, dark_exp, very_dark_exp;
2532 int old_exposure, new_exposure, framerate;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002533
Linus Torvalds1da177e2005-04-16 15:20:36 -07002534 /* get necessary stats and register settings from camera */
2535 /* do_command can't handle this, so do it ourselves */
2536 cmd[0] = CPIA_COMMAND_ReadVPRegs>>8;
2537 cmd[1] = CPIA_COMMAND_ReadVPRegs&0xff;
2538 cmd[2] = 30;
2539 cmd[3] = 4;
2540 cmd[4] = 9;
2541 cmd[5] = 8;
2542 cmd[6] = 8;
2543 cmd[7] = 0;
2544 retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
2545 if (retval) {
2546 LOG("ReadVPRegs(30,4,9,8) - failed, retval=%d\n",
2547 retval);
2548 return;
2549 }
2550 exp_acc = data[0];
2551 bcomp = data[1];
2552 gain = data[2];
2553 coarseL = data[3];
2554
Ingo Molnar3593cab2006-02-07 06:49:14 -02002555 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002556 light_exp = cam->params.colourParams.brightness +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002557 TC - 50 + EXP_ACC_LIGHT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002558 if(light_exp > 255)
2559 light_exp = 255;
2560 dark_exp = cam->params.colourParams.brightness +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002561 TC - 50 - EXP_ACC_DARK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002562 if(dark_exp < 0)
2563 dark_exp = 0;
2564 very_dark_exp = dark_exp/2;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002565
Linus Torvalds1da177e2005-04-16 15:20:36 -07002566 old_exposure = cam->params.exposure.coarseExpHi * 256 +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002567 cam->params.exposure.coarseExpLo;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002568
2569 if(!cam->params.flickerControl.disabled) {
2570 /* Flicker control on */
2571 int max_comp = FIRMWARE_VERSION(1,2) ? MAX_COMP : HIGH_COMP_102;
2572 bcomp += 128; /* decode */
2573 if(bcomp >= max_comp && exp_acc < dark_exp) {
2574 /* dark */
2575 if(exp_acc < very_dark_exp) {
2576 /* very dark */
2577 if(cam->exposure_status == EXPOSURE_VERY_DARK)
2578 ++cam->exposure_count;
2579 else {
2580 cam->exposure_status = EXPOSURE_VERY_DARK;
2581 cam->exposure_count = 1;
2582 }
2583 } else {
2584 /* just dark */
2585 if(cam->exposure_status == EXPOSURE_DARK)
2586 ++cam->exposure_count;
2587 else {
2588 cam->exposure_status = EXPOSURE_DARK;
2589 cam->exposure_count = 1;
2590 }
2591 }
2592 } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) {
2593 /* light */
2594 if(old_exposure <= VERY_LOW_EXP) {
2595 /* very light */
2596 if(cam->exposure_status == EXPOSURE_VERY_LIGHT)
2597 ++cam->exposure_count;
2598 else {
2599 cam->exposure_status = EXPOSURE_VERY_LIGHT;
2600 cam->exposure_count = 1;
2601 }
2602 } else {
2603 /* just light */
2604 if(cam->exposure_status == EXPOSURE_LIGHT)
2605 ++cam->exposure_count;
2606 else {
2607 cam->exposure_status = EXPOSURE_LIGHT;
2608 cam->exposure_count = 1;
2609 }
2610 }
2611 } else {
2612 /* not dark or light */
2613 cam->exposure_status = EXPOSURE_NORMAL;
2614 }
2615 } else {
2616 /* Flicker control off */
2617 if(old_exposure >= MAX_EXP && exp_acc < dark_exp) {
2618 /* dark */
2619 if(exp_acc < very_dark_exp) {
2620 /* very dark */
2621 if(cam->exposure_status == EXPOSURE_VERY_DARK)
2622 ++cam->exposure_count;
2623 else {
2624 cam->exposure_status = EXPOSURE_VERY_DARK;
2625 cam->exposure_count = 1;
2626 }
2627 } else {
2628 /* just dark */
2629 if(cam->exposure_status == EXPOSURE_DARK)
2630 ++cam->exposure_count;
2631 else {
2632 cam->exposure_status = EXPOSURE_DARK;
2633 cam->exposure_count = 1;
2634 }
2635 }
2636 } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) {
2637 /* light */
2638 if(old_exposure <= VERY_LOW_EXP) {
2639 /* very light */
2640 if(cam->exposure_status == EXPOSURE_VERY_LIGHT)
2641 ++cam->exposure_count;
2642 else {
2643 cam->exposure_status = EXPOSURE_VERY_LIGHT;
2644 cam->exposure_count = 1;
2645 }
2646 } else {
2647 /* just light */
2648 if(cam->exposure_status == EXPOSURE_LIGHT)
2649 ++cam->exposure_count;
2650 else {
2651 cam->exposure_status = EXPOSURE_LIGHT;
2652 cam->exposure_count = 1;
2653 }
2654 }
2655 } else {
2656 /* not dark or light */
2657 cam->exposure_status = EXPOSURE_NORMAL;
2658 }
2659 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002660
Linus Torvalds1da177e2005-04-16 15:20:36 -07002661 framerate = cam->fps;
2662 if(framerate > 30 || framerate < 1)
2663 framerate = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002664
Linus Torvalds1da177e2005-04-16 15:20:36 -07002665 if(!cam->params.flickerControl.disabled) {
2666 /* Flicker control on */
2667 if((cam->exposure_status == EXPOSURE_VERY_DARK ||
2668 cam->exposure_status == EXPOSURE_DARK) &&
2669 cam->exposure_count >= DARK_TIME*framerate &&
2670 cam->params.sensorFps.divisor < 3) {
2671
2672 /* dark for too long */
2673 ++cam->params.sensorFps.divisor;
2674 cam->cmd_queue |= COMMAND_SETSENSORFPS;
2675
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002676 cam->params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07002677 flicker_jumps[cam->mainsFreq]
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002678 [cam->params.sensorFps.baserate]
2679 [cam->params.sensorFps.divisor];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002680 cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
2681
2682 new_exposure = cam->params.flickerControl.coarseJump-1;
2683 while(new_exposure < old_exposure/2)
2684 new_exposure += cam->params.flickerControl.coarseJump;
2685 cam->params.exposure.coarseExpLo = new_exposure & 0xff;
2686 cam->params.exposure.coarseExpHi = new_exposure >> 8;
2687 cam->cmd_queue |= COMMAND_SETEXPOSURE;
2688 cam->exposure_status = EXPOSURE_NORMAL;
2689 LOG("Automatically decreasing sensor_fps\n");
2690
2691 } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT ||
2692 cam->exposure_status == EXPOSURE_LIGHT) &&
2693 cam->exposure_count >= LIGHT_TIME*framerate &&
2694 cam->params.sensorFps.divisor > 0) {
2695
2696 /* light for too long */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002697 int max_exp = FIRMWARE_VERSION(1,2) ? MAX_EXP_102 : MAX_EXP ;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002698
2699 --cam->params.sensorFps.divisor;
2700 cam->cmd_queue |= COMMAND_SETSENSORFPS;
2701
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002702 cam->params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07002703 flicker_jumps[cam->mainsFreq]
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002704 [cam->params.sensorFps.baserate]
2705 [cam->params.sensorFps.divisor];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002706 cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
2707
2708 new_exposure = cam->params.flickerControl.coarseJump-1;
2709 while(new_exposure < 2*old_exposure &&
2710 new_exposure+
2711 cam->params.flickerControl.coarseJump < max_exp)
2712 new_exposure += cam->params.flickerControl.coarseJump;
2713 cam->params.exposure.coarseExpLo = new_exposure & 0xff;
2714 cam->params.exposure.coarseExpHi = new_exposure >> 8;
2715 cam->cmd_queue |= COMMAND_SETEXPOSURE;
2716 cam->exposure_status = EXPOSURE_NORMAL;
2717 LOG("Automatically increasing sensor_fps\n");
2718 }
2719 } else {
2720 /* Flicker control off */
2721 if((cam->exposure_status == EXPOSURE_VERY_DARK ||
2722 cam->exposure_status == EXPOSURE_DARK) &&
2723 cam->exposure_count >= DARK_TIME*framerate &&
2724 cam->params.sensorFps.divisor < 3) {
2725
2726 /* dark for too long */
2727 ++cam->params.sensorFps.divisor;
2728 cam->cmd_queue |= COMMAND_SETSENSORFPS;
2729
2730 if(cam->params.exposure.gain > 0) {
2731 --cam->params.exposure.gain;
2732 cam->cmd_queue |= COMMAND_SETEXPOSURE;
2733 }
2734 cam->exposure_status = EXPOSURE_NORMAL;
2735 LOG("Automatically decreasing sensor_fps\n");
2736
2737 } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT ||
2738 cam->exposure_status == EXPOSURE_LIGHT) &&
2739 cam->exposure_count >= LIGHT_TIME*framerate &&
2740 cam->params.sensorFps.divisor > 0) {
2741
2742 /* light for too long */
2743 --cam->params.sensorFps.divisor;
2744 cam->cmd_queue |= COMMAND_SETSENSORFPS;
2745
2746 if(cam->params.exposure.gain <
2747 cam->params.exposure.gainMode-1) {
2748 ++cam->params.exposure.gain;
2749 cam->cmd_queue |= COMMAND_SETEXPOSURE;
2750 }
2751 cam->exposure_status = EXPOSURE_NORMAL;
2752 LOG("Automatically increasing sensor_fps\n");
2753 }
2754 }
Ingo Molnar3593cab2006-02-07 06:49:14 -02002755 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002756}
2757
2758/*-----------------------------------------------------------------*/
2759/* if flicker is switched off, this function switches it back on.It checks,
2760 however, that conditions are suitable before restarting it.
2761 This should only be called for firmware version 1.2.
2762
2763 It also adjust the colour balance when an exposure step is detected - as
2764 long as flicker is running
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002765*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07002766static void restart_flicker(struct cam_data *cam)
2767{
2768 int cam_exposure, old_exp;
2769 if(!FIRMWARE_VERSION(1,2))
2770 return;
Ingo Molnar3593cab2006-02-07 06:49:14 -02002771 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002772 if(cam->params.flickerControl.flickerMode == 0 ||
2773 cam->raw_image[39] == 0) {
Ingo Molnar3593cab2006-02-07 06:49:14 -02002774 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002775 return;
2776 }
2777 cam_exposure = cam->raw_image[39]*2;
2778 old_exp = cam->params.exposure.coarseExpLo +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002779 cam->params.exposure.coarseExpHi*256;
2780 /*
2781 see how far away camera exposure is from a valid
2782 flicker exposure value
2783 */
2784 cam_exposure %= cam->params.flickerControl.coarseJump;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002785 if(!cam->params.flickerControl.disabled &&
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002786 cam_exposure <= cam->params.flickerControl.coarseJump - 3) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002787 /* Flicker control auto-disabled */
2788 cam->params.flickerControl.disabled = 1;
2789 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002790
Linus Torvalds1da177e2005-04-16 15:20:36 -07002791 if(cam->params.flickerControl.disabled &&
2792 cam->params.flickerControl.flickerMode &&
2793 old_exp > cam->params.flickerControl.coarseJump +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002794 ROUND_UP_EXP_FOR_FLICKER) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002795 /* exposure is now high enough to switch
2796 flicker control back on */
2797 set_flicker(&cam->params, &cam->cmd_queue, 1);
2798 if((cam->cmd_queue & COMMAND_SETEXPOSURE) &&
2799 cam->params.exposure.expMode == 2)
2800 cam->exposure_status = EXPOSURE_NORMAL;
2801
2802 }
Ingo Molnar3593cab2006-02-07 06:49:14 -02002803 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002804}
2805#undef FIRMWARE_VERSION
2806
2807static int clear_stall(struct cam_data *cam)
2808{
2809 /* FIXME: Does this actually work? */
2810 LOG("Clearing stall\n");
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002811
Linus Torvalds1da177e2005-04-16 15:20:36 -07002812 cam->ops->streamRead(cam->lowlevel_data, cam->raw_image, 0);
2813 do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0);
2814 return cam->params.status.streamState != STREAM_PAUSED;
2815}
2816
2817/* kernel thread function to read image from camera */
2818static int fetch_frame(void *data)
2819{
2820 int image_size, retry;
2821 struct cam_data *cam = (struct cam_data *)data;
2822 unsigned long oldjif, rate, diff;
2823
2824 /* Allow up to two bad images in a row to be read and
2825 * ignored before an error is reported */
2826 for (retry = 0; retry < 3; ++retry) {
2827 if (retry)
2828 DBG("retry=%d\n", retry);
2829
2830 if (!cam->ops)
2831 continue;
2832
2833 /* load first frame always uncompressed */
2834 if (cam->first_frame &&
2835 cam->params.compression.mode != CPIA_COMPRESSION_NONE) {
2836 do_command(cam, CPIA_COMMAND_SetCompression,
2837 CPIA_COMPRESSION_NONE,
2838 NO_DECIMATION, 0, 0);
2839 /* Trial & error - Discarding a frame prevents the
2840 first frame from having an error in the data. */
2841 do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0);
2842 }
2843
2844 /* init camera upload */
2845 if (do_command(cam, CPIA_COMMAND_GrabFrame, 0,
2846 cam->params.streamStartLine, 0, 0))
2847 continue;
2848
2849 if (cam->ops->wait_for_stream_ready) {
2850 /* loop until image ready */
2851 int count = 0;
2852 do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0);
2853 while (cam->params.status.streamState != STREAM_READY) {
2854 if(++count > READY_TIMEOUT)
2855 break;
2856 if(cam->params.status.streamState ==
2857 STREAM_PAUSED) {
2858 /* Bad news */
2859 if(!clear_stall(cam))
2860 return -EIO;
2861 }
2862
2863 cond_resched();
2864
2865 /* sleep for 10 ms, hopefully ;) */
2866 msleep_interruptible(10);
2867 if (signal_pending(current))
2868 return -EINTR;
2869
2870 do_command(cam, CPIA_COMMAND_GetCameraStatus,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002871 0, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002872 }
2873 if(cam->params.status.streamState != STREAM_READY) {
2874 continue;
2875 }
2876 }
2877
2878 cond_resched();
2879
2880 /* grab image from camera */
2881 oldjif = jiffies;
2882 image_size = cam->ops->streamRead(cam->lowlevel_data,
2883 cam->raw_image, 0);
2884 if (image_size <= 0) {
2885 DBG("streamRead failed: %d\n", image_size);
2886 continue;
2887 }
2888
2889 rate = image_size * HZ / 1024;
2890 diff = jiffies-oldjif;
2891 cam->transfer_rate = diff==0 ? rate : rate/diff;
2892 /* diff==0 ? unlikely but possible */
2893
2894 /* Switch flicker control back on if it got turned off */
2895 restart_flicker(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002896
Linus Torvalds1da177e2005-04-16 15:20:36 -07002897 /* If AEC is enabled, monitor the exposure and
2898 adjust the sensor frame rate if needed */
2899 if(cam->params.exposure.expMode == 2)
2900 monitor_exposure(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002901
Linus Torvalds1da177e2005-04-16 15:20:36 -07002902 /* camera idle now so dispatch queued commands */
2903 dispatch_commands(cam);
2904
2905 /* Update our knowledge of the camera state */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002906 do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
2907 do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002908 do_command(cam, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0);
2909
2910 /* decompress and convert image to by copying it from
2911 * raw_image to decompressed_frame
2912 */
2913
2914 cond_resched();
2915
2916 cam->image_size = parse_picture(cam, image_size);
2917 if (cam->image_size <= 0) {
2918 DBG("parse_picture failed %d\n", cam->image_size);
2919 if(cam->params.compression.mode !=
2920 CPIA_COMPRESSION_NONE) {
2921 /* Compression may not work right if we
2922 had a bad frame, get the next one
2923 uncompressed. */
2924 cam->first_frame = 1;
2925 do_command(cam, CPIA_COMMAND_SetGrabMode,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002926 CPIA_GRAB_SINGLE, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002927 /* FIXME: Trial & error - need up to 70ms for
2928 the grab mode change to complete ? */
2929 msleep_interruptible(70);
2930 if (signal_pending(current))
2931 return -EINTR;
2932 }
2933 } else
2934 break;
2935 }
2936
2937 if (retry < 3) {
2938 /* FIXME: this only works for double buffering */
2939 if (cam->frame[cam->curframe].state == FRAME_READY) {
2940 memcpy(cam->frame[cam->curframe].data,
2941 cam->decompressed_frame.data,
2942 cam->decompressed_frame.count);
2943 cam->frame[cam->curframe].state = FRAME_DONE;
2944 } else
2945 cam->decompressed_frame.state = FRAME_DONE;
2946
2947 if (cam->first_frame) {
2948 cam->first_frame = 0;
2949 do_command(cam, CPIA_COMMAND_SetCompression,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002950 cam->params.compression.mode,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002951 cam->params.compression.decimation, 0, 0);
2952
2953 /* Switch from single-grab to continuous grab */
2954 do_command(cam, CPIA_COMMAND_SetGrabMode,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002955 CPIA_GRAB_CONTINUOUS, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002956 }
2957 return 0;
2958 }
2959 return -EIO;
2960}
2961
2962static int capture_frame(struct cam_data *cam, struct video_mmap *vm)
2963{
2964 if (!cam->frame_buf) {
2965 /* we do lazy allocation */
2966 int err;
2967 if ((err = allocate_frame_buf(cam)))
2968 return err;
2969 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002970
Linus Torvalds1da177e2005-04-16 15:20:36 -07002971 cam->curframe = vm->frame;
2972 cam->frame[cam->curframe].state = FRAME_READY;
2973 return fetch_frame(cam);
2974}
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002975
Linus Torvalds1da177e2005-04-16 15:20:36 -07002976static int goto_high_power(struct cam_data *cam)
2977{
2978 if (do_command(cam, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0))
2979 return -EIO;
2980 msleep_interruptible(40); /* windows driver does it too */
2981 if(signal_pending(current))
2982 return -EINTR;
2983 if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
2984 return -EIO;
2985 if (cam->params.status.systemState == HI_POWER_STATE) {
2986 DBG("camera now in HIGH power state\n");
2987 return 0;
2988 }
2989 printstatus(cam);
2990 return -EIO;
2991}
2992
2993static int goto_low_power(struct cam_data *cam)
2994{
2995 if (do_command(cam, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0))
2996 return -1;
2997 if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
2998 return -1;
2999 if (cam->params.status.systemState == LO_POWER_STATE) {
3000 DBG("camera now in LOW power state\n");
3001 return 0;
3002 }
3003 printstatus(cam);
3004 return -1;
3005}
3006
3007static void save_camera_state(struct cam_data *cam)
3008{
3009 if(!(cam->cmd_queue & COMMAND_SETCOLOURBALANCE))
3010 do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
3011 if(!(cam->cmd_queue & COMMAND_SETEXPOSURE))
3012 do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
3013
3014 DBG("%d/%d/%d/%d/%d/%d/%d/%d\n",
3015 cam->params.exposure.gain,
3016 cam->params.exposure.fineExp,
3017 cam->params.exposure.coarseExpLo,
3018 cam->params.exposure.coarseExpHi,
3019 cam->params.exposure.redComp,
3020 cam->params.exposure.green1Comp,
3021 cam->params.exposure.green2Comp,
3022 cam->params.exposure.blueComp);
3023 DBG("%d/%d/%d\n",
3024 cam->params.colourBalance.redGain,
3025 cam->params.colourBalance.greenGain,
3026 cam->params.colourBalance.blueGain);
3027}
3028
3029static int set_camera_state(struct cam_data *cam)
3030{
3031 cam->cmd_queue = COMMAND_SETCOMPRESSION |
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003032 COMMAND_SETCOMPRESSIONTARGET |
3033 COMMAND_SETCOLOURPARAMS |
3034 COMMAND_SETFORMAT |
3035 COMMAND_SETYUVTHRESH |
3036 COMMAND_SETECPTIMING |
3037 COMMAND_SETCOMPRESSIONPARAMS |
3038 COMMAND_SETEXPOSURE |
3039 COMMAND_SETCOLOURBALANCE |
3040 COMMAND_SETSENSORFPS |
3041 COMMAND_SETAPCOR |
3042 COMMAND_SETFLICKERCTRL |
3043 COMMAND_SETVLOFFSET;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003044
3045 do_command(cam, CPIA_COMMAND_SetGrabMode, CPIA_GRAB_SINGLE,0,0,0);
3046 dispatch_commands(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003047
Linus Torvalds1da177e2005-04-16 15:20:36 -07003048 /* Wait 6 frames for the sensor to get all settings and
3049 AEC/ACB to settle */
3050 msleep_interruptible(6*(cam->params.sensorFps.baserate ? 33 : 40) *
3051 (1 << cam->params.sensorFps.divisor) + 10);
3052
3053 if(signal_pending(current))
3054 return -EINTR;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003055
Linus Torvalds1da177e2005-04-16 15:20:36 -07003056 save_camera_state(cam);
3057
3058 return 0;
3059}
3060
3061static void get_version_information(struct cam_data *cam)
3062{
3063 /* GetCPIAVersion */
3064 do_command(cam, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0);
3065
3066 /* GetPnPID */
3067 do_command(cam, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0);
3068}
3069
3070/* initialize camera */
3071static int reset_camera(struct cam_data *cam)
3072{
3073 int err;
3074 /* Start the camera in low power mode */
3075 if (goto_low_power(cam)) {
3076 if (cam->params.status.systemState != WARM_BOOT_STATE)
3077 return -ENODEV;
3078
3079 /* FIXME: this is just dirty trial and error */
3080 err = goto_high_power(cam);
3081 if(err)
3082 return err;
3083 do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0);
3084 if (goto_low_power(cam))
3085 return -ENODEV;
3086 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003087
Linus Torvalds1da177e2005-04-16 15:20:36 -07003088 /* procedure described in developer's guide p3-28 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003089
Linus Torvalds1da177e2005-04-16 15:20:36 -07003090 /* Check the firmware version. */
3091 cam->params.version.firmwareVersion = 0;
3092 get_version_information(cam);
3093 if (cam->params.version.firmwareVersion != 1)
3094 return -ENODEV;
3095
3096 /* A bug in firmware 1-02 limits gainMode to 2 */
3097 if(cam->params.version.firmwareRevision <= 2 &&
3098 cam->params.exposure.gainMode > 2) {
3099 cam->params.exposure.gainMode = 2;
3100 }
3101
3102 /* set QX3 detected flag */
3103 cam->params.qx3.qx3_detected = (cam->params.pnpID.vendor == 0x0813 &&
3104 cam->params.pnpID.product == 0x0001);
3105
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003106 /* The fatal error checking should be done after
Linus Torvalds1da177e2005-04-16 15:20:36 -07003107 * the camera powers up (developer's guide p 3-38) */
3108
3109 /* Set streamState before transition to high power to avoid bug
3110 * in firmware 1-02 */
3111 do_command(cam, CPIA_COMMAND_ModifyCameraStatus, STREAMSTATE, 0,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003112 STREAM_NOT_READY, 0);
3113
Linus Torvalds1da177e2005-04-16 15:20:36 -07003114 /* GotoHiPower */
3115 err = goto_high_power(cam);
3116 if (err)
3117 return err;
3118
3119 /* Check the camera status */
3120 if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
3121 return -EIO;
3122
3123 if (cam->params.status.fatalError) {
3124 DBG("fatal_error: %#04x\n",
3125 cam->params.status.fatalError);
3126 DBG("vp_status: %#04x\n",
3127 cam->params.status.vpStatus);
3128 if (cam->params.status.fatalError & ~(COM_FLAG|CPIA_FLAG)) {
3129 /* Fatal error in camera */
3130 return -EIO;
3131 } else if (cam->params.status.fatalError & (COM_FLAG|CPIA_FLAG)) {
3132 /* Firmware 1-02 may do this for parallel port cameras,
3133 * just clear the flags (developer's guide p 3-38) */
3134 do_command(cam, CPIA_COMMAND_ModifyCameraStatus,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003135 FATALERROR, ~(COM_FLAG|CPIA_FLAG), 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003136 }
3137 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003138
Linus Torvalds1da177e2005-04-16 15:20:36 -07003139 /* Check the camera status again */
3140 if (cam->params.status.fatalError) {
3141 if (cam->params.status.fatalError)
3142 return -EIO;
3143 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003144
Linus Torvalds1da177e2005-04-16 15:20:36 -07003145 /* VPVersion can't be retrieved before the camera is in HiPower,
3146 * so get it here instead of in get_version_information. */
3147 do_command(cam, CPIA_COMMAND_GetVPVersion, 0, 0, 0, 0);
3148
3149 /* set camera to a known state */
3150 return set_camera_state(cam);
3151}
3152
3153static void put_cam(struct cpia_camera_ops* ops)
3154{
Mariusz Kozlowskiac328982007-01-07 10:36:24 -03003155 module_put(ops->owner);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003156}
3157
3158/* ------------------------- V4L interface --------------------- */
3159static int cpia_open(struct inode *inode, struct file *file)
3160{
3161 struct video_device *dev = video_devdata(file);
3162 struct cam_data *cam = dev->priv;
3163 int err;
3164
3165 if (!cam) {
3166 DBG("Internal error, cam_data not found!\n");
3167 return -ENODEV;
3168 }
3169
3170 if (cam->open_count > 0) {
3171 DBG("Camera already open\n");
3172 return -EBUSY;
3173 }
3174
3175 if (!try_module_get(cam->ops->owner))
3176 return -ENODEV;
3177
Ingo Molnar3593cab2006-02-07 06:49:14 -02003178 mutex_lock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003179 err = -ENOMEM;
3180 if (!cam->raw_image) {
3181 cam->raw_image = rvmalloc(CPIA_MAX_IMAGE_SIZE);
3182 if (!cam->raw_image)
3183 goto oops;
3184 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003185
Linus Torvalds1da177e2005-04-16 15:20:36 -07003186 if (!cam->decompressed_frame.data) {
3187 cam->decompressed_frame.data = rvmalloc(CPIA_MAX_FRAME_SIZE);
3188 if (!cam->decompressed_frame.data)
3189 goto oops;
3190 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003191
Linus Torvalds1da177e2005-04-16 15:20:36 -07003192 /* open cpia */
3193 err = -ENODEV;
3194 if (cam->ops->open(cam->lowlevel_data))
3195 goto oops;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003196
Linus Torvalds1da177e2005-04-16 15:20:36 -07003197 /* reset the camera */
3198 if ((err = reset_camera(cam)) != 0) {
3199 cam->ops->close(cam->lowlevel_data);
3200 goto oops;
3201 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003202
Linus Torvalds1da177e2005-04-16 15:20:36 -07003203 err = -EINTR;
3204 if(signal_pending(current))
3205 goto oops;
3206
3207 /* Set ownership of /proc/cpia/videoX to current user */
3208 if(cam->proc_entry)
3209 cam->proc_entry->uid = current->uid;
3210
3211 /* set mark for loading first frame uncompressed */
3212 cam->first_frame = 1;
3213
3214 /* init it to something */
3215 cam->mmap_kludge = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003216
Linus Torvalds1da177e2005-04-16 15:20:36 -07003217 ++cam->open_count;
3218 file->private_data = dev;
Ingo Molnar3593cab2006-02-07 06:49:14 -02003219 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003220 return 0;
3221
3222 oops:
3223 if (cam->decompressed_frame.data) {
3224 rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
3225 cam->decompressed_frame.data = NULL;
3226 }
3227 if (cam->raw_image) {
3228 rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
3229 cam->raw_image = NULL;
3230 }
Ingo Molnar3593cab2006-02-07 06:49:14 -02003231 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003232 put_cam(cam->ops);
3233 return err;
3234}
3235
3236static int cpia_close(struct inode *inode, struct file *file)
3237{
3238 struct video_device *dev = file->private_data;
3239 struct cam_data *cam = dev->priv;
3240
3241 if (cam->ops) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003242 /* Return ownership of /proc/cpia/videoX to root */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003243 if(cam->proc_entry)
3244 cam->proc_entry->uid = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003245
Linus Torvalds1da177e2005-04-16 15:20:36 -07003246 /* save camera state for later open (developers guide ch 3.5.3) */
3247 save_camera_state(cam);
3248
3249 /* GotoLoPower */
3250 goto_low_power(cam);
3251
3252 /* Update the camera status */
3253 do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
3254
3255 /* cleanup internal state stuff */
3256 free_frames(cam->frame);
3257
3258 /* close cpia */
3259 cam->ops->close(cam->lowlevel_data);
3260
3261 put_cam(cam->ops);
3262 }
3263
3264 if (--cam->open_count == 0) {
3265 /* clean up capture-buffers */
3266 if (cam->raw_image) {
3267 rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
3268 cam->raw_image = NULL;
3269 }
3270
3271 if (cam->decompressed_frame.data) {
3272 rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
3273 cam->decompressed_frame.data = NULL;
3274 }
3275
3276 if (cam->frame_buf)
3277 free_frame_buf(cam);
3278
3279 if (!cam->ops)
3280 kfree(cam);
3281 }
3282 file->private_data = NULL;
3283
3284 return 0;
3285}
3286
3287static ssize_t cpia_read(struct file *file, char __user *buf,
3288 size_t count, loff_t *ppos)
3289{
3290 struct video_device *dev = file->private_data;
3291 struct cam_data *cam = dev->priv;
3292 int err;
3293
3294 /* make this _really_ smp and multithread-safe */
Ingo Molnar3593cab2006-02-07 06:49:14 -02003295 if (mutex_lock_interruptible(&cam->busy_lock))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003296 return -EINTR;
3297
3298 if (!buf) {
3299 DBG("buf NULL\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02003300 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003301 return -EINVAL;
3302 }
3303
3304 if (!count) {
3305 DBG("count 0\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02003306 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003307 return 0;
3308 }
3309
3310 if (!cam->ops) {
3311 DBG("ops NULL\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02003312 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003313 return -ENODEV;
3314 }
3315
3316 /* upload frame */
3317 cam->decompressed_frame.state = FRAME_READY;
3318 cam->mmap_kludge=0;
3319 if((err = fetch_frame(cam)) != 0) {
3320 DBG("ERROR from fetch_frame: %d\n", err);
Ingo Molnar3593cab2006-02-07 06:49:14 -02003321 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003322 return err;
3323 }
3324 cam->decompressed_frame.state = FRAME_UNUSED;
3325
3326 /* copy data to user space */
3327 if (cam->decompressed_frame.count > count) {
3328 DBG("count wrong: %d, %lu\n", cam->decompressed_frame.count,
3329 (unsigned long) count);
Ingo Molnar3593cab2006-02-07 06:49:14 -02003330 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003331 return -EFAULT;
3332 }
3333 if (copy_to_user(buf, cam->decompressed_frame.data,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003334 cam->decompressed_frame.count)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003335 DBG("copy_to_user failed\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02003336 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003337 return -EFAULT;
3338 }
3339
Ingo Molnar3593cab2006-02-07 06:49:14 -02003340 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003341 return cam->decompressed_frame.count;
3342}
3343
3344static int cpia_do_ioctl(struct inode *inode, struct file *file,
3345 unsigned int ioctlnr, void *arg)
3346{
3347 struct video_device *dev = file->private_data;
3348 struct cam_data *cam = dev->priv;
3349 int retval = 0;
3350
3351 if (!cam || !cam->ops)
3352 return -ENODEV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003353
Linus Torvalds1da177e2005-04-16 15:20:36 -07003354 /* make this _really_ smp-safe */
Ingo Molnar3593cab2006-02-07 06:49:14 -02003355 if (mutex_lock_interruptible(&cam->busy_lock))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003356 return -EINTR;
3357
3358 //DBG("cpia_ioctl: %u\n", ioctlnr);
3359
3360 switch (ioctlnr) {
Alexey Dobriyanbe787ac2006-03-07 22:20:23 -03003361 /* query capabilities */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003362 case VIDIOCGCAP:
3363 {
3364 struct video_capability *b = arg;
3365
3366 DBG("VIDIOCGCAP\n");
3367 strcpy(b->name, "CPiA Camera");
3368 b->type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE;
3369 b->channels = 1;
3370 b->audios = 0;
3371 b->maxwidth = 352; /* VIDEOSIZE_CIF */
3372 b->maxheight = 288;
3373 b->minwidth = 48; /* VIDEOSIZE_48_48 */
3374 b->minheight = 48;
3375 break;
3376 }
3377
3378 /* get/set video source - we are a camera and nothing else */
3379 case VIDIOCGCHAN:
3380 {
3381 struct video_channel *v = arg;
3382
3383 DBG("VIDIOCGCHAN\n");
3384 if (v->channel != 0) {
3385 retval = -EINVAL;
3386 break;
3387 }
3388
3389 v->channel = 0;
3390 strcpy(v->name, "Camera");
3391 v->tuners = 0;
3392 v->flags = 0;
3393 v->type = VIDEO_TYPE_CAMERA;
3394 v->norm = 0;
3395 break;
3396 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003397
Linus Torvalds1da177e2005-04-16 15:20:36 -07003398 case VIDIOCSCHAN:
3399 {
3400 struct video_channel *v = arg;
3401
3402 DBG("VIDIOCSCHAN\n");
3403 if (v->channel != 0)
3404 retval = -EINVAL;
3405 break;
3406 }
3407
3408 /* image properties */
3409 case VIDIOCGPICT:
3410 {
3411 struct video_picture *pic = arg;
3412 DBG("VIDIOCGPICT\n");
3413 *pic = cam->vp;
3414 break;
3415 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003416
Linus Torvalds1da177e2005-04-16 15:20:36 -07003417 case VIDIOCSPICT:
3418 {
3419 struct video_picture *vp = arg;
3420
3421 DBG("VIDIOCSPICT\n");
3422
3423 /* check validity */
3424 DBG("palette: %d\n", vp->palette);
3425 DBG("depth: %d\n", vp->depth);
3426 if (!valid_mode(vp->palette, vp->depth)) {
3427 retval = -EINVAL;
3428 break;
3429 }
3430
Ingo Molnar3593cab2006-02-07 06:49:14 -02003431 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003432 /* brightness, colour, contrast need no check 0-65535 */
3433 cam->vp = *vp;
3434 /* update cam->params.colourParams */
3435 cam->params.colourParams.brightness = vp->brightness*100/65535;
3436 cam->params.colourParams.contrast = vp->contrast*100/65535;
3437 cam->params.colourParams.saturation = vp->colour*100/65535;
3438 /* contrast is in steps of 8, so round */
3439 cam->params.colourParams.contrast =
3440 ((cam->params.colourParams.contrast + 3) / 8) * 8;
3441 if (cam->params.version.firmwareVersion == 1 &&
3442 cam->params.version.firmwareRevision == 2 &&
3443 cam->params.colourParams.contrast > 80) {
3444 /* 1-02 firmware limits contrast to 80 */
3445 cam->params.colourParams.contrast = 80;
3446 }
3447
3448 /* Adjust flicker control if necessary */
3449 if(cam->params.flickerControl.allowableOverExposure < 0)
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003450 cam->params.flickerControl.allowableOverExposure =
Linus Torvalds1da177e2005-04-16 15:20:36 -07003451 -find_over_exposure(cam->params.colourParams.brightness);
3452 if(cam->params.flickerControl.flickerMode != 0)
3453 cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003454
Linus Torvalds1da177e2005-04-16 15:20:36 -07003455
3456 /* queue command to update camera */
3457 cam->cmd_queue |= COMMAND_SETCOLOURPARAMS;
Ingo Molnar3593cab2006-02-07 06:49:14 -02003458 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003459 DBG("VIDIOCSPICT: %d / %d // %d / %d / %d / %d\n",
3460 vp->depth, vp->palette, vp->brightness, vp->hue, vp->colour,
3461 vp->contrast);
3462 break;
3463 }
3464
3465 /* get/set capture window */
3466 case VIDIOCGWIN:
3467 {
3468 struct video_window *vw = arg;
3469 DBG("VIDIOCGWIN\n");
3470
3471 *vw = cam->vw;
3472 break;
3473 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003474
Linus Torvalds1da177e2005-04-16 15:20:36 -07003475 case VIDIOCSWIN:
3476 {
3477 /* copy_from_user, check validity, copy to internal structure */
3478 struct video_window *vw = arg;
3479 DBG("VIDIOCSWIN\n");
3480
3481 if (vw->clipcount != 0) { /* clipping not supported */
3482 retval = -EINVAL;
3483 break;
3484 }
3485 if (vw->clips != NULL) { /* clipping not supported */
3486 retval = -EINVAL;
3487 break;
3488 }
3489
3490 /* we set the video window to something smaller or equal to what
3491 * is requested by the user???
3492 */
Ingo Molnar3593cab2006-02-07 06:49:14 -02003493 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003494 if (vw->width != cam->vw.width || vw->height != cam->vw.height) {
3495 int video_size = match_videosize(vw->width, vw->height);
3496
3497 if (video_size < 0) {
3498 retval = -EINVAL;
Ingo Molnar3593cab2006-02-07 06:49:14 -02003499 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003500 break;
3501 }
3502 cam->video_size = video_size;
3503
3504 /* video size is changing, reset the subcapture area */
3505 memset(&cam->vc, 0, sizeof(cam->vc));
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003506
Linus Torvalds1da177e2005-04-16 15:20:36 -07003507 set_vw_size(cam);
3508 DBG("%d / %d\n", cam->vw.width, cam->vw.height);
3509 cam->cmd_queue |= COMMAND_SETFORMAT;
3510 }
3511
Ingo Molnar3593cab2006-02-07 06:49:14 -02003512 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003513
3514 /* setformat ignored by camera during streaming,
3515 * so stop/dispatch/start */
3516 if (cam->cmd_queue & COMMAND_SETFORMAT) {
3517 DBG("\n");
3518 dispatch_commands(cam);
3519 }
3520 DBG("%d/%d:%d\n", cam->video_size,
3521 cam->vw.width, cam->vw.height);
3522 break;
3523 }
3524
3525 /* mmap interface */
3526 case VIDIOCGMBUF:
3527 {
3528 struct video_mbuf *vm = arg;
3529 int i;
3530
3531 DBG("VIDIOCGMBUF\n");
3532 memset(vm, 0, sizeof(*vm));
3533 vm->size = CPIA_MAX_FRAME_SIZE*FRAME_NUM;
3534 vm->frames = FRAME_NUM;
3535 for (i = 0; i < FRAME_NUM; i++)
3536 vm->offsets[i] = CPIA_MAX_FRAME_SIZE * i;
3537 break;
3538 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003539
Linus Torvalds1da177e2005-04-16 15:20:36 -07003540 case VIDIOCMCAPTURE:
3541 {
3542 struct video_mmap *vm = arg;
3543 int video_size;
3544
3545 DBG("VIDIOCMCAPTURE: %d / %d / %dx%d\n", vm->format, vm->frame,
3546 vm->width, vm->height);
3547 if (vm->frame<0||vm->frame>=FRAME_NUM) {
3548 retval = -EINVAL;
3549 break;
3550 }
3551
3552 /* set video format */
3553 cam->vp.palette = vm->format;
3554 switch(vm->format) {
3555 case VIDEO_PALETTE_GREY:
3556 cam->vp.depth=8;
3557 break;
3558 case VIDEO_PALETTE_RGB555:
3559 case VIDEO_PALETTE_RGB565:
3560 case VIDEO_PALETTE_YUV422:
3561 case VIDEO_PALETTE_YUYV:
3562 case VIDEO_PALETTE_UYVY:
3563 cam->vp.depth = 16;
3564 break;
3565 case VIDEO_PALETTE_RGB24:
3566 cam->vp.depth = 24;
3567 break;
3568 case VIDEO_PALETTE_RGB32:
3569 cam->vp.depth = 32;
3570 break;
3571 default:
3572 retval = -EINVAL;
3573 break;
3574 }
3575 if (retval)
3576 break;
3577
3578 /* set video size */
3579 video_size = match_videosize(vm->width, vm->height);
3580 if (video_size < 0) {
3581 retval = -EINVAL;
3582 break;
3583 }
3584 if (video_size != cam->video_size) {
3585 cam->video_size = video_size;
3586
3587 /* video size is changing, reset the subcapture area */
3588 memset(&cam->vc, 0, sizeof(cam->vc));
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003589
Linus Torvalds1da177e2005-04-16 15:20:36 -07003590 set_vw_size(cam);
3591 cam->cmd_queue |= COMMAND_SETFORMAT;
3592 dispatch_commands(cam);
3593 }
3594 /* according to v4l-spec we must start streaming here */
3595 cam->mmap_kludge = 1;
3596 retval = capture_frame(cam, vm);
3597
3598 break;
3599 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003600
Linus Torvalds1da177e2005-04-16 15:20:36 -07003601 case VIDIOCSYNC:
3602 {
3603 int *frame = arg;
3604
3605 //DBG("VIDIOCSYNC: %d\n", *frame);
3606
3607 if (*frame<0 || *frame >= FRAME_NUM) {
3608 retval = -EINVAL;
3609 break;
3610 }
3611
3612 switch (cam->frame[*frame].state) {
3613 case FRAME_UNUSED:
3614 case FRAME_READY:
3615 case FRAME_GRABBING:
3616 DBG("sync to unused frame %d\n", *frame);
3617 retval = -EINVAL;
3618 break;
3619
3620 case FRAME_DONE:
3621 cam->frame[*frame].state = FRAME_UNUSED;
3622 //DBG("VIDIOCSYNC: %d synced\n", *frame);
3623 break;
3624 }
3625 if (retval == -EINTR) {
3626 /* FIXME - xawtv does not handle this nice */
3627 retval = 0;
3628 }
3629 break;
3630 }
3631
3632 case VIDIOCGCAPTURE:
3633 {
3634 struct video_capture *vc = arg;
3635
3636 DBG("VIDIOCGCAPTURE\n");
3637
3638 *vc = cam->vc;
3639
3640 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003641 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003642
3643 case VIDIOCSCAPTURE:
3644 {
3645 struct video_capture *vc = arg;
3646
3647 DBG("VIDIOCSCAPTURE\n");
3648
3649 if (vc->decimation != 0) { /* How should this be used? */
3650 retval = -EINVAL;
3651 break;
3652 }
3653 if (vc->flags != 0) { /* Even/odd grab not supported */
3654 retval = -EINVAL;
3655 break;
3656 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003657
Linus Torvalds1da177e2005-04-16 15:20:36 -07003658 /* Clip to the resolution we can set for the ROI
3659 (every 8 columns and 4 rows) */
3660 vc->x = vc->x & ~(__u32)7;
3661 vc->y = vc->y & ~(__u32)3;
3662 vc->width = vc->width & ~(__u32)7;
3663 vc->height = vc->height & ~(__u32)3;
3664
3665 if(vc->width == 0 || vc->height == 0 ||
3666 vc->x + vc->width > cam->vw.width ||
3667 vc->y + vc->height > cam->vw.height) {
3668 retval = -EINVAL;
3669 break;
3670 }
3671
3672 DBG("%d,%d/%dx%d\n", vc->x,vc->y,vc->width, vc->height);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003673
Ingo Molnar3593cab2006-02-07 06:49:14 -02003674 mutex_lock(&cam->param_lock);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003675
Linus Torvalds1da177e2005-04-16 15:20:36 -07003676 cam->vc.x = vc->x;
3677 cam->vc.y = vc->y;
3678 cam->vc.width = vc->width;
3679 cam->vc.height = vc->height;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003680
Linus Torvalds1da177e2005-04-16 15:20:36 -07003681 set_vw_size(cam);
3682 cam->cmd_queue |= COMMAND_SETFORMAT;
3683
Ingo Molnar3593cab2006-02-07 06:49:14 -02003684 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003685
3686 /* setformat ignored by camera during streaming,
3687 * so stop/dispatch/start */
3688 dispatch_commands(cam);
3689 break;
3690 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003691
Linus Torvalds1da177e2005-04-16 15:20:36 -07003692 case VIDIOCGUNIT:
3693 {
3694 struct video_unit *vu = arg;
3695
3696 DBG("VIDIOCGUNIT\n");
3697
3698 vu->video = cam->vdev.minor;
3699 vu->vbi = VIDEO_NO_UNIT;
3700 vu->radio = VIDEO_NO_UNIT;
3701 vu->audio = VIDEO_NO_UNIT;
3702 vu->teletext = VIDEO_NO_UNIT;
3703
3704 break;
3705 }
3706
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003707
Linus Torvalds1da177e2005-04-16 15:20:36 -07003708 /* pointless to implement overlay with this camera */
3709 case VIDIOCCAPTURE:
3710 case VIDIOCGFBUF:
3711 case VIDIOCSFBUF:
3712 case VIDIOCKEY:
3713 /* tuner interface - we have none */
3714 case VIDIOCGTUNER:
3715 case VIDIOCSTUNER:
3716 case VIDIOCGFREQ:
3717 case VIDIOCSFREQ:
3718 /* audio interface - we have none */
3719 case VIDIOCGAUDIO:
3720 case VIDIOCSAUDIO:
3721 retval = -EINVAL;
3722 break;
3723 default:
3724 retval = -ENOIOCTLCMD;
3725 break;
3726 }
3727
Ingo Molnar3593cab2006-02-07 06:49:14 -02003728 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003729 return retval;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003730}
Linus Torvalds1da177e2005-04-16 15:20:36 -07003731
3732static int cpia_ioctl(struct inode *inode, struct file *file,
3733 unsigned int cmd, unsigned long arg)
3734{
3735 return video_usercopy(inode, file, cmd, arg, cpia_do_ioctl);
3736}
3737
3738
3739/* FIXME */
3740static int cpia_mmap(struct file *file, struct vm_area_struct *vma)
3741{
3742 struct video_device *dev = file->private_data;
3743 unsigned long start = vma->vm_start;
3744 unsigned long size = vma->vm_end - vma->vm_start;
3745 unsigned long page, pos;
3746 struct cam_data *cam = dev->priv;
3747 int retval;
3748
3749 if (!cam || !cam->ops)
3750 return -ENODEV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003751
Linus Torvalds1da177e2005-04-16 15:20:36 -07003752 DBG("cpia_mmap: %ld\n", size);
3753
3754 if (size > FRAME_NUM*CPIA_MAX_FRAME_SIZE)
3755 return -EINVAL;
3756
3757 if (!cam || !cam->ops)
3758 return -ENODEV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003759
Linus Torvalds1da177e2005-04-16 15:20:36 -07003760 /* make this _really_ smp-safe */
Ingo Molnar3593cab2006-02-07 06:49:14 -02003761 if (mutex_lock_interruptible(&cam->busy_lock))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003762 return -EINTR;
3763
3764 if (!cam->frame_buf) { /* we do lazy allocation */
3765 if ((retval = allocate_frame_buf(cam))) {
Ingo Molnar3593cab2006-02-07 06:49:14 -02003766 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003767 return retval;
3768 }
3769 }
3770
3771 pos = (unsigned long)(cam->frame_buf);
3772 while (size > 0) {
3773 page = vmalloc_to_pfn((void *)pos);
3774 if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) {
Ingo Molnar3593cab2006-02-07 06:49:14 -02003775 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003776 return -EAGAIN;
3777 }
3778 start += PAGE_SIZE;
3779 pos += PAGE_SIZE;
3780 if (size > PAGE_SIZE)
3781 size -= PAGE_SIZE;
3782 else
3783 size = 0;
3784 }
3785
3786 DBG("cpia_mmap: %ld\n", size);
Ingo Molnar3593cab2006-02-07 06:49:14 -02003787 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003788
3789 return 0;
3790}
3791
Arjan van de Venfa027c22007-02-12 00:55:33 -08003792static const struct file_operations cpia_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003793 .owner = THIS_MODULE,
3794 .open = cpia_open,
3795 .release = cpia_close,
3796 .read = cpia_read,
3797 .mmap = cpia_mmap,
3798 .ioctl = cpia_ioctl,
Arnd Bergmann0d0fbf82006-01-09 15:24:57 -02003799 .compat_ioctl = v4l_compat_ioctl32,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003800 .llseek = no_llseek,
3801};
3802
3803static struct video_device cpia_template = {
3804 .owner = THIS_MODULE,
3805 .name = "CPiA Camera",
3806 .type = VID_TYPE_CAPTURE,
3807 .hardware = VID_HARDWARE_CPIA,
3808 .fops = &cpia_fops,
3809};
3810
3811/* initialise cam_data structure */
3812static void reset_camera_struct(struct cam_data *cam)
3813{
3814 /* The following parameter values are the defaults from
3815 * "Software Developer's Guide for CPiA Cameras". Any changes
3816 * to the defaults are noted in comments. */
3817 cam->params.colourParams.brightness = 50;
3818 cam->params.colourParams.contrast = 48;
3819 cam->params.colourParams.saturation = 50;
3820 cam->params.exposure.gainMode = 4;
3821 cam->params.exposure.expMode = 2; /* AEC */
3822 cam->params.exposure.compMode = 1;
3823 cam->params.exposure.centreWeight = 1;
3824 cam->params.exposure.gain = 0;
3825 cam->params.exposure.fineExp = 0;
3826 cam->params.exposure.coarseExpLo = 185;
3827 cam->params.exposure.coarseExpHi = 0;
3828 cam->params.exposure.redComp = COMP_RED;
3829 cam->params.exposure.green1Comp = COMP_GREEN1;
3830 cam->params.exposure.green2Comp = COMP_GREEN2;
3831 cam->params.exposure.blueComp = COMP_BLUE;
3832 cam->params.colourBalance.balanceMode = 2; /* ACB */
3833 cam->params.colourBalance.redGain = 32;
3834 cam->params.colourBalance.greenGain = 6;
3835 cam->params.colourBalance.blueGain = 92;
3836 cam->params.apcor.gain1 = 0x18;
3837 cam->params.apcor.gain2 = 0x16;
3838 cam->params.apcor.gain4 = 0x24;
3839 cam->params.apcor.gain8 = 0x34;
3840 cam->params.flickerControl.flickerMode = 0;
3841 cam->params.flickerControl.disabled = 1;
3842
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003843 cam->params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07003844 flicker_jumps[cam->mainsFreq]
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003845 [cam->params.sensorFps.baserate]
3846 [cam->params.sensorFps.divisor];
3847 cam->params.flickerControl.allowableOverExposure =
Linus Torvalds1da177e2005-04-16 15:20:36 -07003848 -find_over_exposure(cam->params.colourParams.brightness);
3849 cam->params.vlOffset.gain1 = 20;
3850 cam->params.vlOffset.gain2 = 24;
3851 cam->params.vlOffset.gain4 = 26;
3852 cam->params.vlOffset.gain8 = 26;
3853 cam->params.compressionParams.hysteresis = 3;
3854 cam->params.compressionParams.threshMax = 11;
3855 cam->params.compressionParams.smallStep = 1;
3856 cam->params.compressionParams.largeStep = 3;
3857 cam->params.compressionParams.decimationHysteresis = 2;
3858 cam->params.compressionParams.frDiffStepThresh = 5;
3859 cam->params.compressionParams.qDiffStepThresh = 3;
3860 cam->params.compressionParams.decimationThreshMod = 2;
3861 /* End of default values from Software Developer's Guide */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003862
Linus Torvalds1da177e2005-04-16 15:20:36 -07003863 cam->transfer_rate = 0;
3864 cam->exposure_status = EXPOSURE_NORMAL;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003865
Linus Torvalds1da177e2005-04-16 15:20:36 -07003866 /* Set Sensor FPS to 15fps. This seems better than 30fps
3867 * for indoor lighting. */
3868 cam->params.sensorFps.divisor = 1;
3869 cam->params.sensorFps.baserate = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003870
Linus Torvalds1da177e2005-04-16 15:20:36 -07003871 cam->params.yuvThreshold.yThreshold = 6; /* From windows driver */
3872 cam->params.yuvThreshold.uvThreshold = 6; /* From windows driver */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003873
Linus Torvalds1da177e2005-04-16 15:20:36 -07003874 cam->params.format.subSample = SUBSAMPLE_422;
3875 cam->params.format.yuvOrder = YUVORDER_YUYV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003876
Linus Torvalds1da177e2005-04-16 15:20:36 -07003877 cam->params.compression.mode = CPIA_COMPRESSION_AUTO;
3878 cam->params.compressionTarget.frTargeting =
3879 CPIA_COMPRESSION_TARGET_QUALITY;
3880 cam->params.compressionTarget.targetFR = 15; /* From windows driver */
3881 cam->params.compressionTarget.targetQ = 5; /* From windows driver */
3882
3883 cam->params.qx3.qx3_detected = 0;
3884 cam->params.qx3.toplight = 0;
3885 cam->params.qx3.bottomlight = 0;
3886 cam->params.qx3.button = 0;
3887 cam->params.qx3.cradled = 0;
3888
3889 cam->video_size = VIDEOSIZE_CIF;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003890
Linus Torvalds1da177e2005-04-16 15:20:36 -07003891 cam->vp.colour = 32768; /* 50% */
3892 cam->vp.hue = 32768; /* 50% */
3893 cam->vp.brightness = 32768; /* 50% */
3894 cam->vp.contrast = 32768; /* 50% */
3895 cam->vp.whiteness = 0; /* not used -> grayscale only */
3896 cam->vp.depth = 24; /* to be set by user */
3897 cam->vp.palette = VIDEO_PALETTE_RGB24; /* to be set by user */
3898
3899 cam->vc.x = 0;
3900 cam->vc.y = 0;
3901 cam->vc.width = 0;
3902 cam->vc.height = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003903
Linus Torvalds1da177e2005-04-16 15:20:36 -07003904 cam->vw.x = 0;
3905 cam->vw.y = 0;
3906 set_vw_size(cam);
3907 cam->vw.chromakey = 0;
3908 cam->vw.flags = 0;
3909 cam->vw.clipcount = 0;
3910 cam->vw.clips = NULL;
3911
3912 cam->cmd_queue = COMMAND_NONE;
3913 cam->first_frame = 1;
3914
3915 return;
3916}
3917
3918/* initialize cam_data structure */
3919static void init_camera_struct(struct cam_data *cam,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003920 struct cpia_camera_ops *ops )
Linus Torvalds1da177e2005-04-16 15:20:36 -07003921{
3922 int i;
3923
3924 /* Default everything to 0 */
3925 memset(cam, 0, sizeof(struct cam_data));
3926
3927 cam->ops = ops;
Ingo Molnar3593cab2006-02-07 06:49:14 -02003928 mutex_init(&cam->param_lock);
3929 mutex_init(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003930
3931 reset_camera_struct(cam);
3932
3933 cam->proc_entry = NULL;
3934
3935 memcpy(&cam->vdev, &cpia_template, sizeof(cpia_template));
3936 cam->vdev.priv = cam;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003937
Linus Torvalds1da177e2005-04-16 15:20:36 -07003938 cam->curframe = 0;
3939 for (i = 0; i < FRAME_NUM; i++) {
3940 cam->frame[i].width = 0;
3941 cam->frame[i].height = 0;
3942 cam->frame[i].state = FRAME_UNUSED;
3943 cam->frame[i].data = NULL;
3944 }
3945 cam->decompressed_frame.width = 0;
3946 cam->decompressed_frame.height = 0;
3947 cam->decompressed_frame.state = FRAME_UNUSED;
3948 cam->decompressed_frame.data = NULL;
3949}
3950
3951struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel)
3952{
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003953 struct cam_data *camera;
3954
Linus Torvalds1da177e2005-04-16 15:20:36 -07003955 if ((camera = kmalloc(sizeof(struct cam_data), GFP_KERNEL)) == NULL)
3956 return NULL;
3957
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003958
Linus Torvalds1da177e2005-04-16 15:20:36 -07003959 init_camera_struct( camera, ops );
3960 camera->lowlevel_data = lowlevel;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003961
Linus Torvalds1da177e2005-04-16 15:20:36 -07003962 /* register v4l device */
3963 if (video_register_device(&camera->vdev, VFL_TYPE_GRABBER, video_nr) == -1) {
3964 kfree(camera);
3965 printk(KERN_DEBUG "video_register_device failed\n");
3966 return NULL;
3967 }
3968
3969 /* get version information from camera: open/reset/close */
3970
3971 /* open cpia */
3972 if (camera->ops->open(camera->lowlevel_data))
3973 return camera;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003974
Linus Torvalds1da177e2005-04-16 15:20:36 -07003975 /* reset the camera */
3976 if (reset_camera(camera) != 0) {
3977 camera->ops->close(camera->lowlevel_data);
3978 return camera;
3979 }
3980
3981 /* close cpia */
3982 camera->ops->close(camera->lowlevel_data);
3983
3984#ifdef CONFIG_PROC_FS
3985 create_proc_cpia_cam(camera);
3986#endif
3987
3988 printk(KERN_INFO " CPiA Version: %d.%02d (%d.%d)\n",
3989 camera->params.version.firmwareVersion,
3990 camera->params.version.firmwareRevision,
3991 camera->params.version.vcVersion,
3992 camera->params.version.vcRevision);
3993 printk(KERN_INFO " CPiA PnP-ID: %04x:%04x:%04x\n",
3994 camera->params.pnpID.vendor,
3995 camera->params.pnpID.product,
3996 camera->params.pnpID.deviceRevision);
3997 printk(KERN_INFO " VP-Version: %d.%d %04x\n",
3998 camera->params.vpVersion.vpVersion,
3999 camera->params.vpVersion.vpRevision,
4000 camera->params.vpVersion.cameraHeadID);
4001
4002 return camera;
4003}
4004
4005void cpia_unregister_camera(struct cam_data *cam)
4006{
4007 DBG("unregistering video\n");
4008 video_unregister_device(&cam->vdev);
4009 if (cam->open_count) {
4010 put_cam(cam->ops);
4011 DBG("camera open -- setting ops to NULL\n");
4012 cam->ops = NULL;
4013 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03004014
Linus Torvalds1da177e2005-04-16 15:20:36 -07004015#ifdef CONFIG_PROC_FS
4016 DBG("destroying /proc/cpia/video%d\n", cam->vdev.minor);
4017 destroy_proc_cpia_cam(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03004018#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07004019 if (!cam->open_count) {
4020 DBG("freeing camera\n");
4021 kfree(cam);
4022 }
4023}
4024
4025static int __init cpia_init(void)
4026{
4027 printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT,
4028 CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER);
4029
4030 printk(KERN_WARNING "Since in-kernel colorspace conversion is not "
4031 "allowed, it is disabled by default now. Users should fix the "
4032 "applications in case they don't work without conversion "
4033 "reenabled by setting the 'colorspace_conv' module "
Randy Dunlap94190452006-03-27 16:18:25 -03004034 "parameter to 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004035
4036#ifdef CONFIG_PROC_FS
4037 proc_cpia_create();
4038#endif
4039
Linus Torvalds1da177e2005-04-16 15:20:36 -07004040 return 0;
4041}
4042
4043static void __exit cpia_exit(void)
4044{
4045#ifdef CONFIG_PROC_FS
4046 proc_cpia_destroy();
4047#endif
4048}
4049
4050module_init(cpia_init);
4051module_exit(cpia_exit);
4052
4053/* Exported symbols for modules. */
4054
4055EXPORT_SYMBOL(cpia_register_camera);
4056EXPORT_SYMBOL(cpia_unregister_camera);