blob: 2377313c041a8c33976db18f0c1ef7a698c94bd6 [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>
Alexey Dobriyana99bbaf2009-10-04 16:11:37 +040034#include <linux/sched.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070035#include <linux/slab.h>
36#include <linux/proc_fs.h>
37#include <linux/ctype.h>
38#include <linux/pagemap.h>
39#include <linux/delay.h>
40#include <asm/io.h>
Ingo Molnar3593cab2006-02-07 06:49:14 -020041#include <linux/mutex.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070042
Linus Torvalds1da177e2005-04-16 15:20:36 -070043#include "cpia.h"
44
Linus Torvalds1da177e2005-04-16 15:20:36 -070045static int video_nr = -1;
46
47#ifdef MODULE
48module_param(video_nr, int, 0);
Randy Dunlap2f8de1a2006-03-21 14:53:22 -030049MODULE_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 -070050MODULE_DESCRIPTION("V4L-driver for Vision CPiA based cameras");
51MODULE_LICENSE("GPL");
52MODULE_SUPPORTED_DEVICE("video");
53#endif
54
Randy Dunlap94190452006-03-27 16:18:25 -030055static unsigned short colorspace_conv;
Linus Torvalds1da177e2005-04-16 15:20:36 -070056module_param(colorspace_conv, ushort, 0444);
57MODULE_PARM_DESC(colorspace_conv,
Mauro Carvalho Chehab4286c6f2006-04-08 16:06:16 -030058 " Colorspace conversion:"
59 "\n 0 = disable, 1 = enable"
60 "\n Default value is 0"
61 );
Linus Torvalds1da177e2005-04-16 15:20:36 -070062
63#define ABOUT "V4L-Driver for Vision CPiA based cameras"
64
Linus Torvalds1da177e2005-04-16 15:20:36 -070065#define CPIA_MODULE_CPIA (0<<5)
66#define CPIA_MODULE_SYSTEM (1<<5)
67#define CPIA_MODULE_VP_CTRL (5<<5)
68#define CPIA_MODULE_CAPTURE (6<<5)
69#define CPIA_MODULE_DEBUG (7<<5)
70
71#define INPUT (DATA_IN << 8)
72#define OUTPUT (DATA_OUT << 8)
73
74#define CPIA_COMMAND_GetCPIAVersion (INPUT | CPIA_MODULE_CPIA | 1)
75#define CPIA_COMMAND_GetPnPID (INPUT | CPIA_MODULE_CPIA | 2)
76#define CPIA_COMMAND_GetCameraStatus (INPUT | CPIA_MODULE_CPIA | 3)
77#define CPIA_COMMAND_GotoHiPower (OUTPUT | CPIA_MODULE_CPIA | 4)
78#define CPIA_COMMAND_GotoLoPower (OUTPUT | CPIA_MODULE_CPIA | 5)
79#define CPIA_COMMAND_GotoSuspend (OUTPUT | CPIA_MODULE_CPIA | 7)
80#define CPIA_COMMAND_GotoPassThrough (OUTPUT | CPIA_MODULE_CPIA | 8)
81#define CPIA_COMMAND_ModifyCameraStatus (OUTPUT | CPIA_MODULE_CPIA | 10)
82
83#define CPIA_COMMAND_ReadVCRegs (INPUT | CPIA_MODULE_SYSTEM | 1)
84#define CPIA_COMMAND_WriteVCReg (OUTPUT | CPIA_MODULE_SYSTEM | 2)
85#define CPIA_COMMAND_ReadMCPorts (INPUT | CPIA_MODULE_SYSTEM | 3)
86#define CPIA_COMMAND_WriteMCPort (OUTPUT | CPIA_MODULE_SYSTEM | 4)
87#define CPIA_COMMAND_SetBaudRate (OUTPUT | CPIA_MODULE_SYSTEM | 5)
88#define CPIA_COMMAND_SetECPTiming (OUTPUT | CPIA_MODULE_SYSTEM | 6)
89#define CPIA_COMMAND_ReadIDATA (INPUT | CPIA_MODULE_SYSTEM | 7)
90#define CPIA_COMMAND_WriteIDATA (OUTPUT | CPIA_MODULE_SYSTEM | 8)
91#define CPIA_COMMAND_GenericCall (OUTPUT | CPIA_MODULE_SYSTEM | 9)
92#define CPIA_COMMAND_I2CStart (OUTPUT | CPIA_MODULE_SYSTEM | 10)
93#define CPIA_COMMAND_I2CStop (OUTPUT | CPIA_MODULE_SYSTEM | 11)
94#define CPIA_COMMAND_I2CWrite (OUTPUT | CPIA_MODULE_SYSTEM | 12)
95#define CPIA_COMMAND_I2CRead (INPUT | CPIA_MODULE_SYSTEM | 13)
96
97#define CPIA_COMMAND_GetVPVersion (INPUT | CPIA_MODULE_VP_CTRL | 1)
98#define CPIA_COMMAND_ResetFrameCounter (INPUT | CPIA_MODULE_VP_CTRL | 2)
99#define CPIA_COMMAND_SetColourParams (OUTPUT | CPIA_MODULE_VP_CTRL | 3)
100#define CPIA_COMMAND_SetExposure (OUTPUT | CPIA_MODULE_VP_CTRL | 4)
101#define CPIA_COMMAND_SetColourBalance (OUTPUT | CPIA_MODULE_VP_CTRL | 6)
102#define CPIA_COMMAND_SetSensorFPS (OUTPUT | CPIA_MODULE_VP_CTRL | 7)
103#define CPIA_COMMAND_SetVPDefaults (OUTPUT | CPIA_MODULE_VP_CTRL | 8)
104#define CPIA_COMMAND_SetApcor (OUTPUT | CPIA_MODULE_VP_CTRL | 9)
105#define CPIA_COMMAND_SetFlickerCtrl (OUTPUT | CPIA_MODULE_VP_CTRL | 10)
106#define CPIA_COMMAND_SetVLOffset (OUTPUT | CPIA_MODULE_VP_CTRL | 11)
107#define CPIA_COMMAND_GetColourParams (INPUT | CPIA_MODULE_VP_CTRL | 16)
108#define CPIA_COMMAND_GetColourBalance (INPUT | CPIA_MODULE_VP_CTRL | 17)
109#define CPIA_COMMAND_GetExposure (INPUT | CPIA_MODULE_VP_CTRL | 18)
110#define CPIA_COMMAND_SetSensorMatrix (OUTPUT | CPIA_MODULE_VP_CTRL | 19)
111#define CPIA_COMMAND_ColourBars (OUTPUT | CPIA_MODULE_VP_CTRL | 25)
112#define CPIA_COMMAND_ReadVPRegs (INPUT | CPIA_MODULE_VP_CTRL | 30)
113#define CPIA_COMMAND_WriteVPReg (OUTPUT | CPIA_MODULE_VP_CTRL | 31)
114
115#define CPIA_COMMAND_GrabFrame (OUTPUT | CPIA_MODULE_CAPTURE | 1)
116#define CPIA_COMMAND_UploadFrame (OUTPUT | CPIA_MODULE_CAPTURE | 2)
117#define CPIA_COMMAND_SetGrabMode (OUTPUT | CPIA_MODULE_CAPTURE | 3)
118#define CPIA_COMMAND_InitStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 4)
119#define CPIA_COMMAND_FiniStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 5)
120#define CPIA_COMMAND_StartStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 6)
121#define CPIA_COMMAND_EndStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 7)
122#define CPIA_COMMAND_SetFormat (OUTPUT | CPIA_MODULE_CAPTURE | 8)
123#define CPIA_COMMAND_SetROI (OUTPUT | CPIA_MODULE_CAPTURE | 9)
124#define CPIA_COMMAND_SetCompression (OUTPUT | CPIA_MODULE_CAPTURE | 10)
125#define CPIA_COMMAND_SetCompressionTarget (OUTPUT | CPIA_MODULE_CAPTURE | 11)
126#define CPIA_COMMAND_SetYUVThresh (OUTPUT | CPIA_MODULE_CAPTURE | 12)
127#define CPIA_COMMAND_SetCompressionParams (OUTPUT | CPIA_MODULE_CAPTURE | 13)
128#define CPIA_COMMAND_DiscardFrame (OUTPUT | CPIA_MODULE_CAPTURE | 14)
129#define CPIA_COMMAND_GrabReset (OUTPUT | CPIA_MODULE_CAPTURE | 15)
130
131#define CPIA_COMMAND_OutputRS232 (OUTPUT | CPIA_MODULE_DEBUG | 1)
132#define CPIA_COMMAND_AbortProcess (OUTPUT | CPIA_MODULE_DEBUG | 4)
133#define CPIA_COMMAND_SetDramPage (OUTPUT | CPIA_MODULE_DEBUG | 5)
134#define CPIA_COMMAND_StartDramUpload (OUTPUT | CPIA_MODULE_DEBUG | 6)
135#define CPIA_COMMAND_StartDummyDtream (OUTPUT | CPIA_MODULE_DEBUG | 8)
136#define CPIA_COMMAND_AbortStream (OUTPUT | CPIA_MODULE_DEBUG | 9)
137#define CPIA_COMMAND_DownloadDRAM (OUTPUT | CPIA_MODULE_DEBUG | 10)
138#define CPIA_COMMAND_Null (OUTPUT | CPIA_MODULE_DEBUG | 11)
139
140enum {
141 FRAME_READY, /* Ready to grab into */
142 FRAME_GRABBING, /* In the process of being grabbed into */
143 FRAME_DONE, /* Finished grabbing, but not been synced yet */
144 FRAME_UNUSED, /* Unused (no MCAPTURE) */
145};
146
147#define COMMAND_NONE 0x0000
148#define COMMAND_SETCOMPRESSION 0x0001
149#define COMMAND_SETCOMPRESSIONTARGET 0x0002
150#define COMMAND_SETCOLOURPARAMS 0x0004
151#define COMMAND_SETFORMAT 0x0008
152#define COMMAND_PAUSE 0x0010
153#define COMMAND_RESUME 0x0020
154#define COMMAND_SETYUVTHRESH 0x0040
155#define COMMAND_SETECPTIMING 0x0080
156#define COMMAND_SETCOMPRESSIONPARAMS 0x0100
157#define COMMAND_SETEXPOSURE 0x0200
158#define COMMAND_SETCOLOURBALANCE 0x0400
159#define COMMAND_SETSENSORFPS 0x0800
160#define COMMAND_SETAPCOR 0x1000
161#define COMMAND_SETFLICKERCTRL 0x2000
162#define COMMAND_SETVLOFFSET 0x4000
163#define COMMAND_SETLIGHTS 0x8000
164
165#define ROUND_UP_EXP_FOR_FLICKER 15
166
167/* Constants for automatic frame rate adjustment */
168#define MAX_EXP 302
169#define MAX_EXP_102 255
170#define LOW_EXP 140
171#define VERY_LOW_EXP 70
172#define TC 94
173#define EXP_ACC_DARK 50
174#define EXP_ACC_LIGHT 90
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300175#define HIGH_COMP_102 160
176#define MAX_COMP 239
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177#define DARK_TIME 3
178#define LIGHT_TIME 3
179
180/* Maximum number of 10ms loops to wait for the stream to become ready */
181#define READY_TIMEOUT 100
182
183/* Developer's Guide Table 5 p 3-34
184 * indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/
185static u8 flicker_jumps[2][2][4] =
186{ { { 76, 38, 19, 9 }, { 92, 46, 23, 11 } },
187 { { 64, 32, 16, 8 }, { 76, 38, 19, 9} }
188};
189
190/* forward declaration of local function */
191static void reset_camera_struct(struct cam_data *cam);
192static int find_over_exposure(int brightness);
193static void set_flicker(struct cam_params *params, volatile u32 *command_flags,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300194 int on);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195
196
197/**********************************************************************
198 *
199 * Memory management
200 *
201 **********************************************************************/
202static void *rvmalloc(unsigned long size)
203{
204 void *mem;
205 unsigned long adr;
206
207 size = PAGE_ALIGN(size);
208 mem = vmalloc_32(size);
209 if (!mem)
210 return NULL;
211
212 memset(mem, 0, size); /* Clear the ram out, no junk to the user */
213 adr = (unsigned long) mem;
214 while (size > 0) {
215 SetPageReserved(vmalloc_to_page((void *)adr));
216 adr += PAGE_SIZE;
217 size -= PAGE_SIZE;
218 }
219
220 return mem;
221}
222
223static void rvfree(void *mem, unsigned long size)
224{
225 unsigned long adr;
226
227 if (!mem)
228 return;
229
230 adr = (unsigned long) mem;
231 while ((long) size > 0) {
232 ClearPageReserved(vmalloc_to_page((void *)adr));
233 adr += PAGE_SIZE;
234 size -= PAGE_SIZE;
235 }
236 vfree(mem);
237}
238
239/**********************************************************************
240 *
241 * /proc interface
242 *
243 **********************************************************************/
244#ifdef CONFIG_PROC_FS
245static struct proc_dir_entry *cpia_proc_root=NULL;
246
247static int cpia_read_proc(char *page, char **start, off_t off,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300248 int count, int *eof, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249{
250 char *out = page;
251 int len, tmp;
252 struct cam_data *cam = data;
253 char tmpstr[29];
254
255 /* IMPORTANT: This output MUST be kept under PAGE_SIZE
256 * or we need to get more sophisticated. */
257
258 out += sprintf(out, "read-only\n-----------------------\n");
259 out += sprintf(out, "V4L Driver version: %d.%d.%d\n",
260 CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER);
261 out += sprintf(out, "CPIA Version: %d.%02d (%d.%d)\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300262 cam->params.version.firmwareVersion,
263 cam->params.version.firmwareRevision,
264 cam->params.version.vcVersion,
265 cam->params.version.vcRevision);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266 out += sprintf(out, "CPIA PnP-ID: %04x:%04x:%04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300267 cam->params.pnpID.vendor, cam->params.pnpID.product,
268 cam->params.pnpID.deviceRevision);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269 out += sprintf(out, "VP-Version: %d.%d %04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300270 cam->params.vpVersion.vpVersion,
271 cam->params.vpVersion.vpRevision,
272 cam->params.vpVersion.cameraHeadID);
273
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274 out += sprintf(out, "system_state: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300275 cam->params.status.systemState);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276 out += sprintf(out, "grab_state: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300277 cam->params.status.grabState);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 out += sprintf(out, "stream_state: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300279 cam->params.status.streamState);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280 out += sprintf(out, "fatal_error: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300281 cam->params.status.fatalError);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282 out += sprintf(out, "cmd_error: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300283 cam->params.status.cmdError);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284 out += sprintf(out, "debug_flags: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300285 cam->params.status.debugFlags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286 out += sprintf(out, "vp_status: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300287 cam->params.status.vpStatus);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288 out += sprintf(out, "error_code: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300289 cam->params.status.errorCode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290 /* QX3 specific entries */
291 if (cam->params.qx3.qx3_detected) {
292 out += sprintf(out, "button: %4d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300293 cam->params.qx3.button);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294 out += sprintf(out, "cradled: %4d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300295 cam->params.qx3.cradled);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296 }
297 out += sprintf(out, "video_size: %s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300298 cam->params.format.videoSize == VIDEOSIZE_CIF ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299 "CIF " : "QCIF");
300 out += sprintf(out, "roi: (%3d, %3d) to (%3d, %3d)\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300301 cam->params.roi.colStart*8,
302 cam->params.roi.rowStart*4,
303 cam->params.roi.colEnd*8,
304 cam->params.roi.rowEnd*4);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305 out += sprintf(out, "actual_fps: %3d\n", cam->fps);
306 out += sprintf(out, "transfer_rate: %4dkB/s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300307 cam->transfer_rate);
308
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309 out += sprintf(out, "\nread-write\n");
310 out += sprintf(out, "----------------------- current min"
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300311 " max default comment\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312 out += sprintf(out, "brightness: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300313 cam->params.colourParams.brightness, 0, 100, 50);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314 if (cam->params.version.firmwareVersion == 1 &&
315 cam->params.version.firmwareRevision == 2)
316 /* 1-02 firmware limits contrast to 80 */
317 tmp = 80;
318 else
319 tmp = 96;
320
321 out += sprintf(out, "contrast: %8d %8d %8d %8d"
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300322 " steps of 8\n",
323 cam->params.colourParams.contrast, 0, tmp, 48);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324 out += sprintf(out, "saturation: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300325 cam->params.colourParams.saturation, 0, 100, 50);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 tmp = (25000+5000*cam->params.sensorFps.baserate)/
327 (1<<cam->params.sensorFps.divisor);
328 out += sprintf(out, "sensor_fps: %4d.%03d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300329 tmp/1000, tmp%1000, 3, 30, 15);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330 out += sprintf(out, "stream_start_line: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300331 2*cam->params.streamStartLine, 0,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332 cam->params.format.videoSize == VIDEOSIZE_CIF ? 288:144,
333 cam->params.format.videoSize == VIDEOSIZE_CIF ? 240:120);
334 out += sprintf(out, "sub_sample: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300335 cam->params.format.subSample == SUBSAMPLE_420 ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 "420" : "422", "420", "422", "422");
337 out += sprintf(out, "yuv_order: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300338 cam->params.format.yuvOrder == YUVORDER_YUYV ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339 "YUYV" : "UYVY", "YUYV" , "UYVY", "YUYV");
340 out += sprintf(out, "ecp_timing: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300341 cam->params.ecpTiming ? "slow" : "normal", "slow",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342 "normal", "normal");
343
344 if (cam->params.colourBalance.balanceMode == 2) {
345 sprintf(tmpstr, "auto");
346 } else {
347 sprintf(tmpstr, "manual");
348 }
349 out += sprintf(out, "color_balance_mode: %8s %8s %8s"
350 " %8s\n", tmpstr, "manual", "auto", "auto");
351 out += sprintf(out, "red_gain: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300352 cam->params.colourBalance.redGain, 0, 212, 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353 out += sprintf(out, "green_gain: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300354 cam->params.colourBalance.greenGain, 0, 212, 6);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355 out += sprintf(out, "blue_gain: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300356 cam->params.colourBalance.blueGain, 0, 212, 92);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357
358 if (cam->params.version.firmwareVersion == 1 &&
359 cam->params.version.firmwareRevision == 2)
360 /* 1-02 firmware limits gain to 2 */
361 sprintf(tmpstr, "%8d %8d %8d", 1, 2, 2);
362 else
363 sprintf(tmpstr, "%8d %8d %8d", 1, 8, 2);
364
365 if (cam->params.exposure.gainMode == 0)
366 out += sprintf(out, "max_gain: unknown %28s"
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300367 " powers of 2\n", tmpstr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 else
369 out += sprintf(out, "max_gain: %8d %28s"
370 " 1,2,4 or 8 \n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300371 1<<(cam->params.exposure.gainMode-1), tmpstr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372
373 switch(cam->params.exposure.expMode) {
374 case 1:
375 case 3:
376 sprintf(tmpstr, "manual");
377 break;
378 case 2:
379 sprintf(tmpstr, "auto");
380 break;
381 default:
382 sprintf(tmpstr, "unknown");
383 break;
384 }
385 out += sprintf(out, "exposure_mode: %8s %8s %8s"
386 " %8s\n", tmpstr, "manual", "auto", "auto");
387 out += sprintf(out, "centre_weight: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300388 (2-cam->params.exposure.centreWeight) ? "on" : "off",
389 "off", "on", "on");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390 out += sprintf(out, "gain: %8d %8d max_gain %8d 1,2,4,8 possible\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300391 1<<cam->params.exposure.gain, 1, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392 if (cam->params.version.firmwareVersion == 1 &&
393 cam->params.version.firmwareRevision == 2)
394 /* 1-02 firmware limits fineExp/2 to 127 */
395 tmp = 254;
396 else
397 tmp = 510;
398
399 out += sprintf(out, "fine_exp: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300400 cam->params.exposure.fineExp*2, 0, tmp, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401 if (cam->params.version.firmwareVersion == 1 &&
402 cam->params.version.firmwareRevision == 2)
403 /* 1-02 firmware limits coarseExpHi to 0 */
404 tmp = MAX_EXP_102;
405 else
406 tmp = MAX_EXP;
407
408 out += sprintf(out, "coarse_exp: %8d %8d %8d"
409 " %8d\n", cam->params.exposure.coarseExpLo+
410 256*cam->params.exposure.coarseExpHi, 0, tmp, 185);
411 out += sprintf(out, "red_comp: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300412 cam->params.exposure.redComp, COMP_RED, 255, COMP_RED);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413 out += sprintf(out, "green1_comp: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300414 cam->params.exposure.green1Comp, COMP_GREEN1, 255,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415 COMP_GREEN1);
416 out += sprintf(out, "green2_comp: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300417 cam->params.exposure.green2Comp, COMP_GREEN2, 255,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 COMP_GREEN2);
419 out += sprintf(out, "blue_comp: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300420 cam->params.exposure.blueComp, COMP_BLUE, 255, COMP_BLUE);
421
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422 out += sprintf(out, "apcor_gain1: %#8x %#8x %#8x %#8x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300423 cam->params.apcor.gain1, 0, 0xff, 0x1c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424 out += sprintf(out, "apcor_gain2: %#8x %#8x %#8x %#8x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300425 cam->params.apcor.gain2, 0, 0xff, 0x1a);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426 out += sprintf(out, "apcor_gain4: %#8x %#8x %#8x %#8x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300427 cam->params.apcor.gain4, 0, 0xff, 0x2d);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428 out += sprintf(out, "apcor_gain8: %#8x %#8x %#8x %#8x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300429 cam->params.apcor.gain8, 0, 0xff, 0x2a);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430 out += sprintf(out, "vl_offset_gain1: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300431 cam->params.vlOffset.gain1, 0, 255, 24);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 out += sprintf(out, "vl_offset_gain2: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300433 cam->params.vlOffset.gain2, 0, 255, 28);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434 out += sprintf(out, "vl_offset_gain4: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300435 cam->params.vlOffset.gain4, 0, 255, 30);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436 out += sprintf(out, "vl_offset_gain8: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300437 cam->params.vlOffset.gain8, 0, 255, 30);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438 out += sprintf(out, "flicker_control: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300439 cam->params.flickerControl.flickerMode ? "on" : "off",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440 "off", "on", "off");
441 out += sprintf(out, "mains_frequency: %8d %8d %8d %8d"
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300442 " only 50/60\n",
443 cam->mainsFreq ? 60 : 50, 50, 60, 50);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444 if(cam->params.flickerControl.allowableOverExposure < 0)
445 out += sprintf(out, "allowable_overexposure: %4dauto auto %8d auto\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300446 -cam->params.flickerControl.allowableOverExposure,
447 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448 else
449 out += sprintf(out, "allowable_overexposure: %8d auto %8d auto\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300450 cam->params.flickerControl.allowableOverExposure,
451 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452 out += sprintf(out, "compression_mode: ");
453 switch(cam->params.compression.mode) {
454 case CPIA_COMPRESSION_NONE:
455 out += sprintf(out, "%8s", "none");
456 break;
457 case CPIA_COMPRESSION_AUTO:
458 out += sprintf(out, "%8s", "auto");
459 break;
460 case CPIA_COMPRESSION_MANUAL:
461 out += sprintf(out, "%8s", "manual");
462 break;
463 default:
464 out += sprintf(out, "%8s", "unknown");
465 break;
466 }
467 out += sprintf(out, " none,auto,manual auto\n");
468 out += sprintf(out, "decimation_enable: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300469 cam->params.compression.decimation ==
470 DECIMATION_ENAB ? "on":"off", "off", "on",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471 "off");
472 out += sprintf(out, "compression_target: %9s %9s %9s %9s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300473 cam->params.compressionTarget.frTargeting ==
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 CPIA_COMPRESSION_TARGET_FRAMERATE ?
475 "framerate":"quality",
476 "framerate", "quality", "quality");
477 out += sprintf(out, "target_framerate: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300478 cam->params.compressionTarget.targetFR, 1, 30, 15);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479 out += sprintf(out, "target_quality: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300480 cam->params.compressionTarget.targetQ, 1, 64, 5);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481 out += sprintf(out, "y_threshold: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300482 cam->params.yuvThreshold.yThreshold, 0, 31, 6);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483 out += sprintf(out, "uv_threshold: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300484 cam->params.yuvThreshold.uvThreshold, 0, 31, 6);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485 out += sprintf(out, "hysteresis: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300486 cam->params.compressionParams.hysteresis, 0, 255, 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487 out += sprintf(out, "threshold_max: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300488 cam->params.compressionParams.threshMax, 0, 255, 11);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489 out += sprintf(out, "small_step: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300490 cam->params.compressionParams.smallStep, 0, 255, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491 out += sprintf(out, "large_step: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300492 cam->params.compressionParams.largeStep, 0, 255, 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493 out += sprintf(out, "decimation_hysteresis: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300494 cam->params.compressionParams.decimationHysteresis,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495 0, 255, 2);
496 out += sprintf(out, "fr_diff_step_thresh: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300497 cam->params.compressionParams.frDiffStepThresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498 0, 255, 5);
499 out += sprintf(out, "q_diff_step_thresh: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300500 cam->params.compressionParams.qDiffStepThresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501 0, 255, 3);
502 out += sprintf(out, "decimation_thresh_mod: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300503 cam->params.compressionParams.decimationThreshMod,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504 0, 255, 2);
505 /* QX3 specific entries */
506 if (cam->params.qx3.qx3_detected) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300507 out += sprintf(out, "toplight: %8s %8s %8s %8s\n",
508 cam->params.qx3.toplight ? "on" : "off",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509 "off", "on", "off");
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300510 out += sprintf(out, "bottomlight: %8s %8s %8s %8s\n",
511 cam->params.qx3.bottomlight ? "on" : "off",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512 "off", "on", "off");
513 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300514
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515 len = out - page;
516 len -= off;
517 if (len < count) {
518 *eof = 1;
519 if (len <= 0) return 0;
520 } else
521 len = count;
522
523 *start = page + off;
524 return len;
525}
526
527
528static int match(char *checkstr, char **buffer, unsigned long *count,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300529 int *find_colon, int *err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530{
531 int ret, colon_found = 1;
532 int len = strlen(checkstr);
533 ret = (len <= *count && strncmp(*buffer, checkstr, len) == 0);
534 if (ret) {
535 *buffer += len;
536 *count -= len;
537 if (*find_colon) {
538 colon_found = 0;
539 while (*count && (**buffer == ' ' || **buffer == '\t' ||
540 (!colon_found && **buffer == ':'))) {
541 if (**buffer == ':')
542 colon_found = 1;
543 --*count;
544 ++*buffer;
545 }
546 if (!*count || !colon_found)
547 *err = -EINVAL;
548 *find_colon = 0;
549 }
550 }
551 return ret;
552}
553
554static unsigned long int value(char **buffer, unsigned long *count, int *err)
555{
556 char *p;
557 unsigned long int ret;
558 ret = simple_strtoul(*buffer, &p, 0);
559 if (p == *buffer)
560 *err = -EINVAL;
561 else {
562 *count -= p - *buffer;
563 *buffer = p;
564 }
565 return ret;
566}
567
568static int cpia_write_proc(struct file *file, const char __user *buf,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300569 unsigned long count, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570{
571 struct cam_data *cam = data;
572 struct cam_params new_params;
573 char *page, *buffer;
574 int retval, find_colon;
575 int size = count;
576 unsigned long val = 0;
577 u32 command_flags = 0;
578 u8 new_mains;
579
580 /*
581 * This code to copy from buf to page is shamelessly copied
582 * from the comx driver
583 */
584 if (count > PAGE_SIZE) {
585 printk(KERN_ERR "count is %lu > %d!!!\n", count, (int)PAGE_SIZE);
586 return -ENOSPC;
587 }
588
589 if (!(page = (char *)__get_free_page(GFP_KERNEL))) return -ENOMEM;
590
591 if(copy_from_user(page, buf, count))
592 {
593 retval = -EFAULT;
594 goto out;
595 }
596
597 if (page[count-1] == '\n')
598 page[count-1] = '\0';
599 else if (count < PAGE_SIZE)
600 page[count] = '\0';
601 else if (page[count]) {
602 retval = -EINVAL;
603 goto out;
604 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300605
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606 buffer = page;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300607
Ingo Molnar3593cab2006-02-07 06:49:14 -0200608 if (mutex_lock_interruptible(&cam->param_lock))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609 return -ERESTARTSYS;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300610
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611 /*
612 * Skip over leading whitespace
613 */
614 while (count && isspace(*buffer)) {
615 --count;
616 ++buffer;
617 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300618
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619 memcpy(&new_params, &cam->params, sizeof(struct cam_params));
620 new_mains = cam->mainsFreq;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300621
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622#define MATCH(x) (match(x, &buffer, &count, &find_colon, &retval))
623#define VALUE (value(&buffer,&count, &retval))
624#define FIRMWARE_VERSION(x,y) (new_params.version.firmwareVersion == (x) && \
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300625 new_params.version.firmwareRevision == (y))
626
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627 retval = 0;
628 while (count && !retval) {
629 find_colon = 1;
630 if (MATCH("brightness")) {
631 if (!retval)
632 val = VALUE;
633
634 if (!retval) {
635 if (val <= 100)
636 new_params.colourParams.brightness = val;
637 else
638 retval = -EINVAL;
639 }
640 command_flags |= COMMAND_SETCOLOURPARAMS;
641 if(new_params.flickerControl.allowableOverExposure < 0)
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300642 new_params.flickerControl.allowableOverExposure =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643 -find_over_exposure(new_params.colourParams.brightness);
644 if(new_params.flickerControl.flickerMode != 0)
645 command_flags |= COMMAND_SETFLICKERCTRL;
646
647 } else if (MATCH("contrast")) {
648 if (!retval)
649 val = VALUE;
650
651 if (!retval) {
652 if (val <= 100) {
653 /* contrast is in steps of 8, so round*/
654 val = ((val + 3) / 8) * 8;
655 /* 1-02 firmware limits contrast to 80*/
656 if (FIRMWARE_VERSION(1,2) && val > 80)
657 val = 80;
658
659 new_params.colourParams.contrast = val;
660 } else
661 retval = -EINVAL;
662 }
663 command_flags |= COMMAND_SETCOLOURPARAMS;
664 } else if (MATCH("saturation")) {
665 if (!retval)
666 val = VALUE;
667
668 if (!retval) {
669 if (val <= 100)
670 new_params.colourParams.saturation = val;
671 else
672 retval = -EINVAL;
673 }
674 command_flags |= COMMAND_SETCOLOURPARAMS;
675 } else if (MATCH("sensor_fps")) {
676 if (!retval)
677 val = VALUE;
678
679 if (!retval) {
680 /* find values so that sensorFPS is minimized,
681 * but >= val */
682 if (val > 30)
683 retval = -EINVAL;
684 else if (val > 25) {
685 new_params.sensorFps.divisor = 0;
686 new_params.sensorFps.baserate = 1;
687 } else if (val > 15) {
688 new_params.sensorFps.divisor = 0;
689 new_params.sensorFps.baserate = 0;
690 } else if (val > 12) {
691 new_params.sensorFps.divisor = 1;
692 new_params.sensorFps.baserate = 1;
693 } else if (val > 7) {
694 new_params.sensorFps.divisor = 1;
695 new_params.sensorFps.baserate = 0;
696 } else if (val > 6) {
697 new_params.sensorFps.divisor = 2;
698 new_params.sensorFps.baserate = 1;
699 } else if (val > 3) {
700 new_params.sensorFps.divisor = 2;
701 new_params.sensorFps.baserate = 0;
702 } else {
703 new_params.sensorFps.divisor = 3;
704 /* Either base rate would work here */
705 new_params.sensorFps.baserate = 1;
706 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300707 new_params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708 flicker_jumps[new_mains]
709 [new_params.sensorFps.baserate]
710 [new_params.sensorFps.divisor];
711 if (new_params.flickerControl.flickerMode)
712 command_flags |= COMMAND_SETFLICKERCTRL;
713 }
714 command_flags |= COMMAND_SETSENSORFPS;
715 cam->exposure_status = EXPOSURE_NORMAL;
716 } else if (MATCH("stream_start_line")) {
717 if (!retval)
718 val = VALUE;
719
720 if (!retval) {
721 int max_line = 288;
722
723 if (new_params.format.videoSize == VIDEOSIZE_QCIF)
724 max_line = 144;
725 if (val <= max_line)
726 new_params.streamStartLine = val/2;
727 else
728 retval = -EINVAL;
729 }
730 } else if (MATCH("sub_sample")) {
731 if (!retval && MATCH("420"))
732 new_params.format.subSample = SUBSAMPLE_420;
733 else if (!retval && MATCH("422"))
734 new_params.format.subSample = SUBSAMPLE_422;
735 else
736 retval = -EINVAL;
737
738 command_flags |= COMMAND_SETFORMAT;
739 } else if (MATCH("yuv_order")) {
740 if (!retval && MATCH("YUYV"))
741 new_params.format.yuvOrder = YUVORDER_YUYV;
742 else if (!retval && MATCH("UYVY"))
743 new_params.format.yuvOrder = YUVORDER_UYVY;
744 else
745 retval = -EINVAL;
746
747 command_flags |= COMMAND_SETFORMAT;
748 } else if (MATCH("ecp_timing")) {
749 if (!retval && MATCH("normal"))
750 new_params.ecpTiming = 0;
751 else if (!retval && MATCH("slow"))
752 new_params.ecpTiming = 1;
753 else
754 retval = -EINVAL;
755
756 command_flags |= COMMAND_SETECPTIMING;
757 } else if (MATCH("color_balance_mode")) {
758 if (!retval && MATCH("manual"))
759 new_params.colourBalance.balanceMode = 3;
760 else if (!retval && MATCH("auto"))
761 new_params.colourBalance.balanceMode = 2;
762 else
763 retval = -EINVAL;
764
765 command_flags |= COMMAND_SETCOLOURBALANCE;
766 } else if (MATCH("red_gain")) {
767 if (!retval)
768 val = VALUE;
769
770 if (!retval) {
771 if (val <= 212) {
772 new_params.colourBalance.redGain = val;
773 new_params.colourBalance.balanceMode = 1;
774 } else
775 retval = -EINVAL;
776 }
777 command_flags |= COMMAND_SETCOLOURBALANCE;
778 } else if (MATCH("green_gain")) {
779 if (!retval)
780 val = VALUE;
781
782 if (!retval) {
783 if (val <= 212) {
784 new_params.colourBalance.greenGain = val;
785 new_params.colourBalance.balanceMode = 1;
786 } else
787 retval = -EINVAL;
788 }
789 command_flags |= COMMAND_SETCOLOURBALANCE;
790 } else if (MATCH("blue_gain")) {
791 if (!retval)
792 val = VALUE;
793
794 if (!retval) {
795 if (val <= 212) {
796 new_params.colourBalance.blueGain = val;
797 new_params.colourBalance.balanceMode = 1;
798 } else
799 retval = -EINVAL;
800 }
801 command_flags |= COMMAND_SETCOLOURBALANCE;
802 } else if (MATCH("max_gain")) {
803 if (!retval)
804 val = VALUE;
805
806 if (!retval) {
807 /* 1-02 firmware limits gain to 2 */
808 if (FIRMWARE_VERSION(1,2) && val > 2)
809 val = 2;
810 switch(val) {
811 case 1:
812 new_params.exposure.gainMode = 1;
813 break;
814 case 2:
815 new_params.exposure.gainMode = 2;
816 break;
817 case 4:
818 new_params.exposure.gainMode = 3;
819 break;
820 case 8:
821 new_params.exposure.gainMode = 4;
822 break;
823 default:
824 retval = -EINVAL;
825 break;
826 }
827 }
828 command_flags |= COMMAND_SETEXPOSURE;
829 } else if (MATCH("exposure_mode")) {
830 if (!retval && MATCH("auto"))
831 new_params.exposure.expMode = 2;
832 else if (!retval && MATCH("manual")) {
833 if (new_params.exposure.expMode == 2)
834 new_params.exposure.expMode = 3;
835 if(new_params.flickerControl.flickerMode != 0)
836 command_flags |= COMMAND_SETFLICKERCTRL;
837 new_params.flickerControl.flickerMode = 0;
838 } else
839 retval = -EINVAL;
840
841 command_flags |= COMMAND_SETEXPOSURE;
842 } else if (MATCH("centre_weight")) {
843 if (!retval && MATCH("on"))
844 new_params.exposure.centreWeight = 1;
845 else if (!retval && MATCH("off"))
846 new_params.exposure.centreWeight = 2;
847 else
848 retval = -EINVAL;
849
850 command_flags |= COMMAND_SETEXPOSURE;
851 } else if (MATCH("gain")) {
852 if (!retval)
853 val = VALUE;
854
855 if (!retval) {
856 switch(val) {
857 case 1:
858 new_params.exposure.gain = 0;
859 break;
860 case 2:
861 new_params.exposure.gain = 1;
862 break;
863 case 4:
864 new_params.exposure.gain = 2;
865 break;
866 case 8:
867 new_params.exposure.gain = 3;
868 break;
869 default:
870 retval = -EINVAL;
871 break;
872 }
873 new_params.exposure.expMode = 1;
874 if(new_params.flickerControl.flickerMode != 0)
875 command_flags |= COMMAND_SETFLICKERCTRL;
876 new_params.flickerControl.flickerMode = 0;
877 command_flags |= COMMAND_SETEXPOSURE;
878 if (new_params.exposure.gain >
879 new_params.exposure.gainMode-1)
880 retval = -EINVAL;
881 }
882 } else if (MATCH("fine_exp")) {
883 if (!retval)
884 val = VALUE/2;
885
886 if (!retval) {
887 if (val < 256) {
888 /* 1-02 firmware limits fineExp/2 to 127*/
889 if (FIRMWARE_VERSION(1,2) && val > 127)
890 val = 127;
891 new_params.exposure.fineExp = val;
892 new_params.exposure.expMode = 1;
893 command_flags |= COMMAND_SETEXPOSURE;
894 if(new_params.flickerControl.flickerMode != 0)
895 command_flags |= COMMAND_SETFLICKERCTRL;
896 new_params.flickerControl.flickerMode = 0;
897 command_flags |= COMMAND_SETFLICKERCTRL;
898 } else
899 retval = -EINVAL;
900 }
901 } else if (MATCH("coarse_exp")) {
902 if (!retval)
903 val = VALUE;
904
905 if (!retval) {
906 if (val <= MAX_EXP) {
907 if (FIRMWARE_VERSION(1,2) &&
908 val > MAX_EXP_102)
909 val = MAX_EXP_102;
910 new_params.exposure.coarseExpLo =
911 val & 0xff;
912 new_params.exposure.coarseExpHi =
913 val >> 8;
914 new_params.exposure.expMode = 1;
915 command_flags |= COMMAND_SETEXPOSURE;
916 if(new_params.flickerControl.flickerMode != 0)
917 command_flags |= COMMAND_SETFLICKERCTRL;
918 new_params.flickerControl.flickerMode = 0;
919 command_flags |= COMMAND_SETFLICKERCTRL;
920 } else
921 retval = -EINVAL;
922 }
923 } else if (MATCH("red_comp")) {
924 if (!retval)
925 val = VALUE;
926
927 if (!retval) {
928 if (val >= COMP_RED && val <= 255) {
929 new_params.exposure.redComp = val;
930 new_params.exposure.compMode = 1;
931 command_flags |= COMMAND_SETEXPOSURE;
932 } else
933 retval = -EINVAL;
934 }
935 } else if (MATCH("green1_comp")) {
936 if (!retval)
937 val = VALUE;
938
939 if (!retval) {
940 if (val >= COMP_GREEN1 && val <= 255) {
941 new_params.exposure.green1Comp = val;
942 new_params.exposure.compMode = 1;
943 command_flags |= COMMAND_SETEXPOSURE;
944 } else
945 retval = -EINVAL;
946 }
947 } else if (MATCH("green2_comp")) {
948 if (!retval)
949 val = VALUE;
950
951 if (!retval) {
952 if (val >= COMP_GREEN2 && val <= 255) {
953 new_params.exposure.green2Comp = val;
954 new_params.exposure.compMode = 1;
955 command_flags |= COMMAND_SETEXPOSURE;
956 } else
957 retval = -EINVAL;
958 }
959 } else if (MATCH("blue_comp")) {
960 if (!retval)
961 val = VALUE;
962
963 if (!retval) {
964 if (val >= COMP_BLUE && val <= 255) {
965 new_params.exposure.blueComp = val;
966 new_params.exposure.compMode = 1;
967 command_flags |= COMMAND_SETEXPOSURE;
968 } else
969 retval = -EINVAL;
970 }
971 } else if (MATCH("apcor_gain1")) {
972 if (!retval)
973 val = VALUE;
974
975 if (!retval) {
976 command_flags |= COMMAND_SETAPCOR;
977 if (val <= 0xff)
978 new_params.apcor.gain1 = val;
979 else
980 retval = -EINVAL;
981 }
982 } else if (MATCH("apcor_gain2")) {
983 if (!retval)
984 val = VALUE;
985
986 if (!retval) {
987 command_flags |= COMMAND_SETAPCOR;
988 if (val <= 0xff)
989 new_params.apcor.gain2 = val;
990 else
991 retval = -EINVAL;
992 }
993 } else if (MATCH("apcor_gain4")) {
994 if (!retval)
995 val = VALUE;
996
997 if (!retval) {
998 command_flags |= COMMAND_SETAPCOR;
999 if (val <= 0xff)
1000 new_params.apcor.gain4 = val;
1001 else
1002 retval = -EINVAL;
1003 }
1004 } else if (MATCH("apcor_gain8")) {
1005 if (!retval)
1006 val = VALUE;
1007
1008 if (!retval) {
1009 command_flags |= COMMAND_SETAPCOR;
1010 if (val <= 0xff)
1011 new_params.apcor.gain8 = val;
1012 else
1013 retval = -EINVAL;
1014 }
1015 } else if (MATCH("vl_offset_gain1")) {
1016 if (!retval)
1017 val = VALUE;
1018
1019 if (!retval) {
1020 if (val <= 0xff)
1021 new_params.vlOffset.gain1 = val;
1022 else
1023 retval = -EINVAL;
1024 }
1025 command_flags |= COMMAND_SETVLOFFSET;
1026 } else if (MATCH("vl_offset_gain2")) {
1027 if (!retval)
1028 val = VALUE;
1029
1030 if (!retval) {
1031 if (val <= 0xff)
1032 new_params.vlOffset.gain2 = val;
1033 else
1034 retval = -EINVAL;
1035 }
1036 command_flags |= COMMAND_SETVLOFFSET;
1037 } else if (MATCH("vl_offset_gain4")) {
1038 if (!retval)
1039 val = VALUE;
1040
1041 if (!retval) {
1042 if (val <= 0xff)
1043 new_params.vlOffset.gain4 = val;
1044 else
1045 retval = -EINVAL;
1046 }
1047 command_flags |= COMMAND_SETVLOFFSET;
1048 } else if (MATCH("vl_offset_gain8")) {
1049 if (!retval)
1050 val = VALUE;
1051
1052 if (!retval) {
1053 if (val <= 0xff)
1054 new_params.vlOffset.gain8 = val;
1055 else
1056 retval = -EINVAL;
1057 }
1058 command_flags |= COMMAND_SETVLOFFSET;
1059 } else if (MATCH("flicker_control")) {
1060 if (!retval && MATCH("on")) {
1061 set_flicker(&new_params, &command_flags, 1);
1062 } else if (!retval && MATCH("off")) {
1063 set_flicker(&new_params, &command_flags, 0);
1064 } else
1065 retval = -EINVAL;
1066
1067 command_flags |= COMMAND_SETFLICKERCTRL;
1068 } else if (MATCH("mains_frequency")) {
1069 if (!retval && MATCH("50")) {
1070 new_mains = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001071 new_params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072 flicker_jumps[new_mains]
1073 [new_params.sensorFps.baserate]
1074 [new_params.sensorFps.divisor];
1075 if (new_params.flickerControl.flickerMode)
1076 command_flags |= COMMAND_SETFLICKERCTRL;
1077 } else if (!retval && MATCH("60")) {
1078 new_mains = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001079 new_params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001080 flicker_jumps[new_mains]
1081 [new_params.sensorFps.baserate]
1082 [new_params.sensorFps.divisor];
1083 if (new_params.flickerControl.flickerMode)
1084 command_flags |= COMMAND_SETFLICKERCTRL;
1085 } else
1086 retval = -EINVAL;
1087 } else if (MATCH("allowable_overexposure")) {
1088 if (!retval && MATCH("auto")) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001089 new_params.flickerControl.allowableOverExposure =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090 -find_over_exposure(new_params.colourParams.brightness);
1091 if(new_params.flickerControl.flickerMode != 0)
1092 command_flags |= COMMAND_SETFLICKERCTRL;
1093 } else {
1094 if (!retval)
1095 val = VALUE;
1096
1097 if (!retval) {
1098 if (val <= 0xff) {
1099 new_params.flickerControl.
1100 allowableOverExposure = val;
1101 if(new_params.flickerControl.flickerMode != 0)
1102 command_flags |= COMMAND_SETFLICKERCTRL;
1103 } else
1104 retval = -EINVAL;
1105 }
1106 }
1107 } else if (MATCH("compression_mode")) {
1108 if (!retval && MATCH("none"))
1109 new_params.compression.mode =
1110 CPIA_COMPRESSION_NONE;
1111 else if (!retval && MATCH("auto"))
1112 new_params.compression.mode =
1113 CPIA_COMPRESSION_AUTO;
1114 else if (!retval && MATCH("manual"))
1115 new_params.compression.mode =
1116 CPIA_COMPRESSION_MANUAL;
1117 else
1118 retval = -EINVAL;
1119
1120 command_flags |= COMMAND_SETCOMPRESSION;
1121 } else if (MATCH("decimation_enable")) {
1122 if (!retval && MATCH("off"))
1123 new_params.compression.decimation = 0;
1124 else if (!retval && MATCH("on"))
1125 new_params.compression.decimation = 1;
1126 else
1127 retval = -EINVAL;
1128
1129 command_flags |= COMMAND_SETCOMPRESSION;
1130 } else if (MATCH("compression_target")) {
1131 if (!retval && MATCH("quality"))
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001132 new_params.compressionTarget.frTargeting =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133 CPIA_COMPRESSION_TARGET_QUALITY;
1134 else if (!retval && MATCH("framerate"))
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001135 new_params.compressionTarget.frTargeting =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136 CPIA_COMPRESSION_TARGET_FRAMERATE;
1137 else
1138 retval = -EINVAL;
1139
1140 command_flags |= COMMAND_SETCOMPRESSIONTARGET;
1141 } else if (MATCH("target_framerate")) {
1142 if (!retval)
1143 val = VALUE;
1144
1145 if (!retval) {
1146 if(val > 0 && val <= 30)
1147 new_params.compressionTarget.targetFR = val;
1148 else
1149 retval = -EINVAL;
1150 }
1151 command_flags |= COMMAND_SETCOMPRESSIONTARGET;
1152 } else if (MATCH("target_quality")) {
1153 if (!retval)
1154 val = VALUE;
1155
1156 if (!retval) {
1157 if(val > 0 && val <= 64)
1158 new_params.compressionTarget.targetQ = val;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001159 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160 retval = -EINVAL;
1161 }
1162 command_flags |= COMMAND_SETCOMPRESSIONTARGET;
1163 } else if (MATCH("y_threshold")) {
1164 if (!retval)
1165 val = VALUE;
1166
1167 if (!retval) {
1168 if (val < 32)
1169 new_params.yuvThreshold.yThreshold = val;
1170 else
1171 retval = -EINVAL;
1172 }
1173 command_flags |= COMMAND_SETYUVTHRESH;
1174 } else if (MATCH("uv_threshold")) {
1175 if (!retval)
1176 val = VALUE;
1177
1178 if (!retval) {
1179 if (val < 32)
1180 new_params.yuvThreshold.uvThreshold = val;
1181 else
1182 retval = -EINVAL;
1183 }
1184 command_flags |= COMMAND_SETYUVTHRESH;
1185 } else if (MATCH("hysteresis")) {
1186 if (!retval)
1187 val = VALUE;
1188
1189 if (!retval) {
1190 if (val <= 0xff)
1191 new_params.compressionParams.hysteresis = val;
1192 else
1193 retval = -EINVAL;
1194 }
1195 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1196 } else if (MATCH("threshold_max")) {
1197 if (!retval)
1198 val = VALUE;
1199
1200 if (!retval) {
1201 if (val <= 0xff)
1202 new_params.compressionParams.threshMax = val;
1203 else
1204 retval = -EINVAL;
1205 }
1206 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1207 } else if (MATCH("small_step")) {
1208 if (!retval)
1209 val = VALUE;
1210
1211 if (!retval) {
1212 if (val <= 0xff)
1213 new_params.compressionParams.smallStep = val;
1214 else
1215 retval = -EINVAL;
1216 }
1217 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1218 } else if (MATCH("large_step")) {
1219 if (!retval)
1220 val = VALUE;
1221
1222 if (!retval) {
1223 if (val <= 0xff)
1224 new_params.compressionParams.largeStep = val;
1225 else
1226 retval = -EINVAL;
1227 }
1228 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1229 } else if (MATCH("decimation_hysteresis")) {
1230 if (!retval)
1231 val = VALUE;
1232
1233 if (!retval) {
1234 if (val <= 0xff)
1235 new_params.compressionParams.decimationHysteresis = val;
1236 else
1237 retval = -EINVAL;
1238 }
1239 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1240 } else if (MATCH("fr_diff_step_thresh")) {
1241 if (!retval)
1242 val = VALUE;
1243
1244 if (!retval) {
1245 if (val <= 0xff)
1246 new_params.compressionParams.frDiffStepThresh = val;
1247 else
1248 retval = -EINVAL;
1249 }
1250 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1251 } else if (MATCH("q_diff_step_thresh")) {
1252 if (!retval)
1253 val = VALUE;
1254
1255 if (!retval) {
1256 if (val <= 0xff)
1257 new_params.compressionParams.qDiffStepThresh = val;
1258 else
1259 retval = -EINVAL;
1260 }
1261 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1262 } else if (MATCH("decimation_thresh_mod")) {
1263 if (!retval)
1264 val = VALUE;
1265
1266 if (!retval) {
1267 if (val <= 0xff)
1268 new_params.compressionParams.decimationThreshMod = val;
1269 else
1270 retval = -EINVAL;
1271 }
1272 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1273 } else if (MATCH("toplight")) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001274 if (!retval && MATCH("on"))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275 new_params.qx3.toplight = 1;
1276 else if (!retval && MATCH("off"))
1277 new_params.qx3.toplight = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001278 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279 retval = -EINVAL;
1280 command_flags |= COMMAND_SETLIGHTS;
1281 } else if (MATCH("bottomlight")) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001282 if (!retval && MATCH("on"))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001283 new_params.qx3.bottomlight = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001284 else if (!retval && MATCH("off"))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001285 new_params.qx3.bottomlight = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001286 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001287 retval = -EINVAL;
1288 command_flags |= COMMAND_SETLIGHTS;
1289 } else {
1290 DBG("No match found\n");
1291 retval = -EINVAL;
1292 }
1293
1294 if (!retval) {
1295 while (count && isspace(*buffer) && *buffer != '\n') {
1296 --count;
1297 ++buffer;
1298 }
1299 if (count) {
1300 if (*buffer == '\0' && count != 1)
1301 retval = -EINVAL;
1302 else if (*buffer != '\n' && *buffer != ';' &&
1303 *buffer != '\0')
1304 retval = -EINVAL;
1305 else {
1306 --count;
1307 ++buffer;
1308 }
1309 }
1310 }
1311 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001312#undef MATCH
Linus Torvalds1da177e2005-04-16 15:20:36 -07001313#undef VALUE
1314#undef FIRMWARE_VERSION
1315 if (!retval) {
1316 if (command_flags & COMMAND_SETCOLOURPARAMS) {
1317 /* Adjust cam->vp to reflect these changes */
1318 cam->vp.brightness =
1319 new_params.colourParams.brightness*65535/100;
1320 cam->vp.contrast =
1321 new_params.colourParams.contrast*65535/100;
1322 cam->vp.colour =
1323 new_params.colourParams.saturation*65535/100;
1324 }
1325 if((command_flags & COMMAND_SETEXPOSURE) &&
1326 new_params.exposure.expMode == 2)
1327 cam->exposure_status = EXPOSURE_NORMAL;
1328
1329 memcpy(&cam->params, &new_params, sizeof(struct cam_params));
1330 cam->mainsFreq = new_mains;
1331 cam->cmd_queue |= command_flags;
1332 retval = size;
1333 } else
1334 DBG("error: %d\n", retval);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001335
Ingo Molnar3593cab2006-02-07 06:49:14 -02001336 mutex_unlock(&cam->param_lock);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001337
Linus Torvalds1da177e2005-04-16 15:20:36 -07001338out:
1339 free_page((unsigned long)page);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001340 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001341}
1342
1343static void create_proc_cpia_cam(struct cam_data *cam)
1344{
Alexey Dobriyan13071f02007-01-14 15:29:42 -03001345 char name[5 + 1 + 10 + 1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346 struct proc_dir_entry *ent;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001347
Linus Torvalds1da177e2005-04-16 15:20:36 -07001348 if (!cpia_proc_root || !cam)
1349 return;
1350
Hans Verkuilc6330fb2008-10-19 18:54:26 -03001351 snprintf(name, sizeof(name), "video%d", cam->vdev.num);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001352
Linus Torvalds1da177e2005-04-16 15:20:36 -07001353 ent = create_proc_entry(name, S_IFREG|S_IRUGO|S_IWUSR, cpia_proc_root);
1354 if (!ent)
1355 return;
1356
1357 ent->data = cam;
1358 ent->read_proc = cpia_read_proc;
1359 ent->write_proc = cpia_write_proc;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001360 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361 size of the proc entry is 3736 bytes for the standard webcam;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001362 the extra features of the QX3 microscope add 189 bytes.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363 (we have not yet probed the camera to see which type it is).
1364 */
1365 ent->size = 3736 + 189;
1366 cam->proc_entry = ent;
1367}
1368
1369static void destroy_proc_cpia_cam(struct cam_data *cam)
1370{
Alexey Dobriyan13071f02007-01-14 15:29:42 -03001371 char name[5 + 1 + 10 + 1];
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001372
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373 if (!cam || !cam->proc_entry)
1374 return;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001375
Hans Verkuilc6330fb2008-10-19 18:54:26 -03001376 snprintf(name, sizeof(name), "video%d", cam->vdev.num);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377 remove_proc_entry(name, cpia_proc_root);
1378 cam->proc_entry = NULL;
1379}
1380
1381static void proc_cpia_create(void)
1382{
Al Viro66600222005-09-28 22:32:57 +01001383 cpia_proc_root = proc_mkdir("cpia", NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001384
Alexey Dobriyan99b76232009-03-25 22:48:06 +03001385 if (!cpia_proc_root)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001386 LOG("Unable to initialise /proc/cpia\n");
1387}
1388
1389static void __exit proc_cpia_destroy(void)
1390{
1391 remove_proc_entry("cpia", NULL);
1392}
1393#endif /* CONFIG_PROC_FS */
1394
1395/* ----------------------- debug functions ---------------------- */
1396
1397#define printstatus(cam) \
1398 DBG("%02x %02x %02x %02x %02x %02x %02x %02x\n",\
1399 cam->params.status.systemState, cam->params.status.grabState, \
1400 cam->params.status.streamState, cam->params.status.fatalError, \
1401 cam->params.status.cmdError, cam->params.status.debugFlags, \
1402 cam->params.status.vpStatus, cam->params.status.errorCode);
1403
1404/* ----------------------- v4l helpers -------------------------- */
1405
1406/* supported frame palettes and depths */
1407static inline int valid_mode(u16 palette, u16 depth)
1408{
1409 if ((palette == VIDEO_PALETTE_YUV422 && depth == 16) ||
1410 (palette == VIDEO_PALETTE_YUYV && depth == 16))
1411 return 1;
1412
1413 if (colorspace_conv)
1414 return (palette == VIDEO_PALETTE_GREY && depth == 8) ||
1415 (palette == VIDEO_PALETTE_RGB555 && depth == 16) ||
1416 (palette == VIDEO_PALETTE_RGB565 && depth == 16) ||
1417 (palette == VIDEO_PALETTE_RGB24 && depth == 24) ||
1418 (palette == VIDEO_PALETTE_RGB32 && depth == 32) ||
1419 (palette == VIDEO_PALETTE_UYVY && depth == 16);
1420
1421 return 0;
1422}
1423
1424static int match_videosize( int width, int height )
1425{
1426 /* return the best match, where 'best' is as always
1427 * the largest that is not bigger than what is requested. */
1428 if (width>=352 && height>=288)
1429 return VIDEOSIZE_352_288; /* CIF */
1430
1431 if (width>=320 && height>=240)
1432 return VIDEOSIZE_320_240; /* SIF */
1433
1434 if (width>=288 && height>=216)
1435 return VIDEOSIZE_288_216;
1436
1437 if (width>=256 && height>=192)
1438 return VIDEOSIZE_256_192;
1439
1440 if (width>=224 && height>=168)
1441 return VIDEOSIZE_224_168;
1442
1443 if (width>=192 && height>=144)
1444 return VIDEOSIZE_192_144;
1445
1446 if (width>=176 && height>=144)
1447 return VIDEOSIZE_176_144; /* QCIF */
1448
1449 if (width>=160 && height>=120)
1450 return VIDEOSIZE_160_120; /* QSIF */
1451
1452 if (width>=128 && height>=96)
1453 return VIDEOSIZE_128_96;
1454
1455 if (width>=88 && height>=72)
1456 return VIDEOSIZE_88_72;
1457
1458 if (width>=64 && height>=48)
1459 return VIDEOSIZE_64_48;
1460
1461 if (width>=48 && height>=48)
1462 return VIDEOSIZE_48_48;
1463
1464 return -1;
1465}
1466
1467/* these are the capture sizes we support */
1468static void set_vw_size(struct cam_data *cam)
1469{
1470 /* the col/row/start/end values are the result of simple math */
1471 /* study the SetROI-command in cpia developers guide p 2-22 */
1472 /* streamStartLine is set to the recommended value in the cpia */
1473 /* developers guide p 3-37 */
1474 switch(cam->video_size) {
1475 case VIDEOSIZE_CIF:
1476 cam->vw.width = 352;
1477 cam->vw.height = 288;
1478 cam->params.format.videoSize=VIDEOSIZE_CIF;
1479 cam->params.roi.colStart=0;
1480 cam->params.roi.rowStart=0;
1481 cam->params.streamStartLine = 120;
1482 break;
1483 case VIDEOSIZE_SIF:
1484 cam->vw.width = 320;
1485 cam->vw.height = 240;
1486 cam->params.format.videoSize=VIDEOSIZE_CIF;
1487 cam->params.roi.colStart=2;
1488 cam->params.roi.rowStart=6;
1489 cam->params.streamStartLine = 120;
1490 break;
1491 case VIDEOSIZE_288_216:
1492 cam->vw.width = 288;
1493 cam->vw.height = 216;
1494 cam->params.format.videoSize=VIDEOSIZE_CIF;
1495 cam->params.roi.colStart=4;
1496 cam->params.roi.rowStart=9;
1497 cam->params.streamStartLine = 120;
1498 break;
1499 case VIDEOSIZE_256_192:
1500 cam->vw.width = 256;
1501 cam->vw.height = 192;
1502 cam->params.format.videoSize=VIDEOSIZE_CIF;
1503 cam->params.roi.colStart=6;
1504 cam->params.roi.rowStart=12;
1505 cam->params.streamStartLine = 120;
1506 break;
1507 case VIDEOSIZE_224_168:
1508 cam->vw.width = 224;
1509 cam->vw.height = 168;
1510 cam->params.format.videoSize=VIDEOSIZE_CIF;
1511 cam->params.roi.colStart=8;
1512 cam->params.roi.rowStart=15;
1513 cam->params.streamStartLine = 120;
1514 break;
1515 case VIDEOSIZE_192_144:
1516 cam->vw.width = 192;
1517 cam->vw.height = 144;
1518 cam->params.format.videoSize=VIDEOSIZE_CIF;
1519 cam->params.roi.colStart=10;
1520 cam->params.roi.rowStart=18;
1521 cam->params.streamStartLine = 120;
1522 break;
1523 case VIDEOSIZE_QCIF:
1524 cam->vw.width = 176;
1525 cam->vw.height = 144;
1526 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1527 cam->params.roi.colStart=0;
1528 cam->params.roi.rowStart=0;
1529 cam->params.streamStartLine = 60;
1530 break;
1531 case VIDEOSIZE_QSIF:
1532 cam->vw.width = 160;
1533 cam->vw.height = 120;
1534 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1535 cam->params.roi.colStart=1;
1536 cam->params.roi.rowStart=3;
1537 cam->params.streamStartLine = 60;
1538 break;
1539 case VIDEOSIZE_128_96:
1540 cam->vw.width = 128;
1541 cam->vw.height = 96;
1542 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1543 cam->params.roi.colStart=3;
1544 cam->params.roi.rowStart=6;
1545 cam->params.streamStartLine = 60;
1546 break;
1547 case VIDEOSIZE_88_72:
1548 cam->vw.width = 88;
1549 cam->vw.height = 72;
1550 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1551 cam->params.roi.colStart=5;
1552 cam->params.roi.rowStart=9;
1553 cam->params.streamStartLine = 60;
1554 break;
1555 case VIDEOSIZE_64_48:
1556 cam->vw.width = 64;
1557 cam->vw.height = 48;
1558 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1559 cam->params.roi.colStart=7;
1560 cam->params.roi.rowStart=12;
1561 cam->params.streamStartLine = 60;
1562 break;
1563 case VIDEOSIZE_48_48:
1564 cam->vw.width = 48;
1565 cam->vw.height = 48;
1566 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1567 cam->params.roi.colStart=8;
1568 cam->params.roi.rowStart=6;
1569 cam->params.streamStartLine = 60;
1570 break;
1571 default:
1572 LOG("bad videosize value: %d\n", cam->video_size);
1573 return;
1574 }
1575
1576 if(cam->vc.width == 0)
1577 cam->vc.width = cam->vw.width;
1578 if(cam->vc.height == 0)
1579 cam->vc.height = cam->vw.height;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001580
Linus Torvalds1da177e2005-04-16 15:20:36 -07001581 cam->params.roi.colStart += cam->vc.x >> 3;
1582 cam->params.roi.colEnd = cam->params.roi.colStart +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001583 (cam->vc.width >> 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001584 cam->params.roi.rowStart += cam->vc.y >> 2;
1585 cam->params.roi.rowEnd = cam->params.roi.rowStart +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001586 (cam->vc.height >> 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001587
1588 return;
1589}
1590
1591static int allocate_frame_buf(struct cam_data *cam)
1592{
1593 int i;
1594
1595 cam->frame_buf = rvmalloc(FRAME_NUM * CPIA_MAX_FRAME_SIZE);
1596 if (!cam->frame_buf)
1597 return -ENOBUFS;
1598
1599 for (i = 0; i < FRAME_NUM; i++)
1600 cam->frame[i].data = cam->frame_buf + i * CPIA_MAX_FRAME_SIZE;
1601
1602 return 0;
1603}
1604
1605static int free_frame_buf(struct cam_data *cam)
1606{
1607 int i;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001608
Linus Torvalds1da177e2005-04-16 15:20:36 -07001609 rvfree(cam->frame_buf, FRAME_NUM*CPIA_MAX_FRAME_SIZE);
1610 cam->frame_buf = NULL;
1611 for (i=0; i < FRAME_NUM; i++)
1612 cam->frame[i].data = NULL;
1613
1614 return 0;
1615}
1616
1617
1618static inline void free_frames(struct cpia_frame frame[FRAME_NUM])
1619{
1620 int i;
1621
1622 for (i=0; i < FRAME_NUM; i++)
1623 frame[i].state = FRAME_UNUSED;
1624 return;
1625}
1626
1627/**********************************************************************
1628 *
1629 * General functions
1630 *
1631 **********************************************************************/
1632/* send an arbitrary command to the camera */
1633static int do_command(struct cam_data *cam, u16 command, u8 a, u8 b, u8 c, u8 d)
1634{
1635 int retval, datasize;
1636 u8 cmd[8], data[8];
1637
1638 switch(command) {
1639 case CPIA_COMMAND_GetCPIAVersion:
1640 case CPIA_COMMAND_GetPnPID:
1641 case CPIA_COMMAND_GetCameraStatus:
1642 case CPIA_COMMAND_GetVPVersion:
1643 datasize=8;
1644 break;
1645 case CPIA_COMMAND_GetColourParams:
1646 case CPIA_COMMAND_GetColourBalance:
1647 case CPIA_COMMAND_GetExposure:
Ingo Molnar3593cab2006-02-07 06:49:14 -02001648 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001649 datasize=8;
1650 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001651 case CPIA_COMMAND_ReadMCPorts:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001652 case CPIA_COMMAND_ReadVCRegs:
1653 datasize = 4;
1654 break;
1655 default:
1656 datasize=0;
1657 break;
1658 }
1659
1660 cmd[0] = command>>8;
1661 cmd[1] = command&0xff;
1662 cmd[2] = a;
1663 cmd[3] = b;
1664 cmd[4] = c;
1665 cmd[5] = d;
1666 cmd[6] = datasize;
1667 cmd[7] = 0;
1668
1669 retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
1670 if (retval) {
1671 DBG("%x - failed, retval=%d\n", command, retval);
1672 if (command == CPIA_COMMAND_GetColourParams ||
1673 command == CPIA_COMMAND_GetColourBalance ||
1674 command == CPIA_COMMAND_GetExposure)
Ingo Molnar3593cab2006-02-07 06:49:14 -02001675 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001676 } else {
1677 switch(command) {
1678 case CPIA_COMMAND_GetCPIAVersion:
1679 cam->params.version.firmwareVersion = data[0];
1680 cam->params.version.firmwareRevision = data[1];
1681 cam->params.version.vcVersion = data[2];
1682 cam->params.version.vcRevision = data[3];
1683 break;
1684 case CPIA_COMMAND_GetPnPID:
1685 cam->params.pnpID.vendor = data[0]+(((u16)data[1])<<8);
1686 cam->params.pnpID.product = data[2]+(((u16)data[3])<<8);
1687 cam->params.pnpID.deviceRevision =
1688 data[4]+(((u16)data[5])<<8);
1689 break;
1690 case CPIA_COMMAND_GetCameraStatus:
1691 cam->params.status.systemState = data[0];
1692 cam->params.status.grabState = data[1];
1693 cam->params.status.streamState = data[2];
1694 cam->params.status.fatalError = data[3];
1695 cam->params.status.cmdError = data[4];
1696 cam->params.status.debugFlags = data[5];
1697 cam->params.status.vpStatus = data[6];
1698 cam->params.status.errorCode = data[7];
1699 break;
1700 case CPIA_COMMAND_GetVPVersion:
1701 cam->params.vpVersion.vpVersion = data[0];
1702 cam->params.vpVersion.vpRevision = data[1];
1703 cam->params.vpVersion.cameraHeadID =
1704 data[2]+(((u16)data[3])<<8);
1705 break;
1706 case CPIA_COMMAND_GetColourParams:
1707 cam->params.colourParams.brightness = data[0];
1708 cam->params.colourParams.contrast = data[1];
1709 cam->params.colourParams.saturation = data[2];
Ingo Molnar3593cab2006-02-07 06:49:14 -02001710 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001711 break;
1712 case CPIA_COMMAND_GetColourBalance:
1713 cam->params.colourBalance.redGain = data[0];
1714 cam->params.colourBalance.greenGain = data[1];
1715 cam->params.colourBalance.blueGain = data[2];
Ingo Molnar3593cab2006-02-07 06:49:14 -02001716 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001717 break;
1718 case CPIA_COMMAND_GetExposure:
1719 cam->params.exposure.gain = data[0];
1720 cam->params.exposure.fineExp = data[1];
1721 cam->params.exposure.coarseExpLo = data[2];
1722 cam->params.exposure.coarseExpHi = data[3];
1723 cam->params.exposure.redComp = data[4];
1724 cam->params.exposure.green1Comp = data[5];
1725 cam->params.exposure.green2Comp = data[6];
1726 cam->params.exposure.blueComp = data[7];
Ingo Molnar3593cab2006-02-07 06:49:14 -02001727 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001728 break;
1729
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001730 case CPIA_COMMAND_ReadMCPorts:
1731 if (!cam->params.qx3.qx3_detected)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001732 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001733 /* test button press */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001734 cam->params.qx3.button = ((data[1] & 0x02) == 0);
1735 if (cam->params.qx3.button) {
1736 /* button pressed - unlock the latch */
1737 do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xDF,0xDF,0);
1738 do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xFF,0xFF,0);
1739 }
1740
1741 /* test whether microscope is cradled */
1742 cam->params.qx3.cradled = ((data[2] & 0x40) == 0);
1743 break;
1744
1745 default:
1746 break;
1747 }
1748 }
1749 return retval;
1750}
1751
1752/* send a command to the camera with an additional data transaction */
1753static int do_command_extended(struct cam_data *cam, u16 command,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001754 u8 a, u8 b, u8 c, u8 d,
1755 u8 e, u8 f, u8 g, u8 h,
1756 u8 i, u8 j, u8 k, u8 l)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001757{
1758 int retval;
1759 u8 cmd[8], data[8];
1760
1761 cmd[0] = command>>8;
1762 cmd[1] = command&0xff;
1763 cmd[2] = a;
1764 cmd[3] = b;
1765 cmd[4] = c;
1766 cmd[5] = d;
1767 cmd[6] = 8;
1768 cmd[7] = 0;
1769 data[0] = e;
1770 data[1] = f;
1771 data[2] = g;
1772 data[3] = h;
1773 data[4] = i;
1774 data[5] = j;
1775 data[6] = k;
1776 data[7] = l;
1777
1778 retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
1779 if (retval)
1780 DBG("%x - failed\n", command);
1781
1782 return retval;
1783}
1784
1785/**********************************************************************
1786 *
1787 * Colorspace conversion
1788 *
1789 **********************************************************************/
1790#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16)
1791
1792static int convert420(unsigned char *yuv, unsigned char *rgb, int out_fmt,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001793 int linesize, int mmap_kludge)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001794{
1795 int y, u, v, r, g, b, y1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001796
Linus Torvalds1da177e2005-04-16 15:20:36 -07001797 /* Odd lines use the same u and v as the previous line.
1798 * Because of compression, it is necessary to get this
1799 * information from the decoded image. */
1800 switch(out_fmt) {
1801 case VIDEO_PALETTE_RGB555:
1802 y = (*yuv++ - 16) * 76310;
1803 y1 = (*yuv - 16) * 76310;
1804 r = ((*(rgb+1-linesize)) & 0x7c) << 1;
1805 g = ((*(rgb-linesize)) & 0xe0) >> 4 |
1806 ((*(rgb+1-linesize)) & 0x03) << 6;
1807 b = ((*(rgb-linesize)) & 0x1f) << 3;
1808 u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
1809 v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
1810 r = 104635 * v;
1811 g = -25690 * u - 53294 * v;
1812 b = 132278 * u;
1813 *rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3);
1814 *rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6);
1815 *rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3);
1816 *rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6);
1817 return 4;
1818 case VIDEO_PALETTE_RGB565:
1819 y = (*yuv++ - 16) * 76310;
1820 y1 = (*yuv - 16) * 76310;
1821 r = (*(rgb+1-linesize)) & 0xf8;
1822 g = ((*(rgb-linesize)) & 0xe0) >> 3 |
1823 ((*(rgb+1-linesize)) & 0x07) << 5;
1824 b = ((*(rgb-linesize)) & 0x1f) << 3;
1825 u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
1826 v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
1827 r = 104635 * v;
1828 g = -25690 * u - 53294 * v;
1829 b = 132278 * u;
1830 *rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3);
1831 *rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5);
1832 *rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3);
1833 *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5);
1834 return 4;
1835 break;
1836 case VIDEO_PALETTE_RGB24:
1837 case VIDEO_PALETTE_RGB32:
1838 y = (*yuv++ - 16) * 76310;
1839 y1 = (*yuv - 16) * 76310;
1840 if (mmap_kludge) {
1841 r = *(rgb+2-linesize);
1842 g = *(rgb+1-linesize);
1843 b = *(rgb-linesize);
1844 } else {
1845 r = *(rgb-linesize);
1846 g = *(rgb+1-linesize);
1847 b = *(rgb+2-linesize);
1848 }
1849 u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
1850 v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
1851 r = 104635 * v;
1852 g = -25690 * u + -53294 * v;
1853 b = 132278 * u;
1854 if (mmap_kludge) {
1855 *rgb++ = LIMIT(b+y);
1856 *rgb++ = LIMIT(g+y);
1857 *rgb++ = LIMIT(r+y);
1858 if(out_fmt == VIDEO_PALETTE_RGB32)
1859 rgb++;
1860 *rgb++ = LIMIT(b+y1);
1861 *rgb++ = LIMIT(g+y1);
1862 *rgb = LIMIT(r+y1);
1863 } else {
1864 *rgb++ = LIMIT(r+y);
1865 *rgb++ = LIMIT(g+y);
1866 *rgb++ = LIMIT(b+y);
1867 if(out_fmt == VIDEO_PALETTE_RGB32)
1868 rgb++;
1869 *rgb++ = LIMIT(r+y1);
1870 *rgb++ = LIMIT(g+y1);
1871 *rgb = LIMIT(b+y1);
1872 }
1873 if(out_fmt == VIDEO_PALETTE_RGB32)
1874 return 8;
1875 return 6;
1876 case VIDEO_PALETTE_YUV422:
1877 case VIDEO_PALETTE_YUYV:
1878 y = *yuv++;
1879 u = *(rgb+1-linesize);
1880 y1 = *yuv;
1881 v = *(rgb+3-linesize);
1882 *rgb++ = y;
1883 *rgb++ = u;
1884 *rgb++ = y1;
1885 *rgb = v;
1886 return 4;
1887 case VIDEO_PALETTE_UYVY:
1888 u = *(rgb-linesize);
1889 y = *yuv++;
1890 v = *(rgb+2-linesize);
1891 y1 = *yuv;
1892 *rgb++ = u;
1893 *rgb++ = y;
1894 *rgb++ = v;
1895 *rgb = y1;
1896 return 4;
1897 case VIDEO_PALETTE_GREY:
1898 *rgb++ = *yuv++;
1899 *rgb = *yuv;
1900 return 2;
1901 default:
1902 DBG("Empty: %d\n", out_fmt);
1903 return 0;
1904 }
1905}
1906
1907
1908static int yuvconvert(unsigned char *yuv, unsigned char *rgb, int out_fmt,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001909 int in_uyvy, int mmap_kludge)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001910{
1911 int y, u, v, r, g, b, y1;
1912
1913 switch(out_fmt) {
1914 case VIDEO_PALETTE_RGB555:
1915 case VIDEO_PALETTE_RGB565:
1916 case VIDEO_PALETTE_RGB24:
1917 case VIDEO_PALETTE_RGB32:
1918 if (in_uyvy) {
1919 u = *yuv++ - 128;
1920 y = (*yuv++ - 16) * 76310;
1921 v = *yuv++ - 128;
1922 y1 = (*yuv - 16) * 76310;
1923 } else {
1924 y = (*yuv++ - 16) * 76310;
1925 u = *yuv++ - 128;
1926 y1 = (*yuv++ - 16) * 76310;
1927 v = *yuv - 128;
1928 }
1929 r = 104635 * v;
1930 g = -25690 * u + -53294 * v;
1931 b = 132278 * u;
1932 break;
1933 default:
1934 y = *yuv++;
1935 u = *yuv++;
1936 y1 = *yuv++;
1937 v = *yuv;
1938 /* Just to avoid compiler warnings */
1939 r = 0;
1940 g = 0;
1941 b = 0;
1942 break;
1943 }
1944 switch(out_fmt) {
1945 case VIDEO_PALETTE_RGB555:
1946 *rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3);
1947 *rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6);
1948 *rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3);
1949 *rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6);
1950 return 4;
1951 case VIDEO_PALETTE_RGB565:
1952 *rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3);
1953 *rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5);
1954 *rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3);
1955 *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5);
1956 return 4;
1957 case VIDEO_PALETTE_RGB24:
1958 if (mmap_kludge) {
1959 *rgb++ = LIMIT(b+y);
1960 *rgb++ = LIMIT(g+y);
1961 *rgb++ = LIMIT(r+y);
1962 *rgb++ = LIMIT(b+y1);
1963 *rgb++ = LIMIT(g+y1);
1964 *rgb = LIMIT(r+y1);
1965 } else {
1966 *rgb++ = LIMIT(r+y);
1967 *rgb++ = LIMIT(g+y);
1968 *rgb++ = LIMIT(b+y);
1969 *rgb++ = LIMIT(r+y1);
1970 *rgb++ = LIMIT(g+y1);
1971 *rgb = LIMIT(b+y1);
1972 }
1973 return 6;
1974 case VIDEO_PALETTE_RGB32:
1975 if (mmap_kludge) {
1976 *rgb++ = LIMIT(b+y);
1977 *rgb++ = LIMIT(g+y);
1978 *rgb++ = LIMIT(r+y);
1979 rgb++;
1980 *rgb++ = LIMIT(b+y1);
1981 *rgb++ = LIMIT(g+y1);
1982 *rgb = LIMIT(r+y1);
1983 } else {
1984 *rgb++ = LIMIT(r+y);
1985 *rgb++ = LIMIT(g+y);
1986 *rgb++ = LIMIT(b+y);
1987 rgb++;
1988 *rgb++ = LIMIT(r+y1);
1989 *rgb++ = LIMIT(g+y1);
1990 *rgb = LIMIT(b+y1);
1991 }
1992 return 8;
1993 case VIDEO_PALETTE_GREY:
1994 *rgb++ = y;
1995 *rgb = y1;
1996 return 2;
1997 case VIDEO_PALETTE_YUV422:
1998 case VIDEO_PALETTE_YUYV:
1999 *rgb++ = y;
2000 *rgb++ = u;
2001 *rgb++ = y1;
2002 *rgb = v;
2003 return 4;
2004 case VIDEO_PALETTE_UYVY:
2005 *rgb++ = u;
2006 *rgb++ = y;
2007 *rgb++ = v;
2008 *rgb = y1;
2009 return 4;
2010 default:
2011 DBG("Empty: %d\n", out_fmt);
2012 return 0;
2013 }
2014}
2015
2016static int skipcount(int count, int fmt)
2017{
2018 switch(fmt) {
2019 case VIDEO_PALETTE_GREY:
2020 return count;
2021 case VIDEO_PALETTE_RGB555:
2022 case VIDEO_PALETTE_RGB565:
2023 case VIDEO_PALETTE_YUV422:
2024 case VIDEO_PALETTE_YUYV:
2025 case VIDEO_PALETTE_UYVY:
2026 return 2*count;
2027 case VIDEO_PALETTE_RGB24:
2028 return 3*count;
2029 case VIDEO_PALETTE_RGB32:
2030 return 4*count;
2031 default:
2032 return 0;
2033 }
2034}
2035
2036static int parse_picture(struct cam_data *cam, int size)
2037{
2038 u8 *obuf, *ibuf, *end_obuf;
2039 int ll, in_uyvy, compressed, decimation, even_line, origsize, out_fmt;
2040 int rows, cols, linesize, subsample_422;
2041
2042 /* make sure params don't change while we are decoding */
Ingo Molnar3593cab2006-02-07 06:49:14 -02002043 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002044
2045 obuf = cam->decompressed_frame.data;
2046 end_obuf = obuf+CPIA_MAX_FRAME_SIZE;
2047 ibuf = cam->raw_image;
2048 origsize = size;
2049 out_fmt = cam->vp.palette;
2050
2051 if ((ibuf[0] != MAGIC_0) || (ibuf[1] != MAGIC_1)) {
2052 LOG("header not found\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02002053 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002054 return -1;
2055 }
2056
2057 if ((ibuf[16] != VIDEOSIZE_QCIF) && (ibuf[16] != VIDEOSIZE_CIF)) {
2058 LOG("wrong video size\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02002059 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002060 return -1;
2061 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002062
Linus Torvalds1da177e2005-04-16 15:20:36 -07002063 if (ibuf[17] != SUBSAMPLE_420 && ibuf[17] != SUBSAMPLE_422) {
2064 LOG("illegal subtype %d\n",ibuf[17]);
Ingo Molnar3593cab2006-02-07 06:49:14 -02002065 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002066 return -1;
2067 }
2068 subsample_422 = ibuf[17] == SUBSAMPLE_422;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002069
Linus Torvalds1da177e2005-04-16 15:20:36 -07002070 if (ibuf[18] != YUVORDER_YUYV && ibuf[18] != YUVORDER_UYVY) {
2071 LOG("illegal yuvorder %d\n",ibuf[18]);
Ingo Molnar3593cab2006-02-07 06:49:14 -02002072 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002073 return -1;
2074 }
2075 in_uyvy = ibuf[18] == YUVORDER_UYVY;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002076
Linus Torvalds1da177e2005-04-16 15:20:36 -07002077 if ((ibuf[24] != cam->params.roi.colStart) ||
2078 (ibuf[25] != cam->params.roi.colEnd) ||
2079 (ibuf[26] != cam->params.roi.rowStart) ||
2080 (ibuf[27] != cam->params.roi.rowEnd)) {
2081 LOG("ROI mismatch\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02002082 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002083 return -1;
2084 }
2085 cols = 8*(ibuf[25] - ibuf[24]);
2086 rows = 4*(ibuf[27] - ibuf[26]);
2087
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002088
Linus Torvalds1da177e2005-04-16 15:20:36 -07002089 if ((ibuf[28] != NOT_COMPRESSED) && (ibuf[28] != COMPRESSED)) {
2090 LOG("illegal compression %d\n",ibuf[28]);
Ingo Molnar3593cab2006-02-07 06:49:14 -02002091 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002092 return -1;
2093 }
2094 compressed = (ibuf[28] == COMPRESSED);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002095
Linus Torvalds1da177e2005-04-16 15:20:36 -07002096 if (ibuf[29] != NO_DECIMATION && ibuf[29] != DECIMATION_ENAB) {
2097 LOG("illegal decimation %d\n",ibuf[29]);
Ingo Molnar3593cab2006-02-07 06:49:14 -02002098 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002099 return -1;
2100 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002101 decimation = (ibuf[29] == DECIMATION_ENAB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002102
2103 cam->params.yuvThreshold.yThreshold = ibuf[30];
2104 cam->params.yuvThreshold.uvThreshold = ibuf[31];
2105 cam->params.status.systemState = ibuf[32];
2106 cam->params.status.grabState = ibuf[33];
2107 cam->params.status.streamState = ibuf[34];
2108 cam->params.status.fatalError = ibuf[35];
2109 cam->params.status.cmdError = ibuf[36];
2110 cam->params.status.debugFlags = ibuf[37];
2111 cam->params.status.vpStatus = ibuf[38];
2112 cam->params.status.errorCode = ibuf[39];
2113 cam->fps = ibuf[41];
Ingo Molnar3593cab2006-02-07 06:49:14 -02002114 mutex_unlock(&cam->param_lock);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002115
Linus Torvalds1da177e2005-04-16 15:20:36 -07002116 linesize = skipcount(cols, out_fmt);
2117 ibuf += FRAME_HEADER_SIZE;
2118 size -= FRAME_HEADER_SIZE;
2119 ll = ibuf[0] | (ibuf[1] << 8);
2120 ibuf += 2;
2121 even_line = 1;
2122
2123 while (size > 0) {
2124 size -= (ll+2);
2125 if (size < 0) {
2126 LOG("Insufficient data in buffer\n");
2127 return -1;
2128 }
2129
2130 while (ll > 1) {
2131 if (!compressed || (compressed && !(*ibuf & 1))) {
2132 if(subsample_422 || even_line) {
2133 obuf += yuvconvert(ibuf, obuf, out_fmt,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002134 in_uyvy, cam->mmap_kludge);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002135 ibuf += 4;
2136 ll -= 4;
2137 } else {
2138 /* SUBSAMPLE_420 on an odd line */
2139 obuf += convert420(ibuf, obuf,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002140 out_fmt, linesize,
2141 cam->mmap_kludge);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002142 ibuf += 2;
2143 ll -= 2;
2144 }
2145 } else {
2146 /*skip compressed interval from previous frame*/
2147 obuf += skipcount(*ibuf >> 1, out_fmt);
2148 if (obuf > end_obuf) {
2149 LOG("Insufficient buffer size\n");
2150 return -1;
2151 }
2152 ++ibuf;
2153 ll--;
2154 }
2155 }
2156 if (ll == 1) {
2157 if (*ibuf != EOL) {
2158 DBG("EOL not found giving up after %d/%d"
2159 " bytes\n", origsize-size, origsize);
2160 return -1;
2161 }
2162
2163 ++ibuf; /* skip over EOL */
2164
2165 if ((size > 3) && (ibuf[0] == EOI) && (ibuf[1] == EOI) &&
2166 (ibuf[2] == EOI) && (ibuf[3] == EOI)) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002167 size -= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002168 break;
2169 }
2170
2171 if(decimation) {
2172 /* skip the odd lines for now */
2173 obuf += linesize;
2174 }
2175
2176 if (size > 1) {
2177 ll = ibuf[0] | (ibuf[1] << 8);
2178 ibuf += 2; /* skip over line length */
2179 }
2180 if(!decimation)
2181 even_line = !even_line;
2182 } else {
2183 LOG("line length was not 1 but %d after %d/%d bytes\n",
2184 ll, origsize-size, origsize);
2185 return -1;
2186 }
2187 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002188
Linus Torvalds1da177e2005-04-16 15:20:36 -07002189 if(decimation) {
2190 /* interpolate odd rows */
2191 int i, j;
2192 u8 *prev, *next;
2193 prev = cam->decompressed_frame.data;
2194 obuf = prev+linesize;
2195 next = obuf+linesize;
2196 for(i=1; i<rows-1; i+=2) {
2197 for(j=0; j<linesize; ++j) {
2198 *obuf++ = ((int)*prev++ + *next++) / 2;
2199 }
2200 prev += linesize;
2201 obuf += linesize;
2202 next += linesize;
2203 }
2204 /* last row is odd, just copy previous row */
2205 memcpy(obuf, prev, linesize);
2206 }
2207
2208 cam->decompressed_frame.count = obuf-cam->decompressed_frame.data;
2209
2210 return cam->decompressed_frame.count;
2211}
2212
2213/* InitStreamCap wrapper to select correct start line */
2214static inline int init_stream_cap(struct cam_data *cam)
2215{
2216 return do_command(cam, CPIA_COMMAND_InitStreamCap,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002217 0, cam->params.streamStartLine, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002218}
2219
2220
2221/* find_over_exposure
2222 * Finds a suitable value of OverExposure for use with SetFlickerCtrl
2223 * Some calculation is required because this value changes with the brightness
2224 * set with SetColourParameters
2225 *
2226 * Parameters: Brightness - last brightness value set with SetColourParameters
2227 *
2228 * Returns: OverExposure value to use with SetFlickerCtrl
2229 */
2230#define FLICKER_MAX_EXPOSURE 250
2231#define FLICKER_ALLOWABLE_OVER_EXPOSURE 146
2232#define FLICKER_BRIGHTNESS_CONSTANT 59
2233static int find_over_exposure(int brightness)
2234{
2235 int MaxAllowableOverExposure, OverExposure;
2236
2237 MaxAllowableOverExposure = FLICKER_MAX_EXPOSURE - brightness -
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002238 FLICKER_BRIGHTNESS_CONSTANT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002239
2240 if (MaxAllowableOverExposure < FLICKER_ALLOWABLE_OVER_EXPOSURE) {
2241 OverExposure = MaxAllowableOverExposure;
2242 } else {
2243 OverExposure = FLICKER_ALLOWABLE_OVER_EXPOSURE;
2244 }
2245
2246 return OverExposure;
2247}
2248#undef FLICKER_MAX_EXPOSURE
2249#undef FLICKER_ALLOWABLE_OVER_EXPOSURE
2250#undef FLICKER_BRIGHTNESS_CONSTANT
2251
2252/* update various camera modes and settings */
2253static void dispatch_commands(struct cam_data *cam)
2254{
Ingo Molnar3593cab2006-02-07 06:49:14 -02002255 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002256 if (cam->cmd_queue==COMMAND_NONE) {
Ingo Molnar3593cab2006-02-07 06:49:14 -02002257 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002258 return;
2259 }
2260 DEB_BYTE(cam->cmd_queue);
2261 DEB_BYTE(cam->cmd_queue>>8);
2262 if (cam->cmd_queue & COMMAND_SETFORMAT) {
2263 do_command(cam, CPIA_COMMAND_SetFormat,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002264 cam->params.format.videoSize,
2265 cam->params.format.subSample,
2266 cam->params.format.yuvOrder, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002267 do_command(cam, CPIA_COMMAND_SetROI,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002268 cam->params.roi.colStart, cam->params.roi.colEnd,
2269 cam->params.roi.rowStart, cam->params.roi.rowEnd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002270 cam->first_frame = 1;
2271 }
2272
2273 if (cam->cmd_queue & COMMAND_SETCOLOURPARAMS)
2274 do_command(cam, CPIA_COMMAND_SetColourParams,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002275 cam->params.colourParams.brightness,
2276 cam->params.colourParams.contrast,
2277 cam->params.colourParams.saturation, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002278
2279 if (cam->cmd_queue & COMMAND_SETAPCOR)
2280 do_command(cam, CPIA_COMMAND_SetApcor,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002281 cam->params.apcor.gain1,
2282 cam->params.apcor.gain2,
2283 cam->params.apcor.gain4,
2284 cam->params.apcor.gain8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002285
2286 if (cam->cmd_queue & COMMAND_SETVLOFFSET)
2287 do_command(cam, CPIA_COMMAND_SetVLOffset,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002288 cam->params.vlOffset.gain1,
2289 cam->params.vlOffset.gain2,
2290 cam->params.vlOffset.gain4,
2291 cam->params.vlOffset.gain8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002292
2293 if (cam->cmd_queue & COMMAND_SETEXPOSURE) {
2294 do_command_extended(cam, CPIA_COMMAND_SetExposure,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002295 cam->params.exposure.gainMode,
2296 1,
2297 cam->params.exposure.compMode,
2298 cam->params.exposure.centreWeight,
2299 cam->params.exposure.gain,
2300 cam->params.exposure.fineExp,
2301 cam->params.exposure.coarseExpLo,
2302 cam->params.exposure.coarseExpHi,
2303 cam->params.exposure.redComp,
2304 cam->params.exposure.green1Comp,
2305 cam->params.exposure.green2Comp,
2306 cam->params.exposure.blueComp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002307 if(cam->params.exposure.expMode != 1) {
2308 do_command_extended(cam, CPIA_COMMAND_SetExposure,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002309 0,
2310 cam->params.exposure.expMode,
2311 0, 0,
2312 cam->params.exposure.gain,
2313 cam->params.exposure.fineExp,
2314 cam->params.exposure.coarseExpLo,
2315 cam->params.exposure.coarseExpHi,
2316 0, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002317 }
2318 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002319
Linus Torvalds1da177e2005-04-16 15:20:36 -07002320 if (cam->cmd_queue & COMMAND_SETCOLOURBALANCE) {
2321 if (cam->params.colourBalance.balanceMode == 1) {
2322 do_command(cam, CPIA_COMMAND_SetColourBalance,
2323 1,
2324 cam->params.colourBalance.redGain,
2325 cam->params.colourBalance.greenGain,
2326 cam->params.colourBalance.blueGain);
2327 do_command(cam, CPIA_COMMAND_SetColourBalance,
2328 3, 0, 0, 0);
2329 }
2330 if (cam->params.colourBalance.balanceMode == 2) {
2331 do_command(cam, CPIA_COMMAND_SetColourBalance,
2332 2, 0, 0, 0);
2333 }
2334 if (cam->params.colourBalance.balanceMode == 3) {
2335 do_command(cam, CPIA_COMMAND_SetColourBalance,
2336 3, 0, 0, 0);
2337 }
2338 }
2339
2340 if (cam->cmd_queue & COMMAND_SETCOMPRESSIONTARGET)
2341 do_command(cam, CPIA_COMMAND_SetCompressionTarget,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002342 cam->params.compressionTarget.frTargeting,
2343 cam->params.compressionTarget.targetFR,
2344 cam->params.compressionTarget.targetQ, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002345
2346 if (cam->cmd_queue & COMMAND_SETYUVTHRESH)
2347 do_command(cam, CPIA_COMMAND_SetYUVThresh,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002348 cam->params.yuvThreshold.yThreshold,
2349 cam->params.yuvThreshold.uvThreshold, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002350
2351 if (cam->cmd_queue & COMMAND_SETCOMPRESSIONPARAMS)
2352 do_command_extended(cam, CPIA_COMMAND_SetCompressionParams,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002353 0, 0, 0, 0,
2354 cam->params.compressionParams.hysteresis,
2355 cam->params.compressionParams.threshMax,
2356 cam->params.compressionParams.smallStep,
2357 cam->params.compressionParams.largeStep,
2358 cam->params.compressionParams.decimationHysteresis,
2359 cam->params.compressionParams.frDiffStepThresh,
2360 cam->params.compressionParams.qDiffStepThresh,
2361 cam->params.compressionParams.decimationThreshMod);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002362
2363 if (cam->cmd_queue & COMMAND_SETCOMPRESSION)
2364 do_command(cam, CPIA_COMMAND_SetCompression,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002365 cam->params.compression.mode,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002366 cam->params.compression.decimation, 0, 0);
2367
2368 if (cam->cmd_queue & COMMAND_SETSENSORFPS)
2369 do_command(cam, CPIA_COMMAND_SetSensorFPS,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002370 cam->params.sensorFps.divisor,
2371 cam->params.sensorFps.baserate, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002372
2373 if (cam->cmd_queue & COMMAND_SETFLICKERCTRL)
2374 do_command(cam, CPIA_COMMAND_SetFlickerCtrl,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002375 cam->params.flickerControl.flickerMode,
2376 cam->params.flickerControl.coarseJump,
2377 abs(cam->params.flickerControl.allowableOverExposure),
2378 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002379
2380 if (cam->cmd_queue & COMMAND_SETECPTIMING)
2381 do_command(cam, CPIA_COMMAND_SetECPTiming,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002382 cam->params.ecpTiming, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002383
2384 if (cam->cmd_queue & COMMAND_PAUSE)
2385 do_command(cam, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0);
2386
2387 if (cam->cmd_queue & COMMAND_RESUME)
2388 init_stream_cap(cam);
2389
2390 if (cam->cmd_queue & COMMAND_SETLIGHTS && cam->params.qx3.qx3_detected)
2391 {
2392 int p1 = (cam->params.qx3.bottomlight == 0) << 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002393 int p2 = (cam->params.qx3.toplight == 0) << 3;
2394 do_command(cam, CPIA_COMMAND_WriteVCReg, 0x90, 0x8F, 0x50, 0);
2395 do_command(cam, CPIA_COMMAND_WriteMCPort, 2, 0, (p1|p2|0xE0), 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002396 }
2397
2398 cam->cmd_queue = COMMAND_NONE;
Ingo Molnar3593cab2006-02-07 06:49:14 -02002399 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002400 return;
2401}
2402
2403
2404
2405static void set_flicker(struct cam_params *params, volatile u32 *command_flags,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002406 int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002407{
2408 /* Everything in here is from the Windows driver */
2409#define FIRMWARE_VERSION(x,y) (params->version.firmwareVersion == (x) && \
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002410 params->version.firmwareRevision == (y))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002411/* define for compgain calculation */
2412#if 0
2413#define COMPGAIN(base, curexp, newexp) \
2414 (u8) ((((float) base - 128.0) * ((float) curexp / (float) newexp)) + 128.5)
2415#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
2416 (u16)((float)curexp * (float)(u8)(curcomp + 128) / (float)(u8)(basecomp - 128))
2417#else
2418 /* equivalent functions without floating point math */
2419#define COMPGAIN(base, curexp, newexp) \
2420 (u8)(128 + (((u32)(2*(base-128)*curexp + newexp)) / (2* newexp)) )
2421#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
2422 (u16)(((u32)(curexp * (u8)(curcomp + 128)) / (u8)(basecomp - 128)))
2423#endif
2424
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002425
Linus Torvalds1da177e2005-04-16 15:20:36 -07002426 int currentexp = params->exposure.coarseExpLo +
2427 params->exposure.coarseExpHi*256;
2428 int startexp;
2429 if (on) {
2430 int cj = params->flickerControl.coarseJump;
2431 params->flickerControl.flickerMode = 1;
2432 params->flickerControl.disabled = 0;
2433 if(params->exposure.expMode != 2)
2434 *command_flags |= COMMAND_SETEXPOSURE;
2435 params->exposure.expMode = 2;
2436 currentexp = currentexp << params->exposure.gain;
2437 params->exposure.gain = 0;
2438 /* round down current exposure to nearest value */
2439 startexp = (currentexp + ROUND_UP_EXP_FOR_FLICKER) / cj;
2440 if(startexp < 1)
2441 startexp = 1;
2442 startexp = (startexp * cj) - 1;
2443 if(FIRMWARE_VERSION(1,2))
2444 while(startexp > MAX_EXP_102)
2445 startexp -= cj;
2446 else
2447 while(startexp > MAX_EXP)
2448 startexp -= cj;
2449 params->exposure.coarseExpLo = startexp & 0xff;
2450 params->exposure.coarseExpHi = startexp >> 8;
2451 if (currentexp > startexp) {
2452 if (currentexp > (2 * startexp))
2453 currentexp = 2 * startexp;
2454 params->exposure.redComp = COMPGAIN (COMP_RED, currentexp, startexp);
2455 params->exposure.green1Comp = COMPGAIN (COMP_GREEN1, currentexp, startexp);
2456 params->exposure.green2Comp = COMPGAIN (COMP_GREEN2, currentexp, startexp);
2457 params->exposure.blueComp = COMPGAIN (COMP_BLUE, currentexp, startexp);
2458 } else {
2459 params->exposure.redComp = COMP_RED;
2460 params->exposure.green1Comp = COMP_GREEN1;
2461 params->exposure.green2Comp = COMP_GREEN2;
2462 params->exposure.blueComp = COMP_BLUE;
2463 }
2464 if(FIRMWARE_VERSION(1,2))
2465 params->exposure.compMode = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002466 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07002467 params->exposure.compMode = 1;
2468
2469 params->apcor.gain1 = 0x18;
2470 params->apcor.gain2 = 0x18;
2471 params->apcor.gain4 = 0x16;
2472 params->apcor.gain8 = 0x14;
2473 *command_flags |= COMMAND_SETAPCOR;
2474 } else {
2475 params->flickerControl.flickerMode = 0;
2476 params->flickerControl.disabled = 1;
2477 /* Coarse = average of equivalent coarse for each comp channel */
2478 startexp = EXP_FROM_COMP(COMP_RED, params->exposure.redComp, currentexp);
2479 startexp += EXP_FROM_COMP(COMP_GREEN1, params->exposure.green1Comp, currentexp);
2480 startexp += EXP_FROM_COMP(COMP_GREEN2, params->exposure.green2Comp, currentexp);
2481 startexp += EXP_FROM_COMP(COMP_BLUE, params->exposure.blueComp, currentexp);
2482 startexp = startexp >> 2;
2483 while(startexp > MAX_EXP &&
2484 params->exposure.gain < params->exposure.gainMode-1) {
2485 startexp = startexp >> 1;
2486 ++params->exposure.gain;
2487 }
2488 if(FIRMWARE_VERSION(1,2) && startexp > MAX_EXP_102)
2489 startexp = MAX_EXP_102;
2490 if(startexp > MAX_EXP)
2491 startexp = MAX_EXP;
2492 params->exposure.coarseExpLo = startexp&0xff;
2493 params->exposure.coarseExpHi = startexp >> 8;
2494 params->exposure.redComp = COMP_RED;
2495 params->exposure.green1Comp = COMP_GREEN1;
2496 params->exposure.green2Comp = COMP_GREEN2;
2497 params->exposure.blueComp = COMP_BLUE;
2498 params->exposure.compMode = 1;
2499 *command_flags |= COMMAND_SETEXPOSURE;
2500 params->apcor.gain1 = 0x18;
2501 params->apcor.gain2 = 0x16;
2502 params->apcor.gain4 = 0x24;
2503 params->apcor.gain8 = 0x34;
2504 *command_flags |= COMMAND_SETAPCOR;
2505 }
2506 params->vlOffset.gain1 = 20;
2507 params->vlOffset.gain2 = 24;
2508 params->vlOffset.gain4 = 26;
2509 params->vlOffset.gain8 = 26;
2510 *command_flags |= COMMAND_SETVLOFFSET;
2511#undef FIRMWARE_VERSION
2512#undef EXP_FROM_COMP
2513#undef COMPGAIN
2514}
2515
2516#define FIRMWARE_VERSION(x,y) (cam->params.version.firmwareVersion == (x) && \
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002517 cam->params.version.firmwareRevision == (y))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002518/* monitor the exposure and adjust the sensor frame rate if needed */
2519static void monitor_exposure(struct cam_data *cam)
2520{
2521 u8 exp_acc, bcomp, gain, coarseL, cmd[8], data[8];
2522 int retval, light_exp, dark_exp, very_dark_exp;
2523 int old_exposure, new_exposure, framerate;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002524
Linus Torvalds1da177e2005-04-16 15:20:36 -07002525 /* get necessary stats and register settings from camera */
2526 /* do_command can't handle this, so do it ourselves */
2527 cmd[0] = CPIA_COMMAND_ReadVPRegs>>8;
2528 cmd[1] = CPIA_COMMAND_ReadVPRegs&0xff;
2529 cmd[2] = 30;
2530 cmd[3] = 4;
2531 cmd[4] = 9;
2532 cmd[5] = 8;
2533 cmd[6] = 8;
2534 cmd[7] = 0;
2535 retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
2536 if (retval) {
2537 LOG("ReadVPRegs(30,4,9,8) - failed, retval=%d\n",
2538 retval);
2539 return;
2540 }
2541 exp_acc = data[0];
2542 bcomp = data[1];
2543 gain = data[2];
2544 coarseL = data[3];
2545
Ingo Molnar3593cab2006-02-07 06:49:14 -02002546 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002547 light_exp = cam->params.colourParams.brightness +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002548 TC - 50 + EXP_ACC_LIGHT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002549 if(light_exp > 255)
2550 light_exp = 255;
2551 dark_exp = cam->params.colourParams.brightness +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002552 TC - 50 - EXP_ACC_DARK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002553 if(dark_exp < 0)
2554 dark_exp = 0;
2555 very_dark_exp = dark_exp/2;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002556
Linus Torvalds1da177e2005-04-16 15:20:36 -07002557 old_exposure = cam->params.exposure.coarseExpHi * 256 +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002558 cam->params.exposure.coarseExpLo;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002559
2560 if(!cam->params.flickerControl.disabled) {
2561 /* Flicker control on */
2562 int max_comp = FIRMWARE_VERSION(1,2) ? MAX_COMP : HIGH_COMP_102;
2563 bcomp += 128; /* decode */
2564 if(bcomp >= max_comp && exp_acc < dark_exp) {
2565 /* dark */
2566 if(exp_acc < very_dark_exp) {
2567 /* very dark */
2568 if(cam->exposure_status == EXPOSURE_VERY_DARK)
2569 ++cam->exposure_count;
2570 else {
2571 cam->exposure_status = EXPOSURE_VERY_DARK;
2572 cam->exposure_count = 1;
2573 }
2574 } else {
2575 /* just dark */
2576 if(cam->exposure_status == EXPOSURE_DARK)
2577 ++cam->exposure_count;
2578 else {
2579 cam->exposure_status = EXPOSURE_DARK;
2580 cam->exposure_count = 1;
2581 }
2582 }
2583 } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) {
2584 /* light */
2585 if(old_exposure <= VERY_LOW_EXP) {
2586 /* very light */
2587 if(cam->exposure_status == EXPOSURE_VERY_LIGHT)
2588 ++cam->exposure_count;
2589 else {
2590 cam->exposure_status = EXPOSURE_VERY_LIGHT;
2591 cam->exposure_count = 1;
2592 }
2593 } else {
2594 /* just light */
2595 if(cam->exposure_status == EXPOSURE_LIGHT)
2596 ++cam->exposure_count;
2597 else {
2598 cam->exposure_status = EXPOSURE_LIGHT;
2599 cam->exposure_count = 1;
2600 }
2601 }
2602 } else {
2603 /* not dark or light */
2604 cam->exposure_status = EXPOSURE_NORMAL;
2605 }
2606 } else {
2607 /* Flicker control off */
2608 if(old_exposure >= MAX_EXP && exp_acc < dark_exp) {
2609 /* dark */
2610 if(exp_acc < very_dark_exp) {
2611 /* very dark */
2612 if(cam->exposure_status == EXPOSURE_VERY_DARK)
2613 ++cam->exposure_count;
2614 else {
2615 cam->exposure_status = EXPOSURE_VERY_DARK;
2616 cam->exposure_count = 1;
2617 }
2618 } else {
2619 /* just dark */
2620 if(cam->exposure_status == EXPOSURE_DARK)
2621 ++cam->exposure_count;
2622 else {
2623 cam->exposure_status = EXPOSURE_DARK;
2624 cam->exposure_count = 1;
2625 }
2626 }
2627 } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) {
2628 /* light */
2629 if(old_exposure <= VERY_LOW_EXP) {
2630 /* very light */
2631 if(cam->exposure_status == EXPOSURE_VERY_LIGHT)
2632 ++cam->exposure_count;
2633 else {
2634 cam->exposure_status = EXPOSURE_VERY_LIGHT;
2635 cam->exposure_count = 1;
2636 }
2637 } else {
2638 /* just light */
2639 if(cam->exposure_status == EXPOSURE_LIGHT)
2640 ++cam->exposure_count;
2641 else {
2642 cam->exposure_status = EXPOSURE_LIGHT;
2643 cam->exposure_count = 1;
2644 }
2645 }
2646 } else {
2647 /* not dark or light */
2648 cam->exposure_status = EXPOSURE_NORMAL;
2649 }
2650 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002651
Linus Torvalds1da177e2005-04-16 15:20:36 -07002652 framerate = cam->fps;
2653 if(framerate > 30 || framerate < 1)
2654 framerate = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002655
Linus Torvalds1da177e2005-04-16 15:20:36 -07002656 if(!cam->params.flickerControl.disabled) {
2657 /* Flicker control on */
2658 if((cam->exposure_status == EXPOSURE_VERY_DARK ||
2659 cam->exposure_status == EXPOSURE_DARK) &&
2660 cam->exposure_count >= DARK_TIME*framerate &&
2661 cam->params.sensorFps.divisor < 3) {
2662
2663 /* dark for too long */
2664 ++cam->params.sensorFps.divisor;
2665 cam->cmd_queue |= COMMAND_SETSENSORFPS;
2666
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002667 cam->params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07002668 flicker_jumps[cam->mainsFreq]
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002669 [cam->params.sensorFps.baserate]
2670 [cam->params.sensorFps.divisor];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002671 cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
2672
2673 new_exposure = cam->params.flickerControl.coarseJump-1;
2674 while(new_exposure < old_exposure/2)
2675 new_exposure += cam->params.flickerControl.coarseJump;
2676 cam->params.exposure.coarseExpLo = new_exposure & 0xff;
2677 cam->params.exposure.coarseExpHi = new_exposure >> 8;
2678 cam->cmd_queue |= COMMAND_SETEXPOSURE;
2679 cam->exposure_status = EXPOSURE_NORMAL;
2680 LOG("Automatically decreasing sensor_fps\n");
2681
2682 } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT ||
2683 cam->exposure_status == EXPOSURE_LIGHT) &&
2684 cam->exposure_count >= LIGHT_TIME*framerate &&
2685 cam->params.sensorFps.divisor > 0) {
2686
2687 /* light for too long */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002688 int max_exp = FIRMWARE_VERSION(1,2) ? MAX_EXP_102 : MAX_EXP ;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002689
2690 --cam->params.sensorFps.divisor;
2691 cam->cmd_queue |= COMMAND_SETSENSORFPS;
2692
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002693 cam->params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07002694 flicker_jumps[cam->mainsFreq]
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002695 [cam->params.sensorFps.baserate]
2696 [cam->params.sensorFps.divisor];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002697 cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
2698
2699 new_exposure = cam->params.flickerControl.coarseJump-1;
2700 while(new_exposure < 2*old_exposure &&
2701 new_exposure+
2702 cam->params.flickerControl.coarseJump < max_exp)
2703 new_exposure += cam->params.flickerControl.coarseJump;
2704 cam->params.exposure.coarseExpLo = new_exposure & 0xff;
2705 cam->params.exposure.coarseExpHi = new_exposure >> 8;
2706 cam->cmd_queue |= COMMAND_SETEXPOSURE;
2707 cam->exposure_status = EXPOSURE_NORMAL;
2708 LOG("Automatically increasing sensor_fps\n");
2709 }
2710 } else {
2711 /* Flicker control off */
2712 if((cam->exposure_status == EXPOSURE_VERY_DARK ||
2713 cam->exposure_status == EXPOSURE_DARK) &&
2714 cam->exposure_count >= DARK_TIME*framerate &&
2715 cam->params.sensorFps.divisor < 3) {
2716
2717 /* dark for too long */
2718 ++cam->params.sensorFps.divisor;
2719 cam->cmd_queue |= COMMAND_SETSENSORFPS;
2720
2721 if(cam->params.exposure.gain > 0) {
2722 --cam->params.exposure.gain;
2723 cam->cmd_queue |= COMMAND_SETEXPOSURE;
2724 }
2725 cam->exposure_status = EXPOSURE_NORMAL;
2726 LOG("Automatically decreasing sensor_fps\n");
2727
2728 } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT ||
2729 cam->exposure_status == EXPOSURE_LIGHT) &&
2730 cam->exposure_count >= LIGHT_TIME*framerate &&
2731 cam->params.sensorFps.divisor > 0) {
2732
2733 /* light for too long */
2734 --cam->params.sensorFps.divisor;
2735 cam->cmd_queue |= COMMAND_SETSENSORFPS;
2736
2737 if(cam->params.exposure.gain <
2738 cam->params.exposure.gainMode-1) {
2739 ++cam->params.exposure.gain;
2740 cam->cmd_queue |= COMMAND_SETEXPOSURE;
2741 }
2742 cam->exposure_status = EXPOSURE_NORMAL;
2743 LOG("Automatically increasing sensor_fps\n");
2744 }
2745 }
Ingo Molnar3593cab2006-02-07 06:49:14 -02002746 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002747}
2748
2749/*-----------------------------------------------------------------*/
2750/* if flicker is switched off, this function switches it back on.It checks,
2751 however, that conditions are suitable before restarting it.
2752 This should only be called for firmware version 1.2.
2753
2754 It also adjust the colour balance when an exposure step is detected - as
2755 long as flicker is running
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002756*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07002757static void restart_flicker(struct cam_data *cam)
2758{
2759 int cam_exposure, old_exp;
2760 if(!FIRMWARE_VERSION(1,2))
2761 return;
Ingo Molnar3593cab2006-02-07 06:49:14 -02002762 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002763 if(cam->params.flickerControl.flickerMode == 0 ||
2764 cam->raw_image[39] == 0) {
Ingo Molnar3593cab2006-02-07 06:49:14 -02002765 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002766 return;
2767 }
2768 cam_exposure = cam->raw_image[39]*2;
2769 old_exp = cam->params.exposure.coarseExpLo +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002770 cam->params.exposure.coarseExpHi*256;
2771 /*
2772 see how far away camera exposure is from a valid
2773 flicker exposure value
2774 */
2775 cam_exposure %= cam->params.flickerControl.coarseJump;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002776 if(!cam->params.flickerControl.disabled &&
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002777 cam_exposure <= cam->params.flickerControl.coarseJump - 3) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002778 /* Flicker control auto-disabled */
2779 cam->params.flickerControl.disabled = 1;
2780 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002781
Linus Torvalds1da177e2005-04-16 15:20:36 -07002782 if(cam->params.flickerControl.disabled &&
2783 cam->params.flickerControl.flickerMode &&
2784 old_exp > cam->params.flickerControl.coarseJump +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002785 ROUND_UP_EXP_FOR_FLICKER) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002786 /* exposure is now high enough to switch
2787 flicker control back on */
2788 set_flicker(&cam->params, &cam->cmd_queue, 1);
2789 if((cam->cmd_queue & COMMAND_SETEXPOSURE) &&
2790 cam->params.exposure.expMode == 2)
2791 cam->exposure_status = EXPOSURE_NORMAL;
2792
2793 }
Ingo Molnar3593cab2006-02-07 06:49:14 -02002794 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002795}
2796#undef FIRMWARE_VERSION
2797
2798static int clear_stall(struct cam_data *cam)
2799{
2800 /* FIXME: Does this actually work? */
2801 LOG("Clearing stall\n");
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002802
Linus Torvalds1da177e2005-04-16 15:20:36 -07002803 cam->ops->streamRead(cam->lowlevel_data, cam->raw_image, 0);
2804 do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0);
2805 return cam->params.status.streamState != STREAM_PAUSED;
2806}
2807
2808/* kernel thread function to read image from camera */
2809static int fetch_frame(void *data)
2810{
2811 int image_size, retry;
2812 struct cam_data *cam = (struct cam_data *)data;
2813 unsigned long oldjif, rate, diff;
2814
2815 /* Allow up to two bad images in a row to be read and
2816 * ignored before an error is reported */
2817 for (retry = 0; retry < 3; ++retry) {
2818 if (retry)
2819 DBG("retry=%d\n", retry);
2820
2821 if (!cam->ops)
2822 continue;
2823
2824 /* load first frame always uncompressed */
2825 if (cam->first_frame &&
2826 cam->params.compression.mode != CPIA_COMPRESSION_NONE) {
2827 do_command(cam, CPIA_COMMAND_SetCompression,
2828 CPIA_COMPRESSION_NONE,
2829 NO_DECIMATION, 0, 0);
2830 /* Trial & error - Discarding a frame prevents the
2831 first frame from having an error in the data. */
2832 do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0);
2833 }
2834
2835 /* init camera upload */
2836 if (do_command(cam, CPIA_COMMAND_GrabFrame, 0,
2837 cam->params.streamStartLine, 0, 0))
2838 continue;
2839
2840 if (cam->ops->wait_for_stream_ready) {
2841 /* loop until image ready */
2842 int count = 0;
2843 do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0);
2844 while (cam->params.status.streamState != STREAM_READY) {
2845 if(++count > READY_TIMEOUT)
2846 break;
2847 if(cam->params.status.streamState ==
2848 STREAM_PAUSED) {
2849 /* Bad news */
2850 if(!clear_stall(cam))
2851 return -EIO;
2852 }
2853
2854 cond_resched();
2855
2856 /* sleep for 10 ms, hopefully ;) */
2857 msleep_interruptible(10);
2858 if (signal_pending(current))
2859 return -EINTR;
2860
2861 do_command(cam, CPIA_COMMAND_GetCameraStatus,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002862 0, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002863 }
2864 if(cam->params.status.streamState != STREAM_READY) {
2865 continue;
2866 }
2867 }
2868
2869 cond_resched();
2870
2871 /* grab image from camera */
2872 oldjif = jiffies;
2873 image_size = cam->ops->streamRead(cam->lowlevel_data,
2874 cam->raw_image, 0);
2875 if (image_size <= 0) {
2876 DBG("streamRead failed: %d\n", image_size);
2877 continue;
2878 }
2879
2880 rate = image_size * HZ / 1024;
2881 diff = jiffies-oldjif;
2882 cam->transfer_rate = diff==0 ? rate : rate/diff;
2883 /* diff==0 ? unlikely but possible */
2884
2885 /* Switch flicker control back on if it got turned off */
2886 restart_flicker(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002887
Linus Torvalds1da177e2005-04-16 15:20:36 -07002888 /* If AEC is enabled, monitor the exposure and
2889 adjust the sensor frame rate if needed */
2890 if(cam->params.exposure.expMode == 2)
2891 monitor_exposure(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002892
Linus Torvalds1da177e2005-04-16 15:20:36 -07002893 /* camera idle now so dispatch queued commands */
2894 dispatch_commands(cam);
2895
2896 /* Update our knowledge of the camera state */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002897 do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
2898 do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002899 do_command(cam, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0);
2900
2901 /* decompress and convert image to by copying it from
2902 * raw_image to decompressed_frame
2903 */
2904
2905 cond_resched();
2906
2907 cam->image_size = parse_picture(cam, image_size);
2908 if (cam->image_size <= 0) {
2909 DBG("parse_picture failed %d\n", cam->image_size);
2910 if(cam->params.compression.mode !=
2911 CPIA_COMPRESSION_NONE) {
2912 /* Compression may not work right if we
2913 had a bad frame, get the next one
2914 uncompressed. */
2915 cam->first_frame = 1;
2916 do_command(cam, CPIA_COMMAND_SetGrabMode,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002917 CPIA_GRAB_SINGLE, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002918 /* FIXME: Trial & error - need up to 70ms for
2919 the grab mode change to complete ? */
2920 msleep_interruptible(70);
2921 if (signal_pending(current))
2922 return -EINTR;
2923 }
2924 } else
2925 break;
2926 }
2927
2928 if (retry < 3) {
2929 /* FIXME: this only works for double buffering */
2930 if (cam->frame[cam->curframe].state == FRAME_READY) {
2931 memcpy(cam->frame[cam->curframe].data,
2932 cam->decompressed_frame.data,
2933 cam->decompressed_frame.count);
2934 cam->frame[cam->curframe].state = FRAME_DONE;
2935 } else
2936 cam->decompressed_frame.state = FRAME_DONE;
2937
2938 if (cam->first_frame) {
2939 cam->first_frame = 0;
2940 do_command(cam, CPIA_COMMAND_SetCompression,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002941 cam->params.compression.mode,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002942 cam->params.compression.decimation, 0, 0);
2943
2944 /* Switch from single-grab to continuous grab */
2945 do_command(cam, CPIA_COMMAND_SetGrabMode,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002946 CPIA_GRAB_CONTINUOUS, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002947 }
2948 return 0;
2949 }
2950 return -EIO;
2951}
2952
2953static int capture_frame(struct cam_data *cam, struct video_mmap *vm)
2954{
2955 if (!cam->frame_buf) {
2956 /* we do lazy allocation */
2957 int err;
2958 if ((err = allocate_frame_buf(cam)))
2959 return err;
2960 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002961
Linus Torvalds1da177e2005-04-16 15:20:36 -07002962 cam->curframe = vm->frame;
2963 cam->frame[cam->curframe].state = FRAME_READY;
2964 return fetch_frame(cam);
2965}
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002966
Linus Torvalds1da177e2005-04-16 15:20:36 -07002967static int goto_high_power(struct cam_data *cam)
2968{
2969 if (do_command(cam, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0))
2970 return -EIO;
2971 msleep_interruptible(40); /* windows driver does it too */
2972 if(signal_pending(current))
2973 return -EINTR;
2974 if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
2975 return -EIO;
2976 if (cam->params.status.systemState == HI_POWER_STATE) {
2977 DBG("camera now in HIGH power state\n");
2978 return 0;
2979 }
2980 printstatus(cam);
2981 return -EIO;
2982}
2983
2984static int goto_low_power(struct cam_data *cam)
2985{
2986 if (do_command(cam, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0))
2987 return -1;
2988 if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
2989 return -1;
2990 if (cam->params.status.systemState == LO_POWER_STATE) {
2991 DBG("camera now in LOW power state\n");
2992 return 0;
2993 }
2994 printstatus(cam);
2995 return -1;
2996}
2997
2998static void save_camera_state(struct cam_data *cam)
2999{
3000 if(!(cam->cmd_queue & COMMAND_SETCOLOURBALANCE))
3001 do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
3002 if(!(cam->cmd_queue & COMMAND_SETEXPOSURE))
3003 do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
3004
3005 DBG("%d/%d/%d/%d/%d/%d/%d/%d\n",
3006 cam->params.exposure.gain,
3007 cam->params.exposure.fineExp,
3008 cam->params.exposure.coarseExpLo,
3009 cam->params.exposure.coarseExpHi,
3010 cam->params.exposure.redComp,
3011 cam->params.exposure.green1Comp,
3012 cam->params.exposure.green2Comp,
3013 cam->params.exposure.blueComp);
3014 DBG("%d/%d/%d\n",
3015 cam->params.colourBalance.redGain,
3016 cam->params.colourBalance.greenGain,
3017 cam->params.colourBalance.blueGain);
3018}
3019
3020static int set_camera_state(struct cam_data *cam)
3021{
3022 cam->cmd_queue = COMMAND_SETCOMPRESSION |
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003023 COMMAND_SETCOMPRESSIONTARGET |
3024 COMMAND_SETCOLOURPARAMS |
3025 COMMAND_SETFORMAT |
3026 COMMAND_SETYUVTHRESH |
3027 COMMAND_SETECPTIMING |
3028 COMMAND_SETCOMPRESSIONPARAMS |
3029 COMMAND_SETEXPOSURE |
3030 COMMAND_SETCOLOURBALANCE |
3031 COMMAND_SETSENSORFPS |
3032 COMMAND_SETAPCOR |
3033 COMMAND_SETFLICKERCTRL |
3034 COMMAND_SETVLOFFSET;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003035
3036 do_command(cam, CPIA_COMMAND_SetGrabMode, CPIA_GRAB_SINGLE,0,0,0);
3037 dispatch_commands(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003038
Linus Torvalds1da177e2005-04-16 15:20:36 -07003039 /* Wait 6 frames for the sensor to get all settings and
3040 AEC/ACB to settle */
3041 msleep_interruptible(6*(cam->params.sensorFps.baserate ? 33 : 40) *
3042 (1 << cam->params.sensorFps.divisor) + 10);
3043
3044 if(signal_pending(current))
3045 return -EINTR;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003046
Linus Torvalds1da177e2005-04-16 15:20:36 -07003047 save_camera_state(cam);
3048
3049 return 0;
3050}
3051
3052static void get_version_information(struct cam_data *cam)
3053{
3054 /* GetCPIAVersion */
3055 do_command(cam, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0);
3056
3057 /* GetPnPID */
3058 do_command(cam, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0);
3059}
3060
3061/* initialize camera */
3062static int reset_camera(struct cam_data *cam)
3063{
3064 int err;
3065 /* Start the camera in low power mode */
3066 if (goto_low_power(cam)) {
3067 if (cam->params.status.systemState != WARM_BOOT_STATE)
3068 return -ENODEV;
3069
3070 /* FIXME: this is just dirty trial and error */
3071 err = goto_high_power(cam);
3072 if(err)
3073 return err;
3074 do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0);
3075 if (goto_low_power(cam))
3076 return -ENODEV;
3077 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003078
Linus Torvalds1da177e2005-04-16 15:20:36 -07003079 /* procedure described in developer's guide p3-28 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003080
Linus Torvalds1da177e2005-04-16 15:20:36 -07003081 /* Check the firmware version. */
3082 cam->params.version.firmwareVersion = 0;
3083 get_version_information(cam);
3084 if (cam->params.version.firmwareVersion != 1)
3085 return -ENODEV;
3086
3087 /* A bug in firmware 1-02 limits gainMode to 2 */
3088 if(cam->params.version.firmwareRevision <= 2 &&
3089 cam->params.exposure.gainMode > 2) {
3090 cam->params.exposure.gainMode = 2;
3091 }
3092
3093 /* set QX3 detected flag */
3094 cam->params.qx3.qx3_detected = (cam->params.pnpID.vendor == 0x0813 &&
3095 cam->params.pnpID.product == 0x0001);
3096
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003097 /* The fatal error checking should be done after
Linus Torvalds1da177e2005-04-16 15:20:36 -07003098 * the camera powers up (developer's guide p 3-38) */
3099
3100 /* Set streamState before transition to high power to avoid bug
3101 * in firmware 1-02 */
3102 do_command(cam, CPIA_COMMAND_ModifyCameraStatus, STREAMSTATE, 0,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003103 STREAM_NOT_READY, 0);
3104
Linus Torvalds1da177e2005-04-16 15:20:36 -07003105 /* GotoHiPower */
3106 err = goto_high_power(cam);
3107 if (err)
3108 return err;
3109
3110 /* Check the camera status */
3111 if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
3112 return -EIO;
3113
3114 if (cam->params.status.fatalError) {
3115 DBG("fatal_error: %#04x\n",
3116 cam->params.status.fatalError);
3117 DBG("vp_status: %#04x\n",
3118 cam->params.status.vpStatus);
3119 if (cam->params.status.fatalError & ~(COM_FLAG|CPIA_FLAG)) {
3120 /* Fatal error in camera */
3121 return -EIO;
3122 } else if (cam->params.status.fatalError & (COM_FLAG|CPIA_FLAG)) {
3123 /* Firmware 1-02 may do this for parallel port cameras,
3124 * just clear the flags (developer's guide p 3-38) */
3125 do_command(cam, CPIA_COMMAND_ModifyCameraStatus,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003126 FATALERROR, ~(COM_FLAG|CPIA_FLAG), 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003127 }
3128 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003129
Linus Torvalds1da177e2005-04-16 15:20:36 -07003130 /* Check the camera status again */
3131 if (cam->params.status.fatalError) {
3132 if (cam->params.status.fatalError)
3133 return -EIO;
3134 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003135
Linus Torvalds1da177e2005-04-16 15:20:36 -07003136 /* VPVersion can't be retrieved before the camera is in HiPower,
3137 * so get it here instead of in get_version_information. */
3138 do_command(cam, CPIA_COMMAND_GetVPVersion, 0, 0, 0, 0);
3139
3140 /* set camera to a known state */
3141 return set_camera_state(cam);
3142}
3143
3144static void put_cam(struct cpia_camera_ops* ops)
3145{
Mariusz Kozlowskiac328982007-01-07 10:36:24 -03003146 module_put(ops->owner);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003147}
3148
3149/* ------------------------- V4L interface --------------------- */
Hans Verkuilbec43662008-12-30 06:58:20 -03003150static int cpia_open(struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003151{
3152 struct video_device *dev = video_devdata(file);
Hans Verkuil601e9442008-08-23 07:24:07 -03003153 struct cam_data *cam = video_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003154 int err;
3155
3156 if (!cam) {
3157 DBG("Internal error, cam_data not found!\n");
3158 return -ENODEV;
3159 }
3160
3161 if (cam->open_count > 0) {
3162 DBG("Camera already open\n");
3163 return -EBUSY;
3164 }
3165
3166 if (!try_module_get(cam->ops->owner))
3167 return -ENODEV;
3168
Ingo Molnar3593cab2006-02-07 06:49:14 -02003169 mutex_lock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003170 err = -ENOMEM;
3171 if (!cam->raw_image) {
3172 cam->raw_image = rvmalloc(CPIA_MAX_IMAGE_SIZE);
3173 if (!cam->raw_image)
3174 goto oops;
3175 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003176
Linus Torvalds1da177e2005-04-16 15:20:36 -07003177 if (!cam->decompressed_frame.data) {
3178 cam->decompressed_frame.data = rvmalloc(CPIA_MAX_FRAME_SIZE);
3179 if (!cam->decompressed_frame.data)
3180 goto oops;
3181 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003182
Linus Torvalds1da177e2005-04-16 15:20:36 -07003183 /* open cpia */
3184 err = -ENODEV;
3185 if (cam->ops->open(cam->lowlevel_data))
3186 goto oops;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003187
Linus Torvalds1da177e2005-04-16 15:20:36 -07003188 /* reset the camera */
3189 if ((err = reset_camera(cam)) != 0) {
3190 cam->ops->close(cam->lowlevel_data);
3191 goto oops;
3192 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003193
Linus Torvalds1da177e2005-04-16 15:20:36 -07003194 err = -EINTR;
3195 if(signal_pending(current))
3196 goto oops;
3197
3198 /* Set ownership of /proc/cpia/videoX to current user */
3199 if(cam->proc_entry)
David Howellsabc94fc2008-08-27 10:46:39 -03003200 cam->proc_entry->uid = current_uid();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003201
3202 /* set mark for loading first frame uncompressed */
3203 cam->first_frame = 1;
3204
3205 /* init it to something */
3206 cam->mmap_kludge = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003207
Linus Torvalds1da177e2005-04-16 15:20:36 -07003208 ++cam->open_count;
3209 file->private_data = dev;
Ingo Molnar3593cab2006-02-07 06:49:14 -02003210 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003211 return 0;
3212
3213 oops:
3214 if (cam->decompressed_frame.data) {
3215 rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
3216 cam->decompressed_frame.data = NULL;
3217 }
3218 if (cam->raw_image) {
3219 rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
3220 cam->raw_image = NULL;
3221 }
Ingo Molnar3593cab2006-02-07 06:49:14 -02003222 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003223 put_cam(cam->ops);
3224 return err;
3225}
3226
Hans Verkuilbec43662008-12-30 06:58:20 -03003227static int cpia_close(struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003228{
3229 struct video_device *dev = file->private_data;
Hans Verkuil601e9442008-08-23 07:24:07 -03003230 struct cam_data *cam = video_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003231
3232 if (cam->ops) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003233 /* Return ownership of /proc/cpia/videoX to root */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003234 if(cam->proc_entry)
3235 cam->proc_entry->uid = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003236
Linus Torvalds1da177e2005-04-16 15:20:36 -07003237 /* save camera state for later open (developers guide ch 3.5.3) */
3238 save_camera_state(cam);
3239
3240 /* GotoLoPower */
3241 goto_low_power(cam);
3242
3243 /* Update the camera status */
3244 do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
3245
3246 /* cleanup internal state stuff */
3247 free_frames(cam->frame);
3248
3249 /* close cpia */
3250 cam->ops->close(cam->lowlevel_data);
3251
3252 put_cam(cam->ops);
3253 }
3254
3255 if (--cam->open_count == 0) {
3256 /* clean up capture-buffers */
3257 if (cam->raw_image) {
3258 rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
3259 cam->raw_image = NULL;
3260 }
3261
3262 if (cam->decompressed_frame.data) {
3263 rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
3264 cam->decompressed_frame.data = NULL;
3265 }
3266
3267 if (cam->frame_buf)
3268 free_frame_buf(cam);
3269
3270 if (!cam->ops)
3271 kfree(cam);
3272 }
3273 file->private_data = NULL;
3274
3275 return 0;
3276}
3277
3278static ssize_t cpia_read(struct file *file, char __user *buf,
3279 size_t count, loff_t *ppos)
3280{
3281 struct video_device *dev = file->private_data;
Hans Verkuil601e9442008-08-23 07:24:07 -03003282 struct cam_data *cam = video_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003283 int err;
3284
3285 /* make this _really_ smp and multithread-safe */
Ingo Molnar3593cab2006-02-07 06:49:14 -02003286 if (mutex_lock_interruptible(&cam->busy_lock))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003287 return -EINTR;
3288
3289 if (!buf) {
3290 DBG("buf NULL\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02003291 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003292 return -EINVAL;
3293 }
3294
3295 if (!count) {
3296 DBG("count 0\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02003297 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003298 return 0;
3299 }
3300
3301 if (!cam->ops) {
3302 DBG("ops NULL\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02003303 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003304 return -ENODEV;
3305 }
3306
3307 /* upload frame */
3308 cam->decompressed_frame.state = FRAME_READY;
3309 cam->mmap_kludge=0;
3310 if((err = fetch_frame(cam)) != 0) {
3311 DBG("ERROR from fetch_frame: %d\n", err);
Ingo Molnar3593cab2006-02-07 06:49:14 -02003312 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003313 return err;
3314 }
3315 cam->decompressed_frame.state = FRAME_UNUSED;
3316
3317 /* copy data to user space */
3318 if (cam->decompressed_frame.count > count) {
3319 DBG("count wrong: %d, %lu\n", cam->decompressed_frame.count,
3320 (unsigned long) count);
Ingo Molnar3593cab2006-02-07 06:49:14 -02003321 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003322 return -EFAULT;
3323 }
3324 if (copy_to_user(buf, cam->decompressed_frame.data,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003325 cam->decompressed_frame.count)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003326 DBG("copy_to_user failed\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02003327 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003328 return -EFAULT;
3329 }
3330
Ingo Molnar3593cab2006-02-07 06:49:14 -02003331 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003332 return cam->decompressed_frame.count;
3333}
3334
Hans Verkuil069b7472008-12-30 07:04:34 -03003335static long cpia_do_ioctl(struct file *file, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003336{
3337 struct video_device *dev = file->private_data;
Hans Verkuil601e9442008-08-23 07:24:07 -03003338 struct cam_data *cam = video_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003339 int retval = 0;
3340
3341 if (!cam || !cam->ops)
3342 return -ENODEV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003343
Linus Torvalds1da177e2005-04-16 15:20:36 -07003344 /* make this _really_ smp-safe */
Ingo Molnar3593cab2006-02-07 06:49:14 -02003345 if (mutex_lock_interruptible(&cam->busy_lock))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003346 return -EINTR;
3347
Hans Verkuilf473bf72008-11-01 08:25:11 -03003348 /* DBG("cpia_ioctl: %u\n", cmd); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003349
Hans Verkuilf473bf72008-11-01 08:25:11 -03003350 switch (cmd) {
Alexey Dobriyanbe787ac2006-03-07 22:20:23 -03003351 /* query capabilities */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003352 case VIDIOCGCAP:
3353 {
3354 struct video_capability *b = arg;
3355
3356 DBG("VIDIOCGCAP\n");
3357 strcpy(b->name, "CPiA Camera");
3358 b->type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE;
3359 b->channels = 1;
3360 b->audios = 0;
3361 b->maxwidth = 352; /* VIDEOSIZE_CIF */
3362 b->maxheight = 288;
3363 b->minwidth = 48; /* VIDEOSIZE_48_48 */
3364 b->minheight = 48;
3365 break;
3366 }
3367
3368 /* get/set video source - we are a camera and nothing else */
3369 case VIDIOCGCHAN:
3370 {
3371 struct video_channel *v = arg;
3372
3373 DBG("VIDIOCGCHAN\n");
3374 if (v->channel != 0) {
3375 retval = -EINVAL;
3376 break;
3377 }
3378
3379 v->channel = 0;
3380 strcpy(v->name, "Camera");
3381 v->tuners = 0;
3382 v->flags = 0;
3383 v->type = VIDEO_TYPE_CAMERA;
3384 v->norm = 0;
3385 break;
3386 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003387
Linus Torvalds1da177e2005-04-16 15:20:36 -07003388 case VIDIOCSCHAN:
3389 {
3390 struct video_channel *v = arg;
3391
3392 DBG("VIDIOCSCHAN\n");
3393 if (v->channel != 0)
3394 retval = -EINVAL;
3395 break;
3396 }
3397
3398 /* image properties */
3399 case VIDIOCGPICT:
3400 {
3401 struct video_picture *pic = arg;
3402 DBG("VIDIOCGPICT\n");
3403 *pic = cam->vp;
3404 break;
3405 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003406
Linus Torvalds1da177e2005-04-16 15:20:36 -07003407 case VIDIOCSPICT:
3408 {
3409 struct video_picture *vp = arg;
3410
3411 DBG("VIDIOCSPICT\n");
3412
3413 /* check validity */
3414 DBG("palette: %d\n", vp->palette);
3415 DBG("depth: %d\n", vp->depth);
3416 if (!valid_mode(vp->palette, vp->depth)) {
3417 retval = -EINVAL;
3418 break;
3419 }
3420
Ingo Molnar3593cab2006-02-07 06:49:14 -02003421 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003422 /* brightness, colour, contrast need no check 0-65535 */
3423 cam->vp = *vp;
3424 /* update cam->params.colourParams */
3425 cam->params.colourParams.brightness = vp->brightness*100/65535;
3426 cam->params.colourParams.contrast = vp->contrast*100/65535;
3427 cam->params.colourParams.saturation = vp->colour*100/65535;
3428 /* contrast is in steps of 8, so round */
3429 cam->params.colourParams.contrast =
3430 ((cam->params.colourParams.contrast + 3) / 8) * 8;
3431 if (cam->params.version.firmwareVersion == 1 &&
3432 cam->params.version.firmwareRevision == 2 &&
3433 cam->params.colourParams.contrast > 80) {
3434 /* 1-02 firmware limits contrast to 80 */
3435 cam->params.colourParams.contrast = 80;
3436 }
3437
3438 /* Adjust flicker control if necessary */
3439 if(cam->params.flickerControl.allowableOverExposure < 0)
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003440 cam->params.flickerControl.allowableOverExposure =
Linus Torvalds1da177e2005-04-16 15:20:36 -07003441 -find_over_exposure(cam->params.colourParams.brightness);
3442 if(cam->params.flickerControl.flickerMode != 0)
3443 cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003444
Linus Torvalds1da177e2005-04-16 15:20:36 -07003445
3446 /* queue command to update camera */
3447 cam->cmd_queue |= COMMAND_SETCOLOURPARAMS;
Ingo Molnar3593cab2006-02-07 06:49:14 -02003448 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003449 DBG("VIDIOCSPICT: %d / %d // %d / %d / %d / %d\n",
3450 vp->depth, vp->palette, vp->brightness, vp->hue, vp->colour,
3451 vp->contrast);
3452 break;
3453 }
3454
3455 /* get/set capture window */
3456 case VIDIOCGWIN:
3457 {
3458 struct video_window *vw = arg;
3459 DBG("VIDIOCGWIN\n");
3460
3461 *vw = cam->vw;
3462 break;
3463 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003464
Linus Torvalds1da177e2005-04-16 15:20:36 -07003465 case VIDIOCSWIN:
3466 {
3467 /* copy_from_user, check validity, copy to internal structure */
3468 struct video_window *vw = arg;
3469 DBG("VIDIOCSWIN\n");
3470
3471 if (vw->clipcount != 0) { /* clipping not supported */
3472 retval = -EINVAL;
3473 break;
3474 }
3475 if (vw->clips != NULL) { /* clipping not supported */
3476 retval = -EINVAL;
3477 break;
3478 }
3479
3480 /* we set the video window to something smaller or equal to what
3481 * is requested by the user???
3482 */
Ingo Molnar3593cab2006-02-07 06:49:14 -02003483 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003484 if (vw->width != cam->vw.width || vw->height != cam->vw.height) {
3485 int video_size = match_videosize(vw->width, vw->height);
3486
3487 if (video_size < 0) {
3488 retval = -EINVAL;
Ingo Molnar3593cab2006-02-07 06:49:14 -02003489 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003490 break;
3491 }
3492 cam->video_size = video_size;
3493
3494 /* video size is changing, reset the subcapture area */
3495 memset(&cam->vc, 0, sizeof(cam->vc));
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003496
Linus Torvalds1da177e2005-04-16 15:20:36 -07003497 set_vw_size(cam);
3498 DBG("%d / %d\n", cam->vw.width, cam->vw.height);
3499 cam->cmd_queue |= COMMAND_SETFORMAT;
3500 }
3501
Ingo Molnar3593cab2006-02-07 06:49:14 -02003502 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003503
3504 /* setformat ignored by camera during streaming,
3505 * so stop/dispatch/start */
3506 if (cam->cmd_queue & COMMAND_SETFORMAT) {
3507 DBG("\n");
3508 dispatch_commands(cam);
3509 }
3510 DBG("%d/%d:%d\n", cam->video_size,
3511 cam->vw.width, cam->vw.height);
3512 break;
3513 }
3514
3515 /* mmap interface */
3516 case VIDIOCGMBUF:
3517 {
3518 struct video_mbuf *vm = arg;
3519 int i;
3520
3521 DBG("VIDIOCGMBUF\n");
3522 memset(vm, 0, sizeof(*vm));
3523 vm->size = CPIA_MAX_FRAME_SIZE*FRAME_NUM;
3524 vm->frames = FRAME_NUM;
3525 for (i = 0; i < FRAME_NUM; i++)
3526 vm->offsets[i] = CPIA_MAX_FRAME_SIZE * i;
3527 break;
3528 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003529
Linus Torvalds1da177e2005-04-16 15:20:36 -07003530 case VIDIOCMCAPTURE:
3531 {
3532 struct video_mmap *vm = arg;
3533 int video_size;
3534
3535 DBG("VIDIOCMCAPTURE: %d / %d / %dx%d\n", vm->format, vm->frame,
3536 vm->width, vm->height);
3537 if (vm->frame<0||vm->frame>=FRAME_NUM) {
3538 retval = -EINVAL;
3539 break;
3540 }
3541
3542 /* set video format */
3543 cam->vp.palette = vm->format;
3544 switch(vm->format) {
3545 case VIDEO_PALETTE_GREY:
3546 cam->vp.depth=8;
3547 break;
3548 case VIDEO_PALETTE_RGB555:
3549 case VIDEO_PALETTE_RGB565:
3550 case VIDEO_PALETTE_YUV422:
3551 case VIDEO_PALETTE_YUYV:
3552 case VIDEO_PALETTE_UYVY:
3553 cam->vp.depth = 16;
3554 break;
3555 case VIDEO_PALETTE_RGB24:
3556 cam->vp.depth = 24;
3557 break;
3558 case VIDEO_PALETTE_RGB32:
3559 cam->vp.depth = 32;
3560 break;
3561 default:
3562 retval = -EINVAL;
3563 break;
3564 }
3565 if (retval)
3566 break;
3567
3568 /* set video size */
3569 video_size = match_videosize(vm->width, vm->height);
3570 if (video_size < 0) {
3571 retval = -EINVAL;
3572 break;
3573 }
3574 if (video_size != cam->video_size) {
3575 cam->video_size = video_size;
3576
3577 /* video size is changing, reset the subcapture area */
3578 memset(&cam->vc, 0, sizeof(cam->vc));
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003579
Linus Torvalds1da177e2005-04-16 15:20:36 -07003580 set_vw_size(cam);
3581 cam->cmd_queue |= COMMAND_SETFORMAT;
3582 dispatch_commands(cam);
3583 }
3584 /* according to v4l-spec we must start streaming here */
3585 cam->mmap_kludge = 1;
3586 retval = capture_frame(cam, vm);
3587
3588 break;
3589 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003590
Linus Torvalds1da177e2005-04-16 15:20:36 -07003591 case VIDIOCSYNC:
3592 {
3593 int *frame = arg;
3594
3595 //DBG("VIDIOCSYNC: %d\n", *frame);
3596
3597 if (*frame<0 || *frame >= FRAME_NUM) {
3598 retval = -EINVAL;
3599 break;
3600 }
3601
3602 switch (cam->frame[*frame].state) {
3603 case FRAME_UNUSED:
3604 case FRAME_READY:
3605 case FRAME_GRABBING:
3606 DBG("sync to unused frame %d\n", *frame);
3607 retval = -EINVAL;
3608 break;
3609
3610 case FRAME_DONE:
3611 cam->frame[*frame].state = FRAME_UNUSED;
3612 //DBG("VIDIOCSYNC: %d synced\n", *frame);
3613 break;
3614 }
3615 if (retval == -EINTR) {
3616 /* FIXME - xawtv does not handle this nice */
3617 retval = 0;
3618 }
3619 break;
3620 }
3621
3622 case VIDIOCGCAPTURE:
3623 {
3624 struct video_capture *vc = arg;
3625
3626 DBG("VIDIOCGCAPTURE\n");
3627
3628 *vc = cam->vc;
3629
3630 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003631 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003632
3633 case VIDIOCSCAPTURE:
3634 {
3635 struct video_capture *vc = arg;
3636
3637 DBG("VIDIOCSCAPTURE\n");
3638
3639 if (vc->decimation != 0) { /* How should this be used? */
3640 retval = -EINVAL;
3641 break;
3642 }
3643 if (vc->flags != 0) { /* Even/odd grab not supported */
3644 retval = -EINVAL;
3645 break;
3646 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003647
Linus Torvalds1da177e2005-04-16 15:20:36 -07003648 /* Clip to the resolution we can set for the ROI
3649 (every 8 columns and 4 rows) */
3650 vc->x = vc->x & ~(__u32)7;
3651 vc->y = vc->y & ~(__u32)3;
3652 vc->width = vc->width & ~(__u32)7;
3653 vc->height = vc->height & ~(__u32)3;
3654
3655 if(vc->width == 0 || vc->height == 0 ||
3656 vc->x + vc->width > cam->vw.width ||
3657 vc->y + vc->height > cam->vw.height) {
3658 retval = -EINVAL;
3659 break;
3660 }
3661
3662 DBG("%d,%d/%dx%d\n", vc->x,vc->y,vc->width, vc->height);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003663
Ingo Molnar3593cab2006-02-07 06:49:14 -02003664 mutex_lock(&cam->param_lock);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003665
Linus Torvalds1da177e2005-04-16 15:20:36 -07003666 cam->vc.x = vc->x;
3667 cam->vc.y = vc->y;
3668 cam->vc.width = vc->width;
3669 cam->vc.height = vc->height;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003670
Linus Torvalds1da177e2005-04-16 15:20:36 -07003671 set_vw_size(cam);
3672 cam->cmd_queue |= COMMAND_SETFORMAT;
3673
Ingo Molnar3593cab2006-02-07 06:49:14 -02003674 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003675
3676 /* setformat ignored by camera during streaming,
3677 * so stop/dispatch/start */
3678 dispatch_commands(cam);
3679 break;
3680 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003681
Linus Torvalds1da177e2005-04-16 15:20:36 -07003682 case VIDIOCGUNIT:
3683 {
3684 struct video_unit *vu = arg;
3685
3686 DBG("VIDIOCGUNIT\n");
3687
3688 vu->video = cam->vdev.minor;
3689 vu->vbi = VIDEO_NO_UNIT;
3690 vu->radio = VIDEO_NO_UNIT;
3691 vu->audio = VIDEO_NO_UNIT;
3692 vu->teletext = VIDEO_NO_UNIT;
3693
3694 break;
3695 }
3696
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003697
Linus Torvalds1da177e2005-04-16 15:20:36 -07003698 /* pointless to implement overlay with this camera */
3699 case VIDIOCCAPTURE:
3700 case VIDIOCGFBUF:
3701 case VIDIOCSFBUF:
3702 case VIDIOCKEY:
3703 /* tuner interface - we have none */
3704 case VIDIOCGTUNER:
3705 case VIDIOCSTUNER:
3706 case VIDIOCGFREQ:
3707 case VIDIOCSFREQ:
3708 /* audio interface - we have none */
3709 case VIDIOCGAUDIO:
3710 case VIDIOCSAUDIO:
3711 retval = -EINVAL;
3712 break;
3713 default:
3714 retval = -ENOIOCTLCMD;
3715 break;
3716 }
3717
Ingo Molnar3593cab2006-02-07 06:49:14 -02003718 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003719 return retval;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003720}
Linus Torvalds1da177e2005-04-16 15:20:36 -07003721
Hans Verkuil069b7472008-12-30 07:04:34 -03003722static long cpia_ioctl(struct file *file,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003723 unsigned int cmd, unsigned long arg)
3724{
Hans Verkuilf473bf72008-11-01 08:25:11 -03003725 return video_usercopy(file, cmd, arg, cpia_do_ioctl);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003726}
3727
3728
3729/* FIXME */
3730static int cpia_mmap(struct file *file, struct vm_area_struct *vma)
3731{
3732 struct video_device *dev = file->private_data;
3733 unsigned long start = vma->vm_start;
3734 unsigned long size = vma->vm_end - vma->vm_start;
3735 unsigned long page, pos;
Hans Verkuil601e9442008-08-23 07:24:07 -03003736 struct cam_data *cam = video_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003737 int retval;
3738
3739 if (!cam || !cam->ops)
3740 return -ENODEV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003741
Linus Torvalds1da177e2005-04-16 15:20:36 -07003742 DBG("cpia_mmap: %ld\n", size);
3743
3744 if (size > FRAME_NUM*CPIA_MAX_FRAME_SIZE)
3745 return -EINVAL;
3746
3747 if (!cam || !cam->ops)
3748 return -ENODEV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003749
Linus Torvalds1da177e2005-04-16 15:20:36 -07003750 /* make this _really_ smp-safe */
Ingo Molnar3593cab2006-02-07 06:49:14 -02003751 if (mutex_lock_interruptible(&cam->busy_lock))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003752 return -EINTR;
3753
3754 if (!cam->frame_buf) { /* we do lazy allocation */
3755 if ((retval = allocate_frame_buf(cam))) {
Ingo Molnar3593cab2006-02-07 06:49:14 -02003756 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003757 return retval;
3758 }
3759 }
3760
3761 pos = (unsigned long)(cam->frame_buf);
3762 while (size > 0) {
3763 page = vmalloc_to_pfn((void *)pos);
3764 if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) {
Ingo Molnar3593cab2006-02-07 06:49:14 -02003765 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003766 return -EAGAIN;
3767 }
3768 start += PAGE_SIZE;
3769 pos += PAGE_SIZE;
3770 if (size > PAGE_SIZE)
3771 size -= PAGE_SIZE;
3772 else
3773 size = 0;
3774 }
3775
3776 DBG("cpia_mmap: %ld\n", size);
Ingo Molnar3593cab2006-02-07 06:49:14 -02003777 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003778
3779 return 0;
3780}
3781
Hans Verkuilbec43662008-12-30 06:58:20 -03003782static const struct v4l2_file_operations cpia_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003783 .owner = THIS_MODULE,
3784 .open = cpia_open,
3785 .release = cpia_close,
3786 .read = cpia_read,
3787 .mmap = cpia_mmap,
3788 .ioctl = cpia_ioctl,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003789};
3790
3791static struct video_device cpia_template = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003792 .name = "CPiA Camera",
Linus Torvalds1da177e2005-04-16 15:20:36 -07003793 .fops = &cpia_fops,
Hans Verkuilaa5e90a2008-08-23 06:23:55 -03003794 .release = video_device_release_empty,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003795};
3796
3797/* initialise cam_data structure */
3798static void reset_camera_struct(struct cam_data *cam)
3799{
3800 /* The following parameter values are the defaults from
3801 * "Software Developer's Guide for CPiA Cameras". Any changes
3802 * to the defaults are noted in comments. */
3803 cam->params.colourParams.brightness = 50;
3804 cam->params.colourParams.contrast = 48;
3805 cam->params.colourParams.saturation = 50;
3806 cam->params.exposure.gainMode = 4;
3807 cam->params.exposure.expMode = 2; /* AEC */
3808 cam->params.exposure.compMode = 1;
3809 cam->params.exposure.centreWeight = 1;
3810 cam->params.exposure.gain = 0;
3811 cam->params.exposure.fineExp = 0;
3812 cam->params.exposure.coarseExpLo = 185;
3813 cam->params.exposure.coarseExpHi = 0;
3814 cam->params.exposure.redComp = COMP_RED;
3815 cam->params.exposure.green1Comp = COMP_GREEN1;
3816 cam->params.exposure.green2Comp = COMP_GREEN2;
3817 cam->params.exposure.blueComp = COMP_BLUE;
3818 cam->params.colourBalance.balanceMode = 2; /* ACB */
3819 cam->params.colourBalance.redGain = 32;
3820 cam->params.colourBalance.greenGain = 6;
3821 cam->params.colourBalance.blueGain = 92;
3822 cam->params.apcor.gain1 = 0x18;
3823 cam->params.apcor.gain2 = 0x16;
3824 cam->params.apcor.gain4 = 0x24;
3825 cam->params.apcor.gain8 = 0x34;
3826 cam->params.flickerControl.flickerMode = 0;
3827 cam->params.flickerControl.disabled = 1;
3828
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003829 cam->params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07003830 flicker_jumps[cam->mainsFreq]
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003831 [cam->params.sensorFps.baserate]
3832 [cam->params.sensorFps.divisor];
3833 cam->params.flickerControl.allowableOverExposure =
Linus Torvalds1da177e2005-04-16 15:20:36 -07003834 -find_over_exposure(cam->params.colourParams.brightness);
3835 cam->params.vlOffset.gain1 = 20;
3836 cam->params.vlOffset.gain2 = 24;
3837 cam->params.vlOffset.gain4 = 26;
3838 cam->params.vlOffset.gain8 = 26;
3839 cam->params.compressionParams.hysteresis = 3;
3840 cam->params.compressionParams.threshMax = 11;
3841 cam->params.compressionParams.smallStep = 1;
3842 cam->params.compressionParams.largeStep = 3;
3843 cam->params.compressionParams.decimationHysteresis = 2;
3844 cam->params.compressionParams.frDiffStepThresh = 5;
3845 cam->params.compressionParams.qDiffStepThresh = 3;
3846 cam->params.compressionParams.decimationThreshMod = 2;
3847 /* End of default values from Software Developer's Guide */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003848
Linus Torvalds1da177e2005-04-16 15:20:36 -07003849 cam->transfer_rate = 0;
3850 cam->exposure_status = EXPOSURE_NORMAL;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003851
Linus Torvalds1da177e2005-04-16 15:20:36 -07003852 /* Set Sensor FPS to 15fps. This seems better than 30fps
3853 * for indoor lighting. */
3854 cam->params.sensorFps.divisor = 1;
3855 cam->params.sensorFps.baserate = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003856
Linus Torvalds1da177e2005-04-16 15:20:36 -07003857 cam->params.yuvThreshold.yThreshold = 6; /* From windows driver */
3858 cam->params.yuvThreshold.uvThreshold = 6; /* From windows driver */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003859
Linus Torvalds1da177e2005-04-16 15:20:36 -07003860 cam->params.format.subSample = SUBSAMPLE_422;
3861 cam->params.format.yuvOrder = YUVORDER_YUYV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003862
Linus Torvalds1da177e2005-04-16 15:20:36 -07003863 cam->params.compression.mode = CPIA_COMPRESSION_AUTO;
3864 cam->params.compressionTarget.frTargeting =
3865 CPIA_COMPRESSION_TARGET_QUALITY;
3866 cam->params.compressionTarget.targetFR = 15; /* From windows driver */
3867 cam->params.compressionTarget.targetQ = 5; /* From windows driver */
3868
3869 cam->params.qx3.qx3_detected = 0;
3870 cam->params.qx3.toplight = 0;
3871 cam->params.qx3.bottomlight = 0;
3872 cam->params.qx3.button = 0;
3873 cam->params.qx3.cradled = 0;
3874
3875 cam->video_size = VIDEOSIZE_CIF;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003876
Linus Torvalds1da177e2005-04-16 15:20:36 -07003877 cam->vp.colour = 32768; /* 50% */
3878 cam->vp.hue = 32768; /* 50% */
3879 cam->vp.brightness = 32768; /* 50% */
3880 cam->vp.contrast = 32768; /* 50% */
3881 cam->vp.whiteness = 0; /* not used -> grayscale only */
3882 cam->vp.depth = 24; /* to be set by user */
3883 cam->vp.palette = VIDEO_PALETTE_RGB24; /* to be set by user */
3884
3885 cam->vc.x = 0;
3886 cam->vc.y = 0;
3887 cam->vc.width = 0;
3888 cam->vc.height = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003889
Linus Torvalds1da177e2005-04-16 15:20:36 -07003890 cam->vw.x = 0;
3891 cam->vw.y = 0;
3892 set_vw_size(cam);
3893 cam->vw.chromakey = 0;
3894 cam->vw.flags = 0;
3895 cam->vw.clipcount = 0;
3896 cam->vw.clips = NULL;
3897
3898 cam->cmd_queue = COMMAND_NONE;
3899 cam->first_frame = 1;
3900
3901 return;
3902}
3903
3904/* initialize cam_data structure */
3905static void init_camera_struct(struct cam_data *cam,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003906 struct cpia_camera_ops *ops )
Linus Torvalds1da177e2005-04-16 15:20:36 -07003907{
3908 int i;
3909
3910 /* Default everything to 0 */
3911 memset(cam, 0, sizeof(struct cam_data));
3912
3913 cam->ops = ops;
Ingo Molnar3593cab2006-02-07 06:49:14 -02003914 mutex_init(&cam->param_lock);
3915 mutex_init(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003916
3917 reset_camera_struct(cam);
3918
3919 cam->proc_entry = NULL;
3920
3921 memcpy(&cam->vdev, &cpia_template, sizeof(cpia_template));
Hans Verkuil601e9442008-08-23 07:24:07 -03003922 video_set_drvdata(&cam->vdev, cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003923
Linus Torvalds1da177e2005-04-16 15:20:36 -07003924 cam->curframe = 0;
3925 for (i = 0; i < FRAME_NUM; i++) {
3926 cam->frame[i].width = 0;
3927 cam->frame[i].height = 0;
3928 cam->frame[i].state = FRAME_UNUSED;
3929 cam->frame[i].data = NULL;
3930 }
3931 cam->decompressed_frame.width = 0;
3932 cam->decompressed_frame.height = 0;
3933 cam->decompressed_frame.state = FRAME_UNUSED;
3934 cam->decompressed_frame.data = NULL;
3935}
3936
3937struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel)
3938{
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003939 struct cam_data *camera;
3940
Linus Torvalds1da177e2005-04-16 15:20:36 -07003941 if ((camera = kmalloc(sizeof(struct cam_data), GFP_KERNEL)) == NULL)
3942 return NULL;
3943
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003944
Linus Torvalds1da177e2005-04-16 15:20:36 -07003945 init_camera_struct( camera, ops );
3946 camera->lowlevel_data = lowlevel;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003947
Linus Torvalds1da177e2005-04-16 15:20:36 -07003948 /* register v4l device */
Hans Verkuildc60de32008-09-03 17:11:58 -03003949 if (video_register_device(&camera->vdev, VFL_TYPE_GRABBER, video_nr) < 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003950 kfree(camera);
3951 printk(KERN_DEBUG "video_register_device failed\n");
3952 return NULL;
3953 }
3954
3955 /* get version information from camera: open/reset/close */
3956
3957 /* open cpia */
3958 if (camera->ops->open(camera->lowlevel_data))
3959 return camera;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003960
Linus Torvalds1da177e2005-04-16 15:20:36 -07003961 /* reset the camera */
3962 if (reset_camera(camera) != 0) {
3963 camera->ops->close(camera->lowlevel_data);
3964 return camera;
3965 }
3966
3967 /* close cpia */
3968 camera->ops->close(camera->lowlevel_data);
3969
3970#ifdef CONFIG_PROC_FS
3971 create_proc_cpia_cam(camera);
3972#endif
3973
3974 printk(KERN_INFO " CPiA Version: %d.%02d (%d.%d)\n",
3975 camera->params.version.firmwareVersion,
3976 camera->params.version.firmwareRevision,
3977 camera->params.version.vcVersion,
3978 camera->params.version.vcRevision);
3979 printk(KERN_INFO " CPiA PnP-ID: %04x:%04x:%04x\n",
3980 camera->params.pnpID.vendor,
3981 camera->params.pnpID.product,
3982 camera->params.pnpID.deviceRevision);
3983 printk(KERN_INFO " VP-Version: %d.%d %04x\n",
3984 camera->params.vpVersion.vpVersion,
3985 camera->params.vpVersion.vpRevision,
3986 camera->params.vpVersion.cameraHeadID);
3987
3988 return camera;
3989}
3990
3991void cpia_unregister_camera(struct cam_data *cam)
3992{
3993 DBG("unregistering video\n");
3994 video_unregister_device(&cam->vdev);
3995 if (cam->open_count) {
3996 put_cam(cam->ops);
3997 DBG("camera open -- setting ops to NULL\n");
3998 cam->ops = NULL;
3999 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03004000
Linus Torvalds1da177e2005-04-16 15:20:36 -07004001#ifdef CONFIG_PROC_FS
Hans Verkuilc6330fb2008-10-19 18:54:26 -03004002 DBG("destroying /proc/cpia/video%d\n", cam->vdev.num);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004003 destroy_proc_cpia_cam(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03004004#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07004005 if (!cam->open_count) {
4006 DBG("freeing camera\n");
4007 kfree(cam);
4008 }
4009}
4010
4011static int __init cpia_init(void)
4012{
4013 printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT,
4014 CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER);
4015
4016 printk(KERN_WARNING "Since in-kernel colorspace conversion is not "
4017 "allowed, it is disabled by default now. Users should fix the "
4018 "applications in case they don't work without conversion "
4019 "reenabled by setting the 'colorspace_conv' module "
Randy Dunlap94190452006-03-27 16:18:25 -03004020 "parameter to 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004021
4022#ifdef CONFIG_PROC_FS
4023 proc_cpia_create();
4024#endif
4025
Linus Torvalds1da177e2005-04-16 15:20:36 -07004026 return 0;
4027}
4028
4029static void __exit cpia_exit(void)
4030{
4031#ifdef CONFIG_PROC_FS
4032 proc_cpia_destroy();
4033#endif
4034}
4035
4036module_init(cpia_init);
4037module_exit(cpia_exit);
4038
4039/* Exported symbols for modules. */
4040
4041EXPORT_SYMBOL(cpia_register_camera);
4042EXPORT_SYMBOL(cpia_unregister_camera);