blob: 7e8d5ef58b61f09ecd3a5e3ec592354a1f31e2af [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>
31#include <linux/moduleparam.h>
32#include <linux/init.h>
33#include <linux/fs.h>
34#include <linux/vmalloc.h>
35#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
43#ifdef CONFIG_KMOD
44#include <linux/kmod.h>
45#endif
46
47#include "cpia.h"
48
Linus Torvalds1da177e2005-04-16 15:20:36 -070049static int video_nr = -1;
50
51#ifdef MODULE
52module_param(video_nr, int, 0);
Randy Dunlap2f8de1a2006-03-21 14:53:22 -030053MODULE_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 -070054MODULE_DESCRIPTION("V4L-driver for Vision CPiA based cameras");
55MODULE_LICENSE("GPL");
56MODULE_SUPPORTED_DEVICE("video");
57#endif
58
Randy Dunlap94190452006-03-27 16:18:25 -030059static unsigned short colorspace_conv;
Linus Torvalds1da177e2005-04-16 15:20:36 -070060module_param(colorspace_conv, ushort, 0444);
61MODULE_PARM_DESC(colorspace_conv,
Mauro Carvalho Chehab4286c6f2006-04-08 16:06:16 -030062 " Colorspace conversion:"
63 "\n 0 = disable, 1 = enable"
64 "\n Default value is 0"
65 );
Linus Torvalds1da177e2005-04-16 15:20:36 -070066
67#define ABOUT "V4L-Driver for Vision CPiA based cameras"
68
69#ifndef VID_HARDWARE_CPIA
70#define VID_HARDWARE_CPIA 24 /* FIXME -> from linux/videodev.h */
71#endif
72
73#define CPIA_MODULE_CPIA (0<<5)
74#define CPIA_MODULE_SYSTEM (1<<5)
75#define CPIA_MODULE_VP_CTRL (5<<5)
76#define CPIA_MODULE_CAPTURE (6<<5)
77#define CPIA_MODULE_DEBUG (7<<5)
78
79#define INPUT (DATA_IN << 8)
80#define OUTPUT (DATA_OUT << 8)
81
82#define CPIA_COMMAND_GetCPIAVersion (INPUT | CPIA_MODULE_CPIA | 1)
83#define CPIA_COMMAND_GetPnPID (INPUT | CPIA_MODULE_CPIA | 2)
84#define CPIA_COMMAND_GetCameraStatus (INPUT | CPIA_MODULE_CPIA | 3)
85#define CPIA_COMMAND_GotoHiPower (OUTPUT | CPIA_MODULE_CPIA | 4)
86#define CPIA_COMMAND_GotoLoPower (OUTPUT | CPIA_MODULE_CPIA | 5)
87#define CPIA_COMMAND_GotoSuspend (OUTPUT | CPIA_MODULE_CPIA | 7)
88#define CPIA_COMMAND_GotoPassThrough (OUTPUT | CPIA_MODULE_CPIA | 8)
89#define CPIA_COMMAND_ModifyCameraStatus (OUTPUT | CPIA_MODULE_CPIA | 10)
90
91#define CPIA_COMMAND_ReadVCRegs (INPUT | CPIA_MODULE_SYSTEM | 1)
92#define CPIA_COMMAND_WriteVCReg (OUTPUT | CPIA_MODULE_SYSTEM | 2)
93#define CPIA_COMMAND_ReadMCPorts (INPUT | CPIA_MODULE_SYSTEM | 3)
94#define CPIA_COMMAND_WriteMCPort (OUTPUT | CPIA_MODULE_SYSTEM | 4)
95#define CPIA_COMMAND_SetBaudRate (OUTPUT | CPIA_MODULE_SYSTEM | 5)
96#define CPIA_COMMAND_SetECPTiming (OUTPUT | CPIA_MODULE_SYSTEM | 6)
97#define CPIA_COMMAND_ReadIDATA (INPUT | CPIA_MODULE_SYSTEM | 7)
98#define CPIA_COMMAND_WriteIDATA (OUTPUT | CPIA_MODULE_SYSTEM | 8)
99#define CPIA_COMMAND_GenericCall (OUTPUT | CPIA_MODULE_SYSTEM | 9)
100#define CPIA_COMMAND_I2CStart (OUTPUT | CPIA_MODULE_SYSTEM | 10)
101#define CPIA_COMMAND_I2CStop (OUTPUT | CPIA_MODULE_SYSTEM | 11)
102#define CPIA_COMMAND_I2CWrite (OUTPUT | CPIA_MODULE_SYSTEM | 12)
103#define CPIA_COMMAND_I2CRead (INPUT | CPIA_MODULE_SYSTEM | 13)
104
105#define CPIA_COMMAND_GetVPVersion (INPUT | CPIA_MODULE_VP_CTRL | 1)
106#define CPIA_COMMAND_ResetFrameCounter (INPUT | CPIA_MODULE_VP_CTRL | 2)
107#define CPIA_COMMAND_SetColourParams (OUTPUT | CPIA_MODULE_VP_CTRL | 3)
108#define CPIA_COMMAND_SetExposure (OUTPUT | CPIA_MODULE_VP_CTRL | 4)
109#define CPIA_COMMAND_SetColourBalance (OUTPUT | CPIA_MODULE_VP_CTRL | 6)
110#define CPIA_COMMAND_SetSensorFPS (OUTPUT | CPIA_MODULE_VP_CTRL | 7)
111#define CPIA_COMMAND_SetVPDefaults (OUTPUT | CPIA_MODULE_VP_CTRL | 8)
112#define CPIA_COMMAND_SetApcor (OUTPUT | CPIA_MODULE_VP_CTRL | 9)
113#define CPIA_COMMAND_SetFlickerCtrl (OUTPUT | CPIA_MODULE_VP_CTRL | 10)
114#define CPIA_COMMAND_SetVLOffset (OUTPUT | CPIA_MODULE_VP_CTRL | 11)
115#define CPIA_COMMAND_GetColourParams (INPUT | CPIA_MODULE_VP_CTRL | 16)
116#define CPIA_COMMAND_GetColourBalance (INPUT | CPIA_MODULE_VP_CTRL | 17)
117#define CPIA_COMMAND_GetExposure (INPUT | CPIA_MODULE_VP_CTRL | 18)
118#define CPIA_COMMAND_SetSensorMatrix (OUTPUT | CPIA_MODULE_VP_CTRL | 19)
119#define CPIA_COMMAND_ColourBars (OUTPUT | CPIA_MODULE_VP_CTRL | 25)
120#define CPIA_COMMAND_ReadVPRegs (INPUT | CPIA_MODULE_VP_CTRL | 30)
121#define CPIA_COMMAND_WriteVPReg (OUTPUT | CPIA_MODULE_VP_CTRL | 31)
122
123#define CPIA_COMMAND_GrabFrame (OUTPUT | CPIA_MODULE_CAPTURE | 1)
124#define CPIA_COMMAND_UploadFrame (OUTPUT | CPIA_MODULE_CAPTURE | 2)
125#define CPIA_COMMAND_SetGrabMode (OUTPUT | CPIA_MODULE_CAPTURE | 3)
126#define CPIA_COMMAND_InitStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 4)
127#define CPIA_COMMAND_FiniStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 5)
128#define CPIA_COMMAND_StartStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 6)
129#define CPIA_COMMAND_EndStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 7)
130#define CPIA_COMMAND_SetFormat (OUTPUT | CPIA_MODULE_CAPTURE | 8)
131#define CPIA_COMMAND_SetROI (OUTPUT | CPIA_MODULE_CAPTURE | 9)
132#define CPIA_COMMAND_SetCompression (OUTPUT | CPIA_MODULE_CAPTURE | 10)
133#define CPIA_COMMAND_SetCompressionTarget (OUTPUT | CPIA_MODULE_CAPTURE | 11)
134#define CPIA_COMMAND_SetYUVThresh (OUTPUT | CPIA_MODULE_CAPTURE | 12)
135#define CPIA_COMMAND_SetCompressionParams (OUTPUT | CPIA_MODULE_CAPTURE | 13)
136#define CPIA_COMMAND_DiscardFrame (OUTPUT | CPIA_MODULE_CAPTURE | 14)
137#define CPIA_COMMAND_GrabReset (OUTPUT | CPIA_MODULE_CAPTURE | 15)
138
139#define CPIA_COMMAND_OutputRS232 (OUTPUT | CPIA_MODULE_DEBUG | 1)
140#define CPIA_COMMAND_AbortProcess (OUTPUT | CPIA_MODULE_DEBUG | 4)
141#define CPIA_COMMAND_SetDramPage (OUTPUT | CPIA_MODULE_DEBUG | 5)
142#define CPIA_COMMAND_StartDramUpload (OUTPUT | CPIA_MODULE_DEBUG | 6)
143#define CPIA_COMMAND_StartDummyDtream (OUTPUT | CPIA_MODULE_DEBUG | 8)
144#define CPIA_COMMAND_AbortStream (OUTPUT | CPIA_MODULE_DEBUG | 9)
145#define CPIA_COMMAND_DownloadDRAM (OUTPUT | CPIA_MODULE_DEBUG | 10)
146#define CPIA_COMMAND_Null (OUTPUT | CPIA_MODULE_DEBUG | 11)
147
148enum {
149 FRAME_READY, /* Ready to grab into */
150 FRAME_GRABBING, /* In the process of being grabbed into */
151 FRAME_DONE, /* Finished grabbing, but not been synced yet */
152 FRAME_UNUSED, /* Unused (no MCAPTURE) */
153};
154
155#define COMMAND_NONE 0x0000
156#define COMMAND_SETCOMPRESSION 0x0001
157#define COMMAND_SETCOMPRESSIONTARGET 0x0002
158#define COMMAND_SETCOLOURPARAMS 0x0004
159#define COMMAND_SETFORMAT 0x0008
160#define COMMAND_PAUSE 0x0010
161#define COMMAND_RESUME 0x0020
162#define COMMAND_SETYUVTHRESH 0x0040
163#define COMMAND_SETECPTIMING 0x0080
164#define COMMAND_SETCOMPRESSIONPARAMS 0x0100
165#define COMMAND_SETEXPOSURE 0x0200
166#define COMMAND_SETCOLOURBALANCE 0x0400
167#define COMMAND_SETSENSORFPS 0x0800
168#define COMMAND_SETAPCOR 0x1000
169#define COMMAND_SETFLICKERCTRL 0x2000
170#define COMMAND_SETVLOFFSET 0x4000
171#define COMMAND_SETLIGHTS 0x8000
172
173#define ROUND_UP_EXP_FOR_FLICKER 15
174
175/* Constants for automatic frame rate adjustment */
176#define MAX_EXP 302
177#define MAX_EXP_102 255
178#define LOW_EXP 140
179#define VERY_LOW_EXP 70
180#define TC 94
181#define EXP_ACC_DARK 50
182#define EXP_ACC_LIGHT 90
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300183#define HIGH_COMP_102 160
184#define MAX_COMP 239
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185#define DARK_TIME 3
186#define LIGHT_TIME 3
187
188/* Maximum number of 10ms loops to wait for the stream to become ready */
189#define READY_TIMEOUT 100
190
191/* Developer's Guide Table 5 p 3-34
192 * indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/
193static u8 flicker_jumps[2][2][4] =
194{ { { 76, 38, 19, 9 }, { 92, 46, 23, 11 } },
195 { { 64, 32, 16, 8 }, { 76, 38, 19, 9} }
196};
197
198/* forward declaration of local function */
199static void reset_camera_struct(struct cam_data *cam);
200static int find_over_exposure(int brightness);
201static void set_flicker(struct cam_params *params, volatile u32 *command_flags,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300202 int on);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203
204
205/**********************************************************************
206 *
207 * Memory management
208 *
209 **********************************************************************/
210static void *rvmalloc(unsigned long size)
211{
212 void *mem;
213 unsigned long adr;
214
215 size = PAGE_ALIGN(size);
216 mem = vmalloc_32(size);
217 if (!mem)
218 return NULL;
219
220 memset(mem, 0, size); /* Clear the ram out, no junk to the user */
221 adr = (unsigned long) mem;
222 while (size > 0) {
223 SetPageReserved(vmalloc_to_page((void *)adr));
224 adr += PAGE_SIZE;
225 size -= PAGE_SIZE;
226 }
227
228 return mem;
229}
230
231static void rvfree(void *mem, unsigned long size)
232{
233 unsigned long adr;
234
235 if (!mem)
236 return;
237
238 adr = (unsigned long) mem;
239 while ((long) size > 0) {
240 ClearPageReserved(vmalloc_to_page((void *)adr));
241 adr += PAGE_SIZE;
242 size -= PAGE_SIZE;
243 }
244 vfree(mem);
245}
246
247/**********************************************************************
248 *
249 * /proc interface
250 *
251 **********************************************************************/
252#ifdef CONFIG_PROC_FS
253static struct proc_dir_entry *cpia_proc_root=NULL;
254
255static int cpia_read_proc(char *page, char **start, off_t off,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300256 int count, int *eof, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257{
258 char *out = page;
259 int len, tmp;
260 struct cam_data *cam = data;
261 char tmpstr[29];
262
263 /* IMPORTANT: This output MUST be kept under PAGE_SIZE
264 * or we need to get more sophisticated. */
265
266 out += sprintf(out, "read-only\n-----------------------\n");
267 out += sprintf(out, "V4L Driver version: %d.%d.%d\n",
268 CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER);
269 out += sprintf(out, "CPIA Version: %d.%02d (%d.%d)\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300270 cam->params.version.firmwareVersion,
271 cam->params.version.firmwareRevision,
272 cam->params.version.vcVersion,
273 cam->params.version.vcRevision);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274 out += sprintf(out, "CPIA PnP-ID: %04x:%04x:%04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300275 cam->params.pnpID.vendor, cam->params.pnpID.product,
276 cam->params.pnpID.deviceRevision);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277 out += sprintf(out, "VP-Version: %d.%d %04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300278 cam->params.vpVersion.vpVersion,
279 cam->params.vpVersion.vpRevision,
280 cam->params.vpVersion.cameraHeadID);
281
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282 out += sprintf(out, "system_state: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300283 cam->params.status.systemState);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284 out += sprintf(out, "grab_state: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300285 cam->params.status.grabState);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286 out += sprintf(out, "stream_state: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300287 cam->params.status.streamState);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288 out += sprintf(out, "fatal_error: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300289 cam->params.status.fatalError);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290 out += sprintf(out, "cmd_error: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300291 cam->params.status.cmdError);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 out += sprintf(out, "debug_flags: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300293 cam->params.status.debugFlags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294 out += sprintf(out, "vp_status: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300295 cam->params.status.vpStatus);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296 out += sprintf(out, "error_code: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300297 cam->params.status.errorCode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298 /* QX3 specific entries */
299 if (cam->params.qx3.qx3_detected) {
300 out += sprintf(out, "button: %4d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300301 cam->params.qx3.button);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 out += sprintf(out, "cradled: %4d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300303 cam->params.qx3.cradled);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304 }
305 out += sprintf(out, "video_size: %s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300306 cam->params.format.videoSize == VIDEOSIZE_CIF ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307 "CIF " : "QCIF");
308 out += sprintf(out, "roi: (%3d, %3d) to (%3d, %3d)\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300309 cam->params.roi.colStart*8,
310 cam->params.roi.rowStart*4,
311 cam->params.roi.colEnd*8,
312 cam->params.roi.rowEnd*4);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313 out += sprintf(out, "actual_fps: %3d\n", cam->fps);
314 out += sprintf(out, "transfer_rate: %4dkB/s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300315 cam->transfer_rate);
316
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317 out += sprintf(out, "\nread-write\n");
318 out += sprintf(out, "----------------------- current min"
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300319 " max default comment\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320 out += sprintf(out, "brightness: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300321 cam->params.colourParams.brightness, 0, 100, 50);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 if (cam->params.version.firmwareVersion == 1 &&
323 cam->params.version.firmwareRevision == 2)
324 /* 1-02 firmware limits contrast to 80 */
325 tmp = 80;
326 else
327 tmp = 96;
328
329 out += sprintf(out, "contrast: %8d %8d %8d %8d"
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300330 " steps of 8\n",
331 cam->params.colourParams.contrast, 0, tmp, 48);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332 out += sprintf(out, "saturation: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300333 cam->params.colourParams.saturation, 0, 100, 50);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334 tmp = (25000+5000*cam->params.sensorFps.baserate)/
335 (1<<cam->params.sensorFps.divisor);
336 out += sprintf(out, "sensor_fps: %4d.%03d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300337 tmp/1000, tmp%1000, 3, 30, 15);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 out += sprintf(out, "stream_start_line: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300339 2*cam->params.streamStartLine, 0,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 cam->params.format.videoSize == VIDEOSIZE_CIF ? 288:144,
341 cam->params.format.videoSize == VIDEOSIZE_CIF ? 240:120);
342 out += sprintf(out, "sub_sample: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300343 cam->params.format.subSample == SUBSAMPLE_420 ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 "420" : "422", "420", "422", "422");
345 out += sprintf(out, "yuv_order: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300346 cam->params.format.yuvOrder == YUVORDER_YUYV ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347 "YUYV" : "UYVY", "YUYV" , "UYVY", "YUYV");
348 out += sprintf(out, "ecp_timing: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300349 cam->params.ecpTiming ? "slow" : "normal", "slow",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350 "normal", "normal");
351
352 if (cam->params.colourBalance.balanceMode == 2) {
353 sprintf(tmpstr, "auto");
354 } else {
355 sprintf(tmpstr, "manual");
356 }
357 out += sprintf(out, "color_balance_mode: %8s %8s %8s"
358 " %8s\n", tmpstr, "manual", "auto", "auto");
359 out += sprintf(out, "red_gain: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300360 cam->params.colourBalance.redGain, 0, 212, 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361 out += sprintf(out, "green_gain: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300362 cam->params.colourBalance.greenGain, 0, 212, 6);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363 out += sprintf(out, "blue_gain: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300364 cam->params.colourBalance.blueGain, 0, 212, 92);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365
366 if (cam->params.version.firmwareVersion == 1 &&
367 cam->params.version.firmwareRevision == 2)
368 /* 1-02 firmware limits gain to 2 */
369 sprintf(tmpstr, "%8d %8d %8d", 1, 2, 2);
370 else
371 sprintf(tmpstr, "%8d %8d %8d", 1, 8, 2);
372
373 if (cam->params.exposure.gainMode == 0)
374 out += sprintf(out, "max_gain: unknown %28s"
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300375 " powers of 2\n", tmpstr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 else
377 out += sprintf(out, "max_gain: %8d %28s"
378 " 1,2,4 or 8 \n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300379 1<<(cam->params.exposure.gainMode-1), tmpstr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380
381 switch(cam->params.exposure.expMode) {
382 case 1:
383 case 3:
384 sprintf(tmpstr, "manual");
385 break;
386 case 2:
387 sprintf(tmpstr, "auto");
388 break;
389 default:
390 sprintf(tmpstr, "unknown");
391 break;
392 }
393 out += sprintf(out, "exposure_mode: %8s %8s %8s"
394 " %8s\n", tmpstr, "manual", "auto", "auto");
395 out += sprintf(out, "centre_weight: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300396 (2-cam->params.exposure.centreWeight) ? "on" : "off",
397 "off", "on", "on");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398 out += sprintf(out, "gain: %8d %8d max_gain %8d 1,2,4,8 possible\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300399 1<<cam->params.exposure.gain, 1, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400 if (cam->params.version.firmwareVersion == 1 &&
401 cam->params.version.firmwareRevision == 2)
402 /* 1-02 firmware limits fineExp/2 to 127 */
403 tmp = 254;
404 else
405 tmp = 510;
406
407 out += sprintf(out, "fine_exp: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300408 cam->params.exposure.fineExp*2, 0, tmp, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409 if (cam->params.version.firmwareVersion == 1 &&
410 cam->params.version.firmwareRevision == 2)
411 /* 1-02 firmware limits coarseExpHi to 0 */
412 tmp = MAX_EXP_102;
413 else
414 tmp = MAX_EXP;
415
416 out += sprintf(out, "coarse_exp: %8d %8d %8d"
417 " %8d\n", cam->params.exposure.coarseExpLo+
418 256*cam->params.exposure.coarseExpHi, 0, tmp, 185);
419 out += sprintf(out, "red_comp: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300420 cam->params.exposure.redComp, COMP_RED, 255, COMP_RED);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421 out += sprintf(out, "green1_comp: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300422 cam->params.exposure.green1Comp, COMP_GREEN1, 255,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423 COMP_GREEN1);
424 out += sprintf(out, "green2_comp: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300425 cam->params.exposure.green2Comp, COMP_GREEN2, 255,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426 COMP_GREEN2);
427 out += sprintf(out, "blue_comp: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300428 cam->params.exposure.blueComp, COMP_BLUE, 255, COMP_BLUE);
429
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430 out += sprintf(out, "apcor_gain1: %#8x %#8x %#8x %#8x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300431 cam->params.apcor.gain1, 0, 0xff, 0x1c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 out += sprintf(out, "apcor_gain2: %#8x %#8x %#8x %#8x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300433 cam->params.apcor.gain2, 0, 0xff, 0x1a);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434 out += sprintf(out, "apcor_gain4: %#8x %#8x %#8x %#8x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300435 cam->params.apcor.gain4, 0, 0xff, 0x2d);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436 out += sprintf(out, "apcor_gain8: %#8x %#8x %#8x %#8x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300437 cam->params.apcor.gain8, 0, 0xff, 0x2a);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438 out += sprintf(out, "vl_offset_gain1: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300439 cam->params.vlOffset.gain1, 0, 255, 24);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440 out += sprintf(out, "vl_offset_gain2: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300441 cam->params.vlOffset.gain2, 0, 255, 28);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442 out += sprintf(out, "vl_offset_gain4: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300443 cam->params.vlOffset.gain4, 0, 255, 30);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444 out += sprintf(out, "vl_offset_gain8: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300445 cam->params.vlOffset.gain8, 0, 255, 30);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446 out += sprintf(out, "flicker_control: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300447 cam->params.flickerControl.flickerMode ? "on" : "off",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448 "off", "on", "off");
449 out += sprintf(out, "mains_frequency: %8d %8d %8d %8d"
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300450 " only 50/60\n",
451 cam->mainsFreq ? 60 : 50, 50, 60, 50);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452 if(cam->params.flickerControl.allowableOverExposure < 0)
453 out += sprintf(out, "allowable_overexposure: %4dauto auto %8d auto\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300454 -cam->params.flickerControl.allowableOverExposure,
455 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456 else
457 out += sprintf(out, "allowable_overexposure: %8d auto %8d auto\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300458 cam->params.flickerControl.allowableOverExposure,
459 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460 out += sprintf(out, "compression_mode: ");
461 switch(cam->params.compression.mode) {
462 case CPIA_COMPRESSION_NONE:
463 out += sprintf(out, "%8s", "none");
464 break;
465 case CPIA_COMPRESSION_AUTO:
466 out += sprintf(out, "%8s", "auto");
467 break;
468 case CPIA_COMPRESSION_MANUAL:
469 out += sprintf(out, "%8s", "manual");
470 break;
471 default:
472 out += sprintf(out, "%8s", "unknown");
473 break;
474 }
475 out += sprintf(out, " none,auto,manual auto\n");
476 out += sprintf(out, "decimation_enable: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300477 cam->params.compression.decimation ==
478 DECIMATION_ENAB ? "on":"off", "off", "on",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479 "off");
480 out += sprintf(out, "compression_target: %9s %9s %9s %9s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300481 cam->params.compressionTarget.frTargeting ==
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482 CPIA_COMPRESSION_TARGET_FRAMERATE ?
483 "framerate":"quality",
484 "framerate", "quality", "quality");
485 out += sprintf(out, "target_framerate: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300486 cam->params.compressionTarget.targetFR, 1, 30, 15);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487 out += sprintf(out, "target_quality: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300488 cam->params.compressionTarget.targetQ, 1, 64, 5);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489 out += sprintf(out, "y_threshold: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300490 cam->params.yuvThreshold.yThreshold, 0, 31, 6);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491 out += sprintf(out, "uv_threshold: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300492 cam->params.yuvThreshold.uvThreshold, 0, 31, 6);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493 out += sprintf(out, "hysteresis: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300494 cam->params.compressionParams.hysteresis, 0, 255, 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495 out += sprintf(out, "threshold_max: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300496 cam->params.compressionParams.threshMax, 0, 255, 11);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497 out += sprintf(out, "small_step: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300498 cam->params.compressionParams.smallStep, 0, 255, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499 out += sprintf(out, "large_step: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300500 cam->params.compressionParams.largeStep, 0, 255, 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501 out += sprintf(out, "decimation_hysteresis: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300502 cam->params.compressionParams.decimationHysteresis,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503 0, 255, 2);
504 out += sprintf(out, "fr_diff_step_thresh: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300505 cam->params.compressionParams.frDiffStepThresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506 0, 255, 5);
507 out += sprintf(out, "q_diff_step_thresh: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300508 cam->params.compressionParams.qDiffStepThresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509 0, 255, 3);
510 out += sprintf(out, "decimation_thresh_mod: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300511 cam->params.compressionParams.decimationThreshMod,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512 0, 255, 2);
513 /* QX3 specific entries */
514 if (cam->params.qx3.qx3_detected) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300515 out += sprintf(out, "toplight: %8s %8s %8s %8s\n",
516 cam->params.qx3.toplight ? "on" : "off",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517 "off", "on", "off");
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300518 out += sprintf(out, "bottomlight: %8s %8s %8s %8s\n",
519 cam->params.qx3.bottomlight ? "on" : "off",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520 "off", "on", "off");
521 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300522
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523 len = out - page;
524 len -= off;
525 if (len < count) {
526 *eof = 1;
527 if (len <= 0) return 0;
528 } else
529 len = count;
530
531 *start = page + off;
532 return len;
533}
534
535
536static int match(char *checkstr, char **buffer, unsigned long *count,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300537 int *find_colon, int *err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538{
539 int ret, colon_found = 1;
540 int len = strlen(checkstr);
541 ret = (len <= *count && strncmp(*buffer, checkstr, len) == 0);
542 if (ret) {
543 *buffer += len;
544 *count -= len;
545 if (*find_colon) {
546 colon_found = 0;
547 while (*count && (**buffer == ' ' || **buffer == '\t' ||
548 (!colon_found && **buffer == ':'))) {
549 if (**buffer == ':')
550 colon_found = 1;
551 --*count;
552 ++*buffer;
553 }
554 if (!*count || !colon_found)
555 *err = -EINVAL;
556 *find_colon = 0;
557 }
558 }
559 return ret;
560}
561
562static unsigned long int value(char **buffer, unsigned long *count, int *err)
563{
564 char *p;
565 unsigned long int ret;
566 ret = simple_strtoul(*buffer, &p, 0);
567 if (p == *buffer)
568 *err = -EINVAL;
569 else {
570 *count -= p - *buffer;
571 *buffer = p;
572 }
573 return ret;
574}
575
576static int cpia_write_proc(struct file *file, const char __user *buf,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300577 unsigned long count, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578{
579 struct cam_data *cam = data;
580 struct cam_params new_params;
581 char *page, *buffer;
582 int retval, find_colon;
583 int size = count;
584 unsigned long val = 0;
585 u32 command_flags = 0;
586 u8 new_mains;
587
588 /*
589 * This code to copy from buf to page is shamelessly copied
590 * from the comx driver
591 */
592 if (count > PAGE_SIZE) {
593 printk(KERN_ERR "count is %lu > %d!!!\n", count, (int)PAGE_SIZE);
594 return -ENOSPC;
595 }
596
597 if (!(page = (char *)__get_free_page(GFP_KERNEL))) return -ENOMEM;
598
599 if(copy_from_user(page, buf, count))
600 {
601 retval = -EFAULT;
602 goto out;
603 }
604
605 if (page[count-1] == '\n')
606 page[count-1] = '\0';
607 else if (count < PAGE_SIZE)
608 page[count] = '\0';
609 else if (page[count]) {
610 retval = -EINVAL;
611 goto out;
612 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300613
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614 buffer = page;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300615
Ingo Molnar3593cab2006-02-07 06:49:14 -0200616 if (mutex_lock_interruptible(&cam->param_lock))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617 return -ERESTARTSYS;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300618
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619 /*
620 * Skip over leading whitespace
621 */
622 while (count && isspace(*buffer)) {
623 --count;
624 ++buffer;
625 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300626
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627 memcpy(&new_params, &cam->params, sizeof(struct cam_params));
628 new_mains = cam->mainsFreq;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300629
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630#define MATCH(x) (match(x, &buffer, &count, &find_colon, &retval))
631#define VALUE (value(&buffer,&count, &retval))
632#define FIRMWARE_VERSION(x,y) (new_params.version.firmwareVersion == (x) && \
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300633 new_params.version.firmwareRevision == (y))
634
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635 retval = 0;
636 while (count && !retval) {
637 find_colon = 1;
638 if (MATCH("brightness")) {
639 if (!retval)
640 val = VALUE;
641
642 if (!retval) {
643 if (val <= 100)
644 new_params.colourParams.brightness = val;
645 else
646 retval = -EINVAL;
647 }
648 command_flags |= COMMAND_SETCOLOURPARAMS;
649 if(new_params.flickerControl.allowableOverExposure < 0)
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300650 new_params.flickerControl.allowableOverExposure =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651 -find_over_exposure(new_params.colourParams.brightness);
652 if(new_params.flickerControl.flickerMode != 0)
653 command_flags |= COMMAND_SETFLICKERCTRL;
654
655 } else if (MATCH("contrast")) {
656 if (!retval)
657 val = VALUE;
658
659 if (!retval) {
660 if (val <= 100) {
661 /* contrast is in steps of 8, so round*/
662 val = ((val + 3) / 8) * 8;
663 /* 1-02 firmware limits contrast to 80*/
664 if (FIRMWARE_VERSION(1,2) && val > 80)
665 val = 80;
666
667 new_params.colourParams.contrast = val;
668 } else
669 retval = -EINVAL;
670 }
671 command_flags |= COMMAND_SETCOLOURPARAMS;
672 } else if (MATCH("saturation")) {
673 if (!retval)
674 val = VALUE;
675
676 if (!retval) {
677 if (val <= 100)
678 new_params.colourParams.saturation = val;
679 else
680 retval = -EINVAL;
681 }
682 command_flags |= COMMAND_SETCOLOURPARAMS;
683 } else if (MATCH("sensor_fps")) {
684 if (!retval)
685 val = VALUE;
686
687 if (!retval) {
688 /* find values so that sensorFPS is minimized,
689 * but >= val */
690 if (val > 30)
691 retval = -EINVAL;
692 else if (val > 25) {
693 new_params.sensorFps.divisor = 0;
694 new_params.sensorFps.baserate = 1;
695 } else if (val > 15) {
696 new_params.sensorFps.divisor = 0;
697 new_params.sensorFps.baserate = 0;
698 } else if (val > 12) {
699 new_params.sensorFps.divisor = 1;
700 new_params.sensorFps.baserate = 1;
701 } else if (val > 7) {
702 new_params.sensorFps.divisor = 1;
703 new_params.sensorFps.baserate = 0;
704 } else if (val > 6) {
705 new_params.sensorFps.divisor = 2;
706 new_params.sensorFps.baserate = 1;
707 } else if (val > 3) {
708 new_params.sensorFps.divisor = 2;
709 new_params.sensorFps.baserate = 0;
710 } else {
711 new_params.sensorFps.divisor = 3;
712 /* Either base rate would work here */
713 new_params.sensorFps.baserate = 1;
714 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300715 new_params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716 flicker_jumps[new_mains]
717 [new_params.sensorFps.baserate]
718 [new_params.sensorFps.divisor];
719 if (new_params.flickerControl.flickerMode)
720 command_flags |= COMMAND_SETFLICKERCTRL;
721 }
722 command_flags |= COMMAND_SETSENSORFPS;
723 cam->exposure_status = EXPOSURE_NORMAL;
724 } else if (MATCH("stream_start_line")) {
725 if (!retval)
726 val = VALUE;
727
728 if (!retval) {
729 int max_line = 288;
730
731 if (new_params.format.videoSize == VIDEOSIZE_QCIF)
732 max_line = 144;
733 if (val <= max_line)
734 new_params.streamStartLine = val/2;
735 else
736 retval = -EINVAL;
737 }
738 } else if (MATCH("sub_sample")) {
739 if (!retval && MATCH("420"))
740 new_params.format.subSample = SUBSAMPLE_420;
741 else if (!retval && MATCH("422"))
742 new_params.format.subSample = SUBSAMPLE_422;
743 else
744 retval = -EINVAL;
745
746 command_flags |= COMMAND_SETFORMAT;
747 } else if (MATCH("yuv_order")) {
748 if (!retval && MATCH("YUYV"))
749 new_params.format.yuvOrder = YUVORDER_YUYV;
750 else if (!retval && MATCH("UYVY"))
751 new_params.format.yuvOrder = YUVORDER_UYVY;
752 else
753 retval = -EINVAL;
754
755 command_flags |= COMMAND_SETFORMAT;
756 } else if (MATCH("ecp_timing")) {
757 if (!retval && MATCH("normal"))
758 new_params.ecpTiming = 0;
759 else if (!retval && MATCH("slow"))
760 new_params.ecpTiming = 1;
761 else
762 retval = -EINVAL;
763
764 command_flags |= COMMAND_SETECPTIMING;
765 } else if (MATCH("color_balance_mode")) {
766 if (!retval && MATCH("manual"))
767 new_params.colourBalance.balanceMode = 3;
768 else if (!retval && MATCH("auto"))
769 new_params.colourBalance.balanceMode = 2;
770 else
771 retval = -EINVAL;
772
773 command_flags |= COMMAND_SETCOLOURBALANCE;
774 } else if (MATCH("red_gain")) {
775 if (!retval)
776 val = VALUE;
777
778 if (!retval) {
779 if (val <= 212) {
780 new_params.colourBalance.redGain = val;
781 new_params.colourBalance.balanceMode = 1;
782 } else
783 retval = -EINVAL;
784 }
785 command_flags |= COMMAND_SETCOLOURBALANCE;
786 } else if (MATCH("green_gain")) {
787 if (!retval)
788 val = VALUE;
789
790 if (!retval) {
791 if (val <= 212) {
792 new_params.colourBalance.greenGain = val;
793 new_params.colourBalance.balanceMode = 1;
794 } else
795 retval = -EINVAL;
796 }
797 command_flags |= COMMAND_SETCOLOURBALANCE;
798 } else if (MATCH("blue_gain")) {
799 if (!retval)
800 val = VALUE;
801
802 if (!retval) {
803 if (val <= 212) {
804 new_params.colourBalance.blueGain = val;
805 new_params.colourBalance.balanceMode = 1;
806 } else
807 retval = -EINVAL;
808 }
809 command_flags |= COMMAND_SETCOLOURBALANCE;
810 } else if (MATCH("max_gain")) {
811 if (!retval)
812 val = VALUE;
813
814 if (!retval) {
815 /* 1-02 firmware limits gain to 2 */
816 if (FIRMWARE_VERSION(1,2) && val > 2)
817 val = 2;
818 switch(val) {
819 case 1:
820 new_params.exposure.gainMode = 1;
821 break;
822 case 2:
823 new_params.exposure.gainMode = 2;
824 break;
825 case 4:
826 new_params.exposure.gainMode = 3;
827 break;
828 case 8:
829 new_params.exposure.gainMode = 4;
830 break;
831 default:
832 retval = -EINVAL;
833 break;
834 }
835 }
836 command_flags |= COMMAND_SETEXPOSURE;
837 } else if (MATCH("exposure_mode")) {
838 if (!retval && MATCH("auto"))
839 new_params.exposure.expMode = 2;
840 else if (!retval && MATCH("manual")) {
841 if (new_params.exposure.expMode == 2)
842 new_params.exposure.expMode = 3;
843 if(new_params.flickerControl.flickerMode != 0)
844 command_flags |= COMMAND_SETFLICKERCTRL;
845 new_params.flickerControl.flickerMode = 0;
846 } else
847 retval = -EINVAL;
848
849 command_flags |= COMMAND_SETEXPOSURE;
850 } else if (MATCH("centre_weight")) {
851 if (!retval && MATCH("on"))
852 new_params.exposure.centreWeight = 1;
853 else if (!retval && MATCH("off"))
854 new_params.exposure.centreWeight = 2;
855 else
856 retval = -EINVAL;
857
858 command_flags |= COMMAND_SETEXPOSURE;
859 } else if (MATCH("gain")) {
860 if (!retval)
861 val = VALUE;
862
863 if (!retval) {
864 switch(val) {
865 case 1:
866 new_params.exposure.gain = 0;
867 break;
868 case 2:
869 new_params.exposure.gain = 1;
870 break;
871 case 4:
872 new_params.exposure.gain = 2;
873 break;
874 case 8:
875 new_params.exposure.gain = 3;
876 break;
877 default:
878 retval = -EINVAL;
879 break;
880 }
881 new_params.exposure.expMode = 1;
882 if(new_params.flickerControl.flickerMode != 0)
883 command_flags |= COMMAND_SETFLICKERCTRL;
884 new_params.flickerControl.flickerMode = 0;
885 command_flags |= COMMAND_SETEXPOSURE;
886 if (new_params.exposure.gain >
887 new_params.exposure.gainMode-1)
888 retval = -EINVAL;
889 }
890 } else if (MATCH("fine_exp")) {
891 if (!retval)
892 val = VALUE/2;
893
894 if (!retval) {
895 if (val < 256) {
896 /* 1-02 firmware limits fineExp/2 to 127*/
897 if (FIRMWARE_VERSION(1,2) && val > 127)
898 val = 127;
899 new_params.exposure.fineExp = val;
900 new_params.exposure.expMode = 1;
901 command_flags |= COMMAND_SETEXPOSURE;
902 if(new_params.flickerControl.flickerMode != 0)
903 command_flags |= COMMAND_SETFLICKERCTRL;
904 new_params.flickerControl.flickerMode = 0;
905 command_flags |= COMMAND_SETFLICKERCTRL;
906 } else
907 retval = -EINVAL;
908 }
909 } else if (MATCH("coarse_exp")) {
910 if (!retval)
911 val = VALUE;
912
913 if (!retval) {
914 if (val <= MAX_EXP) {
915 if (FIRMWARE_VERSION(1,2) &&
916 val > MAX_EXP_102)
917 val = MAX_EXP_102;
918 new_params.exposure.coarseExpLo =
919 val & 0xff;
920 new_params.exposure.coarseExpHi =
921 val >> 8;
922 new_params.exposure.expMode = 1;
923 command_flags |= COMMAND_SETEXPOSURE;
924 if(new_params.flickerControl.flickerMode != 0)
925 command_flags |= COMMAND_SETFLICKERCTRL;
926 new_params.flickerControl.flickerMode = 0;
927 command_flags |= COMMAND_SETFLICKERCTRL;
928 } else
929 retval = -EINVAL;
930 }
931 } else if (MATCH("red_comp")) {
932 if (!retval)
933 val = VALUE;
934
935 if (!retval) {
936 if (val >= COMP_RED && val <= 255) {
937 new_params.exposure.redComp = val;
938 new_params.exposure.compMode = 1;
939 command_flags |= COMMAND_SETEXPOSURE;
940 } else
941 retval = -EINVAL;
942 }
943 } else if (MATCH("green1_comp")) {
944 if (!retval)
945 val = VALUE;
946
947 if (!retval) {
948 if (val >= COMP_GREEN1 && val <= 255) {
949 new_params.exposure.green1Comp = val;
950 new_params.exposure.compMode = 1;
951 command_flags |= COMMAND_SETEXPOSURE;
952 } else
953 retval = -EINVAL;
954 }
955 } else if (MATCH("green2_comp")) {
956 if (!retval)
957 val = VALUE;
958
959 if (!retval) {
960 if (val >= COMP_GREEN2 && val <= 255) {
961 new_params.exposure.green2Comp = val;
962 new_params.exposure.compMode = 1;
963 command_flags |= COMMAND_SETEXPOSURE;
964 } else
965 retval = -EINVAL;
966 }
967 } else if (MATCH("blue_comp")) {
968 if (!retval)
969 val = VALUE;
970
971 if (!retval) {
972 if (val >= COMP_BLUE && val <= 255) {
973 new_params.exposure.blueComp = val;
974 new_params.exposure.compMode = 1;
975 command_flags |= COMMAND_SETEXPOSURE;
976 } else
977 retval = -EINVAL;
978 }
979 } else if (MATCH("apcor_gain1")) {
980 if (!retval)
981 val = VALUE;
982
983 if (!retval) {
984 command_flags |= COMMAND_SETAPCOR;
985 if (val <= 0xff)
986 new_params.apcor.gain1 = val;
987 else
988 retval = -EINVAL;
989 }
990 } else if (MATCH("apcor_gain2")) {
991 if (!retval)
992 val = VALUE;
993
994 if (!retval) {
995 command_flags |= COMMAND_SETAPCOR;
996 if (val <= 0xff)
997 new_params.apcor.gain2 = val;
998 else
999 retval = -EINVAL;
1000 }
1001 } else if (MATCH("apcor_gain4")) {
1002 if (!retval)
1003 val = VALUE;
1004
1005 if (!retval) {
1006 command_flags |= COMMAND_SETAPCOR;
1007 if (val <= 0xff)
1008 new_params.apcor.gain4 = val;
1009 else
1010 retval = -EINVAL;
1011 }
1012 } else if (MATCH("apcor_gain8")) {
1013 if (!retval)
1014 val = VALUE;
1015
1016 if (!retval) {
1017 command_flags |= COMMAND_SETAPCOR;
1018 if (val <= 0xff)
1019 new_params.apcor.gain8 = val;
1020 else
1021 retval = -EINVAL;
1022 }
1023 } else if (MATCH("vl_offset_gain1")) {
1024 if (!retval)
1025 val = VALUE;
1026
1027 if (!retval) {
1028 if (val <= 0xff)
1029 new_params.vlOffset.gain1 = val;
1030 else
1031 retval = -EINVAL;
1032 }
1033 command_flags |= COMMAND_SETVLOFFSET;
1034 } else if (MATCH("vl_offset_gain2")) {
1035 if (!retval)
1036 val = VALUE;
1037
1038 if (!retval) {
1039 if (val <= 0xff)
1040 new_params.vlOffset.gain2 = val;
1041 else
1042 retval = -EINVAL;
1043 }
1044 command_flags |= COMMAND_SETVLOFFSET;
1045 } else if (MATCH("vl_offset_gain4")) {
1046 if (!retval)
1047 val = VALUE;
1048
1049 if (!retval) {
1050 if (val <= 0xff)
1051 new_params.vlOffset.gain4 = val;
1052 else
1053 retval = -EINVAL;
1054 }
1055 command_flags |= COMMAND_SETVLOFFSET;
1056 } else if (MATCH("vl_offset_gain8")) {
1057 if (!retval)
1058 val = VALUE;
1059
1060 if (!retval) {
1061 if (val <= 0xff)
1062 new_params.vlOffset.gain8 = val;
1063 else
1064 retval = -EINVAL;
1065 }
1066 command_flags |= COMMAND_SETVLOFFSET;
1067 } else if (MATCH("flicker_control")) {
1068 if (!retval && MATCH("on")) {
1069 set_flicker(&new_params, &command_flags, 1);
1070 } else if (!retval && MATCH("off")) {
1071 set_flicker(&new_params, &command_flags, 0);
1072 } else
1073 retval = -EINVAL;
1074
1075 command_flags |= COMMAND_SETFLICKERCTRL;
1076 } else if (MATCH("mains_frequency")) {
1077 if (!retval && MATCH("50")) {
1078 new_mains = 0;
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 if (!retval && MATCH("60")) {
1086 new_mains = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001087 new_params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088 flicker_jumps[new_mains]
1089 [new_params.sensorFps.baserate]
1090 [new_params.sensorFps.divisor];
1091 if (new_params.flickerControl.flickerMode)
1092 command_flags |= COMMAND_SETFLICKERCTRL;
1093 } else
1094 retval = -EINVAL;
1095 } else if (MATCH("allowable_overexposure")) {
1096 if (!retval && MATCH("auto")) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001097 new_params.flickerControl.allowableOverExposure =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098 -find_over_exposure(new_params.colourParams.brightness);
1099 if(new_params.flickerControl.flickerMode != 0)
1100 command_flags |= COMMAND_SETFLICKERCTRL;
1101 } else {
1102 if (!retval)
1103 val = VALUE;
1104
1105 if (!retval) {
1106 if (val <= 0xff) {
1107 new_params.flickerControl.
1108 allowableOverExposure = val;
1109 if(new_params.flickerControl.flickerMode != 0)
1110 command_flags |= COMMAND_SETFLICKERCTRL;
1111 } else
1112 retval = -EINVAL;
1113 }
1114 }
1115 } else if (MATCH("compression_mode")) {
1116 if (!retval && MATCH("none"))
1117 new_params.compression.mode =
1118 CPIA_COMPRESSION_NONE;
1119 else if (!retval && MATCH("auto"))
1120 new_params.compression.mode =
1121 CPIA_COMPRESSION_AUTO;
1122 else if (!retval && MATCH("manual"))
1123 new_params.compression.mode =
1124 CPIA_COMPRESSION_MANUAL;
1125 else
1126 retval = -EINVAL;
1127
1128 command_flags |= COMMAND_SETCOMPRESSION;
1129 } else if (MATCH("decimation_enable")) {
1130 if (!retval && MATCH("off"))
1131 new_params.compression.decimation = 0;
1132 else if (!retval && MATCH("on"))
1133 new_params.compression.decimation = 1;
1134 else
1135 retval = -EINVAL;
1136
1137 command_flags |= COMMAND_SETCOMPRESSION;
1138 } else if (MATCH("compression_target")) {
1139 if (!retval && MATCH("quality"))
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001140 new_params.compressionTarget.frTargeting =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141 CPIA_COMPRESSION_TARGET_QUALITY;
1142 else if (!retval && MATCH("framerate"))
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001143 new_params.compressionTarget.frTargeting =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144 CPIA_COMPRESSION_TARGET_FRAMERATE;
1145 else
1146 retval = -EINVAL;
1147
1148 command_flags |= COMMAND_SETCOMPRESSIONTARGET;
1149 } else if (MATCH("target_framerate")) {
1150 if (!retval)
1151 val = VALUE;
1152
1153 if (!retval) {
1154 if(val > 0 && val <= 30)
1155 new_params.compressionTarget.targetFR = val;
1156 else
1157 retval = -EINVAL;
1158 }
1159 command_flags |= COMMAND_SETCOMPRESSIONTARGET;
1160 } else if (MATCH("target_quality")) {
1161 if (!retval)
1162 val = VALUE;
1163
1164 if (!retval) {
1165 if(val > 0 && val <= 64)
1166 new_params.compressionTarget.targetQ = val;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001167 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168 retval = -EINVAL;
1169 }
1170 command_flags |= COMMAND_SETCOMPRESSIONTARGET;
1171 } else if (MATCH("y_threshold")) {
1172 if (!retval)
1173 val = VALUE;
1174
1175 if (!retval) {
1176 if (val < 32)
1177 new_params.yuvThreshold.yThreshold = val;
1178 else
1179 retval = -EINVAL;
1180 }
1181 command_flags |= COMMAND_SETYUVTHRESH;
1182 } else if (MATCH("uv_threshold")) {
1183 if (!retval)
1184 val = VALUE;
1185
1186 if (!retval) {
1187 if (val < 32)
1188 new_params.yuvThreshold.uvThreshold = val;
1189 else
1190 retval = -EINVAL;
1191 }
1192 command_flags |= COMMAND_SETYUVTHRESH;
1193 } else if (MATCH("hysteresis")) {
1194 if (!retval)
1195 val = VALUE;
1196
1197 if (!retval) {
1198 if (val <= 0xff)
1199 new_params.compressionParams.hysteresis = val;
1200 else
1201 retval = -EINVAL;
1202 }
1203 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1204 } else if (MATCH("threshold_max")) {
1205 if (!retval)
1206 val = VALUE;
1207
1208 if (!retval) {
1209 if (val <= 0xff)
1210 new_params.compressionParams.threshMax = val;
1211 else
1212 retval = -EINVAL;
1213 }
1214 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1215 } else if (MATCH("small_step")) {
1216 if (!retval)
1217 val = VALUE;
1218
1219 if (!retval) {
1220 if (val <= 0xff)
1221 new_params.compressionParams.smallStep = val;
1222 else
1223 retval = -EINVAL;
1224 }
1225 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1226 } else if (MATCH("large_step")) {
1227 if (!retval)
1228 val = VALUE;
1229
1230 if (!retval) {
1231 if (val <= 0xff)
1232 new_params.compressionParams.largeStep = val;
1233 else
1234 retval = -EINVAL;
1235 }
1236 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1237 } else if (MATCH("decimation_hysteresis")) {
1238 if (!retval)
1239 val = VALUE;
1240
1241 if (!retval) {
1242 if (val <= 0xff)
1243 new_params.compressionParams.decimationHysteresis = val;
1244 else
1245 retval = -EINVAL;
1246 }
1247 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1248 } else if (MATCH("fr_diff_step_thresh")) {
1249 if (!retval)
1250 val = VALUE;
1251
1252 if (!retval) {
1253 if (val <= 0xff)
1254 new_params.compressionParams.frDiffStepThresh = val;
1255 else
1256 retval = -EINVAL;
1257 }
1258 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1259 } else if (MATCH("q_diff_step_thresh")) {
1260 if (!retval)
1261 val = VALUE;
1262
1263 if (!retval) {
1264 if (val <= 0xff)
1265 new_params.compressionParams.qDiffStepThresh = val;
1266 else
1267 retval = -EINVAL;
1268 }
1269 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1270 } else if (MATCH("decimation_thresh_mod")) {
1271 if (!retval)
1272 val = VALUE;
1273
1274 if (!retval) {
1275 if (val <= 0xff)
1276 new_params.compressionParams.decimationThreshMod = val;
1277 else
1278 retval = -EINVAL;
1279 }
1280 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1281 } else if (MATCH("toplight")) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001282 if (!retval && MATCH("on"))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001283 new_params.qx3.toplight = 1;
1284 else if (!retval && MATCH("off"))
1285 new_params.qx3.toplight = 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 if (MATCH("bottomlight")) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001290 if (!retval && MATCH("on"))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291 new_params.qx3.bottomlight = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001292 else if (!retval && MATCH("off"))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293 new_params.qx3.bottomlight = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001294 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001295 retval = -EINVAL;
1296 command_flags |= COMMAND_SETLIGHTS;
1297 } else {
1298 DBG("No match found\n");
1299 retval = -EINVAL;
1300 }
1301
1302 if (!retval) {
1303 while (count && isspace(*buffer) && *buffer != '\n') {
1304 --count;
1305 ++buffer;
1306 }
1307 if (count) {
1308 if (*buffer == '\0' && count != 1)
1309 retval = -EINVAL;
1310 else if (*buffer != '\n' && *buffer != ';' &&
1311 *buffer != '\0')
1312 retval = -EINVAL;
1313 else {
1314 --count;
1315 ++buffer;
1316 }
1317 }
1318 }
1319 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001320#undef MATCH
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321#undef VALUE
1322#undef FIRMWARE_VERSION
1323 if (!retval) {
1324 if (command_flags & COMMAND_SETCOLOURPARAMS) {
1325 /* Adjust cam->vp to reflect these changes */
1326 cam->vp.brightness =
1327 new_params.colourParams.brightness*65535/100;
1328 cam->vp.contrast =
1329 new_params.colourParams.contrast*65535/100;
1330 cam->vp.colour =
1331 new_params.colourParams.saturation*65535/100;
1332 }
1333 if((command_flags & COMMAND_SETEXPOSURE) &&
1334 new_params.exposure.expMode == 2)
1335 cam->exposure_status = EXPOSURE_NORMAL;
1336
1337 memcpy(&cam->params, &new_params, sizeof(struct cam_params));
1338 cam->mainsFreq = new_mains;
1339 cam->cmd_queue |= command_flags;
1340 retval = size;
1341 } else
1342 DBG("error: %d\n", retval);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001343
Ingo Molnar3593cab2006-02-07 06:49:14 -02001344 mutex_unlock(&cam->param_lock);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001345
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346out:
1347 free_page((unsigned long)page);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001348 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349}
1350
1351static void create_proc_cpia_cam(struct cam_data *cam)
1352{
1353 char name[7];
1354 struct proc_dir_entry *ent;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001355
Linus Torvalds1da177e2005-04-16 15:20:36 -07001356 if (!cpia_proc_root || !cam)
1357 return;
1358
1359 sprintf(name, "video%d", cam->vdev.minor);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001360
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361 ent = create_proc_entry(name, S_IFREG|S_IRUGO|S_IWUSR, cpia_proc_root);
1362 if (!ent)
1363 return;
1364
1365 ent->data = cam;
1366 ent->read_proc = cpia_read_proc;
1367 ent->write_proc = cpia_write_proc;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001368 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001369 size of the proc entry is 3736 bytes for the standard webcam;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001370 the extra features of the QX3 microscope add 189 bytes.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371 (we have not yet probed the camera to see which type it is).
1372 */
1373 ent->size = 3736 + 189;
1374 cam->proc_entry = ent;
1375}
1376
1377static void destroy_proc_cpia_cam(struct cam_data *cam)
1378{
1379 char name[7];
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001380
Linus Torvalds1da177e2005-04-16 15:20:36 -07001381 if (!cam || !cam->proc_entry)
1382 return;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001383
Linus Torvalds1da177e2005-04-16 15:20:36 -07001384 sprintf(name, "video%d", cam->vdev.minor);
1385 remove_proc_entry(name, cpia_proc_root);
1386 cam->proc_entry = NULL;
1387}
1388
1389static void proc_cpia_create(void)
1390{
Al Viro66600222005-09-28 22:32:57 +01001391 cpia_proc_root = proc_mkdir("cpia", NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001392
1393 if (cpia_proc_root)
1394 cpia_proc_root->owner = THIS_MODULE;
1395 else
1396 LOG("Unable to initialise /proc/cpia\n");
1397}
1398
1399static void __exit proc_cpia_destroy(void)
1400{
1401 remove_proc_entry("cpia", NULL);
1402}
1403#endif /* CONFIG_PROC_FS */
1404
1405/* ----------------------- debug functions ---------------------- */
1406
1407#define printstatus(cam) \
1408 DBG("%02x %02x %02x %02x %02x %02x %02x %02x\n",\
1409 cam->params.status.systemState, cam->params.status.grabState, \
1410 cam->params.status.streamState, cam->params.status.fatalError, \
1411 cam->params.status.cmdError, cam->params.status.debugFlags, \
1412 cam->params.status.vpStatus, cam->params.status.errorCode);
1413
1414/* ----------------------- v4l helpers -------------------------- */
1415
1416/* supported frame palettes and depths */
1417static inline int valid_mode(u16 palette, u16 depth)
1418{
1419 if ((palette == VIDEO_PALETTE_YUV422 && depth == 16) ||
1420 (palette == VIDEO_PALETTE_YUYV && depth == 16))
1421 return 1;
1422
1423 if (colorspace_conv)
1424 return (palette == VIDEO_PALETTE_GREY && depth == 8) ||
1425 (palette == VIDEO_PALETTE_RGB555 && depth == 16) ||
1426 (palette == VIDEO_PALETTE_RGB565 && depth == 16) ||
1427 (palette == VIDEO_PALETTE_RGB24 && depth == 24) ||
1428 (palette == VIDEO_PALETTE_RGB32 && depth == 32) ||
1429 (palette == VIDEO_PALETTE_UYVY && depth == 16);
1430
1431 return 0;
1432}
1433
1434static int match_videosize( int width, int height )
1435{
1436 /* return the best match, where 'best' is as always
1437 * the largest that is not bigger than what is requested. */
1438 if (width>=352 && height>=288)
1439 return VIDEOSIZE_352_288; /* CIF */
1440
1441 if (width>=320 && height>=240)
1442 return VIDEOSIZE_320_240; /* SIF */
1443
1444 if (width>=288 && height>=216)
1445 return VIDEOSIZE_288_216;
1446
1447 if (width>=256 && height>=192)
1448 return VIDEOSIZE_256_192;
1449
1450 if (width>=224 && height>=168)
1451 return VIDEOSIZE_224_168;
1452
1453 if (width>=192 && height>=144)
1454 return VIDEOSIZE_192_144;
1455
1456 if (width>=176 && height>=144)
1457 return VIDEOSIZE_176_144; /* QCIF */
1458
1459 if (width>=160 && height>=120)
1460 return VIDEOSIZE_160_120; /* QSIF */
1461
1462 if (width>=128 && height>=96)
1463 return VIDEOSIZE_128_96;
1464
1465 if (width>=88 && height>=72)
1466 return VIDEOSIZE_88_72;
1467
1468 if (width>=64 && height>=48)
1469 return VIDEOSIZE_64_48;
1470
1471 if (width>=48 && height>=48)
1472 return VIDEOSIZE_48_48;
1473
1474 return -1;
1475}
1476
1477/* these are the capture sizes we support */
1478static void set_vw_size(struct cam_data *cam)
1479{
1480 /* the col/row/start/end values are the result of simple math */
1481 /* study the SetROI-command in cpia developers guide p 2-22 */
1482 /* streamStartLine is set to the recommended value in the cpia */
1483 /* developers guide p 3-37 */
1484 switch(cam->video_size) {
1485 case VIDEOSIZE_CIF:
1486 cam->vw.width = 352;
1487 cam->vw.height = 288;
1488 cam->params.format.videoSize=VIDEOSIZE_CIF;
1489 cam->params.roi.colStart=0;
1490 cam->params.roi.rowStart=0;
1491 cam->params.streamStartLine = 120;
1492 break;
1493 case VIDEOSIZE_SIF:
1494 cam->vw.width = 320;
1495 cam->vw.height = 240;
1496 cam->params.format.videoSize=VIDEOSIZE_CIF;
1497 cam->params.roi.colStart=2;
1498 cam->params.roi.rowStart=6;
1499 cam->params.streamStartLine = 120;
1500 break;
1501 case VIDEOSIZE_288_216:
1502 cam->vw.width = 288;
1503 cam->vw.height = 216;
1504 cam->params.format.videoSize=VIDEOSIZE_CIF;
1505 cam->params.roi.colStart=4;
1506 cam->params.roi.rowStart=9;
1507 cam->params.streamStartLine = 120;
1508 break;
1509 case VIDEOSIZE_256_192:
1510 cam->vw.width = 256;
1511 cam->vw.height = 192;
1512 cam->params.format.videoSize=VIDEOSIZE_CIF;
1513 cam->params.roi.colStart=6;
1514 cam->params.roi.rowStart=12;
1515 cam->params.streamStartLine = 120;
1516 break;
1517 case VIDEOSIZE_224_168:
1518 cam->vw.width = 224;
1519 cam->vw.height = 168;
1520 cam->params.format.videoSize=VIDEOSIZE_CIF;
1521 cam->params.roi.colStart=8;
1522 cam->params.roi.rowStart=15;
1523 cam->params.streamStartLine = 120;
1524 break;
1525 case VIDEOSIZE_192_144:
1526 cam->vw.width = 192;
1527 cam->vw.height = 144;
1528 cam->params.format.videoSize=VIDEOSIZE_CIF;
1529 cam->params.roi.colStart=10;
1530 cam->params.roi.rowStart=18;
1531 cam->params.streamStartLine = 120;
1532 break;
1533 case VIDEOSIZE_QCIF:
1534 cam->vw.width = 176;
1535 cam->vw.height = 144;
1536 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1537 cam->params.roi.colStart=0;
1538 cam->params.roi.rowStart=0;
1539 cam->params.streamStartLine = 60;
1540 break;
1541 case VIDEOSIZE_QSIF:
1542 cam->vw.width = 160;
1543 cam->vw.height = 120;
1544 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1545 cam->params.roi.colStart=1;
1546 cam->params.roi.rowStart=3;
1547 cam->params.streamStartLine = 60;
1548 break;
1549 case VIDEOSIZE_128_96:
1550 cam->vw.width = 128;
1551 cam->vw.height = 96;
1552 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1553 cam->params.roi.colStart=3;
1554 cam->params.roi.rowStart=6;
1555 cam->params.streamStartLine = 60;
1556 break;
1557 case VIDEOSIZE_88_72:
1558 cam->vw.width = 88;
1559 cam->vw.height = 72;
1560 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1561 cam->params.roi.colStart=5;
1562 cam->params.roi.rowStart=9;
1563 cam->params.streamStartLine = 60;
1564 break;
1565 case VIDEOSIZE_64_48:
1566 cam->vw.width = 64;
1567 cam->vw.height = 48;
1568 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1569 cam->params.roi.colStart=7;
1570 cam->params.roi.rowStart=12;
1571 cam->params.streamStartLine = 60;
1572 break;
1573 case VIDEOSIZE_48_48:
1574 cam->vw.width = 48;
1575 cam->vw.height = 48;
1576 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1577 cam->params.roi.colStart=8;
1578 cam->params.roi.rowStart=6;
1579 cam->params.streamStartLine = 60;
1580 break;
1581 default:
1582 LOG("bad videosize value: %d\n", cam->video_size);
1583 return;
1584 }
1585
1586 if(cam->vc.width == 0)
1587 cam->vc.width = cam->vw.width;
1588 if(cam->vc.height == 0)
1589 cam->vc.height = cam->vw.height;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001590
Linus Torvalds1da177e2005-04-16 15:20:36 -07001591 cam->params.roi.colStart += cam->vc.x >> 3;
1592 cam->params.roi.colEnd = cam->params.roi.colStart +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001593 (cam->vc.width >> 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001594 cam->params.roi.rowStart += cam->vc.y >> 2;
1595 cam->params.roi.rowEnd = cam->params.roi.rowStart +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001596 (cam->vc.height >> 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001597
1598 return;
1599}
1600
1601static int allocate_frame_buf(struct cam_data *cam)
1602{
1603 int i;
1604
1605 cam->frame_buf = rvmalloc(FRAME_NUM * CPIA_MAX_FRAME_SIZE);
1606 if (!cam->frame_buf)
1607 return -ENOBUFS;
1608
1609 for (i = 0; i < FRAME_NUM; i++)
1610 cam->frame[i].data = cam->frame_buf + i * CPIA_MAX_FRAME_SIZE;
1611
1612 return 0;
1613}
1614
1615static int free_frame_buf(struct cam_data *cam)
1616{
1617 int i;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001618
Linus Torvalds1da177e2005-04-16 15:20:36 -07001619 rvfree(cam->frame_buf, FRAME_NUM*CPIA_MAX_FRAME_SIZE);
1620 cam->frame_buf = NULL;
1621 for (i=0; i < FRAME_NUM; i++)
1622 cam->frame[i].data = NULL;
1623
1624 return 0;
1625}
1626
1627
1628static inline void free_frames(struct cpia_frame frame[FRAME_NUM])
1629{
1630 int i;
1631
1632 for (i=0; i < FRAME_NUM; i++)
1633 frame[i].state = FRAME_UNUSED;
1634 return;
1635}
1636
1637/**********************************************************************
1638 *
1639 * General functions
1640 *
1641 **********************************************************************/
1642/* send an arbitrary command to the camera */
1643static int do_command(struct cam_data *cam, u16 command, u8 a, u8 b, u8 c, u8 d)
1644{
1645 int retval, datasize;
1646 u8 cmd[8], data[8];
1647
1648 switch(command) {
1649 case CPIA_COMMAND_GetCPIAVersion:
1650 case CPIA_COMMAND_GetPnPID:
1651 case CPIA_COMMAND_GetCameraStatus:
1652 case CPIA_COMMAND_GetVPVersion:
1653 datasize=8;
1654 break;
1655 case CPIA_COMMAND_GetColourParams:
1656 case CPIA_COMMAND_GetColourBalance:
1657 case CPIA_COMMAND_GetExposure:
Ingo Molnar3593cab2006-02-07 06:49:14 -02001658 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001659 datasize=8;
1660 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001661 case CPIA_COMMAND_ReadMCPorts:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001662 case CPIA_COMMAND_ReadVCRegs:
1663 datasize = 4;
1664 break;
1665 default:
1666 datasize=0;
1667 break;
1668 }
1669
1670 cmd[0] = command>>8;
1671 cmd[1] = command&0xff;
1672 cmd[2] = a;
1673 cmd[3] = b;
1674 cmd[4] = c;
1675 cmd[5] = d;
1676 cmd[6] = datasize;
1677 cmd[7] = 0;
1678
1679 retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
1680 if (retval) {
1681 DBG("%x - failed, retval=%d\n", command, retval);
1682 if (command == CPIA_COMMAND_GetColourParams ||
1683 command == CPIA_COMMAND_GetColourBalance ||
1684 command == CPIA_COMMAND_GetExposure)
Ingo Molnar3593cab2006-02-07 06:49:14 -02001685 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001686 } else {
1687 switch(command) {
1688 case CPIA_COMMAND_GetCPIAVersion:
1689 cam->params.version.firmwareVersion = data[0];
1690 cam->params.version.firmwareRevision = data[1];
1691 cam->params.version.vcVersion = data[2];
1692 cam->params.version.vcRevision = data[3];
1693 break;
1694 case CPIA_COMMAND_GetPnPID:
1695 cam->params.pnpID.vendor = data[0]+(((u16)data[1])<<8);
1696 cam->params.pnpID.product = data[2]+(((u16)data[3])<<8);
1697 cam->params.pnpID.deviceRevision =
1698 data[4]+(((u16)data[5])<<8);
1699 break;
1700 case CPIA_COMMAND_GetCameraStatus:
1701 cam->params.status.systemState = data[0];
1702 cam->params.status.grabState = data[1];
1703 cam->params.status.streamState = data[2];
1704 cam->params.status.fatalError = data[3];
1705 cam->params.status.cmdError = data[4];
1706 cam->params.status.debugFlags = data[5];
1707 cam->params.status.vpStatus = data[6];
1708 cam->params.status.errorCode = data[7];
1709 break;
1710 case CPIA_COMMAND_GetVPVersion:
1711 cam->params.vpVersion.vpVersion = data[0];
1712 cam->params.vpVersion.vpRevision = data[1];
1713 cam->params.vpVersion.cameraHeadID =
1714 data[2]+(((u16)data[3])<<8);
1715 break;
1716 case CPIA_COMMAND_GetColourParams:
1717 cam->params.colourParams.brightness = data[0];
1718 cam->params.colourParams.contrast = data[1];
1719 cam->params.colourParams.saturation = data[2];
Ingo Molnar3593cab2006-02-07 06:49:14 -02001720 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001721 break;
1722 case CPIA_COMMAND_GetColourBalance:
1723 cam->params.colourBalance.redGain = data[0];
1724 cam->params.colourBalance.greenGain = data[1];
1725 cam->params.colourBalance.blueGain = data[2];
Ingo Molnar3593cab2006-02-07 06:49:14 -02001726 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001727 break;
1728 case CPIA_COMMAND_GetExposure:
1729 cam->params.exposure.gain = data[0];
1730 cam->params.exposure.fineExp = data[1];
1731 cam->params.exposure.coarseExpLo = data[2];
1732 cam->params.exposure.coarseExpHi = data[3];
1733 cam->params.exposure.redComp = data[4];
1734 cam->params.exposure.green1Comp = data[5];
1735 cam->params.exposure.green2Comp = data[6];
1736 cam->params.exposure.blueComp = data[7];
Ingo Molnar3593cab2006-02-07 06:49:14 -02001737 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001738 break;
1739
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001740 case CPIA_COMMAND_ReadMCPorts:
1741 if (!cam->params.qx3.qx3_detected)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001742 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001743 /* test button press */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001744 cam->params.qx3.button = ((data[1] & 0x02) == 0);
1745 if (cam->params.qx3.button) {
1746 /* button pressed - unlock the latch */
1747 do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xDF,0xDF,0);
1748 do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xFF,0xFF,0);
1749 }
1750
1751 /* test whether microscope is cradled */
1752 cam->params.qx3.cradled = ((data[2] & 0x40) == 0);
1753 break;
1754
1755 default:
1756 break;
1757 }
1758 }
1759 return retval;
1760}
1761
1762/* send a command to the camera with an additional data transaction */
1763static int do_command_extended(struct cam_data *cam, u16 command,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001764 u8 a, u8 b, u8 c, u8 d,
1765 u8 e, u8 f, u8 g, u8 h,
1766 u8 i, u8 j, u8 k, u8 l)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001767{
1768 int retval;
1769 u8 cmd[8], data[8];
1770
1771 cmd[0] = command>>8;
1772 cmd[1] = command&0xff;
1773 cmd[2] = a;
1774 cmd[3] = b;
1775 cmd[4] = c;
1776 cmd[5] = d;
1777 cmd[6] = 8;
1778 cmd[7] = 0;
1779 data[0] = e;
1780 data[1] = f;
1781 data[2] = g;
1782 data[3] = h;
1783 data[4] = i;
1784 data[5] = j;
1785 data[6] = k;
1786 data[7] = l;
1787
1788 retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
1789 if (retval)
1790 DBG("%x - failed\n", command);
1791
1792 return retval;
1793}
1794
1795/**********************************************************************
1796 *
1797 * Colorspace conversion
1798 *
1799 **********************************************************************/
1800#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16)
1801
1802static int convert420(unsigned char *yuv, unsigned char *rgb, int out_fmt,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001803 int linesize, int mmap_kludge)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001804{
1805 int y, u, v, r, g, b, y1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001806
Linus Torvalds1da177e2005-04-16 15:20:36 -07001807 /* Odd lines use the same u and v as the previous line.
1808 * Because of compression, it is necessary to get this
1809 * information from the decoded image. */
1810 switch(out_fmt) {
1811 case VIDEO_PALETTE_RGB555:
1812 y = (*yuv++ - 16) * 76310;
1813 y1 = (*yuv - 16) * 76310;
1814 r = ((*(rgb+1-linesize)) & 0x7c) << 1;
1815 g = ((*(rgb-linesize)) & 0xe0) >> 4 |
1816 ((*(rgb+1-linesize)) & 0x03) << 6;
1817 b = ((*(rgb-linesize)) & 0x1f) << 3;
1818 u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
1819 v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
1820 r = 104635 * v;
1821 g = -25690 * u - 53294 * v;
1822 b = 132278 * u;
1823 *rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3);
1824 *rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6);
1825 *rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3);
1826 *rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6);
1827 return 4;
1828 case VIDEO_PALETTE_RGB565:
1829 y = (*yuv++ - 16) * 76310;
1830 y1 = (*yuv - 16) * 76310;
1831 r = (*(rgb+1-linesize)) & 0xf8;
1832 g = ((*(rgb-linesize)) & 0xe0) >> 3 |
1833 ((*(rgb+1-linesize)) & 0x07) << 5;
1834 b = ((*(rgb-linesize)) & 0x1f) << 3;
1835 u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
1836 v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
1837 r = 104635 * v;
1838 g = -25690 * u - 53294 * v;
1839 b = 132278 * u;
1840 *rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3);
1841 *rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5);
1842 *rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3);
1843 *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5);
1844 return 4;
1845 break;
1846 case VIDEO_PALETTE_RGB24:
1847 case VIDEO_PALETTE_RGB32:
1848 y = (*yuv++ - 16) * 76310;
1849 y1 = (*yuv - 16) * 76310;
1850 if (mmap_kludge) {
1851 r = *(rgb+2-linesize);
1852 g = *(rgb+1-linesize);
1853 b = *(rgb-linesize);
1854 } else {
1855 r = *(rgb-linesize);
1856 g = *(rgb+1-linesize);
1857 b = *(rgb+2-linesize);
1858 }
1859 u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
1860 v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
1861 r = 104635 * v;
1862 g = -25690 * u + -53294 * v;
1863 b = 132278 * u;
1864 if (mmap_kludge) {
1865 *rgb++ = LIMIT(b+y);
1866 *rgb++ = LIMIT(g+y);
1867 *rgb++ = LIMIT(r+y);
1868 if(out_fmt == VIDEO_PALETTE_RGB32)
1869 rgb++;
1870 *rgb++ = LIMIT(b+y1);
1871 *rgb++ = LIMIT(g+y1);
1872 *rgb = LIMIT(r+y1);
1873 } else {
1874 *rgb++ = LIMIT(r+y);
1875 *rgb++ = LIMIT(g+y);
1876 *rgb++ = LIMIT(b+y);
1877 if(out_fmt == VIDEO_PALETTE_RGB32)
1878 rgb++;
1879 *rgb++ = LIMIT(r+y1);
1880 *rgb++ = LIMIT(g+y1);
1881 *rgb = LIMIT(b+y1);
1882 }
1883 if(out_fmt == VIDEO_PALETTE_RGB32)
1884 return 8;
1885 return 6;
1886 case VIDEO_PALETTE_YUV422:
1887 case VIDEO_PALETTE_YUYV:
1888 y = *yuv++;
1889 u = *(rgb+1-linesize);
1890 y1 = *yuv;
1891 v = *(rgb+3-linesize);
1892 *rgb++ = y;
1893 *rgb++ = u;
1894 *rgb++ = y1;
1895 *rgb = v;
1896 return 4;
1897 case VIDEO_PALETTE_UYVY:
1898 u = *(rgb-linesize);
1899 y = *yuv++;
1900 v = *(rgb+2-linesize);
1901 y1 = *yuv;
1902 *rgb++ = u;
1903 *rgb++ = y;
1904 *rgb++ = v;
1905 *rgb = y1;
1906 return 4;
1907 case VIDEO_PALETTE_GREY:
1908 *rgb++ = *yuv++;
1909 *rgb = *yuv;
1910 return 2;
1911 default:
1912 DBG("Empty: %d\n", out_fmt);
1913 return 0;
1914 }
1915}
1916
1917
1918static int yuvconvert(unsigned char *yuv, unsigned char *rgb, int out_fmt,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001919 int in_uyvy, int mmap_kludge)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001920{
1921 int y, u, v, r, g, b, y1;
1922
1923 switch(out_fmt) {
1924 case VIDEO_PALETTE_RGB555:
1925 case VIDEO_PALETTE_RGB565:
1926 case VIDEO_PALETTE_RGB24:
1927 case VIDEO_PALETTE_RGB32:
1928 if (in_uyvy) {
1929 u = *yuv++ - 128;
1930 y = (*yuv++ - 16) * 76310;
1931 v = *yuv++ - 128;
1932 y1 = (*yuv - 16) * 76310;
1933 } else {
1934 y = (*yuv++ - 16) * 76310;
1935 u = *yuv++ - 128;
1936 y1 = (*yuv++ - 16) * 76310;
1937 v = *yuv - 128;
1938 }
1939 r = 104635 * v;
1940 g = -25690 * u + -53294 * v;
1941 b = 132278 * u;
1942 break;
1943 default:
1944 y = *yuv++;
1945 u = *yuv++;
1946 y1 = *yuv++;
1947 v = *yuv;
1948 /* Just to avoid compiler warnings */
1949 r = 0;
1950 g = 0;
1951 b = 0;
1952 break;
1953 }
1954 switch(out_fmt) {
1955 case VIDEO_PALETTE_RGB555:
1956 *rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3);
1957 *rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6);
1958 *rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3);
1959 *rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6);
1960 return 4;
1961 case VIDEO_PALETTE_RGB565:
1962 *rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3);
1963 *rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5);
1964 *rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3);
1965 *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5);
1966 return 4;
1967 case VIDEO_PALETTE_RGB24:
1968 if (mmap_kludge) {
1969 *rgb++ = LIMIT(b+y);
1970 *rgb++ = LIMIT(g+y);
1971 *rgb++ = LIMIT(r+y);
1972 *rgb++ = LIMIT(b+y1);
1973 *rgb++ = LIMIT(g+y1);
1974 *rgb = LIMIT(r+y1);
1975 } else {
1976 *rgb++ = LIMIT(r+y);
1977 *rgb++ = LIMIT(g+y);
1978 *rgb++ = LIMIT(b+y);
1979 *rgb++ = LIMIT(r+y1);
1980 *rgb++ = LIMIT(g+y1);
1981 *rgb = LIMIT(b+y1);
1982 }
1983 return 6;
1984 case VIDEO_PALETTE_RGB32:
1985 if (mmap_kludge) {
1986 *rgb++ = LIMIT(b+y);
1987 *rgb++ = LIMIT(g+y);
1988 *rgb++ = LIMIT(r+y);
1989 rgb++;
1990 *rgb++ = LIMIT(b+y1);
1991 *rgb++ = LIMIT(g+y1);
1992 *rgb = LIMIT(r+y1);
1993 } else {
1994 *rgb++ = LIMIT(r+y);
1995 *rgb++ = LIMIT(g+y);
1996 *rgb++ = LIMIT(b+y);
1997 rgb++;
1998 *rgb++ = LIMIT(r+y1);
1999 *rgb++ = LIMIT(g+y1);
2000 *rgb = LIMIT(b+y1);
2001 }
2002 return 8;
2003 case VIDEO_PALETTE_GREY:
2004 *rgb++ = y;
2005 *rgb = y1;
2006 return 2;
2007 case VIDEO_PALETTE_YUV422:
2008 case VIDEO_PALETTE_YUYV:
2009 *rgb++ = y;
2010 *rgb++ = u;
2011 *rgb++ = y1;
2012 *rgb = v;
2013 return 4;
2014 case VIDEO_PALETTE_UYVY:
2015 *rgb++ = u;
2016 *rgb++ = y;
2017 *rgb++ = v;
2018 *rgb = y1;
2019 return 4;
2020 default:
2021 DBG("Empty: %d\n", out_fmt);
2022 return 0;
2023 }
2024}
2025
2026static int skipcount(int count, int fmt)
2027{
2028 switch(fmt) {
2029 case VIDEO_PALETTE_GREY:
2030 return count;
2031 case VIDEO_PALETTE_RGB555:
2032 case VIDEO_PALETTE_RGB565:
2033 case VIDEO_PALETTE_YUV422:
2034 case VIDEO_PALETTE_YUYV:
2035 case VIDEO_PALETTE_UYVY:
2036 return 2*count;
2037 case VIDEO_PALETTE_RGB24:
2038 return 3*count;
2039 case VIDEO_PALETTE_RGB32:
2040 return 4*count;
2041 default:
2042 return 0;
2043 }
2044}
2045
2046static int parse_picture(struct cam_data *cam, int size)
2047{
2048 u8 *obuf, *ibuf, *end_obuf;
2049 int ll, in_uyvy, compressed, decimation, even_line, origsize, out_fmt;
2050 int rows, cols, linesize, subsample_422;
2051
2052 /* make sure params don't change while we are decoding */
Ingo Molnar3593cab2006-02-07 06:49:14 -02002053 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002054
2055 obuf = cam->decompressed_frame.data;
2056 end_obuf = obuf+CPIA_MAX_FRAME_SIZE;
2057 ibuf = cam->raw_image;
2058 origsize = size;
2059 out_fmt = cam->vp.palette;
2060
2061 if ((ibuf[0] != MAGIC_0) || (ibuf[1] != MAGIC_1)) {
2062 LOG("header not found\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02002063 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002064 return -1;
2065 }
2066
2067 if ((ibuf[16] != VIDEOSIZE_QCIF) && (ibuf[16] != VIDEOSIZE_CIF)) {
2068 LOG("wrong video size\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02002069 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002070 return -1;
2071 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002072
Linus Torvalds1da177e2005-04-16 15:20:36 -07002073 if (ibuf[17] != SUBSAMPLE_420 && ibuf[17] != SUBSAMPLE_422) {
2074 LOG("illegal subtype %d\n",ibuf[17]);
Ingo Molnar3593cab2006-02-07 06:49:14 -02002075 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002076 return -1;
2077 }
2078 subsample_422 = ibuf[17] == SUBSAMPLE_422;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002079
Linus Torvalds1da177e2005-04-16 15:20:36 -07002080 if (ibuf[18] != YUVORDER_YUYV && ibuf[18] != YUVORDER_UYVY) {
2081 LOG("illegal yuvorder %d\n",ibuf[18]);
Ingo Molnar3593cab2006-02-07 06:49:14 -02002082 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002083 return -1;
2084 }
2085 in_uyvy = ibuf[18] == YUVORDER_UYVY;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002086
Linus Torvalds1da177e2005-04-16 15:20:36 -07002087 if ((ibuf[24] != cam->params.roi.colStart) ||
2088 (ibuf[25] != cam->params.roi.colEnd) ||
2089 (ibuf[26] != cam->params.roi.rowStart) ||
2090 (ibuf[27] != cam->params.roi.rowEnd)) {
2091 LOG("ROI mismatch\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02002092 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002093 return -1;
2094 }
2095 cols = 8*(ibuf[25] - ibuf[24]);
2096 rows = 4*(ibuf[27] - ibuf[26]);
2097
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002098
Linus Torvalds1da177e2005-04-16 15:20:36 -07002099 if ((ibuf[28] != NOT_COMPRESSED) && (ibuf[28] != COMPRESSED)) {
2100 LOG("illegal compression %d\n",ibuf[28]);
Ingo Molnar3593cab2006-02-07 06:49:14 -02002101 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002102 return -1;
2103 }
2104 compressed = (ibuf[28] == COMPRESSED);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002105
Linus Torvalds1da177e2005-04-16 15:20:36 -07002106 if (ibuf[29] != NO_DECIMATION && ibuf[29] != DECIMATION_ENAB) {
2107 LOG("illegal decimation %d\n",ibuf[29]);
Ingo Molnar3593cab2006-02-07 06:49:14 -02002108 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002109 return -1;
2110 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002111 decimation = (ibuf[29] == DECIMATION_ENAB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002112
2113 cam->params.yuvThreshold.yThreshold = ibuf[30];
2114 cam->params.yuvThreshold.uvThreshold = ibuf[31];
2115 cam->params.status.systemState = ibuf[32];
2116 cam->params.status.grabState = ibuf[33];
2117 cam->params.status.streamState = ibuf[34];
2118 cam->params.status.fatalError = ibuf[35];
2119 cam->params.status.cmdError = ibuf[36];
2120 cam->params.status.debugFlags = ibuf[37];
2121 cam->params.status.vpStatus = ibuf[38];
2122 cam->params.status.errorCode = ibuf[39];
2123 cam->fps = ibuf[41];
Ingo Molnar3593cab2006-02-07 06:49:14 -02002124 mutex_unlock(&cam->param_lock);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002125
Linus Torvalds1da177e2005-04-16 15:20:36 -07002126 linesize = skipcount(cols, out_fmt);
2127 ibuf += FRAME_HEADER_SIZE;
2128 size -= FRAME_HEADER_SIZE;
2129 ll = ibuf[0] | (ibuf[1] << 8);
2130 ibuf += 2;
2131 even_line = 1;
2132
2133 while (size > 0) {
2134 size -= (ll+2);
2135 if (size < 0) {
2136 LOG("Insufficient data in buffer\n");
2137 return -1;
2138 }
2139
2140 while (ll > 1) {
2141 if (!compressed || (compressed && !(*ibuf & 1))) {
2142 if(subsample_422 || even_line) {
2143 obuf += yuvconvert(ibuf, obuf, out_fmt,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002144 in_uyvy, cam->mmap_kludge);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002145 ibuf += 4;
2146 ll -= 4;
2147 } else {
2148 /* SUBSAMPLE_420 on an odd line */
2149 obuf += convert420(ibuf, obuf,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002150 out_fmt, linesize,
2151 cam->mmap_kludge);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002152 ibuf += 2;
2153 ll -= 2;
2154 }
2155 } else {
2156 /*skip compressed interval from previous frame*/
2157 obuf += skipcount(*ibuf >> 1, out_fmt);
2158 if (obuf > end_obuf) {
2159 LOG("Insufficient buffer size\n");
2160 return -1;
2161 }
2162 ++ibuf;
2163 ll--;
2164 }
2165 }
2166 if (ll == 1) {
2167 if (*ibuf != EOL) {
2168 DBG("EOL not found giving up after %d/%d"
2169 " bytes\n", origsize-size, origsize);
2170 return -1;
2171 }
2172
2173 ++ibuf; /* skip over EOL */
2174
2175 if ((size > 3) && (ibuf[0] == EOI) && (ibuf[1] == EOI) &&
2176 (ibuf[2] == EOI) && (ibuf[3] == EOI)) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002177 size -= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002178 break;
2179 }
2180
2181 if(decimation) {
2182 /* skip the odd lines for now */
2183 obuf += linesize;
2184 }
2185
2186 if (size > 1) {
2187 ll = ibuf[0] | (ibuf[1] << 8);
2188 ibuf += 2; /* skip over line length */
2189 }
2190 if(!decimation)
2191 even_line = !even_line;
2192 } else {
2193 LOG("line length was not 1 but %d after %d/%d bytes\n",
2194 ll, origsize-size, origsize);
2195 return -1;
2196 }
2197 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002198
Linus Torvalds1da177e2005-04-16 15:20:36 -07002199 if(decimation) {
2200 /* interpolate odd rows */
2201 int i, j;
2202 u8 *prev, *next;
2203 prev = cam->decompressed_frame.data;
2204 obuf = prev+linesize;
2205 next = obuf+linesize;
2206 for(i=1; i<rows-1; i+=2) {
2207 for(j=0; j<linesize; ++j) {
2208 *obuf++ = ((int)*prev++ + *next++) / 2;
2209 }
2210 prev += linesize;
2211 obuf += linesize;
2212 next += linesize;
2213 }
2214 /* last row is odd, just copy previous row */
2215 memcpy(obuf, prev, linesize);
2216 }
2217
2218 cam->decompressed_frame.count = obuf-cam->decompressed_frame.data;
2219
2220 return cam->decompressed_frame.count;
2221}
2222
2223/* InitStreamCap wrapper to select correct start line */
2224static inline int init_stream_cap(struct cam_data *cam)
2225{
2226 return do_command(cam, CPIA_COMMAND_InitStreamCap,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002227 0, cam->params.streamStartLine, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002228}
2229
2230
2231/* find_over_exposure
2232 * Finds a suitable value of OverExposure for use with SetFlickerCtrl
2233 * Some calculation is required because this value changes with the brightness
2234 * set with SetColourParameters
2235 *
2236 * Parameters: Brightness - last brightness value set with SetColourParameters
2237 *
2238 * Returns: OverExposure value to use with SetFlickerCtrl
2239 */
2240#define FLICKER_MAX_EXPOSURE 250
2241#define FLICKER_ALLOWABLE_OVER_EXPOSURE 146
2242#define FLICKER_BRIGHTNESS_CONSTANT 59
2243static int find_over_exposure(int brightness)
2244{
2245 int MaxAllowableOverExposure, OverExposure;
2246
2247 MaxAllowableOverExposure = FLICKER_MAX_EXPOSURE - brightness -
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002248 FLICKER_BRIGHTNESS_CONSTANT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002249
2250 if (MaxAllowableOverExposure < FLICKER_ALLOWABLE_OVER_EXPOSURE) {
2251 OverExposure = MaxAllowableOverExposure;
2252 } else {
2253 OverExposure = FLICKER_ALLOWABLE_OVER_EXPOSURE;
2254 }
2255
2256 return OverExposure;
2257}
2258#undef FLICKER_MAX_EXPOSURE
2259#undef FLICKER_ALLOWABLE_OVER_EXPOSURE
2260#undef FLICKER_BRIGHTNESS_CONSTANT
2261
2262/* update various camera modes and settings */
2263static void dispatch_commands(struct cam_data *cam)
2264{
Ingo Molnar3593cab2006-02-07 06:49:14 -02002265 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002266 if (cam->cmd_queue==COMMAND_NONE) {
Ingo Molnar3593cab2006-02-07 06:49:14 -02002267 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002268 return;
2269 }
2270 DEB_BYTE(cam->cmd_queue);
2271 DEB_BYTE(cam->cmd_queue>>8);
2272 if (cam->cmd_queue & COMMAND_SETFORMAT) {
2273 do_command(cam, CPIA_COMMAND_SetFormat,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002274 cam->params.format.videoSize,
2275 cam->params.format.subSample,
2276 cam->params.format.yuvOrder, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002277 do_command(cam, CPIA_COMMAND_SetROI,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002278 cam->params.roi.colStart, cam->params.roi.colEnd,
2279 cam->params.roi.rowStart, cam->params.roi.rowEnd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002280 cam->first_frame = 1;
2281 }
2282
2283 if (cam->cmd_queue & COMMAND_SETCOLOURPARAMS)
2284 do_command(cam, CPIA_COMMAND_SetColourParams,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002285 cam->params.colourParams.brightness,
2286 cam->params.colourParams.contrast,
2287 cam->params.colourParams.saturation, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002288
2289 if (cam->cmd_queue & COMMAND_SETAPCOR)
2290 do_command(cam, CPIA_COMMAND_SetApcor,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002291 cam->params.apcor.gain1,
2292 cam->params.apcor.gain2,
2293 cam->params.apcor.gain4,
2294 cam->params.apcor.gain8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002295
2296 if (cam->cmd_queue & COMMAND_SETVLOFFSET)
2297 do_command(cam, CPIA_COMMAND_SetVLOffset,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002298 cam->params.vlOffset.gain1,
2299 cam->params.vlOffset.gain2,
2300 cam->params.vlOffset.gain4,
2301 cam->params.vlOffset.gain8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002302
2303 if (cam->cmd_queue & COMMAND_SETEXPOSURE) {
2304 do_command_extended(cam, CPIA_COMMAND_SetExposure,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002305 cam->params.exposure.gainMode,
2306 1,
2307 cam->params.exposure.compMode,
2308 cam->params.exposure.centreWeight,
2309 cam->params.exposure.gain,
2310 cam->params.exposure.fineExp,
2311 cam->params.exposure.coarseExpLo,
2312 cam->params.exposure.coarseExpHi,
2313 cam->params.exposure.redComp,
2314 cam->params.exposure.green1Comp,
2315 cam->params.exposure.green2Comp,
2316 cam->params.exposure.blueComp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002317 if(cam->params.exposure.expMode != 1) {
2318 do_command_extended(cam, CPIA_COMMAND_SetExposure,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002319 0,
2320 cam->params.exposure.expMode,
2321 0, 0,
2322 cam->params.exposure.gain,
2323 cam->params.exposure.fineExp,
2324 cam->params.exposure.coarseExpLo,
2325 cam->params.exposure.coarseExpHi,
2326 0, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002327 }
2328 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002329
Linus Torvalds1da177e2005-04-16 15:20:36 -07002330 if (cam->cmd_queue & COMMAND_SETCOLOURBALANCE) {
2331 if (cam->params.colourBalance.balanceMode == 1) {
2332 do_command(cam, CPIA_COMMAND_SetColourBalance,
2333 1,
2334 cam->params.colourBalance.redGain,
2335 cam->params.colourBalance.greenGain,
2336 cam->params.colourBalance.blueGain);
2337 do_command(cam, CPIA_COMMAND_SetColourBalance,
2338 3, 0, 0, 0);
2339 }
2340 if (cam->params.colourBalance.balanceMode == 2) {
2341 do_command(cam, CPIA_COMMAND_SetColourBalance,
2342 2, 0, 0, 0);
2343 }
2344 if (cam->params.colourBalance.balanceMode == 3) {
2345 do_command(cam, CPIA_COMMAND_SetColourBalance,
2346 3, 0, 0, 0);
2347 }
2348 }
2349
2350 if (cam->cmd_queue & COMMAND_SETCOMPRESSIONTARGET)
2351 do_command(cam, CPIA_COMMAND_SetCompressionTarget,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002352 cam->params.compressionTarget.frTargeting,
2353 cam->params.compressionTarget.targetFR,
2354 cam->params.compressionTarget.targetQ, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002355
2356 if (cam->cmd_queue & COMMAND_SETYUVTHRESH)
2357 do_command(cam, CPIA_COMMAND_SetYUVThresh,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002358 cam->params.yuvThreshold.yThreshold,
2359 cam->params.yuvThreshold.uvThreshold, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002360
2361 if (cam->cmd_queue & COMMAND_SETCOMPRESSIONPARAMS)
2362 do_command_extended(cam, CPIA_COMMAND_SetCompressionParams,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002363 0, 0, 0, 0,
2364 cam->params.compressionParams.hysteresis,
2365 cam->params.compressionParams.threshMax,
2366 cam->params.compressionParams.smallStep,
2367 cam->params.compressionParams.largeStep,
2368 cam->params.compressionParams.decimationHysteresis,
2369 cam->params.compressionParams.frDiffStepThresh,
2370 cam->params.compressionParams.qDiffStepThresh,
2371 cam->params.compressionParams.decimationThreshMod);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002372
2373 if (cam->cmd_queue & COMMAND_SETCOMPRESSION)
2374 do_command(cam, CPIA_COMMAND_SetCompression,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002375 cam->params.compression.mode,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002376 cam->params.compression.decimation, 0, 0);
2377
2378 if (cam->cmd_queue & COMMAND_SETSENSORFPS)
2379 do_command(cam, CPIA_COMMAND_SetSensorFPS,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002380 cam->params.sensorFps.divisor,
2381 cam->params.sensorFps.baserate, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002382
2383 if (cam->cmd_queue & COMMAND_SETFLICKERCTRL)
2384 do_command(cam, CPIA_COMMAND_SetFlickerCtrl,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002385 cam->params.flickerControl.flickerMode,
2386 cam->params.flickerControl.coarseJump,
2387 abs(cam->params.flickerControl.allowableOverExposure),
2388 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002389
2390 if (cam->cmd_queue & COMMAND_SETECPTIMING)
2391 do_command(cam, CPIA_COMMAND_SetECPTiming,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002392 cam->params.ecpTiming, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002393
2394 if (cam->cmd_queue & COMMAND_PAUSE)
2395 do_command(cam, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0);
2396
2397 if (cam->cmd_queue & COMMAND_RESUME)
2398 init_stream_cap(cam);
2399
2400 if (cam->cmd_queue & COMMAND_SETLIGHTS && cam->params.qx3.qx3_detected)
2401 {
2402 int p1 = (cam->params.qx3.bottomlight == 0) << 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002403 int p2 = (cam->params.qx3.toplight == 0) << 3;
2404 do_command(cam, CPIA_COMMAND_WriteVCReg, 0x90, 0x8F, 0x50, 0);
2405 do_command(cam, CPIA_COMMAND_WriteMCPort, 2, 0, (p1|p2|0xE0), 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002406 }
2407
2408 cam->cmd_queue = COMMAND_NONE;
Ingo Molnar3593cab2006-02-07 06:49:14 -02002409 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002410 return;
2411}
2412
2413
2414
2415static void set_flicker(struct cam_params *params, volatile u32 *command_flags,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002416 int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002417{
2418 /* Everything in here is from the Windows driver */
2419#define FIRMWARE_VERSION(x,y) (params->version.firmwareVersion == (x) && \
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002420 params->version.firmwareRevision == (y))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002421/* define for compgain calculation */
2422#if 0
2423#define COMPGAIN(base, curexp, newexp) \
2424 (u8) ((((float) base - 128.0) * ((float) curexp / (float) newexp)) + 128.5)
2425#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
2426 (u16)((float)curexp * (float)(u8)(curcomp + 128) / (float)(u8)(basecomp - 128))
2427#else
2428 /* equivalent functions without floating point math */
2429#define COMPGAIN(base, curexp, newexp) \
2430 (u8)(128 + (((u32)(2*(base-128)*curexp + newexp)) / (2* newexp)) )
2431#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
2432 (u16)(((u32)(curexp * (u8)(curcomp + 128)) / (u8)(basecomp - 128)))
2433#endif
2434
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002435
Linus Torvalds1da177e2005-04-16 15:20:36 -07002436 int currentexp = params->exposure.coarseExpLo +
2437 params->exposure.coarseExpHi*256;
2438 int startexp;
2439 if (on) {
2440 int cj = params->flickerControl.coarseJump;
2441 params->flickerControl.flickerMode = 1;
2442 params->flickerControl.disabled = 0;
2443 if(params->exposure.expMode != 2)
2444 *command_flags |= COMMAND_SETEXPOSURE;
2445 params->exposure.expMode = 2;
2446 currentexp = currentexp << params->exposure.gain;
2447 params->exposure.gain = 0;
2448 /* round down current exposure to nearest value */
2449 startexp = (currentexp + ROUND_UP_EXP_FOR_FLICKER) / cj;
2450 if(startexp < 1)
2451 startexp = 1;
2452 startexp = (startexp * cj) - 1;
2453 if(FIRMWARE_VERSION(1,2))
2454 while(startexp > MAX_EXP_102)
2455 startexp -= cj;
2456 else
2457 while(startexp > MAX_EXP)
2458 startexp -= cj;
2459 params->exposure.coarseExpLo = startexp & 0xff;
2460 params->exposure.coarseExpHi = startexp >> 8;
2461 if (currentexp > startexp) {
2462 if (currentexp > (2 * startexp))
2463 currentexp = 2 * startexp;
2464 params->exposure.redComp = COMPGAIN (COMP_RED, currentexp, startexp);
2465 params->exposure.green1Comp = COMPGAIN (COMP_GREEN1, currentexp, startexp);
2466 params->exposure.green2Comp = COMPGAIN (COMP_GREEN2, currentexp, startexp);
2467 params->exposure.blueComp = COMPGAIN (COMP_BLUE, currentexp, startexp);
2468 } else {
2469 params->exposure.redComp = COMP_RED;
2470 params->exposure.green1Comp = COMP_GREEN1;
2471 params->exposure.green2Comp = COMP_GREEN2;
2472 params->exposure.blueComp = COMP_BLUE;
2473 }
2474 if(FIRMWARE_VERSION(1,2))
2475 params->exposure.compMode = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002476 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07002477 params->exposure.compMode = 1;
2478
2479 params->apcor.gain1 = 0x18;
2480 params->apcor.gain2 = 0x18;
2481 params->apcor.gain4 = 0x16;
2482 params->apcor.gain8 = 0x14;
2483 *command_flags |= COMMAND_SETAPCOR;
2484 } else {
2485 params->flickerControl.flickerMode = 0;
2486 params->flickerControl.disabled = 1;
2487 /* Coarse = average of equivalent coarse for each comp channel */
2488 startexp = EXP_FROM_COMP(COMP_RED, params->exposure.redComp, currentexp);
2489 startexp += EXP_FROM_COMP(COMP_GREEN1, params->exposure.green1Comp, currentexp);
2490 startexp += EXP_FROM_COMP(COMP_GREEN2, params->exposure.green2Comp, currentexp);
2491 startexp += EXP_FROM_COMP(COMP_BLUE, params->exposure.blueComp, currentexp);
2492 startexp = startexp >> 2;
2493 while(startexp > MAX_EXP &&
2494 params->exposure.gain < params->exposure.gainMode-1) {
2495 startexp = startexp >> 1;
2496 ++params->exposure.gain;
2497 }
2498 if(FIRMWARE_VERSION(1,2) && startexp > MAX_EXP_102)
2499 startexp = MAX_EXP_102;
2500 if(startexp > MAX_EXP)
2501 startexp = MAX_EXP;
2502 params->exposure.coarseExpLo = startexp&0xff;
2503 params->exposure.coarseExpHi = startexp >> 8;
2504 params->exposure.redComp = COMP_RED;
2505 params->exposure.green1Comp = COMP_GREEN1;
2506 params->exposure.green2Comp = COMP_GREEN2;
2507 params->exposure.blueComp = COMP_BLUE;
2508 params->exposure.compMode = 1;
2509 *command_flags |= COMMAND_SETEXPOSURE;
2510 params->apcor.gain1 = 0x18;
2511 params->apcor.gain2 = 0x16;
2512 params->apcor.gain4 = 0x24;
2513 params->apcor.gain8 = 0x34;
2514 *command_flags |= COMMAND_SETAPCOR;
2515 }
2516 params->vlOffset.gain1 = 20;
2517 params->vlOffset.gain2 = 24;
2518 params->vlOffset.gain4 = 26;
2519 params->vlOffset.gain8 = 26;
2520 *command_flags |= COMMAND_SETVLOFFSET;
2521#undef FIRMWARE_VERSION
2522#undef EXP_FROM_COMP
2523#undef COMPGAIN
2524}
2525
2526#define FIRMWARE_VERSION(x,y) (cam->params.version.firmwareVersion == (x) && \
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002527 cam->params.version.firmwareRevision == (y))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002528/* monitor the exposure and adjust the sensor frame rate if needed */
2529static void monitor_exposure(struct cam_data *cam)
2530{
2531 u8 exp_acc, bcomp, gain, coarseL, cmd[8], data[8];
2532 int retval, light_exp, dark_exp, very_dark_exp;
2533 int old_exposure, new_exposure, framerate;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002534
Linus Torvalds1da177e2005-04-16 15:20:36 -07002535 /* get necessary stats and register settings from camera */
2536 /* do_command can't handle this, so do it ourselves */
2537 cmd[0] = CPIA_COMMAND_ReadVPRegs>>8;
2538 cmd[1] = CPIA_COMMAND_ReadVPRegs&0xff;
2539 cmd[2] = 30;
2540 cmd[3] = 4;
2541 cmd[4] = 9;
2542 cmd[5] = 8;
2543 cmd[6] = 8;
2544 cmd[7] = 0;
2545 retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
2546 if (retval) {
2547 LOG("ReadVPRegs(30,4,9,8) - failed, retval=%d\n",
2548 retval);
2549 return;
2550 }
2551 exp_acc = data[0];
2552 bcomp = data[1];
2553 gain = data[2];
2554 coarseL = data[3];
2555
Ingo Molnar3593cab2006-02-07 06:49:14 -02002556 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002557 light_exp = cam->params.colourParams.brightness +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002558 TC - 50 + EXP_ACC_LIGHT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002559 if(light_exp > 255)
2560 light_exp = 255;
2561 dark_exp = cam->params.colourParams.brightness +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002562 TC - 50 - EXP_ACC_DARK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002563 if(dark_exp < 0)
2564 dark_exp = 0;
2565 very_dark_exp = dark_exp/2;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002566
Linus Torvalds1da177e2005-04-16 15:20:36 -07002567 old_exposure = cam->params.exposure.coarseExpHi * 256 +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002568 cam->params.exposure.coarseExpLo;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002569
2570 if(!cam->params.flickerControl.disabled) {
2571 /* Flicker control on */
2572 int max_comp = FIRMWARE_VERSION(1,2) ? MAX_COMP : HIGH_COMP_102;
2573 bcomp += 128; /* decode */
2574 if(bcomp >= max_comp && exp_acc < dark_exp) {
2575 /* dark */
2576 if(exp_acc < very_dark_exp) {
2577 /* very dark */
2578 if(cam->exposure_status == EXPOSURE_VERY_DARK)
2579 ++cam->exposure_count;
2580 else {
2581 cam->exposure_status = EXPOSURE_VERY_DARK;
2582 cam->exposure_count = 1;
2583 }
2584 } else {
2585 /* just dark */
2586 if(cam->exposure_status == EXPOSURE_DARK)
2587 ++cam->exposure_count;
2588 else {
2589 cam->exposure_status = EXPOSURE_DARK;
2590 cam->exposure_count = 1;
2591 }
2592 }
2593 } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) {
2594 /* light */
2595 if(old_exposure <= VERY_LOW_EXP) {
2596 /* very light */
2597 if(cam->exposure_status == EXPOSURE_VERY_LIGHT)
2598 ++cam->exposure_count;
2599 else {
2600 cam->exposure_status = EXPOSURE_VERY_LIGHT;
2601 cam->exposure_count = 1;
2602 }
2603 } else {
2604 /* just light */
2605 if(cam->exposure_status == EXPOSURE_LIGHT)
2606 ++cam->exposure_count;
2607 else {
2608 cam->exposure_status = EXPOSURE_LIGHT;
2609 cam->exposure_count = 1;
2610 }
2611 }
2612 } else {
2613 /* not dark or light */
2614 cam->exposure_status = EXPOSURE_NORMAL;
2615 }
2616 } else {
2617 /* Flicker control off */
2618 if(old_exposure >= MAX_EXP && exp_acc < dark_exp) {
2619 /* dark */
2620 if(exp_acc < very_dark_exp) {
2621 /* very dark */
2622 if(cam->exposure_status == EXPOSURE_VERY_DARK)
2623 ++cam->exposure_count;
2624 else {
2625 cam->exposure_status = EXPOSURE_VERY_DARK;
2626 cam->exposure_count = 1;
2627 }
2628 } else {
2629 /* just dark */
2630 if(cam->exposure_status == EXPOSURE_DARK)
2631 ++cam->exposure_count;
2632 else {
2633 cam->exposure_status = EXPOSURE_DARK;
2634 cam->exposure_count = 1;
2635 }
2636 }
2637 } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) {
2638 /* light */
2639 if(old_exposure <= VERY_LOW_EXP) {
2640 /* very light */
2641 if(cam->exposure_status == EXPOSURE_VERY_LIGHT)
2642 ++cam->exposure_count;
2643 else {
2644 cam->exposure_status = EXPOSURE_VERY_LIGHT;
2645 cam->exposure_count = 1;
2646 }
2647 } else {
2648 /* just light */
2649 if(cam->exposure_status == EXPOSURE_LIGHT)
2650 ++cam->exposure_count;
2651 else {
2652 cam->exposure_status = EXPOSURE_LIGHT;
2653 cam->exposure_count = 1;
2654 }
2655 }
2656 } else {
2657 /* not dark or light */
2658 cam->exposure_status = EXPOSURE_NORMAL;
2659 }
2660 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002661
Linus Torvalds1da177e2005-04-16 15:20:36 -07002662 framerate = cam->fps;
2663 if(framerate > 30 || framerate < 1)
2664 framerate = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002665
Linus Torvalds1da177e2005-04-16 15:20:36 -07002666 if(!cam->params.flickerControl.disabled) {
2667 /* Flicker control on */
2668 if((cam->exposure_status == EXPOSURE_VERY_DARK ||
2669 cam->exposure_status == EXPOSURE_DARK) &&
2670 cam->exposure_count >= DARK_TIME*framerate &&
2671 cam->params.sensorFps.divisor < 3) {
2672
2673 /* dark for too long */
2674 ++cam->params.sensorFps.divisor;
2675 cam->cmd_queue |= COMMAND_SETSENSORFPS;
2676
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002677 cam->params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07002678 flicker_jumps[cam->mainsFreq]
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002679 [cam->params.sensorFps.baserate]
2680 [cam->params.sensorFps.divisor];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002681 cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
2682
2683 new_exposure = cam->params.flickerControl.coarseJump-1;
2684 while(new_exposure < old_exposure/2)
2685 new_exposure += cam->params.flickerControl.coarseJump;
2686 cam->params.exposure.coarseExpLo = new_exposure & 0xff;
2687 cam->params.exposure.coarseExpHi = new_exposure >> 8;
2688 cam->cmd_queue |= COMMAND_SETEXPOSURE;
2689 cam->exposure_status = EXPOSURE_NORMAL;
2690 LOG("Automatically decreasing sensor_fps\n");
2691
2692 } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT ||
2693 cam->exposure_status == EXPOSURE_LIGHT) &&
2694 cam->exposure_count >= LIGHT_TIME*framerate &&
2695 cam->params.sensorFps.divisor > 0) {
2696
2697 /* light for too long */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002698 int max_exp = FIRMWARE_VERSION(1,2) ? MAX_EXP_102 : MAX_EXP ;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002699
2700 --cam->params.sensorFps.divisor;
2701 cam->cmd_queue |= COMMAND_SETSENSORFPS;
2702
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002703 cam->params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07002704 flicker_jumps[cam->mainsFreq]
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002705 [cam->params.sensorFps.baserate]
2706 [cam->params.sensorFps.divisor];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002707 cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
2708
2709 new_exposure = cam->params.flickerControl.coarseJump-1;
2710 while(new_exposure < 2*old_exposure &&
2711 new_exposure+
2712 cam->params.flickerControl.coarseJump < max_exp)
2713 new_exposure += cam->params.flickerControl.coarseJump;
2714 cam->params.exposure.coarseExpLo = new_exposure & 0xff;
2715 cam->params.exposure.coarseExpHi = new_exposure >> 8;
2716 cam->cmd_queue |= COMMAND_SETEXPOSURE;
2717 cam->exposure_status = EXPOSURE_NORMAL;
2718 LOG("Automatically increasing sensor_fps\n");
2719 }
2720 } else {
2721 /* Flicker control off */
2722 if((cam->exposure_status == EXPOSURE_VERY_DARK ||
2723 cam->exposure_status == EXPOSURE_DARK) &&
2724 cam->exposure_count >= DARK_TIME*framerate &&
2725 cam->params.sensorFps.divisor < 3) {
2726
2727 /* dark for too long */
2728 ++cam->params.sensorFps.divisor;
2729 cam->cmd_queue |= COMMAND_SETSENSORFPS;
2730
2731 if(cam->params.exposure.gain > 0) {
2732 --cam->params.exposure.gain;
2733 cam->cmd_queue |= COMMAND_SETEXPOSURE;
2734 }
2735 cam->exposure_status = EXPOSURE_NORMAL;
2736 LOG("Automatically decreasing sensor_fps\n");
2737
2738 } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT ||
2739 cam->exposure_status == EXPOSURE_LIGHT) &&
2740 cam->exposure_count >= LIGHT_TIME*framerate &&
2741 cam->params.sensorFps.divisor > 0) {
2742
2743 /* light for too long */
2744 --cam->params.sensorFps.divisor;
2745 cam->cmd_queue |= COMMAND_SETSENSORFPS;
2746
2747 if(cam->params.exposure.gain <
2748 cam->params.exposure.gainMode-1) {
2749 ++cam->params.exposure.gain;
2750 cam->cmd_queue |= COMMAND_SETEXPOSURE;
2751 }
2752 cam->exposure_status = EXPOSURE_NORMAL;
2753 LOG("Automatically increasing sensor_fps\n");
2754 }
2755 }
Ingo Molnar3593cab2006-02-07 06:49:14 -02002756 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002757}
2758
2759/*-----------------------------------------------------------------*/
2760/* if flicker is switched off, this function switches it back on.It checks,
2761 however, that conditions are suitable before restarting it.
2762 This should only be called for firmware version 1.2.
2763
2764 It also adjust the colour balance when an exposure step is detected - as
2765 long as flicker is running
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002766*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07002767static void restart_flicker(struct cam_data *cam)
2768{
2769 int cam_exposure, old_exp;
2770 if(!FIRMWARE_VERSION(1,2))
2771 return;
Ingo Molnar3593cab2006-02-07 06:49:14 -02002772 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002773 if(cam->params.flickerControl.flickerMode == 0 ||
2774 cam->raw_image[39] == 0) {
Ingo Molnar3593cab2006-02-07 06:49:14 -02002775 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002776 return;
2777 }
2778 cam_exposure = cam->raw_image[39]*2;
2779 old_exp = cam->params.exposure.coarseExpLo +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002780 cam->params.exposure.coarseExpHi*256;
2781 /*
2782 see how far away camera exposure is from a valid
2783 flicker exposure value
2784 */
2785 cam_exposure %= cam->params.flickerControl.coarseJump;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002786 if(!cam->params.flickerControl.disabled &&
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002787 cam_exposure <= cam->params.flickerControl.coarseJump - 3) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002788 /* Flicker control auto-disabled */
2789 cam->params.flickerControl.disabled = 1;
2790 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002791
Linus Torvalds1da177e2005-04-16 15:20:36 -07002792 if(cam->params.flickerControl.disabled &&
2793 cam->params.flickerControl.flickerMode &&
2794 old_exp > cam->params.flickerControl.coarseJump +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002795 ROUND_UP_EXP_FOR_FLICKER) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002796 /* exposure is now high enough to switch
2797 flicker control back on */
2798 set_flicker(&cam->params, &cam->cmd_queue, 1);
2799 if((cam->cmd_queue & COMMAND_SETEXPOSURE) &&
2800 cam->params.exposure.expMode == 2)
2801 cam->exposure_status = EXPOSURE_NORMAL;
2802
2803 }
Ingo Molnar3593cab2006-02-07 06:49:14 -02002804 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002805}
2806#undef FIRMWARE_VERSION
2807
2808static int clear_stall(struct cam_data *cam)
2809{
2810 /* FIXME: Does this actually work? */
2811 LOG("Clearing stall\n");
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002812
Linus Torvalds1da177e2005-04-16 15:20:36 -07002813 cam->ops->streamRead(cam->lowlevel_data, cam->raw_image, 0);
2814 do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0);
2815 return cam->params.status.streamState != STREAM_PAUSED;
2816}
2817
2818/* kernel thread function to read image from camera */
2819static int fetch_frame(void *data)
2820{
2821 int image_size, retry;
2822 struct cam_data *cam = (struct cam_data *)data;
2823 unsigned long oldjif, rate, diff;
2824
2825 /* Allow up to two bad images in a row to be read and
2826 * ignored before an error is reported */
2827 for (retry = 0; retry < 3; ++retry) {
2828 if (retry)
2829 DBG("retry=%d\n", retry);
2830
2831 if (!cam->ops)
2832 continue;
2833
2834 /* load first frame always uncompressed */
2835 if (cam->first_frame &&
2836 cam->params.compression.mode != CPIA_COMPRESSION_NONE) {
2837 do_command(cam, CPIA_COMMAND_SetCompression,
2838 CPIA_COMPRESSION_NONE,
2839 NO_DECIMATION, 0, 0);
2840 /* Trial & error - Discarding a frame prevents the
2841 first frame from having an error in the data. */
2842 do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0);
2843 }
2844
2845 /* init camera upload */
2846 if (do_command(cam, CPIA_COMMAND_GrabFrame, 0,
2847 cam->params.streamStartLine, 0, 0))
2848 continue;
2849
2850 if (cam->ops->wait_for_stream_ready) {
2851 /* loop until image ready */
2852 int count = 0;
2853 do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0);
2854 while (cam->params.status.streamState != STREAM_READY) {
2855 if(++count > READY_TIMEOUT)
2856 break;
2857 if(cam->params.status.streamState ==
2858 STREAM_PAUSED) {
2859 /* Bad news */
2860 if(!clear_stall(cam))
2861 return -EIO;
2862 }
2863
2864 cond_resched();
2865
2866 /* sleep for 10 ms, hopefully ;) */
2867 msleep_interruptible(10);
2868 if (signal_pending(current))
2869 return -EINTR;
2870
2871 do_command(cam, CPIA_COMMAND_GetCameraStatus,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002872 0, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002873 }
2874 if(cam->params.status.streamState != STREAM_READY) {
2875 continue;
2876 }
2877 }
2878
2879 cond_resched();
2880
2881 /* grab image from camera */
2882 oldjif = jiffies;
2883 image_size = cam->ops->streamRead(cam->lowlevel_data,
2884 cam->raw_image, 0);
2885 if (image_size <= 0) {
2886 DBG("streamRead failed: %d\n", image_size);
2887 continue;
2888 }
2889
2890 rate = image_size * HZ / 1024;
2891 diff = jiffies-oldjif;
2892 cam->transfer_rate = diff==0 ? rate : rate/diff;
2893 /* diff==0 ? unlikely but possible */
2894
2895 /* Switch flicker control back on if it got turned off */
2896 restart_flicker(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002897
Linus Torvalds1da177e2005-04-16 15:20:36 -07002898 /* If AEC is enabled, monitor the exposure and
2899 adjust the sensor frame rate if needed */
2900 if(cam->params.exposure.expMode == 2)
2901 monitor_exposure(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002902
Linus Torvalds1da177e2005-04-16 15:20:36 -07002903 /* camera idle now so dispatch queued commands */
2904 dispatch_commands(cam);
2905
2906 /* Update our knowledge of the camera state */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002907 do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
2908 do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002909 do_command(cam, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0);
2910
2911 /* decompress and convert image to by copying it from
2912 * raw_image to decompressed_frame
2913 */
2914
2915 cond_resched();
2916
2917 cam->image_size = parse_picture(cam, image_size);
2918 if (cam->image_size <= 0) {
2919 DBG("parse_picture failed %d\n", cam->image_size);
2920 if(cam->params.compression.mode !=
2921 CPIA_COMPRESSION_NONE) {
2922 /* Compression may not work right if we
2923 had a bad frame, get the next one
2924 uncompressed. */
2925 cam->first_frame = 1;
2926 do_command(cam, CPIA_COMMAND_SetGrabMode,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002927 CPIA_GRAB_SINGLE, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002928 /* FIXME: Trial & error - need up to 70ms for
2929 the grab mode change to complete ? */
2930 msleep_interruptible(70);
2931 if (signal_pending(current))
2932 return -EINTR;
2933 }
2934 } else
2935 break;
2936 }
2937
2938 if (retry < 3) {
2939 /* FIXME: this only works for double buffering */
2940 if (cam->frame[cam->curframe].state == FRAME_READY) {
2941 memcpy(cam->frame[cam->curframe].data,
2942 cam->decompressed_frame.data,
2943 cam->decompressed_frame.count);
2944 cam->frame[cam->curframe].state = FRAME_DONE;
2945 } else
2946 cam->decompressed_frame.state = FRAME_DONE;
2947
2948 if (cam->first_frame) {
2949 cam->first_frame = 0;
2950 do_command(cam, CPIA_COMMAND_SetCompression,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002951 cam->params.compression.mode,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002952 cam->params.compression.decimation, 0, 0);
2953
2954 /* Switch from single-grab to continuous grab */
2955 do_command(cam, CPIA_COMMAND_SetGrabMode,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002956 CPIA_GRAB_CONTINUOUS, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002957 }
2958 return 0;
2959 }
2960 return -EIO;
2961}
2962
2963static int capture_frame(struct cam_data *cam, struct video_mmap *vm)
2964{
2965 if (!cam->frame_buf) {
2966 /* we do lazy allocation */
2967 int err;
2968 if ((err = allocate_frame_buf(cam)))
2969 return err;
2970 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002971
Linus Torvalds1da177e2005-04-16 15:20:36 -07002972 cam->curframe = vm->frame;
2973 cam->frame[cam->curframe].state = FRAME_READY;
2974 return fetch_frame(cam);
2975}
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002976
Linus Torvalds1da177e2005-04-16 15:20:36 -07002977static int goto_high_power(struct cam_data *cam)
2978{
2979 if (do_command(cam, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0))
2980 return -EIO;
2981 msleep_interruptible(40); /* windows driver does it too */
2982 if(signal_pending(current))
2983 return -EINTR;
2984 if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
2985 return -EIO;
2986 if (cam->params.status.systemState == HI_POWER_STATE) {
2987 DBG("camera now in HIGH power state\n");
2988 return 0;
2989 }
2990 printstatus(cam);
2991 return -EIO;
2992}
2993
2994static int goto_low_power(struct cam_data *cam)
2995{
2996 if (do_command(cam, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0))
2997 return -1;
2998 if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
2999 return -1;
3000 if (cam->params.status.systemState == LO_POWER_STATE) {
3001 DBG("camera now in LOW power state\n");
3002 return 0;
3003 }
3004 printstatus(cam);
3005 return -1;
3006}
3007
3008static void save_camera_state(struct cam_data *cam)
3009{
3010 if(!(cam->cmd_queue & COMMAND_SETCOLOURBALANCE))
3011 do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
3012 if(!(cam->cmd_queue & COMMAND_SETEXPOSURE))
3013 do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
3014
3015 DBG("%d/%d/%d/%d/%d/%d/%d/%d\n",
3016 cam->params.exposure.gain,
3017 cam->params.exposure.fineExp,
3018 cam->params.exposure.coarseExpLo,
3019 cam->params.exposure.coarseExpHi,
3020 cam->params.exposure.redComp,
3021 cam->params.exposure.green1Comp,
3022 cam->params.exposure.green2Comp,
3023 cam->params.exposure.blueComp);
3024 DBG("%d/%d/%d\n",
3025 cam->params.colourBalance.redGain,
3026 cam->params.colourBalance.greenGain,
3027 cam->params.colourBalance.blueGain);
3028}
3029
3030static int set_camera_state(struct cam_data *cam)
3031{
3032 cam->cmd_queue = COMMAND_SETCOMPRESSION |
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003033 COMMAND_SETCOMPRESSIONTARGET |
3034 COMMAND_SETCOLOURPARAMS |
3035 COMMAND_SETFORMAT |
3036 COMMAND_SETYUVTHRESH |
3037 COMMAND_SETECPTIMING |
3038 COMMAND_SETCOMPRESSIONPARAMS |
3039 COMMAND_SETEXPOSURE |
3040 COMMAND_SETCOLOURBALANCE |
3041 COMMAND_SETSENSORFPS |
3042 COMMAND_SETAPCOR |
3043 COMMAND_SETFLICKERCTRL |
3044 COMMAND_SETVLOFFSET;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003045
3046 do_command(cam, CPIA_COMMAND_SetGrabMode, CPIA_GRAB_SINGLE,0,0,0);
3047 dispatch_commands(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003048
Linus Torvalds1da177e2005-04-16 15:20:36 -07003049 /* Wait 6 frames for the sensor to get all settings and
3050 AEC/ACB to settle */
3051 msleep_interruptible(6*(cam->params.sensorFps.baserate ? 33 : 40) *
3052 (1 << cam->params.sensorFps.divisor) + 10);
3053
3054 if(signal_pending(current))
3055 return -EINTR;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003056
Linus Torvalds1da177e2005-04-16 15:20:36 -07003057 save_camera_state(cam);
3058
3059 return 0;
3060}
3061
3062static void get_version_information(struct cam_data *cam)
3063{
3064 /* GetCPIAVersion */
3065 do_command(cam, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0);
3066
3067 /* GetPnPID */
3068 do_command(cam, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0);
3069}
3070
3071/* initialize camera */
3072static int reset_camera(struct cam_data *cam)
3073{
3074 int err;
3075 /* Start the camera in low power mode */
3076 if (goto_low_power(cam)) {
3077 if (cam->params.status.systemState != WARM_BOOT_STATE)
3078 return -ENODEV;
3079
3080 /* FIXME: this is just dirty trial and error */
3081 err = goto_high_power(cam);
3082 if(err)
3083 return err;
3084 do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0);
3085 if (goto_low_power(cam))
3086 return -ENODEV;
3087 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003088
Linus Torvalds1da177e2005-04-16 15:20:36 -07003089 /* procedure described in developer's guide p3-28 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003090
Linus Torvalds1da177e2005-04-16 15:20:36 -07003091 /* Check the firmware version. */
3092 cam->params.version.firmwareVersion = 0;
3093 get_version_information(cam);
3094 if (cam->params.version.firmwareVersion != 1)
3095 return -ENODEV;
3096
3097 /* A bug in firmware 1-02 limits gainMode to 2 */
3098 if(cam->params.version.firmwareRevision <= 2 &&
3099 cam->params.exposure.gainMode > 2) {
3100 cam->params.exposure.gainMode = 2;
3101 }
3102
3103 /* set QX3 detected flag */
3104 cam->params.qx3.qx3_detected = (cam->params.pnpID.vendor == 0x0813 &&
3105 cam->params.pnpID.product == 0x0001);
3106
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003107 /* The fatal error checking should be done after
Linus Torvalds1da177e2005-04-16 15:20:36 -07003108 * the camera powers up (developer's guide p 3-38) */
3109
3110 /* Set streamState before transition to high power to avoid bug
3111 * in firmware 1-02 */
3112 do_command(cam, CPIA_COMMAND_ModifyCameraStatus, STREAMSTATE, 0,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003113 STREAM_NOT_READY, 0);
3114
Linus Torvalds1da177e2005-04-16 15:20:36 -07003115 /* GotoHiPower */
3116 err = goto_high_power(cam);
3117 if (err)
3118 return err;
3119
3120 /* Check the camera status */
3121 if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
3122 return -EIO;
3123
3124 if (cam->params.status.fatalError) {
3125 DBG("fatal_error: %#04x\n",
3126 cam->params.status.fatalError);
3127 DBG("vp_status: %#04x\n",
3128 cam->params.status.vpStatus);
3129 if (cam->params.status.fatalError & ~(COM_FLAG|CPIA_FLAG)) {
3130 /* Fatal error in camera */
3131 return -EIO;
3132 } else if (cam->params.status.fatalError & (COM_FLAG|CPIA_FLAG)) {
3133 /* Firmware 1-02 may do this for parallel port cameras,
3134 * just clear the flags (developer's guide p 3-38) */
3135 do_command(cam, CPIA_COMMAND_ModifyCameraStatus,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003136 FATALERROR, ~(COM_FLAG|CPIA_FLAG), 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003137 }
3138 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003139
Linus Torvalds1da177e2005-04-16 15:20:36 -07003140 /* Check the camera status again */
3141 if (cam->params.status.fatalError) {
3142 if (cam->params.status.fatalError)
3143 return -EIO;
3144 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003145
Linus Torvalds1da177e2005-04-16 15:20:36 -07003146 /* VPVersion can't be retrieved before the camera is in HiPower,
3147 * so get it here instead of in get_version_information. */
3148 do_command(cam, CPIA_COMMAND_GetVPVersion, 0, 0, 0, 0);
3149
3150 /* set camera to a known state */
3151 return set_camera_state(cam);
3152}
3153
3154static void put_cam(struct cpia_camera_ops* ops)
3155{
3156 if (ops->owner)
3157 module_put(ops->owner);
3158}
3159
3160/* ------------------------- V4L interface --------------------- */
3161static int cpia_open(struct inode *inode, struct file *file)
3162{
3163 struct video_device *dev = video_devdata(file);
3164 struct cam_data *cam = dev->priv;
3165 int err;
3166
3167 if (!cam) {
3168 DBG("Internal error, cam_data not found!\n");
3169 return -ENODEV;
3170 }
3171
3172 if (cam->open_count > 0) {
3173 DBG("Camera already open\n");
3174 return -EBUSY;
3175 }
3176
3177 if (!try_module_get(cam->ops->owner))
3178 return -ENODEV;
3179
Ingo Molnar3593cab2006-02-07 06:49:14 -02003180 mutex_lock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003181 err = -ENOMEM;
3182 if (!cam->raw_image) {
3183 cam->raw_image = rvmalloc(CPIA_MAX_IMAGE_SIZE);
3184 if (!cam->raw_image)
3185 goto oops;
3186 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003187
Linus Torvalds1da177e2005-04-16 15:20:36 -07003188 if (!cam->decompressed_frame.data) {
3189 cam->decompressed_frame.data = rvmalloc(CPIA_MAX_FRAME_SIZE);
3190 if (!cam->decompressed_frame.data)
3191 goto oops;
3192 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003193
Linus Torvalds1da177e2005-04-16 15:20:36 -07003194 /* open cpia */
3195 err = -ENODEV;
3196 if (cam->ops->open(cam->lowlevel_data))
3197 goto oops;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003198
Linus Torvalds1da177e2005-04-16 15:20:36 -07003199 /* reset the camera */
3200 if ((err = reset_camera(cam)) != 0) {
3201 cam->ops->close(cam->lowlevel_data);
3202 goto oops;
3203 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003204
Linus Torvalds1da177e2005-04-16 15:20:36 -07003205 err = -EINTR;
3206 if(signal_pending(current))
3207 goto oops;
3208
3209 /* Set ownership of /proc/cpia/videoX to current user */
3210 if(cam->proc_entry)
3211 cam->proc_entry->uid = current->uid;
3212
3213 /* set mark for loading first frame uncompressed */
3214 cam->first_frame = 1;
3215
3216 /* init it to something */
3217 cam->mmap_kludge = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003218
Linus Torvalds1da177e2005-04-16 15:20:36 -07003219 ++cam->open_count;
3220 file->private_data = dev;
Ingo Molnar3593cab2006-02-07 06:49:14 -02003221 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003222 return 0;
3223
3224 oops:
3225 if (cam->decompressed_frame.data) {
3226 rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
3227 cam->decompressed_frame.data = NULL;
3228 }
3229 if (cam->raw_image) {
3230 rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
3231 cam->raw_image = NULL;
3232 }
Ingo Molnar3593cab2006-02-07 06:49:14 -02003233 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003234 put_cam(cam->ops);
3235 return err;
3236}
3237
3238static int cpia_close(struct inode *inode, struct file *file)
3239{
3240 struct video_device *dev = file->private_data;
3241 struct cam_data *cam = dev->priv;
3242
3243 if (cam->ops) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003244 /* Return ownership of /proc/cpia/videoX to root */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003245 if(cam->proc_entry)
3246 cam->proc_entry->uid = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003247
Linus Torvalds1da177e2005-04-16 15:20:36 -07003248 /* save camera state for later open (developers guide ch 3.5.3) */
3249 save_camera_state(cam);
3250
3251 /* GotoLoPower */
3252 goto_low_power(cam);
3253
3254 /* Update the camera status */
3255 do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
3256
3257 /* cleanup internal state stuff */
3258 free_frames(cam->frame);
3259
3260 /* close cpia */
3261 cam->ops->close(cam->lowlevel_data);
3262
3263 put_cam(cam->ops);
3264 }
3265
3266 if (--cam->open_count == 0) {
3267 /* clean up capture-buffers */
3268 if (cam->raw_image) {
3269 rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
3270 cam->raw_image = NULL;
3271 }
3272
3273 if (cam->decompressed_frame.data) {
3274 rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
3275 cam->decompressed_frame.data = NULL;
3276 }
3277
3278 if (cam->frame_buf)
3279 free_frame_buf(cam);
3280
3281 if (!cam->ops)
3282 kfree(cam);
3283 }
3284 file->private_data = NULL;
3285
3286 return 0;
3287}
3288
3289static ssize_t cpia_read(struct file *file, char __user *buf,
3290 size_t count, loff_t *ppos)
3291{
3292 struct video_device *dev = file->private_data;
3293 struct cam_data *cam = dev->priv;
3294 int err;
3295
3296 /* make this _really_ smp and multithread-safe */
Ingo Molnar3593cab2006-02-07 06:49:14 -02003297 if (mutex_lock_interruptible(&cam->busy_lock))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003298 return -EINTR;
3299
3300 if (!buf) {
3301 DBG("buf NULL\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02003302 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003303 return -EINVAL;
3304 }
3305
3306 if (!count) {
3307 DBG("count 0\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02003308 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003309 return 0;
3310 }
3311
3312 if (!cam->ops) {
3313 DBG("ops NULL\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02003314 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003315 return -ENODEV;
3316 }
3317
3318 /* upload frame */
3319 cam->decompressed_frame.state = FRAME_READY;
3320 cam->mmap_kludge=0;
3321 if((err = fetch_frame(cam)) != 0) {
3322 DBG("ERROR from fetch_frame: %d\n", err);
Ingo Molnar3593cab2006-02-07 06:49:14 -02003323 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003324 return err;
3325 }
3326 cam->decompressed_frame.state = FRAME_UNUSED;
3327
3328 /* copy data to user space */
3329 if (cam->decompressed_frame.count > count) {
3330 DBG("count wrong: %d, %lu\n", cam->decompressed_frame.count,
3331 (unsigned long) count);
Ingo Molnar3593cab2006-02-07 06:49:14 -02003332 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003333 return -EFAULT;
3334 }
3335 if (copy_to_user(buf, cam->decompressed_frame.data,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003336 cam->decompressed_frame.count)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003337 DBG("copy_to_user failed\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02003338 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003339 return -EFAULT;
3340 }
3341
Ingo Molnar3593cab2006-02-07 06:49:14 -02003342 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003343 return cam->decompressed_frame.count;
3344}
3345
3346static int cpia_do_ioctl(struct inode *inode, struct file *file,
3347 unsigned int ioctlnr, void *arg)
3348{
3349 struct video_device *dev = file->private_data;
3350 struct cam_data *cam = dev->priv;
3351 int retval = 0;
3352
3353 if (!cam || !cam->ops)
3354 return -ENODEV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003355
Linus Torvalds1da177e2005-04-16 15:20:36 -07003356 /* make this _really_ smp-safe */
Ingo Molnar3593cab2006-02-07 06:49:14 -02003357 if (mutex_lock_interruptible(&cam->busy_lock))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003358 return -EINTR;
3359
3360 //DBG("cpia_ioctl: %u\n", ioctlnr);
3361
3362 switch (ioctlnr) {
Alexey Dobriyanbe787ac2006-03-07 22:20:23 -03003363 /* query capabilities */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003364 case VIDIOCGCAP:
3365 {
3366 struct video_capability *b = arg;
3367
3368 DBG("VIDIOCGCAP\n");
3369 strcpy(b->name, "CPiA Camera");
3370 b->type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE;
3371 b->channels = 1;
3372 b->audios = 0;
3373 b->maxwidth = 352; /* VIDEOSIZE_CIF */
3374 b->maxheight = 288;
3375 b->minwidth = 48; /* VIDEOSIZE_48_48 */
3376 b->minheight = 48;
3377 break;
3378 }
3379
3380 /* get/set video source - we are a camera and nothing else */
3381 case VIDIOCGCHAN:
3382 {
3383 struct video_channel *v = arg;
3384
3385 DBG("VIDIOCGCHAN\n");
3386 if (v->channel != 0) {
3387 retval = -EINVAL;
3388 break;
3389 }
3390
3391 v->channel = 0;
3392 strcpy(v->name, "Camera");
3393 v->tuners = 0;
3394 v->flags = 0;
3395 v->type = VIDEO_TYPE_CAMERA;
3396 v->norm = 0;
3397 break;
3398 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003399
Linus Torvalds1da177e2005-04-16 15:20:36 -07003400 case VIDIOCSCHAN:
3401 {
3402 struct video_channel *v = arg;
3403
3404 DBG("VIDIOCSCHAN\n");
3405 if (v->channel != 0)
3406 retval = -EINVAL;
3407 break;
3408 }
3409
3410 /* image properties */
3411 case VIDIOCGPICT:
3412 {
3413 struct video_picture *pic = arg;
3414 DBG("VIDIOCGPICT\n");
3415 *pic = cam->vp;
3416 break;
3417 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003418
Linus Torvalds1da177e2005-04-16 15:20:36 -07003419 case VIDIOCSPICT:
3420 {
3421 struct video_picture *vp = arg;
3422
3423 DBG("VIDIOCSPICT\n");
3424
3425 /* check validity */
3426 DBG("palette: %d\n", vp->palette);
3427 DBG("depth: %d\n", vp->depth);
3428 if (!valid_mode(vp->palette, vp->depth)) {
3429 retval = -EINVAL;
3430 break;
3431 }
3432
Ingo Molnar3593cab2006-02-07 06:49:14 -02003433 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003434 /* brightness, colour, contrast need no check 0-65535 */
3435 cam->vp = *vp;
3436 /* update cam->params.colourParams */
3437 cam->params.colourParams.brightness = vp->brightness*100/65535;
3438 cam->params.colourParams.contrast = vp->contrast*100/65535;
3439 cam->params.colourParams.saturation = vp->colour*100/65535;
3440 /* contrast is in steps of 8, so round */
3441 cam->params.colourParams.contrast =
3442 ((cam->params.colourParams.contrast + 3) / 8) * 8;
3443 if (cam->params.version.firmwareVersion == 1 &&
3444 cam->params.version.firmwareRevision == 2 &&
3445 cam->params.colourParams.contrast > 80) {
3446 /* 1-02 firmware limits contrast to 80 */
3447 cam->params.colourParams.contrast = 80;
3448 }
3449
3450 /* Adjust flicker control if necessary */
3451 if(cam->params.flickerControl.allowableOverExposure < 0)
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003452 cam->params.flickerControl.allowableOverExposure =
Linus Torvalds1da177e2005-04-16 15:20:36 -07003453 -find_over_exposure(cam->params.colourParams.brightness);
3454 if(cam->params.flickerControl.flickerMode != 0)
3455 cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003456
Linus Torvalds1da177e2005-04-16 15:20:36 -07003457
3458 /* queue command to update camera */
3459 cam->cmd_queue |= COMMAND_SETCOLOURPARAMS;
Ingo Molnar3593cab2006-02-07 06:49:14 -02003460 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003461 DBG("VIDIOCSPICT: %d / %d // %d / %d / %d / %d\n",
3462 vp->depth, vp->palette, vp->brightness, vp->hue, vp->colour,
3463 vp->contrast);
3464 break;
3465 }
3466
3467 /* get/set capture window */
3468 case VIDIOCGWIN:
3469 {
3470 struct video_window *vw = arg;
3471 DBG("VIDIOCGWIN\n");
3472
3473 *vw = cam->vw;
3474 break;
3475 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003476
Linus Torvalds1da177e2005-04-16 15:20:36 -07003477 case VIDIOCSWIN:
3478 {
3479 /* copy_from_user, check validity, copy to internal structure */
3480 struct video_window *vw = arg;
3481 DBG("VIDIOCSWIN\n");
3482
3483 if (vw->clipcount != 0) { /* clipping not supported */
3484 retval = -EINVAL;
3485 break;
3486 }
3487 if (vw->clips != NULL) { /* clipping not supported */
3488 retval = -EINVAL;
3489 break;
3490 }
3491
3492 /* we set the video window to something smaller or equal to what
3493 * is requested by the user???
3494 */
Ingo Molnar3593cab2006-02-07 06:49:14 -02003495 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003496 if (vw->width != cam->vw.width || vw->height != cam->vw.height) {
3497 int video_size = match_videosize(vw->width, vw->height);
3498
3499 if (video_size < 0) {
3500 retval = -EINVAL;
Ingo Molnar3593cab2006-02-07 06:49:14 -02003501 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003502 break;
3503 }
3504 cam->video_size = video_size;
3505
3506 /* video size is changing, reset the subcapture area */
3507 memset(&cam->vc, 0, sizeof(cam->vc));
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003508
Linus Torvalds1da177e2005-04-16 15:20:36 -07003509 set_vw_size(cam);
3510 DBG("%d / %d\n", cam->vw.width, cam->vw.height);
3511 cam->cmd_queue |= COMMAND_SETFORMAT;
3512 }
3513
Ingo Molnar3593cab2006-02-07 06:49:14 -02003514 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003515
3516 /* setformat ignored by camera during streaming,
3517 * so stop/dispatch/start */
3518 if (cam->cmd_queue & COMMAND_SETFORMAT) {
3519 DBG("\n");
3520 dispatch_commands(cam);
3521 }
3522 DBG("%d/%d:%d\n", cam->video_size,
3523 cam->vw.width, cam->vw.height);
3524 break;
3525 }
3526
3527 /* mmap interface */
3528 case VIDIOCGMBUF:
3529 {
3530 struct video_mbuf *vm = arg;
3531 int i;
3532
3533 DBG("VIDIOCGMBUF\n");
3534 memset(vm, 0, sizeof(*vm));
3535 vm->size = CPIA_MAX_FRAME_SIZE*FRAME_NUM;
3536 vm->frames = FRAME_NUM;
3537 for (i = 0; i < FRAME_NUM; i++)
3538 vm->offsets[i] = CPIA_MAX_FRAME_SIZE * i;
3539 break;
3540 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003541
Linus Torvalds1da177e2005-04-16 15:20:36 -07003542 case VIDIOCMCAPTURE:
3543 {
3544 struct video_mmap *vm = arg;
3545 int video_size;
3546
3547 DBG("VIDIOCMCAPTURE: %d / %d / %dx%d\n", vm->format, vm->frame,
3548 vm->width, vm->height);
3549 if (vm->frame<0||vm->frame>=FRAME_NUM) {
3550 retval = -EINVAL;
3551 break;
3552 }
3553
3554 /* set video format */
3555 cam->vp.palette = vm->format;
3556 switch(vm->format) {
3557 case VIDEO_PALETTE_GREY:
3558 cam->vp.depth=8;
3559 break;
3560 case VIDEO_PALETTE_RGB555:
3561 case VIDEO_PALETTE_RGB565:
3562 case VIDEO_PALETTE_YUV422:
3563 case VIDEO_PALETTE_YUYV:
3564 case VIDEO_PALETTE_UYVY:
3565 cam->vp.depth = 16;
3566 break;
3567 case VIDEO_PALETTE_RGB24:
3568 cam->vp.depth = 24;
3569 break;
3570 case VIDEO_PALETTE_RGB32:
3571 cam->vp.depth = 32;
3572 break;
3573 default:
3574 retval = -EINVAL;
3575 break;
3576 }
3577 if (retval)
3578 break;
3579
3580 /* set video size */
3581 video_size = match_videosize(vm->width, vm->height);
3582 if (video_size < 0) {
3583 retval = -EINVAL;
3584 break;
3585 }
3586 if (video_size != cam->video_size) {
3587 cam->video_size = video_size;
3588
3589 /* video size is changing, reset the subcapture area */
3590 memset(&cam->vc, 0, sizeof(cam->vc));
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003591
Linus Torvalds1da177e2005-04-16 15:20:36 -07003592 set_vw_size(cam);
3593 cam->cmd_queue |= COMMAND_SETFORMAT;
3594 dispatch_commands(cam);
3595 }
3596 /* according to v4l-spec we must start streaming here */
3597 cam->mmap_kludge = 1;
3598 retval = capture_frame(cam, vm);
3599
3600 break;
3601 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003602
Linus Torvalds1da177e2005-04-16 15:20:36 -07003603 case VIDIOCSYNC:
3604 {
3605 int *frame = arg;
3606
3607 //DBG("VIDIOCSYNC: %d\n", *frame);
3608
3609 if (*frame<0 || *frame >= FRAME_NUM) {
3610 retval = -EINVAL;
3611 break;
3612 }
3613
3614 switch (cam->frame[*frame].state) {
3615 case FRAME_UNUSED:
3616 case FRAME_READY:
3617 case FRAME_GRABBING:
3618 DBG("sync to unused frame %d\n", *frame);
3619 retval = -EINVAL;
3620 break;
3621
3622 case FRAME_DONE:
3623 cam->frame[*frame].state = FRAME_UNUSED;
3624 //DBG("VIDIOCSYNC: %d synced\n", *frame);
3625 break;
3626 }
3627 if (retval == -EINTR) {
3628 /* FIXME - xawtv does not handle this nice */
3629 retval = 0;
3630 }
3631 break;
3632 }
3633
3634 case VIDIOCGCAPTURE:
3635 {
3636 struct video_capture *vc = arg;
3637
3638 DBG("VIDIOCGCAPTURE\n");
3639
3640 *vc = cam->vc;
3641
3642 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003643 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003644
3645 case VIDIOCSCAPTURE:
3646 {
3647 struct video_capture *vc = arg;
3648
3649 DBG("VIDIOCSCAPTURE\n");
3650
3651 if (vc->decimation != 0) { /* How should this be used? */
3652 retval = -EINVAL;
3653 break;
3654 }
3655 if (vc->flags != 0) { /* Even/odd grab not supported */
3656 retval = -EINVAL;
3657 break;
3658 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003659
Linus Torvalds1da177e2005-04-16 15:20:36 -07003660 /* Clip to the resolution we can set for the ROI
3661 (every 8 columns and 4 rows) */
3662 vc->x = vc->x & ~(__u32)7;
3663 vc->y = vc->y & ~(__u32)3;
3664 vc->width = vc->width & ~(__u32)7;
3665 vc->height = vc->height & ~(__u32)3;
3666
3667 if(vc->width == 0 || vc->height == 0 ||
3668 vc->x + vc->width > cam->vw.width ||
3669 vc->y + vc->height > cam->vw.height) {
3670 retval = -EINVAL;
3671 break;
3672 }
3673
3674 DBG("%d,%d/%dx%d\n", vc->x,vc->y,vc->width, vc->height);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003675
Ingo Molnar3593cab2006-02-07 06:49:14 -02003676 mutex_lock(&cam->param_lock);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003677
Linus Torvalds1da177e2005-04-16 15:20:36 -07003678 cam->vc.x = vc->x;
3679 cam->vc.y = vc->y;
3680 cam->vc.width = vc->width;
3681 cam->vc.height = vc->height;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003682
Linus Torvalds1da177e2005-04-16 15:20:36 -07003683 set_vw_size(cam);
3684 cam->cmd_queue |= COMMAND_SETFORMAT;
3685
Ingo Molnar3593cab2006-02-07 06:49:14 -02003686 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003687
3688 /* setformat ignored by camera during streaming,
3689 * so stop/dispatch/start */
3690 dispatch_commands(cam);
3691 break;
3692 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003693
Linus Torvalds1da177e2005-04-16 15:20:36 -07003694 case VIDIOCGUNIT:
3695 {
3696 struct video_unit *vu = arg;
3697
3698 DBG("VIDIOCGUNIT\n");
3699
3700 vu->video = cam->vdev.minor;
3701 vu->vbi = VIDEO_NO_UNIT;
3702 vu->radio = VIDEO_NO_UNIT;
3703 vu->audio = VIDEO_NO_UNIT;
3704 vu->teletext = VIDEO_NO_UNIT;
3705
3706 break;
3707 }
3708
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003709
Linus Torvalds1da177e2005-04-16 15:20:36 -07003710 /* pointless to implement overlay with this camera */
3711 case VIDIOCCAPTURE:
3712 case VIDIOCGFBUF:
3713 case VIDIOCSFBUF:
3714 case VIDIOCKEY:
3715 /* tuner interface - we have none */
3716 case VIDIOCGTUNER:
3717 case VIDIOCSTUNER:
3718 case VIDIOCGFREQ:
3719 case VIDIOCSFREQ:
3720 /* audio interface - we have none */
3721 case VIDIOCGAUDIO:
3722 case VIDIOCSAUDIO:
3723 retval = -EINVAL;
3724 break;
3725 default:
3726 retval = -ENOIOCTLCMD;
3727 break;
3728 }
3729
Ingo Molnar3593cab2006-02-07 06:49:14 -02003730 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003731 return retval;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003732}
Linus Torvalds1da177e2005-04-16 15:20:36 -07003733
3734static int cpia_ioctl(struct inode *inode, struct file *file,
3735 unsigned int cmd, unsigned long arg)
3736{
3737 return video_usercopy(inode, file, cmd, arg, cpia_do_ioctl);
3738}
3739
3740
3741/* FIXME */
3742static int cpia_mmap(struct file *file, struct vm_area_struct *vma)
3743{
3744 struct video_device *dev = file->private_data;
3745 unsigned long start = vma->vm_start;
3746 unsigned long size = vma->vm_end - vma->vm_start;
3747 unsigned long page, pos;
3748 struct cam_data *cam = dev->priv;
3749 int retval;
3750
3751 if (!cam || !cam->ops)
3752 return -ENODEV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003753
Linus Torvalds1da177e2005-04-16 15:20:36 -07003754 DBG("cpia_mmap: %ld\n", size);
3755
3756 if (size > FRAME_NUM*CPIA_MAX_FRAME_SIZE)
3757 return -EINVAL;
3758
3759 if (!cam || !cam->ops)
3760 return -ENODEV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003761
Linus Torvalds1da177e2005-04-16 15:20:36 -07003762 /* make this _really_ smp-safe */
Ingo Molnar3593cab2006-02-07 06:49:14 -02003763 if (mutex_lock_interruptible(&cam->busy_lock))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003764 return -EINTR;
3765
3766 if (!cam->frame_buf) { /* we do lazy allocation */
3767 if ((retval = allocate_frame_buf(cam))) {
Ingo Molnar3593cab2006-02-07 06:49:14 -02003768 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003769 return retval;
3770 }
3771 }
3772
3773 pos = (unsigned long)(cam->frame_buf);
3774 while (size > 0) {
3775 page = vmalloc_to_pfn((void *)pos);
3776 if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) {
Ingo Molnar3593cab2006-02-07 06:49:14 -02003777 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003778 return -EAGAIN;
3779 }
3780 start += PAGE_SIZE;
3781 pos += PAGE_SIZE;
3782 if (size > PAGE_SIZE)
3783 size -= PAGE_SIZE;
3784 else
3785 size = 0;
3786 }
3787
3788 DBG("cpia_mmap: %ld\n", size);
Ingo Molnar3593cab2006-02-07 06:49:14 -02003789 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003790
3791 return 0;
3792}
3793
Arjan van de Venfa027c22007-02-12 00:55:33 -08003794static const struct file_operations cpia_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003795 .owner = THIS_MODULE,
3796 .open = cpia_open,
3797 .release = cpia_close,
3798 .read = cpia_read,
3799 .mmap = cpia_mmap,
3800 .ioctl = cpia_ioctl,
Arnd Bergmann0d0fbf82006-01-09 15:24:57 -02003801 .compat_ioctl = v4l_compat_ioctl32,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003802 .llseek = no_llseek,
3803};
3804
3805static struct video_device cpia_template = {
3806 .owner = THIS_MODULE,
3807 .name = "CPiA Camera",
3808 .type = VID_TYPE_CAPTURE,
3809 .hardware = VID_HARDWARE_CPIA,
3810 .fops = &cpia_fops,
3811};
3812
3813/* initialise cam_data structure */
3814static void reset_camera_struct(struct cam_data *cam)
3815{
3816 /* The following parameter values are the defaults from
3817 * "Software Developer's Guide for CPiA Cameras". Any changes
3818 * to the defaults are noted in comments. */
3819 cam->params.colourParams.brightness = 50;
3820 cam->params.colourParams.contrast = 48;
3821 cam->params.colourParams.saturation = 50;
3822 cam->params.exposure.gainMode = 4;
3823 cam->params.exposure.expMode = 2; /* AEC */
3824 cam->params.exposure.compMode = 1;
3825 cam->params.exposure.centreWeight = 1;
3826 cam->params.exposure.gain = 0;
3827 cam->params.exposure.fineExp = 0;
3828 cam->params.exposure.coarseExpLo = 185;
3829 cam->params.exposure.coarseExpHi = 0;
3830 cam->params.exposure.redComp = COMP_RED;
3831 cam->params.exposure.green1Comp = COMP_GREEN1;
3832 cam->params.exposure.green2Comp = COMP_GREEN2;
3833 cam->params.exposure.blueComp = COMP_BLUE;
3834 cam->params.colourBalance.balanceMode = 2; /* ACB */
3835 cam->params.colourBalance.redGain = 32;
3836 cam->params.colourBalance.greenGain = 6;
3837 cam->params.colourBalance.blueGain = 92;
3838 cam->params.apcor.gain1 = 0x18;
3839 cam->params.apcor.gain2 = 0x16;
3840 cam->params.apcor.gain4 = 0x24;
3841 cam->params.apcor.gain8 = 0x34;
3842 cam->params.flickerControl.flickerMode = 0;
3843 cam->params.flickerControl.disabled = 1;
3844
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003845 cam->params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07003846 flicker_jumps[cam->mainsFreq]
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003847 [cam->params.sensorFps.baserate]
3848 [cam->params.sensorFps.divisor];
3849 cam->params.flickerControl.allowableOverExposure =
Linus Torvalds1da177e2005-04-16 15:20:36 -07003850 -find_over_exposure(cam->params.colourParams.brightness);
3851 cam->params.vlOffset.gain1 = 20;
3852 cam->params.vlOffset.gain2 = 24;
3853 cam->params.vlOffset.gain4 = 26;
3854 cam->params.vlOffset.gain8 = 26;
3855 cam->params.compressionParams.hysteresis = 3;
3856 cam->params.compressionParams.threshMax = 11;
3857 cam->params.compressionParams.smallStep = 1;
3858 cam->params.compressionParams.largeStep = 3;
3859 cam->params.compressionParams.decimationHysteresis = 2;
3860 cam->params.compressionParams.frDiffStepThresh = 5;
3861 cam->params.compressionParams.qDiffStepThresh = 3;
3862 cam->params.compressionParams.decimationThreshMod = 2;
3863 /* End of default values from Software Developer's Guide */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003864
Linus Torvalds1da177e2005-04-16 15:20:36 -07003865 cam->transfer_rate = 0;
3866 cam->exposure_status = EXPOSURE_NORMAL;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003867
Linus Torvalds1da177e2005-04-16 15:20:36 -07003868 /* Set Sensor FPS to 15fps. This seems better than 30fps
3869 * for indoor lighting. */
3870 cam->params.sensorFps.divisor = 1;
3871 cam->params.sensorFps.baserate = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003872
Linus Torvalds1da177e2005-04-16 15:20:36 -07003873 cam->params.yuvThreshold.yThreshold = 6; /* From windows driver */
3874 cam->params.yuvThreshold.uvThreshold = 6; /* From windows driver */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003875
Linus Torvalds1da177e2005-04-16 15:20:36 -07003876 cam->params.format.subSample = SUBSAMPLE_422;
3877 cam->params.format.yuvOrder = YUVORDER_YUYV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003878
Linus Torvalds1da177e2005-04-16 15:20:36 -07003879 cam->params.compression.mode = CPIA_COMPRESSION_AUTO;
3880 cam->params.compressionTarget.frTargeting =
3881 CPIA_COMPRESSION_TARGET_QUALITY;
3882 cam->params.compressionTarget.targetFR = 15; /* From windows driver */
3883 cam->params.compressionTarget.targetQ = 5; /* From windows driver */
3884
3885 cam->params.qx3.qx3_detected = 0;
3886 cam->params.qx3.toplight = 0;
3887 cam->params.qx3.bottomlight = 0;
3888 cam->params.qx3.button = 0;
3889 cam->params.qx3.cradled = 0;
3890
3891 cam->video_size = VIDEOSIZE_CIF;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003892
Linus Torvalds1da177e2005-04-16 15:20:36 -07003893 cam->vp.colour = 32768; /* 50% */
3894 cam->vp.hue = 32768; /* 50% */
3895 cam->vp.brightness = 32768; /* 50% */
3896 cam->vp.contrast = 32768; /* 50% */
3897 cam->vp.whiteness = 0; /* not used -> grayscale only */
3898 cam->vp.depth = 24; /* to be set by user */
3899 cam->vp.palette = VIDEO_PALETTE_RGB24; /* to be set by user */
3900
3901 cam->vc.x = 0;
3902 cam->vc.y = 0;
3903 cam->vc.width = 0;
3904 cam->vc.height = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003905
Linus Torvalds1da177e2005-04-16 15:20:36 -07003906 cam->vw.x = 0;
3907 cam->vw.y = 0;
3908 set_vw_size(cam);
3909 cam->vw.chromakey = 0;
3910 cam->vw.flags = 0;
3911 cam->vw.clipcount = 0;
3912 cam->vw.clips = NULL;
3913
3914 cam->cmd_queue = COMMAND_NONE;
3915 cam->first_frame = 1;
3916
3917 return;
3918}
3919
3920/* initialize cam_data structure */
3921static void init_camera_struct(struct cam_data *cam,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003922 struct cpia_camera_ops *ops )
Linus Torvalds1da177e2005-04-16 15:20:36 -07003923{
3924 int i;
3925
3926 /* Default everything to 0 */
3927 memset(cam, 0, sizeof(struct cam_data));
3928
3929 cam->ops = ops;
Ingo Molnar3593cab2006-02-07 06:49:14 -02003930 mutex_init(&cam->param_lock);
3931 mutex_init(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003932
3933 reset_camera_struct(cam);
3934
3935 cam->proc_entry = NULL;
3936
3937 memcpy(&cam->vdev, &cpia_template, sizeof(cpia_template));
3938 cam->vdev.priv = cam;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003939
Linus Torvalds1da177e2005-04-16 15:20:36 -07003940 cam->curframe = 0;
3941 for (i = 0; i < FRAME_NUM; i++) {
3942 cam->frame[i].width = 0;
3943 cam->frame[i].height = 0;
3944 cam->frame[i].state = FRAME_UNUSED;
3945 cam->frame[i].data = NULL;
3946 }
3947 cam->decompressed_frame.width = 0;
3948 cam->decompressed_frame.height = 0;
3949 cam->decompressed_frame.state = FRAME_UNUSED;
3950 cam->decompressed_frame.data = NULL;
3951}
3952
3953struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel)
3954{
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003955 struct cam_data *camera;
3956
Linus Torvalds1da177e2005-04-16 15:20:36 -07003957 if ((camera = kmalloc(sizeof(struct cam_data), GFP_KERNEL)) == NULL)
3958 return NULL;
3959
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003960
Linus Torvalds1da177e2005-04-16 15:20:36 -07003961 init_camera_struct( camera, ops );
3962 camera->lowlevel_data = lowlevel;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003963
Linus Torvalds1da177e2005-04-16 15:20:36 -07003964 /* register v4l device */
3965 if (video_register_device(&camera->vdev, VFL_TYPE_GRABBER, video_nr) == -1) {
3966 kfree(camera);
3967 printk(KERN_DEBUG "video_register_device failed\n");
3968 return NULL;
3969 }
3970
3971 /* get version information from camera: open/reset/close */
3972
3973 /* open cpia */
3974 if (camera->ops->open(camera->lowlevel_data))
3975 return camera;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003976
Linus Torvalds1da177e2005-04-16 15:20:36 -07003977 /* reset the camera */
3978 if (reset_camera(camera) != 0) {
3979 camera->ops->close(camera->lowlevel_data);
3980 return camera;
3981 }
3982
3983 /* close cpia */
3984 camera->ops->close(camera->lowlevel_data);
3985
3986#ifdef CONFIG_PROC_FS
3987 create_proc_cpia_cam(camera);
3988#endif
3989
3990 printk(KERN_INFO " CPiA Version: %d.%02d (%d.%d)\n",
3991 camera->params.version.firmwareVersion,
3992 camera->params.version.firmwareRevision,
3993 camera->params.version.vcVersion,
3994 camera->params.version.vcRevision);
3995 printk(KERN_INFO " CPiA PnP-ID: %04x:%04x:%04x\n",
3996 camera->params.pnpID.vendor,
3997 camera->params.pnpID.product,
3998 camera->params.pnpID.deviceRevision);
3999 printk(KERN_INFO " VP-Version: %d.%d %04x\n",
4000 camera->params.vpVersion.vpVersion,
4001 camera->params.vpVersion.vpRevision,
4002 camera->params.vpVersion.cameraHeadID);
4003
4004 return camera;
4005}
4006
4007void cpia_unregister_camera(struct cam_data *cam)
4008{
4009 DBG("unregistering video\n");
4010 video_unregister_device(&cam->vdev);
4011 if (cam->open_count) {
4012 put_cam(cam->ops);
4013 DBG("camera open -- setting ops to NULL\n");
4014 cam->ops = NULL;
4015 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03004016
Linus Torvalds1da177e2005-04-16 15:20:36 -07004017#ifdef CONFIG_PROC_FS
4018 DBG("destroying /proc/cpia/video%d\n", cam->vdev.minor);
4019 destroy_proc_cpia_cam(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03004020#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07004021 if (!cam->open_count) {
4022 DBG("freeing camera\n");
4023 kfree(cam);
4024 }
4025}
4026
4027static int __init cpia_init(void)
4028{
4029 printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT,
4030 CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER);
4031
4032 printk(KERN_WARNING "Since in-kernel colorspace conversion is not "
4033 "allowed, it is disabled by default now. Users should fix the "
4034 "applications in case they don't work without conversion "
4035 "reenabled by setting the 'colorspace_conv' module "
Randy Dunlap94190452006-03-27 16:18:25 -03004036 "parameter to 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004037
4038#ifdef CONFIG_PROC_FS
4039 proc_cpia_create();
4040#endif
4041
Linus Torvalds1da177e2005-04-16 15:20:36 -07004042 return 0;
4043}
4044
4045static void __exit cpia_exit(void)
4046{
4047#ifdef CONFIG_PROC_FS
4048 proc_cpia_destroy();
4049#endif
4050}
4051
4052module_init(cpia_init);
4053module_exit(cpia_exit);
4054
4055/* Exported symbols for modules. */
4056
4057EXPORT_SYMBOL(cpia_register_camera);
4058EXPORT_SYMBOL(cpia_unregister_camera);