blob: d6f2653afb6cc05048af79e674ebfcf6c6420958 [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{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001345 struct proc_dir_entry *ent;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001346
Linus Torvalds1da177e2005-04-16 15:20:36 -07001347 if (!cpia_proc_root || !cam)
1348 return;
1349
Laurent Pinchart38c7c032009-11-27 13:57:15 -03001350 ent = create_proc_entry(video_device_node_name(&cam->vdev),
1351 S_IFREG|S_IRUGO|S_IWUSR, cpia_proc_root);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001352 if (!ent)
1353 return;
1354
1355 ent->data = cam;
1356 ent->read_proc = cpia_read_proc;
1357 ent->write_proc = cpia_write_proc;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001358 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359 size of the proc entry is 3736 bytes for the standard webcam;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001360 the extra features of the QX3 microscope add 189 bytes.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361 (we have not yet probed the camera to see which type it is).
1362 */
1363 ent->size = 3736 + 189;
1364 cam->proc_entry = ent;
1365}
1366
1367static void destroy_proc_cpia_cam(struct cam_data *cam)
1368{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001369 if (!cam || !cam->proc_entry)
1370 return;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001371
Laurent Pinchart38c7c032009-11-27 13:57:15 -03001372 remove_proc_entry(video_device_node_name(&cam->vdev), cpia_proc_root);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373 cam->proc_entry = NULL;
1374}
1375
1376static void proc_cpia_create(void)
1377{
Al Viro66600222005-09-28 22:32:57 +01001378 cpia_proc_root = proc_mkdir("cpia", NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379
Alexey Dobriyan99b76232009-03-25 22:48:06 +03001380 if (!cpia_proc_root)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001381 LOG("Unable to initialise /proc/cpia\n");
1382}
1383
1384static void __exit proc_cpia_destroy(void)
1385{
1386 remove_proc_entry("cpia", NULL);
1387}
1388#endif /* CONFIG_PROC_FS */
1389
1390/* ----------------------- debug functions ---------------------- */
1391
1392#define printstatus(cam) \
1393 DBG("%02x %02x %02x %02x %02x %02x %02x %02x\n",\
1394 cam->params.status.systemState, cam->params.status.grabState, \
1395 cam->params.status.streamState, cam->params.status.fatalError, \
1396 cam->params.status.cmdError, cam->params.status.debugFlags, \
1397 cam->params.status.vpStatus, cam->params.status.errorCode);
1398
1399/* ----------------------- v4l helpers -------------------------- */
1400
1401/* supported frame palettes and depths */
1402static inline int valid_mode(u16 palette, u16 depth)
1403{
1404 if ((palette == VIDEO_PALETTE_YUV422 && depth == 16) ||
1405 (palette == VIDEO_PALETTE_YUYV && depth == 16))
1406 return 1;
1407
1408 if (colorspace_conv)
1409 return (palette == VIDEO_PALETTE_GREY && depth == 8) ||
1410 (palette == VIDEO_PALETTE_RGB555 && depth == 16) ||
1411 (palette == VIDEO_PALETTE_RGB565 && depth == 16) ||
1412 (palette == VIDEO_PALETTE_RGB24 && depth == 24) ||
1413 (palette == VIDEO_PALETTE_RGB32 && depth == 32) ||
1414 (palette == VIDEO_PALETTE_UYVY && depth == 16);
1415
1416 return 0;
1417}
1418
1419static int match_videosize( int width, int height )
1420{
1421 /* return the best match, where 'best' is as always
1422 * the largest that is not bigger than what is requested. */
1423 if (width>=352 && height>=288)
1424 return VIDEOSIZE_352_288; /* CIF */
1425
1426 if (width>=320 && height>=240)
1427 return VIDEOSIZE_320_240; /* SIF */
1428
1429 if (width>=288 && height>=216)
1430 return VIDEOSIZE_288_216;
1431
1432 if (width>=256 && height>=192)
1433 return VIDEOSIZE_256_192;
1434
1435 if (width>=224 && height>=168)
1436 return VIDEOSIZE_224_168;
1437
1438 if (width>=192 && height>=144)
1439 return VIDEOSIZE_192_144;
1440
1441 if (width>=176 && height>=144)
1442 return VIDEOSIZE_176_144; /* QCIF */
1443
1444 if (width>=160 && height>=120)
1445 return VIDEOSIZE_160_120; /* QSIF */
1446
1447 if (width>=128 && height>=96)
1448 return VIDEOSIZE_128_96;
1449
1450 if (width>=88 && height>=72)
1451 return VIDEOSIZE_88_72;
1452
1453 if (width>=64 && height>=48)
1454 return VIDEOSIZE_64_48;
1455
1456 if (width>=48 && height>=48)
1457 return VIDEOSIZE_48_48;
1458
1459 return -1;
1460}
1461
1462/* these are the capture sizes we support */
1463static void set_vw_size(struct cam_data *cam)
1464{
1465 /* the col/row/start/end values are the result of simple math */
1466 /* study the SetROI-command in cpia developers guide p 2-22 */
1467 /* streamStartLine is set to the recommended value in the cpia */
1468 /* developers guide p 3-37 */
1469 switch(cam->video_size) {
1470 case VIDEOSIZE_CIF:
1471 cam->vw.width = 352;
1472 cam->vw.height = 288;
1473 cam->params.format.videoSize=VIDEOSIZE_CIF;
1474 cam->params.roi.colStart=0;
1475 cam->params.roi.rowStart=0;
1476 cam->params.streamStartLine = 120;
1477 break;
1478 case VIDEOSIZE_SIF:
1479 cam->vw.width = 320;
1480 cam->vw.height = 240;
1481 cam->params.format.videoSize=VIDEOSIZE_CIF;
1482 cam->params.roi.colStart=2;
1483 cam->params.roi.rowStart=6;
1484 cam->params.streamStartLine = 120;
1485 break;
1486 case VIDEOSIZE_288_216:
1487 cam->vw.width = 288;
1488 cam->vw.height = 216;
1489 cam->params.format.videoSize=VIDEOSIZE_CIF;
1490 cam->params.roi.colStart=4;
1491 cam->params.roi.rowStart=9;
1492 cam->params.streamStartLine = 120;
1493 break;
1494 case VIDEOSIZE_256_192:
1495 cam->vw.width = 256;
1496 cam->vw.height = 192;
1497 cam->params.format.videoSize=VIDEOSIZE_CIF;
1498 cam->params.roi.colStart=6;
1499 cam->params.roi.rowStart=12;
1500 cam->params.streamStartLine = 120;
1501 break;
1502 case VIDEOSIZE_224_168:
1503 cam->vw.width = 224;
1504 cam->vw.height = 168;
1505 cam->params.format.videoSize=VIDEOSIZE_CIF;
1506 cam->params.roi.colStart=8;
1507 cam->params.roi.rowStart=15;
1508 cam->params.streamStartLine = 120;
1509 break;
1510 case VIDEOSIZE_192_144:
1511 cam->vw.width = 192;
1512 cam->vw.height = 144;
1513 cam->params.format.videoSize=VIDEOSIZE_CIF;
1514 cam->params.roi.colStart=10;
1515 cam->params.roi.rowStart=18;
1516 cam->params.streamStartLine = 120;
1517 break;
1518 case VIDEOSIZE_QCIF:
1519 cam->vw.width = 176;
1520 cam->vw.height = 144;
1521 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1522 cam->params.roi.colStart=0;
1523 cam->params.roi.rowStart=0;
1524 cam->params.streamStartLine = 60;
1525 break;
1526 case VIDEOSIZE_QSIF:
1527 cam->vw.width = 160;
1528 cam->vw.height = 120;
1529 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1530 cam->params.roi.colStart=1;
1531 cam->params.roi.rowStart=3;
1532 cam->params.streamStartLine = 60;
1533 break;
1534 case VIDEOSIZE_128_96:
1535 cam->vw.width = 128;
1536 cam->vw.height = 96;
1537 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1538 cam->params.roi.colStart=3;
1539 cam->params.roi.rowStart=6;
1540 cam->params.streamStartLine = 60;
1541 break;
1542 case VIDEOSIZE_88_72:
1543 cam->vw.width = 88;
1544 cam->vw.height = 72;
1545 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1546 cam->params.roi.colStart=5;
1547 cam->params.roi.rowStart=9;
1548 cam->params.streamStartLine = 60;
1549 break;
1550 case VIDEOSIZE_64_48:
1551 cam->vw.width = 64;
1552 cam->vw.height = 48;
1553 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1554 cam->params.roi.colStart=7;
1555 cam->params.roi.rowStart=12;
1556 cam->params.streamStartLine = 60;
1557 break;
1558 case VIDEOSIZE_48_48:
1559 cam->vw.width = 48;
1560 cam->vw.height = 48;
1561 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1562 cam->params.roi.colStart=8;
1563 cam->params.roi.rowStart=6;
1564 cam->params.streamStartLine = 60;
1565 break;
1566 default:
1567 LOG("bad videosize value: %d\n", cam->video_size);
1568 return;
1569 }
1570
1571 if(cam->vc.width == 0)
1572 cam->vc.width = cam->vw.width;
1573 if(cam->vc.height == 0)
1574 cam->vc.height = cam->vw.height;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001575
Linus Torvalds1da177e2005-04-16 15:20:36 -07001576 cam->params.roi.colStart += cam->vc.x >> 3;
1577 cam->params.roi.colEnd = cam->params.roi.colStart +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001578 (cam->vc.width >> 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001579 cam->params.roi.rowStart += cam->vc.y >> 2;
1580 cam->params.roi.rowEnd = cam->params.roi.rowStart +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001581 (cam->vc.height >> 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001582
1583 return;
1584}
1585
1586static int allocate_frame_buf(struct cam_data *cam)
1587{
1588 int i;
1589
1590 cam->frame_buf = rvmalloc(FRAME_NUM * CPIA_MAX_FRAME_SIZE);
1591 if (!cam->frame_buf)
1592 return -ENOBUFS;
1593
1594 for (i = 0; i < FRAME_NUM; i++)
1595 cam->frame[i].data = cam->frame_buf + i * CPIA_MAX_FRAME_SIZE;
1596
1597 return 0;
1598}
1599
1600static int free_frame_buf(struct cam_data *cam)
1601{
1602 int i;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001603
Linus Torvalds1da177e2005-04-16 15:20:36 -07001604 rvfree(cam->frame_buf, FRAME_NUM*CPIA_MAX_FRAME_SIZE);
1605 cam->frame_buf = NULL;
1606 for (i=0; i < FRAME_NUM; i++)
1607 cam->frame[i].data = NULL;
1608
1609 return 0;
1610}
1611
1612
1613static inline void free_frames(struct cpia_frame frame[FRAME_NUM])
1614{
1615 int i;
1616
1617 for (i=0; i < FRAME_NUM; i++)
1618 frame[i].state = FRAME_UNUSED;
1619 return;
1620}
1621
1622/**********************************************************************
1623 *
1624 * General functions
1625 *
1626 **********************************************************************/
1627/* send an arbitrary command to the camera */
1628static int do_command(struct cam_data *cam, u16 command, u8 a, u8 b, u8 c, u8 d)
1629{
1630 int retval, datasize;
1631 u8 cmd[8], data[8];
1632
1633 switch(command) {
1634 case CPIA_COMMAND_GetCPIAVersion:
1635 case CPIA_COMMAND_GetPnPID:
1636 case CPIA_COMMAND_GetCameraStatus:
1637 case CPIA_COMMAND_GetVPVersion:
1638 datasize=8;
1639 break;
1640 case CPIA_COMMAND_GetColourParams:
1641 case CPIA_COMMAND_GetColourBalance:
1642 case CPIA_COMMAND_GetExposure:
Ingo Molnar3593cab2006-02-07 06:49:14 -02001643 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001644 datasize=8;
1645 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001646 case CPIA_COMMAND_ReadMCPorts:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001647 case CPIA_COMMAND_ReadVCRegs:
1648 datasize = 4;
1649 break;
1650 default:
1651 datasize=0;
1652 break;
1653 }
1654
1655 cmd[0] = command>>8;
1656 cmd[1] = command&0xff;
1657 cmd[2] = a;
1658 cmd[3] = b;
1659 cmd[4] = c;
1660 cmd[5] = d;
1661 cmd[6] = datasize;
1662 cmd[7] = 0;
1663
1664 retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
1665 if (retval) {
1666 DBG("%x - failed, retval=%d\n", command, retval);
1667 if (command == CPIA_COMMAND_GetColourParams ||
1668 command == CPIA_COMMAND_GetColourBalance ||
1669 command == CPIA_COMMAND_GetExposure)
Ingo Molnar3593cab2006-02-07 06:49:14 -02001670 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001671 } else {
1672 switch(command) {
1673 case CPIA_COMMAND_GetCPIAVersion:
1674 cam->params.version.firmwareVersion = data[0];
1675 cam->params.version.firmwareRevision = data[1];
1676 cam->params.version.vcVersion = data[2];
1677 cam->params.version.vcRevision = data[3];
1678 break;
1679 case CPIA_COMMAND_GetPnPID:
1680 cam->params.pnpID.vendor = data[0]+(((u16)data[1])<<8);
1681 cam->params.pnpID.product = data[2]+(((u16)data[3])<<8);
1682 cam->params.pnpID.deviceRevision =
1683 data[4]+(((u16)data[5])<<8);
1684 break;
1685 case CPIA_COMMAND_GetCameraStatus:
1686 cam->params.status.systemState = data[0];
1687 cam->params.status.grabState = data[1];
1688 cam->params.status.streamState = data[2];
1689 cam->params.status.fatalError = data[3];
1690 cam->params.status.cmdError = data[4];
1691 cam->params.status.debugFlags = data[5];
1692 cam->params.status.vpStatus = data[6];
1693 cam->params.status.errorCode = data[7];
1694 break;
1695 case CPIA_COMMAND_GetVPVersion:
1696 cam->params.vpVersion.vpVersion = data[0];
1697 cam->params.vpVersion.vpRevision = data[1];
1698 cam->params.vpVersion.cameraHeadID =
1699 data[2]+(((u16)data[3])<<8);
1700 break;
1701 case CPIA_COMMAND_GetColourParams:
1702 cam->params.colourParams.brightness = data[0];
1703 cam->params.colourParams.contrast = data[1];
1704 cam->params.colourParams.saturation = data[2];
Ingo Molnar3593cab2006-02-07 06:49:14 -02001705 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001706 break;
1707 case CPIA_COMMAND_GetColourBalance:
1708 cam->params.colourBalance.redGain = data[0];
1709 cam->params.colourBalance.greenGain = data[1];
1710 cam->params.colourBalance.blueGain = data[2];
Ingo Molnar3593cab2006-02-07 06:49:14 -02001711 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001712 break;
1713 case CPIA_COMMAND_GetExposure:
1714 cam->params.exposure.gain = data[0];
1715 cam->params.exposure.fineExp = data[1];
1716 cam->params.exposure.coarseExpLo = data[2];
1717 cam->params.exposure.coarseExpHi = data[3];
1718 cam->params.exposure.redComp = data[4];
1719 cam->params.exposure.green1Comp = data[5];
1720 cam->params.exposure.green2Comp = data[6];
1721 cam->params.exposure.blueComp = data[7];
Ingo Molnar3593cab2006-02-07 06:49:14 -02001722 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001723 break;
1724
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001725 case CPIA_COMMAND_ReadMCPorts:
1726 if (!cam->params.qx3.qx3_detected)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001727 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001728 /* test button press */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001729 cam->params.qx3.button = ((data[1] & 0x02) == 0);
1730 if (cam->params.qx3.button) {
1731 /* button pressed - unlock the latch */
1732 do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xDF,0xDF,0);
1733 do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xFF,0xFF,0);
1734 }
1735
1736 /* test whether microscope is cradled */
1737 cam->params.qx3.cradled = ((data[2] & 0x40) == 0);
1738 break;
1739
1740 default:
1741 break;
1742 }
1743 }
1744 return retval;
1745}
1746
1747/* send a command to the camera with an additional data transaction */
1748static int do_command_extended(struct cam_data *cam, u16 command,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001749 u8 a, u8 b, u8 c, u8 d,
1750 u8 e, u8 f, u8 g, u8 h,
1751 u8 i, u8 j, u8 k, u8 l)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001752{
1753 int retval;
1754 u8 cmd[8], data[8];
1755
1756 cmd[0] = command>>8;
1757 cmd[1] = command&0xff;
1758 cmd[2] = a;
1759 cmd[3] = b;
1760 cmd[4] = c;
1761 cmd[5] = d;
1762 cmd[6] = 8;
1763 cmd[7] = 0;
1764 data[0] = e;
1765 data[1] = f;
1766 data[2] = g;
1767 data[3] = h;
1768 data[4] = i;
1769 data[5] = j;
1770 data[6] = k;
1771 data[7] = l;
1772
1773 retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
1774 if (retval)
1775 DBG("%x - failed\n", command);
1776
1777 return retval;
1778}
1779
1780/**********************************************************************
1781 *
1782 * Colorspace conversion
1783 *
1784 **********************************************************************/
1785#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16)
1786
1787static int convert420(unsigned char *yuv, unsigned char *rgb, int out_fmt,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001788 int linesize, int mmap_kludge)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001789{
1790 int y, u, v, r, g, b, y1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001791
Linus Torvalds1da177e2005-04-16 15:20:36 -07001792 /* Odd lines use the same u and v as the previous line.
1793 * Because of compression, it is necessary to get this
1794 * information from the decoded image. */
1795 switch(out_fmt) {
1796 case VIDEO_PALETTE_RGB555:
1797 y = (*yuv++ - 16) * 76310;
1798 y1 = (*yuv - 16) * 76310;
1799 r = ((*(rgb+1-linesize)) & 0x7c) << 1;
1800 g = ((*(rgb-linesize)) & 0xe0) >> 4 |
1801 ((*(rgb+1-linesize)) & 0x03) << 6;
1802 b = ((*(rgb-linesize)) & 0x1f) << 3;
1803 u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
1804 v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
1805 r = 104635 * v;
1806 g = -25690 * u - 53294 * v;
1807 b = 132278 * u;
1808 *rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3);
1809 *rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6);
1810 *rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3);
1811 *rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6);
1812 return 4;
1813 case VIDEO_PALETTE_RGB565:
1814 y = (*yuv++ - 16) * 76310;
1815 y1 = (*yuv - 16) * 76310;
1816 r = (*(rgb+1-linesize)) & 0xf8;
1817 g = ((*(rgb-linesize)) & 0xe0) >> 3 |
1818 ((*(rgb+1-linesize)) & 0x07) << 5;
1819 b = ((*(rgb-linesize)) & 0x1f) << 3;
1820 u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
1821 v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
1822 r = 104635 * v;
1823 g = -25690 * u - 53294 * v;
1824 b = 132278 * u;
1825 *rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3);
1826 *rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5);
1827 *rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3);
1828 *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5);
1829 return 4;
1830 break;
1831 case VIDEO_PALETTE_RGB24:
1832 case VIDEO_PALETTE_RGB32:
1833 y = (*yuv++ - 16) * 76310;
1834 y1 = (*yuv - 16) * 76310;
1835 if (mmap_kludge) {
1836 r = *(rgb+2-linesize);
1837 g = *(rgb+1-linesize);
1838 b = *(rgb-linesize);
1839 } else {
1840 r = *(rgb-linesize);
1841 g = *(rgb+1-linesize);
1842 b = *(rgb+2-linesize);
1843 }
1844 u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
1845 v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
1846 r = 104635 * v;
1847 g = -25690 * u + -53294 * v;
1848 b = 132278 * u;
1849 if (mmap_kludge) {
1850 *rgb++ = LIMIT(b+y);
1851 *rgb++ = LIMIT(g+y);
1852 *rgb++ = LIMIT(r+y);
1853 if(out_fmt == VIDEO_PALETTE_RGB32)
1854 rgb++;
1855 *rgb++ = LIMIT(b+y1);
1856 *rgb++ = LIMIT(g+y1);
1857 *rgb = LIMIT(r+y1);
1858 } else {
1859 *rgb++ = LIMIT(r+y);
1860 *rgb++ = LIMIT(g+y);
1861 *rgb++ = LIMIT(b+y);
1862 if(out_fmt == VIDEO_PALETTE_RGB32)
1863 rgb++;
1864 *rgb++ = LIMIT(r+y1);
1865 *rgb++ = LIMIT(g+y1);
1866 *rgb = LIMIT(b+y1);
1867 }
1868 if(out_fmt == VIDEO_PALETTE_RGB32)
1869 return 8;
1870 return 6;
1871 case VIDEO_PALETTE_YUV422:
1872 case VIDEO_PALETTE_YUYV:
1873 y = *yuv++;
1874 u = *(rgb+1-linesize);
1875 y1 = *yuv;
1876 v = *(rgb+3-linesize);
1877 *rgb++ = y;
1878 *rgb++ = u;
1879 *rgb++ = y1;
1880 *rgb = v;
1881 return 4;
1882 case VIDEO_PALETTE_UYVY:
1883 u = *(rgb-linesize);
1884 y = *yuv++;
1885 v = *(rgb+2-linesize);
1886 y1 = *yuv;
1887 *rgb++ = u;
1888 *rgb++ = y;
1889 *rgb++ = v;
1890 *rgb = y1;
1891 return 4;
1892 case VIDEO_PALETTE_GREY:
1893 *rgb++ = *yuv++;
1894 *rgb = *yuv;
1895 return 2;
1896 default:
1897 DBG("Empty: %d\n", out_fmt);
1898 return 0;
1899 }
1900}
1901
1902
1903static int yuvconvert(unsigned char *yuv, unsigned char *rgb, int out_fmt,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001904 int in_uyvy, int mmap_kludge)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001905{
1906 int y, u, v, r, g, b, y1;
1907
1908 switch(out_fmt) {
1909 case VIDEO_PALETTE_RGB555:
1910 case VIDEO_PALETTE_RGB565:
1911 case VIDEO_PALETTE_RGB24:
1912 case VIDEO_PALETTE_RGB32:
1913 if (in_uyvy) {
1914 u = *yuv++ - 128;
1915 y = (*yuv++ - 16) * 76310;
1916 v = *yuv++ - 128;
1917 y1 = (*yuv - 16) * 76310;
1918 } else {
1919 y = (*yuv++ - 16) * 76310;
1920 u = *yuv++ - 128;
1921 y1 = (*yuv++ - 16) * 76310;
1922 v = *yuv - 128;
1923 }
1924 r = 104635 * v;
1925 g = -25690 * u + -53294 * v;
1926 b = 132278 * u;
1927 break;
1928 default:
1929 y = *yuv++;
1930 u = *yuv++;
1931 y1 = *yuv++;
1932 v = *yuv;
1933 /* Just to avoid compiler warnings */
1934 r = 0;
1935 g = 0;
1936 b = 0;
1937 break;
1938 }
1939 switch(out_fmt) {
1940 case VIDEO_PALETTE_RGB555:
1941 *rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3);
1942 *rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6);
1943 *rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3);
1944 *rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6);
1945 return 4;
1946 case VIDEO_PALETTE_RGB565:
1947 *rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3);
1948 *rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5);
1949 *rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3);
1950 *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5);
1951 return 4;
1952 case VIDEO_PALETTE_RGB24:
1953 if (mmap_kludge) {
1954 *rgb++ = LIMIT(b+y);
1955 *rgb++ = LIMIT(g+y);
1956 *rgb++ = LIMIT(r+y);
1957 *rgb++ = LIMIT(b+y1);
1958 *rgb++ = LIMIT(g+y1);
1959 *rgb = LIMIT(r+y1);
1960 } else {
1961 *rgb++ = LIMIT(r+y);
1962 *rgb++ = LIMIT(g+y);
1963 *rgb++ = LIMIT(b+y);
1964 *rgb++ = LIMIT(r+y1);
1965 *rgb++ = LIMIT(g+y1);
1966 *rgb = LIMIT(b+y1);
1967 }
1968 return 6;
1969 case VIDEO_PALETTE_RGB32:
1970 if (mmap_kludge) {
1971 *rgb++ = LIMIT(b+y);
1972 *rgb++ = LIMIT(g+y);
1973 *rgb++ = LIMIT(r+y);
1974 rgb++;
1975 *rgb++ = LIMIT(b+y1);
1976 *rgb++ = LIMIT(g+y1);
1977 *rgb = LIMIT(r+y1);
1978 } else {
1979 *rgb++ = LIMIT(r+y);
1980 *rgb++ = LIMIT(g+y);
1981 *rgb++ = LIMIT(b+y);
1982 rgb++;
1983 *rgb++ = LIMIT(r+y1);
1984 *rgb++ = LIMIT(g+y1);
1985 *rgb = LIMIT(b+y1);
1986 }
1987 return 8;
1988 case VIDEO_PALETTE_GREY:
1989 *rgb++ = y;
1990 *rgb = y1;
1991 return 2;
1992 case VIDEO_PALETTE_YUV422:
1993 case VIDEO_PALETTE_YUYV:
1994 *rgb++ = y;
1995 *rgb++ = u;
1996 *rgb++ = y1;
1997 *rgb = v;
1998 return 4;
1999 case VIDEO_PALETTE_UYVY:
2000 *rgb++ = u;
2001 *rgb++ = y;
2002 *rgb++ = v;
2003 *rgb = y1;
2004 return 4;
2005 default:
2006 DBG("Empty: %d\n", out_fmt);
2007 return 0;
2008 }
2009}
2010
2011static int skipcount(int count, int fmt)
2012{
2013 switch(fmt) {
2014 case VIDEO_PALETTE_GREY:
2015 return count;
2016 case VIDEO_PALETTE_RGB555:
2017 case VIDEO_PALETTE_RGB565:
2018 case VIDEO_PALETTE_YUV422:
2019 case VIDEO_PALETTE_YUYV:
2020 case VIDEO_PALETTE_UYVY:
2021 return 2*count;
2022 case VIDEO_PALETTE_RGB24:
2023 return 3*count;
2024 case VIDEO_PALETTE_RGB32:
2025 return 4*count;
2026 default:
2027 return 0;
2028 }
2029}
2030
2031static int parse_picture(struct cam_data *cam, int size)
2032{
2033 u8 *obuf, *ibuf, *end_obuf;
2034 int ll, in_uyvy, compressed, decimation, even_line, origsize, out_fmt;
2035 int rows, cols, linesize, subsample_422;
2036
2037 /* make sure params don't change while we are decoding */
Ingo Molnar3593cab2006-02-07 06:49:14 -02002038 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002039
2040 obuf = cam->decompressed_frame.data;
2041 end_obuf = obuf+CPIA_MAX_FRAME_SIZE;
2042 ibuf = cam->raw_image;
2043 origsize = size;
2044 out_fmt = cam->vp.palette;
2045
2046 if ((ibuf[0] != MAGIC_0) || (ibuf[1] != MAGIC_1)) {
2047 LOG("header not found\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02002048 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002049 return -1;
2050 }
2051
2052 if ((ibuf[16] != VIDEOSIZE_QCIF) && (ibuf[16] != VIDEOSIZE_CIF)) {
2053 LOG("wrong video size\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02002054 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002055 return -1;
2056 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002057
Linus Torvalds1da177e2005-04-16 15:20:36 -07002058 if (ibuf[17] != SUBSAMPLE_420 && ibuf[17] != SUBSAMPLE_422) {
2059 LOG("illegal subtype %d\n",ibuf[17]);
Ingo Molnar3593cab2006-02-07 06:49:14 -02002060 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002061 return -1;
2062 }
2063 subsample_422 = ibuf[17] == SUBSAMPLE_422;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002064
Linus Torvalds1da177e2005-04-16 15:20:36 -07002065 if (ibuf[18] != YUVORDER_YUYV && ibuf[18] != YUVORDER_UYVY) {
2066 LOG("illegal yuvorder %d\n",ibuf[18]);
Ingo Molnar3593cab2006-02-07 06:49:14 -02002067 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002068 return -1;
2069 }
2070 in_uyvy = ibuf[18] == YUVORDER_UYVY;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002071
Linus Torvalds1da177e2005-04-16 15:20:36 -07002072 if ((ibuf[24] != cam->params.roi.colStart) ||
2073 (ibuf[25] != cam->params.roi.colEnd) ||
2074 (ibuf[26] != cam->params.roi.rowStart) ||
2075 (ibuf[27] != cam->params.roi.rowEnd)) {
2076 LOG("ROI mismatch\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02002077 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002078 return -1;
2079 }
2080 cols = 8*(ibuf[25] - ibuf[24]);
2081 rows = 4*(ibuf[27] - ibuf[26]);
2082
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002083
Linus Torvalds1da177e2005-04-16 15:20:36 -07002084 if ((ibuf[28] != NOT_COMPRESSED) && (ibuf[28] != COMPRESSED)) {
2085 LOG("illegal compression %d\n",ibuf[28]);
Ingo Molnar3593cab2006-02-07 06:49:14 -02002086 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002087 return -1;
2088 }
2089 compressed = (ibuf[28] == COMPRESSED);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002090
Linus Torvalds1da177e2005-04-16 15:20:36 -07002091 if (ibuf[29] != NO_DECIMATION && ibuf[29] != DECIMATION_ENAB) {
2092 LOG("illegal decimation %d\n",ibuf[29]);
Ingo Molnar3593cab2006-02-07 06:49:14 -02002093 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002094 return -1;
2095 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002096 decimation = (ibuf[29] == DECIMATION_ENAB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002097
2098 cam->params.yuvThreshold.yThreshold = ibuf[30];
2099 cam->params.yuvThreshold.uvThreshold = ibuf[31];
2100 cam->params.status.systemState = ibuf[32];
2101 cam->params.status.grabState = ibuf[33];
2102 cam->params.status.streamState = ibuf[34];
2103 cam->params.status.fatalError = ibuf[35];
2104 cam->params.status.cmdError = ibuf[36];
2105 cam->params.status.debugFlags = ibuf[37];
2106 cam->params.status.vpStatus = ibuf[38];
2107 cam->params.status.errorCode = ibuf[39];
2108 cam->fps = ibuf[41];
Ingo Molnar3593cab2006-02-07 06:49:14 -02002109 mutex_unlock(&cam->param_lock);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002110
Linus Torvalds1da177e2005-04-16 15:20:36 -07002111 linesize = skipcount(cols, out_fmt);
2112 ibuf += FRAME_HEADER_SIZE;
2113 size -= FRAME_HEADER_SIZE;
2114 ll = ibuf[0] | (ibuf[1] << 8);
2115 ibuf += 2;
2116 even_line = 1;
2117
2118 while (size > 0) {
2119 size -= (ll+2);
2120 if (size < 0) {
2121 LOG("Insufficient data in buffer\n");
2122 return -1;
2123 }
2124
2125 while (ll > 1) {
2126 if (!compressed || (compressed && !(*ibuf & 1))) {
2127 if(subsample_422 || even_line) {
2128 obuf += yuvconvert(ibuf, obuf, out_fmt,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002129 in_uyvy, cam->mmap_kludge);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002130 ibuf += 4;
2131 ll -= 4;
2132 } else {
2133 /* SUBSAMPLE_420 on an odd line */
2134 obuf += convert420(ibuf, obuf,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002135 out_fmt, linesize,
2136 cam->mmap_kludge);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002137 ibuf += 2;
2138 ll -= 2;
2139 }
2140 } else {
2141 /*skip compressed interval from previous frame*/
2142 obuf += skipcount(*ibuf >> 1, out_fmt);
2143 if (obuf > end_obuf) {
2144 LOG("Insufficient buffer size\n");
2145 return -1;
2146 }
2147 ++ibuf;
2148 ll--;
2149 }
2150 }
2151 if (ll == 1) {
2152 if (*ibuf != EOL) {
2153 DBG("EOL not found giving up after %d/%d"
2154 " bytes\n", origsize-size, origsize);
2155 return -1;
2156 }
2157
2158 ++ibuf; /* skip over EOL */
2159
2160 if ((size > 3) && (ibuf[0] == EOI) && (ibuf[1] == EOI) &&
2161 (ibuf[2] == EOI) && (ibuf[3] == EOI)) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002162 size -= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002163 break;
2164 }
2165
2166 if(decimation) {
2167 /* skip the odd lines for now */
2168 obuf += linesize;
2169 }
2170
2171 if (size > 1) {
2172 ll = ibuf[0] | (ibuf[1] << 8);
2173 ibuf += 2; /* skip over line length */
2174 }
2175 if(!decimation)
2176 even_line = !even_line;
2177 } else {
2178 LOG("line length was not 1 but %d after %d/%d bytes\n",
2179 ll, origsize-size, origsize);
2180 return -1;
2181 }
2182 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002183
Linus Torvalds1da177e2005-04-16 15:20:36 -07002184 if(decimation) {
2185 /* interpolate odd rows */
2186 int i, j;
2187 u8 *prev, *next;
2188 prev = cam->decompressed_frame.data;
2189 obuf = prev+linesize;
2190 next = obuf+linesize;
2191 for(i=1; i<rows-1; i+=2) {
2192 for(j=0; j<linesize; ++j) {
2193 *obuf++ = ((int)*prev++ + *next++) / 2;
2194 }
2195 prev += linesize;
2196 obuf += linesize;
2197 next += linesize;
2198 }
2199 /* last row is odd, just copy previous row */
2200 memcpy(obuf, prev, linesize);
2201 }
2202
2203 cam->decompressed_frame.count = obuf-cam->decompressed_frame.data;
2204
2205 return cam->decompressed_frame.count;
2206}
2207
2208/* InitStreamCap wrapper to select correct start line */
2209static inline int init_stream_cap(struct cam_data *cam)
2210{
2211 return do_command(cam, CPIA_COMMAND_InitStreamCap,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002212 0, cam->params.streamStartLine, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002213}
2214
2215
2216/* find_over_exposure
2217 * Finds a suitable value of OverExposure for use with SetFlickerCtrl
2218 * Some calculation is required because this value changes with the brightness
2219 * set with SetColourParameters
2220 *
2221 * Parameters: Brightness - last brightness value set with SetColourParameters
2222 *
2223 * Returns: OverExposure value to use with SetFlickerCtrl
2224 */
2225#define FLICKER_MAX_EXPOSURE 250
2226#define FLICKER_ALLOWABLE_OVER_EXPOSURE 146
2227#define FLICKER_BRIGHTNESS_CONSTANT 59
2228static int find_over_exposure(int brightness)
2229{
2230 int MaxAllowableOverExposure, OverExposure;
2231
2232 MaxAllowableOverExposure = FLICKER_MAX_EXPOSURE - brightness -
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002233 FLICKER_BRIGHTNESS_CONSTANT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002234
2235 if (MaxAllowableOverExposure < FLICKER_ALLOWABLE_OVER_EXPOSURE) {
2236 OverExposure = MaxAllowableOverExposure;
2237 } else {
2238 OverExposure = FLICKER_ALLOWABLE_OVER_EXPOSURE;
2239 }
2240
2241 return OverExposure;
2242}
2243#undef FLICKER_MAX_EXPOSURE
2244#undef FLICKER_ALLOWABLE_OVER_EXPOSURE
2245#undef FLICKER_BRIGHTNESS_CONSTANT
2246
2247/* update various camera modes and settings */
2248static void dispatch_commands(struct cam_data *cam)
2249{
Ingo Molnar3593cab2006-02-07 06:49:14 -02002250 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002251 if (cam->cmd_queue==COMMAND_NONE) {
Ingo Molnar3593cab2006-02-07 06:49:14 -02002252 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002253 return;
2254 }
2255 DEB_BYTE(cam->cmd_queue);
2256 DEB_BYTE(cam->cmd_queue>>8);
2257 if (cam->cmd_queue & COMMAND_SETFORMAT) {
2258 do_command(cam, CPIA_COMMAND_SetFormat,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002259 cam->params.format.videoSize,
2260 cam->params.format.subSample,
2261 cam->params.format.yuvOrder, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002262 do_command(cam, CPIA_COMMAND_SetROI,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002263 cam->params.roi.colStart, cam->params.roi.colEnd,
2264 cam->params.roi.rowStart, cam->params.roi.rowEnd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002265 cam->first_frame = 1;
2266 }
2267
2268 if (cam->cmd_queue & COMMAND_SETCOLOURPARAMS)
2269 do_command(cam, CPIA_COMMAND_SetColourParams,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002270 cam->params.colourParams.brightness,
2271 cam->params.colourParams.contrast,
2272 cam->params.colourParams.saturation, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002273
2274 if (cam->cmd_queue & COMMAND_SETAPCOR)
2275 do_command(cam, CPIA_COMMAND_SetApcor,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002276 cam->params.apcor.gain1,
2277 cam->params.apcor.gain2,
2278 cam->params.apcor.gain4,
2279 cam->params.apcor.gain8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002280
2281 if (cam->cmd_queue & COMMAND_SETVLOFFSET)
2282 do_command(cam, CPIA_COMMAND_SetVLOffset,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002283 cam->params.vlOffset.gain1,
2284 cam->params.vlOffset.gain2,
2285 cam->params.vlOffset.gain4,
2286 cam->params.vlOffset.gain8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002287
2288 if (cam->cmd_queue & COMMAND_SETEXPOSURE) {
2289 do_command_extended(cam, CPIA_COMMAND_SetExposure,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002290 cam->params.exposure.gainMode,
2291 1,
2292 cam->params.exposure.compMode,
2293 cam->params.exposure.centreWeight,
2294 cam->params.exposure.gain,
2295 cam->params.exposure.fineExp,
2296 cam->params.exposure.coarseExpLo,
2297 cam->params.exposure.coarseExpHi,
2298 cam->params.exposure.redComp,
2299 cam->params.exposure.green1Comp,
2300 cam->params.exposure.green2Comp,
2301 cam->params.exposure.blueComp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002302 if(cam->params.exposure.expMode != 1) {
2303 do_command_extended(cam, CPIA_COMMAND_SetExposure,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002304 0,
2305 cam->params.exposure.expMode,
2306 0, 0,
2307 cam->params.exposure.gain,
2308 cam->params.exposure.fineExp,
2309 cam->params.exposure.coarseExpLo,
2310 cam->params.exposure.coarseExpHi,
2311 0, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002312 }
2313 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002314
Linus Torvalds1da177e2005-04-16 15:20:36 -07002315 if (cam->cmd_queue & COMMAND_SETCOLOURBALANCE) {
2316 if (cam->params.colourBalance.balanceMode == 1) {
2317 do_command(cam, CPIA_COMMAND_SetColourBalance,
2318 1,
2319 cam->params.colourBalance.redGain,
2320 cam->params.colourBalance.greenGain,
2321 cam->params.colourBalance.blueGain);
2322 do_command(cam, CPIA_COMMAND_SetColourBalance,
2323 3, 0, 0, 0);
2324 }
2325 if (cam->params.colourBalance.balanceMode == 2) {
2326 do_command(cam, CPIA_COMMAND_SetColourBalance,
2327 2, 0, 0, 0);
2328 }
2329 if (cam->params.colourBalance.balanceMode == 3) {
2330 do_command(cam, CPIA_COMMAND_SetColourBalance,
2331 3, 0, 0, 0);
2332 }
2333 }
2334
2335 if (cam->cmd_queue & COMMAND_SETCOMPRESSIONTARGET)
2336 do_command(cam, CPIA_COMMAND_SetCompressionTarget,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002337 cam->params.compressionTarget.frTargeting,
2338 cam->params.compressionTarget.targetFR,
2339 cam->params.compressionTarget.targetQ, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002340
2341 if (cam->cmd_queue & COMMAND_SETYUVTHRESH)
2342 do_command(cam, CPIA_COMMAND_SetYUVThresh,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002343 cam->params.yuvThreshold.yThreshold,
2344 cam->params.yuvThreshold.uvThreshold, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002345
2346 if (cam->cmd_queue & COMMAND_SETCOMPRESSIONPARAMS)
2347 do_command_extended(cam, CPIA_COMMAND_SetCompressionParams,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002348 0, 0, 0, 0,
2349 cam->params.compressionParams.hysteresis,
2350 cam->params.compressionParams.threshMax,
2351 cam->params.compressionParams.smallStep,
2352 cam->params.compressionParams.largeStep,
2353 cam->params.compressionParams.decimationHysteresis,
2354 cam->params.compressionParams.frDiffStepThresh,
2355 cam->params.compressionParams.qDiffStepThresh,
2356 cam->params.compressionParams.decimationThreshMod);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002357
2358 if (cam->cmd_queue & COMMAND_SETCOMPRESSION)
2359 do_command(cam, CPIA_COMMAND_SetCompression,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002360 cam->params.compression.mode,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002361 cam->params.compression.decimation, 0, 0);
2362
2363 if (cam->cmd_queue & COMMAND_SETSENSORFPS)
2364 do_command(cam, CPIA_COMMAND_SetSensorFPS,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002365 cam->params.sensorFps.divisor,
2366 cam->params.sensorFps.baserate, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002367
2368 if (cam->cmd_queue & COMMAND_SETFLICKERCTRL)
2369 do_command(cam, CPIA_COMMAND_SetFlickerCtrl,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002370 cam->params.flickerControl.flickerMode,
2371 cam->params.flickerControl.coarseJump,
2372 abs(cam->params.flickerControl.allowableOverExposure),
2373 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002374
2375 if (cam->cmd_queue & COMMAND_SETECPTIMING)
2376 do_command(cam, CPIA_COMMAND_SetECPTiming,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002377 cam->params.ecpTiming, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002378
2379 if (cam->cmd_queue & COMMAND_PAUSE)
2380 do_command(cam, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0);
2381
2382 if (cam->cmd_queue & COMMAND_RESUME)
2383 init_stream_cap(cam);
2384
2385 if (cam->cmd_queue & COMMAND_SETLIGHTS && cam->params.qx3.qx3_detected)
2386 {
2387 int p1 = (cam->params.qx3.bottomlight == 0) << 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002388 int p2 = (cam->params.qx3.toplight == 0) << 3;
2389 do_command(cam, CPIA_COMMAND_WriteVCReg, 0x90, 0x8F, 0x50, 0);
2390 do_command(cam, CPIA_COMMAND_WriteMCPort, 2, 0, (p1|p2|0xE0), 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002391 }
2392
2393 cam->cmd_queue = COMMAND_NONE;
Ingo Molnar3593cab2006-02-07 06:49:14 -02002394 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002395 return;
2396}
2397
2398
2399
2400static void set_flicker(struct cam_params *params, volatile u32 *command_flags,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002401 int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002402{
2403 /* Everything in here is from the Windows driver */
2404#define FIRMWARE_VERSION(x,y) (params->version.firmwareVersion == (x) && \
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002405 params->version.firmwareRevision == (y))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002406/* define for compgain calculation */
2407#if 0
2408#define COMPGAIN(base, curexp, newexp) \
2409 (u8) ((((float) base - 128.0) * ((float) curexp / (float) newexp)) + 128.5)
2410#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
2411 (u16)((float)curexp * (float)(u8)(curcomp + 128) / (float)(u8)(basecomp - 128))
2412#else
2413 /* equivalent functions without floating point math */
2414#define COMPGAIN(base, curexp, newexp) \
2415 (u8)(128 + (((u32)(2*(base-128)*curexp + newexp)) / (2* newexp)) )
2416#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
2417 (u16)(((u32)(curexp * (u8)(curcomp + 128)) / (u8)(basecomp - 128)))
2418#endif
2419
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002420
Linus Torvalds1da177e2005-04-16 15:20:36 -07002421 int currentexp = params->exposure.coarseExpLo +
2422 params->exposure.coarseExpHi*256;
2423 int startexp;
2424 if (on) {
2425 int cj = params->flickerControl.coarseJump;
2426 params->flickerControl.flickerMode = 1;
2427 params->flickerControl.disabled = 0;
2428 if(params->exposure.expMode != 2)
2429 *command_flags |= COMMAND_SETEXPOSURE;
2430 params->exposure.expMode = 2;
2431 currentexp = currentexp << params->exposure.gain;
2432 params->exposure.gain = 0;
2433 /* round down current exposure to nearest value */
2434 startexp = (currentexp + ROUND_UP_EXP_FOR_FLICKER) / cj;
2435 if(startexp < 1)
2436 startexp = 1;
2437 startexp = (startexp * cj) - 1;
2438 if(FIRMWARE_VERSION(1,2))
2439 while(startexp > MAX_EXP_102)
2440 startexp -= cj;
2441 else
2442 while(startexp > MAX_EXP)
2443 startexp -= cj;
2444 params->exposure.coarseExpLo = startexp & 0xff;
2445 params->exposure.coarseExpHi = startexp >> 8;
2446 if (currentexp > startexp) {
2447 if (currentexp > (2 * startexp))
2448 currentexp = 2 * startexp;
2449 params->exposure.redComp = COMPGAIN (COMP_RED, currentexp, startexp);
2450 params->exposure.green1Comp = COMPGAIN (COMP_GREEN1, currentexp, startexp);
2451 params->exposure.green2Comp = COMPGAIN (COMP_GREEN2, currentexp, startexp);
2452 params->exposure.blueComp = COMPGAIN (COMP_BLUE, currentexp, startexp);
2453 } else {
2454 params->exposure.redComp = COMP_RED;
2455 params->exposure.green1Comp = COMP_GREEN1;
2456 params->exposure.green2Comp = COMP_GREEN2;
2457 params->exposure.blueComp = COMP_BLUE;
2458 }
2459 if(FIRMWARE_VERSION(1,2))
2460 params->exposure.compMode = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002461 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07002462 params->exposure.compMode = 1;
2463
2464 params->apcor.gain1 = 0x18;
2465 params->apcor.gain2 = 0x18;
2466 params->apcor.gain4 = 0x16;
2467 params->apcor.gain8 = 0x14;
2468 *command_flags |= COMMAND_SETAPCOR;
2469 } else {
2470 params->flickerControl.flickerMode = 0;
2471 params->flickerControl.disabled = 1;
2472 /* Coarse = average of equivalent coarse for each comp channel */
2473 startexp = EXP_FROM_COMP(COMP_RED, params->exposure.redComp, currentexp);
2474 startexp += EXP_FROM_COMP(COMP_GREEN1, params->exposure.green1Comp, currentexp);
2475 startexp += EXP_FROM_COMP(COMP_GREEN2, params->exposure.green2Comp, currentexp);
2476 startexp += EXP_FROM_COMP(COMP_BLUE, params->exposure.blueComp, currentexp);
2477 startexp = startexp >> 2;
2478 while(startexp > MAX_EXP &&
2479 params->exposure.gain < params->exposure.gainMode-1) {
2480 startexp = startexp >> 1;
2481 ++params->exposure.gain;
2482 }
2483 if(FIRMWARE_VERSION(1,2) && startexp > MAX_EXP_102)
2484 startexp = MAX_EXP_102;
2485 if(startexp > MAX_EXP)
2486 startexp = MAX_EXP;
2487 params->exposure.coarseExpLo = startexp&0xff;
2488 params->exposure.coarseExpHi = startexp >> 8;
2489 params->exposure.redComp = COMP_RED;
2490 params->exposure.green1Comp = COMP_GREEN1;
2491 params->exposure.green2Comp = COMP_GREEN2;
2492 params->exposure.blueComp = COMP_BLUE;
2493 params->exposure.compMode = 1;
2494 *command_flags |= COMMAND_SETEXPOSURE;
2495 params->apcor.gain1 = 0x18;
2496 params->apcor.gain2 = 0x16;
2497 params->apcor.gain4 = 0x24;
2498 params->apcor.gain8 = 0x34;
2499 *command_flags |= COMMAND_SETAPCOR;
2500 }
2501 params->vlOffset.gain1 = 20;
2502 params->vlOffset.gain2 = 24;
2503 params->vlOffset.gain4 = 26;
2504 params->vlOffset.gain8 = 26;
2505 *command_flags |= COMMAND_SETVLOFFSET;
2506#undef FIRMWARE_VERSION
2507#undef EXP_FROM_COMP
2508#undef COMPGAIN
2509}
2510
2511#define FIRMWARE_VERSION(x,y) (cam->params.version.firmwareVersion == (x) && \
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002512 cam->params.version.firmwareRevision == (y))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002513/* monitor the exposure and adjust the sensor frame rate if needed */
2514static void monitor_exposure(struct cam_data *cam)
2515{
2516 u8 exp_acc, bcomp, gain, coarseL, cmd[8], data[8];
2517 int retval, light_exp, dark_exp, very_dark_exp;
2518 int old_exposure, new_exposure, framerate;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002519
Linus Torvalds1da177e2005-04-16 15:20:36 -07002520 /* get necessary stats and register settings from camera */
2521 /* do_command can't handle this, so do it ourselves */
2522 cmd[0] = CPIA_COMMAND_ReadVPRegs>>8;
2523 cmd[1] = CPIA_COMMAND_ReadVPRegs&0xff;
2524 cmd[2] = 30;
2525 cmd[3] = 4;
2526 cmd[4] = 9;
2527 cmd[5] = 8;
2528 cmd[6] = 8;
2529 cmd[7] = 0;
2530 retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
2531 if (retval) {
2532 LOG("ReadVPRegs(30,4,9,8) - failed, retval=%d\n",
2533 retval);
2534 return;
2535 }
2536 exp_acc = data[0];
2537 bcomp = data[1];
2538 gain = data[2];
2539 coarseL = data[3];
2540
Ingo Molnar3593cab2006-02-07 06:49:14 -02002541 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002542 light_exp = cam->params.colourParams.brightness +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002543 TC - 50 + EXP_ACC_LIGHT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002544 if(light_exp > 255)
2545 light_exp = 255;
2546 dark_exp = cam->params.colourParams.brightness +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002547 TC - 50 - EXP_ACC_DARK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002548 if(dark_exp < 0)
2549 dark_exp = 0;
2550 very_dark_exp = dark_exp/2;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002551
Linus Torvalds1da177e2005-04-16 15:20:36 -07002552 old_exposure = cam->params.exposure.coarseExpHi * 256 +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002553 cam->params.exposure.coarseExpLo;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002554
2555 if(!cam->params.flickerControl.disabled) {
2556 /* Flicker control on */
2557 int max_comp = FIRMWARE_VERSION(1,2) ? MAX_COMP : HIGH_COMP_102;
2558 bcomp += 128; /* decode */
2559 if(bcomp >= max_comp && exp_acc < dark_exp) {
2560 /* dark */
2561 if(exp_acc < very_dark_exp) {
2562 /* very dark */
2563 if(cam->exposure_status == EXPOSURE_VERY_DARK)
2564 ++cam->exposure_count;
2565 else {
2566 cam->exposure_status = EXPOSURE_VERY_DARK;
2567 cam->exposure_count = 1;
2568 }
2569 } else {
2570 /* just dark */
2571 if(cam->exposure_status == EXPOSURE_DARK)
2572 ++cam->exposure_count;
2573 else {
2574 cam->exposure_status = EXPOSURE_DARK;
2575 cam->exposure_count = 1;
2576 }
2577 }
2578 } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) {
2579 /* light */
2580 if(old_exposure <= VERY_LOW_EXP) {
2581 /* very light */
2582 if(cam->exposure_status == EXPOSURE_VERY_LIGHT)
2583 ++cam->exposure_count;
2584 else {
2585 cam->exposure_status = EXPOSURE_VERY_LIGHT;
2586 cam->exposure_count = 1;
2587 }
2588 } else {
2589 /* just light */
2590 if(cam->exposure_status == EXPOSURE_LIGHT)
2591 ++cam->exposure_count;
2592 else {
2593 cam->exposure_status = EXPOSURE_LIGHT;
2594 cam->exposure_count = 1;
2595 }
2596 }
2597 } else {
2598 /* not dark or light */
2599 cam->exposure_status = EXPOSURE_NORMAL;
2600 }
2601 } else {
2602 /* Flicker control off */
2603 if(old_exposure >= MAX_EXP && exp_acc < dark_exp) {
2604 /* dark */
2605 if(exp_acc < very_dark_exp) {
2606 /* very dark */
2607 if(cam->exposure_status == EXPOSURE_VERY_DARK)
2608 ++cam->exposure_count;
2609 else {
2610 cam->exposure_status = EXPOSURE_VERY_DARK;
2611 cam->exposure_count = 1;
2612 }
2613 } else {
2614 /* just dark */
2615 if(cam->exposure_status == EXPOSURE_DARK)
2616 ++cam->exposure_count;
2617 else {
2618 cam->exposure_status = EXPOSURE_DARK;
2619 cam->exposure_count = 1;
2620 }
2621 }
2622 } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) {
2623 /* light */
2624 if(old_exposure <= VERY_LOW_EXP) {
2625 /* very light */
2626 if(cam->exposure_status == EXPOSURE_VERY_LIGHT)
2627 ++cam->exposure_count;
2628 else {
2629 cam->exposure_status = EXPOSURE_VERY_LIGHT;
2630 cam->exposure_count = 1;
2631 }
2632 } else {
2633 /* just light */
2634 if(cam->exposure_status == EXPOSURE_LIGHT)
2635 ++cam->exposure_count;
2636 else {
2637 cam->exposure_status = EXPOSURE_LIGHT;
2638 cam->exposure_count = 1;
2639 }
2640 }
2641 } else {
2642 /* not dark or light */
2643 cam->exposure_status = EXPOSURE_NORMAL;
2644 }
2645 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002646
Linus Torvalds1da177e2005-04-16 15:20:36 -07002647 framerate = cam->fps;
2648 if(framerate > 30 || framerate < 1)
2649 framerate = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002650
Linus Torvalds1da177e2005-04-16 15:20:36 -07002651 if(!cam->params.flickerControl.disabled) {
2652 /* Flicker control on */
2653 if((cam->exposure_status == EXPOSURE_VERY_DARK ||
2654 cam->exposure_status == EXPOSURE_DARK) &&
2655 cam->exposure_count >= DARK_TIME*framerate &&
2656 cam->params.sensorFps.divisor < 3) {
2657
2658 /* dark for too long */
2659 ++cam->params.sensorFps.divisor;
2660 cam->cmd_queue |= COMMAND_SETSENSORFPS;
2661
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002662 cam->params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07002663 flicker_jumps[cam->mainsFreq]
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002664 [cam->params.sensorFps.baserate]
2665 [cam->params.sensorFps.divisor];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002666 cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
2667
2668 new_exposure = cam->params.flickerControl.coarseJump-1;
2669 while(new_exposure < old_exposure/2)
2670 new_exposure += cam->params.flickerControl.coarseJump;
2671 cam->params.exposure.coarseExpLo = new_exposure & 0xff;
2672 cam->params.exposure.coarseExpHi = new_exposure >> 8;
2673 cam->cmd_queue |= COMMAND_SETEXPOSURE;
2674 cam->exposure_status = EXPOSURE_NORMAL;
2675 LOG("Automatically decreasing sensor_fps\n");
2676
2677 } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT ||
2678 cam->exposure_status == EXPOSURE_LIGHT) &&
2679 cam->exposure_count >= LIGHT_TIME*framerate &&
2680 cam->params.sensorFps.divisor > 0) {
2681
2682 /* light for too long */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002683 int max_exp = FIRMWARE_VERSION(1,2) ? MAX_EXP_102 : MAX_EXP ;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002684
2685 --cam->params.sensorFps.divisor;
2686 cam->cmd_queue |= COMMAND_SETSENSORFPS;
2687
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002688 cam->params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07002689 flicker_jumps[cam->mainsFreq]
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002690 [cam->params.sensorFps.baserate]
2691 [cam->params.sensorFps.divisor];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002692 cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
2693
2694 new_exposure = cam->params.flickerControl.coarseJump-1;
2695 while(new_exposure < 2*old_exposure &&
2696 new_exposure+
2697 cam->params.flickerControl.coarseJump < max_exp)
2698 new_exposure += cam->params.flickerControl.coarseJump;
2699 cam->params.exposure.coarseExpLo = new_exposure & 0xff;
2700 cam->params.exposure.coarseExpHi = new_exposure >> 8;
2701 cam->cmd_queue |= COMMAND_SETEXPOSURE;
2702 cam->exposure_status = EXPOSURE_NORMAL;
2703 LOG("Automatically increasing sensor_fps\n");
2704 }
2705 } else {
2706 /* Flicker control off */
2707 if((cam->exposure_status == EXPOSURE_VERY_DARK ||
2708 cam->exposure_status == EXPOSURE_DARK) &&
2709 cam->exposure_count >= DARK_TIME*framerate &&
2710 cam->params.sensorFps.divisor < 3) {
2711
2712 /* dark for too long */
2713 ++cam->params.sensorFps.divisor;
2714 cam->cmd_queue |= COMMAND_SETSENSORFPS;
2715
2716 if(cam->params.exposure.gain > 0) {
2717 --cam->params.exposure.gain;
2718 cam->cmd_queue |= COMMAND_SETEXPOSURE;
2719 }
2720 cam->exposure_status = EXPOSURE_NORMAL;
2721 LOG("Automatically decreasing sensor_fps\n");
2722
2723 } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT ||
2724 cam->exposure_status == EXPOSURE_LIGHT) &&
2725 cam->exposure_count >= LIGHT_TIME*framerate &&
2726 cam->params.sensorFps.divisor > 0) {
2727
2728 /* light for too long */
2729 --cam->params.sensorFps.divisor;
2730 cam->cmd_queue |= COMMAND_SETSENSORFPS;
2731
2732 if(cam->params.exposure.gain <
2733 cam->params.exposure.gainMode-1) {
2734 ++cam->params.exposure.gain;
2735 cam->cmd_queue |= COMMAND_SETEXPOSURE;
2736 }
2737 cam->exposure_status = EXPOSURE_NORMAL;
2738 LOG("Automatically increasing sensor_fps\n");
2739 }
2740 }
Ingo Molnar3593cab2006-02-07 06:49:14 -02002741 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002742}
2743
2744/*-----------------------------------------------------------------*/
2745/* if flicker is switched off, this function switches it back on.It checks,
2746 however, that conditions are suitable before restarting it.
2747 This should only be called for firmware version 1.2.
2748
2749 It also adjust the colour balance when an exposure step is detected - as
2750 long as flicker is running
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002751*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07002752static void restart_flicker(struct cam_data *cam)
2753{
2754 int cam_exposure, old_exp;
2755 if(!FIRMWARE_VERSION(1,2))
2756 return;
Ingo Molnar3593cab2006-02-07 06:49:14 -02002757 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002758 if(cam->params.flickerControl.flickerMode == 0 ||
2759 cam->raw_image[39] == 0) {
Ingo Molnar3593cab2006-02-07 06:49:14 -02002760 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002761 return;
2762 }
2763 cam_exposure = cam->raw_image[39]*2;
2764 old_exp = cam->params.exposure.coarseExpLo +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002765 cam->params.exposure.coarseExpHi*256;
2766 /*
2767 see how far away camera exposure is from a valid
2768 flicker exposure value
2769 */
2770 cam_exposure %= cam->params.flickerControl.coarseJump;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002771 if(!cam->params.flickerControl.disabled &&
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002772 cam_exposure <= cam->params.flickerControl.coarseJump - 3) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002773 /* Flicker control auto-disabled */
2774 cam->params.flickerControl.disabled = 1;
2775 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002776
Linus Torvalds1da177e2005-04-16 15:20:36 -07002777 if(cam->params.flickerControl.disabled &&
2778 cam->params.flickerControl.flickerMode &&
2779 old_exp > cam->params.flickerControl.coarseJump +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002780 ROUND_UP_EXP_FOR_FLICKER) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002781 /* exposure is now high enough to switch
2782 flicker control back on */
2783 set_flicker(&cam->params, &cam->cmd_queue, 1);
2784 if((cam->cmd_queue & COMMAND_SETEXPOSURE) &&
2785 cam->params.exposure.expMode == 2)
2786 cam->exposure_status = EXPOSURE_NORMAL;
2787
2788 }
Ingo Molnar3593cab2006-02-07 06:49:14 -02002789 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002790}
2791#undef FIRMWARE_VERSION
2792
2793static int clear_stall(struct cam_data *cam)
2794{
2795 /* FIXME: Does this actually work? */
2796 LOG("Clearing stall\n");
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002797
Linus Torvalds1da177e2005-04-16 15:20:36 -07002798 cam->ops->streamRead(cam->lowlevel_data, cam->raw_image, 0);
2799 do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0);
2800 return cam->params.status.streamState != STREAM_PAUSED;
2801}
2802
2803/* kernel thread function to read image from camera */
2804static int fetch_frame(void *data)
2805{
2806 int image_size, retry;
2807 struct cam_data *cam = (struct cam_data *)data;
2808 unsigned long oldjif, rate, diff;
2809
2810 /* Allow up to two bad images in a row to be read and
2811 * ignored before an error is reported */
2812 for (retry = 0; retry < 3; ++retry) {
2813 if (retry)
2814 DBG("retry=%d\n", retry);
2815
2816 if (!cam->ops)
2817 continue;
2818
2819 /* load first frame always uncompressed */
2820 if (cam->first_frame &&
2821 cam->params.compression.mode != CPIA_COMPRESSION_NONE) {
2822 do_command(cam, CPIA_COMMAND_SetCompression,
2823 CPIA_COMPRESSION_NONE,
2824 NO_DECIMATION, 0, 0);
2825 /* Trial & error - Discarding a frame prevents the
2826 first frame from having an error in the data. */
2827 do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0);
2828 }
2829
2830 /* init camera upload */
2831 if (do_command(cam, CPIA_COMMAND_GrabFrame, 0,
2832 cam->params.streamStartLine, 0, 0))
2833 continue;
2834
2835 if (cam->ops->wait_for_stream_ready) {
2836 /* loop until image ready */
2837 int count = 0;
2838 do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0);
2839 while (cam->params.status.streamState != STREAM_READY) {
2840 if(++count > READY_TIMEOUT)
2841 break;
2842 if(cam->params.status.streamState ==
2843 STREAM_PAUSED) {
2844 /* Bad news */
2845 if(!clear_stall(cam))
2846 return -EIO;
2847 }
2848
2849 cond_resched();
2850
2851 /* sleep for 10 ms, hopefully ;) */
2852 msleep_interruptible(10);
2853 if (signal_pending(current))
2854 return -EINTR;
2855
2856 do_command(cam, CPIA_COMMAND_GetCameraStatus,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002857 0, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002858 }
2859 if(cam->params.status.streamState != STREAM_READY) {
2860 continue;
2861 }
2862 }
2863
2864 cond_resched();
2865
2866 /* grab image from camera */
2867 oldjif = jiffies;
2868 image_size = cam->ops->streamRead(cam->lowlevel_data,
2869 cam->raw_image, 0);
2870 if (image_size <= 0) {
2871 DBG("streamRead failed: %d\n", image_size);
2872 continue;
2873 }
2874
2875 rate = image_size * HZ / 1024;
2876 diff = jiffies-oldjif;
2877 cam->transfer_rate = diff==0 ? rate : rate/diff;
2878 /* diff==0 ? unlikely but possible */
2879
2880 /* Switch flicker control back on if it got turned off */
2881 restart_flicker(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002882
Linus Torvalds1da177e2005-04-16 15:20:36 -07002883 /* If AEC is enabled, monitor the exposure and
2884 adjust the sensor frame rate if needed */
2885 if(cam->params.exposure.expMode == 2)
2886 monitor_exposure(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002887
Linus Torvalds1da177e2005-04-16 15:20:36 -07002888 /* camera idle now so dispatch queued commands */
2889 dispatch_commands(cam);
2890
2891 /* Update our knowledge of the camera state */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002892 do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
2893 do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002894 do_command(cam, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0);
2895
2896 /* decompress and convert image to by copying it from
2897 * raw_image to decompressed_frame
2898 */
2899
2900 cond_resched();
2901
2902 cam->image_size = parse_picture(cam, image_size);
2903 if (cam->image_size <= 0) {
2904 DBG("parse_picture failed %d\n", cam->image_size);
2905 if(cam->params.compression.mode !=
2906 CPIA_COMPRESSION_NONE) {
2907 /* Compression may not work right if we
2908 had a bad frame, get the next one
2909 uncompressed. */
2910 cam->first_frame = 1;
2911 do_command(cam, CPIA_COMMAND_SetGrabMode,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002912 CPIA_GRAB_SINGLE, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002913 /* FIXME: Trial & error - need up to 70ms for
2914 the grab mode change to complete ? */
2915 msleep_interruptible(70);
2916 if (signal_pending(current))
2917 return -EINTR;
2918 }
2919 } else
2920 break;
2921 }
2922
2923 if (retry < 3) {
2924 /* FIXME: this only works for double buffering */
2925 if (cam->frame[cam->curframe].state == FRAME_READY) {
2926 memcpy(cam->frame[cam->curframe].data,
2927 cam->decompressed_frame.data,
2928 cam->decompressed_frame.count);
2929 cam->frame[cam->curframe].state = FRAME_DONE;
2930 } else
2931 cam->decompressed_frame.state = FRAME_DONE;
2932
2933 if (cam->first_frame) {
2934 cam->first_frame = 0;
2935 do_command(cam, CPIA_COMMAND_SetCompression,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002936 cam->params.compression.mode,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002937 cam->params.compression.decimation, 0, 0);
2938
2939 /* Switch from single-grab to continuous grab */
2940 do_command(cam, CPIA_COMMAND_SetGrabMode,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002941 CPIA_GRAB_CONTINUOUS, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002942 }
2943 return 0;
2944 }
2945 return -EIO;
2946}
2947
2948static int capture_frame(struct cam_data *cam, struct video_mmap *vm)
2949{
2950 if (!cam->frame_buf) {
2951 /* we do lazy allocation */
2952 int err;
2953 if ((err = allocate_frame_buf(cam)))
2954 return err;
2955 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002956
Linus Torvalds1da177e2005-04-16 15:20:36 -07002957 cam->curframe = vm->frame;
2958 cam->frame[cam->curframe].state = FRAME_READY;
2959 return fetch_frame(cam);
2960}
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002961
Linus Torvalds1da177e2005-04-16 15:20:36 -07002962static int goto_high_power(struct cam_data *cam)
2963{
2964 if (do_command(cam, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0))
2965 return -EIO;
2966 msleep_interruptible(40); /* windows driver does it too */
2967 if(signal_pending(current))
2968 return -EINTR;
2969 if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
2970 return -EIO;
2971 if (cam->params.status.systemState == HI_POWER_STATE) {
2972 DBG("camera now in HIGH power state\n");
2973 return 0;
2974 }
2975 printstatus(cam);
2976 return -EIO;
2977}
2978
2979static int goto_low_power(struct cam_data *cam)
2980{
2981 if (do_command(cam, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0))
2982 return -1;
2983 if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
2984 return -1;
2985 if (cam->params.status.systemState == LO_POWER_STATE) {
2986 DBG("camera now in LOW power state\n");
2987 return 0;
2988 }
2989 printstatus(cam);
2990 return -1;
2991}
2992
2993static void save_camera_state(struct cam_data *cam)
2994{
2995 if(!(cam->cmd_queue & COMMAND_SETCOLOURBALANCE))
2996 do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
2997 if(!(cam->cmd_queue & COMMAND_SETEXPOSURE))
2998 do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
2999
3000 DBG("%d/%d/%d/%d/%d/%d/%d/%d\n",
3001 cam->params.exposure.gain,
3002 cam->params.exposure.fineExp,
3003 cam->params.exposure.coarseExpLo,
3004 cam->params.exposure.coarseExpHi,
3005 cam->params.exposure.redComp,
3006 cam->params.exposure.green1Comp,
3007 cam->params.exposure.green2Comp,
3008 cam->params.exposure.blueComp);
3009 DBG("%d/%d/%d\n",
3010 cam->params.colourBalance.redGain,
3011 cam->params.colourBalance.greenGain,
3012 cam->params.colourBalance.blueGain);
3013}
3014
3015static int set_camera_state(struct cam_data *cam)
3016{
3017 cam->cmd_queue = COMMAND_SETCOMPRESSION |
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003018 COMMAND_SETCOMPRESSIONTARGET |
3019 COMMAND_SETCOLOURPARAMS |
3020 COMMAND_SETFORMAT |
3021 COMMAND_SETYUVTHRESH |
3022 COMMAND_SETECPTIMING |
3023 COMMAND_SETCOMPRESSIONPARAMS |
3024 COMMAND_SETEXPOSURE |
3025 COMMAND_SETCOLOURBALANCE |
3026 COMMAND_SETSENSORFPS |
3027 COMMAND_SETAPCOR |
3028 COMMAND_SETFLICKERCTRL |
3029 COMMAND_SETVLOFFSET;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003030
3031 do_command(cam, CPIA_COMMAND_SetGrabMode, CPIA_GRAB_SINGLE,0,0,0);
3032 dispatch_commands(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003033
Linus Torvalds1da177e2005-04-16 15:20:36 -07003034 /* Wait 6 frames for the sensor to get all settings and
3035 AEC/ACB to settle */
3036 msleep_interruptible(6*(cam->params.sensorFps.baserate ? 33 : 40) *
3037 (1 << cam->params.sensorFps.divisor) + 10);
3038
3039 if(signal_pending(current))
3040 return -EINTR;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003041
Linus Torvalds1da177e2005-04-16 15:20:36 -07003042 save_camera_state(cam);
3043
3044 return 0;
3045}
3046
3047static void get_version_information(struct cam_data *cam)
3048{
3049 /* GetCPIAVersion */
3050 do_command(cam, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0);
3051
3052 /* GetPnPID */
3053 do_command(cam, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0);
3054}
3055
3056/* initialize camera */
3057static int reset_camera(struct cam_data *cam)
3058{
3059 int err;
3060 /* Start the camera in low power mode */
3061 if (goto_low_power(cam)) {
3062 if (cam->params.status.systemState != WARM_BOOT_STATE)
3063 return -ENODEV;
3064
3065 /* FIXME: this is just dirty trial and error */
3066 err = goto_high_power(cam);
3067 if(err)
3068 return err;
3069 do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0);
3070 if (goto_low_power(cam))
3071 return -ENODEV;
3072 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003073
Linus Torvalds1da177e2005-04-16 15:20:36 -07003074 /* procedure described in developer's guide p3-28 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003075
Linus Torvalds1da177e2005-04-16 15:20:36 -07003076 /* Check the firmware version. */
3077 cam->params.version.firmwareVersion = 0;
3078 get_version_information(cam);
3079 if (cam->params.version.firmwareVersion != 1)
3080 return -ENODEV;
3081
3082 /* A bug in firmware 1-02 limits gainMode to 2 */
3083 if(cam->params.version.firmwareRevision <= 2 &&
3084 cam->params.exposure.gainMode > 2) {
3085 cam->params.exposure.gainMode = 2;
3086 }
3087
3088 /* set QX3 detected flag */
3089 cam->params.qx3.qx3_detected = (cam->params.pnpID.vendor == 0x0813 &&
3090 cam->params.pnpID.product == 0x0001);
3091
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003092 /* The fatal error checking should be done after
Linus Torvalds1da177e2005-04-16 15:20:36 -07003093 * the camera powers up (developer's guide p 3-38) */
3094
3095 /* Set streamState before transition to high power to avoid bug
3096 * in firmware 1-02 */
3097 do_command(cam, CPIA_COMMAND_ModifyCameraStatus, STREAMSTATE, 0,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003098 STREAM_NOT_READY, 0);
3099
Linus Torvalds1da177e2005-04-16 15:20:36 -07003100 /* GotoHiPower */
3101 err = goto_high_power(cam);
3102 if (err)
3103 return err;
3104
3105 /* Check the camera status */
3106 if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
3107 return -EIO;
3108
3109 if (cam->params.status.fatalError) {
3110 DBG("fatal_error: %#04x\n",
3111 cam->params.status.fatalError);
3112 DBG("vp_status: %#04x\n",
3113 cam->params.status.vpStatus);
3114 if (cam->params.status.fatalError & ~(COM_FLAG|CPIA_FLAG)) {
3115 /* Fatal error in camera */
3116 return -EIO;
3117 } else if (cam->params.status.fatalError & (COM_FLAG|CPIA_FLAG)) {
3118 /* Firmware 1-02 may do this for parallel port cameras,
3119 * just clear the flags (developer's guide p 3-38) */
3120 do_command(cam, CPIA_COMMAND_ModifyCameraStatus,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003121 FATALERROR, ~(COM_FLAG|CPIA_FLAG), 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003122 }
3123 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003124
Linus Torvalds1da177e2005-04-16 15:20:36 -07003125 /* Check the camera status again */
3126 if (cam->params.status.fatalError) {
3127 if (cam->params.status.fatalError)
3128 return -EIO;
3129 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003130
Linus Torvalds1da177e2005-04-16 15:20:36 -07003131 /* VPVersion can't be retrieved before the camera is in HiPower,
3132 * so get it here instead of in get_version_information. */
3133 do_command(cam, CPIA_COMMAND_GetVPVersion, 0, 0, 0, 0);
3134
3135 /* set camera to a known state */
3136 return set_camera_state(cam);
3137}
3138
3139static void put_cam(struct cpia_camera_ops* ops)
3140{
Mariusz Kozlowskiac328982007-01-07 10:36:24 -03003141 module_put(ops->owner);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003142}
3143
3144/* ------------------------- V4L interface --------------------- */
Hans Verkuilbec43662008-12-30 06:58:20 -03003145static int cpia_open(struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003146{
3147 struct video_device *dev = video_devdata(file);
Hans Verkuil601e9442008-08-23 07:24:07 -03003148 struct cam_data *cam = video_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003149 int err;
3150
3151 if (!cam) {
3152 DBG("Internal error, cam_data not found!\n");
3153 return -ENODEV;
3154 }
3155
3156 if (cam->open_count > 0) {
3157 DBG("Camera already open\n");
3158 return -EBUSY;
3159 }
3160
3161 if (!try_module_get(cam->ops->owner))
3162 return -ENODEV;
3163
Ingo Molnar3593cab2006-02-07 06:49:14 -02003164 mutex_lock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003165 err = -ENOMEM;
3166 if (!cam->raw_image) {
3167 cam->raw_image = rvmalloc(CPIA_MAX_IMAGE_SIZE);
3168 if (!cam->raw_image)
3169 goto oops;
3170 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003171
Linus Torvalds1da177e2005-04-16 15:20:36 -07003172 if (!cam->decompressed_frame.data) {
3173 cam->decompressed_frame.data = rvmalloc(CPIA_MAX_FRAME_SIZE);
3174 if (!cam->decompressed_frame.data)
3175 goto oops;
3176 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003177
Linus Torvalds1da177e2005-04-16 15:20:36 -07003178 /* open cpia */
3179 err = -ENODEV;
3180 if (cam->ops->open(cam->lowlevel_data))
3181 goto oops;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003182
Linus Torvalds1da177e2005-04-16 15:20:36 -07003183 /* reset the camera */
3184 if ((err = reset_camera(cam)) != 0) {
3185 cam->ops->close(cam->lowlevel_data);
3186 goto oops;
3187 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003188
Linus Torvalds1da177e2005-04-16 15:20:36 -07003189 err = -EINTR;
3190 if(signal_pending(current))
3191 goto oops;
3192
3193 /* Set ownership of /proc/cpia/videoX to current user */
3194 if(cam->proc_entry)
David Howellsabc94fc2008-08-27 10:46:39 -03003195 cam->proc_entry->uid = current_uid();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003196
3197 /* set mark for loading first frame uncompressed */
3198 cam->first_frame = 1;
3199
3200 /* init it to something */
3201 cam->mmap_kludge = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003202
Linus Torvalds1da177e2005-04-16 15:20:36 -07003203 ++cam->open_count;
3204 file->private_data = dev;
Ingo Molnar3593cab2006-02-07 06:49:14 -02003205 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003206 return 0;
3207
3208 oops:
3209 if (cam->decompressed_frame.data) {
3210 rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
3211 cam->decompressed_frame.data = NULL;
3212 }
3213 if (cam->raw_image) {
3214 rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
3215 cam->raw_image = NULL;
3216 }
Ingo Molnar3593cab2006-02-07 06:49:14 -02003217 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003218 put_cam(cam->ops);
3219 return err;
3220}
3221
Hans Verkuilbec43662008-12-30 06:58:20 -03003222static int cpia_close(struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003223{
3224 struct video_device *dev = file->private_data;
Hans Verkuil601e9442008-08-23 07:24:07 -03003225 struct cam_data *cam = video_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003226
3227 if (cam->ops) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003228 /* Return ownership of /proc/cpia/videoX to root */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003229 if(cam->proc_entry)
3230 cam->proc_entry->uid = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003231
Linus Torvalds1da177e2005-04-16 15:20:36 -07003232 /* save camera state for later open (developers guide ch 3.5.3) */
3233 save_camera_state(cam);
3234
3235 /* GotoLoPower */
3236 goto_low_power(cam);
3237
3238 /* Update the camera status */
3239 do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
3240
3241 /* cleanup internal state stuff */
3242 free_frames(cam->frame);
3243
3244 /* close cpia */
3245 cam->ops->close(cam->lowlevel_data);
3246
3247 put_cam(cam->ops);
3248 }
3249
3250 if (--cam->open_count == 0) {
3251 /* clean up capture-buffers */
3252 if (cam->raw_image) {
3253 rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
3254 cam->raw_image = NULL;
3255 }
3256
3257 if (cam->decompressed_frame.data) {
3258 rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
3259 cam->decompressed_frame.data = NULL;
3260 }
3261
3262 if (cam->frame_buf)
3263 free_frame_buf(cam);
3264
3265 if (!cam->ops)
3266 kfree(cam);
3267 }
3268 file->private_data = NULL;
3269
3270 return 0;
3271}
3272
3273static ssize_t cpia_read(struct file *file, char __user *buf,
3274 size_t count, loff_t *ppos)
3275{
3276 struct video_device *dev = file->private_data;
Hans Verkuil601e9442008-08-23 07:24:07 -03003277 struct cam_data *cam = video_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003278 int err;
3279
3280 /* make this _really_ smp and multithread-safe */
Ingo Molnar3593cab2006-02-07 06:49:14 -02003281 if (mutex_lock_interruptible(&cam->busy_lock))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003282 return -EINTR;
3283
3284 if (!buf) {
3285 DBG("buf NULL\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02003286 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003287 return -EINVAL;
3288 }
3289
3290 if (!count) {
3291 DBG("count 0\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02003292 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003293 return 0;
3294 }
3295
3296 if (!cam->ops) {
3297 DBG("ops NULL\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02003298 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003299 return -ENODEV;
3300 }
3301
3302 /* upload frame */
3303 cam->decompressed_frame.state = FRAME_READY;
3304 cam->mmap_kludge=0;
3305 if((err = fetch_frame(cam)) != 0) {
3306 DBG("ERROR from fetch_frame: %d\n", err);
Ingo Molnar3593cab2006-02-07 06:49:14 -02003307 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003308 return err;
3309 }
3310 cam->decompressed_frame.state = FRAME_UNUSED;
3311
3312 /* copy data to user space */
3313 if (cam->decompressed_frame.count > count) {
3314 DBG("count wrong: %d, %lu\n", cam->decompressed_frame.count,
3315 (unsigned long) count);
Ingo Molnar3593cab2006-02-07 06:49:14 -02003316 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003317 return -EFAULT;
3318 }
3319 if (copy_to_user(buf, cam->decompressed_frame.data,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003320 cam->decompressed_frame.count)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003321 DBG("copy_to_user failed\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02003322 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003323 return -EFAULT;
3324 }
3325
Ingo Molnar3593cab2006-02-07 06:49:14 -02003326 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003327 return cam->decompressed_frame.count;
3328}
3329
Hans Verkuil069b7472008-12-30 07:04:34 -03003330static long cpia_do_ioctl(struct file *file, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003331{
3332 struct video_device *dev = file->private_data;
Hans Verkuil601e9442008-08-23 07:24:07 -03003333 struct cam_data *cam = video_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003334 int retval = 0;
3335
3336 if (!cam || !cam->ops)
3337 return -ENODEV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003338
Linus Torvalds1da177e2005-04-16 15:20:36 -07003339 /* make this _really_ smp-safe */
Ingo Molnar3593cab2006-02-07 06:49:14 -02003340 if (mutex_lock_interruptible(&cam->busy_lock))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003341 return -EINTR;
3342
Hans Verkuilf473bf72008-11-01 08:25:11 -03003343 /* DBG("cpia_ioctl: %u\n", cmd); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003344
Hans Verkuilf473bf72008-11-01 08:25:11 -03003345 switch (cmd) {
Alexey Dobriyanbe787ac2006-03-07 22:20:23 -03003346 /* query capabilities */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003347 case VIDIOCGCAP:
3348 {
3349 struct video_capability *b = arg;
3350
3351 DBG("VIDIOCGCAP\n");
3352 strcpy(b->name, "CPiA Camera");
3353 b->type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE;
3354 b->channels = 1;
3355 b->audios = 0;
3356 b->maxwidth = 352; /* VIDEOSIZE_CIF */
3357 b->maxheight = 288;
3358 b->minwidth = 48; /* VIDEOSIZE_48_48 */
3359 b->minheight = 48;
3360 break;
3361 }
3362
3363 /* get/set video source - we are a camera and nothing else */
3364 case VIDIOCGCHAN:
3365 {
3366 struct video_channel *v = arg;
3367
3368 DBG("VIDIOCGCHAN\n");
3369 if (v->channel != 0) {
3370 retval = -EINVAL;
3371 break;
3372 }
3373
3374 v->channel = 0;
3375 strcpy(v->name, "Camera");
3376 v->tuners = 0;
3377 v->flags = 0;
3378 v->type = VIDEO_TYPE_CAMERA;
3379 v->norm = 0;
3380 break;
3381 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003382
Linus Torvalds1da177e2005-04-16 15:20:36 -07003383 case VIDIOCSCHAN:
3384 {
3385 struct video_channel *v = arg;
3386
3387 DBG("VIDIOCSCHAN\n");
3388 if (v->channel != 0)
3389 retval = -EINVAL;
3390 break;
3391 }
3392
3393 /* image properties */
3394 case VIDIOCGPICT:
3395 {
3396 struct video_picture *pic = arg;
3397 DBG("VIDIOCGPICT\n");
3398 *pic = cam->vp;
3399 break;
3400 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003401
Linus Torvalds1da177e2005-04-16 15:20:36 -07003402 case VIDIOCSPICT:
3403 {
3404 struct video_picture *vp = arg;
3405
3406 DBG("VIDIOCSPICT\n");
3407
3408 /* check validity */
3409 DBG("palette: %d\n", vp->palette);
3410 DBG("depth: %d\n", vp->depth);
3411 if (!valid_mode(vp->palette, vp->depth)) {
3412 retval = -EINVAL;
3413 break;
3414 }
3415
Ingo Molnar3593cab2006-02-07 06:49:14 -02003416 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003417 /* brightness, colour, contrast need no check 0-65535 */
3418 cam->vp = *vp;
3419 /* update cam->params.colourParams */
3420 cam->params.colourParams.brightness = vp->brightness*100/65535;
3421 cam->params.colourParams.contrast = vp->contrast*100/65535;
3422 cam->params.colourParams.saturation = vp->colour*100/65535;
3423 /* contrast is in steps of 8, so round */
3424 cam->params.colourParams.contrast =
3425 ((cam->params.colourParams.contrast + 3) / 8) * 8;
3426 if (cam->params.version.firmwareVersion == 1 &&
3427 cam->params.version.firmwareRevision == 2 &&
3428 cam->params.colourParams.contrast > 80) {
3429 /* 1-02 firmware limits contrast to 80 */
3430 cam->params.colourParams.contrast = 80;
3431 }
3432
3433 /* Adjust flicker control if necessary */
3434 if(cam->params.flickerControl.allowableOverExposure < 0)
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003435 cam->params.flickerControl.allowableOverExposure =
Linus Torvalds1da177e2005-04-16 15:20:36 -07003436 -find_over_exposure(cam->params.colourParams.brightness);
3437 if(cam->params.flickerControl.flickerMode != 0)
3438 cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003439
Linus Torvalds1da177e2005-04-16 15:20:36 -07003440
3441 /* queue command to update camera */
3442 cam->cmd_queue |= COMMAND_SETCOLOURPARAMS;
Ingo Molnar3593cab2006-02-07 06:49:14 -02003443 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003444 DBG("VIDIOCSPICT: %d / %d // %d / %d / %d / %d\n",
3445 vp->depth, vp->palette, vp->brightness, vp->hue, vp->colour,
3446 vp->contrast);
3447 break;
3448 }
3449
3450 /* get/set capture window */
3451 case VIDIOCGWIN:
3452 {
3453 struct video_window *vw = arg;
3454 DBG("VIDIOCGWIN\n");
3455
3456 *vw = cam->vw;
3457 break;
3458 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003459
Linus Torvalds1da177e2005-04-16 15:20:36 -07003460 case VIDIOCSWIN:
3461 {
3462 /* copy_from_user, check validity, copy to internal structure */
3463 struct video_window *vw = arg;
3464 DBG("VIDIOCSWIN\n");
3465
3466 if (vw->clipcount != 0) { /* clipping not supported */
3467 retval = -EINVAL;
3468 break;
3469 }
3470 if (vw->clips != NULL) { /* clipping not supported */
3471 retval = -EINVAL;
3472 break;
3473 }
3474
3475 /* we set the video window to something smaller or equal to what
3476 * is requested by the user???
3477 */
Ingo Molnar3593cab2006-02-07 06:49:14 -02003478 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003479 if (vw->width != cam->vw.width || vw->height != cam->vw.height) {
3480 int video_size = match_videosize(vw->width, vw->height);
3481
3482 if (video_size < 0) {
3483 retval = -EINVAL;
Ingo Molnar3593cab2006-02-07 06:49:14 -02003484 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003485 break;
3486 }
3487 cam->video_size = video_size;
3488
3489 /* video size is changing, reset the subcapture area */
3490 memset(&cam->vc, 0, sizeof(cam->vc));
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003491
Linus Torvalds1da177e2005-04-16 15:20:36 -07003492 set_vw_size(cam);
3493 DBG("%d / %d\n", cam->vw.width, cam->vw.height);
3494 cam->cmd_queue |= COMMAND_SETFORMAT;
3495 }
3496
Ingo Molnar3593cab2006-02-07 06:49:14 -02003497 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003498
3499 /* setformat ignored by camera during streaming,
3500 * so stop/dispatch/start */
3501 if (cam->cmd_queue & COMMAND_SETFORMAT) {
3502 DBG("\n");
3503 dispatch_commands(cam);
3504 }
3505 DBG("%d/%d:%d\n", cam->video_size,
3506 cam->vw.width, cam->vw.height);
3507 break;
3508 }
3509
3510 /* mmap interface */
3511 case VIDIOCGMBUF:
3512 {
3513 struct video_mbuf *vm = arg;
3514 int i;
3515
3516 DBG("VIDIOCGMBUF\n");
3517 memset(vm, 0, sizeof(*vm));
3518 vm->size = CPIA_MAX_FRAME_SIZE*FRAME_NUM;
3519 vm->frames = FRAME_NUM;
3520 for (i = 0; i < FRAME_NUM; i++)
3521 vm->offsets[i] = CPIA_MAX_FRAME_SIZE * i;
3522 break;
3523 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003524
Linus Torvalds1da177e2005-04-16 15:20:36 -07003525 case VIDIOCMCAPTURE:
3526 {
3527 struct video_mmap *vm = arg;
3528 int video_size;
3529
3530 DBG("VIDIOCMCAPTURE: %d / %d / %dx%d\n", vm->format, vm->frame,
3531 vm->width, vm->height);
3532 if (vm->frame<0||vm->frame>=FRAME_NUM) {
3533 retval = -EINVAL;
3534 break;
3535 }
3536
3537 /* set video format */
3538 cam->vp.palette = vm->format;
3539 switch(vm->format) {
3540 case VIDEO_PALETTE_GREY:
3541 cam->vp.depth=8;
3542 break;
3543 case VIDEO_PALETTE_RGB555:
3544 case VIDEO_PALETTE_RGB565:
3545 case VIDEO_PALETTE_YUV422:
3546 case VIDEO_PALETTE_YUYV:
3547 case VIDEO_PALETTE_UYVY:
3548 cam->vp.depth = 16;
3549 break;
3550 case VIDEO_PALETTE_RGB24:
3551 cam->vp.depth = 24;
3552 break;
3553 case VIDEO_PALETTE_RGB32:
3554 cam->vp.depth = 32;
3555 break;
3556 default:
3557 retval = -EINVAL;
3558 break;
3559 }
3560 if (retval)
3561 break;
3562
3563 /* set video size */
3564 video_size = match_videosize(vm->width, vm->height);
3565 if (video_size < 0) {
3566 retval = -EINVAL;
3567 break;
3568 }
3569 if (video_size != cam->video_size) {
3570 cam->video_size = video_size;
3571
3572 /* video size is changing, reset the subcapture area */
3573 memset(&cam->vc, 0, sizeof(cam->vc));
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003574
Linus Torvalds1da177e2005-04-16 15:20:36 -07003575 set_vw_size(cam);
3576 cam->cmd_queue |= COMMAND_SETFORMAT;
3577 dispatch_commands(cam);
3578 }
3579 /* according to v4l-spec we must start streaming here */
3580 cam->mmap_kludge = 1;
3581 retval = capture_frame(cam, vm);
3582
3583 break;
3584 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003585
Linus Torvalds1da177e2005-04-16 15:20:36 -07003586 case VIDIOCSYNC:
3587 {
3588 int *frame = arg;
3589
3590 //DBG("VIDIOCSYNC: %d\n", *frame);
3591
3592 if (*frame<0 || *frame >= FRAME_NUM) {
3593 retval = -EINVAL;
3594 break;
3595 }
3596
3597 switch (cam->frame[*frame].state) {
3598 case FRAME_UNUSED:
3599 case FRAME_READY:
3600 case FRAME_GRABBING:
3601 DBG("sync to unused frame %d\n", *frame);
3602 retval = -EINVAL;
3603 break;
3604
3605 case FRAME_DONE:
3606 cam->frame[*frame].state = FRAME_UNUSED;
3607 //DBG("VIDIOCSYNC: %d synced\n", *frame);
3608 break;
3609 }
3610 if (retval == -EINTR) {
3611 /* FIXME - xawtv does not handle this nice */
3612 retval = 0;
3613 }
3614 break;
3615 }
3616
3617 case VIDIOCGCAPTURE:
3618 {
3619 struct video_capture *vc = arg;
3620
3621 DBG("VIDIOCGCAPTURE\n");
3622
3623 *vc = cam->vc;
3624
3625 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003626 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003627
3628 case VIDIOCSCAPTURE:
3629 {
3630 struct video_capture *vc = arg;
3631
3632 DBG("VIDIOCSCAPTURE\n");
3633
3634 if (vc->decimation != 0) { /* How should this be used? */
3635 retval = -EINVAL;
3636 break;
3637 }
3638 if (vc->flags != 0) { /* Even/odd grab not supported */
3639 retval = -EINVAL;
3640 break;
3641 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003642
Linus Torvalds1da177e2005-04-16 15:20:36 -07003643 /* Clip to the resolution we can set for the ROI
3644 (every 8 columns and 4 rows) */
3645 vc->x = vc->x & ~(__u32)7;
3646 vc->y = vc->y & ~(__u32)3;
3647 vc->width = vc->width & ~(__u32)7;
3648 vc->height = vc->height & ~(__u32)3;
3649
3650 if(vc->width == 0 || vc->height == 0 ||
3651 vc->x + vc->width > cam->vw.width ||
3652 vc->y + vc->height > cam->vw.height) {
3653 retval = -EINVAL;
3654 break;
3655 }
3656
3657 DBG("%d,%d/%dx%d\n", vc->x,vc->y,vc->width, vc->height);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003658
Ingo Molnar3593cab2006-02-07 06:49:14 -02003659 mutex_lock(&cam->param_lock);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003660
Linus Torvalds1da177e2005-04-16 15:20:36 -07003661 cam->vc.x = vc->x;
3662 cam->vc.y = vc->y;
3663 cam->vc.width = vc->width;
3664 cam->vc.height = vc->height;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003665
Linus Torvalds1da177e2005-04-16 15:20:36 -07003666 set_vw_size(cam);
3667 cam->cmd_queue |= COMMAND_SETFORMAT;
3668
Ingo Molnar3593cab2006-02-07 06:49:14 -02003669 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003670
3671 /* setformat ignored by camera during streaming,
3672 * so stop/dispatch/start */
3673 dispatch_commands(cam);
3674 break;
3675 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003676
Linus Torvalds1da177e2005-04-16 15:20:36 -07003677 case VIDIOCGUNIT:
3678 {
3679 struct video_unit *vu = arg;
3680
3681 DBG("VIDIOCGUNIT\n");
3682
3683 vu->video = cam->vdev.minor;
3684 vu->vbi = VIDEO_NO_UNIT;
3685 vu->radio = VIDEO_NO_UNIT;
3686 vu->audio = VIDEO_NO_UNIT;
3687 vu->teletext = VIDEO_NO_UNIT;
3688
3689 break;
3690 }
3691
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003692
Linus Torvalds1da177e2005-04-16 15:20:36 -07003693 /* pointless to implement overlay with this camera */
3694 case VIDIOCCAPTURE:
3695 case VIDIOCGFBUF:
3696 case VIDIOCSFBUF:
3697 case VIDIOCKEY:
3698 /* tuner interface - we have none */
3699 case VIDIOCGTUNER:
3700 case VIDIOCSTUNER:
3701 case VIDIOCGFREQ:
3702 case VIDIOCSFREQ:
3703 /* audio interface - we have none */
3704 case VIDIOCGAUDIO:
3705 case VIDIOCSAUDIO:
3706 retval = -EINVAL;
3707 break;
3708 default:
3709 retval = -ENOIOCTLCMD;
3710 break;
3711 }
3712
Ingo Molnar3593cab2006-02-07 06:49:14 -02003713 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003714 return retval;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003715}
Linus Torvalds1da177e2005-04-16 15:20:36 -07003716
Hans Verkuil069b7472008-12-30 07:04:34 -03003717static long cpia_ioctl(struct file *file,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003718 unsigned int cmd, unsigned long arg)
3719{
Hans Verkuilf473bf72008-11-01 08:25:11 -03003720 return video_usercopy(file, cmd, arg, cpia_do_ioctl);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003721}
3722
3723
3724/* FIXME */
3725static int cpia_mmap(struct file *file, struct vm_area_struct *vma)
3726{
3727 struct video_device *dev = file->private_data;
3728 unsigned long start = vma->vm_start;
3729 unsigned long size = vma->vm_end - vma->vm_start;
3730 unsigned long page, pos;
Hans Verkuil601e9442008-08-23 07:24:07 -03003731 struct cam_data *cam = video_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003732 int retval;
3733
3734 if (!cam || !cam->ops)
3735 return -ENODEV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003736
Linus Torvalds1da177e2005-04-16 15:20:36 -07003737 DBG("cpia_mmap: %ld\n", size);
3738
3739 if (size > FRAME_NUM*CPIA_MAX_FRAME_SIZE)
3740 return -EINVAL;
3741
3742 if (!cam || !cam->ops)
3743 return -ENODEV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003744
Linus Torvalds1da177e2005-04-16 15:20:36 -07003745 /* make this _really_ smp-safe */
Ingo Molnar3593cab2006-02-07 06:49:14 -02003746 if (mutex_lock_interruptible(&cam->busy_lock))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003747 return -EINTR;
3748
3749 if (!cam->frame_buf) { /* we do lazy allocation */
3750 if ((retval = allocate_frame_buf(cam))) {
Ingo Molnar3593cab2006-02-07 06:49:14 -02003751 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003752 return retval;
3753 }
3754 }
3755
3756 pos = (unsigned long)(cam->frame_buf);
3757 while (size > 0) {
3758 page = vmalloc_to_pfn((void *)pos);
3759 if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) {
Ingo Molnar3593cab2006-02-07 06:49:14 -02003760 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003761 return -EAGAIN;
3762 }
3763 start += PAGE_SIZE;
3764 pos += PAGE_SIZE;
3765 if (size > PAGE_SIZE)
3766 size -= PAGE_SIZE;
3767 else
3768 size = 0;
3769 }
3770
3771 DBG("cpia_mmap: %ld\n", size);
Ingo Molnar3593cab2006-02-07 06:49:14 -02003772 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003773
3774 return 0;
3775}
3776
Hans Verkuilbec43662008-12-30 06:58:20 -03003777static const struct v4l2_file_operations cpia_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003778 .owner = THIS_MODULE,
3779 .open = cpia_open,
3780 .release = cpia_close,
3781 .read = cpia_read,
3782 .mmap = cpia_mmap,
3783 .ioctl = cpia_ioctl,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003784};
3785
3786static struct video_device cpia_template = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003787 .name = "CPiA Camera",
Linus Torvalds1da177e2005-04-16 15:20:36 -07003788 .fops = &cpia_fops,
Hans Verkuilaa5e90a2008-08-23 06:23:55 -03003789 .release = video_device_release_empty,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003790};
3791
3792/* initialise cam_data structure */
3793static void reset_camera_struct(struct cam_data *cam)
3794{
3795 /* The following parameter values are the defaults from
3796 * "Software Developer's Guide for CPiA Cameras". Any changes
3797 * to the defaults are noted in comments. */
3798 cam->params.colourParams.brightness = 50;
3799 cam->params.colourParams.contrast = 48;
3800 cam->params.colourParams.saturation = 50;
3801 cam->params.exposure.gainMode = 4;
3802 cam->params.exposure.expMode = 2; /* AEC */
3803 cam->params.exposure.compMode = 1;
3804 cam->params.exposure.centreWeight = 1;
3805 cam->params.exposure.gain = 0;
3806 cam->params.exposure.fineExp = 0;
3807 cam->params.exposure.coarseExpLo = 185;
3808 cam->params.exposure.coarseExpHi = 0;
3809 cam->params.exposure.redComp = COMP_RED;
3810 cam->params.exposure.green1Comp = COMP_GREEN1;
3811 cam->params.exposure.green2Comp = COMP_GREEN2;
3812 cam->params.exposure.blueComp = COMP_BLUE;
3813 cam->params.colourBalance.balanceMode = 2; /* ACB */
3814 cam->params.colourBalance.redGain = 32;
3815 cam->params.colourBalance.greenGain = 6;
3816 cam->params.colourBalance.blueGain = 92;
3817 cam->params.apcor.gain1 = 0x18;
3818 cam->params.apcor.gain2 = 0x16;
3819 cam->params.apcor.gain4 = 0x24;
3820 cam->params.apcor.gain8 = 0x34;
3821 cam->params.flickerControl.flickerMode = 0;
3822 cam->params.flickerControl.disabled = 1;
3823
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003824 cam->params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07003825 flicker_jumps[cam->mainsFreq]
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003826 [cam->params.sensorFps.baserate]
3827 [cam->params.sensorFps.divisor];
3828 cam->params.flickerControl.allowableOverExposure =
Linus Torvalds1da177e2005-04-16 15:20:36 -07003829 -find_over_exposure(cam->params.colourParams.brightness);
3830 cam->params.vlOffset.gain1 = 20;
3831 cam->params.vlOffset.gain2 = 24;
3832 cam->params.vlOffset.gain4 = 26;
3833 cam->params.vlOffset.gain8 = 26;
3834 cam->params.compressionParams.hysteresis = 3;
3835 cam->params.compressionParams.threshMax = 11;
3836 cam->params.compressionParams.smallStep = 1;
3837 cam->params.compressionParams.largeStep = 3;
3838 cam->params.compressionParams.decimationHysteresis = 2;
3839 cam->params.compressionParams.frDiffStepThresh = 5;
3840 cam->params.compressionParams.qDiffStepThresh = 3;
3841 cam->params.compressionParams.decimationThreshMod = 2;
3842 /* End of default values from Software Developer's Guide */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003843
Linus Torvalds1da177e2005-04-16 15:20:36 -07003844 cam->transfer_rate = 0;
3845 cam->exposure_status = EXPOSURE_NORMAL;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003846
Linus Torvalds1da177e2005-04-16 15:20:36 -07003847 /* Set Sensor FPS to 15fps. This seems better than 30fps
3848 * for indoor lighting. */
3849 cam->params.sensorFps.divisor = 1;
3850 cam->params.sensorFps.baserate = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003851
Linus Torvalds1da177e2005-04-16 15:20:36 -07003852 cam->params.yuvThreshold.yThreshold = 6; /* From windows driver */
3853 cam->params.yuvThreshold.uvThreshold = 6; /* From windows driver */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003854
Linus Torvalds1da177e2005-04-16 15:20:36 -07003855 cam->params.format.subSample = SUBSAMPLE_422;
3856 cam->params.format.yuvOrder = YUVORDER_YUYV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003857
Linus Torvalds1da177e2005-04-16 15:20:36 -07003858 cam->params.compression.mode = CPIA_COMPRESSION_AUTO;
3859 cam->params.compressionTarget.frTargeting =
3860 CPIA_COMPRESSION_TARGET_QUALITY;
3861 cam->params.compressionTarget.targetFR = 15; /* From windows driver */
3862 cam->params.compressionTarget.targetQ = 5; /* From windows driver */
3863
3864 cam->params.qx3.qx3_detected = 0;
3865 cam->params.qx3.toplight = 0;
3866 cam->params.qx3.bottomlight = 0;
3867 cam->params.qx3.button = 0;
3868 cam->params.qx3.cradled = 0;
3869
3870 cam->video_size = VIDEOSIZE_CIF;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003871
Linus Torvalds1da177e2005-04-16 15:20:36 -07003872 cam->vp.colour = 32768; /* 50% */
3873 cam->vp.hue = 32768; /* 50% */
3874 cam->vp.brightness = 32768; /* 50% */
3875 cam->vp.contrast = 32768; /* 50% */
3876 cam->vp.whiteness = 0; /* not used -> grayscale only */
3877 cam->vp.depth = 24; /* to be set by user */
3878 cam->vp.palette = VIDEO_PALETTE_RGB24; /* to be set by user */
3879
3880 cam->vc.x = 0;
3881 cam->vc.y = 0;
3882 cam->vc.width = 0;
3883 cam->vc.height = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003884
Linus Torvalds1da177e2005-04-16 15:20:36 -07003885 cam->vw.x = 0;
3886 cam->vw.y = 0;
3887 set_vw_size(cam);
3888 cam->vw.chromakey = 0;
3889 cam->vw.flags = 0;
3890 cam->vw.clipcount = 0;
3891 cam->vw.clips = NULL;
3892
3893 cam->cmd_queue = COMMAND_NONE;
3894 cam->first_frame = 1;
3895
3896 return;
3897}
3898
3899/* initialize cam_data structure */
3900static void init_camera_struct(struct cam_data *cam,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003901 struct cpia_camera_ops *ops )
Linus Torvalds1da177e2005-04-16 15:20:36 -07003902{
3903 int i;
3904
3905 /* Default everything to 0 */
3906 memset(cam, 0, sizeof(struct cam_data));
3907
3908 cam->ops = ops;
Ingo Molnar3593cab2006-02-07 06:49:14 -02003909 mutex_init(&cam->param_lock);
3910 mutex_init(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003911
3912 reset_camera_struct(cam);
3913
3914 cam->proc_entry = NULL;
3915
3916 memcpy(&cam->vdev, &cpia_template, sizeof(cpia_template));
Hans Verkuil601e9442008-08-23 07:24:07 -03003917 video_set_drvdata(&cam->vdev, cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003918
Linus Torvalds1da177e2005-04-16 15:20:36 -07003919 cam->curframe = 0;
3920 for (i = 0; i < FRAME_NUM; i++) {
3921 cam->frame[i].width = 0;
3922 cam->frame[i].height = 0;
3923 cam->frame[i].state = FRAME_UNUSED;
3924 cam->frame[i].data = NULL;
3925 }
3926 cam->decompressed_frame.width = 0;
3927 cam->decompressed_frame.height = 0;
3928 cam->decompressed_frame.state = FRAME_UNUSED;
3929 cam->decompressed_frame.data = NULL;
3930}
3931
3932struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel)
3933{
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003934 struct cam_data *camera;
3935
Linus Torvalds1da177e2005-04-16 15:20:36 -07003936 if ((camera = kmalloc(sizeof(struct cam_data), GFP_KERNEL)) == NULL)
3937 return NULL;
3938
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003939
Linus Torvalds1da177e2005-04-16 15:20:36 -07003940 init_camera_struct( camera, ops );
3941 camera->lowlevel_data = lowlevel;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003942
Linus Torvalds1da177e2005-04-16 15:20:36 -07003943 /* register v4l device */
Hans Verkuildc60de32008-09-03 17:11:58 -03003944 if (video_register_device(&camera->vdev, VFL_TYPE_GRABBER, video_nr) < 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003945 kfree(camera);
3946 printk(KERN_DEBUG "video_register_device failed\n");
3947 return NULL;
3948 }
3949
3950 /* get version information from camera: open/reset/close */
3951
3952 /* open cpia */
3953 if (camera->ops->open(camera->lowlevel_data))
3954 return camera;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003955
Linus Torvalds1da177e2005-04-16 15:20:36 -07003956 /* reset the camera */
3957 if (reset_camera(camera) != 0) {
3958 camera->ops->close(camera->lowlevel_data);
3959 return camera;
3960 }
3961
3962 /* close cpia */
3963 camera->ops->close(camera->lowlevel_data);
3964
3965#ifdef CONFIG_PROC_FS
3966 create_proc_cpia_cam(camera);
3967#endif
3968
3969 printk(KERN_INFO " CPiA Version: %d.%02d (%d.%d)\n",
3970 camera->params.version.firmwareVersion,
3971 camera->params.version.firmwareRevision,
3972 camera->params.version.vcVersion,
3973 camera->params.version.vcRevision);
3974 printk(KERN_INFO " CPiA PnP-ID: %04x:%04x:%04x\n",
3975 camera->params.pnpID.vendor,
3976 camera->params.pnpID.product,
3977 camera->params.pnpID.deviceRevision);
3978 printk(KERN_INFO " VP-Version: %d.%d %04x\n",
3979 camera->params.vpVersion.vpVersion,
3980 camera->params.vpVersion.vpRevision,
3981 camera->params.vpVersion.cameraHeadID);
3982
3983 return camera;
3984}
3985
3986void cpia_unregister_camera(struct cam_data *cam)
3987{
3988 DBG("unregistering video\n");
3989 video_unregister_device(&cam->vdev);
3990 if (cam->open_count) {
3991 put_cam(cam->ops);
3992 DBG("camera open -- setting ops to NULL\n");
3993 cam->ops = NULL;
3994 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003995
Linus Torvalds1da177e2005-04-16 15:20:36 -07003996#ifdef CONFIG_PROC_FS
Laurent Pinchart38c7c032009-11-27 13:57:15 -03003997 DBG("destroying /proc/cpia/%s\n", video_device_node_name(&cam->vdev));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003998 destroy_proc_cpia_cam(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003999#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07004000 if (!cam->open_count) {
4001 DBG("freeing camera\n");
4002 kfree(cam);
4003 }
4004}
4005
4006static int __init cpia_init(void)
4007{
4008 printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT,
4009 CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER);
4010
4011 printk(KERN_WARNING "Since in-kernel colorspace conversion is not "
4012 "allowed, it is disabled by default now. Users should fix the "
4013 "applications in case they don't work without conversion "
4014 "reenabled by setting the 'colorspace_conv' module "
Randy Dunlap94190452006-03-27 16:18:25 -03004015 "parameter to 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004016
4017#ifdef CONFIG_PROC_FS
4018 proc_cpia_create();
4019#endif
4020
Linus Torvalds1da177e2005-04-16 15:20:36 -07004021 return 0;
4022}
4023
4024static void __exit cpia_exit(void)
4025{
4026#ifdef CONFIG_PROC_FS
4027 proc_cpia_destroy();
4028#endif
4029}
4030
4031module_init(cpia_init);
4032module_exit(cpia_exit);
4033
4034/* Exported symbols for modules. */
4035
4036EXPORT_SYMBOL(cpia_register_camera);
4037EXPORT_SYMBOL(cpia_unregister_camera);