blob: 028a400d2453b64f4658091cfa0c86eb71b833db [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * cpia CPiA driver
3 *
4 * Supports CPiA based Video Camera's.
5 *
6 * (C) Copyright 1999-2000 Peter Pregler
7 * (C) Copyright 1999-2000 Scott J. Bertin
8 * (C) Copyright 1999-2000 Johannes Erdfelt <johannes@erdfelt.com>
9 * (C) Copyright 2000 STMicroelectronics
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 */
25
26/* define _CPIA_DEBUG_ for verbose debug output (see cpia.h) */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030027/* #define _CPIA_DEBUG_ 1 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070028
Linus Torvalds1da177e2005-04-16 15:20:36 -070029
30#include <linux/module.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070031#include <linux/init.h>
32#include <linux/fs.h>
33#include <linux/vmalloc.h>
34#include <linux/slab.h>
35#include <linux/proc_fs.h>
36#include <linux/ctype.h>
37#include <linux/pagemap.h>
38#include <linux/delay.h>
39#include <asm/io.h>
Ingo Molnar3593cab2006-02-07 06:49:14 -020040#include <linux/mutex.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070041
Linus Torvalds1da177e2005-04-16 15:20:36 -070042#include "cpia.h"
43
Linus Torvalds1da177e2005-04-16 15:20:36 -070044static int video_nr = -1;
45
46#ifdef MODULE
47module_param(video_nr, int, 0);
Randy Dunlap2f8de1a2006-03-21 14:53:22 -030048MODULE_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 -070049MODULE_DESCRIPTION("V4L-driver for Vision CPiA based cameras");
50MODULE_LICENSE("GPL");
51MODULE_SUPPORTED_DEVICE("video");
52#endif
53
Randy Dunlap94190452006-03-27 16:18:25 -030054static unsigned short colorspace_conv;
Linus Torvalds1da177e2005-04-16 15:20:36 -070055module_param(colorspace_conv, ushort, 0444);
56MODULE_PARM_DESC(colorspace_conv,
Mauro Carvalho Chehab4286c6f2006-04-08 16:06:16 -030057 " Colorspace conversion:"
58 "\n 0 = disable, 1 = enable"
59 "\n Default value is 0"
60 );
Linus Torvalds1da177e2005-04-16 15:20:36 -070061
62#define ABOUT "V4L-Driver for Vision CPiA based cameras"
63
Linus Torvalds1da177e2005-04-16 15:20:36 -070064#define CPIA_MODULE_CPIA (0<<5)
65#define CPIA_MODULE_SYSTEM (1<<5)
66#define CPIA_MODULE_VP_CTRL (5<<5)
67#define CPIA_MODULE_CAPTURE (6<<5)
68#define CPIA_MODULE_DEBUG (7<<5)
69
70#define INPUT (DATA_IN << 8)
71#define OUTPUT (DATA_OUT << 8)
72
73#define CPIA_COMMAND_GetCPIAVersion (INPUT | CPIA_MODULE_CPIA | 1)
74#define CPIA_COMMAND_GetPnPID (INPUT | CPIA_MODULE_CPIA | 2)
75#define CPIA_COMMAND_GetCameraStatus (INPUT | CPIA_MODULE_CPIA | 3)
76#define CPIA_COMMAND_GotoHiPower (OUTPUT | CPIA_MODULE_CPIA | 4)
77#define CPIA_COMMAND_GotoLoPower (OUTPUT | CPIA_MODULE_CPIA | 5)
78#define CPIA_COMMAND_GotoSuspend (OUTPUT | CPIA_MODULE_CPIA | 7)
79#define CPIA_COMMAND_GotoPassThrough (OUTPUT | CPIA_MODULE_CPIA | 8)
80#define CPIA_COMMAND_ModifyCameraStatus (OUTPUT | CPIA_MODULE_CPIA | 10)
81
82#define CPIA_COMMAND_ReadVCRegs (INPUT | CPIA_MODULE_SYSTEM | 1)
83#define CPIA_COMMAND_WriteVCReg (OUTPUT | CPIA_MODULE_SYSTEM | 2)
84#define CPIA_COMMAND_ReadMCPorts (INPUT | CPIA_MODULE_SYSTEM | 3)
85#define CPIA_COMMAND_WriteMCPort (OUTPUT | CPIA_MODULE_SYSTEM | 4)
86#define CPIA_COMMAND_SetBaudRate (OUTPUT | CPIA_MODULE_SYSTEM | 5)
87#define CPIA_COMMAND_SetECPTiming (OUTPUT | CPIA_MODULE_SYSTEM | 6)
88#define CPIA_COMMAND_ReadIDATA (INPUT | CPIA_MODULE_SYSTEM | 7)
89#define CPIA_COMMAND_WriteIDATA (OUTPUT | CPIA_MODULE_SYSTEM | 8)
90#define CPIA_COMMAND_GenericCall (OUTPUT | CPIA_MODULE_SYSTEM | 9)
91#define CPIA_COMMAND_I2CStart (OUTPUT | CPIA_MODULE_SYSTEM | 10)
92#define CPIA_COMMAND_I2CStop (OUTPUT | CPIA_MODULE_SYSTEM | 11)
93#define CPIA_COMMAND_I2CWrite (OUTPUT | CPIA_MODULE_SYSTEM | 12)
94#define CPIA_COMMAND_I2CRead (INPUT | CPIA_MODULE_SYSTEM | 13)
95
96#define CPIA_COMMAND_GetVPVersion (INPUT | CPIA_MODULE_VP_CTRL | 1)
97#define CPIA_COMMAND_ResetFrameCounter (INPUT | CPIA_MODULE_VP_CTRL | 2)
98#define CPIA_COMMAND_SetColourParams (OUTPUT | CPIA_MODULE_VP_CTRL | 3)
99#define CPIA_COMMAND_SetExposure (OUTPUT | CPIA_MODULE_VP_CTRL | 4)
100#define CPIA_COMMAND_SetColourBalance (OUTPUT | CPIA_MODULE_VP_CTRL | 6)
101#define CPIA_COMMAND_SetSensorFPS (OUTPUT | CPIA_MODULE_VP_CTRL | 7)
102#define CPIA_COMMAND_SetVPDefaults (OUTPUT | CPIA_MODULE_VP_CTRL | 8)
103#define CPIA_COMMAND_SetApcor (OUTPUT | CPIA_MODULE_VP_CTRL | 9)
104#define CPIA_COMMAND_SetFlickerCtrl (OUTPUT | CPIA_MODULE_VP_CTRL | 10)
105#define CPIA_COMMAND_SetVLOffset (OUTPUT | CPIA_MODULE_VP_CTRL | 11)
106#define CPIA_COMMAND_GetColourParams (INPUT | CPIA_MODULE_VP_CTRL | 16)
107#define CPIA_COMMAND_GetColourBalance (INPUT | CPIA_MODULE_VP_CTRL | 17)
108#define CPIA_COMMAND_GetExposure (INPUT | CPIA_MODULE_VP_CTRL | 18)
109#define CPIA_COMMAND_SetSensorMatrix (OUTPUT | CPIA_MODULE_VP_CTRL | 19)
110#define CPIA_COMMAND_ColourBars (OUTPUT | CPIA_MODULE_VP_CTRL | 25)
111#define CPIA_COMMAND_ReadVPRegs (INPUT | CPIA_MODULE_VP_CTRL | 30)
112#define CPIA_COMMAND_WriteVPReg (OUTPUT | CPIA_MODULE_VP_CTRL | 31)
113
114#define CPIA_COMMAND_GrabFrame (OUTPUT | CPIA_MODULE_CAPTURE | 1)
115#define CPIA_COMMAND_UploadFrame (OUTPUT | CPIA_MODULE_CAPTURE | 2)
116#define CPIA_COMMAND_SetGrabMode (OUTPUT | CPIA_MODULE_CAPTURE | 3)
117#define CPIA_COMMAND_InitStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 4)
118#define CPIA_COMMAND_FiniStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 5)
119#define CPIA_COMMAND_StartStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 6)
120#define CPIA_COMMAND_EndStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 7)
121#define CPIA_COMMAND_SetFormat (OUTPUT | CPIA_MODULE_CAPTURE | 8)
122#define CPIA_COMMAND_SetROI (OUTPUT | CPIA_MODULE_CAPTURE | 9)
123#define CPIA_COMMAND_SetCompression (OUTPUT | CPIA_MODULE_CAPTURE | 10)
124#define CPIA_COMMAND_SetCompressionTarget (OUTPUT | CPIA_MODULE_CAPTURE | 11)
125#define CPIA_COMMAND_SetYUVThresh (OUTPUT | CPIA_MODULE_CAPTURE | 12)
126#define CPIA_COMMAND_SetCompressionParams (OUTPUT | CPIA_MODULE_CAPTURE | 13)
127#define CPIA_COMMAND_DiscardFrame (OUTPUT | CPIA_MODULE_CAPTURE | 14)
128#define CPIA_COMMAND_GrabReset (OUTPUT | CPIA_MODULE_CAPTURE | 15)
129
130#define CPIA_COMMAND_OutputRS232 (OUTPUT | CPIA_MODULE_DEBUG | 1)
131#define CPIA_COMMAND_AbortProcess (OUTPUT | CPIA_MODULE_DEBUG | 4)
132#define CPIA_COMMAND_SetDramPage (OUTPUT | CPIA_MODULE_DEBUG | 5)
133#define CPIA_COMMAND_StartDramUpload (OUTPUT | CPIA_MODULE_DEBUG | 6)
134#define CPIA_COMMAND_StartDummyDtream (OUTPUT | CPIA_MODULE_DEBUG | 8)
135#define CPIA_COMMAND_AbortStream (OUTPUT | CPIA_MODULE_DEBUG | 9)
136#define CPIA_COMMAND_DownloadDRAM (OUTPUT | CPIA_MODULE_DEBUG | 10)
137#define CPIA_COMMAND_Null (OUTPUT | CPIA_MODULE_DEBUG | 11)
138
139enum {
140 FRAME_READY, /* Ready to grab into */
141 FRAME_GRABBING, /* In the process of being grabbed into */
142 FRAME_DONE, /* Finished grabbing, but not been synced yet */
143 FRAME_UNUSED, /* Unused (no MCAPTURE) */
144};
145
146#define COMMAND_NONE 0x0000
147#define COMMAND_SETCOMPRESSION 0x0001
148#define COMMAND_SETCOMPRESSIONTARGET 0x0002
149#define COMMAND_SETCOLOURPARAMS 0x0004
150#define COMMAND_SETFORMAT 0x0008
151#define COMMAND_PAUSE 0x0010
152#define COMMAND_RESUME 0x0020
153#define COMMAND_SETYUVTHRESH 0x0040
154#define COMMAND_SETECPTIMING 0x0080
155#define COMMAND_SETCOMPRESSIONPARAMS 0x0100
156#define COMMAND_SETEXPOSURE 0x0200
157#define COMMAND_SETCOLOURBALANCE 0x0400
158#define COMMAND_SETSENSORFPS 0x0800
159#define COMMAND_SETAPCOR 0x1000
160#define COMMAND_SETFLICKERCTRL 0x2000
161#define COMMAND_SETVLOFFSET 0x4000
162#define COMMAND_SETLIGHTS 0x8000
163
164#define ROUND_UP_EXP_FOR_FLICKER 15
165
166/* Constants for automatic frame rate adjustment */
167#define MAX_EXP 302
168#define MAX_EXP_102 255
169#define LOW_EXP 140
170#define VERY_LOW_EXP 70
171#define TC 94
172#define EXP_ACC_DARK 50
173#define EXP_ACC_LIGHT 90
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300174#define HIGH_COMP_102 160
175#define MAX_COMP 239
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176#define DARK_TIME 3
177#define LIGHT_TIME 3
178
179/* Maximum number of 10ms loops to wait for the stream to become ready */
180#define READY_TIMEOUT 100
181
182/* Developer's Guide Table 5 p 3-34
183 * indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/
184static u8 flicker_jumps[2][2][4] =
185{ { { 76, 38, 19, 9 }, { 92, 46, 23, 11 } },
186 { { 64, 32, 16, 8 }, { 76, 38, 19, 9} }
187};
188
189/* forward declaration of local function */
190static void reset_camera_struct(struct cam_data *cam);
191static int find_over_exposure(int brightness);
192static void set_flicker(struct cam_params *params, volatile u32 *command_flags,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300193 int on);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194
195
196/**********************************************************************
197 *
198 * Memory management
199 *
200 **********************************************************************/
201static void *rvmalloc(unsigned long size)
202{
203 void *mem;
204 unsigned long adr;
205
206 size = PAGE_ALIGN(size);
207 mem = vmalloc_32(size);
208 if (!mem)
209 return NULL;
210
211 memset(mem, 0, size); /* Clear the ram out, no junk to the user */
212 adr = (unsigned long) mem;
213 while (size > 0) {
214 SetPageReserved(vmalloc_to_page((void *)adr));
215 adr += PAGE_SIZE;
216 size -= PAGE_SIZE;
217 }
218
219 return mem;
220}
221
222static void rvfree(void *mem, unsigned long size)
223{
224 unsigned long adr;
225
226 if (!mem)
227 return;
228
229 adr = (unsigned long) mem;
230 while ((long) size > 0) {
231 ClearPageReserved(vmalloc_to_page((void *)adr));
232 adr += PAGE_SIZE;
233 size -= PAGE_SIZE;
234 }
235 vfree(mem);
236}
237
238/**********************************************************************
239 *
240 * /proc interface
241 *
242 **********************************************************************/
243#ifdef CONFIG_PROC_FS
244static struct proc_dir_entry *cpia_proc_root=NULL;
245
246static int cpia_read_proc(char *page, char **start, off_t off,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300247 int count, int *eof, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248{
249 char *out = page;
250 int len, tmp;
251 struct cam_data *cam = data;
252 char tmpstr[29];
253
254 /* IMPORTANT: This output MUST be kept under PAGE_SIZE
255 * or we need to get more sophisticated. */
256
257 out += sprintf(out, "read-only\n-----------------------\n");
258 out += sprintf(out, "V4L Driver version: %d.%d.%d\n",
259 CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER);
260 out += sprintf(out, "CPIA Version: %d.%02d (%d.%d)\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300261 cam->params.version.firmwareVersion,
262 cam->params.version.firmwareRevision,
263 cam->params.version.vcVersion,
264 cam->params.version.vcRevision);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265 out += sprintf(out, "CPIA PnP-ID: %04x:%04x:%04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300266 cam->params.pnpID.vendor, cam->params.pnpID.product,
267 cam->params.pnpID.deviceRevision);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268 out += sprintf(out, "VP-Version: %d.%d %04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300269 cam->params.vpVersion.vpVersion,
270 cam->params.vpVersion.vpRevision,
271 cam->params.vpVersion.cameraHeadID);
272
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273 out += sprintf(out, "system_state: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300274 cam->params.status.systemState);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 out += sprintf(out, "grab_state: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300276 cam->params.status.grabState);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277 out += sprintf(out, "stream_state: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300278 cam->params.status.streamState);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 out += sprintf(out, "fatal_error: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300280 cam->params.status.fatalError);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 out += sprintf(out, "cmd_error: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300282 cam->params.status.cmdError);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283 out += sprintf(out, "debug_flags: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300284 cam->params.status.debugFlags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 out += sprintf(out, "vp_status: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300286 cam->params.status.vpStatus);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287 out += sprintf(out, "error_code: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300288 cam->params.status.errorCode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289 /* QX3 specific entries */
290 if (cam->params.qx3.qx3_detected) {
291 out += sprintf(out, "button: %4d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300292 cam->params.qx3.button);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293 out += sprintf(out, "cradled: %4d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300294 cam->params.qx3.cradled);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 }
296 out += sprintf(out, "video_size: %s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300297 cam->params.format.videoSize == VIDEOSIZE_CIF ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298 "CIF " : "QCIF");
299 out += sprintf(out, "roi: (%3d, %3d) to (%3d, %3d)\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300300 cam->params.roi.colStart*8,
301 cam->params.roi.rowStart*4,
302 cam->params.roi.colEnd*8,
303 cam->params.roi.rowEnd*4);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304 out += sprintf(out, "actual_fps: %3d\n", cam->fps);
305 out += sprintf(out, "transfer_rate: %4dkB/s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300306 cam->transfer_rate);
307
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308 out += sprintf(out, "\nread-write\n");
309 out += sprintf(out, "----------------------- current min"
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300310 " max default comment\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311 out += sprintf(out, "brightness: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300312 cam->params.colourParams.brightness, 0, 100, 50);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313 if (cam->params.version.firmwareVersion == 1 &&
314 cam->params.version.firmwareRevision == 2)
315 /* 1-02 firmware limits contrast to 80 */
316 tmp = 80;
317 else
318 tmp = 96;
319
320 out += sprintf(out, "contrast: %8d %8d %8d %8d"
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300321 " steps of 8\n",
322 cam->params.colourParams.contrast, 0, tmp, 48);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 out += sprintf(out, "saturation: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300324 cam->params.colourParams.saturation, 0, 100, 50);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 tmp = (25000+5000*cam->params.sensorFps.baserate)/
326 (1<<cam->params.sensorFps.divisor);
327 out += sprintf(out, "sensor_fps: %4d.%03d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300328 tmp/1000, tmp%1000, 3, 30, 15);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329 out += sprintf(out, "stream_start_line: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300330 2*cam->params.streamStartLine, 0,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331 cam->params.format.videoSize == VIDEOSIZE_CIF ? 288:144,
332 cam->params.format.videoSize == VIDEOSIZE_CIF ? 240:120);
333 out += sprintf(out, "sub_sample: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300334 cam->params.format.subSample == SUBSAMPLE_420 ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335 "420" : "422", "420", "422", "422");
336 out += sprintf(out, "yuv_order: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300337 cam->params.format.yuvOrder == YUVORDER_YUYV ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 "YUYV" : "UYVY", "YUYV" , "UYVY", "YUYV");
339 out += sprintf(out, "ecp_timing: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300340 cam->params.ecpTiming ? "slow" : "normal", "slow",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 "normal", "normal");
342
343 if (cam->params.colourBalance.balanceMode == 2) {
344 sprintf(tmpstr, "auto");
345 } else {
346 sprintf(tmpstr, "manual");
347 }
348 out += sprintf(out, "color_balance_mode: %8s %8s %8s"
349 " %8s\n", tmpstr, "manual", "auto", "auto");
350 out += sprintf(out, "red_gain: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300351 cam->params.colourBalance.redGain, 0, 212, 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352 out += sprintf(out, "green_gain: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300353 cam->params.colourBalance.greenGain, 0, 212, 6);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354 out += sprintf(out, "blue_gain: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300355 cam->params.colourBalance.blueGain, 0, 212, 92);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356
357 if (cam->params.version.firmwareVersion == 1 &&
358 cam->params.version.firmwareRevision == 2)
359 /* 1-02 firmware limits gain to 2 */
360 sprintf(tmpstr, "%8d %8d %8d", 1, 2, 2);
361 else
362 sprintf(tmpstr, "%8d %8d %8d", 1, 8, 2);
363
364 if (cam->params.exposure.gainMode == 0)
365 out += sprintf(out, "max_gain: unknown %28s"
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300366 " powers of 2\n", tmpstr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367 else
368 out += sprintf(out, "max_gain: %8d %28s"
369 " 1,2,4 or 8 \n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300370 1<<(cam->params.exposure.gainMode-1), tmpstr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371
372 switch(cam->params.exposure.expMode) {
373 case 1:
374 case 3:
375 sprintf(tmpstr, "manual");
376 break;
377 case 2:
378 sprintf(tmpstr, "auto");
379 break;
380 default:
381 sprintf(tmpstr, "unknown");
382 break;
383 }
384 out += sprintf(out, "exposure_mode: %8s %8s %8s"
385 " %8s\n", tmpstr, "manual", "auto", "auto");
386 out += sprintf(out, "centre_weight: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300387 (2-cam->params.exposure.centreWeight) ? "on" : "off",
388 "off", "on", "on");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389 out += sprintf(out, "gain: %8d %8d max_gain %8d 1,2,4,8 possible\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300390 1<<cam->params.exposure.gain, 1, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391 if (cam->params.version.firmwareVersion == 1 &&
392 cam->params.version.firmwareRevision == 2)
393 /* 1-02 firmware limits fineExp/2 to 127 */
394 tmp = 254;
395 else
396 tmp = 510;
397
398 out += sprintf(out, "fine_exp: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300399 cam->params.exposure.fineExp*2, 0, tmp, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400 if (cam->params.version.firmwareVersion == 1 &&
401 cam->params.version.firmwareRevision == 2)
402 /* 1-02 firmware limits coarseExpHi to 0 */
403 tmp = MAX_EXP_102;
404 else
405 tmp = MAX_EXP;
406
407 out += sprintf(out, "coarse_exp: %8d %8d %8d"
408 " %8d\n", cam->params.exposure.coarseExpLo+
409 256*cam->params.exposure.coarseExpHi, 0, tmp, 185);
410 out += sprintf(out, "red_comp: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300411 cam->params.exposure.redComp, COMP_RED, 255, COMP_RED);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412 out += sprintf(out, "green1_comp: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300413 cam->params.exposure.green1Comp, COMP_GREEN1, 255,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 COMP_GREEN1);
415 out += sprintf(out, "green2_comp: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300416 cam->params.exposure.green2Comp, COMP_GREEN2, 255,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417 COMP_GREEN2);
418 out += sprintf(out, "blue_comp: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300419 cam->params.exposure.blueComp, COMP_BLUE, 255, COMP_BLUE);
420
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421 out += sprintf(out, "apcor_gain1: %#8x %#8x %#8x %#8x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300422 cam->params.apcor.gain1, 0, 0xff, 0x1c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423 out += sprintf(out, "apcor_gain2: %#8x %#8x %#8x %#8x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300424 cam->params.apcor.gain2, 0, 0xff, 0x1a);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425 out += sprintf(out, "apcor_gain4: %#8x %#8x %#8x %#8x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300426 cam->params.apcor.gain4, 0, 0xff, 0x2d);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427 out += sprintf(out, "apcor_gain8: %#8x %#8x %#8x %#8x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300428 cam->params.apcor.gain8, 0, 0xff, 0x2a);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429 out += sprintf(out, "vl_offset_gain1: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300430 cam->params.vlOffset.gain1, 0, 255, 24);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431 out += sprintf(out, "vl_offset_gain2: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300432 cam->params.vlOffset.gain2, 0, 255, 28);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433 out += sprintf(out, "vl_offset_gain4: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300434 cam->params.vlOffset.gain4, 0, 255, 30);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435 out += sprintf(out, "vl_offset_gain8: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300436 cam->params.vlOffset.gain8, 0, 255, 30);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437 out += sprintf(out, "flicker_control: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300438 cam->params.flickerControl.flickerMode ? "on" : "off",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439 "off", "on", "off");
440 out += sprintf(out, "mains_frequency: %8d %8d %8d %8d"
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300441 " only 50/60\n",
442 cam->mainsFreq ? 60 : 50, 50, 60, 50);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443 if(cam->params.flickerControl.allowableOverExposure < 0)
444 out += sprintf(out, "allowable_overexposure: %4dauto auto %8d auto\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300445 -cam->params.flickerControl.allowableOverExposure,
446 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447 else
448 out += sprintf(out, "allowable_overexposure: %8d auto %8d auto\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300449 cam->params.flickerControl.allowableOverExposure,
450 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451 out += sprintf(out, "compression_mode: ");
452 switch(cam->params.compression.mode) {
453 case CPIA_COMPRESSION_NONE:
454 out += sprintf(out, "%8s", "none");
455 break;
456 case CPIA_COMPRESSION_AUTO:
457 out += sprintf(out, "%8s", "auto");
458 break;
459 case CPIA_COMPRESSION_MANUAL:
460 out += sprintf(out, "%8s", "manual");
461 break;
462 default:
463 out += sprintf(out, "%8s", "unknown");
464 break;
465 }
466 out += sprintf(out, " none,auto,manual auto\n");
467 out += sprintf(out, "decimation_enable: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300468 cam->params.compression.decimation ==
469 DECIMATION_ENAB ? "on":"off", "off", "on",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470 "off");
471 out += sprintf(out, "compression_target: %9s %9s %9s %9s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300472 cam->params.compressionTarget.frTargeting ==
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473 CPIA_COMPRESSION_TARGET_FRAMERATE ?
474 "framerate":"quality",
475 "framerate", "quality", "quality");
476 out += sprintf(out, "target_framerate: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300477 cam->params.compressionTarget.targetFR, 1, 30, 15);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478 out += sprintf(out, "target_quality: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300479 cam->params.compressionTarget.targetQ, 1, 64, 5);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480 out += sprintf(out, "y_threshold: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300481 cam->params.yuvThreshold.yThreshold, 0, 31, 6);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482 out += sprintf(out, "uv_threshold: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300483 cam->params.yuvThreshold.uvThreshold, 0, 31, 6);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 out += sprintf(out, "hysteresis: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300485 cam->params.compressionParams.hysteresis, 0, 255, 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486 out += sprintf(out, "threshold_max: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300487 cam->params.compressionParams.threshMax, 0, 255, 11);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488 out += sprintf(out, "small_step: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300489 cam->params.compressionParams.smallStep, 0, 255, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490 out += sprintf(out, "large_step: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300491 cam->params.compressionParams.largeStep, 0, 255, 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492 out += sprintf(out, "decimation_hysteresis: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300493 cam->params.compressionParams.decimationHysteresis,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494 0, 255, 2);
495 out += sprintf(out, "fr_diff_step_thresh: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300496 cam->params.compressionParams.frDiffStepThresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497 0, 255, 5);
498 out += sprintf(out, "q_diff_step_thresh: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300499 cam->params.compressionParams.qDiffStepThresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 0, 255, 3);
501 out += sprintf(out, "decimation_thresh_mod: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300502 cam->params.compressionParams.decimationThreshMod,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503 0, 255, 2);
504 /* QX3 specific entries */
505 if (cam->params.qx3.qx3_detected) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300506 out += sprintf(out, "toplight: %8s %8s %8s %8s\n",
507 cam->params.qx3.toplight ? "on" : "off",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508 "off", "on", "off");
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300509 out += sprintf(out, "bottomlight: %8s %8s %8s %8s\n",
510 cam->params.qx3.bottomlight ? "on" : "off",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 "off", "on", "off");
512 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300513
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514 len = out - page;
515 len -= off;
516 if (len < count) {
517 *eof = 1;
518 if (len <= 0) return 0;
519 } else
520 len = count;
521
522 *start = page + off;
523 return len;
524}
525
526
527static int match(char *checkstr, char **buffer, unsigned long *count,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300528 int *find_colon, int *err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529{
530 int ret, colon_found = 1;
531 int len = strlen(checkstr);
532 ret = (len <= *count && strncmp(*buffer, checkstr, len) == 0);
533 if (ret) {
534 *buffer += len;
535 *count -= len;
536 if (*find_colon) {
537 colon_found = 0;
538 while (*count && (**buffer == ' ' || **buffer == '\t' ||
539 (!colon_found && **buffer == ':'))) {
540 if (**buffer == ':')
541 colon_found = 1;
542 --*count;
543 ++*buffer;
544 }
545 if (!*count || !colon_found)
546 *err = -EINVAL;
547 *find_colon = 0;
548 }
549 }
550 return ret;
551}
552
553static unsigned long int value(char **buffer, unsigned long *count, int *err)
554{
555 char *p;
556 unsigned long int ret;
557 ret = simple_strtoul(*buffer, &p, 0);
558 if (p == *buffer)
559 *err = -EINVAL;
560 else {
561 *count -= p - *buffer;
562 *buffer = p;
563 }
564 return ret;
565}
566
567static int cpia_write_proc(struct file *file, const char __user *buf,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300568 unsigned long count, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569{
570 struct cam_data *cam = data;
571 struct cam_params new_params;
572 char *page, *buffer;
573 int retval, find_colon;
574 int size = count;
575 unsigned long val = 0;
576 u32 command_flags = 0;
577 u8 new_mains;
578
579 /*
580 * This code to copy from buf to page is shamelessly copied
581 * from the comx driver
582 */
583 if (count > PAGE_SIZE) {
584 printk(KERN_ERR "count is %lu > %d!!!\n", count, (int)PAGE_SIZE);
585 return -ENOSPC;
586 }
587
588 if (!(page = (char *)__get_free_page(GFP_KERNEL))) return -ENOMEM;
589
590 if(copy_from_user(page, buf, count))
591 {
592 retval = -EFAULT;
593 goto out;
594 }
595
596 if (page[count-1] == '\n')
597 page[count-1] = '\0';
598 else if (count < PAGE_SIZE)
599 page[count] = '\0';
600 else if (page[count]) {
601 retval = -EINVAL;
602 goto out;
603 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300604
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605 buffer = page;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300606
Ingo Molnar3593cab2006-02-07 06:49:14 -0200607 if (mutex_lock_interruptible(&cam->param_lock))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608 return -ERESTARTSYS;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300609
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610 /*
611 * Skip over leading whitespace
612 */
613 while (count && isspace(*buffer)) {
614 --count;
615 ++buffer;
616 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300617
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618 memcpy(&new_params, &cam->params, sizeof(struct cam_params));
619 new_mains = cam->mainsFreq;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300620
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621#define MATCH(x) (match(x, &buffer, &count, &find_colon, &retval))
622#define VALUE (value(&buffer,&count, &retval))
623#define FIRMWARE_VERSION(x,y) (new_params.version.firmwareVersion == (x) && \
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300624 new_params.version.firmwareRevision == (y))
625
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626 retval = 0;
627 while (count && !retval) {
628 find_colon = 1;
629 if (MATCH("brightness")) {
630 if (!retval)
631 val = VALUE;
632
633 if (!retval) {
634 if (val <= 100)
635 new_params.colourParams.brightness = val;
636 else
637 retval = -EINVAL;
638 }
639 command_flags |= COMMAND_SETCOLOURPARAMS;
640 if(new_params.flickerControl.allowableOverExposure < 0)
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300641 new_params.flickerControl.allowableOverExposure =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642 -find_over_exposure(new_params.colourParams.brightness);
643 if(new_params.flickerControl.flickerMode != 0)
644 command_flags |= COMMAND_SETFLICKERCTRL;
645
646 } else if (MATCH("contrast")) {
647 if (!retval)
648 val = VALUE;
649
650 if (!retval) {
651 if (val <= 100) {
652 /* contrast is in steps of 8, so round*/
653 val = ((val + 3) / 8) * 8;
654 /* 1-02 firmware limits contrast to 80*/
655 if (FIRMWARE_VERSION(1,2) && val > 80)
656 val = 80;
657
658 new_params.colourParams.contrast = val;
659 } else
660 retval = -EINVAL;
661 }
662 command_flags |= COMMAND_SETCOLOURPARAMS;
663 } else if (MATCH("saturation")) {
664 if (!retval)
665 val = VALUE;
666
667 if (!retval) {
668 if (val <= 100)
669 new_params.colourParams.saturation = val;
670 else
671 retval = -EINVAL;
672 }
673 command_flags |= COMMAND_SETCOLOURPARAMS;
674 } else if (MATCH("sensor_fps")) {
675 if (!retval)
676 val = VALUE;
677
678 if (!retval) {
679 /* find values so that sensorFPS is minimized,
680 * but >= val */
681 if (val > 30)
682 retval = -EINVAL;
683 else if (val > 25) {
684 new_params.sensorFps.divisor = 0;
685 new_params.sensorFps.baserate = 1;
686 } else if (val > 15) {
687 new_params.sensorFps.divisor = 0;
688 new_params.sensorFps.baserate = 0;
689 } else if (val > 12) {
690 new_params.sensorFps.divisor = 1;
691 new_params.sensorFps.baserate = 1;
692 } else if (val > 7) {
693 new_params.sensorFps.divisor = 1;
694 new_params.sensorFps.baserate = 0;
695 } else if (val > 6) {
696 new_params.sensorFps.divisor = 2;
697 new_params.sensorFps.baserate = 1;
698 } else if (val > 3) {
699 new_params.sensorFps.divisor = 2;
700 new_params.sensorFps.baserate = 0;
701 } else {
702 new_params.sensorFps.divisor = 3;
703 /* Either base rate would work here */
704 new_params.sensorFps.baserate = 1;
705 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300706 new_params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707 flicker_jumps[new_mains]
708 [new_params.sensorFps.baserate]
709 [new_params.sensorFps.divisor];
710 if (new_params.flickerControl.flickerMode)
711 command_flags |= COMMAND_SETFLICKERCTRL;
712 }
713 command_flags |= COMMAND_SETSENSORFPS;
714 cam->exposure_status = EXPOSURE_NORMAL;
715 } else if (MATCH("stream_start_line")) {
716 if (!retval)
717 val = VALUE;
718
719 if (!retval) {
720 int max_line = 288;
721
722 if (new_params.format.videoSize == VIDEOSIZE_QCIF)
723 max_line = 144;
724 if (val <= max_line)
725 new_params.streamStartLine = val/2;
726 else
727 retval = -EINVAL;
728 }
729 } else if (MATCH("sub_sample")) {
730 if (!retval && MATCH("420"))
731 new_params.format.subSample = SUBSAMPLE_420;
732 else if (!retval && MATCH("422"))
733 new_params.format.subSample = SUBSAMPLE_422;
734 else
735 retval = -EINVAL;
736
737 command_flags |= COMMAND_SETFORMAT;
738 } else if (MATCH("yuv_order")) {
739 if (!retval && MATCH("YUYV"))
740 new_params.format.yuvOrder = YUVORDER_YUYV;
741 else if (!retval && MATCH("UYVY"))
742 new_params.format.yuvOrder = YUVORDER_UYVY;
743 else
744 retval = -EINVAL;
745
746 command_flags |= COMMAND_SETFORMAT;
747 } else if (MATCH("ecp_timing")) {
748 if (!retval && MATCH("normal"))
749 new_params.ecpTiming = 0;
750 else if (!retval && MATCH("slow"))
751 new_params.ecpTiming = 1;
752 else
753 retval = -EINVAL;
754
755 command_flags |= COMMAND_SETECPTIMING;
756 } else if (MATCH("color_balance_mode")) {
757 if (!retval && MATCH("manual"))
758 new_params.colourBalance.balanceMode = 3;
759 else if (!retval && MATCH("auto"))
760 new_params.colourBalance.balanceMode = 2;
761 else
762 retval = -EINVAL;
763
764 command_flags |= COMMAND_SETCOLOURBALANCE;
765 } else if (MATCH("red_gain")) {
766 if (!retval)
767 val = VALUE;
768
769 if (!retval) {
770 if (val <= 212) {
771 new_params.colourBalance.redGain = val;
772 new_params.colourBalance.balanceMode = 1;
773 } else
774 retval = -EINVAL;
775 }
776 command_flags |= COMMAND_SETCOLOURBALANCE;
777 } else if (MATCH("green_gain")) {
778 if (!retval)
779 val = VALUE;
780
781 if (!retval) {
782 if (val <= 212) {
783 new_params.colourBalance.greenGain = val;
784 new_params.colourBalance.balanceMode = 1;
785 } else
786 retval = -EINVAL;
787 }
788 command_flags |= COMMAND_SETCOLOURBALANCE;
789 } else if (MATCH("blue_gain")) {
790 if (!retval)
791 val = VALUE;
792
793 if (!retval) {
794 if (val <= 212) {
795 new_params.colourBalance.blueGain = val;
796 new_params.colourBalance.balanceMode = 1;
797 } else
798 retval = -EINVAL;
799 }
800 command_flags |= COMMAND_SETCOLOURBALANCE;
801 } else if (MATCH("max_gain")) {
802 if (!retval)
803 val = VALUE;
804
805 if (!retval) {
806 /* 1-02 firmware limits gain to 2 */
807 if (FIRMWARE_VERSION(1,2) && val > 2)
808 val = 2;
809 switch(val) {
810 case 1:
811 new_params.exposure.gainMode = 1;
812 break;
813 case 2:
814 new_params.exposure.gainMode = 2;
815 break;
816 case 4:
817 new_params.exposure.gainMode = 3;
818 break;
819 case 8:
820 new_params.exposure.gainMode = 4;
821 break;
822 default:
823 retval = -EINVAL;
824 break;
825 }
826 }
827 command_flags |= COMMAND_SETEXPOSURE;
828 } else if (MATCH("exposure_mode")) {
829 if (!retval && MATCH("auto"))
830 new_params.exposure.expMode = 2;
831 else if (!retval && MATCH("manual")) {
832 if (new_params.exposure.expMode == 2)
833 new_params.exposure.expMode = 3;
834 if(new_params.flickerControl.flickerMode != 0)
835 command_flags |= COMMAND_SETFLICKERCTRL;
836 new_params.flickerControl.flickerMode = 0;
837 } else
838 retval = -EINVAL;
839
840 command_flags |= COMMAND_SETEXPOSURE;
841 } else if (MATCH("centre_weight")) {
842 if (!retval && MATCH("on"))
843 new_params.exposure.centreWeight = 1;
844 else if (!retval && MATCH("off"))
845 new_params.exposure.centreWeight = 2;
846 else
847 retval = -EINVAL;
848
849 command_flags |= COMMAND_SETEXPOSURE;
850 } else if (MATCH("gain")) {
851 if (!retval)
852 val = VALUE;
853
854 if (!retval) {
855 switch(val) {
856 case 1:
857 new_params.exposure.gain = 0;
858 break;
859 case 2:
860 new_params.exposure.gain = 1;
861 break;
862 case 4:
863 new_params.exposure.gain = 2;
864 break;
865 case 8:
866 new_params.exposure.gain = 3;
867 break;
868 default:
869 retval = -EINVAL;
870 break;
871 }
872 new_params.exposure.expMode = 1;
873 if(new_params.flickerControl.flickerMode != 0)
874 command_flags |= COMMAND_SETFLICKERCTRL;
875 new_params.flickerControl.flickerMode = 0;
876 command_flags |= COMMAND_SETEXPOSURE;
877 if (new_params.exposure.gain >
878 new_params.exposure.gainMode-1)
879 retval = -EINVAL;
880 }
881 } else if (MATCH("fine_exp")) {
882 if (!retval)
883 val = VALUE/2;
884
885 if (!retval) {
886 if (val < 256) {
887 /* 1-02 firmware limits fineExp/2 to 127*/
888 if (FIRMWARE_VERSION(1,2) && val > 127)
889 val = 127;
890 new_params.exposure.fineExp = val;
891 new_params.exposure.expMode = 1;
892 command_flags |= COMMAND_SETEXPOSURE;
893 if(new_params.flickerControl.flickerMode != 0)
894 command_flags |= COMMAND_SETFLICKERCTRL;
895 new_params.flickerControl.flickerMode = 0;
896 command_flags |= COMMAND_SETFLICKERCTRL;
897 } else
898 retval = -EINVAL;
899 }
900 } else if (MATCH("coarse_exp")) {
901 if (!retval)
902 val = VALUE;
903
904 if (!retval) {
905 if (val <= MAX_EXP) {
906 if (FIRMWARE_VERSION(1,2) &&
907 val > MAX_EXP_102)
908 val = MAX_EXP_102;
909 new_params.exposure.coarseExpLo =
910 val & 0xff;
911 new_params.exposure.coarseExpHi =
912 val >> 8;
913 new_params.exposure.expMode = 1;
914 command_flags |= COMMAND_SETEXPOSURE;
915 if(new_params.flickerControl.flickerMode != 0)
916 command_flags |= COMMAND_SETFLICKERCTRL;
917 new_params.flickerControl.flickerMode = 0;
918 command_flags |= COMMAND_SETFLICKERCTRL;
919 } else
920 retval = -EINVAL;
921 }
922 } else if (MATCH("red_comp")) {
923 if (!retval)
924 val = VALUE;
925
926 if (!retval) {
927 if (val >= COMP_RED && val <= 255) {
928 new_params.exposure.redComp = val;
929 new_params.exposure.compMode = 1;
930 command_flags |= COMMAND_SETEXPOSURE;
931 } else
932 retval = -EINVAL;
933 }
934 } else if (MATCH("green1_comp")) {
935 if (!retval)
936 val = VALUE;
937
938 if (!retval) {
939 if (val >= COMP_GREEN1 && val <= 255) {
940 new_params.exposure.green1Comp = val;
941 new_params.exposure.compMode = 1;
942 command_flags |= COMMAND_SETEXPOSURE;
943 } else
944 retval = -EINVAL;
945 }
946 } else if (MATCH("green2_comp")) {
947 if (!retval)
948 val = VALUE;
949
950 if (!retval) {
951 if (val >= COMP_GREEN2 && val <= 255) {
952 new_params.exposure.green2Comp = val;
953 new_params.exposure.compMode = 1;
954 command_flags |= COMMAND_SETEXPOSURE;
955 } else
956 retval = -EINVAL;
957 }
958 } else if (MATCH("blue_comp")) {
959 if (!retval)
960 val = VALUE;
961
962 if (!retval) {
963 if (val >= COMP_BLUE && val <= 255) {
964 new_params.exposure.blueComp = val;
965 new_params.exposure.compMode = 1;
966 command_flags |= COMMAND_SETEXPOSURE;
967 } else
968 retval = -EINVAL;
969 }
970 } else if (MATCH("apcor_gain1")) {
971 if (!retval)
972 val = VALUE;
973
974 if (!retval) {
975 command_flags |= COMMAND_SETAPCOR;
976 if (val <= 0xff)
977 new_params.apcor.gain1 = val;
978 else
979 retval = -EINVAL;
980 }
981 } else if (MATCH("apcor_gain2")) {
982 if (!retval)
983 val = VALUE;
984
985 if (!retval) {
986 command_flags |= COMMAND_SETAPCOR;
987 if (val <= 0xff)
988 new_params.apcor.gain2 = val;
989 else
990 retval = -EINVAL;
991 }
992 } else if (MATCH("apcor_gain4")) {
993 if (!retval)
994 val = VALUE;
995
996 if (!retval) {
997 command_flags |= COMMAND_SETAPCOR;
998 if (val <= 0xff)
999 new_params.apcor.gain4 = val;
1000 else
1001 retval = -EINVAL;
1002 }
1003 } else if (MATCH("apcor_gain8")) {
1004 if (!retval)
1005 val = VALUE;
1006
1007 if (!retval) {
1008 command_flags |= COMMAND_SETAPCOR;
1009 if (val <= 0xff)
1010 new_params.apcor.gain8 = val;
1011 else
1012 retval = -EINVAL;
1013 }
1014 } else if (MATCH("vl_offset_gain1")) {
1015 if (!retval)
1016 val = VALUE;
1017
1018 if (!retval) {
1019 if (val <= 0xff)
1020 new_params.vlOffset.gain1 = val;
1021 else
1022 retval = -EINVAL;
1023 }
1024 command_flags |= COMMAND_SETVLOFFSET;
1025 } else if (MATCH("vl_offset_gain2")) {
1026 if (!retval)
1027 val = VALUE;
1028
1029 if (!retval) {
1030 if (val <= 0xff)
1031 new_params.vlOffset.gain2 = val;
1032 else
1033 retval = -EINVAL;
1034 }
1035 command_flags |= COMMAND_SETVLOFFSET;
1036 } else if (MATCH("vl_offset_gain4")) {
1037 if (!retval)
1038 val = VALUE;
1039
1040 if (!retval) {
1041 if (val <= 0xff)
1042 new_params.vlOffset.gain4 = val;
1043 else
1044 retval = -EINVAL;
1045 }
1046 command_flags |= COMMAND_SETVLOFFSET;
1047 } else if (MATCH("vl_offset_gain8")) {
1048 if (!retval)
1049 val = VALUE;
1050
1051 if (!retval) {
1052 if (val <= 0xff)
1053 new_params.vlOffset.gain8 = val;
1054 else
1055 retval = -EINVAL;
1056 }
1057 command_flags |= COMMAND_SETVLOFFSET;
1058 } else if (MATCH("flicker_control")) {
1059 if (!retval && MATCH("on")) {
1060 set_flicker(&new_params, &command_flags, 1);
1061 } else if (!retval && MATCH("off")) {
1062 set_flicker(&new_params, &command_flags, 0);
1063 } else
1064 retval = -EINVAL;
1065
1066 command_flags |= COMMAND_SETFLICKERCTRL;
1067 } else if (MATCH("mains_frequency")) {
1068 if (!retval && MATCH("50")) {
1069 new_mains = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001070 new_params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071 flicker_jumps[new_mains]
1072 [new_params.sensorFps.baserate]
1073 [new_params.sensorFps.divisor];
1074 if (new_params.flickerControl.flickerMode)
1075 command_flags |= COMMAND_SETFLICKERCTRL;
1076 } else if (!retval && MATCH("60")) {
1077 new_mains = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001078 new_params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079 flicker_jumps[new_mains]
1080 [new_params.sensorFps.baserate]
1081 [new_params.sensorFps.divisor];
1082 if (new_params.flickerControl.flickerMode)
1083 command_flags |= COMMAND_SETFLICKERCTRL;
1084 } else
1085 retval = -EINVAL;
1086 } else if (MATCH("allowable_overexposure")) {
1087 if (!retval && MATCH("auto")) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001088 new_params.flickerControl.allowableOverExposure =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089 -find_over_exposure(new_params.colourParams.brightness);
1090 if(new_params.flickerControl.flickerMode != 0)
1091 command_flags |= COMMAND_SETFLICKERCTRL;
1092 } else {
1093 if (!retval)
1094 val = VALUE;
1095
1096 if (!retval) {
1097 if (val <= 0xff) {
1098 new_params.flickerControl.
1099 allowableOverExposure = val;
1100 if(new_params.flickerControl.flickerMode != 0)
1101 command_flags |= COMMAND_SETFLICKERCTRL;
1102 } else
1103 retval = -EINVAL;
1104 }
1105 }
1106 } else if (MATCH("compression_mode")) {
1107 if (!retval && MATCH("none"))
1108 new_params.compression.mode =
1109 CPIA_COMPRESSION_NONE;
1110 else if (!retval && MATCH("auto"))
1111 new_params.compression.mode =
1112 CPIA_COMPRESSION_AUTO;
1113 else if (!retval && MATCH("manual"))
1114 new_params.compression.mode =
1115 CPIA_COMPRESSION_MANUAL;
1116 else
1117 retval = -EINVAL;
1118
1119 command_flags |= COMMAND_SETCOMPRESSION;
1120 } else if (MATCH("decimation_enable")) {
1121 if (!retval && MATCH("off"))
1122 new_params.compression.decimation = 0;
1123 else if (!retval && MATCH("on"))
1124 new_params.compression.decimation = 1;
1125 else
1126 retval = -EINVAL;
1127
1128 command_flags |= COMMAND_SETCOMPRESSION;
1129 } else if (MATCH("compression_target")) {
1130 if (!retval && MATCH("quality"))
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001131 new_params.compressionTarget.frTargeting =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132 CPIA_COMPRESSION_TARGET_QUALITY;
1133 else if (!retval && MATCH("framerate"))
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001134 new_params.compressionTarget.frTargeting =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135 CPIA_COMPRESSION_TARGET_FRAMERATE;
1136 else
1137 retval = -EINVAL;
1138
1139 command_flags |= COMMAND_SETCOMPRESSIONTARGET;
1140 } else if (MATCH("target_framerate")) {
1141 if (!retval)
1142 val = VALUE;
1143
1144 if (!retval) {
1145 if(val > 0 && val <= 30)
1146 new_params.compressionTarget.targetFR = val;
1147 else
1148 retval = -EINVAL;
1149 }
1150 command_flags |= COMMAND_SETCOMPRESSIONTARGET;
1151 } else if (MATCH("target_quality")) {
1152 if (!retval)
1153 val = VALUE;
1154
1155 if (!retval) {
1156 if(val > 0 && val <= 64)
1157 new_params.compressionTarget.targetQ = val;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001158 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159 retval = -EINVAL;
1160 }
1161 command_flags |= COMMAND_SETCOMPRESSIONTARGET;
1162 } else if (MATCH("y_threshold")) {
1163 if (!retval)
1164 val = VALUE;
1165
1166 if (!retval) {
1167 if (val < 32)
1168 new_params.yuvThreshold.yThreshold = val;
1169 else
1170 retval = -EINVAL;
1171 }
1172 command_flags |= COMMAND_SETYUVTHRESH;
1173 } else if (MATCH("uv_threshold")) {
1174 if (!retval)
1175 val = VALUE;
1176
1177 if (!retval) {
1178 if (val < 32)
1179 new_params.yuvThreshold.uvThreshold = val;
1180 else
1181 retval = -EINVAL;
1182 }
1183 command_flags |= COMMAND_SETYUVTHRESH;
1184 } else if (MATCH("hysteresis")) {
1185 if (!retval)
1186 val = VALUE;
1187
1188 if (!retval) {
1189 if (val <= 0xff)
1190 new_params.compressionParams.hysteresis = val;
1191 else
1192 retval = -EINVAL;
1193 }
1194 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1195 } else if (MATCH("threshold_max")) {
1196 if (!retval)
1197 val = VALUE;
1198
1199 if (!retval) {
1200 if (val <= 0xff)
1201 new_params.compressionParams.threshMax = val;
1202 else
1203 retval = -EINVAL;
1204 }
1205 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1206 } else if (MATCH("small_step")) {
1207 if (!retval)
1208 val = VALUE;
1209
1210 if (!retval) {
1211 if (val <= 0xff)
1212 new_params.compressionParams.smallStep = val;
1213 else
1214 retval = -EINVAL;
1215 }
1216 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1217 } else if (MATCH("large_step")) {
1218 if (!retval)
1219 val = VALUE;
1220
1221 if (!retval) {
1222 if (val <= 0xff)
1223 new_params.compressionParams.largeStep = val;
1224 else
1225 retval = -EINVAL;
1226 }
1227 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1228 } else if (MATCH("decimation_hysteresis")) {
1229 if (!retval)
1230 val = VALUE;
1231
1232 if (!retval) {
1233 if (val <= 0xff)
1234 new_params.compressionParams.decimationHysteresis = val;
1235 else
1236 retval = -EINVAL;
1237 }
1238 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1239 } else if (MATCH("fr_diff_step_thresh")) {
1240 if (!retval)
1241 val = VALUE;
1242
1243 if (!retval) {
1244 if (val <= 0xff)
1245 new_params.compressionParams.frDiffStepThresh = val;
1246 else
1247 retval = -EINVAL;
1248 }
1249 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1250 } else if (MATCH("q_diff_step_thresh")) {
1251 if (!retval)
1252 val = VALUE;
1253
1254 if (!retval) {
1255 if (val <= 0xff)
1256 new_params.compressionParams.qDiffStepThresh = val;
1257 else
1258 retval = -EINVAL;
1259 }
1260 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1261 } else if (MATCH("decimation_thresh_mod")) {
1262 if (!retval)
1263 val = VALUE;
1264
1265 if (!retval) {
1266 if (val <= 0xff)
1267 new_params.compressionParams.decimationThreshMod = val;
1268 else
1269 retval = -EINVAL;
1270 }
1271 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1272 } else if (MATCH("toplight")) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001273 if (!retval && MATCH("on"))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001274 new_params.qx3.toplight = 1;
1275 else if (!retval && MATCH("off"))
1276 new_params.qx3.toplight = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001277 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001278 retval = -EINVAL;
1279 command_flags |= COMMAND_SETLIGHTS;
1280 } else if (MATCH("bottomlight")) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001281 if (!retval && MATCH("on"))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282 new_params.qx3.bottomlight = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001283 else if (!retval && MATCH("off"))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001284 new_params.qx3.bottomlight = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001285 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286 retval = -EINVAL;
1287 command_flags |= COMMAND_SETLIGHTS;
1288 } else {
1289 DBG("No match found\n");
1290 retval = -EINVAL;
1291 }
1292
1293 if (!retval) {
1294 while (count && isspace(*buffer) && *buffer != '\n') {
1295 --count;
1296 ++buffer;
1297 }
1298 if (count) {
1299 if (*buffer == '\0' && count != 1)
1300 retval = -EINVAL;
1301 else if (*buffer != '\n' && *buffer != ';' &&
1302 *buffer != '\0')
1303 retval = -EINVAL;
1304 else {
1305 --count;
1306 ++buffer;
1307 }
1308 }
1309 }
1310 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001311#undef MATCH
Linus Torvalds1da177e2005-04-16 15:20:36 -07001312#undef VALUE
1313#undef FIRMWARE_VERSION
1314 if (!retval) {
1315 if (command_flags & COMMAND_SETCOLOURPARAMS) {
1316 /* Adjust cam->vp to reflect these changes */
1317 cam->vp.brightness =
1318 new_params.colourParams.brightness*65535/100;
1319 cam->vp.contrast =
1320 new_params.colourParams.contrast*65535/100;
1321 cam->vp.colour =
1322 new_params.colourParams.saturation*65535/100;
1323 }
1324 if((command_flags & COMMAND_SETEXPOSURE) &&
1325 new_params.exposure.expMode == 2)
1326 cam->exposure_status = EXPOSURE_NORMAL;
1327
1328 memcpy(&cam->params, &new_params, sizeof(struct cam_params));
1329 cam->mainsFreq = new_mains;
1330 cam->cmd_queue |= command_flags;
1331 retval = size;
1332 } else
1333 DBG("error: %d\n", retval);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001334
Ingo Molnar3593cab2006-02-07 06:49:14 -02001335 mutex_unlock(&cam->param_lock);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001336
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337out:
1338 free_page((unsigned long)page);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001339 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340}
1341
1342static void create_proc_cpia_cam(struct cam_data *cam)
1343{
Alexey Dobriyan13071f02007-01-14 15:29:42 -03001344 char name[5 + 1 + 10 + 1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001345 struct proc_dir_entry *ent;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001346
Linus Torvalds1da177e2005-04-16 15:20:36 -07001347 if (!cpia_proc_root || !cam)
1348 return;
1349
Hans Verkuilc6330fb2008-10-19 18:54:26 -03001350 snprintf(name, sizeof(name), "video%d", cam->vdev.num);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001351
Linus Torvalds1da177e2005-04-16 15:20:36 -07001352 ent = create_proc_entry(name, S_IFREG|S_IRUGO|S_IWUSR, cpia_proc_root);
1353 if (!ent)
1354 return;
1355
1356 ent->data = cam;
1357 ent->read_proc = cpia_read_proc;
1358 ent->write_proc = cpia_write_proc;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001359 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001360 size of the proc entry is 3736 bytes for the standard webcam;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001361 the extra features of the QX3 microscope add 189 bytes.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001362 (we have not yet probed the camera to see which type it is).
1363 */
1364 ent->size = 3736 + 189;
1365 cam->proc_entry = ent;
1366}
1367
1368static void destroy_proc_cpia_cam(struct cam_data *cam)
1369{
Alexey Dobriyan13071f02007-01-14 15:29:42 -03001370 char name[5 + 1 + 10 + 1];
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001371
Linus Torvalds1da177e2005-04-16 15:20:36 -07001372 if (!cam || !cam->proc_entry)
1373 return;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001374
Hans Verkuilc6330fb2008-10-19 18:54:26 -03001375 snprintf(name, sizeof(name), "video%d", cam->vdev.num);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001376 remove_proc_entry(name, cpia_proc_root);
1377 cam->proc_entry = NULL;
1378}
1379
1380static void proc_cpia_create(void)
1381{
Al Viro66600222005-09-28 22:32:57 +01001382 cpia_proc_root = proc_mkdir("cpia", NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001383
1384 if (cpia_proc_root)
1385 cpia_proc_root->owner = THIS_MODULE;
1386 else
1387 LOG("Unable to initialise /proc/cpia\n");
1388}
1389
1390static void __exit proc_cpia_destroy(void)
1391{
1392 remove_proc_entry("cpia", NULL);
1393}
1394#endif /* CONFIG_PROC_FS */
1395
1396/* ----------------------- debug functions ---------------------- */
1397
1398#define printstatus(cam) \
1399 DBG("%02x %02x %02x %02x %02x %02x %02x %02x\n",\
1400 cam->params.status.systemState, cam->params.status.grabState, \
1401 cam->params.status.streamState, cam->params.status.fatalError, \
1402 cam->params.status.cmdError, cam->params.status.debugFlags, \
1403 cam->params.status.vpStatus, cam->params.status.errorCode);
1404
1405/* ----------------------- v4l helpers -------------------------- */
1406
1407/* supported frame palettes and depths */
1408static inline int valid_mode(u16 palette, u16 depth)
1409{
1410 if ((palette == VIDEO_PALETTE_YUV422 && depth == 16) ||
1411 (palette == VIDEO_PALETTE_YUYV && depth == 16))
1412 return 1;
1413
1414 if (colorspace_conv)
1415 return (palette == VIDEO_PALETTE_GREY && depth == 8) ||
1416 (palette == VIDEO_PALETTE_RGB555 && depth == 16) ||
1417 (palette == VIDEO_PALETTE_RGB565 && depth == 16) ||
1418 (palette == VIDEO_PALETTE_RGB24 && depth == 24) ||
1419 (palette == VIDEO_PALETTE_RGB32 && depth == 32) ||
1420 (palette == VIDEO_PALETTE_UYVY && depth == 16);
1421
1422 return 0;
1423}
1424
1425static int match_videosize( int width, int height )
1426{
1427 /* return the best match, where 'best' is as always
1428 * the largest that is not bigger than what is requested. */
1429 if (width>=352 && height>=288)
1430 return VIDEOSIZE_352_288; /* CIF */
1431
1432 if (width>=320 && height>=240)
1433 return VIDEOSIZE_320_240; /* SIF */
1434
1435 if (width>=288 && height>=216)
1436 return VIDEOSIZE_288_216;
1437
1438 if (width>=256 && height>=192)
1439 return VIDEOSIZE_256_192;
1440
1441 if (width>=224 && height>=168)
1442 return VIDEOSIZE_224_168;
1443
1444 if (width>=192 && height>=144)
1445 return VIDEOSIZE_192_144;
1446
1447 if (width>=176 && height>=144)
1448 return VIDEOSIZE_176_144; /* QCIF */
1449
1450 if (width>=160 && height>=120)
1451 return VIDEOSIZE_160_120; /* QSIF */
1452
1453 if (width>=128 && height>=96)
1454 return VIDEOSIZE_128_96;
1455
1456 if (width>=88 && height>=72)
1457 return VIDEOSIZE_88_72;
1458
1459 if (width>=64 && height>=48)
1460 return VIDEOSIZE_64_48;
1461
1462 if (width>=48 && height>=48)
1463 return VIDEOSIZE_48_48;
1464
1465 return -1;
1466}
1467
1468/* these are the capture sizes we support */
1469static void set_vw_size(struct cam_data *cam)
1470{
1471 /* the col/row/start/end values are the result of simple math */
1472 /* study the SetROI-command in cpia developers guide p 2-22 */
1473 /* streamStartLine is set to the recommended value in the cpia */
1474 /* developers guide p 3-37 */
1475 switch(cam->video_size) {
1476 case VIDEOSIZE_CIF:
1477 cam->vw.width = 352;
1478 cam->vw.height = 288;
1479 cam->params.format.videoSize=VIDEOSIZE_CIF;
1480 cam->params.roi.colStart=0;
1481 cam->params.roi.rowStart=0;
1482 cam->params.streamStartLine = 120;
1483 break;
1484 case VIDEOSIZE_SIF:
1485 cam->vw.width = 320;
1486 cam->vw.height = 240;
1487 cam->params.format.videoSize=VIDEOSIZE_CIF;
1488 cam->params.roi.colStart=2;
1489 cam->params.roi.rowStart=6;
1490 cam->params.streamStartLine = 120;
1491 break;
1492 case VIDEOSIZE_288_216:
1493 cam->vw.width = 288;
1494 cam->vw.height = 216;
1495 cam->params.format.videoSize=VIDEOSIZE_CIF;
1496 cam->params.roi.colStart=4;
1497 cam->params.roi.rowStart=9;
1498 cam->params.streamStartLine = 120;
1499 break;
1500 case VIDEOSIZE_256_192:
1501 cam->vw.width = 256;
1502 cam->vw.height = 192;
1503 cam->params.format.videoSize=VIDEOSIZE_CIF;
1504 cam->params.roi.colStart=6;
1505 cam->params.roi.rowStart=12;
1506 cam->params.streamStartLine = 120;
1507 break;
1508 case VIDEOSIZE_224_168:
1509 cam->vw.width = 224;
1510 cam->vw.height = 168;
1511 cam->params.format.videoSize=VIDEOSIZE_CIF;
1512 cam->params.roi.colStart=8;
1513 cam->params.roi.rowStart=15;
1514 cam->params.streamStartLine = 120;
1515 break;
1516 case VIDEOSIZE_192_144:
1517 cam->vw.width = 192;
1518 cam->vw.height = 144;
1519 cam->params.format.videoSize=VIDEOSIZE_CIF;
1520 cam->params.roi.colStart=10;
1521 cam->params.roi.rowStart=18;
1522 cam->params.streamStartLine = 120;
1523 break;
1524 case VIDEOSIZE_QCIF:
1525 cam->vw.width = 176;
1526 cam->vw.height = 144;
1527 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1528 cam->params.roi.colStart=0;
1529 cam->params.roi.rowStart=0;
1530 cam->params.streamStartLine = 60;
1531 break;
1532 case VIDEOSIZE_QSIF:
1533 cam->vw.width = 160;
1534 cam->vw.height = 120;
1535 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1536 cam->params.roi.colStart=1;
1537 cam->params.roi.rowStart=3;
1538 cam->params.streamStartLine = 60;
1539 break;
1540 case VIDEOSIZE_128_96:
1541 cam->vw.width = 128;
1542 cam->vw.height = 96;
1543 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1544 cam->params.roi.colStart=3;
1545 cam->params.roi.rowStart=6;
1546 cam->params.streamStartLine = 60;
1547 break;
1548 case VIDEOSIZE_88_72:
1549 cam->vw.width = 88;
1550 cam->vw.height = 72;
1551 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1552 cam->params.roi.colStart=5;
1553 cam->params.roi.rowStart=9;
1554 cam->params.streamStartLine = 60;
1555 break;
1556 case VIDEOSIZE_64_48:
1557 cam->vw.width = 64;
1558 cam->vw.height = 48;
1559 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1560 cam->params.roi.colStart=7;
1561 cam->params.roi.rowStart=12;
1562 cam->params.streamStartLine = 60;
1563 break;
1564 case VIDEOSIZE_48_48:
1565 cam->vw.width = 48;
1566 cam->vw.height = 48;
1567 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1568 cam->params.roi.colStart=8;
1569 cam->params.roi.rowStart=6;
1570 cam->params.streamStartLine = 60;
1571 break;
1572 default:
1573 LOG("bad videosize value: %d\n", cam->video_size);
1574 return;
1575 }
1576
1577 if(cam->vc.width == 0)
1578 cam->vc.width = cam->vw.width;
1579 if(cam->vc.height == 0)
1580 cam->vc.height = cam->vw.height;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001581
Linus Torvalds1da177e2005-04-16 15:20:36 -07001582 cam->params.roi.colStart += cam->vc.x >> 3;
1583 cam->params.roi.colEnd = cam->params.roi.colStart +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001584 (cam->vc.width >> 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001585 cam->params.roi.rowStart += cam->vc.y >> 2;
1586 cam->params.roi.rowEnd = cam->params.roi.rowStart +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001587 (cam->vc.height >> 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001588
1589 return;
1590}
1591
1592static int allocate_frame_buf(struct cam_data *cam)
1593{
1594 int i;
1595
1596 cam->frame_buf = rvmalloc(FRAME_NUM * CPIA_MAX_FRAME_SIZE);
1597 if (!cam->frame_buf)
1598 return -ENOBUFS;
1599
1600 for (i = 0; i < FRAME_NUM; i++)
1601 cam->frame[i].data = cam->frame_buf + i * CPIA_MAX_FRAME_SIZE;
1602
1603 return 0;
1604}
1605
1606static int free_frame_buf(struct cam_data *cam)
1607{
1608 int i;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001609
Linus Torvalds1da177e2005-04-16 15:20:36 -07001610 rvfree(cam->frame_buf, FRAME_NUM*CPIA_MAX_FRAME_SIZE);
1611 cam->frame_buf = NULL;
1612 for (i=0; i < FRAME_NUM; i++)
1613 cam->frame[i].data = NULL;
1614
1615 return 0;
1616}
1617
1618
1619static inline void free_frames(struct cpia_frame frame[FRAME_NUM])
1620{
1621 int i;
1622
1623 for (i=0; i < FRAME_NUM; i++)
1624 frame[i].state = FRAME_UNUSED;
1625 return;
1626}
1627
1628/**********************************************************************
1629 *
1630 * General functions
1631 *
1632 **********************************************************************/
1633/* send an arbitrary command to the camera */
1634static int do_command(struct cam_data *cam, u16 command, u8 a, u8 b, u8 c, u8 d)
1635{
1636 int retval, datasize;
1637 u8 cmd[8], data[8];
1638
1639 switch(command) {
1640 case CPIA_COMMAND_GetCPIAVersion:
1641 case CPIA_COMMAND_GetPnPID:
1642 case CPIA_COMMAND_GetCameraStatus:
1643 case CPIA_COMMAND_GetVPVersion:
1644 datasize=8;
1645 break;
1646 case CPIA_COMMAND_GetColourParams:
1647 case CPIA_COMMAND_GetColourBalance:
1648 case CPIA_COMMAND_GetExposure:
Ingo Molnar3593cab2006-02-07 06:49:14 -02001649 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001650 datasize=8;
1651 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001652 case CPIA_COMMAND_ReadMCPorts:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001653 case CPIA_COMMAND_ReadVCRegs:
1654 datasize = 4;
1655 break;
1656 default:
1657 datasize=0;
1658 break;
1659 }
1660
1661 cmd[0] = command>>8;
1662 cmd[1] = command&0xff;
1663 cmd[2] = a;
1664 cmd[3] = b;
1665 cmd[4] = c;
1666 cmd[5] = d;
1667 cmd[6] = datasize;
1668 cmd[7] = 0;
1669
1670 retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
1671 if (retval) {
1672 DBG("%x - failed, retval=%d\n", command, retval);
1673 if (command == CPIA_COMMAND_GetColourParams ||
1674 command == CPIA_COMMAND_GetColourBalance ||
1675 command == CPIA_COMMAND_GetExposure)
Ingo Molnar3593cab2006-02-07 06:49:14 -02001676 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001677 } else {
1678 switch(command) {
1679 case CPIA_COMMAND_GetCPIAVersion:
1680 cam->params.version.firmwareVersion = data[0];
1681 cam->params.version.firmwareRevision = data[1];
1682 cam->params.version.vcVersion = data[2];
1683 cam->params.version.vcRevision = data[3];
1684 break;
1685 case CPIA_COMMAND_GetPnPID:
1686 cam->params.pnpID.vendor = data[0]+(((u16)data[1])<<8);
1687 cam->params.pnpID.product = data[2]+(((u16)data[3])<<8);
1688 cam->params.pnpID.deviceRevision =
1689 data[4]+(((u16)data[5])<<8);
1690 break;
1691 case CPIA_COMMAND_GetCameraStatus:
1692 cam->params.status.systemState = data[0];
1693 cam->params.status.grabState = data[1];
1694 cam->params.status.streamState = data[2];
1695 cam->params.status.fatalError = data[3];
1696 cam->params.status.cmdError = data[4];
1697 cam->params.status.debugFlags = data[5];
1698 cam->params.status.vpStatus = data[6];
1699 cam->params.status.errorCode = data[7];
1700 break;
1701 case CPIA_COMMAND_GetVPVersion:
1702 cam->params.vpVersion.vpVersion = data[0];
1703 cam->params.vpVersion.vpRevision = data[1];
1704 cam->params.vpVersion.cameraHeadID =
1705 data[2]+(((u16)data[3])<<8);
1706 break;
1707 case CPIA_COMMAND_GetColourParams:
1708 cam->params.colourParams.brightness = data[0];
1709 cam->params.colourParams.contrast = data[1];
1710 cam->params.colourParams.saturation = data[2];
Ingo Molnar3593cab2006-02-07 06:49:14 -02001711 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001712 break;
1713 case CPIA_COMMAND_GetColourBalance:
1714 cam->params.colourBalance.redGain = data[0];
1715 cam->params.colourBalance.greenGain = data[1];
1716 cam->params.colourBalance.blueGain = data[2];
Ingo Molnar3593cab2006-02-07 06:49:14 -02001717 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001718 break;
1719 case CPIA_COMMAND_GetExposure:
1720 cam->params.exposure.gain = data[0];
1721 cam->params.exposure.fineExp = data[1];
1722 cam->params.exposure.coarseExpLo = data[2];
1723 cam->params.exposure.coarseExpHi = data[3];
1724 cam->params.exposure.redComp = data[4];
1725 cam->params.exposure.green1Comp = data[5];
1726 cam->params.exposure.green2Comp = data[6];
1727 cam->params.exposure.blueComp = data[7];
Ingo Molnar3593cab2006-02-07 06:49:14 -02001728 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001729 break;
1730
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001731 case CPIA_COMMAND_ReadMCPorts:
1732 if (!cam->params.qx3.qx3_detected)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001733 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001734 /* test button press */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001735 cam->params.qx3.button = ((data[1] & 0x02) == 0);
1736 if (cam->params.qx3.button) {
1737 /* button pressed - unlock the latch */
1738 do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xDF,0xDF,0);
1739 do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xFF,0xFF,0);
1740 }
1741
1742 /* test whether microscope is cradled */
1743 cam->params.qx3.cradled = ((data[2] & 0x40) == 0);
1744 break;
1745
1746 default:
1747 break;
1748 }
1749 }
1750 return retval;
1751}
1752
1753/* send a command to the camera with an additional data transaction */
1754static int do_command_extended(struct cam_data *cam, u16 command,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001755 u8 a, u8 b, u8 c, u8 d,
1756 u8 e, u8 f, u8 g, u8 h,
1757 u8 i, u8 j, u8 k, u8 l)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001758{
1759 int retval;
1760 u8 cmd[8], data[8];
1761
1762 cmd[0] = command>>8;
1763 cmd[1] = command&0xff;
1764 cmd[2] = a;
1765 cmd[3] = b;
1766 cmd[4] = c;
1767 cmd[5] = d;
1768 cmd[6] = 8;
1769 cmd[7] = 0;
1770 data[0] = e;
1771 data[1] = f;
1772 data[2] = g;
1773 data[3] = h;
1774 data[4] = i;
1775 data[5] = j;
1776 data[6] = k;
1777 data[7] = l;
1778
1779 retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
1780 if (retval)
1781 DBG("%x - failed\n", command);
1782
1783 return retval;
1784}
1785
1786/**********************************************************************
1787 *
1788 * Colorspace conversion
1789 *
1790 **********************************************************************/
1791#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16)
1792
1793static int convert420(unsigned char *yuv, unsigned char *rgb, int out_fmt,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001794 int linesize, int mmap_kludge)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001795{
1796 int y, u, v, r, g, b, y1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001797
Linus Torvalds1da177e2005-04-16 15:20:36 -07001798 /* Odd lines use the same u and v as the previous line.
1799 * Because of compression, it is necessary to get this
1800 * information from the decoded image. */
1801 switch(out_fmt) {
1802 case VIDEO_PALETTE_RGB555:
1803 y = (*yuv++ - 16) * 76310;
1804 y1 = (*yuv - 16) * 76310;
1805 r = ((*(rgb+1-linesize)) & 0x7c) << 1;
1806 g = ((*(rgb-linesize)) & 0xe0) >> 4 |
1807 ((*(rgb+1-linesize)) & 0x03) << 6;
1808 b = ((*(rgb-linesize)) & 0x1f) << 3;
1809 u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
1810 v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
1811 r = 104635 * v;
1812 g = -25690 * u - 53294 * v;
1813 b = 132278 * u;
1814 *rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3);
1815 *rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6);
1816 *rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3);
1817 *rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6);
1818 return 4;
1819 case VIDEO_PALETTE_RGB565:
1820 y = (*yuv++ - 16) * 76310;
1821 y1 = (*yuv - 16) * 76310;
1822 r = (*(rgb+1-linesize)) & 0xf8;
1823 g = ((*(rgb-linesize)) & 0xe0) >> 3 |
1824 ((*(rgb+1-linesize)) & 0x07) << 5;
1825 b = ((*(rgb-linesize)) & 0x1f) << 3;
1826 u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
1827 v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
1828 r = 104635 * v;
1829 g = -25690 * u - 53294 * v;
1830 b = 132278 * u;
1831 *rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3);
1832 *rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5);
1833 *rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3);
1834 *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5);
1835 return 4;
1836 break;
1837 case VIDEO_PALETTE_RGB24:
1838 case VIDEO_PALETTE_RGB32:
1839 y = (*yuv++ - 16) * 76310;
1840 y1 = (*yuv - 16) * 76310;
1841 if (mmap_kludge) {
1842 r = *(rgb+2-linesize);
1843 g = *(rgb+1-linesize);
1844 b = *(rgb-linesize);
1845 } else {
1846 r = *(rgb-linesize);
1847 g = *(rgb+1-linesize);
1848 b = *(rgb+2-linesize);
1849 }
1850 u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
1851 v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
1852 r = 104635 * v;
1853 g = -25690 * u + -53294 * v;
1854 b = 132278 * u;
1855 if (mmap_kludge) {
1856 *rgb++ = LIMIT(b+y);
1857 *rgb++ = LIMIT(g+y);
1858 *rgb++ = LIMIT(r+y);
1859 if(out_fmt == VIDEO_PALETTE_RGB32)
1860 rgb++;
1861 *rgb++ = LIMIT(b+y1);
1862 *rgb++ = LIMIT(g+y1);
1863 *rgb = LIMIT(r+y1);
1864 } else {
1865 *rgb++ = LIMIT(r+y);
1866 *rgb++ = LIMIT(g+y);
1867 *rgb++ = LIMIT(b+y);
1868 if(out_fmt == VIDEO_PALETTE_RGB32)
1869 rgb++;
1870 *rgb++ = LIMIT(r+y1);
1871 *rgb++ = LIMIT(g+y1);
1872 *rgb = LIMIT(b+y1);
1873 }
1874 if(out_fmt == VIDEO_PALETTE_RGB32)
1875 return 8;
1876 return 6;
1877 case VIDEO_PALETTE_YUV422:
1878 case VIDEO_PALETTE_YUYV:
1879 y = *yuv++;
1880 u = *(rgb+1-linesize);
1881 y1 = *yuv;
1882 v = *(rgb+3-linesize);
1883 *rgb++ = y;
1884 *rgb++ = u;
1885 *rgb++ = y1;
1886 *rgb = v;
1887 return 4;
1888 case VIDEO_PALETTE_UYVY:
1889 u = *(rgb-linesize);
1890 y = *yuv++;
1891 v = *(rgb+2-linesize);
1892 y1 = *yuv;
1893 *rgb++ = u;
1894 *rgb++ = y;
1895 *rgb++ = v;
1896 *rgb = y1;
1897 return 4;
1898 case VIDEO_PALETTE_GREY:
1899 *rgb++ = *yuv++;
1900 *rgb = *yuv;
1901 return 2;
1902 default:
1903 DBG("Empty: %d\n", out_fmt);
1904 return 0;
1905 }
1906}
1907
1908
1909static int yuvconvert(unsigned char *yuv, unsigned char *rgb, int out_fmt,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001910 int in_uyvy, int mmap_kludge)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001911{
1912 int y, u, v, r, g, b, y1;
1913
1914 switch(out_fmt) {
1915 case VIDEO_PALETTE_RGB555:
1916 case VIDEO_PALETTE_RGB565:
1917 case VIDEO_PALETTE_RGB24:
1918 case VIDEO_PALETTE_RGB32:
1919 if (in_uyvy) {
1920 u = *yuv++ - 128;
1921 y = (*yuv++ - 16) * 76310;
1922 v = *yuv++ - 128;
1923 y1 = (*yuv - 16) * 76310;
1924 } else {
1925 y = (*yuv++ - 16) * 76310;
1926 u = *yuv++ - 128;
1927 y1 = (*yuv++ - 16) * 76310;
1928 v = *yuv - 128;
1929 }
1930 r = 104635 * v;
1931 g = -25690 * u + -53294 * v;
1932 b = 132278 * u;
1933 break;
1934 default:
1935 y = *yuv++;
1936 u = *yuv++;
1937 y1 = *yuv++;
1938 v = *yuv;
1939 /* Just to avoid compiler warnings */
1940 r = 0;
1941 g = 0;
1942 b = 0;
1943 break;
1944 }
1945 switch(out_fmt) {
1946 case VIDEO_PALETTE_RGB555:
1947 *rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3);
1948 *rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6);
1949 *rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3);
1950 *rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6);
1951 return 4;
1952 case VIDEO_PALETTE_RGB565:
1953 *rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3);
1954 *rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5);
1955 *rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3);
1956 *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5);
1957 return 4;
1958 case VIDEO_PALETTE_RGB24:
1959 if (mmap_kludge) {
1960 *rgb++ = LIMIT(b+y);
1961 *rgb++ = LIMIT(g+y);
1962 *rgb++ = LIMIT(r+y);
1963 *rgb++ = LIMIT(b+y1);
1964 *rgb++ = LIMIT(g+y1);
1965 *rgb = LIMIT(r+y1);
1966 } else {
1967 *rgb++ = LIMIT(r+y);
1968 *rgb++ = LIMIT(g+y);
1969 *rgb++ = LIMIT(b+y);
1970 *rgb++ = LIMIT(r+y1);
1971 *rgb++ = LIMIT(g+y1);
1972 *rgb = LIMIT(b+y1);
1973 }
1974 return 6;
1975 case VIDEO_PALETTE_RGB32:
1976 if (mmap_kludge) {
1977 *rgb++ = LIMIT(b+y);
1978 *rgb++ = LIMIT(g+y);
1979 *rgb++ = LIMIT(r+y);
1980 rgb++;
1981 *rgb++ = LIMIT(b+y1);
1982 *rgb++ = LIMIT(g+y1);
1983 *rgb = LIMIT(r+y1);
1984 } else {
1985 *rgb++ = LIMIT(r+y);
1986 *rgb++ = LIMIT(g+y);
1987 *rgb++ = LIMIT(b+y);
1988 rgb++;
1989 *rgb++ = LIMIT(r+y1);
1990 *rgb++ = LIMIT(g+y1);
1991 *rgb = LIMIT(b+y1);
1992 }
1993 return 8;
1994 case VIDEO_PALETTE_GREY:
1995 *rgb++ = y;
1996 *rgb = y1;
1997 return 2;
1998 case VIDEO_PALETTE_YUV422:
1999 case VIDEO_PALETTE_YUYV:
2000 *rgb++ = y;
2001 *rgb++ = u;
2002 *rgb++ = y1;
2003 *rgb = v;
2004 return 4;
2005 case VIDEO_PALETTE_UYVY:
2006 *rgb++ = u;
2007 *rgb++ = y;
2008 *rgb++ = v;
2009 *rgb = y1;
2010 return 4;
2011 default:
2012 DBG("Empty: %d\n", out_fmt);
2013 return 0;
2014 }
2015}
2016
2017static int skipcount(int count, int fmt)
2018{
2019 switch(fmt) {
2020 case VIDEO_PALETTE_GREY:
2021 return count;
2022 case VIDEO_PALETTE_RGB555:
2023 case VIDEO_PALETTE_RGB565:
2024 case VIDEO_PALETTE_YUV422:
2025 case VIDEO_PALETTE_YUYV:
2026 case VIDEO_PALETTE_UYVY:
2027 return 2*count;
2028 case VIDEO_PALETTE_RGB24:
2029 return 3*count;
2030 case VIDEO_PALETTE_RGB32:
2031 return 4*count;
2032 default:
2033 return 0;
2034 }
2035}
2036
2037static int parse_picture(struct cam_data *cam, int size)
2038{
2039 u8 *obuf, *ibuf, *end_obuf;
2040 int ll, in_uyvy, compressed, decimation, even_line, origsize, out_fmt;
2041 int rows, cols, linesize, subsample_422;
2042
2043 /* make sure params don't change while we are decoding */
Ingo Molnar3593cab2006-02-07 06:49:14 -02002044 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002045
2046 obuf = cam->decompressed_frame.data;
2047 end_obuf = obuf+CPIA_MAX_FRAME_SIZE;
2048 ibuf = cam->raw_image;
2049 origsize = size;
2050 out_fmt = cam->vp.palette;
2051
2052 if ((ibuf[0] != MAGIC_0) || (ibuf[1] != MAGIC_1)) {
2053 LOG("header not found\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02002054 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002055 return -1;
2056 }
2057
2058 if ((ibuf[16] != VIDEOSIZE_QCIF) && (ibuf[16] != VIDEOSIZE_CIF)) {
2059 LOG("wrong video size\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02002060 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002061 return -1;
2062 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002063
Linus Torvalds1da177e2005-04-16 15:20:36 -07002064 if (ibuf[17] != SUBSAMPLE_420 && ibuf[17] != SUBSAMPLE_422) {
2065 LOG("illegal subtype %d\n",ibuf[17]);
Ingo Molnar3593cab2006-02-07 06:49:14 -02002066 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002067 return -1;
2068 }
2069 subsample_422 = ibuf[17] == SUBSAMPLE_422;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002070
Linus Torvalds1da177e2005-04-16 15:20:36 -07002071 if (ibuf[18] != YUVORDER_YUYV && ibuf[18] != YUVORDER_UYVY) {
2072 LOG("illegal yuvorder %d\n",ibuf[18]);
Ingo Molnar3593cab2006-02-07 06:49:14 -02002073 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002074 return -1;
2075 }
2076 in_uyvy = ibuf[18] == YUVORDER_UYVY;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002077
Linus Torvalds1da177e2005-04-16 15:20:36 -07002078 if ((ibuf[24] != cam->params.roi.colStart) ||
2079 (ibuf[25] != cam->params.roi.colEnd) ||
2080 (ibuf[26] != cam->params.roi.rowStart) ||
2081 (ibuf[27] != cam->params.roi.rowEnd)) {
2082 LOG("ROI mismatch\n");
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 cols = 8*(ibuf[25] - ibuf[24]);
2087 rows = 4*(ibuf[27] - ibuf[26]);
2088
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002089
Linus Torvalds1da177e2005-04-16 15:20:36 -07002090 if ((ibuf[28] != NOT_COMPRESSED) && (ibuf[28] != COMPRESSED)) {
2091 LOG("illegal compression %d\n",ibuf[28]);
Ingo Molnar3593cab2006-02-07 06:49:14 -02002092 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002093 return -1;
2094 }
2095 compressed = (ibuf[28] == COMPRESSED);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002096
Linus Torvalds1da177e2005-04-16 15:20:36 -07002097 if (ibuf[29] != NO_DECIMATION && ibuf[29] != DECIMATION_ENAB) {
2098 LOG("illegal decimation %d\n",ibuf[29]);
Ingo Molnar3593cab2006-02-07 06:49:14 -02002099 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002100 return -1;
2101 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002102 decimation = (ibuf[29] == DECIMATION_ENAB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002103
2104 cam->params.yuvThreshold.yThreshold = ibuf[30];
2105 cam->params.yuvThreshold.uvThreshold = ibuf[31];
2106 cam->params.status.systemState = ibuf[32];
2107 cam->params.status.grabState = ibuf[33];
2108 cam->params.status.streamState = ibuf[34];
2109 cam->params.status.fatalError = ibuf[35];
2110 cam->params.status.cmdError = ibuf[36];
2111 cam->params.status.debugFlags = ibuf[37];
2112 cam->params.status.vpStatus = ibuf[38];
2113 cam->params.status.errorCode = ibuf[39];
2114 cam->fps = ibuf[41];
Ingo Molnar3593cab2006-02-07 06:49:14 -02002115 mutex_unlock(&cam->param_lock);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002116
Linus Torvalds1da177e2005-04-16 15:20:36 -07002117 linesize = skipcount(cols, out_fmt);
2118 ibuf += FRAME_HEADER_SIZE;
2119 size -= FRAME_HEADER_SIZE;
2120 ll = ibuf[0] | (ibuf[1] << 8);
2121 ibuf += 2;
2122 even_line = 1;
2123
2124 while (size > 0) {
2125 size -= (ll+2);
2126 if (size < 0) {
2127 LOG("Insufficient data in buffer\n");
2128 return -1;
2129 }
2130
2131 while (ll > 1) {
2132 if (!compressed || (compressed && !(*ibuf & 1))) {
2133 if(subsample_422 || even_line) {
2134 obuf += yuvconvert(ibuf, obuf, out_fmt,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002135 in_uyvy, cam->mmap_kludge);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002136 ibuf += 4;
2137 ll -= 4;
2138 } else {
2139 /* SUBSAMPLE_420 on an odd line */
2140 obuf += convert420(ibuf, obuf,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002141 out_fmt, linesize,
2142 cam->mmap_kludge);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002143 ibuf += 2;
2144 ll -= 2;
2145 }
2146 } else {
2147 /*skip compressed interval from previous frame*/
2148 obuf += skipcount(*ibuf >> 1, out_fmt);
2149 if (obuf > end_obuf) {
2150 LOG("Insufficient buffer size\n");
2151 return -1;
2152 }
2153 ++ibuf;
2154 ll--;
2155 }
2156 }
2157 if (ll == 1) {
2158 if (*ibuf != EOL) {
2159 DBG("EOL not found giving up after %d/%d"
2160 " bytes\n", origsize-size, origsize);
2161 return -1;
2162 }
2163
2164 ++ibuf; /* skip over EOL */
2165
2166 if ((size > 3) && (ibuf[0] == EOI) && (ibuf[1] == EOI) &&
2167 (ibuf[2] == EOI) && (ibuf[3] == EOI)) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002168 size -= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002169 break;
2170 }
2171
2172 if(decimation) {
2173 /* skip the odd lines for now */
2174 obuf += linesize;
2175 }
2176
2177 if (size > 1) {
2178 ll = ibuf[0] | (ibuf[1] << 8);
2179 ibuf += 2; /* skip over line length */
2180 }
2181 if(!decimation)
2182 even_line = !even_line;
2183 } else {
2184 LOG("line length was not 1 but %d after %d/%d bytes\n",
2185 ll, origsize-size, origsize);
2186 return -1;
2187 }
2188 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002189
Linus Torvalds1da177e2005-04-16 15:20:36 -07002190 if(decimation) {
2191 /* interpolate odd rows */
2192 int i, j;
2193 u8 *prev, *next;
2194 prev = cam->decompressed_frame.data;
2195 obuf = prev+linesize;
2196 next = obuf+linesize;
2197 for(i=1; i<rows-1; i+=2) {
2198 for(j=0; j<linesize; ++j) {
2199 *obuf++ = ((int)*prev++ + *next++) / 2;
2200 }
2201 prev += linesize;
2202 obuf += linesize;
2203 next += linesize;
2204 }
2205 /* last row is odd, just copy previous row */
2206 memcpy(obuf, prev, linesize);
2207 }
2208
2209 cam->decompressed_frame.count = obuf-cam->decompressed_frame.data;
2210
2211 return cam->decompressed_frame.count;
2212}
2213
2214/* InitStreamCap wrapper to select correct start line */
2215static inline int init_stream_cap(struct cam_data *cam)
2216{
2217 return do_command(cam, CPIA_COMMAND_InitStreamCap,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002218 0, cam->params.streamStartLine, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002219}
2220
2221
2222/* find_over_exposure
2223 * Finds a suitable value of OverExposure for use with SetFlickerCtrl
2224 * Some calculation is required because this value changes with the brightness
2225 * set with SetColourParameters
2226 *
2227 * Parameters: Brightness - last brightness value set with SetColourParameters
2228 *
2229 * Returns: OverExposure value to use with SetFlickerCtrl
2230 */
2231#define FLICKER_MAX_EXPOSURE 250
2232#define FLICKER_ALLOWABLE_OVER_EXPOSURE 146
2233#define FLICKER_BRIGHTNESS_CONSTANT 59
2234static int find_over_exposure(int brightness)
2235{
2236 int MaxAllowableOverExposure, OverExposure;
2237
2238 MaxAllowableOverExposure = FLICKER_MAX_EXPOSURE - brightness -
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002239 FLICKER_BRIGHTNESS_CONSTANT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002240
2241 if (MaxAllowableOverExposure < FLICKER_ALLOWABLE_OVER_EXPOSURE) {
2242 OverExposure = MaxAllowableOverExposure;
2243 } else {
2244 OverExposure = FLICKER_ALLOWABLE_OVER_EXPOSURE;
2245 }
2246
2247 return OverExposure;
2248}
2249#undef FLICKER_MAX_EXPOSURE
2250#undef FLICKER_ALLOWABLE_OVER_EXPOSURE
2251#undef FLICKER_BRIGHTNESS_CONSTANT
2252
2253/* update various camera modes and settings */
2254static void dispatch_commands(struct cam_data *cam)
2255{
Ingo Molnar3593cab2006-02-07 06:49:14 -02002256 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002257 if (cam->cmd_queue==COMMAND_NONE) {
Ingo Molnar3593cab2006-02-07 06:49:14 -02002258 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002259 return;
2260 }
2261 DEB_BYTE(cam->cmd_queue);
2262 DEB_BYTE(cam->cmd_queue>>8);
2263 if (cam->cmd_queue & COMMAND_SETFORMAT) {
2264 do_command(cam, CPIA_COMMAND_SetFormat,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002265 cam->params.format.videoSize,
2266 cam->params.format.subSample,
2267 cam->params.format.yuvOrder, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002268 do_command(cam, CPIA_COMMAND_SetROI,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002269 cam->params.roi.colStart, cam->params.roi.colEnd,
2270 cam->params.roi.rowStart, cam->params.roi.rowEnd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002271 cam->first_frame = 1;
2272 }
2273
2274 if (cam->cmd_queue & COMMAND_SETCOLOURPARAMS)
2275 do_command(cam, CPIA_COMMAND_SetColourParams,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002276 cam->params.colourParams.brightness,
2277 cam->params.colourParams.contrast,
2278 cam->params.colourParams.saturation, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002279
2280 if (cam->cmd_queue & COMMAND_SETAPCOR)
2281 do_command(cam, CPIA_COMMAND_SetApcor,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002282 cam->params.apcor.gain1,
2283 cam->params.apcor.gain2,
2284 cam->params.apcor.gain4,
2285 cam->params.apcor.gain8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002286
2287 if (cam->cmd_queue & COMMAND_SETVLOFFSET)
2288 do_command(cam, CPIA_COMMAND_SetVLOffset,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002289 cam->params.vlOffset.gain1,
2290 cam->params.vlOffset.gain2,
2291 cam->params.vlOffset.gain4,
2292 cam->params.vlOffset.gain8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002293
2294 if (cam->cmd_queue & COMMAND_SETEXPOSURE) {
2295 do_command_extended(cam, CPIA_COMMAND_SetExposure,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002296 cam->params.exposure.gainMode,
2297 1,
2298 cam->params.exposure.compMode,
2299 cam->params.exposure.centreWeight,
2300 cam->params.exposure.gain,
2301 cam->params.exposure.fineExp,
2302 cam->params.exposure.coarseExpLo,
2303 cam->params.exposure.coarseExpHi,
2304 cam->params.exposure.redComp,
2305 cam->params.exposure.green1Comp,
2306 cam->params.exposure.green2Comp,
2307 cam->params.exposure.blueComp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002308 if(cam->params.exposure.expMode != 1) {
2309 do_command_extended(cam, CPIA_COMMAND_SetExposure,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002310 0,
2311 cam->params.exposure.expMode,
2312 0, 0,
2313 cam->params.exposure.gain,
2314 cam->params.exposure.fineExp,
2315 cam->params.exposure.coarseExpLo,
2316 cam->params.exposure.coarseExpHi,
2317 0, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002318 }
2319 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002320
Linus Torvalds1da177e2005-04-16 15:20:36 -07002321 if (cam->cmd_queue & COMMAND_SETCOLOURBALANCE) {
2322 if (cam->params.colourBalance.balanceMode == 1) {
2323 do_command(cam, CPIA_COMMAND_SetColourBalance,
2324 1,
2325 cam->params.colourBalance.redGain,
2326 cam->params.colourBalance.greenGain,
2327 cam->params.colourBalance.blueGain);
2328 do_command(cam, CPIA_COMMAND_SetColourBalance,
2329 3, 0, 0, 0);
2330 }
2331 if (cam->params.colourBalance.balanceMode == 2) {
2332 do_command(cam, CPIA_COMMAND_SetColourBalance,
2333 2, 0, 0, 0);
2334 }
2335 if (cam->params.colourBalance.balanceMode == 3) {
2336 do_command(cam, CPIA_COMMAND_SetColourBalance,
2337 3, 0, 0, 0);
2338 }
2339 }
2340
2341 if (cam->cmd_queue & COMMAND_SETCOMPRESSIONTARGET)
2342 do_command(cam, CPIA_COMMAND_SetCompressionTarget,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002343 cam->params.compressionTarget.frTargeting,
2344 cam->params.compressionTarget.targetFR,
2345 cam->params.compressionTarget.targetQ, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002346
2347 if (cam->cmd_queue & COMMAND_SETYUVTHRESH)
2348 do_command(cam, CPIA_COMMAND_SetYUVThresh,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002349 cam->params.yuvThreshold.yThreshold,
2350 cam->params.yuvThreshold.uvThreshold, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002351
2352 if (cam->cmd_queue & COMMAND_SETCOMPRESSIONPARAMS)
2353 do_command_extended(cam, CPIA_COMMAND_SetCompressionParams,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002354 0, 0, 0, 0,
2355 cam->params.compressionParams.hysteresis,
2356 cam->params.compressionParams.threshMax,
2357 cam->params.compressionParams.smallStep,
2358 cam->params.compressionParams.largeStep,
2359 cam->params.compressionParams.decimationHysteresis,
2360 cam->params.compressionParams.frDiffStepThresh,
2361 cam->params.compressionParams.qDiffStepThresh,
2362 cam->params.compressionParams.decimationThreshMod);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002363
2364 if (cam->cmd_queue & COMMAND_SETCOMPRESSION)
2365 do_command(cam, CPIA_COMMAND_SetCompression,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002366 cam->params.compression.mode,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002367 cam->params.compression.decimation, 0, 0);
2368
2369 if (cam->cmd_queue & COMMAND_SETSENSORFPS)
2370 do_command(cam, CPIA_COMMAND_SetSensorFPS,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002371 cam->params.sensorFps.divisor,
2372 cam->params.sensorFps.baserate, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002373
2374 if (cam->cmd_queue & COMMAND_SETFLICKERCTRL)
2375 do_command(cam, CPIA_COMMAND_SetFlickerCtrl,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002376 cam->params.flickerControl.flickerMode,
2377 cam->params.flickerControl.coarseJump,
2378 abs(cam->params.flickerControl.allowableOverExposure),
2379 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002380
2381 if (cam->cmd_queue & COMMAND_SETECPTIMING)
2382 do_command(cam, CPIA_COMMAND_SetECPTiming,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002383 cam->params.ecpTiming, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002384
2385 if (cam->cmd_queue & COMMAND_PAUSE)
2386 do_command(cam, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0);
2387
2388 if (cam->cmd_queue & COMMAND_RESUME)
2389 init_stream_cap(cam);
2390
2391 if (cam->cmd_queue & COMMAND_SETLIGHTS && cam->params.qx3.qx3_detected)
2392 {
2393 int p1 = (cam->params.qx3.bottomlight == 0) << 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002394 int p2 = (cam->params.qx3.toplight == 0) << 3;
2395 do_command(cam, CPIA_COMMAND_WriteVCReg, 0x90, 0x8F, 0x50, 0);
2396 do_command(cam, CPIA_COMMAND_WriteMCPort, 2, 0, (p1|p2|0xE0), 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002397 }
2398
2399 cam->cmd_queue = COMMAND_NONE;
Ingo Molnar3593cab2006-02-07 06:49:14 -02002400 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002401 return;
2402}
2403
2404
2405
2406static void set_flicker(struct cam_params *params, volatile u32 *command_flags,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002407 int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002408{
2409 /* Everything in here is from the Windows driver */
2410#define FIRMWARE_VERSION(x,y) (params->version.firmwareVersion == (x) && \
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002411 params->version.firmwareRevision == (y))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002412/* define for compgain calculation */
2413#if 0
2414#define COMPGAIN(base, curexp, newexp) \
2415 (u8) ((((float) base - 128.0) * ((float) curexp / (float) newexp)) + 128.5)
2416#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
2417 (u16)((float)curexp * (float)(u8)(curcomp + 128) / (float)(u8)(basecomp - 128))
2418#else
2419 /* equivalent functions without floating point math */
2420#define COMPGAIN(base, curexp, newexp) \
2421 (u8)(128 + (((u32)(2*(base-128)*curexp + newexp)) / (2* newexp)) )
2422#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
2423 (u16)(((u32)(curexp * (u8)(curcomp + 128)) / (u8)(basecomp - 128)))
2424#endif
2425
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002426
Linus Torvalds1da177e2005-04-16 15:20:36 -07002427 int currentexp = params->exposure.coarseExpLo +
2428 params->exposure.coarseExpHi*256;
2429 int startexp;
2430 if (on) {
2431 int cj = params->flickerControl.coarseJump;
2432 params->flickerControl.flickerMode = 1;
2433 params->flickerControl.disabled = 0;
2434 if(params->exposure.expMode != 2)
2435 *command_flags |= COMMAND_SETEXPOSURE;
2436 params->exposure.expMode = 2;
2437 currentexp = currentexp << params->exposure.gain;
2438 params->exposure.gain = 0;
2439 /* round down current exposure to nearest value */
2440 startexp = (currentexp + ROUND_UP_EXP_FOR_FLICKER) / cj;
2441 if(startexp < 1)
2442 startexp = 1;
2443 startexp = (startexp * cj) - 1;
2444 if(FIRMWARE_VERSION(1,2))
2445 while(startexp > MAX_EXP_102)
2446 startexp -= cj;
2447 else
2448 while(startexp > MAX_EXP)
2449 startexp -= cj;
2450 params->exposure.coarseExpLo = startexp & 0xff;
2451 params->exposure.coarseExpHi = startexp >> 8;
2452 if (currentexp > startexp) {
2453 if (currentexp > (2 * startexp))
2454 currentexp = 2 * startexp;
2455 params->exposure.redComp = COMPGAIN (COMP_RED, currentexp, startexp);
2456 params->exposure.green1Comp = COMPGAIN (COMP_GREEN1, currentexp, startexp);
2457 params->exposure.green2Comp = COMPGAIN (COMP_GREEN2, currentexp, startexp);
2458 params->exposure.blueComp = COMPGAIN (COMP_BLUE, currentexp, startexp);
2459 } else {
2460 params->exposure.redComp = COMP_RED;
2461 params->exposure.green1Comp = COMP_GREEN1;
2462 params->exposure.green2Comp = COMP_GREEN2;
2463 params->exposure.blueComp = COMP_BLUE;
2464 }
2465 if(FIRMWARE_VERSION(1,2))
2466 params->exposure.compMode = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002467 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07002468 params->exposure.compMode = 1;
2469
2470 params->apcor.gain1 = 0x18;
2471 params->apcor.gain2 = 0x18;
2472 params->apcor.gain4 = 0x16;
2473 params->apcor.gain8 = 0x14;
2474 *command_flags |= COMMAND_SETAPCOR;
2475 } else {
2476 params->flickerControl.flickerMode = 0;
2477 params->flickerControl.disabled = 1;
2478 /* Coarse = average of equivalent coarse for each comp channel */
2479 startexp = EXP_FROM_COMP(COMP_RED, params->exposure.redComp, currentexp);
2480 startexp += EXP_FROM_COMP(COMP_GREEN1, params->exposure.green1Comp, currentexp);
2481 startexp += EXP_FROM_COMP(COMP_GREEN2, params->exposure.green2Comp, currentexp);
2482 startexp += EXP_FROM_COMP(COMP_BLUE, params->exposure.blueComp, currentexp);
2483 startexp = startexp >> 2;
2484 while(startexp > MAX_EXP &&
2485 params->exposure.gain < params->exposure.gainMode-1) {
2486 startexp = startexp >> 1;
2487 ++params->exposure.gain;
2488 }
2489 if(FIRMWARE_VERSION(1,2) && startexp > MAX_EXP_102)
2490 startexp = MAX_EXP_102;
2491 if(startexp > MAX_EXP)
2492 startexp = MAX_EXP;
2493 params->exposure.coarseExpLo = startexp&0xff;
2494 params->exposure.coarseExpHi = startexp >> 8;
2495 params->exposure.redComp = COMP_RED;
2496 params->exposure.green1Comp = COMP_GREEN1;
2497 params->exposure.green2Comp = COMP_GREEN2;
2498 params->exposure.blueComp = COMP_BLUE;
2499 params->exposure.compMode = 1;
2500 *command_flags |= COMMAND_SETEXPOSURE;
2501 params->apcor.gain1 = 0x18;
2502 params->apcor.gain2 = 0x16;
2503 params->apcor.gain4 = 0x24;
2504 params->apcor.gain8 = 0x34;
2505 *command_flags |= COMMAND_SETAPCOR;
2506 }
2507 params->vlOffset.gain1 = 20;
2508 params->vlOffset.gain2 = 24;
2509 params->vlOffset.gain4 = 26;
2510 params->vlOffset.gain8 = 26;
2511 *command_flags |= COMMAND_SETVLOFFSET;
2512#undef FIRMWARE_VERSION
2513#undef EXP_FROM_COMP
2514#undef COMPGAIN
2515}
2516
2517#define FIRMWARE_VERSION(x,y) (cam->params.version.firmwareVersion == (x) && \
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002518 cam->params.version.firmwareRevision == (y))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002519/* monitor the exposure and adjust the sensor frame rate if needed */
2520static void monitor_exposure(struct cam_data *cam)
2521{
2522 u8 exp_acc, bcomp, gain, coarseL, cmd[8], data[8];
2523 int retval, light_exp, dark_exp, very_dark_exp;
2524 int old_exposure, new_exposure, framerate;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002525
Linus Torvalds1da177e2005-04-16 15:20:36 -07002526 /* get necessary stats and register settings from camera */
2527 /* do_command can't handle this, so do it ourselves */
2528 cmd[0] = CPIA_COMMAND_ReadVPRegs>>8;
2529 cmd[1] = CPIA_COMMAND_ReadVPRegs&0xff;
2530 cmd[2] = 30;
2531 cmd[3] = 4;
2532 cmd[4] = 9;
2533 cmd[5] = 8;
2534 cmd[6] = 8;
2535 cmd[7] = 0;
2536 retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
2537 if (retval) {
2538 LOG("ReadVPRegs(30,4,9,8) - failed, retval=%d\n",
2539 retval);
2540 return;
2541 }
2542 exp_acc = data[0];
2543 bcomp = data[1];
2544 gain = data[2];
2545 coarseL = data[3];
2546
Ingo Molnar3593cab2006-02-07 06:49:14 -02002547 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002548 light_exp = cam->params.colourParams.brightness +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002549 TC - 50 + EXP_ACC_LIGHT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002550 if(light_exp > 255)
2551 light_exp = 255;
2552 dark_exp = cam->params.colourParams.brightness +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002553 TC - 50 - EXP_ACC_DARK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002554 if(dark_exp < 0)
2555 dark_exp = 0;
2556 very_dark_exp = dark_exp/2;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002557
Linus Torvalds1da177e2005-04-16 15:20:36 -07002558 old_exposure = cam->params.exposure.coarseExpHi * 256 +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002559 cam->params.exposure.coarseExpLo;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002560
2561 if(!cam->params.flickerControl.disabled) {
2562 /* Flicker control on */
2563 int max_comp = FIRMWARE_VERSION(1,2) ? MAX_COMP : HIGH_COMP_102;
2564 bcomp += 128; /* decode */
2565 if(bcomp >= max_comp && exp_acc < dark_exp) {
2566 /* dark */
2567 if(exp_acc < very_dark_exp) {
2568 /* very dark */
2569 if(cam->exposure_status == EXPOSURE_VERY_DARK)
2570 ++cam->exposure_count;
2571 else {
2572 cam->exposure_status = EXPOSURE_VERY_DARK;
2573 cam->exposure_count = 1;
2574 }
2575 } else {
2576 /* just dark */
2577 if(cam->exposure_status == EXPOSURE_DARK)
2578 ++cam->exposure_count;
2579 else {
2580 cam->exposure_status = EXPOSURE_DARK;
2581 cam->exposure_count = 1;
2582 }
2583 }
2584 } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) {
2585 /* light */
2586 if(old_exposure <= VERY_LOW_EXP) {
2587 /* very light */
2588 if(cam->exposure_status == EXPOSURE_VERY_LIGHT)
2589 ++cam->exposure_count;
2590 else {
2591 cam->exposure_status = EXPOSURE_VERY_LIGHT;
2592 cam->exposure_count = 1;
2593 }
2594 } else {
2595 /* just light */
2596 if(cam->exposure_status == EXPOSURE_LIGHT)
2597 ++cam->exposure_count;
2598 else {
2599 cam->exposure_status = EXPOSURE_LIGHT;
2600 cam->exposure_count = 1;
2601 }
2602 }
2603 } else {
2604 /* not dark or light */
2605 cam->exposure_status = EXPOSURE_NORMAL;
2606 }
2607 } else {
2608 /* Flicker control off */
2609 if(old_exposure >= MAX_EXP && exp_acc < dark_exp) {
2610 /* dark */
2611 if(exp_acc < very_dark_exp) {
2612 /* very dark */
2613 if(cam->exposure_status == EXPOSURE_VERY_DARK)
2614 ++cam->exposure_count;
2615 else {
2616 cam->exposure_status = EXPOSURE_VERY_DARK;
2617 cam->exposure_count = 1;
2618 }
2619 } else {
2620 /* just dark */
2621 if(cam->exposure_status == EXPOSURE_DARK)
2622 ++cam->exposure_count;
2623 else {
2624 cam->exposure_status = EXPOSURE_DARK;
2625 cam->exposure_count = 1;
2626 }
2627 }
2628 } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) {
2629 /* light */
2630 if(old_exposure <= VERY_LOW_EXP) {
2631 /* very light */
2632 if(cam->exposure_status == EXPOSURE_VERY_LIGHT)
2633 ++cam->exposure_count;
2634 else {
2635 cam->exposure_status = EXPOSURE_VERY_LIGHT;
2636 cam->exposure_count = 1;
2637 }
2638 } else {
2639 /* just light */
2640 if(cam->exposure_status == EXPOSURE_LIGHT)
2641 ++cam->exposure_count;
2642 else {
2643 cam->exposure_status = EXPOSURE_LIGHT;
2644 cam->exposure_count = 1;
2645 }
2646 }
2647 } else {
2648 /* not dark or light */
2649 cam->exposure_status = EXPOSURE_NORMAL;
2650 }
2651 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002652
Linus Torvalds1da177e2005-04-16 15:20:36 -07002653 framerate = cam->fps;
2654 if(framerate > 30 || framerate < 1)
2655 framerate = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002656
Linus Torvalds1da177e2005-04-16 15:20:36 -07002657 if(!cam->params.flickerControl.disabled) {
2658 /* Flicker control on */
2659 if((cam->exposure_status == EXPOSURE_VERY_DARK ||
2660 cam->exposure_status == EXPOSURE_DARK) &&
2661 cam->exposure_count >= DARK_TIME*framerate &&
2662 cam->params.sensorFps.divisor < 3) {
2663
2664 /* dark for too long */
2665 ++cam->params.sensorFps.divisor;
2666 cam->cmd_queue |= COMMAND_SETSENSORFPS;
2667
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002668 cam->params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07002669 flicker_jumps[cam->mainsFreq]
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002670 [cam->params.sensorFps.baserate]
2671 [cam->params.sensorFps.divisor];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002672 cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
2673
2674 new_exposure = cam->params.flickerControl.coarseJump-1;
2675 while(new_exposure < old_exposure/2)
2676 new_exposure += cam->params.flickerControl.coarseJump;
2677 cam->params.exposure.coarseExpLo = new_exposure & 0xff;
2678 cam->params.exposure.coarseExpHi = new_exposure >> 8;
2679 cam->cmd_queue |= COMMAND_SETEXPOSURE;
2680 cam->exposure_status = EXPOSURE_NORMAL;
2681 LOG("Automatically decreasing sensor_fps\n");
2682
2683 } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT ||
2684 cam->exposure_status == EXPOSURE_LIGHT) &&
2685 cam->exposure_count >= LIGHT_TIME*framerate &&
2686 cam->params.sensorFps.divisor > 0) {
2687
2688 /* light for too long */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002689 int max_exp = FIRMWARE_VERSION(1,2) ? MAX_EXP_102 : MAX_EXP ;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002690
2691 --cam->params.sensorFps.divisor;
2692 cam->cmd_queue |= COMMAND_SETSENSORFPS;
2693
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002694 cam->params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07002695 flicker_jumps[cam->mainsFreq]
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002696 [cam->params.sensorFps.baserate]
2697 [cam->params.sensorFps.divisor];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002698 cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
2699
2700 new_exposure = cam->params.flickerControl.coarseJump-1;
2701 while(new_exposure < 2*old_exposure &&
2702 new_exposure+
2703 cam->params.flickerControl.coarseJump < max_exp)
2704 new_exposure += cam->params.flickerControl.coarseJump;
2705 cam->params.exposure.coarseExpLo = new_exposure & 0xff;
2706 cam->params.exposure.coarseExpHi = new_exposure >> 8;
2707 cam->cmd_queue |= COMMAND_SETEXPOSURE;
2708 cam->exposure_status = EXPOSURE_NORMAL;
2709 LOG("Automatically increasing sensor_fps\n");
2710 }
2711 } else {
2712 /* Flicker control off */
2713 if((cam->exposure_status == EXPOSURE_VERY_DARK ||
2714 cam->exposure_status == EXPOSURE_DARK) &&
2715 cam->exposure_count >= DARK_TIME*framerate &&
2716 cam->params.sensorFps.divisor < 3) {
2717
2718 /* dark for too long */
2719 ++cam->params.sensorFps.divisor;
2720 cam->cmd_queue |= COMMAND_SETSENSORFPS;
2721
2722 if(cam->params.exposure.gain > 0) {
2723 --cam->params.exposure.gain;
2724 cam->cmd_queue |= COMMAND_SETEXPOSURE;
2725 }
2726 cam->exposure_status = EXPOSURE_NORMAL;
2727 LOG("Automatically decreasing sensor_fps\n");
2728
2729 } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT ||
2730 cam->exposure_status == EXPOSURE_LIGHT) &&
2731 cam->exposure_count >= LIGHT_TIME*framerate &&
2732 cam->params.sensorFps.divisor > 0) {
2733
2734 /* light for too long */
2735 --cam->params.sensorFps.divisor;
2736 cam->cmd_queue |= COMMAND_SETSENSORFPS;
2737
2738 if(cam->params.exposure.gain <
2739 cam->params.exposure.gainMode-1) {
2740 ++cam->params.exposure.gain;
2741 cam->cmd_queue |= COMMAND_SETEXPOSURE;
2742 }
2743 cam->exposure_status = EXPOSURE_NORMAL;
2744 LOG("Automatically increasing sensor_fps\n");
2745 }
2746 }
Ingo Molnar3593cab2006-02-07 06:49:14 -02002747 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002748}
2749
2750/*-----------------------------------------------------------------*/
2751/* if flicker is switched off, this function switches it back on.It checks,
2752 however, that conditions are suitable before restarting it.
2753 This should only be called for firmware version 1.2.
2754
2755 It also adjust the colour balance when an exposure step is detected - as
2756 long as flicker is running
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002757*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07002758static void restart_flicker(struct cam_data *cam)
2759{
2760 int cam_exposure, old_exp;
2761 if(!FIRMWARE_VERSION(1,2))
2762 return;
Ingo Molnar3593cab2006-02-07 06:49:14 -02002763 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002764 if(cam->params.flickerControl.flickerMode == 0 ||
2765 cam->raw_image[39] == 0) {
Ingo Molnar3593cab2006-02-07 06:49:14 -02002766 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002767 return;
2768 }
2769 cam_exposure = cam->raw_image[39]*2;
2770 old_exp = cam->params.exposure.coarseExpLo +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002771 cam->params.exposure.coarseExpHi*256;
2772 /*
2773 see how far away camera exposure is from a valid
2774 flicker exposure value
2775 */
2776 cam_exposure %= cam->params.flickerControl.coarseJump;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002777 if(!cam->params.flickerControl.disabled &&
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002778 cam_exposure <= cam->params.flickerControl.coarseJump - 3) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002779 /* Flicker control auto-disabled */
2780 cam->params.flickerControl.disabled = 1;
2781 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002782
Linus Torvalds1da177e2005-04-16 15:20:36 -07002783 if(cam->params.flickerControl.disabled &&
2784 cam->params.flickerControl.flickerMode &&
2785 old_exp > cam->params.flickerControl.coarseJump +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002786 ROUND_UP_EXP_FOR_FLICKER) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002787 /* exposure is now high enough to switch
2788 flicker control back on */
2789 set_flicker(&cam->params, &cam->cmd_queue, 1);
2790 if((cam->cmd_queue & COMMAND_SETEXPOSURE) &&
2791 cam->params.exposure.expMode == 2)
2792 cam->exposure_status = EXPOSURE_NORMAL;
2793
2794 }
Ingo Molnar3593cab2006-02-07 06:49:14 -02002795 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002796}
2797#undef FIRMWARE_VERSION
2798
2799static int clear_stall(struct cam_data *cam)
2800{
2801 /* FIXME: Does this actually work? */
2802 LOG("Clearing stall\n");
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002803
Linus Torvalds1da177e2005-04-16 15:20:36 -07002804 cam->ops->streamRead(cam->lowlevel_data, cam->raw_image, 0);
2805 do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0);
2806 return cam->params.status.streamState != STREAM_PAUSED;
2807}
2808
2809/* kernel thread function to read image from camera */
2810static int fetch_frame(void *data)
2811{
2812 int image_size, retry;
2813 struct cam_data *cam = (struct cam_data *)data;
2814 unsigned long oldjif, rate, diff;
2815
2816 /* Allow up to two bad images in a row to be read and
2817 * ignored before an error is reported */
2818 for (retry = 0; retry < 3; ++retry) {
2819 if (retry)
2820 DBG("retry=%d\n", retry);
2821
2822 if (!cam->ops)
2823 continue;
2824
2825 /* load first frame always uncompressed */
2826 if (cam->first_frame &&
2827 cam->params.compression.mode != CPIA_COMPRESSION_NONE) {
2828 do_command(cam, CPIA_COMMAND_SetCompression,
2829 CPIA_COMPRESSION_NONE,
2830 NO_DECIMATION, 0, 0);
2831 /* Trial & error - Discarding a frame prevents the
2832 first frame from having an error in the data. */
2833 do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0);
2834 }
2835
2836 /* init camera upload */
2837 if (do_command(cam, CPIA_COMMAND_GrabFrame, 0,
2838 cam->params.streamStartLine, 0, 0))
2839 continue;
2840
2841 if (cam->ops->wait_for_stream_ready) {
2842 /* loop until image ready */
2843 int count = 0;
2844 do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0);
2845 while (cam->params.status.streamState != STREAM_READY) {
2846 if(++count > READY_TIMEOUT)
2847 break;
2848 if(cam->params.status.streamState ==
2849 STREAM_PAUSED) {
2850 /* Bad news */
2851 if(!clear_stall(cam))
2852 return -EIO;
2853 }
2854
2855 cond_resched();
2856
2857 /* sleep for 10 ms, hopefully ;) */
2858 msleep_interruptible(10);
2859 if (signal_pending(current))
2860 return -EINTR;
2861
2862 do_command(cam, CPIA_COMMAND_GetCameraStatus,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002863 0, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002864 }
2865 if(cam->params.status.streamState != STREAM_READY) {
2866 continue;
2867 }
2868 }
2869
2870 cond_resched();
2871
2872 /* grab image from camera */
2873 oldjif = jiffies;
2874 image_size = cam->ops->streamRead(cam->lowlevel_data,
2875 cam->raw_image, 0);
2876 if (image_size <= 0) {
2877 DBG("streamRead failed: %d\n", image_size);
2878 continue;
2879 }
2880
2881 rate = image_size * HZ / 1024;
2882 diff = jiffies-oldjif;
2883 cam->transfer_rate = diff==0 ? rate : rate/diff;
2884 /* diff==0 ? unlikely but possible */
2885
2886 /* Switch flicker control back on if it got turned off */
2887 restart_flicker(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002888
Linus Torvalds1da177e2005-04-16 15:20:36 -07002889 /* If AEC is enabled, monitor the exposure and
2890 adjust the sensor frame rate if needed */
2891 if(cam->params.exposure.expMode == 2)
2892 monitor_exposure(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002893
Linus Torvalds1da177e2005-04-16 15:20:36 -07002894 /* camera idle now so dispatch queued commands */
2895 dispatch_commands(cam);
2896
2897 /* Update our knowledge of the camera state */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002898 do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
2899 do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002900 do_command(cam, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0);
2901
2902 /* decompress and convert image to by copying it from
2903 * raw_image to decompressed_frame
2904 */
2905
2906 cond_resched();
2907
2908 cam->image_size = parse_picture(cam, image_size);
2909 if (cam->image_size <= 0) {
2910 DBG("parse_picture failed %d\n", cam->image_size);
2911 if(cam->params.compression.mode !=
2912 CPIA_COMPRESSION_NONE) {
2913 /* Compression may not work right if we
2914 had a bad frame, get the next one
2915 uncompressed. */
2916 cam->first_frame = 1;
2917 do_command(cam, CPIA_COMMAND_SetGrabMode,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002918 CPIA_GRAB_SINGLE, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002919 /* FIXME: Trial & error - need up to 70ms for
2920 the grab mode change to complete ? */
2921 msleep_interruptible(70);
2922 if (signal_pending(current))
2923 return -EINTR;
2924 }
2925 } else
2926 break;
2927 }
2928
2929 if (retry < 3) {
2930 /* FIXME: this only works for double buffering */
2931 if (cam->frame[cam->curframe].state == FRAME_READY) {
2932 memcpy(cam->frame[cam->curframe].data,
2933 cam->decompressed_frame.data,
2934 cam->decompressed_frame.count);
2935 cam->frame[cam->curframe].state = FRAME_DONE;
2936 } else
2937 cam->decompressed_frame.state = FRAME_DONE;
2938
2939 if (cam->first_frame) {
2940 cam->first_frame = 0;
2941 do_command(cam, CPIA_COMMAND_SetCompression,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002942 cam->params.compression.mode,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002943 cam->params.compression.decimation, 0, 0);
2944
2945 /* Switch from single-grab to continuous grab */
2946 do_command(cam, CPIA_COMMAND_SetGrabMode,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002947 CPIA_GRAB_CONTINUOUS, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002948 }
2949 return 0;
2950 }
2951 return -EIO;
2952}
2953
2954static int capture_frame(struct cam_data *cam, struct video_mmap *vm)
2955{
2956 if (!cam->frame_buf) {
2957 /* we do lazy allocation */
2958 int err;
2959 if ((err = allocate_frame_buf(cam)))
2960 return err;
2961 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002962
Linus Torvalds1da177e2005-04-16 15:20:36 -07002963 cam->curframe = vm->frame;
2964 cam->frame[cam->curframe].state = FRAME_READY;
2965 return fetch_frame(cam);
2966}
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002967
Linus Torvalds1da177e2005-04-16 15:20:36 -07002968static int goto_high_power(struct cam_data *cam)
2969{
2970 if (do_command(cam, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0))
2971 return -EIO;
2972 msleep_interruptible(40); /* windows driver does it too */
2973 if(signal_pending(current))
2974 return -EINTR;
2975 if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
2976 return -EIO;
2977 if (cam->params.status.systemState == HI_POWER_STATE) {
2978 DBG("camera now in HIGH power state\n");
2979 return 0;
2980 }
2981 printstatus(cam);
2982 return -EIO;
2983}
2984
2985static int goto_low_power(struct cam_data *cam)
2986{
2987 if (do_command(cam, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0))
2988 return -1;
2989 if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
2990 return -1;
2991 if (cam->params.status.systemState == LO_POWER_STATE) {
2992 DBG("camera now in LOW power state\n");
2993 return 0;
2994 }
2995 printstatus(cam);
2996 return -1;
2997}
2998
2999static void save_camera_state(struct cam_data *cam)
3000{
3001 if(!(cam->cmd_queue & COMMAND_SETCOLOURBALANCE))
3002 do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
3003 if(!(cam->cmd_queue & COMMAND_SETEXPOSURE))
3004 do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
3005
3006 DBG("%d/%d/%d/%d/%d/%d/%d/%d\n",
3007 cam->params.exposure.gain,
3008 cam->params.exposure.fineExp,
3009 cam->params.exposure.coarseExpLo,
3010 cam->params.exposure.coarseExpHi,
3011 cam->params.exposure.redComp,
3012 cam->params.exposure.green1Comp,
3013 cam->params.exposure.green2Comp,
3014 cam->params.exposure.blueComp);
3015 DBG("%d/%d/%d\n",
3016 cam->params.colourBalance.redGain,
3017 cam->params.colourBalance.greenGain,
3018 cam->params.colourBalance.blueGain);
3019}
3020
3021static int set_camera_state(struct cam_data *cam)
3022{
3023 cam->cmd_queue = COMMAND_SETCOMPRESSION |
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003024 COMMAND_SETCOMPRESSIONTARGET |
3025 COMMAND_SETCOLOURPARAMS |
3026 COMMAND_SETFORMAT |
3027 COMMAND_SETYUVTHRESH |
3028 COMMAND_SETECPTIMING |
3029 COMMAND_SETCOMPRESSIONPARAMS |
3030 COMMAND_SETEXPOSURE |
3031 COMMAND_SETCOLOURBALANCE |
3032 COMMAND_SETSENSORFPS |
3033 COMMAND_SETAPCOR |
3034 COMMAND_SETFLICKERCTRL |
3035 COMMAND_SETVLOFFSET;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003036
3037 do_command(cam, CPIA_COMMAND_SetGrabMode, CPIA_GRAB_SINGLE,0,0,0);
3038 dispatch_commands(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003039
Linus Torvalds1da177e2005-04-16 15:20:36 -07003040 /* Wait 6 frames for the sensor to get all settings and
3041 AEC/ACB to settle */
3042 msleep_interruptible(6*(cam->params.sensorFps.baserate ? 33 : 40) *
3043 (1 << cam->params.sensorFps.divisor) + 10);
3044
3045 if(signal_pending(current))
3046 return -EINTR;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003047
Linus Torvalds1da177e2005-04-16 15:20:36 -07003048 save_camera_state(cam);
3049
3050 return 0;
3051}
3052
3053static void get_version_information(struct cam_data *cam)
3054{
3055 /* GetCPIAVersion */
3056 do_command(cam, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0);
3057
3058 /* GetPnPID */
3059 do_command(cam, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0);
3060}
3061
3062/* initialize camera */
3063static int reset_camera(struct cam_data *cam)
3064{
3065 int err;
3066 /* Start the camera in low power mode */
3067 if (goto_low_power(cam)) {
3068 if (cam->params.status.systemState != WARM_BOOT_STATE)
3069 return -ENODEV;
3070
3071 /* FIXME: this is just dirty trial and error */
3072 err = goto_high_power(cam);
3073 if(err)
3074 return err;
3075 do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0);
3076 if (goto_low_power(cam))
3077 return -ENODEV;
3078 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003079
Linus Torvalds1da177e2005-04-16 15:20:36 -07003080 /* procedure described in developer's guide p3-28 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003081
Linus Torvalds1da177e2005-04-16 15:20:36 -07003082 /* Check the firmware version. */
3083 cam->params.version.firmwareVersion = 0;
3084 get_version_information(cam);
3085 if (cam->params.version.firmwareVersion != 1)
3086 return -ENODEV;
3087
3088 /* A bug in firmware 1-02 limits gainMode to 2 */
3089 if(cam->params.version.firmwareRevision <= 2 &&
3090 cam->params.exposure.gainMode > 2) {
3091 cam->params.exposure.gainMode = 2;
3092 }
3093
3094 /* set QX3 detected flag */
3095 cam->params.qx3.qx3_detected = (cam->params.pnpID.vendor == 0x0813 &&
3096 cam->params.pnpID.product == 0x0001);
3097
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003098 /* The fatal error checking should be done after
Linus Torvalds1da177e2005-04-16 15:20:36 -07003099 * the camera powers up (developer's guide p 3-38) */
3100
3101 /* Set streamState before transition to high power to avoid bug
3102 * in firmware 1-02 */
3103 do_command(cam, CPIA_COMMAND_ModifyCameraStatus, STREAMSTATE, 0,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003104 STREAM_NOT_READY, 0);
3105
Linus Torvalds1da177e2005-04-16 15:20:36 -07003106 /* GotoHiPower */
3107 err = goto_high_power(cam);
3108 if (err)
3109 return err;
3110
3111 /* Check the camera status */
3112 if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
3113 return -EIO;
3114
3115 if (cam->params.status.fatalError) {
3116 DBG("fatal_error: %#04x\n",
3117 cam->params.status.fatalError);
3118 DBG("vp_status: %#04x\n",
3119 cam->params.status.vpStatus);
3120 if (cam->params.status.fatalError & ~(COM_FLAG|CPIA_FLAG)) {
3121 /* Fatal error in camera */
3122 return -EIO;
3123 } else if (cam->params.status.fatalError & (COM_FLAG|CPIA_FLAG)) {
3124 /* Firmware 1-02 may do this for parallel port cameras,
3125 * just clear the flags (developer's guide p 3-38) */
3126 do_command(cam, CPIA_COMMAND_ModifyCameraStatus,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003127 FATALERROR, ~(COM_FLAG|CPIA_FLAG), 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003128 }
3129 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003130
Linus Torvalds1da177e2005-04-16 15:20:36 -07003131 /* Check the camera status again */
3132 if (cam->params.status.fatalError) {
3133 if (cam->params.status.fatalError)
3134 return -EIO;
3135 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003136
Linus Torvalds1da177e2005-04-16 15:20:36 -07003137 /* VPVersion can't be retrieved before the camera is in HiPower,
3138 * so get it here instead of in get_version_information. */
3139 do_command(cam, CPIA_COMMAND_GetVPVersion, 0, 0, 0, 0);
3140
3141 /* set camera to a known state */
3142 return set_camera_state(cam);
3143}
3144
3145static void put_cam(struct cpia_camera_ops* ops)
3146{
Mariusz Kozlowskiac328982007-01-07 10:36:24 -03003147 module_put(ops->owner);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003148}
3149
3150/* ------------------------- V4L interface --------------------- */
3151static int cpia_open(struct inode *inode, struct file *file)
3152{
3153 struct video_device *dev = video_devdata(file);
Hans Verkuil601e9442008-08-23 07:24:07 -03003154 struct cam_data *cam = video_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003155 int err;
3156
3157 if (!cam) {
3158 DBG("Internal error, cam_data not found!\n");
3159 return -ENODEV;
3160 }
3161
3162 if (cam->open_count > 0) {
3163 DBG("Camera already open\n");
3164 return -EBUSY;
3165 }
3166
3167 if (!try_module_get(cam->ops->owner))
3168 return -ENODEV;
3169
Ingo Molnar3593cab2006-02-07 06:49:14 -02003170 mutex_lock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003171 err = -ENOMEM;
3172 if (!cam->raw_image) {
3173 cam->raw_image = rvmalloc(CPIA_MAX_IMAGE_SIZE);
3174 if (!cam->raw_image)
3175 goto oops;
3176 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003177
Linus Torvalds1da177e2005-04-16 15:20:36 -07003178 if (!cam->decompressed_frame.data) {
3179 cam->decompressed_frame.data = rvmalloc(CPIA_MAX_FRAME_SIZE);
3180 if (!cam->decompressed_frame.data)
3181 goto oops;
3182 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003183
Linus Torvalds1da177e2005-04-16 15:20:36 -07003184 /* open cpia */
3185 err = -ENODEV;
3186 if (cam->ops->open(cam->lowlevel_data))
3187 goto oops;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003188
Linus Torvalds1da177e2005-04-16 15:20:36 -07003189 /* reset the camera */
3190 if ((err = reset_camera(cam)) != 0) {
3191 cam->ops->close(cam->lowlevel_data);
3192 goto oops;
3193 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003194
Linus Torvalds1da177e2005-04-16 15:20:36 -07003195 err = -EINTR;
3196 if(signal_pending(current))
3197 goto oops;
3198
3199 /* Set ownership of /proc/cpia/videoX to current user */
3200 if(cam->proc_entry)
David Howellsabc94fc2008-08-27 10:46:39 -03003201 cam->proc_entry->uid = current_uid();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003202
3203 /* set mark for loading first frame uncompressed */
3204 cam->first_frame = 1;
3205
3206 /* init it to something */
3207 cam->mmap_kludge = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003208
Linus Torvalds1da177e2005-04-16 15:20:36 -07003209 ++cam->open_count;
3210 file->private_data = dev;
Ingo Molnar3593cab2006-02-07 06:49:14 -02003211 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003212 return 0;
3213
3214 oops:
3215 if (cam->decompressed_frame.data) {
3216 rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
3217 cam->decompressed_frame.data = NULL;
3218 }
3219 if (cam->raw_image) {
3220 rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
3221 cam->raw_image = NULL;
3222 }
Ingo Molnar3593cab2006-02-07 06:49:14 -02003223 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003224 put_cam(cam->ops);
3225 return err;
3226}
3227
3228static int cpia_close(struct inode *inode, struct file *file)
3229{
3230 struct video_device *dev = file->private_data;
Hans Verkuil601e9442008-08-23 07:24:07 -03003231 struct cam_data *cam = video_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003232
3233 if (cam->ops) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003234 /* Return ownership of /proc/cpia/videoX to root */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003235 if(cam->proc_entry)
3236 cam->proc_entry->uid = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003237
Linus Torvalds1da177e2005-04-16 15:20:36 -07003238 /* save camera state for later open (developers guide ch 3.5.3) */
3239 save_camera_state(cam);
3240
3241 /* GotoLoPower */
3242 goto_low_power(cam);
3243
3244 /* Update the camera status */
3245 do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
3246
3247 /* cleanup internal state stuff */
3248 free_frames(cam->frame);
3249
3250 /* close cpia */
3251 cam->ops->close(cam->lowlevel_data);
3252
3253 put_cam(cam->ops);
3254 }
3255
3256 if (--cam->open_count == 0) {
3257 /* clean up capture-buffers */
3258 if (cam->raw_image) {
3259 rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
3260 cam->raw_image = NULL;
3261 }
3262
3263 if (cam->decompressed_frame.data) {
3264 rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
3265 cam->decompressed_frame.data = NULL;
3266 }
3267
3268 if (cam->frame_buf)
3269 free_frame_buf(cam);
3270
3271 if (!cam->ops)
3272 kfree(cam);
3273 }
3274 file->private_data = NULL;
3275
3276 return 0;
3277}
3278
3279static ssize_t cpia_read(struct file *file, char __user *buf,
3280 size_t count, loff_t *ppos)
3281{
3282 struct video_device *dev = file->private_data;
Hans Verkuil601e9442008-08-23 07:24:07 -03003283 struct cam_data *cam = video_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003284 int err;
3285
3286 /* make this _really_ smp and multithread-safe */
Ingo Molnar3593cab2006-02-07 06:49:14 -02003287 if (mutex_lock_interruptible(&cam->busy_lock))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003288 return -EINTR;
3289
3290 if (!buf) {
3291 DBG("buf NULL\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02003292 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003293 return -EINVAL;
3294 }
3295
3296 if (!count) {
3297 DBG("count 0\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02003298 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003299 return 0;
3300 }
3301
3302 if (!cam->ops) {
3303 DBG("ops NULL\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02003304 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003305 return -ENODEV;
3306 }
3307
3308 /* upload frame */
3309 cam->decompressed_frame.state = FRAME_READY;
3310 cam->mmap_kludge=0;
3311 if((err = fetch_frame(cam)) != 0) {
3312 DBG("ERROR from fetch_frame: %d\n", err);
Ingo Molnar3593cab2006-02-07 06:49:14 -02003313 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003314 return err;
3315 }
3316 cam->decompressed_frame.state = FRAME_UNUSED;
3317
3318 /* copy data to user space */
3319 if (cam->decompressed_frame.count > count) {
3320 DBG("count wrong: %d, %lu\n", cam->decompressed_frame.count,
3321 (unsigned long) count);
Ingo Molnar3593cab2006-02-07 06:49:14 -02003322 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003323 return -EFAULT;
3324 }
3325 if (copy_to_user(buf, cam->decompressed_frame.data,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003326 cam->decompressed_frame.count)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003327 DBG("copy_to_user failed\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02003328 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003329 return -EFAULT;
3330 }
3331
Ingo Molnar3593cab2006-02-07 06:49:14 -02003332 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003333 return cam->decompressed_frame.count;
3334}
3335
Hans Verkuilf473bf72008-11-01 08:25:11 -03003336static int cpia_do_ioctl(struct file *file, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003337{
3338 struct video_device *dev = file->private_data;
Hans Verkuil601e9442008-08-23 07:24:07 -03003339 struct cam_data *cam = video_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003340 int retval = 0;
3341
3342 if (!cam || !cam->ops)
3343 return -ENODEV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003344
Linus Torvalds1da177e2005-04-16 15:20:36 -07003345 /* make this _really_ smp-safe */
Ingo Molnar3593cab2006-02-07 06:49:14 -02003346 if (mutex_lock_interruptible(&cam->busy_lock))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003347 return -EINTR;
3348
Hans Verkuilf473bf72008-11-01 08:25:11 -03003349 /* DBG("cpia_ioctl: %u\n", cmd); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003350
Hans Verkuilf473bf72008-11-01 08:25:11 -03003351 switch (cmd) {
Alexey Dobriyanbe787ac2006-03-07 22:20:23 -03003352 /* query capabilities */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003353 case VIDIOCGCAP:
3354 {
3355 struct video_capability *b = arg;
3356
3357 DBG("VIDIOCGCAP\n");
3358 strcpy(b->name, "CPiA Camera");
3359 b->type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE;
3360 b->channels = 1;
3361 b->audios = 0;
3362 b->maxwidth = 352; /* VIDEOSIZE_CIF */
3363 b->maxheight = 288;
3364 b->minwidth = 48; /* VIDEOSIZE_48_48 */
3365 b->minheight = 48;
3366 break;
3367 }
3368
3369 /* get/set video source - we are a camera and nothing else */
3370 case VIDIOCGCHAN:
3371 {
3372 struct video_channel *v = arg;
3373
3374 DBG("VIDIOCGCHAN\n");
3375 if (v->channel != 0) {
3376 retval = -EINVAL;
3377 break;
3378 }
3379
3380 v->channel = 0;
3381 strcpy(v->name, "Camera");
3382 v->tuners = 0;
3383 v->flags = 0;
3384 v->type = VIDEO_TYPE_CAMERA;
3385 v->norm = 0;
3386 break;
3387 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003388
Linus Torvalds1da177e2005-04-16 15:20:36 -07003389 case VIDIOCSCHAN:
3390 {
3391 struct video_channel *v = arg;
3392
3393 DBG("VIDIOCSCHAN\n");
3394 if (v->channel != 0)
3395 retval = -EINVAL;
3396 break;
3397 }
3398
3399 /* image properties */
3400 case VIDIOCGPICT:
3401 {
3402 struct video_picture *pic = arg;
3403 DBG("VIDIOCGPICT\n");
3404 *pic = cam->vp;
3405 break;
3406 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003407
Linus Torvalds1da177e2005-04-16 15:20:36 -07003408 case VIDIOCSPICT:
3409 {
3410 struct video_picture *vp = arg;
3411
3412 DBG("VIDIOCSPICT\n");
3413
3414 /* check validity */
3415 DBG("palette: %d\n", vp->palette);
3416 DBG("depth: %d\n", vp->depth);
3417 if (!valid_mode(vp->palette, vp->depth)) {
3418 retval = -EINVAL;
3419 break;
3420 }
3421
Ingo Molnar3593cab2006-02-07 06:49:14 -02003422 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003423 /* brightness, colour, contrast need no check 0-65535 */
3424 cam->vp = *vp;
3425 /* update cam->params.colourParams */
3426 cam->params.colourParams.brightness = vp->brightness*100/65535;
3427 cam->params.colourParams.contrast = vp->contrast*100/65535;
3428 cam->params.colourParams.saturation = vp->colour*100/65535;
3429 /* contrast is in steps of 8, so round */
3430 cam->params.colourParams.contrast =
3431 ((cam->params.colourParams.contrast + 3) / 8) * 8;
3432 if (cam->params.version.firmwareVersion == 1 &&
3433 cam->params.version.firmwareRevision == 2 &&
3434 cam->params.colourParams.contrast > 80) {
3435 /* 1-02 firmware limits contrast to 80 */
3436 cam->params.colourParams.contrast = 80;
3437 }
3438
3439 /* Adjust flicker control if necessary */
3440 if(cam->params.flickerControl.allowableOverExposure < 0)
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003441 cam->params.flickerControl.allowableOverExposure =
Linus Torvalds1da177e2005-04-16 15:20:36 -07003442 -find_over_exposure(cam->params.colourParams.brightness);
3443 if(cam->params.flickerControl.flickerMode != 0)
3444 cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003445
Linus Torvalds1da177e2005-04-16 15:20:36 -07003446
3447 /* queue command to update camera */
3448 cam->cmd_queue |= COMMAND_SETCOLOURPARAMS;
Ingo Molnar3593cab2006-02-07 06:49:14 -02003449 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003450 DBG("VIDIOCSPICT: %d / %d // %d / %d / %d / %d\n",
3451 vp->depth, vp->palette, vp->brightness, vp->hue, vp->colour,
3452 vp->contrast);
3453 break;
3454 }
3455
3456 /* get/set capture window */
3457 case VIDIOCGWIN:
3458 {
3459 struct video_window *vw = arg;
3460 DBG("VIDIOCGWIN\n");
3461
3462 *vw = cam->vw;
3463 break;
3464 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003465
Linus Torvalds1da177e2005-04-16 15:20:36 -07003466 case VIDIOCSWIN:
3467 {
3468 /* copy_from_user, check validity, copy to internal structure */
3469 struct video_window *vw = arg;
3470 DBG("VIDIOCSWIN\n");
3471
3472 if (vw->clipcount != 0) { /* clipping not supported */
3473 retval = -EINVAL;
3474 break;
3475 }
3476 if (vw->clips != NULL) { /* clipping not supported */
3477 retval = -EINVAL;
3478 break;
3479 }
3480
3481 /* we set the video window to something smaller or equal to what
3482 * is requested by the user???
3483 */
Ingo Molnar3593cab2006-02-07 06:49:14 -02003484 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003485 if (vw->width != cam->vw.width || vw->height != cam->vw.height) {
3486 int video_size = match_videosize(vw->width, vw->height);
3487
3488 if (video_size < 0) {
3489 retval = -EINVAL;
Ingo Molnar3593cab2006-02-07 06:49:14 -02003490 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003491 break;
3492 }
3493 cam->video_size = video_size;
3494
3495 /* video size is changing, reset the subcapture area */
3496 memset(&cam->vc, 0, sizeof(cam->vc));
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003497
Linus Torvalds1da177e2005-04-16 15:20:36 -07003498 set_vw_size(cam);
3499 DBG("%d / %d\n", cam->vw.width, cam->vw.height);
3500 cam->cmd_queue |= COMMAND_SETFORMAT;
3501 }
3502
Ingo Molnar3593cab2006-02-07 06:49:14 -02003503 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003504
3505 /* setformat ignored by camera during streaming,
3506 * so stop/dispatch/start */
3507 if (cam->cmd_queue & COMMAND_SETFORMAT) {
3508 DBG("\n");
3509 dispatch_commands(cam);
3510 }
3511 DBG("%d/%d:%d\n", cam->video_size,
3512 cam->vw.width, cam->vw.height);
3513 break;
3514 }
3515
3516 /* mmap interface */
3517 case VIDIOCGMBUF:
3518 {
3519 struct video_mbuf *vm = arg;
3520 int i;
3521
3522 DBG("VIDIOCGMBUF\n");
3523 memset(vm, 0, sizeof(*vm));
3524 vm->size = CPIA_MAX_FRAME_SIZE*FRAME_NUM;
3525 vm->frames = FRAME_NUM;
3526 for (i = 0; i < FRAME_NUM; i++)
3527 vm->offsets[i] = CPIA_MAX_FRAME_SIZE * i;
3528 break;
3529 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003530
Linus Torvalds1da177e2005-04-16 15:20:36 -07003531 case VIDIOCMCAPTURE:
3532 {
3533 struct video_mmap *vm = arg;
3534 int video_size;
3535
3536 DBG("VIDIOCMCAPTURE: %d / %d / %dx%d\n", vm->format, vm->frame,
3537 vm->width, vm->height);
3538 if (vm->frame<0||vm->frame>=FRAME_NUM) {
3539 retval = -EINVAL;
3540 break;
3541 }
3542
3543 /* set video format */
3544 cam->vp.palette = vm->format;
3545 switch(vm->format) {
3546 case VIDEO_PALETTE_GREY:
3547 cam->vp.depth=8;
3548 break;
3549 case VIDEO_PALETTE_RGB555:
3550 case VIDEO_PALETTE_RGB565:
3551 case VIDEO_PALETTE_YUV422:
3552 case VIDEO_PALETTE_YUYV:
3553 case VIDEO_PALETTE_UYVY:
3554 cam->vp.depth = 16;
3555 break;
3556 case VIDEO_PALETTE_RGB24:
3557 cam->vp.depth = 24;
3558 break;
3559 case VIDEO_PALETTE_RGB32:
3560 cam->vp.depth = 32;
3561 break;
3562 default:
3563 retval = -EINVAL;
3564 break;
3565 }
3566 if (retval)
3567 break;
3568
3569 /* set video size */
3570 video_size = match_videosize(vm->width, vm->height);
3571 if (video_size < 0) {
3572 retval = -EINVAL;
3573 break;
3574 }
3575 if (video_size != cam->video_size) {
3576 cam->video_size = video_size;
3577
3578 /* video size is changing, reset the subcapture area */
3579 memset(&cam->vc, 0, sizeof(cam->vc));
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003580
Linus Torvalds1da177e2005-04-16 15:20:36 -07003581 set_vw_size(cam);
3582 cam->cmd_queue |= COMMAND_SETFORMAT;
3583 dispatch_commands(cam);
3584 }
3585 /* according to v4l-spec we must start streaming here */
3586 cam->mmap_kludge = 1;
3587 retval = capture_frame(cam, vm);
3588
3589 break;
3590 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003591
Linus Torvalds1da177e2005-04-16 15:20:36 -07003592 case VIDIOCSYNC:
3593 {
3594 int *frame = arg;
3595
3596 //DBG("VIDIOCSYNC: %d\n", *frame);
3597
3598 if (*frame<0 || *frame >= FRAME_NUM) {
3599 retval = -EINVAL;
3600 break;
3601 }
3602
3603 switch (cam->frame[*frame].state) {
3604 case FRAME_UNUSED:
3605 case FRAME_READY:
3606 case FRAME_GRABBING:
3607 DBG("sync to unused frame %d\n", *frame);
3608 retval = -EINVAL;
3609 break;
3610
3611 case FRAME_DONE:
3612 cam->frame[*frame].state = FRAME_UNUSED;
3613 //DBG("VIDIOCSYNC: %d synced\n", *frame);
3614 break;
3615 }
3616 if (retval == -EINTR) {
3617 /* FIXME - xawtv does not handle this nice */
3618 retval = 0;
3619 }
3620 break;
3621 }
3622
3623 case VIDIOCGCAPTURE:
3624 {
3625 struct video_capture *vc = arg;
3626
3627 DBG("VIDIOCGCAPTURE\n");
3628
3629 *vc = cam->vc;
3630
3631 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003632 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003633
3634 case VIDIOCSCAPTURE:
3635 {
3636 struct video_capture *vc = arg;
3637
3638 DBG("VIDIOCSCAPTURE\n");
3639
3640 if (vc->decimation != 0) { /* How should this be used? */
3641 retval = -EINVAL;
3642 break;
3643 }
3644 if (vc->flags != 0) { /* Even/odd grab not supported */
3645 retval = -EINVAL;
3646 break;
3647 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003648
Linus Torvalds1da177e2005-04-16 15:20:36 -07003649 /* Clip to the resolution we can set for the ROI
3650 (every 8 columns and 4 rows) */
3651 vc->x = vc->x & ~(__u32)7;
3652 vc->y = vc->y & ~(__u32)3;
3653 vc->width = vc->width & ~(__u32)7;
3654 vc->height = vc->height & ~(__u32)3;
3655
3656 if(vc->width == 0 || vc->height == 0 ||
3657 vc->x + vc->width > cam->vw.width ||
3658 vc->y + vc->height > cam->vw.height) {
3659 retval = -EINVAL;
3660 break;
3661 }
3662
3663 DBG("%d,%d/%dx%d\n", vc->x,vc->y,vc->width, vc->height);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003664
Ingo Molnar3593cab2006-02-07 06:49:14 -02003665 mutex_lock(&cam->param_lock);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003666
Linus Torvalds1da177e2005-04-16 15:20:36 -07003667 cam->vc.x = vc->x;
3668 cam->vc.y = vc->y;
3669 cam->vc.width = vc->width;
3670 cam->vc.height = vc->height;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003671
Linus Torvalds1da177e2005-04-16 15:20:36 -07003672 set_vw_size(cam);
3673 cam->cmd_queue |= COMMAND_SETFORMAT;
3674
Ingo Molnar3593cab2006-02-07 06:49:14 -02003675 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003676
3677 /* setformat ignored by camera during streaming,
3678 * so stop/dispatch/start */
3679 dispatch_commands(cam);
3680 break;
3681 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003682
Linus Torvalds1da177e2005-04-16 15:20:36 -07003683 case VIDIOCGUNIT:
3684 {
3685 struct video_unit *vu = arg;
3686
3687 DBG("VIDIOCGUNIT\n");
3688
3689 vu->video = cam->vdev.minor;
3690 vu->vbi = VIDEO_NO_UNIT;
3691 vu->radio = VIDEO_NO_UNIT;
3692 vu->audio = VIDEO_NO_UNIT;
3693 vu->teletext = VIDEO_NO_UNIT;
3694
3695 break;
3696 }
3697
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003698
Linus Torvalds1da177e2005-04-16 15:20:36 -07003699 /* pointless to implement overlay with this camera */
3700 case VIDIOCCAPTURE:
3701 case VIDIOCGFBUF:
3702 case VIDIOCSFBUF:
3703 case VIDIOCKEY:
3704 /* tuner interface - we have none */
3705 case VIDIOCGTUNER:
3706 case VIDIOCSTUNER:
3707 case VIDIOCGFREQ:
3708 case VIDIOCSFREQ:
3709 /* audio interface - we have none */
3710 case VIDIOCGAUDIO:
3711 case VIDIOCSAUDIO:
3712 retval = -EINVAL;
3713 break;
3714 default:
3715 retval = -ENOIOCTLCMD;
3716 break;
3717 }
3718
Ingo Molnar3593cab2006-02-07 06:49:14 -02003719 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003720 return retval;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003721}
Linus Torvalds1da177e2005-04-16 15:20:36 -07003722
3723static int cpia_ioctl(struct inode *inode, struct file *file,
3724 unsigned int cmd, unsigned long arg)
3725{
Hans Verkuilf473bf72008-11-01 08:25:11 -03003726 return video_usercopy(file, cmd, arg, cpia_do_ioctl);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003727}
3728
3729
3730/* FIXME */
3731static int cpia_mmap(struct file *file, struct vm_area_struct *vma)
3732{
3733 struct video_device *dev = file->private_data;
3734 unsigned long start = vma->vm_start;
3735 unsigned long size = vma->vm_end - vma->vm_start;
3736 unsigned long page, pos;
Hans Verkuil601e9442008-08-23 07:24:07 -03003737 struct cam_data *cam = video_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003738 int retval;
3739
3740 if (!cam || !cam->ops)
3741 return -ENODEV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003742
Linus Torvalds1da177e2005-04-16 15:20:36 -07003743 DBG("cpia_mmap: %ld\n", size);
3744
3745 if (size > FRAME_NUM*CPIA_MAX_FRAME_SIZE)
3746 return -EINVAL;
3747
3748 if (!cam || !cam->ops)
3749 return -ENODEV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003750
Linus Torvalds1da177e2005-04-16 15:20:36 -07003751 /* make this _really_ smp-safe */
Ingo Molnar3593cab2006-02-07 06:49:14 -02003752 if (mutex_lock_interruptible(&cam->busy_lock))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003753 return -EINTR;
3754
3755 if (!cam->frame_buf) { /* we do lazy allocation */
3756 if ((retval = allocate_frame_buf(cam))) {
Ingo Molnar3593cab2006-02-07 06:49:14 -02003757 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003758 return retval;
3759 }
3760 }
3761
3762 pos = (unsigned long)(cam->frame_buf);
3763 while (size > 0) {
3764 page = vmalloc_to_pfn((void *)pos);
3765 if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) {
Ingo Molnar3593cab2006-02-07 06:49:14 -02003766 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003767 return -EAGAIN;
3768 }
3769 start += PAGE_SIZE;
3770 pos += PAGE_SIZE;
3771 if (size > PAGE_SIZE)
3772 size -= PAGE_SIZE;
3773 else
3774 size = 0;
3775 }
3776
3777 DBG("cpia_mmap: %ld\n", size);
Ingo Molnar3593cab2006-02-07 06:49:14 -02003778 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003779
3780 return 0;
3781}
3782
Arjan van de Venfa027c22007-02-12 00:55:33 -08003783static const struct file_operations cpia_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003784 .owner = THIS_MODULE,
3785 .open = cpia_open,
3786 .release = cpia_close,
3787 .read = cpia_read,
3788 .mmap = cpia_mmap,
3789 .ioctl = cpia_ioctl,
Douglas Schilling Landgraf078ff792008-04-22 14:46:11 -03003790#ifdef CONFIG_COMPAT
Arnd Bergmann0d0fbf82006-01-09 15:24:57 -02003791 .compat_ioctl = v4l_compat_ioctl32,
Douglas Schilling Landgraf078ff792008-04-22 14:46:11 -03003792#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07003793 .llseek = no_llseek,
3794};
3795
3796static struct video_device cpia_template = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003797 .name = "CPiA Camera",
Linus Torvalds1da177e2005-04-16 15:20:36 -07003798 .fops = &cpia_fops,
Hans Verkuilaa5e90a2008-08-23 06:23:55 -03003799 .release = video_device_release_empty,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003800};
3801
3802/* initialise cam_data structure */
3803static void reset_camera_struct(struct cam_data *cam)
3804{
3805 /* The following parameter values are the defaults from
3806 * "Software Developer's Guide for CPiA Cameras". Any changes
3807 * to the defaults are noted in comments. */
3808 cam->params.colourParams.brightness = 50;
3809 cam->params.colourParams.contrast = 48;
3810 cam->params.colourParams.saturation = 50;
3811 cam->params.exposure.gainMode = 4;
3812 cam->params.exposure.expMode = 2; /* AEC */
3813 cam->params.exposure.compMode = 1;
3814 cam->params.exposure.centreWeight = 1;
3815 cam->params.exposure.gain = 0;
3816 cam->params.exposure.fineExp = 0;
3817 cam->params.exposure.coarseExpLo = 185;
3818 cam->params.exposure.coarseExpHi = 0;
3819 cam->params.exposure.redComp = COMP_RED;
3820 cam->params.exposure.green1Comp = COMP_GREEN1;
3821 cam->params.exposure.green2Comp = COMP_GREEN2;
3822 cam->params.exposure.blueComp = COMP_BLUE;
3823 cam->params.colourBalance.balanceMode = 2; /* ACB */
3824 cam->params.colourBalance.redGain = 32;
3825 cam->params.colourBalance.greenGain = 6;
3826 cam->params.colourBalance.blueGain = 92;
3827 cam->params.apcor.gain1 = 0x18;
3828 cam->params.apcor.gain2 = 0x16;
3829 cam->params.apcor.gain4 = 0x24;
3830 cam->params.apcor.gain8 = 0x34;
3831 cam->params.flickerControl.flickerMode = 0;
3832 cam->params.flickerControl.disabled = 1;
3833
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003834 cam->params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07003835 flicker_jumps[cam->mainsFreq]
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003836 [cam->params.sensorFps.baserate]
3837 [cam->params.sensorFps.divisor];
3838 cam->params.flickerControl.allowableOverExposure =
Linus Torvalds1da177e2005-04-16 15:20:36 -07003839 -find_over_exposure(cam->params.colourParams.brightness);
3840 cam->params.vlOffset.gain1 = 20;
3841 cam->params.vlOffset.gain2 = 24;
3842 cam->params.vlOffset.gain4 = 26;
3843 cam->params.vlOffset.gain8 = 26;
3844 cam->params.compressionParams.hysteresis = 3;
3845 cam->params.compressionParams.threshMax = 11;
3846 cam->params.compressionParams.smallStep = 1;
3847 cam->params.compressionParams.largeStep = 3;
3848 cam->params.compressionParams.decimationHysteresis = 2;
3849 cam->params.compressionParams.frDiffStepThresh = 5;
3850 cam->params.compressionParams.qDiffStepThresh = 3;
3851 cam->params.compressionParams.decimationThreshMod = 2;
3852 /* End of default values from Software Developer's Guide */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003853
Linus Torvalds1da177e2005-04-16 15:20:36 -07003854 cam->transfer_rate = 0;
3855 cam->exposure_status = EXPOSURE_NORMAL;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003856
Linus Torvalds1da177e2005-04-16 15:20:36 -07003857 /* Set Sensor FPS to 15fps. This seems better than 30fps
3858 * for indoor lighting. */
3859 cam->params.sensorFps.divisor = 1;
3860 cam->params.sensorFps.baserate = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003861
Linus Torvalds1da177e2005-04-16 15:20:36 -07003862 cam->params.yuvThreshold.yThreshold = 6; /* From windows driver */
3863 cam->params.yuvThreshold.uvThreshold = 6; /* From windows driver */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003864
Linus Torvalds1da177e2005-04-16 15:20:36 -07003865 cam->params.format.subSample = SUBSAMPLE_422;
3866 cam->params.format.yuvOrder = YUVORDER_YUYV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003867
Linus Torvalds1da177e2005-04-16 15:20:36 -07003868 cam->params.compression.mode = CPIA_COMPRESSION_AUTO;
3869 cam->params.compressionTarget.frTargeting =
3870 CPIA_COMPRESSION_TARGET_QUALITY;
3871 cam->params.compressionTarget.targetFR = 15; /* From windows driver */
3872 cam->params.compressionTarget.targetQ = 5; /* From windows driver */
3873
3874 cam->params.qx3.qx3_detected = 0;
3875 cam->params.qx3.toplight = 0;
3876 cam->params.qx3.bottomlight = 0;
3877 cam->params.qx3.button = 0;
3878 cam->params.qx3.cradled = 0;
3879
3880 cam->video_size = VIDEOSIZE_CIF;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003881
Linus Torvalds1da177e2005-04-16 15:20:36 -07003882 cam->vp.colour = 32768; /* 50% */
3883 cam->vp.hue = 32768; /* 50% */
3884 cam->vp.brightness = 32768; /* 50% */
3885 cam->vp.contrast = 32768; /* 50% */
3886 cam->vp.whiteness = 0; /* not used -> grayscale only */
3887 cam->vp.depth = 24; /* to be set by user */
3888 cam->vp.palette = VIDEO_PALETTE_RGB24; /* to be set by user */
3889
3890 cam->vc.x = 0;
3891 cam->vc.y = 0;
3892 cam->vc.width = 0;
3893 cam->vc.height = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003894
Linus Torvalds1da177e2005-04-16 15:20:36 -07003895 cam->vw.x = 0;
3896 cam->vw.y = 0;
3897 set_vw_size(cam);
3898 cam->vw.chromakey = 0;
3899 cam->vw.flags = 0;
3900 cam->vw.clipcount = 0;
3901 cam->vw.clips = NULL;
3902
3903 cam->cmd_queue = COMMAND_NONE;
3904 cam->first_frame = 1;
3905
3906 return;
3907}
3908
3909/* initialize cam_data structure */
3910static void init_camera_struct(struct cam_data *cam,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003911 struct cpia_camera_ops *ops )
Linus Torvalds1da177e2005-04-16 15:20:36 -07003912{
3913 int i;
3914
3915 /* Default everything to 0 */
3916 memset(cam, 0, sizeof(struct cam_data));
3917
3918 cam->ops = ops;
Ingo Molnar3593cab2006-02-07 06:49:14 -02003919 mutex_init(&cam->param_lock);
3920 mutex_init(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003921
3922 reset_camera_struct(cam);
3923
3924 cam->proc_entry = NULL;
3925
3926 memcpy(&cam->vdev, &cpia_template, sizeof(cpia_template));
Hans Verkuil601e9442008-08-23 07:24:07 -03003927 video_set_drvdata(&cam->vdev, cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003928
Linus Torvalds1da177e2005-04-16 15:20:36 -07003929 cam->curframe = 0;
3930 for (i = 0; i < FRAME_NUM; i++) {
3931 cam->frame[i].width = 0;
3932 cam->frame[i].height = 0;
3933 cam->frame[i].state = FRAME_UNUSED;
3934 cam->frame[i].data = NULL;
3935 }
3936 cam->decompressed_frame.width = 0;
3937 cam->decompressed_frame.height = 0;
3938 cam->decompressed_frame.state = FRAME_UNUSED;
3939 cam->decompressed_frame.data = NULL;
3940}
3941
3942struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel)
3943{
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003944 struct cam_data *camera;
3945
Linus Torvalds1da177e2005-04-16 15:20:36 -07003946 if ((camera = kmalloc(sizeof(struct cam_data), GFP_KERNEL)) == NULL)
3947 return NULL;
3948
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003949
Linus Torvalds1da177e2005-04-16 15:20:36 -07003950 init_camera_struct( camera, ops );
3951 camera->lowlevel_data = lowlevel;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003952
Linus Torvalds1da177e2005-04-16 15:20:36 -07003953 /* register v4l device */
Hans Verkuildc60de32008-09-03 17:11:58 -03003954 if (video_register_device(&camera->vdev, VFL_TYPE_GRABBER, video_nr) < 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003955 kfree(camera);
3956 printk(KERN_DEBUG "video_register_device failed\n");
3957 return NULL;
3958 }
3959
3960 /* get version information from camera: open/reset/close */
3961
3962 /* open cpia */
3963 if (camera->ops->open(camera->lowlevel_data))
3964 return camera;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003965
Linus Torvalds1da177e2005-04-16 15:20:36 -07003966 /* reset the camera */
3967 if (reset_camera(camera) != 0) {
3968 camera->ops->close(camera->lowlevel_data);
3969 return camera;
3970 }
3971
3972 /* close cpia */
3973 camera->ops->close(camera->lowlevel_data);
3974
3975#ifdef CONFIG_PROC_FS
3976 create_proc_cpia_cam(camera);
3977#endif
3978
3979 printk(KERN_INFO " CPiA Version: %d.%02d (%d.%d)\n",
3980 camera->params.version.firmwareVersion,
3981 camera->params.version.firmwareRevision,
3982 camera->params.version.vcVersion,
3983 camera->params.version.vcRevision);
3984 printk(KERN_INFO " CPiA PnP-ID: %04x:%04x:%04x\n",
3985 camera->params.pnpID.vendor,
3986 camera->params.pnpID.product,
3987 camera->params.pnpID.deviceRevision);
3988 printk(KERN_INFO " VP-Version: %d.%d %04x\n",
3989 camera->params.vpVersion.vpVersion,
3990 camera->params.vpVersion.vpRevision,
3991 camera->params.vpVersion.cameraHeadID);
3992
3993 return camera;
3994}
3995
3996void cpia_unregister_camera(struct cam_data *cam)
3997{
3998 DBG("unregistering video\n");
3999 video_unregister_device(&cam->vdev);
4000 if (cam->open_count) {
4001 put_cam(cam->ops);
4002 DBG("camera open -- setting ops to NULL\n");
4003 cam->ops = NULL;
4004 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03004005
Linus Torvalds1da177e2005-04-16 15:20:36 -07004006#ifdef CONFIG_PROC_FS
Hans Verkuilc6330fb2008-10-19 18:54:26 -03004007 DBG("destroying /proc/cpia/video%d\n", cam->vdev.num);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004008 destroy_proc_cpia_cam(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03004009#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07004010 if (!cam->open_count) {
4011 DBG("freeing camera\n");
4012 kfree(cam);
4013 }
4014}
4015
4016static int __init cpia_init(void)
4017{
4018 printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT,
4019 CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER);
4020
4021 printk(KERN_WARNING "Since in-kernel colorspace conversion is not "
4022 "allowed, it is disabled by default now. Users should fix the "
4023 "applications in case they don't work without conversion "
4024 "reenabled by setting the 'colorspace_conv' module "
Randy Dunlap94190452006-03-27 16:18:25 -03004025 "parameter to 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004026
4027#ifdef CONFIG_PROC_FS
4028 proc_cpia_create();
4029#endif
4030
Linus Torvalds1da177e2005-04-16 15:20:36 -07004031 return 0;
4032}
4033
4034static void __exit cpia_exit(void)
4035{
4036#ifdef CONFIG_PROC_FS
4037 proc_cpia_destroy();
4038#endif
4039}
4040
4041module_init(cpia_init);
4042module_exit(cpia_exit);
4043
4044/* Exported symbols for modules. */
4045
4046EXPORT_SYMBOL(cpia_register_camera);
4047EXPORT_SYMBOL(cpia_unregister_camera);