blob: 551ddf216a4bb21d51b9c6425a992946594d0e0f [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * cpia CPiA driver
3 *
4 * Supports CPiA based Video Camera's.
5 *
6 * (C) Copyright 1999-2000 Peter Pregler
7 * (C) Copyright 1999-2000 Scott J. Bertin
8 * (C) Copyright 1999-2000 Johannes Erdfelt <johannes@erdfelt.com>
9 * (C) Copyright 2000 STMicroelectronics
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 */
25
26/* define _CPIA_DEBUG_ for verbose debug output (see cpia.h) */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030027/* #define _CPIA_DEBUG_ 1 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070028
Linus Torvalds1da177e2005-04-16 15:20:36 -070029
30#include <linux/module.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070031#include <linux/init.h>
32#include <linux/fs.h>
33#include <linux/vmalloc.h>
Alexey Dobriyana99bbaf2009-10-04 16:11:37 +040034#include <linux/sched.h>
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -030035#include <linux/seq_file.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070036#include <linux/slab.h>
37#include <linux/proc_fs.h>
38#include <linux/ctype.h>
39#include <linux/pagemap.h>
40#include <linux/delay.h>
41#include <asm/io.h>
Ingo Molnar3593cab2006-02-07 06:49:14 -020042#include <linux/mutex.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070043
Linus Torvalds1da177e2005-04-16 15:20:36 -070044#include "cpia.h"
45
Linus Torvalds1da177e2005-04-16 15:20:36 -070046static int video_nr = -1;
47
48#ifdef MODULE
49module_param(video_nr, int, 0);
Randy Dunlap2f8de1a2006-03-21 14:53:22 -030050MODULE_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 -070051MODULE_DESCRIPTION("V4L-driver for Vision CPiA based cameras");
52MODULE_LICENSE("GPL");
53MODULE_SUPPORTED_DEVICE("video");
54#endif
55
Randy Dunlap94190452006-03-27 16:18:25 -030056static unsigned short colorspace_conv;
Linus Torvalds1da177e2005-04-16 15:20:36 -070057module_param(colorspace_conv, ushort, 0444);
58MODULE_PARM_DESC(colorspace_conv,
Mauro Carvalho Chehab4286c6f2006-04-08 16:06:16 -030059 " Colorspace conversion:"
60 "\n 0 = disable, 1 = enable"
61 "\n Default value is 0"
62 );
Linus Torvalds1da177e2005-04-16 15:20:36 -070063
64#define ABOUT "V4L-Driver for Vision CPiA based cameras"
65
Linus Torvalds1da177e2005-04-16 15:20:36 -070066#define CPIA_MODULE_CPIA (0<<5)
67#define CPIA_MODULE_SYSTEM (1<<5)
68#define CPIA_MODULE_VP_CTRL (5<<5)
69#define CPIA_MODULE_CAPTURE (6<<5)
70#define CPIA_MODULE_DEBUG (7<<5)
71
72#define INPUT (DATA_IN << 8)
73#define OUTPUT (DATA_OUT << 8)
74
75#define CPIA_COMMAND_GetCPIAVersion (INPUT | CPIA_MODULE_CPIA | 1)
76#define CPIA_COMMAND_GetPnPID (INPUT | CPIA_MODULE_CPIA | 2)
77#define CPIA_COMMAND_GetCameraStatus (INPUT | CPIA_MODULE_CPIA | 3)
78#define CPIA_COMMAND_GotoHiPower (OUTPUT | CPIA_MODULE_CPIA | 4)
79#define CPIA_COMMAND_GotoLoPower (OUTPUT | CPIA_MODULE_CPIA | 5)
80#define CPIA_COMMAND_GotoSuspend (OUTPUT | CPIA_MODULE_CPIA | 7)
81#define CPIA_COMMAND_GotoPassThrough (OUTPUT | CPIA_MODULE_CPIA | 8)
82#define CPIA_COMMAND_ModifyCameraStatus (OUTPUT | CPIA_MODULE_CPIA | 10)
83
84#define CPIA_COMMAND_ReadVCRegs (INPUT | CPIA_MODULE_SYSTEM | 1)
85#define CPIA_COMMAND_WriteVCReg (OUTPUT | CPIA_MODULE_SYSTEM | 2)
86#define CPIA_COMMAND_ReadMCPorts (INPUT | CPIA_MODULE_SYSTEM | 3)
87#define CPIA_COMMAND_WriteMCPort (OUTPUT | CPIA_MODULE_SYSTEM | 4)
88#define CPIA_COMMAND_SetBaudRate (OUTPUT | CPIA_MODULE_SYSTEM | 5)
89#define CPIA_COMMAND_SetECPTiming (OUTPUT | CPIA_MODULE_SYSTEM | 6)
90#define CPIA_COMMAND_ReadIDATA (INPUT | CPIA_MODULE_SYSTEM | 7)
91#define CPIA_COMMAND_WriteIDATA (OUTPUT | CPIA_MODULE_SYSTEM | 8)
92#define CPIA_COMMAND_GenericCall (OUTPUT | CPIA_MODULE_SYSTEM | 9)
93#define CPIA_COMMAND_I2CStart (OUTPUT | CPIA_MODULE_SYSTEM | 10)
94#define CPIA_COMMAND_I2CStop (OUTPUT | CPIA_MODULE_SYSTEM | 11)
95#define CPIA_COMMAND_I2CWrite (OUTPUT | CPIA_MODULE_SYSTEM | 12)
96#define CPIA_COMMAND_I2CRead (INPUT | CPIA_MODULE_SYSTEM | 13)
97
98#define CPIA_COMMAND_GetVPVersion (INPUT | CPIA_MODULE_VP_CTRL | 1)
99#define CPIA_COMMAND_ResetFrameCounter (INPUT | CPIA_MODULE_VP_CTRL | 2)
100#define CPIA_COMMAND_SetColourParams (OUTPUT | CPIA_MODULE_VP_CTRL | 3)
101#define CPIA_COMMAND_SetExposure (OUTPUT | CPIA_MODULE_VP_CTRL | 4)
102#define CPIA_COMMAND_SetColourBalance (OUTPUT | CPIA_MODULE_VP_CTRL | 6)
103#define CPIA_COMMAND_SetSensorFPS (OUTPUT | CPIA_MODULE_VP_CTRL | 7)
104#define CPIA_COMMAND_SetVPDefaults (OUTPUT | CPIA_MODULE_VP_CTRL | 8)
105#define CPIA_COMMAND_SetApcor (OUTPUT | CPIA_MODULE_VP_CTRL | 9)
106#define CPIA_COMMAND_SetFlickerCtrl (OUTPUT | CPIA_MODULE_VP_CTRL | 10)
107#define CPIA_COMMAND_SetVLOffset (OUTPUT | CPIA_MODULE_VP_CTRL | 11)
108#define CPIA_COMMAND_GetColourParams (INPUT | CPIA_MODULE_VP_CTRL | 16)
109#define CPIA_COMMAND_GetColourBalance (INPUT | CPIA_MODULE_VP_CTRL | 17)
110#define CPIA_COMMAND_GetExposure (INPUT | CPIA_MODULE_VP_CTRL | 18)
111#define CPIA_COMMAND_SetSensorMatrix (OUTPUT | CPIA_MODULE_VP_CTRL | 19)
112#define CPIA_COMMAND_ColourBars (OUTPUT | CPIA_MODULE_VP_CTRL | 25)
113#define CPIA_COMMAND_ReadVPRegs (INPUT | CPIA_MODULE_VP_CTRL | 30)
114#define CPIA_COMMAND_WriteVPReg (OUTPUT | CPIA_MODULE_VP_CTRL | 31)
115
116#define CPIA_COMMAND_GrabFrame (OUTPUT | CPIA_MODULE_CAPTURE | 1)
117#define CPIA_COMMAND_UploadFrame (OUTPUT | CPIA_MODULE_CAPTURE | 2)
118#define CPIA_COMMAND_SetGrabMode (OUTPUT | CPIA_MODULE_CAPTURE | 3)
119#define CPIA_COMMAND_InitStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 4)
120#define CPIA_COMMAND_FiniStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 5)
121#define CPIA_COMMAND_StartStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 6)
122#define CPIA_COMMAND_EndStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 7)
123#define CPIA_COMMAND_SetFormat (OUTPUT | CPIA_MODULE_CAPTURE | 8)
124#define CPIA_COMMAND_SetROI (OUTPUT | CPIA_MODULE_CAPTURE | 9)
125#define CPIA_COMMAND_SetCompression (OUTPUT | CPIA_MODULE_CAPTURE | 10)
126#define CPIA_COMMAND_SetCompressionTarget (OUTPUT | CPIA_MODULE_CAPTURE | 11)
127#define CPIA_COMMAND_SetYUVThresh (OUTPUT | CPIA_MODULE_CAPTURE | 12)
128#define CPIA_COMMAND_SetCompressionParams (OUTPUT | CPIA_MODULE_CAPTURE | 13)
129#define CPIA_COMMAND_DiscardFrame (OUTPUT | CPIA_MODULE_CAPTURE | 14)
130#define CPIA_COMMAND_GrabReset (OUTPUT | CPIA_MODULE_CAPTURE | 15)
131
132#define CPIA_COMMAND_OutputRS232 (OUTPUT | CPIA_MODULE_DEBUG | 1)
133#define CPIA_COMMAND_AbortProcess (OUTPUT | CPIA_MODULE_DEBUG | 4)
134#define CPIA_COMMAND_SetDramPage (OUTPUT | CPIA_MODULE_DEBUG | 5)
135#define CPIA_COMMAND_StartDramUpload (OUTPUT | CPIA_MODULE_DEBUG | 6)
136#define CPIA_COMMAND_StartDummyDtream (OUTPUT | CPIA_MODULE_DEBUG | 8)
137#define CPIA_COMMAND_AbortStream (OUTPUT | CPIA_MODULE_DEBUG | 9)
138#define CPIA_COMMAND_DownloadDRAM (OUTPUT | CPIA_MODULE_DEBUG | 10)
139#define CPIA_COMMAND_Null (OUTPUT | CPIA_MODULE_DEBUG | 11)
140
141enum {
142 FRAME_READY, /* Ready to grab into */
143 FRAME_GRABBING, /* In the process of being grabbed into */
144 FRAME_DONE, /* Finished grabbing, but not been synced yet */
145 FRAME_UNUSED, /* Unused (no MCAPTURE) */
146};
147
148#define COMMAND_NONE 0x0000
149#define COMMAND_SETCOMPRESSION 0x0001
150#define COMMAND_SETCOMPRESSIONTARGET 0x0002
151#define COMMAND_SETCOLOURPARAMS 0x0004
152#define COMMAND_SETFORMAT 0x0008
153#define COMMAND_PAUSE 0x0010
154#define COMMAND_RESUME 0x0020
155#define COMMAND_SETYUVTHRESH 0x0040
156#define COMMAND_SETECPTIMING 0x0080
157#define COMMAND_SETCOMPRESSIONPARAMS 0x0100
158#define COMMAND_SETEXPOSURE 0x0200
159#define COMMAND_SETCOLOURBALANCE 0x0400
160#define COMMAND_SETSENSORFPS 0x0800
161#define COMMAND_SETAPCOR 0x1000
162#define COMMAND_SETFLICKERCTRL 0x2000
163#define COMMAND_SETVLOFFSET 0x4000
164#define COMMAND_SETLIGHTS 0x8000
165
166#define ROUND_UP_EXP_FOR_FLICKER 15
167
168/* Constants for automatic frame rate adjustment */
169#define MAX_EXP 302
170#define MAX_EXP_102 255
171#define LOW_EXP 140
172#define VERY_LOW_EXP 70
173#define TC 94
174#define EXP_ACC_DARK 50
175#define EXP_ACC_LIGHT 90
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300176#define HIGH_COMP_102 160
177#define MAX_COMP 239
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178#define DARK_TIME 3
179#define LIGHT_TIME 3
180
181/* Maximum number of 10ms loops to wait for the stream to become ready */
182#define READY_TIMEOUT 100
183
184/* Developer's Guide Table 5 p 3-34
185 * indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/
186static u8 flicker_jumps[2][2][4] =
187{ { { 76, 38, 19, 9 }, { 92, 46, 23, 11 } },
188 { { 64, 32, 16, 8 }, { 76, 38, 19, 9} }
189};
190
191/* forward declaration of local function */
192static void reset_camera_struct(struct cam_data *cam);
193static int find_over_exposure(int brightness);
194static void set_flicker(struct cam_params *params, volatile u32 *command_flags,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300195 int on);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196
197
198/**********************************************************************
199 *
200 * Memory management
201 *
202 **********************************************************************/
203static void *rvmalloc(unsigned long size)
204{
205 void *mem;
206 unsigned long adr;
207
208 size = PAGE_ALIGN(size);
209 mem = vmalloc_32(size);
210 if (!mem)
211 return NULL;
212
213 memset(mem, 0, size); /* Clear the ram out, no junk to the user */
214 adr = (unsigned long) mem;
215 while (size > 0) {
216 SetPageReserved(vmalloc_to_page((void *)adr));
217 adr += PAGE_SIZE;
218 size -= PAGE_SIZE;
219 }
220
221 return mem;
222}
223
224static void rvfree(void *mem, unsigned long size)
225{
226 unsigned long adr;
227
228 if (!mem)
229 return;
230
231 adr = (unsigned long) mem;
232 while ((long) size > 0) {
233 ClearPageReserved(vmalloc_to_page((void *)adr));
234 adr += PAGE_SIZE;
235 size -= PAGE_SIZE;
236 }
237 vfree(mem);
238}
239
240/**********************************************************************
241 *
242 * /proc interface
243 *
244 **********************************************************************/
245#ifdef CONFIG_PROC_FS
246static struct proc_dir_entry *cpia_proc_root=NULL;
247
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300248static int cpia_proc_show(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249{
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300250 struct cam_data *cam = m->private;
251 int tmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252 char tmpstr[29];
253
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300254 seq_printf(m, "read-only\n-----------------------\n");
255 seq_printf(m, "V4L Driver version: %d.%d.%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256 CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300257 seq_printf(m, "CPIA Version: %d.%02d (%d.%d)\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300258 cam->params.version.firmwareVersion,
259 cam->params.version.firmwareRevision,
260 cam->params.version.vcVersion,
261 cam->params.version.vcRevision);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300262 seq_printf(m, "CPIA PnP-ID: %04x:%04x:%04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300263 cam->params.pnpID.vendor, cam->params.pnpID.product,
264 cam->params.pnpID.deviceRevision);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300265 seq_printf(m, "VP-Version: %d.%d %04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300266 cam->params.vpVersion.vpVersion,
267 cam->params.vpVersion.vpRevision,
268 cam->params.vpVersion.cameraHeadID);
269
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300270 seq_printf(m, "system_state: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300271 cam->params.status.systemState);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300272 seq_printf(m, "grab_state: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300273 cam->params.status.grabState);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300274 seq_printf(m, "stream_state: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300275 cam->params.status.streamState);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300276 seq_printf(m, "fatal_error: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300277 cam->params.status.fatalError);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300278 seq_printf(m, "cmd_error: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300279 cam->params.status.cmdError);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300280 seq_printf(m, "debug_flags: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300281 cam->params.status.debugFlags);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300282 seq_printf(m, "vp_status: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300283 cam->params.status.vpStatus);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300284 seq_printf(m, "error_code: %#04x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300285 cam->params.status.errorCode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286 /* QX3 specific entries */
287 if (cam->params.qx3.qx3_detected) {
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300288 seq_printf(m, "button: %4d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300289 cam->params.qx3.button);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300290 seq_printf(m, "cradled: %4d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300291 cam->params.qx3.cradled);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 }
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300293 seq_printf(m, "video_size: %s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300294 cam->params.format.videoSize == VIDEOSIZE_CIF ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 "CIF " : "QCIF");
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300296 seq_printf(m, "roi: (%3d, %3d) to (%3d, %3d)\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300297 cam->params.roi.colStart*8,
298 cam->params.roi.rowStart*4,
299 cam->params.roi.colEnd*8,
300 cam->params.roi.rowEnd*4);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300301 seq_printf(m, "actual_fps: %3d\n", cam->fps);
302 seq_printf(m, "transfer_rate: %4dkB/s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300303 cam->transfer_rate);
304
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300305 seq_printf(m, "\nread-write\n");
306 seq_printf(m, "----------------------- current min"
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300307 " max default comment\n");
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300308 seq_printf(m, "brightness: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300309 cam->params.colourParams.brightness, 0, 100, 50);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310 if (cam->params.version.firmwareVersion == 1 &&
311 cam->params.version.firmwareRevision == 2)
312 /* 1-02 firmware limits contrast to 80 */
313 tmp = 80;
314 else
315 tmp = 96;
316
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300317 seq_printf(m, "contrast: %8d %8d %8d %8d"
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300318 " steps of 8\n",
319 cam->params.colourParams.contrast, 0, tmp, 48);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300320 seq_printf(m, "saturation: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300321 cam->params.colourParams.saturation, 0, 100, 50);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 tmp = (25000+5000*cam->params.sensorFps.baserate)/
323 (1<<cam->params.sensorFps.divisor);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300324 seq_printf(m, "sensor_fps: %4d.%03d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300325 tmp/1000, tmp%1000, 3, 30, 15);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300326 seq_printf(m, "stream_start_line: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300327 2*cam->params.streamStartLine, 0,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328 cam->params.format.videoSize == VIDEOSIZE_CIF ? 288:144,
329 cam->params.format.videoSize == VIDEOSIZE_CIF ? 240:120);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300330 seq_printf(m, "sub_sample: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300331 cam->params.format.subSample == SUBSAMPLE_420 ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332 "420" : "422", "420", "422", "422");
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300333 seq_printf(m, "yuv_order: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300334 cam->params.format.yuvOrder == YUVORDER_YUYV ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335 "YUYV" : "UYVY", "YUYV" , "UYVY", "YUYV");
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300336 seq_printf(m, "ecp_timing: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300337 cam->params.ecpTiming ? "slow" : "normal", "slow",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 "normal", "normal");
339
340 if (cam->params.colourBalance.balanceMode == 2) {
341 sprintf(tmpstr, "auto");
342 } else {
343 sprintf(tmpstr, "manual");
344 }
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300345 seq_printf(m, "color_balance_mode: %8s %8s %8s"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346 " %8s\n", tmpstr, "manual", "auto", "auto");
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300347 seq_printf(m, "red_gain: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300348 cam->params.colourBalance.redGain, 0, 212, 32);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300349 seq_printf(m, "green_gain: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300350 cam->params.colourBalance.greenGain, 0, 212, 6);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300351 seq_printf(m, "blue_gain: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300352 cam->params.colourBalance.blueGain, 0, 212, 92);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353
354 if (cam->params.version.firmwareVersion == 1 &&
355 cam->params.version.firmwareRevision == 2)
356 /* 1-02 firmware limits gain to 2 */
357 sprintf(tmpstr, "%8d %8d %8d", 1, 2, 2);
358 else
359 sprintf(tmpstr, "%8d %8d %8d", 1, 8, 2);
360
361 if (cam->params.exposure.gainMode == 0)
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300362 seq_printf(m, "max_gain: unknown %28s"
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300363 " powers of 2\n", tmpstr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364 else
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300365 seq_printf(m, "max_gain: %8d %28s"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366 " 1,2,4 or 8 \n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300367 1<<(cam->params.exposure.gainMode-1), tmpstr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368
369 switch(cam->params.exposure.expMode) {
370 case 1:
371 case 3:
372 sprintf(tmpstr, "manual");
373 break;
374 case 2:
375 sprintf(tmpstr, "auto");
376 break;
377 default:
378 sprintf(tmpstr, "unknown");
379 break;
380 }
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300381 seq_printf(m, "exposure_mode: %8s %8s %8s"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 " %8s\n", tmpstr, "manual", "auto", "auto");
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300383 seq_printf(m, "centre_weight: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300384 (2-cam->params.exposure.centreWeight) ? "on" : "off",
385 "off", "on", "on");
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300386 seq_printf(m, "gain: %8d %8d max_gain %8d 1,2,4,8 possible\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300387 1<<cam->params.exposure.gain, 1, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 if (cam->params.version.firmwareVersion == 1 &&
389 cam->params.version.firmwareRevision == 2)
390 /* 1-02 firmware limits fineExp/2 to 127 */
391 tmp = 254;
392 else
393 tmp = 510;
394
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300395 seq_printf(m, "fine_exp: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300396 cam->params.exposure.fineExp*2, 0, tmp, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397 if (cam->params.version.firmwareVersion == 1 &&
398 cam->params.version.firmwareRevision == 2)
399 /* 1-02 firmware limits coarseExpHi to 0 */
400 tmp = MAX_EXP_102;
401 else
402 tmp = MAX_EXP;
403
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300404 seq_printf(m, "coarse_exp: %8d %8d %8d"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405 " %8d\n", cam->params.exposure.coarseExpLo+
406 256*cam->params.exposure.coarseExpHi, 0, tmp, 185);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300407 seq_printf(m, "red_comp: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300408 cam->params.exposure.redComp, COMP_RED, 255, COMP_RED);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300409 seq_printf(m, "green1_comp: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300410 cam->params.exposure.green1Comp, COMP_GREEN1, 255,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411 COMP_GREEN1);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300412 seq_printf(m, "green2_comp: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300413 cam->params.exposure.green2Comp, COMP_GREEN2, 255,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 COMP_GREEN2);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300415 seq_printf(m, "blue_comp: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300416 cam->params.exposure.blueComp, COMP_BLUE, 255, COMP_BLUE);
417
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300418 seq_printf(m, "apcor_gain1: %#8x %#8x %#8x %#8x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300419 cam->params.apcor.gain1, 0, 0xff, 0x1c);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300420 seq_printf(m, "apcor_gain2: %#8x %#8x %#8x %#8x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300421 cam->params.apcor.gain2, 0, 0xff, 0x1a);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300422 seq_printf(m, "apcor_gain4: %#8x %#8x %#8x %#8x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300423 cam->params.apcor.gain4, 0, 0xff, 0x2d);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300424 seq_printf(m, "apcor_gain8: %#8x %#8x %#8x %#8x\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300425 cam->params.apcor.gain8, 0, 0xff, 0x2a);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300426 seq_printf(m, "vl_offset_gain1: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300427 cam->params.vlOffset.gain1, 0, 255, 24);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300428 seq_printf(m, "vl_offset_gain2: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300429 cam->params.vlOffset.gain2, 0, 255, 28);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300430 seq_printf(m, "vl_offset_gain4: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300431 cam->params.vlOffset.gain4, 0, 255, 30);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300432 seq_printf(m, "vl_offset_gain8: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300433 cam->params.vlOffset.gain8, 0, 255, 30);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300434 seq_printf(m, "flicker_control: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300435 cam->params.flickerControl.flickerMode ? "on" : "off",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436 "off", "on", "off");
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300437 seq_printf(m, "mains_frequency: %8d %8d %8d %8d"
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300438 " only 50/60\n",
439 cam->mainsFreq ? 60 : 50, 50, 60, 50);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440 if(cam->params.flickerControl.allowableOverExposure < 0)
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300441 seq_printf(m, "allowable_overexposure: %4dauto auto %8d auto\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300442 -cam->params.flickerControl.allowableOverExposure,
443 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444 else
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300445 seq_printf(m, "allowable_overexposure: %8d auto %8d auto\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300446 cam->params.flickerControl.allowableOverExposure,
447 255);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300448 seq_printf(m, "compression_mode: ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449 switch(cam->params.compression.mode) {
450 case CPIA_COMPRESSION_NONE:
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300451 seq_printf(m, "%8s", "none");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452 break;
453 case CPIA_COMPRESSION_AUTO:
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300454 seq_printf(m, "%8s", "auto");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 break;
456 case CPIA_COMPRESSION_MANUAL:
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300457 seq_printf(m, "%8s", "manual");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458 break;
459 default:
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300460 seq_printf(m, "%8s", "unknown");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461 break;
462 }
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300463 seq_printf(m, " none,auto,manual auto\n");
464 seq_printf(m, "decimation_enable: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300465 cam->params.compression.decimation ==
466 DECIMATION_ENAB ? "on":"off", "off", "on",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467 "off");
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300468 seq_printf(m, "compression_target: %9s %9s %9s %9s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300469 cam->params.compressionTarget.frTargeting ==
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470 CPIA_COMPRESSION_TARGET_FRAMERATE ?
471 "framerate":"quality",
472 "framerate", "quality", "quality");
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300473 seq_printf(m, "target_framerate: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300474 cam->params.compressionTarget.targetFR, 1, 30, 15);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300475 seq_printf(m, "target_quality: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300476 cam->params.compressionTarget.targetQ, 1, 64, 5);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300477 seq_printf(m, "y_threshold: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300478 cam->params.yuvThreshold.yThreshold, 0, 31, 6);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300479 seq_printf(m, "uv_threshold: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300480 cam->params.yuvThreshold.uvThreshold, 0, 31, 6);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300481 seq_printf(m, "hysteresis: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300482 cam->params.compressionParams.hysteresis, 0, 255, 3);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300483 seq_printf(m, "threshold_max: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300484 cam->params.compressionParams.threshMax, 0, 255, 11);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300485 seq_printf(m, "small_step: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300486 cam->params.compressionParams.smallStep, 0, 255, 1);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300487 seq_printf(m, "large_step: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300488 cam->params.compressionParams.largeStep, 0, 255, 3);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300489 seq_printf(m, "decimation_hysteresis: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300490 cam->params.compressionParams.decimationHysteresis,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491 0, 255, 2);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300492 seq_printf(m, "fr_diff_step_thresh: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300493 cam->params.compressionParams.frDiffStepThresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494 0, 255, 5);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300495 seq_printf(m, "q_diff_step_thresh: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300496 cam->params.compressionParams.qDiffStepThresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497 0, 255, 3);
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300498 seq_printf(m, "decimation_thresh_mod: %8d %8d %8d %8d\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300499 cam->params.compressionParams.decimationThreshMod,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 0, 255, 2);
501 /* QX3 specific entries */
502 if (cam->params.qx3.qx3_detected) {
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300503 seq_printf(m, "toplight: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300504 cam->params.qx3.toplight ? "on" : "off",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505 "off", "on", "off");
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300506 seq_printf(m, "bottomlight: %8s %8s %8s %8s\n",
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300507 cam->params.qx3.bottomlight ? "on" : "off",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508 "off", "on", "off");
509 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300510
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300511 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512}
513
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300514static int cpia_proc_open(struct inode *inode, struct file *file)
515{
516 return single_open(file, cpia_proc_show, PDE(inode)->data);
517}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300519static int match(char *checkstr, char **buffer, size_t *count,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300520 int *find_colon, int *err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521{
522 int ret, colon_found = 1;
523 int len = strlen(checkstr);
524 ret = (len <= *count && strncmp(*buffer, checkstr, len) == 0);
525 if (ret) {
526 *buffer += len;
527 *count -= len;
528 if (*find_colon) {
529 colon_found = 0;
530 while (*count && (**buffer == ' ' || **buffer == '\t' ||
531 (!colon_found && **buffer == ':'))) {
532 if (**buffer == ':')
533 colon_found = 1;
534 --*count;
535 ++*buffer;
536 }
537 if (!*count || !colon_found)
538 *err = -EINVAL;
539 *find_colon = 0;
540 }
541 }
542 return ret;
543}
544
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300545static unsigned long int value(char **buffer, size_t *count, int *err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546{
547 char *p;
548 unsigned long int ret;
549 ret = simple_strtoul(*buffer, &p, 0);
550 if (p == *buffer)
551 *err = -EINVAL;
552 else {
553 *count -= p - *buffer;
554 *buffer = p;
555 }
556 return ret;
557}
558
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300559static ssize_t cpia_proc_write(struct file *file, const char __user *buf,
560 size_t count, loff_t *pos)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561{
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300562 struct cam_data *cam = PDE(file->f_path.dentry->d_inode)->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563 struct cam_params new_params;
564 char *page, *buffer;
565 int retval, find_colon;
566 int size = count;
567 unsigned long val = 0;
568 u32 command_flags = 0;
569 u8 new_mains;
570
571 /*
572 * This code to copy from buf to page is shamelessly copied
573 * from the comx driver
574 */
575 if (count > PAGE_SIZE) {
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -0300576 printk(KERN_ERR "count is %zu > %d!!!\n", count, (int)PAGE_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577 return -ENOSPC;
578 }
579
580 if (!(page = (char *)__get_free_page(GFP_KERNEL))) return -ENOMEM;
581
582 if(copy_from_user(page, buf, count))
583 {
584 retval = -EFAULT;
585 goto out;
586 }
587
588 if (page[count-1] == '\n')
589 page[count-1] = '\0';
590 else if (count < PAGE_SIZE)
591 page[count] = '\0';
592 else if (page[count]) {
593 retval = -EINVAL;
594 goto out;
595 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300596
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597 buffer = page;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300598
Ingo Molnar3593cab2006-02-07 06:49:14 -0200599 if (mutex_lock_interruptible(&cam->param_lock))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600 return -ERESTARTSYS;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300601
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602 /*
603 * Skip over leading whitespace
604 */
605 while (count && isspace(*buffer)) {
606 --count;
607 ++buffer;
608 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300609
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610 memcpy(&new_params, &cam->params, sizeof(struct cam_params));
611 new_mains = cam->mainsFreq;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300612
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613#define MATCH(x) (match(x, &buffer, &count, &find_colon, &retval))
614#define VALUE (value(&buffer,&count, &retval))
615#define FIRMWARE_VERSION(x,y) (new_params.version.firmwareVersion == (x) && \
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300616 new_params.version.firmwareRevision == (y))
617
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618 retval = 0;
619 while (count && !retval) {
620 find_colon = 1;
621 if (MATCH("brightness")) {
622 if (!retval)
623 val = VALUE;
624
625 if (!retval) {
626 if (val <= 100)
627 new_params.colourParams.brightness = val;
628 else
629 retval = -EINVAL;
630 }
631 command_flags |= COMMAND_SETCOLOURPARAMS;
632 if(new_params.flickerControl.allowableOverExposure < 0)
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300633 new_params.flickerControl.allowableOverExposure =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634 -find_over_exposure(new_params.colourParams.brightness);
635 if(new_params.flickerControl.flickerMode != 0)
636 command_flags |= COMMAND_SETFLICKERCTRL;
637
638 } else if (MATCH("contrast")) {
639 if (!retval)
640 val = VALUE;
641
642 if (!retval) {
643 if (val <= 100) {
644 /* contrast is in steps of 8, so round*/
645 val = ((val + 3) / 8) * 8;
646 /* 1-02 firmware limits contrast to 80*/
647 if (FIRMWARE_VERSION(1,2) && val > 80)
648 val = 80;
649
650 new_params.colourParams.contrast = val;
651 } else
652 retval = -EINVAL;
653 }
654 command_flags |= COMMAND_SETCOLOURPARAMS;
655 } else if (MATCH("saturation")) {
656 if (!retval)
657 val = VALUE;
658
659 if (!retval) {
660 if (val <= 100)
661 new_params.colourParams.saturation = val;
662 else
663 retval = -EINVAL;
664 }
665 command_flags |= COMMAND_SETCOLOURPARAMS;
666 } else if (MATCH("sensor_fps")) {
667 if (!retval)
668 val = VALUE;
669
670 if (!retval) {
671 /* find values so that sensorFPS is minimized,
672 * but >= val */
673 if (val > 30)
674 retval = -EINVAL;
675 else if (val > 25) {
676 new_params.sensorFps.divisor = 0;
677 new_params.sensorFps.baserate = 1;
678 } else if (val > 15) {
679 new_params.sensorFps.divisor = 0;
680 new_params.sensorFps.baserate = 0;
681 } else if (val > 12) {
682 new_params.sensorFps.divisor = 1;
683 new_params.sensorFps.baserate = 1;
684 } else if (val > 7) {
685 new_params.sensorFps.divisor = 1;
686 new_params.sensorFps.baserate = 0;
687 } else if (val > 6) {
688 new_params.sensorFps.divisor = 2;
689 new_params.sensorFps.baserate = 1;
690 } else if (val > 3) {
691 new_params.sensorFps.divisor = 2;
692 new_params.sensorFps.baserate = 0;
693 } else {
694 new_params.sensorFps.divisor = 3;
695 /* Either base rate would work here */
696 new_params.sensorFps.baserate = 1;
697 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300698 new_params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699 flicker_jumps[new_mains]
700 [new_params.sensorFps.baserate]
701 [new_params.sensorFps.divisor];
702 if (new_params.flickerControl.flickerMode)
703 command_flags |= COMMAND_SETFLICKERCTRL;
704 }
705 command_flags |= COMMAND_SETSENSORFPS;
706 cam->exposure_status = EXPOSURE_NORMAL;
707 } else if (MATCH("stream_start_line")) {
708 if (!retval)
709 val = VALUE;
710
711 if (!retval) {
712 int max_line = 288;
713
714 if (new_params.format.videoSize == VIDEOSIZE_QCIF)
715 max_line = 144;
716 if (val <= max_line)
717 new_params.streamStartLine = val/2;
718 else
719 retval = -EINVAL;
720 }
721 } else if (MATCH("sub_sample")) {
722 if (!retval && MATCH("420"))
723 new_params.format.subSample = SUBSAMPLE_420;
724 else if (!retval && MATCH("422"))
725 new_params.format.subSample = SUBSAMPLE_422;
726 else
727 retval = -EINVAL;
728
729 command_flags |= COMMAND_SETFORMAT;
730 } else if (MATCH("yuv_order")) {
731 if (!retval && MATCH("YUYV"))
732 new_params.format.yuvOrder = YUVORDER_YUYV;
733 else if (!retval && MATCH("UYVY"))
734 new_params.format.yuvOrder = YUVORDER_UYVY;
735 else
736 retval = -EINVAL;
737
738 command_flags |= COMMAND_SETFORMAT;
739 } else if (MATCH("ecp_timing")) {
740 if (!retval && MATCH("normal"))
741 new_params.ecpTiming = 0;
742 else if (!retval && MATCH("slow"))
743 new_params.ecpTiming = 1;
744 else
745 retval = -EINVAL;
746
747 command_flags |= COMMAND_SETECPTIMING;
748 } else if (MATCH("color_balance_mode")) {
749 if (!retval && MATCH("manual"))
750 new_params.colourBalance.balanceMode = 3;
751 else if (!retval && MATCH("auto"))
752 new_params.colourBalance.balanceMode = 2;
753 else
754 retval = -EINVAL;
755
756 command_flags |= COMMAND_SETCOLOURBALANCE;
757 } else if (MATCH("red_gain")) {
758 if (!retval)
759 val = VALUE;
760
761 if (!retval) {
762 if (val <= 212) {
763 new_params.colourBalance.redGain = val;
764 new_params.colourBalance.balanceMode = 1;
765 } else
766 retval = -EINVAL;
767 }
768 command_flags |= COMMAND_SETCOLOURBALANCE;
769 } else if (MATCH("green_gain")) {
770 if (!retval)
771 val = VALUE;
772
773 if (!retval) {
774 if (val <= 212) {
775 new_params.colourBalance.greenGain = val;
776 new_params.colourBalance.balanceMode = 1;
777 } else
778 retval = -EINVAL;
779 }
780 command_flags |= COMMAND_SETCOLOURBALANCE;
781 } else if (MATCH("blue_gain")) {
782 if (!retval)
783 val = VALUE;
784
785 if (!retval) {
786 if (val <= 212) {
787 new_params.colourBalance.blueGain = val;
788 new_params.colourBalance.balanceMode = 1;
789 } else
790 retval = -EINVAL;
791 }
792 command_flags |= COMMAND_SETCOLOURBALANCE;
793 } else if (MATCH("max_gain")) {
794 if (!retval)
795 val = VALUE;
796
797 if (!retval) {
798 /* 1-02 firmware limits gain to 2 */
799 if (FIRMWARE_VERSION(1,2) && val > 2)
800 val = 2;
801 switch(val) {
802 case 1:
803 new_params.exposure.gainMode = 1;
804 break;
805 case 2:
806 new_params.exposure.gainMode = 2;
807 break;
808 case 4:
809 new_params.exposure.gainMode = 3;
810 break;
811 case 8:
812 new_params.exposure.gainMode = 4;
813 break;
814 default:
815 retval = -EINVAL;
816 break;
817 }
818 }
819 command_flags |= COMMAND_SETEXPOSURE;
820 } else if (MATCH("exposure_mode")) {
821 if (!retval && MATCH("auto"))
822 new_params.exposure.expMode = 2;
823 else if (!retval && MATCH("manual")) {
824 if (new_params.exposure.expMode == 2)
825 new_params.exposure.expMode = 3;
826 if(new_params.flickerControl.flickerMode != 0)
827 command_flags |= COMMAND_SETFLICKERCTRL;
828 new_params.flickerControl.flickerMode = 0;
829 } else
830 retval = -EINVAL;
831
832 command_flags |= COMMAND_SETEXPOSURE;
833 } else if (MATCH("centre_weight")) {
834 if (!retval && MATCH("on"))
835 new_params.exposure.centreWeight = 1;
836 else if (!retval && MATCH("off"))
837 new_params.exposure.centreWeight = 2;
838 else
839 retval = -EINVAL;
840
841 command_flags |= COMMAND_SETEXPOSURE;
842 } else if (MATCH("gain")) {
843 if (!retval)
844 val = VALUE;
845
846 if (!retval) {
847 switch(val) {
848 case 1:
849 new_params.exposure.gain = 0;
850 break;
851 case 2:
852 new_params.exposure.gain = 1;
853 break;
854 case 4:
855 new_params.exposure.gain = 2;
856 break;
857 case 8:
858 new_params.exposure.gain = 3;
859 break;
860 default:
861 retval = -EINVAL;
862 break;
863 }
864 new_params.exposure.expMode = 1;
865 if(new_params.flickerControl.flickerMode != 0)
866 command_flags |= COMMAND_SETFLICKERCTRL;
867 new_params.flickerControl.flickerMode = 0;
868 command_flags |= COMMAND_SETEXPOSURE;
869 if (new_params.exposure.gain >
870 new_params.exposure.gainMode-1)
871 retval = -EINVAL;
872 }
873 } else if (MATCH("fine_exp")) {
874 if (!retval)
875 val = VALUE/2;
876
877 if (!retval) {
878 if (val < 256) {
879 /* 1-02 firmware limits fineExp/2 to 127*/
880 if (FIRMWARE_VERSION(1,2) && val > 127)
881 val = 127;
882 new_params.exposure.fineExp = val;
883 new_params.exposure.expMode = 1;
884 command_flags |= COMMAND_SETEXPOSURE;
885 if(new_params.flickerControl.flickerMode != 0)
886 command_flags |= COMMAND_SETFLICKERCTRL;
887 new_params.flickerControl.flickerMode = 0;
888 command_flags |= COMMAND_SETFLICKERCTRL;
889 } else
890 retval = -EINVAL;
891 }
892 } else if (MATCH("coarse_exp")) {
893 if (!retval)
894 val = VALUE;
895
896 if (!retval) {
897 if (val <= MAX_EXP) {
898 if (FIRMWARE_VERSION(1,2) &&
899 val > MAX_EXP_102)
900 val = MAX_EXP_102;
901 new_params.exposure.coarseExpLo =
902 val & 0xff;
903 new_params.exposure.coarseExpHi =
904 val >> 8;
905 new_params.exposure.expMode = 1;
906 command_flags |= COMMAND_SETEXPOSURE;
907 if(new_params.flickerControl.flickerMode != 0)
908 command_flags |= COMMAND_SETFLICKERCTRL;
909 new_params.flickerControl.flickerMode = 0;
910 command_flags |= COMMAND_SETFLICKERCTRL;
911 } else
912 retval = -EINVAL;
913 }
914 } else if (MATCH("red_comp")) {
915 if (!retval)
916 val = VALUE;
917
918 if (!retval) {
919 if (val >= COMP_RED && val <= 255) {
920 new_params.exposure.redComp = val;
921 new_params.exposure.compMode = 1;
922 command_flags |= COMMAND_SETEXPOSURE;
923 } else
924 retval = -EINVAL;
925 }
926 } else if (MATCH("green1_comp")) {
927 if (!retval)
928 val = VALUE;
929
930 if (!retval) {
931 if (val >= COMP_GREEN1 && val <= 255) {
932 new_params.exposure.green1Comp = val;
933 new_params.exposure.compMode = 1;
934 command_flags |= COMMAND_SETEXPOSURE;
935 } else
936 retval = -EINVAL;
937 }
938 } else if (MATCH("green2_comp")) {
939 if (!retval)
940 val = VALUE;
941
942 if (!retval) {
943 if (val >= COMP_GREEN2 && val <= 255) {
944 new_params.exposure.green2Comp = val;
945 new_params.exposure.compMode = 1;
946 command_flags |= COMMAND_SETEXPOSURE;
947 } else
948 retval = -EINVAL;
949 }
950 } else if (MATCH("blue_comp")) {
951 if (!retval)
952 val = VALUE;
953
954 if (!retval) {
955 if (val >= COMP_BLUE && val <= 255) {
956 new_params.exposure.blueComp = val;
957 new_params.exposure.compMode = 1;
958 command_flags |= COMMAND_SETEXPOSURE;
959 } else
960 retval = -EINVAL;
961 }
962 } else if (MATCH("apcor_gain1")) {
963 if (!retval)
964 val = VALUE;
965
966 if (!retval) {
967 command_flags |= COMMAND_SETAPCOR;
968 if (val <= 0xff)
969 new_params.apcor.gain1 = val;
970 else
971 retval = -EINVAL;
972 }
973 } else if (MATCH("apcor_gain2")) {
974 if (!retval)
975 val = VALUE;
976
977 if (!retval) {
978 command_flags |= COMMAND_SETAPCOR;
979 if (val <= 0xff)
980 new_params.apcor.gain2 = val;
981 else
982 retval = -EINVAL;
983 }
984 } else if (MATCH("apcor_gain4")) {
985 if (!retval)
986 val = VALUE;
987
988 if (!retval) {
989 command_flags |= COMMAND_SETAPCOR;
990 if (val <= 0xff)
991 new_params.apcor.gain4 = val;
992 else
993 retval = -EINVAL;
994 }
995 } else if (MATCH("apcor_gain8")) {
996 if (!retval)
997 val = VALUE;
998
999 if (!retval) {
1000 command_flags |= COMMAND_SETAPCOR;
1001 if (val <= 0xff)
1002 new_params.apcor.gain8 = val;
1003 else
1004 retval = -EINVAL;
1005 }
1006 } else if (MATCH("vl_offset_gain1")) {
1007 if (!retval)
1008 val = VALUE;
1009
1010 if (!retval) {
1011 if (val <= 0xff)
1012 new_params.vlOffset.gain1 = val;
1013 else
1014 retval = -EINVAL;
1015 }
1016 command_flags |= COMMAND_SETVLOFFSET;
1017 } else if (MATCH("vl_offset_gain2")) {
1018 if (!retval)
1019 val = VALUE;
1020
1021 if (!retval) {
1022 if (val <= 0xff)
1023 new_params.vlOffset.gain2 = val;
1024 else
1025 retval = -EINVAL;
1026 }
1027 command_flags |= COMMAND_SETVLOFFSET;
1028 } else if (MATCH("vl_offset_gain4")) {
1029 if (!retval)
1030 val = VALUE;
1031
1032 if (!retval) {
1033 if (val <= 0xff)
1034 new_params.vlOffset.gain4 = val;
1035 else
1036 retval = -EINVAL;
1037 }
1038 command_flags |= COMMAND_SETVLOFFSET;
1039 } else if (MATCH("vl_offset_gain8")) {
1040 if (!retval)
1041 val = VALUE;
1042
1043 if (!retval) {
1044 if (val <= 0xff)
1045 new_params.vlOffset.gain8 = val;
1046 else
1047 retval = -EINVAL;
1048 }
1049 command_flags |= COMMAND_SETVLOFFSET;
1050 } else if (MATCH("flicker_control")) {
1051 if (!retval && MATCH("on")) {
1052 set_flicker(&new_params, &command_flags, 1);
1053 } else if (!retval && MATCH("off")) {
1054 set_flicker(&new_params, &command_flags, 0);
1055 } else
1056 retval = -EINVAL;
1057
1058 command_flags |= COMMAND_SETFLICKERCTRL;
1059 } else if (MATCH("mains_frequency")) {
1060 if (!retval && MATCH("50")) {
1061 new_mains = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001062 new_params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063 flicker_jumps[new_mains]
1064 [new_params.sensorFps.baserate]
1065 [new_params.sensorFps.divisor];
1066 if (new_params.flickerControl.flickerMode)
1067 command_flags |= COMMAND_SETFLICKERCTRL;
1068 } else if (!retval && MATCH("60")) {
1069 new_mains = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001070 new_params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071 flicker_jumps[new_mains]
1072 [new_params.sensorFps.baserate]
1073 [new_params.sensorFps.divisor];
1074 if (new_params.flickerControl.flickerMode)
1075 command_flags |= COMMAND_SETFLICKERCTRL;
1076 } else
1077 retval = -EINVAL;
1078 } else if (MATCH("allowable_overexposure")) {
1079 if (!retval && MATCH("auto")) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001080 new_params.flickerControl.allowableOverExposure =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081 -find_over_exposure(new_params.colourParams.brightness);
1082 if(new_params.flickerControl.flickerMode != 0)
1083 command_flags |= COMMAND_SETFLICKERCTRL;
1084 } else {
1085 if (!retval)
1086 val = VALUE;
1087
1088 if (!retval) {
1089 if (val <= 0xff) {
1090 new_params.flickerControl.
1091 allowableOverExposure = val;
1092 if(new_params.flickerControl.flickerMode != 0)
1093 command_flags |= COMMAND_SETFLICKERCTRL;
1094 } else
1095 retval = -EINVAL;
1096 }
1097 }
1098 } else if (MATCH("compression_mode")) {
1099 if (!retval && MATCH("none"))
1100 new_params.compression.mode =
1101 CPIA_COMPRESSION_NONE;
1102 else if (!retval && MATCH("auto"))
1103 new_params.compression.mode =
1104 CPIA_COMPRESSION_AUTO;
1105 else if (!retval && MATCH("manual"))
1106 new_params.compression.mode =
1107 CPIA_COMPRESSION_MANUAL;
1108 else
1109 retval = -EINVAL;
1110
1111 command_flags |= COMMAND_SETCOMPRESSION;
1112 } else if (MATCH("decimation_enable")) {
1113 if (!retval && MATCH("off"))
1114 new_params.compression.decimation = 0;
1115 else if (!retval && MATCH("on"))
1116 new_params.compression.decimation = 1;
1117 else
1118 retval = -EINVAL;
1119
1120 command_flags |= COMMAND_SETCOMPRESSION;
1121 } else if (MATCH("compression_target")) {
1122 if (!retval && MATCH("quality"))
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001123 new_params.compressionTarget.frTargeting =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001124 CPIA_COMPRESSION_TARGET_QUALITY;
1125 else if (!retval && MATCH("framerate"))
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001126 new_params.compressionTarget.frTargeting =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001127 CPIA_COMPRESSION_TARGET_FRAMERATE;
1128 else
1129 retval = -EINVAL;
1130
1131 command_flags |= COMMAND_SETCOMPRESSIONTARGET;
1132 } else if (MATCH("target_framerate")) {
1133 if (!retval)
1134 val = VALUE;
1135
1136 if (!retval) {
1137 if(val > 0 && val <= 30)
1138 new_params.compressionTarget.targetFR = val;
1139 else
1140 retval = -EINVAL;
1141 }
1142 command_flags |= COMMAND_SETCOMPRESSIONTARGET;
1143 } else if (MATCH("target_quality")) {
1144 if (!retval)
1145 val = VALUE;
1146
1147 if (!retval) {
1148 if(val > 0 && val <= 64)
1149 new_params.compressionTarget.targetQ = val;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001150 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151 retval = -EINVAL;
1152 }
1153 command_flags |= COMMAND_SETCOMPRESSIONTARGET;
1154 } else if (MATCH("y_threshold")) {
1155 if (!retval)
1156 val = VALUE;
1157
1158 if (!retval) {
1159 if (val < 32)
1160 new_params.yuvThreshold.yThreshold = val;
1161 else
1162 retval = -EINVAL;
1163 }
1164 command_flags |= COMMAND_SETYUVTHRESH;
1165 } else if (MATCH("uv_threshold")) {
1166 if (!retval)
1167 val = VALUE;
1168
1169 if (!retval) {
1170 if (val < 32)
1171 new_params.yuvThreshold.uvThreshold = val;
1172 else
1173 retval = -EINVAL;
1174 }
1175 command_flags |= COMMAND_SETYUVTHRESH;
1176 } else if (MATCH("hysteresis")) {
1177 if (!retval)
1178 val = VALUE;
1179
1180 if (!retval) {
1181 if (val <= 0xff)
1182 new_params.compressionParams.hysteresis = val;
1183 else
1184 retval = -EINVAL;
1185 }
1186 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1187 } else if (MATCH("threshold_max")) {
1188 if (!retval)
1189 val = VALUE;
1190
1191 if (!retval) {
1192 if (val <= 0xff)
1193 new_params.compressionParams.threshMax = val;
1194 else
1195 retval = -EINVAL;
1196 }
1197 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1198 } else if (MATCH("small_step")) {
1199 if (!retval)
1200 val = VALUE;
1201
1202 if (!retval) {
1203 if (val <= 0xff)
1204 new_params.compressionParams.smallStep = val;
1205 else
1206 retval = -EINVAL;
1207 }
1208 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1209 } else if (MATCH("large_step")) {
1210 if (!retval)
1211 val = VALUE;
1212
1213 if (!retval) {
1214 if (val <= 0xff)
1215 new_params.compressionParams.largeStep = val;
1216 else
1217 retval = -EINVAL;
1218 }
1219 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1220 } else if (MATCH("decimation_hysteresis")) {
1221 if (!retval)
1222 val = VALUE;
1223
1224 if (!retval) {
1225 if (val <= 0xff)
1226 new_params.compressionParams.decimationHysteresis = val;
1227 else
1228 retval = -EINVAL;
1229 }
1230 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1231 } else if (MATCH("fr_diff_step_thresh")) {
1232 if (!retval)
1233 val = VALUE;
1234
1235 if (!retval) {
1236 if (val <= 0xff)
1237 new_params.compressionParams.frDiffStepThresh = val;
1238 else
1239 retval = -EINVAL;
1240 }
1241 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1242 } else if (MATCH("q_diff_step_thresh")) {
1243 if (!retval)
1244 val = VALUE;
1245
1246 if (!retval) {
1247 if (val <= 0xff)
1248 new_params.compressionParams.qDiffStepThresh = val;
1249 else
1250 retval = -EINVAL;
1251 }
1252 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1253 } else if (MATCH("decimation_thresh_mod")) {
1254 if (!retval)
1255 val = VALUE;
1256
1257 if (!retval) {
1258 if (val <= 0xff)
1259 new_params.compressionParams.decimationThreshMod = val;
1260 else
1261 retval = -EINVAL;
1262 }
1263 command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
1264 } else if (MATCH("toplight")) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001265 if (!retval && MATCH("on"))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001266 new_params.qx3.toplight = 1;
1267 else if (!retval && MATCH("off"))
1268 new_params.qx3.toplight = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001269 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001270 retval = -EINVAL;
1271 command_flags |= COMMAND_SETLIGHTS;
1272 } else if (MATCH("bottomlight")) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001273 if (!retval && MATCH("on"))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001274 new_params.qx3.bottomlight = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001275 else if (!retval && MATCH("off"))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001276 new_params.qx3.bottomlight = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001277 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001278 retval = -EINVAL;
1279 command_flags |= COMMAND_SETLIGHTS;
1280 } else {
1281 DBG("No match found\n");
1282 retval = -EINVAL;
1283 }
1284
1285 if (!retval) {
1286 while (count && isspace(*buffer) && *buffer != '\n') {
1287 --count;
1288 ++buffer;
1289 }
1290 if (count) {
1291 if (*buffer == '\0' && count != 1)
1292 retval = -EINVAL;
1293 else if (*buffer != '\n' && *buffer != ';' &&
1294 *buffer != '\0')
1295 retval = -EINVAL;
1296 else {
1297 --count;
1298 ++buffer;
1299 }
1300 }
1301 }
1302 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001303#undef MATCH
Linus Torvalds1da177e2005-04-16 15:20:36 -07001304#undef VALUE
1305#undef FIRMWARE_VERSION
1306 if (!retval) {
1307 if (command_flags & COMMAND_SETCOLOURPARAMS) {
1308 /* Adjust cam->vp to reflect these changes */
1309 cam->vp.brightness =
1310 new_params.colourParams.brightness*65535/100;
1311 cam->vp.contrast =
1312 new_params.colourParams.contrast*65535/100;
1313 cam->vp.colour =
1314 new_params.colourParams.saturation*65535/100;
1315 }
1316 if((command_flags & COMMAND_SETEXPOSURE) &&
1317 new_params.exposure.expMode == 2)
1318 cam->exposure_status = EXPOSURE_NORMAL;
1319
1320 memcpy(&cam->params, &new_params, sizeof(struct cam_params));
1321 cam->mainsFreq = new_mains;
1322 cam->cmd_queue |= command_flags;
1323 retval = size;
1324 } else
1325 DBG("error: %d\n", retval);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001326
Ingo Molnar3593cab2006-02-07 06:49:14 -02001327 mutex_unlock(&cam->param_lock);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001328
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329out:
1330 free_page((unsigned long)page);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001331 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001332}
1333
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -03001334static const struct file_operations cpia_proc_fops = {
1335 .owner = THIS_MODULE,
1336 .open = cpia_proc_open,
1337 .read = seq_read,
1338 .llseek = seq_lseek,
1339 .release = single_release,
1340 .write = cpia_proc_write,
1341};
1342
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343static void create_proc_cpia_cam(struct cam_data *cam)
1344{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001345 struct proc_dir_entry *ent;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001346
Linus Torvalds1da177e2005-04-16 15:20:36 -07001347 if (!cpia_proc_root || !cam)
1348 return;
1349
Alexey Dobriyan7eca61e2009-11-22 23:02:02 -03001350 ent = proc_create_data(video_device_node_name(&cam->vdev),
1351 S_IRUGO|S_IWUSR, cpia_proc_root,
1352 &cpia_proc_fops, cam);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001353 if (!ent)
1354 return;
1355
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001356 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357 size of the proc entry is 3736 bytes for the standard webcam;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001358 the extra features of the QX3 microscope add 189 bytes.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359 (we have not yet probed the camera to see which type it is).
1360 */
1361 ent->size = 3736 + 189;
1362 cam->proc_entry = ent;
1363}
1364
1365static void destroy_proc_cpia_cam(struct cam_data *cam)
1366{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367 if (!cam || !cam->proc_entry)
1368 return;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001369
Laurent Pinchart38c7c032009-11-27 13:57:15 -03001370 remove_proc_entry(video_device_node_name(&cam->vdev), cpia_proc_root);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371 cam->proc_entry = NULL;
1372}
1373
1374static void proc_cpia_create(void)
1375{
Al Viro66600222005-09-28 22:32:57 +01001376 cpia_proc_root = proc_mkdir("cpia", NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377
Alexey Dobriyan99b76232009-03-25 22:48:06 +03001378 if (!cpia_proc_root)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379 LOG("Unable to initialise /proc/cpia\n");
1380}
1381
1382static void __exit proc_cpia_destroy(void)
1383{
1384 remove_proc_entry("cpia", NULL);
1385}
1386#endif /* CONFIG_PROC_FS */
1387
1388/* ----------------------- debug functions ---------------------- */
1389
1390#define printstatus(cam) \
1391 DBG("%02x %02x %02x %02x %02x %02x %02x %02x\n",\
1392 cam->params.status.systemState, cam->params.status.grabState, \
1393 cam->params.status.streamState, cam->params.status.fatalError, \
1394 cam->params.status.cmdError, cam->params.status.debugFlags, \
1395 cam->params.status.vpStatus, cam->params.status.errorCode);
1396
1397/* ----------------------- v4l helpers -------------------------- */
1398
1399/* supported frame palettes and depths */
1400static inline int valid_mode(u16 palette, u16 depth)
1401{
1402 if ((palette == VIDEO_PALETTE_YUV422 && depth == 16) ||
1403 (palette == VIDEO_PALETTE_YUYV && depth == 16))
1404 return 1;
1405
1406 if (colorspace_conv)
1407 return (palette == VIDEO_PALETTE_GREY && depth == 8) ||
1408 (palette == VIDEO_PALETTE_RGB555 && depth == 16) ||
1409 (palette == VIDEO_PALETTE_RGB565 && depth == 16) ||
1410 (palette == VIDEO_PALETTE_RGB24 && depth == 24) ||
1411 (palette == VIDEO_PALETTE_RGB32 && depth == 32) ||
1412 (palette == VIDEO_PALETTE_UYVY && depth == 16);
1413
1414 return 0;
1415}
1416
1417static int match_videosize( int width, int height )
1418{
1419 /* return the best match, where 'best' is as always
1420 * the largest that is not bigger than what is requested. */
1421 if (width>=352 && height>=288)
1422 return VIDEOSIZE_352_288; /* CIF */
1423
1424 if (width>=320 && height>=240)
1425 return VIDEOSIZE_320_240; /* SIF */
1426
1427 if (width>=288 && height>=216)
1428 return VIDEOSIZE_288_216;
1429
1430 if (width>=256 && height>=192)
1431 return VIDEOSIZE_256_192;
1432
1433 if (width>=224 && height>=168)
1434 return VIDEOSIZE_224_168;
1435
1436 if (width>=192 && height>=144)
1437 return VIDEOSIZE_192_144;
1438
1439 if (width>=176 && height>=144)
1440 return VIDEOSIZE_176_144; /* QCIF */
1441
1442 if (width>=160 && height>=120)
1443 return VIDEOSIZE_160_120; /* QSIF */
1444
1445 if (width>=128 && height>=96)
1446 return VIDEOSIZE_128_96;
1447
1448 if (width>=88 && height>=72)
1449 return VIDEOSIZE_88_72;
1450
1451 if (width>=64 && height>=48)
1452 return VIDEOSIZE_64_48;
1453
1454 if (width>=48 && height>=48)
1455 return VIDEOSIZE_48_48;
1456
1457 return -1;
1458}
1459
1460/* these are the capture sizes we support */
1461static void set_vw_size(struct cam_data *cam)
1462{
1463 /* the col/row/start/end values are the result of simple math */
1464 /* study the SetROI-command in cpia developers guide p 2-22 */
1465 /* streamStartLine is set to the recommended value in the cpia */
1466 /* developers guide p 3-37 */
1467 switch(cam->video_size) {
1468 case VIDEOSIZE_CIF:
1469 cam->vw.width = 352;
1470 cam->vw.height = 288;
1471 cam->params.format.videoSize=VIDEOSIZE_CIF;
1472 cam->params.roi.colStart=0;
1473 cam->params.roi.rowStart=0;
1474 cam->params.streamStartLine = 120;
1475 break;
1476 case VIDEOSIZE_SIF:
1477 cam->vw.width = 320;
1478 cam->vw.height = 240;
1479 cam->params.format.videoSize=VIDEOSIZE_CIF;
1480 cam->params.roi.colStart=2;
1481 cam->params.roi.rowStart=6;
1482 cam->params.streamStartLine = 120;
1483 break;
1484 case VIDEOSIZE_288_216:
1485 cam->vw.width = 288;
1486 cam->vw.height = 216;
1487 cam->params.format.videoSize=VIDEOSIZE_CIF;
1488 cam->params.roi.colStart=4;
1489 cam->params.roi.rowStart=9;
1490 cam->params.streamStartLine = 120;
1491 break;
1492 case VIDEOSIZE_256_192:
1493 cam->vw.width = 256;
1494 cam->vw.height = 192;
1495 cam->params.format.videoSize=VIDEOSIZE_CIF;
1496 cam->params.roi.colStart=6;
1497 cam->params.roi.rowStart=12;
1498 cam->params.streamStartLine = 120;
1499 break;
1500 case VIDEOSIZE_224_168:
1501 cam->vw.width = 224;
1502 cam->vw.height = 168;
1503 cam->params.format.videoSize=VIDEOSIZE_CIF;
1504 cam->params.roi.colStart=8;
1505 cam->params.roi.rowStart=15;
1506 cam->params.streamStartLine = 120;
1507 break;
1508 case VIDEOSIZE_192_144:
1509 cam->vw.width = 192;
1510 cam->vw.height = 144;
1511 cam->params.format.videoSize=VIDEOSIZE_CIF;
1512 cam->params.roi.colStart=10;
1513 cam->params.roi.rowStart=18;
1514 cam->params.streamStartLine = 120;
1515 break;
1516 case VIDEOSIZE_QCIF:
1517 cam->vw.width = 176;
1518 cam->vw.height = 144;
1519 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1520 cam->params.roi.colStart=0;
1521 cam->params.roi.rowStart=0;
1522 cam->params.streamStartLine = 60;
1523 break;
1524 case VIDEOSIZE_QSIF:
1525 cam->vw.width = 160;
1526 cam->vw.height = 120;
1527 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1528 cam->params.roi.colStart=1;
1529 cam->params.roi.rowStart=3;
1530 cam->params.streamStartLine = 60;
1531 break;
1532 case VIDEOSIZE_128_96:
1533 cam->vw.width = 128;
1534 cam->vw.height = 96;
1535 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1536 cam->params.roi.colStart=3;
1537 cam->params.roi.rowStart=6;
1538 cam->params.streamStartLine = 60;
1539 break;
1540 case VIDEOSIZE_88_72:
1541 cam->vw.width = 88;
1542 cam->vw.height = 72;
1543 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1544 cam->params.roi.colStart=5;
1545 cam->params.roi.rowStart=9;
1546 cam->params.streamStartLine = 60;
1547 break;
1548 case VIDEOSIZE_64_48:
1549 cam->vw.width = 64;
1550 cam->vw.height = 48;
1551 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1552 cam->params.roi.colStart=7;
1553 cam->params.roi.rowStart=12;
1554 cam->params.streamStartLine = 60;
1555 break;
1556 case VIDEOSIZE_48_48:
1557 cam->vw.width = 48;
1558 cam->vw.height = 48;
1559 cam->params.format.videoSize=VIDEOSIZE_QCIF;
1560 cam->params.roi.colStart=8;
1561 cam->params.roi.rowStart=6;
1562 cam->params.streamStartLine = 60;
1563 break;
1564 default:
1565 LOG("bad videosize value: %d\n", cam->video_size);
1566 return;
1567 }
1568
1569 if(cam->vc.width == 0)
1570 cam->vc.width = cam->vw.width;
1571 if(cam->vc.height == 0)
1572 cam->vc.height = cam->vw.height;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001573
Linus Torvalds1da177e2005-04-16 15:20:36 -07001574 cam->params.roi.colStart += cam->vc.x >> 3;
1575 cam->params.roi.colEnd = cam->params.roi.colStart +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001576 (cam->vc.width >> 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001577 cam->params.roi.rowStart += cam->vc.y >> 2;
1578 cam->params.roi.rowEnd = cam->params.roi.rowStart +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001579 (cam->vc.height >> 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001580
1581 return;
1582}
1583
1584static int allocate_frame_buf(struct cam_data *cam)
1585{
1586 int i;
1587
1588 cam->frame_buf = rvmalloc(FRAME_NUM * CPIA_MAX_FRAME_SIZE);
1589 if (!cam->frame_buf)
1590 return -ENOBUFS;
1591
1592 for (i = 0; i < FRAME_NUM; i++)
1593 cam->frame[i].data = cam->frame_buf + i * CPIA_MAX_FRAME_SIZE;
1594
1595 return 0;
1596}
1597
1598static int free_frame_buf(struct cam_data *cam)
1599{
1600 int i;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001601
Linus Torvalds1da177e2005-04-16 15:20:36 -07001602 rvfree(cam->frame_buf, FRAME_NUM*CPIA_MAX_FRAME_SIZE);
1603 cam->frame_buf = NULL;
1604 for (i=0; i < FRAME_NUM; i++)
1605 cam->frame[i].data = NULL;
1606
1607 return 0;
1608}
1609
1610
1611static inline void free_frames(struct cpia_frame frame[FRAME_NUM])
1612{
1613 int i;
1614
1615 for (i=0; i < FRAME_NUM; i++)
1616 frame[i].state = FRAME_UNUSED;
1617 return;
1618}
1619
1620/**********************************************************************
1621 *
1622 * General functions
1623 *
1624 **********************************************************************/
1625/* send an arbitrary command to the camera */
1626static int do_command(struct cam_data *cam, u16 command, u8 a, u8 b, u8 c, u8 d)
1627{
1628 int retval, datasize;
1629 u8 cmd[8], data[8];
1630
1631 switch(command) {
1632 case CPIA_COMMAND_GetCPIAVersion:
1633 case CPIA_COMMAND_GetPnPID:
1634 case CPIA_COMMAND_GetCameraStatus:
1635 case CPIA_COMMAND_GetVPVersion:
1636 datasize=8;
1637 break;
1638 case CPIA_COMMAND_GetColourParams:
1639 case CPIA_COMMAND_GetColourBalance:
1640 case CPIA_COMMAND_GetExposure:
Ingo Molnar3593cab2006-02-07 06:49:14 -02001641 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001642 datasize=8;
1643 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001644 case CPIA_COMMAND_ReadMCPorts:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001645 case CPIA_COMMAND_ReadVCRegs:
1646 datasize = 4;
1647 break;
1648 default:
1649 datasize=0;
1650 break;
1651 }
1652
1653 cmd[0] = command>>8;
1654 cmd[1] = command&0xff;
1655 cmd[2] = a;
1656 cmd[3] = b;
1657 cmd[4] = c;
1658 cmd[5] = d;
1659 cmd[6] = datasize;
1660 cmd[7] = 0;
1661
1662 retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
1663 if (retval) {
1664 DBG("%x - failed, retval=%d\n", command, retval);
1665 if (command == CPIA_COMMAND_GetColourParams ||
1666 command == CPIA_COMMAND_GetColourBalance ||
1667 command == CPIA_COMMAND_GetExposure)
Ingo Molnar3593cab2006-02-07 06:49:14 -02001668 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001669 } else {
1670 switch(command) {
1671 case CPIA_COMMAND_GetCPIAVersion:
1672 cam->params.version.firmwareVersion = data[0];
1673 cam->params.version.firmwareRevision = data[1];
1674 cam->params.version.vcVersion = data[2];
1675 cam->params.version.vcRevision = data[3];
1676 break;
1677 case CPIA_COMMAND_GetPnPID:
1678 cam->params.pnpID.vendor = data[0]+(((u16)data[1])<<8);
1679 cam->params.pnpID.product = data[2]+(((u16)data[3])<<8);
1680 cam->params.pnpID.deviceRevision =
1681 data[4]+(((u16)data[5])<<8);
1682 break;
1683 case CPIA_COMMAND_GetCameraStatus:
1684 cam->params.status.systemState = data[0];
1685 cam->params.status.grabState = data[1];
1686 cam->params.status.streamState = data[2];
1687 cam->params.status.fatalError = data[3];
1688 cam->params.status.cmdError = data[4];
1689 cam->params.status.debugFlags = data[5];
1690 cam->params.status.vpStatus = data[6];
1691 cam->params.status.errorCode = data[7];
1692 break;
1693 case CPIA_COMMAND_GetVPVersion:
1694 cam->params.vpVersion.vpVersion = data[0];
1695 cam->params.vpVersion.vpRevision = data[1];
1696 cam->params.vpVersion.cameraHeadID =
1697 data[2]+(((u16)data[3])<<8);
1698 break;
1699 case CPIA_COMMAND_GetColourParams:
1700 cam->params.colourParams.brightness = data[0];
1701 cam->params.colourParams.contrast = data[1];
1702 cam->params.colourParams.saturation = data[2];
Ingo Molnar3593cab2006-02-07 06:49:14 -02001703 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001704 break;
1705 case CPIA_COMMAND_GetColourBalance:
1706 cam->params.colourBalance.redGain = data[0];
1707 cam->params.colourBalance.greenGain = data[1];
1708 cam->params.colourBalance.blueGain = data[2];
Ingo Molnar3593cab2006-02-07 06:49:14 -02001709 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001710 break;
1711 case CPIA_COMMAND_GetExposure:
1712 cam->params.exposure.gain = data[0];
1713 cam->params.exposure.fineExp = data[1];
1714 cam->params.exposure.coarseExpLo = data[2];
1715 cam->params.exposure.coarseExpHi = data[3];
1716 cam->params.exposure.redComp = data[4];
1717 cam->params.exposure.green1Comp = data[5];
1718 cam->params.exposure.green2Comp = data[6];
1719 cam->params.exposure.blueComp = data[7];
Ingo Molnar3593cab2006-02-07 06:49:14 -02001720 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001721 break;
1722
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001723 case CPIA_COMMAND_ReadMCPorts:
1724 if (!cam->params.qx3.qx3_detected)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001725 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001726 /* test button press */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001727 cam->params.qx3.button = ((data[1] & 0x02) == 0);
1728 if (cam->params.qx3.button) {
1729 /* button pressed - unlock the latch */
1730 do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xDF,0xDF,0);
1731 do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xFF,0xFF,0);
1732 }
1733
1734 /* test whether microscope is cradled */
1735 cam->params.qx3.cradled = ((data[2] & 0x40) == 0);
1736 break;
1737
1738 default:
1739 break;
1740 }
1741 }
1742 return retval;
1743}
1744
1745/* send a command to the camera with an additional data transaction */
1746static int do_command_extended(struct cam_data *cam, u16 command,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001747 u8 a, u8 b, u8 c, u8 d,
1748 u8 e, u8 f, u8 g, u8 h,
1749 u8 i, u8 j, u8 k, u8 l)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001750{
1751 int retval;
1752 u8 cmd[8], data[8];
1753
1754 cmd[0] = command>>8;
1755 cmd[1] = command&0xff;
1756 cmd[2] = a;
1757 cmd[3] = b;
1758 cmd[4] = c;
1759 cmd[5] = d;
1760 cmd[6] = 8;
1761 cmd[7] = 0;
1762 data[0] = e;
1763 data[1] = f;
1764 data[2] = g;
1765 data[3] = h;
1766 data[4] = i;
1767 data[5] = j;
1768 data[6] = k;
1769 data[7] = l;
1770
1771 retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
1772 if (retval)
1773 DBG("%x - failed\n", command);
1774
1775 return retval;
1776}
1777
1778/**********************************************************************
1779 *
1780 * Colorspace conversion
1781 *
1782 **********************************************************************/
1783#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16)
1784
1785static int convert420(unsigned char *yuv, unsigned char *rgb, int out_fmt,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001786 int linesize, int mmap_kludge)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001787{
1788 int y, u, v, r, g, b, y1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001789
Linus Torvalds1da177e2005-04-16 15:20:36 -07001790 /* Odd lines use the same u and v as the previous line.
1791 * Because of compression, it is necessary to get this
1792 * information from the decoded image. */
1793 switch(out_fmt) {
1794 case VIDEO_PALETTE_RGB555:
1795 y = (*yuv++ - 16) * 76310;
1796 y1 = (*yuv - 16) * 76310;
1797 r = ((*(rgb+1-linesize)) & 0x7c) << 1;
1798 g = ((*(rgb-linesize)) & 0xe0) >> 4 |
1799 ((*(rgb+1-linesize)) & 0x03) << 6;
1800 b = ((*(rgb-linesize)) & 0x1f) << 3;
1801 u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
1802 v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
1803 r = 104635 * v;
1804 g = -25690 * u - 53294 * v;
1805 b = 132278 * u;
1806 *rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3);
1807 *rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6);
1808 *rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3);
1809 *rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6);
1810 return 4;
1811 case VIDEO_PALETTE_RGB565:
1812 y = (*yuv++ - 16) * 76310;
1813 y1 = (*yuv - 16) * 76310;
1814 r = (*(rgb+1-linesize)) & 0xf8;
1815 g = ((*(rgb-linesize)) & 0xe0) >> 3 |
1816 ((*(rgb+1-linesize)) & 0x07) << 5;
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) & 0xfc) << 3) | (LIMIT(b+y) >> 3);
1824 *rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5);
1825 *rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3);
1826 *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5);
1827 return 4;
1828 break;
1829 case VIDEO_PALETTE_RGB24:
1830 case VIDEO_PALETTE_RGB32:
1831 y = (*yuv++ - 16) * 76310;
1832 y1 = (*yuv - 16) * 76310;
1833 if (mmap_kludge) {
1834 r = *(rgb+2-linesize);
1835 g = *(rgb+1-linesize);
1836 b = *(rgb-linesize);
1837 } else {
1838 r = *(rgb-linesize);
1839 g = *(rgb+1-linesize);
1840 b = *(rgb+2-linesize);
1841 }
1842 u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
1843 v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
1844 r = 104635 * v;
1845 g = -25690 * u + -53294 * v;
1846 b = 132278 * u;
1847 if (mmap_kludge) {
1848 *rgb++ = LIMIT(b+y);
1849 *rgb++ = LIMIT(g+y);
1850 *rgb++ = LIMIT(r+y);
1851 if(out_fmt == VIDEO_PALETTE_RGB32)
1852 rgb++;
1853 *rgb++ = LIMIT(b+y1);
1854 *rgb++ = LIMIT(g+y1);
1855 *rgb = LIMIT(r+y1);
1856 } else {
1857 *rgb++ = LIMIT(r+y);
1858 *rgb++ = LIMIT(g+y);
1859 *rgb++ = LIMIT(b+y);
1860 if(out_fmt == VIDEO_PALETTE_RGB32)
1861 rgb++;
1862 *rgb++ = LIMIT(r+y1);
1863 *rgb++ = LIMIT(g+y1);
1864 *rgb = LIMIT(b+y1);
1865 }
1866 if(out_fmt == VIDEO_PALETTE_RGB32)
1867 return 8;
1868 return 6;
1869 case VIDEO_PALETTE_YUV422:
1870 case VIDEO_PALETTE_YUYV:
1871 y = *yuv++;
1872 u = *(rgb+1-linesize);
1873 y1 = *yuv;
1874 v = *(rgb+3-linesize);
1875 *rgb++ = y;
1876 *rgb++ = u;
1877 *rgb++ = y1;
1878 *rgb = v;
1879 return 4;
1880 case VIDEO_PALETTE_UYVY:
1881 u = *(rgb-linesize);
1882 y = *yuv++;
1883 v = *(rgb+2-linesize);
1884 y1 = *yuv;
1885 *rgb++ = u;
1886 *rgb++ = y;
1887 *rgb++ = v;
1888 *rgb = y1;
1889 return 4;
1890 case VIDEO_PALETTE_GREY:
1891 *rgb++ = *yuv++;
1892 *rgb = *yuv;
1893 return 2;
1894 default:
1895 DBG("Empty: %d\n", out_fmt);
1896 return 0;
1897 }
1898}
1899
1900
1901static int yuvconvert(unsigned char *yuv, unsigned char *rgb, int out_fmt,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001902 int in_uyvy, int mmap_kludge)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001903{
1904 int y, u, v, r, g, b, y1;
1905
1906 switch(out_fmt) {
1907 case VIDEO_PALETTE_RGB555:
1908 case VIDEO_PALETTE_RGB565:
1909 case VIDEO_PALETTE_RGB24:
1910 case VIDEO_PALETTE_RGB32:
1911 if (in_uyvy) {
1912 u = *yuv++ - 128;
1913 y = (*yuv++ - 16) * 76310;
1914 v = *yuv++ - 128;
1915 y1 = (*yuv - 16) * 76310;
1916 } else {
1917 y = (*yuv++ - 16) * 76310;
1918 u = *yuv++ - 128;
1919 y1 = (*yuv++ - 16) * 76310;
1920 v = *yuv - 128;
1921 }
1922 r = 104635 * v;
1923 g = -25690 * u + -53294 * v;
1924 b = 132278 * u;
1925 break;
1926 default:
1927 y = *yuv++;
1928 u = *yuv++;
1929 y1 = *yuv++;
1930 v = *yuv;
1931 /* Just to avoid compiler warnings */
1932 r = 0;
1933 g = 0;
1934 b = 0;
1935 break;
1936 }
1937 switch(out_fmt) {
1938 case VIDEO_PALETTE_RGB555:
1939 *rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3);
1940 *rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6);
1941 *rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3);
1942 *rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6);
1943 return 4;
1944 case VIDEO_PALETTE_RGB565:
1945 *rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3);
1946 *rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5);
1947 *rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3);
1948 *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5);
1949 return 4;
1950 case VIDEO_PALETTE_RGB24:
1951 if (mmap_kludge) {
1952 *rgb++ = LIMIT(b+y);
1953 *rgb++ = LIMIT(g+y);
1954 *rgb++ = LIMIT(r+y);
1955 *rgb++ = LIMIT(b+y1);
1956 *rgb++ = LIMIT(g+y1);
1957 *rgb = LIMIT(r+y1);
1958 } else {
1959 *rgb++ = LIMIT(r+y);
1960 *rgb++ = LIMIT(g+y);
1961 *rgb++ = LIMIT(b+y);
1962 *rgb++ = LIMIT(r+y1);
1963 *rgb++ = LIMIT(g+y1);
1964 *rgb = LIMIT(b+y1);
1965 }
1966 return 6;
1967 case VIDEO_PALETTE_RGB32:
1968 if (mmap_kludge) {
1969 *rgb++ = LIMIT(b+y);
1970 *rgb++ = LIMIT(g+y);
1971 *rgb++ = LIMIT(r+y);
1972 rgb++;
1973 *rgb++ = LIMIT(b+y1);
1974 *rgb++ = LIMIT(g+y1);
1975 *rgb = LIMIT(r+y1);
1976 } else {
1977 *rgb++ = LIMIT(r+y);
1978 *rgb++ = LIMIT(g+y);
1979 *rgb++ = LIMIT(b+y);
1980 rgb++;
1981 *rgb++ = LIMIT(r+y1);
1982 *rgb++ = LIMIT(g+y1);
1983 *rgb = LIMIT(b+y1);
1984 }
1985 return 8;
1986 case VIDEO_PALETTE_GREY:
1987 *rgb++ = y;
1988 *rgb = y1;
1989 return 2;
1990 case VIDEO_PALETTE_YUV422:
1991 case VIDEO_PALETTE_YUYV:
1992 *rgb++ = y;
1993 *rgb++ = u;
1994 *rgb++ = y1;
1995 *rgb = v;
1996 return 4;
1997 case VIDEO_PALETTE_UYVY:
1998 *rgb++ = u;
1999 *rgb++ = y;
2000 *rgb++ = v;
2001 *rgb = y1;
2002 return 4;
2003 default:
2004 DBG("Empty: %d\n", out_fmt);
2005 return 0;
2006 }
2007}
2008
2009static int skipcount(int count, int fmt)
2010{
2011 switch(fmt) {
2012 case VIDEO_PALETTE_GREY:
2013 return count;
2014 case VIDEO_PALETTE_RGB555:
2015 case VIDEO_PALETTE_RGB565:
2016 case VIDEO_PALETTE_YUV422:
2017 case VIDEO_PALETTE_YUYV:
2018 case VIDEO_PALETTE_UYVY:
2019 return 2*count;
2020 case VIDEO_PALETTE_RGB24:
2021 return 3*count;
2022 case VIDEO_PALETTE_RGB32:
2023 return 4*count;
2024 default:
2025 return 0;
2026 }
2027}
2028
2029static int parse_picture(struct cam_data *cam, int size)
2030{
2031 u8 *obuf, *ibuf, *end_obuf;
2032 int ll, in_uyvy, compressed, decimation, even_line, origsize, out_fmt;
2033 int rows, cols, linesize, subsample_422;
2034
2035 /* make sure params don't change while we are decoding */
Ingo Molnar3593cab2006-02-07 06:49:14 -02002036 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002037
2038 obuf = cam->decompressed_frame.data;
2039 end_obuf = obuf+CPIA_MAX_FRAME_SIZE;
2040 ibuf = cam->raw_image;
2041 origsize = size;
2042 out_fmt = cam->vp.palette;
2043
2044 if ((ibuf[0] != MAGIC_0) || (ibuf[1] != MAGIC_1)) {
2045 LOG("header not found\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02002046 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002047 return -1;
2048 }
2049
2050 if ((ibuf[16] != VIDEOSIZE_QCIF) && (ibuf[16] != VIDEOSIZE_CIF)) {
2051 LOG("wrong video size\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02002052 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002053 return -1;
2054 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002055
Linus Torvalds1da177e2005-04-16 15:20:36 -07002056 if (ibuf[17] != SUBSAMPLE_420 && ibuf[17] != SUBSAMPLE_422) {
2057 LOG("illegal subtype %d\n",ibuf[17]);
Ingo Molnar3593cab2006-02-07 06:49:14 -02002058 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002059 return -1;
2060 }
2061 subsample_422 = ibuf[17] == SUBSAMPLE_422;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002062
Linus Torvalds1da177e2005-04-16 15:20:36 -07002063 if (ibuf[18] != YUVORDER_YUYV && ibuf[18] != YUVORDER_UYVY) {
2064 LOG("illegal yuvorder %d\n",ibuf[18]);
Ingo Molnar3593cab2006-02-07 06:49:14 -02002065 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002066 return -1;
2067 }
2068 in_uyvy = ibuf[18] == YUVORDER_UYVY;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002069
Linus Torvalds1da177e2005-04-16 15:20:36 -07002070 if ((ibuf[24] != cam->params.roi.colStart) ||
2071 (ibuf[25] != cam->params.roi.colEnd) ||
2072 (ibuf[26] != cam->params.roi.rowStart) ||
2073 (ibuf[27] != cam->params.roi.rowEnd)) {
2074 LOG("ROI mismatch\n");
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 cols = 8*(ibuf[25] - ibuf[24]);
2079 rows = 4*(ibuf[27] - ibuf[26]);
2080
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002081
Linus Torvalds1da177e2005-04-16 15:20:36 -07002082 if ((ibuf[28] != NOT_COMPRESSED) && (ibuf[28] != COMPRESSED)) {
2083 LOG("illegal compression %d\n",ibuf[28]);
Ingo Molnar3593cab2006-02-07 06:49:14 -02002084 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002085 return -1;
2086 }
2087 compressed = (ibuf[28] == COMPRESSED);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002088
Linus Torvalds1da177e2005-04-16 15:20:36 -07002089 if (ibuf[29] != NO_DECIMATION && ibuf[29] != DECIMATION_ENAB) {
2090 LOG("illegal decimation %d\n",ibuf[29]);
Ingo Molnar3593cab2006-02-07 06:49:14 -02002091 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002092 return -1;
2093 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002094 decimation = (ibuf[29] == DECIMATION_ENAB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002095
2096 cam->params.yuvThreshold.yThreshold = ibuf[30];
2097 cam->params.yuvThreshold.uvThreshold = ibuf[31];
2098 cam->params.status.systemState = ibuf[32];
2099 cam->params.status.grabState = ibuf[33];
2100 cam->params.status.streamState = ibuf[34];
2101 cam->params.status.fatalError = ibuf[35];
2102 cam->params.status.cmdError = ibuf[36];
2103 cam->params.status.debugFlags = ibuf[37];
2104 cam->params.status.vpStatus = ibuf[38];
2105 cam->params.status.errorCode = ibuf[39];
2106 cam->fps = ibuf[41];
Ingo Molnar3593cab2006-02-07 06:49:14 -02002107 mutex_unlock(&cam->param_lock);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002108
Linus Torvalds1da177e2005-04-16 15:20:36 -07002109 linesize = skipcount(cols, out_fmt);
2110 ibuf += FRAME_HEADER_SIZE;
2111 size -= FRAME_HEADER_SIZE;
2112 ll = ibuf[0] | (ibuf[1] << 8);
2113 ibuf += 2;
2114 even_line = 1;
2115
2116 while (size > 0) {
2117 size -= (ll+2);
2118 if (size < 0) {
2119 LOG("Insufficient data in buffer\n");
2120 return -1;
2121 }
2122
2123 while (ll > 1) {
2124 if (!compressed || (compressed && !(*ibuf & 1))) {
2125 if(subsample_422 || even_line) {
2126 obuf += yuvconvert(ibuf, obuf, out_fmt,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002127 in_uyvy, cam->mmap_kludge);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002128 ibuf += 4;
2129 ll -= 4;
2130 } else {
2131 /* SUBSAMPLE_420 on an odd line */
2132 obuf += convert420(ibuf, obuf,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002133 out_fmt, linesize,
2134 cam->mmap_kludge);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002135 ibuf += 2;
2136 ll -= 2;
2137 }
2138 } else {
2139 /*skip compressed interval from previous frame*/
2140 obuf += skipcount(*ibuf >> 1, out_fmt);
2141 if (obuf > end_obuf) {
2142 LOG("Insufficient buffer size\n");
2143 return -1;
2144 }
2145 ++ibuf;
2146 ll--;
2147 }
2148 }
2149 if (ll == 1) {
2150 if (*ibuf != EOL) {
2151 DBG("EOL not found giving up after %d/%d"
2152 " bytes\n", origsize-size, origsize);
2153 return -1;
2154 }
2155
2156 ++ibuf; /* skip over EOL */
2157
2158 if ((size > 3) && (ibuf[0] == EOI) && (ibuf[1] == EOI) &&
2159 (ibuf[2] == EOI) && (ibuf[3] == EOI)) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002160 size -= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002161 break;
2162 }
2163
2164 if(decimation) {
2165 /* skip the odd lines for now */
2166 obuf += linesize;
2167 }
2168
2169 if (size > 1) {
2170 ll = ibuf[0] | (ibuf[1] << 8);
2171 ibuf += 2; /* skip over line length */
2172 }
2173 if(!decimation)
2174 even_line = !even_line;
2175 } else {
2176 LOG("line length was not 1 but %d after %d/%d bytes\n",
2177 ll, origsize-size, origsize);
2178 return -1;
2179 }
2180 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002181
Linus Torvalds1da177e2005-04-16 15:20:36 -07002182 if(decimation) {
2183 /* interpolate odd rows */
2184 int i, j;
2185 u8 *prev, *next;
2186 prev = cam->decompressed_frame.data;
2187 obuf = prev+linesize;
2188 next = obuf+linesize;
2189 for(i=1; i<rows-1; i+=2) {
2190 for(j=0; j<linesize; ++j) {
2191 *obuf++ = ((int)*prev++ + *next++) / 2;
2192 }
2193 prev += linesize;
2194 obuf += linesize;
2195 next += linesize;
2196 }
2197 /* last row is odd, just copy previous row */
2198 memcpy(obuf, prev, linesize);
2199 }
2200
2201 cam->decompressed_frame.count = obuf-cam->decompressed_frame.data;
2202
2203 return cam->decompressed_frame.count;
2204}
2205
2206/* InitStreamCap wrapper to select correct start line */
2207static inline int init_stream_cap(struct cam_data *cam)
2208{
2209 return do_command(cam, CPIA_COMMAND_InitStreamCap,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002210 0, cam->params.streamStartLine, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002211}
2212
2213
2214/* find_over_exposure
2215 * Finds a suitable value of OverExposure for use with SetFlickerCtrl
2216 * Some calculation is required because this value changes with the brightness
2217 * set with SetColourParameters
2218 *
2219 * Parameters: Brightness - last brightness value set with SetColourParameters
2220 *
2221 * Returns: OverExposure value to use with SetFlickerCtrl
2222 */
2223#define FLICKER_MAX_EXPOSURE 250
2224#define FLICKER_ALLOWABLE_OVER_EXPOSURE 146
2225#define FLICKER_BRIGHTNESS_CONSTANT 59
2226static int find_over_exposure(int brightness)
2227{
2228 int MaxAllowableOverExposure, OverExposure;
2229
2230 MaxAllowableOverExposure = FLICKER_MAX_EXPOSURE - brightness -
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002231 FLICKER_BRIGHTNESS_CONSTANT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002232
2233 if (MaxAllowableOverExposure < FLICKER_ALLOWABLE_OVER_EXPOSURE) {
2234 OverExposure = MaxAllowableOverExposure;
2235 } else {
2236 OverExposure = FLICKER_ALLOWABLE_OVER_EXPOSURE;
2237 }
2238
2239 return OverExposure;
2240}
2241#undef FLICKER_MAX_EXPOSURE
2242#undef FLICKER_ALLOWABLE_OVER_EXPOSURE
2243#undef FLICKER_BRIGHTNESS_CONSTANT
2244
2245/* update various camera modes and settings */
2246static void dispatch_commands(struct cam_data *cam)
2247{
Ingo Molnar3593cab2006-02-07 06:49:14 -02002248 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002249 if (cam->cmd_queue==COMMAND_NONE) {
Ingo Molnar3593cab2006-02-07 06:49:14 -02002250 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002251 return;
2252 }
2253 DEB_BYTE(cam->cmd_queue);
2254 DEB_BYTE(cam->cmd_queue>>8);
2255 if (cam->cmd_queue & COMMAND_SETFORMAT) {
2256 do_command(cam, CPIA_COMMAND_SetFormat,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002257 cam->params.format.videoSize,
2258 cam->params.format.subSample,
2259 cam->params.format.yuvOrder, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002260 do_command(cam, CPIA_COMMAND_SetROI,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002261 cam->params.roi.colStart, cam->params.roi.colEnd,
2262 cam->params.roi.rowStart, cam->params.roi.rowEnd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002263 cam->first_frame = 1;
2264 }
2265
2266 if (cam->cmd_queue & COMMAND_SETCOLOURPARAMS)
2267 do_command(cam, CPIA_COMMAND_SetColourParams,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002268 cam->params.colourParams.brightness,
2269 cam->params.colourParams.contrast,
2270 cam->params.colourParams.saturation, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002271
2272 if (cam->cmd_queue & COMMAND_SETAPCOR)
2273 do_command(cam, CPIA_COMMAND_SetApcor,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002274 cam->params.apcor.gain1,
2275 cam->params.apcor.gain2,
2276 cam->params.apcor.gain4,
2277 cam->params.apcor.gain8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002278
2279 if (cam->cmd_queue & COMMAND_SETVLOFFSET)
2280 do_command(cam, CPIA_COMMAND_SetVLOffset,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002281 cam->params.vlOffset.gain1,
2282 cam->params.vlOffset.gain2,
2283 cam->params.vlOffset.gain4,
2284 cam->params.vlOffset.gain8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002285
2286 if (cam->cmd_queue & COMMAND_SETEXPOSURE) {
2287 do_command_extended(cam, CPIA_COMMAND_SetExposure,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002288 cam->params.exposure.gainMode,
2289 1,
2290 cam->params.exposure.compMode,
2291 cam->params.exposure.centreWeight,
2292 cam->params.exposure.gain,
2293 cam->params.exposure.fineExp,
2294 cam->params.exposure.coarseExpLo,
2295 cam->params.exposure.coarseExpHi,
2296 cam->params.exposure.redComp,
2297 cam->params.exposure.green1Comp,
2298 cam->params.exposure.green2Comp,
2299 cam->params.exposure.blueComp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002300 if(cam->params.exposure.expMode != 1) {
2301 do_command_extended(cam, CPIA_COMMAND_SetExposure,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002302 0,
2303 cam->params.exposure.expMode,
2304 0, 0,
2305 cam->params.exposure.gain,
2306 cam->params.exposure.fineExp,
2307 cam->params.exposure.coarseExpLo,
2308 cam->params.exposure.coarseExpHi,
2309 0, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002310 }
2311 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002312
Linus Torvalds1da177e2005-04-16 15:20:36 -07002313 if (cam->cmd_queue & COMMAND_SETCOLOURBALANCE) {
2314 if (cam->params.colourBalance.balanceMode == 1) {
2315 do_command(cam, CPIA_COMMAND_SetColourBalance,
2316 1,
2317 cam->params.colourBalance.redGain,
2318 cam->params.colourBalance.greenGain,
2319 cam->params.colourBalance.blueGain);
2320 do_command(cam, CPIA_COMMAND_SetColourBalance,
2321 3, 0, 0, 0);
2322 }
2323 if (cam->params.colourBalance.balanceMode == 2) {
2324 do_command(cam, CPIA_COMMAND_SetColourBalance,
2325 2, 0, 0, 0);
2326 }
2327 if (cam->params.colourBalance.balanceMode == 3) {
2328 do_command(cam, CPIA_COMMAND_SetColourBalance,
2329 3, 0, 0, 0);
2330 }
2331 }
2332
2333 if (cam->cmd_queue & COMMAND_SETCOMPRESSIONTARGET)
2334 do_command(cam, CPIA_COMMAND_SetCompressionTarget,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002335 cam->params.compressionTarget.frTargeting,
2336 cam->params.compressionTarget.targetFR,
2337 cam->params.compressionTarget.targetQ, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002338
2339 if (cam->cmd_queue & COMMAND_SETYUVTHRESH)
2340 do_command(cam, CPIA_COMMAND_SetYUVThresh,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002341 cam->params.yuvThreshold.yThreshold,
2342 cam->params.yuvThreshold.uvThreshold, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002343
2344 if (cam->cmd_queue & COMMAND_SETCOMPRESSIONPARAMS)
2345 do_command_extended(cam, CPIA_COMMAND_SetCompressionParams,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002346 0, 0, 0, 0,
2347 cam->params.compressionParams.hysteresis,
2348 cam->params.compressionParams.threshMax,
2349 cam->params.compressionParams.smallStep,
2350 cam->params.compressionParams.largeStep,
2351 cam->params.compressionParams.decimationHysteresis,
2352 cam->params.compressionParams.frDiffStepThresh,
2353 cam->params.compressionParams.qDiffStepThresh,
2354 cam->params.compressionParams.decimationThreshMod);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002355
2356 if (cam->cmd_queue & COMMAND_SETCOMPRESSION)
2357 do_command(cam, CPIA_COMMAND_SetCompression,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002358 cam->params.compression.mode,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002359 cam->params.compression.decimation, 0, 0);
2360
2361 if (cam->cmd_queue & COMMAND_SETSENSORFPS)
2362 do_command(cam, CPIA_COMMAND_SetSensorFPS,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002363 cam->params.sensorFps.divisor,
2364 cam->params.sensorFps.baserate, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002365
2366 if (cam->cmd_queue & COMMAND_SETFLICKERCTRL)
2367 do_command(cam, CPIA_COMMAND_SetFlickerCtrl,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002368 cam->params.flickerControl.flickerMode,
2369 cam->params.flickerControl.coarseJump,
2370 abs(cam->params.flickerControl.allowableOverExposure),
2371 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002372
2373 if (cam->cmd_queue & COMMAND_SETECPTIMING)
2374 do_command(cam, CPIA_COMMAND_SetECPTiming,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002375 cam->params.ecpTiming, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002376
2377 if (cam->cmd_queue & COMMAND_PAUSE)
2378 do_command(cam, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0);
2379
2380 if (cam->cmd_queue & COMMAND_RESUME)
2381 init_stream_cap(cam);
2382
2383 if (cam->cmd_queue & COMMAND_SETLIGHTS && cam->params.qx3.qx3_detected)
2384 {
2385 int p1 = (cam->params.qx3.bottomlight == 0) << 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002386 int p2 = (cam->params.qx3.toplight == 0) << 3;
2387 do_command(cam, CPIA_COMMAND_WriteVCReg, 0x90, 0x8F, 0x50, 0);
2388 do_command(cam, CPIA_COMMAND_WriteMCPort, 2, 0, (p1|p2|0xE0), 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002389 }
2390
2391 cam->cmd_queue = COMMAND_NONE;
Ingo Molnar3593cab2006-02-07 06:49:14 -02002392 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002393 return;
2394}
2395
2396
2397
2398static void set_flicker(struct cam_params *params, volatile u32 *command_flags,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002399 int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002400{
2401 /* Everything in here is from the Windows driver */
2402#define FIRMWARE_VERSION(x,y) (params->version.firmwareVersion == (x) && \
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002403 params->version.firmwareRevision == (y))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002404/* define for compgain calculation */
2405#if 0
2406#define COMPGAIN(base, curexp, newexp) \
2407 (u8) ((((float) base - 128.0) * ((float) curexp / (float) newexp)) + 128.5)
2408#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
2409 (u16)((float)curexp * (float)(u8)(curcomp + 128) / (float)(u8)(basecomp - 128))
2410#else
2411 /* equivalent functions without floating point math */
2412#define COMPGAIN(base, curexp, newexp) \
2413 (u8)(128 + (((u32)(2*(base-128)*curexp + newexp)) / (2* newexp)) )
2414#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
2415 (u16)(((u32)(curexp * (u8)(curcomp + 128)) / (u8)(basecomp - 128)))
2416#endif
2417
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002418
Linus Torvalds1da177e2005-04-16 15:20:36 -07002419 int currentexp = params->exposure.coarseExpLo +
2420 params->exposure.coarseExpHi*256;
2421 int startexp;
2422 if (on) {
2423 int cj = params->flickerControl.coarseJump;
2424 params->flickerControl.flickerMode = 1;
2425 params->flickerControl.disabled = 0;
2426 if(params->exposure.expMode != 2)
2427 *command_flags |= COMMAND_SETEXPOSURE;
2428 params->exposure.expMode = 2;
2429 currentexp = currentexp << params->exposure.gain;
2430 params->exposure.gain = 0;
2431 /* round down current exposure to nearest value */
2432 startexp = (currentexp + ROUND_UP_EXP_FOR_FLICKER) / cj;
2433 if(startexp < 1)
2434 startexp = 1;
2435 startexp = (startexp * cj) - 1;
2436 if(FIRMWARE_VERSION(1,2))
2437 while(startexp > MAX_EXP_102)
2438 startexp -= cj;
2439 else
2440 while(startexp > MAX_EXP)
2441 startexp -= cj;
2442 params->exposure.coarseExpLo = startexp & 0xff;
2443 params->exposure.coarseExpHi = startexp >> 8;
2444 if (currentexp > startexp) {
2445 if (currentexp > (2 * startexp))
2446 currentexp = 2 * startexp;
2447 params->exposure.redComp = COMPGAIN (COMP_RED, currentexp, startexp);
2448 params->exposure.green1Comp = COMPGAIN (COMP_GREEN1, currentexp, startexp);
2449 params->exposure.green2Comp = COMPGAIN (COMP_GREEN2, currentexp, startexp);
2450 params->exposure.blueComp = COMPGAIN (COMP_BLUE, currentexp, startexp);
2451 } else {
2452 params->exposure.redComp = COMP_RED;
2453 params->exposure.green1Comp = COMP_GREEN1;
2454 params->exposure.green2Comp = COMP_GREEN2;
2455 params->exposure.blueComp = COMP_BLUE;
2456 }
2457 if(FIRMWARE_VERSION(1,2))
2458 params->exposure.compMode = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002459 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07002460 params->exposure.compMode = 1;
2461
2462 params->apcor.gain1 = 0x18;
2463 params->apcor.gain2 = 0x18;
2464 params->apcor.gain4 = 0x16;
2465 params->apcor.gain8 = 0x14;
2466 *command_flags |= COMMAND_SETAPCOR;
2467 } else {
2468 params->flickerControl.flickerMode = 0;
2469 params->flickerControl.disabled = 1;
2470 /* Coarse = average of equivalent coarse for each comp channel */
2471 startexp = EXP_FROM_COMP(COMP_RED, params->exposure.redComp, currentexp);
2472 startexp += EXP_FROM_COMP(COMP_GREEN1, params->exposure.green1Comp, currentexp);
2473 startexp += EXP_FROM_COMP(COMP_GREEN2, params->exposure.green2Comp, currentexp);
2474 startexp += EXP_FROM_COMP(COMP_BLUE, params->exposure.blueComp, currentexp);
2475 startexp = startexp >> 2;
2476 while(startexp > MAX_EXP &&
2477 params->exposure.gain < params->exposure.gainMode-1) {
2478 startexp = startexp >> 1;
2479 ++params->exposure.gain;
2480 }
2481 if(FIRMWARE_VERSION(1,2) && startexp > MAX_EXP_102)
2482 startexp = MAX_EXP_102;
2483 if(startexp > MAX_EXP)
2484 startexp = MAX_EXP;
2485 params->exposure.coarseExpLo = startexp&0xff;
2486 params->exposure.coarseExpHi = startexp >> 8;
2487 params->exposure.redComp = COMP_RED;
2488 params->exposure.green1Comp = COMP_GREEN1;
2489 params->exposure.green2Comp = COMP_GREEN2;
2490 params->exposure.blueComp = COMP_BLUE;
2491 params->exposure.compMode = 1;
2492 *command_flags |= COMMAND_SETEXPOSURE;
2493 params->apcor.gain1 = 0x18;
2494 params->apcor.gain2 = 0x16;
2495 params->apcor.gain4 = 0x24;
2496 params->apcor.gain8 = 0x34;
2497 *command_flags |= COMMAND_SETAPCOR;
2498 }
2499 params->vlOffset.gain1 = 20;
2500 params->vlOffset.gain2 = 24;
2501 params->vlOffset.gain4 = 26;
2502 params->vlOffset.gain8 = 26;
2503 *command_flags |= COMMAND_SETVLOFFSET;
2504#undef FIRMWARE_VERSION
2505#undef EXP_FROM_COMP
2506#undef COMPGAIN
2507}
2508
2509#define FIRMWARE_VERSION(x,y) (cam->params.version.firmwareVersion == (x) && \
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002510 cam->params.version.firmwareRevision == (y))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002511/* monitor the exposure and adjust the sensor frame rate if needed */
2512static void monitor_exposure(struct cam_data *cam)
2513{
2514 u8 exp_acc, bcomp, gain, coarseL, cmd[8], data[8];
2515 int retval, light_exp, dark_exp, very_dark_exp;
2516 int old_exposure, new_exposure, framerate;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002517
Linus Torvalds1da177e2005-04-16 15:20:36 -07002518 /* get necessary stats and register settings from camera */
2519 /* do_command can't handle this, so do it ourselves */
2520 cmd[0] = CPIA_COMMAND_ReadVPRegs>>8;
2521 cmd[1] = CPIA_COMMAND_ReadVPRegs&0xff;
2522 cmd[2] = 30;
2523 cmd[3] = 4;
2524 cmd[4] = 9;
2525 cmd[5] = 8;
2526 cmd[6] = 8;
2527 cmd[7] = 0;
2528 retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
2529 if (retval) {
2530 LOG("ReadVPRegs(30,4,9,8) - failed, retval=%d\n",
2531 retval);
2532 return;
2533 }
2534 exp_acc = data[0];
2535 bcomp = data[1];
2536 gain = data[2];
2537 coarseL = data[3];
2538
Ingo Molnar3593cab2006-02-07 06:49:14 -02002539 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002540 light_exp = cam->params.colourParams.brightness +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002541 TC - 50 + EXP_ACC_LIGHT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002542 if(light_exp > 255)
2543 light_exp = 255;
2544 dark_exp = cam->params.colourParams.brightness +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002545 TC - 50 - EXP_ACC_DARK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002546 if(dark_exp < 0)
2547 dark_exp = 0;
2548 very_dark_exp = dark_exp/2;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002549
Linus Torvalds1da177e2005-04-16 15:20:36 -07002550 old_exposure = cam->params.exposure.coarseExpHi * 256 +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002551 cam->params.exposure.coarseExpLo;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002552
2553 if(!cam->params.flickerControl.disabled) {
2554 /* Flicker control on */
2555 int max_comp = FIRMWARE_VERSION(1,2) ? MAX_COMP : HIGH_COMP_102;
2556 bcomp += 128; /* decode */
2557 if(bcomp >= max_comp && exp_acc < dark_exp) {
2558 /* dark */
2559 if(exp_acc < very_dark_exp) {
2560 /* very dark */
2561 if(cam->exposure_status == EXPOSURE_VERY_DARK)
2562 ++cam->exposure_count;
2563 else {
2564 cam->exposure_status = EXPOSURE_VERY_DARK;
2565 cam->exposure_count = 1;
2566 }
2567 } else {
2568 /* just dark */
2569 if(cam->exposure_status == EXPOSURE_DARK)
2570 ++cam->exposure_count;
2571 else {
2572 cam->exposure_status = EXPOSURE_DARK;
2573 cam->exposure_count = 1;
2574 }
2575 }
2576 } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) {
2577 /* light */
2578 if(old_exposure <= VERY_LOW_EXP) {
2579 /* very light */
2580 if(cam->exposure_status == EXPOSURE_VERY_LIGHT)
2581 ++cam->exposure_count;
2582 else {
2583 cam->exposure_status = EXPOSURE_VERY_LIGHT;
2584 cam->exposure_count = 1;
2585 }
2586 } else {
2587 /* just light */
2588 if(cam->exposure_status == EXPOSURE_LIGHT)
2589 ++cam->exposure_count;
2590 else {
2591 cam->exposure_status = EXPOSURE_LIGHT;
2592 cam->exposure_count = 1;
2593 }
2594 }
2595 } else {
2596 /* not dark or light */
2597 cam->exposure_status = EXPOSURE_NORMAL;
2598 }
2599 } else {
2600 /* Flicker control off */
2601 if(old_exposure >= MAX_EXP && exp_acc < dark_exp) {
2602 /* dark */
2603 if(exp_acc < very_dark_exp) {
2604 /* very dark */
2605 if(cam->exposure_status == EXPOSURE_VERY_DARK)
2606 ++cam->exposure_count;
2607 else {
2608 cam->exposure_status = EXPOSURE_VERY_DARK;
2609 cam->exposure_count = 1;
2610 }
2611 } else {
2612 /* just dark */
2613 if(cam->exposure_status == EXPOSURE_DARK)
2614 ++cam->exposure_count;
2615 else {
2616 cam->exposure_status = EXPOSURE_DARK;
2617 cam->exposure_count = 1;
2618 }
2619 }
2620 } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) {
2621 /* light */
2622 if(old_exposure <= VERY_LOW_EXP) {
2623 /* very light */
2624 if(cam->exposure_status == EXPOSURE_VERY_LIGHT)
2625 ++cam->exposure_count;
2626 else {
2627 cam->exposure_status = EXPOSURE_VERY_LIGHT;
2628 cam->exposure_count = 1;
2629 }
2630 } else {
2631 /* just light */
2632 if(cam->exposure_status == EXPOSURE_LIGHT)
2633 ++cam->exposure_count;
2634 else {
2635 cam->exposure_status = EXPOSURE_LIGHT;
2636 cam->exposure_count = 1;
2637 }
2638 }
2639 } else {
2640 /* not dark or light */
2641 cam->exposure_status = EXPOSURE_NORMAL;
2642 }
2643 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002644
Linus Torvalds1da177e2005-04-16 15:20:36 -07002645 framerate = cam->fps;
2646 if(framerate > 30 || framerate < 1)
2647 framerate = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002648
Linus Torvalds1da177e2005-04-16 15:20:36 -07002649 if(!cam->params.flickerControl.disabled) {
2650 /* Flicker control on */
2651 if((cam->exposure_status == EXPOSURE_VERY_DARK ||
2652 cam->exposure_status == EXPOSURE_DARK) &&
2653 cam->exposure_count >= DARK_TIME*framerate &&
2654 cam->params.sensorFps.divisor < 3) {
2655
2656 /* dark for too long */
2657 ++cam->params.sensorFps.divisor;
2658 cam->cmd_queue |= COMMAND_SETSENSORFPS;
2659
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002660 cam->params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07002661 flicker_jumps[cam->mainsFreq]
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002662 [cam->params.sensorFps.baserate]
2663 [cam->params.sensorFps.divisor];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002664 cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
2665
2666 new_exposure = cam->params.flickerControl.coarseJump-1;
2667 while(new_exposure < old_exposure/2)
2668 new_exposure += cam->params.flickerControl.coarseJump;
2669 cam->params.exposure.coarseExpLo = new_exposure & 0xff;
2670 cam->params.exposure.coarseExpHi = new_exposure >> 8;
2671 cam->cmd_queue |= COMMAND_SETEXPOSURE;
2672 cam->exposure_status = EXPOSURE_NORMAL;
2673 LOG("Automatically decreasing sensor_fps\n");
2674
2675 } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT ||
2676 cam->exposure_status == EXPOSURE_LIGHT) &&
2677 cam->exposure_count >= LIGHT_TIME*framerate &&
2678 cam->params.sensorFps.divisor > 0) {
2679
2680 /* light for too long */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002681 int max_exp = FIRMWARE_VERSION(1,2) ? MAX_EXP_102 : MAX_EXP ;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002682
2683 --cam->params.sensorFps.divisor;
2684 cam->cmd_queue |= COMMAND_SETSENSORFPS;
2685
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002686 cam->params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07002687 flicker_jumps[cam->mainsFreq]
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002688 [cam->params.sensorFps.baserate]
2689 [cam->params.sensorFps.divisor];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002690 cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
2691
2692 new_exposure = cam->params.flickerControl.coarseJump-1;
2693 while(new_exposure < 2*old_exposure &&
2694 new_exposure+
2695 cam->params.flickerControl.coarseJump < max_exp)
2696 new_exposure += cam->params.flickerControl.coarseJump;
2697 cam->params.exposure.coarseExpLo = new_exposure & 0xff;
2698 cam->params.exposure.coarseExpHi = new_exposure >> 8;
2699 cam->cmd_queue |= COMMAND_SETEXPOSURE;
2700 cam->exposure_status = EXPOSURE_NORMAL;
2701 LOG("Automatically increasing sensor_fps\n");
2702 }
2703 } else {
2704 /* Flicker control off */
2705 if((cam->exposure_status == EXPOSURE_VERY_DARK ||
2706 cam->exposure_status == EXPOSURE_DARK) &&
2707 cam->exposure_count >= DARK_TIME*framerate &&
2708 cam->params.sensorFps.divisor < 3) {
2709
2710 /* dark for too long */
2711 ++cam->params.sensorFps.divisor;
2712 cam->cmd_queue |= COMMAND_SETSENSORFPS;
2713
2714 if(cam->params.exposure.gain > 0) {
2715 --cam->params.exposure.gain;
2716 cam->cmd_queue |= COMMAND_SETEXPOSURE;
2717 }
2718 cam->exposure_status = EXPOSURE_NORMAL;
2719 LOG("Automatically decreasing sensor_fps\n");
2720
2721 } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT ||
2722 cam->exposure_status == EXPOSURE_LIGHT) &&
2723 cam->exposure_count >= LIGHT_TIME*framerate &&
2724 cam->params.sensorFps.divisor > 0) {
2725
2726 /* light for too long */
2727 --cam->params.sensorFps.divisor;
2728 cam->cmd_queue |= COMMAND_SETSENSORFPS;
2729
2730 if(cam->params.exposure.gain <
2731 cam->params.exposure.gainMode-1) {
2732 ++cam->params.exposure.gain;
2733 cam->cmd_queue |= COMMAND_SETEXPOSURE;
2734 }
2735 cam->exposure_status = EXPOSURE_NORMAL;
2736 LOG("Automatically increasing sensor_fps\n");
2737 }
2738 }
Ingo Molnar3593cab2006-02-07 06:49:14 -02002739 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002740}
2741
2742/*-----------------------------------------------------------------*/
2743/* if flicker is switched off, this function switches it back on.It checks,
2744 however, that conditions are suitable before restarting it.
2745 This should only be called for firmware version 1.2.
2746
2747 It also adjust the colour balance when an exposure step is detected - as
2748 long as flicker is running
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002749*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07002750static void restart_flicker(struct cam_data *cam)
2751{
2752 int cam_exposure, old_exp;
2753 if(!FIRMWARE_VERSION(1,2))
2754 return;
Ingo Molnar3593cab2006-02-07 06:49:14 -02002755 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002756 if(cam->params.flickerControl.flickerMode == 0 ||
2757 cam->raw_image[39] == 0) {
Ingo Molnar3593cab2006-02-07 06:49:14 -02002758 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002759 return;
2760 }
2761 cam_exposure = cam->raw_image[39]*2;
2762 old_exp = cam->params.exposure.coarseExpLo +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002763 cam->params.exposure.coarseExpHi*256;
2764 /*
2765 see how far away camera exposure is from a valid
2766 flicker exposure value
2767 */
2768 cam_exposure %= cam->params.flickerControl.coarseJump;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002769 if(!cam->params.flickerControl.disabled &&
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002770 cam_exposure <= cam->params.flickerControl.coarseJump - 3) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002771 /* Flicker control auto-disabled */
2772 cam->params.flickerControl.disabled = 1;
2773 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002774
Linus Torvalds1da177e2005-04-16 15:20:36 -07002775 if(cam->params.flickerControl.disabled &&
2776 cam->params.flickerControl.flickerMode &&
2777 old_exp > cam->params.flickerControl.coarseJump +
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002778 ROUND_UP_EXP_FOR_FLICKER) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002779 /* exposure is now high enough to switch
2780 flicker control back on */
2781 set_flicker(&cam->params, &cam->cmd_queue, 1);
2782 if((cam->cmd_queue & COMMAND_SETEXPOSURE) &&
2783 cam->params.exposure.expMode == 2)
2784 cam->exposure_status = EXPOSURE_NORMAL;
2785
2786 }
Ingo Molnar3593cab2006-02-07 06:49:14 -02002787 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002788}
2789#undef FIRMWARE_VERSION
2790
2791static int clear_stall(struct cam_data *cam)
2792{
2793 /* FIXME: Does this actually work? */
2794 LOG("Clearing stall\n");
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002795
Linus Torvalds1da177e2005-04-16 15:20:36 -07002796 cam->ops->streamRead(cam->lowlevel_data, cam->raw_image, 0);
2797 do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0);
2798 return cam->params.status.streamState != STREAM_PAUSED;
2799}
2800
2801/* kernel thread function to read image from camera */
2802static int fetch_frame(void *data)
2803{
2804 int image_size, retry;
2805 struct cam_data *cam = (struct cam_data *)data;
2806 unsigned long oldjif, rate, diff;
2807
2808 /* Allow up to two bad images in a row to be read and
2809 * ignored before an error is reported */
2810 for (retry = 0; retry < 3; ++retry) {
2811 if (retry)
2812 DBG("retry=%d\n", retry);
2813
2814 if (!cam->ops)
2815 continue;
2816
2817 /* load first frame always uncompressed */
2818 if (cam->first_frame &&
2819 cam->params.compression.mode != CPIA_COMPRESSION_NONE) {
2820 do_command(cam, CPIA_COMMAND_SetCompression,
2821 CPIA_COMPRESSION_NONE,
2822 NO_DECIMATION, 0, 0);
2823 /* Trial & error - Discarding a frame prevents the
2824 first frame from having an error in the data. */
2825 do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0);
2826 }
2827
2828 /* init camera upload */
2829 if (do_command(cam, CPIA_COMMAND_GrabFrame, 0,
2830 cam->params.streamStartLine, 0, 0))
2831 continue;
2832
2833 if (cam->ops->wait_for_stream_ready) {
2834 /* loop until image ready */
2835 int count = 0;
2836 do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0);
2837 while (cam->params.status.streamState != STREAM_READY) {
2838 if(++count > READY_TIMEOUT)
2839 break;
2840 if(cam->params.status.streamState ==
2841 STREAM_PAUSED) {
2842 /* Bad news */
2843 if(!clear_stall(cam))
2844 return -EIO;
2845 }
2846
2847 cond_resched();
2848
2849 /* sleep for 10 ms, hopefully ;) */
2850 msleep_interruptible(10);
2851 if (signal_pending(current))
2852 return -EINTR;
2853
2854 do_command(cam, CPIA_COMMAND_GetCameraStatus,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002855 0, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002856 }
2857 if(cam->params.status.streamState != STREAM_READY) {
2858 continue;
2859 }
2860 }
2861
2862 cond_resched();
2863
2864 /* grab image from camera */
2865 oldjif = jiffies;
2866 image_size = cam->ops->streamRead(cam->lowlevel_data,
2867 cam->raw_image, 0);
2868 if (image_size <= 0) {
2869 DBG("streamRead failed: %d\n", image_size);
2870 continue;
2871 }
2872
2873 rate = image_size * HZ / 1024;
2874 diff = jiffies-oldjif;
2875 cam->transfer_rate = diff==0 ? rate : rate/diff;
2876 /* diff==0 ? unlikely but possible */
2877
2878 /* Switch flicker control back on if it got turned off */
2879 restart_flicker(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002880
Linus Torvalds1da177e2005-04-16 15:20:36 -07002881 /* If AEC is enabled, monitor the exposure and
2882 adjust the sensor frame rate if needed */
2883 if(cam->params.exposure.expMode == 2)
2884 monitor_exposure(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002885
Linus Torvalds1da177e2005-04-16 15:20:36 -07002886 /* camera idle now so dispatch queued commands */
2887 dispatch_commands(cam);
2888
2889 /* Update our knowledge of the camera state */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002890 do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
2891 do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002892 do_command(cam, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0);
2893
2894 /* decompress and convert image to by copying it from
2895 * raw_image to decompressed_frame
2896 */
2897
2898 cond_resched();
2899
2900 cam->image_size = parse_picture(cam, image_size);
2901 if (cam->image_size <= 0) {
2902 DBG("parse_picture failed %d\n", cam->image_size);
2903 if(cam->params.compression.mode !=
2904 CPIA_COMPRESSION_NONE) {
2905 /* Compression may not work right if we
2906 had a bad frame, get the next one
2907 uncompressed. */
2908 cam->first_frame = 1;
2909 do_command(cam, CPIA_COMMAND_SetGrabMode,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002910 CPIA_GRAB_SINGLE, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002911 /* FIXME: Trial & error - need up to 70ms for
2912 the grab mode change to complete ? */
2913 msleep_interruptible(70);
2914 if (signal_pending(current))
2915 return -EINTR;
2916 }
2917 } else
2918 break;
2919 }
2920
2921 if (retry < 3) {
2922 /* FIXME: this only works for double buffering */
2923 if (cam->frame[cam->curframe].state == FRAME_READY) {
2924 memcpy(cam->frame[cam->curframe].data,
2925 cam->decompressed_frame.data,
2926 cam->decompressed_frame.count);
2927 cam->frame[cam->curframe].state = FRAME_DONE;
2928 } else
2929 cam->decompressed_frame.state = FRAME_DONE;
2930
2931 if (cam->first_frame) {
2932 cam->first_frame = 0;
2933 do_command(cam, CPIA_COMMAND_SetCompression,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002934 cam->params.compression.mode,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002935 cam->params.compression.decimation, 0, 0);
2936
2937 /* Switch from single-grab to continuous grab */
2938 do_command(cam, CPIA_COMMAND_SetGrabMode,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002939 CPIA_GRAB_CONTINUOUS, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002940 }
2941 return 0;
2942 }
2943 return -EIO;
2944}
2945
2946static int capture_frame(struct cam_data *cam, struct video_mmap *vm)
2947{
2948 if (!cam->frame_buf) {
2949 /* we do lazy allocation */
2950 int err;
2951 if ((err = allocate_frame_buf(cam)))
2952 return err;
2953 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002954
Linus Torvalds1da177e2005-04-16 15:20:36 -07002955 cam->curframe = vm->frame;
2956 cam->frame[cam->curframe].state = FRAME_READY;
2957 return fetch_frame(cam);
2958}
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002959
Linus Torvalds1da177e2005-04-16 15:20:36 -07002960static int goto_high_power(struct cam_data *cam)
2961{
2962 if (do_command(cam, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0))
2963 return -EIO;
2964 msleep_interruptible(40); /* windows driver does it too */
2965 if(signal_pending(current))
2966 return -EINTR;
2967 if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
2968 return -EIO;
2969 if (cam->params.status.systemState == HI_POWER_STATE) {
2970 DBG("camera now in HIGH power state\n");
2971 return 0;
2972 }
2973 printstatus(cam);
2974 return -EIO;
2975}
2976
2977static int goto_low_power(struct cam_data *cam)
2978{
2979 if (do_command(cam, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0))
2980 return -1;
2981 if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
2982 return -1;
2983 if (cam->params.status.systemState == LO_POWER_STATE) {
2984 DBG("camera now in LOW power state\n");
2985 return 0;
2986 }
2987 printstatus(cam);
2988 return -1;
2989}
2990
2991static void save_camera_state(struct cam_data *cam)
2992{
2993 if(!(cam->cmd_queue & COMMAND_SETCOLOURBALANCE))
2994 do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
2995 if(!(cam->cmd_queue & COMMAND_SETEXPOSURE))
2996 do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
2997
2998 DBG("%d/%d/%d/%d/%d/%d/%d/%d\n",
2999 cam->params.exposure.gain,
3000 cam->params.exposure.fineExp,
3001 cam->params.exposure.coarseExpLo,
3002 cam->params.exposure.coarseExpHi,
3003 cam->params.exposure.redComp,
3004 cam->params.exposure.green1Comp,
3005 cam->params.exposure.green2Comp,
3006 cam->params.exposure.blueComp);
3007 DBG("%d/%d/%d\n",
3008 cam->params.colourBalance.redGain,
3009 cam->params.colourBalance.greenGain,
3010 cam->params.colourBalance.blueGain);
3011}
3012
3013static int set_camera_state(struct cam_data *cam)
3014{
3015 cam->cmd_queue = COMMAND_SETCOMPRESSION |
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003016 COMMAND_SETCOMPRESSIONTARGET |
3017 COMMAND_SETCOLOURPARAMS |
3018 COMMAND_SETFORMAT |
3019 COMMAND_SETYUVTHRESH |
3020 COMMAND_SETECPTIMING |
3021 COMMAND_SETCOMPRESSIONPARAMS |
3022 COMMAND_SETEXPOSURE |
3023 COMMAND_SETCOLOURBALANCE |
3024 COMMAND_SETSENSORFPS |
3025 COMMAND_SETAPCOR |
3026 COMMAND_SETFLICKERCTRL |
3027 COMMAND_SETVLOFFSET;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003028
3029 do_command(cam, CPIA_COMMAND_SetGrabMode, CPIA_GRAB_SINGLE,0,0,0);
3030 dispatch_commands(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003031
Linus Torvalds1da177e2005-04-16 15:20:36 -07003032 /* Wait 6 frames for the sensor to get all settings and
3033 AEC/ACB to settle */
3034 msleep_interruptible(6*(cam->params.sensorFps.baserate ? 33 : 40) *
3035 (1 << cam->params.sensorFps.divisor) + 10);
3036
3037 if(signal_pending(current))
3038 return -EINTR;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003039
Linus Torvalds1da177e2005-04-16 15:20:36 -07003040 save_camera_state(cam);
3041
3042 return 0;
3043}
3044
3045static void get_version_information(struct cam_data *cam)
3046{
3047 /* GetCPIAVersion */
3048 do_command(cam, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0);
3049
3050 /* GetPnPID */
3051 do_command(cam, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0);
3052}
3053
3054/* initialize camera */
3055static int reset_camera(struct cam_data *cam)
3056{
3057 int err;
3058 /* Start the camera in low power mode */
3059 if (goto_low_power(cam)) {
3060 if (cam->params.status.systemState != WARM_BOOT_STATE)
3061 return -ENODEV;
3062
3063 /* FIXME: this is just dirty trial and error */
3064 err = goto_high_power(cam);
3065 if(err)
3066 return err;
3067 do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0);
3068 if (goto_low_power(cam))
3069 return -ENODEV;
3070 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003071
Linus Torvalds1da177e2005-04-16 15:20:36 -07003072 /* procedure described in developer's guide p3-28 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003073
Linus Torvalds1da177e2005-04-16 15:20:36 -07003074 /* Check the firmware version. */
3075 cam->params.version.firmwareVersion = 0;
3076 get_version_information(cam);
3077 if (cam->params.version.firmwareVersion != 1)
3078 return -ENODEV;
3079
3080 /* A bug in firmware 1-02 limits gainMode to 2 */
3081 if(cam->params.version.firmwareRevision <= 2 &&
3082 cam->params.exposure.gainMode > 2) {
3083 cam->params.exposure.gainMode = 2;
3084 }
3085
3086 /* set QX3 detected flag */
3087 cam->params.qx3.qx3_detected = (cam->params.pnpID.vendor == 0x0813 &&
3088 cam->params.pnpID.product == 0x0001);
3089
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003090 /* The fatal error checking should be done after
Linus Torvalds1da177e2005-04-16 15:20:36 -07003091 * the camera powers up (developer's guide p 3-38) */
3092
3093 /* Set streamState before transition to high power to avoid bug
3094 * in firmware 1-02 */
3095 do_command(cam, CPIA_COMMAND_ModifyCameraStatus, STREAMSTATE, 0,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003096 STREAM_NOT_READY, 0);
3097
Linus Torvalds1da177e2005-04-16 15:20:36 -07003098 /* GotoHiPower */
3099 err = goto_high_power(cam);
3100 if (err)
3101 return err;
3102
3103 /* Check the camera status */
3104 if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
3105 return -EIO;
3106
3107 if (cam->params.status.fatalError) {
3108 DBG("fatal_error: %#04x\n",
3109 cam->params.status.fatalError);
3110 DBG("vp_status: %#04x\n",
3111 cam->params.status.vpStatus);
3112 if (cam->params.status.fatalError & ~(COM_FLAG|CPIA_FLAG)) {
3113 /* Fatal error in camera */
3114 return -EIO;
3115 } else if (cam->params.status.fatalError & (COM_FLAG|CPIA_FLAG)) {
3116 /* Firmware 1-02 may do this for parallel port cameras,
3117 * just clear the flags (developer's guide p 3-38) */
3118 do_command(cam, CPIA_COMMAND_ModifyCameraStatus,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003119 FATALERROR, ~(COM_FLAG|CPIA_FLAG), 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003120 }
3121 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003122
Linus Torvalds1da177e2005-04-16 15:20:36 -07003123 /* Check the camera status again */
3124 if (cam->params.status.fatalError) {
3125 if (cam->params.status.fatalError)
3126 return -EIO;
3127 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003128
Linus Torvalds1da177e2005-04-16 15:20:36 -07003129 /* VPVersion can't be retrieved before the camera is in HiPower,
3130 * so get it here instead of in get_version_information. */
3131 do_command(cam, CPIA_COMMAND_GetVPVersion, 0, 0, 0, 0);
3132
3133 /* set camera to a known state */
3134 return set_camera_state(cam);
3135}
3136
3137static void put_cam(struct cpia_camera_ops* ops)
3138{
Mariusz Kozlowskiac328982007-01-07 10:36:24 -03003139 module_put(ops->owner);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003140}
3141
3142/* ------------------------- V4L interface --------------------- */
Hans Verkuilbec43662008-12-30 06:58:20 -03003143static int cpia_open(struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003144{
3145 struct video_device *dev = video_devdata(file);
Hans Verkuil601e9442008-08-23 07:24:07 -03003146 struct cam_data *cam = video_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003147 int err;
3148
3149 if (!cam) {
3150 DBG("Internal error, cam_data not found!\n");
3151 return -ENODEV;
3152 }
3153
3154 if (cam->open_count > 0) {
3155 DBG("Camera already open\n");
3156 return -EBUSY;
3157 }
3158
3159 if (!try_module_get(cam->ops->owner))
3160 return -ENODEV;
3161
Ingo Molnar3593cab2006-02-07 06:49:14 -02003162 mutex_lock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003163 err = -ENOMEM;
3164 if (!cam->raw_image) {
3165 cam->raw_image = rvmalloc(CPIA_MAX_IMAGE_SIZE);
3166 if (!cam->raw_image)
3167 goto oops;
3168 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003169
Linus Torvalds1da177e2005-04-16 15:20:36 -07003170 if (!cam->decompressed_frame.data) {
3171 cam->decompressed_frame.data = rvmalloc(CPIA_MAX_FRAME_SIZE);
3172 if (!cam->decompressed_frame.data)
3173 goto oops;
3174 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003175
Linus Torvalds1da177e2005-04-16 15:20:36 -07003176 /* open cpia */
3177 err = -ENODEV;
3178 if (cam->ops->open(cam->lowlevel_data))
3179 goto oops;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003180
Linus Torvalds1da177e2005-04-16 15:20:36 -07003181 /* reset the camera */
3182 if ((err = reset_camera(cam)) != 0) {
3183 cam->ops->close(cam->lowlevel_data);
3184 goto oops;
3185 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003186
Linus Torvalds1da177e2005-04-16 15:20:36 -07003187 err = -EINTR;
3188 if(signal_pending(current))
3189 goto oops;
3190
3191 /* Set ownership of /proc/cpia/videoX to current user */
3192 if(cam->proc_entry)
David Howellsabc94fc2008-08-27 10:46:39 -03003193 cam->proc_entry->uid = current_uid();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003194
3195 /* set mark for loading first frame uncompressed */
3196 cam->first_frame = 1;
3197
3198 /* init it to something */
3199 cam->mmap_kludge = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003200
Linus Torvalds1da177e2005-04-16 15:20:36 -07003201 ++cam->open_count;
3202 file->private_data = dev;
Ingo Molnar3593cab2006-02-07 06:49:14 -02003203 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003204 return 0;
3205
3206 oops:
3207 if (cam->decompressed_frame.data) {
3208 rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
3209 cam->decompressed_frame.data = NULL;
3210 }
3211 if (cam->raw_image) {
3212 rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
3213 cam->raw_image = NULL;
3214 }
Ingo Molnar3593cab2006-02-07 06:49:14 -02003215 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003216 put_cam(cam->ops);
3217 return err;
3218}
3219
Hans Verkuilbec43662008-12-30 06:58:20 -03003220static int cpia_close(struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003221{
3222 struct video_device *dev = file->private_data;
Hans Verkuil601e9442008-08-23 07:24:07 -03003223 struct cam_data *cam = video_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003224
3225 if (cam->ops) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003226 /* Return ownership of /proc/cpia/videoX to root */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003227 if(cam->proc_entry)
3228 cam->proc_entry->uid = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003229
Linus Torvalds1da177e2005-04-16 15:20:36 -07003230 /* save camera state for later open (developers guide ch 3.5.3) */
3231 save_camera_state(cam);
3232
3233 /* GotoLoPower */
3234 goto_low_power(cam);
3235
3236 /* Update the camera status */
3237 do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
3238
3239 /* cleanup internal state stuff */
3240 free_frames(cam->frame);
3241
3242 /* close cpia */
3243 cam->ops->close(cam->lowlevel_data);
3244
3245 put_cam(cam->ops);
3246 }
3247
3248 if (--cam->open_count == 0) {
3249 /* clean up capture-buffers */
3250 if (cam->raw_image) {
3251 rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
3252 cam->raw_image = NULL;
3253 }
3254
3255 if (cam->decompressed_frame.data) {
3256 rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
3257 cam->decompressed_frame.data = NULL;
3258 }
3259
3260 if (cam->frame_buf)
3261 free_frame_buf(cam);
3262
3263 if (!cam->ops)
3264 kfree(cam);
3265 }
3266 file->private_data = NULL;
3267
3268 return 0;
3269}
3270
3271static ssize_t cpia_read(struct file *file, char __user *buf,
3272 size_t count, loff_t *ppos)
3273{
3274 struct video_device *dev = file->private_data;
Hans Verkuil601e9442008-08-23 07:24:07 -03003275 struct cam_data *cam = video_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003276 int err;
3277
3278 /* make this _really_ smp and multithread-safe */
Ingo Molnar3593cab2006-02-07 06:49:14 -02003279 if (mutex_lock_interruptible(&cam->busy_lock))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003280 return -EINTR;
3281
3282 if (!buf) {
3283 DBG("buf NULL\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02003284 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003285 return -EINVAL;
3286 }
3287
3288 if (!count) {
3289 DBG("count 0\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02003290 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003291 return 0;
3292 }
3293
3294 if (!cam->ops) {
3295 DBG("ops NULL\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02003296 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003297 return -ENODEV;
3298 }
3299
3300 /* upload frame */
3301 cam->decompressed_frame.state = FRAME_READY;
3302 cam->mmap_kludge=0;
3303 if((err = fetch_frame(cam)) != 0) {
3304 DBG("ERROR from fetch_frame: %d\n", err);
Ingo Molnar3593cab2006-02-07 06:49:14 -02003305 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003306 return err;
3307 }
3308 cam->decompressed_frame.state = FRAME_UNUSED;
3309
3310 /* copy data to user space */
3311 if (cam->decompressed_frame.count > count) {
3312 DBG("count wrong: %d, %lu\n", cam->decompressed_frame.count,
3313 (unsigned long) count);
Ingo Molnar3593cab2006-02-07 06:49:14 -02003314 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003315 return -EFAULT;
3316 }
3317 if (copy_to_user(buf, cam->decompressed_frame.data,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003318 cam->decompressed_frame.count)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003319 DBG("copy_to_user failed\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02003320 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003321 return -EFAULT;
3322 }
3323
Ingo Molnar3593cab2006-02-07 06:49:14 -02003324 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003325 return cam->decompressed_frame.count;
3326}
3327
Hans Verkuil069b7472008-12-30 07:04:34 -03003328static long cpia_do_ioctl(struct file *file, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003329{
3330 struct video_device *dev = file->private_data;
Hans Verkuil601e9442008-08-23 07:24:07 -03003331 struct cam_data *cam = video_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003332 int retval = 0;
3333
3334 if (!cam || !cam->ops)
3335 return -ENODEV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003336
Linus Torvalds1da177e2005-04-16 15:20:36 -07003337 /* make this _really_ smp-safe */
Ingo Molnar3593cab2006-02-07 06:49:14 -02003338 if (mutex_lock_interruptible(&cam->busy_lock))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003339 return -EINTR;
3340
Hans Verkuilf473bf72008-11-01 08:25:11 -03003341 /* DBG("cpia_ioctl: %u\n", cmd); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003342
Hans Verkuilf473bf72008-11-01 08:25:11 -03003343 switch (cmd) {
Alexey Dobriyanbe787ac2006-03-07 22:20:23 -03003344 /* query capabilities */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003345 case VIDIOCGCAP:
3346 {
3347 struct video_capability *b = arg;
3348
3349 DBG("VIDIOCGCAP\n");
3350 strcpy(b->name, "CPiA Camera");
3351 b->type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE;
3352 b->channels = 1;
3353 b->audios = 0;
3354 b->maxwidth = 352; /* VIDEOSIZE_CIF */
3355 b->maxheight = 288;
3356 b->minwidth = 48; /* VIDEOSIZE_48_48 */
3357 b->minheight = 48;
3358 break;
3359 }
3360
3361 /* get/set video source - we are a camera and nothing else */
3362 case VIDIOCGCHAN:
3363 {
3364 struct video_channel *v = arg;
3365
3366 DBG("VIDIOCGCHAN\n");
3367 if (v->channel != 0) {
3368 retval = -EINVAL;
3369 break;
3370 }
3371
3372 v->channel = 0;
3373 strcpy(v->name, "Camera");
3374 v->tuners = 0;
3375 v->flags = 0;
3376 v->type = VIDEO_TYPE_CAMERA;
3377 v->norm = 0;
3378 break;
3379 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003380
Linus Torvalds1da177e2005-04-16 15:20:36 -07003381 case VIDIOCSCHAN:
3382 {
3383 struct video_channel *v = arg;
3384
3385 DBG("VIDIOCSCHAN\n");
3386 if (v->channel != 0)
3387 retval = -EINVAL;
3388 break;
3389 }
3390
3391 /* image properties */
3392 case VIDIOCGPICT:
3393 {
3394 struct video_picture *pic = arg;
3395 DBG("VIDIOCGPICT\n");
3396 *pic = cam->vp;
3397 break;
3398 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003399
Linus Torvalds1da177e2005-04-16 15:20:36 -07003400 case VIDIOCSPICT:
3401 {
3402 struct video_picture *vp = arg;
3403
3404 DBG("VIDIOCSPICT\n");
3405
3406 /* check validity */
3407 DBG("palette: %d\n", vp->palette);
3408 DBG("depth: %d\n", vp->depth);
3409 if (!valid_mode(vp->palette, vp->depth)) {
3410 retval = -EINVAL;
3411 break;
3412 }
3413
Ingo Molnar3593cab2006-02-07 06:49:14 -02003414 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003415 /* brightness, colour, contrast need no check 0-65535 */
3416 cam->vp = *vp;
3417 /* update cam->params.colourParams */
3418 cam->params.colourParams.brightness = vp->brightness*100/65535;
3419 cam->params.colourParams.contrast = vp->contrast*100/65535;
3420 cam->params.colourParams.saturation = vp->colour*100/65535;
3421 /* contrast is in steps of 8, so round */
3422 cam->params.colourParams.contrast =
3423 ((cam->params.colourParams.contrast + 3) / 8) * 8;
3424 if (cam->params.version.firmwareVersion == 1 &&
3425 cam->params.version.firmwareRevision == 2 &&
3426 cam->params.colourParams.contrast > 80) {
3427 /* 1-02 firmware limits contrast to 80 */
3428 cam->params.colourParams.contrast = 80;
3429 }
3430
3431 /* Adjust flicker control if necessary */
3432 if(cam->params.flickerControl.allowableOverExposure < 0)
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003433 cam->params.flickerControl.allowableOverExposure =
Linus Torvalds1da177e2005-04-16 15:20:36 -07003434 -find_over_exposure(cam->params.colourParams.brightness);
3435 if(cam->params.flickerControl.flickerMode != 0)
3436 cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003437
Linus Torvalds1da177e2005-04-16 15:20:36 -07003438
3439 /* queue command to update camera */
3440 cam->cmd_queue |= COMMAND_SETCOLOURPARAMS;
Ingo Molnar3593cab2006-02-07 06:49:14 -02003441 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003442 DBG("VIDIOCSPICT: %d / %d // %d / %d / %d / %d\n",
3443 vp->depth, vp->palette, vp->brightness, vp->hue, vp->colour,
3444 vp->contrast);
3445 break;
3446 }
3447
3448 /* get/set capture window */
3449 case VIDIOCGWIN:
3450 {
3451 struct video_window *vw = arg;
3452 DBG("VIDIOCGWIN\n");
3453
3454 *vw = cam->vw;
3455 break;
3456 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003457
Linus Torvalds1da177e2005-04-16 15:20:36 -07003458 case VIDIOCSWIN:
3459 {
3460 /* copy_from_user, check validity, copy to internal structure */
3461 struct video_window *vw = arg;
3462 DBG("VIDIOCSWIN\n");
3463
3464 if (vw->clipcount != 0) { /* clipping not supported */
3465 retval = -EINVAL;
3466 break;
3467 }
3468 if (vw->clips != NULL) { /* clipping not supported */
3469 retval = -EINVAL;
3470 break;
3471 }
3472
3473 /* we set the video window to something smaller or equal to what
3474 * is requested by the user???
3475 */
Ingo Molnar3593cab2006-02-07 06:49:14 -02003476 mutex_lock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003477 if (vw->width != cam->vw.width || vw->height != cam->vw.height) {
3478 int video_size = match_videosize(vw->width, vw->height);
3479
3480 if (video_size < 0) {
3481 retval = -EINVAL;
Ingo Molnar3593cab2006-02-07 06:49:14 -02003482 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003483 break;
3484 }
3485 cam->video_size = video_size;
3486
3487 /* video size is changing, reset the subcapture area */
3488 memset(&cam->vc, 0, sizeof(cam->vc));
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003489
Linus Torvalds1da177e2005-04-16 15:20:36 -07003490 set_vw_size(cam);
3491 DBG("%d / %d\n", cam->vw.width, cam->vw.height);
3492 cam->cmd_queue |= COMMAND_SETFORMAT;
3493 }
3494
Ingo Molnar3593cab2006-02-07 06:49:14 -02003495 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003496
3497 /* setformat ignored by camera during streaming,
3498 * so stop/dispatch/start */
3499 if (cam->cmd_queue & COMMAND_SETFORMAT) {
3500 DBG("\n");
3501 dispatch_commands(cam);
3502 }
3503 DBG("%d/%d:%d\n", cam->video_size,
3504 cam->vw.width, cam->vw.height);
3505 break;
3506 }
3507
3508 /* mmap interface */
3509 case VIDIOCGMBUF:
3510 {
3511 struct video_mbuf *vm = arg;
3512 int i;
3513
3514 DBG("VIDIOCGMBUF\n");
3515 memset(vm, 0, sizeof(*vm));
3516 vm->size = CPIA_MAX_FRAME_SIZE*FRAME_NUM;
3517 vm->frames = FRAME_NUM;
3518 for (i = 0; i < FRAME_NUM; i++)
3519 vm->offsets[i] = CPIA_MAX_FRAME_SIZE * i;
3520 break;
3521 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003522
Linus Torvalds1da177e2005-04-16 15:20:36 -07003523 case VIDIOCMCAPTURE:
3524 {
3525 struct video_mmap *vm = arg;
3526 int video_size;
3527
3528 DBG("VIDIOCMCAPTURE: %d / %d / %dx%d\n", vm->format, vm->frame,
3529 vm->width, vm->height);
3530 if (vm->frame<0||vm->frame>=FRAME_NUM) {
3531 retval = -EINVAL;
3532 break;
3533 }
3534
3535 /* set video format */
3536 cam->vp.palette = vm->format;
3537 switch(vm->format) {
3538 case VIDEO_PALETTE_GREY:
3539 cam->vp.depth=8;
3540 break;
3541 case VIDEO_PALETTE_RGB555:
3542 case VIDEO_PALETTE_RGB565:
3543 case VIDEO_PALETTE_YUV422:
3544 case VIDEO_PALETTE_YUYV:
3545 case VIDEO_PALETTE_UYVY:
3546 cam->vp.depth = 16;
3547 break;
3548 case VIDEO_PALETTE_RGB24:
3549 cam->vp.depth = 24;
3550 break;
3551 case VIDEO_PALETTE_RGB32:
3552 cam->vp.depth = 32;
3553 break;
3554 default:
3555 retval = -EINVAL;
3556 break;
3557 }
3558 if (retval)
3559 break;
3560
3561 /* set video size */
3562 video_size = match_videosize(vm->width, vm->height);
3563 if (video_size < 0) {
3564 retval = -EINVAL;
3565 break;
3566 }
3567 if (video_size != cam->video_size) {
3568 cam->video_size = video_size;
3569
3570 /* video size is changing, reset the subcapture area */
3571 memset(&cam->vc, 0, sizeof(cam->vc));
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003572
Linus Torvalds1da177e2005-04-16 15:20:36 -07003573 set_vw_size(cam);
3574 cam->cmd_queue |= COMMAND_SETFORMAT;
3575 dispatch_commands(cam);
3576 }
3577 /* according to v4l-spec we must start streaming here */
3578 cam->mmap_kludge = 1;
3579 retval = capture_frame(cam, vm);
3580
3581 break;
3582 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003583
Linus Torvalds1da177e2005-04-16 15:20:36 -07003584 case VIDIOCSYNC:
3585 {
3586 int *frame = arg;
3587
3588 //DBG("VIDIOCSYNC: %d\n", *frame);
3589
3590 if (*frame<0 || *frame >= FRAME_NUM) {
3591 retval = -EINVAL;
3592 break;
3593 }
3594
3595 switch (cam->frame[*frame].state) {
3596 case FRAME_UNUSED:
3597 case FRAME_READY:
3598 case FRAME_GRABBING:
3599 DBG("sync to unused frame %d\n", *frame);
3600 retval = -EINVAL;
3601 break;
3602
3603 case FRAME_DONE:
3604 cam->frame[*frame].state = FRAME_UNUSED;
3605 //DBG("VIDIOCSYNC: %d synced\n", *frame);
3606 break;
3607 }
3608 if (retval == -EINTR) {
3609 /* FIXME - xawtv does not handle this nice */
3610 retval = 0;
3611 }
3612 break;
3613 }
3614
3615 case VIDIOCGCAPTURE:
3616 {
3617 struct video_capture *vc = arg;
3618
3619 DBG("VIDIOCGCAPTURE\n");
3620
3621 *vc = cam->vc;
3622
3623 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003624 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003625
3626 case VIDIOCSCAPTURE:
3627 {
3628 struct video_capture *vc = arg;
3629
3630 DBG("VIDIOCSCAPTURE\n");
3631
3632 if (vc->decimation != 0) { /* How should this be used? */
3633 retval = -EINVAL;
3634 break;
3635 }
3636 if (vc->flags != 0) { /* Even/odd grab not supported */
3637 retval = -EINVAL;
3638 break;
3639 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003640
Linus Torvalds1da177e2005-04-16 15:20:36 -07003641 /* Clip to the resolution we can set for the ROI
3642 (every 8 columns and 4 rows) */
3643 vc->x = vc->x & ~(__u32)7;
3644 vc->y = vc->y & ~(__u32)3;
3645 vc->width = vc->width & ~(__u32)7;
3646 vc->height = vc->height & ~(__u32)3;
3647
3648 if(vc->width == 0 || vc->height == 0 ||
3649 vc->x + vc->width > cam->vw.width ||
3650 vc->y + vc->height > cam->vw.height) {
3651 retval = -EINVAL;
3652 break;
3653 }
3654
3655 DBG("%d,%d/%dx%d\n", vc->x,vc->y,vc->width, vc->height);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003656
Ingo Molnar3593cab2006-02-07 06:49:14 -02003657 mutex_lock(&cam->param_lock);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003658
Linus Torvalds1da177e2005-04-16 15:20:36 -07003659 cam->vc.x = vc->x;
3660 cam->vc.y = vc->y;
3661 cam->vc.width = vc->width;
3662 cam->vc.height = vc->height;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003663
Linus Torvalds1da177e2005-04-16 15:20:36 -07003664 set_vw_size(cam);
3665 cam->cmd_queue |= COMMAND_SETFORMAT;
3666
Ingo Molnar3593cab2006-02-07 06:49:14 -02003667 mutex_unlock(&cam->param_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003668
3669 /* setformat ignored by camera during streaming,
3670 * so stop/dispatch/start */
3671 dispatch_commands(cam);
3672 break;
3673 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003674
Linus Torvalds1da177e2005-04-16 15:20:36 -07003675 case VIDIOCGUNIT:
3676 {
3677 struct video_unit *vu = arg;
3678
3679 DBG("VIDIOCGUNIT\n");
3680
3681 vu->video = cam->vdev.minor;
3682 vu->vbi = VIDEO_NO_UNIT;
3683 vu->radio = VIDEO_NO_UNIT;
3684 vu->audio = VIDEO_NO_UNIT;
3685 vu->teletext = VIDEO_NO_UNIT;
3686
3687 break;
3688 }
3689
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003690
Linus Torvalds1da177e2005-04-16 15:20:36 -07003691 /* pointless to implement overlay with this camera */
3692 case VIDIOCCAPTURE:
3693 case VIDIOCGFBUF:
3694 case VIDIOCSFBUF:
3695 case VIDIOCKEY:
3696 /* tuner interface - we have none */
3697 case VIDIOCGTUNER:
3698 case VIDIOCSTUNER:
3699 case VIDIOCGFREQ:
3700 case VIDIOCSFREQ:
3701 /* audio interface - we have none */
3702 case VIDIOCGAUDIO:
3703 case VIDIOCSAUDIO:
3704 retval = -EINVAL;
3705 break;
3706 default:
3707 retval = -ENOIOCTLCMD;
3708 break;
3709 }
3710
Ingo Molnar3593cab2006-02-07 06:49:14 -02003711 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003712 return retval;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003713}
Linus Torvalds1da177e2005-04-16 15:20:36 -07003714
Hans Verkuil069b7472008-12-30 07:04:34 -03003715static long cpia_ioctl(struct file *file,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003716 unsigned int cmd, unsigned long arg)
3717{
Hans Verkuilf473bf72008-11-01 08:25:11 -03003718 return video_usercopy(file, cmd, arg, cpia_do_ioctl);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003719}
3720
3721
3722/* FIXME */
3723static int cpia_mmap(struct file *file, struct vm_area_struct *vma)
3724{
3725 struct video_device *dev = file->private_data;
3726 unsigned long start = vma->vm_start;
3727 unsigned long size = vma->vm_end - vma->vm_start;
3728 unsigned long page, pos;
Hans Verkuil601e9442008-08-23 07:24:07 -03003729 struct cam_data *cam = video_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003730 int retval;
3731
3732 if (!cam || !cam->ops)
3733 return -ENODEV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003734
Linus Torvalds1da177e2005-04-16 15:20:36 -07003735 DBG("cpia_mmap: %ld\n", size);
3736
3737 if (size > FRAME_NUM*CPIA_MAX_FRAME_SIZE)
3738 return -EINVAL;
3739
3740 if (!cam || !cam->ops)
3741 return -ENODEV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003742
Linus Torvalds1da177e2005-04-16 15:20:36 -07003743 /* make this _really_ smp-safe */
Ingo Molnar3593cab2006-02-07 06:49:14 -02003744 if (mutex_lock_interruptible(&cam->busy_lock))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003745 return -EINTR;
3746
3747 if (!cam->frame_buf) { /* we do lazy allocation */
3748 if ((retval = allocate_frame_buf(cam))) {
Ingo Molnar3593cab2006-02-07 06:49:14 -02003749 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003750 return retval;
3751 }
3752 }
3753
3754 pos = (unsigned long)(cam->frame_buf);
3755 while (size > 0) {
3756 page = vmalloc_to_pfn((void *)pos);
3757 if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) {
Ingo Molnar3593cab2006-02-07 06:49:14 -02003758 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003759 return -EAGAIN;
3760 }
3761 start += PAGE_SIZE;
3762 pos += PAGE_SIZE;
3763 if (size > PAGE_SIZE)
3764 size -= PAGE_SIZE;
3765 else
3766 size = 0;
3767 }
3768
3769 DBG("cpia_mmap: %ld\n", size);
Ingo Molnar3593cab2006-02-07 06:49:14 -02003770 mutex_unlock(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003771
3772 return 0;
3773}
3774
Hans Verkuilbec43662008-12-30 06:58:20 -03003775static const struct v4l2_file_operations cpia_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003776 .owner = THIS_MODULE,
3777 .open = cpia_open,
3778 .release = cpia_close,
3779 .read = cpia_read,
3780 .mmap = cpia_mmap,
3781 .ioctl = cpia_ioctl,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003782};
3783
3784static struct video_device cpia_template = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003785 .name = "CPiA Camera",
Linus Torvalds1da177e2005-04-16 15:20:36 -07003786 .fops = &cpia_fops,
Hans Verkuilaa5e90a2008-08-23 06:23:55 -03003787 .release = video_device_release_empty,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003788};
3789
3790/* initialise cam_data structure */
3791static void reset_camera_struct(struct cam_data *cam)
3792{
3793 /* The following parameter values are the defaults from
3794 * "Software Developer's Guide for CPiA Cameras". Any changes
3795 * to the defaults are noted in comments. */
3796 cam->params.colourParams.brightness = 50;
3797 cam->params.colourParams.contrast = 48;
3798 cam->params.colourParams.saturation = 50;
3799 cam->params.exposure.gainMode = 4;
3800 cam->params.exposure.expMode = 2; /* AEC */
3801 cam->params.exposure.compMode = 1;
3802 cam->params.exposure.centreWeight = 1;
3803 cam->params.exposure.gain = 0;
3804 cam->params.exposure.fineExp = 0;
3805 cam->params.exposure.coarseExpLo = 185;
3806 cam->params.exposure.coarseExpHi = 0;
3807 cam->params.exposure.redComp = COMP_RED;
3808 cam->params.exposure.green1Comp = COMP_GREEN1;
3809 cam->params.exposure.green2Comp = COMP_GREEN2;
3810 cam->params.exposure.blueComp = COMP_BLUE;
3811 cam->params.colourBalance.balanceMode = 2; /* ACB */
3812 cam->params.colourBalance.redGain = 32;
3813 cam->params.colourBalance.greenGain = 6;
3814 cam->params.colourBalance.blueGain = 92;
3815 cam->params.apcor.gain1 = 0x18;
3816 cam->params.apcor.gain2 = 0x16;
3817 cam->params.apcor.gain4 = 0x24;
3818 cam->params.apcor.gain8 = 0x34;
3819 cam->params.flickerControl.flickerMode = 0;
3820 cam->params.flickerControl.disabled = 1;
3821
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003822 cam->params.flickerControl.coarseJump =
Linus Torvalds1da177e2005-04-16 15:20:36 -07003823 flicker_jumps[cam->mainsFreq]
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003824 [cam->params.sensorFps.baserate]
3825 [cam->params.sensorFps.divisor];
3826 cam->params.flickerControl.allowableOverExposure =
Linus Torvalds1da177e2005-04-16 15:20:36 -07003827 -find_over_exposure(cam->params.colourParams.brightness);
3828 cam->params.vlOffset.gain1 = 20;
3829 cam->params.vlOffset.gain2 = 24;
3830 cam->params.vlOffset.gain4 = 26;
3831 cam->params.vlOffset.gain8 = 26;
3832 cam->params.compressionParams.hysteresis = 3;
3833 cam->params.compressionParams.threshMax = 11;
3834 cam->params.compressionParams.smallStep = 1;
3835 cam->params.compressionParams.largeStep = 3;
3836 cam->params.compressionParams.decimationHysteresis = 2;
3837 cam->params.compressionParams.frDiffStepThresh = 5;
3838 cam->params.compressionParams.qDiffStepThresh = 3;
3839 cam->params.compressionParams.decimationThreshMod = 2;
3840 /* End of default values from Software Developer's Guide */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003841
Linus Torvalds1da177e2005-04-16 15:20:36 -07003842 cam->transfer_rate = 0;
3843 cam->exposure_status = EXPOSURE_NORMAL;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003844
Linus Torvalds1da177e2005-04-16 15:20:36 -07003845 /* Set Sensor FPS to 15fps. This seems better than 30fps
3846 * for indoor lighting. */
3847 cam->params.sensorFps.divisor = 1;
3848 cam->params.sensorFps.baserate = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003849
Linus Torvalds1da177e2005-04-16 15:20:36 -07003850 cam->params.yuvThreshold.yThreshold = 6; /* From windows driver */
3851 cam->params.yuvThreshold.uvThreshold = 6; /* From windows driver */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003852
Linus Torvalds1da177e2005-04-16 15:20:36 -07003853 cam->params.format.subSample = SUBSAMPLE_422;
3854 cam->params.format.yuvOrder = YUVORDER_YUYV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003855
Linus Torvalds1da177e2005-04-16 15:20:36 -07003856 cam->params.compression.mode = CPIA_COMPRESSION_AUTO;
3857 cam->params.compressionTarget.frTargeting =
3858 CPIA_COMPRESSION_TARGET_QUALITY;
3859 cam->params.compressionTarget.targetFR = 15; /* From windows driver */
3860 cam->params.compressionTarget.targetQ = 5; /* From windows driver */
3861
3862 cam->params.qx3.qx3_detected = 0;
3863 cam->params.qx3.toplight = 0;
3864 cam->params.qx3.bottomlight = 0;
3865 cam->params.qx3.button = 0;
3866 cam->params.qx3.cradled = 0;
3867
3868 cam->video_size = VIDEOSIZE_CIF;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003869
Linus Torvalds1da177e2005-04-16 15:20:36 -07003870 cam->vp.colour = 32768; /* 50% */
3871 cam->vp.hue = 32768; /* 50% */
3872 cam->vp.brightness = 32768; /* 50% */
3873 cam->vp.contrast = 32768; /* 50% */
3874 cam->vp.whiteness = 0; /* not used -> grayscale only */
3875 cam->vp.depth = 24; /* to be set by user */
3876 cam->vp.palette = VIDEO_PALETTE_RGB24; /* to be set by user */
3877
3878 cam->vc.x = 0;
3879 cam->vc.y = 0;
3880 cam->vc.width = 0;
3881 cam->vc.height = 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003882
Linus Torvalds1da177e2005-04-16 15:20:36 -07003883 cam->vw.x = 0;
3884 cam->vw.y = 0;
3885 set_vw_size(cam);
3886 cam->vw.chromakey = 0;
3887 cam->vw.flags = 0;
3888 cam->vw.clipcount = 0;
3889 cam->vw.clips = NULL;
3890
3891 cam->cmd_queue = COMMAND_NONE;
3892 cam->first_frame = 1;
3893
3894 return;
3895}
3896
3897/* initialize cam_data structure */
3898static void init_camera_struct(struct cam_data *cam,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003899 struct cpia_camera_ops *ops )
Linus Torvalds1da177e2005-04-16 15:20:36 -07003900{
3901 int i;
3902
3903 /* Default everything to 0 */
3904 memset(cam, 0, sizeof(struct cam_data));
3905
3906 cam->ops = ops;
Ingo Molnar3593cab2006-02-07 06:49:14 -02003907 mutex_init(&cam->param_lock);
3908 mutex_init(&cam->busy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003909
3910 reset_camera_struct(cam);
3911
3912 cam->proc_entry = NULL;
3913
3914 memcpy(&cam->vdev, &cpia_template, sizeof(cpia_template));
Hans Verkuil601e9442008-08-23 07:24:07 -03003915 video_set_drvdata(&cam->vdev, cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003916
Linus Torvalds1da177e2005-04-16 15:20:36 -07003917 cam->curframe = 0;
3918 for (i = 0; i < FRAME_NUM; i++) {
3919 cam->frame[i].width = 0;
3920 cam->frame[i].height = 0;
3921 cam->frame[i].state = FRAME_UNUSED;
3922 cam->frame[i].data = NULL;
3923 }
3924 cam->decompressed_frame.width = 0;
3925 cam->decompressed_frame.height = 0;
3926 cam->decompressed_frame.state = FRAME_UNUSED;
3927 cam->decompressed_frame.data = NULL;
3928}
3929
3930struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel)
3931{
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003932 struct cam_data *camera;
3933
Linus Torvalds1da177e2005-04-16 15:20:36 -07003934 if ((camera = kmalloc(sizeof(struct cam_data), GFP_KERNEL)) == NULL)
3935 return NULL;
3936
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003937
Linus Torvalds1da177e2005-04-16 15:20:36 -07003938 init_camera_struct( camera, ops );
3939 camera->lowlevel_data = lowlevel;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003940
Linus Torvalds1da177e2005-04-16 15:20:36 -07003941 /* register v4l device */
Hans Verkuildc60de32008-09-03 17:11:58 -03003942 if (video_register_device(&camera->vdev, VFL_TYPE_GRABBER, video_nr) < 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003943 kfree(camera);
3944 printk(KERN_DEBUG "video_register_device failed\n");
3945 return NULL;
3946 }
3947
3948 /* get version information from camera: open/reset/close */
3949
3950 /* open cpia */
3951 if (camera->ops->open(camera->lowlevel_data))
3952 return camera;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003953
Linus Torvalds1da177e2005-04-16 15:20:36 -07003954 /* reset the camera */
3955 if (reset_camera(camera) != 0) {
3956 camera->ops->close(camera->lowlevel_data);
3957 return camera;
3958 }
3959
3960 /* close cpia */
3961 camera->ops->close(camera->lowlevel_data);
3962
3963#ifdef CONFIG_PROC_FS
3964 create_proc_cpia_cam(camera);
3965#endif
3966
3967 printk(KERN_INFO " CPiA Version: %d.%02d (%d.%d)\n",
3968 camera->params.version.firmwareVersion,
3969 camera->params.version.firmwareRevision,
3970 camera->params.version.vcVersion,
3971 camera->params.version.vcRevision);
3972 printk(KERN_INFO " CPiA PnP-ID: %04x:%04x:%04x\n",
3973 camera->params.pnpID.vendor,
3974 camera->params.pnpID.product,
3975 camera->params.pnpID.deviceRevision);
3976 printk(KERN_INFO " VP-Version: %d.%d %04x\n",
3977 camera->params.vpVersion.vpVersion,
3978 camera->params.vpVersion.vpRevision,
3979 camera->params.vpVersion.cameraHeadID);
3980
3981 return camera;
3982}
3983
3984void cpia_unregister_camera(struct cam_data *cam)
3985{
3986 DBG("unregistering video\n");
3987 video_unregister_device(&cam->vdev);
3988 if (cam->open_count) {
3989 put_cam(cam->ops);
3990 DBG("camera open -- setting ops to NULL\n");
3991 cam->ops = NULL;
3992 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003993
Linus Torvalds1da177e2005-04-16 15:20:36 -07003994#ifdef CONFIG_PROC_FS
Laurent Pinchart38c7c032009-11-27 13:57:15 -03003995 DBG("destroying /proc/cpia/%s\n", video_device_node_name(&cam->vdev));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003996 destroy_proc_cpia_cam(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03003997#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07003998 if (!cam->open_count) {
3999 DBG("freeing camera\n");
4000 kfree(cam);
4001 }
4002}
4003
4004static int __init cpia_init(void)
4005{
4006 printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT,
4007 CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER);
4008
4009 printk(KERN_WARNING "Since in-kernel colorspace conversion is not "
4010 "allowed, it is disabled by default now. Users should fix the "
4011 "applications in case they don't work without conversion "
4012 "reenabled by setting the 'colorspace_conv' module "
Randy Dunlap94190452006-03-27 16:18:25 -03004013 "parameter to 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004014
4015#ifdef CONFIG_PROC_FS
4016 proc_cpia_create();
4017#endif
4018
Linus Torvalds1da177e2005-04-16 15:20:36 -07004019 return 0;
4020}
4021
4022static void __exit cpia_exit(void)
4023{
4024#ifdef CONFIG_PROC_FS
4025 proc_cpia_destroy();
4026#endif
4027}
4028
4029module_init(cpia_init);
4030module_exit(cpia_exit);
4031
4032/* Exported symbols for modules. */
4033
4034EXPORT_SYMBOL(cpia_register_camera);
4035EXPORT_SYMBOL(cpia_unregister_camera);