blob: 95c5aceecc5bb9566f64887d805713d4cfc28286 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * cpia CPiA driver
3 *
4 * Supports CPiA based Video Camera's.
5 *
6 * (C) Copyright 1999-2000 Peter Pregler
7 * (C) Copyright 1999-2000 Scott J. Bertin
8 * (C) Copyright 1999-2000 Johannes Erdfelt <johannes@erdfelt.com>
9 * (C) Copyright 2000 STMicroelectronics
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 */
25
26/* define _CPIA_DEBUG_ for verbose debug output (see cpia.h) */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030027/* #define _CPIA_DEBUG_ 1 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070028
29#include <linux/config.h>
30
31#include <linux/module.h>
32#include <linux/moduleparam.h>
33#include <linux/init.h>
34#include <linux/fs.h>
35#include <linux/vmalloc.h>
36#include <linux/slab.h>
37#include <linux/proc_fs.h>
38#include <linux/ctype.h>
39#include <linux/pagemap.h>
40#include <linux/delay.h>
41#include <asm/io.h>
Ingo Molnar3593cab2006-02-07 06:49:14 -020042#include <linux/mutex.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070043
44#ifdef CONFIG_KMOD
45#include <linux/kmod.h>
46#endif
47
48#include "cpia.h"
49
Linus Torvalds1da177e2005-04-16 15:20:36 -070050static int video_nr = -1;
51
52#ifdef MODULE
53module_param(video_nr, int, 0);
Randy Dunlap2f8de1a2006-03-21 14:53:22 -030054MODULE_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 -070055MODULE_DESCRIPTION("V4L-driver for Vision CPiA based cameras");
56MODULE_LICENSE("GPL");
57MODULE_SUPPORTED_DEVICE("video");
58#endif
59
Randy Dunlap94190452006-03-27 16:18:25 -030060static unsigned short colorspace_conv;
Linus Torvalds1da177e2005-04-16 15:20:36 -070061module_param(colorspace_conv, ushort, 0444);
62MODULE_PARM_DESC(colorspace_conv,
Mauro Carvalho Chehab4286c6f2006-04-08 16:06:16 -030063 " Colorspace conversion:"
64 "\n 0 = disable, 1 = enable"
65 "\n Default value is 0"
66 );
Linus Torvalds1da177e2005-04-16 15:20:36 -070067
68#define ABOUT "V4L-Driver for Vision CPiA based cameras"
69
70#ifndef VID_HARDWARE_CPIA
71#define VID_HARDWARE_CPIA 24 /* FIXME -> from linux/videodev.h */
72#endif
73
74#define CPIA_MODULE_CPIA (0<<5)
75#define CPIA_MODULE_SYSTEM (1<<5)
76#define CPIA_MODULE_VP_CTRL (5<<5)
77#define CPIA_MODULE_CAPTURE (6<<5)
78#define CPIA_MODULE_DEBUG (7<<5)
79
80#define INPUT (DATA_IN << 8)
81#define OUTPUT (DATA_OUT << 8)
82
83#define CPIA_COMMAND_GetCPIAVersion (INPUT | CPIA_MODULE_CPIA | 1)
84#define CPIA_COMMAND_GetPnPID (INPUT | CPIA_MODULE_CPIA | 2)
85#define CPIA_COMMAND_GetCameraStatus (INPUT | CPIA_MODULE_CPIA | 3)
86#define CPIA_COMMAND_GotoHiPower (OUTPUT | CPIA_MODULE_CPIA | 4)
87#define CPIA_COMMAND_GotoLoPower (OUTPUT | CPIA_MODULE_CPIA | 5)
88#define CPIA_COMMAND_GotoSuspend (OUTPUT | CPIA_MODULE_CPIA | 7)
89#define CPIA_COMMAND_GotoPassThrough (OUTPUT | CPIA_MODULE_CPIA | 8)
90#define CPIA_COMMAND_ModifyCameraStatus (OUTPUT | CPIA_MODULE_CPIA | 10)
91
92#define CPIA_COMMAND_ReadVCRegs (INPUT | CPIA_MODULE_SYSTEM | 1)
93#define CPIA_COMMAND_WriteVCReg (OUTPUT | CPIA_MODULE_SYSTEM | 2)
94#define CPIA_COMMAND_ReadMCPorts (INPUT | CPIA_MODULE_SYSTEM | 3)
95#define CPIA_COMMAND_WriteMCPort (OUTPUT | CPIA_MODULE_SYSTEM | 4)
96#define CPIA_COMMAND_SetBaudRate (OUTPUT | CPIA_MODULE_SYSTEM | 5)
97#define CPIA_COMMAND_SetECPTiming (OUTPUT | CPIA_MODULE_SYSTEM | 6)
98#define CPIA_COMMAND_ReadIDATA (INPUT | CPIA_MODULE_SYSTEM | 7)
99#define CPIA_COMMAND_WriteIDATA (OUTPUT | CPIA_MODULE_SYSTEM | 8)
100#define CPIA_COMMAND_GenericCall (OUTPUT | CPIA_MODULE_SYSTEM | 9)
101#define CPIA_COMMAND_I2CStart (OUTPUT | CPIA_MODULE_SYSTEM | 10)
102#define CPIA_COMMAND_I2CStop (OUTPUT | CPIA_MODULE_SYSTEM | 11)
103#define CPIA_COMMAND_I2CWrite (OUTPUT | CPIA_MODULE_SYSTEM | 12)
104#define CPIA_COMMAND_I2CRead (INPUT | CPIA_MODULE_SYSTEM | 13)
105
106#define CPIA_COMMAND_GetVPVersion (INPUT | CPIA_MODULE_VP_CTRL | 1)
107#define CPIA_COMMAND_ResetFrameCounter (INPUT | CPIA_MODULE_VP_CTRL | 2)
108#define CPIA_COMMAND_SetColourParams (OUTPUT | CPIA_MODULE_VP_CTRL | 3)
109#define CPIA_COMMAND_SetExposure (OUTPUT | CPIA_MODULE_VP_CTRL | 4)
110#define CPIA_COMMAND_SetColourBalance (OUTPUT | CPIA_MODULE_VP_CTRL | 6)
111#define CPIA_COMMAND_SetSensorFPS (OUTPUT | CPIA_MODULE_VP_CTRL | 7)
112#define CPIA_COMMAND_SetVPDefaults (OUTPUT | CPIA_MODULE_VP_CTRL | 8)
113#define CPIA_COMMAND_SetApcor (OUTPUT | CPIA_MODULE_VP_CTRL | 9)
114#define CPIA_COMMAND_SetFlickerCtrl (OUTPUT | CPIA_MODULE_VP_CTRL | 10)
115#define CPIA_COMMAND_SetVLOffset (OUTPUT | CPIA_MODULE_VP_CTRL | 11)
116#define CPIA_COMMAND_GetColourParams (INPUT | CPIA_MODULE_VP_CTRL | 16)
117#define CPIA_COMMAND_GetColourBalance (INPUT | CPIA_MODULE_VP_CTRL | 17)
118#define CPIA_COMMAND_GetExposure (INPUT | CPIA_MODULE_VP_CTRL | 18)
119#define CPIA_COMMAND_SetSensorMatrix (OUTPUT | CPIA_MODULE_VP_CTRL | 19)
120#define CPIA_COMMAND_ColourBars (OUTPUT | CPIA_MODULE_VP_CTRL | 25)
121#define CPIA_COMMAND_ReadVPRegs (INPUT | CPIA_MODULE_VP_CTRL | 30)
122#define CPIA_COMMAND_WriteVPReg (OUTPUT | CPIA_MODULE_VP_CTRL | 31)
123
124#define CPIA_COMMAND_GrabFrame (OUTPUT | CPIA_MODULE_CAPTURE | 1)
125#define CPIA_COMMAND_UploadFrame (OUTPUT | CPIA_MODULE_CAPTURE | 2)
126#define CPIA_COMMAND_SetGrabMode (OUTPUT | CPIA_MODULE_CAPTURE | 3)
127#define CPIA_COMMAND_InitStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 4)
128#define CPIA_COMMAND_FiniStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 5)
129#define CPIA_COMMAND_StartStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 6)
130#define CPIA_COMMAND_EndStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 7)
131#define CPIA_COMMAND_SetFormat (OUTPUT | CPIA_MODULE_CAPTURE | 8)
132#define CPIA_COMMAND_SetROI (OUTPUT | CPIA_MODULE_CAPTURE | 9)
133#define CPIA_COMMAND_SetCompression (OUTPUT | CPIA_MODULE_CAPTURE | 10)
134#define CPIA_COMMAND_SetCompressionTarget (OUTPUT | CPIA_MODULE_CAPTURE | 11)
135#define CPIA_COMMAND_SetYUVThresh (OUTPUT | CPIA_MODULE_CAPTURE | 12)
136#define CPIA_COMMAND_SetCompressionParams (OUTPUT | CPIA_MODULE_CAPTURE | 13)
137#define CPIA_COMMAND_DiscardFrame (OUTPUT | CPIA_MODULE_CAPTURE | 14)
138#define CPIA_COMMAND_GrabReset (OUTPUT | CPIA_MODULE_CAPTURE | 15)
139
140#define CPIA_COMMAND_OutputRS232 (OUTPUT | CPIA_MODULE_DEBUG | 1)
141#define CPIA_COMMAND_AbortProcess (OUTPUT | CPIA_MODULE_DEBUG | 4)
142#define CPIA_COMMAND_SetDramPage (OUTPUT | CPIA_MODULE_DEBUG | 5)
143#define CPIA_COMMAND_StartDramUpload (OUTPUT | CPIA_MODULE_DEBUG | 6)
144#define CPIA_COMMAND_StartDummyDtream (OUTPUT | CPIA_MODULE_DEBUG | 8)
145#define CPIA_COMMAND_AbortStream (OUTPUT | CPIA_MODULE_DEBUG | 9)
146#define CPIA_COMMAND_DownloadDRAM (OUTPUT | CPIA_MODULE_DEBUG | 10)
147#define CPIA_COMMAND_Null (OUTPUT | CPIA_MODULE_DEBUG | 11)
148
149enum {
150 FRAME_READY, /* Ready to grab into */
151 FRAME_GRABBING, /* In the process of being grabbed into */
152 FRAME_DONE, /* Finished grabbing, but not been synced yet */
153 FRAME_UNUSED, /* Unused (no MCAPTURE) */
154};
155
156#define COMMAND_NONE 0x0000
157#define COMMAND_SETCOMPRESSION 0x0001
158#define COMMAND_SETCOMPRESSIONTARGET 0x0002
159#define COMMAND_SETCOLOURPARAMS 0x0004
160#define COMMAND_SETFORMAT 0x0008
161#define COMMAND_PAUSE 0x0010
162#define COMMAND_RESUME 0x0020
163#define COMMAND_SETYUVTHRESH 0x0040
164#define COMMAND_SETECPTIMING 0x0080
165#define COMMAND_SETCOMPRESSIONPARAMS 0x0100
166#define COMMAND_SETEXPOSURE 0x0200
167#define COMMAND_SETCOLOURBALANCE 0x0400
168#define COMMAND_SETSENSORFPS 0x0800
169#define COMMAND_SETAPCOR 0x1000
170#define COMMAND_SETFLICKERCTRL 0x2000
171#define COMMAND_SETVLOFFSET 0x4000
172#define COMMAND_SETLIGHTS 0x8000
173
174#define ROUND_UP_EXP_FOR_FLICKER 15
175
176/* Constants for automatic frame rate adjustment */
177#define MAX_EXP 302
178#define MAX_EXP_102 255
179#define LOW_EXP 140
180#define VERY_LOW_EXP 70
181#define TC 94
182#define EXP_ACC_DARK 50
183#define EXP_ACC_LIGHT 90
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300184#define HIGH_COMP_102 160
185#define MAX_COMP 239
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186#define DARK_TIME 3
187#define LIGHT_TIME 3
188
189/* Maximum number of 10ms loops to wait for the stream to become ready */
190#define READY_TIMEOUT 100
191
192/* Developer's Guide Table 5 p 3-34
193 * indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/
194static u8 flicker_jumps[2][2][4] =
195{ { { 76, 38, 19, 9 }, { 92, 46, 23, 11 } },
196 { { 64, 32, 16, 8 }, { 76, 38, 19, 9} }
197};
198
199/* forward declaration of local function */
200static void reset_camera_struct(struct cam_data *cam);
201static int find_over_exposure(int brightness);
202static void set_flicker(struct cam_params *params, volatile u32 *command_flags,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300203 int on);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204
205
206/**********************************************************************
207 *
208 * Memory management
209 *
210 **********************************************************************/
211static void *rvmalloc(unsigned long size)
212{
213 void *mem;
214 unsigned long adr;
215
216 size = PAGE_ALIGN(size);
217 mem = vmalloc_32(size);
218 if (!mem)
219 return NULL;
220
221 memset(mem, 0, size); /* Clear the ram out, no junk to the user */
222 adr = (unsigned long) mem;
223 while (size > 0) {
224 SetPageReserved(vmalloc_to_page((void *)adr));
225 adr += PAGE_SIZE;
226 size -= PAGE_SIZE;
227 }
228
229 return mem;
230}
231
232static void rvfree(void *mem, unsigned long size)
233{
234 unsigned long adr;
235
236 if (!mem)
237 return;
238
239 adr = (unsigned long) mem;
240 while ((long) size > 0) {
241 ClearPageReserved(vmalloc_to_page((void *)adr));
242 adr += PAGE_SIZE;
243 size -= PAGE_SIZE;
244 }
245 vfree(mem);
246}
247
248/**********************************************************************
249 *
250 * /proc interface
251 *
252 **********************************************************************/
253#ifdef CONFIG_PROC_FS
254static struct proc_dir_entry *cpia_proc_root=NULL;
255
256static int cpia_read_proc(char *page, char **start, off_t off,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300257 int count, int *eof, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258{
259 char *out = page;
260 int len, tmp;
261 struct cam_data *cam = data;
262 char tmpstr[29];
263
264 /* IMPORTANT: This output MUST be kept under PAGE_SIZE
265 * or we need to get more sophisticated. */
266
267 out += sprintf(out, "read-only\n-----------------------\n");
268 out += sprintf(out, "V4L Driver version: %d.%d.%d\n",
269 CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER);
270 out += sprintf(out, "CPIA Version: %d.%02d (%d.%d)\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300271 cam->params.version.firmwareVersion,
272 cam->params.version.firmwareRevision,
273 cam->params.version.vcVersion,
274 cam->params.version.vcRevision);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 out += sprintf(out, "CPIA PnP-ID: %04x:%04x:%04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300276 cam->params.pnpID.vendor, cam->params.pnpID.product,
277 cam->params.pnpID.deviceRevision);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 out += sprintf(out, "VP-Version: %d.%d %04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300279 cam->params.vpVersion.vpVersion,
280 cam->params.vpVersion.vpRevision,
281 cam->params.vpVersion.cameraHeadID);
282
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283 out += sprintf(out, "system_state: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300284 cam->params.status.systemState);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 out += sprintf(out, "grab_state: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300286 cam->params.status.grabState);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287 out += sprintf(out, "stream_state: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300288 cam->params.status.streamState);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289 out += sprintf(out, "fatal_error: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300290 cam->params.status.fatalError);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 out += sprintf(out, "cmd_error: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300292 cam->params.status.cmdError);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293 out += sprintf(out, "debug_flags: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300294 cam->params.status.debugFlags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 out += sprintf(out, "vp_status: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300296 cam->params.status.vpStatus);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297 out += sprintf(out, "error_code: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300298 cam->params.status.errorCode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299 /* QX3 specific entries */
300 if (cam->params.qx3.qx3_detected) {
301 out += sprintf(out, "button: %4d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300302 cam->params.qx3.button);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 out += sprintf(out, "cradled: %4d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300304 cam->params.qx3.cradled);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305 }
306 out += sprintf(out, "video_size: %s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300307 cam->params.format.videoSize == VIDEOSIZE_CIF ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308 "CIF " : "QCIF");
309 out += sprintf(out, "roi: (%3d, %3d) to (%3d, %3d)\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300310 cam->params.roi.colStart*8,
311 cam->params.roi.rowStart*4,
312 cam->params.roi.colEnd*8,
313 cam->params.roi.rowEnd*4);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314 out += sprintf(out, "actual_fps: %3d\n", cam->fps);
315 out += sprintf(out, "transfer_rate: %4dkB/s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300316 cam->transfer_rate);
317
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318 out += sprintf(out, "\nread-write\n");
319 out += sprintf(out, "----------------------- current min"
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300320 " max default comment\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321 out += sprintf(out, "brightness: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300322 cam->params.colourParams.brightness, 0, 100, 50);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 if (cam->params.version.firmwareVersion == 1 &&
324 cam->params.version.firmwareRevision == 2)
325 /* 1-02 firmware limits contrast to 80 */
326 tmp = 80;
327 else
328 tmp = 96;
329
330 out += sprintf(out, "contrast: %8d %8d %8d %8d"
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300331 " steps of 8\n",
332 cam->params.colourParams.contrast, 0, tmp, 48);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 out += sprintf(out, "saturation: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300334 cam->params.colourParams.saturation, 0, 100, 50);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335 tmp = (25000+5000*cam->params.sensorFps.baserate)/
336 (1<<cam->params.sensorFps.divisor);
337 out += sprintf(out, "sensor_fps: %4d.%03d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300338 tmp/1000, tmp%1000, 3, 30, 15);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339 out += sprintf(out, "stream_start_line: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300340 2*cam->params.streamStartLine, 0,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 cam->params.format.videoSize == VIDEOSIZE_CIF ? 288:144,
342 cam->params.format.videoSize == VIDEOSIZE_CIF ? 240:120);
343 out += sprintf(out, "sub_sample: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300344 cam->params.format.subSample == SUBSAMPLE_420 ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345 "420" : "422", "420", "422", "422");
346 out += sprintf(out, "yuv_order: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300347 cam->params.format.yuvOrder == YUVORDER_YUYV ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348 "YUYV" : "UYVY", "YUYV" , "UYVY", "YUYV");
349 out += sprintf(out, "ecp_timing: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300350 cam->params.ecpTiming ? "slow" : "normal", "slow",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351 "normal", "normal");
352
353 if (cam->params.colourBalance.balanceMode == 2) {
354 sprintf(tmpstr, "auto");
355 } else {
356 sprintf(tmpstr, "manual");
357 }
358 out += sprintf(out, "color_balance_mode: %8s %8s %8s"
359 " %8s\n", tmpstr, "manual", "auto", "auto");
360 out += sprintf(out, "red_gain: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300361 cam->params.colourBalance.redGain, 0, 212, 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362 out += sprintf(out, "green_gain: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300363 cam->params.colourBalance.greenGain, 0, 212, 6);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364 out += sprintf(out, "blue_gain: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300365 cam->params.colourBalance.blueGain, 0, 212, 92);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366
367 if (cam->params.version.firmwareVersion == 1 &&
368 cam->params.version.firmwareRevision == 2)
369 /* 1-02 firmware limits gain to 2 */
370 sprintf(tmpstr, "%8d %8d %8d", 1, 2, 2);
371 else
372 sprintf(tmpstr, "%8d %8d %8d", 1, 8, 2);
373
374 if (cam->params.exposure.gainMode == 0)
375 out += sprintf(out, "max_gain: unknown %28s"
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300376 " powers of 2\n", tmpstr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377 else
378 out += sprintf(out, "max_gain: %8d %28s"
379 " 1,2,4 or 8 \n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300380 1<<(cam->params.exposure.gainMode-1), tmpstr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381
382 switch(cam->params.exposure.expMode) {
383 case 1:
384 case 3:
385 sprintf(tmpstr, "manual");
386 break;
387 case 2:
388 sprintf(tmpstr, "auto");
389 break;
390 default:
391 sprintf(tmpstr, "unknown");
392 break;
393 }
394 out += sprintf(out, "exposure_mode: %8s %8s %8s"
395 " %8s\n", tmpstr, "manual", "auto", "auto");
396 out += sprintf(out, "centre_weight: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300397 (2-cam->params.exposure.centreWeight) ? "on" : "off",
398 "off", "on", "on");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 out += sprintf(out, "gain: %8d %8d max_gain %8d 1,2,4,8 possible\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300400 1<<cam->params.exposure.gain, 1, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401 if (cam->params.version.firmwareVersion == 1 &&
402 cam->params.version.firmwareRevision == 2)
403 /* 1-02 firmware limits fineExp/2 to 127 */
404 tmp = 254;
405 else
406 tmp = 510;
407
408 out += sprintf(out, "fine_exp: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300409 cam->params.exposure.fineExp*2, 0, tmp, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410 if (cam->params.version.firmwareVersion == 1 &&
411 cam->params.version.firmwareRevision == 2)
412 /* 1-02 firmware limits coarseExpHi to 0 */
413 tmp = MAX_EXP_102;
414 else
415 tmp = MAX_EXP;
416
417 out += sprintf(out, "coarse_exp: %8d %8d %8d"
418 " %8d\n", cam->params.exposure.coarseExpLo+
419 256*cam->params.exposure.coarseExpHi, 0, tmp, 185);
420 out += sprintf(out, "red_comp: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300421 cam->params.exposure.redComp, COMP_RED, 255, COMP_RED);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422 out += sprintf(out, "green1_comp: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300423 cam->params.exposure.green1Comp, COMP_GREEN1, 255,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424 COMP_GREEN1);
425 out += sprintf(out, "green2_comp: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300426 cam->params.exposure.green2Comp, COMP_GREEN2, 255,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427 COMP_GREEN2);
428 out += sprintf(out, "blue_comp: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300429 cam->params.exposure.blueComp, COMP_BLUE, 255, COMP_BLUE);
430
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431 out += sprintf(out, "apcor_gain1: %#8x %#8x %#8x %#8x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300432 cam->params.apcor.gain1, 0, 0xff, 0x1c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433 out += sprintf(out, "apcor_gain2: %#8x %#8x %#8x %#8x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300434 cam->params.apcor.gain2, 0, 0xff, 0x1a);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435 out += sprintf(out, "apcor_gain4: %#8x %#8x %#8x %#8x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300436 cam->params.apcor.gain4, 0, 0xff, 0x2d);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437 out += sprintf(out, "apcor_gain8: %#8x %#8x %#8x %#8x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300438 cam->params.apcor.gain8, 0, 0xff, 0x2a);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439 out += sprintf(out, "vl_offset_gain1: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300440 cam->params.vlOffset.gain1, 0, 255, 24);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441 out += sprintf(out, "vl_offset_gain2: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300442 cam->params.vlOffset.gain2, 0, 255, 28);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443 out += sprintf(out, "vl_offset_gain4: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300444 cam->params.vlOffset.gain4, 0, 255, 30);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445 out += sprintf(out, "vl_offset_gain8: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300446 cam->params.vlOffset.gain8, 0, 255, 30);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447 out += sprintf(out, "flicker_control: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300448 cam->params.flickerControl.flickerMode ? "on" : "off",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449 "off", "on", "off");
450 out += sprintf(out, "mains_frequency: %8d %8d %8d %8d"
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300451 " only 50/60\n",
452 cam->mainsFreq ? 60 : 50, 50, 60, 50);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453 if(cam->params.flickerControl.allowableOverExposure < 0)
454 out += sprintf(out, "allowable_overexposure: %4dauto auto %8d auto\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300455 -cam->params.flickerControl.allowableOverExposure,
456 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457 else
458 out += sprintf(out, "allowable_overexposure: %8d auto %8d auto\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300459 cam->params.flickerControl.allowableOverExposure,
460 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461 out += sprintf(out, "compression_mode: ");
462 switch(cam->params.compression.mode) {
463 case CPIA_COMPRESSION_NONE:
464 out += sprintf(out, "%8s", "none");
465 break;
466 case CPIA_COMPRESSION_AUTO:
467 out += sprintf(out, "%8s", "auto");
468 break;
469 case CPIA_COMPRESSION_MANUAL:
470 out += sprintf(out, "%8s", "manual");
471 break;
472 default:
473 out += sprintf(out, "%8s", "unknown");
474 break;
475 }
476 out += sprintf(out, " none,auto,manual auto\n");
477 out += sprintf(out, "decimation_enable: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300478 cam->params.compression.decimation ==
479 DECIMATION_ENAB ? "on":"off", "off", "on",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480 "off");
481 out += sprintf(out, "compression_target: %9s %9s %9s %9s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300482 cam->params.compressionTarget.frTargeting ==
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483 CPIA_COMPRESSION_TARGET_FRAMERATE ?
484 "framerate":"quality",
485 "framerate", "quality", "quality");
486 out += sprintf(out, "target_framerate: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300487 cam->params.compressionTarget.targetFR, 1, 30, 15);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488 out += sprintf(out, "target_quality: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300489 cam->params.compressionTarget.targetQ, 1, 64, 5);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490 out += sprintf(out, "y_threshold: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300491 cam->params.yuvThreshold.yThreshold, 0, 31, 6);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492 out += sprintf(out, "uv_threshold: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300493 cam->params.yuvThreshold.uvThreshold, 0, 31, 6);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494 out += sprintf(out, "hysteresis: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300495 cam->params.compressionParams.hysteresis, 0, 255, 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496 out += sprintf(out, "threshold_max: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300497 cam->params.compressionParams.threshMax, 0, 255, 11);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498 out += sprintf(out, "small_step: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300499 cam->params.compressionParams.smallStep, 0, 255, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 out += sprintf(out, "large_step: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300501 cam->params.compressionParams.largeStep, 0, 255, 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502 out += sprintf(out, "decimation_hysteresis: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300503 cam->params.compressionParams.decimationHysteresis,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504 0, 255, 2);
505 out += sprintf(out, "fr_diff_step_thresh: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300506 cam->params.compressionParams.frDiffStepThresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507 0, 255, 5);
508 out += sprintf(out, "q_diff_step_thresh: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300509 cam->params.compressionParams.qDiffStepThresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510 0, 255, 3);
511 out += sprintf(out, "decimation_thresh_mod: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300512 cam->params.compressionParams.decimationThreshMod,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513 0, 255, 2);
514 /* QX3 specific entries */
515 if (cam->params.qx3.qx3_detected) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300516 out += sprintf(out, "toplight: %8s %8s %8s %8s\n",
517 cam->params.qx3.toplight ? "on" : "off",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 "off", "on", "off");
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300519 out += sprintf(out, "bottomlight: %8s %8s %8s %8s\n",
520 cam->params.qx3.bottomlight ? "on" : "off",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 "off", "on", "off");
522 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300523
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524 len = out - page;
525 len -= off;
526 if (len < count) {
527 *eof = 1;
528 if (len <= 0) return 0;
529 } else
530 len = count;
531
532 *start = page + off;
533 return len;
534}
535
536
537static int match(char *checkstr, char **buffer, unsigned long *count,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300538 int *find_colon, int *err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539{
540 int ret, colon_found = 1;
541 int len = strlen(checkstr);
542 ret = (len <= *count && strncmp(*buffer, checkstr, len) == 0);
543 if (ret) {
544 *buffer += len;
545 *count -= len;
546 if (*find_colon) {
547 colon_found = 0;
548 while (*count && (**buffer == ' ' || **buffer == '\t' ||
549 (!colon_found && **buffer == ':'))) {
550 if (**buffer == ':')
551 colon_found = 1;
552 --*count;
553 ++*buffer;
554 }
555 if (!*count || !colon_found)
556 *err = -EINVAL;
557 *find_colon = 0;
558 }
559 }
560 return ret;
561}
562
563static unsigned long int value(char **buffer, unsigned long *count, int *err)
564{
565 char *p;
566 unsigned long int ret;
567 ret = simple_strtoul(*buffer, &p, 0);
568 if (p == *buffer)
569 *err = -EINVAL;
570 else {
571 *count -= p - *buffer;
572 *buffer = p;
573 }
574 return ret;
575}
576
577static int cpia_write_proc(struct file *file, const char __user *buf,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300578 unsigned long count, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579{
580 struct cam_data *cam = data;
581 struct cam_params new_params;
582 char *page, *buffer;
583 int retval, find_colon;
584 int size = count;
585 unsigned long val = 0;
586 u32 command_flags = 0;
587 u8 new_mains;
588
589 /*
590 * This code to copy from buf to page is shamelessly copied
591 * from the comx driver
592 */
593 if (count > PAGE_SIZE) {
594 printk(KERN_ERR "count is %lu > %d!!!\n", count, (int)PAGE_SIZE);
595 return -ENOSPC;
596 }
597
598 if (!(page = (char *)__get_free_page(GFP_KERNEL))) return -ENOMEM;
599
600 if(copy_from_user(page, buf, count))
601 {
602 retval = -EFAULT;
603 goto out;
604 }
605
606 if (page[count-1] == '\n')
607 page[count-1] = '\0';
608 else if (count < PAGE_SIZE)
609 page[count] = '\0';
610 else if (page[count]) {
611 retval = -EINVAL;
612 goto out;
613 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300614
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615 buffer = page;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300616
Ingo Molnar3593cab2006-02-07 06:49:14 -0200617 if (mutex_lock_interruptible(&cam->param_lock))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618 return -ERESTARTSYS;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300619
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620 /*
621 * Skip over leading whitespace
622 */
623 while (count && isspace(*buffer)) {
624 --count;
625 ++buffer;
626 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300627
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628 memcpy(&new_params, &cam->params, sizeof(struct cam_params));
629 new_mains = cam->mainsFreq;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300630
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631#define MATCH(x) (match(x, &buffer, &count, &find_colon, &retval))
632#define VALUE (value(&buffer,&count, &retval))
633#define FIRMWARE_VERSION(x,y) (new_params.version.firmwareVersion == (x) && \
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300634 new_params.version.firmwareRevision == (y))
635
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636 retval = 0;
637 while (count && !retval) {
638 find_colon = 1;
639 if (MATCH("brightness")) {
640 if (!retval)
641 val = VALUE;
642
643 if (!retval) {
644 if (val <= 100)
645 new_params.colourParams.brightness = val;
646 else
647 retval = -EINVAL;
648 }
649 command_flags |= COMMAND_SETCOLOURPARAMS;
650 if(new_params.flickerControl.allowableOverExposure < 0)
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300651 new_params.flickerControl.allowableOverExposure =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652 -find_over_exposure(new_params.colourParams.brightness);
653 if(new_params.flickerControl.flickerMode != 0)
654 command_flags |= COMMAND_SETFLICKERCTRL;
655
656 } else if (MATCH("contrast")) {
657 if (!retval)
658 val = VALUE;
659
660 if (!retval) {
661 if (val <= 100) {
662 /* contrast is in steps of 8, so round*/
663 val = ((val + 3) / 8) * 8;
664 /* 1-02 firmware limits contrast to 80*/
665 if (FIRMWARE_VERSION(1,2) && val > 80)
666 val = 80;
667
668 new_params.colourParams.contrast = val;
669 } else
670 retval = -EINVAL;
671 }
672 command_flags |= COMMAND_SETCOLOURPARAMS;
673 } else if (MATCH("saturation")) {
674 if (!retval)
675 val = VALUE;
676
677 if (!retval) {
678 if (val <= 100)
679 new_params.colourParams.saturation = val;
680 else
681 retval = -EINVAL;
682 }
683 command_flags |= COMMAND_SETCOLOURPARAMS;
684 } else if (MATCH("sensor_fps")) {
685 if (!retval)
686 val = VALUE;
687
688 if (!retval) {
689 /* find values so that sensorFPS is minimized,
690 * but >= val */
691 if (val > 30)
692 retval = -EINVAL;
693 else if (val > 25) {
694 new_params.sensorFps.divisor = 0;
695 new_params.sensorFps.baserate = 1;
696 } else if (val > 15) {
697 new_params.sensorFps.divisor = 0;
698 new_params.sensorFps.baserate = 0;
699 } else if (val > 12) {
700 new_params.sensorFps.divisor = 1;
701 new_params.sensorFps.baserate = 1;
702 } else if (val > 7) {
703 new_params.sensorFps.divisor = 1;
704 new_params.sensorFps.baserate = 0;
705 } else if (val > 6) {
706 new_params.sensorFps.divisor = 2;
707 new_params.sensorFps.baserate = 1;
708 } else if (val > 3) {
709 new_params.sensorFps.divisor = 2;
710 new_params.sensorFps.baserate = 0;
711 } else {
712 new_params.sensorFps.divisor = 3;
713 /* Either base rate would work here */
714 new_params.sensorFps.baserate = 1;
715 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300716 new_params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717 flicker_jumps[new_mains]
718 [new_params.sensorFps.baserate]
719 [new_params.sensorFps.divisor];
720 if (new_params.flickerControl.flickerMode)
721 command_flags |= COMMAND_SETFLICKERCTRL;
722 }
723 command_flags |= COMMAND_SETSENSORFPS;
724 cam->exposure_status = EXPOSURE_NORMAL;
725 } else if (MATCH("stream_start_line")) {
726 if (!retval)
727 val = VALUE;
728
729 if (!retval) {
730 int max_line = 288;
731
732 if (new_params.format.videoSize == VIDEOSIZE_QCIF)
733 max_line = 144;
734 if (val <= max_line)
735 new_params.streamStartLine = val/2;
736 else
737 retval = -EINVAL;
738 }
739 } else if (MATCH("sub_sample")) {
740 if (!retval && MATCH("420"))
741 new_params.format.subSample = SUBSAMPLE_420;
742 else if (!retval && MATCH("422"))
743 new_params.format.subSample = SUBSAMPLE_422;
744 else
745 retval = -EINVAL;
746
747 command_flags |= COMMAND_SETFORMAT;
748 } else if (MATCH("yuv_order")) {
749 if (!retval && MATCH("YUYV"))
750 new_params.format.yuvOrder = YUVORDER_YUYV;
751 else if (!retval && MATCH("UYVY"))
752 new_params.format.yuvOrder = YUVORDER_UYVY;
753 else
754 retval = -EINVAL;
755
756 command_flags |= COMMAND_SETFORMAT;
757 } else if (MATCH("ecp_timing")) {
758 if (!retval && MATCH("normal"))
759 new_params.ecpTiming = 0;
760 else if (!retval && MATCH("slow"))
761 new_params.ecpTiming = 1;
762 else
763 retval = -EINVAL;
764
765 command_flags |= COMMAND_SETECPTIMING;
766 } else if (MATCH("color_balance_mode")) {
767 if (!retval && MATCH("manual"))
768 new_params.colourBalance.balanceMode = 3;
769 else if (!retval && MATCH("auto"))
770 new_params.colourBalance.balanceMode = 2;
771 else
772 retval = -EINVAL;
773
774 command_flags |= COMMAND_SETCOLOURBALANCE;
775 } else if (MATCH("red_gain")) {
776 if (!retval)
777 val = VALUE;
778
779 if (!retval) {
780 if (val <= 212) {
781 new_params.colourBalance.redGain = val;
782 new_params.colourBalance.balanceMode = 1;
783 } else
784 retval = -EINVAL;
785 }
786 command_flags |= COMMAND_SETCOLOURBALANCE;
787 } else if (MATCH("green_gain")) {
788 if (!retval)
789 val = VALUE;
790
791 if (!retval) {
792 if (val <= 212) {
793 new_params.colourBalance.greenGain = val;
794 new_params.colourBalance.balanceMode = 1;
795 } else
796 retval = -EINVAL;
797 }
798 command_flags |= COMMAND_SETCOLOURBALANCE;
799 } else if (MATCH("blue_gain")) {
800 if (!retval)
801 val = VALUE;
802
803 if (!retval) {
804 if (val <= 212) {
805 new_params.colourBalance.blueGain = val;
806 new_params.colourBalance.balanceMode = 1;
807 } else
808 retval = -EINVAL;
809 }
810 command_flags |= COMMAND_SETCOLOURBALANCE;
811 } else if (MATCH("max_gain")) {
812 if (!retval)
813 val = VALUE;
814
815 if (!retval) {
816 /* 1-02 firmware limits gain to 2 */
817 if (FIRMWARE_VERSION(1,2) && val > 2)
818 val = 2;
819 switch(val) {
820 case 1:
821 new_params.exposure.gainMode = 1;
822 break;
823 case 2:
824 new_params.exposure.gainMode = 2;
825 break;
826 case 4:
827 new_params.exposure.gainMode = 3;
828 break;
829 case 8:
830 new_params.exposure.gainMode = 4;
831 break;
832 default:
833 retval = -EINVAL;
834 break;
835 }
836 }
837 command_flags |= COMMAND_SETEXPOSURE;
838 } else if (MATCH("exposure_mode")) {
839 if (!retval && MATCH("auto"))
840 new_params.exposure.expMode = 2;
841 else if (!retval && MATCH("manual")) {
842 if (new_params.exposure.expMode == 2)
843 new_params.exposure.expMode = 3;
844 if(new_params.flickerControl.flickerMode != 0)
845 command_flags |= COMMAND_SETFLICKERCTRL;
846 new_params.flickerControl.flickerMode = 0;
847 } else
848 retval = -EINVAL;
849
850 command_flags |= COMMAND_SETEXPOSURE;
851 } else if (MATCH("centre_weight")) {
852 if (!retval && MATCH("on"))
853 new_params.exposure.centreWeight = 1;
854 else if (!retval && MATCH("off"))
855 new_params.exposure.centreWeight = 2;
856 else
857 retval = -EINVAL;
858
859 command_flags |= COMMAND_SETEXPOSURE;
860 } else if (MATCH("gain")) {
861 if (!retval)
862 val = VALUE;
863
864 if (!retval) {
865 switch(val) {
866 case 1:
867 new_params.exposure.gain = 0;
868 break;
869 case 2:
870 new_params.exposure.gain = 1;
871 break;
872 case 4:
873 new_params.exposure.gain = 2;
874 break;
875 case 8:
876 new_params.exposure.gain = 3;
877 break;
878 default:
879 retval = -EINVAL;
880 break;
881 }
882 new_params.exposure.expMode = 1;
883 if(new_params.flickerControl.flickerMode != 0)
884 command_flags |= COMMAND_SETFLICKERCTRL;
885 new_params.flickerControl.flickerMode = 0;
886 command_flags |= COMMAND_SETEXPOSURE;
887 if (new_params.exposure.gain >
888 new_params.exposure.gainMode-1)
889 retval = -EINVAL;
890 }
891 } else if (MATCH("fine_exp")) {
892 if (!retval)
893 val = VALUE/2;
894
895 if (!retval) {
896 if (val < 256) {
897 /* 1-02 firmware limits fineExp/2 to 127*/
898 if (FIRMWARE_VERSION(1,2) && val > 127)
899 val = 127;
900 new_params.exposure.fineExp = val;
901 new_params.exposure.expMode = 1;
902 command_flags |= COMMAND_SETEXPOSURE;
903 if(new_params.flickerControl.flickerMode != 0)
904 command_flags |= COMMAND_SETFLICKERCTRL;
905 new_params.flickerControl.flickerMode = 0;
906 command_flags |= COMMAND_SETFLICKERCTRL;
907 } else
908 retval = -EINVAL;
909 }
910 } else if (MATCH("coarse_exp")) {
911 if (!retval)
912 val = VALUE;
913
914 if (!retval) {
915 if (val <= MAX_EXP) {
916 if (FIRMWARE_VERSION(1,2) &&
917 val > MAX_EXP_102)
918 val = MAX_EXP_102;
919 new_params.exposure.coarseExpLo =
920 val & 0xff;
921 new_params.exposure.coarseExpHi =
922 val >> 8;
923 new_params.exposure.expMode = 1;
924 command_flags |= COMMAND_SETEXPOSURE;
925 if(new_params.flickerControl.flickerMode != 0)
926 command_flags |= COMMAND_SETFLICKERCTRL;
927 new_params.flickerControl.flickerMode = 0;
928 command_flags |= COMMAND_SETFLICKERCTRL;
929 } else
930 retval = -EINVAL;
931 }
932 } else if (MATCH("red_comp")) {
933 if (!retval)
934 val = VALUE;
935
936 if (!retval) {
937 if (val >= COMP_RED && val <= 255) {
938 new_params.exposure.redComp = val;
939 new_params.exposure.compMode = 1;
940 command_flags |= COMMAND_SETEXPOSURE;
941 } else
942 retval = -EINVAL;
943 }
944 } else if (MATCH("green1_comp")) {
945 if (!retval)
946 val = VALUE;
947
948 if (!retval) {
949 if (val >= COMP_GREEN1 && val <= 255) {
950 new_params.exposure.green1Comp = val;
951 new_params.exposure.compMode = 1;
952 command_flags |= COMMAND_SETEXPOSURE;
953 } else
954 retval = -EINVAL;
955 }
956 } else if (MATCH("green2_comp")) {
957 if (!retval)
958 val = VALUE;
959
960 if (!retval) {
961 if (val >= COMP_GREEN2 && val <= 255) {
962 new_params.exposure.green2Comp = val;
963 new_params.exposure.compMode = 1;
964 command_flags |= COMMAND_SETEXPOSURE;
965 } else
966 retval = -EINVAL;
967 }
968 } else if (MATCH("blue_comp")) {
969 if (!retval)
970 val = VALUE;
971
972 if (!retval) {
973 if (val >= COMP_BLUE && val <= 255) {
974 new_params.exposure.blueComp = val;
975 new_params.exposure.compMode = 1;
976 command_flags |= COMMAND_SETEXPOSURE;
977 } else
978 retval = -EINVAL;
979 }
980 } else if (MATCH("apcor_gain1")) {
981 if (!retval)
982 val = VALUE;
983
984 if (!retval) {
985 command_flags |= COMMAND_SETAPCOR;
986 if (val <= 0xff)
987 new_params.apcor.gain1 = val;
988 else
989 retval = -EINVAL;
990 }
991 } else if (MATCH("apcor_gain2")) {
992 if (!retval)
993 val = VALUE;
994
995 if (!retval) {
996 command_flags |= COMMAND_SETAPCOR;
997 if (val <= 0xff)
998 new_params.apcor.gain2 = val;
999 else
1000 retval = -EINVAL;
1001 }
1002 } else if (MATCH("apcor_gain4")) {
1003 if (!retval)
1004 val = VALUE;
1005
1006 if (!retval) {
1007 command_flags |= COMMAND_SETAPCOR;
1008 if (val <= 0xff)
1009 new_params.apcor.gain4 = val;
1010 else
1011 retval = -EINVAL;
1012 }
1013 } else if (MATCH("apcor_gain8")) {
1014 if (!retval)
1015 val = VALUE;
1016
1017 if (!retval) {
1018 command_flags |= COMMAND_SETAPCOR;
1019 if (val <= 0xff)
1020 new_params.apcor.gain8 = val;
1021 else
1022 retval = -EINVAL;
1023 }
1024 } else if (MATCH("vl_offset_gain1")) {
1025 if (!retval)
1026 val = VALUE;
1027
1028 if (!retval) {
1029 if (val <= 0xff)
1030 new_params.vlOffset.gain1 = val;
1031 else
1032 retval = -EINVAL;
1033 }
1034 command_flags |= COMMAND_SETVLOFFSET;
1035 } else if (MATCH("vl_offset_gain2")) {
1036 if (!retval)
1037 val = VALUE;
1038
1039 if (!retval) {
1040 if (val <= 0xff)
1041 new_params.vlOffset.gain2 = val;
1042 else
1043 retval = -EINVAL;
1044 }
1045 command_flags |= COMMAND_SETVLOFFSET;
1046 } else if (MATCH("vl_offset_gain4")) {
1047 if (!retval)
1048 val = VALUE;
1049
1050 if (!retval) {
1051 if (val <= 0xff)
1052 new_params.vlOffset.gain4 = val;
1053 else
1054 retval = -EINVAL;
1055 }
1056 command_flags |= COMMAND_SETVLOFFSET;
1057 } else if (MATCH("vl_offset_gain8")) {
1058 if (!retval)
1059 val = VALUE;
1060
1061 if (!retval) {
1062 if (val <= 0xff)
1063 new_params.vlOffset.gain8 = val;
1064 else
1065 retval = -EINVAL;
1066 }
1067 command_flags |= COMMAND_SETVLOFFSET;
1068 } else if (MATCH("flicker_control")) {
1069 if (!retval && MATCH("on")) {
1070 set_flicker(&new_params, &command_flags, 1);
1071 } else if (!retval && MATCH("off")) {
1072 set_flicker(&new_params, &command_flags, 0);
1073 } else
1074 retval = -EINVAL;
1075
1076 command_flags |= COMMAND_SETFLICKERCTRL;
1077 } else if (MATCH("mains_frequency")) {
1078 if (!retval && MATCH("50")) {
1079 new_mains = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001080 new_params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081 flicker_jumps[new_mains]
1082 [new_params.sensorFps.baserate]
1083 [new_params.sensorFps.divisor];
1084 if (new_params.flickerControl.flickerMode)
1085 command_flags |= COMMAND_SETFLICKERCTRL;
1086 } else if (!retval && MATCH("60")) {
1087 new_mains = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001088 new_params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089 flicker_jumps[new_mains]
1090 [new_params.sensorFps.baserate]
1091 [new_params.sensorFps.divisor];
1092 if (new_params.flickerControl.flickerMode)
1093 command_flags |= COMMAND_SETFLICKERCTRL;
1094 } else
1095 retval = -EINVAL;
1096 } else if (MATCH("allowable_overexposure")) {
1097 if (!retval && MATCH("auto")) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001098 new_params.flickerControl.allowableOverExposure =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001099 -find_over_exposure(new_params.colourParams.brightness);
1100 if(new_params.flickerControl.flickerMode != 0)
1101 command_flags |= COMMAND_SETFLICKERCTRL;
1102 } else {
1103 if (!retval)
1104 val = VALUE;
1105
1106 if (!retval) {
1107 if (val <= 0xff) {
1108 new_params.flickerControl.
1109 allowableOverExposure = val;
1110 if(new_params.flickerControl.flickerMode != 0)
1111 command_flags |= COMMAND_SETFLICKERCTRL;
1112 } else
1113 retval = -EINVAL;
1114 }
1115 }
1116 } else if (MATCH("compression_mode")) {
1117 if (!retval && MATCH("none"))
1118 new_params.compression.mode =
1119 CPIA_COMPRESSION_NONE;
1120 else if (!retval && MATCH("auto"))
1121 new_params.compression.mode =
1122 CPIA_COMPRESSION_AUTO;
1123 else if (!retval && MATCH("manual"))
1124 new_params.compression.mode =
1125 CPIA_COMPRESSION_MANUAL;
1126 else
1127 retval = -EINVAL;
1128
1129 command_flags |= COMMAND_SETCOMPRESSION;
1130 } else if (MATCH("decimation_enable")) {
1131 if (!retval && MATCH("off"))
1132 new_params.compression.decimation = 0;
1133 else if (!retval && MATCH("on"))
1134 new_params.compression.decimation = 1;
1135 else
1136 retval = -EINVAL;
1137
1138 command_flags |= COMMAND_SETCOMPRESSION;
1139 } else if (MATCH("compression_target")) {
1140 if (!retval && MATCH("quality"))
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001141 new_params.compressionTarget.frTargeting =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142 CPIA_COMPRESSION_TARGET_QUALITY;
1143 else if (!retval && MATCH("framerate"))
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001144 new_params.compressionTarget.frTargeting =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001145 CPIA_COMPRESSION_TARGET_FRAMERATE;
1146 else
1147 retval = -EINVAL;
1148
1149 command_flags |= COMMAND_SETCOMPRESSIONTARGET;
1150 } else if (MATCH("target_framerate")) {
1151 if (!retval)
1152 val = VALUE;
1153
1154 if (!retval) {
1155 if(val > 0 && val <= 30)
1156 new_params.compressionTarget.targetFR = val;
1157 else
1158 retval = -EINVAL;
1159 }
1160 command_flags |= COMMAND_SETCOMPRESSIONTARGET;
1161 } else if (MATCH("target_quality")) {
1162 if (!retval)
1163 val = VALUE;
1164
1165 if (!retval) {
1166 if(val > 0 && val <= 64)
1167 new_params.compressionTarget.targetQ = val;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001168 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169 retval = -EINVAL;
1170 }
1171 command_flags |= COMMAND_SETCOMPRESSIONTARGET;
1172 } else if (MATCH("y_threshold")) {
1173 if (!retval)
1174 val = VALUE;
1175
1176 if (!retval) {
1177 if (val < 32)
1178 new_params.yuvThreshold.yThreshold = val;
1179 else
1180 retval = -EINVAL;
1181 }
1182 command_flags |= COMMAND_SETYUVTHRESH;
1183 } else if (MATCH("uv_threshold")) {
1184 if (!retval)
1185 val = VALUE;
1186
1187 if (!retval) {
1188 if (val < 32)
1189 new_params.yuvThreshold.uvThreshold = val;
1190 else
1191 retval = -EINVAL;
1192 }
1193 command_flags |= COMMAND_SETYUVTHRESH;
1194 } else if (MATCH("hysteresis")) {
1195 if (!retval)
1196 val = VALUE;
1197
1198 if (!retval) {
1199 if (val <= 0xff)
1200 new_params.compressionParams.hysteresis = val;
1201 else
1202 retval = -EINVAL;
1203 }
1204 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1205 } else if (MATCH("threshold_max")) {
1206 if (!retval)
1207 val = VALUE;
1208
1209 if (!retval) {
1210 if (val <= 0xff)
1211 new_params.compressionParams.threshMax = val;
1212 else
1213 retval = -EINVAL;
1214 }
1215 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1216 } else if (MATCH("small_step")) {
1217 if (!retval)
1218 val = VALUE;
1219
1220 if (!retval) {
1221 if (val <= 0xff)
1222 new_params.compressionParams.smallStep = val;
1223 else
1224 retval = -EINVAL;
1225 }
1226 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1227 } else if (MATCH("large_step")) {
1228 if (!retval)
1229 val = VALUE;
1230
1231 if (!retval) {
1232 if (val <= 0xff)
1233 new_params.compressionParams.largeStep = val;
1234 else
1235 retval = -EINVAL;
1236 }
1237 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1238 } else if (MATCH("decimation_hysteresis")) {
1239 if (!retval)
1240 val = VALUE;
1241
1242 if (!retval) {
1243 if (val <= 0xff)
1244 new_params.compressionParams.decimationHysteresis = val;
1245 else
1246 retval = -EINVAL;
1247 }
1248 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1249 } else if (MATCH("fr_diff_step_thresh")) {
1250 if (!retval)
1251 val = VALUE;
1252
1253 if (!retval) {
1254 if (val <= 0xff)
1255 new_params.compressionParams.frDiffStepThresh = val;
1256 else
1257 retval = -EINVAL;
1258 }
1259 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1260 } else if (MATCH("q_diff_step_thresh")) {
1261 if (!retval)
1262 val = VALUE;
1263
1264 if (!retval) {
1265 if (val <= 0xff)
1266 new_params.compressionParams.qDiffStepThresh = val;
1267 else
1268 retval = -EINVAL;
1269 }
1270 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1271 } else if (MATCH("decimation_thresh_mod")) {
1272 if (!retval)
1273 val = VALUE;
1274
1275 if (!retval) {
1276 if (val <= 0xff)
1277 new_params.compressionParams.decimationThreshMod = val;
1278 else
1279 retval = -EINVAL;
1280 }
1281 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1282 } else if (MATCH("toplight")) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001283 if (!retval && MATCH("on"))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001284 new_params.qx3.toplight = 1;
1285 else if (!retval && MATCH("off"))
1286 new_params.qx3.toplight = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001287 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001288 retval = -EINVAL;
1289 command_flags |= COMMAND_SETLIGHTS;
1290 } else if (MATCH("bottomlight")) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001291 if (!retval && MATCH("on"))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001292 new_params.qx3.bottomlight = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001293 else if (!retval && MATCH("off"))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001294 new_params.qx3.bottomlight = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001295 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296 retval = -EINVAL;
1297 command_flags |= COMMAND_SETLIGHTS;
1298 } else {
1299 DBG("No match found\n");
1300 retval = -EINVAL;
1301 }
1302
1303 if (!retval) {
1304 while (count && isspace(*buffer) && *buffer != '\n') {
1305 --count;
1306 ++buffer;
1307 }
1308 if (count) {
1309 if (*buffer == '\0' && count != 1)
1310 retval = -EINVAL;
1311 else if (*buffer != '\n' && *buffer != ';' &&
1312 *buffer != '\0')
1313 retval = -EINVAL;
1314 else {
1315 --count;
1316 ++buffer;
1317 }
1318 }
1319 }
1320 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001321#undef MATCH
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322#undef VALUE
1323#undef FIRMWARE_VERSION
1324 if (!retval) {
1325 if (command_flags & COMMAND_SETCOLOURPARAMS) {
1326 /* Adjust cam->vp to reflect these changes */
1327 cam->vp.brightness =
1328 new_params.colourParams.brightness*65535/100;
1329 cam->vp.contrast =
1330 new_params.colourParams.contrast*65535/100;
1331 cam->vp.colour =
1332 new_params.colourParams.saturation*65535/100;
1333 }
1334 if((command_flags & COMMAND_SETEXPOSURE) &&
1335 new_params.exposure.expMode == 2)
1336 cam->exposure_status = EXPOSURE_NORMAL;
1337
1338 memcpy(&cam->params, &new_params, sizeof(struct cam_params));
1339 cam->mainsFreq = new_mains;
1340 cam->cmd_queue |= command_flags;
1341 retval = size;
1342 } else
1343 DBG("error: %d\n", retval);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001344
Ingo Molnar3593cab2006-02-07 06:49:14 -02001345 mutex_unlock(&cam->param_lock);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001346
Linus Torvalds1da177e2005-04-16 15:20:36 -07001347out:
1348 free_page((unsigned long)page);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001349 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350}
1351
1352static void create_proc_cpia_cam(struct cam_data *cam)
1353{
1354 char name[7];
1355 struct proc_dir_entry *ent;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001356
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357 if (!cpia_proc_root || !cam)
1358 return;
1359
1360 sprintf(name, "video%d", cam->vdev.minor);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001361
Linus Torvalds1da177e2005-04-16 15:20:36 -07001362 ent = create_proc_entry(name, S_IFREG|S_IRUGO|S_IWUSR, cpia_proc_root);
1363 if (!ent)
1364 return;
1365
1366 ent->data = cam;
1367 ent->read_proc = cpia_read_proc;
1368 ent->write_proc = cpia_write_proc;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001369 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370 size of the proc entry is 3736 bytes for the standard webcam;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001371 the extra features of the QX3 microscope add 189 bytes.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001372 (we have not yet probed the camera to see which type it is).
1373 */
1374 ent->size = 3736 + 189;
1375 cam->proc_entry = ent;
1376}
1377
1378static void destroy_proc_cpia_cam(struct cam_data *cam)
1379{
1380 char name[7];
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001381
Linus Torvalds1da177e2005-04-16 15:20:36 -07001382 if (!cam || !cam->proc_entry)
1383 return;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001384
Linus Torvalds1da177e2005-04-16 15:20:36 -07001385 sprintf(name, "video%d", cam->vdev.minor);
1386 remove_proc_entry(name, cpia_proc_root);
1387 cam->proc_entry = NULL;
1388}
1389
1390static void proc_cpia_create(void)
1391{
Al Viro66600222005-09-28 22:32:57 +01001392 cpia_proc_root = proc_mkdir("cpia", NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001393
1394 if (cpia_proc_root)
1395 cpia_proc_root->owner = THIS_MODULE;
1396 else
1397 LOG("Unable to initialise /proc/cpia\n");
1398}
1399
1400static void __exit proc_cpia_destroy(void)
1401{
1402 remove_proc_entry("cpia", NULL);
1403}
1404#endif /* CONFIG_PROC_FS */
1405
1406/* ----------------------- debug functions ---------------------- */
1407
1408#define printstatus(cam) \
1409 DBG("%02x %02x %02x %02x %02x %02x %02x %02x\n",\
1410 cam->params.status.systemState, cam->params.status.grabState, \
1411 cam->params.status.streamState, cam->params.status.fatalError, \
1412 cam->params.status.cmdError, cam->params.status.debugFlags, \
1413 cam->params.status.vpStatus, cam->params.status.errorCode);
1414
1415/* ----------------------- v4l helpers -------------------------- */
1416
1417/* supported frame palettes and depths */
1418static inline int valid_mode(u16 palette, u16 depth)
1419{
1420 if ((palette == VIDEO_PALETTE_YUV422 && depth == 16) ||
1421 (palette == VIDEO_PALETTE_YUYV && depth == 16))
1422 return 1;
1423
1424 if (colorspace_conv)
1425 return (palette == VIDEO_PALETTE_GREY && depth == 8) ||
1426 (palette == VIDEO_PALETTE_RGB555 && depth == 16) ||
1427 (palette == VIDEO_PALETTE_RGB565 && depth == 16) ||
1428 (palette == VIDEO_PALETTE_RGB24 && depth == 24) ||
1429 (palette == VIDEO_PALETTE_RGB32 && depth == 32) ||
1430 (palette == VIDEO_PALETTE_UYVY && depth == 16);
1431
1432 return 0;
1433}
1434
1435static int match_videosize( int width, int height )
1436{
1437 /* return the best match, where 'best' is as always
1438 * the largest that is not bigger than what is requested. */
1439 if (width>=352 && height>=288)
1440 return VIDEOSIZE_352_288; /* CIF */
1441
1442 if (width>=320 && height>=240)
1443 return VIDEOSIZE_320_240; /* SIF */
1444
1445 if (width>=288 && height>=216)
1446 return VIDEOSIZE_288_216;
1447
1448 if (width>=256 && height>=192)
1449 return VIDEOSIZE_256_192;
1450
1451 if (width>=224 && height>=168)
1452 return VIDEOSIZE_224_168;
1453
1454 if (width>=192 && height>=144)
1455 return VIDEOSIZE_192_144;
1456
1457 if (width>=176 && height>=144)
1458 return VIDEOSIZE_176_144; /* QCIF */
1459
1460 if (width>=160 && height>=120)
1461 return VIDEOSIZE_160_120; /* QSIF */
1462
1463 if (width>=128 && height>=96)
1464 return VIDEOSIZE_128_96;
1465
1466 if (width>=88 && height>=72)
1467 return VIDEOSIZE_88_72;
1468
1469 if (width>=64 && height>=48)
1470 return VIDEOSIZE_64_48;
1471
1472 if (width>=48 && height>=48)
1473 return VIDEOSIZE_48_48;
1474
1475 return -1;
1476}
1477
1478/* these are the capture sizes we support */
1479static void set_vw_size(struct cam_data *cam)
1480{
1481 /* the col/row/start/end values are the result of simple math */
1482 /* study the SetROI-command in cpia developers guide p 2-22 */
1483 /* streamStartLine is set to the recommended value in the cpia */
1484 /* developers guide p 3-37 */
1485 switch(cam->video_size) {
1486 case VIDEOSIZE_CIF:
1487 cam->vw.width = 352;
1488 cam->vw.height = 288;
1489 cam->params.format.videoSize=VIDEOSIZE_CIF;
1490 cam->params.roi.colStart=0;
1491 cam->params.roi.rowStart=0;
1492 cam->params.streamStartLine = 120;
1493 break;
1494 case VIDEOSIZE_SIF:
1495 cam->vw.width = 320;
1496 cam->vw.height = 240;
1497 cam->params.format.videoSize=VIDEOSIZE_CIF;
1498 cam->params.roi.colStart=2;
1499 cam->params.roi.rowStart=6;
1500 cam->params.streamStartLine = 120;
1501 break;
1502 case VIDEOSIZE_288_216:
1503 cam->vw.width = 288;
1504 cam->vw.height = 216;
1505 cam->params.format.videoSize=VIDEOSIZE_CIF;
1506 cam->params.roi.colStart=4;
1507 cam->params.roi.rowStart=9;
1508 cam->params.streamStartLine = 120;
1509 break;
1510 case VIDEOSIZE_256_192:
1511 cam->vw.width = 256;
1512 cam->vw.height = 192;
1513 cam->params.format.videoSize=VIDEOSIZE_CIF;
1514 cam->params.roi.colStart=6;
1515 cam->params.roi.rowStart=12;
1516 cam->params.streamStartLine = 120;
1517 break;
1518 case VIDEOSIZE_224_168:
1519 cam->vw.width = 224;
1520 cam->vw.height = 168;
1521 cam->params.format.videoSize=VIDEOSIZE_CIF;
1522 cam->params.roi.colStart=8;
1523 cam->params.roi.rowStart=15;
1524 cam->params.streamStartLine = 120;
1525 break;
1526 case VIDEOSIZE_192_144:
1527 cam->vw.width = 192;
1528 cam->vw.height = 144;
1529 cam->params.format.videoSize=VIDEOSIZE_CIF;
1530 cam->params.roi.colStart=10;
1531 cam->params.roi.rowStart=18;
1532 cam->params.streamStartLine = 120;
1533 break;
1534 case VIDEOSIZE_QCIF:
1535 cam->vw.width = 176;
1536 cam->vw.height = 144;
1537 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1538 cam->params.roi.colStart=0;
1539 cam->params.roi.rowStart=0;
1540 cam->params.streamStartLine = 60;
1541 break;
1542 case VIDEOSIZE_QSIF:
1543 cam->vw.width = 160;
1544 cam->vw.height = 120;
1545 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1546 cam->params.roi.colStart=1;
1547 cam->params.roi.rowStart=3;
1548 cam->params.streamStartLine = 60;
1549 break;
1550 case VIDEOSIZE_128_96:
1551 cam->vw.width = 128;
1552 cam->vw.height = 96;
1553 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1554 cam->params.roi.colStart=3;
1555 cam->params.roi.rowStart=6;
1556 cam->params.streamStartLine = 60;
1557 break;
1558 case VIDEOSIZE_88_72:
1559 cam->vw.width = 88;
1560 cam->vw.height = 72;
1561 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1562 cam->params.roi.colStart=5;
1563 cam->params.roi.rowStart=9;
1564 cam->params.streamStartLine = 60;
1565 break;
1566 case VIDEOSIZE_64_48:
1567 cam->vw.width = 64;
1568 cam->vw.height = 48;
1569 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1570 cam->params.roi.colStart=7;
1571 cam->params.roi.rowStart=12;
1572 cam->params.streamStartLine = 60;
1573 break;
1574 case VIDEOSIZE_48_48:
1575 cam->vw.width = 48;
1576 cam->vw.height = 48;
1577 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1578 cam->params.roi.colStart=8;
1579 cam->params.roi.rowStart=6;
1580 cam->params.streamStartLine = 60;
1581 break;
1582 default:
1583 LOG("bad videosize value: %d\n", cam->video_size);
1584 return;
1585 }
1586
1587 if(cam->vc.width == 0)
1588 cam->vc.width = cam->vw.width;
1589 if(cam->vc.height == 0)
1590 cam->vc.height = cam->vw.height;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001591
Linus Torvalds1da177e2005-04-16 15:20:36 -07001592 cam->params.roi.colStart += cam->vc.x >> 3;
1593 cam->params.roi.colEnd = cam->params.roi.colStart +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001594 (cam->vc.width >> 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001595 cam->params.roi.rowStart += cam->vc.y >> 2;
1596 cam->params.roi.rowEnd = cam->params.roi.rowStart +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001597 (cam->vc.height >> 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001598
1599 return;
1600}
1601
1602static int allocate_frame_buf(struct cam_data *cam)
1603{
1604 int i;
1605
1606 cam->frame_buf = rvmalloc(FRAME_NUM * CPIA_MAX_FRAME_SIZE);
1607 if (!cam->frame_buf)
1608 return -ENOBUFS;
1609
1610 for (i = 0; i < FRAME_NUM; i++)
1611 cam->frame[i].data = cam->frame_buf + i * CPIA_MAX_FRAME_SIZE;
1612
1613 return 0;
1614}
1615
1616static int free_frame_buf(struct cam_data *cam)
1617{
1618 int i;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001619
Linus Torvalds1da177e2005-04-16 15:20:36 -07001620 rvfree(cam->frame_buf, FRAME_NUM*CPIA_MAX_FRAME_SIZE);
1621 cam->frame_buf = NULL;
1622 for (i=0; i < FRAME_NUM; i++)
1623 cam->frame[i].data = NULL;
1624
1625 return 0;
1626}
1627
1628
1629static inline void free_frames(struct cpia_frame frame[FRAME_NUM])
1630{
1631 int i;
1632
1633 for (i=0; i < FRAME_NUM; i++)
1634 frame[i].state = FRAME_UNUSED;
1635 return;
1636}
1637
1638/**********************************************************************
1639 *
1640 * General functions
1641 *
1642 **********************************************************************/
1643/* send an arbitrary command to the camera */
1644static int do_command(struct cam_data *cam, u16 command, u8 a, u8 b, u8 c, u8 d)
1645{
1646 int retval, datasize;
1647 u8 cmd[8], data[8];
1648
1649 switch(command) {
1650 case CPIA_COMMAND_GetCPIAVersion:
1651 case CPIA_COMMAND_GetPnPID:
1652 case CPIA_COMMAND_GetCameraStatus:
1653 case CPIA_COMMAND_GetVPVersion:
1654 datasize=8;
1655 break;
1656 case CPIA_COMMAND_GetColourParams:
1657 case CPIA_COMMAND_GetColourBalance:
1658 case CPIA_COMMAND_GetExposure:
Ingo Molnar3593cab2006-02-07 06:49:14 -02001659 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001660 datasize=8;
1661 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001662 case CPIA_COMMAND_ReadMCPorts:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001663 case CPIA_COMMAND_ReadVCRegs:
1664 datasize = 4;
1665 break;
1666 default:
1667 datasize=0;
1668 break;
1669 }
1670
1671 cmd[0] = command>>8;
1672 cmd[1] = command&0xff;
1673 cmd[2] = a;
1674 cmd[3] = b;
1675 cmd[4] = c;
1676 cmd[5] = d;
1677 cmd[6] = datasize;
1678 cmd[7] = 0;
1679
1680 retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
1681 if (retval) {
1682 DBG("%x - failed, retval=%d\n", command, retval);
1683 if (command == CPIA_COMMAND_GetColourParams ||
1684 command == CPIA_COMMAND_GetColourBalance ||
1685 command == CPIA_COMMAND_GetExposure)
Ingo Molnar3593cab2006-02-07 06:49:14 -02001686 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001687 } else {
1688 switch(command) {
1689 case CPIA_COMMAND_GetCPIAVersion:
1690 cam->params.version.firmwareVersion = data[0];
1691 cam->params.version.firmwareRevision = data[1];
1692 cam->params.version.vcVersion = data[2];
1693 cam->params.version.vcRevision = data[3];
1694 break;
1695 case CPIA_COMMAND_GetPnPID:
1696 cam->params.pnpID.vendor = data[0]+(((u16)data[1])<<8);
1697 cam->params.pnpID.product = data[2]+(((u16)data[3])<<8);
1698 cam->params.pnpID.deviceRevision =
1699 data[4]+(((u16)data[5])<<8);
1700 break;
1701 case CPIA_COMMAND_GetCameraStatus:
1702 cam->params.status.systemState = data[0];
1703 cam->params.status.grabState = data[1];
1704 cam->params.status.streamState = data[2];
1705 cam->params.status.fatalError = data[3];
1706 cam->params.status.cmdError = data[4];
1707 cam->params.status.debugFlags = data[5];
1708 cam->params.status.vpStatus = data[6];
1709 cam->params.status.errorCode = data[7];
1710 break;
1711 case CPIA_COMMAND_GetVPVersion:
1712 cam->params.vpVersion.vpVersion = data[0];
1713 cam->params.vpVersion.vpRevision = data[1];
1714 cam->params.vpVersion.cameraHeadID =
1715 data[2]+(((u16)data[3])<<8);
1716 break;
1717 case CPIA_COMMAND_GetColourParams:
1718 cam->params.colourParams.brightness = data[0];
1719 cam->params.colourParams.contrast = data[1];
1720 cam->params.colourParams.saturation = data[2];
Ingo Molnar3593cab2006-02-07 06:49:14 -02001721 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001722 break;
1723 case CPIA_COMMAND_GetColourBalance:
1724 cam->params.colourBalance.redGain = data[0];
1725 cam->params.colourBalance.greenGain = data[1];
1726 cam->params.colourBalance.blueGain = data[2];
Ingo Molnar3593cab2006-02-07 06:49:14 -02001727 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001728 break;
1729 case CPIA_COMMAND_GetExposure:
1730 cam->params.exposure.gain = data[0];
1731 cam->params.exposure.fineExp = data[1];
1732 cam->params.exposure.coarseExpLo = data[2];
1733 cam->params.exposure.coarseExpHi = data[3];
1734 cam->params.exposure.redComp = data[4];
1735 cam->params.exposure.green1Comp = data[5];
1736 cam->params.exposure.green2Comp = data[6];
1737 cam->params.exposure.blueComp = data[7];
Ingo Molnar3593cab2006-02-07 06:49:14 -02001738 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001739 break;
1740
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001741 case CPIA_COMMAND_ReadMCPorts:
1742 if (!cam->params.qx3.qx3_detected)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001743 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001744 /* test button press */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001745 cam->params.qx3.button = ((data[1] & 0x02) == 0);
1746 if (cam->params.qx3.button) {
1747 /* button pressed - unlock the latch */
1748 do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xDF,0xDF,0);
1749 do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xFF,0xFF,0);
1750 }
1751
1752 /* test whether microscope is cradled */
1753 cam->params.qx3.cradled = ((data[2] & 0x40) == 0);
1754 break;
1755
1756 default:
1757 break;
1758 }
1759 }
1760 return retval;
1761}
1762
1763/* send a command to the camera with an additional data transaction */
1764static int do_command_extended(struct cam_data *cam, u16 command,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001765 u8 a, u8 b, u8 c, u8 d,
1766 u8 e, u8 f, u8 g, u8 h,
1767 u8 i, u8 j, u8 k, u8 l)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001768{
1769 int retval;
1770 u8 cmd[8], data[8];
1771
1772 cmd[0] = command>>8;
1773 cmd[1] = command&0xff;
1774 cmd[2] = a;
1775 cmd[3] = b;
1776 cmd[4] = c;
1777 cmd[5] = d;
1778 cmd[6] = 8;
1779 cmd[7] = 0;
1780 data[0] = e;
1781 data[1] = f;
1782 data[2] = g;
1783 data[3] = h;
1784 data[4] = i;
1785 data[5] = j;
1786 data[6] = k;
1787 data[7] = l;
1788
1789 retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
1790 if (retval)
1791 DBG("%x - failed\n", command);
1792
1793 return retval;
1794}
1795
1796/**********************************************************************
1797 *
1798 * Colorspace conversion
1799 *
1800 **********************************************************************/
1801#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16)
1802
1803static int convert420(unsigned char *yuv, unsigned char *rgb, int out_fmt,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001804 int linesize, int mmap_kludge)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001805{
1806 int y, u, v, r, g, b, y1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001807
Linus Torvalds1da177e2005-04-16 15:20:36 -07001808 /* Odd lines use the same u and v as the previous line.
1809 * Because of compression, it is necessary to get this
1810 * information from the decoded image. */
1811 switch(out_fmt) {
1812 case VIDEO_PALETTE_RGB555:
1813 y = (*yuv++ - 16) * 76310;
1814 y1 = (*yuv - 16) * 76310;
1815 r = ((*(rgb+1-linesize)) & 0x7c) << 1;
1816 g = ((*(rgb-linesize)) & 0xe0) >> 4 |
1817 ((*(rgb+1-linesize)) & 0x03) << 6;
1818 b = ((*(rgb-linesize)) & 0x1f) << 3;
1819 u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
1820 v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
1821 r = 104635 * v;
1822 g = -25690 * u - 53294 * v;
1823 b = 132278 * u;
1824 *rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3);
1825 *rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6);
1826 *rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3);
1827 *rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6);
1828 return 4;
1829 case VIDEO_PALETTE_RGB565:
1830 y = (*yuv++ - 16) * 76310;
1831 y1 = (*yuv - 16) * 76310;
1832 r = (*(rgb+1-linesize)) & 0xf8;
1833 g = ((*(rgb-linesize)) & 0xe0) >> 3 |
1834 ((*(rgb+1-linesize)) & 0x07) << 5;
1835 b = ((*(rgb-linesize)) & 0x1f) << 3;
1836 u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
1837 v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
1838 r = 104635 * v;
1839 g = -25690 * u - 53294 * v;
1840 b = 132278 * u;
1841 *rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3);
1842 *rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5);
1843 *rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3);
1844 *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5);
1845 return 4;
1846 break;
1847 case VIDEO_PALETTE_RGB24:
1848 case VIDEO_PALETTE_RGB32:
1849 y = (*yuv++ - 16) * 76310;
1850 y1 = (*yuv - 16) * 76310;
1851 if (mmap_kludge) {
1852 r = *(rgb+2-linesize);
1853 g = *(rgb+1-linesize);
1854 b = *(rgb-linesize);
1855 } else {
1856 r = *(rgb-linesize);
1857 g = *(rgb+1-linesize);
1858 b = *(rgb+2-linesize);
1859 }
1860 u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
1861 v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
1862 r = 104635 * v;
1863 g = -25690 * u + -53294 * v;
1864 b = 132278 * u;
1865 if (mmap_kludge) {
1866 *rgb++ = LIMIT(b+y);
1867 *rgb++ = LIMIT(g+y);
1868 *rgb++ = LIMIT(r+y);
1869 if(out_fmt == VIDEO_PALETTE_RGB32)
1870 rgb++;
1871 *rgb++ = LIMIT(b+y1);
1872 *rgb++ = LIMIT(g+y1);
1873 *rgb = LIMIT(r+y1);
1874 } else {
1875 *rgb++ = LIMIT(r+y);
1876 *rgb++ = LIMIT(g+y);
1877 *rgb++ = LIMIT(b+y);
1878 if(out_fmt == VIDEO_PALETTE_RGB32)
1879 rgb++;
1880 *rgb++ = LIMIT(r+y1);
1881 *rgb++ = LIMIT(g+y1);
1882 *rgb = LIMIT(b+y1);
1883 }
1884 if(out_fmt == VIDEO_PALETTE_RGB32)
1885 return 8;
1886 return 6;
1887 case VIDEO_PALETTE_YUV422:
1888 case VIDEO_PALETTE_YUYV:
1889 y = *yuv++;
1890 u = *(rgb+1-linesize);
1891 y1 = *yuv;
1892 v = *(rgb+3-linesize);
1893 *rgb++ = y;
1894 *rgb++ = u;
1895 *rgb++ = y1;
1896 *rgb = v;
1897 return 4;
1898 case VIDEO_PALETTE_UYVY:
1899 u = *(rgb-linesize);
1900 y = *yuv++;
1901 v = *(rgb+2-linesize);
1902 y1 = *yuv;
1903 *rgb++ = u;
1904 *rgb++ = y;
1905 *rgb++ = v;
1906 *rgb = y1;
1907 return 4;
1908 case VIDEO_PALETTE_GREY:
1909 *rgb++ = *yuv++;
1910 *rgb = *yuv;
1911 return 2;
1912 default:
1913 DBG("Empty: %d\n", out_fmt);
1914 return 0;
1915 }
1916}
1917
1918
1919static int yuvconvert(unsigned char *yuv, unsigned char *rgb, int out_fmt,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001920 int in_uyvy, int mmap_kludge)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001921{
1922 int y, u, v, r, g, b, y1;
1923
1924 switch(out_fmt) {
1925 case VIDEO_PALETTE_RGB555:
1926 case VIDEO_PALETTE_RGB565:
1927 case VIDEO_PALETTE_RGB24:
1928 case VIDEO_PALETTE_RGB32:
1929 if (in_uyvy) {
1930 u = *yuv++ - 128;
1931 y = (*yuv++ - 16) * 76310;
1932 v = *yuv++ - 128;
1933 y1 = (*yuv - 16) * 76310;
1934 } else {
1935 y = (*yuv++ - 16) * 76310;
1936 u = *yuv++ - 128;
1937 y1 = (*yuv++ - 16) * 76310;
1938 v = *yuv - 128;
1939 }
1940 r = 104635 * v;
1941 g = -25690 * u + -53294 * v;
1942 b = 132278 * u;
1943 break;
1944 default:
1945 y = *yuv++;
1946 u = *yuv++;
1947 y1 = *yuv++;
1948 v = *yuv;
1949 /* Just to avoid compiler warnings */
1950 r = 0;
1951 g = 0;
1952 b = 0;
1953 break;
1954 }
1955 switch(out_fmt) {
1956 case VIDEO_PALETTE_RGB555:
1957 *rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3);
1958 *rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6);
1959 *rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3);
1960 *rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6);
1961 return 4;
1962 case VIDEO_PALETTE_RGB565:
1963 *rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3);
1964 *rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5);
1965 *rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3);
1966 *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5);
1967 return 4;
1968 case VIDEO_PALETTE_RGB24:
1969 if (mmap_kludge) {
1970 *rgb++ = LIMIT(b+y);
1971 *rgb++ = LIMIT(g+y);
1972 *rgb++ = LIMIT(r+y);
1973 *rgb++ = LIMIT(b+y1);
1974 *rgb++ = LIMIT(g+y1);
1975 *rgb = LIMIT(r+y1);
1976 } else {
1977 *rgb++ = LIMIT(r+y);
1978 *rgb++ = LIMIT(g+y);
1979 *rgb++ = LIMIT(b+y);
1980 *rgb++ = LIMIT(r+y1);
1981 *rgb++ = LIMIT(g+y1);
1982 *rgb = LIMIT(b+y1);
1983 }
1984 return 6;
1985 case VIDEO_PALETTE_RGB32:
1986 if (mmap_kludge) {
1987 *rgb++ = LIMIT(b+y);
1988 *rgb++ = LIMIT(g+y);
1989 *rgb++ = LIMIT(r+y);
1990 rgb++;
1991 *rgb++ = LIMIT(b+y1);
1992 *rgb++ = LIMIT(g+y1);
1993 *rgb = LIMIT(r+y1);
1994 } else {
1995 *rgb++ = LIMIT(r+y);
1996 *rgb++ = LIMIT(g+y);
1997 *rgb++ = LIMIT(b+y);
1998 rgb++;
1999 *rgb++ = LIMIT(r+y1);
2000 *rgb++ = LIMIT(g+y1);
2001 *rgb = LIMIT(b+y1);
2002 }
2003 return 8;
2004 case VIDEO_PALETTE_GREY:
2005 *rgb++ = y;
2006 *rgb = y1;
2007 return 2;
2008 case VIDEO_PALETTE_YUV422:
2009 case VIDEO_PALETTE_YUYV:
2010 *rgb++ = y;
2011 *rgb++ = u;
2012 *rgb++ = y1;
2013 *rgb = v;
2014 return 4;
2015 case VIDEO_PALETTE_UYVY:
2016 *rgb++ = u;
2017 *rgb++ = y;
2018 *rgb++ = v;
2019 *rgb = y1;
2020 return 4;
2021 default:
2022 DBG("Empty: %d\n", out_fmt);
2023 return 0;
2024 }
2025}
2026
2027static int skipcount(int count, int fmt)
2028{
2029 switch(fmt) {
2030 case VIDEO_PALETTE_GREY:
2031 return count;
2032 case VIDEO_PALETTE_RGB555:
2033 case VIDEO_PALETTE_RGB565:
2034 case VIDEO_PALETTE_YUV422:
2035 case VIDEO_PALETTE_YUYV:
2036 case VIDEO_PALETTE_UYVY:
2037 return 2*count;
2038 case VIDEO_PALETTE_RGB24:
2039 return 3*count;
2040 case VIDEO_PALETTE_RGB32:
2041 return 4*count;
2042 default:
2043 return 0;
2044 }
2045}
2046
2047static int parse_picture(struct cam_data *cam, int size)
2048{
2049 u8 *obuf, *ibuf, *end_obuf;
2050 int ll, in_uyvy, compressed, decimation, even_line, origsize, out_fmt;
2051 int rows, cols, linesize, subsample_422;
2052
2053 /* make sure params don't change while we are decoding */
Ingo Molnar3593cab2006-02-07 06:49:14 -02002054 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002055
2056 obuf = cam->decompressed_frame.data;
2057 end_obuf = obuf+CPIA_MAX_FRAME_SIZE;
2058 ibuf = cam->raw_image;
2059 origsize = size;
2060 out_fmt = cam->vp.palette;
2061
2062 if ((ibuf[0] != MAGIC_0) || (ibuf[1] != MAGIC_1)) {
2063 LOG("header not found\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02002064 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002065 return -1;
2066 }
2067
2068 if ((ibuf[16] != VIDEOSIZE_QCIF) && (ibuf[16] != VIDEOSIZE_CIF)) {
2069 LOG("wrong video size\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02002070 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002071 return -1;
2072 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002073
Linus Torvalds1da177e2005-04-16 15:20:36 -07002074 if (ibuf[17] != SUBSAMPLE_420 && ibuf[17] != SUBSAMPLE_422) {
2075 LOG("illegal subtype %d\n",ibuf[17]);
Ingo Molnar3593cab2006-02-07 06:49:14 -02002076 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002077 return -1;
2078 }
2079 subsample_422 = ibuf[17] == SUBSAMPLE_422;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002080
Linus Torvalds1da177e2005-04-16 15:20:36 -07002081 if (ibuf[18] != YUVORDER_YUYV && ibuf[18] != YUVORDER_UYVY) {
2082 LOG("illegal yuvorder %d\n",ibuf[18]);
Ingo Molnar3593cab2006-02-07 06:49:14 -02002083 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002084 return -1;
2085 }
2086 in_uyvy = ibuf[18] == YUVORDER_UYVY;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002087
Linus Torvalds1da177e2005-04-16 15:20:36 -07002088 if ((ibuf[24] != cam->params.roi.colStart) ||
2089 (ibuf[25] != cam->params.roi.colEnd) ||
2090 (ibuf[26] != cam->params.roi.rowStart) ||
2091 (ibuf[27] != cam->params.roi.rowEnd)) {
2092 LOG("ROI mismatch\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02002093 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002094 return -1;
2095 }
2096 cols = 8*(ibuf[25] - ibuf[24]);
2097 rows = 4*(ibuf[27] - ibuf[26]);
2098
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002099
Linus Torvalds1da177e2005-04-16 15:20:36 -07002100 if ((ibuf[28] != NOT_COMPRESSED) && (ibuf[28] != COMPRESSED)) {
2101 LOG("illegal compression %d\n",ibuf[28]);
Ingo Molnar3593cab2006-02-07 06:49:14 -02002102 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002103 return -1;
2104 }
2105 compressed = (ibuf[28] == COMPRESSED);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002106
Linus Torvalds1da177e2005-04-16 15:20:36 -07002107 if (ibuf[29] != NO_DECIMATION && ibuf[29] != DECIMATION_ENAB) {
2108 LOG("illegal decimation %d\n",ibuf[29]);
Ingo Molnar3593cab2006-02-07 06:49:14 -02002109 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002110 return -1;
2111 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002112 decimation = (ibuf[29] == DECIMATION_ENAB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002113
2114 cam->params.yuvThreshold.yThreshold = ibuf[30];
2115 cam->params.yuvThreshold.uvThreshold = ibuf[31];
2116 cam->params.status.systemState = ibuf[32];
2117 cam->params.status.grabState = ibuf[33];
2118 cam->params.status.streamState = ibuf[34];
2119 cam->params.status.fatalError = ibuf[35];
2120 cam->params.status.cmdError = ibuf[36];
2121 cam->params.status.debugFlags = ibuf[37];
2122 cam->params.status.vpStatus = ibuf[38];
2123 cam->params.status.errorCode = ibuf[39];
2124 cam->fps = ibuf[41];
Ingo Molnar3593cab2006-02-07 06:49:14 -02002125 mutex_unlock(&cam->param_lock);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002126
Linus Torvalds1da177e2005-04-16 15:20:36 -07002127 linesize = skipcount(cols, out_fmt);
2128 ibuf += FRAME_HEADER_SIZE;
2129 size -= FRAME_HEADER_SIZE;
2130 ll = ibuf[0] | (ibuf[1] << 8);
2131 ibuf += 2;
2132 even_line = 1;
2133
2134 while (size > 0) {
2135 size -= (ll+2);
2136 if (size < 0) {
2137 LOG("Insufficient data in buffer\n");
2138 return -1;
2139 }
2140
2141 while (ll > 1) {
2142 if (!compressed || (compressed && !(*ibuf & 1))) {
2143 if(subsample_422 || even_line) {
2144 obuf += yuvconvert(ibuf, obuf, out_fmt,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002145 in_uyvy, cam->mmap_kludge);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002146 ibuf += 4;
2147 ll -= 4;
2148 } else {
2149 /* SUBSAMPLE_420 on an odd line */
2150 obuf += convert420(ibuf, obuf,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002151 out_fmt, linesize,
2152 cam->mmap_kludge);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002153 ibuf += 2;
2154 ll -= 2;
2155 }
2156 } else {
2157 /*skip compressed interval from previous frame*/
2158 obuf += skipcount(*ibuf >> 1, out_fmt);
2159 if (obuf > end_obuf) {
2160 LOG("Insufficient buffer size\n");
2161 return -1;
2162 }
2163 ++ibuf;
2164 ll--;
2165 }
2166 }
2167 if (ll == 1) {
2168 if (*ibuf != EOL) {
2169 DBG("EOL not found giving up after %d/%d"
2170 " bytes\n", origsize-size, origsize);
2171 return -1;
2172 }
2173
2174 ++ibuf; /* skip over EOL */
2175
2176 if ((size > 3) && (ibuf[0] == EOI) && (ibuf[1] == EOI) &&
2177 (ibuf[2] == EOI) && (ibuf[3] == EOI)) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002178 size -= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002179 break;
2180 }
2181
2182 if(decimation) {
2183 /* skip the odd lines for now */
2184 obuf += linesize;
2185 }
2186
2187 if (size > 1) {
2188 ll = ibuf[0] | (ibuf[1] << 8);
2189 ibuf += 2; /* skip over line length */
2190 }
2191 if(!decimation)
2192 even_line = !even_line;
2193 } else {
2194 LOG("line length was not 1 but %d after %d/%d bytes\n",
2195 ll, origsize-size, origsize);
2196 return -1;
2197 }
2198 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002199
Linus Torvalds1da177e2005-04-16 15:20:36 -07002200 if(decimation) {
2201 /* interpolate odd rows */
2202 int i, j;
2203 u8 *prev, *next;
2204 prev = cam->decompressed_frame.data;
2205 obuf = prev+linesize;
2206 next = obuf+linesize;
2207 for(i=1; i<rows-1; i+=2) {
2208 for(j=0; j<linesize; ++j) {
2209 *obuf++ = ((int)*prev++ + *next++) / 2;
2210 }
2211 prev += linesize;
2212 obuf += linesize;
2213 next += linesize;
2214 }
2215 /* last row is odd, just copy previous row */
2216 memcpy(obuf, prev, linesize);
2217 }
2218
2219 cam->decompressed_frame.count = obuf-cam->decompressed_frame.data;
2220
2221 return cam->decompressed_frame.count;
2222}
2223
2224/* InitStreamCap wrapper to select correct start line */
2225static inline int init_stream_cap(struct cam_data *cam)
2226{
2227 return do_command(cam, CPIA_COMMAND_InitStreamCap,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002228 0, cam->params.streamStartLine, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002229}
2230
2231
2232/* find_over_exposure
2233 * Finds a suitable value of OverExposure for use with SetFlickerCtrl
2234 * Some calculation is required because this value changes with the brightness
2235 * set with SetColourParameters
2236 *
2237 * Parameters: Brightness - last brightness value set with SetColourParameters
2238 *
2239 * Returns: OverExposure value to use with SetFlickerCtrl
2240 */
2241#define FLICKER_MAX_EXPOSURE 250
2242#define FLICKER_ALLOWABLE_OVER_EXPOSURE 146
2243#define FLICKER_BRIGHTNESS_CONSTANT 59
2244static int find_over_exposure(int brightness)
2245{
2246 int MaxAllowableOverExposure, OverExposure;
2247
2248 MaxAllowableOverExposure = FLICKER_MAX_EXPOSURE - brightness -
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002249 FLICKER_BRIGHTNESS_CONSTANT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002250
2251 if (MaxAllowableOverExposure < FLICKER_ALLOWABLE_OVER_EXPOSURE) {
2252 OverExposure = MaxAllowableOverExposure;
2253 } else {
2254 OverExposure = FLICKER_ALLOWABLE_OVER_EXPOSURE;
2255 }
2256
2257 return OverExposure;
2258}
2259#undef FLICKER_MAX_EXPOSURE
2260#undef FLICKER_ALLOWABLE_OVER_EXPOSURE
2261#undef FLICKER_BRIGHTNESS_CONSTANT
2262
2263/* update various camera modes and settings */
2264static void dispatch_commands(struct cam_data *cam)
2265{
Ingo Molnar3593cab2006-02-07 06:49:14 -02002266 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002267 if (cam->cmd_queue==COMMAND_NONE) {
Ingo Molnar3593cab2006-02-07 06:49:14 -02002268 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002269 return;
2270 }
2271 DEB_BYTE(cam->cmd_queue);
2272 DEB_BYTE(cam->cmd_queue>>8);
2273 if (cam->cmd_queue & COMMAND_SETFORMAT) {
2274 do_command(cam, CPIA_COMMAND_SetFormat,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002275 cam->params.format.videoSize,
2276 cam->params.format.subSample,
2277 cam->params.format.yuvOrder, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002278 do_command(cam, CPIA_COMMAND_SetROI,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002279 cam->params.roi.colStart, cam->params.roi.colEnd,
2280 cam->params.roi.rowStart, cam->params.roi.rowEnd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002281 cam->first_frame = 1;
2282 }
2283
2284 if (cam->cmd_queue & COMMAND_SETCOLOURPARAMS)
2285 do_command(cam, CPIA_COMMAND_SetColourParams,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002286 cam->params.colourParams.brightness,
2287 cam->params.colourParams.contrast,
2288 cam->params.colourParams.saturation, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002289
2290 if (cam->cmd_queue & COMMAND_SETAPCOR)
2291 do_command(cam, CPIA_COMMAND_SetApcor,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002292 cam->params.apcor.gain1,
2293 cam->params.apcor.gain2,
2294 cam->params.apcor.gain4,
2295 cam->params.apcor.gain8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002296
2297 if (cam->cmd_queue & COMMAND_SETVLOFFSET)
2298 do_command(cam, CPIA_COMMAND_SetVLOffset,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002299 cam->params.vlOffset.gain1,
2300 cam->params.vlOffset.gain2,
2301 cam->params.vlOffset.gain4,
2302 cam->params.vlOffset.gain8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002303
2304 if (cam->cmd_queue & COMMAND_SETEXPOSURE) {
2305 do_command_extended(cam, CPIA_COMMAND_SetExposure,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002306 cam->params.exposure.gainMode,
2307 1,
2308 cam->params.exposure.compMode,
2309 cam->params.exposure.centreWeight,
2310 cam->params.exposure.gain,
2311 cam->params.exposure.fineExp,
2312 cam->params.exposure.coarseExpLo,
2313 cam->params.exposure.coarseExpHi,
2314 cam->params.exposure.redComp,
2315 cam->params.exposure.green1Comp,
2316 cam->params.exposure.green2Comp,
2317 cam->params.exposure.blueComp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002318 if(cam->params.exposure.expMode != 1) {
2319 do_command_extended(cam, CPIA_COMMAND_SetExposure,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002320 0,
2321 cam->params.exposure.expMode,
2322 0, 0,
2323 cam->params.exposure.gain,
2324 cam->params.exposure.fineExp,
2325 cam->params.exposure.coarseExpLo,
2326 cam->params.exposure.coarseExpHi,
2327 0, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002328 }
2329 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002330
Linus Torvalds1da177e2005-04-16 15:20:36 -07002331 if (cam->cmd_queue & COMMAND_SETCOLOURBALANCE) {
2332 if (cam->params.colourBalance.balanceMode == 1) {
2333 do_command(cam, CPIA_COMMAND_SetColourBalance,
2334 1,
2335 cam->params.colourBalance.redGain,
2336 cam->params.colourBalance.greenGain,
2337 cam->params.colourBalance.blueGain);
2338 do_command(cam, CPIA_COMMAND_SetColourBalance,
2339 3, 0, 0, 0);
2340 }
2341 if (cam->params.colourBalance.balanceMode == 2) {
2342 do_command(cam, CPIA_COMMAND_SetColourBalance,
2343 2, 0, 0, 0);
2344 }
2345 if (cam->params.colourBalance.balanceMode == 3) {
2346 do_command(cam, CPIA_COMMAND_SetColourBalance,
2347 3, 0, 0, 0);
2348 }
2349 }
2350
2351 if (cam->cmd_queue & COMMAND_SETCOMPRESSIONTARGET)
2352 do_command(cam, CPIA_COMMAND_SetCompressionTarget,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002353 cam->params.compressionTarget.frTargeting,
2354 cam->params.compressionTarget.targetFR,
2355 cam->params.compressionTarget.targetQ, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002356
2357 if (cam->cmd_queue & COMMAND_SETYUVTHRESH)
2358 do_command(cam, CPIA_COMMAND_SetYUVThresh,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002359 cam->params.yuvThreshold.yThreshold,
2360 cam->params.yuvThreshold.uvThreshold, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002361
2362 if (cam->cmd_queue & COMMAND_SETCOMPRESSIONPARAMS)
2363 do_command_extended(cam, CPIA_COMMAND_SetCompressionParams,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002364 0, 0, 0, 0,
2365 cam->params.compressionParams.hysteresis,
2366 cam->params.compressionParams.threshMax,
2367 cam->params.compressionParams.smallStep,
2368 cam->params.compressionParams.largeStep,
2369 cam->params.compressionParams.decimationHysteresis,
2370 cam->params.compressionParams.frDiffStepThresh,
2371 cam->params.compressionParams.qDiffStepThresh,
2372 cam->params.compressionParams.decimationThreshMod);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002373
2374 if (cam->cmd_queue & COMMAND_SETCOMPRESSION)
2375 do_command(cam, CPIA_COMMAND_SetCompression,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002376 cam->params.compression.mode,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002377 cam->params.compression.decimation, 0, 0);
2378
2379 if (cam->cmd_queue & COMMAND_SETSENSORFPS)
2380 do_command(cam, CPIA_COMMAND_SetSensorFPS,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002381 cam->params.sensorFps.divisor,
2382 cam->params.sensorFps.baserate, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002383
2384 if (cam->cmd_queue & COMMAND_SETFLICKERCTRL)
2385 do_command(cam, CPIA_COMMAND_SetFlickerCtrl,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002386 cam->params.flickerControl.flickerMode,
2387 cam->params.flickerControl.coarseJump,
2388 abs(cam->params.flickerControl.allowableOverExposure),
2389 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002390
2391 if (cam->cmd_queue & COMMAND_SETECPTIMING)
2392 do_command(cam, CPIA_COMMAND_SetECPTiming,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002393 cam->params.ecpTiming, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002394
2395 if (cam->cmd_queue & COMMAND_PAUSE)
2396 do_command(cam, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0);
2397
2398 if (cam->cmd_queue & COMMAND_RESUME)
2399 init_stream_cap(cam);
2400
2401 if (cam->cmd_queue & COMMAND_SETLIGHTS && cam->params.qx3.qx3_detected)
2402 {
2403 int p1 = (cam->params.qx3.bottomlight == 0) << 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002404 int p2 = (cam->params.qx3.toplight == 0) << 3;
2405 do_command(cam, CPIA_COMMAND_WriteVCReg, 0x90, 0x8F, 0x50, 0);
2406 do_command(cam, CPIA_COMMAND_WriteMCPort, 2, 0, (p1|p2|0xE0), 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002407 }
2408
2409 cam->cmd_queue = COMMAND_NONE;
Ingo Molnar3593cab2006-02-07 06:49:14 -02002410 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002411 return;
2412}
2413
2414
2415
2416static void set_flicker(struct cam_params *params, volatile u32 *command_flags,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002417 int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002418{
2419 /* Everything in here is from the Windows driver */
2420#define FIRMWARE_VERSION(x,y) (params->version.firmwareVersion == (x) && \
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002421 params->version.firmwareRevision == (y))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002422/* define for compgain calculation */
2423#if 0
2424#define COMPGAIN(base, curexp, newexp) \
2425 (u8) ((((float) base - 128.0) * ((float) curexp / (float) newexp)) + 128.5)
2426#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
2427 (u16)((float)curexp * (float)(u8)(curcomp + 128) / (float)(u8)(basecomp - 128))
2428#else
2429 /* equivalent functions without floating point math */
2430#define COMPGAIN(base, curexp, newexp) \
2431 (u8)(128 + (((u32)(2*(base-128)*curexp + newexp)) / (2* newexp)) )
2432#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
2433 (u16)(((u32)(curexp * (u8)(curcomp + 128)) / (u8)(basecomp - 128)))
2434#endif
2435
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002436
Linus Torvalds1da177e2005-04-16 15:20:36 -07002437 int currentexp = params->exposure.coarseExpLo +
2438 params->exposure.coarseExpHi*256;
2439 int startexp;
2440 if (on) {
2441 int cj = params->flickerControl.coarseJump;
2442 params->flickerControl.flickerMode = 1;
2443 params->flickerControl.disabled = 0;
2444 if(params->exposure.expMode != 2)
2445 *command_flags |= COMMAND_SETEXPOSURE;
2446 params->exposure.expMode = 2;
2447 currentexp = currentexp << params->exposure.gain;
2448 params->exposure.gain = 0;
2449 /* round down current exposure to nearest value */
2450 startexp = (currentexp + ROUND_UP_EXP_FOR_FLICKER) / cj;
2451 if(startexp < 1)
2452 startexp = 1;
2453 startexp = (startexp * cj) - 1;
2454 if(FIRMWARE_VERSION(1,2))
2455 while(startexp > MAX_EXP_102)
2456 startexp -= cj;
2457 else
2458 while(startexp > MAX_EXP)
2459 startexp -= cj;
2460 params->exposure.coarseExpLo = startexp & 0xff;
2461 params->exposure.coarseExpHi = startexp >> 8;
2462 if (currentexp > startexp) {
2463 if (currentexp > (2 * startexp))
2464 currentexp = 2 * startexp;
2465 params->exposure.redComp = COMPGAIN (COMP_RED, currentexp, startexp);
2466 params->exposure.green1Comp = COMPGAIN (COMP_GREEN1, currentexp, startexp);
2467 params->exposure.green2Comp = COMPGAIN (COMP_GREEN2, currentexp, startexp);
2468 params->exposure.blueComp = COMPGAIN (COMP_BLUE, currentexp, startexp);
2469 } else {
2470 params->exposure.redComp = COMP_RED;
2471 params->exposure.green1Comp = COMP_GREEN1;
2472 params->exposure.green2Comp = COMP_GREEN2;
2473 params->exposure.blueComp = COMP_BLUE;
2474 }
2475 if(FIRMWARE_VERSION(1,2))
2476 params->exposure.compMode = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002477 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07002478 params->exposure.compMode = 1;
2479
2480 params->apcor.gain1 = 0x18;
2481 params->apcor.gain2 = 0x18;
2482 params->apcor.gain4 = 0x16;
2483 params->apcor.gain8 = 0x14;
2484 *command_flags |= COMMAND_SETAPCOR;
2485 } else {
2486 params->flickerControl.flickerMode = 0;
2487 params->flickerControl.disabled = 1;
2488 /* Coarse = average of equivalent coarse for each comp channel */
2489 startexp = EXP_FROM_COMP(COMP_RED, params->exposure.redComp, currentexp);
2490 startexp += EXP_FROM_COMP(COMP_GREEN1, params->exposure.green1Comp, currentexp);
2491 startexp += EXP_FROM_COMP(COMP_GREEN2, params->exposure.green2Comp, currentexp);
2492 startexp += EXP_FROM_COMP(COMP_BLUE, params->exposure.blueComp, currentexp);
2493 startexp = startexp >> 2;
2494 while(startexp > MAX_EXP &&
2495 params->exposure.gain < params->exposure.gainMode-1) {
2496 startexp = startexp >> 1;
2497 ++params->exposure.gain;
2498 }
2499 if(FIRMWARE_VERSION(1,2) && startexp > MAX_EXP_102)
2500 startexp = MAX_EXP_102;
2501 if(startexp > MAX_EXP)
2502 startexp = MAX_EXP;
2503 params->exposure.coarseExpLo = startexp&0xff;
2504 params->exposure.coarseExpHi = startexp >> 8;
2505 params->exposure.redComp = COMP_RED;
2506 params->exposure.green1Comp = COMP_GREEN1;
2507 params->exposure.green2Comp = COMP_GREEN2;
2508 params->exposure.blueComp = COMP_BLUE;
2509 params->exposure.compMode = 1;
2510 *command_flags |= COMMAND_SETEXPOSURE;
2511 params->apcor.gain1 = 0x18;
2512 params->apcor.gain2 = 0x16;
2513 params->apcor.gain4 = 0x24;
2514 params->apcor.gain8 = 0x34;
2515 *command_flags |= COMMAND_SETAPCOR;
2516 }
2517 params->vlOffset.gain1 = 20;
2518 params->vlOffset.gain2 = 24;
2519 params->vlOffset.gain4 = 26;
2520 params->vlOffset.gain8 = 26;
2521 *command_flags |= COMMAND_SETVLOFFSET;
2522#undef FIRMWARE_VERSION
2523#undef EXP_FROM_COMP
2524#undef COMPGAIN
2525}
2526
2527#define FIRMWARE_VERSION(x,y) (cam->params.version.firmwareVersion == (x) && \
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002528 cam->params.version.firmwareRevision == (y))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002529/* monitor the exposure and adjust the sensor frame rate if needed */
2530static void monitor_exposure(struct cam_data *cam)
2531{
2532 u8 exp_acc, bcomp, gain, coarseL, cmd[8], data[8];
2533 int retval, light_exp, dark_exp, very_dark_exp;
2534 int old_exposure, new_exposure, framerate;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002535
Linus Torvalds1da177e2005-04-16 15:20:36 -07002536 /* get necessary stats and register settings from camera */
2537 /* do_command can't handle this, so do it ourselves */
2538 cmd[0] = CPIA_COMMAND_ReadVPRegs>>8;
2539 cmd[1] = CPIA_COMMAND_ReadVPRegs&0xff;
2540 cmd[2] = 30;
2541 cmd[3] = 4;
2542 cmd[4] = 9;
2543 cmd[5] = 8;
2544 cmd[6] = 8;
2545 cmd[7] = 0;
2546 retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
2547 if (retval) {
2548 LOG("ReadVPRegs(30,4,9,8) - failed, retval=%d\n",
2549 retval);
2550 return;
2551 }
2552 exp_acc = data[0];
2553 bcomp = data[1];
2554 gain = data[2];
2555 coarseL = data[3];
2556
Ingo Molnar3593cab2006-02-07 06:49:14 -02002557 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002558 light_exp = cam->params.colourParams.brightness +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002559 TC - 50 + EXP_ACC_LIGHT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002560 if(light_exp > 255)
2561 light_exp = 255;
2562 dark_exp = cam->params.colourParams.brightness +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002563 TC - 50 - EXP_ACC_DARK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002564 if(dark_exp < 0)
2565 dark_exp = 0;
2566 very_dark_exp = dark_exp/2;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002567
Linus Torvalds1da177e2005-04-16 15:20:36 -07002568 old_exposure = cam->params.exposure.coarseExpHi * 256 +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002569 cam->params.exposure.coarseExpLo;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002570
2571 if(!cam->params.flickerControl.disabled) {
2572 /* Flicker control on */
2573 int max_comp = FIRMWARE_VERSION(1,2) ? MAX_COMP : HIGH_COMP_102;
2574 bcomp += 128; /* decode */
2575 if(bcomp >= max_comp && exp_acc < dark_exp) {
2576 /* dark */
2577 if(exp_acc < very_dark_exp) {
2578 /* very dark */
2579 if(cam->exposure_status == EXPOSURE_VERY_DARK)
2580 ++cam->exposure_count;
2581 else {
2582 cam->exposure_status = EXPOSURE_VERY_DARK;
2583 cam->exposure_count = 1;
2584 }
2585 } else {
2586 /* just dark */
2587 if(cam->exposure_status == EXPOSURE_DARK)
2588 ++cam->exposure_count;
2589 else {
2590 cam->exposure_status = EXPOSURE_DARK;
2591 cam->exposure_count = 1;
2592 }
2593 }
2594 } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) {
2595 /* light */
2596 if(old_exposure <= VERY_LOW_EXP) {
2597 /* very light */
2598 if(cam->exposure_status == EXPOSURE_VERY_LIGHT)
2599 ++cam->exposure_count;
2600 else {
2601 cam->exposure_status = EXPOSURE_VERY_LIGHT;
2602 cam->exposure_count = 1;
2603 }
2604 } else {
2605 /* just light */
2606 if(cam->exposure_status == EXPOSURE_LIGHT)
2607 ++cam->exposure_count;
2608 else {
2609 cam->exposure_status = EXPOSURE_LIGHT;
2610 cam->exposure_count = 1;
2611 }
2612 }
2613 } else {
2614 /* not dark or light */
2615 cam->exposure_status = EXPOSURE_NORMAL;
2616 }
2617 } else {
2618 /* Flicker control off */
2619 if(old_exposure >= MAX_EXP && exp_acc < dark_exp) {
2620 /* dark */
2621 if(exp_acc < very_dark_exp) {
2622 /* very dark */
2623 if(cam->exposure_status == EXPOSURE_VERY_DARK)
2624 ++cam->exposure_count;
2625 else {
2626 cam->exposure_status = EXPOSURE_VERY_DARK;
2627 cam->exposure_count = 1;
2628 }
2629 } else {
2630 /* just dark */
2631 if(cam->exposure_status == EXPOSURE_DARK)
2632 ++cam->exposure_count;
2633 else {
2634 cam->exposure_status = EXPOSURE_DARK;
2635 cam->exposure_count = 1;
2636 }
2637 }
2638 } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) {
2639 /* light */
2640 if(old_exposure <= VERY_LOW_EXP) {
2641 /* very light */
2642 if(cam->exposure_status == EXPOSURE_VERY_LIGHT)
2643 ++cam->exposure_count;
2644 else {
2645 cam->exposure_status = EXPOSURE_VERY_LIGHT;
2646 cam->exposure_count = 1;
2647 }
2648 } else {
2649 /* just light */
2650 if(cam->exposure_status == EXPOSURE_LIGHT)
2651 ++cam->exposure_count;
2652 else {
2653 cam->exposure_status = EXPOSURE_LIGHT;
2654 cam->exposure_count = 1;
2655 }
2656 }
2657 } else {
2658 /* not dark or light */
2659 cam->exposure_status = EXPOSURE_NORMAL;
2660 }
2661 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002662
Linus Torvalds1da177e2005-04-16 15:20:36 -07002663 framerate = cam->fps;
2664 if(framerate > 30 || framerate < 1)
2665 framerate = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002666
Linus Torvalds1da177e2005-04-16 15:20:36 -07002667 if(!cam->params.flickerControl.disabled) {
2668 /* Flicker control on */
2669 if((cam->exposure_status == EXPOSURE_VERY_DARK ||
2670 cam->exposure_status == EXPOSURE_DARK) &&
2671 cam->exposure_count >= DARK_TIME*framerate &&
2672 cam->params.sensorFps.divisor < 3) {
2673
2674 /* dark for too long */
2675 ++cam->params.sensorFps.divisor;
2676 cam->cmd_queue |= COMMAND_SETSENSORFPS;
2677
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002678 cam->params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07002679 flicker_jumps[cam->mainsFreq]
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002680 [cam->params.sensorFps.baserate]
2681 [cam->params.sensorFps.divisor];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002682 cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
2683
2684 new_exposure = cam->params.flickerControl.coarseJump-1;
2685 while(new_exposure < old_exposure/2)
2686 new_exposure += cam->params.flickerControl.coarseJump;
2687 cam->params.exposure.coarseExpLo = new_exposure & 0xff;
2688 cam->params.exposure.coarseExpHi = new_exposure >> 8;
2689 cam->cmd_queue |= COMMAND_SETEXPOSURE;
2690 cam->exposure_status = EXPOSURE_NORMAL;
2691 LOG("Automatically decreasing sensor_fps\n");
2692
2693 } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT ||
2694 cam->exposure_status == EXPOSURE_LIGHT) &&
2695 cam->exposure_count >= LIGHT_TIME*framerate &&
2696 cam->params.sensorFps.divisor > 0) {
2697
2698 /* light for too long */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002699 int max_exp = FIRMWARE_VERSION(1,2) ? MAX_EXP_102 : MAX_EXP ;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002700
2701 --cam->params.sensorFps.divisor;
2702 cam->cmd_queue |= COMMAND_SETSENSORFPS;
2703
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002704 cam->params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07002705 flicker_jumps[cam->mainsFreq]
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002706 [cam->params.sensorFps.baserate]
2707 [cam->params.sensorFps.divisor];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002708 cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
2709
2710 new_exposure = cam->params.flickerControl.coarseJump-1;
2711 while(new_exposure < 2*old_exposure &&
2712 new_exposure+
2713 cam->params.flickerControl.coarseJump < max_exp)
2714 new_exposure += cam->params.flickerControl.coarseJump;
2715 cam->params.exposure.coarseExpLo = new_exposure & 0xff;
2716 cam->params.exposure.coarseExpHi = new_exposure >> 8;
2717 cam->cmd_queue |= COMMAND_SETEXPOSURE;
2718 cam->exposure_status = EXPOSURE_NORMAL;
2719 LOG("Automatically increasing sensor_fps\n");
2720 }
2721 } else {
2722 /* Flicker control off */
2723 if((cam->exposure_status == EXPOSURE_VERY_DARK ||
2724 cam->exposure_status == EXPOSURE_DARK) &&
2725 cam->exposure_count >= DARK_TIME*framerate &&
2726 cam->params.sensorFps.divisor < 3) {
2727
2728 /* dark for too long */
2729 ++cam->params.sensorFps.divisor;
2730 cam->cmd_queue |= COMMAND_SETSENSORFPS;
2731
2732 if(cam->params.exposure.gain > 0) {
2733 --cam->params.exposure.gain;
2734 cam->cmd_queue |= COMMAND_SETEXPOSURE;
2735 }
2736 cam->exposure_status = EXPOSURE_NORMAL;
2737 LOG("Automatically decreasing sensor_fps\n");
2738
2739 } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT ||
2740 cam->exposure_status == EXPOSURE_LIGHT) &&
2741 cam->exposure_count >= LIGHT_TIME*framerate &&
2742 cam->params.sensorFps.divisor > 0) {
2743
2744 /* light for too long */
2745 --cam->params.sensorFps.divisor;
2746 cam->cmd_queue |= COMMAND_SETSENSORFPS;
2747
2748 if(cam->params.exposure.gain <
2749 cam->params.exposure.gainMode-1) {
2750 ++cam->params.exposure.gain;
2751 cam->cmd_queue |= COMMAND_SETEXPOSURE;
2752 }
2753 cam->exposure_status = EXPOSURE_NORMAL;
2754 LOG("Automatically increasing sensor_fps\n");
2755 }
2756 }
Ingo Molnar3593cab2006-02-07 06:49:14 -02002757 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002758}
2759
2760/*-----------------------------------------------------------------*/
2761/* if flicker is switched off, this function switches it back on.It checks,
2762 however, that conditions are suitable before restarting it.
2763 This should only be called for firmware version 1.2.
2764
2765 It also adjust the colour balance when an exposure step is detected - as
2766 long as flicker is running
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002767*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07002768static void restart_flicker(struct cam_data *cam)
2769{
2770 int cam_exposure, old_exp;
2771 if(!FIRMWARE_VERSION(1,2))
2772 return;
Ingo Molnar3593cab2006-02-07 06:49:14 -02002773 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002774 if(cam->params.flickerControl.flickerMode == 0 ||
2775 cam->raw_image[39] == 0) {
Ingo Molnar3593cab2006-02-07 06:49:14 -02002776 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002777 return;
2778 }
2779 cam_exposure = cam->raw_image[39]*2;
2780 old_exp = cam->params.exposure.coarseExpLo +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002781 cam->params.exposure.coarseExpHi*256;
2782 /*
2783 see how far away camera exposure is from a valid
2784 flicker exposure value
2785 */
2786 cam_exposure %= cam->params.flickerControl.coarseJump;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002787 if(!cam->params.flickerControl.disabled &&
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002788 cam_exposure <= cam->params.flickerControl.coarseJump - 3) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002789 /* Flicker control auto-disabled */
2790 cam->params.flickerControl.disabled = 1;
2791 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002792
Linus Torvalds1da177e2005-04-16 15:20:36 -07002793 if(cam->params.flickerControl.disabled &&
2794 cam->params.flickerControl.flickerMode &&
2795 old_exp > cam->params.flickerControl.coarseJump +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002796 ROUND_UP_EXP_FOR_FLICKER) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002797 /* exposure is now high enough to switch
2798 flicker control back on */
2799 set_flicker(&cam->params, &cam->cmd_queue, 1);
2800 if((cam->cmd_queue & COMMAND_SETEXPOSURE) &&
2801 cam->params.exposure.expMode == 2)
2802 cam->exposure_status = EXPOSURE_NORMAL;
2803
2804 }
Ingo Molnar3593cab2006-02-07 06:49:14 -02002805 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002806}
2807#undef FIRMWARE_VERSION
2808
2809static int clear_stall(struct cam_data *cam)
2810{
2811 /* FIXME: Does this actually work? */
2812 LOG("Clearing stall\n");
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002813
Linus Torvalds1da177e2005-04-16 15:20:36 -07002814 cam->ops->streamRead(cam->lowlevel_data, cam->raw_image, 0);
2815 do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0);
2816 return cam->params.status.streamState != STREAM_PAUSED;
2817}
2818
2819/* kernel thread function to read image from camera */
2820static int fetch_frame(void *data)
2821{
2822 int image_size, retry;
2823 struct cam_data *cam = (struct cam_data *)data;
2824 unsigned long oldjif, rate, diff;
2825
2826 /* Allow up to two bad images in a row to be read and
2827 * ignored before an error is reported */
2828 for (retry = 0; retry < 3; ++retry) {
2829 if (retry)
2830 DBG("retry=%d\n", retry);
2831
2832 if (!cam->ops)
2833 continue;
2834
2835 /* load first frame always uncompressed */
2836 if (cam->first_frame &&
2837 cam->params.compression.mode != CPIA_COMPRESSION_NONE) {
2838 do_command(cam, CPIA_COMMAND_SetCompression,
2839 CPIA_COMPRESSION_NONE,
2840 NO_DECIMATION, 0, 0);
2841 /* Trial & error - Discarding a frame prevents the
2842 first frame from having an error in the data. */
2843 do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0);
2844 }
2845
2846 /* init camera upload */
2847 if (do_command(cam, CPIA_COMMAND_GrabFrame, 0,
2848 cam->params.streamStartLine, 0, 0))
2849 continue;
2850
2851 if (cam->ops->wait_for_stream_ready) {
2852 /* loop until image ready */
2853 int count = 0;
2854 do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0);
2855 while (cam->params.status.streamState != STREAM_READY) {
2856 if(++count > READY_TIMEOUT)
2857 break;
2858 if(cam->params.status.streamState ==
2859 STREAM_PAUSED) {
2860 /* Bad news */
2861 if(!clear_stall(cam))
2862 return -EIO;
2863 }
2864
2865 cond_resched();
2866
2867 /* sleep for 10 ms, hopefully ;) */
2868 msleep_interruptible(10);
2869 if (signal_pending(current))
2870 return -EINTR;
2871
2872 do_command(cam, CPIA_COMMAND_GetCameraStatus,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002873 0, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002874 }
2875 if(cam->params.status.streamState != STREAM_READY) {
2876 continue;
2877 }
2878 }
2879
2880 cond_resched();
2881
2882 /* grab image from camera */
2883 oldjif = jiffies;
2884 image_size = cam->ops->streamRead(cam->lowlevel_data,
2885 cam->raw_image, 0);
2886 if (image_size <= 0) {
2887 DBG("streamRead failed: %d\n", image_size);
2888 continue;
2889 }
2890
2891 rate = image_size * HZ / 1024;
2892 diff = jiffies-oldjif;
2893 cam->transfer_rate = diff==0 ? rate : rate/diff;
2894 /* diff==0 ? unlikely but possible */
2895
2896 /* Switch flicker control back on if it got turned off */
2897 restart_flicker(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002898
Linus Torvalds1da177e2005-04-16 15:20:36 -07002899 /* If AEC is enabled, monitor the exposure and
2900 adjust the sensor frame rate if needed */
2901 if(cam->params.exposure.expMode == 2)
2902 monitor_exposure(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002903
Linus Torvalds1da177e2005-04-16 15:20:36 -07002904 /* camera idle now so dispatch queued commands */
2905 dispatch_commands(cam);
2906
2907 /* Update our knowledge of the camera state */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002908 do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
2909 do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002910 do_command(cam, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0);
2911
2912 /* decompress and convert image to by copying it from
2913 * raw_image to decompressed_frame
2914 */
2915
2916 cond_resched();
2917
2918 cam->image_size = parse_picture(cam, image_size);
2919 if (cam->image_size <= 0) {
2920 DBG("parse_picture failed %d\n", cam->image_size);
2921 if(cam->params.compression.mode !=
2922 CPIA_COMPRESSION_NONE) {
2923 /* Compression may not work right if we
2924 had a bad frame, get the next one
2925 uncompressed. */
2926 cam->first_frame = 1;
2927 do_command(cam, CPIA_COMMAND_SetGrabMode,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002928 CPIA_GRAB_SINGLE, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002929 /* FIXME: Trial & error - need up to 70ms for
2930 the grab mode change to complete ? */
2931 msleep_interruptible(70);
2932 if (signal_pending(current))
2933 return -EINTR;
2934 }
2935 } else
2936 break;
2937 }
2938
2939 if (retry < 3) {
2940 /* FIXME: this only works for double buffering */
2941 if (cam->frame[cam->curframe].state == FRAME_READY) {
2942 memcpy(cam->frame[cam->curframe].data,
2943 cam->decompressed_frame.data,
2944 cam->decompressed_frame.count);
2945 cam->frame[cam->curframe].state = FRAME_DONE;
2946 } else
2947 cam->decompressed_frame.state = FRAME_DONE;
2948
2949 if (cam->first_frame) {
2950 cam->first_frame = 0;
2951 do_command(cam, CPIA_COMMAND_SetCompression,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002952 cam->params.compression.mode,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002953 cam->params.compression.decimation, 0, 0);
2954
2955 /* Switch from single-grab to continuous grab */
2956 do_command(cam, CPIA_COMMAND_SetGrabMode,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002957 CPIA_GRAB_CONTINUOUS, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002958 }
2959 return 0;
2960 }
2961 return -EIO;
2962}
2963
2964static int capture_frame(struct cam_data *cam, struct video_mmap *vm)
2965{
2966 if (!cam->frame_buf) {
2967 /* we do lazy allocation */
2968 int err;
2969 if ((err = allocate_frame_buf(cam)))
2970 return err;
2971 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002972
Linus Torvalds1da177e2005-04-16 15:20:36 -07002973 cam->curframe = vm->frame;
2974 cam->frame[cam->curframe].state = FRAME_READY;
2975 return fetch_frame(cam);
2976}
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002977
Linus Torvalds1da177e2005-04-16 15:20:36 -07002978static int goto_high_power(struct cam_data *cam)
2979{
2980 if (do_command(cam, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0))
2981 return -EIO;
2982 msleep_interruptible(40); /* windows driver does it too */
2983 if(signal_pending(current))
2984 return -EINTR;
2985 if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
2986 return -EIO;
2987 if (cam->params.status.systemState == HI_POWER_STATE) {
2988 DBG("camera now in HIGH power state\n");
2989 return 0;
2990 }
2991 printstatus(cam);
2992 return -EIO;
2993}
2994
2995static int goto_low_power(struct cam_data *cam)
2996{
2997 if (do_command(cam, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0))
2998 return -1;
2999 if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
3000 return -1;
3001 if (cam->params.status.systemState == LO_POWER_STATE) {
3002 DBG("camera now in LOW power state\n");
3003 return 0;
3004 }
3005 printstatus(cam);
3006 return -1;
3007}
3008
3009static void save_camera_state(struct cam_data *cam)
3010{
3011 if(!(cam->cmd_queue & COMMAND_SETCOLOURBALANCE))
3012 do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
3013 if(!(cam->cmd_queue & COMMAND_SETEXPOSURE))
3014 do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
3015
3016 DBG("%d/%d/%d/%d/%d/%d/%d/%d\n",
3017 cam->params.exposure.gain,
3018 cam->params.exposure.fineExp,
3019 cam->params.exposure.coarseExpLo,
3020 cam->params.exposure.coarseExpHi,
3021 cam->params.exposure.redComp,
3022 cam->params.exposure.green1Comp,
3023 cam->params.exposure.green2Comp,
3024 cam->params.exposure.blueComp);
3025 DBG("%d/%d/%d\n",
3026 cam->params.colourBalance.redGain,
3027 cam->params.colourBalance.greenGain,
3028 cam->params.colourBalance.blueGain);
3029}
3030
3031static int set_camera_state(struct cam_data *cam)
3032{
3033 cam->cmd_queue = COMMAND_SETCOMPRESSION |
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003034 COMMAND_SETCOMPRESSIONTARGET |
3035 COMMAND_SETCOLOURPARAMS |
3036 COMMAND_SETFORMAT |
3037 COMMAND_SETYUVTHRESH |
3038 COMMAND_SETECPTIMING |
3039 COMMAND_SETCOMPRESSIONPARAMS |
3040 COMMAND_SETEXPOSURE |
3041 COMMAND_SETCOLOURBALANCE |
3042 COMMAND_SETSENSORFPS |
3043 COMMAND_SETAPCOR |
3044 COMMAND_SETFLICKERCTRL |
3045 COMMAND_SETVLOFFSET;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003046
3047 do_command(cam, CPIA_COMMAND_SetGrabMode, CPIA_GRAB_SINGLE,0,0,0);
3048 dispatch_commands(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003049
Linus Torvalds1da177e2005-04-16 15:20:36 -07003050 /* Wait 6 frames for the sensor to get all settings and
3051 AEC/ACB to settle */
3052 msleep_interruptible(6*(cam->params.sensorFps.baserate ? 33 : 40) *
3053 (1 << cam->params.sensorFps.divisor) + 10);
3054
3055 if(signal_pending(current))
3056 return -EINTR;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003057
Linus Torvalds1da177e2005-04-16 15:20:36 -07003058 save_camera_state(cam);
3059
3060 return 0;
3061}
3062
3063static void get_version_information(struct cam_data *cam)
3064{
3065 /* GetCPIAVersion */
3066 do_command(cam, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0);
3067
3068 /* GetPnPID */
3069 do_command(cam, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0);
3070}
3071
3072/* initialize camera */
3073static int reset_camera(struct cam_data *cam)
3074{
3075 int err;
3076 /* Start the camera in low power mode */
3077 if (goto_low_power(cam)) {
3078 if (cam->params.status.systemState != WARM_BOOT_STATE)
3079 return -ENODEV;
3080
3081 /* FIXME: this is just dirty trial and error */
3082 err = goto_high_power(cam);
3083 if(err)
3084 return err;
3085 do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0);
3086 if (goto_low_power(cam))
3087 return -ENODEV;
3088 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003089
Linus Torvalds1da177e2005-04-16 15:20:36 -07003090 /* procedure described in developer's guide p3-28 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003091
Linus Torvalds1da177e2005-04-16 15:20:36 -07003092 /* Check the firmware version. */
3093 cam->params.version.firmwareVersion = 0;
3094 get_version_information(cam);
3095 if (cam->params.version.firmwareVersion != 1)
3096 return -ENODEV;
3097
3098 /* A bug in firmware 1-02 limits gainMode to 2 */
3099 if(cam->params.version.firmwareRevision <= 2 &&
3100 cam->params.exposure.gainMode > 2) {
3101 cam->params.exposure.gainMode = 2;
3102 }
3103
3104 /* set QX3 detected flag */
3105 cam->params.qx3.qx3_detected = (cam->params.pnpID.vendor == 0x0813 &&
3106 cam->params.pnpID.product == 0x0001);
3107
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003108 /* The fatal error checking should be done after
Linus Torvalds1da177e2005-04-16 15:20:36 -07003109 * the camera powers up (developer's guide p 3-38) */
3110
3111 /* Set streamState before transition to high power to avoid bug
3112 * in firmware 1-02 */
3113 do_command(cam, CPIA_COMMAND_ModifyCameraStatus, STREAMSTATE, 0,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003114 STREAM_NOT_READY, 0);
3115
Linus Torvalds1da177e2005-04-16 15:20:36 -07003116 /* GotoHiPower */
3117 err = goto_high_power(cam);
3118 if (err)
3119 return err;
3120
3121 /* Check the camera status */
3122 if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
3123 return -EIO;
3124
3125 if (cam->params.status.fatalError) {
3126 DBG("fatal_error: %#04x\n",
3127 cam->params.status.fatalError);
3128 DBG("vp_status: %#04x\n",
3129 cam->params.status.vpStatus);
3130 if (cam->params.status.fatalError & ~(COM_FLAG|CPIA_FLAG)) {
3131 /* Fatal error in camera */
3132 return -EIO;
3133 } else if (cam->params.status.fatalError & (COM_FLAG|CPIA_FLAG)) {
3134 /* Firmware 1-02 may do this for parallel port cameras,
3135 * just clear the flags (developer's guide p 3-38) */
3136 do_command(cam, CPIA_COMMAND_ModifyCameraStatus,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003137 FATALERROR, ~(COM_FLAG|CPIA_FLAG), 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003138 }
3139 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003140
Linus Torvalds1da177e2005-04-16 15:20:36 -07003141 /* Check the camera status again */
3142 if (cam->params.status.fatalError) {
3143 if (cam->params.status.fatalError)
3144 return -EIO;
3145 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003146
Linus Torvalds1da177e2005-04-16 15:20:36 -07003147 /* VPVersion can't be retrieved before the camera is in HiPower,
3148 * so get it here instead of in get_version_information. */
3149 do_command(cam, CPIA_COMMAND_GetVPVersion, 0, 0, 0, 0);
3150
3151 /* set camera to a known state */
3152 return set_camera_state(cam);
3153}
3154
3155static void put_cam(struct cpia_camera_ops* ops)
3156{
3157 if (ops->owner)
3158 module_put(ops->owner);
3159}
3160
3161/* ------------------------- V4L interface --------------------- */
3162static int cpia_open(struct inode *inode, struct file *file)
3163{
3164 struct video_device *dev = video_devdata(file);
3165 struct cam_data *cam = dev->priv;
3166 int err;
3167
3168 if (!cam) {
3169 DBG("Internal error, cam_data not found!\n");
3170 return -ENODEV;
3171 }
3172
3173 if (cam->open_count > 0) {
3174 DBG("Camera already open\n");
3175 return -EBUSY;
3176 }
3177
3178 if (!try_module_get(cam->ops->owner))
3179 return -ENODEV;
3180
Ingo Molnar3593cab2006-02-07 06:49:14 -02003181 mutex_lock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003182 err = -ENOMEM;
3183 if (!cam->raw_image) {
3184 cam->raw_image = rvmalloc(CPIA_MAX_IMAGE_SIZE);
3185 if (!cam->raw_image)
3186 goto oops;
3187 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003188
Linus Torvalds1da177e2005-04-16 15:20:36 -07003189 if (!cam->decompressed_frame.data) {
3190 cam->decompressed_frame.data = rvmalloc(CPIA_MAX_FRAME_SIZE);
3191 if (!cam->decompressed_frame.data)
3192 goto oops;
3193 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003194
Linus Torvalds1da177e2005-04-16 15:20:36 -07003195 /* open cpia */
3196 err = -ENODEV;
3197 if (cam->ops->open(cam->lowlevel_data))
3198 goto oops;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003199
Linus Torvalds1da177e2005-04-16 15:20:36 -07003200 /* reset the camera */
3201 if ((err = reset_camera(cam)) != 0) {
3202 cam->ops->close(cam->lowlevel_data);
3203 goto oops;
3204 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003205
Linus Torvalds1da177e2005-04-16 15:20:36 -07003206 err = -EINTR;
3207 if(signal_pending(current))
3208 goto oops;
3209
3210 /* Set ownership of /proc/cpia/videoX to current user */
3211 if(cam->proc_entry)
3212 cam->proc_entry->uid = current->uid;
3213
3214 /* set mark for loading first frame uncompressed */
3215 cam->first_frame = 1;
3216
3217 /* init it to something */
3218 cam->mmap_kludge = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003219
Linus Torvalds1da177e2005-04-16 15:20:36 -07003220 ++cam->open_count;
3221 file->private_data = dev;
Ingo Molnar3593cab2006-02-07 06:49:14 -02003222 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003223 return 0;
3224
3225 oops:
3226 if (cam->decompressed_frame.data) {
3227 rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
3228 cam->decompressed_frame.data = NULL;
3229 }
3230 if (cam->raw_image) {
3231 rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
3232 cam->raw_image = NULL;
3233 }
Ingo Molnar3593cab2006-02-07 06:49:14 -02003234 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003235 put_cam(cam->ops);
3236 return err;
3237}
3238
3239static int cpia_close(struct inode *inode, struct file *file)
3240{
3241 struct video_device *dev = file->private_data;
3242 struct cam_data *cam = dev->priv;
3243
3244 if (cam->ops) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003245 /* Return ownership of /proc/cpia/videoX to root */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003246 if(cam->proc_entry)
3247 cam->proc_entry->uid = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003248
Linus Torvalds1da177e2005-04-16 15:20:36 -07003249 /* save camera state for later open (developers guide ch 3.5.3) */
3250 save_camera_state(cam);
3251
3252 /* GotoLoPower */
3253 goto_low_power(cam);
3254
3255 /* Update the camera status */
3256 do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
3257
3258 /* cleanup internal state stuff */
3259 free_frames(cam->frame);
3260
3261 /* close cpia */
3262 cam->ops->close(cam->lowlevel_data);
3263
3264 put_cam(cam->ops);
3265 }
3266
3267 if (--cam->open_count == 0) {
3268 /* clean up capture-buffers */
3269 if (cam->raw_image) {
3270 rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
3271 cam->raw_image = NULL;
3272 }
3273
3274 if (cam->decompressed_frame.data) {
3275 rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
3276 cam->decompressed_frame.data = NULL;
3277 }
3278
3279 if (cam->frame_buf)
3280 free_frame_buf(cam);
3281
3282 if (!cam->ops)
3283 kfree(cam);
3284 }
3285 file->private_data = NULL;
3286
3287 return 0;
3288}
3289
3290static ssize_t cpia_read(struct file *file, char __user *buf,
3291 size_t count, loff_t *ppos)
3292{
3293 struct video_device *dev = file->private_data;
3294 struct cam_data *cam = dev->priv;
3295 int err;
3296
3297 /* make this _really_ smp and multithread-safe */
Ingo Molnar3593cab2006-02-07 06:49:14 -02003298 if (mutex_lock_interruptible(&cam->busy_lock))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003299 return -EINTR;
3300
3301 if (!buf) {
3302 DBG("buf NULL\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02003303 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003304 return -EINVAL;
3305 }
3306
3307 if (!count) {
3308 DBG("count 0\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02003309 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003310 return 0;
3311 }
3312
3313 if (!cam->ops) {
3314 DBG("ops NULL\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02003315 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003316 return -ENODEV;
3317 }
3318
3319 /* upload frame */
3320 cam->decompressed_frame.state = FRAME_READY;
3321 cam->mmap_kludge=0;
3322 if((err = fetch_frame(cam)) != 0) {
3323 DBG("ERROR from fetch_frame: %d\n", err);
Ingo Molnar3593cab2006-02-07 06:49:14 -02003324 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003325 return err;
3326 }
3327 cam->decompressed_frame.state = FRAME_UNUSED;
3328
3329 /* copy data to user space */
3330 if (cam->decompressed_frame.count > count) {
3331 DBG("count wrong: %d, %lu\n", cam->decompressed_frame.count,
3332 (unsigned long) count);
Ingo Molnar3593cab2006-02-07 06:49:14 -02003333 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003334 return -EFAULT;
3335 }
3336 if (copy_to_user(buf, cam->decompressed_frame.data,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003337 cam->decompressed_frame.count)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003338 DBG("copy_to_user failed\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02003339 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003340 return -EFAULT;
3341 }
3342
Ingo Molnar3593cab2006-02-07 06:49:14 -02003343 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003344 return cam->decompressed_frame.count;
3345}
3346
3347static int cpia_do_ioctl(struct inode *inode, struct file *file,
3348 unsigned int ioctlnr, void *arg)
3349{
3350 struct video_device *dev = file->private_data;
3351 struct cam_data *cam = dev->priv;
3352 int retval = 0;
3353
3354 if (!cam || !cam->ops)
3355 return -ENODEV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003356
Linus Torvalds1da177e2005-04-16 15:20:36 -07003357 /* make this _really_ smp-safe */
Ingo Molnar3593cab2006-02-07 06:49:14 -02003358 if (mutex_lock_interruptible(&cam->busy_lock))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003359 return -EINTR;
3360
3361 //DBG("cpia_ioctl: %u\n", ioctlnr);
3362
3363 switch (ioctlnr) {
Alexey Dobriyanbe787ac2006-03-07 22:20:23 -03003364 /* query capabilities */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003365 case VIDIOCGCAP:
3366 {
3367 struct video_capability *b = arg;
3368
3369 DBG("VIDIOCGCAP\n");
3370 strcpy(b->name, "CPiA Camera");
3371 b->type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE;
3372 b->channels = 1;
3373 b->audios = 0;
3374 b->maxwidth = 352; /* VIDEOSIZE_CIF */
3375 b->maxheight = 288;
3376 b->minwidth = 48; /* VIDEOSIZE_48_48 */
3377 b->minheight = 48;
3378 break;
3379 }
3380
3381 /* get/set video source - we are a camera and nothing else */
3382 case VIDIOCGCHAN:
3383 {
3384 struct video_channel *v = arg;
3385
3386 DBG("VIDIOCGCHAN\n");
3387 if (v->channel != 0) {
3388 retval = -EINVAL;
3389 break;
3390 }
3391
3392 v->channel = 0;
3393 strcpy(v->name, "Camera");
3394 v->tuners = 0;
3395 v->flags = 0;
3396 v->type = VIDEO_TYPE_CAMERA;
3397 v->norm = 0;
3398 break;
3399 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003400
Linus Torvalds1da177e2005-04-16 15:20:36 -07003401 case VIDIOCSCHAN:
3402 {
3403 struct video_channel *v = arg;
3404
3405 DBG("VIDIOCSCHAN\n");
3406 if (v->channel != 0)
3407 retval = -EINVAL;
3408 break;
3409 }
3410
3411 /* image properties */
3412 case VIDIOCGPICT:
3413 {
3414 struct video_picture *pic = arg;
3415 DBG("VIDIOCGPICT\n");
3416 *pic = cam->vp;
3417 break;
3418 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003419
Linus Torvalds1da177e2005-04-16 15:20:36 -07003420 case VIDIOCSPICT:
3421 {
3422 struct video_picture *vp = arg;
3423
3424 DBG("VIDIOCSPICT\n");
3425
3426 /* check validity */
3427 DBG("palette: %d\n", vp->palette);
3428 DBG("depth: %d\n", vp->depth);
3429 if (!valid_mode(vp->palette, vp->depth)) {
3430 retval = -EINVAL;
3431 break;
3432 }
3433
Ingo Molnar3593cab2006-02-07 06:49:14 -02003434 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003435 /* brightness, colour, contrast need no check 0-65535 */
3436 cam->vp = *vp;
3437 /* update cam->params.colourParams */
3438 cam->params.colourParams.brightness = vp->brightness*100/65535;
3439 cam->params.colourParams.contrast = vp->contrast*100/65535;
3440 cam->params.colourParams.saturation = vp->colour*100/65535;
3441 /* contrast is in steps of 8, so round */
3442 cam->params.colourParams.contrast =
3443 ((cam->params.colourParams.contrast + 3) / 8) * 8;
3444 if (cam->params.version.firmwareVersion == 1 &&
3445 cam->params.version.firmwareRevision == 2 &&
3446 cam->params.colourParams.contrast > 80) {
3447 /* 1-02 firmware limits contrast to 80 */
3448 cam->params.colourParams.contrast = 80;
3449 }
3450
3451 /* Adjust flicker control if necessary */
3452 if(cam->params.flickerControl.allowableOverExposure < 0)
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003453 cam->params.flickerControl.allowableOverExposure =
Linus Torvalds1da177e2005-04-16 15:20:36 -07003454 -find_over_exposure(cam->params.colourParams.brightness);
3455 if(cam->params.flickerControl.flickerMode != 0)
3456 cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003457
Linus Torvalds1da177e2005-04-16 15:20:36 -07003458
3459 /* queue command to update camera */
3460 cam->cmd_queue |= COMMAND_SETCOLOURPARAMS;
Ingo Molnar3593cab2006-02-07 06:49:14 -02003461 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003462 DBG("VIDIOCSPICT: %d / %d // %d / %d / %d / %d\n",
3463 vp->depth, vp->palette, vp->brightness, vp->hue, vp->colour,
3464 vp->contrast);
3465 break;
3466 }
3467
3468 /* get/set capture window */
3469 case VIDIOCGWIN:
3470 {
3471 struct video_window *vw = arg;
3472 DBG("VIDIOCGWIN\n");
3473
3474 *vw = cam->vw;
3475 break;
3476 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003477
Linus Torvalds1da177e2005-04-16 15:20:36 -07003478 case VIDIOCSWIN:
3479 {
3480 /* copy_from_user, check validity, copy to internal structure */
3481 struct video_window *vw = arg;
3482 DBG("VIDIOCSWIN\n");
3483
3484 if (vw->clipcount != 0) { /* clipping not supported */
3485 retval = -EINVAL;
3486 break;
3487 }
3488 if (vw->clips != NULL) { /* clipping not supported */
3489 retval = -EINVAL;
3490 break;
3491 }
3492
3493 /* we set the video window to something smaller or equal to what
3494 * is requested by the user???
3495 */
Ingo Molnar3593cab2006-02-07 06:49:14 -02003496 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003497 if (vw->width != cam->vw.width || vw->height != cam->vw.height) {
3498 int video_size = match_videosize(vw->width, vw->height);
3499
3500 if (video_size < 0) {
3501 retval = -EINVAL;
Ingo Molnar3593cab2006-02-07 06:49:14 -02003502 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003503 break;
3504 }
3505 cam->video_size = video_size;
3506
3507 /* video size is changing, reset the subcapture area */
3508 memset(&cam->vc, 0, sizeof(cam->vc));
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003509
Linus Torvalds1da177e2005-04-16 15:20:36 -07003510 set_vw_size(cam);
3511 DBG("%d / %d\n", cam->vw.width, cam->vw.height);
3512 cam->cmd_queue |= COMMAND_SETFORMAT;
3513 }
3514
Ingo Molnar3593cab2006-02-07 06:49:14 -02003515 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003516
3517 /* setformat ignored by camera during streaming,
3518 * so stop/dispatch/start */
3519 if (cam->cmd_queue & COMMAND_SETFORMAT) {
3520 DBG("\n");
3521 dispatch_commands(cam);
3522 }
3523 DBG("%d/%d:%d\n", cam->video_size,
3524 cam->vw.width, cam->vw.height);
3525 break;
3526 }
3527
3528 /* mmap interface */
3529 case VIDIOCGMBUF:
3530 {
3531 struct video_mbuf *vm = arg;
3532 int i;
3533
3534 DBG("VIDIOCGMBUF\n");
3535 memset(vm, 0, sizeof(*vm));
3536 vm->size = CPIA_MAX_FRAME_SIZE*FRAME_NUM;
3537 vm->frames = FRAME_NUM;
3538 for (i = 0; i < FRAME_NUM; i++)
3539 vm->offsets[i] = CPIA_MAX_FRAME_SIZE * i;
3540 break;
3541 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003542
Linus Torvalds1da177e2005-04-16 15:20:36 -07003543 case VIDIOCMCAPTURE:
3544 {
3545 struct video_mmap *vm = arg;
3546 int video_size;
3547
3548 DBG("VIDIOCMCAPTURE: %d / %d / %dx%d\n", vm->format, vm->frame,
3549 vm->width, vm->height);
3550 if (vm->frame<0||vm->frame>=FRAME_NUM) {
3551 retval = -EINVAL;
3552 break;
3553 }
3554
3555 /* set video format */
3556 cam->vp.palette = vm->format;
3557 switch(vm->format) {
3558 case VIDEO_PALETTE_GREY:
3559 cam->vp.depth=8;
3560 break;
3561 case VIDEO_PALETTE_RGB555:
3562 case VIDEO_PALETTE_RGB565:
3563 case VIDEO_PALETTE_YUV422:
3564 case VIDEO_PALETTE_YUYV:
3565 case VIDEO_PALETTE_UYVY:
3566 cam->vp.depth = 16;
3567 break;
3568 case VIDEO_PALETTE_RGB24:
3569 cam->vp.depth = 24;
3570 break;
3571 case VIDEO_PALETTE_RGB32:
3572 cam->vp.depth = 32;
3573 break;
3574 default:
3575 retval = -EINVAL;
3576 break;
3577 }
3578 if (retval)
3579 break;
3580
3581 /* set video size */
3582 video_size = match_videosize(vm->width, vm->height);
3583 if (video_size < 0) {
3584 retval = -EINVAL;
3585 break;
3586 }
3587 if (video_size != cam->video_size) {
3588 cam->video_size = video_size;
3589
3590 /* video size is changing, reset the subcapture area */
3591 memset(&cam->vc, 0, sizeof(cam->vc));
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003592
Linus Torvalds1da177e2005-04-16 15:20:36 -07003593 set_vw_size(cam);
3594 cam->cmd_queue |= COMMAND_SETFORMAT;
3595 dispatch_commands(cam);
3596 }
3597 /* according to v4l-spec we must start streaming here */
3598 cam->mmap_kludge = 1;
3599 retval = capture_frame(cam, vm);
3600
3601 break;
3602 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003603
Linus Torvalds1da177e2005-04-16 15:20:36 -07003604 case VIDIOCSYNC:
3605 {
3606 int *frame = arg;
3607
3608 //DBG("VIDIOCSYNC: %d\n", *frame);
3609
3610 if (*frame<0 || *frame >= FRAME_NUM) {
3611 retval = -EINVAL;
3612 break;
3613 }
3614
3615 switch (cam->frame[*frame].state) {
3616 case FRAME_UNUSED:
3617 case FRAME_READY:
3618 case FRAME_GRABBING:
3619 DBG("sync to unused frame %d\n", *frame);
3620 retval = -EINVAL;
3621 break;
3622
3623 case FRAME_DONE:
3624 cam->frame[*frame].state = FRAME_UNUSED;
3625 //DBG("VIDIOCSYNC: %d synced\n", *frame);
3626 break;
3627 }
3628 if (retval == -EINTR) {
3629 /* FIXME - xawtv does not handle this nice */
3630 retval = 0;
3631 }
3632 break;
3633 }
3634
3635 case VIDIOCGCAPTURE:
3636 {
3637 struct video_capture *vc = arg;
3638
3639 DBG("VIDIOCGCAPTURE\n");
3640
3641 *vc = cam->vc;
3642
3643 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003644 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003645
3646 case VIDIOCSCAPTURE:
3647 {
3648 struct video_capture *vc = arg;
3649
3650 DBG("VIDIOCSCAPTURE\n");
3651
3652 if (vc->decimation != 0) { /* How should this be used? */
3653 retval = -EINVAL;
3654 break;
3655 }
3656 if (vc->flags != 0) { /* Even/odd grab not supported */
3657 retval = -EINVAL;
3658 break;
3659 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003660
Linus Torvalds1da177e2005-04-16 15:20:36 -07003661 /* Clip to the resolution we can set for the ROI
3662 (every 8 columns and 4 rows) */
3663 vc->x = vc->x & ~(__u32)7;
3664 vc->y = vc->y & ~(__u32)3;
3665 vc->width = vc->width & ~(__u32)7;
3666 vc->height = vc->height & ~(__u32)3;
3667
3668 if(vc->width == 0 || vc->height == 0 ||
3669 vc->x + vc->width > cam->vw.width ||
3670 vc->y + vc->height > cam->vw.height) {
3671 retval = -EINVAL;
3672 break;
3673 }
3674
3675 DBG("%d,%d/%dx%d\n", vc->x,vc->y,vc->width, vc->height);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003676
Ingo Molnar3593cab2006-02-07 06:49:14 -02003677 mutex_lock(&cam->param_lock);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003678
Linus Torvalds1da177e2005-04-16 15:20:36 -07003679 cam->vc.x = vc->x;
3680 cam->vc.y = vc->y;
3681 cam->vc.width = vc->width;
3682 cam->vc.height = vc->height;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003683
Linus Torvalds1da177e2005-04-16 15:20:36 -07003684 set_vw_size(cam);
3685 cam->cmd_queue |= COMMAND_SETFORMAT;
3686
Ingo Molnar3593cab2006-02-07 06:49:14 -02003687 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003688
3689 /* setformat ignored by camera during streaming,
3690 * so stop/dispatch/start */
3691 dispatch_commands(cam);
3692 break;
3693 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003694
Linus Torvalds1da177e2005-04-16 15:20:36 -07003695 case VIDIOCGUNIT:
3696 {
3697 struct video_unit *vu = arg;
3698
3699 DBG("VIDIOCGUNIT\n");
3700
3701 vu->video = cam->vdev.minor;
3702 vu->vbi = VIDEO_NO_UNIT;
3703 vu->radio = VIDEO_NO_UNIT;
3704 vu->audio = VIDEO_NO_UNIT;
3705 vu->teletext = VIDEO_NO_UNIT;
3706
3707 break;
3708 }
3709
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003710
Linus Torvalds1da177e2005-04-16 15:20:36 -07003711 /* pointless to implement overlay with this camera */
3712 case VIDIOCCAPTURE:
3713 case VIDIOCGFBUF:
3714 case VIDIOCSFBUF:
3715 case VIDIOCKEY:
3716 /* tuner interface - we have none */
3717 case VIDIOCGTUNER:
3718 case VIDIOCSTUNER:
3719 case VIDIOCGFREQ:
3720 case VIDIOCSFREQ:
3721 /* audio interface - we have none */
3722 case VIDIOCGAUDIO:
3723 case VIDIOCSAUDIO:
3724 retval = -EINVAL;
3725 break;
3726 default:
3727 retval = -ENOIOCTLCMD;
3728 break;
3729 }
3730
Ingo Molnar3593cab2006-02-07 06:49:14 -02003731 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003732 return retval;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003733}
Linus Torvalds1da177e2005-04-16 15:20:36 -07003734
3735static int cpia_ioctl(struct inode *inode, struct file *file,
3736 unsigned int cmd, unsigned long arg)
3737{
3738 return video_usercopy(inode, file, cmd, arg, cpia_do_ioctl);
3739}
3740
3741
3742/* FIXME */
3743static int cpia_mmap(struct file *file, struct vm_area_struct *vma)
3744{
3745 struct video_device *dev = file->private_data;
3746 unsigned long start = vma->vm_start;
3747 unsigned long size = vma->vm_end - vma->vm_start;
3748 unsigned long page, pos;
3749 struct cam_data *cam = dev->priv;
3750 int retval;
3751
3752 if (!cam || !cam->ops)
3753 return -ENODEV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003754
Linus Torvalds1da177e2005-04-16 15:20:36 -07003755 DBG("cpia_mmap: %ld\n", size);
3756
3757 if (size > FRAME_NUM*CPIA_MAX_FRAME_SIZE)
3758 return -EINVAL;
3759
3760 if (!cam || !cam->ops)
3761 return -ENODEV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003762
Linus Torvalds1da177e2005-04-16 15:20:36 -07003763 /* make this _really_ smp-safe */
Ingo Molnar3593cab2006-02-07 06:49:14 -02003764 if (mutex_lock_interruptible(&cam->busy_lock))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003765 return -EINTR;
3766
3767 if (!cam->frame_buf) { /* we do lazy allocation */
3768 if ((retval = allocate_frame_buf(cam))) {
Ingo Molnar3593cab2006-02-07 06:49:14 -02003769 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003770 return retval;
3771 }
3772 }
3773
3774 pos = (unsigned long)(cam->frame_buf);
3775 while (size > 0) {
3776 page = vmalloc_to_pfn((void *)pos);
3777 if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) {
Ingo Molnar3593cab2006-02-07 06:49:14 -02003778 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003779 return -EAGAIN;
3780 }
3781 start += PAGE_SIZE;
3782 pos += PAGE_SIZE;
3783 if (size > PAGE_SIZE)
3784 size -= PAGE_SIZE;
3785 else
3786 size = 0;
3787 }
3788
3789 DBG("cpia_mmap: %ld\n", size);
Ingo Molnar3593cab2006-02-07 06:49:14 -02003790 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003791
3792 return 0;
3793}
3794
3795static struct file_operations cpia_fops = {
3796 .owner = THIS_MODULE,
3797 .open = cpia_open,
3798 .release = cpia_close,
3799 .read = cpia_read,
3800 .mmap = cpia_mmap,
3801 .ioctl = cpia_ioctl,
Arnd Bergmann0d0fbf82006-01-09 15:24:57 -02003802 .compat_ioctl = v4l_compat_ioctl32,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003803 .llseek = no_llseek,
3804};
3805
3806static struct video_device cpia_template = {
3807 .owner = THIS_MODULE,
3808 .name = "CPiA Camera",
3809 .type = VID_TYPE_CAPTURE,
3810 .hardware = VID_HARDWARE_CPIA,
3811 .fops = &cpia_fops,
3812};
3813
3814/* initialise cam_data structure */
3815static void reset_camera_struct(struct cam_data *cam)
3816{
3817 /* The following parameter values are the defaults from
3818 * "Software Developer's Guide for CPiA Cameras". Any changes
3819 * to the defaults are noted in comments. */
3820 cam->params.colourParams.brightness = 50;
3821 cam->params.colourParams.contrast = 48;
3822 cam->params.colourParams.saturation = 50;
3823 cam->params.exposure.gainMode = 4;
3824 cam->params.exposure.expMode = 2; /* AEC */
3825 cam->params.exposure.compMode = 1;
3826 cam->params.exposure.centreWeight = 1;
3827 cam->params.exposure.gain = 0;
3828 cam->params.exposure.fineExp = 0;
3829 cam->params.exposure.coarseExpLo = 185;
3830 cam->params.exposure.coarseExpHi = 0;
3831 cam->params.exposure.redComp = COMP_RED;
3832 cam->params.exposure.green1Comp = COMP_GREEN1;
3833 cam->params.exposure.green2Comp = COMP_GREEN2;
3834 cam->params.exposure.blueComp = COMP_BLUE;
3835 cam->params.colourBalance.balanceMode = 2; /* ACB */
3836 cam->params.colourBalance.redGain = 32;
3837 cam->params.colourBalance.greenGain = 6;
3838 cam->params.colourBalance.blueGain = 92;
3839 cam->params.apcor.gain1 = 0x18;
3840 cam->params.apcor.gain2 = 0x16;
3841 cam->params.apcor.gain4 = 0x24;
3842 cam->params.apcor.gain8 = 0x34;
3843 cam->params.flickerControl.flickerMode = 0;
3844 cam->params.flickerControl.disabled = 1;
3845
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003846 cam->params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07003847 flicker_jumps[cam->mainsFreq]
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003848 [cam->params.sensorFps.baserate]
3849 [cam->params.sensorFps.divisor];
3850 cam->params.flickerControl.allowableOverExposure =
Linus Torvalds1da177e2005-04-16 15:20:36 -07003851 -find_over_exposure(cam->params.colourParams.brightness);
3852 cam->params.vlOffset.gain1 = 20;
3853 cam->params.vlOffset.gain2 = 24;
3854 cam->params.vlOffset.gain4 = 26;
3855 cam->params.vlOffset.gain8 = 26;
3856 cam->params.compressionParams.hysteresis = 3;
3857 cam->params.compressionParams.threshMax = 11;
3858 cam->params.compressionParams.smallStep = 1;
3859 cam->params.compressionParams.largeStep = 3;
3860 cam->params.compressionParams.decimationHysteresis = 2;
3861 cam->params.compressionParams.frDiffStepThresh = 5;
3862 cam->params.compressionParams.qDiffStepThresh = 3;
3863 cam->params.compressionParams.decimationThreshMod = 2;
3864 /* End of default values from Software Developer's Guide */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003865
Linus Torvalds1da177e2005-04-16 15:20:36 -07003866 cam->transfer_rate = 0;
3867 cam->exposure_status = EXPOSURE_NORMAL;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003868
Linus Torvalds1da177e2005-04-16 15:20:36 -07003869 /* Set Sensor FPS to 15fps. This seems better than 30fps
3870 * for indoor lighting. */
3871 cam->params.sensorFps.divisor = 1;
3872 cam->params.sensorFps.baserate = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003873
Linus Torvalds1da177e2005-04-16 15:20:36 -07003874 cam->params.yuvThreshold.yThreshold = 6; /* From windows driver */
3875 cam->params.yuvThreshold.uvThreshold = 6; /* From windows driver */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003876
Linus Torvalds1da177e2005-04-16 15:20:36 -07003877 cam->params.format.subSample = SUBSAMPLE_422;
3878 cam->params.format.yuvOrder = YUVORDER_YUYV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003879
Linus Torvalds1da177e2005-04-16 15:20:36 -07003880 cam->params.compression.mode = CPIA_COMPRESSION_AUTO;
3881 cam->params.compressionTarget.frTargeting =
3882 CPIA_COMPRESSION_TARGET_QUALITY;
3883 cam->params.compressionTarget.targetFR = 15; /* From windows driver */
3884 cam->params.compressionTarget.targetQ = 5; /* From windows driver */
3885
3886 cam->params.qx3.qx3_detected = 0;
3887 cam->params.qx3.toplight = 0;
3888 cam->params.qx3.bottomlight = 0;
3889 cam->params.qx3.button = 0;
3890 cam->params.qx3.cradled = 0;
3891
3892 cam->video_size = VIDEOSIZE_CIF;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003893
Linus Torvalds1da177e2005-04-16 15:20:36 -07003894 cam->vp.colour = 32768; /* 50% */
3895 cam->vp.hue = 32768; /* 50% */
3896 cam->vp.brightness = 32768; /* 50% */
3897 cam->vp.contrast = 32768; /* 50% */
3898 cam->vp.whiteness = 0; /* not used -> grayscale only */
3899 cam->vp.depth = 24; /* to be set by user */
3900 cam->vp.palette = VIDEO_PALETTE_RGB24; /* to be set by user */
3901
3902 cam->vc.x = 0;
3903 cam->vc.y = 0;
3904 cam->vc.width = 0;
3905 cam->vc.height = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003906
Linus Torvalds1da177e2005-04-16 15:20:36 -07003907 cam->vw.x = 0;
3908 cam->vw.y = 0;
3909 set_vw_size(cam);
3910 cam->vw.chromakey = 0;
3911 cam->vw.flags = 0;
3912 cam->vw.clipcount = 0;
3913 cam->vw.clips = NULL;
3914
3915 cam->cmd_queue = COMMAND_NONE;
3916 cam->first_frame = 1;
3917
3918 return;
3919}
3920
3921/* initialize cam_data structure */
3922static void init_camera_struct(struct cam_data *cam,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003923 struct cpia_camera_ops *ops )
Linus Torvalds1da177e2005-04-16 15:20:36 -07003924{
3925 int i;
3926
3927 /* Default everything to 0 */
3928 memset(cam, 0, sizeof(struct cam_data));
3929
3930 cam->ops = ops;
Ingo Molnar3593cab2006-02-07 06:49:14 -02003931 mutex_init(&cam->param_lock);
3932 mutex_init(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003933
3934 reset_camera_struct(cam);
3935
3936 cam->proc_entry = NULL;
3937
3938 memcpy(&cam->vdev, &cpia_template, sizeof(cpia_template));
3939 cam->vdev.priv = cam;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003940
Linus Torvalds1da177e2005-04-16 15:20:36 -07003941 cam->curframe = 0;
3942 for (i = 0; i < FRAME_NUM; i++) {
3943 cam->frame[i].width = 0;
3944 cam->frame[i].height = 0;
3945 cam->frame[i].state = FRAME_UNUSED;
3946 cam->frame[i].data = NULL;
3947 }
3948 cam->decompressed_frame.width = 0;
3949 cam->decompressed_frame.height = 0;
3950 cam->decompressed_frame.state = FRAME_UNUSED;
3951 cam->decompressed_frame.data = NULL;
3952}
3953
3954struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel)
3955{
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003956 struct cam_data *camera;
3957
Linus Torvalds1da177e2005-04-16 15:20:36 -07003958 if ((camera = kmalloc(sizeof(struct cam_data), GFP_KERNEL)) == NULL)
3959 return NULL;
3960
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003961
Linus Torvalds1da177e2005-04-16 15:20:36 -07003962 init_camera_struct( camera, ops );
3963 camera->lowlevel_data = lowlevel;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003964
Linus Torvalds1da177e2005-04-16 15:20:36 -07003965 /* register v4l device */
3966 if (video_register_device(&camera->vdev, VFL_TYPE_GRABBER, video_nr) == -1) {
3967 kfree(camera);
3968 printk(KERN_DEBUG "video_register_device failed\n");
3969 return NULL;
3970 }
3971
3972 /* get version information from camera: open/reset/close */
3973
3974 /* open cpia */
3975 if (camera->ops->open(camera->lowlevel_data))
3976 return camera;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003977
Linus Torvalds1da177e2005-04-16 15:20:36 -07003978 /* reset the camera */
3979 if (reset_camera(camera) != 0) {
3980 camera->ops->close(camera->lowlevel_data);
3981 return camera;
3982 }
3983
3984 /* close cpia */
3985 camera->ops->close(camera->lowlevel_data);
3986
3987#ifdef CONFIG_PROC_FS
3988 create_proc_cpia_cam(camera);
3989#endif
3990
3991 printk(KERN_INFO " CPiA Version: %d.%02d (%d.%d)\n",
3992 camera->params.version.firmwareVersion,
3993 camera->params.version.firmwareRevision,
3994 camera->params.version.vcVersion,
3995 camera->params.version.vcRevision);
3996 printk(KERN_INFO " CPiA PnP-ID: %04x:%04x:%04x\n",
3997 camera->params.pnpID.vendor,
3998 camera->params.pnpID.product,
3999 camera->params.pnpID.deviceRevision);
4000 printk(KERN_INFO " VP-Version: %d.%d %04x\n",
4001 camera->params.vpVersion.vpVersion,
4002 camera->params.vpVersion.vpRevision,
4003 camera->params.vpVersion.cameraHeadID);
4004
4005 return camera;
4006}
4007
4008void cpia_unregister_camera(struct cam_data *cam)
4009{
4010 DBG("unregistering video\n");
4011 video_unregister_device(&cam->vdev);
4012 if (cam->open_count) {
4013 put_cam(cam->ops);
4014 DBG("camera open -- setting ops to NULL\n");
4015 cam->ops = NULL;
4016 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03004017
Linus Torvalds1da177e2005-04-16 15:20:36 -07004018#ifdef CONFIG_PROC_FS
4019 DBG("destroying /proc/cpia/video%d\n", cam->vdev.minor);
4020 destroy_proc_cpia_cam(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03004021#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07004022 if (!cam->open_count) {
4023 DBG("freeing camera\n");
4024 kfree(cam);
4025 }
4026}
4027
4028static int __init cpia_init(void)
4029{
4030 printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT,
4031 CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER);
4032
4033 printk(KERN_WARNING "Since in-kernel colorspace conversion is not "
4034 "allowed, it is disabled by default now. Users should fix the "
4035 "applications in case they don't work without conversion "
4036 "reenabled by setting the 'colorspace_conv' module "
Randy Dunlap94190452006-03-27 16:18:25 -03004037 "parameter to 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004038
4039#ifdef CONFIG_PROC_FS
4040 proc_cpia_create();
4041#endif
4042
Linus Torvalds1da177e2005-04-16 15:20:36 -07004043 return 0;
4044}
4045
4046static void __exit cpia_exit(void)
4047{
4048#ifdef CONFIG_PROC_FS
4049 proc_cpia_destroy();
4050#endif
4051}
4052
4053module_init(cpia_init);
4054module_exit(cpia_exit);
4055
4056/* Exported symbols for modules. */
4057
4058EXPORT_SYMBOL(cpia_register_camera);
4059EXPORT_SYMBOL(cpia_unregister_camera);