blob: 43ab0adf3b610a8927299e833c6bb3ab5f3b73ad [file] [log] [blame]
/*
* cpia CPiA driver
*
* Supports CPiA based Video Camera's.
*
* (C) Copyright 1999-2000 Peter Pregler
* (C) Copyright 1999-2000 Scott J. Bertin
* (C) Copyright 1999-2000 Johannes Erdfelt <johannes@erdfelt.com>
* (C) Copyright 2000 STMicroelectronics
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* define _CPIA_DEBUG_ for verbose debug output (see cpia.h) */
/* #define _CPIA_DEBUG_ 1 */
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <linux/proc_fs.h>
#include <linux/ctype.h>
#include <linux/pagemap.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <linux/mutex.h>
#include "cpia.h"
static int video_nr = -1;
#ifdef MODULE
module_param(video_nr, int, 0);
MODULE_AUTHOR("Scott J. Bertin <sbertin@securenym.net> & Peter Pregler <Peter_Pregler@email.com> & Johannes Erdfelt <johannes@erdfelt.com>");
MODULE_DESCRIPTION("V4L-driver for Vision CPiA based cameras");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("video");
#endif
static unsigned short colorspace_conv;
module_param(colorspace_conv, ushort, 0444);
MODULE_PARM_DESC(colorspace_conv,
" Colorspace conversion:"
"\n 0 = disable, 1 = enable"
"\n Default value is 0"
);
#define ABOUT "V4L-Driver for Vision CPiA based cameras"
#define CPIA_MODULE_CPIA (0<<5)
#define CPIA_MODULE_SYSTEM (1<<5)
#define CPIA_MODULE_VP_CTRL (5<<5)
#define CPIA_MODULE_CAPTURE (6<<5)
#define CPIA_MODULE_DEBUG (7<<5)
#define INPUT (DATA_IN << 8)
#define OUTPUT (DATA_OUT << 8)
#define CPIA_COMMAND_GetCPIAVersion (INPUT | CPIA_MODULE_CPIA | 1)
#define CPIA_COMMAND_GetPnPID (INPUT | CPIA_MODULE_CPIA | 2)
#define CPIA_COMMAND_GetCameraStatus (INPUT | CPIA_MODULE_CPIA | 3)
#define CPIA_COMMAND_GotoHiPower (OUTPUT | CPIA_MODULE_CPIA | 4)
#define CPIA_COMMAND_GotoLoPower (OUTPUT | CPIA_MODULE_CPIA | 5)
#define CPIA_COMMAND_GotoSuspend (OUTPUT | CPIA_MODULE_CPIA | 7)
#define CPIA_COMMAND_GotoPassThrough (OUTPUT | CPIA_MODULE_CPIA | 8)
#define CPIA_COMMAND_ModifyCameraStatus (OUTPUT | CPIA_MODULE_CPIA | 10)
#define CPIA_COMMAND_ReadVCRegs (INPUT | CPIA_MODULE_SYSTEM | 1)
#define CPIA_COMMAND_WriteVCReg (OUTPUT | CPIA_MODULE_SYSTEM | 2)
#define CPIA_COMMAND_ReadMCPorts (INPUT | CPIA_MODULE_SYSTEM | 3)
#define CPIA_COMMAND_WriteMCPort (OUTPUT | CPIA_MODULE_SYSTEM | 4)
#define CPIA_COMMAND_SetBaudRate (OUTPUT | CPIA_MODULE_SYSTEM | 5)
#define CPIA_COMMAND_SetECPTiming (OUTPUT | CPIA_MODULE_SYSTEM | 6)
#define CPIA_COMMAND_ReadIDATA (INPUT | CPIA_MODULE_SYSTEM | 7)
#define CPIA_COMMAND_WriteIDATA (OUTPUT | CPIA_MODULE_SYSTEM | 8)
#define CPIA_COMMAND_GenericCall (OUTPUT | CPIA_MODULE_SYSTEM | 9)
#define CPIA_COMMAND_I2CStart (OUTPUT | CPIA_MODULE_SYSTEM | 10)
#define CPIA_COMMAND_I2CStop (OUTPUT | CPIA_MODULE_SYSTEM | 11)
#define CPIA_COMMAND_I2CWrite (OUTPUT | CPIA_MODULE_SYSTEM | 12)
#define CPIA_COMMAND_I2CRead (INPUT | CPIA_MODULE_SYSTEM | 13)
#define CPIA_COMMAND_GetVPVersion (INPUT | CPIA_MODULE_VP_CTRL | 1)
#define CPIA_COMMAND_ResetFrameCounter (INPUT | CPIA_MODULE_VP_CTRL | 2)
#define CPIA_COMMAND_SetColourParams (OUTPUT | CPIA_MODULE_VP_CTRL | 3)
#define CPIA_COMMAND_SetExposure (OUTPUT | CPIA_MODULE_VP_CTRL | 4)
#define CPIA_COMMAND_SetColourBalance (OUTPUT | CPIA_MODULE_VP_CTRL | 6)
#define CPIA_COMMAND_SetSensorFPS (OUTPUT | CPIA_MODULE_VP_CTRL | 7)
#define CPIA_COMMAND_SetVPDefaults (OUTPUT | CPIA_MODULE_VP_CTRL | 8)
#define CPIA_COMMAND_SetApcor (OUTPUT | CPIA_MODULE_VP_CTRL | 9)
#define CPIA_COMMAND_SetFlickerCtrl (OUTPUT | CPIA_MODULE_VP_CTRL | 10)
#define CPIA_COMMAND_SetVLOffset (OUTPUT | CPIA_MODULE_VP_CTRL | 11)
#define CPIA_COMMAND_GetColourParams (INPUT | CPIA_MODULE_VP_CTRL | 16)
#define CPIA_COMMAND_GetColourBalance (INPUT | CPIA_MODULE_VP_CTRL | 17)
#define CPIA_COMMAND_GetExposure (INPUT | CPIA_MODULE_VP_CTRL | 18)
#define CPIA_COMMAND_SetSensorMatrix (OUTPUT | CPIA_MODULE_VP_CTRL | 19)
#define CPIA_COMMAND_ColourBars (OUTPUT | CPIA_MODULE_VP_CTRL | 25)
#define CPIA_COMMAND_ReadVPRegs (INPUT | CPIA_MODULE_VP_CTRL | 30)
#define CPIA_COMMAND_WriteVPReg (OUTPUT | CPIA_MODULE_VP_CTRL | 31)
#define CPIA_COMMAND_GrabFrame (OUTPUT | CPIA_MODULE_CAPTURE | 1)
#define CPIA_COMMAND_UploadFrame (OUTPUT | CPIA_MODULE_CAPTURE | 2)
#define CPIA_COMMAND_SetGrabMode (OUTPUT | CPIA_MODULE_CAPTURE | 3)
#define CPIA_COMMAND_InitStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 4)
#define CPIA_COMMAND_FiniStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 5)
#define CPIA_COMMAND_StartStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 6)
#define CPIA_COMMAND_EndStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 7)
#define CPIA_COMMAND_SetFormat (OUTPUT | CPIA_MODULE_CAPTURE | 8)
#define CPIA_COMMAND_SetROI (OUTPUT | CPIA_MODULE_CAPTURE | 9)
#define CPIA_COMMAND_SetCompression (OUTPUT | CPIA_MODULE_CAPTURE | 10)
#define CPIA_COMMAND_SetCompressionTarget (OUTPUT | CPIA_MODULE_CAPTURE | 11)
#define CPIA_COMMAND_SetYUVThresh (OUTPUT | CPIA_MODULE_CAPTURE | 12)
#define CPIA_COMMAND_SetCompressionParams (OUTPUT | CPIA_MODULE_CAPTURE | 13)
#define CPIA_COMMAND_DiscardFrame (OUTPUT | CPIA_MODULE_CAPTURE | 14)
#define CPIA_COMMAND_GrabReset (OUTPUT | CPIA_MODULE_CAPTURE | 15)
#define CPIA_COMMAND_OutputRS232 (OUTPUT | CPIA_MODULE_DEBUG | 1)
#define CPIA_COMMAND_AbortProcess (OUTPUT | CPIA_MODULE_DEBUG | 4)
#define CPIA_COMMAND_SetDramPage (OUTPUT | CPIA_MODULE_DEBUG | 5)
#define CPIA_COMMAND_StartDramUpload (OUTPUT | CPIA_MODULE_DEBUG | 6)
#define CPIA_COMMAND_StartDummyDtream (OUTPUT | CPIA_MODULE_DEBUG | 8)
#define CPIA_COMMAND_AbortStream (OUTPUT | CPIA_MODULE_DEBUG | 9)
#define CPIA_COMMAND_DownloadDRAM (OUTPUT | CPIA_MODULE_DEBUG | 10)
#define CPIA_COMMAND_Null (OUTPUT | CPIA_MODULE_DEBUG | 11)
enum {
FRAME_READY, /* Ready to grab into */
FRAME_GRABBING, /* In the process of being grabbed into */
FRAME_DONE, /* Finished grabbing, but not been synced yet */
FRAME_UNUSED, /* Unused (no MCAPTURE) */
};
#define COMMAND_NONE 0x0000
#define COMMAND_SETCOMPRESSION 0x0001
#define COMMAND_SETCOMPRESSIONTARGET 0x0002
#define COMMAND_SETCOLOURPARAMS 0x0004
#define COMMAND_SETFORMAT 0x0008
#define COMMAND_PAUSE 0x0010
#define COMMAND_RESUME 0x0020
#define COMMAND_SETYUVTHRESH 0x0040
#define COMMAND_SETECPTIMING 0x0080
#define COMMAND_SETCOMPRESSIONPARAMS 0x0100
#define COMMAND_SETEXPOSURE 0x0200
#define COMMAND_SETCOLOURBALANCE 0x0400
#define COMMAND_SETSENSORFPS 0x0800
#define COMMAND_SETAPCOR 0x1000
#define COMMAND_SETFLICKERCTRL 0x2000
#define COMMAND_SETVLOFFSET 0x4000
#define COMMAND_SETLIGHTS 0x8000
#define ROUND_UP_EXP_FOR_FLICKER 15
/* Constants for automatic frame rate adjustment */
#define MAX_EXP 302
#define MAX_EXP_102 255
#define LOW_EXP 140
#define VERY_LOW_EXP 70
#define TC 94
#define EXP_ACC_DARK 50
#define EXP_ACC_LIGHT 90
#define HIGH_COMP_102 160
#define MAX_COMP 239
#define DARK_TIME 3
#define LIGHT_TIME 3
/* Maximum number of 10ms loops to wait for the stream to become ready */
#define READY_TIMEOUT 100
/* Developer's Guide Table 5 p 3-34
* indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/
static u8 flicker_jumps[2][2][4] =
{ { { 76, 38, 19, 9 }, { 92, 46, 23, 11 } },
{ { 64, 32, 16, 8 }, { 76, 38, 19, 9} }
};
/* forward declaration of local function */
static void reset_camera_struct(struct cam_data *cam);
static int find_over_exposure(int brightness);
static void set_flicker(struct cam_params *params, volatile u32 *command_flags,
int on);
/**********************************************************************
*
* Memory management
*
**********************************************************************/
static void *rvmalloc(unsigned long size)
{
void *mem;
unsigned long adr;
size = PAGE_ALIGN(size);
mem = vmalloc_32(size);
if (!mem)
return NULL;
memset(mem, 0, size); /* Clear the ram out, no junk to the user */
adr = (unsigned long) mem;
while (size > 0) {
SetPageReserved(vmalloc_to_page((void *)adr));
adr += PAGE_SIZE;
size -= PAGE_SIZE;
}
return mem;
}
static void rvfree(void *mem, unsigned long size)
{
unsigned long adr;
if (!mem)
return;
adr = (unsigned long) mem;
while ((long) size > 0) {
ClearPageReserved(vmalloc_to_page((void *)adr));
adr += PAGE_SIZE;
size -= PAGE_SIZE;
}
vfree(mem);
}
/**********************************************************************
*
* /proc interface
*
**********************************************************************/
#ifdef CONFIG_PROC_FS
static struct proc_dir_entry *cpia_proc_root=NULL;
static int cpia_read_proc(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
char *out = page;
int len, tmp;
struct cam_data *cam = data;
char tmpstr[29];
/* IMPORTANT: This output MUST be kept under PAGE_SIZE
* or we need to get more sophisticated. */
out += sprintf(out, "read-only\n-----------------------\n");
out += sprintf(out, "V4L Driver version: %d.%d.%d\n",
CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER);
out += sprintf(out, "CPIA Version: %d.%02d (%d.%d)\n",
cam->params.version.firmwareVersion,
cam->params.version.firmwareRevision,
cam->params.version.vcVersion,
cam->params.version.vcRevision);
out += sprintf(out, "CPIA PnP-ID: %04x:%04x:%04x\n",
cam->params.pnpID.vendor, cam->params.pnpID.product,
cam->params.pnpID.deviceRevision);
out += sprintf(out, "VP-Version: %d.%d %04x\n",
cam->params.vpVersion.vpVersion,
cam->params.vpVersion.vpRevision,
cam->params.vpVersion.cameraHeadID);
out += sprintf(out, "system_state: %#04x\n",
cam->params.status.systemState);
out += sprintf(out, "grab_state: %#04x\n",
cam->params.status.grabState);
out += sprintf(out, "stream_state: %#04x\n",
cam->params.status.streamState);
out += sprintf(out, "fatal_error: %#04x\n",
cam->params.status.fatalError);
out += sprintf(out, "cmd_error: %#04x\n",
cam->params.status.cmdError);
out += sprintf(out, "debug_flags: %#04x\n",
cam->params.status.debugFlags);
out += sprintf(out, "vp_status: %#04x\n",
cam->params.status.vpStatus);
out += sprintf(out, "error_code: %#04x\n",
cam->params.status.errorCode);
/* QX3 specific entries */
if (cam->params.qx3.qx3_detected) {
out += sprintf(out, "button: %4d\n",
cam->params.qx3.button);
out += sprintf(out, "cradled: %4d\n",
cam->params.qx3.cradled);
}
out += sprintf(out, "video_size: %s\n",
cam->params.format.videoSize == VIDEOSIZE_CIF ?
"CIF " : "QCIF");
out += sprintf(out, "roi: (%3d, %3d) to (%3d, %3d)\n",
cam->params.roi.colStart*8,
cam->params.roi.rowStart*4,
cam->params.roi.colEnd*8,
cam->params.roi.rowEnd*4);
out += sprintf(out, "actual_fps: %3d\n", cam->fps);
out += sprintf(out, "transfer_rate: %4dkB/s\n",
cam->transfer_rate);
out += sprintf(out, "\nread-write\n");
out += sprintf(out, "----------------------- current min"
" max default comment\n");
out += sprintf(out, "brightness: %8d %8d %8d %8d\n",
cam->params.colourParams.brightness, 0, 100, 50);
if (cam->params.version.firmwareVersion == 1 &&
cam->params.version.firmwareRevision == 2)
/* 1-02 firmware limits contrast to 80 */
tmp = 80;
else
tmp = 96;
out += sprintf(out, "contrast: %8d %8d %8d %8d"
" steps of 8\n",
cam->params.colourParams.contrast, 0, tmp, 48);
out += sprintf(out, "saturation: %8d %8d %8d %8d\n",
cam->params.colourParams.saturation, 0, 100, 50);
tmp = (25000+5000*cam->params.sensorFps.baserate)/
(1<<cam->params.sensorFps.divisor);
out += sprintf(out, "sensor_fps: %4d.%03d %8d %8d %8d\n",
tmp/1000, tmp%1000, 3, 30, 15);
out += sprintf(out, "stream_start_line: %8d %8d %8d %8d\n",
2*cam->params.streamStartLine, 0,
cam->params.format.videoSize == VIDEOSIZE_CIF ? 288:144,
cam->params.format.videoSize == VIDEOSIZE_CIF ? 240:120);
out += sprintf(out, "sub_sample: %8s %8s %8s %8s\n",
cam->params.format.subSample == SUBSAMPLE_420 ?
"420" : "422", "420", "422", "422");
out += sprintf(out, "yuv_order: %8s %8s %8s %8s\n",
cam->params.format.yuvOrder == YUVORDER_YUYV ?
"YUYV" : "UYVY", "YUYV" , "UYVY", "YUYV");
out += sprintf(out, "ecp_timing: %8s %8s %8s %8s\n",
cam->params.ecpTiming ? "slow" : "normal", "slow",
"normal", "normal");
if (cam->params.colourBalance.balanceMode == 2) {
sprintf(tmpstr, "auto");
} else {
sprintf(tmpstr, "manual");
}
out += sprintf(out, "color_balance_mode: %8s %8s %8s"
" %8s\n", tmpstr, "manual", "auto", "auto");
out += sprintf(out, "red_gain: %8d %8d %8d %8d\n",
cam->params.colourBalance.redGain, 0, 212, 32);
out += sprintf(out, "green_gain: %8d %8d %8d %8d\n",
cam->params.colourBalance.greenGain, 0, 212, 6);
out += sprintf(out, "blue_gain: %8d %8d %8d %8d\n",
cam->params.colourBalance.blueGain, 0, 212, 92);
if (cam->params.version.firmwareVersion == 1 &&
cam->params.version.firmwareRevision == 2)
/* 1-02 firmware limits gain to 2 */
sprintf(tmpstr, "%8d %8d %8d", 1, 2, 2);
else
sprintf(tmpstr, "%8d %8d %8d", 1, 8, 2);
if (cam->params.exposure.gainMode == 0)
out += sprintf(out, "max_gain: unknown %28s"
" powers of 2\n", tmpstr);
else
out += sprintf(out, "max_gain: %8d %28s"
" 1,2,4 or 8 \n",
1<<(cam->params.exposure.gainMode-1), tmpstr);
switch(cam->params.exposure.expMode) {
case 1:
case 3:
sprintf(tmpstr, "manual");
break;
case 2:
sprintf(tmpstr, "auto");
break;
default:
sprintf(tmpstr, "unknown");
break;
}
out += sprintf(out, "exposure_mode: %8s %8s %8s"
" %8s\n", tmpstr, "manual", "auto", "auto");
out += sprintf(out, "centre_weight: %8s %8s %8s %8s\n",
(2-cam->params.exposure.centreWeight) ? "on" : "off",
"off", "on", "on");
out += sprintf(out, "gain: %8d %8d max_gain %8d 1,2,4,8 possible\n",
1<<cam->params.exposure.gain, 1, 1);
if (cam->params.version.firmwareVersion == 1 &&
cam->params.version.firmwareRevision == 2)
/* 1-02 firmware limits fineExp/2 to 127 */
tmp = 254;
else
tmp = 510;
out += sprintf(out, "fine_exp: %8d %8d %8d %8d\n",
cam->params.exposure.fineExp*2, 0, tmp, 0);
if (cam->params.version.firmwareVersion == 1 &&
cam->params.version.firmwareRevision == 2)
/* 1-02 firmware limits coarseExpHi to 0 */
tmp = MAX_EXP_102;
else
tmp = MAX_EXP;
out += sprintf(out, "coarse_exp: %8d %8d %8d"
" %8d\n", cam->params.exposure.coarseExpLo+
256*cam->params.exposure.coarseExpHi, 0, tmp, 185);
out += sprintf(out, "red_comp: %8d %8d %8d %8d\n",
cam->params.exposure.redComp, COMP_RED, 255, COMP_RED);
out += sprintf(out, "green1_comp: %8d %8d %8d %8d\n",
cam->params.exposure.green1Comp, COMP_GREEN1, 255,
COMP_GREEN1);
out += sprintf(out, "green2_comp: %8d %8d %8d %8d\n",
cam->params.exposure.green2Comp, COMP_GREEN2, 255,
COMP_GREEN2);
out += sprintf(out, "blue_comp: %8d %8d %8d %8d\n",
cam->params.exposure.blueComp, COMP_BLUE, 255, COMP_BLUE);
out += sprintf(out, "apcor_gain1: %#8x %#8x %#8x %#8x\n",
cam->params.apcor.gain1, 0, 0xff, 0x1c);
out += sprintf(out, "apcor_gain2: %#8x %#8x %#8x %#8x\n",
cam->params.apcor.gain2, 0, 0xff, 0x1a);
out += sprintf(out, "apcor_gain4: %#8x %#8x %#8x %#8x\n",
cam->params.apcor.gain4, 0, 0xff, 0x2d);
out += sprintf(out, "apcor_gain8: %#8x %#8x %#8x %#8x\n",
cam->params.apcor.gain8, 0, 0xff, 0x2a);
out += sprintf(out, "vl_offset_gain1: %8d %8d %8d %8d\n",
cam->params.vlOffset.gain1, 0, 255, 24);
out += sprintf(out, "vl_offset_gain2: %8d %8d %8d %8d\n",
cam->params.vlOffset.gain2, 0, 255, 28);
out += sprintf(out, "vl_offset_gain4: %8d %8d %8d %8d\n",
cam->params.vlOffset.gain4, 0, 255, 30);
out += sprintf(out, "vl_offset_gain8: %8d %8d %8d %8d\n",
cam->params.vlOffset.gain8, 0, 255, 30);
out += sprintf(out, "flicker_control: %8s %8s %8s %8s\n",
cam->params.flickerControl.flickerMode ? "on" : "off",
"off", "on", "off");
out += sprintf(out, "mains_frequency: %8d %8d %8d %8d"
" only 50/60\n",
cam->mainsFreq ? 60 : 50, 50, 60, 50);
if(cam->params.flickerControl.allowableOverExposure < 0)
out += sprintf(out, "allowable_overexposure: %4dauto auto %8d auto\n",
-cam->params.flickerControl.allowableOverExposure,
255);
else
out += sprintf(out, "allowable_overexposure: %8d auto %8d auto\n",
cam->params.flickerControl.allowableOverExposure,
255);
out += sprintf(out, "compression_mode: ");
switch(cam->params.compression.mode) {
case CPIA_COMPRESSION_NONE:
out += sprintf(out, "%8s", "none");
break;
case CPIA_COMPRESSION_AUTO:
out += sprintf(out, "%8s", "auto");
break;
case CPIA_COMPRESSION_MANUAL:
out += sprintf(out, "%8s", "manual");
break;
default:
out += sprintf(out, "%8s", "unknown");
break;
}
out += sprintf(out, " none,auto,manual auto\n");
out += sprintf(out, "decimation_enable: %8s %8s %8s %8s\n",
cam->params.compression.decimation ==
DECIMATION_ENAB ? "on":"off", "off", "on",
"off");
out += sprintf(out, "compression_target: %9s %9s %9s %9s\n",
cam->params.compressionTarget.frTargeting ==
CPIA_COMPRESSION_TARGET_FRAMERATE ?
"framerate":"quality",
"framerate", "quality", "quality");
out += sprintf(out, "target_framerate: %8d %8d %8d %8d\n",
cam->params.compressionTarget.targetFR, 1, 30, 15);
out += sprintf(out, "target_quality: %8d %8d %8d %8d\n",
cam->params.compressionTarget.targetQ, 1, 64, 5);
out += sprintf(out, "y_threshold: %8d %8d %8d %8d\n",
cam->params.yuvThreshold.yThreshold, 0, 31, 6);
out += sprintf(out, "uv_threshold: %8d %8d %8d %8d\n",
cam->params.yuvThreshold.uvThreshold, 0, 31, 6);
out += sprintf(out, "hysteresis: %8d %8d %8d %8d\n",
cam->params.compressionParams.hysteresis, 0, 255, 3);
out += sprintf(out, "threshold_max: %8d %8d %8d %8d\n",
cam->params.compressionParams.threshMax, 0, 255, 11);
out += sprintf(out, "small_step: %8d %8d %8d %8d\n",
cam->params.compressionParams.smallStep, 0, 255, 1);
out += sprintf(out, "large_step: %8d %8d %8d %8d\n",
cam->params.compressionParams.largeStep, 0, 255, 3);
out += sprintf(out, "decimation_hysteresis: %8d %8d %8d %8d\n",
cam->params.compressionParams.decimationHysteresis,
0, 255, 2);
out += sprintf(out, "fr_diff_step_thresh: %8d %8d %8d %8d\n",
cam->params.compressionParams.frDiffStepThresh,
0, 255, 5);
out += sprintf(out, "q_diff_step_thresh: %8d %8d %8d %8d\n",
cam->params.compressionParams.qDiffStepThresh,
0, 255, 3);
out += sprintf(out, "decimation_thresh_mod: %8d %8d %8d %8d\n",
cam->params.compressionParams.decimationThreshMod,
0, 255, 2);
/* QX3 specific entries */
if (cam->params.qx3.qx3_detected) {
out += sprintf(out, "toplight: %8s %8s %8s %8s\n",
cam->params.qx3.toplight ? "on" : "off",
"off", "on", "off");
out += sprintf(out, "bottomlight: %8s %8s %8s %8s\n",
cam->params.qx3.bottomlight ? "on" : "off",
"off", "on", "off");
}
len = out - page;
len -= off;
if (len < count) {
*eof = 1;
if (len <= 0) return 0;
} else
len = count;
*start = page + off;
return len;
}
static int match(char *checkstr, char **buffer, unsigned long *count,
int *find_colon, int *err)
{
int ret, colon_found = 1;
int len = strlen(checkstr);
ret = (len <= *count && strncmp(*buffer, checkstr, len) == 0);
if (ret) {
*buffer += len;
*count -= len;
if (*find_colon) {
colon_found = 0;
while (*count && (**buffer == ' ' || **buffer == '\t' ||
(!colon_found && **buffer == ':'))) {
if (**buffer == ':')
colon_found = 1;
--*count;
++*buffer;
}
if (!*count || !colon_found)
*err = -EINVAL;
*find_colon = 0;
}
}
return ret;
}
static unsigned long int value(char **buffer, unsigned long *count, int *err)
{
char *p;
unsigned long int ret;
ret = simple_strtoul(*buffer, &p, 0);
if (p == *buffer)
*err = -EINVAL;
else {
*count -= p - *buffer;
*buffer = p;
}
return ret;
}
static int cpia_write_proc(struct file *file, const char __user *buf,
unsigned long count, void *data)
{
struct cam_data *cam = data;
struct cam_params new_params;
char *page, *buffer;
int retval, find_colon;
int size = count;
unsigned long val = 0;
u32 command_flags = 0;
u8 new_mains;
/*
* This code to copy from buf to page is shamelessly copied
* from the comx driver
*/
if (count > PAGE_SIZE) {
printk(KERN_ERR "count is %lu > %d!!!\n", count, (int)PAGE_SIZE);
return -ENOSPC;
}
if (!(page = (char *)__get_free_page(GFP_KERNEL))) return -ENOMEM;
if(copy_from_user(page, buf, count))
{
retval = -EFAULT;
goto out;
}
if (page[count-1] == '\n')
page[count-1] = '\0';
else if (count < PAGE_SIZE)
page[count] = '\0';
else if (page[count]) {
retval = -EINVAL;
goto out;
}
buffer = page;
if (mutex_lock_interruptible(&cam->param_lock))
return -ERESTARTSYS;
/*
* Skip over leading whitespace
*/
while (count && isspace(*buffer)) {
--count;
++buffer;
}
memcpy(&new_params, &cam->params, sizeof(struct cam_params));
new_mains = cam->mainsFreq;
#define MATCH(x) (match(x, &buffer, &count, &find_colon, &retval))
#define VALUE (value(&buffer,&count, &retval))
#define FIRMWARE_VERSION(x,y) (new_params.version.firmwareVersion == (x) && \
new_params.version.firmwareRevision == (y))
retval = 0;
while (count && !retval) {
find_colon = 1;
if (MATCH("brightness")) {
if (!retval)
val = VALUE;
if (!retval) {
if (val <= 100)
new_params.colourParams.brightness = val;
else
retval = -EINVAL;
}
command_flags |= COMMAND_SETCOLOURPARAMS;
if(new_params.flickerControl.allowableOverExposure < 0)
new_params.flickerControl.allowableOverExposure =
-find_over_exposure(new_params.colourParams.brightness);
if(new_params.flickerControl.flickerMode != 0)
command_flags |= COMMAND_SETFLICKERCTRL;
} else if (MATCH("contrast")) {
if (!retval)
val = VALUE;
if (!retval) {
if (val <= 100) {
/* contrast is in steps of 8, so round*/
val = ((val + 3) / 8) * 8;
/* 1-02 firmware limits contrast to 80*/
if (FIRMWARE_VERSION(1,2) && val > 80)
val = 80;
new_params.colourParams.contrast = val;
} else
retval = -EINVAL;
}
command_flags |= COMMAND_SETCOLOURPARAMS;
} else if (MATCH("saturation")) {
if (!retval)
val = VALUE;
if (!retval) {
if (val <= 100)
new_params.colourParams.saturation = val;
else
retval = -EINVAL;
}
command_flags |= COMMAND_SETCOLOURPARAMS;
} else if (MATCH("sensor_fps")) {
if (!retval)
val = VALUE;
if (!retval) {
/* find values so that sensorFPS is minimized,
* but >= val */
if (val > 30)
retval = -EINVAL;
else if (val > 25) {
new_params.sensorFps.divisor = 0;
new_params.sensorFps.baserate = 1;
} else if (val > 15) {
new_params.sensorFps.divisor = 0;
new_params.sensorFps.baserate = 0;
} else if (val > 12) {
new_params.sensorFps.divisor = 1;
new_params.sensorFps.baserate = 1;
} else if (val > 7) {
new_params.sensorFps.divisor = 1;
new_params.sensorFps.baserate = 0;
} else if (val > 6) {
new_params.sensorFps.divisor = 2;
new_params.sensorFps.baserate = 1;
} else if (val > 3) {
new_params.sensorFps.divisor = 2;
new_params.sensorFps.baserate = 0;
} else {
new_params.sensorFps.divisor = 3;
/* Either base rate would work here */
new_params.sensorFps.baserate = 1;
}
new_params.flickerControl.coarseJump =
flicker_jumps[new_mains]
[new_params.sensorFps.baserate]
[new_params.sensorFps.divisor];
if (new_params.flickerControl.flickerMode)
command_flags |= COMMAND_SETFLICKERCTRL;
}
command_flags |= COMMAND_SETSENSORFPS;
cam->exposure_status = EXPOSURE_NORMAL;
} else if (MATCH("stream_start_line")) {
if (!retval)
val = VALUE;
if (!retval) {
int max_line = 288;
if (new_params.format.videoSize == VIDEOSIZE_QCIF)
max_line = 144;
if (val <= max_line)
new_params.streamStartLine = val/2;
else
retval = -EINVAL;
}
} else if (MATCH("sub_sample")) {
if (!retval && MATCH("420"))
new_params.format.subSample = SUBSAMPLE_420;
else if (!retval && MATCH("422"))
new_params.format.subSample = SUBSAMPLE_422;
else
retval = -EINVAL;
command_flags |= COMMAND_SETFORMAT;
} else if (MATCH("yuv_order")) {
if (!retval && MATCH("YUYV"))
new_params.format.yuvOrder = YUVORDER_YUYV;
else if (!retval && MATCH("UYVY"))
new_params.format.yuvOrder = YUVORDER_UYVY;
else
retval = -EINVAL;
command_flags |= COMMAND_SETFORMAT;
} else if (MATCH("ecp_timing")) {
if (!retval && MATCH("normal"))
new_params.ecpTiming = 0;
else if (!retval && MATCH("slow"))
new_params.ecpTiming = 1;
else
retval = -EINVAL;
command_flags |= COMMAND_SETECPTIMING;
} else if (MATCH("color_balance_mode")) {
if (!retval && MATCH("manual"))
new_params.colourBalance.balanceMode = 3;
else if (!retval && MATCH("auto"))
new_params.colourBalance.balanceMode = 2;
else
retval = -EINVAL;
command_flags |= COMMAND_SETCOLOURBALANCE;
} else if (MATCH("red_gain")) {
if (!retval)
val = VALUE;
if (!retval) {
if (val <= 212) {
new_params.colourBalance.redGain = val;
new_params.colourBalance.balanceMode = 1;
} else
retval = -EINVAL;
}
command_flags |= COMMAND_SETCOLOURBALANCE;
} else if (MATCH("green_gain")) {
if (!retval)
val = VALUE;
if (!retval) {
if (val <= 212) {
new_params.colourBalance.greenGain = val;
new_params.colourBalance.balanceMode = 1;
} else
retval = -EINVAL;
}
command_flags |= COMMAND_SETCOLOURBALANCE;
} else if (MATCH("blue_gain")) {
if (!retval)
val = VALUE;
if (!retval) {
if (val <= 212) {
new_params.colourBalance.blueGain = val;
new_params.colourBalance.balanceMode = 1;
} else
retval = -EINVAL;
}
command_flags |= COMMAND_SETCOLOURBALANCE;
} else if (MATCH("max_gain")) {
if (!retval)
val = VALUE;
if (!retval) {
/* 1-02 firmware limits gain to 2 */
if (FIRMWARE_VERSION(1,2) && val > 2)
val = 2;
switch(val) {
case 1:
new_params.exposure.gainMode = 1;
break;
case 2:
new_params.exposure.gainMode = 2;
break;
case 4:
new_params.exposure.gainMode = 3;
break;
case 8:
new_params.exposure.gainMode = 4;
break;
default:
retval = -EINVAL;
break;
}
}
command_flags |= COMMAND_SETEXPOSURE;
} else if (MATCH("exposure_mode")) {
if (!retval && MATCH("auto"))
new_params.exposure.expMode = 2;
else if (!retval && MATCH("manual")) {
if (new_params.exposure.expMode == 2)
new_params.exposure.expMode = 3;
if(new_params.flickerControl.flickerMode != 0)
command_flags |= COMMAND_SETFLICKERCTRL;
new_params.flickerControl.flickerMode = 0;
} else
retval = -EINVAL;
command_flags |= COMMAND_SETEXPOSURE;
} else if (MATCH("centre_weight")) {
if (!retval && MATCH("on"))
new_params.exposure.centreWeight = 1;
else if (!retval && MATCH("off"))
new_params.exposure.centreWeight = 2;
else
retval = -EINVAL;
command_flags |= COMMAND_SETEXPOSURE;
} else if (MATCH("gain")) {
if (!retval)
val = VALUE;
if (!retval) {
switch(val) {
case 1:
new_params.exposure.gain = 0;
break;
case 2:
new_params.exposure.gain = 1;
break;
case 4:
new_params.exposure.gain = 2;
break;
case 8:
new_params.exposure.gain = 3;
break;
default:
retval = -EINVAL;
break;
}
new_params.exposure.expMode = 1;
if(new_params.flickerControl.flickerMode != 0)
command_flags |= COMMAND_SETFLICKERCTRL;
new_params.flickerControl.flickerMode = 0;
command_flags |= COMMAND_SETEXPOSURE;
if (new_params.exposure.gain >
new_params.exposure.gainMode-1)
retval = -EINVAL;
}
} else if (MATCH("fine_exp")) {
if (!retval)
val = VALUE/2;
if (!retval) {
if (val < 256) {
/* 1-02 firmware limits fineExp/2 to 127*/
if (FIRMWARE_VERSION(1,2) && val > 127)
val = 127;
new_params.exposure.fineExp = val;
new_params.exposure.expMode = 1;
command_flags |= COMMAND_SETEXPOSURE;
if(new_params.flickerControl.flickerMode != 0)
command_flags |= COMMAND_SETFLICKERCTRL;
new_params.flickerControl.flickerMode = 0;
command_flags |= COMMAND_SETFLICKERCTRL;
} else
retval = -EINVAL;
}
} else if (MATCH("coarse_exp")) {
if (!retval)
val = VALUE;
if (!retval) {
if (val <= MAX_EXP) {
if (FIRMWARE_VERSION(1,2) &&
val > MAX_EXP_102)
val = MAX_EXP_102;
new_params.exposure.coarseExpLo =
val & 0xff;
new_params.exposure.coarseExpHi =
val >> 8;
new_params.exposure.expMode = 1;
command_flags |= COMMAND_SETEXPOSURE;
if(new_params.flickerControl.flickerMode != 0)
command_flags |= COMMAND_SETFLICKERCTRL;
new_params.flickerControl.flickerMode = 0;
command_flags |= COMMAND_SETFLICKERCTRL;
} else
retval = -EINVAL;
}
} else if (MATCH("red_comp")) {
if (!retval)
val = VALUE;
if (!retval) {
if (val >= COMP_RED && val <= 255) {
new_params.exposure.redComp = val;
new_params.exposure.compMode = 1;
command_flags |= COMMAND_SETEXPOSURE;
} else
retval = -EINVAL;
}
} else if (MATCH("green1_comp")) {
if (!retval)
val = VALUE;
if (!retval) {
if (val >= COMP_GREEN1 && val <= 255) {
new_params.exposure.green1Comp = val;
new_params.exposure.compMode = 1;
command_flags |= COMMAND_SETEXPOSURE;
} else
retval = -EINVAL;
}
} else if (MATCH("green2_comp")) {
if (!retval)
val = VALUE;
if (!retval) {
if (val >= COMP_GREEN2 && val <= 255) {
new_params.exposure.green2Comp = val;
new_params.exposure.compMode = 1;
command_flags |= COMMAND_SETEXPOSURE;
} else
retval = -EINVAL;
}
} else if (MATCH("blue_comp")) {
if (!retval)
val = VALUE;
if (!retval) {
if (val >= COMP_BLUE && val <= 255) {
new_params.exposure.blueComp = val;
new_params.exposure.compMode = 1;
command_flags |= COMMAND_SETEXPOSURE;
} else
retval = -EINVAL;
}
} else if (MATCH("apcor_gain1")) {
if (!retval)
val = VALUE;
if (!retval) {
command_flags |= COMMAND_SETAPCOR;
if (val <= 0xff)
new_params.apcor.gain1 = val;
else
retval = -EINVAL;
}
} else if (MATCH("apcor_gain2")) {
if (!retval)
val = VALUE;
if (!retval) {
command_flags |= COMMAND_SETAPCOR;
if (val <= 0xff)
new_params.apcor.gain2 = val;
else
retval = -EINVAL;
}
} else if (MATCH("apcor_gain4")) {
if (!retval)
val = VALUE;
if (!retval) {
command_flags |= COMMAND_SETAPCOR;
if (val <= 0xff)
new_params.apcor.gain4 = val;
else
retval = -EINVAL;
}
} else if (MATCH("apcor_gain8")) {
if (!retval)
val = VALUE;
if (!retval) {
command_flags |= COMMAND_SETAPCOR;
if (val <= 0xff)
new_params.apcor.gain8 = val;
else
retval = -EINVAL;
}
} else if (MATCH("vl_offset_gain1")) {
if (!retval)
val = VALUE;
if (!retval) {
if (val <= 0xff)
new_params.vlOffset.gain1 = val;
else
retval = -EINVAL;
}
command_flags |= COMMAND_SETVLOFFSET;
} else if (MATCH("vl_offset_gain2")) {
if (!retval)
val = VALUE;
if (!retval) {
if (val <= 0xff)
new_params.vlOffset.gain2 = val;
else
retval = -EINVAL;
}
command_flags |= COMMAND_SETVLOFFSET;
} else if (MATCH("vl_offset_gain4")) {
if (!retval)
val = VALUE;
if (!retval) {
if (val <= 0xff)
new_params.vlOffset.gain4 = val;
else
retval = -EINVAL;
}
command_flags |= COMMAND_SETVLOFFSET;
} else if (MATCH("vl_offset_gain8")) {
if (!retval)
val = VALUE;
if (!retval) {
if (val <= 0xff)
new_params.vlOffset.gain8 = val;
else
retval = -EINVAL;
}
command_flags |= COMMAND_SETVLOFFSET;
} else if (MATCH("flicker_control")) {
if (!retval && MATCH("on")) {
set_flicker(&new_params, &command_flags, 1);
} else if (!retval && MATCH("off")) {
set_flicker(&new_params, &command_flags, 0);
} else
retval = -EINVAL;
command_flags |= COMMAND_SETFLICKERCTRL;
} else if (MATCH("mains_frequency")) {
if (!retval && MATCH("50")) {
new_mains = 0;
new_params.flickerControl.coarseJump =
flicker_jumps[new_mains]
[new_params.sensorFps.baserate]
[new_params.sensorFps.divisor];
if (new_params.flickerControl.flickerMode)
command_flags |= COMMAND_SETFLICKERCTRL;
} else if (!retval && MATCH("60")) {
new_mains = 1;
new_params.flickerControl.coarseJump =
flicker_jumps[new_mains]
[new_params.sensorFps.baserate]
[new_params.sensorFps.divisor];
if (new_params.flickerControl.flickerMode)
command_flags |= COMMAND_SETFLICKERCTRL;
} else
retval = -EINVAL;
} else if (MATCH("allowable_overexposure")) {
if (!retval && MATCH("auto")) {
new_params.flickerControl.allowableOverExposure =
-find_over_exposure(new_params.colourParams.brightness);
if(new_params.flickerControl.flickerMode != 0)
command_flags |= COMMAND_SETFLICKERCTRL;
} else {
if (!retval)
val = VALUE;
if (!retval) {
if (val <= 0xff) {
new_params.flickerControl.
allowableOverExposure = val;
if(new_params.flickerControl.flickerMode != 0)
command_flags |= COMMAND_SETFLICKERCTRL;
} else
retval = -EINVAL;
}
}
} else if (MATCH("compression_mode")) {
if (!retval && MATCH("none"))
new_params.compression.mode =
CPIA_COMPRESSION_NONE;
else if (!retval && MATCH("auto"))
new_params.compression.mode =
CPIA_COMPRESSION_AUTO;
else if (!retval && MATCH("manual"))
new_params.compression.mode =
CPIA_COMPRESSION_MANUAL;
else
retval = -EINVAL;
command_flags |= COMMAND_SETCOMPRESSION;
} else if (MATCH("decimation_enable")) {
if (!retval && MATCH("off"))
new_params.compression.decimation = 0;
else if (!retval && MATCH("on"))
new_params.compression.decimation = 1;
else
retval = -EINVAL;
command_flags |= COMMAND_SETCOMPRESSION;
} else if (MATCH("compression_target")) {
if (!retval && MATCH("quality"))
new_params.compressionTarget.frTargeting =
CPIA_COMPRESSION_TARGET_QUALITY;
else if (!retval && MATCH("framerate"))
new_params.compressionTarget.frTargeting =
CPIA_COMPRESSION_TARGET_FRAMERATE;
else
retval = -EINVAL;
command_flags |= COMMAND_SETCOMPRESSIONTARGET;
} else if (MATCH("target_framerate")) {
if (!retval)
val = VALUE;
if (!retval) {
if(val > 0 && val <= 30)
new_params.compressionTarget.targetFR = val;
else
retval = -EINVAL;
}
command_flags |= COMMAND_SETCOMPRESSIONTARGET;
} else if (MATCH("target_quality")) {
if (!retval)
val = VALUE;
if (!retval) {
if(val > 0 && val <= 64)
new_params.compressionTarget.targetQ = val;
else
retval = -EINVAL;
}
command_flags |= COMMAND_SETCOMPRESSIONTARGET;
} else if (MATCH("y_threshold")) {
if (!retval)
val = VALUE;
if (!retval) {
if (val < 32)
new_params.yuvThreshold.yThreshold = val;
else
retval = -EINVAL;
}
command_flags |= COMMAND_SETYUVTHRESH;
} else if (MATCH("uv_threshold")) {
if (!retval)
val = VALUE;
if (!retval) {
if (val < 32)
new_params.yuvThreshold.uvThreshold = val;
else
retval = -EINVAL;
}
command_flags |= COMMAND_SETYUVTHRESH;
} else if (MATCH("hysteresis")) {
if (!retval)
val = VALUE;
if (!retval) {
if (val <= 0xff)
new_params.compressionParams.hysteresis = val;
else
retval = -EINVAL;
}
command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
} else if (MATCH("threshold_max")) {
if (!retval)
val = VALUE;
if (!retval) {
if (val <= 0xff)
new_params.compressionParams.threshMax = val;
else
retval = -EINVAL;
}
command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
} else if (MATCH("small_step")) {
if (!retval)
val = VALUE;
if (!retval) {
if (val <= 0xff)
new_params.compressionParams.smallStep = val;
else
retval = -EINVAL;
}
command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
} else if (MATCH("large_step")) {
if (!retval)
val = VALUE;
if (!retval) {
if (val <= 0xff)
new_params.compressionParams.largeStep = val;
else
retval = -EINVAL;
}
command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
} else if (MATCH("decimation_hysteresis")) {
if (!retval)
val = VALUE;
if (!retval) {
if (val <= 0xff)
new_params.compressionParams.decimationHysteresis = val;
else
retval = -EINVAL;
}
command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
} else if (MATCH("fr_diff_step_thresh")) {
if (!retval)
val = VALUE;
if (!retval) {
if (val <= 0xff)
new_params.compressionParams.frDiffStepThresh = val;
else
retval = -EINVAL;
}
command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
} else if (MATCH("q_diff_step_thresh")) {
if (!retval)
val = VALUE;
if (!retval) {
if (val <= 0xff)
new_params.compressionParams.qDiffStepThresh = val;
else
retval = -EINVAL;
}
command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
} else if (MATCH("decimation_thresh_mod")) {
if (!retval)
val = VALUE;
if (!retval) {
if (val <= 0xff)
new_params.compressionParams.decimationThreshMod = val;
else
retval = -EINVAL;
}
command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
} else if (MATCH("toplight")) {
if (!retval && MATCH("on"))
new_params.qx3.toplight = 1;
else if (!retval && MATCH("off"))
new_params.qx3.toplight = 0;
else
retval = -EINVAL;
command_flags |= COMMAND_SETLIGHTS;
} else if (MATCH("bottomlight")) {
if (!retval && MATCH("on"))
new_params.qx3.bottomlight = 1;
else if (!retval && MATCH("off"))
new_params.qx3.bottomlight = 0;
else
retval = -EINVAL;
command_flags |= COMMAND_SETLIGHTS;
} else {
DBG("No match found\n");
retval = -EINVAL;
}
if (!retval) {
while (count && isspace(*buffer) && *buffer != '\n') {
--count;
++buffer;
}
if (count) {
if (*buffer == '\0' && count != 1)
retval = -EINVAL;
else if (*buffer != '\n' && *buffer != ';' &&
*buffer != '\0')
retval = -EINVAL;
else {
--count;
++buffer;
}
}
}
}
#undef MATCH
#undef VALUE
#undef FIRMWARE_VERSION
if (!retval) {
if (command_flags & COMMAND_SETCOLOURPARAMS) {
/* Adjust cam->vp to reflect these changes */
cam->vp.brightness =
new_params.colourParams.brightness*65535/100;
cam->vp.contrast =
new_params.colourParams.contrast*65535/100;
cam->vp.colour =
new_params.colourParams.saturation*65535/100;
}
if((command_flags & COMMAND_SETEXPOSURE) &&
new_params.exposure.expMode == 2)
cam->exposure_status = EXPOSURE_NORMAL;
memcpy(&cam->params, &new_params, sizeof(struct cam_params));
cam->mainsFreq = new_mains;
cam->cmd_queue |= command_flags;
retval = size;
} else
DBG("error: %d\n", retval);
mutex_unlock(&cam->param_lock);
out:
free_page((unsigned long)page);
return retval;
}
static void create_proc_cpia_cam(struct cam_data *cam)
{
char name[5 + 1 + 10 + 1];
struct proc_dir_entry *ent;
if (!cpia_proc_root || !cam)
return;
snprintf(name, sizeof(name), "video%d", cam->vdev.num);
ent = create_proc_entry(name, S_IFREG|S_IRUGO|S_IWUSR, cpia_proc_root);
if (!ent)
return;
ent->data = cam;
ent->read_proc = cpia_read_proc;
ent->write_proc = cpia_write_proc;
/*
size of the proc entry is 3736 bytes for the standard webcam;
the extra features of the QX3 microscope add 189 bytes.
(we have not yet probed the camera to see which type it is).
*/
ent->size = 3736 + 189;
cam->proc_entry = ent;
}
static void destroy_proc_cpia_cam(struct cam_data *cam)
{
char name[5 + 1 + 10 + 1];
if (!cam || !cam->proc_entry)
return;
snprintf(name, sizeof(name), "video%d", cam->vdev.num);
remove_proc_entry(name, cpia_proc_root);
cam->proc_entry = NULL;
}
static void proc_cpia_create(void)
{
cpia_proc_root = proc_mkdir("cpia", NULL);
if (!cpia_proc_root)
LOG("Unable to initialise /proc/cpia\n");
}
static void __exit proc_cpia_destroy(void)
{
remove_proc_entry("cpia", NULL);
}
#endif /* CONFIG_PROC_FS */
/* ----------------------- debug functions ---------------------- */
#define printstatus(cam) \
DBG("%02x %02x %02x %02x %02x %02x %02x %02x\n",\
cam->params.status.systemState, cam->params.status.grabState, \
cam->params.status.streamState, cam->params.status.fatalError, \
cam->params.status.cmdError, cam->params.status.debugFlags, \
cam->params.status.vpStatus, cam->params.status.errorCode);
/* ----------------------- v4l helpers -------------------------- */
/* supported frame palettes and depths */
static inline int valid_mode(u16 palette, u16 depth)
{
if ((palette == VIDEO_PALETTE_YUV422 && depth == 16) ||
(palette == VIDEO_PALETTE_YUYV && depth == 16))
return 1;
if (colorspace_conv)
return (palette == VIDEO_PALETTE_GREY && depth == 8) ||
(palette == VIDEO_PALETTE_RGB555 && depth == 16) ||
(palette == VIDEO_PALETTE_RGB565 && depth == 16) ||
(palette == VIDEO_PALETTE_RGB24 && depth == 24) ||
(palette == VIDEO_PALETTE_RGB32 && depth == 32) ||
(palette == VIDEO_PALETTE_UYVY && depth == 16);
return 0;
}
static int match_videosize( int width, int height )
{
/* return the best match, where 'best' is as always
* the largest that is not bigger than what is requested. */
if (width>=352 && height>=288)
return VIDEOSIZE_352_288; /* CIF */
if (width>=320 && height>=240)
return VIDEOSIZE_320_240; /* SIF */
if (width>=288 && height>=216)
return VIDEOSIZE_288_216;
if (width>=256 && height>=192)
return VIDEOSIZE_256_192;
if (width>=224 && height>=168)
return VIDEOSIZE_224_168;
if (width>=192 && height>=144)
return VIDEOSIZE_192_144;
if (width>=176 && height>=144)
return VIDEOSIZE_176_144; /* QCIF */
if (width>=160 && height>=120)
return VIDEOSIZE_160_120; /* QSIF */
if (width>=128 && height>=96)
return VIDEOSIZE_128_96;
if (width>=88 && height>=72)
return VIDEOSIZE_88_72;
if (width>=64 && height>=48)
return VIDEOSIZE_64_48;
if (width>=48 && height>=48)
return VIDEOSIZE_48_48;
return -1;
}
/* these are the capture sizes we support */
static void set_vw_size(struct cam_data *cam)
{
/* the col/row/start/end values are the result of simple math */
/* study the SetROI-command in cpia developers guide p 2-22 */
/* streamStartLine is set to the recommended value in the cpia */
/* developers guide p 3-37 */
switch(cam->video_size) {
case VIDEOSIZE_CIF:
cam->vw.width = 352;
cam->vw.height = 288;
cam->params.format.videoSize=VIDEOSIZE_CIF;
cam->params.roi.colStart=0;
cam->params.roi.rowStart=0;
cam->params.streamStartLine = 120;
break;
case VIDEOSIZE_SIF:
cam->vw.width = 320;
cam->vw.height = 240;
cam->params.format.videoSize=VIDEOSIZE_CIF;
cam->params.roi.colStart=2;
cam->params.roi.rowStart=6;
cam->params.streamStartLine = 120;
break;
case VIDEOSIZE_288_216:
cam->vw.width = 288;
cam->vw.height = 216;
cam->params.format.videoSize=VIDEOSIZE_CIF;
cam->params.roi.colStart=4;
cam->params.roi.rowStart=9;
cam->params.streamStartLine = 120;
break;
case VIDEOSIZE_256_192:
cam->vw.width = 256;
cam->vw.height = 192;
cam->params.format.videoSize=VIDEOSIZE_CIF;
cam->params.roi.colStart=6;
cam->params.roi.rowStart=12;
cam->params.streamStartLine = 120;
break;
case VIDEOSIZE_224_168:
cam->vw.width = 224;
cam->vw.height = 168;
cam->params.format.videoSize=VIDEOSIZE_CIF;
cam->params.roi.colStart=8;
cam->params.roi.rowStart=15;
cam->params.streamStartLine = 120;
break;
case VIDEOSIZE_192_144:
cam->vw.width = 192;
cam->vw.height = 144;
cam->params.format.videoSize=VIDEOSIZE_CIF;
cam->params.roi.colStart=10;
cam->params.roi.rowStart=18;
cam->params.streamStartLine = 120;
break;
case VIDEOSIZE_QCIF:
cam->vw.width = 176;
cam->vw.height = 144;
cam->params.format.videoSize=VIDEOSIZE_QCIF;
cam->params.roi.colStart=0;
cam->params.roi.rowStart=0;
cam->params.streamStartLine = 60;
break;
case VIDEOSIZE_QSIF:
cam->vw.width = 160;
cam->vw.height = 120;
cam->params.format.videoSize=VIDEOSIZE_QCIF;
cam->params.roi.colStart=1;
cam->params.roi.rowStart=3;
cam->params.streamStartLine = 60;
break;
case VIDEOSIZE_128_96:
cam->vw.width = 128;
cam->vw.height = 96;
cam->params.format.videoSize=VIDEOSIZE_QCIF;
cam->params.roi.colStart=3;
cam->params.roi.rowStart=6;
cam->params.streamStartLine = 60;
break;
case VIDEOSIZE_88_72:
cam->vw.width = 88;
cam->vw.height = 72;
cam->params.format.videoSize=VIDEOSIZE_QCIF;
cam->params.roi.colStart=5;
cam->params.roi.rowStart=9;
cam->params.streamStartLine = 60;
break;
case VIDEOSIZE_64_48:
cam->vw.width = 64;
cam->vw.height = 48;
cam->params.format.videoSize=VIDEOSIZE_QCIF;
cam->params.roi.colStart=7;
cam->params.roi.rowStart=12;
cam->params.streamStartLine = 60;
break;
case VIDEOSIZE_48_48:
cam->vw.width = 48;
cam->vw.height = 48;
cam->params.format.videoSize=VIDEOSIZE_QCIF;
cam->params.roi.colStart=8;
cam->params.roi.rowStart=6;
cam->params.streamStartLine = 60;
break;
default:
LOG("bad videosize value: %d\n", cam->video_size);
return;
}
if(cam->vc.width == 0)
cam->vc.width = cam->vw.width;
if(cam->vc.height == 0)
cam->vc.height = cam->vw.height;
cam->params.roi.colStart += cam->vc.x >> 3;
cam->params.roi.colEnd = cam->params.roi.colStart +
(cam->vc.width >> 3);
cam->params.roi.rowStart += cam->vc.y >> 2;
cam->params.roi.rowEnd = cam->params.roi.rowStart +
(cam->vc.height >> 2);
return;
}
static int allocate_frame_buf(struct cam_data *cam)
{
int i;
cam->frame_buf = rvmalloc(FRAME_NUM * CPIA_MAX_FRAME_SIZE);
if (!cam->frame_buf)
return -ENOBUFS;
for (i = 0; i < FRAME_NUM; i++)
cam->frame[i].data = cam->frame_buf + i * CPIA_MAX_FRAME_SIZE;
return 0;
}
static int free_frame_buf(struct cam_data *cam)
{
int i;
rvfree(cam->frame_buf, FRAME_NUM*CPIA_MAX_FRAME_SIZE);
cam->frame_buf = NULL;
for (i=0; i < FRAME_NUM; i++)
cam->frame[i].data = NULL;
return 0;
}
static inline void free_frames(struct cpia_frame frame[FRAME_NUM])
{
int i;
for (i=0; i < FRAME_NUM; i++)
frame[i].state = FRAME_UNUSED;
return;
}
/**********************************************************************
*
* General functions
*
**********************************************************************/
/* send an arbitrary command to the camera */
static int do_command(struct cam_data *cam, u16 command, u8 a, u8 b, u8 c, u8 d)
{
int retval, datasize;
u8 cmd[8], data[8];
switch(command) {
case CPIA_COMMAND_GetCPIAVersion:
case CPIA_COMMAND_GetPnPID:
case CPIA_COMMAND_GetCameraStatus:
case CPIA_COMMAND_GetVPVersion:
datasize=8;
break;
case CPIA_COMMAND_GetColourParams:
case CPIA_COMMAND_GetColourBalance:
case CPIA_COMMAND_GetExposure:
mutex_lock(&cam->param_lock);
datasize=8;
break;
case CPIA_COMMAND_ReadMCPorts:
case CPIA_COMMAND_ReadVCRegs:
datasize = 4;
break;
default:
datasize=0;
break;
}
cmd[0] = command>>8;
cmd[1] = command&0xff;
cmd[2] = a;
cmd[3] = b;
cmd[4] = c;
cmd[5] = d;
cmd[6] = datasize;
cmd[7] = 0;
retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
if (retval) {
DBG("%x - failed, retval=%d\n", command, retval);
if (command == CPIA_COMMAND_GetColourParams ||
command == CPIA_COMMAND_GetColourBalance ||
command == CPIA_COMMAND_GetExposure)
mutex_unlock(&cam->param_lock);
} else {
switch(command) {
case CPIA_COMMAND_GetCPIAVersion:
cam->params.version.firmwareVersion = data[0];
cam->params.version.firmwareRevision = data[1];
cam->params.version.vcVersion = data[2];
cam->params.version.vcRevision = data[3];
break;
case CPIA_COMMAND_GetPnPID:
cam->params.pnpID.vendor = data[0]+(((u16)data[1])<<8);
cam->params.pnpID.product = data[2]+(((u16)data[3])<<8);
cam->params.pnpID.deviceRevision =
data[4]+(((u16)data[5])<<8);
break;
case CPIA_COMMAND_GetCameraStatus:
cam->params.status.systemState = data[0];
cam->params.status.grabState = data[1];
cam->params.status.streamState = data[2];
cam->params.status.fatalError = data[3];
cam->params.status.cmdError = data[4];
cam->params.status.debugFlags = data[5];
cam->params.status.vpStatus = data[6];
cam->params.status.errorCode = data[7];
break;
case CPIA_COMMAND_GetVPVersion:
cam->params.vpVersion.vpVersion = data[0];
cam->params.vpVersion.vpRevision = data[1];
cam->params.vpVersion.cameraHeadID =
data[2]+(((u16)data[3])<<8);
break;
case CPIA_COMMAND_GetColourParams:
cam->params.colourParams.brightness = data[0];
cam->params.colourParams.contrast = data[1];
cam->params.colourParams.saturation = data[2];
mutex_unlock(&cam->param_lock);
break;
case CPIA_COMMAND_GetColourBalance:
cam->params.colourBalance.redGain = data[0];
cam->params.colourBalance.greenGain = data[1];
cam->params.colourBalance.blueGain = data[2];
mutex_unlock(&cam->param_lock);
break;
case CPIA_COMMAND_GetExposure:
cam->params.exposure.gain = data[0];
cam->params.exposure.fineExp = data[1];
cam->params.exposure.coarseExpLo = data[2];
cam->params.exposure.coarseExpHi = data[3];
cam->params.exposure.redComp = data[4];
cam->params.exposure.green1Comp = data[5];
cam->params.exposure.green2Comp = data[6];
cam->params.exposure.blueComp = data[7];
mutex_unlock(&cam->param_lock);
break;
case CPIA_COMMAND_ReadMCPorts:
if (!cam->params.qx3.qx3_detected)
break;
/* test button press */
cam->params.qx3.button = ((data[1] & 0x02) == 0);
if (cam->params.qx3.button) {
/* button pressed - unlock the latch */
do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xDF,0xDF,0);
do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xFF,0xFF,0);
}
/* test whether microscope is cradled */
cam->params.qx3.cradled = ((data[2] & 0x40) == 0);
break;
default:
break;
}
}
return retval;
}
/* send a command to the camera with an additional data transaction */
static int do_command_extended(struct cam_data *cam, u16 command,
u8 a, u8 b, u8 c, u8 d,
u8 e, u8 f, u8 g, u8 h,
u8 i, u8 j, u8 k, u8 l)
{
int retval;
u8 cmd[8], data[8];
cmd[0] = command>>8;
cmd[1] = command&0xff;
cmd[2] = a;
cmd[3] = b;
cmd[4] = c;
cmd[5] = d;
cmd[6] = 8;
cmd[7] = 0;
data[0] = e;
data[1] = f;
data[2] = g;
data[3] = h;
data[4] = i;
data[5] = j;
data[6] = k;
data[7] = l;
retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
if (retval)
DBG("%x - failed\n", command);
return retval;
}
/**********************************************************************
*
* Colorspace conversion
*
**********************************************************************/
#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16)
static int convert420(unsigned char *yuv, unsigned char *rgb, int out_fmt,
int linesize, int mmap_kludge)
{
int y, u, v, r, g, b, y1;
/* Odd lines use the same u and v as the previous line.
* Because of compression, it is necessary to get this
* information from the decoded image. */
switch(out_fmt) {
case VIDEO_PALETTE_RGB555:
y = (*yuv++ - 16) * 76310;
y1 = (*yuv - 16) * 76310;
r = ((*(rgb+1-linesize)) & 0x7c) << 1;
g = ((*(rgb-linesize)) & 0xe0) >> 4 |
((*(rgb+1-linesize)) & 0x03) << 6;
b = ((*(rgb-linesize)) & 0x1f) << 3;
u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
r = 104635 * v;
g = -25690 * u - 53294 * v;
b = 132278 * u;
*rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3);
*rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6);
*rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3);
*rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6);
return 4;
case VIDEO_PALETTE_RGB565:
y = (*yuv++ - 16) * 76310;
y1 = (*yuv - 16) * 76310;
r = (*(rgb+1-linesize)) & 0xf8;
g = ((*(rgb-linesize)) & 0xe0) >> 3 |
((*(rgb+1-linesize)) & 0x07) << 5;
b = ((*(rgb-linesize)) & 0x1f) << 3;
u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
r = 104635 * v;
g = -25690 * u - 53294 * v;
b = 132278 * u;
*rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3);
*rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5);
*rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3);
*rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5);
return 4;
break;
case VIDEO_PALETTE_RGB24:
case VIDEO_PALETTE_RGB32:
y = (*yuv++ - 16) * 76310;
y1 = (*yuv - 16) * 76310;
if (mmap_kludge) {
r = *(rgb+2-linesize);
g = *(rgb+1-linesize);
b = *(rgb-linesize);
} else {
r = *(rgb-linesize);
g = *(rgb+1-linesize);
b = *(rgb+2-linesize);
}
u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
r = 104635 * v;
g = -25690 * u + -53294 * v;
b = 132278 * u;
if (mmap_kludge) {
*rgb++ = LIMIT(b+y);
*rgb++ = LIMIT(g+y);
*rgb++ = LIMIT(r+y);
if(out_fmt == VIDEO_PALETTE_RGB32)
rgb++;
*rgb++ = LIMIT(b+y1);
*rgb++ = LIMIT(g+y1);
*rgb = LIMIT(r+y1);
} else {
*rgb++ = LIMIT(r+y);
*rgb++ = LIMIT(g+y);
*rgb++ = LIMIT(b+y);
if(out_fmt == VIDEO_PALETTE_RGB32)
rgb++;
*rgb++ = LIMIT(r+y1);
*rgb++ = LIMIT(g+y1);
*rgb = LIMIT(b+y1);
}
if(out_fmt == VIDEO_PALETTE_RGB32)
return 8;
return 6;
case VIDEO_PALETTE_YUV422:
case VIDEO_PALETTE_YUYV:
y = *yuv++;
u = *(rgb+1-linesize);
y1 = *yuv;
v = *(rgb+3-linesize);
*rgb++ = y;
*rgb++ = u;
*rgb++ = y1;
*rgb = v;
return 4;
case VIDEO_PALETTE_UYVY:
u = *(rgb-linesize);
y = *yuv++;
v = *(rgb+2-linesize);
y1 = *yuv;
*rgb++ = u;
*rgb++ = y;
*rgb++ = v;
*rgb = y1;
return 4;
case VIDEO_PALETTE_GREY:
*rgb++ = *yuv++;
*rgb = *yuv;
return 2;
default:
DBG("Empty: %d\n", out_fmt);
return 0;
}
}
static int yuvconvert(unsigned char *yuv, unsigned char *rgb, int out_fmt,
int in_uyvy, int mmap_kludge)
{
int y, u, v, r, g, b, y1;
switch(out_fmt) {
case VIDEO_PALETTE_RGB555:
case VIDEO_PALETTE_RGB565:
case VIDEO_PALETTE_RGB24:
case VIDEO_PALETTE_RGB32:
if (in_uyvy) {
u = *yuv++ - 128;
y = (*yuv++ - 16) * 76310;
v = *yuv++ - 128;
y1 = (*yuv - 16) * 76310;
} else {
y = (*yuv++ - 16) * 76310;
u = *yuv++ - 128;
y1 = (*yuv++ - 16) * 76310;
v = *yuv - 128;
}
r = 104635 * v;
g = -25690 * u + -53294 * v;
b = 132278 * u;
break;
default:
y = *yuv++;
u = *yuv++;
y1 = *yuv++;
v = *yuv;
/* Just to avoid compiler warnings */
r = 0;
g = 0;
b = 0;
break;
}
switch(out_fmt) {
case VIDEO_PALETTE_RGB555:
*rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3);
*rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6);
*rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3);
*rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6);
return 4;
case VIDEO_PALETTE_RGB565:
*rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3);
*rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5);
*rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3);
*rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5);
return 4;
case VIDEO_PALETTE_RGB24:
if (mmap_kludge) {
*rgb++ = LIMIT(b+y);
*rgb++ = LIMIT(g+y);
*rgb++ = LIMIT(r+y);
*rgb++ = LIMIT(b+y1);
*rgb++ = LIMIT(g+y1);
*rgb = LIMIT(r+y1);
} else {
*rgb++ = LIMIT(r+y);
*rgb++ = LIMIT(g+y);
*rgb++ = LIMIT(b+y);
*rgb++ = LIMIT(r+y1);
*rgb++ = LIMIT(g+y1);
*rgb = LIMIT(b+y1);
}
return 6;
case VIDEO_PALETTE_RGB32:
if (mmap_kludge) {
*rgb++ = LIMIT(b+y);
*rgb++ = LIMIT(g+y);
*rgb++ = LIMIT(r+y);
rgb++;
*rgb++ = LIMIT(b+y1);
*rgb++ = LIMIT(g+y1);
*rgb = LIMIT(r+y1);
} else {
*rgb++ = LIMIT(r+y);
*rgb++ = LIMIT(g+y);
*rgb++ = LIMIT(b+y);
rgb++;
*rgb++ = LIMIT(r+y1);
*rgb++ = LIMIT(g+y1);
*rgb = LIMIT(b+y1);
}
return 8;
case VIDEO_PALETTE_GREY:
*rgb++ = y;
*rgb = y1;
return 2;
case VIDEO_PALETTE_YUV422:
case VIDEO_PALETTE_YUYV:
*rgb++ = y;
*rgb++ = u;
*rgb++ = y1;
*rgb = v;
return 4;
case VIDEO_PALETTE_UYVY:
*rgb++ = u;
*rgb++ = y;
*rgb++ = v;
*rgb = y1;
return 4;
default:
DBG("Empty: %d\n", out_fmt);
return 0;
}
}
static int skipcount(int count, int fmt)
{
switch(fmt) {
case VIDEO_PALETTE_GREY:
return count;
case VIDEO_PALETTE_RGB555:
case VIDEO_PALETTE_RGB565:
case VIDEO_PALETTE_YUV422:
case VIDEO_PALETTE_YUYV:
case VIDEO_PALETTE_UYVY:
return 2*count;
case VIDEO_PALETTE_RGB24:
return 3*count;
case VIDEO_PALETTE_RGB32:
return 4*count;
default:
return 0;
}
}
static int parse_picture(struct cam_data *cam, int size)
{
u8 *obuf, *ibuf, *end_obuf;
int ll, in_uyvy, compressed, decimation, even_line, origsize, out_fmt;
int rows, cols, linesize, subsample_422;
/* make sure params don't change while we are decoding */
mutex_lock(&cam->param_lock);
obuf = cam->decompressed_frame.data;
end_obuf = obuf+CPIA_MAX_FRAME_SIZE;
ibuf = cam->raw_image;
origsize = size;
out_fmt = cam->vp.palette;
if ((ibuf[0] != MAGIC_0) || (ibuf[1] != MAGIC_1)) {
LOG("header not found\n");
mutex_unlock(&cam->param_lock);
return -1;
}
if ((ibuf[16] != VIDEOSIZE_QCIF) && (ibuf[16] != VIDEOSIZE_CIF)) {
LOG("wrong video size\n");
mutex_unlock(&cam->param_lock);
return -1;
}
if (ibuf[17] != SUBSAMPLE_420 && ibuf[17] != SUBSAMPLE_422) {
LOG("illegal subtype %d\n",ibuf[17]);
mutex_unlock(&cam->param_lock);
return -1;
}
subsample_422 = ibuf[17] == SUBSAMPLE_422;
if (ibuf[18] != YUVORDER_YUYV && ibuf[18] != YUVORDER_UYVY) {
LOG("illegal yuvorder %d\n",ibuf[18]);
mutex_unlock(&cam->param_lock);
return -1;
}
in_uyvy = ibuf[18] == YUVORDER_UYVY;
if ((ibuf[24] != cam->params.roi.colStart) ||
(ibuf[25] != cam->params.roi.colEnd) ||
(ibuf[26] != cam->params.roi.rowStart) ||
(ibuf[27] != cam->params.roi.rowEnd)) {
LOG("ROI mismatch\n");
mutex_unlock(&cam->param_lock);
return -1;
}
cols = 8*(ibuf[25] - ibuf[24]);
rows = 4*(ibuf[27] - ibuf[26]);
if ((ibuf[28] != NOT_COMPRESSED) && (ibuf[28] != COMPRESSED)) {
LOG("illegal compression %d\n",ibuf[28]);
mutex_unlock(&cam->param_lock);
return -1;
}
compressed = (ibuf[28] == COMPRESSED);
if (ibuf[29] != NO_DECIMATION && ibuf[29] != DECIMATION_ENAB) {
LOG("illegal decimation %d\n",ibuf[29]);
mutex_unlock(&cam->param_lock);
return -1;
}
decimation = (ibuf[29] == DECIMATION_ENAB);
cam->params.yuvThreshold.yThreshold = ibuf[30];
cam->params.yuvThreshold.uvThreshold = ibuf[31];
cam->params.status.systemState = ibuf[32];
cam->params.status.grabState = ibuf[33];
cam->params.status.streamState = ibuf[34];
cam->params.status.fatalError = ibuf[35];
cam->params.status.cmdError = ibuf[36];
cam->params.status.debugFlags = ibuf[37];
cam->params.status.vpStatus = ibuf[38];
cam->params.status.errorCode = ibuf[39];
cam->fps = ibuf[41];
mutex_unlock(&cam->param_lock);
linesize = skipcount(cols, out_fmt);
ibuf += FRAME_HEADER_SIZE;
size -= FRAME_HEADER_SIZE;
ll = ibuf[0] | (ibuf[1] << 8);
ibuf += 2;
even_line = 1;
while (size > 0) {
size -= (ll+2);
if (size < 0) {
LOG("Insufficient data in buffer\n");
return -1;
}
while (ll > 1) {
if (!compressed || (compressed && !(*ibuf & 1))) {
if(subsample_422 || even_line) {
obuf += yuvconvert(ibuf, obuf, out_fmt,
in_uyvy, cam->mmap_kludge);
ibuf += 4;
ll -= 4;
} else {
/* SUBSAMPLE_420 on an odd line */
obuf += convert420(ibuf, obuf,
out_fmt, linesize,
cam->mmap_kludge);
ibuf += 2;
ll -= 2;
}
} else {
/*skip compressed interval from previous frame*/
obuf += skipcount(*ibuf >> 1, out_fmt);
if (obuf > end_obuf) {
LOG("Insufficient buffer size\n");
return -1;
}
++ibuf;
ll--;
}
}
if (ll == 1) {
if (*ibuf != EOL) {
DBG("EOL not found giving up after %d/%d"
" bytes\n", origsize-size, origsize);
return -1;
}
++ibuf; /* skip over EOL */
if ((size > 3) && (ibuf[0] == EOI) && (ibuf[1] == EOI) &&
(ibuf[2] == EOI) && (ibuf[3] == EOI)) {
size -= 4;
break;
}
if(decimation) {
/* skip the odd lines for now */
obuf += linesize;
}
if (size > 1) {
ll = ibuf[0] | (ibuf[1] << 8);
ibuf += 2; /* skip over line length */
}
if(!decimation)
even_line = !even_line;
} else {
LOG("line length was not 1 but %d after %d/%d bytes\n",
ll, origsize-size, origsize);
return -1;
}
}
if(decimation) {
/* interpolate odd rows */
int i, j;
u8 *prev, *next;
prev = cam->decompressed_frame.data;
obuf = prev+linesize;
next = obuf+linesize;
for(i=1; i<rows-1; i+=2) {
for(j=0; j<linesize; ++j) {
*obuf++ = ((int)*prev++ + *next++) / 2;
}
prev += linesize;
obuf += linesize;
next += linesize;
}
/* last row is odd, just copy previous row */
memcpy(obuf, prev, linesize);
}
cam->decompressed_frame.count = obuf-cam->decompressed_frame.data;
return cam->decompressed_frame.count;
}
/* InitStreamCap wrapper to select correct start line */
static inline int init_stream_cap(struct cam_data *cam)
{
return do_command(cam, CPIA_COMMAND_InitStreamCap,
0, cam->params.streamStartLine, 0, 0);
}
/* find_over_exposure
* Finds a suitable value of OverExposure for use with SetFlickerCtrl
* Some calculation is required because this value changes with the brightness
* set with SetColourParameters
*
* Parameters: Brightness - last brightness value set with SetColourParameters
*
* Returns: OverExposure value to use with SetFlickerCtrl
*/
#define FLICKER_MAX_EXPOSURE 250
#define FLICKER_ALLOWABLE_OVER_EXPOSURE 146
#define FLICKER_BRIGHTNESS_CONSTANT 59
static int find_over_exposure(int brightness)
{
int MaxAllowableOverExposure, OverExposure;
MaxAllowableOverExposure = FLICKER_MAX_EXPOSURE - brightness -
FLICKER_BRIGHTNESS_CONSTANT;
if (MaxAllowableOverExposure < FLICKER_ALLOWABLE_OVER_EXPOSURE) {
OverExposure = MaxAllowableOverExposure;
} else {
OverExposure = FLICKER_ALLOWABLE_OVER_EXPOSURE;
}
return OverExposure;
}
#undef FLICKER_MAX_EXPOSURE
#undef FLICKER_ALLOWABLE_OVER_EXPOSURE
#undef FLICKER_BRIGHTNESS_CONSTANT
/* update various camera modes and settings */
static void dispatch_commands(struct cam_data *cam)
{
mutex_lock(&cam->param_lock);
if (cam->cmd_queue==COMMAND_NONE) {
mutex_unlock(&cam->param_lock);
return;
}
DEB_BYTE(cam->cmd_queue);
DEB_BYTE(cam->cmd_queue>>8);
if (cam->cmd_queue & COMMAND_SETFORMAT) {
do_command(cam, CPIA_COMMAND_SetFormat,
cam->params.format.videoSize,
cam->params.format.subSample,
cam->params.format.yuvOrder, 0);
do_command(cam, CPIA_COMMAND_SetROI,
cam->params.roi.colStart, cam->params.roi.colEnd,
cam->params.roi.rowStart, cam->params.roi.rowEnd);
cam->first_frame = 1;
}
if (cam->cmd_queue & COMMAND_SETCOLOURPARAMS)
do_command(cam, CPIA_COMMAND_SetColourParams,
cam->params.colourParams.brightness,
cam->params.colourParams.contrast,
cam->params.colourParams.saturation, 0);
if (cam->cmd_queue & COMMAND_SETAPCOR)
do_command(cam, CPIA_COMMAND_SetApcor,
cam->params.apcor.gain1,
cam->params.apcor.gain2,
cam->params.apcor.gain4,
cam->params.apcor.gain8);
if (cam->cmd_queue & COMMAND_SETVLOFFSET)
do_command(cam, CPIA_COMMAND_SetVLOffset,
cam->params.vlOffset.gain1,
cam->params.vlOffset.gain2,
cam->params.vlOffset.gain4,
cam->params.vlOffset.gain8);
if (cam->cmd_queue & COMMAND_SETEXPOSURE) {
do_command_extended(cam, CPIA_COMMAND_SetExposure,
cam->params.exposure.gainMode,
1,
cam->params.exposure.compMode,
cam->params.exposure.centreWeight,
cam->params.exposure.gain,
cam->params.exposure.fineExp,
cam->params.exposure.coarseExpLo,
cam->params.exposure.coarseExpHi,
cam->params.exposure.redComp,
cam->params.exposure.green1Comp,
cam->params.exposure.green2Comp,
cam->params.exposure.blueComp);
if(cam->params.exposure.expMode != 1) {
do_command_extended(cam, CPIA_COMMAND_SetExposure,
0,
cam->params.exposure.expMode,
0, 0,
cam->params.exposure.gain,
cam->params.exposure.fineExp,
cam->params.exposure.coarseExpLo,
cam->params.exposure.coarseExpHi,
0, 0, 0, 0);
}
}
if (cam->cmd_queue & COMMAND_SETCOLOURBALANCE) {
if (cam->params.colourBalance.balanceMode == 1) {
do_command(cam, CPIA_COMMAND_SetColourBalance,
1,
cam->params.colourBalance.redGain,
cam->params.colourBalance.greenGain,
cam->params.colourBalance.blueGain);
do_command(cam, CPIA_COMMAND_SetColourBalance,
3, 0, 0, 0);
}
if (cam->params.colourBalance.balanceMode == 2) {
do_command(cam, CPIA_COMMAND_SetColourBalance,
2, 0, 0, 0);
}
if (cam->params.colourBalance.balanceMode == 3) {
do_command(cam, CPIA_COMMAND_SetColourBalance,
3, 0, 0, 0);
}
}
if (cam->cmd_queue & COMMAND_SETCOMPRESSIONTARGET)
do_command(cam, CPIA_COMMAND_SetCompressionTarget,
cam->params.compressionTarget.frTargeting,
cam->params.compressionTarget.targetFR,
cam->params.compressionTarget.targetQ, 0);
if (cam->cmd_queue & COMMAND_SETYUVTHRESH)
do_command(cam, CPIA_COMMAND_SetYUVThresh,
cam->params.yuvThreshold.yThreshold,
cam->params.yuvThreshold.uvThreshold, 0, 0);
if (cam->cmd_queue & COMMAND_SETCOMPRESSIONPARAMS)
do_command_extended(cam, CPIA_COMMAND_SetCompressionParams,
0, 0, 0, 0,
cam->params.compressionParams.hysteresis,
cam->params.compressionParams.threshMax,
cam->params.compressionParams.smallStep,
cam->params.compressionParams.largeStep,
cam->params.compressionParams.decimationHysteresis,
cam->params.compressionParams.frDiffStepThresh,
cam->params.compressionParams.qDiffStepThresh,
cam->params.compressionParams.decimationThreshMod);
if (cam->cmd_queue & COMMAND_SETCOMPRESSION)
do_command(cam, CPIA_COMMAND_SetCompression,
cam->params.compression.mode,
cam->params.compression.decimation, 0, 0);
if (cam->cmd_queue & COMMAND_SETSENSORFPS)
do_command(cam, CPIA_COMMAND_SetSensorFPS,
cam->params.sensorFps.divisor,
cam->params.sensorFps.baserate, 0, 0);
if (cam->cmd_queue & COMMAND_SETFLICKERCTRL)
do_command(cam, CPIA_COMMAND_SetFlickerCtrl,
cam->params.flickerControl.flickerMode,
cam->params.flickerControl.coarseJump,
abs(cam->params.flickerControl.allowableOverExposure),
0);
if (cam->cmd_queue & COMMAND_SETECPTIMING)
do_command(cam, CPIA_COMMAND_SetECPTiming,
cam->params.ecpTiming, 0, 0, 0);
if (cam->cmd_queue & COMMAND_PAUSE)
do_command(cam, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0);
if (cam->cmd_queue & COMMAND_RESUME)
init_stream_cap(cam);
if (cam->cmd_queue & COMMAND_SETLIGHTS && cam->params.qx3.qx3_detected)
{
int p1 = (cam->params.qx3.bottomlight == 0) << 1;
int p2 = (cam->params.qx3.toplight == 0) << 3;
do_command(cam, CPIA_COMMAND_WriteVCReg, 0x90, 0x8F, 0x50, 0);
do_command(cam, CPIA_COMMAND_WriteMCPort, 2, 0, (p1|p2|0xE0), 0);
}
cam->cmd_queue = COMMAND_NONE;
mutex_unlock(&cam->param_lock);
return;
}
static void set_flicker(struct cam_params *params, volatile u32 *command_flags,
int on)
{
/* Everything in here is from the Windows driver */
#define FIRMWARE_VERSION(x,y) (params->version.firmwareVersion == (x) && \
params->version.firmwareRevision == (y))
/* define for compgain calculation */
#if 0
#define COMPGAIN(base, curexp, newexp) \
(u8) ((((float) base - 128.0) * ((float) curexp / (float) newexp)) + 128.5)
#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
(u16)((float)curexp * (float)(u8)(curcomp + 128) / (float)(u8)(basecomp - 128))
#else
/* equivalent functions without floating point math */
#define COMPGAIN(base, curexp, newexp) \
(u8)(128 + (((u32)(2*(base-128)*curexp + newexp)) / (2* newexp)) )
#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
(u16)(((u32)(curexp * (u8)(curcomp + 128)) / (u8)(basecomp - 128)))
#endif
int currentexp = params->exposure.coarseExpLo +
params->exposure.coarseExpHi*256;
int startexp;
if (on) {
int cj = params->flickerControl.coarseJump;
params->flickerControl.flickerMode = 1;
params->flickerControl.disabled = 0;
if(params->exposure.expMode != 2)
*command_flags |= COMMAND_SETEXPOSURE;
params->exposure.expMode = 2;
currentexp = currentexp << params->exposure.gain;
params->exposure.gain = 0;
/* round down current exposure to nearest value */
startexp = (currentexp + ROUND_UP_EXP_FOR_FLICKER) / cj;
if(startexp < 1)
startexp = 1;
startexp = (startexp * cj) - 1;
if(FIRMWARE_VERSION(1,2))
while(startexp > MAX_EXP_102)
startexp -= cj;
else
while(startexp > MAX_EXP)
startexp -= cj;
params->exposure.coarseExpLo = startexp & 0xff;
params->exposure.coarseExpHi = startexp >> 8;
if (currentexp > startexp) {
if (currentexp > (2 * startexp))
currentexp = 2 * startexp;
params->exposure.redComp = COMPGAIN (COMP_RED, currentexp, startexp);
params->exposure.green1Comp = COMPGAIN (COMP_GREEN1, currentexp, startexp);
params->exposure.green2Comp = COMPGAIN (COMP_GREEN2, currentexp, startexp);
params->exposure.blueComp = COMPGAIN (COMP_BLUE, currentexp, startexp);
} else {
params->exposure.redComp = COMP_RED;
params->exposure.green1Comp = COMP_GREEN1;
params->exposure.green2Comp = COMP_GREEN2;
params->exposure.blueComp = COMP_BLUE;
}
if(FIRMWARE_VERSION(1,2))
params->exposure.compMode = 0;
else
params->exposure.compMode = 1;
params->apcor.gain1 = 0x18;
params->apcor.gain2 = 0x18;
params->apcor.gain4 = 0x16;
params->apcor.gain8 = 0x14;
*command_flags |= COMMAND_SETAPCOR;
} else {
params->flickerControl.flickerMode = 0;
params->flickerControl.disabled = 1;
/* Coarse = average of equivalent coarse for each comp channel */
startexp = EXP_FROM_COMP(COMP_RED, params->exposure.redComp, currentexp);
startexp += EXP_FROM_COMP(COMP_GREEN1, params->exposure.green1Comp, currentexp);
startexp += EXP_FROM_COMP(COMP_GREEN2, params->exposure.green2Comp, currentexp);
startexp += EXP_FROM_COMP(COMP_BLUE, params->exposure.blueComp, currentexp);
startexp = startexp >> 2;
while(startexp > MAX_EXP &&
params->exposure.gain < params->exposure.gainMode-1) {
startexp = startexp >> 1;
++params->exposure.gain;
}
if(FIRMWARE_VERSION(1,2) && startexp > MAX_EXP_102)
startexp = MAX_EXP_102;
if(startexp > MAX_EXP)
startexp = MAX_EXP;
params->exposure.coarseExpLo = startexp&0xff;
params->exposure.coarseExpHi = startexp >> 8;
params->exposure.redComp = COMP_RED;
params->exposure.green1Comp = COMP_GREEN1;
params->exposure.green2Comp = COMP_GREEN2;
params->exposure.blueComp = COMP_BLUE;
params->exposure.compMode = 1;
*command_flags |= COMMAND_SETEXPOSURE;
params->apcor.gain1 = 0x18;
params->apcor.gain2 = 0x16;
params->apcor.gain4 = 0x24;
params->apcor.gain8 = 0x34;
*command_flags |= COMMAND_SETAPCOR;
}
params->vlOffset.gain1 = 20;
params->vlOffset.gain2 = 24;
params->vlOffset.gain4 = 26;
params->vlOffset.gain8 = 26;
*command_flags |= COMMAND_SETVLOFFSET;
#undef FIRMWARE_VERSION
#undef EXP_FROM_COMP
#undef COMPGAIN
}
#define FIRMWARE_VERSION(x,y) (cam->params.version.firmwareVersion == (x) && \
cam->params.version.firmwareRevision == (y))
/* monitor the exposure and adjust the sensor frame rate if needed */
static void monitor_exposure(struct cam_data *cam)
{
u8 exp_acc, bcomp, gain, coarseL, cmd[8], data[8];
int retval, light_exp, dark_exp, very_dark_exp;
int old_exposure, new_exposure, framerate;
/* get necessary stats and register settings from camera */
/* do_command can't handle this, so do it ourselves */
cmd[0] = CPIA_COMMAND_ReadVPRegs>>8;
cmd[1] = CPIA_COMMAND_ReadVPRegs&0xff;
cmd[2] = 30;
cmd[3] = 4;
cmd[4] = 9;
cmd[5] = 8;
cmd[6] = 8;
cmd[7] = 0;
retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
if (retval) {
LOG("ReadVPRegs(30,4,9,8) - failed, retval=%d\n",
retval);
return;
}
exp_acc = data[0];
bcomp = data[1];
gain = data[2];
coarseL = data[3];
mutex_lock(&cam->param_lock);
light_exp = cam->params.colourParams.brightness +
TC - 50 + EXP_ACC_LIGHT;
if(light_exp > 255)
light_exp = 255;
dark_exp = cam->params.colourParams.brightness +
TC - 50 - EXP_ACC_DARK;
if(dark_exp < 0)
dark_exp = 0;
very_dark_exp = dark_exp/2;
old_exposure = cam->params.exposure.coarseExpHi * 256 +
cam->params.exposure.coarseExpLo;
if(!cam->params.flickerControl.disabled) {
/* Flicker control on */
int max_comp = FIRMWARE_VERSION(1,2) ? MAX_COMP : HIGH_COMP_102;
bcomp += 128; /* decode */
if(bcomp >= max_comp && exp_acc < dark_exp) {
/* dark */
if(exp_acc < very_dark_exp) {
/* very dark */
if(cam->exposure_status == EXPOSURE_VERY_DARK)
++cam->exposure_count;
else {
cam->exposure_status = EXPOSURE_VERY_DARK;
cam->exposure_count = 1;
}
} else {
/* just dark */
if(cam->exposure_status == EXPOSURE_DARK)
++cam->exposure_count;
else {
cam->exposure_status = EXPOSURE_DARK;
cam->exposure_count = 1;
}
}
} else if(old_exposure <= LOW_EXP || exp_acc > light_exp) {
/* light */
if(old_exposure <= VERY_LOW_EXP) {
/* very light */
if(cam->exposure_status == EXPOSURE_VERY_LIGHT)
++cam->exposure_count;
else {
cam->exposure_status = EXPOSURE_VERY_LIGHT;
cam->exposure_count = 1;
}
} else {
/* just light */
if(cam->exposure_status == EXPOSURE_LIGHT)
++cam->exposure_count;
else {
cam->exposure_status = EXPOSURE_LIGHT;
cam->exposure_count = 1;
}
}
} else {
/* not dark or light */
cam->exposure_status = EXPOSURE_NORMAL;
}
} else {
/* Flicker control off */
if(old_exposure >= MAX_EXP && exp_acc < dark_exp) {
/* dark */
if(exp_acc < very_dark_exp) {
/* very dark */
if(cam->exposure_status == EXPOSURE_VERY_DARK)
++cam->exposure_count;
else {
cam->exposure_status = EXPOSURE_VERY_DARK;
cam->exposure_count = 1;
}
} else {
/* just dark */
if(cam->exposure_status == EXPOSURE_DARK)
++cam->exposure_count;
else {
cam->exposure_status = EXPOSURE_DARK;
cam->exposure_count = 1;
}
}
} else if(old_exposure <= LOW_EXP || exp_acc > light_exp) {
/* light */
if(old_exposure <= VERY_LOW_EXP) {
/* very light */
if(cam->exposure_status == EXPOSURE_VERY_LIGHT)
++cam->exposure_count;
else {
cam->exposure_status = EXPOSURE_VERY_LIGHT;
cam->exposure_count = 1;
}
} else {
/* just light */
if(cam->exposure_status == EXPOSURE_LIGHT)
++cam->exposure_count;
else {
cam->exposure_status = EXPOSURE_LIGHT;
cam->exposure_count = 1;
}
}
} else {
/* not dark or light */
cam->exposure_status = EXPOSURE_NORMAL;
}
}