blob: 43ab0adf3b610a8927299e833c6bb3ab5f3b73ad [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
Alexey Dobriyan99b76232009-03-25 22:48:06 +03001384 if (!cpia_proc_root)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001385 LOG("Unable to initialise /proc/cpia\n");
1386}
1387
1388static void __exit proc_cpia_destroy(void)
1389{
1390 remove_proc_entry("cpia", NULL);
1391}
1392#endif /* CONFIG_PROC_FS */
1393
1394/* ----------------------- debug functions ---------------------- */
1395
1396#define printstatus(cam) \
1397 DBG("%02x %02x %02x %02x %02x %02x %02x %02x\n",\
1398 cam->params.status.systemState, cam->params.status.grabState, \
1399 cam->params.status.streamState, cam->params.status.fatalError, \
1400 cam->params.status.cmdError, cam->params.status.debugFlags, \
1401 cam->params.status.vpStatus, cam->params.status.errorCode);
1402
1403/* ----------------------- v4l helpers -------------------------- */
1404
1405/* supported frame palettes and depths */
1406static inline int valid_mode(u16 palette, u16 depth)
1407{
1408 if ((palette == VIDEO_PALETTE_YUV422 && depth == 16) ||
1409 (palette == VIDEO_PALETTE_YUYV && depth == 16))
1410 return 1;
1411
1412 if (colorspace_conv)
1413 return (palette == VIDEO_PALETTE_GREY && depth == 8) ||
1414 (palette == VIDEO_PALETTE_RGB555 && depth == 16) ||
1415 (palette == VIDEO_PALETTE_RGB565 && depth == 16) ||
1416 (palette == VIDEO_PALETTE_RGB24 && depth == 24) ||
1417 (palette == VIDEO_PALETTE_RGB32 && depth == 32) ||
1418 (palette == VIDEO_PALETTE_UYVY && depth == 16);
1419
1420 return 0;
1421}
1422
1423static int match_videosize( int width, int height )
1424{
1425 /* return the best match, where 'best' is as always
1426 * the largest that is not bigger than what is requested. */
1427 if (width>=352 && height>=288)
1428 return VIDEOSIZE_352_288; /* CIF */
1429
1430 if (width>=320 && height>=240)
1431 return VIDEOSIZE_320_240; /* SIF */
1432
1433 if (width>=288 && height>=216)
1434 return VIDEOSIZE_288_216;
1435
1436 if (width>=256 && height>=192)
1437 return VIDEOSIZE_256_192;
1438
1439 if (width>=224 && height>=168)
1440 return VIDEOSIZE_224_168;
1441
1442 if (width>=192 && height>=144)
1443 return VIDEOSIZE_192_144;
1444
1445 if (width>=176 && height>=144)
1446 return VIDEOSIZE_176_144; /* QCIF */
1447
1448 if (width>=160 && height>=120)
1449 return VIDEOSIZE_160_120; /* QSIF */
1450
1451 if (width>=128 && height>=96)
1452 return VIDEOSIZE_128_96;
1453
1454 if (width>=88 && height>=72)
1455 return VIDEOSIZE_88_72;
1456
1457 if (width>=64 && height>=48)
1458 return VIDEOSIZE_64_48;
1459
1460 if (width>=48 && height>=48)
1461 return VIDEOSIZE_48_48;
1462
1463 return -1;
1464}
1465
1466/* these are the capture sizes we support */
1467static void set_vw_size(struct cam_data *cam)
1468{
1469 /* the col/row/start/end values are the result of simple math */
1470 /* study the SetROI-command in cpia developers guide p 2-22 */
1471 /* streamStartLine is set to the recommended value in the cpia */
1472 /* developers guide p 3-37 */
1473 switch(cam->video_size) {
1474 case VIDEOSIZE_CIF:
1475 cam->vw.width = 352;
1476 cam->vw.height = 288;
1477 cam->params.format.videoSize=VIDEOSIZE_CIF;
1478 cam->params.roi.colStart=0;
1479 cam->params.roi.rowStart=0;
1480 cam->params.streamStartLine = 120;
1481 break;
1482 case VIDEOSIZE_SIF:
1483 cam->vw.width = 320;
1484 cam->vw.height = 240;
1485 cam->params.format.videoSize=VIDEOSIZE_CIF;
1486 cam->params.roi.colStart=2;
1487 cam->params.roi.rowStart=6;
1488 cam->params.streamStartLine = 120;
1489 break;
1490 case VIDEOSIZE_288_216:
1491 cam->vw.width = 288;
1492 cam->vw.height = 216;
1493 cam->params.format.videoSize=VIDEOSIZE_CIF;
1494 cam->params.roi.colStart=4;
1495 cam->params.roi.rowStart=9;
1496 cam->params.streamStartLine = 120;
1497 break;
1498 case VIDEOSIZE_256_192:
1499 cam->vw.width = 256;
1500 cam->vw.height = 192;
1501 cam->params.format.videoSize=VIDEOSIZE_CIF;
1502 cam->params.roi.colStart=6;
1503 cam->params.roi.rowStart=12;
1504 cam->params.streamStartLine = 120;
1505 break;
1506 case VIDEOSIZE_224_168:
1507 cam->vw.width = 224;
1508 cam->vw.height = 168;
1509 cam->params.format.videoSize=VIDEOSIZE_CIF;
1510 cam->params.roi.colStart=8;
1511 cam->params.roi.rowStart=15;
1512 cam->params.streamStartLine = 120;
1513 break;
1514 case VIDEOSIZE_192_144:
1515 cam->vw.width = 192;
1516 cam->vw.height = 144;
1517 cam->params.format.videoSize=VIDEOSIZE_CIF;
1518 cam->params.roi.colStart=10;
1519 cam->params.roi.rowStart=18;
1520 cam->params.streamStartLine = 120;
1521 break;
1522 case VIDEOSIZE_QCIF:
1523 cam->vw.width = 176;
1524 cam->vw.height = 144;
1525 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1526 cam->params.roi.colStart=0;
1527 cam->params.roi.rowStart=0;
1528 cam->params.streamStartLine = 60;
1529 break;
1530 case VIDEOSIZE_QSIF:
1531 cam->vw.width = 160;
1532 cam->vw.height = 120;
1533 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1534 cam->params.roi.colStart=1;
1535 cam->params.roi.rowStart=3;
1536 cam->params.streamStartLine = 60;
1537 break;
1538 case VIDEOSIZE_128_96:
1539 cam->vw.width = 128;
1540 cam->vw.height = 96;
1541 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1542 cam->params.roi.colStart=3;
1543 cam->params.roi.rowStart=6;
1544 cam->params.streamStartLine = 60;
1545 break;
1546 case VIDEOSIZE_88_72:
1547 cam->vw.width = 88;
1548 cam->vw.height = 72;
1549 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1550 cam->params.roi.colStart=5;
1551 cam->params.roi.rowStart=9;
1552 cam->params.streamStartLine = 60;
1553 break;
1554 case VIDEOSIZE_64_48:
1555 cam->vw.width = 64;
1556 cam->vw.height = 48;
1557 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1558 cam->params.roi.colStart=7;
1559 cam->params.roi.rowStart=12;
1560 cam->params.streamStartLine = 60;
1561 break;
1562 case VIDEOSIZE_48_48:
1563 cam->vw.width = 48;
1564 cam->vw.height = 48;
1565 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1566 cam->params.roi.colStart=8;
1567 cam->params.roi.rowStart=6;
1568 cam->params.streamStartLine = 60;
1569 break;
1570 default:
1571 LOG("bad videosize value: %d\n", cam->video_size);
1572 return;
1573 }
1574
1575 if(cam->vc.width == 0)
1576 cam->vc.width = cam->vw.width;
1577 if(cam->vc.height == 0)
1578 cam->vc.height = cam->vw.height;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001579
Linus Torvalds1da177e2005-04-16 15:20:36 -07001580 cam->params.roi.colStart += cam->vc.x >> 3;
1581 cam->params.roi.colEnd = cam->params.roi.colStart +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001582 (cam->vc.width >> 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001583 cam->params.roi.rowStart += cam->vc.y >> 2;
1584 cam->params.roi.rowEnd = cam->params.roi.rowStart +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001585 (cam->vc.height >> 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001586
1587 return;
1588}
1589
1590static int allocate_frame_buf(struct cam_data *cam)
1591{
1592 int i;
1593
1594 cam->frame_buf = rvmalloc(FRAME_NUM * CPIA_MAX_FRAME_SIZE);
1595 if (!cam->frame_buf)
1596 return -ENOBUFS;
1597
1598 for (i = 0; i < FRAME_NUM; i++)
1599 cam->frame[i].data = cam->frame_buf + i * CPIA_MAX_FRAME_SIZE;
1600
1601 return 0;
1602}
1603
1604static int free_frame_buf(struct cam_data *cam)
1605{
1606 int i;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001607
Linus Torvalds1da177e2005-04-16 15:20:36 -07001608 rvfree(cam->frame_buf, FRAME_NUM*CPIA_MAX_FRAME_SIZE);
1609 cam->frame_buf = NULL;
1610 for (i=0; i < FRAME_NUM; i++)
1611 cam->frame[i].data = NULL;
1612
1613 return 0;
1614}
1615
1616
1617static inline void free_frames(struct cpia_frame frame[FRAME_NUM])
1618{
1619 int i;
1620
1621 for (i=0; i < FRAME_NUM; i++)
1622 frame[i].state = FRAME_UNUSED;
1623 return;
1624}
1625
1626/**********************************************************************
1627 *
1628 * General functions
1629 *
1630 **********************************************************************/
1631/* send an arbitrary command to the camera */
1632static int do_command(struct cam_data *cam, u16 command, u8 a, u8 b, u8 c, u8 d)
1633{
1634 int retval, datasize;
1635 u8 cmd[8], data[8];
1636
1637 switch(command) {
1638 case CPIA_COMMAND_GetCPIAVersion:
1639 case CPIA_COMMAND_GetPnPID:
1640 case CPIA_COMMAND_GetCameraStatus:
1641 case CPIA_COMMAND_GetVPVersion:
1642 datasize=8;
1643 break;
1644 case CPIA_COMMAND_GetColourParams:
1645 case CPIA_COMMAND_GetColourBalance:
1646 case CPIA_COMMAND_GetExposure:
Ingo Molnar3593cab2006-02-07 06:49:14 -02001647 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001648 datasize=8;
1649 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001650 case CPIA_COMMAND_ReadMCPorts:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001651 case CPIA_COMMAND_ReadVCRegs:
1652 datasize = 4;
1653 break;
1654 default:
1655 datasize=0;
1656 break;
1657 }
1658
1659 cmd[0] = command>>8;
1660 cmd[1] = command&0xff;
1661 cmd[2] = a;
1662 cmd[3] = b;
1663 cmd[4] = c;
1664 cmd[5] = d;
1665 cmd[6] = datasize;
1666 cmd[7] = 0;
1667
1668 retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
1669 if (retval) {
1670 DBG("%x - failed, retval=%d\n", command, retval);
1671 if (command == CPIA_COMMAND_GetColourParams ||
1672 command == CPIA_COMMAND_GetColourBalance ||
1673 command == CPIA_COMMAND_GetExposure)
Ingo Molnar3593cab2006-02-07 06:49:14 -02001674 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001675 } else {
1676 switch(command) {
1677 case CPIA_COMMAND_GetCPIAVersion:
1678 cam->params.version.firmwareVersion = data[0];
1679 cam->params.version.firmwareRevision = data[1];
1680 cam->params.version.vcVersion = data[2];
1681 cam->params.version.vcRevision = data[3];
1682 break;
1683 case CPIA_COMMAND_GetPnPID:
1684 cam->params.pnpID.vendor = data[0]+(((u16)data[1])<<8);
1685 cam->params.pnpID.product = data[2]+(((u16)data[3])<<8);
1686 cam->params.pnpID.deviceRevision =
1687 data[4]+(((u16)data[5])<<8);
1688 break;
1689 case CPIA_COMMAND_GetCameraStatus:
1690 cam->params.status.systemState = data[0];
1691 cam->params.status.grabState = data[1];
1692 cam->params.status.streamState = data[2];
1693 cam->params.status.fatalError = data[3];
1694 cam->params.status.cmdError = data[4];
1695 cam->params.status.debugFlags = data[5];
1696 cam->params.status.vpStatus = data[6];
1697 cam->params.status.errorCode = data[7];
1698 break;
1699 case CPIA_COMMAND_GetVPVersion:
1700 cam->params.vpVersion.vpVersion = data[0];
1701 cam->params.vpVersion.vpRevision = data[1];
1702 cam->params.vpVersion.cameraHeadID =
1703 data[2]+(((u16)data[3])<<8);
1704 break;
1705 case CPIA_COMMAND_GetColourParams:
1706 cam->params.colourParams.brightness = data[0];
1707 cam->params.colourParams.contrast = data[1];
1708 cam->params.colourParams.saturation = data[2];
Ingo Molnar3593cab2006-02-07 06:49:14 -02001709 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001710 break;
1711 case CPIA_COMMAND_GetColourBalance:
1712 cam->params.colourBalance.redGain = data[0];
1713 cam->params.colourBalance.greenGain = data[1];
1714 cam->params.colourBalance.blueGain = data[2];
Ingo Molnar3593cab2006-02-07 06:49:14 -02001715 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001716 break;
1717 case CPIA_COMMAND_GetExposure:
1718 cam->params.exposure.gain = data[0];
1719 cam->params.exposure.fineExp = data[1];
1720 cam->params.exposure.coarseExpLo = data[2];
1721 cam->params.exposure.coarseExpHi = data[3];
1722 cam->params.exposure.redComp = data[4];
1723 cam->params.exposure.green1Comp = data[5];
1724 cam->params.exposure.green2Comp = data[6];
1725 cam->params.exposure.blueComp = data[7];
Ingo Molnar3593cab2006-02-07 06:49:14 -02001726 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001727 break;
1728
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001729 case CPIA_COMMAND_ReadMCPorts:
1730 if (!cam->params.qx3.qx3_detected)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001731 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001732 /* test button press */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001733 cam->params.qx3.button = ((data[1] & 0x02) == 0);
1734 if (cam->params.qx3.button) {
1735 /* button pressed - unlock the latch */
1736 do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xDF,0xDF,0);
1737 do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xFF,0xFF,0);
1738 }
1739
1740 /* test whether microscope is cradled */
1741 cam->params.qx3.cradled = ((data[2] & 0x40) == 0);
1742 break;
1743
1744 default:
1745 break;
1746 }
1747 }
1748 return retval;
1749}
1750
1751/* send a command to the camera with an additional data transaction */
1752static int do_command_extended(struct cam_data *cam, u16 command,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001753 u8 a, u8 b, u8 c, u8 d,
1754 u8 e, u8 f, u8 g, u8 h,
1755 u8 i, u8 j, u8 k, u8 l)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001756{
1757 int retval;
1758 u8 cmd[8], data[8];
1759
1760 cmd[0] = command>>8;
1761 cmd[1] = command&0xff;
1762 cmd[2] = a;
1763 cmd[3] = b;
1764 cmd[4] = c;
1765 cmd[5] = d;
1766 cmd[6] = 8;
1767 cmd[7] = 0;
1768 data[0] = e;
1769 data[1] = f;
1770 data[2] = g;
1771 data[3] = h;
1772 data[4] = i;
1773 data[5] = j;
1774 data[6] = k;
1775 data[7] = l;
1776
1777 retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
1778 if (retval)
1779 DBG("%x - failed\n", command);
1780
1781 return retval;
1782}
1783
1784/**********************************************************************
1785 *
1786 * Colorspace conversion
1787 *
1788 **********************************************************************/
1789#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16)
1790
1791static int convert420(unsigned char *yuv, unsigned char *rgb, int out_fmt,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001792 int linesize, int mmap_kludge)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001793{
1794 int y, u, v, r, g, b, y1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001795
Linus Torvalds1da177e2005-04-16 15:20:36 -07001796 /* Odd lines use the same u and v as the previous line.
1797 * Because of compression, it is necessary to get this
1798 * information from the decoded image. */
1799 switch(out_fmt) {
1800 case VIDEO_PALETTE_RGB555:
1801 y = (*yuv++ - 16) * 76310;
1802 y1 = (*yuv - 16) * 76310;
1803 r = ((*(rgb+1-linesize)) & 0x7c) << 1;
1804 g = ((*(rgb-linesize)) & 0xe0) >> 4 |
1805 ((*(rgb+1-linesize)) & 0x03) << 6;
1806 b = ((*(rgb-linesize)) & 0x1f) << 3;
1807 u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
1808 v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
1809 r = 104635 * v;
1810 g = -25690 * u - 53294 * v;
1811 b = 132278 * u;
1812 *rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3);
1813 *rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6);
1814 *rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3);
1815 *rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6);
1816 return 4;
1817 case VIDEO_PALETTE_RGB565:
1818 y = (*yuv++ - 16) * 76310;
1819 y1 = (*yuv - 16) * 76310;
1820 r = (*(rgb+1-linesize)) & 0xf8;
1821 g = ((*(rgb-linesize)) & 0xe0) >> 3 |
1822 ((*(rgb+1-linesize)) & 0x07) << 5;
1823 b = ((*(rgb-linesize)) & 0x1f) << 3;
1824 u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
1825 v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
1826 r = 104635 * v;
1827 g = -25690 * u - 53294 * v;
1828 b = 132278 * u;
1829 *rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3);
1830 *rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5);
1831 *rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3);
1832 *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5);
1833 return 4;
1834 break;
1835 case VIDEO_PALETTE_RGB24:
1836 case VIDEO_PALETTE_RGB32:
1837 y = (*yuv++ - 16) * 76310;
1838 y1 = (*yuv - 16) * 76310;
1839 if (mmap_kludge) {
1840 r = *(rgb+2-linesize);
1841 g = *(rgb+1-linesize);
1842 b = *(rgb-linesize);
1843 } else {
1844 r = *(rgb-linesize);
1845 g = *(rgb+1-linesize);
1846 b = *(rgb+2-linesize);
1847 }
1848 u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
1849 v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
1850 r = 104635 * v;
1851 g = -25690 * u + -53294 * v;
1852 b = 132278 * u;
1853 if (mmap_kludge) {
1854 *rgb++ = LIMIT(b+y);
1855 *rgb++ = LIMIT(g+y);
1856 *rgb++ = LIMIT(r+y);
1857 if(out_fmt == VIDEO_PALETTE_RGB32)
1858 rgb++;
1859 *rgb++ = LIMIT(b+y1);
1860 *rgb++ = LIMIT(g+y1);
1861 *rgb = LIMIT(r+y1);
1862 } else {
1863 *rgb++ = LIMIT(r+y);
1864 *rgb++ = LIMIT(g+y);
1865 *rgb++ = LIMIT(b+y);
1866 if(out_fmt == VIDEO_PALETTE_RGB32)
1867 rgb++;
1868 *rgb++ = LIMIT(r+y1);
1869 *rgb++ = LIMIT(g+y1);
1870 *rgb = LIMIT(b+y1);
1871 }
1872 if(out_fmt == VIDEO_PALETTE_RGB32)
1873 return 8;
1874 return 6;
1875 case VIDEO_PALETTE_YUV422:
1876 case VIDEO_PALETTE_YUYV:
1877 y = *yuv++;
1878 u = *(rgb+1-linesize);
1879 y1 = *yuv;
1880 v = *(rgb+3-linesize);
1881 *rgb++ = y;
1882 *rgb++ = u;
1883 *rgb++ = y1;
1884 *rgb = v;
1885 return 4;
1886 case VIDEO_PALETTE_UYVY:
1887 u = *(rgb-linesize);
1888 y = *yuv++;
1889 v = *(rgb+2-linesize);
1890 y1 = *yuv;
1891 *rgb++ = u;
1892 *rgb++ = y;
1893 *rgb++ = v;
1894 *rgb = y1;
1895 return 4;
1896 case VIDEO_PALETTE_GREY:
1897 *rgb++ = *yuv++;
1898 *rgb = *yuv;
1899 return 2;
1900 default:
1901 DBG("Empty: %d\n", out_fmt);
1902 return 0;
1903 }
1904}
1905
1906
1907static int yuvconvert(unsigned char *yuv, unsigned char *rgb, int out_fmt,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001908 int in_uyvy, int mmap_kludge)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001909{
1910 int y, u, v, r, g, b, y1;
1911
1912 switch(out_fmt) {
1913 case VIDEO_PALETTE_RGB555:
1914 case VIDEO_PALETTE_RGB565:
1915 case VIDEO_PALETTE_RGB24:
1916 case VIDEO_PALETTE_RGB32:
1917 if (in_uyvy) {
1918 u = *yuv++ - 128;
1919 y = (*yuv++ - 16) * 76310;
1920 v = *yuv++ - 128;
1921 y1 = (*yuv - 16) * 76310;
1922 } else {
1923 y = (*yuv++ - 16) * 76310;
1924 u = *yuv++ - 128;
1925 y1 = (*yuv++ - 16) * 76310;
1926 v = *yuv - 128;
1927 }
1928 r = 104635 * v;
1929 g = -25690 * u + -53294 * v;
1930 b = 132278 * u;
1931 break;
1932 default:
1933 y = *yuv++;
1934 u = *yuv++;
1935 y1 = *yuv++;
1936 v = *yuv;
1937 /* Just to avoid compiler warnings */
1938 r = 0;
1939 g = 0;
1940 b = 0;
1941 break;
1942 }
1943 switch(out_fmt) {
1944 case VIDEO_PALETTE_RGB555:
1945 *rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3);
1946 *rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6);
1947 *rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3);
1948 *rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6);
1949 return 4;
1950 case VIDEO_PALETTE_RGB565:
1951 *rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3);
1952 *rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5);
1953 *rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3);
1954 *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5);
1955 return 4;
1956 case VIDEO_PALETTE_RGB24:
1957 if (mmap_kludge) {
1958 *rgb++ = LIMIT(b+y);
1959 *rgb++ = LIMIT(g+y);
1960 *rgb++ = LIMIT(r+y);
1961 *rgb++ = LIMIT(b+y1);
1962 *rgb++ = LIMIT(g+y1);
1963 *rgb = LIMIT(r+y1);
1964 } else {
1965 *rgb++ = LIMIT(r+y);
1966 *rgb++ = LIMIT(g+y);
1967 *rgb++ = LIMIT(b+y);
1968 *rgb++ = LIMIT(r+y1);
1969 *rgb++ = LIMIT(g+y1);
1970 *rgb = LIMIT(b+y1);
1971 }
1972 return 6;
1973 case VIDEO_PALETTE_RGB32:
1974 if (mmap_kludge) {
1975 *rgb++ = LIMIT(b+y);
1976 *rgb++ = LIMIT(g+y);
1977 *rgb++ = LIMIT(r+y);
1978 rgb++;
1979 *rgb++ = LIMIT(b+y1);
1980 *rgb++ = LIMIT(g+y1);
1981 *rgb = LIMIT(r+y1);
1982 } else {
1983 *rgb++ = LIMIT(r+y);
1984 *rgb++ = LIMIT(g+y);
1985 *rgb++ = LIMIT(b+y);
1986 rgb++;
1987 *rgb++ = LIMIT(r+y1);
1988 *rgb++ = LIMIT(g+y1);
1989 *rgb = LIMIT(b+y1);
1990 }
1991 return 8;
1992 case VIDEO_PALETTE_GREY:
1993 *rgb++ = y;
1994 *rgb = y1;
1995 return 2;
1996 case VIDEO_PALETTE_YUV422:
1997 case VIDEO_PALETTE_YUYV:
1998 *rgb++ = y;
1999 *rgb++ = u;
2000 *rgb++ = y1;
2001 *rgb = v;
2002 return 4;
2003 case VIDEO_PALETTE_UYVY:
2004 *rgb++ = u;
2005 *rgb++ = y;
2006 *rgb++ = v;
2007 *rgb = y1;
2008 return 4;
2009 default:
2010 DBG("Empty: %d\n", out_fmt);
2011 return 0;
2012 }
2013}
2014
2015static int skipcount(int count, int fmt)
2016{
2017 switch(fmt) {
2018 case VIDEO_PALETTE_GREY:
2019 return count;
2020 case VIDEO_PALETTE_RGB555:
2021 case VIDEO_PALETTE_RGB565:
2022 case VIDEO_PALETTE_YUV422:
2023 case VIDEO_PALETTE_YUYV:
2024 case VIDEO_PALETTE_UYVY:
2025 return 2*count;
2026 case VIDEO_PALETTE_RGB24:
2027 return 3*count;
2028 case VIDEO_PALETTE_RGB32:
2029 return 4*count;
2030 default:
2031 return 0;
2032 }
2033}
2034
2035static int parse_picture(struct cam_data *cam, int size)
2036{
2037 u8 *obuf, *ibuf, *end_obuf;
2038 int ll, in_uyvy, compressed, decimation, even_line, origsize, out_fmt;
2039 int rows, cols, linesize, subsample_422;
2040
2041 /* make sure params don't change while we are decoding */
Ingo Molnar3593cab2006-02-07 06:49:14 -02002042 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002043
2044 obuf = cam->decompressed_frame.data;
2045 end_obuf = obuf+CPIA_MAX_FRAME_SIZE;
2046 ibuf = cam->raw_image;
2047 origsize = size;
2048 out_fmt = cam->vp.palette;
2049
2050 if ((ibuf[0] != MAGIC_0) || (ibuf[1] != MAGIC_1)) {
2051 LOG("header not found\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02002052 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002053 return -1;
2054 }
2055
2056 if ((ibuf[16] != VIDEOSIZE_QCIF) && (ibuf[16] != VIDEOSIZE_CIF)) {
2057 LOG("wrong video size\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02002058 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002059 return -1;
2060 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002061
Linus Torvalds1da177e2005-04-16 15:20:36 -07002062 if (ibuf[17] != SUBSAMPLE_420 && ibuf[17] != SUBSAMPLE_422) {
2063 LOG("illegal subtype %d\n",ibuf[17]);
Ingo Molnar3593cab2006-02-07 06:49:14 -02002064 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002065 return -1;
2066 }
2067 subsample_422 = ibuf[17] == SUBSAMPLE_422;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002068
Linus Torvalds1da177e2005-04-16 15:20:36 -07002069 if (ibuf[18] != YUVORDER_YUYV && ibuf[18] != YUVORDER_UYVY) {
2070 LOG("illegal yuvorder %d\n",ibuf[18]);
Ingo Molnar3593cab2006-02-07 06:49:14 -02002071 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002072 return -1;
2073 }
2074 in_uyvy = ibuf[18] == YUVORDER_UYVY;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002075
Linus Torvalds1da177e2005-04-16 15:20:36 -07002076 if ((ibuf[24] != cam->params.roi.colStart) ||
2077 (ibuf[25] != cam->params.roi.colEnd) ||
2078 (ibuf[26] != cam->params.roi.rowStart) ||
2079 (ibuf[27] != cam->params.roi.rowEnd)) {
2080 LOG("ROI mismatch\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02002081 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002082 return -1;
2083 }
2084 cols = 8*(ibuf[25] - ibuf[24]);
2085 rows = 4*(ibuf[27] - ibuf[26]);
2086
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002087
Linus Torvalds1da177e2005-04-16 15:20:36 -07002088 if ((ibuf[28] != NOT_COMPRESSED) && (ibuf[28] != COMPRESSED)) {
2089 LOG("illegal compression %d\n",ibuf[28]);
Ingo Molnar3593cab2006-02-07 06:49:14 -02002090 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002091 return -1;
2092 }
2093 compressed = (ibuf[28] == COMPRESSED);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002094
Linus Torvalds1da177e2005-04-16 15:20:36 -07002095 if (ibuf[29] != NO_DECIMATION && ibuf[29] != DECIMATION_ENAB) {
2096 LOG("illegal decimation %d\n",ibuf[29]);
Ingo Molnar3593cab2006-02-07 06:49:14 -02002097 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002098 return -1;
2099 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002100 decimation = (ibuf[29] == DECIMATION_ENAB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002101
2102 cam->params.yuvThreshold.yThreshold = ibuf[30];
2103 cam->params.yuvThreshold.uvThreshold = ibuf[31];
2104 cam->params.status.systemState = ibuf[32];
2105 cam->params.status.grabState = ibuf[33];
2106 cam->params.status.streamState = ibuf[34];
2107 cam->params.status.fatalError = ibuf[35];
2108 cam->params.status.cmdError = ibuf[36];
2109 cam->params.status.debugFlags = ibuf[37];
2110 cam->params.status.vpStatus = ibuf[38];
2111 cam->params.status.errorCode = ibuf[39];
2112 cam->fps = ibuf[41];
Ingo Molnar3593cab2006-02-07 06:49:14 -02002113 mutex_unlock(&cam->param_lock);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002114
Linus Torvalds1da177e2005-04-16 15:20:36 -07002115 linesize = skipcount(cols, out_fmt);
2116 ibuf += FRAME_HEADER_SIZE;
2117 size -= FRAME_HEADER_SIZE;
2118 ll = ibuf[0] | (ibuf[1] << 8);
2119 ibuf += 2;
2120 even_line = 1;
2121
2122 while (size > 0) {
2123 size -= (ll+2);
2124 if (size < 0) {
2125 LOG("Insufficient data in buffer\n");
2126 return -1;
2127 }
2128
2129 while (ll > 1) {
2130 if (!compressed || (compressed && !(*ibuf & 1))) {
2131 if(subsample_422 || even_line) {
2132 obuf += yuvconvert(ibuf, obuf, out_fmt,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002133 in_uyvy, cam->mmap_kludge);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002134 ibuf += 4;
2135 ll -= 4;
2136 } else {
2137 /* SUBSAMPLE_420 on an odd line */
2138 obuf += convert420(ibuf, obuf,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002139 out_fmt, linesize,
2140 cam->mmap_kludge);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002141 ibuf += 2;
2142 ll -= 2;
2143 }
2144 } else {
2145 /*skip compressed interval from previous frame*/
2146 obuf += skipcount(*ibuf >> 1, out_fmt);
2147 if (obuf > end_obuf) {
2148 LOG("Insufficient buffer size\n");
2149 return -1;
2150 }
2151 ++ibuf;
2152 ll--;
2153 }
2154 }
2155 if (ll == 1) {
2156 if (*ibuf != EOL) {
2157 DBG("EOL not found giving up after %d/%d"
2158 " bytes\n", origsize-size, origsize);
2159 return -1;
2160 }
2161
2162 ++ibuf; /* skip over EOL */
2163
2164 if ((size > 3) && (ibuf[0] == EOI) && (ibuf[1] == EOI) &&
2165 (ibuf[2] == EOI) && (ibuf[3] == EOI)) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002166 size -= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002167 break;
2168 }
2169
2170 if(decimation) {
2171 /* skip the odd lines for now */
2172 obuf += linesize;
2173 }
2174
2175 if (size > 1) {
2176 ll = ibuf[0] | (ibuf[1] << 8);
2177 ibuf += 2; /* skip over line length */
2178 }
2179 if(!decimation)
2180 even_line = !even_line;
2181 } else {
2182 LOG("line length was not 1 but %d after %d/%d bytes\n",
2183 ll, origsize-size, origsize);
2184 return -1;
2185 }
2186 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002187
Linus Torvalds1da177e2005-04-16 15:20:36 -07002188 if(decimation) {
2189 /* interpolate odd rows */
2190 int i, j;
2191 u8 *prev, *next;
2192 prev = cam->decompressed_frame.data;
2193 obuf = prev+linesize;
2194 next = obuf+linesize;
2195 for(i=1; i<rows-1; i+=2) {
2196 for(j=0; j<linesize; ++j) {
2197 *obuf++ = ((int)*prev++ + *next++) / 2;
2198 }
2199 prev += linesize;
2200 obuf += linesize;
2201 next += linesize;
2202 }
2203 /* last row is odd, just copy previous row */
2204 memcpy(obuf, prev, linesize);
2205 }
2206
2207 cam->decompressed_frame.count = obuf-cam->decompressed_frame.data;
2208
2209 return cam->decompressed_frame.count;
2210}
2211
2212/* InitStreamCap wrapper to select correct start line */
2213static inline int init_stream_cap(struct cam_data *cam)
2214{
2215 return do_command(cam, CPIA_COMMAND_InitStreamCap,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002216 0, cam->params.streamStartLine, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002217}
2218
2219
2220/* find_over_exposure
2221 * Finds a suitable value of OverExposure for use with SetFlickerCtrl
2222 * Some calculation is required because this value changes with the brightness
2223 * set with SetColourParameters
2224 *
2225 * Parameters: Brightness - last brightness value set with SetColourParameters
2226 *
2227 * Returns: OverExposure value to use with SetFlickerCtrl
2228 */
2229#define FLICKER_MAX_EXPOSURE 250
2230#define FLICKER_ALLOWABLE_OVER_EXPOSURE 146
2231#define FLICKER_BRIGHTNESS_CONSTANT 59
2232static int find_over_exposure(int brightness)
2233{
2234 int MaxAllowableOverExposure, OverExposure;
2235
2236 MaxAllowableOverExposure = FLICKER_MAX_EXPOSURE - brightness -
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002237 FLICKER_BRIGHTNESS_CONSTANT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002238
2239 if (MaxAllowableOverExposure < FLICKER_ALLOWABLE_OVER_EXPOSURE) {
2240 OverExposure = MaxAllowableOverExposure;
2241 } else {
2242 OverExposure = FLICKER_ALLOWABLE_OVER_EXPOSURE;
2243 }
2244
2245 return OverExposure;
2246}
2247#undef FLICKER_MAX_EXPOSURE
2248#undef FLICKER_ALLOWABLE_OVER_EXPOSURE
2249#undef FLICKER_BRIGHTNESS_CONSTANT
2250
2251/* update various camera modes and settings */
2252static void dispatch_commands(struct cam_data *cam)
2253{
Ingo Molnar3593cab2006-02-07 06:49:14 -02002254 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002255 if (cam->cmd_queue==COMMAND_NONE) {
Ingo Molnar3593cab2006-02-07 06:49:14 -02002256 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002257 return;
2258 }
2259 DEB_BYTE(cam->cmd_queue);
2260 DEB_BYTE(cam->cmd_queue>>8);
2261 if (cam->cmd_queue & COMMAND_SETFORMAT) {
2262 do_command(cam, CPIA_COMMAND_SetFormat,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002263 cam->params.format.videoSize,
2264 cam->params.format.subSample,
2265 cam->params.format.yuvOrder, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002266 do_command(cam, CPIA_COMMAND_SetROI,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002267 cam->params.roi.colStart, cam->params.roi.colEnd,
2268 cam->params.roi.rowStart, cam->params.roi.rowEnd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002269 cam->first_frame = 1;
2270 }
2271
2272 if (cam->cmd_queue & COMMAND_SETCOLOURPARAMS)
2273 do_command(cam, CPIA_COMMAND_SetColourParams,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002274 cam->params.colourParams.brightness,
2275 cam->params.colourParams.contrast,
2276 cam->params.colourParams.saturation, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002277
2278 if (cam->cmd_queue & COMMAND_SETAPCOR)
2279 do_command(cam, CPIA_COMMAND_SetApcor,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002280 cam->params.apcor.gain1,
2281 cam->params.apcor.gain2,
2282 cam->params.apcor.gain4,
2283 cam->params.apcor.gain8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002284
2285 if (cam->cmd_queue & COMMAND_SETVLOFFSET)
2286 do_command(cam, CPIA_COMMAND_SetVLOffset,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002287 cam->params.vlOffset.gain1,
2288 cam->params.vlOffset.gain2,
2289 cam->params.vlOffset.gain4,
2290 cam->params.vlOffset.gain8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002291
2292 if (cam->cmd_queue & COMMAND_SETEXPOSURE) {
2293 do_command_extended(cam, CPIA_COMMAND_SetExposure,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002294 cam->params.exposure.gainMode,
2295 1,
2296 cam->params.exposure.compMode,
2297 cam->params.exposure.centreWeight,
2298 cam->params.exposure.gain,
2299 cam->params.exposure.fineExp,
2300 cam->params.exposure.coarseExpLo,
2301 cam->params.exposure.coarseExpHi,
2302 cam->params.exposure.redComp,
2303 cam->params.exposure.green1Comp,
2304 cam->params.exposure.green2Comp,
2305 cam->params.exposure.blueComp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002306 if(cam->params.exposure.expMode != 1) {
2307 do_command_extended(cam, CPIA_COMMAND_SetExposure,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002308 0,
2309 cam->params.exposure.expMode,
2310 0, 0,
2311 cam->params.exposure.gain,
2312 cam->params.exposure.fineExp,
2313 cam->params.exposure.coarseExpLo,
2314 cam->params.exposure.coarseExpHi,
2315 0, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002316 }
2317 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002318
Linus Torvalds1da177e2005-04-16 15:20:36 -07002319 if (cam->cmd_queue & COMMAND_SETCOLOURBALANCE) {
2320 if (cam->params.colourBalance.balanceMode == 1) {
2321 do_command(cam, CPIA_COMMAND_SetColourBalance,
2322 1,
2323 cam->params.colourBalance.redGain,
2324 cam->params.colourBalance.greenGain,
2325 cam->params.colourBalance.blueGain);
2326 do_command(cam, CPIA_COMMAND_SetColourBalance,
2327 3, 0, 0, 0);
2328 }
2329 if (cam->params.colourBalance.balanceMode == 2) {
2330 do_command(cam, CPIA_COMMAND_SetColourBalance,
2331 2, 0, 0, 0);
2332 }
2333 if (cam->params.colourBalance.balanceMode == 3) {
2334 do_command(cam, CPIA_COMMAND_SetColourBalance,
2335 3, 0, 0, 0);
2336 }
2337 }
2338
2339 if (cam->cmd_queue & COMMAND_SETCOMPRESSIONTARGET)
2340 do_command(cam, CPIA_COMMAND_SetCompressionTarget,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002341 cam->params.compressionTarget.frTargeting,
2342 cam->params.compressionTarget.targetFR,
2343 cam->params.compressionTarget.targetQ, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002344
2345 if (cam->cmd_queue & COMMAND_SETYUVTHRESH)
2346 do_command(cam, CPIA_COMMAND_SetYUVThresh,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002347 cam->params.yuvThreshold.yThreshold,
2348 cam->params.yuvThreshold.uvThreshold, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002349
2350 if (cam->cmd_queue & COMMAND_SETCOMPRESSIONPARAMS)
2351 do_command_extended(cam, CPIA_COMMAND_SetCompressionParams,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002352 0, 0, 0, 0,
2353 cam->params.compressionParams.hysteresis,
2354 cam->params.compressionParams.threshMax,
2355 cam->params.compressionParams.smallStep,
2356 cam->params.compressionParams.largeStep,
2357 cam->params.compressionParams.decimationHysteresis,
2358 cam->params.compressionParams.frDiffStepThresh,
2359 cam->params.compressionParams.qDiffStepThresh,
2360 cam->params.compressionParams.decimationThreshMod);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002361
2362 if (cam->cmd_queue & COMMAND_SETCOMPRESSION)
2363 do_command(cam, CPIA_COMMAND_SetCompression,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002364 cam->params.compression.mode,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002365 cam->params.compression.decimation, 0, 0);
2366
2367 if (cam->cmd_queue & COMMAND_SETSENSORFPS)
2368 do_command(cam, CPIA_COMMAND_SetSensorFPS,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002369 cam->params.sensorFps.divisor,
2370 cam->params.sensorFps.baserate, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002371
2372 if (cam->cmd_queue & COMMAND_SETFLICKERCTRL)
2373 do_command(cam, CPIA_COMMAND_SetFlickerCtrl,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002374 cam->params.flickerControl.flickerMode,
2375 cam->params.flickerControl.coarseJump,
2376 abs(cam->params.flickerControl.allowableOverExposure),
2377 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002378
2379 if (cam->cmd_queue & COMMAND_SETECPTIMING)
2380 do_command(cam, CPIA_COMMAND_SetECPTiming,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002381 cam->params.ecpTiming, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002382
2383 if (cam->cmd_queue & COMMAND_PAUSE)
2384 do_command(cam, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0);
2385
2386 if (cam->cmd_queue & COMMAND_RESUME)
2387 init_stream_cap(cam);
2388
2389 if (cam->cmd_queue & COMMAND_SETLIGHTS && cam->params.qx3.qx3_detected)
2390 {
2391 int p1 = (cam->params.qx3.bottomlight == 0) << 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002392 int p2 = (cam->params.qx3.toplight == 0) << 3;
2393 do_command(cam, CPIA_COMMAND_WriteVCReg, 0x90, 0x8F, 0x50, 0);
2394 do_command(cam, CPIA_COMMAND_WriteMCPort, 2, 0, (p1|p2|0xE0), 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002395 }
2396
2397 cam->cmd_queue = COMMAND_NONE;
Ingo Molnar3593cab2006-02-07 06:49:14 -02002398 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002399 return;
2400}
2401
2402
2403
2404static void set_flicker(struct cam_params *params, volatile u32 *command_flags,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002405 int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002406{
2407 /* Everything in here is from the Windows driver */
2408#define FIRMWARE_VERSION(x,y) (params->version.firmwareVersion == (x) && \
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002409 params->version.firmwareRevision == (y))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002410/* define for compgain calculation */
2411#if 0
2412#define COMPGAIN(base, curexp, newexp) \
2413 (u8) ((((float) base - 128.0) * ((float) curexp / (float) newexp)) + 128.5)
2414#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
2415 (u16)((float)curexp * (float)(u8)(curcomp + 128) / (float)(u8)(basecomp - 128))
2416#else
2417 /* equivalent functions without floating point math */
2418#define COMPGAIN(base, curexp, newexp) \
2419 (u8)(128 + (((u32)(2*(base-128)*curexp + newexp)) / (2* newexp)) )
2420#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
2421 (u16)(((u32)(curexp * (u8)(curcomp + 128)) / (u8)(basecomp - 128)))
2422#endif
2423
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002424
Linus Torvalds1da177e2005-04-16 15:20:36 -07002425 int currentexp = params->exposure.coarseExpLo +
2426 params->exposure.coarseExpHi*256;
2427 int startexp;
2428 if (on) {
2429 int cj = params->flickerControl.coarseJump;
2430 params->flickerControl.flickerMode = 1;
2431 params->flickerControl.disabled = 0;
2432 if(params->exposure.expMode != 2)
2433 *command_flags |= COMMAND_SETEXPOSURE;
2434 params->exposure.expMode = 2;
2435 currentexp = currentexp << params->exposure.gain;
2436 params->exposure.gain = 0;
2437 /* round down current exposure to nearest value */
2438 startexp = (currentexp + ROUND_UP_EXP_FOR_FLICKER) / cj;
2439 if(startexp < 1)
2440 startexp = 1;
2441 startexp = (startexp * cj) - 1;
2442 if(FIRMWARE_VERSION(1,2))
2443 while(startexp > MAX_EXP_102)
2444 startexp -= cj;
2445 else
2446 while(startexp > MAX_EXP)
2447 startexp -= cj;
2448 params->exposure.coarseExpLo = startexp & 0xff;
2449 params->exposure.coarseExpHi = startexp >> 8;
2450 if (currentexp > startexp) {
2451 if (currentexp > (2 * startexp))
2452 currentexp = 2 * startexp;
2453 params->exposure.redComp = COMPGAIN (COMP_RED, currentexp, startexp);
2454 params->exposure.green1Comp = COMPGAIN (COMP_GREEN1, currentexp, startexp);
2455 params->exposure.green2Comp = COMPGAIN (COMP_GREEN2, currentexp, startexp);
2456 params->exposure.blueComp = COMPGAIN (COMP_BLUE, currentexp, startexp);
2457 } else {
2458 params->exposure.redComp = COMP_RED;
2459 params->exposure.green1Comp = COMP_GREEN1;
2460 params->exposure.green2Comp = COMP_GREEN2;
2461 params->exposure.blueComp = COMP_BLUE;
2462 }
2463 if(FIRMWARE_VERSION(1,2))
2464 params->exposure.compMode = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002465 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07002466 params->exposure.compMode = 1;
2467
2468 params->apcor.gain1 = 0x18;
2469 params->apcor.gain2 = 0x18;
2470 params->apcor.gain4 = 0x16;
2471 params->apcor.gain8 = 0x14;
2472 *command_flags |= COMMAND_SETAPCOR;
2473 } else {
2474 params->flickerControl.flickerMode = 0;
2475 params->flickerControl.disabled = 1;
2476 /* Coarse = average of equivalent coarse for each comp channel */
2477 startexp = EXP_FROM_COMP(COMP_RED, params->exposure.redComp, currentexp);
2478 startexp += EXP_FROM_COMP(COMP_GREEN1, params->exposure.green1Comp, currentexp);
2479 startexp += EXP_FROM_COMP(COMP_GREEN2, params->exposure.green2Comp, currentexp);
2480 startexp += EXP_FROM_COMP(COMP_BLUE, params->exposure.blueComp, currentexp);
2481 startexp = startexp >> 2;
2482 while(startexp > MAX_EXP &&
2483 params->exposure.gain < params->exposure.gainMode-1) {
2484 startexp = startexp >> 1;
2485 ++params->exposure.gain;
2486 }
2487 if(FIRMWARE_VERSION(1,2) && startexp > MAX_EXP_102)
2488 startexp = MAX_EXP_102;
2489 if(startexp > MAX_EXP)
2490 startexp = MAX_EXP;
2491 params->exposure.coarseExpLo = startexp&0xff;
2492 params->exposure.coarseExpHi = startexp >> 8;
2493 params->exposure.redComp = COMP_RED;
2494 params->exposure.green1Comp = COMP_GREEN1;
2495 params->exposure.green2Comp = COMP_GREEN2;
2496 params->exposure.blueComp = COMP_BLUE;
2497 params->exposure.compMode = 1;
2498 *command_flags |= COMMAND_SETEXPOSURE;
2499 params->apcor.gain1 = 0x18;
2500 params->apcor.gain2 = 0x16;
2501 params->apcor.gain4 = 0x24;
2502 params->apcor.gain8 = 0x34;
2503 *command_flags |= COMMAND_SETAPCOR;
2504 }
2505 params->vlOffset.gain1 = 20;
2506 params->vlOffset.gain2 = 24;
2507 params->vlOffset.gain4 = 26;
2508 params->vlOffset.gain8 = 26;
2509 *command_flags |= COMMAND_SETVLOFFSET;
2510#undef FIRMWARE_VERSION
2511#undef EXP_FROM_COMP
2512#undef COMPGAIN
2513}
2514
2515#define FIRMWARE_VERSION(x,y) (cam->params.version.firmwareVersion == (x) && \
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002516 cam->params.version.firmwareRevision == (y))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002517/* monitor the exposure and adjust the sensor frame rate if needed */
2518static void monitor_exposure(struct cam_data *cam)
2519{
2520 u8 exp_acc, bcomp, gain, coarseL, cmd[8], data[8];
2521 int retval, light_exp, dark_exp, very_dark_exp;
2522 int old_exposure, new_exposure, framerate;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002523
Linus Torvalds1da177e2005-04-16 15:20:36 -07002524 /* get necessary stats and register settings from camera */
2525 /* do_command can't handle this, so do it ourselves */
2526 cmd[0] = CPIA_COMMAND_ReadVPRegs>>8;
2527 cmd[1] = CPIA_COMMAND_ReadVPRegs&0xff;
2528 cmd[2] = 30;
2529 cmd[3] = 4;
2530 cmd[4] = 9;
2531 cmd[5] = 8;
2532 cmd[6] = 8;
2533 cmd[7] = 0;
2534 retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
2535 if (retval) {
2536 LOG("ReadVPRegs(30,4,9,8) - failed, retval=%d\n",
2537 retval);
2538 return;
2539 }
2540 exp_acc = data[0];
2541 bcomp = data[1];
2542 gain = data[2];
2543 coarseL = data[3];
2544
Ingo Molnar3593cab2006-02-07 06:49:14 -02002545 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002546 light_exp = cam->params.colourParams.brightness +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002547 TC - 50 + EXP_ACC_LIGHT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002548 if(light_exp > 255)
2549 light_exp = 255;
2550 dark_exp = cam->params.colourParams.brightness +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002551 TC - 50 - EXP_ACC_DARK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002552 if(dark_exp < 0)
2553 dark_exp = 0;
2554 very_dark_exp = dark_exp/2;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002555
Linus Torvalds1da177e2005-04-16 15:20:36 -07002556 old_exposure = cam->params.exposure.coarseExpHi * 256 +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002557 cam->params.exposure.coarseExpLo;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002558
2559 if(!cam->params.flickerControl.disabled) {
2560 /* Flicker control on */
2561 int max_comp = FIRMWARE_VERSION(1,2) ? MAX_COMP : HIGH_COMP_102;
2562 bcomp += 128; /* decode */
2563 if(bcomp >= max_comp && exp_acc < dark_exp) {
2564 /* dark */
2565 if(exp_acc < very_dark_exp) {
2566 /* very dark */
2567 if(cam->exposure_status == EXPOSURE_VERY_DARK)
2568 ++cam->exposure_count;
2569 else {
2570 cam->exposure_status = EXPOSURE_VERY_DARK;
2571 cam->exposure_count = 1;
2572 }
2573 } else {
2574 /* just dark */
2575 if(cam->exposure_status == EXPOSURE_DARK)
2576 ++cam->exposure_count;
2577 else {
2578 cam->exposure_status = EXPOSURE_DARK;
2579 cam->exposure_count = 1;
2580 }
2581 }
2582 } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) {
2583 /* light */
2584 if(old_exposure <= VERY_LOW_EXP) {
2585 /* very light */
2586 if(cam->exposure_status == EXPOSURE_VERY_LIGHT)
2587 ++cam->exposure_count;
2588 else {
2589 cam->exposure_status = EXPOSURE_VERY_LIGHT;
2590 cam->exposure_count = 1;
2591 }
2592 } else {
2593 /* just light */
2594 if(cam->exposure_status == EXPOSURE_LIGHT)
2595 ++cam->exposure_count;
2596 else {
2597 cam->exposure_status = EXPOSURE_LIGHT;
2598 cam->exposure_count = 1;
2599 }
2600 }
2601 } else {
2602 /* not dark or light */
2603 cam->exposure_status = EXPOSURE_NORMAL;
2604 }
2605 } else {
2606 /* Flicker control off */
2607 if(old_exposure >= MAX_EXP && exp_acc < dark_exp) {
2608 /* dark */
2609 if(exp_acc < very_dark_exp) {
2610 /* very dark */
2611 if(cam->exposure_status == EXPOSURE_VERY_DARK)
2612 ++cam->exposure_count;
2613 else {
2614 cam->exposure_status = EXPOSURE_VERY_DARK;
2615 cam->exposure_count = 1;
2616 }
2617 } else {
2618 /* just dark */
2619 if(cam->exposure_status == EXPOSURE_DARK)
2620 ++cam->exposure_count;
2621 else {
2622 cam->exposure_status = EXPOSURE_DARK;
2623 cam->exposure_count = 1;
2624 }
2625 }
2626 } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) {
2627 /* light */
2628 if(old_exposure <= VERY_LOW_EXP) {
2629 /* very light */
2630 if(cam->exposure_status == EXPOSURE_VERY_LIGHT)
2631 ++cam->exposure_count;
2632 else {
2633 cam->exposure_status = EXPOSURE_VERY_LIGHT;
2634 cam->exposure_count = 1;
2635 }
2636 } else {
2637 /* just light */
2638 if(cam->exposure_status == EXPOSURE_LIGHT)
2639 ++cam->exposure_count;
2640 else {
2641 cam->exposure_status = EXPOSURE_LIGHT;
2642 cam->exposure_count = 1;
2643 }
2644 }
2645 } else {
2646 /* not dark or light */
2647 cam->exposure_status = EXPOSURE_NORMAL;
2648 }
2649 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002650
Linus Torvalds1da177e2005-04-16 15:20:36 -07002651 framerate = cam->fps;
2652 if(framerate > 30 || framerate < 1)
2653 framerate = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002654
Linus Torvalds1da177e2005-04-16 15:20:36 -07002655 if(!cam->params.flickerControl.disabled) {
2656 /* Flicker control on */
2657 if((cam->exposure_status == EXPOSURE_VERY_DARK ||
2658 cam->exposure_status == EXPOSURE_DARK) &&
2659 cam->exposure_count >= DARK_TIME*framerate &&
2660 cam->params.sensorFps.divisor < 3) {
2661
2662 /* dark for too long */
2663 ++cam->params.sensorFps.divisor;
2664 cam->cmd_queue |= COMMAND_SETSENSORFPS;
2665
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002666 cam->params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07002667 flicker_jumps[cam->mainsFreq]
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002668 [cam->params.sensorFps.baserate]
2669 [cam->params.sensorFps.divisor];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002670 cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
2671
2672 new_exposure = cam->params.flickerControl.coarseJump-1;
2673 while(new_exposure < old_exposure/2)
2674 new_exposure += cam->params.flickerControl.coarseJump;
2675 cam->params.exposure.coarseExpLo = new_exposure & 0xff;
2676 cam->params.exposure.coarseExpHi = new_exposure >> 8;
2677 cam->cmd_queue |= COMMAND_SETEXPOSURE;
2678 cam->exposure_status = EXPOSURE_NORMAL;
2679 LOG("Automatically decreasing sensor_fps\n");
2680
2681 } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT ||
2682 cam->exposure_status == EXPOSURE_LIGHT) &&
2683 cam->exposure_count >= LIGHT_TIME*framerate &&
2684 cam->params.sensorFps.divisor > 0) {
2685
2686 /* light for too long */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002687 int max_exp = FIRMWARE_VERSION(1,2) ? MAX_EXP_102 : MAX_EXP ;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002688
2689 --cam->params.sensorFps.divisor;
2690 cam->cmd_queue |= COMMAND_SETSENSORFPS;
2691
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002692 cam->params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07002693 flicker_jumps[cam->mainsFreq]
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002694 [cam->params.sensorFps.baserate]
2695 [cam->params.sensorFps.divisor];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002696 cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
2697
2698 new_exposure = cam->params.flickerControl.coarseJump-1;
2699 while(new_exposure < 2*old_exposure &&
2700 new_exposure+
2701 cam->params.flickerControl.coarseJump < max_exp)
2702 new_exposure += cam->params.flickerControl.coarseJump;
2703 cam->params.exposure.coarseExpLo = new_exposure & 0xff;
2704 cam->params.exposure.coarseExpHi = new_exposure >> 8;
2705 cam->cmd_queue |= COMMAND_SETEXPOSURE;
2706 cam->exposure_status = EXPOSURE_NORMAL;
2707 LOG("Automatically increasing sensor_fps\n");
2708 }
2709 } else {
2710 /* Flicker control off */
2711 if((cam->exposure_status == EXPOSURE_VERY_DARK ||
2712 cam->exposure_status == EXPOSURE_DARK) &&
2713 cam->exposure_count >= DARK_TIME*framerate &&
2714 cam->params.sensorFps.divisor < 3) {
2715
2716 /* dark for too long */
2717 ++cam->params.sensorFps.divisor;
2718 cam->cmd_queue |= COMMAND_SETSENSORFPS;
2719
2720 if(cam->params.exposure.gain > 0) {
2721 --cam->params.exposure.gain;
2722 cam->cmd_queue |= COMMAND_SETEXPOSURE;
2723 }
2724 cam->exposure_status = EXPOSURE_NORMAL;
2725 LOG("Automatically decreasing sensor_fps\n");
2726
2727 } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT ||
2728 cam->exposure_status == EXPOSURE_LIGHT) &&
2729 cam->exposure_count >= LIGHT_TIME*framerate &&
2730 cam->params.sensorFps.divisor > 0) {
2731
2732 /* light for too long */
2733 --cam->params.sensorFps.divisor;
2734 cam->cmd_queue |= COMMAND_SETSENSORFPS;
2735
2736 if(cam->params.exposure.gain <
2737 cam->params.exposure.gainMode-1) {
2738 ++cam->params.exposure.gain;
2739 cam->cmd_queue |= COMMAND_SETEXPOSURE;
2740 }
2741 cam->exposure_status = EXPOSURE_NORMAL;
2742 LOG("Automatically increasing sensor_fps\n");
2743 }
2744 }
Ingo Molnar3593cab2006-02-07 06:49:14 -02002745 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002746}
2747
2748/*-----------------------------------------------------------------*/
2749/* if flicker is switched off, this function switches it back on.It checks,
2750 however, that conditions are suitable before restarting it.
2751 This should only be called for firmware version 1.2.
2752
2753 It also adjust the colour balance when an exposure step is detected - as
2754 long as flicker is running
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002755*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07002756static void restart_flicker(struct cam_data *cam)
2757{
2758 int cam_exposure, old_exp;
2759 if(!FIRMWARE_VERSION(1,2))
2760 return;
Ingo Molnar3593cab2006-02-07 06:49:14 -02002761 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002762 if(cam->params.flickerControl.flickerMode == 0 ||
2763 cam->raw_image[39] == 0) {
Ingo Molnar3593cab2006-02-07 06:49:14 -02002764 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002765 return;
2766 }
2767 cam_exposure = cam->raw_image[39]*2;
2768 old_exp = cam->params.exposure.coarseExpLo +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002769 cam->params.exposure.coarseExpHi*256;
2770 /*
2771 see how far away camera exposure is from a valid
2772 flicker exposure value
2773 */
2774 cam_exposure %= cam->params.flickerControl.coarseJump;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002775 if(!cam->params.flickerControl.disabled &&
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002776 cam_exposure <= cam->params.flickerControl.coarseJump - 3) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002777 /* Flicker control auto-disabled */
2778 cam->params.flickerControl.disabled = 1;
2779 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002780
Linus Torvalds1da177e2005-04-16 15:20:36 -07002781 if(cam->params.flickerControl.disabled &&
2782 cam->params.flickerControl.flickerMode &&
2783 old_exp > cam->params.flickerControl.coarseJump +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002784 ROUND_UP_EXP_FOR_FLICKER) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002785 /* exposure is now high enough to switch
2786 flicker control back on */
2787 set_flicker(&cam->params, &cam->cmd_queue, 1);
2788 if((cam->cmd_queue & COMMAND_SETEXPOSURE) &&
2789 cam->params.exposure.expMode == 2)
2790 cam->exposure_status = EXPOSURE_NORMAL;
2791
2792 }
Ingo Molnar3593cab2006-02-07 06:49:14 -02002793 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002794}
2795#undef FIRMWARE_VERSION
2796
2797static int clear_stall(struct cam_data *cam)
2798{
2799 /* FIXME: Does this actually work? */
2800 LOG("Clearing stall\n");
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002801
Linus Torvalds1da177e2005-04-16 15:20:36 -07002802 cam->ops->streamRead(cam->lowlevel_data, cam->raw_image, 0);
2803 do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0);
2804 return cam->params.status.streamState != STREAM_PAUSED;
2805}
2806
2807/* kernel thread function to read image from camera */
2808static int fetch_frame(void *data)
2809{
2810 int image_size, retry;
2811 struct cam_data *cam = (struct cam_data *)data;
2812 unsigned long oldjif, rate, diff;
2813
2814 /* Allow up to two bad images in a row to be read and
2815 * ignored before an error is reported */
2816 for (retry = 0; retry < 3; ++retry) {
2817 if (retry)
2818 DBG("retry=%d\n", retry);
2819
2820 if (!cam->ops)
2821 continue;
2822
2823 /* load first frame always uncompressed */
2824 if (cam->first_frame &&
2825 cam->params.compression.mode != CPIA_COMPRESSION_NONE) {
2826 do_command(cam, CPIA_COMMAND_SetCompression,
2827 CPIA_COMPRESSION_NONE,
2828 NO_DECIMATION, 0, 0);
2829 /* Trial & error - Discarding a frame prevents the
2830 first frame from having an error in the data. */
2831 do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0);
2832 }
2833
2834 /* init camera upload */
2835 if (do_command(cam, CPIA_COMMAND_GrabFrame, 0,
2836 cam->params.streamStartLine, 0, 0))
2837 continue;
2838
2839 if (cam->ops->wait_for_stream_ready) {
2840 /* loop until image ready */
2841 int count = 0;
2842 do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0);
2843 while (cam->params.status.streamState != STREAM_READY) {
2844 if(++count > READY_TIMEOUT)
2845 break;
2846 if(cam->params.status.streamState ==
2847 STREAM_PAUSED) {
2848 /* Bad news */
2849 if(!clear_stall(cam))
2850 return -EIO;
2851 }
2852
2853 cond_resched();
2854
2855 /* sleep for 10 ms, hopefully ;) */
2856 msleep_interruptible(10);
2857 if (signal_pending(current))
2858 return -EINTR;
2859
2860 do_command(cam, CPIA_COMMAND_GetCameraStatus,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002861 0, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002862 }
2863 if(cam->params.status.streamState != STREAM_READY) {
2864 continue;
2865 }
2866 }
2867
2868 cond_resched();
2869
2870 /* grab image from camera */
2871 oldjif = jiffies;
2872 image_size = cam->ops->streamRead(cam->lowlevel_data,
2873 cam->raw_image, 0);
2874 if (image_size <= 0) {
2875 DBG("streamRead failed: %d\n", image_size);
2876 continue;
2877 }
2878
2879 rate = image_size * HZ / 1024;
2880 diff = jiffies-oldjif;
2881 cam->transfer_rate = diff==0 ? rate : rate/diff;
2882 /* diff==0 ? unlikely but possible */
2883
2884 /* Switch flicker control back on if it got turned off */
2885 restart_flicker(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002886
Linus Torvalds1da177e2005-04-16 15:20:36 -07002887 /* If AEC is enabled, monitor the exposure and
2888 adjust the sensor frame rate if needed */
2889 if(cam->params.exposure.expMode == 2)
2890 monitor_exposure(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002891
Linus Torvalds1da177e2005-04-16 15:20:36 -07002892 /* camera idle now so dispatch queued commands */
2893 dispatch_commands(cam);
2894
2895 /* Update our knowledge of the camera state */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002896 do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
2897 do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002898 do_command(cam, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0);
2899
2900 /* decompress and convert image to by copying it from
2901 * raw_image to decompressed_frame
2902 */
2903
2904 cond_resched();
2905
2906 cam->image_size = parse_picture(cam, image_size);
2907 if (cam->image_size <= 0) {
2908 DBG("parse_picture failed %d\n", cam->image_size);
2909 if(cam->params.compression.mode !=
2910 CPIA_COMPRESSION_NONE) {
2911 /* Compression may not work right if we
2912 had a bad frame, get the next one
2913 uncompressed. */
2914 cam->first_frame = 1;
2915 do_command(cam, CPIA_COMMAND_SetGrabMode,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002916 CPIA_GRAB_SINGLE, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002917 /* FIXME: Trial & error - need up to 70ms for
2918 the grab mode change to complete ? */
2919 msleep_interruptible(70);
2920 if (signal_pending(current))
2921 return -EINTR;
2922 }
2923 } else
2924 break;
2925 }
2926
2927 if (retry < 3) {
2928 /* FIXME: this only works for double buffering */
2929 if (cam->frame[cam->curframe].state == FRAME_READY) {
2930 memcpy(cam->frame[cam->curframe].data,
2931 cam->decompressed_frame.data,
2932 cam->decompressed_frame.count);
2933 cam->frame[cam->curframe].state = FRAME_DONE;
2934 } else
2935 cam->decompressed_frame.state = FRAME_DONE;
2936
2937 if (cam->first_frame) {
2938 cam->first_frame = 0;
2939 do_command(cam, CPIA_COMMAND_SetCompression,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002940 cam->params.compression.mode,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002941 cam->params.compression.decimation, 0, 0);
2942
2943 /* Switch from single-grab to continuous grab */
2944 do_command(cam, CPIA_COMMAND_SetGrabMode,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002945 CPIA_GRAB_CONTINUOUS, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002946 }
2947 return 0;
2948 }
2949 return -EIO;
2950}
2951
2952static int capture_frame(struct cam_data *cam, struct video_mmap *vm)
2953{
2954 if (!cam->frame_buf) {
2955 /* we do lazy allocation */
2956 int err;
2957 if ((err = allocate_frame_buf(cam)))
2958 return err;
2959 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002960
Linus Torvalds1da177e2005-04-16 15:20:36 -07002961 cam->curframe = vm->frame;
2962 cam->frame[cam->curframe].state = FRAME_READY;
2963 return fetch_frame(cam);
2964}
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002965
Linus Torvalds1da177e2005-04-16 15:20:36 -07002966static int goto_high_power(struct cam_data *cam)
2967{
2968 if (do_command(cam, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0))
2969 return -EIO;
2970 msleep_interruptible(40); /* windows driver does it too */
2971 if(signal_pending(current))
2972 return -EINTR;
2973 if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
2974 return -EIO;
2975 if (cam->params.status.systemState == HI_POWER_STATE) {
2976 DBG("camera now in HIGH power state\n");
2977 return 0;
2978 }
2979 printstatus(cam);
2980 return -EIO;
2981}
2982
2983static int goto_low_power(struct cam_data *cam)
2984{
2985 if (do_command(cam, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0))
2986 return -1;
2987 if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
2988 return -1;
2989 if (cam->params.status.systemState == LO_POWER_STATE) {
2990 DBG("camera now in LOW power state\n");
2991 return 0;
2992 }
2993 printstatus(cam);
2994 return -1;
2995}
2996
2997static void save_camera_state(struct cam_data *cam)
2998{
2999 if(!(cam->cmd_queue & COMMAND_SETCOLOURBALANCE))
3000 do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
3001 if(!(cam->cmd_queue & COMMAND_SETEXPOSURE))
3002 do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
3003
3004 DBG("%d/%d/%d/%d/%d/%d/%d/%d\n",
3005 cam->params.exposure.gain,
3006 cam->params.exposure.fineExp,
3007 cam->params.exposure.coarseExpLo,
3008 cam->params.exposure.coarseExpHi,
3009 cam->params.exposure.redComp,
3010 cam->params.exposure.green1Comp,
3011 cam->params.exposure.green2Comp,
3012 cam->params.exposure.blueComp);
3013 DBG("%d/%d/%d\n",
3014 cam->params.colourBalance.redGain,
3015 cam->params.colourBalance.greenGain,
3016 cam->params.colourBalance.blueGain);
3017}
3018
3019static int set_camera_state(struct cam_data *cam)
3020{
3021 cam->cmd_queue = COMMAND_SETCOMPRESSION |
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003022 COMMAND_SETCOMPRESSIONTARGET |
3023 COMMAND_SETCOLOURPARAMS |
3024 COMMAND_SETFORMAT |
3025 COMMAND_SETYUVTHRESH |
3026 COMMAND_SETECPTIMING |
3027 COMMAND_SETCOMPRESSIONPARAMS |
3028 COMMAND_SETEXPOSURE |
3029 COMMAND_SETCOLOURBALANCE |
3030 COMMAND_SETSENSORFPS |
3031 COMMAND_SETAPCOR |
3032 COMMAND_SETFLICKERCTRL |
3033 COMMAND_SETVLOFFSET;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003034
3035 do_command(cam, CPIA_COMMAND_SetGrabMode, CPIA_GRAB_SINGLE,0,0,0);
3036 dispatch_commands(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003037
Linus Torvalds1da177e2005-04-16 15:20:36 -07003038 /* Wait 6 frames for the sensor to get all settings and
3039 AEC/ACB to settle */
3040 msleep_interruptible(6*(cam->params.sensorFps.baserate ? 33 : 40) *
3041 (1 << cam->params.sensorFps.divisor) + 10);
3042
3043 if(signal_pending(current))
3044 return -EINTR;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003045
Linus Torvalds1da177e2005-04-16 15:20:36 -07003046 save_camera_state(cam);
3047
3048 return 0;
3049}
3050
3051static void get_version_information(struct cam_data *cam)
3052{
3053 /* GetCPIAVersion */
3054 do_command(cam, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0);
3055
3056 /* GetPnPID */
3057 do_command(cam, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0);
3058}
3059
3060/* initialize camera */
3061static int reset_camera(struct cam_data *cam)
3062{
3063 int err;
3064 /* Start the camera in low power mode */
3065 if (goto_low_power(cam)) {
3066 if (cam->params.status.systemState != WARM_BOOT_STATE)
3067 return -ENODEV;
3068
3069 /* FIXME: this is just dirty trial and error */
3070 err = goto_high_power(cam);
3071 if(err)
3072 return err;
3073 do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0);
3074 if (goto_low_power(cam))
3075 return -ENODEV;
3076 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003077
Linus Torvalds1da177e2005-04-16 15:20:36 -07003078 /* procedure described in developer's guide p3-28 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003079
Linus Torvalds1da177e2005-04-16 15:20:36 -07003080 /* Check the firmware version. */
3081 cam->params.version.firmwareVersion = 0;
3082 get_version_information(cam);
3083 if (cam->params.version.firmwareVersion != 1)
3084 return -ENODEV;
3085
3086 /* A bug in firmware 1-02 limits gainMode to 2 */
3087 if(cam->params.version.firmwareRevision <= 2 &&
3088 cam->params.exposure.gainMode > 2) {
3089 cam->params.exposure.gainMode = 2;
3090 }
3091
3092 /* set QX3 detected flag */
3093 cam->params.qx3.qx3_detected = (cam->params.pnpID.vendor == 0x0813 &&
3094 cam->params.pnpID.product == 0x0001);
3095
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003096 /* The fatal error checking should be done after
Linus Torvalds1da177e2005-04-16 15:20:36 -07003097 * the camera powers up (developer's guide p 3-38) */
3098
3099 /* Set streamState before transition to high power to avoid bug
3100 * in firmware 1-02 */
3101 do_command(cam, CPIA_COMMAND_ModifyCameraStatus, STREAMSTATE, 0,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003102 STREAM_NOT_READY, 0);
3103
Linus Torvalds1da177e2005-04-16 15:20:36 -07003104 /* GotoHiPower */
3105 err = goto_high_power(cam);
3106 if (err)
3107 return err;
3108
3109 /* Check the camera status */
3110 if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
3111 return -EIO;
3112
3113 if (cam->params.status.fatalError) {
3114 DBG("fatal_error: %#04x\n",
3115 cam->params.status.fatalError);
3116 DBG("vp_status: %#04x\n",
3117 cam->params.status.vpStatus);
3118 if (cam->params.status.fatalError & ~(COM_FLAG|CPIA_FLAG)) {
3119 /* Fatal error in camera */
3120 return -EIO;
3121 } else if (cam->params.status.fatalError & (COM_FLAG|CPIA_FLAG)) {
3122 /* Firmware 1-02 may do this for parallel port cameras,
3123 * just clear the flags (developer's guide p 3-38) */
3124 do_command(cam, CPIA_COMMAND_ModifyCameraStatus,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003125 FATALERROR, ~(COM_FLAG|CPIA_FLAG), 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003126 }
3127 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003128
Linus Torvalds1da177e2005-04-16 15:20:36 -07003129 /* Check the camera status again */
3130 if (cam->params.status.fatalError) {
3131 if (cam->params.status.fatalError)
3132 return -EIO;
3133 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003134
Linus Torvalds1da177e2005-04-16 15:20:36 -07003135 /* VPVersion can't be retrieved before the camera is in HiPower,
3136 * so get it here instead of in get_version_information. */
3137 do_command(cam, CPIA_COMMAND_GetVPVersion, 0, 0, 0, 0);
3138
3139 /* set camera to a known state */
3140 return set_camera_state(cam);
3141}
3142
3143static void put_cam(struct cpia_camera_ops* ops)
3144{
Mariusz Kozlowskiac328982007-01-07 10:36:24 -03003145 module_put(ops->owner);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003146}
3147
3148/* ------------------------- V4L interface --------------------- */
Hans Verkuilbec43662008-12-30 06:58:20 -03003149static int cpia_open(struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003150{
3151 struct video_device *dev = video_devdata(file);
Hans Verkuil601e9442008-08-23 07:24:07 -03003152 struct cam_data *cam = video_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003153 int err;
3154
3155 if (!cam) {
3156 DBG("Internal error, cam_data not found!\n");
3157 return -ENODEV;
3158 }
3159
3160 if (cam->open_count > 0) {
3161 DBG("Camera already open\n");
3162 return -EBUSY;
3163 }
3164
3165 if (!try_module_get(cam->ops->owner))
3166 return -ENODEV;
3167
Ingo Molnar3593cab2006-02-07 06:49:14 -02003168 mutex_lock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003169 err = -ENOMEM;
3170 if (!cam->raw_image) {
3171 cam->raw_image = rvmalloc(CPIA_MAX_IMAGE_SIZE);
3172 if (!cam->raw_image)
3173 goto oops;
3174 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003175
Linus Torvalds1da177e2005-04-16 15:20:36 -07003176 if (!cam->decompressed_frame.data) {
3177 cam->decompressed_frame.data = rvmalloc(CPIA_MAX_FRAME_SIZE);
3178 if (!cam->decompressed_frame.data)
3179 goto oops;
3180 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003181
Linus Torvalds1da177e2005-04-16 15:20:36 -07003182 /* open cpia */
3183 err = -ENODEV;
3184 if (cam->ops->open(cam->lowlevel_data))
3185 goto oops;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003186
Linus Torvalds1da177e2005-04-16 15:20:36 -07003187 /* reset the camera */
3188 if ((err = reset_camera(cam)) != 0) {
3189 cam->ops->close(cam->lowlevel_data);
3190 goto oops;
3191 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003192
Linus Torvalds1da177e2005-04-16 15:20:36 -07003193 err = -EINTR;
3194 if(signal_pending(current))
3195 goto oops;
3196
3197 /* Set ownership of /proc/cpia/videoX to current user */
3198 if(cam->proc_entry)
David Howellsabc94fc2008-08-27 10:46:39 -03003199 cam->proc_entry->uid = current_uid();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003200
3201 /* set mark for loading first frame uncompressed */
3202 cam->first_frame = 1;
3203
3204 /* init it to something */
3205 cam->mmap_kludge = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003206
Linus Torvalds1da177e2005-04-16 15:20:36 -07003207 ++cam->open_count;
3208 file->private_data = dev;
Ingo Molnar3593cab2006-02-07 06:49:14 -02003209 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003210 return 0;
3211
3212 oops:
3213 if (cam->decompressed_frame.data) {
3214 rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
3215 cam->decompressed_frame.data = NULL;
3216 }
3217 if (cam->raw_image) {
3218 rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
3219 cam->raw_image = NULL;
3220 }
Ingo Molnar3593cab2006-02-07 06:49:14 -02003221 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003222 put_cam(cam->ops);
3223 return err;
3224}
3225
Hans Verkuilbec43662008-12-30 06:58:20 -03003226static int cpia_close(struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003227{
3228 struct video_device *dev = file->private_data;
Hans Verkuil601e9442008-08-23 07:24:07 -03003229 struct cam_data *cam = video_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003230
3231 if (cam->ops) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003232 /* Return ownership of /proc/cpia/videoX to root */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003233 if(cam->proc_entry)
3234 cam->proc_entry->uid = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003235
Linus Torvalds1da177e2005-04-16 15:20:36 -07003236 /* save camera state for later open (developers guide ch 3.5.3) */
3237 save_camera_state(cam);
3238
3239 /* GotoLoPower */
3240 goto_low_power(cam);
3241
3242 /* Update the camera status */
3243 do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
3244
3245 /* cleanup internal state stuff */
3246 free_frames(cam->frame);
3247
3248 /* close cpia */
3249 cam->ops->close(cam->lowlevel_data);
3250
3251 put_cam(cam->ops);
3252 }
3253
3254 if (--cam->open_count == 0) {
3255 /* clean up capture-buffers */
3256 if (cam->raw_image) {
3257 rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
3258 cam->raw_image = NULL;
3259 }
3260
3261 if (cam->decompressed_frame.data) {
3262 rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
3263 cam->decompressed_frame.data = NULL;
3264 }
3265
3266 if (cam->frame_buf)
3267 free_frame_buf(cam);
3268
3269 if (!cam->ops)
3270 kfree(cam);
3271 }
3272 file->private_data = NULL;
3273
3274 return 0;
3275}
3276
3277static ssize_t cpia_read(struct file *file, char __user *buf,
3278 size_t count, loff_t *ppos)
3279{
3280 struct video_device *dev = file->private_data;
Hans Verkuil601e9442008-08-23 07:24:07 -03003281 struct cam_data *cam = video_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003282 int err;
3283
3284 /* make this _really_ smp and multithread-safe */
Ingo Molnar3593cab2006-02-07 06:49:14 -02003285 if (mutex_lock_interruptible(&cam->busy_lock))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003286 return -EINTR;
3287
3288 if (!buf) {
3289 DBG("buf NULL\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02003290 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003291 return -EINVAL;
3292 }
3293
3294 if (!count) {
3295 DBG("count 0\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02003296 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003297 return 0;
3298 }
3299
3300 if (!cam->ops) {
3301 DBG("ops NULL\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02003302 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003303 return -ENODEV;
3304 }
3305
3306 /* upload frame */
3307 cam->decompressed_frame.state = FRAME_READY;
3308 cam->mmap_kludge=0;
3309 if((err = fetch_frame(cam)) != 0) {
3310 DBG("ERROR from fetch_frame: %d\n", err);
Ingo Molnar3593cab2006-02-07 06:49:14 -02003311 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003312 return err;
3313 }
3314 cam->decompressed_frame.state = FRAME_UNUSED;
3315
3316 /* copy data to user space */
3317 if (cam->decompressed_frame.count > count) {
3318 DBG("count wrong: %d, %lu\n", cam->decompressed_frame.count,
3319 (unsigned long) count);
Ingo Molnar3593cab2006-02-07 06:49:14 -02003320 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003321 return -EFAULT;
3322 }
3323 if (copy_to_user(buf, cam->decompressed_frame.data,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003324 cam->decompressed_frame.count)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003325 DBG("copy_to_user failed\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02003326 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003327 return -EFAULT;
3328 }
3329
Ingo Molnar3593cab2006-02-07 06:49:14 -02003330 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003331 return cam->decompressed_frame.count;
3332}
3333
Hans Verkuil069b7472008-12-30 07:04:34 -03003334static long cpia_do_ioctl(struct file *file, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003335{
3336 struct video_device *dev = file->private_data;
Hans Verkuil601e9442008-08-23 07:24:07 -03003337 struct cam_data *cam = video_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003338 int retval = 0;
3339
3340 if (!cam || !cam->ops)
3341 return -ENODEV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003342
Linus Torvalds1da177e2005-04-16 15:20:36 -07003343 /* make this _really_ smp-safe */
Ingo Molnar3593cab2006-02-07 06:49:14 -02003344 if (mutex_lock_interruptible(&cam->busy_lock))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003345 return -EINTR;
3346
Hans Verkuilf473bf72008-11-01 08:25:11 -03003347 /* DBG("cpia_ioctl: %u\n", cmd); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003348
Hans Verkuilf473bf72008-11-01 08:25:11 -03003349 switch (cmd) {
Alexey Dobriyanbe787ac2006-03-07 22:20:23 -03003350 /* query capabilities */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003351 case VIDIOCGCAP:
3352 {
3353 struct video_capability *b = arg;
3354
3355 DBG("VIDIOCGCAP\n");
3356 strcpy(b->name, "CPiA Camera");
3357 b->type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE;
3358 b->channels = 1;
3359 b->audios = 0;
3360 b->maxwidth = 352; /* VIDEOSIZE_CIF */
3361 b->maxheight = 288;
3362 b->minwidth = 48; /* VIDEOSIZE_48_48 */
3363 b->minheight = 48;
3364 break;
3365 }
3366
3367 /* get/set video source - we are a camera and nothing else */
3368 case VIDIOCGCHAN:
3369 {
3370 struct video_channel *v = arg;
3371
3372 DBG("VIDIOCGCHAN\n");
3373 if (v->channel != 0) {
3374 retval = -EINVAL;
3375 break;
3376 }
3377
3378 v->channel = 0;
3379 strcpy(v->name, "Camera");
3380 v->tuners = 0;
3381 v->flags = 0;
3382 v->type = VIDEO_TYPE_CAMERA;
3383 v->norm = 0;
3384 break;
3385 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003386
Linus Torvalds1da177e2005-04-16 15:20:36 -07003387 case VIDIOCSCHAN:
3388 {
3389 struct video_channel *v = arg;
3390
3391 DBG("VIDIOCSCHAN\n");
3392 if (v->channel != 0)
3393 retval = -EINVAL;
3394 break;
3395 }
3396
3397 /* image properties */
3398 case VIDIOCGPICT:
3399 {
3400 struct video_picture *pic = arg;
3401 DBG("VIDIOCGPICT\n");
3402 *pic = cam->vp;
3403 break;
3404 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003405
Linus Torvalds1da177e2005-04-16 15:20:36 -07003406 case VIDIOCSPICT:
3407 {
3408 struct video_picture *vp = arg;
3409
3410 DBG("VIDIOCSPICT\n");
3411
3412 /* check validity */
3413 DBG("palette: %d\n", vp->palette);
3414 DBG("depth: %d\n", vp->depth);
3415 if (!valid_mode(vp->palette, vp->depth)) {
3416 retval = -EINVAL;
3417 break;
3418 }
3419
Ingo Molnar3593cab2006-02-07 06:49:14 -02003420 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003421 /* brightness, colour, contrast need no check 0-65535 */
3422 cam->vp = *vp;
3423 /* update cam->params.colourParams */
3424 cam->params.colourParams.brightness = vp->brightness*100/65535;
3425 cam->params.colourParams.contrast = vp->contrast*100/65535;
3426 cam->params.colourParams.saturation = vp->colour*100/65535;
3427 /* contrast is in steps of 8, so round */
3428 cam->params.colourParams.contrast =
3429 ((cam->params.colourParams.contrast + 3) / 8) * 8;
3430 if (cam->params.version.firmwareVersion == 1 &&
3431 cam->params.version.firmwareRevision == 2 &&
3432 cam->params.colourParams.contrast > 80) {
3433 /* 1-02 firmware limits contrast to 80 */
3434 cam->params.colourParams.contrast = 80;
3435 }
3436
3437 /* Adjust flicker control if necessary */
3438 if(cam->params.flickerControl.allowableOverExposure < 0)
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003439 cam->params.flickerControl.allowableOverExposure =
Linus Torvalds1da177e2005-04-16 15:20:36 -07003440 -find_over_exposure(cam->params.colourParams.brightness);
3441 if(cam->params.flickerControl.flickerMode != 0)
3442 cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003443
Linus Torvalds1da177e2005-04-16 15:20:36 -07003444
3445 /* queue command to update camera */
3446 cam->cmd_queue |= COMMAND_SETCOLOURPARAMS;
Ingo Molnar3593cab2006-02-07 06:49:14 -02003447 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003448 DBG("VIDIOCSPICT: %d / %d // %d / %d / %d / %d\n",
3449 vp->depth, vp->palette, vp->brightness, vp->hue, vp->colour,
3450 vp->contrast);
3451 break;
3452 }
3453
3454 /* get/set capture window */
3455 case VIDIOCGWIN:
3456 {
3457 struct video_window *vw = arg;
3458 DBG("VIDIOCGWIN\n");
3459
3460 *vw = cam->vw;
3461 break;
3462 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003463
Linus Torvalds1da177e2005-04-16 15:20:36 -07003464 case VIDIOCSWIN:
3465 {
3466 /* copy_from_user, check validity, copy to internal structure */
3467 struct video_window *vw = arg;
3468 DBG("VIDIOCSWIN\n");
3469
3470 if (vw->clipcount != 0) { /* clipping not supported */
3471 retval = -EINVAL;
3472 break;
3473 }
3474 if (vw->clips != NULL) { /* clipping not supported */
3475 retval = -EINVAL;
3476 break;
3477 }
3478
3479 /* we set the video window to something smaller or equal to what
3480 * is requested by the user???
3481 */
Ingo Molnar3593cab2006-02-07 06:49:14 -02003482 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003483 if (vw->width != cam->vw.width || vw->height != cam->vw.height) {
3484 int video_size = match_videosize(vw->width, vw->height);
3485
3486 if (video_size < 0) {
3487 retval = -EINVAL;
Ingo Molnar3593cab2006-02-07 06:49:14 -02003488 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003489 break;
3490 }
3491 cam->video_size = video_size;
3492
3493 /* video size is changing, reset the subcapture area */
3494 memset(&cam->vc, 0, sizeof(cam->vc));
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003495
Linus Torvalds1da177e2005-04-16 15:20:36 -07003496 set_vw_size(cam);
3497 DBG("%d / %d\n", cam->vw.width, cam->vw.height);
3498 cam->cmd_queue |= COMMAND_SETFORMAT;
3499 }
3500
Ingo Molnar3593cab2006-02-07 06:49:14 -02003501 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003502
3503 /* setformat ignored by camera during streaming,
3504 * so stop/dispatch/start */
3505 if (cam->cmd_queue & COMMAND_SETFORMAT) {
3506 DBG("\n");
3507 dispatch_commands(cam);
3508 }
3509 DBG("%d/%d:%d\n", cam->video_size,
3510 cam->vw.width, cam->vw.height);
3511 break;
3512 }
3513
3514 /* mmap interface */
3515 case VIDIOCGMBUF:
3516 {
3517 struct video_mbuf *vm = arg;
3518 int i;
3519
3520 DBG("VIDIOCGMBUF\n");
3521 memset(vm, 0, sizeof(*vm));
3522 vm->size = CPIA_MAX_FRAME_SIZE*FRAME_NUM;
3523 vm->frames = FRAME_NUM;
3524 for (i = 0; i < FRAME_NUM; i++)
3525 vm->offsets[i] = CPIA_MAX_FRAME_SIZE * i;
3526 break;
3527 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003528
Linus Torvalds1da177e2005-04-16 15:20:36 -07003529 case VIDIOCMCAPTURE:
3530 {
3531 struct video_mmap *vm = arg;
3532 int video_size;
3533
3534 DBG("VIDIOCMCAPTURE: %d / %d / %dx%d\n", vm->format, vm->frame,
3535 vm->width, vm->height);
3536 if (vm->frame<0||vm->frame>=FRAME_NUM) {
3537 retval = -EINVAL;
3538 break;
3539 }
3540
3541 /* set video format */
3542 cam->vp.palette = vm->format;
3543 switch(vm->format) {
3544 case VIDEO_PALETTE_GREY:
3545 cam->vp.depth=8;
3546 break;
3547 case VIDEO_PALETTE_RGB555:
3548 case VIDEO_PALETTE_RGB565:
3549 case VIDEO_PALETTE_YUV422:
3550 case VIDEO_PALETTE_YUYV:
3551 case VIDEO_PALETTE_UYVY:
3552 cam->vp.depth = 16;
3553 break;
3554 case VIDEO_PALETTE_RGB24:
3555 cam->vp.depth = 24;
3556 break;
3557 case VIDEO_PALETTE_RGB32:
3558 cam->vp.depth = 32;
3559 break;
3560 default:
3561 retval = -EINVAL;
3562 break;
3563 }
3564 if (retval)
3565 break;
3566
3567 /* set video size */
3568 video_size = match_videosize(vm->width, vm->height);
3569 if (video_size < 0) {
3570 retval = -EINVAL;
3571 break;
3572 }
3573 if (video_size != cam->video_size) {
3574 cam->video_size = video_size;
3575
3576 /* video size is changing, reset the subcapture area */
3577 memset(&cam->vc, 0, sizeof(cam->vc));
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003578
Linus Torvalds1da177e2005-04-16 15:20:36 -07003579 set_vw_size(cam);
3580 cam->cmd_queue |= COMMAND_SETFORMAT;
3581 dispatch_commands(cam);
3582 }
3583 /* according to v4l-spec we must start streaming here */
3584 cam->mmap_kludge = 1;
3585 retval = capture_frame(cam, vm);
3586
3587 break;
3588 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003589
Linus Torvalds1da177e2005-04-16 15:20:36 -07003590 case VIDIOCSYNC:
3591 {
3592 int *frame = arg;
3593
3594 //DBG("VIDIOCSYNC: %d\n", *frame);
3595
3596 if (*frame<0 || *frame >= FRAME_NUM) {
3597 retval = -EINVAL;
3598 break;
3599 }
3600
3601 switch (cam->frame[*frame].state) {
3602 case FRAME_UNUSED:
3603 case FRAME_READY:
3604 case FRAME_GRABBING:
3605 DBG("sync to unused frame %d\n", *frame);
3606 retval = -EINVAL;
3607 break;
3608
3609 case FRAME_DONE:
3610 cam->frame[*frame].state = FRAME_UNUSED;
3611 //DBG("VIDIOCSYNC: %d synced\n", *frame);
3612 break;
3613 }
3614 if (retval == -EINTR) {
3615 /* FIXME - xawtv does not handle this nice */
3616 retval = 0;
3617 }
3618 break;
3619 }
3620
3621 case VIDIOCGCAPTURE:
3622 {
3623 struct video_capture *vc = arg;
3624
3625 DBG("VIDIOCGCAPTURE\n");
3626
3627 *vc = cam->vc;
3628
3629 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003630 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003631
3632 case VIDIOCSCAPTURE:
3633 {
3634 struct video_capture *vc = arg;
3635
3636 DBG("VIDIOCSCAPTURE\n");
3637
3638 if (vc->decimation != 0) { /* How should this be used? */
3639 retval = -EINVAL;
3640 break;
3641 }
3642 if (vc->flags != 0) { /* Even/odd grab not supported */
3643 retval = -EINVAL;
3644 break;
3645 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003646
Linus Torvalds1da177e2005-04-16 15:20:36 -07003647 /* Clip to the resolution we can set for the ROI
3648 (every 8 columns and 4 rows) */
3649 vc->x = vc->x & ~(__u32)7;
3650 vc->y = vc->y & ~(__u32)3;
3651 vc->width = vc->width & ~(__u32)7;
3652 vc->height = vc->height & ~(__u32)3;
3653
3654 if(vc->width == 0 || vc->height == 0 ||
3655 vc->x + vc->width > cam->vw.width ||
3656 vc->y + vc->height > cam->vw.height) {
3657 retval = -EINVAL;
3658 break;
3659 }
3660
3661 DBG("%d,%d/%dx%d\n", vc->x,vc->y,vc->width, vc->height);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003662
Ingo Molnar3593cab2006-02-07 06:49:14 -02003663 mutex_lock(&cam->param_lock);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003664
Linus Torvalds1da177e2005-04-16 15:20:36 -07003665 cam->vc.x = vc->x;
3666 cam->vc.y = vc->y;
3667 cam->vc.width = vc->width;
3668 cam->vc.height = vc->height;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003669
Linus Torvalds1da177e2005-04-16 15:20:36 -07003670 set_vw_size(cam);
3671 cam->cmd_queue |= COMMAND_SETFORMAT;
3672
Ingo Molnar3593cab2006-02-07 06:49:14 -02003673 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003674
3675 /* setformat ignored by camera during streaming,
3676 * so stop/dispatch/start */
3677 dispatch_commands(cam);
3678 break;
3679 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003680
Linus Torvalds1da177e2005-04-16 15:20:36 -07003681 case VIDIOCGUNIT:
3682 {
3683 struct video_unit *vu = arg;
3684
3685 DBG("VIDIOCGUNIT\n");
3686
3687 vu->video = cam->vdev.minor;
3688 vu->vbi = VIDEO_NO_UNIT;
3689 vu->radio = VIDEO_NO_UNIT;
3690 vu->audio = VIDEO_NO_UNIT;
3691 vu->teletext = VIDEO_NO_UNIT;
3692
3693 break;
3694 }
3695
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003696
Linus Torvalds1da177e2005-04-16 15:20:36 -07003697 /* pointless to implement overlay with this camera */
3698 case VIDIOCCAPTURE:
3699 case VIDIOCGFBUF:
3700 case VIDIOCSFBUF:
3701 case VIDIOCKEY:
3702 /* tuner interface - we have none */
3703 case VIDIOCGTUNER:
3704 case VIDIOCSTUNER:
3705 case VIDIOCGFREQ:
3706 case VIDIOCSFREQ:
3707 /* audio interface - we have none */
3708 case VIDIOCGAUDIO:
3709 case VIDIOCSAUDIO:
3710 retval = -EINVAL;
3711 break;
3712 default:
3713 retval = -ENOIOCTLCMD;
3714 break;
3715 }
3716
Ingo Molnar3593cab2006-02-07 06:49:14 -02003717 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003718 return retval;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003719}
Linus Torvalds1da177e2005-04-16 15:20:36 -07003720
Hans Verkuil069b7472008-12-30 07:04:34 -03003721static long cpia_ioctl(struct file *file,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003722 unsigned int cmd, unsigned long arg)
3723{
Hans Verkuilf473bf72008-11-01 08:25:11 -03003724 return video_usercopy(file, cmd, arg, cpia_do_ioctl);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003725}
3726
3727
3728/* FIXME */
3729static int cpia_mmap(struct file *file, struct vm_area_struct *vma)
3730{
3731 struct video_device *dev = file->private_data;
3732 unsigned long start = vma->vm_start;
3733 unsigned long size = vma->vm_end - vma->vm_start;
3734 unsigned long page, pos;
Hans Verkuil601e9442008-08-23 07:24:07 -03003735 struct cam_data *cam = video_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003736 int retval;
3737
3738 if (!cam || !cam->ops)
3739 return -ENODEV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003740
Linus Torvalds1da177e2005-04-16 15:20:36 -07003741 DBG("cpia_mmap: %ld\n", size);
3742
3743 if (size > FRAME_NUM*CPIA_MAX_FRAME_SIZE)
3744 return -EINVAL;
3745
3746 if (!cam || !cam->ops)
3747 return -ENODEV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003748
Linus Torvalds1da177e2005-04-16 15:20:36 -07003749 /* make this _really_ smp-safe */
Ingo Molnar3593cab2006-02-07 06:49:14 -02003750 if (mutex_lock_interruptible(&cam->busy_lock))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003751 return -EINTR;
3752
3753 if (!cam->frame_buf) { /* we do lazy allocation */
3754 if ((retval = allocate_frame_buf(cam))) {
Ingo Molnar3593cab2006-02-07 06:49:14 -02003755 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003756 return retval;
3757 }
3758 }
3759
3760 pos = (unsigned long)(cam->frame_buf);
3761 while (size > 0) {
3762 page = vmalloc_to_pfn((void *)pos);
3763 if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) {
Ingo Molnar3593cab2006-02-07 06:49:14 -02003764 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003765 return -EAGAIN;
3766 }
3767 start += PAGE_SIZE;
3768 pos += PAGE_SIZE;
3769 if (size > PAGE_SIZE)
3770 size -= PAGE_SIZE;
3771 else
3772 size = 0;
3773 }
3774
3775 DBG("cpia_mmap: %ld\n", size);
Ingo Molnar3593cab2006-02-07 06:49:14 -02003776 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003777
3778 return 0;
3779}
3780
Hans Verkuilbec43662008-12-30 06:58:20 -03003781static const struct v4l2_file_operations cpia_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003782 .owner = THIS_MODULE,
3783 .open = cpia_open,
3784 .release = cpia_close,
3785 .read = cpia_read,
3786 .mmap = cpia_mmap,
3787 .ioctl = cpia_ioctl,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003788};
3789
3790static struct video_device cpia_template = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003791 .name = "CPiA Camera",
Linus Torvalds1da177e2005-04-16 15:20:36 -07003792 .fops = &cpia_fops,
Hans Verkuilaa5e90a2008-08-23 06:23:55 -03003793 .release = video_device_release_empty,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003794};
3795
3796/* initialise cam_data structure */
3797static void reset_camera_struct(struct cam_data *cam)
3798{
3799 /* The following parameter values are the defaults from
3800 * "Software Developer's Guide for CPiA Cameras". Any changes
3801 * to the defaults are noted in comments. */
3802 cam->params.colourParams.brightness = 50;
3803 cam->params.colourParams.contrast = 48;
3804 cam->params.colourParams.saturation = 50;
3805 cam->params.exposure.gainMode = 4;
3806 cam->params.exposure.expMode = 2; /* AEC */
3807 cam->params.exposure.compMode = 1;
3808 cam->params.exposure.centreWeight = 1;
3809 cam->params.exposure.gain = 0;
3810 cam->params.exposure.fineExp = 0;
3811 cam->params.exposure.coarseExpLo = 185;
3812 cam->params.exposure.coarseExpHi = 0;
3813 cam->params.exposure.redComp = COMP_RED;
3814 cam->params.exposure.green1Comp = COMP_GREEN1;
3815 cam->params.exposure.green2Comp = COMP_GREEN2;
3816 cam->params.exposure.blueComp = COMP_BLUE;
3817 cam->params.colourBalance.balanceMode = 2; /* ACB */
3818 cam->params.colourBalance.redGain = 32;
3819 cam->params.colourBalance.greenGain = 6;
3820 cam->params.colourBalance.blueGain = 92;
3821 cam->params.apcor.gain1 = 0x18;
3822 cam->params.apcor.gain2 = 0x16;
3823 cam->params.apcor.gain4 = 0x24;
3824 cam->params.apcor.gain8 = 0x34;
3825 cam->params.flickerControl.flickerMode = 0;
3826 cam->params.flickerControl.disabled = 1;
3827
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003828 cam->params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07003829 flicker_jumps[cam->mainsFreq]
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003830 [cam->params.sensorFps.baserate]
3831 [cam->params.sensorFps.divisor];
3832 cam->params.flickerControl.allowableOverExposure =
Linus Torvalds1da177e2005-04-16 15:20:36 -07003833 -find_over_exposure(cam->params.colourParams.brightness);
3834 cam->params.vlOffset.gain1 = 20;
3835 cam->params.vlOffset.gain2 = 24;
3836 cam->params.vlOffset.gain4 = 26;
3837 cam->params.vlOffset.gain8 = 26;
3838 cam->params.compressionParams.hysteresis = 3;
3839 cam->params.compressionParams.threshMax = 11;
3840 cam->params.compressionParams.smallStep = 1;
3841 cam->params.compressionParams.largeStep = 3;
3842 cam->params.compressionParams.decimationHysteresis = 2;
3843 cam->params.compressionParams.frDiffStepThresh = 5;
3844 cam->params.compressionParams.qDiffStepThresh = 3;
3845 cam->params.compressionParams.decimationThreshMod = 2;
3846 /* End of default values from Software Developer's Guide */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003847
Linus Torvalds1da177e2005-04-16 15:20:36 -07003848 cam->transfer_rate = 0;
3849 cam->exposure_status = EXPOSURE_NORMAL;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003850
Linus Torvalds1da177e2005-04-16 15:20:36 -07003851 /* Set Sensor FPS to 15fps. This seems better than 30fps
3852 * for indoor lighting. */
3853 cam->params.sensorFps.divisor = 1;
3854 cam->params.sensorFps.baserate = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003855
Linus Torvalds1da177e2005-04-16 15:20:36 -07003856 cam->params.yuvThreshold.yThreshold = 6; /* From windows driver */
3857 cam->params.yuvThreshold.uvThreshold = 6; /* From windows driver */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003858
Linus Torvalds1da177e2005-04-16 15:20:36 -07003859 cam->params.format.subSample = SUBSAMPLE_422;
3860 cam->params.format.yuvOrder = YUVORDER_YUYV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003861
Linus Torvalds1da177e2005-04-16 15:20:36 -07003862 cam->params.compression.mode = CPIA_COMPRESSION_AUTO;
3863 cam->params.compressionTarget.frTargeting =
3864 CPIA_COMPRESSION_TARGET_QUALITY;
3865 cam->params.compressionTarget.targetFR = 15; /* From windows driver */
3866 cam->params.compressionTarget.targetQ = 5; /* From windows driver */
3867
3868 cam->params.qx3.qx3_detected = 0;
3869 cam->params.qx3.toplight = 0;
3870 cam->params.qx3.bottomlight = 0;
3871 cam->params.qx3.button = 0;
3872 cam->params.qx3.cradled = 0;
3873
3874 cam->video_size = VIDEOSIZE_CIF;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003875
Linus Torvalds1da177e2005-04-16 15:20:36 -07003876 cam->vp.colour = 32768; /* 50% */
3877 cam->vp.hue = 32768; /* 50% */
3878 cam->vp.brightness = 32768; /* 50% */
3879 cam->vp.contrast = 32768; /* 50% */
3880 cam->vp.whiteness = 0; /* not used -> grayscale only */
3881 cam->vp.depth = 24; /* to be set by user */
3882 cam->vp.palette = VIDEO_PALETTE_RGB24; /* to be set by user */
3883
3884 cam->vc.x = 0;
3885 cam->vc.y = 0;
3886 cam->vc.width = 0;
3887 cam->vc.height = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003888
Linus Torvalds1da177e2005-04-16 15:20:36 -07003889 cam->vw.x = 0;
3890 cam->vw.y = 0;
3891 set_vw_size(cam);
3892 cam->vw.chromakey = 0;
3893 cam->vw.flags = 0;
3894 cam->vw.clipcount = 0;
3895 cam->vw.clips = NULL;
3896
3897 cam->cmd_queue = COMMAND_NONE;
3898 cam->first_frame = 1;
3899
3900 return;
3901}
3902
3903/* initialize cam_data structure */
3904static void init_camera_struct(struct cam_data *cam,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003905 struct cpia_camera_ops *ops )
Linus Torvalds1da177e2005-04-16 15:20:36 -07003906{
3907 int i;
3908
3909 /* Default everything to 0 */
3910 memset(cam, 0, sizeof(struct cam_data));
3911
3912 cam->ops = ops;
Ingo Molnar3593cab2006-02-07 06:49:14 -02003913 mutex_init(&cam->param_lock);
3914 mutex_init(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003915
3916 reset_camera_struct(cam);
3917
3918 cam->proc_entry = NULL;
3919
3920 memcpy(&cam->vdev, &cpia_template, sizeof(cpia_template));
Hans Verkuil601e9442008-08-23 07:24:07 -03003921 video_set_drvdata(&cam->vdev, cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003922
Linus Torvalds1da177e2005-04-16 15:20:36 -07003923 cam->curframe = 0;
3924 for (i = 0; i < FRAME_NUM; i++) {
3925 cam->frame[i].width = 0;
3926 cam->frame[i].height = 0;
3927 cam->frame[i].state = FRAME_UNUSED;
3928 cam->frame[i].data = NULL;
3929 }
3930 cam->decompressed_frame.width = 0;
3931 cam->decompressed_frame.height = 0;
3932 cam->decompressed_frame.state = FRAME_UNUSED;
3933 cam->decompressed_frame.data = NULL;
3934}
3935
3936struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel)
3937{
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003938 struct cam_data *camera;
3939
Linus Torvalds1da177e2005-04-16 15:20:36 -07003940 if ((camera = kmalloc(sizeof(struct cam_data), GFP_KERNEL)) == NULL)
3941 return NULL;
3942
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003943
Linus Torvalds1da177e2005-04-16 15:20:36 -07003944 init_camera_struct( camera, ops );
3945 camera->lowlevel_data = lowlevel;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003946
Linus Torvalds1da177e2005-04-16 15:20:36 -07003947 /* register v4l device */
Hans Verkuildc60de32008-09-03 17:11:58 -03003948 if (video_register_device(&camera->vdev, VFL_TYPE_GRABBER, video_nr) < 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003949 kfree(camera);
3950 printk(KERN_DEBUG "video_register_device failed\n");
3951 return NULL;
3952 }
3953
3954 /* get version information from camera: open/reset/close */
3955
3956 /* open cpia */
3957 if (camera->ops->open(camera->lowlevel_data))
3958 return camera;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003959
Linus Torvalds1da177e2005-04-16 15:20:36 -07003960 /* reset the camera */
3961 if (reset_camera(camera) != 0) {
3962 camera->ops->close(camera->lowlevel_data);
3963 return camera;
3964 }
3965
3966 /* close cpia */
3967 camera->ops->close(camera->lowlevel_data);
3968
3969#ifdef CONFIG_PROC_FS
3970 create_proc_cpia_cam(camera);
3971#endif
3972
3973 printk(KERN_INFO " CPiA Version: %d.%02d (%d.%d)\n",
3974 camera->params.version.firmwareVersion,
3975 camera->params.version.firmwareRevision,
3976 camera->params.version.vcVersion,
3977 camera->params.version.vcRevision);
3978 printk(KERN_INFO " CPiA PnP-ID: %04x:%04x:%04x\n",
3979 camera->params.pnpID.vendor,
3980 camera->params.pnpID.product,
3981 camera->params.pnpID.deviceRevision);
3982 printk(KERN_INFO " VP-Version: %d.%d %04x\n",
3983 camera->params.vpVersion.vpVersion,
3984 camera->params.vpVersion.vpRevision,
3985 camera->params.vpVersion.cameraHeadID);
3986
3987 return camera;
3988}
3989
3990void cpia_unregister_camera(struct cam_data *cam)
3991{
3992 DBG("unregistering video\n");
3993 video_unregister_device(&cam->vdev);
3994 if (cam->open_count) {
3995 put_cam(cam->ops);
3996 DBG("camera open -- setting ops to NULL\n");
3997 cam->ops = NULL;
3998 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003999
Linus Torvalds1da177e2005-04-16 15:20:36 -07004000#ifdef CONFIG_PROC_FS
Hans Verkuilc6330fb2008-10-19 18:54:26 -03004001 DBG("destroying /proc/cpia/video%d\n", cam->vdev.num);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004002 destroy_proc_cpia_cam(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03004003#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07004004 if (!cam->open_count) {
4005 DBG("freeing camera\n");
4006 kfree(cam);
4007 }
4008}
4009
4010static int __init cpia_init(void)
4011{
4012 printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT,
4013 CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER);
4014
4015 printk(KERN_WARNING "Since in-kernel colorspace conversion is not "
4016 "allowed, it is disabled by default now. Users should fix the "
4017 "applications in case they don't work without conversion "
4018 "reenabled by setting the 'colorspace_conv' module "
Randy Dunlap94190452006-03-27 16:18:25 -03004019 "parameter to 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004020
4021#ifdef CONFIG_PROC_FS
4022 proc_cpia_create();
4023#endif
4024
Linus Torvalds1da177e2005-04-16 15:20:36 -07004025 return 0;
4026}
4027
4028static void __exit cpia_exit(void)
4029{
4030#ifdef CONFIG_PROC_FS
4031 proc_cpia_destroy();
4032#endif
4033}
4034
4035module_init(cpia_init);
4036module_exit(cpia_exit);
4037
4038/* Exported symbols for modules. */
4039
4040EXPORT_SYMBOL(cpia_register_camera);
4041EXPORT_SYMBOL(cpia_unregister_camera);