blob: b14c59c281eb2b71c46189701d77596d18960089 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * cpia CPiA driver
3 *
4 * Supports CPiA based Video Camera's.
5 *
6 * (C) Copyright 1999-2000 Peter Pregler
7 * (C) Copyright 1999-2000 Scott J. Bertin
8 * (C) Copyright 1999-2000 Johannes Erdfelt <johannes@erdfelt.com>
9 * (C) Copyright 2000 STMicroelectronics
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 */
25
26/* define _CPIA_DEBUG_ for verbose debug output (see cpia.h) */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030027/* #define _CPIA_DEBUG_ 1 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070028
Linus Torvalds1da177e2005-04-16 15:20:36 -070029
30#include <linux/module.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070031#include <linux/init.h>
32#include <linux/fs.h>
33#include <linux/vmalloc.h>
34#include <linux/slab.h>
35#include <linux/proc_fs.h>
36#include <linux/ctype.h>
37#include <linux/pagemap.h>
38#include <linux/delay.h>
39#include <asm/io.h>
Ingo Molnar3593cab2006-02-07 06:49:14 -020040#include <linux/mutex.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070041
42#ifdef CONFIG_KMOD
43#include <linux/kmod.h>
44#endif
45
46#include "cpia.h"
47
Linus Torvalds1da177e2005-04-16 15:20:36 -070048static int video_nr = -1;
49
50#ifdef MODULE
51module_param(video_nr, int, 0);
Randy Dunlap2f8de1a2006-03-21 14:53:22 -030052MODULE_AUTHOR("Scott J. Bertin <sbertin@securenym.net> & Peter Pregler <Peter_Pregler@email.com> & Johannes Erdfelt <johannes@erdfelt.com>");
Linus Torvalds1da177e2005-04-16 15:20:36 -070053MODULE_DESCRIPTION("V4L-driver for Vision CPiA based cameras");
54MODULE_LICENSE("GPL");
55MODULE_SUPPORTED_DEVICE("video");
56#endif
57
Randy Dunlap94190452006-03-27 16:18:25 -030058static unsigned short colorspace_conv;
Linus Torvalds1da177e2005-04-16 15:20:36 -070059module_param(colorspace_conv, ushort, 0444);
60MODULE_PARM_DESC(colorspace_conv,
Mauro Carvalho Chehab4286c6f2006-04-08 16:06:16 -030061 " Colorspace conversion:"
62 "\n 0 = disable, 1 = enable"
63 "\n Default value is 0"
64 );
Linus Torvalds1da177e2005-04-16 15:20:36 -070065
66#define ABOUT "V4L-Driver for Vision CPiA based cameras"
67
Linus Torvalds1da177e2005-04-16 15:20:36 -070068#define CPIA_MODULE_CPIA (0<<5)
69#define CPIA_MODULE_SYSTEM (1<<5)
70#define CPIA_MODULE_VP_CTRL (5<<5)
71#define CPIA_MODULE_CAPTURE (6<<5)
72#define CPIA_MODULE_DEBUG (7<<5)
73
74#define INPUT (DATA_IN << 8)
75#define OUTPUT (DATA_OUT << 8)
76
77#define CPIA_COMMAND_GetCPIAVersion (INPUT | CPIA_MODULE_CPIA | 1)
78#define CPIA_COMMAND_GetPnPID (INPUT | CPIA_MODULE_CPIA | 2)
79#define CPIA_COMMAND_GetCameraStatus (INPUT | CPIA_MODULE_CPIA | 3)
80#define CPIA_COMMAND_GotoHiPower (OUTPUT | CPIA_MODULE_CPIA | 4)
81#define CPIA_COMMAND_GotoLoPower (OUTPUT | CPIA_MODULE_CPIA | 5)
82#define CPIA_COMMAND_GotoSuspend (OUTPUT | CPIA_MODULE_CPIA | 7)
83#define CPIA_COMMAND_GotoPassThrough (OUTPUT | CPIA_MODULE_CPIA | 8)
84#define CPIA_COMMAND_ModifyCameraStatus (OUTPUT | CPIA_MODULE_CPIA | 10)
85
86#define CPIA_COMMAND_ReadVCRegs (INPUT | CPIA_MODULE_SYSTEM | 1)
87#define CPIA_COMMAND_WriteVCReg (OUTPUT | CPIA_MODULE_SYSTEM | 2)
88#define CPIA_COMMAND_ReadMCPorts (INPUT | CPIA_MODULE_SYSTEM | 3)
89#define CPIA_COMMAND_WriteMCPort (OUTPUT | CPIA_MODULE_SYSTEM | 4)
90#define CPIA_COMMAND_SetBaudRate (OUTPUT | CPIA_MODULE_SYSTEM | 5)
91#define CPIA_COMMAND_SetECPTiming (OUTPUT | CPIA_MODULE_SYSTEM | 6)
92#define CPIA_COMMAND_ReadIDATA (INPUT | CPIA_MODULE_SYSTEM | 7)
93#define CPIA_COMMAND_WriteIDATA (OUTPUT | CPIA_MODULE_SYSTEM | 8)
94#define CPIA_COMMAND_GenericCall (OUTPUT | CPIA_MODULE_SYSTEM | 9)
95#define CPIA_COMMAND_I2CStart (OUTPUT | CPIA_MODULE_SYSTEM | 10)
96#define CPIA_COMMAND_I2CStop (OUTPUT | CPIA_MODULE_SYSTEM | 11)
97#define CPIA_COMMAND_I2CWrite (OUTPUT | CPIA_MODULE_SYSTEM | 12)
98#define CPIA_COMMAND_I2CRead (INPUT | CPIA_MODULE_SYSTEM | 13)
99
100#define CPIA_COMMAND_GetVPVersion (INPUT | CPIA_MODULE_VP_CTRL | 1)
101#define CPIA_COMMAND_ResetFrameCounter (INPUT | CPIA_MODULE_VP_CTRL | 2)
102#define CPIA_COMMAND_SetColourParams (OUTPUT | CPIA_MODULE_VP_CTRL | 3)
103#define CPIA_COMMAND_SetExposure (OUTPUT | CPIA_MODULE_VP_CTRL | 4)
104#define CPIA_COMMAND_SetColourBalance (OUTPUT | CPIA_MODULE_VP_CTRL | 6)
105#define CPIA_COMMAND_SetSensorFPS (OUTPUT | CPIA_MODULE_VP_CTRL | 7)
106#define CPIA_COMMAND_SetVPDefaults (OUTPUT | CPIA_MODULE_VP_CTRL | 8)
107#define CPIA_COMMAND_SetApcor (OUTPUT | CPIA_MODULE_VP_CTRL | 9)
108#define CPIA_COMMAND_SetFlickerCtrl (OUTPUT | CPIA_MODULE_VP_CTRL | 10)
109#define CPIA_COMMAND_SetVLOffset (OUTPUT | CPIA_MODULE_VP_CTRL | 11)
110#define CPIA_COMMAND_GetColourParams (INPUT | CPIA_MODULE_VP_CTRL | 16)
111#define CPIA_COMMAND_GetColourBalance (INPUT | CPIA_MODULE_VP_CTRL | 17)
112#define CPIA_COMMAND_GetExposure (INPUT | CPIA_MODULE_VP_CTRL | 18)
113#define CPIA_COMMAND_SetSensorMatrix (OUTPUT | CPIA_MODULE_VP_CTRL | 19)
114#define CPIA_COMMAND_ColourBars (OUTPUT | CPIA_MODULE_VP_CTRL | 25)
115#define CPIA_COMMAND_ReadVPRegs (INPUT | CPIA_MODULE_VP_CTRL | 30)
116#define CPIA_COMMAND_WriteVPReg (OUTPUT | CPIA_MODULE_VP_CTRL | 31)
117
118#define CPIA_COMMAND_GrabFrame (OUTPUT | CPIA_MODULE_CAPTURE | 1)
119#define CPIA_COMMAND_UploadFrame (OUTPUT | CPIA_MODULE_CAPTURE | 2)
120#define CPIA_COMMAND_SetGrabMode (OUTPUT | CPIA_MODULE_CAPTURE | 3)
121#define CPIA_COMMAND_InitStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 4)
122#define CPIA_COMMAND_FiniStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 5)
123#define CPIA_COMMAND_StartStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 6)
124#define CPIA_COMMAND_EndStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 7)
125#define CPIA_COMMAND_SetFormat (OUTPUT | CPIA_MODULE_CAPTURE | 8)
126#define CPIA_COMMAND_SetROI (OUTPUT | CPIA_MODULE_CAPTURE | 9)
127#define CPIA_COMMAND_SetCompression (OUTPUT | CPIA_MODULE_CAPTURE | 10)
128#define CPIA_COMMAND_SetCompressionTarget (OUTPUT | CPIA_MODULE_CAPTURE | 11)
129#define CPIA_COMMAND_SetYUVThresh (OUTPUT | CPIA_MODULE_CAPTURE | 12)
130#define CPIA_COMMAND_SetCompressionParams (OUTPUT | CPIA_MODULE_CAPTURE | 13)
131#define CPIA_COMMAND_DiscardFrame (OUTPUT | CPIA_MODULE_CAPTURE | 14)
132#define CPIA_COMMAND_GrabReset (OUTPUT | CPIA_MODULE_CAPTURE | 15)
133
134#define CPIA_COMMAND_OutputRS232 (OUTPUT | CPIA_MODULE_DEBUG | 1)
135#define CPIA_COMMAND_AbortProcess (OUTPUT | CPIA_MODULE_DEBUG | 4)
136#define CPIA_COMMAND_SetDramPage (OUTPUT | CPIA_MODULE_DEBUG | 5)
137#define CPIA_COMMAND_StartDramUpload (OUTPUT | CPIA_MODULE_DEBUG | 6)
138#define CPIA_COMMAND_StartDummyDtream (OUTPUT | CPIA_MODULE_DEBUG | 8)
139#define CPIA_COMMAND_AbortStream (OUTPUT | CPIA_MODULE_DEBUG | 9)
140#define CPIA_COMMAND_DownloadDRAM (OUTPUT | CPIA_MODULE_DEBUG | 10)
141#define CPIA_COMMAND_Null (OUTPUT | CPIA_MODULE_DEBUG | 11)
142
143enum {
144 FRAME_READY, /* Ready to grab into */
145 FRAME_GRABBING, /* In the process of being grabbed into */
146 FRAME_DONE, /* Finished grabbing, but not been synced yet */
147 FRAME_UNUSED, /* Unused (no MCAPTURE) */
148};
149
150#define COMMAND_NONE 0x0000
151#define COMMAND_SETCOMPRESSION 0x0001
152#define COMMAND_SETCOMPRESSIONTARGET 0x0002
153#define COMMAND_SETCOLOURPARAMS 0x0004
154#define COMMAND_SETFORMAT 0x0008
155#define COMMAND_PAUSE 0x0010
156#define COMMAND_RESUME 0x0020
157#define COMMAND_SETYUVTHRESH 0x0040
158#define COMMAND_SETECPTIMING 0x0080
159#define COMMAND_SETCOMPRESSIONPARAMS 0x0100
160#define COMMAND_SETEXPOSURE 0x0200
161#define COMMAND_SETCOLOURBALANCE 0x0400
162#define COMMAND_SETSENSORFPS 0x0800
163#define COMMAND_SETAPCOR 0x1000
164#define COMMAND_SETFLICKERCTRL 0x2000
165#define COMMAND_SETVLOFFSET 0x4000
166#define COMMAND_SETLIGHTS 0x8000
167
168#define ROUND_UP_EXP_FOR_FLICKER 15
169
170/* Constants for automatic frame rate adjustment */
171#define MAX_EXP 302
172#define MAX_EXP_102 255
173#define LOW_EXP 140
174#define VERY_LOW_EXP 70
175#define TC 94
176#define EXP_ACC_DARK 50
177#define EXP_ACC_LIGHT 90
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300178#define HIGH_COMP_102 160
179#define MAX_COMP 239
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180#define DARK_TIME 3
181#define LIGHT_TIME 3
182
183/* Maximum number of 10ms loops to wait for the stream to become ready */
184#define READY_TIMEOUT 100
185
186/* Developer's Guide Table 5 p 3-34
187 * indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/
188static u8 flicker_jumps[2][2][4] =
189{ { { 76, 38, 19, 9 }, { 92, 46, 23, 11 } },
190 { { 64, 32, 16, 8 }, { 76, 38, 19, 9} }
191};
192
193/* forward declaration of local function */
194static void reset_camera_struct(struct cam_data *cam);
195static int find_over_exposure(int brightness);
196static void set_flicker(struct cam_params *params, volatile u32 *command_flags,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300197 int on);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198
199
200/**********************************************************************
201 *
202 * Memory management
203 *
204 **********************************************************************/
205static void *rvmalloc(unsigned long size)
206{
207 void *mem;
208 unsigned long adr;
209
210 size = PAGE_ALIGN(size);
211 mem = vmalloc_32(size);
212 if (!mem)
213 return NULL;
214
215 memset(mem, 0, size); /* Clear the ram out, no junk to the user */
216 adr = (unsigned long) mem;
217 while (size > 0) {
218 SetPageReserved(vmalloc_to_page((void *)adr));
219 adr += PAGE_SIZE;
220 size -= PAGE_SIZE;
221 }
222
223 return mem;
224}
225
226static void rvfree(void *mem, unsigned long size)
227{
228 unsigned long adr;
229
230 if (!mem)
231 return;
232
233 adr = (unsigned long) mem;
234 while ((long) size > 0) {
235 ClearPageReserved(vmalloc_to_page((void *)adr));
236 adr += PAGE_SIZE;
237 size -= PAGE_SIZE;
238 }
239 vfree(mem);
240}
241
242/**********************************************************************
243 *
244 * /proc interface
245 *
246 **********************************************************************/
247#ifdef CONFIG_PROC_FS
248static struct proc_dir_entry *cpia_proc_root=NULL;
249
250static int cpia_read_proc(char *page, char **start, off_t off,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300251 int count, int *eof, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252{
253 char *out = page;
254 int len, tmp;
255 struct cam_data *cam = data;
256 char tmpstr[29];
257
258 /* IMPORTANT: This output MUST be kept under PAGE_SIZE
259 * or we need to get more sophisticated. */
260
261 out += sprintf(out, "read-only\n-----------------------\n");
262 out += sprintf(out, "V4L Driver version: %d.%d.%d\n",
263 CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER);
264 out += sprintf(out, "CPIA Version: %d.%02d (%d.%d)\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300265 cam->params.version.firmwareVersion,
266 cam->params.version.firmwareRevision,
267 cam->params.version.vcVersion,
268 cam->params.version.vcRevision);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269 out += sprintf(out, "CPIA PnP-ID: %04x:%04x:%04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300270 cam->params.pnpID.vendor, cam->params.pnpID.product,
271 cam->params.pnpID.deviceRevision);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 out += sprintf(out, "VP-Version: %d.%d %04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300273 cam->params.vpVersion.vpVersion,
274 cam->params.vpVersion.vpRevision,
275 cam->params.vpVersion.cameraHeadID);
276
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277 out += sprintf(out, "system_state: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300278 cam->params.status.systemState);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 out += sprintf(out, "grab_state: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300280 cam->params.status.grabState);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 out += sprintf(out, "stream_state: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300282 cam->params.status.streamState);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283 out += sprintf(out, "fatal_error: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300284 cam->params.status.fatalError);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 out += sprintf(out, "cmd_error: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300286 cam->params.status.cmdError);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287 out += sprintf(out, "debug_flags: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300288 cam->params.status.debugFlags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289 out += sprintf(out, "vp_status: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300290 cam->params.status.vpStatus);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 out += sprintf(out, "error_code: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300292 cam->params.status.errorCode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293 /* QX3 specific entries */
294 if (cam->params.qx3.qx3_detected) {
295 out += sprintf(out, "button: %4d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300296 cam->params.qx3.button);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297 out += sprintf(out, "cradled: %4d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300298 cam->params.qx3.cradled);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299 }
300 out += sprintf(out, "video_size: %s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300301 cam->params.format.videoSize == VIDEOSIZE_CIF ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 "CIF " : "QCIF");
303 out += sprintf(out, "roi: (%3d, %3d) to (%3d, %3d)\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300304 cam->params.roi.colStart*8,
305 cam->params.roi.rowStart*4,
306 cam->params.roi.colEnd*8,
307 cam->params.roi.rowEnd*4);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308 out += sprintf(out, "actual_fps: %3d\n", cam->fps);
309 out += sprintf(out, "transfer_rate: %4dkB/s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300310 cam->transfer_rate);
311
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312 out += sprintf(out, "\nread-write\n");
313 out += sprintf(out, "----------------------- current min"
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300314 " max default comment\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315 out += sprintf(out, "brightness: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300316 cam->params.colourParams.brightness, 0, 100, 50);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317 if (cam->params.version.firmwareVersion == 1 &&
318 cam->params.version.firmwareRevision == 2)
319 /* 1-02 firmware limits contrast to 80 */
320 tmp = 80;
321 else
322 tmp = 96;
323
324 out += sprintf(out, "contrast: %8d %8d %8d %8d"
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300325 " steps of 8\n",
326 cam->params.colourParams.contrast, 0, tmp, 48);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327 out += sprintf(out, "saturation: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300328 cam->params.colourParams.saturation, 0, 100, 50);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329 tmp = (25000+5000*cam->params.sensorFps.baserate)/
330 (1<<cam->params.sensorFps.divisor);
331 out += sprintf(out, "sensor_fps: %4d.%03d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300332 tmp/1000, tmp%1000, 3, 30, 15);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 out += sprintf(out, "stream_start_line: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300334 2*cam->params.streamStartLine, 0,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335 cam->params.format.videoSize == VIDEOSIZE_CIF ? 288:144,
336 cam->params.format.videoSize == VIDEOSIZE_CIF ? 240:120);
337 out += sprintf(out, "sub_sample: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300338 cam->params.format.subSample == SUBSAMPLE_420 ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339 "420" : "422", "420", "422", "422");
340 out += sprintf(out, "yuv_order: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300341 cam->params.format.yuvOrder == YUVORDER_YUYV ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342 "YUYV" : "UYVY", "YUYV" , "UYVY", "YUYV");
343 out += sprintf(out, "ecp_timing: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300344 cam->params.ecpTiming ? "slow" : "normal", "slow",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345 "normal", "normal");
346
347 if (cam->params.colourBalance.balanceMode == 2) {
348 sprintf(tmpstr, "auto");
349 } else {
350 sprintf(tmpstr, "manual");
351 }
352 out += sprintf(out, "color_balance_mode: %8s %8s %8s"
353 " %8s\n", tmpstr, "manual", "auto", "auto");
354 out += sprintf(out, "red_gain: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300355 cam->params.colourBalance.redGain, 0, 212, 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356 out += sprintf(out, "green_gain: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300357 cam->params.colourBalance.greenGain, 0, 212, 6);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358 out += sprintf(out, "blue_gain: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300359 cam->params.colourBalance.blueGain, 0, 212, 92);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360
361 if (cam->params.version.firmwareVersion == 1 &&
362 cam->params.version.firmwareRevision == 2)
363 /* 1-02 firmware limits gain to 2 */
364 sprintf(tmpstr, "%8d %8d %8d", 1, 2, 2);
365 else
366 sprintf(tmpstr, "%8d %8d %8d", 1, 8, 2);
367
368 if (cam->params.exposure.gainMode == 0)
369 out += sprintf(out, "max_gain: unknown %28s"
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300370 " powers of 2\n", tmpstr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371 else
372 out += sprintf(out, "max_gain: %8d %28s"
373 " 1,2,4 or 8 \n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300374 1<<(cam->params.exposure.gainMode-1), tmpstr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375
376 switch(cam->params.exposure.expMode) {
377 case 1:
378 case 3:
379 sprintf(tmpstr, "manual");
380 break;
381 case 2:
382 sprintf(tmpstr, "auto");
383 break;
384 default:
385 sprintf(tmpstr, "unknown");
386 break;
387 }
388 out += sprintf(out, "exposure_mode: %8s %8s %8s"
389 " %8s\n", tmpstr, "manual", "auto", "auto");
390 out += sprintf(out, "centre_weight: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300391 (2-cam->params.exposure.centreWeight) ? "on" : "off",
392 "off", "on", "on");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393 out += sprintf(out, "gain: %8d %8d max_gain %8d 1,2,4,8 possible\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300394 1<<cam->params.exposure.gain, 1, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395 if (cam->params.version.firmwareVersion == 1 &&
396 cam->params.version.firmwareRevision == 2)
397 /* 1-02 firmware limits fineExp/2 to 127 */
398 tmp = 254;
399 else
400 tmp = 510;
401
402 out += sprintf(out, "fine_exp: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300403 cam->params.exposure.fineExp*2, 0, tmp, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404 if (cam->params.version.firmwareVersion == 1 &&
405 cam->params.version.firmwareRevision == 2)
406 /* 1-02 firmware limits coarseExpHi to 0 */
407 tmp = MAX_EXP_102;
408 else
409 tmp = MAX_EXP;
410
411 out += sprintf(out, "coarse_exp: %8d %8d %8d"
412 " %8d\n", cam->params.exposure.coarseExpLo+
413 256*cam->params.exposure.coarseExpHi, 0, tmp, 185);
414 out += sprintf(out, "red_comp: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300415 cam->params.exposure.redComp, COMP_RED, 255, COMP_RED);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 out += sprintf(out, "green1_comp: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300417 cam->params.exposure.green1Comp, COMP_GREEN1, 255,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 COMP_GREEN1);
419 out += sprintf(out, "green2_comp: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300420 cam->params.exposure.green2Comp, COMP_GREEN2, 255,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421 COMP_GREEN2);
422 out += sprintf(out, "blue_comp: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300423 cam->params.exposure.blueComp, COMP_BLUE, 255, COMP_BLUE);
424
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425 out += sprintf(out, "apcor_gain1: %#8x %#8x %#8x %#8x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300426 cam->params.apcor.gain1, 0, 0xff, 0x1c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427 out += sprintf(out, "apcor_gain2: %#8x %#8x %#8x %#8x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300428 cam->params.apcor.gain2, 0, 0xff, 0x1a);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429 out += sprintf(out, "apcor_gain4: %#8x %#8x %#8x %#8x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300430 cam->params.apcor.gain4, 0, 0xff, 0x2d);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431 out += sprintf(out, "apcor_gain8: %#8x %#8x %#8x %#8x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300432 cam->params.apcor.gain8, 0, 0xff, 0x2a);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433 out += sprintf(out, "vl_offset_gain1: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300434 cam->params.vlOffset.gain1, 0, 255, 24);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435 out += sprintf(out, "vl_offset_gain2: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300436 cam->params.vlOffset.gain2, 0, 255, 28);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437 out += sprintf(out, "vl_offset_gain4: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300438 cam->params.vlOffset.gain4, 0, 255, 30);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439 out += sprintf(out, "vl_offset_gain8: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300440 cam->params.vlOffset.gain8, 0, 255, 30);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441 out += sprintf(out, "flicker_control: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300442 cam->params.flickerControl.flickerMode ? "on" : "off",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443 "off", "on", "off");
444 out += sprintf(out, "mains_frequency: %8d %8d %8d %8d"
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300445 " only 50/60\n",
446 cam->mainsFreq ? 60 : 50, 50, 60, 50);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447 if(cam->params.flickerControl.allowableOverExposure < 0)
448 out += sprintf(out, "allowable_overexposure: %4dauto 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 else
452 out += sprintf(out, "allowable_overexposure: %8d auto %8d auto\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300453 cam->params.flickerControl.allowableOverExposure,
454 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 out += sprintf(out, "compression_mode: ");
456 switch(cam->params.compression.mode) {
457 case CPIA_COMPRESSION_NONE:
458 out += sprintf(out, "%8s", "none");
459 break;
460 case CPIA_COMPRESSION_AUTO:
461 out += sprintf(out, "%8s", "auto");
462 break;
463 case CPIA_COMPRESSION_MANUAL:
464 out += sprintf(out, "%8s", "manual");
465 break;
466 default:
467 out += sprintf(out, "%8s", "unknown");
468 break;
469 }
470 out += sprintf(out, " none,auto,manual auto\n");
471 out += sprintf(out, "decimation_enable: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300472 cam->params.compression.decimation ==
473 DECIMATION_ENAB ? "on":"off", "off", "on",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 "off");
475 out += sprintf(out, "compression_target: %9s %9s %9s %9s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300476 cam->params.compressionTarget.frTargeting ==
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477 CPIA_COMPRESSION_TARGET_FRAMERATE ?
478 "framerate":"quality",
479 "framerate", "quality", "quality");
480 out += sprintf(out, "target_framerate: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300481 cam->params.compressionTarget.targetFR, 1, 30, 15);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482 out += sprintf(out, "target_quality: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300483 cam->params.compressionTarget.targetQ, 1, 64, 5);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 out += sprintf(out, "y_threshold: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300485 cam->params.yuvThreshold.yThreshold, 0, 31, 6);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486 out += sprintf(out, "uv_threshold: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300487 cam->params.yuvThreshold.uvThreshold, 0, 31, 6);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488 out += sprintf(out, "hysteresis: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300489 cam->params.compressionParams.hysteresis, 0, 255, 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490 out += sprintf(out, "threshold_max: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300491 cam->params.compressionParams.threshMax, 0, 255, 11);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492 out += sprintf(out, "small_step: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300493 cam->params.compressionParams.smallStep, 0, 255, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494 out += sprintf(out, "large_step: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300495 cam->params.compressionParams.largeStep, 0, 255, 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496 out += sprintf(out, "decimation_hysteresis: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300497 cam->params.compressionParams.decimationHysteresis,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498 0, 255, 2);
499 out += sprintf(out, "fr_diff_step_thresh: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300500 cam->params.compressionParams.frDiffStepThresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501 0, 255, 5);
502 out += sprintf(out, "q_diff_step_thresh: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300503 cam->params.compressionParams.qDiffStepThresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504 0, 255, 3);
505 out += sprintf(out, "decimation_thresh_mod: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300506 cam->params.compressionParams.decimationThreshMod,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507 0, 255, 2);
508 /* QX3 specific entries */
509 if (cam->params.qx3.qx3_detected) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300510 out += sprintf(out, "toplight: %8s %8s %8s %8s\n",
511 cam->params.qx3.toplight ? "on" : "off",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512 "off", "on", "off");
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300513 out += sprintf(out, "bottomlight: %8s %8s %8s %8s\n",
514 cam->params.qx3.bottomlight ? "on" : "off",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515 "off", "on", "off");
516 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300517
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 len = out - page;
519 len -= off;
520 if (len < count) {
521 *eof = 1;
522 if (len <= 0) return 0;
523 } else
524 len = count;
525
526 *start = page + off;
527 return len;
528}
529
530
531static int match(char *checkstr, char **buffer, unsigned long *count,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300532 int *find_colon, int *err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533{
534 int ret, colon_found = 1;
535 int len = strlen(checkstr);
536 ret = (len <= *count && strncmp(*buffer, checkstr, len) == 0);
537 if (ret) {
538 *buffer += len;
539 *count -= len;
540 if (*find_colon) {
541 colon_found = 0;
542 while (*count && (**buffer == ' ' || **buffer == '\t' ||
543 (!colon_found && **buffer == ':'))) {
544 if (**buffer == ':')
545 colon_found = 1;
546 --*count;
547 ++*buffer;
548 }
549 if (!*count || !colon_found)
550 *err = -EINVAL;
551 *find_colon = 0;
552 }
553 }
554 return ret;
555}
556
557static unsigned long int value(char **buffer, unsigned long *count, int *err)
558{
559 char *p;
560 unsigned long int ret;
561 ret = simple_strtoul(*buffer, &p, 0);
562 if (p == *buffer)
563 *err = -EINVAL;
564 else {
565 *count -= p - *buffer;
566 *buffer = p;
567 }
568 return ret;
569}
570
571static int cpia_write_proc(struct file *file, const char __user *buf,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300572 unsigned long count, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573{
574 struct cam_data *cam = data;
575 struct cam_params new_params;
576 char *page, *buffer;
577 int retval, find_colon;
578 int size = count;
579 unsigned long val = 0;
580 u32 command_flags = 0;
581 u8 new_mains;
582
583 /*
584 * This code to copy from buf to page is shamelessly copied
585 * from the comx driver
586 */
587 if (count > PAGE_SIZE) {
588 printk(KERN_ERR "count is %lu > %d!!!\n", count, (int)PAGE_SIZE);
589 return -ENOSPC;
590 }
591
592 if (!(page = (char *)__get_free_page(GFP_KERNEL))) return -ENOMEM;
593
594 if(copy_from_user(page, buf, count))
595 {
596 retval = -EFAULT;
597 goto out;
598 }
599
600 if (page[count-1] == '\n')
601 page[count-1] = '\0';
602 else if (count < PAGE_SIZE)
603 page[count] = '\0';
604 else if (page[count]) {
605 retval = -EINVAL;
606 goto out;
607 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300608
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609 buffer = page;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300610
Ingo Molnar3593cab2006-02-07 06:49:14 -0200611 if (mutex_lock_interruptible(&cam->param_lock))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612 return -ERESTARTSYS;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300613
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614 /*
615 * Skip over leading whitespace
616 */
617 while (count && isspace(*buffer)) {
618 --count;
619 ++buffer;
620 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300621
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622 memcpy(&new_params, &cam->params, sizeof(struct cam_params));
623 new_mains = cam->mainsFreq;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300624
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625#define MATCH(x) (match(x, &buffer, &count, &find_colon, &retval))
626#define VALUE (value(&buffer,&count, &retval))
627#define FIRMWARE_VERSION(x,y) (new_params.version.firmwareVersion == (x) && \
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300628 new_params.version.firmwareRevision == (y))
629
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630 retval = 0;
631 while (count && !retval) {
632 find_colon = 1;
633 if (MATCH("brightness")) {
634 if (!retval)
635 val = VALUE;
636
637 if (!retval) {
638 if (val <= 100)
639 new_params.colourParams.brightness = val;
640 else
641 retval = -EINVAL;
642 }
643 command_flags |= COMMAND_SETCOLOURPARAMS;
644 if(new_params.flickerControl.allowableOverExposure < 0)
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300645 new_params.flickerControl.allowableOverExposure =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646 -find_over_exposure(new_params.colourParams.brightness);
647 if(new_params.flickerControl.flickerMode != 0)
648 command_flags |= COMMAND_SETFLICKERCTRL;
649
650 } else if (MATCH("contrast")) {
651 if (!retval)
652 val = VALUE;
653
654 if (!retval) {
655 if (val <= 100) {
656 /* contrast is in steps of 8, so round*/
657 val = ((val + 3) / 8) * 8;
658 /* 1-02 firmware limits contrast to 80*/
659 if (FIRMWARE_VERSION(1,2) && val > 80)
660 val = 80;
661
662 new_params.colourParams.contrast = val;
663 } else
664 retval = -EINVAL;
665 }
666 command_flags |= COMMAND_SETCOLOURPARAMS;
667 } else if (MATCH("saturation")) {
668 if (!retval)
669 val = VALUE;
670
671 if (!retval) {
672 if (val <= 100)
673 new_params.colourParams.saturation = val;
674 else
675 retval = -EINVAL;
676 }
677 command_flags |= COMMAND_SETCOLOURPARAMS;
678 } else if (MATCH("sensor_fps")) {
679 if (!retval)
680 val = VALUE;
681
682 if (!retval) {
683 /* find values so that sensorFPS is minimized,
684 * but >= val */
685 if (val > 30)
686 retval = -EINVAL;
687 else if (val > 25) {
688 new_params.sensorFps.divisor = 0;
689 new_params.sensorFps.baserate = 1;
690 } else if (val > 15) {
691 new_params.sensorFps.divisor = 0;
692 new_params.sensorFps.baserate = 0;
693 } else if (val > 12) {
694 new_params.sensorFps.divisor = 1;
695 new_params.sensorFps.baserate = 1;
696 } else if (val > 7) {
697 new_params.sensorFps.divisor = 1;
698 new_params.sensorFps.baserate = 0;
699 } else if (val > 6) {
700 new_params.sensorFps.divisor = 2;
701 new_params.sensorFps.baserate = 1;
702 } else if (val > 3) {
703 new_params.sensorFps.divisor = 2;
704 new_params.sensorFps.baserate = 0;
705 } else {
706 new_params.sensorFps.divisor = 3;
707 /* Either base rate would work here */
708 new_params.sensorFps.baserate = 1;
709 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300710 new_params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711 flicker_jumps[new_mains]
712 [new_params.sensorFps.baserate]
713 [new_params.sensorFps.divisor];
714 if (new_params.flickerControl.flickerMode)
715 command_flags |= COMMAND_SETFLICKERCTRL;
716 }
717 command_flags |= COMMAND_SETSENSORFPS;
718 cam->exposure_status = EXPOSURE_NORMAL;
719 } else if (MATCH("stream_start_line")) {
720 if (!retval)
721 val = VALUE;
722
723 if (!retval) {
724 int max_line = 288;
725
726 if (new_params.format.videoSize == VIDEOSIZE_QCIF)
727 max_line = 144;
728 if (val <= max_line)
729 new_params.streamStartLine = val/2;
730 else
731 retval = -EINVAL;
732 }
733 } else if (MATCH("sub_sample")) {
734 if (!retval && MATCH("420"))
735 new_params.format.subSample = SUBSAMPLE_420;
736 else if (!retval && MATCH("422"))
737 new_params.format.subSample = SUBSAMPLE_422;
738 else
739 retval = -EINVAL;
740
741 command_flags |= COMMAND_SETFORMAT;
742 } else if (MATCH("yuv_order")) {
743 if (!retval && MATCH("YUYV"))
744 new_params.format.yuvOrder = YUVORDER_YUYV;
745 else if (!retval && MATCH("UYVY"))
746 new_params.format.yuvOrder = YUVORDER_UYVY;
747 else
748 retval = -EINVAL;
749
750 command_flags |= COMMAND_SETFORMAT;
751 } else if (MATCH("ecp_timing")) {
752 if (!retval && MATCH("normal"))
753 new_params.ecpTiming = 0;
754 else if (!retval && MATCH("slow"))
755 new_params.ecpTiming = 1;
756 else
757 retval = -EINVAL;
758
759 command_flags |= COMMAND_SETECPTIMING;
760 } else if (MATCH("color_balance_mode")) {
761 if (!retval && MATCH("manual"))
762 new_params.colourBalance.balanceMode = 3;
763 else if (!retval && MATCH("auto"))
764 new_params.colourBalance.balanceMode = 2;
765 else
766 retval = -EINVAL;
767
768 command_flags |= COMMAND_SETCOLOURBALANCE;
769 } else if (MATCH("red_gain")) {
770 if (!retval)
771 val = VALUE;
772
773 if (!retval) {
774 if (val <= 212) {
775 new_params.colourBalance.redGain = val;
776 new_params.colourBalance.balanceMode = 1;
777 } else
778 retval = -EINVAL;
779 }
780 command_flags |= COMMAND_SETCOLOURBALANCE;
781 } else if (MATCH("green_gain")) {
782 if (!retval)
783 val = VALUE;
784
785 if (!retval) {
786 if (val <= 212) {
787 new_params.colourBalance.greenGain = val;
788 new_params.colourBalance.balanceMode = 1;
789 } else
790 retval = -EINVAL;
791 }
792 command_flags |= COMMAND_SETCOLOURBALANCE;
793 } else if (MATCH("blue_gain")) {
794 if (!retval)
795 val = VALUE;
796
797 if (!retval) {
798 if (val <= 212) {
799 new_params.colourBalance.blueGain = val;
800 new_params.colourBalance.balanceMode = 1;
801 } else
802 retval = -EINVAL;
803 }
804 command_flags |= COMMAND_SETCOLOURBALANCE;
805 } else if (MATCH("max_gain")) {
806 if (!retval)
807 val = VALUE;
808
809 if (!retval) {
810 /* 1-02 firmware limits gain to 2 */
811 if (FIRMWARE_VERSION(1,2) && val > 2)
812 val = 2;
813 switch(val) {
814 case 1:
815 new_params.exposure.gainMode = 1;
816 break;
817 case 2:
818 new_params.exposure.gainMode = 2;
819 break;
820 case 4:
821 new_params.exposure.gainMode = 3;
822 break;
823 case 8:
824 new_params.exposure.gainMode = 4;
825 break;
826 default:
827 retval = -EINVAL;
828 break;
829 }
830 }
831 command_flags |= COMMAND_SETEXPOSURE;
832 } else if (MATCH("exposure_mode")) {
833 if (!retval && MATCH("auto"))
834 new_params.exposure.expMode = 2;
835 else if (!retval && MATCH("manual")) {
836 if (new_params.exposure.expMode == 2)
837 new_params.exposure.expMode = 3;
838 if(new_params.flickerControl.flickerMode != 0)
839 command_flags |= COMMAND_SETFLICKERCTRL;
840 new_params.flickerControl.flickerMode = 0;
841 } else
842 retval = -EINVAL;
843
844 command_flags |= COMMAND_SETEXPOSURE;
845 } else if (MATCH("centre_weight")) {
846 if (!retval && MATCH("on"))
847 new_params.exposure.centreWeight = 1;
848 else if (!retval && MATCH("off"))
849 new_params.exposure.centreWeight = 2;
850 else
851 retval = -EINVAL;
852
853 command_flags |= COMMAND_SETEXPOSURE;
854 } else if (MATCH("gain")) {
855 if (!retval)
856 val = VALUE;
857
858 if (!retval) {
859 switch(val) {
860 case 1:
861 new_params.exposure.gain = 0;
862 break;
863 case 2:
864 new_params.exposure.gain = 1;
865 break;
866 case 4:
867 new_params.exposure.gain = 2;
868 break;
869 case 8:
870 new_params.exposure.gain = 3;
871 break;
872 default:
873 retval = -EINVAL;
874 break;
875 }
876 new_params.exposure.expMode = 1;
877 if(new_params.flickerControl.flickerMode != 0)
878 command_flags |= COMMAND_SETFLICKERCTRL;
879 new_params.flickerControl.flickerMode = 0;
880 command_flags |= COMMAND_SETEXPOSURE;
881 if (new_params.exposure.gain >
882 new_params.exposure.gainMode-1)
883 retval = -EINVAL;
884 }
885 } else if (MATCH("fine_exp")) {
886 if (!retval)
887 val = VALUE/2;
888
889 if (!retval) {
890 if (val < 256) {
891 /* 1-02 firmware limits fineExp/2 to 127*/
892 if (FIRMWARE_VERSION(1,2) && val > 127)
893 val = 127;
894 new_params.exposure.fineExp = val;
895 new_params.exposure.expMode = 1;
896 command_flags |= COMMAND_SETEXPOSURE;
897 if(new_params.flickerControl.flickerMode != 0)
898 command_flags |= COMMAND_SETFLICKERCTRL;
899 new_params.flickerControl.flickerMode = 0;
900 command_flags |= COMMAND_SETFLICKERCTRL;
901 } else
902 retval = -EINVAL;
903 }
904 } else if (MATCH("coarse_exp")) {
905 if (!retval)
906 val = VALUE;
907
908 if (!retval) {
909 if (val <= MAX_EXP) {
910 if (FIRMWARE_VERSION(1,2) &&
911 val > MAX_EXP_102)
912 val = MAX_EXP_102;
913 new_params.exposure.coarseExpLo =
914 val & 0xff;
915 new_params.exposure.coarseExpHi =
916 val >> 8;
917 new_params.exposure.expMode = 1;
918 command_flags |= COMMAND_SETEXPOSURE;
919 if(new_params.flickerControl.flickerMode != 0)
920 command_flags |= COMMAND_SETFLICKERCTRL;
921 new_params.flickerControl.flickerMode = 0;
922 command_flags |= COMMAND_SETFLICKERCTRL;
923 } else
924 retval = -EINVAL;
925 }
926 } else if (MATCH("red_comp")) {
927 if (!retval)
928 val = VALUE;
929
930 if (!retval) {
931 if (val >= COMP_RED && val <= 255) {
932 new_params.exposure.redComp = val;
933 new_params.exposure.compMode = 1;
934 command_flags |= COMMAND_SETEXPOSURE;
935 } else
936 retval = -EINVAL;
937 }
938 } else if (MATCH("green1_comp")) {
939 if (!retval)
940 val = VALUE;
941
942 if (!retval) {
943 if (val >= COMP_GREEN1 && val <= 255) {
944 new_params.exposure.green1Comp = val;
945 new_params.exposure.compMode = 1;
946 command_flags |= COMMAND_SETEXPOSURE;
947 } else
948 retval = -EINVAL;
949 }
950 } else if (MATCH("green2_comp")) {
951 if (!retval)
952 val = VALUE;
953
954 if (!retval) {
955 if (val >= COMP_GREEN2 && val <= 255) {
956 new_params.exposure.green2Comp = val;
957 new_params.exposure.compMode = 1;
958 command_flags |= COMMAND_SETEXPOSURE;
959 } else
960 retval = -EINVAL;
961 }
962 } else if (MATCH("blue_comp")) {
963 if (!retval)
964 val = VALUE;
965
966 if (!retval) {
967 if (val >= COMP_BLUE && val <= 255) {
968 new_params.exposure.blueComp = val;
969 new_params.exposure.compMode = 1;
970 command_flags |= COMMAND_SETEXPOSURE;
971 } else
972 retval = -EINVAL;
973 }
974 } else if (MATCH("apcor_gain1")) {
975 if (!retval)
976 val = VALUE;
977
978 if (!retval) {
979 command_flags |= COMMAND_SETAPCOR;
980 if (val <= 0xff)
981 new_params.apcor.gain1 = val;
982 else
983 retval = -EINVAL;
984 }
985 } else if (MATCH("apcor_gain2")) {
986 if (!retval)
987 val = VALUE;
988
989 if (!retval) {
990 command_flags |= COMMAND_SETAPCOR;
991 if (val <= 0xff)
992 new_params.apcor.gain2 = val;
993 else
994 retval = -EINVAL;
995 }
996 } else if (MATCH("apcor_gain4")) {
997 if (!retval)
998 val = VALUE;
999
1000 if (!retval) {
1001 command_flags |= COMMAND_SETAPCOR;
1002 if (val <= 0xff)
1003 new_params.apcor.gain4 = val;
1004 else
1005 retval = -EINVAL;
1006 }
1007 } else if (MATCH("apcor_gain8")) {
1008 if (!retval)
1009 val = VALUE;
1010
1011 if (!retval) {
1012 command_flags |= COMMAND_SETAPCOR;
1013 if (val <= 0xff)
1014 new_params.apcor.gain8 = val;
1015 else
1016 retval = -EINVAL;
1017 }
1018 } else if (MATCH("vl_offset_gain1")) {
1019 if (!retval)
1020 val = VALUE;
1021
1022 if (!retval) {
1023 if (val <= 0xff)
1024 new_params.vlOffset.gain1 = val;
1025 else
1026 retval = -EINVAL;
1027 }
1028 command_flags |= COMMAND_SETVLOFFSET;
1029 } else if (MATCH("vl_offset_gain2")) {
1030 if (!retval)
1031 val = VALUE;
1032
1033 if (!retval) {
1034 if (val <= 0xff)
1035 new_params.vlOffset.gain2 = val;
1036 else
1037 retval = -EINVAL;
1038 }
1039 command_flags |= COMMAND_SETVLOFFSET;
1040 } else if (MATCH("vl_offset_gain4")) {
1041 if (!retval)
1042 val = VALUE;
1043
1044 if (!retval) {
1045 if (val <= 0xff)
1046 new_params.vlOffset.gain4 = val;
1047 else
1048 retval = -EINVAL;
1049 }
1050 command_flags |= COMMAND_SETVLOFFSET;
1051 } else if (MATCH("vl_offset_gain8")) {
1052 if (!retval)
1053 val = VALUE;
1054
1055 if (!retval) {
1056 if (val <= 0xff)
1057 new_params.vlOffset.gain8 = val;
1058 else
1059 retval = -EINVAL;
1060 }
1061 command_flags |= COMMAND_SETVLOFFSET;
1062 } else if (MATCH("flicker_control")) {
1063 if (!retval && MATCH("on")) {
1064 set_flicker(&new_params, &command_flags, 1);
1065 } else if (!retval && MATCH("off")) {
1066 set_flicker(&new_params, &command_flags, 0);
1067 } else
1068 retval = -EINVAL;
1069
1070 command_flags |= COMMAND_SETFLICKERCTRL;
1071 } else if (MATCH("mains_frequency")) {
1072 if (!retval && MATCH("50")) {
1073 new_mains = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001074 new_params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075 flicker_jumps[new_mains]
1076 [new_params.sensorFps.baserate]
1077 [new_params.sensorFps.divisor];
1078 if (new_params.flickerControl.flickerMode)
1079 command_flags |= COMMAND_SETFLICKERCTRL;
1080 } else if (!retval && MATCH("60")) {
1081 new_mains = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001082 new_params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083 flicker_jumps[new_mains]
1084 [new_params.sensorFps.baserate]
1085 [new_params.sensorFps.divisor];
1086 if (new_params.flickerControl.flickerMode)
1087 command_flags |= COMMAND_SETFLICKERCTRL;
1088 } else
1089 retval = -EINVAL;
1090 } else if (MATCH("allowable_overexposure")) {
1091 if (!retval && MATCH("auto")) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001092 new_params.flickerControl.allowableOverExposure =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093 -find_over_exposure(new_params.colourParams.brightness);
1094 if(new_params.flickerControl.flickerMode != 0)
1095 command_flags |= COMMAND_SETFLICKERCTRL;
1096 } else {
1097 if (!retval)
1098 val = VALUE;
1099
1100 if (!retval) {
1101 if (val <= 0xff) {
1102 new_params.flickerControl.
1103 allowableOverExposure = val;
1104 if(new_params.flickerControl.flickerMode != 0)
1105 command_flags |= COMMAND_SETFLICKERCTRL;
1106 } else
1107 retval = -EINVAL;
1108 }
1109 }
1110 } else if (MATCH("compression_mode")) {
1111 if (!retval && MATCH("none"))
1112 new_params.compression.mode =
1113 CPIA_COMPRESSION_NONE;
1114 else if (!retval && MATCH("auto"))
1115 new_params.compression.mode =
1116 CPIA_COMPRESSION_AUTO;
1117 else if (!retval && MATCH("manual"))
1118 new_params.compression.mode =
1119 CPIA_COMPRESSION_MANUAL;
1120 else
1121 retval = -EINVAL;
1122
1123 command_flags |= COMMAND_SETCOMPRESSION;
1124 } else if (MATCH("decimation_enable")) {
1125 if (!retval && MATCH("off"))
1126 new_params.compression.decimation = 0;
1127 else if (!retval && MATCH("on"))
1128 new_params.compression.decimation = 1;
1129 else
1130 retval = -EINVAL;
1131
1132 command_flags |= COMMAND_SETCOMPRESSION;
1133 } else if (MATCH("compression_target")) {
1134 if (!retval && MATCH("quality"))
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001135 new_params.compressionTarget.frTargeting =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136 CPIA_COMPRESSION_TARGET_QUALITY;
1137 else if (!retval && MATCH("framerate"))
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001138 new_params.compressionTarget.frTargeting =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139 CPIA_COMPRESSION_TARGET_FRAMERATE;
1140 else
1141 retval = -EINVAL;
1142
1143 command_flags |= COMMAND_SETCOMPRESSIONTARGET;
1144 } else if (MATCH("target_framerate")) {
1145 if (!retval)
1146 val = VALUE;
1147
1148 if (!retval) {
1149 if(val > 0 && val <= 30)
1150 new_params.compressionTarget.targetFR = val;
1151 else
1152 retval = -EINVAL;
1153 }
1154 command_flags |= COMMAND_SETCOMPRESSIONTARGET;
1155 } else if (MATCH("target_quality")) {
1156 if (!retval)
1157 val = VALUE;
1158
1159 if (!retval) {
1160 if(val > 0 && val <= 64)
1161 new_params.compressionTarget.targetQ = val;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001162 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163 retval = -EINVAL;
1164 }
1165 command_flags |= COMMAND_SETCOMPRESSIONTARGET;
1166 } else if (MATCH("y_threshold")) {
1167 if (!retval)
1168 val = VALUE;
1169
1170 if (!retval) {
1171 if (val < 32)
1172 new_params.yuvThreshold.yThreshold = val;
1173 else
1174 retval = -EINVAL;
1175 }
1176 command_flags |= COMMAND_SETYUVTHRESH;
1177 } else if (MATCH("uv_threshold")) {
1178 if (!retval)
1179 val = VALUE;
1180
1181 if (!retval) {
1182 if (val < 32)
1183 new_params.yuvThreshold.uvThreshold = val;
1184 else
1185 retval = -EINVAL;
1186 }
1187 command_flags |= COMMAND_SETYUVTHRESH;
1188 } else if (MATCH("hysteresis")) {
1189 if (!retval)
1190 val = VALUE;
1191
1192 if (!retval) {
1193 if (val <= 0xff)
1194 new_params.compressionParams.hysteresis = val;
1195 else
1196 retval = -EINVAL;
1197 }
1198 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1199 } else if (MATCH("threshold_max")) {
1200 if (!retval)
1201 val = VALUE;
1202
1203 if (!retval) {
1204 if (val <= 0xff)
1205 new_params.compressionParams.threshMax = val;
1206 else
1207 retval = -EINVAL;
1208 }
1209 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1210 } else if (MATCH("small_step")) {
1211 if (!retval)
1212 val = VALUE;
1213
1214 if (!retval) {
1215 if (val <= 0xff)
1216 new_params.compressionParams.smallStep = val;
1217 else
1218 retval = -EINVAL;
1219 }
1220 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1221 } else if (MATCH("large_step")) {
1222 if (!retval)
1223 val = VALUE;
1224
1225 if (!retval) {
1226 if (val <= 0xff)
1227 new_params.compressionParams.largeStep = val;
1228 else
1229 retval = -EINVAL;
1230 }
1231 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1232 } else if (MATCH("decimation_hysteresis")) {
1233 if (!retval)
1234 val = VALUE;
1235
1236 if (!retval) {
1237 if (val <= 0xff)
1238 new_params.compressionParams.decimationHysteresis = val;
1239 else
1240 retval = -EINVAL;
1241 }
1242 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1243 } else if (MATCH("fr_diff_step_thresh")) {
1244 if (!retval)
1245 val = VALUE;
1246
1247 if (!retval) {
1248 if (val <= 0xff)
1249 new_params.compressionParams.frDiffStepThresh = val;
1250 else
1251 retval = -EINVAL;
1252 }
1253 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1254 } else if (MATCH("q_diff_step_thresh")) {
1255 if (!retval)
1256 val = VALUE;
1257
1258 if (!retval) {
1259 if (val <= 0xff)
1260 new_params.compressionParams.qDiffStepThresh = val;
1261 else
1262 retval = -EINVAL;
1263 }
1264 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1265 } else if (MATCH("decimation_thresh_mod")) {
1266 if (!retval)
1267 val = VALUE;
1268
1269 if (!retval) {
1270 if (val <= 0xff)
1271 new_params.compressionParams.decimationThreshMod = val;
1272 else
1273 retval = -EINVAL;
1274 }
1275 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1276 } else if (MATCH("toplight")) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001277 if (!retval && MATCH("on"))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001278 new_params.qx3.toplight = 1;
1279 else if (!retval && MATCH("off"))
1280 new_params.qx3.toplight = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001281 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282 retval = -EINVAL;
1283 command_flags |= COMMAND_SETLIGHTS;
1284 } else if (MATCH("bottomlight")) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001285 if (!retval && MATCH("on"))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286 new_params.qx3.bottomlight = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001287 else if (!retval && MATCH("off"))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001288 new_params.qx3.bottomlight = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001289 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290 retval = -EINVAL;
1291 command_flags |= COMMAND_SETLIGHTS;
1292 } else {
1293 DBG("No match found\n");
1294 retval = -EINVAL;
1295 }
1296
1297 if (!retval) {
1298 while (count && isspace(*buffer) && *buffer != '\n') {
1299 --count;
1300 ++buffer;
1301 }
1302 if (count) {
1303 if (*buffer == '\0' && count != 1)
1304 retval = -EINVAL;
1305 else if (*buffer != '\n' && *buffer != ';' &&
1306 *buffer != '\0')
1307 retval = -EINVAL;
1308 else {
1309 --count;
1310 ++buffer;
1311 }
1312 }
1313 }
1314 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001315#undef MATCH
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316#undef VALUE
1317#undef FIRMWARE_VERSION
1318 if (!retval) {
1319 if (command_flags & COMMAND_SETCOLOURPARAMS) {
1320 /* Adjust cam->vp to reflect these changes */
1321 cam->vp.brightness =
1322 new_params.colourParams.brightness*65535/100;
1323 cam->vp.contrast =
1324 new_params.colourParams.contrast*65535/100;
1325 cam->vp.colour =
1326 new_params.colourParams.saturation*65535/100;
1327 }
1328 if((command_flags & COMMAND_SETEXPOSURE) &&
1329 new_params.exposure.expMode == 2)
1330 cam->exposure_status = EXPOSURE_NORMAL;
1331
1332 memcpy(&cam->params, &new_params, sizeof(struct cam_params));
1333 cam->mainsFreq = new_mains;
1334 cam->cmd_queue |= command_flags;
1335 retval = size;
1336 } else
1337 DBG("error: %d\n", retval);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001338
Ingo Molnar3593cab2006-02-07 06:49:14 -02001339 mutex_unlock(&cam->param_lock);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001340
Linus Torvalds1da177e2005-04-16 15:20:36 -07001341out:
1342 free_page((unsigned long)page);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001343 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344}
1345
1346static void create_proc_cpia_cam(struct cam_data *cam)
1347{
Alexey Dobriyan13071f02007-01-14 15:29:42 -03001348 char name[5 + 1 + 10 + 1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349 struct proc_dir_entry *ent;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001350
Linus Torvalds1da177e2005-04-16 15:20:36 -07001351 if (!cpia_proc_root || !cam)
1352 return;
1353
Alexey Dobriyan13071f02007-01-14 15:29:42 -03001354 snprintf(name, sizeof(name), "video%d", cam->vdev.minor);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001355
Linus Torvalds1da177e2005-04-16 15:20:36 -07001356 ent = create_proc_entry(name, S_IFREG|S_IRUGO|S_IWUSR, cpia_proc_root);
1357 if (!ent)
1358 return;
1359
1360 ent->data = cam;
1361 ent->read_proc = cpia_read_proc;
1362 ent->write_proc = cpia_write_proc;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001363 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001364 size of the proc entry is 3736 bytes for the standard webcam;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001365 the extra features of the QX3 microscope add 189 bytes.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366 (we have not yet probed the camera to see which type it is).
1367 */
1368 ent->size = 3736 + 189;
1369 cam->proc_entry = ent;
1370}
1371
1372static void destroy_proc_cpia_cam(struct cam_data *cam)
1373{
Alexey Dobriyan13071f02007-01-14 15:29:42 -03001374 char name[5 + 1 + 10 + 1];
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001375
Linus Torvalds1da177e2005-04-16 15:20:36 -07001376 if (!cam || !cam->proc_entry)
1377 return;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001378
Alexey Dobriyan13071f02007-01-14 15:29:42 -03001379 snprintf(name, sizeof(name), "video%d", cam->vdev.minor);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001380 remove_proc_entry(name, cpia_proc_root);
1381 cam->proc_entry = NULL;
1382}
1383
1384static void proc_cpia_create(void)
1385{
Al Viro66600222005-09-28 22:32:57 +01001386 cpia_proc_root = proc_mkdir("cpia", NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001387
1388 if (cpia_proc_root)
1389 cpia_proc_root->owner = THIS_MODULE;
1390 else
1391 LOG("Unable to initialise /proc/cpia\n");
1392}
1393
1394static void __exit proc_cpia_destroy(void)
1395{
1396 remove_proc_entry("cpia", NULL);
1397}
1398#endif /* CONFIG_PROC_FS */
1399
1400/* ----------------------- debug functions ---------------------- */
1401
1402#define printstatus(cam) \
1403 DBG("%02x %02x %02x %02x %02x %02x %02x %02x\n",\
1404 cam->params.status.systemState, cam->params.status.grabState, \
1405 cam->params.status.streamState, cam->params.status.fatalError, \
1406 cam->params.status.cmdError, cam->params.status.debugFlags, \
1407 cam->params.status.vpStatus, cam->params.status.errorCode);
1408
1409/* ----------------------- v4l helpers -------------------------- */
1410
1411/* supported frame palettes and depths */
1412static inline int valid_mode(u16 palette, u16 depth)
1413{
1414 if ((palette == VIDEO_PALETTE_YUV422 && depth == 16) ||
1415 (palette == VIDEO_PALETTE_YUYV && depth == 16))
1416 return 1;
1417
1418 if (colorspace_conv)
1419 return (palette == VIDEO_PALETTE_GREY && depth == 8) ||
1420 (palette == VIDEO_PALETTE_RGB555 && depth == 16) ||
1421 (palette == VIDEO_PALETTE_RGB565 && depth == 16) ||
1422 (palette == VIDEO_PALETTE_RGB24 && depth == 24) ||
1423 (palette == VIDEO_PALETTE_RGB32 && depth == 32) ||
1424 (palette == VIDEO_PALETTE_UYVY && depth == 16);
1425
1426 return 0;
1427}
1428
1429static int match_videosize( int width, int height )
1430{
1431 /* return the best match, where 'best' is as always
1432 * the largest that is not bigger than what is requested. */
1433 if (width>=352 && height>=288)
1434 return VIDEOSIZE_352_288; /* CIF */
1435
1436 if (width>=320 && height>=240)
1437 return VIDEOSIZE_320_240; /* SIF */
1438
1439 if (width>=288 && height>=216)
1440 return VIDEOSIZE_288_216;
1441
1442 if (width>=256 && height>=192)
1443 return VIDEOSIZE_256_192;
1444
1445 if (width>=224 && height>=168)
1446 return VIDEOSIZE_224_168;
1447
1448 if (width>=192 && height>=144)
1449 return VIDEOSIZE_192_144;
1450
1451 if (width>=176 && height>=144)
1452 return VIDEOSIZE_176_144; /* QCIF */
1453
1454 if (width>=160 && height>=120)
1455 return VIDEOSIZE_160_120; /* QSIF */
1456
1457 if (width>=128 && height>=96)
1458 return VIDEOSIZE_128_96;
1459
1460 if (width>=88 && height>=72)
1461 return VIDEOSIZE_88_72;
1462
1463 if (width>=64 && height>=48)
1464 return VIDEOSIZE_64_48;
1465
1466 if (width>=48 && height>=48)
1467 return VIDEOSIZE_48_48;
1468
1469 return -1;
1470}
1471
1472/* these are the capture sizes we support */
1473static void set_vw_size(struct cam_data *cam)
1474{
1475 /* the col/row/start/end values are the result of simple math */
1476 /* study the SetROI-command in cpia developers guide p 2-22 */
1477 /* streamStartLine is set to the recommended value in the cpia */
1478 /* developers guide p 3-37 */
1479 switch(cam->video_size) {
1480 case VIDEOSIZE_CIF:
1481 cam->vw.width = 352;
1482 cam->vw.height = 288;
1483 cam->params.format.videoSize=VIDEOSIZE_CIF;
1484 cam->params.roi.colStart=0;
1485 cam->params.roi.rowStart=0;
1486 cam->params.streamStartLine = 120;
1487 break;
1488 case VIDEOSIZE_SIF:
1489 cam->vw.width = 320;
1490 cam->vw.height = 240;
1491 cam->params.format.videoSize=VIDEOSIZE_CIF;
1492 cam->params.roi.colStart=2;
1493 cam->params.roi.rowStart=6;
1494 cam->params.streamStartLine = 120;
1495 break;
1496 case VIDEOSIZE_288_216:
1497 cam->vw.width = 288;
1498 cam->vw.height = 216;
1499 cam->params.format.videoSize=VIDEOSIZE_CIF;
1500 cam->params.roi.colStart=4;
1501 cam->params.roi.rowStart=9;
1502 cam->params.streamStartLine = 120;
1503 break;
1504 case VIDEOSIZE_256_192:
1505 cam->vw.width = 256;
1506 cam->vw.height = 192;
1507 cam->params.format.videoSize=VIDEOSIZE_CIF;
1508 cam->params.roi.colStart=6;
1509 cam->params.roi.rowStart=12;
1510 cam->params.streamStartLine = 120;
1511 break;
1512 case VIDEOSIZE_224_168:
1513 cam->vw.width = 224;
1514 cam->vw.height = 168;
1515 cam->params.format.videoSize=VIDEOSIZE_CIF;
1516 cam->params.roi.colStart=8;
1517 cam->params.roi.rowStart=15;
1518 cam->params.streamStartLine = 120;
1519 break;
1520 case VIDEOSIZE_192_144:
1521 cam->vw.width = 192;
1522 cam->vw.height = 144;
1523 cam->params.format.videoSize=VIDEOSIZE_CIF;
1524 cam->params.roi.colStart=10;
1525 cam->params.roi.rowStart=18;
1526 cam->params.streamStartLine = 120;
1527 break;
1528 case VIDEOSIZE_QCIF:
1529 cam->vw.width = 176;
1530 cam->vw.height = 144;
1531 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1532 cam->params.roi.colStart=0;
1533 cam->params.roi.rowStart=0;
1534 cam->params.streamStartLine = 60;
1535 break;
1536 case VIDEOSIZE_QSIF:
1537 cam->vw.width = 160;
1538 cam->vw.height = 120;
1539 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1540 cam->params.roi.colStart=1;
1541 cam->params.roi.rowStart=3;
1542 cam->params.streamStartLine = 60;
1543 break;
1544 case VIDEOSIZE_128_96:
1545 cam->vw.width = 128;
1546 cam->vw.height = 96;
1547 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1548 cam->params.roi.colStart=3;
1549 cam->params.roi.rowStart=6;
1550 cam->params.streamStartLine = 60;
1551 break;
1552 case VIDEOSIZE_88_72:
1553 cam->vw.width = 88;
1554 cam->vw.height = 72;
1555 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1556 cam->params.roi.colStart=5;
1557 cam->params.roi.rowStart=9;
1558 cam->params.streamStartLine = 60;
1559 break;
1560 case VIDEOSIZE_64_48:
1561 cam->vw.width = 64;
1562 cam->vw.height = 48;
1563 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1564 cam->params.roi.colStart=7;
1565 cam->params.roi.rowStart=12;
1566 cam->params.streamStartLine = 60;
1567 break;
1568 case VIDEOSIZE_48_48:
1569 cam->vw.width = 48;
1570 cam->vw.height = 48;
1571 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1572 cam->params.roi.colStart=8;
1573 cam->params.roi.rowStart=6;
1574 cam->params.streamStartLine = 60;
1575 break;
1576 default:
1577 LOG("bad videosize value: %d\n", cam->video_size);
1578 return;
1579 }
1580
1581 if(cam->vc.width == 0)
1582 cam->vc.width = cam->vw.width;
1583 if(cam->vc.height == 0)
1584 cam->vc.height = cam->vw.height;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001585
Linus Torvalds1da177e2005-04-16 15:20:36 -07001586 cam->params.roi.colStart += cam->vc.x >> 3;
1587 cam->params.roi.colEnd = cam->params.roi.colStart +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001588 (cam->vc.width >> 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001589 cam->params.roi.rowStart += cam->vc.y >> 2;
1590 cam->params.roi.rowEnd = cam->params.roi.rowStart +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001591 (cam->vc.height >> 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001592
1593 return;
1594}
1595
1596static int allocate_frame_buf(struct cam_data *cam)
1597{
1598 int i;
1599
1600 cam->frame_buf = rvmalloc(FRAME_NUM * CPIA_MAX_FRAME_SIZE);
1601 if (!cam->frame_buf)
1602 return -ENOBUFS;
1603
1604 for (i = 0; i < FRAME_NUM; i++)
1605 cam->frame[i].data = cam->frame_buf + i * CPIA_MAX_FRAME_SIZE;
1606
1607 return 0;
1608}
1609
1610static int free_frame_buf(struct cam_data *cam)
1611{
1612 int i;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001613
Linus Torvalds1da177e2005-04-16 15:20:36 -07001614 rvfree(cam->frame_buf, FRAME_NUM*CPIA_MAX_FRAME_SIZE);
1615 cam->frame_buf = NULL;
1616 for (i=0; i < FRAME_NUM; i++)
1617 cam->frame[i].data = NULL;
1618
1619 return 0;
1620}
1621
1622
1623static inline void free_frames(struct cpia_frame frame[FRAME_NUM])
1624{
1625 int i;
1626
1627 for (i=0; i < FRAME_NUM; i++)
1628 frame[i].state = FRAME_UNUSED;
1629 return;
1630}
1631
1632/**********************************************************************
1633 *
1634 * General functions
1635 *
1636 **********************************************************************/
1637/* send an arbitrary command to the camera */
1638static int do_command(struct cam_data *cam, u16 command, u8 a, u8 b, u8 c, u8 d)
1639{
1640 int retval, datasize;
1641 u8 cmd[8], data[8];
1642
1643 switch(command) {
1644 case CPIA_COMMAND_GetCPIAVersion:
1645 case CPIA_COMMAND_GetPnPID:
1646 case CPIA_COMMAND_GetCameraStatus:
1647 case CPIA_COMMAND_GetVPVersion:
1648 datasize=8;
1649 break;
1650 case CPIA_COMMAND_GetColourParams:
1651 case CPIA_COMMAND_GetColourBalance:
1652 case CPIA_COMMAND_GetExposure:
Ingo Molnar3593cab2006-02-07 06:49:14 -02001653 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001654 datasize=8;
1655 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001656 case CPIA_COMMAND_ReadMCPorts:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001657 case CPIA_COMMAND_ReadVCRegs:
1658 datasize = 4;
1659 break;
1660 default:
1661 datasize=0;
1662 break;
1663 }
1664
1665 cmd[0] = command>>8;
1666 cmd[1] = command&0xff;
1667 cmd[2] = a;
1668 cmd[3] = b;
1669 cmd[4] = c;
1670 cmd[5] = d;
1671 cmd[6] = datasize;
1672 cmd[7] = 0;
1673
1674 retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
1675 if (retval) {
1676 DBG("%x - failed, retval=%d\n", command, retval);
1677 if (command == CPIA_COMMAND_GetColourParams ||
1678 command == CPIA_COMMAND_GetColourBalance ||
1679 command == CPIA_COMMAND_GetExposure)
Ingo Molnar3593cab2006-02-07 06:49:14 -02001680 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001681 } else {
1682 switch(command) {
1683 case CPIA_COMMAND_GetCPIAVersion:
1684 cam->params.version.firmwareVersion = data[0];
1685 cam->params.version.firmwareRevision = data[1];
1686 cam->params.version.vcVersion = data[2];
1687 cam->params.version.vcRevision = data[3];
1688 break;
1689 case CPIA_COMMAND_GetPnPID:
1690 cam->params.pnpID.vendor = data[0]+(((u16)data[1])<<8);
1691 cam->params.pnpID.product = data[2]+(((u16)data[3])<<8);
1692 cam->params.pnpID.deviceRevision =
1693 data[4]+(((u16)data[5])<<8);
1694 break;
1695 case CPIA_COMMAND_GetCameraStatus:
1696 cam->params.status.systemState = data[0];
1697 cam->params.status.grabState = data[1];
1698 cam->params.status.streamState = data[2];
1699 cam->params.status.fatalError = data[3];
1700 cam->params.status.cmdError = data[4];
1701 cam->params.status.debugFlags = data[5];
1702 cam->params.status.vpStatus = data[6];
1703 cam->params.status.errorCode = data[7];
1704 break;
1705 case CPIA_COMMAND_GetVPVersion:
1706 cam->params.vpVersion.vpVersion = data[0];
1707 cam->params.vpVersion.vpRevision = data[1];
1708 cam->params.vpVersion.cameraHeadID =
1709 data[2]+(((u16)data[3])<<8);
1710 break;
1711 case CPIA_COMMAND_GetColourParams:
1712 cam->params.colourParams.brightness = data[0];
1713 cam->params.colourParams.contrast = data[1];
1714 cam->params.colourParams.saturation = 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_GetColourBalance:
1718 cam->params.colourBalance.redGain = data[0];
1719 cam->params.colourBalance.greenGain = data[1];
1720 cam->params.colourBalance.blueGain = data[2];
Ingo Molnar3593cab2006-02-07 06:49:14 -02001721 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001722 break;
1723 case CPIA_COMMAND_GetExposure:
1724 cam->params.exposure.gain = data[0];
1725 cam->params.exposure.fineExp = data[1];
1726 cam->params.exposure.coarseExpLo = data[2];
1727 cam->params.exposure.coarseExpHi = data[3];
1728 cam->params.exposure.redComp = data[4];
1729 cam->params.exposure.green1Comp = data[5];
1730 cam->params.exposure.green2Comp = data[6];
1731 cam->params.exposure.blueComp = data[7];
Ingo Molnar3593cab2006-02-07 06:49:14 -02001732 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001733 break;
1734
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001735 case CPIA_COMMAND_ReadMCPorts:
1736 if (!cam->params.qx3.qx3_detected)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001737 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001738 /* test button press */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001739 cam->params.qx3.button = ((data[1] & 0x02) == 0);
1740 if (cam->params.qx3.button) {
1741 /* button pressed - unlock the latch */
1742 do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xDF,0xDF,0);
1743 do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xFF,0xFF,0);
1744 }
1745
1746 /* test whether microscope is cradled */
1747 cam->params.qx3.cradled = ((data[2] & 0x40) == 0);
1748 break;
1749
1750 default:
1751 break;
1752 }
1753 }
1754 return retval;
1755}
1756
1757/* send a command to the camera with an additional data transaction */
1758static int do_command_extended(struct cam_data *cam, u16 command,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001759 u8 a, u8 b, u8 c, u8 d,
1760 u8 e, u8 f, u8 g, u8 h,
1761 u8 i, u8 j, u8 k, u8 l)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001762{
1763 int retval;
1764 u8 cmd[8], data[8];
1765
1766 cmd[0] = command>>8;
1767 cmd[1] = command&0xff;
1768 cmd[2] = a;
1769 cmd[3] = b;
1770 cmd[4] = c;
1771 cmd[5] = d;
1772 cmd[6] = 8;
1773 cmd[7] = 0;
1774 data[0] = e;
1775 data[1] = f;
1776 data[2] = g;
1777 data[3] = h;
1778 data[4] = i;
1779 data[5] = j;
1780 data[6] = k;
1781 data[7] = l;
1782
1783 retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
1784 if (retval)
1785 DBG("%x - failed\n", command);
1786
1787 return retval;
1788}
1789
1790/**********************************************************************
1791 *
1792 * Colorspace conversion
1793 *
1794 **********************************************************************/
1795#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16)
1796
1797static int convert420(unsigned char *yuv, unsigned char *rgb, int out_fmt,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001798 int linesize, int mmap_kludge)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001799{
1800 int y, u, v, r, g, b, y1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001801
Linus Torvalds1da177e2005-04-16 15:20:36 -07001802 /* Odd lines use the same u and v as the previous line.
1803 * Because of compression, it is necessary to get this
1804 * information from the decoded image. */
1805 switch(out_fmt) {
1806 case VIDEO_PALETTE_RGB555:
1807 y = (*yuv++ - 16) * 76310;
1808 y1 = (*yuv - 16) * 76310;
1809 r = ((*(rgb+1-linesize)) & 0x7c) << 1;
1810 g = ((*(rgb-linesize)) & 0xe0) >> 4 |
1811 ((*(rgb+1-linesize)) & 0x03) << 6;
1812 b = ((*(rgb-linesize)) & 0x1f) << 3;
1813 u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
1814 v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
1815 r = 104635 * v;
1816 g = -25690 * u - 53294 * v;
1817 b = 132278 * u;
1818 *rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3);
1819 *rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6);
1820 *rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3);
1821 *rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6);
1822 return 4;
1823 case VIDEO_PALETTE_RGB565:
1824 y = (*yuv++ - 16) * 76310;
1825 y1 = (*yuv - 16) * 76310;
1826 r = (*(rgb+1-linesize)) & 0xf8;
1827 g = ((*(rgb-linesize)) & 0xe0) >> 3 |
1828 ((*(rgb+1-linesize)) & 0x07) << 5;
1829 b = ((*(rgb-linesize)) & 0x1f) << 3;
1830 u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
1831 v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
1832 r = 104635 * v;
1833 g = -25690 * u - 53294 * v;
1834 b = 132278 * u;
1835 *rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3);
1836 *rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5);
1837 *rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3);
1838 *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5);
1839 return 4;
1840 break;
1841 case VIDEO_PALETTE_RGB24:
1842 case VIDEO_PALETTE_RGB32:
1843 y = (*yuv++ - 16) * 76310;
1844 y1 = (*yuv - 16) * 76310;
1845 if (mmap_kludge) {
1846 r = *(rgb+2-linesize);
1847 g = *(rgb+1-linesize);
1848 b = *(rgb-linesize);
1849 } else {
1850 r = *(rgb-linesize);
1851 g = *(rgb+1-linesize);
1852 b = *(rgb+2-linesize);
1853 }
1854 u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
1855 v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
1856 r = 104635 * v;
1857 g = -25690 * u + -53294 * v;
1858 b = 132278 * u;
1859 if (mmap_kludge) {
1860 *rgb++ = LIMIT(b+y);
1861 *rgb++ = LIMIT(g+y);
1862 *rgb++ = LIMIT(r+y);
1863 if(out_fmt == VIDEO_PALETTE_RGB32)
1864 rgb++;
1865 *rgb++ = LIMIT(b+y1);
1866 *rgb++ = LIMIT(g+y1);
1867 *rgb = LIMIT(r+y1);
1868 } else {
1869 *rgb++ = LIMIT(r+y);
1870 *rgb++ = LIMIT(g+y);
1871 *rgb++ = LIMIT(b+y);
1872 if(out_fmt == VIDEO_PALETTE_RGB32)
1873 rgb++;
1874 *rgb++ = LIMIT(r+y1);
1875 *rgb++ = LIMIT(g+y1);
1876 *rgb = LIMIT(b+y1);
1877 }
1878 if(out_fmt == VIDEO_PALETTE_RGB32)
1879 return 8;
1880 return 6;
1881 case VIDEO_PALETTE_YUV422:
1882 case VIDEO_PALETTE_YUYV:
1883 y = *yuv++;
1884 u = *(rgb+1-linesize);
1885 y1 = *yuv;
1886 v = *(rgb+3-linesize);
1887 *rgb++ = y;
1888 *rgb++ = u;
1889 *rgb++ = y1;
1890 *rgb = v;
1891 return 4;
1892 case VIDEO_PALETTE_UYVY:
1893 u = *(rgb-linesize);
1894 y = *yuv++;
1895 v = *(rgb+2-linesize);
1896 y1 = *yuv;
1897 *rgb++ = u;
1898 *rgb++ = y;
1899 *rgb++ = v;
1900 *rgb = y1;
1901 return 4;
1902 case VIDEO_PALETTE_GREY:
1903 *rgb++ = *yuv++;
1904 *rgb = *yuv;
1905 return 2;
1906 default:
1907 DBG("Empty: %d\n", out_fmt);
1908 return 0;
1909 }
1910}
1911
1912
1913static int yuvconvert(unsigned char *yuv, unsigned char *rgb, int out_fmt,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001914 int in_uyvy, int mmap_kludge)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001915{
1916 int y, u, v, r, g, b, y1;
1917
1918 switch(out_fmt) {
1919 case VIDEO_PALETTE_RGB555:
1920 case VIDEO_PALETTE_RGB565:
1921 case VIDEO_PALETTE_RGB24:
1922 case VIDEO_PALETTE_RGB32:
1923 if (in_uyvy) {
1924 u = *yuv++ - 128;
1925 y = (*yuv++ - 16) * 76310;
1926 v = *yuv++ - 128;
1927 y1 = (*yuv - 16) * 76310;
1928 } else {
1929 y = (*yuv++ - 16) * 76310;
1930 u = *yuv++ - 128;
1931 y1 = (*yuv++ - 16) * 76310;
1932 v = *yuv - 128;
1933 }
1934 r = 104635 * v;
1935 g = -25690 * u + -53294 * v;
1936 b = 132278 * u;
1937 break;
1938 default:
1939 y = *yuv++;
1940 u = *yuv++;
1941 y1 = *yuv++;
1942 v = *yuv;
1943 /* Just to avoid compiler warnings */
1944 r = 0;
1945 g = 0;
1946 b = 0;
1947 break;
1948 }
1949 switch(out_fmt) {
1950 case VIDEO_PALETTE_RGB555:
1951 *rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3);
1952 *rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6);
1953 *rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3);
1954 *rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6);
1955 return 4;
1956 case VIDEO_PALETTE_RGB565:
1957 *rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3);
1958 *rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5);
1959 *rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3);
1960 *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5);
1961 return 4;
1962 case VIDEO_PALETTE_RGB24:
1963 if (mmap_kludge) {
1964 *rgb++ = LIMIT(b+y);
1965 *rgb++ = LIMIT(g+y);
1966 *rgb++ = LIMIT(r+y);
1967 *rgb++ = LIMIT(b+y1);
1968 *rgb++ = LIMIT(g+y1);
1969 *rgb = LIMIT(r+y1);
1970 } else {
1971 *rgb++ = LIMIT(r+y);
1972 *rgb++ = LIMIT(g+y);
1973 *rgb++ = LIMIT(b+y);
1974 *rgb++ = LIMIT(r+y1);
1975 *rgb++ = LIMIT(g+y1);
1976 *rgb = LIMIT(b+y1);
1977 }
1978 return 6;
1979 case VIDEO_PALETTE_RGB32:
1980 if (mmap_kludge) {
1981 *rgb++ = LIMIT(b+y);
1982 *rgb++ = LIMIT(g+y);
1983 *rgb++ = LIMIT(r+y);
1984 rgb++;
1985 *rgb++ = LIMIT(b+y1);
1986 *rgb++ = LIMIT(g+y1);
1987 *rgb = LIMIT(r+y1);
1988 } else {
1989 *rgb++ = LIMIT(r+y);
1990 *rgb++ = LIMIT(g+y);
1991 *rgb++ = LIMIT(b+y);
1992 rgb++;
1993 *rgb++ = LIMIT(r+y1);
1994 *rgb++ = LIMIT(g+y1);
1995 *rgb = LIMIT(b+y1);
1996 }
1997 return 8;
1998 case VIDEO_PALETTE_GREY:
1999 *rgb++ = y;
2000 *rgb = y1;
2001 return 2;
2002 case VIDEO_PALETTE_YUV422:
2003 case VIDEO_PALETTE_YUYV:
2004 *rgb++ = y;
2005 *rgb++ = u;
2006 *rgb++ = y1;
2007 *rgb = v;
2008 return 4;
2009 case VIDEO_PALETTE_UYVY:
2010 *rgb++ = u;
2011 *rgb++ = y;
2012 *rgb++ = v;
2013 *rgb = y1;
2014 return 4;
2015 default:
2016 DBG("Empty: %d\n", out_fmt);
2017 return 0;
2018 }
2019}
2020
2021static int skipcount(int count, int fmt)
2022{
2023 switch(fmt) {
2024 case VIDEO_PALETTE_GREY:
2025 return count;
2026 case VIDEO_PALETTE_RGB555:
2027 case VIDEO_PALETTE_RGB565:
2028 case VIDEO_PALETTE_YUV422:
2029 case VIDEO_PALETTE_YUYV:
2030 case VIDEO_PALETTE_UYVY:
2031 return 2*count;
2032 case VIDEO_PALETTE_RGB24:
2033 return 3*count;
2034 case VIDEO_PALETTE_RGB32:
2035 return 4*count;
2036 default:
2037 return 0;
2038 }
2039}
2040
2041static int parse_picture(struct cam_data *cam, int size)
2042{
2043 u8 *obuf, *ibuf, *end_obuf;
2044 int ll, in_uyvy, compressed, decimation, even_line, origsize, out_fmt;
2045 int rows, cols, linesize, subsample_422;
2046
2047 /* make sure params don't change while we are decoding */
Ingo Molnar3593cab2006-02-07 06:49:14 -02002048 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002049
2050 obuf = cam->decompressed_frame.data;
2051 end_obuf = obuf+CPIA_MAX_FRAME_SIZE;
2052 ibuf = cam->raw_image;
2053 origsize = size;
2054 out_fmt = cam->vp.palette;
2055
2056 if ((ibuf[0] != MAGIC_0) || (ibuf[1] != MAGIC_1)) {
2057 LOG("header not found\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 }
2061
2062 if ((ibuf[16] != VIDEOSIZE_QCIF) && (ibuf[16] != VIDEOSIZE_CIF)) {
2063 LOG("wrong video size\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02002064 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002065 return -1;
2066 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002067
Linus Torvalds1da177e2005-04-16 15:20:36 -07002068 if (ibuf[17] != SUBSAMPLE_420 && ibuf[17] != SUBSAMPLE_422) {
2069 LOG("illegal subtype %d\n",ibuf[17]);
Ingo Molnar3593cab2006-02-07 06:49:14 -02002070 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002071 return -1;
2072 }
2073 subsample_422 = ibuf[17] == SUBSAMPLE_422;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002074
Linus Torvalds1da177e2005-04-16 15:20:36 -07002075 if (ibuf[18] != YUVORDER_YUYV && ibuf[18] != YUVORDER_UYVY) {
2076 LOG("illegal yuvorder %d\n",ibuf[18]);
Ingo Molnar3593cab2006-02-07 06:49:14 -02002077 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002078 return -1;
2079 }
2080 in_uyvy = ibuf[18] == YUVORDER_UYVY;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002081
Linus Torvalds1da177e2005-04-16 15:20:36 -07002082 if ((ibuf[24] != cam->params.roi.colStart) ||
2083 (ibuf[25] != cam->params.roi.colEnd) ||
2084 (ibuf[26] != cam->params.roi.rowStart) ||
2085 (ibuf[27] != cam->params.roi.rowEnd)) {
2086 LOG("ROI mismatch\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02002087 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002088 return -1;
2089 }
2090 cols = 8*(ibuf[25] - ibuf[24]);
2091 rows = 4*(ibuf[27] - ibuf[26]);
2092
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002093
Linus Torvalds1da177e2005-04-16 15:20:36 -07002094 if ((ibuf[28] != NOT_COMPRESSED) && (ibuf[28] != COMPRESSED)) {
2095 LOG("illegal compression %d\n",ibuf[28]);
Ingo Molnar3593cab2006-02-07 06:49:14 -02002096 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002097 return -1;
2098 }
2099 compressed = (ibuf[28] == COMPRESSED);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002100
Linus Torvalds1da177e2005-04-16 15:20:36 -07002101 if (ibuf[29] != NO_DECIMATION && ibuf[29] != DECIMATION_ENAB) {
2102 LOG("illegal decimation %d\n",ibuf[29]);
Ingo Molnar3593cab2006-02-07 06:49:14 -02002103 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002104 return -1;
2105 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002106 decimation = (ibuf[29] == DECIMATION_ENAB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002107
2108 cam->params.yuvThreshold.yThreshold = ibuf[30];
2109 cam->params.yuvThreshold.uvThreshold = ibuf[31];
2110 cam->params.status.systemState = ibuf[32];
2111 cam->params.status.grabState = ibuf[33];
2112 cam->params.status.streamState = ibuf[34];
2113 cam->params.status.fatalError = ibuf[35];
2114 cam->params.status.cmdError = ibuf[36];
2115 cam->params.status.debugFlags = ibuf[37];
2116 cam->params.status.vpStatus = ibuf[38];
2117 cam->params.status.errorCode = ibuf[39];
2118 cam->fps = ibuf[41];
Ingo Molnar3593cab2006-02-07 06:49:14 -02002119 mutex_unlock(&cam->param_lock);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002120
Linus Torvalds1da177e2005-04-16 15:20:36 -07002121 linesize = skipcount(cols, out_fmt);
2122 ibuf += FRAME_HEADER_SIZE;
2123 size -= FRAME_HEADER_SIZE;
2124 ll = ibuf[0] | (ibuf[1] << 8);
2125 ibuf += 2;
2126 even_line = 1;
2127
2128 while (size > 0) {
2129 size -= (ll+2);
2130 if (size < 0) {
2131 LOG("Insufficient data in buffer\n");
2132 return -1;
2133 }
2134
2135 while (ll > 1) {
2136 if (!compressed || (compressed && !(*ibuf & 1))) {
2137 if(subsample_422 || even_line) {
2138 obuf += yuvconvert(ibuf, obuf, out_fmt,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002139 in_uyvy, cam->mmap_kludge);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002140 ibuf += 4;
2141 ll -= 4;
2142 } else {
2143 /* SUBSAMPLE_420 on an odd line */
2144 obuf += convert420(ibuf, obuf,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002145 out_fmt, linesize,
2146 cam->mmap_kludge);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002147 ibuf += 2;
2148 ll -= 2;
2149 }
2150 } else {
2151 /*skip compressed interval from previous frame*/
2152 obuf += skipcount(*ibuf >> 1, out_fmt);
2153 if (obuf > end_obuf) {
2154 LOG("Insufficient buffer size\n");
2155 return -1;
2156 }
2157 ++ibuf;
2158 ll--;
2159 }
2160 }
2161 if (ll == 1) {
2162 if (*ibuf != EOL) {
2163 DBG("EOL not found giving up after %d/%d"
2164 " bytes\n", origsize-size, origsize);
2165 return -1;
2166 }
2167
2168 ++ibuf; /* skip over EOL */
2169
2170 if ((size > 3) && (ibuf[0] == EOI) && (ibuf[1] == EOI) &&
2171 (ibuf[2] == EOI) && (ibuf[3] == EOI)) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002172 size -= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002173 break;
2174 }
2175
2176 if(decimation) {
2177 /* skip the odd lines for now */
2178 obuf += linesize;
2179 }
2180
2181 if (size > 1) {
2182 ll = ibuf[0] | (ibuf[1] << 8);
2183 ibuf += 2; /* skip over line length */
2184 }
2185 if(!decimation)
2186 even_line = !even_line;
2187 } else {
2188 LOG("line length was not 1 but %d after %d/%d bytes\n",
2189 ll, origsize-size, origsize);
2190 return -1;
2191 }
2192 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002193
Linus Torvalds1da177e2005-04-16 15:20:36 -07002194 if(decimation) {
2195 /* interpolate odd rows */
2196 int i, j;
2197 u8 *prev, *next;
2198 prev = cam->decompressed_frame.data;
2199 obuf = prev+linesize;
2200 next = obuf+linesize;
2201 for(i=1; i<rows-1; i+=2) {
2202 for(j=0; j<linesize; ++j) {
2203 *obuf++ = ((int)*prev++ + *next++) / 2;
2204 }
2205 prev += linesize;
2206 obuf += linesize;
2207 next += linesize;
2208 }
2209 /* last row is odd, just copy previous row */
2210 memcpy(obuf, prev, linesize);
2211 }
2212
2213 cam->decompressed_frame.count = obuf-cam->decompressed_frame.data;
2214
2215 return cam->decompressed_frame.count;
2216}
2217
2218/* InitStreamCap wrapper to select correct start line */
2219static inline int init_stream_cap(struct cam_data *cam)
2220{
2221 return do_command(cam, CPIA_COMMAND_InitStreamCap,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002222 0, cam->params.streamStartLine, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002223}
2224
2225
2226/* find_over_exposure
2227 * Finds a suitable value of OverExposure for use with SetFlickerCtrl
2228 * Some calculation is required because this value changes with the brightness
2229 * set with SetColourParameters
2230 *
2231 * Parameters: Brightness - last brightness value set with SetColourParameters
2232 *
2233 * Returns: OverExposure value to use with SetFlickerCtrl
2234 */
2235#define FLICKER_MAX_EXPOSURE 250
2236#define FLICKER_ALLOWABLE_OVER_EXPOSURE 146
2237#define FLICKER_BRIGHTNESS_CONSTANT 59
2238static int find_over_exposure(int brightness)
2239{
2240 int MaxAllowableOverExposure, OverExposure;
2241
2242 MaxAllowableOverExposure = FLICKER_MAX_EXPOSURE - brightness -
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002243 FLICKER_BRIGHTNESS_CONSTANT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002244
2245 if (MaxAllowableOverExposure < FLICKER_ALLOWABLE_OVER_EXPOSURE) {
2246 OverExposure = MaxAllowableOverExposure;
2247 } else {
2248 OverExposure = FLICKER_ALLOWABLE_OVER_EXPOSURE;
2249 }
2250
2251 return OverExposure;
2252}
2253#undef FLICKER_MAX_EXPOSURE
2254#undef FLICKER_ALLOWABLE_OVER_EXPOSURE
2255#undef FLICKER_BRIGHTNESS_CONSTANT
2256
2257/* update various camera modes and settings */
2258static void dispatch_commands(struct cam_data *cam)
2259{
Ingo Molnar3593cab2006-02-07 06:49:14 -02002260 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002261 if (cam->cmd_queue==COMMAND_NONE) {
Ingo Molnar3593cab2006-02-07 06:49:14 -02002262 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002263 return;
2264 }
2265 DEB_BYTE(cam->cmd_queue);
2266 DEB_BYTE(cam->cmd_queue>>8);
2267 if (cam->cmd_queue & COMMAND_SETFORMAT) {
2268 do_command(cam, CPIA_COMMAND_SetFormat,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002269 cam->params.format.videoSize,
2270 cam->params.format.subSample,
2271 cam->params.format.yuvOrder, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002272 do_command(cam, CPIA_COMMAND_SetROI,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002273 cam->params.roi.colStart, cam->params.roi.colEnd,
2274 cam->params.roi.rowStart, cam->params.roi.rowEnd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002275 cam->first_frame = 1;
2276 }
2277
2278 if (cam->cmd_queue & COMMAND_SETCOLOURPARAMS)
2279 do_command(cam, CPIA_COMMAND_SetColourParams,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002280 cam->params.colourParams.brightness,
2281 cam->params.colourParams.contrast,
2282 cam->params.colourParams.saturation, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002283
2284 if (cam->cmd_queue & COMMAND_SETAPCOR)
2285 do_command(cam, CPIA_COMMAND_SetApcor,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002286 cam->params.apcor.gain1,
2287 cam->params.apcor.gain2,
2288 cam->params.apcor.gain4,
2289 cam->params.apcor.gain8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002290
2291 if (cam->cmd_queue & COMMAND_SETVLOFFSET)
2292 do_command(cam, CPIA_COMMAND_SetVLOffset,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002293 cam->params.vlOffset.gain1,
2294 cam->params.vlOffset.gain2,
2295 cam->params.vlOffset.gain4,
2296 cam->params.vlOffset.gain8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002297
2298 if (cam->cmd_queue & COMMAND_SETEXPOSURE) {
2299 do_command_extended(cam, CPIA_COMMAND_SetExposure,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002300 cam->params.exposure.gainMode,
2301 1,
2302 cam->params.exposure.compMode,
2303 cam->params.exposure.centreWeight,
2304 cam->params.exposure.gain,
2305 cam->params.exposure.fineExp,
2306 cam->params.exposure.coarseExpLo,
2307 cam->params.exposure.coarseExpHi,
2308 cam->params.exposure.redComp,
2309 cam->params.exposure.green1Comp,
2310 cam->params.exposure.green2Comp,
2311 cam->params.exposure.blueComp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002312 if(cam->params.exposure.expMode != 1) {
2313 do_command_extended(cam, CPIA_COMMAND_SetExposure,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002314 0,
2315 cam->params.exposure.expMode,
2316 0, 0,
2317 cam->params.exposure.gain,
2318 cam->params.exposure.fineExp,
2319 cam->params.exposure.coarseExpLo,
2320 cam->params.exposure.coarseExpHi,
2321 0, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002322 }
2323 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002324
Linus Torvalds1da177e2005-04-16 15:20:36 -07002325 if (cam->cmd_queue & COMMAND_SETCOLOURBALANCE) {
2326 if (cam->params.colourBalance.balanceMode == 1) {
2327 do_command(cam, CPIA_COMMAND_SetColourBalance,
2328 1,
2329 cam->params.colourBalance.redGain,
2330 cam->params.colourBalance.greenGain,
2331 cam->params.colourBalance.blueGain);
2332 do_command(cam, CPIA_COMMAND_SetColourBalance,
2333 3, 0, 0, 0);
2334 }
2335 if (cam->params.colourBalance.balanceMode == 2) {
2336 do_command(cam, CPIA_COMMAND_SetColourBalance,
2337 2, 0, 0, 0);
2338 }
2339 if (cam->params.colourBalance.balanceMode == 3) {
2340 do_command(cam, CPIA_COMMAND_SetColourBalance,
2341 3, 0, 0, 0);
2342 }
2343 }
2344
2345 if (cam->cmd_queue & COMMAND_SETCOMPRESSIONTARGET)
2346 do_command(cam, CPIA_COMMAND_SetCompressionTarget,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002347 cam->params.compressionTarget.frTargeting,
2348 cam->params.compressionTarget.targetFR,
2349 cam->params.compressionTarget.targetQ, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002350
2351 if (cam->cmd_queue & COMMAND_SETYUVTHRESH)
2352 do_command(cam, CPIA_COMMAND_SetYUVThresh,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002353 cam->params.yuvThreshold.yThreshold,
2354 cam->params.yuvThreshold.uvThreshold, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002355
2356 if (cam->cmd_queue & COMMAND_SETCOMPRESSIONPARAMS)
2357 do_command_extended(cam, CPIA_COMMAND_SetCompressionParams,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002358 0, 0, 0, 0,
2359 cam->params.compressionParams.hysteresis,
2360 cam->params.compressionParams.threshMax,
2361 cam->params.compressionParams.smallStep,
2362 cam->params.compressionParams.largeStep,
2363 cam->params.compressionParams.decimationHysteresis,
2364 cam->params.compressionParams.frDiffStepThresh,
2365 cam->params.compressionParams.qDiffStepThresh,
2366 cam->params.compressionParams.decimationThreshMod);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002367
2368 if (cam->cmd_queue & COMMAND_SETCOMPRESSION)
2369 do_command(cam, CPIA_COMMAND_SetCompression,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002370 cam->params.compression.mode,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002371 cam->params.compression.decimation, 0, 0);
2372
2373 if (cam->cmd_queue & COMMAND_SETSENSORFPS)
2374 do_command(cam, CPIA_COMMAND_SetSensorFPS,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002375 cam->params.sensorFps.divisor,
2376 cam->params.sensorFps.baserate, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002377
2378 if (cam->cmd_queue & COMMAND_SETFLICKERCTRL)
2379 do_command(cam, CPIA_COMMAND_SetFlickerCtrl,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002380 cam->params.flickerControl.flickerMode,
2381 cam->params.flickerControl.coarseJump,
2382 abs(cam->params.flickerControl.allowableOverExposure),
2383 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002384
2385 if (cam->cmd_queue & COMMAND_SETECPTIMING)
2386 do_command(cam, CPIA_COMMAND_SetECPTiming,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002387 cam->params.ecpTiming, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002388
2389 if (cam->cmd_queue & COMMAND_PAUSE)
2390 do_command(cam, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0);
2391
2392 if (cam->cmd_queue & COMMAND_RESUME)
2393 init_stream_cap(cam);
2394
2395 if (cam->cmd_queue & COMMAND_SETLIGHTS && cam->params.qx3.qx3_detected)
2396 {
2397 int p1 = (cam->params.qx3.bottomlight == 0) << 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002398 int p2 = (cam->params.qx3.toplight == 0) << 3;
2399 do_command(cam, CPIA_COMMAND_WriteVCReg, 0x90, 0x8F, 0x50, 0);
2400 do_command(cam, CPIA_COMMAND_WriteMCPort, 2, 0, (p1|p2|0xE0), 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002401 }
2402
2403 cam->cmd_queue = COMMAND_NONE;
Ingo Molnar3593cab2006-02-07 06:49:14 -02002404 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002405 return;
2406}
2407
2408
2409
2410static void set_flicker(struct cam_params *params, volatile u32 *command_flags,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002411 int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002412{
2413 /* Everything in here is from the Windows driver */
2414#define FIRMWARE_VERSION(x,y) (params->version.firmwareVersion == (x) && \
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002415 params->version.firmwareRevision == (y))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002416/* define for compgain calculation */
2417#if 0
2418#define COMPGAIN(base, curexp, newexp) \
2419 (u8) ((((float) base - 128.0) * ((float) curexp / (float) newexp)) + 128.5)
2420#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
2421 (u16)((float)curexp * (float)(u8)(curcomp + 128) / (float)(u8)(basecomp - 128))
2422#else
2423 /* equivalent functions without floating point math */
2424#define COMPGAIN(base, curexp, newexp) \
2425 (u8)(128 + (((u32)(2*(base-128)*curexp + newexp)) / (2* newexp)) )
2426#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
2427 (u16)(((u32)(curexp * (u8)(curcomp + 128)) / (u8)(basecomp - 128)))
2428#endif
2429
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002430
Linus Torvalds1da177e2005-04-16 15:20:36 -07002431 int currentexp = params->exposure.coarseExpLo +
2432 params->exposure.coarseExpHi*256;
2433 int startexp;
2434 if (on) {
2435 int cj = params->flickerControl.coarseJump;
2436 params->flickerControl.flickerMode = 1;
2437 params->flickerControl.disabled = 0;
2438 if(params->exposure.expMode != 2)
2439 *command_flags |= COMMAND_SETEXPOSURE;
2440 params->exposure.expMode = 2;
2441 currentexp = currentexp << params->exposure.gain;
2442 params->exposure.gain = 0;
2443 /* round down current exposure to nearest value */
2444 startexp = (currentexp + ROUND_UP_EXP_FOR_FLICKER) / cj;
2445 if(startexp < 1)
2446 startexp = 1;
2447 startexp = (startexp * cj) - 1;
2448 if(FIRMWARE_VERSION(1,2))
2449 while(startexp > MAX_EXP_102)
2450 startexp -= cj;
2451 else
2452 while(startexp > MAX_EXP)
2453 startexp -= cj;
2454 params->exposure.coarseExpLo = startexp & 0xff;
2455 params->exposure.coarseExpHi = startexp >> 8;
2456 if (currentexp > startexp) {
2457 if (currentexp > (2 * startexp))
2458 currentexp = 2 * startexp;
2459 params->exposure.redComp = COMPGAIN (COMP_RED, currentexp, startexp);
2460 params->exposure.green1Comp = COMPGAIN (COMP_GREEN1, currentexp, startexp);
2461 params->exposure.green2Comp = COMPGAIN (COMP_GREEN2, currentexp, startexp);
2462 params->exposure.blueComp = COMPGAIN (COMP_BLUE, currentexp, startexp);
2463 } else {
2464 params->exposure.redComp = COMP_RED;
2465 params->exposure.green1Comp = COMP_GREEN1;
2466 params->exposure.green2Comp = COMP_GREEN2;
2467 params->exposure.blueComp = COMP_BLUE;
2468 }
2469 if(FIRMWARE_VERSION(1,2))
2470 params->exposure.compMode = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002471 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07002472 params->exposure.compMode = 1;
2473
2474 params->apcor.gain1 = 0x18;
2475 params->apcor.gain2 = 0x18;
2476 params->apcor.gain4 = 0x16;
2477 params->apcor.gain8 = 0x14;
2478 *command_flags |= COMMAND_SETAPCOR;
2479 } else {
2480 params->flickerControl.flickerMode = 0;
2481 params->flickerControl.disabled = 1;
2482 /* Coarse = average of equivalent coarse for each comp channel */
2483 startexp = EXP_FROM_COMP(COMP_RED, params->exposure.redComp, currentexp);
2484 startexp += EXP_FROM_COMP(COMP_GREEN1, params->exposure.green1Comp, currentexp);
2485 startexp += EXP_FROM_COMP(COMP_GREEN2, params->exposure.green2Comp, currentexp);
2486 startexp += EXP_FROM_COMP(COMP_BLUE, params->exposure.blueComp, currentexp);
2487 startexp = startexp >> 2;
2488 while(startexp > MAX_EXP &&
2489 params->exposure.gain < params->exposure.gainMode-1) {
2490 startexp = startexp >> 1;
2491 ++params->exposure.gain;
2492 }
2493 if(FIRMWARE_VERSION(1,2) && startexp > MAX_EXP_102)
2494 startexp = MAX_EXP_102;
2495 if(startexp > MAX_EXP)
2496 startexp = MAX_EXP;
2497 params->exposure.coarseExpLo = startexp&0xff;
2498 params->exposure.coarseExpHi = startexp >> 8;
2499 params->exposure.redComp = COMP_RED;
2500 params->exposure.green1Comp = COMP_GREEN1;
2501 params->exposure.green2Comp = COMP_GREEN2;
2502 params->exposure.blueComp = COMP_BLUE;
2503 params->exposure.compMode = 1;
2504 *command_flags |= COMMAND_SETEXPOSURE;
2505 params->apcor.gain1 = 0x18;
2506 params->apcor.gain2 = 0x16;
2507 params->apcor.gain4 = 0x24;
2508 params->apcor.gain8 = 0x34;
2509 *command_flags |= COMMAND_SETAPCOR;
2510 }
2511 params->vlOffset.gain1 = 20;
2512 params->vlOffset.gain2 = 24;
2513 params->vlOffset.gain4 = 26;
2514 params->vlOffset.gain8 = 26;
2515 *command_flags |= COMMAND_SETVLOFFSET;
2516#undef FIRMWARE_VERSION
2517#undef EXP_FROM_COMP
2518#undef COMPGAIN
2519}
2520
2521#define FIRMWARE_VERSION(x,y) (cam->params.version.firmwareVersion == (x) && \
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002522 cam->params.version.firmwareRevision == (y))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002523/* monitor the exposure and adjust the sensor frame rate if needed */
2524static void monitor_exposure(struct cam_data *cam)
2525{
2526 u8 exp_acc, bcomp, gain, coarseL, cmd[8], data[8];
2527 int retval, light_exp, dark_exp, very_dark_exp;
2528 int old_exposure, new_exposure, framerate;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002529
Linus Torvalds1da177e2005-04-16 15:20:36 -07002530 /* get necessary stats and register settings from camera */
2531 /* do_command can't handle this, so do it ourselves */
2532 cmd[0] = CPIA_COMMAND_ReadVPRegs>>8;
2533 cmd[1] = CPIA_COMMAND_ReadVPRegs&0xff;
2534 cmd[2] = 30;
2535 cmd[3] = 4;
2536 cmd[4] = 9;
2537 cmd[5] = 8;
2538 cmd[6] = 8;
2539 cmd[7] = 0;
2540 retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
2541 if (retval) {
2542 LOG("ReadVPRegs(30,4,9,8) - failed, retval=%d\n",
2543 retval);
2544 return;
2545 }
2546 exp_acc = data[0];
2547 bcomp = data[1];
2548 gain = data[2];
2549 coarseL = data[3];
2550
Ingo Molnar3593cab2006-02-07 06:49:14 -02002551 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002552 light_exp = cam->params.colourParams.brightness +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002553 TC - 50 + EXP_ACC_LIGHT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002554 if(light_exp > 255)
2555 light_exp = 255;
2556 dark_exp = cam->params.colourParams.brightness +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002557 TC - 50 - EXP_ACC_DARK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002558 if(dark_exp < 0)
2559 dark_exp = 0;
2560 very_dark_exp = dark_exp/2;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002561
Linus Torvalds1da177e2005-04-16 15:20:36 -07002562 old_exposure = cam->params.exposure.coarseExpHi * 256 +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002563 cam->params.exposure.coarseExpLo;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002564
2565 if(!cam->params.flickerControl.disabled) {
2566 /* Flicker control on */
2567 int max_comp = FIRMWARE_VERSION(1,2) ? MAX_COMP : HIGH_COMP_102;
2568 bcomp += 128; /* decode */
2569 if(bcomp >= max_comp && exp_acc < dark_exp) {
2570 /* dark */
2571 if(exp_acc < very_dark_exp) {
2572 /* very dark */
2573 if(cam->exposure_status == EXPOSURE_VERY_DARK)
2574 ++cam->exposure_count;
2575 else {
2576 cam->exposure_status = EXPOSURE_VERY_DARK;
2577 cam->exposure_count = 1;
2578 }
2579 } else {
2580 /* just dark */
2581 if(cam->exposure_status == EXPOSURE_DARK)
2582 ++cam->exposure_count;
2583 else {
2584 cam->exposure_status = EXPOSURE_DARK;
2585 cam->exposure_count = 1;
2586 }
2587 }
2588 } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) {
2589 /* light */
2590 if(old_exposure <= VERY_LOW_EXP) {
2591 /* very light */
2592 if(cam->exposure_status == EXPOSURE_VERY_LIGHT)
2593 ++cam->exposure_count;
2594 else {
2595 cam->exposure_status = EXPOSURE_VERY_LIGHT;
2596 cam->exposure_count = 1;
2597 }
2598 } else {
2599 /* just light */
2600 if(cam->exposure_status == EXPOSURE_LIGHT)
2601 ++cam->exposure_count;
2602 else {
2603 cam->exposure_status = EXPOSURE_LIGHT;
2604 cam->exposure_count = 1;
2605 }
2606 }
2607 } else {
2608 /* not dark or light */
2609 cam->exposure_status = EXPOSURE_NORMAL;
2610 }
2611 } else {
2612 /* Flicker control off */
2613 if(old_exposure >= MAX_EXP && exp_acc < dark_exp) {
2614 /* dark */
2615 if(exp_acc < very_dark_exp) {
2616 /* very dark */
2617 if(cam->exposure_status == EXPOSURE_VERY_DARK)
2618 ++cam->exposure_count;
2619 else {
2620 cam->exposure_status = EXPOSURE_VERY_DARK;
2621 cam->exposure_count = 1;
2622 }
2623 } else {
2624 /* just dark */
2625 if(cam->exposure_status == EXPOSURE_DARK)
2626 ++cam->exposure_count;
2627 else {
2628 cam->exposure_status = EXPOSURE_DARK;
2629 cam->exposure_count = 1;
2630 }
2631 }
2632 } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) {
2633 /* light */
2634 if(old_exposure <= VERY_LOW_EXP) {
2635 /* very light */
2636 if(cam->exposure_status == EXPOSURE_VERY_LIGHT)
2637 ++cam->exposure_count;
2638 else {
2639 cam->exposure_status = EXPOSURE_VERY_LIGHT;
2640 cam->exposure_count = 1;
2641 }
2642 } else {
2643 /* just light */
2644 if(cam->exposure_status == EXPOSURE_LIGHT)
2645 ++cam->exposure_count;
2646 else {
2647 cam->exposure_status = EXPOSURE_LIGHT;
2648 cam->exposure_count = 1;
2649 }
2650 }
2651 } else {
2652 /* not dark or light */
2653 cam->exposure_status = EXPOSURE_NORMAL;
2654 }
2655 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002656
Linus Torvalds1da177e2005-04-16 15:20:36 -07002657 framerate = cam->fps;
2658 if(framerate > 30 || framerate < 1)
2659 framerate = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002660
Linus Torvalds1da177e2005-04-16 15:20:36 -07002661 if(!cam->params.flickerControl.disabled) {
2662 /* Flicker control on */
2663 if((cam->exposure_status == EXPOSURE_VERY_DARK ||
2664 cam->exposure_status == EXPOSURE_DARK) &&
2665 cam->exposure_count >= DARK_TIME*framerate &&
2666 cam->params.sensorFps.divisor < 3) {
2667
2668 /* dark for too long */
2669 ++cam->params.sensorFps.divisor;
2670 cam->cmd_queue |= COMMAND_SETSENSORFPS;
2671
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002672 cam->params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07002673 flicker_jumps[cam->mainsFreq]
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002674 [cam->params.sensorFps.baserate]
2675 [cam->params.sensorFps.divisor];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002676 cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
2677
2678 new_exposure = cam->params.flickerControl.coarseJump-1;
2679 while(new_exposure < old_exposure/2)
2680 new_exposure += cam->params.flickerControl.coarseJump;
2681 cam->params.exposure.coarseExpLo = new_exposure & 0xff;
2682 cam->params.exposure.coarseExpHi = new_exposure >> 8;
2683 cam->cmd_queue |= COMMAND_SETEXPOSURE;
2684 cam->exposure_status = EXPOSURE_NORMAL;
2685 LOG("Automatically decreasing sensor_fps\n");
2686
2687 } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT ||
2688 cam->exposure_status == EXPOSURE_LIGHT) &&
2689 cam->exposure_count >= LIGHT_TIME*framerate &&
2690 cam->params.sensorFps.divisor > 0) {
2691
2692 /* light for too long */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002693 int max_exp = FIRMWARE_VERSION(1,2) ? MAX_EXP_102 : MAX_EXP ;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002694
2695 --cam->params.sensorFps.divisor;
2696 cam->cmd_queue |= COMMAND_SETSENSORFPS;
2697
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002698 cam->params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07002699 flicker_jumps[cam->mainsFreq]
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002700 [cam->params.sensorFps.baserate]
2701 [cam->params.sensorFps.divisor];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002702 cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
2703
2704 new_exposure = cam->params.flickerControl.coarseJump-1;
2705 while(new_exposure < 2*old_exposure &&
2706 new_exposure+
2707 cam->params.flickerControl.coarseJump < max_exp)
2708 new_exposure += cam->params.flickerControl.coarseJump;
2709 cam->params.exposure.coarseExpLo = new_exposure & 0xff;
2710 cam->params.exposure.coarseExpHi = new_exposure >> 8;
2711 cam->cmd_queue |= COMMAND_SETEXPOSURE;
2712 cam->exposure_status = EXPOSURE_NORMAL;
2713 LOG("Automatically increasing sensor_fps\n");
2714 }
2715 } else {
2716 /* Flicker control off */
2717 if((cam->exposure_status == EXPOSURE_VERY_DARK ||
2718 cam->exposure_status == EXPOSURE_DARK) &&
2719 cam->exposure_count >= DARK_TIME*framerate &&
2720 cam->params.sensorFps.divisor < 3) {
2721
2722 /* dark for too long */
2723 ++cam->params.sensorFps.divisor;
2724 cam->cmd_queue |= COMMAND_SETSENSORFPS;
2725
2726 if(cam->params.exposure.gain > 0) {
2727 --cam->params.exposure.gain;
2728 cam->cmd_queue |= COMMAND_SETEXPOSURE;
2729 }
2730 cam->exposure_status = EXPOSURE_NORMAL;
2731 LOG("Automatically decreasing sensor_fps\n");
2732
2733 } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT ||
2734 cam->exposure_status == EXPOSURE_LIGHT) &&
2735 cam->exposure_count >= LIGHT_TIME*framerate &&
2736 cam->params.sensorFps.divisor > 0) {
2737
2738 /* light for too long */
2739 --cam->params.sensorFps.divisor;
2740 cam->cmd_queue |= COMMAND_SETSENSORFPS;
2741
2742 if(cam->params.exposure.gain <
2743 cam->params.exposure.gainMode-1) {
2744 ++cam->params.exposure.gain;
2745 cam->cmd_queue |= COMMAND_SETEXPOSURE;
2746 }
2747 cam->exposure_status = EXPOSURE_NORMAL;
2748 LOG("Automatically increasing sensor_fps\n");
2749 }
2750 }
Ingo Molnar3593cab2006-02-07 06:49:14 -02002751 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002752}
2753
2754/*-----------------------------------------------------------------*/
2755/* if flicker is switched off, this function switches it back on.It checks,
2756 however, that conditions are suitable before restarting it.
2757 This should only be called for firmware version 1.2.
2758
2759 It also adjust the colour balance when an exposure step is detected - as
2760 long as flicker is running
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002761*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07002762static void restart_flicker(struct cam_data *cam)
2763{
2764 int cam_exposure, old_exp;
2765 if(!FIRMWARE_VERSION(1,2))
2766 return;
Ingo Molnar3593cab2006-02-07 06:49:14 -02002767 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002768 if(cam->params.flickerControl.flickerMode == 0 ||
2769 cam->raw_image[39] == 0) {
Ingo Molnar3593cab2006-02-07 06:49:14 -02002770 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002771 return;
2772 }
2773 cam_exposure = cam->raw_image[39]*2;
2774 old_exp = cam->params.exposure.coarseExpLo +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002775 cam->params.exposure.coarseExpHi*256;
2776 /*
2777 see how far away camera exposure is from a valid
2778 flicker exposure value
2779 */
2780 cam_exposure %= cam->params.flickerControl.coarseJump;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002781 if(!cam->params.flickerControl.disabled &&
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002782 cam_exposure <= cam->params.flickerControl.coarseJump - 3) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002783 /* Flicker control auto-disabled */
2784 cam->params.flickerControl.disabled = 1;
2785 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002786
Linus Torvalds1da177e2005-04-16 15:20:36 -07002787 if(cam->params.flickerControl.disabled &&
2788 cam->params.flickerControl.flickerMode &&
2789 old_exp > cam->params.flickerControl.coarseJump +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002790 ROUND_UP_EXP_FOR_FLICKER) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002791 /* exposure is now high enough to switch
2792 flicker control back on */
2793 set_flicker(&cam->params, &cam->cmd_queue, 1);
2794 if((cam->cmd_queue & COMMAND_SETEXPOSURE) &&
2795 cam->params.exposure.expMode == 2)
2796 cam->exposure_status = EXPOSURE_NORMAL;
2797
2798 }
Ingo Molnar3593cab2006-02-07 06:49:14 -02002799 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002800}
2801#undef FIRMWARE_VERSION
2802
2803static int clear_stall(struct cam_data *cam)
2804{
2805 /* FIXME: Does this actually work? */
2806 LOG("Clearing stall\n");
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002807
Linus Torvalds1da177e2005-04-16 15:20:36 -07002808 cam->ops->streamRead(cam->lowlevel_data, cam->raw_image, 0);
2809 do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0);
2810 return cam->params.status.streamState != STREAM_PAUSED;
2811}
2812
2813/* kernel thread function to read image from camera */
2814static int fetch_frame(void *data)
2815{
2816 int image_size, retry;
2817 struct cam_data *cam = (struct cam_data *)data;
2818 unsigned long oldjif, rate, diff;
2819
2820 /* Allow up to two bad images in a row to be read and
2821 * ignored before an error is reported */
2822 for (retry = 0; retry < 3; ++retry) {
2823 if (retry)
2824 DBG("retry=%d\n", retry);
2825
2826 if (!cam->ops)
2827 continue;
2828
2829 /* load first frame always uncompressed */
2830 if (cam->first_frame &&
2831 cam->params.compression.mode != CPIA_COMPRESSION_NONE) {
2832 do_command(cam, CPIA_COMMAND_SetCompression,
2833 CPIA_COMPRESSION_NONE,
2834 NO_DECIMATION, 0, 0);
2835 /* Trial & error - Discarding a frame prevents the
2836 first frame from having an error in the data. */
2837 do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0);
2838 }
2839
2840 /* init camera upload */
2841 if (do_command(cam, CPIA_COMMAND_GrabFrame, 0,
2842 cam->params.streamStartLine, 0, 0))
2843 continue;
2844
2845 if (cam->ops->wait_for_stream_ready) {
2846 /* loop until image ready */
2847 int count = 0;
2848 do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0);
2849 while (cam->params.status.streamState != STREAM_READY) {
2850 if(++count > READY_TIMEOUT)
2851 break;
2852 if(cam->params.status.streamState ==
2853 STREAM_PAUSED) {
2854 /* Bad news */
2855 if(!clear_stall(cam))
2856 return -EIO;
2857 }
2858
2859 cond_resched();
2860
2861 /* sleep for 10 ms, hopefully ;) */
2862 msleep_interruptible(10);
2863 if (signal_pending(current))
2864 return -EINTR;
2865
2866 do_command(cam, CPIA_COMMAND_GetCameraStatus,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002867 0, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002868 }
2869 if(cam->params.status.streamState != STREAM_READY) {
2870 continue;
2871 }
2872 }
2873
2874 cond_resched();
2875
2876 /* grab image from camera */
2877 oldjif = jiffies;
2878 image_size = cam->ops->streamRead(cam->lowlevel_data,
2879 cam->raw_image, 0);
2880 if (image_size <= 0) {
2881 DBG("streamRead failed: %d\n", image_size);
2882 continue;
2883 }
2884
2885 rate = image_size * HZ / 1024;
2886 diff = jiffies-oldjif;
2887 cam->transfer_rate = diff==0 ? rate : rate/diff;
2888 /* diff==0 ? unlikely but possible */
2889
2890 /* Switch flicker control back on if it got turned off */
2891 restart_flicker(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002892
Linus Torvalds1da177e2005-04-16 15:20:36 -07002893 /* If AEC is enabled, monitor the exposure and
2894 adjust the sensor frame rate if needed */
2895 if(cam->params.exposure.expMode == 2)
2896 monitor_exposure(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002897
Linus Torvalds1da177e2005-04-16 15:20:36 -07002898 /* camera idle now so dispatch queued commands */
2899 dispatch_commands(cam);
2900
2901 /* Update our knowledge of the camera state */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002902 do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
2903 do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002904 do_command(cam, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0);
2905
2906 /* decompress and convert image to by copying it from
2907 * raw_image to decompressed_frame
2908 */
2909
2910 cond_resched();
2911
2912 cam->image_size = parse_picture(cam, image_size);
2913 if (cam->image_size <= 0) {
2914 DBG("parse_picture failed %d\n", cam->image_size);
2915 if(cam->params.compression.mode !=
2916 CPIA_COMPRESSION_NONE) {
2917 /* Compression may not work right if we
2918 had a bad frame, get the next one
2919 uncompressed. */
2920 cam->first_frame = 1;
2921 do_command(cam, CPIA_COMMAND_SetGrabMode,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002922 CPIA_GRAB_SINGLE, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002923 /* FIXME: Trial & error - need up to 70ms for
2924 the grab mode change to complete ? */
2925 msleep_interruptible(70);
2926 if (signal_pending(current))
2927 return -EINTR;
2928 }
2929 } else
2930 break;
2931 }
2932
2933 if (retry < 3) {
2934 /* FIXME: this only works for double buffering */
2935 if (cam->frame[cam->curframe].state == FRAME_READY) {
2936 memcpy(cam->frame[cam->curframe].data,
2937 cam->decompressed_frame.data,
2938 cam->decompressed_frame.count);
2939 cam->frame[cam->curframe].state = FRAME_DONE;
2940 } else
2941 cam->decompressed_frame.state = FRAME_DONE;
2942
2943 if (cam->first_frame) {
2944 cam->first_frame = 0;
2945 do_command(cam, CPIA_COMMAND_SetCompression,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002946 cam->params.compression.mode,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002947 cam->params.compression.decimation, 0, 0);
2948
2949 /* Switch from single-grab to continuous grab */
2950 do_command(cam, CPIA_COMMAND_SetGrabMode,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002951 CPIA_GRAB_CONTINUOUS, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002952 }
2953 return 0;
2954 }
2955 return -EIO;
2956}
2957
2958static int capture_frame(struct cam_data *cam, struct video_mmap *vm)
2959{
2960 if (!cam->frame_buf) {
2961 /* we do lazy allocation */
2962 int err;
2963 if ((err = allocate_frame_buf(cam)))
2964 return err;
2965 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002966
Linus Torvalds1da177e2005-04-16 15:20:36 -07002967 cam->curframe = vm->frame;
2968 cam->frame[cam->curframe].state = FRAME_READY;
2969 return fetch_frame(cam);
2970}
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002971
Linus Torvalds1da177e2005-04-16 15:20:36 -07002972static int goto_high_power(struct cam_data *cam)
2973{
2974 if (do_command(cam, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0))
2975 return -EIO;
2976 msleep_interruptible(40); /* windows driver does it too */
2977 if(signal_pending(current))
2978 return -EINTR;
2979 if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
2980 return -EIO;
2981 if (cam->params.status.systemState == HI_POWER_STATE) {
2982 DBG("camera now in HIGH power state\n");
2983 return 0;
2984 }
2985 printstatus(cam);
2986 return -EIO;
2987}
2988
2989static int goto_low_power(struct cam_data *cam)
2990{
2991 if (do_command(cam, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0))
2992 return -1;
2993 if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
2994 return -1;
2995 if (cam->params.status.systemState == LO_POWER_STATE) {
2996 DBG("camera now in LOW power state\n");
2997 return 0;
2998 }
2999 printstatus(cam);
3000 return -1;
3001}
3002
3003static void save_camera_state(struct cam_data *cam)
3004{
3005 if(!(cam->cmd_queue & COMMAND_SETCOLOURBALANCE))
3006 do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
3007 if(!(cam->cmd_queue & COMMAND_SETEXPOSURE))
3008 do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
3009
3010 DBG("%d/%d/%d/%d/%d/%d/%d/%d\n",
3011 cam->params.exposure.gain,
3012 cam->params.exposure.fineExp,
3013 cam->params.exposure.coarseExpLo,
3014 cam->params.exposure.coarseExpHi,
3015 cam->params.exposure.redComp,
3016 cam->params.exposure.green1Comp,
3017 cam->params.exposure.green2Comp,
3018 cam->params.exposure.blueComp);
3019 DBG("%d/%d/%d\n",
3020 cam->params.colourBalance.redGain,
3021 cam->params.colourBalance.greenGain,
3022 cam->params.colourBalance.blueGain);
3023}
3024
3025static int set_camera_state(struct cam_data *cam)
3026{
3027 cam->cmd_queue = COMMAND_SETCOMPRESSION |
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003028 COMMAND_SETCOMPRESSIONTARGET |
3029 COMMAND_SETCOLOURPARAMS |
3030 COMMAND_SETFORMAT |
3031 COMMAND_SETYUVTHRESH |
3032 COMMAND_SETECPTIMING |
3033 COMMAND_SETCOMPRESSIONPARAMS |
3034 COMMAND_SETEXPOSURE |
3035 COMMAND_SETCOLOURBALANCE |
3036 COMMAND_SETSENSORFPS |
3037 COMMAND_SETAPCOR |
3038 COMMAND_SETFLICKERCTRL |
3039 COMMAND_SETVLOFFSET;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003040
3041 do_command(cam, CPIA_COMMAND_SetGrabMode, CPIA_GRAB_SINGLE,0,0,0);
3042 dispatch_commands(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003043
Linus Torvalds1da177e2005-04-16 15:20:36 -07003044 /* Wait 6 frames for the sensor to get all settings and
3045 AEC/ACB to settle */
3046 msleep_interruptible(6*(cam->params.sensorFps.baserate ? 33 : 40) *
3047 (1 << cam->params.sensorFps.divisor) + 10);
3048
3049 if(signal_pending(current))
3050 return -EINTR;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003051
Linus Torvalds1da177e2005-04-16 15:20:36 -07003052 save_camera_state(cam);
3053
3054 return 0;
3055}
3056
3057static void get_version_information(struct cam_data *cam)
3058{
3059 /* GetCPIAVersion */
3060 do_command(cam, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0);
3061
3062 /* GetPnPID */
3063 do_command(cam, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0);
3064}
3065
3066/* initialize camera */
3067static int reset_camera(struct cam_data *cam)
3068{
3069 int err;
3070 /* Start the camera in low power mode */
3071 if (goto_low_power(cam)) {
3072 if (cam->params.status.systemState != WARM_BOOT_STATE)
3073 return -ENODEV;
3074
3075 /* FIXME: this is just dirty trial and error */
3076 err = goto_high_power(cam);
3077 if(err)
3078 return err;
3079 do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0);
3080 if (goto_low_power(cam))
3081 return -ENODEV;
3082 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003083
Linus Torvalds1da177e2005-04-16 15:20:36 -07003084 /* procedure described in developer's guide p3-28 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003085
Linus Torvalds1da177e2005-04-16 15:20:36 -07003086 /* Check the firmware version. */
3087 cam->params.version.firmwareVersion = 0;
3088 get_version_information(cam);
3089 if (cam->params.version.firmwareVersion != 1)
3090 return -ENODEV;
3091
3092 /* A bug in firmware 1-02 limits gainMode to 2 */
3093 if(cam->params.version.firmwareRevision <= 2 &&
3094 cam->params.exposure.gainMode > 2) {
3095 cam->params.exposure.gainMode = 2;
3096 }
3097
3098 /* set QX3 detected flag */
3099 cam->params.qx3.qx3_detected = (cam->params.pnpID.vendor == 0x0813 &&
3100 cam->params.pnpID.product == 0x0001);
3101
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003102 /* The fatal error checking should be done after
Linus Torvalds1da177e2005-04-16 15:20:36 -07003103 * the camera powers up (developer's guide p 3-38) */
3104
3105 /* Set streamState before transition to high power to avoid bug
3106 * in firmware 1-02 */
3107 do_command(cam, CPIA_COMMAND_ModifyCameraStatus, STREAMSTATE, 0,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003108 STREAM_NOT_READY, 0);
3109
Linus Torvalds1da177e2005-04-16 15:20:36 -07003110 /* GotoHiPower */
3111 err = goto_high_power(cam);
3112 if (err)
3113 return err;
3114
3115 /* Check the camera status */
3116 if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
3117 return -EIO;
3118
3119 if (cam->params.status.fatalError) {
3120 DBG("fatal_error: %#04x\n",
3121 cam->params.status.fatalError);
3122 DBG("vp_status: %#04x\n",
3123 cam->params.status.vpStatus);
3124 if (cam->params.status.fatalError & ~(COM_FLAG|CPIA_FLAG)) {
3125 /* Fatal error in camera */
3126 return -EIO;
3127 } else if (cam->params.status.fatalError & (COM_FLAG|CPIA_FLAG)) {
3128 /* Firmware 1-02 may do this for parallel port cameras,
3129 * just clear the flags (developer's guide p 3-38) */
3130 do_command(cam, CPIA_COMMAND_ModifyCameraStatus,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003131 FATALERROR, ~(COM_FLAG|CPIA_FLAG), 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003132 }
3133 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003134
Linus Torvalds1da177e2005-04-16 15:20:36 -07003135 /* Check the camera status again */
3136 if (cam->params.status.fatalError) {
3137 if (cam->params.status.fatalError)
3138 return -EIO;
3139 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003140
Linus Torvalds1da177e2005-04-16 15:20:36 -07003141 /* VPVersion can't be retrieved before the camera is in HiPower,
3142 * so get it here instead of in get_version_information. */
3143 do_command(cam, CPIA_COMMAND_GetVPVersion, 0, 0, 0, 0);
3144
3145 /* set camera to a known state */
3146 return set_camera_state(cam);
3147}
3148
3149static void put_cam(struct cpia_camera_ops* ops)
3150{
Mariusz Kozlowskiac328982007-01-07 10:36:24 -03003151 module_put(ops->owner);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003152}
3153
3154/* ------------------------- V4L interface --------------------- */
3155static int cpia_open(struct inode *inode, struct file *file)
3156{
3157 struct video_device *dev = video_devdata(file);
Hans Verkuil601e9442008-08-23 07:24:07 -03003158 struct cam_data *cam = video_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003159 int err;
3160
3161 if (!cam) {
3162 DBG("Internal error, cam_data not found!\n");
3163 return -ENODEV;
3164 }
3165
3166 if (cam->open_count > 0) {
3167 DBG("Camera already open\n");
3168 return -EBUSY;
3169 }
3170
3171 if (!try_module_get(cam->ops->owner))
3172 return -ENODEV;
3173
Ingo Molnar3593cab2006-02-07 06:49:14 -02003174 mutex_lock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003175 err = -ENOMEM;
3176 if (!cam->raw_image) {
3177 cam->raw_image = rvmalloc(CPIA_MAX_IMAGE_SIZE);
3178 if (!cam->raw_image)
3179 goto oops;
3180 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003181
Linus Torvalds1da177e2005-04-16 15:20:36 -07003182 if (!cam->decompressed_frame.data) {
3183 cam->decompressed_frame.data = rvmalloc(CPIA_MAX_FRAME_SIZE);
3184 if (!cam->decompressed_frame.data)
3185 goto oops;
3186 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003187
Linus Torvalds1da177e2005-04-16 15:20:36 -07003188 /* open cpia */
3189 err = -ENODEV;
3190 if (cam->ops->open(cam->lowlevel_data))
3191 goto oops;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003192
Linus Torvalds1da177e2005-04-16 15:20:36 -07003193 /* reset the camera */
3194 if ((err = reset_camera(cam)) != 0) {
3195 cam->ops->close(cam->lowlevel_data);
3196 goto oops;
3197 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003198
Linus Torvalds1da177e2005-04-16 15:20:36 -07003199 err = -EINTR;
3200 if(signal_pending(current))
3201 goto oops;
3202
3203 /* Set ownership of /proc/cpia/videoX to current user */
3204 if(cam->proc_entry)
3205 cam->proc_entry->uid = current->uid;
3206
3207 /* set mark for loading first frame uncompressed */
3208 cam->first_frame = 1;
3209
3210 /* init it to something */
3211 cam->mmap_kludge = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003212
Linus Torvalds1da177e2005-04-16 15:20:36 -07003213 ++cam->open_count;
3214 file->private_data = dev;
Ingo Molnar3593cab2006-02-07 06:49:14 -02003215 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003216 return 0;
3217
3218 oops:
3219 if (cam->decompressed_frame.data) {
3220 rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
3221 cam->decompressed_frame.data = NULL;
3222 }
3223 if (cam->raw_image) {
3224 rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
3225 cam->raw_image = NULL;
3226 }
Ingo Molnar3593cab2006-02-07 06:49:14 -02003227 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003228 put_cam(cam->ops);
3229 return err;
3230}
3231
3232static int cpia_close(struct inode *inode, struct file *file)
3233{
3234 struct video_device *dev = file->private_data;
Hans Verkuil601e9442008-08-23 07:24:07 -03003235 struct cam_data *cam = video_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003236
3237 if (cam->ops) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003238 /* Return ownership of /proc/cpia/videoX to root */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003239 if(cam->proc_entry)
3240 cam->proc_entry->uid = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003241
Linus Torvalds1da177e2005-04-16 15:20:36 -07003242 /* save camera state for later open (developers guide ch 3.5.3) */
3243 save_camera_state(cam);
3244
3245 /* GotoLoPower */
3246 goto_low_power(cam);
3247
3248 /* Update the camera status */
3249 do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
3250
3251 /* cleanup internal state stuff */
3252 free_frames(cam->frame);
3253
3254 /* close cpia */
3255 cam->ops->close(cam->lowlevel_data);
3256
3257 put_cam(cam->ops);
3258 }
3259
3260 if (--cam->open_count == 0) {
3261 /* clean up capture-buffers */
3262 if (cam->raw_image) {
3263 rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
3264 cam->raw_image = NULL;
3265 }
3266
3267 if (cam->decompressed_frame.data) {
3268 rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
3269 cam->decompressed_frame.data = NULL;
3270 }
3271
3272 if (cam->frame_buf)
3273 free_frame_buf(cam);
3274
3275 if (!cam->ops)
3276 kfree(cam);
3277 }
3278 file->private_data = NULL;
3279
3280 return 0;
3281}
3282
3283static ssize_t cpia_read(struct file *file, char __user *buf,
3284 size_t count, loff_t *ppos)
3285{
3286 struct video_device *dev = file->private_data;
Hans Verkuil601e9442008-08-23 07:24:07 -03003287 struct cam_data *cam = video_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003288 int err;
3289
3290 /* make this _really_ smp and multithread-safe */
Ingo Molnar3593cab2006-02-07 06:49:14 -02003291 if (mutex_lock_interruptible(&cam->busy_lock))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003292 return -EINTR;
3293
3294 if (!buf) {
3295 DBG("buf NULL\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02003296 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003297 return -EINVAL;
3298 }
3299
3300 if (!count) {
3301 DBG("count 0\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02003302 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003303 return 0;
3304 }
3305
3306 if (!cam->ops) {
3307 DBG("ops NULL\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02003308 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003309 return -ENODEV;
3310 }
3311
3312 /* upload frame */
3313 cam->decompressed_frame.state = FRAME_READY;
3314 cam->mmap_kludge=0;
3315 if((err = fetch_frame(cam)) != 0) {
3316 DBG("ERROR from fetch_frame: %d\n", err);
Ingo Molnar3593cab2006-02-07 06:49:14 -02003317 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003318 return err;
3319 }
3320 cam->decompressed_frame.state = FRAME_UNUSED;
3321
3322 /* copy data to user space */
3323 if (cam->decompressed_frame.count > count) {
3324 DBG("count wrong: %d, %lu\n", cam->decompressed_frame.count,
3325 (unsigned long) count);
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 if (copy_to_user(buf, cam->decompressed_frame.data,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003330 cam->decompressed_frame.count)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003331 DBG("copy_to_user failed\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02003332 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003333 return -EFAULT;
3334 }
3335
Ingo Molnar3593cab2006-02-07 06:49:14 -02003336 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003337 return cam->decompressed_frame.count;
3338}
3339
3340static int cpia_do_ioctl(struct inode *inode, struct file *file,
3341 unsigned int ioctlnr, void *arg)
3342{
3343 struct video_device *dev = file->private_data;
Hans Verkuil601e9442008-08-23 07:24:07 -03003344 struct cam_data *cam = video_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003345 int retval = 0;
3346
3347 if (!cam || !cam->ops)
3348 return -ENODEV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003349
Linus Torvalds1da177e2005-04-16 15:20:36 -07003350 /* make this _really_ smp-safe */
Ingo Molnar3593cab2006-02-07 06:49:14 -02003351 if (mutex_lock_interruptible(&cam->busy_lock))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003352 return -EINTR;
3353
3354 //DBG("cpia_ioctl: %u\n", ioctlnr);
3355
3356 switch (ioctlnr) {
Alexey Dobriyanbe787ac2006-03-07 22:20:23 -03003357 /* query capabilities */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003358 case VIDIOCGCAP:
3359 {
3360 struct video_capability *b = arg;
3361
3362 DBG("VIDIOCGCAP\n");
3363 strcpy(b->name, "CPiA Camera");
3364 b->type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE;
3365 b->channels = 1;
3366 b->audios = 0;
3367 b->maxwidth = 352; /* VIDEOSIZE_CIF */
3368 b->maxheight = 288;
3369 b->minwidth = 48; /* VIDEOSIZE_48_48 */
3370 b->minheight = 48;
3371 break;
3372 }
3373
3374 /* get/set video source - we are a camera and nothing else */
3375 case VIDIOCGCHAN:
3376 {
3377 struct video_channel *v = arg;
3378
3379 DBG("VIDIOCGCHAN\n");
3380 if (v->channel != 0) {
3381 retval = -EINVAL;
3382 break;
3383 }
3384
3385 v->channel = 0;
3386 strcpy(v->name, "Camera");
3387 v->tuners = 0;
3388 v->flags = 0;
3389 v->type = VIDEO_TYPE_CAMERA;
3390 v->norm = 0;
3391 break;
3392 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003393
Linus Torvalds1da177e2005-04-16 15:20:36 -07003394 case VIDIOCSCHAN:
3395 {
3396 struct video_channel *v = arg;
3397
3398 DBG("VIDIOCSCHAN\n");
3399 if (v->channel != 0)
3400 retval = -EINVAL;
3401 break;
3402 }
3403
3404 /* image properties */
3405 case VIDIOCGPICT:
3406 {
3407 struct video_picture *pic = arg;
3408 DBG("VIDIOCGPICT\n");
3409 *pic = cam->vp;
3410 break;
3411 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003412
Linus Torvalds1da177e2005-04-16 15:20:36 -07003413 case VIDIOCSPICT:
3414 {
3415 struct video_picture *vp = arg;
3416
3417 DBG("VIDIOCSPICT\n");
3418
3419 /* check validity */
3420 DBG("palette: %d\n", vp->palette);
3421 DBG("depth: %d\n", vp->depth);
3422 if (!valid_mode(vp->palette, vp->depth)) {
3423 retval = -EINVAL;
3424 break;
3425 }
3426
Ingo Molnar3593cab2006-02-07 06:49:14 -02003427 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003428 /* brightness, colour, contrast need no check 0-65535 */
3429 cam->vp = *vp;
3430 /* update cam->params.colourParams */
3431 cam->params.colourParams.brightness = vp->brightness*100/65535;
3432 cam->params.colourParams.contrast = vp->contrast*100/65535;
3433 cam->params.colourParams.saturation = vp->colour*100/65535;
3434 /* contrast is in steps of 8, so round */
3435 cam->params.colourParams.contrast =
3436 ((cam->params.colourParams.contrast + 3) / 8) * 8;
3437 if (cam->params.version.firmwareVersion == 1 &&
3438 cam->params.version.firmwareRevision == 2 &&
3439 cam->params.colourParams.contrast > 80) {
3440 /* 1-02 firmware limits contrast to 80 */
3441 cam->params.colourParams.contrast = 80;
3442 }
3443
3444 /* Adjust flicker control if necessary */
3445 if(cam->params.flickerControl.allowableOverExposure < 0)
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003446 cam->params.flickerControl.allowableOverExposure =
Linus Torvalds1da177e2005-04-16 15:20:36 -07003447 -find_over_exposure(cam->params.colourParams.brightness);
3448 if(cam->params.flickerControl.flickerMode != 0)
3449 cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003450
Linus Torvalds1da177e2005-04-16 15:20:36 -07003451
3452 /* queue command to update camera */
3453 cam->cmd_queue |= COMMAND_SETCOLOURPARAMS;
Ingo Molnar3593cab2006-02-07 06:49:14 -02003454 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003455 DBG("VIDIOCSPICT: %d / %d // %d / %d / %d / %d\n",
3456 vp->depth, vp->palette, vp->brightness, vp->hue, vp->colour,
3457 vp->contrast);
3458 break;
3459 }
3460
3461 /* get/set capture window */
3462 case VIDIOCGWIN:
3463 {
3464 struct video_window *vw = arg;
3465 DBG("VIDIOCGWIN\n");
3466
3467 *vw = cam->vw;
3468 break;
3469 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003470
Linus Torvalds1da177e2005-04-16 15:20:36 -07003471 case VIDIOCSWIN:
3472 {
3473 /* copy_from_user, check validity, copy to internal structure */
3474 struct video_window *vw = arg;
3475 DBG("VIDIOCSWIN\n");
3476
3477 if (vw->clipcount != 0) { /* clipping not supported */
3478 retval = -EINVAL;
3479 break;
3480 }
3481 if (vw->clips != NULL) { /* clipping not supported */
3482 retval = -EINVAL;
3483 break;
3484 }
3485
3486 /* we set the video window to something smaller or equal to what
3487 * is requested by the user???
3488 */
Ingo Molnar3593cab2006-02-07 06:49:14 -02003489 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003490 if (vw->width != cam->vw.width || vw->height != cam->vw.height) {
3491 int video_size = match_videosize(vw->width, vw->height);
3492
3493 if (video_size < 0) {
3494 retval = -EINVAL;
Ingo Molnar3593cab2006-02-07 06:49:14 -02003495 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003496 break;
3497 }
3498 cam->video_size = video_size;
3499
3500 /* video size is changing, reset the subcapture area */
3501 memset(&cam->vc, 0, sizeof(cam->vc));
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003502
Linus Torvalds1da177e2005-04-16 15:20:36 -07003503 set_vw_size(cam);
3504 DBG("%d / %d\n", cam->vw.width, cam->vw.height);
3505 cam->cmd_queue |= COMMAND_SETFORMAT;
3506 }
3507
Ingo Molnar3593cab2006-02-07 06:49:14 -02003508 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003509
3510 /* setformat ignored by camera during streaming,
3511 * so stop/dispatch/start */
3512 if (cam->cmd_queue & COMMAND_SETFORMAT) {
3513 DBG("\n");
3514 dispatch_commands(cam);
3515 }
3516 DBG("%d/%d:%d\n", cam->video_size,
3517 cam->vw.width, cam->vw.height);
3518 break;
3519 }
3520
3521 /* mmap interface */
3522 case VIDIOCGMBUF:
3523 {
3524 struct video_mbuf *vm = arg;
3525 int i;
3526
3527 DBG("VIDIOCGMBUF\n");
3528 memset(vm, 0, sizeof(*vm));
3529 vm->size = CPIA_MAX_FRAME_SIZE*FRAME_NUM;
3530 vm->frames = FRAME_NUM;
3531 for (i = 0; i < FRAME_NUM; i++)
3532 vm->offsets[i] = CPIA_MAX_FRAME_SIZE * i;
3533 break;
3534 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003535
Linus Torvalds1da177e2005-04-16 15:20:36 -07003536 case VIDIOCMCAPTURE:
3537 {
3538 struct video_mmap *vm = arg;
3539 int video_size;
3540
3541 DBG("VIDIOCMCAPTURE: %d / %d / %dx%d\n", vm->format, vm->frame,
3542 vm->width, vm->height);
3543 if (vm->frame<0||vm->frame>=FRAME_NUM) {
3544 retval = -EINVAL;
3545 break;
3546 }
3547
3548 /* set video format */
3549 cam->vp.palette = vm->format;
3550 switch(vm->format) {
3551 case VIDEO_PALETTE_GREY:
3552 cam->vp.depth=8;
3553 break;
3554 case VIDEO_PALETTE_RGB555:
3555 case VIDEO_PALETTE_RGB565:
3556 case VIDEO_PALETTE_YUV422:
3557 case VIDEO_PALETTE_YUYV:
3558 case VIDEO_PALETTE_UYVY:
3559 cam->vp.depth = 16;
3560 break;
3561 case VIDEO_PALETTE_RGB24:
3562 cam->vp.depth = 24;
3563 break;
3564 case VIDEO_PALETTE_RGB32:
3565 cam->vp.depth = 32;
3566 break;
3567 default:
3568 retval = -EINVAL;
3569 break;
3570 }
3571 if (retval)
3572 break;
3573
3574 /* set video size */
3575 video_size = match_videosize(vm->width, vm->height);
3576 if (video_size < 0) {
3577 retval = -EINVAL;
3578 break;
3579 }
3580 if (video_size != cam->video_size) {
3581 cam->video_size = video_size;
3582
3583 /* video size is changing, reset the subcapture area */
3584 memset(&cam->vc, 0, sizeof(cam->vc));
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003585
Linus Torvalds1da177e2005-04-16 15:20:36 -07003586 set_vw_size(cam);
3587 cam->cmd_queue |= COMMAND_SETFORMAT;
3588 dispatch_commands(cam);
3589 }
3590 /* according to v4l-spec we must start streaming here */
3591 cam->mmap_kludge = 1;
3592 retval = capture_frame(cam, vm);
3593
3594 break;
3595 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003596
Linus Torvalds1da177e2005-04-16 15:20:36 -07003597 case VIDIOCSYNC:
3598 {
3599 int *frame = arg;
3600
3601 //DBG("VIDIOCSYNC: %d\n", *frame);
3602
3603 if (*frame<0 || *frame >= FRAME_NUM) {
3604 retval = -EINVAL;
3605 break;
3606 }
3607
3608 switch (cam->frame[*frame].state) {
3609 case FRAME_UNUSED:
3610 case FRAME_READY:
3611 case FRAME_GRABBING:
3612 DBG("sync to unused frame %d\n", *frame);
3613 retval = -EINVAL;
3614 break;
3615
3616 case FRAME_DONE:
3617 cam->frame[*frame].state = FRAME_UNUSED;
3618 //DBG("VIDIOCSYNC: %d synced\n", *frame);
3619 break;
3620 }
3621 if (retval == -EINTR) {
3622 /* FIXME - xawtv does not handle this nice */
3623 retval = 0;
3624 }
3625 break;
3626 }
3627
3628 case VIDIOCGCAPTURE:
3629 {
3630 struct video_capture *vc = arg;
3631
3632 DBG("VIDIOCGCAPTURE\n");
3633
3634 *vc = cam->vc;
3635
3636 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003637 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003638
3639 case VIDIOCSCAPTURE:
3640 {
3641 struct video_capture *vc = arg;
3642
3643 DBG("VIDIOCSCAPTURE\n");
3644
3645 if (vc->decimation != 0) { /* How should this be used? */
3646 retval = -EINVAL;
3647 break;
3648 }
3649 if (vc->flags != 0) { /* Even/odd grab not supported */
3650 retval = -EINVAL;
3651 break;
3652 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003653
Linus Torvalds1da177e2005-04-16 15:20:36 -07003654 /* Clip to the resolution we can set for the ROI
3655 (every 8 columns and 4 rows) */
3656 vc->x = vc->x & ~(__u32)7;
3657 vc->y = vc->y & ~(__u32)3;
3658 vc->width = vc->width & ~(__u32)7;
3659 vc->height = vc->height & ~(__u32)3;
3660
3661 if(vc->width == 0 || vc->height == 0 ||
3662 vc->x + vc->width > cam->vw.width ||
3663 vc->y + vc->height > cam->vw.height) {
3664 retval = -EINVAL;
3665 break;
3666 }
3667
3668 DBG("%d,%d/%dx%d\n", vc->x,vc->y,vc->width, vc->height);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003669
Ingo Molnar3593cab2006-02-07 06:49:14 -02003670 mutex_lock(&cam->param_lock);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003671
Linus Torvalds1da177e2005-04-16 15:20:36 -07003672 cam->vc.x = vc->x;
3673 cam->vc.y = vc->y;
3674 cam->vc.width = vc->width;
3675 cam->vc.height = vc->height;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003676
Linus Torvalds1da177e2005-04-16 15:20:36 -07003677 set_vw_size(cam);
3678 cam->cmd_queue |= COMMAND_SETFORMAT;
3679
Ingo Molnar3593cab2006-02-07 06:49:14 -02003680 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003681
3682 /* setformat ignored by camera during streaming,
3683 * so stop/dispatch/start */
3684 dispatch_commands(cam);
3685 break;
3686 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003687
Linus Torvalds1da177e2005-04-16 15:20:36 -07003688 case VIDIOCGUNIT:
3689 {
3690 struct video_unit *vu = arg;
3691
3692 DBG("VIDIOCGUNIT\n");
3693
3694 vu->video = cam->vdev.minor;
3695 vu->vbi = VIDEO_NO_UNIT;
3696 vu->radio = VIDEO_NO_UNIT;
3697 vu->audio = VIDEO_NO_UNIT;
3698 vu->teletext = VIDEO_NO_UNIT;
3699
3700 break;
3701 }
3702
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003703
Linus Torvalds1da177e2005-04-16 15:20:36 -07003704 /* pointless to implement overlay with this camera */
3705 case VIDIOCCAPTURE:
3706 case VIDIOCGFBUF:
3707 case VIDIOCSFBUF:
3708 case VIDIOCKEY:
3709 /* tuner interface - we have none */
3710 case VIDIOCGTUNER:
3711 case VIDIOCSTUNER:
3712 case VIDIOCGFREQ:
3713 case VIDIOCSFREQ:
3714 /* audio interface - we have none */
3715 case VIDIOCGAUDIO:
3716 case VIDIOCSAUDIO:
3717 retval = -EINVAL;
3718 break;
3719 default:
3720 retval = -ENOIOCTLCMD;
3721 break;
3722 }
3723
Ingo Molnar3593cab2006-02-07 06:49:14 -02003724 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003725 return retval;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003726}
Linus Torvalds1da177e2005-04-16 15:20:36 -07003727
3728static int cpia_ioctl(struct inode *inode, struct file *file,
3729 unsigned int cmd, unsigned long arg)
3730{
3731 return video_usercopy(inode, file, cmd, arg, cpia_do_ioctl);
3732}
3733
3734
3735/* FIXME */
3736static int cpia_mmap(struct file *file, struct vm_area_struct *vma)
3737{
3738 struct video_device *dev = file->private_data;
3739 unsigned long start = vma->vm_start;
3740 unsigned long size = vma->vm_end - vma->vm_start;
3741 unsigned long page, pos;
Hans Verkuil601e9442008-08-23 07:24:07 -03003742 struct cam_data *cam = video_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003743 int retval;
3744
3745 if (!cam || !cam->ops)
3746 return -ENODEV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003747
Linus Torvalds1da177e2005-04-16 15:20:36 -07003748 DBG("cpia_mmap: %ld\n", size);
3749
3750 if (size > FRAME_NUM*CPIA_MAX_FRAME_SIZE)
3751 return -EINVAL;
3752
3753 if (!cam || !cam->ops)
3754 return -ENODEV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003755
Linus Torvalds1da177e2005-04-16 15:20:36 -07003756 /* make this _really_ smp-safe */
Ingo Molnar3593cab2006-02-07 06:49:14 -02003757 if (mutex_lock_interruptible(&cam->busy_lock))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003758 return -EINTR;
3759
3760 if (!cam->frame_buf) { /* we do lazy allocation */
3761 if ((retval = allocate_frame_buf(cam))) {
Ingo Molnar3593cab2006-02-07 06:49:14 -02003762 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003763 return retval;
3764 }
3765 }
3766
3767 pos = (unsigned long)(cam->frame_buf);
3768 while (size > 0) {
3769 page = vmalloc_to_pfn((void *)pos);
3770 if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) {
Ingo Molnar3593cab2006-02-07 06:49:14 -02003771 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003772 return -EAGAIN;
3773 }
3774 start += PAGE_SIZE;
3775 pos += PAGE_SIZE;
3776 if (size > PAGE_SIZE)
3777 size -= PAGE_SIZE;
3778 else
3779 size = 0;
3780 }
3781
3782 DBG("cpia_mmap: %ld\n", size);
Ingo Molnar3593cab2006-02-07 06:49:14 -02003783 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003784
3785 return 0;
3786}
3787
Arjan van de Venfa027c22007-02-12 00:55:33 -08003788static const struct file_operations cpia_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003789 .owner = THIS_MODULE,
3790 .open = cpia_open,
3791 .release = cpia_close,
3792 .read = cpia_read,
3793 .mmap = cpia_mmap,
3794 .ioctl = cpia_ioctl,
Douglas Schilling Landgraf078ff792008-04-22 14:46:11 -03003795#ifdef CONFIG_COMPAT
Arnd Bergmann0d0fbf82006-01-09 15:24:57 -02003796 .compat_ioctl = v4l_compat_ioctl32,
Douglas Schilling Landgraf078ff792008-04-22 14:46:11 -03003797#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07003798 .llseek = no_llseek,
3799};
3800
3801static struct video_device cpia_template = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003802 .name = "CPiA Camera",
Linus Torvalds1da177e2005-04-16 15:20:36 -07003803 .fops = &cpia_fops,
Hans Verkuilaa5e90a2008-08-23 06:23:55 -03003804 .release = video_device_release_empty,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003805};
3806
3807/* initialise cam_data structure */
3808static void reset_camera_struct(struct cam_data *cam)
3809{
3810 /* The following parameter values are the defaults from
3811 * "Software Developer's Guide for CPiA Cameras". Any changes
3812 * to the defaults are noted in comments. */
3813 cam->params.colourParams.brightness = 50;
3814 cam->params.colourParams.contrast = 48;
3815 cam->params.colourParams.saturation = 50;
3816 cam->params.exposure.gainMode = 4;
3817 cam->params.exposure.expMode = 2; /* AEC */
3818 cam->params.exposure.compMode = 1;
3819 cam->params.exposure.centreWeight = 1;
3820 cam->params.exposure.gain = 0;
3821 cam->params.exposure.fineExp = 0;
3822 cam->params.exposure.coarseExpLo = 185;
3823 cam->params.exposure.coarseExpHi = 0;
3824 cam->params.exposure.redComp = COMP_RED;
3825 cam->params.exposure.green1Comp = COMP_GREEN1;
3826 cam->params.exposure.green2Comp = COMP_GREEN2;
3827 cam->params.exposure.blueComp = COMP_BLUE;
3828 cam->params.colourBalance.balanceMode = 2; /* ACB */
3829 cam->params.colourBalance.redGain = 32;
3830 cam->params.colourBalance.greenGain = 6;
3831 cam->params.colourBalance.blueGain = 92;
3832 cam->params.apcor.gain1 = 0x18;
3833 cam->params.apcor.gain2 = 0x16;
3834 cam->params.apcor.gain4 = 0x24;
3835 cam->params.apcor.gain8 = 0x34;
3836 cam->params.flickerControl.flickerMode = 0;
3837 cam->params.flickerControl.disabled = 1;
3838
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003839 cam->params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07003840 flicker_jumps[cam->mainsFreq]
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003841 [cam->params.sensorFps.baserate]
3842 [cam->params.sensorFps.divisor];
3843 cam->params.flickerControl.allowableOverExposure =
Linus Torvalds1da177e2005-04-16 15:20:36 -07003844 -find_over_exposure(cam->params.colourParams.brightness);
3845 cam->params.vlOffset.gain1 = 20;
3846 cam->params.vlOffset.gain2 = 24;
3847 cam->params.vlOffset.gain4 = 26;
3848 cam->params.vlOffset.gain8 = 26;
3849 cam->params.compressionParams.hysteresis = 3;
3850 cam->params.compressionParams.threshMax = 11;
3851 cam->params.compressionParams.smallStep = 1;
3852 cam->params.compressionParams.largeStep = 3;
3853 cam->params.compressionParams.decimationHysteresis = 2;
3854 cam->params.compressionParams.frDiffStepThresh = 5;
3855 cam->params.compressionParams.qDiffStepThresh = 3;
3856 cam->params.compressionParams.decimationThreshMod = 2;
3857 /* End of default values from Software Developer's Guide */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003858
Linus Torvalds1da177e2005-04-16 15:20:36 -07003859 cam->transfer_rate = 0;
3860 cam->exposure_status = EXPOSURE_NORMAL;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003861
Linus Torvalds1da177e2005-04-16 15:20:36 -07003862 /* Set Sensor FPS to 15fps. This seems better than 30fps
3863 * for indoor lighting. */
3864 cam->params.sensorFps.divisor = 1;
3865 cam->params.sensorFps.baserate = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003866
Linus Torvalds1da177e2005-04-16 15:20:36 -07003867 cam->params.yuvThreshold.yThreshold = 6; /* From windows driver */
3868 cam->params.yuvThreshold.uvThreshold = 6; /* From windows driver */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003869
Linus Torvalds1da177e2005-04-16 15:20:36 -07003870 cam->params.format.subSample = SUBSAMPLE_422;
3871 cam->params.format.yuvOrder = YUVORDER_YUYV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003872
Linus Torvalds1da177e2005-04-16 15:20:36 -07003873 cam->params.compression.mode = CPIA_COMPRESSION_AUTO;
3874 cam->params.compressionTarget.frTargeting =
3875 CPIA_COMPRESSION_TARGET_QUALITY;
3876 cam->params.compressionTarget.targetFR = 15; /* From windows driver */
3877 cam->params.compressionTarget.targetQ = 5; /* From windows driver */
3878
3879 cam->params.qx3.qx3_detected = 0;
3880 cam->params.qx3.toplight = 0;
3881 cam->params.qx3.bottomlight = 0;
3882 cam->params.qx3.button = 0;
3883 cam->params.qx3.cradled = 0;
3884
3885 cam->video_size = VIDEOSIZE_CIF;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003886
Linus Torvalds1da177e2005-04-16 15:20:36 -07003887 cam->vp.colour = 32768; /* 50% */
3888 cam->vp.hue = 32768; /* 50% */
3889 cam->vp.brightness = 32768; /* 50% */
3890 cam->vp.contrast = 32768; /* 50% */
3891 cam->vp.whiteness = 0; /* not used -> grayscale only */
3892 cam->vp.depth = 24; /* to be set by user */
3893 cam->vp.palette = VIDEO_PALETTE_RGB24; /* to be set by user */
3894
3895 cam->vc.x = 0;
3896 cam->vc.y = 0;
3897 cam->vc.width = 0;
3898 cam->vc.height = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003899
Linus Torvalds1da177e2005-04-16 15:20:36 -07003900 cam->vw.x = 0;
3901 cam->vw.y = 0;
3902 set_vw_size(cam);
3903 cam->vw.chromakey = 0;
3904 cam->vw.flags = 0;
3905 cam->vw.clipcount = 0;
3906 cam->vw.clips = NULL;
3907
3908 cam->cmd_queue = COMMAND_NONE;
3909 cam->first_frame = 1;
3910
3911 return;
3912}
3913
3914/* initialize cam_data structure */
3915static void init_camera_struct(struct cam_data *cam,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003916 struct cpia_camera_ops *ops )
Linus Torvalds1da177e2005-04-16 15:20:36 -07003917{
3918 int i;
3919
3920 /* Default everything to 0 */
3921 memset(cam, 0, sizeof(struct cam_data));
3922
3923 cam->ops = ops;
Ingo Molnar3593cab2006-02-07 06:49:14 -02003924 mutex_init(&cam->param_lock);
3925 mutex_init(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003926
3927 reset_camera_struct(cam);
3928
3929 cam->proc_entry = NULL;
3930
3931 memcpy(&cam->vdev, &cpia_template, sizeof(cpia_template));
Hans Verkuil601e9442008-08-23 07:24:07 -03003932 video_set_drvdata(&cam->vdev, cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003933
Linus Torvalds1da177e2005-04-16 15:20:36 -07003934 cam->curframe = 0;
3935 for (i = 0; i < FRAME_NUM; i++) {
3936 cam->frame[i].width = 0;
3937 cam->frame[i].height = 0;
3938 cam->frame[i].state = FRAME_UNUSED;
3939 cam->frame[i].data = NULL;
3940 }
3941 cam->decompressed_frame.width = 0;
3942 cam->decompressed_frame.height = 0;
3943 cam->decompressed_frame.state = FRAME_UNUSED;
3944 cam->decompressed_frame.data = NULL;
3945}
3946
3947struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel)
3948{
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003949 struct cam_data *camera;
3950
Linus Torvalds1da177e2005-04-16 15:20:36 -07003951 if ((camera = kmalloc(sizeof(struct cam_data), GFP_KERNEL)) == NULL)
3952 return NULL;
3953
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003954
Linus Torvalds1da177e2005-04-16 15:20:36 -07003955 init_camera_struct( camera, ops );
3956 camera->lowlevel_data = lowlevel;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003957
Linus Torvalds1da177e2005-04-16 15:20:36 -07003958 /* register v4l device */
Hans Verkuildc60de32008-09-03 17:11:58 -03003959 if (video_register_device(&camera->vdev, VFL_TYPE_GRABBER, video_nr) < 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003960 kfree(camera);
3961 printk(KERN_DEBUG "video_register_device failed\n");
3962 return NULL;
3963 }
3964
3965 /* get version information from camera: open/reset/close */
3966
3967 /* open cpia */
3968 if (camera->ops->open(camera->lowlevel_data))
3969 return camera;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003970
Linus Torvalds1da177e2005-04-16 15:20:36 -07003971 /* reset the camera */
3972 if (reset_camera(camera) != 0) {
3973 camera->ops->close(camera->lowlevel_data);
3974 return camera;
3975 }
3976
3977 /* close cpia */
3978 camera->ops->close(camera->lowlevel_data);
3979
3980#ifdef CONFIG_PROC_FS
3981 create_proc_cpia_cam(camera);
3982#endif
3983
3984 printk(KERN_INFO " CPiA Version: %d.%02d (%d.%d)\n",
3985 camera->params.version.firmwareVersion,
3986 camera->params.version.firmwareRevision,
3987 camera->params.version.vcVersion,
3988 camera->params.version.vcRevision);
3989 printk(KERN_INFO " CPiA PnP-ID: %04x:%04x:%04x\n",
3990 camera->params.pnpID.vendor,
3991 camera->params.pnpID.product,
3992 camera->params.pnpID.deviceRevision);
3993 printk(KERN_INFO " VP-Version: %d.%d %04x\n",
3994 camera->params.vpVersion.vpVersion,
3995 camera->params.vpVersion.vpRevision,
3996 camera->params.vpVersion.cameraHeadID);
3997
3998 return camera;
3999}
4000
4001void cpia_unregister_camera(struct cam_data *cam)
4002{
4003 DBG("unregistering video\n");
4004 video_unregister_device(&cam->vdev);
4005 if (cam->open_count) {
4006 put_cam(cam->ops);
4007 DBG("camera open -- setting ops to NULL\n");
4008 cam->ops = NULL;
4009 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03004010
Linus Torvalds1da177e2005-04-16 15:20:36 -07004011#ifdef CONFIG_PROC_FS
4012 DBG("destroying /proc/cpia/video%d\n", cam->vdev.minor);
4013 destroy_proc_cpia_cam(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03004014#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07004015 if (!cam->open_count) {
4016 DBG("freeing camera\n");
4017 kfree(cam);
4018 }
4019}
4020
4021static int __init cpia_init(void)
4022{
4023 printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT,
4024 CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER);
4025
4026 printk(KERN_WARNING "Since in-kernel colorspace conversion is not "
4027 "allowed, it is disabled by default now. Users should fix the "
4028 "applications in case they don't work without conversion "
4029 "reenabled by setting the 'colorspace_conv' module "
Randy Dunlap94190452006-03-27 16:18:25 -03004030 "parameter to 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004031
4032#ifdef CONFIG_PROC_FS
4033 proc_cpia_create();
4034#endif
4035
Linus Torvalds1da177e2005-04-16 15:20:36 -07004036 return 0;
4037}
4038
4039static void __exit cpia_exit(void)
4040{
4041#ifdef CONFIG_PROC_FS
4042 proc_cpia_destroy();
4043#endif
4044}
4045
4046module_init(cpia_init);
4047module_exit(cpia_exit);
4048
4049/* Exported symbols for modules. */
4050
4051EXPORT_SYMBOL(cpia_register_camera);
4052EXPORT_SYMBOL(cpia_unregister_camera);